yuma123_2.14/0000775000175000017500000000000014770057112013131 5ustar vladimirvladimiryuma123_2.14/README0000664000175000017500000000401414770023131014002 0ustar vladimirvladimirYuma123 README ----------- ==What is Yuma123== The purpose of the Yuma123 project is to provide an opensource YANG API in C and cli (yangcli) and server (netconfd) YANG automation enabled appications. Branching from the last BSD licensed branch of the Yuma project the code has evolved in the following direction: - a more mainstream build system based on autoconf/automake was added - a number of critical bugs have been fixed - new IETF standards support was added (ietf-nacm, ietf-system, etc.) - support was added for new YANG extensions - added to debian.org repositories ==Compilation== #Build dependencies resolution for Debian Bullseye. Replace with appropriate command line if you are not using Debian. sudo apt-get install git autoconf automake pkg-config gcc libtool libxml2-dev libssh2-1-dev make libncurses5-dev zlib1g-dev libreadline-dev libssl-dev git clone git://git.code.sf.net/p/yuma123/git yuma123-git cd yuma123-git autoreconf -i -f ./configure CFLAGS='-g -O0' CXXFLAGS='-g -O0' --prefix=/usr make sudo make install ==Configure and start== #Runtime dependencies resolution for Debian Bullseye. Replace with appropriate command line if you are not using Debian. sudo apt-get install openssh-server echo '' > /tmp/startup-cfg.xml /usr/sbin/netconfd --module=helloworld --startup=/tmp/startup-cfg.xml --log-level="debug4" --superuser="$USER" The server is now started with the example helloworld module. Tell sshd to listen on port 830. Add the following 2 lines to /etc/ssh/sshd_config: Port 830 Subsystem netconf /usr/sbin/netconf-subsystem --ncxserver-sockname=830@/tmp/ncxserver.sock Start sshd (on Debian): sudo /etc/init.d/ssh restart You can verify everything is OK: root@lmf:~# yangcli --user="$USER" --server=localhost --password='mysecretpass' ... yangcli root@localhost> xget /helloworld-state RPC Data Reply 2 for session 3: rpc-reply { data { helloworld-state { message 'Hello World!' } } } yangcli root@localhost> or yangcli root@localhost> xget / ... yuma123_2.14/libtecla/0000775000175000017500000000000014770023131014702 5ustar vladimirvladimiryuma123_2.14/libtecla/version.c0000664000175000017500000000230714770023131016535 0ustar vladimirvladimir#include "libtecla.h" /*....................................................................... * Return the version number of the tecla library. * * Input: * major int * The major version number of the library * will be assigned to *major. This number is * only incremented when a change to the library is * made that breaks binary (shared library) and/or * compilation backwards compatibility. * minor int * The minor version number of the library * will be assigned to *minor. This number is * incremented whenever new functions are added to * the public API. * micro int * The micro version number of the library will be * assigned to *micro. This number is incremented * whenever internal changes are made that don't * change the public API, such as bug fixes and * performance enhancements. */ void libtecla_version(int *major, int *minor, int *micro) { if(major) *major = TECLA_MAJOR_VER; if(minor) *minor = TECLA_MINOR_VER; if(micro) *micro = TECLA_MICRO_VER; } yuma123_2.14/libtecla/enhance.c0000664000175000017500000005201414770023131016451 0ustar vladimirvladimir#include #include #include #include #include #include #include #include #ifdef HAVE_SELECT #ifdef HAVE_SYS_SELECT_H #include #endif #endif #include #include #include #include #include #if HAVE_SYSV_PTY #include /* System-V stream I/O */ char *ptsname(int fd); int grantpt(int fd); int unlockpt(int fd); #endif #include "libtecla.h" /* * Pseudo-terminal devices are found in the following directory. */ #define PTY_DEV_DIR "/dev/" /* * Pseudo-terminal controller device file names start with the following * prefix. */ #define PTY_CNTRL "pty" /* * Pseudo-terminal slave device file names start with the following * prefix. */ #define PTY_SLAVE "tty" /* * Specify the maximum suffix length for the control and slave device * names. */ #define PTY_MAX_SUFFIX 10 /* * Set the maximum length of the master and slave terminal device filenames, * including space for a terminating '\0'. */ #define PTY_MAX_NAME (sizeof(PTY_DEV_DIR)-1 + \ (sizeof(PTY_SLAVE) > sizeof(PTY_CNTRL) ? \ sizeof(PTY_SLAVE) : sizeof(PTY_CNTRL))-1 \ + PTY_MAX_SUFFIX + 1) /* * Set the maximum length of an input line. */ #define PTY_MAX_LINE 4096 /* * Set the size of the buffer used for accumulating bytes written by the * user's terminal to its stdout. */ #define PTY_MAX_READ 1000 /* * Set the amount of memory used to record history. */ #define PTY_HIST_SIZE 10000 /* * Set the timeout delay used to check for quickly arriving * sequential output from the application. */ #define PTY_READ_TIMEOUT 100000 /* micro-seconds */ static int pty_open_master(const char *prog, int *cntrl, char *slave_name); static int pty_open_slave(const char *prog, char *slave_name); static int pty_child(const char *prog, int slave, char *argv[]); static int pty_parent(const char *prog, int cntrl); static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff); static GL_FD_EVENT_FN(pty_read_from_program); static int pty_write_to_fd(int fd, const char *string, int n); static void pty_child_exited(int sig); static int pty_master_readable(int fd, long usec); /*....................................................................... * Run a program with enhanced terminal editing facilities. * * Usage: * enhance program [args...] */ int main(int argc, char *argv[]) { int cntrl = -1; /* The fd of the pseudo-terminal controller device */ int slave = -1; /* The fd of the pseudo-terminal slave device */ pid_t pid; /* The return value of fork() */ int status; /* The return statuses of the parent and child functions */ char slave_name[PTY_MAX_NAME]; /* The filename of the slave end of the */ /* pseudo-terminal. */ char *prog; /* The name of the program (ie. argv[0]) */ /* * Check the arguments. */ if(argc < 2) { fprintf(stderr, "Usage: %s [arguments...]\n", argv[0]); return 1; }; /* * Get the name of the program. */ prog = argv[0]; /* * If the user has the LC_CTYPE or LC_ALL environment variables set, * enable display of characters corresponding to the specified locale. */ (void) setlocale(LC_CTYPE, ""); /* * If the program is taking its input from a pipe or a file, or * sending its output to something other than a terminal, run the * program without tecla. */ if(!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) { if(execvp(argv[1], argv + 1) < 0) { fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[1], strerror(errno)); fflush(stderr); _exit(1); }; }; /* * Open the master side of a pseudo-terminal pair, and return * the corresponding file descriptor and the filename of the * slave end of the pseudo-terminal. */ if(pty_open_master(prog, &cntrl, slave_name)) return 1; /* * Set up a signal handler to watch for the child process exiting. */ signal(SIGCHLD, pty_child_exited); /* * The above signal handler sends the parent process a SIGINT signal. * This signal is caught by gl_get_line(), which resets the terminal * settings, and if the application signal handler for this signal * doesn't abort the process, gl_get_line() returns NULL with errno * set to EINTR. Arrange to ignore the signal, so that gl_get_line() * returns and we have a chance to cleanup. */ signal(SIGINT, SIG_IGN); /* * We will read user input in one process, and run the user's program * in a child process. */ pid = fork(); if(pid < 0) { fprintf(stderr, "%s: Unable to fork child process (%s).\n", prog, strerror(errno)); return 1; }; /* * Are we the parent? */ if(pid!=0) { status = pty_parent(prog, cntrl); close(cntrl); } else { close(cntrl); /* The child doesn't use the slave device */ signal(SIGCHLD, pty_child_exited); if((slave = pty_open_slave(prog, slave_name)) >= 0) { status = pty_child(prog, slave, argv + 1); close(slave); } else { status = 1; }; }; return status; } /*....................................................................... * Open the master side of a pseudo-terminal pair, and return * the corresponding file descriptor and the filename of the * slave end of the pseudo-terminal. * * Input/Output: * prog const char * The name of this program. * cntrl int * The file descriptor of the pseudo-terminal * controller device will be assigned tp *cntrl. * slave_name char * The file-name of the pseudo-terminal slave device * will be recorded in slave_name[], which must have * at least PTY_MAX_NAME elements. * Output: * return int 0 - OK. * 1 - Error. */ static int pty_open_master(const char *prog, int *cntrl, char *slave_name) { char master_name[PTY_MAX_NAME]; /* The filename of the master device */ DIR *dir; /* The directory iterator */ struct dirent *file; /* A file in "/dev" */ /* * Mark the controller device as not opened yet. */ *cntrl = -1; /* * On systems with the Sys-V pseudo-terminal interface, we don't * have to search for a free master terminal. We just open /dev/ptmx, * and if there is a free master terminal device, we are given a file * descriptor connected to it. */ #if HAVE_SYSV_PTY *cntrl = open("/dev/ptmx", O_RDWR); if(*cntrl >= 0) { /* * Get the filename of the slave side of the pseudo-terminal. */ char *name = ptsname(*cntrl); if(name) { if(strlen(name)+1 > PTY_MAX_NAME) { fprintf(stderr, "%s: Slave pty filename too long.\n", prog); return 1; }; strcpy(slave_name, name); /* * If unable to get the slave name, discard the controller file descriptor, * ready to try a search instead. */ } else { close(*cntrl); *cntrl = -1; }; } else { #endif /* * On systems without /dev/ptmx, or if opening /dev/ptmx failed, * we open one master terminal after another, until one that isn't * in use by another program is found. * * Open the devices directory. */ dir = opendir(PTY_DEV_DIR); if(!dir) { fprintf(stderr, "%s: Couldn't open %s (%s)\n", prog, PTY_DEV_DIR, strerror(errno)); return 1; }; /* * Look for pseudo-terminal controller device files in the devices * directory. */ while(*cntrl < 0 && (file = readdir(dir))) { if(strncmp(file->d_name, PTY_CNTRL, sizeof(PTY_CNTRL)-1) == 0) { /* * Get the common extension of the control and slave filenames. */ const char *ext = file->d_name + sizeof(PTY_CNTRL)-1; if(strlen(ext) > PTY_MAX_SUFFIX) continue; /* * Attempt to open the control file. */ strcpy(master_name, PTY_DEV_DIR); strcat(master_name, PTY_CNTRL); strcat(master_name, ext); *cntrl = open(master_name, O_RDWR); if(*cntrl < 0) continue; /* * Attempt to open the matching slave file. */ strcpy(slave_name, PTY_DEV_DIR); strcat(slave_name, PTY_SLAVE); strcat(slave_name, ext); }; }; closedir(dir); #if HAVE_SYSV_PTY }; #endif /* * Did we fail to find a pseudo-terminal pair that we could open? */ if(*cntrl < 0) { fprintf(stderr, "%s: Unable to find a free pseudo-terminal.\n", prog); return 1; }; /* * System V systems require the program that opens the master to * grant access to the slave side of the pseudo-terminal. */ #ifdef HAVE_SYSV_PTY if(grantpt(*cntrl) < 0 || unlockpt(*cntrl) < 0) { fprintf(stderr, "%s: Unable to unlock terminal (%s).\n", prog, strerror(errno)); return 1; }; #endif /* * Success. */ return 0; } /*....................................................................... * Open the slave end of a pseudo-terminal. * * Input: * prog const char * The name of this program. * slave_name char * The filename of the slave device. * Output: * return int The file descriptor of the successfully opened * slave device, or < 0 on error. */ static int pty_open_slave(const char *prog, char *slave_name) { int fd; /* The file descriptor of the slave device */ /* * Place the process in its own process group. In system-V based * OS's, this ensures that when the pseudo-terminal is opened, it * becomes the controlling terminal of the process. */ if(setsid() < 0) { fprintf(stderr, "%s: Unable to form new process group (%s).\n", prog, strerror(errno)); return -1; }; /* * Attempt to open the specified device. */ fd = open(slave_name, O_RDWR); if(fd < 0) { fprintf(stderr, "%s: Unable to open pseudo-terminal slave device (%s).\n", prog, strerror(errno)); return -1; }; /* * On system-V streams based systems, we need to push the stream modules * that implement pseudo-terminal and termio interfaces. At least on * Solaris, which pushes these automatically when a slave is opened, * this is redundant, so ignore errors when pushing the modules. */ #if HAVE_SYSV_PTY (void) ioctl(fd, I_PUSH, "ptem"); (void) ioctl(fd, I_PUSH, "ldterm"); /* * On BSD based systems other than SunOS 4.x, the following makes the * pseudo-terminal the controlling terminal of the child process. * According to the pseudo-terminal example code in Steven's * Advanced programming in the unix environment, the !defined(CIBAUD) * part of the clause prevents this from being used under SunOS. Since * I only have his code with me, and won't have access to the book, * I don't know why this is necessary. */ #elif defined(TIOCSCTTY) && !defined(CIBAUD) if(ioctl(fd, TIOCSCTTY, (char *) 0) < 0) { fprintf(stderr, "%s: Unable to establish controlling terminal (%s).\n", prog, strerror(errno)); close(fd); return -1; }; #endif return fd; } /*....................................................................... * Read input from the controlling terminal of the program, using * gl_get_line(), and feed it to the user's program running in a child * process, via the controller side of the pseudo-terminal. Also pass * data received from the user's program via the conroller end of * the pseudo-terminal, to stdout. * * Input: * prog const char * The name of this program. * cntrl int The file descriptor of the controller end of the * pseudo-terminal. * Output: * return int 0 - OK. * 1 - Error. */ static int pty_parent(const char *prog, int cntrl) { GetLine *gl = NULL; /* The gl_get_line() resource object */ char *line; /* An input line read from the user */ char *rbuff=NULL; /* A buffer for reading from the pseudo terminal */ /* * Allocate the gl_get_line() resource object. */ gl = new_GetLine(PTY_MAX_LINE, PTY_HIST_SIZE); if(!gl) return pty_stop_parent(1, cntrl, gl, rbuff); /* * Allocate a buffer to use to accumulate bytes read from the * pseudo-terminal. */ rbuff = (char *) malloc(PTY_MAX_READ+1); if(!rbuff) return pty_stop_parent(1, cntrl, gl, rbuff); rbuff[0] = '\0'; /* * Register an event handler to watch for data appearing from the * user's program on the controller end of the pseudo terminal. */ if(gl_watch_fd(gl, cntrl, GLFD_READ, pty_read_from_program, rbuff)) return pty_stop_parent(1, cntrl, gl, rbuff); /* * Read input lines from the user and pass them on to the user's program, * by writing to the controller end of the pseudo-terminal. */ while((line=gl_get_line(gl, rbuff, NULL, 0))) { if(pty_write_to_fd(cntrl, line, strlen(line))) return pty_stop_parent(1, cntrl, gl, rbuff); rbuff[0] = '\0'; }; return pty_stop_parent(0, cntrl, gl, rbuff); } /*....................................................................... * This is a private return function of pty_parent(), used to release * dynamically allocated resources, close the controller end of the * pseudo-terminal, and wait for the child to exit. It returns the * exit status of the child process, unless the caller reports an * error itself, in which case the caller's error status is returned. * * Input: * waserr int True if the caller is calling this function because * an error occured. * cntrl int The file descriptor of the controller end of the * pseudo-terminal. * gl GetLine * The resource object of gl_get_line(). * rbuff char * The buffer used to accumulate bytes read from * the pseudo-terminal. * Output: * return int The desired exit status of the program. */ static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff) { int status; /* The return status of the child process */ /* * Close the controller end of the terminal. */ close(cntrl); /* * Delete the resource object. */ gl = del_GetLine(gl); /* * Delete the read buffer. */ if(rbuff) free(rbuff); /* * Wait for the user's program to end. */ (void) wait(&status); /* * Return either our error status, or the return status of the child * program. */ return waserr ? 1 : status; } /*....................................................................... * Run the user's program, with its stdin and stdout connected to the * slave end of the psuedo-terminal. * * Input: * prog const char * The name of this program. * slave int The file descriptor of the slave end of the * pseudo terminal. * argv char *[] The argument vector to pass to the user's program, * where argv[0] is the name of the user's program, * and the last argument is followed by a pointer * to NULL. * Output: * return int If this function returns at all, an error must * have occured when trying to overlay the process * with the user's program. In this case 1 is * returned. */ static int pty_child(const char *prog, int slave, char *argv[]) { struct termios attr; /* The terminal attributes */ /* * We need to stop the pseudo-terminal from echoing everything that we send it. */ if(tcgetattr(slave, &attr)) { fprintf(stderr, "%s: Can't get pseudo-terminal attributes (%s).\n", prog, strerror(errno)); return 1; }; attr.c_lflag &= ~(ECHO); while(tcsetattr(slave, TCSADRAIN, &attr)) { if(errno != EINTR) { fprintf(stderr, "%s: tcsetattr error: %s\n", prog, strerror(errno)); return 1; }; }; /* * Arrange for stdin, stdout and stderr to be connected to the slave device, * ignoring errors that imply that either stdin or stdout is closed. */ while(dup2(slave, STDIN_FILENO) < 0 && errno==EINTR) ; while(dup2(slave, STDOUT_FILENO) < 0 && errno==EINTR) ; while(dup2(slave, STDERR_FILENO) < 0 && errno==EINTR) ; /* * Run the user's program. */ if(execvp(argv[0], argv) < 0) { fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[0], strerror(errno)); fflush(stderr); _exit(1); }; return 0; /* This should never be reached */ } /*....................................................................... * This is the event-handler that is called by gl_get_line() whenever * there is tet waiting to be read from the user's program, via the * controller end of the pseudo-terminal. See libtecla.h for details * about its arguments. */ static GL_FD_EVENT_FN(pty_read_from_program) { char *nlptr; /* A pointer to the last newline in the accumulated string */ char *crptr; /* A pointer to the last '\r' in the accumulated string */ char *nextp; /* A pointer to the next unprocessed character */ /* * Get the read buffer in which we are accumulating a line to be * forwarded to stdout. */ char *rbuff = (char *) data; /* * New data may arrive while we are processing the current read, and * it is more efficient to display this here than to keep returning to * gl_get_line() and have it display the latest prefix as a prompt, * followed by the current input line, so we loop, delaying a bit at * the end of each iteration to check for more data arriving from * the application, before finally returning to gl_get_line() when * no more input is available. */ do { /* * Get the current length of the output string. */ int len = strlen(rbuff); /* * Read the text from the program. */ int nnew = read(fd, rbuff + len, PTY_MAX_READ - len); if(nnew < 0) return GLFD_ABORT; len += nnew; /* * Nul terminate the accumulated string. */ rbuff[len] = '\0'; /* * Find the last newline and last carriage return in the buffer, if any. */ nlptr = strrchr(rbuff, '\n'); crptr = strrchr(rbuff, '\r'); /* * We want to output up to just before the last newline or carriage * return. If there are no newlines of carriage returns in the line, * and the buffer is full, then we should output the whole line. In * all cases a new output line will be started after the latest text * has been output. The intention is to leave any incomplete line * in the buffer, for (perhaps temporary) use as the current prompt. */ if(nlptr) { nextp = crptr && crptr < nlptr ? crptr : nlptr; } else if(crptr) { nextp = crptr; } else if(len >= PTY_MAX_READ) { nextp = rbuff + len; } else { nextp = NULL; }; /* * Do we have any text to output yet? */ if(nextp) { /* * If there was already some text in rbuff before this function * was called, then it will have been used as a prompt. Arrange * to rewrite this prefix, plus the new suffix, by moving back to * the start of the line. */ if(len > 0) (void) pty_write_to_fd(STDOUT_FILENO, "\r", 1); /* * Write everything up to the last newline to stdout. */ (void) pty_write_to_fd(STDOUT_FILENO, rbuff, nextp - rbuff); /* * Start a new line. */ (void) pty_write_to_fd(STDOUT_FILENO, "\r\n", 2); /* * Skip trailing carriage returns and newlines. */ while(*nextp=='\n' || *nextp=='\r') nextp++; /* * Move any unwritten text following the newline, to the start of the * buffer. */ memmove(rbuff, nextp, len - (nextp - rbuff) + 1); }; } while(pty_master_readable(fd, PTY_READ_TIMEOUT)); /* * Make the incomplete line in the output buffer the current prompt. */ gl_replace_prompt(gl, rbuff); return GLFD_REFRESH; } /*....................................................................... * Write a given string to a specified file descriptor. * * Input: * fd int The file descriptor to write to. * string const char * The string to write (of at least 'n' characters). * n int The number of characters to write. * Output: * return int 0 - OK. * 1 - Error. */ static int pty_write_to_fd(int fd, const char *string, int n) { int ndone = 0; /* The number of characters written so far */ /* * Do as many writes as are needed to write the whole string. */ while(ndone < n) { int nnew = write(fd, string + ndone, n - ndone); if(nnew > 0) ndone += nnew; else if(errno != EINTR) return 1; }; return 0; } /*....................................................................... * This is the signal handler that is called when the child process * that is running the user's program exits for any reason. It closes * the slave end of the terminal, so that gl_get_line() in the parent * process sees an end of file. */ static void pty_child_exited(int sig) { raise(SIGINT); } /*....................................................................... * Return non-zero after a given amount of time if there is data waiting * to be read from a given file descriptor. * * Input: * fd int The descriptor to watch. * usec long The number of micro-seconds to wait for input to * arrive before giving up. * Output: * return int 0 - No data is waiting to be read (or select isn't * available). * 1 - Data is waiting to be read. */ static int pty_master_readable(int fd, long usec) { #if HAVE_SELECT fd_set rfds; /* The set of file descriptors to check */ struct timeval timeout; /* The timeout */ FD_ZERO(&rfds); FD_SET(fd, &rfds); timeout.tv_sec = 0; timeout.tv_usec = usec; return select(fd+1, &rfds, NULL, NULL, &timeout) == 1; #else return 0; #endif } yuma123_2.14/libtecla/strngmem.h0000664000175000017500000000626714770023131016722 0ustar vladimirvladimir#ifndef stringmem_h #define stringmem_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ typedef struct StringMem StringMem; /* * Applications that dynamically allocate lots of small strings * run the risk of significantly fragmenting the heap. This module * aims to reduce this risk by allocating large arrays of small fixed * length strings, arranging them as a free-list and allowing * callers to allocate from the list. Strings that are too long * to be allocated from the free-list are allocated from the heap. * Since typical implementations of malloc() eat up a minimum of * 16 bytes per call to malloc() [because of alignment and space * management constraints] it makes sense to set the free-list * string size to 16 bytes. Note that unlike malloc() which typically * keeps 8 bytes per allocation for its own use, our allocator will * return all but one of the 16 bytes for use. One hidden byte of overhead * is reserved for flagging whether the string was allocated directly * from malloc or from the free-list. */ /* * Set the length of each free-list string. The longest string that * will be returned without calling malloc() will be one less than * this number. */ #define SM_STRLEN 16 /* * Create a string free-list container and the first block of its free-list. */ StringMem *_new_StringMem(unsigned blocking_factor); /* * Delete a string free-list. */ StringMem *_del_StringMem(StringMem *sm, int force); /* * Allocate an array of 'length' chars. */ char *_new_StringMemString(StringMem *sm, size_t size); /* * Free a string that was previously returned by _new_StringMemString(). */ char *_del_StringMemString(StringMem *sm, char *s); #endif yuma123_2.14/libtecla/configure0000775000175000017500000044472714770023131016633 0ustar vladimirvladimir#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.57. # # Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 # Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi # Support unset when possible. if (FOO=FOO; unset FOO) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" # Sed expression to map a string onto a valid variable name. as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` exec 6>&1 # # Initializations. # ac_default_prefix=/usr/local ac_config_libobj_dir=. cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Maximum number of lines to put in a shell here document. # This variable seems obsolete. It should probably be removed, and # only ac_max_sed_lines should be used. : ${ac_max_here_lines=38} # Identity of this package. PACKAGE_NAME= PACKAGE_TARNAME= PACKAGE_VERSION= PACKAGE_STRING= PACKAGE_BUGREPORT= ac_unique_file="getline.c" # Factoring default headers for most tests. ac_includes_default="\ #include #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if STDC_HEADERS # include # include #else # if HAVE_STDLIB_H # include # endif #endif #if HAVE_STRING_H # if !STDC_HEADERS && HAVE_MEMORY_H # include # endif # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_INTTYPES_H # include #else # if HAVE_STDINT_H # include # endif #endif #if HAVE_UNISTD_H # include #endif" ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS MAJOR_VER MINOR_VER MICRO_VER CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT SET_MAKE LN_S AWK RANLIB ac_ct_RANLIB LD ac_ct_LD build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os CPP EGREP TARGETS SHARED_EXT SHARED_ALT SHARED_CFLAGS LINK_SHARED DEFS_R LIBR_MANDIR LIBR_MANEXT FUNC_MANDIR FUNC_MANEXT PROG_MANDIR PROG_MANEXT MISC_MANDIR MISC_MANEXT FILE_MANDIR FILE_MANEXT TARGET_LIBS MAKE_MAN_PAGES LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. ac_init_help= ac_init_version=false # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datadir='${prefix}/share' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' libdir='${exec_prefix}/lib' includedir='${prefix}/include' oldincludedir='/usr/include' infodir='${prefix}/info' mandir='${prefix}/man' ac_prev= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval "$ac_prev=\$ac_option" ac_prev= continue fi ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_option in -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad | --data | --dat | --da) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ | --da=*) datadir=$ac_optarg ;; -disable-* | --disable-*) ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` eval "enable_$ac_feature=no" ;; -enable-* | --enable-*) ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "enable_$ac_feature='$ac_optarg'" ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst \ | --locals | --local | --loca | --loc | --lo) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* \ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package| sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "with_$ac_package='$ac_optarg'" ;; -without-* | --without-*) ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package | sed 's/-/_/g'` eval "with_$ac_package=no" ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) { echo "$as_me: error: unrecognized option: $ac_option Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 { (exit 1); exit 1; }; } ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` eval "$ac_envvar='$ac_optarg'" export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` { echo "$as_me: error: missing argument to $ac_option" >&2 { (exit 1); exit 1; }; } fi # Be sure to have absolute paths. for ac_var in exec_prefix prefix do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* | NONE | '' ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # Be sure to have absolute paths. for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ localstatedir libdir includedir oldincludedir infodir mandir do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then its parent. ac_confdir=`(dirname "$0") 2>/dev/null || $as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$0" : 'X\(//\)[^/]' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$0" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` srcdir=$ac_confdir if test ! -r $srcdir/$ac_unique_file; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r $srcdir/$ac_unique_file; then if test "$ac_srcdir_defaulted" = yes; then { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 { (exit 1); exit 1; }; } else { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 { (exit 1); exit 1; }; } fi fi (cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 { (exit 1); exit 1; }; } srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` ac_env_build_alias_set=${build_alias+set} ac_env_build_alias_value=$build_alias ac_cv_env_build_alias_set=${build_alias+set} ac_cv_env_build_alias_value=$build_alias ac_env_host_alias_set=${host_alias+set} ac_env_host_alias_value=$host_alias ac_cv_env_host_alias_set=${host_alias+set} ac_cv_env_host_alias_value=$host_alias ac_env_target_alias_set=${target_alias+set} ac_env_target_alias_value=$target_alias ac_cv_env_target_alias_set=${target_alias+set} ac_cv_env_target_alias_value=$target_alias ac_env_CC_set=${CC+set} ac_env_CC_value=$CC ac_cv_env_CC_set=${CC+set} ac_cv_env_CC_value=$CC ac_env_CFLAGS_set=${CFLAGS+set} ac_env_CFLAGS_value=$CFLAGS ac_cv_env_CFLAGS_set=${CFLAGS+set} ac_cv_env_CFLAGS_value=$CFLAGS ac_env_LDFLAGS_set=${LDFLAGS+set} ac_env_LDFLAGS_value=$LDFLAGS ac_cv_env_LDFLAGS_set=${LDFLAGS+set} ac_cv_env_LDFLAGS_value=$LDFLAGS ac_env_CPPFLAGS_set=${CPPFLAGS+set} ac_env_CPPFLAGS_value=$CPPFLAGS ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} ac_cv_env_CPPFLAGS_value=$CPPFLAGS ac_env_CPP_set=${CPP+set} ac_env_CPP_value=$CPP ac_cv_env_CPP_set=${CPP+set} ac_cv_env_CPP_value=$CPP # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] _ACEOF cat <<_ACEOF Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --datadir=DIR read-only architecture-independent data [PREFIX/share] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --infodir=DIR info documentation [PREFIX/info] --mandir=DIR man documentation [PREFIX/man] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] --target=TARGET configure for building compilers for TARGET [HOST] _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-file-actions Should users of gl_get_line() have access to the filesystem (default=yes) --with-file-system Does the target have a filesystem (default=yes) --with-man-pages Are man pages desired (default=yes) Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. _ACEOF fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. ac_popdir=`pwd` for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d $ac_dir || continue ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be # absolute. ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` cd $ac_dir # Check for guested configure; otherwise get Cygnus style configure. if test -f $ac_srcdir/configure.gnu; then echo $SHELL $ac_srcdir/configure.gnu --help=recursive elif test -f $ac_srcdir/configure; then echo $SHELL $ac_srcdir/configure --help=recursive elif test -f $ac_srcdir/configure.ac || test -f $ac_srcdir/configure.in; then echo $ac_configure --help else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi cd $ac_popdir done fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit 0 fi exec 5>config.log cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.57. Invocation command line was $ $0 $@ _ACEOF { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` hostinfo = `(hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. echo "PATH: $as_dir" done } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_sep= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; 2) ac_configure_args1="$ac_configure_args1 '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" # Get rid of the leading space. ac_sep=" " ;; esac done done $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Be sure not to use single quotes in there, as some shells, # such as our DU 5.0 friend, will then `close' the trap. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo cat <<\_ASBOX ## ---------------- ## ## Cache variables. ## ## ---------------- ## _ASBOX echo # The following way of writing the cache mishandles newlines in values, { (set) 2>&1 | case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in *ac_space=\ *) sed -n \ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" ;; *) sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } echo cat <<\_ASBOX ## ----------------- ## ## Output variables. ## ## ----------------- ## _ASBOX echo for ac_var in $ac_subst_vars do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo if test -n "$ac_subst_files"; then cat <<\_ASBOX ## ------------- ## ## Output files. ## ## ------------- ## _ASBOX echo for ac_var in $ac_subst_files do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo fi if test -s confdefs.h; then cat <<\_ASBOX ## ----------- ## ## confdefs.h. ## ## ----------- ## _ASBOX echo sed "/^$/d" confdefs.h | sort echo fi test "$ac_signal" != 0 && echo "$as_me: caught signal $ac_signal" echo "$as_me: exit $exit_status" } >&5 rm -f core core.* *.core && rm -rf conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -rf conftest* confdefs.h # AIX cpp loses on an empty file, so make sure it contains at least a newline. echo >confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer explicitly selected file to automatically selected ones. if test -z "$CONFIG_SITE"; then if test "x$prefix" != xNONE; then CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" else CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special # files actually), so we avoid doing that. if test -f "$cache_file"; then { echo "$as_me:$LINENO: loading cache $cache_file" >&5 echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . $cache_file;; *) . ./$cache_file;; esac fi else { echo "$as_me:$LINENO: creating cache $cache_file" >&5 echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in `(set) 2>&1 | sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val="\$ac_cv_env_${ac_var}_value" eval ac_new_val="\$ac_env_${ac_var}_value" case $ac_old_set,$ac_new_set in set,) { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 echo "$as_me: former value: $ac_old_val" >&2;} { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 echo "$as_me: current value: $ac_new_val" >&2;} ac_cache_corrupted=: fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 echo "$as_me: error: changes in the environment can compromise the build" >&2;} { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu MAJOR_VER="1" MINOR_VER="6" MICRO_VER="1" CFLAGS="$CFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$ac_ct_CC" && break done CC=$ac_ct_CC fi fi test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&5 echo "$as_me: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } # Provide some information about the compiler. echo "$as_me:$LINENO:" \ "checking for C compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 (eval $ac_compiler --version &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 (eval $ac_compiler -v &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 (eval $ac_compiler -V &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. echo "$as_me:$LINENO: checking for C compiler default output" >&5 echo $ECHO_N "checking for C compiler default output... $ECHO_C" >&6 ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 (eval $ac_link_default) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # Find the output, starting from the most likely. This scheme is # not robust to junk in `.', hence go to wildcards (a.*) only as a last # resort. # Be careful to initialize this variable, since it used to be cached. # Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. ac_cv_exeext= # b.out is created by i960 compilers. for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; conftest.$ac_ext ) # This is the source file. ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` # FIXME: I believe we export ac_cv_exeext for Libtool, # but it would be cool to find out if it's true. Does anybody # maintain Libtool? --akim. export ac_cv_exeext break;; * ) break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: C compiler cannot create executables See \`config.log' for more details." >&5 echo "$as_me: error: C compiler cannot create executables See \`config.log' for more details." >&2;} { (exit 77); exit 77; }; } fi ac_exeext=$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_file" >&5 echo "${ECHO_T}$ac_file" >&6 # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether the C compiler works" >&5 echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 # FIXME: These cross compiler hacks should be removed for Autoconf 3.0 # If not cross compiling, check that we can run a simple program. if test "$cross_compiling" != yes; then if { ac_try='./$ac_file' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { echo "$as_me:$LINENO: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&5 echo "$as_me: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi fi fi echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 rm -f a.out a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 echo "$as_me:$LINENO: result: $cross_compiling" >&5 echo "${ECHO_T}$cross_compiling" >&6 echo "$as_me:$LINENO: checking for suffix of executables" >&5 echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` export ac_cv_exeext break;; * ) break;; esac done else { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 echo "${ECHO_T}$ac_cv_exeext" >&6 rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT echo "$as_me:$LINENO: checking for suffix of object files" >&5 echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 if test "${ac_cv_objext+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 echo "${ECHO_T}$ac_cv_objext" >&6 OBJEXT=$ac_cv_objext ac_objext=$OBJEXT echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 if test "${ac_cv_c_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 GCC=`test $ac_compiler_gnu = yes && echo yes` ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS CFLAGS="-g" echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 if test "${ac_cv_prog_cc_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_prog_cc_g=no fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 if test "${ac_cv_prog_cc_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_prog_cc_stdc=no ac_save_CC=$CC cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF # Don't try gcc -ansi; that turns off useful extensions and # breaks some systems' header files. # AIX -qlanglvl=ansi # Ultrix and OSF/1 -std1 # HP-UX 10.20 and later -Ae # HP-UX older versions -Aa -D_HPUX_SOURCE # SVR4 -Xc -D__EXTENSIONS__ for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_stdc=$ac_arg break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.$ac_objext done rm -f conftest.$ac_ext conftest.$ac_objext CC=$ac_save_CC fi case "x$ac_cv_prog_cc_stdc" in x|xno) echo "$as_me:$LINENO: result: none needed" >&5 echo "${ECHO_T}none needed" >&6 ;; *) echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 CC="$CC $ac_cv_prog_cc_stdc" ;; esac # Some people use a C++ compiler to compile C. Since we use `exit', # in C++ we need to declare it. In case someone uses the same compiler # for both compiling C and C++ we need to have the C++ compiler decide # the declaration of exit, since it's the most demanding environment. cat >conftest.$ac_ext <<_ACEOF #ifndef __cplusplus choke me #endif _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then for ac_declaration in \ ''\ '#include ' \ 'extern "C" void std::exit (int) throw (); using std::exit;' \ 'extern "C" void std::exit (int); using std::exit;' \ 'extern "C" void exit (int) throw ();' \ 'extern "C" void exit (int);' \ 'void exit (int);' do cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include $ac_declaration int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 continue fi rm -f conftest.$ac_objext conftest.$ac_ext cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.$ac_objext conftest.$ac_ext done rm -f conftest* if test -n "$ac_declaration"; then echo '#ifdef __cplusplus' >>confdefs.h echo $ac_declaration >>confdefs.h echo '#endif' >>confdefs.h fi else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,./+-,__p_,'` if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.make <<\_ACEOF all: @echo 'ac_maketemp="$(MAKE)"' _ACEOF # GNU make sometimes prints "make[1]: Entering...", which would confuse us. eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` if test -n "$ac_maketemp"; then eval ac_cv_prog_make_${ac_make}_set=yes else eval ac_cv_prog_make_${ac_make}_set=no fi rm -f conftest.make fi if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 SET_MAKE= else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 SET_MAKE="MAKE=${MAKE-make}" fi echo "$as_me:$LINENO: checking whether ln -s works" >&5 echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6 LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 else echo "$as_me:$LINENO: result: no, using $LN_S" >&5 echo "${ECHO_T}no, using $LN_S" >&6 fi for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_AWK+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then echo "$as_me:$LINENO: result: $AWK" >&5 echo "${ECHO_T}$AWK" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$AWK" && break done if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_RANLIB+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then echo "$as_me:$LINENO: result: $RANLIB" >&5 echo "${ECHO_T}$RANLIB" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 echo "${ECHO_T}$ac_ct_RANLIB" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi RANLIB=$ac_ct_RANLIB else RANLIB="$ac_cv_prog_RANLIB" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ld", so it can be a program name with args. set dummy ${ac_tool_prefix}ld; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_LD+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$LD"; then ac_cv_prog_LD="$LD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_LD="${ac_tool_prefix}ld" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi LD=$ac_cv_prog_LD if test -n "$LD"; then echo "$as_me:$LINENO: result: $LD" >&5 echo "${ECHO_T}$LD" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_LD"; then ac_ct_LD=$LD # Extract the first word of "ld", so it can be a program name with args. set dummy ld; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_LD+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_LD"; then ac_cv_prog_ac_ct_LD="$ac_ct_LD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_LD="ld" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_LD=$ac_cv_prog_ac_ct_LD if test -n "$ac_ct_LD"; then echo "$as_me:$LINENO: result: $ac_ct_LD" >&5 echo "${ECHO_T}$ac_ct_LD" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi LD=$ac_ct_LD else LD="$ac_cv_prog_LD" fi ac_aux_dir= for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do if test -f $ac_dir/install-sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f $ac_dir/install.sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f $ac_dir/shtool; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} { (exit 1); exit 1; }; } fi ac_config_guess="$SHELL $ac_aux_dir/config.guess" ac_config_sub="$SHELL $ac_aux_dir/config.sub" ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. # Make sure we can run config.sub. $ac_config_sub sun4 >/dev/null 2>&1 || { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5 echo "$as_me: error: cannot run $ac_config_sub" >&2;} { (exit 1); exit 1; }; } echo "$as_me:$LINENO: checking build system type" >&5 echo $ECHO_N "checking build system type... $ECHO_C" >&6 if test "${ac_cv_build+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_build_alias=$build_alias test -z "$ac_cv_build_alias" && ac_cv_build_alias=`$ac_config_guess` test -z "$ac_cv_build_alias" && { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 echo "$as_me: error: cannot guess build type; you must specify one" >&2;} { (exit 1); exit 1; }; } ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5 echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_build" >&5 echo "${ECHO_T}$ac_cv_build" >&6 build=$ac_cv_build build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$as_me:$LINENO: checking host system type" >&5 echo $ECHO_N "checking host system type... $ECHO_C" >&6 if test "${ac_cv_host+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_host_alias=$host_alias test -z "$ac_cv_host_alias" && ac_cv_host_alias=$ac_cv_build_alias ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5 echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_host" >&5 echo "${ECHO_T}$ac_cv_host" >&6 host=$ac_cv_host host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$as_me:$LINENO: checking target system type" >&5 echo $ECHO_N "checking target system type... $ECHO_C" >&6 if test "${ac_cv_target+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_target_alias=$target_alias test "x$ac_cv_target_alias" = "x" && ac_cv_target_alias=$ac_cv_host_alias ac_cv_target=`$ac_config_sub $ac_cv_target_alias` || { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_target_alias failed" >&5 echo "$as_me: error: $ac_config_sub $ac_cv_target_alias failed" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_target" >&5 echo "${ECHO_T}$ac_cv_target" >&6 target=$ac_cv_target target_cpu=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` target_vendor=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` target_os=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` # The aliases save the names the user supplied, while $host etc. # will get canonicalized. test -n "$target_alias" && test "$program_prefix$program_suffix$program_transform_name" = \ NONENONEs,x,x, && program_prefix=${target_alias}- case $target_os in solaris2.[0-6]|solaris2.[0-6].*) LIBS="$LIBS -L/usr/ccs/lib" ;; esac if test "$GCC"_ = "yes"_; then touch foo.c fix=`$CC -E -Wp,-v foo.c 2>&1 | $AWK ' /^#include <...> search starts here:/ {in_list=1;ndir=0} / *\// && in_list {path[ndir++] = $1} /^End of search list/ {in_list=0} END { if(path[0] ~ /\/usr\/local\/include/) { for(dir=1; dir&5 echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test "${ac_cv_prog_CPP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi echo "$as_me:$LINENO: result: $CPP" >&5 echo "${ECHO_T}$CPP" >&6 ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&5 echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu echo "$as_me:$LINENO: checking for egrep" >&5 echo $ECHO_N "checking for egrep... $ECHO_C" >&6 if test "${ac_cv_prog_egrep+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if echo a | (grep -E '(a|b)') >/dev/null 2>&1 then ac_cv_prog_egrep='grep -E' else ac_cv_prog_egrep='egrep' fi fi echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 echo "${ECHO_T}$ac_cv_prog_egrep" >&6 EGREP=$ac_cv_prog_egrep echo "$as_me:$LINENO: checking for ANSI C header files" >&5 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 if test "${ac_cv_header_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_stdc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdc=no fi rm -f conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 echo "${ECHO_T}$ac_cv_header_stdc" >&6 if test $ac_cv_header_stdc = yes; then cat >>confdefs.h <<\_ACEOF #define STDC_HEADERS 1 _ACEOF fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking for tigetstr in -lcurses" >&5 echo $ECHO_N "checking for tigetstr in -lcurses... $ECHO_C" >&6 if test "${ac_cv_lib_curses_tigetstr+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcurses $LIBS" cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char tigetstr (); int main () { tigetstr (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_curses_tigetstr=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_curses_tigetstr=no fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_curses_tigetstr" >&5 echo "${ECHO_T}$ac_cv_lib_curses_tigetstr" >&6 if test $ac_cv_lib_curses_tigetstr = yes; then cat >>confdefs.h <<\_ACEOF #define USE_TERMINFO 1 _ACEOF LIBS="$LIBS -lcurses" else echo "$as_me:$LINENO: checking for tigetstr in -lncurses" >&5 echo $ECHO_N "checking for tigetstr in -lncurses... $ECHO_C" >&6 if test "${ac_cv_lib_ncurses_tigetstr+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lncurses $LIBS" cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char tigetstr (); int main () { tigetstr (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_ncurses_tigetstr=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_ncurses_tigetstr=no fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_ncurses_tigetstr" >&5 echo "${ECHO_T}$ac_cv_lib_ncurses_tigetstr" >&6 if test $ac_cv_lib_ncurses_tigetstr = yes; then cat >>confdefs.h <<\_ACEOF #define USE_TERMINFO 1 _ACEOF LIBS="$LIBS -lncurses" else echo "$as_me:$LINENO: checking for tgetstr in -lcurses" >&5 echo $ECHO_N "checking for tgetstr in -lcurses... $ECHO_C" >&6 if test "${ac_cv_lib_curses_tgetstr+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcurses $LIBS" cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char tgetstr (); int main () { tgetstr (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_curses_tgetstr=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_curses_tgetstr=no fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_curses_tgetstr" >&5 echo "${ECHO_T}$ac_cv_lib_curses_tgetstr" >&6 if test $ac_cv_lib_curses_tgetstr = yes; then cat >>confdefs.h <<\_ACEOF #define USE_TERMCAP 1 _ACEOF LIBS="$LIBS -lcurses" if test "${ac_cv_header_termcap_h+set}" = set; then echo "$as_me:$LINENO: checking for termcap.h" >&5 echo $ECHO_N "checking for termcap.h... $ECHO_C" >&6 if test "${ac_cv_header_termcap_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: $ac_cv_header_termcap_h" >&5 echo "${ECHO_T}$ac_cv_header_termcap_h" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking termcap.h usability" >&5 echo $ECHO_N "checking termcap.h usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking termcap.h presence" >&5 echo $ECHO_N "checking termcap.h presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc in yes:no ) { echo "$as_me:$LINENO: WARNING: termcap.h: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: termcap.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: termcap.h: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: termcap.h: proceeding with the preprocessor's result" >&2;} ( cat <<\_ASBOX ## ------------------------------------ ## ## Report this to bug-autoconf@gnu.org. ## ## ------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; no:yes ) { echo "$as_me:$LINENO: WARNING: termcap.h: present but cannot be compiled" >&5 echo "$as_me: WARNING: termcap.h: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: termcap.h: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: termcap.h: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: termcap.h: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: termcap.h: proceeding with the preprocessor's result" >&2;} ( cat <<\_ASBOX ## ------------------------------------ ## ## Report this to bug-autoconf@gnu.org. ## ## ------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for termcap.h" >&5 echo $ECHO_N "checking for termcap.h... $ECHO_C" >&6 if test "${ac_cv_header_termcap_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_header_termcap_h=$ac_header_preproc fi echo "$as_me:$LINENO: result: $ac_cv_header_termcap_h" >&5 echo "${ECHO_T}$ac_cv_header_termcap_h" >&6 fi if test $ac_cv_header_termcap_h = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_TERMCAP_H 1 _ACEOF fi fi fi fi for ac_header in curses.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc in yes:no ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} ( cat <<\_ASBOX ## ------------------------------------ ## ## Report this to bug-autoconf@gnu.org. ## ## ------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; no:yes ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} ( cat <<\_ASBOX ## ------------------------------------ ## ## Report this to bug-autoconf@gnu.org. ## ## ------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF for ac_header in term.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done else for ac_header in ncurses/curses.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc in yes:no ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} ( cat <<\_ASBOX ## ------------------------------------ ## ## Report this to bug-autoconf@gnu.org. ## ## ------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; no:yes ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} ( cat <<\_ASBOX ## ------------------------------------ ## ## Report this to bug-autoconf@gnu.org. ## ## ------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF for ac_header in ncurses/term.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done fi done fi done TARGETS="normal reentrant" echo "$as_me:$LINENO: checking for reentrant functions" >&5 echo $ECHO_N "checking for reentrant functions... $ECHO_C" >&6 if test "${tecla_cv_reentrant+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else KEPT_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199506L" cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include #include int main () { (void) readdir_r(NULL, NULL, NULL); (void) getpwuid_r(geteuid(), NULL, NULL, 0, NULL); (void) getpwnam_r(NULL, NULL, NULL, 0, NULL); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then tecla_cv_reentrant=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 tecla_cv_reentrant=no fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext CFLAGS="$KEPT_CFLAGS" fi echo "$as_me:$LINENO: result: $tecla_cv_reentrant" >&5 echo "${ECHO_T}$tecla_cv_reentrant" >&6 if test $tecla_cv_reentrant = no; then TARGETS="normal" fi if test "${ac_cv_header_sys_select_h+set}" = set; then echo "$as_me:$LINENO: checking for sys/select.h" >&5 echo $ECHO_N "checking for sys/select.h... $ECHO_C" >&6 if test "${ac_cv_header_sys_select_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: $ac_cv_header_sys_select_h" >&5 echo "${ECHO_T}$ac_cv_header_sys_select_h" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking sys/select.h usability" >&5 echo $ECHO_N "checking sys/select.h usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking sys/select.h presence" >&5 echo $ECHO_N "checking sys/select.h presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc in yes:no ) { echo "$as_me:$LINENO: WARNING: sys/select.h: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: sys/select.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: sys/select.h: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: sys/select.h: proceeding with the preprocessor's result" >&2;} ( cat <<\_ASBOX ## ------------------------------------ ## ## Report this to bug-autoconf@gnu.org. ## ## ------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; no:yes ) { echo "$as_me:$LINENO: WARNING: sys/select.h: present but cannot be compiled" >&5 echo "$as_me: WARNING: sys/select.h: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: sys/select.h: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: sys/select.h: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: sys/select.h: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: sys/select.h: proceeding with the preprocessor's result" >&2;} ( cat <<\_ASBOX ## ------------------------------------ ## ## Report this to bug-autoconf@gnu.org. ## ## ------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for sys/select.h" >&5 echo $ECHO_N "checking for sys/select.h... $ECHO_C" >&6 if test "${ac_cv_header_sys_select_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_header_sys_select_h=$ac_header_preproc fi echo "$as_me:$LINENO: result: $ac_cv_header_sys_select_h" >&5 echo "${ECHO_T}$ac_cv_header_sys_select_h" >&6 fi if test $ac_cv_header_sys_select_h = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_SYS_SELECT_H 1 _ACEOF fi echo "$as_me:$LINENO: checking for select system call" >&5 echo $ECHO_N "checking for select system call... $ECHO_C" >&6 if test "${tecla_cv_select+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #ifdef HAVE_SYS_SELECT_H #include #endif int main () { fd_set fds; int nready; FD_ZERO(&fds); FD_SET(1, &fds); nready = select(2, &fds, &fds, &fds, NULL); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then tecla_cv_select=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 tecla_cv_select=no fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $tecla_cv_select" >&5 echo "${ECHO_T}$tecla_cv_select" >&6 if test $tecla_cv_select = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_SELECT 1 _ACEOF fi echo "$as_me:$LINENO: checking for SysV pseudo-terminals" >&5 echo $ECHO_N "checking for SysV pseudo-terminals... $ECHO_C" >&6 if test "${tecla_cv_sysv_pty+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include int main () { char *name = ptsname(0); int i1 = grantpt(0); int i2 = unlockpt(0); int i3 = ioctl(0, I_PUSH, "ptem"); return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then tecla_cv_sysv_pty=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 tecla_cv_sysv_pty=no fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $tecla_cv_sysv_pty" >&5 echo "${ECHO_T}$tecla_cv_sysv_pty" >&6 if test $tecla_cv_sysv_pty = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_SYSV_PTY 1 _ACEOF fi SHARED_EXT="" SHARED_ALT="" SHARED_CFLAGS="" LINK_SHARED="" DEFS_R="-D_POSIX_C_SOURCE=199506L -DPREFER_REENTRANT" LIBR_MANDIR="man3" LIBR_MANEXT="3" FUNC_MANDIR="man3" FUNC_MANEXT="3" PROG_MANDIR="man1" PROG_MANEXT="1" MISC_MANDIR="man7" MISC_MANEXT="7" FILE_MANDIR="man5" FILE_MANEXT="5" # Check whether --with-file-actions or --without-file-actions was given. if test "${with_file_actions+set}" = set; then withval="$with_file_actions" cat >>confdefs.h <<\_ACEOF #define HIDE_FILE_SYSTEM 1 _ACEOF fi; # Check whether --with-file-system or --without-file-system was given. if test "${with_file_system+set}" = set; then withval="$with_file_system" cat >>confdefs.h <<\_ACEOF #define WITHOUT_FILE_SYSTEM 1 _ACEOF fi; case $target in *solaris*) cat >>confdefs.h <<\_ACEOF #define __EXTENSIONS__ 1 _ACEOF SHARED_EXT=".so.${MAJOR_VER}" SHARED_ALT=".so" LINK_SHARED="$LD"' -G -M $$(srcdir)/libtecla.map -o $$@ -h $$(@F) -z defs -i $$(LIB_OBJECTS) $$(LIBS) -lc' SHARED_CFLAGS="-Kpic" case $CC in */cc|cc) SHARED_CFLAGS="$SHARED_CFLAGS -xstrconst" ;; esac case $target_cpu in sparc) SHARED_CFLAGS="$SHARED_CFLAGS -xregs=no%appl" esac case $target_os in solaris2.[89]*|solaris2.1[0-9]*) LIBR_MANEXT=3lib FUNC_MANEXT=3tecla LIBR_MANDIR=man$LIBR_MANEXT FUNC_MANDIR=man$FUNC_MANEXT esac MISC_MANDIR="man5" MISC_MANEXT="5" FILE_MANDIR="man4" FILE_MANEXT="4" ;; *linux*) SHARED_EXT=".so.${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}" SHARED_ALT=".so .so.${MAJOR_VER}" echo "$as_me:$LINENO: checking for --version-script in GNU ld" >&5 echo $ECHO_N "checking for --version-script in GNU ld... $ECHO_C" >&6 if test "${tecla_cv_gnu_ld_script+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if (echo 'void dummy(void) {return;}' > dummy.c; $CC -c -fpic dummy.c; \ $LD -o dummy.so dummy.o -shared --version-script=$srcdir/libtecla.map) 1>&2 2>/dev/null; then tecla_cv_gnu_ld_script=yes else tecla_cv_gnu_ld_script=no fi rm -f dummy.c dummy.o dummy.so fi echo "$as_me:$LINENO: result: $tecla_cv_gnu_ld_script" >&5 echo "${ECHO_T}$tecla_cv_gnu_ld_script" >&6 if test $tecla_cv_gnu_ld_script = yes; then VERSION_OPT='--version-script=$$(srcdir)/libtecla.map' else VERSION_OPT='' fi LINK_SHARED="$LD"' -o $$@ -soname libtecla$$(SUFFIX).so.'${MAJOR_VER}' -shared '$VERSION_OPT' $$(LIB_OBJECTS) $$(LIBS) -lc' SHARED_CFLAGS="-fpic" CFLAGS="-D_SVID_SOURCE -D_BSD_SOURCE $CFLAGS" ;; *hpux*) SHARED_EXT=".${MAJOR_VER}" SHARED_ALT=".sl" LINK_SHARED="$LD"' -b +h $$(@F) +k +vshlibunsats -o $$@ -c libtecla.map.opt $$(LIB_OBJECTS) $$(LIBS) -lc' SHARED_CFLAGS="+z" MISC_MANEXT=5 FILE_MANEXT=4 MISC_MANDIR=man$MISC_MANEXT FILE_MANDIR=man$FILE_MANEXT ;; *darwin*) SHARED_EXT=".${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}.dylib" SHARED_ALT=".dylib .${MAJOR_VER}.dylib" LINK_SHARED='$(CC) -o $$@ -dynamiclib -flat_namespace -undefined suppress -compatibility_version '${MAJOR_VER}.${MINOR_VER}' -current_version '${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}' -install_name '${libdir}'/$$@ $$(LIB_OBJECTS)' SHARED_CFLAGS="" ;; *dec-osf*) cat >>confdefs.h <<\_ACEOF #define _OSF_SOURCE 1 _ACEOF ;; *freebsd*) SHARED_EXT=".so.${MAJOR_VER}" SHARED_ALT=".so" VERSION_OPT='--version-script=$$(srcdir)/libtecla.map' LINK_SHARED='ld -o $$@ -soname libtecla$$(SUFFIX).so.'${MAJOR_VER}' -shared '$VERSION_OPT' $$(LIB_OBJECTS) $$(LIBS) -lc' SHARED_CFLAGS="-fpic" ;; mips-sgi-irix*) DEFS_R="$DEFS_R -D_XOPEN_SOURCE=500" if test "$RANLIB"_ = "_"; then RANLIB=":" fi ;; esac if test "$GCC"_ = "yes"_ && test "$LINK_SHARED"_ != "_" ; then SHARED_CFLAGS="-fpic" case $target in sparc-*-solaris*) SHARED_CFLAGS="$SHARED_CFLAGS -mno-app-regs" ;; *darwin*) SHARED_CFLAGS="" ;; esac LINK_SHARED="$LINK_SHARED `gcc -print-libgcc-file-name`" fi if test "$LINK_SHARED"_ != "_"; then TARGET_LIBS="static shared" else TARGET_LIBS="static" LINK_SHARED="@:" fi # Check whether --with-man-pages or --without-man-pages was given. if test "${with_man_pages+set}" = set; then withval="$with_man_pages" MAKE_MAN_PAGES="$withval" else MAKE_MAN_PAGES="yes" fi; OUTPUT_FILES="Makefile" rm -rf man/man* if test "$MAKE_MAN_PAGES"_ = "yes"_; then for area in libr func misc prog file; do for page in man/$area/*.in; do OUTPUT_FILES="$OUTPUT_FILES `echo $page | sed 's/\.in$//'`" done done fi ac_config_files="$ac_config_files $OUTPUT_FILES" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, don't put newlines in cache variables' values. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. { (set) 2>&1 | case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote # substitution turns \\\\ into \\, and sed turns \\ into \). sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } | sed ' t clear : clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ : end' >>confcache if diff $cache_file confcache >/dev/null 2>&1; then :; else if test -w $cache_file; then test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" cat confcache >$cache_file else echo "not updating unwritable cache $cache_file" fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # VPATH may cause trouble with some makes, so we remove $(srcdir), # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=/{ s/:*\$(srcdir):*/:/; s/:*\${srcdir}:*/:/; s/:*@srcdir@:*/:/; s/^\([^=]*=[ ]*\):*/\1/; s/:*$//; s/^[^=]*=[ ]*$//; }' fi # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then we branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. cat >confdef2opt.sed <<\_ACEOF t clear : clear s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g t quote s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g t quote d : quote s,[ `~#$^&*(){}\\|;'"<>?],\\&,g s,\[,\\&,g s,\],\\&,g s,\$,$$,g p _ACEOF # We use echo to avoid assuming a particular line-breaking character. # The extra dot is to prevent the shell from consuming trailing # line-breaks from the sub-command output. A line-break within # single-quotes doesn't work because, if this script is created in a # platform that uses two characters for line-breaks (e.g., DOS), tr # would break. ac_LF_and_DOT=`echo; echo .` DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'` rm -f confdef2opt.sed ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_i=`echo "$ac_i" | sed 's/\$U\././;s/\.o$//;s/\.obj$//'` # 2. Add them. ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 echo "$as_me: creating $CONFIG_STATUS" >&6;} cat >$CONFIG_STATUS <<_ACEOF #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi # Support unset when possible. if (FOO=FOO; unset FOO) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" # Sed expression to map a string onto a valid variable name. as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH exec 6>&1 # Open the log real soon, to keep \$[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. Logging --version etc. is OK. exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX } >&5 cat >&5 <<_CSEOF This file was extended by $as_me, which was generated by GNU Autoconf 2.57. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ _CSEOF echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 echo >&5 _ACEOF # Files that config.status was made for. if test -n "$ac_config_files"; then echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS fi if test -n "$ac_config_headers"; then echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS fi if test -n "$ac_config_links"; then echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS fi if test -n "$ac_config_commands"; then echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS fi cat >>$CONFIG_STATUS <<\_ACEOF ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. Usage: $0 [OPTIONS] [FILE]... -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.57, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." srcdir=$srcdir _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If no file are specified by the user, then we need to provide default # value. By we need to know if files were specified by the user. ac_need_defaults=: while test $# != 0 do case $1 in --*=*) ac_option=`expr "x$1" : 'x\([^=]*\)='` ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` ac_shift=: ;; -*) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; *) # This is not an option, so the user has probably given explicit # arguments. ac_option=$1 ac_need_defaults=false;; esac case $ac_option in # Handling of the options. _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --vers* | -V ) echo "$ac_cs_version"; exit 0 ;; --he | --h) # Conflict between --help and --header { { echo "$as_me:$LINENO: error: ambiguous option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: ambiguous option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; };; --help | --hel | -h ) echo "$ac_cs_usage"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift CONFIG_FILES="$CONFIG_FILES $ac_optarg" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" ac_need_defaults=false;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: unrecognized option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; } ;; *) ac_config_targets="$ac_config_targets $1" ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF if \$ac_cs_recheck; then echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_config_target in $ac_config_targets do case "$ac_config_target" in # Handling of arguments. "$OUTPUT_FILES" ) CONFIG_FILES="$CONFIG_FILES $OUTPUT_FILES" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason to put it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Create a temporary directory, and hook for its removal unless debugging. $debug || { trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 trap '{ (exit 1); exit 1; }' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./confstat$$-$RANDOM (umask 077 && mkdir $tmp) } || { echo "$me: cannot create a temporary directory in ." >&2 { (exit 1); exit 1; } } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF # # CONFIG_FILES section. # # No need to generate the scripts if there are no CONFIG_FILES. # This happens for instance when ./config.status config.h if test -n "\$CONFIG_FILES"; then # Protect against being on the right side of a sed subst in config.status. sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF s,@SHELL@,$SHELL,;t t s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t s,@exec_prefix@,$exec_prefix,;t t s,@prefix@,$prefix,;t t s,@program_transform_name@,$program_transform_name,;t t s,@bindir@,$bindir,;t t s,@sbindir@,$sbindir,;t t s,@libexecdir@,$libexecdir,;t t s,@datadir@,$datadir,;t t s,@sysconfdir@,$sysconfdir,;t t s,@sharedstatedir@,$sharedstatedir,;t t s,@localstatedir@,$localstatedir,;t t s,@libdir@,$libdir,;t t s,@includedir@,$includedir,;t t s,@oldincludedir@,$oldincludedir,;t t s,@infodir@,$infodir,;t t s,@mandir@,$mandir,;t t s,@build_alias@,$build_alias,;t t s,@host_alias@,$host_alias,;t t s,@target_alias@,$target_alias,;t t s,@DEFS@,$DEFS,;t t s,@ECHO_C@,$ECHO_C,;t t s,@ECHO_N@,$ECHO_N,;t t s,@ECHO_T@,$ECHO_T,;t t s,@LIBS@,$LIBS,;t t s,@MAJOR_VER@,$MAJOR_VER,;t t s,@MINOR_VER@,$MINOR_VER,;t t s,@MICRO_VER@,$MICRO_VER,;t t s,@CC@,$CC,;t t s,@CFLAGS@,$CFLAGS,;t t s,@LDFLAGS@,$LDFLAGS,;t t s,@CPPFLAGS@,$CPPFLAGS,;t t s,@ac_ct_CC@,$ac_ct_CC,;t t s,@EXEEXT@,$EXEEXT,;t t s,@OBJEXT@,$OBJEXT,;t t s,@SET_MAKE@,$SET_MAKE,;t t s,@LN_S@,$LN_S,;t t s,@AWK@,$AWK,;t t s,@RANLIB@,$RANLIB,;t t s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t s,@LD@,$LD,;t t s,@ac_ct_LD@,$ac_ct_LD,;t t s,@build@,$build,;t t s,@build_cpu@,$build_cpu,;t t s,@build_vendor@,$build_vendor,;t t s,@build_os@,$build_os,;t t s,@host@,$host,;t t s,@host_cpu@,$host_cpu,;t t s,@host_vendor@,$host_vendor,;t t s,@host_os@,$host_os,;t t s,@target@,$target,;t t s,@target_cpu@,$target_cpu,;t t s,@target_vendor@,$target_vendor,;t t s,@target_os@,$target_os,;t t s,@CPP@,$CPP,;t t s,@EGREP@,$EGREP,;t t s,@TARGETS@,$TARGETS,;t t s,@SHARED_EXT@,$SHARED_EXT,;t t s,@SHARED_ALT@,$SHARED_ALT,;t t s,@SHARED_CFLAGS@,$SHARED_CFLAGS,;t t s,@LINK_SHARED@,$LINK_SHARED,;t t s,@DEFS_R@,$DEFS_R,;t t s,@LIBR_MANDIR@,$LIBR_MANDIR,;t t s,@LIBR_MANEXT@,$LIBR_MANEXT,;t t s,@FUNC_MANDIR@,$FUNC_MANDIR,;t t s,@FUNC_MANEXT@,$FUNC_MANEXT,;t t s,@PROG_MANDIR@,$PROG_MANDIR,;t t s,@PROG_MANEXT@,$PROG_MANEXT,;t t s,@MISC_MANDIR@,$MISC_MANDIR,;t t s,@MISC_MANEXT@,$MISC_MANEXT,;t t s,@FILE_MANDIR@,$FILE_MANDIR,;t t s,@FILE_MANEXT@,$FILE_MANEXT,;t t s,@TARGET_LIBS@,$TARGET_LIBS,;t t s,@MAKE_MAN_PAGES@,$MAKE_MAN_PAGES,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t CEOF _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # Split the substitutions into bite-sized pieces for seds with # small command number limits, like on Digital OSF/1 and HP-UX. ac_max_sed_lines=48 ac_sed_frag=1 # Number of current file. ac_beg=1 # First line for current file. ac_end=$ac_max_sed_lines # Line after last line for current file. ac_more_lines=: ac_sed_cmds= while $ac_more_lines; do if test $ac_beg -gt 1; then sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag else sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag fi if test ! -s $tmp/subs.frag; then ac_more_lines=false else # The purpose of the label and of the branching condition is to # speed up the sed processing (if there are no `@' at all, there # is no need to browse any of the substitutions). # These are the two extra sed commands mentioned above. (echo ':t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed if test -z "$ac_sed_cmds"; then ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" else ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" fi ac_sed_frag=`expr $ac_sed_frag + 1` ac_beg=$ac_end ac_end=`expr $ac_end + $ac_max_sed_lines` fi done if test -z "$ac_sed_cmds"; then ac_sed_cmds=cat fi fi # test -n "$CONFIG_FILES" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case $ac_file in - | *:- | *:-:* ) # input from stdin cat >$tmp/stdin ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; * ) ac_file_in=$ac_file.in ;; esac # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. ac_dir=`(dirname "$ac_file") 2>/dev/null || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be # absolute. ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` if test x"$ac_file" != x-; then { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} rm -f "$ac_file" fi # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ if test x"$ac_file" = x-; then configure_input= else configure_input="$ac_file. " fi configure_input=$configure_input"Generated from `echo $ac_file_in | sed 's,.*/,,'` by configure." # First look for the input files in the build tree, otherwise in the # src tree. ac_file_inputs=`IFS=: for f in $ac_file_in; do case $f in -) echo $tmp/stdin ;; [\\/$]*) # Absolute (can't be DOS-style, as IFS=:) test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } echo $f;; *) # Relative if test -f "$f"; then # Build tree echo $f elif test -f "$srcdir/$f"; then # Source tree echo $srcdir/$f else # /dev/null tree { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } fi;; esac done` || { (exit 1); exit 1; } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF sed "$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s,@configure_input@,$configure_input,;t t s,@srcdir@,$ac_srcdir,;t t s,@abs_srcdir@,$ac_abs_srcdir,;t t s,@top_srcdir@,$ac_top_srcdir,;t t s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t s,@builddir@,$ac_builddir,;t t s,@abs_builddir@,$ac_abs_builddir,;t t s,@top_builddir@,$ac_top_builddir,;t t s,@abs_top_builddir@,$ac_abs_top_builddir,;t t " $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out rm -f $tmp/stdin if test x"$ac_file" != x-; then mv $tmp/out $ac_file else cat $tmp/out rm -f $tmp/out fi done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF { (exit 0); exit 0; } _ACEOF chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || { (exit 1); exit 1; } fi yuma123_2.14/libtecla/direader.c0000664000175000017500000002011414770023131016623 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * If file-system access is to be excluded, this module has no function, * so all of its code should be excluded. */ #ifndef WITHOUT_FILE_SYSTEM /* * Standard includes. */ #include #include #include #include /* * Operating system includes. */ #include #include #include #include #include "direader.h" #include "errmsg.h" /* * Use the reentrant POSIX threads version of readdir()? */ #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L #define USE_READDIR_R 1 #endif /* * Objects of the following type are used to maintain the resources * needed to read directories. */ struct DirReader { ErrMsg *err; /* The error reporting buffer */ DIR *dir; /* The directory stream (if open, NULL otherwise) */ struct dirent *file; /* The latest directory entry */ #ifdef USE_READDIR_R struct dirent *buffer; /* A buffer used by the threaded version of */ /* readdir() */ int buffer_dim; /* The allocated size of buffer[] */ #endif }; static int _dr_path_is_dir(const char *pathname); /*....................................................................... * Create a new DirReader object. * * Output: * return DirReader * The new object, or NULL on error. */ DirReader *_new_DirReader(void) { DirReader *dr; /* The object to be returned */ /* * Allocate the container. */ dr = (DirReader *) malloc(sizeof(DirReader)); if(!dr) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_DirReader(). */ dr->err = NULL; dr->dir = NULL; dr->file = NULL; #ifdef USE_READDIR_R dr->buffer = NULL; dr->buffer_dim = 0; #endif /* * Allocate a place to record error messages. */ dr->err = _new_ErrMsg(); if(!dr->err) return _del_DirReader(dr); return dr; } /*....................................................................... * Delete a DirReader object. * * Input: * dr DirReader * The object to be deleted. * Output: * return DirReader * The deleted object (always NULL). */ DirReader *_del_DirReader(DirReader *dr) { if(dr) { _dr_close_dir(dr); #ifdef USE_READDIR_R free(dr->buffer); #endif dr->err = _del_ErrMsg(dr->err); free(dr); }; return NULL; } /*....................................................................... * Open a new directory. * * Input: * dr DirReader * The directory reader resource object. * path const char * The directory to be opened. * Input/Output: * errmsg char ** If an error occurs and errmsg isn't NULL, a * pointer to an error description will be assigned * to *errmsg. * Output: * return int 0 - OK. * 1 - Error (see *errmsg for a description). */ int _dr_open_dir(DirReader *dr, const char *path, char **errmsg) { DIR *dir = NULL; /* The directory stream */ /* * If a directory is already open, close it first. */ (void) _dr_close_dir(dr); /* * Is the path a directory? */ if(!_dr_path_is_dir(path)) { if(errmsg) { _err_record_msg(dr->err, "Can't open directory: ", path, END_ERR_MSG); *errmsg = _err_get_msg(dr->err); }; return 1; }; /* * Attempt to open the directory. */ dir = opendir(path); if(!dir) { if(errmsg) { _err_record_msg(dr->err, "Can't open directory: ", path, END_ERR_MSG); *errmsg = _err_get_msg(dr->err); }; return 1; }; /* * If using POSIX threads, allocate a buffer for readdir_r(). */ #ifdef USE_READDIR_R { size_t size; int name_max = pathconf(path, _PC_NAME_MAX); #ifdef NAME_MAX if(name_max < 0) name_max = NAME_MAX; #endif if(name_max < 0) { if(errmsg) { _err_record_msg(dr->err, "Unable to deduce readdir() buffer size.", END_ERR_MSG); *errmsg = _err_get_msg(dr->err); }; closedir(dir); return 1; }; /* * How big a buffer do we need to allocate? */ size = sizeof(struct dirent) + name_max; /* * Extend the buffer? */ if(size > dr->buffer_dim || !dr->buffer) { struct dirent *buffer = (struct dirent *) (dr->buffer ? realloc(dr->buffer, size) : malloc(size)); if(!buffer) { if(errmsg) { _err_record_msg(dr->err, "Insufficient memory for readdir() buffer.", END_ERR_MSG); *errmsg = _err_get_msg(dr->err); }; closedir(dir); errno = ENOMEM; return 1; }; dr->buffer = buffer; dr->buffer_dim = size; }; }; #endif /* * Record the successfully opened directory. */ dr->dir = dir; return 0; } /*....................................................................... * If the DirReader object is currently contains an open directory, * close it. * * Input: * dr DirReader * The directory reader resource object. */ void _dr_close_dir(DirReader *dr) { if(dr && dr->dir) { closedir(dr->dir); dr->dir = NULL; dr->file = NULL; _err_clear_msg(dr->err); }; } /*....................................................................... * Read the next file from the directory opened with _dr_open_dir(). * * Input: * dr DirReader * The directory reader resource object. * Output: * return char * The name of the new file, or NULL if we reached * the end of the directory. */ char *_dr_next_file(DirReader *dr) { /* * Are we currently reading a directory? */ if(dr->dir) { /* * Read the next directory entry. */ #ifdef USE_READDIR_R if(readdir_r(dr->dir, dr->buffer, &dr->file) == 0 && dr->file) return dr->file->d_name; #else dr->file = readdir(dr->dir); if(dr->file) return dr->file->d_name; #endif }; /* * When the end of a directory is reached, close it. */ _dr_close_dir(dr); return NULL; } /*....................................................................... * Return 1 if the specified pathname refers to a directory. * * Input: * pathname const char * The path to test. * Output: * return int 0 - Not a directory. * 1 - pathname[] refers to a directory. */ static int _dr_path_is_dir(const char *pathname) { struct stat statbuf; /* The file-statistics return buffer */ /* * Look up the file attributes. */ if(stat(pathname, &statbuf) < 0) return 0; /* * Is the file a directory? */ return S_ISDIR(statbuf.st_mode) != 0; } #endif /* ifndef WITHOUT_FILE_SYSTEM */ yuma123_2.14/libtecla/PORTING0000664000175000017500000000315414770023131015752 0ustar vladimirvladimirThe Tecla library was written with portability in mind, so no modifications to the source code should be needed on UNIX or LINUX platforms. The default compilation and linking arrangements should also work unchanged on these systems, but if no specific configuration has been provided for your system, shared libraries won't be compiled. Configuring these requires modifications to be made to the file: configure.in This file is heavily commented (comments start with the word dnl) and is relatively simple, so the instructions and suggestions that you find in this file should be sufficient to help you figure out how to add a configuration for your system. This file is an input file to the GNU autoconf program, which uses it as a template for generating the distributed configure script. If autoconf is installed on your system, creating a new configure script is a simple matter of typing. autoconf To avoid confusion with the leftovers of the previous configuration, you should then do the following: rm -f config.cache ./configure make clean ./configure make The first ./configure creates a new makefile for your system, allowing you to type 'make clean' to discard any files that were compiled with the previous configuration. Since 'make clean' also deletes the new makefile, a second invokation of the configure script is then performed to re-create the makefile. The final make then creates the tecla library from scratch. Once you have confirmed that the new configuration works, please send the modified "configure.in" template to mcs@astro.caltech.edu, so that your changes can be included in subsequent releases. yuma123_2.14/libtecla/install-sh0000775000175000017500000001273614770023131016717 0ustar vladimirvladimir#!/bin/sh # # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. It can only install one file at a time, a restriction # shared with many OS's install programs. # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: chmodcmd="" else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 yuma123_2.14/libtecla/homedir.c0000664000175000017500000003544414770023131016507 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * If file-system access is to be excluded, this module has no function, * so all of its code should be excluded. */ #ifndef WITHOUT_FILE_SYSTEM #include #include #include #include #include #include #include #include #include "pathutil.h" #include "homedir.h" #include "errmsg.h" /* * Use the reentrant POSIX threads versions of the password lookup functions? */ #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L #define THREAD_COMPATIBLE 1 /* * Under Solaris we can use thr_main() to determine whether * threads are actually running, and thus when it is necessary * to avoid non-reentrant features. */ #if defined __sun && defined __SVR4 #include /* Solaris thr_main() */ #endif #endif /* * Provide a password buffer size fallback in case the max size reported * by sysconf() is said to be indeterminate. */ #define DEF_GETPW_R_SIZE_MAX 1024 /* * The resources needed to lookup and record a home directory are * maintained in objects of the following type. */ struct HomeDir { ErrMsg *err; /* The error message report buffer */ char *buffer; /* A buffer for reading password entries and */ /* directory paths. */ int buflen; /* The allocated size of buffer[] */ #ifdef THREAD_COMPATIBLE struct passwd pwd; /* The password entry of a user */ #endif }; static const char *hd_getpwd(HomeDir *home); /*....................................................................... * Create a new HomeDir object. * * Output: * return HomeDir * The new object, or NULL on error. */ HomeDir *_new_HomeDir(void) { HomeDir *home; /* The object to be returned */ size_t pathlen; /* The estimated maximum size of a pathname */ /* * Allocate the container. */ home = (HomeDir *) malloc(sizeof(HomeDir)); if(!home) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_HomeDir(). */ home->err = NULL; home->buffer = NULL; home->buflen = 0; /* * Allocate a place to record error messages. */ home->err = _new_ErrMsg(); if(!home->err) return _del_HomeDir(home); /* * Allocate the buffer that is used by the reentrant POSIX password-entry * lookup functions. */ #ifdef THREAD_COMPATIBLE /* * Get the length of the buffer needed by the reentrant version * of getpwnam(). */ #ifndef _SC_GETPW_R_SIZE_MAX home->buflen = DEF_GETPW_R_SIZE_MAX; #else errno = 0; home->buflen = sysconf(_SC_GETPW_R_SIZE_MAX); /* * If the limit isn't available, substitute a suitably large fallback value. */ if(home->buflen < 0 || errno) home->buflen = DEF_GETPW_R_SIZE_MAX; #endif #endif /* * If the existing buffer length requirement is too restrictive to record * a pathname, increase its length. */ pathlen = _pu_pathname_dim(); if(pathlen > home->buflen) home->buflen = pathlen; /* * Allocate a work buffer. */ home->buffer = (char *) malloc(home->buflen); if(!home->buffer) { errno = ENOMEM; return _del_HomeDir(home); }; return home; } /*....................................................................... * Delete a HomeDir object. * * Input: * home HomeDir * The object to be deleted. * Output: * return HomeDir * The deleted object (always NULL). */ HomeDir *_del_HomeDir(HomeDir *home) { if(home) { home->err = _del_ErrMsg(home->err); if(home->buffer) free(home->buffer); free(home); }; return NULL; } /*....................................................................... * Lookup the home directory of a given user in the password file. * * Input: * home HomeDir * The resources needed to lookup the home directory. * user const char * The name of the user to lookup, or "" to lookup * the home directory of the person running the * program. * Output: * return const char * The home directory. If the library was compiled * with threads, this string is part of the HomeDir * object and will change on subsequent calls. If * the library wasn't compiled to be reentrant, * then the string is a pointer into a static string * in the C library and will change not only on * subsequent calls to this function, but also if * any calls are made to the C library password * file lookup functions. Thus to be safe, you should * make a copy of this string before calling any * other function that might do a password file * lookup. * * On error, NULL is returned and a description * of the error can be acquired by calling * _hd_last_home_dir_error(). */ const char *_hd_lookup_home_dir(HomeDir *home, const char *user) { const char *home_dir; /* A pointer to the home directory of the user */ /* * If no username has been specified, arrange to lookup the current * user. */ int login_user = !user || *user=='\0'; /* * Check the arguments. */ if(!home) { errno = EINVAL; return NULL; }; /* * Handle the ksh "~+". This expands to the absolute path of the * current working directory. */ if(!login_user && strcmp(user, "+") == 0) { home_dir = hd_getpwd(home); if(!home_dir) { _err_record_msg(home->err, "Can't determine current directory", END_ERR_MSG); return NULL; } return home_dir; }; /* * When looking up the home directory of the current user, see if the * HOME environment variable is set, and if so, return its value. */ if(login_user) { home_dir = getenv("HOME"); if(home_dir) return home_dir; }; /* * Look up the password entry of the user. * First the POSIX threads version - this is painful! */ #ifdef THREAD_COMPATIBLE { struct passwd *ret; /* The returned pointer to pwd */ int status; /* The return value of getpwnam_r() */ /* * Look up the password entry of the specified user. */ if(login_user) status = getpwuid_r(geteuid(), &home->pwd, home->buffer, home->buflen, &ret); else status = getpwnam_r(user, &home->pwd, home->buffer, home->buflen, &ret); if(status || !ret) { _err_record_msg(home->err, "User '", user, "' doesn't exist.", END_ERR_MSG); return NULL; }; /* * Get a pointer to the string that holds the home directory. */ home_dir = home->pwd.pw_dir; }; /* * Now the classic unix version. */ #else { struct passwd *pwd = login_user ? getpwuid(geteuid()) : getpwnam(user); if(!pwd) { _err_record_msg(home->err, "User '", user, "' doesn't exist.", END_ERR_MSG); return NULL; }; /* * Get a pointer to the home directory. */ home_dir = pwd->pw_dir; }; #endif return home_dir; } /*....................................................................... * Return a description of the last error that caused _hd_lookup_home_dir() * to return NULL. * * Input: * home HomeDir * The resources needed to record the home directory. * Output: * return char * The description of the last error. */ const char *_hd_last_home_dir_error(HomeDir *home) { return home ? _err_get_msg(home->err) : "NULL HomeDir argument"; } /*....................................................................... * The _hd_scan_user_home_dirs() function calls a user-provided function * for each username known by the system, passing the function both * the name and the home directory of the user. * * Input: * home HomeDir * The resource object for reading home * directories. * prefix const char * Only information for usernames that * start with this prefix will be * returned. Note that the empty & string "", matches all usernames. * data void * Anonymous data to be passed to the * callback function. * callback_fn HOME_DIR_FN(*) The function to call for each user. * Output: * return int 0 - Successful completion. * 1 - An error occurred. A description * of the error can be obtained by * calling _hd_last_home_dir_error(). */ int _hd_scan_user_home_dirs(HomeDir *home, const char *prefix, void *data, HOME_DIR_FN(*callback_fn)) { int waserr = 0; /* True after errors */ int prefix_len; /* The length of prefix[] */ /* * Check the arguments. */ if(!home || !prefix || !callback_fn) { if(home) { _err_record_msg(home->err, "_hd_scan_user_home_dirs: Missing callback function", END_ERR_MSG); }; return 1; }; /* * Get the length of the username prefix. */ prefix_len = strlen(prefix); /* * There are no reentrant versions of getpwent() etc for scanning * the password file, so disable username completion when the * library is compiled to be reentrant. */ #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L #if defined __sun && defined __SVR4 if(thr_main() >= 0) /* thread library is linked in */ #else if(1) #endif { struct passwd pwd_buffer; /* A returned password entry */ struct passwd *pwd; /* A pointer to pwd_buffer */ char buffer[512]; /* The buffer in which the string members of */ /* pwd_buffer are stored. */ /* * See if the prefix that is being completed is a complete username. */ if(!waserr && getpwnam_r(prefix, &pwd_buffer, buffer, sizeof(buffer), &pwd) == 0 && pwd != NULL) { waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir, _err_get_msg(home->err), ERR_MSG_LEN); }; /* * See if the username of the current user minimally matches the prefix. */ if(!waserr && getpwuid_r(getuid(), &pwd_buffer, buffer, sizeof(buffer), &pwd) == 0 && pwd != NULL && strncmp(prefix, pwd->pw_name, prefix_len)==0) { waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir, _err_get_msg(home->err), ERR_MSG_LEN); }; /* * Reentrancy not required? */ } else #endif { struct passwd *pwd; /* The pointer to the latest password entry */ /* * Open the password file. */ setpwent(); /* * Read the contents of the password file, looking for usernames * that start with the specified prefix, and adding them to the * list of matches. */ while((pwd = getpwent()) != NULL && !waserr) { if(strncmp(prefix, pwd->pw_name, prefix_len) == 0) { waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir, _err_get_msg(home->err), ERR_MSG_LEN); }; }; /* * Close the password file. */ endpwent(); }; /* * Under ksh ~+ stands for the absolute pathname of the current working * directory. */ if(!waserr && strncmp(prefix, "+", prefix_len) == 0) { const char *pwd = hd_getpwd(home); if(pwd) { waserr = callback_fn(data, "+", pwd, _err_get_msg(home->err),ERR_MSG_LEN); } else { waserr = 1; _err_record_msg(home->err, "Can't determine current directory.", END_ERR_MSG); }; }; return waserr; } /*....................................................................... * Return the value of getenv("PWD") if this points to the current * directory, or the return value of getcwd() otherwise. The reason for * prefering PWD over getcwd() is that the former preserves the history * of symbolic links that have been traversed to reach the current * directory. This function is designed to provide the equivalent * expansion of the ksh ~+ directive, which normally returns its value * of PWD. * * Input: * home HomeDir * The resource object for reading home directories. * Output: * return const char * A pointer to either home->buffer, where the * pathname is recorded, the string returned by * getenv("PWD"), or NULL on error. */ static const char *hd_getpwd(HomeDir *home) { /* * Get the absolute path of the current working directory. */ char *cwd = getcwd(home->buffer, home->buflen); /* * Some shells set PWD with the path of the current working directory. * This will differ from cwd in that it won't have had symbolic links * expanded. */ const char *pwd = getenv("PWD"); /* * If PWD was set, and it points to the same directory as cwd, return * its value. Note that it won't be the same if the current shell or * the current program has changed directories, after inheriting PWD * from a parent shell. */ struct stat cwdstat, pwdstat; if(pwd && cwd && stat(cwd, &cwdstat)==0 && stat(pwd, &pwdstat)==0 && cwdstat.st_dev == pwdstat.st_dev && cwdstat.st_ino == pwdstat.st_ino) return pwd; /* * Also return pwd if getcwd() failed, since it represents the best * information that we have access to. */ if(!cwd) return pwd; /* * In the absence of a valid PWD, return cwd. */ return cwd; } #endif /* ifndef WITHOUT_FILE_SYSTEM */ yuma123_2.14/libtecla/README0000664000175000017500000000516714770023131015573 0ustar vladimirvladimirThis is version 1.6.1 of the tecla command-line editing library. For the current official release, please direct your browser to: http://www.astro.caltech.edu/~mcs/tecla/index.html The tecla library provides UNIX and LINUX programs with interactive command line editing facilities, similar to those of the unix tcsh shell. In addition to simple command-line editing, it supports recall of previously entered command lines, TAB completion of file names, and in-line wild-card expansion of filenames. The internal functions which perform file-name completion and wild-card expansion are also available externally for optional use by programs, along with a module for tab-completion and lookup of filenames in a list of directories. Note that special care has been taken to allow the use of this library in threaded programs. The option to enable this is discussed in the Makefile, and specific discussions of thread safety are presented in the included man pages. For instructions on how to compile and install the library, please see the INSTALL file, which should be in the same directory as this file. Copyright and Disclaimer ------------------------ Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. yuma123_2.14/libtecla/chrqueue.h0000664000175000017500000000743714770023131016707 0ustar vladimirvladimir#ifndef chrqueue_h #define chrqueue_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /*----------------------------------------------------------------------- * This module implements a queue of characters to be processed in some * way. It is used by gl_get_line() to maintain a queue of characters * to be sent to a remote terminal. Characters are recorded in a * dynamically extensible list of fixed sized buffers. */ typedef struct GlCharQueue GlCharQueue; /* * Create a new character queue. */ GlCharQueue *_new_GlCharQueue(void); /* * Delete a redundant character queue. */ GlCharQueue *_del_GlCharQueue(GlCharQueue *cq); /* * Append an array of n characters to a character queue. */ int _glq_append_chars(GlCharQueue *cq, const char *chars, int n, GlWriteFn *write_fn, void *data); /* * Clear a character queue. */ void _glq_empty_queue(GlCharQueue *cq); /* * Return a count of the number of characters in the queue. */ int _glq_char_count(GlCharQueue *cq); /* * A structure of the following type is used by _glq_peek_chars() to * return characters at the start of the queue. */ typedef struct { const char *buff; /* A pointer to the first undeleted byte in the */ /* first buffer of the queue. */ int nbuff; /* The number of characters in buff[] */ } GlCharQueueBuff; /* * Enumerator values of the following type are returned by * _glq_flush_queue() to indicate the status of the flush operation. */ typedef enum { GLQ_FLUSH_DONE, /* The flush operation completed successfully */ GLQ_FLUSH_AGAIN, /* The flush operation couldn't be completed on this */ /* call. Call this function again when the output */ /* channel can accept further output. */ GLQ_FLUSH_ERROR /* Unrecoverable error. */ } GlqFlushState; /* * Transfer as much of the contents of a character queue to an output * channel as possible, returning before the queue is empty if the * write_fn() callback says that it can't currently write anymore. */ GlqFlushState _glq_flush_queue(GlCharQueue *cq, GlWriteFn *write_fn, void *data); /* * Provide information about the last error that occurred while calling * any of the above functions. */ const char *_glq_last_error(GlCharQueue *cq); #endif yuma123_2.14/libtecla/demo.c0000664000175000017500000001520614770023131015776 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include #include #include #include #include "libtecla.h" /* The function which displays the introductory text of the demo */ static void show_demo_introduction(GetLine *gl); static char* my_tab_context_string_array[] = {"one", "two", "three", NULL}; static int my_tab_callback (WordCompletion *cpl, void *data, const char *line, int word_end) { int word_start; int i; int retval; for(word_start=word_end;word_start>0;word_start--) { if(line[word_start-1]==' ') break; } i=0; while(my_tab_context_string_array[i]!=NULL) { if(strlen(my_tab_context_string_array[i])>=(word_end-word_start)) { if(((word_end-word_start)==0) || (0==memcmp(line+word_start,my_tab_context_string_array[i],word_end-word_start))) { retval = cpl_add_completion(cpl, line, word_start, word_end, (const char *)my_tab_context_string_array[i]+word_end-word_start, (const char *)":num:", (const char *)" + "); } } i++; } return 0; } /*....................................................................... * This program demonstrates how to use gl_get_line() as a line editor to * to enable users to enter input. It takes no arguments. */ int main(int argc, char *argv[]) { char *line; /* A line of input */ GetLine *gl; /* The line editor */ int major,minor,micro; /* The version number of the library */ int retval; /* * Create the line editor, specifying a max line length of 500 bytes, * and 10000 bytes to allocate to storage of historical input lines. */ gl = new_GetLine(500, 5000); if(!gl) return 1; /* setup CLI tab line completion */ retval = gl_customize_completion(gl, my_tab_context_string_array, my_tab_callback); if (retval != 0) { printf("\nError: cannot set GL tab completion"); return 1; } /* * If the user has the LC_CTYPE or LC_ALL environment variables set, * enable display of characters corresponding to the specified locale. */ (void) setlocale(LC_CTYPE, ""); /* * Lookup and display the version number of the library. */ libtecla_version(&major, &minor, µ); printf("\n Welcome to the main demo program of libtecla version %d.%d.%d\n", major, minor, micro); /* * Display an introductory banner. */ show_demo_introduction(gl); /* * Load history. */ #ifndef WITHOUT_FILE_SYSTEM (void) gl_load_history(gl, "~/.demo_history", "#"); #endif /* * Read lines of input from the user and print them to stdout. */ do { /* * Get a new line from the user. */ line = gl_get_line(gl, "$ ", NULL, 0); if(!line) break; /* * Display what was entered. */ if(printf("You entered: %s", line) < 0 || fflush(stdout)) break; /* * If the user types "exit", quit the program. */ if(strcmp(line, "exit\n")==0) break; else if(strcmp(line, "history\n")==0) gl_show_history(gl, stdout, "%N %T %H\n", 0, -1); else if(strcmp(line, "size\n")==0) { GlTerminalSize size = gl_terminal_size(gl, 80, 24); printf("Terminal size = %d columns x %d lines.\n", size.ncolumn, size.nline); } else if(strcmp(line, "clear\n")==0) { if(gl_erase_terminal(gl)) return 1; }; } while(1); /* * Save historical command lines. */ #ifndef WITHOUT_FILE_SYSTEM (void) gl_save_history(gl, "~/.demo_history", "#", -1); #endif /* * Clean up. */ gl = del_GetLine(gl); return 0; } /*....................................................................... * Display introductory text to the user, formatted according to the * current terminal width and enclosed in a box of asterixes. * * Input: * gl GetLine * The resource object of gl_get_line(). */ static void show_demo_introduction(GetLine *gl) { int start; /* The column in which gl_display_text() left the cursor */ int i; /* * Break the indtroductory text into an array of strings, so as to * avoid overflowing any compiler string limits. */ const char *doc[] = { "This program is a simple shell with which you can experiment with the ", "line editing and tab completion facilities provided by the gl_get_line() ", "function. The file demo.c also serves as a fully commented example ", "of how to use gl_get_line().\n" }; /* * Form the top line of the documentation box by filling the area of * the line between a " *" prefix and a "* " suffix with asterixes. */ printf("\n"); gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); /* * Justify the documentation text within margins of asterixes. */ for(start=0,i=0; i= 0; i++) start = gl_display_text(gl, 0, " * ", " * ", ' ', 80, start,doc[i]); /* * Draw the bottom line of the documentation box. */ gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); printf("\n"); } yuma123_2.14/libtecla/errmsg.c0000664000175000017500000001144714770023131016354 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include "errmsg.h" /* * Encapsulate the error reporting buffer in an opaque object. */ struct ErrMsg { char msg[ERR_MSG_LEN+1]; /* An error message */ }; /*....................................................................... * Create a new error-message object. * * Output: * return ErrMsg * The new object, or NULL on error. */ ErrMsg *_new_ErrMsg(void) { ErrMsg *err; /* The object to be returned */ /* * Allocate the container. */ err = malloc(sizeof(ErrMsg)); if(!err) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_ErrMsg(). */ err->msg[0] = '\0'; return err; } /*....................................................................... * Delete an error-message object. * * Input: * err ErrMsg * The object to be deleted. * Output: * return ErrMsg * The deleted object (always NULL). */ ErrMsg *_del_ErrMsg(ErrMsg *err) { if(err) { free(err); }; return NULL; } /*....................................................................... * Record the concatenation of a list of string arguments in an error * message object. The last argument must be END_ERR_MSG to terminate * the argument list. * * Input: * err ErrMsg * The error-message container. * ... const char * Zero or more strings to be concatenated in buff[]. * ... const char * The last argument must always be END_ERR_MSG to * terminate the argument list. */ void _err_record_msg(ErrMsg *err, ...) { va_list ap; /* The variable argument list */ const char *s; /* The string being printed */ size_t msglen = 0; /* The total length of the message */ /* * Nowhere to record the result? */ if(!err) { errno = EINVAL; return; }; /* * Concatenate the list of argument strings in err->msg[]. */ va_start(ap, err); while((s = va_arg(ap, const char *)) != END_ERR_MSG) { /* * How much room is left in the output buffer (note that the output * buffer has ERR_MSG_LEN+1 elements). */ int nleft = ERR_MSG_LEN - msglen; /* * How long is the next string to be appended? */ size_t slen = strlen(s); /* * If there is any room left, append as much of the string * as will fit. */ if(nleft > 0) { int nnew = slen < nleft ? slen : nleft; strncpy(err->msg + msglen, s, nnew); msglen += nnew; }; }; va_end(ap); /* * Terminate the message. */ err->msg[msglen] = '\0'; return; } /*....................................................................... * Return a pointer to the error message buffer. * * Input: * err ErrMsg * The container of the error message buffer. * Output: * return char * The current error message, or NULL if err==NULL. */ char *_err_get_msg(ErrMsg *err) { return err ? err->msg : NULL; } /*....................................................................... * Replace the current error message with an empty string. * * Input: * err ErrMsg * The container of the error message buffer. */ void _err_clear_msg(ErrMsg *err) { if(err) err->msg[0] = '\0'; } yuma123_2.14/libtecla/CHANGES0000664000175000017500000042430514770023131015705 0ustar vladimirvladimirIn the following log, modification dates are listed using the European convention in which the day comes before the month (ie. DD/MM/YYYY). The most recent modifications are listed first. 31/10/2004 mcs@astro.caltech.edu (problem reported by Godfrey van der Linden) getline.c The gl_event_handler() function had the endif of a conditional compilation clause in the wrong place. This only upset the compiler on unusual systems that don't have select(). The problem was seen under Mac OS X, due to the configuration problem in 1.6.0 that caused the configure script to mistakenly report that select wasn't available. 31/10/2004 mcs@astro.caltech.edu (info provided by Ivan Rayner) configure.in configure Makefile.in Ivan reported that under IRIX 6.5 it is necessary to add -D_XOPEN_SOURCE=500 to the compiler flags, when compiling the reentrant version of the library. Thus, whereas previously I hardwired the value of DEFINES_R in Makefile.in, I have now made this a variable in the configure script, which is augmented with the above addition, within an IRIX-specific switch clause. Also apparently configure leaves the RANLIB variable blank, instead of setting it to ":", so I have now explicitly set this to ":", within the new IRIX clause of the configure script. 31/10/2004 mcs@astro.caltech.edu (info provided by Ivan Rayner) getline.c Under IRIX, the compiler warned that gl_read_unmasked() was returning an int, which was then being assigned to an enumeration type. This is techically fine, but it highlighted the fact that I had meant to declare gl_read_unmasked() to directly return the enumerated type. I have now done so. 26/09/2004 mcs@astro.caltech.edu getline.c Users can now turn off interactive command-line editing by setting the TERM environment variable to the word "dumb". 18/07/2004 mcs@astro.caltech.edu (problem noted by Michael MacFaden) getline.c Calling gl_terminal_size() on a system without support for SIGWINCH caused a divide-by-zero error in an unintended call to gl_erase_line(), because gl_update_size() was incorrectly being called to query the terminal size, instead of gl_query_size(). 18/07/2004 Padraig Brady (documented here by mcs@astro.caltech.edu) getline.c The suspend and termination signal-handlers installed by gl_tty_signals(), were being installed swapped. 03/06/2004 Mike Meaney (documented here by mcs@astro.caltech.edu) getline.c Mike pointed out the fact that the curses setupterm() function is actually documented to exit the application if an error occurs while its optional errret argument is NULL. I hadn't noticed this, and because I didn't need the extra information returned in the errret argument, I was passing it a NULL. As suggested by Mike, I now pass this argument a pointer to a dummy errret variable. 23/05/2004 mcs@astro.caltech.edu (problem noted by John Beck) man/func/cpl_complete_word.in Some of the prototypes of functions and types documented by the cpl_complete_word man page, weren't listed in the Synopsis section of this man page. They are now listed there. 23/05/2004 mcs@astro.caltech.edu getline.c man/func/gl_get_line.in I have now added support for calling gl_normal_io() from any callback functions that the application installs by calling either gl_inactivity_timeout(), or gl_watch_fd(). Previously, if one of these callback functions called gl_normal_io(), then after returning to gl_get_line(), gl_get_line() would incorrectly assume that the terminal was still in raw I/O mode. Now, gl_get_line() checks to see if gl_normal_io() was called by the callback, and if so, calls _gl_raw_io() to reinstate raw I/O mode. 21/05/2004 mcs@astro.caltech.edu configure.in configure On Mac OS X the code that the configure script used to check for select() failed due to missing symbols in sys/select.h. Moving the inclusion of sys/select.h to after the inclusion of sys/time.h, sys/types.h and sys/unistd.h fixed this. 11/05/2004 mcs@astro.caltech.edu getline.c man/func/gl_get_line.in If the line buffer returned by one call to gl_get_line() was passed as the start_line argument of the next call to gl_get_line(), then instead of the just-entered line being presented back to the user for further editing, the start_line argument was effectively ignored, because the line buffer whose pointer was being passed back, was being cleared before the start_line pointer was examined. This appears to have been a case of me incorrectly thinking that I had forgotten to initialize gl->line[] and gl->ntotal in the gl_reset_input_line() function, and then "fixing" this supposed omission. Removing this erroneous fix, restored things to how they were meant to be. To make it unlikely that I will make the same mistake again, I have renamed the function from gl_reset_input_line() to gl_reset_editor(), to stop it looking as though it is meant to reset the contents of the input line (that is what gl_truncate_buffer() is for), explicitly stated that it doesn't clear the input line, in the header comments of the function, and added a prominent warning comment in the body of the function. Also, since support for passing back the returned line pointer via the start_line argument of the next call to gl_get_line(), wasn't documented in the man page, but was meant to be supported, and definitely used to work, I have now amended the man page documentation of gl_get_line() to explicitly state that this feature is officially supported. 2?/04/2004 Released 1.6.0 22/04/2004 mcs@astro.caltech.edu (Fixed a bug reported by John Beck) getline.c When an error, signal, or other abnormal event aborted gl_get_line(), the cleanup code that restored the terminal to a sane state, also overwrote the value of errno that was associated with the aborting event. An I/O error occurring in the cleanup code would have also overwritten the value to be returned by gl_return_status(), and thus remove any possibility of the caller finding out what really caused gl_get_line() to abort. I have now written a new internal function called, gl_record_status(), which records the completion status to be returned by gl_return_status(), and the value to assign to errno just before gl_get_line() returns. This is called wherever code detects conditions that require gl_get_line() to return early. The function ensures that once an abnormal completion status has been recorded for return, subsequent completions statuses aren't recorded. This ensures that the caller sees the original cause of the abnormal return, rather than any error that occurs during cleaning up from this before return. 17/04/2004 mcs@astro.caltech.edu getline.c If an application's callback called gl_read_char() after calling gl_normal_io(), it would inappropriately redisplay the input line, when it called _gl_raw_io() to temporarily switch the terminal back into raw mode. To fix this, _gl_raw_io() now takes a new 'redisplay' argument, which specifies whether or not to queue a redisplay of the input line. I also created a new gl->postpone flag, which is set by gl_normal_io(), and cleared by _gl_raw_io() (when its redisplay argument is true). When this flag is set, gl_flush_output() ignores queued redisplays, as it generally should between calls to gl_normal_io() and gl_raw_io(). Thus its effect is to postpone redisplays while line editing is suspended. 11/04/2004 mcs@astro.caltech.edu history.c man/misc/tecla.in History searches can now include the globbing operators *, ?, []. When a search prefix is found to have at least one of these characters, then only history lines that completely match that pattern are returned. 11/04/2004 mcs@astro.caltech.edu (issue raised by Mark Coiley) getline.c ioutil.c There appears to be a bug in Solaris's terminal I/O. When the terminal file descriptor is placed in non-blocking I/O mode, and the terminal is switched from canonical to raw mode, characters that were previously entered in canonical I/O mode don't become available to be read until the user types one character more. Select() incorrectly says that there are no characters available, and read() returns EAGAIN. This is only a problem for gl_get_line() when gl_get_line() is in non-blocking server I/O mode, so most users won't have experienced any problems with this. The only way that I have found to get read() to return the characters, without the user first having to type another character, is to turn off non-blocking I/O before calling read(). Select() still claims that there are no characters available to be read, but read happily returns them anyway. Fortunately, one can perform non-blocking terminal reads without setting the non-blocking I/O flag of the file descriptor, simply by setting the VTIME terminal attribute to zero (which I already was doing). Thus, when in non-blocking server I/O, I now turn off the non-blocking I/O flag, attempt to read one character and only if this fails, do I then call the select() based event handler to implement any configured non-zero timeout, before attempting the read again. Of course the non-blocking I/O flag is still needed for writing, so I only turn it off temporarily while reading. 25/03/2004 mcs@astro.caltech.edu (bug reported by Gregory Harris) Makefile.in It appears that when in February, I patched Makefile.in to add abolute paths to the install-sh shell-script, I accidentally replaced install-sh with install.sh. I corrected the name in the Makefile. 25/03/2004 Gregory Harris (documented here by mcs) configure.in configure Greg added the configuration parameters needed to build the shared version of the libtecla library under FreeBSD. 25/03/2004 mcs@astro.caltech.edu getline.c libtecla.h libtecla.map man/func/gl_get_line.in man/func/gl_read_char.in I wrote a public function called gl_read_char(). Unlike gl_query_char(), this function neither prompts the user for input, nor displays the character that was entered. In fact it doesn't write anything to the terminal, and takes pains not to disturb any incompletely entered input line, and can safely be called from application callback functions. 21/03/2004 mcs@astro.caltech.edu getline.c libtecla.h libtecla.map man/func/gl_get_line.in man/func/gl_query_char.in I wrote a public function called gl_query_char(), which prompts the user and awaits a single-character reply, without the user having to hit return. 23/02/2004 mcs@astro.caltech.edu (bug reported by Gregory Harris) configure.in configure getline.c enhance.c demo3.c The configure script now checks for the sys/select.h header file, and arranges for a C macro called HAVE_SYS_SELECT_H to be set if it exists. Thus the files that use select() now use this macro to conditionally include sys/select.h where available. Apparently this header is required under FreeBSD 5.1. 23/02/2004 mcs@astro.caltech.edu getline.c libtecla.h man/func/gl_get_line.in I wrote two new public functions, gl_append_history() and gl_automatic_history(). Together these allow the application to take over the responsibility of adding lines to the history list from gl_get_line(). I then documented their functionality in the gl_get_line man page. Version 1.6.0 I incremented the minor version number of the library, to comply with the requirement to do so when additions are made to the public interface. See libtecla.map for details. libtecla.map I added a new 1.6.0 group for the new minor version, and added the above pair of functions to it. 15/02/2004 mcs@astro.caltech.edu (fixes a bug reported by Satya Sahoo) history.c Calling gl_load_history() multiple times, eventually led to a segmentation fault. This was due to the head of the list of unused history string segments not getting reset when the history buffer was cleared. While debugging this problem I also noticed that the history resizing function was way too complicated to verify, so after fixing the above bug, I heavily simplified the history resizing function, trading off a small reduction in memory efficiency, for greatly improved clarity, and thus made it much more verifiable and maintainable. 14/02/2004 mcs@astro.caltech.edu (fixes a bug reported by Tim Burress). getline.c If gl_change_terminal() was first used to tell gl_get_line to read input from a file, then called later to tell it to read subsequent input from a terminal, no prompt would be displayed for the first line of interactive input. The problem was that on reaching the end of the input file, gl_get_line() should have called gl_abandon_line(), to tell the next call to gl_get_line() to start inputting a new line from scratch. I have added this now. 14/02/2004 Krister Walfridsson (documented here by mcs@astro.caltech.edu) Makefile.in Krister noticed that I had failed to put $(srcdir)/ in front of some invokations of install.sh. I have remedied this. config.guess config.sub I hadn't updated these for a long time, so apparently they didn't recognise the BSD system that Krister was using. I have now updated them to the versions that come with autoconf-2.59. 22/01/2004 mcs@astro.caltech.edu keytab.c When parsing key-binding specifications, backslash escaped characters following ^ characters were not being expanded. Thus ^\\ got interpretted as a control-\ character followed by a \ character, rather than simply as a control-\ character. 12/01/2004 mcs@astro.caltech.edu cplfile.c cplmatch.c demo2.c demo3.c demo.c direader.c expand.c getline.c history.c homedir.c pathutil.c pcache.c configure.in configure INSTALL The configuration script now takes a "--without-file-system" argument. This is primarily for intended for embedded systems that either don't have filesystems, or where the file-system code in libtecla is unwanted bloat. It sets the WITHOUT_FILE_SYSTEM macro. This removes all code related to filesystem access, including the entire public file-expansion, file-completion and path-lookup facilities. Note that the general word completion facility is still included, but without the normally bundled file completion callback. Actually the callback is still there, but it reports no completions, regardless of what string you ask it to complete. This option is described in the INSTALL document. 12/01/2004 mcs@astro.caltech.edu getline.c configure.in configure INSTALL The configuration script now takes a "--without-file-actions" argument. This allows an application author/installer to prevent users of gl_get_line() from accessing the filesystem from the builtin actions of gl_get_line(). It defines a macro called HIDE_FILE_SYSTEM. This causes the "expand-filename", "read-from-file", "read-init-files", and "list-glob" action functions to be completely removed. It also changes the default behavior of actions such as "complete-word" and "list-or-eof" to show no completions, instead of the normal default of showing filename completions. This option is described in the INSTALL document. 11/01/2004 mcs@astro.caltech.edu getline.c man/func/gl_get_line.in In case an application's customized completion handler needs to write to the terminal for some unforseen reason, there needs to be a way for the it to cleanly suspend raw line editing, before writing to the terminal, and the caller then needs to be aware that it may need to resurrect the input line when the callback returns. I have now arranged that the completion callback functions can call the gl_normal_io() function for this purpose, and documented this in the gl_get_line() man page. 11/01/2004 mcs@astro.caltech.edu (In response to a bug report by Satya Sahoo) getline.c The gl_configure_getline() function makes a malloc'd copy of the names of the configuration files that it is asked to read. Before the bug fix, if the application made one or more calls to this function, the memory allocated by the final call that it made before calling del_GetLine(), wasn't being freed. Note that memory allocated in all but the final call was being correctly freed, so the maximum extent of the memory leak was the length of the file name(s) passed in the final call to gl_configure_getline(), and an application that didn't call gl_configure_getline() didn't suffer any leak. 20/12/2003 mcs@astro.caltech.edu history.c Ellen tested the history fix that I reported below, and pointed out that it still had a problem. This turned out to be because getline.c was making some incorrect assumptions about the new behavior of history.c. This problem and the previous one both revolved around how search prefixes were stored and discarded, so I have now re-written this part of the code. Previously the search prefix was retained by looking for a line with that prefix, and keeping a pointer to that line. This saved memory, compared to storing a separate copy of the prefix, but it led to all kinds of hairy interdependencies, so I have now changed the code to keep a separate copy of search prefixes. To keep the memory requirements constant, the search prefix is stored in the history buffer, like normal history lines, but not referenced by the time-ordered history list. The prefix can now be kept around indefinitely, until a new search prefix is specified, regardless of changes to the archived lines in the history buffer. This is actually necessary to make the vi-mode re-search actions work correctly. In particular, I no longer discard the search prefix whenever a history search session ends. Also, rather than have getline.c keep its own record of when a history session is in progress, it now consults history.c, so that failed assumptions can't cause the kind of discrepancy that occurred before. For this to work, getline.c now explicitly tells history.c to cancel search sessions whenever it executes any non-history action. 14/12/2003 mcs@astro.caltech.edu (bug reported by Ellen Oschmann) history.c If one searched backwards for a prefix, then returned to the original line, changed that line, then started another backwards prefix search, getline incorrectly discarded the new search prefix in the process of throwing away its cached copy of the previous pre-search input line. In other words getline was belatedly cancelling a previous search, after a new search had already partially begun, and thus messed up the new search. The obvious fix was to arrange for the current search to be cancelled whenever the history pointer returns to its starting point, rather than waiting for the next search to begin from there. 14/12/2003 mcs@astro.caltech.edu history.c _glh_recall_line() was returning the last line in the history buffer instead of the line requested by the caller. This only affected the obscure "repeat-history" action-function, which probably isn't used by anybody. 09/12/2003 Version 1.5.0 released. 28/09/2003 mcs@astro.caltech.edu homedir.c When the home directory of the login user is requested, see if the HOME environment variable exists, and if so return its value, rather than looking up the user's home directory in the password file. This seems to be the convention adopted by other unix programs that perform tilde expansion, and it works around a strange problem, where a third-party libtecla program, statically compiled under an old version of RedHat, unexpectedly complained that getpwd() returned an error when the program was run under RedHat 9. 01/09/2003 mcs@astro.caltech.edu getline.c libtecla.h libtecla.map man/func/gl_get_line.in man/func/gl_register_action.in. It is now possible for an application to register external functions as action functions. These actions are initially bound to specified key-sequences, but if they are registered before the user's configuration file is loaded, they can also be re-bound by the user to different key-sequences. The function used to register a new action, is called gl_register_action(). Action functions are passed a readonly copy of the input line and the cursor position. They can display text to the terminal, or perform other operations on the application environment. Currently, they can't edit the input line or move the cursor. This will require the future addition of functions to queue the invokation of the built-in action functions. 26/08/2003 mcs@astro.caltech.edu getline.c I modified gl_update_buffer() to ensure that the cursor stays within the input line after external line modifications, and to queue a redisplay of the potentially modified input line. 21/07/2003 mcs@astro.caltech.edu configure.in configure Makefile.in Makefile.stub INSTALL By specifying --without-man-pages or --with-man-pages=no as command-line arguments to the configure script, it is now possible to have the configure script skip the man-page preprocessing step, and arrange for the man-page installation targets in the Makefile to do nothing. This option is designed for people who embed libtecla within other packages. It is also used by Makefile.stub when the distclean target is specified. 21/07/2003 mcs@astro.caltech.edu configure.in configure The previous workaround for recent versions of gcc placing /usr/local/include at the start of the system inlcude-file search path, broke something else. The fix placed /usr/include before gcc's include area, which meant that gcc's modified version of stdarg.h was being ignored in deference to the version in /usr/include. I have changed the fix to have gcc report the search path, then have awk add options to CFLAGS to reorder this path, plaing /usr/local/include at the end. Also, under Solaris 9, including term.h without first including curses.h results in complaints about undefined symbols, such as bool. As a result the configure script's test for term.h was failing. I have now modified it to include curses.h in the test code that it uses to check for term.h. In the process I also improved the tests for curses.h and term.h to prevent an ncurses version of term.h from being used with the system-default version of curses.h. 29/06/2003 mcs@astro.caltech.edu Makefile.in direader.c homedir.c On some systems (eg. linux) the _POSIX_C_SOURCE feature-test macro is set by system headers, rather than being an option set by a project's Makefile at compilation time. In software, such as tecla, where the definition of this macro is used as an indication of whether to use the non-reentrant or reentrant versions of system functions, this means that the reentrant functions are always used, regardless of whether this macro is set or not by the project Makefile. Thus, on such systems the reentrant and non-reentrant versions of the tecla library are essentially identical. This has a couple of drawbacks. First, since thread-safe functions for traversing the password file don't exist, the supposedly non-reentrant version of the tecla library can't support ambiguous tab-completion of usernames in ~username/ constructions. Secondly, on some systems the use of reentrant system functions dictates the use of a shared library that isn't needed for the non-reentrant functions, thus making it more difficult to distribute binary versions of the library. To remedy this situation I have modified the DEFINES_R variable in Makefile.in to arrange for the compiler to define a C macro called PREFER_REENTRANT when it is compiling the reentrant version of the tecla library. This macro is now used in the source code to determine when to require reentrant code. Whithin the source code, wherever a potentially non-reentrant interface is used, the existance of both this macro and a suitably valued _POSIX_C_SOURCE macro, are tested for to see if a reentrant alternative to the problem code should be used. 22/06/2003 mcs@astro.caltech.edu getline.c I changed the way that redisplays are requested and performed. Redisplays are now queued by calling gl_queue_redisplay(), and subsequently performed by gl_flush_output(), when the queue of already pending output has been completely dispatched. This was necessary to prevent event handlers from filling up the output queue with redisplays, and it also simplifies a number of things. In the process I removed the gl_queue_display() function. I also wrote a gl_line_erased() function, which is now called by all functions that erase the input line. I also split the gl_abandon_line() function into public and private callable parts, and used the private version internally to arrange to discard the input line after errors. The raw_mode flag was not being initialized by new_GetLine(). It is now initialized to zero. I removed the zapline flag, since using the endline flag to communicate the desire to terminate the line, did the same thing. gl_terminal_move_cursor() now does nothing when the input line isn't displayed. 18/03/2003 mcs@astro.caltech.edu getline.c Fixed bug which was causing newlines not to be output at the end of each newly entered line. I was interpreting the gl->endline flag in conflicting ways in two places. To fix this I have created a gl->displayed flag. This flags whether an input line is currently displayed. 17/03/2003 mcs@astro.caltech.edu getline.c libtecla.h man/func/gl_get_line.in man/func/gl_erase_terminal.in libtecla.map I added a new function that programs can call to clear the terminal between calls to gl_get_line(). 11/03/2003 mcs@astro.caltech.edu configure.in configure Under linux when _POSIX_C_SOURCE is defined, getpwent() and associated functions become undefined, because _SVID_SOURCE and _BSD_SOURCE become undefined. Adding these feature macros back to CFLAGS resolves this. 06/03/2003 mcs@astro.caltech.edu getline.c libtecla.map man/func/gl_get_line.in Following the lead of Edward Chien, I wrote a function called gl_bind_keyseq(), which binds a specified key-sequence to a given action, or unbinds the key-sequence. 24/02/2003 mcs@astro.caltech.edu getline.c libtecla.map man/func/cpl_complete_word.in I implemented a simple function called cpl_recall_matches(). This recalls the return value of the last call to cpl_complete_word(). 19/01/2003 mcs@astro.caltech.edu getline.c The documented signal handling, fd event-handling, inactivity timeout handling, and server-mode non-blocking I/O features are now implemented for non-interactive input streams, such as pipes and files. 19/01/2003 mcs@astro.caltech.edu getline.c libtecla.h man/func/gl_get_line.in demo3.c I added a new return status enumerator to report when an end-of-file condition causes gl_get_line() to return NULL. 13/01/2003 mcs@astro.caltech.edu history.c I rewrote the history facility. The previous circular buffer implementation was a nightmare to change, and it couldn't efficiently support certain newly requested features. The new implementation stores history lines in linked lists of fixed sized string segments, taken from the buffer, with each line being reference counted and recorded in a hash table. If the user enters a line multiple times, only one copy of the line is now stored. Not only does this make better use of the available buffer space, but it also makes it easy to ensure that a line whose prefix matches the current search prefix, isn't returned more than once in sequence, since we can simply see if the latest search result has the same hash-table pointer as the previous one, rather than having to compare strings. Another plus is that due to the use of linked lists of nodes of fixed size line segments, there is no longer any need to continually shuffle the contents of the buffer in order to defragment it. As far as the user is concerned, the visible differences are as follows: 1. If the user enters a given line multiple times in a row, each one will be recorded in the history list, and will thus be listed by gl_show_history(), and saved in the history file. Previously only one line was recorded when consecutive duplicates were entered. This was a kludge to prevent history recall from recalling the same line multiple times in a row. This only achieved the desired result when not recalling by prefix. 2. Not only simple recall, but prefix-based history line recalls now don't return the same line multiple times in a row. As mentioned in (1) above, previously this only worked when performing a simple recall, without a search prefix. 28/12/2002 mcs@astro.caltech.edu getline.c The one-line function, gl_buff_curpos_to_term_curpos() was only being used by gl_place_cursor(), so I inlined it in that function, and removed it. 28/12/2002 mcs@astro.caltech.edu getline.c gl_suspend_process() was calling the application-level gl_normal_io() and gl_raw_io() functions, where it should have been calling the internal versions _gl_normal_io() and _gl_raw_io(). Also gl_handle_signal() was masking and unmasking just the signals of the first element of the gl[] array argument. It now masks and unmasks all trappable signals. 28/12/2002 mcs@astro.caltech.edu getline.c Now that the number of terminal characters used to display the current input line, is recorded, the relative line on which the last character of the input line resides can be determined without having to call gl_buff_curpos_to_term_curpos(). This is now used by gl_normal_io() via gl_start_newline(), so there is now no need for gl_buff_curpos_to_term_curpos() to be async-signal safe. I have thus removed the annoying gl->cwidth[] array, and gl_buff_curpos_to_term_curpos() now calls gl_width_of_char() directly again. There is also now no need for the gl_line_of_char_start() and gl_line_of_char_end() functions, so I have removed them. 28/12/2002 mcs@astro.caltech.edu getline.c Unfortunately it turns out that the terminfo/termcap control sequence which is defined to delete everything from the current position to the end of the terminal, is only defined to work when at the start of a terminal line. In gnome terminals in RedHat 8.0, if it is used within a terminal line, it erases the whole terminal line, rather than just what follows the cursor. Thus to portably truncate the displayed input line it is necessary to first use the control sequence which deletes from the cursor position to the end of the line, then if there are more terminal lines, move to the start of the next line, and use the delete to end-of-terminal control sequence, then restore the cursor position. This requires that one know how many physical terminal lines are used by the current input line, so I now keep a record of the number of characters so far displayed to the terminal following the start of the prompt, and the new gl_truncate_display() function uses this information to truncate the displayed input line from the current cursor position. 28/12/2002 mcs@astro.caltech.edu getline.c gl_start_newline() now moves to an empty line following the input line, rather than just to the next line. It also arranges for the input line to be redisplayed before editing resumes. A major user of this is gl_print_info(), which now need not be followed by an explicit call to gl_redisplay(), since the terminal input loop in gl_get_input_line() ensures that gl_redisplay() is called after any action function that asserts gl->redisplay. Also, all functions that erase the displayed input line can now call the gl_erase_line() function, which is designed to work correctly even when a terminal resize invalidates the horizontal cursor position. Finally, the new gl_queue_display() function is now used by functions that need to arrange for the input line to be displayed from scratch after the displayed line has been erased or invalidated by other text being written to the terminal. All of these changes are aimed at reducing the number of places that directly modify gl->term_curpos and gl->redisplay. 22/12/2002 Markus Gyger (logged here by mcs) Makefile.in update_html In places where echo and sed were being used to extract the base names of files, Markus substituted the basename command. He also replaced explicit cp and chmod commands with invokations of the install-sh script. configure.in Use $target_os and $target_cpu, where appropriate, instead of $target. configure.in The Solaris man function and library man pages should be in sections 3lib and 3tecla respectively, only in Solaris version 2.8 and above. configure.in Markus provided values for the man page configuration variables for HPUX. man/*/*.in I had missed parameterizing man page section numbers in the man page titles, Markus corrected this. man/func/libtecla_version.in Fixed incorrect section number in the link to the libtecla man page. homedir.c When compiled to be reentrant, although one can't use the non-reentrant getpwent() function to scan the password file for username completions, one can at least see if the prefix being completed is a valid username, and if the username of the current user minimally matches the prefix, and if so list them. I simplified Markus' modification by adding a prefix argument to the _hd_scan_user_home_dirs() function, and redefining the function description accordingly, such that now it reports only those password file entries who's usernames minimally match the specified prefix. Without this, it would have been necessary to peak inside the private data argument passed in by cf_complete_username(). Markus also provided code which under Solaris uses the non-reentrant interfaces if the reentrant version of the library isn't linked with the threads library. 19/12/2002 mcs@astro.caltech.edu Makefile.in Markus pointed out that LDFLAGS was being picked up by the configure script, but not then being interpolated into te Makefile. I have thus added the necessary assignment to Makefile.in and arranged for the value of LDFLAGS to be passed on to recursive make's. I also did the same for CPPFLAGS, which had also been omitted. 18/12/2002 mcs@astro.caltech.edu man/* man/*/* configure.in configure Makefile.in update_html It turns out that the assignment of man page sections to topics differs somewhat from system to system, so this is another thing that needs to be configured by the main configuration script, rather than being hardwired. All man pages have now been moved into suitably named topic-specific sub-directories of the top-level man directory, and instead of having a numeric suffix, now have the .in suffix, since they are now preprocessed by the configure script, in the same fashion as Makefile.in. Whithin these *.in versions of the man pages, and within Makefile.in, the installation subdirectory (eg. man1) and the file-name suffix (eg. 1), are written using configuration macros, so that they get expanded to the appropriate tokens when the configure script is run. In principle, the man pages could also take advantage of other configuration macros, such as the one which expands to the library installation directory, to include full path names to installed files in the documentation, so in the future this feature could have more uses than just that of parameterizing man page sections. 18/12/2002 mcs@astro.caltech.edu man3 man3/* Makefile.in html/index.html update_html Markus suggested splitting the gl_get_line(3) man page into user and developer sections, and also pointed out that the enhance man page should be in section 1, not section 3. I have thus created a top-level man directory in which to place the various sections, and moved the man3 directory into it. The enhance.3 man page is now in man/man1/enhance.1. I have extracted all user-oriented sections from the gl_get_line(3) man page and placed them in a new man7/tecla.7 man page. 18/12/2002 mcs@astro.caltech.edu getline.c Terminal resizing was broken in normal mode, due to me forcing the terminal cursor position to zero in the wrong place in gl_check_caught_signal(). 14/12/2002 Markus Gyger (logged here by mcs) configure.in configure Under Solaris, recent versions of gcc search /usr/local/include for header files before the system directories. This caused a problem if ncurses was installed under Solaris, since the termcap.h include file in /usr/local/include ended up being used at compile time, whereas the system default version of the curses library was used at link time. Since the two libraries declare tputs() differently, this evoked a complaint from gcc. Markus came up with a way to force Gnu cpp to move /usr/local/include to the end of the system-include-file search path, where it belongs. 13/12/2002 mcs@astro.caltech.edu man3/gl_io_mode.3 I rewrote the man page which documents the new non-blocking server I/O mode. 12/12/2002 mcs@astro.caltech.edu demo3.c I wrote a new version of demo3.c, using signal handlers that call gl_handle_signal() and gl_abandon_line(), where previously in this demo, these functions were called from the application code. 05/12/2002 mcs@astro.caltech.edu getline.c gl_normal_io(), gl_raw_io() and gl_handle_signal() and gl_abandon_line() are now signal safe, provided that signal handlers that call them are installed with sa_mask's that block all other signals who's handlers call them. This is the case if gl_tty_signals() is used to install signal handlers that call any of these functions. A major stumbling block that had to be overcome was that gl_displayed_char_width() calls isprint(), which can't safely be called from a signal handler (eg. under linux, the is*() functions all use thread-specific data facilities to support per-thread locales, and the thread-specific data facilities aren't signal safe). To work around this, all functions that modify the input-line buffer, now do so via accessor functions which also maintain a parallel array of character widths, for use by gl_buff_curpos_to_term_curpos() in place of gl_displayed_char_width(). Other minor problems were the need to avoid tputs(), who's signal safety isn't defined. 05/12/2002 Eric Norum (logged here by mcs@astro.caltech.edu) configure.in Eric provided the configuration information needed to build shared libraries under Darwin (Max OS X). 05/12/2002 Richard Mlynarik (logged here by mcs@astro.caltech.edu) configure.in AC_PROG_RANLIB gets the wrong version of ranlib when cross compiling, so has now been replaced by an invokation of AC_CHECK_TOOL. In addition, AC_CHECK_TOOL is also now used to find an appropriate version of LD. 05/12/2002 mcs@astro.caltech.edu (based on patch by Pankaj Rathore) getline.c libtecla.h libtecla.map man3/gl_get_line.3 The new gl_set_term_size() function provides a way to tell gl_get_line() about changes in the size of the terminal in cases where the values returned by ioctl(TIOCGWINSZ) isn't correct. 05/12/2002 mcs@astro.caltech.edu getline.c Rather than calling sprintf() to see how much space would be needed to print a given number in octal, I wrote a gl_octal_width() function, for use by gl_displayed_char_width(). This makes the latter function async signal safe. 05/12/2002 mcs@astro.caltech.edu chrqueue.c Whenever the buffer is exhausted, and getting a new buffer node would require a call to malloc(), attempt to flush the buffer to the terminal. In blocking I/O mode this means that the buffer never grows. In non-blocking I/O mode, it just helps keep the buffer size down. 05/12/2002 mcs@astro.caltech.edu freelist.h freelist.c The new _idle_FreeListNodes() function queries the number of nodes in the freelist which aren't currently in use. 05/12/2002 mcs@astro.caltech.edu Makefile.stub This now accepts all of the targets that the configured makefile does, and after configuring the latter makefile, it invokes it with the same options. 03/12/2002 mcs@astro.caltech.edu mans3/gl_io_mode.3 I completed the man page for all of the new functions related to non-blocking I/O. 01/12/2002 mcs@astro.caltech.edu man3/gl_get_line.3 I wrote a long section on reliable signal handling, explaining how gl_get_line() does this, how to make use of this in a program, and how to handle signals reliably when faced with other blocking functions. This basically documents what I have learnt about signal handling while working on this library. 01/12/2002 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 In non-blocking server mode, the gl_replace_prompt() function can now be used between calls to gl_get_line() if the application wants to change the prompt of the line that is being edited. 01/12/2002 mcs@astro.caltech.edu man3/gl_get_line.3 I documented the new gl_return_status() and gl_error_message() functions. 01/12/2002 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 Added SIGPOLL and SIGXFSZ to the list of signals that are trapped by default. These are process termination signals, so the terminal needs to be restored to a usable state before they terminate the process. 27/11/2002 mcs@astro.caltech.edu getline.c libtecla.h Completed the essential changes needed to support non-blocking server-I/O mode. The new gl_io_mode() function allows one to switch to and from non-blocking server-I/O mode. The new gl_raw_io() function is used in non-blocking server-I/O mode to switch the terminal into non-blocking raw I/O mode. The new gl_normal_io() function is used in non-blocking server-I/O mode to switch the restore the terminal to a normal, blocking state. This is used to suspend line input before suspending the process or writing messages to the terminal. The new gl_tty_signals() function installs specified signals handlers for all signals that suspend, terminate or resume processes, and also for signals that indicate that the terminal has been resized. This not only saves the application from having to keep its own ifdef'd list of such signals, of which there are many, but it also makes sure that these signal handlers are registered correctly. This includes using the sa_mask member of each sigaction structure to ensure that only one of these handlers runs at a time. This is essential to avoid the signal handlers all trying to simultaneously modify shared global data. The new gl_handle_signal() function is provided for responding (from application level) to signals caught by the application. It handles process suspension, process termination and terminal resize signals. The new gl_pending_io() function tells the application what direction of I/O gl_get_line() is currently waiting for. In non-blocking server I/O mode, the new gl_abandon_line() function can be called between calls to gl_get_line() to discard an input line and force the next call to gl_get_line() to start the input of a new line. Also, in non-blocking server-I/O gl_get_line() doesn't attempt to do anything but return when one of the signals that it is configured to catch is caught. This is necessary because when in this mode, the application is required to handle these signals when gl_get_line() is running, and the default configuration of most of these signals in gl_get_line() is to restore the terminal then call the application signal handlers. This would be a case of too many cooks spoiling the broth, so in this mode, gl_get_line() always defers to the application's signal handlers. 26/11/2002 mcs@astro.caltech.edu getline.c libtecla.h I implemented a couple of new functions to support reliable signal handling, as now documented (see above) in the gl_get_line(3) man page. The new gl_catch_blocked() function tells gl_get_line() to unblock all configured signals around calls to long-running functions, not only those that aren't blocked when gl_get_line() is called. This allows the caller to implement reliable signal handling, since the unblocking is only done from within code protected by sigsetjmp(), which avoids race conditions. The new gl_list_signals() function fills a provided sigset_t with the set of signals that gl_get_line() is currently configured to catch. This allows callers to block said signals, such that they are only unblocked by gl_get_line() when it is waiting for I/O. When used in conjunction with the gl_catch_blocked() function, this removes the potential for race conditions. Also, when gl_get_line() installs its signal handler, it uses the sa_mask member of the sigaction structure to ensure that only one instance of this signal handler will ever be executing at a time. 25/11/2002 mcs@astro.caltech.edu (bug reported by Pankaj Rathore) getline.c When any history recall action was invoked when the input line buffer was full, an error message would be displayed complaining about the length of the string in the line input buffer being inconsistent with the specified allocated size. This was because instead of sending the allocated size of the input line, I was sending the length excluding the element that is reserved for the '\0' terminator. Sending it the correct size corrected the problem. 24/11/2002 mcs@astro.caltech.edu getline.c All public functions which take GetLine objects as arguments now block signals on entry and restore the signal mask on return. This was an attempt to make it safe to call getline functions from signal handlers, but the fact is that the functions that I really wanted this to apply to, potentially call malloc(), so this currently isn't the case. 23/11/2002 mcs@astro.caltech.edu getline.c libtecla.h The new gl_return_status() function returns an enumerated return status which can be used to query what caused gl_get_line() to return. 22/11/2002 mcs@astro.caltech.edu Most existing .c and .h files, plus errmsg.c errmsg.h Makefile.rules Until now, many library functions would report error messages to stderr. This isn't appropriate for library functions, so in place of this behavior, error messages are now recorded in internal ErrMsg objects, and passed between modules via new module-specific error querying functions. In addition, errno is now set appropriately. Thus when gl_get_line() and related functions return an error, strerror() can be used to look up system errors, and gl_error_message() can be used to recover a higher level error message. Note that error messages that are responses to user actions continue to be reported to the terminal, as before. 21/11/2002 mcs@astro.caltech.edu getline.c keytab.h keytab.c Makefile.rules I wrote a new version of _kt_lookup_binding() that didn't require the caller to have access to the innards of a KeyTab object. This then enabled me to move the definition of KeyTab objects into keytab.c and make the typedef in keytab.h opaque. Many nested includes were also moved from keytab.h into keytab.c. 05/11/2002 mcs@astro.caltech.edu getline.c libtecla.map libtecla.h demo3.c I split the old gl_resize_terminal() function into two parts, gl_query_size() and gl_update_size(), with the latter calling the former to get the new terminal size. 05/11/2002 mcs@astro.caltech.edu getline.c I fixed a long time bug in the terminal resizing code. When the cursor wasn't on the last terminal line of the input line, the resizing code would redisplay the the line one or more lines above where it should be restored. This was due to an error in the calculation of the number of lines above the cursor position. 04/11/2002 mcs@astro.caltech.edu demo.c demo2.c demo3.c I used the new gl_display_text() function to display introductory text at the startup of each of the demo programs. The text is enclosed within a box of asterixes, drawn dynamically to fit within the confines of the available terminal width. 04/11/2002 mcs@astro.caltech.edu libtecla.h getline.c ioutil.c ioutil.h Makefile.rules libtecla.map man3/gl_get_line.3 man3/gl_display_text.3 Needing a way to display introductory text intelligently in the demo programs, I wrote and documented the gl_display_text() function. This justifies arbitrary length text within the bounds of the terminal width, with or without optional indentation, prefixes and suffixes. 03/11/2002 mcs@astro.caltech.edu demo3.c Makefile.rules I wrote a new demonstration program. This program acts exactly like the main demonstration program, except that it uses an external event loop instead of using the gl_get_line() internal event loop. This is thus an example of the new non-blocking server I/O facility. 02/11/2002 mcs@astro.caltech.edu getline.c keytab.c keytab.h libtecla.h man3/gl_get_line.3 man3/gl_completion_action.3 I added the ability to register additional word completion actions via the new function gl_completion_action(). All action functions now take a new (void *data) argument, which is stored with the function in the symbol table of actions. The new gl_completion_action() function uses this feature to record dynamically allocated objects containing the specified completion function and callback data along with either the gl_complete_word() action function, or the gl_list_completions() action function. These two actions continue to use the builtin completion functions when their data pointer is NULL. 20/10/2002 mcs@astro.caltech.edu The following are changes merged from the non-blocking gl_get_line() development branch. getline.c I wrote a gl_start_newline() function, to replace all of the explicit calls to output \r\n to stdout. Informational messages are now written to the terminal using a new variadic function called gl_print_info(). This starts a newline, writes string arguments until a special argument, GL_END_INFO, is seen, then starts another newline. Changed _output_ to _print_ in the following function names gl_output_control_sequence(), gl_output_char(), gl_output_string() and gl_output_raw_string(). gl_print_raw_string() now has a length argument, so that strings that aren't terminated with '\0' can be printed. The display of the initial contents of a new line to be edited has been moved into a new function called gl_present_line(). The gl_get_input_line() function now takes the prompt string as an argument so that gl_replace_prompt() can be called from within this function instead of from gl_get_line(). Keyboard input is now buffered in a persistent buffer in the parent GetLine object. gl_read_character() checks this for unprocessed characters in preference to calling gl_read_terminal() to append characters to it. A new function, gl_discard_chars(), removes processed characters from this buffer. This change is in preparation for a non-blocking version of gl_get_line(), where partially input key-sequences must be stored between calls to gl_get_line(). getline.c getline.h history.c history.h cplmatch.c \ cplmatch.h expand.c expand.h All terminal output from gl_get_line() is now routed through a GL_WRITE_FN() callback function called gl_write_fn. Internal functions in cplmatch.c, expand.c and history.c have been created which take such callbacks to write output. These are used both by functions in getline.c, to display file completions, expansions, history etc, and as the internals of existing public functions in these files that print to stdio streams. In the latter case an internal stdio GL_WRITE_FN() callback is substituted, so that the functions behave as before. getline.c chrqueue.c chrqueue.h The gl_write_fn() callback used by gl_get_line() now writes to a queue, implemented in chrqueue.c. This queue is implemented as a list of blocks of buffer segments, the number of which shrink and grow as needed. The contents of the queue are flushed to the terminal via another GL_WRITE_FN() callback passed to the queue object. Currently gl_get_line() passes an internal function assigned to gl->flush_fn, called gl_flush_terminal(), which writes the contents of the queue to the terminal, and knows how to handle both blocking and non-blocking I/O. The output queue is designed to be flushed to the terminal incrementally, and thereby also facilitates non-blocking I/O. getline.c getline.h gl_get_line() now reads all input via the GL_READ_FN() callback, assigned to gl->read_fn. Currently this is set to an internal function called gl_read_terminal(), which knows how to handle both blocking and non-blocking I/O. getline.c libtecla.h The new gl_set_nonblocking() function can be used to enable or disable non-blocking I/O. The default is still blocking I/O. In non-blocking mode, the terminal is told not to wait when either reading or writing would block. gl_get_line() then returns, with a return value of NULL, but with the terminal left in raw mode, so that the caller's event loop can detect key presses. The caller should call gl_return_status() to check whether the NULL return value was due to an error, lack of input, or inability to write to the terminal without waiting. If either reading or writing was said to have blocked, the user then should check for I/O readiness in the specified direction before calling gl_get_line() again to incrementally build up the input line. 05/08/2002 mcs@astro.caltech.edu man3/gl_get_line.3 man3/gl_inactivity_timeout.3 I documented the new gl_inactivity_timeout() function. 08/07/2002 mcs@astro.caltech.edu libtecla.h getline.c libtecla.map I added a new gl_inactivity_timeout() function. On systems that have the select system call, this provides the option of registering a function that is then called whenever no I/O activity has been seen for more than a specified period of time. Like the gl_watch_fd() facility, timeout callbacks return a code which tells gl_get_line() how to proceed after the timeout has been handled. 04/07/2002 mcs@astro.caltech.edu (based on a bug report from Michael MacFaden) getline.c The internal event handler wasn't responding to write events on client file descriptors, due to a typo which resulted in read events being checked for twice, and writes not checked for at all. pathutil.c The amount of space to allocate for pathnames is supposed to come from PATH_MAX in limits.h, but I had neglected to include limits.h. This went unnoticed because on most systems the equivalent number is deduced by calling pathconf(). Apparently under NetBSD this function doesn't work correctly over NFS mounts. 30/05/2002 Version 1.4.1 released. 25/05/2002 mcs@astro.caltech.edu (based on suggestions by Paul Smith) pathutil.c Apparently, under QNX pathconf("/",_PC_PATH_MAX) returns EINVAL. At Paul's suggestion I have modified the code to silently substitute the existing MAX_PATHLEN_FALLBACK value if pathconf() returns an error of any kind. homedir.c Under QNX, sysconf(_SC_GETPW_R_SIZE_MAX) also apparently returns EINVAL, so as with pathconf() I modified the code to substitute a fallback default, rather than complaining and failing. enhance.c Paul told me that the inclusion of sys/termios.h was causing compilation of enhance.c to fail under QNX. This line is a bug. The correct thing to do is include termios.h without a sub-directory prefix, as I was already doing futher up in the file, so I have just removed the errant include line. 07/05/2002 mcs@astro.caltech.edu (async development branch only) getline.c gl_read_character() now caches and reads unprocessed characters from a key-press lookahead buffer. Whenever gl_intepret_char() receives a new character which makes an initially promising key-sequence no longer match the prefix of any binding, it now simply discards the first character from the key-press buffer and resets the buffer pointer so that the next call to gl_read_character() returns the character that followed it, from the buffer. getline.c The part of gl_get_input_line() which preloads, displays and prepares to edit a new input line, has now been moved into a function called gl_present_line(). 12/02/2002 mcs@astro.caltech.edu getline.c configure.in configure Mac OS X doesn't have a term.h or termcap.h, but it does define prototypes for tputs() and setupterm(), so the default prototypes that I was including if no headers where available, upset it. I've removed these prototypes. I also now conditionally include whichever is found of curses.h and ncurses/curses.h for both termcap and terminfo (before I wasn't including curses.h when termcap was selected). 12/02/2002 mcs@astro.caltech.edu Updated version number to 1.4.1, ready for a micro release. 12/02/2002 mcs@astro.caltech.edu html/index.html Added Mac OS X and Cygwin to the list of systems that can compile libtecla. 12/02/2002 mcs@astro.caltech.edu getline.c Under Mac OS X, the tputs() callback function returns void, instead of the int return value used by other systems. This declaration is now used if both __MACH__ and __APPLE__ are defined. Hopefully these are the correct system macros to check. Thanks for Stephan Fiedler for providing information on Mac OS X. 11/02/2002 mcs@astro.caltech.edu configure.in configure getline.c Some systems don't have term.h, and others have it hidden in an ncurses sub-directory of the standard system include directory. If term.h can't be found, simply don't include it. If it is in an ncurses sub-directory, include ncurses/term.h instead of term.h. 04/02/2002 mcs@astro.caltech.edu configure.in configure Makefile.in Makefile.rules Use ranlib on systems that need it (Mac OS X). Also, make all components of the installation directories where needed, instead of assuming that they exist. 04/02/2002 mcs@astro.caltech.edu getline.c When the tab completion binding was unbound from the tab key, hitting the tab key caused gl_get_line() to ring the bell instead of inserting a tab character. This is problematic when using the 'enhance' program with Jython, since tabs are important in Python. I have corrected this. 10/12/2001 Version 1.4.0 released. 10/12/2001 mcs@astro.caltech.edu getline.c If the TIOCGWINSZ ioctl doesn't work, as is the case when running in an emacs shell, leave the size unchanged, rather than returning a fatal error. 07/12/2001 mcs@astro.caltech.edu configure.in configure Now that the configure version of CFLAGS is included in the makefile, I noticed that the optimization flags -g and -O2 had been added. It turns out that if CFLAGS isn't already set, the autoconf AC_PROG_CC macro initializes it with these two optimization flags. Since this would break backwards compatibility in embedded distributions that already use the OPT= makefile argument, and because turning debugging on needlessly bloats the library, I now make sure that CFLAGS is set before calling this macro. 07/12/2001 mcs@astro.caltech.edu enhance.c Use argv[0] in error reports instead of using a hardcoded macro. 07/12/2001 mcs@astro.caltech.edu getline.c The cut buffer wasn't being cleared after being used as a work buffer by gl_load_history(). 06/12/2001 mcs@astro.caltech.edu configure.in configure I removed my now redundant definition of SUN_TPUTS from CFLAGS. I also added "-I/usr/include" to CFLAGS under Solaris to prevent gcc from seeing conflicting versions of system header files in /usr/local/include. 06/12/2001 Markus Gyger (logged here by mcs) Lots of files. Lots of corrections to misspellings and typos in the comments. getline.c Markus reverted a supposed fix that I added a day or two ago. I had incorrectly thought that in Solaris 8, Sun had finally brought their declaration of the callback function of tputs() into line with other systems, but it turned out that gcc was pulling in a GNU version of term.h from /usr/local/include, and this was what confused me. 05/12/2001 mcs@astro.caltech.edu Makefile.in I added @CFLAGS@ to the CFLAGS assignment, so that if CFLAGS is set as an environment variable when configure is run, the corresponding make variable includes its values in the output makefile. 05/12/2001 mcs@astro.caltech.edu getline.c libtecla.h libtecla.map man3/gl_get_line.3 man3/gl_last_signal.3 I added a function that programs can use to find out which signal caused gl_get_line() to return EINTR. 05/12/2001 mcs@astro.caltech.edu getline.c When the newline action was triggered by a printable character, it failed to display that character. It now does. Also, extra control codes that I had added, to clear to the end of the display after the carriage return, but before displaying the prompt, were confusing expect scripts, so I have removed them. This step is now done instead in gl_redisplay() after displaying the full input line. 05/12/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 A user convinced me that continuing to invoke meta keybindings for meta characters that are printable is a bad idea, as is allowing users to ask to have setlocale() called behind the application's back. I have thus changed this. The setlocale configuration option has gone, and gl_get_line() is now completely 8-bit clean, by default. This means that if a meta character is printable, it is treated as a literal character, rather than a potential M-c binding. Meta bindings can still be invoked via their Esc-c equivalents, and indeed most terminal emulators either output such escape pairs by default when the meta character is pressed, or can be configured to do so. I have documented how to configure xterm to do this, in the man page. 03/12/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 gl_get_line() by default now prints any 8-bit printable characters that don't match keybindings. Previously characters > 127 were only printed if preceded by the literal-next action. Alternatively, by placing the command literal_if_printable in the tecla configuration file, all printable characters are treated as literal characters, even if they are bound to action functions. For international users of programs written by programmers that weren't aware of the need to call setlocale() to support alternate character sets, the configuration file can now also contain the single-word command "setlocale", which tells gl_get_line() to remedy this. 27/11/2001 mcs@astro.caltech.edu demo.c demo2.c enhance man3/gl_get_line.3 All demos and programs now call setlocale(LC_CTYPE,""). This makes them support character sets of different locales, where specified with the LC_CTYPE, LC_ALL, or LANG environment variables. I also added this to the demo in the man page, and documented its effect. 27/11/2001 mcs@astro.caltech.edu getline.c When displaying unsigned characters with values over 127 literally, previously it was assumed that they would all be displayable. Now isprint() is consulted, and if it says that a character isn't printable, the character code is displayed in octal like \307. In non-C locales, some characters with values > 127 are displayable, and isprint() tells gl_get_line() which are and which aren't. 27/11/2001 mcs@astro.caltech.edu getline.c pathutil.c history.c enhance.c demo2.c All arguments of the ctype.h character class functions are now cast to (int)(unsigned char). Previously they were cast to (int), which doesn't correctly conform to the requirements of the C standard, and could cause problems for characters with values > 127 on systems with signed char's. 26/11/2001 mcs@astro.caltech.edu man3/enhance.3 man3/libtecla.3 I started writing a man page for the enhance program. 26/11/2001 mcs@astro.caltech.edu Makefile.in Makefile.rules INSTALL It is now possible to specify whether the demos and other programs are to be built, by overriding the default values of the DEMOS, PROGRAMS and PROGRAMS_R variables. I have also documented the BINDIR variable and the install_bin makefile target. 22/11/2001 mcs@astro.caltech.edu getline.c libtecla.h libtecla.map man3/gl_get_line.3 man3/gl_ignore_signal.3 man3/gl_trap_signal.3 Signal handling has now been modified to be customizable. Signals that are trapped by default can be removed from the list of trapped signals, and signals that aren't currently trapped, can be added to the list. Applications can also specify the signal and terminal environments in which an application's signal handler is invoked, and what gl_get_line() does after the signal handler returns. 13/11/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 Added half-bright, reverse-video and blinking text to the available prompt formatting options. getline.c Removed ^O from the default VT100 sgr0 capability string. Apparently it can cause problems with some terminal emulators, and we don't need it, since it turns off the alternative character set mode, which we don't use. getline.c gl_tigetstr() and gl_tgetstr() didn't guard against the error returns of tigetstr() and tgetstr() respectively. They now do. 11/11/2001 mcs@astro.caltech.edu getline.c libtecla.h libtecla.map man3/gl_get_line.3 man3/gl_prompt_style.3 Although the default remains to display the prompt string literally, the new gl_prompt_style() function can be used to enable text attribute formatting directives in prompt strings, such as underlining, bold font, and highlighting directives. 09/11/2001 mcs@astro.caltech.edu enhance.c Makefile.rules configure.in configure I added a new program to the distribution that allows one to run most third party programs with the tecla library providing command-line editing. 08/11/2001 mcs@astro.caltech.edu libtecla.h getline.c man3/gl_get_line.3 history.c history.h I added a max_lines argument to gl_show_history() and _glh_show_history(). This can optionally be used to set a limit on the number of history lines displayed. libtecla.h getline.c man3/gl_get_line.3 I added a new function called gl_replace_prompt(). This can be used by gl_get_line() callback functions to request that a new prompt be use when they return. 06/11/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 I implemented, bound and documented the list-history action, used for listing historical lines of the current history group. getline.c man3/gl_get_line.3 man3/gl_echo_mode.3 I wrote functions to specify and query whether subsequent lines will be visible as they are being typed. 28/10/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 For those cases where a terminal provides its own high-level terminal editing facilities, you can now specify an edit-mode argument of 'none'. This disables all tecla key bindings, and by using canonical terminal input mode instead of raw input mode, editing is left up to the terminal driver. 21/10/2001 mcs@astro.caltech.edu libtecla.h getline.c history.c history.h man3/gl_get_line.3 man3/gl_history_info.3 I added the new gl_state_of_history(), gl_range_of_history() and gl_size_of_history() functions for querying information about the history list. history.c While testing the new gl_size_of_history() function, I noticed that when the history buffer wrapped, any location nodes of old lines between the most recent line and the end of the buffer weren't being removed. This could result in bogus entries appearing at the start of the history list. Now fixed. 20/10/2001 mcs@astro.caltech.edu libtecla.h getline.c history.c history.h man3/gl_get_line.3 man3/gl_lookup_history.3 I added a function called gl_lookup_history(), that the application can use to lookup lines in the history list. libtecla.h getline.c history.c history.h man3/gl_get_line.3 gl_show_history() now takes a format string argument to control how the line is displayed, and with what information. It also now provides the option of either displaying all history lines or just those of the current history group. getline.c man3/gl_get_line.3 gl_get_line() only archives lines in the history buffer if the newline action was invoked by a newline or carriage return character. 16/10/2001 mcs@astro.caltech.edu history.c history.h getline.c libtecla.h libtecla.map man3/gl_get_line.3 man3/gl_resize_history.3 man3/gl_limit_history.3 man3/gl_clear_history.3 man3/gl_toggle_history.3 I added a number of miscellaneous history configuration functions. You can now resize or delete the history buffer, limit the number of lines that are allowed in the buffer, clear either all history or just the history of the current history group, and temporarily enable and disable the history mechanism. 13/10/2001 mcs@astro.caltech.edu getline.c tputs_fp is now only declared if using termcap or terminfo. getline.c libtecla.map man3/gl_get_line.3 man3/gl_terminal_size.3 I added a public gl_terminal_size() function for updating and querying the current size of the terminal. update_version configure.in libtecla.h A user noted that on systems where the configure script couldn't be used, it was inconvenient to have the version number macros set by the configure script, so they are now specified in libtecla.h. To reduce the likelihood that the various files where the version number now appears might get out of sync, I have written the update_version script, which changes the version number in all of these files to a given value. 01/10/2001 mcs@astro.caltech.edu getline.c history.c history.h man3/gl_get_line.3 I added a max_lines argument to gl_save_history(), to allow people to optionally place a ceiling on the number of history lines saved. Specifying this as -1 sets the ceiling to infinity. 01/10/2001 mcs@astro.caltech.edu configure.in configure Under digital unix, getline wouldn't compile with _POSIX_C_SOURCE set, due to type definitions needed by select being excluded by this flag. Defining the _OSF_SOURCE macro as well on this system, resolved this. 30/09/2001 mcs@astro.caltech.edu getline.c libtecla.h history.c history.h man3/gl_get_line.3 man3/gl_group_history.3 I implemented history streams. History streams effectively allow multiple history lists to be stored in a single history buffer. Lines in the buffer are tagged with the current stream identification number, and lookups only consider lines that are marked with the current stream identifier. getline.c libtecla.h history.c history.h man3/gl_get_line.3 man3/gl_show_history.3 The new gl_show_history function displays the current history to a given stdio output stream. 29/09/2001 mcs@astro.caltech.edu getline.c Previously new_GetLine() installed a persistent signal handler to be sure to catch the SIGWINCH (terminal size change) signal between calls to gl_get_line(). This had the drawback that if multiple GetLine objects were created, only the first GetLine object used after the signal was received, would see the signal and adapt to the new terminal size. Instead of this, a signal handler for sigwinch is only installed while gl_get_line() is running, and just after installing this handler, gl_get_line() checks for terminal size changes that might have occurred while the signal handler wasn't installed. getline.c Dynamically allocated copies of capability strings looked up in the terminfo or termcap databases are now made, so that calls to setupterm() etc for one GetLine object don't get trashed when another GetLine object calls setupterm() etc. It is now safe to allocate and use multiple GetLine objects, albeit only within a single thread. 28/09/2001 mcs@astro.caltech.edu version.c Makefile.rules I added a function for querying the version number of the library. 26/09/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 I added the new gl_watch_fd() function, which allows applications to register callback functions to be invoked when activity is seen on arbitrary file descriptors while gl_get_line() is awaiting keyboard input from the user. keytab.c If a request is received to delete a non-existent binding, which happens to be an ambiguous prefix of other bindings no complaint is now generated about it being ambiguous. 23/09/2001 mcs@astro.caltech.edu getline.c history.c history.h man3/gl_get_line.3 libtecla.map demo.c I added new public functions for saving and restoring the contents of the history list. The demo program now uses these functions to load and save history in ~/.demo_history. 23/09/2001 mcs@astro.caltech.edu getline.c On trying the demo for the first time on a KDE konsole terminal, I discovered that the default M-O binding to repeat history was hiding the arrow keys, which are M-OA etc. I have removed this binding. The M-o (ie the lower case version of this), is still bound. 18/09/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 libtecla.map Automatic reading of ~/.teclarc is now postponed until the first call to gl_get_line(), to give the application the chance to specify alternative configuration sources with the new function gl_configure_getline(). The latter function allows configuration to be done with a string, a specified application-specific file, and/or a specified user-specific file. I also added a read-init-files action function, for re-reading the configuration files, if any. This is by default bound to ^X^R. This is all documented in gl_get_line.3. 08/09/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 It is now possible to bind actions to key-sequences that start with printable characters. Previously keysequences were required to start with meta or control characters. This is documented in gl_get_line.3. getline.c man3/gl_get_line.3 A customized completion function can now arrange for gl_get_line() to return the current input line whenever a successful completion has been made. This is signalled by setting the last character of the optional continuation suffix to a newline character. This is documented in gl_get_line.3. 05/07/2001 Bug reported by Mike MacFaden, fixed by mcs configure.in There was a bug in the configure script that only revealed itself on systems without termcap but not terminfo (eg. NetBSD). I traced the bug back to a lack of sufficient quoting of multi-line m4 macro arguments in configure.in, and have now fixed this and recreated the configure script. 05/07/2001 Bug reported and patched by Mike MacFaden (patch modified by mcs to match original intentions). getline.c getline.c wouldn't compile when termcap was selected as the terminal information database. setupterm() was being passed a non-existent variable, in place of the term[] argument of gl_control_strings(). Also if gl_change_terminal() is called with term==NULL, "ansi" is now substituted. 02/07/2001 Version 1.3.3 released. 27/06/2001 mcs@astro.caltech.edu getline.c expand.c cplmatch.c Added checks to fprintf() statements that write to the terminal. getline.c Move the cursor to the end of the line before suspending, so that the cursor doesn't get left in the middle of the input line. Makefile.in On systems that don't support shared libraries, the distclean target of make deleted libtecla.h. This has now been fixed. getline.c gl_change_terminal() was being called by gl_change_editor(), with the unwanted side effect that raw terminal modes were stored as those to be restored later, if called by an action function. gl_change_terminal() was being called in this case to re-establish terminal-specific key bindings, so I have just split this part of the function out into a separate function for both gl_change_editor() and gl_change_terminal() to call. 12/06/2001 mcs@astro.caltech.edu getline.c Signal handling has been improved. Many more signals are now trapped, and instead of using a simple flag set by a signal handler, race conditions are avoided by blocking signals during most of the gl_get_line() code, and unblocking them via calls to sigsetjmp(), just before attempting to read each new character from the user. The matching use of siglongjmp() in the signal handlers ensures that signals are reblocked correctly before they are handled. In most cases, signals cause gl_get_line() to restore the terminal modes and signal handlers of the calling application, then resend the signal to the application. In the case of SIGINT, SIGHUP, SIGPIPE, and SIGQUIT, if the process still exists after the signals are resent, gl_get_line() immediately returns with appropriate values assigned to errno. If SIGTSTP, SIGTTIN or SIGTTOU signals are received, the process is suspended. If any other signal is received, and the process continues to exist after the signal is resent to the calling application, line input is resumed after the terminal is put back into raw mode, the gl_get_line() signal handling is restored, and the input line redrawn. man/gl_get_line(3) I added a SIGNAL HANDLING section to the gl_get_line() man page, describing the new signal handling features. 21/05/2001 Version 1.3.2 released. 21/05/2001 mcs@astro.caltech.edu getline.c When vi-replace-char was used to replace the character at the end of the line, it left the cursor one character to its right instead of on top of it. Now rememdied. getline.c When undoing, to properly emulate vi, the cursor is now left at the leftmost of the saved and current cursor positions. getline.c man3/gl_get_line.3 Implemented find-parenthesis (%), delete-to-paren (M-d%), vi-change-to-paren (M-c%), copy-to-paren (M-y%). cplfile.c pcache.c In three places I was comparing the last argument of strncmp() to zero instead of the return value of strncmp(). 20/05/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 Implemented and documented the vi-repeat-change action, bound to the period key. This repeats the last action that modified the input line. 19/05/2001 mcs@astro.caltech.edu man3/gl_get_line.3 I documented the new action functions and bindings provided by Tim Eliseo, plus the ring-bell action and the new "nobeep" configuration option. getline.c I modified gl_change_editor() to remove and reinstate the terminal settings as well as the default bindings, since these have editor-specific differences. I also modified it to not abort if a key-sequence can't be bound for some reason. This allows the new vi-mode and emacs-mode bindings to be used safely. getline.c When the line was re-displayed on receipt of a SIGWINCH signal, the result wasn't visible until the next character was typed, since a call to fflush() was needed. gl_redisplay_line() now calls gl_flush_output() to remedy this. 17/05/2001 mcs@astro.catlech.edu getline.c Under Linux, calling fflush(gl->output_fd) hangs if terminal output has been suspended with ^S. With the tecla library taking responsability for reading the stop and start characters this was a problem, because once hung in fflush(), the keyboard input loop wasn't entered, so the user couldn't type the start character to resume output. To remedy this, I now have the terminal process these characters, rather than the library. 12/05/2001 mcs@astro.caltech.edu getline.c The literal-next action is now implemented as a single function which reads the next character itself. Previously it just set a flag which effected the interpretation of the next character read by the input loop. getline.c Added a ring-bell action function. This is currently unbound to any key by default, but it is used internally, and can be used by users that want to disable any of the default key-bindings. 12/05/2001 Tim Eliseo (logged here by mcs) getline.c Don't reset gl->number until after calling an action function. By looking at whether gl->number is <0 or not, action functions can then tell whether the count that they were passed was explicitly specified by the user, as opposed to being defaulted to 1. getline.c In vi, the position at which input mode is entered acts as a barrier to backward motion for the few backward moving actions that are enabled in input mode. Tim added this barrier to getline. getline.c In gl_get_line() after reading an input line, or having the read aborted by a signal, the sig_atomic_t gl_pending_signal was being compared to zero instead of -1 to see if no signals had been received. gl_get_line() will thus have been calling raise(-1), which luckily didn't seem to do anything. Tim also arranged for errno to be set to EINTR when a signal aborts gl_get_line(). getline.c The test in gl_add_char_to_line() for detecting when overwriting a character with a wider character, had a < where it needed a >. Overwriting with a wider character thus overwrote trailing characters. Tim also removed a redundant copy of the character into the line buffer. getline.c gl_cursor_left() and gl->cursor_right() were executing a lot of redundant code, when the existing call to the recently added gl_place_cursor() function, does all that is necessary. getline.c Remove redundant code from backward_kill_line() by re-implimenting in terms of gl_place_cursor() and gl_delete_chars(). getline.c gl_forward_delete_char() now records characters in cut buffer when in vi command mode. getline.c In vi mode gl_backward_delete_char() now only deletes up to the point at which input mode was entered. Also gl_delete_chars() restores from the undo buffer when deleting in vi insert mode. getline.c Added action functions, vi-delete-goto-column, vi-change-to-bol, vi-change-line, emacs-mode, vi-mode, vi-forward-change-find, vi-backward-change-find, vi-forward-change-to, vi-backward-change-to, vi-change-goto-col, forward-delete-find, backward-delete-find, forward-delete-to, backward-delete-to, delete-refind, delete-invert-refind, forward-copy-find, backward-copy-find, forward-copy-to, backward-copy-to copy-goto-column, copy-rest-of-line, copy-to-bol, copy-line, history-re-search-forward, history-re-search-backward. 06/05/2001 Version 1.3.1 released. 03/05/2001 mcs@astro.caltech.edu configure.in Old versions of GNU ld don't accept version scripts. Under Linux I thus added a test to try out ld with the --version-script argument to see if it works. If not, version scripts aren't used. configure.in My test for versions of Solaris earlier than 7 failed when confronted by a three figure version number (2.5.1). Fixed. 30/04/2001 mcs@astro.caltech.edu getline.c In vi mode, history-search-backward and history-search-forward weren't doing anything when invoked at the start of an empty line, whereas they should have acted like up-history and down-history. Makefile.in Makefile.rules When shared libraries are being created, the build procedure now arranges for any alternate library links to be created as well, before linking the demos. Without this the demos always linked to the static libraries (which was perfectly ok, but wasn't a good example). Makefile.in Makefile.rules On systems on which shared libraries were being created, if there were no alternate list of names, make would abort due to a Bourne shell 'for' statement that didn't have any arguments. Currently there are no systems who's shared library configurations would trigger this problem. Makefile.rules The demos now relink to take account of changes to the library. configure.in configure When determining whether the reentrant version of the library should be compiled by default, the configure script now attempts to compile a dummy program that includes all of the appropriate system headers and defines _POSIX_C_SOURCE. This should now be a robust test on systems which use C macros to alias these function names to other internal functions. configure.in Under Solaris 2.6 and earlier, the curses library is in /usr/ccs/lib. Gcc wasn't finding this. In addition to remedying this, I had to remove "-z text" from LINK_SHARED under Solaris to get it to successfully compile the shared library against the static curses library. configure.in Under Linux the -soname directive was being used incorrectly, citing the fully qualified name of the library instead of its major version alias. This will unfortunately mean that binaries linked with the 1.2.3 and 1.2.4 versions of the shared library won't use later versions of the library unless relinked. 30/04/2001 mcs@astro.caltech.edu getline.c In gl_get_input_line(), don't redundantly copy the start_line if start_line == gl->line. 30/04/2001 Version 1.3.0 released. 28/04/2001 mcs@astro.caltech.edu configure.in I removed the --no-undefined directive from the Linux LINK_SHARED command. After recent patches to our RedHat 7.0 systems ld started reporting some internal symbols of libc as being undefined. Using nm on libc indicated that the offending symbols are indeed defined, albeit as "common" symbols, so there appears to be a bug in RedHat's ld. Removing this flag allows the tecla shared library to compile, and programs appear to function fine. man3/gl_get_line.3 The default key-sequence used to invoke the read-from-file action was incorrectly cited as ^Xi instead of ^X^F. 26/04/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 A new vi-style editing mode was added. This involved adding many new action functions, adding support for specifying editing modes in users' ~/.teclarc files, writing a higher level cursor motion function to support the different line-end bounds required in vi command mode, and a few small changes to support the fact that vi has two modes, input mode and command mode with different bindings. When vi editing mode is enabled, any binding that starts with an escape or a meta character, is interpreted as a command-mode binding, and switches the library to vi command mode if not already in that mode. Once in command mode the first character of all keysequences entered until input mode is re-enabled, are quietly coerced to meta characters before being looked up in the key-binding table. So, for example, in the key-binding table, the standard vi command-mode 'w' key, which moves the cursor one word to the right, is represented by M-w. This emulates vi's dual sets of bindings in a natural way without needing large changes to the library, or new binding syntaxes. Since cursor keys normally emit keysequences which start with escape, it also does something sensible when a cursor key is pressed during input mode (unlike true vi, which gets upset). I also added a ^Xg binding for the new list-glob action to both the emacs and vi key-binding tables. This lists the files that match the wild-card expression that precedes it on the command line. The function that reads in ~/.teclarc used to tell new_GetLine() to abort if it encountered anything that it didn't understand in this file. It now just reports an error and continues onto the next line. Makefile.in: When passing LIBS=$(LIBS) to recursive invokations of make, quotes weren't included around the $(LIBS) part. This would cause problems if LIBS ever contained more than one word (with the supplied configure script this doesn't happen currently). I added these quotes. expand.c man3/ef_expand_file.3: I wrote a new public function called ef_list_expansions(), to list the matching filenames returned by ef_expand_file(). I also fixed the example in the man page, which cited exp->file instead of exp->files, and changed the dangerous name 'exp' with 'expn'. keytab.c: Key-binding tables start with 100 elements, and are supposedly incremented in size by 100 elements whenever the a table runs out of space. The realloc arguments to do this were wrong. This would have caused problems if anybody added a lot of personal bindings in their ~/.teclarc file. I only noticed it because the number of key bindings needed by the new vi mode exceeded this number. libtecla.map ef_expand_file() is now reported as having been added in the upcoming 1.3.0 release. 25/03/2001 Markus Gyger (logged here by mcs) Makefile.in: Make symbolic links to alternative shared library names relative instead of absolute. Makefile.rules: The HP-UX libtecla.map.opt file should be made in the compilation directory, to allow the source code directory to be on a readonly filesystem. cplmatch.c demo2.c history.c pcache.c To allow the library to be compiled with a C++ compiler, without generating warnings, a few casts were added where void* return values were being assigned directly to none void* pointer variables. 25/03/2001 mcs@astro.caltech.edu libtecla.map: Added comment header to explain the purpose of the file. Also added cpl_init_FileArgs to the list of exported symbols. This symbol is deprecated, and no longer documented, but for backwards compatibility, it should still be exported. configure: I had forgotten to run autoconf before releasing version 1.2.4, so I have just belatedly done so. This enables Markus' changes to "configure.in" documented previously, (see 17/03/2001). 20/03/2001 John Levon (logged here by mcs) libtecla.h A couple of the function prototypes in libtecla.h have (FILE *) argument declarations, which means that stdio.h needs to be included. The header file should be self contained, so libtecla.h now includes stdio.h. 18/03/2001 Version 1.2.4 released. README html/index.html configure.in Incremented minor version from 3 to 4. 18/03/2001 mcs@astro.caltech.edu getline.c The fix for the end-of-line problem that I released a couple of weeks ago, only worked for the first line, because I was handling this case when the cursor position was equal to the last column, rather than when the cursor position modulo ncolumn was zero. Makefile.in Makefile.rules The demos are now made by default, their rules now being int Makefile.rules instead of Makefile.in. INSTALL I documented how to compile the library in a different directory than the distribution directory. I also documented features designed to facilitate configuring and building the library as part of another package. 17/03/2001 Markus Gyger (logged here by mcs) getline.c Until now cursor motions were done one at a time. Markus has added code to make use the of the terminfo capability that moves the cursor by more than one position at a time. This greatly improves performance when editing near the start of long lines. getline.c To further improve performance, Markus switched from writing one character at a time to the terminal, using the write() system call, to using C buffered output streams. The output buffer is only flushed when necessary. Makefile.rules Makefile.in configure.in Added support for compiling for different architectures in different directories. Simply create another directory and run the configure script located in the original directory. Makefile.in configure.in libtecla.map Under Solaris, Linux and HP-UX, symbols that are to be exported by tecla shared libraries are explicitly specified via symbol map files. Only publicly documented functions are thus visible to applications. configure.in When linking shared libraries under Solaris SPARC, registers that are reserved for applications are marked as off limits to the library, using -xregs=no%appl when compiling with Sun cc, or -mno-app-regs when compiling with gcc. Also removed -z redlocsym for Solaris, which caused problems under some releases of ld. homedir.c (after minor changes by mcs) Under ksh, ~+ expands to the current value of the ksh PWD environment variable, which contains the path of the current working directory, including any symbolic links that were traversed to get there. The special username "+" is now treated equally by tecla, except that it substitutes the return value of getcwd() if PWD either isn't set, or if it points at a different directory than that reported by getcwd(). 08/03/2001 Version 1.2.3 released. 08/03/2001 mcs@astro.caltech.edu getline.c On compiling the library under HP-UX for the first time I encountered and fixed a couple of bugs: 1. On all systems except Solaris, the callback function required by tputs() takes an int argument for the character that is to be printed. Under Solaris it takes a char argument. The callback function was passing this argument, regardless of type, to write(), which wrote the first byte of the argument. This was fine under Solaris and under little-endian systems, because the first byte contained the character to be written, but on big-endian systems, it always wrote the zero byte at the other end of the word. As a result, no control characters were being written to the terminal. 2. While attempting to start a newline after the user hit enter, the library was outputting the control sequence for moving the cursor down, instead of the newline character. On many systems the control sequence for moving the cursor down happends to be a newline character, but under HP-UX it isn't. The result was that no new line was being started under HP-UX. 04/03/2001 mcs@astro.caltech.edu configure.in Makefile.in Makefile.stub configure config.guess config.sub Makefile.rules install-sh PORTING README INSTALL Configuration and compilation of the library is now performed with the help of an autoconf configure script. In addition to relieving the user of the need to edit the Makefile, this also allows automatic compilation of the reentrant version of the library on platforms that can handle it, along with the creation of shared libraries where configured. On systems that aren't known to the configure script, just the static tecla library is compiled. This is currently the case on all systems except Linux, Solaris and HP-UX. In the hope that installers will provide specific conigurations for other systems, the configure.in script is heavily commented, and instructions on how to use are included in a new PORTING file. 24/02/2001 Version 1.2b released. 22/02/2001 mcs@astro.caltech.edu getline.c It turns out that most terminals, but not all, on writing a character in the rightmost column, don't wrap the cursor onto the next line until the next character is output. This library wasn't aware of this and thus if one tried to reposition the cursor from the last column, gl_get_line() thought that it was moving relative to a point on the next line, and thus moved the cursor up a line. The fix was to write one extra character when in the last column to force the cursor onto the next line, then backup the cursor to the start of the new line. getline.c On terminal initialization, the dynamic LINES and COLUMNS environment variables were ignored unless terminfo/termcap didn't return sensible dimensions. In practice, when present they should override the static versions in the terminfo/termcap databases. This is the new behavior. In reality this probably won't have caused many problems, because a SIGWINCH signal which informs of terminal size changes is sent when the terminal is opened, so the dimensions established during initialization quickly get updated on most systems. 18/02/2001 Version 1.2a released. 18/02/2001 mcs@astro.caltech.edu getline.c Three months ago I moved the point at which termios.h was included in getline.c. Unfortunately, I didn't notice that this moved it to after the test for TIOCGWINSZ being defined. This resulted in SIGWINCH signals not being trapped for, and thus terminal size changes went unnoticed. I have now moved the test to after the inclusion of termios.h. 12/02/2001 Markus Gyger (described here by mcs) man3/pca_lookup_file.3 man3/gl_get_line.3 man3/ef_expand_file.3 man3/cpl_complete_word.3 In the 1.2 release of the library, all functions in the library were given man pages. Most of these simply include one of the above 4 man pages, which describe the functions while describing the modules that they are in. Markus added all of these function names to the lists in the "NAME" headers of the respective man pages. Previously only the primary function of each module was named there. 11/02/2001 mcs@astro.caltech.edu getline.c On entering a line that wrapped over two or more terminal, if the user pressed enter when the cursor wasn't on the last of the wrapped lines, the text of the wrapped lines that followed it got mixed up with the next line written by the application, or the next input line. Somehow this slipped through the cracks and wasn't noticed until now. Anyway, it is fixed now. 09/02/2001 Version 1.2 released. 04/02/2001 mcs@astro.caltech.edu pcache.c libtecla.h With all filesystems local, demo2 was very fast to start up, but on a Sun system with one of the target directories being on a remote nfs mounted filesystem, the startup time was many seconds. This was due to the executable selection callback being applied to all files in the path at startup. To avoid this, all files are now included in the cache, and the application specified file-selection callback is only called on files as they are matched. Whether the callback rejected or accepted them is then cached so that the next time an already checked file is looked at, the callback doesn't have to be called. As a result, startup is now fast on all systems, and since usually there are only a few matching file completions at a time, the delay during completion is also usually small. The only exception is if the user tries to complete an empty string, at which point all files have to be checked. Having done this once, however, doing it again is fast. man3/pca_lookup_file.3 I added a man page documenting the new PathCache module. man3/.3 I have added man pages for all of the functions in each of the modules. These 1-line pages use the .so directive to redirect nroff to the man page of the parent module. man Makefile update_html I renamed man to man3 to make it easier to test man page rediction, and updated Makefile and update_html accordingly. I also instructed update_html to ignore 1-line man pages when making html equivalents of the man pages. cplmatch.c In cpl_list_completions() the size_t return value of strlen() was being used as the length argument of a "%*s" printf directive. This ought to be an int, so the return value of strlen() is now cast to int. This would have caused problems on architectures where the size of a size_t is not equal to the size of an int. 02/02/2001 mcs@astro.caltech.edu getline.c Under UNIX, certain terminal bindings are set using the stty command. This, for example, specifies which control key generates a user-interrupt (usually ^C or ^Y). What I hadn't realized was that ASCII NUL is used as the way to specify that one of these bindings is unset. I have now modified the code to skip unset bindings, leaving the corresponding action bound to the built-in default, or a user provided binding. 28/01/2001 mcs@astro.caltech.edu pcache.c libtecla.h A new module was added which supports searching for files in any colon separated list of directories, such as the unix execution PATH environment variable. Files in these directories, after being individually okayed for inclusion via an application provided callback, are cached in a PathCache object. You can then look up the full pathname of a given filename, or you can use the provided completion callback to list possible completions in the path-list. The contents of relative directories, such as ".", obviously can't be cached, so these directories are read on the fly during lookups and completions. The obvious application of this facility is to provide Tab-completion of commands, and thus a callback to place executable files in the cache, is provided. demo2.c This new program demonstrates the new PathCache module. It reads and processes lines of input until the word 'exit' is entered, or C-d is pressed. The default tab-completion callback is replaced with one which at the start of a line, looks up completions of commands in the user's execution path, and when invoked in other parts of the line, reverts to normal filename completion. Whenever a new line is entered, it extracts the first word on the line, looks it up in the user's execution path to see if it corresponds to a known command file, and if so, displays the full pathname of the file, along with the remaining arguments. cplfile.c I added an optional pair of callback function/data members to the new cpl_file_completions() configuration structure. Where provided, this callback is asked on a file-by-file basis, which files should be included in the list of file completions. For example, a callback is provided for listing only completions of executable files. cplmatch.c When listing completions, the length of the type suffix of each completion wasn't being taken into account correctly when computing the column widths. Thus the listing appeared ragged sometimes. This is now fixed. pathutil.c I added a function for prepending a string to a path, and another for testing whether a pathname referred to an executable file. 28/01/2001 mcs@astro.caltech.edu libtecla.h cplmatch.c man/cpl_complete_word.3 The use of a publically defined structure to configure the cpl_file_completions() callback was flawed, so a new approach has been designed, and the old method, albeit still supported, is no longer documented in the man pages. The definition of the CplFileArgs structure in libtecla.h is now accompanied by comments warning people not to modify it, since modifications could break applications linked to shared versions of the tecla library. The new method involves an opaque CplFileConf object, instances of which are returned by a provided constructor function, configured with provided accessor functions, and when no longer needed, deleted with a provided destructor function. This is documented in the cpl_complete_word man page. The cpl_file_completions() callback distinguishes what type of configuration structure it has been sent by virtue of a code placed at the beginning of the CplFileConf argument by its constructor. 04/01/2001 mcs@astro.caltech.edu (Release of version 1.1j) getline.c I added upper-case bindings for the default meta-letter keysequences such as M-b. They thus continue to work when the user has caps-lock on. Makefile I re-implemented the "install" target in terms of new install_lib, install_inc and install_man targets. When distributing the library with other packages, these new targets allows for finer grained control of the installation process. 30/12/2000 mcs@astro.caltech.edu getline.c man/gl_get_line.3 I realized that the recall-history action that I implemented wasn't what Markus had asked me for. What he actually wanted was for down-history to continue going forwards through a previous history recall session if no history recall session had been started while entering the current line. I have thus removed the recall-history action and modified the down-history action function accordingly. 24/12/2000 mcs@astro.caltech.edu getline.c I modified gl_get_line() to allow the previously returned line to be passed in the start_line argument. getline.c man/gl_get_line.3 I added a recall-history action function, bound to M^P. This recalls the last recalled history line, regardless of whether it was from the current or previous line. 13/12/2000 mcs@astro.caltech.edu (Release of version 1.1i) getline.c history.h history.c man/gl_get_line.3 I implemented the equivalent of the ksh Operate action. I have named the tecla equivalent "repeat-history". This causes the line that is to be edited to returned, and arranges for the next most recent history line to be preloaded on the next call to gl_get_line(). Repeated invocations of this action thus result in successive history lines being repeated - hence the name. Implementing the ksh Operate action was suggested by Markus Gyger. In ksh it is bound to ^O, but since ^O is traditionally bound by the default terminal settings, to stop-output, I have bound the tecla equivalent to M-o. 01/12/2000 mcs@astro.caltech.edu (Release of version 1.1h) getline.c keytab.c keytab.h man/gl_get_line.3 I added a digit-argument action, to allow repeat counts for actions to be entered. As in both tcsh and readline, this is bound by default to each of M-0, M-1 through to M-9, the number being appended to the current repeat count. Once one of these has been pressed, the subsequent digits of the repeat count can be typed with or without the meta key pressed. It is also possible to bind digit-argument to other keys, with or without a numeric final keystroke. See man page for details. getline.c man/gl_get_line.3 Markus noted that my choice of M-< for the default binding of read-from-file, could be confusing, since readline binds this to beginning-of-history. I have thus rebound it to ^X^F (ie. like find-file in emacs). getline.c history.c history.h man/gl_get_line.3 I have now implemented equivalents of the readline beginning-of-history and end-of-history actions. These are bound to M-< and M-> respectively. history.c history.h I Moved the definition of the GlHistory type, and its subordinate types from history.h to history.c. There is no good reason for any other module to have access to the innards of this structure. 27/11/2000 mcs@astro.caltech.edu (Release of version 1.1g) getline.c man/gl_get_line.3 I added a "read-from-file" action function and bound it by default to M-<. This causes gl_get_line() to temporarily return input from the file who's name precedes the cursor. 26/11/2000 mcs@astro.caltech.edu getline.c keytab.c keytab.h man/gl_get_line.3 I have reworked some of the keybinding code again. Now, within key binding strings, in addition to the previously existing notation, you can now use M-a to denote meta-a, and C-a to denote control-a. For example, a key binding which triggers when the user presses the meta key, the control key and the letter [ simultaneously, can now be denoted by M-C-[, or M-^[ or \EC-[ or \E^[. I also updated the man page to use M- instead of \E in the list of default bindings, since this looks cleaner. getline.c man/gl_get_line.3 I added a copy-region-as-kill action function and gave it a default binding to M-w. 22/11/2000 mcs@astro.caltech.edu *.c Markus Gyger sent me a copy of a previous version of the library, with const qualifiers added in appropriate places. I have done the same for the latest version. Among other things, this gets rid of the warnings that are generated if one tells the compiler to const qualify literal strings. getline.c getline.h glconf.c I have moved the contents of glconf.c and the declaration of the GetLine structure into getline.c. This is cleaner, since now only functions in getline.c can mess with the innards of GetLine objects. It also clears up some problems with system header inclusion order under Solaris, and also the possibility that this might result in inconsistent system macro definitions, which in turn could cause different declarations of the structure to be seen in different files. hash.c I wrote a wrapper function to go around strcmp(), such that when hash.c is compiled with a C++ compiler, the pointer to the wrapper function is a C++ function pointer. This makes it compatible with comparison function pointer recorded in the hash table. cplmatch.c getline.c libtecla.h Markus noted that the Sun C++ compiler wasn't able to match up the declaration of cpl_complete_word() in libtecla.h, where it is surrounded by a extern "C" {} wrapper, with the definition of this function in cplmatch.c. My suspicion is that the compiler looks not only at the function name, but also at the function arguments to see if two functions match, and that the match_fn() argument, being a fully blown function pointer declaration, got interpetted as that of a C function in one case, and a C++ function in the other, thus preventing a match. To fix this I now define a CplMatchFn typedef in libtecla.h, and use this to declare the match_fn callback. 20/11/2000 (Changes suggested by Markus Gyger to support C++ compilers): expand.c Renamed a variable called "explicit" to "xplicit", to avoid conflicts when compiling with C++ compilers. *.c Added explicit casts when converting from (void *) to other pointer types. This isn't needed in C but it is in C++. getline.c tputs() has a strange declaration under Solaris. I was enabling this declaration when the SPARC feature-test macro was set. Markus changed the test to hinge on the __sun and __SVR4 macros. direader.c glconf.c stringrp.c I had omitted to include string.h in these two files. Markus also suggested some other changes, which are still under discussion. With the just above changes however, the library compiles without complaint using g++. 19/11/2000 mcs@astro.caltech.edu getline.h getline.c keytab.c keytab.h glconf.c man/gl_get_line.3 I added support for backslash escapes (include \e for the keyboard escape key) and literal binary characters to the characters allowed within key sequences of key bindings. getline.h getline.c keytab.c keytab.h glconf.c man/gl_get_line.3 I introduced symbolic names for the arrow keys, and modified the library to use the cursor key sequences reported by terminfo/termcap in addition to the default ANSI ones. Anything bound to the symbolically named arrow keys also gets bound to the default and terminfo/termcap cursor key sequences. Note that under Solaris terminfo/termcap report the properties of hardware X terminals when TERM is xterm instead of the terminal emulator properties, and the cursor keys on these two systems generate different key sequences. This is an example of why extra default sequences are needed. getline.h getline.c keytab.c For some reason I was using \e to represent the escape character. This is supported by gcc, which thus doesn't emit a warning except with the -pedantic flag, but isn't part of standard C. I now use a macro to define escape as \033 in getline.h, and this is now used wherever the escape character is needed. 17/11/2000 mcs@astro.caltech.edu (Release of version 1.1d) getline.c, man/gl_get_line(3), html/gl_get_line.html In tcsh ^D is bound to a function which does different things depending on where the cursor is within the input line. I have implemented its equivalent in the tecla library. When invoked at the end of the line this action function displays possible completions. When invoked on an empty line it causes gl_get_line() to return NULL, thus signalling end of input. When invoked within a line it invokes forward-delete-char, as before. The new action function is called del-char-or-list-or-eof. getline.c, man/gl_get_line(3), html/gl_get_line.html I found that the complete-word and expand-file actions had underscores in their names instead of hyphens. This made them different from all other action functions, so I have changed the underscores to hyphens. homedir.c On SCO UnixWare while getpwuid_r() is available, the associated _SC_GETPW_R_SIZE_MAX macro used by sysconf() to find out how big to make the buffer to pass to this function to cater for any password entry, doesn't exist. I also hadn't catered for the case where sysconf() reports that this limit is indeterminate. I have thus change the code to substitute a default limit of 1024 if either the above macro isn't defined or if sysconf() says that the associated limit is indeterminate. 17/11/2000 mcs@astro.caltech.edu (Release of version 1.1c) getline.c, getline.h, history.c, history.h I have modified the way that the history recall functions operate, to make them better emulate the behavior of tcsh. Previously the history search bindings always searched for the prefix that preceded the cursor, then left the cursor at the same point in the line, so that a following search would search using the same prefix. This isn't how tcsh operates. On finding a matching line, tcsh puts the cursor at the end of the line, but arranges for the followup search to continue with the same prefix, unless the user does any cursor motion or character insertion operations in between, in which case it changes the search prefix to the new set of characters that are before the cursor. There are other complications as well, which I have attempted to emulate. As far as I can tell, the tecla history recall facilities now fully emulate those of tcsh. 16/11/2000 mcs@astro.caltech.edu (Release of version 1.1b) demo.c: One can now quit from the demo by typing exit. keytab.c: The first entry of the table was getting deleted by _kt_clear_bindings() regardless of the source of the binding. This deleted the up-arrow binding. Symptoms noted by gazelle@yin.interaccess.com. getline.h: Depending on which system include files were include before the inclusion of getline.h, SIGWINCH and TIOCGWINSZ might or might not be defined. This resulted in different definitions of the GetLine object in different files, and thus some very strange bugs! I have now added #includes for the necessary system header files in getline.h itself. The symptom was that on creating a ~/.teclarc file, the demo program complained of a NULL argument to kt_set_keybinding() for the first line of the file. 15/11/2000 mcs@astro.caltech.edu (Release of version 1.1a) demo.c: I had neglected to check the return value of new_GetLine() in the demo program. Oops. getline.c libtecla.h: I wrote gl_change_terminal(). This allows one to change to a different terminal or I/O stream, by specifying the stdio streams to use for input and output, along with the type of terminal that they are connected to. getline.c libtecla.h: Renamed GetLine::isterm to GetLine::is_term. Standard C reserves names that start with "is" followed by alphanumeric characters, so this avoids potential clashes in the future. keytab.c keytab.h Each key-sequence can now have different binding functions from different sources, with the user provided binding having the highest precedence, followed by the default binding, followed by any terminal specific binding. This allows gl_change_terminal() to redefine the terminal-specific bindings each time that gl_change_terminal() is called, without overwriting the user specified or default bindings. In the future, it will also allow for reconfiguration of user specified bindings after the call to new_GetLine(). Ie. deleting a user specified binding should reinstate any default or terminal specific binding. man/cpl_complete_word.3 html/cpl_complete_word.html man/ef_expand_file.3 html/ef_expand_file.html man/gl_get_line.3 html/gl_get_line.html I added sections on thread safety to the man pages of the individual modules. man/gl_get_line.3 html/gl_get_line.html I documented the new gl_change_terminal() function. man/gl_get_line.3 html/gl_get_line.html In the description of the ~/.teclarc configuration file, I had omitted the 'bind' command word in the example entry. I have now remedied this. yuma123_2.14/libtecla/libtecla.h0000664000175000017500000024360414770023131016643 0ustar vladimirvladimir#ifndef libtecla_h #define libtecla_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #ifdef __cplusplus extern "C" { #endif #include /* FILE * */ #include /* size_t */ #include /* time_t */ #include /* struct sigaction */ /* * The following are the three components of the libtecla version number. * Note that it is better to use the libtecla_version() function than these * macros since the macros only tell you which version of the library your * code was compiled against, whereas the libtecla_version() function * tells you which version of the shared tecla library your program is * actually linked to. */ #define TECLA_MAJOR_VER 1 #define TECLA_MINOR_VER 6 #define TECLA_MICRO_VER 1 /*....................................................................... * Query the version number of the tecla library. * * Input: * major int * The major version number of the library * will be assigned to *major. This number is * only incremented when a change to the library is * made that breaks binary (shared library) and/or * compilation backwards compatibility. * minor int * The minor version number of the library * will be assigned to *minor. This number is * incremented whenever new functions are added to * the public API. * micro int * The micro version number of the library will be * assigned to *micro. This number is incremented * whenever internal changes are made that don't * change the public API, such as bug fixes and * performance enhancements. */ void libtecla_version(int *major, int *minor, int *micro); /*----------------------------------------------------------------------- * The getline module provides interactive command-line input, recall * and editing by users at terminals. See the gl_getline(3) man page for * more details. *-----------------------------------------------------------------------*/ /* * Provide an opaque handle for the resource object that is defined in * getline.h. */ typedef struct GetLine GetLine; /* * The following two functions are used to create and delete the * resource objects that are used by the gl_getline() function. */ GetLine *new_GetLine(size_t linelen, size_t histlen); GetLine *del_GetLine(GetLine *gl); /* * Read a line into an internal buffer of gl. */ char *gl_get_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos); /*....................................................................... * Prompt the user for a single-character reply. * * Input: * gl GetLine * A resource object returned by new_GetLine(). * prompt char * The prompt to prefix the query with, or NULL * to reuse the previous prompt. * defchar char The character to substitute if the * user simply hits return, or '\n' if you don't * need to substitute anything. * Output: * return int The character that was read, or EOF if the read * had to be aborted (in which case you can call * gl_return_status() to find out why). */ int gl_query_char(GetLine *gl, const char *prompt, char defchar); /*....................................................................... * Read a single uninterpretted character from the user, without * displaying anything. * * Input: * gl GetLine * A resource object previously returned by * new_GetLine(). * Output: * return int The character that was read, or EOF if the read * had to be aborted (in which case you can call * gl_return_status() to find out why). */ int gl_read_char(GetLine *gl); /* * Configure the application specific and/or user-specific behavior of * gl_get_line(). */ int gl_configure_getline(GetLine *gl, const char *app_string, const char *app_file, const char *user_file); /* * The following enumerators specify the origin of a key binding, and * are listed in order of decreasing priority, such that user-specified * key-bindings take precedence over application default bindings. */ typedef enum { GL_USER_KEY, /* A key-binding specified by the user */ GL_APP_KEY /* A key-binding specified by the application */ } GlKeyOrigin; /* * Bind a key sequence to a given action. If action==NULL, unbind the * key-sequence. */ int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin, const char *keyseq, const char *action); /*----------------------------------------------------------------------- * The file-expansion module provides facilities for expanding ~user/ and * $envvar expressions, and for expanding glob-style wildcards. * See the ef_expand_file(3) man page for more details. *-----------------------------------------------------------------------*/ /* * ExpandFile objects contain the resources needed to expand pathnames. */ typedef struct ExpandFile ExpandFile; /* * The following functions are used to create and delete the resource * objects that are used by the ef_expand_file() function. */ ExpandFile *new_ExpandFile(void); ExpandFile *del_ExpandFile(ExpandFile *ef); /* * A container of the following type is returned by ef_expand_file(). */ typedef struct { int exists; /* True if the files in files[] currently exist. */ /* This only time that this may not be true is if */ /* the input filename didn't contain any wildcards */ /* and thus wasn't matched against existing files. */ /* In this case the single entry in 'nfile' may not */ /* refer to an existing file. */ int nfile; /* The number of files in files[] */ char **files; /* An array of 'nfile' filenames. */ } FileExpansion; /* * The ef_expand_file() function expands a specified pathname, converting * ~user/ and ~/ patterns at the start of the pathname to the * corresponding home directories, replacing $envvar with the value of * the corresponding environment variable, and then, if there are any * wildcards, matching these against existing filenames. * * If no errors occur, a container is returned containing the array of * files that resulted from the expansion. If there were no wildcards * in the input pathname, this will contain just the original pathname * after expansion of ~ and $ expressions. If there were any wildcards, * then the array will contain the files that matched them. Note that * if there were any wildcards but no existing files match them, this * is counted as an error and NULL is returned. * * The supported wildcards and their meanings are: * * - Match any sequence of zero or more characters. * ? - Match any single character. * [chars] - Match any single character that appears in 'chars'. * If 'chars' contains an expression of the form a-b, * then any character between a and b, including a and b, * matches. The '-' character looses its special meaning * as a range specifier when it appears at the start * of the sequence of characters. * [^chars] - The same as [chars] except that it matches any single * character that doesn't appear in 'chars'. * * Wildcard expressions are applied to individual filename components. * They don't match across directory separators. A '.' character at * the beginning of a filename component must also be matched * explicitly by a '.' character in the input pathname, since these * are UNIX's hidden files. * * Input: * fe ExpandFile * The pathname expansion resource object. * path const char * The path name to be expanded. * pathlen int The length of the suffix of path[] that * constitutes the filename to be expanded, * or -1 to specify that the whole of the * path string should be used. * Output: * return FileExpansion * A pointer to a results container within the * given ExpandFile object. This contains an * array of the pathnames that resulted from * expanding ~ and $ expressions and from * matching any wildcards, sorted into lexical * order. * * This container and its contents will be * recycled on subsequent calls, so if you need * to keep the results of two successive runs, * you will either have to allocate a private * copy of the array, or use two ExpandFile * objects. * * On error, NULL is returned. A description * of the error can be acquired by calling the * ef_last_error() function. */ FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen); /*....................................................................... * Print out an array of matching files. * * Input: * result FileExpansion * The container of the sorted array of * expansions. * fp FILE * The output stream to write to. * term_width int The width of the terminal. * Output: * return int 0 - OK. * 1 - Error. */ int ef_list_expansions(FileExpansion *result, FILE *fp, int term_width); /* * The ef_last_error() function returns a description of the last error * that occurred in a call ef_expand_file(). Note that this message is * contained in an array which is allocated as part of *ef, and its * contents thus potentially change on every call to ef_expand_file(). */ const char *ef_last_error(ExpandFile *ef); /*----------------------------------------------------------------------- * The WordCompletion module is used for completing incomplete words, such * as filenames. Programs can use functions within this module to register * their own customized completion functions. *-----------------------------------------------------------------------*/ /* * Ambiguous completion matches are recorded in objects of the * following type. */ typedef struct WordCompletion WordCompletion; /* * Create a new completion object. */ WordCompletion *new_WordCompletion(void); /* * Delete a redundant completion object. */ WordCompletion *del_WordCompletion(WordCompletion *cpl); /*....................................................................... * Callback functions declared and prototyped using the following macro * are called upon to return an array of possible completion suffixes * for the token that precedes a specified location in the given * input line. It is up to this function to figure out where the token * starts, and to call cpl_add_completion() to register each possible * completion before returning. * * Input: * cpl WordCompletion * An opaque pointer to the object that will * contain the matches. This should be filled * via zero or more calls to cpl_add_completion(). * data void * The anonymous 'data' argument that was * passed to cpl_complete_word() or * gl_customize_completion()). * line const char * The current input line. * word_end int The index of the character in line[] which * follows the end of the token that is being * completed. * Output * return int 0 - OK. * 1 - Error. */ #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, void *data, \ const char *line, int word_end) typedef CPL_MATCH_FN(CplMatchFn); /*....................................................................... * Optional callback functions declared and prototyped using the * following macro are called upon to return non-zero if a given * file, specified by its pathname, is to be included in a list of * completions. * * Input: * data void * The application specified pointer which * was specified when this callback function * was registered. This can be used to have * anything you like passed to your callback. * pathname const char * The pathname of the file to be checked to * see if it should be included in the list * of completions. * Output * return int 0 - Ignore this file. * 1 - Do include this file in the list * of completions. */ #define CPL_CHECK_FN(fn) int (fn)(void *data, const char *pathname) typedef CPL_CHECK_FN(CplCheckFn); /* * You can use the following CplCheckFn callback function to only * have executables included in a list of completions. */ CPL_CHECK_FN(cpl_check_exe); /* * cpl_file_completions() is the builtin filename completion callback * function. This can also be called by your own custom CPL_MATCH_FN() * callback functions. To do this pass on all of the arguments of your * custom callback function to cpl_file_completions(), with the exception * of the (void *data) argument. The data argument should either be passed * NULL to request the default behaviour of the file-completion function, * or be passed a pointer to a CplFileConf structure (see below). In the * latter case the contents of the structure modify the behavior of the * file-completer. */ CPL_MATCH_FN(cpl_file_completions); /* * Objects of the following type can be used to change the default * behavior of the cpl_file_completions() callback function. */ typedef struct CplFileConf CplFileConf; /* * If you want to change the behavior of the cpl_file_completions() * callback function, call the following function to allocate a * configuration object, then call one or more of the subsequent * functions to change any of the default configuration parameters * that you don't want. This function returns NULL when there is * insufficient memory. */ CplFileConf *new_CplFileConf(void); /* * If backslashes in the prefix being passed to cpl_file_completions() * should be treated as literal characters, call the following function * with literal=1. Otherwise the default is to treat them as escape * characters which remove the special meanings of spaces etc.. */ void cfc_literal_escapes(CplFileConf *cfc, int literal); /* * Before calling cpl_file_completions(), call this function if you * know the index at which the filename prefix starts in the input line. * Otherwise by default, or if you specify start_index to be -1, the * filename is taken to start after the first unescaped space preceding * the cursor, or the start of the line, which ever comes first. */ void cfc_file_start(CplFileConf *cfc, int start_index); /* * If you only want certain types of files to be included in the * list of completions, use the following function to specify a * callback function which will be called to ask whether a given file * should be included. The chk_data argument is will be passed to the * callback function whenever it is called and can be anything you want. */ void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data); /* * The following function deletes a CplFileConf objects previously * returned by new_CplFileConf(). It always returns NULL. */ CplFileConf *del_CplFileConf(CplFileConf *cfc); /* * The following configuration structure is deprecated. Do not change * its contents, since this will break any programs that still use it, * and don't use it in new programs. Instead use opaque CplFileConf * objects as described above. cpl_file_completions() figures out * what type of structure you pass it, by virtue of a magic int code * placed at the start of CplFileConf object by new_CplFileConf(). */ typedef struct { int escaped; /* Opposite to the argument of cfc_literal_escapes() */ int file_start; /* Equivalent to the argument of cfc_file_start() */ } CplFileArgs; /* * This initializes the deprecated CplFileArgs structures. */ void cpl_init_FileArgs(CplFileArgs *cfa); /*....................................................................... * When an error occurs while performing a completion, custom completion * callback functions should register a terse description of the error * by calling cpl_record_error(). This message will then be returned on * the next call to cpl_last_error() and used by getline to display an * error message to the user. * * Input: * cpl WordCompletion * The string-completion resource object that was * originally passed to the callback. * errmsg const char * The description of the error. */ void cpl_record_error(WordCompletion *cpl, const char *errmsg); /*....................................................................... * This function can be used to replace the builtin filename-completion * function with one of the user's choice. The user's completion function * has the option of calling the builtin filename-completion function * if it believes that the token that it has been presented with is a * filename (see cpl_file_completions() above). * * Input: * gl GetLine * The resource object of the command-line input * module. * data void * This is passed to match_fn() whenever it is * called. It could, for example, point to a * symbol table that match_fn() would look up * matches in. * match_fn CplMatchFn * The function that will identify the prefix * to be completed from the input line, and * report matching symbols. * Output: * return int 0 - OK. * 1 - Error. */ int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn); /*....................................................................... * This function allows you to install alternate completion action * functions or completion listing functions, or to change the * completion function of an existing action of the same type. This * should preferably be called before the first call to gl_get_line() * so that the name of the action becomes defined before the user's * configuration file is read. * * Input: * gl GetLine * The resource object of the command-line input * module. * data void * This is passed to match_fn() whenever it is * called. It could, for example, point to a * symbol table that match_fn() would look up * matches in. * match_fn CplMatchFn * The function that will identify the prefix * to be completed from the input line, and * report matching symbols. * list_only int If non-zero, install an action that only lists * possible completions, rather than attempting * to perform the completion. * name const char * The name with which users can refer to the * binding in tecla configuration files. * keyseq const char * The key sequence with which to invoke * the binding. This should be specified in the * same manner as key-sequences in tecla * configuration files (eg. "M-^I"). * Output: * return int 0 - OK. * 1 - Error. */ int gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn, int list_only, const char *name, const char *keyseq); /*....................................................................... * Change the terminal (or stream) that getline interacts with. * * Input: * gl GetLine * The resource object of the command-line input * module. * input_fp FILE * The stdio stream to read from. * output_fp FILE * The stdio stream to write to. * term const char * The terminal type. This can be NULL if * either or both of input_fp and output_fp don't * refer to a terminal. Otherwise it should refer * to an entry in the terminal information database. * Output: * return int 0 - OK. * 1 - Error. */ int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, const char *term); /*....................................................................... * The following functions can be used to save and restore the contents * of the history buffer. * * Input: * gl GetLine * The resource object of the command-line input * module. * filename const char * The name of the new file to write to. * comment const char * Extra information such as timestamps will * be recorded on a line started with this * string, the idea being that the file can * double as a command file. Specify "" if * you don't care. Be sure to specify the * same string to both functions. * max_lines int The maximum number of lines to save, or -1 * to save all of the lines in the history * list. * Output: * return int 0 - OK. * 1 - Error. */ int gl_save_history(GetLine *gl, const char *filename, const char *comment, int max_lines); int gl_load_history(GetLine *gl, const char *filename, const char *comment); /* * Enumerate file-descriptor events that can be waited for. */ typedef enum { GLFD_READ, /* Watch for data waiting to be read from a file descriptor */ GLFD_WRITE, /* Watch for ability to write to a file descriptor */ GLFD_URGENT /* Watch for urgent out-of-band data on the file descriptor */ } GlFdEvent; /* * The following enumeration is used for the return status of file * descriptor event callbacks. */ typedef enum { GLFD_ABORT, /* Cause gl_get_line() to abort with an error */ GLFD_REFRESH, /* Redraw the input line and continue waiting for input */ GLFD_CONTINUE /* Continue to wait for input, without redrawing the line */ } GlFdStatus; /*....................................................................... * On systems that have the select() system call, while gl_get_line() * is waiting for terminal input, it can also be asked to listen for * activity on arbitrary file descriptors. Callback functions of the * following type can be registered to be called when activity is * seen. If your callback needs to write to the terminal or use * signals, please see the gl_get_line(3) man page. * * Input: * gl GetLine * The gl_get_line() resource object. You can use * this safely to call gl_watch_fd() or * gl_inactivity_timeout(). The effect of calling other * functions that take a gl argument is undefined, * and must be avoided. * data void * A pointer to arbitrary callback data, as originally * registered with gl_watch_fd(). * fd int The file descriptor that has activity. * event GlFdEvent The activity seen on the file descriptor. The * inclusion of this argument allows the same * callback to be registered for multiple events. * Output: * return GlFdStatus GLFD_ABORT - Cause gl_get_line() to abort with * an error (set errno if you need it). * GLFD_REFRESH - Redraw the input line and continue * waiting for input. Use this if you * wrote something to the terminal. * GLFD_CONTINUE - Continue to wait for input, without * redrawing the line. */ #define GL_FD_EVENT_FN(fn) GlFdStatus (fn)(GetLine *gl, void *data, int fd, \ GlFdEvent event) typedef GL_FD_EVENT_FN(GlFdEventFn); /*....................................................................... * Where possible, register a function and associated data to be called * whenever a specified event is seen on a file descriptor. * * Input: * gl GetLine * The resource object of the command-line input * module. * fd int The file descriptor to watch. * event GlFdEvent The type of activity to watch for. * callback GlFdEventFn * The function to call when the specified * event occurs. Setting this to 0 removes * any existing callback. * data void * A pointer to arbitrary data to pass to the * callback function. * Output: * return int 0 - OK. * 1 - Either gl==NULL, or this facility isn't * available on the the host system * (ie. select() isn't available). No * error message is generated in the latter * case. */ int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, GlFdEventFn *callback, void *data); /* * Enumerators from the following list are returned by activity * timeout callbacks registered by gl_inactivity_timeout(). They tell * gl_get_line() whether and how to procede. */ typedef enum { GLTO_ABORT, /* Cause gl_get_line() to abort with an error */ GLTO_REFRESH, /* Redraw the input line and continue waiting for input */ GLTO_CONTINUE /* Continue to wait for input, without redrawing the line */ } GlAfterTimeout; /*....................................................................... * On systems that have the select() system call, the application has * the option of providing a callback function of the following type, * which is called whenever no terminal input or other I/O activity is * seen for the timeout duration specified in the last call to * gl_inactivity_timeout(). * * Input: * gl GetLine * The gl_get_line() resource object. You can use * this safely to call gl_watch_fd() or * gl_inactivity_timeout(). The effect of calling other * functions that take a gl argument is undefined, * and must be avoided. * data void * A pointer to arbitrary callback data, as * originally registered with gl_inactivity_timeout(). * Output: * return GlAfterTimeout GLTO_ABORT - Cause gl_get_line() to * abort with an error (set * errno if you need it). * GLTO_REFRESH - Redraw the input line and * continue waiting for * input. Use this if you * wrote something to the * terminal. * GLTO_CONTINUE - Continue to wait for * input, without redrawing * the line. */ #define GL_TIMEOUT_FN(fn) GlAfterTimeout (fn)(GetLine *gl, void *data) typedef GL_TIMEOUT_FN(GlTimeoutFn); /*....................................................................... * On systems with the select() system call, the gl_inactivity_timeout() * function provides the option of setting (or cancelling) an * inactivity timeout. Inactivity, in this case, refers both to * terminal input received from the user, and to I/O on any file * descriptors registered by calls to gl_watch_fd(). If at any time, * no activity is seen for the requested time period, the specified * timeout callback function is called. On returning, this callback * returns a code which tells gl_get_line() what to do next. Note that * each call to gl_inactivity_timeout() replaces any previously installed * timeout callback, and that specifying a callback of 0, turns off * inactivity timing. * * Beware that although the timeout argument includes a nano-second * component, few computer clocks presently have resolutions finer * than a few milliseconds, so asking for less than a few milliseconds * is equivalent to zero on a lot of systems. * * Input: * gl GetLine * The resource object of the command-line input * module. * callback GlTimeoutFn * The function to call when the inactivity * timeout is exceeded. To turn off * inactivity timeouts altogether, send 0. * data void * A pointer to arbitrary data to pass to the * callback function. * sec unsigned long The number of whole seconds in the timeout. * nsec unsigned long The fractional number of seconds in the * timeout, expressed in nano-seconds (see * the caveat above). * Output: * return int 0 - OK. * 1 - Either gl==NULL, or this facility isn't * available on the the host system * (ie. select() isn't available). No * error message is generated in the latter * case. */ int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *timeout_fn, void *data, unsigned long sec, unsigned long nsec); /*....................................................................... * Switch history streams. History streams represent separate history * lists recorded within a single history buffer. Different streams * are distinguished by integer identifiers chosen by the calling * appplicaton. Initially new_GetLine() sets the stream identifier to * 0. Whenever a new line is appended to the history list, the current * stream identifier is recorded with it, and history lookups only * consider lines marked with the current stream identifier. * * Input: * gl GetLine * The resource object of gl_get_line(). * id unsigned The new history stream identifier. * Output: * return int 0 - OK. * 1 - Error. */ int gl_group_history(GetLine *gl, unsigned id); /*....................................................................... * Display the contents of the history list. * * Input: * gl GetLine * The resource object of gl_get_line(). * fp FILE * The stdio output stream to write to. * fmt const char * A format string. This containing characters to be * written verbatim, plus any of the following * format directives: * %D - The date, formatted like 2001-11-20 * %T - The time of day, formatted like 23:59:59 * %N - The sequential entry number of the * line in the history buffer. * %G - The number of the history group that * the line belongs to. * %% - A literal % character. * %H - The history line itself. * Note that a '\n' newline character is not * appended by default. * all_groups int If true, display history lines from all * history groups. Otherwise only display * those of the current history group. * max_lines int If max_lines is < 0, all available lines * are displayed. Otherwise only the most * recent max_lines lines will be displayed. * Output: * return int 0 - OK. * 1 - Error. */ int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, int max_lines); /*....................................................................... * Resize or delete the history buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * bufsize size_t The number of bytes in the history buffer, or 0 * to delete the buffer completely. * Output: * return int 0 - OK. * 1 - Insufficient memory (the previous buffer * will have been retained). No error message * will be displayed. */ int gl_resize_history(GetLine *gl, size_t bufsize); /*....................................................................... * Set an upper limit to the number of lines that can be recorded in the * history list, or remove a previously specified limit. * * Input: * gl GetLine * The resource object of gl_get_line(). * max_lines int The maximum number of lines to allow, or -1 to * cancel a previous limit and allow as many lines * as will fit in the current history buffer size. */ void gl_limit_history(GetLine *gl, int max_lines); /*....................................................................... * Discard either all historical lines, or just those associated with the * current history group. * * Input: * gl GetLine * The resource object of gl_get_line(). * all_groups int If true, clear all of the history. If false, * clear only the stored lines associated with the * currently selected history group. */ void gl_clear_history(GetLine *gl, int all_groups); /*....................................................................... * Temporarily enable or disable the gl_get_line() history mechanism. * * Input: * gl GetLine * The resource object of gl_get_line(). * enable int If true, turn on the history mechanism. If * false, disable it. */ void gl_toggle_history(GetLine *gl, int enable); /* * Objects of the following type are returned by gl_terminal_size(). */ typedef struct { int nline; /* The terminal has nline lines */ int ncolumn; /* The terminal has ncolumn columns */ } GlTerminalSize; /*....................................................................... * Update if necessary, and return the current size of the terminal. * * Input: * gl GetLine * The resource object of gl_get_line(). * def_ncolumn int If the number of columns in the terminal * can't be determined, substitute this number. * def_nline int If the number of lines in the terminal can't * be determined, substitute this number. * Output: * return GlTerminalSize The current terminal size. */ GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline); /*....................................................................... * Tell gl_get_line() the current terminal size. Note that this is only * necessary on systems where changes in terminal size aren't reported * via SIGWINCH. * * Input: * gl GetLine * The resource object of gl_get_line(). * ncolumn int The number of columns in the terminal. * nline int The number of rows in the terminal. * Output: * return int 0 - OK. * 1 - Error. */ int gl_set_term_size(GetLine *gl, int ncolumn, int nline); /* * The gl_lookup_history() function returns information in an * argument of the following type. */ typedef struct { const char *line; /* The requested history line */ unsigned group; /* The history group to which the */ /* line belongs. */ time_t timestamp; /* The date and time at which the */ /* line was originally entered. */ } GlHistoryLine; /*....................................................................... * Lookup a history line by its sequential number of entry in the * history buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * id unsigned long The identification number of the line to * be returned, where 0 denotes the first line * that was entered in the history list, and * each subsequently added line has a number * one greater than the previous one. For * the range of lines currently in the list, * see the gl_range_of_history() function. * Input/Output: * line GlHistoryLine * A pointer to the variable in which to * return the details of the line. * Output: * return int 0 - The line is no longer in the history * list, and *line has not been changed. * 1 - The requested line can be found in * *line. Note that the string in * line->line is part of the history * buffer and will change, so a private * copy should be made if you wish to * use it after subsequent calls to any * functions that take gl as an argument. */ int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *line); /* * The gl_state_of_history() function returns information in an argument * of the following type. */ typedef struct { int enabled; /* True if history is enabled */ unsigned group; /* The current history group */ int max_lines; /* The current upper limit on the number of lines */ /* in the history list, or -1 if unlimited. */ } GlHistoryState; /*....................................................................... * Query the state of the history list. Note that any of the input/output * pointers can be specified as NULL. * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * state GlHistoryState * A pointer to the variable in which to record * the return values. */ void gl_state_of_history(GetLine *gl, GlHistoryState *state); /* * The gl_range_of_history() function returns information in an argument * of the following type. */ typedef struct { unsigned long oldest; /* The sequential entry number of the oldest */ /* line in the history list. */ unsigned long newest; /* The sequential entry number of the newest */ /* line in the history list. */ int nlines; /* The number of lines in the history list */ } GlHistoryRange; /*....................................................................... * Query the number and range of lines in the history buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * range GlHistoryRange * A pointer to the variable in which to record * the return values. If range->nline=0, the * range of lines will be given as 0-0. */ void gl_range_of_history(GetLine *gl, GlHistoryRange *range); /* * The gl_size_of_history() function returns information in an argument * of the following type. */ typedef struct { size_t size; /* The size of the history buffer (bytes) */ size_t used; /* The number of bytes of the history buffer */ /* that are currently occupied. */ } GlHistorySize; /*....................................................................... * Return the size of the history buffer and the amount of the * buffer that is currently in use. * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * GlHistorySize size * A pointer to the variable in which to return * the results. */ void gl_size_of_history(GetLine *gl, GlHistorySize *size); /*....................................................................... * Enable or disable the automatic addition of newly entered lines to the * history list. * * Input: * gl GetLine * The resource object of gl_get_line(). * enable int If true, subsequently entered lines will * automatically be added to the history list * before they are returned to the caller of * gl_get_line(). If 0, the choice of how and * when to archive lines in the history list, * is left up to the calling application, which * can do so via calls to gl_append_history(). * Output: * return int 0 - OK. * 1 - Error. */ int gl_automatic_history(GetLine *gl, int enable); /*....................................................................... * Append a specified line to the history list. * * Input: * gl GetLine * The resource object of gl_get_line(). * line const char * The line to be added. * Output: * return int 0 - OK. * 1 - Error. */ int gl_append_history(GetLine *gl, const char *line); /*....................................................................... * Specify whether text that users type should be displayed or hidden. * In the latter case, only the prompt is displayed, and the final * input line is not archived in the history list. * * Input: * gl GetLine * The input-line history maintenance object. * enable int 0 - Disable echoing. * 1 - Enable echoing. * -1 - Just query the mode without changing it. * Output: * return int The echoing disposition that was in effect * before this function was called: * 0 - Echoing was disabled. * 1 - Echoing was enabled. */ int gl_echo_mode(GetLine *gl, int enable); /*....................................................................... * This function can be called from gl_get_line() callbacks to have * the prompt changed when they return. It has no effect if gl_get_line() * is not currently being invoked. * * Input: * gl GetLine * The resource object of gl_get_line(). * prompt const char * The new prompt. */ void gl_replace_prompt(GetLine *gl, const char *prompt); /* * Enumerate the available prompt formatting styles. */ typedef enum { GL_LITERAL_PROMPT, /* Display the prompt string literally */ GL_FORMAT_PROMPT /* The prompt string can contain any of the */ /* following formatting directives: */ /* %B - Display subsequent characters */ /* with a bold font. */ /* %b - Stop displaying characters */ /* with the bold font. */ /* %U - Underline subsequent characters. */ /* %u - Stop underlining characters. */ /* %S - Highlight subsequent characters */ /* (also known as standout mode). */ /* %s - Stop highlighting characters */ /* %% - Display a single % character. */ } GlPromptStyle; /*....................................................................... * Specify whether to heed text attribute directives within prompt * strings. * * Input: * gl GetLine * The resource object of gl_get_line(). * style GlPromptStyle The style of prompt (see the definition of * GlPromptStyle in libtecla.h for details). */ void gl_prompt_style(GetLine *gl, GlPromptStyle style); /*....................................................................... * Remove a signal from the list of signals that gl_get_line() traps. * * Input: * gl GetLine * The resource object of gl_get_line(). * signo int The number of the signal to be ignored. * Output: * return int 0 - OK. * 1 - Error. */ int gl_ignore_signal(GetLine *gl, int signo); /* * A bitwise union of the following enumerators is passed to * gl_trap_signal() to specify the environment in which the * application's signal handler is to be called. */ typedef enum { GLS_RESTORE_SIG=1, /* Restore the caller's signal environment */ /* while handling the signal. */ GLS_RESTORE_TTY=2, /* Restore the caller's terminal settings */ /* while handling the signal. */ GLS_RESTORE_LINE=4, /* Move the cursor to the start of the next line */ GLS_REDRAW_LINE=8, /* Redraw the input line when the signal handler */ /* returns. */ GLS_UNBLOCK_SIG=16, /* Normally a signal who's delivery is found to */ /* be blocked by the calling application is not */ /* trapped by gl_get_line(). Including this flag */ /* causes it to be temporarily unblocked and */ /* trapped while gl_get_line() is executing. */ GLS_DONT_FORWARD=32,/* Don't forward the signal to the signal handler */ /* of the calling program. */ GLS_RESTORE_ENV = GLS_RESTORE_SIG | GLS_RESTORE_TTY | GLS_REDRAW_LINE, GLS_SUSPEND_INPUT = GLS_RESTORE_ENV | GLS_RESTORE_LINE } GlSignalFlags; /* * The following enumerators are passed to gl_trap_signal() to tell * it what to do after the application's signal handler has been called. */ typedef enum { GLS_RETURN, /* Return the line as though the user had pressed the */ /* return key. */ GLS_ABORT, /* Cause gl_get_line() to return NULL */ GLS_CONTINUE /* After handling the signal, resume command line editing */ } GlAfterSignal; /*....................................................................... * Tell gl_get_line() how to respond to a given signal. This can be used * both to override the default responses to signals that gl_get_line() * normally catches and to add new signals to the list that are to be * caught. * * Input: * gl GetLine * The resource object of gl_get_line(). * signo int The number of the signal to be caught. * flags unsigned A bitwise union of GlSignalFlags enumerators. * after GlAfterSignal What to do after the application's signal * handler has been called. * errno_value int The value to set errno to. * Output: * return int 0 - OK. * 1 - Insufficient memory to record the * new signal disposition. */ int gl_trap_signal(GetLine *gl, int signo, unsigned flags, GlAfterSignal after, int errno_value); /*....................................................................... * By default, gl_get_line() doesn't trap signals that are blocked * when it is called. This default can be changed either on a * per-signal basis by calling gl_trap_signal(), or on a global basis * by calling this function. What this function does is add the * GLS_UNBLOCK_SIG flag to all signals that are currently configured * to be trapped by gl_get_line(), such that when subsequent calls to * gl_get_line() wait for I/O, these signals are temporarily * unblocked. This behavior is useful in non-blocking server-I/O mode, * where it is used to avoid race conditions related to handling these * signals externally to gl_get_line(). See the demonstration code in * demo3.c, or the gl_handle_signal() man page for further * information. * * Input: * gl GetLine * The resource object of gl_get_line(). */ void gl_catch_blocked(GetLine *gl); /*....................................................................... * In server-I/O mode the terminal is left in raw mode between calls * to gl_get_line(), so it is necessary for the application to install * terminal restoring signal handlers for signals that could terminate * or suspend the process, plus a terminal reconfiguration handler to * be called when a process resumption signal is received, and finally * a handler to be called when a terminal-resize signal is received. * * Since there are many signals that by default terminate or suspend * processes, and different systems support different sub-sets of * these signals, this function provides a convenient wrapper around * sigaction() for assigning the specified handlers to all appropriate * signals. It also arranges that when any one of these signals is * being handled, all other catchable signals are blocked. This is * necessary so that the specified signal handlers can safely call * gl_raw_io(), gl_normal_io() and gl_update_size() without reentrancy * issues. * * Input: * term_handler void (*)(int) The signal handler to invoke when * a process terminating signal is * received. * susp_handler void (*)(int) The signal handler to invoke when * a process suspending signal is * received. * cont_handler void (*)(int) The signal handler to invoke when * a process resumption signal is * received (ie. SIGCONT). * size_handler void (*)(int) The signal handler to invoke when * a terminal-resize signal (ie. SIGWINCH) * is received. * Output: * return int 0 - OK. * 1 - Error. */ int gl_tty_signals(void (*term_handler)(int), void (*susp_handler)(int), void (*cont_handler)(int), void (*size_handler)(int)); /*....................................................................... * Return the last signal that was caught by the most recent call to * gl_get_line(), or -1 if no signals were caught. This is useful if * gl_get_line() returns errno=EINTR and you need to find out what signal * caused it to abort. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int The last signal caught by the most recent * call to gl_get_line(), or -1 if no signals * were caught. */ int gl_last_signal(GetLine *gl); /*....................................................................... * Return the signal mask used by gl_get_line(). This is the set of * signals that gl_get_line() is currently configured to trap. * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * set sigset_t * The set of signals will be returned in *set, * in the form of a signal process mask, as * used by sigaction(), sigprocmask(), * sigpending(), sigsuspend(), sigsetjmp() and * other standard POSIX signal-aware * functions. * Output: * return int 0 - OK. * 1 - Error (examine errno for reason). */ int gl_list_signals(GetLine *gl, sigset_t *set); /*....................................................................... * Respond to signals who's default effects have important * consequences to gl_get_line(). This is intended for use in * non-blocking server mode, where the external event loop is * responsible for catching signals. Signals that are handled include * those that by default terminate or suspend the process, and the * signal that indicates that the terminal size has changed. Note that * this function is not signal safe and should thus not be called from * a signal handler itself. See the gl_io_mode() man page for how it * should be used. * * In the case of signals that by default terminate or suspend * processes, command-line editing will be suspended, the terminal * returned to a usable state, then the default disposition of the * signal restored and the signal resent, in order to suspend or * terminate the process. If the process subsequently resumes, * command-line editing is resumed. * * In the case of signals that indicate that the terminal has been * resized, the new size will be queried, and any input line that is * being edited will be redrawn to fit the new dimensions of the * terminal. * * Input: * signo int The number of the signal to respond to. * gl GetLine * The first element of an array of 'ngl' GetLine * objects. * ngl int The number of elements in the gl[] array. Normally * this will be one. */ void gl_handle_signal(int signo, GetLine *gl, int ngl); /*....................................................................... * Return extra information (ie. in addition to that provided by errno) * about the last error to occur in either gl_get_line() or its * associated public functions. * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * buff char * An optional output buffer. Note that if the * calling application calls any gl_*() * functions from signal handlers, it should * provide a buffer here, so that a copy of * the latest error message can safely be made * while signals are blocked. * n size_t The allocated size of buff[]. * Output: * return const char * A pointer to the error message. This will * be the buff argument, unless buff==NULL, in * which case it will be a pointer to an * internal error buffer. In the latter case, * note that the contents of the returned buffer * will change on subsequent calls to any gl_*() * functions. */ const char *gl_error_message(GetLine *gl, char *buff, size_t n); /*....................................................................... * Clear the terminal and leave the cursor at the home position. In * server I/O mode, arrange for the input line to be redrawn from scratch * when gl_get_line() is next called. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int 0 - OK. * 1 - Error. */ int gl_erase_terminal(GetLine *gl); /*....................................................................... * Display a left-justified string over multiple terminal lines, * taking account of the current width of the terminal. Optional * indentation and an optional prefix string can be specified to be * displayed at the start of each new terminal line used. Similarly, * an optional suffix can be specified to be displayed at the end of * each terminal line. If needed, a single paragraph can be broken * across multiple calls. Note that literal newlines in the input * string can be used to force a newline at any point and that you * should use this feature to explicitly end all paragraphs, including * at the end of the last string that you write. Note that when a new * line is started between two words that are separated by spaces, * those spaces are not output, whereas when a new line is started * because a newline character was found in the string, only the * spaces before the newline character are discarded. * * Input: * gl GetLine * The resource object of gl_get_line(). * indentation int The number of spaces of indentation to write * at the beginning of each new terminal line. * prefix const char * An optional prefix string to write after the * indentation margin at the start of each new * terminal line. You can specify NULL if no * prefix is required. * suffix const char * An optional suffix string to draw at the end * of the terminal line. Spaces will be added * where necessary to ensure that the suffix ends * in the last column of the terminal line. If * no suffix is desired, specify NULL. * fill_char int The padding character to use when indenting * the line or padding up to the suffix. * def_width int If the terminal width isn't known, such as when * writing to a pipe or redirecting to a file, * this number specifies what width to assume. * start int The number of characters already written to * the start of the current terminal line. This * is primarily used to allow individual * paragraphs to be written over multiple calls * to this function, but can also be used to * allow you to start the first line of a * paragraph with a different prefix or * indentation than those specified above. * string const char * The string to be written. * Output: * return int On error -1 is returned. Otherwise the * return value is the terminal column index at * which the cursor was left after writing the * final word in the string. Successful return * values can thus be passed verbatim to the * 'start' arguments of subsequent calls to * gl_display_text() to allow the printing of a * paragraph to be broken across multiple calls * to gl_display_text(). */ int gl_display_text(GetLine *gl, int indentation, const char *prefix, const char *suffix, int fill_char, int def_width, int start, const char *string); /* * Enumerate the I/O modes supported by gl_get_line(). */ typedef enum { GL_NORMAL_MODE, /* Normal line-at-a-time mode using gl_get_line()'s */ /* internal event loop. */ GL_SERVER_MODE /* Non-blocking server mode, driven by an external */ /* event loop. */ } GlIOMode; /*....................................................................... * Select the I/O mode to be used by gl_get_line(). * * Input: * gl GetLine * The resource object of gl_get_line(). * mode GlIOMode The I/O mode to establish. Note that * when server mode, the terminal is placed * in raw mode, as though gl_raw_io() had * been called. * Output: * return int 0 - OK. * 1 - Error. */ int gl_io_mode(GetLine *gl, GlIOMode mode); /*....................................................................... * In server mode, this function configures the terminal for non-blocking * raw terminal I/O. In normal I/O mode it does nothing. * * Callers of this function must be careful to trap all signals that * terminate or suspend the program, and call gl_normal_io() * from the corresponding signal handlers in order to restore the * terminal to its original settings before the program is terminated * or suspended. They should also trap the SIGCONT signal to detect * when the program resumes, and ensure that its signal handler * call gl_raw_io() to redisplay the line and resume editing. * * Input: * gl GetLine * The line editor resource object. * Output: * return int 0 - OK. * 1 - Error. */ int gl_raw_io(GetLine *gl); /*....................................................................... * Restore the terminal to the state that it had when gl_raw_io() was * last called. After calling gl_raw_io(), this function must be called * before terminating or suspending the program, and before attempting * other uses of the terminal from within the program. See gl_raw_io() * for more details. * * Input: * gl GetLine * The line editor resource object. * Output: * return int 0 - OK. * 1 - Error. */ int gl_normal_io(GetLine *gl); /*....................................................................... * When in non-blocking server mode, this function can be used to abandon * the current incompletely entered input line, and prepare to start * editing a new line on the next call to gl_get_line(). * * Input: * gl GetLine * The line editor resource object. * Output: * return int 0 - OK. * 1 - Error. */ void gl_abandon_line(GetLine *gl); /* * Enumerators of the following type are used to report why * gl_get_line() returned. This is most useful in non-blocking * server mode, since in that mode a NULL return value can mean * either that an error occurred, or that I/O blocked. */ typedef enum { GLR_NEWLINE, /* A new input line was returned */ GLR_BLOCKED, /* The terminal was in non-blocking mode, and input */ /* or output would have blocked. */ GLR_SIGNAL, /* A signal caused gl_get_line() to return. */ GLR_TIMEOUT, /* An application timeout callback returned GLTO_ABORT */ GLR_FDABORT, /* An application I/O callack returned GLFD_ABORT */ GLR_EOF, /* End of file reached */ GLR_ERROR /* An unexpected error caused gl_get_line() to abort */ } GlReturnStatus; /*....................................................................... * Ask gl_get_line() what caused it to return. * * Input: * gl GetLine * The line editor resource object. * Output: * return GlReturnStatus The return status of the last call to * gl_get_line(). */ GlReturnStatus gl_return_status(GetLine *gl); /* * Enumerate the types of I/O that gl_get_line() can be waiting for * in non-blocking sedrver I/O mode. */ typedef enum { GLP_READ, /* gl_get_line() is waiting to write to the terminal */ GLP_WRITE /* gl_get_line() is waiting to read from the terminal */ } GlPendingIO; /*....................................................................... * In non-blocking server-I/O mode, this function should be called * from the application's external event loop to see what type of * terminal I/O is being waited for by gl_get_line(), and thus what * direction of I/O to wait for with select() or poll(). * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return GlPendingIO The type of pending I/O being waited for. */ GlPendingIO gl_pending_io(GetLine *gl); /* * The following enumerators are returned by externally defined action * functions to tell gl_get_line() how to procede after the action * function returns. */ typedef enum { GLA_ABORT, /* Cause gl_get_line() to return NULL */ GLA_RETURN, /* Return the line as though the user had pressed the */ /* return key. */ GLA_CONTINUE /* Resume command-line editing */ } GlAfterAction; /*....................................................................... * Functions of the following form implement external * application-specific action functions, which can then be bound to * sequences of terminal keys. * * Input: * gl GetLine * The line editor resource object. * data void * The anonymous 'data' argument that was * passed to gl_external_action() when the * callback function was registered. * count int A positive repeat count specified by the user, * or 1 if not specified. Action functions should * ignore this if repeating the action multiple * times isn't appropriate. Alternatively they * can interpret it as a general numeric * argument. * curpos size_t The position of the cursor within the input * line, expressed as the index of the * corresponding character within the line[] * array. * line const char * A read-only copy of the current input line. * Output * return GlAfterAction What should gl_get_line() do when the action * function returns? * GLA_ABORT - Cause gl_get_line() to * abort with an error (set * errno if you need it). * GLA_RETURN - Return the input line as * though the user had typed * the return key. * GLA_CONTINUE - Resume waiting for keyboard * input. */ #define GL_ACTION_FN(fn) GlAfterAction (fn)(GetLine *gl, void *data, \ int count, size_t curpos, const char *line) typedef GL_ACTION_FN(GlActionFn); /*....................................................................... * Register an application-provided function as an action function. * This should preferably be called before the first call to gl_get_line() * so that the name of the action becomes defined before the user's * configuration file is read. * * Input: * gl GetLine * The resource object of the command-line input * module. * data void * Arbitrary application-specific callback * data to be passed to the callback * function, fn(). * fn GlActionFn * The application-specific function that * implements the action. This will be invoked * whenever the user presses any * key-sequence which is bound to this action. * name const char * The name with which users can refer to the * binding in tecla configuration files. * keyseq const char * The key sequence with which to invoke * the binding. This should be specified in the * same manner as key-sequences in tecla * configuration files (eg. "M-^I"). * Output: * return int 0 - OK. * 1 - Error. */ int gl_register_action(GetLine *gl, void *data, GlActionFn *fn, const char *name, const char *keyseq); /*....................................................................... * This function is designed to be called by CPL_MATCH_FN() callback * functions. It adds one possible completion of the token that is being * completed to an array of completions. If the completion needs any * special quoting to be valid when displayed in the input line, this * quoting must be included in the string. * * Input: * cpl WordCompletion * The argument of the same name that was passed * to the calling CPL_MATCH_FN() callback function. * line const char * The input line, as received by the callback * function. * word_start int The index within line[] of the start of the * word that is being completed. If an empty * string is being completed, set this to be * the same as word_end. * word_end int The index within line[] of the character which * follows the incomplete word, as received by the * callback function. * suffix const char * The appropriately quoted string that could * be appended to the incomplete token to complete * it. A copy of this string will be allocated * internally. * type_suffix const char * When listing multiple completions, gl_get_line() * appends this string to the completion to indicate * its type to the user. If not pertinent pass "". * Otherwise pass a literal or static string. * cont_suffix const char * If this turns out to be the only completion, * gl_get_line() will append this string as * a continuation. For example, the builtin * file-completion callback registers a directory * separator here for directory matches, and a * space otherwise. If the match were a function * name you might want to append an open * parenthesis, etc.. If not relevant pass "". * Otherwise pass a literal or static string. * Output: * return int 0 - OK. * 1 - Error. */ int cpl_add_completion(WordCompletion *cpl, const char *line, int word_start, int word_end, const char *suffix, const char *type_suffix, const char *cont_suffix); /* * Each possible completion string is recorded in an array element of * the following type. */ typedef struct { char *completion; /* The matching completion string */ char *suffix; /* The pointer into completion[] at which the */ /* string was extended. */ const char *type_suffix; /* A suffix to be added when listing completions */ /* to indicate the type of the completion. */ } CplMatch; /* * Completions are returned in a container of the following form. */ typedef struct { char *suffix; /* The common initial part of all of the */ /* completion suffixes. */ const char *cont_suffix; /* Optional continuation string to be appended to */ /* the sole completion when nmatch==1. */ CplMatch *matches; /* The array of possible completion strings, */ /* sorted into lexical order. */ int nmatch; /* The number of elements in matches[] */ } CplMatches; /*....................................................................... * Given an input line and the point at which completion is to be * attempted, return an array of possible completions. * * Input: * cpl WordCompletion * The word-completion resource object. * line const char * The current input line. * word_end int The index of the character in line[] which * follows the end of the token that is being * completed. * data void * Anonymous 'data' to be passed to match_fn(). * match_fn CplMatchFn * The function that will identify the prefix * to be completed from the input line, and * record completion suffixes. * Output: * return CplMatches * The container of the array of possible * completions. The returned pointer refers * to a container owned by the parent Completion * object, and its contents thus potentially * change on every call to cpl_complete_word(). */ CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, int word_end, void *data, CplMatchFn *match_fn); /*....................................................................... * Recall the return value of the last call to cpl_complete_word(). * * Input: * cpl WordCompletion * The completion resource object. * Output: * return CplMatches * The container of the array of possible * completions, as returned by the last call to * cpl_complete_word(). The returned pointer refers * to a container owned by the parent WordCompletion * object, and its contents thus potentially * change on every call to cpl_complete_word(). * On error, either in the execution of this * function, or in the last call to * cpl_complete_word(), NULL is returned, and a * description of the error can be acquired by * calling cpl_last_error(cpl). */ CplMatches *cpl_recall_matches(WordCompletion *cpl); /*....................................................................... * Print out an array of matching completions. * * Input: * result CplMatches * The container of the sorted array of * completions. * fp FILE * The output stream to write to. * term_width int The width of the terminal. * Output: * return int 0 - OK. * 1 - Error. */ int cpl_list_completions(CplMatches *result, FILE *fp, int term_width); /*....................................................................... * Return a description of the error that occurred on the last call to * cpl_complete_word() or cpl_add_completion(). * * Input: * cpl WordCompletion * The string-completion resource object. * Output: * return const char * The description of the last error. */ const char *cpl_last_error(WordCompletion *cpl); /* * PathCache objects encapsulate the resources needed to record * files of interest from comma-separated lists of directories. */ typedef struct PathCache PathCache; /*....................................................................... * Create an object who's function is to maintain a cache of filenames * found within a list of directories, and provide quick lookup and * completion of selected files in this cache. * * Output: * return PathCache * The new, initially empty cache, or NULL * on error. */ PathCache *new_PathCache(void); /*....................................................................... * Delete a given cache of files, returning the resources that it * was using to the system. * * Input: * pc PathCache * The cache to be deleted (can be NULL). * Output: * return PathCache * The deleted object (ie. allways NULL). */ PathCache *del_PathCache(PathCache *pc); /*....................................................................... * Return a description of the last path-caching error that occurred. * * Input: * pc PathCache * The filename cache that suffered the error. * Output: * return char * The description of the last error. */ const char *pca_last_error(PathCache *pc); /*....................................................................... * Build the list of files of interest contained in a given * colon-separated list of directories. * * Input: * pc PathCache * The cache in which to store the names of * the files that are found in the list of * directories. * path const char * A colon-separated list of directory * paths. Under UNIX, when searching for * executables, this should be the return * value of getenv("PATH"). * Output: * return int 0 - OK. * 1 - An error occurred. */ int pca_scan_path(PathCache *pc, const char *path); /*....................................................................... * If you want subsequent calls to pca_lookup_file() and * pca_path_completions() to only return the filenames of certain * types of files, for example executables, or filenames ending in * ".ps", call this function to register a file-selection callback * function. This callback function takes the full pathname of a file, * plus application-specific data, and returns 1 if the file is of * interest, and zero otherwise. * * Input: * pc PathCache * The filename cache. * check_fn CplCheckFn * The function to call to see if the name of * a given file should be included in the * cache. This determines what type of files * will reside in the cache. To revert to * selecting all files, regardless of type, * pass 0 here. * data void * You can pass a pointer to anything you * like here, including NULL. It will be * passed to your check_fn() callback * function, for its private use. */ void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, void *data); /*....................................................................... * Given the simple name of a file, search the cached list of files * in the order in which they where found in the list of directories * previously presented to pca_scan_path(), and return the pathname * of the first file which has this name. * * Input: * pc PathCache * The cached list of files. * name const char * The name of the file to lookup. * name_len int The length of the filename substring at the * beginning of name[], or -1 to assume that the * filename occupies the whole of the string. * literal int If this argument is zero, lone backslashes * in name[] are ignored during comparison * with filenames in the cache, under the * assumption that they were in the input line * soley to escape the special significance of * characters like spaces. To have them treated * as normal characters, give this argument a * non-zero value, such as 1. * Output: * return char * The pathname of the first matching file, * or NULL if not found. Note that the returned * pointer points to memory owned by *pc, and * will become invalid on the next call. */ char *pca_lookup_file(PathCache *pc, const char *name, int name_len, int literal); /* * Objects of the following type can be used to change the default * behavior of the pca_path_completions() callback function. */ typedef struct PcaPathConf PcaPathConf; /* * pca_path_completions() is a completion callback function for use directly * with cpl_complete_word() or gl_customize_completions(), or indirectly * from your own completion callback function. It requires that a PcaPathConf * object be passed via its 'void *data' argument (see below). */ CPL_MATCH_FN(pca_path_completions); /*....................................................................... * Allocate and initialize a pca_path_completions() configuration object. * * Input: * pc PathCache * The filename cache in which to look for * file name completions. * Output: * return PcaPathConf * The new configuration structure, or NULL * on error. */ PcaPathConf *new_PcaPathConf(PathCache *pc); /*....................................................................... * Deallocate memory, previously allocated by new_PcaPathConf(). * * Input: * ppc PcaPathConf * Any pointer previously returned by * new_PcaPathConf() [NULL is allowed]. * Output: * return PcaPathConf * The deleted structure (always NULL). */ PcaPathConf *del_PcaPathConf(PcaPathConf *ppc); /* * If backslashes in the prefix being passed to pca_path_completions() * should be treated as literal characters, call the following function * with literal=1. Otherwise the default is to treat them as escape * characters which remove the special meanings of spaces etc.. */ void ppc_literal_escapes(PcaPathConf *ppc, int literal); /* * Before calling pca_path_completions, call this function if you know * the index at which the filename prefix starts in the input line. * Otherwise by default, or if you specify start_index to be -1, the * filename is taken to start after the first unescaped space preceding * the cursor, or the start of the line, whichever comes first. */ void ppc_file_start(PcaPathConf *ppc, int start_index); #ifdef __cplusplus } #endif #endif yuma123_2.14/libtecla/direader.h0000664000175000017500000000357014770023131016637 0ustar vladimirvladimir#ifndef dirreader_h #define dirreader_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ typedef struct DirReader DirReader; DirReader *_new_DirReader(void); DirReader *_del_DirReader(DirReader *dr); int _dr_open_dir(DirReader *dr, const char *pathname, char **errmsg); char *_dr_next_file(DirReader *dr); void _dr_close_dir(DirReader *dr); #endif yuma123_2.14/libtecla/pcache.c0000664000175000017500000015217714770023131016306 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * If file-system access is to be excluded, this module has no function, * so all of its code should be excluded. */ #ifndef WITHOUT_FILE_SYSTEM #include #include #include #include #include "libtecla.h" #include "pathutil.h" #include "homedir.h" #include "freelist.h" #include "direader.h" #include "stringrp.h" #include "errmsg.h" /* * The new_PcaPathConf() constructor sets the integer first member of * the returned object to the following magic number. This is then * checked for by pca_path_completions() as a sanity check. */ #define PPC_ID_CODE 4567 /* * A pointer to a structure of the following type can be passed to * the builtin path-completion callback function to modify its behavior. */ struct PcaPathConf { int id; /* This is set to PPC_ID_CODE by new_PcaPathConf() */ PathCache *pc; /* The path-list cache in which to look up the executables */ int escaped; /* If non-zero, backslashes in the input line are */ /* interpreted as escaping special characters and */ /* spaces, and any special characters and spaces in */ /* the listed completions will also be escaped with */ /* added backslashes. This is the default behaviour. */ /* If zero, backslashes are interpreted as being */ /* literal parts of the file name, and none are added */ /* to the completion suffixes. */ int file_start; /* The index in the input line of the first character */ /* of the file name. If you specify -1 here, */ /* pca_path_completions() identifies the */ /* the start of the file by looking backwards for */ /* an unescaped space, or the beginning of the line. */ }; /* * Prepended to each chached filename is a character which contains * one of the following status codes. When a given filename (minus * this byte) is passed to the application's check_fn(), the result * is recorded in this byte, such that the next time it is looked * up, we don't have to call check_fn() again. These codes are cleared * whenever the path is scanned and whenever the check_fn() callback * is changed. */ typedef enum { PCA_F_ENIGMA='?', /* The file remains to be checked */ PCA_F_WANTED='+', /* The file has been selected by the caller's callback */ PCA_F_IGNORE='-' /* The file has been rejected by the caller's callback */ } PcaFileStatus; /* * Encapsulate the memory management objects which supply memoy for * the arrays of filenames. */ typedef struct { StringGroup *sg; /* The memory used to record the names of files */ size_t files_dim; /* The allocated size of files[] */ char **files; /* Memory for 'files_dim' pointers to files */ size_t nfiles; /* The number of filenames currently in files[] */ } CacheMem; static CacheMem *new_CacheMem(void); static CacheMem *del_CacheMem(CacheMem *cm); static void rst_CacheMem(CacheMem *cm); /* * Lists of nodes of the following type are used to record the * names and contents of individual directories. */ typedef struct PathNode PathNode; struct PathNode { PathNode *next; /* The next directory in the path */ int relative; /* True if the directory is a relative pathname */ CacheMem *mem; /* The memory used to store dir[] and files[] */ char *dir; /* The directory pathname (stored in pc->sg) */ int nfile; /* The number of filenames stored in 'files' */ char **files; /* Files of interest in the current directory, */ /* or NULL if dir[] is a relative pathname */ /* who's contents can't be cached. This array */ /* and its contents are taken from pc->abs_mem */ /* or pc->rel_mem */ }; /* * Append a new node to the list of directories in the path. */ static int add_PathNode(PathCache *pc, const char *dirname); /* * Set the maximum length allowed for usernames. * names. */ #define USR_LEN 100 /* * PathCache objects encapsulate the resources needed to record * files of interest from comma-separated lists of directories. */ struct PathCache { ErrMsg *err; /* The error reporting buffer */ FreeList *node_mem; /* A free-list of PathNode objects */ CacheMem *abs_mem; /* Memory for the filenames of absolute paths */ CacheMem *rel_mem; /* Memory for the filenames of relative paths */ PathNode *head; /* The head of the list of directories in the */ /* path, or NULL if no path has been scanned yet. */ PathNode *tail; /* The tail of the list of directories in the */ /* path, or NULL if no path has been scanned yet. */ PathName *path; /* The fully qualified name of a file */ HomeDir *home; /* Home-directory lookup object */ DirReader *dr; /* A portable directory reader */ CplFileConf *cfc; /* Configuration parameters to pass to */ /* cpl_file_completions() */ CplCheckFn *check_fn; /* The callback used to determine if a given */ /* filename should be recorded in the cache. */ void *data; /* Annonymous data to be passed to pc->check_fn() */ char usrnam[USR_LEN+1];/* The buffer used when reading the names of */ /* users. */ }; /* * Empty the cache. */ static void pca_clear_cache(PathCache *pc); /* * Read a username from string[] and record it in pc->usrnam[]. */ static int pca_read_username(PathCache *pc, const char *string, int slen, int literal, const char **nextp); /* * Extract the next component of a colon separated list of directory * paths. */ static int pca_extract_dir(PathCache *pc, const char *path, const char **nextp); /* * Scan absolute directories for files of interest, recording their names * in mem->sg and recording pointers to these names in mem->files[]. */ static int pca_scan_dir(PathCache *pc, const char *dirname, CacheMem *mem); /* * A qsort() comparison function for comparing the cached filename * strings pointed to by two (char **) array elements. Note that * this ignores the initial cache-status byte of each filename. */ static int pca_cmp_matches(const void *v1, const void *v2); /* * A qsort() comparison function for comparing a filename * against an element of an array of pointers to filename cache * entries. */ static int pca_cmp_file(const void *v1, const void *v2); /* * Initialize a PcaPathConf configuration objects with the default * options. */ static int pca_init_PcaPathConf(PcaPathConf *ppc, PathCache *pc); /* * Make a copy of a completion suffix, suitable for passing to * cpl_add_completion(). */ static int pca_prepare_suffix(PathCache *pc, const char *suffix, int add_escapes); /* * Return non-zero if the specified string appears to start with a pathname. */ static int cpa_cmd_contains_path(const char *prefix, int prefix_len); /* * Return a given prefix with escapes optionally removed. */ static const char *pca_prepare_prefix(PathCache *pc, const char *prefix, size_t prefix_len, int escaped); /* * If there is a tilde expression at the beginning of the specified path, * place the corresponding home directory into pc->path. Otherwise * just clear pc->path. */ static int pca_expand_tilde(PathCache *pc, const char *path, int pathlen, int literal, const char **endp); /* * Clear the filename status codes that are recorded before each filename * in the cache. */ static void pca_remove_marks(PathCache *pc); /* * Specify how many PathNode's to allocate at a time. */ #define PATH_NODE_BLK 30 /* * Specify the amount by which the files[] arrays are to be extended * whenever they are found to be too small. */ #define FILES_BLK_FACT 256 /*....................................................................... * Create a new object who's function is to maintain a cache of * filenames found within a list of directories, and provide quick * lookup and completion of selected files in this cache. * * Output: * return PathCache * The new, initially empty cache, or NULL * on error. */ PathCache *new_PathCache(void) { PathCache *pc; /* The object to be returned */ /* * Allocate the container. */ pc = (PathCache *)malloc(sizeof(PathCache)); if(!pc) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_PathCache(). */ pc->err = NULL; pc->node_mem = NULL; pc->abs_mem = NULL; pc->rel_mem = NULL; pc->head = NULL; pc->tail = NULL; pc->path = NULL; pc->home = NULL; pc->dr = NULL; pc->cfc = NULL; pc->check_fn = 0; pc->data = NULL; pc->usrnam[0] = '\0'; /* * Allocate a place to record error messages. */ pc->err = _new_ErrMsg(); if(!pc->err) return del_PathCache(pc); /* * Allocate the freelist of directory list nodes. */ pc->node_mem = _new_FreeList(sizeof(PathNode), PATH_NODE_BLK); if(!pc->node_mem) return del_PathCache(pc); /* * Allocate memory for recording names of files in absolute paths. */ pc->abs_mem = new_CacheMem(); if(!pc->abs_mem) return del_PathCache(pc); /* * Allocate memory for recording names of files in relative paths. */ pc->rel_mem = new_CacheMem(); if(!pc->rel_mem) return del_PathCache(pc); /* * Allocate a pathname buffer. */ pc->path = _new_PathName(); if(!pc->path) return del_PathCache(pc); /* * Allocate an object for looking up home-directories. */ pc->home = _new_HomeDir(); if(!pc->home) return del_PathCache(pc); /* * Allocate an object for reading directories. */ pc->dr = _new_DirReader(); if(!pc->dr) return del_PathCache(pc); /* * Allocate a cpl_file_completions() configuration object. */ pc->cfc = new_CplFileConf(); if(!pc->cfc) return del_PathCache(pc); /* * Configure cpl_file_completions() to use check_fn() to select * files of interest. */ cfc_set_check_fn(pc->cfc, pc->check_fn, pc->data); /* * Return the cache, ready for use. */ return pc; } /*....................................................................... * Delete a given cache of files, returning the resources that it * was using to the system. * * Input: * pc PathCache * The cache to be deleted (can be NULL). * Output: * return PathCache * The deleted object (ie. allways NULL). */ PathCache *del_PathCache(PathCache *pc) { if(pc) { /* * Delete the error message buffer. */ pc->err = _del_ErrMsg(pc->err); /* * Delete the memory of the list of path nodes. */ pc->node_mem = _del_FreeList(pc->node_mem, 1); /* * Delete the memory used to record filenames. */ pc->abs_mem = del_CacheMem(pc->abs_mem); pc->rel_mem = del_CacheMem(pc->rel_mem); /* * The list of PathNode's was already deleted when node_mem was * deleted. */ pc->head = NULL; pc->tail = NULL; /* * Delete the pathname buffer. */ pc->path = _del_PathName(pc->path); /* * Delete the home-directory lookup object. */ pc->home = _del_HomeDir(pc->home); /* * Delete the directory reader. */ pc->dr = _del_DirReader(pc->dr); /* * Delete the cpl_file_completions() config object. */ pc->cfc = del_CplFileConf(pc->cfc); /* * Delete the container. */ free(pc); }; return NULL; } /*....................................................................... * If you want subsequent calls to pca_lookup_file() and * pca_path_completions() to only return the filenames of certain * types of files, for example executables, or filenames ending in * ".ps", call this function to register a file-selection callback * function. This callback function takes the full pathname of a file, * plus application-specific data, and returns 1 if the file is of * interest, and zero otherwise. * * Input: * pc PathCache * The filename cache. * check_fn CplCheckFn * The function to call to see if the name of * a given file should be included in the * cache. This determines what type of files * will reside in the cache. To revert to * selecting all files, regardless of type, * pass 0 here. * data void * You can pass a pointer to anything you * like here, including NULL. It will be * passed to your check_fn() callback * function, for its private use. */ void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, void *data) { if(pc) { /* * If the callback or its data pointer have changed, clear the cached * statuses of files that were accepted or rejected by the previous * calback. */ if(check_fn != pc->check_fn || data != pc->data) pca_remove_marks(pc); /* * Record the new callback locally. */ pc->check_fn = check_fn; pc->data = data; /* * Configure cpl_file_completions() to use the same callback to * select files of interest. */ cfc_set_check_fn(pc->cfc, check_fn, data); }; return; } /*....................................................................... * Return a description of the last path-caching error that occurred. * * Input: * pc PathCache * The filename cache that suffered the error. * Output: * return char * The description of the last error. */ const char *pca_last_error(PathCache *pc) { return pc ? _err_get_msg(pc->err) : "NULL PathCache argument"; } /*....................................................................... * Discard all cached filenames. * * Input: * pc PathCache * The cache to be cleared. */ static void pca_clear_cache(PathCache *pc) { if(pc) { /* * Return all path-nodes to the freelist. */ _rst_FreeList(pc->node_mem); pc->head = pc->tail = NULL; /* * Delete all filename strings. */ rst_CacheMem(pc->abs_mem); rst_CacheMem(pc->rel_mem); }; return; } /*....................................................................... * Build the list of files of interest contained in a given * colon-separated list of directories. * * Input: * pc PathCache * The cache in which to store the names of * the files that are found in the list of * directories. * path const char * A colon-separated list of directory * paths. Under UNIX, when searching for * executables, this should be the return * value of getenv("PATH"). * Output: * return int 0 - OK. * 1 - An error occurred. A description of * the error can be acquired by calling * pca_last_error(pc). */ int pca_scan_path(PathCache *pc, const char *path) { const char *pptr; /* A pointer to the next unprocessed character in path[] */ PathNode *node; /* A node in the list of directory paths */ char **fptr; /* A pointer into pc->abs_mem->files[] */ /* * Check the arguments. */ if(!pc) return 1; /* * Clear the outdated contents of the cache. */ pca_clear_cache(pc); /* * If no path list was provided, there is nothing to be added to the * cache. */ if(!path) return 0; /* * Extract directories from the path list, expanding tilde expressions * on the fly into pc->pathname, then add them to the list of path * nodes, along with a sorted list of the filenames of interest that * the directories hold. */ pptr = path; while(*pptr) { /* * Extract the next pathname component into pc->path->name. */ if(pca_extract_dir(pc, pptr, &pptr)) return 1; /* * Add a new node to the list of paths, containing both the * directory name and, if not a relative pathname, the list of * files of interest in the directory. */ if(add_PathNode(pc, pc->path->name)) return 1; }; /* * The file arrays in each absolute directory node are sections of * pc->abs_mem->files[]. Record pointers to the starts of each * of these sections in each directory node. Note that this couldn't * be done in add_PathNode(), because pc->abs_mem->files[] may * get reallocated in subsequent calls to add_PathNode(), thus * invalidating any pointers to it. */ fptr = pc->abs_mem->files; for(node=pc->head; node; node=node->next) { node->files = fptr; fptr += node->nfile; }; return 0; } /*....................................................................... * Extract the next directory path from a colon-separated list of * directories, expanding tilde home-directory expressions where needed. * * Input: * pc PathCache * The cache of filenames. * path const char * A pointer to the start of the next component * in the path list. * Input/Output: * nextp const char ** A pointer to the next unprocessed character * in path[] will be assigned to *nextp. * Output: * return int 0 - OK. The extracted path is in pc->path->name. * 1 - Error. A description of the error will * have been left in pc->err. */ static int pca_extract_dir(PathCache *pc, const char *path, const char **nextp) { const char *pptr; /* A pointer into path[] */ const char *sptr; /* The path following tilde expansion */ int escaped = 0; /* True if the last character was a backslash */ /* * If there is a tilde expression at the beginning of the specified path, * place the corresponding home directory into pc->path. Otherwise * just clear pc->path. */ if(pca_expand_tilde(pc, path, strlen(path), 0, &pptr)) return 1; /* * Keep a record of the current location in the path. */ sptr = pptr; /* * Locate the end of the directory name in the pathname string, stopping * when either the end of the string is reached, or an un-escaped colon * separator is seen. */ while(*pptr && (escaped || *pptr != ':')) escaped = !escaped && *pptr++ == '\\'; /* * Append the rest of the directory path to the pathname buffer. */ if(_pn_append_to_path(pc->path, sptr, pptr - sptr, 1) == NULL) { _err_record_msg(pc->err, "Insufficient memory to record directory name", END_ERR_MSG); return 1; }; /* * To facilitate subsequently appending filenames to the directory * path name, make sure that the recorded directory name ends in a * directory separator. */ { int dirlen = strlen(pc->path->name); if(dirlen < FS_DIR_SEP_LEN || strncmp(pc->path->name + dirlen - FS_DIR_SEP_LEN, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0) { if(_pn_append_to_path(pc->path, FS_DIR_SEP, FS_DIR_SEP_LEN, 0) == NULL) { _err_record_msg(pc->err, "Insufficient memory to record directory name", END_ERR_MSG); return 1; }; }; }; /* * Skip the separator unless we have reached the end of the path. */ if(*pptr==':') pptr++; /* * Return the unprocessed tail of the path-list string. */ *nextp = pptr; return 0; } /*....................................................................... * Read a username, stopping when a directory separator is seen, a colon * separator is seen, the end of the string is reached, or the username * buffer overflows. * * Input: * pc PathCache * The cache of filenames. * string char * The string who's prefix contains the name. * slen int The max number of characters to read from string[]. * literal int If true, treat backslashes as literal characters * instead of escapes. * Input/Output: * nextp char ** A pointer to the next unprocessed character * in string[] will be assigned to *nextp. * Output: * return int 0 - OK. The username can be found in pc->usrnam. * 1 - Error. A description of the error message * can be found in pc->err. */ static int pca_read_username(PathCache *pc, const char *string, int slen, int literal, const char **nextp) { int usrlen; /* The number of characters in pc->usrnam[] */ const char *sptr; /* A pointer into string[] */ int escaped = 0; /* True if the last character was a backslash */ /* * Extract the username. */ for(sptr=string,usrlen=0; usrlen < USR_LEN && (sptr-string) < slen; sptr++) { /* * Stop if the end of the string is reached, or a directory separator * or un-escaped colon separator is seen. */ if(!*sptr || strncmp(sptr, FS_DIR_SEP, FS_DIR_SEP_LEN)==0 || (!escaped && *sptr == ':')) break; /* * Escape the next character? */ if(!literal && !escaped && *sptr == '\\') { escaped = 1; } else { escaped = 0; pc->usrnam[usrlen++] = *sptr; }; }; /* * Did the username overflow the buffer? */ if(usrlen >= USR_LEN) { _err_record_msg(pc->err, "Username too long", END_ERR_MSG); return 1; }; /* * Terminate the string. */ pc->usrnam[usrlen] = '\0'; /* * Indicate where processing of the input string should continue. */ *nextp = sptr; return 0; } /*....................................................................... * Create a new CacheMem object. * * Output: * return CacheMem * The new object, or NULL on error. */ static CacheMem *new_CacheMem(void) { CacheMem *cm; /* The object to be returned */ /* * Allocate the container. */ cm = (CacheMem *)malloc(sizeof(CacheMem)); if(!cm) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_CacheMem(). */ cm->sg = NULL; cm->files_dim = 0; cm->files = NULL; cm->nfiles = 0; /* * Allocate a list of string segments for storing filenames. */ cm->sg = _new_StringGroup(_pu_pathname_dim()); if(!cm->sg) return del_CacheMem(cm); /* * Allocate an array of pointers to filenames. * This will be extended later if needed. */ cm->files_dim = FILES_BLK_FACT; cm->files = (char **) malloc(sizeof(*cm->files) * cm->files_dim); if(!cm->files) { errno = ENOMEM; return del_CacheMem(cm); }; return cm; } /*....................................................................... * Delete a CacheMem object. * * Input: * cm CacheMem * The object to be deleted. * Output: * return CacheMem * The deleted object (always NULL). */ static CacheMem *del_CacheMem(CacheMem *cm) { if(cm) { /* * Delete the memory that was used to record filename strings. */ cm->sg = _del_StringGroup(cm->sg); /* * Delete the array of pointers to filenames. */ cm->files_dim = 0; if(cm->files) { free(cm->files); cm->files = NULL; }; /* * Delete the container. */ free(cm); }; return NULL; } /*....................................................................... * Re-initialize the memory used to allocate filename strings. * * Input: * cm CacheMem * The memory cache to be cleared. */ static void rst_CacheMem(CacheMem *cm) { _clr_StringGroup(cm->sg); cm->nfiles = 0; return; } /*....................................................................... * Append a new directory node to the list of directories read from the * path. * * Input: * pc PathCache * The filename cache. * dirname const char * The name of the new directory. * Output: * return int 0 - OK. * 1 - Error. */ static int add_PathNode(PathCache *pc, const char *dirname) { PathNode *node; /* The new directory list node */ int relative; /* True if dirname[] is a relative pathname */ /* * Have we been passed a relative pathname or an absolute pathname? */ relative = strncmp(dirname, FS_ROOT_DIR, FS_ROOT_DIR_LEN) != 0; /* * If it's an absolute pathname, ignore it if the corresponding * directory doesn't exist. */ if(!relative && !_pu_path_is_dir(dirname)) return 0; /* * Allocate a new list node to record the specifics of the new directory. */ node = (PathNode *) _new_FreeListNode(pc->node_mem); if(!node) { _err_record_msg(pc->err, "Insufficient memory to cache new directory.", END_ERR_MSG); return 1; }; /* * Initialize the node. */ node->next = NULL; node->relative = relative; node->mem = relative ? pc->rel_mem : pc->abs_mem; node->dir = NULL; node->nfile = 0; node->files = NULL; /* * Make a copy of the directory pathname. */ node->dir = _sg_store_string(pc->abs_mem->sg, dirname, 0); if(!node->dir) { _err_record_msg(pc->err, "Insufficient memory to store directory name.", END_ERR_MSG); return 1; }; /* * Scan absolute directories for files of interest, recording their names * in node->mem->sg and appending pointers to these names to the * node->mem->files[] array. */ if(!node->relative) { int nfile = node->nfile = pca_scan_dir(pc, node->dir, node->mem); if(nfile < 1) { /* No files matched or an error occurred */ node = (PathNode *) _del_FreeListNode(pc->node_mem, node); return nfile < 0; }; }; /* * Append the new node to the list. */ if(pc->head) { pc->tail->next = node; pc->tail = node; } else { pc->head = pc->tail = node; }; return 0; } /*....................................................................... * Scan a given directory for files of interest, record their names * in mem->sg and append pointers to them to the mem->files[] array. * * Input: * pc PathCache * The filename cache. * dirname const char * The pathname of the directory to be scanned. * mem CacheMem * The memory in which to store filenames of * interest. * Output: * return int The number of files recorded, or -1 if a * memory error occurs. Note that the * inability to read the contents of the * directory is not counted as an error. */ static int pca_scan_dir(PathCache *pc, const char *dirname, CacheMem *mem) { int nfile = 0; /* The number of filenames recorded */ const char *filename; /* The name of the file being looked at */ /* * Attempt to open the directory. If the directory can't be read then * there are no accessible files of interest in the directory. */ if(_dr_open_dir(pc->dr, dirname, NULL)) return 0; /* * Record the names of all files in the directory in the cache. */ while((filename = _dr_next_file(pc->dr))) { char *copy; /* A copy of the filename */ /* * Make a temporary copy of the filename with an extra byte prepended. */ _pn_clear_path(pc->path); if(_pn_append_to_path(pc->path, " ", 1, 0) == NULL || _pn_append_to_path(pc->path, filename, -1, 1) == NULL) { _err_record_msg(pc->err, "Insufficient memory to record filename", END_ERR_MSG); return -1; }; /* * Store the filename. */ copy = _sg_store_string(mem->sg, pc->path->name, 0); if(!copy) { _err_record_msg(pc->err, "Insufficient memory to cache file name.", END_ERR_MSG); return -1; }; /* * Mark the filename as unchecked. */ copy[0] = PCA_F_ENIGMA; /* * Make room to store a pointer to the copy in mem->files[]. */ if(mem->nfiles + 1 > mem->files_dim) { int needed = mem->files_dim + FILES_BLK_FACT; char **files = (char **) realloc(mem->files, sizeof(*mem->files)*needed); if(!files) { _err_record_msg(pc->err, "Insufficient memory to extend filename cache.", END_ERR_MSG); return 1; }; mem->files = files; mem->files_dim = needed; }; /* * Record a pointer to the copy of the filename at the end of the files[] * array. */ mem->files[mem->nfiles++] = copy; /* * Keep a record of the number of files matched so far. */ nfile++; }; /* * Sort the list of files into lexical order. */ qsort(mem->files + mem->nfiles - nfile, nfile, sizeof(*mem->files), pca_cmp_matches); /* * Return the number of files recorded in mem->files[]. */ return nfile; } /*....................................................................... * A qsort() comparison function for comparing the cached filename * strings pointed to by two (char **) array elements. Note that * this ignores the initial cache-status byte of each filename. * * Input: * v1, v2 void * Pointers to the pointers of two strings to be compared. * Output: * return int -1 -> v1 < v2. * 0 -> v1 == v2 * 1 -> v1 > v2 */ static int pca_cmp_matches(const void *v1, const void *v2) { const char **s1 = (const char **) v1; const char **s2 = (const char **) v2; return strcmp(*s1+1, *s2+1); } /*....................................................................... * Given the simple name of a file, search the cached list of files * in the order in which they where found in the list of directories * previously presented to pca_scan_path(), and return the pathname * of the first file which has this name. If a pathname to a file is * given instead of a simple filename, this is returned without being * looked up in the cache, but with any initial ~username expression * expanded, and optionally, unescaped backslashes removed. * * Input: * pc PathCache * The cached list of files. * name const char * The name of the file to lookup. * name_len int The length of the filename string at the * beginning of name[], or -1 to indicate that * the filename occupies the whole of the * string. * literal int If this argument is zero, lone backslashes * in name[] are ignored during comparison * with filenames in the cache, under the * assumption that they were in the input line * soley to escape the special significance of * characters like spaces. To have them treated * as normal characters, give this argument a * non-zero value, such as 1. * Output: * return char * The pathname of the first matching file, * or NULL if not found. Note that the returned * pointer points to memory owned by *pc, and * will become invalid on the next call to any * function in the PathCache module. */ char *pca_lookup_file(PathCache *pc, const char *name, int name_len, int literal) { PathNode *node; /* A node in the list of directories in the path */ char **match; /* A pointer to a matching filename string in the cache */ /* * Check the arguments. */ if(!pc || !name || name_len==0) return NULL; /* * If no length was specified, determine the length of the string to * be looked up. */ if(name_len < 0) name_len = strlen(name); /* * If the word starts with a ~username expression, the root directory, * of it contains any directory separators, then treat it isn't a simple * filename that can be looked up in the cache, but rather appears to * be the pathname of a file. If so, return a copy of this pathname with * escapes removed, if requested, and any initial ~username expression * expanded. */ if(cpa_cmd_contains_path(name, name_len)) { const char *nptr; if(pca_expand_tilde(pc, name, name_len, literal, &nptr) || _pn_append_to_path(pc->path, nptr, name_len - (nptr-name), !literal) == NULL) return NULL; return pc->path->name; }; /* * Look up the specified filename in each of the directories of the path, * in the same order that they were listed in the path, and stop as soon * as an instance of the file is found. */ for(node=pc->head; node; node=node->next) { /* * If the directory of the latest node is a relative pathname, * scan it for files of interest. */ if(node->relative) { rst_CacheMem(node->mem); if(pca_scan_dir(pc, node->dir, node->mem) < 1) continue; node->files = node->mem->files; node->nfile = node->mem->nfiles; }; /* * Copy the filename into a temporary buffer, while interpretting * escape characters if needed. */ _pn_clear_path(pc->path); if(_pn_append_to_path(pc->path, name, name_len, !literal) == NULL) return NULL; /* * Perform a binary search for the requested filename. */ match = (char **)bsearch(pc->path->name, node->files, node->nfile, sizeof(*node->files), pca_cmp_file); if(match) { /* * Prepend the pathname in which the directory was found, which we have * guaranteed to end in a directory separator, to the located filename. */ if(_pn_prepend_to_path(pc->path, node->dir, -1, 0) == NULL) return NULL; /* * Return the matching pathname unless it is rejected by the application. */ if(!pc->check_fn || (*match)[0] == PCA_F_WANTED || ((*match)[0]==PCA_F_ENIGMA && pc->check_fn(pc->data, pc->path->name))){ (*match)[0] = PCA_F_WANTED; return pc->path->name; } else { *(match)[0] = PCA_F_IGNORE; }; }; }; /* * File not found. */ return NULL; } /*....................................................................... * A qsort() comparison function for comparing a filename string to * a cached filename string pointed to by a (char **) array element. * This ignores the initial code byte at the start of the cached filename * string. * * Input: * v1, v2 void * Pointers to the pointers of two strings to be compared. * Output: * return int -1 -> v1 < v2. * 0 -> v1 == v2 * 1 -> v1 > v2 */ static int pca_cmp_file(const void *v1, const void *v2) { const char *file_name = (const char *) v1; const char **cache_name = (const char **) v2; return strcmp(file_name, *cache_name + 1); } /*....................................................................... * The PcaPathConf structure may have options added to it in the future. * To allow your application to be linked against a shared version of the * tecla library, without these additions causing your application to * crash, you should use new_PcaPathConf() to allocate such structures. * This will set all of the configuration options to their default values, * which you can then change before passing the structure to * pca_path_completions(). * * Input: * pc PathCache * The filename cache in which to look for * file name completions. * Output: * return PcaPathConf * The new configuration structure, or NULL * on error. A descripition of the error * can be found by calling pca_last_error(pc). */ PcaPathConf *new_PcaPathConf(PathCache *pc) { PcaPathConf *ppc; /* The object to be returned */ /* * Check the arguments. */ if(!pc) return NULL; /* * Allocate the container. */ ppc = (PcaPathConf *)malloc(sizeof(PcaPathConf)); if(!ppc) { _err_record_msg(pc->err, "Insufficient memory.", END_ERR_MSG); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_PcaPathConf(). */ if(pca_init_PcaPathConf(ppc, pc)) return del_PcaPathConf(ppc); return ppc; } /*....................................................................... * Initialize a PcaPathConf configuration structure with defaults. * * Input: * ppc PcaPathConf * The structre to be initialized. * pc PathCache * The cache in which completions will be looked up. * Output: * return int 0 - OK. * 1 - Error. A description of the error can be * obtained by calling pca_last_error(pc). */ static int pca_init_PcaPathConf(PcaPathConf *ppc, PathCache *pc) { /* * Check the arguments. */ if(!pc) return 1; /* * Set the default options. */ ppc->id = PPC_ID_CODE; ppc->pc = pc; ppc->escaped = 1; ppc->file_start = -1; return 0; } /*....................................................................... * Delete a PcaPathConf object. * * Input: * ppc PcaPathConf * The object to be deleted. * Output: * return PcaPathConf * The deleted object (always NULL). */ PcaPathConf *del_PcaPathConf(PcaPathConf *ppc) { if(ppc) { ppc->pc = NULL; /* It is up to the caller to delete the cache */ /* * Delete the container. */ free(ppc); }; return NULL; } /*....................................................................... * pca_path_completions() is a completion callback function for use * directly with cpl_complete_word() or gl_customize_completions(), or * indirectly from your own completion callback function. It requires * that a CpaPathArgs object be passed via its 'void *data' argument. */ CPL_MATCH_FN(pca_path_completions) { PcaPathConf *ppc; /* The configuration arguments */ PathCache *pc; /* The cache in which to look for completions */ PathNode *node; /* A node in the list of directories in the path */ const char *filename; /* The name of the file being looked at */ const char *start_path; /* The pointer to the start of the pathname */ /* in line[]. */ int word_start; /* The index in line[] corresponding to start_path */ const char *prefix; /* The file-name prefix being searched for */ size_t prefix_len; /* The length of the prefix being completed */ int bot; /* The lowest index of the array not searched yet */ int top; /* The highest index of the array not searched yet */ /* * Check the arguments. */ if(!cpl) return 1; if(!line || word_end < 0 || !data) { cpl_record_error(cpl, "pca_path_completions: Invalid arguments."); return 1; }; /* * Get the configuration arguments. */ ppc = (PcaPathConf *) data; /* * Check that the callback data is a PcaPathConf structure returned * by new_PcaPathConf(). */ if(ppc->id != PPC_ID_CODE) { cpl_record_error(cpl, "Invalid callback data passed to pca_path_completions()"); return 1; }; /* * Get the filename cache. */ pc = ppc->pc; /* * Get the start of the file name. If not specified by the caller, * identify it by searching backwards in the input line for an * unescaped space or the start of the line. */ if(ppc->file_start < 0) { start_path = _pu_start_of_path(line, word_end); if(!start_path) { cpl_record_error(cpl, "Unable to find the start of the file name."); return 1; }; } else { start_path = line + ppc->file_start; }; /* * Get the index of the start of the word being completed. */ word_start = start_path - line; /* * Work out the length of the prefix that is bein completed. */ prefix_len = word_end - word_start; /* * If the word starts with a ~username expression or the root directory, * of it contains any directory separators, then completion must be * delegated to cpl_file_completions(). */ if(cpa_cmd_contains_path(start_path, prefix_len)) { cfc_file_start(pc->cfc, word_start); return cpl_file_completions(cpl, pc->cfc, line, word_end); }; /* * Look up the specified file name in each of the directories of the path, * in the same order that they were listed in the path, and stop as soon * as an instance of the file is found. */ for(node=pc->head; node; node=node->next) { /* * If the directory of the latest node is a relative pathname, * scan it for files of interest. */ if(node->relative) { rst_CacheMem(node->mem); if(pca_scan_dir(pc, node->dir, node->mem) < 1) continue; node->files = node->mem->files; node->nfile = node->mem->nfiles; }; /* * If needed, make a copy of the file-name being matched, with * escapes removed. Note that we need to do this anew every loop * iteration, because the above call to pca_scan_dir() uses * pc->path. */ prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); if(!prefix) return 1; /* * The directory entries are sorted, so we can perform a binary * search for an instance of the prefix being searched for. */ bot = 0; top = node->nfile - 1; while(top >= bot) { int mid = (top + bot)/2; int test = strncmp(node->files[mid]+1, prefix, prefix_len); if(test > 0) top = mid - 1; else if(test < 0) bot = mid + 1; else { top = bot = mid; break; }; }; /* * If we found a match, look to see if any of its neigbors also match. */ if(top == bot) { while(--bot >= 0 && strncmp(node->files[bot]+1, prefix, prefix_len) == 0) ; while(++top < node->nfile && strncmp(node->files[top]+1, prefix, prefix_len) == 0) ; /* * We will have gone one too far in each direction. */ bot++; top--; /* * Add the completions to the list after checking them against the * callers requirements. */ for( ; bot<=top; bot++) { char *match = node->files[bot]; /* * Form the full pathname of the file. */ _pn_clear_path(pc->path); if(_pn_append_to_path(pc->path, node->dir, -1, 0) == NULL || _pn_append_to_path(pc->path, match+1, -1, 0) == NULL) { _err_record_msg(pc->err, "Insufficient memory to complete file name", END_ERR_MSG); return 1; }; /* * Should the file be included in the list of completions? */ if(!pc->check_fn || match[0] == PCA_F_WANTED || (match[0]==PCA_F_ENIGMA && pc->check_fn(pc->data, pc->path->name))) { match[0] = PCA_F_WANTED; /* * Copy the completion suffix into the work pathname pc->path->name, * adding backslash escapes if needed. */ if(pca_prepare_suffix(pc, match + 1 + prefix_len, ppc->escaped)) return 1; /* * Record the completion. */ if(cpl_add_completion(cpl, line, word_start, word_end, pc->path->name, "", " ")) return 1; /* * The file was rejected by the application. */ } else { match[0] = PCA_F_IGNORE; }; }; }; }; /* * We now need to search for subdirectories of the current directory which * have matching prefixes. First, if needed, make a copy of the word being * matched, with escapes removed. */ prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); if(!prefix) return 1; /* * Now open the current directory. */ if(_dr_open_dir(pc->dr, FS_PWD, NULL)) return 0; /* * Scan the current directory for sub-directories whos names start with * the prefix that we are completing. */ while((filename = _dr_next_file(pc->dr))) { /* * Does the latest filename match the prefix, and is it a directory? */ if(strncmp(filename, prefix, prefix_len) == 0 && _pu_path_is_dir(filename)){ /* * Record the completion. */ if(pca_prepare_suffix(pc, filename + prefix_len, ppc->escaped) || cpl_add_completion(cpl, line, word_start, word_end, pc->path->name, FS_DIR_SEP, FS_DIR_SEP)) return 1; /* * The prefix in pc->path->name will have been overwritten by * pca_prepare_suffix(). Restore it here. */ prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); if(!prefix) return 1; }; }; _dr_close_dir(pc->dr); return 0; } /*....................................................................... * Using the work buffer pc->path, make a suitably escaped copy of a * given completion suffix, ready to be passed to cpl_add_completion(). * * Input: * pc PathCache * The filename cache resource object. * suffix char * The suffix to be copied. * add_escapes int If true, escape special characters. * Output: * return int 0 - OK. * 1 - Error. */ static int pca_prepare_suffix(PathCache *pc, const char *suffix, int add_escapes) { const char *sptr; /* A pointer into suffix[] */ int nbsl; /* The number of backslashes to add to the suffix */ int i; /* * How long is the suffix? */ int suffix_len = strlen(suffix); /* * Clear the work buffer. */ _pn_clear_path(pc->path); /* * Count the number of backslashes that will have to be added to * escape spaces, tabs, backslashes and wildcard characters. */ nbsl = 0; if(add_escapes) { for(sptr = suffix; *sptr; sptr++) { switch(*sptr) { case ' ': case '\t': case '\\': case '*': case '?': case '[': nbsl++; break; }; }; }; /* * Arrange for the output path buffer to have sufficient room for the * both the suffix and any backslashes that have to be inserted. */ if(_pn_resize_path(pc->path, suffix_len + nbsl) == NULL) { _err_record_msg(pc->err, "Insufficient memory to complete file name", END_ERR_MSG); return 1; }; /* * If the suffix doesn't need any escapes, copy it directly into the * work buffer. */ if(nbsl==0) { strcpy(pc->path->name, suffix); } else { /* * Make a copy with special characters escaped? */ if(nbsl > 0) { const char *src = suffix; char *dst = pc->path->name; for(i=0; i= FS_ROOT_DIR_LEN && strncmp(prefix, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0) return 1; /* * Search the prefix for directory separators, returning as soon as * any are found, since their presence indicates that the filename * starts with a pathname specification (valid or otherwise). */ for(i=0; i= FS_DIR_SEP_LEN && strncmp(prefix + i, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) return 1; }; /* * The file name doesn't appear to start with a pathname specification. */ return 0; } /*....................................................................... * If needed make a new copy of the prefix being matched, in pc->path->name, * but with escapes removed. If no escapes are to be removed, simply return * the original prefix string. * * Input: * pc PathCache * The cache being searched. * prefix const char * The prefix to be processed. * prefix_len size_t The length of the prefix. * escaped int If true, return a copy with escapes removed. * Output: * return const char * The prepared prefix, or NULL on error, in * which case an error message will have been * left in pc->err. */ static const char *pca_prepare_prefix(PathCache *pc, const char *prefix, size_t prefix_len, int escaped) { /* * Make a copy with escapes removed? */ if(escaped) { _pn_clear_path(pc->path); if(_pn_append_to_path(pc->path, prefix, prefix_len, 1) == NULL) { _err_record_msg(pc->err, "Insufficient memory to complete filename", END_ERR_MSG); return NULL; }; return pc->path->name; }; return prefix; } /*....................................................................... * If backslashes in the filename should be treated as literal * characters, call the following function with literal=1. Otherwise * the default is to treat them as escape characters, used for escaping * spaces etc.. * * Input: * ppc PcaPathConf * The pca_path_completions() configuration object * to be configured. * literal int Pass non-zero here to enable literal interpretation * of backslashes. Pass 0 to turn off literal * interpretation. */ void ppc_literal_escapes(PcaPathConf *ppc, int literal) { if(ppc) ppc->escaped = !literal; } /*....................................................................... * Call this function if you know where the index at which the * filename prefix starts in the input line. Otherwise by default, * or if you specify start_index to be -1, the filename is taken * to start after the first unescaped space preceding the cursor, * or the start of the line, which ever comes first. * * Input: * ppc PcaPathConf * The pca_path_completions() configuration object * to be configured. * start_index int The index of the start of the filename in * the input line, or -1 to select the default. */ void ppc_file_start(PcaPathConf *ppc, int start_index) { if(ppc) ppc->file_start = start_index; } /*....................................................................... * Expand any ~user expression found at the start of a path, leaving * either an empty string in pc->path if there is no ~user expression, * or the corresponding home directory. * * Input: * pc PathCache * The filename cache. * path const char * The path to expand. * pathlen int The max number of characters to look at in path[]. * literal int If true, treat backslashes as literal characters * instead of escapes. * Input/Output: * endp const char * A pointer to the next unprocessed character in * path[] will be assigned to *endp. * Output: * return int 0 - OK * 1 - Error (a description will have been placed * in pc->err). */ static int pca_expand_tilde(PathCache *pc, const char *path, int pathlen, int literal, const char **endp) { const char *pptr = path; /* A pointer into path[] */ const char *homedir=NULL; /* A home directory */ /* * Clear the pathname buffer. */ _pn_clear_path(pc->path); /* * If the first character is a tilde, then perform home-directory * interpolation. */ if(*pptr == '~') { /* * Skip the tilde character and attempt to read the username that follows * it, into pc->usrnam[]. */ if(pca_read_username(pc, ++pptr, pathlen-1, literal, &pptr)) return 1; /* * Attempt to lookup the home directory of the user. */ homedir = _hd_lookup_home_dir(pc->home, pc->usrnam); if(!homedir) { _err_record_msg(pc->err, _hd_last_home_dir_error(pc->home), END_ERR_MSG); return 1; }; /* * Append the home directory to the pathname string. */ if(_pn_append_to_path(pc->path, homedir, -1, 0) == NULL) { _err_record_msg(pc->err, "Insufficient memory for home directory expansion", END_ERR_MSG); return 1; }; }; /* * ~user and ~ are usually followed by a directory separator to * separate them from the file contained in the home directory. * If the home directory is the root directory, then we don't want * to follow the home directory by a directory separator, so we should * skip over it so that it doesn't get copied into the output pathname */ if(homedir && strcmp(homedir, FS_ROOT_DIR) == 0 && (pptr-path) + FS_DIR_SEP_LEN < pathlen && strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { pptr += FS_DIR_SEP_LEN; }; /* * Return a pointer to the next unprocessed character. */ *endp = pptr; return 0; } /*....................................................................... * Clear the filename status codes that are recorded before each filename * in the cache. * * Input: * pc PathCache * The filename cache. */ static void pca_remove_marks(PathCache *pc) { PathNode *node; /* A node in the list of directories in the path */ int i; /* * Traverse the absolute directories of the path, clearing the * filename status marks that precede each filename. */ for(node=pc->head; node; node=node->next) { if(!node->relative) { for(i=0; infile; i++) *node->files[i] = PCA_F_ENIGMA; }; }; return; } #endif /* ifndef WITHOUT_FILE_SYSTEM */ yuma123_2.14/libtecla/update_html0000775000175000017500000000217214770023131017140 0ustar vladimirvladimir#!/bin/sh # Convert man pages to html files. for dir in man/prog man/libr man/func man/misc man/file; do for template in $dir/*.in;do page=`basename "$template" .in` if [ `wc -l < $template` -gt 1 ]; then html="html/$page.html" man2html $template > $html for ref in libtecla cpl_complete_word ef_expand_file gl_get_line pca_lookup_file enhance gl_io_mode tecla; do link="$ref.html" ed -s $html << EOF %s|$ref[(][^)][^) ]*[)]|$ref|g w q EOF done fi done done # Convert the change log into a web page. cd html echo 'The tecla library change log' > changes.html echo '
' >> changes.html
sed 's/&/&/g; s//\>/g' ../CHANGES >> changes.html
echo '
' >> changes.html # Do the same to the release-notes file. cd ../html echo 'The tecla library release notes' > release.html echo '
' >> release.html
sed 's/&/&/g; s/> release.html
echo '
' >> release.html yuma123_2.14/libtecla/history.c0000664000175000017500000024620414770023131016557 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include #include #include "ioutil.h" #include "history.h" #include "freelist.h" #include "errmsg.h" /* * History lines are split into sub-strings of GLH_SEG_SIZE * characters. To avoid wasting space in the GlhLineSeg structure, * this should be a multiple of the size of a pointer. */ #define GLH_SEG_SIZE 16 /* * GlhLineSeg structures contain fixed sized segments of a larger * string. These are linked into lists to record strings, with all but * the last segment having GLH_SEG_SIZE characters. The last segment * of a string is terminated within the GLH_SEG_SIZE characters with a * '\0'. */ typedef struct GlhLineSeg GlhLineSeg; struct GlhLineSeg { GlhLineSeg *next; /* The next sub-string of the history line */ char s[GLH_SEG_SIZE]; /* The sub-string. Beware that only the final */ /* substring of a line, as indicated by 'next' */ /* being NULL, is '\0' terminated. */ }; /* * History lines are recorded in a hash table, such that repeated * lines are stored just once. * * Start by defining the size of the hash table. This should be a * prime number. */ #define GLH_HASH_SIZE 113 typedef struct GlhHashBucket GlhHashBucket; /* * Each history line will be represented in the hash table by a * structure of the following type. */ typedef struct GlhHashNode GlhHashNode; struct GlhHashNode { GlhHashBucket *bucket; /* The parent hash-table bucket of this node */ GlhHashNode *next; /* The next in the list of nodes within the */ /* parent hash-table bucket. */ GlhLineSeg *head; /* The list of sub-strings which make up a line */ int len; /* The length of the line, excluding any '\0' */ int used; /* The number of times this string is pointed to by */ /* the time-ordered list of history lines. */ int reported; /* A flag that is used when searching to ensure that */ /* a line isn't reported redundantly. */ }; /* * How many new GlhHashNode elements should be allocated at a time? */ #define GLH_HASH_INCR 50 static int _glh_is_line(GlhHashNode *hash, const char *line, size_t n); static int _glh_line_matches_prefix(GlhHashNode *line, GlhHashNode *prefix); static void _glh_return_line(GlhHashNode *hash, char *line, size_t dim); /* * All history lines which hash to a given bucket in the hash table, are * recorded in a structure of the following type. */ struct GlhHashBucket { GlhHashNode *lines; /* The list of history lines which fall in this bucket */ }; static GlhHashBucket *glh_find_bucket(GlHistory *glh, const char *line, size_t n); static GlhHashNode *glh_find_hash_node(GlhHashBucket *bucket, const char *line, size_t n); typedef struct { FreeList *node_mem; /* A free-list of GlhHashNode structures */ GlhHashBucket bucket[GLH_HASH_SIZE]; /* The buckets of the hash table */ } GlhLineHash; /* * GlhLineNode's are used to record history lines in time order. */ typedef struct GlhLineNode GlhLineNode; struct GlhLineNode { long id; /* The unique identifier of this history line */ time_t timestamp; /* The time at which the line was archived */ unsigned group; /* The identifier of the history group to which the */ /* the line belongs. */ GlhLineNode *next; /* The next youngest line in the list */ GlhLineNode *prev; /* The next oldest line in the list */ GlhHashNode *line; /* The hash-table entry of the history line */ }; /* * The number of GlhLineNode elements per freelist block. */ #define GLH_LINE_INCR 100 /* * Encapsulate the time-ordered list of historical lines. */ typedef struct { FreeList *node_mem; /* A freelist of GlhLineNode objects */ GlhLineNode *head; /* The oldest line in the list */ GlhLineNode *tail; /* The newest line in the list */ } GlhLineList; /* * The _glh_lookup_history() returns copies of history lines in a * dynamically allocated array. This array is initially allocated * GLH_LOOKUP_SIZE bytes. If subsequently this size turns out to be * too small, realloc() is used to increase its size to the required * size plus GLH_LOOKUP_MARGIN. The idea of the later parameter is to * reduce the number of realloc() operations needed. */ #define GLH_LBUF_SIZE 300 #define GLH_LBUF_MARGIN 100 /* * Encapsulate all of the resources needed to store historical input lines. */ struct GlHistory { ErrMsg *err; /* The error-reporting buffer */ GlhLineSeg *buffer; /* An array of sub-line nodes to be partitioned */ /* into lists of sub-strings recording input lines. */ int nbuff; /* The allocated dimension of buffer[] */ GlhLineSeg *unused; /* The list of free nodes in buffer[] */ GlhLineList list; /* A time ordered list of history lines */ GlhLineNode *recall; /* The last line recalled, or NULL if no recall */ /* session is currently active. */ GlhLineNode *id_node;/* The node at which the last ID search terminated */ GlhLineHash hash; /* A hash-table of reference-counted history lines */ GlhHashNode *prefix; /* A pointer to a line containing the prefix that */ /* is being searched for. Note that if prefix==NULL */ /* and prefix_len>0, this means that no line in */ /* the buffer starts with the requested prefix. */ int prefix_len; /* The length of the prefix being searched for. */ char *lbuf; /* The array in which _glh_lookup_history() returns */ /* history lines */ int lbuf_dim; /* The allocated size of lbuf[] */ int nbusy; /* The number of line segments in buffer[] that are */ /* currently being used to record sub-lines */ int nfree; /* The number of line segments in buffer that are */ /* not currently being used to record sub-lines */ unsigned long seq; /* The next ID to assign to a line node */ unsigned group; /* The identifier of the current history group */ int nline; /* The number of lines currently in the history list */ int max_lines; /* Either -1 or a ceiling on the number of lines */ int enable; /* If false, ignore history additions and lookups */ }; #ifndef WITHOUT_FILE_SYSTEM static int _glh_cant_load_history(GlHistory *glh, const char *filename, int lineno, const char *message, FILE *fp); static int _glh_cant_save_history(GlHistory *glh, const char *message, const char *filename, FILE *fp); static int _glh_write_timestamp(FILE *fp, time_t timestamp); static int _glh_decode_timestamp(char *string, char **endp, time_t *timestamp); #endif static void _glh_discard_line(GlHistory *glh, GlhLineNode *node); static GlhLineNode *_glh_find_id(GlHistory *glh, GlhLineID id); static GlhHashNode *_glh_acquire_copy(GlHistory *glh, const char *line, size_t n); static GlhHashNode *_glh_discard_copy(GlHistory *glh, GlhHashNode *hnode); static int _glh_prepare_for_recall(GlHistory *glh, char *line); /* * The following structure and functions are used to iterate through * the characters of a segmented history line. */ typedef struct { GlhLineSeg *seg; /* The line segment that the next character will */ /* be returned from. */ int posn; /* The index in the above line segment, containing */ /* the next unread character. */ char c; /* The current character in the input line */ } GlhLineStream; static void glh_init_stream(GlhLineStream *str, GlhHashNode *line); static void glh_step_stream(GlhLineStream *str); /* * See if search prefix contains any globbing characters. */ static int glh_contains_glob(GlhHashNode *prefix); /* * Match a line against a search pattern. */ static int glh_line_matches_glob(GlhLineStream *lstr, GlhLineStream *pstr); static int glh_matches_range(char c, GlhLineStream *pstr); /*....................................................................... * Create a line history maintenance object. * * Input: * buflen size_t The number of bytes to allocate to the * buffer that is used to record all of the * most recent lines of user input that will fit. * If buflen==0, no buffer will be allocated. * Output: * return GlHistory * The new object, or NULL on error. */ GlHistory *_new_GlHistory(size_t buflen) { GlHistory *glh; /* The object to be returned */ int i; /* * Allocate the container. */ glh = (GlHistory *) malloc(sizeof(GlHistory)); if(!glh) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_GlHistory(). */ glh->err = NULL; glh->buffer = NULL; glh->nbuff = (buflen+GLH_SEG_SIZE-1) / GLH_SEG_SIZE; glh->unused = NULL; glh->list.node_mem = NULL; glh->list.head = glh->list.tail = NULL; glh->recall = NULL; glh->id_node = NULL; glh->hash.node_mem = NULL; for(i=0; ihash.bucket[i].lines = NULL; glh->prefix = NULL; glh->lbuf = NULL; glh->lbuf_dim = 0; glh->nbusy = 0; glh->nfree = glh->nbuff; glh->seq = 0; glh->group = 0; glh->nline = 0; glh->max_lines = -1; glh->enable = 1; /* * Allocate a place to record error messages. */ glh->err = _new_ErrMsg(); if(!glh->err) return _del_GlHistory(glh); /* * Allocate the buffer, if required. */ if(glh->nbuff > 0) { glh->nbuff = glh->nfree; glh->buffer = (GlhLineSeg *) malloc(sizeof(GlhLineSeg) * glh->nbuff); if(!glh->buffer) { errno = ENOMEM; return _del_GlHistory(glh); }; /* * All nodes of the buffer are currently unused, so link them all into * a list and make glh->unused point to the head of this list. */ glh->unused = glh->buffer; for(i=0; inbuff-1; i++) { GlhLineSeg *seg = glh->unused + i; seg->next = seg + 1; }; glh->unused[i].next = NULL; }; /* * Allocate the GlhLineNode freelist. */ glh->list.node_mem = _new_FreeList(sizeof(GlhLineNode), GLH_LINE_INCR); if(!glh->list.node_mem) return _del_GlHistory(glh); /* * Allocate the GlhHashNode freelist. */ glh->hash.node_mem = _new_FreeList(sizeof(GlhLineNode), GLH_HASH_INCR); if(!glh->hash.node_mem) return _del_GlHistory(glh); /* * Allocate the array that _glh_lookup_history() uses to return a * copy of a given history line. This will be resized when necessary. */ glh->lbuf_dim = GLH_LBUF_SIZE; glh->lbuf = (char *) malloc(glh->lbuf_dim); if(!glh->lbuf) { errno = ENOMEM; return _del_GlHistory(glh); }; return glh; } /*....................................................................... * Delete a GlHistory object. * * Input: * glh GlHistory * The object to be deleted. * Output: * return GlHistory * The deleted object (always NULL). */ GlHistory *_del_GlHistory(GlHistory *glh) { if(glh) { /* * Delete the error-message buffer. */ glh->err = _del_ErrMsg(glh->err); /* * Delete the buffer. */ if(glh->buffer) { free(glh->buffer); glh->buffer = NULL; glh->unused = NULL; }; /* * Delete the freelist of GlhLineNode's. */ glh->list.node_mem = _del_FreeList(glh->list.node_mem, 1); /* * The contents of the list were deleted by deleting the freelist. */ glh->list.head = NULL; glh->list.tail = NULL; /* * Delete the freelist of GlhHashNode's. */ glh->hash.node_mem = _del_FreeList(glh->hash.node_mem, 1); /* * Delete the lookup buffer. */ if(glh->lbuf) free(glh->lbuf); /* * Delete the container. */ free(glh); }; return NULL; } /*....................................................................... * Append a new line to the history list, deleting old lines to make * room, if needed. * * Input: * glh GlHistory * The input-line history maintenance object. * line char * The line to be archived. * force int Unless this flag is non-zero, empty lines aren't * archived. This flag requests that the line be * archived regardless. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_add_history(GlHistory *glh, const char *line, int force) { int slen; /* The length of the line to be recorded (minus the '\0') */ int empty; /* True if the string is empty */ const char *nlptr; /* A pointer to a newline character in line[] */ GlhHashNode *hnode; /* The hash-table node of the line */ GlhLineNode *lnode; /* A node in the time-ordered list of lines */ int i; /* * Check the arguments. */ if(!glh || !line) { errno = EINVAL; return 1; }; /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return 0; /* * Cancel any ongoing search. */ if(_glh_cancel_search(glh)) return 1; /* * How long is the string to be recorded, being careful not to include * any terminating '\n' character. */ nlptr = strchr(line, '\n'); if(nlptr) slen = (nlptr - line); else slen = strlen(line); /* * Is the line empty? */ empty = 1; for(i=0; imax_lines >= 0) { /* * If necessary, remove old lines until there is room to add one new * line without exceeding the specified line limit. */ while(glh->nline > 0 && glh->nline >= glh->max_lines) _glh_discard_line(glh, glh->list.head); /* * We can't archive the line if the maximum number of lines allowed is * zero. */ if(glh->max_lines == 0) return 0; }; /* * Unless already stored, store a copy of the line in the history buffer, * then return a reference-counted hash-node pointer to this copy. */ hnode = _glh_acquire_copy(glh, line, slen); if(!hnode) { _err_record_msg(glh->err, "No room to store history line", END_ERR_MSG); errno = ENOMEM; return 1; }; /* * Allocate a new node in the time-ordered list of lines. */ lnode = (GlhLineNode *) _new_FreeListNode(glh->list.node_mem); /* * If a new line-node couldn't be allocated, discard our copy of the * stored line before reporting the error. */ if(!lnode) { hnode = _glh_discard_copy(glh, hnode); _err_record_msg(glh->err, "No room to store history line", END_ERR_MSG); errno = ENOMEM; return 1; }; /* * Record a pointer to the hash-table record of the line in the new * list node. */ lnode->id = glh->seq++; lnode->timestamp = time(NULL); lnode->group = glh->group; lnode->line = hnode; /* * Append the new node to the end of the time-ordered list. */ if(glh->list.head) glh->list.tail->next = lnode; else glh->list.head = lnode; lnode->next = NULL; lnode->prev = glh->list.tail; glh->list.tail = lnode; /* * Record the addition of a line to the list. */ glh->nline++; return 0; } /*....................................................................... * Recall the next oldest line that has the search prefix last recorded * by _glh_search_prefix(). * * Input: * glh GlHistory * The input-line history maintenance object. * line char * The input line buffer. On input this should contain * the current input line, and on output, if anything * was found, its contents will have been replaced * with the matching line. * dim size_t The allocated dimension of the line buffer. * Output: * return char * A pointer to line[0], or NULL if not found. */ char *_glh_find_backwards(GlHistory *glh, char *line, size_t dim) { GlhLineNode *node; /* The line location node being checked */ GlhHashNode *old_line; /* The previous recalled line */ /* * Check the arguments. */ if(!glh || !line) { if(glh) _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return NULL; }; /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return NULL; /* * Check the line dimensions. */ if(dim < strlen(line) + 1) { _err_record_msg(glh->err, "'dim' argument inconsistent with strlen(line)", END_ERR_MSG); errno = EINVAL; return NULL; }; /* * Preserve the input line if needed. */ if(_glh_prepare_for_recall(glh, line)) return NULL; /* * From where should we start the search? */ if(glh->recall) { node = glh->recall->prev; old_line = glh->recall->line; } else { node = glh->list.tail; old_line = NULL; }; /* * Search backwards through the list for the first match with the * prefix string that differs from the last line that was recalled. */ while(node && (node->group != glh->group || node->line == old_line || !_glh_line_matches_prefix(node->line, glh->prefix))) node = node->prev; /* * Was a matching line found? */ if(node) { /* * Recall the found node as the starting point for subsequent * searches. */ glh->recall = node; /* * Copy the matching line into the provided line buffer. */ _glh_return_line(node->line, line, dim); /* * Return it. */ return line; }; /* * No match was found. */ return NULL; } /*....................................................................... * Recall the next newest line that has the search prefix last recorded * by _glh_search_prefix(). * * Input: * glh GlHistory * The input-line history maintenance object. * line char * The input line buffer. On input this should contain * the current input line, and on output, if anything * was found, its contents will have been replaced * with the matching line. * dim size_t The allocated dimensions of the line buffer. * Output: * return char * The line requested, or NULL if no matching line * was found. */ char *_glh_find_forwards(GlHistory *glh, char *line, size_t dim) { GlhLineNode *node; /* The line location node being checked */ GlhHashNode *old_line; /* The previous recalled line */ /* * Check the arguments. */ if(!glh || !line) { if(glh) _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return NULL; }; /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return NULL; /* * Check the line dimensions. */ if(dim < strlen(line) + 1) { _err_record_msg(glh->err, "'dim' argument inconsistent with strlen(line)", END_ERR_MSG); errno = EINVAL; return NULL; }; /* * From where should we start the search? */ if(glh->recall) { node = glh->recall->next; old_line = glh->recall->line; } else { return NULL; }; /* * Search forwards through the list for the first match with the * prefix string. */ while(node && (node->group != glh->group || node->line == old_line || !_glh_line_matches_prefix(node->line, glh->prefix))) node = node->next; /* * Was a matching line found? */ if(node) { /* * Copy the matching line into the provided line buffer. */ _glh_return_line(node->line, line, dim); /* * Record the starting point of the next search. */ glh->recall = node; /* * If we just returned the line that was being entered when the search * session first started, cancel the search. */ if(node == glh->list.tail) _glh_cancel_search(glh); /* * Return the matching line to the user. */ return line; }; /* * No match was found. */ return NULL; } /*....................................................................... * If a search is in progress, cancel it. * * This involves discarding the line that was temporarily saved by * _glh_find_backwards() when the search was originally started, * and reseting the search iteration pointer to NULL. * * Input: * glh GlHistory * The input-line history maintenance object. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_cancel_search(GlHistory *glh) { /* * Check the arguments. */ if(!glh) { errno = EINVAL; return 1; }; /* * If there wasn't a search in progress, do nothing. */ if(!glh->recall) return 0; /* * Reset the search pointers. Note that it is essential to set * glh->recall to NULL before calling _glh_discard_line(), to avoid an * infinite recursion. */ glh->recall = NULL; /* * Delete the node of the preserved line. */ _glh_discard_line(glh, glh->list.tail); return 0; } /*....................................................................... * Set the prefix of subsequent history searches. * * Input: * glh GlHistory * The input-line history maintenance object. * line const char * The command line who's prefix is to be used. * prefix_len int The length of the prefix. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_search_prefix(GlHistory *glh, const char *line, int prefix_len) { /* * Check the arguments. */ if(!glh) { errno = EINVAL; return 1; }; /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return 0; /* * Discard any existing prefix. */ glh->prefix = _glh_discard_copy(glh, glh->prefix); /* * Only store a copy of the prefix string if it isn't a zero-length string. */ if(prefix_len > 0) { /* * Get a reference-counted copy of the prefix from the history cache buffer. */ glh->prefix = _glh_acquire_copy(glh, line, prefix_len); /* * Was there insufficient buffer space? */ if(!glh->prefix) { _err_record_msg(glh->err, "The search prefix is too long to store", END_ERR_MSG); errno = ENOMEM; return 1; }; }; return 0; } /*....................................................................... * Recall the oldest recorded line. * * Input: * glh GlHistory * The input-line history maintenance object. * line char * The input line buffer. On input this should contain * the current input line, and on output, its contents * will have been replaced with the oldest line. * dim size_t The allocated dimensions of the line buffer. * Output: * return char * A pointer to line[0], or NULL if not found. */ char *_glh_oldest_line(GlHistory *glh, char *line, size_t dim) { GlhLineNode *node; /* The line location node being checked */ /* * Check the arguments. */ if(!glh || !line) { if(glh) _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return NULL; }; /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return NULL; /* * Check the line dimensions. */ if(dim < strlen(line) + 1) { _err_record_msg(glh->err, "'dim' argument inconsistent with strlen(line)", END_ERR_MSG); errno = EINVAL; return NULL; }; /* * Preserve the input line if needed. */ if(_glh_prepare_for_recall(glh, line)) return NULL; /* * Locate the oldest line that belongs to the current group. */ for(node=glh->list.head; node && node->group != glh->group; node = node->next) ; /* * No line found? */ if(!node) return NULL; /* * Record the above node as the starting point for subsequent * searches. */ glh->recall = node; /* * Copy the recalled line into the provided line buffer. */ _glh_return_line(node->line, line, dim); /* * If we just returned the line that was being entered when the search * session first started, cancel the search. */ if(node == glh->list.tail) _glh_cancel_search(glh); return line; } /*....................................................................... * Recall the line that was being entered when the search started. * * Input: * glh GlHistory * The input-line history maintenance object. * line char * The input line buffer. On input this should contain * the current input line, and on output, its contents * will have been replaced with the line that was * being entered when the search was started. * dim size_t The allocated dimensions of the line buffer. * Output: * return char * A pointer to line[0], or NULL if not found. */ char *_glh_current_line(GlHistory *glh, char *line, size_t dim) { /* * Check the arguments. */ if(!glh || !line) { if(glh) _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return NULL; }; /* * If history isn't enabled, or no history search has yet been started, * ignore the call. */ if(!glh->enable || !glh->buffer || glh->max_lines == 0 || !glh->recall) return NULL; /* * Check the line dimensions. */ if(dim < strlen(line) + 1) { _err_record_msg(glh->err, "'dim' argument inconsistent with strlen(line)", END_ERR_MSG); errno = EINVAL; return NULL; }; /* * Copy the recalled line into the provided line buffer. */ _glh_return_line(glh->list.tail->line, line, dim); /* * Since we have returned to the starting point of the search, cancel it. */ _glh_cancel_search(glh); return line; } /*....................................................................... * Query the id of a history line offset by a given number of lines from * the one that is currently being recalled. If a recall session isn't * in progress, or the offset points outside the history list, 0 is * returned. * * Input: * glh GlHistory * The input-line history maintenance object. * offset int The line offset (0 for the current line, < 0 * for an older line, > 0 for a newer line. * Output: * return GlhLineID The identifier of the line that is currently * being recalled, or 0 if no recall session is * currently in progress. */ GlhLineID _glh_line_id(GlHistory *glh, int offset) { GlhLineNode *node; /* The line location node being checked */ /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return 0; /* * Search forward 'offset' lines to find the required line. */ if(offset >= 0) { for(node=glh->recall; node && offset != 0; node=node->next) { if(node->group == glh->group) offset--; }; } else { for(node=glh->recall; node && offset != 0; node=node->prev) { if(node->group == glh->group) offset++; }; }; return node ? node->id : 0; } /*....................................................................... * Recall a line by its history buffer ID. If the line is no longer * in the buffer, or the id is zero, NULL is returned. * * Input: * glh GlHistory * The input-line history maintenance object. * id GlhLineID The ID of the line to be returned. * line char * The input line buffer. On input this should contain * the current input line, and on output, its contents * will have been replaced with the saved line. * dim size_t The allocated dimensions of the line buffer. * Output: * return char * A pointer to line[0], or NULL if not found. */ char *_glh_recall_line(GlHistory *glh, GlhLineID id, char *line, size_t dim) { GlhLineNode *node; /* The line location node being checked */ /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return NULL; /* * Preserve the input line if needed. */ if(_glh_prepare_for_recall(glh, line)) return NULL; /* * Search for the specified line. */ node = _glh_find_id(glh, id); /* * Not found? */ if(!node || node->group != glh->group) return NULL; /* * Record the node of the matching line as the starting point * for subsequent searches. */ glh->recall = node; /* * Copy the recalled line into the provided line buffer. */ _glh_return_line(node->line, line, dim); return line; } /*....................................................................... * Save the current history in a specified file. * * Input: * glh GlHistory * The input-line history maintenance object. * filename const char * The name of the new file to record the * history in. * comment const char * Extra information such as timestamps will * be recorded on a line started with this * string, the idea being that the file can * double as a command file. Specify "" if * you don't care. * max_lines int The maximum number of lines to save, or -1 * to save all of the lines in the history * list. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_save_history(GlHistory *glh, const char *filename, const char *comment, int max_lines) { #ifdef WITHOUT_FILE_SYSTEM _err_record_msg(glh->err, "Can't save history without filesystem access", END_ERR_MSG); errno = EINVAL; return 1; #else FILE *fp; /* The output file */ GlhLineNode *node; /* The line being saved */ GlhLineNode *head; /* The head of the list of lines to be saved */ GlhLineSeg *seg; /* One segment of a line being saved */ /* * Check the arguments. */ if(!glh || !filename || !comment) { if(glh) _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Attempt to open the specified file. */ fp = fopen(filename, "w"); if(!fp) return _glh_cant_save_history(glh, "Can't open", filename, NULL); /* * If a ceiling on the number of lines to save was specified, count * that number of lines backwards, to find the first line to be saved. */ head = NULL; if(max_lines >= 0) { for(head=glh->list.tail; head && --max_lines > 0; head=head->prev) ; }; if(!head) head = glh->list.head; /* * Write the contents of the history buffer to the history file, writing * associated data such as timestamps, to a line starting with the * specified comment string. */ for(node=head; node; node=node->next) { /* * Write peripheral information associated with the line, as a comment. */ if(fprintf(fp, "%s ", comment) < 0 || _glh_write_timestamp(fp, node->timestamp) || fprintf(fp, " %u\n", node->group) < 0) { return _glh_cant_save_history(glh, "Error writing", filename, fp); }; /* * Write the history line. */ for(seg=node->line->head; seg; seg=seg->next) { size_t slen = seg->next ? GLH_SEG_SIZE : strlen(seg->s); if(fwrite(seg->s, sizeof(char), slen, fp) != slen) return _glh_cant_save_history(glh, "Error writing", filename, fp); }; fputc('\n', fp); }; /* * Close the history file. */ if(fclose(fp) == EOF) return _glh_cant_save_history(glh, "Error writing", filename, NULL); return 0; #endif } #ifndef WITHOUT_FILE_SYSTEM /*....................................................................... * This is a private error return function of _glh_save_history(). It * composes an error report in the error buffer, composed using * sprintf("%s %s (%s)", message, filename, strerror(errno)). It then * closes fp and returns the error return code of _glh_save_history(). * * Input: * glh GlHistory * The input-line history maintenance object. * message const char * A message to be followed by the filename. * filename const char * The name of the offending output file. * fp FILE * The stream to be closed (send NULL if not * open). * Output: * return int Always 1. */ static int _glh_cant_save_history(GlHistory *glh, const char *message, const char *filename, FILE *fp) { _err_record_msg(glh->err, message, filename, " (", strerror(errno), ")", END_ERR_MSG); if(fp) (void) fclose(fp); return 1; } /*....................................................................... * Write a timestamp to a given stdio stream, in the format * yyyymmddhhmmss * * Input: * fp FILE * The stream to write to. * timestamp time_t The timestamp to be written. * Output: * return int 0 - OK. * 1 - Error. */ static int _glh_write_timestamp(FILE *fp, time_t timestamp) { struct tm *t; /* THe broken-down calendar time */ /* * Get the calendar components corresponding to the given timestamp. */ if(timestamp < 0 || (t = localtime(×tamp)) == NULL) { if(fprintf(fp, "?") < 0) return 1; return 0; }; /* * Write the calendar time as yyyymmddhhmmss. */ if(fprintf(fp, "%04d%02d%02d%02d%02d%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec) < 0) return 1; return 0; } #endif /*....................................................................... * Restore previous history lines from a given file. * * Input: * glh GlHistory * The input-line history maintenance object. * filename const char * The name of the file to read from. * comment const char * The same comment string that was passed to * _glh_save_history() when this file was * written. * line char * A buffer into which lines can be read. * dim size_t The allocated dimension of line[]. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_load_history(GlHistory *glh, const char *filename, const char *comment, char *line, size_t dim) { #ifdef WITHOUT_FILE_SYSTEM _err_record_msg(glh->err, "Can't load history without filesystem access", END_ERR_MSG); errno = EINVAL; return 1; #else FILE *fp; /* The output file */ size_t comment_len; /* The length of the comment string */ time_t timestamp; /* The timestamp of the history line */ unsigned group; /* The identifier of the history group to which */ /* the line belongs. */ int lineno; /* The line number being read */ /* * Check the arguments. */ if(!glh || !filename || !comment || !line) { if(glh) _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Measure the length of the comment string. */ comment_len = strlen(comment); /* * Clear the history list. */ _glh_clear_history(glh, 1); /* * Attempt to open the specified file. Don't treat it as an error * if the file doesn't exist. */ fp = fopen(filename, "r"); if(!fp) return 0; /* * Attempt to read each line and preceding peripheral info, and add these * to the history list. */ for(lineno=1; fgets(line, dim, fp) != NULL; lineno++) { char *lptr; /* A pointer into the input line */ /* * Check that the line starts with the comment string. */ if(strncmp(line, comment, comment_len) != 0) { return _glh_cant_load_history(glh, filename, lineno, "Corrupt history parameter line", fp); }; /* * Skip spaces and tabs after the comment. */ for(lptr=line+comment_len; *lptr && (*lptr==' ' || *lptr=='\t'); lptr++) ; /* * The next word must be a timestamp. */ if(_glh_decode_timestamp(lptr, &lptr, ×tamp)) { return _glh_cant_load_history(glh, filename, lineno, "Corrupt timestamp", fp); }; /* * Skip spaces and tabs. */ while(*lptr==' ' || *lptr=='\t') lptr++; /* * The next word must be an unsigned integer group number. */ group = (int) strtoul(lptr, &lptr, 10); if(*lptr != ' ' && *lptr != '\n') { return _glh_cant_load_history(glh, filename, lineno, "Corrupt group id", fp); }; /* * Skip spaces and tabs. */ while(*lptr==' ' || *lptr=='\t') lptr++; /* * There shouldn't be anything left on the line. */ if(*lptr != '\n') { return _glh_cant_load_history(glh, filename, lineno, "Corrupt parameter line", fp); }; /* * Now read the history line itself. */ lineno++; if(fgets(line, dim, fp) == NULL) return _glh_cant_load_history(glh, filename, lineno, "Read error", fp); /* * Append the line to the history buffer. */ if(_glh_add_history(glh, line, 1)) { return _glh_cant_load_history(glh, filename, lineno, "Insufficient memory to record line", fp); }; /* * Record the group and timestamp information along with the line. */ if(glh->list.tail) { glh->list.tail->timestamp = timestamp; glh->list.tail->group = group; }; }; /* * Close the file. */ (void) fclose(fp); return 0; #endif } #ifndef WITHOUT_FILE_SYSTEM /*....................................................................... * This is a private error return function of _glh_load_history(). */ static int _glh_cant_load_history(GlHistory *glh, const char *filename, int lineno, const char *message, FILE *fp) { char lnum[20]; /* * Convert the line number to a string. */ sprintf(lnum, "%d", lineno); /* * Render an error message. */ _err_record_msg(glh->err, filename, ":", lnum, ":", message, END_ERR_MSG); /* * Close the file. */ if(fp) (void) fclose(fp); return 1; } /*....................................................................... * Read a timestamp from a string. * * Input: * string char * The string to read from. * Input/Output: * endp char ** On output *endp will point to the next unprocessed * character in string[]. * timestamp time_t * The timestamp will be assigned to *t. * Output: * return int 0 - OK. * 1 - Error. */ static int _glh_decode_timestamp(char *string, char **endp, time_t *timestamp) { unsigned year,month,day,hour,min,sec; /* Calendar time components */ struct tm t; /* * There are 14 characters in the date format yyyymmddhhmmss. */ enum {TSLEN=14}; char timestr[TSLEN+1]; /* The timestamp part of the string */ /* * If the time wasn't available at the time that the line was recorded * it will have been written as "?". Check for this before trying * to read the timestamp. */ if(string[0] == '\?') { *endp = string+1; *timestamp = -1; return 0; }; /* * The timestamp is expected to be written in the form yyyymmddhhmmss. */ if(strlen(string) < TSLEN) { *endp = string; return 1; }; /* * Copy the timestamp out of the string. */ strncpy(timestr, string, TSLEN); timestr[TSLEN] = '\0'; /* * Decode the timestamp. */ if(sscanf(timestr, "%4u%2u%2u%2u%2u%2u", &year, &month, &day, &hour, &min, &sec) != 6) { *endp = string; return 1; }; /* * Advance the string pointer over the successfully read timestamp. */ *endp = string + TSLEN; /* * Copy the read values into a struct tm. */ t.tm_sec = sec; t.tm_min = min; t.tm_hour = hour; t.tm_mday = day; t.tm_wday = 0; t.tm_yday = 0; t.tm_mon = month - 1; t.tm_year = year - 1900; t.tm_isdst = -1; /* * Convert the contents of the struct tm to a time_t. */ *timestamp = mktime(&t); return 0; } #endif /*....................................................................... * Switch history groups. * * Input: * glh GlHistory * The input-line history maintenance object. * group unsigned The new group identifier. This will be recorded * with subsequent history lines, and subsequent * history searches will only return lines with * this group identifier. This allows multiple * separate history lists to exist within * a single GlHistory object. Note that the * default group identifier is 0. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_set_group(GlHistory *glh, unsigned group) { /* * Check the arguments. */ if(!glh) { if(glh) _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Is the group being changed? */ if(group != glh->group) { /* * Cancel any ongoing search. */ if(_glh_cancel_search(glh)) return 1; /* * Record the new group. */ glh->group = group; }; return 0; } /*....................................................................... * Query the current history group. * * Input: * glh GlHistory * The input-line history maintenance object. * Output: * return unsigned The group identifier. */ int _glh_get_group(GlHistory *glh) { return glh ? glh->group : 0; } /*....................................................................... * Display the contents of the history list. * * Input: * glh GlHistory * The input-line history maintenance object. * write_fn GlWriteFn * The function to call to write the line, or * 0 to discard the output. * data void * Anonymous data to pass to write_fn(). * fmt const char * A format string. This can contain arbitrary * characters, which are written verbatim, plus * any of the following format directives: * %D - The date, like 2001-11-20 * %T - The time of day, like 23:59:59 * %N - The sequential entry number of the * line in the history buffer. * %G - The history group number of the line. * %% - A literal % character. * %H - The history line. * all_groups int If true, display history lines from all * history groups. Otherwise only display * those of the current history group. * max_lines int If max_lines is < 0, all available lines * are displayed. Otherwise only the most * recent max_lines lines will be displayed. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_show_history(GlHistory *glh, GlWriteFn *write_fn, void *data, const char *fmt, int all_groups, int max_lines) { GlhLineNode *node; /* The line being displayed */ GlhLineNode *oldest; /* The oldest line to display */ GlhLineSeg *seg; /* One segment of a line being displayed */ enum {TSMAX=32}; /* The maximum length of the date and time string */ char buffer[TSMAX+1]; /* The buffer in which to write the date and time */ int idlen; /* The length of displayed ID strings */ unsigned grpmax; /* The maximum group number in the buffer */ int grplen; /* The number of characters needed to print grpmax */ int len; /* The length of a string to be written */ /* * Check the arguments. */ if(!glh || !write_fn || !fmt) { if(glh) _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Is history enabled? */ if(!glh->enable || !glh->list.head) return 0; /* * Work out the length to display ID numbers, choosing the length of * the biggest number in the buffer. Smaller numbers will be padded * with leading zeroes if needed. */ sprintf(buffer, "%lu", (unsigned long) glh->list.tail->id); idlen = strlen(buffer); /* * Find the largest group number. */ grpmax = 0; for(node=glh->list.head; node; node=node->next) { if(node->group > grpmax) grpmax = node->group; }; /* * Find out how many characters are needed to display the group number. */ sprintf(buffer, "%u", (unsigned) grpmax); grplen = strlen(buffer); /* * Find the node that follows the oldest line to be displayed. */ if(max_lines < 0) { oldest = glh->list.head; } else if(max_lines==0) { return 0; } else { for(oldest=glh->list.tail; oldest; oldest=oldest->prev) { if((all_groups || oldest->group == glh->group) && --max_lines <= 0) break; }; /* * If the number of lines in the buffer doesn't exceed the specified * maximum, start from the oldest line in the buffer. */ if(!oldest) oldest = glh->list.head; }; /* * List the history lines in increasing time order. */ for(node=oldest; node; node=node->next) { /* * Only display lines from the current history group, unless * told otherwise. */ if(all_groups || node->group == glh->group) { const char *fptr; /* A pointer into the format string */ struct tm *t = NULL; /* The broken time version of the timestamp */ /* * Work out the calendar representation of the node timestamp. */ if(node->timestamp != (time_t) -1) t = localtime(&node->timestamp); /* * Parse the format string. */ fptr = fmt; while(*fptr) { /* * Search for the start of the next format directive or the end of the string. */ const char *start = fptr; while(*fptr && *fptr != '%') fptr++; /* * Display any literal characters that precede the located directive. */ if(fptr > start) { len = (int) (fptr - start); if(write_fn(data, start, len) != len) return 1; }; /* * Did we hit a new directive before the end of the line? */ if(*fptr) { /* * Obey the directive. Ignore unknown directives. */ switch(*++fptr) { case 'D': /* Display the date */ if(t && strftime(buffer, TSMAX, "%Y-%m-%d", t) != 0) { len = strlen(buffer); if(write_fn(data, buffer, len) != len) return 1; }; break; case 'T': /* Display the time of day */ if(t && strftime(buffer, TSMAX, "%H:%M:%S", t) != 0) { len = strlen(buffer); if(write_fn(data, buffer, len) != len) return 1; }; break; case 'N': /* Display the sequential entry number */ sprintf(buffer, "%*lu", idlen, (unsigned long) node->id); len = strlen(buffer); if(write_fn(data, buffer, len) != len) return 1; break; case 'G': sprintf(buffer, "%*u", grplen, (unsigned) node->group); len = strlen(buffer); if(write_fn(data, buffer, len) != len) return 1; break; case 'H': /* Display the history line */ for(seg=node->line->head; seg; seg=seg->next) { len = seg->next ? GLH_SEG_SIZE : strlen(seg->s); if(write_fn(data, seg->s, len) != len) return 1; }; break; case '%': /* A literal % symbol */ if(write_fn(data, "%", 1) != 1) return 1; break; }; /* * Skip the directive. */ if(*fptr) fptr++; }; }; }; }; return 0; } /*....................................................................... * Change the size of the history buffer. * * Input: * glh GlHistory * The input-line history maintenance object. * bufsize size_t The number of bytes in the history buffer, or 0 * to delete the buffer completely. * Output: * return int 0 - OK. * 1 - Insufficient memory (the previous buffer * will have been retained). No error message * will be displayed. */ int _glh_resize_history(GlHistory *glh, size_t bufsize) { int nbuff; /* The number of segments in the new buffer */ int i; /* * Check the arguments. */ if(!glh) { errno = EINVAL; return 1; }; /* * How many buffer segments does the requested buffer size correspond * to? */ nbuff = (bufsize+GLH_SEG_SIZE-1) / GLH_SEG_SIZE; /* * Has a different size than the current size been requested? */ if(glh->nbuff != nbuff) { /* * Cancel any ongoing search. */ (void) _glh_cancel_search(glh); /* * Create a wholly new buffer? */ if(glh->nbuff == 0 && nbuff>0) { glh->buffer = (GlhLineSeg *) malloc(sizeof(GlhLineSeg) * nbuff); if(!glh->buffer) return 1; glh->nbuff = nbuff; glh->nfree = glh->nbuff; glh->nbusy = 0; glh->nline = 0; /* * Link the currently unused nodes of the buffer into a list. */ glh->unused = glh->buffer; for(i=0; inbuff-1; i++) { GlhLineSeg *seg = glh->unused + i; seg->next = seg + 1; }; glh->unused[i].next = NULL; /* * Delete an existing buffer? */ } else if(nbuff == 0) { _glh_clear_history(glh, 1); free(glh->buffer); glh->buffer = NULL; glh->unused = NULL; glh->nbuff = 0; glh->nfree = 0; glh->nbusy = 0; glh->nline = 0; /* * Change from one finite buffer size to another? */ } else { GlhLineSeg *buffer; /* The resized buffer */ int nbusy; /* The number of used line segments in the new buffer */ /* * Starting from the oldest line in the buffer, discard lines until * the buffer contains at most 'nbuff' used line segments. */ while(glh->list.head && glh->nbusy > nbuff) _glh_discard_line(glh, glh->list.head); /* * Attempt to allocate a new buffer. */ buffer = (GlhLineSeg *) malloc(nbuff * sizeof(GlhLineSeg)); if(!buffer) { errno = ENOMEM; return 1; }; /* * Copy the used segments of the old buffer to the start of the new buffer. */ nbusy = 0; for(i=0; ihash.bucket + i; GlhHashNode *hnode; for(hnode=b->lines; hnode; hnode=hnode->next) { GlhLineSeg *seg = hnode->head; hnode->head = buffer + nbusy; for( ; seg; seg=seg->next) { buffer[nbusy] = *seg; buffer[nbusy].next = seg->next ? &buffer[nbusy+1] : NULL; nbusy++; }; }; }; /* * Make a list of the new buffer's unused segments. */ for(i=nbusy; ibuffer); /* * Install the new buffer. */ glh->buffer = buffer; glh->nbuff = nbuff; glh->nbusy = nbusy; glh->nfree = nbuff - nbusy; glh->unused = glh->nfree > 0 ? (buffer + nbusy) : NULL; }; }; return 0; } /*....................................................................... * Set an upper limit to the number of lines that can be recorded in the * history list, or remove a previously specified limit. * * Input: * glh GlHistory * The input-line history maintenance object. * max_lines int The maximum number of lines to allow, or -1 to * cancel a previous limit and allow as many lines * as will fit in the current history buffer size. */ void _glh_limit_history(GlHistory *glh, int max_lines) { if(!glh) return; /* * Apply a new limit? */ if(max_lines >= 0 && max_lines != glh->max_lines) { /* * Count successively older lines until we reach the start of the * list, or until we have seen max_lines lines (at which point 'node' * will be line number max_lines+1). */ int nline = 0; GlhLineNode *node; for(node=glh->list.tail; node && ++nline <= max_lines; node=node->prev) ; /* * Discard any lines that exceed the limit. */ if(node) { GlhLineNode *oldest = node->next; /* The oldest line to be kept */ /* * Delete nodes from the head of the list until we reach the node that * is to be kept. */ while(glh->list.head && glh->list.head != oldest) _glh_discard_line(glh, glh->list.head); }; }; /* * Record the new limit. */ glh->max_lines = max_lines; return; } /*....................................................................... * Discard either all history, or the history associated with the current * history group. * * Input: * glh GlHistory * The input-line history maintenance object. * all_groups int If true, clear all of the history. If false, * clear only the stored lines associated with the * currently selected history group. */ void _glh_clear_history(GlHistory *glh, int all_groups) { int i; /* * Check the arguments. */ if(!glh) return; /* * Cancel any ongoing search. */ (void) _glh_cancel_search(glh); /* * Delete all history lines regardless of group? */ if(all_groups) { /* * Claer the time-ordered list of lines. */ _rst_FreeList(glh->list.node_mem); glh->list.head = glh->list.tail = NULL; glh->nline = 0; glh->id_node = NULL; /* * Clear the hash table. */ for(i=0; ihash.bucket[i].lines = NULL; _rst_FreeList(glh->hash.node_mem); /* * Move all line segment nodes back onto the list of unused segments. */ if(glh->buffer) { glh->unused = glh->buffer; for(i=0; inbuff-1; i++) { GlhLineSeg *seg = glh->unused + i; seg->next = seg + 1; }; glh->unused[i].next = NULL; glh->nfree = glh->nbuff; glh->nbusy = 0; } else { glh->unused = NULL; glh->nbusy = glh->nfree = 0; }; /* * Just delete lines of the current group? */ } else { GlhLineNode *node; /* The line node being checked */ GlhLineNode *next; /* The line node that follows 'node' */ /* * Search out and delete the line nodes of the current group. */ for(node=glh->list.head; node; node=next) { /* * Keep a record of the following node before we delete the current * node. */ next = node->next; /* * Discard this node? */ if(node->group == glh->group) _glh_discard_line(glh, node); }; }; return; } /*....................................................................... * Temporarily enable or disable the history list. * * Input: * glh GlHistory * The input-line history maintenance object. * enable int If true, turn on the history mechanism. If * false, disable it. */ void _glh_toggle_history(GlHistory *glh, int enable) { if(glh) glh->enable = enable; } /*....................................................................... * Discard a given archived input line. * * Input: * glh GlHistory * The history container object. * node GlhLineNode * The line to be discarded, specified via its * entry in the time-ordered list of historical * input lines. */ static void _glh_discard_line(GlHistory *glh, GlhLineNode *node) { /* * Remove the node from the linked list. */ if(node->prev) node->prev->next = node->next; else glh->list.head = node->next; if(node->next) node->next->prev = node->prev; else glh->list.tail = node->prev; /* * If we are deleting the node that is marked as the start point of the * last ID search, remove the cached starting point. */ if(node == glh->id_node) glh->id_node = NULL; /* * If we are deleting the node that is marked as the start point of the * next prefix search, cancel the search. */ if(node == glh->recall) _glh_cancel_search(glh); /* * Delete our copy of the line. */ node->line = _glh_discard_copy(glh, node->line); /* * Return the node to the freelist. */ (void) _del_FreeListNode(glh->list.node_mem, node); /* * Record the removal of a line from the list. */ glh->nline--; return; } /*....................................................................... * Lookup the details of a given history line, given its id. * * Input: * glh GlHistory * The input-line history maintenance object. * id GlLineID The sequential number of the line. * Input/Output: * line const char ** A pointer to a copy of the history line will be * assigned to *line. Beware that this pointer may * be invalidated by the next call to any public * history function. * group unsigned * The group membership of the line will be assigned * to *group. * timestamp time_t * The timestamp of the line will be assigned to * *timestamp. * Output: * return int 0 - The requested line wasn't found. * 1 - The line was found. */ int _glh_lookup_history(GlHistory *glh, GlhLineID id, const char **line, unsigned *group, time_t *timestamp) { GlhLineNode *node; /* The located line location node */ /* * Check the arguments. */ if(!glh) return 0; /* * Search for the line that has the specified ID. */ node = _glh_find_id(glh, id); /* * Not found? */ if(!node) return 0; /* * Has the history line been requested? */ if(line) { /* * If necessary, reallocate the lookup buffer to accomodate the size of * a copy of the located line. */ if(node->line->len + 1 > glh->lbuf_dim) { int lbuf_dim = node->line->len + 1; char *lbuf = realloc(glh->lbuf, lbuf_dim); if(!lbuf) { errno = ENOMEM; return 0; }; glh->lbuf_dim = lbuf_dim; glh->lbuf = lbuf; }; /* * Copy the history line into the lookup buffer. */ _glh_return_line(node->line, glh->lbuf, glh->lbuf_dim); /* * Assign the lookup buffer as the returned line pointer. */ *line = glh->lbuf; }; /* * Does the caller want to know the group of the line? */ if(group) *group = node->group; /* * Does the caller want to know the timestamp of the line? */ if(timestamp) *timestamp = node->timestamp; return 1; } /*....................................................................... * Lookup a node in the history list by its ID. * * Input: * glh GlHistory * The input-line history maintenance object. * id GlhLineID The ID of the line to be returned. * Output: * return GlhLIneNode * The located node, or NULL if not found. */ static GlhLineNode *_glh_find_id(GlHistory *glh, GlhLineID id) { GlhLineNode *node; /* The node being checked */ /* * Is history enabled? */ if(!glh->enable || !glh->list.head) return NULL; /* * If possible, start at the end point of the last ID search. * Otherwise start from the head of the list. */ node = glh->id_node; if(!node) node = glh->list.head; /* * Search forwards from 'node'? */ if(node->id < id) { while(node && node->id != id) node = node->next; glh->id_node = node ? node : glh->list.tail; /* * Search backwards from 'node'? */ } else { while(node && node->id != id) node = node->prev; glh->id_node = node ? node : glh->list.head; }; /* * Return the located node (this will be NULL if the ID wasn't found). */ return node; } /*....................................................................... * Query the state of the history list. Note that any of the input/output * pointers can be specified as NULL. * * Input: * glh GlHistory * The input-line history maintenance object. * Input/Output: * enabled int * If history is enabled, *enabled will be * set to 1. Otherwise it will be assigned 0. * group unsigned * The current history group ID will be assigned * to *group. * max_lines int * The currently requested limit on the number * of history lines in the list, or -1 if * unlimited. */ void _glh_state_of_history(GlHistory *glh, int *enabled, unsigned *group, int *max_lines) { if(glh) { if(enabled) *enabled = glh->enable; if(group) *group = glh->group; if(max_lines) *max_lines = glh->max_lines; }; } /*....................................................................... * Get the range of lines in the history buffer. * * Input: * glh GlHistory * The input-line history maintenance object. * Input/Output: * oldest unsigned long * The sequential entry number of the oldest * line in the history list will be assigned * to *oldest, unless there are no lines, in * which case 0 will be assigned. * newest unsigned long * The sequential entry number of the newest * line in the history list will be assigned * to *newest, unless there are no lines, in * which case 0 will be assigned. * nlines int * The number of lines currently in the history * list. */ void _glh_range_of_history(GlHistory *glh, unsigned long *oldest, unsigned long *newest, int *nlines) { if(glh) { if(oldest) *oldest = glh->list.head ? glh->list.head->id : 0; if(newest) *newest = glh->list.tail ? glh->list.tail->id : 0; if(nlines) *nlines = glh->nline; }; } /*....................................................................... * Return the size of the history buffer and the amount of the * buffer that is currently in use. * * Input: * glh GlHistory * The input-line history maintenance object. * Input/Output: * buff_size size_t * The size of the history buffer (bytes). * buff_used size_t * The amount of the history buffer that * is currently occupied (bytes). */ void _glh_size_of_history(GlHistory *glh, size_t *buff_size, size_t *buff_used) { if(glh) { if(buff_size) *buff_size = (glh->nbusy + glh->nfree) * GLH_SEG_SIZE; /* * Determine the amount of buffer space that is currently occupied. */ if(buff_used) *buff_used = glh->nbusy * GLH_SEG_SIZE; }; } /*....................................................................... * Return extra information (ie. in addition to that provided by errno) * about the last error to occur in any of the public functions of this * module. * * Input: * glh GlHistory * The container of the history list. * Output: * return const char * A pointer to the internal buffer in which * the error message is temporarily stored. */ const char *_glh_last_error(GlHistory *glh) { return glh ? _err_get_msg(glh->err) : "NULL GlHistory argument"; } /*....................................................................... * Unless already stored, store a copy of the line in the history buffer, * then return a reference-counted hash-node pointer to this copy. * * Input: * glh GlHistory * The history maintenance buffer. * line const char * The history line to be recorded. * n size_t The length of the string, excluding any '\0' * terminator. * Output: * return GlhHashNode * The hash-node containing the stored line, or * NULL on error. */ static GlhHashNode *_glh_acquire_copy(GlHistory *glh, const char *line, size_t n) { GlhHashBucket *bucket; /* The hash-table bucket of the line */ GlhHashNode *hnode; /* The hash-table node of the line */ int i; /* * In which bucket should the line be recorded? */ bucket = glh_find_bucket(glh, line, n); /* * Is the line already recorded there? */ hnode = glh_find_hash_node(bucket, line, n); /* * If the line isn't recorded in the buffer yet, make room for it. */ if(!hnode) { GlhLineSeg *seg; /* A line segment */ int offset; /* An offset into line[] */ /* * How many string segments will be needed to record the new line, * including space for a '\0' terminator? */ int nseg = ((n+1) + GLH_SEG_SIZE-1) / GLH_SEG_SIZE; /* * Discard the oldest history lines in the buffer until at least * 'nseg' segments have been freed up, or until we run out of buffer * space. */ while(glh->nfree < nseg && glh->nbusy > 0) _glh_discard_line(glh, glh->list.head); /* * If the buffer is smaller than the new line, don't attempt to truncate * it to fit. Simply don't archive it. */ if(glh->nfree < nseg) return NULL; /* * Record the line in the first 'nseg' segments of the list of unused segments. */ offset = 0; for(i=0,seg=glh->unused; inext, offset+=GLH_SEG_SIZE) memcpy(seg->s, line + offset, GLH_SEG_SIZE); memcpy(seg->s, line + offset, n-offset); seg->s[n-offset] = '\0'; /* * Create a new hash-node for the line. */ hnode = (GlhHashNode *) _new_FreeListNode(glh->hash.node_mem); if(!hnode) return NULL; /* * Move the copy of the line from the list of unused segments to * the hash node. */ hnode->head = glh->unused; glh->unused = seg->next; seg->next = NULL; glh->nbusy += nseg; glh->nfree -= nseg; /* * Prepend the new hash node to the list within the associated bucket. */ hnode->next = bucket->lines; bucket->lines = hnode; /* * Initialize the rest of the members of the hash node. */ hnode->len = n; hnode->reported = 0; hnode->used = 0; hnode->bucket = bucket; }; /* * Increment the reference count of the line. */ hnode->used++; return hnode; } /*....................................................................... * Decrement the reference count of the history line of a given hash-node, * and if the count reaches zero, delete both the hash-node and the * buffered copy of the line. * * Input: * glh GlHistory * The history container object. * hnode GlhHashNode * The node to be removed. * Output: * return GlhHashNode * The deleted hash-node (ie. NULL). */ static GlhHashNode *_glh_discard_copy(GlHistory *glh, GlhHashNode *hnode) { if(hnode) { GlhHashBucket *bucket = hnode->bucket; /* * If decrementing the reference count of the hash-node doesn't reduce * the reference count to zero, then the line is still in use in another * object, so don't delete it yet. Return NULL to indicate that the caller's * access to the hash-node copy has been deleted. */ if(--hnode->used >= 1) return NULL; /* * Remove the hash-node from the list in its parent bucket. */ if(bucket->lines == hnode) { bucket->lines = hnode->next; } else { GlhHashNode *prev; /* The node which precedes hnode in the bucket */ for(prev=bucket->lines; prev && prev->next != hnode; prev=prev->next) ; if(prev) prev->next = hnode->next; }; hnode->next = NULL; /* * Return the line segments of the hash-node to the list of unused segments. */ if(hnode->head) { GlhLineSeg *tail; /* The last node in the list of line segments */ int nseg; /* The number of segments being discarded */ /* * Get the last node of the list of line segments referenced in the hash-node, * while counting the number of line segments used. */ for(nseg=1,tail=hnode->head; tail->next; nseg++,tail=tail->next) ; /* * Prepend the list of line segments used by the hash node to the * list of unused line segments. */ tail->next = glh->unused; glh->unused = hnode->head; glh->nbusy -= nseg; glh->nfree += nseg; }; /* * Return the container of the hash-node to the freelist. */ hnode = (GlhHashNode *) _del_FreeListNode(glh->hash.node_mem, hnode); }; return NULL; } /*....................................................................... * Private function to locate the hash bucket associated with a given * history line. * * This uses a hash-function described in the dragon-book * ("Compilers - Principles, Techniques and Tools", by Aho, Sethi and * Ullman; pub. Adison Wesley) page 435. * * Input: * glh GlHistory * The history container object. * line const char * The historical line to look up. * n size_t The length of the line in line[], excluding * any '\0' terminator. * Output: * return GlhHashBucket * The located hash-bucket. */ static GlhHashBucket *glh_find_bucket(GlHistory *glh, const char *line, size_t n) { unsigned long h = 0L; int i; for(i=0; ihash.bucket + (h % GLH_HASH_SIZE); } /*....................................................................... * Find a given history line within a given hash-table bucket. * * Input: * bucket GlhHashBucket * The hash-table bucket in which to search. * line const char * The historical line to lookup. * n size_t The length of the line in line[], excluding * any '\0' terminator. * Output: * return GlhHashNode * The hash-table entry of the line, or NULL * if not found. */ static GlhHashNode *glh_find_hash_node(GlhHashBucket *bucket, const char *line, size_t n) { GlhHashNode *node; /* A node in the list of lines in the bucket */ /* * Compare each of the lines in the list of lines, against 'line'. */ for(node=bucket->lines; node; node=node->next) { if(_glh_is_line(node, line, n)) return node; }; return NULL; } /*....................................................................... * Return non-zero if a given string is equal to a given segmented line * node. * * Input: * hash GlhHashNode * The hash-table entry of the line. * line const char * The string to be compared to the segmented * line. * n size_t The length of the line in line[], excluding * any '\0' terminator. * Output: * return int 0 - The lines differ. * 1 - The lines are the same. */ static int _glh_is_line(GlhHashNode *hash, const char *line, size_t n) { GlhLineSeg *seg; /* A node in the list of line segments */ int i; /* * Do the two lines have the same length? */ if(n != hash->len) return 0; /* * Compare the characters of the segmented and unsegmented versions * of the line. */ for(seg=hash->head; n>0 && seg; seg=seg->next) { const char *s = seg->s; for(i=0; n>0 && ilen > line->len) return 0; /* * Compare the line to the prefix. */ while(pstr.c != '\0' && pstr.c == lstr.c) { glh_step_stream(&lstr); glh_step_stream(&pstr); }; /* * Did we reach the end of the prefix string before finding * any differences? */ return pstr.c == '\0'; } /*....................................................................... * Copy a given history line into a specified output string. * * Input: * hash GlhHashNode The hash-table entry of the history line to * be copied. * line char * A copy of the history line. * dim size_t The allocated dimension of the line buffer. */ static void _glh_return_line(GlhHashNode *hash, char *line, size_t dim) { GlhLineSeg *seg; /* A node in the list of line segments */ int i; for(seg=hash->head; dim>0 && seg; seg=seg->next) { const char *s = seg->s; for(i=0; dim>0 && irecall && glh->recall == glh->list.tail && !_glh_is_line(glh->recall->line, line, strlen(line))) { _glh_cancel_search(glh); }; /* * If this is the first line recall of a new recall session, save the * current line for potential recall later, and mark it as the last * line recalled. */ if(!glh->recall) { if(_glh_add_history(glh, line, 1)) return 1; glh->recall = glh->list.tail; /* * The above call to _glh_add_history() will have incremented the line * sequence number, after adding the line. Since we only want this to * to be incremented for permanently entered lines, decrement it again. */ glh->seq--; }; return 0; } /*....................................................................... * Return non-zero if a history search session is currently in progress. * * Input: * glh GlHistory * The input-line history maintenance object. * Output: * return int 0 - No search is currently in progress. * 1 - A search is in progress. */ int _glh_search_active(GlHistory *glh) { return glh && glh->recall; } /*....................................................................... * Initialize a character iterator object to point to the start of a * given history line. The first character of the line will be placed * in str->c, and subsequent characters can be placed there by calling * glh_strep_stream(). * * Input: * str GlhLineStream * The iterator object to be initialized. * line GlhHashNode * The history line to be iterated over (a * NULL value here, is interpretted as an * empty string by glh_step_stream()). */ static void glh_init_stream(GlhLineStream *str, GlhHashNode *line) { str->seg = line ? line->head : NULL; str->posn = 0; str->c = str->seg ? str->seg->s[0] : '\0'; } /*....................................................................... * Copy the next unread character in the line being iterated, in str->c. * Once the end of the history line has been reached, all futher calls * set str->c to '\0'. * * Input: * str GlhLineStream * The history-line iterator to read from. */ static void glh_step_stream(GlhLineStream *str) { /* * Get the character from the current iterator position within the line. */ str->c = str->seg ? str->seg->s[str->posn] : '\0'; /* * Unless we have reached the end of the string, move the iterator * to the position of the next character in the line. */ if(str->c != '\0' && ++str->posn >= GLH_SEG_SIZE) { str->posn = 0; str->seg = str->seg->next; }; } /*....................................................................... * Return non-zero if the specified search prefix contains any glob * wildcard characters. * * Input: * prefix GlhHashNode * The search prefix. * Output: * return int 0 - The prefix doesn't contain any globbing * characters. * 1 - The prefix contains at least one * globbing character. */ static int glh_contains_glob(GlhHashNode *prefix) { GlhLineStream pstr; /* The stream that is used to traverse 'prefix' */ /* * Wrap a stream iterator around the prefix, so that we can traverse it * without worrying about line-segmentation. */ glh_init_stream(&pstr, prefix); /* * Search for unescaped wildcard characters. */ while(pstr.c != '\0') { switch(pstr.c) { case '\\': /* Skip escaped characters */ glh_step_stream(&pstr); break; case '*': case '?': case '[': /* A wildcard character? */ return 1; break; }; glh_step_stream(&pstr); }; /* * No wildcard characters were found. */ return 0; } /*....................................................................... * Return non-zero if the history line matches a search prefix containing * a glob pattern. * * Input: * lstr GlhLineStream * The iterator stream being used to traverse * the history line that is being matched. * pstr GlhLineStream * The iterator stream being used to traverse * the pattern. * Output: * return int 0 - Doesn't match. * 1 - The line matches the pattern. */ static int glh_line_matches_glob(GlhLineStream *lstr, GlhLineStream *pstr) { /* * Match each character of the pattern until we reach the end of the * pattern. */ while(pstr->c != '\0') { /* * Handle the next character of the pattern. */ switch(pstr->c) { /* * A match zero-or-more characters wildcard operator. */ case '*': /* * Skip the '*' character in the pattern. */ glh_step_stream(pstr); /* * If the pattern ends with the '*' wildcard, then the * rest of the line matches this. */ if(pstr->c == '\0') return 1; /* * Using the wildcard to match successively longer sections of * the remaining characters of the line, attempt to match * the tail of the line against the tail of the pattern. */ while(lstr->c) { GlhLineStream old_lstr = *lstr; GlhLineStream old_pstr = *pstr; if(glh_line_matches_glob(lstr, pstr)) return 1; /* * Restore the line and pattern iterators for a new try. */ *lstr = old_lstr; *pstr = old_pstr; /* * Prepare to try again, one character further into the line. */ glh_step_stream(lstr); }; return 0; /* The pattern following the '*' didn't match */ break; /* * A match-one-character wildcard operator. */ case '?': /* * If there is a character to be matched, skip it and advance the * pattern pointer. */ if(lstr->c) { glh_step_stream(lstr); glh_step_stream(pstr); /* * If we hit the end of the line, there is no character * matching the operator, so the pattern doesn't match. */ } else { return 0; }; break; /* * A character range operator, with the character ranges enclosed * in matching square brackets. */ case '[': glh_step_stream(pstr); /* Skip the '[' character */ if(!lstr->c || !glh_matches_range(lstr->c, pstr)) return 0; glh_step_stream(lstr); /* Skip the character that matched */ break; /* * A backslash in the pattern prevents the following character as * being seen as a special character. */ case '\\': glh_step_stream(pstr); /* Skip the backslash */ /* Note fallthrough to default */ /* * A normal character to be matched explicitly. */ default: if(lstr->c == pstr->c) { glh_step_stream(lstr); glh_step_stream(pstr); } else { return 0; }; break; }; }; /* * To get here, pattern must have been exhausted. The line only * matches the pattern if the line as also been exhausted. */ return pstr->c == '\0' && lstr->c == '\0'; } /*....................................................................... * Match a character range expression terminated by an unescaped close * square bracket. * * Input: * c char The character to be matched with the range * pattern. * pstr GlhLineStream * The iterator stream being used to traverse * the pattern. * Output: * return int 0 - Doesn't match. * 1 - The character matched. */ static int glh_matches_range(char c, GlhLineStream *pstr) { int invert = 0; /* True to invert the sense of the match */ int matched = 0; /* True if the character matched the pattern */ char lastc = '\0'; /* The previous character in the pattern */ /* * If the first character is a caret, the sense of the match is * inverted and only if the character isn't one of those in the * range, do we say that it matches. */ if(pstr->c == '^') { glh_step_stream(pstr); invert = 1; }; /* * The hyphen is only a special character when it follows the first * character of the range (not including the caret). */ if(pstr->c == '-') { glh_step_stream(pstr); if(c == '-') matched = 1; /* * Skip other leading '-' characters since they make no sense. */ while(pstr->c == '-') glh_step_stream(pstr); }; /* * The hyphen is only a special character when it follows the first * character of the range (not including the caret or a hyphen). */ if(pstr->c == ']') { glh_step_stream(pstr); if(c == ']') matched = 1; }; /* * Having dealt with the characters that have special meanings at * the beginning of a character range expression, see if the * character matches any of the remaining characters of the range, * up until a terminating ']' character is seen. */ while(!matched && pstr->c && pstr->c != ']') { /* * Is this a range of characters signaled by the two end characters * separated by a hyphen? */ if(pstr->c == '-') { glh_step_stream(pstr); /* Skip the hyphen */ if(pstr->c != ']') { if(c >= lastc && c <= pstr->c) matched = 1; }; /* * A normal character to be compared directly. */ } else if(pstr->c == c) { matched = 1; }; /* * Record and skip the character that we just processed. */ lastc = pstr->c; if(pstr->c != ']') glh_step_stream(pstr); }; /* * Find the terminating ']'. */ while(pstr->c && pstr->c != ']') glh_step_stream(pstr); /* * Did we find a terminating ']'? */ if(pstr->c == ']') { /* * Skip the terminating ']'. */ glh_step_stream(pstr); /* * If the pattern started with a caret, invert the sense of the match. */ if(invert) matched = !matched; /* * If the pattern didn't end with a ']', then it doesn't match, * regardless of the value of the required sense of the match. */ } else { matched = 0; }; return matched; } yuma123_2.14/libtecla/cplfile.h0000664000175000017500000001060714770023131016475 0ustar vladimirvladimir#ifndef cplfile_h #define cplfile_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ typedef struct CompleteFile CompleteFile; /* * Create a file-completion resource object. */ CompleteFile *_new_CompleteFile(void); /* * Delete a file-completion resource object. */ CompleteFile *_del_CompleteFile(CompleteFile *cf); /*....................................................................... * Complete the string between path[0] and path[len-1] as a pathname, * leaving the last component uncompleted if it is potentially ambiguous, * and returning an array of possible completions. Note that the returned * container belongs to the 'cf' object and its contents will change on * subsequent calls to this function. * * Input: * cpl WordCompletion * The object in which to record the completions. * cf CompleteFile * The filename-completion resource object. * line const char * The string containing the incomplete filename. * word_start int The index of the first character in line[] * of the incomplete filename. * word_end int The index of the character in line[] that * follows the last character of the incomplete * filename. * escaped int If true, backslashes in path[] are * interpreted as escaping the characters * that follow them, and any spaces, tabs, * backslashes, or wildcard characters in the * returned suffixes will be similarly be escaped. * If false, backslashes will be interpreted as * literal parts of the file name, and no * backslashes will be added to the returned * suffixes. * check_fn CplCheckFn * If not zero, this argument specifies a * function to call to ask whether a given * file should be included in the list * of completions. * check_data void * Anonymous data to be passed to check_fn(). * Output: * return int 0 - OK. * 1 - Error. A description of the error can be * acquired by calling cf_last_error(cf). */ int _cf_complete_file(WordCompletion *cpl, CompleteFile *cf, const char *line, int word_start, int word_end, int escaped, CplCheckFn *check_fn, void *check_data); /*....................................................................... * Return a description of the error that occurred on the last call to * cf_complete_file(). * * Input: * cf CompleteFile * The path-completion resource object. * Output: * return char * The description of the last error. */ const char *_cf_last_error(CompleteFile *cf); #endif yuma123_2.14/libtecla/freelist.h0000664000175000017500000000620114770023131016667 0ustar vladimirvladimir#ifndef freelist_h #define freelist_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * This module provides a memory allocation scheme that helps to * prevent memory fragmentation by allocating large blocks of * fixed sized objects and forming them into a free-list for * subsequent allocations. The free-list is expanded as needed. */ typedef struct FreeList FreeList; /* * Allocate a new free-list from blocks of 'blocking_factor' objects of size * node_size. The node_size argument should be determined by applying * the sizeof() operator to the object type that you intend to allocate from * the freelist. */ FreeList *_new_FreeList(size_t node_size, unsigned blocking_factor); /* * If it is known that none of the nodes currently allocated from * a freelist are still in use, the following function can be called * to return all nodes to the freelist without the overhead of * having to call del_FreeListNode() for every allocated node. The * nodes of the freelist can then be reused by future callers to * new_FreeListNode(). */ void _rst_FreeList(FreeList *fl); /* * Delete a free-list. */ FreeList *_del_FreeList(FreeList *fl, int force); /* * Determine the number of nodes that are currently in use. */ long _busy_FreeListNodes(FreeList *fl); /* * Query the number of allocated nodes in the freelist which are * currently unused. */ long _idle_FreeListNodes(FreeList *fl); /* * Allocate a new object from a free-list. */ void *_new_FreeListNode(FreeList *fl); /* * Return an object to the free-list that it was allocated from. */ void *_del_FreeListNode(FreeList *fl, void *object); #endif yuma123_2.14/libtecla/chrqueue.c0000664000175000017500000003327114770023131016675 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include "ioutil.h" #include "chrqueue.h" #include "freelist.h" #include "errmsg.h" /* * Set the number of bytes allocated to each node of the list of * character buffers. This facility is designed principally as * an expandible I/O output buffer, so use the stdio buffer size * where available. */ #ifdef BUFSIZ #define GL_CQ_SIZE BUFSIZ #else #define GL_CQ_SIZE 512 #endif /* * The queue is contained in a list of fixed sized buffers. New nodes * are appended to this list as needed to accomodate newly added bytes. * Old nodes at the head of the list are removed as they are emptied. */ typedef struct CqCharBuff CqCharBuff; struct CqCharBuff { CqCharBuff *next; /* The next node in the list of buffers */ char bytes[GL_CQ_SIZE]; /* The fixed size buffer of this node */ }; /* * Define the structure that is used to contain a list of character * buffers. */ struct GlCharQueue { ErrMsg *err; /* A buffer in which to record error messages */ FreeList *bufmem; /* A free-list of CqCharBuff structures */ struct { CqCharBuff *head; /* The head of the list of output buffers */ CqCharBuff *tail; /* The tail of the list of output buffers */ } buffers; int nflush; /* The total number of characters that have been */ /* flushed from the start of the queue since */ /* _glq_empty_queue() was last called. */ int ntotal; /* The total number of characters that have been */ /* appended to the queue since _glq_empty_queue() */ /* was last called. */ }; /*....................................................................... * Create a new GlCharQueue object. * * Output: * return GlCharQueue * The new object, or NULL on error. */ GlCharQueue *_new_GlCharQueue(void) { GlCharQueue *cq; /* The object to be returned */ /* * Allocate the container. */ cq = malloc(sizeof(GlCharQueue)); if(!cq) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_GlCharQueue(). */ cq->err = NULL; cq->bufmem = NULL; cq->buffers.head = NULL; cq->buffers.tail = NULL; cq->nflush = cq->ntotal = 0; /* * Allocate a place to record error messages. */ cq->err = _new_ErrMsg(); if(!cq->err) return _del_GlCharQueue(cq); /* * Allocate the freelist of CqCharBuff structures. */ cq->bufmem = _new_FreeList(sizeof(CqCharBuff), 1); if(!cq->bufmem) return _del_GlCharQueue(cq); return cq; } /*....................................................................... * Delete a GlCharQueue object. * * Input: * cq GlCharQueue * The object to be deleted. * Output: * return GlCharQueue * The deleted object (always NULL). */ GlCharQueue *_del_GlCharQueue(GlCharQueue *cq) { if(cq) { cq->err = _del_ErrMsg(cq->err); cq->bufmem = _del_FreeList(cq->bufmem, 1); free(cq); }; return NULL; } /*....................................................................... * Append an array of n characters to a character queue. * * Input: * cq GlCharQueue * The queue to append to. * chars const char * The array of n characters to be appended. * n int The number of characters in chars[]. * write_fn GL_WRITE_FN * The function to call to output characters, * or 0 to simply discard the contents of the * queue. This will be called whenever the * buffer becomes full. If it fails to release * any space, the buffer will be extended. * data void * Anonymous data to pass to write_fn(). * Output: * return int The number of characters successfully * appended. This will only be < n on error. */ int _glq_append_chars(GlCharQueue *cq, const char *chars, int n, GlWriteFn *write_fn, void *data) { int ndone = 0; /* The number of characters appended so far */ /* * Check the arguments. */ if(!cq || !chars) { errno = EINVAL; return 0; }; /* * The appended characters may have to be split between multiple * buffers, so loop for each buffer. */ while(ndone < n) { int ntodo; /* The number of characters remaining to be appended */ int nleft; /* The amount of space remaining in cq->buffers.tail */ int nnew; /* The number of characters to append to cq->buffers.tail */ /* * Compute the offset at which the next character should be written * into the tail buffer segment. */ int boff = cq->ntotal % GL_CQ_SIZE; /* * Since we don't allocate a new buffer until we have at least one * character to write into it, if boff is 0 at this point, it means * that we hit the end of the tail buffer segment on the last append, * so we need to allocate a new one. * * If allocating this new node will require a call to malloc(), as * opposed to using a currently unused node in the freelist, first try * flushing the current contents of the buffer to the terminal. When * write_fn() uses blocking I/O, this stops the buffer size ever getting * bigger than a single buffer node. When it is non-blocking, it helps * to keep the amount of memory, but it isn't gauranteed to do so. */ if(boff == 0 && _idle_FreeListNodes(cq->bufmem) == 0) { switch(_glq_flush_queue(cq, write_fn, data)) { case GLQ_FLUSH_DONE: break; case GLQ_FLUSH_AGAIN: errno = 0; /* Don't confuse the caller */ break; default: return ndone; /* Error */ }; boff = cq->ntotal % GL_CQ_SIZE; }; /* * Since we don't allocate a new buffer until we have at least one * character to write into it, if boff is 0 at this point, it means * that we hit the end of the tail buffer segment on the last append, * so we need to allocate a new one. */ if(boff == 0) { /* * Allocate the new node. */ CqCharBuff *node = (CqCharBuff *) _new_FreeListNode(cq->bufmem); if(!node) { _err_record_msg(cq->err, "Insufficient memory to buffer output.", END_ERR_MSG); return ndone; }; /* * Initialize the node. */ node->next = NULL; /* * Append the new node to the tail of the list. */ if(cq->buffers.tail) cq->buffers.tail->next = node; else cq->buffers.head = node; cq->buffers.tail = node; }; /* * How much room is there for new characters in the current tail node? */ nleft = GL_CQ_SIZE - boff; /* * How many characters remain to be appended? */ ntodo = n - ndone; /* * How many characters should we append to the current tail node? */ nnew = nleft < ntodo ? nleft : ntodo; /* * Append the latest prefix of nnew characters. */ memcpy(cq->buffers.tail->bytes + boff, chars + ndone, nnew); cq->ntotal += nnew; ndone += nnew; }; /* * Return the count of the number of characters successfully appended. */ return ndone; } /*....................................................................... * Discard the contents of a queue of characters. * * Input: * cq GlCharQueue * The queue to clear. */ void _glq_empty_queue(GlCharQueue *cq) { if(cq) { /* * Return all list nodes to their respective free-lists. */ _rst_FreeList(cq->bufmem); /* * Mark the lists as empty. */ cq->buffers.head = cq->buffers.tail = NULL; cq->nflush = cq->ntotal = 0; }; } /*....................................................................... * Return a count of the number of characters currently in the queue. * * Input: * cq GlCharQueue * The queue of interest. * Output: * return int The number of characters in the queue. */ int _glq_char_count(GlCharQueue *cq) { return (cq && cq->buffers.head) ? (cq->ntotal - cq->nflush) : 0; } /*....................................................................... * Write as many characters as possible from the start of a character * queue via a given output callback function, removing those written * from the queue. * * Input: * cq GlCharQueue * The queue to write characters from. * write_fn GL_WRITE_FN * The function to call to output characters, * or 0 to simply discard the contents of the * queue. * data void * Anonymous data to pass to write_fn(). * Output: * return GlFlushState The status of the flush operation: * GLQ_FLUSH_DONE - The flush operation * completed successfully. * GLQ_FLUSH_AGAIN - The flush operation * couldn't be completed * on this call. Call this * function again when the * output channel can accept * further output. * GLQ_FLUSH_ERROR Unrecoverable error. */ GlqFlushState _glq_flush_queue(GlCharQueue *cq, GlWriteFn *write_fn, void *data) { /* * Check the arguments. */ if(!cq) { errno = EINVAL; return GLQ_FLUSH_ERROR; }; /* * If possible keep writing until all of the chained buffers have been * emptied and removed from the list. */ while(cq->buffers.head) { /* * Are we looking at the only node in the list? */ int is_tail = cq->buffers.head == cq->buffers.tail; /* * How many characters more than an exact multiple of the buffer-segment * size have been added to the buffer so far? */ int nmodulo = cq->ntotal % GL_CQ_SIZE; /* * How many characters of the buffer segment at the head of the list * have been used? Note that this includes any characters that have * already been flushed. Also note that if nmodulo==0, this means that * the tail buffer segment is full. The reason for this is that we * don't allocate new tail buffer segments until there is at least one * character to be added to them. */ int nhead = (!is_tail || nmodulo == 0) ? GL_CQ_SIZE : nmodulo; /* * How many characters remain to be flushed from the buffer * at the head of the list? */ int nbuff = nhead - (cq->nflush % GL_CQ_SIZE); /* * Attempt to write this number. */ int nnew = write_fn(data, cq->buffers.head->bytes + cq->nflush % GL_CQ_SIZE, nbuff); /* * Was anything written? */ if(nnew > 0) { /* * Increment the count of the number of characters that have * been flushed from the head of the queue. */ cq->nflush += nnew; /* * If we succeded in writing all of the contents of the current * buffer segment, remove it from the queue. */ if(nnew == nbuff) { /* * If we just emptied the last node left in the list, then the queue is * now empty and should be reset. */ if(is_tail) { _glq_empty_queue(cq); } else { /* * Get the node to be removed from the head of the list. */ CqCharBuff *node = cq->buffers.head; /* * Make the node that follows it the new head of the queue. */ cq->buffers.head = node->next; /* * Return it to the freelist. */ node = (CqCharBuff *) _del_FreeListNode(cq->bufmem, node); }; }; /* * If the write blocked, request that this function be called again * when space to write next becomes available. */ } else if(nnew==0) { return GLQ_FLUSH_AGAIN; /* * I/O error. */ } else { _err_record_msg(cq->err, "Error writing to terminal", END_ERR_MSG); return GLQ_FLUSH_ERROR; }; }; /* * To get here the queue must now be empty. */ return GLQ_FLUSH_DONE; } /*....................................................................... * Return extra information (ie. in addition to that provided by errno) * about the last error to occur in any of the public functions of this * module. * * Input: * cq GlCharQueue * The container of the history list. * Output: * return const char * A pointer to the internal buffer in which * the error message is temporarily stored. */ const char *_glq_last_error(GlCharQueue *cq) { return cq ? _err_get_msg(cq->err) : "NULL GlCharQueue argument"; } yuma123_2.14/libtecla/pathutil.c0000664000175000017500000003633014770023131016705 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * If file-system access is to be excluded, this module has no function, * so all of its code should be excluded. */ #ifndef WITHOUT_FILE_SYSTEM #include #include #include #include #include #include #include #include #include #include "pathutil.h" /*....................................................................... * Create a new PathName object. * * Output: * return PathName * The new object, or NULL on error. */ PathName *_new_PathName(void) { PathName *path; /* The object to be returned */ /* * Allocate the container. */ path = (PathName *) malloc(sizeof(PathName)); if(!path) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_PathName(). */ path->name = NULL; path->dim = 0; /* * Figure out the maximum length of an expanded pathname. */ path->dim = _pu_pathname_dim(); if(path->dim == 0) return _del_PathName(path); /* * Allocate the pathname buffer. */ path->name = (char *)malloc(path->dim * sizeof(char)); if(!path->name) { errno = ENOMEM; return _del_PathName(path); }; return path; } /*....................................................................... * Delete a PathName object. * * Input: * path PathName * The object to be deleted. * Output: * return PathName * The deleted object (always NULL). */ PathName *_del_PathName(PathName *path) { if(path) { if(path->name) free(path->name); free(path); }; return NULL; } /*....................................................................... * Return the pathname to a zero-length string. * * Input: * path PathName * The pathname container. * Output: * return char * The cleared pathname buffer, or NULL on error. */ char *_pn_clear_path(PathName *path) { /* * Check the arguments. */ if(!path) { errno = EINVAL; return NULL; }; path->name[0] = '\0'; return path->name; } /*....................................................................... * Append a string to a pathname, increasing the size of the pathname * buffer if needed. * * Input: * path PathName * The pathname container. * string const char * The string to be appended to the pathname. * Note that regardless of the slen argument, * this should be a '\0' terminated string. * slen int The maximum number of characters to append * from string[], or -1 to append the whole * string. * remove_escapes int If true, remove the backslashes that escape * spaces, tabs, backslashes etc.. * Output: * return char * The pathname string path->name[], which may * have been reallocated, or NULL if there was * insufficient memory to extend the pathname. */ char *_pn_append_to_path(PathName *path, const char *string, int slen, int remove_escapes) { int pathlen; /* The length of the pathname */ int i; /* * Check the arguments. */ if(!path || !string) { errno = EINVAL; return NULL; }; /* * Get the current length of the pathname. */ pathlen = strlen(path->name); /* * How many characters should be appended? */ if(slen < 0 || slen > strlen(string)) slen = strlen(string); /* * Resize the pathname if needed. */ if(!_pn_resize_path(path, pathlen + slen)) return NULL; /* * Append the string to the output pathname, removing any escape * characters found therein. */ if(remove_escapes) { int is_escape = 0; for(i=0; iname[pathlen++] = string[i]; }; /* * Terminate the string. */ path->name[pathlen] = '\0'; } else { /* * Append the string directly to the pathname. */ memcpy(path->name + pathlen, string, slen); path->name[pathlen + slen] = '\0'; }; return path->name; } /*....................................................................... * Prepend a string to a pathname, increasing the size of the pathname * buffer if needed. * * Input: * path PathName * The pathname container. * string const char * The string to be prepended to the pathname. * Note that regardless of the slen argument, * this should be a '\0' terminated string. * slen int The maximum number of characters to prepend * from string[], or -1 to append the whole * string. * remove_escapes int If true, remove the backslashes that escape * spaces, tabs, backslashes etc.. * Output: * return char * The pathname string path->name[], which may * have been reallocated, or NULL if there was * insufficient memory to extend the pathname. */ char *_pn_prepend_to_path(PathName *path, const char *string, int slen, int remove_escapes) { int pathlen; /* The length of the pathname */ int shift; /* The number of characters to shift the suffix by */ int i,j; /* * Check the arguments. */ if(!path || !string) { errno = EINVAL; return NULL; }; /* * Get the current length of the pathname. */ pathlen = strlen(path->name); /* * How many characters should be appended? */ if(slen < 0 || slen > strlen(string)) slen = strlen(string); /* * Work out how far we need to shift the original path string to make * way for the new prefix. When removing escape characters, we need * final length of the new prefix, after unescaped backslashes have * been removed. */ if(remove_escapes) { int is_escape = 0; for(shift=0,i=0; iname + shift, path->name, pathlen+1); /* * Copy the new prefix into the vacated space at the beginning of the * output pathname, removing any escape characters if needed. */ if(remove_escapes) { int is_escape = 0; for(i=j=0; iname[j++] = string[i]; }; } else { memcpy(path->name, string, slen); }; return path->name; } /*....................................................................... * If needed reallocate a given pathname buffer to allow a string of * a given length to be stored in it. * * Input: * path PathName * The pathname container object. * length size_t The required length of the pathname buffer, * not including the terminating '\0'. * Output: * return char * The pathname buffer, or NULL if there was * insufficient memory. */ char *_pn_resize_path(PathName *path, size_t length) { /* * Check the arguments. */ if(!path) { errno = EINVAL; return NULL; }; /* * If the pathname buffer isn't large enough to accomodate a string * of the specified length, attempt to reallocate it with the new * size, plus space for a terminating '\0'. Also add a bit of * head room to prevent too many reallocations if the initial length * turned out to be very optimistic. */ if(length + 1 > path->dim) { size_t dim = length + 1 + PN_PATHNAME_INC; char *name = (char *) realloc(path->name, dim); if(!name) return NULL; path->name = name; path->dim = dim; }; return path->name; } /*....................................................................... * Estimate the largest amount of space needed to store a pathname. * * Output: * return size_t The number of bytes needed, including space for the * terminating '\0'. */ size_t _pu_pathname_dim(void) { int maxlen; /* The return value excluding space for the '\0' */ /* * If the POSIX PATH_MAX macro is defined in limits.h, use it. */ #ifdef PATH_MAX maxlen = PATH_MAX; /* * If we have pathconf, use it. */ #elif defined(_PC_PATH_MAX) errno = 0; maxlen = pathconf(FS_ROOT_DIR, _PC_PATH_MAX); if(maxlen <= 0 || errno) maxlen = MAX_PATHLEN_FALLBACK; /* * None of the above approaches worked, so substitute our fallback * guess. */ #else maxlen = MAX_PATHLEN_FALLBACK; #endif /* * Return the amount of space needed to accomodate a pathname plus * a terminating '\0'. */ return maxlen + 1; } /*....................................................................... * Return non-zero if the specified path name refers to a directory. * * Input: * pathname const char * The path to test. * Output: * return int 0 - Not a directory. * 1 - pathname[] refers to a directory. */ int _pu_path_is_dir(const char *pathname) { struct stat statbuf; /* The file-statistics return buffer */ /* * Look up the file attributes. */ if(stat(pathname, &statbuf) < 0) return 0; /* * Is the file a directory? */ return S_ISDIR(statbuf.st_mode) != 0; } /*....................................................................... * Return non-zero if the specified path name refers to a regular file. * * Input: * pathname const char * The path to test. * Output: * return int 0 - Not a regular file. * 1 - pathname[] refers to a regular file. */ int _pu_path_is_file(const char *pathname) { struct stat statbuf; /* The file-statistics return buffer */ /* * Look up the file attributes. */ if(stat(pathname, &statbuf) < 0) return 0; /* * Is the file a regular file? */ return S_ISREG(statbuf.st_mode) != 0; } /*....................................................................... * Return non-zero if the specified path name refers to an executable. * * Input: * pathname const char * The path to test. * Output: * return int 0 - Not an executable file. * 1 - pathname[] refers to an executable file. */ int _pu_path_is_exe(const char *pathname) { struct stat statbuf; /* The file-statistics return buffer */ /* * Look up the file attributes. */ if(stat(pathname, &statbuf) < 0) return 0; /* * Is the file a regular file which is executable by the current user. */ return S_ISREG(statbuf.st_mode) != 0 && (statbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) && access(pathname, X_OK) == 0; } /*....................................................................... * Search backwards for the potential start of a filename. This * looks backwards from the specified index in a given string, * stopping at the first unescaped space or the start of the line. * * Input: * string const char * The string to search backwards in. * back_from int The index of the first character in string[] * that follows the pathname. * Output: * return char * The pointer to the first character of * the potential pathname, or NULL on error. */ char *_pu_start_of_path(const char *string, int back_from) { int i, j; /* * Check the arguments. */ if(!string || back_from < 0) { errno = EINVAL; return NULL; }; /* * Search backwards from the specified index. */ for(i=back_from-1; i>=0; i--) { int c = string[i]; /* * Stop on unescaped spaces. */ if(isspace((int)(unsigned char)c)) { /* * The space can't be escaped if we are at the start of the line. */ if(i==0) break; /* * Find the extent of the escape characters which precedes the space. */ for(j=i-1; j>=0 && string[j]=='\\'; j--) ; /* * If there isn't an odd number of escape characters before the space, * then the space isn't escaped. */ if((i - 1 - j) % 2 == 0) break; }; }; return (char *)string + i + 1; } /*....................................................................... * Find the length of a potential filename starting from a given * point. This looks forwards from the specified index in a given string, * stopping at the first unescaped space or the end of the line. * * Input: * string const char * The string to search backwards in. * start_from int The index of the first character of the pathname * in string[]. * Output: * return char * The pointer to the character that follows * the potential pathname, or NULL on error. */ char *_pu_end_of_path(const char *string, int start_from) { int c; /* The character being examined */ int escaped = 0; /* True when the next character is escaped */ int i; /* * Check the arguments. */ if(!string || start_from < 0) { errno = EINVAL; return NULL; }; /* * Search forwards from the specified index. */ for(i=start_from; (c=string[i]) != '\0'; i++) { if(escaped) { escaped = 0; } else if(isspace(c)) { break; } else if(c == '\\') { escaped = 1; }; }; return (char *)string + i; } /*....................................................................... * Return non-zero if the specified path name refers to an existing file. * * Input: * pathname const char * The path to test. * Output: * return int 0 - The file doesn't exist. * 1 - The file does exist. */ int _pu_file_exists(const char *pathname) { struct stat statbuf; return stat(pathname, &statbuf) == 0; } #endif /* ifndef WITHOUT_FILE_SYSTEM */ yuma123_2.14/libtecla/stringrp.c0000664000175000017500000002113014770023131016713 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include "freelist.h" #include "stringrp.h" /* * StringSegment objects store lots of small strings in larger * character arrays. Since the total length of all of the strings can't * be known in advance, an extensible list of large character arrays, * called string-segments are used. */ typedef struct StringSegment StringSegment; struct StringSegment { StringSegment *next; /* A pointer to the next segment in the list */ char *block; /* An array of characters to be shared between strings */ int unused; /* The amount of unused space at the end of block[] */ }; /* * StringGroup is typedef'd in stringrp.h. */ struct StringGroup { FreeList *node_mem; /* The StringSegment free-list */ int block_size; /* The dimension of each character array block */ StringSegment *head; /* The list of character arrays */ }; /* * Specify how many StringSegment's to allocate at a time. */ #define STR_SEG_BLK 20 /*....................................................................... * Create a new StringGroup object. * * Input: * segment_size int The length of each of the large character * arrays in which multiple strings will be * stored. This sets the length of longest * string that can be stored, and for efficiency * should be at least 10 times as large as * the average string that will be stored. * Output: * return StringGroup * The new object, or NULL on error. */ StringGroup *_new_StringGroup(int segment_size) { StringGroup *sg; /* The object to be returned */ /* * Check the arguments. */ if(segment_size < 1) { errno = EINVAL; return NULL; }; /* * Allocate the container. */ sg = (StringGroup *) malloc(sizeof(StringGroup)); if(!sg) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_StringGroup(). */ sg->node_mem = NULL; sg->head = NULL; sg->block_size = segment_size; /* * Allocate the free list that is used to allocate list nodes. */ sg->node_mem = _new_FreeList(sizeof(StringSegment), STR_SEG_BLK); if(!sg->node_mem) return _del_StringGroup(sg); return sg; } /*....................................................................... * Delete a StringGroup object. * * Input: * sg StringGroup * The object to be deleted. * Output: * return StringGroup * The deleted object (always NULL). */ StringGroup *_del_StringGroup(StringGroup *sg) { if(sg) { StringSegment *node; /* * Delete the character arrays. */ for(node=sg->head; node; node=node->next) { if(node->block) free(node->block); node->block = NULL; }; /* * Delete the list nodes that contained the string segments. */ sg->node_mem = _del_FreeList(sg->node_mem, 1); sg->head = NULL; /* Already deleted by deleting sg->node_mem */ /* * Delete the container. */ free(sg); }; return NULL; } /*....................................................................... * Make a copy of a string in the specified string group, and return * a pointer to the copy. * * Input: * sg StringGroup * The group to store the string in. * string const char * The string to be recorded. * remove_escapes int If true, omit backslashes which escape * other characters when making the copy. * Output: * return char * The pointer to the copy of the string, * or NULL if there was insufficient memory. */ char *_sg_store_string(StringGroup *sg, const char *string, int remove_escapes) { char *copy; /* The recorded copy of string[] */ /* * Check the arguments. */ if(!sg || !string) return NULL; /* * Get memory for the string. */ copy = _sg_alloc_string(sg, strlen(string)); if(copy) { /* * If needed, remove backslash escapes while copying the input string * into the cache string. */ if(remove_escapes) { int escaped = 0; /* True if the next character should be */ /* escaped. */ const char *src = string; /* A pointer into the input string */ char *dst = copy; /* A pointer into the cached copy of the */ /* string. */ while(*src) { if(!escaped && *src == '\\') { escaped = 1; src++; } else { escaped = 0; *dst++ = *src++; }; }; *dst = '\0'; /* * If escapes have already been removed, copy the input string directly * into the cache. */ } else { strcpy(copy, string); }; }; /* * Return a pointer to the copy of the string (or NULL if the allocation * failed). */ return copy; } /*....................................................................... * Allocate memory for a string of a given length. * * Input: * sg StringGroup * The group to store the string in. * length int The required length of the string. * Output: * return char * The pointer to the copy of the string, * or NULL if there was insufficient memory. */ char *_sg_alloc_string(StringGroup *sg, int length) { StringSegment *node; /* A node of the list of string segments */ char *copy; /* The allocated string */ /* * If the string is longer than block_size, then we can't record it. */ if(length > sg->block_size || length < 0) return NULL; /* * See if there is room to record the string in one of the existing * string segments. Do this by advancing the node pointer until we find * a node with length+1 bytes unused, or we get to the end of the list. */ for(node=sg->head; node && node->unused <= length; node=node->next) ; /* * If there wasn't room, allocate a new string segment. */ if(!node) { node = (StringSegment *) _new_FreeListNode(sg->node_mem); if(!node) return NULL; /* * Initialize the segment. */ node->next = NULL; node->block = NULL; node->unused = sg->block_size; /* * Attempt to allocate the string segment character array. */ node->block = (char *) malloc(sg->block_size); if(!node->block) return NULL; /* * Prepend the node to the list. */ node->next = sg->head; sg->head = node; }; /* * Get memory for the string. */ copy = node->block + sg->block_size - node->unused; node->unused -= length + 1; /* * Return a pointer to the string memory. */ return copy; } /*....................................................................... * Delete all of the strings that are currently stored by a specified * StringGroup object. * * Input: * sg StringGroup * The group of strings to clear. */ void _clr_StringGroup(StringGroup *sg) { StringSegment *node; /* A node in the list of string segments */ /* * Mark all of the string segments as unoccupied. */ for(node=sg->head; node; node=node->next) node->unused = sg->block_size; return; } yuma123_2.14/libtecla/stringrp.h0000664000175000017500000000673614770023131016737 0ustar vladimirvladimir#ifndef stringrp_h #define stringrp_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * StringGroup objects provide memory for modules that need to * allocate lots of small strings without needing to free any of them * individually, but rather is happy to free them all at the same * time. Taking advantage of these properties, StringGroup objects * avoid the heap fragmentation that tends to occur when lots of small * strings are allocated directly from the heap and later free'd. They * do this by allocating a list of large character arrays in each of * which multiple strings are stored. Thus instead of allocating lots * of small strings, a few large character arrays are allocated. When * the strings are free'd on mass, this list of character arrays is * maintained, ready for subsequent use in recording another set of * strings. */ typedef struct StringGroup StringGroup; /* * The following constructor allocates a string-allocation object. * The segment_size argument specifies how long each string segment * array should be. This should be at least 10 times the length of * the average string to be recorded in the string group, and * sets the length of the longest string that can be stored. */ StringGroup *_new_StringGroup(int segment_size); /* * Delete all of the strings that are currently stored by a specified * StringGroup object. */ void _clr_StringGroup(StringGroup *sg); /* * Make a copy of the specified string, returning a pointer to * the copy, or NULL if there was insufficient memory. If the * remove_escapes argument is non-zero, backslashes that escape * other characters will be removed. */ char *_sg_store_string(StringGroup *sg, const char *string, int remove_escapes); /* * Allocate memory for a string of a given length. */ char *_sg_alloc_string(StringGroup *sg, int length); /* * Delete a StringGroup object (and all of the strings that it * contains). */ StringGroup *_del_StringGroup(StringGroup *sg); #endif yuma123_2.14/libtecla/demo2.c0000664000175000017500000003306014770023131016056 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include #include #include #include #include "libtecla.h" /* * If the library is being built with file-system access excluded, this * demo program won't have anything to demonstrate. */ #ifdef WITHOUT_FILE_SYSTEM int main(int argc, char *argv[]) { fprintf(stderr, "\n" " This program normally demonstrates tecla's path-lookup\n" " facility. However libtecla has been installed with\n" " file-system facilities explicitly excluded, so there is\n" " nothing to demonstrate.\n\n"); return 1; } #else /* * Encapsulate the resources needed by this demo. */ typedef struct { GetLine *gl; /* The line editor */ PathCache *pc; /* A cache of executables in the user's path */ PcaPathConf *ppc; /* The configuration argument of pca_path_completions() */ } DemoRes; /* * The following functions allocate and free instances of the above * structure. */ static DemoRes *new_DemoRes(void); static DemoRes *del_DemoRes(DemoRes *res); /* * Search backwards for the start of a pathname. */ static char *start_of_path(const char *string, int back_from); /* * Find the array indexes of the first character of the first * space-delimited word in the specified string, and of the character * that follows it. */ static int get_word_limits(const char *string, int *wa, int *wb); /* * This is the demonstration completion callback function (defined below). */ static CPL_MATCH_FN(demo_cpl_fn); /* The function which displays the introductory text of the demo */ static void show_demo_introduction(GetLine *gl); /*....................................................................... * This demo takes no arguments. It reads lines of input until the * word 'exit' is entered, or C-d is pressed. It replaces the default * tab-completion callback with one which when invoked at the start of * a line, looks up completions of commands in the user's execution * path, and when invoked in other parts of the line, reverts to * normal filename completion. Whenever a new line is entered, it * extracts the first word on the line, looks it up in the user's * execution path to see if it corresponds to a known executable file, * and if so, displays the full pathname of the file, along with the * remaining arguments. */ int main(int argc, char *argv[]) { char *line; /* A line of input */ DemoRes *res; /* The resources of the demo */ int wa,wb; /* The delimiting indexes of a word in line[] */ int major,minor,micro; /* The version number of the library */ /* * Allocate the resources needed by this demo. */ res = new_DemoRes(); if(!res) return 1; /* * If the user has the LC_CTYPE or LC_ALL environment variables set, * enable display of characters corresponding to the specified locale. */ (void) setlocale(LC_CTYPE, ""); /* * Lookup and display the version number of the library. */ libtecla_version(&major, &minor, µ); printf("\n Welcome to the path-search demo of libtecla version %d.%d.%d\n", major, minor, micro); /* * Display some introductory text, left-justifying it within the current * width of the terminal and enclosing it in a box of asterixes. */ show_demo_introduction(res->gl); /* * Read lines of input from the user and print them to stdout. */ do { /* * Get a new line from the user. */ line = gl_get_line(res->gl, "$ ", NULL, 0); if(!line) break; /* * Work out the extent of the first word in the input line, and * try to identify this as a command in the path, displaying the * full pathname of the match if found. */ if(get_word_limits(line, &wa, &wb) == 0) { char *cmd = pca_lookup_file(res->pc, line + wa, wb-wa, 0); if(cmd) { printf("Command=%s\n", cmd); printf("Arguments=%s", line+wb); } else { printf("Command not found\n"); }; }; /* * If the user types "exit", quit the program. */ if(strcmp(line, "exit\n")==0) break; } while(1); /* * Clean up. */ res = del_DemoRes(res); return 0; } /*....................................................................... * This completion callback searches for completions of executables in * the user's path when invoked on a word at the start of the path, and * performs normal filename completion elsewhere. */ static CPL_MATCH_FN(demo_cpl_fn) { /* * Get the resource object that was passed to gl_customize_completion(). */ DemoRes *res = (DemoRes *) data; /* * Find the start of the filename prefix to be completed, searching * backwards for the first unescaped space, or the start of the line. */ char *start = start_of_path(line, word_end); /* * Skip spaces preceding the start of the prefix. */ while(start > line && isspace((int)(unsigned char) start[-1])) start--; /* * If the filename prefix is at the start of the line, attempt * to complete the filename as a command in the path. Otherwise * perform normal filename completion. */ return (start == line) ? pca_path_completions(cpl, res->ppc, line, word_end) : cpl_file_completions(cpl, NULL, line, word_end); } /*....................................................................... * Search backwards for the potential start of a filename. This * looks backwards from the specified index in a given string, * stopping at the first unescaped space or the start of the line. * * Input: * string const char * The string to search backwards in. * back_from int The index of the first character in string[] * that follows the pathname. * Output: * return char * The pointer to the first character of * the potential pathname, or NULL on error. */ static char *start_of_path(const char *string, int back_from) { int i, j; /* * Search backwards from the specified index. */ for(i=back_from-1; i>=0; i--) { int c = string[i]; /* * Stop on unescaped spaces. */ if(isspace((int)(unsigned char)c)) { /* * The space can't be escaped if we are at the start of the line. */ if(i==0) break; /* * Find the extent of the escape characters which precedes the space. */ for(j=i-1; j>=0 && string[j]=='\\'; j--) ; /* * If there isn't an odd number of escape characters before the space, * then the space isn't escaped. */ if((i - 1 - j) % 2 == 0) break; }; }; return (char *)string + i + 1; } /*....................................................................... * Create a new DemoRes object containing the resources needed by the * demo. * * Output: * return DemoRes * The new object, or NULL on error. */ static DemoRes *new_DemoRes(void) { DemoRes *res; /* The object to be returned */ /* * Allocate the container. */ res = (DemoRes *)malloc(sizeof(DemoRes)); if(!res) { fprintf(stderr, "new_DemoRes: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_DemoRes(). */ res->gl = NULL; res->pc = NULL; res->ppc = NULL; /* * Create the line editor, specifying a max line length of 500 bytes, * and 10000 bytes to allocate to storage of historical input lines. */ res->gl = new_GetLine(500, 10000); if(!res->gl) return del_DemoRes(res); /* * Enable text attribute formatting directives in prompt strings. */ gl_prompt_style(res->gl, GL_FORMAT_PROMPT); /* * Allocate a cache of the executable files found in the user's path. */ res->pc = new_PathCache(); if(!res->pc) return del_DemoRes(res); /* * Populate the cache with the contents of the user's path. */ if(pca_scan_path(res->pc, getenv("PATH"))) return del_DemoRes(res); /* * Arrange for susequent calls to pca_lookup_file() and pca_path_completions() * to only report files that are executable by the user. */ pca_set_check_fn(res->pc, cpl_check_exe, NULL); /* * Allocate a configuration object for use with pca_path_completions(). */ res->ppc = new_PcaPathConf(res->pc); if(!res->ppc) return del_DemoRes(res); /* * Replace the builtin filename completion callback with one which * searches for completions of executables in the user's path when * invoked on a word at the start of the path, and completes files * elsewhere. */ if(gl_customize_completion(res->gl, res, demo_cpl_fn)) return del_DemoRes(res); return res; } /*....................................................................... * Delete a DemoRes object. * * Input: * res DemoRes * The object to be deleted. * Output: * return DemoRes * The deleted object (always NULL). */ static DemoRes *del_DemoRes(DemoRes *res) { if(res) { res->gl = del_GetLine(res->gl); res->pc = del_PathCache(res->pc); res->ppc = del_PcaPathConf(res->ppc); free(res); }; return NULL; } /*....................................................................... * Return the limits of the word at the start of a given string, ignoring * leading white-space, and interpretting the first unescaped space, tab or * the end of the line, as the end of the word. * * Input: * string const char * The string to tokenize. * Input/Output: * wa,wb int * The indexes of the first character of the word, * and the character which follows the last * character of the word, will be assigned to * *wa and *wb, respectively. * Output: * return int 0 - A word was found. * 1 - No word was found before the end of the * string. */ static int get_word_limits(const char *string, int *wa, int *wb) { int escaped = 0; /* True if the next character is escaped */ /* * Skip leading white-space. */ for(*wa=0; isspace((int)(unsigned char)string[*wa]); (*wa)++) ; /* * Find the first unescaped space, stopping early if the end of the * string is reached. */ for(*wb = *wa; ; (*wb)++) { int c = string[*wb]; if(c=='\\') escaped = !escaped; else if((!escaped && isspace((int)(unsigned char)c)) || c=='\0') break; }; return *wa == *wb; } /*....................................................................... * Display introductory text to the user, formatted according to the * current terminal width and enclosed in a box of asterixes. * * Input: * gl GetLine * The resource object of gl_get_line(). */ static void show_demo_introduction(GetLine *gl) { int start; /* The column in which gl_display_text() left the cursor */ int i; /* * Break the indtroductory text into an array of strings, so as to * avoid overflowing any compiler string limits. */ const char *doc[] = { "This program demonstrates the use of the pca_lookup_file() function ", "for finding executables in the UNIX PATH. It also demonstrates ", "tab completion of the names of executables found in the path. For ", "example, if you type:\n\n ta\n\nthen hit the tab key, you will be ", "presented with a list of executables such as tar and tail whose names ", "start with the string \"ta\". If you decide to add an \"r\" to select ", "the tar command, then you type return, the full pathname of the tar ", "program will be printed.\n\nThe file demo2.c contains the code ", "of this program, and is fully commented to enable its use as ", "a working example of how to use the facilities documented in the ", "pca_lookup_file man page.\n"}; /* * Form the top line of the documentation box by filling the area of * the line between a " *" prefix and a "* " suffix with asterixes. */ printf("\n"); gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); /* * Justify the documentation text within margins of asterixes. */ for(start=0,i=0; i= 0; i++) start = gl_display_text(gl, 0, " * ", " * ", ' ', 80, start,doc[i]); /* * Draw the bottom line of the documentation box. */ gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); printf("\n"); } #endif /* ifndef WITHOUT_FILE_SYSTEM */ yuma123_2.14/libtecla/configure.in0000664000175000017500000005363214770023131017224 0ustar vladimirvladimirdnl This is the input file which autoconf uses to construct a dnl "configure" script for the tecla library. It is a bourne shell dnl script which autoconf pre-processes with the m4 preprocessor to dnl expand autoconf-defined m4 macros such as AC_INIT(). The dnl following line just initializes autoconf. Autoconf interprets the dnl single argument as the name of an arbitrary file, which it uses to dnl ensure that it is being run correctly from the directory which dnl contains the libtecla source code. AC_INIT(getline.c) dnl Here we set the major version number of the tecla library. dnl Incrementing this number implies that a change has been made to dnl the library's public interface, which makes it binary incompatible dnl with programs that were linked with previous shared versions of dnl the tecla library. Incompatible changes of this type should be dnl avoided at all costs, so it is hoped that the major version number dnl won't ever have to change. The major version number must be a dnl small integer number, preferably a single numeric digit. AC_SUBST(MAJOR_VER) MAJOR_VER="1" dnl Set the minor version number of the tecla library. This number dnl should be incremented by one whenever additional functionality, dnl such as new functions or modules, are added to the library. The dnl idea is that a program that was linked with a shared library of dnl the same major version number, but a lower minor version number, dnl will continue to function when the run-time loader links it dnl against the updated version. The minor version number must be a dnl small integer number, which should be reset to 0 whenever the dnl major version number is incremented. AC_SUBST(MINOR_VER) MINOR_VER="6" dnl Set the micro version number of the tecla library. This is dnl incremented whenever modifications to the library are made which dnl make no changes to the public interface, but which fix bugs and/or dnl improve the behind-the-scenes implementation. The micro version dnl number should be reset to 0 whenever the minor version number is dnl incremented. The micro version number must be a small integer dnl number. AC_SUBST(MICRO_VER) MICRO_VER="1" dnl The AC_PROG_CC line looks for a C compiler, and if gcc is chosen, dnl sets the $GCC shell variable to "yes". Make sure that CFLAGS is dnl set to something first, to prevent AC_PROG_CC from substituting -g dnl for the optimization level. CFLAGS="$CFLAGS" AC_PROG_CC dnl Apparently not all implementations of the 'make' command define dnl the MAKE variable. The following directive creates a variable dnl called SET_MAKE which when expanded in a makefile is either empty dnl if the local 'make' command was found to define the MAKE variable, dnl or contains an assignment which will give the MAKE variable the dnl value 'make'. AC_PROG_MAKE_SET dnl The following directive causes autoconf to see if symbolic links dnl are supported on the current filesystem. If so, it sets the dnl variable LN_S to "ln -s". Otherwise it sets LN_S to just "ln". dnl This allows us to create symbolic links where possible, but falls dnl back to creating hard links where symbolic links aren't available. AC_PROG_LN_S dnl The following macro searches for the best implementation of awk dnl on the host system, and records it in the AWK shell variable. AC_PROG_AWK dnl If ranlib is needed on the target system, the RANLIB make variable dnl is set to ranlib. Otherwise it is set to :, which is the do-nothing dnl command of the bourne shell. dnl Note that we do not use AC_PROG_RANLIB because (at least in dnl autoconf 2.53) this does not check for cross-compilation. AC_CHECK_TOOL(RANLIB, ranlib) dnl Set LD as appropriate, especially when cross-compiling AC_CHECK_TOOL(LD, ld) dnl The following directive tells autoconf to figure out the target dnl system type and assign a canonical name for this to the $target dnl shell variable. This is used below in the target-specific case dnl statement. AC_CANONICAL_SYSTEM dnl In early versions of Solaris, some libraries are in /usr/ccs/lib, dnl where gcc doesn't look. The tests below for the curses library dnl would thus fail without this directory being added to the search dnl path. We thus add it here before the tests. Note that in the dnl following, since [ and ] are m4 quotes, and m4 will remove the dnl outermost quotes when it processes this file, we have to double dnl them up here to get [0-6] to appear in the output configure dnl script. case $target_os in solaris2.[[0-6]]|solaris2.[[0-6]].*) LIBS="$LIBS -L/usr/ccs/lib" ;; esac dnl Recent versions of gcc place /usr/local/include at the head of the dnl system include-file search path. This causes problems when include dnl files that have the same name as standard system include files are dnl placed in this directory by third-party packages. To avoid this, dnl move /usr/local/include to the end of the search path. if test "$GCC"_ = "yes"_; then touch foo.c fix=`$CC -E -Wp,-v foo.c 2>&1 | $AWK ' /^#include <...> search starts here:/ {in_list=1;ndir=0} / *\// && in_list {path[[ndir++]] = $1} /^End of search list/ {in_list=0} END { if(path[[0]] ~ /\/usr\/local\/include/) { for(dir=1; dir])], [AC_CHECK_HEADERS(ncurses/curses.h, [AC_CHECK_HEADERS(ncurses/term.h,[],[],[#include ])])]) dnl The following variable lists the targets that will be created if dnl the user runs make without any arguments. Initially we assume dnl that we can create both the normal and the reentrant versions dnl of the library. AC_SUBST(TARGETS) TARGETS="normal reentrant" dnl Check for reentrant functions by attempting to compile and link a dnl temporary program which calls them, being sure to include the dnl appropriate headers and define _POSIX_C_SOURCE, just in case any dnl of the functions are defined as macros. In the following, dnl AC_CACHE_CHECK outputs the message "checking for reentrant dnl functions". If this check has been done before, it assigns the dnl cached yes/no value to tecla_cv_reentrant. Otherwise it uses dnl AC_TRY_LINK() to attempt to compile and link the specified dummy dnl program, and sets tecla_cv_reentrant to yes or no, depending on dnl whether this succeeds. Finally it caches the value of dnl tecla_cv_reentrant in the file config.cache, and writes "yes" or dnl "no" to the terminal. AC_CACHE_CHECK(for reentrant functions, tecla_cv_reentrant, [ KEPT_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199506L" AC_TRY_LINK([ #include #include #include #include #include ], [ (void) readdir_r(NULL, NULL, NULL); (void) getpwuid_r(geteuid(), NULL, NULL, 0, NULL); (void) getpwnam_r(NULL, NULL, NULL, 0, NULL); ], tecla_cv_reentrant=yes, tecla_cv_reentrant=no) CFLAGS="$KEPT_CFLAGS" ]) dnl If the necessary reentrant functions weren't found to be dnl available, default to only compiling the non-reentrant version of dnl the library. if test $tecla_cv_reentrant = no; then TARGETS="normal" fi dnl If sys/select.h exists, arrange for the HAVE_SYS_SELECT_H C-macro to dnl be defined when compiling the library. AC_CHECK_HEADER(sys/select.h, AC_DEFINE(HAVE_SYS_SELECT_H)) dnl Check for the select system call with the normal arguments, dnl by attempting to compile and link a temporary program which dnl calls it, being sure to include the appropriate headers. dnl In the following, AC_CACHE_CHECK outputs the message dnl "checking for select system call". If this check has been done dnl before, it assigns the cached yes/no value to tecla_cv_select. dnl Otherwise it uses AC_TRY_LINK() to attempt to compile and link dnl the specified dummy program, and sets tecla_cv_select to yes dnl or no, depending on whether this succeeds. Finally it caches dnl the value of tecla_cv_select in the file config.cache, and dnl writes "yes" or "no" to the terminal. AC_CACHE_CHECK(for select system call, tecla_cv_select, [ AC_TRY_LINK([ #include #include #include #ifdef HAVE_SYS_SELECT_H #include #endif ], [ fd_set fds; int nready; FD_ZERO(&fds); FD_SET(1, &fds); nready = select(2, &fds, &fds, &fds, NULL); ], tecla_cv_select=yes, tecla_cv_select=no) ]) dnl If the select function was available, arrange for HAVE_SELECT to dnl be defined by CFLAGS. if test $tecla_cv_select = yes; then AC_DEFINE(HAVE_SELECT) fi dnl Check if this system supports the system V pseudo terminal interface. AC_CACHE_CHECK(for SysV pseudo-terminals, tecla_cv_sysv_pty, [ AC_TRY_LINK([ #include #include #include ], [ char *name = ptsname(0); int i1 = grantpt(0); int i2 = unlockpt(0); int i3 = ioctl(0, I_PUSH, "ptem"); return 0; ], tecla_cv_sysv_pty=yes, tecla_cv_sysv_pty=no) ]) dnl If the system-V pseudo-terminal interface is available, arrange dnl for HAVE_SYSV_PTY to be defined by CFLAGS. if test $tecla_cv_sysv_pty = yes; then AC_DEFINE(HAVE_SYSV_PTY) fi dnl The following variable contains the extension to append to dnl "libtecla" and "libtecla_r" when creating shared libraries on the dnl target platform. This is system dependent and is ignored if dnl LINK_SHARED remains an empty string. On most platforms that dnl support shared libaries, this will be .so.$MAJOR_VER, where dnl MAJOR_VER is the major version number described above, which on dnl some systems, tells the run-time loader if the program being dnl loaded is binary compatible with a given version of the library dnl (see the discussion of MAJOR_VER near the top of this file). dnl The following empty default can be overriden on a system by system dnl basis later in this file. AC_SUBST(SHARED_EXT) SHARED_EXT="" dnl When a shared library is installed with the extension $SHARED_EXT, dnl you can optionally produce other copies of this library with dnl different extensions. This is done using symbolic or hard links, dnl depending on what is available on the current filesystem, and the dnl extensions to use for these links are listed in the following dnl variable, separated by spaces. The following empty default can be dnl overriden on a system by system basis later in this file. AC_SUBST(SHARED_ALT) SHARED_ALT="" dnl The following variable lists extra compilation flags needed to dnl create object files that can be included in shared libraries. dnl Normally one would include a flag to tell the C compiler to dnl compile position-independent code. This option commonly includes dnl the acronym 'pic'. AC_SUBST(SHARED_CFLAGS) SHARED_CFLAGS="" dnl On systems that support shared libraries, the following variable dnl provides the command needed to make a shared library. In this dnl variable, $$@ will be replaced with the name of the shared dnl library, $$(LIB_OBJECTS) will be replaced with a space separated dnl list of the object files that are to be included in the library, dnl and libtecla$$(SUFFIX) will be the name of the library being dnl built, minus the system-specific extension (eg. libtecla or dnl libtecla_r). If LINK_SHARED is left as an empty string, shared dnl library creation will not attempted. If your system supports dnl shared library creation, you should override the default value of dnl this variable in the target-specific case statement later in this dnl file. AC_SUBST(LINK_SHARED) LINK_SHARED="" dnl When compiling the reentrant version of the library, the following dnl compiler flags are presented to the compiler, in addition to those dnl that are used when compiling the non-reentrant version of the dnl library. The PREFER_REENTRANT macro is an internal libtecla macro dnl whose presence reports when the reentrant version of the library dnl is being compiled. This allows the code to determine when to dnl disable features that can't portably be implemented reentrantly, dnl such as username completion. The existence of the _POSIX_C_SOURCE dnl macro can't be reliably used for this purpose, since some systems dnl define it by default for all code. AC_SUBST(DEFS_R) DEFS_R="-D_POSIX_C_SOURCE=199506L -DPREFER_REENTRANT" dnl For man pages relating to library features, the following two dnl variables determine in which sub-directory of the top-level man dnl directory the man pages should go, and what file-name extensions dnl these files should have. On systems where the following defaults dnl are not valid, the default values should be overriden in the dnl target-specific case statement later in this file. AC_SUBST(LIBR_MANDIR) AC_SUBST(LIBR_MANEXT) LIBR_MANDIR="man3" LIBR_MANEXT="3" dnl For man pages relating to library functions, the following two dnl variables serve the same purpose as the previously described dnl LIBR_MANDIR and LIBR_MANEXT variables. AC_SUBST(FUNC_MANDIR) AC_SUBST(FUNC_MANEXT) FUNC_MANDIR="man3" FUNC_MANEXT="3" dnl For man pages relating to programs, the following two variables dnl serve the same purpose as the previously described LIBR_MANDIR dnl and LIBR_MANEXT variables. AC_SUBST(PROG_MANDIR) AC_SUBST(PROG_MANEXT) PROG_MANDIR="man1" PROG_MANEXT="1" dnl For man pages on miscellaneous topics, the following two variables dnl serve the same purpose as the previously described LIBR_MANDIR dnl and LIBR_MANEXT variables. AC_SUBST(MISC_MANDIR) AC_SUBST(MISC_MANEXT) MISC_MANDIR="man7" MISC_MANEXT="7" dnl For man pages relating to configuration files, the following two dnl variables serve the same purpose as the previously described dnl LIBR_MANDIR and LIBR_MANEXT variables. AC_SUBST(FILE_MANDIR) AC_SUBST(FILE_MANEXT) FILE_MANDIR="man5" FILE_MANEXT="5" dnl If the application doesn't want the user to have access to the dnl filesystem, it can remove all action functions that list, read or dnl write files, by including the configuration argument dnl --without-file-actions. AC_ARG_WITH(file-actions, AC_HELP_STRING([--with-file-actions], [Should users of gl_get_line() have access to the filesystem (default=yes)]), AC_DEFINE(HIDE_FILE_SYSTEM), ) dnl If the target system either has no file-system, or file-system access dnl isn't needed, libtecla can be made smaller by excluding all file and dnl directory access code. This is done by adding the configuration dnl argument --without-file-system. AC_ARG_WITH(file-system, AC_HELP_STRING([--with-file-system], [Does the target have a filesystem (default=yes)]), AC_DEFINE(WITHOUT_FILE_SYSTEM), ) dnl The following bourne shell case statement is where system dnl dependencies can be added. In particular, if your system supports dnl shared library creation, the following switch is the place to dnl configure it. To do so you will first need to find out what target dnl type was just assigned by the AC_CANONICAL_SYSTEM macro executed dnl previously. The target type of your current system can be dnl determined by cd'ing to the top level directory of the tecla dnl distribution, and typing the command "sh config.guess". This will dnl report what autoconf thinks the system type is. Note that this dnl will be very specific, so if you know that the configuration dnl parameters that you are about to provide apply to different dnl versions of the current system type, you can express this in the dnl case statement by using a wild-card in place of the version dnl number, or by using an | alternation to list one or more version dnl names. Beware that autoconf uses [] as quote characters, so if you dnl want to use a regexp character range like [a-z], you should write dnl this as [[a-z]]. case $target in *solaris*) AC_DEFINE(__EXTENSIONS__) SHARED_EXT=".so.${MAJOR_VER}" SHARED_ALT=".so" LINK_SHARED="$LD"' -G -M $$(srcdir)/libtecla.map -o $$@ -h $$(@F) -z defs -i $$(LIB_OBJECTS) $$(LIBS) -lc' SHARED_CFLAGS="-Kpic" case $CC in */cc|cc) SHARED_CFLAGS="$SHARED_CFLAGS -xstrconst" ;; esac case $target_cpu in sparc) SHARED_CFLAGS="$SHARED_CFLAGS -xregs=no%appl" esac case $target_os in solaris2.[[89]]*|solaris2.1[[0-9]]*) LIBR_MANEXT=3lib FUNC_MANEXT=3tecla LIBR_MANDIR=man$LIBR_MANEXT FUNC_MANDIR=man$FUNC_MANEXT esac MISC_MANDIR="man5" MISC_MANEXT="5" FILE_MANDIR="man4" FILE_MANEXT="4" ;; *linux*) SHARED_EXT=".so.${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}" SHARED_ALT=".so .so.${MAJOR_VER}" dnl See if the installed version of Gnu ld accepts version scripts. AC_CACHE_CHECK([for --version-script in GNU ld], tecla_cv_gnu_ld_script, [ if (echo 'void dummy(void) {return;}' > dummy.c; $CC -c -fpic dummy.c; \ $LD -o dummy.so dummy.o -shared --version-script=$srcdir/libtecla.map) 1>&2 2>/dev/null; then tecla_cv_gnu_ld_script=yes else tecla_cv_gnu_ld_script=no fi rm -f dummy.c dummy.o dummy.so ]) if test $tecla_cv_gnu_ld_script = yes; then VERSION_OPT='--version-script=$$(srcdir)/libtecla.map' else VERSION_OPT='' fi LINK_SHARED="$LD"' -o $$@ -soname libtecla$$(SUFFIX).so.'${MAJOR_VER}' -shared '$VERSION_OPT' $$(LIB_OBJECTS) $$(LIBS) -lc' SHARED_CFLAGS="-fpic" dnl Reenable the inclusion of symbols which get undefined when POSIX_C_SOURCE dnl is specified. CFLAGS="-D_SVID_SOURCE -D_BSD_SOURCE $CFLAGS" ;; *hpux*) SHARED_EXT=".${MAJOR_VER}" SHARED_ALT=".sl" LINK_SHARED="$LD"' -b +h $$(@F) +k +vshlibunsats -o $$@ -c libtecla.map.opt $$(LIB_OBJECTS) $$(LIBS) -lc' SHARED_CFLAGS="+z" MISC_MANEXT=5 FILE_MANEXT=4 MISC_MANDIR=man$MISC_MANEXT FILE_MANDIR=man$FILE_MANEXT ;; *darwin*) SHARED_EXT=".${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}.dylib" SHARED_ALT=".dylib .${MAJOR_VER}.dylib" LINK_SHARED='$(CC) -o $$@ -dynamiclib -flat_namespace -undefined suppress -compatibility_version '${MAJOR_VER}.${MINOR_VER}' -current_version '${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}' -install_name '${libdir}'/$$@ $$(LIB_OBJECTS)' SHARED_CFLAGS="" ;; *dec-osf*) AC_DEFINE(_OSF_SOURCE) ;; *freebsd*) SHARED_EXT=".so.${MAJOR_VER}" SHARED_ALT=".so" VERSION_OPT='--version-script=$$(srcdir)/libtecla.map' LINK_SHARED='ld -o $$@ -soname libtecla$$(SUFFIX).so.'${MAJOR_VER}' -shared '$VERSION_OPT' $$(LIB_OBJECTS) $$(LIBS) -lc' SHARED_CFLAGS="-fpic" ;; mips-sgi-irix*) DEFS_R="$DEFS_R -D_XOPEN_SOURCE=500" if test "$RANLIB"_ = "_"; then RANLIB=":" fi ;; esac dnl The following statement checks to see if the GNU C compiler has dnl been chosen instead of the normal compiler of the host operating dnl system. If it has, and shared library creation has been dnl configured, it replaces the shared-library-specific C compilation dnl flags with those supported by gcc. Also append the gcc run-time dnl library to the shared library link line. if test "$GCC"_ = "yes"_ && test "$LINK_SHARED"_ != "_" ; then SHARED_CFLAGS="-fpic" case $target in sparc-*-solaris*) SHARED_CFLAGS="$SHARED_CFLAGS -mno-app-regs" ;; *darwin*) SHARED_CFLAGS="" ;; esac LINK_SHARED="$LINK_SHARED `gcc -print-libgcc-file-name`" fi dnl The following variable will list which types of libraries, dnl "static", and possibly "shared", are to be created and installed. AC_SUBST(TARGET_LIBS) dnl If shared library creation has been configured, add shared dnl libraries to the list of libraries to be built. if test "$LINK_SHARED"_ != "_"; then TARGET_LIBS="static shared" else TARGET_LIBS="static" LINK_SHARED="@:" fi dnl Set the shell variable and Makefile variable, MAKE_MAN_PAGES, to dnl "yes" if man pages are desired. By default they are, but if the dnl user specifies --with-man-pages=no or --without-man-pages, then dnl they won't be preprocessed by the configure script or installed dnl by the Makefile. AC_SUBST(MAKE_MAN_PAGES) AC_ARG_WITH(man-pages, AC_HELP_STRING([--with-man-pages], [Are man pages desired (default=yes)]), MAKE_MAN_PAGES="$withval", MAKE_MAN_PAGES="yes") dnl Create the list of files to be generated by the configure script. OUTPUT_FILES="Makefile" rm -rf man/man* if test "$MAKE_MAN_PAGES"_ = "yes"_; then for area in libr func misc prog file; do for page in man/$area/*.in; do OUTPUT_FILES="$OUTPUT_FILES `echo $page | sed 's/\.in$//'`" done done fi dnl The following directive must always be the last line of any dnl autoconf script. It causes autoconf to create the configure dnl script, which for each argument of AC_OUTPUT, will look for a dnl filename formed by appending ".in" to the argument, preprocess dnl that file, replacing @VAR@ directives with the corresponding value dnl of the specified shell variable VAR, as set above in this file, dnl and write the resulting output to the filename given in the dnl argument. Note that only shell variables that were exported above dnl with the AC_SUBST() directive will be substituted in @VAR@ dnl directives (some macros like AC_PROG_CC also call AC_SUBST for you dnl for the variables that they output). AC_OUTPUT($OUTPUT_FILES) yuma123_2.14/libtecla/INSTALL0000664000175000017500000002353414770023131015742 0ustar vladimirvladimirTo compile and optionally install the library, it is first necessary to create a makefile for your system, by typing: ./configure The Makefile that this generates is designed to install the files of the library in subdirectories of /usr/local/. If you would prefer to install them under a different directory, you can type: ./configure --prefix /wherever Where you would replace /wherever with your chosen directory. Other command-line options are available, and can be listed by typing: ./configure --help Having run the configure script, you are then ready to make the library. To do this, just type: make What 'make' does depends on whether the configure script knows about your system. If the configure script doesn't know anything specific about your system, it will arrange for 'make' to produce the static tecla library, called libtecla.a, and if possible, the reentrant version of this called libtecla_r.a. If it does know about your system, it will also create shared libraries if possible. If you are on a system that isn't known, and you would like shared libraries to be compiled, please read the file called PORTING to see how this can be achieved. To install the library, its include file and it manual pages, type: make install Note that this will also compile the library if you haven't already done so. Having compiled the library, if you wish, you can test it by running the demo programs. After building the library, you should find two programs, called demo and demo2, in the current directory. The first of the demos programs reads input lines from the user, and writes what was typed back to the screen. While typing a line of input, you can experiment with line editing, tab completion, history recall etc.. For details about these line editing features, see the man page gl_get_line(3). If you haven't installed this yet, you can see it anyway by typing: nroff -man man3/gl_get_line.3 | more The second demo program, called demo2, demonstrates command-completion with the UNIX PATH. If you type in a partial command name, and press TAB, the command name will be completed if possible, and possible completions will be listed if it is ambiguous. When you then enter the line, the demo program then prints out the full pathname of the command that you typed. If you type anything after the command name, filename completion with the tab key reverts to its default behavior of completing filenames in the current directory. COMPILING IN A DIFFERENT DIRECTORY ---------------------------------- If you unpack the distribution in a directory which is visible from multiple hosts which have different architectures, you have the option of compiling the library for the different architectures in different directories. You might for example create a sub-directory for each architecture, under the top level directory of the distribution. You would then log in to a host of one of these architectures, cd to the sub-directory that you created for it, and type: ../configure The configure script then creates a makefile in the current directory which is designed to build the library, object files, demos etc for the architecture of the current host, in the current directory, using the original source code in ../. You then repeat this procedure on hosts of other architectures. The compilation directories don't have to be sub-directories of the top level directory of the distribution. That was just described as an example. They can be anywhere you like. Every rule in the makefiles that are generated by the configure script, cites the paths of the target and source files explicitly, so this procedure should work on any system, without the need for vpath makefile support. EMBEDDING IN OTHER PACKAGE DISTRIBUTIONS ---------------------------------------- If you distribute the library with another package, which has its own heirarchy and configuration procedures, the following installation options may be of interest to you. At first glance, the use of a GNU configure script by the tecla library, may appear to reduce your options for controlling what gets made, and where it gets installed, but this isn't the case, because many of the parameters configured by the configure script are assigned to makefile variables which can be overriden when you run make. Configure script options: If the users of your package won't benefit from having access to the tecla man pages, you can shorten the length of time taken to run the configure script by telling this script not to preprocess the man pages. This is done as follows. ./configure --without-man-pages Note that this option also causes the makefile man-page installation targets to do nothing. Similarly, if you don't want your users to have access to the filesystem while they are editing input lines using gl_get_line(), then use the following configuration argument. ./configure --without-file-actions This will completely remove the "expand-filename", "read-from-file", "read-init-files", and "list-glob" action functions. It will also arrange that the default behavior of actions such as "complete-word" and "list-or-eof" be changed to show no completions, instead of the normal default of showing filename completions. If you are using a system that doesn't have a file-system, such as an embedded system, then libtecla can be built with all code that accesses the filesystem removed. This will make the library a bit smaller, and potentially avoid running into problems of missing system functions related to file-system access. This is done with the following configuration option. ./configure --without-file-system Beware that this not only prevents the user from accessing the filesystem while editing an input line in gl_get_line(), but also removes all public file-related functions, such as the pathname expansion module. When using gl_get_line() in this configuration, the functions that load and save history from and to files, are stubs that report an error if called upon to read files. The gl_configure_getline() function can still be called upon to configure gl_get_line() via its app_string argument, but passing it a filename in either the app_file or user_file arguments will result in an error being reported. Now lets say that you have your own configuration script in the parent directory of the libtecla top-level directory, and that you don't want tecla's man pages to be generated. In your configuration script, you would first need to have a line similar to the following: (cd libtecla; ./configure --without-man-pages) Now, from your makefile or whatever script you use to build your application, you would need to make the tecla library. Assuming that your makefile or build script is in the parent directory of the libtecla distribution, then the following line tells make to just build the non-reentrant, static version of the tecla library, and then to install it and the tecla include file in sub-directories called lib and include in your current directory. (cd libtecla; make LIBDIR=../lib INCDIR=../include TARGETS=normal TARGET_LIBS="static" install_lib install_inc) In this statement the LIBDIR=../lib component means that on installing the library, the make command should place the library in the directory libtecla/../lib. Similarly INCDIR tells make where to place the include files. The install_lib and install_inc targets tell make to install the libraries and the include file. Because the install_man and install_bin targets have been omitted in this example, the man pages and programs aren't installed. If you were to include these additional targets then you could use the MANDIR and BINDIR variables, respectively to control where they were installed. The TARGETS variable is used to specify which of the normal and reentrant versions of the library are compiled. This can contain one or both of the words "normal" and "reentrant". If you don't specify this when you invoke make, the default value generated by the configure script will be used. Depending on whether reentrant POSIX functions are available for compilation of the reentrant version, this will be either "normal" or "normal reentrant". The TARGET_LIBS variable is used to specify which of the static and shared libraries are to be built. This can contain one or both of the words "static" and "shared". If you don't specify this when you invoke make, the default value generated by the configure script will be used. Depending on whether the configure script knows how to create shared libraries on the target system, this will be either "static" or "static shared". Beware that shared libraries aren't supported on many systems, so requiring "shared" will limit which systems you can compile your package on. Also note that unless your package installs the tecla library in a directory which all users of your program will have access to, you should only compile the static version. Instructions for adding shared-library creation rules for new systems are included in the PORTING file. The OPT variable can be used to change the default optimization from the default of "-O" to something else. The DEMOS variable controls whether the demo programs are built. Normally this has the value "demos", which tells the makefile to build the demo programs. Setting it to an empty string stops the demos from being built. The PROGRAMS variable is used to specify which programs are to be built and subsequently installed. All available programs are built by default. Currently there is only one such program, selected by specifying the word "enhance". This program uses tecla-enhanced pseudo-terminals to layer command line editing on top of third party programs. The PROGRAMS_R variable serves the same purpose as the PROGRAMS variable, except that programs listed here are linked with the reentrant version of the library, and should be specified with a _r suffix. Currently this variable is empty by default. Martin Shepherd (mcs@astro.caltech.edu) yuma123_2.14/libtecla/expand.h0000664000175000017500000000372714770023131016343 0ustar vladimirvladimir#ifndef expand_h #define expand_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * This header is not for use by external applicatons. It contains * internal immplementation features of the libtecla library, which * may change incompatibly between releases. */ /* * Print a list of expansions via a callback function. */ int _ef_output_expansions(FileExpansion *result, GlWriteFn *write_fn, void *data, int term_width); #endif yuma123_2.14/libtecla/errmsg.h0000664000175000017500000000562414770023131016361 0ustar vladimirvladimir#ifndef errmsg_h #define errmsg_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * Set the longest expected length of an error message (excluding its * '\0' terminator. Since any message over a nominal terminal width of * 80 characters is going to look a mess, it makes no sense to support * huge lengths. Note that many uses of strings declared with this * macro assume that it will be at least 81, so don't reduce it below * this limit. */ #define ERR_MSG_LEN 128 /* * Provide an opaque typedef to the error-message object. */ typedef struct ErrMsg ErrMsg; /* * The following token is used to terminate the argument lists of calls * to _err_record_msg(). */ #define END_ERR_MSG ((const char *)0) /* * Allocate a new error-message buffer. */ ErrMsg *_new_ErrMsg(void); /* * Delete an error message buffer. */ ErrMsg *_del_ErrMsg(ErrMsg *err); /* * Concatenate a list of string arguments into the specified buffer, buff[], * which has an allocated size of buffdim characters. * The last argument must be END_ERR_MSG to terminate the argument list. */ void _err_record_msg(ErrMsg *err, ...); /* * Replace the current error message with an empty string. */ void _err_clear_msg(ErrMsg *err); /* * Return a pointer to the error message buffer. This is * a '\0' terminated character array containing ERR_MSG_LEN+1 * elements. */ char *_err_get_msg(ErrMsg *err); #endif yuma123_2.14/libtecla/keytab.c0000664000175000017500000007364114770023131016340 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include #include "keytab.h" #include "strngmem.h" #include "getline.h" #include "errmsg.h" #include "hash.h" /* * When allocating or reallocating the key-binding table, how * many entries should be added? */ #define KT_TABLE_INC 100 /* * Define the size of the hash table that is used to associate action * names with action functions. This should be a prime number. */ #define KT_HASH_SIZE 113 /* * Define a binary-symbol-table object. */ struct KeyTab { ErrMsg *err; /* Information about the last error */ int size; /* The allocated dimension of table[] */ int nkey; /* The current number of members in the table */ KeySym *table; /* The table of lexically sorted key sequences */ HashTable *actions; /* The hash table of actions */ StringMem *smem; /* Memory for allocating strings */ }; static int _kt_extend_table(KeyTab *kt); static int _kt_parse_keybinding_string(const char *keyseq, char *binary, int *nc); static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2); static void _kt_assign_action(KeySym *sym, KtBinder binder, KtKeyFn *keyfn, void *data); static char _kt_backslash_escape(const char *string, const char **endp); static int _kt_is_emacs_meta(const char *string); static int _kt_is_emacs_ctrl(const char *string); static KtKeyMatch _kt_locate_keybinding(KeyTab *kt, const char *binary_keyseq, int nc, int *first, int *last); /*....................................................................... * Create a new key-binding symbol table. * * Output: * return KeyTab * The new object, or NULL on error. */ KeyTab *_new_KeyTab(void) { KeyTab *kt; /* The object to be returned */ /* * Allocate the container. */ kt = (KeyTab *) malloc(sizeof(KeyTab)); if(!kt) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_KeyTab(). */ kt->err = NULL; kt->size = KT_TABLE_INC; kt->nkey = 0; kt->table = NULL; kt->actions = NULL; kt->smem = NULL; /* * Allocate a place to record error messages. */ kt->err = _new_ErrMsg(); if(!kt->err) return _del_KeyTab(kt); /* * Allocate the table. */ kt->table = (KeySym *) malloc(sizeof(kt->table[0]) * kt->size); if(!kt->table) { errno = ENOMEM; return _del_KeyTab(kt); }; /* * Allocate a hash table of actions. */ kt->actions = _new_HashTable(NULL, KT_HASH_SIZE, IGNORE_CASE, NULL, 0); if(!kt->actions) return _del_KeyTab(kt); /* * Allocate a string allocation object. This allows allocation of * small strings without fragmenting the heap. */ kt->smem = _new_StringMem(KT_TABLE_INC); if(!kt->smem) return _del_KeyTab(kt); return kt; } /*....................................................................... * Delete a KeyTab object. * * Input: * kt KeyTab * The object to be deleted. * Output: * return KeyTab * The deleted object (always NULL). */ KeyTab *_del_KeyTab(KeyTab *kt) { if(kt) { if(kt->table) free(kt->table); kt->actions = _del_HashTable(kt->actions); kt->smem = _del_StringMem(kt->smem, 1); kt->err = _del_ErrMsg(kt->err); free(kt); }; return NULL; } /*....................................................................... * Increase the size of the table to accomodate more keys. * * Input: * kt KeyTab * The table to be extended. * Output: * return int 0 - OK. * 1 - Error. */ static int _kt_extend_table(KeyTab *kt) { /* * Attempt to increase the size of the table. */ KeySym *newtab = (KeySym *) realloc(kt->table, sizeof(kt->table[0]) * (kt->size + KT_TABLE_INC)); /* * Failed? */ if(!newtab) { _err_record_msg(kt->err, "Can't extend keybinding table", END_ERR_MSG); errno = ENOMEM; return 1; }; /* * Install the resized table. */ kt->table = newtab; kt->size += KT_TABLE_INC; return 0; } /*....................................................................... * Add, update or remove a keybinding to the table. * * Input: * kt KeyTab * The table to add the binding to. * binder KtBinder The source of the binding. * keyseq const char * The key-sequence to bind. * action char * The action to associate with the key sequence, or * NULL to remove the action associated with the * key sequence. * Output: * return int 0 - OK. * 1 - Error. */ int _kt_set_keybinding(KeyTab *kt, KtBinder binder, const char *keyseq, const char *action) { KtKeyFn *keyfn; /* The action function */ void *data; /* The callback data of the action function */ /* * Check arguments. */ if(kt==NULL || !keyseq) { errno = EINVAL; if(kt) _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); return 1; }; /* * Lookup the function that implements the specified action. */ if(!action) { keyfn = 0; data = NULL; } else { Symbol *sym = _find_HashSymbol(kt->actions, action); if(!sym) { _err_record_msg(kt->err, "Unknown key-binding action: ", action, END_ERR_MSG); errno = EINVAL; return 1; }; keyfn = (KtKeyFn *) sym->fn; data = sym->data; }; /* * Record the action in the table. */ return _kt_set_keyfn(kt, binder, keyseq, keyfn, data); } /*....................................................................... * Add, update or remove a keybinding to the table, specifying an action * function directly. * * Input: * kt KeyTab * The table to add the binding to. * binder KtBinder The source of the binding. * keyseq char * The key-sequence to bind. * keyfn KtKeyFn * The action function, or NULL to remove any existing * action function. * data void * A pointer to anonymous data to be passed to keyfn * whenever it is called. * Output: * return int 0 - OK. * 1 - Error. */ int _kt_set_keyfn(KeyTab *kt, KtBinder binder, const char *keyseq, KtKeyFn *keyfn, void *data) { const char *kptr; /* A pointer into keyseq[] */ char *binary; /* The binary version of keyseq[] */ int nc; /* The number of characters in binary[] */ int first,last; /* The first and last entries in the table which */ /* minimally match. */ int size; /* The size to allocate for the binary string */ int i; /* * Check arguments. */ if(kt==NULL || !keyseq) { errno = EINVAL; if(kt) _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); return 1; }; /* * Work out a pessimistic estimate of how much space will be needed * for the binary copy of the string, noting that binary meta characters * embedded in the input string get split into two characters. */ for(size=0,kptr = keyseq; *kptr; kptr++) size += IS_META_CHAR(*kptr) ? 2 : 1; /* * Allocate a string that has the length of keyseq[]. */ binary = _new_StringMemString(kt->smem, size + 1); if(!binary) { errno = ENOMEM; _err_record_msg(kt->err, "Insufficient memory to record key sequence", END_ERR_MSG); return 1; }; /* * Convert control and octal character specifications to binary characters. */ if(_kt_parse_keybinding_string(keyseq, binary, &nc)) { binary = _del_StringMemString(kt->smem, binary); return 1; }; /* * Lookup the position in the table at which to insert the binding. */ switch(_kt_locate_keybinding(kt, binary, nc, &first, &last)) { /* * If an exact match for the key-sequence is already in the table, * simply replace its binding function (or delete the entry if * the new binding is 0). */ case KT_EXACT_MATCH: if(keyfn) { _kt_assign_action(kt->table + first, binder, keyfn, data); } else { _del_StringMemString(kt->smem, kt->table[first].keyseq); memmove(kt->table + first, kt->table + first + 1, (kt->nkey - first - 1) * sizeof(kt->table[0])); kt->nkey--; }; binary = _del_StringMemString(kt->smem, binary); break; /* * If an ambiguous match has been found and we are installing a * callback, then our new key-sequence would hide all of the ambiguous * matches, so we shouldn't allow it. */ case KT_AMBIG_MATCH: if(keyfn) { _err_record_msg(kt->err, "Can't bind \"", keyseq, "\", because it is a prefix of another binding", END_ERR_MSG); binary = _del_StringMemString(kt->smem, binary); errno = EPERM; return 1; }; break; /* * If the entry doesn't exist, create it. */ case KT_NO_MATCH: /* * Add a new binding? */ if(keyfn) { KeySym *sym; /* * We will need a new entry, extend the table if needed. */ if(kt->nkey + 1 > kt->size) { if(_kt_extend_table(kt)) { binary = _del_StringMemString(kt->smem, binary); return 1; }; }; /* * Make space to insert the new key-sequence before 'last'. */ if(last < kt->nkey) { memmove(kt->table + last + 1, kt->table + last, (kt->nkey - last) * sizeof(kt->table[0])); }; /* * Insert the new binding in the vacated position. */ sym = kt->table + last; sym->keyseq = binary; sym->nc = nc; for(i=0; iactions + i; action->fn = 0; action->data = NULL; }; sym->binder = -1; _kt_assign_action(sym, binder, keyfn, data); kt->nkey++; }; break; case KT_BAD_MATCH: binary = _del_StringMemString(kt->smem, binary); return 1; break; }; return 0; } /*....................................................................... * Perform a min-match lookup of a key-binding. * * Input: * kt KeyTab * The keybinding table to lookup in. * binary_keyseq char * The binary key-sequence to lookup. * nc int the number of characters in keyseq[]. * Input/Output: * first,last int * If there is an ambiguous or exact match, the indexes * of the first and last symbols that minimally match * will be assigned to *first and *last respectively. * If there is no match, then first and last will * bracket the location where the symbol should be * inserted. * Output: * return KtKeyMatch One of the following enumerators: * KT_EXACT_MATCH - An exact match was found. * KT_AMBIG_MATCH - An ambiguous match was found. * KT_NO_MATCH - No match was found. * KT_BAD_MATCH - An error occurred while searching. */ static KtKeyMatch _kt_locate_keybinding(KeyTab *kt, const char *binary_keyseq, int nc, int *first, int *last) { int mid; /* The index at which to bisect the table */ int bot; /* The lowest index of the table not searched yet */ int top; /* The highest index of the table not searched yet */ int test; /* The return value of strcmp() */ /* * Perform a binary search for the key-sequence. */ bot = 0; top = kt->nkey - 1; while(top >= bot) { mid = (top + bot)/2; test = _kt_compare_strings(kt->table[mid].keyseq, kt->table[mid].nc, binary_keyseq, nc); if(test > 0) top = mid - 1; else if(test < 0) bot = mid + 1; else { *first = *last = mid; return KT_EXACT_MATCH; }; }; /* * An exact match wasn't found, but top is the index just below the * index where a match would be found, and bot is the index just above * where the match ought to be found. */ *first = top; *last = bot; /* * See if any ambiguous matches exist, and if so make *first and *last * refer to the first and last matches. */ if(*last < kt->nkey && kt->table[*last].nc > nc && _kt_compare_strings(kt->table[*last].keyseq, nc, binary_keyseq, nc)==0) { *first = *last; while(*last+1 < kt->nkey && kt->table[*last+1].nc > nc && _kt_compare_strings(kt->table[*last+1].keyseq, nc, binary_keyseq, nc)==0) (*last)++; return KT_AMBIG_MATCH; }; /* * No match. */ return KT_NO_MATCH; } /*....................................................................... * Lookup the sub-array of key-bindings who's key-sequences minimally * match a given key-sequence. * * Input: * kt KeyTab * The keybinding table to lookup in. * binary_keyseq char * The binary key-sequence to lookup. * nc int the number of characters in keyseq[]. * Input/Output: * matches KeySym ** The array of minimally matching symbols * can be found in (*matches)[0..nmatch-1], unless * no match was found, in which case *matches will * be set to NULL. * nmatch int The number of ambiguously matching symbols. This * will be 0 if there is no match, 1 for an exact * match, and a number greater than 1 for an ambiguous * match. * Output: * return KtKeyMatch One of the following enumerators: * KT_EXACT_MATCH - An exact match was found. * KT_AMBIG_MATCH - An ambiguous match was found. * KT_NO_MATCH - No match was found. * KT_BAD_MATCH - An error occurred while searching. */ KtKeyMatch _kt_lookup_keybinding(KeyTab *kt, const char *binary_keyseq, int nc, KeySym **matches, int *nmatch) { KtKeyMatch status; /* The return status */ int first,last; /* The indexes of the first and last matching entry */ /* in the symbol table. */ /* * Check the arguments. */ if(!kt || !binary_keyseq || !matches || !nmatch || nc < 0) { errno = EINVAL; if(kt) _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); return KT_BAD_MATCH; }; /* * Lookup the indexes of the binding-table entries that bracket the * target key-sequence. */ status = _kt_locate_keybinding(kt, binary_keyseq, nc, &first, &last); /* * Translate the indexes into the corresponding subarray of matching * table entries. */ switch(status) { case KT_EXACT_MATCH: case KT_AMBIG_MATCH: *matches = kt->table + first; *nmatch = last - first + 1; break; default: *matches = NULL; *nmatch = 0; break; }; return status; } /*....................................................................... * Convert a keybinding string into a uniq binary representation. * * Control characters can be given directly in their binary form, * expressed as either ^ or C-, followed by the character, expressed in * octal, like \129 or via C-style backslash escapes, with the addition * of '\E' to denote the escape key. Similarly, meta characters can be * given directly in binary or expressed as M- followed by the character. * Meta characters are recorded as two characters in the binary output * string, the first being the escape key, and the second being the key * that was modified by the meta key. This means that binding to * \EA or ^[A or M-A are all equivalent. * * Input: * keyseq char * The key sequence being added. * Input/Output: * binary char * The binary version of the key sequence will be * assigned to binary[], which must have at least * as many characters as keyseq[] plus the number * of embedded binary meta characters. * nc int * The number of characters assigned to binary[] * will be recorded in *nc. * Output: * return int 0 - OK. * 1 - Error. */ static int _kt_parse_keybinding_string(const char *keyseq, char *binary, int *nc) { const char *iptr = keyseq; /* Pointer into keyseq[] */ char *optr = binary; /* Pointer into binary[] */ char c; /* An intermediate character */ /* * Parse the input characters until they are exhausted or the * output string becomes full. */ while(*iptr) { /* * Check for special characters. */ switch(*iptr) { case '^': /* A control character specification */ /* * Convert the caret expression into the corresponding control * character unless no character follows the caret, in which case * record a literal caret. */ if(iptr[1]) { /* * Get the next, possibly escaped, character. */ if(iptr[1] == '\\') { c = _kt_backslash_escape(iptr+2, &iptr); } else { c = iptr[1]; iptr += 2; }; /* * Convert the character to a control character. */ *optr++ = MAKE_CTRL(c); } else { *optr++ = *iptr++; }; break; /* * A backslash-escaped character? */ case '\\': /* * Convert the escape sequence to a binary character. */ *optr++ = _kt_backslash_escape(iptr+1, &iptr); break; /* * Possibly an emacs-style meta character? */ case 'M': if(_kt_is_emacs_meta(iptr)) { *optr++ = GL_ESC_CHAR; iptr += 2; } else { *optr++ = *iptr++; }; break; /* * Possibly an emacs-style control character specification? */ case 'C': if(_kt_is_emacs_ctrl(iptr)) { *optr++ = MAKE_CTRL(iptr[2]); iptr += 3; } else { *optr++ = *iptr++; }; break; default: /* * Convert embedded meta characters into an escape character followed * by the meta-unmodified character. */ if(IS_META_CHAR(*iptr)) { *optr++ = GL_ESC_CHAR; *optr++ = META_TO_CHAR(*iptr); iptr++; /* * To allow keysequences that start with printable characters to * be distinguished from the cursor-key keywords, prepend a backslash * to the former. This same operation is performed in gl_interpret_char() * before looking up a keysequence that starts with a printable character. */ } else if(iptr==keyseq && !IS_CTRL_CHAR(*iptr) && strcmp(keyseq, "up") != 0 && strcmp(keyseq, "down") != 0 && strcmp(keyseq, "left") != 0 && strcmp(keyseq, "right") != 0) { *optr++ = '\\'; *optr++ = *iptr++; } else { *optr++ = *iptr++; }; }; }; /* * How many characters were placed in the output array? */ *nc = optr - binary; return 0; } /*....................................................................... * Add, remove or modify an action. * * Input: * kt KeyTab * The key-binding table. * action char * The name of the action. * fn KtKeyFn * The function that implements the action, or NULL * to remove an existing action. * data void * A pointer to arbitrary callback data to pass to the * action function whenever it is called. * Output: * return int 0 - OK. * 1 - Error. */ int _kt_set_action(KeyTab *kt, const char *action, KtKeyFn *fn, void *data) { Symbol *sym; /* The symbol table entry of the action */ /* * Check the arguments. */ if(!kt || !action) { errno = EINVAL; if(kt) _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); return 1; }; /* * If no function was provided, delete an existing action. */ if(!fn) { sym = _del_HashSymbol(kt->actions, action); return 0; }; /* * If the action already exists, replace its action function. */ sym = _find_HashSymbol(kt->actions, action); if(sym) { sym->fn = (void (*)(void))fn; sym->data = data; return 0; }; /* * Add a new action. */ if(!_new_HashSymbol(kt->actions, action, 0, (void (*)(void))fn, data, 0)) { _err_record_msg(kt->err, "Insufficient memory to record key-binding action", END_ERR_MSG); return 1; }; return 0; } /*....................................................................... * Compare two strings of specified length which may contain embedded * ascii NUL's. * * Input: * s1 char * The first of the strings to be compared. * n1 int The length of the string in s1. * s2 char * The second of the strings to be compared. * n2 int The length of the string in s2. * Output: * return int < 0 if(s1 < s2) * 0 if(s1 == s2) * > 0 if(s1 > s2) */ static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2) { int i; /* * Find the first character where the two strings differ. */ for(i=0; i= KTB_NBIND) return; /* * Record the action according to its source. */ action = sym->actions + binder; action->fn = keyfn; action->data = data; /* * Find the highest priority binding source that has supplied an * action. Note that the actions[] array is ordered in order of * descreasing priority, so the first entry that contains a function * is the one to use. */ for(i=0; iactions[i].fn; i++) ; /* * Record the index of this action for use during lookups. */ sym->binder = i < KTB_NBIND ? i : -1; return; } /*....................................................................... * Remove all key bindings that came from a specified source. * * Input: * kt KeyTab * The table of key bindings. * binder KtBinder The source of the bindings to be cleared. */ void _kt_clear_bindings(KeyTab *kt, KtBinder binder) { int oldkey; /* The index of a key in the original binding table */ int newkey; /* The index of a key in the updated binding table */ /* * If there is no table, then no bindings exist to be deleted. */ if(!kt) return; /* * Clear bindings of the given source. */ for(oldkey=0; oldkeynkey; oldkey++) _kt_assign_action(kt->table + oldkey, binder, 0, NULL); /* * Delete entries that now don't have a binding from any source. */ newkey = 0; for(oldkey=0; oldkeynkey; oldkey++) { KeySym *sym = kt->table + oldkey; if(sym->binder < 0) { _del_StringMemString(kt->smem, sym->keyseq); } else { if(oldkey != newkey) kt->table[newkey] = *sym; newkey++; }; }; /* * Record the number of keys that were kept. */ kt->nkey = newkey; return; } /*....................................................................... * Translate a backslash escape sequence to a binary character. * * Input: * string const char * The characters that follow the backslash. * Input/Output: * endp const char ** If endp!=NULL, on return *endp will be made to * point to the character in string[] which follows * the escape sequence. * Output: * return char The binary character. */ static char _kt_backslash_escape(const char *string, const char **endp) { char c; /* The output character */ /* * Is the backslash followed by one or more octal digits? */ switch(*string) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c = strtol(string, (char **)&string, 8); break; case 'a': c = '\a'; string++; break; case 'b': c = '\b'; string++; break; case 'e': case 'E': /* Escape */ c = GL_ESC_CHAR; string++; break; case 'f': c = '\f'; string++; break; case 'n': c = '\n'; string++; break; case 'r': c = '\r'; string++; break; case 't': c = '\t'; string++; break; case 'v': c = '\v'; string++; break; case '\0': c = '\\'; break; default: c = *string++; break; }; /* * Report the character which follows the escape sequence. */ if(endp) *endp = string; return c; } /*....................................................................... * Return non-zero if the next two characters are M- and a third character * follows. Otherwise return 0. * * Input: * string const char * The sub-string to scan. * Output: * return int 1 - The next two characters are M- and these * are followed by at least one character. * 0 - The next two characters aren't M- or no * character follows a M- pair. */ static int _kt_is_emacs_meta(const char *string) { return *string++ == 'M' && *string++ == '-' && *string; } /*....................................................................... * Return non-zero if the next two characters are C- and a third character * follows. Otherwise return 0. * * Input: * string const char * The sub-string to scan. * Output: * return int 1 - The next two characters are C- and these * are followed by at least one character. * 0 - The next two characters aren't C- or no * character follows a C- pair. */ static int _kt_is_emacs_ctrl(const char *string) { return *string++ == 'C' && *string++ == '-' && *string; } /*....................................................................... * Merge an array of bindings with existing bindings. * * Input: * kt KeyTab * The table of key bindings. * binder KtBinder The source of the bindings. * bindings const KtKeyBinding * The array of bindings. * n int The number of bindings in bindings[]. * Output: * return int 0 - OK. * 1 - Error. */ int _kt_add_bindings(KeyTab *kt, KtBinder binder, const KtKeyBinding *bindings, unsigned n) { int i; /* * Check the arguments. */ if(!kt || !bindings) { errno = EINVAL; if(kt) _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); return 1; }; /* * Install the array of bindings. */ for(i=0; ierr, "NULL argument(s)", END_ERR_MSG); return 1; }; /* * Lookup the symbol table entry of the action. */ sym = _find_HashSymbol(kt->actions, action); if(!sym) return 1; /* * Return the function and ccallback data associated with the action. */ if(fn) *fn = (KtKeyFn *) sym->fn; if(data) *data = sym->data; return 0; } /*....................................................................... * Return extra information (ie. in addition to that provided by errno) * about the last error to occur in any of the public functions of this * module. * * Input: * kt KeyTab * The table of key bindings. * Output: * return const char * A pointer to the internal buffer in which * the error message is temporarily stored. */ const char *_kt_last_error(KeyTab *kt) { return kt ? _err_get_msg(kt->err) : "NULL KeyTab argument"; } yuma123_2.14/libtecla/libtecla.map0000664000175000017500000001011214770023131017153 0ustar vladimirvladimir# This mapfile (or version script) lists the public symbols that are # publically exported by each version of the tecla library. This file # has the format required by the Sun and Linux linkers, and also acts # as a template from which map files for other systems can be derived # with awk or sed. # # Under Solaris and Linux, this map file is used by ld during shared # library creation. It has two purposes: # # 1. It specifies which symbols in the library are to be made visible # to applications. This has the dual benefits of reducing namespace # polution, and of preventing applications from using private # internal library functions that might change or disappear in # future releases. # # 2. The information listed in this file is recorded in the shared # library, such that when an application is linked against it, the # linker can record a dependency in the application which says # which is the earliest library version which included all of the # symbols that the application needs. This means that if the # application is copied to another system that has an earlier # version of the library, the linker can quickly determine whether # the earlier version contains all of the symbols that it needs. # # Under Linux, mapfiles can also be used to allow multiple # incompatible versions of a given function to exist in a library, # thus supporting applications that were compiled against different # incompatible versions of the library. Since this feature (and the # inclusion of .symver directives) isn't supported by Solaris, it # can't be included in this file. Non backwards compatibility in the # ABI must instead be handled in the more traditional way, by # incrementing the major version number. # # When a new minor release is made, a new tecla_1.x specification # should be added which inherits the symbols of the previous release # and lists newly added functions. For example, below you will find # the following clause: # # tecla_1.3 { # global: # ef_list_expansions; # } tecla_1.2; # # This says that ef_list_expansions is the name of a public function # that was added in the 1.3 release, and that the symbols defined in # the previous tecla_1.2 clause have been inherited by tecla_1.3. # # For more details see the following URL: # # http://www.usenix.org/publications/library/proceedings/als2000/browndavid.html #------------------------------------------------------------------------------- tecla_1.2 { global: cfc_file_start; cfc_literal_escapes; cfc_set_check_fn; cpl_add_completion; cpl_check_exe; cpl_complete_word; cpl_file_completions; cpl_init_FileArgs; cpl_last_error; cpl_list_completions; cpl_record_error; del_CplFileConf; del_ExpandFile; del_GetLine; del_PathCache; del_PcaPathConf; del_WordCompletion; ef_expand_file; ef_last_error; gl_change_terminal; gl_customize_completion; gl_get_line; new_CplFileConf; new_ExpandFile; new_GetLine; new_PathCache; new_PcaPathConf; new_WordCompletion; pca_last_error; pca_lookup_file; pca_path_completions; pca_scan_path; pca_set_check_fn; ppc_file_start; ppc_literal_escapes; local: *; }; tecla_1.3 { global: ef_list_expansions; } tecla_1.2; tecla_1.4 { global: gl_configure_getline; gl_save_history; gl_load_history; gl_group_history; gl_show_history; gl_resize_history; gl_limit_history; gl_clear_history; gl_toggle_history; gl_watch_fd; libtecla_version; gl_terminal_size; gl_state_of_history; gl_range_of_history; gl_size_of_history; gl_lookup_history; gl_echo_mode; gl_replace_prompt; gl_prompt_style; gl_ignore_signal; gl_trap_signal; gl_last_signal; } tecla_1.3; tecla_l.5 { global: gl_inactivity_timeout; gl_completion_action; gl_register_action; gl_display_text; gl_error_message; gl_return_status; gl_set_term_size; gl_list_signals; gl_catch_blocked; gl_io_mode; gl_raw_io; gl_normal_io; gl_tty_signals; gl_abandon_line; gl_handle_signal; gl_pending_io; gl_bind_keyseq; cpl_recall_matches; gl_erase_terminal; } tecla_1.4; tecla_1.6 { global: gl_append_history; gl_automatic_history; gl_query_char; gl_read_char; } tecla_l.5; yuma123_2.14/libtecla/strngmem.c0000664000175000017500000001525214770023131016707 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include "strngmem.h" #include "freelist.h" struct StringMem { unsigned long nmalloc; /* The number of strings allocated with malloc */ FreeList *fl; /* The free-list */ }; /*....................................................................... * Create a string free-list container and the first block of its free-list. * * Input: * blocking_factor int The blocking_factor argument specifies how * many strings of length SM_STRLEN * bytes (see stringmem.h) are allocated in each * free-list block. * For example if blocking_factor=64 and * SM_STRLEN=16, then each new * free-list block will take 1K of memory. * Output: * return StringMem * The new free-list container, or NULL on * error. */ StringMem *_new_StringMem(unsigned blocking_factor) { StringMem *sm; /* The container to be returned. */ /* * Check arguments. */ if(blocking_factor < 1) { errno = EINVAL; return NULL; }; /* * Allocate the container. */ sm = (StringMem *) malloc(sizeof(StringMem)); if(!sm) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize * the container at least up to the point at which it can safely * be passed to _del_StringMem(). */ sm->nmalloc = 0; sm->fl = NULL; /* * Allocate the free-list. */ sm->fl = _new_FreeList(SM_STRLEN, blocking_factor); if(!sm->fl) return _del_StringMem(sm, 1); /* * Return the free-list container. */ return sm; } /*....................................................................... * Delete a string free-list. * * Input: * sm StringMem * The string free-list to be deleted, or NULL. * force int If force==0 then _del_StringMem() will complain * and refuse to delete the free-list if any * of nodes have not been returned to the free-list. * If force!=0 then _del_StringMem() will not check * whether any nodes are still in use and will * always delete the list. * Output: * return StringMem * Always NULL (even if the list couldn't be * deleted). */ StringMem *_del_StringMem(StringMem *sm, int force) { if(sm) { /* * Check whether any strings have not been returned to the free-list. */ if(!force && (sm->nmalloc > 0 || _busy_FreeListNodes(sm->fl) > 0)) { errno = EBUSY; return NULL; }; /* * Delete the free-list. */ sm->fl = _del_FreeList(sm->fl, force); /* * Delete the container. */ free(sm); }; return NULL; } /*....................................................................... * Allocate an array of 'length' chars. * * Input: * sm StringMem * The string free-list to allocate from. * length size_t The length of the new string (including '\0'). * Output: * return char * The new string or NULL on error. */ char *_new_StringMemString(StringMem *sm, size_t length) { char *string; /* The string to be returned */ int was_malloc; /* True if malloc was used to allocate the string */ /* * Check arguments. */ if(!sm) return NULL; if(length < 1) length = 1; /* * Allocate the new node from the free list if possible. */ if(length < SM_STRLEN) { string = (char *)_new_FreeListNode(sm->fl); if(!string) return NULL; was_malloc = 0; } else { string = (char *) malloc(length+1); /* Leave room for the flag byte */ if(!string) return NULL; /* * Count malloc allocations. */ was_malloc = 1; sm->nmalloc++; }; /* * Use the first byte of the string to record whether the string was * allocated with malloc or from the free-list. Then return the rest * of the string for use by the user. */ string[0] = (char) was_malloc; return string + 1; } /*....................................................................... * Free a string that was previously returned by _new_StringMemString(). * * Input: * sm StringMem * The free-list from which the string was originally * allocated. * s char * The string to be returned to the free-list, or NULL. * Output: * return char * Always NULL. */ char *_del_StringMemString(StringMem *sm, char *s) { int was_malloc; /* True if the string originally came from malloc() */ /* * Is there anything to be deleted? */ if(s && sm) { /* * Retrieve the true string pointer. This is one less than the one * returned by _new_StringMemString() because the first byte of the * allocated memory is reserved by _new_StringMemString as a flag byte * to say whether the memory was allocated from the free-list or directly * from malloc(). */ s--; /* * Get the origination flag. */ was_malloc = s[0]; if(was_malloc) { free(s); s = NULL; sm->nmalloc--; } else { s = (char *) _del_FreeListNode(sm->fl, s); }; }; return NULL; } yuma123_2.14/libtecla/Makefile.am0000664000175000017500000000341214770023131016736 0ustar vladimirvladimirnoinst_LTLIBRARIES = libtecla.la noinst_HEADERS= \ $(top_srcdir)/libtecla/chrqueue.h \ $(top_srcdir)/libtecla/cplfile.h \ $(top_srcdir)/libtecla/cplmatch.h \ $(top_srcdir)/libtecla/direader.h \ $(top_srcdir)/libtecla/errmsg.h \ $(top_srcdir)/libtecla/expand.h \ $(top_srcdir)/libtecla/freelist.h \ $(top_srcdir)/libtecla/getline.h \ $(top_srcdir)/libtecla/hash.h \ $(top_srcdir)/libtecla/history.h \ $(top_srcdir)/libtecla/homedir.h \ $(top_srcdir)/libtecla/ioutil.h \ $(top_srcdir)/libtecla/keytab.h \ $(top_srcdir)/libtecla/libtecla.h \ $(top_srcdir)/libtecla/pathutil.h \ $(top_srcdir)/libtecla/stringrp.h \ $(top_srcdir)/libtecla/strngmem.h libtecla_la_SOURCES = \ $(top_srcdir)/libtecla/chrqueue.c \ $(top_srcdir)/libtecla/cplfile.c \ $(top_srcdir)/libtecla/cplmatch.c \ $(top_srcdir)/libtecla/direader.c \ $(top_srcdir)/libtecla/enhance.c \ $(top_srcdir)/libtecla/errmsg.c \ $(top_srcdir)/libtecla/expand.c \ $(top_srcdir)/libtecla/freelist.c \ $(top_srcdir)/libtecla/getline.c \ $(top_srcdir)/libtecla/hash.c \ $(top_srcdir)/libtecla/history.c \ $(top_srcdir)/libtecla/homedir.c \ $(top_srcdir)/libtecla/ioutil.c \ $(top_srcdir)/libtecla/keytab.c \ $(top_srcdir)/libtecla/pathutil.c \ $(top_srcdir)/libtecla/pcache.c \ $(top_srcdir)/libtecla/stringrp.c \ $(top_srcdir)/libtecla/strngmem.c \ $(top_srcdir)/libtecla/version.c libtecla_la_CPPFLAGS = -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/libtecla -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DUSE_TERMINFO=1 -DHAVE_CURSES_H=1 -DHAVE_TERM_H=1 -DHAVE_SYS_SELECT_H=1 -DHAVE_SELECT=1 -DHAVE_SYSV_PTY=1 -D_SVID_SOURCE -D_BSD_SOURCE libtecla_la_LDFLAGS = -static yuma123_2.14/libtecla/RELEASE.NOTES0000664000175000017500000007020314770023131016576 0ustar vladimirvladimirThis file lists major changes which accompany each new release. Version 1.6.1: This is primarily a minor bug-fix release. One added feature is the ability to call gl_normal_io() from callbacks registered by gl_watch_fd() and gl_inactivity_timeout(). This allows these callbacks to cleanly suspend line editing before either reading from the terminal, or writing to the terminal; and then subsequently causes the input line to be automatically redisplayed, and line-editing to be resumed by gl_get_line(), as soon as the callback returns. Another minor change is that if the terminal type specified in the TERM environment variable is set to "dumb", gl_get_line() now treats the terminal as though it were a non-interactive stream, rather than treating it as a VT100-compatible terminal. This means that it doesn't either prompt for input, or perform any command-line editing, even when it really is interacting with a terminal. This is aimed at the rare situation where a third-pary program that connects to libtecla through an embedded pseudo-terminal, needs to be forced to behave as though it weren't talking to a terminal, in order that it be useable in non-interactive scripts. Note that in the previous release, the optional configuration function, gl_tty_signals(), was incorrectly swapping the suspend and terminal signal handlers before installing them. A configuration problem that prevented select() from being used under MacOS X, has been fixed. Although not documented in the man page, it was meant to be possible to take the input line that one call to gl_get_line() returned, and ask the next call to gl_get_line() to present it back to the user for re-editing, simply by passing the pointer returned by one call to gl_get_line() as the start_line argument of the next call to gl_get_line(). This feature unfortunately stopped working in 1.6.0, so this release restores it, and officially documents it in the man page documentation of gl_get_line(). In the previous version of the library, calling gl_terminal_size() on a system without SIGWINCH support, would crash the application. This has been fixed. Libtecla now apparently compiles cleanly under IRIX. Version 1.6.0: This release is primarily a bug-fix release. However there are also four new functions, so the minor version number has been incremented to reflect this. Two of the new functions are gl_automatic_history() and gl_append_history(). The former of these functions allows the application to tell gl_get_line() not to automatically archive entered lines in the history list. The second of these functions allows the application to explicitly append a line to the history list. Thus together, these two functions allow the calling application to take over control of what is placed in the history list. The third new function is gl_query_char(), which prompts the user for a single character reply, which the user can then type without having to hit return to enter it. Unless echoing is disabled, the character that is entered is then displayed after the prompt, and a newline is started. Finally, the 4th new function is gl_read_char(), which also reads a single character from the user, but doesn't prompt the user, write anything to the terminal, or disturb any partially entered input line. It is thus safe to call this function not only from between calls to gl_get_line(), but also from application callback functions, even if gl_normal_io() hasn't been called. When using the history-search-backwards or history-search-forwards actions, if the search prefix that the user typed, contains any of the *,? or [ globbing characters, it is now treated as a glob pattern to be matched against historical lines, instead of a simple prefix. I have added a --without-file-system option to the configure script. This is intended for use in embedded systems that either don't have filesystems, or where the file-system code in libtecla is seen as unwanted bloat. See the INSTALL document for details. Similarly, I also added a --without-file-actions option to the configure script. This allows the application author/installer to prevent users of gl_get_line() from accessing the filesystem with the builtin actions of gl_get_line(). It does this by removing a number of action functions, such as expand-filename, and list-glob, and by changing the default behavior of other actions, such as complete-word and list-or-eof, to show no completions. Now to the bugs that have been fixed. Version 1.5.0 had a lot of big internal changes, so there are a number of bugs that needed to be fixed. There was a bug which caused a crash if gl_load_history() was called multiple times. There was another bug which caused a prompt not to be displayed on the next line after switching from reading input from a file to reading from the terminal. Also, in tecla configuration files, backslash escaped characters within key-binding key-sequences weren't being escaped. Thus ^\\ got interpretted as a control-\ followed by a \ character instead of as a control-\. There was a bug in the history recall mechanism which caused the search prefix to be forgotten in certain complicated usage scenarios. There was a minor memory leak in the gl_configure_getline() function. Finally, if gl_get_line() was aborted by a signal, or any other abnormal event, the value of errno which originally indicated what had happened, got zeroed by the code that restored the terminal to a usable state. Thus the application couldn't figure out what had caused the error, apart from by looking at gl_return_status(). All of these bugs have been fixed. In the Makefile, there were a number of places where install-sh was invoked without a path prefix. This has now been remedied. A fully functional workaround for a bug in Solaris' terminal I/O code has also been implemented. This bug, which only manifested itself in libtecla's uncommonly used non-blocking server I/O mode, caused characters entered while in normal I/O mode, between calls to gl_get_line() to be invisible to the next call to gl_get_line(), until the user typed at least one more key after raw terminal mode was restored. The Gnu autoconf config.guess and config.sub scripts have been updated to their latest versions. Apparently the old versions that I was previously using were too old to know about certain BSD ports. Version 1.5.0: This release includes several major new features for those using gl_get_line(), shared library support in Darwin, better cross compilation support, and various minor bug fixes. The biggest new feature is the option of a non-blocking I/O mode, in which gl_get_line() can safely be called from an application's external event-loop to incrementally read input lines from the user. This feature is documented in the gl_io_mode(3) man page. In addition, there is now support for the definition of additional word-completion action functions, which can then be bound to different keys. See the documentation of the gl_completion_action() function in the gl_get_line(3) man page. Externally defined action functions can also be defined, although presently they don't have write access to the input line, so they are restricted to operations that display information text to the terminal, or modify the environment of the calling application in some way. See the documentation of the gl_register_action() function in the gl_get_line(3) man page. Some of the non-blocking I/O support functions can also be used for improved signal handling in the normal blocking mode. In particular, the gl_list_signals() and gl_catch_blocked() functions make it easier to write reliable signal handling around gl_get_line(). The new "RELIABLE SIGNAL HANDLING" section of the gl_get_line(3) man page is intended as an introduction to this subject. Programs can now clear the terminal between calls to gl_get_line(), by calling the new gl_erase_terminal() function. The gl_display_text() function, now used in the demos to display introductory banners, is provided for formatting text according to the width of the terminal. It is now possible to install inactivity timeout callbacks in gl_get_line(), using the new gl_inactivity_timeout() function. The new gl_set_term_size() function allows the application to explicitly set the terminal size, for cases, such as when one is using a terminal at the end of a serial lineq, where the terminal driver doesn't send the process a SIGWINCH when the terminal size changes. The new gl_bind_keyseq() function provides a convenient alternative to gl_configure_getline(), for binding or unbinding one key-sequence at a time. gl_get_line()s signal handling, file-descriptor event-handling, inactivity-timeout handling and server-mode non-blocking I/O features now not only work when input is coming from a terminal, but now also work when input is coming from non-interactive streams, such as files and pipes. The history implementation has been re-written to make it more efficient and easier to modify. The biggest user-level change is that when recalling history lines using a search prefix, the same line is no longer returned more than once in a row. Previously this duplicate elimination only worked when one was recalling a line without specifying a search prefix, and this was naively performed by preventing neighboring duplicates from existing in the history list, rather than by skipping duplicates at search time. In previous versions of the library, when gl_get_line() and its associated public functions detected invalid arguments, or couldn't allocate memory, etc, error messages were written to stderr. This isn't appropriate for library functions, so instead of writing such messages to stderr, these messages are now recorded in buffers within the affected GetLine object. The latest error message can then subsequently be queried by calling gl_error_message(). The use of errno has also been expanded, and a new function called gl_return_status() has been provided to expand on the cause of the last return from gl_get_line(). User level usage and configuration information has now been split out of the gl_get_line(3) man page into a separate tecla(7) man page. The enhance(3) man page has also been renamed to enhance(1). When expanding "~/", gl_get_line() now checks for, and returns the value of the HOME environment variable, if it exists, in preference to looking up the directory of the current user in the password file. When the terminal was resized to a narrower width, previous versions of gl_get_line() would redraw the line higher up the terminal. This bug has been fixed. A bug in history recall has also been fixed, in which an error message was being generated if one attempted to recall a line while the cursor was at the end of the longest possible input line. A more serious bug, in which callbacks registered by gl_watch_fd() weren't being called for write-events, has also been fixed. Finally, a few minor fixes have been made to improve support under QNX and Mac OS X. Beware that in this release, much of the underlying code has undergone some radical re-work, so although backwards compatibility of all documented features has been preserved, there may be some lingering bugs that could break existing programs. So, if you plan to use this version in production code, please test it as far as possible within your application before releasing it to your clients, and as always, please report any unexpected behavior. Version 1.4.1: This is a maintenance release. It includes minor changes to support Mac OS X (Darwin), the QNX real-time operating system, and Cygwin under Windows. It also fixes an oversight that was preventing the tab key from inserting tab characters when users unbound the complete-word action from it. Version 1.4.0: The contents of the history list can now be saved and restored with the new gl_save_history() and gl_load_history() functions. Event handlers can now be registered to watch for and respond to I/O on arbitrary file descriptors while gl_get_line() is waiting for terminal input from the user. See the gl_get_line(3) man page for details on gl_watch_fd(). As an optional alternative to getting configuration information only from ~/.teclarc, the new gl_configure_getline() function allows configuration commands to be taken from any of, a string, a specified application-specific file, and/or a specified user-specific file. See the gl_get_line(3) man page for details. The version number of the library can now be queried using the libtecla_version() function. See the libtecla(3) man page. The new gl_group_history() function allows applications to group different types of input line in the history buffer, and arrange for only members of the appropriate group to be recalled on a given call to gl_get_line(). See the gl_get_line(3) man page. The new gl_show_history() function displays the current history list to a given stdio output stream. See the gl_get_line(3) man page. new_GetLine() now allows you to specify a history buffer size of zero, thus requesting that no history buffer be allocated. You can subsequently resize or delete the history buffer at any time, by calling gl_resize_history(), limit the number of lines that are allowed in the buffer by calling gl_limit_history(), clear either all history lines from the history list, or just the history lines that are associated with the current history group, by calling gl_clear_history, and toggle the history mechanism on and off by calling gl_toggle_history(). The new gl_terminal_size() function can be used to query the current terminal size. It can also be used to supply a default terminal size on systems where no mechanism is available for looking up the size. The contents and configuration of the history list can now be obtained by the calling application, by calling the new gl_lookup_history(), gl_state_of_history(), gl_range_of_history() and gl_size_of_history() functions. See the gl_get_line(3) man page. Echoing of the input line as it is typed, can now be turned on and off via the new gl_echo_mode() function. While echoing is disabled, newly entered input lines are omitted from the history list. See the gl_get_line(3) man page. While the default remains to display the prompt string literally, the new gl_prompt_style() function can be used to enable text attribute formatting directives in prompt strings, such as underlining, bold font, and highlighting directives. Signal handling in gl_get_line() is now customizable. The default signal handling behavior remains essentially the same, except that the SIGTSTP, SIGTTIN and SIGTTOU are now forwarded to the corresponding signal handler of the calling program, instead of causing a SIGSTOP to be sent to the application. It is now possible to remove signals from the list that are trapped by gl_get_line(), as well as add new signals to this list. The signal and terminal environments in which the signal handler of the calling program is invoked, and what gl_get_line() does after the signal handler returns, is now customizable on a per signal basis. You can now also query the last signal that was caught by gl_get_line(). This is useful when gl_get_line() aborts with errno=EINTR, and you need to know which signal caused it to abort. Key-sequences bound to action functions can now start with printable characters. Previously only keysequences starting with control or meta characters were permitted. gl_get_line() is now 8-bit clean. If the calling program has correctly called setlocale(LC_CTYPE,""), then the user can select an alternate locale by setting the standard LC_CTYPE, LC_ALL, or LANG environment variables, and international characters can then be entered directly, either by using a non-US keyboard, or by using a compose key on a standard US keyboard. Note that in locales in which meta characters become printable, meta characters no longer match M-c bindings, which then have to be entered using their escape-c equivalents. Fortunately most modern terminal emulators either output the escape-c version by default when the meta key is used, or can be configured to do so (see the gl_get_line(3) man page), so in most cases you can continue to use the meta key. Completion callback functions can now tell gl_get_line() to return the input line immediately after a successful tab completion, simply by setting the last character of the optional continuation suffix to a newline character (ie. in the call to cpl_add_completion()). It is now safe to create and use multiple GetLine objects, albeit still only from a single thread. In conjunction with the new gl_configure_getline() function, this optionally allows multiple GetLine objects with different bindings to be used to implement different input modes. The edit-mode configuration command now accepts the argument, none. This tells gl_get_line() to revert to using just the native line editing facilities provided by the terminal driver. This could be used if the termcap or terminfo entry of the host terminal were badly corrupted. Application callback functions invoked by gl_get_line() can now change the displayed prompt using the gl_replace_prompt() function. Their is now an optional program distributed with the library. This is a beta release of a program which adds tecla command-line editing to virtually any third party application without the application needing to be linked to the library. See the enhance(3) man page for further details. Although built and installed by default, the INSTALL document explains how to prevent this. The INSTALL document now explains how you can stop the demo programs from being built and installed. NetBSD/termcap fixes. Mike MacFaden reported two problems that he saw when compiling libtecla under NetBSD. Both cases were related to the use of termcap. Most systems use terminfo, so this problem has gone unnoticed until now, and won't have affected the grand majority of users. The configure script had a bug which prevented the check for CPP working properly, and getline.c wouldn't compile due to an undeclared variable when USE_TERMCAP was defined. Both problems have now been fixed. Note that if you successfully compiled version 1.3.3, this problem didn't affect you. An unfortunate and undocumented binding of the key-sequence M-O was shadowing the arrow-key bindings on systems that use ^[OA etc. I have removed this binding (the documented lower case M-o binding remains bound). Under the KDE konsole terminal this was causing the arrow keys to do something other than expected. There was a bug in the history list code which could result in strange entries appearing at the start of the history list once enough history lines had been added to the list to cause the circular history buffer to wrap. This is now fixed. Version 1.3.3: Signal handling has been re-written, and documentation of its behaviour has been added to the gl_get_line(3) man page. In addition to eliminating race conditions, and appropriately setting errno for those signals that abort gl_get_line(), many more signals are now intercepted, making it less likely that the terminal will be left in raw mode by a signal that isn't trapped by gl_get_line(). A bug was also fixed that was leaving the terminal in raw mode if the editing mode was changed interactively between vi and emacs. This was only noticeable when running programs from old shells that don't reset terminal modes. Version 1.3.2: Tim Eliseo contributed a number of improvements to vi mode, including a fuller set of vi key-bindings, implementation of the vi constraint that the cursor can't backup past the point at which input mode was entered, and restoration of overwritten characters when backspacing in overwrite mode. There are also now new bindings to allow users to toggle between vi and emacs modes interactively. The terminal bell is now used in some circumstances, such as when an unrecognized key sequence is entered. This can be turned off by the new nobeep option in the tecla configuration file. Unrelated to the above, a problem under Linux which prevented ^Q from being used to resume terminal output after the user had pressed ^S, has been fixed. Version 1.3.1: In vi mode a bug was preventing the history-search-backward and history-search-forward actions from doing anything when invoked on empty lines. On empty lines they now act like up-history and down-history respectively, as in emacs mode. When creating shared libraries under Linux, the -soname directive was being used incorrectly. The result is that Linux binaries linked with the 1.2.3, 1.2.4 and 1.3.0 versions of the tecla shared libraries, will refuse to see other versions of the shared library until relinked with version 1.3.1 or higher. The configure script can now handle the fact that under Solaris-2.6 and earlier, the only curses library is a static one that hides in /usr/ccs/lib. Under Linux it now also caters for old versions of GNU ld which don't accept version scripts. The demos are now linked against the shared version of the library if possible. Previously they were always linked with the static version. Version 1.3.0: The major change in this release is the addition of an optional vi command-line editing mode in gl_get_line(), along with lots of new action functions to support its bindings. To enable this, first create a ~/.teclarc file if you don't already have one, then add the following line to it. edit-mode vi The default vi bindings, which are designed to mimic those of the vi editor as closely as possible, are described in the gl_get_line(3) man page. A new convenience function called ef_list_expansions() has been added for listing filename expansions. See the ef_list_expansions(3) man page for details. This is used in a new list-glob binding, bound to ^Xg in emacs mode, and ^G in vi input mode. A bug has been fixed in the key-binding table expansion code. This bug would have caused problems to anybody who defined more than about 18 personalized key-bindings in their ~/.teclarc file. Version 1.2.4: Buffered I/O is now used for writing to terminals, and where supported, cursor motion is done with move-n-positions terminfo capabilities instead of doing lots of move-1-position requests. This greatly improves how the library feels over slow links. You can now optionally compile different architectures in different directories, without having to make multiple copies of the distribution. This is documented in the INSTALL file. The ksh ~+ directive is now supported. Thanks to Markus Gyger for the above improvements. Documentation has been added to the INSTALL file describing features designed to facilitate configuration and installation of the library as part of larger packages. These features are intended to remove the need to modify the tecla distribution's configuration and build procedures when embedding the libtecla distribution in other package distributions. A previous fix to stop the cursor from warping when the last character of the input line was in the last column of the terminal, was only being used for the first terminal line of the input line. It is now used for all subsequent lines as well, as originally intended. Version 1.2.3: The installation procedure has been better automated with the addition of an autoconf configure script. This means that installers can now compile and install the library by typing: ./configure make make install On all systems this makes at least the normal static version of the tecla library. It also makes the reentrant version if reentrant POSIX functions are detected. Under Solaris, Linux and HP-UX the configuration script arranges for shared libraries to be compiled in addition to the static libraries. It is hoped that installers will return information about how to compile shared libraries on other systems, for inclusion in future releases, and to this end, a new PORTING guide has been provided. The versioning number scheme has been changed. This release would have been 1.2c, but instead will be refered to as 1.2.3. The versioning scheme, based on conventions used by Sun Microsystems, is described in configure.in. The library was also tested under HP-UX, and this revealed two serious bugs, both of which have now been fixed. The first bug prevented the library from writing control codes to terminals on big-endian machines, with the exception of those running under Solaris. This was due to an int variable being used where a char was needed. The second bug had the symptom that on systems that don't use the newline character as the control code for moving the cursor down a line, a newline wasn't started when the user hit enter. Version 1.2b: Two more minor bug fixes: Many terminals don't wrap the cursor to the next line when a character is written to the rightmost terminal column. Instead, they delay starting a new line until one more character is written, at which point they move the cursor two positions. gl_get_line() wasn't aware of this, so cursor repositionings just after writing the last character of a column, caused it to erroneously go up a line. This has now been remedied, using a method that should work regardless of whether a terminal exhibits this behavior or not. Some systems dynamically record the current terminal dimensions in environment variables called LINES and COLUMNS. On such systems, during the initial terminal setup, these values should override the static values read from the terminal information databases, and now do. Previously they were only used if the dimensions returned by terminfo/termcap looked bogus. Version 1.2a: This minor release fixes the following two bugs: The initial terminal size and subsequent changes thereto, weren't being noticed by gl_get_line(). This was because the test for the existence of TIOCWINSZ was erroneously placed before the inclusion of termios.h. One of the results was that on input lines that spanned more than one terminal line, the cursor occasionally jumped unexpectedly to the previous terminal line. On entering a line that wrapped over multiple terminal lines, gl_get_line() simply output a carriage-return line-feed at the point at which the user pressed return. Thus if one typed in such a line, then moved back onto one of the earlier terminal lines before hitting return, the cursor was left on a line containing part of the line that had just been entered. This didn't do any harm, but it looked a mess. Version 1.2: A new facility for looking up and completing filenames in UNIX-style paths has now been added (eg. you can search for, or complete commands using the UNIX PATH environment variable). See the pca_lookup_file(3) man page. The already existing filename completion callback can now be made selective in what types of files it lists. See the cpl_complete_word(3) man page. Due to its potential to break applications when changed, the use of the publically defined CplFileArgs structure to configure the cpl_file_completions() callback is now deprecated. The definition of this structure has been frozen, and its documentation has been removed from the man pages. It will remain supported, but if you have used it, you are recommended to switch to the new method, which involves a new opaque configuration object, allocated via a provided constructor function, configured via accessor functions, and eventually deleted with a provided destructor function. The cpl_file_completions() callback distinguishes which structure type it has been sent by virtue of a code placed at the start of the new structure by the constructor. It is assumed that no existing applications set the boolean 'escaped' member of the CplFileArgs structure to 4568. The new method is documented in the cpl_complete_word(3) man page. Version 1.1j This was the initial public release on freshmeat.org. yuma123_2.14/libtecla/ioutil.h0000664000175000017500000000632714770023131016370 0ustar vladimirvladimir#ifndef ioutil_h #define ioutil_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /*....................................................................... * Callback functions of the following type can be registered to write * to a terminal, when the default blocking writes to a local terminal * aren't appropriate. In particular, if you don't want gl_get_line() * to block, then this function should return before writing the * specified number of characters if doing otherwise would involve * waiting. * * Input: * data void * The anonymous data pointer that was registered with * this callback function. * s const char * The string to be written. Beware that this string * may not have a terminating '\0' character. * n int The length of the prefix of s[] to attempt to * write. * Output: * return int The number of characters written from s[]. This * should normally be a number in the range 0 to n. * To signal that an I/O error occurred, return -1. */ #define GL_WRITE_FN(fn) int (fn)(void *data, const char *s, int n) typedef GL_WRITE_FN(GlWriteFn); /* * The following output callback function requires a (FILE *) callback * data argument, and writes to this stream using the fwrite stdio * function. */ GL_WRITE_FN(_io_write_stdio); /* * Left justify text within the bounds of the terminal adding optional * indentation, prefixes and suffixes to each line if requested. */ int _io_display_text(GlWriteFn *write_fn, void *data, int indentation, const char *prefix, const char *suffix, int fill_char, int term_width, int start, const char *string); #endif yuma123_2.14/libtecla/Makefile.rules0000664000175000017500000001331414770023131017475 0ustar vladimirvladimirdefault: $(OBJDIR) $(TARGETS) $(DEMOS) $(PROGRAMS) #----------------------------------------------------------------------- # You shouldn't need to change anything in this file. #----------------------------------------------------------------------- # Create the directory in which the object files will be created. $(OBJDIR): mkdir $(OBJDIR) # Construct the compilation command. COMPILE = $(CC) -c $(CFLAGS) -o $@ LIB_OBJECTS = $(OBJDIR)/getline.o $(OBJDIR)/keytab.o $(OBJDIR)/freelist.o \ $(OBJDIR)/strngmem.o $(OBJDIR)/hash.o $(OBJDIR)/history.o \ $(OBJDIR)/direader.o $(OBJDIR)/homedir.o $(OBJDIR)/pathutil.o \ $(OBJDIR)/expand.o $(OBJDIR)/stringrp.o $(OBJDIR)/cplfile.o \ $(OBJDIR)/cplmatch.o $(OBJDIR)/pcache.o $(OBJDIR)/version.o \ $(OBJDIR)/chrqueue.o $(OBJDIR)/ioutil.o $(OBJDIR)/errmsg.o # List the available demonstration programs. DEMO_PROGS = demo$(SUFFIX) demo2$(SUFFIX) demo3$(SUFFIX) # List all of the programs that this makefile can build. PROGS = $(DEMO_PROGS) enhance$(SUFFIX) static: libtecla$(SUFFIX).a libtecla$(SUFFIX).a: $(LIB_OBJECTS) ar -ru $@ $(LIB_OBJECTS); \ $(RANLIB) $@; \ rm -f $(PROGS) shared: libtecla$(SUFFIX)$(SHARED_EXT) libtecla$(SUFFIX)$(SHARED_EXT): $(LIB_OBJECTS) $(srcdir)/libtecla.map \ libtecla.map.opt $(LINK_SHARED) @endings="$(SHARED_ALT)" ; \ for alt in $$endings ; do \ lnk="libtecla$(SUFFIX)$$alt"; \ echo "rm -f $$lnk; $(LN_S) $@ $$lnk"; \ rm -f $$lnk; $(LN_S) $@ $$lnk; \ done; \ rm -f $(PROGS) libtecla.map.opt: $(srcdir)/libtecla.map sed -n 's/^[ ]*\([_a-zA-Z0-9]*\)[ ]*;.*/+e \1/p' $? >$@ demos: $(DEMO_PROGS) demo$(SUFFIX): $(OBJDIR)/demo.o LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ $(OBJDIR)/demo.o -L. -ltecla$(SUFFIX) $(LIBS) demo2$(SUFFIX): $(OBJDIR)/demo2.o LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ $(OBJDIR)/demo2.o -L. -ltecla$(SUFFIX) $(LIBS) demo3$(SUFFIX): $(OBJDIR)/demo3.o LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ $(OBJDIR)/demo3.o -L. -ltecla$(SUFFIX) $(LIBS) enhance$(SUFFIX): $(OBJDIR)/enhance.o LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ $(OBJDIR)/enhance.o -L. -ltecla$(SUFFIX) $(LIBS) #----------------------------------------------------------------------- # Object file dependencies. #----------------------------------------------------------------------- $(OBJDIR)/getline.o: $(srcdir)/getline.c $(srcdir)/pathutil.h \ $(srcdir)/libtecla.h $(OBJDIR)/keytab.h $(srcdir)/history.h \ $(srcdir)/freelist.h $(srcdir)/stringrp.h $(srcdir)/getline.h \ $(srcdir)/ioutil.h $(srcdir)/chrqueue.h $(srcdir)/cplmatch.h \ $(srcdir)/expand.h $(srcdir)/errmsg.h $(COMPILE) $(srcdir)/getline.c $(OBJDIR)/keytab.o: $(srcdir)/keytab.c $(OBJDIR)/keytab.h \ $(srcdir)/strngmem.h $(srcdir)/getline.h $(srcdir)/errmsg.h \ $(srcdir)/hash.h $(COMPILE) $(srcdir)/keytab.c $(OBJDIR)/strngmem.o: $(srcdir)/strngmem.c $(srcdir)/strngmem.h \ $(srcdir)/freelist.h $(COMPILE) $(srcdir)/strngmem.c $(OBJDIR)/freelist.o: $(srcdir)/freelist.c $(srcdir)/freelist.h $(COMPILE) $(srcdir)/freelist.c $(OBJDIR)/hash.o: $(srcdir)/hash.c $(srcdir)/hash.h $(srcdir)/strngmem.h \ $(srcdir)/freelist.h $(COMPILE) $(srcdir)/hash.c $(OBJDIR)/history.o: $(srcdir)/history.c $(srcdir)/ioutil.h \ $(srcdir)/history.h $(srcdir)/freelist.h $(srcdir)/errmsg.h $(COMPILE) $(srcdir)/history.c $(OBJDIR)/expand.o: $(srcdir)/expand.c $(srcdir)/freelist.h \ $(srcdir)/direader.h $(srcdir)/pathutil.h $(srcdir)/homedir.h \ $(srcdir)/stringrp.h $(srcdir)/libtecla.h $(srcdir)/ioutil.h \ $(srcdir)/expand.h $(srcdir)/errmsg.h $(COMPILE) $(srcdir)/expand.c $(OBJDIR)/direader.o: $(srcdir)/direader.c $(srcdir)/direader.h \ $(srcdir)/errmsg.h $(COMPILE) $(srcdir)/direader.c $(OBJDIR)/homedir.o: $(srcdir)/homedir.c $(srcdir)/pathutil.h \ $(srcdir)/homedir.h $(srcdir)/errmsg.h $(COMPILE) $(srcdir)/homedir.c $(OBJDIR)/pathutil.o: $(srcdir)/pathutil.c $(srcdir)/pathutil.h $(COMPILE) $(srcdir)/pathutil.c $(OBJDIR)/stringrp.o: $(srcdir)/stringrp.c $(srcdir)/freelist.h \ $(srcdir)/stringrp.h $(COMPILE) $(srcdir)/stringrp.c $(OBJDIR)/cplfile.o: $(srcdir)/cplfile.c $(srcdir)/libtecla.h \ $(srcdir)/direader.h $(srcdir)/homedir.h $(srcdir)/pathutil.h \ $(srcdir)/cplfile.h $(srcdir)/errmsg.h $(COMPILE) $(srcdir)/cplfile.c $(OBJDIR)/cplmatch.o: $(srcdir)/cplmatch.c $(srcdir)/libtecla.h \ $(srcdir)/ioutil.h $(srcdir)/stringrp.h $(srcdir)/pathutil.h \ $(srcdir)/cplfile.h $(srcdir)/cplmatch.h $(srcdir)/errmsg.h $(COMPILE) $(srcdir)/cplmatch.c $(OBJDIR)/pcache.o: $(srcdir)/pcache.c $(srcdir)/libtecla.h \ $(srcdir)/pathutil.h $(srcdir)/homedir.h $(srcdir)/freelist.h \ $(srcdir)/direader.h $(srcdir)/stringrp.h $(errmsg.h) $(COMPILE) $(srcdir)/pcache.c $(OBJDIR)/demo.o: $(srcdir)/demo.c $(srcdir)/libtecla.h $(COMPILE) $(srcdir)/demo.c $(OBJDIR)/demo2.o: $(srcdir)/demo2.c $(srcdir)/libtecla.h $(COMPILE) $(srcdir)/demo2.c $(OBJDIR)/demo3.o: $(srcdir)/demo3.c $(srcdir)/libtecla.h $(COMPILE) $(srcdir)/demo3.c $(OBJDIR)/version.o: $(srcdir)/version.c $(srcdir)/libtecla.h $(COMPILE) $(srcdir)/version.c $(OBJDIR)/enhance.o: $(srcdir)/enhance.c $(srcdir)/libtecla.h $(COMPILE) $(srcdir)/enhance.c $(OBJDIR)/chrqueue.o: $(srcdir)/chrqueue.c $(srcdir)/ioutil.h \ $(srcdir)/chrqueue.h $(srcdir)/freelist.h $(srcdir)/errmsg.h $(COMPILE) $(srcdir)/chrqueue.c $(OBJDIR)/ioutil.o: $(srcdir)/ioutil.c $(srcdir)/ioutil.h $(COMPILE) $(srcdir)/ioutil.c $(OBJDIR)/errmsg.o: $(srcdir)/errmsg.c $(srcdir)/errmsg.h $(COMPILE) $(srcdir)/errmsg.c #----------------------------------------------------------------------- # Include file dependencies. #----------------------------------------------------------------------- $(OBJDIR)/keytab.h: $(srcdir)/keytab.h $(srcdir)/libtecla.h cp $(srcdir)/keytab.h $@ yuma123_2.14/libtecla/freelist.c0000664000175000017500000002752314770023131016674 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include "freelist.h" typedef struct FreeListBlock FreeListBlock; struct FreeListBlock { FreeListBlock *next; /* The next block in the list */ char *nodes; /* The array of free-list nodes */ }; struct FreeList { size_t node_size; /* The size of a free-list node */ unsigned blocking_factor; /* The number of nodes per block */ long nbusy; /* The number of nodes that are in use */ long ntotal; /* The total number of nodes in the free list */ FreeListBlock *block; /* The head of the list of free-list blocks */ void *free_list; /* The free-list of nodes */ }; static FreeListBlock *_new_FreeListBlock(FreeList *fl); static FreeListBlock *_del_FreeListBlock(FreeListBlock *fl); static void _thread_FreeListBlock(FreeList *fl, FreeListBlock *block); /*....................................................................... * Allocate a new free-list from blocks of 'blocking_factor' objects of size * node_size. * * Input: * node_size size_t The size of the free-list nodes to be returned * by _new_FreeListNode(). Use sizeof() to * determine this. * blocking_factor unsigned The number of objects of size 'object_size' * to allocate per block. * Output: * return FreeList * The new freelist, or NULL on error. */ FreeList *_new_FreeList(size_t node_size, unsigned blocking_factor) { FreeList *fl; /* The new free-list container */ /* * When a free-list node is on the free-list, it is used as a (void *) * link field. Roundup node_size to a mulitple of the size of a void * pointer. This, plus the fact that the array of nodes is obtained via * malloc, which returns memory suitably aligned for any object, will * ensure that the first sizeof(void *) bytes of each node will be * suitably aligned to use as a (void *) link pointer. */ node_size = sizeof(void *) * ((node_size + sizeof(void *) - 1) / sizeof(void *)); /* * Enfore a minimum block size. */ if(blocking_factor < 1) blocking_factor = 1; /* * Allocate the container of the free list. */ fl = (FreeList *) malloc(sizeof(FreeList)); if(!fl) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_FreeList(). */ fl->node_size = node_size; fl->blocking_factor = blocking_factor; fl->nbusy = 0; fl->ntotal = 0; fl->block = NULL; fl->free_list = NULL; /* * Allocate the first block of memory. */ fl->block = _new_FreeListBlock(fl); if(!fl->block) { errno = ENOMEM; return _del_FreeList(fl, 1); }; /* * Add the new list of nodes to the free-list. */ fl->free_list = fl->block->nodes; /* * Return the free-list for use. */ return fl; } /*....................................................................... * Re-thread a freelist to reclaim all allocated nodes. * This function should not be called unless if it is known that none * of the currently allocated nodes are still being used. * * Input: * fl FreeList * The free-list to be reset, or NULL. */ void _rst_FreeList(FreeList *fl) { if(fl) { FreeListBlock *block; /* * Re-thread the nodes of each block into individual free-lists. */ for(block=fl->block; block; block=block->next) _thread_FreeListBlock(fl, block); /* * Link all of the block freelists into one large freelist. */ fl->free_list = NULL; for(block=fl->block; block; block=block->next) { /* * Locate the last node of the current block. */ char *last_node = block->nodes + fl->node_size * (fl->blocking_factor - 1); /* * Make the link-field of the last node point to the first * node of the current freelist, then make the first node of the * new block the start of the freelist. */ *(void **)last_node = fl->free_list; fl->free_list = block->nodes; }; /* * All allocated nodes have now been returned to the freelist. */ fl->nbusy = 0; }; } /*....................................................................... * Delete a free-list. * * Input: * fl FreeList * The free-list to be deleted, or NULL. * force int If force==0 then _del_FreeList() will complain * and refuse to delete the free-list if any * of nodes have not been returned to the free-list. * If force!=0 then _del_FreeList() will not check * whether any nodes are still in use and will * always delete the list. * Output: * return FreeList * Always NULL (even if the list couldn't be * deleted). */ FreeList *_del_FreeList(FreeList *fl, int force) { if(fl) { /* * Check whether any nodes are in use. */ if(!force && _busy_FreeListNodes(fl) != 0) { errno = EBUSY; return NULL; }; /* * Delete the list blocks. */ { FreeListBlock *next = fl->block; while(next) { FreeListBlock *block = next; next = block->next; block = _del_FreeListBlock(block); }; }; fl->block = NULL; fl->free_list = NULL; /* * Discard the container. */ free(fl); }; return NULL; } /*....................................................................... * Allocate a new object from a free-list. * * Input: * fl FreeList * The free-list to return an object from. * Output: * return void * A new object of the size that was specified via * the node_size argument of _new_FreeList() when * the free-list was created, or NULL if there * is insufficient memory, or 'fl' is NULL. */ void *_new_FreeListNode(FreeList *fl) { void *node; /* The node to be returned */ /* * Check arguments. */ if(!fl) return NULL; /* * If the free-list has been exhausted extend it by allocating * another block of nodes. */ if(!fl->free_list) { FreeListBlock *block = _new_FreeListBlock(fl); if(!block) return NULL; /* * Prepend the new block to the list of free-list blocks. */ block->next = fl->block; fl->block = block; /* * Add the new list of nodes to the free-list. */ fl->free_list = fl->block->nodes; }; /* * Remove and return a node from the front of the free list. */ node = fl->free_list; fl->free_list = *(void **)node; /* * Record the loss of a node from the free-list. */ fl->nbusy++; /* * Return the node. */ return node; } /*....................................................................... * Return an object to the free-list that it was allocated from. * * Input: * fl FreeList * The free-list from which the object was taken. * object void * The node to be returned. * Output: * return void * Always NULL. */ void *_del_FreeListNode(FreeList *fl, void *object) { /* * Check arguments. */ if(!fl) return NULL; /* * Return the node to the head of the free list. */ if(object) { *(void **)object = fl->free_list; fl->free_list = object; /* * Record the return of the node to the free-list. */ fl->nbusy--; }; return NULL; } /*....................................................................... * Return a count of the number of nodes that are currently allocated. * * Input: * fl FreeList * The list to count wrt, or NULL. * Output: * return long The number of nodes (or 0 if fl==NULL). */ long _busy_FreeListNodes(FreeList *fl) { return fl ? fl->nbusy : 0; } /*....................................................................... * Query the number of allocated nodes in the freelist which are * currently unused. * * Input: * fl FreeList * The list to count wrt, or NULL. * Output: * return long The number of unused nodes (or 0 if fl==NULL). */ long _idle_FreeListNodes(FreeList *fl) { return fl ? (fl->ntotal - fl->nbusy) : 0; } /*....................................................................... * Allocate a new list of free-list nodes. On return the nodes will * be linked together as a list starting with the node at the lowest * address and ending with a NULL next pointer. * * Input: * fl FreeList * The free-list to allocate the list for. * Output: * return FreeListBlock * The new linked block of free-list nodes, * or NULL on error. */ static FreeListBlock *_new_FreeListBlock(FreeList *fl) { FreeListBlock *block; /* The new block to be returned */ /* * Allocate the container. */ block = (FreeListBlock *) malloc(sizeof(FreeListBlock)); if(!block) return NULL; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_FreeListBlock(). */ block->next = NULL; block->nodes = NULL; /* * Allocate the block of nodes. */ block->nodes = (char *) malloc(fl->node_size * fl->blocking_factor); if(!block->nodes) return _del_FreeListBlock(block); /* * Initialize the block as a linked list of FreeListNode's. */ _thread_FreeListBlock(fl, block); /* * Update the record of the number of nodes in the freelist. */ fl->ntotal += fl->blocking_factor; return block; } /*....................................................................... * Link each node of a freelist block to the node that follows it. * * Input: * fl FreeList * The freelist that contains the block. * block FreeListBlock * The block to be threaded. */ static void _thread_FreeListBlock(FreeList *fl, FreeListBlock *block) { char *mem = block->nodes; int i; for(i=0; iblocking_factor - 1; i++, mem += fl->node_size) *(void **)mem = mem + fl->node_size; /* Link to the next node */ *(void **)mem = NULL; /* Terminate the list */ } /*....................................................................... * Delete a free-list block. * * Input: * fl FreeListBlock * The block to be deleted, or NULL. * Output: * return FreeListBlock * Always NULL. */ static FreeListBlock *_del_FreeListBlock(FreeListBlock *fl) { if(fl) { fl->next = NULL; if(fl->nodes) free(fl->nodes); fl->nodes = NULL; free(fl); }; return NULL; } yuma123_2.14/libtecla/ioutil.c0000664000175000017500000003026014770023131016354 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include #include "ioutil.h" static int _io_pad_line(GlWriteFn *write_fn, void *data, int c, int n); /*....................................................................... * Display a left-justified string over multiple terminal lines, * taking account of the specified width of the terminal. Optional * indentation and an option prefix string can be specified to be * displayed at the start of each new terminal line used, and if * needed, a single paragraph can be broken across multiple calls. * Note that literal newlines in the input string can be used to force * a newline at any point, and that in order to allow individual * paragraphs to be written using multiple calls to this function, * unless an explicit newline character is specified at the end of the * string, a newline will not be started at the end of the last word * in the string. Note that when a new line is started between two * words that are separated by spaces, those spaces are not output, * whereas when a new line is started because a newline character was * found in the string, only the spaces before the newline character * are discarded. * * Input: * write_fn GlWriteFn * The callback function to use to write the * output. * data void * A pointer to arbitrary data to be passed to * write_fn() whenever it is called. * fp FILE * The stdio stream to write to. * indentation int The number of fill characters to use to * indent the start of each new terminal line. * prefix const char * An optional prefix string to write after the * indentation margin at the start of each new * terminal line. You can specify NULL if no * prefix is required. * suffix const char * An optional suffix string to draw at the end * of the terminal line. The line will be padded * where necessary to ensure that the suffix ends * in the last column of the terminal line. If * no suffix is desired, specify NULL. * fill_char int The padding character to use when indenting * and filling up to the suffix. * term_width int The width of the terminal being written to. * start int The number of characters already written to * the start of the current terminal line. This * is primarily used to allow individual * paragraphs to be written over multiple calls * to this function, but can also be used to * allow you to start the first line of a * paragraph with a different prefix or * indentation than those specified above. * string const char * The string to be written. * Output: * return int On error -1 is returned. Otherwise the * return value is the terminal column index at * which the cursor was left after writing the * final word in the string. Successful return * values can thus be passed verbatim to the * 'start' arguments of subsequent calls to * _io_display_text() to allow the printing of a * paragraph to be broken across multiple calls * to _io_display_text(). */ int _io_display_text(GlWriteFn *write_fn, void *data, int indentation, const char *prefix, const char *suffix, int fill_char, int term_width, int start, const char *string) { int ndone; /* The number of characters written from string[] */ int nnew; /* The number of characters to be displayed next */ int was_space; /* True if the previous character was a space or tab */ int last = start; /* The column number of the last character written */ int prefix_len; /* The length of the optional line prefix string */ int suffix_len; /* The length of the optional line prefix string */ int margin_width; /* The total number of columns used by the indentation */ /* margin and the prefix string. */ int i; /* * Check the arguments? */ if(!string || !write_fn) { errno = EINVAL; return -1; }; /* * Enforce sensible values on the arguments. */ if(term_width < 0) term_width = 0; if(indentation > term_width) indentation = term_width; else if(indentation < 0) indentation = 0; if(start > term_width) start = term_width; else if(start < 0) start = 0; /* * Get the length of the prefix string. */ prefix_len = prefix ? strlen(prefix) : 0; /* * Get the length of the suffix string. */ suffix_len = suffix ? strlen(suffix) : 0; /* * How many characters are devoted to indenting and prefixing each line? */ margin_width = indentation + prefix_len; /* * Write as many terminal lines as are needed to display the whole string. */ for(ndone=0; string[ndone]; start=0) { last = start; /* * Write spaces from the current position in the terminal line to the * width of the requested indentation margin. */ if(indentation > 0 && last < indentation) { if(_io_pad_line(write_fn, data, fill_char, indentation - last)) return -1; last = indentation; }; /* * If a prefix string has been specified, display it unless we have * passed where it should end in the terminal output line. */ if(prefix_len > 0 && last < margin_width) { int pstart = last - indentation; int plen = prefix_len - pstart; if(write_fn(data, prefix+pstart, plen) != plen) return -1; last = margin_width; }; /* * Locate the end of the last complete word in the string before * (term_width - start) characters have been seen. To handle the case * where a single word is wider than the available space after the * indentation and prefix margins, always make sure that at least one * word is printed after the margin, regardless of whether it won't * fit on the line. The two exceptions to this rule are if an embedded * newline is found in the string or the end of the string is reached * before any word has been seen. */ nnew = 0; was_space = 0; for(i=ndone; string[i] && (last+i-ndone < term_width - suffix_len || (nnew==0 && last==margin_width)); i++) { if(string[i] == '\n') { if(!was_space) nnew = i-ndone; break; } else if(isspace((int) string[i])) { if(!was_space) { nnew = i-ndone+1; was_space = 1; }; } else { was_space = 0; }; }; /* * Does the end of the string delimit the last word that will fit on the * output line? */ if(nnew==0 && string[i] == '\0') nnew = i-ndone; /* * Write the new line. */ if(write_fn(data, string+ndone, nnew) != nnew) return -1; ndone += nnew; last += nnew; /* * Start a newline unless we have reached the end of the input string. * In the latter case, in order to give the caller the chance to * concatenate multiple calls to _io_display_text(), omit the newline, * leaving it up to the caller to write this. */ if(string[ndone] != '\0') { /* * If a suffix has been provided, pad out the end of the line with spaces * such that the suffix will end in the right-most terminal column. */ if(suffix_len > 0) { int npad = term_width - suffix_len - last; if(npad > 0 && _io_pad_line(write_fn, data, fill_char, npad)) return -1; last += npad; if(write_fn(data, suffix, suffix_len) != suffix_len) return -1; last += suffix_len; }; /* * Start a new line. */ if(write_fn(data, "\n", 1) != 1) return -1; /* * Skip any spaces and tabs that follow the last word that was written. */ while(string[ndone] && isspace((int)string[ndone]) && string[ndone] != '\n') ndone++; /* * If the terminating character was a literal newline character, * skip it in the input string, since we just wrote it. */ if(string[ndone] == '\n') ndone++; last = 0; }; }; /* * Return the column number of the last character printed. */ return last; } /*....................................................................... * Write a given number of spaces to the specified stdio output string. * * Input: * write_fn GlWriteFn * The callback function to use to write the * output. * data void * A pointer to arbitrary data to be passed to * write_fn() whenever it is called. * c int The padding character. * n int The number of spaces to be written. * Output: * return int 0 - OK. * 1 - Error. */ static int _io_pad_line(GlWriteFn *write_fn, void *data, int c, int n) { enum {FILL_SIZE=20}; char fill[FILL_SIZE+1]; /* * Fill the buffer with the specified padding character. */ memset(fill, c, FILL_SIZE); fill[FILL_SIZE] = '\0'; /* * Write the spaces using the above literal string of spaces as * many times as needed to output the requested number of spaces. */ while(n > 0) { int nnew = n <= FILL_SIZE ? n : FILL_SIZE; if(write_fn(data, fill, nnew) != nnew) return 1; n -= nnew; }; return 0; } /*....................................................................... * The following is an output callback function which uses fwrite() * to write to the stdio stream specified via its callback data argument. * * Input: * data void * The stdio stream to write to, specified via a * (FILE *) pointer cast to (void *). * s const char * The string to be written. * n int The length of the prefix of s[] to attempt to * write. * Output: * return int The number of characters written from s[]. This * should normally be a number in the range 0 to n. * To signal that an I/O error occurred, return -1. */ GL_WRITE_FN(_io_write_stdio) { int ndone; /* The total number of characters written */ int nnew; /* The number of characters written in the latest write */ /* * The callback data is the stdio stream to write to. */ FILE *fp = (FILE *) data; /* * Because of signals we may need to do more than one write to output * the whole string. */ for(ndone=0; ndone #include #include #include #include #include "hash.h" #include "strngmem.h" #include "freelist.h" /* * The following container object contains free-lists to be used * for allocation of HashTable containers and nodes. */ struct HashMemory { FreeList *hash_memory; /* HashTable free-list */ FreeList *node_memory; /* HashNode free-list */ StringMem *string_memory; /* Memory used to allocate hash strings */ }; /* * Define a hash symbol-table entry. * See symbol.h for the definition of the Symbol container type. */ typedef struct HashNode HashNode; struct HashNode { Symbol symbol; /* The symbol stored in the hash-entry */ HashNode *next; /* The next hash-table entry in a bucket list */ }; /* * Each hash-table bucket contains a linked list of entries that * hash to the same bucket. */ typedef struct { HashNode *head; /* The head of the bucket hash-node list */ int count; /* The number of entries in the list */ } HashBucket; /* * A hash-table consists of 'size' hash buckets. * Note that the HashTable typedef for this struct is contained in hash.h. */ struct HashTable { HashMemory *mem; /* HashTable free-list */ int internal_mem; /* True if 'mem' was allocated by _new_HashTable() */ int case_sensitive; /* True if case is significant in lookup keys */ int size; /* The number of hash buckets */ HashBucket *bucket; /* An array of 'size' hash buckets */ int (*keycmp)(const char *, const char *); /* Key comparison function */ void *app_data; /* Application-provided data */ HASH_DEL_FN(*del_fn); /* Application-provided 'app_data' destructor */ }; static HashNode *_del_HashNode(HashTable *hash, HashNode *node); static HashNode *_new_HashNode(HashTable *hash, const char *name, int code, void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)); static HashNode *_find_HashNode(HashTable *hash, HashBucket *bucket, const char *name, HashNode **prev); static HashBucket *_find_HashBucket(HashTable *hash, const char *name); static int _ht_lower_strcmp(const char *node_key, const char *look_key); static int _ht_strcmp(const char *node_key, const char *look_key); /*....................................................................... * Allocate a free-list for use in allocating hash tables and their nodes. * * Input: * list_count int The number of HashTable containers per free-list * block. * node_count int The number of HashTable nodes per free-list block. * Output: * return HashMemory * The new free-list for use in allocating hash tables * and their nodes. */ HashMemory *_new_HashMemory(int hash_count, int node_count) { HashMemory *mem; /* * Allocate the free-list container. */ mem = (HashMemory *) malloc(sizeof(HashMemory)); if(!mem) { errno = ENOMEM; return NULL; }; /* * Initialize the container at least up to the point at which it can * safely be passed to _del_HashMemory(). */ mem->hash_memory = NULL; mem->node_memory = NULL; mem->string_memory = NULL; /* * Allocate the two free-lists. */ mem->hash_memory = _new_FreeList(sizeof(HashTable), hash_count); if(!mem->hash_memory) return _del_HashMemory(mem, 1); mem->node_memory = _new_FreeList(sizeof(HashNode), node_count); if(!mem->node_memory) return _del_HashMemory(mem, 1); mem->string_memory = _new_StringMem(64); if(!mem->string_memory) return _del_HashMemory(mem, 1); /* * Return the free-list container. */ return mem; } /*....................................................................... * Delete a HashTable free-list. An error will be displayed if the list is * still in use and the deletion will be aborted. * * Input: * mem HashMemory * The free-list container to be deleted. * force int If force==0 then _del_HashMemory() will complain * and refuse to delete the free-list if any * of nodes have not been returned to the free-list. * If force!=0 then _del_HashMemory() will not check * whether any nodes are still in use and will * always delete the list. * Output: * return HashMemory * Always NULL (even if the memory could not be * deleted). */ HashMemory *_del_HashMemory(HashMemory *mem, int force) { if(mem) { if(!force && (_busy_FreeListNodes(mem->hash_memory) > 0 || _busy_FreeListNodes(mem->node_memory) > 0)) { errno = EBUSY; return NULL; }; mem->hash_memory = _del_FreeList(mem->hash_memory, force); mem->node_memory = _del_FreeList(mem->node_memory, force); mem->string_memory = _del_StringMem(mem->string_memory, force); free(mem); }; return NULL; } /*....................................................................... * Create a new hash table. * * Input: * mem HashMemory * An optional free-list for use in allocating * HashTable containers and nodes. See explanation * in hash.h. If you are going to allocate more * than one hash table, then it will be more * efficient to allocate a single free-list for * all of them than to force each hash table * to allocate its own private free-list. * size int The size of the hash table. Best performance * will be acheived if this is a prime number. * hcase HashCase Specify how symbol case is considered when * looking up symbols, from: * IGNORE_CASE - Upper and lower case versions * of a letter are treated as * being identical. * HONOUR_CASE - Upper and lower case versions * of a letter are treated as * being distinct. * characters in a lookup name is significant. * app_data void * Optional application data to be registered * to the table. This is presented to user * provided SYM_DEL_FN() symbol destructors along * with the symbol data. * del_fn() HASH_DEL_FN(*) If you want app_data to be free'd when the * hash-table is destroyed, register a suitable * destructor function here. * Output: * return HashTable * The new hash table, or NULL on error. */ HashTable *_new_HashTable(HashMemory *mem, int size, HashCase hcase, void *app_data, HASH_DEL_FN(*del_fn)) { HashTable *hash; /* The table to be returned */ int allocate_mem = !mem; /* True if mem should be internally allocated */ int i; /* * Check arguments. */ if(size <= 0) { errno = EINVAL; return NULL; }; /* * Allocate an internal free-list? */ if(allocate_mem) { mem = _new_HashMemory(1, 100); if(!mem) return NULL; }; /* * Allocate the container. */ hash = (HashTable *) _new_FreeListNode(mem->hash_memory); if(!hash) { errno = ENOMEM; if(allocate_mem) mem = _del_HashMemory(mem, 1); return NULL; }; /* * Before attempting any operation that might fail, initialize * the container at least up to the point at which it can safely * be passed to _del_HashTable(). */ hash->mem = mem; hash->internal_mem = allocate_mem; hash->case_sensitive = hcase==HONOUR_CASE; hash->size = size; hash->bucket = NULL; hash->keycmp = hash->case_sensitive ? _ht_strcmp : _ht_lower_strcmp; hash->app_data = app_data; hash->del_fn = del_fn; /* * Allocate the array of 'size' hash buckets. */ hash->bucket = (HashBucket *) malloc(sizeof(HashBucket) * size); if(!hash->bucket) { errno = ENOMEM; return _del_HashTable(hash); }; /* * Initialize the bucket array. */ for(i=0; ibucket + i; b->head = NULL; b->count = 0; }; /* * The table is ready for use - albeit currently empty. */ return hash; } /*....................................................................... * Delete a hash-table. * * Input: * hash HashTable * The hash table to be deleted. * Output: * return HashTable * The deleted hash table (always NULL). */ HashTable *_del_HashTable(HashTable *hash) { if(hash) { /* * Clear and delete the bucket array. */ if(hash->bucket) { _clear_HashTable(hash); free(hash->bucket); hash->bucket = NULL; }; /* * Delete application data. */ if(hash->del_fn) hash->del_fn(hash->app_data); /* * If the hash table was allocated from an internal free-list, delete * it and the hash table by deleting the free-list. Otherwise just * return the hash-table to the external free-list. */ if(hash->internal_mem) _del_HashMemory(hash->mem, 1); else hash = (HashTable *) _del_FreeListNode(hash->mem->hash_memory, hash); }; return NULL; } /*....................................................................... * Create and install a new entry in a hash table. If an entry with the * same name already exists, replace its contents with the new data. * * Input: * hash HashTable * The hash table to insert the symbol into. * name const char * The name to tag the entry with. * code int An application-specific code to be stored in * the entry. * fn void (*)(void) An application-specific function to be stored * in the entry. * data void * An application-specific pointer to data to be * associated with the entry, or NULL if not * relevant. * del_fn SYM_DEL_FN(*) An optional destructor function. When the * symbol is deleted this function will be called * with the 'code' and 'data' arguments given * above. Any application data that was registered * to the table via the app_data argument of * _new_HashTable() will also be passed. * Output: * return HashNode * The new entry, or NULL if there was insufficient * memory or the arguments were invalid. */ Symbol *_new_HashSymbol(HashTable *hash, const char *name, int code, void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)) { HashBucket *bucket; /* The hash-bucket associated with the name */ HashNode *node; /* The new node */ /* * Check arguments. */ if(!hash || !name) { errno = EINVAL; return NULL; }; /* * Get the hash bucket of the specified name. */ bucket = _find_HashBucket(hash, name); /* * See if a node with the same name already exists. */ node = _find_HashNode(hash, bucket, name, NULL); /* * If found, delete its contents by calling the user-supplied * destructor function, if provided. */ if(node) { if(node->symbol.data && node->symbol.del_fn) { node->symbol.data = node->symbol.del_fn(hash->app_data, node->symbol.code, node->symbol.data); }; /* * Allocate a new node if necessary. */ } else { node = _new_HashNode(hash, name, code, fn, data, del_fn); if(!node) return NULL; }; /* * Install the node at the head of the hash-bucket list. */ node->next = bucket->head; bucket->head = node; bucket->count++; return &node->symbol; } /*....................................................................... * Remove and delete a given hash-table entry. * * Input: * hash HashTable * The hash table to find the symbol in. * name const char * The name of the entry. * Output: * return HashNode * The deleted hash node (always NULL). */ Symbol *_del_HashSymbol(HashTable *hash, const char *name) { if(hash && name) { HashBucket *bucket = _find_HashBucket(hash, name); HashNode *prev; /* The node preceding the located node */ HashNode *node = _find_HashNode(hash, bucket, name, &prev); /* * Node found? */ if(node) { /* * Remove the node from the bucket list. */ if(prev) { prev->next = node->next; } else { bucket->head = node->next; }; /* * Record the loss of a node. */ bucket->count--; /* * Delete the node. */ (void) _del_HashNode(hash, node); }; }; return NULL; } /*....................................................................... * Look up a symbol in the hash table. * * Input: * hash HashTable * The table to look up the string in. * name const char * The name of the symbol to look up. * Output: * return Symbol * The located hash-table symbol, or NULL if not * found. */ Symbol *_find_HashSymbol(HashTable *hash, const char *name) { HashBucket *bucket; /* The hash-table bucket associated with name[] */ HashNode *node; /* The hash-table node of the requested symbol */ /* * Check arguments. */ if(!hash) return NULL; /* * Nothing to lookup? */ if(!name) return NULL; /* * Hash the name to a hash-table bucket. */ bucket = _find_HashBucket(hash, name); /* * Find the bucket entry that exactly matches the name. */ node = _find_HashNode(hash, bucket, name, NULL); if(!node) return NULL; return &node->symbol; } /*....................................................................... * Private function used to allocate a hash-table node. * The caller is responsible for checking that the specified symbol * is unique and for installing the returned entry in the table. * * Input: * hash HashTable * The table to allocate the node for. * name const char * The name of the new entry. * code int A user-supplied context code. * fn void (*)(void) A user-supplied function pointer. * data void * A user-supplied data pointer. * del_fn SYM_DEL_FN(*) An optional 'data' destructor function. * Output: * return HashNode * The new node, or NULL on error. */ static HashNode *_new_HashNode(HashTable *hash, const char *name, int code, void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)) { HashNode *node; /* The new node */ /* * Allocate the new node from the free list. */ node = (HashNode *) _new_FreeListNode(hash->mem->node_memory); if(!node) return NULL; /* * Before attempting any operation that might fail, initialize the * contents of 'node' at least up to the point at which it can be * safely passed to _del_HashNode(). */ node->symbol.name = NULL; node->symbol.code = code; node->symbol.fn = fn; node->symbol.data = data; node->symbol.del_fn = del_fn; node->next = NULL; /* * Allocate a copy of 'name'. */ node->symbol.name = _new_StringMemString(hash->mem->string_memory, strlen(name) + 1); if(!node->symbol.name) return _del_HashNode(hash, node); /* * If character-case is insignificant in the current table, convert the * name to lower case while copying it. */ if(hash->case_sensitive) { strcpy(node->symbol.name, name); } else { const char *src = name; char *dst = node->symbol.name; for( ; *src; src++,dst++) *dst = tolower(*src); *dst = '\0'; }; return node; } /*....................................................................... * Private function used to delete a hash-table node. * The node must have been removed from its list before calling this * function. * * Input: * hash HashTable * The table for which the node was originally * allocated. * node HashNode * The node to be deleted. * Output: * return HashNode * The deleted node (always NULL). */ static HashNode *_del_HashNode(HashTable *hash, HashNode *node) { if(node) { node->symbol.name = _del_StringMemString(hash->mem->string_memory, node->symbol.name); /* * Call the user-supplied data-destructor if provided. */ if(node->symbol.data && node->symbol.del_fn) node->symbol.data = node->symbol.del_fn(hash->app_data, node->symbol.code, node->symbol.data); /* * Return the node to the free-list. */ node->next = NULL; node = (HashNode *) _del_FreeListNode(hash->mem->node_memory, node); }; return NULL; } /*....................................................................... * Private function to locate the hash bucket associated with a given * name. * * This uses a hash-function described in the dragon-book * ("Compilers - Principles, Techniques and Tools", by Aho, Sethi and * Ullman; pub. Adison Wesley) page 435. * * Input: * hash HashTable * The table to look up the string in. * name const char * The name of the symbol to look up. * Output: * return HashBucket * The located hash-bucket. */ static HashBucket *_find_HashBucket(HashTable *hash, const char *name) { unsigned const char *kp; unsigned long h = 0L; if(hash->case_sensitive) { for(kp=(unsigned const char *) name; *kp; kp++) h = 65599UL * h + *kp; /* 65599 is a prime close to 2^16 */ } else { for(kp=(unsigned const char *) name; *kp; kp++) h = 65599UL * h + tolower((int)*kp); /* 65599 is a prime close to 2^16 */ }; return hash->bucket + (h % hash->size); } /*....................................................................... * Search for a given name in the entries of a given bucket. * * Input: * hash HashTable * The hash-table being searched. * bucket HashBucket * The bucket to search (use _find_HashBucket()). * name const char * The name to search for. * Output: * prev HashNode ** If prev!=NULL then the pointer to the node * preceding the located node in the list will * be recorded in *prev. This will be NULL either * if the name is not found or the located node is * at the head of the list of entries. * return HashNode * The located hash-table node, or NULL if not * found. */ static HashNode *_find_HashNode(HashTable *hash, HashBucket *bucket, const char *name, HashNode **prev) { HashNode *last; /* The previously searched node */ HashNode *node; /* The node that is being searched */ /* * Search the list for a node containing the specified name. */ for(last=NULL, node=bucket->head; node && hash->keycmp(node->symbol.name, name)!=0; last = node, node=node->next) ; if(prev) *prev = node ? last : NULL; return node; } /*....................................................................... * When hash->case_sensitive is zero this function is called * in place of strcmp(). In such cases the hash-table names are stored * as lower-case versions of the original strings so this function * performs the comparison against lower-case copies of the characters * of the string being compared. * * Input: * node_key const char * The lower-case hash-node key being compared * against. * look_key const char * The lookup key. * Output: * return int <0 if node_key < look_key. * 0 if node_key == look_key. * >0 if node_key > look_key. */ static int _ht_lower_strcmp(const char *node_key, const char *look_key) { int cn; /* The latest character from node_key[] */ int cl; /* The latest character from look_key[] */ do { cn = *node_key++; cl = *look_key++; } while(cn && cn==tolower(cl)); return cn - tolower(cl); } /*....................................................................... * This is a wrapper around strcmp for comparing hash-keys in a case * sensitive manner. The reason for having this wrapper, instead of using * strcmp() directly, is to make some C++ compilers happy. The problem * is that when the library is compiled with a C++ compiler, the * declaration of the comparison function is a C++ declaration, whereas * strcmp() is a pure C function and thus although it appears to have the * same declaration, the compiler disagrees. * * Input: * node_key char * The lower-case hash-node key being compared against. * look_key char * The lookup key. * Output: * return int <0 if node_key < look_key. * 0 if node_key == look_key. * >0 if node_key > look_key. */ static int _ht_strcmp(const char *node_key, const char *look_key) { return strcmp(node_key, look_key); } /*....................................................................... * Empty a hash-table by deleting all of its entries. * * Input: * hash HashTable * The hash table to clear. * Output: * return int 0 - OK. * 1 - Invalid arguments. */ int _clear_HashTable(HashTable *hash) { int i; /* * Check the arguments. */ if(!hash) return 1; /* * Clear the contents of the bucket array. */ for(i=0; isize; i++) { HashBucket *bucket = hash->bucket + i; /* * Delete the list of active hash nodes from the bucket. */ HashNode *node = bucket->head; while(node) { HashNode *next = node->next; (void) _del_HashNode(hash, node); node = next; }; /* * Mark the bucket as empty. */ bucket->head = NULL; bucket->count = 0; }; return 0; } /*....................................................................... * Execute a given function on each entry of a hash table, returning * before completion if the the specified function returns non-zero. * * Input: * hash HashTable * The table to traverse. * scan_fn HASH_SCAN_FN(*) The function to call. * context void * Optional caller-specific context data * to be passed to scan_fn(). * Output: * return int 0 - OK. * 1 - Either the arguments were invalid, or * scan_fn() returned non-zero at some * point. */ int _scan_HashTable(HashTable *hash, HASH_SCAN_FN(*scan_fn), void *context) { int i; /* * Check the arguments. */ if(!hash || !scan_fn) return 1; /* * Iterate through the buckets of the table. */ for(i=0; isize; i++) { HashBucket *bucket = hash->bucket + i; HashNode *node; /* * Iterate through the list of symbols that fall into bucket i, * passing each one to the caller-specified function. */ for(node=bucket->head; node; node=node->next) { if(scan_fn(&node->symbol, context)) return 1; }; }; return 0; } yuma123_2.14/libtecla/cplmatch.c0000664000175000017500000011226314770023131016646 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * Standard includes. */ #include #include #include #include /* * Local includes. */ #include "libtecla.h" #include "ioutil.h" #include "stringrp.h" #include "pathutil.h" #include "cplfile.h" #include "cplmatch.h" #include "errmsg.h" /* * Specify the number of strings to allocate when the string free-list * is exhausted. This also sets the number of elements to expand the * matches[] array by whenever it is found to be too small. */ #define STR_BLK_FACT 100 /* * Set the default number of spaces place between columns when listing * a set of completions. */ #define CPL_COL_SEP 2 /* * Completion matches are recorded in containers of the following * type. */ struct WordCompletion { ErrMsg *err; /* The error reporting buffer */ StringGroup *sg; /* Memory for a group of strings */ int matches_dim; /* The allocated size of result.matches[] */ CplMatches result; /* Completions to be returned to the caller */ #ifndef WITHOUT_FILE_SYSTEM CompleteFile *cf; /* The resources used for filename completion */ #endif }; static void cpl_sort_matches(WordCompletion *cpl); static void cpl_zap_duplicates(WordCompletion *cpl); static void cpl_clear_completions(WordCompletion *cpl); static int cpl_cmp_matches(const void *v1, const void *v2); static int cpl_cmp_suffixes(const void *v1, const void *v2); /* * The new_CplFileConf() constructor sets the integer first member of * the returned object to the following magic number. On seeing this, * cpl_file_completions() knows when it is passed a valid CplFileConf * object. */ #define CFC_ID_CODE 4568 #ifndef WITHOUT_FILE_SYSTEM /* * A pointer to a structure of the following type can be passed to * the builtin file-completion callback function to modify its behavior. */ struct CplFileConf { int id; /* new_CplFileConf() sets this to CFC_ID_CODE */ int escaped; /* If none-zero, backslashes in the input line are */ /* interpreted as escaping special characters and */ /* spaces, and any special characters and spaces in */ /* the listed completions will also be escaped with */ /* added backslashes. This is the default behaviour. */ /* If zero, backslashes are interpreted as being */ /* literal parts of the filename, and none are added */ /* to the completion suffixes. */ int file_start; /* The index in the input line of the first character */ /* of the filename. If you specify -1 here, */ /* cpl_file_completions() identifies the */ /* the start of the filename by looking backwards for */ /* an unescaped space, or the beginning of the line. */ CplCheckFn *chk_fn; /* If not zero, this argument specifies a */ /* function to call to ask whether a given */ /* file should be included in the list */ /* of completions. */ void *chk_data; /* Anonymous data to be passed to check_fn(). */ }; static void cpl_init_FileConf(CplFileConf *cfc); /* * When file-system access is being excluded, define a dummy structure * to satisfy the typedef in libtecla.h. */ #else struct CplFileConf {int dummy;}; #endif /* * Encapsulate the formatting information needed to layout a * multi-column listing of completions. */ typedef struct { int term_width; /* The width of the terminal (characters) */ int column_width; /* The number of characters within in each column. */ int ncol; /* The number of columns needed */ int nline; /* The number of lines needed */ } CplListFormat; /* * Given the current terminal width, and a list of completions, determine * how to best use the terminal width to display a multi-column listing * of completions. */ static void cpl_plan_listing(CplMatches *result, int term_width, CplListFormat *fmt); /* * Display a given line of a multi-column list of completions. */ static int cpl_format_line(CplMatches *result, CplListFormat *fmt, int lnum, GlWriteFn *write_fn, void *data); /*....................................................................... * Create a new string-completion object. * * Output: * return WordCompletion * The new object, or NULL on error. */ WordCompletion *new_WordCompletion(void) { WordCompletion *cpl; /* The object to be returned */ /* * Allocate the container. */ cpl = (WordCompletion *) malloc(sizeof(WordCompletion)); if(!cpl) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_WordCompletion(). */ cpl->err = NULL; cpl->sg = NULL; cpl->matches_dim = 0; cpl->result.suffix = NULL; cpl->result.cont_suffix = NULL; cpl->result.matches = NULL; cpl->result.nmatch = 0; #ifndef WITHOUT_FILE_SYSTEM cpl->cf = NULL; #endif /* * Allocate a place to record error messages. */ cpl->err = _new_ErrMsg(); if(!cpl->err) return del_WordCompletion(cpl); /* * Allocate an object that allows a group of strings to be allocated * efficiently by placing many of them in contiguous string segments. */ #ifdef WITHOUT_FILE_SYSTEM cpl->sg = _new_StringGroup(MAX_PATHLEN_FALLBACK); #else cpl->sg = _new_StringGroup(_pu_pathname_dim()); #endif if(!cpl->sg) return del_WordCompletion(cpl); /* * Allocate an array for matching completions. This will be extended later * if needed. */ cpl->matches_dim = STR_BLK_FACT; cpl->result.matches = (CplMatch *) malloc(sizeof(cpl->result.matches[0]) * cpl->matches_dim); if(!cpl->result.matches) { errno = ENOMEM; return del_WordCompletion(cpl); }; /* * Allocate a filename-completion resource object. */ #ifndef WITHOUT_FILE_SYSTEM cpl->cf = _new_CompleteFile(); if(!cpl->cf) return del_WordCompletion(cpl); #endif return cpl; } /*....................................................................... * Delete a string-completion object. * * Input: * cpl WordCompletion * The object to be deleted. * Output: * return WordCompletion * The deleted object (always NULL). */ WordCompletion *del_WordCompletion(WordCompletion *cpl) { if(cpl) { cpl->err = _del_ErrMsg(cpl->err); cpl->sg = _del_StringGroup(cpl->sg); if(cpl->result.matches) { free(cpl->result.matches); cpl->result.matches = NULL; #ifndef WITHOUT_FILE_SYSTEM cpl->cf = _del_CompleteFile(cpl->cf); #endif }; free(cpl); }; return NULL; } /*....................................................................... * This function is designed to be called by CplMatchFn callback * functions. It adds one possible completion of the token that is being * completed to an array of completions. If the completion needs any * special quoting to be valid when displayed in the input line, this * quoting must be included in the string. * * Input: * cpl WordCompletion * The argument of the same name that was passed * to the calling CplMatchFn callback function. * line const char * The input line, as received by the callback * function. * word_start int The index within line[] of the start of the * word that is being completed. * word_end int The index within line[] of the character which * follows the incomplete word, as received by the * calling callback function. * suffix const char * The appropriately quoted string that could * be appended to the incomplete token to complete * it. A copy of this string will be allocated * internally. * type_suffix const char * When listing multiple completions, gl_get_line() * appends this string to the completion to indicate * its type to the user. If not pertinent pass "". * Otherwise pass a literal or static string. * cont_suffix const char * If this turns out to be the only completion, * gl_get_line() will append this string as * a continuation. For example, the builtin * file-completion callback registers a directory * separator here for directory matches, and a * space otherwise. If the match were a function * name you might want to append an open * parenthesis, etc.. If not relevant pass "". * Otherwise pass a literal or static string. * Output: * return int 0 - OK. * 1 - Error. */ int cpl_add_completion(WordCompletion *cpl, const char *line, int word_start, int word_end, const char *suffix, const char *type_suffix, const char *cont_suffix) { CplMatch *match; /* The container of the new match */ char *string; /* A newly allocated copy of the completion string */ /* * Check the arguments. */ if(!cpl) return 1; if(!suffix) return 0; if(!type_suffix) type_suffix = ""; if(!cont_suffix) cont_suffix = ""; /* * Do we need to extend the array of matches[]? */ if(cpl->result.nmatch+1 > cpl->matches_dim) { int needed = cpl->matches_dim + STR_BLK_FACT; CplMatch *matches = (CplMatch *) realloc(cpl->result.matches, sizeof(cpl->result.matches[0]) * needed); if(!matches) { _err_record_msg(cpl->err, "Insufficient memory to extend array of matches.", END_ERR_MSG); return 1; }; cpl->result.matches = matches; cpl->matches_dim = needed; }; /* * Allocate memory to store the combined completion prefix and the * new suffix. */ string = _sg_alloc_string(cpl->sg, word_end-word_start + strlen(suffix)); if(!string) { _err_record_msg(cpl->err, "Insufficient memory to extend array of matches.", END_ERR_MSG); return 1; }; /* * Compose the string. */ strncpy(string, line + word_start, word_end - word_start); strcpy(string + word_end - word_start, suffix); /* * Record the new match. */ match = cpl->result.matches + cpl->result.nmatch++; match->completion = string; match->suffix = string + word_end - word_start; match->type_suffix = type_suffix; /* * Record the continuation suffix. */ cpl->result.cont_suffix = cont_suffix; return 0; } /*....................................................................... * Sort the array of matches. * * Input: * cpl WordCompletion * The completion resource object. */ static void cpl_sort_matches(WordCompletion *cpl) { qsort(cpl->result.matches, cpl->result.nmatch, sizeof(cpl->result.matches[0]), cpl_cmp_matches); } /*....................................................................... * This is a qsort() comparison function used to sort matches. * * Input: * v1, v2 void * Pointers to the two matches to be compared. * Output: * return int -1 -> v1 < v2. * 0 -> v1 == v2 * 1 -> v1 > v2 */ static int cpl_cmp_matches(const void *v1, const void *v2) { const CplMatch *m1 = (const CplMatch *) v1; const CplMatch *m2 = (const CplMatch *) v2; return strcmp(m1->completion, m2->completion); } /*....................................................................... * Sort the array of matches in order of their suffixes. * * Input: * cpl WordCompletion * The completion resource object. */ static void cpl_sort_suffixes(WordCompletion *cpl) { qsort(cpl->result.matches, cpl->result.nmatch, sizeof(cpl->result.matches[0]), cpl_cmp_suffixes); } /*....................................................................... * This is a qsort() comparison function used to sort matches in order of * their suffixes. * * Input: * v1, v2 void * Pointers to the two matches to be compared. * Output: * return int -1 -> v1 < v2. * 0 -> v1 == v2 * 1 -> v1 > v2 */ static int cpl_cmp_suffixes(const void *v1, const void *v2) { const CplMatch *m1 = (const CplMatch *) v1; const CplMatch *m2 = (const CplMatch *) v2; return strcmp(m1->suffix, m2->suffix); } /*....................................................................... * Find the common prefix of all of the matching completion matches, * and record a pointer to it in cpl->result.suffix. Note that this has * the side effect of sorting the matches into suffix order. * * Input: * cpl WordCompletion * The completion resource object. * Output: * return int 0 - OK. * 1 - Error. */ static int cpl_common_suffix(WordCompletion *cpl) { CplMatches *result; /* The result container */ const char *first, *last; /* The first and last matching suffixes */ int length; /* The length of the common suffix */ /* * Get the container of the array of matching files. */ result = &cpl->result; /* * No matching completions? */ if(result->nmatch < 1) return 0; /* * Sort th matches into suffix order. */ cpl_sort_suffixes(cpl); /* * Given that the array of matches is sorted, the first and last * suffixes are those that differ most in their prefixes, so the common * prefix of these strings is the longest common prefix of all of the * suffixes. */ first = result->matches[0].suffix; last = result->matches[result->nmatch - 1].suffix; /* * Find the point at which the first and last matching strings * first difffer. */ while(*first && *first == *last) { first++; last++; }; /* * How long is the common suffix? */ length = first - result->matches[0].suffix; /* * Allocate memory to record the common suffix. */ result->suffix = _sg_alloc_string(cpl->sg, length); if(!result->suffix) { _err_record_msg(cpl->err, "Insufficient memory to record common completion suffix.", END_ERR_MSG); return 1; }; /* * Record the common suffix. */ strncpy(result->suffix, result->matches[0].suffix, length); result->suffix[length] = '\0'; return 0; } /*....................................................................... * Discard the contents of the array of possible completion matches. * * Input: * cpl WordCompletion * The word-completion resource object. */ static void cpl_clear_completions(WordCompletion *cpl) { /* * Discard all of the strings. */ _clr_StringGroup(cpl->sg); /* * Record the fact that the array is now empty. */ cpl->result.nmatch = 0; cpl->result.suffix = NULL; cpl->result.cont_suffix = ""; /* * Also clear the error message. */ _err_clear_msg(cpl->err); return; } /*....................................................................... * Given an input line and the point at which it completion is to be * attempted, return an array of possible completions. * * Input: * cpl WordCompletion * The completion resource object. * line char * The current input line. * word_end int The index of the character in line[] which * follows the end of the token that is being * completed. * data void * Anonymous 'data' to be passed to match_fn(). * match_fn CplMatchFn * The function that will identify the prefix * to be completed from the input line, and * record completion matches. * Output: * return CplMatches * The container of the array of possible * completions. The returned pointer refers * to a container owned by the parent WordCompletion * object, and its contents thus potentially * change on every call to cpl_matches(). * On error, NULL is returned, and a description * of the error can be acquired by calling * cpl_last_error(cpl). */ CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, int word_end, void *data, CplMatchFn *match_fn) { int line_len; /* The total length of the input line */ /* * How long is the input line? */ line_len = strlen(line); /* * Check the arguments. */ if(!cpl || !line || !match_fn || word_end < 0 || word_end > line_len) { if(cpl) { _err_record_msg(cpl->err, "cpl_complete_word: Invalid arguments.", END_ERR_MSG); }; return NULL; }; /* * Clear the return container. */ cpl_clear_completions(cpl); /* * Have the matching function record possible completion matches in * cpl->result.matches. */ if(match_fn(cpl, data, line, word_end)) { if(_err_get_msg(cpl->err)[0] == '\0') _err_record_msg(cpl->err, "Error completing word.", END_ERR_MSG); return NULL; }; /* * Record a copy of the common initial part of all of the prefixes * in cpl->result.common. */ if(cpl_common_suffix(cpl)) return NULL; /* * Sort the matches into lexicographic order. */ cpl_sort_matches(cpl); /* * Discard any duplicate matches. */ cpl_zap_duplicates(cpl); /* * If there is more than one match, discard the continuation suffix. */ if(cpl->result.nmatch > 1) cpl->result.cont_suffix = ""; /* * Return the array of matches. */ return &cpl->result; } /*....................................................................... * Recall the return value of the last call to cpl_complete_word(). * * Input: * cpl WordCompletion * The completion resource object. * Output: * return CplMatches * The container of the array of possible * completions, as returned by the last call to * cpl_complete_word(). The returned pointer refers * to a container owned by the parent WordCompletion * object, and its contents thus potentially * change on every call to cpl_complete_word(). * On error, either in the execution of this * function, or in the last call to * cpl_complete_word(), NULL is returned, and a * description of the error can be acquired by * calling cpl_last_error(cpl). */ CplMatches *cpl_recall_matches(WordCompletion *cpl) { return (!cpl || *_err_get_msg(cpl->err)!='\0') ? NULL : &cpl->result; } /*....................................................................... * Print out an array of matching completions. * * Input: * result CplMatches * The container of the sorted array of * completions. * fp FILE * The output stream to write to. * term_width int The width of the terminal. * Output: * return int 0 - OK. * 1 - Error. */ int cpl_list_completions(CplMatches *result, FILE *fp, int term_width) { return _cpl_output_completions(result, _io_write_stdio, fp, term_width); } /*....................................................................... * Print an array of matching completions via a callback function. * * Input: * result CplMatches * The container of the sorted array of * completions. * write_fn GlWriteFn * The function to call to write the completions, * or 0 to discard the output. * data void * Anonymous data to pass to write_fn(). * term_width int The width of the terminal. * Output: * return int 0 - OK. * 1 - Error. */ int _cpl_output_completions(CplMatches *result, GlWriteFn *write_fn, void *data, int term_width) { CplListFormat fmt; /* List formatting information */ int lnum; /* The sequential number of the line to print next */ /* * Not enough space to list anything? */ if(term_width < 1) return 0; /* * Do we have a callback to write via, and any completions to be listed? */ if(write_fn && result && result->nmatch>0) { /* * Work out how to arrange the listing into fixed sized columns. */ cpl_plan_listing(result, term_width, &fmt); /* * Print the listing via the specified callback. */ for(lnum=0; lnum < fmt.nline; lnum++) { if(cpl_format_line(result, &fmt, lnum, write_fn, data)) return 1; }; }; return 0; } /*....................................................................... * Return a description of the string-completion error that occurred. * * Input: * cpl WordCompletion * The string-completion resource object. * Output: * return const char * The description of the last error. */ const char *cpl_last_error(WordCompletion *cpl) { return cpl ? _err_get_msg(cpl->err) : "NULL WordCompletion argument"; } /*....................................................................... * When an error occurs while performing a completion, you registerf a * terse description of the error by calling cpl_record_error(). This * message will then be returned on the next call to cpl_last_error(). * * Input: * cpl WordCompletion * The string-completion resource object that was * originally passed to the callback. * errmsg const char * The description of the error. */ void cpl_record_error(WordCompletion *cpl, const char *errmsg) { if(cpl && errmsg) _err_record_msg(cpl->err, errmsg, END_ERR_MSG); } /*....................................................................... * This is the builtin completion callback function which performs file * completion. * * Input: * cpl WordCompletion * An opaque pointer to the object that will * contain the matches. This should be filled * via zero or more calls to cpl_add_completion(). * data void * Either NULL to request the default * file-completion behavior, or a pointer to a * CplFileConf structure, whose members specify * a different behavior. * line char * The current input line. * word_end int The index of the character in line[] which * follows the end of the token that is being * completed. * Output * return int 0 - OK. * 1 - Error. */ CPL_MATCH_FN(cpl_file_completions) { #ifdef WITHOUT_FILE_SYSTEM return 0; #else const char *start_path; /* The pointer to the start of the pathname */ /* in line[]. */ CplFileConf *conf; /* The new-style configuration object. */ /* * The following configuration object will be used if the caller didn't * provide one. */ CplFileConf default_conf; /* * This function can be called externally, so check its arguments. */ if(!cpl) return 1; if(!line || word_end < 0) { _err_record_msg(cpl->err, "cpl_file_completions: Invalid arguments.", END_ERR_MSG); return 1; }; /* * The 'data' argument is either a CplFileConf pointer, identifiable * by having an integer id code as its first member, or the deprecated * CplFileArgs pointer, or can be NULL to request the default * configuration. */ if(data && *(int *)data == CFC_ID_CODE) { conf = (CplFileConf *) data; } else { /* * Select the defaults. */ conf = &default_conf; cpl_init_FileConf(&default_conf); /* * If we have been passed an instance of the deprecated CplFileArgs * structure, copy its configuration parameters over the defaults. */ if(data) { CplFileArgs *args = (CplFileArgs *) data; conf->escaped = args->escaped; conf->file_start = args->file_start; }; }; /* * Get the start of the filename. If not specified by the caller * identify it by searching backwards in the input line for an * unescaped space or the start of the line. */ if(conf->file_start < 0) { start_path = _pu_start_of_path(line, word_end); if(!start_path) { _err_record_msg(cpl->err, "Unable to find the start of the filename.", END_ERR_MSG); return 1; }; } else { start_path = line + conf->file_start; }; /* * Perform the completion. */ if(_cf_complete_file(cpl, cpl->cf, line, start_path - line, word_end, conf->escaped, conf->chk_fn, conf->chk_data)) { cpl_record_error(cpl, _cf_last_error(cpl->cf)); return 1; }; return 0; #endif } /*....................................................................... * Initialize a CplFileArgs structure with default configuration * parameters. Note that the CplFileArgs configuration type is * deprecated. The opaque CplFileConf object should be used in future * applications. * * Input: * cfa CplFileArgs * The configuration object of the * cpl_file_completions() callback. */ void cpl_init_FileArgs(CplFileArgs *cfa) { if(cfa) { cfa->escaped = 1; cfa->file_start = -1; }; } #ifndef WITHOUT_FILE_SYSTEM /*....................................................................... * Initialize a CplFileConf structure with default configuration * parameters. * * Input: * cfc CplFileConf * The configuration object of the * cpl_file_completions() callback. */ static void cpl_init_FileConf(CplFileConf *cfc) { if(cfc) { cfc->id = CFC_ID_CODE; cfc->escaped = 1; cfc->file_start = -1; cfc->chk_fn = 0; cfc->chk_data = NULL; }; } #endif /*....................................................................... * Create a new CplFileConf object and initialize it with defaults. * * Output: * return CplFileConf * The new object, or NULL on error. */ CplFileConf *new_CplFileConf(void) { #ifdef WITHOUT_FILE_SYSTEM errno = EINVAL; return NULL; #else CplFileConf *cfc; /* The object to be returned */ /* * Allocate the container. */ cfc = (CplFileConf *)malloc(sizeof(CplFileConf)); if(!cfc) return NULL; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_CplFileConf(). */ cpl_init_FileConf(cfc); return cfc; #endif } /*....................................................................... * Delete a CplFileConf object. * * Input: * cfc CplFileConf * The object to be deleted. * Output: * return CplFileConf * The deleted object (always NULL). */ CplFileConf *del_CplFileConf(CplFileConf *cfc) { #ifndef WITHOUT_FILE_SYSTEM if(cfc) { /* * Delete the container. */ free(cfc); }; #endif return NULL; } /*....................................................................... * If backslashes in the filename should be treated as literal * characters, call the following function with literal=1. Otherwise * the default is to treat them as escape characters, used for escaping * spaces etc.. * * Input: * cfc CplFileConf * The cpl_file_completions() configuration object * to be configured. * literal int Pass non-zero here to enable literal interpretation * of backslashes. Pass 0 to turn off literal * interpretation. */ void cfc_literal_escapes(CplFileConf *cfc, int literal) { #ifndef WITHOUT_FILE_SYSTEM if(cfc) cfc->escaped = !literal; #endif } /*....................................................................... * Call this function if you know where the index at which the * filename prefix starts in the input line. Otherwise by default, * or if you specify start_index to be -1, the filename is taken * to start after the first unescaped space preceding the cursor, * or the start of the line, which ever comes first. * * Input: * cfc CplFileConf * The cpl_file_completions() configuration object * to be configured. * start_index int The index of the start of the filename in * the input line, or -1 to select the default. */ void cfc_file_start(CplFileConf *cfc, int start_index) { #ifndef WITHOUT_FILE_SYSTEM if(cfc) cfc->file_start = start_index; #endif } /*....................................................................... * If you only want certain types of files to be included in the * list of completions, you use the following function to specify a * callback function which will be called to ask whether a given file * should be included. * * Input: * cfc CplFileConf * The cpl_file_completions() configuration object * to be configured. * chk_fn CplCheckFn * Zero to disable filtering, or a pointer to a * function that returns 1 if a given file should * be included in the list of completions. * chk_data void * Anonymous data to be passed to chk_fn() * every time that it is called. */ void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data) { #ifndef WITHOUT_FILE_SYSTEM if(cfc) { cfc->chk_fn = chk_fn; cfc->chk_data = chk_data; }; #endif } /*....................................................................... * The following CplCheckFn callback returns non-zero if the specified * filename is that of an executable. */ CPL_CHECK_FN(cpl_check_exe) { #ifdef WITHOUT_FILE_SYSTEM return 0; #else return _pu_path_is_exe(pathname); #endif } /*....................................................................... * Remove duplicates from a sorted array of matches. * * Input: * cpl WordCompletion * The completion resource object. */ static void cpl_zap_duplicates(WordCompletion *cpl) { CplMatch *matches; /* The array of matches */ int nmatch; /* The number of elements in matches[] */ const char *completion; /* The completion string of the last unique match */ const char *type_suffix; /* The type of the last unique match */ int src; /* The index of the match being considered */ int dst; /* The index at which to record the next */ /* unique match. */ /* * Get the array of matches and the number of matches that it * contains. */ matches = cpl->result.matches; nmatch = cpl->result.nmatch; /* * No matches? */ if(nmatch < 1) return; /* * Initialize the comparison strings with the first match. */ completion = matches[0].completion; type_suffix = matches[0].type_suffix; /* * Go through the array of matches, copying each new unrecorded * match at the head of the array, while discarding duplicates. */ for(src=dst=1; srccompletion) != 0 || strcmp(type_suffix, match->type_suffix) != 0) { if(src != dst) matches[dst] = *match; dst++; completion = match->completion; type_suffix = match->type_suffix; }; }; /* * Record the number of unique matches that remain. */ cpl->result.nmatch = dst; return; } /*....................................................................... * Work out how to arrange a given array of completions into a listing * of one or more fixed size columns. * * Input: * result CplMatches * The set of completions to be listed. * term_width int The width of the terminal. A lower limit of * zero is quietly enforced. * Input/Output: * fmt CplListFormat * The formatting information will be assigned * to the members of *fmt. */ static void cpl_plan_listing(CplMatches *result, int term_width, CplListFormat *fmt) { int maxlen; /* The length of the longest matching string */ int i; /* * Ensure that term_width >= 0. */ if(term_width < 0) term_width = 0; /* * Start by assuming the worst case, that either nothing will fit * on the screen, or that there are no matches to be listed. */ fmt->term_width = term_width; fmt->column_width = 0; fmt->nline = fmt->ncol = 0; /* * Work out the maximum length of the matching strings. */ maxlen = 0; for(i=0; inmatch; i++) { CplMatch *match = result->matches + i; int len = strlen(match->completion) + strlen(match->type_suffix); if(len > maxlen) maxlen = len; }; /* * Nothing to list? */ if(maxlen == 0) return; /* * Split the available terminal width into columns of * maxlen + CPL_COL_SEP characters. */ fmt->column_width = maxlen; fmt->ncol = fmt->term_width / (fmt->column_width + CPL_COL_SEP); /* * If the column width is greater than the terminal width, zero columns * will have been selected. Set a lower limit of one column. Leave it * up to the caller how to deal with completions who's widths exceed * the available terminal width. */ if(fmt->ncol < 1) fmt->ncol = 1; /* * How many lines of output will be needed? */ fmt->nline = (result->nmatch + fmt->ncol - 1) / fmt->ncol; return; } /*....................................................................... * Render one line of a multi-column listing of completions, using a * callback function to pass the output to an arbitrary destination. * * Input: * result CplMatches * The container of the sorted array of * completions. * fmt CplListFormat * Formatting information. * lnum int The index of the line to print, starting * from 0, and incrementing until the return * value indicates that there is nothing more * to be printed. * write_fn GlWriteFn * The function to call to write the line, or * 0 to discard the output. * data void * Anonymous data to pass to write_fn(). * Output: * return int 0 - Line printed ok. * 1 - Nothing to print. */ static int cpl_format_line(CplMatches *result, CplListFormat *fmt, int lnum, GlWriteFn *write_fn, void *data) { int col; /* The index of the list column being output */ /* * If the line index is out of bounds, there is nothing to be written. */ if(lnum < 0 || lnum >= fmt->nline) return 1; /* * If no output function has been provided, return as though the * line had been printed. */ if(!write_fn) return 0; /* * Print the matches in 'ncol' columns, sorted in line order within each * column. */ for(col=0; col < fmt->ncol; col++) { int m = col*fmt->nline + lnum; /* * Is there another match to be written? Note that in general * the last line of a listing will have fewer filled columns * than the initial lines. */ if(m < result->nmatch) { CplMatch *match = result->matches + m; /* * How long are the completion and type-suffix strings? */ int clen = strlen(match->completion); int tlen = strlen(match->type_suffix); /* * Write the completion string. */ if(write_fn(data, match->completion, clen) != clen) return 1; /* * Write the type suffix, if any. */ if(tlen > 0 && write_fn(data, match->type_suffix, tlen) != tlen) return 1; /* * If another column follows the current one, pad to its start with spaces. */ if(col+1 < fmt->ncol) { /* * The following constant string of spaces is used to pad the output. */ static const char spaces[] = " "; static const int nspace = sizeof(spaces) - 1; /* * Pad to the next column, using as few sub-strings of the spaces[] * array as possible. */ int npad = fmt->column_width + CPL_COL_SEP - clen - tlen; while(npad>0) { int n = npad > nspace ? nspace : npad; if(write_fn(data, spaces + nspace - n, n) != n) return 1; npad -= n; }; }; }; }; /* * Start a new line. */ { char s[] = "\r\n"; int n = strlen(s); if(write_fn(data, s, n) != n) return 1; }; return 0; } yuma123_2.14/libtecla/LICENSE.TERMS0000664000175000017500000000275514770023131016611 0ustar vladimirvladimirCopyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. yuma123_2.14/libtecla/pathutil.h0000664000175000017500000001056314770023131016712 0ustar vladimirvladimir#ifndef pathutil_h #define pathutil_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * The following object encapsulates a buffer designed to be used to * store pathnames. The pathname member of the object is initially * allocated with the size that _pu_pathname_dim() returns, and then * if this turns out to be pessimistic, the pathname can be reallocated * via calls to pb_append_to_path() and/or pb_resize_path(). */ typedef struct { char *name; /* The path buffer */ size_t dim; /* The current allocated size of buffer[] */ } PathName; PathName *_new_PathName(void); PathName *_del_PathName(PathName *path); char *_pn_clear_path(PathName *path); char *_pn_append_to_path(PathName *path, const char *string, int slen, int remove_escapes); char *_pn_prepend_to_path(PathName *path, const char *string, int slen, int remove_escapes); char *_pn_resize_path(PathName *path, size_t length); /* * Search backwards for the potential start of a filename. This * looks backwards from the specified index in a given string, * stopping at the first unescaped space or the start of the line. */ char *_pu_start_of_path(const char *string, int back_from); /* * Find the end of a potential filename, starting from a given index * in the string. This looks forwards from the specified index in a * given string, stopping at the first unescaped space or the end * of the line. */ char *_pu_end_of_path(const char *string, int start_from); /* * Return an estimate of the the length of the longest pathname * on the local system. */ size_t _pu_pathname_dim(void); /* * Return non-zero if the specified path name refers to a directory. */ int _pu_path_is_dir(const char *pathname); /* * Return non-zero if the specified path name refers to a regular file. */ int _pu_path_is_file(const char *pathname); /* * Return non-zero if the specified path name refers to an executable. */ int _pu_path_is_exe(const char *pathname); /* * Return non-zero if a file exists with the specified pathname. */ int _pu_file_exists(const char *pathname); /* * If neither the POSIX PATH_MAX macro nor the pathconf() function * can be used to find out the maximum pathlength on the target * system, the following fallback maximum length is used. */ #define MAX_PATHLEN_FALLBACK 1024 /* * If the pathname buffer turns out to be too small, it will be extended * in chunks of the following amount (plus whatever is needed at the time). */ #define PN_PATHNAME_INC 100 /* * Define the special character-sequences of the filesystem. */ #define FS_ROOT_DIR "/" /* The root directory */ #define FS_ROOT_DIR_LEN (sizeof(FS_ROOT_DIR) - 1) #define FS_PWD "." /* The current working directory */ #define FS_PWD_LEN (sizeof(FS_PWD_LEN) - 1) #define FS_DIR_SEP "/" /* The directory separator string */ #define FS_DIR_SEP_LEN (sizeof(FS_DIR_SEP) - 1) #endif yuma123_2.14/libtecla/hash.h0000664000175000017500000001346414770023131016006 0ustar vladimirvladimir#ifndef hash_h #define hash_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * The following macro can be used to prototype or define a * function that deletes the data of a symbol-table entry. * * Input: * app_data void * The _new_HashTable() app_data argument. * code int The Symbol::code argument. * sym_data void * The Symbol::data argument to be deleted. * Output: * return void * The deleted data (always return NULL). */ #define SYM_DEL_FN(fn) void *(fn)(void *app_data, int code, void *sym_data) /* * The following macro can be used to prototype or define a * function that deletes the application-data of a hash-table. * * Input: * data void * The _new_HashTable() 'app_data' argument to be * deleted. * Output: * return void * The deleted data (always return NULL). */ #define HASH_DEL_FN(fn) void *(fn)(void *app_data) /* * The following is a container for recording the context * of a symbol in a manner that is independant of the particular * symbol-table implementation. Each hash-table entry contains * the following user supplied parameters: * * 1. An optional integral parameter 'code'. This is useful for * enumerating a symbol or for describing what type of data * or function is stored in the symbol. * * 2. An optional generic function pointer. This is useful for * associating functions with names. The user is responsible * for casting between the generic function type and the * actual function type. The code field could be used to * enumerate what type of function to cast to. * * 3. An optional generic pointer to a static or heap-allocated * object. It is up to the user to cast this back to the * appropriate object type. Again, the code field could be used * to describe what type of object is stored there. * If the object is dynamically allocated and should be discarded * when the symbol is deleted from the symbol table, send a * destructor function to have it deleted automatically. */ typedef struct { char *name; /* The name of the symbol */ int code; /* Application supplied integral code */ void (*fn)(void); /* Application supplied generic function */ void *data; /* Application supplied context data */ SYM_DEL_FN(*del_fn); /* Data destructor function */ } Symbol; /* * HashNode's and HashTable's are small objects. Separately allocating * many such objects would normally cause memory fragmentation. To * counter this, HashMemory objects are used. These contain * dedicated free-lists formed from large dynamically allocated arrays * of objects. One HashMemory object can be shared between multiple hash * tables (within a single thread). */ typedef struct HashMemory HashMemory; /* Create a free-list for allocation of hash tables and their nodes */ HashMemory *_new_HashMemory(int hash_count, int node_count); /* Delete a redundant free-list if not being used */ HashMemory *_del_HashMemory(HashMemory *mem, int force); /* * Declare an alias for the private HashTable structure defined in * hash.c. */ typedef struct HashTable HashTable; /* * Enumerate case-sensitivity options. */ typedef enum { IGNORE_CASE, /* Ignore case when looking up symbols */ HONOUR_CASE /* Honor case when looking up symbols */ } HashCase; /* Create a new hash-table */ HashTable *_new_HashTable(HashMemory *mem, int size, HashCase hcase, void *app_data, HASH_DEL_FN(*del_fn)); /* Delete a reference to a hash-table */ HashTable *_del_HashTable(HashTable *hash); /* Add an entry to a hash table */ Symbol *_new_HashSymbol(HashTable *hash, const char *key, int code, void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)); /* Remove and delete all the entries in a given hash table */ int _clear_HashTable(HashTable *hash); /* Remove and delete a given hash-table entry */ Symbol *_del_HashSymbol(HashTable *hash, const char *key); /* Lookup a given hash-table entry */ Symbol *_find_HashSymbol(HashTable *hash, const char *key); /* Execute a given function on each entry of a hash table, returning */ /* before completion if the specified function returns non-zero. */ #define HASH_SCAN_FN(fn) int (fn)(Symbol *sym, void *context) int _scan_HashTable(HashTable *hash, HASH_SCAN_FN(*scan_fn), void *context); #endif yuma123_2.14/libtecla/cplfile.c0000664000175000017500000007026714770023131016500 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * If file-system access is to be excluded, this module has no function, * so all of its code should be excluded. */ #ifndef WITHOUT_FILE_SYSTEM /* * Standard includes. */ #include #include #include #include #include #include /* * Local includes. */ #include "libtecla.h" #include "direader.h" #include "homedir.h" #include "pathutil.h" #include "cplfile.h" #include "errmsg.h" /* * Set the maximum length allowed for usernames. * names. */ #define USR_LEN 100 /* * Set the maximum length allowed for environment variable names. */ #define ENV_LEN 100 /* * The resources needed to complete a filename are maintained in objects * of the following type. */ struct CompleteFile { ErrMsg *err; /* The error reporting buffer */ DirReader *dr; /* A directory reader */ HomeDir *home; /* A home directory expander */ PathName *path; /* The buffer in which to accumulate the path */ PathName *buff; /* A pathname work buffer */ char usrnam[USR_LEN+1]; /* The buffer used when reading the names of */ /* users. */ char envnam[ENV_LEN+1]; /* The buffer used when reading the names of */ /* environment variables. */ }; static int cf_expand_home_dir(CompleteFile *cf, const char *user); static int cf_complete_username(CompleteFile *cf, WordCompletion *cpl, const char *prefix, const char *line, int word_start, int word_end, int escaped); static HOME_DIR_FN(cf_homedir_callback); static int cf_complete_entry(CompleteFile *cf, WordCompletion *cpl, const char *line, int word_start, int word_end, int escaped, CplCheckFn *check_fn, void *check_data); static char *cf_read_name(CompleteFile *cf, const char *type, const char *string, int slen, char *nambuf, int nammax); static int cf_prepare_suffix(CompleteFile *cf, const char *suffix, int add_escapes); /* * A stack based object of the following type is used to pass data to the * cf_homedir_callback() function. */ typedef struct { CompleteFile *cf; /* The file-completion resource object */ WordCompletion *cpl; /* The string-completion rsource object */ size_t prefix_len; /* The length of the prefix being completed */ const char *line; /* The line from which the prefix was extracted */ int word_start; /* The index in line[] of the start of the username */ int word_end; /* The index in line[] following the end of the prefix */ int escaped; /* If true, add escapes to the completion suffixes */ } CfHomeArgs; /*....................................................................... * Create a new file-completion object. * * Output: * return CompleteFile * The new object, or NULL on error. */ CompleteFile *_new_CompleteFile(void) { CompleteFile *cf; /* The object to be returned */ /* * Allocate the container. */ cf = (CompleteFile *) malloc(sizeof(CompleteFile)); if(!cf) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_CompleteFile(). */ cf->err = NULL; cf->dr = NULL; cf->home = NULL; cf->path = NULL; cf->buff = NULL; cf->usrnam[0] = '\0'; cf->envnam[0] = '\0'; /* * Allocate a place to record error messages. */ cf->err = _new_ErrMsg(); if(!cf->err) return _del_CompleteFile(cf); /* * Create the object that is used for reading directories. */ cf->dr = _new_DirReader(); if(!cf->dr) return _del_CompleteFile(cf); /* * Create the object that is used to lookup home directories. */ cf->home = _new_HomeDir(); if(!cf->home) return _del_CompleteFile(cf); /* * Create the buffer in which the completed pathname is accumulated. */ cf->path = _new_PathName(); if(!cf->path) return _del_CompleteFile(cf); /* * Create a pathname work buffer. */ cf->buff = _new_PathName(); if(!cf->buff) return _del_CompleteFile(cf); return cf; } /*....................................................................... * Delete a file-completion object. * * Input: * cf CompleteFile * The object to be deleted. * Output: * return CompleteFile * The deleted object (always NULL). */ CompleteFile *_del_CompleteFile(CompleteFile *cf) { if(cf) { cf->err = _del_ErrMsg(cf->err); cf->dr = _del_DirReader(cf->dr); cf->home = _del_HomeDir(cf->home); cf->path = _del_PathName(cf->path); cf->buff = _del_PathName(cf->buff); free(cf); }; return NULL; } /*....................................................................... * Look up the possible completions of the incomplete filename that * lies between specified indexes of a given command-line string. * * Input: * cpl WordCompletion * The object in which to record the completions. * cf CompleteFile * The filename-completion resource object. * line const char * The string containing the incomplete filename. * word_start int The index of the first character in line[] * of the incomplete filename. * word_end int The index of the character in line[] that * follows the last character of the incomplete * filename. * escaped int If true, backslashes in line[] are * interpreted as escaping the characters * that follow them, and any spaces, tabs, * backslashes, or wildcard characters in the * returned suffixes will be similarly escaped. * If false, backslashes will be interpreted as * literal parts of the file name, and no * backslashes will be added to the returned * suffixes. * check_fn CplCheckFn * If not zero, this argument specifies a * function to call to ask whether a given * file should be included in the list * of completions. * check_data void * Anonymous data to be passed to check_fn(). * Output: * return int 0 - OK. * 1 - Error. A description of the error can be * acquired by calling _cf_last_error(cf). */ int _cf_complete_file(WordCompletion *cpl, CompleteFile *cf, const char *line, int word_start, int word_end, int escaped, CplCheckFn *check_fn, void *check_data) { const char *lptr; /* A pointer into line[] */ int nleft; /* The number of characters still to be processed */ /* in line[]. */ /* * Check the arguments. */ if(!cpl || !cf || !line || word_end < word_start) { if(cf) { _err_record_msg(cf->err, "_cf_complete_file: Invalid arguments", END_ERR_MSG); }; return 1; }; /* * Clear the buffer in which the filename will be constructed. */ _pn_clear_path(cf->path); /* * How many characters are to be processed? */ nleft = word_end - word_start; /* * Get a pointer to the start of the incomplete filename. */ lptr = line + word_start; /* * If the first character is a tilde, then perform home-directory * interpolation. */ if(nleft > 0 && *lptr == '~') { int slen; if(!cf_read_name(cf, "User", ++lptr, --nleft, cf->usrnam, USR_LEN)) return 1; /* * Advance over the username in the input line. */ slen = strlen(cf->usrnam); lptr += slen; nleft -= slen; /* * If we haven't hit the end of the input string then we have a complete * username to translate to the corresponding home directory. */ if(nleft > 0) { if(cf_expand_home_dir(cf, cf->usrnam)) return 1; /* * ~user and ~ are usually followed by a directory separator to * separate them from the file contained in the home directory. * If the home directory is the root directory, then we don't want * to follow the home directory by a directory separator, so we should * skip over it so that it doesn't get copied into the filename. */ if(strcmp(cf->path->name, FS_ROOT_DIR) == 0 && strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { lptr += FS_DIR_SEP_LEN; nleft -= FS_DIR_SEP_LEN; }; /* * If we have reached the end of the input string, then the username * may be incomplete, and we should attempt to complete it. */ } else { /* * Look up the possible completions of the username. */ return cf_complete_username(cf, cpl, cf->usrnam, line, word_start+1, word_end, escaped); }; }; /* * Copy the rest of the path, stopping to expand $envvar expressions * where encountered. */ while(nleft > 0) { int seglen; /* The length of the next segment to be copied */ /* * Find the length of the next segment to be copied, stopping if an * unescaped '$' is seen, or the end of the path is reached. */ for(seglen=0; seglen < nleft; seglen++) { int c = lptr[seglen]; if(escaped && c == '\\') seglen++; else if(c == '$') break; /* * We will be completing the last component of the file name, * so whenever a directory separator is seen, assume that it * might be the start of the last component, and mark the character * that follows it as the start of the name that is to be completed. */ if(nleft >= FS_DIR_SEP_LEN && strncmp(lptr + seglen, FS_DIR_SEP, FS_DIR_SEP_LEN)==0) { word_start = (lptr + seglen) - line + FS_DIR_SEP_LEN; }; }; /* * We have reached either the end of the filename or the start of * $environment_variable expression. Record the newly checked * segment of the filename in the output filename, removing * backslash-escapes where needed. */ if(_pn_append_to_path(cf->path, lptr, seglen, escaped) == NULL) { _err_record_msg(cf->err, "Insufficient memory to complete filename", END_ERR_MSG); return 1; }; lptr += seglen; nleft -= seglen; /* * If the above loop finished before we hit the end of the filename, * then this was because an unescaped $ was seen. In this case, interpolate * the value of the environment variable that follows it into the output * filename. */ if(nleft > 0) { char *value; /* The value of the environment variable */ int vlen; /* The length of the value string */ int nlen; /* The length of the environment variable name */ /* * Read the name of the environment variable. */ if(!cf_read_name(cf, "Environment", ++lptr, --nleft, cf->envnam, ENV_LEN)) return 1; /* * Advance over the environment variable name in the input line. */ nlen = strlen(cf->envnam); lptr += nlen; nleft -= nlen; /* * Get the value of the environment variable. */ value = getenv(cf->envnam); if(!value) { _err_record_msg(cf->err, "Unknown environment variable: ", cf->envnam, END_ERR_MSG); return 1; }; vlen = strlen(value); /* * If we are at the start of the filename and the first character of the * environment variable value is a '~', attempt home-directory * interpolation. */ if(cf->path->name[0] == '\0' && value[0] == '~') { if(!cf_read_name(cf, "User", value+1, vlen-1, cf->usrnam, USR_LEN) || cf_expand_home_dir(cf, cf->usrnam)) return 1; /* * If the home directory is the root directory, and the ~usrname expression * was followed by a directory separator, prevent the directory separator * from being appended to the root directory by skipping it in the * input line. */ if(strcmp(cf->path->name, FS_ROOT_DIR) == 0 && strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { lptr += FS_DIR_SEP_LEN; nleft -= FS_DIR_SEP_LEN; }; } else { /* * Append the value of the environment variable to the output path. */ if(_pn_append_to_path(cf->path, value, strlen(value), escaped)==NULL) { _err_record_msg(cf->err, "Insufficient memory to complete filename", END_ERR_MSG); return 1; }; /* * Prevent extra directory separators from being added. */ if(nleft >= FS_DIR_SEP_LEN && strcmp(cf->path->name, FS_ROOT_DIR) == 0 && strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { lptr += FS_DIR_SEP_LEN; nleft -= FS_DIR_SEP_LEN; } else if(vlen > FS_DIR_SEP_LEN && strcmp(value + vlen - FS_DIR_SEP_LEN, FS_DIR_SEP)==0) { cf->path->name[vlen-FS_DIR_SEP_LEN] = '\0'; }; }; /* * If adding the environment variable didn't form a valid directory, * we can't complete the line, since there is no way to separate append * a partial filename to an environment variable reference without * that appended part of the name being seen later as part of the * environment variable name. Thus if the currently constructed path * isn't a directory, quite now with no completions having been * registered. */ if(!_pu_path_is_dir(cf->path->name)) return 0; /* * For the reasons given above, if we have reached the end of the filename * with the expansion of an environment variable, the only allowed * completion involves the addition of a directory separator. */ if(nleft == 0) { if(cpl_add_completion(cpl, line, lptr-line, word_end, FS_DIR_SEP, "", "")) { _err_record_msg(cf->err, cpl_last_error(cpl), END_ERR_MSG); return 1; }; return 0; }; }; }; /* * Complete the filename if possible. */ return cf_complete_entry(cf, cpl, line, word_start, word_end, escaped, check_fn, check_data); } /*....................................................................... * Return a description of the last path-completion error that occurred. * * Input: * cf CompleteFile * The path-completion resource object. * Output: * return const char * The description of the last error. */ const char *_cf_last_error(CompleteFile *cf) { return cf ? _err_get_msg(cf->err) : "NULL CompleteFile argument"; } /*....................................................................... * Lookup the home directory of the specified user, or the current user * if no name is specified, appending it to output pathname. * * Input: * cf CompleteFile * The pathname completion resource object. * user const char * The username to lookup, or "" to lookup the * current user. * Output: * return int 0 - OK. * 1 - Error. */ static int cf_expand_home_dir(CompleteFile *cf, const char *user) { /* * Attempt to lookup the home directory. */ const char *home_dir = _hd_lookup_home_dir(cf->home, user); /* * Failed? */ if(!home_dir) { _err_record_msg(cf->err, _hd_last_home_dir_error(cf->home), END_ERR_MSG); return 1; }; /* * Append the home directory to the pathname string. */ if(_pn_append_to_path(cf->path, home_dir, -1, 0) == NULL) { _err_record_msg(cf->err, "Insufficient memory for home directory expansion", END_ERR_MSG); return 1; }; return 0; } /*....................................................................... * Lookup and report all completions of a given username prefix. * * Input: * cf CompleteFile * The filename-completion resource object. * cpl WordCompletion * The object in which to record the completions. * prefix const char * The prefix of the usernames to lookup. * line const char * The command-line in which the username appears. * word_start int The index within line[] of the start of the * username that is being completed. * word_end int The index within line[] of the character which * follows the incomplete username. * escaped int True if the completions need to have special * characters escaped. * Output: * return int 0 - OK. * 1 - Error. */ static int cf_complete_username(CompleteFile *cf, WordCompletion *cpl, const char *prefix, const char *line, int word_start, int word_end, int escaped) { /* * Set up a container of anonymous arguments to be sent to the * username-lookup iterator. */ CfHomeArgs args; args.cf = cf; args.cpl = cpl; args.prefix_len = strlen(prefix); args.line = line; args.word_start = word_start; args.word_end = word_end; args.escaped = escaped; /* * Iterate through the list of users, recording those which start * with the specified prefix. */ if(_hd_scan_user_home_dirs(cf->home, prefix, &args, cf_homedir_callback)) { _err_record_msg(cf->err, _hd_last_home_dir_error(cf->home), END_ERR_MSG); return 1; }; return 0; } /*....................................................................... * The user/home-directory scanner callback function (see homedir.h) * used by cf_complete_username(). */ static HOME_DIR_FN(cf_homedir_callback) { /* * Get the file-completion resources from the anonymous data argument. */ CfHomeArgs *args = (CfHomeArgs *) data; WordCompletion *cpl = args->cpl; CompleteFile *cf = args->cf; /* * Copy the username into the pathname work buffer, adding backslash * escapes where needed. */ if(cf_prepare_suffix(cf, usrnam+args->prefix_len, args->escaped)) { strncpy(errmsg, _err_get_msg(cf->err), maxerr); errmsg[maxerr] = '\0'; return 1; }; /* * Report the completion suffix that was copied above. */ if(cpl_add_completion(cpl, args->line, args->word_start, args->word_end, cf->buff->name, FS_DIR_SEP, FS_DIR_SEP)) { strncpy(errmsg, cpl_last_error(cpl), maxerr); errmsg[maxerr] = '\0'; return 1; }; return 0; } /*....................................................................... * Report possible completions of the filename in cf->path->name[]. * * Input: * cf CompleteFile * The file-completion resource object. * cpl WordCompletion * The object in which to record the completions. * line const char * The input line, as received by the callback * function. * word_start int The index within line[] of the start of the * last component of the filename that is being * completed. * word_end int The index within line[] of the character which * follows the incomplete filename. * escaped int If true, escape special characters in the * completion suffixes. * check_fn CplCheckFn * If not zero, this argument specifies a * function to call to ask whether a given * file should be included in the list * of completions. * check_data void * Anonymous data to be passed to check_fn(). * Output: * return int 0 - OK. * 1 - Error. */ static int cf_complete_entry(CompleteFile *cf, WordCompletion *cpl, const char *line, int word_start, int word_end, int escaped, CplCheckFn *check_fn, void *check_data) { const char *dirpath; /* The name of the parent directory */ int start; /* The index of the start of the last filename */ /* component in the transcribed filename. */ const char *prefix; /* The filename prefix to be completed */ int prefix_len; /* The length of the filename prefix */ const char *file_name; /* The lastest filename being compared */ int waserr = 0; /* True after errors */ int terminated=0; /* True if the directory part had to be terminated */ /* * Get the pathname string and its current length. */ char *pathname = cf->path->name; int pathlen = strlen(pathname); /* * Locate the start of the final component of the pathname. */ for(start=pathlen - 1; start >= 0 && strncmp(pathname + start, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0; start--) ; /* * Is the parent directory the root directory? */ if(start==0 || (start < 0 && strncmp(pathname, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0)) { dirpath = FS_ROOT_DIR; start += FS_ROOT_DIR_LEN; /* * If we found a directory separator then the part which precedes the * last component is the name of the directory to be opened. */ } else if(start > 0) { /* * The _dr_open_dir() function requires the directory name to be '\0' * terminated, so temporarily do this by overwriting the first character * of the directory separator. */ pathname[start] = '\0'; dirpath = pathname; terminated = 1; /* * We reached the start of the pathname before finding a directory * separator, so arrange to open the current working directory. */ } else { start = 0; dirpath = FS_PWD; }; /* * Attempt to open the directory. */ if(_dr_open_dir(cf->dr, dirpath, NULL)) { _err_record_msg(cf->err, "Can't open directory: ", dirpath, END_ERR_MSG); return 1; }; /* * If removed above, restore the directory separator and skip over it * to the start of the filename. */ if(terminated) { memcpy(pathname + start, FS_DIR_SEP, FS_DIR_SEP_LEN); start += FS_DIR_SEP_LEN; }; /* * Get the filename prefix and its length. */ prefix = pathname + start; prefix_len = strlen(prefix); /* * Traverse the directory, looking for files who's prefixes match the * last component of the pathname. */ while((file_name = _dr_next_file(cf->dr)) != NULL && !waserr) { int name_len = strlen(file_name); /* * Is the latest filename a possible completion of the filename prefix? */ if(name_len >= prefix_len && strncmp(prefix, file_name, prefix_len)==0) { /* * When listing all files in a directory, don't list files that start * with '.'. This is how hidden files are denoted in UNIX. */ if(prefix_len > 0 || file_name[0] != '.') { /* * Copy the completion suffix into the work pathname cf->buff->name, * adding backslash escapes if needed. */ if(cf_prepare_suffix(cf, file_name + prefix_len, escaped)) { waserr = 1; } else { /* * We want directories to be displayed with directory suffixes, * and other fully completed filenames to be followed by spaces. * To check the type of the file, append the current suffix * to the path being completed, check the filetype, then restore * the path to its original form. */ const char *cont_suffix = ""; /* The suffix to add if fully */ /* completed. */ const char *type_suffix = ""; /* The suffix to add when listing */ if(_pn_append_to_path(cf->path, file_name + prefix_len, -1, escaped) == NULL) { _err_record_msg(cf->err, "Insufficient memory to complete filename.", END_ERR_MSG); return 1; }; /* * Specify suffixes according to the file type. */ if(_pu_path_is_dir(cf->path->name)) { cont_suffix = FS_DIR_SEP; type_suffix = FS_DIR_SEP; } else if(!check_fn || check_fn(check_data, cf->path->name)) { cont_suffix = " "; } else { cf->path->name[pathlen] = '\0'; continue; }; /* * Remove the temporarily added suffix. */ cf->path->name[pathlen] = '\0'; /* * Record the latest completion. */ if(cpl_add_completion(cpl, line, word_start, word_end, cf->buff->name, type_suffix, cont_suffix)) waserr = 1; }; }; }; }; /* * Close the directory. */ _dr_close_dir(cf->dr); return waserr; } /*....................................................................... * Read a username or environment variable name, stopping when a directory * separator is seen, when the end of the string is reached, or the * output buffer overflows. * * Input: * cf CompleteFile * The file-completion resource object. * type char * The capitalized name of the type of name being read. * string char * The string who's prefix contains the name. * slen int The number of characters in string[]. * nambuf char * The output name buffer. * nammax int The longest string that will fit in nambuf[], excluding * the '\0' terminator. * Output: * return char * A pointer to nambuf on success. On error NULL is * returned and a description of the error is recorded * in cf->err. */ static char *cf_read_name(CompleteFile *cf, const char *type, const char *string, int slen, char *nambuf, int nammax) { int namlen; /* The number of characters in nambuf[] */ const char *sptr; /* A pointer into string[] */ /* * Work out the max number of characters that should be copied. */ int nmax = nammax < slen ? nammax : slen; /* * Get the environment variable name that follows the dollar. */ for(sptr=string,namlen=0; namlen < nmax && (slen-namlen < FS_DIR_SEP_LEN || strncmp(sptr, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0); namlen++) { nambuf[namlen] = *sptr++; }; /* * Did the name overflow the buffer? */ if(namlen >= nammax) { _err_record_msg(cf->err, type, " name too long", END_ERR_MSG); return NULL; }; /* * Terminate the string. */ nambuf[namlen] = '\0'; return nambuf; } /*....................................................................... * Using the work buffer cf->buff, make a suitably escaped copy of a * given completion suffix, ready to be passed to cpl_add_completion(). * * Input: * cf CompleteFile * The file-completion resource object. * suffix char * The suffix to be copied. * add_escapes int If true, escape special characters. * Output: * return int 0 - OK. * 1 - Error. */ static int cf_prepare_suffix(CompleteFile *cf, const char *suffix, int add_escapes) { const char *sptr; /* A pointer into suffix[] */ int nbsl; /* The number of backslashes to add to the suffix */ int i; /* * How long is the suffix? */ int suffix_len = strlen(suffix); /* * Clear the work buffer. */ _pn_clear_path(cf->buff); /* * Count the number of backslashes that will have to be added to * escape spaces, tabs, backslashes and wildcard characters. */ nbsl = 0; if(add_escapes) { for(sptr = suffix; *sptr; sptr++) { switch(*sptr) { case ' ': case '\t': case '\\': case '*': case '?': case '[': nbsl++; break; }; }; }; /* * Arrange for the output path buffer to have sufficient room for the * both the suffix and any backslashes that have to be inserted. */ if(_pn_resize_path(cf->buff, suffix_len + nbsl) == NULL) { _err_record_msg(cf->err, "Insufficient memory to complete filename", END_ERR_MSG); return 1; }; /* * If the suffix doesn't need any escapes, copy it directly into the * work buffer. */ if(nbsl==0) { strcpy(cf->buff->name, suffix); } else { /* * Make a copy with special characters escaped? */ if(nbsl > 0) { const char *src = suffix; char *dst = cf->buff->name; for(i=0; i #include #include #include #include #include #include #include /* * UNIX headers. */ #include #ifdef HAVE_SELECT #ifdef HAVE_SYS_SELECT_H #include #endif #include #include #endif /* * Handle the different sources of terminal control string and size * information. Note that if no terminal information database is available, * ANSI VT100 control sequences are used. */ #if defined(USE_TERMINFO) || defined(USE_TERMCAP) /* * Include curses.h or ncurses/curses.h depending on which is available. */ #ifdef HAVE_CURSES_H #include #elif defined(HAVE_NCURSES_CURSES_H) #include #endif /* * Include term.h where available. */ #if defined(HAVE_TERM_H) #include #elif defined(HAVE_NCURSES_TERM_H) #include #endif /* * When using termcap, include termcap.h on systems that have it. * Otherwise assume that all prototypes are provided by curses.h. */ #if defined(USE_TERMCAP) && defined(HAVE_TERMCAP_H) #include #endif /* * Under Solaris default Curses the output function that tputs takes is * declared to have a char argument. On all other systems and on Solaris * X/Open Curses (Issue 4, Version 2) it expects an int argument (using * c89 or options -I /usr/xpg4/include -L /usr/xpg4/lib -R /usr/xpg4/lib * selects XPG4v2 Curses on Solaris 2.6 and later). * * Similarly, under Mac OS X, the return value of the tputs output * function is declared as void, whereas it is declared as int on * other systems. */ #if defined __sun && defined __SVR4 && !defined _XOPEN_CURSES typedef int TputsRetType; typedef char TputsArgType; /* int tputs(char c, FILE *fp) */ #define TPUTS_RETURNS_VALUE 1 #elif defined(__APPLE__) && defined(__MACH__) typedef void TputsRetType; typedef int TputsArgType; /* void tputs(int c, FILE *fp) */ #define TPUTS_RETURNS_VALUE 0 #else typedef int TputsRetType; typedef int TputsArgType; /* int tputs(int c, FILE *fp) */ #define TPUTS_RETURNS_VALUE 1 #endif /* * Use the above specifications to prototype our tputs callback function. */ static TputsRetType gl_tputs_putchar(TputsArgType c); #endif /* defined(USE_TERMINFO) || defined(USE_TERMCAP) */ /* * If the library is being compiled without filesystem access facilities, * ensure that none of the action functions that normally do access the * filesystem are bound by default, and that it they do get bound, that * they don't do anything. */ #if WITHOUT_FILE_SYSTEM #define HIDE_FILE_SYSTEM #endif /* * POSIX headers. */ #include #include #include /* * Provide typedefs for standard POSIX structures. */ typedef struct sigaction SigAction; typedef struct termios Termios; /* * Which flag is used to select non-blocking I/O with fcntl()? */ #undef NON_BLOCKING_FLAG #if defined(O_NONBLOCK) #define NON_BLOCKING_FLAG (O_NONBLOCK) #elif defined(O_NDELAY) #define NON_BLOCKING_FLAG (O_NDELAY) #endif /* * What value should we give errno if I/O blocks when it shouldn't. */ #undef BLOCKED_ERRNO #if defined(EAGAIN) #define BLOCKED_ERRNO (EAGAIN) #elif defined(EWOULDBLOCK) #define BLOCKED_ERRNO (EWOULDBLOCK) #elif defined(EIO) #define BLOCKED_ERRNO (EIO) #else #define BLOCKED_ERRNO 0 #endif /* * Local headers. */ #ifndef WITHOUT_FILE_SYSTEM #include "pathutil.h" #endif #include "libtecla.h" #include "keytab.h" #include "getline.h" #include "ioutil.h" #include "history.h" #include "freelist.h" #include "stringrp.h" #include "chrqueue.h" #include "cplmatch.h" #ifndef WITHOUT_FILE_SYSTEM #include "expand.h" #endif #include "errmsg.h" /* * Enumerate the available editing styles. */ typedef enum { GL_EMACS_MODE, /* Emacs style editing */ GL_VI_MODE, /* Vi style editing */ GL_NO_EDITOR /* Fall back to the basic OS-provided editing */ } GlEditor; /* * Set the largest key-sequence that can be handled. */ #define GL_KEY_MAX 64 /* * In vi mode, the following datatype is used to implement the * undo command. It records a copy of the input line from before * the command-mode action which edited the input line. */ typedef struct { char *line; /* A historical copy of the input line */ int buff_curpos; /* The historical location of the cursor in */ /* line[] when the line was modified. */ int ntotal; /* The number of characters in line[] */ int saved; /* True once a line has been saved after the */ /* last call to gl_interpret_char(). */ } ViUndo; /* * In vi mode, the following datatype is used to record information * needed by the vi-repeat-change command. */ typedef struct { KtAction action; /* The last action function that made a */ /* change to the line. */ int count; /* The repeat count that was passed to the */ /* above command. */ int input_curpos; /* Whenever vi command mode is entered, the */ /* the position at which it was first left */ /* is recorded here. */ int command_curpos; /* Whenever vi command mode is entered, the */ /* the location of the cursor is recorded */ /* here. */ char input_char; /* Commands that call gl_read_terminal() */ /* record the character here, so that it can */ /* used on repeating the function. */ int saved; /* True if a function has been saved since the */ /* last call to gl_interpret_char(). */ int active; /* True while a function is being repeated. */ } ViRepeat; /* * The following datatype is used to encapsulate information specific * to vi mode. */ typedef struct { ViUndo undo; /* Information needed to implement the vi */ /* undo command. */ ViRepeat repeat; /* Information needed to implement the vi */ /* repeat command. */ int command; /* True in vi command-mode */ int find_forward; /* True if the last character search was in the */ /* forward direction. */ int find_onto; /* True if the last character search left the */ /* on top of the located character, as opposed */ /* to just before or after it. */ char find_char; /* The last character sought, or '\0' if no */ /* searches have been performed yet. */ } ViMode; #ifdef HAVE_SELECT /* * Define a type for recording a file-descriptor callback and its associated * data. */ typedef struct { GlFdEventFn *fn; /* The callback function */ void *data; /* Anonymous data to pass to the callback function */ } GlFdHandler; /* * A list of nodes of the following type is used to record file-activity * event handlers, but only on systems that have the select() system call. */ typedef struct GlFdNode GlFdNode; struct GlFdNode { GlFdNode *next; /* The next in the list of nodes */ int fd; /* The file descriptor being watched */ GlFdHandler rd; /* The callback to call when fd is readable */ GlFdHandler wr; /* The callback to call when fd is writable */ GlFdHandler ur; /* The callback to call when fd has urgent data */ }; /* * Set the number of the above structures to allocate every time that * the freelist of GlFdNode's becomes exhausted. */ #define GLFD_FREELIST_BLOCKING 10 static int gl_call_fd_handler(GetLine *gl, GlFdHandler *gfh, int fd, GlFdEvent event); static int gl_call_timeout_handler(GetLine *gl); #endif /* * Each signal that gl_get_line() traps is described by a list node * of the following type. */ typedef struct GlSignalNode GlSignalNode; struct GlSignalNode { GlSignalNode *next; /* The next signal in the list */ int signo; /* The number of the signal */ sigset_t proc_mask; /* A process mask which only includes signo */ SigAction original; /* The signal disposition of the calling program */ /* for this signal. */ unsigned flags; /* A bitwise union of GlSignalFlags enumerators */ GlAfterSignal after; /* What to do after the signal has been handled */ int errno_value; /* What to set errno to */ }; /* * Set the number of the above structures to allocate every time that * the freelist of GlSignalNode's becomes exhausted. */ #define GLS_FREELIST_BLOCKING 30 /* * Completion handlers and their callback data are recorded in * nodes of the following type. */ typedef struct GlCplCallback GlCplCallback; struct GlCplCallback { CplMatchFn *fn; /* The completion callback function */ void *data; /* Arbitrary callback data */ }; /* * The following function is used as the default completion handler when * the filesystem is to be hidden. It simply reports no completions. */ #ifdef HIDE_FILE_SYSTEM static CPL_MATCH_FN(gl_no_completions); #endif /* * Specify how many GlCplCallback nodes are added to the GlCplCallback freelist * whenever it becomes exhausted. */ #define GL_CPL_FREELIST_BLOCKING 10 /* * External action functions and their callback data are recorded in * nodes of the following type. */ typedef struct GlExternalAction GlExternalAction; struct GlExternalAction { GlActionFn *fn; /* The function which implements the action */ void *data; /* Arbitrary callback data */ }; /* * Specify how many GlExternalAction nodes are added to the * GlExternalAction freelist whenever it becomes exhausted. */ #define GL_EXT_ACT_FREELIST_BLOCKING 10 /* * Define the contents of the GetLine object. * Note that the typedef for this object can be found in libtecla.h. */ struct GetLine { ErrMsg *err; /* The error-reporting buffer */ GlHistory *glh; /* The line-history buffer */ WordCompletion *cpl; /* String completion resource object */ GlCplCallback cplfn; /* The completion callback */ #ifndef WITHOUT_FILE_SYSTEM ExpandFile *ef; /* ~user/, $envvar and wildcard expansion */ /* resource object. */ #endif StringGroup *capmem; /* Memory for recording terminal capability */ /* strings. */ GlCharQueue *cq; /* The terminal output character queue */ int input_fd; /* The file descriptor to read on */ int output_fd; /* The file descriptor to write to */ FILE *input_fp; /* A stream wrapper around input_fd */ FILE *output_fp; /* A stream wrapper around output_fd */ FILE *file_fp; /* When input is being temporarily taken from */ /* a file, this is its file-pointer. Otherwise */ /* it is NULL. */ char *term; /* The terminal type specified on the last call */ /* to gl_change_terminal(). */ int is_term; /* True if stdin is a terminal */ GlWriteFn *flush_fn; /* The function to call to write to the terminal */ GlIOMode io_mode; /* The I/O mode established by gl_io_mode() */ int raw_mode; /* True while the terminal is in raw mode */ GlPendingIO pending_io; /* The type of I/O that is currently pending */ GlReturnStatus rtn_status; /* The reason why gl_get_line() returned */ int rtn_errno; /* THe value of errno associated with rtn_status */ size_t linelen; /* The max number of characters per line */ char *line; /* A line-input buffer of allocated size */ /* linelen+2. The extra 2 characters are */ /* reserved for "\n\0". */ char *cutbuf; /* A cut-buffer of the same size as line[] */ char *prompt; /* The current prompt string */ int prompt_len; /* The length of the prompt string */ int prompt_changed; /* True after a callback changes the prompt */ int prompt_style; /* How the prompt string is displayed */ FreeList *cpl_mem; /* Memory for GlCplCallback objects */ FreeList *ext_act_mem; /* Memory for GlExternalAction objects */ FreeList *sig_mem; /* Memory for nodes of the signal list */ GlSignalNode *sigs; /* The head of the list of signals */ int signals_masked; /* True between calls to gl_mask_signals() and */ /* gl_unmask_signals() */ int signals_overriden; /* True between calls to gl_override_signals() */ /* and gl_restore_signals() */ sigset_t all_signal_set; /* The set of all signals that we are trapping */ sigset_t old_signal_set; /* The set of blocked signals on entry to */ /* gl_get_line(). */ sigset_t use_signal_set; /* The subset of all_signal_set to unblock */ /* while waiting for key-strokes */ Termios oldattr; /* Saved terminal attributes. */ KeyTab *bindings; /* A table of key-bindings */ int ntotal; /* The number of characters in gl->line[] */ int buff_curpos; /* The cursor position within gl->line[] */ int term_curpos; /* The cursor position on the terminal */ int term_len; /* The number of terminal characters used to */ /* display the current input line. */ int buff_mark; /* A marker location in the buffer */ int insert_curpos; /* The cursor position at start of insert */ int insert; /* True in insert mode */ int number; /* If >= 0, a numeric argument is being read */ int endline; /* True to tell gl_get_input_line() to return */ /* the current contents of gl->line[] */ int displayed; /* True if an input line is currently displayed */ int redisplay; /* If true, the input line will be redrawn */ /* either after the current action function */ /* returns, or when gl_get_input_line() */ /* is next called. */ int postpone; /* _gl_normal_io() sets this flag, to */ /* postpone any redisplays until */ /* is next called, to resume line editing. */ char keybuf[GL_KEY_MAX+1]; /* A buffer of currently unprocessed key presses */ int nbuf; /* The number of characters in keybuf[] */ int nread; /* The number of characters read from keybuf[] */ KtAction current_action; /* The action function that is being invoked */ int current_count; /* The repeat count passed to */ /* current_acction.fn() */ GlhLineID preload_id; /* When not zero, this should be the ID of a */ /* line in the history buffer for potential */ /* recall. */ int preload_history; /* If true, preload the above history line when */ /* gl_get_input_line() is next called. */ long keyseq_count; /* The number of key sequences entered by the */ /* the user since new_GetLine() was called. */ long last_search; /* The value of keyseq_count during the last */ /* history search operation. */ GlEditor editor; /* The style of editing, (eg. vi or emacs) */ int silence_bell; /* True if gl_ring_bell() should do nothing. */ int automatic_history; /* True to automatically archive entered lines */ /* in the history list. */ ViMode vi; /* Parameters used when editing in vi mode */ const char *left; /* The string that moves the cursor 1 character */ /* left. */ const char *right; /* The string that moves the cursor 1 character */ /* right. */ const char *up; /* The string that moves the cursor 1 character */ /* up. */ const char *down; /* The string that moves the cursor 1 character */ /* down. */ const char *home; /* The string that moves the cursor home */ const char *bol; /* Move cursor to beginning of line */ const char *clear_eol; /* The string that clears from the cursor to */ /* the end of the line. */ const char *clear_eod; /* The string that clears from the cursor to */ /* the end of the display. */ const char *u_arrow; /* The string returned by the up-arrow key */ const char *d_arrow; /* The string returned by the down-arrow key */ const char *l_arrow; /* The string returned by the left-arrow key */ const char *r_arrow; /* The string returned by the right-arrow key */ const char *sound_bell; /* The string needed to ring the terminal bell */ const char *bold; /* Switch to the bold font */ const char *underline; /* Underline subsequent characters */ const char *standout; /* Turn on standout mode */ const char *dim; /* Switch to a dim font */ const char *reverse; /* Turn on reverse video */ const char *blink; /* Switch to a blinking font */ const char *text_attr_off; /* Turn off all text attributes */ int nline; /* The height of the terminal in lines */ int ncolumn; /* The width of the terminal in columns */ #ifdef USE_TERMCAP char *tgetent_buf; /* The buffer that is used by tgetent() to */ /* store a terminal description. */ char *tgetstr_buf; /* The buffer that is used by tgetstr() to */ /* store terminal capabilities. */ #endif #ifdef USE_TERMINFO const char *left_n; /* The parameter string that moves the cursor */ /* n characters left. */ const char *right_n; /* The parameter string that moves the cursor */ /* n characters right. */ #endif char *app_file; /* The pathname of the application-specific */ /* .teclarc configuration file, or NULL. */ char *user_file; /* The pathname of the user-specific */ /* .teclarc configuration file, or NULL. */ int configured; /* True as soon as any teclarc configuration */ /* file has been read. */ int echo; /* True to display the line as it is being */ /* entered. If 0, only the prompt will be */ /* displayed, and the line will not be */ /* archived in the history list. */ int last_signal; /* The last signal that was caught by */ /* the last call to gl_get_line(), or -1 */ /* if no signal has been caught yet. */ #ifdef HAVE_SELECT FreeList *fd_node_mem; /* A freelist of GlFdNode structures */ GlFdNode *fd_nodes; /* The list of fd event descriptions */ fd_set rfds; /* The set of fds to watch for readability */ fd_set wfds; /* The set of fds to watch for writability */ fd_set ufds; /* The set of fds to watch for urgent data */ int max_fd; /* The maximum file-descriptor being watched */ struct { /* Inactivity timeout related data */ struct timeval dt; /* The inactivity timeout when timer.fn() */ /* isn't 0 */ GlTimeoutFn *fn; /* The application callback to call when */ /* the inactivity timer expires, or 0 if */ /* timeouts are not required. */ void *data; /* Application provided data to be passed to */ /* timer.fn(). */ } timer; #endif }; /* * Define the max amount of space needed to store a termcap terminal * description. Unfortunately this has to be done by guesswork, so * there is the potential for buffer overflows if we guess too small. * Fortunately termcap has been replaced by terminfo on most * platforms, and with terminfo this isn't an issue. The value that I * am using here is the conventional value, as recommended by certain * web references. */ #ifdef USE_TERMCAP #define TERMCAP_BUF_SIZE 2048 #endif /* * Set the size of the string segments used to store terminal capability * strings. */ #define CAPMEM_SEGMENT_SIZE 512 /* * If no terminal size information is available, substitute the * following vt100 default sizes. */ #define GL_DEF_NLINE 24 #define GL_DEF_NCOLUMN 80 /* * Enumerate the attributes needed to classify different types of * signals. These attributes reflect the standard default * characteristics of these signals (according to Richard Steven's * Advanced Programming in the UNIX Environment). Note that these values * are all powers of 2, so that they can be combined in a bitwise union. */ typedef enum { GLSA_TERM=1, /* A signal that terminates processes */ GLSA_SUSP=2, /* A signal that suspends processes */ GLSA_CONT=4, /* A signal that is sent when suspended processes resume */ GLSA_IGN=8, /* A signal that is ignored */ GLSA_CORE=16, /* A signal that generates a core dump */ GLSA_HARD=32, /* A signal generated by a hardware exception */ GLSA_SIZE=64 /* A signal indicating terminal size changes */ } GlSigAttr; /* * List the signals that we need to catch. In general these are * those that by default terminate or suspend the process, since * in such cases we need to restore terminal settings. */ static const struct GlDefSignal { int signo; /* The number of the signal */ unsigned flags; /* A bitwise union of GlSignalFlags enumerators */ GlAfterSignal after; /* What to do after the signal has been delivered */ int attr; /* The default attributes of this signal, expressed */ /* as a bitwise union of GlSigAttr enumerators */ int errno_value; /* What to set errno to */ } gl_signal_list[] = { {SIGABRT, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM|GLSA_CORE, EINTR}, {SIGALRM, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_TERM, 0}, {SIGCONT, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_CONT|GLSA_IGN, 0}, #if defined(SIGHUP) #ifdef ENOTTY {SIGHUP, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, ENOTTY}, #else {SIGHUP, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, EINTR}, #endif #endif {SIGINT, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, EINTR}, #if defined(SIGPIPE) #ifdef EPIPE {SIGPIPE, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, EPIPE}, #else {SIGPIPE, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, EINTR}, #endif #endif #ifdef SIGPOLL {SIGPOLL, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, EINTR}, #endif #ifdef SIGPWR {SIGPWR, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_IGN, 0}, #endif #ifdef SIGQUIT {SIGQUIT, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM|GLSA_CORE, EINTR}, #endif {SIGTERM, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, EINTR}, #ifdef SIGTSTP {SIGTSTP, GLS_SUSPEND_INPUT, GLS_CONTINUE, GLSA_SUSP, 0}, #endif #ifdef SIGTTIN {SIGTTIN, GLS_SUSPEND_INPUT, GLS_CONTINUE, GLSA_SUSP, 0}, #endif #ifdef SIGTTOU {SIGTTOU, GLS_SUSPEND_INPUT, GLS_CONTINUE, GLSA_SUSP, 0}, #endif #ifdef SIGUSR1 {SIGUSR1, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_TERM, 0}, #endif #ifdef SIGUSR2 {SIGUSR2, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_TERM, 0}, #endif #ifdef SIGVTALRM {SIGVTALRM, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_TERM, 0}, #endif #ifdef SIGWINCH {SIGWINCH, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_SIZE|GLSA_IGN, 0}, #endif #ifdef SIGXCPU {SIGXCPU, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_TERM|GLSA_CORE, 0}, #endif #ifdef SIGXFSZ {SIGXFSZ, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_TERM|GLSA_CORE, 0}, #endif }; /* * Define file-scope variables for use in signal handlers. */ static volatile sig_atomic_t gl_pending_signal = -1; static sigjmp_buf gl_setjmp_buffer; static void gl_signal_handler(int signo); static int gl_check_caught_signal(GetLine *gl); /* * Respond to an externally caught process suspension or * termination signal. */ static void gl_suspend_process(int signo, GetLine *gl, int ngl); /* Return the default attributes of a given signal */ static int gl_classify_signal(int signo); /* * Unfortunately both terminfo and termcap require one to use the tputs() * function to output terminal control characters, and this function * doesn't allow one to specify a file stream. As a result, the following * file-scope variable is used to pass the current output file stream. * This is bad, but there doesn't seem to be any alternative. */ static GetLine *tputs_gl = NULL; /* * Define a tab to be a string of 8 spaces. */ #define TAB_WIDTH 8 /* * Lookup the current size of the terminal. */ static void gl_query_size(GetLine *gl, int *ncolumn, int *nline); /* * Getline calls this to temporarily override certain signal handlers * of the calling program. */ static int gl_override_signal_handlers(GetLine *gl); /* * Getline calls this to restore the signal handlers of the calling * program. */ static int gl_restore_signal_handlers(GetLine *gl); /* * Temporarily block the delivery of all signals that gl_get_line() * is currently configured to trap. */ static int gl_mask_signals(GetLine *gl, sigset_t *oldset); /* * Restore the process signal mask that was overriden by a previous * call to gl_mask_signals(). */ static int gl_unmask_signals(GetLine *gl, sigset_t *oldset); /* * Unblock the signals that gl_get_line() has been configured to catch. */ static int gl_catch_signals(GetLine *gl); /* * Return the set of all trappable signals. */ static void gl_list_trappable_signals(sigset_t *signals); /* * Put the terminal into raw input mode, after saving the original * terminal attributes in gl->oldattr. */ static int gl_raw_terminal_mode(GetLine *gl); /* * Restore the terminal attributes from gl->oldattr. */ static int gl_restore_terminal_attributes(GetLine *gl); /* * Switch to non-blocking I/O if possible. */ static int gl_nonblocking_io(GetLine *gl, int fd); /* * Switch to blocking I/O if possible. */ static int gl_blocking_io(GetLine *gl, int fd); /* * Read a line from the user in raw mode. */ static int gl_get_input_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos); /* * Query the user for a single character. */ static int gl_get_query_char(GetLine *gl, const char *prompt, int defchar); /* * Read input from a non-interactive input stream. */ static int gl_read_stream_line(GetLine *gl); /* * Read a single character from a non-interactive input stream. */ static int gl_read_stream_char(GetLine *gl); /* * Prepare to edit a new line. */ static int gl_present_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos); /* * Reset all line-editing parameters for a new input line. */ static void gl_reset_editor(GetLine *gl); /* * Handle the receipt of the potential start of a new key-sequence from * the user. */ static int gl_interpret_char(GetLine *gl, char c); /* * Bind a single control or meta character to an action. */ static int gl_bind_control_char(GetLine *gl, KtBinder binder, char c, const char *action); /* * Set up terminal-specific key bindings. */ static int gl_bind_terminal_keys(GetLine *gl); /* * Lookup terminal control string and size information. */ static int gl_control_strings(GetLine *gl, const char *term); /* * Wrappers around the terminfo and termcap functions that lookup * strings in the terminal information databases. */ #ifdef USE_TERMINFO static const char *gl_tigetstr(GetLine *gl, const char *name); #elif defined(USE_TERMCAP) static const char *gl_tgetstr(GetLine *gl, const char *name, char **bufptr); #endif /* * Output a binary string directly to the terminal. */ static int gl_print_raw_string(GetLine *gl, int buffered, const char *string, int n); /* * Print an informational message, starting and finishing on new lines. * After the list of strings to be printed, the last argument MUST be * GL_END_INFO. */ static int gl_print_info(GetLine *gl, ...); #define GL_END_INFO ((const char *)0) /* * Start a newline and place the cursor at its start. */ static int gl_start_newline(GetLine *gl, int buffered); /* * Output a terminal control sequence. */ static int gl_print_control_sequence(GetLine *gl, int nline, const char *string); /* * Output a character or string to the terminal after converting tabs * to spaces and control characters to a caret followed by the modified * character. */ static int gl_print_char(GetLine *gl, char c, char pad); static int gl_print_string(GetLine *gl, const char *string, char pad); /* * Delete nc characters starting from the one under the cursor. * Optionally copy the deleted characters to the cut buffer. */ static int gl_delete_chars(GetLine *gl, int nc, int cut); /* * Add a character to the line buffer at the current cursor position, * inserting or overwriting according the current mode. */ static int gl_add_char_to_line(GetLine *gl, char c); /* * Insert/append a string to the line buffer and terminal at the current * cursor position. */ static int gl_add_string_to_line(GetLine *gl, const char *s); /* * Record a new character in the input-line buffer. */ static int gl_buffer_char(GetLine *gl, char c, int bufpos); /* * Record a string in the input-line buffer. */ static int gl_buffer_string(GetLine *gl, const char *s, int n, int bufpos); /* * Make way to insert a string in the input-line buffer. */ static int gl_make_gap_in_buffer(GetLine *gl, int start, int n); /* * Remove characters from the input-line buffer, and move any characters * that followed them to the start of the vacated space. */ static void gl_remove_from_buffer(GetLine *gl, int start, int n); /* * Terminate the input-line buffer after a specified number of characters. */ static int gl_truncate_buffer(GetLine *gl, int n); /* * Delete the displayed part of the input line that follows the current * terminal cursor position. */ static int gl_truncate_display(GetLine *gl); /* * Accomodate changes to the contents of the input line buffer * that weren't made by the above gl_*buffer functions. */ static void gl_update_buffer(GetLine *gl); /* * Read a single character from the terminal. */ static int gl_read_terminal(GetLine *gl, int keep, char *c); /* * Discard processed characters from the key-press lookahead buffer. */ static void gl_discard_chars(GetLine *gl, int nused); /* * Move the terminal cursor n positions to the left or right. */ static int gl_terminal_move_cursor(GetLine *gl, int n); /* * Move the terminal cursor to a given position. */ static int gl_set_term_curpos(GetLine *gl, int term_curpos); /* * Set the position of the cursor both in the line input buffer and on the * terminal. */ static int gl_place_cursor(GetLine *gl, int buff_curpos); /* * How many characters are needed to write a number as an octal string? */ static int gl_octal_width(unsigned num); /* * Return the number of spaces needed to display a tab character at * a given location of the terminal. */ static int gl_displayed_tab_width(GetLine *gl, int term_curpos); /* * Return the number of terminal characters needed to display a * given raw character. */ static int gl_displayed_char_width(GetLine *gl, char c, int term_curpos); /* * Return the number of terminal characters needed to display a * given substring. */ static int gl_displayed_string_width(GetLine *gl, const char *string, int nc, int term_curpos); /* * Return non-zero if 'c' is to be considered part of a word. */ static int gl_is_word_char(int c); /* * Read a tecla configuration file. */ static int _gl_read_config_file(GetLine *gl, const char *filename, KtBinder who); /* * Read a tecla configuration string. */ static int _gl_read_config_string(GetLine *gl, const char *buffer, KtBinder who); /* * Define the callback function used by _gl_parse_config_line() to * read the next character of a configuration stream. */ #define GLC_GETC_FN(fn) int (fn)(void *stream) typedef GLC_GETC_FN(GlcGetcFn); static GLC_GETC_FN(glc_file_getc); /* Read from a file */ static GLC_GETC_FN(glc_buff_getc); /* Read from a string */ /* * Parse a single configuration command line. */ static int _gl_parse_config_line(GetLine *gl, void *stream, GlcGetcFn *getc_fn, const char *origin, KtBinder who, int *lineno); static int gl_report_config_error(GetLine *gl, const char *origin, int lineno, const char *errmsg); /* * Bind the actual arrow key bindings to match those of the symbolic * arrow-key bindings. */ static int _gl_bind_arrow_keys(GetLine *gl); /* * Copy the binding of the specified symbolic arrow-key binding to * the terminal specific, and default arrow-key key-sequences. */ static int _gl_rebind_arrow_key(GetLine *gl, const char *name, const char *term_seq, const char *def_seq1, const char *def_seq2); /* * After the gl_read_from_file() action has been used to tell gl_get_line() * to temporarily read input from a file, gl_revert_input() arranges * for input to be reverted to the input stream last registered with * gl_change_terminal(). */ static void gl_revert_input(GetLine *gl); /* * Flush unwritten characters to the terminal. */ static int gl_flush_output(GetLine *gl); /* * The callback through which all terminal output is routed. * This simply appends characters to a queue buffer, which is * subsequently flushed to the output channel by gl_flush_output(). */ static GL_WRITE_FN(gl_write_fn); /* * The callback function which the output character queue object * calls to transfer characters to the output channel. */ static GL_WRITE_FN(gl_flush_terminal); /* * Enumerate the possible return statuses of gl_read_input(). */ typedef enum { GL_READ_OK, /* A character was read successfully */ GL_READ_ERROR, /* A read-error occurred */ GL_READ_BLOCKED, /* The read would have blocked the caller */ GL_READ_EOF /* The end of the current input file was reached */ } GlReadStatus; static GlReadStatus gl_read_input(GetLine *gl, char *c); /* * Private functions of gl_read_input(). */ static int gl_event_handler(GetLine *gl, int fd); static GlReadStatus gl_read_unmasked(GetLine *gl, int fd, char *c); /* * A private function of gl_tty_signals(). */ static int gl_set_tty_signal(int signo, void (*handler)(int)); /* * Change the editor style being emulated. */ static int gl_change_editor(GetLine *gl, GlEditor editor); /* * Searching in a given direction, return the index of a given (or * read) character in the input line, or the character that precedes * it in the specified search direction. Return -1 if not found. */ static int gl_find_char(GetLine *gl, int count, int forward, int onto, char c); /* * Return the buffer index of the nth word ending after the cursor. */ static int gl_nth_word_end_forward(GetLine *gl, int n); /* * Return the buffer index of the nth word start after the cursor. */ static int gl_nth_word_start_forward(GetLine *gl, int n); /* * Return the buffer index of the nth word start before the cursor. */ static int gl_nth_word_start_backward(GetLine *gl, int n); /* * When called when vi command mode is enabled, this function saves the * current line and cursor position for potential restoration later * by the vi undo command. */ static void gl_save_for_undo(GetLine *gl); /* * If in vi mode, switch to vi command mode. */ static void gl_vi_command_mode(GetLine *gl); /* * In vi mode this is used to delete up to or onto a given or read * character in the input line. Also switch to insert mode if requested * after the deletion. */ static int gl_delete_find(GetLine *gl, int count, char c, int forward, int onto, int change); /* * Copy the characters between the cursor and the count'th instance of * a specified (or read) character in the input line, into the cut buffer. */ static int gl_copy_find(GetLine *gl, int count, char c, int forward, int onto); /* * Return the line index of the parenthesis that either matches the one under * the cursor, or not over a parenthesis character, the index of the next * close parenthesis. Return -1 if not found. */ static int gl_index_of_matching_paren(GetLine *gl); /* * Replace a malloc'd string (or NULL), with another malloc'd copy of * a string (or NULL). */ static int gl_record_string(char **sptr, const char *string); /* * Enumerate text display attributes as powers of two, suitable for * use in a bit-mask. */ typedef enum { GL_TXT_STANDOUT=1, /* Display text highlighted */ GL_TXT_UNDERLINE=2, /* Display text underlined */ GL_TXT_REVERSE=4, /* Display text with reverse video */ GL_TXT_BLINK=8, /* Display blinking text */ GL_TXT_DIM=16, /* Display text in a dim font */ GL_TXT_BOLD=32 /* Display text using a bold font */ } GlTextAttr; /* * Display the prompt regardless of the current visibility mode. */ static int gl_display_prompt(GetLine *gl); /* * Return the number of characters used by the prompt on the terminal. */ static int gl_displayed_prompt_width(GetLine *gl); /* * Prepare to return the current input line to the caller of gl_get_line(). */ static int gl_line_ended(GetLine *gl, int newline_char); /* * Arrange for the input line to be redisplayed when the current contents * of the output queue have been flushed. */ static void gl_queue_redisplay(GetLine *gl); /* * Erase the displayed representation of the input line, without * touching the buffered copy. */ static int gl_erase_line(GetLine *gl); /* * This function is called whenever the input line has been erased. */ static void gl_line_erased(GetLine *gl); /* * Arrange for the current input line to be discarded. */ void _gl_abandon_line(GetLine *gl); /* * The following are private internally callable versions of pertinent * public functions. Unlike their public wrapper functions, they don't * block signals while running, and assume that their arguments are valid. * They are designed to be called from places where signals are already * blocked, and where simple sanity checks have already been applied to * their arguments. */ static char *_gl_get_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos); static int _gl_query_char(GetLine *gl, const char *prompt, char defchar); static int _gl_read_char(GetLine *gl); static int _gl_update_size(GetLine *gl); /* * Redraw the current input line to account for a change in the terminal * size. Also install the new size in gl. */ static int gl_handle_tty_resize(GetLine *gl, int ncolumn, int nline); static int _gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, const char *term); static int _gl_configure_getline(GetLine *gl, const char *app_string, const char *app_file, const char *user_file); static int _gl_save_history(GetLine *gl, const char *filename, const char *comment, int max_lines); static int _gl_load_history(GetLine *gl, const char *filename, const char *comment); static int _gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, GlFdEventFn *callback, void *data); static void _gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline, GlTerminalSize *size); static void _gl_replace_prompt(GetLine *gl, const char *prompt); static int _gl_trap_signal(GetLine *gl, int signo, unsigned flags, GlAfterSignal after, int errno_value); static int _gl_raw_io(GetLine *gl, int redisplay); static int _gl_normal_io(GetLine *gl); static int _gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn, int list_only, const char *name, const char *keyseq); static int _gl_register_action(GetLine *gl, void *data, GlActionFn *fn, const char *name, const char *keyseq); static int _gl_io_mode(GetLine *gl, GlIOMode mode); static int _gl_set_term_size(GetLine *gl, int ncolumn, int nline); static int _gl_append_history(GetLine *gl, const char *line); /* * Reset the completion status and associated errno value in * gl->rtn_status and gl->rtn_errno. */ static void gl_clear_status(GetLine *gl); /* * Record a completion status, unless a previous abnormal completion * status has already been recorded for the current call. */ static void gl_record_status(GetLine *gl, GlReturnStatus rtn_status, int rtn_errno); /* * Set the maximum length of a line in a user's tecla configuration * file (not counting comments). */ #define GL_CONF_BUFLEN 100 /* * Set the maximum number of arguments supported by individual commands * in tecla configuration files. */ #define GL_CONF_MAXARG 10 /* * Prototype the available action functions. */ static KT_KEY_FN(gl_user_interrupt); static KT_KEY_FN(gl_abort); static KT_KEY_FN(gl_suspend); static KT_KEY_FN(gl_stop_output); static KT_KEY_FN(gl_start_output); static KT_KEY_FN(gl_literal_next); static KT_KEY_FN(gl_cursor_left); static KT_KEY_FN(gl_cursor_right); static KT_KEY_FN(gl_insert_mode); static KT_KEY_FN(gl_beginning_of_line); static KT_KEY_FN(gl_end_of_line); static KT_KEY_FN(gl_delete_line); static KT_KEY_FN(gl_kill_line); static KT_KEY_FN(gl_forward_word); static KT_KEY_FN(gl_backward_word); static KT_KEY_FN(gl_forward_delete_char); static KT_KEY_FN(gl_backward_delete_char); static KT_KEY_FN(gl_forward_delete_word); static KT_KEY_FN(gl_backward_delete_word); static KT_KEY_FN(gl_delete_refind); static KT_KEY_FN(gl_delete_invert_refind); static KT_KEY_FN(gl_delete_to_column); static KT_KEY_FN(gl_delete_to_parenthesis); static KT_KEY_FN(gl_forward_delete_find); static KT_KEY_FN(gl_backward_delete_find); static KT_KEY_FN(gl_forward_delete_to); static KT_KEY_FN(gl_backward_delete_to); static KT_KEY_FN(gl_upcase_word); static KT_KEY_FN(gl_downcase_word); static KT_KEY_FN(gl_capitalize_word); static KT_KEY_FN(gl_redisplay); static KT_KEY_FN(gl_clear_screen); static KT_KEY_FN(gl_transpose_chars); static KT_KEY_FN(gl_set_mark); static KT_KEY_FN(gl_exchange_point_and_mark); static KT_KEY_FN(gl_kill_region); static KT_KEY_FN(gl_copy_region_as_kill); static KT_KEY_FN(gl_yank); static KT_KEY_FN(gl_up_history); static KT_KEY_FN(gl_down_history); static KT_KEY_FN(gl_history_search_backward); static KT_KEY_FN(gl_history_re_search_backward); static KT_KEY_FN(gl_history_search_forward); static KT_KEY_FN(gl_history_re_search_forward); static KT_KEY_FN(gl_complete_word); #ifndef HIDE_FILE_SYSTEM static KT_KEY_FN(gl_expand_filename); static KT_KEY_FN(gl_read_from_file); static KT_KEY_FN(gl_read_init_files); static KT_KEY_FN(gl_list_glob); #endif static KT_KEY_FN(gl_del_char_or_list_or_eof); static KT_KEY_FN(gl_list_or_eof); static KT_KEY_FN(gl_beginning_of_history); static KT_KEY_FN(gl_end_of_history); static KT_KEY_FN(gl_digit_argument); static KT_KEY_FN(gl_newline); static KT_KEY_FN(gl_repeat_history); static KT_KEY_FN(gl_vi_insert); static KT_KEY_FN(gl_vi_overwrite); static KT_KEY_FN(gl_change_case); static KT_KEY_FN(gl_vi_insert_at_bol); static KT_KEY_FN(gl_vi_append_at_eol); static KT_KEY_FN(gl_vi_append); static KT_KEY_FN(gl_backward_kill_line); static KT_KEY_FN(gl_goto_column); static KT_KEY_FN(gl_forward_to_word); static KT_KEY_FN(gl_vi_replace_char); static KT_KEY_FN(gl_vi_change_rest_of_line); static KT_KEY_FN(gl_vi_change_line); static KT_KEY_FN(gl_vi_change_to_bol); static KT_KEY_FN(gl_vi_change_refind); static KT_KEY_FN(gl_vi_change_invert_refind); static KT_KEY_FN(gl_vi_change_to_column); static KT_KEY_FN(gl_vi_change_to_parenthesis); static KT_KEY_FN(gl_vi_forward_change_word); static KT_KEY_FN(gl_vi_backward_change_word); static KT_KEY_FN(gl_vi_forward_change_find); static KT_KEY_FN(gl_vi_backward_change_find); static KT_KEY_FN(gl_vi_forward_change_to); static KT_KEY_FN(gl_vi_backward_change_to); static KT_KEY_FN(gl_vi_forward_change_char); static KT_KEY_FN(gl_vi_backward_change_char); static KT_KEY_FN(gl_forward_copy_char); static KT_KEY_FN(gl_backward_copy_char); static KT_KEY_FN(gl_forward_find_char); static KT_KEY_FN(gl_backward_find_char); static KT_KEY_FN(gl_forward_to_char); static KT_KEY_FN(gl_backward_to_char); static KT_KEY_FN(gl_repeat_find_char); static KT_KEY_FN(gl_invert_refind_char); static KT_KEY_FN(gl_append_yank); static KT_KEY_FN(gl_backward_copy_word); static KT_KEY_FN(gl_forward_copy_word); static KT_KEY_FN(gl_copy_to_bol); static KT_KEY_FN(gl_copy_refind); static KT_KEY_FN(gl_copy_invert_refind); static KT_KEY_FN(gl_copy_to_column); static KT_KEY_FN(gl_copy_to_parenthesis); static KT_KEY_FN(gl_copy_rest_of_line); static KT_KEY_FN(gl_copy_line); static KT_KEY_FN(gl_backward_copy_find); static KT_KEY_FN(gl_forward_copy_find); static KT_KEY_FN(gl_backward_copy_to); static KT_KEY_FN(gl_forward_copy_to); static KT_KEY_FN(gl_vi_undo); static KT_KEY_FN(gl_emacs_editing_mode); static KT_KEY_FN(gl_vi_editing_mode); static KT_KEY_FN(gl_ring_bell); static KT_KEY_FN(gl_vi_repeat_change); static KT_KEY_FN(gl_find_parenthesis); static KT_KEY_FN(gl_list_history); static KT_KEY_FN(gl_list_completions); static KT_KEY_FN(gl_run_external_action); /* * Name the available action functions. */ static const struct {const char *name; KT_KEY_FN(*fn);} gl_actions[] = { {"user-interrupt", gl_user_interrupt}, {"abort", gl_abort}, {"suspend", gl_suspend}, {"stop-output", gl_stop_output}, {"start-output", gl_start_output}, {"literal-next", gl_literal_next}, {"cursor-right", gl_cursor_right}, {"cursor-left", gl_cursor_left}, {"insert-mode", gl_insert_mode}, {"beginning-of-line", gl_beginning_of_line}, {"end-of-line", gl_end_of_line}, {"delete-line", gl_delete_line}, {"kill-line", gl_kill_line}, {"forward-word", gl_forward_word}, {"backward-word", gl_backward_word}, {"forward-delete-char", gl_forward_delete_char}, {"backward-delete-char", gl_backward_delete_char}, {"forward-delete-word", gl_forward_delete_word}, {"backward-delete-word", gl_backward_delete_word}, {"delete-refind", gl_delete_refind}, {"delete-invert-refind", gl_delete_invert_refind}, {"delete-to-column", gl_delete_to_column}, {"delete-to-parenthesis", gl_delete_to_parenthesis}, {"forward-delete-find", gl_forward_delete_find}, {"backward-delete-find", gl_backward_delete_find}, {"forward-delete-to", gl_forward_delete_to}, {"backward-delete-to", gl_backward_delete_to}, {"upcase-word", gl_upcase_word}, {"downcase-word", gl_downcase_word}, {"capitalize-word", gl_capitalize_word}, {"redisplay", gl_redisplay}, {"clear-screen", gl_clear_screen}, {"transpose-chars", gl_transpose_chars}, {"set-mark", gl_set_mark}, {"exchange-point-and-mark", gl_exchange_point_and_mark}, {"kill-region", gl_kill_region}, {"copy-region-as-kill", gl_copy_region_as_kill}, {"yank", gl_yank}, {"up-history", gl_up_history}, {"down-history", gl_down_history}, {"history-search-backward", gl_history_search_backward}, {"history-re-search-backward", gl_history_re_search_backward}, {"history-search-forward", gl_history_search_forward}, {"history-re-search-forward", gl_history_re_search_forward}, {"complete-word", gl_complete_word}, #ifndef HIDE_FILE_SYSTEM {"expand-filename", gl_expand_filename}, {"read-from-file", gl_read_from_file}, {"read-init-files", gl_read_init_files}, {"list-glob", gl_list_glob}, #endif {"del-char-or-list-or-eof", gl_del_char_or_list_or_eof}, {"beginning-of-history", gl_beginning_of_history}, {"end-of-history", gl_end_of_history}, {"digit-argument", gl_digit_argument}, {"newline", gl_newline}, {"repeat-history", gl_repeat_history}, {"vi-insert", gl_vi_insert}, {"vi-overwrite", gl_vi_overwrite}, {"vi-insert-at-bol", gl_vi_insert_at_bol}, {"vi-append-at-eol", gl_vi_append_at_eol}, {"vi-append", gl_vi_append}, {"change-case", gl_change_case}, {"backward-kill-line", gl_backward_kill_line}, {"goto-column", gl_goto_column}, {"forward-to-word", gl_forward_to_word}, {"vi-replace-char", gl_vi_replace_char}, {"vi-change-rest-of-line", gl_vi_change_rest_of_line}, {"vi-change-line", gl_vi_change_line}, {"vi-change-to-bol", gl_vi_change_to_bol}, {"vi-change-refind", gl_vi_change_refind}, {"vi-change-invert-refind", gl_vi_change_invert_refind}, {"vi-change-to-column", gl_vi_change_to_column}, {"vi-change-to-parenthesis", gl_vi_change_to_parenthesis}, {"forward-copy-char", gl_forward_copy_char}, {"backward-copy-char", gl_backward_copy_char}, {"forward-find-char", gl_forward_find_char}, {"backward-find-char", gl_backward_find_char}, {"forward-to-char", gl_forward_to_char}, {"backward-to-char", gl_backward_to_char}, {"repeat-find-char", gl_repeat_find_char}, {"invert-refind-char", gl_invert_refind_char}, {"append-yank", gl_append_yank}, {"backward-copy-word", gl_backward_copy_word}, {"forward-copy-word", gl_forward_copy_word}, {"copy-to-bol", gl_copy_to_bol}, {"copy-refind", gl_copy_refind}, {"copy-invert-refind", gl_copy_invert_refind}, {"copy-to-column", gl_copy_to_column}, {"copy-to-parenthesis", gl_copy_to_parenthesis}, {"copy-rest-of-line", gl_copy_rest_of_line}, {"copy-line", gl_copy_line}, {"backward-copy-find", gl_backward_copy_find}, {"forward-copy-find", gl_forward_copy_find}, {"backward-copy-to", gl_backward_copy_to}, {"forward-copy-to", gl_forward_copy_to}, {"list-or-eof", gl_list_or_eof}, {"vi-undo", gl_vi_undo}, {"vi-backward-change-word", gl_vi_backward_change_word}, {"vi-forward-change-word", gl_vi_forward_change_word}, {"vi-backward-change-find", gl_vi_backward_change_find}, {"vi-forward-change-find", gl_vi_forward_change_find}, {"vi-backward-change-to", gl_vi_backward_change_to}, {"vi-forward-change-to", gl_vi_forward_change_to}, {"vi-backward-change-char", gl_vi_backward_change_char}, {"vi-forward-change-char", gl_vi_forward_change_char}, {"emacs-mode", gl_emacs_editing_mode}, {"vi-mode", gl_vi_editing_mode}, {"ring-bell", gl_ring_bell}, {"vi-repeat-change", gl_vi_repeat_change}, {"find-parenthesis", gl_find_parenthesis}, {"list-history", gl_list_history}, }; /* * Define the default key-bindings in emacs mode. */ static const KtKeyBinding gl_emacs_bindings[] = { {"right", "cursor-right"}, {"^F", "cursor-right"}, {"left", "cursor-left"}, {"^B", "cursor-left"}, {"M-i", "insert-mode"}, {"M-I", "insert-mode"}, {"^A", "beginning-of-line"}, {"^E", "end-of-line"}, {"^U", "delete-line"}, {"^K", "kill-line"}, {"M-f", "forward-word"}, {"M-F", "forward-word"}, {"M-b", "backward-word"}, {"M-B", "backward-word"}, {"^D", "del-char-or-list-or-eof"}, {"^H", "backward-delete-char"}, {"^?", "backward-delete-char"}, {"M-d", "forward-delete-word"}, {"M-D", "forward-delete-word"}, {"M-^H", "backward-delete-word"}, {"M-^?", "backward-delete-word"}, {"M-u", "upcase-word"}, {"M-U", "upcase-word"}, {"M-l", "downcase-word"}, {"M-L", "downcase-word"}, {"M-c", "capitalize-word"}, {"M-C", "capitalize-word"}, {"^R", "redisplay"}, {"^L", "clear-screen"}, {"^T", "transpose-chars"}, {"^@", "set-mark"}, {"^X^X", "exchange-point-and-mark"}, {"^W", "kill-region"}, {"M-w", "copy-region-as-kill"}, {"M-W", "copy-region-as-kill"}, {"^Y", "yank"}, {"^P", "up-history"}, {"up", "up-history"}, {"^N", "down-history"}, {"down", "down-history"}, {"M-p", "history-search-backward"}, {"M-P", "history-search-backward"}, {"M-n", "history-search-forward"}, {"M-N", "history-search-forward"}, {"\t", "complete-word"}, #ifndef HIDE_FILE_SYSTEM {"^X*", "expand-filename"}, {"^X^F", "read-from-file"}, {"^X^R", "read-init-files"}, {"^Xg", "list-glob"}, {"^XG", "list-glob"}, #endif {"^Xh", "list-history"}, {"^XH", "list-history"}, {"M-<", "beginning-of-history"}, {"M->", "end-of-history"}, {"M-0", "digit-argument"}, {"M-1", "digit-argument"}, {"M-2", "digit-argument"}, {"M-3", "digit-argument"}, {"M-4", "digit-argument"}, {"M-5", "digit-argument"}, {"M-6", "digit-argument"}, {"M-7", "digit-argument"}, {"M-8", "digit-argument"}, {"M-9", "digit-argument"}, {"\r", "newline"}, {"\n", "newline"}, {"M-o", "repeat-history"}, {"M-C-v", "vi-mode"}, }; /* * Define the default key-bindings in vi mode. Note that in vi-mode * meta-key bindings are command-mode bindings. For example M-i first * switches to command mode if not already in that mode, then moves * the cursor one position right, as in vi. */ static const KtKeyBinding gl_vi_bindings[] = { {"^D", "list-or-eof"}, #ifndef HIDE_FILE_SYSTEM {"^G", "list-glob"}, #endif {"^H", "backward-delete-char"}, {"\t", "complete-word"}, {"\r", "newline"}, {"\n", "newline"}, {"^L", "clear-screen"}, {"^N", "down-history"}, {"^P", "up-history"}, {"^R", "redisplay"}, {"^U", "backward-kill-line"}, {"^W", "backward-delete-word"}, #ifndef HIDE_FILE_SYSTEM {"^X^F", "read-from-file"}, {"^X^R", "read-init-files"}, {"^X*", "expand-filename"}, #endif {"^?", "backward-delete-char"}, {"M- ", "cursor-right"}, {"M-$", "end-of-line"}, #ifndef HIDE_FILE_SYSTEM {"M-*", "expand-filename"}, #endif {"M-+", "down-history"}, {"M--", "up-history"}, {"M-<", "beginning-of-history"}, {"M->", "end-of-history"}, {"M-^", "beginning-of-line"}, {"M-;", "repeat-find-char"}, {"M-,", "invert-refind-char"}, {"M-|", "goto-column"}, {"M-~", "change-case"}, {"M-.", "vi-repeat-change"}, {"M-%", "find-parenthesis"}, {"M-0", "digit-argument"}, {"M-1", "digit-argument"}, {"M-2", "digit-argument"}, {"M-3", "digit-argument"}, {"M-4", "digit-argument"}, {"M-5", "digit-argument"}, {"M-6", "digit-argument"}, {"M-7", "digit-argument"}, {"M-8", "digit-argument"}, {"M-9", "digit-argument"}, {"M-a", "vi-append"}, {"M-A", "vi-append-at-eol"}, {"M-b", "backward-word"}, {"M-B", "backward-word"}, {"M-C", "vi-change-rest-of-line"}, {"M-cb", "vi-backward-change-word"}, {"M-cB", "vi-backward-change-word"}, {"M-cc", "vi-change-line"}, {"M-ce", "vi-forward-change-word"}, {"M-cE", "vi-forward-change-word"}, {"M-cw", "vi-forward-change-word"}, {"M-cW", "vi-forward-change-word"}, {"M-cF", "vi-backward-change-find"}, {"M-cf", "vi-forward-change-find"}, {"M-cT", "vi-backward-change-to"}, {"M-ct", "vi-forward-change-to"}, {"M-c;", "vi-change-refind"}, {"M-c,", "vi-change-invert-refind"}, {"M-ch", "vi-backward-change-char"}, {"M-c^H", "vi-backward-change-char"}, {"M-c^?", "vi-backward-change-char"}, {"M-cl", "vi-forward-change-char"}, {"M-c ", "vi-forward-change-char"}, {"M-c^", "vi-change-to-bol"}, {"M-c0", "vi-change-to-bol"}, {"M-c$", "vi-change-rest-of-line"}, {"M-c|", "vi-change-to-column"}, {"M-c%", "vi-change-to-parenthesis"}, {"M-dh", "backward-delete-char"}, {"M-d^H", "backward-delete-char"}, {"M-d^?", "backward-delete-char"}, {"M-dl", "forward-delete-char"}, {"M-d ", "forward-delete-char"}, {"M-dd", "delete-line"}, {"M-db", "backward-delete-word"}, {"M-dB", "backward-delete-word"}, {"M-de", "forward-delete-word"}, {"M-dE", "forward-delete-word"}, {"M-dw", "forward-delete-word"}, {"M-dW", "forward-delete-word"}, {"M-dF", "backward-delete-find"}, {"M-df", "forward-delete-find"}, {"M-dT", "backward-delete-to"}, {"M-dt", "forward-delete-to"}, {"M-d;", "delete-refind"}, {"M-d,", "delete-invert-refind"}, {"M-d^", "backward-kill-line"}, {"M-d0", "backward-kill-line"}, {"M-d$", "kill-line"}, {"M-D", "kill-line"}, {"M-d|", "delete-to-column"}, {"M-d%", "delete-to-parenthesis"}, {"M-e", "forward-word"}, {"M-E", "forward-word"}, {"M-f", "forward-find-char"}, {"M-F", "backward-find-char"}, {"M--", "up-history"}, {"M-h", "cursor-left"}, {"M-H", "beginning-of-history"}, {"M-i", "vi-insert"}, {"M-I", "vi-insert-at-bol"}, {"M-j", "down-history"}, {"M-J", "history-search-forward"}, {"M-k", "up-history"}, {"M-K", "history-search-backward"}, {"M-l", "cursor-right"}, {"M-L", "end-of-history"}, {"M-n", "history-re-search-forward"}, {"M-N", "history-re-search-backward"}, {"M-p", "append-yank"}, {"M-P", "yank"}, {"M-r", "vi-replace-char"}, {"M-R", "vi-overwrite"}, {"M-s", "vi-forward-change-char"}, {"M-S", "vi-change-line"}, {"M-t", "forward-to-char"}, {"M-T", "backward-to-char"}, {"M-u", "vi-undo"}, {"M-w", "forward-to-word"}, {"M-W", "forward-to-word"}, {"M-x", "forward-delete-char"}, {"M-X", "backward-delete-char"}, {"M-yh", "backward-copy-char"}, {"M-y^H", "backward-copy-char"}, {"M-y^?", "backward-copy-char"}, {"M-yl", "forward-copy-char"}, {"M-y ", "forward-copy-char"}, {"M-ye", "forward-copy-word"}, {"M-yE", "forward-copy-word"}, {"M-yw", "forward-copy-word"}, {"M-yW", "forward-copy-word"}, {"M-yb", "backward-copy-word"}, {"M-yB", "backward-copy-word"}, {"M-yf", "forward-copy-find"}, {"M-yF", "backward-copy-find"}, {"M-yt", "forward-copy-to"}, {"M-yT", "backward-copy-to"}, {"M-y;", "copy-refind"}, {"M-y,", "copy-invert-refind"}, {"M-y^", "copy-to-bol"}, {"M-y0", "copy-to-bol"}, {"M-y$", "copy-rest-of-line"}, {"M-yy", "copy-line"}, {"M-Y", "copy-line"}, {"M-y|", "copy-to-column"}, {"M-y%", "copy-to-parenthesis"}, {"M-^E", "emacs-mode"}, {"M-^H", "cursor-left"}, {"M-^?", "cursor-left"}, {"M-^L", "clear-screen"}, {"M-^N", "down-history"}, {"M-^P", "up-history"}, {"M-^R", "redisplay"}, {"M-^D", "list-or-eof"}, {"M-\r", "newline"}, {"M-\t", "complete-word"}, {"M-\n", "newline"}, #ifndef HIDE_FILE_SYSTEM {"M-^X^R", "read-init-files"}, #endif {"M-^Xh", "list-history"}, {"M-^XH", "list-history"}, {"down", "down-history"}, {"up", "up-history"}, {"left", "cursor-left"}, {"right", "cursor-right"}, }; /*....................................................................... * Create a new GetLine object. * * Input: * linelen size_t The maximum line length to allow for. * histlen size_t The number of bytes to allocate for recording * a circular buffer of history lines. * Output: * return GetLine * The new object, or NULL on error. */ GetLine *new_GetLine(size_t linelen, size_t histlen) { GetLine *gl; /* The object to be returned */ int i; /* * Check the arguments. */ if(linelen < 10) { errno = ENOMEM; return NULL; }; /* * Allocate the container. */ gl = (GetLine *) malloc(sizeof(GetLine)); if(!gl) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_GetLine(). */ gl->err = NULL; gl->glh = NULL; gl->cpl = NULL; #ifndef HIDE_FILE_SYSTEM gl->cplfn.fn = cpl_file_completions; #else gl->cplfn.fn = gl_no_completions; #endif gl->cplfn.data = NULL; #ifndef WITHOUT_FILE_SYSTEM gl->ef = NULL; #endif gl->capmem = NULL; gl->cq = NULL; gl->input_fd = -1; gl->output_fd = -1; gl->input_fp = NULL; gl->output_fp = NULL; gl->file_fp = NULL; gl->term = NULL; gl->is_term = 0; gl->flush_fn = gl_flush_terminal; gl->io_mode = GL_NORMAL_MODE; gl->raw_mode = 0; gl->pending_io = GLP_WRITE; /* We will start by writing the prompt */ gl_clear_status(gl); gl->linelen = linelen; gl->line = NULL; gl->cutbuf = NULL; gl->prompt = NULL; gl->prompt_len = 0; gl->prompt_changed = 0; gl->prompt_style = GL_LITERAL_PROMPT; gl->cpl_mem = NULL; gl->ext_act_mem = NULL; gl->sig_mem = NULL; gl->sigs = NULL; gl->signals_masked = 0; gl->signals_overriden = 0; sigemptyset(&gl->all_signal_set); sigemptyset(&gl->old_signal_set); sigemptyset(&gl->use_signal_set); gl->bindings = NULL; gl->ntotal = 0; gl->buff_curpos = 0; gl->term_curpos = 0; gl->term_len = 0; gl->buff_mark = 0; gl->insert_curpos = 0; gl->insert = 1; gl->number = -1; gl->endline = 1; gl->displayed = 0; gl->redisplay = 0; gl->postpone = 0; gl->keybuf[0]='\0'; gl->nbuf = 0; gl->nread = 0; gl->current_action.fn = 0; gl->current_action.data = NULL; gl->current_count = 0; gl->preload_id = 0; gl->preload_history = 0; gl->keyseq_count = 0; gl->last_search = -1; gl->editor = GL_EMACS_MODE; gl->silence_bell = 0; gl->automatic_history = 1; gl->vi.undo.line = NULL; gl->vi.undo.buff_curpos = 0; gl->vi.undo.ntotal = 0; gl->vi.undo.saved = 0; gl->vi.repeat.action.fn = 0; gl->vi.repeat.action.data = 0; gl->vi.repeat.count = 0; gl->vi.repeat.input_curpos = 0; gl->vi.repeat.command_curpos = 0; gl->vi.repeat.input_char = '\0'; gl->vi.repeat.saved = 0; gl->vi.repeat.active = 0; gl->vi.command = 0; gl->vi.find_forward = 0; gl->vi.find_onto = 0; gl->vi.find_char = '\0'; gl->left = NULL; gl->right = NULL; gl->up = NULL; gl->down = NULL; gl->home = NULL; gl->bol = 0; gl->clear_eol = NULL; gl->clear_eod = NULL; gl->u_arrow = NULL; gl->d_arrow = NULL; gl->l_arrow = NULL; gl->r_arrow = NULL; gl->sound_bell = NULL; gl->bold = NULL; gl->underline = NULL; gl->standout = NULL; gl->dim = NULL; gl->reverse = NULL; gl->blink = NULL; gl->text_attr_off = NULL; gl->nline = 0; gl->ncolumn = 0; #ifdef USE_TERMINFO gl->left_n = NULL; gl->right_n = NULL; #elif defined(USE_TERMCAP) gl->tgetent_buf = NULL; gl->tgetstr_buf = NULL; #endif gl->app_file = NULL; gl->user_file = NULL; gl->configured = 0; gl->echo = 1; gl->last_signal = -1; #ifdef HAVE_SELECT gl->fd_node_mem = NULL; gl->fd_nodes = NULL; FD_ZERO(&gl->rfds); FD_ZERO(&gl->wfds); FD_ZERO(&gl->ufds); gl->max_fd = 0; gl->timer.dt.tv_sec = 0; gl->timer.dt.tv_usec = 0; gl->timer.fn = 0; gl->timer.data = NULL; #endif /* * Allocate an error reporting buffer. */ gl->err = _new_ErrMsg(); if(!gl->err) return del_GetLine(gl); /* * Allocate the history buffer. */ gl->glh = _new_GlHistory(histlen); if(!gl->glh) return del_GetLine(gl); /* * Allocate the resource object for file-completion. */ gl->cpl = new_WordCompletion(); if(!gl->cpl) return del_GetLine(gl); /* * Allocate the resource object for file-completion. */ #ifndef WITHOUT_FILE_SYSTEM gl->ef = new_ExpandFile(); if(!gl->ef) return del_GetLine(gl); #endif /* * Allocate a string-segment memory allocator for use in storing terminal * capablity strings. */ gl->capmem = _new_StringGroup(CAPMEM_SEGMENT_SIZE); if(!gl->capmem) return del_GetLine(gl); /* * Allocate the character queue that is used to buffer terminal output. */ gl->cq = _new_GlCharQueue(); if(!gl->cq) return del_GetLine(gl); /* * Allocate a line buffer, leaving 2 extra characters for the terminating * '\n' and '\0' characters */ gl->line = (char *) malloc(linelen + 2); if(!gl->line) { errno = ENOMEM; return del_GetLine(gl); }; /* * Start with an empty input line. */ gl_truncate_buffer(gl, 0); /* * Allocate a cut buffer. */ gl->cutbuf = (char *) malloc(linelen + 2); if(!gl->cutbuf) { errno = ENOMEM; return del_GetLine(gl); }; gl->cutbuf[0] = '\0'; /* * Allocate an initial empty prompt. */ _gl_replace_prompt(gl, NULL); if(!gl->prompt) { errno = ENOMEM; return del_GetLine(gl); }; /* * Allocate a vi undo buffer. */ gl->vi.undo.line = (char *) malloc(linelen + 2); if(!gl->vi.undo.line) { errno = ENOMEM; return del_GetLine(gl); }; gl->vi.undo.line[0] = '\0'; /* * Allocate a freelist from which to allocate nodes for the list * of completion functions. */ gl->cpl_mem = _new_FreeList(sizeof(GlCplCallback), GL_CPL_FREELIST_BLOCKING); if(!gl->cpl_mem) return del_GetLine(gl); /* * Allocate a freelist from which to allocate nodes for the list * of external action functions. */ gl->ext_act_mem = _new_FreeList(sizeof(GlExternalAction), GL_EXT_ACT_FREELIST_BLOCKING); if(!gl->ext_act_mem) return del_GetLine(gl); /* * Allocate a freelist from which to allocate nodes for the list * of signals. */ gl->sig_mem = _new_FreeList(sizeof(GlSignalNode), GLS_FREELIST_BLOCKING); if(!gl->sig_mem) return del_GetLine(gl); /* * Install initial dispositions for the default list of signals that * gl_get_line() traps. */ for(i=0; isigno, sig->flags, sig->after, sig->errno_value)) return del_GetLine(gl); }; /* * Allocate an empty table of key bindings. */ gl->bindings = _new_KeyTab(); if(!gl->bindings) return del_GetLine(gl); /* * Define the available actions that can be bound to key sequences. */ for(i=0; ibindings, gl_actions[i].name, gl_actions[i].fn, NULL)) return del_GetLine(gl); }; /* * Set up the default bindings. */ if(gl_change_editor(gl, gl->editor)) return del_GetLine(gl); /* * Allocate termcap buffers. */ #ifdef USE_TERMCAP gl->tgetent_buf = (char *) malloc(TERMCAP_BUF_SIZE); gl->tgetstr_buf = (char *) malloc(TERMCAP_BUF_SIZE); if(!gl->tgetent_buf || !gl->tgetstr_buf) { errno = ENOMEM; return del_GetLine(gl); }; #endif /* * Set up for I/O assuming stdin and stdout. */ if(_gl_change_terminal(gl, stdin, stdout, getenv("TERM"))) return del_GetLine(gl); /* * Create a freelist for use in allocating GlFdNode list nodes. */ #ifdef HAVE_SELECT gl->fd_node_mem = _new_FreeList(sizeof(GlFdNode), GLFD_FREELIST_BLOCKING); if(!gl->fd_node_mem) return del_GetLine(gl); #endif /* * We are done for now. */ return gl; } /*....................................................................... * Delete a GetLine object. * * Input: * gl GetLine * The object to be deleted. * Output: * return GetLine * The deleted object (always NULL). */ GetLine *del_GetLine(GetLine *gl) { if(gl) { /* * If the terminal is in raw server mode, reset it. */ _gl_normal_io(gl); /* * Deallocate all objects contained by gl. */ gl->err = _del_ErrMsg(gl->err); gl->glh = _del_GlHistory(gl->glh); gl->cpl = del_WordCompletion(gl->cpl); #ifndef WITHOUT_FILE_SYSTEM gl->ef = del_ExpandFile(gl->ef); #endif gl->capmem = _del_StringGroup(gl->capmem); gl->cq = _del_GlCharQueue(gl->cq); if(gl->file_fp) fclose(gl->file_fp); if(gl->term) free(gl->term); if(gl->line) free(gl->line); if(gl->cutbuf) free(gl->cutbuf); if(gl->prompt) free(gl->prompt); gl->cpl_mem = _del_FreeList(gl->cpl_mem, 1); gl->ext_act_mem = _del_FreeList(gl->ext_act_mem, 1); gl->sig_mem = _del_FreeList(gl->sig_mem, 1); gl->sigs = NULL; /* Already freed by freeing sig_mem */ gl->bindings = _del_KeyTab(gl->bindings); if(gl->vi.undo.line) free(gl->vi.undo.line); #ifdef USE_TERMCAP if(gl->tgetent_buf) free(gl->tgetent_buf); if(gl->tgetstr_buf) free(gl->tgetstr_buf); #endif if(gl->app_file) free(gl->app_file); if(gl->user_file) free(gl->user_file); #ifdef HAVE_SELECT gl->fd_node_mem = _del_FreeList(gl->fd_node_mem, 1); gl->fd_nodes = NULL; /* Already freed by freeing gl->fd_node_mem */ #endif /* * Delete the now empty container. */ free(gl); }; return NULL; } /*....................................................................... * Bind a control or meta character to an action. * * Input: * gl GetLine * The resource object of this program. * binder KtBinder The source of the binding. * c char The control or meta character. * If this is '\0', the call is ignored. * action const char * The action name to bind the key to. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_bind_control_char(GetLine *gl, KtBinder binder, char c, const char *action) { char keyseq[2]; /* * Quietly reject binding to the NUL control character, since this * is an ambiguous prefix of all bindings. */ if(c == '\0') return 0; /* * Making sure not to bind characters which aren't either control or * meta characters. */ if(IS_CTRL_CHAR(c) || IS_META_CHAR(c)) { keyseq[0] = c; keyseq[1] = '\0'; } else { return 0; }; /* * Install the binding. */ if(_kt_set_keybinding(gl->bindings, binder, keyseq, action)) { _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); return 1; }; return 0; } /*....................................................................... * Read a line from the user. * * Input: * gl GetLine * A resource object returned by new_GetLine(). * prompt char * The prompt to prefix the line with. * start_line char * The initial contents of the input line, or NULL * if it should start out empty. * start_pos int If start_line isn't NULL, this specifies the * index of the character over which the cursor * should initially be positioned within the line. * If you just want it to follow the last character * of the line, send -1. * Output: * return char * An internal buffer containing the input line, or * NULL at the end of input. If the line fitted in * the buffer there will be a '\n' newline character * before the terminating '\0'. If it was truncated * there will be no newline character, and the remains * of the line should be retrieved via further calls * to this function. */ char *gl_get_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos) { char *retval; /* The return value of _gl_get_line() */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return NULL; }; /* * Temporarily block all of the signals that we have been asked to trap. */ if(gl_mask_signals(gl, &gl->old_signal_set)) return NULL; /* * Perform the command-line editing task. */ retval = _gl_get_line(gl, prompt, start_line, start_pos); /* * Restore the process signal mask to how it was when this function was * first called. */ gl_unmask_signals(gl, &gl->old_signal_set); return retval; } /*....................................................................... * This is the main body of the public function gl_get_line(). */ static char *_gl_get_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos) { int waserr = 0; /* True if an error occurs */ /* * Assume that this call will successfully complete the input * line until proven otherwise. */ gl_clear_status(gl); /* * If this is the first call to this function since new_GetLine(), * complete any postponed configuration. */ if(!gl->configured) { (void) _gl_configure_getline(gl, NULL, NULL, TECLA_CONFIG_FILE); gl->configured = 1; }; /* * Before installing our signal handler functions, record the fact * that there are no pending signals. */ gl_pending_signal = -1; /* * Temporarily override the signal handlers of the calling program, * so that we can intercept signals that would leave the terminal * in a bad state. */ waserr = gl_override_signal_handlers(gl); /* * After recording the current terminal settings, switch the terminal * into raw input mode. */ waserr = waserr || _gl_raw_io(gl, 1); /* * Attempt to read the line. This will require more than one attempt if * either a current temporary input file is opened by gl_get_input_line() * or the end of a temporary input file is reached by gl_read_stream_line(). */ while(!waserr) { /* * Read a line from a non-interactive stream? */ if(gl->file_fp || !gl->is_term) { if(gl_read_stream_line(gl)==0) { break; } else if(gl->file_fp) { gl_revert_input(gl); gl_record_status(gl, GLR_NEWLINE, 0); } else { waserr = 1; break; }; }; /* * Read from the terminal? Note that the above if() block may have * changed gl->file_fp, so it is necessary to retest it here, rather * than using an else statement. */ if(!gl->file_fp && gl->is_term) { if(gl_get_input_line(gl, prompt, start_line, start_pos)) waserr = 1; else break; }; }; /* * If an error occurred, but gl->rtn_status is still set to * GLR_NEWLINE, change the status to GLR_ERROR. Otherwise * leave it at whatever specific value was assigned by the function * that aborted input. This means that only functions that trap * non-generic errors have to remember to update gl->rtn_status * themselves. */ if(waserr && gl->rtn_status == GLR_NEWLINE) gl_record_status(gl, GLR_ERROR, errno); /* * Restore terminal settings. */ if(gl->io_mode != GL_SERVER_MODE) _gl_normal_io(gl); /* * Restore the signal handlers. */ gl_restore_signal_handlers(gl); /* * If gl_get_line() gets aborted early, the errno value associated * with the event that caused this to happen is recorded in * gl->rtn_errno. Since errno may have been overwritten by cleanup * functions after this, restore its value to the value that it had * when the error condition occured, so that the caller can examine it * to find out what happened. */ errno = gl->rtn_errno; /* * Check the completion status to see how to return. */ switch(gl->rtn_status) { case GLR_NEWLINE: /* Success */ return gl->line; case GLR_BLOCKED: /* These events abort the current input line, */ case GLR_SIGNAL: /* when in normal blocking I/O mode, but only */ case GLR_TIMEOUT: /* temporarily pause line editing when in */ case GLR_FDABORT: /* non-blocking server I/O mode. */ if(gl->io_mode != GL_SERVER_MODE) _gl_abandon_line(gl); return NULL; case GLR_ERROR: /* Unrecoverable errors abort the input line, */ case GLR_EOF: /* regardless of the I/O mode. */ default: _gl_abandon_line(gl); return NULL; }; } /*....................................................................... * Read a single character from the user. * * Input: * gl GetLine * A resource object returned by new_GetLine(). * prompt char * The prompt to prefix the line with, or NULL if * no prompt is required. * defchar char The character to substitute if the * user simply hits return, or '\n' if you don't * need to substitute anything. * Output: * return int The character that was read, or EOF if the read * had to be aborted (in which case you can call * gl_return_status() to find out why). */ int gl_query_char(GetLine *gl, const char *prompt, char defchar) { int retval; /* The return value of _gl_query_char() */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return EOF; }; /* * Temporarily block all of the signals that we have been asked to trap. */ if(gl_mask_signals(gl, &gl->old_signal_set)) return EOF; /* * Perform the character reading task. */ retval = _gl_query_char(gl, prompt, defchar); /* * Restore the process signal mask to how it was when this function was * first called. */ gl_unmask_signals(gl, &gl->old_signal_set); return retval; } /*....................................................................... * This is the main body of the public function gl_query_char(). */ static int _gl_query_char(GetLine *gl, const char *prompt, char defchar) { int c = EOF; /* The character to be returned */ int waserr = 0; /* True if an error occurs */ /* * Assume that this call will successfully complete the input operation * until proven otherwise. */ gl_clear_status(gl); /* * If this is the first call to this function or gl_get_line(), * since new_GetLine(), complete any postponed configuration. */ if(!gl->configured) { (void) _gl_configure_getline(gl, NULL, NULL, TECLA_CONFIG_FILE); gl->configured = 1; }; /* * Before installing our signal handler functions, record the fact * that there are no pending signals. */ gl_pending_signal = -1; /* * Temporarily override the signal handlers of the calling program, * so that we can intercept signals that would leave the terminal * in a bad state. */ waserr = gl_override_signal_handlers(gl); /* * After recording the current terminal settings, switch the terminal * into raw input mode without redisplaying any partially entered * input line. */ waserr = waserr || _gl_raw_io(gl, 0); /* * Attempt to read the line. This will require more than one attempt if * either a current temporary input file is opened by gl_get_input_line() * or the end of a temporary input file is reached by gl_read_stream_line(). */ while(!waserr) { /* * Read a line from a non-interactive stream? */ if(gl->file_fp || !gl->is_term) { c = gl_read_stream_char(gl); if(c != EOF) { /* Success? */ if(c=='\n') c = defchar; break; } else if(gl->file_fp) { /* End of temporary input file? */ gl_revert_input(gl); gl_record_status(gl, GLR_NEWLINE, 0); } else { /* An error? */ waserr = 1; break; }; }; /* * Read from the terminal? Note that the above if() block may have * changed gl->file_fp, so it is necessary to retest it here, rather * than using an else statement. */ if(!gl->file_fp && gl->is_term) { c = gl_get_query_char(gl, prompt, defchar); if(c==EOF) waserr = 1; else break; }; }; /* * If an error occurred, but gl->rtn_status is still set to * GLR_NEWLINE, change the status to GLR_ERROR. Otherwise * leave it at whatever specific value was assigned by the function * that aborted input. This means that only functions that trap * non-generic errors have to remember to update gl->rtn_status * themselves. */ if(waserr && gl->rtn_status == GLR_NEWLINE) gl_record_status(gl, GLR_ERROR, errno); /* * Restore terminal settings. */ if(gl->io_mode != GL_SERVER_MODE) _gl_normal_io(gl); /* * Restore the signal handlers. */ gl_restore_signal_handlers(gl); /* * If this function gets aborted early, the errno value associated * with the event that caused this to happen is recorded in * gl->rtn_errno. Since errno may have been overwritten by cleanup * functions after this, restore its value to the value that it had * when the error condition occured, so that the caller can examine it * to find out what happened. */ errno = gl->rtn_errno; /* * Error conditions are signalled to the caller, by setting the returned * character to EOF. */ if(gl->rtn_status != GLR_NEWLINE) c = EOF; /* * In this mode, every character that is read is a completed * transaction, just like reading a completed input line, so prepare * for the next input line or character. */ _gl_abandon_line(gl); /* * Return the acquired character. */ return c; } /*....................................................................... * Record of the signal handlers of the calling program, so that they * can be restored later. * * Input: * gl GetLine * The resource object of this library. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_override_signal_handlers(GetLine *gl) { GlSignalNode *sig; /* A node in the list of signals to be caught */ /* * Set up our signal handler. */ SigAction act; act.sa_handler = gl_signal_handler; memcpy(&act.sa_mask, &gl->all_signal_set, sizeof(sigset_t)); act.sa_flags = 0; /* * Get the subset of the signals that we are supposed to trap that * should actually be trapped. */ sigemptyset(&gl->use_signal_set); for(sig=gl->sigs; sig; sig=sig->next) { /* * Trap this signal? If it is blocked by the calling program and we * haven't been told to unblock it, don't arrange to trap this signal. */ if(sig->flags & GLS_UNBLOCK_SIG || !sigismember(&gl->old_signal_set, sig->signo)) { if(sigaddset(&gl->use_signal_set, sig->signo) == -1) { _err_record_msg(gl->err, "sigaddset error", END_ERR_MSG); return 1; }; }; }; /* * Override the actions of the signals that we are trapping. */ for(sig=gl->sigs; sig; sig=sig->next) { if(sigismember(&gl->use_signal_set, sig->signo)) { sigdelset(&act.sa_mask, sig->signo); if(sigaction(sig->signo, &act, &sig->original)) { _err_record_msg(gl->err, "sigaction error", END_ERR_MSG); return 1; }; sigaddset(&act.sa_mask, sig->signo); }; }; /* * Record the fact that the application's signal handlers have now * been overriden. */ gl->signals_overriden = 1; /* * Just in case a SIGWINCH signal was sent to the process while our * SIGWINCH signal handler wasn't in place, check to see if the terminal * size needs updating. */ if(_gl_update_size(gl)) return 1; return 0; } /*....................................................................... * Restore the signal handlers of the calling program. * * Input: * gl GetLine * The resource object of this library. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_restore_signal_handlers(GetLine *gl) { GlSignalNode *sig; /* A node in the list of signals to be caught */ /* * Restore application signal handlers that were overriden * by gl_override_signal_handlers(). */ for(sig=gl->sigs; sig; sig=sig->next) { if(sigismember(&gl->use_signal_set, sig->signo) && sigaction(sig->signo, &sig->original, NULL)) { _err_record_msg(gl->err, "sigaction error", END_ERR_MSG); return 1; }; }; /* * Record the fact that the application's signal handlers have now * been restored. */ gl->signals_overriden = 0; return 0; } /*....................................................................... * This signal handler simply records the fact that a given signal was * caught in the file-scope gl_pending_signal variable. */ static void gl_signal_handler(int signo) { gl_pending_signal = signo; siglongjmp(gl_setjmp_buffer, 1); } /*....................................................................... * Switch the terminal into raw mode after storing the previous terminal * settings in gl->attributes. * * Input: * gl GetLine * The resource object of this program. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_raw_terminal_mode(GetLine *gl) { Termios newattr; /* The new terminal attributes */ /* * If the terminal is already in raw mode, do nothing. */ if(gl->raw_mode) return 0; /* * Record the current terminal attributes. */ if(tcgetattr(gl->input_fd, &gl->oldattr)) { _err_record_msg(gl->err, "tcgetattr error", END_ERR_MSG); return 1; }; /* * This function shouldn't do anything but record the current terminal * attritubes if editing has been disabled. */ if(gl->editor == GL_NO_EDITOR) return 0; /* * Modify the existing attributes. */ newattr = gl->oldattr; /* * Turn off local echo, canonical input mode and extended input processing. */ newattr.c_lflag &= ~(ECHO | ICANON | IEXTEN); /* * Don't translate carriage return to newline, turn off input parity * checking, don't strip off 8th bit, turn off output flow control. */ newattr.c_iflag &= ~(ICRNL | INPCK | ISTRIP); /* * Clear size bits, turn off parity checking, and allow 8-bit characters. */ newattr.c_cflag &= ~(CSIZE | PARENB); newattr.c_cflag |= CS8; /* * Turn off output processing. */ newattr.c_oflag &= ~(OPOST); /* * Request one byte at a time, without waiting. */ newattr.c_cc[VMIN] = gl->io_mode==GL_SERVER_MODE ? 0:1; newattr.c_cc[VTIME] = 0; /* * Install the new terminal modes. */ while(tcsetattr(gl->input_fd, TCSADRAIN, &newattr)) { if(errno != EINTR) { _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG); return 1; }; }; /* * Record the new terminal mode. */ gl->raw_mode = 1; return 0; } /*....................................................................... * Restore the terminal attributes recorded in gl->oldattr. * * Input: * gl GetLine * The resource object of this library. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_restore_terminal_attributes(GetLine *gl) { int waserr = 0; /* * If not in raw mode, do nothing. */ if(!gl->raw_mode) return 0; /* * Before changing the terminal attributes, make sure that all output * has been passed to the terminal. */ if(gl_flush_output(gl)) waserr = 1; /* * Reset the terminal attributes to the values that they had on * entry to gl_get_line(). */ while(tcsetattr(gl->input_fd, TCSADRAIN, &gl->oldattr)) { if(errno != EINTR) { _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG); waserr = 1; break; }; }; /* * Record the new terminal mode. */ gl->raw_mode = 0; return waserr; } /*....................................................................... * Switch the terminal file descriptor to use non-blocking I/O. * * Input: * gl GetLine * The resource object of gl_get_line(). * fd int The file descriptor to make non-blocking. */ static int gl_nonblocking_io(GetLine *gl, int fd) { int fcntl_flags; /* The new file-descriptor control flags */ /* * Is non-blocking I/O supported on this system? Note that even * without non-blocking I/O, the terminal will probably still act as * though it was non-blocking, because we also set the terminal * attributes to return immediately if no input is available and we * use select() to wait to be able to write. If select() also isn't * available, then input will probably remain fine, but output could * block, depending on the behaviour of the terminal driver. */ #if defined(NON_BLOCKING_FLAG) /* * Query the current file-control flags, and add the * non-blocking I/O flag. */ fcntl_flags = fcntl(fd, F_GETFL) | NON_BLOCKING_FLAG; /* * Install the new control flags. */ if(fcntl(fd, F_SETFL, fcntl_flags) == -1) { _err_record_msg(gl->err, "fcntl error", END_ERR_MSG); return 1; }; #endif return 0; } /*....................................................................... * Switch to blocking terminal I/O. * * Input: * gl GetLine * The resource object of gl_get_line(). * fd int The file descriptor to make blocking. */ static int gl_blocking_io(GetLine *gl, int fd) { int fcntl_flags; /* The new file-descriptor control flags */ /* * Is non-blocking I/O implemented on this system? */ #if defined(NON_BLOCKING_FLAG) /* * Query the current file control flags and remove the non-blocking * I/O flag. */ fcntl_flags = fcntl(fd, F_GETFL) & ~NON_BLOCKING_FLAG; /* * Install the modified control flags. */ if(fcntl(fd, F_SETFL, fcntl_flags) == -1) { _err_record_msg(gl->err, "fcntl error", END_ERR_MSG); return 1; }; #endif return 0; } /*....................................................................... * Read a new input line from the user. * * Input: * gl GetLine * The resource object of this library. * prompt char * The prompt to prefix the line with, or NULL to * use the same prompt that was used by the previous * line. * start_line char * The initial contents of the input line, or NULL * if it should start out empty. * start_pos int If start_line isn't NULL, this specifies the * index of the character over which the cursor * should initially be positioned within the line. * If you just want it to follow the last character * of the line, send -1. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_get_input_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos) { char c; /* The character being read */ /* * Flush any pending output to the terminal. */ if(_glq_char_count(gl->cq) > 0 && gl_flush_output(gl)) return 1; /* * Are we starting a new line? */ if(gl->endline) { /* * Delete any incompletely enterred line. */ if(gl_erase_line(gl)) return 1; /* * Display the new line to be edited. */ if(gl_present_line(gl, prompt, start_line, start_pos)) return 1; }; /* * Read one character at a time. */ while(gl_read_terminal(gl, 1, &c) == 0) { /* * Increment the count of the number of key sequences entered. */ gl->keyseq_count++; /* * Interpret the character either as the start of a new key-sequence, * as a continuation of a repeat count, or as a printable character * to be added to the line. */ if(gl_interpret_char(gl, c)) break; /* * If we just ran an action function which temporarily asked for * input to be taken from a file, abort this call. */ if(gl->file_fp) return 0; /* * Has the line been completed? */ if(gl->endline) return gl_line_ended(gl, c); }; /* * To get here, gl_read_terminal() must have returned non-zero. See * whether a signal was caught that requested that the current line * be returned. */ if(gl->endline) return gl_line_ended(gl, '\n'); /* * If I/O blocked while attempting to get the latest character * of the key sequence, rewind the key buffer to allow interpretation of * the current key sequence to be restarted on the next call to this * function. */ if(gl->rtn_status == GLR_BLOCKED && gl->pending_io == GLP_READ) gl->nread = 0; return 1; } /*....................................................................... * This is the private function of gl_query_char() that handles * prompting the user, reading a character from the terminal, and * displaying what the user entered. * * Input: * gl GetLine * The resource object of this library. * prompt char * The prompt to prefix the line with. * defchar char The character to substitute if the * user simply hits return, or '\n' if you don't * need to substitute anything. * Output: * return int The character that was read, or EOF if something * prevented a character from being read. */ static int gl_get_query_char(GetLine *gl, const char *prompt, int defchar) { char c; /* The character being read */ int retval; /* The return value of this function */ /* * Flush any pending output to the terminal. */ if(_glq_char_count(gl->cq) > 0 && gl_flush_output(gl)) return EOF; /* * Delete any incompletely entered line. */ if(gl_erase_line(gl)) return EOF; /* * Reset the line input parameters and display the prompt, if any. */ if(gl_present_line(gl, prompt, NULL, 0)) return EOF; /* * Read one character. */ if(gl_read_terminal(gl, 1, &c) == 0) { /* * In this mode, count each character as being a new key-sequence. */ gl->keyseq_count++; /* * Delete the character that was read, from the key-press buffer. */ gl_discard_chars(gl, gl->nread); /* * Convert carriage returns to newlines. */ if(c == '\r') c = '\n'; /* * If the user just hit return, subsitute the default character. */ if(c == '\n') c = defchar; /* * Display the entered character to the right of the prompt. */ if(c!='\n') { if(gl_end_of_line(gl, 1, NULL)==0) gl_print_char(gl, c, ' '); }; /* * Record the return character, and mark the call as successful. */ retval = c; gl_record_status(gl, GLR_NEWLINE, 0); /* * Was a signal caught whose disposition is to cause the current input * line to be returned? If so return a newline character. */ } else if(gl->endline) { retval = '\n'; gl_record_status(gl, GLR_NEWLINE, 0); } else { retval = EOF; }; /* * Start a new line. */ if(gl_start_newline(gl, 1)) return EOF; /* * Attempt to flush any pending output. */ (void) gl_flush_output(gl); /* * Return either the character that was read, or EOF if an error occurred. */ return retval; } /*....................................................................... * Add a character to the line buffer at the current cursor position, * inserting or overwriting according the current mode. * * Input: * gl GetLine * The resource object of this library. * c char The character to be added. * Output: * return int 0 - OK. * 1 - Insufficient room. */ static int gl_add_char_to_line(GetLine *gl, char c) { /* * Keep a record of the current cursor position. */ int buff_curpos = gl->buff_curpos; int term_curpos = gl->term_curpos; /* * Work out the displayed width of the new character. */ int width = gl_displayed_char_width(gl, c, term_curpos); /* * If we are in insert mode, or at the end of the line, * check that we can accomodate a new character in the buffer. * If not, simply return, leaving it up to the calling program * to check for the absence of a newline character. */ if((gl->insert || buff_curpos >= gl->ntotal) && gl->ntotal >= gl->linelen) return 0; /* * Are we adding characters to the line (ie. inserting or appending)? */ if(gl->insert || buff_curpos >= gl->ntotal) { /* * If inserting, make room for the new character. */ if(buff_curpos < gl->ntotal) gl_make_gap_in_buffer(gl, buff_curpos, 1); /* * Copy the character into the buffer. */ gl_buffer_char(gl, c, buff_curpos); gl->buff_curpos++; /* * Redraw the line from the cursor position to the end of the line, * and move the cursor to just after the added character. */ if(gl_print_string(gl, gl->line + buff_curpos, '\0') || gl_set_term_curpos(gl, term_curpos + width)) return 1; /* * Are we overwriting an existing character? */ } else { /* * Get the width of the character being overwritten. */ int old_width = gl_displayed_char_width(gl, gl->line[buff_curpos], term_curpos); /* * Overwrite the character in the buffer. */ gl_buffer_char(gl, c, buff_curpos); /* * If we are replacing with a narrower character, we need to * redraw the terminal string to the end of the line, then * overwrite the trailing old_width - width characters * with spaces. */ if(old_width > width) { if(gl_print_string(gl, gl->line + buff_curpos, '\0')) return 1; /* * Clear to the end of the terminal. */ if(gl_truncate_display(gl)) return 1; /* * Move the cursor to the end of the new character. */ if(gl_set_term_curpos(gl, term_curpos + width)) return 1; gl->buff_curpos++; /* * If we are replacing with a wider character, then we will be * inserting new characters, and thus extending the line. */ } else if(width > old_width) { /* * Redraw the line from the cursor position to the end of the line, * and move the cursor to just after the added character. */ if(gl_print_string(gl, gl->line + buff_curpos, '\0') || gl_set_term_curpos(gl, term_curpos + width)) return 1; gl->buff_curpos++; /* * The original and replacement characters have the same width, * so simply overwrite. */ } else { /* * Copy the character into the buffer. */ gl_buffer_char(gl, c, buff_curpos); gl->buff_curpos++; /* * Overwrite the original character. */ if(gl_print_char(gl, c, gl->line[gl->buff_curpos])) return 1; }; }; return 0; } /*....................................................................... * Insert/append a string to the line buffer and terminal at the current * cursor position. * * Input: * gl GetLine * The resource object of this library. * s char * The string to be added. * Output: * return int 0 - OK. * 1 - Insufficient room. */ static int gl_add_string_to_line(GetLine *gl, const char *s) { int buff_slen; /* The length of the string being added to line[] */ int term_slen; /* The length of the string being written to the terminal */ int buff_curpos; /* The original value of gl->buff_curpos */ int term_curpos; /* The original value of gl->term_curpos */ /* * Keep a record of the current cursor position. */ buff_curpos = gl->buff_curpos; term_curpos = gl->term_curpos; /* * How long is the string to be added? */ buff_slen = strlen(s); term_slen = gl_displayed_string_width(gl, s, buff_slen, term_curpos); /* * Check that we can accomodate the string in the buffer. * If not, simply return, leaving it up to the calling program * to check for the absence of a newline character. */ if(gl->ntotal + buff_slen > gl->linelen) return 0; /* * Move the characters that follow the cursor in the buffer by * buff_slen characters to the right. */ if(gl->ntotal > gl->buff_curpos) gl_make_gap_in_buffer(gl, gl->buff_curpos, buff_slen); /* * Copy the string into the buffer. */ gl_buffer_string(gl, s, buff_slen, gl->buff_curpos); gl->buff_curpos += buff_slen; /* * Write the modified part of the line to the terminal, then move * the terminal cursor to the end of the displayed input string. */ if(gl_print_string(gl, gl->line + buff_curpos, '\0') || gl_set_term_curpos(gl, term_curpos + term_slen)) return 1; return 0; } /*....................................................................... * Read a single character from the terminal. * * Input: * gl GetLine * The resource object of this library. * keep int If true, the returned character will be kept in * the input buffer, for potential replays. It should * subsequently be removed from the buffer when the * key sequence that it belongs to has been fully * processed, by calling gl_discard_chars(). * Input/Output: * c char * The character that is read, is assigned to *c. * Output: * return int 0 - OK. * 1 - Either an I/O error occurred, or a signal was * caught who's disposition is to abort gl_get_line() * or to have gl_get_line() return the current line * as though the user had pressed return. In the * latter case gl->endline will be non-zero. */ static int gl_read_terminal(GetLine *gl, int keep, char *c) { /* * Before waiting for a new character to be input, flush unwritten * characters to the terminal. */ if(gl_flush_output(gl)) return 1; /* * Record the fact that we are about to read from the terminal. */ gl->pending_io = GLP_READ; /* * If there is already an unread character in the buffer, * return it. */ if(gl->nread < gl->nbuf) { *c = gl->keybuf[gl->nread]; /* * Retain the character in the key buffer, but mark it as having been read? */ if(keep) { gl->nread++; /* * Completely remove the character from the key buffer? */ } else { memmove(gl->keybuf + gl->nread, gl->keybuf + gl->nread + 1, gl->nbuf - gl->nread - 1); }; return 0; }; /* * Make sure that there is space in the key buffer for one more character. * This should always be true if gl_interpret_char() is called for each * new character added, since it will clear the buffer once it has recognized * or rejected a key sequence. */ if(gl->nbuf + 1 > GL_KEY_MAX) { gl_print_info(gl, "gl_read_terminal: Buffer overflow avoided.", GL_END_INFO); errno = EIO; return 1; }; /* * Read one character from the terminal. */ switch(gl_read_input(gl, c)) { case GL_READ_OK: break; case GL_READ_BLOCKED: gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO); return 1; break; default: return 1; break; }; /* * Append the character to the key buffer? */ if(keep) { gl->keybuf[gl->nbuf] = *c; gl->nread = ++gl->nbuf; }; return 0; } /*....................................................................... * Read one or more keypresses from the terminal of an input stream. * * Input: * gl GetLine * The resource object of this module. * c char * The character that was read is assigned to *c. * Output: * return GlReadStatus The completion status of the read operation. */ static GlReadStatus gl_read_input(GetLine *gl, char *c) { /* * We may have to repeat the read if window change signals are received. */ for(;;) { /* * Which file descriptor should we read from? Mark this volatile, so * that siglongjmp() can't clobber it. */ volatile int fd = gl->file_fp ? fileno(gl->file_fp) : gl->input_fd; /* * If the endline flag becomes set, don't wait for another character. */ if(gl->endline) return GL_READ_ERROR; /* * Since the code in this function can block, trap signals. */ if(sigsetjmp(gl_setjmp_buffer, 1)==0) { /* * Handle the different I/O modes. */ switch(gl->io_mode) { /* * In normal I/O mode, we call the event handler before attempting * to read, since read() blocks. */ case GL_NORMAL_MODE: if(gl_event_handler(gl, fd)) return GL_READ_ERROR; return gl_read_unmasked(gl, fd, c); /* Read one character */ break; /* * In non-blocking server I/O mode, we attempt to read a character, * and only if this fails, call the event handler to wait for a any * user-configured timeout and any other user-configured events. In * addition, we turn off the fcntl() non-blocking flag when reading * from the terminal, to work around a bug in Solaris. We can do this * without causing the read() to block, because when in non-blocking * server-I/O mode, gl_raw_io() sets the VMIN terminal attribute to 0, * which tells the terminal driver to return immediately if no * characters are available to be read. */ case GL_SERVER_MODE: { GlReadStatus status; /* The return status */ if(isatty(fd)) /* If we reading from a terminal, */ gl_blocking_io(gl, fd); /* switch to blocking I/O */ status = gl_read_unmasked(gl, fd, c); /* Try reading */ if(status == GL_READ_BLOCKED) { /* Nothing readable yet */ if(gl_event_handler(gl, fd)) /* Wait for input */ status = GL_READ_ERROR; else status = gl_read_unmasked(gl, fd, c); /* Try reading again */ }; gl_nonblocking_io(gl, fd); /* Restore non-blocking I/O */ return status; }; break; }; }; /* * To get here, one of the signals that we are trapping must have * been received. Note that by using sigsetjmp() instead of setjmp() * the signal mask that was blocking these signals will have been * reinstated, so we can be sure that no more of these signals will * be received until we explicitly unblock them again. * * First, if non-blocking I/O was temporarily disabled, reinstate it. */ if(gl->io_mode == GL_SERVER_MODE) gl_nonblocking_io(gl, fd); /* * Now respond to the signal that was caught. */ if(gl_check_caught_signal(gl)) return GL_READ_ERROR; }; } /*....................................................................... * This is a private function of gl_read_input(), which unblocks signals * temporarily while it reads a single character from the specified file * descriptor. * * Input: * gl GetLine * The resource object of this module. * fd int The file descriptor to read from. * c char * The character that was read is assigned to *c. * Output: * return GlReadStatus The completion status of the read. */ static GlReadStatus gl_read_unmasked(GetLine *gl, int fd, char *c) { int nread; /* The return value of read() */ /* * Unblock the signals that we are trapping, while waiting for I/O. */ gl_catch_signals(gl); /* * Attempt to read one character from the terminal, restarting the read * if any signals that we aren't trapping, are received. */ do { errno = 0; nread = read(fd, c, 1); } while(nread < 0 && errno==EINTR); /* * Block all of the signals that we are trapping. */ gl_mask_signals(gl, NULL); /* * Check the completion status of the read. */ switch(nread) { case 1: return GL_READ_OK; case 0: return (isatty(fd) || errno != 0) ? GL_READ_BLOCKED : GL_READ_EOF; default: return GL_READ_ERROR; }; } /*....................................................................... * Remove a specified number of characters from the start of the * key-press lookahead buffer, gl->keybuf[], and arrange for the next * read to start from the character at the start of the shifted buffer. * * Input: * gl GetLine * The resource object of this module. * nused int The number of characters to discard from the start * of the buffer. */ static void gl_discard_chars(GetLine *gl, int nused) { int nkeep = gl->nbuf - nused; if(nkeep > 0) { memmove(gl->keybuf, gl->keybuf + nused, nkeep); gl->nbuf = nkeep; gl->nread = 0; } else { gl->nbuf = gl->nread = 0; }; } /*....................................................................... * This function is called to handle signals caught between calls to * sigsetjmp() and siglongjmp(). * * Input: * gl GetLine * The resource object of this library. * Output: * return int 0 - Signal handled internally. * 1 - Signal requires gl_get_line() to abort. */ static int gl_check_caught_signal(GetLine *gl) { GlSignalNode *sig; /* The signal disposition */ SigAction keep_action; /* The signal disposition of tecla signal handlers */ unsigned flags; /* The signal processing flags to use */ int signo; /* The signal to be handled */ /* * Was no signal caught? */ if(gl_pending_signal == -1) return 0; /* * Get the signal to be handled. */ signo = gl_pending_signal; /* * Mark the signal as handled. Note that at this point, all of * the signals that we are trapping are blocked from delivery. */ gl_pending_signal = -1; /* * Record the signal that was caught, so that the user can query it later. */ gl->last_signal = signo; /* * In non-blocking server mode, the application is responsible for * responding to terminal signals, and we don't want gl_get_line()s * normal signal handling to clash with this, so whenever a signal * is caught, we arrange for gl_get_line() to abort and requeue the * signal while signals are still blocked. If the application * had the signal unblocked when gl_get_line() was called, the signal * will be delivered again as soon as gl_get_line() restores the * process signal mask, just before returning to the application. * Note that the caller of this function should set gl->pending_io * to the appropriate choice of GLP_READ and GLP_WRITE, before returning. */ if(gl->io_mode==GL_SERVER_MODE) { gl_record_status(gl, GLR_SIGNAL, EINTR); raise(signo); return 1; }; /* * Lookup the requested disposition of this signal. */ for(sig=gl->sigs; sig && sig->signo != signo; sig=sig->next) ; if(!sig) return 0; /* * Get the signal response flags for this signal. */ flags = sig->flags; /* * Only perform terminal-specific actions if the session is interactive. */ if(gl->is_term) { /* * Did we receive a terminal size signal? */ #ifdef SIGWINCH if(signo == SIGWINCH && _gl_update_size(gl)) return 1; #endif /* * Start a fresh line? */ if(flags & GLS_RESTORE_LINE) { if(gl_start_newline(gl, 0)) return 1; }; /* * Restore terminal settings to how they were before gl_get_line() was * called? */ if(flags & GLS_RESTORE_TTY) gl_restore_terminal_attributes(gl); }; /* * Restore signal handlers to how they were before gl_get_line() was * called? If this hasn't been requested, only reinstate the signal * handler of the signal that we are handling. */ if(flags & GLS_RESTORE_SIG) { gl_restore_signal_handlers(gl); gl_unmask_signals(gl, &gl->old_signal_set); } else { (void) sigaction(sig->signo, &sig->original, &keep_action); (void) sigprocmask(SIG_UNBLOCK, &sig->proc_mask, NULL); }; /* * Forward the signal to the application's signal handler. */ if(!(flags & GLS_DONT_FORWARD)) raise(signo); /* * Reinstate our signal handlers. */ if(flags & GLS_RESTORE_SIG) { gl_mask_signals(gl, NULL); gl_override_signal_handlers(gl); } else { (void) sigaction(sig->signo, &keep_action, NULL); (void) sigprocmask(SIG_BLOCK, &sig->proc_mask, NULL); }; /* * Prepare the terminal for continued editing, if this is an interactive * session. */ if(gl->is_term) { /* * Do we need to reinstate our terminal settings? */ if(flags & GLS_RESTORE_TTY) gl_raw_terminal_mode(gl); /* * Redraw the line? */ if(flags & GLS_REDRAW_LINE) gl_queue_redisplay(gl); }; /* * What next? */ switch(sig->after) { case GLS_RETURN: gl_newline(gl, 1, NULL); return gl->is_term && gl_flush_output(gl); break; case GLS_ABORT: gl_record_status(gl, GLR_SIGNAL, sig->errno_value); return 1; break; case GLS_CONTINUE: return gl->is_term && gl_flush_output(gl); break; }; return 0; } /*....................................................................... * Get pertinent terminal control strings and the initial terminal size. * * Input: * gl GetLine * The resource object of this library. * term char * The type of the terminal. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_control_strings(GetLine *gl, const char *term) { int bad_term = 0; /* True if term is unusable */ /* * Discard any existing control strings from a previous terminal. */ gl->left = NULL; gl->right = NULL; gl->up = NULL; gl->down = NULL; gl->home = NULL; gl->bol = 0; gl->clear_eol = NULL; gl->clear_eod = NULL; gl->u_arrow = NULL; gl->d_arrow = NULL; gl->l_arrow = NULL; gl->r_arrow = NULL; gl->sound_bell = NULL; gl->bold = NULL; gl->underline = NULL; gl->standout = NULL; gl->dim = NULL; gl->reverse = NULL; gl->blink = NULL; gl->text_attr_off = NULL; gl->nline = 0; gl->ncolumn = 0; #ifdef USE_TERMINFO gl->left_n = NULL; gl->right_n = NULL; #endif /* * If possible lookup the information in a terminal information * database. */ #ifdef USE_TERMINFO { int errret; if(!term || setupterm((char *)term, gl->input_fd, &errret) == ERR) { bad_term = 1; } else { _clr_StringGroup(gl->capmem); gl->left = gl_tigetstr(gl, "cub1"); gl->right = gl_tigetstr(gl, "cuf1"); gl->up = gl_tigetstr(gl, "cuu1"); gl->down = gl_tigetstr(gl, "cud1"); gl->home = gl_tigetstr(gl, "home"); gl->clear_eol = gl_tigetstr(gl, "el"); gl->clear_eod = gl_tigetstr(gl, "ed"); gl->u_arrow = gl_tigetstr(gl, "kcuu1"); gl->d_arrow = gl_tigetstr(gl, "kcud1"); gl->l_arrow = gl_tigetstr(gl, "kcub1"); gl->r_arrow = gl_tigetstr(gl, "kcuf1"); gl->left_n = gl_tigetstr(gl, "cub"); gl->right_n = gl_tigetstr(gl, "cuf"); gl->sound_bell = gl_tigetstr(gl, "bel"); gl->bold = gl_tigetstr(gl, "bold"); gl->underline = gl_tigetstr(gl, "smul"); gl->standout = gl_tigetstr(gl, "smso"); gl->dim = gl_tigetstr(gl, "dim"); gl->reverse = gl_tigetstr(gl, "rev"); gl->blink = gl_tigetstr(gl, "blink"); gl->text_attr_off = gl_tigetstr(gl, "sgr0"); }; }; #elif defined(USE_TERMCAP) if(!term || tgetent(gl->tgetent_buf, (char *)term) < 0) { bad_term = 1; } else { char *tgetstr_buf_ptr = gl->tgetstr_buf; _clr_StringGroup(gl->capmem); gl->left = gl_tgetstr(gl, "le", &tgetstr_buf_ptr); gl->right = gl_tgetstr(gl, "nd", &tgetstr_buf_ptr); gl->up = gl_tgetstr(gl, "up", &tgetstr_buf_ptr); gl->down = gl_tgetstr(gl, "do", &tgetstr_buf_ptr); gl->home = gl_tgetstr(gl, "ho", &tgetstr_buf_ptr); gl->clear_eol = gl_tgetstr(gl, "ce", &tgetstr_buf_ptr); gl->clear_eod = gl_tgetstr(gl, "cd", &tgetstr_buf_ptr); gl->u_arrow = gl_tgetstr(gl, "ku", &tgetstr_buf_ptr); gl->d_arrow = gl_tgetstr(gl, "kd", &tgetstr_buf_ptr); gl->l_arrow = gl_tgetstr(gl, "kl", &tgetstr_buf_ptr); gl->r_arrow = gl_tgetstr(gl, "kr", &tgetstr_buf_ptr); gl->sound_bell = gl_tgetstr(gl, "bl", &tgetstr_buf_ptr); gl->bold = gl_tgetstr(gl, "md", &tgetstr_buf_ptr); gl->underline = gl_tgetstr(gl, "us", &tgetstr_buf_ptr); gl->standout = gl_tgetstr(gl, "so", &tgetstr_buf_ptr); gl->dim = gl_tgetstr(gl, "mh", &tgetstr_buf_ptr); gl->reverse = gl_tgetstr(gl, "mr", &tgetstr_buf_ptr); gl->blink = gl_tgetstr(gl, "mb", &tgetstr_buf_ptr); gl->text_attr_off = gl_tgetstr(gl, "me", &tgetstr_buf_ptr); }; #endif /* * Report term being unusable. */ if(bad_term) { gl_print_info(gl, "Bad terminal type: \"", term ? term : "(null)", "\". Will assume vt100.", GL_END_INFO); }; /* * Fill in missing information with ANSI VT100 strings. */ if(!gl->left) gl->left = "\b"; /* ^H */ if(!gl->right) gl->right = GL_ESC_STR "[C"; if(!gl->up) gl->up = GL_ESC_STR "[A"; if(!gl->down) gl->down = "\n"; if(!gl->home) gl->home = GL_ESC_STR "[H"; if(!gl->bol) gl->bol = "\r"; if(!gl->clear_eol) gl->clear_eol = GL_ESC_STR "[K"; if(!gl->clear_eod) gl->clear_eod = GL_ESC_STR "[J"; if(!gl->u_arrow) gl->u_arrow = GL_ESC_STR "[A"; if(!gl->d_arrow) gl->d_arrow = GL_ESC_STR "[B"; if(!gl->l_arrow) gl->l_arrow = GL_ESC_STR "[D"; if(!gl->r_arrow) gl->r_arrow = GL_ESC_STR "[C"; if(!gl->sound_bell) gl->sound_bell = "\a"; if(!gl->bold) gl->bold = GL_ESC_STR "[1m"; if(!gl->underline) gl->underline = GL_ESC_STR "[4m"; if(!gl->standout) gl->standout = GL_ESC_STR "[1;7m"; if(!gl->dim) gl->dim = ""; /* Not available */ if(!gl->reverse) gl->reverse = GL_ESC_STR "[7m"; if(!gl->blink) gl->blink = GL_ESC_STR "[5m"; if(!gl->text_attr_off) gl->text_attr_off = GL_ESC_STR "[m"; /* * Find out the current terminal size. */ (void) _gl_terminal_size(gl, GL_DEF_NCOLUMN, GL_DEF_NLINE, NULL); return 0; } #ifdef USE_TERMINFO /*....................................................................... * This is a private function of gl_control_strings() used to look up * a termninal capability string from the terminfo database and make * a private copy of it. * * Input: * gl GetLine * The resource object of gl_get_line(). * name const char * The name of the terminfo string to look up. * Output: * return const char * The local copy of the capability, or NULL * if not available. */ static const char *gl_tigetstr(GetLine *gl, const char *name) { const char *value = tigetstr((char *)name); if(!value || value == (char *) -1) return NULL; return _sg_store_string(gl->capmem, value, 0); } #elif defined(USE_TERMCAP) /*....................................................................... * This is a private function of gl_control_strings() used to look up * a termninal capability string from the termcap database and make * a private copy of it. Note that some emulations of tgetstr(), such * as that used by Solaris, ignores the buffer pointer that is past to * it, so we can't assume that a private copy has been made that won't * be trashed by another call to gl_control_strings() by another * GetLine object. So we make what may be a redundant private copy * of the string in gl->capmem. * * Input: * gl GetLine * The resource object of gl_get_line(). * name const char * The name of the terminfo string to look up. * Input/Output: * bufptr char ** On input *bufptr points to the location in * gl->tgetstr_buf at which to record the * capability string. On output *bufptr is * incremented over the stored string. * Output: * return const char * The local copy of the capability, or NULL * on error. */ static const char *gl_tgetstr(GetLine *gl, const char *name, char **bufptr) { const char *value = tgetstr((char *)name, bufptr); if(!value || value == (char *) -1) return NULL; return _sg_store_string(gl->capmem, value, 0); } #endif /*....................................................................... * This is an action function that implements a user interrupt (eg. ^C). */ static KT_KEY_FN(gl_user_interrupt) { raise(SIGINT); return 1; } /*....................................................................... * This is an action function that implements the abort signal. */ static KT_KEY_FN(gl_abort) { raise(SIGABRT); return 1; } /*....................................................................... * This is an action function that sends a suspend signal (eg. ^Z) to the * the parent process. */ static KT_KEY_FN(gl_suspend) { raise(SIGTSTP); return 0; } /*....................................................................... * This is an action function that halts output to the terminal. */ static KT_KEY_FN(gl_stop_output) { tcflow(gl->output_fd, TCOOFF); return 0; } /*....................................................................... * This is an action function that resumes halted terminal output. */ static KT_KEY_FN(gl_start_output) { tcflow(gl->output_fd, TCOON); return 0; } /*....................................................................... * This is an action function that allows the next character to be accepted * without any interpretation as a special character. */ static KT_KEY_FN(gl_literal_next) { char c; /* The character to be added to the line */ int i; /* * Get the character to be inserted literally. */ if(gl_read_terminal(gl, 1, &c)) return 1; /* * Add the character to the line 'count' times. */ for(i=0; incolumn) % TAB_WIDTH); } /*....................................................................... * Return the number of characters needed to display a given character * on the screen. Tab characters require eight spaces, and control * characters are represented by a caret followed by the modified * character. * * Input: * gl GetLine * The resource object of this library. * c char The character to be displayed. * term_curpos int The destination terminal location of the character. * This is needed because the width of tab characters * depends on where they are, relative to the * preceding tab stops. * Output: * return int The number of terminal charaters needed. */ static int gl_displayed_char_width(GetLine *gl, char c, int term_curpos) { if(c=='\t') return gl_displayed_tab_width(gl, term_curpos); if(IS_CTRL_CHAR(c)) return 2; if(!isprint((int)(unsigned char) c)) return gl_octal_width((int)(unsigned char)c) + 1; return 1; } /*....................................................................... * Work out the length of given string of characters on the terminal. * * Input: * gl GetLine * The resource object of this library. * string char * The string to be measured. * nc int The number of characters to be measured, or -1 * to measure the whole string. * term_curpos int The destination terminal location of the character. * This is needed because the width of tab characters * depends on where they are, relative to the * preceding tab stops. * Output: * return int The number of displayed characters. */ static int gl_displayed_string_width(GetLine *gl, const char *string, int nc, int term_curpos) { int slen = 0; /* The displayed number of characters */ int i; /* * How many characters are to be measured? */ if(nc < 0) nc = strlen(string); /* * Add up the length of the displayed string. */ for(i=0; iflush_fn; /* * Only display output when echoing is turned on. */ if(gl->echo) { int ndone = 0; /* The number of characters written so far */ /* * When using un-buffered I/O, flush pending output first. */ if(!buffered) { if(gl_flush_output(gl)) return 1; }; /* * If no length has been provided, measure the length of the string. */ if(n < 0) n = strlen(string); /* * Write the string. */ if(write_fn(gl, string + ndone, n-ndone) != n) return 1; }; return 0; } /*....................................................................... * Output a terminal control sequence. When using terminfo, * this must be a sequence returned by tgetstr() or tigetstr() * respectively. * * Input: * gl GetLine * The resource object of this library. * nline int The number of lines affected by the operation, * or 1 if not relevant. * string char * The control sequence to be sent. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_print_control_sequence(GetLine *gl, int nline, const char *string) { int waserr = 0; /* True if an error occurs */ /* * Only write characters to the terminal when echoing is enabled. */ if(gl->echo) { #if defined(USE_TERMINFO) || defined(USE_TERMCAP) tputs_gl = gl; errno = 0; tputs((char *)string, nline, gl_tputs_putchar); waserr = errno != 0; #else waserr = gl_print_raw_string(gl, 1, string, -1); #endif }; return waserr; } #if defined(USE_TERMINFO) || defined(USE_TERMCAP) /*....................................................................... * The following callback function is called by tputs() to output a raw * control character to the terminal. */ static TputsRetType gl_tputs_putchar(TputsArgType c) { char ch = c; #if TPUTS_RETURNS_VALUE return gl_print_raw_string(tputs_gl, 1, &ch, 1); #else (void) gl_print_raw_string(tputs_gl, 1, &ch, 1); #endif } #endif /*....................................................................... * Move the terminal cursor n characters to the left or right. * * Input: * gl GetLine * The resource object of this program. * n int number of positions to the right (> 0) or left (< 0). * Output: * return int 0 - OK. * 1 - Error. */ static int gl_terminal_move_cursor(GetLine *gl, int n) { int cur_row, cur_col; /* The current terminal row and column index of */ /* the cursor wrt the start of the input line. */ int new_row, new_col; /* The target terminal row and column index of */ /* the cursor wrt the start of the input line. */ /* * Do nothing if the input line isn't currently displayed. In this * case, the cursor will be moved to the right place when the line * is next redisplayed. */ if(!gl->displayed) return 0; /* * How far can we move left? */ if(gl->term_curpos + n < 0) n = gl->term_curpos; /* * Break down the current and target cursor locations into rows and columns. */ /**** BEGIN ABB: 2009-06-15 div-by-zero exception ***/ if (gl->ncolumn == 0) { gl->ncolumn = 1; } /**** END ABB: 2009-06-15 div-by-zero exception ***/ cur_row = gl->term_curpos / gl->ncolumn; cur_col = gl->term_curpos % gl->ncolumn; new_row = (gl->term_curpos + n) / gl->ncolumn; new_col = (gl->term_curpos + n) % gl->ncolumn; /* * Move down to the next line. */ for(; cur_row < new_row; cur_row++) { if(gl_print_control_sequence(gl, 1, gl->down)) return 1; }; /* * Move up to the previous line. */ for(; cur_row > new_row; cur_row--) { if(gl_print_control_sequence(gl, 1, gl->up)) return 1; }; /* * Move to the right within the target line? */ if(cur_col < new_col) { #ifdef USE_TERMINFO /* * Use a parameterized control sequence if it generates less control * characters (guess based on ANSI terminal termcap entry). */ if(gl->right_n != NULL && new_col - cur_col > 1) { if(gl_print_control_sequence(gl, 1, tparm((char *)gl->right_n, (long)(new_col - cur_col), 0l, 0l, 0l, 0l, 0l, 0l, 0l, 0l))) return 1; } else #endif { for(; cur_col < new_col; cur_col++) { if(gl_print_control_sequence(gl, 1, gl->right)) return 1; }; }; /* * Move to the left within the target line? */ } else if(cur_col > new_col) { #ifdef USE_TERMINFO /* * Use a parameterized control sequence if it generates less control * characters (guess based on ANSI terminal termcap entry). */ if(gl->left_n != NULL && cur_col - new_col > 3) { if(gl_print_control_sequence(gl, 1, tparm((char *)gl->left_n, (long)(cur_col - new_col), 0l, 0l, 0l, 0l, 0l, 0l, 0l, 0l))) return 1; } else #endif { for(; cur_col > new_col; cur_col--) { if(gl_print_control_sequence(gl, 1, gl->left)) return 1; }; }; } /* * Update the recorded position of the terminal cursor. */ gl->term_curpos += n; return 0; } /*....................................................................... * Write a character to the terminal after expanding tabs and control * characters to their multi-character representations. * * Input: * gl GetLine * The resource object of this program. * c char The character to be output. * pad char Many terminals have the irritating feature that * when one writes a character in the last column of * of the terminal, the cursor isn't wrapped to the * start of the next line until one more character * is written. Some terminals don't do this, so * after such a write, we don't know where the * terminal is unless we output an extra character. * This argument specifies the character to write. * If at the end of the input line send '\0' or a * space, and a space will be written. Otherwise, * pass the next character in the input line * following the one being written. * Output: * return int 0 - OK. */ static int gl_print_char(GetLine *gl, char c, char pad) { char string[TAB_WIDTH + 4]; /* A work area for composing compound strings */ int nchar; /* The number of terminal characters */ int i; /* * Check for special characters. */ if(c == '\t') { /* * How many spaces do we need to represent a tab at the current terminal * column? */ nchar = gl_displayed_tab_width(gl, gl->term_curpos); /* * Compose the tab string. */ for(i=0; iterm_curpos += nchar; /* * Keep a record of the number of characters in the terminal version * of the input line. */ if(gl->term_curpos > gl->term_len) gl->term_len = gl->term_curpos; /* * If the new character ended exactly at the end of a line, * most terminals won't move the cursor onto the next line until we * have written a character on the next line, so append an extra * space then move the cursor back. */ if(gl->term_curpos % gl->ncolumn == 0) { int term_curpos = gl->term_curpos; if(gl_print_char(gl, pad ? pad : ' ', ' ') || gl_set_term_curpos(gl, term_curpos)) return 1; }; return 0; } /*....................................................................... * Write a string to the terminal after expanding tabs and control * characters to their multi-character representations. * * Input: * gl GetLine * The resource object of this program. * string char * The string to be output. * pad char Many terminals have the irritating feature that * when one writes a character in the last column of * of the terminal, the cursor isn't wrapped to the * start of the next line until one more character * is written. Some terminals don't do this, so * after such a write, we don't know where the * terminal is unless we output an extra character. * This argument specifies the character to write. * If at the end of the input line send '\0' or a * space, and a space will be written. Otherwise, * pass the next character in the input line * following the one being written. * Output: * return int 0 - OK. */ static int gl_print_string(GetLine *gl, const char *string, char pad) { const char *cptr; /* A pointer into string[] */ for(cptr=string; *cptr; cptr++) { char nextc = cptr[1]; if(gl_print_char(gl, *cptr, nextc ? nextc : pad)) return 1; }; return 0; } /*....................................................................... * Move the terminal cursor position. * * Input: * gl GetLine * The resource object of this library. * term_curpos int The destination terminal cursor position. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_set_term_curpos(GetLine *gl, int term_curpos) { return gl_terminal_move_cursor(gl, term_curpos - gl->term_curpos); } /*....................................................................... * This is an action function that moves the buffer cursor one character * left, and updates the terminal cursor to match. */ static KT_KEY_FN(gl_cursor_left) { return gl_place_cursor(gl, gl->buff_curpos - count); } /*....................................................................... * This is an action function that moves the buffer cursor one character * right, and updates the terminal cursor to match. */ static KT_KEY_FN(gl_cursor_right) { return gl_place_cursor(gl, gl->buff_curpos + count); } /*....................................................................... * This is an action function that toggles between overwrite and insert * mode. */ static KT_KEY_FN(gl_insert_mode) { gl->insert = !gl->insert; return 0; } /*....................................................................... * This is an action function which moves the cursor to the beginning of * the line. */ static KT_KEY_FN(gl_beginning_of_line) { return gl_place_cursor(gl, 0); } /*....................................................................... * This is an action function which moves the cursor to the end of * the line. */ static KT_KEY_FN(gl_end_of_line) { return gl_place_cursor(gl, gl->ntotal); } /*....................................................................... * This is an action function which deletes the entire contents of the * current line. */ static KT_KEY_FN(gl_delete_line) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Copy the contents of the line to the cut buffer. */ strcpy(gl->cutbuf, gl->line); /* * Clear the buffer. */ gl_truncate_buffer(gl, 0); /* * Move the terminal cursor to just after the prompt. */ if(gl_place_cursor(gl, 0)) return 1; /* * Clear from the end of the prompt to the end of the terminal. */ if(gl_truncate_display(gl)) return 1; return 0; } /*....................................................................... * This is an action function which deletes all characters between the * current cursor position and the end of the line. */ static KT_KEY_FN(gl_kill_line) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Copy the part of the line that is about to be deleted to the cut buffer. */ strcpy(gl->cutbuf, gl->line + gl->buff_curpos); /* * Terminate the buffered line at the current cursor position. */ gl_truncate_buffer(gl, gl->buff_curpos); /* * Clear the part of the line that follows the cursor. */ if(gl_truncate_display(gl)) return 1; /* * Explicitly reset the cursor position to allow vi command mode * constraints on its position to be set. */ return gl_place_cursor(gl, gl->buff_curpos); } /*....................................................................... * This is an action function which deletes all characters between the * start of the line and the current cursor position. */ static KT_KEY_FN(gl_backward_kill_line) { /* * How many characters are to be deleted from before the cursor? */ int nc = gl->buff_curpos - gl->insert_curpos; if (!nc) return 0; /* * Move the cursor to the start of the line, or in vi input mode, * the start of the sub-line at which insertion started, and delete * up to the old cursor position. */ return gl_place_cursor(gl, gl->insert_curpos) || gl_delete_chars(gl, nc, gl->editor == GL_EMACS_MODE || gl->vi.command); } /*....................................................................... * This is an action function which moves the cursor forward by a word. */ static KT_KEY_FN(gl_forward_word) { return gl_place_cursor(gl, gl_nth_word_end_forward(gl, count) + (gl->editor==GL_EMACS_MODE)); } /*....................................................................... * This is an action function which moves the cursor forward to the start * of the next word. */ static KT_KEY_FN(gl_forward_to_word) { return gl_place_cursor(gl, gl_nth_word_start_forward(gl, count)); } /*....................................................................... * This is an action function which moves the cursor backward by a word. */ static KT_KEY_FN(gl_backward_word) { return gl_place_cursor(gl, gl_nth_word_start_backward(gl, count)); } /*....................................................................... * Delete one or more characters, starting with the one under the cursor. * * Input: * gl GetLine * The resource object of this library. * nc int The number of characters to delete. * cut int If true, copy the characters to the cut buffer. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_delete_chars(GetLine *gl, int nc, int cut) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * If there are fewer than nc characters following the cursor, limit * nc to the number available. */ if(gl->buff_curpos + nc > gl->ntotal) nc = gl->ntotal - gl->buff_curpos; /* * Copy the about to be deleted region to the cut buffer. */ if(cut) { memcpy(gl->cutbuf, gl->line + gl->buff_curpos, nc); gl->cutbuf[nc] = '\0'; } /* * Nothing to delete? */ if(nc <= 0) return 0; /* * In vi overwrite mode, restore any previously overwritten characters * from the undo buffer. */ if(gl->editor == GL_VI_MODE && !gl->vi.command && !gl->insert) { /* * How many of the characters being deleted can be restored from the * undo buffer? */ int nrestore = gl->buff_curpos + nc <= gl->vi.undo.ntotal ? nc : gl->vi.undo.ntotal - gl->buff_curpos; /* * Restore any available characters. */ if(nrestore > 0) { gl_buffer_string(gl, gl->vi.undo.line + gl->buff_curpos, nrestore, gl->buff_curpos); }; /* * If their were insufficient characters in the undo buffer, then this * implies that we are deleting from the end of the line, so we need * to terminate the line either where the undo buffer ran out, or if * we are deleting from beyond the end of the undo buffer, at the current * cursor position. */ if(nc != nrestore) { gl_truncate_buffer(gl, (gl->vi.undo.ntotal > gl->buff_curpos) ? gl->vi.undo.ntotal : gl->buff_curpos); }; } else { /* * Copy the remaining part of the line back over the deleted characters. */ gl_remove_from_buffer(gl, gl->buff_curpos, nc); }; /* * Redraw the remaining characters following the cursor. */ if(gl_print_string(gl, gl->line + gl->buff_curpos, '\0')) return 1; /* * Clear to the end of the terminal. */ if(gl_truncate_display(gl)) return 1; /* * Place the cursor at the start of where the deletion was performed. */ return gl_place_cursor(gl, gl->buff_curpos); } /*....................................................................... * This is an action function which deletes character(s) under the * cursor without moving the cursor. */ static KT_KEY_FN(gl_forward_delete_char) { /* * Delete 'count' characters. */ return gl_delete_chars(gl, count, gl->vi.command); } /*....................................................................... * This is an action function which deletes character(s) under the * cursor and moves the cursor back one character. */ static KT_KEY_FN(gl_backward_delete_char) { /* * Restrict the deletion count to the number of characters that * precede the insertion point. */ if(count > gl->buff_curpos - gl->insert_curpos) count = gl->buff_curpos - gl->insert_curpos; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); return gl_cursor_left(gl, count, NULL) || gl_delete_chars(gl, count, gl->vi.command); } /*....................................................................... * Starting from the cursor position delete to the specified column. */ static KT_KEY_FN(gl_delete_to_column) { if (--count >= gl->buff_curpos) return gl_forward_delete_char(gl, count - gl->buff_curpos, NULL); else return gl_backward_delete_char(gl, gl->buff_curpos - count, NULL); } /*....................................................................... * Starting from the cursor position delete characters to a matching * parenthesis. */ static KT_KEY_FN(gl_delete_to_parenthesis) { int curpos = gl_index_of_matching_paren(gl); if(curpos >= 0) { gl_save_for_undo(gl); if(curpos >= gl->buff_curpos) return gl_forward_delete_char(gl, curpos - gl->buff_curpos + 1, NULL); else return gl_backward_delete_char(gl, ++gl->buff_curpos - curpos + 1, NULL); }; return 0; } /*....................................................................... * This is an action function which deletes from the cursor to the end * of the word that the cursor is either in or precedes. */ static KT_KEY_FN(gl_forward_delete_word) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * In emacs mode delete to the end of the word. In vi mode delete to the * start of the net word. */ if(gl->editor == GL_EMACS_MODE) { return gl_delete_chars(gl, gl_nth_word_end_forward(gl,count) - gl->buff_curpos + 1, 1); } else { return gl_delete_chars(gl, gl_nth_word_start_forward(gl,count) - gl->buff_curpos, gl->vi.command); }; } /*....................................................................... * This is an action function which deletes the word that precedes the * cursor. */ static KT_KEY_FN(gl_backward_delete_word) { /* * Keep a record of the current cursor position. */ int buff_curpos = gl->buff_curpos; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Move back 'count' words. */ if(gl_backward_word(gl, count, NULL)) return 1; /* * Delete from the new cursor position to the original one. */ return gl_delete_chars(gl, buff_curpos - gl->buff_curpos, gl->editor == GL_EMACS_MODE || gl->vi.command); } /*....................................................................... * Searching in a given direction, delete to the count'th * instance of a specified or queried character, in the input line. * * Input: * gl GetLine * The getline resource object. * count int The number of times to search. * c char The character to be searched for, or '\0' if * the character should be read from the user. * forward int True if searching forward. * onto int True if the search should end on top of the * character, false if the search should stop * one character before the character in the * specified search direction. * change int If true, this function is being called upon * to do a vi change command, in which case the * user will be left in insert mode after the * deletion. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_delete_find(GetLine *gl, int count, char c, int forward, int onto, int change) { /* * Search for the character, and abort the deletion if not found. */ int pos = gl_find_char(gl, count, forward, onto, c); if(pos < 0) return 0; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Allow the cursor to be at the end of the line if this is a change * command. */ if(change) gl->vi.command = 0; /* * Delete the appropriate span of characters. */ if(forward) { if(gl_delete_chars(gl, pos - gl->buff_curpos + 1, 1)) return 1; } else { int buff_curpos = gl->buff_curpos; if(gl_place_cursor(gl, pos) || gl_delete_chars(gl, buff_curpos - gl->buff_curpos, 1)) return 1; }; /* * If this is a change operation, switch the insert mode. */ if(change && gl_vi_insert(gl, 0, NULL)) return 1; return 0; } /*....................................................................... * This is an action function which deletes forward from the cursor up to and * including a specified character. */ static KT_KEY_FN(gl_forward_delete_find) { return gl_delete_find(gl, count, '\0', 1, 1, 0); } /*....................................................................... * This is an action function which deletes backward from the cursor back to * and including a specified character. */ static KT_KEY_FN(gl_backward_delete_find) { return gl_delete_find(gl, count, '\0', 0, 1, 0); } /*....................................................................... * This is an action function which deletes forward from the cursor up to but * not including a specified character. */ static KT_KEY_FN(gl_forward_delete_to) { return gl_delete_find(gl, count, '\0', 1, 0, 0); } /*....................................................................... * This is an action function which deletes backward from the cursor back to * but not including a specified character. */ static KT_KEY_FN(gl_backward_delete_to) { return gl_delete_find(gl, count, '\0', 0, 0, 0); } /*....................................................................... * This is an action function which deletes to a character specified by a * previous search. */ static KT_KEY_FN(gl_delete_refind) { return gl_delete_find(gl, count, gl->vi.find_char, gl->vi.find_forward, gl->vi.find_onto, 0); } /*....................................................................... * This is an action function which deletes to a character specified by a * previous search, but in the opposite direction. */ static KT_KEY_FN(gl_delete_invert_refind) { return gl_delete_find(gl, count, gl->vi.find_char, !gl->vi.find_forward, gl->vi.find_onto, 0); } /*....................................................................... * This is an action function which converts the characters in the word * following the cursor to upper case. */ static KT_KEY_FN(gl_upcase_word) { /* * Locate the count'th word ending after the cursor. */ int last = gl_nth_word_end_forward(gl, count); /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Upcase characters from the current cursor position to 'last'. */ while(gl->buff_curpos <= last) { char *cptr = gl->line + gl->buff_curpos; /* * Convert the character to upper case? */ if(islower((int)(unsigned char) *cptr)) gl_buffer_char(gl, toupper((int) *cptr), gl->buff_curpos); gl->buff_curpos++; /* * Write the possibly modified character back. Note that for non-modified * characters we want to do this as well, so as to advance the cursor. */ if(gl_print_char(gl, *cptr, cptr[1])) return 1; }; return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ } /*....................................................................... * This is an action function which converts the characters in the word * following the cursor to lower case. */ static KT_KEY_FN(gl_downcase_word) { /* * Locate the count'th word ending after the cursor. */ int last = gl_nth_word_end_forward(gl, count); /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Upcase characters from the current cursor position to 'last'. */ while(gl->buff_curpos <= last) { char *cptr = gl->line + gl->buff_curpos; /* * Convert the character to upper case? */ if(isupper((int)(unsigned char) *cptr)) gl_buffer_char(gl, tolower((int) *cptr), gl->buff_curpos); gl->buff_curpos++; /* * Write the possibly modified character back. Note that for non-modified * characters we want to do this as well, so as to advance the cursor. */ if(gl_print_char(gl, *cptr, cptr[1])) return 1; }; return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ } /*....................................................................... * This is an action function which converts the first character of the * following word to upper case, in order to capitalize the word, and * leaves the cursor at the end of the word. */ static KT_KEY_FN(gl_capitalize_word) { char *cptr; /* &gl->line[gl->buff_curpos] */ int first; /* True for the first letter of the word */ int i; /* * Keep a record of the current insert mode and the cursor position. */ int insert = gl->insert; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * We want to overwrite the modified word. */ gl->insert = 0; /* * Capitalize 'count' words. */ for(i=0; ibuff_curpos < gl->ntotal; i++) { int pos = gl->buff_curpos; /* * If we are not already within a word, skip to the start of the word. */ for(cptr = gl->line + pos ; posntotal && !gl_is_word_char((int) *cptr); pos++, cptr++) ; /* * Move the cursor to the new position. */ if(gl_place_cursor(gl, pos)) return 1; /* * While searching for the end of the word, change lower case letters * to upper case. */ for(first=1; gl->buff_curposntotal && gl_is_word_char((int) *cptr); gl->buff_curpos++, cptr++) { /* * Convert the character to upper case? */ if(first) { if(islower((int)(unsigned char) *cptr)) gl_buffer_char(gl, toupper((int) *cptr), cptr - gl->line); } else { if(isupper((int)(unsigned char) *cptr)) gl_buffer_char(gl, tolower((int) *cptr), cptr - gl->line); }; first = 0; /* * Write the possibly modified character back. Note that for non-modified * characters we want to do this as well, so as to advance the cursor. */ if(gl_print_char(gl, *cptr, cptr[1])) return 1; }; }; /* * Restore the insertion mode. */ gl->insert = insert; return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ } /*....................................................................... * This is an action function which redraws the current line. */ static KT_KEY_FN(gl_redisplay) { /* * Keep a record of the current cursor position. */ int buff_curpos = gl->buff_curpos; /* * Do nothing if there is no line to be redisplayed. */ if(gl->endline) return 0; /* * Erase the current input line. */ if(gl_erase_line(gl)) return 1; /* * Display the current prompt. */ if(gl_display_prompt(gl)) return 1; /* * Render the part of the line that the user has typed in so far. */ if(gl_print_string(gl, gl->line, '\0')) return 1; /* * Restore the cursor position. */ if(gl_place_cursor(gl, buff_curpos)) return 1; /* * Mark the redisplay operation as having been completed. */ gl->redisplay = 0; /* * Flush the redisplayed line to the terminal. */ return gl_flush_output(gl); } /*....................................................................... * This is an action function which clears the display and redraws the * input line from the home position. */ static KT_KEY_FN(gl_clear_screen) { /* * Home the cursor and clear from there to the end of the display. */ if(gl_print_control_sequence(gl, gl->nline, gl->home) || gl_print_control_sequence(gl, gl->nline, gl->clear_eod)) return 1; /* * The input line is no longer displayed. */ gl_line_erased(gl); /* * Arrange for the input line to be redisplayed. */ gl_queue_redisplay(gl); return 0; } /*....................................................................... * This is an action function which swaps the character under the cursor * with the character to the left of the cursor. */ static KT_KEY_FN(gl_transpose_chars) { char from[3]; /* The original string of 2 characters */ char swap[3]; /* The swapped string of two characters */ /* * If we are at the beginning or end of the line, there aren't two * characters to swap. */ if(gl->buff_curpos < 1 || gl->buff_curpos >= gl->ntotal) return 0; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Get the original and swapped strings of the two characters. */ from[0] = gl->line[gl->buff_curpos - 1]; from[1] = gl->line[gl->buff_curpos]; from[2] = '\0'; swap[0] = gl->line[gl->buff_curpos]; swap[1] = gl->line[gl->buff_curpos - 1]; swap[2] = '\0'; /* * Move the cursor to the start of the two characters. */ if(gl_place_cursor(gl, gl->buff_curpos-1)) return 1; /* * Swap the two characters in the buffer. */ gl_buffer_char(gl, swap[0], gl->buff_curpos); gl_buffer_char(gl, swap[1], gl->buff_curpos+1); /* * If the sum of the displayed width of the two characters * in their current and final positions is the same, swapping can * be done by just overwriting with the two swapped characters. */ if(gl_displayed_string_width(gl, from, -1, gl->term_curpos) == gl_displayed_string_width(gl, swap, -1, gl->term_curpos)) { int insert = gl->insert; gl->insert = 0; if(gl_print_char(gl, swap[0], swap[1]) || gl_print_char(gl, swap[1], gl->line[gl->buff_curpos+2])) return 1; gl->insert = insert; /* * If the swapped substring has a different displayed size, we need to * redraw everything after the first of the characters. */ } else { if(gl_print_string(gl, gl->line + gl->buff_curpos, '\0') || gl_truncate_display(gl)) return 1; }; /* * Advance the cursor to the character after the swapped pair. */ return gl_place_cursor(gl, gl->buff_curpos + 2); } /*....................................................................... * This is an action function which sets a mark at the current cursor * location. */ static KT_KEY_FN(gl_set_mark) { gl->buff_mark = gl->buff_curpos; return 0; } /*....................................................................... * This is an action function which swaps the mark location for the * cursor location. */ static KT_KEY_FN(gl_exchange_point_and_mark) { /* * Get the old mark position, and limit to the extent of the input * line. */ int old_mark = gl->buff_mark <= gl->ntotal ? gl->buff_mark : gl->ntotal; /* * Make the current cursor position the new mark. */ gl->buff_mark = gl->buff_curpos; /* * Move the cursor to the old mark position. */ return gl_place_cursor(gl, old_mark); } /*....................................................................... * This is an action function which deletes the characters between the * mark and the cursor, recording them in gl->cutbuf for later pasting. */ static KT_KEY_FN(gl_kill_region) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Limit the mark to be within the line. */ if(gl->buff_mark > gl->ntotal) gl->buff_mark = gl->ntotal; /* * If there are no characters between the cursor and the mark, simply clear * the cut buffer. */ if(gl->buff_mark == gl->buff_curpos) { gl->cutbuf[0] = '\0'; return 0; }; /* * If the mark is before the cursor, swap the cursor and the mark. */ if(gl->buff_mark < gl->buff_curpos && gl_exchange_point_and_mark(gl,1,NULL)) return 1; /* * Delete the characters. */ if(gl_delete_chars(gl, gl->buff_mark - gl->buff_curpos, 1)) return 1; /* * Make the mark the same as the cursor position. */ gl->buff_mark = gl->buff_curpos; return 0; } /*....................................................................... * This is an action function which records the characters between the * mark and the cursor, in gl->cutbuf for later pasting. */ static KT_KEY_FN(gl_copy_region_as_kill) { int ca, cb; /* The indexes of the first and last characters in the region */ int mark; /* The position of the mark */ /* * Get the position of the mark, limiting it to lie within the line. */ mark = gl->buff_mark > gl->ntotal ? gl->ntotal : gl->buff_mark; /* * If there are no characters between the cursor and the mark, clear * the cut buffer. */ if(mark == gl->buff_curpos) { gl->cutbuf[0] = '\0'; return 0; }; /* * Get the line indexes of the first and last characters in the region. */ if(mark < gl->buff_curpos) { ca = mark; cb = gl->buff_curpos - 1; } else { ca = gl->buff_curpos; cb = mark - 1; }; /* * Copy the region to the cut buffer. */ memcpy(gl->cutbuf, gl->line + ca, cb + 1 - ca); gl->cutbuf[cb + 1 - ca] = '\0'; return 0; } /*....................................................................... * This is an action function which inserts the contents of the cut * buffer at the current cursor location. */ static KT_KEY_FN(gl_yank) { int i; /* * Set the mark at the current location. */ gl->buff_mark = gl->buff_curpos; /* * Do nothing else if the cut buffer is empty. */ if(gl->cutbuf[0] == '\0') return gl_ring_bell(gl, 1, NULL); /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Insert the string count times. */ for(i=0; icutbuf)) return 1; }; /* * gl_add_string_to_line() leaves the cursor after the last character that * was pasted, whereas vi leaves the cursor over the last character pasted. */ if(gl->editor == GL_VI_MODE && gl_cursor_left(gl, 1, NULL)) return 1; return 0; } /*....................................................................... * This is an action function which inserts the contents of the cut * buffer one character beyond the current cursor location. */ static KT_KEY_FN(gl_append_yank) { int was_command = gl->vi.command; int i; /* * If the cut buffer is empty, ring the terminal bell. */ if(gl->cutbuf[0] == '\0') return gl_ring_bell(gl, 1, NULL); /* * Set the mark at the current location + 1. */ gl->buff_mark = gl->buff_curpos + 1; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Arrange to paste the text in insert mode after the current character. */ if(gl_vi_append(gl, 0, NULL)) return 1; /* * Insert the string count times. */ for(i=0; icutbuf)) return 1; }; /* * Switch back to command mode if necessary. */ if(was_command) gl_vi_command_mode(gl); return 0; } /*....................................................................... * Attempt to ask the terminal for its current size. On systems that * don't support the TIOCWINSZ ioctl() for querying the terminal size, * the current values of gl->ncolumn and gl->nrow are returned. * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * ncolumn int * The number of columns will be assigned to *ncolumn. * nline int * The number of lines will be assigned to *nline. */ static void gl_query_size(GetLine *gl, int *ncolumn, int *nline) { #ifdef TIOCGWINSZ /* * Query the new terminal window size. Ignore invalid responses. */ struct winsize size; if(ioctl(gl->output_fd, TIOCGWINSZ, &size) == 0 && size.ws_row > 0 && size.ws_col > 0) { *ncolumn = size.ws_col; *nline = size.ws_row; return; }; #endif /* * Return the existing values. */ *ncolumn = gl->ncolumn; *nline = gl->nline; return; } /*....................................................................... * Query the size of the terminal, and if it has changed, redraw the * current input line accordingly. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int 0 - OK. * 1 - Error. */ static int _gl_update_size(GetLine *gl) { int ncolumn, nline; /* The new size of the terminal */ /* * Query the new terminal window size. */ gl_query_size(gl, &ncolumn, &nline); /* * Update gl and the displayed line to fit the new dimensions. */ return gl_handle_tty_resize(gl, ncolumn, nline); } /*....................................................................... * Redraw the current input line to account for a change in the terminal * size. Also install the new size in gl. * * Input: * gl GetLine * The resource object of gl_get_line(). * ncolumn int The new number of columns. * nline int The new number of lines. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_handle_tty_resize(GetLine *gl, int ncolumn, int nline) { /* * If the input device isn't a terminal, just record the new size. */ if(!gl->is_term) { gl->nline = nline; gl->ncolumn = ncolumn; /* * Has the size actually changed? */ } else if(ncolumn != gl->ncolumn || nline != gl->nline) { /* * If we are currently editing a line, erase it. */ if(gl_erase_line(gl)) return 1; /* * Update the recorded window size. */ gl->nline = nline; gl->ncolumn = ncolumn; /* * Arrange for the input line to be redrawn before the next character * is read from the terminal. */ gl_queue_redisplay(gl); }; return 0; } /*....................................................................... * This is the action function that recalls the previous line in the * history buffer. */ static KT_KEY_FN(gl_up_history) { /* * In vi mode, switch to command mode, since the user is very * likely to want to move around newly recalled lines. */ gl_vi_command_mode(gl); /* * Forget any previous recall session. */ gl->preload_id = 0; /* * Record the key sequence number of this search action. */ gl->last_search = gl->keyseq_count; /* * We don't want a search prefix for this function. */ if(_glh_search_prefix(gl->glh, gl->line, 0)) { _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); return 1; }; /* * Recall the count'th next older line in the history list. If the first one * fails we can return since nothing has changed, otherwise we must continue * and update the line state. */ if(_glh_find_backwards(gl->glh, gl->line, gl->linelen+1) == NULL) return 0; while(--count && _glh_find_backwards(gl->glh, gl->line, gl->linelen+1)) ; /* * Accomodate the new contents of gl->line[]. */ gl_update_buffer(gl); /* * Arrange to have the cursor placed at the end of the new line. */ gl->buff_curpos = gl->ntotal; /* * Erase and display the new line. */ gl_queue_redisplay(gl); return 0; } /*....................................................................... * This is the action function that recalls the next line in the * history buffer. */ static KT_KEY_FN(gl_down_history) { /* * In vi mode, switch to command mode, since the user is very * likely to want to move around newly recalled lines. */ gl_vi_command_mode(gl); /* * Record the key sequence number of this search action. */ gl->last_search = gl->keyseq_count; /* * If no search is currently in progress continue a previous recall * session from a previous entered line if possible. */ if(_glh_line_id(gl->glh, 0) == 0 && gl->preload_id) { _glh_recall_line(gl->glh, gl->preload_id, gl->line, gl->linelen+1); gl->preload_id = 0; } else { /* * We don't want a search prefix for this function. */ if(_glh_search_prefix(gl->glh, gl->line, 0)) { _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); return 1; }; /* * Recall the count'th next newer line in the history list. If the first one * fails we can return since nothing has changed otherwise we must continue * and update the line state. */ if(_glh_find_forwards(gl->glh, gl->line, gl->linelen+1) == NULL) return 0; while(--count && _glh_find_forwards(gl->glh, gl->line, gl->linelen+1)) ; }; /* * Accomodate the new contents of gl->line[]. */ gl_update_buffer(gl); /* * Arrange to have the cursor placed at the end of the new line. */ gl->buff_curpos = gl->ntotal; /* * Erase and display the new line. */ gl_queue_redisplay(gl); return 0; } /*....................................................................... * This is the action function that recalls the previous line in the * history buffer whos prefix matches the characters that currently * precede the cursor. By setting count=-1, this can be used internally * to force searching for the prefix used in the last search. */ static KT_KEY_FN(gl_history_search_backward) { /* * In vi mode, switch to command mode, since the user is very * likely to want to move around newly recalled lines. */ gl_vi_command_mode(gl); /* * Forget any previous recall session. */ gl->preload_id = 0; /* * Record the key sequence number of this search action. */ gl->last_search = gl->keyseq_count; /* * If a prefix search isn't already in progress, replace the search * prefix to the string that precedes the cursor. In vi command mode * include the character that is under the cursor in the string. If * count<0 keep the previous search prefix regardless, so as to force * a repeat search even if the last command wasn't a history command. */ if(count >= 0 && !_glh_search_active(gl->glh) && _glh_search_prefix(gl->glh, gl->line, gl->buff_curpos + (gl->editor==GL_VI_MODE && gl->ntotal>0))) { _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); return 1; }; /* * Search backwards for a match to the part of the line which precedes the * cursor. */ if(_glh_find_backwards(gl->glh, gl->line, gl->linelen+1) == NULL) return 0; /* * Accomodate the new contents of gl->line[]. */ gl_update_buffer(gl); /* * Arrange to have the cursor placed at the end of the new line. */ gl->buff_curpos = gl->ntotal; /* * Erase and display the new line. */ gl_queue_redisplay(gl); return 0; } /*....................................................................... * This is the action function that recalls the previous line in the * history buffer who's prefix matches that specified in an earlier call * to gl_history_search_backward() or gl_history_search_forward(). */ static KT_KEY_FN(gl_history_re_search_backward) { return gl_history_search_backward(gl, -1, NULL); } /*....................................................................... * This is the action function that recalls the next line in the * history buffer who's prefix matches that specified in the earlier call * to gl_history_search_backward) which started the history search. * By setting count=-1, this can be used internally to force searching * for the prefix used in the last search. */ static KT_KEY_FN(gl_history_search_forward) { /* * In vi mode, switch to command mode, since the user is very * likely to want to move around newly recalled lines. */ gl_vi_command_mode(gl); /* * Record the key sequence number of this search action. */ gl->last_search = gl->keyseq_count; /* * If a prefix search isn't already in progress, replace the search * prefix to the string that precedes the cursor. In vi command mode * include the character that is under the cursor in the string. If * count<0 keep the previous search prefix regardless, so as to force * a repeat search even if the last command wasn't a history command. */ if(count >= 0 && !_glh_search_active(gl->glh) && _glh_search_prefix(gl->glh, gl->line, gl->buff_curpos + (gl->editor==GL_VI_MODE && gl->ntotal>0))) { _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); return 1; }; /* * Search forwards for the next matching line. */ if(_glh_find_forwards(gl->glh, gl->line, gl->linelen+1) == NULL) return 0; /* * Accomodate the new contents of gl->line[]. */ gl_update_buffer(gl); /* * Arrange for the cursor to be placed at the end of the new line. */ gl->buff_curpos = gl->ntotal; /* * Erase and display the new line. */ gl_queue_redisplay(gl); return 0; } /*....................................................................... * This is the action function that recalls the next line in the * history buffer who's prefix matches that specified in an earlier call * to gl_history_search_backward() or gl_history_search_forward(). */ static KT_KEY_FN(gl_history_re_search_forward) { return gl_history_search_forward(gl, -1, NULL); } #ifdef HIDE_FILE_SYSTEM /*....................................................................... * The following function is used as the default completion handler when * the filesystem is to be hidden. It simply reports no completions. */ static CPL_MATCH_FN(gl_no_completions) { return 0; } #endif /*....................................................................... * This is the tab completion function that completes the filename that * precedes the cursor position. Its callback data argument must be a * pointer to a GlCplCallback containing the completion callback function * and its callback data, or NULL to use the builtin filename completer. */ static KT_KEY_FN(gl_complete_word) { CplMatches *matches; /* The possible completions */ int suffix_len; /* The length of the completion extension */ int cont_len; /* The length of any continuation suffix */ int nextra; /* The number of characters being added to the */ /* total length of the line. */ int buff_pos; /* The buffer index at which the completion is */ /* to be inserted. */ int waserr = 0; /* True after errors */ /* * Get the container of the completion callback and its callback data. */ GlCplCallback *cb = data ? (GlCplCallback *) data : &gl->cplfn; /* * In vi command mode, switch to append mode so that the character under * the cursor is included in the completion (otherwise people can't * complete at the end of the line). */ if(gl->vi.command && gl_vi_append(gl, 0, NULL)) return 1; /* * Get the cursor position at which the completion is to be inserted. */ buff_pos = gl->buff_curpos; /* * Perform the completion. */ matches = cpl_complete_word(gl->cpl, gl->line, gl->buff_curpos, cb->data, cb->fn); /* * No matching completions? */ if(!matches) { waserr = gl_print_info(gl, cpl_last_error(gl->cpl), GL_END_INFO); /* * Are there any completions? */ } else if(matches->nmatch >= 1) { /* * If there any ambiguous matches, report them, starting on a new line. */ if(matches->nmatch > 1 && gl->echo) { if(_gl_normal_io(gl) || _cpl_output_completions(matches, gl_write_fn, gl, gl->ncolumn)) waserr = 1; }; /* * Get the length of the suffix and any continuation suffix to add to it. */ suffix_len = strlen(matches->suffix); cont_len = strlen(matches->cont_suffix); /* * If there is an unambiguous match, and the continuation suffix ends in * a newline, strip that newline and arrange to have getline return * after this action function returns. */ if(matches->nmatch==1 && cont_len > 0 && matches->cont_suffix[cont_len - 1] == '\n') { cont_len--; if(gl_newline(gl, 1, NULL)) waserr = 1; }; /* * Work out the number of characters that are to be added. */ nextra = suffix_len + cont_len; /* * Is there anything to be added? */ if(!waserr && nextra) { /* * Will there be space for the expansion in the line buffer? */ if(gl->ntotal + nextra < gl->linelen) { /* * Make room to insert the filename extension. */ gl_make_gap_in_buffer(gl, gl->buff_curpos, nextra); /* * Insert the filename extension. */ gl_buffer_string(gl, matches->suffix, suffix_len, gl->buff_curpos); /* * Add the terminating characters. */ gl_buffer_string(gl, matches->cont_suffix, cont_len, gl->buff_curpos + suffix_len); /* * Place the cursor position at the end of the completion. */ gl->buff_curpos += nextra; /* * If we don't have to redisplay the whole line, redisplay the part * of the line which follows the original cursor position, and place * the cursor at the end of the completion. */ if(gl->displayed) { if(gl_truncate_display(gl) || gl_print_string(gl, gl->line + buff_pos, '\0') || gl_place_cursor(gl, gl->buff_curpos)) waserr = 1; }; } else { (void) gl_print_info(gl, "Insufficient room in line for file completion.", GL_END_INFO); waserr = 1; }; }; }; /* * If any output had to be written to the terminal, then editing will * have been suspended, make sure that we are back in raw line editing * mode before returning. */ if(_gl_raw_io(gl, 1)) waserr = 1; return 0; } #ifndef HIDE_FILE_SYSTEM /*....................................................................... * This is the function that expands the filename that precedes the * cursor position. It expands ~user/ expressions, $envvar expressions, * and wildcards. */ static KT_KEY_FN(gl_expand_filename) { char *start_path; /* The pointer to the start of the pathname in */ /* gl->line[]. */ FileExpansion *result; /* The results of the filename expansion */ int pathlen; /* The length of the pathname being expanded */ int length; /* The number of characters needed to display the */ /* expanded files. */ int nextra; /* The number of characters to be added */ int i,j; /* * In vi command mode, switch to append mode so that the character under * the cursor is included in the completion (otherwise people can't * complete at the end of the line). */ if(gl->vi.command && gl_vi_append(gl, 0, NULL)) return 1; /* * Locate the start of the filename that precedes the cursor position. */ start_path = _pu_start_of_path(gl->line, gl->buff_curpos); if(!start_path) return 1; /* * Get the length of the string that is to be expanded. */ pathlen = gl->buff_curpos - (start_path - gl->line); /* * Attempt to expand it. */ result = ef_expand_file(gl->ef, start_path, pathlen); /* * If there was an error, report the error on a new line. */ if(!result) return gl_print_info(gl, ef_last_error(gl->ef), GL_END_INFO); /* * If no files matched, report this as well. */ if(result->nfile == 0 || !result->exists) return gl_print_info(gl, "No files match.", GL_END_INFO); /* * If in vi command mode, preserve the current line for potential use by * vi-undo. */ gl_save_for_undo(gl); /* * Work out how much space we will need to display all of the matching * filenames, taking account of the space that we need to place between * them, and the number of additional '\' characters needed to escape * spaces, tabs and backslash characters in the individual filenames. */ length = 0; for(i=0; infile; i++) { char *file = result->files[i]; while(*file) { int c = *file++; switch(c) { case ' ': case '\t': case '\\': case '*': case '?': case '[': length++; /* Count extra backslash characters */ }; length++; /* Count the character itself */ }; length++; /* Count the space that follows each filename */ }; /* * Work out the number of characters that are to be added. */ nextra = length - pathlen; /* * Will there be space for the expansion in the line buffer? */ if(gl->ntotal + nextra >= gl->linelen) { return gl_print_info(gl, "Insufficient room in line for file expansion.", GL_END_INFO); } else { /* * Do we need to move the part of the line that followed the unexpanded * filename? */ if(nextra > 0) { gl_make_gap_in_buffer(gl, gl->buff_curpos, nextra); } else if(nextra < 0) { gl->buff_curpos += nextra; gl_remove_from_buffer(gl, gl->buff_curpos, -nextra); }; /* * Insert the filenames, separated by spaces, and with internal spaces, * tabs and backslashes escaped with backslashes. */ for(i=0,j=start_path - gl->line; infile; i++) { char *file = result->files[i]; while(*file) { int c = *file++; switch(c) { case ' ': case '\t': case '\\': case '*': case '?': case '[': gl_buffer_char(gl, '\\', j++); }; gl_buffer_char(gl, c, j++); }; gl_buffer_char(gl, ' ', j++); }; }; /* * Redisplay the part of the line which follows the start of * the original filename. */ if(gl_place_cursor(gl, start_path - gl->line) || gl_truncate_display(gl) || gl_print_string(gl, start_path, start_path[length])) return 1; /* * Move the cursor to the end of the expansion. */ return gl_place_cursor(gl, (start_path - gl->line) + length); } #endif #ifndef HIDE_FILE_SYSTEM /*....................................................................... * This is the action function that lists glob expansions of the * filename that precedes the cursor position. It expands ~user/ * expressions, $envvar expressions, and wildcards. */ static KT_KEY_FN(gl_list_glob) { char *start_path; /* The pointer to the start of the pathname in */ /* gl->line[]. */ FileExpansion *result; /* The results of the filename expansion */ int pathlen; /* The length of the pathname being expanded */ /* * Locate the start of the filename that precedes the cursor position. */ start_path = _pu_start_of_path(gl->line, gl->buff_curpos); if(!start_path) return 1; /* * Get the length of the string that is to be expanded. */ pathlen = gl->buff_curpos - (start_path - gl->line); /* * Attempt to expand it. */ result = ef_expand_file(gl->ef, start_path, pathlen); /* * If there was an error, report it. */ if(!result) { return gl_print_info(gl, ef_last_error(gl->ef), GL_END_INFO); /* * If no files matched, report this as well. */ } else if(result->nfile == 0 || !result->exists) { return gl_print_info(gl, "No files match.", GL_END_INFO); /* * List the matching expansions. */ } else if(gl->echo) { if(gl_start_newline(gl, 1) || _ef_output_expansions(result, gl_write_fn, gl, gl->ncolumn)) return 1; gl_queue_redisplay(gl); }; return 0; } #endif /*....................................................................... * Return non-zero if a character should be considered a part of a word. * * Input: * c int The character to be tested. * Output: * return int True if the character should be considered part of a word. */ static int gl_is_word_char(int c) { return isalnum((int)(unsigned char)c) || strchr(GL_WORD_CHARS, c) != NULL; } /*....................................................................... * Override the builtin file-completion callback that is bound to the * "complete_word" action function. * * Input: * gl GetLine * The resource object of the command-line input * module. * data void * This is passed to match_fn() whenever it is * called. It could, for example, point to a * symbol table where match_fn() could look * for possible completions. * match_fn CplMatchFn * The function that will identify the prefix * to be completed from the input line, and * report matching symbols. * Output: * return int 0 - OK. * 1 - Error. */ int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn) { sigset_t oldset; /* The signals that were blocked on entry to this function */ /* * Check the arguments. */ if(!gl || !match_fn) { if(gl) _err_record_msg(gl->err, "NULL argument", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Record the new completion function and its callback data. */ gl->cplfn.fn = match_fn; gl->cplfn.data = data; /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); return 0; } /*....................................................................... * Change the terminal (or stream) that getline interacts with. * * Input: * gl GetLine * The resource object of the command-line input * module. * input_fp FILE * The stdio stream to read from. * output_fp FILE * The stdio stream to write to. * term char * The terminal type. This can be NULL if * either or both of input_fp and output_fp don't * refer to a terminal. Otherwise it should refer * to an entry in the terminal information database. * Output: * return int 0 - OK. * 1 - Error. */ int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, const char *term) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of _gl_change_terminal() */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Execute the private body of the function while signals are blocked. */ status = _gl_change_terminal(gl, input_fp, output_fp, term); /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the gl_change_terminal() function. It * assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static int _gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, const char *term) { int is_term = 0; /* True if both input_fd and output_fd are associated */ /* with a terminal. */ /* * Require that input_fp and output_fp both be valid. */ if(!input_fp || !output_fp) { gl_print_info(gl, "Can't change terminal. Bad input/output stream(s).", GL_END_INFO); return 1; }; /* * Are we displacing an existing terminal (as opposed to setting the * initial terminal)? */ if(gl->input_fd >= 0) { /* * Make sure to leave the previous terminal in a usable state. */ if(_gl_normal_io(gl)) return 1; /* * Remove the displaced terminal from the list of fds to watch. */ #ifdef HAVE_SELECT FD_CLR(gl->input_fd, &gl->rfds); #endif }; /* * Record the file descriptors and streams. */ gl->input_fp = input_fp; gl->input_fd = fileno(input_fp); gl->output_fp = output_fp; gl->output_fd = fileno(output_fp); /* * If needed, expand the record of the maximum file-descriptor that might * need to be monitored with select(). */ #ifdef HAVE_SELECT if(gl->input_fd > gl->max_fd) gl->max_fd = gl->input_fd; #endif /* * Disable terminal interaction until we have enough info to interact * with the terminal. */ gl->is_term = 0; /* * For terminal editing, we need both output_fd and input_fd to refer to * a terminal. While we can't verify that they both point to the same * terminal, we can verify that they point to terminals. If the user * sets the TERM environment variable to "dumb", treat a terminal as * a non-interactive I/O stream. */ is_term = (isatty(gl->input_fd) && isatty(gl->output_fd)) && !(term && strcmp(term, "dumb")==0); /* * If we are interacting with a terminal and no terminal type has been * specified, treat it as a generic ANSI terminal. */ if(is_term && !term) term = "ansi"; /* * Make a copy of the terminal type string. */ if(term != gl->term) { /* * Delete any old terminal type string. */ if(gl->term) { free(gl->term); gl->term = NULL; }; /* * Make a copy of the new terminal-type string, if any. */ if(term) { gl->term = (char *) malloc(strlen(term)+1); if(gl->term) strcpy(gl->term, term); }; }; /* * Clear any terminal-specific key bindings that were taken from the * settings of the last terminal. */ _kt_clear_bindings(gl->bindings, KTB_TERM); /* * If we have a terminal install new bindings for it. */ if(is_term) { /* * Get the current settings of the terminal. */ if(tcgetattr(gl->input_fd, &gl->oldattr)) { _err_record_msg(gl->err, "tcgetattr error", END_ERR_MSG); return 1; }; /* * If we don't set this now, gl_control_strings() won't know * that it is talking to a terminal. */ gl->is_term = 1; /* * Lookup the terminal control string and size information. */ if(gl_control_strings(gl, term)) { gl->is_term = 0; return 1; }; /* * Bind terminal-specific keys. */ if(gl_bind_terminal_keys(gl)) return 1; }; /* * Assume that the caller has given us a terminal in a sane state. */ gl->io_mode = GL_NORMAL_MODE; /* * Switch into the currently configured I/O mode. */ if(_gl_io_mode(gl, gl->io_mode)) return 1; return 0; } /*....................................................................... * Set up terminal-specific key bindings. * * Input: * gl GetLine * The resource object of the command-line input * module. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_bind_terminal_keys(GetLine *gl) { /* * Install key-bindings for the special terminal characters. */ if(gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VINTR], "user-interrupt") || gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VQUIT], "abort") || gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VSUSP], "suspend")) return 1; /* * In vi-mode, arrange for the above characters to be seen in command * mode. */ if(gl->editor == GL_VI_MODE) { if(gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VINTR]), "user-interrupt") || gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VQUIT]), "abort") || gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VSUSP]), "suspend")) return 1; }; /* * Non-universal special keys. */ #ifdef VLNEXT if(gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VLNEXT], "literal-next")) return 1; #else if(_kt_set_keybinding(gl->bindings, KTB_TERM, "^V", "literal-next")) { _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); return 1; }; #endif /* * Bind action functions to the terminal-specific arrow keys * looked up by gl_control_strings(). */ if(_gl_bind_arrow_keys(gl)) return 1; return 0; } /*....................................................................... * This function is normally bound to control-D. When it is invoked within * a line it deletes the character which follows the cursor. When invoked * at the end of the line it lists possible file completions, and when * invoked on an empty line it causes gl_get_line() to return EOF. This * function emulates the one that is normally bound to control-D by tcsh. */ static KT_KEY_FN(gl_del_char_or_list_or_eof) { /* * If we have an empty line arrange to return EOF. */ if(gl->ntotal < 1) { gl_record_status(gl, GLR_EOF, 0); return 1; /* * If we are at the end of the line list possible completions. */ } else if(gl->buff_curpos >= gl->ntotal) { return gl_list_completions(gl, 1, NULL); /* * Within the line delete the character that follows the cursor. */ } else { /* * If in vi command mode, first preserve the current line for potential use * by vi-undo. */ gl_save_for_undo(gl); /* * Delete 'count' characters. */ return gl_forward_delete_char(gl, count, NULL); }; } /*....................................................................... * This function is normally bound to control-D in vi mode. When it is * invoked within a line it lists possible file completions, and when * invoked on an empty line it causes gl_get_line() to return EOF. This * function emulates the one that is normally bound to control-D by tcsh. */ static KT_KEY_FN(gl_list_or_eof) { /* * If we have an empty line arrange to return EOF. */ if(gl->ntotal < 1) { gl_record_status(gl, GLR_EOF, 0); return 1; /* * Otherwise list possible completions. */ } else { return gl_list_completions(gl, 1, NULL); }; } /*....................................................................... * List possible completions of the word that precedes the cursor. The * callback data argument must either be NULL to select the default * file completion callback, or be a GlCplCallback object containing the * completion callback function to call. */ static KT_KEY_FN(gl_list_completions) { int waserr = 0; /* True after errors */ /* * Get the container of the completion callback and its callback data. */ GlCplCallback *cb = data ? (GlCplCallback *) data : &gl->cplfn; /* * Get the list of possible completions. */ CplMatches *matches = cpl_complete_word(gl->cpl, gl->line, gl->buff_curpos, cb->data, cb->fn); /* * No matching completions? */ if(!matches) { waserr = gl_print_info(gl, cpl_last_error(gl->cpl), GL_END_INFO); /* * List the matches. */ } else if(matches->nmatch > 0 && gl->echo) { if(_gl_normal_io(gl) || _cpl_output_completions(matches, gl_write_fn, gl, gl->ncolumn)) waserr = 1; }; /* * If any output had to be written to the terminal, then editing will * have been suspended, make sure that we are back in raw line editing * mode before returning. */ if(_gl_raw_io(gl, 1)) waserr = 1; return waserr; } /*....................................................................... * Where the user has used the symbolic arrow-key names to specify * arrow key bindings, bind the specified action functions to the default * and terminal specific arrow key sequences. * * Input: * gl GetLine * The getline resource object. * Output: * return int 0 - OK. * 1 - Error. */ static int _gl_bind_arrow_keys(GetLine *gl) { /* * Process each of the arrow keys. */ if(_gl_rebind_arrow_key(gl, "up", gl->u_arrow, "^[[A", "^[OA") || _gl_rebind_arrow_key(gl, "down", gl->d_arrow, "^[[B", "^[OB") || _gl_rebind_arrow_key(gl, "left", gl->l_arrow, "^[[D", "^[OD") || _gl_rebind_arrow_key(gl, "right", gl->r_arrow, "^[[C", "^[OC")) return 1; return 0; } /*....................................................................... * Lookup the action function of a symbolic arrow-key binding, and bind * it to the terminal-specific and default arrow-key sequences. Note that * we don't trust the terminal-specified key sequences to be correct. * The main reason for this is that on some machines the xterm terminfo * entry is for hardware X-terminals, rather than xterm terminal emulators * and the two terminal types emit different character sequences when the * their cursor keys are pressed. As a result we also supply a couple * of default key sequences. * * Input: * gl GetLine * The resource object of gl_get_line(). * name char * The symbolic name of the arrow key. * term_seq char * The terminal-specific arrow-key sequence. * def_seq1 char * The first default arrow-key sequence. * def_seq2 char * The second arrow-key sequence. * Output: * return int 0 - OK. * 1 - Error. */ static int _gl_rebind_arrow_key(GetLine *gl, const char *name, const char *term_seq, const char *def_seq1, const char *def_seq2) { KeySym *keysym; /* The binding-table entry matching the arrow-key name */ int nsym; /* The number of ambiguous matches */ /* * Lookup the key binding for the symbolic name of the arrow key. This * will either be the default action, or a user provided one. */ if(_kt_lookup_keybinding(gl->bindings, name, strlen(name), &keysym, &nsym) == KT_EXACT_MATCH) { /* * Get the action function. */ KtAction *action = keysym->actions + keysym->binder; KtKeyFn *fn = action->fn; void *data = action->data; /* * Bind this to each of the specified key sequences. */ if((term_seq && _kt_set_keyfn(gl->bindings, KTB_TERM, term_seq, fn, data)) || (def_seq1 && _kt_set_keyfn(gl->bindings, KTB_NORM, def_seq1, fn, data)) || (def_seq2 && _kt_set_keyfn(gl->bindings, KTB_NORM, def_seq2, fn, data))) { _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); return 1; }; }; return 0; } /*....................................................................... * Read getline configuration information from a given file. * * Input: * gl GetLine * The getline resource object. * filename const char * The name of the file to read configuration * information from. The contents of this file * are as described in the gl_get_line(3) man * page for the default ~/.teclarc configuration * file. * who KtBinder Who bindings are to be installed for. * Output: * return int 0 - OK. * 1 - Irrecoverable error. */ static int _gl_read_config_file(GetLine *gl, const char *filename, KtBinder who) { /* * If filesystem access is to be excluded, configuration files can't * be read. */ #ifdef WITHOUT_FILE_SYSTEM _err_record_msg(gl->err, "Can't read configuration files without filesystem access", END_ERR_MSG); errno = EINVAL; return 1; #else FileExpansion *expansion; /* The expansion of the filename */ FILE *fp; /* The opened file */ int waserr = 0; /* True if an error occurred while reading */ int lineno = 1; /* The line number being processed */ /* * Check the arguments. */ if(!gl || !filename) { if(gl) _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Expand the filename. */ expansion = ef_expand_file(gl->ef, filename, -1); if(!expansion) { gl_print_info(gl, "Unable to expand ", filename, " (", ef_last_error(gl->ef), ").", GL_END_INFO); return 1; }; /* * Attempt to open the file. */ fp = fopen(expansion->files[0], "r"); /* * It isn't an error for there to be no configuration file. */ if(!fp) return 0; /* * Parse the contents of the file. */ while(!waserr && !feof(fp)) waserr = _gl_parse_config_line(gl, fp, glc_file_getc, filename, who, &lineno); /* * Bind action functions to the terminal-specific arrow keys. */ if(_gl_bind_arrow_keys(gl)) return 1; /* * Clean up. */ (void) fclose(fp); return waserr; #endif } /*....................................................................... * Read GetLine configuration information from a string. The contents of * the string are the same as those described in the gl_get_line(3) * man page for the contents of the ~/.teclarc configuration file. */ static int _gl_read_config_string(GetLine *gl, const char *buffer, KtBinder who) { const char *bptr; /* A pointer into buffer[] */ int waserr = 0; /* True if an error occurred while reading */ int lineno = 1; /* The line number being processed */ /* * Check the arguments. */ if(!gl || !buffer) { if(gl) _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Get a pointer to the start of the buffer. */ bptr = buffer; /* * Parse the contents of the buffer. */ while(!waserr && *bptr) waserr = _gl_parse_config_line(gl, &bptr, glc_buff_getc, "", who, &lineno); /* * Bind action functions to the terminal-specific arrow keys. */ if(_gl_bind_arrow_keys(gl)) return 1; return waserr; } /*....................................................................... * Parse the next line of a getline configuration file. * * Input: * gl GetLine * The getline resource object. * stream void * The pointer representing the stream to be read * by getc_fn(). * getc_fn GlcGetcFn * A callback function which when called with * 'stream' as its argument, returns the next * unread character from the stream. * origin const char * The name of the entity being read (eg. a * file name). * who KtBinder Who bindings are to be installed for. * Input/Output: * lineno int * The line number being processed is to be * maintained in *lineno. * Output: * return int 0 - OK. * 1 - Irrecoverable error. */ static int _gl_parse_config_line(GetLine *gl, void *stream, GlcGetcFn *getc_fn, const char *origin, KtBinder who, int *lineno) { char buffer[GL_CONF_BUFLEN+1]; /* The input line buffer */ char *argv[GL_CONF_MAXARG]; /* The argument list */ int argc = 0; /* The number of arguments in argv[] */ int c; /* A character from the file */ int escaped = 0; /* True if the next character is escaped */ int i; /* * Skip spaces and tabs. */ do c = getc_fn(stream); while(c==' ' || c=='\t'); /* * Comments extend to the end of the line. */ if(c=='#') do c = getc_fn(stream); while(c != '\n' && c != EOF); /* * Ignore empty lines. */ if(c=='\n' || c==EOF) { (*lineno)++; return 0; }; /* * Record the buffer location of the start of the first argument. */ argv[argc] = buffer; /* * Read the rest of the line, stopping early if a comment is seen, or * the buffer overflows, and replacing sequences of spaces with a * '\0', and recording the thus terminated string as an argument. */ i = 0; while(i= GL_CONF_MAXARG) { gl_report_config_error(gl, origin, *lineno, "Too many arguments."); do c = getc_fn(stream); while(c!='\n' && c!=EOF); /* Skip past eol */ return 0; }; argv[argc] = buffer + i; /* * The next character was preceded by spaces, so it isn't escaped. */ escaped = 0; } else { /* * If we hit an unescaped backslash, this means that we should arrange * to treat the next character like a simple alphabetical character. */ if(c=='\\' && !escaped) { escaped = 1; /* * Splice lines where the newline is escaped. */ } else if(c=='\n' && escaped) { (*lineno)++; /* * Record a normal character, preserving any preceding backslash. */ } else { if(escaped) buffer[i++] = '\\'; if(i>=GL_CONF_BUFLEN) break; escaped = 0; buffer[i++] = c; }; /* * Get the next character. */ c = getc_fn(stream); }; }; /* * Did the buffer overflow? */ if(i>=GL_CONF_BUFLEN) { gl_report_config_error(gl, origin, *lineno, "Line too long."); return 0; }; /* * The first argument should be a command name. */ if(strcmp(argv[0], "bind") == 0) { const char *action = NULL; /* A NULL action removes a keybinding */ const char *keyseq = NULL; switch(argc) { case 3: action = argv[2]; case 2: /* Note the intentional fallthrough */ keyseq = argv[1]; /* * Attempt to record the new keybinding. */ if(_kt_set_keybinding(gl->bindings, who, keyseq, action)) { gl_report_config_error(gl, origin, *lineno, _kt_last_error(gl->bindings)); }; break; default: gl_report_config_error(gl, origin, *lineno, "Wrong number of arguments."); }; } else if(strcmp(argv[0], "edit-mode") == 0) { if(argc == 2 && strcmp(argv[1], "emacs") == 0) { gl_change_editor(gl, GL_EMACS_MODE); } else if(argc == 2 && strcmp(argv[1], "vi") == 0) { gl_change_editor(gl, GL_VI_MODE); } else if(argc == 2 && strcmp(argv[1], "none") == 0) { gl_change_editor(gl, GL_NO_EDITOR); } else { gl_report_config_error(gl, origin, *lineno, "The argument of editor should be vi or emacs."); }; } else if(strcmp(argv[0], "nobeep") == 0) { gl->silence_bell = 1; } else { gl_report_config_error(gl, origin, *lineno, "Unknown command name."); }; /* * Skip any trailing comment. */ while(c != '\n' && c != EOF) c = getc_fn(stream); (*lineno)++; return 0; } /*....................................................................... * This is a private function of _gl_parse_config_line() which prints * out an error message about the contents of the line, prefixed by the * name of the origin of the line and its line number. * * Input: * gl GetLine * The resource object of gl_get_line(). * origin const char * The name of the entity being read (eg. a * file name). * lineno int The line number at which the error occurred. * errmsg const char * The error message. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_report_config_error(GetLine *gl, const char *origin, int lineno, const char *errmsg) { char lnum[20]; /* A buffer in which to render a single integer */ /* * Convert the line number into a string. */ sprintf(lnum, "%d", lineno); /* * Have the string printed on the terminal. */ return gl_print_info(gl, origin, ":", lnum, ": ", errmsg, GL_END_INFO); } /*....................................................................... * This is the _gl_parse_config_line() callback function which reads the * next character from a configuration file. */ static GLC_GETC_FN(glc_file_getc) { return fgetc((FILE *) stream); } /*....................................................................... * This is the _gl_parse_config_line() callback function which reads the * next character from a buffer. Its stream argument is a pointer to a * variable which is, in turn, a pointer into the buffer being read from. */ static GLC_GETC_FN(glc_buff_getc) { const char **lptr = (char const **) stream; return **lptr ? *(*lptr)++ : EOF; } #ifndef HIDE_FILE_SYSTEM /*....................................................................... * When this action is triggered, it arranges to temporarily read command * lines from the regular file whos name precedes the cursor. * The current line is first discarded. */ static KT_KEY_FN(gl_read_from_file) { char *start_path; /* The pointer to the start of the pathname in */ /* gl->line[]. */ FileExpansion *result; /* The results of the filename expansion */ int pathlen; /* The length of the pathname being expanded */ /* * Locate the start of the filename that precedes the cursor position. */ start_path = _pu_start_of_path(gl->line, gl->buff_curpos); if(!start_path) return 1; /* * Get the length of the pathname string. */ pathlen = gl->buff_curpos - (start_path - gl->line); /* * Attempt to expand the pathname. */ result = ef_expand_file(gl->ef, start_path, pathlen); /* * If there was an error, report the error on a new line. */ if(!result) { return gl_print_info(gl, ef_last_error(gl->ef), GL_END_INFO); /* * If no files matched, report this as well. */ } else if(result->nfile == 0 || !result->exists) { return gl_print_info(gl, "No files match.", GL_END_INFO); /* * Complain if more than one file matches. */ } else if(result->nfile > 1) { return gl_print_info(gl, "More than one file matches.", GL_END_INFO); /* * Disallow input from anything but normal files. In principle we could * also support input from named pipes. Terminal files would be a problem * since we wouldn't know the terminal type, and other types of files * might cause the library to lock up. */ } else if(!_pu_path_is_file(result->files[0])) { return gl_print_info(gl, "Not a normal file.", GL_END_INFO); } else { /* * Attempt to open and install the specified file for reading. */ gl->file_fp = fopen(result->files[0], "r"); if(!gl->file_fp) { return gl_print_info(gl, "Unable to open: ", result->files[0], GL_END_INFO); }; /* * If needed, expand the record of the maximum file-descriptor that might * need to be monitored with select(). */ #ifdef HAVE_SELECT if(fileno(gl->file_fp) > gl->max_fd) gl->max_fd = fileno(gl->file_fp); #endif /* * Is non-blocking I/O needed? */ if(gl->raw_mode && gl->io_mode==GL_SERVER_MODE && gl_nonblocking_io(gl, fileno(gl->file_fp))) { gl_revert_input(gl); return gl_print_info(gl, "Can't read file %s with non-blocking I/O", result->files[0]); }; /* * Inform the user what is happening. */ if(gl_print_info(gl, "files[0], ">", GL_END_INFO)) return 1; }; return 0; } #endif /*....................................................................... * Close any temporary file that is being used for input. * * Input: * gl GetLine * The getline resource object. */ static void gl_revert_input(GetLine *gl) { if(gl->file_fp) fclose(gl->file_fp); gl->file_fp = NULL; gl->endline = 1; } /*....................................................................... * This is the action function that recalls the oldest line in the * history buffer. */ static KT_KEY_FN(gl_beginning_of_history) { /* * In vi mode, switch to command mode, since the user is very * likely to want to move around newly recalled lines. */ gl_vi_command_mode(gl); /* * Forget any previous recall session. */ gl->preload_id = 0; /* * Record the key sequence number of this search action. */ gl->last_search = gl->keyseq_count; /* * Recall the next oldest line in the history list. */ if(_glh_oldest_line(gl->glh, gl->line, gl->linelen+1) == NULL) return 0; /* * Accomodate the new contents of gl->line[]. */ gl_update_buffer(gl); /* * Arrange to have the cursor placed at the end of the new line. */ gl->buff_curpos = gl->ntotal; /* * Erase and display the new line. */ gl_queue_redisplay(gl); return 0; } /*....................................................................... * If a history session is currently in progress, this action function * recalls the line that was being edited when the session started. If * no history session is in progress, it does nothing. */ static KT_KEY_FN(gl_end_of_history) { /* * In vi mode, switch to command mode, since the user is very * likely to want to move around newly recalled lines. */ gl_vi_command_mode(gl); /* * Forget any previous recall session. */ gl->preload_id = 0; /* * Record the key sequence number of this search action. */ gl->last_search = gl->keyseq_count; /* * Recall the next oldest line in the history list. */ if(_glh_current_line(gl->glh, gl->line, gl->linelen+1) == NULL) return 0; /* * Accomodate the new contents of gl->line[]. */ gl_update_buffer(gl); /* * Arrange to have the cursor placed at the end of the new line. */ gl->buff_curpos = gl->ntotal; /* * Erase and display the new line. */ gl_queue_redisplay(gl); return 0; } /*....................................................................... * This action function is treated specially, in that its count argument * is set to the end keystroke of the keysequence that activated it. * It accumulates a numeric argument, adding one digit on each call in * which the last keystroke was a numeric digit. */ static KT_KEY_FN(gl_digit_argument) { /* * Was the last keystroke a digit? */ int is_digit = isdigit((int)(unsigned char) count); /* * In vi command mode, a lone '0' means goto-start-of-line. */ if(gl->vi.command && gl->number < 0 && count == '0') return gl_beginning_of_line(gl, count, NULL); /* * Are we starting to accumulate a new number? */ if(gl->number < 0 || !is_digit) gl->number = 0; /* * Was the last keystroke a digit? */ if(is_digit) { /* * Read the numeric value of the digit, without assuming ASCII. */ int n; char s[2]; s[0] = count; s[1] = '\0'; n = atoi(s); /* * Append the new digit. */ gl->number = gl->number * 10 + n; }; return 0; } /*....................................................................... * The newline action function sets gl->endline to tell * gl_get_input_line() that the line is now complete. */ static KT_KEY_FN(gl_newline) { GlhLineID id; /* The last history line recalled while entering this line */ /* * Flag the line as ended. */ gl->endline = 1; /* * Record the next position in the history buffer, for potential * recall by an action function on the next call to gl_get_line(). */ id = _glh_line_id(gl->glh, 1); if(id) gl->preload_id = id; return 0; } /*....................................................................... * The 'repeat' action function sets gl->endline to tell * gl_get_input_line() that the line is now complete, and records the * ID of the next history line in gl->preload_id so that the next call * to gl_get_input_line() will preload the line with that history line. */ static KT_KEY_FN(gl_repeat_history) { gl->endline = 1; gl->preload_id = _glh_line_id(gl->glh, 1); gl->preload_history = 1; return 0; } /*....................................................................... * Flush unwritten characters to the terminal. * * Input: * gl GetLine * The getline resource object. * Output: * return int 0 - OK. * 1 - Either an error occured, or the output * blocked and non-blocking I/O is being used. * See gl->rtn_status for details. */ static int gl_flush_output(GetLine *gl) { /* * Record the fact that we are about to write to the terminal. */ gl->pending_io = GLP_WRITE; /* * Attempt to flush the output to the terminal. */ errno = 0; switch(_glq_flush_queue(gl->cq, gl->flush_fn, gl)) { case GLQ_FLUSH_DONE: return gl->redisplay && !gl->postpone && gl_redisplay(gl, 1, NULL); break; case GLQ_FLUSH_AGAIN: /* Output blocked */ gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO); return 1; break; default: /* Abort the line if an error occurs */ gl_record_status(gl, errno==EINTR ? GLR_SIGNAL : GLR_ERROR, errno); return 1; break; }; } /*....................................................................... * This is the callback which _glq_flush_queue() uses to write buffered * characters to the terminal. */ static GL_WRITE_FN(gl_flush_terminal) { int ndone = 0; /* The number of characters written so far */ /* * Get the line-editor resource object. */ GetLine *gl = (GetLine *) data; /* * Transfer the latest array of characters to stdio. */ while(ndone < n) { int nnew = write(gl->output_fd, s, n-ndone); /* * If the write was successful, add to the recorded number of bytes * that have now been written. */ if(nnew > 0) { ndone += nnew; /* * If a signal interrupted the call, restart the write(), since all of * the signals that gl_get_line() has been told to watch for are * currently blocked. */ } else if(errno == EINTR) { continue; /* * If we managed to write something before an I/O error occurred, or * output blocked before anything was written, report the number of * bytes that were successfully written before this happened. */ } else if(ndone > 0 #if defined(EAGAIN) || errno==EAGAIN #endif #if defined(EWOULDBLOCK) || errno==EWOULDBLOCK #endif ) { return ndone; /* * To get here, an error must have occurred before anything new could * be written. */ } else { return -1; }; }; /* * To get here, we must have successfully written the number of * bytes that was specified. */ return n; } /*....................................................................... * Change the style of editing to emulate a given editor. * * Input: * gl GetLine * The getline resource object. * editor GlEditor The type of editor to emulate. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_change_editor(GetLine *gl, GlEditor editor) { /* * Install the default key-bindings of the requested editor. */ switch(editor) { case GL_EMACS_MODE: _kt_clear_bindings(gl->bindings, KTB_NORM); _kt_clear_bindings(gl->bindings, KTB_TERM); (void) _kt_add_bindings(gl->bindings, KTB_NORM, gl_emacs_bindings, sizeof(gl_emacs_bindings)/sizeof(gl_emacs_bindings[0])); break; case GL_VI_MODE: _kt_clear_bindings(gl->bindings, KTB_NORM); _kt_clear_bindings(gl->bindings, KTB_TERM); (void) _kt_add_bindings(gl->bindings, KTB_NORM, gl_vi_bindings, sizeof(gl_vi_bindings)/sizeof(gl_vi_bindings[0])); break; case GL_NO_EDITOR: break; default: _err_record_msg(gl->err, "Unknown editor", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Record the new editing mode. */ gl->editor = editor; gl->vi.command = 0; /* Start in input mode */ gl->insert_curpos = 0; /* * Reinstate terminal-specific bindings. */ if(gl->editor != GL_NO_EDITOR && gl->input_fp) (void) gl_bind_terminal_keys(gl); return 0; } /*....................................................................... * This is an action function that switches to editing using emacs bindings */ static KT_KEY_FN(gl_emacs_editing_mode) { return gl_change_editor(gl, GL_EMACS_MODE); } /*....................................................................... * This is an action function that switches to editing using vi bindings */ static KT_KEY_FN(gl_vi_editing_mode) { return gl_change_editor(gl, GL_VI_MODE); } /*....................................................................... * This is the action function that switches to insert mode. */ static KT_KEY_FN(gl_vi_insert) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Switch to vi insert mode. */ gl->insert = 1; gl->vi.command = 0; gl->insert_curpos = gl->buff_curpos; return 0; } /*....................................................................... * This is an action function that switches to overwrite mode. */ static KT_KEY_FN(gl_vi_overwrite) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Switch to vi overwrite mode. */ gl->insert = 0; gl->vi.command = 0; gl->insert_curpos = gl->buff_curpos; return 0; } /*....................................................................... * This action function toggles the case of the character under the * cursor. */ static KT_KEY_FN(gl_change_case) { int i; /* * Keep a record of the current insert mode and the cursor position. */ int insert = gl->insert; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * We want to overwrite the modified word. */ gl->insert = 0; /* * Toggle the case of 'count' characters. */ for(i=0; ibuff_curpos < gl->ntotal; i++) { char *cptr = gl->line + gl->buff_curpos++; /* * Convert the character to upper case? */ if(islower((int)(unsigned char) *cptr)) gl_buffer_char(gl, toupper((int) *cptr), cptr - gl->line); else if(isupper((int)(unsigned char) *cptr)) gl_buffer_char(gl, tolower((int) *cptr), cptr - gl->line); /* * Write the possibly modified character back. Note that for non-modified * characters we want to do this as well, so as to advance the cursor. */ if(gl_print_char(gl, *cptr, cptr[1])) return 1; }; /* * Restore the insertion mode. */ gl->insert = insert; return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ } /*....................................................................... * This is the action function which implements the vi-style action which * moves the cursor to the start of the line, then switches to insert mode. */ static KT_KEY_FN(gl_vi_insert_at_bol) { gl_save_for_undo(gl); return gl_beginning_of_line(gl, 0, NULL) || gl_vi_insert(gl, 0, NULL); } /*....................................................................... * This is the action function which implements the vi-style action which * moves the cursor to the end of the line, then switches to insert mode * to allow text to be appended to the line. */ static KT_KEY_FN(gl_vi_append_at_eol) { gl_save_for_undo(gl); gl->vi.command = 0; /* Allow cursor at EOL */ return gl_end_of_line(gl, 0, NULL) || gl_vi_insert(gl, 0, NULL); } /*....................................................................... * This is the action function which implements the vi-style action which * moves the cursor to right one then switches to insert mode, thus * allowing text to be appended after the next character. */ static KT_KEY_FN(gl_vi_append) { gl_save_for_undo(gl); gl->vi.command = 0; /* Allow cursor at EOL */ return gl_cursor_right(gl, 1, NULL) || gl_vi_insert(gl, 0, NULL); } /*....................................................................... * This action function moves the cursor to the column specified by the * numeric argument. Column indexes start at 1. */ static KT_KEY_FN(gl_goto_column) { return gl_place_cursor(gl, count - 1); } /*....................................................................... * Starting with the character under the cursor, replace 'count' * characters with the next character that the user types. */ static KT_KEY_FN(gl_vi_replace_char) { char c; /* The replacement character */ int i; /* * Keep a record of the current insert mode. */ int insert = gl->insert; /* * Get the replacement character. */ if(gl->vi.repeat.active) { c = gl->vi.repeat.input_char; } else { if(gl_read_terminal(gl, 1, &c)) return 1; gl->vi.repeat.input_char = c; }; /* * Are there 'count' characters to be replaced? */ if(gl->ntotal - gl->buff_curpos >= count) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Temporarily switch to overwrite mode. */ gl->insert = 0; /* * Overwrite the current character plus count-1 subsequent characters * with the replacement character. */ for(i=0; iinsert = insert; }; return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ } /*....................................................................... * This is an action function which changes all characters between the * current cursor position and the end of the line. */ static KT_KEY_FN(gl_vi_change_rest_of_line) { gl_save_for_undo(gl); gl->vi.command = 0; /* Allow cursor at EOL */ return gl_kill_line(gl, count, NULL) || gl_vi_insert(gl, 0, NULL); } /*....................................................................... * This is an action function which changes all characters between the * start of the line and the current cursor position. */ static KT_KEY_FN(gl_vi_change_to_bol) { return gl_backward_kill_line(gl,count,NULL) || gl_vi_insert(gl,0,NULL); } /*....................................................................... * This is an action function which deletes the entire contents of the * current line and switches to insert mode. */ static KT_KEY_FN(gl_vi_change_line) { return gl_delete_line(gl,count,NULL) || gl_vi_insert(gl,0,NULL); } /*....................................................................... * Starting from the cursor position and looking towards the end of the * line, copy 'count' characters to the cut buffer. */ static KT_KEY_FN(gl_forward_copy_char) { /* * Limit the count to the number of characters available. */ if(gl->buff_curpos + count >= gl->ntotal) count = gl->ntotal - gl->buff_curpos; if(count < 0) count = 0; /* * Copy the characters to the cut buffer. */ memcpy(gl->cutbuf, gl->line + gl->buff_curpos, count); gl->cutbuf[count] = '\0'; return 0; } /*....................................................................... * Starting from the character before the cursor position and looking * backwards towards the start of the line, copy 'count' characters to * the cut buffer. */ static KT_KEY_FN(gl_backward_copy_char) { /* * Limit the count to the number of characters available. */ if(count > gl->buff_curpos) count = gl->buff_curpos; if(count < 0) count = 0; gl_place_cursor(gl, gl->buff_curpos - count); /* * Copy the characters to the cut buffer. */ memcpy(gl->cutbuf, gl->line + gl->buff_curpos, count); gl->cutbuf[count] = '\0'; return 0; } /*....................................................................... * Starting from the cursor position copy to the specified column into the * cut buffer. */ static KT_KEY_FN(gl_copy_to_column) { if (--count >= gl->buff_curpos) return gl_forward_copy_char(gl, count - gl->buff_curpos, NULL); else return gl_backward_copy_char(gl, gl->buff_curpos - count, NULL); } /*....................................................................... * Starting from the cursor position copy characters up to a matching * parenthesis into the cut buffer. */ static KT_KEY_FN(gl_copy_to_parenthesis) { int curpos = gl_index_of_matching_paren(gl); if(curpos >= 0) { gl_save_for_undo(gl); if(curpos >= gl->buff_curpos) return gl_forward_copy_char(gl, curpos - gl->buff_curpos + 1, NULL); else return gl_backward_copy_char(gl, ++gl->buff_curpos - curpos + 1, NULL); }; return 0; } /*....................................................................... * Starting from the cursor position copy the rest of the line into the * cut buffer. */ static KT_KEY_FN(gl_copy_rest_of_line) { /* * Copy the characters to the cut buffer. */ memcpy(gl->cutbuf, gl->line + gl->buff_curpos, gl->ntotal - gl->buff_curpos); gl->cutbuf[gl->ntotal - gl->buff_curpos] = '\0'; return 0; } /*....................................................................... * Copy from the beginning of the line to the cursor position into the * cut buffer. */ static KT_KEY_FN(gl_copy_to_bol) { /* * Copy the characters to the cut buffer. */ memcpy(gl->cutbuf, gl->line, gl->buff_curpos); gl->cutbuf[gl->buff_curpos] = '\0'; gl_place_cursor(gl, 0); return 0; } /*....................................................................... * Copy the entire line into the cut buffer. */ static KT_KEY_FN(gl_copy_line) { /* * Copy the characters to the cut buffer. */ memcpy(gl->cutbuf, gl->line, gl->ntotal); gl->cutbuf[gl->ntotal] = '\0'; return 0; } /*....................................................................... * Search forwards for the next character that the user enters. */ static KT_KEY_FN(gl_forward_find_char) { int pos = gl_find_char(gl, count, 1, 1, '\0'); return pos >= 0 && gl_place_cursor(gl, pos); } /*....................................................................... * Search backwards for the next character that the user enters. */ static KT_KEY_FN(gl_backward_find_char) { int pos = gl_find_char(gl, count, 0, 1, '\0'); return pos >= 0 && gl_place_cursor(gl, pos); } /*....................................................................... * Search forwards for the next character that the user enters. Move up to, * but not onto, the found character. */ static KT_KEY_FN(gl_forward_to_char) { int pos = gl_find_char(gl, count, 1, 0, '\0'); return pos >= 0 && gl_place_cursor(gl, pos); } /*....................................................................... * Search backwards for the next character that the user enters. Move back to, * but not onto, the found character. */ static KT_KEY_FN(gl_backward_to_char) { int pos = gl_find_char(gl, count, 0, 0, '\0'); return pos >= 0 && gl_place_cursor(gl, pos); } /*....................................................................... * Searching in a given direction, return the index of a given (or * read) character in the input line, or the character that precedes * it in the specified search direction. Return -1 if not found. * * Input: * gl GetLine * The getline resource object. * count int The number of times to search. * forward int True if searching forward. * onto int True if the search should end on top of the * character, false if the search should stop * one character before the character in the * specified search direction. * c char The character to be sought, or '\0' if the * character should be read from the user. * Output: * return int The index of the character in gl->line[], or * -1 if not found. */ static int gl_find_char(GetLine *gl, int count, int forward, int onto, char c) { int pos; /* The index reached in searching the input line */ int i; /* * Get a character from the user? */ if(!c) { /* * If we are in the process of repeating a previous change command, substitute * the last find character. */ if(gl->vi.repeat.active) { c = gl->vi.find_char; } else { if(gl_read_terminal(gl, 1, &c)) return -1; /* * Record the details of the new search, for use by repeat finds. */ gl->vi.find_forward = forward; gl->vi.find_onto = onto; gl->vi.find_char = c; }; }; /* * Which direction should we search? */ if(forward) { /* * Search forwards 'count' times for the character, starting with the * character that follows the cursor. */ for(i=0, pos=gl->buff_curpos; intotal; i++) { /* * Advance past the last match (or past the current cursor position * on the first search). */ pos++; /* * Search for the next instance of c. */ for( ; posntotal && c!=gl->line[pos]; pos++) ; }; /* * If the character was found and we have been requested to return the * position of the character that precedes the desired character, then * we have gone one character too far. */ if(!onto && posntotal) pos--; } else { /* * Search backwards 'count' times for the character, starting with the * character that precedes the cursor. */ for(i=0, pos=gl->buff_curpos; i= gl->insert_curpos; i++) { /* * Step back one from the last match (or from the current cursor * position on the first search). */ pos--; /* * Search for the next instance of c. */ for( ; pos>=gl->insert_curpos && c!=gl->line[pos]; pos--) ; }; /* * If the character was found and we have been requested to return the * position of the character that precedes the desired character, then * we have gone one character too far. */ if(!onto && pos>=gl->insert_curpos) pos++; }; /* * If found, return the cursor position of the count'th match. * Otherwise ring the terminal bell. */ if(pos >= gl->insert_curpos && pos < gl->ntotal) { return pos; } else { (void) gl_ring_bell(gl, 1, NULL); return -1; } } /*....................................................................... * Repeat the last character search in the same direction as the last * search. */ static KT_KEY_FN(gl_repeat_find_char) { int pos = gl->vi.find_char ? gl_find_char(gl, count, gl->vi.find_forward, gl->vi.find_onto, gl->vi.find_char) : -1; return pos >= 0 && gl_place_cursor(gl, pos); } /*....................................................................... * Repeat the last character search in the opposite direction as the last * search. */ static KT_KEY_FN(gl_invert_refind_char) { int pos = gl->vi.find_char ? gl_find_char(gl, count, !gl->vi.find_forward, gl->vi.find_onto, gl->vi.find_char) : -1; return pos >= 0 && gl_place_cursor(gl, pos); } /*....................................................................... * Search forward from the current position of the cursor for 'count' * word endings, returning the index of the last one found, or the end of * the line if there were less than 'count' words. * * Input: * gl GetLine * The getline resource object. * n int The number of word boundaries to search for. * Output: * return int The buffer index of the located position. */ static int gl_nth_word_end_forward(GetLine *gl, int n) { int bufpos; /* The buffer index being checked. */ int i; /* * In order to guarantee forward motion to the next word ending, * we need to start from one position to the right of the cursor * position, since this may already be at the end of a word. */ bufpos = gl->buff_curpos + 1; /* * If we are at the end of the line, return the index of the last * real character on the line. Note that this will be -1 if the line * is empty. */ if(bufpos >= gl->ntotal) return gl->ntotal - 1; /* * Search 'n' times, unless the end of the input line is reached first. */ for(i=0; intotal; i++) { /* * If we are not already within a word, skip to the start of the next word. */ for( ; bufposntotal && !gl_is_word_char((int)gl->line[bufpos]); bufpos++) ; /* * Find the end of the next word. */ for( ; bufposntotal && gl_is_word_char((int)gl->line[bufpos]); bufpos++) ; }; /* * We will have overshot. */ return bufpos > 0 ? bufpos-1 : bufpos; } /*....................................................................... * Search forward from the current position of the cursor for 'count' * word starts, returning the index of the last one found, or the end of * the line if there were less than 'count' words. * * Input: * gl GetLine * The getline resource object. * n int The number of word boundaries to search for. * Output: * return int The buffer index of the located position. */ static int gl_nth_word_start_forward(GetLine *gl, int n) { int bufpos; /* The buffer index being checked. */ int i; /* * Get the current cursor position. */ bufpos = gl->buff_curpos; /* * Search 'n' times, unless the end of the input line is reached first. */ for(i=0; intotal; i++) { /* * Find the end of the current word. */ for( ; bufposntotal && gl_is_word_char((int)gl->line[bufpos]); bufpos++) ; /* * Skip to the start of the next word. */ for( ; bufposntotal && !gl_is_word_char((int)gl->line[bufpos]); bufpos++) ; }; return bufpos; } /*....................................................................... * Search backward from the current position of the cursor for 'count' * word starts, returning the index of the last one found, or the start * of the line if there were less than 'count' words. * * Input: * gl GetLine * The getline resource object. * n int The number of word boundaries to search for. * Output: * return int The buffer index of the located position. */ static int gl_nth_word_start_backward(GetLine *gl, int n) { int bufpos; /* The buffer index being checked. */ int i; /* * Get the current cursor position. */ bufpos = gl->buff_curpos; /* * Search 'n' times, unless the beginning of the input line (or vi insertion * point) is reached first. */ for(i=0; i gl->insert_curpos; i++) { /* * Starting one character back from the last search, so as not to keep * settling on the same word-start, search backwards until finding a * word character. */ while(--bufpos >= gl->insert_curpos && !gl_is_word_char((int)gl->line[bufpos])) ; /* * Find the start of the word. */ while(--bufpos >= gl->insert_curpos && gl_is_word_char((int)gl->line[bufpos])) ; /* * We will have gone one character too far. */ bufpos++; }; return bufpos >= gl->insert_curpos ? bufpos : gl->insert_curpos; } /*....................................................................... * Copy one or more words into the cut buffer without moving the cursor * or deleting text. */ static KT_KEY_FN(gl_forward_copy_word) { /* * Find the location of the count'th start or end of a word * after the cursor, depending on whether in emacs or vi mode. */ int next = gl->editor == GL_EMACS_MODE ? gl_nth_word_end_forward(gl, count) : gl_nth_word_start_forward(gl, count); /* * How many characters are to be copied into the cut buffer? */ int n = next - gl->buff_curpos; /* * Copy the specified segment and terminate the string. */ memcpy(gl->cutbuf, gl->line + gl->buff_curpos, n); gl->cutbuf[n] = '\0'; return 0; } /*....................................................................... * Copy one or more words preceding the cursor into the cut buffer, * without moving the cursor or deleting text. */ static KT_KEY_FN(gl_backward_copy_word) { /* * Find the location of the count'th start of word before the cursor. */ int next = gl_nth_word_start_backward(gl, count); /* * How many characters are to be copied into the cut buffer? */ int n = gl->buff_curpos - next; gl_place_cursor(gl, next); /* * Copy the specified segment and terminate the string. */ memcpy(gl->cutbuf, gl->line + next, n); gl->cutbuf[n] = '\0'; return 0; } /*....................................................................... * Copy the characters between the cursor and the count'th instance of * a specified character in the input line, into the cut buffer. * * Input: * gl GetLine * The getline resource object. * count int The number of times to search. * c char The character to be searched for, or '\0' if * the character should be read from the user. * forward int True if searching forward. * onto int True if the search should end on top of the * character, false if the search should stop * one character before the character in the * specified search direction. * Output: * return int 0 - OK. * 1 - Error. * */ static int gl_copy_find(GetLine *gl, int count, char c, int forward, int onto) { int n; /* The number of characters in the cut buffer */ /* * Search for the character, and abort the operation if not found. */ int pos = gl_find_char(gl, count, forward, onto, c); if(pos < 0) return 0; /* * Copy the specified segment. */ if(forward) { n = pos + 1 - gl->buff_curpos; memcpy(gl->cutbuf, gl->line + gl->buff_curpos, n); } else { n = gl->buff_curpos - pos; memcpy(gl->cutbuf, gl->line + pos, n); if(gl->editor == GL_VI_MODE) gl_place_cursor(gl, pos); } /* * Terminate the copy. */ gl->cutbuf[n] = '\0'; return 0; } /*....................................................................... * Copy a section up to and including a specified character into the cut * buffer without moving the cursor or deleting text. */ static KT_KEY_FN(gl_forward_copy_find) { return gl_copy_find(gl, count, '\0', 1, 1); } /*....................................................................... * Copy a section back to and including a specified character into the cut * buffer without moving the cursor or deleting text. */ static KT_KEY_FN(gl_backward_copy_find) { return gl_copy_find(gl, count, '\0', 0, 1); } /*....................................................................... * Copy a section up to and not including a specified character into the cut * buffer without moving the cursor or deleting text. */ static KT_KEY_FN(gl_forward_copy_to) { return gl_copy_find(gl, count, '\0', 1, 0); } /*....................................................................... * Copy a section back to and not including a specified character into the cut * buffer without moving the cursor or deleting text. */ static KT_KEY_FN(gl_backward_copy_to) { return gl_copy_find(gl, count, '\0', 0, 0); } /*....................................................................... * Copy to a character specified in a previous search into the cut * buffer without moving the cursor or deleting text. */ static KT_KEY_FN(gl_copy_refind) { return gl_copy_find(gl, count, gl->vi.find_char, gl->vi.find_forward, gl->vi.find_onto); } /*....................................................................... * Copy to a character specified in a previous search, but in the opposite * direction, into the cut buffer without moving the cursor or deleting text. */ static KT_KEY_FN(gl_copy_invert_refind) { return gl_copy_find(gl, count, gl->vi.find_char, !gl->vi.find_forward, gl->vi.find_onto); } /*....................................................................... * Set the position of the cursor in the line input buffer and the * terminal. * * Input: * gl GetLine * The getline resource object. * buff_curpos int The new buffer cursor position. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_place_cursor(GetLine *gl, int buff_curpos) { /* * Don't allow the cursor position to go out of the bounds of the input * line. */ if(buff_curpos >= gl->ntotal) buff_curpos = gl->vi.command ? gl->ntotal-1 : gl->ntotal; if(buff_curpos < 0) buff_curpos = 0; /* * Record the new buffer position. */ gl->buff_curpos = buff_curpos; /* * Move the terminal cursor to the corresponding character. */ return gl_set_term_curpos(gl, gl->prompt_len + gl_displayed_string_width(gl, gl->line, buff_curpos, gl->prompt_len)); } /*....................................................................... * In vi command mode, this function saves the current line to the * historical buffer needed by the undo command. In emacs mode it does * nothing. In order to allow action functions to call other action * functions, gl_interpret_char() sets gl->vi.undo.saved to 0 before * invoking an action, and thereafter once any call to this function * has set it to 1, further calls are ignored. * * Input: * gl GetLine * The getline resource object. */ static void gl_save_for_undo(GetLine *gl) { if(gl->vi.command && !gl->vi.undo.saved) { strcpy(gl->vi.undo.line, gl->line); gl->vi.undo.buff_curpos = gl->buff_curpos; gl->vi.undo.ntotal = gl->ntotal; gl->vi.undo.saved = 1; }; if(gl->vi.command && !gl->vi.repeat.saved && gl->current_action.fn != gl_vi_repeat_change) { gl->vi.repeat.action = gl->current_action; gl->vi.repeat.count = gl->current_count; gl->vi.repeat.saved = 1; }; return; } /*....................................................................... * In vi mode, restore the line to the way it was before the last command * mode operation, storing the current line in the buffer so that the * undo operation itself can subsequently be undone. */ static KT_KEY_FN(gl_vi_undo) { /* * Get pointers into the two lines. */ char *undo_ptr = gl->vi.undo.line; char *line_ptr = gl->line; /* * Swap the characters of the two buffers up to the length of the shortest * line. */ while(*undo_ptr && *line_ptr) { char c = *undo_ptr; *undo_ptr++ = *line_ptr; *line_ptr++ = c; }; /* * Copy the rest directly. */ if(gl->ntotal > gl->vi.undo.ntotal) { strcpy(undo_ptr, line_ptr); *line_ptr = '\0'; } else { strcpy(line_ptr, undo_ptr); *undo_ptr = '\0'; }; /* * Record the length of the stored string. */ gl->vi.undo.ntotal = gl->ntotal; /* * Accomodate the new contents of gl->line[]. */ gl_update_buffer(gl); /* * Set both cursor positions to the leftmost of the saved and current * cursor positions to emulate what vi does. */ if(gl->buff_curpos < gl->vi.undo.buff_curpos) gl->vi.undo.buff_curpos = gl->buff_curpos; else gl->buff_curpos = gl->vi.undo.buff_curpos; /* * Since we have bipassed calling gl_save_for_undo(), record repeat * information inline. */ gl->vi.repeat.action.fn = gl_vi_undo; gl->vi.repeat.action.data = NULL; gl->vi.repeat.count = 1; /* * Display the restored line. */ gl_queue_redisplay(gl); return 0; } /*....................................................................... * Delete the following word and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_forward_change_word) { gl_save_for_undo(gl); gl->vi.command = 0; /* Allow cursor at EOL */ return gl_forward_delete_word(gl, count, NULL) || gl_vi_insert(gl, 0, NULL); } /*....................................................................... * Delete the preceding word and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_backward_change_word) { return gl_backward_delete_word(gl, count, NULL) || gl_vi_insert(gl, 0, NULL); } /*....................................................................... * Delete the following section and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_forward_change_find) { return gl_delete_find(gl, count, '\0', 1, 1, 1); } /*....................................................................... * Delete the preceding section and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_backward_change_find) { return gl_delete_find(gl, count, '\0', 0, 1, 1); } /*....................................................................... * Delete the following section and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_forward_change_to) { return gl_delete_find(gl, count, '\0', 1, 0, 1); } /*....................................................................... * Delete the preceding section and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_backward_change_to) { return gl_delete_find(gl, count, '\0', 0, 0, 1); } /*....................................................................... * Delete to a character specified by a previous search and leave the user * in vi insert mode. */ static KT_KEY_FN(gl_vi_change_refind) { return gl_delete_find(gl, count, gl->vi.find_char, gl->vi.find_forward, gl->vi.find_onto, 1); } /*....................................................................... * Delete to a character specified by a previous search, but in the opposite * direction, and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_change_invert_refind) { return gl_delete_find(gl, count, gl->vi.find_char, !gl->vi.find_forward, gl->vi.find_onto, 1); } /*....................................................................... * Delete the following character and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_forward_change_char) { gl_save_for_undo(gl); gl->vi.command = 0; /* Allow cursor at EOL */ return gl_delete_chars(gl, count, 1) || gl_vi_insert(gl, 0, NULL); } /*....................................................................... * Delete the preceding character and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_backward_change_char) { return gl_backward_delete_char(gl, count, NULL) || gl_vi_insert(gl, 0, NULL); } /*....................................................................... * Starting from the cursor position change characters to the specified column. */ static KT_KEY_FN(gl_vi_change_to_column) { if (--count >= gl->buff_curpos) return gl_vi_forward_change_char(gl, count - gl->buff_curpos, NULL); else return gl_vi_backward_change_char(gl, gl->buff_curpos - count, NULL); } /*....................................................................... * Starting from the cursor position change characters to a matching * parenthesis. */ static KT_KEY_FN(gl_vi_change_to_parenthesis) { int curpos = gl_index_of_matching_paren(gl); if(curpos >= 0) { gl_save_for_undo(gl); if(curpos >= gl->buff_curpos) return gl_vi_forward_change_char(gl, curpos - gl->buff_curpos + 1, NULL); else return gl_vi_backward_change_char(gl, ++gl->buff_curpos - curpos + 1, NULL); }; return 0; } /*....................................................................... * If in vi mode, switch to vi command mode. * * Input: * gl GetLine * The getline resource object. */ static void gl_vi_command_mode(GetLine *gl) { if(gl->editor == GL_VI_MODE && !gl->vi.command) { gl->insert = 1; gl->vi.command = 1; gl->vi.repeat.input_curpos = gl->insert_curpos; gl->vi.repeat.command_curpos = gl->buff_curpos; gl->insert_curpos = 0; /* unrestrict left motion boundary */ gl_cursor_left(gl, 1, NULL); /* Vi moves 1 left on entering command mode */ }; } /*....................................................................... * This is an action function which rings the terminal bell. */ static KT_KEY_FN(gl_ring_bell) { return gl->silence_bell ? 0 : gl_print_control_sequence(gl, 1, gl->sound_bell); } /*....................................................................... * This is the action function which implements the vi-repeat-change * action. */ static KT_KEY_FN(gl_vi_repeat_change) { int status; /* The return status of the repeated action function */ int i; /* * Nothing to repeat? */ if(!gl->vi.repeat.action.fn) return gl_ring_bell(gl, 1, NULL); /* * Provide a way for action functions to know whether they are being * called by us. */ gl->vi.repeat.active = 1; /* * Re-run the recorded function. */ status = gl->vi.repeat.action.fn(gl, gl->vi.repeat.count, gl->vi.repeat.action.data); /* * Mark the repeat as completed. */ gl->vi.repeat.active = 0; /* * Is we are repeating a function that has just switched to input * mode to allow the user to type, re-enter the text that the user * previously entered. */ if(status==0 && !gl->vi.command) { /* * Make sure that the current line has been saved. */ gl_save_for_undo(gl); /* * Repeat a previous insertion or overwrite? */ if(gl->vi.repeat.input_curpos >= 0 && gl->vi.repeat.input_curpos <= gl->vi.repeat.command_curpos && gl->vi.repeat.command_curpos <= gl->vi.undo.ntotal) { /* * Using the current line which is saved in the undo buffer, plus * the range of characters therein, as recorded by gl_vi_command_mode(), * add the characters that the user previously entered, to the input * line. */ for(i=gl->vi.repeat.input_curpos; ivi.repeat.command_curpos; i++) { if(gl_add_char_to_line(gl, gl->vi.undo.line[i])) return 1; }; }; /* * Switch back to command mode, now that the insertion has been repeated. */ gl_vi_command_mode(gl); }; return status; } /*....................................................................... * If the cursor is currently over a parenthesis character, return the * index of its matching parenthesis. If not currently over a parenthesis * character, return the next close parenthesis character to the right of * the cursor. If the respective parenthesis character isn't found, * ring the terminal bell and return -1. * * Input: * gl GetLine * The getline resource object. * Output: * return int Either the index of the matching parenthesis, * or -1 if not found. */ static int gl_index_of_matching_paren(GetLine *gl) { int i; /* * List the recognized parentheses, and their matches. */ const char *o_paren = "([{"; const char *c_paren = ")]}"; const char *cptr; /* * Get the character that is currently under the cursor. */ char c = gl->line[gl->buff_curpos]; /* * If the character under the cursor is an open parenthesis, look forward * for the matching close parenthesis. */ if((cptr=strchr(o_paren, c))) { char match = c_paren[cptr - o_paren]; int matches_needed = 1; for(i=gl->buff_curpos+1; intotal; i++) { if(gl->line[i] == c) matches_needed++; else if(gl->line[i] == match && --matches_needed==0) return i; }; /* * If the character under the cursor is an close parenthesis, look forward * for the matching open parenthesis. */ } else if((cptr=strchr(c_paren, c))) { char match = o_paren[cptr - c_paren]; int matches_needed = 1; for(i=gl->buff_curpos-1; i>=0; i--) { if(gl->line[i] == c) matches_needed++; else if(gl->line[i] == match && --matches_needed==0) return i; }; /* * If not currently over a parenthesis character, search forwards for * the first close parenthesis (this is what the vi % binding does). */ } else { for(i=gl->buff_curpos+1; intotal; i++) if(strchr(c_paren, gl->line[i]) != NULL) return i; }; /* * Not found. */ (void) gl_ring_bell(gl, 1, NULL); return -1; } /*....................................................................... * If the cursor is currently over a parenthesis character, this action * function moves the cursor to its matching parenthesis. */ static KT_KEY_FN(gl_find_parenthesis) { int curpos = gl_index_of_matching_paren(gl); if(curpos >= 0) return gl_place_cursor(gl, curpos); return 0; } /*....................................................................... * Handle the receipt of the potential start of a new key-sequence from * the user. * * Input: * gl GetLine * The resource object of this library. * first_char char The first character of the sequence. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_interpret_char(GetLine *gl, char first_char) { char keyseq[GL_KEY_MAX+1]; /* A special key sequence being read */ int nkey=0; /* The number of characters in the key sequence */ int count; /* The repeat count of an action function */ int ret; /* The return value of an action function */ int i; /* * Get the first character. */ char c = first_char; /* * If editing is disabled, just add newly entered characters to the * input line buffer, and watch for the end of the line. */ if(gl->editor == GL_NO_EDITOR) { gl_discard_chars(gl, 1); if(gl->ntotal >= gl->linelen) return 0; if(c == '\n' || c == '\r') return gl_newline(gl, 1, NULL); gl_buffer_char(gl, c, gl->ntotal); return 0; }; /* * If the user is in the process of specifying a repeat count and the * new character is a digit, increment the repeat count accordingly. */ if(gl->number >= 0 && isdigit((int)(unsigned char) c)) { gl_discard_chars(gl, 1); return gl_digit_argument(gl, c, NULL); /* * In vi command mode, all key-sequences entered need to be * either implicitly or explicitly prefixed with an escape character. */ } else if(gl->vi.command && c != GL_ESC_CHAR) { keyseq[nkey++] = GL_ESC_CHAR; /* * If the first character of the sequence is a printable character, * then to avoid confusion with the special "up", "down", "left" * or "right" cursor key bindings, we need to prefix the * printable character with a backslash escape before looking it up. */ } else if(!IS_META_CHAR(c) && !IS_CTRL_CHAR(c)) { keyseq[nkey++] = '\\'; }; /* * Compose a potentially multiple key-sequence in gl->keyseq. */ while(nkey < GL_KEY_MAX) { KtAction *action; /* An action function */ KeySym *keysym; /* The symbol-table entry of a key-sequence */ int nsym; /* The number of ambiguously matching key-sequences */ /* * If the character is an unprintable meta character, split it * into two characters, an escape character and the character * that was modified by the meta key. */ if(IS_META_CHAR(c)) { keyseq[nkey++] = GL_ESC_CHAR; c = META_TO_CHAR(c); continue; }; /* * Append the latest character to the key sequence. */ keyseq[nkey++] = c; /* * When doing vi-style editing, an escape at the beginning of any binding * switches to command mode. */ if(keyseq[0] == GL_ESC_CHAR && !gl->vi.command) gl_vi_command_mode(gl); /* * Lookup the key sequence. */ switch(_kt_lookup_keybinding(gl->bindings, keyseq, nkey, &keysym, &nsym)) { case KT_EXACT_MATCH: /* * Get the matching action function. */ action = keysym->actions + keysym->binder; /* * Get the repeat count, passing the last keystroke if executing the * digit-argument action. */ if(action->fn == gl_digit_argument) { count = c; } else { count = gl->number >= 0 ? gl->number : 1; }; /* * Record the function that is being invoked. */ gl->current_action = *action; gl->current_count = count; /* * Mark the current line as not yet preserved for use by the vi undo command. */ gl->vi.undo.saved = 0; gl->vi.repeat.saved = 0; /* * Execute the action function. Note the action function can tell * whether the provided repeat count was defaulted or specified * explicitly by looking at whether gl->number is -1 or not. If * it is negative, then no repeat count was specified by the user. */ ret = action->fn(gl, count, action->data); /* * In server mode, the action will return immediately if it tries to * read input from the terminal, and no input is currently available. * If this happens, abort. Note that gl_get_input_line() will rewind * the read-ahead buffer to allow the next call to redo the function * from scratch. */ if(gl->rtn_status == GLR_BLOCKED && gl->pending_io==GLP_READ) return 1; /* * Discard the now processed characters from the key sequence buffer. */ gl_discard_chars(gl, gl->nread); /* * If the latest action function wasn't a history action, cancel any * current history search. */ if(gl->last_search != gl->keyseq_count) _glh_cancel_search(gl->glh); /* * Reset the repeat count after running action functions. */ if(action->fn != gl_digit_argument) gl->number = -1; return ret ? 1 : 0; break; case KT_AMBIG_MATCH: /* Ambiguous match - so read the next character */ if(gl_read_terminal(gl, 1, &c)) return 1; break; case KT_NO_MATCH: /* * If the first character looked like it might be a prefix of a key-sequence * but it turned out not to be, ring the bell to tell the user that it * wasn't recognised. */ if(keyseq[0] != '\\' && keyseq[0] != '\t') { gl_ring_bell(gl, 1, NULL); } else { /* * The user typed a single printable character that doesn't match * the start of any keysequence, so add it to the line in accordance * with the current repeat count. */ count = gl->number >= 0 ? gl->number : 1; for(i=0; inumber = -1; }; gl_discard_chars(gl, 1); _glh_cancel_search(gl->glh); return 0; break; case KT_BAD_MATCH: gl_ring_bell(gl, 1, NULL); gl_discard_chars(gl, gl->nread); _glh_cancel_search(gl->glh); return 1; break; }; }; /* * If the key sequence was too long to match, ring the bell, then * discard the first character, so that the next attempt to match a * key-sequence continues with the next key press. In practice this * shouldn't happen, since one isn't allowed to bind action functions * to keysequences that are longer than GL_KEY_MAX. */ gl_ring_bell(gl, 1, NULL); gl_discard_chars(gl, 1); return 0; } /*....................................................................... * Configure the application and/or user-specific behavior of * gl_get_line(). * * Note that calling this function between calling new_GetLine() and * the first call to gl_get_line(), disables the otherwise automatic * reading of ~/.teclarc on the first call to gl_get_line(). * * Input: * gl GetLine * The resource object of this library. * app_string const char * Either NULL, or a string containing one * or more .teclarc command lines, separated * by newline characters. This can be used to * establish an application-specific * configuration, without the need for an external * file. This is particularly useful in embedded * environments where there is no filesystem. * app_file const char * Either NULL, or the pathname of an * application-specific .teclarc file. The * contents of this file, if provided, are * read after the contents of app_string[]. * user_file const char * Either NULL, or the pathname of a * user-specific .teclarc file. Except in * embedded applications, this should * usually be "~/.teclarc". * Output: * return int 0 - OK. * 1 - Bad argument(s). */ int gl_configure_getline(GetLine *gl, const char *app_string, const char *app_file, const char *user_file) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of _gl_configure_getline() */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Execute the private body of the function while signals are blocked. */ status = _gl_configure_getline(gl, app_string, app_file, user_file); /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the gl_configure_getline() function. It * assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static int _gl_configure_getline(GetLine *gl, const char *app_string, const char *app_file, const char *user_file) { /* * Mark getline as having been explicitly configured. */ gl->configured = 1; /* * Start by parsing the configuration string, if provided. */ if(app_string) (void) _gl_read_config_string(gl, app_string, KTB_NORM); /* * Now parse the application-specific configuration file, if provided. */ if(app_file) (void) _gl_read_config_file(gl, app_file, KTB_NORM); /* * Finally, parse the user-specific configuration file, if provided. */ if(user_file) (void) _gl_read_config_file(gl, user_file, KTB_USER); /* * Record the names of the configuration files to allow them to * be re-read if requested at a later time. */ if(gl_record_string(&gl->app_file, app_file) || gl_record_string(&gl->user_file, user_file)) { errno = ENOMEM; _err_record_msg(gl->err, "Insufficient memory to record tecla configuration file names", END_ERR_MSG); return 1; }; return 0; } /*....................................................................... * Replace a malloc'd string (or NULL), with another malloc'd copy of * a string (or NULL). * * Input: * sptr char ** On input if *sptr!=NULL, *sptr will be * free'd and *sptr will be set to NULL. Then, * on output, if string!=NULL a malloc'd copy * of this string will be assigned to *sptr. * string const char * The string to be copied, or NULL to simply * discard any existing string. * Output: * return int 0 - OK. * 1 - Malloc failure (no error message is generated). */ static int gl_record_string(char **sptr, const char *string) { /* * If the original string is the same string, don't do anything. */ if(*sptr == string || (*sptr && string && strcmp(*sptr, string)==0)) return 0; /* * Discard any existing cached string. */ if(*sptr) { free(*sptr); *sptr = NULL; }; /* * Allocate memory for a copy of the specified string. */ if(string) { *sptr = (char *) malloc(strlen(string) + 1); if(!*sptr) return 1; /* * Copy the string. */ strcpy(*sptr, string); }; return 0; } #ifndef HIDE_FILE_SYSTEM /*....................................................................... * Re-read any application-specific and user-specific files previously * specified via the gl_configure_getline() function. */ static KT_KEY_FN(gl_read_init_files) { return _gl_configure_getline(gl, NULL, gl->app_file, gl->user_file); } #endif /*....................................................................... * Save the contents of the history buffer to a given new file. * * Input: * gl GetLine * The resource object of this library. * filename const char * The name of the new file to write to. * comment const char * Extra information such as timestamps will * be recorded on a line started with this * string, the idea being that the file can * double as a command file. Specify "" if * you don't care. * max_lines int The maximum number of lines to save, or -1 * to save all of the lines in the history * list. * Output: * return int 0 - OK. * 1 - Error. */ int gl_save_history(GetLine *gl, const char *filename, const char *comment, int max_lines) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of _gl_save_history() */ /* * Check the arguments. */ if(!gl || !filename || !comment) { if(gl) _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Execute the private body of the function while signals are blocked. */ status = _gl_save_history(gl, filename, comment, max_lines); /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the gl_save_history() function. It * assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static int _gl_save_history(GetLine *gl, const char *filename, const char *comment, int max_lines) { /* * If filesystem access is to be excluded, then history files can't * be written. */ #ifdef WITHOUT_FILE_SYSTEM _err_record_msg(gl->err, "Can't save history without filesystem access", END_ERR_MSG); errno = EINVAL; return 1; #else FileExpansion *expansion; /* The expansion of the filename */ /* * Expand the filename. */ expansion = ef_expand_file(gl->ef, filename, -1); if(!expansion) { gl_print_info(gl, "Unable to expand ", filename, " (", ef_last_error(gl->ef), ").", GL_END_INFO); return 1; }; /* * Attempt to save to the specified file. */ if(_glh_save_history(gl->glh, expansion->files[0], comment, max_lines)) { _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); return 1; }; return 0; #endif } /*....................................................................... * Restore the contents of the history buffer from a given new file. * * Input: * gl GetLine * The resource object of this library. * filename const char * The name of the new file to write to. * comment const char * This must be the same string that was * passed to gl_save_history() when the file * was written. * Output: * return int 0 - OK. * 1 - Error. */ int gl_load_history(GetLine *gl, const char *filename, const char *comment) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of _gl_load_history() */ /* * Check the arguments. */ if(!gl || !filename || !comment) { if(gl) _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Execute the private body of the function while signals are blocked. */ status = _gl_load_history(gl, filename, comment); /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the gl_load_history() function. It * assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static int _gl_load_history(GetLine *gl, const char *filename, const char *comment) { /* * If filesystem access is to be excluded, then history files can't * be read. */ #ifdef WITHOUT_FILE_SYSTEM _err_record_msg(gl->err, "Can't load history without filesystem access", END_ERR_MSG); errno = EINVAL; return 1; #else FileExpansion *expansion; /* The expansion of the filename */ /* * Expand the filename. */ expansion = ef_expand_file(gl->ef, filename, -1); if(!expansion) { gl_print_info(gl, "Unable to expand ", filename, " (", ef_last_error(gl->ef), ").", GL_END_INFO); return 1; }; /* * Attempt to load from the specified file. */ if(_glh_load_history(gl->glh, expansion->files[0], comment, gl->cutbuf, gl->linelen+1)) { _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); gl->cutbuf[0] = '\0'; return 1; }; gl->cutbuf[0] = '\0'; return 0; #endif } /*....................................................................... * Where possible, register a function and associated data to be called * whenever a specified event is seen on a file descriptor. * * Input: * gl GetLine * The resource object of the command-line input * module. * fd int The file descriptor to watch. * event GlFdEvent The type of activity to watch for. * callback GlFdEventFn * The function to call when the specified * event occurs. Setting this to 0 removes * any existing callback. * data void * A pointer to arbitrary data to pass to the * callback function. * Output: * return int 0 - OK. * 1 - Either gl==NULL, or this facility isn't * available on the the host system * (ie. select() isn't available). No * error message is generated in the latter * case. */ int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, GlFdEventFn *callback, void *data) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of _gl_watch_fd() */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return 1; }; if(fd < 0) { _err_record_msg(gl->err, "Error: fd < 0", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Execute the private body of the function while signals are blocked. */ status = _gl_watch_fd(gl, fd, event, callback, data); /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the gl_watch_fd() function. It * assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static int _gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, GlFdEventFn *callback, void *data) #if !defined(HAVE_SELECT) {return 1;} /* The facility isn't supported on this system */ #else { GlFdNode *prev; /* The node that precedes 'node' in gl->fd_nodes */ GlFdNode *node; /* The file-descriptor node being checked */ /* * Search the list of already registered fd activity nodes for the specified * file descriptor. */ for(prev=NULL,node=gl->fd_nodes; node && node->fd != fd; prev=node, node=node->next) ; /* * Hasn't a node been allocated for this fd yet? */ if(!node) { /* * If there is no callback to record, just ignore the call. */ if(!callback) return 0; /* * Allocate the new node. */ node = (GlFdNode *) _new_FreeListNode(gl->fd_node_mem); if(!node) { errno = ENOMEM; _err_record_msg(gl->err, "Insufficient memory", END_ERR_MSG); return 1; }; /* * Prepend the node to the list. */ node->next = gl->fd_nodes; gl->fd_nodes = node; /* * Initialize the node. */ node->fd = fd; node->rd.fn = 0; node->rd.data = NULL; node->ur = node->wr = node->rd; }; /* * Record the new callback. */ switch(event) { case GLFD_READ: node->rd.fn = callback; node->rd.data = data; if(callback) FD_SET(fd, &gl->rfds); else FD_CLR(fd, &gl->rfds); break; case GLFD_WRITE: node->wr.fn = callback; node->wr.data = data; if(callback) FD_SET(fd, &gl->wfds); else FD_CLR(fd, &gl->wfds); break; case GLFD_URGENT: node->ur.fn = callback; node->ur.data = data; if(callback) FD_SET(fd, &gl->ufds); else FD_CLR(fd, &gl->ufds); break; }; /* * Keep a record of the largest file descriptor being watched. */ if(fd > gl->max_fd) gl->max_fd = fd; /* * If we are deleting an existing callback, also delete the parent * activity node if no callbacks are registered to the fd anymore. */ if(!callback) { if(!node->rd.fn && !node->wr.fn && !node->ur.fn) { if(prev) prev->next = node->next; else gl->fd_nodes = node->next; node = (GlFdNode *) _del_FreeListNode(gl->fd_node_mem, node); }; }; return 0; } #endif /*....................................................................... * On systems with the select() system call, the gl_inactivity_timeout() * function provides the option of setting (or cancelling) an * inactivity timeout. Inactivity, in this case, refers both to * terminal input received from the user, and to I/O on any file * descriptors registered by calls to gl_watch_fd(). If at any time, * no activity is seen for the requested time period, the specified * timeout callback function is called. On returning, this callback * returns a code which tells gl_get_line() what to do next. Note that * each call to gl_inactivity_timeout() replaces any previously installed * timeout callback, and that specifying a callback of 0, turns off * inactivity timing. * * Beware that although the timeout argument includes a nano-second * component, few computer clocks presently have resolutions finer * than a few milliseconds, so asking for less than a few milliseconds * is equivalent to zero on a lot of systems. * * Input: * gl GetLine * The resource object of the command-line input * module. * callback GlTimeoutFn * The function to call when the inactivity * timeout is exceeded. To turn off * inactivity timeouts altogether, send 0. * data void * A pointer to arbitrary data to pass to the * callback function. * sec unsigned long The number of whole seconds in the timeout. * nsec unsigned long The fractional number of seconds in the * timeout, expressed in nano-seconds (see * the caveat above). * Output: * return int 0 - OK. * 1 - Either gl==NULL, or this facility isn't * available on the the host system * (ie. select() isn't available). No * error message is generated in the latter * case. */ int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *timeout_fn, void *data, unsigned long sec, unsigned long nsec) #if !defined(HAVE_SELECT) {return 1;} /* The facility isn't supported on this system */ #else { sigset_t oldset; /* The signals that were blocked on entry to this function */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Install a new timeout? */ if(timeout_fn) { gl->timer.dt.tv_sec = sec; gl->timer.dt.tv_usec = nsec / 1000; gl->timer.fn = timeout_fn; gl->timer.data = data; } else { gl->timer.fn = 0; gl->timer.data = NULL; }; /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return 0; } #endif /*....................................................................... * When select() is available, this is a private function of * gl_read_input() which responds to file-descriptor events registered by * the caller. Note that it assumes that it is being called from within * gl_read_input()'s sigsetjump() clause. * * Input: * gl GetLine * The resource object of this module. * fd int The file descriptor to be watched for user input. * Output: * return int 0 - OK. * 1 - An error occurred. */ static int gl_event_handler(GetLine *gl, int fd) #if !defined(HAVE_SELECT) {return 0;} #else { /* * Set up a zero-second timeout. */ struct timeval zero; zero.tv_sec = zero.tv_usec = 0; /* * If at any time no external callbacks remain, quit the loop return, * so that we can simply wait in read(). This is designed as an * optimization for when no callbacks have been registered on entry to * this function, but since callbacks can delete themselves, it can * also help later. */ while(gl->fd_nodes || gl->timer.fn) { int nready; /* The number of file descriptors that are ready for I/O */ /* * Get the set of descriptors to be watched. */ fd_set rfds = gl->rfds; fd_set wfds = gl->wfds; fd_set ufds = gl->ufds; /* * Get the appropriate timeout. */ struct timeval dt = gl->timer.fn ? gl->timer.dt : zero; /* * Add the specified user-input file descriptor tot he set that is to * be watched. */ FD_SET(fd, &rfds); /* * Unblock the signals that we are watching, while select is blocked * waiting for I/O. */ gl_catch_signals(gl); /* * Wait for activity on any of the file descriptors. */ nready = select(gl->max_fd+1, &rfds, &wfds, &ufds, (gl->timer.fn || gl->io_mode==GL_SERVER_MODE) ? &dt : NULL); /* * We don't want to do a longjmp in the middle of a callback that * might be modifying global or heap data, so block all the signals * that we are trapping before executing callback functions. Note that * the caller will unblock them again when it needs to, so there is * no need to undo this before returning. */ gl_mask_signals(gl, NULL); /* * If select() returns but none of the file descriptors are reported * to have activity, then select() timed out. */ if(nready == 0) { /* * Note that in non-blocking server mode, the inactivity timer is used * to allow I/O to block for a specified amount of time, so in this * mode we return the postponed blocked status when an abort is * requested. */ if(gl_call_timeout_handler(gl)) { return 1; } else if(gl->io_mode == GL_SERVER_MODE) { gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO); return 1; }; /* * If nready < 0, this means an error occurred. */ } else if(nready < 0) { if(errno != EINTR) { gl_record_status(gl, GLR_ERROR, errno); return 1; }; /* * If the user-input file descriptor has data available, return. */ } else if(FD_ISSET(fd, &rfds)) { return 0; /* * Check for activity on any of the file descriptors registered by the * calling application, and call the associated callback functions. */ } else { GlFdNode *node; /* The fd event node being checked */ /* * Search the list for the file descriptor that caused select() to return. */ for(node=gl->fd_nodes; node; node=node->next) { /* * Is there urgent out of band data waiting to be read on fd? */ if(node->ur.fn && FD_ISSET(node->fd, &ufds)) { if(gl_call_fd_handler(gl, &node->ur, node->fd, GLFD_URGENT)) return 1; break; /* The callback may have changed the list of nodes */ /* * Is the fd readable? */ } else if(node->rd.fn && FD_ISSET(node->fd, &rfds)) { if(gl_call_fd_handler(gl, &node->rd, node->fd, GLFD_READ)) return 1; break; /* The callback may have changed the list of nodes */ /* * Is the fd writable? */ } else if(node->wr.fn && FD_ISSET(node->fd, &wfds)) { if(gl_call_fd_handler(gl, &node->wr, node->fd, GLFD_WRITE)) return 1; break; /* The callback may have changed the list of nodes */ }; }; }; /* * Just in case the above event handlers asked for the input line to * be redrawn, flush any pending output. */ if(gl_flush_output(gl)) return 1; }; return 0; } #endif #if defined(HAVE_SELECT) /*....................................................................... * This is a private function of gl_event_handler(), used to call a * file-descriptor callback. * * Input: * gl GetLine * The resource object of gl_get_line(). * gfh GlFdHandler * The I/O handler. * fd int The file-descriptor being reported. * event GlFdEvent The I/O event being reported. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_call_fd_handler(GetLine *gl, GlFdHandler *gfh, int fd, GlFdEvent event) { Termios attr; /* The terminal attributes */ int waserr = 0; /* True after any error */ /* * Re-enable conversion of newline characters to carriage-return/linefeed, * so that the callback can write to the terminal without having to do * anything special. */ if(tcgetattr(gl->input_fd, &attr)) { _err_record_msg(gl->err, "tcgetattr error", END_ERR_MSG); return 1; }; attr.c_oflag |= OPOST; while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) { if(errno != EINTR) { _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG); return 1; }; }; /* * Invoke the application's callback function. */ switch(gfh->fn(gl, gfh->data, fd, event)) { default: case GLFD_ABORT: gl_record_status(gl, GLR_FDABORT, 0); waserr = 1; break; case GLFD_REFRESH: gl_queue_redisplay(gl); break; case GLFD_CONTINUE: break; }; /* * If the callback function called gl_normal_io(), restore raw mode, * and queue a redisplay of the input line. */ if(!gl->raw_mode) waserr = waserr || _gl_raw_io(gl, 1); /* * Disable conversion of newline characters to carriage-return/linefeed. */ attr.c_oflag &= ~(OPOST); while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) { if(errno != EINTR) { _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG); return 1; }; }; return waserr; } /*....................................................................... * This is a private function of gl_event_handler(), used to call a * inactivity timer callbacks. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int 0 - OK. * 1 - Error. */ static int gl_call_timeout_handler(GetLine *gl) { Termios attr; /* The terminal attributes */ int waserr = 0; /* True after any error */ /* * Make sure that there is an inactivity timeout callback. */ if(!gl->timer.fn) return 0; /* * Re-enable conversion of newline characters to carriage-return/linefeed, * so that the callback can write to the terminal without having to do * anything special. */ if(tcgetattr(gl->input_fd, &attr)) { _err_record_msg(gl->err, "tcgetattr error", END_ERR_MSG); return 1; }; attr.c_oflag |= OPOST; while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) { if(errno != EINTR) { _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG); return 1; }; }; /* * Invoke the application's callback function. */ switch(gl->timer.fn(gl, gl->timer.data)) { default: case GLTO_ABORT: gl_record_status(gl, GLR_TIMEOUT, 0); waserr = 1; break; case GLTO_REFRESH: gl_queue_redisplay(gl); break; case GLTO_CONTINUE: break; }; /* * If the callback function called gl_normal_io(), restore raw mode, * and queue a redisplay of the input line. */ if(!gl->raw_mode) waserr = waserr || _gl_raw_io(gl, 1); /* * Disable conversion of newline characters to carriage-return/linefeed. */ attr.c_oflag &= ~(OPOST); while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) { if(errno != EINTR) { _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG); return 1; }; }; return waserr; } #endif /* HAVE_SELECT */ /*....................................................................... * Switch history groups. History groups represent separate history * lists recorded within a single history buffer. Different groups * are distinguished by integer identifiers chosen by the calling * appplicaton. Initially new_GetLine() sets the group identifier to * 0. Whenever a new line is appended to the history list, the current * group identifier is recorded with it, and history lookups only * consider lines marked with the current group identifier. * * Input: * gl GetLine * The resource object of gl_get_line(). * id unsigned The new history group identifier. * Output: * return int 0 - OK. * 1 - Error. */ int gl_group_history(GetLine *gl, unsigned id) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of this function */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return 1; }; /* * Block all signals while we install the new configuration. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * If the group isn't being changed, do nothing. */ if(_glh_get_group(gl->glh) == id) { status = 0; /* * Establish the new group. */ } else if(_glh_set_group(gl->glh, id)) { _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); status = 1; /* * Prevent history information from the previous group being * inappropriately used by the next call to gl_get_line(). */ } else { gl->preload_history = 0; gl->last_search = -1; status = 0; }; /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * Display the contents of the history list. * * Input: * gl GetLine * The resource object of gl_get_line(). * fp FILE * The stdio output stream to write to. * fmt const char * A format string. This containing characters to be * written verbatim, plus any of the following * format directives: * %D - The date, formatted like 2001-11-20 * %T - The time of day, formatted like 23:59:59 * %N - The sequential entry number of the * line in the history buffer. * %G - The number of the history group that * the line belongs to. * %% - A literal % character. * %H - The history line itself. * Note that a '\n' newline character is not * appended by default. * all_groups int If true, display history lines from all * history groups. Otherwise only display * those of the current history group. * max_lines int If max_lines is < 0, all available lines * are displayed. Otherwise only the most * recent max_lines lines will be displayed. * Output: * return int 0 - OK. * 1 - Error. */ int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, int max_lines) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of this function */ /* * Check the arguments. */ if(!gl || !fp || !fmt) { if(gl) _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Display the specified history group(s) while signals are blocked. */ status = _glh_show_history(gl->glh, _io_write_stdio, fp, fmt, all_groups, max_lines) || fflush(fp)==EOF; if(!status) _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * Update if necessary, and return the current size of the terminal. * * Input: * gl GetLine * The resource object of gl_get_line(). * def_ncolumn int If the number of columns in the terminal * can't be determined, substitute this number. * def_nline int If the number of lines in the terminal can't * be determined, substitute this number. * Output: * return GlTerminalSize The current terminal size. */ GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline) { GlTerminalSize size; /* The object to be returned */ sigset_t oldset; /* The signals that were blocked on entry */ /* to this function */ /* * Block all signals while accessing gl. */ gl_mask_signals(gl, &oldset); /* * Lookup/configure the terminal size. */ _gl_terminal_size(gl, def_ncolumn, def_nline, &size); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); return size; } /*....................................................................... * This is the private body of the gl_terminal_size() function. It * assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static void _gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline, GlTerminalSize *size) { const char *env; /* The value of an environment variable */ int n; /* A number read from env[] */ /* * Set the number of lines and columns to non-sensical values so that * we know later if they have been set. */ gl->nline = 0; gl->ncolumn = 0; /* * Are we reading from a terminal? */ if(gl->is_term) { /* * Ask the terminal directly if possible. */ gl_query_size(gl, &gl->ncolumn, &gl->nline); /* * If gl_query_size() couldn't ask the terminal, it will have * left gl->nrow and gl->ncolumn unchanged. If these values haven't * been changed from their initial values of zero, we need to find * a different method to get the terminal size. * * If the number of lines isn't known yet, first see if the * LINES environment ariable exists and specifies a believable number. * If this doesn't work, look up the default size in the terminal * information database. */ if(gl->nline < 1) { if((env = getenv("LINES")) && (n=atoi(env)) > 0) gl->nline = n; #ifdef USE_TERMINFO else gl->nline = tigetnum((char *)"lines"); #elif defined(USE_TERMCAP) else gl->nline = tgetnum("li"); #endif }; /* * If the number of lines isn't known yet, first see if the COLUMNS * environment ariable exists and specifies a believable number. If * this doesn't work, look up the default size in the terminal * information database. */ if(gl->ncolumn < 1) { if((env = getenv("COLUMNS")) && (n=atoi(env)) > 0) gl->ncolumn = n; #ifdef USE_TERMINFO else gl->ncolumn = tigetnum((char *)"cols"); #elif defined(USE_TERMCAP) else gl->ncolumn = tgetnum("co"); #endif }; }; /* * If we still haven't been able to acquire reasonable values, substitute * the default values specified by the caller. */ if(gl->nline <= 0) gl->nline = def_nline; if(gl->ncolumn <= 0) gl->ncolumn = def_ncolumn; /* * Copy the new size into the return value. */ if(size) { size->nline = gl->nline; size->ncolumn = gl->ncolumn; }; return; } /*....................................................................... * Resize or delete the history buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * bufsize size_t The number of bytes in the history buffer, or 0 * to delete the buffer completely. * Output: * return int 0 - OK. * 1 - Insufficient memory (the previous buffer * will have been retained). No error message * will be displayed. */ int gl_resize_history(GetLine *gl, size_t bufsize) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of this function */ /* * Check the arguments. */ if(!gl) return 1; /* * Block all signals while modifying the contents of gl. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Perform the resize while signals are blocked. */ status = _glh_resize_history(gl->glh, bufsize); if(status) _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * Set an upper limit to the number of lines that can be recorded in the * history list, or remove a previously specified limit. * * Input: * gl GetLine * The resource object of gl_get_line(). * max_lines int The maximum number of lines to allow, or -1 to * cancel a previous limit and allow as many lines * as will fit in the current history buffer size. */ void gl_limit_history(GetLine *gl, int max_lines) { if(gl) { sigset_t oldset; /* The signals that were blocked on entry to this block */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Apply the limit while signals are blocked. */ _glh_limit_history(gl->glh, max_lines); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); }; } /*....................................................................... * Discard either all historical lines, or just those associated with the * current history group. * * Input: * gl GetLine * The resource object of gl_get_line(). * all_groups int If true, clear all of the history. If false, * clear only the stored lines associated with the * currently selected history group. */ void gl_clear_history(GetLine *gl, int all_groups) { if(gl) { sigset_t oldset; /* The signals that were blocked on entry to this block */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Clear the history buffer while signals are blocked. */ _glh_clear_history(gl->glh, all_groups); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); }; } /*....................................................................... * Temporarily enable or disable the gl_get_line() history mechanism. * * Input: * gl GetLine * The resource object of gl_get_line(). * enable int If true, turn on the history mechanism. If * false, disable it. */ void gl_toggle_history(GetLine *gl, int enable) { if(gl) { sigset_t oldset; /* The signals that were blocked on entry to this block */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Change the history recording mode while signals are blocked. */ _glh_toggle_history(gl->glh, enable); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); }; } /*....................................................................... * Lookup a history line by its sequential number of entry in the * history buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * id unsigned long The identification number of the line to * be returned, where 0 denotes the first line * that was entered in the history list, and * each subsequently added line has a number * one greater than the previous one. For * the range of lines currently in the list, * see the gl_range_of_history() function. * Input/Output: * line GlHistoryLine * A pointer to the variable in which to * return the details of the line. * Output: * return int 0 - The line is no longer in the history * list, and *line has not been changed. * 1 - The requested line can be found in * *line. Note that line->line is part * of the history buffer, so a * private copy should be made if you * wish to use it after subsequent calls * to any functions that take *gl as an * argument. */ int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *line) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of this function */ /* * Check the arguments. */ if(!gl) return 0; /* * Block all signals while modifying the contents of gl. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Perform the lookup while signals are blocked. */ status = _glh_lookup_history(gl->glh, (GlhLineID) id, &line->line, &line->group, &line->timestamp); if(status) _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * Query the state of the history list. Note that any of the input/output * pointers can be specified as NULL. * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * state GlHistoryState * A pointer to the variable in which to record * the return values. */ void gl_state_of_history(GetLine *gl, GlHistoryState *state) { if(gl && state) { sigset_t oldset; /* The signals that were blocked on entry to this block */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Lookup the status while signals are blocked. */ _glh_state_of_history(gl->glh, &state->enabled, &state->group, &state->max_lines); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); }; } /*....................................................................... * Query the number and range of lines in the history buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * range GlHistoryRange * A pointer to the variable in which to record * the return values. If range->nline=0, the * range of lines will be given as 0-0. */ void gl_range_of_history(GetLine *gl, GlHistoryRange *range) { if(gl && range) { sigset_t oldset; /* The signals that were blocked on entry to this block */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Lookup the information while signals are blocked. */ _glh_range_of_history(gl->glh, &range->oldest, &range->newest, &range->nlines); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); }; } /*....................................................................... * Return the size of the history buffer and the amount of the * buffer that is currently in use. * * Input: * gl GetLine * The gl_get_line() resource object. * Input/Output: * GlHistorySize size * A pointer to the variable in which to return * the results. */ void gl_size_of_history(GetLine *gl, GlHistorySize *size) { if(gl && size) { sigset_t oldset; /* The signals that were blocked on entry to this block */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Lookup the information while signals are blocked. */ _glh_size_of_history(gl->glh, &size->size, &size->used); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); }; } /*....................................................................... * This is the action function that lists the contents of the history * list. */ static KT_KEY_FN(gl_list_history) { /* * Start a new line. */ if(gl_start_newline(gl, 1)) return 1; /* * List history lines that belong to the current group. */ _glh_show_history(gl->glh, gl_write_fn, gl, "%N %T %H\r\n", 0, count<=1 ? -1 : count); /* * Arrange for the input line to be redisplayed. */ gl_queue_redisplay(gl); return 0; } /*....................................................................... * Specify whether text that users type should be displayed or hidden. * In the latter case, only the prompt is displayed, and the final * input line is not archived in the history list. * * Input: * gl GetLine * The gl_get_line() resource object. * enable int 0 - Disable echoing. * 1 - Enable echoing. * -1 - Just query the mode without changing it. * Output: * return int The echoing disposition that was in effect * before this function was called: * 0 - Echoing was disabled. * 1 - Echoing was enabled. */ int gl_echo_mode(GetLine *gl, int enable) { if(gl) { sigset_t oldset; /* The signals that were blocked on entry to this block */ int was_echoing; /* The echoing disposition on entry to this function */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Install the new disposition while signals are blocked. */ was_echoing = gl->echo; if(enable >= 0) gl->echo = enable; /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); /* * Return the original echoing disposition. */ return was_echoing; }; return 1; } /*....................................................................... * Display the prompt. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int 0 - OK. * 1 - Error. */ static int gl_display_prompt(GetLine *gl) { const char *pptr; /* A pointer into gl->prompt[] */ unsigned old_attr=0; /* The current text display attributes */ unsigned new_attr=0; /* The requested text display attributes */ /* * Temporarily switch to echoing output characters. */ int kept_echo = gl->echo; gl->echo = 1; /* * In case the screen got messed up, send a carriage return to * put the cursor at the beginning of the current terminal line. */ if(gl_print_control_sequence(gl, 1, gl->bol)) return 1; /* * Mark the line as partially displayed. */ gl->displayed = 1; /* * Write the prompt, using the currently selected prompt style. */ switch(gl->prompt_style) { case GL_LITERAL_PROMPT: if(gl_print_string(gl, gl->prompt, '\0')) return 1; break; case GL_FORMAT_PROMPT: for(pptr=gl->prompt; *pptr; pptr++) { /* * Does the latest character appear to be the start of a directive? */ if(*pptr == '%') { /* * Check for and act on attribute changing directives. */ switch(pptr[1]) { /* * Add or remove a text attribute from the new set of attributes. */ case 'B': case 'U': case 'S': case 'P': case 'F': case 'V': case 'b': case 'u': case 's': case 'p': case 'f': case 'v': switch(*++pptr) { case 'B': /* Switch to a bold font */ new_attr |= GL_TXT_BOLD; break; case 'b': /* Switch to a non-bold font */ new_attr &= ~GL_TXT_BOLD; break; case 'U': /* Start underlining */ new_attr |= GL_TXT_UNDERLINE; break; case 'u': /* Stop underlining */ new_attr &= ~GL_TXT_UNDERLINE; break; case 'S': /* Start highlighting */ new_attr |= GL_TXT_STANDOUT; break; case 's': /* Stop highlighting */ new_attr &= ~GL_TXT_STANDOUT; break; case 'P': /* Switch to a pale font */ new_attr |= GL_TXT_DIM; break; case 'p': /* Switch to a non-pale font */ new_attr &= ~GL_TXT_DIM; break; case 'F': /* Switch to a flashing font */ new_attr |= GL_TXT_BLINK; break; case 'f': /* Switch to a steady font */ new_attr &= ~GL_TXT_BLINK; break; case 'V': /* Switch to reverse video */ new_attr |= GL_TXT_REVERSE; break; case 'v': /* Switch out of reverse video */ new_attr &= ~GL_TXT_REVERSE; break; }; continue; /* * A literal % is represented by %%. Skip the leading %. */ case '%': pptr++; break; }; }; /* * Many terminals, when asked to turn off a single text attribute, turn * them all off, so the portable way to turn one off individually is to * explicitly turn them all off, then specify those that we want from * scratch. */ if(old_attr & ~new_attr) { if(gl_print_control_sequence(gl, 1, gl->text_attr_off)) return 1; old_attr = 0; }; /* * Install new text attributes? */ if(new_attr != old_attr) { if(new_attr & GL_TXT_BOLD && !(old_attr & GL_TXT_BOLD) && gl_print_control_sequence(gl, 1, gl->bold)) return 1; if(new_attr & GL_TXT_UNDERLINE && !(old_attr & GL_TXT_UNDERLINE) && gl_print_control_sequence(gl, 1, gl->underline)) return 1; if(new_attr & GL_TXT_STANDOUT && !(old_attr & GL_TXT_STANDOUT) && gl_print_control_sequence(gl, 1, gl->standout)) return 1; if(new_attr & GL_TXT_DIM && !(old_attr & GL_TXT_DIM) && gl_print_control_sequence(gl, 1, gl->dim)) return 1; if(new_attr & GL_TXT_REVERSE && !(old_attr & GL_TXT_REVERSE) && gl_print_control_sequence(gl, 1, gl->reverse)) return 1; if(new_attr & GL_TXT_BLINK && !(old_attr & GL_TXT_BLINK) && gl_print_control_sequence(gl, 1, gl->blink)) return 1; old_attr = new_attr; }; /* * Display the latest character. */ if(gl_print_char(gl, *pptr, pptr[1])) return 1; }; /* * Turn off all text attributes now that we have finished drawing * the prompt. */ if(gl_print_control_sequence(gl, 1, gl->text_attr_off)) return 1; break; }; /* * Restore the original echo mode. */ gl->echo = kept_echo; /* * The prompt has now been displayed at least once. */ gl->prompt_changed = 0; return 0; } /*....................................................................... * This function can be called from gl_get_line() callbacks to have * the prompt changed when they return. It has no effect if gl_get_line() * is not currently being invoked. * * Input: * gl GetLine * The resource object of gl_get_line(). * prompt const char * The new prompt. */ void gl_replace_prompt(GetLine *gl, const char *prompt) { if(gl) { sigset_t oldset; /* The signals that were blocked on entry to this block */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Replace the prompt. */ _gl_replace_prompt(gl, prompt); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); }; } /*....................................................................... * This is the private body of the gl_replace_prompt() function. It * assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static void _gl_replace_prompt(GetLine *gl, const char *prompt) { /* * Substitute an empty prompt? */ if(!prompt) prompt = ""; /* * Gaurd against aliasing between prompt and gl->prompt. */ if(gl->prompt != prompt) { /* * Get the length of the new prompt string. */ size_t slen = strlen(prompt); /* * If needed, allocate a new buffer for the prompt string. */ if(!gl->prompt || slen > strlen(gl->prompt)) { size_t size = sizeof(char) * (slen + 1); char *new_prompt = gl->prompt ? realloc(gl->prompt, size) : malloc(size); if(!new_prompt) return; gl->prompt = new_prompt; }; /* * Make a copy of the new prompt. */ strcpy(gl->prompt, prompt); }; /* * Record the statistics of the new prompt. */ gl->prompt_len = gl_displayed_prompt_width(gl); gl->prompt_changed = 1; gl_queue_redisplay(gl); return; } /*....................................................................... * Work out the length of the current prompt on the terminal, according * to the current prompt formatting style. * * Input: * gl GetLine * The resource object of this library. * Output: * return int The number of displayed characters. */ static int gl_displayed_prompt_width(GetLine *gl) { int slen=0; /* The displayed number of characters */ const char *pptr; /* A pointer into prompt[] */ /* * The length differs according to the prompt display style. */ switch(gl->prompt_style) { case GL_LITERAL_PROMPT: return gl_displayed_string_width(gl, gl->prompt, -1, 0); break; case GL_FORMAT_PROMPT: /* * Add up the length of the displayed string, while filtering out * attribute directives. */ for(pptr=gl->prompt; *pptr; pptr++) { /* * Does the latest character appear to be the start of a directive? */ if(*pptr == '%') { /* * Check for and skip attribute changing directives. */ switch(pptr[1]) { case 'B': case 'b': case 'U': case 'u': case 'S': case 's': pptr++; continue; /* * A literal % is represented by %%. Skip the leading %. */ case '%': pptr++; break; }; }; slen += gl_displayed_char_width(gl, *pptr, slen); }; break; }; return slen; } /*....................................................................... * Specify whether to heed text attribute directives within prompt * strings. * * Input: * gl GetLine * The resource object of gl_get_line(). * style GlPromptStyle The style of prompt (see the definition of * GlPromptStyle in libtecla.h for details). */ void gl_prompt_style(GetLine *gl, GlPromptStyle style) { if(gl) { sigset_t oldset; /* The signals that were blocked on entry to this block */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Install the new style in gl while signals are blocked. */ if(style != gl->prompt_style) { gl->prompt_style = style; gl->prompt_len = gl_displayed_prompt_width(gl); gl->prompt_changed = 1; gl_queue_redisplay(gl); }; /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); }; } /*....................................................................... * Tell gl_get_line() how to respond to a given signal. This can be used * both to override the default responses to signals that gl_get_line() * normally catches and to add new signals to the list that are to be * caught. * * Input: * gl GetLine * The resource object of gl_get_line(). * signo int The number of the signal to be caught. * flags unsigned A bitwise union of GlSignalFlags enumerators. * after GlAfterSignal What to do after the application's signal * handler has been called. * errno_value int The value to set errno to. * Output: * return int 0 - OK. * 1 - Error. */ int gl_trap_signal(GetLine *gl, int signo, unsigned flags, GlAfterSignal after, int errno_value) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of this function */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return 1; }; /* * Block all signals while modifying the contents of gl. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Perform the modification while signals are blocked. */ status = _gl_trap_signal(gl, signo, flags, after, errno_value); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the gl_trap_signal() function. It * assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static int _gl_trap_signal(GetLine *gl, int signo, unsigned flags, GlAfterSignal after, int errno_value) { GlSignalNode *sig; /* * Complain if an attempt is made to trap untrappable signals. * These would otherwise cause errors later in gl_mask_signals(). */ if(0 #ifdef SIGKILL || signo==SIGKILL #endif #ifdef SIGBLOCK || signo==SIGBLOCK #endif ) { return 1; }; /* * See if the signal has already been registered. */ for(sig=gl->sigs; sig && sig->signo != signo; sig = sig->next) ; /* * If the signal hasn't already been registered, allocate a node for * it. */ if(!sig) { sig = (GlSignalNode *) _new_FreeListNode(gl->sig_mem); if(!sig) return 1; /* * Add the new node to the head of the list. */ sig->next = gl->sigs; gl->sigs = sig; /* * Record the signal number. */ sig->signo = signo; /* * Create a signal set that includes just this signal. */ sigemptyset(&sig->proc_mask); if(sigaddset(&sig->proc_mask, signo) == -1) { _err_record_msg(gl->err, "sigaddset error", END_ERR_MSG); sig = (GlSignalNode *) _del_FreeListNode(gl->sig_mem, sig); return 1; }; /* * Add the signal to the bit-mask of signals being trapped. */ sigaddset(&gl->all_signal_set, signo); }; /* * Record the new signal attributes. */ sig->flags = flags; sig->after = after; sig->errno_value = errno_value; return 0; } /*....................................................................... * Remove a signal from the list of signals that gl_get_line() traps. * * Input: * gl GetLine * The resource object of gl_get_line(). * signo int The number of the signal to be ignored. * Output: * return int 0 - OK. * 1 - Error. */ int gl_ignore_signal(GetLine *gl, int signo) { GlSignalNode *sig; /* The gl->sigs list node of the specified signal */ GlSignalNode *prev; /* The node that precedes sig in the list */ sigset_t oldset; /* The signals that were blocked on entry to this */ /* function. */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return 1; }; /* * Block all signals while modifying the contents of gl. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Find the node of the gl->sigs list which records the disposition * of the specified signal. */ for(prev=NULL,sig=gl->sigs; sig && sig->signo != signo; prev=sig,sig=sig->next) ; if(sig) { /* * Remove the node from the list. */ if(prev) prev->next = sig->next; else gl->sigs = sig->next; /* * Return the node to the freelist. */ sig = (GlSignalNode *) _del_FreeListNode(gl->sig_mem, sig); /* * Remove the signal from the bit-mask union of signals being trapped. */ sigdelset(&gl->all_signal_set, signo); }; /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); return 0; } /*....................................................................... * This function is called when an input line has been completed. It * appends the specified newline character, terminates the line, * records the line in the history buffer if appropriate, and positions * the terminal cursor at the start of the next line. * * Input: * gl GetLine * The resource object of gl_get_line(). * newline_char int The newline character to add to the end * of the line. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_line_ended(GetLine *gl, int newline_char) { /* * If the newline character is printable, display it at the end of * the line, and add it to the input line buffer. */ if(isprint((int)(unsigned char) newline_char)) { if(gl_end_of_line(gl, 1, NULL) || gl_add_char_to_line(gl, newline_char)) return 1; } else { /* * Otherwise just append a newline character to the input line buffer. */ newline_char = '\n'; gl_buffer_char(gl, newline_char, gl->ntotal); }; /* * Add the line to the history buffer if it was entered with a * newline character. */ if(gl->echo && gl->automatic_history && newline_char=='\n') (void) _gl_append_history(gl, gl->line); /* * Except when depending on the system-provided line editing, start a new * line after the end of the line that has just been entered. */ if(gl->editor != GL_NO_EDITOR && gl_start_newline(gl, 1)) return 1; /* * Record the successful return status. */ gl_record_status(gl, GLR_NEWLINE, 0); /* * Attempt to flush any pending output. */ (void) gl_flush_output(gl); /* * The next call to gl_get_line() will write the prompt for a new line * (or continue the above flush if incomplete), so if we manage to * flush the terminal now, report that we are waiting to write to the * terminal. */ gl->pending_io = GLP_WRITE; return 0; } /*....................................................................... * Return the last signal that was caught by the most recent call to * gl_get_line(), or -1 if no signals were caught. This is useful if * gl_get_line() returns errno=EINTR and you need to find out what signal * caused it to abort. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int The last signal caught by the most recent * call to gl_get_line(), or -1 if no signals * were caught. */ int gl_last_signal(GetLine *gl) { int signo = -1; /* The requested signal number */ if(gl) { sigset_t oldset; /* The signals that were blocked on entry to this block */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Access gl now that signals are blocked. */ signo = gl->last_signal; /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); }; return signo; } /*....................................................................... * Prepare to edit a new line. * * Input: * gl GetLine * The resource object of this library. * prompt char * The prompt to prefix the line with, or NULL to * use the same prompt that was used by the previous * line. * start_line char * The initial contents of the input line, or NULL * if it should start out empty. * start_pos int If start_line isn't NULL, this specifies the * index of the character over which the cursor * should initially be positioned within the line. * If you just want it to follow the last character * of the line, send -1. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_present_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos) { /* * Prepare the line-editing properties for a new editing session. */ gl_reset_editor(gl); /* * Record the new prompt and its displayed width. */ if(prompt) _gl_replace_prompt(gl, prompt); /* * Reset the history search pointers. */ if(_glh_cancel_search(gl->glh)) { _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); return 1; }; /* * If the previous line was entered via the repeat-history action, * preload the specified history line. */ if(gl->preload_history) { gl->preload_history = 0; if(_glh_recall_line(gl->glh, gl->preload_id, gl->line, gl->linelen+1)) { gl_update_buffer(gl); /* Compute gl->ntotal etc.. */ gl->buff_curpos = gl->ntotal; } else { gl_truncate_buffer(gl, 0); }; gl->preload_id = 0; /* * Present a specified initial line? */ } else if(start_line) { char *cptr; /* A pointer into gl->line[] */ /* * Measure the length of the starting line. */ int start_len = strlen(start_line); /* * If the length of the line is greater than the available space, * truncate it. */ if(start_len > gl->linelen) start_len = gl->linelen; /* * Load the line into the buffer. */ if(start_line != gl->line) { /*** START ABB: 2009-06-18 ***/ gl_truncate_buffer(gl, 0); /*** START ABB: 2009-06-18 ***/ gl_buffer_string(gl, start_line, start_len, 0); } /* * Strip off any trailing newline and carriage return characters. */ for(cptr=gl->line + gl->ntotal - 1; cptr >= gl->line && (*cptr=='\n' || *cptr=='\r'); cptr--,gl->ntotal--) ; gl_truncate_buffer(gl, gl->ntotal < 0 ? 0 : gl->ntotal); /*** ABB: gl->ntotal was still set to the last time *** gl->line was set. The previous loop should *** have started at the end of start_line *** and this length was not recorded ***/ /* * Where should the cursor be placed within the line? */ if(start_pos < 0 || start_pos > gl->ntotal) { if(gl_place_cursor(gl, gl->ntotal)) return 1; } else { if(gl_place_cursor(gl, start_pos)) return 1; }; /* * Clear the input line? */ } else { gl_truncate_buffer(gl, 0); }; /* * Arrange for the line to be displayed by gl_flush_output(). */ gl_queue_redisplay(gl); /* * Update the display. */ return gl_flush_output(gl); } /*....................................................................... * Reset all line-editing parameters for a new editing session. Note * that this does not empty the input line, since that would prevent a * gl_get_line() caller from specifying the returned line buffer as * the start_line argument of the next call to gl_get_line(). * * Input: * gl GetLine * The line editor resource object. */ static void gl_reset_editor(GetLine *gl) { /* * Warning: Don't clear gl->line[] and gl->ntotal here (see above). */ gl->buff_curpos = 0; gl->term_curpos = 0; gl->term_len = 0; gl->insert_curpos = 0; gl->number = -1; gl->displayed = 0; gl->endline = 0; gl->redisplay = 0; gl->postpone = 0; gl->nbuf = 0; gl->nread = 0; gl->vi.command = 0; gl->vi.undo.line[0] = '\0'; gl->vi.undo.ntotal = 0; gl->vi.undo.buff_curpos = 0; gl->vi.repeat.action.fn = 0; gl->vi.repeat.action.data = 0; gl->last_signal = -1; } /*....................................................................... * Print an informational message to the terminal, after starting a new * line. * * Input: * gl GetLine * The line editor resource object. * ... const char * Zero or more strings to be printed. * ... void * The last argument must always be GL_END_INFO. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_print_info(GetLine *gl, ...) { va_list ap; /* The variable argument list */ const char *s; /* The string being printed */ int waserr = 0; /* True after an error */ /* * Only display output when echoing is on. */ if(gl->echo) { /* * Skip to the start of the next empty line before displaying the message. */ if(gl_start_newline(gl, 1)) return 1; /* * Display the list of provided messages. */ va_start(ap, gl); while(!waserr && (s = va_arg(ap, const char *)) != GL_END_INFO) waserr = gl_print_raw_string(gl, 1, s, -1); va_end(ap); /* * Start a newline. */ waserr = waserr || gl_print_raw_string(gl, 1, "\n\r", -1); /* * Arrange for the input line to be redrawn. */ gl_queue_redisplay(gl); }; return waserr; } /*....................................................................... * Go to the start of the next empty line, ready to output miscellaneous * text to the screen. * * Note that when async-signal safety is required, the 'buffered' * argument must be 0. * * Input: * gl GetLine * The line editor resource object. * buffered int If true, used buffered I/O when writing to * the terminal. Otherwise use async-signal-safe * unbuffered I/O. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_start_newline(GetLine *gl, int buffered) { int waserr = 0; /* True after any I/O error */ /* * Move the cursor to the start of the terminal line that follows the * last line of the partially enterred line. In order that this * function remain async-signal safe when write_fn is signal safe, we * can't call our normal output functions, since they call tputs(), * who's signal saftey isn't defined. Fortunately, we can simply use * \r and \n to move the cursor to the right place. */ if(gl->displayed) { /* Is an input line currently displayed? */ /* * On which terminal lines are the cursor and the last character of the * input line? */ int curs_line = gl->term_curpos / gl->ncolumn; int last_line = gl->term_len / gl->ncolumn; /* * Move the cursor to the start of the line that follows the last * terminal line that is occupied by the input line. */ for( ; curs_line < last_line + 1; curs_line++) waserr = waserr || gl_print_raw_string(gl, buffered, "\n", 1); waserr = waserr || gl_print_raw_string(gl, buffered, "\r", 1); /* * Mark the line as no longer displayed. */ gl_line_erased(gl); }; return waserr; } /*....................................................................... * The callback through which all terminal output is routed. * This simply appends characters to a queue buffer, which is * subsequently flushed to the output channel by gl_flush_output(). * * Input: * data void * The pointer to a GetLine line editor resource object * cast to (void *). * s const char * The string to be written. * n int The number of characters to write from s[]. * Output: * return int The number of characters written. This will always * be equal to 'n' unless an error occurs. */ static GL_WRITE_FN(gl_write_fn) { GetLine *gl = (GetLine *) data; int ndone = _glq_append_chars(gl->cq, s, n, gl->flush_fn, gl); if(ndone != n) _err_record_msg(gl->err, _glq_last_error(gl->cq), END_ERR_MSG); return ndone; } /*....................................................................... * Ask gl_get_line() what caused it to return. * * Input: * gl GetLine * The line editor resource object. * Output: * return GlReturnStatus The return status of the last call to * gl_get_line(). */ GlReturnStatus gl_return_status(GetLine *gl) { GlReturnStatus rtn_status = GLR_ERROR; /* The requested status */ if(gl) { sigset_t oldset; /* The signals that were blocked on entry to this block */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Access gl while signals are blocked. */ rtn_status = gl->rtn_status; /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); }; return rtn_status; } /*....................................................................... * In non-blocking server-I/O mode, this function should be called * from the application's external event loop to see what type of * terminal I/O is being waited for by gl_get_line(), and thus what * direction of I/O to wait for with select() or poll(). * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return GlPendingIO The type of pending I/O being waited for. */ GlPendingIO gl_pending_io(GetLine *gl) { GlPendingIO pending_io = GLP_WRITE; /* The requested information */ if(gl) { sigset_t oldset; /* The signals that were blocked on entry to this block */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Access gl while signals are blocked. */ pending_io = gl->pending_io; /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); }; return pending_io; } /*....................................................................... * In server mode, this function configures the terminal for non-blocking * raw terminal I/O. In normal I/O mode it does nothing. * * Callers of this function must be careful to trap all signals that * terminate or suspend the program, and call gl_normal_io() * from the corresponding signal handlers in order to restore the * terminal to its original settings before the program is terminated * or suspended. They should also trap the SIGCONT signal to detect * when the program resumes, and ensure that its signal handler * call gl_raw_io() to redisplay the line and resume editing. * * This function is async signal safe. * * Input: * gl GetLine * The line editor resource object. * Output: * return int 0 - OK. * 1 - Error. */ int gl_raw_io(GetLine *gl) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of _gl_raw_io() */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Don't allow applications to switch into raw mode unless in server mode. */ if(gl->io_mode != GL_SERVER_MODE) { _err_record_msg(gl->err, "Can't switch to raw I/O unless in server mode", END_ERR_MSG); errno = EPERM; status = 1; } else { /* * Execute the private body of the function while signals are blocked. */ status = _gl_raw_io(gl, 1); }; /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the public function, gl_raw_io(). * It assumes that the caller has checked its arguments and blocked the * delivery of signals. * * This function is async signal safe. */ static int _gl_raw_io(GetLine *gl, int redisplay) { /* * If we are already in the correct mode, do nothing. */ if(gl->raw_mode) return 0; /* * Switch the terminal to raw mode. */ if(gl->is_term && gl_raw_terminal_mode(gl)) return 1; /* * Switch to non-blocking I/O mode? */ if(gl->io_mode==GL_SERVER_MODE && (gl_nonblocking_io(gl, gl->input_fd) || gl_nonblocking_io(gl, gl->output_fd) || (gl->file_fp && gl_nonblocking_io(gl, fileno(gl->file_fp))))) { if(gl->is_term) gl_restore_terminal_attributes(gl); return 1; }; /* * If an input line is being entered, arrange for it to be * displayed. */ if(redisplay) { gl->postpone = 0; gl_queue_redisplay(gl); }; return 0; } /*....................................................................... * Restore the terminal to the state that it had when * gl_raw_io() was last called. After calling * gl_raw_io(), this function must be called before * terminating or suspending the program, and before attempting other * uses of the terminal from within the program. See gl_raw_io() * for more details. * * Input: * gl GetLine * The line editor resource object. * Output: * return int 0 - OK. * 1 - Error. */ int gl_normal_io(GetLine *gl) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of _gl_normal_io() */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Execute the private body of the function while signals are blocked. */ status = _gl_normal_io(gl); /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the public function, gl_normal_io(). * It assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static int _gl_normal_io(GetLine *gl) { /* * If we are already in normal mode, do nothing. */ if(!gl->raw_mode) return 0; /* * Postpone subsequent redisplays until after _gl_raw_io(gl, 1) * is next called. */ gl->postpone = 1; /* * Switch back to blocking I/O. Note that this is essential to do * here, because when using non-blocking I/O, the terminal output * buffering code can't always make room for new output without calling * malloc(), and a call to malloc() would mean that this function * couldn't safely be called from signal handlers. */ if(gl->io_mode==GL_SERVER_MODE && (gl_blocking_io(gl, gl->input_fd) || gl_blocking_io(gl, gl->output_fd) || (gl->file_fp && gl_blocking_io(gl, fileno(gl->file_fp))))) return 1; /* * Move the cursor to the next empty terminal line. Note that * unbuffered I/O is requested, to ensure that gl_start_newline() be * async-signal-safe. */ if(gl->is_term && gl_start_newline(gl, 0)) return 1; /* * Switch the terminal to normal mode. */ if(gl->is_term && gl_restore_terminal_attributes(gl)) { /* * On error, revert to non-blocking I/O if needed, so that on failure * we remain in raw mode. */ if(gl->io_mode==GL_SERVER_MODE) { gl_nonblocking_io(gl, gl->input_fd); gl_nonblocking_io(gl, gl->output_fd); if(gl->file_fp) gl_nonblocking_io(gl, fileno(gl->file_fp)); }; return 1; }; return 0; } /*....................................................................... * This function allows you to install an additional completion * action, or to change the completion function of an existing * one. This should be called before the first call to gl_get_line() * so that the name of the action be defined before the user's * configuration file is read. * * Input: * gl GetLine * The resource object of the command-line input * module. * data void * This is passed to match_fn() whenever it is * called. It could, for example, point to a * symbol table that match_fn() would look up * matches in. * match_fn CplMatchFn * The function that will identify the prefix * to be completed from the input line, and * report matching symbols. * list_only int If non-zero, install an action that only lists * possible completions, rather than attempting * to perform the completion. * name const char * The name with which users can refer to the * binding in tecla configuration files. * keyseq const char * Either NULL, or a key sequence with which * to invoke the binding. This should be * specified in the same manner as key-sequences * in tecla configuration files (eg. "M-^I"). * Output: * return int 0 - OK. * 1 - Error. */ int gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn, int list_only, const char *name, const char *keyseq) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of _gl_completion_action() */ /* * Check the arguments. */ if(!gl || !name || !match_fn) { errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Install the new action while signals are blocked. */ status = _gl_completion_action(gl, data, match_fn, list_only, name, keyseq); /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the public function, gl_completion_action(). * It assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static int _gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn, int list_only, const char *name, const char *keyseq) { KtKeyFn *current_fn; /* An existing action function */ void *current_data; /* The action-function callback data */ /* * Which action function is desired? */ KtKeyFn *action_fn = list_only ? gl_list_completions : gl_complete_word; /* * Is there already an action of the specified name? */ if(_kt_lookup_action(gl->bindings, name, ¤t_fn, ¤t_data) == 0) { /* * If the action has the same type as the one being requested, * simply change the contents of its GlCplCallback callback data. */ if(current_fn == action_fn) { GlCplCallback *cb = (GlCplCallback *) current_data; cb->fn = match_fn; cb->data = data; } else { errno = EINVAL; _err_record_msg(gl->err, "Illegal attempt to change the type of an existing completion action", END_ERR_MSG); return 1; }; /* * No existing action has the specified name. */ } else { /* * Allocate a new GlCplCallback callback object. */ GlCplCallback *cb = (GlCplCallback *) _new_FreeListNode(gl->cpl_mem); if(!cb) { errno = ENOMEM; _err_record_msg(gl->err, "Insufficient memory to add completion action", END_ERR_MSG); return 1; }; /* * Record the completion callback data. */ cb->fn = match_fn; cb->data = data; /* * Attempt to register the new action. */ if(_kt_set_action(gl->bindings, name, action_fn, cb)) { _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); _del_FreeListNode(gl->cpl_mem, (void *) cb); return 1; }; }; /* * Bind the action to a given key-sequence? */ if(keyseq && _kt_set_keybinding(gl->bindings, KTB_NORM, keyseq, name)) { _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); return 1; }; return 0; } /*....................................................................... * Register an application-provided function as an action function. * This should preferably be called before the first call to gl_get_line() * so that the name of the action becomes defined before the user's * configuration file is read. * * Input: * gl GetLine * The resource object of the command-line input * module. * data void * Arbitrary application-specific callback * data to be passed to the callback * function, fn(). * fn GlActionFn * The application-specific function that * implements the action. This will be invoked * whenever the user presses any * key-sequence which is bound to this action. * name const char * The name with which users can refer to the * binding in tecla configuration files. * keyseq const char * The key sequence with which to invoke * the binding. This should be specified in the * same manner as key-sequences in tecla * configuration files (eg. "M-^I"). * Output: * return int 0 - OK. * 1 - Error. */ int gl_register_action(GetLine *gl, void *data, GlActionFn *fn, const char *name, const char *keyseq) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of _gl_register_action() */ /* * Check the arguments. */ if(!gl || !name || !fn) { errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Install the new action while signals are blocked. */ status = _gl_register_action(gl, data, fn, name, keyseq); /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the public function, gl_register_action(). * It assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static int _gl_register_action(GetLine *gl, void *data, GlActionFn *fn, const char *name, const char *keyseq) { KtKeyFn *current_fn; /* An existing action function */ void *current_data; /* The action-function callback data */ /* * Get the action function which actually runs the application-provided * function. */ KtKeyFn *action_fn = gl_run_external_action; /* * Is there already an action of the specified name? */ if(_kt_lookup_action(gl->bindings, name, ¤t_fn, ¤t_data) == 0) { /* * If the action has the same type as the one being requested, * simply change the contents of its GlCplCallback callback data. */ if(current_fn == action_fn) { GlExternalAction *a = (GlExternalAction *) current_data; a->fn = fn; a->data = data; } else { errno = EINVAL; _err_record_msg(gl->err, "Illegal attempt to change the type of an existing action", END_ERR_MSG); return 1; }; /* * No existing action has the specified name. */ } else { /* * Allocate a new GlCplCallback callback object. */ GlExternalAction *a = (GlExternalAction *) _new_FreeListNode(gl->ext_act_mem); if(!a) { errno = ENOMEM; _err_record_msg(gl->err, "Insufficient memory to add completion action", END_ERR_MSG); return 1; }; /* * Record the completion callback data. */ a->fn = fn; a->data = data; /* * Attempt to register the new action. */ if(_kt_set_action(gl->bindings, name, action_fn, a)) { _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); _del_FreeListNode(gl->cpl_mem, (void *) a); return 1; }; }; /* * Bind the action to a given key-sequence? */ if(keyseq && _kt_set_keybinding(gl->bindings, KTB_NORM, keyseq, name)) { _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); return 1; }; return 0; } /*....................................................................... * Invoke an action function previously registered by a call to * gl_register_action(). */ static KT_KEY_FN(gl_run_external_action) { GlAfterAction status; /* The return value of the action function */ /* * Get the container of the action function and associated callback data. */ GlExternalAction *a = (GlExternalAction *) data; /* * Invoke the action function. */ status = a->fn(gl, a->data, count, gl->buff_curpos, gl->line); /* * If the callback took us out of raw (possibly non-blocking) input * mode, restore this mode, and queue a redisplay of the input line. */ if(_gl_raw_io(gl, 1)) return 1; /* * Finally, check to see what the action function wants us to do next. */ switch(status) { default: case GLA_ABORT: gl_record_status(gl, GLR_ERROR, errno); return 1; break; case GLA_RETURN: return gl_newline(gl, 1, NULL); break; case GLA_CONTINUE: break; }; return 0; } /*....................................................................... * In server-I/O mode the terminal is left in raw mode between calls * to gl_get_line(), so it is necessary for the application to install * terminal restoring signal handlers for signals that could terminate * or suspend the process, plus a terminal reconfiguration handler to * be called when a process resumption signal is received, and finally * a handler to be called when a terminal-resize signal is received. * * Since there are many signals that by default terminate or suspend * processes, and different systems support different sub-sets of * these signals, this function provides a convenient wrapper around * sigaction() for assigning the specified handlers to all appropriate * signals. It also arranges that when any one of these signals is * being handled, all other catchable signals are blocked. This is * necessary so that the specified signal handlers can safely call * gl_raw_io(), gl_normal_io() and gl_update_size() without * reentrancy issues. * * Input: * term_handler void (*)(int) The signal handler to invoke when * a process-terminating signal is * received. * susp_handler void (*)(int) The signal handler to invoke when * a process-suspending signal is * received. * cont_handler void (*)(int) The signal handler to invoke when * a process-resumption signal is * received (ie. SIGCONT). * size_handler void (*)(int) The signal handler to invoke when * a terminal-resize signal (ie. SIGWINCH) * is received. * Output: * return int 0 - OK. * 1 - Error. */ int gl_tty_signals(void (*term_handler)(int), void (*susp_handler)(int), void (*cont_handler)(int), void (*size_handler)(int)) { int i; /* * Search for signals of the specified classes, and assign the * associated signal handler to them. */ for(i=0; iattr & GLSA_SUSP) { if(gl_set_tty_signal(sig->signo, susp_handler)) return 1; } else if(sig->attr & GLSA_TERM) { if(gl_set_tty_signal(sig->signo, term_handler)) return 1; } else if(sig->attr & GLSA_CONT) { if(gl_set_tty_signal(sig->signo, cont_handler)) return 1; } else if(sig->attr & GLSA_SIZE) { if(gl_set_tty_signal(sig->signo, size_handler)) return 1; }; }; return 0; } /*....................................................................... * This is a private function of gl_tty_signals(). It installs a given * signal handler, and arranges that when that signal handler is being * invoked other signals are blocked. The latter is important to allow * functions like gl_normal_io(), gl_raw_io() and gl_update_size() * to be called from signal handlers. * * Input: * signo int The signal to be trapped. * handler void (*)(int) The signal handler to assign to the signal. */ static int gl_set_tty_signal(int signo, void (*handler)(int)) { SigAction act; /* The signal handler configuation */ /* * Arrange to block all trappable signals except the one that is being * assigned (the trapped signal will be blocked automatically by the * system). */ gl_list_trappable_signals(&act.sa_mask); sigdelset(&act.sa_mask, signo); /* * Assign the signal handler. */ act.sa_handler = handler; /* * There is only one portable signal handling flag, and it isn't * relevant to us, so don't specify any flags. */ act.sa_flags = 0; /* * Register the signal handler. */ if(sigaction(signo, &act, NULL)) return 1; return 0; } /*....................................................................... * Display a left-justified string over multiple terminal lines, * taking account of the current width of the terminal. Optional * indentation and an optional prefix string can be specified to be * displayed at the start of each new terminal line used. Similarly, * an optional suffix can be specified to be displayed at the end of * each terminal line. If needed, a single paragraph can be broken * across multiple calls. Note that literal newlines in the input * string can be used to force a newline at any point and that you * should use this feature to explicitly end all paragraphs, including * at the end of the last string that you write. Note that when a new * line is started between two words that are separated by spaces, * those spaces are not output, whereas when a new line is started * because a newline character was found in the string, only the * spaces before the newline character are discarded. * * Input: * gl GetLine * The resource object of gl_get_line(). * indentation int The number of spaces of indentation to write * at the beginning of each new terminal line. * prefix const char * An optional prefix string to write after the * indentation margin at the start of each new * terminal line. You can specify NULL if no * prefix is required. * suffix const char * An optional suffix string to draw at the end * of the terminal line. Spaces will be added * where necessary to ensure that the suffix ends * in the last column of the terminal line. If * no suffix is desired, specify NULL. * fill_char int The padding character to use when indenting * the line or padding up to the suffix. * def_width int If the terminal width isn't known, such as when * writing to a pipe or redirecting to a file, * this number specifies what width to assume. * start int The number of characters already written to * the start of the current terminal line. This * is primarily used to allow individual * paragraphs to be written over multiple calls * to this function, but can also be used to * allow you to start the first line of a * paragraph with a different prefix or * indentation than those specified above. * string const char * The string to be written. * Output: * return int On error -1 is returned. Otherwise the * return value is the terminal column index at * which the cursor was left after writing the * final word in the string. Successful return * values can thus be passed verbatim to the * 'start' arguments of subsequent calls to * gl_display_text() to allow the printing of a * paragraph to be broken across multiple calls * to gl_display_text(). */ int gl_display_text(GetLine *gl, int indentation, const char *prefix, const char *suffix, int fill_char, int def_width, int start, const char *string) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of _gl_completion_action() */ /* * Check the arguments? */ if(!gl || !string) { errno = EINVAL; return -1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return -1; /* * Display the text while signals are blocked. */ status = _io_display_text(_io_write_stdio, gl->output_fp, indentation, prefix, suffix, fill_char, gl->ncolumn > 0 ? gl->ncolumn : def_width, start, string); /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * Block all of the signals that we are currently trapping. * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * oldset sigset_t * The superseded process signal mask * will be return in *oldset unless oldset is * NULL. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_mask_signals(GetLine *gl, sigset_t *oldset) { /* * Block all signals in all_signal_set, along with any others that are * already blocked by the application. */ if(sigprocmask(SIG_BLOCK, &gl->all_signal_set, oldset) >= 0) { gl->signals_masked = 1; return 0; }; /* * On error attempt to query the current process signal mask, so * that oldset be the correct process signal mask to restore later * if the caller of this function ignores the error return value. */ if(oldset) (void) sigprocmask(SIG_SETMASK, NULL, oldset); gl->signals_masked = 0; return 1; } /*....................................................................... * Restore a process signal mask that was previously returned via the * oldset argument of gl_mask_signals(). * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * oldset sigset_t * The process signal mask to be restored. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_unmask_signals(GetLine *gl, sigset_t *oldset) { gl->signals_masked = 0; return sigprocmask(SIG_SETMASK, oldset, NULL) < 0; } /*....................................................................... * Arrange to temporarily catch the signals marked in gl->use_signal_set. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int 0 - OK. * 1 - Error. */ static int gl_catch_signals(GetLine *gl) { return sigprocmask(SIG_UNBLOCK, &gl->use_signal_set, NULL) < 0; } /*....................................................................... * Select the I/O mode to be used by gl_get_line(). * * Input: * gl GetLine * The resource object of gl_get_line(). * mode GlIOMode The I/O mode to establish. * Output: * return int 0 - OK. * 1 - Error. */ int gl_io_mode(GetLine *gl, GlIOMode mode) { sigset_t oldset; /* The signals that were blocked on entry to this function */ int status; /* The return status of _gl_io_mode() */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return 1; }; /* * Check that the requested mode is known. */ switch(mode) { case GL_NORMAL_MODE: case GL_SERVER_MODE: break; default: errno = EINVAL; _err_record_msg(gl->err, "Unknown gl_get_line() I/O mode requested.", END_ERR_MSG); return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Invoke the private body of this function. */ status = _gl_io_mode(gl, mode); /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the public function, gl_io_mode(). * It assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static int _gl_io_mode(GetLine *gl, GlIOMode mode) { /* * Are we already in the specified mode? */ if(mode == gl->io_mode) return 0; /* * First revert to normal I/O in the current I/O mode. */ _gl_normal_io(gl); /* * Record the new mode. */ gl->io_mode = mode; /* * Perform any actions needed by the new mode. */ if(mode==GL_SERVER_MODE) { if(_gl_raw_io(gl, 1)) return 1; }; return 0; } /*....................................................................... * Return extra information (ie. in addition to that provided by errno) * about the last error to occur in either gl_get_line() or its * associated public functions. * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * buff char * An optional output buffer. Note that if the * calling application calls any gl_*() * functions from signal handlers, it should * provide a buffer here, so that a copy of * the latest error message can safely be made * while signals are blocked. * n size_t The allocated size of buff[]. * Output: * return const char * A pointer to the error message. This will * be the buff argument, unless buff==NULL, in * which case it will be a pointer to an * internal error buffer. In the latter case, * note that the contents of the returned buffer * will change on subsequent calls to any gl_*() * functions. */ const char *gl_error_message(GetLine *gl, char *buff, size_t n) { if(!gl) { static const char *msg = "NULL GetLine argument"; if(buff) { strncpy(buff, msg, n); buff[n-1] = '\0'; } else { return msg; }; } else if(buff) { sigset_t oldset; /* The signals that were blocked on entry to this block */ /* * Temporarily block all signals. */ gl_mask_signals(gl, &oldset); /* * Copy the error message into the specified buffer. */ if(buff && n > 0) { strncpy(buff, _err_get_msg(gl->err), n); buff[n-1] = '\0'; }; /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); } else { return _err_get_msg(gl->err); }; return buff; } /*....................................................................... * Return the signal mask used by gl_get_line(). This is the set of * signals that gl_get_line() is currently configured to trap. * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * set sigset_t * The set of signals will be returned in *set, * in the form of a signal process mask, as * used by sigaction(), sigprocmask(), * sigpending(), sigsuspend(), sigsetjmp() and * other standard POSIX signal-aware * functions. * Output: * return int 0 - OK. * 1 - Error (examine errno for reason). */ int gl_list_signals(GetLine *gl, sigset_t *set) { /* * Check the arguments. */ if(!gl || !set) { if(gl) _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Copy the signal mask into *set. */ memcpy(set, &gl->all_signal_set, sizeof(*set)); return 0; } /*....................................................................... * By default, gl_get_line() doesn't trap signals that are blocked * when it is called. This default can be changed either on a * per-signal basis by calling gl_trap_signal(), or on a global basis * by calling this function. What this function does is add the * GLS_UNBLOCK_SIG flag to all signals that are currently configured * to be trapped by gl_get_line(), such that when subsequent calls to * gl_get_line() wait for I/O, these signals are temporarily * unblocked. This behavior is useful in non-blocking server-I/O mode, * where it is used to avoid race conditions related to handling these * signals externally to gl_get_line(). See the demonstration code in * demo3.c, or the gl_handle_signal() man page for further * information. * * Input: * gl GetLine * The resource object of gl_get_line(). */ void gl_catch_blocked(GetLine *gl) { sigset_t oldset; /* The process signal mask to restore */ GlSignalNode *sig; /* A signal node in gl->sigs */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return; }; /* * Temporarily block all signals while we modify the contents of gl. */ gl_mask_signals(gl, &oldset); /* * Add the GLS_UNBLOCK_SIG flag to all configured signals. */ for(sig=gl->sigs; sig; sig=sig->next) sig->flags |= GLS_UNBLOCK_SIG; /* * Restore the process signal mask that was superseded by the call * to gl_mask_signals(). */ gl_unmask_signals(gl, &oldset); return; } /*....................................................................... * Respond to signals who's default effects have important * consequences to gl_get_line(). This is intended for use in * non-blocking server mode, where the external event loop is * responsible for catching signals. Signals that are handled include * those that by default terminate or suspend the process, and the * signal that indicates that the terminal size has changed. Note that * this function is not signal safe and should thus not be called from * a signal handler itself. See the gl_io_mode() man page for how it * should be used. * * In the case of signals that by default terminate or suspend * processes, command-line editing will be suspended, the terminal * returned to a usable state, then the default disposition of the * signal restored and the signal resent, in order to suspend or * terminate the process. If the process subsequently resumes, * command-line editing is resumed. * * In the case of signals that indicate that the terminal has been * resized, the new size will be queried, and any input line that is * being edited will be redrawn to fit the new dimensions of the * terminal. * * Input: * signo int The number of the signal to respond to. * gl GetLine * The first element of an array of 'ngl' GetLine * objects. * ngl int The number of elements in the gl[] array. Normally * this will be one. */ void gl_handle_signal(int signo, GetLine *gl, int ngl) { int attr; /* The attributes of the specified signal */ sigset_t all_signals; /* The set of trappable signals */ sigset_t oldset; /* The process signal mask to restore */ int i; /* * NULL operation? */ if(ngl < 1 || !gl) return; /* * Look up the default attributes of the specified signal. */ attr = gl_classify_signal(signo); /* * If the signal isn't known, we are done. */ if(!attr) return; /* * Temporarily block all signals while we modify the gl objects. */ gl_list_trappable_signals(&all_signals); sigprocmask(SIG_BLOCK, &all_signals, &oldset); /* * Suspend or terminate the process? */ if(attr & (GLSA_SUSP | GLSA_TERM)) { gl_suspend_process(signo, gl, ngl); /* * Resize the terminal? Note that ioctl() isn't defined as being * signal safe, so we can't call gl_update_size() here. However, * gl_get_line() checks for resizes on each call, so simply arrange * for the application's event loop to call gl_get_line() as soon as * it becomes possible to write to the terminal. Note that if the * caller is calling select() or poll when this happens, these functions * get interrupted, since a signal has been caught. */ } else if(attr & GLSA_SIZE) { for(i=0; iraw_mode) { _gl_normal_io(obj); if(!obj->raw_mode) /* Check that gl_normal_io() succeded */ obj->raw_mode = -1; /* Flag raw mode as needing to be restored */ }; }; /* * Restore the system default disposition of the signal that we * caught. Note that this signal is currently blocked. Note that we * don't use memcpy() to copy signal sets here, because the signal safety * of memcpy() is undefined. */ def_action.sa_handler = SIG_DFL; { char *orig = (char *) &all_signals; char *dest = (char *) &def_action.sa_mask; for(i=0; iraw_mode == -1) { /* Did we flag the need to restore raw mode? */ obj->raw_mode = 0; /* gl_raw_io() does nothing unless raw_mode==0 */ _gl_raw_io(obj, 1); }; }; /* * Restore the process signal mask to the way it was when this function * was called. */ sigprocmask(SIG_SETMASK, &oldset, NULL); return; } /*....................................................................... * Return the information about the default attributes of a given signal. * The attributes that are returned are as defined by the standards that * created them, including POSIX, SVR4 and 4.3+BSD, and are taken from a * table in Richard Steven's book, "Advanced programming in the UNIX * environment". * * Input: * signo int The signal to be characterized. * Output: * return int A bitwise union of GlSigAttr enumerators, or 0 * if the signal isn't known. */ static int gl_classify_signal(int signo) { int i; /* * Search for the specified signal in the gl_signal_list[] table. */ for(i=0; isigno == signo) return sig->attr; }; /* * Signal not known. */ return 0; } /*....................................................................... * When in non-blocking server mode, this function can be used to abandon * the current incompletely entered input line, and prepare to start * editing a new line on the next call to gl_get_line(). * * Input: * gl GetLine * The line editor resource object. */ void gl_abandon_line(GetLine *gl) { sigset_t oldset; /* The process signal mask to restore */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return; }; /* * Temporarily block all signals while we modify the contents of gl. */ gl_mask_signals(gl, &oldset); /* * Mark the input line as discarded. */ _gl_abandon_line(gl); /* * Restore the process signal mask that was superseded by the call * to gl_mask_signals(). */ gl_unmask_signals(gl, &oldset); return; } /*....................................................................... * This is the private body of the gl_abandon_line() function. It * assumes that the caller has checked its arguments and blocked the * delivery of signals. */ void _gl_abandon_line(GetLine *gl) { gl->endline = 1; gl->pending_io = GLP_WRITE; } /*....................................................................... * How many characters are needed to write a number as an octal string? * * Input: * num unsigned The to be measured. * Output: * return int The number of characters needed. */ static int gl_octal_width(unsigned num) { int n; /* The number of characters needed to render the number */ for(n=1; num /= 8; n++) ; return n; } /*....................................................................... * Tell gl_get_line() the current terminal size. Note that this is only * necessary on systems where changes in terminal size aren't reported * via SIGWINCH. * * Input: * gl GetLine * The resource object of gl_get_line(). * ncolumn int The number of columns in the terminal. * nline int The number of lines in the terminal. * Output: * return int 0 - OK. * 1 - Error. */ int gl_set_term_size(GetLine *gl, int ncolumn, int nline) { sigset_t oldset; /* The signals that were blocked on entry */ /* to this function */ int status; /* The return status */ /* * Block all signals while accessing gl. */ gl_mask_signals(gl, &oldset); /* * Install the new terminal size. */ status = _gl_set_term_size(gl, ncolumn, nline); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the gl_set_term_size() function. It * assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static int _gl_set_term_size(GetLine *gl, int ncolumn, int nline) { /* * Check the arguments. */ if(!gl) { errno = EINVAL; return 1; }; /* * Reject non-sensical dimensions. */ if(ncolumn <= 0 || nline <= 0) { _err_record_msg(gl->err, "Invalid terminal size", END_ERR_MSG); errno = EINVAL; return 1; }; /* * Install the new dimensions in the terminal driver if possible, so * that future calls to gl_query_size() get the new value. */ #ifdef TIOCSWINSZ if(gl->is_term) { struct winsize size; size.ws_row = nline; size.ws_col = ncolumn; size.ws_xpixel = 0; size.ws_ypixel = 0; if(ioctl(gl->output_fd, TIOCSWINSZ, &size) == -1) { _err_record_msg(gl->err, "Can't change terminal size", END_ERR_MSG); return 1; }; }; #endif /* * If an input line is in the process of being edited, redisplay it to * accomodate the new dimensions, and record the new dimensions in * gl->nline and gl->ncolumn. */ return gl_handle_tty_resize(gl, ncolumn, nline); } /*....................................................................... * Record a character in the input line buffer at a given position. * * Input: * gl GetLine * The resource object of gl_get_line(). * c char The character to be recorded. * bufpos int The index in the buffer at which to record the * character. * Output: * return int 0 - OK. * 1 - Insufficient room. */ static int gl_buffer_char(GetLine *gl, char c, int bufpos) { /* * Guard against buffer overruns. */ if(bufpos >= gl->linelen) return 1; /* * Record the new character. */ gl->line[bufpos] = c; /* * If the new character was placed beyond the end of the current input * line, update gl->ntotal to reflect the increased number of characters * that are in gl->line, and terminate the string. */ if(bufpos >= gl->ntotal) { gl->ntotal = bufpos+1; gl->line[gl->ntotal] = '\0'; }; return 0; } /*....................................................................... * Copy a given string into the input buffer, overwriting the current * contents. * * Input: * gl GetLine * The resource object of gl_get_line(). * s const char * The string to be recorded. * n int The number of characters to be copied from the * string. * bufpos int The index in the buffer at which to place the * the first character of the string. * Output: * return int 0 - OK. * 1 - String truncated to fit. */ static int gl_buffer_string(GetLine *gl, const char *s, int n, int bufpos) { int nnew; /* The number of characters actually recorded */ int i; /* * How many of the characters will fit within the buffer? */ nnew = bufpos + n <= gl->linelen ? n : (gl->linelen - bufpos); /* * Record the first nnew characters of s[] in the buffer. */ for(i=0; intotal + n > gl->linelen) return 1; /* * Move everything including and beyond the character at 'start' * towards the end of the string. */ memmove(gl->line + start + n, gl->line + start, gl->ntotal - start + 1); /* * Update the recorded size of the line. */ gl->ntotal += n; return 1; } /*....................................................................... * Remove a given number of characters from the input buffer. This * involves moving the characters that follow the removed characters to * where the removed sub-string started in the input buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * start int The first character to be removed. * n int The number of characters to remove. */ static void gl_remove_from_buffer(GetLine *gl, int start, int n) { memmove(gl->line + start, gl->line + start + n, gl->ntotal - start - n + 1); /* * Update the recorded size of the line. */ gl->ntotal -= n; } /*....................................................................... * Truncate the string in the input line buffer after a given number of * characters. * * Input: * gl GetLine * The resource object of gl_get_line(). * n int The new length of the line. * Output: * return int 0 - OK. * 1 - n > gl->linelen. */ static int gl_truncate_buffer(GetLine *gl, int n) { if(n > gl->linelen) return 1; gl->line[n] = '\0'; gl->ntotal = n; return 0; } /*....................................................................... * When the contents of gl->line[] are changed without calling any of the * gl_ buffer manipulation functions, this function must be called to * compute the length of this string, and ancillary information. * * Input: * gl GetLine * The resource object of gl_get_line(). */ static void gl_update_buffer(GetLine *gl) { int len; /* The length of the line */ /* * Measure the length of the input line. */ for(len=0; len <= gl->linelen && gl->line[len]; len++) ; /* * Just in case the string wasn't correctly terminated, do so here. */ gl->line[len] = '\0'; /* * Record the number of characters that are now in gl->line[]. */ gl->ntotal = len; /* * Ensure that the cursor stays within the bounds of the modified * input line. */ if(gl->buff_curpos > gl->ntotal) gl->buff_curpos = gl->ntotal; /* * Arrange for the input line to be redrawn. */ gl_queue_redisplay(gl); return; } /*....................................................................... * Erase the displayed input line, including its prompt, and leave the * cursor where the erased line started. Note that to allow this * function to be used when responding to a terminal resize, this * function is designed to work even if the horizontal cursor position * doesn't match the internally recorded position. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int 0 - OK. * 1 - Error. */ static int gl_erase_line(GetLine *gl) { /* * Is a line currently displayed? */ if(gl->displayed) { /* * Relative the the start of the input line, which terminal line of * the current input line is the cursor currently on? */ int cursor_line = gl->term_curpos / gl->ncolumn; /* * Move the cursor to the start of the line. */ for( ; cursor_line > 0; cursor_line--) { if(gl_print_control_sequence(gl, 1, gl->up)) return 1; }; if(gl_print_control_sequence(gl, 1, gl->bol)) return 1; /* * Clear from the start of the line to the end of the terminal. */ if(gl_print_control_sequence(gl, gl->nline, gl->clear_eod)) return 1; /* * Mark the line as no longer displayed. */ gl_line_erased(gl); }; return 0; } /*....................................................................... * Arrange for the input line to be redisplayed by gl_flush_output(), * as soon as the output queue becomes empty. * * Input: * gl GetLine * The resource object of gl_get_line(). */ static void gl_queue_redisplay(GetLine *gl) { gl->redisplay = 1; gl->pending_io = GLP_WRITE; } /*....................................................................... * Truncate the displayed input line starting from the current * terminal cursor position, and leave the cursor at the end of the * truncated line. The input-line buffer is not affected. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int 0 - OK. * 1 - Error. */ static int gl_truncate_display(GetLine *gl) { /* * Keep a record of the current terminal cursor position. */ int term_curpos = gl->term_curpos; /* * First clear from the cursor to the end of the current input line. */ if(gl_print_control_sequence(gl, 1, gl->clear_eol)) return 1; /* * If there is more than one line displayed, go to the start of the * next line and clear from there to the end of the display. Note that * we can't use clear_eod to do the whole job of clearing from the * current cursor position to the end of the terminal because * clear_eod is only defined when used at the start of a terminal line * (eg. with gnome terminals, clear_eod clears from the start of the * current terminal line, rather than from the current cursor * position). */ if(gl->term_len / gl->ncolumn > gl->term_curpos / gl->ncolumn) { if(gl_print_control_sequence(gl, 1, gl->down) || gl_print_control_sequence(gl, 1, gl->bol) || gl_print_control_sequence(gl, gl->nline, gl->clear_eod)) return 1; /* * Where is the cursor now? */ gl->term_curpos = gl->ncolumn * (term_curpos / gl->ncolumn + 1); /* * Restore the cursor position. */ gl_set_term_curpos(gl, term_curpos); }; /* * Update the recorded position of the final character. */ gl->term_len = gl->term_curpos; return 0; } /*....................................................................... * Return the set of all trappable signals. * * Input: * signals sigset_t * The set of signals will be recorded in * *signals. */ static void gl_list_trappable_signals(sigset_t *signals) { /* * Start with the set of all signals. */ sigfillset(signals); /* * Remove un-trappable signals from this set. */ #ifdef SIGKILL sigdelset(signals, SIGKILL); #endif #ifdef SIGSTOP sigdelset(signals, SIGSTOP); #endif } /*....................................................................... * Read an input line from a non-interactive input stream. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int 0 - OK * 1 - Error. */ static int gl_read_stream_line(GetLine *gl) { char c = '\0'; /* The latest character read from fp */ /* * Record the fact that we are about to read input. */ gl->pending_io = GLP_READ; /* * If we are starting a new line, reset the line-editing parameters, * and discard the previous input line. */ if(gl->endline) { gl_reset_editor(gl); gl_truncate_buffer(gl, 0); }; /* * Read one character at a time. */ while(gl->ntotal < gl->linelen && c != '\n') { /* * Attempt to read one more character. */ switch(gl_read_input(gl, &c)) { case GL_READ_OK: break; case GL_READ_EOF: /* Reached end-of-file? */ /* * If any characters were read before the end-of-file condition, * interpolate a newline character, so that the caller sees a * properly terminated line. Otherwise return an end-of-file * condition. */ if(gl->ntotal > 0) { c = '\n'; } else { gl_record_status(gl, GLR_EOF, 0); return 1; }; break; case GL_READ_BLOCKED: /* Input blocked? */ gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO); return 1; break; case GL_READ_ERROR: /* I/O error? */ return 1; break; }; /* * Append the character to the line buffer. */ if(gl_buffer_char(gl, c, gl->ntotal)) return 1; }; /* * Was the end of the input line reached before running out of buffer space? */ gl->endline = (c == '\n'); return 0; } /*....................................................................... * Read a single character from a non-interactive input stream. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int The character, or EOF on error. */ static int gl_read_stream_char(GetLine *gl) { char c = '\0'; /* The latest character read from fp */ int retval = EOF; /* The return value of this function */ /* * Arrange to discard any incomplete input line. */ _gl_abandon_line(gl); /* * Record the fact that we are about to read input. */ gl->pending_io = GLP_READ; /* * Attempt to read one more character. */ switch(gl_read_input(gl, &c)) { case GL_READ_OK: /* Success */ retval = c; break; case GL_READ_BLOCKED: /* The read blocked */ gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO); retval = EOF; /* Failure */ break; case GL_READ_EOF: /* End of file reached */ gl_record_status(gl, GLR_EOF, 0); retval = EOF; /* Failure */ break; case GL_READ_ERROR: retval = EOF; /* Failure */ break; }; return retval; } /*....................................................................... * Bind a key sequence to a given action. * * Input: * gl GetLine * The resource object of gl_get_line(). * origin GlKeyOrigin The originator of the key binding. * key const char * The key-sequence to be bound (or unbound). * action const char * The name of the action to bind the key to, * or either NULL or "" to unbind the * key-sequence. * Output: * return int 0 - OK * 1 - Error. */ int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin, const char *keyseq, const char *action) { KtBinder binder; /* The private internal equivalent of 'origin' */ /* * Check the arguments. */ if(!gl || !keyseq) { errno = EINVAL; if(gl) _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); return 1; }; /* * An empty action string requests that the key-sequence be unbound. * This is indicated to _kt_set_keybinding() by passing a NULL action * string, so convert an empty string to a NULL action pointer. */ if(action && *action=='\0') action = NULL; /* * Translate the public originator enumeration to the private equivalent. */ binder = origin==GL_USER_KEY ? KTB_USER : KTB_NORM; /* * Bind the action to a given key-sequence? */ if(keyseq && _kt_set_keybinding(gl->bindings, binder, keyseq, action)) { _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); return 1; }; return 0; } /*....................................................................... * This is the public wrapper around the gl_clear_termina() function. * It clears the terminal and leaves the cursor at the home position. * In server I/O mode, the next call to gl_get_line() will also * redisplay the current input line. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int 0 - OK. * 1 - Error. */ int gl_erase_terminal(GetLine *gl) { sigset_t oldset; /* The signals that were blocked on entry */ /* to this function */ int status; /* The return status */ /* * Block all signals while accessing gl. */ gl_mask_signals(gl, &oldset); /* * Clear the terminal. */ status = gl_clear_screen(gl, 1, NULL); /* * Attempt to flush the clear-screen control codes to the terminal. * If this doesn't complete the job, the next call to gl_get_line() * will. */ (void) gl_flush_output(gl); /* * Restore the process signal mask before returning. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This function must be called by any function that erases the input * line. * * Input: * gl GetLine * The resource object of gl_get_line(). */ static void gl_line_erased(GetLine *gl) { gl->displayed = 0; gl->term_curpos = 0; gl->term_len = 0; } /*....................................................................... * Append a specified line to the history list. * * Input: * gl GetLine * The resource object of gl_get_line(). * line const char * The line to be added. * Output: * return int 0 - OK. * 1 - Error. */ int gl_append_history(GetLine *gl, const char *line) { sigset_t oldset; /* The signals that were blocked on entry */ /* to this function */ int status; /* The return status */ /* * Check the arguments. */ if(!gl || !line) { errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Execute the private body of the function while signals are blocked. */ status = _gl_append_history(gl, line); /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return status; } /*....................................................................... * This is the private body of the public function, gl_append_history(). * It assumes that the caller has checked its arguments and blocked the * delivery of signals. */ static int _gl_append_history(GetLine *gl, const char *line) { int status =_glh_add_history(gl->glh, line, 0); if(status) _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); return status; } /*....................................................................... * Enable or disable the automatic addition of newly entered lines to the * history list. * * Input: * gl GetLine * The resource object of gl_get_line(). * enable int If true, subsequently entered lines will * automatically be added to the history list * before they are returned to the caller of * gl_get_line(). If 0, the choice of how and * when to archive lines in the history list, * is left up to the calling application, which * can do so via calls to gl_append_history(). * Output: * return int 0 - OK. * 1 - Error. */ int gl_automatic_history(GetLine *gl, int enable) { sigset_t oldset; /* The signals that were blocked on entry */ /* to this function */ /* * Check the arguments. */ if(!gl) { errno = EINVAL; return 1; }; /* * Block all signals. */ if(gl_mask_signals(gl, &oldset)) return 1; /* * Execute the private body of the function while signals are blocked. */ gl->automatic_history = enable; /* * Restore the process signal mask. */ gl_unmask_signals(gl, &oldset); return 0; } /*....................................................................... * This is a public function that reads a single uninterpretted * character from the user, without displaying anything. * * Input: * gl GetLine * A resource object previously returned by * new_GetLine(). * Output: * return int The character that was read, or EOF if the read * had to be aborted (in which case you can call * gl_return_status() to find out why). */ int gl_read_char(GetLine *gl) { int retval; /* The return value of _gl_read_char() */ /* * This function can be called from application callback functions, * so check whether signals have already been masked, so that we don't * do it again, and overwrite gl->old_signal_set. */ int was_masked = gl->signals_masked; /* * Check the arguments. */ if(!gl) { errno = EINVAL; return EOF; }; /* * Temporarily block all of the signals that we have been asked to trap. */ if(!was_masked && gl_mask_signals(gl, &gl->old_signal_set)) return EOF; /* * Perform the character reading task. */ retval = _gl_read_char(gl); /* * Restore the process signal mask to how it was when this function was * first called. */ if(!was_masked) gl_unmask_signals(gl, &gl->old_signal_set); return retval; } /*....................................................................... * This is the main body of the public function gl_read_char(). */ static int _gl_read_char(GetLine *gl) { int retval = EOF; /* The return value */ int waserr = 0; /* True if an error occurs */ char c; /* The character read */ /* * This function can be called from application callback functions, * so check whether signals have already been overriden, so that we don't * overwrite the preserved signal handlers with gl_get_line()s. Also * record whether we are currently in raw I/O mode or not, so that this * can be left in the same state on leaving this function. */ int was_overriden = gl->signals_overriden; int was_raw = gl->raw_mode; /* * Also keep a record of the direction of any I/O that gl_get_line() * is awaiting, so that we can restore this status on return. */ GlPendingIO old_pending_io = gl->pending_io; /* * Assume that this call will successfully complete the input operation * until proven otherwise. */ gl_clear_status(gl); /* * If this is the first call to this function or gl_get_line(), * since new_GetLine(), complete any postponed configuration. */ if(!gl->configured) { (void) _gl_configure_getline(gl, NULL, NULL, TECLA_CONFIG_FILE); gl->configured = 1; }; /* * Before installing our signal handler functions, record the fact * that there are no pending signals. */ gl_pending_signal = -1; /* * Temporarily override the signal handlers of the calling program, * so that we can intercept signals that would leave the terminal * in a bad state. */ if(!was_overriden) waserr = gl_override_signal_handlers(gl); /* * After recording the current terminal settings, switch the terminal * into raw input mode, without redisplaying any partially entered input * line. */ if(!was_raw) waserr = waserr || _gl_raw_io(gl, 0); /* * Attempt to read the line. This will require more than one attempt if * either a current temporary input file is opened by gl_get_input_line() * or the end of a temporary input file is reached by gl_read_stream_line(). */ while(!waserr) { /* * Read a line from a non-interactive stream? */ if(gl->file_fp || !gl->is_term) { retval = gl_read_stream_char(gl); if(retval != EOF) { /* Success? */ break; } else if(gl->file_fp) { /* End of temporary input file? */ gl_revert_input(gl); gl_record_status(gl, GLR_NEWLINE, 0); } else { /* An error? */ waserr = 1; break; }; }; /* * Read from the terminal? Note that the above if() block may have * changed gl->file_fp, so it is necessary to retest it here, rather * than using an else statement. */ if(!gl->file_fp && gl->is_term) { /* * Flush any pending output to the terminal before waiting * for the user to type a character. */ if(_glq_char_count(gl->cq) > 0 && gl_flush_output(gl)) { retval = EOF; /* * Read one character. Don't append it to the key buffer, since * this would subseuqnely appear as bogus input to the line editor. */ } else if(gl_read_terminal(gl, 0, &c) == 0) { /* * Record the character for return. */ retval = c; /* * In this mode, count each character as being a new key-sequence. */ gl->keyseq_count++; /* * Delete the character that was read, from the key-press buffer. */ gl_discard_chars(gl, 1); }; if(retval==EOF) waserr = 1; else break; }; }; /* * If an error occurred, but gl->rtn_status is still set to * GLR_NEWLINE, change the status to GLR_ERROR. Otherwise * leave it at whatever specific value was assigned by the function * that aborted input. This means that only functions that trap * non-generic errors have to remember to update gl->rtn_status * themselves. */ if(waserr && gl->rtn_status == GLR_NEWLINE) gl_record_status(gl, GLR_ERROR, errno); /* * Restore terminal settings, if they were changed by this function. */ if(!was_raw && gl->io_mode != GL_SERVER_MODE) _gl_normal_io(gl); /* * Restore the signal handlers, if they were overriden by this function. */ if(!was_overriden) gl_restore_signal_handlers(gl); /* * If this function gets aborted early, the errno value associated * with the event that caused this to happen is recorded in * gl->rtn_errno. Since errno may have been overwritten by cleanup * functions after this, restore its value to the value that it had * when the error condition occured, so that the caller can examine it * to find out what happened. */ errno = gl->rtn_errno; /* * Error conditions are signalled to the caller, by setting the returned * character to EOF. */ if(gl->rtn_status != GLR_NEWLINE) retval = EOF; /* * Restore the indication of what direction of I/O gl_get_line() * was awaiting before this call. */ gl->pending_io = old_pending_io; /* * Return the acquired character. */ return retval; } /*....................................................................... * Reset the GetLine completion status. This function should be called * at the start of gl_get_line(), gl_read_char() and gl_query_char() * to discard the completion status and non-zero errno value of any * preceding calls to these functions. * * Input: * gl GetLine * The resource object of this module. */ static void gl_clear_status(GetLine *gl) { gl_record_status(gl, GLR_NEWLINE, 0); } /*....................................................................... * When an error or other event causes gl_get_line() to return, this * function should be called to record information about what * happened, including the value of errno and the value that * gl_return_status() should return. * * Input: * gl GetLine * The resource object of this module. * rtn_status GlReturnStatus The completion status. To clear a * previous abnormal completion status, * specify GLR_NEWLINE (this is what * gl_clear_status() does). * rtn_errno int The associated value of errno. */ static void gl_record_status(GetLine *gl, GlReturnStatus rtn_status, int rtn_errno) { /* * If rtn_status==GLR_NEWLINE, then this resets the completion status, so we * should always heed this. Otherwise, only record the first abnormal * condition that occurs after such a reset. */ if(rtn_status == GLR_NEWLINE || gl->rtn_status == GLR_NEWLINE) { gl->rtn_status = rtn_status; gl->rtn_errno = rtn_errno; }; } yuma123_2.14/libtecla/expand.c0000664000175000017500000013024014770023131016325 0ustar vladimirvladimir/* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * If file-system access is to be excluded, this module has no function, * so all of its code should be excluded. */ #ifndef WITHOUT_FILE_SYSTEM #include #include #include #include #include "freelist.h" #include "direader.h" #include "pathutil.h" #include "homedir.h" #include "stringrp.h" #include "libtecla.h" #include "ioutil.h" #include "expand.h" #include "errmsg.h" /* * Specify the number of elements to extend the files[] array by * when it proves to be too small. This also sets the initial size * of the array. */ #define MATCH_BLK_FACT 256 /* * A list of directory iterators is maintained using nodes of the * following form. */ typedef struct DirNode DirNode; struct DirNode { DirNode *next; /* The next directory in the list */ DirNode *prev; /* The node that precedes this node in the list */ DirReader *dr; /* The directory reader object */ }; typedef struct { FreeList *mem; /* Memory for DirNode list nodes */ DirNode *head; /* The head of the list of used and unused cache nodes */ DirNode *next; /* The next unused node between head and tail */ DirNode *tail; /* The tail of the list of unused cache nodes */ } DirCache; /* * Specify how many directory cache nodes to allocate at a time. */ #define DIR_CACHE_BLK 20 /* * Set the maximum length allowed for usernames. */ #define USR_LEN 100 /* * Set the maximum length allowed for environment variable names. */ #define ENV_LEN 100 /* * Set the default number of spaces place between columns when listing * a set of expansions. */ #define EF_COL_SEP 2 struct ExpandFile { ErrMsg *err; /* The error reporting buffer */ StringGroup *sg; /* A list of string segments in which */ /* matching filenames are stored. */ DirCache cache; /* The cache of directory reader objects */ PathName *path; /* The pathname being matched */ HomeDir *home; /* Home-directory lookup object */ int files_dim; /* The allocated dimension of result.files[] */ char usrnam[USR_LEN+1]; /* A user name */ char envnam[ENV_LEN+1]; /* An environment variable name */ FileExpansion result; /* The container used to return the results of */ /* expanding a path. */ }; static int ef_record_pathname(ExpandFile *ef, const char *pathname, int remove_escapes); static char *ef_cache_pathname(ExpandFile *ef, const char *pathname, int remove_escapes); static void ef_clear_files(ExpandFile *ef); static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname); static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node); static char *ef_expand_special(ExpandFile *ef, const char *path, int pathlen); static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr, const char *pattern, int separate); static int ef_matches_range(int c, const char *pattern, const char **endp); static int ef_string_matches_pattern(const char *file, const char *pattern, int xplicit, const char *nextp); static int ef_cmp_strings(const void *v1, const void *v2); /* * Encapsulate the formatting information needed to layout a * multi-column listing of expansions. */ typedef struct { int term_width; /* The width of the terminal (characters) */ int column_width; /* The number of characters within in each column. */ int ncol; /* The number of columns needed */ int nline; /* The number of lines needed */ } EfListFormat; /* * Given the current terminal width, and a list of file expansions, * determine how to best use the terminal width to display a multi-column * listing of expansions. */ static void ef_plan_listing(FileExpansion *result, int term_width, EfListFormat *fmt); /* * Display a given line of a multi-column list of file-expansions. */ static int ef_format_line(FileExpansion *result, EfListFormat *fmt, int lnum, GlWriteFn *write_fn, void *data); /*....................................................................... * Create the resources needed to expand filenames. * * Output: * return ExpandFile * The new object, or NULL on error. */ ExpandFile *new_ExpandFile(void) { ExpandFile *ef; /* The object to be returned */ /* * Allocate the container. */ ef = (ExpandFile *) malloc(sizeof(ExpandFile)); if(!ef) { errno = ENOMEM; return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_ExpandFile(). */ ef->err = NULL; ef->sg = NULL; ef->cache.mem = NULL; ef->cache.head = NULL; ef->cache.next = NULL; ef->cache.tail = NULL; ef->path = NULL; ef->home = NULL; ef->result.files = NULL; ef->result.nfile = 0; ef->usrnam[0] = '\0'; ef->envnam[0] = '\0'; /* * Allocate a place to record error messages. */ ef->err = _new_ErrMsg(); if(!ef->err) return del_ExpandFile(ef); /* * Allocate a list of string segments for storing filenames. */ ef->sg = _new_StringGroup(_pu_pathname_dim()); if(!ef->sg) return del_ExpandFile(ef); /* * Allocate a freelist for allocating directory cache nodes. */ ef->cache.mem = _new_FreeList(sizeof(DirNode), DIR_CACHE_BLK); if(!ef->cache.mem) return del_ExpandFile(ef); /* * Allocate a pathname buffer. */ ef->path = _new_PathName(); if(!ef->path) return del_ExpandFile(ef); /* * Allocate an object for looking up home-directories. */ ef->home = _new_HomeDir(); if(!ef->home) return del_ExpandFile(ef); /* * Allocate an array for files. This will be extended later if needed. */ ef->files_dim = MATCH_BLK_FACT; ef->result.files = (char **) malloc(sizeof(ef->result.files[0]) * ef->files_dim); if(!ef->result.files) { errno = ENOMEM; return del_ExpandFile(ef); }; return ef; } /*....................................................................... * Delete a ExpandFile object. * * Input: * ef ExpandFile * The object to be deleted. * Output: * return ExpandFile * The deleted object (always NULL). */ ExpandFile *del_ExpandFile(ExpandFile *ef) { if(ef) { DirNode *dnode; /* * Delete the string segments. */ ef->sg = _del_StringGroup(ef->sg); /* * Delete the cached directory readers. */ for(dnode=ef->cache.head; dnode; dnode=dnode->next) dnode->dr = _del_DirReader(dnode->dr); /* * Delete the memory from which the DirNode list was allocated, thus * deleting the list at the same time. */ ef->cache.mem = _del_FreeList(ef->cache.mem, 1); ef->cache.head = ef->cache.tail = ef->cache.next = NULL; /* * Delete the pathname buffer. */ ef->path = _del_PathName(ef->path); /* * Delete the home-directory lookup object. */ ef->home = _del_HomeDir(ef->home); /* * Delete the array of pointers to files. */ if(ef->result.files) { free(ef->result.files); ef->result.files = NULL; }; /* * Delete the error report buffer. */ ef->err = _del_ErrMsg(ef->err); /* * Delete the container. */ free(ef); }; return NULL; } /*....................................................................... * Expand a pathname, converting ~user/ and ~/ patterns at the start * of the pathname to the corresponding home directories, replacing * $envvar with the value of the corresponding environment variable, * and then, if there are any wildcards, matching these against existing * filenames. * * If no errors occur, a container is returned containing the array of * files that resulted from the expansion. If there were no wildcards * in the input pathname, this will contain just the original pathname * after expansion of ~ and $ expressions. If there were any wildcards, * then the array will contain the files that matched them. Note that * if there were any wildcards but no existing files match them, this * is counted as an error and NULL is returned. * * The supported wildcards and their meanings are: * * - Match any sequence of zero or more characters. * ? - Match any single character. * [chars] - Match any single character that appears in 'chars'. * If 'chars' contains an expression of the form a-b, * then any character between a and b, including a and b, * matches. The '-' character looses its special meaning * as a range specifier when it appears at the start * of the sequence of characters. * [^chars] - The same as [chars] except that it matches any single * character that doesn't appear in 'chars'. * * Wildcard expressions are applied to individual filename components. * They don't match across directory separators. A '.' character at * the beginning of a filename component must also be matched * explicitly by a '.' character in the input pathname, since these * are UNIX's hidden files. * * Input: * ef ExpandFile * The pathname expansion resource object. * path char * The path name to be expanded. * pathlen int The length of the suffix of path[] that * constitutes the filename to be expanded, * or -1 to specify that the whole of the * path string should be used. Note that * regardless of the value of this argument, * path[] must contain a '\0' terminated * string, since this function checks that * pathlen isn't mistakenly too long. * Output: * return FileExpansion * A pointer to a container within the given * ExpandFile object. This contains an array * of the pathnames that resulted from expanding * ~ and $ expressions and from matching any * wildcards, sorted into lexical order. * This container and its contents will be * recycled on subsequent calls, so if you need * to keep the results of two successive runs, * you will either have to allocate a private * copy of the array, or use two ExpandFile * objects. * * On error NULL is returned. A description * of the error can be acquired by calling the * ef_last_error() function. */ FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen) { DirNode *dnode; /* A directory-reader cache node */ const char *dirname; /* The name of the top level directory of the search */ const char *pptr; /* A pointer into path[] */ int wild; /* True if the path contains any wildcards */ /* * Check the arguments. */ if(!ef || !path) { if(ef) { _err_record_msg(ef->err, "ef_expand_file: NULL path argument", END_ERR_MSG); }; errno = EINVAL; return NULL; }; /* * If the caller specified that the whole of path[] be matched, * work out the corresponding length. */ if(pathlen < 0 || pathlen > strlen(path)) pathlen = strlen(path); /* * Discard previous expansion results. */ ef_clear_files(ef); /* * Preprocess the path, expanding ~/, ~user/ and $envvar references, * using ef->path as a work directory and returning a pointer to * a copy of the resulting pattern in the cache. */ path = ef_expand_special(ef, path, pathlen); if(!path) return NULL; /* * Clear the pathname buffer. */ _pn_clear_path(ef->path); /* * Does the pathname contain any wildcards? */ for(wild=0,pptr=path; !wild && *pptr; pptr++) { switch(*pptr) { case '\\': /* Skip escaped characters */ if(pptr[1]) pptr++; break; case '*': case '?': case '[': /* A wildcard character? */ wild = 1; break; }; }; /* * If there are no wildcards to match, copy the current expanded * path into the output array, removing backslash escapes while doing so. */ if(!wild) { if(ef_record_pathname(ef, path, 1)) return NULL; /* * Does the filename exist? */ ef->result.exists = _pu_file_exists(ef->result.files[0]); /* * Match wildcards against existing files. */ } else { /* * Only existing files that match the pattern will be returned in the * cache. */ ef->result.exists = 1; /* * Treat matching of the root-directory as a special case since it * isn't contained in a directory. */ if(strcmp(path, FS_ROOT_DIR) == 0) { if(ef_record_pathname(ef, FS_ROOT_DIR, 0)) return NULL; } else { /* * What should the top level directory of the search be? */ if(strncmp(path, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0) { dirname = FS_ROOT_DIR; if(!_pn_append_to_path(ef->path, FS_ROOT_DIR, -1, 0)) { _err_record_msg(ef->err, "Insufficient memory to record path", END_ERR_MSG); return NULL; }; path += FS_ROOT_DIR_LEN; } else { dirname = FS_PWD; }; /* * Open the top-level directory of the search. */ dnode = ef_open_dir(ef, dirname); if(!dnode) return NULL; /* * Recursively match successive directory components of the path. */ if(ef_match_relative_pathname(ef, dnode->dr, path, 0)) { dnode = ef_close_dir(ef, dnode); return NULL; }; /* * Cleanup. */ dnode = ef_close_dir(ef, dnode); }; /* * No files matched? */ if(ef->result.nfile < 1) { _err_record_msg(ef->err, "No files match", END_ERR_MSG); return NULL; }; /* * Sort the pathnames that matched. */ qsort(ef->result.files, ef->result.nfile, sizeof(ef->result.files[0]), ef_cmp_strings); }; /* * Return the result container. */ return &ef->result; } /*....................................................................... * Attempt to recursively match the given pattern with the contents of * the current directory, descending sub-directories as needed. * * Input: * ef ExpandFile * The pathname expansion resource object. * dr DirReader * The directory reader object of the directory * to be searched. * pattern const char * The pattern to match with files in the current * directory. * separate int When appending a filename from the specified * directory to ef->pathname, insert a directory * separator between the existing pathname and * the filename, unless separate is zero. * Output: * return int 0 - OK. * 1 - Error. */ static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr, const char *pattern, int separate) { const char *nextp; /* The pointer to the character that follows the part */ /* of the pattern that is to be matched with files */ /* in the current directory. */ char *file; /* The name of the file being matched */ int pathlen; /* The length of ef->pathname[] on entry to this */ /* function */ /* * Record the current length of the pathname string recorded in * ef->pathname[]. */ pathlen = strlen(ef->path->name); /* * Get a pointer to the character that follows the end of the part of * the pattern that should be matched to files within the current directory. * This will either point to a directory separator, or to the '\0' terminator * of the pattern string. */ for(nextp=pattern; *nextp && strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0; nextp++) ; /* * Read each file from the directory, attempting to match it to the * current pattern. */ while((file=_dr_next_file(dr)) != NULL) { /* * Does the latest file match the pattern up to nextp? */ if(ef_string_matches_pattern(file, pattern, file[0]=='.', nextp)) { /* * Append the new directory entry to the current matching pathname. */ if((separate && _pn_append_to_path(ef->path, FS_DIR_SEP, -1, 0)==NULL) || _pn_append_to_path(ef->path, file, -1, 0)==NULL) { _err_record_msg(ef->err, "Insufficient memory to record path", END_ERR_MSG); return 1; }; /* * If we have reached the end of the pattern, record the accumulated * pathname in the list of matching files. */ if(*nextp == '\0') { if(ef_record_pathname(ef, ef->path->name, 0)) return 1; /* * If the matching directory entry is a subdirectory, and the * next character of the pattern is a directory separator, * recursively call the current function to scan the sub-directory * for matches. */ } else if(_pu_path_is_dir(ef->path->name) && strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { /* * If the pattern finishes with the directory separator, then * record the pathame as matching. */ if(nextp[FS_DIR_SEP_LEN] == '\0') { if(ef_record_pathname(ef, ef->path->name, 0)) return 1; /* * Match files within the directory. */ } else { DirNode *subdnode = ef_open_dir(ef, ef->path->name); if(subdnode) { if(ef_match_relative_pathname(ef, subdnode->dr, nextp+FS_DIR_SEP_LEN, 1)) { subdnode = ef_close_dir(ef, subdnode); return 1; }; subdnode = ef_close_dir(ef, subdnode); }; }; }; /* * Remove the latest filename from the pathname string, so that * another matching file can be appended. */ ef->path->name[pathlen] = '\0'; }; }; return 0; } /*....................................................................... * Record a new matching filename. * * Input: * ef ExpandFile * The filename-match resource object. * pathname const char * The pathname to record. * remove_escapes int If true, remove backslash escapes in the * recorded copy of the pathname. * Output: * return int 0 - OK. * 1 - Error (ef->err will contain a * description of the error). */ static int ef_record_pathname(ExpandFile *ef, const char *pathname, int remove_escapes) { char *copy; /* The recorded copy of pathname[] */ /* * Attempt to make a copy of the pathname in the cache. */ copy = ef_cache_pathname(ef, pathname, remove_escapes); if(!copy) return 1; /* * If there isn't room to record a pointer to the recorded pathname in the * array of files, attempt to extend the array. */ if(ef->result.nfile + 1 > ef->files_dim) { int files_dim = ef->files_dim + MATCH_BLK_FACT; char **files = (char **) realloc(ef->result.files, files_dim * sizeof(files[0])); if(!files) { _err_record_msg(ef->err, "Insufficient memory to record all of the matching filenames", END_ERR_MSG); errno = ENOMEM; return 1; }; ef->result.files = files; ef->files_dim = files_dim; }; /* * Record a pointer to the new match. */ ef->result.files[ef->result.nfile++] = copy; return 0; } /*....................................................................... * Record a pathname in the cache. * * Input: * ef ExpandFile * The filename-match resource object. * pathname char * The pathname to record. * remove_escapes int If true, remove backslash escapes in the * copy of the pathname. * Output: * return char * The pointer to the copy of the pathname. * On error NULL is returned and a description * of the error is left in ef->err. */ static char *ef_cache_pathname(ExpandFile *ef, const char *pathname, int remove_escapes) { char *copy = _sg_store_string(ef->sg, pathname, remove_escapes); if(!copy) _err_record_msg(ef->err, "Insufficient memory to store pathname", END_ERR_MSG); return copy; } /*....................................................................... * Clear the results of the previous expansion operation, ready for the * next. * * Input: * ef ExpandFile * The pathname expansion resource object. */ static void ef_clear_files(ExpandFile *ef) { _clr_StringGroup(ef->sg); _pn_clear_path(ef->path); ef->result.exists = 0; ef->result.nfile = 0; _err_clear_msg(ef->err); return; } /*....................................................................... * Get a new directory reader object from the cache. * * Input: * ef ExpandFile * The pathname expansion resource object. * pathname const char * The pathname of the directory. * Output: * return DirNode * The cache entry of the new directory reader, * or NULL on error. On error, ef->err will * contain a description of the error. */ static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname) { char *errmsg = NULL; /* An error message from a called function */ DirNode *node; /* The cache node used */ /* * Get the directory reader cache. */ DirCache *cache = &ef->cache; /* * Extend the cache if there are no free cache nodes. */ if(!cache->next) { node = (DirNode *) _new_FreeListNode(cache->mem); if(!node) { _err_record_msg(ef->err, "Insufficient memory to open a new directory", END_ERR_MSG); return NULL; }; /* * Initialize the cache node. */ node->next = NULL; node->prev = NULL; node->dr = NULL; /* * Allocate a directory reader object. */ node->dr = _new_DirReader(); if(!node->dr) { _err_record_msg(ef->err, "Insufficient memory to open a new directory", END_ERR_MSG); node = (DirNode *) _del_FreeListNode(cache->mem, node); return NULL; }; /* * Append the node to the cache list. */ node->prev = cache->tail; if(cache->tail) cache->tail->next = node; else cache->head = node; cache->next = cache->tail = node; }; /* * Get the first unused node, but don't remove it from the list yet. */ node = cache->next; /* * Attempt to open the specified directory. */ if(_dr_open_dir(node->dr, pathname, &errmsg)) { _err_record_msg(ef->err, errmsg, END_ERR_MSG); return NULL; }; /* * Now that we have successfully opened the specified directory, * remove the cache node from the list, and relink the list around it. */ cache->next = node->next; if(node->prev) node->prev->next = node->next; else cache->head = node->next; if(node->next) node->next->prev = node->prev; else cache->tail = node->prev; node->next = node->prev = NULL; /* * Return the successfully initialized cache node to the caller. */ return node; } /*....................................................................... * Return a directory reader object to the cache, after first closing * the directory that it was managing. * * Input: * ef ExpandFile * The pathname expansion resource object. * node DirNode * The cache entry of the directory reader, as returned * by ef_open_dir(). * Output: * return DirNode * The deleted DirNode (ie. allways NULL). */ static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node) { /* * Get the directory reader cache. */ DirCache *cache = &ef->cache; /* * Close the directory. */ _dr_close_dir(node->dr); /* * Return the node to the tail of the cache list. */ node->next = NULL; node->prev = cache->tail; if(cache->tail) cache->tail->next = node; else cache->head = cache->tail = node; if(!cache->next) cache->next = node; return NULL; } /*....................................................................... * Return non-zero if the specified file name matches a given glob * pattern. * * Input: * file const char * The file-name component to be matched to the pattern. * pattern const char * The start of the pattern to match against file[]. * xplicit int If non-zero, the first character must be matched * explicitly (ie. not with a wildcard). * nextp const char * The pointer to the the character following the * end of the pattern in pattern[]. * Output: * return int 0 - Doesn't match. * 1 - The file-name string matches the pattern. */ static int ef_string_matches_pattern(const char *file, const char *pattern, int xplicit, const char *nextp) { const char *pptr = pattern; /* The pointer used to scan the pattern */ const char *fptr = file; /* The pointer used to scan the filename string */ /* * Match each character of the pattern in turn. */ while(pptr < nextp) { /* * Handle the next character of the pattern. */ switch(*pptr) { /* * A match zero-or-more characters wildcard operator. */ case '*': /* * Skip the '*' character in the pattern. */ pptr++; /* * If wildcards aren't allowed, the pattern doesn't match. */ if(xplicit) return 0; /* * If the pattern ends with a the '*' wildcard, then the * rest of the filename matches this. */ if(pptr >= nextp) return 1; /* * Using the wildcard to match successively longer sections of * the remaining characters of the filename, attempt to match * the tail of the filename against the tail of the pattern. */ for( ; *fptr; fptr++) { if(ef_string_matches_pattern(fptr, pptr, 0, nextp)) return 1; }; return 0; /* The pattern following the '*' didn't match */ break; /* * A match-one-character wildcard operator. */ case '?': /* * If there is a character to be matched, skip it and advance the * pattern pointer. */ if(!xplicit && *fptr) { fptr++; pptr++; /* * If we hit the end of the filename string, there is no character * matching the operator, so the string doesn't match. */ } else { return 0; }; break; /* * A character range operator, with the character ranges enclosed * in matching square brackets. */ case '[': if(xplicit || !ef_matches_range(*fptr++, ++pptr, &pptr)) return 0; break; /* * A backslash in the pattern prevents the following character as * being seen as a special character. */ case '\\': pptr++; /* Note fallthrough to default */ /* * A normal character to be matched explicitly. */ default: if(*fptr == *pptr) { fptr++; pptr++; } else { return 0; }; break; }; /* * After passing the first character, turn off the explicit match * requirement. */ xplicit = 0; }; /* * To get here the pattern must have been exhausted. If the filename * string matched, then the filename string must also have been * exhausted. */ return *fptr == '\0'; } /*....................................................................... * Match a character range expression terminated by an unescaped close * square bracket. * * Input: * c int The character to be matched with the range * pattern. * pattern const char * The range pattern to be matched (ie. after the * initiating '[' character). * endp const char ** On output a pointer to the character following the * range expression will be assigned to *endp. * Output: * return int 0 - Doesn't match. * 1 - The character matched. */ static int ef_matches_range(int c, const char *pattern, const char **endp) { const char *pptr = pattern; /* The pointer used to scan the pattern */ int invert = 0; /* True to invert the sense of the match */ int matched = 0; /* True if the character matched the pattern */ /* * If the first character is a caret, the sense of the match is * inverted and only if the character isn't one of those in the * range, do we say that it matches. */ if(*pptr == '^') { pptr++; invert = 1; }; /* * The hyphen is only a special character when it follows the first * character of the range (not including the caret). */ if(*pptr == '-') { pptr++; if(c == '-') { *endp = pptr; matched = 1; }; /* * Skip other leading '-' characters since they make no sense. */ while(*pptr == '-') pptr++; }; /* * The hyphen is only a special character when it follows the first * character of the range (not including the caret or a hyphen). */ if(*pptr == ']') { pptr++; if(c == ']') { *endp = pptr; matched = 1; }; }; /* * Having dealt with the characters that have special meanings at * the beginning of a character range expression, see if the * character matches any of the remaining characters of the range, * up until a terminating ']' character is seen. */ while(!matched && *pptr && *pptr != ']') { /* * Is this a range of characters signaled by the two end characters * separated by a hyphen? */ if(*pptr == '-') { if(pptr[1] != ']') { if(c >= pptr[-1] && c <= pptr[1]) matched = 1; pptr += 2; }; /* * A normal character to be compared directly. */ } else if(*pptr++ == c) { matched = 1; }; }; /* * Find the terminating ']'. */ while(*pptr && *pptr != ']') pptr++; /* * Did we find a terminating ']'? */ if(*pptr == ']') { *endp = pptr + 1; return matched ? !invert : invert; }; /* * If the pattern didn't end with a ']' then it doesn't match, regardless * of the value of the required sense of the match. */ *endp = pptr; return 0; } /*....................................................................... * This is a qsort() comparison function used to sort strings. * * Input: * v1, v2 void * Pointers to the two strings to be compared. * Output: * return int -1 -> v1 < v2. * 0 -> v1 == v2 * 1 -> v1 > v2 */ static int ef_cmp_strings(const void *v1, const void *v2) { char * const *s1 = (char * const *) v1; char * const *s2 = (char * const *) v2; return strcmp(*s1, *s2); } /*....................................................................... * Preprocess a path, expanding ~/, ~user/ and $envvar references, using * ef->path as a work buffer, then copy the result into a cache entry, * and return a pointer to this copy. * * Input: * ef ExpandFile * The resource object of the file matcher. * pathlen int The length of the prefix of path[] to be expanded. * Output: * return char * A pointer to a copy of the output path in the * cache. On error NULL is returned, and a description * of the error is left in ef->err. */ static char *ef_expand_special(ExpandFile *ef, const char *path, int pathlen) { int spos; /* The index of the start of the path segment that needs */ /* to be copied from path[] to the output pathname. */ int ppos; /* The index of a character in path[] */ char *pptr; /* A pointer into the output path */ int escaped; /* True if the previous character was a '\' */ int i; /* * Clear the pathname buffer. */ _pn_clear_path(ef->path); /* * We need to perform two passes, one to expand environment variables * and a second to do tilde expansion. This caters for the case * where an initial dollar expansion yields a tilde expression. */ escaped = 0; for(spos=ppos=0; ppos < pathlen; ppos++) { int c = path[ppos]; if(escaped) { escaped = 0; } else if(c == '\\') { escaped = 1; } else if(c == '$') { int envlen; /* The length of the environment variable */ char *value; /* The value of the environment variable */ /* * Record the preceding unrecorded part of the pathname. */ if(spos < ppos && _pn_append_to_path(ef->path, path + spos, ppos-spos, 0) == NULL) { _err_record_msg(ef->err, "Insufficient memory to expand path", END_ERR_MSG); return NULL; }; /* * Skip the dollar. */ ppos++; /* * Copy the environment variable name that follows the dollar into * ef->envnam[], stopping if a directory separator or end of string * is seen. */ for(envlen=0; envlenenvnam[envlen] = path[ppos++]; /* * If the username overflowed the buffer, treat it as invalid (note that * on most unix systems only 8 characters are allowed in a username, * whereas our ENV_LEN is much bigger than that. */ if(envlen >= ENV_LEN) { _err_record_msg(ef->err, "Environment variable name too long", END_ERR_MSG); return NULL; }; /* * Terminate the environment variable name. */ ef->envnam[envlen] = '\0'; /* * Lookup the value of the environment variable. */ value = getenv(ef->envnam); if(!value) { _err_record_msg(ef->err, "No expansion found for: $", ef->envnam, END_ERR_MSG); return NULL; }; /* * Copy the value of the environment variable into the output pathname. */ if(_pn_append_to_path(ef->path, value, -1, 0) == NULL) { _err_record_msg(ef->err, "Insufficient memory to expand path", END_ERR_MSG); return NULL; }; /* * Record the start of the uncopied tail of the input pathname. */ spos = ppos; }; }; /* * Record the uncopied tail of the pathname. */ if(spos < ppos && _pn_append_to_path(ef->path, path + spos, ppos-spos, 0) == NULL) { _err_record_msg(ef->err, "Insufficient memory to expand path", END_ERR_MSG); return NULL; }; /* * If the first character of the resulting pathname is a tilde, * then attempt to substitute the home directory of the specified user. */ pptr = ef->path->name; if(*pptr == '~' && path[0] != '\\') { int usrlen; /* The length of the username following the tilde */ const char *homedir; /* The home directory of the user */ int homelen; /* The length of the home directory string */ int plen; /* The current length of the path */ int skip=0; /* The number of characters to skip after the ~user */ /* * Get the current length of the output path. */ plen = strlen(ef->path->name); /* * Skip the tilde. */ pptr++; /* * Copy the optional username that follows the tilde into ef->usrnam[]. */ for(usrlen=0; usrlenusrnam[usrlen] = *pptr++; /* * If the username overflowed the buffer, treat it as invalid (note that * on most unix systems only 8 characters are allowed in a username, * whereas our USR_LEN is much bigger than that. */ if(usrlen >= USR_LEN) { _err_record_msg(ef->err, "Username too long", END_ERR_MSG); return NULL; }; /* * Terminate the username string. */ ef->usrnam[usrlen] = '\0'; /* * Lookup the home directory of the user. */ homedir = _hd_lookup_home_dir(ef->home, ef->usrnam); if(!homedir) { _err_record_msg(ef->err, _hd_last_home_dir_error(ef->home), END_ERR_MSG); return NULL; }; homelen = strlen(homedir); /* * ~user and ~ are usually followed by a directory separator to * separate them from the file contained in the home directory. * If the home directory is the root directory, then we don't want * to follow the home directory by a directory separator, so we must * erase it. */ if(strcmp(homedir, FS_ROOT_DIR) == 0 && strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { skip = FS_DIR_SEP_LEN; }; /* * If needed, increase the size of the pathname buffer to allow it * to accomodate the home directory instead of the tilde expression. * Note that pptr may not be valid after this call. */ if(_pn_resize_path(ef->path, plen - usrlen - 1 - skip + homelen)==NULL) { _err_record_msg(ef->err, "Insufficient memory to expand filename", END_ERR_MSG); return NULL; }; /* * Move the part of the pathname that follows the tilde expression to * the end of where the home directory will need to be inserted. */ memmove(ef->path->name + homelen, ef->path->name + 1 + usrlen + skip, plen - usrlen - 1 - skip+1); /* * Write the home directory at the beginning of the string. */ for(i=0; ipath->name[i] = homedir[i]; }; /* * Copy the result into the cache, and return a pointer to the copy. */ return ef_cache_pathname(ef, ef->path->name, 0); } /*....................................................................... * Return a description of the last path-expansion error that occurred. * * Input: * ef ExpandFile * The path-expansion resource object. * Output: * return char * The description of the last error. */ const char *ef_last_error(ExpandFile *ef) { return ef ? _err_get_msg(ef->err) : "NULL ExpandFile argument"; } /*....................................................................... * Print out an array of matching files. * * Input: * result FileExpansion * The container of the sorted array of * expansions. * fp FILE * The output stream to write to. * term_width int The width of the terminal. * Output: * return int 0 - OK. * 1 - Error. */ int ef_list_expansions(FileExpansion *result, FILE *fp, int term_width) { return _ef_output_expansions(result, _io_write_stdio, fp, term_width); } /*....................................................................... * Print out an array of matching files via a callback. * * Input: * result FileExpansion * The container of the sorted array of * expansions. * write_fn GlWriteFn * The function to call to write the * expansions or 0 to discard the output. * data void * Anonymous data to pass to write_fn(). * term_width int The width of the terminal. * Output: * return int 0 - OK. * 1 - Error. */ int _ef_output_expansions(FileExpansion *result, GlWriteFn *write_fn, void *data, int term_width) { EfListFormat fmt; /* List formatting information */ int lnum; /* The sequential number of the line to print next */ /* * Not enough space to list anything? */ if(term_width < 1) return 0; /* * Do we have a callback to write via, and any expansions to be listed? */ if(write_fn && result && result->nfile>0) { /* * Work out how to arrange the listing into fixed sized columns. */ ef_plan_listing(result, term_width, &fmt); /* * Print the listing to the specified stream. */ for(lnum=0; lnum < fmt.nline; lnum++) { if(ef_format_line(result, &fmt, lnum, write_fn, data)) return 1; }; }; return 0; } /*....................................................................... * Work out how to arrange a given array of completions into a listing * of one or more fixed size columns. * * Input: * result FileExpansion * The set of completions to be listed. * term_width int The width of the terminal. A lower limit of * zero is quietly enforced. * Input/Output: * fmt EfListFormat * The formatting information will be assigned * to the members of *fmt. */ static void ef_plan_listing(FileExpansion *result, int term_width, EfListFormat *fmt) { int maxlen; /* The length of the longest matching string */ int i; /* * Ensure that term_width >= 0. */ if(term_width < 0) term_width = 0; /* * Start by assuming the worst case, that either nothing will fit * on the screen, or that there are no matches to be listed. */ fmt->term_width = term_width; fmt->column_width = 0; fmt->nline = fmt->ncol = 0; /* * Work out the maximum length of the matching strings. */ maxlen = 0; for(i=0; infile; i++) { int len = strlen(result->files[i]); if(len > maxlen) maxlen = len; }; /* * Nothing to list? */ if(maxlen == 0) return; /* * Split the available terminal width into columns of * maxlen + EF_COL_SEP characters. */ fmt->column_width = maxlen; fmt->ncol = fmt->term_width / (fmt->column_width + EF_COL_SEP); /* * If the column width is greater than the terminal width, zero columns * will have been selected. Set a lower limit of one column. Leave it * up to the caller how to deal with completions who's widths exceed * the available terminal width. */ if(fmt->ncol < 1) fmt->ncol = 1; /* * How many lines of output will be needed? */ fmt->nline = (result->nfile + fmt->ncol - 1) / fmt->ncol; return; } /*....................................................................... * Render one line of a multi-column listing of completions, using a * callback function to pass the output to an arbitrary destination. * * Input: * result FileExpansion * The container of the sorted array of * completions. * fmt EfListFormat * Formatting information. * lnum int The index of the line to print, starting * from 0, and incrementing until the return * value indicates that there is nothing more * to be printed. * write_fn GlWriteFn * The function to call to write the line, or * 0 to discard the output. * data void * Anonymous data to pass to write_fn(). * Output: * return int 0 - Line printed ok. * 1 - Nothing to print. */ static int ef_format_line(FileExpansion *result, EfListFormat *fmt, int lnum, GlWriteFn *write_fn, void *data) { int col; /* The index of the list column being output */ /* * If the line index is out of bounds, there is nothing to be written. */ if(lnum < 0 || lnum >= fmt->nline) return 1; /* * If no output function has been provided, return as though the line * had been printed. */ if(!write_fn) return 0; /* * Print the matches in 'ncol' columns, sorted in line order within each * column. */ for(col=0; col < fmt->ncol; col++) { int m = col*fmt->nline + lnum; /* * Is there another match to be written? Note that in general * the last line of a listing will have fewer filled columns * than the initial lines. */ if(m < result->nfile) { char *file = result->files[m]; /* * How long are the completion and type-suffix strings? */ int flen = strlen(file); /* * Write the completion string. */ if(write_fn(data, file, flen) != flen) return 1; /* * If another column follows the current one, pad to its start with spaces. */ if(col+1 < fmt->ncol) { /* * The following constant string of spaces is used to pad the output. */ static const char spaces[] = " "; static const int nspace = sizeof(spaces) - 1; /* * Pad to the next column, using as few sub-strings of the spaces[] * array as possible. */ int npad = fmt->column_width + EF_COL_SEP - flen; while(npad>0) { int n = npad > nspace ? nspace : npad; if(write_fn(data, spaces + nspace - n, n) != n) return 1; npad -= n; }; }; }; }; /* * Start a new line. */ { char s[] = "\r\n"; int n = strlen(s); if(write_fn(data, s, n) != n) return 1; }; return 0; } #endif /* ifndef WITHOUT_FILE_SYSTEM */ yuma123_2.14/libtecla/html/0000775000175000017500000000000014770023131015646 5ustar vladimirvladimiryuma123_2.14/libtecla/html/gl_get_line.html0000664000175000017500000030171514770023131021013 0ustar vladimirvladimir Manual Page
gl_get_line                          gl_get_line



NAME

       gl_get_line,    new_GetLine,    del_GetLine,   gl_customize_completion,
       gl_change_terminal, gl_configure_getline, gl_load_history, gl_save_his-
       tory,   gl_group_history,   gl_show_history,  gl_watch_fd,  gl_inactiv-
       ity_timeout,  gl_terminal_size,  gl_set_term_size,   gl_resize_history,
       gl_limit_history,  gl_clear_history,  gl_toggle_history, gl_lookup_his-
       tory,  gl_state_of_history,  gl_range_of_history,   gl_size_of_history,
       gl_echo_mode,   gl_replace_prompt,  gl_prompt_style,  gl_ignore_signal,
       gl_trap_signal, gl_last_signal, gl_completion_action,  gl_display_text,
       gl_return_status,  gl_error_message, gl_catch_blocked, gl_list_signals,
       gl_bind_keyseq, gl_erase_terminal, gl_automatic_history, gl_append_his-
       tory,  gl_query_char, gl_read_char - allow the user to compose an input
       line

SYNOPSIS

       #include <stdio.h>
       #include <libtecla.h>

       GetLine *new_GetLine(size_t linelen, size_t histlen);

       GetLine *del_GetLine(GetLine *gl);

       char *gl_get_line(GetLine *gl, const char *prompt,
                         const char *start_line, int start_pos);

       int gl_query_char(GetLine *gl, const char *prompt,
                         char defchar);

       int gl_read_char(GetLine *gl);

       int gl_customize_completion(GetLine *gl, void *data,
                                   CplMatchFn *match_fn);

       int gl_change_terminal(GetLine *gl, FILE *input_fp,
                              FILE *output_fp, const char *term);

       int gl_configure_getline(GetLine *gl,
                                const char *app_string,
                                const char *app_file,
                                const char *user_file);

       int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin,
                          const char *keyseq, const char *action);

       int gl_save_history(GetLine *gl, const char *filename,
                           const char *comment, int max_lines);

       int gl_load_history(GetLine *gl, const char *filename,
                           const char *comment);

       int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event,
                       GlFdEventFn *callback, void *data);

       int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *callback,
                          void *data, unsigned long sec,
                          unsigned long nsec);

       int gl_group_history(GetLine *gl, unsigned stream);

       int gl_show_history(GetLine *gl, FILE *fp,
                           const char *fmt, int all_groups,
                           int max_lines);

       int gl_resize_history(GetLine *gl, size_t bufsize);

       void gl_limit_history(GetLine *gl, int max_lines);

       void gl_clear_history(GetLine *gl, int all_groups);

       void gl_toggle_history(GetLine *gl, int enable);

       GlTerminalSize gl_terminal_size(GetLine *gl,
                                       int def_ncolumn,
                                       int def_nline);

       int gl_set_term_size(GetLine *gl, int ncolumn, int nline);

       int gl_lookup_history(GetLine *gl, unsigned long id,
                             GlHistoryLine *hline);

       void gl_state_of_history(GetLine *gl,
                                GlHistoryState *state);

       void gl_range_of_history(GetLine *gl,
                                GlHistoryRange *range);

       void gl_size_of_history(GetLine *gl, GlHistorySize *size);

       void gl_echo_mode(GetLine *gl, int enable);

       void gl_replace_prompt(GetLine *gl, const char *prompt);

       void gl_prompt_style(GetLine *gl, GlPromptStyle style);

       int gl_ignore_signal(GetLine *gl, int signo);

       int gl_trap_signal(GetLine *gl, int signo, unsigned flags,
                          GlAfterSignal after, int errno_value);

       int gl_last_signal(GetLine *gl);

       int gl_completion_action(GetLine *gl,
                                void *data, CplMatchFn *match_fn,
                                int list_only, const char *name,
                                const char *keyseq);

       int gl_register_action(GetLine *gl, void *data,
                              GlActionFn *fn, const char *name,
                              const char *keyseq);

       int gl_display_text(GetLine *gl, int indentation,
                           const char *prefix,
                           const char *suffix, int fill_char,
                           int def_width, int start,
                           const char *string);

       GlReturnStatus gl_return_status(GetLine *gl);

       const char *gl_error_message(GetLine *gl, char *buff,
                                    size_t n);

       void gl_catch_blocked(GetLine *gl);

       int gl_list_signals(GetLine *gl, sigset_t *set);

       int gl_append_history(GetLine *gl, const char *line);

       int gl_automatic_history(GetLine *gl, int enable);



DESCRIPTION

       The gl_get_line() function is part of the tecla library (see the libte-
       cla(@LIBR_MANEXT@) man page). If the user is typing at a terminal, each
       call prompts them for an line of input, then provides interactive edit-
       ing facilities, similar to those of the unix tcsh shell. In addition to
       simple command-line editing, it supports recall of  previously  entered
       command  lines,  TAB  completion  of  file names, and in-line wild-card
       expansion of filenames. Documentation of both the  user-level  command-
       line  editing features and all user configuration options, can be found
       in the tecla man page. This  man  page  concerns  itself
       with  documentation for programmers interested in using this library in
       their application.


AN EXAMPLE

       The following shows a complete example of how to use the  gl_get_line()
       function to get input from the user:

         #include <stdio.h>
         #include <locale.h>
         #include <libtecla.h>

         int main(int argc, char *argv[])
         {
           char *line;    /* The line that the user typed */
           GetLine *gl;   /* The gl_get_line() resource object */

           setlocale(LC_CTYPE, ""); /* Adopt the user's choice */
                                    /* of character set. */

           gl = new_GetLine(1024, 2048);
           if(!gl)
             return 1;

           while((line=gl_get_line(gl, "$ ", NULL, -1)) != NULL &&
                  strcmp(line, "exit\n") != 0)
             printf("You typed: %s\n", line);

           gl = del_GetLine(gl);
           return 0;
         }

       In  the  example, first the resources needed by the gl_get_line() func-
       tion are created by calling new_GetLine(). This  allocates  the  memory
       used  in  subsequent calls to the gl_get_line() function, including the
       history buffer for recording previously entered lines. Then one or more
       lines are read from the user, until either an error occurs, or the user
       types exit. Then finally the resources that were allocated by  new_Get-
       Line(),  are  returned to the system by calling del_GetLine(). Note the
       use of the NULL return value of del_GetLine() to make gl NULL. This  is
       a safety precaution. If the program subsequently attempts to pass gl to
       gl_get_line(), said  function  will  complain,  and  return  an  error,
       instead of attempting to use the deleted resource object.



THE FUNCTIONS USED IN THE EXAMPLE

       The descriptions of the functions used in the example are as follows:

         GetLine *new_GetLine(size_t linelen, size_t histlen)

       This  function creates the resources used by the gl_get_line() function
       and returns an opaque pointer to the object that  contains  them.   The
       maximum  length of an input line is specified via the linelen argument,
       and the number of bytes to allocate for storing history lines is set by
       the histlen argument. History lines are stored back-to-back in a single
       buffer of this size. Note that this means that the  number  of  history
       lines  that  can be stored at any given time, depends on the lengths of
       the individual lines.  If you want to place an upper limit on the  num-
       ber  of  lines  that can be stored, see the gl_limit_history() function
       described later. If you don't want history at all, specify  histlen  as
       zero, and no history buffer will be allocated.

       On error, a message is printed to stderr and NULL is returned.

         GetLine *del_GetLine(GetLine *gl)

       This  function  deletes  the resources that were returned by a previous
       call to new_GetLine(). It always returns NULL (ie a deleted object). It
       does nothing if the gl argument is NULL.

         char *gl_get_line(GetLine *gl, const char *prompt,
                          const char *start_line, int start_pos);

       The  gl_get_line()  function  can be called any number of times to read
       input from the user. The gl argument must have been previously returned
       by  a call to new_GetLine(). The prompt argument should be a normal NUL
       terminated string, specifying the prompt to present the user  with.  By
       default  prompts  are  displayed  literally,  but  if  enabled with the
       gl_prompt_style() function (see later), prompts can contain  directives
       to  do underlining, switch to and from bold fonts, or turn highlighting
       on and off.

       If you want to specify the initial contents of the line, for  the  user
       to  edit,  pass the desired string via the start_line argument. You can
       then specify which character of this line the cursor is initially posi-
       tioned  over,  using  the  start_pos argument. This should be -1 if you
       want the cursor to follow the last character of the start line. If  you
       don't want to preload the line in this manner, send start_line as NULL,
       and set start_pos to -1. Note that the line  pointer  returned  by  one
       call  to  gl_get_line()  can  be  passed  back  to  the  next  call  to
       gl_get_line() via the start_line. This allows the application  to  take
       the  last entered line, and if it contains an error, to then present it
       back to the user for re-editing, with the cursor  initially  positioned
       where the error was encountered.

       The gl_get_line() function returns a pointer to the line entered by the
       user, or NULL on error or at the end of the input. The returned pointer
       is  part  of  the  specified gl resource object, and thus should not be
       free'd by the caller, or assumed to be unchanging from one call to  the
       next.  When  reading  from a user at a terminal, there will always be a
       newline character at the end of the returned line.  When standard input
       is being taken from a pipe or a file, there will similarly be a newline
       unless the input line was too long to store in the internal buffer.  In
       the latter case you should call gl_get_line() again to read the rest of
       the line. Note  that  this  behavior  makes  gl_get_line()  similar  to
       fgets().    In   fact   when   stdin   isn't   connected  to  a  termi-
       nal,gl_get_line() just calls fgets().


THE RETURN STATUS OF GL_GET_LINE

       As described above, the gl_get_line() function has two possible  return
       values;  a pointer to the completed input line, or NULL. Extra informa-
       tion about what caused gl_get_line() to return  is  available  both  by
       inspecting errno, and by calling the gl_return_status() function.


         GlReturnStatus gl_return_status(GetLine *gl);


       The  following  are  the  possible enumerated values that this function
       returns.


         GLR_NEWLINE     -  The last call to gl_get_line()
                            successfully returned a completed
                            input line.

         GLR_BLOCKED     -  gl_get_line() was in non-blocking
                            server mode, and returned early to
                            avoid blocking the process while
                            waiting for terminal I/O. The
                            gl_pending_io() function can be
                            used to see what type of I/O
                            gl_get_line() was waiting for.
                            (see the gl_io_mode man page
                            for details).

         GLR_SIGNAL      -  A signal was caught by
                            gl_get_line() that had an
                            after-signal disposition of
                            GLS_ABORT (See gl_trap_signal()).

         GLR_TIMEOUT     -  The inactivity timer expired while
                            gl_get_line() was waiting for
                            input, and the timeout callback
                            function returned GLTO_ABORT.
                            See gl_inactivity_timeout() for
                            information about timeouts.

         GLR_FDABORT     -  An application I/O callack returned
                            GLFD_ABORT (see gl_watch_fd()).

         GLR_EOF         -  End of file reached. This can happen
                            when input is coming from a file or a
                            pipe, instead of the terminal. It also
                            occurs if the user invokes the
                            list-or-eof or del-char-or-list-or-eof
                            actions at the start of a new line.

         GLR_ERROR       -  An unexpected error caused
                            gl_get_line() to abort (consult
                            errno and/or
                            gl_error_message() for details.


       When gl_return_status() returns GLR_ERROR, and the value of errno isn't
       sufficient to explain what happened, you can use the gl_error_message()
       function to request a description of the last error that occurred.


         const char *gl_error_message(GetLine *gl, char *buff,
                                      size_t n);


       The return value is a pointer to the message that occurred. If the buff
       argument  is  NULL, this will be a pointer to a buffer within gl, who's
       value will probably change on the next call to any function  associated
       with gl_get_line(). Otherwise, if a non-NULL buff argument is provided,
       the error message, including a '\0' terminator, will be written  within
       the  first  n  elements  of this buffer, and the return value will be a
       pointer to the first element of this buffer. If the message  won't  fit
       in the provided buffer, it will be truncated to fit.


OPTIONAL PROMPT FORMATTING

       Whereas by default the prompt string that you specify is displayed lit-
       erally, without any special interpretation of the characters within it,
       the  gl_prompt_style()  function can be used to enable optional format-
       ting directives within the prompt.

         void gl_prompt_style(GetLine *gl, GlPromptStyle style);

       The style argument, which specifies the formatting style, can take  any
       of the following values:

         GL_FORMAT_PROMPT   -  In this style, the formatting
                               directives described below, when
                               included in prompt strings, are
                               interpreted as follows:

                                 %B  -  Display subsequent
                                        characters with a bold
                                        font.
                                 %b  -  Stop displaying characters
                                        with the bold font.
                                 %F  -  Make subsequent characters
                                        flash.
                                 %f  -  Turn off flashing
                                        characters.
                                 %U  -  Underline subsequent
                                        characters.
                                 %u  -  Stop underlining
                                        characters.
                                 %P  -  Switch to a pale (half
                                        brightness) font.
                                 %p  -  Stop using the pale font.
                                 %S  -  Highlight subsequent
                                        characters (also known as
                                        standout mode).
                                 %s  -  Stop highlighting
                                        characters.
                                 %V  -  Turn on reverse video.
                                 %v  -  Turn off reverse video.
                                 %%  -  Display a single %
                                        character.

                               For example, in this mode, a prompt
                               string like "%UOK%u$ " would
                               display the prompt "OK$ ",
                               but with the OK part
                               underlined.

                               Note that although a pair of
                               characters that starts with a %
                               character, but doesn't match any of
                               the above directives is displayed
                               literally, if a new directive is
                               subsequently introduced which does
                               match, the displayed prompt will
                               change, so it is better to always
                               use %% to display a literal %.

                               Also note that not all terminals
                               support all of these text
                               attributes, and that some substitute
                               a different attribute for missing
                               ones.

         GL_LITERAL_PROMPT  -  In this style, the prompt string is
                               printed literally. This is the
                               default style.


ALTERNATE CONFIGURATION SOURCES

       As mentioned above, by default users have the option of configuring the
       behavior of gl_get_line() via a configuration file called  .teclarc  in
       their  home directories. The fact that all applications share this same
       configuration file is both an advantage and a  disadvantage.   In  most
       cases it is an advantage, since it encourages uniformity, and frees the
       user from having to configure each  application  separately.   In  some
       applications, however, this single means of configuration is a problem.
       This is particularly  true  of  embedded  software,  where  there's  no
       filesystem  to read a configuration file from, and also in applications
       where a radically different choice of keybindings is needed to  emulate
       a  legacy  keyboard  interface.  To cater for such cases, the following
       function allows the application to control where configuration informa-
       tion is read from.


         int gl_configure_getline(GetLine *gl,
                                  const char *app_string,
                                  const char *app_file,
                                  const char *user_file);


       It allows the configuration commands that would normally be read from a
       user's ~/.teclarc file, to be read from any or none of,  a  string,  an
       application specific configuration file, and/or a user-specific config-
       uration file. If this function is  called  before  the  first  call  to
       gl_get_line(),  the default behavior of reading ~/.teclarc on the first
       call to  gl_get_line()  is  disabled,  so  all  configuration  must  be
       achieved  using the configuration sources specified with this function.

       If app_string != NULL, then it is interpreted as  a  string  containing
       one  or  more  configuration commands, separated from each other in the
       string by embedded newline characters. If app_file != NULL then  it  is
       interpreted  as the full pathname of an application-specific configura-
       tion file. If user_file != NULL then it  is  interpreted  as  the  full
       pathname of a user-specific configuration file, such as ~/.teclarc. For
       example, in the following call,


         gl_configure_getline(gl, "edit-mode vi \n nobeep",
                                  "/usr/share/myapp/teclarc",
                                  "~/.teclarc");


       the app_string argument causes the calling application to start  in  vi
       edit-mode,  instead of the default emacs mode, and turns off the use of
       the terminal bell by the library. It then attempts to read  system-wide
       configuration     commands     from    an    optional    file    called
       /usr/share/myapp/teclarc, then finally reads  user-specific  configura-
       tion  commands from an optional .teclarc file in the user's home direc-
       tory. Note that the arguments are listed in ascending order  of  prior-
       ity,  with  the  contents  of app_string being potentially overriden by
       commands in app_file, and commands in app_file potentially being  over-
       riden by commands in user_file.

       You  can  call this function as many times as needed, the results being
       cumulative, but note that copies of any  filenames  specified  via  the
       app_file and user_file arguments are recorded internally for subsequent
       use by the read-init-files key-binding function, so if you plan to call
       this  function multiple times, be sure that the last call specifies the
       filenames that you want re-read when the user requests that the config-
       uration files be re-read.

       Individual  key  sequences  can  also  be  bound  and unbound using the
       gl_bind_keyseq() function.


         int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin,
                            const char *keyseq,
                            const char *action);


       The origin argument specifies the priority of the binding, according to
       who  it  is being established for, and must be one of the following two
       values.

         GL_USER_KEY   -   The user requested this key-binding.
         GL_APP_KEY    -   This is a default binding set by the
                           application.

       When both user and application bindings for a given  key-sequence  have
       been  specified,  the  user binding takes precedence. The application's
       binding is subsequently reinstated  if  the  user's  binding  is  later
       unbound  via  either  another to this function, or a call to gl_config-
       ure_getline().

       The keyseq argument specifies the key-sequence to be bound or  unbound,
       and is expressed in the same way as in a ~/.teclarc configuration file.
       The action argument must either be a string containing the name of  the
       action  to bind the key-sequence to, or it must be NULL or "" to unbind
       the key-sequence.


CUSTOMIZED WORD COMPLETION

       If in your application, you would like to have TAB completion  complete
       other  things  in  addition to or instead of filenames, you can arrange
       this by registering an alternate completion callback  function,  via  a
       call to the gl_customize_completion() function.

         int gl_customize_completion(GetLine *gl, void *data,
                                     CplMatchFn *match_fn);

       The  data  argument  provides  a way for your application to pass arbi-
       trary, application-specific information to the callback function.  This
       is  passed  to  the callback every time that it is called. It might for
       example, point to the symbol table from which possible completions  are
       to  be sought. The match_fn argument specifies the callback function to
       be called. The CplMatchFn function type is defined in libtecla.h, as is
       a  CPL_MATCH_FN() macro that you can use to declare and prototype call-
       back functions. The declaration and responsibilities of callback  func-
       tions  are  described  in depth in the cpl_complete_word
       man page.

       In brief, the callback function is responsible for looking backwards in
       the  input  line, back from the point at which the user pressed TAB, to
       find the start of the word being completed. It then must lookup  possi-
       ble  completions  of this word, and record them one by one in the Word-
       Completion object that is passed to it as an argument, by  calling  the
       cpl_add_completion()  function. If the callback function wishes to pro-
       vide filename completion in addition to its own  specific  completions,
       it  has  the  option of itself calling the builtin file-name completion
       callback.    This    also,    is    documented    in    the    cpl_com-
       plete_word(@FUNC_MANEXT@) man page.

       Note  that  if you would like gl_get_line() to return the current input
       line when a successful completion is been made, you  can  arrange  this
       when you call cpl_add_completion(), by making the last character of the
       continuation suffix a newline character. If you do this, the input line
       will   be   updated  to  display  the  completion,  together  with  any
       contiuation suffix up to the newline character, then gl_get_line() will
       return this input line.


       If, for some reason, your callback function needs to write something to
       the terminal, it must call gl_normal_io() before doing  so.  This  will
       start  a  new line after the input line that is currently being edited,
       reinstate normal terminal I/O, and tell gl_get_line()  that  the  input
       line will need to be redrawn when the callback returns.


ADDING COMPLETION ACTIONS

       In  the  previous  section the ability to customize the behavior of the
       only default completion action, complete-word, was described.  In  this
       section  the  ability  to  install additional action functions, so that
       different types of word completion  can  be  bound  to  different  key-
       sequences,  is  described.  This  is  achieved  by using the gl_comple-
       tion_action() function.


         int gl_completion_action(GetLine *gl,
                                  void *data, CplMatchFn *match_fn,
                                  int list_only, const char *name,
                                  const char *keyseq);


       The data and match_fn  arguments  are  as  described  in  the  cpl_com-
       plete_word  man  page, and specify the callback function that should be
       invoked to  identify  possible  completions.   The  list_only  argument
       determines  whether  the action that is being defined should attempt to
       complete the word as far as possible in the input line before  display-
       ing  any  possible  ambiguous  completions, or whether it should simply
       display the list of possible completions  without  touching  the  input
       line. The former option is selected by specifying a value of 0, and the
       latter by specifying a value of 1. The name argument specifies the name
       by  which  configuration  files and future invokations of this function
       should refer to the action. This must either be the name of an existing
       completion  action  to  be  changed,  or be a new unused name for a new
       action. Finally, the keyseq argument specifies the default key-sequence
       to  bind  the  action  to.  If this is NULL, no new keysequence will be
       bound to the action.

       Beware that in order for the user to be able to change the key-sequence
       that  is  bound  to actions that are installed in this manner, when you
       call gl_completion_action() to install a given  action  for  the  first
       time,  you  should  do this between calling new_GetLine() and the first
       call to gl_get_line().  Otherwise, when the user's  configuration  file
       is  read on the first call to gl_get_line(), the name of the your addi-
       tional action won't be known, and any reference to it in the configura-
       tion file will generate an error.

       As  discussed for gl_customize_completion(), if your callback function,
       for some reason, needs to write anything to the terminal, it must  call
       gl_normal_io() before doing so.


DEFINING CUSTOM ACTIONS

       Although  the built-in key-binding actions are sufficient for the needs
       of most applications, occasionally a specialized application  may  need
       to  define  one  or  more custom actions, bound to application-specific
       key-sequences. For example, a sales application would benefit from hav-
       ing  a key-sequence that displayed the part name that corresponded to a
       part number preceding the cursor. Such a feature is clearly beyond  the
       scope  of the built-in action functions. So for such special cases, the
       gl_register_action() function is provided.


         int gl_register_action(GetLine *gl, void *data,
                       GlActionFn *fn, const char *name,
                       const char *keyseq);


       This function lets the application register an external  function,  fn,
       that  will  thereafter  be  called  whenever  either the specified key-
       sequence, keyseq, is entered by the user, or the user enters any  other
       key-sequence  that  the user subsequently binds to the specified action
       name, name, in their configuration file. The data  argument  can  be  a
       pointer  to  anything that the application wishes to have passed to the
       action function, fn, whenever that function is invoked.

       The action function, fn, should be declared using the following  macro,
       which is defined in libtecla.h.


         #define GL_ACTION_FN(fn) GlAfterAction (fn)(GetLine *gl, \
                     void *data, int count, size_t curpos, \
                     const char *line)


       The  gl  and  data  arguments  are those that were previously passed to
       gl_register_action() when the action function was registered. The count
       argument  is a numeric argument which the user has the option of enter-
       ing using the digit-argument action, before invoking the action. If the
       user doesn't enter a number, then the count argument is set to 1. Nomi-
       nally this argument is interpreted as a repeat count, meaning that  the
       action  should  be  repeated  that many times. In practice however, for
       some actions a repeat count makes little sense. In such cases,  actions
       can  either  simply  ignore  the count argument, or use its value for a
       different purpose.

       A copy of the current input line is passed in the read-only line  argu-
       ment.  The  current  cursor position within this string is given by the
       index contained in the curpos argument. Note that  direct  manipulation
       of  the  input  line  and the cursor position is not permitted. This is
       because the rules dicated by various modes,  such  as  vi  mode  versus
       emacs  mode,  no-echo mode, and insert mode versus overstrike mode etc,
       make it too complex for an application writer  to  write  a  conforming
       editing action, as well as constrain future changes to the internals of
       gl_get_line(). A potential solution to this dilema would  be  to  allow
       the  action  function  to  edit  the  line  using  the existing editing
       actions. This is currently under consideration.

       If the action function wishes to write text to  the  terminal,  without
       this  getting  mixed  up  with the displayed text of the input line, or
       read from the terminal without having to handle raw terminal I/O,  then
       before  doing  either  of these operations, it must temporarily suspend
       line editing by calling  the  gl_normal_io()  function.  This  function
       flushes  any  pending  output  to the terminal, moves the cursor to the
       start of the line that follows the last  terminal  line  of  the  input
       line,  then  restores  the terminal to a state that is suitable for use
       with the C stdio facilities. The latter includes such things as restor-
       ing the normal mapping of \n to \r\n, and, when in server mode, restor-
       ing the normal blocking form of terminal I/O. Having called this  func-
       tion, the action function can read from and write to the terminal with-
       out the fear of creating a mess.  It isn't  necessary  for  the  action
       function to restore the original editing environment before it returns.
       This is done automatically by gl_get_line() after the  action  function
       returns.  The following is a simple example of an action function which
       writes the sentence "Hello world" on a new terminal line after the line
       being  edited. When this function returns, the input line is redrawn on
       the line that follows the "Hello world" line, and line editing resumes.


         static GL_ACTION_FN(say_hello_fn)
         {
           if(gl_normal_io(gl))   /* Temporarily suspend editing */
             return GLA_ABORT;
           printf("Hello world\n");
           return GLA_CONTINUE;
         }


       Action  functions  must  return  one  of  the following values, to tell
       gl_get_line() how to procede.


         GLA_ABORT     -   Cause gl_get_line() to return NULL.
         GLA_RETURN    -   Cause gl_get_line() to return the
                           completed input line.
         GLA_CONTINUE  -   Resume command-line editing.


       Note that the name argument of gl_register_action() specifies the  name
       by  which  a  user can refer to the action in their configuration file.
       This allows them to re-bind the action to an alternate key-seqeunce. In
       order  for  this  to work, it is necessary to call gl_register_action()
       between calling new_GetLine() and the first call to gl_get_line().


HISTORY FILES

       To save the contents of the history buffer before quitting your  appli-
       cation,  and subsequently restore them when you next start the applica-
       tion, the following functions are provided.


        int gl_save_history(GetLine *gl, const char *filename,
                            const char *comment, int max_lines);
        int gl_load_history(GetLine *gl, const char *filename,
                            const char *comment);


       The filename argument specifies the name to give the history file  when
       saving, or the name of an existing history file, when loading. This may
       contain home-directory and environment variable  expressions,  such  as
       "~/.myapp_history" or "$HOME/.myapp_history".

       Along  with each history line, extra information about it, such as when
       it was entered by the user, and what its nesting level is, is  recorded
       as  a comment preceding the line in the history file. Writing this as a
       comment allows the history file to double as a command  file,  just  in
       case  you  wish  to replay a whole session using it. Since comment pre-
       fixes differ in different languages, the comment argument  is  provided
       for  specifying  the  comment  prefix. For example, if your application
       were a unix shell, such as the bourne  shell,  you  would  specify  "#"
       here.  Whatever  you choose for the comment character, you must specify
       the same prefix to gl_load_history() that  you  used  when  you  called
       gl_save_history() to write the history file.

       The  max_lines  must be either -1 to specify that all lines in the his-
       tory list be saved, or a positive number specifying a  ceiling  on  how
       many of the most recent lines should be saved.

       Both  fuctions return non-zero on error, after writing an error message
       to stderr. Note that gl_load_history() does not consider the  non-exis-
       tence of a file to be an error.


MULTIPLE HISTORY LISTS

       If your application uses a single GetLine object for entering many dif-
       ferent types of input lines, you may wish gl_get_line() to  distinguish
       the different types of lines in the history list, and only recall lines
       that match the current type  of  line.  To  support  this  requirement,
       gl_get_line()  marks  lines  being recorded in the history list with an
       integer identifier chosen by the application.  Initially  this  identi-
       fier  is  set to 0 by new_GetLine(), but it can be changed subsequently
       by calling gl_group_history().


         int gl_group_history(GetLine *gl, unsigned id);


       The integer identifier id can be any number chosen by the  application,
       but  note  that  gl_save_history()  and  gl_load_history() preserve the
       association between identifiers and historical input lines between pro-
       gram  invokations,  so you should choose fixed identifiers for the dif-
       ferent types of input line used by your application.

       Whenever gl_get_line() appends a new input line to  the  history  list,
       the  current  history  identifier  is  recorded with it, and when it is
       asked to recall a historical input line, it only recalls lines that are
       marked with the current identifier.


DISPLAYING HISTORY

       The history list can be displayed by calling gl_show_history().


         int gl_show_history(GetLine *gl, FILE *fp,
                             const char *fmt,
                             int all_groups,
                             int max_lines);


       This  displays  the  current  contents of the history list to the stdio
       output stream fp. If the max_lines argument is greater than or equal to
       zero,  then  no  more than this number of the most recent lines will be
       displayed. If the all_groups argument is non-zero, lines from all  his-
       tory  groups  are  displayed.  Otherwise  just  those  of the currently
       selected history group are displayed. The format string argument,  fmt,
       determines  how the line is displayed. This can contain arbitrary char-
       acters which are written verbatim, interleaved with any of the  follow-
       ing format directives:

         %D  -  The date on which the line was originally
                entered, formatted like 2001-11-20.
         %T  -  The time of day when the line was entered,
                formatted like 23:59:59.
         %N  -  The sequential entry number of the line in
                the history buffer.
         %G  -  The number of the history group which the
                line belongs to.
         %%  -  A literal % character.
         %H  -  The history line itself.

       Thus a format string like "%D %T  %H0 would output something like:

         2001-11-20 10:23:34  Hello world

       Note  the  inclusion  of  an  explicit  newline character in the format
       string.


LOOKING UP HISTORY

       The gl_lookup_history() function allows the calling application to look
       up lines in the history list.


         typedef struct {
           const char *line;    /* The requested historical */
                                /*  line. */
           unsigned group;      /* The history group to which */
                                /*  the line belongs. */
           time_t timestamp;    /* The date and time at which */
                                /*  the line was originally */
                                /*  entered. */
         } GlHistoryLine;

         int gl_lookup_history(GetLine *gl, unsigned long id,
                               GlHistoryLine *hline);


       The  id  argument indicates which line to look up, where the first line
       that was entered in the history list after new_GetLine() was called, is
       denoted  by  0, and subsequently entered lines are denoted with succes-
       sively higher numbers. Note that the range of lines currently preserved
       in the history list can be queried by calling the gl_range_of_history()
       function, described later. If the requested  line  is  in  the  history
       list,  the  details of the line are recorded in the variable pointed to
       by the hline argument, and 1 is returned. Otherwise 0 is returned,  and
       the variable pointed to by hline is left unchanged.

       Beware  that  the string returned in hline->line is part of the history
       buffer, so it must not be modified by the caller, and will be  recycled
       on  the next call to any function that takes gl as its argument. There-
       fore you should make a private copy of this string if you need to  keep
       it around.


MANUAL HISTORY ARCHIVAL

       By default, whenever a line is entered by the user, it is automatically
       appended to the history list, just  before  gl_get_line()  returns  the
       line  to  the  caller.  This is convenient for the majority of applica-
       tions, but there are also applications that need finer grained  control
       over  what gets added to the history list. In such cases, the automatic
       addition of entered lines to the history list  can  be  turned  off  by
       calling the gl_automatic_history() function.


         int gl_automatic_history(GetLine *gl, int enable);


       If  this  function  is  called  with  its  enable  argument  set  to 0,
       gl_get_line() won't automatically archive subsequently  entered  lines.
       Automatic  archiving  can be reenabled at a later time, by calling this
       function again, with its enable argument set  to  1.   While  automatic
       history  archiving  is  disabled,  the  calling application can use the
       gl_append_history() to append lines to the history list as needed.


         int gl_append_history(GetLine *gl, const char *line);


       The line argument specifies the line to be added to the  history  list.
       This  must  be  a normal ' ' terminated string. If this string contains
       any newline characters, the line that gets archived in the history list
       will  be  terminated by the first of these. Otherwise it will be termi-
       nated by the ' ' terminator.  If the line is longer  than  the  maximum
       input  line  length,  that was specified when new_GetLine() was called,
       when the line  is  recalled,  it  will  get  truncated  to  the  actual
       gl_get_line() line length.

       If successful, gl_append_history() returns 0. Otherwise it returns non-
       zero, and sets errno to one of the following values.


          EINVAL  -  One of the arguments passed to
                     gl_append_history() was NULL.
          ENOMEM  -  The specified line was longer than the allocated
                     size of the history buffer (as specified when
                     new_GetLine() was called), so it couldn't be
                     archived.


       A textual description of the error can optionally be obtained by  call-
       ing gl_error_message(). Note that after such an error, the history list
       remains in a valid state to receive new history lines, so there is lit-
       tle harm in simply ignoring the return status of gl_append_history().


MISCELLANEOUS HISTORY CONFIGURATION

       If  you  wish  to change the size of the history buffer that was origi-
       nally specified in the call to new_GetLine(), you can do  so  with  the
       gl_resize_history() function.


         int gl_resize_history(GetLine *gl, size_t histlen);


       The  histlen argument specifies the new size in bytes, and if you spec-
       ify this as 0, the buffer will be deleted.

       As mentioned in the discussion of new_GetLine(), the  number  of  lines
       that can be stored in the history buffer, depends on the lengths of the
       individual lines. For example, a 1000 byte buffer could  equally  store
       10  lines  of average length 100 bytes, or 2 lines of average length 50
       bytes. Although the buffer is never expanded when new lines are  added,
       a  list  of  pointers  into the buffer does get expanded when needed to
       accomodate the number of lines currently stored in the buffer. To place
       an upper limit on the number of lines in the buffer, and thus a ceiling
       on  the  amount  of  memory  used  in  this  list,  you  can  call  the
       gl_limit_history() function.


         void gl_limit_history(GetLine *gl, int max_lines);


       The  max_lines  should  either be a positive number >= 0, specifying an
       upper limit on the number of lines in the buffer, or be  -1  to  cancel
       any  previously  specified  limit.  When a limit is in effect, only the
       max_lines most recently appended lines are kept in  the  buffer.  Older
       lines are discarded.

       To  discard  lines  from the history buffer, use the gl_clear_history()
       function.

         void gl_clear_history(GetLine *gl, int all_groups);

       The all_groups argument tells the function whether to delete  just  the
       lines  associated  with  the  current  history group (see gl_group_his-
       tory()), or all historical lines in the buffer.

       The gl_toggle_history() function allows you to toggle  history  on  and
       off without losing the current contents of the history list.


         void gl_toggle_history(GetLine *gl, int enable);


       Setting  the  enable argument to 0 turns off the history mechanism, and
       setting it to 1 turns it back on. When history is turned  off,  no  new
       lines  will  be added to the history list, and history lookup key-bind-
       ings will act as though there is nothing in the history buffer.


QUERYING HISTORY INFORMATION

       The configured state of the  history  list  can  be  queried  with  the
       gl_history_state() function.


         typedef struct {
           int enabled;     /* True if history is enabled */
           unsigned group;  /* The current history group */
           int max_lines;   /* The current upper limit on the */
                            /*  number of lines in the history */
                            /*  list, or -1 if unlimited. */
         } GlHistoryState;

         void gl_state_of_history(GetLine *gl,
                                  GlHistoryState *state);

       On  return,  the status information is recorded in the variable pointed
       to by the state argument.

       The gl_range_of_history() function returns  the  number  and  range  of
       lines in the history list.


       typedef struct {
         unsigned long oldest;  /* The sequential entry number */
                                /*  of the oldest line in the */
                                /*  history list. */
         unsigned long newest;  /* The sequential entry number */
                                /*  of the newest line in the */
                                /*  history list. */
         int nlines;            /* The number of lines in the */
                                /*  history list. */
       } GlHistoryRange;

       void gl_range_of_history(GetLine *gl, GlHistoryRange *range);

       The  return values are recorded in the variable pointed to by the range
       argument. If the nlines member of this structure is greater than  zero,
       then  the  oldest  and  newest members report the range of lines in the
       list, and newest=oldest+nlines-1.  Otherwise they are both zero.

       The gl_size_of_history() function returns the total size of the history
       buffer and the amount of the buffer that is currently occupied.

         typedef struct {
           size_t size;      /* The size of the history buffer */
                             /*  (bytes). */
           size_t used;      /* The number of bytes of the */
                             /*  history buffer that are */
                             /*  currently occupied. */
         } GlHistorySize;

         void gl_size_of_history(GetLine *gl, GlHistorySize *size);

       On  return, the size information is recorded in the variable pointed to
       by the size argument.


CHANGING TERMINALS

       The new_GetLine() constructor function assumes that input is to be read
       from stdin, and output written to stdout. The following function allows
       you to switch to different input and output streams.

         int gl_change_terminal(GetLine *gl, FILE *input_fp,
                                FILE *output_fp, const char *term);

       The gl argument is the object that was returned by new_GetLine().   The
       input_fp  argument  specifies  the  stream  to read from, and output_fp
       specifies the stream to be written to. Only if both of these refer to a
       terminal,  will  interactive  terminal  input  be  enabled.   Otherwise
       gl_get_line() will simply call fgets() to read command input.  If  both
       streams refer to a terminal, then they must refer to the same terminal,
       and the type of this terminal must be specified via the term  argument.
       The value of the term argument is looked up in the terminal information
       database (terminfo or termcap), in order  to  determine  which  special
       control  sequences  are needed to control various aspects of the termi-
       nal.  new_GetLine()  for  example,   passes   the   return   value   of
       getenv("TERM")  in  this argument. Note that if one or both of input_fp
       and output_fp don't refer to a terminal, then it is legal to pass  NULL
       instead of a terminal type.

       Note that if you want to pass file descriptors to gl_change_terminal(),
       you can do this by creating  stdio  stream  wrappers  using  the  POSIX
       fdopen() function.


EXTERNAL EVENT HANDLING

       By  default, gl_get_line() doesn't return until either a complete input
       line has been entered by the user, or an error occurs. In programs that
       need  to  watch for I/O from other sources than the terminal, there are
       two options.


         1. Use the functions described in the
            gl_io_mode man page to switch
            gl_get_line() into non-blocking server mode. In this mode,
            gl_get_line() becomes a non-blocking, incremental
            line-editing function that can safely be called from
            an external event loop. Although this is a very
            versatile method, it involves taking on some
            responsibilities that are normally performed behind
            the scenes by gl_get_line().

         2. While gl_get_line() is waiting for keyboard
            input from the user, you can ask it to also watch for
            activity on arbitrary file descriptors, such as
            network sockets, pipes etc, and have it call functions
            of your choosing when activity is seen. This works on
            any system that has the select() system call,
            which is most, if not all flavors of unix.


       Registering a file descriptor to be watched by  gl_get_line()  involves
       calling the gl_watch_fd() function.


         int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event,
                         GlFdEventFn *callback, void *data);


       If  this returns non-zero, then it means that either your arguments are
       invalid, or that this facility isn't supported on the host system.

       The fd argument is the file descriptor to be watched. The  event  argu-
       ment  specifies  what  type of activity is of interest, chosen from the
       following enumerated values:


         GLFD_READ   -  Watch for the arrival of data to be read.
         GLFD_WRITE  -  Watch for the ability to write to the file
                        descriptor without blocking.
         GLFD_URGENT -  Watch for the arrival of urgent
                        out-of-band data on the file descriptor.


       The callback argument  is  the  function  to  call  when  the  selected
       activity  is seen. It should be defined with the following macro, which
       is defined in libtecla.h.


         #define GL_FD_EVENT_FN(fn) GlFdStatus (fn)(GetLine *gl, \
                                             void *data, int fd, \
                                             GlFdEvent event)

       The data argument of the gl_watch_fd() function is passed to the  call-
       back  function  for  its  own  use, and can point to anything you like,
       including NULL. The file descriptor and the  event  argument  are  also
       passed  to  the callback function, and this potentially allows the same
       callback function to be registered to  more  than  one  type  of  event
       and/or  more than one file descriptor. The return value of the callback
       function should be one of the following values.


         GLFD_ABORT    -  Tell gl_get_line() to abort. When this
                          happens, gl_get_line() returns
                          NULL, and a following call to
                          gl_return_status() will return
                          GLR_FDABORT. Note that if the
                          application needs errno always to
                          have a meaningful value when
                          gl_get_line() returns NULL,
                          the callback function should set
                          errno appropriately.
         GLFD_REFRESH  -  Redraw the input line then continue
                          waiting for input. Return this if
                          your callback wrote to the terminal.
         GLFD_CONTINUE -  Continue to wait for input, without
                          redrawing the line.

       Note that before calling the callback, gl_get_line() blocks  most  sig-
       nals,  and  leaves its own signal handlers installed, so if you need to
       catch a particular signal you will need  to  both  temporarily  install
       your  own  signal  handler, and unblock the signal. Be sure to re-block
       the signal (if it was originally blocked) and  reinstate  the  original
       signal handler, if any, before returning.



       If  the  callback  function  needs to read or write to the terminal, it
       should ideally first call gl_normal_io(gl) to temporarily suspend  line
       editing.  This  will  restore  the terminal to canonical, blocking-I/O,
       mode, and move the cursor to the start of a new terminal  line.  Later,
       when  the  callback  returns,  gl_get_line()  will  notice that gl_nor-
       mal_io() was called, redisplay the input line and resume editing.  Note
       that in this case the return values, GLFD_REFRESH and GLFD_CONTINUE are
       equivalent.



       To support cases where the callback function calls a third-party  func-
       tion  which  occasionally and u0prisicre-enabledesbeforee themicallback
       automatic conversion of "0 to "
       function  is called. If the callack knows that the third-party function
       wrote to the terminal, it should then return  the  GLFD_REFRESH  return
       value, to tell gl_get_line() to redisplay the input line.



       To  remove  a  callback  function  that you previously registered for a
       given file descriptor and event, simply  call  gl_watch_fd()  with  the
       same  file descriptor and event arguments, but with a callback argument
       of 0. The data argument is ignored in this case.


SETTING AN INACTIVITY TIMEOUT

       On systems with the select() system call,  the  gl_inactivity_timeout()
       function can be used to set or cancel an inactivity timeout. Inactivity
       in this case refers both to keyboard input, and  to  I/O  on  any  file
       descriptors  registered by prior and subsequent calls to gl_watch_fd().
       On oddball systems that don't have select(), this call has no effect.


         int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *callback,
                            void *data, unsigned long sec,
                            unsigned long nsec);


       The timeout is specified in the form of an integral number  of  seconds
       and  an  integral number of nanoseconds, via the sec and nsec arguments
       respectively. Subsequently, whenever no activity is seen for this  time
       period, the function specified via the callback argument is called. The
       data argument of gl_inactivity_timeout() is  passed  verbatim  to  this
       callback  function whenever it is invoked, and can thus be used to pass
       arbitrary application-specific information to the callback. The follow-
       ing  macro is provided in libtecla.h for applications to use to declare
       and prototype timeout callback functions.


         #define GL_TIMEOUT_FN(fn) \
                      GlAfterTimeout (fn)(GetLine *gl, void *data)


       On returning, the application's callback is expected to return  one  of
       the  following  enumerators  to tell gl_get_line() how to procede after
       the timeout has been handled by the callback.


         GLTO_ABORT    -  Tell gl_get_line() to abort. When
                          this happens, gl_get_line() will
                          return NULL, and a following call
                          to gl_return_status() will return
                          GLR_TIMEOUT. Note that if the
                          application needs errno always to
                          have a meaningful value when
                          gl_get_line() returns NULL,
                          the callback function should set
                          errno appropriately.
         GLTO_REFRESH  -  Redraw the input line, then continue
                          waiting for input. You should return
                          this value if your callback wrote to the
                          terminal without having first called
                          gl_normal_io(gl).
         GLTO_CONTINUE -  In normal blocking-I/O mode, continue to
                          wait for input, without redrawing the
                          user's input line.
                          In non-blocking server I/O mode (see
                          gl_io_mode), cause gl_get_line()
                          to act as though I/O blocked. This means
                          that gl_get_line() will immediately
                          return NULL, and a following call
                          to gl_return_status() will return
                          GLR_BLOCKED.


       Note that before calling the callback, gl_get_line() blocks  most  sig-
       nals,  and  leaves its own signal handlers installed, so if you need to
       catch a particular signal you will need  to  both  temporarily  install
       your  own  signal  handler, and unblock the signal. Be sure to re-block
       the signal (if it was originally blocked) and  reinstate  the  original
       signal handler, if any, before returning.



       If  the  callback  function  needs to read or write to the terminal, it
       should ideally first call gl_normal_io(gl) to temporarily suspend  line
       editing.  This  will  restore  the terminal to canonical, blocking-I/O,
       mode, and move the cursor to the start of a new terminal  line.  Later,
       when  the  callback  returns,  gl_get_line()  will  notice that gl_nor-
       mal_io() was called, redisplay the input line and resume editing.  Note
       that in this case the return values, GLTO_REFRESH and GLTO_CONTINUE are
       equivalent.



       To support cases where the callback function calls a third-party  func-
       tion  which  occasionally and u0prisicre-enabledesbeforee themicallback
       automatic conversion of "0 to "
       function  is called. If the callack knows that the third-party function
       wrote to the terminal, it should then return  the  GLTO_REFRESH  return
       value, to tell gl_get_line() to redisplay the input line.



       Note  that  although the timeout argument includes a nano-second compo-
       nent, few computer clocks presently have  resolutions  that  are  finer
       than  a few milliseconds, so asking for less than a few milliseconds is
       equivalent to requesting zero seconds on a  lot  of  systems.  If  this
       would  be  a  problem,  you  should  base your timeout selection on the
       actual   resolution   of   the   host    clock    (eg.    by    calling
       sysconf(_SC_CLK_TCK)).



       To  turn off timeouts, simply call gl_inactivity_timeout() with a call-
       back argument of 0. The data argument is ignored in this case.


SIGNAL HANDLING DEFAULTS

       By default, the gl_get_line() function intercepts a number of  signals.
       This  is particularly important for signals which would by default ter-
       minate the process, since the terminal needs to be restored to a usable
       state  before  this  happens.  In  this  section,  the signals that are
       trapped  by  default,  and  how  gl_get_line()  responds  to  them,  is
       described.  Changing  these defaults is the topic of the following sec-
       tion.

       When the following subset of signals are  caught,  gl_get_line()  first
       restores  the  terminal  settings  and signal handling to how they were
       before gl_get_line() was called, resends the signal, to allow the call-
       ing  application's  signal  handlers  to handle it, then if the process
       still exists, gl_get_line() returns NULL and sets  errno  as  specified
       below.


        SIGINT  -  This signal is generated both by the keyboard
                   interrupt key (usually ^C), and the keyboard
                   break key.

                   errno=EINTR

        SIGHUP  -  This signal is generated when the controlling
                   terminal exits.

                   errno=ENOTTY

        SIGPIPE -  This signal is generated when a program attempts
                   to write to a pipe who's remote end isn't being
                   read by any process. This can happen for example
                   if you have called gl_change_terminal() to
                   redirect output to a pipe hidden under a pseudo
                   terminal.

                   errno=EPIPE

        SIGQUIT -  This signal is generated by the keyboard quit
                   key (usually ^\).

                   errno=EINTR

        SIGABRT -  This signal is generated by the standard C,
                   abort() function. By default it both
                   terminates the process and generates a core
                   dump.

                   errno=EINTR

        SIGTERM -  This is the default signal that the UN*X
                   kill command sends to processes.

                   errno=EINTR

       Note  that in the case of all of the above signals, POSIX mandates that
       by default the process is terminated, with the addition of a core  dump
       in  the  case  of  the  SIGQUIT  signal. In other words, if the calling
       application doesn't override the default handler by supplying  its  own
       signal  handler, receipt of the corresponding signal will terminate the
       application before gl_get_line() returns.

       If gl_get_line() aborts with errno set to EINTR, you can find out  what
       signal caused it to abort, by calling the following function.

         int gl_last_signal(const GetLine *gl);

       This  returns the numeric code (eg. SIGINT) of the last signal that was
       received during the most recent call to gl_get_line(), or -1 if no sig-
       nals were received.

       On  systems  that support it, when a SIGWINCH (window change) signal is
       received, gl_get_line() queries the terminal to find out its new  size,
       redraws the current input line to accomodate the new size, then returns
       to waiting for keyboard input from the user. Unlike other signals, this
       signal isn't resent to the application.

       Finally, the following signals cause gl_get_line() to first restore the
       terminal  and  signal  environment  to  that  which  prevailed   before
       gl_get_line() was called, then resend the signal to the application. If
       the process still exists after the  signal  has  been  delivered,  then
       gl_get_line() then re-establishes its own signal handlers, switches the
       terminal back to raw mode, redisplays the input line, and goes back  to
       awaiting terminal input from the user.

        SIGCONT    -  This signal is generated when a suspended
                      process is resumed.

        SIGPOLL    -  On SVR4 systems, this signal notifies the
                      process of an asynchronous I/O event. Note
                      that under 4.3+BSD, SIGIO and SIGPOLL are
                      the same. On other systems, SIGIO is ignored
                      by default, so gl_get_line() doesn't
                      trap it by default.

        SIGPWR     -  This signal is generated when a power failure
                      occurs (presumably when the system is on a
                      UPS).

        SIGALRM    -  This signal is generated when a timer
                      expires.

        SIGUSR1    -  An application specific signal.

        SIGUSR2    -  Another application specific signal.

        SIGVTALRM  -  This signal is generated when a virtual
                      timer expires (see man setitimer(2)).

        SIGXCPU    -  This signal is generated when a process
                      exceeds its soft CPU time limit.

        SIGXFSZ    -  This signal is generated when a process
                      exceeds its soft file-size limit.

        SIGTSTP    -  This signal is generated by the terminal
                      suspend key, which is usually ^Z, or the
                      delayed terminal suspend key, which is
                      usually ^Y.

        SIGTTIN    -  This signal is generated if the program
                      attempts to read from the terminal while the
                      program is running in the background.

        SIGTTOU    -  This signal is generated if the program
                      attempts to write to the terminal while the
                      program is running in the background.


       Obviously not all of the above signals are supported on all systems, so
       code to support them is conditionally compiled into the tecla  library.

       Note  that  if SIGKILL or SIGPOLL, which by definition can't be caught,
       or any of the hardware generated exception signals,  such  as  SIGSEGV,
       SIGBUS  and  SIGFPE, are received and unhandled while gl_get_line() has
       the terminal in raw mode, the program will be  terminated  without  the
       terminal  having been restored to a usable state. In practice, job-con-
       trol shells usually reset the terminal settings when a  process  relin-
       quishes  the controlling terminal, so this is only a problem with older
       shells.


CUSTOMIZED SIGNAL HANDLING

       The previous section listed the signals  that  gl_get_line()  traps  by
       default,  and described how it responds to them. This section describes
       how to both add and remove signals from the list  of  trapped  signals,
       and  how to specify how gl_get_line() should respond to a given signal.

       If you don't need gl_get_line() to do anything in response to a  signal
       that  it  normally  traps, you can tell to gl_get_line() to ignore that
       signal by calling gl_ignore_signal().

         int gl_ignore_signal(GetLine *gl, int signo);

       The signo argument is the number of the signal (eg.  SIGINT)  that  you
       want  to  have  ignored. If the specified signal isn't currently one of
       those being trapped, this function does nothing.

       The gl_trap_signal() function allows you to either add a new signal  to
       the  list that gl_get_line() traps, or modify how it responds to a sig-
       nal that it already traps.

         int gl_trap_signal(GetLine *gl, int signo, unsigned flags,
                            GlAfterSignal after, int errno_value);

       The signo argument is the number of the signal that you  wish  to  have
       trapped. The flags argument is a set of flags which determine the envi-
       ronment in which the application's signal handler is invoked, the after
       argument  tells gl_get_line() what to do after the application's signal
       handler returns, and errno_value tells gl_get_line() what to set  errno
       to if told to abort.

       The  flags  argument  is  a bitwise OR of zero or more of the following
       enumerators:

         GLS_RESTORE_SIG  -  Restore the caller's signal
                             environment while handling the
                             signal.

         GLS_RESTORE_TTY  -  Restore the caller's terminal settings
                             while handling the signal.

         GLS_RESTORE_LINE -  Move the cursor to the start of the
                             line following the input line before
                             invoking the application's signal
                             handler.

         GLS_REDRAW_LINE  -  Redraw the input line when the
                             application's signal handler returns.

         GLS_UNBLOCK_SIG  -  Normally, if the calling program has
                             a signal blocked (man sigprocmask),
                             gl_get_line() does not trap that
                             signal. This flag tells gl_get_line()
                             to trap the signal and unblock it for
                             the duration of the call to
                             gl_get_line().

         GLS_DONT_FORWARD -  If this flag is included, the signal
                             will not be forwarded to the signal
                             handler of the calling program.

       Two commonly useful flag combinations are also enumerated as follows:

         GLS_RESTORE_ENV   = GLS_RESTORE_SIG | GLS_RESTORE_TTY |
                             GLS_REDRAW_LINE

         GLS_SUSPEND_INPUT = GLS_RESTORE_ENV | GLS_RESTORE_LINE


       If your signal handler, or the default system signal handler  for  this
       signal, if you haven't overridden it, never either writes to the termi-
       nal, nor suspends or terminates  the  calling  program,  then  you  can
       safely set the flags argument to 0.

       If your signal handler always writes to the terminal, reads from it, or
       suspends or terminates the program, you should specify the flags  argu-
       ment as GL_SUSPEND_INPUT, so that:

       1. The cursor doesn't get left in the middle of the input
          line.
       2. So that the user can type in input and have it echoed.
       3. So that you don't need to end each output line with
          \r\n, instead of just \n.

       The  GL_RESTORE_ENV combination is the same as GL_SUSPEND_INPUT, except
       that it doesn't move the cursor, and if  your  signal  handler  doesn't
       read  or write anything to the terminal, the user won't see any visible
       indication that a signal was caught. This can be useful if you  have  a
       signal  handler  that  only  occasionally writes to the terminal, where
       using GL_SUSPEND_LINE would cause the input line  to  be  unnecessarily
       duplicated  when nothing had been written to the terminal.  Such a sig-
       nal handler, when it does write to the  terminal,  should  be  sure  to
       start a new line at the start of its first write, by writing a new line
       before returning. If the signal arrives while the user  is  entering  a
       line  that only occupies a signal terminal line, or if the cursor is on
       the last terminal line of a longer input line, this will have the  same
       effect  as  GL_SUSPEND_INPUT. Otherwise it will start writing on a line
       that already contains part of the displayed input line.   This  doesn't
       do any harm, but it looks a bit ugly, which is why the GL_SUSPEND_INPUT
       combination is better if you know that you are always going to be writ-
       ting to the terminal.

       The  after argument, which determines what gl_get_line() does after the
       application's signal handler returns (if it returns), can take any  one
       of the following values:

         GLS_RETURN   - Return the completed input line, just as
                        though the user had pressed the return
                        key.

         GLS_ABORT    - Cause gl_get_line() to abort. When
                        this happens, gl_get_line() returns
                        NULL, and a following call to
                        gl_return_status() will return
                        GLR_SIGNAL. Note that if the
                        application needs errno always to
                        have a meaningful value when
                        gl_get_line() returns NULL,
                        the callback function should set
                        errno appropriately.
         GLS_CONTINUE - Resume command line editing.

       The  errno_value argument is intended to be combined with the GLS_ABORT
       option, telling gl_get_line() what to set the standard  errno  variable
       to  before returning NULL to the calling program. It can also, however,
       be used with the GL_RETURN option, in case you wish to have  a  way  to
       distinguish  between  an  input  line that was entered using the return
       key, and one that was entered by the receipt of a signal.


RELIABLE SIGNAL HANDLING

       Signal handling is suprisingly hard to do reliably without race  condi-
       tions.  In gl_get_line() a lot of care has been taken to allow applica-
       tions to perform reliable signal handling  around  gl_get_line().  This
       section explains how to make use of this.

       As  an  example of the problems that can arise if the application isn't
       written correctly, imagine that one's application has a  SIGINT  signal
       handler that sets a global flag. Now suppose that the application tests
       this flag just before invoking gl_get_line(). If a SIGINT  signal  hap-
       pens  to  be received in the small window of time between the statement
       that tests the value  of  this  flag,  and  the  statement  that  calls
       gl_get_line(), then gl_get_line() will not see the signal, and will not
       be interrupted. As a result, the application won't be able  to  respond
       to  the  signal  until  the  user gets around to finishing entering the
       input line and gl_get_line() returns.  Depending  on  the  application,
       this  might  or might not be a disaster, but at the very least it would
       puzzle the user.

       The way to avoid such problems is to do the following.

       1. If needed, use the gl_trap_signal() function to
          configure gl_get_line() to abort when important
          signals are caught.

       2. Configure gl_get_line() such that if any of the
          signals that it catches are blocked when
          gl_get_line() is called, they will be unblocked
          automatically during times when gl_get_line() is
          waiting for I/O. This can be done either
          on a per signal basis, by calling the
          gl_trap_signal() function, and specifying the
          GLS_UNBLOCK attribute of the signal, or globally by
          calling the gl_catch_blocked() function.


            void gl_catch_blocked(GetLine *gl);


          This function simply adds the GLS_UNBLOCK attribute
          to all of the signals that it is currently configured to
          trap.

       3. Just before calling gl_get_line(), block delivery
          of all of the signals that gl_get_line() is
          configured to trap. This can be done using the POSIX
          sigprocmask() function in conjunction with the
          gl_list_signals() function.


             int gl_list_signals(GetLine *gl, sigset_t *set);


          This function returns the set of signals that it is
          currently configured to catch in the set argument,
          which is in the form required by sigprocmask().

       4. In the example, one would now test the global flag that
          the signal handler sets, knowing that there is now no
          danger of this flag being set again until
          gl_get_line() unblocks its signals while performing
          I/O.

       5. Eventually gl_get_line() returns, either because
          a signal was caught, an error occurred, or the user
          finished entering their input line.

       6. Now one would check the global signal flag again, and if
          it is set, respond to it, and zero the flag.

       7. Use sigprocmask() to unblock the signals that were
          blocked in step 3.

       The same technique can be used around certain POSIX signal-aware  func-
       tions,  such  as  sigsetjmp()  and sigsuspend(), and in particular, the
       former of these two functions can be  used  in  conjunction  with  sig-
       longjmp() to implement race-condition free signal handling around other
       long-running system calls. The way to do this, is  explained  next,  by
       showing how gl_get_line() manages to reliably trap signals around calls
       to functions like read() and select() without race conditions.

       The first thing that gl_get_line() does, whenever it is called,  is  to
       use  the  POSIX  sigprocmask() function to block the delivery of all of
       the signals that it is currently configured to catch. This is redundant
       if  the  application  has already blocked them, but it does no harm. It
       undoes this step just before returning.

       Whenever gl_get_line() needs to call read() or  select()  to  wait  for
       input  from  the  user,  it first calls the POSIX sigsetjmp() function,
       being sure to specify a non-zero value for its savesigs argument.   The
       reason for the latter argument will become clear shortly.

       If sigsetjmp() returns zero, gl_get_line() then does the following.


       a. It uses the POSIX sigaction() function to register
          a temporary signal handler to all of the signals that it
          is configured to catch. This signal handler does two
          things.

          1. It records the number of the signal that was received
             in a file-scope variable.

          2. It then calls the POSIX siglongjmp()
             function using the buffer that was passed to
             sigsetjmp() for its first argument, and
             a non-zero value for its second argument.

          When this signal handler is registered, the sa_mask
          member of the struct sigaction act argument of the
          call to sigaction() is configured to contain all of
          the signals that gl_get_line() is catching. This
          ensures that only one signal will be caught at once by
          our signal handler, which in turn ensures that multiple
          instances of our signal handler don't tread on each
          other's toes.

       b. Now that the signal handler has been set up,
          gl_get_line() unblocks all of the signals that it
          is configured to catch.

       c. It then calls the read() or select() system
          calls to wait for keyboard input.

       d. If this system call returns (ie. no signal is received),
          gl_get_line() blocks delivery of the signals of
          interest again.

       e. It then reinstates the signal handlers that were
          displaced by the one that was just installed.


       Alternatively,  if sigsetjmp() returns non-zero, this means that one of
       the signals being trapped was caught while the above steps were execut-
       ing. When this happens, gl_get_line() does the following.

       First,  note  that  when  a  call to siglongjmp() causes sigsetjmp() to
       return, provided that the savesigs argument  of  sigsetjmp()  was  non-
       zero, as specified above, the signal process mask is restored to how it
       was when sigsetjmp() was  called.  This  is  the  important  difference
       between  sigsetjmp()  and  the  older  problematic setjmp(), and is the
       essential ingredient that makes it possible to  avoid  signal  handling
       race  conditions.   Because  of  this we are guaranteed that all of the
       signals that we blocked before calling sigsetjmp() are blocked again as
       soon  as any signal is caught. The following statements, which are then
       executed, are thus guaranteed to be executed without any  further  sig-
       nals being caught.

       1. If so instructed by the gl_get_line() configuration
          attributes of the signal that was caught,
          gl_get_line() restores the terminal attributes to
          the state that they had when gl_get_line() was
          called. This is particularly important for signals that
          suspend or terminate the process, since otherwise the
          terminal would be left in an unusable state.

       2. It then reinstates the application's signal handlers.

       3. Then it uses the C standard-library raise()
          function to re-send the application the signal that
          was caught.

       3. Next it unblocks delivery of the signal that we just
          sent. This results in the signal that was just sent
          via raise(), being caught by the application's
          original signal handler, which can now handle it as it
          sees fit.

       4. If the signal handler returns (ie. it doesn't terminate
          the process), gl_get_line() blocks delivery of the
          above signal again.

       5. It then undoes any actions performed in the first of the
          above steps, and redisplays the line, if the signal
          configuration calls for this.

       6. gl_get_line() then either resumes trying to
          read a character, or aborts, depending on the
          configuration of the signal that was caught.

       What  the above steps do in essence is to take asynchronously delivered
       signals and handle them synchronously, one at a time, at a point in the
       code where gl_get_line() has complete control over its environment.


THE TERMINAL SIZE

       On  most  systems  the combination of the TIOCGWINSZ ioctl and the SIG-
       WINCH signal is used to maintain an accurate idea of the terminal size.
       The  terminal  size  is  newly queried every time that gl_get_line() is
       called and whenever a SIGWINCH signal is received.

       On the few systems where this mechanism  isn't  available,  at  startup
       new_GetLine()  first  looks for the LINES and COLUMNS environment vari-
       ables.  If these aren't found, or they contain unusable values, then if
       a  terminal information database like terminfo or termcap is available,
       the default size of the terminal is looked up in this database. If this
       too fails to provide the terminal size, a default size of 80 columns by
       24 lines is used.

       Even on systems that do support ioctl(TIOCGWINSZ), if the  terminal  is
       on the other end of a serial line, the terminal driver generally has no
       way of detecting when a resize occurs or of querying what  the  current
       size  is.  In  such  cases  no SIGWINCH is sent to the process, and the
       dimensions returned by ioctl(TIOCGWINSZ) aren't correct. The  only  way
       to  handle  such  instances is to provide a way for the user to enter a
       command that tells the remote system what the new size is. This command
       would  then  call the gl_set_term_size() function to tell gl_get_line()
       about the change in size.


         int gl_set_term_size(GetLine *gl, int ncolumn, int nline);


       The ncolumn and nline arguments are used to specify the new  dimensions
       of  the  terminal, and must not be less than 1. On systems that do sup-
       port ioctl(TIOCGWINSZ), this function first calls ioctl(TIOCSWINSZ)  to
       tell  the  terminal  driver  about  the change in size. In non-blocking
       server-I/O mode, if a line is currently being input, the input line  is
       then redrawn to accomodate the changed size. Finally the new values are
       recorded in gl for future use by gl_get_line().

       The gl_terminal_size() function allows you to query the current size of
       the  terminal,  and  install an alternate fallback size for cases where
       the size isn't available.  Beware  that  the  terminal  size  won't  be
       available  if  reading from a pipe or a file, so the default values can
       be important even on systems that do support ways of  finding  out  the
       terminal size.

         typedef struct {
           int nline;        /* The terminal has nline lines */
           int ncolumn;      /* The terminal has ncolumn columns */
         } GlTerminalSize;

         GlTerminalSize gl_terminal_size(GetLine *gl,
                                         int def_ncolumn,
                                         int def_nline);

       This  function  first  updates gl_get_line()'s fallback terminal dimen-
       sions, then records its findings in the return value.

       The def_ncolumn and def_nline specify the default  number  of  terminal
       columns  and  lines to use if the terminal size can't be determined via
       ioctl(TIOCGWINSZ) or environment variables.


HIDING WHAT YOU TYPE

       When entering sensitive information, such as passwords, it is best  not
       to  have  the  text that you are entering echoed on the terminal.  Fur-
       thermore, such text should not be recorded in the history  list,  since
       somebody  finding  your  terminal  unattended  could then recall it, or
       somebody snooping through your directories could see it in your history
       file. With this in mind, the gl_echo_mode() function allows you to tog-
       gle on and off the display and archival of  any  text  that  is  subse-
       quently entered in calls to gl_get_line().


         int gl_echo_mode(GetLine *gl, int enable);


       The enable argument specifies whether entered text should be visible or
       not. If it is 0, then subsequently entered lines will not be visible on
       the terminal, and will not be recorded in the history list. If it is 1,
       then subsequent input lines will be displayed as they are entered,  and
       provided  that  history  hasn't  been  turned off via a call to gl_tog-
       gle_history(), then they will also be archived  in  the  history  list.
       Finally,  if  the  enable argument is -1, then the echoing mode is left
       unchanged, which allows you to non-destructively query the current set-
       ting  via the return value. In all cases, the return value of the func-
       tion is 0 if echoing was disabled before the function was called, and 1
       if it was enabled.

       When  echoing  is  turned  off,  note that although tab completion will
       invisibly complete your prefix as far as  possible,  ambiguous  comple-
       tions will not be displayed.


SINGLE CHARACTER QUERIES

       Using  gl_get_line() to query the user for a single character reply, is
       inconvenient for the user, since they must hit the enter or return  key
       before  the  character that they typed is returned to the program. Thus
       the gl_query_char() function has been  provided  for  single  character
       queries like this.


         int gl_query_char(GetLine *gl, const char *prompt,
                           char defchar);


       This function displays the specified prompt at the start of a new line,
       and waits for the user to type a character. When the user types a char-
       acter, gl_query_char() displays it to the right of the prompt, starts a
       newline, then returns the character to the calling program. The  return
       value  of the function is the character that was typed. If the read had
       to be aborted for some reason, EOF is returned instead. In  the  latter
       case, the application can call the previously documented gl_return_sta-
       tus(), to find out what went wrong. This could, for example, have  been
       the  reception of a signal, or the optional inactivity timer going off.

       If the user simply hits enter, the value of  the  defchar  argument  is
       substituted.  This  means  that  when  the  user hits either newline or
       return, the character specified in  defchar,  is  displayed  after  the
       prompt,  as  though the user had typed it, as well as being returned to
       the calling application. If such a replacement is not important, simply
       pass '0 as the value of defchar.

       If  the  entered character is an unprintable character, it is displayed
       symbolically. For example, control-A is displayed as ^A, and characters
       beyond 127 are displayed in octal, preceded by a backslash.

       As with gl_get_line(), echoing of the entered character can be disabled
       using the gl_echo_mode() function.

       If the calling process is suspended while waiting for the user to  type
       their  response,  the  cursor is moved to the line following the prompt
       line, then when the process resumes, the  prompt  is  redisplayed,  and
       gl_query_char() resumes waiting for the user to type a character.

       Note that in non-blocking server mode, (see gl_io_mode),
       if an incomplete input line is  in  the  process  of  being  read  when
       gl_query_char()  is  called,  the  partial input line is discarded, and
       erased from the terminal, before the new prompt is displayed. The  next
       call to gl_get_line() will thus start editing a new line.


READING RAW CHARACTERS

       Whereas  the  gl_query_char()  function  visibly prompts the user for a
       character, and displays what they typed,  the  gl_read_char()  function
       reads a signal character from the user, without writing anything to the
       terminal, or perturbing any incompletely entered input line. This means
       that it can be called not only from between calls to gl_get_line(), but
       also from callback functions that the application has registered to  be
       called by gl_get_line().


         int gl_read_char(GetLine *gl);


       On  success,  the  return value of gl_read_char() is the character that
       was read. On failure, EOF is returned, and the gl_return_status() func-
       tion  can  be called to find out what went wrong. Possibilities include
       the optional inactivity timer going off, the receipt of a  signal  that
       is configured to abort gl_get_line(), or terminal I/O blocking, when in
       non-blocking server-I/O mode.

       Beware that certain keyboard keys, such as function  keys,  and  cursor
       keys,  usually generate at least 3 characters each, so a single call to
       gl_read_char() won't be enough to identify such keystrokes.


CLEARING THE TERMINAL

       The calling program can clear the terminal by  calling  gl_erase_termi-
       nal(). In non-blocking server-I/O mode, this function also arranges for
       the current input line to be redrawn from scratch when gl_get_line() is
       next called.


         int gl_erase_terminal(GetLine *gl);



DISPLAYING TEXT DYNAMICALLY

       Between calls to gl_get_line(), the gl_display_text() function provides
       a convenient way to display  paragraphs  of  text,  left-justified  and
       split  over  one or more terminal lines according to the constraints of
       the current width of the terminal. Examples of the use of this function
       may  be  found in the demo programs, where it is used to display intro-
       ductions. In those examples the advanced use of optional prefixes, suf-
       fixes  and  filled  lines  to draw a box around the text is also illus-
       trated.


         int gl_display_text(GetLine *gl, int indentation,
                             const char *prefix,
                             const char *suffix, int fill_char,
                             int def_width, int start,
                             const char *string);

       If gl isn't currently connected to a terminal, for example if the  out-
       put of a program that uses gl_get_line() is being piped to another pro-
       gram or redirected to a file, then the value of the def_width parameter
       is used as the terminal width.

       The  indentation  argument specifies the number of characters to use to
       indent each line of ouput. The fill_char argument specifies the charac-
       ter that will be used to perform this indentation.

       The  prefix argument can either be NULL, or be a string to place at the
       beginning of each new line (after  any  indentation).   Similarly,  the
       suffix  argument can either be NULL, or be a string to place at the end
       of each line. The suffix is placed flush against the right edge of  the
       terminal,  and  any space between its first character and the last word
       on that line is filled with the character specified via  the  fill_char
       argument.  Normally the fill-character is a space.

       The  start  argument  tells  gl_display_text() how many characters have
       already been written to the current terminal line, and  thus  tells  it
       the  starting  column  index  of the cursor.  Since the return value of
       gl_display_text() is the ending column index of the cursor, by  passing
       the  return value of one call to the start argument of the next call, a
       paragraph that is broken between more than one string can  be  composed
       by  calling  gl_display_text() for each successive portion of the para-
       graph. Note that literal newline characters are necessary at the end of
       each paragraph to force a new line to be started.

       On error, gl_display_text() returns -1.


CALLBACK FUNCTION FACILITIES

       Unless  otherwise  stated,  callback  functions, such as tab completion
       callbacks and event callbacks should not call  any  functions  in  this
       module.  The following functions, however, are designed specifically to
       be used by callback functions.

       Calling  the  gl_replace_prompt()  function  from  a   callback   tells
       gl_get_line()  to display a different prompt when the callback returns.
       Except in non-blocking server mode, it has no effect  if  used  between
       calls   to   gl_get_line().   In  non-blocking  server  mode  (see  the
       gl_io_mode man page, when  used  between  two  calls  to
       gl_get_line()  that  are  operating on the same input line, the current
       input line will be re-drawn with the new prompt on the  following  call
       to gl_get_line().


         void gl_replace_prompt(GetLine *gl, const char *prompt);



INTERNATIONAL CHARACTER SETS

       Since  libtecla version 1.4.0, gl_get_line() has been 8-bit clean. This
       means that all 8-bit characters that are printable in the  user's  cur-
       rent  locale  are  now  displayed verbatim and included in the returned
       input line.  Assuming that the calling  program  correctly  contains  a
       call like the following,

         setlocale(LC_CTYPE, "");

       then  the  current locale is determined by the first of the environment
       variables LC_CTYPE, LC_ALL, and LANG, that is found to contain a  valid
       locale  name.  If  none  of these variables are defined, or the program
       neglects to call setlocale, then the default C locale is used, which is
       US  7-bit  ASCII.  On  most  unix-like platforms, you can get a list of
       valid locales by typing the command:

         locale -a

       at the shell prompt. Further documentation on how the user can make use
       of  this  to  enter  international  characters  can  be  found  in  the
       tecla man page.


THREAD SAFETY

       In a multi-threaded program, you should use the libtecla_r.a version of
       the  library.  This  uses reentrant versions of system functions, where
       available. Unfortunately neither terminfo nor termcap were designed  to
       be reentrant, so you can't safely use the functions of the getline mod-
       ule in multiple threads (you can use the  separate  file-expansion  and
       word-completion  modules in multiple threads, see the corresponding man
       pages for details). However due to the use of POSIX reentrant functions
       for looking up home directories etc, it is safe to use this module from
       a single thread of a multi-threaded program, provided that  your  other
       threads don't use any termcap or terminfo functions.


FILES

       libtecla.a      -    The tecla library
       libtecla.h      -    The tecla header file.
       ~/.teclarc      -    The personal tecla customization file.


SEE ALSO

       libtecla, gl_io_mode, tecla, ef_expand_file,
       cpl_complete_word, pca_lookup_file


AUTHOR

       Martin Shepherd  (mcs@astro.caltech.edu)



                                                    gl_get_line
yuma123_2.14/libtecla/html/enhance.html0000664000175000017500000000635714770023131020150 0ustar vladimirvladimir Manual Page
enhance                                  enhance



NAME

       enhance  - A program that adds command-line editing to third party pro-
       grams.

SYNOPSIS

       enhance command [ argument ... ]


DESCRIPTION

       The enhance program provides enhanced command-line  editing  facilities
       to  users  of  third  party applications, to which one doesn't have any
       source code. It does this by  placing  a  pseudo-terminal  between  the
       application and the real terminal. It uses the tecla command-line edit-
       ing library to read input from the real terminal,  then  forwards  each
       just  completed  input line to the application via the pseudo-terminal.
       All output from the application is forwarded back unchanged to the real
       terminal.

       Whenever  the application stops generating output for more than a tenth
       of a second, the enhance program treats the  latest  incomplete  output
       line  as the prompt, and redisplays any incompleted input line that the
       user has typed after it. Note that the small delay, which is  impercep-
       tible  to  the  user, isn't necessary for correct operation of the pro-
       gram. It is just an optimization, designed to stop the input line  from
       being redisplayed so often that it slows down output.

       Note  that  the  user-level command-line editing facilities provided by
       the Tecla library are documented in the tecla man page


DEFICIENCIES

       The one major problem that hasn't been solved yet, is how to deal  with
       applications  that  change  whether typed input is echo'd by their con-
       trolling terminal. For example, programs that ask for a password,  such
       as  ftp  and telnet, temporarily tell their controlling terminal not to
       echo what the user types. Since this request goes  to  the  application
       side  of the psuedo terminal, the enhance program has no way of knowing
       that this has happened, and continues to echo typed input to  its  con-
       trolling terminal, while the user types their password.

       Furthermore, before executing the host application, the enhance program
       initially sets the pseudo terminal to noecho mode, so  that  everything
       that  it sends to the program doesn't get redundantly echoed. If a pro-
       gram that switches to noecho mode explicitly  restores  echoing  after-
       wards, rather than restoring the terminal modes that were previously in
       force, then subsequently, every time that you enter a new input line, a
       duplicate copy will be displayed on the next line.


FILES

       libtecla.a    -   The tecla library.
       ~/.teclarc    -   The tecla personal customization file.


SEE ALSO

       tecla, libtecla


AUTHOR

       Martin Shepherd  (mcs@astro.caltech.edu)



                                                        enhance
yuma123_2.14/libtecla/html/tecla.html0000664000175000017500000016247414770023131017642 0ustar vladimirvladimir Manual Page
tecla                                      tecla



NAME

       tecla, teclarc - The user interface provided by the Tecla library.

DESCRIPTION

       This  man  page  describes  the  command-line editing features that are
       available to users of programs that read keyboard input via  the  Tecla
       library.  Users  of  the  tcsh shell will find the default key-bindings
       very familiar. Users of the bash shell will also find it  quite  famil-
       iar,  but with a few minor differences, most notably in how forward and
       backward searches through the list  of  historical  commands  are  per-
       formed.  There  are  two  major editing modes, one with emacs-like key-
       bindings and another with vi-like key-bindings. By default  emacs  mode
       is  enabled,  but  vi mode can alternatively be selected via the user's
       configuration file. This file can also be used to change  the  bindings
       of individual keys to suit the user's preferences. By default, tab com-
       pletion is provided. If the application  hasn't  reconfigured  this  to
       complete  other  types  of symbols, then tab completion completes file-
       names.


KEY SEQUENCE NOTATION

       In the rest of this man page,  and  also  in  all  Tecla  configuration
       files, key-sequences are expressed as follows.


       ^A  or  C-a
           This is a control-A, entered by pressing the control key at
           the same time as the A key.

       \E    or   M-
           In key-sequences, both of these notations can be entered
           either by pressing the escape key, then the following key, or by
           pressing the Meta key at the same time as the following key. Thus
           the key sequence M-p can be typed in two ways, by pressing
           the escape key, followed by pressing p, or by pressing the
           Meta key at the same time as p.

       up
           This refers to the up-arrow key.

       down
           This refers to the down-arrow key.

       left
           This refers to the left-arrow key.

       right
           This refers to the right-arrow key.

       a
           This is just a normal A key.



THE TECLA CONFIGURATION FILE

       By  default, Tecla looks for a file called .teclarc in your home direc-
       tory (ie. ~/.teclarc).  If it finds this file, it reads it,  interpret-
       ing each line as defining a new key binding or an editing configuration
       option. Since the emacs keybindings are installed by  default,  if  you
       want to use the non-default vi editing mode, the most important item to
       go in this file is the following line:

         edit-mode vi

       This will re-configure the default bindings for vi-mode.  The  complete
       set of arguments that this command accepts are:

         vi     -  Install key-bindings like those of the vi
                   editor.
         emacs  -  Install key-bindings like those of the emacs
                   editor. This is the default.
         none   -  Use just the native line editing facilities
                   provided by the terminal driver.

       To  prevent the terminal bell from being rung, such as when an unrecog-
       nized control-sequence is typed, place the following line in  the  con-
       figuration file:

         nobeep

       An  example of a key binding line in the configuration file is the fol-
       lowing.

         bind M-[2~ insert-mode

       On many keyboards, the above key sequence is generated when one presses
       the  insert  key,  so  with this keybinding, one can toggle between the
       emacs-mode insert and overwrite modes by hitting  one  key.  One  could
       also  do  it by typing out the above sequence of characters one by one.
       As explained above, the M- part of this sequence can be typed either by
       pressing  the  escape  key before the following key, or by pressing the
       Meta key at the same time as the following key. Thus if you had set the
       above  key binding, and the insert key on your keyboard didn't generate
       the above key sequence, you could still type it in either of  the  fol-
       lowing 2 ways.

         1. Hit the escape key momentarily, then press '[', then '2', then
            finally '~'.

         2. Press the meta key at the same time as pressing the '[' key,
            then press '2', then '~'.

       If  you  set a keybinding for a key-sequence that is already bound to a
       function, the new binding overrides the old one. If in the new  binding
       you  omit the name of the new function to bind to the key-sequence, the
       original binding becomes undefined.

       Starting with versions of libtecla later than 1.3.3 it is now  possible
       to  bind keysequences that begin with a printable character. Previously
       key-sequences were required to start with a control or meta  character.

       Note  that  the special keywords "up", "down", "left" and "right" refer
       to the arrow keys, and are thus not treated as  keysequences.  So,  for
       example, to rebind the up and down arrow keys to use the history search
       mechanism instead of the simple history recall method, you could  place
       the following in your configuration file:

         bind up history-search-backwards
         bind down history-search-backwards

       To unbind an existing binding, you can do this with the bind command by
       omitting to name any action to rebind the key sequence to.   For  exam-
       ple,  by  not  specifying  an  action  function,  the following command
       unbinds the default beginning-of-line action from the ^A key sequence:

         bind ^A

       If you create a ~/.teclarc configuration file, but it appears  to  have
       no effect on the program, check the documentation of the program to see
       if the author chose a different name for this file.


FILENAME AND TILDE COMPLETION

       With the default key bindings, pressing the TAB key (aka.  ^I)  results
       in  Tecla  attempting to complete the incomplete filename that precedes
       the cursor. Tecla searches backwards from the cursor, looking  for  the
       start  of  the  filename,  stopping  when it hits either a space or the
       start of the line. If more than one file has the specified prefix, then
       Tecla  completes  the  filename  up to the point at which the ambiguous
       matches start to differ, then lists the possible matches.

       In addition to literally written filenames, Tecla  can  complete  files
       that  start  with  ~/  and  ~user/ expressions and that contain $envvar
       expressions. In particular, if you hit TAB within an incomplete  ~user,
       expression,  Tecla  will  attempt to complete the username, listing any
       ambiguous matches.

       The completion binding is implemented using the  cpl_word_completions()
       function,  which is also available separately to users of this library.
       See the cpl_word_completions(@LIBR_MANEXT@) man page for more  details.


FILENAME EXPANSION

       With  the default key bindings, pressing ^X* causes Tecla to expand the
       filename that precedes the cursor, replacing ~/ and ~user/  expressions
       with  the corresponding home directories, and replacing $envvar expres-
       sions with the value of the specified  environment  variable,  then  if
       there  are any wildcards, replacing the so far expanded filename with a
       space-separated list of the files which match the wild cards.

       The expansion binding is implemented using the  ef_expand_file()  func-
       tion.  See the ef_expand_file man page for more details.


RECALLING PREVIOUSLY TYPED LINES

       Every time that a new line is entered by the user, it is appended to  a
       list  of  historical input lines maintained within the GetLine resource
       object. You can traverse up and down this list using the  up  and  down
       arrow  keys.  Alternatively,  you  can  do the same with the ^P, and ^N
       keys, and in vi command mode you can alternatively  use  the  k  and  j
       characters.  Thus  pressing  up-arrow  once, replaces the current input
       line  with  the  previously  entered  line.  Pressing  up-arrow  again,
       replaces  this  with  the line that was entered before it, etc.. Having
       gone back one or more lines into the history list, one  can  return  to
       newer  lines  by  pressing down-arrow one or more times. If you do this
       sufficient times, you will return to the original line  that  you  were
       entering when you first hit up-arrow.

       Note  that  in  vi mode, all of the history recall functions switch the
       library into command mode.

       In emacs mode the M-p and M-n keys work just like the ^P and  ^N  keys,
       except  that  they  skip all but those historical lines which share the
       prefix that precedes the cursor. In vi command mode the  upper  case  K
       and  J  characters  do the same thing, except that the string that they
       search for includes the character under the cursor as well as what pre-
       cedes it.

       Thus for example, suppose that you were in emacs mode, and you had just
       entered the following list of commands in the order shown:

         ls ~/tecla/
         cd ~/tecla
         ls -l getline.c
         emacs ~/tecla/getline.c

       If you next typed:

         ls

       and then hit M-p, then rather than returning the previously typed emacs
       line, which doesn't start with "ls", Tecla would recall the "ls -l get-
       line.c" line. Pressing M-p again would recall the "ls ~/tecla/" line.

       Note that if the string that you are searching for, contains any of the
       special  characters, *, ?, or '[', then it is interpretted as a pattern
       to be matched. Thus, cotinuing with the above example, after typing  in
       the list of commands shown, if you then typed:

         *tecla*

       and  hit M-p, then the "emacs ~/tecla/getline.c" line would be recalled
       first, since it contains the word tecla somewhere in  the  line,  Simi-
       larly, hitting M-p again, would recall the "ls ~/tecla/" line, and hit-
       ting it once more would recall the "ls ~/tecla/" line. The pattern syn-
       tax  is  the  same  as  that  described  for filename expansion, in the
       ef_expand_file(@LIBR_MANEXT@ man page.


HISTORY FILES

       Authors of programs that use the Tecla library have the option of  sav-
       ing historical command-lines in a file before exiting, and subsequently
       reading them back in from this file when the program is  next  started.
       There  is no standard name for this file, since it makes sense for each
       application to use its own history file, so that commands from  differ-
       ent applications don't get mixed up.


INTERNATIONAL CHARACTER SETS

       Since  libtecla  version  1.4.0, Tecla has been 8-bit clean. This means
       that all 8-bit characters that are  printable  in  the  user's  current
       locale  are  now  displayed verbatim and included in the returned input
       line.  Assuming that the calling program correctly contains a call like
       the following,

         setlocale(LC_CTYPE, "");

       then  the  current locale is determined by the first of the environment
       variables LC_CTYPE, LC_ALL, and LANG, that is found to contain a  valid
       locale  name.  If  none  of these variables are defined, or the program
       neglects to call setlocale, then the default C locale is used, which is
       US  7-bit  ASCII.  On  most  unix-like platforms, you can get a list of
       valid locales by typing the command:

         locale -a

       at the shell prompt.


   Meta keys and locales
       Beware that in most locales other than the default C locale, meta char-
       acters  become  printable,  and  they  are then no longer considered to
       match M-c style key bindings. This allows international  characters  to
       be  entered  with  the compose key without unexpectedly triggering meta
       key bindings. You can still invoke meta bindings, since there are actu-
       ally  two  ways  to  do  this.  For example the binding M-c can also be
       invoked by pressing the escape key momentarily,  then  pressing  the  c
       key,  and  this  will  work regardless of locale. Moreover, many modern
       terminal emulators, such as gnome's gnome-terminal's and KDE's  konsole
       terminals,  already  generate  escape  pairs like this when you use the
       meta key, rather than a real meta character, and other  emulators  usu-
       ally  have  a  way to request this behavior, so you can continue to use
       the meta key on most systems.

       For example, although xterm terminal emulators generate real 8-bit meta
       characters by default when you use the meta key, they can be configured
       to output the equivalent escape pair by setting their  EightBitInput  X
       resource  to  False.  You can either do this by placing a line like the
       following in your ~/.Xdefaults file,

         XTerm*EightBitInput: False

       or by starting an xterm with an -xrm '*EightBitInput:  False'  command-
       line  argument. In recent versions of xterm you can toggle this feature
       on and off with the "Meta Sends Escape" option in the menu that is dis-
       played  when you press the left mouse button and the control key within
       an xterm window. In CDE, dtterms can be similarly coerced  to  generate
       escape pairs in place of meta characters, by setting the Dtterm*KshMode
       resource to True.


   Entering international characters
       If you don't have a keyboard that generates all  of  the  international
       characters  that  you  need,  there  is usually a compose key that will
       allow you to enter special characters, or a  way  to  create  one.  For
       example, under X windows on unix-like systems, if your keyboard doesn't
       have a compose key, you can designate a redundant  key  to  serve  this
       purpose  with  the  xmodmap  command. For example, on many PC keyboards
       there is a microsoft-windows key,  which  is  otherwise  useless  under
       Linux. On my laptop the xev program reports that pressing this key gen-
       erates keycode 115, so to turn this key into a compose key,  I  do  the
       following:

         xmodmap -e 'keycode 115 = Multi_key'

       I  can  then  enter an i with a umlaut over it by typing this key, fol-
       lowed by ", followed by i.


THE AVAILABLE KEY BINDING FUNCTIONS

       The following is a list of the editing functions provided by the  Tecla
       library.  The  names  in the leftmost column of the list can be used in
       configuration files to specify which function a given key  or  combina-
       tion of keys should invoke. They are also used in the next two sections
       to list the default key-bindings in emacs and vi modes.

         user-interrupt           -  Send a SIGINT signal to the
                                     parent process.
         abort                    -  Send a SIGABRT signal to the
                                     parent process.
         suspend                  -  Suspend the parent process.
         stop-output              -  Pause terminal output.
         start-output             -  Resume paused terminal output.
         literal-next             -  Arrange for the next character
                                     to be treated as a normal
                                     character. This allows control
                                     characters to be entered.
         cursor-right             -  Move the cursor one character
                                     right.
         cursor-left              -  Move the cursor one character
                                     left.
         insert-mode              -  Toggle between insert mode and
                                     overwrite mode.
         beginning-of-line        -  Move the cursor to the
                                     beginning of the line.
         end-of-line              -  Move the cursor to the end of
                                     the line.
         delete-line              -  Delete the contents of the
                                     current line.
         kill-line                -  Delete everything that follows
                                     the cursor.
         backward-kill-line       -  Delete all characters between
                                     the cursor and the start of the
                                     line.
         forward-word             -  Move to the end of the word
                                     which follows the cursor.
         forward-to-word          -  Move the cursor to the start of
                                     the word that follows the
                                     cursor.
         backward-word            -  Move to the start of the word
                                     which precedes the cursor.
         goto-column              -  Move the cursor to the
                                     1-relative column in the line
                                     specified by any preceding
                                     digit-argument sequences (see
                                     ENTERING REPEAT COUNTS below).
         find-parenthesis         -  If the cursor is currently
                                     over a parenthesis character,
                                     move it to the matching
                                     parenthesis character. If not
                                     over a parenthesis character
                                     move right to the next close
                                     parenthesis.
         forward-delete-char      -  Delete the character under the
                                     cursor.
         backward-delete-char     -  Delete the character which
                                     precedes the cursor.
         list-or-eof              -  This is intended for binding
                                     to ^D. When invoked when the
                                     cursor is within the line it
                                     displays all possible
                                     completions then redisplays
                                     the line unchanged. When
                                     invoked on an empty line, it
                                     signals end-of-input (EOF) to
                                     the caller of gl_get_line().
         del-char-or-list-or-eof  -  This is intended for binding
                                     to ^D. When invoked when the
                                     cursor is within the line it
                                     invokes forward-delete-char.
                                     When invoked at the end of the
                                     line it displays all possible
                                     completions then redisplays
                                     the line unchanged. When
                                     invoked on an empty line, it
                                     signals end-of-input (EOF) to
                                     the caller of gl_get_line().
         forward-delete-word      -  Delete the word which follows
                                     the cursor.
         backward-delete-word     -  Delete the word which precedes
                                     the cursor.
         upcase-word              -  Convert all of the characters
                                     of the word which follows the
                                     cursor, to upper case.
         downcase-word            -  Convert all of the characters
                                     of the word which follows the
                                     cursor, to lower case.
         capitalize-word          -  Capitalize the word which
                                     follows the cursor.
         change-case              -  If the next character is upper
                                     case, toggle it to lower case
                                     and vice versa.
         redisplay                -  Redisplay the line.
         clear-screen             -  Clear the terminal, then
                                     redisplay the current line.
         transpose-chars          -  Swap the character under the
                                     cursor with the character just
                                     before the cursor.
         set-mark                 -  Set a mark at the position of
                                     the cursor.
         exchange-point-and-mark  -  Move the cursor to the last
                                     mark that was set, and move
                                     the mark to where the cursor
                                     used to be.
         kill-region              -  Delete the characters that lie
                                     between the last mark that was
                                     set, and the cursor.
         copy-region-as-kill      -  Copy the text between the mark
                                     and the cursor to the cut
                                     buffer, without deleting the
                                     original text.
         yank                     -  Insert the text that was last
                                     deleted, just before the
                                     current position of the cursor.
         append-yank              -  Paste the current contents of
                                     the cut buffer, after the
                                     cursor.
         up-history               -  Recall the next oldest line
                                     that was entered. Note that
                                     in vi mode you are left in
                                     command mode.
         down-history             -  Recall the next most recent
                                     line that was entered. If no
                                     history recall session is
                                     currently active, the next
                                     line from a previous recall
                                     session is recalled. Note that
                                     in vi mode you are left in
                                     command mode.
         history-search-backward  -  Recall the next oldest line
                                     who's prefix matches the string
                                     which currently precedes the
                                     cursor (in vi command-mode the
                                     character under the cursor is
                                     also included in the search
                                     string).  Note that in vi mode
                                     you are left in command mode.
         history-search-forward   -  Recall the next newest line
                                     who's prefix matches the string
                                     which currently precedes the
                                     cursor (in vi command-mode the
                                     character under the cursor is
                                     also included in the search
                                     string).  Note that in vi mode
                                     you are left in command mode.
         history-re-search-backward -Recall the next oldest line
                                     who's prefix matches that
                                     established by the last
                                     invocation of either
                                     history-search-forward or
                                     history-search-backward.
         history-re-search-forward - Recall the next newest line
                                     who's prefix matches that
                                     established by the last
                                     invocation of either
                                     history-search-forward or
                                     history-search-backward.
         complete-word            -  Attempt to complete the
                                     incomplete word which
                                     precedes the cursor. Unless
                                     the host program has customized
                                     word completion, filename
                                     completion is attempted. In vi
                                     commmand mode the character
                                     under the cursor is also
                                     included in the word being
                                     completed, and you are left in
                                     vi insert mode.
         expand-filename          -  Within the command line, expand
                                     wild cards, tilde expressions
                                     and dollar expressions in the
                                     filename which immediately
                                     precedes the cursor. In vi
                                     commmand mode the character
                                     under the cursor is also
                                     included in the filename being
                                     expanded, and you are left in
                                     vi insert mode.
         list-glob                -  List any filenames which match
                                     the wild-card, tilde and dollar
                                     expressions in the filename
                                     which immediately precedes the
                                     cursor, then redraw the input
                                     line unchanged.
         list-history             -  Display the contents of the
                                     history list for the current
                                     history group. If a repeat
                                     count of > 1 is specified,
                                     only that many of the most
                                     recent lines are displayed.
                                     See the "ENTERING REPEAT
                                     COUNTS" section.
         read-from-file           -  Temporarily switch to reading
                                     input from the file who's
                                     name precedes the cursor.
         read-init-files          -  Re-read teclarc configuration
                                     files.
         beginning-of-history     -  Move to the oldest line in the
                                     history list. Note that in vi
                                     mode you are left in command
                                     mode.
         end-of-history           -  Move to the newest line in the
                                     history list (ie. the current
                                     line). Note that in vi mode
                                     this leaves you in command
                                     mode.
         digit-argument           -  Enter a repeat count for the
                                     next key-binding function.
                                     For details, see the ENTERING
                                     REPEAT COUNTS section.
         newline                  -  Terminate and return the
                                     current contents of the
                                     line, after appending a
                                     newline character. The newline
                                     character is normally '\n',
                                     but will be the first
                                     character of the key-sequence
                                     that invoked the newline
                                     action, if this happens to be
                                     a printable character. If the
                                     action was invoked by the
                                     '\n' newline character or the
                                     '\r' carriage return
                                     character, the line is
                                     appended to the history
                                     buffer.
         repeat-history           -  Return the line that is being
                                     edited, then arrange for the
                                     next most recent entry in the
                                     history buffer to be recalled
                                     when Tecla is next called.
                                     Repeatedly invoking this
                                     action causes successive
                                     historical input lines to be
                                     re-executed. Note that this
                                     action is equivalent to the
                                     'Operate' action in ksh.
         ring-bell                -  Ring the terminal bell, unless
                                     the bell has been silenced via
                                     the nobeep configuration
                                     option (see the THE TECLA
                                     CONFIGURATION FILE section).
         forward-copy-char        -  Copy the next character into
                                     the cut buffer (NB. use repeat
                                     counts to copy more than one).
         backward-copy-char       -  Copy the previous character
                                     into the cut buffer.
         forward-copy-word        -  Copy the next word into the cut
                                     buffer.
         backward-copy-word       -  Copy the previous word into the
                                     cut buffer.
         forward-find-char        -  Move the cursor to the next
                                     occurrence of the next
                                     character that you type.
         backward-find-char       -  Move the cursor to the last
                                     occurrence of the next
                                     character that you type.
         forward-to-char          -  Move the cursor to the
                                     character just before the next
                                     occurrence of the next
                                     character that the user types.
         backward-to-char         -  Move the cursor to the
                                     character just after the last
                                     occurrence before the cursor
                                     of the next character that the
                                     user types.
         repeat-find-char         -  Repeat the last
                                     backward-find-char,
                                     forward-find-char,
                                     backward-to-char or
                                     forward-to-char.
         invert-refind-char       -  Repeat the last
                                     backward-find-char,
                                     forward-find-char,
                                     backward-to-char, or
                                     forward-to-char in the
                                     opposite direction.
         delete-to-column         -  Delete the characters from the
                                     cursor up to the column that
                                     is specified by the repeat
                                     count.
         delete-to-parenthesis    -  Delete the characters from the
                                     cursor up to and including
                                     the matching parenthesis, or
                                     next close parenthesis.
         forward-delete-find      -  Delete the characters from the
                                     cursor up to and including the
                                     following occurence of the
                                     next character typed.
         backward-delete-find     -  Delete the characters from the
                                     cursor up to and including the
                                     preceding occurence of the
                                     next character typed.
         forward-delete-to        -  Delete the characters from the
                                     cursor up to, but not
                                     including, the following
                                     occurence of the next
                                     character typed.
         backward-delete-to       -  Delete the characters from the
                                     cursor up to, but not
                                     including, the preceding
                                     occurence of the next
                                     character typed.
         delete-refind            -  Repeat the last *-delete-find
                                     or *-delete-to action.
         delete-invert-refind     -  Repeat the last *-delete-find
                                     or *-delete-to action, in the
                                     opposite direction.
         copy-to-column           -  Copy the characters from the
                                     cursor up to the column that
                                     is specified by the repeat
                                     count, into the cut buffer.
         copy-to-parenthesis      -  Copy the characters from the
                                     cursor up to and including
                                     the matching parenthesis, or
                                     next close parenthesis, into
                                     the cut buffer.
         forward-copy-find        -  Copy the characters from the
                                     cursor up to and including the
                                     following occurence of the
                                     next character typed, into the
                                     cut buffer.
         backward-copy-find       -  Copy the characters from the
                                     cursor up to and including the
                                     preceding occurence of the
                                     next character typed, into the
                                     cut buffer.
         forward-copy-to          -  Copy the characters from the
                                     cursor up to, but not
                                     including, the following
                                     occurence of the next
                                     character typed, into the cut
                                     buffer.
         backward-copy-to         -  Copy the characters from the
                                     cursor up to, but not
                                     including, the preceding
                                     occurence of the next
                                     character typed, into the cut
                                     buffer.
         copy-refind              -  Repeat the last *-copy-find
                                     or *-copy-to action.
         copy-invert-refind       -  Repeat the last *-copy-find
                                     or *-copy-to action, in the
                                     opposite direction.
         vi-mode                  -  Switch to vi mode from emacs
                                     mode.
         emacs-mode               -  Switch to emacs mode from vi
                                     mode.
         vi-insert                -  From vi command mode, switch to
                                     insert mode.
         vi-overwrite             -  From vi command mode, switch to
                                     overwrite mode.
         vi-insert-at-bol         -  From vi command mode, move the
                                     cursor to the start of the line
                                     and switch to insert mode.
         vi-append-at-eol         -  From vi command mode, move the
                                     cursor to the end of the line
                                     and switch to append mode.
         vi-append                -  From vi command mode, move the
                                     cursor one position right, and
                                     switch to insert mode.
         vi-replace-char          -  From vi command mode, replace
                                     the character under the cursor
                                     with the the next character
                                     entered.
         vi-forward-change-char   -  From vi command mode, delete
                                     the next character then enter
                                     insert mode.
         vi-backward-change-char  -  From vi command mode, delete
                                     the preceding character then
                                     enter insert mode.
         vi-forward-change-word   -  From vi command mode, delete
                                     the next word then enter
                                     insert mode.
         vi-backward-change-word  -  From vi command mode, delete
                                     the preceding word then
                                     enter insert mode.
         vi-change-rest-of-line   -  From vi command mode, delete
                                     from the cursor to the end of
                                     the line, then enter insert
                                     mode.
         vi-change-line           -  From vi command mode, delete
                                     the current line, then enter
                                     insert mode.
         vi-change-to-bol         -  From vi command mode, delete
                                     all characters between the
                                     cursor and the beginning of
                                     the line, then enter insert
                                     mode.
         vi-change-to-column      -  From vi command mode, delete
                                     the characters from the cursor
                                     up to the column that is
                                     specified by the repeat count,
                                     then enter insert mode.
         vi-change-to-parenthesis -  Delete the characters from the
                                     cursor up to and including
                                     the matching parenthesis, or
                                     next close parenthesis, then
                                     enter vi insert mode.
         vi-forward-change-find   -  From vi command mode, delete
                                     the characters from the
                                     cursor up to and including the
                                     following occurence of the
                                     next character typed, then
                                     enter insert mode.
         vi-backward-change-find  -  From vi command mode, delete
                                     the characters from the
                                     cursor up to and including the
                                     preceding occurence of the
                                     next character typed, then
                                     enter insert mode.
         vi-forward-change-to     -  From vi command mode, delete
                                     the characters from the
                                     cursor up to, but not
                                     including, the following
                                     occurence of the next
                                     character typed, then enter
                                     insert mode.
         vi-backward-change-to    -  From vi command mode, delete
                                     the characters from the
                                     cursor up to, but not
                                     including, the preceding
                                     occurence of the next
                                     character typed, then enter
                                     insert mode.
         vi-change-refind         -  Repeat the last
                                     vi-*-change-find or
                                     vi-*-change-to action.
         vi-change-invert-refind  -  Repeat the last
                                     vi-*-change-find or
                                     vi-*-change-to action, in the
                                     opposite direction.
         vi-undo                  -  In vi mode, undo the last
                                     editing operation.
         vi-repeat-change         -  In vi command mode, repeat the
                                     last command that modified the
                                     line.


DEFAULT KEY BINDINGS IN EMACS MODE

       The following default key bindings, which can be overriden by the Tecla
       configuration  file,  are designed to mimic most of the bindings of the
       unix tcsh shell, when it is in emacs editing mode.

       This is the default editing mode of the Tecla library.

       Under UNIX the terminal driver sets a number of special keys  for  cer-
       tain  functions. The tecla library attempts to use the same keybindings
       to maintain consistency. The key sequences shown for  the  following  6
       bindings  are  thus just examples of what they will probably be set to.
       If you have used the stty  command  to  change  these  keys,  then  the
       default bindings should match.

         ^C     ->   user-interrupt
         ^\     ->   abort
         ^Z     ->   suspend
         ^Q     ->   start-output
         ^S     ->   stop-output
         ^V     ->   literal-next

       The  cursor  keys are refered to by name, as follows. This is necessary
       because different types of terminals generate different  key  sequences
       when their cursor keys are pressed.

         right  ->   cursor-right
         left   ->   cursor-left
         up     ->   up-history
         down   ->   down-history

       The remaining bindings don't depend on the terminal setttings.

         ^F     ->   cursor-right
         ^B     ->   cursor-left
         M-i    ->   insert-mode
         ^A     ->   beginning-of-line
         ^E     ->   end-of-line
         ^U     ->   delete-line
         ^K     ->   kill-line
         M-f    ->   forward-word
         M-b    ->   backward-word
         ^D     ->   del-char-or-list-or-eof
         ^H     ->   backward-delete-char
         ^?     ->   backward-delete-char
         M-d    ->   forward-delete-word
         M-^H   ->   backward-delete-word
         M-^?   ->   backward-delete-word
         M-u    ->   upcase-word
         M-l    ->   downcase-word
         M-c    ->   capitalize-word
         ^R     ->   redisplay
         ^L     ->   clear-screen
         ^T     ->   transpose-chars
         ^@     ->   set-mark
         ^X^X   ->   exchange-point-and-mark
         ^W     ->   kill-region
         M-w    ->   copy-region-as-kill
         ^Y     ->   yank
         ^P     ->   up-history
         ^N     ->   down-history
         M-p    ->   history-search-backward
         M-n    ->   history-search-forward
         ^I     ->   complete-word
         ^X*    ->   expand-filename
         ^X^F   ->   read-from-file
         ^X^R   ->   read-init-files
         ^Xg    ->   list-glob
         ^Xh    ->   list-history
         M-<    ->   beginning-of-history
         M->    ->   end-of-history
         \n     ->   newline
         \r     ->   newline
         M-o    ->   repeat-history
         M-^V   ->   vi-mode

         M-0, M-1, ... M-9  ->  digit-argument  (see below)

       Note  that  ^I is what the TAB key generates, and that ^@ can be gener-
       ated not only by pressing the control key and the @ key simultaneously,
       but  also  by  pressing  the  control key and the space bar at the same
       time.


DEFAULT KEY BINDINGS IN VI MODE

       The following default key bindings are designed to mimic the  vi  style
       of  editing  as  closely  as possible. This means that very few editing
       functions are provided in the initial  character  input  mode,  editing
       functions  instead  being  provided  by the vi command mode. Vi command
       mode is entered whenever the escape character is pressed, or whenever a
       key-sequence  that starts with a meta character is entered. In addition
       to mimicing vi, libtecla provides bindings for  tab  completion,  wild-
       card expansion of file names, and historical line recall.

       To  learn  how  to tell the Tecla library to use vi mode instead of the
       default emacs editing mode, see the earlier section entitled THE  TECLA
       CONFIGURATION FILE.

       Under  UNIX  the terminal driver sets a number of special keys for cer-
       tain functions. The Tecla library attempts to use the same  keybindings
       to maintain consistency, binding them both in input mode and in command
       mode. The key sequences shown for the following  6  bindings  are  thus
       just  examples  of  what they will probably be set to. If you have used
       the stty command to change these keys, then the default bindings should
       match.

         ^C     ->   user-interrupt
         ^\     ->   abort
         ^Z     ->   suspend
         ^Q     ->   start-output
         ^S     ->   stop-output
         ^V     ->   literal-next
         M-^C   ->   user-interrupt
         M-^\   ->   abort
         M-^Z   ->   suspend
         M-^Q   ->   start-output
         M-^S   ->   stop-output

       Note  that above, most of the bindings are defined twice, once as a raw
       control code like ^C and then a second time as a  meta  character  like
       M-^C.  The  former is the binding for vi input mode, whereas the latter
       is the binding for vi command mode.  Once  in  command  mode  all  key-
       sequences  that the user types that they don't explicitly start with an
       escape or a meta key, have their first key secretly converted to a meta
       character  before  the key sequence is looked up in the key binding ta-
       ble. Thus, once in command mode, when you type the letter i, for  exam-
       ple, the Tecla library actually looks up the binding for M-i.

       The  cursor  keys are refered to by name, as follows. This is necessary
       because different types of terminals generate different  key  sequences
       when their cursor keys are pressed.

         right  ->   cursor-right
         left   ->   cursor-left
         up     ->   up-history
         down   ->   down-history

       The  cursor  keys  normally  generate  a keysequence that start with an
       escape character, so beware that using the arrow keys will put you into
       command mode (if you aren't already in command mode).

       The  following  are  the terminal-independent key bindings for vi input
       mode.

         ^D     ->   list-or-eof
         ^G     ->   list-glob
         ^H     ->   backward-delete-char
         ^I     ->   complete-word
         \r     ->   newline
         \n     ->   newline
         ^L     ->   clear-screen
         ^N     ->   down-history
         ^P     ->   up-history
         ^R     ->   redisplay
         ^U     ->   backward-kill-line
         ^W     ->   backward-delete-word
         ^X*    ->   expand-filename
         ^X^F   ->   read-from-file
         ^X^R   ->   read-init-files
         ^?     ->   backward-delete-char

       The following are the key bindings that are defined in vi command mode,
       this  being  specified  by  them all starting with a meta character. As
       mentioned above, once in command mode the  initial  meta  character  is
       optional.  For example, you might enter command mode by typing Esc, and
       then press h twice to move the cursor two positions to the left. Both h
       characters  get  quietly  converted to M-h before being compared to the
       key-binding table, the first one because Escape followed by a character
       is  always  converted  to the equivalent meta character, and the second
       because command mode was already active.

         M-\     ->   cursor-right     (Meta-space)
         M-$     ->   end-of-line
         M-*     ->   expand-filename
         M-+     ->   down-history
         M--     ->   up-history
         M-<     ->   beginning-of-history
         M->     ->   end-of-history
         M-^     ->   beginning-of-line
         M-;     ->   repeat-find-char
         M-,     ->   invert-refind-char
         M-|     ->   goto-column
         M-~     ->   change-case
         M-.     ->   vi-repeat-change
         M-%     ->   find-parenthesis
         M-a     ->   vi-append
         M-A     ->   vi-append-at-eol
         M-b     ->   backward-word
         M-B     ->   backward-word
         M-C     ->   vi-change-rest-of-line
         M-cb    ->   vi-backward-change-word
         M-cB    ->   vi-backward-change-word
         M-cc    ->   vi-change-line
         M-ce    ->   vi-forward-change-word
         M-cE    ->   vi-forward-change-word
         M-cw    ->   vi-forward-change-word
         M-cW    ->   vi-forward-change-word
         M-cF    ->   vi-backward-change-find
         M-cf    ->   vi-forward-change-find
         M-cT    ->   vi-backward-change-to
         M-ct    ->   vi-forward-change-to
         M-c;    ->   vi-change-refind
         M-c,    ->   vi-change-invert-refind
         M-ch    ->   vi-backward-change-char
         M-c^H   ->   vi-backward-change-char
         M-c^?   ->   vi-backward-change-char
         M-cl    ->   vi-forward-change-char
         M-c\    ->   vi-forward-change-char  (Meta-c-space)
         M-c^    ->   vi-change-to-bol
         M-c0    ->   vi-change-to-bol
         M-c$    ->   vi-change-rest-of-line
         M-c|    ->   vi-change-to-column
         M-c%    ->   vi-change-to-parenthesis
         M-dh    ->   backward-delete-char
         M-d^H   ->   backward-delete-char
         M-d^?   ->   backward-delete-char
         M-dl    ->   forward-delete-char
         M-d     ->   forward-delete-char    (Meta-d-space)
         M-dd    ->   delete-line
         M-db    ->   backward-delete-word
         M-dB    ->   backward-delete-word
         M-de    ->   forward-delete-word
         M-dE    ->   forward-delete-word
         M-dw    ->   forward-delete-word
         M-dW    ->   forward-delete-word
         M-dF    ->   backward-delete-find
         M-df    ->   forward-delete-find
         M-dT    ->   backward-delete-to
         M-dt    ->   forward-delete-to
         M-d;    ->   delete-refind
         M-d,    ->   delete-invert-refind
         M-d^    ->   backward-kill-line
         M-d0    ->   backward-kill-line
         M-d$    ->   kill-line
         M-D     ->   kill-line
         M-d|    ->   delete-to-column
         M-d%    ->   delete-to-parenthesis
         M-e     ->   forward-word
         M-E     ->   forward-word
         M-f     ->   forward-find-char
         M-F     ->   backward-find-char
         M--     ->   up-history
         M-h     ->   cursor-left
         M-H     ->   beginning-of-history
         M-i     ->   vi-insert
         M-I     ->   vi-insert-at-bol
         M-j     ->   down-history
         M-J     ->   history-search-forward
         M-k     ->   up-history
         M-K     ->   history-search-backward
         M-l     ->   cursor-right
         M-L     ->   end-of-history
         M-n     ->   history-re-search-forward
         M-N     ->   history-re-search-backward
         M-p     ->   append-yank
         M-P     ->   yank
         M-r     ->   vi-replace-char
         M-R     ->   vi-overwrite
         M-s     ->   vi-forward-change-char
         M-S     ->   vi-change-line
         M-t     ->   forward-to-char
         M-T     ->   backward-to-char
         M-u     ->   vi-undo
         M-w     ->   forward-to-word
         M-W     ->   forward-to-word
         M-x     ->   forward-delete-char
         M-X     ->   backward-delete-char
         M-yh    ->   backward-copy-char
         M-y^H   ->   backward-copy-char
         M-y^?   ->   backward-copy-char
         M-yl    ->   forward-copy-char
         M-y\    ->   forward-copy-char  (Meta-y-space)
         M-ye    ->   forward-copy-word
         M-yE    ->   forward-copy-word
         M-yw    ->   forward-copy-word
         M-yW    ->   forward-copy-word
         M-yb    ->   backward-copy-word
         M-yB    ->   backward-copy-word
         M-yf    ->   forward-copy-find
         M-yF    ->   backward-copy-find
         M-yt    ->   forward-copy-to
         M-yT    ->   backward-copy-to
         M-y;    ->   copy-refind
         M-y,    ->   copy-invert-refind
         M-y^    ->   copy-to-bol
         M-y0    ->   copy-to-bol
         M-y$    ->   copy-rest-of-line
         M-yy    ->   copy-line
         M-Y     ->   copy-line
         M-y|    ->   copy-to-column
         M-y%    ->   copy-to-parenthesis
         M-^E    ->   emacs-mode
         M-^H    ->   cursor-left
         M-^?    ->   cursor-left
         M-^L    ->   clear-screen
         M-^N    ->   down-history
         M-^P    ->   up-history
         M-^R    ->   redisplay
         M-^D    ->   list-or-eof
         M-^I    ->   complete-word
         M-\r    ->   newline
         M-\n    ->   newline
         M-^X^R  ->   read-init-files
         M-^Xh   ->   list-history

         M-0, M-1, ... M-9  ->  digit-argument  (see below)

       Note that ^I is what the TAB key generates.


ENTERING REPEAT COUNTS

       Many of  the  key  binding  functions  described  previously,  take  an
       optional count, typed in before the target keysequence.  This is inter-
       preted as a repeat count by most bindings. A notable exception  is  the
       goto-column binding, which interprets the count as a column number.

       By default you can specify this count argument by pressing the meta key
       while typing in the numeric count. This relies  on  the  digit-argument
       action  being bound to Meta-0, Meta-1 etc.  Once any one of these bind-
       ings has been activated, you can optionally take your  finger  off  the
       meta  key  to type in the rest of the number, since every numeric digit
       thereafter is treated as part of the number, unless it is  preceded  by
       the  literal-next binding. As soon as a non-digit, or literal digit key
       is pressed the repeat count is terminated and either  causes  the  just
       typed  character to be added to the line that many times, or causes the
       next key-binding function to be given that argument.

       For example, in emacs mode, typing:

         M-12a

       causes the letter 'a' to be added to the line 12 times, whereas

         M-4M-c

       Capitalizes the next 4 words.

       In vi command mode the Meta modifier  is  automatically  added  to  all
       characters  typed  in,  so  to  enter  a count in vi command-mode, just
       involves typing in the number, just as it does in the vi editor itself.
       So for example, in vi command mode, typing:

         4w2x

       moves  the cursor four words to the right, then deletes two characters.

       You can also bind digit-argument to other key sequences. If  these  end
       in  a  numeric  digit,  that  digit gets appended to the current repeat
       count. If it doesn't end in a numeric digit,  a  new  repeat  count  is
       started  with  a  value  of zero, and can be completed by typing in the
       number, after letting go of the key which triggered the  digit-argument
       action.


FILES

       libtecla.a      -    The Tecla library
       libtecla.h      -    The Tecla header file.
       ~/.teclarc      -    The personal Tecla customization file.


SEE ALSO

       libtecla, gl_get_line, gl_io_mode, ef_expand_file,
       cpl_complete_word, pca_lookup_file


AUTHOR

       Martin Shepherd  (mcs@astro.caltech.edu)



                                                          tecla
yuma123_2.14/libtecla/html/pca_lookup_file.html0000664000175000017500000003413414770023131021674 0ustar vladimirvladimir Manual Page
pca_lookup_file                  pca_lookup_file



NAME

       pca_lookup_file,    del_PathCache,    del_PcaPathConf,   new_PathCache,
       new_PcaPathConf, pca_last_error,  pca_path_completions,  pca_scan_path,
       pca_set_check_fn,  ppc_file_start,  ppc_literal_escapes - lookup a file
       in a list of directories

SYNOPSIS

       #include <libtecla.h>

       PathCache *new_PathCache(void);

       PathCache *del_PathCache(PathCache *pc);

       int pca_scan_path(PathCache *pc, const char *path);

       void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn,
                             void *data);

       char *pca_lookup_file(PathCache *pc, const char *name,
                             int name_len, int literal);

       const char *pca_last_error(PathCache *pc);

       CPL_MATCH_FN(pca_path_completions);



DESCRIPTION

       The PathCache object is part of  the  tecla  library  (see  the  libte-
       cla(@LIBR_MANEXT@) man page).

       PathCache objects allow an application to search for files in any colon
       separated list of directories, such as the unix execution PATH environ-
       ment  variable. Files in absolute directories are cached in a PathCache
       object, whereas relative directories are scanned  as  needed.  Using  a
       PathCache  object,  you can look up the full pathname of a simple file-
       name, or you can obtain a list of the possible completions of  a  given
       filename  prefix.  By  default all files in the list of directories are
       targets for lookup and completion, but a versatile  mechanism  is  pro-
       vided  for only selecting specific types of files. The obvious applica-
       tion of this facility is to provide Tab-completion and lookup  of  exe-
       cutable  commands  in  the  unix  PATH,  so  an optional callback which
       rejects all but executable files, is provided.


AN EXAMPLE

       Under UNIX, the following example program looks  up  and  displays  the
       full pathnames of each of the command names on the command line.

         #include <stdio.h>
         #include <stdlib.h>
         #include <libtecla.h>

         int main(int argc, char *argv[])
         {
           int i;
         /*
          * Create a cache for executable files.
          */
           PathCache *pc = new_PathCache();
           if(!pc)
             exit(1);
         /*
          * Scan the user's PATH for executables.
          */
           if(pca_scan_path(pc, getenv("PATH"))) {
             fprintf(stderr, "%s\n", pca_last_error(pc));
             exit(1);
           }
         /*
          * Arrange to only report executable files.
          */
          pca_set_check_fn(pc, cpl_check_exe, NULL);
         /*
          * Lookup and display the full pathname of each of the
          * commands listed on the command line.
          */
           for(i=1; i<argc; i++) {
             char *cmd = pca_lookup_file(pc, argv[i], -1, 0);
             printf("The full pathname of '%s' is %s\n", argv[i],
                    cmd ? cmd : "unknown");
           }
           pc = del_PathCache(pc);  /* Clean up */
           return 0;
         }

       The following is an example of what this does on my laptop under linux:

         $ ./example less more blob
         The full pathname of 'less' is /usr/bin/less
         The full pathname of 'more' is /bin/more
         The full pathname of 'blob' is unknown
         $


FUNCTION DESCRIPTIONS

       In order to use the facilities of this module, you must first  allocate
       a PathCache object by calling the new_PathCache() constructor function.

         PathCache *new_PathCache(void)

       This function creates the resources needed to cache and lookup files in
       a list of directories. It returns NULL on error.


POPULATING THE CACHE

       Once you have created a cache, it needs to be populated with files.  To
       do this, call the pca_scan_path() function.

         int pca_scan_path(PathCache *pc, const char *path);

       Whenever this function is called, it discards the current  contents  of
       the  cache,  then  scans  the list of directories specified in its path
       argument for files. The path argument must be  a  string  containing  a
       colon-separated       list       of      directories,      such      as
       "/usr/bin:/home/mcs/bin:.". This can include directories  specified  by
       absolute pathnames such as "/usr/bin", as well as sub-directories spec-
       ified by relative pathnames such as "." or "bin". Files in the absolute
       directories  are  immediately cached in the specified PathCache object,
       whereas sub-directories, whose identities obviously change whenever the
       current  working  directory is changed, are marked to be scanned on the
       fly whenever a file is looked up.

       On success this function return  0.  On  error  it  returns  1,  and  a
       description of the error can be obtained by calling pca_last_error(pc).


LOOKING UP FILES

       Once the cache has been populated with files, you can look up the  full
       pathname   of   a   file,   simply   by   specifying  its  filename  to
       pca_lookup_file().

         char *pca_lookup_file(PathCache *pc, const char *name,
                               int name_len, int literal);

       To make it possible to pass this function a filename which is  actually
       part  of  a longer string, the name_len argument can be used to specify
       the length of the filename at the start of the name[] argument. If  you
       pass  -1  for  this length, the length of the string will be determined
       with strlen(). If the name[]  string  might  contain  backslashes  that
       escape  the  special  meanings  of spaces and tabs within the filename,
       give the literal argument,  the  value  0.  Otherwise,  if  backslashes
       should  be  treated  as  normal characters, pass 1 for the value of the
       literal argument.


FILENAME COMPLETION

       Looking up the potential completions of a filename-prefix in the  file-
       name  cache, is achieved by passing the provided pca_path_completions()
       callback function to the cpl_complete_word() function (see the cpl_com-
       plete_word(@FUNC_MANEXT@) man page).

         CPL_MATCH_FN(pca_path_completions);

       This  callback  requires that its data argument be a pointer to a PcaP-
       athConf object. Configuration objects of this  type  are  allocated  by
       calling new_PcaPathConf().

         PcaPathConf *new_PcaPathConf(PathCache *pc);

       This  function returns an object initialized with default configuration
       parameters, which determine  how  the  cpl_path_completions()  callback
       function  behaves. The functions which allow you to individually change
       these parameters are discussed below.

       By default, the pca_path_completions() callback function searches back-
       wards  for  the  start of the filename being completed, looking for the
       first un-escaped space or the start of the input line. If you  wish  to
       specify  a  different location, call ppc_file_start() with the index at
       which the filename starts in the input line. Passing start_index=-1 re-
       enables the default behavior.

         void ppc_file_start(PcaPathConf *ppc, int start_index);

       By  default,  when  pca_path_completions()  looks  at a filename in the
       input line, each lone backslash in the input  line  is  interpreted  as
       being a special character which removes any special significance of the
       character which follows it, such as a space which should  be  taken  as
       part  of the filename rather than delimiting the start of the filename.
       These backslashes are thus ignored while looking for  completions,  and
       subsequently  added  before spaces, tabs and literal backslashes in the
       list of completions. To have unescaped backslashes  treated  as  normal
       characters,  call  ppc_literal_escapes()  with  a non-zero value in its
       literal argument.

         void ppc_literal_escapes(PcaPathConf *ppc, int literal);

       When you have finished with a PcaPathConf variable, you can pass it  to
       the del_PcaPathConf() destructor function to reclaim its memory.

         PcaPathConf *del_PcaPathConf(PcaPathConf *ppc);



BEING SELECTIVE

       If  you  are  only  interested  in certain types or files, such as, for
       example, executable files, or files whose names  end  in  a  particular
       suffix, you can arrange for the file completion and lookup functions to
       be selective in the filenames that they return.  This is done by regis-
       tering  a  callback  function  with  your PathCache object. Thereafter,
       whenever a filename is found which  either  matches  a  filename  being
       looked  up, or matches a prefix which is being completed, your callback
       function will be called with the full pathname of the  file,  plus  any
       application-specific data that you provide, and if the callback returns
       1 the filename will be reported as a match, and if  it  returns  0,  it
       will  be  ignored.   Suitable  callback  functions and their prototypes
       should be declared with the following macro. The CplCheckFn typedef  is
       also provided in case you wish to declare pointers to such functions.

         #define CPL_CHECK_FN(fn) int (fn)(void *data, \
                                           const char *pathname)
         typedef CPL_CHECK_FN(CplCheckFn);

       Registering    one    of   these   functions   involves   calling   the
       pca_set_check_fn() function. In  addition  to  the  callback  function,
       passed  via  the  check_fn argument, you can pass a pointer to anything
       via the data argument. This pointer will be passed on to your  callback
       function,  via  its  own  data argument, whenever it is called, so this
       provides a way to pass appplication specific data to your callback.

         void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn,
                               void *data);

       Note that these callbacks are passed the full pathname of each matching
       file,  so the decision about whether a file is of interest can be based
       on any property of the file, not just its filename. As an example,  the
       provided cpl_check_exe() callback function looks at the executable per-
       missions of the file and the permissions of its parent directories, and
       only  returns  1  if  the user has execute permission to the file. This
       callback function can thus be used to lookup or complete command  names
       found  in  the  directories listed in the user's PATH environment vari-
       able. The example program given earlier in this  man  page  provides  a
       demonstration of this.

       Beware  that  if somebody tries to complete an empty string, your call-
       back will get called once for every file in the cache, which could num-
       ber  in  the  thousands. If your callback does anything time consuming,
       this could result in an unacceptable delay for the user,  so  callbacks
       should be kept short.

       To  improve performance, whenever one of these callbacks is called, the
       choice that it makes is cached, and the  next  time  the  corresponding
       file  is  looked  up, instead of calling the callback again, the cached
       record of whether it was accepted or rejected is used. Thus if somebody
       tries  to  complete  an  empty  string, and hits tab a second time when
       nothing appears to happen, there will only be one long delay, since the
       second  pass  will operate entirely from the cached dispositions of the
       files. These cached dipositions are discarded whenever  pca_scan_path()
       is called, and whenever pca_set_check_fn() is called with changed call-
       back function or data arguments.


ERROR HANDLING

       If pca_scan_path() reports that an error occurred by returning  1,  you
       can   obtain   a   terse   description   of   the   error   by  calling
       pca_last_error(pc). This returns an internal string containing an error
       message.

         const char *pca_last_error(PathCache *pc);



CLEANING UP

       Once  you  have  finished using a PathCache object, you can reclaim its
       resources by passing it to  the  del_PathCache()  destructor  function.
       This  takes a pointer to one of these objects, and always returns NULL.

         PathCache *del_PathCache(PathCache *pc);


THREAD SAFETY

       In multi-threaded programs, you should use the libtecla_r.a version  of
       the library. This uses POSIX reentrant functions where available (hence
       the _r suffix), and disables features that rely on non-reentrant system
       functions.  In  the  case  of this module, the only disabled feature is
       username completion  in  ~username/  expressions,  in  cpl_path_comple-
       tions().

       Using  the  libtecla_r.a  version of the library, it is safe to use the
       facilities of this module  in  multiple  threads,  provided  that  each
       thread uses a separately allocated PathCache object. In other words, if
       two threads want to do path searching, they should each call  new_Path-
       Cache() to allocate their own caches.


FILES

       libtecla.a    -    The tecla library
       libtecla.h    -    The tecla header file.


SEE ALSO

       libtecla, gl_get_line, ef_expand_file,
       cpl_complete_word


AUTHOR

       Martin Shepherd  (mcs@astro.caltech.edu)



                                                pca_lookup_file
yuma123_2.14/libtecla/html/cpl_complete_word.html0000664000175000017500000004606014770023131022243 0ustar vladimirvladimir Manual Page
cpl_complete_word              cpl_complete_word



NAME

       cpl_complete_word,         cfc_file_start,         cfc_literal_escapes,
       cfc_set_check_fn,       cpl_add_completion,       cpl_file_completions,
       cpl_last_error,        cpl_list_completions,        cpl_recall_matches,
       cpl_record_error, del_CplFileConf, del_WordCompletion, new_CplFileConf,
       new_WordCompletion - lookup possible completions for a word

SYNOPSIS

       #include <stdio.h>
       #include <libtecla.h>

       WordCompletion *new_WordCompletion(void);

       WordCompletion *del_WordCompletion(WordCompletion *cpl);


       #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \
                                         void *data, \
                                         const char *line, \
                                         int word_end)
       typedef CPL_MATCH_FN(CplMatchFn);

       CPL_MATCH_FN(cpl_file_completions);


       CplMatches *cpl_complete_word(WordCompletion *cpl,
                                     const char *line,
                                     int word_end, void *data,
                                     CplMatchFn *match_fn);

       CplMatches *cpl_recall_matches(WordCompletion *cpl);

       int cpl_list_completions(CplMatches *result, FILE *fp,
                                int term_width);

       int cpl_add_completion(WordCompletion *cpl,
                              const char *line, int word_start,
                              int word_end, const char *suffix,
                              const char *type_suffix,
                              const char *cont_suffix);

       void cpl_record_error(WordCompletion *cpl,
                             const char *errmsg);

       const char *cpl_last_error(WordCompletion *cpl);


       #define CPL_CHECK_FN(fn) int (fn)(void *data, \
                                         const char *pathname)

       typedef CPL_CHECK_FN(CplCheckFn);

       CPL_CHECK_FN(cpl_check_exe);

       CplFileConf *new_CplFileConf(void);

       CplFileConf *del_CplFileConf(CplFileConf *cfc);

       void cfc_literal_escapes(CplFileConf *cfc, int literal);

       void cfc_file_start(CplFileConf *cfc, int start_index);

       void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn,
                             void *chk_data);



DESCRIPTION

       The  cpl_complete_word() function is part of the tecla library (see the
       libtecla man page). It  is  usually  called  behind  the
       scenes  by  gl_get_line,  but  can  also be called sepa-
       rately.

       Given an input line containing an incomplete word to be  completed,  it
       calls  a  user-provided callback function (or the provided file-comple-
       tion callback function) to look up all possible completion suffixes for
       that  word.  The  callback function is expected to look backward in the
       line, starting from the specified cursor position, to find the start of
       the  word  to be completed, then to look up all possible completions of
       that word and record them, one at a  time  by  calling  cpl_add_comple-
       tion().


       Descriptions of the functions of this module are as follows:

         WordCompletion *new_WordCompletion(void)

       This  function  creates  the  resources used by the cpl_complete_word()
       function. In particular, it maintains the memory that is used to return
       the results of calling cpl_complete_word().

         WordCompletion *del_WordCompletion(WordCompletion *cpl)

       This  function  deletes  the resources that were returned by a previous
       call to new_WordCompletion(). It always returns  NULL  (ie.  a  deleted
       object). It does nothing if the cpl argument is NULL.

       The  callback  functions  which  lookup  possible completions should be
       defined with the following macro (which is defined in libtecla.h).

         #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \
                                           void *data, \
                                           const char *line, \
                                           int word_end)

       Functions of this type are called by cpl_complete_word(),  and  all  of
       the  arguments of the callback are those that were passed to said func-
       tion. In particular, the line argument contains the input line contain-
       ing  the word to be completed, and word_end is the index of the charac-
       ter that follows the last character of the incomplete word within  this
       string.  The  callback  is expected to look backwards from word_end for
       the start of the incomplete word. What constitutes the start of a  word
       clearly  depends on the application, so it makes sense for the callback
       to take on this responsibility. For example, the builtin filename  com-
       pletion  function  looks backwards until it hits an unescaped space, or
       the start of the line.  Having found the start of the word,  the  call-
       back  should  then  lookup  all  possible completions of this word, and
       record each completion via separate calls to  cpl_add_completion().  If
       the  callback  needs access to an application-specific symbol table, it
       can pass it and any other data that it needs, via  the  data  argument.
       This removes any need for globals.

       The callback function should return 0 if no errors occur. On failure it
       should return 1, and register a terse description of the error by call-
       ing cpl_record_error().

         void cpl_record_error(WordCompletion *cpl,
                               const char *errmsg);

       The last error message recorded by calling cpl_record_error(), can sub-
       sequently be queried by calling cpl_last_error(), as described later.

         int cpl_add_completion(WordCompletion *cpl,
                                const char *line, int word_start,
                                int word_end, const char *suffix,
                                const char *type_suffix,
                                const char *cont_suffix);

       The cpl_add_completion() function is called zero or more times  by  the
       completion  callback function to record each possible completion in the
       specified WordCompletion object.  These  completions  are  subsequently
       returned by cpl_complete_word(), as described later. The cpl, line, and
       word_end arguments should be those that were  passed  to  the  callback
       function.  The word_start argument should be the index within the input
       line string of the start of the word  that  is  being  completed.  This
       should  equal  word_end if a zero-length string is being completed. The
       suffix argument is the string that would have to  be  appended  to  the
       incomplete  word  to  complete  it.  If this needs any quoting (eg. the
       addition of backslashes before special charaters) to  be  valid  within
       the displayed input line, this should be included. A copy of the suffix
       string is allocated internally, so there is no need  to  maintain  your
       copy of the string after cpl_add_completion() returns.

       Note  that  in  the  array  of  possible completions which the cpl_com-
       plete_word() function returns, the suffix recorded  by  cpl_add_comple-
       tion()  is listed along with the concatentation of this suffix with the
       word that lies between word_start and word_end in the input line.

       The type_suffix argument specifies an optional string to be appended to
       the  completion  if it is displayed as part of a list of completions by
       cpl_list_completions(). The intention is that this indicate to the user
       the  type of each completion. For example, the file completion function
       places a directory separator after completions that are directories, to
       indicate  their  nature to the user. Similary, if the completion were a
       function, you could indicate this to the user by setting type_suffix to
       "()". Note that the type_suffix string isn't copied, so if the argument
       isn't a literal string between speech marks, be sure  that  the  string
       remains  valid  for  at  least  as  long  as  the  results  of cpl_com-
       plete_word() are needed.

       The cont_suffix is a continuation suffix to  append  to  the  completed
       word  in  the  input line if this is the only completion. This is some-
       thing that isn't part of the completion itself, but that gives the user
       an  indication  about how they might continue to extend the token.  For
       example, the file-completion callback function adds a directory separa-
       tor  if the completed word is a directory. If the completed word were a
       function name, you could similarly aid the user  by  arranging  for  an
       open parenthesis to be appended.

         CplMatches *cpl_complete_word(WordCompletion *cpl,
                                       const char *line,
                                       int word_end, void *data,
                                       CplMatchFn *match_fn);

       The  cpl_complete_word()  is  normally  called  behind  the  scenes  by
       gl_get_line, but can also be called  separately  if  you
       separately  allocate  a WordCompletion object. It performs word comple-
       tion, as described at the beginning of this section. Its first argument
       is  a resource object previously returned by new_WordCompletion().  The
       line argument is the input line string, containing the word to be  com-
       pleted.  The  word_end  argument contains the index of the character in
       the input line, that just follows the last character of the word to  be
       completed.  When  called  by  gl_get_line(), this is the character over
       which the user pressed TAB.  The  match_fn  argument  is  the  function
       pointer of the callback function which will lookup possible completions
       of the word, as described above, and the data argument provides  a  way
       for the application to pass arbitrary data to the callback function.

       If  no errors occur, the cpl_complete_word() function returns a pointer
       to a CplMatches container, as defined below. This  container  is  allo-
       cated as part of the cpl object that was passed to cpl_complete_word(),
       and will thus change on each call which uses the same cpl argument.

         typedef struct {
           char *completion;        /* A matching completion */
                                    /*  string */
           char *suffix;            /* The part of the */
                                    /*  completion string which */
                                    /*  would have to be */
                                    /*  appended to complete the */
                                    /*  original word. */
           const char *type_suffix; /* A suffix to be added when */
                                    /*  listing completions, to */
                                    /*  indicate the type of the */
                                    /*  completion. */
         } CplMatch;

         typedef struct {
           char *suffix;            /* The common initial part */
                                    /*  of all of the completion */
                                    /*  suffixes. */
           const char *cont_suffix; /* Optional continuation */
                                    /*  string to be appended to */
                                    /*  the sole completion when */
                                    /*  nmatch==1. */
           CplMatch *matches;       /* The array of possible */
                                    /*  completion strings, */
                                    /*  sorted into lexical */
                                    /*  order. */
           int nmatch;              /* The number of elements in */
                                    /*  the above matches[] */
                                    /*  array. */
         } CplMatches;

       If an error occurs during completion, cpl_complete_word() returns NULL.
       A   description   of   the   error  can  be  acquired  by  calling  the
       cpl_last_error() function.

         const char *cpl_last_error(WordCompletion *cpl);

       The cpl_last_error() function returns a terse description of the  error
       which  occurred on the last call to cpl_complete_word() or cpl_add_com-
       pletion().

         CplMatches *cpl_recall_matches(WordCompletion *cpl);

       As a convenience, the  return  value  of  the  last  call  to  cpl_com-
       plete_word()   can   be   recalled   at   a   later   time  by  calling
       cpl_recall_matches(). If cpl_complete_word()  returned  NULL,  so  will
       cpl_recall_matches().

         int cpl_list_completions(CplMatches *result, FILE *fp,
                                  int terminal_width);

       When the cpl_complete_word() function returns multiple possible comple-
       tions, the cpl_list_completions() function can be called upon  to  list
       them,  suitably arranged across the available width of the terminal. It
       arranges for the displayed columns of completions to all have the  same
       width,  set  by the longest completion. It also appends the type_suffix
       strings that were recorded with each completion, thus indicating  their
       types to the user.


THE BUILT-IN FILENAME-COMPLETION CALLBACK

       By  default the gl_get_line function, passes the follow-
       ing completion callback function to cpl_complete_word(). This  function
       can  also  be  used  separately,  either  by  sending  it  to  cpl_com-
       plete_word(), or by calling it directly from your own completion  call-
       back function.

         CPL_MATCH_FN(cpl_file_completions);

       Certain aspects of the behavior of this callback can be changed via its
       data argument. If you are happy with its default behavior you can  pass
       NULL  in  this argument. Otherwise it should be a pointer to a CplFile-
       Conf object, previously allocated by calling new_CplFileConf().

         CplFileConf *new_CplFileConf(void);

       CplFileConf  objects  encapsulate  the  configuration   parameters   of
       cpl_file_completions().  These parameters, which start out with default
       values, can be changed by  calling  the  accessor  functions  described
       below.

       By default, the cpl_file_completions() callback function searches back-
       wards for the start of the filename being completed,  looking  for  the
       first  un-escaped  space or the start of the input line. If you wish to
       specify a different location, call cfc_file_start() with the  index  at
       which the filename starts in the input line. Passing start_index=-1 re-
       enables the default behavior.

         void cfc_file_start(CplFileConf *cfc, int start_index);

       By default, when cpl_file_completions() looks  at  a  filename  in  the
       input  line,  each  lone  backslash in the input line is interpreted as
       being a special character which removes any special significance of the
       character  which  follows  it, such as a space which should be taken as
       part of the filename rather than delimiting the start of the  filename.
       These  backslashes  are thus ignored while looking for completions, and
       subsequently added before spaces, tabs and literal backslashes  in  the
       list  of  completions.  To have unescaped backslashes treated as normal
       characters, call cfc_literal_escapes() with a  non-zero  value  in  its
       literal argument.

         void cfc_literal_escapes(CplFileConf *cfc, int literal);

       By  default, cpl_file_completions() reports all files who's names start
       with the prefix that is being completed. If you only  want  a  selected
       subset  of  these  files to be reported in the list of completions, you
       can arrange this by providing a callback function which takes the  full
       pathname  of  a file, and returns 0 if the file should be ignored, or 1
       if the file should be included in the list of completions. To  register
       such    a    function   for   use   by   cpl_file_completions(),   call
       cfc_set_check_fn(), and pass it a pointer  to  the  function,  together
       with  a pointer to any data that you would like passed to this callback
       whenever it is called. Your callback can make its  decisions  based  on
       any property of the file, such as the filename itself, whether the file
       is readable, writable or executable, or even based  on  what  the  file
       contains.

         #define CPL_CHECK_FN(fn) int (fn)(void *data, \
                                           const char *pathname)
         typedef CPL_CHECK_FN(CplCheckFn);

         void cfc_set_check_fn(CplFileConf *cfc,
                               CplCheckFn *chk_fn, void *chk_data);

       The  cpl_check_exe() function is a provided callback of the above type,
       for use with cpl_file_completions(). It returns non-zero if  the  file-
       name  that  it is given represents a normal file that the user has exe-
       cute permission to. You could use this to  have  cpl_file_completions()
       only list completions of executable files.

       When  you have finished with a CplFileConf variable, you can pass it to
       the del_CplFileConf() destructor function to reclaim its memory.

         CplFileConf *del_CplFileConf(CplFileConf *cfc);



THREAD SAFETY

       In multi-threaded programs, you should use the libtecla_r.a version  of
       the library. This uses POSIX reentrant functions where available (hence
       the _r suffix), and disables features that rely on non-reentrant system
       functions.  In  the  case  of this module, the only disabled feature is
       username completion  in  ~username/  expressions,  in  cpl_file_comple-
       tions().

       Using  the  libtecla_r.a  version of the library, it is safe to use the
       facilities of this module  in  multiple  threads,  provided  that  each
       thread  uses  a  separately  allocated  WordCompletion object. In other
       words, if two threads want to do word completion, they should each call
       new_WordCompletion() to allocate their own completion objects.


FILES

       libtecla.a    -    The tecla library
       libtecla.h    -    The tecla header file.


SEE ALSO

       libtecla, gl_get_line, ef_expand_file,
       pca_lookup_file


AUTHOR

       Martin Shepherd  (mcs@astro.caltech.edu)



                                              cpl_complete_word
yuma123_2.14/libtecla/html/index.html0000664000175000017500000001130114770023131017637 0ustar vladimirvladimirThe tecla command-line editing library.

The Tecla command-line editing library.

The tecla library provides UNIX and LINUX programs with interactive command line editing facilities, similar to those of the UNIX tcsh shell. In addition to simple command-line editing, it supports recall of previously entered command lines, TAB completion of file names or other tokens, and in-line wild-card expansion of filenames. The internal functions which perform file-name completion and wild-card expansion are also available externally for optional use by programs.

In addition, the library includes a path-searching module. This allows an application to provide completion and lookup of files located in UNIX style paths. Although not built into the line editor by default, it can easily be called from custom tab-completion callback functions. This was originally conceived for completing the names of executables and providing a way to look up their locations in the user's PATH environment variable, but it can easily be asked to look up and complete other types of files in any list of directories.

Note that special care has been taken to allow the use of this library in threaded programs. The option to enable this is discussed in the Makefile, and specific discussions of thread safety are presented in the included man pages.

The current version is version 1.6.1. This may be obtained from:

http://www.astro.caltech.edu/~mcs/tecla/libtecla-1.6.1.tar.gz

For the sake of automated scripts, the following URL always points to the latest version. Note that the version number can be found in the README file.

http://www.astro.caltech.edu/~mcs/tecla/libtecla.tar.gz

The library is distributed under a permissive non-copyleft free software license (the X11 license with the name of the copyright holder changed). This is compatible with, but not as restrictive as the GNU GPL.

Release notes

The list of major changes that accompany each new release can be found here.

Modifications

The gory details of changes in the latest and previous versions of the library can be found here.

Library documentation

The following are html versions of the libtecla man pages:
  • tecla - Documentation for users of programs which use gl_get_line().
  • libtecla - A programmers introduction to the tecla library.
  • gl_get_line - The interactive line-input function.
  • gl_io_mode - Using gl_get_line() in a non-blocking fashion.
  • cpl_complete_word - The word (eg. filename) completion function.
  • ef_expand_file - The filename expansion function.
  • pca_lookup_file - A directory-list based filename lookup and completion module.
  • enhance - A program that adds command-line editing to third party programs.

Portability

In principle, the standard version of the library should compile without any problems on any UNIX or UNIX like system. So far it has been reported to work on the following systems:
  Sun Solaris 2.5,2.6,7,8,9 with any of gcc, Sun C, or g++.
  Mandrake Linux 7.1 etc.., gcc
  Red Hat Linux 7 etc.., gcc
  Fedora Core 1, gcc
  Suse Linux 6.4, gcc
  IBM AIX 4.3.3, gcc
  HP-UX 10.20, HP-UX 11, gcc, c89
  FreeBSD, gcc
  Alpha OSF1, cc, gcc
  Mac OS X
  Cygwin (running under Windows)
  QNX
  NetBSD 1.6, 386, gcc
  SGI IRIX 6.5
There haven't been many reports concerning the POSIX reentrant version, so the absence of any of the above from the following list of systems on which the reentrant version is known to work, shouldn't be taken as an indication that the reentrant version doesn't work.
  Sun Solaris 2.5,2.6,7,8,9 with any of gcc, Sun C, or g++.
  Mandrake Linux, gcc
  RedHat Linux, gcc
  Fedora Core, gcc
  SuSE Linux, gcc
  HP-UX 11, gcc
  IBM AIX 4.3.3, gcc
  Alpha OSF1, cc
  SGI IRIX 6.5
The only system that is known to have issues with the reentrant version of the library is SCO UnixWare 7.1.1. The problem is in the system provided signal.h, which breaks when POSIX_C_SOURCE is defined. It has been reported that this can be "fixed" by editing signal.h.

If you compile the library on a system that isn't mentioned above, please send E-mail to mcs@astro.caltech.edu.


Martin Shepherd (31-Oct-2004) yuma123_2.14/libtecla/html/libtecla.html0000664000175000017500000001442114770023131020315 0ustar vladimirvladimir Manual Page
libtecla                                libtecla



NAME

       libtecla - An interactive command-line input library.

SYNOPSIS

       @CC@ ... -ltecla -lcurses


DESCRIPTION

       The tecla library provides programs with interactive command line edit-
       ing facilities, similar to those of the unix tcsh shell. In addition to
       simple  command-line  editing, it supports recall of previously entered
       command lines, TAB completion of file names or other  tokens,  and  in-
       line  wild-card  expansion  of  filenames. The internal functions which
       perform file-name completion and wild-card expansion are also available
       externally for optional use by the calling program.

       The  various  parts  of the library are documented in the following man
       pages:

         tecla              -  Use level documentation of the
                               command-line editing facilities
                               provided by gl_get_line().
         gl_get_line        -  The interactive line-input module.
         gl_io_mode         -  How to use gl_get_line() in an
                               incremental, non-blocking fashion.
         cpl_complete_word  -  The word completion module.
         ef_expand_file     -  The filename expansion module.
         pca_lookup_file    -  A directory-list based filename
                               lookup and completion module.

       In addition there is one  optional  application  distributed  with  the
       library:

         enhance            -  Add command-line editing to third
                                  party applications.


THREAD SAFETY

       If  the  library  is compiled with -D_POSIX_C_SOURCE=199506L, reentrant
       versions of as many functions as possible are used. This includes using
       getpwuid_r() and getpwnam_r() instead of getpwuid() and getpwnam() when
       looking up the home directories of specific users in the password  file
       (for  ~user/ expansion), and readdir_r() instead of readdir() for read-
       ing directory entries when doing  filename  completion.  The  reentrant
       version of the library is usually called libtecla_r.a instead of libte-
       cla.a, so if only the latter is available, it probably isn't  the  cor-
       rect version to link with threaded programs.

       Reentrant  functions  for  iterating  through  the password file aren't
       available, so when the library is compiled to be reentrant, TAB comple-
       tion  of  incomplete  usernames  in ~username/ expressions is disabled.
       This doesn't disable expansion of complete ~username expressions, which
       can  be  done  reentrantly, or expansion of the parts of filenames that
       follow them, so this doesn't remove much functionality.

       The terminfo functions setupterm(), tigetstr(), tigetnum() and  tputs()
       also aren't reentrant, but very few programs will want to interact with
       multiple terminals, so this shouldn't prevent this library  from  being
       used in threaded programs.


LIBRARY VERSION NUMBER

       The  version  number  of the library can be queried using the following
       function.

        void libtecla_version(int *major, int *minor, int *micro);


       On return, this function records the three components of  the  libtecla
       version  number  in  *major,  *minor, *micro. The formal meaning of the
       three components is as follows.


        major - Incrementing this number implies that a change has
                been made to the library's public interface, which
                makes it binary incompatible  with programs that
                were linked with previous shared versions of the
                tecla library.

        minor - This number is incremented by one whenever
                additional functionality, such as new functions or
                modules, are added to the library.

        micro - This is incremented whenever modifications to the
                library are made which make no changes to the
                public interface, but which fix bugs and/or improve
                the behind-the-scenes implementation.



TRIVIA

       In Spanish, a "tecla" is the key of a keyboard. Since this library cen-
       ters  on  keyboard  input,  and  given that I wrote much of the library
       while working in Chile, this seemed like a suitable name.


FILES

       libtecla.a    -   The tecla library.
       libtecla.h    -   The tecla header file.
       ~/.teclarc    -   The tecla personal customization file.


SEE ALSO

       gl_get_line, tecla, gl_io_mode, ef_expand_file,
       cpl_complete_word, pca_lookup_file, enhance


AUTHOR

       Martin Shepherd  (mcs@astro.caltech.edu)


ACKNOWLEDGMENTS

       Markus Gyger  - Lots of assistance, including help with
                       shared libraries, configuration information,
                       particularly for Solaris; modifications to
                       support C++ compilers, improvements for ksh
                       users, faster cursor motion, output
                       buffering, and changes to make gl_get_line()
                       8-bit clean.
       Mike MacFaden - Suggestions, feedback and testing that led
                       to many of the major new functions that were
                       added in version 1.4.0.
       Tim Eliseo    - Many vi-mode bindings and fixes.



                                                       libtecla
yuma123_2.14/libtecla/html/changes.html0000664000175000017500000042462114770023131020155 0ustar vladimirvladimirThe tecla library change log
In the following log, modification dates are listed using the European
convention in which the day comes before the month (ie. DD/MM/YYYY).
The most recent modifications are listed first.

31/10/2004 mcs@astro.caltech.edu (problem reported by Godfrey van der Linden) 
           getline.c
             The gl_event_handler() function had the endif of a
             conditional compilation clause in the wrong place. This
             only upset the compiler on unusual systems that don't
             have select(). The problem was seen under Mac OS X, due
             to the configuration problem in 1.6.0 that caused the
             configure script to mistakenly report that select wasn't
             available.

31/10/2004 mcs@astro.caltech.edu (info provided by Ivan Rayner)
           configure.in configure Makefile.in
             Ivan reported that under IRIX 6.5 it is necessary to add
             -D_XOPEN_SOURCE=500 to the compiler flags, when compiling
             the reentrant version of the library. Thus, whereas
             previously I hardwired the value of DEFINES_R in
             Makefile.in, I have now made this a variable in the
             configure script, which is augmented with the above
             addition, within an IRIX-specific switch clause.

             Also apparently configure leaves the RANLIB variable
             blank, instead of setting it to ":", so I have now
             explicitly set this to ":", within the new IRIX clause of
             the configure script.

31/10/2004 mcs@astro.caltech.edu (info provided by Ivan Rayner)
           getline.c
             Under IRIX, the compiler warned that gl_read_unmasked()
             was returning an int, which was then being assigned to an
             enumeration type. This is techically fine, but it
             highlighted the fact that I had meant to declare
             gl_read_unmasked() to directly return the enumerated
             type. I have now done so.

26/09/2004 mcs@astro.caltech.edu
           getline.c
             Users can now turn off interactive command-line editing
             by setting the TERM environment variable to the word "dumb".

18/07/2004 mcs@astro.caltech.edu (problem noted by Michael MacFaden)
           getline.c
             Calling gl_terminal_size() on a system without support
             for SIGWINCH caused a divide-by-zero error in an unintended
             call to gl_erase_line(), because gl_update_size() was
             incorrectly being called to query the terminal size,
             instead of gl_query_size().

18/07/2004 Padraig Brady  (documented here by mcs@astro.caltech.edu)
           getline.c
             The suspend and termination signal-handlers installed by
             gl_tty_signals(), were being installed swapped.

03/06/2004 Mike Meaney  (documented here by mcs@astro.caltech.edu)
           getline.c
             Mike pointed out the fact that the curses setupterm()
             function is actually documented to exit the application
             if an error occurs while its optional errret argument is
             NULL. I hadn't noticed this, and because I didn't need
             the extra information returned in the errret argument, I
             was passing it a NULL. As suggested by Mike, I now pass
             this argument a pointer to a dummy errret variable.

23/05/2004 mcs@astro.caltech.edu (problem noted by John Beck)
           man/func/cpl_complete_word.in
             Some of the prototypes of functions and types documented
             by the cpl_complete_word man page, weren't listed in the
             Synopsis section of this man page. They are now listed
             there.
            
23/05/2004 mcs@astro.caltech.edu
           getline.c man/func/gl_get_line.in
             I have now added support for calling gl_normal_io() from
             any callback functions that the application installs by
             calling either gl_inactivity_timeout(), or gl_watch_fd().
             Previously, if one of these callback functions called
             gl_normal_io(), then after returning to gl_get_line(),
             gl_get_line() would incorrectly assume that the terminal
             was still in raw I/O mode. Now, gl_get_line() checks to
             see if gl_normal_io() was called by the callback, and
             if so, calls _gl_raw_io() to reinstate raw I/O mode.

21/05/2004 mcs@astro.caltech.edu
           configure.in configure
             On Mac OS X the code that the configure script used to
             check for select() failed due to missing symbols in
             sys/select.h. Moving the inclusion of sys/select.h to
             after the inclusion of sys/time.h, sys/types.h and
             sys/unistd.h fixed this.

11/05/2004 mcs@astro.caltech.edu
           getline.c man/func/gl_get_line.in
             If the line buffer returned by one call to gl_get_line()
             was passed as the start_line argument of the next call to
             gl_get_line(), then instead of the just-entered line
             being presented back to the user for further editing, the
             start_line argument was effectively ignored, because the
             line buffer whose pointer was being passed back, was
             being cleared before the start_line pointer was examined.
             This appears to have been a case of me incorrectly
             thinking that I had forgotten to initialize gl->line[]
             and gl->ntotal in the gl_reset_input_line() function, and
             then "fixing" this supposed omission. Removing this
             erroneous fix, restored things to how they were meant to
             be. To make it unlikely that I will make the same mistake
             again, I have renamed the function from
             gl_reset_input_line() to gl_reset_editor(), to stop it
             looking as though it is meant to reset the contents of
             the input line (that is what gl_truncate_buffer() is
             for), explicitly stated that it doesn't clear the input
             line, in the header comments of the function, and added a
             prominent warning comment in the body of the function.

             Also, since support for passing back the returned line
             pointer via the start_line argument of the next call to
             gl_get_line(), wasn't documented in the man page, but was
             meant to be supported, and definitely used to work, I
             have now amended the man page documentation of
             gl_get_line() to explicitly state that this feature is
             officially supported.

2?/04/2004 Released 1.6.0

22/04/2004 mcs@astro.caltech.edu  (Fixed a bug reported by John Beck)
           getline.c
             When an error, signal, or other abnormal event aborted
             gl_get_line(), the cleanup code that restored the
             terminal to a sane state, also overwrote the value of
             errno that was associated with the aborting event. An
             I/O error occurring in the cleanup code would have also
             overwritten the value to be returned by
             gl_return_status(), and thus remove any possibility of
             the caller finding out what really caused gl_get_line()
             to abort. I have now written a new internal function
             called, gl_record_status(), which records the completion
             status to be returned by gl_return_status(), and the
             value to assign to errno just before gl_get_line()
             returns. This is called wherever code detects conditions
             that require gl_get_line() to return early. The function
             ensures that once an abnormal completion status has been
             recorded for return, subsequent completions statuses
             aren't recorded. This ensures that the caller sees the
             original cause of the abnormal return, rather than any
             error that occurs during cleaning up from this before
             return.

17/04/2004 mcs@astro.caltech.edu
           getline.c
             If an application's callback called gl_read_char() after
             calling gl_normal_io(), it would inappropriately
             redisplay the input line, when it called _gl_raw_io() to
             temporarily switch the terminal back into raw mode.

             To fix this, _gl_raw_io() now takes a new 'redisplay'
             argument, which specifies whether or not to queue a
             redisplay of the input line. I also created a new
             gl->postpone flag, which is set by gl_normal_io(), and
             cleared by _gl_raw_io() (when its redisplay argument is
             true). When this flag is set, gl_flush_output() ignores
             queued redisplays, as it generally should between calls
             to gl_normal_io() and gl_raw_io(). Thus its effect is to
             postpone redisplays while line editing is suspended.

11/04/2004 mcs@astro.caltech.edu
           history.c man/misc/tecla.in
             History searches can now include the globbing operators
             *, ?, []. When a search prefix is found to have at least
             one of these characters, then only history lines that
             completely match that pattern are returned.

11/04/2004 mcs@astro.caltech.edu  (issue raised by Mark Coiley)
           getline.c ioutil.c
             There appears to be a bug in Solaris's terminal I/O.
             When the terminal file descriptor is placed in
             non-blocking I/O mode, and the terminal is switched from
             canonical to raw mode, characters that were previously
             entered in canonical I/O mode don't become available to
             be read until the user types one character more. Select()
             incorrectly says that there are no characters available,
             and read() returns EAGAIN. This is only a problem for
             gl_get_line() when gl_get_line() is in non-blocking
             server I/O mode, so most users won't have experienced any
             problems with this.

             The only way that I have found to get read() to return
             the characters, without the user first having to type
             another character, is to turn off non-blocking I/O before
             calling read(). Select() still claims that there are no
             characters available to be read, but read happily returns
             them anyway. Fortunately, one can perform non-blocking
             terminal reads without setting the non-blocking I/O flag
             of the file descriptor, simply by setting the VTIME
             terminal attribute to zero (which I already was
             doing). Thus, when in non-blocking server I/O, I now turn
             off the non-blocking I/O flag, attempt to read one
             character and only if this fails, do I then call the
             select() based event handler to implement any configured
             non-zero timeout, before attempting the read again. Of
             course the non-blocking I/O flag is still needed for
             writing, so I only turn it off temporarily while reading.

25/03/2004 mcs@astro.caltech.edu  (bug reported by Gregory Harris)
           Makefile.in
             It appears that when in February, I patched Makefile.in
             to add abolute paths to the install-sh shell-script,
             I accidentally replaced install-sh with install.sh. I
             corrected the name in the Makefile.

25/03/2004 Gregory Harris  (documented here by mcs)
           configure.in configure
             Greg added the configuration parameters needed to build
             the shared version of the libtecla library under FreeBSD.

25/03/2004 mcs@astro.caltech.edu
           getline.c libtecla.h libtecla.map man/func/gl_get_line.in
           man/func/gl_read_char.in
             I wrote a public function called gl_read_char(). Unlike
             gl_query_char(), this function neither prompts the user
             for input, nor displays the character that was entered.
             In fact it doesn't write anything to the terminal, and
             takes pains not to disturb any incompletely entered
             input line, and can safely be called from application
             callback functions.

21/03/2004 mcs@astro.caltech.edu
           getline.c libtecla.h libtecla.map man/func/gl_get_line.in
           man/func/gl_query_char.in
             I wrote a public function called gl_query_char(), which
             prompts the user and awaits a single-character reply,
             without the user having to hit return.

23/02/2004 mcs@astro.caltech.edu (bug reported by Gregory Harris)
           configure.in configure getline.c enhance.c demo3.c
             The configure script now checks for the sys/select.h
             header file, and arranges for a C macro called
             HAVE_SYS_SELECT_H to be set if it exists. Thus the files
             that use select() now use this macro to conditionally
             include sys/select.h where available. Apparently this
             header is required under FreeBSD 5.1.

23/02/2004 mcs@astro.caltech.edu
           getline.c libtecla.h man/func/gl_get_line.in
             I wrote two new public functions, gl_append_history() and
             gl_automatic_history(). Together these allow the
             application to take over the responsibility of adding
             lines to the history list from gl_get_line(). I then
             documented their functionality in the gl_get_line man
             page.
           Version 1.6.0
             I incremented the minor version number of the library, to
             comply with the requirement to do so when additions are
             made to the public interface. See libtecla.map for
             details.
           libtecla.map
             I added a new 1.6.0 group for the new minor version, and
             added the above pair of functions to it.

15/02/2004 mcs@astro.caltech.edu (fixes a bug reported by Satya Sahoo)
           history.c
             Calling gl_load_history() multiple times, eventually led
             to a segmentation fault. This was due to the head of the
             list of unused history string segments not getting
             reset when the history buffer was cleared. While
             debugging this problem I also noticed that the history
             resizing function was way too complicated to verify, so
             after fixing the above bug, I heavily simplified the
             history resizing function, trading off a small reduction
             in memory efficiency, for greatly improved clarity, and
             thus made it much more verifiable and maintainable.

14/02/2004 mcs@astro.caltech.edu (fixes a bug reported by Tim Burress).
           getline.c
             If gl_change_terminal() was first used to tell
             gl_get_line to read input from a file, then called later
             to tell it to read subsequent input from a terminal, no
             prompt would be displayed for the first line of
             interactive input. The problem was that on reaching the
             end of the input file, gl_get_line() should have called
             gl_abandon_line(), to tell the next call to gl_get_line()
             to start inputting a new line from scratch. I have added
             this now.

14/02/2004 Krister Walfridsson (documented here by mcs@astro.caltech.edu)
           Makefile.in
             Krister noticed that I had failed to put $(srcdir)/ in front
             of some invokations of install.sh. I have remedied this.
           config.guess config.sub
             I hadn't updated these for a long time, so apparently they
             didn't recognise the BSD system that Krister was using.
             I have now updated them to the versions that come with
             autoconf-2.59. 

22/01/2004 mcs@astro.caltech.edu
           keytab.c
             When parsing key-binding specifications, backslash escaped
             characters following ^ characters were not being expanded.
             Thus ^\\ got interpretted as a control-\ character followed
             by a \ character, rather than simply as a control-\
             character.

12/01/2004 mcs@astro.caltech.edu
           cplfile.c cplmatch.c demo2.c demo3.c demo.c direader.c
           expand.c getline.c history.c homedir.c pathutil.c pcache.c
           configure.in configure INSTALL
             The configuration script now takes a
             "--without-file-system" argument. This is primarily for
             intended for embedded systems that either don't have
             filesystems, or where the file-system code in libtecla is
             unwanted bloat. It sets the WITHOUT_FILE_SYSTEM
             macro. This removes all code related to filesystem
             access, including the entire public file-expansion,
             file-completion and path-lookup facilities. Note that the
             general word completion facility is still included, but
             without the normally bundled file completion
             callback. Actually the callback is still there, but it
             reports no completions, regardless of what string you ask
             it to complete.

             This option is described in the INSTALL document.

12/01/2004 mcs@astro.caltech.edu
           getline.c configure.in configure INSTALL
             The configuration script now takes a
             "--without-file-actions" argument. This allows an
             application author/installer to prevent users of
             gl_get_line() from accessing the filesystem from the
             builtin actions of gl_get_line(). It defines a macro
             called HIDE_FILE_SYSTEM. This causes the
             "expand-filename", "read-from-file", "read-init-files",
             and "list-glob" action functions to be completely
             removed. It also changes the default behavior of actions
             such as "complete-word" and "list-or-eof" to show no
             completions, instead of the normal default of showing
             filename completions.

             This option is described in the INSTALL document.

11/01/2004 mcs@astro.caltech.edu
           getline.c man/func/gl_get_line.in
             In case an application's customized completion handler
             needs to write to the terminal for some unforseen reason,
             there needs to be a way for the it to cleanly suspend raw
             line editing, before writing to the terminal, and the
             caller then needs to be aware that it may need to
             resurrect the input line when the callback returns. I
             have now arranged that the completion callback functions
             can call the gl_normal_io() function for this purpose,
             and documented this in the gl_get_line() man page.

11/01/2004 mcs@astro.caltech.edu  (In response to a bug report by Satya Sahoo)
           getline.c
             The gl_configure_getline() function makes a malloc'd copy
             of the names of the configuration files that it is asked
             to read. Before the bug fix, if the application made one
             or more calls to this function, the memory allocated by
             the final call that it made before calling del_GetLine(),
             wasn't being freed. Note that memory allocated in all but
             the final call was being correctly freed, so the maximum
             extent of the memory leak was the length of the file
             name(s) passed in the final call to
             gl_configure_getline(), and an application that didn't
             call gl_configure_getline() didn't suffer any leak.

20/12/2003 mcs@astro.caltech.edu
           history.c
             Ellen tested the history fix that I reported below, and
             pointed out that it still had a problem. This turned out
             to be because getline.c was making some incorrect
             assumptions about the new behavior of history.c. This
             problem and the previous one both revolved around how
             search prefixes were stored and discarded, so I have now
             re-written this part of the code. Previously the search
             prefix was retained by looking for a line with that
             prefix, and keeping a pointer to that line. This saved
             memory, compared to storing a separate copy of the
             prefix, but it led to all kinds of hairy
             interdependencies, so I have now changed the code to keep
             a separate copy of search prefixes. To keep the memory
             requirements constant, the search prefix is stored in the
             history buffer, like normal history lines, but not
             referenced by the time-ordered history list. The prefix
             can now be kept around indefinitely, until a new search
             prefix is specified, regardless of changes to the
             archived lines in the history buffer. This is actually
             necessary to make the vi-mode re-search actions work
             correctly. In particular, I no longer discard the search
             prefix whenever a history search session ends. Also,
             rather than have getline.c keep its own record of when a
             history session is in progress, it now consults
             history.c, so that failed assumptions can't cause the
             kind of discrepancy that occurred before. For this to
             work, getline.c now explicitly tells history.c to cancel
             search sessions whenever it executes any non-history
             action.

14/12/2003 mcs@astro.caltech.edu (bug reported by Ellen Oschmann)
           history.c
             If one searched backwards for a prefix, then returned to
             the original line, changed that line, then started
             another backwards prefix search, getline incorrectly
             discarded the new search prefix in the process of
             throwing away its cached copy of the previous pre-search
             input line. In other words getline was belatedly
             cancelling a previous search, after a new search had
             already partially begun, and thus messed up the new
             search. The obvious fix was to arrange for the current
             search to be cancelled whenever the history pointer
             returns to its starting point, rather than waiting for
             the next search to begin from there.

14/12/2003 mcs@astro.caltech.edu
           history.c
             _glh_recall_line() was returning the last line in the
             history buffer instead of the line requested by the
             caller. This only affected the obscure "repeat-history"
             action-function, which probably isn't used by anybody.

09/12/2003 Version 1.5.0 released.

28/09/2003 mcs@astro.caltech.edu
           homedir.c
             When the home directory of the login user is requested,
             see if the HOME environment variable exists, and if so
             return its value, rather than looking up the user's home
             directory in the password file. This seems to be the
             convention adopted by other unix programs that perform
             tilde expansion, and it works around a strange problem,
             where a third-party libtecla program, statically compiled
             under an old version of RedHat, unexpectedly complained
             that getpwd() returned an error when the program was run
             under RedHat 9.

01/09/2003 mcs@astro.caltech.edu
           getline.c libtecla.h libtecla.map man/func/gl_get_line.in
           man/func/gl_register_action.in.
             It is now possible for an application to register
             external functions as action functions. These actions are
             initially bound to specified key-sequences, but if they
             are registered before the user's configuration file is
             loaded, they can also be re-bound by the user to
             different key-sequences. The function used to register a
             new action, is called gl_register_action().  Action
             functions are passed a readonly copy of the input line
             and the cursor position. They can display text to the
             terminal, or perform other operations on the application
             environment. Currently, they can't edit the input line or
             move the cursor. This will require the future addition of
             functions to queue the invokation of the built-in action
             functions.

26/08/2003 mcs@astro.caltech.edu
           getline.c
             I modified gl_update_buffer() to ensure that the cursor
             stays within the input line after external line
             modifications, and to queue a redisplay of the
             potentially modified input line.

21/07/2003 mcs@astro.caltech.edu
           configure.in configure Makefile.in Makefile.stub INSTALL
             By specifying --without-man-pages or --with-man-pages=no
             as command-line arguments to the configure script, it is
             now possible to have the configure script skip the
             man-page preprocessing step, and arrange for the man-page
             installation targets in the Makefile to do nothing. This
             option is designed for people who embed libtecla within
             other packages. It is also used by Makefile.stub when
             the distclean target is specified.

21/07/2003 mcs@astro.caltech.edu
           configure.in configure
             The previous workaround for recent versions of gcc
             placing /usr/local/include at the start of the system
             inlcude-file search path, broke something else.  The fix
             placed /usr/include before gcc's include area, which
             meant that gcc's modified version of stdarg.h was being
             ignored in deference to the version in /usr/include. I
             have changed the fix to have gcc report the search path,
             then have awk add options to CFLAGS to reorder this path,
             plaing /usr/local/include at the end.

             Also, under Solaris 9, including term.h without first
             including curses.h results in complaints about undefined
             symbols, such as bool. As a result the configure script's
             test for term.h was failing. I have now modified it to
             include curses.h in the test code that it uses to check
             for term.h. In the process I also improved the tests for
             curses.h and term.h to prevent an ncurses version of
             term.h from being used with the system-default version of
             curses.h.

29/06/2003 mcs@astro.caltech.edu
           Makefile.in direader.c homedir.c
             On some systems (eg. linux) the _POSIX_C_SOURCE
             feature-test macro is set by system headers, rather than
             being an option set by a project's Makefile at
             compilation time.  In software, such as tecla, where the
             definition of this macro is used as an indication of
             whether to use the non-reentrant or reentrant versions of
             system functions, this means that the reentrant functions
             are always used, regardless of whether this macro is set
             or not by the project Makefile. Thus, on such systems the
             reentrant and non-reentrant versions of the tecla library
             are essentially identical. This has a couple of
             drawbacks.  First, since thread-safe functions for
             traversing the password file don't exist, the supposedly
             non-reentrant version of the tecla library can't support
             ambiguous tab-completion of usernames in ~username/
             constructions. Secondly, on some systems the use of
             reentrant system functions dictates the use of a shared
             library that isn't needed for the non-reentrant
             functions, thus making it more difficult to distribute
             binary versions of the library.

             To remedy this situation I have modified the DEFINES_R
             variable in Makefile.in to arrange for the compiler to
             define a C macro called PREFER_REENTRANT when it is
             compiling the reentrant version of the tecla library.
             This macro is now used in the source code to determine
             when to require reentrant code. Whithin the source code,
             wherever a potentially non-reentrant interface is used,
             the existance of both this macro and a suitably valued
             _POSIX_C_SOURCE macro, are tested for to see if a
             reentrant alternative to the problem code should be used.

22/06/2003 mcs@astro.caltech.edu
           getline.c
             I changed the way that redisplays are requested and
             performed.  Redisplays are now queued by calling
             gl_queue_redisplay(), and subsequently performed by
             gl_flush_output(), when the queue of already pending
             output has been completely dispatched. This was necessary
             to prevent event handlers from filling up the output
             queue with redisplays, and it also simplifies a number of
             things. In the process I removed the gl_queue_display()
             function. I also wrote a gl_line_erased() function, which
             is now called by all functions that erase the input
             line. I also split the gl_abandon_line() function into
             public and private callable parts, and used the private
             version internally to arrange to discard the input line
             after errors.

             The raw_mode flag was not being initialized by new_GetLine().
             It is now initialized to zero.

             I removed the zapline flag, since using the endline flag to
             communicate the desire to terminate the line, did the same
             thing.

             gl_terminal_move_cursor() now does nothing when the input
             line isn't displayed.

18/03/2003 mcs@astro.caltech.edu
           getline.c
             Fixed bug which was causing newlines not to be output
             at the end of each newly entered line. I was
             interpreting the gl->endline flag in conflicting ways in
             two places. To fix this I have created a gl->displayed
             flag. This flags whether an input line is currently
             displayed.

17/03/2003 mcs@astro.caltech.edu
           getline.c libtecla.h man/func/gl_get_line.in
           man/func/gl_erase_terminal.in libtecla.map
             I added a new function that programs can call to clear
             the terminal between calls to gl_get_line().

11/03/2003 mcs@astro.caltech.edu
           configure.in configure
             Under linux when _POSIX_C_SOURCE is defined, getpwent()
             and associated functions become undefined, because
             _SVID_SOURCE and _BSD_SOURCE become undefined. Adding
             these feature macros back to CFLAGS resolves this.

06/03/2003 mcs@astro.caltech.edu
           getline.c libtecla.map man/func/gl_get_line.in
             Following the lead of Edward Chien, I wrote a function
             called gl_bind_keyseq(), which binds a specified
             key-sequence to a given action, or unbinds the
             key-sequence.

24/02/2003 mcs@astro.caltech.edu
           getline.c libtecla.map man/func/cpl_complete_word.in
             I implemented a simple function called
             cpl_recall_matches().  This recalls the return value of
             the last call to cpl_complete_word().

19/01/2003 mcs@astro.caltech.edu
           getline.c
             The documented signal handling, fd event-handling,
             inactivity timeout handling, and server-mode non-blocking
             I/O features are now implemented for non-interactive
             input streams, such as pipes and files.

19/01/2003 mcs@astro.caltech.edu
           getline.c libtecla.h man/func/gl_get_line.in demo3.c
             I added a new return status enumerator to report
             when an end-of-file condition causes gl_get_line()
             to return NULL.

13/01/2003 mcs@astro.caltech.edu
           history.c
             I rewrote the history facility. The previous
             circular buffer implementation was a nightmare to change,
             and it couldn't efficiently support certain newly
             requested features. The new implementation stores history
             lines in linked lists of fixed sized string segments,
             taken from the buffer, with each line being reference
             counted and recorded in a hash table. If the user enters
             a line multiple times, only one copy of the line is now
             stored. Not only does this make better use of the
             available buffer space, but it also makes it easy to
             ensure that a line whose prefix matches the current
             search prefix, isn't returned more than once in sequence,
             since we can simply see if the latest search result has
             the same hash-table pointer as the previous one, rather
             than having to compare strings. Another plus is that due
             to the use of linked lists of nodes of fixed size line
             segments, there is no longer any need to continually
             shuffle the contents of the buffer in order to defragment
             it. As far as the user is concerned, the visible
             differences are as follows:

             1. If the user enters a given line multiple times in a
                row, each one will be recorded in the history list,
                and will thus be listed by gl_show_history(), and
                saved in the history file. Previously only one line
                was recorded when consecutive duplicates were entered.
                This was a kludge to prevent history recall from
                recalling the same line multiple times in a row. This
                only achieved the desired result when not recalling by
                prefix.

             2. Not only simple recall, but prefix-based history line
                recalls now don't return the same line multiple times
                in a row. As mentioned in (1) above, previously this
                only worked when performing a simple recall, without a
                search prefix.

28/12/2002 mcs@astro.caltech.edu
           getline.c
             The one-line function, gl_buff_curpos_to_term_curpos()
             was only being used by gl_place_cursor(), so I inlined it
             in that function, and removed it.

28/12/2002 mcs@astro.caltech.edu
           getline.c
             gl_suspend_process() was calling the application-level
             gl_normal_io() and gl_raw_io() functions, where it should
             have been calling the internal versions _gl_normal_io()
             and _gl_raw_io().
             Also gl_handle_signal() was masking and unmasking just
             the signals of the first element of the gl[] array
             argument. It now masks and unmasks all trappable signals.

28/12/2002 mcs@astro.caltech.edu
           getline.c
             Now that the number of terminal characters used to
             display the current input line, is recorded, the relative
             line on which the last character of the input line
             resides can be determined without having to call
             gl_buff_curpos_to_term_curpos(). This is now used by
             gl_normal_io() via gl_start_newline(), so there is now no
             need for gl_buff_curpos_to_term_curpos() to be
             async-signal safe. I have thus removed the annoying
             gl->cwidth[] array, and gl_buff_curpos_to_term_curpos()
             now calls gl_width_of_char() directly again. There is
             also now no need for the gl_line_of_char_start() and
             gl_line_of_char_end() functions, so I have removed them.

28/12/2002 mcs@astro.caltech.edu
           getline.c
             Unfortunately it turns out that the terminfo/termcap
             control sequence which is defined to delete everything
             from the current position to the end of the terminal, is
             only defined to work when at the start of a terminal
             line. In gnome terminals in RedHat 8.0, if it is used
             within a terminal line, it erases the whole terminal
             line, rather than just what follows the cursor. Thus to
             portably truncate the displayed input line it is
             necessary to first use the control sequence which deletes
             from the cursor position to the end of the line, then if
             there are more terminal lines, move to the start of the
             next line, and use the delete to end-of-terminal control
             sequence, then restore the cursor position. This requires
             that one know how many physical terminal lines are used
             by the current input line, so I now keep a record of the
             number of characters so far displayed to the terminal
             following the start of the prompt, and the new
             gl_truncate_display() function uses this information to
             truncate the displayed input line from the current cursor
             position.

28/12/2002 mcs@astro.caltech.edu
           getline.c
             gl_start_newline() now moves to an empty line following
             the input line, rather than just to the next line. It
             also arranges for the input line to be redisplayed before
             editing resumes. A major user of this is gl_print_info(),
             which now need not be followed by an explicit call to
             gl_redisplay(), since the terminal input loop in
             gl_get_input_line() ensures that gl_redisplay() is called
             after any action function that asserts gl->redisplay.
             Also, all functions that erase the displayed input line
             can now call the gl_erase_line() function, which is
             designed to work correctly even when a terminal resize
             invalidates the horizontal cursor position.  Finally, the
             new gl_queue_display() function is now used by functions
             that need to arrange for the input line to be displayed
             from scratch after the displayed line has been erased or
             invalidated by other text being written to the terminal.
             All of these changes are aimed at reducing the number of
             places that directly modify gl->term_curpos and
             gl->redisplay.

22/12/2002 Markus Gyger   (logged here by mcs)
           Makefile.in update_html
	     In places where echo and sed were being used to extract
	     the base names of files, Markus substituted the basename
	     command. He also replaced explicit cp and chmod commands
	     with invokations of the install-sh script.
           configure.in
             Use $target_os and $target_cpu, where appropriate,
	     instead of $target.
           configure.in
             The Solaris man function and library man pages should
	     be in sections 3lib and 3tecla respectively, only in
	     Solaris version 2.8 and above.
           configure.in
             Markus provided values for the man page configuration
             variables for HPUX.
           man/*/*.in
             I had missed parameterizing man page section numbers in
	     the man page titles, Markus corrected this.
           man/func/libtecla_version.in
             Fixed incorrect section number in the link to the
             libtecla man page.
           homedir.c
             When compiled to be reentrant, although one can't use the
	     non-reentrant getpwent() function to scan the password
	     file for username completions, one can at least see if
	     the prefix being completed is a valid username, and if
	     the username of the current user minimally matches the
	     prefix, and if so list them. I simplified Markus'
	     modification by adding a prefix argument to the
             _hd_scan_user_home_dirs() function, and redefining the
	     function description accordingly, such that now it
	     reports only those password file entries who's usernames
	     minimally match the specified prefix. Without this, it
	     would have been necessary to peak inside the private data
	     argument passed in by cf_complete_username().
             Markus also provided code which under Solaris uses the
             non-reentrant interfaces if the reentrant version of the
             library isn't linked with the threads library.

19/12/2002 mcs@astro.caltech.edu
           Makefile.in
             Markus pointed out that LDFLAGS was being picked up by
             the configure script, but not then being interpolated
             into te Makefile. I have thus added the necessary
             assignment to Makefile.in and arranged for the value of
             LDFLAGS to be passed on to recursive make's. I also did
             the same for CPPFLAGS, which had also been omitted.

18/12/2002 mcs@astro.caltech.edu
           man/* man/*/* configure.in configure Makefile.in
           update_html
             It turns out that the assignment of man page sections to
             topics differs somewhat from system to system, so this is
             another thing that needs to be configured by the main
             configuration script, rather than being hardwired. All
             man pages have now been moved into suitably named
             topic-specific sub-directories of the top-level man
             directory, and instead of having a numeric suffix, now
             have the .in suffix, since they are now preprocessed by
             the configure script, in the same fashion as Makefile.in.
             Whithin these *.in versions of the man pages, and within
             Makefile.in, the installation subdirectory (eg. man1) and
             the file-name suffix (eg. 1), are written using
             configuration macros, so that they get expanded to the
             appropriate tokens when the configure script is run. In
             principle, the man pages could also take advantage of
             other configuration macros, such as the one which expands
             to the library installation directory, to include full
             path names to installed files in the documentation, so in
             the future this feature could have more uses than just
             that of parameterizing man page sections.

18/12/2002 mcs@astro.caltech.edu
           man3 man3/* Makefile.in html/index.html update_html
             Markus suggested splitting the gl_get_line(3) man page
             into user and developer sections, and also pointed out
             that the enhance man page should be in section 1, not
             section 3. I have thus created a top-level man
             directory in which to place the various sections, and
             moved the man3 directory into it. The enhance.3 man page
             is now in man/man1/enhance.1. I have extracted all
             user-oriented sections from the gl_get_line(3) man page
             and placed them in a new man7/tecla.7 man page.

18/12/2002 mcs@astro.caltech.edu
           getline.c
             Terminal resizing was broken in normal mode, due to
             me forcing the terminal cursor position to zero in the
             wrong place in gl_check_caught_signal().

14/12/2002 Markus Gyger  (logged here by mcs)
           configure.in configure
             Under Solaris, recent versions of gcc search
             /usr/local/include for header files before the system
             directories. This caused a problem if ncurses was
             installed under Solaris, since the termcap.h include file
             in /usr/local/include ended up being used at compile
             time, whereas the system default version of the curses
             library was used at link time. Since the two libraries
             declare tputs() differently, this evoked a complaint from
             gcc. Markus came up with a way to force Gnu cpp to move
             /usr/local/include to the end of the system-include-file
             search path, where it belongs.

13/12/2002 mcs@astro.caltech.edu
           man3/gl_io_mode.3
             I rewrote the man page which documents the new non-blocking
             server I/O mode.

12/12/2002 mcs@astro.caltech.edu
           demo3.c
             I wrote a new version of demo3.c, using signal handlers
             that call gl_handle_signal() and gl_abandon_line(), where
             previously in this demo, these functions were called from
             the application code.

05/12/2002 mcs@astro.caltech.edu
           getline.c
             gl_normal_io(), gl_raw_io() and gl_handle_signal() and
             gl_abandon_line() are now signal safe, provided that
             signal handlers that call them are installed with sa_mask's
             that block all other signals who's handlers call them.
             This is the case if gl_tty_signals() is used to install
             signal handlers that call any of these functions.

             A major stumbling block that had to be overcome was that
             gl_displayed_char_width() calls isprint(), which can't
             safely be called from a signal handler (eg. under linux,
             the is*() functions all use thread-specific data
             facilities to support per-thread locales, and the
             thread-specific data facilities aren't signal safe). To
             work around this, all functions that modify the
             input-line buffer, now do so via accessor functions which
             also maintain a parallel array of character widths, for
             use by gl_buff_curpos_to_term_curpos() in place of
             gl_displayed_char_width(). Other minor problems were the
             need to avoid tputs(), who's signal safety isn't defined.

05/12/2002 Eric Norum        (logged here by mcs@astro.caltech.edu)
           configure.in
             Eric provided the configuration information needed
             to build shared libraries under Darwin (Max OS X).

05/12/2002 Richard Mlynarik  (logged here by mcs@astro.caltech.edu)
           configure.in
             AC_PROG_RANLIB gets the wrong version of ranlib when
             cross compiling, so has now been replaced by an
             invokation of AC_CHECK_TOOL. In addition, AC_CHECK_TOOL
             is also now used to find an appropriate version of LD.

05/12/2002 mcs@astro.caltech.edu (based on patch by Pankaj Rathore)
           getline.c libtecla.h libtecla.map man3/gl_get_line.3
             The new gl_set_term_size() function provides a way
             to tell gl_get_line() about changes in the size of
             the terminal in cases where the values returned by
             ioctl(TIOCGWINSZ) isn't correct.

05/12/2002 mcs@astro.caltech.edu
           getline.c
             Rather than calling sprintf() to see how much space would
             be needed to print a given number in octal, I wrote a
             gl_octal_width() function, for use by
             gl_displayed_char_width().  This makes the latter
             function async signal safe.

05/12/2002 mcs@astro.caltech.edu
           chrqueue.c
             Whenever the buffer is exhausted, and getting a new
             buffer node would require a call to malloc(), attempt
             to flush the buffer to the terminal. In blocking I/O
             mode this means that the buffer never grows. In
             non-blocking I/O mode, it just helps keep the buffer
             size down.

05/12/2002 mcs@astro.caltech.edu
           freelist.h freelist.c
             The new _idle_FreeListNodes() function queries the
             number of nodes in the freelist which aren't currently
             in use.

05/12/2002 mcs@astro.caltech.edu
           Makefile.stub
             This now accepts all of the targets that the configured
             makefile does, and after configuring the latter makefile,
             it invokes it with the same options.

03/12/2002 mcs@astro.caltech.edu
           mans3/gl_io_mode.3
             I completed the man page for all of the new functions
             related to non-blocking I/O.

01/12/2002 mcs@astro.caltech.edu
           man3/gl_get_line.3
             I wrote a long section on reliable signal handling,
             explaining how gl_get_line() does this, how to make
             use of this in a program, and how to handle signals
             reliably when faced with other blocking functions.
             This basically documents what I have learnt about
             signal handling while working on this library.

01/12/2002 mcs@astro.caltech.edu
           getline.c man3/gl_get_line.3
             In non-blocking server mode, the gl_replace_prompt()
             function can now be used between calls to gl_get_line()
             if the application wants to change the prompt of the
             line that is being edited.

01/12/2002 mcs@astro.caltech.edu
           man3/gl_get_line.3
             I documented the new gl_return_status() and
             gl_error_message() functions.

01/12/2002 mcs@astro.caltech.edu
           getline.c man3/gl_get_line.3
             Added SIGPOLL and SIGXFSZ to the list of signals that
             are trapped by default. These are process termination
             signals, so the terminal needs to be restored to a
             usable state before they terminate the process.

27/11/2002 mcs@astro.caltech.edu
           getline.c libtecla.h
             Completed the essential changes needed to support
             non-blocking server-I/O mode.

             The new gl_io_mode() function allows one to switch to
             and from non-blocking server-I/O mode.

             The new gl_raw_io() function is used in non-blocking
             server-I/O mode to switch the terminal into non-blocking
             raw I/O mode.

             The new gl_normal_io() function is used in non-blocking
             server-I/O mode to switch the restore the terminal to
             a normal, blocking state. This is used to suspend line
             input before suspending the process or writing messages
             to the terminal.

             The new gl_tty_signals() function installs specified
             signals handlers for all signals that suspend, terminate
             or resume processes, and also for signals that indicate
             that the terminal has been resized. This not only saves
             the application from having to keep its own ifdef'd list
             of such signals, of which there are many, but it also
             makes sure that these signal handlers are registered
             correctly. This includes using the sa_mask member of each
             sigaction structure to ensure that only one of these
             handlers runs at a time. This is essential to avoid the
             signal handlers all trying to simultaneously modify
             shared global data.

             The new gl_handle_signal() function is provided for
             responding (from application level) to signals caught by
             the application. It handles process suspension, process
             termination and terminal resize signals.

             The new gl_pending_io() function tells the application
             what direction of I/O gl_get_line() is currently waiting
             for.

             In non-blocking server I/O mode, the new
             gl_abandon_line() function can be called between calls to
             gl_get_line() to discard an input line and force the next
             call to gl_get_line() to start the input of a new line.

             Also, in non-blocking server-I/O gl_get_line() doesn't
             attempt to do anything but return when one of the signals
             that it is configured to catch is caught. This is
             necessary because when in this mode, the application is
             required to handle these signals when gl_get_line() is
             running, and the default configuration of most of these
             signals in gl_get_line() is to restore the terminal then
             call the application signal handlers. This would be a
             case of too many cooks spoiling the broth, so in this
             mode, gl_get_line() always defers to the application's
             signal handlers.
             
26/11/2002 mcs@astro.caltech.edu
           getline.c libtecla.h
             I implemented a couple of new functions to support
             reliable signal handling, as now documented
             (see above) in the gl_get_line(3) man page.

             The new gl_catch_blocked() function tells gl_get_line()
             to unblock all configured signals around calls to
             long-running functions, not only those that aren't
             blocked when gl_get_line() is called. This allows
             the caller to implement reliable signal handling,
             since the unblocking is only done from within code
             protected by sigsetjmp(), which avoids race conditions.

             The new gl_list_signals() function fills a provided
             sigset_t with the set of signals that gl_get_line() is
             currently configured to catch. This allows callers to
             block said signals, such that they are only unblocked by
             gl_get_line() when it is waiting for I/O. When used in
             conjunction with the gl_catch_blocked() function, this
             removes the potential for race conditions.

             Also, when gl_get_line() installs its signal handler,
             it uses the sa_mask member of the sigaction structure
             to ensure that only one instance of this signal handler
             will ever be executing at a time.

25/11/2002 mcs@astro.caltech.edu (bug reported by Pankaj Rathore)
           getline.c
             When any history recall action was invoked when the
             input line buffer was full, an error message would be
             displayed complaining about the length of the string
             in the line input buffer being inconsistent with the
             specified allocated size. This was because instead of
             sending the allocated size of the input line, I was
             sending the length excluding the element that is
             reserved for the '\0' terminator. Sending it the
             correct size corrected the problem.

24/11/2002 mcs@astro.caltech.edu
           getline.c
             All public functions which take GetLine objects as
             arguments now block signals on entry and restore the
             signal mask on return. This was an attempt to make it
             safe to call getline functions from signal handlers, but
             the fact is that the functions that I really wanted this
             to apply to, potentially call malloc(), so this currently
             isn't the case.

23/11/2002 mcs@astro.caltech.edu
           getline.c libtecla.h
             The new gl_return_status() function returns an enumerated
             return status which can be used to query what caused
             gl_get_line() to return.

22/11/2002 mcs@astro.caltech.edu
           Most existing .c and .h files, plus errmsg.c errmsg.h
           Makefile.rules
             Until now, many library functions would report error
             messages to stderr. This isn't appropriate for library
             functions, so in place of this behavior, error messages
             are now recorded in internal ErrMsg objects, and passed
             between modules via new module-specific error querying
             functions. In addition, errno is now set appropriately.
             Thus when gl_get_line() and related functions return an
             error, strerror() can be used to look up system errors,
             and gl_error_message() can be used to recover a higher level
             error message. Note that error messages that are
             responses to user actions continue to be reported to the
             terminal, as before.

21/11/2002 mcs@astro.caltech.edu
           getline.c keytab.h keytab.c Makefile.rules
             I wrote a new version of _kt_lookup_binding() that didn't
             require the caller to have access to the innards of a
             KeyTab object. This then enabled me to move the definition
             of KeyTab objects into keytab.c and make the typedef in
             keytab.h opaque. Many nested includes were also moved from
             keytab.h into keytab.c.

05/11/2002 mcs@astro.caltech.edu
           getline.c libtecla.map libtecla.h demo3.c
             I split the old gl_resize_terminal() function into
             two parts, gl_query_size() and gl_update_size(), with
             the latter calling the former to get the new terminal
             size.

05/11/2002 mcs@astro.caltech.edu
           getline.c
             I fixed a long time bug in the terminal resizing code.
             When the cursor wasn't on the last terminal line of the
             input line, the resizing code would redisplay the
             the line one or more lines above where it should be
             restored. This was due to an error in the calculation of
             the number of lines above the cursor position.

04/11/2002 mcs@astro.caltech.edu
           demo.c demo2.c demo3.c
             I used the new gl_display_text() function to display
             introductory text at the startup of each of the demo
             programs. The text is enclosed within a box of asterixes,
             drawn dynamically to fit within the confines of the
             available terminal width.

04/11/2002 mcs@astro.caltech.edu
           libtecla.h getline.c ioutil.c ioutil.h Makefile.rules
           libtecla.map man3/gl_get_line.3 man3/gl_display_text.3
             Needing a way to display introductory text intelligently
             in the demo programs, I wrote and documented the
             gl_display_text() function. This justifies arbitrary
             length text within the bounds of the terminal width,
             with or without optional indentation, prefixes and
             suffixes.

03/11/2002 mcs@astro.caltech.edu
           demo3.c Makefile.rules
             I wrote a new demonstration program. This program acts
             exactly like the main demonstration program, except that
             it uses an external event loop instead of using the
             gl_get_line() internal event loop. This is thus an example
             of the new non-blocking server I/O facility.

02/11/2002 mcs@astro.caltech.edu
           getline.c keytab.c keytab.h libtecla.h man3/gl_get_line.3
           man3/gl_completion_action.3
             I added the ability to register additional word
             completion actions via the new function
             gl_completion_action().  All action functions now take a
             new (void *data) argument, which is stored with the
             function in the symbol table of actions. The new
             gl_completion_action() function uses this feature to
             record dynamically allocated objects containing the
             specified completion function and callback data along
             with either the gl_complete_word() action function, or
             the gl_list_completions() action function.  These two
             actions continue to use the builtin completion functions
             when their data pointer is NULL.

20/10/2002 mcs@astro.caltech.edu
           The following are changes merged from the non-blocking
           gl_get_line() development branch.

           getline.c
             I wrote a gl_start_newline() function, to replace all of
             the explicit calls to output \r\n to stdout.

             Informational messages are now written to the terminal
             using a new variadic function called gl_print_info().
             This starts a newline, writes string arguments until a
             special argument, GL_END_INFO, is seen, then starts
             another newline.

             Changed _output_ to _print_ in the following function
             names gl_output_control_sequence(), gl_output_char(),
             gl_output_string() and gl_output_raw_string().

             gl_print_raw_string() now has a length argument, so that
             strings that aren't terminated with '\0' can be printed.

             The display of the initial contents of a new line to be
             edited has been moved into a new function called
             gl_present_line().

             The gl_get_input_line() function now takes the prompt
             string as an argument so that gl_replace_prompt() can be
             called from within this function instead of from
             gl_get_line().

             Keyboard input is now buffered in a persistent buffer in
             the parent GetLine object. gl_read_character() checks
             this for unprocessed characters in preference to calling
             gl_read_terminal() to append characters to it.  A new
             function, gl_discard_chars(), removes processed
             characters from this buffer. This change is in
             preparation for a non-blocking version of gl_get_line(),
             where partially input key-sequences must be stored
             between calls to gl_get_line().

           getline.c getline.h history.c history.h cplmatch.c \
           cplmatch.h expand.c expand.h
             All terminal output from gl_get_line() is now routed
             through a GL_WRITE_FN() callback function called
             gl_write_fn. Internal functions in cplmatch.c,
             expand.c and history.c have been created which take
             such callbacks to write output. These are used both
             by functions in getline.c, to display file completions,
             expansions, history etc, and as the internals of existing
             public functions in these files that print to stdio
             streams. In the latter case an internal stdio
             GL_WRITE_FN() callback is substituted, so that the
             functions behave as before.

           getline.c chrqueue.c chrqueue.h
             The gl_write_fn() callback used by gl_get_line() now
             writes to a queue, implemented in chrqueue.c. This queue
             is implemented as a list of blocks of buffer segments,
             the number of which shrink and grow as
             needed. The contents of the queue are flushed to the
             terminal via another GL_WRITE_FN() callback passed to the
             queue object. Currently gl_get_line() passes an internal
             function assigned to gl->flush_fn, called
             gl_flush_terminal(), which writes the contents of the
             queue to the terminal, and knows how to handle both
             blocking and non-blocking I/O. The output queue is
             designed to be flushed to the terminal incrementally, and
             thereby also facilitates non-blocking I/O.

           getline.c getline.h
             gl_get_line() now reads all input via the GL_READ_FN()
             callback, assigned to gl->read_fn. Currently this is
             set to an internal function called gl_read_terminal(),
             which knows how to handle both blocking and
             non-blocking I/O.

           getline.c libtecla.h
             The new gl_set_nonblocking() function can be used to
             enable or disable non-blocking I/O. The default is still
             blocking I/O. In non-blocking mode, the terminal is told
             not to wait when either reading or writing would block.
             gl_get_line() then returns, with a return value of NULL,
             but with the terminal left in raw mode, so that the
             caller's event loop can detect key presses. The caller
             should call gl_return_status() to check whether the NULL
             return value was due to an error, lack of input, or
             inability to write to the terminal without waiting. If
             either reading or writing was said to have blocked, the
             user then should check for I/O readiness in the specified
             direction before calling gl_get_line() again to
             incrementally build up the input line.

05/08/2002 mcs@astro.caltech.edu
           man3/gl_get_line.3 man3/gl_inactivity_timeout.3
             I documented the new gl_inactivity_timeout() function.

08/07/2002 mcs@astro.caltech.edu
           libtecla.h getline.c libtecla.map
             I added a new gl_inactivity_timeout() function. On
             systems that have the select system call, this provides
             the option of registering a function that is then called
             whenever no I/O activity has been seen for more than a
             specified period of time. Like the gl_watch_fd()
             facility, timeout callbacks return a code which tells
             gl_get_line() how to proceed after the timeout has been
             handled.
             
04/07/2002 mcs@astro.caltech.edu  (based on a bug report from Michael MacFaden)
           getline.c
             The internal event handler wasn't responding to write
             events on client file descriptors, due to a typo which
             resulted in read events being checked for twice, and
             writes not checked for at all.
           pathutil.c
             The amount of space to allocate for pathnames is supposed
             to come from PATH_MAX in limits.h, but I had neglected to
             include limits.h. This went unnoticed because on most
             systems the equivalent number is deduced by calling
             pathconf(). Apparently under NetBSD this function doesn't
             work correctly over NFS mounts.

30/05/2002 Version 1.4.1 released.

25/05/2002 mcs@astro.caltech.edu  (based on suggestions by Paul Smith)
           pathutil.c
             Apparently, under QNX pathconf("/",_PC_PATH_MAX) returns
             EINVAL. At Paul's suggestion I have modified the code to
             silently substitute the existing MAX_PATHLEN_FALLBACK
             value if pathconf() returns an error of any kind.
           homedir.c
             Under QNX, sysconf(_SC_GETPW_R_SIZE_MAX) also apparently
             returns EINVAL, so as with pathconf() I modified the code
             to substitute a fallback default, rather than
             complaining and failing.
           enhance.c
             Paul told me that the inclusion of sys/termios.h was
             causing compilation of enhance.c to fail under QNX. This
             line is a bug.  The correct thing to do is include
             termios.h without a sub-directory prefix, as I was
             already doing futher up in the file, so I have just
             removed the errant include line.

07/05/2002 mcs@astro.caltech.edu  (async development branch only)
           getline.c
             gl_read_character() now caches and reads unprocessed
             characters from a key-press lookahead buffer. Whenever
             gl_intepret_char() receives a new character which makes
             an initially promising key-sequence no longer match the
             prefix of any binding, it now simply discards the first
             character from the key-press buffer and resets the buffer
             pointer so that the next call to gl_read_character()
             returns the character that followed it, from the buffer.
           getline.c
             The part of gl_get_input_line() which preloads, displays
             and prepares to edit a new input line, has now been moved
             into a function called gl_present_line().

12/02/2002 mcs@astro.caltech.edu
           getline.c configure.in configure
             Mac OS X doesn't have a term.h or termcap.h, but it does
             define prototypes for tputs() and setupterm(), so the
             default prototypes that I was including if no headers
             where available, upset it. I've removed these prototypes.
             I also now conditionally include whichever is found of
             curses.h and ncurses/curses.h for both termcap and
             terminfo (before I wasn't including curses.h when
             termcap was selected).

12/02/2002 mcs@astro.caltech.edu
           Updated version number to 1.4.1, ready for a micro
           release.

12/02/2002 mcs@astro.caltech.edu
           html/index.html
             Added Mac OS X and Cygwin to the list of systems that
             can compile libtecla.

12/02/2002 mcs@astro.caltech.edu
           getline.c
             Under Mac OS X, the tputs() callback function returns
             void, instead of the int return value used by other
             systems. This declaration is now used if both __MACH__
             and __APPLE__ are defined. Hopefully these are the
             correct system macros to check. Thanks for Stephan
             Fiedler for providing information on Mac OS X.

11/02/2002 mcs@astro.caltech.edu
           configure.in configure getline.c
             Some systems don't have term.h, and others have it hidden
             in an ncurses sub-directory of the standard system include
             directory. If term.h can't be found, simply don't include
             it. If it is in an ncurses sub-directory, include
             ncurses/term.h instead of term.h.

04/02/2002 mcs@astro.caltech.edu
           configure.in configure Makefile.in Makefile.rules
             Use ranlib on systems that need it (Mac OS X).  Also,
             make all components of the installation directories where
             needed, instead of assuming that they exist.

04/02/2002 mcs@astro.caltech.edu
           getline.c
             When the tab completion binding was unbound from the tab
             key, hitting the tab key caused gl_get_line() to ring the
             bell instead of inserting a tab character. This is
             problematic when using the 'enhance' program with
             Jython, since tabs are important in Python. I have
             corrected this.

10/12/2001 Version 1.4.0 released.

10/12/2001 mcs@astro.caltech.edu
           getline.c
             If the TIOCGWINSZ ioctl doesn't work, as is the case when
             running in an emacs shell, leave the size unchanged, rather
             than returning a fatal error.

07/12/2001 mcs@astro.caltech.edu
           configure.in configure
             Now that the configure version of CFLAGS is included in
             the makefile, I noticed that the optimization flags -g
             and -O2 had been added. It turns out that if CFLAGS isn't
             already set, the autoconf AC_PROG_CC macro initializes it
             with these two optimization flags. Since this would break
             backwards compatibility in embedded distributions that
             already use the OPT= makefile argument, and because
             turning debugging on needlessly bloats the library, I now
             make sure that CFLAGS is set before calling this macro.

07/12/2001 mcs@astro.caltech.edu
           enhance.c
             Use argv[0] in error reports instead of using a
             hardcoded macro.

07/12/2001 mcs@astro.caltech.edu
           getline.c
             The cut buffer wasn't being cleared after being
             used as a work buffer by gl_load_history().

06/12/2001 mcs@astro.caltech.edu
           configure.in configure
             I removed my now redundant definition of SUN_TPUTS from
             CFLAGS. I also added "-I/usr/include" to CFLAGS under
             Solaris to prevent gcc from seeing conflicting versions
             of system header files in /usr/local/include.

06/12/2001 Markus Gyger (logged here by mcs)
           Lots of files.
             Lots of corrections to misspellings and typos in the
             comments.
           getline.c
             Markus reverted a supposed fix that I added a day or two
             ago. I had incorrectly thought that in Solaris 8, Sun had
             finally brought their declaration of the callback
             function of tputs() into line with other systems, but it
             turned out that gcc was pulling in a GNU version of
             term.h from /usr/local/include, and this was what
             confused me.

05/12/2001 mcs@astro.caltech.edu
           Makefile.in
             I added @CFLAGS@ to the CFLAGS assignment, so that
             if CFLAGS is set as an environment variable when
             configure is run, the corresponding make variable
             includes its values in the output makefile.

05/12/2001 mcs@astro.caltech.edu
           getline.c libtecla.h libtecla.map man3/gl_get_line.3
           man3/gl_last_signal.3
             I added a function that programs can use to find out
             which signal caused gl_get_line() to return EINTR.

05/12/2001 mcs@astro.caltech.edu
           getline.c
             When the newline action was triggered by a printable
             character, it failed to display that character. It now
             does. Also, extra control codes that I had added, to
             clear to the end of the display after the carriage return,
             but before displaying the prompt, were confusing expect
             scripts, so I have removed them. This step is now done
             instead in gl_redisplay() after displaying the full input
             line.

05/12/2001 mcs@astro.caltech.edu
           getline.c man3/gl_get_line.3
             A user convinced me that continuing to invoke meta
             keybindings for meta characters that are printable is a
             bad idea, as is allowing users to ask to have setlocale()
             called behind the application's back. I have thus changed
             this. The setlocale configuration option has gone, and
             gl_get_line() is now completely 8-bit clean, by default.
             This means that if a meta character is printable, it is
             treated as a literal character, rather than a potential
             M-c binding.  Meta bindings can still be invoked via
             their Esc-c equivalents, and indeed most terminal
             emulators either output such escape pairs by default when
             the meta character is pressed, or can be configured to do
             so. I have documented how to configure xterm to do this,
             in the man page.

03/12/2001 mcs@astro.caltech.edu
           getline.c man3/gl_get_line.3
             gl_get_line() by default now prints any 8-bit printable
             characters that don't match keybindings. Previously
             characters > 127 were only printed if preceded by the
             literal-next action.  Alternatively, by placing the
             command literal_if_printable in the tecla configuration
             file, all printable characters are treated as literal
             characters, even if they are bound to action functions.

             For international users of programs written by
             programmers that weren't aware of the need to call
             setlocale() to support alternate character sets, the
             configuration file can now also contain the single-word
             command "setlocale", which tells gl_get_line() to remedy
             this.

27/11/2001 mcs@astro.caltech.edu
           demo.c demo2.c enhance man3/gl_get_line.3
             All demos and programs now call setlocale(LC_CTYPE,"").
             This makes them support character sets of different
             locales, where specified with the LC_CTYPE, LC_ALL, or
             LANG environment variables. I also added this to the demo
             in the man page, and documented its effect.

27/11/2001 mcs@astro.caltech.edu
           getline.c
             When displaying unsigned characters with values over
             127 literally, previously it was assumed that they would
             all be displayable. Now isprint() is consulted, and if it
             says that a character isn't printable, the character code
             is displayed in octal like \307. In non-C locales, some
             characters with values > 127 are displayable, and
             isprint() tells gl_get_line() which are and which aren't.

27/11/2001 mcs@astro.caltech.edu
           getline.c pathutil.c history.c enhance.c demo2.c
             All arguments of the ctype.h character class functions
             are now cast to (int)(unsigned char). Previously they
             were cast to (int), which doesn't correctly conform to
             the requirements of the C standard, and could cause
             problems for characters with values > 127 on systems
             with signed char's.

26/11/2001 mcs@astro.caltech.edu
           man3/enhance.3 man3/libtecla.3
             I started writing a man page for the enhance program.

26/11/2001 mcs@astro.caltech.edu
           Makefile.in Makefile.rules INSTALL
             It is now possible to specify whether the demos and other
             programs are to be built, by overriding the default
             values of the DEMOS, PROGRAMS and PROGRAMS_R variables.
             I have also documented the BINDIR variable and the
             install_bin makefile target.

22/11/2001 mcs@astro.caltech.edu
           getline.c libtecla.h libtecla.map man3/gl_get_line.3
           man3/gl_ignore_signal.3 man3/gl_trap_signal.3
             Signal handling has now been modified to be customizable.
             Signals that are trapped by default can be removed from
             the list of trapped signals, and signals that aren't
             currently trapped, can be added to the list. Applications
             can also specify the signal and terminal environments in
             which an application's signal handler is invoked, and
             what gl_get_line() does after the signal handler returns.

13/11/2001 mcs@astro.caltech.edu
           getline.c man3/gl_get_line.3
             Added half-bright, reverse-video and blinking text to the
             available prompt formatting options.
           getline.c
             Removed ^O from the default VT100 sgr0 capability
             string.  Apparently it can cause problems with some
             terminal emulators, and we don't need it, since it turns
             off the alternative character set mode, which we don't
             use.
           getline.c
             gl_tigetstr() and gl_tgetstr() didn't guard against the
             error returns of tigetstr() and tgetstr() respectively.
             They now do.

11/11/2001 mcs@astro.caltech.edu
           getline.c libtecla.h libtecla.map man3/gl_get_line.3
           man3/gl_prompt_style.3
             Although the default remains to display the prompt string
             literally, the new gl_prompt_style() function can be used
             to enable text attribute formatting directives in prompt
             strings, such as underlining, bold font, and highlighting
             directives.

09/11/2001 mcs@astro.caltech.edu
           enhance.c Makefile.rules configure.in configure
             I added a new program to the distribution that allows one
             to run most third party programs with the tecla library
             providing command-line editing.

08/11/2001 mcs@astro.caltech.edu
           libtecla.h getline.c man3/gl_get_line.3 history.c history.h
             I added a max_lines argument to gl_show_history() and
             _glh_show_history(). This can optionally be used to
             set a limit on the number of history lines displayed.
           libtecla.h getline.c man3/gl_get_line.3
             I added a new function called gl_replace_prompt(). This
             can be used by gl_get_line() callback functions to
             request that a new prompt be use when they return.

06/11/2001 mcs@astro.caltech.edu
           getline.c man3/gl_get_line.3
             I implemented, bound and documented the list-history
             action, used for listing historical lines of the current
             history group.
           getline.c man3/gl_get_line.3 man3/gl_echo_mode.3
             I wrote functions to specify and query whether subsequent
             lines will be visible as they are being typed.

28/10/2001 mcs@astro.caltech.edu
           getline.c man3/gl_get_line.3
             For those cases where a terminal provides its own
             high-level terminal editing facilities, you can now
             specify an edit-mode argument of 'none'. This disables
             all tecla key bindings, and by using canonical terminal
             input mode instead of raw input mode, editing is left up
             to the terminal driver.

21/10/2001 mcs@astro.caltech.edu
           libtecla.h getline.c history.c history.h
           man3/gl_get_line.3 man3/gl_history_info.3
             I added the new gl_state_of_history(),
             gl_range_of_history() and gl_size_of_history()
             functions for querying information about the
             history list.
           history.c
             While testing the new gl_size_of_history()
             function, I noticed that when the history buffer
             wrapped, any location nodes of old lines between
             the most recent line and the end of the buffer
             weren't being removed. This could result in bogus
             entries appearing at the start of the history list.
             Now fixed.

20/10/2001 mcs@astro.caltech.edu

           libtecla.h getline.c history.c history.h
           man3/gl_get_line.3 man3/gl_lookup_history.3
             I added a function called gl_lookup_history(), that
             the application can use to lookup lines in the history
             list.
           libtecla.h getline.c history.c history.h man3/gl_get_line.3
             gl_show_history() now takes a format string argument
             to control how the line is displayed, and with what
             information. It also now provides the option of either
             displaying all history lines or just those of the
             current history group.
           getline.c man3/gl_get_line.3
             gl_get_line() only archives lines in the history buffer
             if the newline action was invoked by a newline or
             carriage return character.

16/10/2001 mcs@astro.caltech.edu

           history.c history.h getline.c libtecla.h libtecla.map
           man3/gl_get_line.3 man3/gl_resize_history.3
           man3/gl_limit_history.3 man3/gl_clear_history.3
           man3/gl_toggle_history.3
	     I added a number of miscellaneous history configuration
	     functions. You can now resize or delete the history
	     buffer, limit the number of lines that are allowed in the
	     buffer, clear either all history or just the history of
	     the current history group, and temporarily enable and
	     disable the history mechanism.

13/10/2001 mcs@astro.caltech.edu

           getline.c
             tputs_fp is now only declared if using termcap or
             terminfo.
           getline.c libtecla.map man3/gl_get_line.3
           man3/gl_terminal_size.3
             I added a public gl_terminal_size() function for
             updating and querying the current size of the terminal.
           update_version configure.in libtecla.h
             A user noted that on systems where the configure script
             couldn't be used, it was inconvenient to have the version
             number macros set by the configure script, so they are
             now specified in libtecla.h. To reduce the likelihood
             that the various files where the version number now
             appears might get out of sync, I have written the
             update_version script, which changes the version number
             in all of these files to a given value.

01/10/2001 mcs@astro.caltech.edu

           getline.c history.c history.h man3/gl_get_line.3
             I added a max_lines argument to gl_save_history(), to
             allow people to optionally place a ceiling on the number
             of history lines saved. Specifying this as -1 sets the
             ceiling to infinity.

01/10/2001 mcs@astro.caltech.edu

           configure.in configure
             Under digital unix, getline wouldn't compile with
             _POSIX_C_SOURCE set, due to type definitions needed by
             select being excluded by this flag. Defining the
             _OSF_SOURCE macro as well on this system, resolved this.

30/09/2001 mcs@astro.caltech.edu

           getline.c libtecla.h history.c history.h man3/gl_get_line.3
           man3/gl_group_history.3
             I implemented history streams. History streams
             effectively allow multiple history lists to be stored in
             a single history buffer. Lines in the buffer are tagged
             with the current stream identification number, and
             lookups only consider lines that are marked with the
             current stream identifier.
           getline.c libtecla.h history.c history.h man3/gl_get_line.3
           man3/gl_show_history.3
             The new gl_show_history function displays the current
             history to a given stdio output stream.

29/09/2001 mcs@astro.caltech.edu

           getline.c
             Previously new_GetLine() installed a persistent signal
             handler to be sure to catch the SIGWINCH (terminal size
             change) signal between calls to gl_get_line(). This had
             the drawback that if multiple GetLine objects were
             created, only the first GetLine object used after the
             signal was received, would see the signal and adapt to
             the new terminal size. Instead of this, a signal handler
             for sigwinch is only installed while gl_get_line() is
             running, and just after installing this handler,
             gl_get_line() checks for terminal size changes that
             might have occurred while the signal handler wasn't
             installed.
           getline.c
             Dynamically allocated copies of capability strings looked
             up in the terminfo or termcap databases are now made, so
             that calls to setupterm() etc for one GetLine object
             don't get trashed when another GetLine object calls
             setupterm() etc. It is now safe to allocate and use
             multiple GetLine objects, albeit only within a single
             thread.
           
28/09/2001 mcs@astro.caltech.edu

           version.c Makefile.rules
             I added a function for querying the version number of
             the library.

26/09/2001 mcs@astro.caltech.edu

           getline.c man3/gl_get_line.3
             I added the new gl_watch_fd() function, which allows
             applications to register callback functions to be invoked
             when activity is seen on arbitrary file descriptors while
             gl_get_line() is awaiting keyboard input from the user.

           keytab.c
             If a request is received to delete a non-existent
             binding, which happens to be an ambiguous prefix of other
             bindings no complaint is now generated about it being
             ambiguous.

23/09/2001 mcs@astro.caltech.edu

           getline.c history.c history.h man3/gl_get_line.3
           libtecla.map demo.c
             I added new public functions for saving and restoring the
             contents of the history list. The demo program now uses
             these functions to load and save history in ~/.demo_history.

23/09/2001 mcs@astro.caltech.edu

           getline.c
             On trying the demo for the first time on a KDE konsole
             terminal, I discovered that the default M-O binding
             to repeat history was hiding the arrow keys, which are
             M-OA etc. I have removed this binding. The M-o (ie the
             lower case version of this), is still bound.

18/09/2001 mcs@astro.caltech.edu

           getline.c man3/gl_get_line.3 libtecla.map
             Automatic reading of ~/.teclarc is now postponed until
             the first call to gl_get_line(), to give the application
             the chance to specify alternative configuration sources
             with the new function gl_configure_getline(). The latter
             function allows configuration to be done with a string, a
             specified application-specific file, and/or a specified
             user-specific file. I also added a read-init-files action
             function, for re-reading the configuration files, if any.
             This is by default bound to ^X^R. This is all documented
             in gl_get_line.3.

08/09/2001 mcs@astro.caltech.edu

           getline.c man3/gl_get_line.3
             It is now possible to bind actions to key-sequences
             that start with printable characters. Previously
             keysequences were required to start with meta or control
             characters. This is documented in gl_get_line.3.

           getline.c man3/gl_get_line.3
             A customized completion function can now arrange for
             gl_get_line() to return the current input line whenever a
             successful completion has been made. This is signalled by
             setting the last character of the optional continuation
             suffix to a newline character. This is documented in
             gl_get_line.3.

05/07/2001 Bug reported by Mike MacFaden, fixed by mcs

           configure.in
             There was a bug in the configure script that only
             revealed itself on systems without termcap but not
             terminfo (eg. NetBSD). I traced the bug back to a lack of
             sufficient quoting of multi-line m4 macro arguments in
             configure.in, and have now fixed this and recreated the
             configure script.

05/07/2001 Bug reported and patched by Mike MacFaden (patch modified
           by mcs to match original intentions).

           getline.c
             getline.c wouldn't compile when termcap was selected as
             the terminal information database. setupterm() was being
             passed a non-existent variable, in place of the term[]
             argument of gl_control_strings(). Also if
             gl_change_terminal() is called with term==NULL, "ansi"
             is now substituted.

02/07/2001 Version 1.3.3 released.

27/06/2001 mcs@astro.caltech.edu

           getline.c expand.c cplmatch.c
             Added checks to fprintf() statements that write to the
             terminal.
           getline.c
             Move the cursor to the end of the line before suspending,
             so that the cursor doesn't get left in the middle of the
             input line.
           Makefile.in
             On systems that don't support shared libraries, the
             distclean target of make deleted libtecla.h. This has
             now been fixed.
           getline.c
             gl_change_terminal() was being called by gl_change_editor(),
             with the unwanted side effect that raw terminal modes were
             stored as those to be restored later, if called by an
             action function. gl_change_terminal() was being called in
             this case to re-establish terminal-specific key bindings,
             so I have just split this part of the function out into
             a separate function for both gl_change_editor() and
             gl_change_terminal() to call.

12/06/2001 mcs@astro.caltech.edu

           getline.c
             Signal handling has been improved. Many more signals are
             now trapped, and instead of using a simple flag set by a
             signal handler, race conditions are avoided by blocking
             signals during most of the gl_get_line() code, and
             unblocking them via calls to sigsetjmp(), just before
             attempting to read each new character from the user.
             The matching use of siglongjmp() in the signal
             handlers ensures that signals are reblocked correctly
             before they are handled. In most cases, signals cause
             gl_get_line() to restore the terminal modes and signal
             handlers of the calling application, then resend the
             signal to the application. In the case of SIGINT, SIGHUP,
             SIGPIPE, and SIGQUIT, if the process still exists after
             the signals are resent, gl_get_line() immediately returns
             with appropriate values assigned to errno. If SIGTSTP,
             SIGTTIN or SIGTTOU signals are received, the process is
             suspended. If any other signal is received, and the
             process continues to exist after the signal is resent to
             the calling application, line input is resumed after the
             terminal is put back into raw mode, the gl_get_line()
             signal handling is restored, and the input line redrawn.
           man/gl_get_line(3)
             I added a SIGNAL HANDLING section to the gl_get_line()
             man page, describing the new signal handling features.

21/05/2001 Version 1.3.2 released.

21/05/2001 mcs@astro.caltech.edu

           getline.c
             When vi-replace-char was used to replace the character at
             the end of the line, it left the cursor one character to
             its right instead of on top of it. Now rememdied.
           getline.c
             When undoing, to properly emulate vi, the cursor is now
             left at the leftmost of the saved and current cursor
             positions.
           getline.c man3/gl_get_line.3
             Implemented find-parenthesis (%), delete-to-paren (M-d%),
             vi-change-to-paren (M-c%), copy-to-paren (M-y%).
           cplfile.c pcache.c
             In three places I was comparing the last argument of
             strncmp() to zero instead of the return value of
             strncmp().

20/05/2001 mcs@astro.caltech.edu

           getline.c man3/gl_get_line.3
             Implemented and documented the vi-repeat-change action,
             bound to the period key. This repeats the last action
             that modified the input line.

19/05/2001 mcs@astro.caltech.edu

           man3/gl_get_line.3
             I documented the new action functions and bindings
             provided by Tim Eliseo, plus the ring-bell action and
             the new "nobeep" configuration option.
           getline.c
             I modified gl_change_editor() to remove and reinstate the
             terminal settings as well as the default bindings, since
             these have editor-specific differences. I also modified
             it to not abort if a key-sequence can't be bound for some
             reason. This allows the new vi-mode and emacs-mode
             bindings to be used safely.
           getline.c
             When the line was re-displayed on receipt of a SIGWINCH
             signal, the result wasn't visible until the next
             character was typed, since a call to fflush() was needed.
             gl_redisplay_line() now calls gl_flush_output() to remedy
             this.

17/05/2001 mcs@astro.catlech.edu

           getline.c
             Under Linux, calling fflush(gl->output_fd) hangs if
             terminal output has been suspended with ^S. With the
             tecla library taking responsability for reading the stop
             and start characters this was a problem, because once
             hung in fflush(), the keyboard input loop wasn't entered,
             so the user couldn't type the start character to resume
             output.  To remedy this, I now have the terminal process
             these characters, rather than the library.

12/05/2001 mcs@astro.caltech.edu

           getline.c
             The literal-next action is now implemented as a single
             function which reads the next character itself.
             Previously it just set a flag which effected the
             interpretation of the next character read by the input
             loop.
           getline.c
             Added a ring-bell action function. This is currently
             unbound to any key by default, but it is used internally,
             and can be used by users that want to disable any of the
             default key-bindings.

12/05/2001 Tim Eliseo    (logged here by mcs)

           getline.c
             Don't reset gl->number until after calling an action
             function. By looking at whether gl->number is <0 or
             not, action functions can then tell whether the count
             that they were passed was explicitly specified by the
             user, as opposed to being defaulted to 1.
           getline.c
             In vi, the position at which input mode is entered
             acts as a barrier to backward motion for the few
             backward moving actions that are enabled in input mode.
             Tim added this barrier to getline.
           getline.c
             In gl_get_line() after reading an input line, or
             having the read aborted by a signal, the sig_atomic_t
             gl_pending_signal was being compared to zero instead
             of -1 to see if no signals had been received.
             gl_get_line() will thus have been calling raise(-1),
             which luckily didn't seem to do anything. Tim also
             arranged for errno to be set to EINTR when a signal
             aborts gl_get_line().
           getline.c
             The test in gl_add_char_to_line() for detecting
             when overwriting a character with a wider character,
             had a < where it needed a >. Overwriting with a wider
             character thus overwrote trailing characters. Tim also
             removed a redundant copy of the character into the
             line buffer.
           getline.c
             gl_cursor_left() and gl->cursor_right() were executing
             a lot of redundant code, when the existing call to the
             recently added gl_place_cursor() function, does all that
             is necessary.
           getline.c
             Remove redundant code from backward_kill_line() by
             re-implimenting in terms of gl_place_cursor() and
             gl_delete_chars().
           getline.c
             gl_forward_delete_char() now records characters in cut
             buffer when in vi command mode.
           getline.c
             In vi mode gl_backward_delete_char() now only deletes
             up to the point at which input mode was entered. Also
             gl_delete_chars() restores from the undo buffer when
             deleting in vi insert mode.
           getline.c
             Added action functions, vi-delete-goto-column,
             vi-change-to-bol, vi-change-line, emacs-mode, vi-mode,
             vi-forward-change-find, vi-backward-change-find,
             vi-forward-change-to, vi-backward-change-to,
             vi-change-goto-col, forward-delete-find, backward-delete-find,
             forward-delete-to, backward-delete-to,
             delete-refind, delete-invert-refind, forward-copy-find,
             backward-copy-find, forward-copy-to, backward-copy-to
             copy-goto-column, copy-rest-of-line, copy-to-bol, copy-line,
             history-re-search-forward, history-re-search-backward.

06/05/2001 Version 1.3.1 released.

03/05/2001 mcs@astro.caltech.edu

           configure.in
             Old versions of GNU ld don't accept version scripts.
             Under Linux I thus added a test to try out ld with
             the --version-script argument to see if it works.
             If not, version scripts aren't used.
           configure.in
             My test for versions of Solaris earlier than 7
             failed when confronted by a three figure version
             number (2.5.1). Fixed.

30/04/2001 mcs@astro.caltech.edu

           getline.c
             In vi mode, history-search-backward and
             history-search-forward weren't doing anything when
             invoked at the start of an empty line, whereas
             they should have acted like up-history and down-history.
           Makefile.in Makefile.rules
             When shared libraries are being created, the build
             procedure now arranges for any alternate library
             links to be created as well, before linking the
             demos. Without this the demos always linked to the
             static libraries (which was perfectly ok, but wasn't a
             good example).
           Makefile.in Makefile.rules
             On systems on which shared libraries were being created,
             if there were no alternate list of names, make would
             abort due to a Bourne shell 'for' statement that didn't
             have any arguments. Currently there are no systems who's
             shared library configurations would trigger this
             problem.
           Makefile.rules
             The demos now relink to take account of changes to the
             library.
           configure.in configure
             When determining whether the reentrant version of the
             library should be compiled by default, the configure
             script now attempts to compile a dummy program that
             includes all of the appropriate system headers and
             defines _POSIX_C_SOURCE. This should now be a robust test
             on systems which use C macros to alias these function
             names to other internal functions.
           configure.in
             Under Solaris 2.6 and earlier, the curses library is in
             /usr/ccs/lib. Gcc wasn't finding this. In addition to
             remedying this, I had to remove "-z text" from
             LINK_SHARED under Solaris to get it to successfully
             compile the shared library against the static curses
             library.
           configure.in
             Under Linux the -soname directive was being used
             incorrectly, citing the fully qualified name of the
             library instead of its major version alias. This will
             unfortunately mean that binaries linked with the 1.2.3
             and 1.2.4 versions of the shared library won't use
             later versions of the library unless relinked.

30/04/2001 mcs@astro.caltech.edu

           getline.c
             In gl_get_input_line(), don't redundantly copy the
             start_line if start_line == gl->line.

30/04/2001 Version 1.3.0 released.

28/04/2001 mcs@astro.caltech.edu

           configure.in
             I removed the --no-undefined directive from the Linux
             LINK_SHARED command. After recent patches to our RedHat
             7.0 systems ld started reporting some internal symbols of
             libc as being undefined.  Using nm on libc indicated that
             the offending symbols are indeed defined, albeit as
             "common" symbols, so there appears to be a bug in
             RedHat's ld. Removing this flag allows the tecla shared
             library to compile, and programs appear to function fine.
           man3/gl_get_line.3
             The default key-sequence used to invoke the
             read-from-file action was incorrectly cited as ^Xi
             instead of ^X^F.

26/04/2001 mcs@astro.caltech.edu

           getline.c man3/gl_get_line.3
             A new vi-style editing mode was added. This involved
             adding many new action functions, adding support for
             specifying editing modes in users' ~/.teclarc files,
             writing a higher level cursor motion function to support
             the different line-end bounds required in vi command
             mode, and a few small changes to support the fact that vi
             has two modes, input mode and command mode with different
             bindings.

             When vi editing mode is enabled, any binding that starts
             with an escape or a meta character, is interpreted as a
             command-mode binding, and switches the library to vi
             command mode if not already in that mode. Once in command
             mode the first character of all keysequences entered
             until input mode is re-enabled, are quietly coerced to
             meta characters before being looked up in the key-binding
             table. So, for example, in the key-binding table, the
             standard vi command-mode 'w' key, which moves the cursor
             one word to the right, is represented by M-w. This
             emulates vi's dual sets of bindings in a natural way
             without needing large changes to the library, or new
             binding syntaxes. Since cursor keys normally emit
             keysequences which start with escape, it also does
             something sensible when a cursor key is pressed during
             input mode (unlike true vi, which gets upset).

             I also added a ^Xg binding for the new list-glob action
             to both the emacs and vi key-binding tables. This lists
             the files that match the wild-card expression that
             precedes it on the command line.

             The function that reads in ~/.teclarc used to tell
             new_GetLine() to abort if it encountered anything that it
             didn't understand in this file. It now just reports an
             error and continues onto the next line.
           Makefile.in:
             When passing LIBS=$(LIBS) to recursive invokations of
             make, quotes weren't included around the $(LIBS) part.
             This would cause problems if LIBS ever contained more
             than one word (with the supplied configure script this
             doesn't happen currently). I added these quotes.
           expand.c man3/ef_expand_file.3:
             I wrote a new public function called ef_list_expansions(),
             to list the matching filenames returned by
             ef_expand_file().

             I also fixed the example in the man page, which cited
             exp->file instead of exp->files, and changed the
             dangerous name 'exp' with 'expn'.
           keytab.c:
             Key-binding tables start with 100 elements, and are
             supposedly incremented in size by 100 elements whenever
             the a table runs out of space. The realloc arguments to
             do this were wrong. This would have caused problems if
             anybody added a lot of personal bindings in their
             ~/.teclarc file. I only noticed it because the number of
             key bindings needed by the new vi mode exceeded this
             number.
           libtecla.map
             ef_expand_file() is now reported as having been added in
             the upcoming 1.3.0 release.

25/03/2001 Markus Gyger  (logged here by mcs)

           Makefile.in:
             Make symbolic links to alternative shared library names
             relative instead of absolute.
           Makefile.rules:
             The HP-UX libtecla.map.opt file should be made in the
             compilation directory, to allow the source code directory
             to be on a readonly filesystem.
           cplmatch.c demo2.c history.c pcache.c
             To allow the library to be compiled with a C++ compiler,
             without generating warnings, a few casts were added where
             void* return values were being assigned directly to
             none void* pointer variables.

25/03/2001 mcs@astro.caltech.edu

           libtecla.map:
             Added comment header to explain the purpose of the file.
             Also added cpl_init_FileArgs to the list of exported
             symbols. This symbol is deprecated, and no longer
             documented, but for backwards compatibility, it should
             still be exported.
           configure:
             I had forgotten to run autoconf before releasing version
             1.2.4, so I have just belatedly done so.  This enables
             Markus' changes to "configure.in" documented previously,
             (see 17/03/2001).

20/03/2001 John Levon   (logged here by mcs)

           libtecla.h
             A couple of the function prototypes in libtecla.h have
             (FILE *) argument declarations, which means that stdio.h
             needs to be included. The header file should be self
             contained, so libtecla.h now includes stdio.h.

18/03/2001 Version 1.2.4 released.

           README html/index.html configure.in
             Incremented minor version from 3 to 4.

18/03/2001 mcs@astro.caltech.edu

           getline.c
             The fix for the end-of-line problem that I released a
             couple of weeks ago, only worked for the first line,
             because I was handling this case when the cursor position
             was equal to the last column, rather than when the cursor
             position modulo ncolumn was zero.
           Makefile.in Makefile.rules
             The demos are now made by default, their rules now being
             int Makefile.rules instead of Makefile.in.
           INSTALL
             I documented how to compile the library in a different
             directory than the distribution directory.
             I also documented features designed to facilitate
             configuring and building the library as part of another
             package.

17/03/2001 Markus Gyger (logged here by mcs)

           getline.c
             Until now cursor motions were done one at a time. Markus
             has added code to make use the of the terminfo capability
             that moves the cursor by more than one position at a
             time. This greatly improves performance when editing near
             the start of long lines.
           getline.c
             To further improve performance, Markus switched from
             writing one character at a time to the terminal, using
             the write() system call, to using C buffered output
             streams. The output buffer is only flushed when
             necessary.
           Makefile.rules Makefile.in configure.in
             Added support for compiling for different architectures
             in different directories. Simply create another directory
             and run the configure script located in the original
             directory.
           Makefile.in configure.in libtecla.map
             Under Solaris, Linux and HP-UX, symbols that are to be
             exported by tecla shared libraries are explicitly specified
             via symbol map files. Only publicly documented functions
             are thus visible to applications.
           configure.in
             When linking shared libraries under Solaris SPARC,
             registers that are reserved for applications are marked
             as off limits to the library, using -xregs=no%appl when
             compiling with Sun cc, or -mno-app-regs when compiling
             with gcc. Also removed -z redlocsym for Solaris, which
             caused problems under some releases of ld.
           homedir.c  (after minor changes by mcs)
             Under ksh, ~+ expands to the current value of the ksh
             PWD environment variable, which contains the path of
             the current working directory, including any symbolic
             links that were traversed to get there. The special
             username "+" is now treated equally by tecla, except
             that it substitutes the return value of getcwd() if PWD
             either isn't set, or if it points at a different
             directory than that reported by getcwd().

08/03/2001 Version 1.2.3 released.

08/03/2001 mcs@astro.caltech.edu

           getline.c
             On compiling the library under HP-UX for the first time
             I encountered and fixed a couple of bugs:

             1. On all systems except Solaris, the callback function
                required by tputs() takes an int argument for the
                character that is to be printed. Under Solaris it
                takes a char argument. The callback function was
                passing this argument, regardless of type, to write(),
                which wrote the first byte of the argument.  This was
                fine under Solaris and under little-endian systems,
                because the first byte contained the character to be
                written, but on big-endian systems, it always wrote
                the zero byte at the other end of the word. As a
                result, no control characters were being written to
                the terminal.
             2. While attempting to start a newline after the user hit
                enter, the library was outputting the control sequence
                for moving the cursor down, instead of the newline
                character. On many systems the control sequence for
                moving the cursor down happends to be a newline
                character, but under HP-UX it isn't. The result was
                that no new line was being started under HP-UX.

04/03/2001 mcs@astro.caltech.edu

           configure.in Makefile.in Makefile.stub configure config.guess
           config.sub Makefile.rules install-sh PORTING README INSTALL
             Configuration and compilation of the library is now
             performed with the help of an autoconf configure
             script. In addition to relieving the user of the need to
             edit the Makefile, this also allows automatic compilation
             of the reentrant version of the library on platforms that
             can handle it, along with the creation of shared
             libraries where configured. On systems that aren't known
             to the configure script, just the static tecla library is
             compiled. This is currently the case on all systems
             except Linux, Solaris and HP-UX. In the hope that
             installers will provide specific conigurations for other
             systems, the configure.in script is heavily commented,
             and instructions on how to use are included in a new
             PORTING file.

24/02/2001 Version 1.2b released.

22/02/2001 mcs@astro.caltech.edu

           getline.c
             It turns out that most terminals, but not all, on writing
             a character in the rightmost column, don't wrap the
             cursor onto the next line until the next character is
             output. This library wasn't aware of this and thus if one
             tried to reposition the cursor from the last column,
             gl_get_line() thought that it was moving relative to a
             point on the next line, and thus moved the cursor up a
             line. The fix was to write one extra character when in
             the last column to force the cursor onto the next line,
             then backup the cursor to the start of the new line.
           getline.c
             On terminal initialization, the dynamic LINES and COLUMNS
             environment variables were ignored unless
             terminfo/termcap didn't return sensible dimensions. In
             practice, when present they should override the static
             versions in the terminfo/termcap databases. This is the
             new behavior. In reality this probably won't have caused
             many problems, because a SIGWINCH signal which informs of
             terminal size changes is sent when the terminal is
             opened, so the dimensions established during
             initialization quickly get updated on most systems.

18/02/2001 Version 1.2a released.

18/02/2001 mcs@astro.caltech.edu

           getline.c
             Three months ago I moved the point at which termios.h
             was included in getline.c. Unfortunately, I didn't notice
             that this moved it to after the test for TIOCGWINSZ being
             defined. This resulted in SIGWINCH signals not being
             trapped for, and thus terminal size changes went
             unnoticed. I have now moved the test to after the 
             inclusion of termios.h.

12/02/2001 Markus Gyger     (described here by mcs)

           man3/pca_lookup_file.3 man3/gl_get_line.3
           man3/ef_expand_file.3 man3/cpl_complete_word.3
             In the 1.2 release of the library, all functions in the
             library were given man pages. Most of these simply
             include one of the above 4 man pages, which describe the
             functions while describing the modules that they are in.
             Markus added all of these function names to the lists in
             the "NAME" headers of the respective man pages.
             Previously only the primary function of each module was
             named there.

11/02/2001 mcs@astro.caltech.edu

           getline.c
             On entering a line that wrapped over two or more
             terminal, if the user pressed enter when the cursor
             wasn't on the last of the wrapped lines, the text of the
             wrapped lines that followed it got mixed up with the next
             line written by the application, or the next input
             line. Somehow this slipped through the cracks and wasn't
             noticed until now. Anyway, it is fixed now.

09/02/2001 Version 1.2 released.

04/02/2001 mcs@astro.caltech.edu

           pcache.c libtecla.h
             With all filesystems local, demo2 was very fast to start
             up, but on a Sun system with one of the target
             directories being on a remote nfs mounted filesystem, the
             startup time was many seconds. This was due to the
             executable selection callback being applied to all files
             in the path at startup. To avoid this, all files are now
             included in the cache, and the application specified
             file-selection callback is only called on files as they
             are matched. Whether the callback rejected or accepted
             them is then cached so that the next time an already
             checked file is looked at, the callback doesn't have to
             be called. As a result, startup is now fast on all
             systems, and since usually there are only a few matching
             file completions at a time, the delay during completion
             is also usually small. The only exception is if the user
             tries to complete an empty string, at which point all
             files have to be checked. Having done this once, however,
             doing it again is fast.
           man3/pca_lookup_file.3
             I added a man page documenting the new PathCache module.
           man3/<many-new-files>.3
             I have added man pages for all of the functions in each
             of the modules. These 1-line pages use the .so directive
             to redirect nroff to the man page of the parent module.
           man Makefile update_html
             I renamed man to man3 to make it easier to test man page
             rediction, and updated Makefile and update_html
             accordingly. I also instructed update_html to ignore
             1-line man pages when making html equivalents of the man
             pages.
           cplmatch.c
             In cpl_list_completions() the size_t return value of
             strlen() was being used as the length argument of a "%*s"
             printf directive. This ought to be an int, so the return
             value of strlen() is now cast to int. This would have
             caused problems on architectures where the size of a
             size_t is not equal to the size of an int.

02/02/2001 mcs@astro.caltech.edu

           getline.c
             Under UNIX, certain terminal bindings are set using the
             stty command. This, for example, specifies which control
             key generates a user-interrupt (usually ^C or ^Y). What I
             hadn't realized was that ASCII NUL is used as the way to
             specify that one of these bindings is unset. I have now
             modified the code to skip unset bindings, leaving the
             corresponding action bound to the built-in default, or a
             user provided binding.

28/01/2001 mcs@astro.caltech.edu

           pcache.c libtecla.h
             A new module was added which supports searching for files
             in any colon separated list of directories, such as the
             unix execution PATH environment variable. Files in these
             directories, after being individually okayed for
             inclusion via an application provided callback, are
             cached in a PathCache object. You can then look up the
             full pathname of a given filename, or you can use the
             provided completion callback to list possible completions
             in the path-list. The contents of relative directories,
             such as ".", obviously can't be cached, so these
             directories are read on the fly during lookups and
             completions. The obvious application of this facility is
             to provide Tab-completion of commands, and thus a
             callback to place executable files in the cache, is
             provided.
           demo2.c
             This new program demonstrates the new PathCache
             module. It reads and processes lines of input until the
             word 'exit' is entered, or C-d is pressed. The default
             tab-completion callback is replaced with one which at the
             start of a line, looks up completions of commands in the
             user's execution path, and when invoked in other parts of
             the line, reverts to normal filename completion. Whenever
             a new line is entered, it extracts the first word on the
             line, looks it up in the user's execution path to see if
             it corresponds to a known command file, and if so,
             displays the full pathname of the file, along with the
             remaining arguments.
           cplfile.c
             I added an optional pair of callback function/data
             members to the new cpl_file_completions() configuration
             structure. Where provided, this callback is asked
             on a file-by-file basis, which files should be included
             in the list of file completions. For example, a callback
             is provided for listing only completions of executable
             files.
           cplmatch.c
             When listing completions, the length of the type suffix
             of each completion wasn't being taken into account
             correctly when computing the column widths. Thus the
             listing appeared ragged sometimes. This is now fixed.
           pathutil.c
             I added a function for prepending a string to a path,
             and another for testing whether a pathname referred to
             an executable file.

28/01/2001 mcs@astro.caltech.edu

           libtecla.h cplmatch.c man/cpl_complete_word.3
             The use of a publically defined structure to configure
             the cpl_file_completions() callback was flawed, so a new
             approach has been designed, and the old method, albeit
             still supported, is no longer documented in the man
             pages. The definition of the CplFileArgs structure in
             libtecla.h is now accompanied by comments warning people
             not to modify it, since modifications could break
             applications linked to shared versions of the tecla
             library. The new method involves an opaque CplFileConf
             object, instances of which are returned by a provided
             constructor function, configured with provided accessor
             functions, and when no longer needed, deleted with a
             provided destructor function. This is documented in the
             cpl_complete_word man page. The cpl_file_completions()
             callback distinguishes what type of configuration
             structure it has been sent by virtue of a code placed at
             the beginning of the CplFileConf argument by its
             constructor.

04/01/2001 mcs@astro.caltech.edu (Release of version 1.1j)

           getline.c
             I added upper-case bindings for the default meta-letter
             keysequences such as M-b. They thus continue to work
             when the user has caps-lock on.
           Makefile
             I re-implemented the "install" target in terms of new
             install_lib, install_inc and install_man targets. When
             distributing the library with other packages, these new
             targets allows for finer grained control of the
             installation process.

30/12/2000 mcs@astro.caltech.edu

           getline.c man/gl_get_line.3
             I realized that the recall-history action that I
             implemented wasn't what Markus had asked me for. What he
             actually wanted was for down-history to continue going
             forwards through a previous history recall session if no
             history recall session had been started while entering
             the current line. I have thus removed the recall-history
             action and modified the down-history action function
             accordingly.

24/12/2000 mcs@astro.caltech.edu

           getline.c
             I modified gl_get_line() to allow the previously returned
             line to be passed in the start_line argument.
           getline.c man/gl_get_line.3
             I added a recall-history action function, bound to M^P.
             This recalls the last recalled history line, regardless
             of whether it was from the current or previous line.

13/12/2000 mcs@astro.caltech.edu (Release of version 1.1i)

           getline.c history.h history.c man/gl_get_line.3
             I implemented the equivalent of the ksh Operate action. I
             have named the tecla equivalent "repeat-history". This
             causes the line that is to be edited to returned, and
             arranges for the next most recent history line to be
             preloaded on the next call to gl_get_line(). Repeated
             invocations of this action thus result in successive
             history lines being repeated - hence the
             name. Implementing the ksh Operate action was suggested
             by Markus Gyger. In ksh it is bound to ^O, but since ^O
             is traditionally bound by the default terminal settings,
             to stop-output, I have bound the tecla equivalent to M-o.

01/12/2000 mcs@astro.caltech.edu (Release of version 1.1h)

           getline.c keytab.c keytab.h man/gl_get_line.3
             I added a digit-argument action, to allow repeat
             counts for actions to be entered. As in both tcsh
             and readline, this is bound by default to each of
             M-0, M-1 through to M-9, the number being appended
             to the current repeat count. Once one of these has been
             pressed, the subsequent digits of the repeat count can be
             typed with or without the meta key pressed. It is also
             possible to bind digit-argument to other keys, with or
             without a numeric final keystroke. See man page for
             details.

           getline.c man/gl_get_line.3
             Markus noted that my choice of M-< for the default
             binding of read-from-file, could be confusing, since
             readline binds this to beginning-of-history. I have
             thus rebound it to ^X^F (ie. like find-file in emacs).

           getline.c history.c history.h man/gl_get_line.3
             I have now implemented equivalents of the readline
             beginning-of-history and end-of-history actions.
             These are bound to M-< and M-> respectively.

           history.c history.h
             I Moved the definition of the GlHistory type, and
             its subordinate types from history.h to history.c.
             There is no good reason for any other module to
             have access to the innards of this structure.

27/11/2000 mcs@astro.caltech.edu (Release of version 1.1g)

           getline.c man/gl_get_line.3
             I added a "read-from-file" action function and bound it
             by default to M-<. This causes gl_get_line() to
             temporarily return input from the file who's name
             precedes the cursor.
             
26/11/2000 mcs@astro.caltech.edu

           getline.c keytab.c keytab.h man/gl_get_line.3
             I have reworked some of the keybinding code again.

             Now, within key binding strings, in addition to the
             previously existing notation, you can now use M-a to
             denote meta-a, and C-a to denote control-a. For example,
             a key binding which triggers when the user presses the
             meta key, the control key and the letter [
             simultaneously, can now be denoted by M-C-[, or M-^[ or
             \EC-[ or \E^[.

             I also updated the man page to use M- instead of \E in
             the list of default bindings, since this looks cleaner.

           getline.c man/gl_get_line.3
             I added a copy-region-as-kill action function and
             gave it a default binding to M-w.

22/11/2000 mcs@astro.caltech.edu

           *.c
             Markus Gyger sent me a copy of a previous version of
             the library, with const qualifiers added in appropriate
             places. I have done the same for the latest version.
             Among other things, this gets rid of the warnings
             that are generated if one tells the compiler to
             const qualify literal strings.

           getline.c getline.h glconf.c
             I have moved the contents of glconf.c and the declaration
             of the GetLine structure into getline.c. This is cleaner,
             since now only functions in getline.c can mess with the
             innards of GetLine objects. It also clears up some problems
             with system header inclusion order under Solaris, and also
             the possibility that this might result in inconsistent
             system macro definitions, which in turn could cause different
             declarations of the structure to be seen in different files.

           hash.c
             I wrote a wrapper function to go around strcmp(), such that
             when hash.c is compiled with a C++ compiler, the pointer
             to the wrapper function is a C++ function pointer.
             This makes it compatible with comparison function pointer
             recorded in the hash table.

           cplmatch.c getline.c libtecla.h
             Markus noted that the Sun C++ compiler wasn't able to
             match up the declaration of cpl_complete_word() in
             libtecla.h, where it is surrounded by a extern "C" {}
             wrapper, with the definition of this function in
             cplmatch.c. My suspicion is that the compiler looks not
             only at the function name, but also at the function
             arguments to see if two functions match, and that the
             match_fn() argument, being a fully blown function pointer
             declaration, got interpetted as that of a C function in
             one case, and a C++ function in the other, thus
             preventing a match.

             To fix this I now define a CplMatchFn typedef in libtecla.h,
             and use this to declare the match_fn callback.

20/11/2000 (Changes suggested by Markus Gyger to support C++ compilers):
           expand.c
             Renamed a variable called "explicit" to "xplicit", to
             avoid conflicts when compiling with C++ compilers.
           *.c
             Added explicit casts when converting from (void *) to
             other pointer types. This isn't needed in C but it is
             in C++.
           getline.c
             tputs() has a strange declaration under Solaris. I was
             enabling this declaration when the SPARC feature-test
             macro was set. Markus changed the test to hinge on the
             __sun and __SVR4 macros.
           direader.c glconf.c stringrp.c
             I had omitted to include string.h in these two files.

           Markus also suggested some other changes, which are still
           under discussion. With the just above changes however, the
           library compiles without complaint using g++.

19/11/2000 mcs@astro.caltech.edu
           getline.h getline.c keytab.c keytab.h glconf.c
           man/gl_get_line.3
             I added support for backslash escapes (include \e
             for the keyboard escape key) and literal binary
             characters to the characters allowed within key sequences
             of key bindings.

           getline.h getline.c keytab.c keytab.h glconf.c
           man/gl_get_line.3
             I introduced symbolic names for the arrow keys, and
             modified the library to use the cursor key sequences
             reported by terminfo/termcap in addition to the default
             ANSI ones. Anything bound to the symbolically named arrow
             keys also gets bound to the default and terminfo/termcap
             cursor key sequences. Note that under Solaris
             terminfo/termcap report the properties of hardware X
             terminals when TERM is xterm instead of the terminal
             emulator properties, and the cursor keys on these two
             systems generate different key sequences. This is an
             example of why extra default sequences are needed.

           getline.h getline.c keytab.c
             For some reason I was using \e to represent the escape
             character. This is supported by gcc, which thus doesn't
             emit a warning except with the -pedantic flag, but isn't
             part of standard C. I now use a macro to define escape
             as \033 in getline.h, and this is now used wherever the
             escape character is needed.

17/11/2000 mcs@astro.caltech.edu (Release of version 1.1d)

           getline.c, man/gl_get_line(3), html/gl_get_line.html
             In tcsh ^D is bound to a function which does different
             things depending on where the cursor is within the input
             line. I have implemented its equivalent in the tecla
             library. When invoked at the end of the line this action
             function displays possible completions. When invoked on
             an empty line it causes gl_get_line() to return NULL,
             thus signalling end of input. When invoked within a line
             it invokes forward-delete-char, as before. The new action
             function is called del-char-or-list-or-eof.

           getline.c, man/gl_get_line(3), html/gl_get_line.html
             I found that the complete-word and expand-file actions
             had underscores in their names instead of hyphens. This
             made them different from all other action functions, so I
             have changed the underscores to hyphens.

           homedir.c
             On SCO UnixWare while getpwuid_r() is available, the
             associated _SC_GETPW_R_SIZE_MAX macro used by sysconf()
             to find out how big to make the buffer to pass to this
             function to cater for any password entry, doesn't
             exist. I also hadn't catered for the case where sysconf()
             reports that this limit is indeterminate. I have thus
             change the code to substitute a default limit of 1024 if
             either the above macro isn't defined or if sysconf() says
             that the associated limit is indeterminate.
           
17/11/2000 mcs@astro.caltech.edu (Release of version 1.1c)

           getline.c, getline.h, history.c, history.h
             I have modified the way that the history recall functions
             operate, to make them better emulate the behavior of
             tcsh. Previously the history search bindings always
             searched for the prefix that preceded the cursor, then
             left the cursor at the same point in the line, so that a
             following search would search using the same prefix. This
             isn't how tcsh operates. On finding a matching line, tcsh
             puts the cursor at the end of the line, but arranges for
             the followup search to continue with the same prefix,
             unless the user does any cursor motion or character
             insertion operations in between, in which case it changes
             the search prefix to the new set of characters that are
             before the cursor. There are other complications as well,
             which I have attempted to emulate. As far as I can
             tell, the tecla history recall facilities now fully
             emulate those of tcsh.

16/11/2000 mcs@astro.caltech.edu (Release of version 1.1b)

           demo.c:
             One can now quit from the demo by typing exit.

           keytab.c:
             The first entry of the table was getting deleted
             by _kt_clear_bindings() regardless of the source
             of the binding. This deleted the up-arrow binding.
             Symptoms noted by gazelle@yin.interaccess.com.

           getline.h:
             Depending on which system include files were include
             before the inclusion of getline.h, SIGWINCH and
             TIOCGWINSZ might or might not be defined. This resulted
             in different definitions of the GetLine object in
             different files, and thus some very strange bugs! I have
             now added #includes for the necessary system header files
             in getline.h itself. The symptom was that on creating a
             ~/.teclarc file, the demo program complained of a NULL
             argument to kt_set_keybinding() for the first line of the
             file.

15/11/2000 mcs@astro.caltech.edu (Release of version 1.1a)

           demo.c:
             I had neglected to check the return value of
             new_GetLine() in the demo program. Oops.

           getline.c libtecla.h:
             I wrote gl_change_terminal(). This allows one to change to
             a different terminal or I/O stream, by specifying the
             stdio streams to use for input and output, along with the
             type of terminal that they are connected to.

           getline.c libtecla.h:
             Renamed GetLine::isterm to GetLine::is_term. Standard
             C reserves names that start with "is" followed by
             alphanumeric characters, so this avoids potential
             clashes in the future.

           keytab.c keytab.h
             Each key-sequence can now have different binding
             functions from different sources, with the user provided
             binding having the highest precedence, followed by the
             default binding, followed by any terminal specific
             binding. This allows gl_change_terminal() to redefine the
             terminal-specific bindings each time that
             gl_change_terminal() is called, without overwriting the
             user specified or default bindings. In the future, it will
             also allow for reconfiguration of user specified
             bindings after the call to new_GetLine(). Ie. deleting a
             user specified binding should reinstate any default or
             terminal specific binding.

           man/cpl_complete_word.3 html/cpl_complete_word.html
           man/ef_expand_file.3    html/ef_expand_file.html
           man/gl_get_line.3       html/gl_get_line.html
             I added sections on thread safety to the man pages of the
             individual modules.

           man/gl_get_line.3       html/gl_get_line.html
             I documented the new gl_change_terminal() function.

           man/gl_get_line.3       html/gl_get_line.html
             In the description of the ~/.teclarc configuration file,
             I had omitted the 'bind' command word in the example
             entry. I have now remedied this.
yuma123_2.14/libtecla/html/ef_expand_file.html0000664000175000017500000002333614770023131021473 0ustar vladimirvladimir Manual Page
ef_expand_file                    ef_expand_file



NAME

       ef_expand_file,   del_ExpandFile,   ef_last_error,  ef_list_expansions,
       new_ExpandFile - expand filenames containing ~user/$envvar and wildcard
       expressions

SYNOPSIS

       #include <libtecla.h>

       ExpandFile *new_ExpandFile(void);

       ExpandFile *del_ExpandFile(ExpandFile *ef);

       FileExpansion *ef_expand_file(ExpandFile *ef,
                                     const char *path,
                                     int pathlen);

       int ef_list_expansions(FileExpansion *result, FILE *fp,
                              int term_width);

       const char *ef_last_error(ExpandFile *ef);


DESCRIPTION

       The  ef_expand_file()  function  is  part of the tecla library (see the
       libtecla man page). It  expands  a  specified  filename,
       converting  ~user/  and  ~/ expressions at the start of the filename to
       the corresponding home directories, replacing $envvar with the value of
       the  corresponding  environment  variable,  and  then, if there are any
       wildcards, matching these against existing  filenames.  Backslashes  in
       the  input filename are interpreted as escaping any special meanings of
       the characters that follow them.  Only backslahes that  are  themselves
       preceded by backslashes are preserved in the expanded filename.

       In  the  presence  of  wildcards,  the  returned list of filenames only
       includes the names of existing files which match the wildcards.  Other-
       wise,  the  original  filename is returned after expansion of tilde and
       dollar expressions, and the result  is  not  checked  against  existing
       files. This mimics the file-globbing behavior of the unix tcsh shell.

       The supported wildcards and their meanings are:
         *        -  Match any sequence of zero or more characters.
         ?        -  Match any single character.
         [chars]  -  Match any single character that appears in
                     'chars'.  If 'chars' contains an expression of
                     the form a-b, then any character between a and
                     b, including a and b, matches. The '-'
                     character looses its special meaning as a
                     range specifier when it appears at the start
                     of the sequence of characters. The ']'
                     character also looses its significance as the
                     terminator of the range expression if it
                     appears immediately after the opening '[', at
                     which point it is treated one of the
                     characters of the range. If you want both '-'
                     and ']' to be part of the range, the '-'
                     should come first and the ']' second.

         [^chars] -  The same as [chars] except that it matches any
                     single character that doesn't appear in
                     'chars'.

       Note that wildcards never match the initial dot in filenames that start
       with '.'. The initial '.' must be explicitly specified in the filename.
       This  again  mimics  the globbing behavior of most unix shells, and its
       rational is based in the fact that in unix, files with names that start
       with  '.'  are usually hidden configuration files, which are not listed
       by default by the ls command.

       The following is a complete example of how to use  the  file  expansion
       function.

         #include <stdio.h>
         #include <libtecla.h>

         int main(int argc, char *argv[])
         {
           ExpandFile *ef;      /* The expansion resource object */
           char *filename;      /* The filename being expanded */
           FileExpansion *expn; /* The results of the expansion */
           int i;

           ef = new_ExpandFile();
           if(!ef)
             return 1;

           for(arg = *(argv++); arg; arg = *(argv++)) {
             if((expn = ef_expand_file(ef, arg, -1)) == NULL) {
               fprintf(stderr, "Error expanding %s (%s).\n", arg,
                                ef_last_error(ef));
             } else {
               printf("%s matches the following files:\n", arg);
               for(i=0; i<expn->nfile; i++)
                 printf(" %s\n", expn->files[i]);
             }
           }

           ef = del_ExpandFile(ef);
           return 0;
         }

       Descriptions of the functions used above are as follows:

         ExpandFile *new_ExpandFile(void)

       This  function creates the resources used by the ef_expand_file() func-
       tion. In particular, it maintains the memory that is used to record the
       array  of matching filenames that is returned by ef_expand_file(). This
       array is expanded as needed, so there is no built in limit to the  num-
       ber of files that can be matched.

         ExpandFile *del_ExpandFile(ExpandFile *ef)

       This  function  deletes  the resources that were returned by a previous
       call to new_ExpandFile(). It always returns NULL (ie a deleted object).
       It does nothing if the ef argument is NULL.

       A container of the following type is returned by ef_expand_file().

         typedef struct {
           int exists;   /* True if the files in files[] exist */
           int nfile;    /* The number of files in files[] */
           char **files; /* An array of 'nfile' filenames. */
         } FileExpansion;

         FileExpansion *ef_expand_file(ExpandFile *ef,
                                       const char *path,
                                       int pathlen)

       The  ef_expand_file()  function  performs  filename expansion, as docu-
       mented at the start of this section. Its first argument is  a  resource
       object  returned  by  new_ExpandFile().  A  pointer to the start of the
       filename to be matched is passed via the path argument. This must be  a
       normal  NUL  terminated  string, but unless a length of -1 is passed in
       pathlen, only the first pathlen characters will be used in the filename
       expansion.   If  the length is specified as -1, the whole of the string
       will be expanded.

       The function returns a pointer to a container who's  contents  are  the
       results  of  the expansion. If there were no wildcards in the filename,
       the nfile member will be 1, and the exists member should be queried  if
       it  is  important to know if the expanded file currently exists or not.
       If there were wildcards, then the contained files[] array will  contain
       the names of the nfile existing files that matched the wildcarded file-
       name, and the exists member will  have  the  value  1.  Note  that  the
       returned container belongs to the specified ef object, and its contents
       will change on each call, so if you need to retain the results of  more
       than  one  call  to  ef_expand_file(), you should either make a private
       copy  of  the  returned  results,  or  create  multiple  file-expansion
       resource objects via multiple calls to new_ExpandFile().

       On  error,  NULL  is  returned,  and an explanation of the error can be
       determined by calling ef_last_error(ef).

         const char *ef_last_error(ExpandFile *ef)

       This function returns  the  message  which  describes  the  error  that
       occurred  on  the last call to ef_expand_file(), for the given (Expand-
       File *ef) resource object.

         int ef_list_expansions(FileExpansion *result, FILE *fp,
                                int terminal_width);

       The ef_list_expansions() function provides a convenient way to list the
       filename expansions returned by ef_expand_file(). Like the unix ls com-
       mand, it arranges the filenames into equal width columns,  each  column
       having  the  width  of  the largest file. The number of columns used is
       thus determined by the length of the longest filename, and  the  speci-
       fied  terminal  width.  Beware  that filenames that are longer than the
       specified terminal width are printed without being truncated, so output
       longer than the specified terminal width can occur. The list is written
       to the stdio stream specified by the fp argument.


THREAD SAFETY

       In multi-threaded programs, you should use the libtecla_r.a version  of
       the library. This uses POSIX reentrant functions where available (hence
       the _r suffix), and disables features that rely on non-reentrant system
       functions. Currently there are no features disabled in this module.

       Using  the  libtecla_r.a  version of the library, it is safe to use the
       facilities of this module  in  multiple  threads,  provided  that  each
       thread  uses  a separately allocated ExpandFile object. In other words,
       if two threads want  to  do  file  expansion,  they  should  each  call
       new_ExpandFile() to allocate their own file-expansion objects.


FILES

       libtecla.a    -    The tecla library
       libtecla.h    -    The tecla header file.


SEE ALSO

       libtecla, gl_get_line, cpl_complete_word,
       pca_lookup_file


AUTHOR

       Martin Shepherd  (mcs@astro.caltech.edu)



                                                 ef_expand_file
yuma123_2.14/libtecla/html/release.html0000664000175000017500000007037014770023131020163 0ustar vladimirvladimirThe tecla library release notes
This file lists major changes which accompany each new release.

Version 1.6.1:

  This is primarily a minor bug-fix release.

  One added feature is the ability to call gl_normal_io() from
  callbacks registered by gl_watch_fd() and
  gl_inactivity_timeout(). This allows these callbacks to cleanly
  suspend line editing before either reading from the terminal, or
  writing to the terminal; and then subsequently causes the input line
  to be automatically redisplayed, and line-editing to be resumed by
  gl_get_line(), as soon as the callback returns.

  Another minor change is that if the terminal type specified in the
  TERM environment variable is set to "dumb", gl_get_line() now treats
  the terminal as though it were a non-interactive stream, rather than
  treating it as a VT100-compatible terminal. This means that it
  doesn't either prompt for input, or perform any command-line
  editing, even when it really is interacting with a terminal. This is
  aimed at the rare situation where a third-pary program that connects
  to libtecla through an embedded pseudo-terminal, needs to be forced
  to behave as though it weren't talking to a terminal, in order that
  it be useable in non-interactive scripts.

  Note that in the previous release, the optional configuration
  function, gl_tty_signals(), was incorrectly swapping the suspend and
  terminal signal handlers before installing them.

  A configuration problem that prevented select() from being used
  under MacOS X, has been fixed.

  Although not documented in the man page, it was meant to be possible
  to take the input line that one call to gl_get_line() returned, and
  ask the next call to gl_get_line() to present it back to the user
  for re-editing, simply by passing the pointer returned by one call
  to gl_get_line() as the start_line argument of the next call to
  gl_get_line(). This feature unfortunately stopped working in 1.6.0,
  so this release restores it, and officially documents it in the man
  page documentation of gl_get_line().

  In the previous version of the library, calling gl_terminal_size()
  on a system without SIGWINCH support, would crash the
  application. This has been fixed.

  Libtecla now apparently compiles cleanly under IRIX.

Version 1.6.0:

  This release is primarily a bug-fix release. However there are also
  four new functions, so the minor version number has been
  incremented to reflect this.

  Two of the new functions are gl_automatic_history() and
  gl_append_history(). The former of these functions allows the
  application to tell gl_get_line() not to automatically archive
  entered lines in the history list. The second of these functions
  allows the application to explicitly append a line to the history
  list. Thus together, these two functions allow the calling
  application to take over control of what is placed in the history
  list.

  The third new function is gl_query_char(), which prompts the user
  for a single character reply, which the user can then type without
  having to hit return to enter it. Unless echoing is disabled, the
  character that is entered is then displayed after the prompt,
  and a newline is started.

  Finally, the 4th new function is gl_read_char(), which also reads
  a single character from the user, but doesn't prompt the user, write
  anything to the terminal, or disturb any partially entered input
  line. It is thus safe to call this function not only from between
  calls to gl_get_line(), but also from application callback
  functions, even if gl_normal_io() hasn't been called.

  When using the history-search-backwards or history-search-forwards
  actions, if the search prefix that the user typed, contains any of
  the *,? or [ globbing characters, it is now treated as a glob
  pattern to be matched against historical lines, instead of a simple
  prefix.

  I have added a --without-file-system option to the configure
  script. This is intended for use in embedded systems that either
  don't have filesystems, or where the file-system code in libtecla is
  seen as unwanted bloat. See the INSTALL document for details.

  Similarly, I also added a --without-file-actions option to the
  configure script. This allows the application author/installer to
  prevent users of gl_get_line() from accessing the filesystem with
  the builtin actions of gl_get_line(). It does this by removing a
  number of action functions, such as expand-filename, and list-glob,
  and by changing the default behavior of other actions, such as
  complete-word and list-or-eof, to show no completions.

  Now to the bugs that have been fixed. Version 1.5.0 had a lot of big
  internal changes, so there are a number of bugs that needed to be
  fixed.  There was a bug which caused a crash if gl_load_history()
  was called multiple times. There was another bug which caused a
  prompt not to be displayed on the next line after switching from
  reading input from a file to reading from the terminal. Also, in
  tecla configuration files, backslash escaped characters within
  key-binding key-sequences weren't being escaped. Thus ^\\ got
  interpretted as a control-\ followed by a \ character instead of as
  a control-\. There was a bug in the history recall mechanism which
  caused the search prefix to be forgotten in certain complicated
  usage scenarios. There was a minor memory leak in the
  gl_configure_getline() function. Finally, if gl_get_line() was
  aborted by a signal, or any other abnormal event, the value of errno
  which originally indicated what had happened, got zeroed by the
  code that restored the terminal to a usable state. Thus the
  application couldn't figure out what had caused the error, apart
  from by looking at gl_return_status(). All of these bugs have been
  fixed.

  In the Makefile, there were a number of places where install-sh was
  invoked without a path prefix. This has now been remedied.

  A fully functional workaround for a bug in Solaris' terminal I/O
  code has also been implemented. This bug, which only manifested
  itself in libtecla's uncommonly used non-blocking server I/O mode,
  caused characters entered while in normal I/O mode, between calls to
  gl_get_line() to be invisible to the next call to gl_get_line(),
  until the user typed at least one more key after raw terminal mode
  was restored.

  The Gnu autoconf config.guess and config.sub scripts have been
  updated to their latest versions. Apparently the old versions that I
  was previously using were too old to know about certain BSD ports.
  
Version 1.5.0:

  This release includes several major new features for those using
  gl_get_line(), shared library support in Darwin, better cross
  compilation support, and various minor bug fixes.

  The biggest new feature is the option of a non-blocking I/O mode, in
  which gl_get_line() can safely be called from an application's
  external event-loop to incrementally read input lines from the user.
  This feature is documented in the gl_io_mode(3) man page.

  In addition, there is now support for the definition of additional
  word-completion action functions, which can then be bound to
  different keys. See the documentation of the gl_completion_action()
  function in the gl_get_line(3) man page.

  Externally defined action functions can also be defined, although
  presently they don't have write access to the input line, so they
  are restricted to operations that display information text to the
  terminal, or modify the environment of the calling application in
  some way. See the documentation of the gl_register_action() function
  in the gl_get_line(3) man page.

  Some of the non-blocking I/O support functions can also be used for
  improved signal handling in the normal blocking mode. In particular,
  the gl_list_signals() and gl_catch_blocked() functions make it
  easier to write reliable signal handling around gl_get_line(). The
  new "RELIABLE SIGNAL HANDLING" section of the gl_get_line(3) man
  page is intended as an introduction to this subject.

  Programs can now clear the terminal between calls to gl_get_line(),
  by calling the new gl_erase_terminal() function.

  The gl_display_text() function, now used in the demos to display
  introductory banners, is provided for formatting text according to
  the width of the terminal.

  It is now possible to install inactivity timeout callbacks in
  gl_get_line(), using the new gl_inactivity_timeout() function.

  The new gl_set_term_size() function allows the application to
  explicitly set the terminal size, for cases, such as when one is
  using a terminal at the end of a serial lineq, where the terminal
  driver doesn't send the process a SIGWINCH when the terminal size
  changes.

  The new gl_bind_keyseq() function provides a convenient
  alternative to gl_configure_getline(), for binding or unbinding
  one key-sequence at a time.

  gl_get_line()s signal handling, file-descriptor event-handling,
  inactivity-timeout handling and server-mode non-blocking I/O
  features now not only work when input is coming from a terminal, but
  now also work when input is coming from non-interactive streams,
  such as files and pipes.

  The history implementation has been re-written to make it more
  efficient and easier to modify. The biggest user-level change is
  that when recalling history lines using a search prefix, the same
  line is no longer returned more than once in a row.  Previously this
  duplicate elimination only worked when one was recalling a line
  without specifying a search prefix, and this was naively performed
  by preventing neighboring duplicates from existing in the history
  list, rather than by skipping duplicates at search time.

  In previous versions of the library, when gl_get_line() and its
  associated public functions detected invalid arguments, or couldn't
  allocate memory, etc, error messages were written to stderr. This
  isn't appropriate for library functions, so instead of writing such
  messages to stderr, these messages are now recorded in buffers
  within the affected GetLine object. The latest error message can
  then subsequently be queried by calling gl_error_message(). The use
  of errno has also been expanded, and a new function called
  gl_return_status() has been provided to expand on the cause of the
  last return from gl_get_line().

  User level usage and configuration information has now been split
  out of the gl_get_line(3) man page into a separate tecla(7) man
  page. The enhance(3) man page has also been renamed to enhance(1).

  When expanding "~/", gl_get_line() now checks for, and returns the
  value of the HOME environment variable, if it exists, in preference
  to looking up the directory of the current user in the password
  file.

  When the terminal was resized to a narrower width, previous versions
  of gl_get_line() would redraw the line higher up the terminal. This
  bug has been fixed. A bug in history recall has also been fixed, in
  which an error message was being generated if one attempted to
  recall a line while the cursor was at the end of the longest
  possible input line. A more serious bug, in which callbacks
  registered by gl_watch_fd() weren't being called for write-events,
  has also been fixed. Finally, a few minor fixes have been made to
  improve support under QNX and Mac OS X.

  Beware that in this release, much of the underlying code has
  undergone some radical re-work, so although backwards compatibility
  of all documented features has been preserved, there may be some
  lingering bugs that could break existing programs.  So, if you plan
  to use this version in production code, please test it as far as
  possible within your application before releasing it to your
  clients, and as always, please report any unexpected behavior.

Version 1.4.1:

  This is a maintenance release. It includes minor changes to support
  Mac OS X (Darwin), the QNX real-time operating system, and Cygwin
  under Windows. It also fixes an oversight that was preventing the
  tab key from inserting tab characters when users unbound the
  complete-word action from it.

Version 1.4.0:

  The contents of the history list can now be saved and restored with
  the new gl_save_history() and gl_load_history() functions.

  Event handlers can now be registered to watch for and respond to I/O
  on arbitrary file descriptors while gl_get_line() is waiting for
  terminal input from the user. See the gl_get_line(3) man page
  for details on gl_watch_fd().

  As an optional alternative to getting configuration information only
  from ~/.teclarc, the new gl_configure_getline() function allows
  configuration commands to be taken from any of, a string, a
  specified application-specific file, and/or a specified
  user-specific file. See the gl_get_line(3) man page for details.

  The version number of the library can now be queried using the
  libtecla_version() function. See the libtecla(3) man page.

  The new gl_group_history() function allows applications to group
  different types of input line in the history buffer, and arrange for
  only members of the appropriate group to be recalled on a given call
  to gl_get_line(). See the gl_get_line(3) man page.

  The new gl_show_history() function displays the current history list
  to a given stdio output stream. See the gl_get_line(3) man page.

  new_GetLine() now allows you to specify a history buffer size of
  zero, thus requesting that no history buffer be allocated. You can
  subsequently resize or delete the history buffer at any time, by
  calling gl_resize_history(), limit the number of lines that are
  allowed in the buffer by calling gl_limit_history(), clear either
  all history lines from the history list, or just the history lines
  that are associated with the current history group, by calling
  gl_clear_history, and toggle the history mechanism on and off by
  calling gl_toggle_history().

  The new gl_terminal_size() function can be used to query the
  current terminal size. It can also be used to supply a default
  terminal size on systems where no mechanism is available for
  looking up the size.

  The contents and configuration of the history list can now be
  obtained by the calling application, by calling the new
  gl_lookup_history(), gl_state_of_history(), gl_range_of_history()
  and gl_size_of_history() functions. See the gl_get_line(3) man page.

  Echoing of the input line as it is typed, can now be turned on and
  off via the new gl_echo_mode() function. While echoing is disabled,
  newly entered input lines are omitted from the history list.  See
  the gl_get_line(3) man page.

  While the default remains to display the prompt string literally,
  the new gl_prompt_style() function can be used to enable text
  attribute formatting directives in prompt strings, such as
  underlining, bold font, and highlighting directives.

  Signal handling in gl_get_line() is now customizable. The default
  signal handling behavior remains essentially the same, except that
  the SIGTSTP, SIGTTIN and SIGTTOU are now forwarded to the
  corresponding signal handler of the calling program, instead of
  causing a SIGSTOP to be sent to the application.  It is now possible
  to remove signals from the list that are trapped by gl_get_line(),
  as well as add new signals to this list. The signal and terminal
  environments in which the signal handler of the calling program is
  invoked, and what gl_get_line() does after the signal handler
  returns, is now customizable on a per signal basis. You can now also
  query the last signal that was caught by gl_get_line(). This is
  useful when gl_get_line() aborts with errno=EINTR, and you need to
  know which signal caused it to abort.

  Key-sequences bound to action functions can now start with printable
  characters. Previously only keysequences starting with control or
  meta characters were permitted.

  gl_get_line() is now 8-bit clean. If the calling program has
  correctly called setlocale(LC_CTYPE,""), then the user can select an
  alternate locale by setting the standard LC_CTYPE, LC_ALL, or LANG
  environment variables, and international characters can then be
  entered directly, either by using a non-US keyboard, or by using a
  compose key on a standard US keyboard. Note that in locales in which
  meta characters become printable, meta characters no longer match
  M-c bindings, which then have to be entered using their escape-c
  equivalents.  Fortunately most modern terminal emulators either
  output the escape-c version by default when the meta key is used, or
  can be configured to do so (see the gl_get_line(3) man page), so in
  most cases you can continue to use the meta key.

  Completion callback functions can now tell gl_get_line() to return
  the input line immediately after a successful tab completion, simply
  by setting the last character of the optional continuation suffix to
  a newline character (ie. in the call to cpl_add_completion()).

  It is now safe to create and use multiple GetLine objects, albeit
  still only from a single thread. In conjunction with the new
  gl_configure_getline() function, this optionally allows multiple
  GetLine objects with different bindings to be used to implement
  different input modes.

  The edit-mode configuration command now accepts the argument,
  none. This tells gl_get_line() to revert to using just the native
  line editing facilities provided by the terminal driver. This could
  be used if the termcap or terminfo entry of the host terminal were
  badly corrupted.

  Application callback functions invoked by gl_get_line() can now
  change the displayed prompt using the gl_replace_prompt() function.

  Their is now an optional program distributed with the library. This
  is a beta release of a program which adds tecla command-line editing
  to virtually any third party application without the application
  needing to be linked to the library. See the enhance(3) man page for
  further details. Although built and installed by default, the
  INSTALL document explains how to prevent this.

  The INSTALL document now explains how you can stop the demo programs
  from being built and installed.

  NetBSD/termcap fixes. Mike MacFaden reported two problems that he
  saw when compiling libtecla under NetBSD. Both cases were related to
  the use of termcap.  Most systems use terminfo, so this problem has
  gone unnoticed until now, and won't have affected the grand majority
  of users.  The configure script had a bug which prevented the check
  for CPP working properly, and getline.c wouldn't compile due to an
  undeclared variable when USE_TERMCAP was defined. Both problems have
  now been fixed. Note that if you successfully compiled version
  1.3.3, this problem didn't affect you.

  An unfortunate and undocumented binding of the key-sequence M-O was
  shadowing the arrow-key bindings on systems that use ^[OA etc. I
  have removed this binding (the documented lower case M-o binding
  remains bound). Under the KDE konsole terminal this was causing the
  arrow keys to do something other than expected.

  There was a bug in the history list code which could result in
  strange entries appearing at the start of the history list once
  enough history lines had been added to the list to cause the
  circular history buffer to wrap. This is now fixed.
 
Version 1.3.3:

  Signal handling has been re-written, and documentation of its
  behaviour has been added to the gl_get_line(3) man page. In addition
  to eliminating race conditions, and appropriately setting errno for
  those signals that abort gl_get_line(), many more signals are now
  intercepted, making it less likely that the terminal will be left in
  raw mode by a signal that isn't trapped by gl_get_line().

  A bug was also fixed that was leaving the terminal in raw mode if
  the editing mode was changed interactively between vi and emacs.
  This was only noticeable when running programs from old shells that
  don't reset terminal modes.

Version 1.3.2:

  Tim Eliseo contributed a number of improvements to vi mode,
  including a fuller set of vi key-bindings, implementation of the vi
  constraint that the cursor can't backup past the point at which
  input mode was entered, and restoration of overwritten characters
  when backspacing in overwrite mode. There are also now new bindings
  to allow users to toggle between vi and emacs modes interactively.
  The terminal bell is now used in some circumstances, such as when an
  unrecognized key sequence is entered. This can be turned off by the
  new nobeep option in the tecla configuration file.

  Unrelated to the above, a problem under Linux which prevented ^Q
  from being used to resume terminal output after the user had pressed
  ^S, has been fixed.

Version 1.3.1:

  In vi mode a bug was preventing the history-search-backward and
  history-search-forward actions from doing anything when invoked on
  empty lines. On empty lines they now act like up-history and
  down-history respectively, as in emacs mode.

  When creating shared libraries under Linux, the -soname directive
  was being used incorrectly. The result is that Linux binaries linked
  with the 1.2.3, 1.2.4 and 1.3.0 versions of the tecla shared
  libraries, will refuse to see other versions of the shared library
  until relinked with version 1.3.1 or higher.

  The configure script can now handle the fact that under Solaris-2.6
  and earlier, the only curses library is a static one that hides in
  /usr/ccs/lib. Under Linux it now also caters for old versions of GNU
  ld which don't accept version scripts.

  The demos are now linked against the shared version of the library
  if possible. Previously they were always linked with the static
  version.

Version 1.3.0:

  The major change in this release is the addition of an optional vi
  command-line editing mode in gl_get_line(), along with lots of new
  action functions to support its bindings. To enable this, first
  create a ~/.teclarc file if you don't already have one, then add the
  following line to it.

   edit-mode vi

  The default vi bindings, which are designed to mimic those of the vi
  editor as closely as possible, are described in the gl_get_line(3)
  man page.

  A new convenience function called ef_list_expansions() has been
  added for listing filename expansions. See the ef_list_expansions(3)
  man page for details. This is used in a new list-glob binding, bound
  to ^Xg in emacs mode, and ^G in vi input mode.

  A bug has been fixed in the key-binding table expansion code. This
  bug would have caused problems to anybody who defined more than
  about 18 personalized key-bindings in their ~/.teclarc file.

Version 1.2.4:

  Buffered I/O is now used for writing to terminals, and where
  supported, cursor motion is done with move-n-positions terminfo
  capabilities instead of doing lots of move-1-position requests. This
  greatly improves how the library feels over slow links.

  You can now optionally compile different architectures in different
  directories, without having to make multiple copies of the
  distribution. This is documented in the INSTALL file.

  The ksh ~+ directive is now supported.

  Thanks to Markus Gyger for the above improvements.

  Documentation has been added to the INSTALL file describing features
  designed to facilitate configuration and installation of the library
  as part of larger packages. These features are intended to remove
  the need to modify the tecla distribution's configuration and build
  procedures when embedding the libtecla distribution in other package
  distributions.

  A previous fix to stop the cursor from warping when the last
  character of the input line was in the last column of the terminal,
  was only being used for the first terminal line of the input line.
  It is now used for all subsequent lines as well, as originally
  intended.
  
Version 1.2.3:

  The installation procedure has been better automated with the
  addition of an autoconf configure script. This means that installers
  can now compile and install the library by typing:

    ./configure
    make
    make install

  On all systems this makes at least the normal static version of the
  tecla library. It also makes the reentrant version if reentrant
  POSIX functions are detected.  Under Solaris, Linux and HP-UX the
  configuration script arranges for shared libraries to be compiled in
  addition to the static libraries.  It is hoped that installers will
  return information about how to compile shared libraries on other
  systems, for inclusion in future releases, and to this end, a new
  PORTING guide has been provided.

  The versioning number scheme has been changed. This release would
  have been 1.2c, but instead will be refered to as 1.2.3. The
  versioning scheme, based on conventions used by Sun Microsystems, is
  described in configure.in.

  The library was also tested under HP-UX, and this revealed two
  serious bugs, both of which have now been fixed.
  
  The first bug prevented the library from writing control codes to
  terminals on big-endian machines, with the exception of those
  running under Solaris. This was due to an int variable being used
  where a char was needed.

  The second bug had the symptom that on systems that don't use the
  newline character as the control code for moving the cursor down a
  line, a newline wasn't started when the user hit enter.

Version 1.2b:

  Two more minor bug fixes:

  Many terminals don't wrap the cursor to the next line when a
  character is written to the rightmost terminal column. Instead, they
  delay starting a new line until one more character is written, at
  which point they move the cursor two positions.  gl_get_line()
  wasn't aware of this, so cursor repositionings just after writing
  the last character of a column, caused it to erroneously go up a
  line. This has now been remedied, using a method that should work
  regardless of whether a terminal exhibits this behavior or not.

  Some systems dynamically record the current terminal dimensions in
  environment variables called LINES and COLUMNS. On such systems,
  during the initial terminal setup, these values should override the
  static values read from the terminal information databases, and now
  do.  Previously they were only used if the dimensions returned by
  terminfo/termcap looked bogus.

Version 1.2a:

  This minor release fixes the following two bugs:

  The initial terminal size and subsequent changes thereto, weren't
  being noticed by gl_get_line(). This was because the test for the
  existence of TIOCWINSZ was erroneously placed before the inclusion
  of termios.h. One of the results was that on input lines that
  spanned more than one terminal line, the cursor occasionally jumped
  unexpectedly to the previous terminal line.

  On entering a line that wrapped over multiple terminal lines,
  gl_get_line() simply output a carriage-return line-feed at the point
  at which the user pressed return. Thus if one typed in such a line,
  then moved back onto one of the earlier terminal lines before
  hitting return, the cursor was left on a line containing part of the
  line that had just been entered. This didn't do any harm, but it
  looked a mess.

Version 1.2:

  A new facility for looking up and completing filenames in UNIX-style
  paths has now been added (eg. you can search for, or complete
  commands using the UNIX PATH environment variable). See the
  pca_lookup_file(3) man page.

  The already existing filename completion callback can now be made
  selective in what types of files it lists. See the
  cpl_complete_word(3) man page.

  Due to its potential to break applications when changed, the use of
  the publically defined CplFileArgs structure to configure the
  cpl_file_completions() callback is now deprecated.  The definition
  of this structure has been frozen, and its documentation has been
  removed from the man pages.  It will remain supported, but if you
  have used it, you are recommended to switch to the new method, which
  involves a new opaque configuration object, allocated via a provided
  constructor function, configured via accessor functions, and
  eventually deleted with a provided destructor function. The
  cpl_file_completions() callback distinguishes which structure type
  it has been sent by virtue of a code placed at the start of the new
  structure by the constructor.  It is assumed that no existing
  applications set the boolean 'escaped' member of the CplFileArgs
  structure to 4568.  The new method is documented in the
  cpl_complete_word(3) man page.

Version 1.1j

  This was the initial public release on freshmeat.org.
yuma123_2.14/libtecla/html/gl_io_mode.html0000664000175000017500000006147514770023131020646 0ustar vladimirvladimir Manual Page
gl_io_mode                            gl_io_mode



NAME

        gl_io_mode, gl_raw_io, gl_normal_io, gl_tty_signals, gl_abandon_line,
        gl_handle_signal,  gl_pending_io  -  How  to use gl_get_line() from an
       external event loop.

SYNOPSIS

       #include <libtecla.h>

       int gl_io_mode(GetLine *gl, GlIOMode mode);

       int gl_raw_io(GetLine *gl);

       int gl_normal_io(GetLine *gl);

       int gl_tty_signals(void (*term_handler)(int),
                          void (*susp_handler)(int),
                          void (*cont_handler)(int),
                          void (*size_handler)(int));

       void gl_abandon_line(GetLine *gl);

       void gl_handle_signal(int signo, GetLine *gl, int ngl);

       GlPendingIO gl_pending_io(GetLine *gl);



DESCRIPTION

       The gl_get_line() function,  which  is  documented  separately  in  the
       gl_get_line  man page, supports two different I/O modes.
       These are selected by calling the gl_io_mode() function.


         int gl_io_mode(GetLine *gl, GlIOMode mode);


       The mode argument of this function specifies the new I/O mode, and must
       be one of the following.


         GL_NORMAL_MODE   -  Select the normal blocking-I/O mode.
                             In this mode gl_get_line()
                             doesn't return until either an error
                             occurs of the user finishes entering a
                             new line. This mode is the focus of
                             the gl_get_line man page.

         GL_SERVER_MODE   -  Select non-blocking server I/O mode.
                             In this mode, since non-blocking
                             terminal I/O is used, the entry of
                             each new input line typically requires
                             many calls to gl_get_line() from
                             an external I/O-driven event loop.
                             This mode is the focus of this man
                             page.


       Newly created GetLine objects start in normal I/O mode, so to switch to
       non-blocking server mode requires an initial call to gl_io_mode().


SERVER I/O MODE

       In non-blocking server I/O mode, the application is required to have an
       event  loop  which  calls  gl_get_line()  whenever  the  terminal  file
       descriptor can do the type I/O that gl_get_line() is  waiting  for.  To
       determine  which type of I/O gl_get_line() is waiting for, the applica-
       tion calls the gl_pending_io() function.


         GlPendingIO gl_pending_io(GetLine *gl);


       The return value of this function is one of the following  two  enumer-
       ated values.


         GLP_READ    -  gl_get_line() is waiting to write a
                        character to the terminal.

         GLP_WRITE   -  gl_get_line() is waiting to read a
                        character from the keyboad.


       If  the application is using either the select() or poll() system calls
       to watch for I/O on a group of file descriptors, then  it  should  call
       the gl_pending_io() function before each call to these functions to see
       which direction of I/O it should tell them to watch for, and  configure
       their  arguments  accordingly. In the case of the select() system call,
       this means using the FD_SET() macro to add the terminal file descriptor
       either to the set of file descriptors to be watched for readability, or
       the set to be watched for writability.

       As in normal I/O mode, the return value of gl_get_line()  is  either  a
       pointer  to a completed input line, or NULL. However, whereas in normal
       I/O mode a NULL return value always means that an  error  occurred,  in
       non-blocking  server  mode,  NULL  is  also returned when gl_get_line()
       can't read or write to the terminal  without  blocking.  Thus  in  non-
       blocking  server  mode,  in order to determine when a NULL return value
       signifies that an error occurred or not, it is necessary  to  call  the
       gl_return_status()  function.  If  this function returns the enumerated
       value, GLR_BLOCKED, as documented in the gl_get_line man
       page,  this  means  that gl_get_line() is waiting for I/O, and no error
       has occurred.

       When gl_get_line() returns NULL and gl_return_status()  indicates  that
       this  is  due  to  blocked  terminal  I/O,  the application should call
       gl_get_line() again when the type of I/O  reported  by  gl_pending_io()
       becomes  possible.  The  prompt,  start_line and start_pos arguments of
       gl_get_line() will be ignored on these calls.  If you  need  to  change
       the  prompt  of  the  line that is currently being edited, then you can
       call   the   gl_replace_prompt()   function    (documented    in    the
       gl_get_line man page) between calls to gl_get_line().


GIVING UP THE TERMINAL

       A  complication  that  is unique to non-blocking server mode is that it
       requires that the terminal  be  left  in  raw  mode  between  calls  to
       gl_get_line().  If  this  weren't  the  case,  the  external event loop
       wouldn't be able to detect individual key-presses, and the  basic  line
       editing implemented by the terminal driver would clash with the editing
       provided by gl_get_line(). What this means is that any  time  that  the
       terminal  needs  to  be used for other things than entering a new input
       line with gl_get_line(), it needs to be restored to a usable state.  In
       particular, whenever the process is suspended or terminated, the termi-
       nal must be returned to a  normal  state.  If  this  isn't  done,  then
       depending  on  the characteristics of the shell that was used to invoke
       the program, the user may end up with a hung terminal. To this end, the
       gl_normal_io()  function is provided for switching the terminal back to
       the state that it was in when raw mode was last established.


         int gl_normal_io(GetLine *gl);


       What this function does is first flush any pending output to the termi-
       nal,  then move the cursor to the start of the terminal line which fol-
       lows the end of the incompletely entered input line. At this  point  it
       is  safe  to  suspend  or terminate the process, and it is safe for the
       application to read and write to the terminal. To resume entry  of  the
       input line, the application should call the gl_raw_io() function.


         int gl_raw_io(GetLine *gl);


       This  function  starts  a  new line, redisplays the partially completed
       input line (if any), restores the cursor position within this  line  to
       where it was when gl_normal_io() was called, then switches back to raw,
       non-blocking terminal mode ready to continue entry of  the  input  line
       when gl_get_line() is next called.

       Note that in non-blocking server mode, if gl_get_line() is called after
       a call to gl_normal_io(), without an intervening call  to  gl_raw_io(),
       gl_get_line()  will  call  gl_raw_mode()  itself, and the terminal will
       remain in this mode when gl_get_line() returns.


SIGNAL HANDLING

       In the previous section it was pointed out that in non-blocking  server
       mode,  the  terminal must be restored to a sane state whenever a signal
       is received that either suspends or terminates the process.  In  normal
       I/O  mode,  this  is done for you by gl_get_line(), but in non-blocking
       server mode, since the terminal is left in raw mode  between  calls  to
       gl_get_line(),  this signal handling has to be done by the application.
       Since there are many signals that can suspend or terminate  a  process,
       as  well  as other signals that are important to gl_get_line(), such as
       the SIGWINCH signal, which tells it when the terminal size has changed,
       the  gl_tty_signals()  function  is provided for installing signal han-
       dlers for all pertinent signals.


         int gl_tty_signals(void (*term_handler)(int),
                            void (*susp_handler)(int),
                            void (*cont_handler)(int),
                            void (*size_handler)(int));


       What this does is use  gl_get_line()'s  internal  list  of  signals  to
       assign specified signal handlers to groups of signals. The arguments of
       this function are as follows.


         term_handler  -  This is the signal handler that is to be
                          used to trap signals that by default
                          terminate any process that receives
                          them (eg. SIGINT or SIGTERM).

         susp_handler  -  This is the signal handler that is to be
                          used to trap signals that by default
                          suspend any process that receives them,
                          (eg. SIGTSTP or SIGTTOU).

         cont_handler  -  This is the signal handler that is to be
                          used to trap signals that are usually
                          sent when a process resumes after being
                          suspended (usually SIGCONT). Beware that there is
                          nothing to stop a user from sending one of these
                          signals at other times.

         size_handler  -  This signal handler is used to trap
                          signals that are sent to processes when
                          their controlling terminals are resized
                          by the user (eg. SIGWINCH).


       These arguments can all be the same, if so desired, and you can specify
       SIG_IGN  (ignore  this  signal)  or  SIG_DFL  (use  the system-provided
       default signal handler) instead of a function where pertinent. In  par-
       ticular, it is rarely useful to trap SIGCONT, so the cont_handler argu-
       ment will usually be SIG_DFL or SIG_IGN.

       The gl_tty_signals() function uses the POSIX  sigaction()  function  to
       install  these  signal  handlers,  and it is careful to use the sa_mask
       member of each sigaction structure to ensure that  only  one  of  these
       signals  is  ever  delivered  at  a time. This guards against different
       instances of these signal handlers from simultaneously trying to  write
       to common global data, such as a shared sigsetjmp() buffer or a signal-
       received flag.

       The signal handlers that are installed by this  function,  should  call
       the gl_handle_signal().


         void gl_handle_signal(int signo, GetLine *gl, int ngl);


       The  signo  argument tells this function which signal it is being asked
       to respond to, and the gl argument should be a  pointer  to  the  first
       element  of  an  array of ngl GetLine objects. If your application only
       has one of these objects, just pass its pointer as the gl argument  and
       specify ngl as 1.

       Depending  on the signal that is being handled, this function does dif-
       ferent things.


   Terminal resize signals (SIGWINCH)
       If the signal indicates that the terminal was resized, then it arranges
       for the next call to gl_get_line() to ask the terminal for its new size
       and redraw the input line accordingly. In order that  gl_get_line()  be
       called as soon as possible to do this, gl_handle_signal() also arranges
       that the next call to gl_pending_io() will return  GLP_WRITE.  Thus  if
       the  application waits for I/O in select() or poll(), then the applica-
       tion needs to ensure that these functions will be reliably aborted when
       a  signal is caught and handled by the application. More on this below.


Process termination signals.

       If the signal that was caught is one of those that  by  default  termi-
       nates  any  process  that receives it, then gl_handle_signal() does the
       following steps.

       1. First it blocks the delivery of all signals that can be
          blocked (ie. SIGKILL and SIGSTOP can't be blocked)

       2. Next it calls gl_normal_io() for each of the ngl
          GetLine objects. Note that this does nothing to any of the
          GetLine objects that aren't currently in raw mode.

       3. Next it sets the signal handler of the signal to its default,
          process-termination disposition.

       4. Next it re-sends the process the signal that was caught.

       5. Finally it unblocks delivery of this signal, which
          results in the process being terminated.


Process suspension signals.

       If the default disposition of the signal is to suspend the process, the
       same steps are executed as for process termination signals, except that
       when the process is later resumed,  gl_handle_signal()  continues,  and
       does the following steps.

       6. It re-blocks delivery of the signal.

       7. It reinstates the signal handler of the signal to the one
          that was displaced when its default disposition was substituted.

       8. For any of the GetLine objects that were in raw mode when
          gl_handle_signal() was called, gl_handle_signal() then
          calls gl_raw_io(), to resume entry of the input lines on
          those terminals.

       9. Finally, it restores the signal process mask to how it
          was when gl_handle_signal() was called.

       Note  that  the  process  is suspended or terminated using the original
       signal that was caught, rather than using the uncatchable  SIGSTOP  and
       SIGKILL signals. This is important, because when a process is suspended
       or terminated, the parent of the process may wish  to  use  the  status
       value returned by the wait() system call to figure out which signal was
       responsible. In particular, most shells use this information to print a
       corresponding  message to the terminal. Users would be rightly confused
       if when their process received a SIGPIPE signal, the program  responded
       by  sending itself a SIGKILL signal, and the shell then printed out the
       provocative statement, "Killed!".


INTERRUPTING THE EVENT LOOP

       If a signal is caught and handled when the application's event loop  is
       waiting  in  select()  or  poll(), these functions will be aborted with
       errno set to EINTR. When  this  happens  the  event  loop  should  call
       gl_pending_io(),  before  calling  select()  or poll() again. It should
       then arrange for select() or poll() to wait for the type  of  I/O  that
       this reports. This is necessary, because any signal handler which calls
       gl_handle_signal(),  will  frequently  change  the  type  of  I/O  that
       gl_get_line() is waiting for.

       Unfortunately, if a signal arrives between the statements which config-
       ure the arguments of select() or poll() and the calls  to  these  func-
       tions,  then the signal will not be seen by these functions, which will
       then not be aborted. If these functions are waiting for keyboard  input
       from  the  user  when  the  signal  is received, and the signal handler
       arranges to redraw the input line to accomodate a  terminal  resize  or
       the resumption of the process, then this redisplay will be end up being
       delayed until the user hits the next key. Apart from puzzling the user,
       this  clearly  isn't  a serious problem. However there is a way, albeit
       complicated, to completely avoid this  race  condition.  The  following
       steps illustrate this.

       1. Block all of the signals that gl_get_line() catches,
          by passing the signal set returned by gl_list_signals() to
          sigprocmask().

       2. Call gl_pending_io() and set up the arguments of
          select() or poll() accordingly.

       3. Call sigsetjmp() with a non-zero savesigs argument.

       4. Initially this sigsetjmp() statement will return zero,
          indicating that control isn't resuming there after a matching
          call to siglongjmp().

       5. Replace all of the handlers of the signals that gl_get_line()
          is configured to catch, with a signal handler that first records
          the number of the signal that was caught, in a file-scope variable,
          then calls siglongjmp() with a non-zero value argument, to
          return execution to the above sigsetjmp()
          statement.  Registering these signal handlers can conveniently be
          done using the gl_tty_signals() function.

       6. Set the file-scope variable that the above signal handler uses to
          record any signal that is caught to -1, so that we can check
          whether a signal was caught by seeing if it contains a valid signal
          number.

       7. Now unblock the signals that were blocked in step 1. Any signal
          that was received by the process in between step 1 and now will
          now be delivered, and trigger our signal handler, as will any
          signal that is received until we block these signals again.

       8. Now call select() or poll().

       9. When select() returns, again block the signals that were
          unblocked in step 7.

       If a signal is arrived any time during the above steps, our signal han-
       dler will be triggered and cause control to return to  the  sigsetjmp()
       statement,  where this time, sigsetjmp() will return non-zero, indicat-
       ing that a signal was caught. When this  happens  we  simply  skip  the
       above  block of statements, and continue with the following statements,
       which are executed regardless of whether or not  a  signal  is  caught.
       Note  that when sigsetjmp() returns, regardless of why it returned, the
       process signal mask is returned to how  it  was  when  sigsetjmp()  was
       called.  Thus  the following statements are always executed with all of
       our signals blocked.

       9. Reinstate the signal handlers that were displaced in step 5.

       10. Check wether a signal was caught, by checking the file-scope
           variable that the signal handler records signal numbers in.

       11. If a signal was caught, send this signal to the application
           again, and unblock just this signal, so that it invokes the
           signal handler which we just reinstated in step 10.

       12. Unblock all of the signals that were blocked in step 7.

       Since this is complicated, note that demo3.c includes a working example
       of  how to do this. The method used there however, is more general than
       the above. What it provides is a wrapper function around select() which
       encompasses   steps   3  to  11.  In  this  wrapper,  rather  than  use
       gl_list_signals()  to  figure  out  the  signals  to  block,  and   and
       gl_tty_signals() to assign and revert signal handlers, one of its argu-
       ments is a sigset_t which specifies which signals to block  and  assign
       signal  handlers to. This function thus doesn't depend on gl_get_line()
       and can thus be used in other situations where race-condition-free sig-
       nal handling is required.


SIGNALS CAUGHT BY GL_GET_LINE

       Since  the  application  is  expected to handle signals in non-blocking
       server mode, gl_get_line() doesn't attempt to duplicate this when it is
       being  called.  If one of the signals that it is configured to catch is
       sent  to  the  application  while  gl_get_line()   is   being   called,
       gl_get_line() reinstates the caller's signal handlers, then just before
       returning, re-sends the signal to the process to let the  application's
       signal  handler handle it. If the process isn't terminated by this sig-
       nal, gl_get_line() returns NULL, and a following call to gl_return_sta-
       tus() returns the enumerated value GLR_SIGNAL.


ABORTING LINE INPUT

       Often,  rather  than  letting  it  terminate  the process, applications
       respond to the SIGINT user-interrupt signal  by  aborting  the  current
       input  line.  The  way to do this in non-blocking server-I/O mode is to
       not call gl_handle_signal() when this signal is caught, but instead  to
       call the gl_abandon_line().


         void gl_abandon_line(GetLine *gl);


       This function arranges that when gl_get_line() is next called, it first
       flushes any pending output to the terminal, then discardes the  current
       input  line,  outputs a new prompt on the next line, and finally starts
       accepting input of a new input line from the user.


SIGNAL SAFE FUNCTIONS

       Provided that certain rules are followed, the following  functions  can
       have  been  written  to  be safely callable from signal handlers. Other
       functions in this library should not be called from signal handlers.


         gl_normal_io()
         gl_raw_io()
         gl_handle_signal()
         gl_abandon_line()


       In order for this to be true, all signal handlers that call these func-
       tions  must  be  registered in such a way that only one instance of any
       one of them can be running at one time. The way to do this  is  to  use
       the  POSIX  sigaction()  function  to register all signal handlers, and
       when doing this, use the sa_mask member of the corresponding  sigaction
       structure,  to  indicate  that all of the signals who's handlers invoke
       the above functions, should be blocked when the current signal is being
       handled.  This prevents two signal handlers from operating on a GetLine
       object at the same time.

       To prevent signal  handlers  from  accessing  a  GetLine  object  while
       gl_get_line()  or  any of its associated public functions are operating
       on it, all public functions associated  with  gl_get_line(),  including
       gl_get_line()  itself,  temporarily  block the delivery of signals when
       they are accessing GetLine objects. Beware that the only  signals  that
       they  block  are the signals that gl_get_line() is currently configured
       to catch, so be sure that if you call any of the above  functions  from
       signal  handlers,  that the signals that these handlers are assigned to
       are configured to be caught by gl_get_line() (see gl_trap_signal()).


USING TIMEOUTS TO POLL

       If instead of using select() or poll() to wait for I/O,  your  applica-
       tion  just needs to get out of gl_get_line() periodically to briefly do
       something else before returning to accept input from the user, this can
       be  done  in  non-blocking server mode by using the gl_inactivity_time-
       out() function (see  gl_get_line),  to  specify  that  a
       callback  function that returns GLTO_CONTINUE should be called whenever
       gl_get_line() has been waiting for I/O for more than a specified amount
       of time.

       When  this callback is triggered, gl_get_line() will return NULL, and a
       following call to gl_return_status() will return GLR_BLOCKED.

       Beware that gl_get_line() won't return until the user  hasn't  typed  a
       key  for  the  specified  interval, so if the interval is long, and the
       user keeps typing, gl_get_line() may not return for a while.  In  other
       words  there is no guarantee that it will return in the time specified.


THE SERVER DEMO PROGRAM

       The demo3 program that is distributed  with  the  library,  provides  a
       working  example  of  how to use non-blocking server I/O mode in a real
       program. As far  as  the  user  is  concerned,  this  program  operates
       identically to the main demo program (called demo), except that whereas
       the main demo program uses the normal blocking I/O  mode,  demo3  using
       non-blocking  I/O  and  an  external event loop. The source code can be
       found in demo3.c, and the comments therein explain the various steps.


FILES

       libtecla.a      -    The tecla library
       libtecla.h      -    The tecla header file.


SEE ALSO

       libtecla, gl_get_line, tecla, ef_expand_file,
       cpl_complete_word, pca_lookup_file


AUTHOR

       Martin Shepherd  (mcs@astro.caltech.edu)



                                                     gl_io_mode
yuma123_2.14/libtecla/man/0000775000175000017500000000000014770023131015455 5ustar vladimirvladimiryuma123_2.14/libtecla/man/file/0000775000175000017500000000000014770023131016374 5ustar vladimirvladimiryuma123_2.14/libtecla/man/file/teclarc.in0000664000175000017500000000004614770023131020341 0ustar vladimirvladimir.so @MISC_MANDIR@/tecla.@MISC_MANEXT@ yuma123_2.14/libtecla/man/libr/0000775000175000017500000000000014770023131016405 5ustar vladimirvladimiryuma123_2.14/libtecla/man/libr/libtecla.in0000664000175000017500000001517314770023131020523 0ustar vladimirvladimir.\" Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd .\" .\" All rights reserved. .\" .\" Permission is hereby granted, free of charge, to any person obtaining a .\" copy of this software and associated documentation files (the .\" "Software"), to deal in the Software without restriction, including .\" without limitation the rights to use, copy, modify, merge, publish, .\" distribute, and/or sell copies of the Software, and to permit persons .\" to whom the Software is furnished to do so, provided that the above .\" copyright notice(s) and this permission notice appear in all copies of .\" the Software and that both the above copyright notice(s) and this .\" permission notice appear in supporting documentation. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS .\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF .\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR .\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL .\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING .\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, .\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION .\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Except as contained in this notice, the name of a copyright holder .\" shall not be used in advertising or otherwise to promote the sale, use .\" or other dealings in this Software without prior written authorization .\" of the copyright holder. .TH libtecla @LIBR_MANEXT@ .SH NAME libtecla - An interactive command-line input library. .SH SYNOPSIS .nf @CC@ ... -ltecla -lcurses .fi .SH DESCRIPTION The \f3tecla\f1 library provides programs with interactive command line editing facilities, similar to those of the unix \f3tcsh\f1 shell. In addition to simple command-line editing, it supports recall of previously entered command lines, TAB completion of file names or other tokens, and in-line wild-card expansion of filenames. The internal functions which perform file-name completion and wild-card expansion are also available externally for optional use by the calling program. .sp The various parts of the library are documented in the following man pages: .nf tecla(@MISC_MANEXT@) - Use level documentation of the command-line editing facilities provided by \f3gl_get_line()\f1. gl_get_line(@FUNC_MANEXT@) - The interactive line-input module. gl_io_mode(@FUNC_MANEXT@) - How to use \f3gl_get_line()\f1 in an incremental, non-blocking fashion. cpl_complete_word(@FUNC_MANEXT@) - The word completion module. ef_expand_file(@FUNC_MANEXT@) - The filename expansion module. pca_lookup_file(@FUNC_MANEXT@) - A directory-list based filename lookup and completion module. .fi In addition there is one optional application distributed with the library: .nf enhance(@PROG_MANEXT@) - Add command-line editing to third party applications. .fi .SH THREAD SAFETY If the library is compiled with -D_POSIX_C_SOURCE=199506L, reentrant versions of as many functions as possible are used. This includes using getpwuid_r() and getpwnam_r() instead of getpwuid() and getpwnam() when looking up the home directories of specific users in the password file (for ~user/ expansion), and readdir_r() instead of readdir() for reading directory entries when doing filename completion. The reentrant version of the library is usually called libtecla_r.a instead of libtecla.a, so if only the latter is available, it probably isn't the correct version to link with threaded programs. Reentrant functions for iterating through the password file aren't available, so when the library is compiled to be reentrant, TAB completion of incomplete usernames in \f3~username/\f1 expressions is disabled. This doesn't disable expansion of complete \f3~username\f1 expressions, which can be done reentrantly, or expansion of the parts of filenames that follow them, so this doesn't remove much functionality. The terminfo functions setupterm(), tigetstr(), tigetnum() and tputs() also aren't reentrant, but very few programs will want to interact with multiple terminals, so this shouldn't prevent this library from being used in threaded programs. .SH LIBRARY VERSION NUMBER The version number of the library can be queried using the following function. .sp .nf void libtecla_version(int *major, int *minor, int *micro); .fi .sp On return, this function records the three components of the libtecla version number in \f3*major\f1, \f3*minor\f1, \f3*micro\f1. The formal meaning of the three components is as follows. .sp .nf major - Incrementing this number implies that a change has been made to the library's public interface, which makes it binary incompatible with programs that were linked with previous shared versions of the tecla library. minor - This number is incremented by one whenever additional functionality, such as new functions or modules, are added to the library. micro - This is incremented whenever modifications to the library are made which make no changes to the public interface, but which fix bugs and/or improve the behind-the-scenes implementation. .fi .sp .SH TRIVIA In Spanish, a "tecla" is the key of a keyboard. Since this library centers on keyboard input, and given that I wrote much of the library while working in Chile, this seemed like a suitable name. .SH FILES .nf libtecla.a - The tecla library. libtecla.h - The tecla header file. ~/.teclarc - The tecla personal customization file. .fi .SH SEE ALSO .nf gl_get_line(@FUNC_MANEXT@), tecla(@MISC_MANEXT@), gl_io_mode(@FUNC_MANEXT@), ef_expand_file(@FUNC_MANEXT@), cpl_complete_word(@FUNC_MANEXT@), pca_lookup_file(@FUNC_MANEXT@), enhance(@PROG_MANEXT@) .fi .SH AUTHOR Martin Shepherd (mcs@astro.caltech.edu) .SH ACKNOWLEDGMENTS .nf Markus Gyger - Lots of assistance, including help with shared libraries, configuration information, particularly for Solaris; modifications to support C++ compilers, improvements for ksh users, faster cursor motion, output buffering, and changes to make gl_get_line() 8-bit clean. Mike MacFaden - Suggestions, feedback and testing that led to many of the major new functions that were added in version 1.4.0. Tim Eliseo - Many vi-mode bindings and fixes. .fi yuma123_2.14/libtecla/man/misc/0000775000175000017500000000000014770023131016410 5ustar vladimirvladimiryuma123_2.14/libtecla/man/misc/tecla.in0000664000175000017500000014626014770023131020041 0ustar vladimirvladimir.\" Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd .\" .\" All rights reserved. .\" .\" Permission is hereby granted, free of charge, to any person obtaining a .\" copy of this software and associated documentation files (the .\" "Software"), to deal in the Software without restriction, including .\" without limitation the rights to use, copy, modify, merge, publish, .\" distribute, and/or sell copies of the Software, and to permit persons .\" to whom the Software is furnished to do so, provided that the above .\" copyright notice(s) and this permission notice appear in all copies of .\" the Software and that both the above copyright notice(s) and this .\" permission notice appear in supporting documentation. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS .\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF .\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR .\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL .\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING .\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, .\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION .\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Except as contained in this notice, the name of a copyright holder .\" shall not be used in advertising or otherwise to promote the sale, use .\" or other dealings in this Software without prior written authorization .\" of the copyright holder. .TH tecla @MISC_MANEXT@ .SH NAME tecla, teclarc \- The user interface provided by the Tecla library. .SH DESCRIPTION This man page describes the command-line editing features that are available to users of programs that read keyboard input via the Tecla library. Users of the tcsh shell will find the default key-bindings very familiar. Users of the bash shell will also find it quite familiar, but with a few minor differences, most notably in how forward and backward searches through the list of historical commands are performed. There are two major editing modes, one with emacs-like key-bindings and another with vi-like key-bindings. By default emacs mode is enabled, but vi mode can alternatively be selected via the user's configuration file. This file can also be used to change the bindings of individual keys to suit the user's preferences. By default, tab completion is provided. If the application hasn't reconfigured this to complete other types of symbols, then tab completion completes file-names. .SH KEY SEQUENCE NOTATION In the rest of this man page, and also in all Tecla configuration files, key-sequences are expressed as follows. .sp .nf \f3^A\f1 or \f3C-a\f1 This is a control-A, entered by pressing the control key at the same time as the \f3A\f1 key. \f3\\E\f1 or \f3M-\f1 In key-sequences, both of these notations can be entered either by pressing the escape key, then the following key, or by pressing the Meta key at the same time as the following key. Thus the key sequence \f3M-p\f1 can be typed in two ways, by pressing the escape key, followed by pressing \f3p\f1, or by pressing the Meta key at the same time as \f3p\f1. \f3up\f1 This refers to the up-arrow key. \f3down\f1 This refers to the down-arrow key. \f3left\f1 This refers to the left-arrow key. \f3right\f1 This refers to the right-arrow key. \f3a\f1 This is just a normal A key. .fi .sp .SH THE TECLA CONFIGURATION FILE By default, Tecla looks for a file called \f3\&.teclarc\f1 in your home directory (ie. \f3~/.teclarc\f1). If it finds this file, it reads it, interpreting each line as defining a new key binding or an editing configuration option. Since the emacs keybindings are installed by default, if you want to use the non-default vi editing mode, the most important item to go in this file is the following line: .nf edit-mode vi .fi This will re-configure the default bindings for vi-mode. The complete set of arguments that this command accepts are: .sp .nf vi - Install key-bindings like those of the vi editor. emacs - Install key-bindings like those of the emacs editor. This is the default. none - Use just the native line editing facilities provided by the terminal driver. .fi .sp To prevent the terminal bell from being rung, such as when an unrecognized control-sequence is typed, place the following line in the configuration file: .nf nobeep .fi An example of a key binding line in the configuration file is the following. .nf bind M-[2~ insert-mode .fi On many keyboards, the above key sequence is generated when one presses the \f3insert\f1 key, so with this keybinding, one can toggle between the emacs-mode insert and overwrite modes by hitting one key. One could also do it by typing out the above sequence of characters one by one. As explained above, the \f3M-\f1 part of this sequence can be typed either by pressing the escape key before the following key, or by pressing the Meta key at the same time as the following key. Thus if you had set the above key binding, and the insert key on your keyboard didn't generate the above key sequence, you could still type it in either of the following 2 ways. .nf 1. Hit the escape key momentarily, then press '[', then '2', then finally '~'. 2. Press the meta key at the same time as pressing the '[' key, then press '2', then '~'. .fi If you set a keybinding for a key-sequence that is already bound to a function, the new binding overrides the old one. If in the new binding you omit the name of the new function to bind to the key-sequence, the original binding becomes undefined. .sp Starting with versions of libtecla later than 1.3.3 it is now possible to bind keysequences that begin with a printable character. Previously key-sequences were required to start with a control or meta character. .sp Note that the special keywords "up", "down", "left" and "right" refer to the arrow keys, and are thus not treated as keysequences. So, for example, to rebind the up and down arrow keys to use the history search mechanism instead of the simple history recall method, you could place the following in your configuration file: .nf bind up history-search-backwards bind down history-search-backwards .fi .sp To unbind an existing binding, you can do this with the bind command by omitting to name any action to rebind the key sequence to. For example, by not specifying an action function, the following command unbinds the default beginning-of-line action from the ^A key sequence: .nf bind ^A .fi If you create a \f3~/.teclarc\f1 configuration file, but it appears to have no effect on the program, check the documentation of the program to see if the author chose a different name for this file. .SH FILENAME AND TILDE COMPLETION With the default key bindings, pressing the TAB key (aka. \f3^I\f1) results in Tecla attempting to complete the incomplete filename that precedes the cursor. Tecla searches backwards from the cursor, looking for the start of the filename, stopping when it hits either a space or the start of the line. If more than one file has the specified prefix, then Tecla completes the filename up to the point at which the ambiguous matches start to differ, then lists the possible matches. .sp In addition to literally written filenames, Tecla can complete files that start with \f3~/\f1 and \f3~user/\f1 expressions and that contain \f3$envvar\f1 expressions. In particular, if you hit TAB within an incomplete \f3~user\f1, expression, Tecla will attempt to complete the username, listing any ambiguous matches. .sp The completion binding is implemented using the \f3cpl_word_completions()\f1 function, which is also available separately to users of this library. See the \f3cpl_word_completions(@LIBR_MANEXT@)\f1 man page for more details. .SH FILENAME EXPANSION With the default key bindings, pressing \f3^X*\f1 causes Tecla to expand the filename that precedes the cursor, replacing \f3~/\f1 and \f3~user/\f1 expressions with the corresponding home directories, and replacing \f3$envvar\f1 expressions with the value of the specified environment variable, then if there are any wildcards, replacing the so far expanded filename with a space-separated list of the files which match the wild cards. .sp The expansion binding is implemented using the \f3ef_expand_file()\f1 function. See the \f3ef_expand_file(@LIBR_MANEXT@)\f1 man page for more details. .SH RECALLING PREVIOUSLY TYPED LINES Every time that a new line is entered by the user, it is appended to a list of historical input lines maintained within the GetLine resource object. You can traverse up and down this list using the up and down arrow keys. Alternatively, you can do the same with the \f3^P\f1, and \f3^N\f1 keys, and in vi command mode you can alternatively use the k and j characters. Thus pressing up-arrow once, replaces the current input line with the previously entered line. Pressing up-arrow again, replaces this with the line that was entered before it, etc.. Having gone back one or more lines into the history list, one can return to newer lines by pressing down-arrow one or more times. If you do this sufficient times, you will return to the original line that you were entering when you first hit up-arrow. .sp Note that in vi mode, all of the history recall functions switch the library into command mode. .sp In emacs mode the \f3M-p\f1 and \f3M-n\f1 keys work just like the \f3^P\f1 and \f3^N\f1 keys, except that they skip all but those historical lines which share the prefix that precedes the cursor. In vi command mode the upper case \f3K\f1 and \f3J\f1 characters do the same thing, except that the string that they search for includes the character under the cursor as well as what precedes it. .sp Thus for example, suppose that you were in emacs mode, and you had just entered the following list of commands in the order shown: .nf ls ~/tecla/ cd ~/tecla ls -l getline.c emacs ~/tecla/getline.c .fi If you next typed: .nf ls .fi and then hit \f3M-p\f1, then rather than returning the previously typed emacs line, which doesn't start with "ls", Tecla would recall the "ls -l getline.c" line. Pressing \f3M-p\f1 again would recall the "ls ~/tecla/" line. Note that if the string that you are searching for, contains any of the special characters, *, ?, or '[', then it is interpretted as a pattern to be matched. Thus, cotinuing with the above example, after typing in the list of commands shown, if you then typed: .nf *tecla* .fi and hit \f3M-p\f1, then the "emacs ~/tecla/getline.c" line would be recalled first, since it contains the word tecla somewhere in the line, Similarly, hitting \f3M-p\f1 again, would recall the "ls ~/tecla/" line, and hitting it once more would recall the "ls ~/tecla/" line. The pattern syntax is the same as that described for filename expansion, in the \f3ef_expand_file(@LIBR_MANEXT@\f1 man page. .SH HISTORY FILES Authors of programs that use the Tecla library have the option of saving historical command-lines in a file before exiting, and subsequently reading them back in from this file when the program is next started. There is no standard name for this file, since it makes sense for each application to use its own history file, so that commands from different applications don't get mixed up. .SH INTERNATIONAL CHARACTER SETS Since libtecla version 1.4.0, Tecla has been 8-bit clean. This means that all 8-bit characters that are printable in the user's current locale are now displayed verbatim and included in the returned input line. Assuming that the calling program correctly contains a call like the following, .sp .nf setlocale(LC_CTYPE, ""); .fi .sp then the current locale is determined by the first of the environment variables \f3LC_CTYPE\f1, \f3LC_ALL\f1, and \f3LANG\f1, that is found to contain a valid locale name. If none of these variables are defined, or the program neglects to call setlocale, then the default \f3C\f1 locale is used, which is US 7-bit ASCII. On most unix-like platforms, you can get a list of valid locales by typing the command: .sp .nf locale -a .fi .sp at the shell prompt. .sp .SS "Meta keys and locales" Beware that in most locales other than the default C locale, meta characters become printable, and they are then no longer considered to match \f3M-c\f1 style key bindings. This allows international characters to be entered with the compose key without unexpectedly triggering meta key bindings. You can still invoke meta bindings, since there are actually two ways to do this. For example the binding \f3M-c\f1 can also be invoked by pressing the escape key momentarily, then pressing the \f3c\f1 key, and this will work regardless of locale. Moreover, many modern terminal emulators, such as gnome's gnome-terminal's and KDE's konsole terminals, already generate escape pairs like this when you use the meta key, rather than a real meta character, and other emulators usually have a way to request this behavior, so you can continue to use the meta key on most systems. .sp For example, although xterm terminal emulators generate real 8-bit meta characters by default when you use the meta key, they can be configured to output the equivalent escape pair by setting their \f3EightBitInput\f1 X resource to \f3False\f1. You can either do this by placing a line like the following in your \f3~/.Xdefaults\f1 file, .sp .nf XTerm*EightBitInput: False .sp .fi or by starting an xterm with an \f3-xrm '*EightBitInput: False'\f1 command-line argument. In recent versions of xterm you can toggle this feature on and off with the \f3"Meta Sends Escape"\f1 option in the menu that is displayed when you press the left mouse button and the control key within an xterm window. In CDE, dtterms can be similarly coerced to generate escape pairs in place of meta characters, by setting the \f3Dtterm*KshMode\f1 resource to \f3True\f1. .sp .SS "Entering international characters" If you don't have a keyboard that generates all of the international characters that you need, there is usually a compose key that will allow you to enter special characters, or a way to create one. For example, under X windows on unix-like systems, if your keyboard doesn't have a compose key, you can designate a redundant key to serve this purpose with the xmodmap command. For example, on many PC keyboards there is a microsoft-windows key, which is otherwise useless under Linux. On my laptop the \f3xev\f1 program reports that pressing this key generates keycode 115, so to turn this key into a compose key, I do the following: .sp .nf xmodmap -e 'keycode 115 = Multi_key' .fi .sp I can then enter an i with a umlaut over it by typing this key, followed by \f3"\f1, followed by i. .SH THE AVAILABLE KEY BINDING FUNCTIONS The following is a list of the editing functions provided by the Tecla library. The names in the leftmost column of the list can be used in configuration files to specify which function a given key or combination of keys should invoke. They are also used in the next two sections to list the default key-bindings in emacs and vi modes. .nf user-interrupt - Send a SIGINT signal to the parent process. abort - Send a SIGABRT signal to the parent process. suspend - Suspend the parent process. stop-output - Pause terminal output. start-output - Resume paused terminal output. literal-next - Arrange for the next character to be treated as a normal character. This allows control characters to be entered. cursor-right - Move the cursor one character right. cursor-left - Move the cursor one character left. insert-mode - Toggle between insert mode and overwrite mode. beginning-of-line - Move the cursor to the beginning of the line. end-of-line - Move the cursor to the end of the line. delete-line - Delete the contents of the current line. kill-line - Delete everything that follows the cursor. backward-kill-line - Delete all characters between the cursor and the start of the line. forward-word - Move to the end of the word which follows the cursor. forward-to-word - Move the cursor to the start of the word that follows the cursor. backward-word - Move to the start of the word which precedes the cursor. goto-column - Move the cursor to the 1-relative column in the line specified by any preceding digit-argument sequences (see ENTERING REPEAT COUNTS below). find-parenthesis - If the cursor is currently over a parenthesis character, move it to the matching parenthesis character. If not over a parenthesis character move right to the next close parenthesis. forward-delete-char - Delete the character under the cursor. backward-delete-char - Delete the character which precedes the cursor. list-or-eof - This is intended for binding to ^D. When invoked when the cursor is within the line it displays all possible completions then redisplays the line unchanged. When invoked on an empty line, it signals end-of-input (EOF) to the caller of gl_get_line(). del-char-or-list-or-eof - This is intended for binding to ^D. When invoked when the cursor is within the line it invokes forward-delete-char. When invoked at the end of the line it displays all possible completions then redisplays the line unchanged. When invoked on an empty line, it signals end-of-input (EOF) to the caller of gl_get_line(). forward-delete-word - Delete the word which follows the cursor. backward-delete-word - Delete the word which precedes the cursor. upcase-word - Convert all of the characters of the word which follows the cursor, to upper case. downcase-word - Convert all of the characters of the word which follows the cursor, to lower case. capitalize-word - Capitalize the word which follows the cursor. change-case - If the next character is upper case, toggle it to lower case and vice versa. redisplay - Redisplay the line. clear-screen - Clear the terminal, then redisplay the current line. transpose-chars - Swap the character under the cursor with the character just before the cursor. set-mark - Set a mark at the position of the cursor. exchange-point-and-mark - Move the cursor to the last mark that was set, and move the mark to where the cursor used to be. kill-region - Delete the characters that lie between the last mark that was set, and the cursor. copy-region-as-kill - Copy the text between the mark and the cursor to the cut buffer, without deleting the original text. yank - Insert the text that was last deleted, just before the current position of the cursor. append-yank - Paste the current contents of the cut buffer, after the cursor. up-history - Recall the next oldest line that was entered. Note that in vi mode you are left in command mode. down-history - Recall the next most recent line that was entered. If no history recall session is currently active, the next line from a previous recall session is recalled. Note that in vi mode you are left in command mode. history-search-backward - Recall the next oldest line who's prefix matches the string which currently precedes the cursor (in vi command-mode the character under the cursor is also included in the search string). Note that in vi mode you are left in command mode. history-search-forward - Recall the next newest line who's prefix matches the string which currently precedes the cursor (in vi command-mode the character under the cursor is also included in the search string). Note that in vi mode you are left in command mode. history-re-search-backward -Recall the next oldest line who's prefix matches that established by the last invocation of either history-search-forward or history-search-backward. history-re-search-forward - Recall the next newest line who's prefix matches that established by the last invocation of either history-search-forward or history-search-backward. complete-word - Attempt to complete the incomplete word which precedes the cursor. Unless the host program has customized word completion, filename completion is attempted. In vi commmand mode the character under the cursor is also included in the word being completed, and you are left in vi insert mode. expand-filename - Within the command line, expand wild cards, tilde expressions and dollar expressions in the filename which immediately precedes the cursor. In vi commmand mode the character under the cursor is also included in the filename being expanded, and you are left in vi insert mode. list-glob - List any filenames which match the wild-card, tilde and dollar expressions in the filename which immediately precedes the cursor, then redraw the input line unchanged. list-history - Display the contents of the history list for the current history group. If a repeat count of > 1 is specified, only that many of the most recent lines are displayed. See the "ENTERING REPEAT COUNTS" section. read-from-file - Temporarily switch to reading input from the file who's name precedes the cursor. read-init-files - Re-read teclarc configuration files. beginning-of-history - Move to the oldest line in the history list. Note that in vi mode you are left in command mode. end-of-history - Move to the newest line in the history list (ie. the current line). Note that in vi mode this leaves you in command mode. digit-argument - Enter a repeat count for the next key-binding function. For details, see the ENTERING REPEAT COUNTS section. newline - Terminate and return the current contents of the line, after appending a newline character. The newline character is normally '\\n', but will be the first character of the key-sequence that invoked the newline action, if this happens to be a printable character. If the action was invoked by the '\\n' newline character or the '\\r' carriage return character, the line is appended to the history buffer. repeat-history - Return the line that is being edited, then arrange for the next most recent entry in the history buffer to be recalled when Tecla is next called. Repeatedly invoking this action causes successive historical input lines to be re-executed. Note that this action is equivalent to the 'Operate' action in ksh. ring-bell - Ring the terminal bell, unless the bell has been silenced via the \f3nobeep\f1 configuration option (see the THE TECLA CONFIGURATION FILE section). forward-copy-char - Copy the next character into the cut buffer (NB. use repeat counts to copy more than one). backward-copy-char - Copy the previous character into the cut buffer. forward-copy-word - Copy the next word into the cut buffer. backward-copy-word - Copy the previous word into the cut buffer. forward-find-char - Move the cursor to the next occurrence of the next character that you type. backward-find-char - Move the cursor to the last occurrence of the next character that you type. forward-to-char - Move the cursor to the character just before the next occurrence of the next character that the user types. backward-to-char - Move the cursor to the character just after the last occurrence before the cursor of the next character that the user types. repeat-find-char - Repeat the last backward-find-char, forward-find-char, backward-to-char or forward-to-char. invert-refind-char - Repeat the last backward-find-char, forward-find-char, backward-to-char, or forward-to-char in the opposite direction. delete-to-column - Delete the characters from the cursor up to the column that is specified by the repeat count. delete-to-parenthesis - Delete the characters from the cursor up to and including the matching parenthesis, or next close parenthesis. forward-delete-find - Delete the characters from the cursor up to and including the following occurence of the next character typed. backward-delete-find - Delete the characters from the cursor up to and including the preceding occurence of the next character typed. forward-delete-to - Delete the characters from the cursor up to, but not including, the following occurence of the next character typed. backward-delete-to - Delete the characters from the cursor up to, but not including, the preceding occurence of the next character typed. delete-refind - Repeat the last *-delete-find or *-delete-to action. delete-invert-refind - Repeat the last *-delete-find or *-delete-to action, in the opposite direction. copy-to-column - Copy the characters from the cursor up to the column that is specified by the repeat count, into the cut buffer. copy-to-parenthesis - Copy the characters from the cursor up to and including the matching parenthesis, or next close parenthesis, into the cut buffer. forward-copy-find - Copy the characters from the cursor up to and including the following occurence of the next character typed, into the cut buffer. backward-copy-find - Copy the characters from the cursor up to and including the preceding occurence of the next character typed, into the cut buffer. forward-copy-to - Copy the characters from the cursor up to, but not including, the following occurence of the next character typed, into the cut buffer. backward-copy-to - Copy the characters from the cursor up to, but not including, the preceding occurence of the next character typed, into the cut buffer. copy-refind - Repeat the last *-copy-find or *-copy-to action. copy-invert-refind - Repeat the last *-copy-find or *-copy-to action, in the opposite direction. vi-mode - Switch to vi mode from emacs mode. emacs-mode - Switch to emacs mode from vi mode. vi-insert - From vi command mode, switch to insert mode. vi-overwrite - From vi command mode, switch to overwrite mode. vi-insert-at-bol - From vi command mode, move the cursor to the start of the line and switch to insert mode. vi-append-at-eol - From vi command mode, move the cursor to the end of the line and switch to append mode. vi-append - From vi command mode, move the cursor one position right, and switch to insert mode. vi-replace-char - From vi command mode, replace the character under the cursor with the the next character entered. vi-forward-change-char - From vi command mode, delete the next character then enter insert mode. vi-backward-change-char - From vi command mode, delete the preceding character then enter insert mode. vi-forward-change-word - From vi command mode, delete the next word then enter insert mode. vi-backward-change-word - From vi command mode, delete the preceding word then enter insert mode. vi-change-rest-of-line - From vi command mode, delete from the cursor to the end of the line, then enter insert mode. vi-change-line - From vi command mode, delete the current line, then enter insert mode. vi-change-to-bol - From vi command mode, delete all characters between the cursor and the beginning of the line, then enter insert mode. vi-change-to-column - From vi command mode, delete the characters from the cursor up to the column that is specified by the repeat count, then enter insert mode. vi-change-to-parenthesis - Delete the characters from the cursor up to and including the matching parenthesis, or next close parenthesis, then enter vi insert mode. vi-forward-change-find - From vi command mode, delete the characters from the cursor up to and including the following occurence of the next character typed, then enter insert mode. vi-backward-change-find - From vi command mode, delete the characters from the cursor up to and including the preceding occurence of the next character typed, then enter insert mode. vi-forward-change-to - From vi command mode, delete the characters from the cursor up to, but not including, the following occurence of the next character typed, then enter insert mode. vi-backward-change-to - From vi command mode, delete the characters from the cursor up to, but not including, the preceding occurence of the next character typed, then enter insert mode. vi-change-refind - Repeat the last vi-*-change-find or vi-*-change-to action. vi-change-invert-refind - Repeat the last vi-*-change-find or vi-*-change-to action, in the opposite direction. vi-undo - In vi mode, undo the last editing operation. vi-repeat-change - In vi command mode, repeat the last command that modified the line. .fi .SH DEFAULT KEY BINDINGS IN EMACS MODE The following default key bindings, which can be overriden by the Tecla configuration file, are designed to mimic most of the bindings of the unix \f3tcsh\f1 shell, when it is in emacs editing mode. .sp This is the default editing mode of the Tecla library. .sp Under UNIX the terminal driver sets a number of special keys for certain functions. The tecla library attempts to use the same keybindings to maintain consistency. The key sequences shown for the following 6 bindings are thus just examples of what they will probably be set to. If you have used the \f3stty\f1 command to change these keys, then the default bindings should match. .nf ^C -> user-interrupt ^\\ -> abort ^Z -> suspend ^Q -> start-output ^S -> stop-output ^V -> literal-next .fi The cursor keys are refered to by name, as follows. This is necessary because different types of terminals generate different key sequences when their cursor keys are pressed. right -> cursor-right left -> cursor-left up -> up-history down -> down-history The remaining bindings don't depend on the terminal setttings. .nf ^F -> cursor-right ^B -> cursor-left M-i -> insert-mode ^A -> beginning-of-line ^E -> end-of-line ^U -> delete-line ^K -> kill-line M-f -> forward-word M-b -> backward-word ^D -> del-char-or-list-or-eof ^H -> backward-delete-char ^? -> backward-delete-char M-d -> forward-delete-word M-^H -> backward-delete-word M-^? -> backward-delete-word M-u -> upcase-word M-l -> downcase-word M-c -> capitalize-word ^R -> redisplay ^L -> clear-screen ^T -> transpose-chars ^@ -> set-mark ^X^X -> exchange-point-and-mark ^W -> kill-region M-w -> copy-region-as-kill ^Y -> yank ^P -> up-history ^N -> down-history M-p -> history-search-backward M-n -> history-search-forward ^I -> complete-word ^X* -> expand-filename ^X^F -> read-from-file ^X^R -> read-init-files ^Xg -> list-glob ^Xh -> list-history M-< -> beginning-of-history M-> -> end-of-history \\n -> newline \\r -> newline M-o -> repeat-history M-^V -> vi-mode M-0, M-1, ... M-9 -> digit-argument (see below) .fi Note that \f3^I\f1 is what the TAB key generates, and that \f3^@\f1 can be generated not only by pressing the control key and the \f3@\f1 key simultaneously, but also by pressing the control key and the space bar at the same time. .SH DEFAULT KEY BINDINGS IN VI MODE The following default key bindings are designed to mimic the vi style of editing as closely as possible. This means that very few editing functions are provided in the initial character input mode, editing functions instead being provided by the vi command mode. Vi command mode is entered whenever the escape character is pressed, or whenever a key-sequence that starts with a meta character is entered. In addition to mimicing vi, libtecla provides bindings for tab completion, wild-card expansion of file names, and historical line recall. .sp To learn how to tell the Tecla library to use vi mode instead of the default emacs editing mode, see the earlier section entitled THE TECLA CONFIGURATION FILE. .sp Under UNIX the terminal driver sets a number of special keys for certain functions. The Tecla library attempts to use the same keybindings to maintain consistency, binding them both in input mode and in command mode. The key sequences shown for the following 6 bindings are thus just examples of what they will probably be set to. If you have used the \f3stty\f1 command to change these keys, then the default bindings should match. .nf ^C -> user-interrupt ^\\ -> abort ^Z -> suspend ^Q -> start-output ^S -> stop-output ^V -> literal-next M-^C -> user-interrupt M-^\\ -> abort M-^Z -> suspend M-^Q -> start-output M-^S -> stop-output .fi Note that above, most of the bindings are defined twice, once as a raw control code like \f3^C\f1 and then a second time as a meta character like \f3M-^C\f1. The former is the binding for vi input mode, whereas the latter is the binding for vi command mode. Once in command mode all key-sequences that the user types that they don't explicitly start with an escape or a meta key, have their first key secretly converted to a meta character before the key sequence is looked up in the key binding table. Thus, once in command mode, when you type the letter \f3i\f1, for example, the Tecla library actually looks up the binding for \f3M-i\f1. The cursor keys are refered to by name, as follows. This is necessary because different types of terminals generate different key sequences when their cursor keys are pressed. right -> cursor-right left -> cursor-left up -> up-history down -> down-history The cursor keys normally generate a keysequence that start with an escape character, so beware that using the arrow keys will put you into command mode (if you aren't already in command mode). .sp The following are the terminal-independent key bindings for vi input mode. .nf ^D -> list-or-eof ^G -> list-glob ^H -> backward-delete-char ^I -> complete-word \\r -> newline \\n -> newline ^L -> clear-screen ^N -> down-history ^P -> up-history ^R -> redisplay ^U -> backward-kill-line ^W -> backward-delete-word ^X* -> expand-filename ^X^F -> read-from-file ^X^R -> read-init-files ^? -> backward-delete-char .fi The following are the key bindings that are defined in vi command mode, this being specified by them all starting with a meta character. As mentioned above, once in command mode the initial meta character is optional. For example, you might enter command mode by typing Esc, and then press h twice to move the cursor two positions to the left. Both h characters get quietly converted to M-h before being compared to the key-binding table, the first one because Escape followed by a character is always converted to the equivalent meta character, and the second because command mode was already active. .nf M-\\ -> cursor-right (Meta-space) M-$ -> end-of-line M-* -> expand-filename M-+ -> down-history M-- -> up-history M-< -> beginning-of-history M-> -> end-of-history M-^ -> beginning-of-line M-; -> repeat-find-char M-, -> invert-refind-char M-| -> goto-column M-~ -> change-case M-. -> vi-repeat-change M-% -> find-parenthesis M-a -> vi-append M-A -> vi-append-at-eol M-b -> backward-word M-B -> backward-word M-C -> vi-change-rest-of-line M-cb -> vi-backward-change-word M-cB -> vi-backward-change-word M-cc -> vi-change-line M-ce -> vi-forward-change-word M-cE -> vi-forward-change-word M-cw -> vi-forward-change-word M-cW -> vi-forward-change-word M-cF -> vi-backward-change-find M-cf -> vi-forward-change-find M-cT -> vi-backward-change-to M-ct -> vi-forward-change-to M-c; -> vi-change-refind M-c, -> vi-change-invert-refind M-ch -> vi-backward-change-char M-c^H -> vi-backward-change-char M-c^? -> vi-backward-change-char M-cl -> vi-forward-change-char M-c\\ -> vi-forward-change-char (Meta-c-space) M-c^ -> vi-change-to-bol M-c0 -> vi-change-to-bol M-c$ -> vi-change-rest-of-line M-c| -> vi-change-to-column M-c% -> vi-change-to-parenthesis M-dh -> backward-delete-char M-d^H -> backward-delete-char M-d^? -> backward-delete-char M-dl -> forward-delete-char M-d -> forward-delete-char (Meta-d-space) M-dd -> delete-line M-db -> backward-delete-word M-dB -> backward-delete-word M-de -> forward-delete-word M-dE -> forward-delete-word M-dw -> forward-delete-word M-dW -> forward-delete-word M-dF -> backward-delete-find M-df -> forward-delete-find M-dT -> backward-delete-to M-dt -> forward-delete-to M-d; -> delete-refind M-d, -> delete-invert-refind M-d^ -> backward-kill-line M-d0 -> backward-kill-line M-d$ -> kill-line M-D -> kill-line M-d| -> delete-to-column M-d% -> delete-to-parenthesis M-e -> forward-word M-E -> forward-word M-f -> forward-find-char M-F -> backward-find-char M-- -> up-history M-h -> cursor-left M-H -> beginning-of-history M-i -> vi-insert M-I -> vi-insert-at-bol M-j -> down-history M-J -> history-search-forward M-k -> up-history M-K -> history-search-backward M-l -> cursor-right M-L -> end-of-history M-n -> history-re-search-forward M-N -> history-re-search-backward M-p -> append-yank M-P -> yank M-r -> vi-replace-char M-R -> vi-overwrite M-s -> vi-forward-change-char M-S -> vi-change-line M-t -> forward-to-char M-T -> backward-to-char M-u -> vi-undo M-w -> forward-to-word M-W -> forward-to-word M-x -> forward-delete-char M-X -> backward-delete-char M-yh -> backward-copy-char M-y^H -> backward-copy-char M-y^? -> backward-copy-char M-yl -> forward-copy-char M-y\\ -> forward-copy-char (Meta-y-space) M-ye -> forward-copy-word M-yE -> forward-copy-word M-yw -> forward-copy-word M-yW -> forward-copy-word M-yb -> backward-copy-word M-yB -> backward-copy-word M-yf -> forward-copy-find M-yF -> backward-copy-find M-yt -> forward-copy-to M-yT -> backward-copy-to M-y; -> copy-refind M-y, -> copy-invert-refind M-y^ -> copy-to-bol M-y0 -> copy-to-bol M-y$ -> copy-rest-of-line M-yy -> copy-line M-Y -> copy-line M-y| -> copy-to-column M-y% -> copy-to-parenthesis M-^E -> emacs-mode M-^H -> cursor-left M-^? -> cursor-left M-^L -> clear-screen M-^N -> down-history M-^P -> up-history M-^R -> redisplay M-^D -> list-or-eof M-^I -> complete-word M-\\r -> newline M-\\n -> newline M-^X^R -> read-init-files M-^Xh -> list-history M-0, M-1, ... M-9 -> digit-argument (see below) .fi Note that \f3^I\f1 is what the TAB key generates. .SH ENTERING REPEAT COUNTS Many of the key binding functions described previously, take an optional count, typed in before the target keysequence. This is interpreted as a repeat count by most bindings. A notable exception is the goto-column binding, which interprets the count as a column number. .sp By default you can specify this count argument by pressing the meta key while typing in the numeric count. This relies on the \f3digit-argument\f1 action being bound to Meta-0, Meta-1 etc. Once any one of these bindings has been activated, you can optionally take your finger off the meta key to type in the rest of the number, since every numeric digit thereafter is treated as part of the number, unless it is preceded by the \f3literal-next\f1 binding. As soon as a non-digit, or literal digit key is pressed the repeat count is terminated and either causes the just typed character to be added to the line that many times, or causes the next key-binding function to be given that argument. .sp For example, in emacs mode, typing: .sp .nf M-12a .fi .sp causes the letter 'a' to be added to the line 12 times, whereas .sp .nf M-4M-c .fi .sp Capitalizes the next 4 words. .sp In vi command mode the Meta modifier is automatically added to all characters typed in, so to enter a count in vi command-mode, just involves typing in the number, just as it does in the vi editor itself. So for example, in vi command mode, typing: .sp .nf 4w2x .fi .sp moves the cursor four words to the right, then deletes two characters. .sp You can also bind \f3digit-argument\f1 to other key sequences. If these end in a numeric digit, that digit gets appended to the current repeat count. If it doesn't end in a numeric digit, a new repeat count is started with a value of zero, and can be completed by typing in the number, after letting go of the key which triggered the digit-argument action. .SH FILES .nf libtecla.a - The Tecla library libtecla.h - The Tecla header file. ~/.teclarc - The personal Tecla customization file. .fi .SH SEE ALSO .nf libtecla(@LIBR_MANEXT@), gl_get_line(@LIBR_MANEXT@), gl_io_mode(@LIBR_MANEXT@), ef_expand_file(@LIBR_MANEXT@), cpl_complete_word(@LIBR_MANEXT@), pca_lookup_file(@LIBR_MANEXT@) .fi .SH AUTHOR Martin Shepherd (mcs@astro.caltech.edu) yuma123_2.14/libtecla/man/func/0000775000175000017500000000000014770023131016410 5ustar vladimirvladimiryuma123_2.14/libtecla/man/func/cpl_complete_word.in0000664000175000017500000004504214770023131022446 0ustar vladimirvladimir.\" Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd .\" .\" All rights reserved. .\" .\" Permission is hereby granted, free of charge, to any person obtaining a .\" copy of this software and associated documentation files (the .\" "Software"), to deal in the Software without restriction, including .\" without limitation the rights to use, copy, modify, merge, publish, .\" distribute, and/or sell copies of the Software, and to permit persons .\" to whom the Software is furnished to do so, provided that the above .\" copyright notice(s) and this permission notice appear in all copies of .\" the Software and that both the above copyright notice(s) and this .\" permission notice appear in supporting documentation. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS .\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF .\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR .\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL .\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING .\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, .\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION .\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Except as contained in this notice, the name of a copyright holder .\" shall not be used in advertising or otherwise to promote the sale, use .\" or other dealings in this Software without prior written authorization .\" of the copyright holder. .TH cpl_complete_word @FUNC_MANEXT@ .SH NAME cpl_complete_word, cfc_file_start, cfc_literal_escapes, cfc_set_check_fn, cpl_add_completion, cpl_file_completions, cpl_last_error, cpl_list_completions, cpl_recall_matches, cpl_record_error, del_CplFileConf, del_WordCompletion, new_CplFileConf, new_WordCompletion \- lookup possible completions for a word .SH SYNOPSIS .nf #include #include WordCompletion *new_WordCompletion(void); WordCompletion *del_WordCompletion(WordCompletion *cpl); #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \\ void *data, \\ const char *line, \\ int word_end) typedef CPL_MATCH_FN(CplMatchFn); CPL_MATCH_FN(cpl_file_completions); CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, int word_end, void *data, CplMatchFn *match_fn); CplMatches *cpl_recall_matches(WordCompletion *cpl); int cpl_list_completions(CplMatches *result, FILE *fp, int term_width); int cpl_add_completion(WordCompletion *cpl, const char *line, int word_start, int word_end, const char *suffix, const char *type_suffix, const char *cont_suffix); void cpl_record_error(WordCompletion *cpl, const char *errmsg); const char *cpl_last_error(WordCompletion *cpl); #define CPL_CHECK_FN(fn) int (fn)(void *data, \\ const char *pathname) typedef CPL_CHECK_FN(CplCheckFn); CPL_CHECK_FN(cpl_check_exe); CplFileConf *new_CplFileConf(void); CplFileConf *del_CplFileConf(CplFileConf *cfc); void cfc_literal_escapes(CplFileConf *cfc, int literal); void cfc_file_start(CplFileConf *cfc, int start_index); void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data); .fi .SH DESCRIPTION The \f3cpl_complete_word()\f1 function is part of the tecla library (see the libtecla(@LIBR_MANEXT@) man page). It is usually called behind the scenes by \f3gl_get_line(@FUNC_MANEXT@)\f1, but can also be called separately. Given an input line containing an incomplete word to be completed, it calls a user-provided callback function (or the provided file-completion callback function) to look up all possible completion suffixes for that word. The callback function is expected to look backward in the line, starting from the specified cursor position, to find the start of the word to be completed, then to look up all possible completions of that word and record them, one at a time by calling \f3cpl_add_completion()\f1. .sp Descriptions of the functions of this module are as follows: .sp .nf WordCompletion *new_WordCompletion(void) .fi .sp This function creates the resources used by the \f3cpl_complete_word()\f1 function. In particular, it maintains the memory that is used to return the results of calling \f3cpl_complete_word()\f1. .sp .nf WordCompletion *del_WordCompletion(WordCompletion *cpl) .fi .sp This function deletes the resources that were returned by a previous call to \f3new_WordCompletion()\f1. It always returns \f3NULL\f1 (ie. a deleted object). It does nothing if the \f3cpl\f1 argument is \f3NULL\f1. .sp The callback functions which lookup possible completions should be defined with the following macro (which is defined in libtecla.h). .sp .nf #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \\ void *data, \\ const char *line, \\ int word_end) .fi .sp Functions of this type are called by \f3cpl_complete_word()\f1, and all of the arguments of the callback are those that were passed to said function. In particular, the \f3line\f1 argument contains the input line containing the word to be completed, and \f3word_end\f1 is the index of the character that follows the last character of the incomplete word within this string. The callback is expected to look backwards from \f3word_end\f1 for the start of the incomplete word. What constitutes the start of a word clearly depends on the application, so it makes sense for the callback to take on this responsibility. For example, the builtin filename completion function looks backwards until it hits an unescaped space, or the start of the line. Having found the start of the word, the callback should then lookup all possible completions of this word, and record each completion via separate calls to \f3cpl_add_completion()\f1. If the callback needs access to an application-specific symbol table, it can pass it and any other data that it needs, via the \f3data\f1 argument. This removes any need for globals. .sp The callback function should return 0 if no errors occur. On failure it should return 1, and register a terse description of the error by calling \f3cpl_record_error()\f1. .sp .nf void cpl_record_error(WordCompletion *cpl, const char *errmsg); .fi .sp The last error message recorded by calling \f3cpl_record_error()\f1, can subsequently be queried by calling \f3cpl_last_error()\f1, as described later. .sp .nf int cpl_add_completion(WordCompletion *cpl, const char *line, int word_start, int word_end, const char *suffix, const char *type_suffix, const char *cont_suffix); .fi .sp The \f3cpl_add_completion()\f1 function is called zero or more times by the completion callback function to record each possible completion in the specified \f3WordCompletion\f1 object. These completions are subsequently returned by \f3cpl_complete_word()\f1, as described later. The \f3cpl\f1, \f3line\f1, and \f3word_end\f1 arguments should be those that were passed to the callback function. The \f3word_start\f1 argument should be the index within the input line string of the start of the word that is being completed. This should equal \f3word_end\f1 if a zero-length string is being completed. The \f3suffix\f1 argument is the string that would have to be appended to the incomplete word to complete it. If this needs any quoting (eg. the addition of backslashes before special charaters) to be valid within the displayed input line, this should be included. A copy of the suffix string is allocated internally, so there is no need to maintain your copy of the string after \f3cpl_add_completion()\f1 returns. .sp Note that in the array of possible completions which the \f3cpl_complete_word()\f1 function returns, the suffix recorded by \f3cpl_add_completion()\f1 is listed along with the concatentation of this suffix with the word that lies between \f3word_start\f1 and \f3word_end\f1 in the input line. .sp The \f3type_suffix\f1 argument specifies an optional string to be appended to the completion if it is displayed as part of a list of completions by \f3cpl_list_completions()\f1. The intention is that this indicate to the user the type of each completion. For example, the file completion function places a directory separator after completions that are directories, to indicate their nature to the user. Similary, if the completion were a function, you could indicate this to the user by setting \f3type_suffix\f1 to "()". Note that the \f3type_suffix\f1 string isn't copied, so if the argument isn't a literal string between speech marks, be sure that the string remains valid for at least as long as the results of \f3cpl_complete_word()\f1 are needed. .sp The \f3cont_suffix\f1 is a continuation suffix to append to the completed word in the input line if this is the only completion. This is something that isn't part of the completion itself, but that gives the user an indication about how they might continue to extend the token. For example, the file-completion callback function adds a directory separator if the completed word is a directory. If the completed word were a function name, you could similarly aid the user by arranging for an open parenthesis to be appended. .sp .nf CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, int word_end, void *data, CplMatchFn *match_fn); .fi .sp The \f3cpl_complete_word()\f1 is normally called behind the scenes by \f3gl_get_line(@FUNC_MANEXT@)\f1, but can also be called separately if you separately allocate a \f3WordCompletion\f1 object. It performs word completion, as described at the beginning of this section. Its first argument is a resource object previously returned by \f3new_WordCompletion()\f1. The \f3line\f1 argument is the input line string, containing the word to be completed. The \f3word_end\f1 argument contains the index of the character in the input line, that just follows the last character of the word to be completed. When called by \f3gl_get_line()\f1, this is the character over which the user pressed \f3TAB\f1. The \f3match_fn\f3 argument is the function pointer of the callback function which will lookup possible completions of the word, as described above, and the \f3data\f1 argument provides a way for the application to pass arbitrary data to the callback function. .sp If no errors occur, the \f3cpl_complete_word()\f1 function returns a pointer to a \f3CplMatches\f1 container, as defined below. This container is allocated as part of the \f3cpl\f1 object that was passed to \f3cpl_complete_word()\f1, and will thus change on each call which uses the same \f3cpl\f1 argument. .sp .nf typedef struct { char *completion; /* A matching completion */ /* string */ char *suffix; /* The part of the */ /* completion string which */ /* would have to be */ /* appended to complete the */ /* original word. */ const char *type_suffix; /* A suffix to be added when */ /* listing completions, to */ /* indicate the type of the */ /* completion. */ } CplMatch; typedef struct { char *suffix; /* The common initial part */ /* of all of the completion */ /* suffixes. */ const char *cont_suffix; /* Optional continuation */ /* string to be appended to */ /* the sole completion when */ /* nmatch==1. */ CplMatch *matches; /* The array of possible */ /* completion strings, */ /* sorted into lexical */ /* order. */ int nmatch; /* The number of elements in */ /* the above matches[] */ /* array. */ } CplMatches; .fi .sp If an error occurs during completion, \f3cpl_complete_word()\f1 returns NULL. A description of the error can be acquired by calling the \f3cpl_last_error()\f3 function. .sp .nf const char *cpl_last_error(WordCompletion *cpl); .fi .sp The \f3cpl_last_error()\f3 function returns a terse description of the error which occurred on the last call to \f3cpl_complete_word()\f1 or \f3cpl_add_completion()\f1. .sp .nf CplMatches *cpl_recall_matches(WordCompletion *cpl); .fi .sp As a convenience, the return value of the last call to \f3cpl_complete_word()\f1 can be recalled at a later time by calling \f3cpl_recall_matches()\f1. If \f3cpl_complete_word()\f1 returned \f3NULL\f1, so will \f3cpl_recall_matches()\f1. .sp .nf int cpl_list_completions(CplMatches *result, FILE *fp, int terminal_width); .fi .sp When the \f3cpl_complete_word()\f1 function returns multiple possible completions, the \f3cpl_list_completions()\f1 function can be called upon to list them, suitably arranged across the available width of the terminal. It arranges for the displayed columns of completions to all have the same width, set by the longest completion. It also appends the \f3type_suffix\f1 strings that were recorded with each completion, thus indicating their types to the user. .SH THE BUILT-IN FILENAME-COMPLETION CALLBACK By default the \f3gl_get_line(@FUNC_MANEXT@)\f1 function, passes the following completion callback function to \f3cpl_complete_word()\f1. This function can also be used separately, either by sending it to \f3cpl_complete_word()\f1, or by calling it directly from your own completion callback function. .sp .nf CPL_MATCH_FN(cpl_file_completions); .fi .sp Certain aspects of the behavior of this callback can be changed via its \f3data\f1 argument. If you are happy with its default behavior you can pass \f3NULL\f1 in this argument. Otherwise it should be a pointer to a \f3CplFileConf\f1 object, previously allocated by calling \f3new_CplFileConf()\f1. .sp .nf CplFileConf *new_CplFileConf(void); .fi .sp \f3CplFileConf\f1 objects encapsulate the configuration parameters of \f3cpl_file_completions()\f1. These parameters, which start out with default values, can be changed by calling the accessor functions described below. .sp By default, the \f3cpl_file_completions()\f3 callback function searches backwards for the start of the filename being completed, looking for the first un-escaped space or the start of the input line. If you wish to specify a different location, call \f3cfc_file_start()\f1 with the index at which the filename starts in the input line. Passing start_index=-1 re-enables the default behavior. .sp .nf void cfc_file_start(CplFileConf *cfc, int start_index); .fi .sp By default, when \f3cpl_file_completions()\f1 looks at a filename in the input line, each lone backslash in the input line is interpreted as being a special character which removes any special significance of the character which follows it, such as a space which should be taken as part of the filename rather than delimiting the start of the filename. These backslashes are thus ignored while looking for completions, and subsequently added before spaces, tabs and literal backslashes in the list of completions. To have unescaped backslashes treated as normal characters, call \f3cfc_literal_escapes()\f1 with a non-zero value in its \f3literal\f1 argument. .sp .nf void cfc_literal_escapes(CplFileConf *cfc, int literal); .fi .sp By default, \f3cpl_file_completions()\f1 reports all files who's names start with the prefix that is being completed. If you only want a selected subset of these files to be reported in the list of completions, you can arrange this by providing a callback function which takes the full pathname of a file, and returns \f30\f1 if the file should be ignored, or \f31\f1 if the file should be included in the list of completions. To register such a function for use by \f3cpl_file_completions()\f1, call \f3cfc_set_check_fn()\f1, and pass it a pointer to the function, together with a pointer to any data that you would like passed to this callback whenever it is called. Your callback can make its decisions based on any property of the file, such as the filename itself, whether the file is readable, writable or executable, or even based on what the file contains. .sp .nf #define CPL_CHECK_FN(fn) int (fn)(void *data, \\ const char *pathname) typedef CPL_CHECK_FN(CplCheckFn); void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data); .fi .sp The \f3cpl_check_exe()\f1 function is a provided callback of the above type, for use with \f3cpl_file_completions()\f1. It returns non-zero if the filename that it is given represents a normal file that the user has execute permission to. You could use this to have \f3cpl_file_completions()\f1 only list completions of executable files. .sp When you have finished with a \f3CplFileConf\f1 variable, you can pass it to the \f3del_CplFileConf()\f1 destructor function to reclaim its memory. .sp .nf CplFileConf *del_CplFileConf(CplFileConf *cfc); .fi .sp .SH THREAD SAFETY In multi-threaded programs, you should use the \f3libtecla_r.a\f1 version of the library. This uses POSIX reentrant functions where available (hence the \f3_r\f1 suffix), and disables features that rely on non-reentrant system functions. In the case of this module, the only disabled feature is username completion in \f3~username/\f1 expressions, in \f3cpl_file_completions()\f1. Using the \f3libtecla_r.a\f1 version of the library, it is safe to use the facilities of this module in multiple threads, provided that each thread uses a separately allocated \f3WordCompletion\f1 object. In other words, if two threads want to do word completion, they should each call \f3new_WordCompletion()\f1 to allocate their own completion objects. .SH FILES .nf libtecla.a - The tecla library libtecla.h - The tecla header file. .fi .SH SEE ALSO .nf libtecla(@LIBR_MANEXT@), gl_get_line(@FUNC_MANEXT@), ef_expand_file(@FUNC_MANEXT@), pca_lookup_file(@FUNC_MANEXT@) .fi .SH AUTHOR Martin Shepherd (mcs@astro.caltech.edu) yuma123_2.14/libtecla/man/func/new_WordCompletion.in0000664000175000017500000000006214770023131022554 0ustar vladimirvladimir.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/cpl_last_error.in0000664000175000017500000000006214770023131021750 0ustar vladimirvladimir.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_toggle_history.in0000664000175000017500000000005414770023131022463 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_echo_mode.in0000664000175000017500000000005414770023131021343 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/del_PathCache.in0000664000175000017500000000006014770023131021400 0ustar vladimirvladimir.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_change_terminal.in0000664000175000017500000000005414770023131022541 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/pca_path_completions.in0000664000175000017500000000006014770023131023127 0ustar vladimirvladimir.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/ef_expand_file.in0000664000175000017500000002375714770023131021706 0ustar vladimirvladimir.\" Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd .\" .\" All rights reserved. .\" .\" Permission is hereby granted, free of charge, to any person obtaining a .\" copy of this software and associated documentation files (the .\" "Software"), to deal in the Software without restriction, including .\" without limitation the rights to use, copy, modify, merge, publish, .\" distribute, and/or sell copies of the Software, and to permit persons .\" to whom the Software is furnished to do so, provided that the above .\" copyright notice(s) and this permission notice appear in all copies of .\" the Software and that both the above copyright notice(s) and this .\" permission notice appear in supporting documentation. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS .\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF .\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR .\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL .\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING .\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, .\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION .\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Except as contained in this notice, the name of a copyright holder .\" shall not be used in advertising or otherwise to promote the sale, use .\" or other dealings in this Software without prior written authorization .\" of the copyright holder. .TH ef_expand_file @FUNC_MANEXT@ .SH NAME ef_expand_file, del_ExpandFile, ef_last_error, ef_list_expansions, new_ExpandFile \- expand filenames containing ~user/$envvar and wildcard expressions .SH SYNOPSIS .nf #include ExpandFile *new_ExpandFile(void); ExpandFile *del_ExpandFile(ExpandFile *ef); FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen); int ef_list_expansions(FileExpansion *result, FILE *fp, int term_width); const char *ef_last_error(ExpandFile *ef); .fi .SH DESCRIPTION The \f3ef_expand_file()\f1 function is part of the tecla library (see the libtecla(@LIBR_MANEXT@) man page). It expands a specified filename, converting \f3~user/\f1 and \f3~/\f1 expressions at the start of the filename to the corresponding home directories, replacing \f3$envvar\f1 with the value of the corresponding environment variable, and then, if there are any wildcards, matching these against existing filenames. Backslashes in the input filename are interpreted as escaping any special meanings of the characters that follow them. Only backslahes that are themselves preceded by backslashes are preserved in the expanded filename. .sp In the presence of wildcards, the returned list of filenames only includes the names of existing files which match the wildcards. Otherwise, the original filename is returned after expansion of tilde and dollar expressions, and the result is not checked against existing files. This mimics the file-globbing behavior of the unix \f3tcsh\f1 shell. .sp The supported wildcards and their meanings are: .nf * - Match any sequence of zero or more characters. ? - Match any single character. [chars] - Match any single character that appears in 'chars'. If 'chars' contains an expression of the form a-b, then any character between a and b, including a and b, matches. The '-' character looses its special meaning as a range specifier when it appears at the start of the sequence of characters. The ']' character also looses its significance as the terminator of the range expression if it appears immediately after the opening '[', at which point it is treated one of the characters of the range. If you want both '-' and ']' to be part of the range, the '-' should come first and the ']' second. [^chars] - The same as [chars] except that it matches any single character that doesn't appear in 'chars'. .fi Note that wildcards never match the initial dot in filenames that start with '.'. The initial '.' must be explicitly specified in the filename. This again mimics the globbing behavior of most unix shells, and its rational is based in the fact that in unix, files with names that start with '.' are usually hidden configuration files, which are not listed by default by the ls command. .sp The following is a complete example of how to use the file expansion function. .nf #include #include int main(int argc, char *argv[]) { ExpandFile *ef; /* The expansion resource object */ char *filename; /* The filename being expanded */ FileExpansion *expn; /* The results of the expansion */ int i; ef = new_ExpandFile(); if(!ef) return 1; for(arg = *(argv++); arg; arg = *(argv++)) { if((expn = ef_expand_file(ef, arg, -1)) == NULL) { fprintf(stderr, "Error expanding %s (%s).\\n", arg, ef_last_error(ef)); } else { printf("%s matches the following files:\\n", arg); for(i=0; infile; i++) printf(" %s\\n", expn->files[i]); } } ef = del_ExpandFile(ef); return 0; } .fi .sp Descriptions of the functions used above are as follows: .sp .nf ExpandFile *new_ExpandFile(void) .fi .sp This function creates the resources used by the \f3ef_expand_file()\f1 function. In particular, it maintains the memory that is used to record the array of matching filenames that is returned by \f3ef_expand_file()\f1. This array is expanded as needed, so there is no built in limit to the number of files that can be matched. .sp .nf ExpandFile *del_ExpandFile(ExpandFile *ef) .fi .sp This function deletes the resources that were returned by a previous call to \f3new_ExpandFile()\f1. It always returns \f3NULL\f1 (ie a deleted object). It does nothing if the \f3ef\f1 argument is \f3NULL\f1. .sp A container of the following type is returned by \f3ef_expand_file()\f1. .sp .nf typedef struct { int exists; /* True if the files in files[] exist */ int nfile; /* The number of files in files[] */ char **files; /* An array of 'nfile' filenames. */ } FileExpansion; .fi .sp .nf FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen) .fi .sp The \f3ef_expand_file()\f1 function performs filename expansion, as documented at the start of this section. Its first argument is a resource object returned by \f3new_ExpandFile()\f1. A pointer to the start of the filename to be matched is passed via the \f3path\f1 argument. This must be a normal \f3NUL\f1 terminated string, but unless a length of -1 is passed in \f3pathlen\f1, only the first \f3pathlen\f1 characters will be used in the filename expansion. If the length is specified as -1, the whole of the string will be expanded. .sp The function returns a pointer to a container who's contents are the results of the expansion. If there were no wildcards in the filename, the \f3nfile\f1 member will be 1, and the \f3exists\f1 member should be queried if it is important to know if the expanded file currently exists or not. If there were wildcards, then the contained \f3files[]\f1 array will contain the names of the \f3nfile\f1 existing files that matched the wildcarded filename, and the \f3exists\f1 member will have the value 1. Note that the returned container belongs to the specified \f3ef\f1 object, and its contents will change on each call, so if you need to retain the results of more than one call to \f3ef_expand_file()\f1, you should either make a private copy of the returned results, or create multiple file-expansion resource objects via multiple calls to \f3new_ExpandFile()\f1. .sp On error, \f3NULL\f1 is returned, and an explanation of the error can be determined by calling \f3ef_last_error(ef)\f1. .sp .nf const char *ef_last_error(ExpandFile *ef) .fi .sp This function returns the message which describes the error that occurred on the last call to \f3ef_expand_file()\f1, for the given \f3(ExpandFile *ef)\f1 resource object. .sp .nf int ef_list_expansions(FileExpansion *result, FILE *fp, int terminal_width); .fi .sp The \f3ef_list_expansions()\f1 function provides a convenient way to list the filename expansions returned by \f3ef_expand_file()\f1. Like the unix \f3ls\f1 command, it arranges the filenames into equal width columns, each column having the width of the largest file. The number of columns used is thus determined by the length of the longest filename, and the specified terminal width. Beware that filenames that are longer than the specified terminal width are printed without being truncated, so output longer than the specified terminal width can occur. The list is written to the stdio stream specified by the \f3fp\f1 argument. .SH THREAD SAFETY In multi-threaded programs, you should use the \f3libtecla_r.a\f1 version of the library. This uses POSIX reentrant functions where available (hence the \f3_r\f1 suffix), and disables features that rely on non-reentrant system functions. Currently there are no features disabled in this module. Using the \f3libtecla_r.a\f1 version of the library, it is safe to use the facilities of this module in multiple threads, provided that each thread uses a separately allocated \f3ExpandFile\f1 object. In other words, if two threads want to do file expansion, they should each call \f3new_ExpandFile()\f1 to allocate their own file-expansion objects. .SH FILES .nf libtecla.a - The tecla library libtecla.h - The tecla header file. .fi .SH SEE ALSO .nf libtecla(@LIBR_MANEXT@), gl_get_line(@FUNC_MANEXT@), cpl_complete_word(@FUNC_MANEXT@), pca_lookup_file(@FUNC_MANEXT@) .fi .SH AUTHOR Martin Shepherd (mcs@astro.caltech.edu) yuma123_2.14/libtecla/man/func/gl_group_history.in0000664000175000017500000000005414770023131022336 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/cpl_list_completions.in0000664000175000017500000000006214770023131023163 0ustar vladimirvladimir.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_trap_signal.in0000664000175000017500000000005414770023131021724 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/pca_last_error.in0000664000175000017500000000006014770023131021733 0ustar vladimirvladimir.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_set_term_size.in0000664000175000017500000000005414770023131022275 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_read_char.in0000664000175000017500000000005414770023131021331 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/cpl_file_completions.in0000664000175000017500000000006214770023131023127 0ustar vladimirvladimir.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/cfc_set_check_fn.in0000664000175000017500000000006214770023131022164 0ustar vladimirvladimir.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/ef_last_error.in0000664000175000017500000000005714770023131021570 0ustar vladimirvladimir.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/pca_set_check_fn.in0000664000175000017500000000006014770023131022172 0ustar vladimirvladimir.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_watch_fd.in0000664000175000017500000000005414770023131021200 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_prompt_style.in0000664000175000017500000000005414770023131022162 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/cpl_add_completion.in0000664000175000017500000000006214770023131022555 0ustar vladimirvladimir.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_configure_getline.in0000664000175000017500000000005414770023131023111 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/new_CplFileConf.in0000664000175000017500000000006214770023131021733 0ustar vladimirvladimir.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/del_WordCompletion.in0000664000175000017500000000006214770023131022527 0ustar vladimirvladimir.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_load_history.in0000664000175000017500000000005414770023131022121 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_raw_io.in0000664000175000017500000000005314770023131020700 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_get_line.in0000664000175000017500000026377014770023131021227 0ustar vladimirvladimir.\" Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd .\" .\" All rights reserved. .\" .\" Permission is hereby granted, free of charge, to any person obtaining a .\" copy of this software and associated documentation files (the .\" "Software"), to deal in the Software without restriction, including .\" without limitation the rights to use, copy, modify, merge, publish, .\" distribute, and/or sell copies of the Software, and to permit persons .\" to whom the Software is furnished to do so, provided that the above .\" copyright notice(s) and this permission notice appear in all copies of .\" the Software and that both the above copyright notice(s) and this .\" permission notice appear in supporting documentation. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS .\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF .\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR .\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL .\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING .\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, .\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION .\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Except as contained in this notice, the name of a copyright holder .\" shall not be used in advertising or otherwise to promote the sale, use .\" or other dealings in this Software without prior written authorization .\" of the copyright holder. .TH gl_get_line @FUNC_MANEXT@ .SH NAME gl_get_line, new_GetLine, del_GetLine, gl_customize_completion, gl_change_terminal, gl_configure_getline, gl_load_history, gl_save_history, gl_group_history, gl_show_history, gl_watch_fd, gl_inactivity_timeout, gl_terminal_size, gl_set_term_size, gl_resize_history, gl_limit_history, gl_clear_history, gl_toggle_history, gl_lookup_history, gl_state_of_history, gl_range_of_history, gl_size_of_history, gl_echo_mode, gl_replace_prompt, gl_prompt_style, gl_ignore_signal, gl_trap_signal, gl_last_signal, gl_completion_action, gl_display_text, gl_return_status, gl_error_message, gl_catch_blocked, gl_list_signals, gl_bind_keyseq, gl_erase_terminal, gl_automatic_history, gl_append_history, gl_query_char, gl_read_char \- allow the user to compose an input line .SH SYNOPSIS .nf #include #include GetLine *new_GetLine(size_t linelen, size_t histlen); GetLine *del_GetLine(GetLine *gl); char *gl_get_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos); int gl_query_char(GetLine *gl, const char *prompt, char defchar); int gl_read_char(GetLine *gl); int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn); int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, const char *term); int gl_configure_getline(GetLine *gl, const char *app_string, const char *app_file, const char *user_file); int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin, const char *keyseq, const char *action); int gl_save_history(GetLine *gl, const char *filename, const char *comment, int max_lines); int gl_load_history(GetLine *gl, const char *filename, const char *comment); int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, GlFdEventFn *callback, void *data); int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *callback, void *data, unsigned long sec, unsigned long nsec); int gl_group_history(GetLine *gl, unsigned stream); int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, int max_lines); int gl_resize_history(GetLine *gl, size_t bufsize); void gl_limit_history(GetLine *gl, int max_lines); void gl_clear_history(GetLine *gl, int all_groups); void gl_toggle_history(GetLine *gl, int enable); GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline); int gl_set_term_size(GetLine *gl, int ncolumn, int nline); int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *hline); void gl_state_of_history(GetLine *gl, GlHistoryState *state); void gl_range_of_history(GetLine *gl, GlHistoryRange *range); void gl_size_of_history(GetLine *gl, GlHistorySize *size); void gl_echo_mode(GetLine *gl, int enable); void gl_replace_prompt(GetLine *gl, const char *prompt); void gl_prompt_style(GetLine *gl, GlPromptStyle style); int gl_ignore_signal(GetLine *gl, int signo); int gl_trap_signal(GetLine *gl, int signo, unsigned flags, GlAfterSignal after, int errno_value); int gl_last_signal(GetLine *gl); int gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn, int list_only, const char *name, const char *keyseq); int gl_register_action(GetLine *gl, void *data, GlActionFn *fn, const char *name, const char *keyseq); int gl_display_text(GetLine *gl, int indentation, const char *prefix, const char *suffix, int fill_char, int def_width, int start, const char *string); GlReturnStatus gl_return_status(GetLine *gl); const char *gl_error_message(GetLine *gl, char *buff, size_t n); void gl_catch_blocked(GetLine *gl); int gl_list_signals(GetLine *gl, sigset_t *set); int gl_append_history(GetLine *gl, const char *line); int gl_automatic_history(GetLine *gl, int enable); .fi .SH DESCRIPTION The \f3gl_get_line()\f1 function is part of the tecla library (see the \f3libtecla(@LIBR_MANEXT@)\f1 man page). If the user is typing at a terminal, each call prompts them for an line of input, then provides interactive editing facilities, similar to those of the unix \f3tcsh\f1 shell. In addition to simple command-line editing, it supports recall of previously entered command lines, TAB completion of file names, and in-line wild-card expansion of filenames. Documentation of both the user-level command-line editing features and all user configuration options, can be found in the \f3tecla(@MISC_MANEXT@)\f1 man page. This man page concerns itself with documentation for programmers interested in using this library in their application. .sp .SH AN EXAMPLE The following shows a complete example of how to use the \f3gl_get_line()\f1 function to get input from the user: .nf #include #include #include int main(int argc, char *argv[]) { char *line; /* The line that the user typed */ GetLine *gl; /* The gl_get_line() resource object */ setlocale(LC_CTYPE, ""); /* Adopt the user's choice */ /* of character set. */ gl = new_GetLine(1024, 2048); if(!gl) return 1; while((line=gl_get_line(gl, "$ ", NULL, -1)) != NULL && strcmp(line, "exit\\n") != 0) printf("You typed: %s\\n", line); gl = del_GetLine(gl); return 0; } .fi .sp In the example, first the resources needed by the \f3gl_get_line()\f1 function are created by calling \f3new_GetLine()\f1. This allocates the memory used in subsequent calls to the \f3gl_get_line()\f1 function, including the history buffer for recording previously entered lines. Then one or more lines are read from the user, until either an error occurs, or the user types \f3exit\f1. Then finally the resources that were allocated by \f3new_GetLine()\f1, are returned to the system by calling \f3del_GetLine()\f1. Note the use of the \f3NULL\f1 return value of \f3del_GetLine()\f1 to make \f3gl\f1 \f3NULL\f1. This is a safety precaution. If the program subsequently attempts to pass \f3gl\f1 to \f3gl_get_line()\f1, said function will complain, and return an error, instead of attempting to use the deleted resource object. .sp .SH THE FUNCTIONS USED IN THE EXAMPLE The descriptions of the functions used in the example are as follows: .sp .nf GetLine *new_GetLine(size_t linelen, size_t histlen) .fi .sp This function creates the resources used by the \f3gl_get_line()\f1 function and returns an opaque pointer to the object that contains them. The maximum length of an input line is specified via the \f3linelen\f1 argument, and the number of bytes to allocate for storing history lines is set by the \f3histlen\f1 argument. History lines are stored back-to-back in a single buffer of this size. Note that this means that the number of history lines that can be stored at any given time, depends on the lengths of the individual lines. If you want to place an upper limit on the number of lines that can be stored, see the \f3gl_limit_history()\f1 function described later. If you don't want history at all, specify \f3histlen\f1 as zero, and no history buffer will be allocated. .sp On error, a message is printed to \f3stderr\f1 and \f3NULL\f1 is returned. .sp .nf GetLine *del_GetLine(GetLine *gl) .fi .sp This function deletes the resources that were returned by a previous call to \f3new_GetLine()\f1. It always returns \f3NULL\f1 (ie a deleted object). It does nothing if the \f3gl\f1 argument is \f3NULL\f1. .sp .nf char *gl_get_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos); .fi .sp The \f3gl_get_line()\f1 function can be called any number of times to read input from the user. The \f3gl\f1 argument must have been previously returned by a call to \f3new_GetLine()\f1. The \f3prompt\f1 argument should be a normal \f3NUL\f1 terminated string, specifying the prompt to present the user with. By default prompts are displayed literally, but if enabled with the \f3gl_prompt_style()\f1 function (see later), prompts can contain directives to do underlining, switch to and from bold fonts, or turn highlighting on and off. If you want to specify the initial contents of the line, for the user to edit, pass the desired string via the \f3start_line\f1 argument. You can then specify which character of this line the cursor is initially positioned over, using the \f3start_pos\f1 argument. This should be -1 if you want the cursor to follow the last character of the start line. If you don't want to preload the line in this manner, send \f3start_line\f1 as \f3NULL\f1, and set \f3start_pos\f1 to -1. Note that the line pointer returned by one call to \f3gl_get_line()\f1 can be passed back to the next call to \f3gl_get_line()\f1 via the \f3start_line\f1. This allows the application to take the last entered line, and if it contains an error, to then present it back to the user for re-editing, with the cursor initially positioned where the error was encountered. The \f3gl_get_line()\f1 function returns a pointer to the line entered by the user, or \f3NULL\f1 on error or at the end of the input. The returned pointer is part of the specified \f3gl\f1 resource object, and thus should not be free'd by the caller, or assumed to be unchanging from one call to the next. When reading from a user at a terminal, there will always be a newline character at the end of the returned line. When standard input is being taken from a pipe or a file, there will similarly be a newline unless the input line was too long to store in the internal buffer. In the latter case you should call \f3gl_get_line()\f1 again to read the rest of the line. Note that this behavior makes \f3gl_get_line()\f1 similar to \f3fgets()\f1. In fact when \f3stdin\f1 isn't connected to a terminal,\f3gl_get_line()\f1 just calls \f3fgets()\f1. .SH THE RETURN STATUS OF GL_GET_LINE As described above, the \f3gl_get_line()\f1 function has two possible return values; a pointer to the completed input line, or \f3NULL\f1. Extra information about what caused \f3gl_get_line()\f1 to return is available both by inspecting \f3errno\f1, and by calling the \f3gl_return_status()\f1 function. .sp .nf GlReturnStatus gl_return_status(GetLine *gl); .fi .sp The following are the possible enumerated values that this function returns. .sp .nf GLR_NEWLINE - The last call to \f3gl_get_line()\f1 successfully returned a completed input line. GLR_BLOCKED - \f3gl_get_line()\f1 was in non-blocking server mode, and returned early to avoid blocking the process while waiting for terminal I/O. The \f3gl_pending_io()\f1 function can be used to see what type of I/O \f3gl_get_line()\f1 was waiting for. (see the \f3gl_io_mode(@FUNC_MANEXT@)\f1 man page for details). GLR_SIGNAL - A signal was caught by \f3gl_get_line()\f1 that had an after-signal disposition of \f3GLS_ABORT\f1 (See \f3gl_trap_signal()\f1). GLR_TIMEOUT - The inactivity timer expired while \f3gl_get_line()\f1 was waiting for input, and the timeout callback function returned \f3GLTO_ABORT\f1. See \f3gl_inactivity_timeout()\f1 for information about timeouts. GLR_FDABORT - An application I/O callack returned \f3GLFD_ABORT\f1 (see \f3gl_watch_fd()\f1). GLR_EOF - End of file reached. This can happen when input is coming from a file or a pipe, instead of the terminal. It also occurs if the user invokes the \f3list-or-eof\f1 or \f3del-char-or-list-or-eof\f1 actions at the start of a new line. GLR_ERROR - An unexpected error caused \f3gl_get_line()\f1 to abort (consult \f3errno\f1 and/or \f3gl_error_message()\f1 for details. .fi .sp When \f3gl_return_status()\f1 returns \f3GLR_ERROR\f1, and the value of \f3errno\f1 isn't sufficient to explain what happened, you can use the \f3gl_error_message()\f1 function to request a description of the last error that occurred. .sp .nf const char *gl_error_message(GetLine *gl, char *buff, size_t n); .fi .sp The return value is a pointer to the message that occurred. If the \f3buff\f1 argument is \f3NULL\f1, this will be a pointer to a buffer within \f3gl\f1, who's value will probably change on the next call to any function associated with \f3gl_get_line()\f1. Otherwise, if a non-\f3NULL\f1 \f3buff\f1 argument is provided, the error message, including a \f3'\\0'\f1 terminator, will be written within the first \f3n\f1 elements of this buffer, and the return value will be a pointer to the first element of this buffer. If the message won't fit in the provided buffer, it will be truncated to fit. .SH OPTIONAL PROMPT FORMATTING Whereas by default the prompt string that you specify is displayed literally, without any special interpretation of the characters within it, the \f3gl_prompt_style()\f1 function can be used to enable optional formatting directives within the prompt. .sp .nf void gl_prompt_style(GetLine *gl, GlPromptStyle style); .fi .sp The \f3style\f1 argument, which specifies the formatting style, can take any of the following values: .sp .nf GL_FORMAT_PROMPT - In this style, the formatting directives described below, when included in prompt strings, are interpreted as follows: %B - Display subsequent characters with a bold font. %b - Stop displaying characters with the bold font. %F - Make subsequent characters flash. %f - Turn off flashing characters. %U - Underline subsequent characters. %u - Stop underlining characters. %P - Switch to a pale (half brightness) font. %p - Stop using the pale font. %S - Highlight subsequent characters (also known as standout mode). %s - Stop highlighting characters. %V - Turn on reverse video. %v - Turn off reverse video. %% - Display a single % character. For example, in this mode, a prompt string like \f3"%UOK%u$ "\f1 would display the prompt \f3"OK$ "\f1, but with the \f3OK\f1 part underlined. Note that although a pair of characters that starts with a % character, but doesn't match any of the above directives is displayed literally, if a new directive is subsequently introduced which does match, the displayed prompt will change, so it is better to always use %% to display a literal %. Also note that not all terminals support all of these text attributes, and that some substitute a different attribute for missing ones. GL_LITERAL_PROMPT - In this style, the prompt string is printed literally. This is the default style. .fi .SH ALTERNATE CONFIGURATION SOURCES As mentioned above, by default users have the option of configuring the behavior of \f3gl_get_line()\f1 via a configuration file called \f3\&.teclarc\f1 in their home directories. The fact that all applications share this same configuration file is both an advantage and a disadvantage. In most cases it is an advantage, since it encourages uniformity, and frees the user from having to configure each application separately. In some applications, however, this single means of configuration is a problem. This is particularly true of embedded software, where there's no filesystem to read a configuration file from, and also in applications where a radically different choice of keybindings is needed to emulate a legacy keyboard interface. To cater for such cases, the following function allows the application to control where configuration information is read from. .sp .nf int gl_configure_getline(GetLine *gl, const char *app_string, const char *app_file, const char *user_file); .fi .sp It allows the configuration commands that would normally be read from a user's \f3~/.teclarc\f1 file, to be read from any or none of, a string, an application specific configuration file, and/or a user-specific configuration file. If this function is called before the first call to \f3gl_get_line()\f1, the default behavior of reading \f3~/.teclarc\f1 on the first call to \f3gl_get_line()\f1 is disabled, so all configuration must be achieved using the configuration sources specified with this function. If \f3app_string != NULL\f1, then it is interpreted as a string containing one or more configuration commands, separated from each other in the string by embedded newline characters. If \f3app_file != NULL\f1 then it is interpreted as the full pathname of an application-specific configuration file. If \f3user_file != NULL\f1 then it is interpreted as the full pathname of a user-specific configuration file, such as \f3~/.teclarc\f1. For example, in the following call, .sp .nf gl_configure_getline(gl, "edit-mode vi \\n nobeep", "/usr/share/myapp/teclarc", "~/.teclarc"); .fi .sp the \f3app_string\f1 argument causes the calling application to start in vi edit-mode, instead of the default emacs mode, and turns off the use of the terminal bell by the library. It then attempts to read system-wide configuration commands from an optional file called \f3/usr/share/myapp/teclarc\f1, then finally reads user-specific configuration commands from an optional \f3\&.teclarc\f1 file in the user's home directory. Note that the arguments are listed in ascending order of priority, with the contents of \f3app_string\f1 being potentially overriden by commands in \f3app_file\f1, and commands in \f3app_file\f1 potentially being overriden by commands in \f3user_file\f1. .sp You can call this function as many times as needed, the results being cumulative, but note that copies of any filenames specified via the \f3app_file\f1 and \f3user_file\f1 arguments are recorded internally for subsequent use by the \f3read-init-files\f1 key-binding function, so if you plan to call this function multiple times, be sure that the last call specifies the filenames that you want re-read when the user requests that the configuration files be re-read. .sp Individual key sequences can also be bound and unbound using the \f3gl_bind_keyseq()\f1 function. .sp .nf int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin, const char *keyseq, const char *action); .fi .sp The \f3origin\f1 argument specifies the priority of the binding, according to who it is being established for, and must be one of the following two values. .sp .nf GL_USER_KEY - The user requested this key-binding. GL_APP_KEY - This is a default binding set by the application. .fi .sp When both user and application bindings for a given key-sequence have been specified, the user binding takes precedence. The application's binding is subsequently reinstated if the user's binding is later unbound via either another to this function, or a call to \f3gl_configure_getline()\f1. The \f3keyseq\f1 argument specifies the key-sequence to be bound or unbound, and is expressed in the same way as in a \f3~/.teclarc\f1 configuration file. The \f3action\f1 argument must either be a string containing the name of the action to bind the key-sequence to, or it must be \f3NULL\f1 or "" to unbind the key-sequence. .SH CUSTOMIZED WORD COMPLETION If in your application, you would like to have TAB completion complete other things in addition to or instead of filenames, you can arrange this by registering an alternate completion callback function, via a call to the \f3gl_customize_completion()\f1 function. .sp .nf int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn); .fi .sp The \f3data\f1 argument provides a way for your application to pass arbitrary, application-specific information to the callback function. This is passed to the callback every time that it is called. It might for example, point to the symbol table from which possible completions are to be sought. The \f3match_fn\f1 argument specifies the callback function to be called. The \f3CplMatchFn\f1 function type is defined in \f3libtecla.h\f1, as is a \f3CPL_MATCH_FN()\f1 macro that you can use to declare and prototype callback functions. The declaration and responsibilities of callback functions are described in depth in the \f1cpl_complete_word(@FUNC_MANEXT@)\f1 man page. .sp In brief, the callback function is responsible for looking backwards in the input line, back from the point at which the user pressed TAB, to find the start of the word being completed. It then must lookup possible completions of this word, and record them one by one in the \f3WordCompletion\f1 object that is passed to it as an argument, by calling the \f3cpl_add_completion()\f1 function. If the callback function wishes to provide filename completion in addition to its own specific completions, it has the option of itself calling the builtin file-name completion callback. This also, is documented in the \f3cpl_complete_word(@FUNC_MANEXT@)\f1 man page. .sp Note that if you would like \f3gl_get_line()\f1 to return the current input line when a successful completion is been made, you can arrange this when you call \f3cpl_add_completion()\f1, by making the last character of the continuation suffix a newline character. If you do this, the input line will be updated to display the completion, together with any contiuation suffix up to the newline character, then \f3gl_get_line()\f1 will return this input line. .sp If, for some reason, your callback function needs to write something to the terminal, it must call \f3gl_normal_io()\f1 before doing so. This will start a new line after the input line that is currently being edited, reinstate normal terminal I/O, and tell \f3gl_get_line()\f1 that the input line will need to be redrawn when the callback returns. .SH ADDING COMPLETION ACTIONS In the previous section the ability to customize the behavior of the only default completion action, \f3complete-word\f1, was described. In this section the ability to install additional action functions, so that different types of word completion can be bound to different key-sequences, is described. This is achieved by using the \f3gl_completion_action()\f1 function. .sp .nf int gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn, int list_only, const char *name, const char *keyseq); .fi .sp The \f3data\f1 and \f3match_fn\f1 arguments are as described in the \f3cpl_complete_word\f1 man page, and specify the callback function that should be invoked to identify possible completions. The \f3list_only\f1 argument determines whether the action that is being defined should attempt to complete the word as far as possible in the input line before displaying any possible ambiguous completions, or whether it should simply display the list of possible completions without touching the input line. The former option is selected by specifying a value of \f30\f1, and the latter by specifying a value of \f31\f1. The \f3name\f1 argument specifies the name by which configuration files and future invokations of this function should refer to the action. This must either be the name of an existing completion action to be changed, or be a new unused name for a new action. Finally, the \f3keyseq\f1 argument specifies the default key-sequence to bind the action to. If this is \f3NULL\f1, no new keysequence will be bound to the action. Beware that in order for the user to be able to change the key-sequence that is bound to actions that are installed in this manner, when you call \f3gl_completion_action()\f1 to install a given action for the first time, you should do this between calling \f3new_GetLine()\f1 and the first call to \f3gl_get_line()\f1. Otherwise, when the user's configuration file is read on the first call to \f3gl_get_line()\f1, the name of the your additional action won't be known, and any reference to it in the configuration file will generate an error. As discussed for \f3gl_customize_completion()\f1, if your callback function, for some reason, needs to write anything to the terminal, it must call \f3gl_normal_io()\f1 before doing so. .SH DEFINING CUSTOM ACTIONS Although the built-in key-binding actions are sufficient for the needs of most applications, occasionally a specialized application may need to define one or more custom actions, bound to application-specific key-sequences. For example, a sales application would benefit from having a key-sequence that displayed the part name that corresponded to a part number preceding the cursor. Such a feature is clearly beyond the scope of the built-in action functions. So for such special cases, the \f3gl_register_action()\f1 function is provided. .sp .nf int gl_register_action(GetLine *gl, void *data, GlActionFn *fn, const char *name, const char *keyseq); .fi .sp This function lets the application register an external function, \f3fn\f1, that will thereafter be called whenever either the specified key-sequence, \f3keyseq\f1, is entered by the user, or the user enters any other key-sequence that the user subsequently binds to the specified action name, \f3name\f1, in their configuration file. The \f3data\f1 argument can be a pointer to anything that the application wishes to have passed to the action function, \f3fn\f1, whenever that function is invoked. The action function, \f3fn\f1, should be declared using the following macro, which is defined in \f3libtecla.h\f1. .sp .nf #define GL_ACTION_FN(fn) GlAfterAction (fn)(GetLine *gl, \\ void *data, int count, size_t curpos, \\ const char *line) .fi .sp The \f3gl\f1 and \f3data\f1 arguments are those that were previously passed to \f3gl_register_action()\f1 when the action function was registered. The \f3count\f1 argument is a numeric argument which the user has the option of entering using the \f3digit-argument\f1 action, before invoking the action. If the user doesn't enter a number, then the \f3count\f1 argument is set to 1. Nominally this argument is interpreted as a repeat count, meaning that the action should be repeated that many times. In practice however, for some actions a repeat count makes little sense. In such cases, actions can either simply ignore the \f3count\f1 argument, or use its value for a different purpose. A copy of the current input line is passed in the read-only \f3line\f1 argument. The current cursor position within this string is given by the index contained in the \f3curpos\f1 argument. Note that direct manipulation of the input line and the cursor position is not permitted. This is because the rules dicated by various modes, such as vi mode versus emacs mode, no-echo mode, and insert mode versus overstrike mode etc, make it too complex for an application writer to write a conforming editing action, as well as constrain future changes to the internals of \f3gl_get_line()\f1. A potential solution to this dilema would be to allow the action function to edit the line using the existing editing actions. This is currently under consideration. If the action function wishes to write text to the terminal, without this getting mixed up with the displayed text of the input line, or read from the terminal without having to handle raw terminal I/O, then before doing either of these operations, it must temporarily suspend line editing by calling the \f3gl_normal_io()\f1 function. This function flushes any pending output to the terminal, moves the cursor to the start of the line that follows the last terminal line of the input line, then restores the terminal to a state that is suitable for use with the C stdio facilities. The latter includes such things as restoring the normal mapping of \f3\\n\f1 to \f3\\r\\n\f1, and, when in server mode, restoring the normal blocking form of terminal I/O. Having called this function, the action function can read from and write to the terminal without the fear of creating a mess. It isn't necessary for the action function to restore the original editing environment before it returns. This is done automatically by \f3gl_get_line()\f1 after the action function returns. The following is a simple example of an action function which writes the sentence "Hello world" on a new terminal line after the line being edited. When this function returns, the input line is redrawn on the line that follows the "Hello world" line, and line editing resumes. .sp .nf static GL_ACTION_FN(say_hello_fn) { if(gl_normal_io(gl)) /* Temporarily suspend editing */ return GLA_ABORT; printf("Hello world\\n"); return GLA_CONTINUE; } .fi .sp Action functions must return one of the following values, to tell \f3gl_get_line()\f1 how to procede. .sp .nf GLA_ABORT - Cause gl_get_line() to return NULL. GLA_RETURN - Cause gl_get_line() to return the completed input line. GLA_CONTINUE - Resume command-line editing. .fi .sp Note that the \f3name\f1 argument of \f3gl_register_action()\f1 specifies the name by which a user can refer to the action in their configuration file. This allows them to re-bind the action to an alternate key-seqeunce. In order for this to work, it is necessary to call \f3gl_register_action()\f1 between calling \f3new_GetLine()\f1 and the first call to \f3gl_get_line()\f1. .SH HISTORY FILES To save the contents of the history buffer before quitting your application, and subsequently restore them when you next start the application, the following functions are provided. .sp .nf int gl_save_history(GetLine *gl, const char *filename, const char *comment, int max_lines); int gl_load_history(GetLine *gl, const char *filename, const char *comment); .fi .sp The \f3filename\f1 argument specifies the name to give the history file when saving, or the name of an existing history file, when loading. This may contain home-directory and environment variable expressions, such as "~/.myapp_history" or "$HOME/.myapp_history". .sp Along with each history line, extra information about it, such as when it was entered by the user, and what its nesting level is, is recorded as a comment preceding the line in the history file. Writing this as a comment allows the history file to double as a command file, just in case you wish to replay a whole session using it. Since comment prefixes differ in different languages, the \f3comment\f1 argument is provided for specifying the comment prefix. For example, if your application were a unix shell, such as the bourne shell, you would specify "#" here. Whatever you choose for the comment character, you must specify the same prefix to \f3gl_load_history()\f1 that you used when you called \f3gl_save_history()\f1 to write the history file. .sp The \f3max_lines\f1 must be either -1 to specify that all lines in the history list be saved, or a positive number specifying a ceiling on how many of the most recent lines should be saved. .sp Both fuctions return non-zero on error, after writing an error message to stderr. Note that \f3gl_load_history()\f1 does not consider the non-existence of a file to be an error. .SH MULTIPLE HISTORY LISTS If your application uses a single \f3GetLine\f1 object for entering many different types of input lines, you may wish \f3gl_get_line()\f1 to distinguish the different types of lines in the history list, and only recall lines that match the current type of line. To support this requirement, \f3gl_get_line()\f1 marks lines being recorded in the history list with an integer identifier chosen by the application. Initially this identifier is set to \f10\f3 by \f3new_GetLine()\f1, but it can be changed subsequently by calling \f3gl_group_history()\f1. .sp .nf int gl_group_history(GetLine *gl, unsigned id); .fi .sp The integer identifier \f3id\f1 can be any number chosen by the application, but note that \f3gl_save_history()\f1 and \f3gl_load_history()\f1 preserve the association between identifiers and historical input lines between program invokations, so you should choose fixed identifiers for the different types of input line used by your application. .sp Whenever \f3gl_get_line()\f1 appends a new input line to the history list, the current history identifier is recorded with it, and when it is asked to recall a historical input line, it only recalls lines that are marked with the current identifier. .SH DISPLAYING HISTORY The history list can be displayed by calling \f3gl_show_history()\f1. .sp .nf int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, int max_lines); .fi .sp This displays the current contents of the history list to the stdio output stream \f3fp\f1. If the \f3max_lines\f1 argument is greater than or equal to zero, then no more than this number of the most recent lines will be displayed. If the \f3all_groups\f1 argument is non-zero, lines from all history groups are displayed. Otherwise just those of the currently selected history group are displayed. The format string argument, \f3fmt\f1, determines how the line is displayed. This can contain arbitrary characters which are written verbatim, interleaved with any of the following format directives: .nf %D - The date on which the line was originally entered, formatted like 2001-11-20. %T - The time of day when the line was entered, formatted like 23:59:59. %N - The sequential entry number of the line in the history buffer. %G - The number of the history group which the line belongs to. %% - A literal % character. %H - The history line itself. .fi Thus a format string like \f3"%D %T %H\n"\f1 would output something like: .nf 2001-11-20 10:23:34 Hello world .fi Note the inclusion of an explicit newline character in the format string. .SH LOOKING UP HISTORY The \f3gl_lookup_history()\f1 function allows the calling application to look up lines in the history list. .sp .nf typedef struct { const char *line; /* The requested historical */ /* line. */ unsigned group; /* The history group to which */ /* the line belongs. */ time_t timestamp; /* The date and time at which */ /* the line was originally */ /* entered. */ } GlHistoryLine; int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *hline); .fi .sp The \f3id\f1 argument indicates which line to look up, where the first line that was entered in the history list after \f3new_GetLine()\f1 was called, is denoted by 0, and subsequently entered lines are denoted with successively higher numbers. Note that the range of lines currently preserved in the history list can be queried by calling the \f3gl_range_of_history()\f1 function, described later. If the requested line is in the history list, the details of the line are recorded in the variable pointed to by the \f3hline\f1 argument, and \f31\f1 is returned. Otherwise \f30\f1 is returned, and the variable pointed to by \f3hline\f1 is left unchanged. .sp Beware that the string returned in \f3hline->line\f1 is part of the history buffer, so it must not be modified by the caller, and will be recycled on the next call to any function that takes \f3gl\f1 as its argument. Therefore you should make a private copy of this string if you need to keep it around. .SH MANUAL HISTORY ARCHIVAL By default, whenever a line is entered by the user, it is automatically appended to the history list, just before \f3gl_get_line()\f1 returns the line to the caller. This is convenient for the majority of applications, but there are also applications that need finer grained control over what gets added to the history list. In such cases, the automatic addition of entered lines to the history list can be turned off by calling the \f3gl_automatic_history()\f1 function. .sp .nf int gl_automatic_history(GetLine *gl, int enable); .fi .sp If this function is called with its \f3enable\f1 argument set to \f30\f1, \f3gl_get_line()\f1 won't automatically archive subsequently entered lines. Automatic archiving can be reenabled at a later time, by calling this function again, with its \f3enable\f1 argument set to 1. While automatic history archiving is disabled, the calling application can use the \f3gl_append_history()\f1 to append lines to the history list as needed. .sp .nf int gl_append_history(GetLine *gl, const char *line); .fi .sp The \f3line\f1 argument specifies the line to be added to the history list. This must be a normal \f3'\0'\f1 terminated string. If this string contains any newline characters, the line that gets archived in the history list will be terminated by the first of these. Otherwise it will be terminated by the \f3'\0'\f1 terminator. If the line is longer than the maximum input line length, that was specified when \f3new_GetLine()\f1 was called, when the line is recalled, it will get truncated to the actual \f3gl_get_line()\f1 line length. If successful, \f3gl_append_history()\f1 returns 0. Otherwise it returns non-zero, and sets \f3errno\f1 to one of the following values. .sp .nf EINVAL - One of the arguments passed to gl_append_history() was NULL. ENOMEM - The specified line was longer than the allocated size of the history buffer (as specified when new_GetLine() was called), so it couldn't be archived. .fi .sp A textual description of the error can optionally be obtained by calling \f3gl_error_message()\f1. Note that after such an error, the history list remains in a valid state to receive new history lines, so there is little harm in simply ignoring the return status of \f3gl_append_history()\f1. .SH MISCELLANEOUS HISTORY CONFIGURATION If you wish to change the size of the history buffer that was originally specified in the call to \f3new_GetLine()\f1, you can do so with the \f3gl_resize_history()\f1 function. .sp .nf int gl_resize_history(GetLine *gl, size_t histlen); .fi .sp The \f3histlen\f1 argument specifies the new size in bytes, and if you specify this as 0, the buffer will be deleted. .sp As mentioned in the discussion of \f3new_GetLine()\f1, the number of lines that can be stored in the history buffer, depends on the lengths of the individual lines. For example, a 1000 byte buffer could equally store 10 lines of average length 100 bytes, or 2 lines of average length 50 bytes. Although the buffer is never expanded when new lines are added, a list of pointers into the buffer does get expanded when needed to accomodate the number of lines currently stored in the buffer. To place an upper limit on the number of lines in the buffer, and thus a ceiling on the amount of memory used in this list, you can call the \f3gl_limit_history()\f1 function. .sp .nf void gl_limit_history(GetLine *gl, int max_lines); .fi .sp The \f3max_lines\f1 should either be a positive number \f3>= 0\f1, specifying an upper limit on the number of lines in the buffer, or be \f3-1\f1 to cancel any previously specified limit. When a limit is in effect, only the \f3max_lines\f1 most recently appended lines are kept in the buffer. Older lines are discarded. .sp To discard lines from the history buffer, use the \f3gl_clear_history()\f1 function. .sp .nf void gl_clear_history(GetLine *gl, int all_groups); .fi .sp The \f3all_groups\f1 argument tells the function whether to delete just the lines associated with the current history group (see \f3gl_group_history()\f1), or all historical lines in the buffer. .sp The \f3gl_toggle_history()\f1 function allows you to toggle history on and off without losing the current contents of the history list. .sp .nf void gl_toggle_history(GetLine *gl, int enable); .fi .sp Setting the \f3enable\f1 argument to 0 turns off the history mechanism, and setting it to 1 turns it back on. When history is turned off, no new lines will be added to the history list, and history lookup key-bindings will act as though there is nothing in the history buffer. .SH QUERYING HISTORY INFORMATION The configured state of the history list can be queried with the \f3gl_history_state()\f1 function. .sp .nf typedef struct { int enabled; /* True if history is enabled */ unsigned group; /* The current history group */ int max_lines; /* The current upper limit on the */ /* number of lines in the history */ /* list, or -1 if unlimited. */ } GlHistoryState; void gl_state_of_history(GetLine *gl, GlHistoryState *state); .fi .sp On return, the status information is recorded in the variable pointed to by the \f3state\f1 argument. .sp The \f3gl_range_of_history()\f1 function returns the number and range of lines in the history list. .sp .nf typedef struct { unsigned long oldest; /* The sequential entry number */ /* of the oldest line in the */ /* history list. */ unsigned long newest; /* The sequential entry number */ /* of the newest line in the */ /* history list. */ int nlines; /* The number of lines in the */ /* history list. */ } GlHistoryRange; void gl_range_of_history(GetLine *gl, GlHistoryRange *range); .fi .sp The return values are recorded in the variable pointed to by the \f3range\f1 argument. If the \f3nlines\f1 member of this structure is greater than zero, then the \f3oldest\f1 and \f3newest\f1 members report the range of lines in the list, and \f3newest=oldest+nlines-1\f1. Otherwise they are both zero. .sp The \f3gl_size_of_history()\f1 function returns the total size of the history buffer and the amount of the buffer that is currently occupied. .sp .nf typedef struct { size_t size; /* The size of the history buffer */ /* (bytes). */ size_t used; /* The number of bytes of the */ /* history buffer that are */ /* currently occupied. */ } GlHistorySize; void gl_size_of_history(GetLine *gl, GlHistorySize *size); .fi .sp On return, the size information is recorded in the variable pointed to by the \f3size\f1 argument. .SH CHANGING TERMINALS The \f3new_GetLine()\f1 constructor function assumes that input is to be read from \f3stdin\f1, and output written to \f3stdout\f1. The following function allows you to switch to different input and output streams. .sp .nf int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, const char *term); .fi .sp The \f3gl\f1 argument is the object that was returned by \f3new_GetLine()\f1. The \f3input_fp\f1 argument specifies the stream to read from, and \f3output_fp\f1 specifies the stream to be written to. Only if both of these refer to a terminal, will interactive terminal input be enabled. Otherwise \f3gl_get_line()\f1 will simply call \f3fgets()\f1 to read command input. If both streams refer to a terminal, then they must refer to the same terminal, and the type of this terminal must be specified via the \f3term\f1 argument. The value of the \f3term\f1 argument is looked up in the terminal information database (terminfo or termcap), in order to determine which special control sequences are needed to control various aspects of the terminal. \f3new_GetLine()\f1 for example, passes the return value of \f3getenv("TERM")\f1 in this argument. Note that if one or both of \f3input_fp\f1 and \f3output_fp\f1 don't refer to a terminal, then it is legal to pass \f3NULL\f1 instead of a terminal type. .sp Note that if you want to pass file descriptors to \f3gl_change_terminal()\f1, you can do this by creating stdio stream wrappers using the POSIX \f3fdopen()\f1 function. .SH EXTERNAL EVENT HANDLING By default, \f3gl_get_line()\f1 doesn't return until either a complete input line has been entered by the user, or an error occurs. In programs that need to watch for I/O from other sources than the terminal, there are two options. .sp .nf 1. Use the functions described in the \f3gl_io_mode(@FUNC_MANEXT@)\f1 man page to switch \f3gl_get_line()\f1 into non-blocking server mode. In this mode, \f3gl_get_line()\f1 becomes a non-blocking, incremental line-editing function that can safely be called from an external event loop. Although this is a very versatile method, it involves taking on some responsibilities that are normally performed behind the scenes by \f3gl_get_line()\f1. 2. While \f3gl_get_line()\f1 is waiting for keyboard input from the user, you can ask it to also watch for activity on arbitrary file descriptors, such as network sockets, pipes etc, and have it call functions of your choosing when activity is seen. This works on any system that has the \f3select()\f1 system call, which is most, if not all flavors of unix. .fi .sp Registering a file descriptor to be watched by \f3gl_get_line()\f1 involves calling the \f3gl_watch_fd()\f1 function. .sp .nf int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, GlFdEventFn *callback, void *data); .fi .sp If this returns non-zero, then it means that either your arguments are invalid, or that this facility isn't supported on the host system. .sp The \f3fd\f1 argument is the file descriptor to be watched. The \f3event\f1 argument specifies what type of activity is of interest, chosen from the following enumerated values: .sp .nf GLFD_READ - Watch for the arrival of data to be read. GLFD_WRITE - Watch for the ability to write to the file descriptor without blocking. GLFD_URGENT - Watch for the arrival of urgent out-of-band data on the file descriptor. .fi .sp The \f3callback\f1 argument is the function to call when the selected activity is seen. It should be defined with the following macro, which is defined in libtecla.h. .sp .nf #define GL_FD_EVENT_FN(fn) GlFdStatus (fn)(GetLine *gl, \\ void *data, int fd, \\ GlFdEvent event) .fi .sp The \f3data\f1 argument of the \f3gl_watch_fd()\f1 function is passed to the callback function for its own use, and can point to anything you like, including \f3NULL\f1. The file descriptor and the event argument are also passed to the callback function, and this potentially allows the same callback function to be registered to more than one type of event and/or more than one file descriptor. The return value of the callback function should be one of the following values. .sp .nf GLFD_ABORT - Tell gl_get_line() to abort. When this happens, \f3gl_get_line()\f1 returns \f3NULL\f1, and a following call to \f3gl_return_status()\f1 will return \f3GLR_FDABORT\f1. Note that if the application needs \f3errno\f1 always to have a meaningful value when \f3gl_get_line()\f1 returns \f3NULL\f1, the callback function should set \f3errno\f1 appropriately. GLFD_REFRESH - Redraw the input line then continue waiting for input. Return this if your callback wrote to the terminal. GLFD_CONTINUE - Continue to wait for input, without redrawing the line. .fi .sp Note that before calling the callback, \f3gl_get_line()\f1 blocks most signals, and leaves its own signal handlers installed, so if you need to catch a particular signal you will need to both temporarily install your own signal handler, and unblock the signal. Be sure to re-block the signal (if it was originally blocked) and reinstate the original signal handler, if any, before returning. .sp If the callback function needs to read or write to the terminal, it should ideally first call \f3gl_normal_io(gl)\f1 to temporarily suspend line editing. This will restore the terminal to canonical, blocking-I/O, mode, and move the cursor to the start of a new terminal line. Later, when the callback returns, \f3gl_get_line()\f1 will notice that \f3gl_normal_io()\f1 was called, redisplay the input line and resume editing. Note that in this case the return values, \f3GLFD_REFRESH\f1 and \f3GLFD_CONTINUE\f1 are equivalent. .sp To support cases where the callback function calls a third-party function which occasionally and unpredictably writes to the terminal, the automatic conversion of \f3"\n"\f1 to \f3"\r\n"\f1 is re-enabled before the callback function is called. If the callack knows that the third-party function wrote to the terminal, it should then return the \f3GLFD_REFRESH\f1 return value, to tell \f3gl_get_line()\f1 to redisplay the input line. .sp To remove a callback function that you previously registered for a given file descriptor and event, simply call \f3gl_watch_fd()\f1 with the same file descriptor and \f3event\f1 arguments, but with a \f3callback\f1 argument of \f30\f1. The \f3data\f1 argument is ignored in this case. .SH SETTING AN INACTIVITY TIMEOUT On systems with the \f3select()\f1 system call, the \f3gl_inactivity_timeout()\f1 function can be used to set or cancel an inactivity timeout. Inactivity in this case refers both to keyboard input, and to I/O on any file descriptors registered by prior and subsequent calls to \f3gl_watch_fd()\f1. On oddball systems that don't have \f3select()\f1, this call has no effect. .sp .nf int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *callback, void *data, unsigned long sec, unsigned long nsec); .fi .sp The timeout is specified in the form of an integral number of seconds and an integral number of nanoseconds, via the \f3sec\f1 and \f3nsec\f1 arguments respectively. Subsequently, whenever no activity is seen for this time period, the function specified via the \f3callback\f1 argument is called. The \f3data\f1 argument of \f3gl_inactivity_timeout()\f1 is passed verbatim to this callback function whenever it is invoked, and can thus be used to pass arbitrary application-specific information to the callback. The following macro is provided in \f3libtecla.h\f1 for applications to use to declare and prototype timeout callback functions. .sp .nf #define GL_TIMEOUT_FN(fn) \\ GlAfterTimeout (fn)(GetLine *gl, void *data) .fi .sp On returning, the application's callback is expected to return one of the following enumerators to tell \f3gl_get_line()\f1 how to procede after the timeout has been handled by the callback. .sp .nf GLTO_ABORT - Tell gl_get_line() to abort. When this happens, \f3gl_get_line()\f1 will return \f3NULL\f1, and a following call to \f3gl_return_status()\f1 will return \f3GLR_TIMEOUT\f1. Note that if the application needs \f3errno\f1 always to have a meaningful value when \f3gl_get_line()\f1 returns \f3NULL\f1, the callback function should set \f3errno\f1 appropriately. GLTO_REFRESH - Redraw the input line, then continue waiting for input. You should return this value if your callback wrote to the terminal without having first called \f3gl_normal_io(gl)\f1. GLTO_CONTINUE - In normal blocking-I/O mode, continue to wait for input, without redrawing the user's input line. In non-blocking server I/O mode (see gl_io_mode(@FUNC_MANEXT@)), cause \f3gl_get_line()\f1 to act as though I/O blocked. This means that \f3gl_get_line()\f1 will immediately return \f3NULL\f1, and a following call to \f3gl_return_status()\f1 will return \f3GLR_BLOCKED\f1. .fi .sp Note that before calling the callback, \f3gl_get_line()\f1 blocks most signals, and leaves its own signal handlers installed, so if you need to catch a particular signal you will need to both temporarily install your own signal handler, and unblock the signal. Be sure to re-block the signal (if it was originally blocked) and reinstate the original signal handler, if any, before returning. .sp If the callback function needs to read or write to the terminal, it should ideally first call \f3gl_normal_io(gl)\f1 to temporarily suspend line editing. This will restore the terminal to canonical, blocking-I/O, mode, and move the cursor to the start of a new terminal line. Later, when the callback returns, \f3gl_get_line()\f1 will notice that \f3gl_normal_io()\f1 was called, redisplay the input line and resume editing. Note that in this case the return values, \f3GLTO_REFRESH\f1 and \f3GLTO_CONTINUE\f1 are equivalent. .sp To support cases where the callback function calls a third-party function which occasionally and unpredictably writes to the terminal, the automatic conversion of \f3"\n"\f1 to \f3"\r\n"\f1 is re-enabled before the callback function is called. If the callack knows that the third-party function wrote to the terminal, it should then return the \f3GLTO_REFRESH\f1 return value, to tell \f3gl_get_line()\f1 to redisplay the input line. .sp Note that although the timeout argument includes a nano-second component, few computer clocks presently have resolutions that are finer than a few milliseconds, so asking for less than a few milliseconds is equivalent to requesting zero seconds on a lot of systems. If this would be a problem, you should base your timeout selection on the actual resolution of the host clock (eg. by calling \f3sysconf(_SC_CLK_TCK)\f1). .sp To turn off timeouts, simply call \f3gl_inactivity_timeout()\f1 with a \f3callback\f1 argument of \f30\f1. The \f3data\f1 argument is ignored in this case. .SH SIGNAL HANDLING DEFAULTS By default, the \f3gl_get_line()\f1 function intercepts a number of signals. This is particularly important for signals which would by default terminate the process, since the terminal needs to be restored to a usable state before this happens. In this section, the signals that are trapped by default, and how \f3gl_get_line()\f1 responds to them, is described. Changing these defaults is the topic of the following section. .sp When the following subset of signals are caught, \f3gl_get_line()\f1 first restores the terminal settings and signal handling to how they were before \f3gl_get_line()\f1 was called, resends the signal, to allow the calling application's signal handlers to handle it, then if the process still exists, \f3gl_get_line()\f1 returns \f3NULL\f1 and sets \f3errno\f1 as specified below. .sp .nf SIGINT - This signal is generated both by the keyboard interrupt key (usually ^C), and the keyboard break key. errno=EINTR SIGHUP - This signal is generated when the controlling terminal exits. errno=ENOTTY SIGPIPE - This signal is generated when a program attempts to write to a pipe who's remote end isn't being read by any process. This can happen for example if you have called \f3gl_change_terminal()\f1 to redirect output to a pipe hidden under a pseudo terminal. errno=EPIPE SIGQUIT - This signal is generated by the keyboard quit key (usually ^\\). errno=EINTR SIGABRT - This signal is generated by the standard C, abort() function. By default it both terminates the process and generates a core dump. errno=EINTR SIGTERM - This is the default signal that the UN*X kill command sends to processes. errno=EINTR .fi .sp Note that in the case of all of the above signals, POSIX mandates that by default the process is terminated, with the addition of a core dump in the case of the \f3SIGQUIT\f1 signal. In other words, if the calling application doesn't override the default handler by supplying its own signal handler, receipt of the corresponding signal will terminate the application before \f3gl_get_line()\f1 returns. .sp If gl_get_line() aborts with errno set to EINTR, you can find out what signal caused it to abort, by calling the following function. .sp .nf int gl_last_signal(const GetLine *gl); .fi .sp This returns the numeric code (eg. \f3SIGINT\f1) of the last signal that was received during the most recent call to \f3gl_get_line()\f1, or \f3-1\f1 if no signals were received. .sp On systems that support it, when a SIGWINCH (window change) signal is received, \f3gl_get_line()\f1 queries the terminal to find out its new size, redraws the current input line to accomodate the new size, then returns to waiting for keyboard input from the user. Unlike other signals, this signal isn't resent to the application. .sp Finally, the following signals cause \f3gl_get_line()\f1 to first restore the terminal and signal environment to that which prevailed before \f3gl_get_line()\f1 was called, then resend the signal to the application. If the process still exists after the signal has been delivered, then \f3gl_get_line()\f1 then re-establishes its own signal handlers, switches the terminal back to raw mode, redisplays the input line, and goes back to awaiting terminal input from the user. .sp .nf SIGCONT - This signal is generated when a suspended process is resumed. SIGPOLL - On SVR4 systems, this signal notifies the process of an asynchronous I/O event. Note that under 4.3+BSD, SIGIO and SIGPOLL are the same. On other systems, SIGIO is ignored by default, so \f3gl_get_line()\f1 doesn't trap it by default. SIGPWR - This signal is generated when a power failure occurs (presumably when the system is on a UPS). SIGALRM - This signal is generated when a timer expires. SIGUSR1 - An application specific signal. SIGUSR2 - Another application specific signal. SIGVTALRM - This signal is generated when a virtual timer expires (see man setitimer(2)). SIGXCPU - This signal is generated when a process exceeds its soft CPU time limit. SIGXFSZ - This signal is generated when a process exceeds its soft file-size limit. SIGTSTP - This signal is generated by the terminal suspend key, which is usually ^Z, or the delayed terminal suspend key, which is usually ^Y. SIGTTIN - This signal is generated if the program attempts to read from the terminal while the program is running in the background. SIGTTOU - This signal is generated if the program attempts to write to the terminal while the program is running in the background. .fi .sp Obviously not all of the above signals are supported on all systems, so code to support them is conditionally compiled into the tecla library. .sp Note that if \f3SIGKILL\f1 or \f3SIGPOLL\f1, which by definition can't be caught, or any of the hardware generated exception signals, such as \f3SIGSEGV\f1, \f3SIGBUS\f1 and \f3SIGFPE\f1, are received and unhandled while \f3gl_get_line()\f1 has the terminal in raw mode, the program will be terminated without the terminal having been restored to a usable state. In practice, job-control shells usually reset the terminal settings when a process relinquishes the controlling terminal, so this is only a problem with older shells. .SH CUSTOMIZED SIGNAL HANDLING The previous section listed the signals that \f3gl_get_line()\f1 traps by default, and described how it responds to them. This section describes how to both add and remove signals from the list of trapped signals, and how to specify how \f3gl_get_line()\f1 should respond to a given signal. .sp If you don't need \f3gl_get_line()\f1 to do anything in response to a signal that it normally traps, you can tell to \f3gl_get_line()\f1 to ignore that signal by calling \f3gl_ignore_signal()\f1. .sp .nf int gl_ignore_signal(GetLine *gl, int signo); .fi .sp The \f3signo\f1 argument is the number of the signal (eg. \f3SIGINT\f1) that you want to have ignored. If the specified signal isn't currently one of those being trapped, this function does nothing. .sp The \f3gl_trap_signal()\f1 function allows you to either add a new signal to the list that \f3gl_get_line()\f1 traps, or modify how it responds to a signal that it already traps. .sp .nf int gl_trap_signal(GetLine *gl, int signo, unsigned flags, GlAfterSignal after, int errno_value); .fi .sp The \f3signo\f1 argument is the number of the signal that you wish to have trapped. The \f3flags\f1 argument is a set of flags which determine the environment in which the application's signal handler is invoked, the \f3after\f1 argument tells \f3gl_get_line()\f1 what to do after the application's signal handler returns, and \f3errno_value\f1 tells \f3gl_get_line()\f1 what to set \f3errno\f1 to if told to abort. .sp The \f3flags\f1 argument is a bitwise OR of zero or more of the following enumerators: .sp .nf GLS_RESTORE_SIG - Restore the caller's signal environment while handling the signal. GLS_RESTORE_TTY - Restore the caller's terminal settings while handling the signal. GLS_RESTORE_LINE - Move the cursor to the start of the line following the input line before invoking the application's signal handler. GLS_REDRAW_LINE - Redraw the input line when the application's signal handler returns. GLS_UNBLOCK_SIG - Normally, if the calling program has a signal blocked (man sigprocmask), gl_get_line() does not trap that signal. This flag tells gl_get_line() to trap the signal and unblock it for the duration of the call to gl_get_line(). GLS_DONT_FORWARD - If this flag is included, the signal will not be forwarded to the signal handler of the calling program. .fi .sp Two commonly useful flag combinations are also enumerated as follows: .sp .nf GLS_RESTORE_ENV = GLS_RESTORE_SIG | GLS_RESTORE_TTY | GLS_REDRAW_LINE GLS_SUSPEND_INPUT = GLS_RESTORE_ENV | GLS_RESTORE_LINE .fi .sp If your signal handler, or the default system signal handler for this signal, if you haven't overridden it, never either writes to the terminal, nor suspends or terminates the calling program, then you can safely set the \f3flags\f1 argument to \f30\f1. .sp If your signal handler always writes to the terminal, reads from it, or suspends or terminates the program, you should specify the \f3flags\f1 argument as \f3GL_SUSPEND_INPUT\f1, so that: .sp .nf 1. The cursor doesn't get left in the middle of the input line. 2. So that the user can type in input and have it echoed. 3. So that you don't need to end each output line with \f3\\r\\n\f1, instead of just \f3\\n\f1. .fi .sp The \f3GL_RESTORE_ENV\f1 combination is the same as \f3GL_SUSPEND_INPUT\f1, except that it doesn't move the cursor, and if your signal handler doesn't read or write anything to the terminal, the user won't see any visible indication that a signal was caught. This can be useful if you have a signal handler that only occasionally writes to the terminal, where using \f3GL_SUSPEND_LINE\f1 would cause the input line to be unnecessarily duplicated when nothing had been written to the terminal. Such a signal handler, when it does write to the terminal, should be sure to start a new line at the start of its first write, by writing a '\\n' character, and should be sure to leave the cursor on a new line before returning. If the signal arrives while the user is entering a line that only occupies a signal terminal line, or if the cursor is on the last terminal line of a longer input line, this will have the same effect as \f3GL_SUSPEND_INPUT\f1. Otherwise it will start writing on a line that already contains part of the displayed input line. This doesn't do any harm, but it looks a bit ugly, which is why the \f3GL_SUSPEND_INPUT\f1 combination is better if you know that you are always going to be writting to the terminal. .sp The \f3after\f1 argument, which determines what \f3gl_get_line()\f1 does after the application's signal handler returns (if it returns), can take any one of the following values: .sp .nf GLS_RETURN - Return the completed input line, just as though the user had pressed the return key. GLS_ABORT - Cause \f3gl_get_line()\f1 to abort. When this happens, \f3gl_get_line()\f1 returns \f3NULL\f1, and a following call to \f3gl_return_status()\f1 will return \f3GLR_SIGNAL\f1. Note that if the application needs \f3errno\f1 always to have a meaningful value when \f3gl_get_line()\f1 returns \f3NULL\f1, the callback function should set \f3errno\f1 appropriately. GLS_CONTINUE - Resume command line editing. .fi .sp The \f3errno_value\f1 argument is intended to be combined with the \f3GLS_ABORT\f1 option, telling \f3gl_get_line()\f1 what to set the standard \f3errno\f1 variable to before returning \f3NULL\f1 to the calling program. It can also, however, be used with the \f3GL_RETURN\f1 option, in case you wish to have a way to distinguish between an input line that was entered using the return key, and one that was entered by the receipt of a signal. .SH RELIABLE SIGNAL HANDLING Signal handling is suprisingly hard to do reliably without race conditions. In \f3gl_get_line()\f1 a lot of care has been taken to allow applications to perform reliable signal handling around \f3gl_get_line()\f1. This section explains how to make use of this. As an example of the problems that can arise if the application isn't written correctly, imagine that one's application has a SIGINT signal handler that sets a global flag. Now suppose that the application tests this flag just before invoking \f3gl_get_line()\f1. If a SIGINT signal happens to be received in the small window of time between the statement that tests the value of this flag, and the statement that calls \f3gl_get_line()\f1, then \f3gl_get_line()\f1 will not see the signal, and will not be interrupted. As a result, the application won't be able to respond to the signal until the user gets around to finishing entering the input line and \f3gl_get_line()\f1 returns. Depending on the application, this might or might not be a disaster, but at the very least it would puzzle the user. The way to avoid such problems is to do the following. 1. If needed, use the \f3gl_trap_signal()\f1 function to configure \f3gl_get_line()\f1 to abort when important signals are caught. 2. Configure \f3gl_get_line()\f1 such that if any of the signals that it catches are blocked when \f3gl_get_line()\f1 is called, they will be unblocked automatically during times when \f3gl_get_line()\f1 is waiting for I/O. This can be done either on a per signal basis, by calling the \f3gl_trap_signal()\f1 function, and specifying the \f3GLS_UNBLOCK\f1 attribute of the signal, or globally by calling the \f3gl_catch_blocked()\f1 function. .sp .nf void gl_catch_blocked(GetLine *gl); .fi .sp This function simply adds the \f3GLS_UNBLOCK\f1 attribute to all of the signals that it is currently configured to trap. 3. Just before calling \f3gl_get_line()\f1, block delivery of all of the signals that \f3gl_get_line()\f1 is configured to trap. This can be done using the POSIX \f3sigprocmask()\f1 function in conjunction with the \f3gl_list_signals()\f1 function. .sp .nf int gl_list_signals(GetLine *gl, sigset_t *set); .fi .sp This function returns the set of signals that it is currently configured to catch in the \f3set\f1 argument, which is in the form required by \f3sigprocmask()\f1. 4. In the example, one would now test the global flag that the signal handler sets, knowing that there is now no danger of this flag being set again until \f3gl_get_line()\f1 unblocks its signals while performing I/O. 5. Eventually \f3gl_get_line()\f1 returns, either because a signal was caught, an error occurred, or the user finished entering their input line. 6. Now one would check the global signal flag again, and if it is set, respond to it, and zero the flag. 7. Use \f3sigprocmask()\f1 to unblock the signals that were blocked in step 3. The same technique can be used around certain POSIX signal-aware functions, such as \f3sigsetjmp()\f1 and \f3sigsuspend()\f1, and in particular, the former of these two functions can be used in conjunction with \f3siglongjmp()\f1 to implement race-condition free signal handling around other long-running system calls. The way to do this, is explained next, by showing how \f3gl_get_line()\f1 manages to reliably trap signals around calls to functions like \f3read()\f1 and \f3select()\f1 without race conditions. The first thing that \f3gl_get_line()\f1 does, whenever it is called, is to use the POSIX \f3sigprocmask()\f1 function to block the delivery of all of the signals that it is currently configured to catch. This is redundant if the application has already blocked them, but it does no harm. It undoes this step just before returning. Whenever \f3gl_get_line()\f1 needs to call \f3read()\f1 or \f3select()\f1 to wait for input from the user, it first calls the POSIX \f3sigsetjmp()\f1 function, being sure to specify a non-zero value for its \f3savesigs\f1 argument. The reason for the latter argument will become clear shortly. If \f3sigsetjmp()\f1 returns zero, \f3gl_get_line()\f1 then does the following. .sp .nf a. It uses the POSIX \f3sigaction()\f1 function to register a temporary signal handler to all of the signals that it is configured to catch. This signal handler does two things. 1. It records the number of the signal that was received in a file-scope variable. 2. It then calls the POSIX \f3siglongjmp()\f1 function using the buffer that was passed to \f3sigsetjmp()\f1 for its first argument, and a non-zero value for its second argument. When this signal handler is registered, the \f3sa_mask\f1 member of the \f3struct sigaction act\f1 argument of the call to \f3sigaction()\f1 is configured to contain all of the signals that \f3gl_get_line()\f1 is catching. This ensures that only one signal will be caught at once by our signal handler, which in turn ensures that multiple instances of our signal handler don't tread on each other's toes. b. Now that the signal handler has been set up, \f3gl_get_line()\f1 unblocks all of the signals that it is configured to catch. c. It then calls the \f3read()\f1 or \f3select()\f1 system calls to wait for keyboard input. d. If this system call returns (ie. no signal is received), \f3gl_get_line()\f1 blocks delivery of the signals of interest again. e. It then reinstates the signal handlers that were displaced by the one that was just installed. .fi .sp Alternatively, if \f3sigsetjmp()\f1 returns non-zero, this means that one of the signals being trapped was caught while the above steps were executing. When this happens, \f3gl_get_line()\f1 does the following. First, note that when a call to \f3siglongjmp()\f1 causes \f3sigsetjmp()\f1 to return, provided that the \f3savesigs\f1 argument of \f3sigsetjmp()\f1 was non-zero, as specified above, the signal process mask is restored to how it was when \f3sigsetjmp()\f1 was called. This is the important difference between \f3sigsetjmp()\f1 and the older problematic \f3setjmp()\f1, and is the essential ingredient that makes it possible to avoid signal handling race conditions. Because of this we are guaranteed that all of the signals that we blocked before calling \f3sigsetjmp()\f1 are blocked again as soon as any signal is caught. The following statements, which are then executed, are thus guaranteed to be executed without any further signals being caught. 1. If so instructed by the \f3gl_get_line()\f1 configuration attributes of the signal that was caught, \f3gl_get_line()\f1 restores the terminal attributes to the state that they had when \f3gl_get_line()\f1 was called. This is particularly important for signals that suspend or terminate the process, since otherwise the terminal would be left in an unusable state. 2. It then reinstates the application's signal handlers. 3. Then it uses the C standard-library \f3raise()\f1 function to re-send the application the signal that was caught. 3. Next it unblocks delivery of the signal that we just sent. This results in the signal that was just sent via \f3raise()\f1, being caught by the application's original signal handler, which can now handle it as it sees fit. 4. If the signal handler returns (ie. it doesn't terminate the process), \f3gl_get_line()\f1 blocks delivery of the above signal again. 5. It then undoes any actions performed in the first of the above steps, and redisplays the line, if the signal configuration calls for this. 6. \f3gl_get_line()\f1 then either resumes trying to read a character, or aborts, depending on the configuration of the signal that was caught. What the above steps do in essence is to take asynchronously delivered signals and handle them synchronously, one at a time, at a point in the code where \f3gl_get_line()\f1 has complete control over its environment. .SH THE TERMINAL SIZE On most systems the combination of the \f3TIOCGWINSZ\f1 ioctl and the \f3SIGWINCH\f1 signal is used to maintain an accurate idea of the terminal size. The terminal size is newly queried every time that \f3gl_get_line()\f1 is called and whenever a \f3SIGWINCH\f1 signal is received. .sp On the few systems where this mechanism isn't available, at startup \f3new_GetLine()\f1 first looks for the \f3LINES\f1 and \f3COLUMNS\f1 environment variables. If these aren't found, or they contain unusable values, then if a terminal information database like terminfo or termcap is available, the default size of the terminal is looked up in this database. If this too fails to provide the terminal size, a default size of 80 columns by 24 lines is used. .sp Even on systems that do support \f3ioctl(TIOCGWINSZ)\f1, if the terminal is on the other end of a serial line, the terminal driver generally has no way of detecting when a resize occurs or of querying what the current size is. In such cases no \f3SIGWINCH\f1 is sent to the process, and the dimensions returned by \f3ioctl(TIOCGWINSZ)\f1 aren't correct. The only way to handle such instances is to provide a way for the user to enter a command that tells the remote system what the new size is. This command would then call the \f3gl_set_term_size()\f1 function to tell \f3gl_get_line()\f1 about the change in size. .sp .nf int gl_set_term_size(GetLine *gl, int ncolumn, int nline); .fi .sp The \f3ncolumn\f1 and \f3nline\f1 arguments are used to specify the new dimensions of the terminal, and must not be less than 1. On systems that do support \f3ioctl(TIOCGWINSZ)\f1, this function first calls \f3ioctl(TIOCSWINSZ)\f1 to tell the terminal driver about the change in size. In non-blocking server-I/O mode, if a line is currently being input, the input line is then redrawn to accomodate the changed size. Finally the new values are recorded in \f3gl\f1 for future use by \f3gl_get_line()\f1. .sp The \f3gl_terminal_size()\f1 function allows you to query the current size of the terminal, and install an alternate fallback size for cases where the size isn't available. Beware that the terminal size won't be available if reading from a pipe or a file, so the default values can be important even on systems that do support ways of finding out the terminal size. .sp .nf typedef struct { int nline; /* The terminal has nline lines */ int ncolumn; /* The terminal has ncolumn columns */ } GlTerminalSize; GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline); .fi .sp This function first updates \f3gl_get_line()\f1's fallback terminal dimensions, then records its findings in the return value. .sp The \f3def_ncolumn\f1 and \f3def_nline\f1 specify the default number of terminal columns and lines to use if the terminal size can't be determined via \f3ioctl(TIOCGWINSZ)\f1 or environment variables. .SH HIDING WHAT YOU TYPE When entering sensitive information, such as passwords, it is best not to have the text that you are entering echoed on the terminal. Furthermore, such text should not be recorded in the history list, since somebody finding your terminal unattended could then recall it, or somebody snooping through your directories could see it in your history file. With this in mind, the \f3gl_echo_mode()\f1 function allows you to toggle on and off the display and archival of any text that is subsequently entered in calls to \f3gl_get_line()\f1. .sp .nf int gl_echo_mode(GetLine *gl, int enable); .fi .sp The \f3enable\f1 argument specifies whether entered text should be visible or not. If it is \f30\f1, then subsequently entered lines will not be visible on the terminal, and will not be recorded in the history list. If it is \f31\f1, then subsequent input lines will be displayed as they are entered, and provided that history hasn't been turned off via a call to \f3gl_toggle_history()\f1, then they will also be archived in the history list. Finally, if the \f3enable\f1 argument is \f3-1\f1, then the echoing mode is left unchanged, which allows you to non-destructively query the current setting via the return value. In all cases, the return value of the function is \f30\f1 if echoing was disabled before the function was called, and \f31\f1 if it was enabled. .sp When echoing is turned off, note that although tab completion will invisibly complete your prefix as far as possible, ambiguous completions will not be displayed. .SH SINGLE CHARACTER QUERIES Using \f3gl_get_line()\f1 to query the user for a single character reply, is inconvenient for the user, since they must hit the enter or return key before the character that they typed is returned to the program. Thus the \f3gl_query_char()\f1 function has been provided for single character queries like this. .sp .nf int gl_query_char(GetLine *gl, const char *prompt, char defchar); .fi .sp This function displays the specified prompt at the start of a new line, and waits for the user to type a character. When the user types a character, \f3gl_query_char()\f1 displays it to the right of the prompt, starts a newline, then returns the character to the calling program. The return value of the function is the character that was typed. If the read had to be aborted for some reason, \f3EOF\f1 is returned instead. In the latter case, the application can call the previously documented \f3gl_return_status()\f1, to find out what went wrong. This could, for example, have been the reception of a signal, or the optional inactivity timer going off. If the user simply hits enter, the value of the \f3defchar\f1 argument is substituted. This means that when the user hits either newline or return, the character specified in \f3defchar\f1, is displayed after the prompt, as though the user had typed it, as well as being returned to the calling application. If such a replacement is not important, simply pass \f3'\n'\f1 as the value of \f3defchar\f1. If the entered character is an unprintable character, it is displayed symbolically. For example, control-A is displayed as ^A, and characters beyond 127 are displayed in octal, preceded by a backslash. As with \f3gl_get_line()\f1, echoing of the entered character can be disabled using the \f3gl_echo_mode()\f1 function. If the calling process is suspended while waiting for the user to type their response, the cursor is moved to the line following the prompt line, then when the process resumes, the prompt is redisplayed, and \f3gl_query_char()\f1 resumes waiting for the user to type a character. Note that in non-blocking server mode, (see gl_io_mode(@FUNC_MANEXT@)), if an incomplete input line is in the process of being read when \f3gl_query_char()\f1 is called, the partial input line is discarded, and erased from the terminal, before the new prompt is displayed. The next call to \f3gl_get_line()\f1 will thus start editing a new line. .SH READING RAW CHARACTERS Whereas the \f3gl_query_char()\f1 function visibly prompts the user for a character, and displays what they typed, the \f3gl_read_char()\f1 function reads a signal character from the user, without writing anything to the terminal, or perturbing any incompletely entered input line. This means that it can be called not only from between calls to \f3gl_get_line()\f1, but also from callback functions that the application has registered to be called by \f3gl_get_line()\f1. .sp .nf int gl_read_char(GetLine *gl); .fi .sp On success, the return value of \f3gl_read_char()\f1 is the character that was read. On failure, \f3EOF\f1 is returned, and the \f3gl_return_status()\f1 function can be called to find out what went wrong. Possibilities include the optional inactivity timer going off, the receipt of a signal that is configured to abort gl_get_line(), or terminal I/O blocking, when in non-blocking server-I/O mode. Beware that certain keyboard keys, such as function keys, and cursor keys, usually generate at least 3 characters each, so a single call to \f3gl_read_char()\f1 won't be enough to identify such keystrokes. .SH CLEARING THE TERMINAL The calling program can clear the terminal by calling \f3gl_erase_terminal()\f1. In non-blocking server-I/O mode, this function also arranges for the current input line to be redrawn from scratch when \f3gl_get_line()\f1 is next called. .sp .nf int gl_erase_terminal(GetLine *gl); .fi .sp .SH DISPLAYING TEXT DYNAMICALLY Between calls to \f3gl_get_line()\f1, the \f3gl_display_text()\f1 function provides a convenient way to display paragraphs of text, left-justified and split over one or more terminal lines according to the constraints of the current width of the terminal. Examples of the use of this function may be found in the demo programs, where it is used to display introductions. In those examples the advanced use of optional prefixes, suffixes and filled lines to draw a box around the text is also illustrated. .sp .nf int gl_display_text(GetLine *gl, int indentation, const char *prefix, const char *suffix, int fill_char, int def_width, int start, const char *string); .fi .sp If \f3gl\f1 isn't currently connected to a terminal, for example if the output of a program that uses \f3gl_get_line()\f1 is being piped to another program or redirected to a file, then the value of the \f3def_width\f1 parameter is used as the terminal width. The \f3indentation\f1 argument specifies the number of characters to use to indent each line of ouput. The \f3fill_char\f1 argument specifies the character that will be used to perform this indentation. The \f3prefix\f1 argument can either be \f3NULL\f1, or be a string to place at the beginning of each new line (after any indentation). Similarly, the \f3suffix\f1 argument can either be \f3NULL\f1, or be a string to place at the end of each line. The suffix is placed flush against the right edge of the terminal, and any space between its first character and the last word on that line is filled with the character specified via the \f3fill_char\f1 argument. Normally the fill-character is a space. The \f3start\f1 argument tells \f3gl_display_text()\f1 how many characters have already been written to the current terminal line, and thus tells it the starting column index of the cursor. Since the return value of \f3gl_display_text()\f1 is the ending column index of the cursor, by passing the return value of one call to the \f3start\f1 argument of the next call, a paragraph that is broken between more than one string can be composed by calling \f3gl_display_text()\f1 for each successive portion of the paragraph. Note that literal newline characters are necessary at the end of each paragraph to force a new line to be started. On error, \f3gl_display_text()\f1 returns -1. .SH CALLBACK FUNCTION FACILITIES Unless otherwise stated, callback functions, such as tab completion callbacks and event callbacks should not call any functions in this module. The following functions, however, are designed specifically to be used by callback functions. .sp Calling the \f3gl_replace_prompt()\f1 function from a callback tells \f3gl_get_line()\f1 to display a different prompt when the callback returns. Except in non-blocking server mode, it has no effect if used between calls to \f3gl_get_line()\f1. In non-blocking server mode (see the \f3gl_io_mode(@FUNC_MANEXT@)\f1 man page, when used between two calls to \f3gl_get_line()\f1 that are operating on the same input line, the current input line will be re-drawn with the new prompt on the following call to \f3gl_get_line()\f1. .sp .nf void gl_replace_prompt(GetLine *gl, const char *prompt); .fi .sp .SH INTERNATIONAL CHARACTER SETS Since libtecla version 1.4.0, \f3gl_get_line()\f1 has been 8-bit clean. This means that all 8-bit characters that are printable in the user's current locale are now displayed verbatim and included in the returned input line. Assuming that the calling program correctly contains a call like the following, .sp .nf setlocale(LC_CTYPE, ""); .fi .sp then the current locale is determined by the first of the environment variables \f3LC_CTYPE\f1, \f3LC_ALL\f1, and \f3LANG\f1, that is found to contain a valid locale name. If none of these variables are defined, or the program neglects to call setlocale, then the default \f3C\f1 locale is used, which is US 7-bit ASCII. On most unix-like platforms, you can get a list of valid locales by typing the command: .sp .nf locale -a .fi .sp at the shell prompt. Further documentation on how the user can make use of this to enter international characters can be found in the \f3tecla(@MISC_MANEXT@)\f1 man page. .SH THREAD SAFETY In a multi-threaded program, you should use the libtecla_r.a version of the library. This uses reentrant versions of system functions, where available. Unfortunately neither terminfo nor termcap were designed to be reentrant, so you can't safely use the functions of the getline module in multiple threads (you can use the separate file-expansion and word-completion modules in multiple threads, see the corresponding man pages for details). However due to the use of POSIX reentrant functions for looking up home directories etc, it is safe to use this module from a single thread of a multi-threaded program, provided that your other threads don't use any termcap or terminfo functions. .SH FILES .nf libtecla.a - The tecla library libtecla.h - The tecla header file. ~/.teclarc - The personal tecla customization file. .fi .SH SEE ALSO .nf libtecla(@LIBR_MANEXT@), gl_io_mode(@FUNC_MANEXT@), tecla(@MISC_MANEXT@), ef_expand_file(@FUNC_MANEXT@), cpl_complete_word(@FUNC_MANEXT@), pca_lookup_file(@FUNC_MANEXT@) .fi .SH AUTHOR Martin Shepherd (mcs@astro.caltech.edu) yuma123_2.14/libtecla/man/func/del_ExpandFile.in0000664000175000017500000000005714770023131021605 0ustar vladimirvladimir.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_return_status.in0000664000175000017500000000005414770023131022343 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_save_history.in0000664000175000017500000000005414770023131022140 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_lookup_history.in0000664000175000017500000000005414770023131022513 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/ppc_literal_escapes.in0000664000175000017500000000006014770023131022735 0ustar vladimirvladimir.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/cpl_recall_matches.in0000664000175000017500000000006214770023131022542 0ustar vladimirvladimir.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/ppc_file_start.in0000664000175000017500000000006014770023131021732 0ustar vladimirvladimir.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/pca_scan_path.in0000664000175000017500000000006014770023131021517 0ustar vladimirvladimir.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_ignore_signal.in0000664000175000017500000000005414770023131022241 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_range_of_history.in0000664000175000017500000000005414770023131022762 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_abandon_line.in0000664000175000017500000000005314770023131022031 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/del_CplFileConf.in0000664000175000017500000000006214770023131021706 0ustar vladimirvladimir.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_erase_terminal.in0000664000175000017500000000005414770023131022413 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_display_text.in0000664000175000017500000000005414770023131022132 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/cfc_file_start.in0000664000175000017500000000006214770023131021705 0ustar vladimirvladimir.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/ef_list_expansions.in0000664000175000017500000000005714770023131022636 0ustar vladimirvladimir.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_state_of_history.in0000664000175000017500000000005414770023131023006 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_clear_history.in0000664000175000017500000000005414770023131022270 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_error_message.in0000664000175000017500000000005414770023131022256 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/libtecla_version.in0000664000175000017500000000005114770023131022260 0ustar vladimirvladimir.so @LIBR_MANDIR@/libtecla.@LIBR_MANEXT@ yuma123_2.14/libtecla/man/func/gl_show_history.in0000664000175000017500000000005414770023131022162 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/gl_io_mode.in0000664000175000017500000005765114770023131021053 0ustar vladimirvladimir.\" Copyright (c) 2002, 2003, 2004 by Martin C. Shepherd .\" .\" All rights reserved. .\" .\" Permission is hereby granted, free of charge, to any person obtaining a .\" copy of this software and associated documentation files (the .\" "Software"), to deal in the Software without restriction, including .\" without limitation the rights to use, copy, modify, merge, publish, .\" distribute, and/or sell copies of the Software, and to permit persons .\" to whom the Software is furnished to do so, provided that the above .\" copyright notice(s) and this permission notice appear in all copies of .\" the Software and that both the above copyright notice(s) and this .\" permission notice appear in supporting documentation. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS .\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF .\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR .\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL .\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING .\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, .\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION .\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Except as contained in this notice, the name of a copyright holder .\" shall not be used in advertising or otherwise to promote the sale, use .\" or other dealings in this Software without prior written authorization .\" of the copyright holder. .TH gl_io_mode @FUNC_MANEXT@ .SH NAME gl_io_mode, gl_raw_io, gl_normal_io, gl_tty_signals, gl_abandon_line, gl_handle_signal, gl_pending_io \- How to use gl_get_line() from an external event loop. .SH SYNOPSIS .nf #include int gl_io_mode(GetLine *gl, GlIOMode mode); int gl_raw_io(GetLine *gl); int gl_normal_io(GetLine *gl); int gl_tty_signals(void (*term_handler)(int), void (*susp_handler)(int), void (*cont_handler)(int), void (*size_handler)(int)); void gl_abandon_line(GetLine *gl); void gl_handle_signal(int signo, GetLine *gl, int ngl); GlPendingIO gl_pending_io(GetLine *gl); .fi .SH DESCRIPTION The \f3gl_get_line()\f1 function, which is documented separately in the \f3gl_get_line(@FUNC_MANEXT@)\f1 man page, supports two different I/O modes. These are selected by calling the \f3gl_io_mode()\f1 function. .sp .nf int gl_io_mode(GetLine *gl, GlIOMode mode); .fi .sp The \f3mode\f1 argument of this function specifies the new I/O mode, and must be one of the following. .sp .nf GL_NORMAL_MODE - Select the normal blocking-I/O mode. In this mode \f3gl_get_line()\f1 doesn't return until either an error occurs of the user finishes entering a new line. This mode is the focus of the \f3gl_get_line(@FUNC_MANEXT@)\f1 man page. GL_SERVER_MODE - Select non-blocking server I/O mode. In this mode, since non-blocking terminal I/O is used, the entry of each new input line typically requires many calls to \f3gl_get_line()\f1 from an external I/O-driven event loop. This mode is the focus of this man page. .fi .sp Newly created \f3GetLine\f1 objects start in normal I/O mode, so to switch to non-blocking server mode requires an initial call to \f3gl_io_mode()\f1. .SH SERVER I/O MODE In non-blocking server I/O mode, the application is required to have an event loop which calls \f3gl_get_line()\f1 whenever the terminal file descriptor can do the type I/O that \f3gl_get_line()\f1 is waiting for. To determine which type of I/O \f3gl_get_line()\f1 is waiting for, the application calls the \f3gl_pending_io()\f1 function. .sp .nf GlPendingIO gl_pending_io(GetLine *gl); .fi .sp The return value of this function is one of the following two enumerated values. .sp .nf GLP_READ - gl_get_line() is waiting to write a character to the terminal. GLP_WRITE - gl_get_line() is waiting to read a character from the keyboad. .fi .sp If the application is using either the \f3select()\f1 or \f3poll()\f1 system calls to watch for I/O on a group of file descriptors, then it should call the \f3gl_pending_io()\f1 function before each call to these functions to see which direction of I/O it should tell them to watch for, and configure their arguments accordingly. In the case of the \f3select()\f1 system call, this means using the \f3FD_SET()\f1 macro to add the terminal file descriptor either to the set of file descriptors to be watched for readability, or the set to be watched for writability. As in normal I/O mode, the return value of \f3gl_get_line()\f1 is either a pointer to a completed input line, or \f3NULL\f1. However, whereas in normal I/O mode a \f3NULL\f1 return value always means that an error occurred, in non-blocking server mode, \f3NULL\f1 is also returned when \f3gl_get_line()\f1 can't read or write to the terminal without blocking. Thus in non-blocking server mode, in order to determine when a \f3NULL\f1 return value signifies that an error occurred or not, it is necessary to call the \f3gl_return_status()\f1 function. If this function returns the enumerated value, \f3GLR_BLOCKED\f1, as documented in the \f3gl_get_line(@FUNC_MANEXT@)\f1 man page, this means that \f3gl_get_line()\f1 is waiting for I/O, and no error has occurred. When \f3gl_get_line()\f1 returns \f3NULL\f1 and \f3gl_return_status()\f1 indicates that this is due to blocked terminal I/O, the application should call \f3gl_get_line()\f1 again when the type of I/O reported by \f3gl_pending_io()\f1 becomes possible. The \f3prompt\f1, \f3start_line\f1 and \f3start_pos\f1 arguments of \f3gl_get_line()\f1 will be ignored on these calls. If you need to change the prompt of the line that is currently being edited, then you can call the \f3gl_replace_prompt()\f1 function (documented in the \f3gl_get_line(@FUNC_MANEXT@) man page) between calls to \f3gl_get_line()\f1. .SH GIVING UP THE TERMINAL A complication that is unique to non-blocking server mode is that it requires that the terminal be left in raw mode between calls to \f3gl_get_line()\f1. If this weren't the case, the external event loop wouldn't be able to detect individual key-presses, and the basic line editing implemented by the terminal driver would clash with the editing provided by \f3gl_get_line()\f1. What this means is that any time that the terminal needs to be used for other things than entering a new input line with \f3gl_get_line()\f1, it needs to be restored to a usable state. In particular, whenever the process is suspended or terminated, the terminal must be returned to a normal state. If this isn't done, then depending on the characteristics of the shell that was used to invoke the program, the user may end up with a hung terminal. To this end, the \f3gl_normal_io()\f1 function is provided for switching the terminal back to the state that it was in when raw mode was last established. .sp .nf int gl_normal_io(GetLine *gl); .fi .sp What this function does is first flush any pending output to the terminal, then move the cursor to the start of the terminal line which follows the end of the incompletely entered input line. At this point it is safe to suspend or terminate the process, and it is safe for the application to read and write to the terminal. To resume entry of the input line, the application should call the \f3gl_raw_io()\f1 function. .sp .nf int gl_raw_io(GetLine *gl); .fi .sp This function starts a new line, redisplays the partially completed input line (if any), restores the cursor position within this line to where it was when \f3gl_normal_io()\f1 was called, then switches back to raw, non-blocking terminal mode ready to continue entry of the input line when \f3gl_get_line()\f1 is next called. Note that in non-blocking server mode, if \f3gl_get_line()\f1 is called after a call to \f3gl_normal_io()\f1, without an intervening call to \f3gl_raw_io()\f1, \f3gl_get_line()\f1 will call \f3gl_raw_mode()\f1 itself, and the terminal will remain in this mode when \f3gl_get_line()\f1 returns. .SH SIGNAL HANDLING In the previous section it was pointed out that in non-blocking server mode, the terminal must be restored to a sane state whenever a signal is received that either suspends or terminates the process. In normal I/O mode, this is done for you by \f3gl_get_line()\f1, but in non-blocking server mode, since the terminal is left in raw mode between calls to \f3gl_get_line()\f1, this signal handling has to be done by the application. Since there are many signals that can suspend or terminate a process, as well as other signals that are important to \f3gl_get_line()\f1, such as the \f3SIGWINCH\f1 signal, which tells it when the terminal size has changed, the \f3gl_tty_signals()\f1 function is provided for installing signal handlers for all pertinent signals. .sp .nf int gl_tty_signals(void (*term_handler)(int), void (*susp_handler)(int), void (*cont_handler)(int), void (*size_handler)(int)); .fi .sp What this does is use \f3gl_get_line()\f1's internal list of signals to assign specified signal handlers to groups of signals. The arguments of this function are as follows. .sp .nf term_handler - This is the signal handler that is to be used to trap signals that by default terminate any process that receives them (eg. SIGINT or SIGTERM). susp_handler - This is the signal handler that is to be used to trap signals that by default suspend any process that receives them, (eg. SIGTSTP or SIGTTOU). cont_handler - This is the signal handler that is to be used to trap signals that are usually sent when a process resumes after being suspended (usually SIGCONT). Beware that there is nothing to stop a user from sending one of these signals at other times. size_handler - This signal handler is used to trap signals that are sent to processes when their controlling terminals are resized by the user (eg. SIGWINCH). .fi .sp These arguments can all be the same, if so desired, and you can specify \f3SIG_IGN\f1 (ignore this signal) or \f3SIG_DFL\f1 (use the system-provided default signal handler) instead of a function where pertinent. In particular, it is rarely useful to trap \f3SIGCONT\f1, so the \f3cont_handler\f1 argument will usually be \f3SIG_DFL\f1 or \f3SIG_IGN\f1. The \f3gl_tty_signals()\f1 function uses the POSIX \f3sigaction()\f1 function to install these signal handlers, and it is careful to use the \f3sa_mask\f1 member of each sigaction structure to ensure that only one of these signals is ever delivered at a time. This guards against different instances of these signal handlers from simultaneously trying to write to common global data, such as a shared \f3sigsetjmp()\f1 buffer or a signal-received flag. The signal handlers that are installed by this function, should call the \f3gl_handle_signal(). .sp .nf void gl_handle_signal(int signo, GetLine *gl, int ngl); .fi .sp The \f3signo\f1 argument tells this function which signal it is being asked to respond to, and the \f3gl\f1 argument should be a pointer to the first element of an array of \f3ngl\f1 \f3GetLine\f1 objects. If your application only has one of these objects, just pass its pointer as the \f3gl\f1 argument and specify \f3ngl\f1 as \f31\f1. Depending on the signal that is being handled, this function does different things. .SS Terminal resize signals (SIGWINCH) If the signal indicates that the terminal was resized, then it arranges for the next call to \f3gl_get_line()\f1 to ask the terminal for its new size and redraw the input line accordingly. In order that \f3gl_get_line()\f1 be called as soon as possible to do this, \f3gl_handle_signal()\f1 also arranges that the next call to \f3gl_pending_io()\f1 will return \f3GLP_WRITE\f1. Thus if the application waits for I/O in \f3select()\f1 or \f3poll()\f1, then the application needs to ensure that these functions will be reliably aborted when a signal is caught and handled by the application. More on this below. .SH Process termination signals. If the signal that was caught is one of those that by default terminates any process that receives it, then \f3gl_handle_signal()\f1 does the following steps. 1. First it blocks the delivery of all signals that can be blocked (ie. \f3SIGKILL\f1 and \f3SIGSTOP\f1 can't be blocked) 2. Next it calls \f3gl_normal_io()\f1 for each of the \f3ngl\f1 \f3GetLine\f1 objects. Note that this does nothing to any of the \f3GetLine\f1 objects that aren't currently in raw mode. 3. Next it sets the signal handler of the signal to its default, process-termination disposition. 4. Next it re-sends the process the signal that was caught. 5. Finally it unblocks delivery of this signal, which results in the process being terminated. .SH Process suspension signals. If the default disposition of the signal is to suspend the process, the same steps are executed as for process termination signals, except that when the process is later resumed, \f3gl_handle_signal()\f1 continues, and does the following steps. 6. It re-blocks delivery of the signal. 7. It reinstates the signal handler of the signal to the one that was displaced when its default disposition was substituted. 8. For any of the \f3GetLine\f1 objects that were in raw mode when \f3gl_handle_signal()\f1 was called, \f3gl_handle_signal()\f1 then calls \f3gl_raw_io()\f1, to resume entry of the input lines on those terminals. 9. Finally, it restores the signal process mask to how it was when \f3gl_handle_signal()\f1 was called. Note that the process is suspended or terminated using the original signal that was caught, rather than using the uncatchable \f3SIGSTOP\f1 and \f3SIGKILL\f1 signals. This is important, because when a process is suspended or terminated, the parent of the process may wish to use the status value returned by the \f3wait()\f1 system call to figure out which signal was responsible. In particular, most shells use this information to print a corresponding message to the terminal. Users would be rightly confused if when their process received a \f3SIGPIPE\f1 signal, the program responded by sending itself a \f3SIGKILL\f1 signal, and the shell then printed out the provocative statement, "Killed!". .SH INTERRUPTING THE EVENT LOOP If a signal is caught and handled when the application's event loop is waiting in \f3select()\f1 or \f3poll()\f1, these functions will be aborted with \f3errno\f1 set to \f3EINTR\f1. When this happens the event loop should call \f3gl_pending_io()\f1, before calling \f3select()\f1 or \f3poll()\f1 again. It should then arrange for \f3select()\f1 or \f3poll()\f1 to wait for the type of I/O that this reports. This is necessary, because any signal handler which calls \f3gl_handle_signal()\f1, will frequently change the type of I/O that \f3gl_get_line()\f1 is waiting for. Unfortunately, if a signal arrives between the statements which configure the arguments of \f3select()\f1 or \f3poll()\f1 and the calls to these functions, then the signal will not be seen by these functions, which will then not be aborted. If these functions are waiting for keyboard input from the user when the signal is received, and the signal handler arranges to redraw the input line to accomodate a terminal resize or the resumption of the process, then this redisplay will be end up being delayed until the user hits the next key. Apart from puzzling the user, this clearly isn't a serious problem. However there is a way, albeit complicated, to completely avoid this race condition. The following steps illustrate this. 1. Block all of the signals that \f3gl_get_line()\f1 catches, by passing the signal set returned by \f3gl_list_signals()\f1 to \f3sigprocmask()\f1. 2. Call \f3gl_pending_io()\f1 and set up the arguments of \f3select()\f1 or \f3poll()\f1 accordingly. 3. Call \f3sigsetjmp()\f1 with a non-zero \f3savesigs\f1 argument. 4. Initially this \f3sigsetjmp()\f1 statement will return zero, indicating that control isn't resuming there after a matching call to \f3siglongjmp()\f1. 5. Replace all of the handlers of the signals that \f3gl_get_line()\f1 is configured to catch, with a signal handler that first records the number of the signal that was caught, in a file-scope variable, then calls \f3siglongjmp()\f1 with a non-zero value argument, to return execution to the above \f3sigsetjmp()\f1 statement. Registering these signal handlers can conveniently be done using the \f3gl_tty_signals()\f1 function. 6. Set the file-scope variable that the above signal handler uses to record any signal that is caught to -1, so that we can check whether a signal was caught by seeing if it contains a valid signal number. 7. Now unblock the signals that were blocked in step 1. Any signal that was received by the process in between step 1 and now will now be delivered, and trigger our signal handler, as will any signal that is received until we block these signals again. 8. Now call \f3select()\f1 or \f3poll()\f1. 9. When \f3select()\f1 returns, again block the signals that were unblocked in step 7. If a signal is arrived any time during the above steps, our signal handler will be triggered and cause control to return to the \f3sigsetjmp()\f1 statement, where this time, \f3sigsetjmp()\f1 will return non-zero, indicating that a signal was caught. When this happens we simply skip the above block of statements, and continue with the following statements, which are executed regardless of whether or not a signal is caught. Note that when \f3sigsetjmp()\f1 returns, regardless of why it returned, the process signal mask is returned to how it was when \f3sigsetjmp()\f1 was called. Thus the following statements are always executed with all of our signals blocked. 9. Reinstate the signal handlers that were displaced in step 5. 10. Check wether a signal was caught, by checking the file-scope variable that the signal handler records signal numbers in. 11. If a signal was caught, send this signal to the application again, and unblock just this signal, so that it invokes the signal handler which we just reinstated in step 10. 12. Unblock all of the signals that were blocked in step 7. Since this is complicated, note that \f3demo3.c\f1 includes a working example of how to do this. The method used there however, is more general than the above. What it provides is a wrapper function around \f3select()\f1 which encompasses steps 3 to 11. In this wrapper, rather than use \f3gl_list_signals()\f1 to figure out the signals to block, and and \f3gl_tty_signals()\f1 to assign and revert signal handlers, one of its arguments is a \f3sigset_t\f1 which specifies which signals to block and assign signal handlers to. This function thus doesn't depend on \f3gl_get_line()\f1 and can thus be used in other situations where race-condition-free signal handling is required. .SH SIGNALS CAUGHT BY GL_GET_LINE Since the application is expected to handle signals in non-blocking server mode, \f3gl_get_line()\f1 doesn't attempt to duplicate this when it is being called. If one of the signals that it is configured to catch is sent to the application while \f3gl_get_line()\f1 is being called, \f3gl_get_line()\f1 reinstates the caller's signal handlers, then just before returning, re-sends the signal to the process to let the application's signal handler handle it. If the process isn't terminated by this signal, \f3gl_get_line()\f1 returns \f3NULL\f1, and a following call to \f3gl_return_status()\f1 returns the enumerated value \f3GLR_SIGNAL\f1. .SH ABORTING LINE INPUT Often, rather than letting it terminate the process, applications respond to the SIGINT user-interrupt signal by aborting the current input line. The way to do this in non-blocking server-I/O mode is to not call \f3gl_handle_signal()\f1 when this signal is caught, but instead to call the \f3gl_abandon_line()\f1. .sp .nf void gl_abandon_line(GetLine *gl); .fi .sp This function arranges that when \f3gl_get_line()\f1 is next called, it first flushes any pending output to the terminal, then discardes the current input line, outputs a new prompt on the next line, and finally starts accepting input of a new input line from the user. .SH SIGNAL SAFE FUNCTIONS Provided that certain rules are followed, the following functions can have been written to be safely callable from signal handlers. Other functions in this library should not be called from signal handlers. .sp .nf gl_normal_io() gl_raw_io() gl_handle_signal() gl_abandon_line() .fi .sp In order for this to be true, all signal handlers that call these functions must be registered in such a way that only one instance of any one of them can be running at one time. The way to do this is to use the POSIX \f3sigaction()\f1 function to register all signal handlers, and when doing this, use the \f3sa_mask\f1 member of the corresponding sigaction structure, to indicate that all of the signals who's handlers invoke the above functions, should be blocked when the current signal is being handled. This prevents two signal handlers from operating on a \f3GetLine\f1 object at the same time. To prevent signal handlers from accessing a \f3GetLine\f1 object while \f3gl_get_line()\f1 or any of its associated public functions are operating on it, all public functions associated with \f3gl_get_line()\f1, including \f3gl_get_line()\f1 itself, temporarily block the delivery of signals when they are accessing \f3GetLine\f1 objects. Beware that the only signals that they block are the signals that \f3gl_get_line()\f1 is currently configured to catch, so be sure that if you call any of the above functions from signal handlers, that the signals that these handlers are assigned to are configured to be caught by \f3gl_get_line()\f1 (see \f3gl_trap_signal()\f1). .SH USING TIMEOUTS TO POLL If instead of using \f3select()\f1 or \f3poll()\f1 to wait for I/O, your application just needs to get out of \f3gl_get_line()\f1 periodically to briefly do something else before returning to accept input from the user, this can be done in non-blocking server mode by using the \f3gl_inactivity_timeout()\f1 function (see \f3gl_get_line(@FUNC_MANEXT@)\f1), to specify that a callback function that returns \f3GLTO_CONTINUE\f1 should be called whenever \f3gl_get_line()\f1 has been waiting for I/O for more than a specified amount of time. When this callback is triggered, \f3gl_get_line()\f1 will return \f3NULL\f1, and a following call to \f3gl_return_status()\f1 will return \f3GLR_BLOCKED\f1. Beware that \f3gl_get_line()\f1 won't return until the user hasn't typed a key for the specified interval, so if the interval is long, and the user keeps typing, \f3gl_get_line()\f1 may not return for a while. In other words there is no guarantee that it will return in the time specified. .SH THE SERVER DEMO PROGRAM The \f3demo3\f1 program that is distributed with the library, provides a working example of how to use non-blocking server I/O mode in a real program. As far as the user is concerned, this program operates identically to the main demo program (called \f3demo\f1), except that whereas the main demo program uses the normal blocking I/O mode, \f3demo3\f1 using non-blocking I/O and an external event loop. The source code can be found in \f3demo3.c\f1, and the comments therein explain the various steps. .SH FILES .nf libtecla.a - The tecla library libtecla.h - The tecla header file. .fi .SH SEE ALSO .nf libtecla(@LIBR_MANEXT@), gl_get_line(@FUNC_MANEXT@), tecla(@MISC_MANEXT@), ef_expand_file(@FUNC_MANEXT@), cpl_complete_word(@FUNC_MANEXT@), pca_lookup_file(@FUNC_MANEXT@) .fi .SH AUTHOR Martin Shepherd (mcs@astro.caltech.edu) yuma123_2.14/libtecla/man/func/gl_query_char.in0000664000175000017500000000005414770023131021563 0ustar vladimirvladimir.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ yuma123_2.14/libtecla/man/func/pca_lookup_file.in0000664000175000017500000003356414770023131022106 0ustar vladimirvladimir.\" Copyright (c) 2001, 2002, 2003, 2004 by Martin C. Shepherd .\" .\" All rights reserved. .\" .\" Permission is hereby granted, free of charge, to any person obtaining a .\" copy of this software and associated documentation files (the .\" "Software"), to deal in the Software without restriction, including .\" without limitation the rights to use, copy, modify, merge, publish, .\" distribute, and/or sell copies of the Software, and to permit persons .\" to whom the Software is furnished to do so, provided that the above .\" copyright notice(s) and this permission notice appear in all copies of .\" the Software and that both the above copyright notice(s) and this .\" permission notice appear in supporting documentation. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS .\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF .\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR .\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL .\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING .\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, .\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION .\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Except as contained in this notice, the name of a copyright holder .\" shall not be used in advertising or otherwise to promote the sale, use .\" or other dealings in this Software without prior written authorization .\" of the copyright holder. .TH pca_lookup_file @FUNC_MANEXT@ .SH NAME pca_lookup_file, del_PathCache, del_PcaPathConf, new_PathCache, new_PcaPathConf, pca_last_error, pca_path_completions, pca_scan_path, pca_set_check_fn, ppc_file_start, ppc_literal_escapes \- lookup a file in a list of directories .SH SYNOPSIS .nf #include PathCache *new_PathCache(void); PathCache *del_PathCache(PathCache *pc); int pca_scan_path(PathCache *pc, const char *path); void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, void *data); char *pca_lookup_file(PathCache *pc, const char *name, int name_len, int literal); const char *pca_last_error(PathCache *pc); CPL_MATCH_FN(pca_path_completions); .fi .SH DESCRIPTION The \f3PathCache\f1 object is part of the tecla library (see the libtecla(@LIBR_MANEXT@) man page). .sp \f3PathCache\f1 objects allow an application to search for files in any colon separated list of directories, such as the unix execution PATH environment variable. Files in absolute directories are cached in a \f3PathCache\f1 object, whereas relative directories are scanned as needed. Using a \f3PathCache\f1 object, you can look up the full pathname of a simple filename, or you can obtain a list of the possible completions of a given filename prefix. By default all files in the list of directories are targets for lookup and completion, but a versatile mechanism is provided for only selecting specific types of files. The obvious application of this facility is to provide Tab-completion and lookup of executable commands in the unix PATH, so an optional callback which rejects all but executable files, is provided. .sp .SH AN EXAMPLE Under UNIX, the following example program looks up and displays the full pathnames of each of the command names on the command line. .sp .nf #include #include #include int main(int argc, char *argv[]) { int i; /* * Create a cache for executable files. */ PathCache *pc = new_PathCache(); if(!pc) exit(1); /* * Scan the user's PATH for executables. */ if(pca_scan_path(pc, getenv("PATH"))) { fprintf(stderr, "%s\\n", pca_last_error(pc)); exit(1); } /* * Arrange to only report executable files. */ pca_set_check_fn(pc, cpl_check_exe, NULL); /* * Lookup and display the full pathname of each of the * commands listed on the command line. */ for(i=1; i/dev/null # Check that the version components are all positive integers. for c in $major $minor $micro; do if echo "$c" | awk '{exit $1 ~ /^[0-9]+$/}'; then echo 'Version number components must all be positive integers.' exit 1 fi done # # Update the version number in the configure.in script. # ed -s configure.in << EOF /^MAJOR_VER=\"[0-9][0-9]*\"/ s/^.*$/MAJOR_VER=\"$major\"/ /^MINOR_VER=\"[0-9][0-9]*\"/ s/^.*$/MINOR_VER=\"$minor\"/ /^MICRO_VER=\"[0-9][0-9]*\"/ s/^.*$/MICRO_VER=\"$micro\"/ w q EOF if which autoconf 1>/dev/null 2>&1; then autoconf else echo 'Note that autoconf needs to be run.' fi # # Update the version number in the libtecla header file script. # ed -s libtecla.h << EOF /^#define TECLA_MAJOR_VER [0-9][0-9]*/ s/^.*$/#define TECLA_MAJOR_VER $major/ /^#define TECLA_MINOR_VER [0-9][0-9]*/ s/^.*$/#define TECLA_MINOR_VER $minor/ /^#define TECLA_MICRO_VER [0-9][0-9]*/ s/^.*$/#define TECLA_MICRO_VER $micro/ w q EOF # # Update the version number in the README file. # ed -s README << EOF /version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]* / s/version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*/version $major.$minor.$micro/ w q EOF # # Update the version number in the html index file. # ed -s html/index.html << EOF /version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./ s/version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*/version $major.$minor.$micro/g /libtecla-[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./ s/libtecla-[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./libtecla-$major.$minor.$micro./g w q EOF yuma123_2.14/libtecla/cplmatch.h0000664000175000017500000000373614770023131016657 0ustar vladimirvladimir#ifndef cplmatch_h #define cplmatch_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * This header is not for use by external applicatons. It contains * internal immplementation features of the libtecla library, which * may change incompatibly between releases. */ /* * Display a list of completions via a callback function. */ int _cpl_output_completions(CplMatches *result, GlWriteFn *write_fn, void *data, int term_width); #endif yuma123_2.14/libtecla/getline.h0000664000175000017500000000602214770023131016502 0ustar vladimirvladimir#ifndef getline_h #define getline_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * Set the name of the getline configuration file. */ #define TECLA_CONFIG_FILE "~/.teclarc" /* * The following macro returns non-zero if a character is * a control character. */ #define IS_CTRL_CHAR(c) ((unsigned char)(c) < ' ' || (unsigned char)(c)=='\177') /* * The following macro returns non-zero if a character is * a meta character. */ #define IS_META_CHAR(c) (((unsigned char)(c) & 0x80) && !isprint((int)(unsigned char)(c))) /* * Return the character that would be produced by pressing the * specified key plus the control key. */ #define MAKE_CTRL(c) ((c)=='?' ? '\177' : ((unsigned char)toupper(c) & ~0x40)) /* * Return the character that would be produced by pressing the * specified key plus the meta key. */ #define MAKE_META(c) ((unsigned char)(c) | 0x80) /* * Given a binary control character, return the character that * had to be pressed at the same time as the control key. */ #define CTRL_TO_CHAR(c) (toupper((unsigned char)(c) | 0x40)) /* * Given a meta character, return the character that was pressed * at the same time as the meta key. */ #define META_TO_CHAR(c) ((unsigned char)(c) & ~0x80) /* * Specify the string of characters other than the alphanumeric characters, * that are to be considered parts of words. */ #define GL_WORD_CHARS "_*\?\\[]" /* * Define the escape character, both as a string and as a character. */ #define GL_ESC_STR "\033" #define GL_ESC_CHAR '\033' #endif yuma123_2.14/libtecla/history.h0000664000175000017500000001266014770023131016561 0ustar vladimirvladimir#ifndef history_h #define history_h /* * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include /* FILE * */ /*----------------------------------------------------------------------- * This module is used to record and traverse historical lines of user input. */ typedef struct GlHistory GlHistory; /* * Create a new history maintenance object. */ GlHistory *_new_GlHistory(size_t buflen); /* * Delete a history maintenance object. */ GlHistory *_del_GlHistory(GlHistory *glh); int _glh_add_history(GlHistory *glh, const char *line, int force); int _glh_search_prefix(GlHistory *glh, const char *line, int prefix_len); char *_glh_find_backwards(GlHistory *glh, char *line, size_t dim); char *_glh_find_forwards(GlHistory *glh, char *line, size_t dim); int _glh_cancel_search(GlHistory *glh); char *_glh_oldest_line(GlHistory *glh, char *line, size_t dim); char *_glh_current_line(GlHistory *glh, char *line, size_t dim); /* * Whenever a new line is added to the history buffer, it is given * a unique ID, recorded in an object of the following type. */ typedef unsigned long GlhLineID; /* * Query the id of a history line offset by a given number of lines from * the one that is currently being recalled. If a recall session isn't * in progress, or the offset points outside the history list, 0 is * returned. */ GlhLineID _glh_line_id(GlHistory *glh, int offset); /* * Recall a line by its history buffer ID. If the line is no longer * in the buffer, or the specified id is zero, NULL is returned. */ char *_glh_recall_line(GlHistory *glh, GlhLineID id, char *line, size_t dim); /* * Write the contents of the history buffer to a given file. Note that * ~ and $ expansion are not performed on the filename. */ int _glh_save_history(GlHistory *glh, const char *filename, const char *comment, int max_lines); /* * Restore the contents of the history buffer from a given file. * Note that ~ and $ expansion are not performed on the filename. */ int _glh_load_history(GlHistory *glh, const char *filename, const char *comment, char *line, size_t dim); /* * Set and query the current history group. */ int _glh_set_group(GlHistory *glh, unsigned group); int _glh_get_group(GlHistory *glh); /* * Display the contents of the history list to the specified stdio * output group. */ int _glh_show_history(GlHistory *glh, GlWriteFn *write_fn, void *data, const char *fmt, int all_groups, int max_lines); /* * Change the size of the history buffer. */ int _glh_resize_history(GlHistory *glh, size_t bufsize); /* * Set an upper limit to the number of lines that can be recorded in the * history list, or remove a previously specified limit. */ void _glh_limit_history(GlHistory *glh, int max_lines); /* * Discard either all history, or the history associated with the current * history group. */ void _glh_clear_history(GlHistory *glh, int all_groups); /* * Temporarily enable or disable the history facility. */ void _glh_toggle_history(GlHistory *glh, int enable); /* * Lookup a history line by its sequential number of entry in the * history buffer. */ int _glh_lookup_history(GlHistory *glh, GlhLineID id, const char **line, unsigned *group, time_t *timestamp); /* * Query the state of the history list. */ void _glh_state_of_history(GlHistory *glh, int *enabled, unsigned *group, int *max_lines); /* * Get the range of lines in the history buffer. */ void _glh_range_of_history(GlHistory *glh, unsigned long *oldest, unsigned long *newest, int *nlines); /* * Return the size of the history buffer and the amount of the * buffer that is currently in use. */ void _glh_size_of_history(GlHistory *glh, size_t *buff_size, size_t *buff_used); /* * Get information about the last error in this module. */ const char *_glh_last_error(GlHistory *glh); /* * Return non-zero if a history search session is currently in progress. */ int _glh_search_active(GlHistory *glh); #endif yuma123_2.14/libtecla/demo3.c0000664000175000017500000006330514770023131016064 0ustar vladimirvladimir/* * Copyright (c) 2002, 2003, 2004 by Martin C. Shepherd * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include #include #include #ifdef HAVE_SELECT #ifdef HAVE_SYS_SELECT_H #include #endif #endif #include #include #include #include #include #include "libtecla.h" /* * The SignalActions object provides a way to temporarily install * a signal handler to a given set of signals, and later restore all * of the signal handlers that this displaced. */ typedef struct { int nsignal; /* The number of signals on the host OS */ sigset_t mask; /* The set of signals who's signal handlers */ /* are stored in the following actions[] */ /* array. */ struct sigaction *actions; /* An array of nsignal actions */ } SignalActions; static SignalActions *new_SignalActions(void); static SignalActions *del_SignalActions(SignalActions *si); static int displace_signal_handlers(SignalActions *si, sigset_t *mask, void (*handler)(int)); static int reinstate_signal_handlers(SignalActions *si); /* Return resources, restore the terminal to a usable state and exit */ static void cleanup_and_exit(GetLine *gl, SignalActions *si, int status); /* The function which displays the introductory text of the demo */ static void show_demo_introduction(GetLine *gl); /* A signal-aware version of select() */ static int demo_sigselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout, sigset_t *mask, SignalActions *si); /* * The following variables are accessed from signal handlers. Note * that these variables don't need to be either volatile or * sig_atomic_t because: * * 1. Outside of signal handlers we only access them when signal * delivery is blocked, so we know that no signal handlers can * be accessing them at that time. * * 2. When the signal handlers that set these variables are installed, * the sa_mask member of the sigaction structure is used to ensure * that only one instance of these signal handlers can be running * at a time, so we also know that there can't be simultaneous * accesses to them by multiple signal handlers. */ static GetLine *demo_gl; /* The line editor object */ static sigjmp_buf demo_setjmp_buffer; /* The sigsetjmp() buffer */ static int demo_setjmp_signo = -1; /* The signal that was caught */ /* Signal handlers */ static void demo_signal_handler(int signo); static void demo_setjmp_handler(int signo); /* * Set the amount of time that gl_get_line() should wait for I/O before * returning to let the external event loop continue. */ #define DEMO_IO_TIMEOUT 100000000 /* ns => 100ms */ /* The timeout handler */ static GL_TIMEOUT_FN(demo_timeout_fn); /*....................................................................... * This program demonstrates the use of gl_get_line() from an external * event loop. It takes no arguments. */ int main(int argc, char *argv[]) { int major,minor,micro; /* The version number of the library */ GetLine *gl=NULL; /* The resource object of gl_get_line() */ SignalActions *si=NULL; /* Temporary storage of displaced signal */ /* handlers. */ sigset_t all_signal_mask; /* The set of signals known by gl_get_line() */ /* * This program requires select(). */ #if !defined(HAVE_SELECT) fprintf(stderr, "The select() system call isn't available - aborting.\n"); exit(1); #else /* * Create the line editor, specifying a maximum line length of 500 bytes, * and 10000 bytes to allocate to storage of historical input lines. */ gl = demo_gl = new_GetLine(500, 5000); if(!gl) cleanup_and_exit(gl, si, 1); /* * Allocate an object in which to temporarily record displaced * signal handlers. */ si = new_SignalActions(); if(!si) cleanup_and_exit(gl, si, 1); /* * If the user has the LC_CTYPE or LC_ALL environment variables set, * enable display of characters corresponding to the specified locale. */ (void) setlocale(LC_CTYPE, ""); /* * Lookup and display the version number of the library. */ libtecla_version(&major, &minor, µ); printf( "\n Welcome to the server-mode demo program of libtecla version %d.%d.%d\n", major, minor, micro); /* * Display some introductory text, left-justifying it within the current * width of the terminal and enclosing it in a box of asterixes. */ show_demo_introduction(gl); /* * Load history. */ #ifndef WITHOUT_FILE_SYSTEM (void) gl_load_history(gl, "~/.demo_history", "#"); #endif /* * In this demo, rather than having gl_get_line() return immediately * when it would otherwise have to wait for I/O, we register a timeout * callback which causes gl_get_line() to give up waiting after a short * interval. */ gl_inactivity_timeout(gl, demo_timeout_fn, NULL, 0, DEMO_IO_TIMEOUT); /* * Install our signal handlers for process termination, suspension and * terminal resize signals. Ignore process continuation signals. */ gl_tty_signals(demo_signal_handler, demo_signal_handler, SIG_DFL, demo_signal_handler); /* * Get a list of all of the signals that gl_get_line() currently catches. */ gl_list_signals(gl, &all_signal_mask); /* * Switch gl_get_line() to non-blocking server mode. */ if(gl_io_mode(gl, GL_SERVER_MODE)) cleanup_and_exit(gl, si, 1); /* * Instruct gl_get_line() to unblock any signals that it catches * while waiting for input. Note that in non-blocking server mode, * this is only necessary when using gl_inactivity_timeout() to make * gl_get_line() block for a non-zero amount of time. */ gl_catch_blocked(gl); /* * Enter the event loop. */ while(1) { int nready; /* The number of file-descriptors that are */ /* ready for I/O */ fd_set rfds; /* The set of file descriptors to watch for */ /* readability */ fd_set wfds; /* The set of file descriptors to watch for */ /* writability */ /* * Construct the sets of file descriptors to be watched by select(), * starting from empty sets. */ FD_ZERO(&rfds); FD_ZERO(&wfds); /* * To ensure that no signals are received whos handlers might change * the requirements for the contents of the above signal sets, block * all of the signals that we are handling. */ sigprocmask(SIG_BLOCK, &all_signal_mask, NULL); /* * Depending on which direction of I/O gl_get_line()s is currently * waiting for, add the terminal file descriptor to either the set * of file descriptors to watch for readability, or those to watch * for writability. Note that at the start of a new line, such as * after an error, or the return of a completed line, we need to * wait for writability, so that a prompt can be written. */ switch(gl_pending_io(gl)) { case GLP_READ: FD_SET(STDIN_FILENO, &rfds); break; default: FD_SET(STDIN_FILENO, &wfds); break; }; /* * Wait for I/O to become possible on the selected file descriptors. * The following is a signal-aware wrapper around the select() system * call. This wrapper guarantees that if any of the signals marked in * all_signal_mask arrive after the statement above where we blocked * these signals, it will detect this and abort with nready=-1 and * errno=EINTR. If instead, we just unblocked the above signals just * before calling a normal call to select(), there would be a small * window of time between those two statements in which a signal could * arrive without aborting select(). This would be a problem, since * the functions called by our signal handler may change the type * of I/O that gl_get_line() wants us to wait for in select(). */ nready = demo_sigselect(STDIN_FILENO + 1, &rfds, &wfds, NULL, NULL, &all_signal_mask, si); /* * We can now unblock our signals again. */ sigprocmask(SIG_UNBLOCK, &all_signal_mask, NULL); /* * Did an I/O error occur? */ if(nready < 0 && errno != EINTR) cleanup_and_exit(gl, si, 1); /* * If the terminal file descriptor is now ready for I/O, call * gl_get_line() to continue editing the current input line. */ if(FD_ISSET(STDIN_FILENO, &rfds) || FD_ISSET(STDIN_FILENO, &wfds)) { /* * Start or continue editing an input line. */ char *line = gl_get_line(gl, "$ ", NULL, 0); /* * Did the user finish entering a new line? */ if(line) { /* * Before writing messages to the terminal, start a new line and * switch back to normal terminal I/O. */ gl_normal_io(gl); /* * Display what was entered. */ if(printf("You entered: %s", line) < 0 || fflush(stdout)) break; /* * Implement a few simple commands. */ if(strcmp(line, "exit\n")==0) cleanup_and_exit(gl, si, 0); else if(strcmp(line, "history\n")==0) gl_show_history(gl, stdout, "%N %T %H\n", 0, -1); else if(strcmp(line, "size\n")==0) { GlTerminalSize size = gl_terminal_size(gl, 80, 24); printf("Terminal size = %d columns x %d lines.\n", size.ncolumn, size.nline); } else if(strcmp(line, "clear\n")==0) { if(gl_erase_terminal(gl)) return 1; }; /* * To resume command-line editing, return the terminal to raw, * non-blocking I/O mode. */ gl_raw_io(gl); /* * If gl_get_line() returned NULL because of an error or end-of-file, * abort the program. */ } else if(gl_return_status(gl) == GLR_ERROR || gl_return_status(gl) == GLR_EOF) { cleanup_and_exit(gl, si, 1); }; }; }; #endif return 0; } /*....................................................................... * This function is called to return resources to the system and restore * the terminal to its original state before exiting the process. * * Input: * gl GetLine * The line editor. * si SignalActions * The repository for displaced signal handlers. * status int The exit code of the process. */ static void cleanup_and_exit(GetLine *gl, SignalActions *si, int status) { /* * Restore the terminal to its original state before exiting the program. */ gl_normal_io(gl); /* * Save historical command lines. */ #ifndef WITHOUT_FILE_SYSTEM (void) gl_save_history(gl, "~/.demo_history", "#", -1); #endif /* * Clean up. */ gl = del_GetLine(gl); si = del_SignalActions(si); /* * Exit the process. */ exit(status); } /*....................................................................... * This is a signal-aware wrapper around the select() system call. It * is designed to facilitate reliable signal handling of a given set * of signals, without the race conditions that would usually surround * the use of select(). See the "RELIABLE SIGNAL HANDLING" section of * the gl_get_line(3) man page for further details. * * Provided that the calling function has blocked the specified set of * signals before calling this function, this function guarantees that * select() will be aborted by any signal that arrives between the * time that the caller blocked the specified signals and this * function returns. On return these signals will again be blocked to * prevent any signals that arrive after select() returns, from being * missed by the caller. * * Note that this function is written not to be specific to this * program, and is thus suitable for use in other programs, whether or * not they use gl_get_line(). * * Also note that this function depends on the NSIG preprocessor * constant being >= the maximum number of signals available on the * host operating system. Under BSD and SysV, this macro is set * appropriately in signal.h. On other systems, a reasonably large * guess should be substituted. Although nothing terrible will happen * if a value that is too small is chosen, signal numbers that exceed * the specified value of NSIG will be ignored by this function. A * more robust method than depending on nsig would be to use the * POSIX sigismember() function to count valid signals, and use this * to allocate the array of sigaction structures used to preserve * * * Input: * n int The number of file descriptors to pay * attention to at the start of each of the * following sets of file descriptors. * readfds fd_set * The set of file descriptors to check for * readability, or NULL if not pertinent. * wwritefds fd_set * The set of file descriptors to check for * writability, or NULL if not pertinent. * exceptfds fd_set * The set of file descriptors to check for * the arrival of urgent data, or NULL if * not pertinent. * timeout struct timeval * The maximum time that select() should * wait, or NULL to wait forever. * mask sigset_t * The set of signals to catch. * si SignalHandlers * An object in which to preserve temporary * copies signal handlers. * Output: * return int > 0 The number of entries in all of the * sets of descriptors that are ready * for I/O. * 0 Select() timed out. * -1 Error (see errno). */ static int demo_sigselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout, sigset_t *mask, SignalActions *si) { /* * The reason that the the following variables are marked as volatile * is to prevent the compiler from placing their values in registers * that might not be saved and restored by sigsetjmp(). */ volatile sigset_t old_mask; /* The displaced process signal mask */ volatile int status; /* The return value of select() */ /* * Make sure that all of the specified signals are blocked. This is * redundant if the caller has already blocked signals. */ if(sigprocmask(SIG_BLOCK, mask, (sigset_t *) &old_mask) < 0) return -1; /* * Record the fact that no signal has been caught yet. */ demo_setjmp_signo = -1; /* * Now set up the point where our temporary signal handlers will return * control if a signal is received. */ if(sigsetjmp(demo_setjmp_buffer, 1) == 0) { /* * Now install the temporary signal handlers that cause the above * sigsetjmp() to return non-zero when a signal is detected. */ if(displace_signal_handlers(si, mask, demo_setjmp_handler)) { reinstate_signal_handlers(si); return 1; }; /* * Now that we are ready to catch the signals, unblock them. */ sigprocmask(SIG_UNBLOCK, mask, NULL); /* * At last, call select(). */ status = select(n, readfds, writefds, exceptfds, timeout); /* * Block the specified signals again. */ sigprocmask(SIG_BLOCK, mask, NULL); /* * Record the fact that no signal was caught. */ demo_setjmp_signo = -1; }; /* * We can get to this point in one of two ways. Either no signals were * caught, and the above block ran to completion (with demo_setjmp_signo=-1), * or a signal was caught that caused the above block to be aborted, * in which case demo_setjmp_signo will now equal the number of the signal that * was caught, and sigsetjmp() will have restored the process signal * mask to how it was before it was called (ie. all of the specified * signals blocked). * * First restore the signal handlers to how they were on entry to * this function. */ reinstate_signal_handlers(si); /* * Was a signal caught? */ if(demo_setjmp_signo > 0) { sigset_t new_mask; /* * Send the signal again, then unblock its delivery, so that the application's * signal handler gets invoked. */ raise(demo_setjmp_signo); sigemptyset(&new_mask); sigaddset(&new_mask, demo_setjmp_signo); sigprocmask(SIG_UNBLOCK, &new_mask, NULL); /* * Set the return status to show that a signal was caught. */ errno = EINTR; status = -1; }; /* * Now restore the process signal mask to how it was on entry to this * function. */ sigprocmask(SIG_SETMASK, (sigset_t *) &old_mask, NULL); return status; } /*....................................................................... * This is the main signal handler of this demonstration program. If a * SIGINT is received by the process, it arranges that the next call * to gl_get_line() will abort entry of the current line and start * entering a new one. Otherwise it calls the library function which * handles terminal resize signals and process suspension and process * termination signals. Both of the functions called by this signal * handler are designed to be async-signal safe, provided that the * rules laid out in the gl_io_mode(3) man page are followed. */ static void demo_signal_handler(int signo) { if(signo==SIGINT) gl_abandon_line(demo_gl); else gl_handle_signal(signo, demo_gl, 1); } /*....................................................................... * The following signal handler is installed while select() is being * called from within a block of code protected by sigsetjmp(). It * simply records the signal that was caught in setjmp_signo, then * causes the sigsetjmp() to return non-zero. */ static void demo_setjmp_handler(int signo) { demo_setjmp_signo = signo; siglongjmp(demo_setjmp_buffer, 1); } /*....................................................................... * This optional inactivity timeout function is used in this * demonstration to cause gl_get_line() to wait for a small amount of * time for I/O, before returning and allowing the event loop to * continue. This isn't needed if you want gl_get_line() to return * immediately, rather than blocking. */ static GL_TIMEOUT_FN(demo_timeout_fn) { return GLTO_CONTINUE; } /*....................................................................... * Display introductory text to the user, formatted according to the * current terminal width and enclosed in a box of asterixes. * * Input: * gl GetLine * The resource object of gl_get_line(). */ static void show_demo_introduction(GetLine *gl) { int start; /* The column in which gl_display_text() left the cursor */ int i; /* * Break the indtroductory text into an array of strings, so as to * avoid overflowing any compiler string limits. */ const char *doc[] = { "To the user this program appears to act identically to the main ", "demo program. However whereas the code underlying the main demo ", "program uses gl_get_line() in its default configuration, where each ", "call blocks the caller until the user has entered a complete input ", "line, demo3 uses gl_get_line() in its non-blocking server mode, ", "where it must be called repeatedly from an external ", "event loop to incrementally accept entry of the input ", "line, as and when terminal I/O becomes possible. The well commented ", "source code of demo3, which can be found in demo3.c, thus provides ", "a working example of how to use gl_get_line() in a manner that ", "doesn't block the caller. Documentation of this mode can be found ", "in the gl_io_mode(3) man page.\n" }; /* * Form the top line of the documentation box by filling the area of * the line between a " *" prefix and a "* " suffix with asterixes. */ printf("\n"); gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); /* * Justify the documentation text within margins of asterixes. */ for(start=0,i=0; i= 0; i++) start = gl_display_text(gl, 0, " * ", " * ", ' ', 80, start,doc[i]); /* * Draw the bottom line of the documentation box. */ gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); printf("\n"); } /*....................................................................... * This is a constructor function for an object who's role is to allow * a signal handler to be assigned to potentially all available signals, * while preserving a copy of the original signal handlers, for later * restration. * * Output: * return SignalActions * The new object, or NULL on error. */ static SignalActions *new_SignalActions(void) { SignalActions *si; /* The object to be returned */ /* * Allocate the container. */ si = malloc(sizeof(SignalActions)); if(!si) { fprintf(stderr, "new_SignalActions: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_SignalActions(). */ si->nsignal = 0; sigemptyset(&si->mask); si->actions = NULL; /* * Count the number of signals that are available of the host * platform. Note that si->mask has no members set, and that * sigismember() is defined to return -1 if the signal number * isn't valid. */ for(si->nsignal=1; sigismember(&si->mask, si->nsignal) == 0; si->nsignal++) ; /* * Allocate the array of sigaction structures to use to keep a record * of displaced signal handlers. */ si->actions = (struct sigaction *) malloc(sizeof(*si->actions) * si->nsignal); if(!si->actions) { fprintf(stderr, "Insufficient memory for %d sigaction structures.\n", si->nsignal); return del_SignalActions(si); }; return si; } /*....................................................................... * Delete a SignalActions object. * * Input: * si SignalActions * The object to be deleted. * Output: * return SignalActions * The deleted object (always NULL). */ static SignalActions *del_SignalActions(SignalActions *si) { if(si) { if(si->actions) free(si->actions); free(si); }; return NULL; } /*....................................................................... * Replace the signal handlers of all of the signals in 'mask' with * the signal handler 'handler'. * * Input: * si SignalActions * The object in which to record the displaced * signal handlers. * mask sigset_t * The set of signals who's signal handlers * should be displaced. * handler void (*handler)(int) The new signal handler to assign to each * of the signals marked in 'mask'. * Output: * return int 0 - OK. * 1 - Error. */ static int displace_signal_handlers(SignalActions *si, sigset_t *mask, void (*handler)(int)) { int signo; /* A signal number */ struct sigaction action; /* The new signal handler */ /* * Mark the fact that so far we haven't displaced any signal handlers. */ sigemptyset(&si->mask); /* * Set up the description of the new signal handler. Note that * we make sa_mask=mask. This ensures that only one instance of the * signal handler will ever be running at one time. */ action.sa_handler = handler; memcpy(&action.sa_mask, mask, sizeof(*mask)); action.sa_flags = 0; /* * Check each of the available signals to see if it is specified in 'mask'. * If so, install the new signal handler, record the displaced one in * the corresponding element of si->actions[], and make a record in * si->mask that this signal handler has been displaced. */ for(signo=1; signo < si->nsignal; signo++) { if(sigismember(mask, signo)) { if(sigaction(signo, &action, &si->actions[signo]) < 0) { fprintf(stderr, "sigaction error (%s)\n", strerror(errno)); return 1; }; sigaddset(&si->mask, signo); }; }; return 0; } /*....................................................................... * Reinstate any signal handlers displaced by displace_signal_handlers(). * * Input: * sig SignalActions * The object containing the displaced signal * handlers. * Output: * return int 0 - OK. * 1 - Error. */ static int reinstate_signal_handlers(SignalActions *si) { int signo; /* A signal number */ /* * Check each of the available signals to see if it is specified in * si->mask. If so, reinstate the displaced recorded in the * corresponding element of si->actions[], and make a record in * si->mask that this signal handler has been reinstated. */ for(signo=1; signo < si->nsignal; signo++) { if(sigismember(&si->mask, signo)) { if(sigaction(signo, &si->actions[signo], NULL) < 0) { fprintf(stderr, "sigaction error (%s)\n", strerror(errno)); return 1; }; sigdelset(&si->mask, signo); }; }; return 0; } yuma123_2.14/netconf/0000775000175000017500000000000014770023131014557 5ustar vladimirvladimiryuma123_2.14/netconf/data/0000775000175000017500000000000014770023131015470 5ustar vladimirvladimiryuma123_2.14/netconf/data/py_header.txt0000664000175000017500000000063214770023131020172 0ustar vladimirvladimir# -*- coding: utf-8 -*- """ * Copyright (c) 2010, Netconf Central, Inc. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. """ yuma123_2.14/netconf/data/c_header.txt0000664000175000017500000000060114770023131017760 0ustar vladimirvladimir/* * Copyright (c) 2010, Netconf Central, Inc. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ yuma123_2.14/netconf/data/test/0000775000175000017500000000000014770023131016447 5ustar vladimirvladimiryuma123_2.14/netconf/data/test/test.xml0000664000175000017500000000027614770023131020155 0ustar vladimirvladimir 44 yuma123_2.14/netconf/data/test.xml0000664000175000017500000000101114770023131017162 0ustar vladimirvladimir lo eth0 virbr0 yuma123_2.14/netconf/data/yangdump-css-contents.txt0000664000175000017500000002216414770023131022503 0ustar vladimirvladimir /* entire contents of /trunk/ncorg/ncorg/static/css/style.css * if missing, this file on netconfcentral will be referenced * http://www.netconfcentral.com/static/css/sytle.css */ html, body { background-color: white; margin: 0; padding: 0; font-size: 10pt; } div.ncmain { margin-left: .5in; margin-bottom: .5in; margin-right: .5in; width: 90%; } div.ncmain h2 { text-align: center; } div.nchello { width: 5.00in; height: 1.7in; border: 2px solid black; padding-top: 1px; padding-left: 5px; padding-bottom: 10px; padding-right: 5px; margin-top: .75in; margin-left: 1.5in; margin-bottom: .5in; margin-right: 1in; text-align: center; background-color: #fffed6; font-size: 115%; } div.nchello h4 { padding-left: 10px; text-align: left; } div.nchello_home { padding-top: 1px; padding-left: 5px; padding-bottom: 10px; padding-right: 5px; margin-top: .75in; margin-left: .5in; margin-bottom: .5in; margin-right: 1in; text-align: center; } div.nchello_home h3 { text-align: center; } div.download { width: 80%; font-size: 100%; padding-top: 1px; padding-left: 5px; padding-bottom: 10px; padding-right: 5px; margin-top: .75in; margin-left: .5in; margin-bottom: .5in; margin-right: 1in; text-align: left; } div.download p { font-size: 100%; } table.ncheader { width: 100%; height: .3in; background: inherit; border: none; } table.ncheader a { border: none; } table.ncheader img { padding: none; border: none; } td.ncheader_right { padding-left: 1in; } td.ncheader_right h1 { text-align: left; } div.ncresults { padding-left: 1in; } div.ncresults h1 { text-align: left; } div.ncbanner { width: 100%; font-size: 100%; padding-top: 1px; padding-bottom: 1px; padding-left: 8px; } div.ncsearch { margin: .5in; width: 90%; text-align: left; } div.ncsearch h2 { text-align: center; } fieldlabel { text-align: right; } #nav, #nav ul { float: left; width: 100%; list-style: none; line-height: 1; background: white; font-weight: bold; font-size: 100%; padding: 0; border: solid #eda; border-width: 1px 0; margin: 0 0 1em 0; background-color: #fffed6; } #nav a { display: block; width: 10em; w\idth: 6em; color: #7C6240; text-decoration: none; padding: 0.25em 2em; } #nav a.daddy { background: url('../images/rightarrow2.gif') center right no-repeat; padding-top: 1px; padding-bottom: 1px; } /* the url clause above does not work, but the following clause does work */ li.daddy { background: url('../images/rightarrow2.gif') center right no-repeat; padding-top: 1px; padding-bottom: 1px; } #nav li { float: left; padding: 0; width: 10em; } #nav li ul { position: absolute; left: -999em; height: auto; width: 14.4em; w\idth: 13.9em; font-weight: normal; border-width: 0.25em; margin: 0; } #nav li li { padding-right: 1em; width: 13em } #nav li ul a { width: 13em; w\idth: 9em; } #nav li ul ul { margin: -1.75em 0 0 14em; } #nav li:hover ul ul, #nav li:hover ul ul ul, #nav li:hover ul ul ul ul, #nav li.sfhover ul ul, #nav li.sfhover ul ul ul, #nav li.sfhover ul ul ul ul { left: -999em; } #nav li:hover ul, #nav li li:hover ul, #nav li li li:hover ul, #nav li li li li:hover ul, #nav li.sfhover ul, #nav li li.sfhover ul, #nav li li li.sfhover ul, #nav li li li li.sfhover ul { left: auto; } #nav li:hover, #nav li.sfhover { background: #eda; } #main_content { color: black; font-size: 100%; width: 800px; margin: 0 auto 0 auto; padding: 2px; margin-top: .2in; } #nclist { margin: 0; margin-top: 1px; margin-bottom: 1px; width: 100%; } #nclist table { margin: 0; } #nclist tr { margin: 0; } #nclist td { text-align: left; } #nclist pre { margin: 0; } #nclist_banner { margin: 0; padding-top: 5px; margin-top: 3px; margin-bottom: 2px; font-size: 125%; color: red; text-align: left; } #nclist h2 { margin: 0; padding-top: 5px; margin-top: 3px; margin-bottom: 2px; font-size: 125%; color: red; text-align: left; } #nclist_hdr { font-size: 120%; color: red; } h1,h2,h3,h4,h5,h6 { font-family: "Century Schoolbook L", Georgia, serif; font-weight: bold; } h1 { text-align: center; } h2 { font-size: 130%; } #footer { color: #999; font-size: 77%; text-align: center; width: 757px; padding: 10px; margin: 0 auto 1em auto; } #footer p { padding: 0; margin-top: 1px; margin-bottom: 1px; } .code { font-family: monospace; } span.code { font-weight: bold; background: #eee; } pre.code { font-weight: bold; background: #eee; } #status_block { margin: 0 auto 0.5em auto; padding: 15px 10px 15px 55px; background: #cec URL('../images/ok.png') left center no-repeat; border: 1px solid #9c9; width: 450px; font-size: 120%; font-weight: bolder; } .notice { margin: 0.5em auto 0.5em auto; padding: 15px 10px 15px 55px; width: 450px; background: #eef URL('../images/info.png') left center no-repeat; border: 1px solid #cce; } .fielderror { color: red; font-weight: bold; } #about h3 { margin: 0; margin-bottom: 10px; font-size: 14px; } #about-content { background-color: #ffd; border: 1px solid #fc0; margin-left: -11px; } #about-content table { margin-top: 10px; margin-bottom: 10px; font-size: 11px; border-collapse: collapse; } #about-content td { padding: 10px; padding-top: 3px; padding-bottom: 3px; } #about-content td.name {color: #555} #about-content td.value {color: #000} #about-content.failure { background-color: #fcc; border: 1px solid #f00; } #about-content.failure p { margin: 0; padding: 10px; } a:link { color: blue; } a:visited { color: blue; } a:active { color: red; } a:hover { color: red; text-decoration: underline; } #tabheader { font-weight: bold; font-size: 120%; padding-top: 2px; padding-bottom: 2px; padding-left: 1px; padding-right: 1px; text-align: center; } #tablist { width:100%; } #tablist_odd { min-width: 80px; text-wrap: suppress; padding: 2px 2px 2px 2px; } #tablist_even { min-width: 80px; text-wrap: suppress; padding: 2px 2px 2px 2px; background-color: #fbfadb; } #tablist_m { font-size: 70%; padding-top: 4px; padding-bottom: 4px; padding-left: 80%; } #tablist_m1 { padding: 2px 2px 2px 2px; background-color: #ec9793; } #tablist_m2 { padding: 2px 2px 2px 2px; background-color: #c5f5b9; } #tablist_m3 { padding: 2px 2px 2px 2px; background-color: #c3f0f5; } #tableform { width: 75%; border: 2px solid black; padding: 25px; margin-top: .75in; margin-left: .5in; margin-bottom: .5in; margin-right: 1in; text-align: left; /* background-color: #fffed6; */ } #tableform h2 { text-align: center; } #tableform h3 { text-align: center; color: red; } div.formtab { background-color: white; } form.listform { text-align: left; padding: 4px; margin: 4px; } form.listform li { text-align: left; padding: 4px; margin: 4px; } div.ncdocs { padding-top: 1px; padding-left: 5px; padding-bottom: 10px; padding-right: 5px; margin-top: .3in; margin-left: .5in; margin-bottom: .5in; margin-right: 1in; text-align: center; font-size: 10pt; } div.ncdocs table { text-align: left; } div.ncdocs td { padding-left: 4px; } div.ncdocs h2 { text-align: left; } div.ncdocs h3 { text-align: left; } div.ncdocs h4 { text-align: left; } div.ncdocs ol { text-align: left; margin-left: .5in; } div.ncdocs p { text-align: left; font-size: 100%; margin-left: .3in; width: 90%; } div.ncdocs pre { text-align: left; font-size: 85%; margin-left: .5in; width: 90%; border: 1px solid black; padding-top: 5px; background: #e5e5e5; } div.ncdocs ul { text-align: left; font-size: 100%; margin-left: .4in; width: 80%; } a.yang_kw:link { color: blue; background-color: transparent; } a.yang_kw:visited { color: blue; background-color: transparent; } a.yang_kw:active { color: blue; background-color: transparent; } a.yang_id:link { color: red; background-color: transparent; } a.yang_id:visited { color: red; background-color: transparent; } a.yang_id:active { color: red; background-color: transparent; } div.yang { width: 80%; background-color: white; font-size: 9pt; color: black; margin: 10px 10px 10px .4in; padding: 1px 10px 10px 1px; } h1.yang { font-size: 150%; font-weight: bold; color: black; text-align: center; padding: 10px 10px 1px 1px; } pre.yang { font-family: monospace; } span.yang_kw { color: blue; } span.yang_id { color: red; } span.yang_id a { color: red; } span.yang_cmt { color: brown; } span.yang_str { color: green; } div.tocplain { width: 80%; font-size: 80%; padding-top: 1px; padding-bottom: 1px; padding-left: 8px; } div.tocmenu { width: 80%; font-size: 50%; padding-top: 1px; padding-bottom: 1px; padding-left: 8px; } yuma123_2.14/netconf/doc/0000775000175000017500000000000014770023131015324 5ustar vladimirvladimiryuma123_2.14/netconf/doc/extra/0000775000175000017500000000000014770023131016447 5ustar vladimirvladimiryuma123_2.14/netconf/doc/extra/AUTHORS0000664000175000017500000000007514770023131017521 0ustar vladimirvladimirYuma is written by: * Andy Bierman yuma123_2.14/netconf/doc/extra/README0000664000175000017500000000553114770023131017333 0ustar vladimirvladimir Yuma -- A Toolset for the YANG data modeling language and NETCONF protocol ========================================================================== Copyright (c) 2008 - 2012, Andy Bierman Copyright (c) 2012, YumaWorks, Inc. See the file "/usr/share/doc/yuma/COPYRIGHT" for information on usage and redistribution of this toolset, and for a DISCLAIMER OF ALL WARRANTIES. Source: ------- http://www.netconfcentral.org/ FILES: ------ - a binary linux program called 'yangdump' /usr/bin/yangdump - a binary linux program called 'yangdiff' /usr/bin/yangdiff - a binary linux program called 'yangcli' /usr/bin/yangcli - a binary linux program called 'netconfd' /usr/sbin/netconfd - a binary linux program called 'netconf-subsystem' /usr/sbin/netconf-subsystem - a binary linux library called 'libncx.so' /usr/lib/libncx.so - a binary linux library called 'libagt.so' /usr/lib/libagt.so - a binary linux library called 'libtoaster.so' /usr/lib/yuma/libtoaster.so - documentation pages /usr/share/doc/yuma/* - a set of freely available YANG modules /usr/share/yuma/modules/* - a set of sample CLI 'conf' files for the yuma programs /etc/yuma/etc/* (copy foo-sample.conf to foo.conf to enable) - a set of man page files for the yuma programs /usr/man/man1/* - developer utilities /usr/share/yuma/util/* - yangcli user-history file $HOME/.yuma/.yangcli_history - yangcli user-specific temporary files $HOME/.yuma/tmp/* Some of the YANG modules includes with this software were generated with the smidump program, available at: http://www.ibr.cs.tu-bs.de/projects/libsmi/ WARNING ======= This version of yuma implements the YANG language specification as defined in RFC 6020: http://www.ietf.org/rfc/rfc6020.txt IMPORTANT INSTALLATION NOTE =========================== ************************************************************** * * * These programs will not function unless the YANG * * modules are properly installed and available in * * the module path. * * * * These files must be installed in one of the default * * locations, such as ~/modules or /usr/share/yuma/modules * * or the YUMA_INSTALL or YUMA_HOME environment variable * * must be properly set. * * * * These files are used for CLI and config file processing. * * * ************************************************************** Refer to the manuals in the /usr/share/doc/yuma/ directories for installation and operation instructions. file:///usr/share/doc/yuma/index.html yuma123_2.14/netconf/doc/extra/yuma-legal-notices.txt0000664000175000017500000000625514770023131022717 0ustar vladimirvladimir'''Yuma ''Legal Notices '''''Copyright 2009 - 2012, Andy Bierman ALL RIGHTS RESERVED. All Yuma distributions include code from the following sources: The '''libxml2''' library is licensed from http://xmlsoft.org/ under the MIT License. -------------- The '''libssh2''' library is licensed from http://www.libssh2.org/ under the New BSD License (http://www.ohloh.net/licenses/bsd_ish). --------------- The '''ncurses''' library is licensed from http://www.gnu.org/software/ncurses/ under the GNU/Linux license. ---------------- The '''libtecla''' library is licensed from http://www.astro.caltech.edu/~mcs/tecla/ under the license found at http://www.astro.caltech.edu/~mcs/tecla/LICENSE.TERMS ---------------- The base64 content transfer encoding functions in '''ncx/b64.c''' are by Bob Trower, under the MIT license. MODULE NAME: b64.c AUTHOR: Bob Trower 08/04/01 PROJECT: Crypt Data Packaging COPYRIGHT: Copyright (c) Trantor Standard Systems Inc., 2001 LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------- The bobhash function in '''ncx/bobhash.c '''implements the BOB hash algorithm that is originally published as Public domain code in the 1997 Dr Dobbs article "By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this code any way you wish, private, educational, or commercial. It's free." This function is referenced in - From Packet Sampling Techniques RFC 5475 - implemented from ----------------- Notice to U.S. Government End Users: . RESTRICTED RIGHTS LEGEND. This software is provided with RESTRICTED RIGHTS. Use, duplication, or disclosure by the U.S. Government is subject to restrictions set forth in subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial Computer Software - Restricted Rights at 48 CFR 52.227-19, as applicable. The "Manufacturer" for purposes of these regulations is Netconf Central, Inc. 374 Laguna Terrace, Simi Valley, CA 93065 USA. yuma123_2.14/netconf/doc/extra/COPYRIGHT0000664000175000017500000000410314770023131017740 0ustar vladimirvladimirThis package was debianized by Andy Bierman Thu, 09 Aug 2012 12:20:00 -0800 It was downloaded from Upstream Author: Andy Bierman Copyright: Copyright (C) 2008 - 2012 by Andy Bierman Copyright (C) 2012 by YumaWorks, Inc. License: BSD Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andy Bierman BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. See the file `yuma-legal-notices.pdf', installed in the '/usr/share/doc/yuma' directory for additional license information pertaining to external packages used by some Yuma programs. Packaging: Copyright (C) 2012, YumaWorks, Inc. released under the BSD license. yuma123_2.14/netconf/doc/yuma_docs/0000775000175000017500000000000014770023131017307 5ustar vladimirvladimiryuma123_2.14/netconf/doc/yuma_docs/yuma-quickstart-guide.txt0000664000175000017500000015352514770023131024321 0ustar vladimirvladimir
'''Yuma Quickstart Guide'''
YANG-Based Unified Modular Automation Tools
Client/Server Quickstart Guide
Version yuma123-2.13
= Preface = == Legal Statements == Copyright 2009 - 2012, Andy Bierman, All Rights Reserved. Copyright 2013 - 2022, Vladimir Vassilev, All Rights Reserved. == Additional Resources == This document assumes you have successfully set up the software as described in the printed document: [[Yuma Installation Guide]] Other documentation includes: [[Yuma User Manual]] [[Yuma netconfd Manual]] [[Yuma yangcli Manual]] [[Yuma Developer Manual]] There are several sources of free information and tools for use with YANG and/or NETCONF. The following section lists the resources available at this time. === WEB Sites === * '''Netconf Central''' ** [http://www.netconfcentral.org/ http://www.netconfcentral.org/] ** Yuma Home Page ** Free information on NETCONF and YANG, tutorials, on-line YANG module validation and documentation database * '''Yuma123 SourceForge open source project''' ** [http://sourceforge.net/projects/yuma123/ http://sourceforge.net/projects/yuma123/] ** Download Yuma source and documentation * '''Yang Central''' ** [http://www.yang-central.org/ http://www.yang-central.org] ** Free information and tutorials on YANG, free YANG tools for download * '''NETCONF Working Group Wiki Page''' ** [http://trac.tools.ietf.org/wg/netconf/trac/wiki http://trac.tools.ietf.org/wg/netconf/trac/wiki] ** Free information on NETCONF standardization activities and NETCONF implementations * '''NETCONF WG Status Page''' ** http://tools.ietf.org/wg/netconf/ ** IETF Internet draft status for NETCONF documents * '''libsmi Home Page''' ** [http://www.ibr.cs.tu-bs.de/projects/libsmi/ http://www.ibr.cs.tu-bs.de/projects/libsmi/] ** Free tools such as smidump, to convert SMIv2 to YANG * '''YumaWorks''' ** [http://www.yumaworks.com/ http://www.yumaworks.com] ** Offers support, training, and consulting for Yuma. ** Offers YumaPro, a professional version of Yuma that includes concurrency, external database support, sub-agent support, multiple northbound interfaces, and more. API compatible with Yuma. Availability: September, 2012. Licensed. * '''Transpacket''' ** [http://www.transpacket.com/ http://www.transpacket.com] ** Offers Linux based embedded operating system distribution which uses Yuma for configuration and monitoring. ** Offers support, training, and consulting for YANG and netconf. === Mailing Lists === * '''NETCONF Working Group''' ** http://www.ietf.org/html.charters/netconf-charter.html ** Technical issues related to the NETCONF protocol are discussed on the NETCONF WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. * '''NETMOD Working Group''' ** [http://www.ietf.org/html.charters/netmod-charter.html http://www.ietf.org/html.charters/netmod-charter.html] ** Technical issues related to the YANG language and YANG data types are discussed on the NETMOD WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. == Conventions Used in this Document == The following formatting conventions are used throughout this document: {| class="wikitable" border="1" !Convention !Description |- | '''--foo''' | CLI parameter foo |- | '''''' | XML parameter foo |- | '''foo''' | '''yangcli''' command or parameter |- | '''$FOO''' | Environment variable FOO |- | '''$$foo''' | '''yangcli''' global variable foo |- | some text | Example command or PDU |- | some text | Plain text |} = Introduction = [[Image:yuma-tools.png]] Refer to section 3 of the [[Yuma User Manual]] for a complete introduction to Yuma Tools. This section focuses on the client and server tools within the Yuma Tools programs. == Intended Audience == This document is intended for users of the Yuma Tools NETCONF client and server programs. It covers the basic usage of the '''yangcli''' client application and the '''netconfd''' server. == What is NETCONF and YANG? == The Yuma Tools suite provides automated support for development and usage of network management information. Information is exchanged in XML encoding within a session between a client and a server. The IETF "Network Configuration Protocol" (NETCONF) is used to provide the management sessions, operations, and database framework available on the server. The operations, notifications, and the database contents supported by a particular NETCONF server are extensible, and defined with a modular and easy-to-learn language called YANG. The database is used to contain YANG data structures which represent the configuration of the device containing the NETCONF server. This configuration can be saved in non-volatile storage so the configuration can be restored upon reboot. The IETF "YANG Data Modeling Language" is used to define the syntax and semantics of the NETCONF operations, notification events, and database content. Machine and human readable semantics and constraints are used by YANG tools (including Yuma Tools) to automate behavior within the NETCONF protocol for clients and servers. For people familiar with SNMP and SMIv2, NETCONF is like an XML-based, high-level version of SNMP, and a YANG module is like a MIB module, except MIB tables can be nested and much more complex than in SMIv2. Instead of Enterprise IDs and OBJECT-IDENTIFIERs, YANG uses XML namespaces and XPath path expressions to identify module ownership and contents within the protocol PDUs. == How Does an Operator Use NETCONF and YANG? == An operator uses a NETCONF session almost like it was a CLI session, except there are structured, schema-defined requests and responses, encoded in XML. YANG modules are like MIB modules for CLI content. Instead of ad-hoc unstructured documentation like CLI, NETCONF uses a data definition language to define management modules. The actual modules that a server supports will vary, just like MIB (SMIv2) modules. The NETCONF protocol is available for many different transports. The most popular is the SSH2 protocol. The 'netconf' subsystem is used (on TCP port 830) to start a special SSH session with the NETCONF server. Using NETCONF over SSH is just like using CLI over SSH to manage a networking device, except the messages are exchanged in XML, not plain-text. SSH user names and passwords are used for session authentication and authorization. NETCONF is designed to provide a programmatic interface, so it is usually used with a management application, instead of a direct (raw) SSH terminal application. The '''yangcli''' program within Yuma Tools is a YANG-driven NETCONF client application that supports scripts, XPath, and many automated features to simplify management of NETCONF servers. Once a session is started, similar to a CLI session, the operator issues commands (NETCONF operations) to the server, and the server performs each requested operation in order, and returns a status message and/or some data to the client. Notifications can also be received, if the session has requested them with the operation. When a NETCONF session starts, a message is sent by the server that has all the NETCONF capabilities and YANG modules supported by the server. Capabilities are optional protocol mechanisms, beyond those defined in the base protocol (RFC 4741, RFC 6241). The client application knows what operations, notification events, and database contents are supported on the server, based on the information in the message. NETCONF has a set of basic database (CRUD) operations for managing the configuration database. In addition, any YANG module can define new protocol operations and notification events. == How Does a Developer Use NETCONF and YANG? == A NETCONF server developer decides what modules need to be supported by the NETCONF server, and implements the device instrumentation code for those modules. Much of the NETCONF protocol related code is handled by the NETCONF stack, based on the YANG module contents. Therefore, the most important task for a developer is designing a good YANG module. After the YANG module is written, the device instrumentation code for the YANG module is then added by the developer. The code uses the Yuma API to register callbacks and access the YANG database. The 'callback code' is called from the NETCONF stack when database operation requests for the object(s) in the YANG module are received by the server. Once this library is completed, the YANG module and its binary server instrumentation library (SIL) can be loaded into the NETCONF server at run-time. There is no need to recompile the '''netconfd''' server, or even reboot it. = Getting Started with toaster.yang = This section will demonstrate the basic operation of Yuma Tools to use a NETCONF session to manage a remote device with a YANG data model. The Yuma Tools programs and libraries must already be installed. Refer to the Yuma Tools Installation Guide if this has not yet been done. The '''yangcli''' client program and '''netconfd''' server program do not need to be installed on the same machine. For simplicity, the server address 'localhost' is used in the examples below. == What is libtoaster? == There is a sample server instrumentation library (SIL) included, named libtoaster. [https://sourceforge.net/p/yuma123/git/ci/master/tree/libtoaster/src/toaster.c toaster.c] is the module-specific server instrumentation code for the management data defined in [https://sourceforge.net/p/yuma123/git/ci/master/tree/netconf/modules/netconfcentral/toaster.yang toaster.yang ]. This is based on the original TOASTER-MIB by Epilogue. This YANG module provides simple operations to make toast, and some simple NETCONF database objects to enable and monitor the toaster. The new YANG version of the TOASTER-MIB is different is some ways: * extensible YANG identities are used to identify the bread type, instead of a hard-wired enumerated list. * protocol operations ( and ) are used instead of an 'up/down' switch within the database. NETCONF databases are intended to contain persistent data structures, and 'actions' such as starting or stopping the toaster are done with new protocol operations, instead of editing the database with the standard operations. * A simple configuration 'presence container' object is used to enable and disable the toaster service, instead of hard-wiring the toaster service availability. * A notification is generated when the toast is done or canceled. This notification can be used instead of polling the toaster status object. == Other examples: ietf-interfaces and ietf-system == The partial SIL implementations of the stadard ietf-system.yang and ietf-interfaces.yang models are included as examples and should work out of the box for standard Linux distributions. *Model: [https://sourceforge.net/p/yuma123/git/ci/master/tree/netconf/modules/ietf/ietf-interfaces.yang ietf-interfaces.yang ] ([https://tools.ietf.org/rfc/rfc7223.txt rfc7223]) + Implementation: [https://sourceforge.net/p/yuma123/git/ci/master/tree/example-modules/ietf-interfaces/ietf-interfaces.c ietf-interfaces.c] *Model: [https://sourceforge.net/p/yuma123/git/ci/master/tree/netconf/modules/ietf/ietf-system.yang ietf-system.yang] ([https://tools.ietf.org/rfc/rfc7317.txt rfc7317]) + Implementation: [https://sourceforge.net/p/yuma123/git/ci/master/tree/example-modules/ietf-system/ietf-system.c ietf-system.c] One can start (provided you have already compiled installed and configured netconfd and the modules) netconfd and load the YANG models with the installed SIL implementations like this: /usr/sbin/netconfd --module=ietf-system --module=ietf-interfaces == Start the netconfd server == If the '''netconfd''' server is already running, then skip this section. Details for all the '''netconfd''' configuration parameters can be found in the [[Yuma netconfd Manual]]. === Configuration Defaults === To keep the example simple, the default settings will be used: * the server will accept sessions on TCP port 830 * the target database is * no database (mirrored NV-save) * the operation is supported * the access control mode is 'enforcing' * the super user account name is 'superuser' * the server will search startup-cfg.xml using the default search path * the default behavior is 'explicit' * notification replay is enabled with a buffer size of 1000 events and a maximum message burst per session of 10 notifications * the exchange timeout is 10 minutes * the session idle timeout is 1 hour * the default session indent amount is 2 spaces * the default session line-size is 72 characters * violation of strict YANG XML ordering will not cause errors * logging level 'info' is enabled and sent to STDOUT === SSH Server === To start the NETCONF server, make sure that the '''sshd''' server is running, and the following configuration is included in '''/etc/ssh/sshd_config.''' '''Port 22''' '''Port 830''' '''Subsystem netconf /usr/sbin/netconf-subsystem''' The 'Subsystem' command may be different if '''netconf-subsystem''' has been installed in a different location than '''/usr/local/sbin'''. The 'Port 22' command is needed to make sure the SSH server will accept SSH sessions in addition to NETCONF sessions. === NETCONF Server === For this example, the superuser account needs to be enabled. This is done with a CLI parameter, and the user name 'joe' is used. Replace 'joe' with your username. To start the '''netconfd''' server in the foreground: mydir> '''/usr/sbin/netconfd --superuser=joe''' Starting netconfd... Copyright (c) 2008-2012, Andy Bierman, All Rights Reserved. Copyright (c) 2013-2017, Vladimir Vassilev, All Rights Reserved. Warning: Parse failed and --startup-error=continue agt: Startup config loaded OK Source: /home/vladimir/.yuma/startup-cfg.xml Running netconfd server (2.10-0) If no startup configuration is available, then the server defaults will be used instead. Any message about 'startup-cfg.xml' not found can be ignored. It just means the server booted with the factory default configuration. To start the '''netconfd''' server in the background: mydir> /usr/sbin/netconfd --superuser=joe --log=~/mylog & mydir> This example shows that a logfile in the user's home directory called 'mylog' will be used for all server log messages. The '&' at the end causes the command to be run in the background. == Start the yangcli client == Once the NETCONF server is running, it will accept client sessions If running '''netconfd''' interactively on localhost, then start a new terminal window to continue. === Configuration Defaults === To keep the example simple, the default settings will be used: * the client will attempt to start sessions on TCP port 830 * the client will attempt to automatically complete partial commands * the command line history will be automatically loaded upon startup, and saved upon exit * the client will attempt to automatically load any YANG modules advertised in the server message * the client will check before using invalid parameter values * the plain display mode will be used, with 72 characters per line * each nest level of displayed data will be indented 2 spaces * the XML order of messages sent to the server will be corrected, as needed * the logging level of 'info' is set, and log messages are sent to STDOUT * the client will wait 30 seconds for responses === Run yangcli === The yangcli program should be found in the PATH environment variable. mydir> '''yangcli''' If the yangcli program is not found, then try the full path: mydir> '''/usr/bin/yangcli''' === Startup Screen === The startup screen shows the following information: * program version and copyright * tab key can be used for command and parameter completion * basic help instructions * basic statement instructions === Command Line Editing === The command lines are stored in a history buffer. Any previous command line (except a password parameter line) can be recalled and used again. Any command in the command buffer (current or recalled) can be edited. The default key settings are aligned with the emacs editor. Refer to the [[Yuma yangcli Manual]] for more details. === Escape Commands === Not all parameters need to be entered at one time. If yangcli needs more information, based on the initial command line, then 1 or more missing parameters will be requested, in sequence. It is possible to get help, skip a parameter, or even cancel the entire command during one of these sub-command modes, by using an escape command. This is a 1 or 2 character command, followed by the 'enter' key (as usual to end a command).
'''Escape Command Summary'''
{| class="wikitable" border="1" ! command ! description |- | ?s | skip the current parameter |- | ?c | cancel the current command |- | ? | get help |- | ?? | get full help |} Using the '?s' command to skip a parameter may cause the request to be invalid. Depending on the setting of the '''--bad-data''' configuration parameter, this may or may not be allowed. The default setting is to warn and confirm. This configuration parameter also affects parameter values that are invalid according to the YANG module definition. == Getting Context Sensitive Help == The '''yangcli''' program provides context-sensitive help based on the current NETCONF session status and the set of YANG modules currently loaded. When a NETCONF session is active, the set of modules advertised in the message by the server will be used to generate help text, if available. The ''''mgrload'''' command can be used to force '''yangcli''' to use different or additional YANG modules. If the '''yangcli''' program does not have the advertised revision of a particular module available in the module search path, and the NETCONF server supports the standard operation, then the module will be retrieved from the server, and used just for that session. If any features or deviations are advertised for a YANG module, then they will be applied to the modules used just for the current session. The help text and the error checking done for the module will be based on this 'patched' module, not the 'plain' module specified in the capability URI string. === Tab Key for Command Completion === The 'tab' key can be used at any time to see a list of the possible completions that the command interpreter will accept. The list will be displayed for command names and some command parameters. When a NETCONF session is active, all the NETCONF operations will be available. Additional commands may also be available if the server advertised any YANG modules containing 'rpc' statements. === The '?' and '??' Escape Sequences === If a partial command is entered, or if a data structure is being filled, then the help escape sequences are available to get help about that parameter or data node. Use one question mark for help, and two question marks for maximum help.
'''Help Escape Sequences'''
{| class="wikitable" border="1" ! sequence ! description |- | ? | Print some help text, but not description statements and some other information. |- | ?? | Print maximum help text. |} The following example shows the help text for the 'user' parameter for the 'connect' operation: yangcli> '''connect''' Enter string value for leaf yangcli:connect> '''? ''' leaf user [NcxUserName] length: 1..63 pattern: [a-z,A-Z][a-z,A-Z,0-9,\-,_,\.]{0,62} Enter string value for leaf yangcli:connect> The type of object, its name, data type, and any restrictions, will be printed. After that, the previous prompt will be redisplayed. === The 'help' Command === The '''help''' command can be used to display all kinds of information about the '''yangcli''' program and the YANG data module contents in use at the time.
'''Help Command Variants'''
{| class="wikitable" border="1" ! command ! description |- | help help command | Display help for the specified yangcli command or YANG rpc statement. |- | help commands | Display help text for all commands. |- | help object | Display help text for a YANG database top-level object (only if its module is available). |- | help notification | Display help text for a YANG notification event (only if its module is available). |- | help type | Display help text for an exported YANG data type (only if its module is available). |} Each of the help command variants also accepts a 'help-mode' parameter to control how much help text is displayed:
'''Help Output Modes'''
{| class="wikitable" border="1" ! mode ! description |- | --brief | Display minimal help text. |- | --normal | Display a lot, but not always all the help text available (default mode). |- | --full | Display all available help text, including description statements. |} The following table shows some valid help commands: {| class="wikitable" border="1" ! command ! description |- | help help | Get normal help for the help command. |- | help commands brief | Get a 1 line description of each command. |- | help object system full | Get all available help for the /system container and all its descendant nodes. |- | help type NcxIdentifier | Get summary and description of the data type called 'NcxIdentifier'. |- | help notification sysSessionStart | Get a summary of the 'sysSessionStart' notification, and each of objects in its payload. |} == Start a NETCONF session == Each yangcli program instance can run 1 NETCONF session at a time. If no session is currently active, then the prompt will contain just the program name, indicating that the 'connect' command is available: yangcli> === The connect Command === The 'connect' command is used to start a NETCONF session. There are 3 mandatory parameters for this command: * '''user''': the system (or SSH) user name to use * '''server''': the IP address or DNS name of the NETCONF server to use * '''password''': the password string to use Make sure you have a user name and password already configured on the NETCONF server. If a partial command is given, then yangcli will prompt for any missing mandatory parameters. In this example, the complete command is given at once: yangcli'''>''' '''connect server=localhost user=joe password=yangrocks''' After this command is entered, '''yangcli''' will generate some informational log messages to the screen. If the session is started successfully, a summary of the server session capabilities and available modules should be displayed. Also, the command prompt will change to indicate that a NETCONF session is currently active. yangcli joe@localhost> At this point any command supported by the server can be entered, in addition to any '''yangcli''' command (except 'connect'). === Fixing Connection Problems === If the session did not start correctly, check the error messages to fix the problem. Some common problems: * Make sure the '''netconfd''' program is running. * Make sure the '''netconf-subsystem''' program is properly installed. * Check if the SSH configuration contains the portion for NETCONF. * If the SSH configuration looks correct, then try restarting the SSH server to make sure that configuration file is the one being used. * If the SSH server seems to be running correctly, then check if any firewall or other security mechanism is blocking TCP port 830. If so, either enable TCP port 830, or enable port 22 on the NETCONF server (by restarting the server), and include 'port=22' in the 'connect' command parameters. * If no firewall or other security measure is blocking TCP port 830, try to establish a normal SSH session with the server. * If a normal SSH session works correctly, then check the log messages on the NETCONF server for more information. == Enable Notification Delivery == In order to receive the 'toastDone' notification event, a notification subscription has to be enabled. A default NETCONF notification stream can be started with the 'create-subscription' command: yangcli joe@localhost> '''create-subscription''' RPC OK Reply 2 for session 1: yangcli joe@localhost> Depending on other activity within the NETCONF server, it is possible other notification events, such as 'sysSessionStart' or 'sysSessionEnd' will be generated. Notifications are displayed in their entirety, but not during 'rpc reply output'. If a command is being entered, the notification will be displayed, and then the command line restored. == Load the Toaster Module == The toaster module is not a core system module, and is not available automatically. The module has to be explicitly loaded by the NETCONF client. To load the server-supported version of the toaster module, use the 'load' command: yangcli joe@localhost> '''load toaster''' RPC Data Reply 2 for session 1: rpc-reply { mod-revision 2009-11-20 } Incoming notification: notification { eventTime 2009-12-28T00:44:45Z sysCapabilityChange { changed-by { userName joe sessionId 1 remoteHost 127.0.0.1 } added-capability http://netconfcentral.com/ns/toaster?module=toaster&revision=2009-11-20 } sequence-id 3 } yangcli joe@localhost> If the module was successfully loaded, then a data response will be sent, containing the revision date of the toaster module that was loaded. This response will be returned even if the module was already loaded. Note that the 'sysCapabilityChange' notification event will only be sent if the module has not already been loaded into the server. In this case, it was not advertised in the message for this session, and the toaster module needs to be loaded manually into yangcli with the 'mgrload' command: yangcli joe@localhost> '''mgrload toaster''' Load module 'toaster' OK yangcli joe@localhost> == Enable the Toaster == Try to make some toast, using the 'make-toast' command: yangcli joe@localhost> '''make-toast''' RPC Error Reply 4 for session 1: rpc-reply { rpc-error { error-type protocol error-tag resource-denied error-severity error error-app-tag no-access error-message 'resource denied' error-info { error-number 269 } } } yangcli joe@localhost> What happened? A 'resource-denied' error was returned instead of 'OK', because the toaster service is not enabled yet. A node has to be created in the NETCONF database before the 'make-toast' command can be used. === Lock the Databases === The first step is to lock the NETCONF databases for writing. Locks do not affect read operations. The yangcli program has a high-level command to deal with locking, called 'get-locks'. It will handle retries for any missing locks, until an overall timeout occurs or all the locks needed are acquired. yangcli joe@localhost>''' get-locks''' Sending operations for get-locks... get-locks finished OK yangcli joe@localhost> === Create the toaster Container === The toaster module uses a simple YANG 'presence' container to configure the toaster service. Once the /toaster container is created, the read-only nodes within that container will be maintained by the server, and the toaster service will be enabled. The first step is to create the /toaster node in the configuration database: yangcli joe@localhost>''' create /toaster''' Filling container /toaster: RPC OK Reply 5 for session 1: yangcli joe@localhost> Now the /toaster node is created in the database. === Commit the Database Changes === In order to activate these changes, the 'commit' command needs to be issued. yangcli joe@localhost> '''commit''' RPC OK Reply 6 for session 1: Incoming notification: notification { eventTime 2009-12-28T00:59:58Z sysConfigChange { userName joe sessionId 1 remoteHost 127.0.0.1 edit { target /toast:toaster operation create } } sequence-id 4 } The 'RPC OK' message indicate that the server successfully commited the configuration. The 'sysConfigChange' notification indicates what was changed in the running configuration, and who made the change(s). The toaster server should now be enabled. === Unlock the Databases === The database locks need to be released as soon as possible after the edits are completed or discarded. The high-level command 'release-locks' must be used if 'get-locks' was used to acquire the database locks. yangcli joe@localhost> '''release-locks ''' Sending operations for release-locks... yangcli joe@localhost> == Get the Toaster State Information == To discover the toaster model and its current status, the 'sget' or 'xget' commands can be used to retrieve just the toaster portion of the conceptual state data available on the server. The 'sget' command is high-level subtree filter handler for the operation: yangcli joe@localhost> '''sget /toaster''' The 'xget' command is high-level XPath filter handler for the operation. It is only available if the NETCONF server supports the ''':xpath '''capability (like '''netconfd'''). yangcli joe@localhost> '''xget /toaster''' Both commands should return the same data: Filling container /toaster: RPC Data Reply 7 for session 1: rpc-reply { data { toaster { toasterManufacturer 'Acme, Inc.' toasterModelNumber 'Super Toastamatic 2000' toasterStatus up } } } This data shows that the 'Super Toastamatic 2000' is ready to make toast! == Start Making Toast == Now that the toaster is enabled, the 'make-toast' command should work. Instead of using the default parameter values, let's make a frozen waffle a little less done than normal: yangcli joe@localhost> '''make-toast toasterDoneness=4 toasterToastType=toast:frozen-waffle ''' RPC OK Reply 8 for session 1: At this point the toaster timer is running, and the simulated waffle is cooking, After about 40 seconds, the 'toastDone' notification should be received: Incoming notification: notification { eventTime 2009-12-29T01:20:05Z toastDone { toastStatus done } sequence-id 5 } This 'toastDone' event shows that the toast was completed, and is ready to eat. == Stop Making Toast == What if you change your mind, and want wheat toast instead of a waffle? Repeat the previous command (Control-P should recall the previous command): yangcli joe@localhost> '''make-toast toasterDoneness=4 toasterToastType=toast:frozen-waffle ''' RPC OK Reply 9 for session 1: Now enter the 'cancel-toast' command right away, before the waffle finishes: yangcli joe@localhost> '''cancel-toast ''' RPC OK Reply 10 for session 1: Incoming notification: notification { eventTime 2009-12-29T01:24:36Z toastDone { toastStatus cancelled } sequence-id 6 } This 'toastDone' event shows that the toast was cancelled. == Close the NETCONF Session == To close the NETCONF session, use the 'close-session' command: yangcli joe@localhost> '''close-session''' RPC OK Reply 11 for session 1: ses: session 1 shut by remote peer yangcli> Note that the prompt returned to the default form, once the session was dropped by the NETCONF server. The terminate the yangcli program, use the 'quit' command: yangcli> '''quit ''' mydir> = Advanced Topics = This section introduces some advanced features of the NETCONF protocol and YANG data modeling language. == Data Retrieval == === Basic NETCONF Retrieval Operations === The NETCONF protocol has 2 different retrieval operations: * '''''': get state data and the running configuration database. * '''''': get just the specified configuration database. Each of these operations accepts a parameter, which has 2 forms: * '''subtree filter:''' retrieve just the subtrees in the database that match the XML subtrees in the filter. * '''XPath filter:''' retrieve just the subtrees that match the result node set produced by evaluating the specified XPath expression against the database. This mode cannot be used unless the ''':xpath''' capability must be advertised by the server. The '''yangcli''' program supports 3 different forms of each command: * '''plain''': plain NETCONF operation with user-supplied filter * '''subtree''': XPath path expression or user variable is converted to XML for the parameter subtree XML. * '''xpath''': XPath path expression or user variable is converted to XML for the parameter 'select' XML attribute
'''yangcli Retrieval Commands'''
{| class="wikitable" border="1" ! command ! description ! example |- | '''get''' | plain operation | get with-defaults=trim |- | '''get-config''' | plain operation | get-config source=candidate |- | '''sget''' | with a subtree filter | sget /system |- | '''sget-config''' | with a subtree filter | sget-config source=running /nacm/rules |- | '''xget''' | with an XPath filter | xget "/interfaces-state/interface/statistics" |- | '''xget-config''' | with an XPath filter | xget-config source=candidate "/interface[name='eth0']" |} The retrieval commands return an element named containing the requested XML subtrees. If any identifier nodes (YANG key leafs) are needed to distinguish the data in the reply, they will be added as needed by the server. In the 'xget' example above, the element for each interface would be returned, even though it was not directly requested by the XPath expression. === Default Value Filtering === The data will also be filtered according to the defaults handling behavior of the server, unless the parameter is added to the command. This parameter is only supported if the server advertised the 'with-defaults' capability, If not, the client does not get any indication from the server what type of defaults filtering is being done (if any). There are 3 types of defaults filtering provided: * '''report-all''': no filtering -- return all nodes even those the server might normally suppress because they are considerer to be default values by the server. * '''trim''': return all nodes except skip any leaf nodes that match the schema defined default value * '''explicit''': return all nodes that were set by the client or the server to some value, even if the value happens to be the schema defined default. This is normally the default behavior for the '''netconfd''' server. The defaults handling behavior can be changed just for a specific NETCONF session, using the operation. This is only available on the '''netconfd''' server. yangcli joe@localhost>''' set-my-session with-defaults=report-all ''' RPC OK Reply 12 for session 1: yangcli joe@localhost> In this example, the 'basic' behavior is changed from 'explicit' to 'report-all', but just for session 1. This setting is temporary, and will not be remembered when the session is terminated. If the parameter is present, it will be used instead of this value. === Special Retrieval Operations === Any YANG module can add new operations with the 'rpc' statement. New retrieval operations may also be added which are associated with a protocol capability. Just like any other data model content, the operator (or application) needs to understand the YANG file definitions, including the description statements, to understand how each custom retrieval operation works. There are 2 custom retrieval operations supported by '''netconfd''':
'''Special Retrieval Operations'''
{| class="wikitable" border="1" ! operation ! description |- | '''get-schema''' | Retrieve the YANG or YIN source file for one of the modules advertised by the server.This is a standard operation defined in the '''ietf-netconf-monitoring''' module. |- | '''get-my-session''' | Retrieve the customizable settings for my session. This is a proprietary operation defined in the '''yuma-my-session''' module. |} == Notifications == Notifications are used in NETCONF to send server event information to the client application. A session must request notifications with the 'create-subscription' command. Notifications are grouped into 'streams', but only the 'NETCONF' stream is defined at this time. A notification subscription request specifies the stream name (and perhaps more parameters). A NETCONF session on the netconfd server will never expire due to inactivity, while a notification subscription is active. This allows notification processing applications to maintain long-lived connections without worrying about a NETCONF timeout. Note that the SSH server may also be configured to drop idle SSH sessions, whether a notification subscription is active or not. === Notification Contents === [[Image:notification-structure.png]] The 'notification' element is sent from the server to the client, if an event occurs, and the client has created a notification subscription. The child nodes of this element comprise the notification content, and it is divided into 3 sections: # '''event generation time-stamp''': This standard NETCONF leaf is always the first child element within the notification element. # '''event payload''': The module-specific event payload is represented as a container with the name of the notification. Any data nodes defined within the YANG notification statement appear (in order) as child nodes of the event type container. # '''proprietary extensions''': Zero or more vendor-specific elements may appear after the event payload element. For example, the monotonically increasing 'sequence-id' element is added to each notification saved in the '''netconfd''' event log. === Notification Replay === [[Image:notification-replay-buffer.png]] The NETCONF server will maintain an ordered buffer of saved notification events, if the :notification-replay capability is supported by the server. For the '''netconfd''' server, this is a configurable feature, set by the '''--eventlog-size''' parameter. The '''netconfd''' default is to save the most recent 1000 notification events. Only system events are saved and are available for retrieval. The 'replayComplete' and 'subscriptionComplete' events are session-specific events, and are therefore not saved in the replay buffer. The 'create-subscription' command has 2 parameters to request that stored notifications be delivered to the client session: * '''startTime''': the date (or date-and-time) to compare against the event generation time-stamp. Only notification events that occurred after this time are delivered. * '''stopTime''': the date (or date-and-time) to compare against the event generation time-stamp. Only notification events that occurred before this time are delivered. This parameter can specify a time in the future. When that time has passed, the subscription will be terminated. The stopTime does not cause the server to wait that period of time to generate an event. If the stopTime is in the past, then the subscription will terminate after all the matching event timestamps in the replay buffer have been delivered. Notifications are delivered in the order they are stored. Each new '''netconfd''' notification contains a monotonically increasing sequence-id (unsigned integer). This can be used to help determine if any configured notification filters are working as expected. === The interleave capability === The '''netconfd''' server supports the :interleave capability, which means that all commands (except create-subscription) will be accepted by the server. The client should expect and messages. The server will always maintain proper message serialization. These messages will always be sent in their entirety, which may impact applications (e.g., a really long response on the same session will delay notification delivery). If the NETCONF server does not support the :interleave capability, then it may only allow the operation while the notification subscription is active. In this case, a new NETCONF session is required to perform any management operations. This special mode is only applicable while a notification subscription is active. It is possible for a replay subscription to terminate, without terminating the session as well. In this case, the 'notificationComplete' event will be generated, and the session will return to accepting all possible operations. == Database Editing == NETCONF supports multiple conceptual configuration databases. Only the 'running' database is actually active. All other databases are scratch-pad databases, or some other special-purpose off-line database. Every NETCONF server must allow arbitrary partial (and concurrent) editing to its configuration with the operation. Refer to the Yuma Tools User Manual for complete details on this NETCONF operation. The '''yangcli''' program has simplified editing commands, which are explained below. The element within an PDU represents the 'root node' (/) in the path expression for each node in the conceptual database. Each top-level YANG object that is supported and configured will be represented as child nodes to this root node. The conceptual database can be processed as an XML instance document with multiple top nodes (similar to XSLT rules). Database editing in NETCONF has several variants, but basically, it follows this simple procedure: # Lock the database(s) that will be affected. # Use or on the target database to make changes. # Activate and save/commit the database edits. # Unlock the database(s) that were previously locked. === The Target Database === Usually a NETCONF server supports the operation on only one database, which is either the candidate or the running database. This is called the 'target' database, which corresponds to the parameter in the operation. If the target database is the candidate configuration, then the operation does not always cause all possible database validation checking to be done by the server. Since the candidate database is just a scratch-pad for (possibly) incremental edits, the server is not required to completely validate its contents. Instead, these 'final validation' tests are only required to be done when the operation is invoked. The '''yangcli''' program will automatically handle the target database management, based on the server capabilities reported each session, if the 'save' command is used. The manual procedure ( and/or maybe operations) is also supported, but do not mix them within the same editing session. === Database Locking === NETCONF supports database locking so a session can have exclusive write access to the configuration. These locks are intended to be short-lived, but there is no actual time limit on a lock. If the session terminates for any reason with any locks, they will be released automatically by the server. All the databases that are involved in the edit should be locked. This always includes the running database, and the candidate and startup databases, if they are supported by the server. The yangcli program has 2 special commands to handle all locking: * '''get-locks''': Wait until all database locks have been acquired or the timeout occurs * '''release-locks''': Rlease any locks that were obtained with get-locks Refer to the Yuma Tools User Manual for more details on these commands. === Non-Volatile Storage === The startup configuration is the conceptual database used on the next reboot of the NETCONF server. It is important to know whether the NETCONF server supports the :startup capability or not. If yes, then the operator must explicitly save the running database to non-volatile storage (the startup database), using the operation. If no, then the server will keep the running and startup databases synchronized. The '''yangcli''' program has a high-level 'save' command, used after the editing operations, that will automatically issue the correct protocol operations to complete the edit, and save the changes in non-volatile storage. The startup database is configurable in the '''netconfd''' server. The '''--with-startup''' configuration parameter controls whether the startup database will be used or not. The --startup parameter can be used to control the initial load of the running configuration in 3 different ways: # '''no startup''': skip this step and just use factory defaults # '''default startup''': look for the default '''startup-cfg.xml''' file in the configured data path. # '''specific startup''': use a specified file, either absolute file-spec, or a relative path in the configured data path Refer to the Yuma Tools User Manual for more details on controlling non-volatile storage. === Editing Commands === The operation should be used to make configuration changes. The operation can also be used, but this is a blunt hammer approach. Although the '''netconfd''' server will always analyze the edit request and only affect the nodes that actually changed, this is not a requirement in the standard. The operation allows the operator to have precise control of the server. These database edits are performed by the server using a combination of 3 factors: # The nodes that currently exist in the target database. # The nodes that exist in the 'source' of the edits (either the inline element or indirectly through the element. # The parameter and any XML attributes in the source XML elements (nc:operation attribute and YANG insert operation attributes). The '''yangcli''' program provides some high-level commands to automatically handle the complexity of the operation. These commands use XPath expressions and a series of interactive prompts (e.g., for the mandatory nodes and key leafs) to fill in the specified data structures, and construct an optimized NETCONF message.
'''yangcli Editing Commands'''
{| class="wikitable" border="1" !
command
!
description
|- | create | Create a new sub-tree, only if it does not already exist |- | delete | Delete an existing sub-tree, only if it exists |- | merge | Merge the source sub-tree into the target sub-tree, keeping any existing nodes that are not explicitly contained in the source. |- | replace | Merge the source sub-tree into the target sub-tree, deleting any existing nodes that are not explicitly contained in the source. This is the mode used for the operation. |- | insert | Insert or move a YANG list or leaf-list entry |} Refer to the Yuma Tools User Manual for details on these commands. == Access Control == The '''netconfd''' server can be configured to give precise access rights to each user (the SSH user name associated with the NETCONF session). Some important points to remember about access control: * There are 3 types of access -- read, write, and execute. * If a user does not have read access to some data, then it is silently omitted from the reply. * The 'access-denied' error is not generated for read requests. It is only generated for write requests to the database, or operation execution requests. * An access request results in 1 of 2 outcomes: permit or deny * The server resolves the access request by searching the access control rules. Either an explicit rule will apply, or the default access rights will be checked if no rule is found. * The default access rights are configurable, but usually set as follows: ** read access is permitted ** write access is denied ** exec access is permitted * The '''nacm:secure''' and '''nacm:very-secure''' extensions can be used by the YANG module author to override the default access rights, and deny access instead. For example, the operation is not permitted by default. * There is a configurable 'superuser' user name. If desired, a specific user name will be considered the 'super user' and all access control will be bypassed for this user. By default, this is the name 'superuser', not 'root', since root login to the SSH server is not recommended. == Variables == The '''yangcli''' program supports variables for easier reuse and script-based operations. There are 2 types of variables: * '''file variables''': the variable name is a file name, and the contents of the variable are stored in this file. * '''internal variables''': the variable name is just an internal identifier, and the contents of the variable are stored in memory Variables are set with assignment statements. Here are some examples: yangcli joe@localhost>''' $$backup = get-config source=running''' yangcli joe@localhost>''' $$bad-data = "warn"''' yangcli joe@localhost>''' $itf = "//interface[name='eth0']"''' Note that in order to assign a string value (e.g., $$bad-data = "warn" above), single or double quotes must be used. An unquoted string will be interpreted as a command name, not a simple string value. Variables are referenced in a similar manner, except the variable is on the right-hand side of the equation. These commands are equivalent in this example: yangcli joe@localhost>''' @myfile.xml = xget select=$itf''' yangcli joe@localhost>''' @myfile.xml = xget //interface[name='eth0']''' Complex variable substitution is also supported: yangcli joe@localhost>''' copy-config source=$$backup target=candidate''' Note that '''yangcli''' will attempt to figure out the structure of the parameter (e.g., 'source' and 'target' above), and adjust the NETCONF operation content. In the example above, since 'source' and 'target' are choices, the real nodes within the cases are examined, and the most appropriate case is selected. The 'source' parameter will contain an in-line element with all the child nodes in the '''$$backup''' variable, and the target parameter will contain an empty element named '''.''' There are several types of internal variables available in the '''yangcli''' program: * read-only system variables ($$USER) * read-write system variables ($$default-operation) * global user variables, available at all 'runstack' levels ($$backup) * local user variables available in the current 'runstack' level only ($itf) The command 'show vars' can be used to see the current value of all program variables: yangcli joe@localhost> '''show vars ''' CLI Variables yangcli { aliases-file ~/.yuma/.yangcli_aliases alt-names true autoaliases true autocomp true autohistory true autoload true autouservars true bad-data check display-mode plain echo-replies true feature-enable-default true fixorder true force-target candidate indent 2 log-level info match-names one-nocase ncport 830 password **** private-key /home/joe/.ssh/id_rsa public-key /home/joe/.ssh/id_rsa.pub server localhost subdirs true tcp-direct-enable false time-rpcs false timeout 30 transport ssh use-xmlheader true user vladimir uservars-file ~/.yuma/yangcli_uservars.xml warn-idlen 64 warn-linelen 0 keep-session-model-copies-after-compilation false } Read-only environment variables HOME /home/joe HOSTNAME LANG en_US.utf8 PWD /home/joe SHELL /bin/bash USER joe YUMA_DATAPATH YUMA_HOME YUMA_MODPATH YUMA_RUNPATH Read-write system variables aliases-file ~/.yuma/.yangcli_aliases alt-names true autoaliases true autocomp true autohistory true autoload true autouservars true bad-data check default-module default-operation merge display-mode plain echo-replies true error-option none fixorder true indent 2 keep-session-model-copies-after-compilation false log-level info match-names one-nocase optional false server localhost test-option set time-rpcs false timeout 30 use-xmlheader true user vladimir uservars-file ~/.yuma/yangcli_uservars.xml with-defaults none Global variables backup { interfaces nacm } Local variables itf //interface[name='eth0'] yangcli joe@localhost> == Scripts == Scripts are simply a collection of '''yangcli''' commands and/or assignment statements that are stored in a text file, instead of typed directly. Scripts can call other scripts (except loops are not allowed), and numbered parameters are available (e.g., --P1='fred' passed as parameter, the $1 expands to 'fred' inside the script). The '''$YUMA_RUNPATH''' environment variable, or the '''--runpath''' configuration variable, can be used to set the directory path to look for script files. There is also a default path for finding files, explained in the Yuma Tools User Manual. The command ''''list scripts'''' can be used to show the potential script file available in the run path. The command ''''run foo'''' is used to invoke a script named 'foo' (with no file extension). If a command fails during a script, execution is halted right away and no more commands in the script are executed. If 'get-locks' was used, then any locks obtained will be automatically released. All script runstack levels will be canceled, not just the current script. Script syntax will be expanded in a future release to provide loops and conditional statements. yuma123_2.14/netconf/doc/yuma_docs/yuma-netconfd-manual.txt0000664000175000017500000073553014770023131024111 0ustar vladimirvladimir
'''Yuma netconfd Manual'''
YANG-Based Unified Modular Automation Tools
NETCONF Over SSH Server
Version yuma123-2.13
= Preface = == Legal Statements == Copyright 2009 – 2012, Andy Bierman, All Rights Reserved. Copyright 2013 – 2022, Vladimir Vassilev, All Rights Reserved. == Additional Resources == This document assumes you have successfully set up the software as described in the printed document: [[Yuma Installation Guide]] Other documentation includes: [[Yuma User Manual]] [[Yuma yangcli Manual]] [[Yuma yangdiff Manual]] [[Yuma yangdump Manual]] [[Yuma Developer Manual]] There are several sources of free information and tools for use with YANG and/or NETCONF. The following section lists the resources available at this time. === WEB Sites === * '''Netconf Central''' ** [http://www.netconfcentral.org/ http://www.netconfcentral.org/] ** Yuma Home Page *** Free information on NETCONF and YANG, tutorials, on-line YANG module validation and documentation database * '''Yuma123 open source project''' ** [http://sourceforge.net/projects/yuma123/ http://sourceforge.net/projects/yuma123/] *** Download Yuma source and binaries; project forums and help * '''Yang Central''' ** [http://www.yang-central.org/ http://www.yang-central.org] ** Free information and tutorials on YANG, free YANG tools for download * '''NETCONF Working Group Wiki Page''' ** [http://trac.tools.ietf.org/wg/netconf/trac/wiki http://trac.tools.ietf.org/wg/netconf/trac/wiki] ** Free information on NETCONF standardization activities and NETCONF implementations * '''NETCONF WG Status Page''' ** http://tools.ietf.org/wg/netconf/ ** IETF Internet draft status for NETCONF documents * '''libsmi Home Page''' ** [http://www.ibr.cs.tu-bs.de/projects/libsmi/ http://www.ibr.cs.tu-bs.de/projects/libsmi/] ** Free tools such as smidump, to convert SMIv2 to YANG * '''YumaWorks''' ** [http://www.yumaworks.com/ http://www.yumaworks.com] ** Offers support, training, and consulting for Yuma. ** Offers YumaPro, a professional version of Yuma that includes concurrency, external database support, sub-agent support, multiple northbound interfaces, and more. API compatible with Yuma. Availability: September, 2012. Licensed. * '''Transpacket''' ** [http://www.transpacket.com/ http://www.transpacket.com] ** Offers Linux based embedded operating system distribution which uses Yuma for configuration and monitoring. ** Offers support, training, and consulting for YANG and netconf. === Mailing Lists === * '''NETCONF Working Group''' ** http://www.ietf.org/html.charters/netconf-charter.html ** Technical issues related to the NETCONF protocol are discussed on the NETCONF WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. * '''NETMOD Working Group''' ** [http://www.ietf.org/html.charters/netmod-charter.html http://www.ietf.org/html.charters/netmod-charter.html] ** Technical issues related to the YANG language and YANG data types are discussed on the NETMOD WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. == Conventions Used in this Document == The following formatting conventions are used throughout this document:
'''Documentation Conventions'''
{| class=wikitable border="1" !
Convention
!
Description
|- | '''--foo''' | CLI parameter foo |- | '''''' | XML parameter foo |- | '''foo''' | '''yangcli''' command or parameter |- | '''$FOO''' | Environment variable FOO |- | '''$$foo''' | '''yangcli''' global variable foo |- | some text | Example command or PDU |- | some text | Plain text |} = netconfd User Guide =
'''netconfd Program Components'''
[[Image:]] == Introduction == The '''netconfd''' program is a NETCONF-over-SSH server implementation. It is driven directly by YANG files, and provides a robust and secure database interface using standard NETCONF protocol operations. All aspects of NETCONF protocol operation handling can be done automatically by the '''netconfd''' server. However, the interface between the NETCONF database and the device instrumentation is not covered in this document. Refer to the server Developers Guide for details on adding YANG module instrumentation code to the '''netconfd''' server. === Features === The '''netconfd''' server has the following features: * Complete implementation of NETCONF versions 1.0 and 1.1 * Automatic support for all NETCONF operations, including the YANG 'insert' operation. * Supports , , and databases * Supports the complete NETCONF protocol defined in RFC 4741 and RFC 6241. * Supports the complete SSH transport binding defined in RFC 4742 and RFC 6242. * Full, automatic run-time support for any YANG-defined NETCONF content: ** rpc statement automatically supported, so new operations can be added at run-time ** all YANG data statements automatically supported, so new database objects can be added at run-time ** notification statement automatically supported, so new notification event types can be added at run-time * Complete XML 1.0 implementation with full support for XML Namespaces * Automatic support for all capability registration and message processing * Full, automatic generation of all YANG module contents, including features and deviations * Automatic session management, including unlimited number of concurrent sessions, session customization, and all database cleanup * Fully recoverable, automatic database editing, using a simple 3 phase callback model ** 1) validate, 2) apply, 3) commit or rollback * Full support for database locking, editing, validation, including extensive user-callback capabilities to support device instrumentation. * Automatic support for all YANG validation mechanisms, including all XPath conditionals * Automatic subtree and full XPath filtering * Automatic confirmed-commit and rollback procedures * Automatic database audit log and change notification support * Complete reporting support, including user-defined errors via YANG error-app-tag and error-message statements. * Several extensions, including and , for easier debugging * Comprehensive, fully NETCONF configurable, access control model, defined in '''yuma-nacm.yang'''. * Complete RFC 5277 Notification support, including notification replay, :interleave capability, and several useful notifications implemented in''' yuma-system.yang'''. * Complete RFC 5717 Partial Lock support with full XPath support, and all partial locking monitoring data defined in i'''etf-netconf-monitoring.yang.''' * Full support for all YANG constructs, including deviations * Full support of YANG sub-modules, including nested sub-modules * Multiple concurrent module versions supported (import-by-revision) * Multiple concurrent submodule versions supported (include-by-revision) * Optimized, full XPath 1.0 implementation, including all YANG extensions * Full implementation of the '''ietf-netconf-monitoring''' data model, including the operation to retrieve YANG or YIN modules from the server. * Configurable default node handling, including full support of the standard, in '''ietf-with-defaults.yang'''. * System information automatically supported, as defined in '''yuma-system.yang.''' * Comprehensive logging capabilities for easy debugging during YANG content development or normal operation. * Time filtering support for and requests with 'modified-since' and 'if-modified-since' per-datastore timestamps. === Setting the Server Profile === The '''netconfd''' server can behave in different ways, depending on the initial configuration parameters used. The following parameters should be considered, and if the default behavior is not desired, then an explicit value should be provided instead: * '''--yuma-home''' or '''$YUMA_HOME''' setting will affect YANG search path. * '''--modpath''' or '''$YUMA_MODPATH''' setting will affect YANG search path. * '''--datapath''' or '''$YUMA_DATAPATH''' setting will affect startup-cfg.xml search path. * '''--target''' setting will select the edit target. The default is 'candidate', so this parameter must be set to choose 'running' as the edit target. * '''--with-startup''' setting will enable the database if set to 'true'. * '''--with-validate''' setting will enable the :validate capability if set to 'true' * '''--access-control '''setting will affect how access control is enforced. The default is fully on ('enforcing'). * '''--superuser''' setting will affect access control, if it is enabled. The default is 'superuser'. * '''--default-style''' setting will affect how default leaf values are returned in retrieval requests. The default is 'trim'. This returns everything except leafs containing the YANG default-stmt value, by default. To report every leaf value by default, set this parameter to 'report-all'. To report only leafs not set by the server by default, use the 'explicit' enumeration. * '''--log''' and '''--log-level''' settings will affect how log messages are generated. The default is to send all messages to STDOUT, and use the 'info' logging level. If the server is a DEBUG image, then the default logging level will be 'debug' instead. * '''--eventlog-size''' setting will control the memory used by the notification replay buffer * '''--max-burst''' will control the of notifications sent at once to a single session * '''--hello-timeout''' will control how long sessions can be stuck waiting for a hello message before they are dropped. * '''-- idle-timeout''' will control how long active sessions can remain idle before they are dropped. === Loading YANG Modules === The --'''module''' parameter can be used from the CLI or .conf file to pre-load YANG modules and any related device instrumentation code into the server. A fatal error will occur if any module cannot be loaded, or it contains any YANG errors. At run-time, the operation (defined in '''yuma-system.yang''') can be used to do the same thing, except the server will simply return an instead of terminate, if the requested module cannot be loaded. === Starting netconfd === The current working directory in use when '''netconfd''' is invoked is important. It is most convenient to run '''netconfd''' in the background, and save all output to a log file. The '''netconfd''' program listens for connection requests on a local socket, that is located in '''/tmp/ncxserver.sock'''. In order for NETCONF sessions to be enabled, the '''SSH''' '''server''' and the '''netconf-subsystem''' programs must be properly installed first. The '''netconfd''' program does not directly process the SSH protocol messages. Instead, it is implemented as an SSH subsystem. The number of concurrent SSH sessions that can connect to the '''netconfd''' server at once may be limited by the SSH server configuration. Refer to the Yuma Installation Guide for more details on configuring the SSH server for use with '''netconfd'''. The '''netconfd''' program can be invoked several ways: * To get the current version and exit:
'''netconfd --version''' * To get program help and exit:
'''netconfd --help''' '''netconfd --help --brief''' '''netconfd --help --full''' * To start the server in the background, set the loggin level to 'debug', and send logging messages to a log file
'''netconfd --log-level=debug --log=~/mylog &''' * To start the server interactively, and send all log messages to STDOUT:
'''netconfd''' * To start the server interactively, with a new log file:
'''netconfd –logfile=''mylogfile''''' * To start the server interactively, and append to an existing log file:
'''netconfd'' --''logfile''=mylogfile –''log-append''' * To get parameters from a configuration file:
'''netconfd –config=/etc/yuma/netconfd.conf''' === Stopping netconfd === To terminate the '''netconfd''' program when running interactively, use the control-C character sequence. This will cause the server to cleanup and terminate gracefully. The or operations can also be used to terminate or restart the server. The '''yuma-nacm.yang''' access control rules must be configured to allow any user except the 'superuser' account to invoke this operation. === Signal Handling === The server will respond to Unix signals sent to the '''netconfd''' process. If the server is being run in the foreground, then the Control-C character sequence will perform the same action as a SIGINT signal.
'''Signals Recognized by netconfd'''
{| class=wikitable border="1" |
'''signal'''
|
'''number'''
|
'''description'''
|- | SIGHUP (Hangup) | 1 | Restart the server. |- | SIGINT (Control-C) | 2 | Shutdown the server. |- | SIGQUIT | 3 | Shutdown the server. |- | SIGILL | 4 | Shutdown the server. |- | SIGTRAP | 5 | Shutdown the server. |- | SIGABRT | 6 | Shutdown the server. |- | SIGKILL | 9 | Shutdown the server. |- | SIGPIPE | 13 | Handle I/O connection error. |- | SIGTERM | 15 | Shutdown the server. |} The '''kill''' command in Unix can be used to send signals to a process running in the background. Refer to the Unix man pages for more details. === Error Handling === All of the error handling requirements specified by the NETCONF protocol, and the YANG language error extensions for NETCONF, are supported automatically by '''netconfd'''. There are 4 categories of error handling done by the server: * incoming PDU validation * ** Errors for invalid PDU contents are reported immediately. The server will attempt to find all the errors in the input request, and not stop detecting errors after one is found. ** All machine-readable YANG statements are utilized to automate the detection and reporting of errors. ** A node that is present, but has a false 'when' statement, is treated as an error. * server instrumentation PDU validation ** Semantic requirements expressed only in description statements will be checked by device instrumentation callbacks. The specific YANG data module should indicate which errors may be reported, and when they should be reported. * database validation ** Several automated tests are performed when a database is validated. ** If the edit target is the configuration, then referential integrity tests are postponed until the operation is attempted. ** The specific conditions checked automatically are: *** referential integrity condition test failed (must) *** missing leaf (mandatory) *** missing choice (mandatory) *** extra container or leaf *** too few instances of a list or leaf-list (min-elements) *** too many instances of a list or leaf-list (max-elements) *** instance not unique (unique) ** Nodes that are unsupported by the server will automatically be removed from these tests. This can occur in the following ways: *** node is defined within a feature that is not supported (if-feature) *** node has conditional existence test that is false (when) *** nodes derived from a 'uses' statement which has a conditional existence test that is false (when) *** nodes derived from an 'augment' statement which has a conditional existence test that is false (when) * server instrumentation database validation and activation ** Errors can occur related to the specific YANG data model module, which can only be detected and reported by the server instrumentation. ** Resource denied errors can occur while the server instrumentation is attempting to activate the networking features associated with some configuration parameters. ** Instrumentation code can fail for a number of reasons, such as underlying hardware failure or removal. === Module Summary === The following YANG modules are built into the netconfd server, and cannot be loaded manually with the '''module''' parameter or operation.
'''Pre-loaded YANG Modules'''
{| class=wikitable border="1" |
'''module'''
|
'''description'''
|- | ietf-inet-types | standard data types |- | ietf-netconf-monitoring | standard NETCONF monitoring, and the operation |- | ietf-netconf-partial-lock | standard NETCONF and operations |- | ietf-netconf-with-defaults | extension |- | ietf-yang-types | standard data types |- | yuma-interfaces | network interfaces information |- | yuma-nacm | NETCONF Access Control Model |- | nc-notifications | standard replay notifications |- | netconfd | Server CLI parameters |- | notifications | standard notification operations |- | yuma-arp | ARP configuration and monitoring |- | yuma-ncx | Yuma NETCONF extensions |- | yuma-app-common | Common CLI parameters |- | yuma-types | Yuma common data types |- | yuma-mysession | Get and Set session-specific parameters |- | yuma-proc | /proc file system monitoriing |- | yuma-system | system monitoring, operations, and notifications |- | yuma-time-filter | Get only if datastore changed since a specified timestamp |} === Notification Summary === The following notification event types are built into the '''netconfd''' server:
'''Pre-loaded Notifications'''
{| class=wikitable border="1" |
'''module'''
|
'''event type'''
|
'''description'''
|- | nc-notifications | | Notification replay has ended |- | nc-notifications | | Notification delivery has ended |- | yuma-system | | server startup event |- | yuma-system | | NETCONF session started |- | yuma-system | | NETCONF session ended |- | yuma-system | | configuration has changed |- | yuma-system | | server capability added or deleted |- | yuma-system | | confirmed-commit procedure event |} === Operation Summary === The following protocol operations are built into the '''netconfd''' server:
'''Pre-loaded Operations'''
{| class=wikitable border="1" |
'''module'''
|
'''operation'''
|
'''description'''
|- | Ietf-netconf | | Cancel a confirmed-commit operation. |- | ietf-netconf | | Terminate the current session. |- | ietf-netconf | | Activate edits in . |- | ietf-netconf | | Copy an entire configuration. |- | notifications | | Start receiving notifications. |- | ietf-netconf | | Delete a configuration. |- | ietf-netconf | | Discard edits in . |- | ietf-netconf | | Edit the target configuration. |- | ietf-netconf | | Retrieve or state data. |- | ietf-netconf | | Retrieve all or part of a configuration. |- | yuma-mysession | | Retrieve session customization parameters. |- | ietf-netconf-monitoring | | Retrieve a YANG or YIN module definition file. |- | ietf-netconf | | Terminate a NETCONF session. |- | netconfd | | Load a YANG module. |- | ietf-netconf | | Lock a database. |- | netconfd | | No operation. |- | ietf-netconf-partial-lock | | Lock part of the database |- | ietf-netconf-partial-lock | | Unlock part of the database |- | netconfd | | Restart the server. |- | yuma-mysession | | Set the session customization parameters. |- | yuma-system | | Set the logging verbosity level. |- | netconfd | | Shutdown the server. |- | ietf-netconf | | Unlock a database. |- | ietf-netconf | | Validate a database |} === Configuration Parameter List === The following configuration parameters are used by '''netconfd'''. Refer to the CLI Reference for more details.
'''netconfd CLI Parameters'''
{| class=wikitable border="1" |
'''parameter'''
|
'''description'''
|- | --audit-log | Specifies the audit log of changes to the running database, after initial load is done. |- | --audit-log-append | Append audit entryies to the existing log is present; Otherwise start a new audit log. |- | --access-control | Specifies how access control will be enforced |- | --config | Specifies the configuration file to use for parameters |- | --datapath | Specifies the search path for the configuration file. |- | --default-style | Specifies the default behavior |- | --delete-empty-npcontainers | Specifies that empty NP containers should be deleted or not |- | --deviation | Species one or more YANG modules to load as deviations |- | --eventlog-size | Specifies the maximum number of events stored in the notification replay buffer. |- | --feature-disable | Leaf list of features to disable |- | --feature-enable | Specifies a feature that should be enabled |- | --feature-enable-default | Specifies if a feature should be enabled or disabled by default |- | --help | Get context-sensitive help with --brief or --full extension |- | --hello-timeout | Set the number of seconds to wait for a PDU |- | --idle-timeout | Set the number of seconds to wait for a PDU |- | --indent | Specifies the indent count to use when writing data |- | --log | Specifies the log file to use instead of STDOUT |- | --log-append | Controls whether a log file will be reused or overwritten |- | --log-level | Controls the verbosity of logging messages |- | --max-burst | Specifies the maximum number of notifications to send to one session in a row. |- | --modpath | Sets the module search path |- | --module | Specifies one or more YANG modules to load upon startup |- | --protocols | Specifies which NETCONF protocol versions to enable |- | --no-startup | If present, the startup configuration will not be used (if present), and the factory defaults will be used instead. |- | --port | Specifies up to 4 TCP port numbers to accept NETCONF connections from |- | --runpath | Server instrumentation library (SIL) search path |- | --running-error | Specifies whether the server should stop or continue if the running configuration contains any errors at boot-time (such as missing mandatory nodes) |- | --startup | Specifies the startup configuration file location to override the default. Not allowed if the --no-startup parameter is present. |- | --startup-error | Specifies whether the server should stop or continue if the startup configuration contains any recoverable errors (the bad configuration data can be removed) |- | --subdirs | If true, then sub-directories will be searched when looking for files. Otherwise just the specified directory will be used and none of its sub-directories (if any). |- | --superuser | Specifies the user name (or empty string for none) to be given super user privileges, instead of the default 'superuser'. |- | --system-sorted | Specifies whether system ordered lists and leaf-lists should be maintained in sorted order |- | --target | Specifies if the or configuration should be the edit target |- | --usexmlorder | Forces strict YANG XML ordering to be enforced |- | --version | Prints the program version and exits |- | --warn-idlen | Controls how identifier lengths are checked |- | --warn-linelen | Controls how line lengths are checked |- | --warn-off | Suppresses the specified warning number |- | --with-startup | Enable or disable the database |- | --with-url | Enable or disable the ''':url''' capability |- | --with-validate | Enable or disable the ''':validate''' capability |- | --yuma-home | Specifies the '''$YUMA_HOME''' project root to use when searching for files |} == Capabilities == Server capabilities are the primary mechanism to specify optional behavior in the NETCONF protocol. This section describes the capabilities that are supported by the '''netconfd''' server. === :base:1.0 === The ''':base:1.0''' capability indicates that the RFC 4741 version of the NETCONF protocol is supported. This capability can be controlled with the '''–protocols''' CLI parameter. This capability is supported by default. === :base:1.1 === The ''':base:1.1''' capability indicates that the RFC 6241 version of the NETCONF protocol is supported. This capability can be controlled with the '''–protocols''' CLI parameter. This capability is supported by default. === :candidate === The''' :candidate''' capability indicates that database edits will be done to the database, and saved with the operation. The configuration is a shared scratch-pad, so it should be used with the locking. Database edits are collected in the and then applied, all or nothing, to the database, with the operation. This capability is supported by default. It is controlled by the --'''target''' configuration parameter (--target=candidate). By default, only the superuser account can use the operation on the configuration. === :confirmed-commit === The ''':confirmed-commit '''capability indicates that the server will support the and parameters to the operation. If the 'base:1.1' protocol version is in use, then the and parameters are also supported. The confirmed commit procedure requires that two operations be used to apply changes from the candidate configuration to the running configuration. If the second operation is not received before the value (default 10 minutes), then the running and candidate configurations will be reset to the contents of the running configuration, before the first operation. If the session that started the confirmed-commit procedure is terminated for any reason before the second operation is completed, then the running configuration will be reset, as if the confirm-timeout interval had expired. If the confirmed-commit procedure is used, and the :startup capability is also supported, then the contents of NV-storage (e.g., startup-cfg.xml) will not be updated or altered by this procedure. Only the running configuration will be affected by the rollback, If the parameter is used again, in the second operation, then the timeout interval will be extended, and any changes made to the candidate configuration will be committed. If the running and candidate configurations are reverted, any intermediate edits made since the first operation will be lost. === :interleave === The ''':interleave''' capability indicates that the server will accept requests other than during notification delivery. It is supported at all times, and cannot be configured. === :netconf-monitoring === The''' :netconf-monitoring''' capability indicates that the '''/ietf-netconf-monitoring''' data sub-tree is supported. The netconfd server supports all of the tables in this module, except partial-locking, because the ''':partial-lock''' capability is not supported at this time. The''' /netconf-state/capabilities''' subtree can be examined to discover the active set of NETCONF capabilities. The''' /netconf-state/datastores''' subtree can be examined to discover the active database locks. The''' /netconf-state/schemas''' subtree can be examined for all the YANG modules that are available for download with the operation. The''' /netconf-state/sessions''' subtree can be examined for monitoring NETCONF session activity. The''' /netconf-state/statistics''' subtree can be examined for monitoring global NETCONF counters. === :notification === The ''':notification''' capability indicates that the server will accept the operation, and deliver notifications to the session, according to the subscription request. All options and features are supported. A notification log is maintained on the server, which is restarted every time the server reboots. This log can be accessed as a 'replay subscription'. The first notification in the log will be for the event. The and event types are not stored in the log. === :partial-lock === The ''':partial-lock''' capability indicates that RFC 5717 is implemented, and partial locking of the database is supported. The operation is not supported using the database as a target, so partial locks do not affect that operation. The operation on the database is allowed if the --target parameter is set to 'running'. The operation will fail if any portion of the altered configuration is locked by another session. Data in the database which is identical to the corresponding data in the configuration is not affected by a operation. The constant '''VAL_MAX_PLOCKS''' in ncx/val.h controls the maximum number of concurrent locks that a single session can own on a database node. The default value is 4. There is no hard resource limit for: * the number of total partial-locks * the number of ''' ** type: XPath 1.0 string ** This parameter contains an XPath expression for a node-set result, identifying the database nodes to lock. ** One of more instances of this parameter is required. ** The server allows relaxed XPath syntax. If prefixes are used, then a proper namespace declaration must be present in the request. If prefixes are not used, then any available namespace that matches the local name will be used. Optional Parameters: * none Returns: * '''''' ** type: uint32 (range 1 .. MAX_UINT) ** This data identifies the lock ID to use when releasing the lock with the operation. ** There will be one of these elements in the reply. * '''''' ** type: instance-identifier ** This data identifies a locked node as a result of the request. ** There will be one or more of these elements in the reply. Possible Operation Errors: * access denied, in-use, resource-denied Example Request: message-id="260"> //interface Example Reply: message-id="260" xmlns:if="http://netconfcentral.org/ns/yuma-interfaces"> 1 /if:interfaces/if:interface[if:name='virbr0'] /if:interfaces/if:interface[if:name='eth0'] /if:interfaces/if:interface[if:name='lo'] === === The operation is used to unlock part of the database that was previously locked with the operation. Only the session that called can release the lock with this operation. Refer to RFC 5717 or the''' ietf-netconf-partial-lock.yang''' module for details on this operation.
''' operation'''
{| class=wikitable border="1" | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | ietf-netconf-partial-lock.yang |- | Capabilities needed: | :partial-lock |} Mandatory Parameters: * '''''' ** type: unit32 (1 .. MAX_UINT) ** This parameter contains the lock ID of the partial lock to release. ** One of more instances of this parameter is required. ** The server allows relaxed XPath syntax. If prefixes are used, then a proper namespace declaration must be present in the request. If prefixes are not used, then any available namespace that matches the local name will be used. Optional Parameters: * none Returns: * Possible Operation Errors: * access denied, invalid-value Example Request: message-id="263"> 1 Example Reply: message-id="263"> === === The operation is used to restart the '''netconfd''' server. By default, only the 'superuser' account is allowed to invoke this operation. If permission is granted, then the current NETCONF session will dropped, during the server restart.
''' operation'''
{| class=wikitable border="1" | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | none |- | YANG file: | yuma-system.yang |- | Capabilities needed: | none |} Mandatory Parameters: * none Optional Parameters: * none Returns: * none; session will be dropped upon success Possible Operation Errors: * access denied Example Request: message-id="63"> Example Reply: [no reply will be sent; session will be dropped instead.] === === The operation is used to configure the server logging verbosity level. Only the designated superuser user can invoke this operation by default.
''' operation'''
{| class=wikitable border="1" | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yuma-system.yang |- | Capabilities needed: | none |} Mandatory Parameters: * '''''' ** type: enumeration (off, error, warn, info, debug, debug2, debug3) ** default: none ** This parameter specifies the server logging verbosity level. Optional Parameters: * none Returns: * Possible Operation Errors: * access-denied Example Request: message-id="3"> 4 64 trim Example Reply: message-id="3"> === === The operation is used to configure the session customization data for the current session. The session indent amount, line size, and default behavior for the with-defaults parameter can be controlled at this time.
''' operation'''
{| class=wikitable border="1" | Min parameters: | 0 |- | Max parameters: | 3 |- | Return type: | status |- | YANG file: | yuma-mysession.yang |- | Capabilities needed: | none |} Mandatory Parameters: * none Optional Parameters: * '''''' ** type: uint32 (range 0 .. 9) ** default: 3 (can be changed with the '''--indent''' configuration parameter) ** This parameter specifies the desired indent amount for the session. * '''''' ** type: uint32 (range 40 .. 1024) ** default: 72 ** This parameter specifies the desired line length for the session. * '''''' ** type: enumeration (report-all report-all-tagged trim explicit) ** default: report-all (can be changed with the '''--default-style '''configuration parameter) ** This parameter specifies the desired default with-defaults behavior for the session. The server-wide default value is set with the '''--default-style '''configuration parameter. Returns: * Possible Operation Errors: * access-denied Example Request: message-id="3"> 4 64 trim Example Reply: message-id="3"> === === The operation is used to shut down the '''netconfd''' server. By default, only the 'superuser' account is allowed to invoke this operation. If permission is granted, then the current NETCONF session will dropped, during the server shutdown.
''' operation'''
{| class=wikitable border="1" | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | none |- | YANG file: | yuma-system.yang |- | Capabilities needed: | none |} Mandatory Parameters: * none Optional Parameters: * none Returns: * none; session will be dropped upon success Possible Operation Errors: * access denied Example Request: message-id="1"> Example Reply: [no reply will be sent; session will be dropped instead.] === === The operation is used to release a global lock held by the current session. The specified configuration database must be locked, or a 'no-access' and an of 'wrong-config-state' will be returned. If the configuration contains any edits that have not been committed, then these edits will all be lost if the operation is invoked. A operation is performed automatically by the server when the database is unlocked.
''' operation'''
{| class=wikitable border="1" | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yuma-netconf.yang |- | Capabilities needed: | none |- | Capabilities optional: | :candidate:startup |} Mandatory Parameters: * '''target''' ** type: container with 1 of N choice of leafs ** This parameter specifies the name of the target database to be unlocked. ** container contents: 1 of N: *** '''candidate''' **** type: empty **** capabilities needed:''' ''':candidate *** '''running''' **** type: empty **** capabilities needed: none *** '''startup''' **** type: empty **** capabilities needed: startup Optional Parameters: * none Returns: * Possible Operation Errors: * access denied * no-access (database not locked) * invalid-value (database not supported) Example Request: message-id="65"> Example Reply: message-id="65"> === === The operation is used to perform the validation tests against a database or some in-line configuration data.
''' operation'''
{| class=wikitable border="1" | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yuma-netconf.yang |- | Capabilities needed: | :validate |- | Capabilities optional: | :candidate:startup |} Mandatory Parameters: * '''target''' ** type: container with 1 of N choice of leafs ** This parameter specifies the name of the target database, or the in-line configuration data, that should be validated. ** container contents: 1 of N: *** '''candidate''' **** type: empty **** capabilities needed:''' ''':candidate *** '''running''' **** type: empty **** capabilities needed: none *** '''startup''' **** type: empty **** capabilities needed: startup *** '''config''' **** type anyxml **** capabilities needed: none Optional Parameters: * none Returns: * Possible Operation Errors: * access denied * validation errors Example Request: message-id="76"> Example Reply: message-id="76"> == Access Control == The '''netconfd''' access control data model is defined in '''yuma-nacm.yang'''. The '''nacm:secure '''and''' nacm:very-secure''' extensions also affect access control. If present in the YANG definition, then the default behavior (when no rule is found) is not followed. Instead, the super user account must be used to allow default access. There are 3 types of access control rules that can be defined: # module rule # RPC operation rule # database rule Rules apply to one or more groups. Each group contains zero or more user names. Database rules are applied top-down, at every level. The user needs permission for the requested access (read or write) for all referenced nodes within the database. For example, if there was a leaf from module X that augments a container in module Y, the user would need permission to access the container from module Y, and then permission to access the leaf from module X. The NACM data model can be used without any configuration at all. Refer to the section on access control modes for more details. [[Image:]] Normally, some configuration is required: * groups: configure one or more administrative groups * rules: configure one or more access control rules The entire '''/nacm''' subtree is tagged as '''nacm:very-secure'''. By default, only the super-user account can read or write any of its contents. It is suggested that even read access to this data structure be controlled carefully at all times. === NACM Module Structure === The '''/nacm''' subtree consists of 3 read-only leafs and 2 containers: * leaf '''''': enable or disable access control enforcement * leaf''' ''': permit or deny read access when no rule found * leaf''' ''': permit or deny write access when no rule found * leaf''' ''': permit or deny execute access when no rule found * leaf '''''': read-only counter of denied RPC operation requests * leaf '''''': read-only counter of denied database write requests * container''' ''': need 1 or more '''group''' list entries in order for '''rules''' to have any effect ** list''' ''': principle that is assigned access privileges *** leaf''' ''': group identifier string *** leaf-list <'''user-name>''': leaf-list of users that belong to the group * container''' ''': ** list''' ''': an access rule for an entire module namespace *** leaf''' ''': [key] name of the YANG module associated with the rule *** leaf''' ''': [key] privileges granted to all groups associated with the rule *** leaf-list''' ''': leaf-list of one or more group identifiers that are associated with the rule *** leaf''' ''': comment string for the rule (ignored by the server) ** list''' ''': an access rule for one specific protocol operation, defined in a YANG rpc statement. *** leaf''' ''': [key] name of the YANG module associated with the rule *** leaf <'''rpc-name>:''' [key] name of the RPC operation associated with the rule *** leaf''' ''': [key] privileges granted to all groups associated with the rule *** leaf-list''' ''': leaf-list of one or more group identifiers that are associated with the rule *** leaf''' ''': comment string for the rule (ignored by the server) ** list''' ''': an user-ordered access rule for one or more database subtrees. *** leaf''' ''': [key] arbitrary name string for the rule *** leaf''' ''': XPath expression for the database instances which are associated with the rule *** leaf''' ''': privileges granted to all groups associated with the rule *** leaf-list''' ''': leaf-list of one or more group identifiers that are associated with the rule *** leaf''' ''':> comment string for the rule (ignored by the server) ** list''' ''': an user-ordered access rule for one notification events. *** leaf''' ''': [key] name of the YANG module associated with the rule *** leaf <'''notification-name>:''' [key] name of the notification event type associated with the rule *** leaf''' ''': [key] privileges granted to all groups associated with the rule *** leaf''' ''': privileges granted to all groups associated with the rule *** leaf-list''' ''': leaf-list of one or more group identifiers that are associated with the rule *** leaf''' ''':> comment string for the rule (ignored by the server) === Users and Groups === Access rights in NACM are given to groups. A group entry consists of a group identifier and a list of user names that are members of the group. A group is named with is a YANG identity, which has a base of 'nacmRoot': identity nacmGroups { description "Root of all NETCONF Administrative Groups"; } There are 3 hard-wired group names that can be used: * nacm:admin * nacm:monitor * nacm:guest Any module can define an extension identity for the 'nacmGroups' base type, and use it as a group name. There are no special semantics associated with any particular group name. import nacm { prefix nacm; } identity mygroup { description "My special administrator's group."; base nacm:nacmGroups; } By default, there are no groups created. Each '''/nacm/groups/group''' entry must be created by the client. There is no user name table either. It is assumed that the operator will know which user names are valid within each managed device. === Creating New Groups === The operation can be used to create new group entries. Each group is identified only by its leaf. A user name can appear within the same group zero or one times. A user name can appear in zero or more groups. When a user is a member of multiple groups, all these groups will be used to match against rules, in a conceptual 'OR' expression. If any of these groups matches one of the leaf-list nodes within one of the 3 rule types, then that rule will be the one that is used. Rules are always searched in the order they are entered, even the system-ordered lists. The path to the group element is '''/nacm:nacm/nacm:groups/nacm:group.''' The following example shows how a group can be defined. The group name is 'nacm:guest',and the users 'fred' and 'barney' are the initial members of this group. message-id="3"> nc:operation="create"> nacm:guest fred barney === Access Control Modes === The''' --access-control '''configuration parameter is used to globally enable or disable the access control system. It cannot be changed at run-time, or through a NETCONF session. * '''off''': no access control enforcement is done at all. * '''disabled''': all '''nacm:secure''' and '''nacm:very-secure''' tagged objects will require the super user account to access, but no other access control enforcement will be done. * '''permissive''': all '''nacm:very-secure''' objects will require super user account to read. No other read access enforcement will be done. Write and exec access will be checked. * '''enforcing''': all access control enforcement will be checked. This is the default behavior. === Permissions === There are 3 types of access permissions defined: # '''read''': retrieval of any kind # '''write''': modification of any kind # '''exec''': right to invoke an RPC operation The object in each of the 3 access control rule entries is a 'bits' leaf, which is allowed to contain any of these string tokens, or none of them to deny all access to a set of groups. When a rule is found which matches the current request, the leaf will be used to grant permission or not. If the bit for the requested operation is present, then the request is permitted. If the bit is not present, then the request is denied. === Special YANG Extensions For Access Control === There are 3 YANG language extensions defined that can be used to force access control behavior for specific data nodes in the configuration database. # '''nacm:secure''' (no parameter)If present in a data node statement, this extension will cause the write default to be ignored, and any write access to instances of this object will be rejected with an 'access-denied' error unless there is an explicit NACM rule allowing write access to the user session. If present in an 'rpc' statement, then exec access will be denied unless there is an explicit NACM rule granting exec access. # '''nacm:very-secure''' (no parameter)If present in a data node statement, this extension will cause the read and write defaults to be ignored, and any write access to instances of this object will be rejected with an 'access-denied' error unless there is an explicit NACM rule allowing write access to the user session. Read access will be denied, which causes that data to be removed from the . If present in an 'rpc' statement, then exec access will be denied unless there is an explicit NACM rule granting exec access. # '''ncx:user-write''' (parameter: permitted, type: bits: create, update, delete) # Used within database configuration data definition statements to control user write access to the database object containing this statement. The 'permitted' argument is a list of operations that users are permitted to invoke for the specified node. These permissions will over-ride all NACM access control rules, even if NACM is disabled.To dis-allow all user access, provide an empty string for the 'permitted' parameter.
To allow only create and delete user access, provide the string 'create delete' for the parameter. Use this for YANG database objects that cannot be changed once they are set. === Default Enforcement Behavior === Each access type has a default behavior if no rule is found and no special YANG extensions apply: * read default: permit * write default: deny * exec default: permit These defaults can be changed by the server developer, by modifying the YANG definitions in '''yuma-nacm.yang'''. If the data node object definition contains the special YANG extensions described in the previous section, then the extension will define default access and the NACM default access rule will not be used. === Access Control Algorithm === The following logic represents the steps taken during access control enforcement. '''Phase 1: RPC Operation Access''' * Step 1) If the '''--access-control''' configuration parameter is set to 'off' then grant access and exit. * Step 2) If the user name matches the '''--superuser''' configuration variable (or the default 'superuser' if not set), then grant access and exit. * Step 3) If the RPC operation requested is the NETCONF operation, then grant access and exit. * Step 4) If the''' --access-control''' configuration parameter is set to 'disabled': ** a) If the requested RPC operation is tagged as '''nacm:secure '''or '''nacm:very-secure''', then deny access, otherwise grant access, and exit. * Step 5) Retrieve all the NACM groups that this user is found within. ** If there are no groups found: *** a) If the requested RPC operation is tagged as '''nacm:secure '''or '''nacm:very-secure''', then deny access and exit. *** b) If the leaf is set to 'permit' then grant access, otherwise deny access, and exit. ** If there are any groups found, then proceed to step 6. * Step 6) Check if there are any''' /nacm/rules''' nodes configured. ** If there are no rules configured, then if the leaf is set to 'permit' then grant access, otherwise deny access, and exit. ** If there are some entries within the container, then proceed to step 7. * Step 7) Check for any '''/nacm/rules/rpc-rule''' entries that contain an with the same value as one of the groups found in step 5, and is also for the requested RPC operation. The first entry found will be used. ** *** If an entry is found, then check its leaf for the 'exec' bit. **** If the 'exec' bit is found, then grant access, otherwise deny access, and exit. *** If a matching entry is not found, then proceed to step 8. * Step 8) Check for any '''/nacm/rules/module-rule''' entries that contain an with the same value as one of the groups found in step 5, and is also for same module as the requested RPC operation. The first entry found will be used. ** *** If an entry is found, then check its leaf for the 'exec' bit. **** If 'exec' bit is found, then grant access, otherwise deny access, and exit. *** If a matching entry is not found, then proceed to step 9. * Step 9) If the leaf is set to 'permit' then grant access, otherwise deny access. ** Continue to phase 2a if any read access, or phase 2b if any write access to the NETCONF database contents is requested on behalf of the specific RPC operation. '''Phase 2a: Database Read and Notification Access''' * Step 10) If the user name matches the '''--superuser''' configuration variable (or the default 'superuser' if not set), then grant access and exit. * Step 11) Check the access control mode: ** a) If the '''--access-control''' configuration parameter is set to 'permissive' *** If the requested object is tagged as '''nacm:very-secure''', then deny access, otherwise grant access, and exit ** b) Otherwise, this must be normal 'enforcing' mode, so proceed to step 12. * Step 12) Make sure there are some groups and rules ** a) If there were no groups found in step 5, or no rules found in step 6, then: *** If the requested object is tagged as '''nacm:very-secure''', then deny access, otherwise: **** If the is set to 'permit', then grant access, otherwise deny access, and exit. ** b) If there are some groups and rules, then proceed to step 13 to start checking access control rules. * Step 13) Check for any '''/nacm/rules/data-rule''' entries that contain a expression that evaluates to an XPath node-set that contains the requested database node, and the leaf-list contains an entry with the same value as one of the groups found in step 5. The first such entry found will be used. ** *** If an entry is found, then check its leaf for the 'read' bit. **** If the 'read' bit is found, then grant access, otherwise deny access, and exit. *** If an entry is not found, then proceed to step 14. * Step 14) Check for any '''/nacm/rules/module-rule''' entries that contain an with the same value as one of the groups found in step 5, and is also for the same module as the requested database node. The first entry found will be used. ** *** If an entry is found, then check its leaf for the 'read' bit. **** If the 'read' bit is found, then grant access, otherwise deny access, and exit. *** If an entry is not found, then proceed to step 15. * Step 15) If the leaf is set to 'permit' then grant access, otherwise deny access, and exit. '''Phase 2b) Database Write Access''' * Step 16) If the user name matches the '''--superuser''' configuration variable (or the default 'superuser' if not set), then grant access and exit. * Step 17) Make sure there are some groups and rules ** a) If there were no groups found in step 5, or no rules found in step 6, then: *** If the requested object is tagged as '''nacm:secure '''or''' nacm:very-secure''', then deny access, otherwise: **** If the is set to 'permit', then grant access, otherwise deny access, and exit. ** b) If there are some groups and rules, then proceed to step 18 to start checking access control rules. * Step 18) Check for any '''/nacm/rules/data-rule''' entries that contain a expression that evaluates to an XPath node-set that contains the requested database node, and the leaf-list contains an entry with the same value as one of the groups found in step 5. The first such entry found will be used. ** *** If an entry is found, then check its leaf for the 'write' bit. **** If the 'write' bit is found, then grant access, otherwise deny access, and exit. *** If an entry is not found, then proceed to step 19. * Step 19) Check for any '''/nacm/rules/module-mule''' entries that contain an with the same value as one of the groups found in step 5, and is also for the same module as the requested database node. The first entry found will be used. ** *** If an entry is found, then check its leaf for the 'write' bit. **** If the 'write' bit is found, then grant access, otherwise deny access, and exit. *** If an entry is not found, then proceed to step 20. * Step 20) If the leaf is set to 'permit' then grant access, otherwise deny access, and exit. === Module Access Control Rules === The''' /nacm/rules/module-rule''' data structure is used to configure access for any object or RPC operation from a specific YANG module. If the module namespace URI is the same as the XML namespace used in the NETCONF PDU, then the module rule is considered a match. Multiple instances can appear for a single module, as long as the key leaf value is different in each entry. This allows different groups to get different access to the same module (e.g., read vs. read and write). There is no way to move entries around, once they are created. If a group appears in multiple entries for the same module name, then the first one encountered will be used. Entries are checked in the same order they are returned in a reply message. The following example shows an operation which creates 2 entries, for the following configuration: * the 'admin' group is allowed to read and write the NACM module. * the 'monitor' group is allowed to read the NACM module.
message-id="4"> nc:operation="create"> nacm read write nacm:admin nc:operation="create"> nacm read nacm:monitor === RPC Access Control Rules === The''' /nacm/rules/rpc-rule''' data structure is used to configure access for a specific RPC operation from a specific YANG module. If the module namespace URI for the value is the same as the XML namespace used in the NETCONF PDU,and the value is the same as the RPC method name, then the RPC rule is considered a match. Multiple instances can appear for a single RPC operation, as long as the key leaf value is different in each entry. This allows different groups to get different access to the same operation (e.g., exec vs. no access). There is no way to move entries around, once they are created. If a group appears in multiple entries for the same RPC operation, then the first one encountered will be used. Entries are checked in the same order they are returned in a reply message. If the 'read' or 'write' access bits are set in the key leaf, then they will be ignored. This will not cause an error, but it these bits have no effect within an RPC rule. The following example shows an operation which creates 2 entries, for the following configuration: * the 'admin' and 'monitor' groups are allowed to execute the operation. * the 'guest' group is '''not''' allowed to execute the operation
message-id="5"> nc:operation="create"> netconf get-config exec nacm:admin nacm:monitor nc:operation="create"> netconf get-config nacm:guest === Data Access Control Rules === The''' /nacm/rules/data-rule''' data structure is used to configure access for a specific set of database nodes (or notification payload nodes). If the requested node in contained within the node-set result of the XPath path expression, then the data rule is considered a match. Multiple instances of the same expression (or equivalent expressions), can appear at any time, and in any order. The leaf is not allowed to contain an arbitrary XPath expression (at this time). Instead, an '''ncx:schema-instance''' string is allowed. This has the same syntax as a YANG instance-identifier built-in type, except that the key leaf predicates (e.g., [name='eth0']) are optional instead of mandatory. A missing key leaf predicate indicates that all instances of that key leaf are going to match the data rule. This is a user-created list, and the key leaf is the arbitrary field. Use the YANG 'insert' operation to add data rules in some order other than 'last'. Entries are checked in the same order they are returned in a reply message. If a group appears in multiple entries, then the first one that produces a result node-set with a matching node wiill be used. If the 'exec' access bit is set in the key leaf, then it will be ignored. This will not cause an error, but it this bit has no effect within a data rule. The following example shows an operation which creates 3 entries, for the following configuration: * the 'admin' group is allowed to read or write all interfaces. * the 'monitor' group is allowed to read the 'eth0' interface * the 'guest' group is not allowed to read any interfaces information
message-id="3"> none nc:operation="create"> itf-1 xmlns:if="http://netconfcentral.org/ns/yuma-interfaces"> /if:interfaces/if:interface read write nacm:admin let admin group read and write all interfaces nc:operation="create"> itf-2 xmlns:if="http://netconfcentral.org/ns/interfaces"> /if:interfaces/if:interface[if:name='eth0'] read nacm:monitor let monitor group read interface 'eth0' nc:operation="create"> itf-3 xmlns:if="http://netconfcentral.org/ns/interfaces"> /if:interfaces nacm:guest do not let guest group read any interfaces info == Monitoring == The and operations are fully supported for retrieving data from the and configuration databases. The operation is not supported for the configuration. The operation is only supported copying the configuration to the configuration. If the NACM access control policy denies permission to read a particular node, then that node is silently skipped in the output. No error or warning messages will be generated. client applications should be prepared to receive XML subtrees that have been pruned by access control. The element will always be present, so an empty element indicates that no data was returned, either because the did not match, or because access control pruned the requested nodes. There are really five types of filters available for retrieval operations:
'''Filter Types'''
{| class=wikitable border="1" |
'''type'''
|
'''description'''
|- | is_config() | Choose the operation for all objects, or for just config=true objects |- | is_default() | Set the parameter to 'trim' |- | is_client_set() | Set the parameter to 'explicit' |- | subtree filtering | Use // some-xml-subtree to retrieve portions of the or configurations. |- | XPath filtering | Use select="expr"/> to retrieve portions of the or configurations. |} === Using Subtree Filters === The subtree filtering feature is fully supported. The order of nodes within the element is not relevant. Data returned in the should follow the same top-level order as the request, but this should not be relied on to always be the case. Duplicate or overlapping subtrees within the request will be combined in the output, so the common ancestor nodes are not duplicated in the reply. XML namespaces are optional to use: * If there is no namespace in affect for a filter component, or the 'NETCONF' namespace is in effect, the server will attempt to find any top-level data node which matches. * Namespaces within descendant nodes of the node children are inherited from their parent. If none is in effect, then the first matching child node for the current parent node will be used. * Invalid namespace errors for the element are suppressed in the server. An invalid namespace or unknown element is simply a 'no-match' condition. For example, the following PDU would be valid, even though it is not technically valid XML: message-id="8"> ''' ''' Note that there is no default namespace in effect for the subtree. However, the server will accept this filter as if the yuma-nacm.yang module namespace was properly declared. Subtree filters can select specific list entries using content match nodes. The following example would return the entire contents of the entry for 'eth0': message-id="9"> eth0 To retrieve only specific nodes (such as counters) from a single list entry, use select nodes for the desired counter(s), and include a content match node for each key leaf. A missing key leaf will match any entry for that key. The following example request shows how just the and counters could be retrieved from the entry for 'eth0'. Example Request: message-id="10"> eth0 Example Reply: message-id="10"> eth0 290046042 112808406 === Using XPath Filters === The :xpath capability is fully supported, including the YANG extensions to this capability. The XPath 2.0 rule for default XML namespace behavior is used, not XPath 1.0 rules, as specified by the YANG language. This means that any module with a node with the same local-name, in the same position in the schema tree, will match a missing XML prefix. This allows much simpler specification of XPath filters, but it may match more nodes than intended. Remember that any nodes added via an external YANG augment statement may have the same local-name, even though they are bound to a different XML namespace. If the XPath expression does not return a node-set result, then the empty element will be returned in the . If no nodes in the node-set result exist in the specified target database, then an empty element will be returned in the . If a node in the result node-set matches a node in the target database, then it is included in the , If a node selected for retrieval are contained within a YANG list node, then all the key leaf nodes for the specific list entry will be returned in the response. The powerful '//' operator (equivalent to "descendant-or-self::node()") can be used to construct really simple XPath expressions. The following example shows how a simple filter like '//name' will return nodes from all over the database, yet they can all be fully identified because the path from root is part of the response data. Example Request: * xget //name
message-id="43"> ]]>]]> Example Reply: message-id="43"> nacm-tree itf-1 barney lo eth0 virbr0 pan0 xmlns:ns="urn:ietf:params:xml:ns:netconf:monitoring"> xmlns:manageEvent="urn:ietf:params:xml:ns:netmod:notification"> NETCONF In order to refine the previous filter to select nodes from just one module, the use the XML prefix in the node identifier. The example below selects only the nodes from the interfaces module. Example Request: * xget //if:name
message-id="44"> xmlns:if="http://netconfcentral.org/ns/interfaces" select="//if:name"/> Example Reply: message-id="44"> lo eth0 virbr0 pan0 === Using Time Filters === The module '''yuma-time-filter.yang '''defines a timestamp mechanism to help reduce polling overhead for a client. * Timestamps are specified in '''date-and-time''' format (from the '''ietf-yang-types.yang''' module). * The timestamp parameter ''''if-modified-since'''' is added to the and operations. * The timestamp monitoring node ''''last-modified'''' is added to the '''/netconf-state/datastores/datastore''' list. * The XML attribute ''''last-modified' '''is added to the element for replies to and operations. * The per-datastore last modified timestamp is for the entire datastore contents. If the ''''if-modified-since'''' parameter is present in the or request, the the server will check the corresponding 'l'''ast-modified'''' timestamp. ** If the ''''last-modified'''' timestamp is more recent than the 'i'''f-modified-since'''' value, then the or operation is processed as normal. ** If the ''''last-modified'''' timestamp is not more recent than the 'i'''f-modified-since'''' value, then the or operation is not processed. Instead, an empty element is returned. The 'last-modified' XML attribute in the will indicate the last modification timestamp for the datastore. Example Request: xmlns="[http://netconfcentral.org/ns/yuma-time-filter http://netconfcentral.org/ns/yuma-time-filter]"> '''2011-08-21T21:51:46Z''' Empty reply because datastore not modified since specified time: xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" last-modified="'''2011-08-21T17:51:46Z'''" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> == Notifications == The '''netconfd''' server supports all the capabilities of RFC 5277, and the notification monitoring portion of the '''ietf-netconf-monitoring.yang''' data model. There are also some proprietary notifications defined in '''yuma-system.yang'''. === Subscriptions === The operation is used to start receiving notification. It can be used in 4 different modes: * Get all or some of the stored notifications. ** and parameters used; The stop time is in the past. * Get all or some of the stored notifications, then receive live notifications until some point in the future. ** and parameters used; The stop time is in the future. * Get all or some of the stored notifications, then start receiving live notifications until the session is terminated. ** parameter used, but parameter is not used * Start receiving live notifications until the session is terminated ** neither or are used Once a subscription is started, notifications may start arriving, after the for the operation is sent. If the parameter is used, then zero or more stored notifications will be returned, followed by the notification. If the parameter is also used, then the notification will be sent when this stop time has passed. After that, no more notifications will be sent to the session, and the subscription is terminated. After this point, another subscription could be started. Only one subscription can be active on a session at a time. There is no way to terminate a subscription, other than to close the session. === Notification Log === Each system event is saved to the notification replay buffer. The and notifications are not saved to this buffer because they are subscription-specific events, and not system events. The size of the replay buffer is controlled by the '''--eventlog-size''' configuration parameter. The default size is 1000 events. The oldest event will be deleted when a new event is added, when this limit is reached. If '''--eventlog-size '''is set to zero, then there will be no replayed notifications available, and the notification will be sent right away, if is present. Each event in the replay buffer is assigned a sequential sequence ID, starting from 1. The leaf is an unsigned 32-bit integer, which is added to the element, after the event element. This sequence can be used to debug filters by comparing the sequence IDs of the notifications that were delivered against the expected sequence IDs. === Using Notification Filters === A notification filter is different than a or filter. Instead of selecting sub-trees of the specified database, it is treated as a boolean expression. If the filter matches the content in the notification, then the notification is sent to that subscription. If the filter does not match the content, then the notification is not sent to that subscription. A filter match for notification purposes means that the filter is conceptually applied, as if it were a operation, and if any nodes are selected (non-empty result node-set), then the filter is a match. If no content is selected (empty result node-set), then the filter is not a match. The first node that can appear in the filter is the event type. The and nodes are siblings of the event type element, so they cannot be used in a notification filter. === Element === The notification element contains 2 or 3 child elements, in this order: # '''eventTime''': timestamp for the event. The namespace URI for this element is "urn:ietf:params:xml:ns:netconf:notification:1.0" # '''eventType''': The real name will be the name of the YANG notification, such as 'sysStartup'. The contents of this element will depend on the YANG notification definition. The namespace URI for this element will be different for every event type. It will be the same value as the YANG namespace statement in the module that defines the notification statement for the particular event type. # '''sequence-id''': The system event sequence ID. Session or subscription specific events, such as 'replayComplete' and 'notficationComplete' do not have this element. The namespace URI for this element is "http://netconfcentral.org/ns/system". === Event === The event is generated on a subscription that requested notification replay (by supplying the parameter). This event type cannot be filtered out. The server will always attempt to deliver this notification event type when it is generated.
''' notification'''
{| class=wikitable border="1" | Description: | Buffered notification delivery has ended for a subscription |- | Min parameters: | 0 |- | Max parameters: | 0 |- | YANG file: | nc-notifications.yang |} Example: xmlns:ncEvent="urn:ietf:params:xml:ns:netconf:notification:1.0"> 2009-07-29T17:21:37Z xmlns:manageEvent="urn:ietf:params:xml:ns:netmod:notification"/> === Event === The event is generated on a subscription that requested notification replay, and requested that the notification delivery stop (i.e., terminate subscription), after a certain time, using the parameter. This event type cannot be filtered out. The server will always attempt to deliver this notification event type when it is generated.
''' notification'''
{| class=wikitable border="1" | Description: | All notification delivery has ended, and the subscription is terminated. |- | Min parameters: | 0 |- | Max parameters: | 0 |- | YANG file: | nc-notifications.yang |} Example: xmlns:ncEvent="urn:ietf:params:xml:ns:netconf:notification:1.0"> 2009-07-29T17:31:22Z xmlns:manageEvent="urn:ietf:params:xml:ns:netmod:notification"/> === Event === The event is the first notification generated when the server starts or restarts. It contains the startup file source (if any) and lists any contents that were detected at boot-time, during the copying of the startup configuration into the running configuration.
''' notification'''
{| class=wikitable border="1" | Description: | The '''netconfd''' server has started |- | Min parameters: | 0 |- | Max parameters: | 2 |- | YANG file: | yuma-system.yang |} Parameters: * '''startupSource''' ** type: string ** usage: optional (will not be present if '''--no-startup''' was present) ** This parameter identifies the local file specification associated with the source of the startup configuration contents. * '''bootError''' ** type: list (same structure as element) ** usage: optional (will only be present if errors were recorded during boot) ** There will be one entry for each encountered during the load config operation. The fields are used directly. ** There is no particular order, so no key is defined. ** All fields except will be present from the original that was generated during the boot process. Example: 2009-07-29T17:21:13Z /home/andy/swdev/yuma/trunk/netconf/data/startup-cfg.xml xmlns:sys="[http://netconfcentral.com/ns/system http://netconfcentral.org/ns/system]">1 === Event === The notification is generated when a NETCONF session is started. The username, remote address, and session ID that was assigned are returned in the event payload.
''' notification'''
{| class=wikitable border="1" | Description: | A new NETCONF session has started. |- | Min parameters: | 3 |- | Max parameters: | 3 |- | YANG file: | yuma-system.yang |} Parameters: * '''userName''' ** type: string ** usage: mandatory ** This parameter identifies the SSH user name that is associated with the session. * '''sessionId''' ** type: uint32 (range 1 to max) ** usage: mandatory ** This parameter identifies the NETCONF session ID assigned to the session. * '''remoteHost''' ** type: inet:ip-address ** usage: mandatory ** This parameter identifies the remote host IP address that is associated with the session. Example: 2009-07-29T21:53:04Z andy 2 192.168.0.6 xmlns:sys="[http://netconfcentral.com/ns/system http://netconfcentral.org/ns/system]">4 === Event === The notification is generated when a NETCONF session is terminated. The username, remote address, and session ID that was assigned are returned in the event payload. The termination reason is also included. If the session was terminated before it properly started, it is possible that there will not be a notification event to match the event. For example, if the initial SSH connection setup fails before the message is processed, then only a notification event will be generated. In this case, the user name and other session information may not be available.
''' notification'''
{| class=wikitable border="1" | Description: | A NETCONF session has terminated. |- | Min parameters: | 1 |- | Max parameters: | 5 |- | YANG file: | yuma-system.yang |} Parameters: * '''userName''' ** type: string ** usage: mandatory ** This parameter identifies the SSH user name that is associated with the session. * '''sessionId''' ** type: uint32 (range 1 to max) ** usage: mandatory ** This parameter identifies the NETCONF session ID assigned to the session. * '''remoteHost''' ** type: inet:ip-address ** usage: mandatory ** This parameter identifies the remote host IP address that is associated with the session. * '''killedBy''' ** type: uint32 (range 1 to max) ** usage: optional (will only be present if the terminationReason leaf is equal to 'killed'. ** This parameter identifies the session number of the session that issued the operation. * '''terminationReason''' ** type: enumeration (closed, killed, dropped, timeout, bad-start, bad-hello, other) ** usage: mandatory ** This parameter indicates why the session was terminated. Example: 2009-07-29T21:53:12Z andy 2 192.168.0.6 closed xmlns:sys="[http://netconfcentral.com/ns/system http://netconfcentral.org/ns/system]">5 === Event === The notification is generated when the configuration database is altered by a NETCONF session. If the ''':candidate''' capability is supported, then this event is generated when the operation completes. If the ''':writable-running''' capability is supported instead, then this even is generated when the operation completes. The user name, remote address, and session ID that made the change are reported. A summary of the changes that were made is also included in the event payload. If multiple changes are made at once, then one event will be generated for each change. There is no significance to the order that these events are generated.
''' notification'''
{| class=wikitable border="1" | Description: | The configuration has been changed by a NETCONF session. |- | Min parameters: | 4 |- | Max parameters: | 4 |- | YANG file: | yuma-system.yang |} Parameters: * '''userName''' ** type: string ** usage: mandatory ** This parameter identifies the SSH user name that is associated with the session. * '''sessionId''' ** type: uint32 (range 1 to max) ** usage: mandatory ** This parameter identifies the NETCONF session ID assigned to the session. * '''remoteHost''' ** type: inet:ip-address ** usage: mandatory ** This parameter identifies the remote host IP address that is associated with the session. * '''edit''' ** list (with no key) of edit operations performed, 1 entry for each edit: *** '''target''' **** type: instance-identifier **** usage: mandatory **** This parameter contains the absolute path expression of the database object node that was modified. *** '''operation''' **** type: enumeration (merge, replace, create, delete) **** usage: mandatory **** This parameter identifies the nc:operation that was performed on the target node in the database. Example: 2009-07-29T22:21:18Z andy 3 192.168.0.6 /nd:config/nacm:nacm/nacm:groups/nacm:group[nacm:groupIdentity=nacm:admin] create xmlns:sys="http://netconfcentral.org/ns/system">7 === Event === The notification is generated when the set of active capabilities for the server has changed. The most common way this notification is generated is after the operation has been used to add a new YANG module to the system. It is possible that this notification will be generated for removal of capabilities. However, at this time, there are no NETCONF capabilities that can be removed from the running system. The leaf list will contain the capability URI for each new capability that has just been added. The leaf list will contain the capability URI for each existing capability that has just been deleted. The container will identity whether the server or a NETCONF session caused the capability change. If the change was made by the server, then this container will have an empty leaf named . If the change was made by a NETCONF session, the user name, remote address, and session ID for the session that caused the change are reported. If multiple changes are made at once, then one event will be generated for all the changes. There will be multiple instances of the or leaf-list elements in this case. When this notification is generated, the i'''etf-netconf-monitoring''' data model data structure is updated to reflect the changes.
''' notification'''
{| class=wikitable border="1" | Description: | The set of currently active URIs has changed |- | Min parameters: | 2 |- | Max parameters: | 5 |- | YANG file: | yuma-system.yang |} Parameters: * choice changed-by (server or by-user) ** '''server''' *** type: empty *** usage: mandatory *** If this empty leaf is present, then the server caused the capability change. ** case by-user (if a NETCONF session caused the capability change) * ** *** '''userName''' **** type: string **** usage: mandatory **** This parameter identifies the SSH user name that is associated with the session. *** '''sessionId''' **** type: uint32 (range 1 to max) **** usage: mandatory **** This parameter identifies the NETCONF session ID assigned to the session. *** '''remoteHost''' **** type: inet:ip-address **** usage: mandatory **** This parameter identifies the remote host IP address that is associated with the session. * '''added-capability''' ** type:leaf-list of capability URI strings ** usage: optional ** This parameter contains one entry for each capability that was just added * '''deleted-capability''' ** type:leaf-list of capability URI strings ** usage: optional ** This parameter contains one entry for each capability that was just deleted. Example: 2009-07-29T23:03:06Z andy 3 192.168.0.61 http://netconfcentral.org/ns/toaster?module=toaster&revision=2009-06-23&features=clock xmlns:sys="[http://netconfcentral.com/ns/system http://netconfcentral.org/ns/system]">8 === Event === The notification is generated when the state of the confirmed-commit procedure has changed. The confirmed-commit procedure is started when a operation with a parameter is executed. A notification is generated several times for a single confirmed-commit procedure. One or more of the following sub-events will be generated: * '''start''': A confirmed-commit procedure has started. ** Sent once when the first operation is executed. ** This event starts the confirmed-commit procedure. ** If the database is not altered, then the confirmed commit procedure will be skipped. * '''cancel''': A confirmed-commit procedure has been canceled ** Sent only if the original session is terminated. ** This event terminates the confirmed-commit procedure. * '''timeout''': A confirmed-commit procedure has timed out. ** Sent only if the confirm-timeout interval expires. ** This event terminates the confirmed-commit procedure. * '''extend''': A confirmed-commit procedure has been extended. ** Sent if the 2nd to N-1th operation contains a parameter. ** This event restarts the confirm-timeout interval, but does not reset the backup database. ** Any new changes in the database will be committed. * '''complete''': A confirmed-commit procedure has completed. ** Sent if the 2nd to Nth operation is executed before the confirm-timeout interval expires. ** This event terminates the confirmed-commit procedure.
''' notification'''
{| class=wikitable border="1" | Description: | The state of the confirmed-commit procedure has changed. |- | Min parameters: | 1 |- | Max parameters: | 4 |- | YANG file: | yuma-system.yang |} Parameters: * '''userName''' ** type: string ** usage: mandatory ** This parameter identifies the SSH user name that is associated with the session that caused the confirmed-commit procedure state change. * '''sessionId''' ** type: uint32 (range 1 to max) ** usage: mandatory ** This parameter identifies the NETCONF session ID assigned to the session. * '''remoteHost''' ** type: inet:ip-address ** usage: mandatory ** This parameter identifies the remote host IP address that is associated with the session. * '''confirmEvent''' ** type: enumeration (start, cancel, timeout, extend, complete) ** usage: mandatory ** This parameter indicates why the confirmed-commit procedure changed state. Example: 2009-08-29T23:03:06Z andy 3 192.168.0.61 start xmlns:sys="[http://netconfcentral.com/ns/system http://netconfcentral.org/ns/system]">9 = CLI Reference = The '''netconfd''' program uses command line interface (CLI) parameters to control program behavior. The following sections document all the Yuma CLI parameters relevant to this program, in alphabetical order. == --audit-log == The '''--audit-log''' parameter specifies the file path of the configuration edit audit log. If this parameter is present, then edits to the running database will cause an audit log entry to be created for each edit point. This is done in addition to normal logging, but it is not affected by the '''–log-level''' parameter.
'''--audit-log parameter'''
{| class=wikitable border="1" | Syntax | ''filespec'' |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd \ --audit-log=/var/log/nc-audit.log& |} == --audit-log-append == The '''--audit-log-append''' parameter specifies that the existing audit log file (if any) should be appended, instead of deleted. It is ignored unless the '''--audit-log''' parameter is present.
'''--audit-log-append parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd \ --audit-log=/var/log/ncaudit.log \ --audit-log-append& |} == --access-control == The '''--access-control''' parameter specifies how access control is enforced in the '''netconfd''' program. It is an enumeration with the following values: * '''enforcing''': All configured access control rules will be enforced. * '''permissive''': All configured access control rules will be enforced for write and execute requests. All read requests will be allowed, unless the requested object contains the '''nacm:very-secure''' extension. In that case, all configured access control rules will be enforced, and no default access will be allowed. * '''disabled''': All read, write, and execute requests will be allowed, unless the object contains the '''nacm:secure''' or '''nacm:very-secure''' extension. ** If the '''nacm:secure''' extension is in effect, then all configured access control rules will be enforced for write and execute requests. ** If the '''nacm:very-secure''' extension is in effect, then all configured access control rules will be enforced for all requests. Use this mode with caution. * '''off''': All access control enforcement is disabled. Use this mode with extreme caution.
'''--access-control parameter'''
{| class=wikitable border="1" | Syntax | enumeration: '''enforcing permissive disabled off''' |- | Default: | enforcing |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd \ --access-control=permissive |} == --config == The '''--config''' parameter specifies the name of a Yuma configuration file that contains more parameters to process, in addition to the CLI parameters. Refer to the 'Configuration Files' section for details on the format of this file.
'''--config parameter'''
{| class=wikitable border="1" | Syntax | string: complete file specification of the text file to parse for more parameters. |- | Default: | /etc/yuma/.conf |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd \ --config=~/testconf.conf |} == --datapath == The '''--datapath''' parameter specifies the directory search path to use while searching for data files. It consists of a colon (':') separated list of path specifications, commonly found in Unix, such as the '''$PATH''' environment variable. This parameter overrides the '''$YUMA_DATAPATH''' environment variable, if it is present.
'''--datapath parameter'''
{| class=wikitable border="1" | Syntax | string: list of directory specifications |- | Default: | '''$YUMA_DATAPATH''' environment variable |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd \ --datapath=â€~/work2:~/data†|} == --default-style == The''' --default-style''' parameter specifies the way leafs with default values are returned from the server for data retrieval operations. This setting will be used as the default behavior when processing the operations that support the extension, and no value is provided. The values and their meanings are defined in '''ietf-with-defaults.yang'''. Here is a summary: * '''report-all''': Report all nodes as the default behavior. The will be the behavior used if this parameter is not specified. * '''trim''': Report only nodes that do not have the server-assigned value, as the default behavior. This includes all leafs with a YANG default and any other node created by the server. * '''explicit''': Report only nodes that have been set by client action, as the default behavior. Any node created via the configuration at boot-time is considered to be a client-created node. It does not matter what the actual values are, with respect to YANG defaults or server-supplied defaults. Any nodes created by the server are skipped.
'''--default-style parameter'''
{| class=wikitable border="1" | Syntax | enumeration:''' report-all trim explicit''' |- | Default: | report-all |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd \ --default-style=trim |} == --delete-empty-npcontainers == The '''--delete-empty-npcontainers''' parameter is a boolean that indicates whether the server should keep or delete empty non-presence containers in the database. If 'true', empty NP containers will be deleted. If 'false', they will not be deleted.
'''--delete-empty-npcontainers parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | FALSE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd \ --delete-empty-npcontainers=true |} == --deviation == The '''--deviation''' parameter is a leaf-list of modules that should be loaded automatically when the program starts, as a deviation module. In this mode, only the deviation statements are parsed and then made available later when the module that contains the objects being deviated is parsed. The deviations must be known to the parser before the target module is parsed. This parameter is used to identify any modules that have deviation statements for the set of modules being parsed (e.g., '''--module''' and''' --subtree''' parameters). A module can be listed with both the '''--module''' and '''--deviation''' parameters, but that is not needed unless the module contains external deviations. If the module only contains deviations for objects in the same module, then the''' --deviation '''parameter does not need to be used. The program will attempt to load each module in deviation parsing mode, in the order the parameters are entered. For the '''netconfd''' program, If any modules have fatal errors then the program will terminate. For the '''yangdump '''and''' yangcli''' programs, each module will be processed as requested.
'''--deviation parameter'''
{| class=wikitable border="1" | Syntax | module name or filespec |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | netconfdyangcliyangdump |- | Example: | netconfd \ --deviation=test1_deviations |} == --eventlog-size == The '''--eventlog-size''' parameter controls the maximum number of events that will be stored in the notification replay buffer by the '''netconfd''' server. If set to 0, then notification replay will be disabled, meaning that all requests for replay subscriptions will cause the event to be sent right away, since there are no stored notifications. The server will delete the oldest entry, when this limit is reached and a new event is added to the replay buffer. No memory is actually set aside for the notification replay buffer, so memory limits may be reached before the maximum number of events is actually stored, at any given time.
'''--eventlog-size parameter'''
{| class=wikitable border="1" | Syntax | uint32 |- | Default: | 1000 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --eventlog-size=20000 |} == --feature-disable == The '''--feature-disable''' parameter directs all programs to disable a specific feature. This parameter is a formatted string containing a module name, followed by a colon ':', followed by a feature name, e.g., test:feature1 It is an error if a '''--feature-enable''' and '''--feature-disable''' parameter specify the same feature. Parameters for unknown features will be ignored.
'''--feature-disable parameter'''
{| class=wikitable border="1" | Syntax | formatted string (module:feature |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | yangcliyangdiffyangdumpnetconfd |- | Example: | netconfd\ --feature-disable=test:feature1 |} == --feature-enable == The '''--feature-enable''' parameter directs all programs to enable a specific feature. This parameter is a formatted string containing a module name, followed by a colon ':', followed by a feature name, e.g., test:feature1 It is an error if a '''--feature-disable''' and '''--feature-enable''' parameter specify the same feature. Parameters for unknown features will be ignored.
'''--feature-enable parameter'''
{| class=wikitable border="1" | Syntax | formatted string (module:feature |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | yangcliyangdiffyangdumpnetconfd |- | Example: | netconfd --feature-enable=test:feature1 |} == --feature-enable-default == The '''--feature-enable-default''' parameter controls how '''yangdump''' will generate C code for YANG features by default. If 'true', then by default, features will be enabled. If 'false', then by default, features will be disabled. If a '''--feature-enable''' or '''--feature-disable''' parameter is present for a specific feature, then this parameter will be ignored for that feature.
'''--feature-enable-default parameter'''
{| class=wikitable border="1" | Syntax | boolean (true or false) |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcliyangdiffyangdumpnetconfd |- | Example: | netconfd \ --feature-enable-default=false |} == --hello-timeout == The''' --hello-timeout''' parameter controls the maximum number of seconds that the '''netconfd''' server will wait for a PDU, for each session. If set to 0, then hello state timeouts will be disabled, meaning that no sessions will be deleted while waiting for a PDU. It is strongly suggested that this parameter not be disabled, since a denial-of-service attack will be possible if sessions are allowed to remain in the 'hello wait' state forever. A finite number of SSH and NETCONF sessions are supported, so if an attacker simply opened lots of SSH connections to the netconf subsystem, the server would quickly run out of available sessions. Sessions cannot be deleted manually via operation if no new sessions are being allocated by the server.
'''--hello-timeout parameter'''
{| class=wikitable border="1" | Syntax | uint32; 0 == disabled; range 10 .. 3600 seconds (1 hour max) |- | Default: | 600 (10 minutes) |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --hello-timeout=300 |} == --help == The '''--help''' parameter causes program help text to be printed, and then the program will exit instead of running as normal. This parameter can be combined with the '''--help-mode''' parameter to control the verbosity of the help text. Use '''--brief''' for less, and '''--full '''for more than the normal verbosity. This parameter can be combined with the '''--version''' parameter in all programs. It can also be combined with the '''--show-errors''' parameter in '''yangdump'''. The program configuration parameters will be displayed in alphabetical order, not in schema order.
'''--help parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd --help |} == --help-mode == The '''--help-mode''' parameter is used to control the amount of detail printed when help text is requested in some command. It is always used with another command, and makes no sense by itself. It is ignored unless used with the '''--help''' parameter.
'''--help-mode parameter'''
{| class=wikitable border="1" | Syntax | choice of 3 empty leafs '''--brief --normal --full''' |- | Default: | normal |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd --help --help-mode=full |} * '''default choice: '''normal * '''choice help-mode''' ** '''brief''' *** type: empty *** This parameter specifies that brief documentation mode should be used. ** '''normal''' *** type: empty *** This parameter specifies that normal documentation mode should be used. ** '''full''' *** type: empty *** This parameter specifies that full documentation mode should be used. == --idle-timeout == The''' --idle-timeout''' parameter controls the maximum number of seconds that the '''netconfd''' server will wait for a PDU, for each session. If set to 0, then idle state timeouts will be disabled, meaning that no sessions will be deleted while waiting for a PDU. A session will never be considered idle while a notification subscription is active. It is strongly suggested that this parameter not be disabled, since a denial-of-service attack will be possible if sessions are allowed to remain in the 'idle wait' state forever. A finite number of SSH and NETCONF sessions are supported, so if an attacker simply opened lots of SSH connections to the netconf subsystem, the server would quickly run out of available sessions. This parameter will affect a operation! Make sure this timeout interval is larger than the value of the parameter used in the confirmed commit procedure. Otherwise, it is possible that the session will be terminated before the confirm-timeout interval has expired, effectively replacing that timer with this one.
'''--idle-timeout parameter'''
{| class=wikitable border="1" | Syntax | uint32; 0 == disabled; range 30 .. 360000 seconds (100 hours max) |- | Default: | 3600 (1 hour) |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --idle-timeout=30000 |} == --indent == The '''--indent''' parameter specifies the number of spaces that will be used to add to the indentation level, each time a child node is printed during program operation.
'''--indent parameter'''
{| class=wikitable border="1" | Syntax | uint32 (0 .. 9) |- | Default: | 2 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd --indent=4 |} == --log == The '''--log''' parameter specifies the file name to be used for logging program messages, instead of STDOUT. It can be used with the optional''' --log-append''' and '''--log-level''' parameters to control how the log file is written.
'''--log parameter'''
{| class=wikitable border="1" | Syntax | string: log file specification |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd --log=~/server.log& |} == --log-append == The '''--log-append''' parameter specifies that the existing log file (if any) should be appended , instead of deleted. It is ignored unless the '''--log''' parameter is present.
'''--log-append parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd --log-append \ --log=~/server.log& |} == --log-level == The '''--log-level''' parameter controls the verbosity level of messages printed to the log file or STDOUT, if no log file is specified. The log levels are incremental, meaning that each higher level includes everything from the previous level, plus additional messages. There are 7 settings that can be used: * '''none''': All logging is suppressed. Use with extreme caution! * '''error''': Error messages are printed, indicating problems that require attention. * '''warn''': Warning messages are printed, indicating problems that may require attention. * '''info''': Informational messages are printed, that indicate program status changes. * '''debug''': Debugging messages are printed that indicate program activity. * '''debug2''': Protocol debugging and trace messages are enabled. * '''debug3''': Very verbose debugging messages are enabled. This has an impact on resources and performance, and should be used with caution.
'''log-level parameter'''
{| class=wikitable border="1" | Syntax | enumeration: '''off error warn info debug debug2 debug3 debug4''' |- | Default: | --info (--debug for DEBUG builds) |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd --log-level=debug \ --log=~/server.log& |} == --max-burst == The '''--max-burst''' parameter specifies the maximum number of notifications to send in a burst, to one session. Even though TCP will control the transmission rate, this parameter can limit the memory usage due to buffering of notifications waiting to be sent. The value zero means no limit will be used at all.
'''--max-burst parameter'''
{| class=wikitable border="1" | Syntax | uint32 |- | Default: | 10 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcli |- | Example: | netconfd --max-burst=100 |} == --modpath == The '''--modpath''' parameter specifies the YANG module search path to use while searching for YANG files. It consists of a colon (':') separated list of path specifications, commonly found in Unix, such as the '''$PATH''' environment variable. This parameter overrides the '''$YUMA_MODPATH''' environment variable, if it is present.
'''--modpath parameter'''
{| class=wikitable border="1" | Syntax | string: list of directory specifications |- | Default: | '''$YUMA_MODPATH''' environment variable |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd \ --modpath=â€~/testmodules:~/modules:~/trunk/netconf/modules†|} == --module == The '''--module''' parameter is a leaf-list of modules that should be loaded automatically when the program starts. The program will attempt to load each module in the order the parameters were entered. For the '''netconfd''' program, If any modules have fatal errors then the program will terminate. For the '''yangdump''' program, each module will be processed as requested.
'''--module parameter'''
{| class=wikitable border="1" | Syntax | module name or filespec |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | netconfdyangcliyangdump |- | Example: | netconfd \ --module=test1 \ --module=test2 |} == --port == The '''--port''' parameter specifies the TCP port number(S) that should be used for NETCONF sessions. This parameter specifies the TCP port numbers to accept NETCONF session from. If any instances of this parameter are found, then the default (port 830) will not be added automatically. Up to 4 port parameter values can be entered.
'''--port parameter'''
{| class=wikitable border="1" | Syntax | uint32 |- | Default: | 830 |- | Min Allowed: | 0 |- | Max Allowed: | 4 |- | Supported by: | netconfd |- | Example: | netconfd --port=22 \ --port=830 |} == --protocols == The '''--protocols''' parameter specifies which NETCONF protocol versions should be enabled.The base:1.0 (RFC 4741/4742) and base:1.1 (RFC 6241/6242) capabilities are controlled with this parameter.
'''--protocols parameter'''
{| class=wikitable border="1" | Syntax | bits |- | Default: | netconf1.0 netconf1.1 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd –protocols=netconf1.0 |} == --running-error == The '''--running-error''' parameter is an enumeration, specifying how the '''netconfd''' server will treat errors encountered while validating the running database, when loaded from the non-volatile startup configuration at boot-time. * leaf '''running-error''' ** type: enumeration (stop or continue) ** Specifies if running configuration errors will be treated as fatal or recoverable errors.
'''startup-error parameter'''
{| class=wikitable border="1" | Syntax | enum: stop continue |- | Default: | stop |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --running-error=continue |} == --runpath == The '''--runpath''' parameter specifies the server instrumentation library (SIL) search path to use while searching for library files. It consists of a colon (':') separated list of path specifications, commonly found in Unix, such as the '''$PATH''' environment variable. This parameter overrides the '''$YUMA_RUNPATH''' environment variable, if it is present.If no path is specified or a SIL is not found, the directories '''/usr/lib/yuma''' and '''/usr/lib64/yuma''' (for 64-bit platforms) are checked.
'''--runpath parameter'''
{| class=wikitable border="1" | Syntax | string: list of directory specifications |- | Default: | '''$YUMA_RUNPATH''' environment variable |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcli |- | Example: | netconfd \ --runpath=â€~/testsil:/yuma/device†|} == --start == The '''--start''' parameter is a choice, specifying how the '''netconfd''' server will load the non-volatile startup configuration at boot-time. If no choice is made at all, then the server will check its data path for the file '''startup-cfg.xml'''. * choice '''start''' ** '''no-startup''' *** type: empty *** Specifies that no configuration will be loaded at all at boot-time ** '''factory-startup''' *** type: empty *** Force the system to use the factory configuration and delete the startup config file if it exists. Force the NV-storage startup to contain the factory default configuration. ** '''startup''' *** type: string *** Specifies the file name of the boot-time configuration. The server expects this to be an XML configuration file. Unless a path specification is included, the '''$YUMA_DATAPATH''' environment variable or --datapath parameter will be used to search for the specified file name. No '.xml' extension will be added. The exact file name will be used instead.
'''start parameter'''
{| class=wikitable border="1" | Syntax | choice: '''--no-startup --factory-startup''' '''--startup'''=''filespec'' |- | Default: | first startup-cfg.xml in the data path |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd \ --startup=$TESTDIR/testrun |} == --startup-error == The '''--startup-error''' parameter is an enumeration, specifying how the '''netconfd''' server will treat errors encountered while loading the non-volatile startup configuration at boot-time. * leaf '''startup-error''' ** type: enumeration (stop or continue) ** Specifies if startup configuration errors will be treated as fatal or recoverable errors. **
'''startup-error parameter'''
{| class=wikitable border="1" | Syntax | enum: stop continue |- | Default: | stop |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --startup-error=continue |} == --subdirs == The '''--subdirs''' parameter controls whether sub-directories should be searched or not, if they are found during a module search. If false, the file search paths for modules, scripts, and data files will not include sub-directories if they exist in the specified path.
'''--subdirs parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | netconfd \ --subdirs=false |} == --superuser == The '''--superuser''' parameter specifies the user name that the '''netconfd''' server will treat as the super-user account. The 'root' account should be used with caution. It is strongly suggested that root access be disabled in '''sshd''', and a different account be used as the NETCONF super-user account. Any NETCONF session started with this user name will be exempt from access control enforcement. This is especially useful if the current yuma-nacm.yang configuration is preventing access to the subtree itself. The super-user account can be used in this situation to repair the mis-configured access control rules. By default, no user will be accepted as the super-user account, if no value is specified. To disable all super-user account privileges, set this parameter to a zero-length string.
'''--superuser parameter'''
{| class=wikitable border="1" | Syntax | ''string'' |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --superuser=admin |} == --system-sorted == The '''--system-sorted''' parameter specifies whether the server will keep system-ordered lists and leaf-lists in sorted order. If 'true', then lists and leaf-lists will be maintained in order, based on the list keys, or leaf-list value itself. If 'false', then system ordered entries will be kept in the order they were entered in the database. All entries are maintained in schema-order (except list keys are ordered first).
'''--system-sorted parameter'''
{| class=wikitable border="1" | Syntax | ''boolean'' |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --system-sorted=false |} == --target == The '''--target''' parameter specifies the name of the database that '''netconfd''' will use as its edit target. There are two targets supported at this time: * '''running''': Edits will be written directly to the configuration. The :startup capability will also be used in this mode. This means that a operation (from to ) must be used to save any edits to non-volatile storage, for use on the next reboot. * '''candidate''': Edits will be written to the configuration. The operation must be used to save any edits in configuration into the configuration. The non-volatile storage is updated automatically in this mode, and the configuration will not be present.
'''--target parameter'''
{| class=wikitable border="1" | Syntax | enumeration: '''running''' '''candidate''' |- | Default: | candidate |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --target=running |} == --usexmlorder == The '''--usexmlorder''' parameter specifies that the '''netconfd''' server should enforce XML ordering when applicable (such as YANG list key leafs entered first). The default is not to check for adherence to strict XML order, as defined by YANG.
'''--usexmlorder parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | off |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --usexmlorder |} == --version == The '''--version''' parameter causes the program version string to be printed, and then the program will exit instead of running as normal. All Yuma version strings use the same format: DEBUG: .. or NON-DEBUG: .- An example version number that may be printed: netconfd 2.0-0 This parameter can be combined with the '''--help '''parameter.
'''--version parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd --version |} == --validate-config-only == If '''--validate-config-only''' parameter is present netconfd acts as command line YANG configuration validator. Loads the YANG schema modules, validates the startup configuration and exits without opening socket and listening for incoming sessions. This parameter can be combined with the '''--startup''' parameter specifying the xml configuration file and '''--module''' parameter specifying the modules (SILs or YANG modules). {| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --validate-config-only --startup-error=stop --startup=startup-cfg-good.xml --module=/usr/share/yuma/modules/ietf/ietf-interfaces.yang --module=/usr/share/yuma/modules/ietf/iana-if-type.yang |} == --warn-idlen == The''' --warn-idlen''' parameter controls whether identifier length warnings will be generated. The value zero disables all identifier length checking. If non-zero, then a warning will be generated if an identifier is defined which has a length is greater than this amount.
'''--warn-idlen parameter'''
{| class=wikitable border="1" | Syntax | uint32: 0 to disable, or 8 .. 1023 |- | Default: | 64 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd --warn-idlen=50 |} == --warn-linelen == The''' --warn-linelen''' parameter controls whether line length warnings will be generated. The value zero disables all line length checking. If non-zero, then a warning will be generated if a YANG file line is entered which has a length is greater than this amount. Tab characters are counted as 8 spaces.
'''--warn-linelen parameter'''
{| class=wikitable border="1" | Syntax | uint32: 0 to disable, or 40 .. 4095 |- | Default: | 72 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd --warn-linelen=79 |} == --warn-off == The''' --warn-off''' parameter suppresses a specific warning number. The error and warning numbers, and the default messages, can be viewed with the yangdump program by using the '''--show-errors''' configuration parameter. The specific warning message will be disabled for all modules. No message will be printed and the warning will not count towards the total for that module.
'''--warn-off parameter'''
{| class=wikitable border="1" | Syntax | uint32: 400 .. 899 |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 499 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd --warn-off=435 # revision order not descending |} == --with-startup == The''' --with-startup''' parameter controls whether the server will support the :startup capability or not. This is a memory intensive capability, and setting this parameter to 'false' will reduce memory usage during normal operations. The non-volatile copy of the configuration will not be saved automatically if this capability is enabled. Instead, a operation from the to configuration will be needed.
'''--with-startup parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | FALSE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --with-startup=true |} == --with-url == The''' --with-url''' parameter controls whether the server will support the :url capability or not. This capability requires file system storage.
'''--with-url parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --with-url=false |} == --with-validate == The''' --with-validate''' parameter controls whether the server will support the :validate capability or not. This is a memory intensive capability, and setting this parameter to 'false' will reduce memory usage during operations.
'''--with-validate parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfd |- | Example: | netconfd --with-validate=false |} == --yuma-home == The '''--yuma-home''' parameter specifies the project directory root to use when searching for files. If present, this directory location will override the ''''$YUMA_HOME''' environment variable, if it is set. If this parameter is set to a zero-length string, then the $'''YUMA_HOME''' environment variable will be ignored. The following directories are searched when either the '''$YUMA_HOME''' environment variable or this parameter is set: * '''$YUMA_HOME/modules''' ** This sub-tree is searched for YANG files. * '''$YUMA_HOME/data''' ** This directory is searched for data files. * '''$YUMA_HOME/scripts''' ** This directory is searched for '''yangcli''' script files.
'''yuma-home parameter'''
{| class=wikitable border="1" | Syntax | string: directory specification |- | Default: | '''$YUMA_HOME''' environment variable |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | netconfd \ --yuma-home=~/sw/netconf \ --log=~/server.log& |} yuma123_2.14/netconf/doc/yuma_docs/yuma-dev-manual.txt0000664000175000017500000056060314770023131023064 0ustar vladimirvladimir
'''Yuma Developer Manual'''
YANG-Based Unified Modular Automation Tools
Server Instrumentation Library Development
Version yuma123-2.12
= Preface = == Legal Statements == Copyright 2009 - 2012, Andy Bierman, All Rights Reserved. Copyright 2013 - 2018, Vladimir Vassilev, All Rights Reserved. == Additional Resources == This document assumes you have successfully set up the software as described in the printed document: [[Yuma Installation Guide]] Other documentation includes: [[Yuma User Manual]] [[Yuma netconfd Manual]] [[Yuma yangcli Manual]] [[Yuma yangdiff Manual]] [[Yuma yangdump Manual]] There are several sources of free information and tools for use with YANG and/or NETCONF. The following section lists the resources available at this time. === WEB Sites === * '''Netconf Central''' ** [http://www.netconfcentral.org/ http://www.netconfcentral.org/] ** Yuma Home Page *** Free information on NETCONF and YANG, tutorials, on-line YANG module validation and documentation database * '''Yuma123 OpenSource Project''' ** [http://sourceforge.net/projects/yuma123/ http://sourceforge.net/projects/yuma123/] *** Download Yuma source and documentation * '''Yang Central''' ** [http://www.yang-central.org/ http://www.yang-central.org] ** Free information and tutorials on YANG, free YANG tools for download * '''NETCONF Working Group Wiki Page''' ** [http://trac.tools.ietf.org/wg/netconf/trac/wiki http://trac.tools.ietf.org/wg/netconf/trac/wiki] ** Free information on NETCONF standardization activities and NETCONF implementations * '''NETCONF WG Status Page''' ** http://tools.ietf.org/wg/netconf/ ** IETF Internet draft status for NETCONF documents * '''libsmi Home Page''' ** [http://www.ibr.cs.tu-bs.de/projects/libsmi/ http://www.ibr.cs.tu-bs.de/projects/libsmi/] ** Free tools such as smidump, to convert SMIv2 to YANG * '''YumaWorks''' ** [http://www.yumaworks.com/ http://www.yumaworks.com] ** Offers support, training, and consulting for Yuma. ** Offers YumaPro, a professional version of Yuma that includes concurrency, external database support, sub-agent support, multiple northbound interfaces, and more. API compatible with Yuma. Availability: September, 2012. Licensed. * '''Transpacket''' ** [http://www.transpacket.com/ http://www.transpacket.com] ** Uses Yuma for configuration and monitoring of its products. === Mailing Lists === * '''NETCONF Working Group''' ** http://www.ietf.org/html.charters/netconf-charter.html ** Technical issues related to the NETCONF protocol are discussed on the NETCONF WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. * '''NETMOD Working Group''' ** [http://www.ietf.org/html.charters/netmod-charter.html http://www.ietf.org/html.charters/netmod-charter.html] ** Technical issues related to the YANG language and YANG data types are discussed on the NETMOD WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. == Conventions Used in this Document == The following formatting conventions are used throughout this document:
'''Documentation Conventions'''
{| class="wikitable" border="1" !Convention !Description |- | '''--foo''' | CLI parameter foo |- | '''''' | XML parameter foo |- | '''foo''' | '''yangcli''' command or parameter |- | '''$FOO''' | Environment variable FOO |- | '''$$foo''' | '''yangcli''' global variable foo |- | some text | Example command or PDU |- | some text | Plain text |} = Software Overview = [[Image:yuma-tools.png]] == Introduction == Refer to [[Yuma_User_Manual#Introduction]] for a complete introduction to Yuma Tools. This section focuses on the software development aspects of NETCONF, YANG, and the '''netconfd''' server. === Intended Audience === This document is intended for developers of server instrumentation library software, which can be used with the programs in the Yuma suite. It covers the design and operation of the '''netconfd''' server, and the development of server instrumentation library code, intended for use with the '''netconfd''' server. === What does Yuma Do? === The Yuma Tools suite provides automated support for development and usage of network management information. Refer to the [[Yuma User Manual]] for an introduction to the YANG data modeling language and the NETCONF protocol. This section describes the Yuma development environment and the basic tasks that a software developer needs to perform, in order to integrate YANG module instrumentation into a device. This manual contains the following information: * Yuma Development Environment * Yuma Runtime Environment * Yuma Source Code Overview * Yuma Server Instrumentation Library Development Guide Yuma Tools programs are written in the C programming language, using the 'gnu99' C standard, and should be easily integrated into any operating system or embedded device that supports the Gnu C compiler. === What is a Yuma Root? === [[Image:yuma-root-directory.png]] The Yuma Tools programs will search for some types of files in default locations * '''YANG Modules''': The 'modules' sub-directory is used as the root of the YANG module library. * '''Client Scripts''': The yangcli program looks in the 'scripts' sub-directory for user scripts. * '''Program Data''': The yangcli and netconfd programs look for saved data structures in the 'data' sub-directory. === Searching for Yuma Roots === [[Image:searching-for-yuma-roots.png]] '''1) $HOME Directory''' The first Yuma root checked when searching for files is the directory identified by the $HOME environment variable. If a ''''$HOME/modules'''', ''''$HOME/data''''. and/or ''''$HOME/scripts'''' directory exists, then it will be checked for the specified file(s). '''2) The $YUMA_HOME Directory''' The second Yuma root checked when searching for files is the directory identified by the $YUMA_HOME environment variable. This is usually set to private work directory, but a shared directory could be used as well. If a ''''$YUMA_HOME/modules'''', ''''$YUMA_HOME/data''''. and/or ''''$YUMA_HOME/scripts'''' directory exists, then it will be checked for the specified file(s). '''3) The $YUMA_INSTALL Directory''' The last Yuma root checked when searching for files is the directory identified by the $YUMA_INSTALL environment variable. If it is not set, then the default value of '/'''usr/share/yuma'''' is used instead. This is usually set to the public directory where all users should find the default modules. If a ''''$YUMA_INSTALL/modules'''', ''''$YUMA_INSTALL/data''''. and/or ''''$YUMA_INSTALL/scripts'''' directory exists, then it will be checked for the specified file(s). === What is a SIL? === A SIL is a Server Instrumentation Library. It contains the 'glue code' that binds YANG content (managed by the '''netconfd''' server), to your networking device, which implements the specific behavior, as defined by the YANG module statements. The '''netconfd''' server handles all aspects of the NETCONF protocol operation, except data model semantics that are contained in description statements. The server uses YANG files directly, loaded at boot-time or run-time, to manage all NETCONF content, operations, and notifications. Callback functions are used to hook device and data model specific behavior to database objects and RPC operations. The [https://sourceforge.net/p/yuma123/git/ci/master/tree/example-modules/helloworld/helloworld.c helloworld.c] is a minimalistic example implementing [https://sourceforge.net/p/yuma123/git/ci/master/tree/example-modules/helloworld/helloworld.yang?format=raw helloworld.yang] model. * Create the YANG module or use an existing YANG module. * Validate the YANG module with YANG compiler '''pyang''', '''yangdump''' or '''yanglint''' program and make sure it does not contain any errors. All warnings should also be examined to determine if they indicate data modeling bugs or not. * Write and compile *.C file containing the 3 mandatory functions y__init, y__init2, y__cleanup . Check the example-modules e.g. ietf-interfaces, helloworld etc. to get started. == Yuma Source Files == This section lists the files that are included within the''' netconf/src''' directory. === src/ncx Directory === This directory contains the code that is used to build the''' libyumancx.so''' binary shared library that is used by all Yuma Tools programs. It handles many of the core NETCONF/YANG data structure support, including all of the YANG/YIN, XML, and XPath processing. The following table describes the purpose of each file. Refer to the actual include file (e.g., ncx.h in /usr/include/yuma) for more details on each external function in each C source module.
'''src/ncx C Modules'''
{| class="wikitable" border="1" !
C Module
!
Description
|- | b64 | Encoding and decoding the YANG binary data type. |- | blob | Encoding and decoding the SQL BLOB data type. |- | bobhash | Implementation of the BOB hash function. |- | cap | NETCONF capability definitions and support functions |- | cfg | NETCONF database data structures and configuration locking support. |- | cli | CLI parameter parsing data driven by YANG definitions. |- | conf | Text .conf file encoding and decoding, data driven by YANG definitions. |- | def_reg | Hash-driven definition registry for quick lookup support of some data structures. Contains back-pointers to the actual data. |- | dlq | Double linked queue support |- | ext | YANG extension data structure support |- | grp | YANG grouping data structure support |- | help | Automatic help text, data-driven by YANG definitions |- | log | System logging support |- | ncx_appinfo | Yuma Netconf Extensions (NCX) support |- | ncx | YANG module data structure support, and some utilities |- | ncx_feature | YANG feature and if-feature statement data structure support |- | ncx_list | Support for the '''ncx_list_t''' data structure, used for YANG bits and '''ncx:xsdlist''' data types. |- | ncxmod | File Management: Controls finding and searching for YANG/YIN files, data files, and script files |- | ncx_num | Yuma '''ncx_num_t''' data structure support. Used for processing value nodes and XPath numbers. |- | ncx_str | Yuma '''string''' support. |- | obj | Yuma object '''(obj_template_t)''' data structure access |- | obj_help | Automated object help support used with help module |- | op | NETCONF operations definitions and support functions |- | rpc | NETCONF and data structures and support functions |- | rpc_err | NETCONF data structures and support functions |- | runstack | Script execution stack support for yangcli scripts |- | send_buff | NETCONF send buffer function |- | ses | NETCONF session data structures and session access functions |- | ses_msg | Message buffering support for NETCONF sessions |- | status | Error code definitions and error support functions |- | tk | Token chain data structures used for parsing YANG, XPath and other syntaxes. |- | top | Top-level XML node registration support. The and elements are registered by the server. The , , and elements are registered by the client. |- | tstamp | Time and date stamp support functions |- | typ | YANG typedef data structures and access functions |- | val | Yuma value tree data structures and access functions |- | val_util | High-level utilities for some common SIL tasks related to the value tree. |- | var | User variable support, used by '''yangcli''' and (TBD) XPath |- | xml_msg | XML message data structures and support functions |- | xmlns | XML Namespace registry |- | xml_util | XML parse and utility functions |- | xml_val | High level support functions for constructing XML-ready '''val_value_t '''data structures |- | xml_wr | XML output support functions and access-control protected message generation support |- | xpath1 | XPath 1.0 implementation |- | xpath | XPath data structures and support functions |- | xpath_wr | Support for generating XPath expression content within an XML instance document |- | xpath_yang | Special YANG XPath construct support, such as path expressions and instance identifiers |- | yang | YANG definitions and general support functions |- | yang_ext | YANG parsing and validation of the extension statement |- | yang_grp | YANG parsing and validation of the grouping statement |- | yang_obj | YANG parsing and validation of the rpc, notification, and data definition statements |- | yang_parse | Top level YANG parse and validation support |- | yang_typ | YANG typedef and type statement support |- | yin | YANG to YIN mapping definitions |- | yinyang | YIN to YANG translation |} === src/platform Directory === This directory contains platform support include files and Makefile support files. It is used by all Yuma C modules to provide an insulating layer between Yuma programs and the hardware platform that is used. For example the '''m__getMem, m__getObj''', and '''m__freeMem''' macros are used instead of '''malloc''' and '''free''' functions directly. The following table describes the files that are contained in this directory:
'''src/platform Files'''
{| class="wikitable" border="1" !
File
!
Description
|- | procdefs.h | Platform definitions. Contains basic data types and macros used throughout the Yuma code. All C files include this file before any other Yuma files. |} === src/agt Directory === This directory contains the NETCONF server implementation and built-in module SIL code. A library called '''libyumaagt.so''' is built and loaded by the '''netconfd''' program. The following table describes the C modules contained in this directory:
'''src/agt C Modules'''
{| class="wikitable" border="1" !
C Module
!
Description
|- | agt_acm | NETCONF access control implementation. Contains the '''yuma-nacm''' module SIL callback functions. |- | agt | Server initialization and cleanup control points. Also contains the '''agt_profile_t''' data structure. |- | agt_cap | Server capabilities. Generates the server element content. |- | agt_cb | SIL callback support functions. |- | agt_cli | Server CLI and .conf file control functions. |- | agt_connect | Handles the internal message sent from the '''netconf-subsystem''' to the '''netconfd''' server. |- | agt_hello | Handles the incoming client message and generates the server message. |- | agt_if | Yuma Interfaces module implementation. Contains the '''yuma-interfaces '''module SIL callback functions. |- | agt_ncx | NETCONF protocol operation implementation. Contains the '''yuma-netconf '''module SIL callback functions. |- | agt_ncxserver | Implements the ncxserver loop, handling the IO between the server NETCONF sessions and the '''netconf-subsystem''' thin client program. |- | agt_not | NETCONF Notifications implementation. Contains the '''notifications '''and''' nc-notifications''' modules SIL callback functions. |- | agt_proc | '''/proc '''system monitoring implementation. Contains the '''yuma-proc''' module SIL callback functions. |- | agt_rpc | NETCONF RPC operation handler |- | agt_rpcerr | NETCONF generation |- | agt_ses | NETCONF session support and implementation of the Yuma Session extensions. Contains the '''yuma-mysession''' module SIL callback functions. |- | agt_signal | Server signal handling support |- | agt_state | Standard NETCONF monitoring implementation. Contains the''' ietf-netconf-monitoring''' SIL callback functions. |- | agt_sys | Server system monitoring and notification generation. Contains the '''yuma-system''' module SIL callback functions. |- | agt_timer | SIL periodic timer callback support functions |- | agt_top | Server registration and dispatch of top-level XML messages |- | agt_tree | Subtree filtering implementation |- | agt_util | SIL callback utilities |- | agt_val | Server validation, commit, and rollback support for NETCONF database operations |- | agt_val_parse | Incoming and content parse and complete YANG constraint validation |- | agt_xml | Server XML processing interface to '''ncx/xml_util''' functions |- | agt_xpath | XPath filtering implementation |} === src/mgr Directory === This directory contains the NETCONF server implementation and built-in module SIL code. A library called '''libyumaagt.so''' is built and loaded by the '''netconfd''' program. The following table describes the C modules contained in this directory: This directory contains the NETCONF client support code. It handles all the basic NETCONF details so a simple internal API can be used by NETCONF applications such as '''yangcli'''. A library called '''libyumamgr.so''' is built and installed. The '''yangcli''' program uses this library. The following table describes the C modules contained in this directory:
'''src/mgr C Modules'''
{| class="wikitable" border="1" !
C Module
!
Description
|- | mgr | Client initialization and cleanup control points. Also contains manager session control block data structure support functions. |- | mgr_cap | Generate the client NETCONF element content |- | mgr_hello | Handles the incoming server message and generates the client message. |- | mgr_io | Handles SSH server IO support for client NETCONF sessions |- | mgr_not | Handles incoming server messages |- | mgr_rpc | Generate messages going to the NETCONF server and process incoming messages from the NETCONF server. |- | mgr_ses | Handles all aspects of client NETCONF sessions. |- | mgr_signal | Client signal handler |- | mgr_top | Client registration and dispatch of top-level XML messages |- | mgr_val_parse | Incoming , , and content parse and complete YANG constraint validation. |- | mgr_xml | Client XML processing interface to '''ncx/xml_util''' functions |} === src/subsys Directory === This directory contains the '''netconf-subsystem''' program. This is a thin-client application that just transfers input and output between the SSH server and the NETCONF server. It contains one C source module called '''netconf-subsystem'''. This is a stand-alone binary that is installed together with the '''netconfd''' program. It is installed in the '''/usr/sbin/ '''directory. === src/netconfd Directory === This directory contains the '''netconfd''' program, which implements the NETCONF server. It contains one C module called '''netconfd''', which defines the NETCONF server 'main' function. It is installed in the '''/usr/sbin/ '''directory. === src/yangcli Directory === This directory contains the '''yangcli''' program, which is the Yuma NETCONF client program. It is installed in the '''/usr/bin/ '''directory. The following table describes the C modules contained in this directory:
'''src/yangcli C Modules'''
{| class="wikitable" border="1" !
C Module
!
Description
|- | yangcli | NETCONF client program, provides interactive and script-based CLI, based on YANG modules. |- | yangcli_autoload | Uses the server capabilities from the message to automatically load any missing YANG modules from the server, and apply all features and deviations. |- | yang_autolock | Provides protocol exchange support for the high-level get-locks and release-locks commands |- | yangcli_cmd | Main local command processor |- | yangcli_list | Implements yangcli 'list' command |- | yangcli_save | Implements yangcli 'save' command |- | yangcli_show | Implements yangcli 'show' command |- | yangcli_tab | Implements context-sensitive tab word completion |- | yangcli_util | Utilities used by other yangcli C modules |} === src/yangdiff Directory === This directory contains the '''yangdiff''' program, which is the Yuma YANG module compare program. It is installed in the '''/usr/bin/ '''directory. The following table describes the C modules contained in this directory:
'''src/yangdiff'''
{| class="wikitable" border="1" !
C Module
!
Description
|- | yangdiff | YANG module semantic compare program |- | yangdiff_grp | Implements semantic diff for YANG grouping statement |- | yangdiff_obj | Implements semantic diff for YANG data definition statements |- | yangdiff_typ | Implements semantic diff for YANG typedef and type statements |- | yangdiff_util | Utilities used by the other '''yangdiff''' C modules |} === src/yangdump Directory === This directory contains the '''yangdump''' program, which is the Yuma YANG compiler program. It is installed in the '''/usr/bin/ '''directory. The following table describes the C modules contained in this directory:
'''src/yangdump C Modules'''
{| class="wikitable" border="1" !
C Module
!
Description
|- | c | Implements SIL C file generation |- | c_util | Utilities used for SIL code generation |- | h | Implements SIL H file generation |- | html | Implements YANG to HTML translation |- | sql | Implements SQL generation for YANG module WEB Docs |- | xsd | Implements YANG to XSD translation |- | xsd_typ | Implements YANG typedef/type statement to XSD simpleType and complexType statements |- | xsd_yang | YANG to XSD translation utilities |- | yangdump | YANG module compiler |- | yangdump_util | Utilities used by all yangdump C modules |- | yangyin | Implements YANG to YIN translation |} == Server Design == This section describes the basic design used in the '''netconfd''' server. [[Image:netconf-server.png]] '''Initialization:''' The '''netconfd''' server will process the YANG modules, CLI parameters, config file parameters, and startup device NETCONF database, then wait for NETCONF sessions. '''ncxserver loop:''' The SSH2 server will listen for incoming connections which request the 'netconf' subsystem. When a new session request is received, the '''netconf-subsystem''' program is called, which opens a local connection to the '''netconfd''' server, via the ncxserver loop. NETCONF requests are processed by the internal NETCONF stack. The module-specific callback functions (blue boxes) can be loaded into the system at build-time or run-time. This is the device instrumentation code, also called a server implementation library (SIL). For example, for '''libtoaster''', this is the code that controls the toaster hardware. '''Cleanup''': If the or operations are invoked, then the server will cleanup. For a reboot, the init cycle is started again, instead of exiting the program. === YANG Native Operation === [[Image:yang-to-internal-data.png]] Yuma uses YANG source modules directly to implement NETCONF protocol operations automatically within the server. The same YANG parser is used by all Yuma programs. It is located in the 'ncx' source directory (libyumancx.so). There are several different parsing modes, which is set by the application. In the 'server mode', the descriptive statements, such as 'description' and 'reference' are discarded upon input. Only the machine-readable statements are saved. All possible database validation, filtering, processing, initialization, NV-storage, and error processing is done, based on these machine readable statements. For example, in order to set the platform-specific default value for some leaf, instead of hard-coded it into the server instrumentation, the default is stored in YANG data instead. The YANG file can be altered, either directly (by editing) or indirectly (via deviation statements), and the new or altered default value specified there. In addition, range statements, patterns, XPath expressions, and all other machine-readable statements are all processed automatically, so the YANG statements themselves are like server source code. YANG also allows vendor and platform-specific deviations to be specified, which are like generic patches to the common YANG module for whatever purpose needed. YANG also allows annotations to be defined and added to YANG modules, which are specified with the 'extension' statement. Yuma uses some extensions to control some automation features, but any module can define extensions, and module instrumentation code can access these annotation during server operation, to control device behavior. There are CLI parameters that can be used to control parser behavior such as warning suppression, and protocol behavior related to the YANG content, such as XML order enforcement and NETCONF protocol operation support. These parameters are stored in the server profile, which can be customized for each platform. === YANG Object Tree === [[Image:yang-object-tree.png]] The YANG statements found in a module are converted to internal data structures. For NETCONF and database operations, a single tree of '''obj_template_t '''data''' '''structures is maintained by the server. This tree represents all the NETCONF data that is supported by the server. It does not represent any actual data structure instances. It just defines the data instances that are allowed to exist on the server. '''Raw YANG vs. Cooked YANG:''' Some of the nodes in this tree represent the exact YANG statements that the data modeler has used, such as 'augment', 'refine', and 'uses', but these nodes are not used directly in the object tree. They exist in the object tree, but they are processed to produce a final set of YANG data statements, translated into 'cooked' nodes in the object tree. If any deviation statements are used by server implementation of a YANG data node (to change it to match the actual platform implementation of the data node), then these are also 'patched' into the cooked YANG nodes in the object tree. === YANG Data Tree === [[Image:yang-data-tree.png]] A YANG data tree represents the instances of 1 or more of the objects in the object tree. Each NETCONF database is a separate data tree. A data tree is constructed for each incoming message as well. The server has automated functions to process the data tree, based on the desired NETCONF operation and the object tree node corresponding to each data node. Every NETCONF node (including database nodes) are distinguished with XML Qualified Names (QName). The YANG module namespace is used as the XML namespace, and the YANG identifier is used as the XML local name. Each data node contains a pointer back to its object tree schema node. The value tree is comprised of the '''val_value_t '''structure. Only real data is actually stored in the value tree. For example, there are no data tree nodes for choices and cases. These are conceptual layers, not real layers, within the data tree. The NETCONF server engine accesses individual SIL callback functions through the data tree and object tree. Each data node contains a pointer to its corresponding object node. Each data node may have several different callback functions stored in the object tree node. Usually, the actual configuration value is stored in the database, However, '''virtual data nodes''' are also supported. These are simply placeholder nodes within the data tree, and usually used for non-configuration nodes, such as counters. Instead of using a static value stored in the data node, a callback function is used to retrieve the instrumentation value each time it is accessed. === Service Layering === All of the major server functions are supported by service layers in the 'agt' or 'ncx' libraries: * '''Memory management''': macros in '''platform/procdefs.h''' are used instead of using direct heap functions. The macros '''m__getMem '''or '''m__getObj '''are used by Yuma code to allocate memory. Both of these functions increment a global counter called '''malloc_count'''. The macro''' m__free''' is used to delete all malloced memory. This macro increments a global counter called '''free_count'''. When a Yuma program exists, it checks if '''malloc_count''' equals '''free_count''', and if not, generates an error message. If this occurs, the '''MEMTRACE=1''' parameter can be added to the make command to activate 'mtrace' debugging. * '''Queue management''': APIs in '''ncx/dlq.h''' are used for all double-linked queue management. * '''XML namespaces''': XML namespaces (including YANG module namespaces) are managed with functions in''' ncx/xmlns.h'''. An internal 'namespace ID is used internally instead of the actual URI. * '''XML parsing''': XML input processing is found in '''ncx/xml_util.h''' data structures and functions. * '''XML message processing''': XML message support is found in '''ncx/xml_msg.h''' data structures and functions. * '''XML message writing with access control''': XML message generation is controlled through API functions located in '''ncx/xml_wr.h'''. High level (value tree output) and low-level (individual tag output) XML output functions are provided, which hide all namespace, indentation, and other details. Access control is integrated into XML message output to enforce the configured data access policies uniformly for all RPC operations and notifications. The access control model cannot be bypassed by any dynamically loaded module server instrumentation code. * '''XPath Services''': All NETCONF XPath filtering, and all YANG XPath-based constraint validation, is handled with common data structures and API functions. The XPath 1.0 implementation is native to the server, and uses the object and value trees directly to generate XPath results for NETCONF and YANG purposes. NETCONF uses XPath differently than XSLT, and libxml2 XPath processing is memory intensive. These functions are located in '''ncx/xpath.h''', '''ncx/xpath1.h''', and '''ncx/xpath_yang.h'''. XPath filtered responses are generated in '''agt/agt_xpath.c'''. * '''Logging service''': Encapsulates server output to a log file or to the standard output, filtered by a configurable log level. Located in '''ncx/log.h'''. In addition, the macro '''SET_ERROR()''' in '''ncx/status.h '''is used to report programming errors to the log. * '''Session management''': All server activity is associated with a session. The session control block and API functions are located in '''ncx/ses.h'''. All input, output, access control, and protocol operation support is controlled through the session control block (ses_cb_t). * '''Timer service''': A periodic timer service is available to SIL modules for performing background maintenance within the main service loop. These functions are located in '''agt/agt_timer.h'''. * '''Connection management''': All TCP connections to the netconfd server are controlled through a main service loop, located in '''agt/agt_ncxserver.c'''. It is expected that the 'select' loop in this file will be replaced in embedded systems. The default netconfd server actually listens for local connections on an AF_LOCAL socket. The openSSH server listens for connections on port 830 (or other configured TCP ports), and the netconf-subsystem thin client acts as a conduit between the SSH server and the '''netconfd''' server. * '''Database management''': All configuration databases use a common configuration template, defined in '''ncx/cfg.h'''. Locking and other generic database functions are handled in this module. The actual manipulation of the value tree is handled by API functions in '''ncx/val.h''', '''ncx/val_util.h, agt/agt_val_parse.h, '''and '''agt/agt_val.h'''. * '''NETCONF operations''': All standard NETCONF RPC callback functions are located in '''agt/agt_ncx.c'''. All operations are completely automated, so there is no server instrumentation APIs in this file. * '''NETCONF request processing''': All requests and replies use common data structures and APIs, found in '''ncx/rpc.h''' and''' agt/agt_rpc.h'''. Automated reply generation, automatic filter processing, and message state data is contained in the RPC message control block. * '''NETCONF error reporting:''' All elements use common data structures defined in ncx/rpc_err.h and agt/agt_rpcerr.h. Most errors are handled automatically, but 'description statement' semantics need to be enforced by the SIL callback functions. These functions use the API functions in '''agt/agt_util.h''' (such as agt_record_error) to generate data structures that will be translated to the proper contents when a reply is sent. * '''YANG module library management:''' All YANG modules are loaded into a common data structure (ncx_module_t) located in ncx/ncxtypes.h. The API functions in''' ncx/ncxmod.h '''(such as ncxmod_load_module) are used to locate YANG modules, parse them, and store the internal data structures in a central library. Multiple versions of the same module can be loaded at once, as required by YANG. === Session Control Block === Once a NETCONF session is started, it is assigned a session control block for the life of the session. All NETCONF and system activity in driven through this interface, so the '''ncxserver''' loop can be replaced in an embedded system. Each session control block (ses_scb_t) controls the input and output for one session, which is associated with one SSH user name. Access control (see ietf-netconf-acm.yang) is enforced within the context of a session control block. Unauthorized return data is automatically removed from the response. Unauthorized or database write requests are automatically rejected with an 'access-denied' error-tag. The user preferences for each session are also stored in this data structure. They are initially derived from the server default values, but can be altered with the operation and retrieved with the operation. === Server Message Flows === [[Image:main-server-components.png]] The '''netconfd''' server provides the following type of components: * NETCONF session management * NETCONF/YANG database management * NETCONF/YANG protocol operations * Access control configuration and enforcement * RPC error reporting * Notification subscription management * Default data retrieval processing * Database editing * Database validation * Subtree and XPath retrieval filtering * Dynamic and static capability management * Conditional object management (if-feature, when) * Memory management * Logging management * Timer services All NETCONF and YANG protocol operation details are handled automatically within the '''netconfd''' server. All database locking and editing is also handled by the server. There are callback functions available at different points of the processing model for your module specific instrumentation code to process each server request, and/or generate notifications. Everything except the 'description statement' semantics are usually handled The server instrumentation stub files associated with the data model semantics can be generated automatically with the '''yangdump''' program. The developer fills in server callback functions to activate the networking device behavior represented by each YANG data model. === Main ncxserver Loop === [[Image:netconf-server-io-loop.png]] The '''ncxserver''' loop does very little, and it is designed to be replaced in an embedded server that has its own SSH server: * A client request to start an SSH session results in an SSH channel being established to an instance of the '''netconf-subsystem''' program. * The '''netconf-subsystem '''program will open a local socket (/tmp/ncxserver.sock) and send a proprietary message to the netconfd server, which is listening on this local socket with a select loop (in agt_ncxserver.c). * When a valid message is received by '''netconfd''', a new NETCONF session is created. * After sending the message, the '''netconf-subsystem '''program goes into 'transfer mode', and simply passes input from the SSH channel to the '''netconfd''' server, and passes output from the '''netconfd''' server to the SSH server. * The '''ncxserver''' loop simply waits for input on the open connections, with a quick timeout. Each timeout, the server checks if a reboot, shutdown, signal, or other event occurred that needs attention. * Notifications may also be sent during the timeout check, if any events are queued for processing. The '''--max-burst''' configuration parameter controls the number of notifications sent to each notification subscription, during this timeout check. * Input messages are buffered, and when a complete message is received (based on the NETCONF End-of-Message marker), it is processed by the server and any instrumentation module callback functions that are affected by the request. When the agt_ncxserver_run function in agt/agt_ncxserver.c is replaced within an embedded system, the replacement code must handle the following tasks: * Call '''agt_ses_new_session''' in '''agt/agt_ses.c''' when a new NETCONF session starts. * Call '''ses_accept_input''' in '''ncx/ses.c''' with the correct session control block when NETCONF data is received. * Call '''agt_ses_process_first_ready''' in '''agt/agt_ses.c''' after input is received. This should be called repeatedly until all serialized NETCONF messages have been processed. * Call '''agt_ses_kill_session''' in '''agt/agt_ses.c''' when the NETCONF session is terminated. * The following functions are used for sending NETCONF responses, if responses are buffered instead of sent directly (streamed). ** '''ses_msg_send_buffs''' in '''ncx/ses_msg.c''' is used to output any queued send buffers. * The following functions need to be called periodically: ** '''agt_shutdown_requested''' in '''agt/agt_util.c''' to check if the server should terminate or reboot ** '''agt_ses_check_timeouts''' in '''agt/agt_ses.c''' to check for idle sessions or sessions stuck waiting for a NETCONF message. ** '''agt_timer_handler''' in '''agt/agt_timer.c''' to process server and SIL periodic callback functions. ** '''send_some_notifications '''in '''agt/agt_ncxserver.c''' to process some outgoing notifications. === SIL Callback Functions === [[Image:netconf-callback-points.png]] * Top Level: The top-level incoming messages are registered, not hard-wired, in the server message processing design. The '''agt_ncxserver''' module accepts the message from '''netconf-subsystem'''. The '''agt_rpc''' module accepts the NETCONF message. Additional messages can be supported by the server using the '''top_register_node''' function. * All RPC operations are implemented in a data-driven fashion by the server. Each NETCONF operation is handled by a separate function in '''agt_ncx.c'''. Any proprietary operation can be automatically supported, using the '''agt_rpc_register_method''' function. ** Note: Once the YANG module is loaded into the server, all RPC operations defined in the module are available. If no SIL code is found, these will be dummy 'no-op' functions. This mode can be used to provide some server simulation capability for client applications under development. * All database operations are performed in a structured manner, using special database access callback functions. Not all database nodes need callback functions. One callback function can be used for each 'phase', or the same function can be used for multiple phases. The '''agt_cb_register_callback''' function in''' agt/agt_cb.c''' is used by SIL code to hook into NETCONF database operations. == Server Operation == This section briefly describes the server internal behavior for some basic NETCONF operations. === Initialization === The file '''netconfd/netconfd.c''' contains the initial 'main' function that is used to start the server. * The common services support for most core data structures is located in 'libyumancx.so'. The ''''ncx_init'''' function is called to setup these data structures. This function also calls the bootstrap_cli function in ncx/ncx.c, which processes some key configuration parameters that need to be set right away, such as the logging parameters and the module search path. * Most of the actual server code is located in the 'agt' directory. The ''''agt_init1'''' function is called to initialize core server functions. The configuration parameters are processed, and the server profile is completed. ** The''' agt_profile_t '''data structure in agt/agt.h is used to contain all the vendor-related boot-time options, such as the database target (candidate or running). The''' init_server_profile''' function can be edited if the Yuma default values are not desired. This will insure the proper factory defaults for server behavior are used, even if no configuration parameters are provided. * The function '''init_server_profile''' in '''agt/agt.c''' is used to set the factory defaults for the server behavior. The '''agt_init1''' function also loads the core NETCONF protocol, '''netconfd''' CLI, and YANG data type modules. * Note: '''netconfd''' uses '''yuma-netconf.yang''', not '''ietf-netconf.yang''' to support a data-driven implementation. The only difference is that the yuma version adds some data structures and extensions (such as ncx:root), to automate processing of all NETCONF messages. After the core definition modules are loaded successfully, the''' agt_cli_process_input''' function in '''agt/agt_cli.c''' is called to process any command line and/or configuration file parameters that have been entered. * Note: Any defaults set in the G module definitions will be added to the CLI parameter set. The '''val_set_by_default''' function in '''ncx/val.c''' can be used to check if the node is set by the server to the YANG default value. If not set, and the node has the YANG default value, then the client set this value explicitly. This is different than the '''val_is_default''' function in '''ncx/val.c''', which just checks if the node contains the YANG default value. All the configuration parameters are saved, and those that can be processed right away are handled. The '''agt_cli_get_valset''' function in '''agt/agt_cli.c''' can be used to retrieve the entire set of load-time configuration parameters. === Loading Modules and SIL Code === YANG modules and their associated device instrumentation can be loaded dynamically with the '''--module''' configuration parameter. Some examples are shown below: module=foo module=bar module=baz@2009-01-05 module=~/mymodules/myfoo.yang * The '''ncxmod_find_sil_file''' function in '''ncx/ncxmod.c''' is used to find the library code associated with the each module name. The following search sequence is followed: ** Check the '''$YUMA_HOME/target/lib''' directory ** Check each directory in the '''$YUMA_RUNPATH''' environment variable or '''--runpath''' configuration variable. ** Check the''' /usr/lib/yuma''' directory * If the module parameter contains any sub-directories or a file extension, then it is treated as a file, and the module search path will not be used. Instead the absolute or relative file specification will be used. * If the first term starts with an environment variable or the tilde (~) character, and will be expanded first * If the 'at sign' (@) followed by a revision date is present, then that exact revision will be loaded. * If no file extension or directories are specified, then the module search path is checked for YANG and YIN files that match. The first match will be used, which may not be the newest, depending on the actual search path sequence. * The '''$YUMA_MODPATH''' environment variable or '''--modpath''' configuration parameter can be used to configure one or more directory sub-trees to be searched. * The '''$YUMA_HOME''' environment variable or '''--yuma-home''' configuration parameter can be used to specify the Yuma project tree to use if nothing is found in the currect directory or the module search path. * The '''$YUMA_INSTALL''' environment variable or default Yuma install location (/usr/share/yuma/modules) will be used as a last resort to find a YANG or YIN file. The server processes''' --module''' parameters by first checking if a dynamic library can be found which has an 'soname' that matches the module name. If so, then the SIL phase 1 initialization function is called, and that function is expected to call the ncxmod_load_module function. If no SIL file can be found for the module, then the server will load the YANG module anyway, and support database operations for the module, for provisioning purposes. Any RPC operations defined in the module will also be accepted (depending on access control settings), but the action will not actually be performed. Only the input parameters will be checked, and or some returned. === Core Module Initialization === The '''agt_init2''' function in '''agt/agt.c''' is called after the configuration parameters have been collected. * Initialize the core server code modules * Static device-specific modules can be added to the agt_init2 function after the core modules have been initialized * Any 'module' parameters found in the CLI or server configuration file are processed. * The '''agt_cap_set_modules''' function in '''agt/agt_cap.c''' is called to set the initial module capabilities for the '''ietf-netconf-monitoring''' module === Startup Configuration Processing === After the static and dynamic server modules are loaded, the '''--startup''' (or '''--no-startup''') parameter is processed by '''agt_init2''' in '''agt/agt.c''': * If the --startup parameter is used and includes any sub-directories, it is treated as a file and must be found, as specified. * Otherwise, the '''$YUMA_DATAPATH '''environment variable or''' --datapath''' configuration parameter can be used to determine where to find the startup configuration file. * If neither the '''--startup''' or '''--no-startup''' configuration parameter is present, then the data search path will be used to find the default '''startup-cfg.xml''' * The '''$YUMA_HOME''' environment variable or --yuma-home configuration parameter is checked if no file is found in the data search path. The '''$YUMA_HOME/data''' directory is checked if this parameter is set. * The '''$YUMA_INSTALL''' environment variable or default location (/etc/yuma/) is checked next, if the startup configuration is still not found. It is a fatal error if a startup config is specified and it cannot be found. As the startup configuration is loaded, any SIL callbacks that have been registered will be invoked for the association data present in the startup configuration file. The edit operation will be OP_EDITOP_LOAD during this callback. After the startup configuration is loaded into the running configuration database, all the stage 2 initialization routines are called. These are needed for modules which add read-only data nodes to the tree containing the running configuration. SIL modules may also use their 'init2' function to create factory default configuration nodes (which can be saved for the next reboot). === Process an Incoming Request === [[Image:5-phase-rpc-processing-model.png]] * '''PARSE Phase:''' The incoming buffer is converted to a stream of XML nodes, using the '''xmlTextReader''' functions from '''libxml2'''. The agt_val_parse function is used to convert the stream of XML nodes to a '''val_value_t''' structure, representing the incoming request according to the YANG definition for the RPC operation. An '''rpc_msg_t''' structure is also built for the request. * '''VALIDATE Phase: '''If a message is parsed correctly,''' '''then the incoming message is validated according to the YANG machine-readable constraints. Any description statement constraints need to be checked with a callback function. The''' agt_rpc_register_method''' function in '''agt/agt_rpc.c''' is used to register callback functions. * '''INVOKE Phase: '''If the message is validated correctly, then the invoke callback is executed. This is usually the only required callback function. Without it, the RPC operation has no affect. This callback will set fields in the '''rpc_msg_t''' header that will allow the server to construct or stream the message back to the client. * '''REPLY Phase: '''Unless some catastrophic error occurs, the server will generate an response. If any elements are needed, they are generated first. If there is any response data to send, that is generated or streamed (via callback function provided earlier) at this time. Any unauthorized data (according to to the '''ietf-netconf-acm.yang''' module configuration) will be silently dropped from the message payload. If there were no errors and no data to send, then an resonse is generated. * '''POST_REPLY Phase: '''After the response has been sent, a rarely-used callback function can be invoked to cleanup any memory allocation or other data-model related tasks. For example, if the''' rpc_user1''' or '''rpc_user2''' pointers in the message header contain allocated memory then they need to be freed at this time. === Edit the Database === [[Image:3-phase-databse-editing-model.png]] * '''Validate Phase:''' The server will determine the edit operation and the actual nodes in the target database (candidate or running) that will be affected by the operation. All of the machine-readable YANG statements which apply to the affected node(s) are tested against the incoming PDU and the target database. If there are no errors, the server will search for a SIL validate callback function for the affected node(s). If the SIL code has registered a database callback function for the node or its local ancestors, it will be invoked. This SIL callback function usually checks additional constraints that are contained in the YANG description statements for the database objects. * '''Test-Apply and Apply Phase:''' If the validate phase completes without errors, then the requested changes are applied to the target database. If the target database is the running configuration, or if the edit-config 'test-option' parameter is set to 'test-then-set' (the default if '''--with-validate'''=true), then the test-apply phase is executed first. This is essentially the same as the real apply phase, except that changes are made to a copy of the target database. Once all objects have been altered as requested, the entire test database is validated, including all cross-referential integrity tests. If this test completes without any errors, then the procedure is repeated on the real target database. ** Note: This phase is used for the internal data tree manipulation and validation only. It is not used to alter device behavior. Resources may need to be reserved during the SIL apply callback, but the database changes are not activated at this time. * '''Commit or Rollback Phase:''' If the validate and apply phases complete without errors, then the server will search for SIL commit callback functions for the affected node(s) in the target database. This SIL callback phase is used to apply the changes to the device and/or network. It is only called when a commit procedure is attempted. This can be due to a operation, or an or operation on the running database. ** Note: If there are errors during the commit phase, then the backup configuration will be applied, and the server will search for a SIL callback to invoke with a 'rollback operation'. The same procedure is used for confirmed commit operations which timeout or are canceled by the client. === Save the Database === The following bullets describe how the server saves configuration changes to non-volatile storage: * If the '''--with-startup=true''' parameter is used, then the server will support the :startup capability. In this case, the command needs to be used to cause the running configuration to be saved. * If the '''--with-startup=false''' parameter is used, then the server will not support the :startup capability. In this case, the database will be saved each time the running configuration is changed. * The or operations will cause the startup configuration file to be saved, even if nothing has changed. This allows an operator to replace a corrupted or missing startup configuration file at any time. * The database is saved with the '''agt_ncx_cfg_save '''function in '''agt/agt_ncx.c.''' * The '''with-defaults''' 'explicit' mode is used during the save operation to filter the database contents. ** Any values that have been set by the client will be saved in NV-storage. ** Any value set by the server to a YANG default value will not be saved in the database. ** If the server create a node that does not have a YANG default value (e.g., containers, lists, keys), then this node will be saved in NV storage. * If the '''--startup=filespec''' parameter is used, then the server will save the database by overwriting that file. The file will be renamed to backup-cfg.xml first. * If the '''--no-startup''' parameter is used, or no startup file is specified and no default is found, then the server will create a file called 'startup-cfg.xml', in the following manner: ** If the '''$YUMA_HOME''' variable is set, the configuration will be saved in '''$YUMA_HOME/data/startup-cfg.xml'''. ** Otherwise, the configuration will be saved in''' $HOME/.yuma'''/'''startup-cfg.xml.''' * The database is saved as an XML instance document, using the element in the NETCONF 'base' namespace as the root element. Each top-level YANG module supported by the server, which contains some explicit configuration data, will be saved as a child node of the element. There is no particular order to the top-level data model elements. == Built-in Server Modules == There are several YANG modules which are implemented within the server, and not loaded at run-time like a dynamic SIL module. Some of them are NETCONF standard modules and some are Yuma extension modules. === ietf-inet-types.yang === This module contains the standard YANG Internet address types. These types are available for commonly used management object types. A YANG module author should check this module first, before creating any new data types with the YANG typedef statement. There are no accessible objects in this module, so there are no SIL callback functions. The YANG data-types are supported within the Yuma engine core modules, such as '''ncx/val.c '''and '''ncx/xml_wr.c.''' === ietf-netconf-monitoring.yang === The standard NETCONF Monitoring module is used to examine the capabilities, current state, and statistics related to the NETCONF server. The entire module is supported. This module is also used to retrieve the actual YANG or YIN files (or URLs for them) that the server is using. Clients can use the RPC operation to retrieve the YANG or YIN files listed in the '''/netconf-state/schemas''' subtree. A client will normally check the message from the server for module capabilities, and use its own local copy of a server YANG module, if it can. If not, then the function can be used to retrieve the YANG module. The '''agt/agt_state.c''' contains the SIL callback functions for this module. === ietf-with-defaults.yang === The standard extension to some NETCONF operations is defined in this module. This parameter is added to the , , and operations to let the client control how 'default leafs' are returned by the server. The Yuma server can be configured to use any of the default handling styles (report-all, trim, or explicit). The filtering of default nodes is handled automatically by the server support functions in '''agt/agt_util.c''', and the XML write functions in''' ncx/xml_wr.c.''' === ietf-yang-types.yang === This module contains the standard YANG general user data types. These types are available for commonly used derived types. A YANG module author should check this module first, before creating any new data types with the YANG typedef statement. There are no accessible objects in this module, so there are no SIL callback functions. The YANG data-types are supported within the Yuma engine core modules, such as '''ncx/val.c''' and '''ncx/xml_wr.c.''' === ietf-netconf-acm.yang === This module contains the RFC 6536 IETF NETCONF Access Control Model implementation. It provides all user-configurable access control settings and also provides API functions to check if a specific access request should be allowed or not. The file '''agt/agt_acm.c''' contains the SIL callback functions for this module. === nc-notifications.yang === This module is defined in RFC 5277, the NETCONF Notifications specification. It contains the and notification event definitions. The file '''agt/agt_not.c''' contains the SIL support code for this module. === notifications.yang === This module is defined in RFC 5277, the NETCONF Notifications specification. All of this RFC is supported in the server. This module contains the RPC operation. The notification replay feature is controlled with the '''--eventlog-size''' configuration parameter. The operation is fully supported, including XPath and subtree filters. The '''ietf-netconf-acm''' module can be used to control what notification events a user is allowed to receive. The filter allows the client to select which notification events it wants to receive. The file''' agt/agt_not.c '''contains the SIL callback functions for this modules. === yuma-app-common.yang === This module contains some common groupings of CLI parameters supported by some or all Yuma programs. Each program with CLI parameters defines its own module of CLI parameters (using the ncx:cli extension). The program name is used for the YANG module name as well (e.g., yangdump.yang or netconfd.yang). The SIL callback functions for the common groupings in this module are found in '''ncx/val_util.c''', such as the '''val_set_feature_parms''' function. === yuma-mysession.yang === This module provides the Yuma proprietary and RPC operations. These are used by the client to set some session output preferences, such as the desired line length, indentation amount, and defaults handling behavior. The file '''agt/agt_ses.c''' contains the SIL callback functions for this module. === yuma-ncx.yang === This module provides the YANG language extension statements that are used by Yuma programs to automate certain parts of the NETCONF protocol, document generation, code generation, etc. There are no SIL callback functions for this module. There are support functions within the '''src/ncx''' directory that include the '''obj_set_ncx_flags''' function in '''ncx/obj.c''' === yuma-netconf.yang === The NETCONF protocol operations, message structures, and error information are all data-driven, based on the YANG statements in the''' yuma-netconf.yang''' module. The ietf-netconf.yang module is not used at this time because it does not contain the complete set of YANG statements needed. The yuma-netconf.yang version is a super-set of the IETF version. Only one YANG module can be associated with an XML namespace in Yuma. In a future version, the extra data structures will be moved to an annotation module. The file '''agt/agt_ncx.c''' contains the SIL callback functions for this module. This module is not advertised in the server capabilities. It is only used internally within the server. === yuma-proc.yang === This module provides some Unix '''/proc''' file-system data, in nested XML format. This module will not load if the files '''/proc/meminfo''' and '''/proc/cpuinfo''' are not found. The file '''agt/agt_proc.c''' contains the SIL callback functions for this module. === yuma-system.yang === This module augments the ietf-system.yang container '''/system-state''' adding '''/sys:system-state/yuma-sys:yuma''' container, providing basic server information, unix 'uname' data, and all the Yuma proprietary notification event definitions. The file '''agt/agt_sys.c''' contains the SIL callback functions for this module. === yuma-time-filter.yang === This module contains the Yuma '''last-modified''' leaf, which extends the standard '''/netconf-state/datastores/datastore '''structure in '''ietf-netconf-monitoring.yang,''' with the database last-modified timestamp. The standard and operations are augmented with the '''if-modified-since''' leaf, to allow all-or-none filtering of the configuration, based on its modification timestamp. The file '''agt/agt_sys.c''' contains the SIL callback functions for this module. === yuma-types.yang === This module provides some common data types that are used by other Yuma YANG modules. There are no SIL callback functions for this module. = YANG Objects and Data Nodes = This section describes the basic design of the YANG object tree and the corresponding data tree that represents instances of various object nodes that the client or the server can create. == Object Definition Tree == The object tree is a tree representation of all the YANG module rpc, data definition, and notification statements. It starts with a 'root' container. This is defined with a YANG container statement which has an''' ncx:root '''extension statement within it. The parameter within the operation is an example of an object node which is treated as a root container. Each configuration database maintained by the server (e.g., and ) has a root container value node as its top-level object. A root container does not have any child nodes defined in it within the YANG file. However, the Yuma tools will treat this special container as if any top-level YANG data node is allowed to be a child node of the 'root' container type. === Object Node Types === There are 14 different YANG object node types, and a discriminated union of sub-data structures contains fields common to each sub-type. Object templates are defined in ncx/obj.h.
'''YANG Object Types'''
{| class="wikitable" border="1" | style="border-top:0.05pt solid #000000;border-bottom:0.05pt solid #000000;border-left:0.05pt solid #000000;border-right:none;padding:0.0382in;"|
'''object type'''
| style="border:0.05pt solid #000000;padding:0.0382in;"|
'''description'''
|- | OBJ_TYP_ANYXML | This object represents a YANG '''anyxml''' data node. |- | OBJ_TYP_CONTAINER | This object represents a YANG presence or non-presence '''container.''' |- | OBJ_TYP_CONTAINER + ncx:root | If the ncx:root extension is present within a container definition, then the object represents a NETCONF database '''root'''. No child nodes |- | OBJ_TYP_LEAF | This object represents a YANG '''leaf''' data node. |- | OBJ_TYP_LEAF_LIST | This object represents a YANG''' leaf-list''' data node. |- | OBJ_TYP_LIST | This object represents a YANG '''list''' data node. |- | OBJ_TYP_CHOICE | This object represents a YANG '''choice''' schema node. The only children allowed are case objects. This object does not have instances in the data tree. |- | OBJ_TYP_CASE | This object represents a YANG '''case''' schema node. This object does not have instances in the data tree. |- | OBJ_TYP_USES | This object represents a YANG '''uses''' schema node. The contents of the grouping it represents will be expanded into object tree. It is saved in the object tree even during operation, in order for the expanded objects to share common data. This object does not have instances in the data tree. |- | OBJ_TYP_REFINE | This object represents a YANG '''refine''' statement. It is used to alter the grouping contents during the expansion of a uses statement. This object is only allowed to be a child of a uses statement. It does not have instances in the data tree. |- | OBJ_TYP_AUGMENT | This object represents a YANG '''augment''' statement. It is used to add additional objects to an existing data structure. This object is only allowed to be a child of a uses statement or a child of a 'root' container. It does not have instances in the data tree, however any children of the augment node will generate object nodes that have instances in the data tree. |- | OBJ_TYP_RPC | This object represents a YANG '''rpc''' statement. It is used to define new operations. This object will only appear as a child of a 'root' container. It does not have instances in the data tree. Only 'rpcio' nodes are allowed to be children of an RPC node. |- | OBJ_TYP_RPCIO | This object represents a YANG '''input '''or''' output''' statement. It is used to define new operations. This object will only appear as a child of an RPC node. It does not have instances in the data tree. |- | OBJ_TYP_NOTIF | This object represents a YANG '''notification''' statement. It is used to define new event types. This object will only appear as a child of a 'root' container. It does not have instances in the data tree. |} === Object Node Template (obj_template_t) === The following typedef is used to represent an object tree node: /* One YANG data-def-stmt */ typedef struct obj_template_t_ { dlq_hdr_t qhdr; obj_type_t objtype; uint32 flags; /* see OBJ_FL_* definitions */ ncx_error_t tkerr; grp_template_t *grp; /* non-NULL == in a grp.datadefQ */ /* 3 back pointers */ struct obj_template_t_ *parent; struct obj_template_t_ *usesobj; struct obj_template_t_ *augobj; struct xpath_pcb_t_ *when; /* optional when clause */ dlq_hdr_t metadataQ; /* Q of obj_metadata_t */ dlq_hdr_t appinfoQ; /* Q of ncx_appinfo_t */ dlq_hdr_t iffeatureQ; /* Q of ncx_iffeature_t */ dlq_hdr_t inherited_iffeatureQ; /* Q of obj_iffeature_ptr_t */ dlq_hdr_t inherited_whenQ; /* Q of obj_xpath_ptr_t */ /* cbset is agt_rpc_cbset_t for RPC or agt_cb_fnset_t for OBJ */ void *cbset; /* object module and namespace ID * assigned at runtime * this can be changed over and over as a * uses statement is expanded. The final * expansion into a real object will leave * the correct value in place */ struct ncx_module_t_ *mod; xmlns_id_t nsid; union def_ { obj_container_t *container; obj_leaf_t *leaf; obj_leaflist_t *leaflist; obj_list_t *list; obj_choice_t *choic; obj_case_t *cas; obj_uses_t *uses; obj_refine_t *refine; obj_augment_t *augment; obj_rpc_t *rpc; obj_rpcio_t *rpcio; obj_notif_t *notif; } def; } obj_template_t; The following table highlights the fields within the obj_template_t data structure:
'''obj_template_t Fields'''
{| class="wikitable" border="1" !
Field
!
Description
|- | qhdr | Queue header to allow the object template to be stored in a child queue |- | objtype | enumeration to identify which variant of the 'def' union is present |- | flags | Internal state and properties |- | tkerr | Error message information |- | grp | back-pointer to parent group if this is a top-level data node within a grouping |- | parent | Parent node if any |- | usesobj | Back pointer to uses object if this is a top-level data node within an expanded grouping |- | augobj | Back pointer to augment object if this is a top-level data node within an expanded augment |- | when | XPath structure for YANG when statement |- | metadataQ | Queue of obj_template_t for any XML attributes (ncx:metadata) defined for this object node |- | appinfoQ | Queue of ncx_appinfo_t for any YANG extensions found defined within the object, that were not collected within a deeper appinfoQ (e.g., within a type statement) |- | iffeatureQ | Queue of ncx_iffeature_t for any if-feature statements found within this object node |- | cbset | Set of server callback functions for this object node. |- | nsid | Object node namespace ID assigned by xmlns.c |- | def | Union of object type specific nodes containing the rest of the YANG statements. Note that the server discards all descriptive statements such as description, reference, contact,. |} === obj_template_t Access Functions === The file '''ncx/obj.h''' contains many API functions so that object properties do not have to be accessed directly. The following table highlights the most commonly used functions. Refer to the H file for a complete definition of each API function.
'''obj_template_t Access Functions'''
{| class="wikitable" border="1" !
Function
!
Description
|- | obj_find_template | Find a top-level object template within a module |- | obj_find_child | Find the specified child node within a complex object template . Skips over any nodes without names (augment, uses, etc.) |- | obj_first_child | Get the first child node within a complex object template . Skips over any nodes without names. |- | obj_next_child | Get the next child node after the current specified child. Skips over any nodes without names. |- | obj_first_child_deep | Get the first child node within a complex object template . Skips over any nodes without names, and also any choice and case nodes. |- | obj_next_child_deep | Get the next child node after the current specified child. Skips over any nodes without names, and also any choice and case nodes. |- | obj_find_case | Find the specified case object child node within the specific complex object node. |- | obj_find_type | Check if a typ_template_t in the obj typedefQ hierarchy. |- | obj_find_grouping | Check if a grp_template_t in the obj typedefQ hierarchy. |- | obj_find_key | Find a specific key component by key leaf identifier name |- | obj_first_key | Get the first obj_key_t struct for the specified list object type |- | obj_next_key | Get the next obj_key_t struct for the specified list object type |- | obj_gen_object_id | Allocate and generate the YANG object ID for an object node |- | obj_get_name | Get the object name string |- | obj_has_name | Return TRUE if the object has a name field |- | obj_has_text_content | Return TRUE if the object has text content |- | obj_get_status | Get the YANG status for the object |- | obj_get_description | Get the YANG description statement for an object. Note that the server will always return a NULL pointer. |- | obj_get_reference | Get the YANG reference statement for an object. Note that the server will always return a NULL pointer. |- | obj_get_config_flag | Get the YANG config statement value for an object |- | obj_get_typestr | Get the name string for the type of an object |- | obj_get_default | Get the YANG default value for an object |- | obj_get_default_case | Get the name of the default case for a choice object |- | obj_get_typdef | Get the internal type definition for the leaf or leaf-list object |- | obj_get_basetype | Get the internal base type enumeration for an object |- | obj_get_mod_prefix | Get the module prefix for an object |- | obj_get_mod_name | Get the module name containing an object |- | obj_get_mod_version | Get the module revision date for the module containing an object. |- | obj_get_nsid | Get the internal XML namespace ID for an object |- | obj_get_min_elements | Get the YANG min-elements value for a list or leaf-list object |- | obj_get_max_elements | Get the YANG max-elements value for a list or leaf-list object |- | obj_get_units | Get the YANG units field for a leaf or leaf-list object |- | obj_get_parent | Get the parent object node for an object |- | obj_get_presence_string | Get the YANG presence statement for a container object |- | obj_get_child_count | Get the number of child nodes for a complex object. |- | obj_get_fraction_digits | Get the YANG fraction-digits statement for a decimal64 leaf or leaf-list object |- | obj_is_leafy | Return TRUE if the object is a leaf or leaf-list type |- | obj_is_mandatory | Return TRUE if the object is YANG mandatory |- | obj_is_mandatory_when | Return TRUE if the object is YANG mandatory, but first check if any when statements are FALSE first |- | obj_is_cloned | Return TRUE if the object is expanded from a grouping or augment statement |- | obj_is_data_db | Return TRUE if the object is defined within a YANG database definition |- | obj_in_rpc | Return TRUE if the object is defined within an RPC statement |- | obj_in_notif | Return TRUE if the object is defined within a notification statement |- | obj_is_hidden | Return TRUE if object contains the ncx:hidden extension |- | obj_is_root | Return TRUE if object contains the ncx:root extension |- | obj_is_password | Return TRUE if object contains the ncx:password extension |- | obj_is_cli | Return TRUE if object contains the ncx:cli extension |- | obj_is_abstract | Return TRUE if object contains the ncx:abstract extension |- | obj_is_xpath_string | Return TRUE if the object is a leaf or leaf-list containing an XPath string |- | obj_is_schema_instance_string | Return TRUE if the object is a leaf or leaf-list containing a schema instance identifier string |- | obj_is_secure | Return TRUE if object contains the nacm:secure extension |- | obj_is_very_secure | Return TRUE if object contains the nacm:very-secure extension |- | obj_is_system_ordered | Return TRUE if the list or leaf-list object is system ordered; FALSE if it is user ordered |- | obj_is_np_container | Return TRUE if the object is a YANG non presence container |- | obj_is_enabled | Return TRUE if the object is enabled; FALSE if any if-feature, when-stmt, or deviation-stmt has removed the object from the system. |- | obj_sort_children | Rearrange any child nodes in YANG schema order |} == Data Tree == A Yuma data tree is a representation of some subset of all possible object instances that a server is maintaining within a configuration database or other structure. Each data tree starts with a 'root' container, and any child nodes represent top-level YANG module data nodes that exist within the server. Each configuration database maintains its own copy (and version) of the data tree. There is only one object tree, however, and all data trees use the same object tree for reference. Not all object types have a corresponding node within a data tree. Only 'real' data nodes are present. Object nodes that are used as meta-data to organize the object tree (e.g., choice, augment) are not present. The following table lists the object types and whether each one is found in a data tree.
'''Object Types in the Data Tree'''
{| class="wikitable" border="1" !
Object Type
!
Found In Data Tree?
|- | OBJ_TYP_ANYXML | Yes |- | OBJ_TYP_CONTAINER | Yes |- | OBJ_TYP_CONTAINER (ncx:root) | Yes |- | OBJ_TYP_LEAF | Yes |- | OBJ_TYP_LEAF_LIST | Yes |- | OBJ_TYP_LIST | Yes |- | OBJ_TYP_CHOICE | No |- | OBJ_TYP_CASE | No |- | OBJ_TYP_USES | No |- | OBJ_TYP_REFINE | No |- | OBJ_TYP_AUGMENT | No |- | OBJ_TYP_RPC | No |- | OBJ_TYP_RPCIO | No |- | OBJ_TYP_NOTIF | No |} === Data Node Types === The '''ncx_btype_t''' enumeration in '''ncx/ncxtypes.h''' is used within each '''val_value_t''' to quickly identify which variant of the data node structure is being used. The following table describes the different enumeration values:
'''Yuma Data Types (ncx_btype_t)'''
{| class="wikitable" border="1" !
Data Type
!
Description
|- | NCX_BT_NONE | No type has been set yet. The val_new_value() function has been called but no specific init function has been called to set the base type. |- | NCX_BT_ANY | The node is a YANG 'anyxml' node. When the client or server parses an 'anyxml' object, it will be converted to containers and strings. This type should not be used directly. |- | NCX_BT_BITS | YANG 'bits' data type |- | NCX_BT_ENUM | YANG 'enumeration' data type |- | NCX_BT_EMPTY | YANG 'empty' data type |- | NCX_BT_BOOLEAN | YANG 'boolean' data type |- | NCX_BT_INT8 | YANG 'int8' data type |- | NCX_BT_INT16 | YANG 'int16' data type |- | NCX_BT_INT32 | YANG 'int32' data type |- | NCX_BT_INT64 | YANG 'int64' data type |- | NCX_BT_UINT8 | YANG 'uint8' data type |- | NCX_BT_UINT16 | YANG 'uint16' data type |- | NCX_BT_UINT32 | YANG 'uint32' data type |- | NCX_BT_UINT64 | YANG 'uint64' data type |- | NCX_BT_DECIMAL64 | YANG 'decimal64' data type |- | NCX_BT_FLOAT64 | Hidden double type, used just for XPath. If the HAS_FLOAT #define is false, then this type will be implemented as a string, not a double. |- | NCX_BT_STRING | YANG 'string' type. There are also some Yuma extensions that are used with this data type for special strings. The server needs to know if a string contains XML prefixes or not, and there are several flavors to automatate processing of each one correctly. |- | NCX_BT_BINARY | YANG 'binary' data type |- | NCX_BT_INSTANCE_ID | YANG 'instance-identifier' data type |- | NCX_BT_UNION | YANG 'union' data type. This is a meta-type. When the client or server parses a value, it will resolve the union to one of the data types defined within the union. |- | NCX_BT_LEAFREF | YANG 'leafref' data type. This is a meta-type. The client or server will resolve this data type to the type of the actual 'pointed-at' leaf that is being referenced. |- | NCX_BT_IDREF | YANG 'identityref' data type |- | NCX_BT_SLIST | XSD list data type (ncx:xsdlist extension) |- | NCX_BT_CONTAINER | YANG container |- | NCX_BT_CHOICE | YANG choice. This is a meta-type and placeholder. It does not appear in the data tree. |- | NCX_BT_CASE | YANG case. This is a meta-type and placeholder. It does not appear in the data tree. |- | NCX_BT_LIST | YANG list |- | NCX_BT_EXTERN | Internal 'external' data type, used in yangcli. It indicates that the content is actually in an external file. |- | NCX_BT_INTERN | Internal 'buffer' data type, used in yangcli. The content is actually stored verbatim in an internal buffer. |} === Yuma Data Node Edit Variables (val_editvars_t) === There is a temporary data structure which is attached to a data node while editing operations are in progress, called '''val_editvars_t'''. This structure is used by the functions in agt/agt_val.c to manipulate the value tree nodes during an , , , or operation. The SIL callback functions may wish to refer to the fields in this data structure. There is also a SIL cookie field to allow data to be transferred from one callback stage to the later stages. For example, if an edit operation caused the device instrumentation to reserve some memory, then this cookie could store that pointer. The following typedef is used to define the val_editvars_t structure: /* one set of edit-in-progress variables for one value node */ typedef struct val_editvars_t_ { /* these fields are only used in modified values before they are * actually added to the config database (TBD: move into struct) * curparent == parent of curnode for merge */ struct val_value_t_ *curparent; //op_editop_t editop; /* effective edit operation */ op_insertop_t insertop; /* YANG insert operation */ xmlChar *insertstr; /* saved value or key attr */ struct xpath_pcb_t_ *insertxpcb; /* key attr for insert */ struct val_value_t_ *insertval; /* back-ptr */ boolean iskey; /* T: key, F: value */ boolean operset; /* nc:operation here */ void *pcookie; /* user pointer cookie */ int icookie; /* user integer cookie */ } val_editvars_t; The following fields within the val_editvars_t are highlighted:
'''val_editvars_t Fields'''
{| class="wikitable" border="1" !
Field
!
Description
|- | curparent | A 'new' node will use this field to remember the parent of the 'current' value. This is needed to support the YANG insert operation. |- | editop | The effective edit operation for this node. |- | insertop | The YANG insert operation, if any. |- | insertstr | The YANG 'value' or 'key' attribute value string, used to support the YANG insert operation. |- | insertxpcb | XPath parser control block for the insert 'key' expression, if needed. Used to support the YANG insert operation. |- | insertval | Back pointer to the value node to insert ahead of, or behind, if needed. Used to support the 'before' and 'after' modes of the YANG insert operation. |- | iskey | TRUE if this is a key leaf. FALSE otherwise. |- | operset | TRUE if there was an nc:operation attribute found in this node; FALSE if the 'editop' is derived from its parent. |- | pcookie | SIL user pointer cookie. Not used by the server. Reserved for SIL callback code. |- | icookie | SIL user integer cookie. Not used by the server. Reserved for SIL callback code. |} === Yuma Data Nodes (val_value_t) === The '''val_value_t''' data structure is used to maintain the internal representation of all NETCONF databases, non-configuration data available with the operation, all RPC operation input and output parameters, and all notification contents. The following typedef is used to define a value node: /* one value to match one type */ typedef struct val_value_t_ { dlq_hdr_t qhdr; /* common fields */ struct obj_template_t_ *obj; /* bptr to object def */ typ_def_t *typdef; /* bptr to typdef if leaf */ const xmlChar *name; /* back pointer to elname */ xmlChar *dname; /* AND malloced name if needed */ struct val_value_t_ *parent; /* back-ptr to parent if any */ xmlns_id_t nsid; /* namespace ID for this node */ ncx_btype_t btyp; /* base type of this value */ uint32 flags; /* internal status flags */ ncx_data_class_t dataclass; /* config or state data */ /* YANG does not support user-defined meta-data but NCX does. * The , and operations * use attributes in the RPC parameters, the metaQ is still used * * The ncx:metadata extension allows optional attributes * to be added to object nodes for anyxml, leaf, leaf-list, * list, and container nodes. The config property will * be inherited from the object that contains the metadata * * This is used mostly for RPC input parameters * and is strongly discouraged. Full edit-config * support is not provided for metdata */ dlq_hdr_t metaQ; /* Q of val_value_t */ /* value editing variables */ val_editvars_t *editvars; /* edit-vars from attrs */ op_editop_t editop; /* needed for all edits */ status_t res; /* validation result */ /* Used by Agent only: * if this field is non-NULL, then the entire value node * is actually a placeholder for a dynamic read-only object * and all read access is done via this callback function; * the real data type is getcb_fn_t * */ void *getcb; /* if this field is non-NULL, then a malloced value struct * representing the real value retrieved by * val_get_virtual_value, is cached here for / */ struct val_value_t_ *virtualval; time_t cachetime; /* these fields are used for NCX_BT_LIST */ struct val_index_t_ *index; /* back-ptr/flag in use as index */ dlq_hdr_t indexQ; /* Q of val_index_t or ncx_filptr_t */ /* this field is used for NCX_BT_CHOICE * If set, the object path for this node is really: * $this --> casobj --> casobj.parent --> $this.parent * the OBJ_TYP_CASE and OBJ_TYP_CHOICE nodes are skipped * inside an XML instance document */ struct obj_template_t_ *casobj; /* these fields are for NCX_BT_LEAFREF * NCX_BT_INSTANCE_ID, or tagged ncx:xpath * value stored in v union as a string */ struct xpath_pcb_t_ *xpathpcb; /* back-ptr to the partial locks that are held * against this node */ plock_cb_t *plock[VAL_MAX_PLOCKS]; /* union of all the NCX-specific sub-types * note that the following invisible constructs should * never show up in this struct: * NCX_BT_CHOICE * NCX_BT_CASE * NCX_BT_UNION */ union v_ { /* complex types have a Q of val_value_t representing * the child nodes with values * NCX_BT_CONTAINER * NCX_BT_LIST */ dlq_hdr_t childQ; /* Numeric data types: * NCX_BT_INT8, NCX_BT_INT16, * NCX_BT_INT32, NCX_BT_INT64 * NCX_BT_UINT8, NCX_BT_UINT16 * NCX_BT_UINT32, NCX_BT_UINT64 * NCX_BT_DECIMAL64, NCX_BT_FLOAT64 */ ncx_num_t num; /* String data types: * NCX_BT_STRING * NCX_BT_INSTANCE_ID */ ncx_str_t str; val_idref_t idref; ncx_binary_t binary; /* NCX_BT_BINARY */ ncx_list_t list; /* NCX_BT_BITS, NCX_BT_SLIST */ boolean boo; /* NCX_BT_EMPTY, NCX_BT_BOOLEAN */ ncx_enum_t enu; /* NCX_BT_UNION, NCX_BT_ENUM */ xmlChar *fname; /* NCX_BT_EXTERN */ xmlChar *intbuff; /* NCX_BT_INTERN */ } v; } val_value_t; The following table highlights the fields in this data structure:
'''val_value_t Fields'''
{| class="wikitable" border="1" !
Field
!
Description
|- | qhdr | Internal queue header to allow a value node to be stored in a queue. A complex node maintains a child queue of val_value_t nodes. |- | obj | Back pointer to the object template for this data node |- | typdef | Back pointer to the typedef structure if this is a leaf or leaf-list node. |- | name | Back pointer to the name string for this node |- | dname | Malloced name string if the client or server changed the name of this node, so the object node name is not being used. This is used for anyxml processing (and other things) to allow generic objects (container, string, empty, etc.) to be used to represent the contents of an 'anyxml' node. |- | parent | Back pointer to the parent of this node, if any |- | nsid | Namespace ID for this node. This may not be the same as the object node namespace ID, e.g., anyxml child node contents will override the generic object namespace. |- | btyp | The ncx_btype_t base type enumeration for this node. This is the final resolved value, in the event the object type is not a final resolved base type. |- | flags | Internal flags field. Do not access directly. |- | dataclass | Internal config or non-config enumeration |- | metaQ | Queue of val_value_t structures that represent any meta-variables (XML attributes) found for this data node. For example, the NETCONF filter 'type' and 'select' attributes are defined for the element in yuma-netconf.yang. |- | editvars | Pointer to the malloced edit variables structure for this data node. This node will be freed (and NULL value) when the edit variables are not in use. |- | res | Internal validation result status for this node during editing or parsing. |- | getcb | Internal server callback function pointer. Used only if this is a 'virtual' node, and the actual value node contents are generated by a SIL callback function instead of being stored in the node itself. |- | virtualval | The temporary cached virtual node value, if the getcb pointer is non-NULL. |- | indexQ | Queue of internal data structures used during parsing and filtering streamed output. |- | casobj | Back pointer to the OBJ_TYP_CASE object node for this data node, if this node is a top-level child of a YANG case statement. |- | xpathpcb | XPath parser control block, used if this value contains some sort of XPath string or instance-identifier. For example, the XML namespace ID mappings are stored, so the XML prefix map generated for the will contain and reuse the proper namespace attributes, as needed. |- | v | Union of different internal fields, depending on the 'btyp' field value. |- | v.childQ | Queue of val_value_t child nodes, if this is a complex node. |- | v.num | ncx_num_t for all numeric data types |- | v.str | Malloced string value for the string data type |- | v.idref | Internal data structure for the YANG identityref data type |- | v.binary | Internal data structure for the YANG binary data type |- | v.list | Internal data structure for YANG bits and NCX xsdlist data types |- | v.boo | YANG boolean data type |- | v.enu | Internal data structure for YANG enumeration data type |- | v.fname | File name for NCX 'external' data type |- | v.intbuff | Malloced buffer for 'internal' data type |} === val_value_t Access Macros === There are a set of macros defined to access the fields within a val_value_t structure. These should be used instead of accessing the fields directly. There are functions defined as well. These macros are provided in addition the the access functions for quick access to the actual node value. These macros must only be used when the base type ('btyp') field has been properly set and known by the SIL code. Some auto-generated SIL code uses these macros. The following table summarized the val_value_t macros that are defined in '''ncx/val.h''': {| class="wikitable" border="1" !
Macro
!
Description
|- | VAL_BOOL(V) | Access value for NCX_BT_BOOLEAN |- | VAL_EMPTY(V) | Access value for NCX_BT_EMPTY |- | VAL_DOUBLE(V) | Access value for NCX_BT_FLOAT64 |- | VAL_STRING(V) | Access value for NCX_BT_STRING |- | VAL_BINARY(V) | Access value for NCX_BT_BINARY |- | VAL_ENU(V) | Access entire '''ncx_enum_t''' structure for NCX_BT_ENUM |- | VAL_ENUM(V) | Access enumeration integer value for NCX_BT_ENUM |- | VAL_ENUM_NAME(V) | Access enumeration name string for NCX_BT_ENUM |- | VAL_FLAG(V) | Deprecated: use VAL_BOOL instead |- | VAL_LONG(V) | Access NCX_BT_INT64 value |- | VAL_INT(V) | Access NCX_BT_INT32 value |- | VAL_INT8(V) | Access NCX_BT_INT8 value |- | VAL_INT16(V) | Access NCX_BT_INT16 value |- | VAL_STR(V) | Deprecated: use VAL_STRING instead |- | VAL_INSTANCE_ID(V) | Access NCX_BT_INSTANCE_ID value |- | VAL_IDREF(V) | Access entire '''val_idref_t''' structure for NCX_BT_IDREF |- | VAL_IDREF_NSID(V) | Access the identityref namespace ID for NCX_BT_IDREF |- | VAL_IDREF_NAME(V) | Access the identityref name string for NCX_BT_IDREF |- | VAL_UINT(V) | Access the NCX_BT_UINT32 value |- | VAL_UINT8(V) | Access the NCX_BT_UINT8 value |- | VAL_UINT16(V) | Access the NCX_BT_UINT16 value |- | VAL_ULONG(V) | Access the NCX_BT_UINT64 value |- | VAL_DEC64(V) | Access the '''ncx_dec64'''structure for NCX_BT_DEC64 |- | VAL_LIST(V) | Access the '''ncx_list_t '''structure for NCX_BT_LIST |- | VAL_BITS | Access the '''ncx_list_t '''structure for NCX_BT_BITS. (Same as VAL_LIST) |} === val_value_t Access Functions === The file '''ncx/val.h''' contains many API functions so that object properties do not have to be accessed directly. In addition, the file ncx/val_util.h contains more (high-level) utility functions. The following table highlights the most commonly used functions. Refer to the H files for a complete definition of each API function. {| class="wikitable" border="1" !
Function
!
Description
|- | val_new_value | Malloc a new value node with type NCX_BT_NONE. |- | val_init_complex | Initialize a malloced value node as one of the complex data types. |- | val_init_virtual | Initialize a malloced value node as a virtual node (provide a 'get' callback function). |- | val_init_from_template | Initialize a malloced value node using an object template. This is the most common form of the init function used by SIL callback functions. |- | val_free_value | Clean and free a malloced value node. |- | val_set_name | Set or replace the value node name. |- | val_set_qname | Set or replace the value node namespace ID and name. |- | val_string_ok | Check if the string value is valid for the value node object type. |- | val_string_ok_errinfo | Check if the string value is valid for the value node object type, and provide the error information to use if it is not OK. |- | val_list_ok | Check if the list value is valid for the value node object type. |- | val_list_ok_errinfo | Check if the list value is valid for the value node object type, and provide the error information to use if it is not OK. |- | val_enum_ok | Check if the enumeration value is valid for the value node object type. |- | val_enum_ok_errinfo | Check if the enumeration value is valid for the value node object type, and provide the error information to use if it is not OK. |- | val_bit_ok | Check if the bits value is valid for the value node object type. |- | val_idref_ok | Check if the identityref value is valid for the value node object type. |- | val_parse_idref | Convert a string to an internal QName string into its various parts and find the identity struct that is being referenced (if available). |- | val_simval_ok | Check if the smple value is valid for the value node object type. |- | val_simval_ok_errinfo | Check if the simple value is valid for the value node object type, and provide the error information to use if it is not OK. |- | val_get_first_meta | Get the first meta-variable (XML attribute value) for a value node. |- | val_get_next_meta | Get the next meta-variable (XML attribute value) for a value node. |- | val_find_meta | Find the specified meta-variable in a value node. |- | val_dump_value | Debug function to print the contents of any value node. |- | val_dump_value_ex | Debug function to print the contents of any value node, with extended parameters to control the output. |- | val_dump_value_max | Debug function to print the contents of any value node, with full control of the output parameters. |- | val_set_string | Set a malloced value node as a generic string value. Used instead of val_init_from_template. |- | val_set_string2 | Set a malloced value node as a specified string type. Used instead of val_init_from_template. |- | val_set_simval | Set a malloced value node as a specified simple type. Used instead of val_init_from_template. |- | val_set_simval_str | Set a malloced value node as a specified simple type. Used instead of val_init_from_template. Use a counted string value instead of a zero-terminated string value. |- | val_make_serialized_string | Serialize value into malloced buffer. Supported format modes: NCX_DISPLAY_MODE_NONE, NCX_DISPLAY_MODE_PLAIN, NCX_DISPLAY_MODE_PREFIX, NCX_DISPLAY_MODE_MODULE, NCX_DISPLAY_MODE_XML, NCX_DISPLAY_MODE_XML_NONS, NCX_DISPLAY_MODE_JSON |- | val_make_string | Create a complete malloced generic string value node. |- | val_clone | Clone a value node |- | val_clone_test | Clone a value node with a 'test' callback function to prune certain descendant nodes during the clone procedure. |- | val_clone_config_data | Clone a value node but skip all the non-configuration descendant nodes. |- | val_add_child | Add a child value node to a parent value node. |- | val_insert_child | Insert a child value node into a specific spot into a parent value node. |- | val_remove_child | Remove a child value node from its parent. |- | val_swap_child | Replace a child node within its parent with a different value node. |- | val_first_child_match | Match a child node name; Used for partial command completion in yangcli. |- | val_next_child_match | Match the next child node name; Used for partial command completion in yangcli. |- | val_get_first_child | Get the first child value node. |- | val_get_next_child | Get the next child value node. |- | val_find_child | Find a specific child value node. |- | val_find_next_child | Find the next occurrence of a specified child node. |- | val_match_child | Match a potential partial node name against the child node names, and return the first match found, if any. |- | val_child_cnt | Get the number of child nodes within a parent node. |- | val_liststr_count | Get the number of strings within an NCX_BT_LIST value node. |- | val_index_match | Check if 2 value list nodes have the same set of key leaf values. |- | val_compare | Compare 2 value nodes |- | val_compare_ex | Compare 2 value nodes with extra parameters. |- | val_compare_to_string | Compare a value node to a string value instead of another value node. |- | val_sprintf_simval_nc | Output the value node as a string into the specified buffer. |- | val_make_sprintf_string | Malloc a buffer and fill it with a zero-terminated string representation of the value node. |- | val_resolve_scoped_name | Find a descendant node within a value node, from a relative path expression. |- | val_has_content | Return TRUE if the value node has any content; FALSE if an empty XML element could represent its value. |- | val_has_index | Return TRUE if the value node is a list with a key statement. |- | val_get_first_index | Get the first index node for the specified list value node. |- | val_get_next_index | Get the next index node for the specified list value node. |- | val_set_extern | Set a malloced value node as an NCX_BT_EXTERN internal data type. |- | val_set_intern | Set a malloced value node as an NCX_BT_INTERN internal data type. |- | val_fit_oneline | Return TRUE if the value node should fit on 1 display line; Sometimes a guess is made instead of determining the exact value. XML namespace declarations generated during XML output can cause this function value to sometimes be wrong. |- | val_create_allowed | Return TRUE if the NETCONF create operation is allowed for the specified value node. |- | val_delete_allowed | Return TRUE if the NETCONF delete operation is allowed for the specified value node. |- | val_is_config_data | Return TRUE if the value node represents configuration data. |- | val_get_virtual_value | Get the value for a virtual node from its 'get' callback function. |- | val_is_default | Return TRUE if the value node is set to its YANG default value. |- | val_is_real | Check if a value node is a real node or one of the abstract node types. |- | val_get_parent_nsid | Get the namespace ID for the parent value node of a specified child node. |- | val_instance_count | Get the number of occurrences of the specified child value node within a parent value node. |- | val_need_quotes | Return TRUE if the printed string representation of a value node needs quotes (because it contains some whitespace or special characters). |- | val_get_dirty_flag | Check if a value node has been altered by an RPC operation, but this edit has not been finalized yet. |- | val_get_nest_level | Get the current numeric nest level of the specified value node. |- | val_get_mod_name | Get the module name for the specified value node. |- | val_get_mod_prefix | Get the module prefix string for the specified value node. |- | val_get_nsid | Get the namespace ID for the specified value node. |- | val_change_nsid | Change the namespace ID for the specified value node and all of its descendents. |- | val_set_pcookie | Set the SIL pointer cookie in the value node editvars structure. |- | val_set_icookie | Set the SIL integer cookie in the value node editvars structure. |- | val_get_pcookie | Get the SIL pointer cookie in the value node editvars structure. |- | val_get_icookie | Get the SIL integer cookie in the value node editvars structure. |- | val_get_typdef | Get the typedef structure for a leaf or leaf-list value node. |- | val_move_children | Move all the child nodes of one complex value node to another complex value node. |- | val_set_canonical_order | Re-order the descendant nodes of a value node so they are in YANG order. Does not change the relative order of system-ordered lists and leaf-lists. |- | val_gen_index_chain | Generate the internal key leaf lookup chain for a list value node.. |- | val_add_defaults | Generate the leafs that have default values. |- | val_instance_check | Check a value node against its template to see if the correct number of descendant nodes are present. |- | val_get_choice_first_set | Get the first real node that is present for a conceptual choice statement. |- | val_get_choice_next_set | Get the next real node that is present for a conceptual choice statement. |- | val_choice_is_set | Return TRUE if some real data node is present for a conceptual choice statement. |- | val_new_child_val | Create a child node during an edit operation. Used by the server. SIL code does not need to maintain the value tree. |- | val_gen_instance_id | Malloc and generate the YANG instance-identifier string for the value node. |- | val_check_obj_when | Check if an object node has any 'when' statements, and if so, evaluate the XPath condition(s) against the value tree to determine if the object should be considered present or not. |- | val_check_child_conditional | Check if a child object node has any FALSE 'if-feature' or 'when' statements. |- | val_is_mandatory | Check if the child object node is currently mandatory or optional. |- | val_get_xpathpcb | Access the XPath parser control block for this value node, if any. |- | val_make_simval_obj | Malloc and fill in a value node from an object template and a value string. |- | val_set_simval_obj | Fill in a value node from an object template and a value string. |} === val_value_t Extended Access Functions === {| class="wikitable" border="1" !
Function
!
Description
|- | xpath_find_val_target | Follow the absolute-path instance-identifier (Xpath expression) and return the target value |} === SIL Utility Functions === There are some high-level SIL callback utilities in '''agt/agt_util.h'''. These functions access the lower-level functions in libyumancx to provide simpler functions for common SIL tasks. The following table highlights the functions available in this module:
'''agt/agt_util Functions'''
{| class="wikitable" border="1" !
Function
!
Description
|- | agt_commit_validate_register | Registers callback function called after the configuration is validated and before the first commit callback is called. |- | agt_commit_complete_register | Registers callback function called after the last commit callback is complete. |- | agt_get_cfg_from_parm | For value nodes that represent a NETCONF configuration database name (e.g., empty element named 'running'). The configuration control block for the referenced database is retrieved. |- | agt_get_inline_cfg_from_parm | For value nodes that represent inline NETCONF configuration data. The value node for the inline config node is retrieved. |- | agt_get_parmval | Get the specified parameter name within the RPC input section, from an RPC message control block. |- | agt_record_error | Generate a complete RPC error record to be used when the is sent. |- | agt_record_error_errinfo | Generate a complete RPC error record to be used when the is sent, using the YANG specified error information, not the default error information. |- | agt_record_attr_error | Generate a complete RPC error record to be used when the is sent for an XML attribute error. |- | agt_record_insert_error | Generate a complete RPC error record to be used when the is sent for a YANG insert operation error. |- | agt_record_unique_error | Generate a complete RPC error record to be used when the is sent for a YANG unique statement contraint error. |- | agt_check_default | val_nodetest_fn_t Node Test Callback function to filter out default data from streamed replies, according to the server's definition of a default node. |- | agt_check_save | val_nodetest_fn_t Node Test Callback function to filter out data nodes that should not be saved to NV-storage. |- | agt_enable_feature | Enable the specified YANG feature |- | agt_disable_feature | Disable the specified YANG feature |- | agt_make_leaf | Create a child value node. |- | agt_make_virtual_leaf | Create a virtual value child node. Most device monitoring leafs use this function because the value is retrieved with a device-specific API, not stored in the value tree. |- | agt_init_cache | Initialize a cached pointer to a node in a data tree. |- | agt_check_cache | Check if a cached pointer to a node in a data tree needs to be updated or set to NULL. |} = SIL External Interface = Each SIL has 2 initialization functions and 1 cleanup function that must be present. * The first initialization callback function is used to set up the configuration related objects. * The second initialization callback is used to setup up non-configuration objects, after the running configuration has been loaded from the startup file. * The cleanup callback is used to remove all SIL data structures and unregister all callback functions. These are the only SIL functions that the server will invoke directly. There is an option to generated a starting point/boilerplate code with '''yangdump''' with the '''--format'''=c parameter along with the rest of the definitions derived from the YANG model or one can simply write the 3 functions manually and not use '''yangdump''' (''helloworld'', ''toaster'', ''ietf-interfaces'' and ''ietf-system'' SIL module examples can be used as reference for the code). Most of the work done by SIL code is through callback functions for specific RPC operations and database objects. These callback functions are registered during the initialization functions. == Stage 1 Initialization == The stage 1 initialization function is the first function called in the library by the server. If the '''netconfd''' configuration parameters include a 'load' command for the module, then this function will be called during server initialization. It can also be called if the operation is invoked during server operation. This function MUST NOT attempt to access any database. There will not be any configuration databases if this function is called during server initialization. Use the 'init2' function to adjust the running configuration. This callback function is expected to perform the following functions: * initialize any module static data * make sure the requested module name and optional revision date parameters are correct * load the requested module name and revision with '''ncxmod_load_module''' * setup top-level object cache pointers (if needed) * register any RPC method callbacks with '''agt_rpc_register_method''' * register any database object callbacks with '''agt_cb_register_callback''' * perform any device-specific and/or module-specific initialization Name Format: y__init Input: * modname == string containing module name to load * revision == string containing revision date to use == NULL if the operator did not specify a revision. Returns: * operation status (0 if success) Example function generated by '''yangdump''': /******************************************************************** * FUNCTION y_toaster_init * * initialize the toaster server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ status_t y_toaster_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; y_toaster_init_static_vars(); /* change if custom handling done */ if (xml_strcmp(modname, y_toaster_M_toaster)) { return ERR_NCX_UNKNOWN_MODULE; } if (revision && xml_strcmp(revision, y_toaster_R_toaster)) { return ERR_NCX_WRONG_VERSION; } agt_profile = agt_get_profile(); res = ncxmod_load_module( y_toaster_M_toaster, y_toaster_R_toaster, &agt_profile->agt_savedevQ, &toaster_mod); if (res != NO_ERR) { return res; } toaster_obj = ncx_find_object( toaster_mod, y_toaster_N_toaster); if (toaster_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } make_toast_obj = ncx_find_object( toaster_mod, y_toaster_N_make_toast); if (toaster_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } cancel_toast_obj = ncx_find_object( toaster_mod, y_toaster_N_cancel_toast); if (toaster_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } toastDone_obj = ncx_find_object( toaster_mod, y_toaster_N_toastDone); if (toaster_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_make_toast, AGT_RPC_PH_VALIDATE, y_toaster_make_toast_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_make_toast, AGT_RPC_PH_INVOKE, y_toaster_make_toast_invoke); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_cancel_toast, AGT_RPC_PH_VALIDATE, y_toaster_cancel_toast_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_cancel_toast, AGT_RPC_PH_INVOKE, y_toaster_cancel_toast_invoke); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_toaster_M_toaster, (const xmlChar *)"/toaster", (const xmlChar *)"2009-11-20", y_toaster_toaster_edit); if (res != NO_ERR) { return res; } /* put your module initialization code here */ return res; } /* y_toaster_init */ == Stage 2 Initialization == The stage 2 initialization function is the second function called in the library by the server: * It will only be called if the stage 1 initialization is called first, and it returns 0 (NO_ERR status). * This function is used to initialize any needed data structures in the running configuration, such as factory default configuration, read-only counters and status objects. * It is called after the startup configuration has been loaded into the server. * If the operation is used during server operation, then this function will be called immediately after the state 1 initialization function. Note that configuration data structures that are loaded during server initialization '''(load_running_config''') will be handled by the database callback functions registered during phase 1 initialization. Any server-created configuration nodes should be created during phase 2 initialization (this function), after examining the explicitly-provided configuration data. For example, the top-level /nacm container will be created (by agt_acm.c) if it is not provided in the startup configuration. This callback function is expected to perform the following functions: * load non-configuration data structures into the server (if needed) * initialize top-level data node cache pointers (if needed) * load factory-default configuration data structures into the server (if needed) * optionally save a cached pointer to a data tree node (such as the root node for the module). Name Format: y__init2 Returns: * operation status (0 if success) Example function generated by '''yangdump''': /******************************************************************** * FUNCTION y_toaster_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ status_t y_toaster_init2 (void) { status_t res; res = NO_ERR; toaster_val = agt_init_cache( y_toaster_M_toaster, y_toaster_N_toaster, &res); if (res != NO_ERR) { return res; } /* put your init2 code here */ return res; } /* y_toaster_init2 */ == Cleanup == The cleanup function is called during server shutdown. It is only called if the stage 1 initialization function is called. It will be called right away if either the stage 1 or stage 2 initialization functions return a non-zero error status. This callback function is expected to perform the following functions: * cleanup any module static data * free any top-level object cache pointers (if needed) * unregister any RPC method callbacks with '''agt_rpc_unregister_method''' * unregister any database object callbacks with '''agt_cb_unregister_callbacks''' * perform any device-specific and/or module-specific cleanup Name Format: y__cleanup Example function generated by '''yangdump''': /******************************************************************** * FUNCTION y_toaster_cleanup * cleanup the server instrumentation library * ********************************************************************/ void y_toaster_cleanup (void) { agt_rpc_unregister_method( y_toaster_M_toaster, y_toaster_N_make_toast); agt_rpc_unregister_method( y_toaster_M_toaster, y_toaster_N_cancel_toast); agt_cb_unregister_callbacks( y_toaster_M_toaster, (const xmlChar *)"/toaster"); /* put your cleanup code here */ } /* y_toaster_cleanup */ = SIL Callback Interface = This section briefly describes the SIL code that a developer will need to create to handle the data-model specific details. SIL functions access internal server data structures, either directly or through utility functions. Database mechanics and XML processing are done by the server engine, not the SIL code. A more complete reference can be found in section 5. When a request is received, the NETCONF server engine will perform the following tasks before calling any SIL: * parse the RPC operation element, and find its associated YANG rpc template * if found, check if the session is allowed to invoke this RPC operation * if the RPC is allowed, parse the rest of the XML message, using the '''rpc_template_t '''for the RPC operation to determine if the basic structure is valid. * if the basic structure is valid, construct an '''rpc_msg_t''' data structure for the incoming message. * check all YANG machine-readable constraints, such as must, when, if-feature, min-elements, etc. * if the incoming message is completely 'YANG valid', then the server will check for an RPC validate function, and call it if found. This SIL code is only needed if there are additional system constraints to check. For example: ** need to check if a configuration name such as is supported ** need to check if a configuration database is locked by another session ** need to check description statement constraints not covered by machine-readable constraints ** need to check if a specific capability or feature is enabled * If the validate function returns a NO_ERR status value, then the server will call the SIL invoke callback, if it is present. This SIL code should always be present, otherwise the RPC operation will have no real affect on the system. * At this point, an is generated, based on the data in the rpc_msg_t. ** Errors are recorded in a queue when they are detected. ** The server will handle the error reply generation for all errors it detects. ** For SIL detected errors, the '''agt_record_error''' function in agt/agt_util.h is usually used to save the error details. ** Reply data can be generated by the SIL invoke callback function and stored in the rpc_msg_t structure. ** Reply data can be streamed by the SIL code via reply callback functions. For example, the and operations use callback functions to deal with filters, and stream the reply by walking the target data tree. * After the is sent, the server will check for an RPC post reply callback function. This is only needed if the SIL code allocated some per-message data structures. For example, the '''rpc_msg_t''' contains 2 SIL controlled pointers ('''rpc_user1''' and '''rpc_user2'''). The post reply callback is used by the SIL code to free these pointers, if needed. The database edit SIL callbacks are only used for database operations that alter the database. The validate and invoke callback functions for these operations will in turn invoke the data-model specific SIL callback functions, depending on the success or failure of the edit request. == RPC Operation Interface == All RPC operations are data-driven within the server, using the YANG rpc statement for the operation and SIL callback functions. Any new protocol operation can be added by defining a new YANG rpc statement in a module, and providing the proper SIL code. === RPC Callback Initialization === The''' agt_rpc_register_method''' function in agt/agt_rpc.h is used to provide a callback function for a specific callback phase. The same function can be used for multiple phases if desired. /* Template for RPC server callbacks * The same template is used for all RPC callback phases */ typedef status_t (*agt_rpc_method_t) (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode); extern status_t agt_rpc_register_method (const xmlChar *module, const xmlChar *method_name, agt_rpc_phase_t phase, agt_rpc_method_t method);
'''agt_rpc_register_method'''
{| class="wikitable" border="1" !
Parameter
!
Description
|- | module | The name of the module that contains the rpc statement |- | method_name | The identifier for the rpc statement |- | phase | AGT_PH_VALIDATE(0): validate phase, AGT_PH_INVOKE(1): invoke phase, AGT_PH_POST_REPLY(2): post-reply phase |- | method | The address of the callback function to register |} === RPC Message Header === The NETCONF server will parse the incoming XML message and construct an RPC message header, which is used to maintain state and any other message-specific data during the processing of an incoming request. The '''rpc_msg_t''' data structure in ncx/rpc.h is used for this purpose. The following table summarizes the fields:
'''rpc_msg_t'''
{| class="wikitable" border="1" !
Field
!
Type
!
User Mode
!
Description
|- | qhdr | dlq_hdr_t | none | Queue header to store RPC messages in a queue (within the session header) |- | mhdr | xml_msg_hdr_t | none | XML message prefix map and other data used to parse the request and generate the reply. |- | rpc_in_attrs | xml_attrs_t * | readwrite | Queue of xml_attr_t representing any XML attributes that were present in the element. A callback function may add xml_attr_t structs to this queue to send in the reply. |- | rpc_method | obj_template_t * | read | Back-pointer to the object template for this RPC operation. |- | rpc_agt_state | int | read | Enum value (0, 1, 2) for the current RPC callback phase. |- | rpc_err_option | op_errop_t | read | Enum value for the parameter. This is only set if the operation is in progress. |- | rpc_top_editop | op_editop_t | read | Enum value for the parameter. This is only set if the operation is in progress. |- | rpc_input | val_value_t * | read | Value tree representing the container of 'input' parameters for this RPC operation. |- | rpc_user1 | void * | readwrite | Void pointer that can be used by the SIL functions to store their own message-specific data. |- | rpc_user2 | void * | readwrite | Void pointer that can be used by the SIL functions to store their own message-specific data. |- | rpc_returncode | uint32 | none | Internal return code used to control nested callbacks. |- | rpc_data_type | rpc_data_t | write | For RPC operations that return data, this enumeration is set to indicate which type of data is desired. '''RPC_DATA_STD''': A container will be used to encapsulate any returned data, within the element. '''RPC_DATA_YANG''': The element will be the only container encapsulated any returned data. |- | rpc_datacb | void * | write | For operations that return streamed data, this pointer is set to the desired callback function to use for generated the data portion of the XML response. The template for this callback is '''agt_rpc_data_cb_t,''' found in agt_rpc.h |- | rpc_dataQ | dlq_hdr_t | write | For operations that return stored data, this queue of val_value_t structures can be used to provide the response data. Each val_value_t structure will be encoded as one of the corresponding RPC output parameters. |- | rpc_filter | op_filter_t | none | Internal structure for optimizing subtree and XPath retrieval operations. |- | rpc_need_undo | boolean | none | Internal flag to indicate if rollback-on-error is in effect dusing an operation. |- | rpc_undoQ | dlq_hdr_t | none | Queue of '''rpc_undo_rec_t''' structures, used to undo edits if rollback-on-error is in affect during an operation. |- | rpc_auditQ | dlq_hdr_t | none | Queue of rpc_audit_rec_t structures used internally to generate database alteration notifications and audit log entries. |} The following C code represents the '''rpc_msg_t''' data structure: /* NETCONF Server and Client RPC Request/Reply Message Header */ typedef struct rpc_msg_t_ { dlq_hdr_t qhdr; /* generic XML message header */ xml_msg_hdr_t mhdr; /* incoming: top-level rpc element data */ xml_attrs_t *rpc_in_attrs; /* borrowed from elem */ /* incoming: * 2nd-level method name element data, used in agt_output_filter * to check get or get-config; cannot import obj.h here! */ struct obj_template_t_ *rpc_method; /* incoming: SERVER RPC processing state */ int rpc_agt_state; /* agt_rpc_phase_t */ op_errop_t rpc_err_option; op_editop_t rpc_top_editop; val_value_t *rpc_input; /* incoming: * hooks for method routines to save context or whatever */ void *rpc_user1; void *rpc_user2; uint32 rpc_returncode; /* for nested callbacks */ /* incoming: get method reply handling builtin * If the rpc_datacb is non-NULL then it will be used as a * callback to generate the rpc-reply inline, instead of * buffering the output. * The rpc_data and rpc_filter parameters are optionally used * by the rpc_datacb function to generate a reply. */ rpc_data_t rpc_data_type; /* type of data reply */ void *rpc_datacb; /* agt_rpc_data_cb_t */ dlq_hdr_t rpc_dataQ; /* data reply: Q of val_value_t */ op_filter_t rpc_filter; /* backptrs for get* methods */ /* incoming: agent database edit transaction control block * must be freed by an upper layer if set to malloced data */ struct agt_cfg_transaction_t_ *rpc_txcb; /* load-config parse-error and --startup-error=continue * flag if the val_purge_errors_from_root function is needed */ boolean rpc_parse_errors; } rpc_msg_t; === SIL Support Functions For RPC Operations === The file agt/agt_rpc.c contains some functions that are used by SIL callback functions. The following table highlights the functions that may be useful to SIL developers:
'''agt/agt_rpc.c Functions'''
{| class="wikitable" border="1" !
Function
!
Description
|- | agt_rpc_register_method | Register a SIL RPC operation callback function for 1 callback phase. |- | agt_rpc_support_method | Tell the server that an RPC operation is supported by the system.. |- | agt_rpc_unsupport_method | Tell the server that an RPC operation is not supported by the system.. |- | agt_rpc_unregister_method | Remove all the SIL RPC operation callback functions for 1 RPC operation. |} === RPC Validate Callback Function === [[Image:rpc-validate-phase.png]] The RPC validate callback function is optional to use. Its purpose is to validate any aspects of an RPC operation, beyond the constraints checked by the server engine. Only 1 function can register for each YANG rpc statement. The standard NETCONF operations are reserved by the server engine. There is usually zero or one of these callback functions for every 'rpc' statement in the YANG module associated with the SIL code. It is enabled with the '''agt_rpc_register_method''' function, within the phase 1 initialization callback function. The '''yangdump''' code generator will create this SIL callback function by default. There will C comments in the code to indicate where your additional C code should be added. The '''val_find_child''' function is commonly used to find particular parameters within the RPC input section, which is encoded as a '''val_value_t''' tree. The '''agt_record_error''' function is commonly used to record any parameter or other errors. In the '''libtoaster''' example, there are internal state variables ('''toaster_enabled''' and '''toaster_toasting'''), maintained by the SIL code, which are checked in addition to any provided parameters. Example SIL Function Registration res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_make_toast, AGT_RPC_PH_VALIDATE, y_toaster_make_toast_validate); if (res != NO_ERR) { return res; } Example SIL Function: /******************************************************************** * FUNCTION y_toaster_make_toast_validate * * RPC validation phase * All YANG constriants have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_make_toast_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; val_value_t *errorval; const xmlChar *errorstr; val_value_t *toasterDoneness_val; val_value_t *toasterToastType_val; //uint32 toasterDoneness; //val_idref_t *toasterToastType; res = NO_ERR; errorval = NULL; errorstr = NULL; toasterDoneness_val = val_find_child( msg->rpc_input, y_toaster_M_toaster, y_toaster_N_toasterDoneness); if (toasterDoneness_val != NULL && toasterDoneness_val->res == NO_ERR) { //toasterDoneness = VAL_UINT(toasterDoneness_val); // validate toast doneness within instrumentation if needed } toasterToastType_val = val_find_child( msg->rpc_input, y_toaster_M_toaster, y_toaster_N_toasterToastType); if (toasterToastType_val != NULL && toasterToastType_val->res == NO_ERR) { //toasterToastType = VAL_IDREF(toasterToastType_val); // validate toast-type within instrumentation if needed } /* added code starts here */ if (toaster_enabled) { /* toaster service enabled, check if in use */ if (toaster_toasting) { res = ERR_NCX_IN_USE; } else { /* this is where a check on bread inventory would go */ /* this is where a check on toaster HW ready would go */ } } else { /* toaster service disabled */ res = ERR_NCX_RESOURCE_DENIED; } /* added code ends here */ /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_toaster_make_toast_validate */ === RPC Invoke Callback Function === [[Image:rpc-invoke-phase.png]] The RPC invoke callback function is used to perform the operation requested by the client session. Only 1 function can register for each YANG rpc statement. The standard NETCONF operations are reserved by the server engine. There is usually one of these callback functions for every 'rpc' statement in the YANG module associated with the SIL code. The RPC invoke callback function is optional to use, although if no invoke callback is provided, then the operation will have no affect. Normally, this is only the case if the module is be tested by an application developer, using '''netconfd''' as a server simulator. It is enabled with the '''agt_rpc_register_method''' function, within the phase 1 initialization callback function. The '''yangdump''' code generator will create this SIL callback function by default. There will be C comments in the code to indicate where your additional C code should be added. The '''val_find_child''' function is commonly used to retrieve particular parameters within the RPC input section, which is encoded as a '''val_value_t''' tree. The''' rpc_user1''' and '''rpc_user2''' cache pointers in the '''rpc_msg_t''' structure can also be used to store data in the validation phase, so it can be immediately available in the invoke phase. The '''agt_record_error''' function is commonly used to record any internal or platform-specific errors. In the '''libtoaster''' example, if the request to create a timer callback control block fails, then an error is recorded. For RPC operations that return either an or response, there is nothing more required of the RPC invoke callback function. For operations which return some data or , the SIL code must do 1 of 2 additional tasks: * add a '''val_value_t''' structure to the '''rpc_dataQ''' queue in the''' rpc_msg_t '''for each parameter listed in the YANG rpc 'output' section. * set the rpc_datacb pointer in the rpc_msg_t structure to the address of your data reply callback function. See the '''agt_rpc_data_cb_t''' definition in agt/agt_rpc.h for more details. Example SIL Function Registration res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_make_toast, AGT_RPC_PH_INVOKE, y_toaster_make_toast_invoke); if (res != NO_ERR) { return res; } Example SIL Function: /******************************************************************** * FUNCTION y_toaster_make_toast_invoke * * RPC invocation phase * All constraints have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_make_toast_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; val_value_t *toasterDoneness_val; val_value_t *toasterToastType_val; uint32 toasterDoneness; //val_idref_t *toasterToastType; res = NO_ERR; toasterDoneness = 0; toasterDoneness_val = val_find_child( msg->rpc_input, y_toaster_M_toaster, y_toaster_N_toasterDoneness); if (toasterDoneness_val != NULL && toasterDoneness_val->res == NO_ERR) { toasterDoneness = VAL_UINT(toasterDoneness_val); } toasterToastType_val = val_find_child( msg->rpc_input, y_toaster_M_toaster, y_toaster_N_toasterToastType); if (toasterToastType_val != NULL && toasterToastType_val->res == NO_ERR) { //toasterToastType = VAL_IDREF(toasterToastType_val); // invoke instrumentation with this toast type } /* invoke your device instrumentation code here */ /* make sure the toasterDoneness value is set */ if (toasterDoneness_val == NULL) { toasterDoneness = 5; /* set the default */ } /* arbitrary formula to convert toaster doneness to the * number of seconds the toaster should be on */ toaster_duration = toasterDoneness * 12; /* this is where the code would go to adjust the duration * based on the bread type */ if (LOGDEBUG) { log_debug("\ntoaster: starting toaster for %u seconds", toaster_duration); } /* this is where the code would go to start the toaster * heater element */ /* start a timer to toast for the specified time interval */ res = agt_timer_create(toaster_duration, FALSE, toaster_timer_fn, NULL, &toaster_timer_id); if (res == NO_ERR) { toaster_toasting = TRUE; } else { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } /* added code ends here */ return res; } /* y_toaster_make_toast_invoke */ === RPC Post Reply Callback Function === [[Image:rpc-post-reply-phase.png]] The RPC post-reply callback function is used to clean up after a message has been processed. Only 1 function can register for each YANG rpc statement. The standard NETCONF operations are reserved by the server engine. This callback is not needed unless the SIL validate or invoke callback allocated some memory that needs to be deleted after the is sent. The RPC post reply callback function is optional to use. It is enabled with the '''agt_rpc_register_method''' function, within the phase 1 initialization callback function. The '''yangdump''' code generator will not create this SIL callback function by default. Example SIL Function Registration res = agt_rpc_register_method( y_foo_M_foo, y_foo_N_command, AGT_RPC_PH_POST_REPLY, y_foo_command_post); if (res != NO_ERR) { return res; } Example SIL Function: /******************************************************************** * FUNCTION y_foo_command_post * * RPC post reply phase * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_foo_command_post ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { (void)scb; (void)methnode; if (msg->rpc_user1 != NULL) { m__free(msg->rpc_user1); msg->rpc_user1 = NULL; } return NO_ERR; } /* y_foo_command_post */ == Database Operations == The server database is designed so that the SIL callback functions do not need to really know which database model is being used by the server (e.g., target is candidate vs. running configuration). There are three SIL database edit callback phases: # '''Validate''': Check the parameters no matter what database is the target # '''Apply''': The server will manipulate the database nodes as needed. The SIL usually has nothing to do in this phase unless internal resources need to be reserved. # '''Commit or Rollback''': Depending on the result of the previous phases, either the commit or the rollback callback phase will be invoked, if and when the changes are going to be finalized in the running configuration. The SIL code is not responsible for maintaining the value tree for any database. This is done by the server. The SIL database edit callback code is responsible for the following tasks: * Perform any data-model specific validation that is not already covered by a machine-readable statement, during the validation phase. * Reserve any data-model specific resources for the proposed new configuration content, during the apply phase. * Activate any data-model behavior changes based on the new configuration content, during the commit phase. * Release any reserved resources that were previously allocated in the apply phase, during the rollback phase. === Database Template (cfg_template_t) === Every NETCONF database has a common template control block, and common set of access functions. NETCONF databases are not separate entities like separate SQL databases. Each NETCONF database is conceptually the same database, but in different states: * '''candidate''': A complete configuration that may contain changes that have not been applied yet. This is only available if the :candidate capability is advertised by the server. The value tree for this database contains only configuration data nodes. * '''running''': The complete current server configuration. This is available on every NETCONF server. The value tree for this database contains configuration data nodes and non-configuration nodes created and maintained by the server. The server will maintain read-only nodes when , , or operations are performed on the running configuration. SIL code should not alter the data nodes within a configuration directly. This work is handled by the server. SIL callback code should only alter its own data structures, if needed. * '''startup''': A complete configuration that will be used upon the next reboot of the device. This is only available if the :startup capability is advertised by the server. The value tree for this database contains only configuration data nodes. NETCONF also recognized external files via the parameter, if the :url capability is advertised by the server. These databases will be supported in a future release of the server. The NETCONF standard does not require that these external databases support the same set of protocol operations as the standard databases, listed above. A client application can reliably copy from and to an external database, but editing and filtered retrieval may not be supported. The following typedef is used to define a NETCONF database template (ncx/cfg.h): /* struct representing 1 configuration database */ typedef struct cfg_template_t_ { ncx_cfg_t cfg_id; cfg_location_t cfg_loc; cfg_state_t cfg_state; cfg_transaction_id_t last_txid; cfg_transaction_id_t cur_txid; xmlChar *name; xmlChar *src_url; xmlChar lock_time[TSTAMP_MIN_SIZE]; xmlChar last_ch_time[TSTAMP_MIN_SIZE]; uint32 flags; ses_id_t locked_by; cfg_source_t lock_src; dlq_hdr_t load_errQ; /* Q of rpc_err_rec_t */ dlq_hdr_t plockQ; /* Q of plock_cb_t */ val_value_t *root; /* btyp == NCX_BT_CONTAINER */ } cfg_template_t; The following table highlights the fields in the cfg_template_t data structure:
'''cfg_template_t Fields'''
{| class="wikitable" border="1" !
Field
!
Description
|- | cfg_id | Internal configuration ID assigned to this configuration. |- | cfg_loc | Enumeration identifying the configuration source location. |- | cfg_state | Current internal configuration state. |- | name | Name string for this configuration. |- | last_txid | |- | cur_txid | |- | src_url | URL for use with 'cfg_loc' to identify the configuration source. |- | load_time | Date and time string when the configuration was loaded. |- | lock_time | Date and time string when the configuration was last locked. |- | last_ch_time | Date and time string when the configuration was last changed. |- | flags | Internal configuration flags. Do not use directly. |- | locked_by | Session ID that owns the global configuration lock, if the database is currently locked. |- | lock_src | If the database is locked, identifies the protocol or other source that currently caused the database to be locked. |- | load_errQ | Queue of rpc_err_rec_t structures that represent any records that were generated when the configuration was loaded, if any. |- | plockQ | |- | root | The root of the value tree representing the entire database. |} === Database Access Functions === The file '''ncx/cfg.h''' contains some high-level database access functions that may be of interest to SIL callback functions for custom RPC operations. All database access details are handled by the server if the database edit callback functions are used (associated with a particular object node supported by the server)..The following table highlights the most commonly used functions. Refer to the H file for a complete definition of each API function.
'''cfg_template_t Access Functions'''
{| class="wikitable" border="1" !
Function
!
Description
|- | cfg_new_template | Create a new configuration database. |- | cfg_free_template | Free a configuration database. |- | cfg_get_state | Get the current internal database state. |- | cfg_get_config | Get a configuration database template pointer, from a configuration name string. |- | cfg_get_config_id | Get a configuration database template pointer, from a configuration ID. |- | cfg_fill_candidate_from_inline | Fill the candidate database from an internal value tree data structure. |- | cfg_get_dirty_flag | Returns TRUE if the database has changes in it that have not been saved yet. This applies to the candidate and running databases at this time. |- | cfg_ok_to_lock | Check if the database could be successfully locked by a specific session. |- | cfg_ok_to_unlock | Check if the database could be successfully unlocked by a specific session. |- | cfg_ok_to_read | Check if the database is in a state where read operations are allowed. |- | cfg_ok_to_write | Check if the database could be successfully written by a specific session. Checks the global configuration lock, if any is set. |- | cfg_is_global_locked | Returns TRUE if the database is locked right now with a global lock. |- | cfg_get_global_lock_info | Get some information about the current global lock on the database. |- | cfg_lock | Get a global lock on the database. |- | cfg_unlock | Release the global lock on the database. |- | cfg_release_locks | Release all locks on all databases |} === Database Callback Initialization and Cleanup === The file '''agt/agt_cb.h''' contains functions that a SIL developer needs to register and unregister database edit callback functions. The same callback function can be used for different phases, if desired. The following function template definition is used for all SIL database edit callback functions: /* Callback function for server object handler * Used to provide a callback sub-mode for * a specific named object * * INPUTS: * scb == session control block making the request * msg == incoming rpc_msg_t in progress * cbtyp == reason for the callback * editop == the parent edit-config operation type, which * is also used for all other callbacks * that operate on objects * newval == container object holding the proposed changes to * apply to the current config, depending on * the editop value. Will not be NULL. * curval == current container values from the * or configuration, if any. Could be NULL * for create and other operations. * * RETURNS: * status: */ typedef status_t ('''*agt_cb_fn_t''') (ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval);
'''SIL Database Callback Template'''
{| class="wikitable" border="1" !
Parameter
!
Description
|- | scb | The session control block making the edit request. The SIL callback code should not need this parameter except to pass to functions that need the SCB. |- | msg | Incoming RPC message in progress. The server uses some fields in this structure, and there are 2 SIL fields for the RPC callback functions. The SIL callback code should not need this parameter except to pass to functions that need the message header. |- | cbtyp | Enumeration for the callback phase in progress.. |- | editop | The edit operation in effect as this node is being processed. |- | newval | Value node containing the new value for a create, merge, replace, or insert operation. The 'newval' parm may be NULL and should be ignored for a delete operation. In that case, the 'curval' pointer contains the node being deleted. |- | curval | Value node containing the current database node that corresponds to the 'newval' node, if any is available. |} A SIL database edit callback function is hooked into the server with the '''agt_cb_register_callback''' or '''agt_cb_register_callbacks''' functions, described below. The SIL code generated by yangdump uses the first function to register a single callback function for all callback phases. extern status_t '''agt_cb_register_callback '''(const xmlChar *modname, const xmlChar *defpath, const xmlChar *version, const agt_cb_fn_t cbfn);
'''agt_cb_register_callback'''
{| class="wikitable" border="1" !
Parameter
!
Description
|- | modname | Module name string that defines this object node. |- | defpath | Absolute path expression string indicating which node the callback function is for. |- | version | If non-NULL, indicates the exact module version expected. |- | cbfn | The callback function address. This function will be used for all callback phases. |} extern status_t '''agt_cb_register_callbacks''' (const xmlChar *modname, const xmlChar *defpath, const xmlChar *version, const agt_cb_fnset_t *cbfnset);
'''agt_cb_register_callbacks'''
{| class="wikitable" border="1" !
Parameter
!
Description
|- | modname | Module name string that defines this object node. |- | defpath | Absolute path expression string indicating which node the callback function is for. |- | version | If non-NULL, indicates the exact module version expected. |- | cbfnset | The callback function set structure, fillied in with the addesses of all desired callback phases. Any NULL slots will cause that phase to be skipped. |} The '''agt_cb_unregister_callbacks''' function is called during the module cleanup. It is generated by '''yangdump''' automatically for all RPC operations. extern void '''agt_cb_unregister_callbacks '''(const xmlChar *modname, const xmlChar *defpath);
'''agt_cb_unregister_callbacks'''
{| class="wikitable" border="1" !
Parameter
!
Description
|- | modname | Module name string that defines this object node. |- | defpath | Absolute path expression string indicating which node the callback function is for. |} === Example SIL Database Edit Callback Function === The following example code is from the '''libtoaster''' source code: /******************************************************************** * FUNCTION y_toaster_toaster_edit * * Edit database object callback * Path: /toaster * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_toaster_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: toaster_enabled = TRUE; toaster_toasting = FALSE; break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: toaster_enabled = TRUE; toaster_toasting = FALSE; break; case OP_EDITOP_DELETE: toaster_enabled = FALSE; if (toaster_toasting) { agt_timer_delete(toaster_timer_id); toaster_timer_id = 0; toaster_toasting = FALSE; y_toaster_toastDone_send((const xmlChar *)"error"); } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res == NO_ERR) { res = agt_check_cache( &toaster_val, newval, curval, editop); } if (res == NO_ERR && (editop == OP_EDITOP_LOAD || editop == OP_EDITOP_CREATE)) { res = y_toaster_toaster_mro(newval); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_toaster_toaster_edit */ === Database Edit Validate Callback Phase === [[Image:database-edit-validate-phase.png]] A SIL database validation phase callback function is responsible for checking all the 'description statement' sort of data model requirements that are not covered by any of the YANG machine-readable statements. For example, if a 'user name' parameter needed to match an existing user name in''' /etc/passwd, '''then the SIL validation callback would call the system APIs needed to check if the 'newval' string value matched a valid user name. The server will make sure the user name is well-formed and could be a valid user name. === Database Edit Apply Callback Phase === The callback function for this phase is called when database edits are being applied to the running configuration. The resources needed for the requested operation may be reserved at this time, if needed. === Database Edit Commit Callback Phase === This callback function for this phase is called when database edits are being committed to the running configuration. The SIL callback function is expected to finalize and apply any data-model dependent system behavior at this time. === Database Edit Rollback Callback Phase === This callback function for this phase is called when database edits are being undone, after some apply phase or commit phase callback function returned an error, or a confirmed commit operation timed out. The SIL callback function is expected to release any resources it allocated during the apply or commit phases. Usually only the commit or the rollback function will be called for a given SIL callback, but it is possible for both to be called. For example, if the 'rollback-on-error' option is in effect, and some SIL commit callback fails after your SIL commit callback succeeds, then your SIL rollback callback may be called as well. === Database Virtual Node Get Callback Function === A common SIL callback function to use is a virtual node 'get' function. A virtual node can be either a configuration or non-configuration node, but is more likely to be a non-configuration node, such as a counter or hardware status object. The function '''agt_make_virtual_leaf''' in '''agt/agt_util.h''' is a common API used for creating a virtual leaf within an existing parent container. The following typedef defines the '''getcb_fn_t '''template, used by all virtual callback functions. This function is responsible for filling in a value node with the current instance value. The status NO_ERR is returned if this is done successfully. /* getcb_fn_t * * Callback function for agent node get handler * * INPUTS: * scb == session that issued the get (may be NULL) * can be used for access control purposes * cbmode == reason for the callback * virval == place-holder node in the data model for * this virtual value node * dstval == pointer to value output struct * * OUTPUTS: * *fil may be adjusted depending on callback reason * *dstval should be filled in, depending on the callback reason * * RETURNS: * status: */ typedef status_t (*getcb_fn_t) (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); The following table describes the parameters.
'''getcb_fn_t Parameters'''
{| class="wikitable" border="1" !
Parameter
!
Description
|- | scb | This is the session control block making the request, if available. This pointer may be NULL, so in the rare event this parameter is needed by the SIL callback function, it should be checked first. |- | cbmode | The callback type. The only supported enumeration at this time is GETCB_GET_VALUE. Other values may be used in the future for different retrieval modes. |- | virval | The virtual value node in the data tree that is being referenced, The''' val_get_virtual_value''' function was called for this value node. |- | dstval | The destination value node that needs to be filled in. This is just an empty value node that has been malloced and then initialized with the '''val_init_from_template''' function. The SIL callback function needs to set the value properly. The '''val_set_simval''' and '''val_set_simval_obj''' functions are two API functions that can be used for this purpose. |} ==== Example 1==== The following example from '''agt/agt_ses.c''' shows a SIL get callback function for the ietf-netconf-monitoring data model, which returns the 'in-sessions' counter value: /******************************************************************** * FUNCTION agt_ses_get_inSessions * * operation handler for the inSessions counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ status_t agt_ses_get_inSessions (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { (void)scb; (void)virval; if (cbmode == GETCB_GET_VALUE) { VAL_UINT(dstval) = agttotals->inSessions; return NO_ERR; } else { return ERR_NCX_OPERATION_NOT_SUPPORTED; } } /* agt_ses_get_inSessions */ ==== Example 2==== The following example from '''agt/agt_state.c''' shows a complex get callback function for the same data model, which returns the entire element when it is requested. This is done by simply cloning the 'official copy' of the server capabilities that is maintained by the agt/agt_caps.c module. static status_t get_caps (ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *virval, val_value_t *dstval) { val_value_t *capsval; status_t res; (void)scb; (void)virval; res = NO_ERR; if (cbmode == GETCB_GET_VALUE) { capsval = val_clone(agt_cap_get_capsval()); if (!capsval) { return ERR_INTERNAL_MEM; } /* change the namespace to this module, * and get rid of the netconf NSID */ val_change_nsid(capsval, statemod->nsid); val_move_children(capsval, dstval); val_free_value(capsval); } else { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } return res; } /* get_caps */ == Notifications == The '''yangdump''' program will automatically generate functions to queue a specific notification type for processing. It is up to the SIL callback code to invoke this function when the notification event needs to be generated. The SIL code is expected to provide the value nodes that are needed for any notification payload objects. === Notification Send Function === The function to generate a notification control block and queue it for notification replay and delivery is generated by the '''yangdump''' program. A function parameter will exist for each top-level data node defined in the YANG notification definition. In the example below, the 'toastDone' notification event contains just one leaf, called the 'toastStatus'. There is SIL timer callback code which calls this function, and provides the final toast status, after the operation has been completed or canceled. /******************************************************************** * FUNCTION y_toaster_toastDone_send * * Send a y_toaster_toastDone notification * Called by your code when notification event occurs * ********************************************************************/ void y_toaster_toastDone_send ( const xmlChar *toastStatus) { agt_not_msg_t *notif; val_value_t *parmval; status_t res; res = NO_ERR; if (LOGDEBUG) { log_debug("\nGenerating notification"); } notif = agt_not_new_notification(toastDone_obj); if (notif == NULL) { log_error("\nError: malloc failed, cannot send notification"); return; } /* add toastStatus to payload */ parmval = agt_make_leaf( toastDone_obj, y_toaster_N_toastStatus, toastStatus, &res); if (parmval == NULL) { log_error( "\nError: make leaf failed (%s), cannot send notification", get_error_string(res)); } else { agt_not_add_to_payload(notif, parmval); } agt_not_queue_notification(notif); } /* y_toaster_toastDone_send */ == Periodic Timer Service == Some SIL code may need to be called at periodic intervals to check system status, update counters, and/or perhaps send notifications. The file '''agt/agt_timer.h''' contains the timer access function declarations. This section provides a brief overview of the SIL timer service. === Timer Callback Function === The timer callback function is expected to do a short amount of work, and not block the running process. The function returns zero for a normal exit, and -1 if there was an error and the timer should be destroyed. The '''agt_timer_fn_t''' template in '''agt/agt_timer.h''' is used to define the SIL timer callback function prototype. This typedef defines the callback function template expected by the server for use with the timer service: /* timer callback function * * Process the timer expired event * * INPUTS: * timer_id == timer identifier * cookie == context pointer, such as a session control block, * passed to agt_timer_set function (may be NULL) * * RETURNS: * 0 == normal exit * -1 == error exit, delete timer upon return */ typedef int (*agt_timer_fn_t) (uint32 timer_id, void *cookie); The following table describes the parameters for this callback function:
'''SIL Timer Callback Function Parameters'''
{| class="wikitable" border="1" !
Parameter
!
Description
|- | timer_id | The timer ID that was returned when the '''agt_timer_create''' function was called. |- | cookie | The cookie parameter value that was passed to the server when the '''agt_timer_create''' function was called. |} === Timer Access Functions === A SIL timer can be set up as a periodic timer or a one-time event. The timer interval (in seconds) and the SIL timer callback function are provided when the timer is created. A timer can also be restarted if it is running, and the time interval can be changed as well. The following table highlights the SIL timer access functions in '''agt/agt_timer.h:'''
'''SIL Timer Access Functions'''
{| class="wikitable" border="1" !
Function
!
Description
|- | agt_timer_create | Create a SIL timer. |- | agt_timer_restart | Restart a SIL timer |- | agt_timer_delete | Delete a SIL timer |} === Example Timer Callback Function === The following example from toaster.c simply completes the toast when the timer expires and calls the auto-generated 'toastDone' send notification function: /******************************************************************** * FUNCTION toaster_timer_fn * * Added timeout function for toaster function * * INPUTS: * see agt/agt_timer.h * * RETURNS: * 0 for OK; -1 to kill periodic timer ********************************************************************/ static int toaster_timer_fn (uint32 timer_id, void *cookie) { (void)timer_id; (void)cookie; /* toast is finished */ toaster_toasting = FALSE; toaster_timer_id = 0; if (LOGDEBUG2) { log_debug2("\ntoast is finished"); } y_toaster_toastDone_send((const xmlChar *)"done"); return 0; } /* toaster_timer_fn */ = Server Callback Examples = This section was written by Mark Pashley for the Yuma Integration Test Suite. It is included here because it explains the details of SIL callback for several message flow examples. This document details the SIL callbacks that are made when Yuma processes NETCONF edit queries when configured to use the candidate database configuration (--target=candidate). == YANG == The following YANG defines the configuration that is used for all of the examples within this document. '''container xpo''' { presence "Indicates that the Device Test API is available."; description "Top-level container for all configuration and status objects."; //////////////////////////////////// // Start of main configuration block //////////////////////////////////// '''grouping connectionItem''' { description "Connection container."; leaf sourceId { description "The ID of the item providing the input to the connection."; type uint32; } leaf bitrate { description "The maximum expected bitrate over this connection."; type uint32; units "bps"; } } '''list profile''' { key id; description "Profile container."; leaf id { description "Unique ID for this profile."; type uint32; } '''list streamConnection''' { description "Connection between two streams."; key id; leaf id { description "Connection identifier."; type uint32; } uses connectionItem; } } '''leaf activeProfile''' { description "The number of the active profile."; type uint32; } } == Edit Operations == The following sections identify the messages sent by the Netconf Client and the SIL callbacks that will be made. The message sequence charts do not show operations for locking and unlocking of the running and candidate configurations. === Create an XPO container === The message sequence chart below shows the expected SIL callbacks for a create xpo container operation. [[Image:create-an-xpo-container.png]] # The client issues an ''rpc edit-config'' to create an xpo container: xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> merge nc:operation="create" xmlns="http://www.ericsson.com/television/ns/xpo3/base"/>
# Netconf executes the ''xpo_edit'' callback to validate the change to the ''candidate'' configuration. # Netconf executes the ''xpo_edit'' callback to apply the change to the ''candidate'' configuration. # The client receives an ''rpc-reply'' indicating success. # The client issues an ''rpc commit'' to commit the change to the ''running'' configuration. # Netconf executes the ''xpo_edit'' callback to validate the change to the ''running'' configuration. # Netconf executes the ''xpo_edit'' callback to perform a create operation change to the ''candidate'' configuration. # The client receives an ''rpc-reply'' indicating success. === Create a Profile === The message sequence chart below shows the expected SIL callbacks for a create profile operation. [[Image:create-an-xpo-profile.png]] * The client issues an ''rpc edit-config'' to create a profile: xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> merge xmlns="http://www.ericsson.com/television/ns/xpo3/base"> nc:operation="create">1 * Netconf executes the ''profile_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_id_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_id_edit'' callback for the ''candidate'' configuration. * The client receives an ''rpc-reply'' indicating success. * The client issues an ''rpc commit'' to commit the change to the ''running'' configuration. * Netconf executes the ''profile_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_id_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_id_edit< commit create >'' callback for the ''running'' configuration. * The client receives an ''rpc-reply'' indicating success. === Create a Stream Connection === The message sequence chart below shows the expected SIL callbacks for a create profile stream connection operation. In this scenario the XPO container is empty prior to step 1. [[Image:create-an-xpo-stream.png]] * The client issues an ''rpc edit-config'' to create a profile stream connection. (Note the profile has not previously been created) xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> merge 1 1 100 500 * Netconf executes the ''profile_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_id_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_streamConnection_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_streamConnection_id_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_streamConnection_sourceId_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_streamConnection_bitrate_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_id_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_streamConnection_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_streamConnection_id_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_streamConnection_sourceId_edit'' callback for the ''candidate'' configuration. * Netconf executes the ''profile_streamConnection_bitrate_edit'' callback for the ''candidate'' configuration. * The client receives an ''rpc-reply'' indicating success. * The client issues an ''rpc commit'' to commit the change to the ''running'' configuration. * Netconf executes the ''profile_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_id_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_streamConnection_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_streamConnection_id_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_streamConnection_sourceId_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_streamConnection_bitrate_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_id_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_streamConnection_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_streamConnection_id_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_streamConnection_sourceId_edit'' callback for the ''running'' configuration. * Netconf executes the ''profile_streamConnection_bitrate_edit'' callback for the ''running'' configuration. * The client receives an ''rpc-reply'' indicating success. === Delete an XPO Container === The message sequence chart below shows the expected SIL callbacks for a delete xpo container operation. In this scenario the XPO container is populated with at last one profile prior to step 1. [[Image:delete-an-xpo-container.png]] # The client issues an ''rpc edit-config'' to delete an xpo container: xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> merge nc:operation="delete" xmlns="http://www.ericsson.com/television/ns/xpo3/base"/> # Netconf executes the ''xpo_edit'' callback to validate the change to the ''candidate'' configuration. # Netconf executes the ''xpo_edit'' callback to apply the change to the ''candidate'' configuration. # The client receives an ''rpc-reply'' indicating success. # The client issues an ''rpc commit'' to commit the change to the ''running'' configuration. # Netconf executes the ''xpo_edit'' callback to validate the change to the ''running'' configuration. # Netconf executes the ''xpo_edit'' callback to perform a create operation change to the ''candidate'' configuration. # The client receives an ''rpc-reply'' indicating success. === Delete a Profile === The message sequence chart below shows the expected SIL callbacks for a delete xpo profile operation. In this scenario the XPO container is populated with at last one profile prior to step 1. [[Image:delete-an-xpo-profile.png]] # The client issues an ''rpc edit-config'' to delete a profile: xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> merge 1 # Netconf executes the ''xpo_profile_edit'' callback to validate the change to the ''candidate'' configuration. # Netconf executes the ''xpo_ profile_edit'' callback to apply the change to the ''candidate'' configuration. # The client receives an ''rpc-reply'' indicating success. # The client issues an ''rpc commit'' to commit the change to the ''running'' configuration. # Netconf executes the ''xpo_ profile_edit'' callback to validate the change to the ''running'' configuration. # Netconf executes the ''xpo_ profile_edit'' callback to perform a create operation change to the ''candidate'' configuration. # The client receives an ''rpc-reply'' indicating success. === Delete a Stream Connection === Deleting a stream connection is no different to deleting a profile. For delete operations SIL callbacks are only made for the highest level node that is being deleted. = Development Environment = This section describes the Yuma Tools development environment used to produce the Linux binaries. == Programs and Libraries Needed == There are several components used in the Yuma software development environment: * gcc compiler and linker * ldconfig and install programs * GNU make program * shell program, such as bash * Yuma development tree: the source tree containing Yuma code, specified with the '''$YUMA_HOME''' environment variable. * SIL development tree: the source tree containing server instrumentation code The following external program is used by Yuma, and needs to be pre-installed: * '''opensshd (needed by netconfd)''' ** The SSH2 server code does not link with '''netconfd'''. Instead, the '''netconf-subsystem''' program is invoked, and local connections are made to the '''netconfd''' server from this SSH2 subsystem. The following program is part of Yuma Tools, and needs to be installed: * '''netconf-subsystem (needed by netconfd)''' ** The thin client sub-program that is called by '''sshd''' when a new SSH2 connection to the 'netconf' sub-system is attempted. ** This program will use an AF_LOCAL socket, using a proprietary '''''' message, to communicate with the '''netconfd''' server.. ** After establishing a connection with the '''netconfd''' server, this program simply transfers SSH2 NETCONF channel data between '''sshd''' and '''netconfd'''. The following program is part of Yuma Tools, and usually found within the Yuma development tree: * '''netconfd''' ** The NETCONF server that processes all protocol operations. ** The '''agt_ncxserver''' component will listen for '''''' messages on the designated socket (e.g. /tmp/ncxserver.sock). If an invalid message is received, the connection will be dropped. Otherwise, the''' netconf-subsystem '''will begin passing NETCONF channel data to the netconfd server. The first message is expected to be a valid NETCONF PDU. The following external libraries are used by Yuma, and need to be pre-installed: * '''ncurses (needed by yangcli)''' ** character processing library needed by '''libtecla'''; used within '''yangcli''' * '''libc (or glibc, needed by all applications)''' ** unix system library * '''libssh2 (needed by yangcli)''' ** SSH2 client library, used by yangcli * '''libxml2 (needed by all applications)''' ** xmlTextReader XML parser ** pattern support == SIL Makefile == The automake program is used at this time. === Target Platforms === Tested on Debian and Ubuntu should work on any Unix distribution with autotools support. === Example: Building a SIL === ====GCC command line==== This will build the libtoaster.so SIL from toaster.c: gcc -shared -fPIC -DPIC -I. -I/usr/include/yuma/agt -I/usr/include/yuma/ncx -I/usr/include/yuma/platform -I/usr/include/libxml2 -I/usr/include/libxml2/libxml -rdynamic toaster.c /usr/lib/libyumancx.so /usr/lib/libyumaagt.so /usr/lib/i386-linux-gnu/libxml2.so -lz -ldl -O0 -o libtoaster.so This will install it .. and the module is ready to be used: sudo cp libtoaster.so /usr/lib/yuma/ ====Autotools==== You need a configure.ac and Makefile.am: autoreconf -i -f ./configure make sudo make install == Automation Control == The YANG language includes many ways to specify conditions for database validity, which traditionally are only documented in DESCRIPTION clauses. The YANG language allows vendors and even data modelers to add new statements to the standard syntax, in a way that allows all tools to skip extension statements that they do not understand. The '''yangdump''' YANG compiler sames all the non-standard language statements it finds, even those it does not recognize. These are stores in the '''ncx_appinfo_t''' data structure in '''ncx/ncxtypes.h'''.. There are also SIL access functions defined in '''ncx/ncx_appinfo.h''' that allow these language statements to be accessed. If an argument string was provided, it is saved along with the command name. Several data structures contains an 'appinfoQ' field to contain all the ncx_appinfo_t stuctures that were generated within the same YANG syntax block (e.g., within a typedef, type, leaf, import statement). === Built-in YANG Language Extensions === There are several YANG extensions that are supported by Yuma. They are all defined in the YANG file named '''ncx.yang'''. They are used to 'tag' YANG definitions for some sort of automatic processing by Yuma programs. Extensions are position-sensitive, and if not used in the proper context, they will be ignored. A YANG extension statement must be defined (somewhere) for every extension used in a YANG file, or an error will occur. Most of these extensions apply to '''netconfd''' server behavior, but not all of them. For example, the '''ncx:hidden''' extension will prevent '''yangcli''' from displaying help for an object containing this extension. Also, '''yangdump''' will skip this object in HTML output mode. The following table describes the supported YANG language extensions. All other YANG extension statements will be ignored by Yuma, if encountered in a YANG file:
'''YANG Language Extensions'''
{| class="wikitable" border="1" |
'''extension'''
|
'''description'''
|- | '''ncx:hidden'''; | Declares that the object definition should be hidden from all automatic documentation generation. Help will not be available for the object in '''yangcli'''. |- | '''ncx:metadata ''' "''attr-type attr-name''"; | Defines a qualified XML attribute in the module namespace. Allowed within an RPC input parameter. '''attr-type''' is a valid type name with optional YANG prefix. '''attr-name''' is the name of the XML attribute. |- | '''ncx:no-duplicates'''; | Declares that the '''ncx:xsdlist''' data type is not allowed to contain duplicate values. The default is to allow duplicate token strings within an '''ncx:xsdlist''' value. |- | '''ncx:password'''; | Declares that a string data type is really a password, and will not be displayed or matched by any filter. |- | '''ncx:qname;''' | Declares that a string data type is really an XML qualified name. XML prefixes will be properly generated by '''yangcli''' and '''netconfd'''. |- | '''ncx:root'''; | Declares that the container parameter is really a NETCONF database root, like in the operations. The child nodes of this container are not specified in the YANG file. Instead, they are allowed to contain any top-level object from any YANG file supported by the server. |- | '''ncx:schema-instance'''; | Declares that a string data type is really an special schema instance identifier string. It is the same as an instance-identifier built-in type except the key leaf predicates are optional. For example, missing key values indicate wild cards that will match all values in '''nacm''' expressions. |- | '''ncx:secure;''' | Declares that the database object is a secure object. If the object is an '''rpc''' statement, then only the '''netconfd''' 'superuser' will be allowed to invoke this operation by default. Otherwise, only read access will be allowed to this object by default, Write access will only be allowed by the 'superuser', by default. |- | '''ncx:very-secure;''' | Declares that the database object is a very secure object. Only the 'superuser' will be allowed to access the object, by default. |- | '''ncx:xsdlist''' "''list-type''"; | Declares that a string data type is really an XSD style list. '''list-type''' is a valid type name with optional YANG prefix. List processing within will be automatically handled by '''netconfd'''. |- | '''ncx:xpath'''; | Declares that a string data type is really an XPath expression. XML prefixes and all XPath processing will be done automatically by '''yangcli''' and '''netconfd'''. |} === SIL Language Extension Access Functions === The following table highlights the SIL functions in ncx/ncx_appinfo.h that allow SIL code to examine any of the non-standard language statements that were found in the YANG module:
'''Language Extension Access Functions'''
{| class="wikitable" border="1" !
Function
!
Description
|- | ncx_find_appinfo | Find an '''ncx_appinfo_t '''structure by its prefix and name, in a queue of these entries. |- | ncx_find_next_appinfo | Find the next occurrence of the specified '''ncx_appinfo_t''' data structure. |- | ncx_clone_appinfo | Clone the specified''' ncx_appinfo_t''' data structure. |} yuma123_2.14/netconf/doc/yuma_docs/yuma-installation-guide.txt0000664000175000017500000002513714770023131024625 0ustar vladimirvladimir
'''Yuma Installation Guide'''
YANG-Based Unified Modular Automation Tools
YUMA Package Installation
Version yuma123-2.13
= Preface = == Legal Statements == Copyright 2009 – 2012, Andy Bierman, All Rights Reserved. Copyright 2013 – 2017, Vladimir Vassilev, All Rights Reserved. == Additional Resources == Other documentation includes: [[Yuma Quickstart Guide]] [[Yuma User Manual]] [[Yuma netconfd Manual]] [[Yuma yangcli Manual]] [[Yuma Developer Manual]] There are several sources of free information and tools for use with YANG and/or NETCONF. The following section lists the resources available at this time. === WEB Sites === * '''Netconf Central''' ** [http://www.netconfcentral.org/ http://www.netconfcentral.org/] ** Yuma Home Page ** Free information on NETCONF and YANG, tutorials, on-line YANG module validation and documentation database * '''Yuma123 SourceForge open source Project''' ** [http://sourceforge.net/projects/yuma123/ http://sourceforge.net/projects/yuma123/] ** Download Yuma source and documentation * '''Yang Central''' ** [http://www.yang-central.org/ http://www.yang-central.org] ** Free information and tutorials on YANG, free YANG tools for download * '''NETCONF Working Group Wiki Page''' ** [http://trac.tools.ietf.org/wg/netconf/trac/wiki http://trac.tools.ietf.org/wg/netconf/trac/wiki] ** Free information on NETCONF standardization activities and NETCONF implementations * '''NETCONF WG Status Page''' ** http://tools.ietf.org/wg/netconf/ ** IETF Internet draft status for NETCONF documents * '''libsmi Home Page''' ** [http://www.ibr.cs.tu-bs.de/projects/libsmi/ http://www.ibr.cs.tu-bs.de/projects/libsmi/] ** Free tools such as smidump, to convert SMIv2 to YANG * '''YumaWorks''' ** [http://www.yumaworks.com/ http://www.yumaworks.com] ** Offers support, training, and consulting for Yuma. ** Offers YumaPro, a professional version of Yuma that includes concurrency, external database support, sub-agent support, multiple northbound interfaces, and more. API compatible with Yuma. Availability: September, 2012. Licensed. * '''Transpacket''' ** [http://www.transpacket.com/ http://www.transpacket.com] ** Offers Linux based embedded operating system distribution which uses Yuma for configuration and monitoring. ** Offers support, training, and consulting for YANG and netconf. === Mailing Lists === * '''NETCONF Working Group''' ** http://www.ietf.org/html.charters/netconf-charter.html ** Technical issues related to the NETCONF protocol are discussed on the NETCONF WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. * '''NETMOD Working Group''' ** [http://www.ietf.org/html.charters/netmod-charter.html http://www.ietf.org/html.charters/netmod-charter.html] ** Technical issues related to the YANG language and YANG data types are discussed on the NETMOD WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. == Conventions Used in this Document == The following formatting conventions are used throughout this document: {| class="wikitable" border="1" !Convention !Description |- | '''--foo''' | CLI parameter foo |- | '''''' | XML parameter foo |- | '''foo''' | '''yangcli''' command or parameter |- | '''$FOO''' | Environment variable FOO |- | '''$$foo''' | '''yangcli''' global variable foo |- | some text | Example command or PDU |- | some text | Plain text |} = Introduction = [[Image:yuma-tools.png]] Refer to section 3 of the Yuma User Manual for a complete introduction to Yuma. This section focuses on the client and server tools within the Yuma programs. == Intended Audience == This document is intended for users of the Yuma NETCONF client and server programs. It covers the installation of the Yuma packages. = Installation Requirements = The following requirements must be met for Yuma to be installed. == Supported Platforms == There are no binary packages distributed at this time. Binaries can be compiled from source and installed using Autotools/Automake or built as Debian package from source using the Debian package management tools. The build scripts are tested on the following platforms: * Debian 8.0 "jessie" (32 bit x86 and 64-bit AMD) * Ubuntu 12.04.5 LTS (Precise Pangolin) (32 bit x86 and 64-bit AMD) == External Packages == The following programs and libraries need to be available for Yuma to work. === libxml2 === The '''libxml2''' package is needed by the yuma package for some of the XML parsing functions. This package is installed by the default Linux installation process. To build yuma sources, also install the developer version of this package. It is called '''libxml2-dev '''on Debian systems. === libssh2 === The '''libssh2''' package is needed by the yuma package for the '''yangcli''' program to connect to NETCONF servers using the SSH protocol. This package is called '''libssh2-1''' on Ubuntu platforms. This package is '''not''' installed by the default Linux installation process. To build yuma sources, also install the developer version of this package. It is called '''libssh2-1-dev '''on Debian systems. === ncurses === The '''ncurses''' library is needed by the yuma package for some terminal support. This package is installed by the default Linux installation process. It is called '''libncurses5''' on Ubuntu systems. To build yuma sources, also install the developer version of this package. It is called '''libncurses5-dev '''on Debian systems. === zlib === The '''zlib''' library is needed by the yuma package for some compression support, used by other libraries that yuma imports. This package is installed by the default Linux installation process. To build yuma sources, also install the developer version of this package. It is called '''libz-dev '''on Debian systems. === libreadline === The '''libreadline''' library is needed by the yuma package for command line handling. This package is installed by the default Linux installation process. To build yuma sources, also install the developer version of this package. It is called '''libreadline-dev '''on Debian systems. = Getting the source = ~> git clone git://git.code.sf.net/p/yuma123/git yuma123 ~> cd yuma123 = Building and Installation = You can use the Debian/Ubuntu package management tools, RPM or directly use the Autotools build scripts if the platform you have employs neither package manager or you need more flexibility. == Alternative 1: Debian/Ubuntu *.deb package build and installation steps == Check if you have any unmet dependencies: yuma123> dpkg-checkbuilddeps dpkg-checkbuilddeps: Unmet build dependencies: libssh2-1-dev libxml2-dev Install the missing packages: yuma123> sudo apt-get install libssh2-1-dev libxml2-dev Build the *.deb: yuma123> dpkg-buildpackage -rfakeroot -uc -b The generated *.deb package e.g. ../yuma123_2.5-1_i386.deb can be installed: yuma123> sudo dpkg -i ../yuma123_2.5-1_i386.deb == Alternative 2: Autotools build and installation steps== Assuming you have no unresolved dependencies: yuma123> autoreconf -i -f yuma123> ./configure CFLAGS='-g -O0' CXXFLAGS='-g -O0' --prefix=/usr yuma123> make yuma123> sudo make install == Alternative 3: RedHat RPM build and installation == Generate the RPM spec file yuma123> autoreconf -i -f && ./configure Install build dependencies yuma123> yum-builddep rpm/yuma123.spec Create the distribution tar file yuma123> make dist Build the RPMs yuma123> rpmbuild -tb yuma123-2.11*gz Install yuma123> cd ~/rpmbuild/RPMS/x86_64/ yuma123> sudo yum localinstall yuma123-2.11-1.el7.x86_64.rpm yuma123-libyuma2-2.11-1.el7.x86_64.rpm yuma123-netconfd-2.11-1.el7.x86_64.rpm = Installed Files = * '''/usr/bin''' directory contains the following programs: **yangcli **yangrpc-example * '''/usr/sbin''' directory contains the following server programs: ** netconfd ** netconf-subsystem * '''/usr/lib '''directory contains the following files: ** libyumancx.so ** libyumaagt.so ** libyumamgr.so ** libyangrpc.so * '''/usr/lib/yuma''' directory contains the following file: ** libhelloworld.so ** libtoaster.so * '''/usr/share/yuma/modules''' directory contains all the YANG modules: **yang/ **ietf/ **netconfcentral/ **ietf-draft/ **helloworld.yang * '''/usr/share/doc/yuma123''' directory (*.deb only) containing the following files: ** copyright ** changelog.Debian.gz * '''/usr/include/yuma '''directory contains H files needed to compile SIL code so it can be loaded into the server at runtime: ** ncx/*.h ** agt/*.h ** platform/*.h ** yangrpc/*.h = Next Steps = == More Documentation == [[Yuma Quickstart Guide]] [[Yuma User Manual]] [[Yuma netconfd Manual]] [[Yuma yangcli Manual]] [[Yuma Developer Manual]] Each program also has extensive help information available with the''' --help''' CLI parameter. For example: * '''yangcli --help''' * '''netconfd --help''' == Running the Yuma Programs == === yangcli === If you are just using the Yuma client applications, then there is no further mandatory setup required. * If a work directory is used, then the '''$YUMA_HOME '''environment variable needs to be defined. Refer to the user manual for details. * If Yuma is installed in a location other than the default location described above, then the '''$YUMA_INSTALL''' environment variable needs to be defined. Refer to the user manual for details. * The following binary applications are available: ** '''/usr/bin/yangcli''': NETCONF-over-SSH client application === netconfd and netconf-subsystem === The Yuma server does not automatically start running when installed. This will be supported in a future release. The following steps must be taken to start the '''netconfd''' server: * You must modify the '''/etc/ssh/sshd_config''' file, and add the 'netconf' subsystem, as described in the user manual.If the yuma package was installed in a non-default location, then the path to the netconf-subsystem will be different than the example below. The following commands must be present: '''Port 22''' '''Port 830''' '''Subsystem netconf /usr/sbin/netconf-subsystem''' * Start the '''netconfd''' server, as described in the [[Yuma User Manual]] or the [[Yuma Quickstart Guide]]. This can be in the foreground or the background. If it is in the background, then the ''''--log'''' CLI parameter should be provided, as shown below: mydir> /'''usr/sbin/netconfd --log=$HOME/mylog &''' * Restart the SSH server. This is a platform-specific task. Refer to the '''sshd''' manual page for your system for more details. This step may need to be run as root or with the 'sudo' program. Debian/Ubuntu: mydir> sudo /etc/init.d/ssh restart Fedora 12 version: mydir> sudo /etc/rc.d/init.d/sshd restart yuma123_2.14/netconf/doc/yuma_docs/yuma-yangdump-manual.txt0000664000175000017500000025556714770023131024144 0ustar vladimirvladimir
'''Yuma yangdump Manual'''
YANG-Based Unified Modular Automation Tools
YANG Module Compiler
Version yuma123-2.13
== Legal Statements == Copyright 2009 – 2012, Andy Bierman, All Rights Reserved. == Additional Resources == This document assumes you have successfully set up the software as described in the printed document: Yuma Installation Guide Other documentation includes: Yuma Quickstart Guide Yuma User Manual Yuma netconfd Manual Yuma yangcli Manual Yuma yangdiff Manual Yuma Developer Manual There are several sources of free information and tools for use with YANG and/or NETCONF. The following section lists the resources available at this time. === WEB Sites === * '''Netconf Central''' ** [http://www.netconfcentral.org/ http://www.netconfcentral.org/] ** Yuma Home Page *** Free information on NETCONF and YANG, tutorials, on-line YANG module validation and documentation database * '''Yuma SourceFource OpenSource Project''' ** [http://sourceforge.net/projects/yuma/ http://sourceforge.net/projects/yuma/] *** Download Yuma source and binaries; project forums and help * '''Yang Central''' ** [http://www.yang-central.org/ http://www.yang-central.org] ** Free information and tutorials on YANG, free YANG tools for download * '''NETCONF Working Group Wiki Page''' ** [http://trac.tools.ietf.org/wg/netconf/trac/wiki http://trac.tools.ietf.org/wg/netconf/trac/wiki] ** Free information on NETCONF standardization activities and NETCONF implementations * '''NETCONF WG Status Page''' ** http://tools.ietf.org/wg/netconf/ ** IETF Internet draft status for NETCONF documents * '''libsmi Home Page''' ** [http://www.ibr.cs.tu-bs.de/projects/libsmi/ http://www.ibr.cs.tu-bs.de/projects/libsmi/] ** Free tools such as smidump, to convert SMIv2 to YANG * '''YumaWorks''' ** [http://www.yumaworks.com/ http://www.yumaworks.com] ** Offers support, training, and consulting for Yuma. ** Offers YumaPro, a professional version of Yuma that includes concurrency, external database support, sub-agent support, multiple northbound interfaces, and more. API compatible with Yuma. Availability: September, 2012. Licensed. === Mailing Lists === * '''NETCONF Working Group''' ** http://www.ietf.org/html.charters/netconf-charter.html ** Technical issues related to the NETCONF protocol are discussed on the NETCONF WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. * '''NETMOD Working Group''' ** [http://www.ietf.org/html.charters/netmod-charter.html http://www.ietf.org/html.charters/netmod-charter.html] ** Technical issues related to the YANG language and YANG data types are discussed on the NETMOD WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. == Conventions Used in this Document == The following formatting conventions are used throughout this document:
'''Documentation Conventions'''
{| class=wikitable border="1" !
Convention
!
Description
|- | '''--foo''' | CLI parameter foo |- | '''''' | XML parameter foo |- | '''foo''' | '''yangcli''' command or parameter |- | '''$FOO''' | Environment variable FOO |- | '''$$foo''' | '''yangcli''' global variable foo |- | some text | Example command or PDU |- | some text | Plain text |} = yangdump = [[Image:]] == Introduction == The '''yangdump''' program is used to validate YANG files, and convert them to other formats. It is normally used by authors while developing new YANG data models. The compiler and validation engine portion of '''yangdump''' (libyumancx.so) is used in all '''Yuma''' programs. The '''yangdumpcode''' program is the same as '''yangdump''', except that the '''--format '''parameter allows more options to generate code for Yuma applications. The following '''--format '''parameter values are allowing '''yangdumpcode''', but not '''yangdump''': * '''format=c (Experimental)''' ** C file for server instrumentation library for a YANG module * '''format=h (Experimental)''' ** H file for server instrumentation library for a YANG module * '''format=sqldb''' ** SQL database contents for the Yuma WEB docs server for a YANG module * '''format=tg2 (TBD – Not Supported)''' ** Turbogears 2 source code files for Yuma WEB applications for a YANG module === Features === The '''yangdump''' compiler has the following features: * full support for all YANG language constructs. * full support for YANG submodules. * full support for YANG revisions and deviations across an arbitrary set of YANG files. * validates the entire YANG source file, even statements within unused objects. * builds a complete internal representation of the 'cooked' object tree, including deviations, to validate or output the exact data structures that result from a set of YANG files. * Over 100 separate validation checks, errors, and warnings, covering every type of programming mistake that can be made in a YANG file. * Some warnings are fully configurable, and any warning can be suppressed. * Can process any number specific modules and directory trees containing modules at once. * Any number of modules containing deviations can be specified, and any target nodes in all modules affected will be properly patched. * Ignores CVS and subversion directories, if encountered in directory searches. * Attempts to find all errors and warnings possible in the file, instead of stopping on the first error. * Verifies that all YANG statement syntax is correct, according to the specification. * Verifies that all YANG typedef statements are correct: ** The entire typedef chain is checked, until a built-in data type is reached. ** Checks valid combined refinements to named types and built-in types ** Check for valid range statements. ** Check for valid pattern statements. ** Verify the path statement is correct for leafref type statements. ** Verify the default statement is valid. * Compile time loop detection: ** import statement loops. ** include statement loops. ** belongs-to statement references self. ** typedef type statement references self. ** derived type statement loops. ** path statement loops for leafref objects. ** base statement loops for identity statements. ** feature name reference loops for if-feature statements. ** uses statement loop in groupings. * Compile-time duplicate detection: ** import statements ** include statements ** must statements ** if-feature statements ** unique statements ** local typedef of grouping name collision ** verifies that the correct number of instances appear for every YANG sub-statement. ** duplicate sibling node names due to uses or augment statement expansion ** checks multiple refine statements within a uses statement for duplicate refinements to the same target node. ** checks multiple deviate statements within a deviation statement for duplicate alterations to the same target node. ** checks deep within all choice statements to make sure that no accessible object names conflict with sibling nodes that will appear in the value nodes, within a NETCONF database. * Verifies that all default statements are correct for the declared type. ** Only the static properties of the leafref and instance-identifier built-in data types can be validated at compile time. * Verifies that all XPath expressions in must and when statements, even those in groupings, contain only valid XPath and reference valid data nodes. * Verifies all when statements for a given cooked object, even those inherited from uses and augment statements. * Detects namespace statement conflicts across a set of modules. * Detects prefix statement conflicts across a set of modules. * Detects augment statement conflicts. * Checks if any key leafs are more conditional than their parent node. This can occur if any 'when' or 'if-feature' statements apply to the key leaf, but not to the parent. * Detects unused typedefs and groupings. * Checks line length and identifier length. * Checks revision statements present and in correct order. * Detects any top-level mandatory configuration objects: ** leaf with mandatory statement set to true. ** choice with mandatory statement set to true. ** non-presence container with any mandatory descendants. * Checks all refine statements for proper usage, and checks that the resulting set of objects remains valid. Multiple refine statements for the same target will be combined and checked as if there was only one refine statement for each target node. * Checks all deviation statements for proper usage, and checks that the resulting set of objects remains valid. Multiple deviation statements for the same target will be combined and checked as if there was only one deviation statement for each target node. The '''yangdump''' translator has the following features: * Full support for YANG string specification syntax: ** All double quoted string formatting rules. ** Preservation of single-quoted string content. ** All forms of string concatenation. ** Unquoted string without any whitespace. * Generates YIN syntax from the YANG token sequence ** YIN format is the standard XML version of a YANG module * Generates hyper-linked HTML for a set of YANG modules. ** Rich set of configuration parameters to control HTML generation. ** Uses configurable and customizable Cascading Style Sheets (CSS). ** Can generate full WEB page or single
for embedding in a WEB page. ** Can combine all sub-modules into a single conceptual module. ** Objects tagged as''' ncx:hidden''' will be ignored in the HTML output. * Generates canonical YANG (all statements in the same order, etc.): ** Combines refine and deviation statements for the same target. ** Combines range statement components into canonical form. ** Generates consistent (configurable) indentation for all statements. ** Objects tagged as''' ncx:hidden''' will be ignored in the YANG output. * Generates SQL statements for populating the netconf-central database with quick lookup information about a set of YANG modules. * Generates C source code stubs for '''netconfd''' server instrumentation libraries, for dynamic loading of a YANG module. * Generates informational reports on the contents of a YANG file: ** Imported modules (dependencies). ** Exported symbols (exports). ** Object name tree (identifiers or tree-identifiers) ** Module revision date (modversion) ** YANG module metrics reports (stats and totals) The following features are not yet available, but planned for a future release: * naming conflicts report * preserve and translate YANG comments * preserve or reformat YANG string concatenation for '''–objview=cooked''' ** YANG and HTML output will preserve original string token sequences for '''–objview=raw''' (the default). Most text string fields are preserved if '''–format=html''' or''' --format=yang''' === Starting yangdump === The current working directory in use when '''yangdump''' is invoked is important. It is most convenient to run '''yangdump''' from a work directory, rather than the installation directory or within the module library. The '''yangdump''' program can be invoked several ways: * To get the current version and exit: '''yangdump –version''' * To get program help and exit: '''yangdump –help''' '''yangdump --help –brief''' '''yangdump --help –full''' The default parameter for the '''yangdump''' CLI is the --module parameter. If an unknown parameter is given and it could be a valid module parameter, then it will be treated as an instance of this parameter. * To validate a single YANG module named 'foo', the following command lines are equivalent: '''yangdump foo''' '''yangdump –module=foo''' * To validate the './test1' and './test2' directory subtrees as an entire set of modules, and save the output to 'mylogfile': '''yangdump --subtree=test1 --subtree=test2 –logfile=mylogfile''' * To get all the configuration parameters from a text file named '~/yangdump-project1.conf': '''yangdump –config=~/yangdump-project1.conf''' * To generate YANG HTML documentation files in the '~/work' directory (with the default naming scheme) for all the YANG modules in the './test1' directory subtree: '''yangdump --subtree=test1 --output=~/work --format=html –defnames=true''' === Stopping yangdump === There is no interactive mode for '''yangdump''', so there is no need for a command to exit the program. The Control C character sequence can be used to cancel the '''yangdump''' processing in progress. However, this will leave any partially completed output files in place. === Configuration Parameter List === The following configuration parameters are used by '''yangdump'''. Refer to the CLI Reference for more details.
'''yangdump CLI Parameters'''
{| class=wikitable border="1" |
'''parameter'''
|
'''description'''
|- | --config | Specifies the configuration file to use for parameters. |- | --datapath | Sets the data file search path. |- | --defnames | Specifies if the default naming scheme is used for any output files. |- | --dependencies | Generate the module dependencies report. |- | --deviation | Species one or more YANG modules to load as deviations. |- | --exports | Generate the module exports report. |- | --feature-code-default | Specifies if a feature should use static or dynamic code by default |- | --feature-disable | Leaf list of features to disable |- | --feature-dynamic | Specifies a feature that uses dynamic code |- | --feature-enable | Specifies a feature that should be enabled |- | --feature-enable-default | Specifies if a feature should be enabled or disabled by default |- | --feature-static | Specifies a feature that uses static code |- | --format | Specifies the type of translation format that should be used for the output. |- | --help | Get context-sensitive help, then exit. |- | --help-mode | Adjust the help output (--brief, or --full). |- | --html-div | For HTML translation, controls whether to generate a
element instead of a complete HTML document. |- | --html-toc | For HTML translation, controls the table of contents that is gnerated. |- | --identifiers | Generate the module object identifiers report. |- | --indent | Specifies the indent count to use when writing data. |- | --log | Specifies the log file to use instead of STDOUT. |- | --log-append | Controls whether a log file will be reused or overwritten. |- | --log-level | Controls the verbosity of logging messages. |- | --modpath | Sets the module search path. |- | --module | Specifies one or more YANG modules to load upon startup. |- | --modversion | Generate the module version report. |- | --subdirs | Controls whether sub-directories will be searched during file searches. |- | --versionnames | Controls whether the revision date is used in YANG module file names. |- | --objview | Specifies whether raw or cooked objects will be generated during HTML and YANG translation. |- | --output | Specifies where output files should be generated. |- | --runpath | Sets the executable file search path. |- | --show-errors | Print all the error and warning messages, then exit. |- | --simurls | Controls how URL parameters are generated during HTML file output. |- | --stats | Control YANG usage statistics reporting for each input module. |- | --subdirs | Controls whether sub-directories are searched for YANG files. |- | --subtree | Specifies one or more directory subtrees to search for YANG modules. |- | --tree-identifiers | Generate the module object identifier local-names report in tree format. |- | --totals | Controls statistics summary reporting for a set of input modules. |- | --unified | Controls whether to combine submodules into the main module in the translation output. |- | --urlstart | Specifies the start of URLs to use during HTML file generation. |- | --version | Prints the program version and then exit. |- | --warn-idlen | Controls how identifier lengths are checked. |- | --warn-linelen | Controls how line lengths are checked. |- | --warn-off | Suppresses the specified warning number. |- | --xsd-schemaloc | Generate schemaLocation attributes during XSD translation. |- | --yuma-home | Specifies the '''$YUMA_HOME''' project root to use when searching for files. |} == Validating YANG Files == The '''yangdump''' program will always validate the modules specified in the configuration parameters, unless one of the quick exit modes is requested (--version or --help). If a valid -'''-format '''parameter is present, then some sort of translation will also be attempted. The modules or submodules to validate are specified with the''' --module''' and/or '''--subtree''' configuration parameters. All sub-modules that are included, and all modules that are imported, are also validated. This is done in the order the import or include statements are encountered. Errors and warnings that occur in included submodules or imported modules may be repeated, if it is used more than once within the requested set of files to validate. The '''netconfd''' server will refuse to run if any of its core YANG modules contain any errors, as reported by the '''yangdump''' parser. The''' --deviation''' parameter is used to specify any YANG module files that contain YANG deviation statements, which may apply to any of the modules specified with the '''--module''' or '''--subtree''' parameters. Modules parsed as deviation files are not validated. Imported modules are not actually read, and therefore not validated either. A module can appear in the deviation list and the module or subtree module list. The deviation statements will simply be processed separately from the other YANG statements found in the file. If all the deviation statements for a module appear in the same module as the objects that are the target(s) of the deviations, then the '''--deviation''' parameter does not need to be used. There is no way to specify the exact revision date associated with each file, at this time. The module search path needs to be configured such that the module search algorithms will find the appropriate versions. This only applies if the revision-date statement is not present in the import statement, of course. Otherwise, only the specified revision-date will be used, or an error will be generated if the exact import file was not found. === Yangdump Validation Messages === A error is generated, and the error count for the module is incremented, if any violation of the YANG standard is detected. A warning is generated, and the warning count for the module is incremented, if: * any suspected misuse of the YANG standard is detected. * any YANG statements which have no affect are detected. * any duplicate statements which may conflict with previous statements are detected. * any violation of a YANG usage guideline is detected. * any statements which may cause operational conflicts, due to duplicate values from another module is detected. The module prefix or an augment statement which adds a node with the same name are examples of this type of operational conflict. An informational message is generated if any potentially interesting conditions or abnormality is detected. A debugging message is generated if available, to track the internal behavior of the '''yangdump''' parser. The default '''--log-level''' configuration parameter value is 'info'. It is strongly suggested that the log-level not be set to 'off' or 'error'. Instead, set the log level to at least 'warning', and use the '''--warn-idlen''', '''----warn-linelen''', and '''--warn-off''' configuration parameters to suppress or modify specific warning messages. Generally, a context-specific error message is generated, followed by a structured error message. A context-sensitive error message will begin "Error: ....". A structured error message will begin with a line in the following format: '''::: error():''' where: * module: module or submodule name === Validation Example === The following example shows the error messages that are generated for the 'testloops.yang' file, located in the modules/test sub-directory. *** Generated by yangdump 0.9.7.452 at 2009-08-27T23:56:21Z Error: import 'testloops' for current module testloops.yang:6.5: error(327): import loop Error: include 'testloops' for current submodule testloops.yang:10.5: error(328): include loop Error: typedef 'typeloop' cannot use type 'typeloop' testloops.yang:35.8: error(325): definition loop detected Error: named type loops with type 'typeloop2' on line 43 testloops.yang:47.8: error(325): definition loop detected Error: uses of 'grouploop' is contained within that grouping, defined on line 50 testloops.yang:51.8: error(325): definition loop detected Error: grouping 'grouploop2' on line 54 loops in uses, defined in module testloops, line 63 testloops.yang:54.5: error(325): definition loop detected Error: leafref path in leaf LR1 loops with leaf LR3 testloops.yang:16.5: error(359): leafref path loop Error: leafref path in leaf LR2 loops with leaf LR1 testloops.yang:22.5: error(359): leafref path loop Error: leafref path in leaf LR3 loops with leaf LR2 testloops.yang:28.5: error(359): leafref path loop *** /home/andy/modules/test/testloops.yang: 9 Errors, 0 Warnings == Translating YANG to Other Formats == The '''yangdump''' program can be used to translate YANG files to other formats, using the '''--format''' parameter. This section describes the available translation modes. In all cases: * the '''--subtree '''or '''--module '''parameter is required to specify the input files. Any number of these parameters can be entered, given in any order. * the '''--deviation '''parameter will affect how objects are generated, if '''--objview=cooked'''. * the''' --output''' parameter is usually specified to cause the files to be copied to a different directory. If this parameter is not present, then the current directory will be used for generation of output files. The default is either STDOUT, or to the current directory if '''--defnames '''is set to 'true'. * the '''--indent''' parameter can be used to specify the number of spaces to indent each new nest level. The default value is 3 spaces. * the '''--defnames''' parameter can be used to force the default naming scheme. For many translation modes, this parameter is the assumed default, and cannot be changed. * the '''--urlstart''' parameter can be used to specify the string to start all URLs, if URLs are generated in the output. === YIN Format === The standard YIN format can be generated with the '''--format=yin''' parameter value. The YIN representation of a YANG module is an XML instance document consisting of a or a element. The set of YANG prefixes used in the module will be used as the XML prefixes available in the document. These namespace attributes are added to the top-level element for the module prefix and any imported modules. The following example shows the XML instance document that is generated for '''--module=test4''' and –'''format=yin:''' name="test4" xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:t4="http://netconfcentral.org/ns/test4"> Initial revision. test type === HTML Translation === xHTML 1.0 files can be generated by using the '''--format=html '''parameter value. An HTML version of a YANG file will contain color-coded YANG tokens to make the file easier to read. There will also be links created whenever possible, for statements which reference typedefs, groupings, extensions, etc. The following configuration parameters are available to control some of the details: * '''--html-div''': If 'true', create a single
element that can be integrated into other WEB pages. The default is 'false', to create an entire WEB page ( element). * '''--html-toc''': controls how (or if) a table of contents section is generated. The default is to create a drop-down menu type ToC, which requires that Javascript is enabled in the browser. * -'''-objview''': controls whether raw (with uses, augments, refine, and deviation statements) or cooked (final object tree without uses, etc). The default is the 'raw' (original) view. * '''--simurls''': controls how URLs with parameter fragments are generated. The default is 'false', which is to use traditional encoded parameters. * '''--unified''': controls whether submodules are combined into the main module, or if the 'include' statements are left in place. The default is 'false', to treat each file separately, and not expand any include statements. * -'''-urlstart''': specifies the string that starts every generated URL in all A and HREF attributes. There is no default for this parameter. The following example shows the HTML that is generated for --module=test4, using default values for all HTML generation parameters: "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> YANG Module test4 Version 2009-09-09

test4.yang


 

    module test4 { 

       yang-version 1; 

       namespace "http://netconfcentral.org/ns/test4"; 

       prefix "t4"; 

       revision "2009-09-09" { 
          description "Initial revision."
       ; 
       }

       typedef aa-type { 
          type int32 { 
             range "1..10 | 20..30"; 
          } 
          description "test type"; 
       } 

       container a { 
          config "true"; 
          leaf-list aa { 
             type aa-type; 
             max-elements "5"; 
             ordered-by "system"; 
          } 
       }  // container a 

       leaf b { 
          type leafref { 
             path "../a/aa"; 
          } 
          config "true"; 
       } 

       list c { 
          key "x"; 
          config "true"; 
          ordered-by "system"; 
          leaf x { 
             type uint16; 
          } 

          leaf y { 
             type instance-identifier; 
          } 
       }  // list c 
    }  // module test4 
 
[[Image:]]The following picture shows how this WEB page looks in Firefox 3.0 on Ubuntu: === XSD Translation === XSD 1.0 files can be generated by using the '''--format=xsd '''parameter value. XSD format may be deprecated in a future release because the official YANG translation format is DSDL. The XSD translation of a YANG file will contain an XSD representation of the cooked objects in the module. The top-level typedefs and objects will be translated. Local typedefs and groupings, extensions, identities, and other YANG constructs are not translated at this time. The '''--xsd-schemaloc''' parameter can be used to specify the ''schemaLocation'' attribute value for the XSD. The data structures generated during XSD translation will be extensions to the NETCONF XSD defined in RFC 4741. * A YANG '''rpc''' statement will be translated to an element. * A YANG '''notification''' statement will be translated to an element. * A YANG '''leaf''' and''' leaf-list''' statements will be translated to a , and corresponding definition. * A YANG '''container''' or '''list''' statement will be translated to a , and corresponding definition. * A YANG choice statement will be translated to a element. * A YANG top-level '''typedef''' statement will be translated to a element. * A YANG '''range'''-statement will be translated to elements or a union of elements if the YANG range is non-contiguous. * A YANG '''pattern''' statement will be translated into a element. * Multiple YANG '''patterns''' within a single typedef will be translated into nested inline elements, to preserve the AND llogic, instead of the XSD OR logic. * YANG '''description''' and '''reference''' statements are translated to elements within an element. * YANG '''header''' constructs are translated into elements that are defined in '''yuma-ncx.yang.''' The following example shows the translation of test/test4.yang. The command options used are '''--format=xsd '''and –'''module=test4'''. xmlns="http://netconfcentral.org/ns/test4" targetNamespace="http://netconfcentral.org/ns/test4" elementFormDefault="qualified" attributeFormDefault="unqualified" xml:lang="en" version="2009-09-09" xmlns:ncx="http://netconfcentral.org/ncx" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> Converted from YANG file 'test4.yang' by yangdump version 0.9.7.471 Module: test4 Version: 2009-09-09 /home/andy/swdev/yuma/trunk/netconf/modules/test/test4.yang 2009-09-09 Initial revision. test type true maxOccurs="5"> system maxOccurs="unbounded" abstract="true"/> true true system maxOccurs="unbounded" abstract="true"/> === SQL Translation === SQL translation is reserved for future use. The '''--format=sql '''enumeration is not supported yet. === SQL Documentation Database Translation === SQL documentation database translation for the Netconf Central documentation database can be generated using the '''--format=sqldb '''parameter value. Refer to the Yuma Developer Guide for more details. === Canonical YANG Translation === Canonical YANG files can be generated by using the --format=yang parameter value. In this translation mode, all YANG constructs are generated in the same order, with the same indentation. The order used is the order defined in the YANG specification ABNF. The order is somewhat arbitrary, but the purpose of this translation mode is to generate consistent documentation. Normally, the order of top-level statements within a module is preserved in the canonical YANG translation. Only the sub-statements within the top-level statements may be reordered. Child objects are never reordered, just the sub-statements within them. Range statements will be converted to canonical form. If there are contiguous range parts, then they will be combined. Example range statement: range "1 .. 10 | 11 | 12 .. 20"; Canonical form for this example: range "1 .. 20"; If the '''--unified''' parameter is set to 'true', then all statements of a similar type will be grouped together, from all sub-modules included by the main module. The''' ncx:hidden''' extension will cause objects containing this extension to be omitted during translation. Comments are not preserved at this time. This will be addressed in a future release. === Copy and Rename YANG Files === Exact copies of a YANG file can be copied to another directory and renamed to the default naming scheme, using the '''--format=copy '''parameter value. Only YANG files that have no errors will be copied. If the validation phase produces a non-zero error count, then the file will not be copied and renamed. The '''--defnames''' parameter is set to 'true' in this mode. The default YANG file names will be created for each input file. Example file ''foo.yang'': module foo { namespace http://example.com/ns/foo; prefix foo; // header skipped revision 2009-02-03 { description "Initial version"; } } Example renamed file, if '''--output=~/workdir''': * If '''--versionnames=true:''' ** ~/workdir/foo.2009-02-03.yang * If '''--versionnames=false:''' ** ~/workdir/foo.yang === Agent Instrumentation H File Generation === There are 3 different parameters for generating SIL developer H files: {| class=wikitable border="1" !
Parameter
!
Description
|- | --format=h | Generate combined User and Yuma SIL H file |- | --format=uh | Generate split User SIL H file |- | --format=yh | Generate split Yuma SIL H file |} These''' '''parameter values are only relevant for Yuma server developers. Refer to the Yuma Developer Guide for more details. === Agent Instrumentation C File Generation === There are 3 different parameters for generating SIL developer C files: {| class=wikitable border="1" !
Parameter
!
Description
|- | --format=c | Generate combined User and Yuma SIL C file |- | --format=uc | Generate split User SIL C file |- | --format=yc | Generate split Yuma SIL C file |} These''' '''parameter values are only relevant for Yuma server developers. Refer to the Yuma Developer Guide for more details. = CLI Reference = The '''yangdump''' program uses command line interface (CLI) parameters to control program behavior. The following sections document all the Yuma CLI parameters relevant to this program, in alphabetical order. == --config == The '''--config''' parameter specifies the name of a Yuma configuration file that contains more parameters to process, in addition to the CLI parameters. Refer to the 'Configuration Files' section for details on the format of this file.
'''--config parameter'''
{| class=wikitable border="1" | Syntax | string: complete file specification of the text file to parse for more parameters. |- | Default: | /etc/yuma/.conf |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump testmod \ --config=~/testconf.conf |} == --defnames == The '''--defnames '''parameter causes the program output file to be named with the default name for the format, based on the input module name and revision date. Refer to the section on generating WEB documentation for details on specific file formats for HTML output. If the '''--output''' parameter is present and represents an existing directory, then the default filename will be created in that directory, instead of the current directory. This parameter is ignored if the '''--format''' parameter is missing.
'''--defnames parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | FALSE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --defnames=true \ --subtree=~/workdir/ \ --format=html |} == --dependencies == The '''--dependencies''' parameter causes information to be reported for the symbols that this [sub]module imports from other modules. The following information is reported for each dependency: * module name * revision date Example report for module 'yangdump': dependencies: import ncx 2009-06-12 import ncx-app-common 2009-04-10 import ncxtypes 2008-07-20
'''--dependencies parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --dependencies \ --module=test4 |} == --deviation == The '''--deviation''' parameter is a leaf-list of modules that should be loaded automatically when the program starts, as a deviation module. In this mode, only the deviation statements are parsed and then made available later when the module that contains the objects being deviated is parsed. The deviations must be known to the parser before the target module is parsed. This parameter is used to identify any modules that have deviation statements for the set of modules being parsed (e.g., '''--module''' and''' --subtree''' parameters). A module can be listed with both the '''--module''' and '''--deviation''' parameters, but that is not needed unless the module contains external deviations. If the module only contains deviations for objects in the same module, then the''' --deviation '''parameter does not need to be used. The program will attempt to load each module in deviation parsing mode, in the order the parameters are entered. For the '''netconfd''' program, If any modules have fatal errors then the program will terminate. For the '''yangdump '''and''' yangcli''' programs, each module will be processed as requested.
'''--deviation parameter'''
{| class=wikitable border="1" | Syntax | module name or filespec |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | netconfdyangcliyangdump |- | Example: | yangcli \ --module=test1 \ --deviation=test1_deviations |} == --exports == The '''--exports''' parameter causes information to be reported for the symbols that this [sub]module exports to other modules. The exports for the entire module are printed, unless the specified input file is a YANG submodule. In that case, just the exports in the submodule are reported. It includes the following information, generated in this order: * [sub]module name * version * source filespec * namespace (module only) * prefix (module only) * belongs-to (submodule only) * typedefs * groupings * objects * RPC operations * notifications * extensions * features
'''--exports parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --exports \ --module=test3 |} == --feature-code-default == The '''--feature-code-default''' parameter controls how '''yangdump''' will generate C code for YANG features by default. If 'dynamic', then by default, features can be loaded at run-time, and objects with if-feature statements will be available in case the feature is enabled. If 'static', then by default, features are set at compile-time, and any disabled objects will be removed at load-time. If a '''--feature-dynamic''' or '''--feature-static''' parameter is present for a specific feature, then this parameter will be ignored for that feature.
'''--feature-code-default parameter'''
{| class=wikitable border="1" | Syntax | enum (dynamic or static) |- | Default: | dynamic |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump --format=c \ --feature-code-default=static \ --module=test |} == --feature-disable == The '''--feature-disable''' parameter directs all programs to disable a specific feature. This parameter is a formatted string containing a module name, followed by a colon ':', followed by a feature name, e.g., test:feature1 It is an error if a '''--feature-enable''' and '''--feature-disable''' parameter specify the same feature. Parameters for unknown features will be ignored.
'''--feature-disable parameter'''
{| class=wikitable border="1" | Syntax | formatted string (module:feature |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | yangcliyangdiffyangdumpnetconfd |- | Example: | yangdump --format=c \ --feature-disable=test:feature1 \ --module=test |} == --feature-dynamic == The '''--feature-dynamic''' parameter controls how '''yangdump''' will generate dynamic code for a specific feature. This parameter is a formatted string containing a module name, followed by a colon ':', followed by a feature name, e.g., test:feature1 It is an error if a '''--feature-static''' and '''--feature-dynamic''' parameter specify the same feature. Parameters for unknown features will be ignored.
'''--feature-dynamic parameter'''
{| class=wikitable border="1" | Syntax | formatted string (module:feature |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | yangdump |- | Example: | yangdump --format=c \ --feature-dynamic=test:feature1 \ --module=test |} == --feature-enable == The '''--feature-enable''' parameter directs all programs to enable a specific feature. This parameter is a formatted string containing a module name, followed by a colon ':', followed by a feature name, e.g., test:feature1 It is an error if a '''--feature-disable''' and '''--feature-enable''' parameter specify the same feature. Parameters for unknown features will be ignored.
'''--feature-enable parameter'''
{| class=wikitable border="1" | Syntax | formatted string (module:feature |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | yangcliyangdiffyangdumpnetconfd |- | Example: | yangdump --format=c \ --feature-enable=test:feature1 \ --module=test |} == --feature-enable-default == The '''--feature-enable-default''' parameter controls how '''yangdump''' will generate C code for YANG features by default. If 'true', then by default, features will be enabled. If 'false', then by default, features will be disabled. If a '''--feature-enable''' or '''--feature-disable''' parameter is present for a specific feature, then this parameter will be ignored for that feature.
'''--feature-enable-default parameter'''
{| class=wikitable border="1" | Syntax | boolean (true or false) |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcliyangdiffyangdumpnetconfd |- | Example: | netconfd \ --feature-enable-default=false |} == --feature-static == The '''--feature-static''' parameter controls how '''yangdump''' will generate static code for a specific feature. This parameter is a formatted string containing a module name, followed by a colon ':', followed by a feature name, e.g., test:feature1 It is an error if a '''--feature-static''' and '''--feature-dynamic''' parameter specify the same feature. Parameters for unknown features will be ignored.
'''--feature-static parameter'''
{| class=wikitable border="1" | Syntax | formatted string (module:feature |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | yangdump |- | Example: | yangdump --format=c \ --feature-static=test:feature1 \ --module=test |} == --format == The '''--format''' parameter controls the type of '''yangdump''' conversion desired, if any. Yuma SIL developers should refer to the '''make_sil_dir''' script to generate SIL code instead of using the '''yangdump''' command directly for code generation formats (c, h, uc, uh, yc, yh). If this parameter is missing, then no translation will be done, but the module will be validated, and any requested reports will be generated. The following values are supported: * '''yin''' ** Generate standard YIN format (XML instance document) * '''xsd''' ** XSD 1.0 translation . ** Data model XSD can be integrated with the with the NETCONF protocol XSD in RFC 4741. * '''html''' ** XHTML 1.0 translation. * '''yang''' ** Canonical YANG translation . * '''copy''' ** Copy with a new name, in canonical module naming format. * '''sql''' ** TBD: Generate a module specific SQL template ** This option is reserved for future use. * '''sqldb''' ** Generate module specific SQL data for the netconfcentral.sql template. * '''h''' ** Generate a module specific '''netconfd''' agent instrumentation combined SIL H file. * '''c''' ** Generate a module specific '''netconfd''' agent instrumentation combined SIL C file. * '''uh''' ** Generate a module specific '''netconfd''' agent instrumentation User SIL H file. * '''uc''' ** Generate a module specific '''netconfd''' agent instrumentation User SIL C file. * '''yh''' ** Generate a module specific '''netconfd''' agent instrumentation Yuma SIL H file. * '''yc''' ** Generate a module specific '''netconfd''' agent instrumentation Yuma SIL C file.
'''--format parameter'''
{| class=wikitable border="1" | Syntax | enumeration (xsd, sql, sqldb, html, yang, copy, h, c, uc, uh, yc, yh)) |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump test1 test2 \ --format=xsd \ --defnames=true \ --output=~/workdir |} == --help == The '''--help''' parameter causes program help text to be printed, and then the program will exit instead of running as normal. This parameter can be combined with the '''--help-mode''' parameter to control the verbosity of the help text. Use '''--brief''' for less, and '''--full '''for more than the normal verbosity. This parameter can be combined with the '''--version''' parameter in all programs. It can also be combined with the '''--show-errors''' parameter in '''yangdump'''. The program configuration parameters will be displayed in alphabetical order, not in schema order.
'''--help parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump --help |} == --help-mode == The '''--help-mode''' parameter is used to control the amount of detail printed when help text is requested in some command. It is always used with another command, and makes no sense by itself. It is ignored unless used with the '''--help''' parameter.
'''--help-mode parameter'''
{| class=wikitable border="1" | Syntax | choice of 3 empty leafs '''--brief --normal --full''' |- | Default: | normal |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump --help --help-mode=full |} * '''default choice: '''normal * '''choice help-mode''' ** '''brief''' *** type: empty *** This parameter specifies that brief documentation mode should be used. ** '''normal''' *** type: empty *** This parameter specifies that normal documentation mode should be used. ** '''full''' *** type: empty *** This parameter specifies that full documentation mode should be used. == --html-div == The''' --html-div''' parameter controls whether '''yangdump''' HTML translation will generate a single
element, or an entire HTML document. If HTML translation is requested, then setting this parameter to 'true' will cause the output to be a single
element, instead of an entire HTML file. If it is not requested ('''--forma'''t=html), then this parameter is ignored. This parameter allows the HTML translation to be easily integrated within more complex WEB pages, but the proper CSS definitions need to be present for the HTML to render properly. It is suggested only HTML experts use this parameter. Refer to the following stylesheet file for more details: [http://www.netconfcentral.org/static/css/style.css http://www.netconfcentral.org/static/css/style.css] The default filename extension will be '.div' instead of '.html' if this parameter is present. The contents will be well-formed XHTML 1.0, but without any namespace declarations.
'''--html-div parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | FALSE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --html-div=true --format=html --module=test |} == --html-toc == The''' --html-toc''' parameter controls how '''yangdump''' HTML translation will generate a table of contents for the HTML document. If HTML translation is requested, then this parameter will cause the output to contain a bullet list TOC, a drop-down menu TOC, or none. This option has no effect unless the '''--format'''=html parameter is also present. The following values are supported: * '''none''' ** No TOC is generated. * '''plain''' ** A plain list-based TOC is generated * '''menu''' ** A suckerfish (Javascript) drop-down menu will be generated. ** This is the default option. Note that if the 'menu' enumeration is used, then the following javascript file must be available to the WEB server which is hosting the output HTML file: '''http://www.netconfcentral.org/static/javascript/suckerfish.js'''
'''--html-toc parameter'''
{| class=wikitable border="1" | Syntax | enumeration (none, plain, menu) |- | Default: | menu |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --html-toc=plain --format=html --module=test |} == --identifiers == The '''--identifiers''' parameter causes information to be object identifier strings to be reported for the object, RPC operation, and notification definitions within the input module(s). The following information is reported for each identifier: * object type (e.g., leaf, container, rpc) * absolute XPath expression for the object Example report for module 'yuma-mysession': identifiers: rpc /get-my-session container /get-my-session/output leaf /get-my-session/output/indent leaf /get-my-session/output/linesize leaf /get-my-session/output/with-defaults container /get-my-session/input rpc /set-my-session container /set-my-session/input leaf /set-my-session/input/indent leaf /set-my-session/input/linesize leaf /set-my-session/input/with-defaults container /set-my-session/output
'''--identifiers parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --identifiers \ --module=yuma-mysession |} == --indent == The '''--indent''' parameter specifies the number of spaces that will be used to add to the indentation level, each time a child node is printed during program operation.
'''--indent parameter'''
{| class=wikitable border="1" | Syntax | uint32 (0 .. 9) |- | Default: | 2 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump --indent=4 |} == --log == The '''--log''' parameter specifies the file name to be used for logging program messages, instead of STDOUT. It can be used with the optional''' --log-append''' and '''--log-level''' parameters to control how the log file is written.
'''--log parameter'''
{| class=wikitable border="1" | Syntax | string: log file specification |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump --log=~/test.log& |} == --log-append == The '''--log-append''' parameter specifies that the existing log file (if any) should be appended , instead of deleted. It is ignored unless the '''--log''' parameter is present.
'''--log-append parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump --log-append \ --log=~/test.log& |} == --log-level == The '''--log-level''' parameter controls the verbosity level of messages printed to the log file or STDOUT, if no log file is specified. The log levels are incremental, meaning that each higher level includes everything from the previous level, plus additional messages. There are 7 settings that can be used: * '''none''': All logging is suppressed. Use with extreme caution! * '''error''': Error messages are printed, indicating problems that require attention. * '''warn''': Warning messages are printed, indicating problems that may require attention. * '''info''': Informational messages are printed, that indicate program status changes. * '''debug''': Debugging messages are printed that indicate program activity. * '''debug2''': Protocol debugging and trace messages are enabled. * '''debug3''': Very verbose debugging messages are enabled. This has an impact on resources and performance, and should be used with caution.
'''log-level parameter'''
{| class=wikitable border="1" | Syntax | enumeration: '''off error warn info debug debug2 debug3 debug4''' |- | Default: | --info (--debug for DEBUG builds) |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump --log-level=debug \ --log=~/test.log& |} == --modpath == The '''--modpath''' parameter specifies the YANG module search path to use while searching for YANG files. It consists of a colon (':') separated list of path specifications, commonly found in Unix, such as the '''$PATH''' environment variable. This parameter overrides the '''$YUMA_MODPATH''' environment variable, if it is present.
'''--modpath parameter'''
{| class=wikitable border="1" | Syntax | string: list of directory specifications |- | Default: | '''$YUMA_MODPATH''' environment variable |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump \ --modpath=â€~/testmodules:~/modules:~/trunk/netconf/modules†\ --module=~/test42.yang |} == --module == The '''--module''' parameter is a leaf-list of modules that should be loaded automatically when the program starts. The program will attempt to load each module in the order the parameters were entered. For the '''netconfd''' program, If any modules have fatal errors then the program will terminate. For the '''yangdump''' program, each module will be processed as requested.
'''--module parameter'''
{| class=wikitable border="1" | Syntax | module name or filespec |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | netconfdyangcliyangdump |- | Example: | yangcli \ --module=test1 \ --module=test2 |} == --modversion == The '''--modversion''' parameter causes the module name and revision date to be displayed for each module specified in the input. Example output for module 'test': modversion: module test 2009-06-10
'''--modversion parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --modversion \ --module=test |} == --objview == The '''--objview''' parameter specifies how objects will be generated during translation, for HTML and canonical YANG file translations. The enumeration values are: * '''raw''' ** output includes augment and uses clauses, not the expanded results of those clauses. * '''cooked''' ** output does not include augment or uses clauses, just the objects generated from those clauses. The default mode is the 'raw' view. XSD output is always 'cooked', since refined groupings and locally-scoped definitions are not supported in XSD. This parameter is not implemented for other values of the '''--format '''parameter.
'''--objview parameter'''
{| class=wikitable border="1" | Syntax | enumeration (raw, cooked) |- | Default: | raw |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --objview=cooked \ --module=test \ --format=html |} == --output == The '''--output''' parameter specifies where the output files generated by the program will be stored. * The default is STDOUT if this parameter is not specified and the '''--defnames''' parameter is set to 'false'. * If this parameter represents an existing directory, then the '''--defnames''' parameter will be set to 'true' by default. * If this parameter represents a file name, then the '''--defnames''' parameter will be ignored, and all translation output will be directed to the specified file. * If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path * If the target specified in this parameter '''does not '''exist: ** If there is only one file to be output, then this parameter is used as the file name. ** If there are multiple files to be output, then this parameter is used as a directory name. A new directory will be created, if it is needed. * If the target specified in this parameter '''does''' exist: ** If there is only one file to be output: *** If the existing target is also a file, then the current file is over-written. *** If the existing target is a directory, then the output file will be created in this directory. ** If there are multiple files to be output: *** If the existing target is a file, then an error will be generated instead of the output files. *** If the existing target is a directory, then the output files will be created in the specified directory. * Use a trailing path separator character to force this parameter value to be treated as a path specification (e.g., '~/workfiles/'). * This parameter is ignored if the '''--format''' parameter is missing.
'''--output parameter'''
{| class=wikitable border="1" | Syntax | string (path or file specification) |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdumpyangdiff |- | Example: | yangdump \ --output=~/html-files --subtree=testfiles \ --format=html |} == --show-errors == The '''--show-errors''' parameter causes the yangdump program to list all the error and warning numbers, and the default message for each one. The 3 digit number, followed by the message string, will be printed 1 per line. After this is done, the '''yangdump''' program will exit. This parameter can be combined with the '''--help''' parameter. The '''--version '''parameter has no affect if this parameter is present. The program version string will be printed in both cases. The NETCONF error-tag values are used directly when no other error number is appropriate. These error numbers are as follows: 257 resource in use 258 invalid value 259 too big 260 missing attribute 261 bad attribute 262 unknown attribute 263 missing element 264 bad element 265 unknown element 266 unknown namespace 267 access denied 268 lock denied 269 resource denied 270 rollback failed 271 data exists 272 data missing 273 operation not supported 274 operation failed 275 partial operation
'''-show-errors parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump --show-errors |} == --simurls == The '''--simurls''' parameter specifies how URL strings are generated for HTML links. If HTML translation is requested, then setting this parameter to 'true' will cause the format of URLs within links to be generated in simplified form, for WEB development engines such as CherryPy, which support this format. * Normal URL format (false): ** http://example.com/mymodule.html?parm1=foo&parm2=bar#frag * Simplified URL format (true): ** '''http://example.com/mymodule/foo/bar#frag '''
'''--simurls parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | FALSE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --simurls=true \ --format=html \ --module=test |} == --stats == The '''--stats''' parameter is used to request a YANG usage statistics report. See the '''--totals''' parameter to control statistics totals reporting, when used with this parameter. Statistics are not normally collected. This must be enabled by setting this parameter to a value other than 'none' (the default). Any other value will cause statistics to be collected and reported after each input module is validated. The verbosity of the statistics report is controlled with this parameter. Each enumeration includes all the previous statistics (from lower enumerations), plus additional statistics. The following table describes the counters that are displayed at each minimum '''--stats''' enumeration value. {| class=wikitable border="1" !
counter
!
level
!
description
|- | Complexity score | brief | Numeric score characterizing the level of implementation complexity for the module. A higher number indicates a more complex module. |- | Total nodes | brief | The total number of object nodes (data, notification, rpc) in the module. * The Config nodes + Non-config nodes should equal this counter. * The Mandatory nodes + Optional nodes should equal this counter. |- | Extensions | basic | The number of extension statements. |- | Features | basic | The number of feature statements. |- | Groupings | basic | The number of exported groupings. |- | Typedefs | basic | The number of exported typedefs. |- | Deviations | basic | The number of deviation statements. |- | Top Data Nodes | basic | The number of top-level data nodes. |- | Config nodes | basic | The number of configurable nodes. |- | Non-config nodes | basic | The number of non-configurable nodes. |- | Mandatory nodes | advanced | The number of mandatory nodes. |- | Optional nodes | advanced | The number of optional nodes. |- | Notifications | advanced | The number of notification statements. |- | Rpcs | advanced | The number of rpc statements. |- | Rpc inputs | advanced | The number of RPC input statements. |- | Rpc outputs | advanced | The number of rpc output statements |- | Augments | advanced | The number of top-level augment statements within data nodes (not groupings). |- | Uses | advanced | The number of uses statements within data nodes (not groupings). |- | Choices | advanced | The number of choice statements. |- | Cases | advanced | The number of case statements. This includes implied cases which contain a single data node. |- | Anyxmls | advanced | The number of anyxml statements. |- | NP containers | advanced | The number of non-presence containers. |- | P containers | advanced | The number of presence containers. |- | Lists | advanced | The number of list statements. |- | Leaf-lists | advanced | The number of leaf-list statements. |- | Key leafs | advanced | The number of leaf statements which are defined within a list to be a key. |- | Plain leafs | advanced | The number of plain leaf statements. |- | Imports | all | The number of import statements. |- | Integral numbers | advanced | The number of leaf and leaf-list statements that used a numeric data type other than decimal64. |- | Decimal64s | advanced | The number of leaf and leaf-list statements that used the decimal64 data type. |- | Enumerations | advanced | The number of leaf and leaf-list statements that used the enumeration data type. |- | Bits | advanced | The number of leaf and leaf-list statements that used the bits data type. |- | Booleans | advanced | The number of leaf and leaf-list statements that used the boolean data type. |- | Emptys | advanced | The number of leaf and leaf-list statements that used the empty data type. |- | Strings | advanced | The number of leaf and leaf-list statements that used the string data type. |- | Binarys | advanced | The number of leaf and leaf-list statements that used the binary data type. |- | Instance Identifiers | advanced | The number of leaf and leaf-list statements that used the instance-identifier data type. |- | Leafrefs | advanced | The number of leaf and leaf-list statements that used the leafref data type. |- | Identityrefs | advanced | The number of leaf and leaf-list statements that used the identityref data type. |- | Unions | advanced | The number of leaf and leaf-list statements that used the union data type. |}
'''--stats parameter'''
{| class=wikitable border="1" | Syntax | enumeration [none, brief, basic, advanced, all] |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --stats=advanced --module=test |} == --subdirs == The '''--subdirs''' parameter controls whether sub-directories should be searched or not, if they are found during a module search. If false, the file search paths for modules, scripts, and data files will not include sub-directories if they exist in the specified path.
'''--subdirs parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --subdirs=false \ --subtree=/testpath |} == --subtree == The '''--subtree''' parameter is a leaf-list of path specifications that may contain YANG files. It must be a string value that represents a path specification of the directory subtree to use. All of the YANG source modules contained in the specified directory sub-tree will be processed. Note that symbolic links are not followed during the directory traversal. Only real directories will be searched and regular files will be checked as modules. Processing will continue to the next file if a module contains errors. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. If the '''--subdirs=false''' parameter is present, then only the top-level directory specified by this parameter will be searched for YANG files. If the '''--subdirs=true''' parameter is present, then all the directories contained within the one specified by this parameter will also be searched for YANG files. The exceptions are: * any directory beginning with a dot character ('.'), such as '.svn' * any directory named 'CVS' Examples: ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path
'''subtree parameter'''
{| class=wikitable border="1" | Syntax | string: path specification |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | yangdiffyangdump |- | Example: | yangdump \ --format=html \ --subtree=~/testmods --subtree=./workdir --output=./yang-html-files |} == --totals == The '''--totals''' parameter is used with the '''--stats''' parameter to control how summary statistics are reported. * The '''--stats '''parameter must be set to a value other than 'none' for this parameter to have any affect. The value of this parameter will control which statistics are reported. * Normally, no summary is generated (default is 'none'). * The 'summary' value will not have any affect unless there is more than one input module in the statistics collection. * The 'summary-only' value will cause the module statistics reports to be skipped, and only a summary of all the input modules will be displayed.
'''--totals parameter'''
{| class=wikitable border="1" | Syntax | enumeration [none, summary, summary-only] |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ ---stats=basic --totals=summary-only --subtree=~/my-test-modules |} == --tree-identifiers == The '''--tree-identifiers''' parameter causes information to be object identifier name strings to be reported for the object, RPC operation, and notification definitions within the input module(s). The following information is reported for each identifier: * object type (e.g., leaf, container, rpc) * local name for the object (indented for each descendant node) Example report for module 'yuma-mysession': identifiers: rpc get-my-session container output leaf indent leaf linesize leaf with-defaults container input rpc set-my-session container input leaf indent leaf linesize leaf with-defaults container output
'''--tree-identifiers parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --tree-identifiers \ --module=yuma-mysession |} == --unified == The '''--unified''' parameter indicates if submodules should be combined into the main module for '''yangdump''' translation output. Any include statements will be replaced by the module definitions contained within the submodule. If set to 'true', then submodules will be processed within the main module, in a unified report, instead of separately, one report for each file. For translation purposes, this parameter will cause any sub-modules to be treated as if they were defined in the main module. Actual definitions will be generated instead of an 'include' statement, for each submodule. If this mode is selected, then submodules will be ignored: * If entered with the '''--module''' parameter explicitly. * If found when searching for main YANG files to process in a directory, e.g., for the '''--subtree''' parameter. If set to 'false', then a separate output file is generated for each input file, so that XSD output and other reports for a main module will not include information for submodules.
'''--unified parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | FALSE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --unified=true \ --format=html \ --subtree=$PROJECT_X/modules \ --defnames=true \ --output=$PROJECT_X/html |} == --urlstart == The '''--urlstart''' parameter specifies the string to start all URLs during '''yangdump''' HTML translation. If present, then this string will be used to start all HREF links and URLs generated for SQL and HTML translation. It is expected to be a URL ending with a directory path. The trailing separator '/' will be added if it is missing. If not present (the default), then relative URLs, starting with the file name will be generated instead. For example, if this parameter is set to the following string: http://acme.com/public The URL generated for the 'bar' type on line 53, in the module FOO (version 2008-01-01) would be: * if '''--versionnames=false''': http://acme.com/public/FOO.html#bar.53 * if '''--versionnames=true''' (default): http://acme.com/public/FOO_2008-01-01.html#bar.53
'''--urlstart parameter'''
{| class=wikitable border="1" | Syntax | string (URL format) |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --urlstart="/example.com/public/" \ --unified=true \ --format=html \ --subtree=$PROJECT_X/modules \ --defnames=true \ --output=$PROJECT_X/html |} == --version == The '''--version''' parameter causes the program version string to be printed, and then the program will exit instead of running as normal. All Yuma version strings use the same format: DEBUG: .. or NON-DEBUG: .- An example version number that may be printed: yangdump 2.0-0 This parameter can be combined with the '''--help '''parameter.
'''--version parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump --version |} == --versionnames == The '''--versionnames '''parameter indicates that revision dates should not be used when constructing any output YANG module file names. If present, the default filenames will not contain the module version string. Normally, the [sub]module name and version string are both used to generate a default file name, when the '''--defnames''' output parameter is set to 'true'. This parameter will cause filenames to be generated which do not contain the revision date string.
'''--versionnames parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --versionnames=false \ --subtree=/testpath \ --defnames=true \ --output=~/work3 \ --format=html |} == --warn-idlen == The''' --warn-idlen''' parameter controls whether identifier length warnings will be generated. The value zero disables all identifier length checking. If non-zero, then a warning will be generated if an identifier is defined which has a length is greater than this amount.
'''--warn-idlen parameter'''
{| class=wikitable border="1" | Syntax | uint32: 0 to disable, or 8 .. 1023 |- | Default: | 64 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump --warn-idlen=50 |} == --warn-linelen == The''' --warn-linelen''' parameter controls whether line length warnings will be generated. The value zero disables all line length checking. If non-zero, then a warning will be generated if a YANG file line is entered which has a length is greater than this amount. Tab characters are counted as 8 spaces.
'''--warn-linelen parameter'''
{| class=wikitable border="1" | Syntax | uint32: 0 to disable, or 40 .. 4095 |- | Default: | 72 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump --warn-linelen=79 |} == --warn-off == The''' --warn-off''' parameter suppresses a specific warning number. The error and warning numbers, and the default messages, can be viewed with the yangdump program by using the '''--show-errors''' configuration parameter. The specific warning message will be disabled for all modules. No message will be printed and the warning will not count towards the total for that module.
'''--warn-off parameter'''
{| class=wikitable border="1" | Syntax | uint32: 400 .. 899 |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 499 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump --warn-off=435 # revision order not descending |} == --xsd-schemaloc == The '''--xsd-schemaloc''' parameter specifies that '''yangdump''' XSD translations should include the 'schemaLocation' attribute. If present, then the 'schemaLocation' attribute will be generated during XSD translation. This will be done for the module being processed, and any modules that are imported into that module. If not present (the default), then the 'schemaLocation' attribute is not generated during XSD translation. Relative URLs for include and import directives will be generated, starting with the file name. For example, if this parameter is set to the following string: [http://example.com/public http://example.com/public] The 'schemaLocation' XSD for the module test3 (version 10-19-2008) would be: * If '''--versionnames=false''': xsi:schemaLocation='[http://netconfcentral.com/ns/test3 http://netconfcentral.org/ns/test3] http://example.com/public/test3.xsd' * if '''--versionnames=true''' (default): xsi:schemaLocation='[http://netconfcentral.com/ns/test3 http://netconfcentral.org/ns/test3] http://example.com/public/test3_2008-10-19.xsd'
'''--xsd-schemaloc parameter'''
{| class=wikitable border="1" | Syntax | string (URL formal) |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangdump \ --xsd-schemaloc="[http://example.com/ http://example.com]" --format=xsd --module=test3 --defnames=true |} == --yuma-home == The '''--yuma-home''' parameter specifies the project directory root to use when searching for files. If present, this directory location will override the ''''$YUMA_HOME''' environment variable, if it is set. If this parameter is set to a zero-length string, then the $'''YUMA_HOME''' environment variable will be ignored. The following directories are searched when either the '''$YUMA_HOME''' environment variable or this parameter is set: * '''$YUMA_HOME/modules''' ** This sub-tree is searched for YANG files. * '''$YUMA_HOME/data''' ** This directory is searched for data files. * '''$YUMA_HOME/scripts''' ** This directory is searched for '''yangcli''' script files.
'''yuma-home parameter'''
{| class=wikitable border="1" | Syntax | string: directory specification |- | Default: | '''$YUMA_HOME''' environment variable |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdump \ --yuma-home=~/sw/netconf \ --log=~/server.log& |} yuma123_2.14/netconf/doc/yuma_docs/yuma-yangcli-manual.txt0000664000175000017500000100530214770023131023723 0ustar vladimirvladimir
'''Yuma yangcli Manual'''
YANG-Based Unified Modular Automation Tools
NETCONF Over SSH Client
Version yuma123-2.13
= Preface = == Legal Statements == Copyright 2009 - 2012, Andy Bierman, All Rights Reserved. Copyright 2013 - 2022, Vladimir Vassilev, All Rights Reserved. == Additional Resources == This document assumes you have successfully set up the software as described in the printed document: [[Yuma Installation Guide]] Other documentation includes: [[Yuma User Manual]] [[Yuma netconfd Manual]] [[Yuma yangdiff Manual]] [[Yuma yangdump Manual]] [[Yuma Developer Manual]] There are several sources of free information and tools for use with YANG and/or NETCONF. The following section lists the resources available at this time. === WEB Sites === * '''Netconf Central''' ** [http://www.netconfcentral.org/ http://www.netconfcentral.org/] ** Yuma Home Page *** Free information on NETCONF and YANG, tutorials, on-line YANG module validation and documentation database * '''Yuma SourceFource OpenSource Project''' ** [http://sourceforge.net/projects/yuma/ http://sourceforge.net/projects/yuma/] *** Download Yuma source and binaries; project forums and help * '''Yang Central''' ** [http://www.yang-central.org/ http://www.yang-central.org] ** Free information and tutorials on YANG, free YANG tools for download * '''NETCONF Working Group Wiki Page''' ** [http://trac.tools.ietf.org/wg/netconf/trac/wiki http://trac.tools.ietf.org/wg/netconf/trac/wiki] ** Free information on NETCONF standardization activities and NETCONF implementations * '''NETCONF WG Status Page''' ** http://tools.ietf.org/wg/netconf/ ** IETF Internet draft status for NETCONF documents * '''libsmi Home Page''' ** [http://www.ibr.cs.tu-bs.de/projects/libsmi/ http://www.ibr.cs.tu-bs.de/projects/libsmi/] ** Free tools such as smidump, to convert SMIv2 to YANG * '''YumaWorks''' ** [http://www.yumaworks.com/ http://www.yumaworks.com] ** Offers support, training, and consulting for Yuma. ** Offers YumaPro, a professional version of Yuma that includes concurrency, external database support, sub-agent support, multiple northbound interfaces, and more. API compatible with Yuma. Availability: September, 2012. Licensed. * '''Transpacket''' ** [http://www.transpacket.com/ http://www.transpacket.com] ** Uses Yuma for configuration and monitoring of its products. === Mailing Lists === * '''NETCONF Working Group''' ** http://www.ietf.org/html.charters/netconf-charter.html ** Technical issues related to the NETCONF protocol are discussed on the NETCONF WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. * '''NETMOD Working Group''' ** [http://www.ietf.org/html.charters/netmod-charter.html http://www.ietf.org/html.charters/netmod-charter.html] ** Technical issues related to the YANG language and YANG data types are discussed on the NETMOD WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. == Conventions Used in this Document == The following formatting conventions are used throughout this document:
'''Documentation Conventions'''
{| class=wikitable border="1" !
Convention
!
Description
|- | '''--foo''' | CLI parameter foo |- | '''''' | XML parameter foo |- | '''foo''' | '''yangcli''' command or parameter |- | '''$FOO''' | Environment variable FOO |- | '''$$foo''' | '''yangcli''' global variable foo |- | some text | Example command or PDU |- | some text | Plain text |} = yangcli User Guide =
'''Program Components'''
[[Image:]] == Introduction == The '''yangcli''' program is a NETCONF over SSH client application. It is driven directly by YANG modules, and provides a simple but powerful application interface for any YANG file. There is no configuration required at all to use any YANG file, although there are some YANG extensions that will be utilized if present. === Features === The '''yangcli''' client has the following features: * Automatic support for all NETCONF protocol operations, including several 'short-hand' commands for the most common operations, like and . * Automated database locking, unlocking and error cleanup, using the high-level''' get-locks''' and '''release-locks''' commands. * Automatic, standards-based, server schema synchronization, using the YANG module capability URI information in the PDU, and the operation: ** For each session, the exact view of the server schema definition tree is created, based on the module capability: *** module namespace *** module name *** module revision date *** enabled features *** names of any modules that contain deviations for this module ** The help text and parameter validation for each session will be tailored to the capabilities advertised by the server. ** Parses each potential matching YANG file to make sure the module name, revision date, and namespace URI value are all exactly the same as the values in the module capability URI. * Understands all NETCONF protocol capabilities, and complex hard-wired logic simplifies protocol usage, and allows high-level commands to pick appropriate defaults for many RPC operation parameters. * Load any YANG module at boot-time or run-time and start using it immediately. * Full concurrent support for multiple revisions of the same module. * Supports NETCONF notifications, including :interleave capability. * Full XPath 1.0 and subtree filtering support. * Automatic support for all YANG language mechanisms, including extensions. * Any YANG '''''' operation is automatically available as a '''yangcli''' command. * Uses YANG files directly as input, so no pre-processing or configuration needed to use a new module. * Can be called from '''unix''' scripts in 'batch-mode' to automatically establish a NETCONF session, issue a command or invoke a yangcli script, close the session, and return the results. * Extensive interactive shell environment, including user variables, file variables, smart parameter set completion, and a simple scripting environment for automatic operation. * Automatic, context-sensitive (tab key) command-line completion. * Full support for XPath, instance-identifier, leafref, and identityref parameters. * Automatic, context-sensitive help system, based completely on YANG files and using the exact modules supported by the current NETCONF session, if connected. * Full, customizable command line editing, using '''emacs''' by default, but '''vi''' or a custom set of keystroke bindings are also supported. * Command line history and command recall. * Store and recall command line history files for later use. * Automatic NETCONF session management, including support for all YANG extensions to the element. * Automatic recognition and support for all NETCONF 'capability' related operations. * Automatic support for all YANG additions to the NETCONF protocol, such as the '''insert''' operation * Unlimited nested scripts with up to 10 parameters each can automate testing and other management tasks * User configurable command aliases that can be saved and restored. === Starting yangcli === The current working directory in use when '''yangcli''' is invoked is important. It is most convenient to run '''yangcli''' from a work directory, rather than the installation directory or within the module library. The '''yangcli''' program can be invoked several ways: * To get the current version and exit:
'''yangcli --version''' * To get program help and exit:
'''yangcli --help''' '''yangcli --help --brief''' '''yangcli --help --full''' * To start an interactive session with the default parameters:
'''yangcli''' * To start an interactive session with a new log file:
'''yangcli --logfile=''mylogfile''''' * To start an interactive session and append to an existing log file:
'''yangcli'' --''logfile''=mylogfile ''--log-append''' * To get parameters from a configuration file:
'''yangcli'' --''config''=~/yangcli.conf''''' * To begin to connect to a server upon startup, provide the '''--server''' parameter. The '''connect''' command will be started upon startup and the user will be prompted to enter the rest of the mandatory parameters to the '''connect''' command.
'''yangcli server=myserver.example.com''' * To connect to a server and automatically connect without any interactive interruption, enter the '''--server''', '''--user''', and '''--password''' parameters. A session startup will be attempted right away, using these parameters. Any optional parameters for the '''connect''' command ('''--port''' or '''--timeout''') may be entered as well. All parameters can be entered from a config file, and/or the command line.
'''yangcli --server=myserver.example.com \''' '''--user=andy --password=yangrocks''' * To automatically connect to a server, run a script in non-interactive mode, and then remain connected to the server, add the '''--run-script''' parameter to the connection parameters. The '''--runpath''' parameter can also be entered, if needed.
'''yangcli --server=myserver.example.com \''' '''--user=andy --password=yangrocks \''' '''--run-script=mytestscript''' * To automatically connect to a server, run a script in non-interactive mode, and then exit the program, add the '''--batch-mode''' and '''--run-script''' parameters to the connection parameters. The '''--runpath''' parameter can also be entered, if needed.
'''yangcli --server=myserver.example.com \''' '''--user=andy --password=yangrocks \''' '''--run-script=mytestscript --batch-mode''' * To automatically connect to a server, and run a single command instead of a script, and then exit, use the '''--run-command''' parameter instead of the '''--run-script''' parameter. The''' --batch-mode''' parameter can be left out to remain in the current session (in interactive mode) after the command is invoked. '''yangcli --server=myserver.example.com \''' '''--user=andy --password=yangrocks \''' '''--batch-mode --run-command=â€sget /systemâ€''' === Stopping yangcli === To terminate the '''yangcli''' program, use the '''quit''' command. The control-C character sequence will also cause the program to be terminated. === Statements === The '''yangcli''' script interpreter accepts several types of statements:
'''yangcli Statements'''
{| class=wikitable border="1" |
'''type'''
|
'''description'''
|
'''example'''
|- | command | invoke a local command and/or send an to the server | sget /system |- | variable assignment | set a user variable to some value | $system = sget /system |- | file assignment | set the contents of a file to some value | @save.txt = $system |- | variable deletion | delete a user variable or clear a system variable | $system = |} A command can be as simple like 'get' or complex, like 'edit-config'. A variable assignment sets the specified user or system variable to the right hand side of the expression. An expression has many forms, including the result from a local command or a remote NETCONF operation. If a string that matches a command is used as the assignment value, then it must be entered in quotes (single or double). For example, the''' $$default-operation''' system configuration variable accepts enumeration values which also match RPC commands: yangcli> '''$$default-operation = 'merge'''' A file assignment is essentially the same as a variable assignment, except the right hand side of the expression is saved in the specified file, instead of a user variable. To delete a user variable, leave the right hand side of the expression empty (or just whitespace). Note: More statement types will be available in the next release. === Commands === The yangcli program has several built-in commands, defined in '''yangcli.yang''', '''yuma'''-'''netconf.yang,''' and '''notifications.yang'''. The YANG '''rpc''' statement is used to define the syntax and behavior of each command. There are 2 types of yangcli commands: * '''local''': the command is executed within the yangcli application, and can be invoked at any time. * '''remote''': the command is executed on the remote server, and is only available when a NETCONF session is active. Any YANG '''rpc''' statement that '''yangcli''' does not recognize as a local command is treated as a remote command available on the server.
'''Local Commands'''
{| class=wikitable border="1" |
'''command'''
|
'''description'''
|- | alias | Show or set a specific yangcli command alias |- | aliases | Manage the yangcli command aliases |- | cd | Change the current working directory |- | connect | Connect to a server and start a NETCONF session |- | elif | Start an 'else-if' conditional block |- | else | Start an 'else' conditional block |- | end | End an 'if' or 'while' block |- | eval | Evaluate an XPath expression |- | eventlog | View or clear the notification event log |- | fill | Fill a user variable |- | help | Get context-sensitive help |- | history | Manage the command history buffer |- | if | Start an 'if' conditional block |- | list | List modules, objects, or other properties of the session |- | log-debug | Log a debug message |- | log-error | Log an error message |- | log-info | Log an info message |- | log-warn | Log a warning message |- | mgrload | Load a YANG file into the client only |- | pwd | Print the current working directoty |- | quit | Exit the program |- | recall | Recall a line from the command history buffer |- | run | Run a script |- | show | Show variables and objects currently available |- | start-timer | Start a script timer |- | stop-timer | Stop a script timer and display the elapsed time |- | unset | Remove a command alias from memory |- | uservars | Manage the yangcli user variables |- | while | Start a 'while' conditional loop block |} The following table shows the standard NETCONF protocol operations that are directly available for use, depending on the capabilities of the server.
'''Standard NETCONF Commands'''
{| class=wikitable border="1" |
'''command'''
|
'''description'''
|- | cancel-commit | Cancel the current confirmed commit procedure |- | close-session | Close the current NETCONF session |- | commit | Make the candidate database be the running config |- | copy-config | Copy an entire NETCONF database |- | create-subscription | Start receiving NETCONF notifications |- | delete-config | Delete an entire NETCONF database |- | discard-changes | Discard any edits in the candidate database |- | edit-config | Alteration of the target database |- | get | Filtered retrieval of state data and running config |- | get-config | Filtered retrieval of any NETCONF database |- | get-schema | Get a data model definition file from the server |- | kill-session | Force close another NETCONF session |- | lock | Lock a NETCONF database that is currently unlocked |- | unlock | Unlock a NETCONF database that is currently locked |- | validate | Validate the contents of a NETCONF database |} The following '''yangcli''' commands are available for simplified access to standard NETCONF operations
'''Custom NETCONF Commands'''
{| class=wikitable border="1" |
'''command'''
|
'''description'''
|- | create | Invoke an create operation |- | delete | Invoke an delete operation |- | get-locks | Lock all the databases with the operation |- | insert | Invoke an YANG insert operation |- | merge | Invoke an merge operation |- | release-locks | Unlock all the locked databases with the operation |- | remove | Invoke an remove operation |- | replace | Invoke an replace operation |- | save | Save the current edits on the server in NV-storage |- | sget | Invoke a operation with a subtree filter |- | sget-config | Invoke a operation with a subtree filter |- | xget | Invoke a operation with an XPath filter |- | xget-config | Invoke a operation with an XPath filter |} The following table shows the extended NETCONF protocol operations that are available on the '''netconfd''' server only.
'''Extended netconfd Commands'''
{| class=wikitable border="1" |
'''command'''
|
'''description'''
|- | get-my-session | Retrieve session customization parameters |- | load | Load a module into the server. |- | no-op | No operation. |- | restart | Restart the server. |- | set-log-level | Set the server logging verbosity level. |- | set-my-session | Set session customization parameters. |- | shutdown | Shutdown the server. |} === Variables === The '''yangcli''' program utilizes several types of user-accessible variables. These variables can be listed with the ''''show vars'''' command and other commands as well. A variable reference consists of 1 or 2 dollar signs ('$'), followed immediately by a valid identifier string (e.g., '''$$global-log''' or '''$local-log'''). Variables can be 1 or more characters in length, and follow the YANG rules for identifier names. The first character must be a letter, 'A' to 'Z', or 'a' to 'z'. The 2nd to last characters can be a letter 'A' to 'Z', or 'a' to 'z', number ('0' to '9'), an underscore ('_'), a dash ('-'), or a period ('.') character. There are 4 categories of parameters supported: # Read-only system variables # Read-write system variables # Read-write global user variables (saved in $HOME/.yuma directory) # Read-write local user variables It is an error if a variable is referenced (in the right-hand-side of a statement) that does not exist. The first 3 types are '''global variables''', which means that they are available to all run-levels of all scripts. The last type, called a '''local variable,''' is only visible to the current run-level of the current script (or interactive shell). Refer to the following section for more details on run levels.
'''Variable Syntax'''
{| class=wikitable border="1" |
'''syntax'''
|
'''description'''
|
'''example'''
|- | $$ | Left hand side: set the global variable to some value | $$saved_get = get |- | $$ | Right hand side: access the value of a global variable | fill --target=\ $$mytarget |- | $ | Left hand side: set the local variable to some value | $myloglevel = \ $$log-level |- | $ | Right hand side: access the value of any variable with this name (try local, then global) | $myuser = $user |} The following table shows the '''unix''' environment variables that are available as read-only global variables in '''yangcli'''. These variables are set once when the program is started, and cannot be used in the the left hand side of an assignment statement.
'''Read-only system variables'''
{| class=wikitable border="1" |
'''variable'''
|
'''description'''
|- | $$HOME | the '''HOME''' environment variable |- | $$HOSTNAME | the '''HOST''' or '''HOSTNAME''' environment variable |- | $$LANG | the '''LANG''' environment variable |- | $$PWD | the '''PWD''' environment variable, when '''yangcli''' was invoked |- | $$SHELL | the '''SHELL''' environment variable |- | $$USER | the '''USER''' environment variable |- | $$YUMA_DATAPATH | the '''YUMA_DATAPATH''' environment variable |- | $$YUMA_HOME | the '''YUMA_HOME''' environment variable |- | $$YUMA_MODPATH | the '''YUMA_MODPATH''' environment variable |- | $$YUMA_RUNPATH | the '''YUMA_RUNPATH''' environment variable |} The following table shows the CLI configuration parameters that can be read or changed (but not deleted). If a particular parameter was not set during program invocation, then the associated variable will contain the empty string.
'''Read-write system variables'''
{| class=wikitable border="1" |
'''variable'''
|
'''description'''
|- | $$alt-names | the '''–alt-names''' configuration parameter |- | $$autocomp | the -'''-autocomp''' configuration parameter |- | $$autoload | the '''--autoload''' configuration parameter |- | $$baddata | the '''--baddata''' configuration parameter |- | $$default-module | the '''--default-module''' configuration parameter |- | $$default-operation | the parameter for the operation |- | $$display-mode | the '''--display-mode''' configuration parameter |- | $$error-option | the default parameter for the operation |- | $$fixorder | the '''--fixorder''' configuration parameter |- | $$indent | the '''–indent''' configuration parameter |- | $$log-level | the '''--log-level''' configuration parameter |- | $$match-names | the '''–match-names''' configuration parameter |- | $$optional | the '''--optional''' configuration parameter |- | $$server | the '''--server''' configuration parameter |- | $$test-option | the parameter for the operation |- | $$time-rpcs | the '''–time-rpcs''' configuration parameter |- | $$timeout | the '''--timeout''' configuration parameter |- | $$use-xmlheader | the '''–use-xmlheader '''configuration parameter |- | $$user | the '''--user''' configuration parameter |- | $$with-defaults | the '''--with-defaults''' configuration parameter |} '''Read-write global user variables''' If a unrecognized global variable (e.g., $$foo) is used in the left-hand side of an assignment statement, then a global user variable will be created with that name. If the global user variable already exists, then its value will be overwritten. The '''uservars''' command can be used to load or store these variables so they are loaded at boot-time. By default, the XML file used to store these variables is '''$HOME/.yuma/yangcli_uservars.xml'''. '''Read-write local user variables''' If a local variable (e.g., $foo) is used in the left-hand side of an assignment statement, then a local user variable will be created with that name. If the local user variable already exists, then its value will be overwritten. If the variable is created within a script (i.e., run-level greater than zero), then it will be deleted when the script exits. === Files === File contents can be used in '''yangcli''' statements, similar to user variables. A file reference consist of the 'at-sign' character ('@') followed immediately by a valid file specification. @foo.yang = get-schema --identifier=foo --version=â€â€ --format=â€YANG†mgrload --module=foo If the file extension is “'''.yang'''â€, “'''.log'''â€, “'''.txt'''â€, or “'''.text'''â€, then the value (or command output) will be saved, and yangcli will strip off the outermost XML (if needed) to save the requested file as a pure text file. Otherwise, the file will be saved in XML format. The display mode set by the user can affect XML output. If the display mode i s'xml-nons' then XML without namespace (xmlns) attributes will be generated instead of normal XML. Note: The '''--display-mode''' configuration parameter, and '''$$display-mode''' system variable, only affect the output and display of data in the yangcli program. NETCONF protocol messages are always sent in 'xml' mode. Files may also be used as parameter replacements, within a command. $saved_get = get [mailto:--filter%3D@filter.xml --filter=@filter.xml] --with-defaults=explicit It is an error if the file referenced does not exist or cannot be read. === Scripts === Any command can be entered at the interactive shell, or stored in a script file, and invoked with the ''''run'''' command. Scripts are simply text files, and the extension used does not matter. There are no formal templates for scripts, like there are for RPC operations, at this time. Instead, positional parameters can be passed to any script. The parameters named '''--P1''' to '''--P9''' allow up to 9 parameters to be passed to any script. Within each script, the numbered parameters ''''$1'''' to ''''$9'''' are available, and contain the value that was passed as the corresponding ---Pn†parameter when calling the script. If a line contains only optional whitespace, followed by the pound sign character '#', then the line is treated as a comment. These lines will be skipped. If an error occurs during a command within a script, or an is received from the server during a NETCONF session, then the running script will be canceled, and if this was a nested script, then all parent scripts will also be canceled. Script Example: run connect --P1=andy --P2==localhost --P3=yangrocks // connect script # start a NETCONF session $user = $1 $server = $2 $password = $3 connect --user=$user --server=$server --password=$password '''Run Levels''' The '''run''' command can appear in a script. When '''yangcli''' starts up, either in interactive mode or in batch mode, the script interpreter is at run level zero. Each time a '''run''' command is invoked, either at the command line or within a script currently being invoked, a new run level with the next higher value is assigned. Local variables are only visible within the current run level. A maximum of 512 run levels are supported in '''yangcli'''. Scripts can be called recursively, but a stop condition needs to be used to break the recursion (e.g., call the script from within a conditional code block. '''Conditional Command Blocks''' The 'if', 'elif', 'else', and 'end' commands are used to create an 'if command sequence'. Any other commands that appear between these commands are part of a conditional command block. These blocks can be nested. The current conditional state is inherited, so an if command sequence within a false conditional block will not be executed. A block can contain zero or more command lines, These command work exactly like an 'if' expression within a program language such as Python. Note that indentation is not significant, but it may be used to make scripts more readable. The 'if' command starts a new if-command sequence. It is followed by a conditional command block. This block ends when an 'elif', 'else', or 'end' command within the same if command block is encountered. At most, only one conditional code block within the if command sequence will be executed. Once a conditional command block has been exectuted, any remaining blocks will be skipped. All user and system variables that are available to the current script run level can be used within the XPath expressions for determining the conditional block state (true or false). '''Conditional Command Loop Blocks''' The 'while' and 'end' commands are used to create an 'while loop sequence'. Any other commands that appear between these commands are part of a conditional command loop block. These blocks can be nested. The current conditional state is inherited, so a while loop sequence within a false conditional block will not be executed. A block can contain zero or more command lines, The loop condition can be a constant expression. The maxloops parameter will prevent infinite looping, and can be utilized to use the while loop sequence as a simple 'for' loop, iterating a specific number of times. All user and system variables that are available to the current script run level can be used within the XPath expressions for determining the conditional block state (continue or terminate loop). '''Sample Script''' The following script does not do any useful work. It is provided to demonstrate some simple constructs. $x = 0 while '$x < 2' # this is a comment log-info 'start 1' $x = eval '$x + 1' $y = 0 while '$y < 4' log-info 'start 2' $y = eval '$y + 1' if "module-loaded('test')" log-info 'module test loaded' elif '$x > 1' log-info 'x>1' elif "feature-enabled('test3', 'feature1')" log-info 'feature1' else log-info 'else' end log-info 'end 2' end log-info 'end 1' end if "feature-enabled('test5', 'feature-foo')" log-info 'feature-foo' run add-foo-parms end === Configuration Parameter List === The following configuration parameters are used by '''yangcli'''. Refer to the CLI Reference for more details.
'''yangcli CLI Parameters'''
{| class=wikitable border="1" |
'''parameter'''
|
'''description'''
|- | --aliases-file | Specifies the command aliases file to use. |- | --alt-names | Controls whether alternate names will be checked for UrlPath searches. |- | --autoaliases | Controls automatic loading and saving of the command aliases |- | --autocomp | Controls whether partial commands are allowed or not. |- | --autohistory | Controls whether th command line history buffer will be automatically loaded at startup and saved on exit. |- | --autoload | Controls whether modules used by the server will be loaded automatically, as needed. |- | --autouservars | Controls automatic loading and saving of the global user variables |- | --bad-data | Controls how bad data about to be sent to the server is handled. |- | --batch-mode | Indicates the interactive shell should not be used. |- | --config | Specifies the configuration file to use for parameters. |- | --datapath | Sets the data file search path. |- | --default-module | Specifies the default module to use to resolve identifiers. |- | --deviation | Species one or more YANG modules to load as deviations.` |- | --display-mode | Specifies how values should be displayed. |- | --echo-replies | Controls whether RPC replies will be displayed in the log output, if log-level >= 'info' |- | --echo-requests | Controls whether RPC requests will be displayed in the log output, if log-level >= 'info' |- | --feature-disable | Leaf list of features to disable |- | --feature-enable | Specifies a feature that should be enabled |- | --feature-enable-default | Specifies if a feature should be enabled or disabled by default |- | --fixorder | Controls whether PDUs are changed to canonical order before sending them to the server. |- | --force-target | Controls whether the candidate or running configuration datastore will be used as the default edit target, when both are supported by the server. |- | --help | Get program help. |- | --help-mode | Adjust the help output (--brief or --full). |- | --home | Override the $HOME environment variable. |- | --indent | Specifies the indent count to use when writing data. |- | --log | Specifies the log file to use instead of STDOUT. |- | --log-append | Controls whether a log file will be reused or overwritten. |- | --log-level | Controls the verbosity of logging messages. |- | --match-names | Match mode to use for UrlPath searches |- | --modpath | Sets the module search path. |- | --module | Specifies one or more YANG modules to load upon startup. |- | --ncport | Specifies the NETCONF server port number to use in the '''connect''' command. |- | --password | Specifies the password to use in the '''connect''' command. |- | --private-key | Contains the file path specification for the file containing the client-side private key. |- | --protocols | Controls which NETCONF protocol versions will be enabled |- | --public-key | Contains the file path specification for the file containing the client-side public key. |- | --runpath | Sets the executable file search path. |- | --run-command | Specifies the command to run at startup time. |- | --run-script | Specifies the script to run at startup time. |- | --server | Specifies the server address to use in the '''connect''' command. |- | --subdirs | Specifies whether child sub-directories should be searched when looking for files. |- | --time-rpcs | Measure the round-trip time of each request and at the session level. |- | --timeout | Specifies the timeout to use in the '''connect''' command. |- | --transport | Specified the transport protocol to use (ssh or tcp) |- | --use-xmlheader | Specifies how file result variables will be written for XML files. Controls whether the XML preamble header will be written or not. |- | --user | The default user name for NETCONF sessions. |- | --uservars-file | Specifies the global user variable files to load. |- | --version | Prints the program version and exits. |- | --warn-idlen | Controls how identifier lengths are checked. |- | --warn-linelen | Controls how line lengths are checked. |- | --warn-off | Suppresses the specified warning number. |- | --yuma-home | Specifies the '''$YUMA_HOME''' project root to use when searching for files. |} == Invoking Commands == Commands can be entered with parameters: * in one continuous line
merge target=/toaster/toastControl --value=â€down†* in several lines (using line continuation)
merge target=/toaster/toastControl \ --value=â€down†* interactively prompted for each parameter
merge
(will be prompted for target and value) * a combination of the above
merge target=/toaster/toastControl
(will be prompted for value) When a command is entered, and the yangcli script interpreter is running in interactive mode ('''--batch-mode''' not active), then the user will be prompted for any missing mandatory parameters. If the '''--optional''' parameter is present (or the '''$$optional''' system variable is set to 'true'), then the user will be prompted for any optional parameters that are missing. A command has the basic form: ''' *''' The command name is a qualified name matching the name used in the YANG rpc statement. This is followed by zero or more parameters (in any order). A command parameter has the same syntax as a CLI configuration parameter. The command name syntax is described below. An un-escaped end-of-line character ('enter key') terminates command line input. === Command Prompt === The '''yangcli''' command prompt changes, depending on the context. '''Idle Mode:''' If the script interpreter is idle and there is no NETCONF session active, then the prompt is simply the program name: yangcli> If the script interpreter is idle and there is a NETCONF session active, then the prompt is the program name, followed by ' @', depending on the parameters used in the '''connect''' command: yangcli andy@myserver> '''Continuation Mode:''' If a backslash, end-of-line sequence ended the previous line, the prompt will simply be the word 'more' indented 3 spaces: yangcli andy@myserver> get \ more> The 'more>' prompt will continue if the new line also ends in with an escaped end-of-line. When a new line is finally terminated, all the fragments are spliced together and delivered as one command line. Note: context-sensitive command completion is not available in this mode. '''Choice mode:''' If a partial command has been entered in interactive mode, and the script interpreter needs to prompt for a YANG 'choice' parameter, then a list of numbered cases will be presented, and the prompt will be the same as it was before (depending on whether a NETCONF session is active or not), except a colon character (':'), followed by the command name, will be added at the end. As long as parameters for the same command are being entered (i.e., prompted for child nodes within a selected case, the command name will be appended to the prompt. yangcli andy@myserver> sget Enter a number of the selected case statement: 1: case varref: leaf varref 2: case from-cli: leaf target leaf optional anyxml value Enter choice number [1 - 2]: yangcli andy@myserver:sget> '''Parameter mode:''' If a partial command has been entered in interactive mode, and the script interpreter needs to prompt for a leaf or leaf-list, then the parameter name will appear in angle brackets ('<' and '>'). Filling mandatory case /sget/input/from/from-cli: Enter string value for leaf yangcli andy@myserver:sget> If the ''''ncx:password'''' extension is part of the YANG definition for the leaf or leaf-list, then the characters entered at the prompt in this mode will not be echoed, and they will not be saved in the command history buffer. Any default value will not be printed either. Instead, 4 asterisks '****' will be printed, even though the correct value will be used in the command. If a default value is available, it will appear in square brackets ('[' and ']'). In this case, entering 'return' will not be interpreted as an empty string, but rather the default value that was presented. yangcli> connect Enter string value for leaf [andy] yangcli:connect> Enter string value for leaf [myserver] yangcli:connect> Enter string value for leaf [****] yangcli:connect> Enter uint16 value for leaf [830] yangcli:connect> Enter uint32 value for leaf [30] yangcli:connect> Note: After a NETCONF session is terminated for any reason, the connection parameters will be remembered , and presented as defaults the next time the connect command is entered. '''False Conditional Block Mode''' If a conditional command (if, elif, else, or while command) is active, but the conditional expression is false, then any commands defined within that conditional block will not be executed. If this occurs in interactive mode instead of a script, the string '[F]' will be added to the command prompt. Note that the 'help' and 'log-info' commands do not get executed in the following example: yangcli> if 0 yangcli[F]> help yangcli[F]> log-info 'this will not get printed' yangcli[F]> end yangcli> === Command Name === The command name can be entered with or without an XML prefix: yangcli [mailto:andy@www.example.com andy@myserver]> nc:get yangcli [mailto:andy@www.example.com andy@myserver]> get If there is a prefix (e.g., 'nc:get'), then it needs to be one of the XML prefixes in use at the time. Use the 'show modules' command to see the modules and prefixes in use. The YANG prefix will usually be the same as the XML prefix, but not always. XML prefixes are required to be unique, so if any 2 YANG modules pick the same prefix, then 1 of them has to be changed for XML encoding purposes. If the '''--default-module''' configuration parameter (or '''$$default-module''' system variable) is set, it will be used to resolve a command name without any prefix, if it is not a NETCONF or '''yangcli''' command. An error message will be printed if the command entered cannot be found in any YANG module, or if there are multiple commands that match the same string. === ncx:default-parm Extension === Each command may define a default parameter, by placing an ''''ncx:default-parm'''' extension in the rpc input section in the YANG rpc statement. This extension allows less typing in '''yangcli''' to accomplish the same thing. If the script interpreter encounters a string in the command line that is not a recognized parameter for that command, and there is a default parameter defined, then the string will be used as a value for the default parameter. For example, the 'show' parameter is the default parameter for the 'history' command, so both of these commands will be interpreted to mean the same thing: history --show=10 history 10 Note: the default parameter does not allow the wrong form of a parameter type to be entered, to accept the default for that parameter. For example, the 'show' parameter above has a default value of '25': # this is the same as history show=25 history # this is an error, not the same as the above history show The following table shows the default parameters that are available at this time.
'''Default Parameters'''
{| class=wikitable border="1" |
'''command'''
|
'''default parameter'''
|- | alias | var |- | aliases | show |- | cd | dir |- | connect | server |- | create | target |- | elif | expr |- | else | expr |- | if | expr |- | eventlog | show |- | fill | target |- | help | command |- | history | show |- | insert | target |- | load | module |- | log-debug | msg |- | log-error | msg |- | log-info | msg |- | log-warn | msg |- | merge | target |- | mgrload | module |- | recall | index |- | replace | target |- | run | script |- | set-log-level | log-level |- | sget | target |- | sget-config | target |- | xget | select |- | xget-config | select |- | while | expr |} === Parameter Mode Escape Commands === There are 4 escape sequence commands that can be used while entering parameters. They all begin with the question mark character ('?'), and end with the 'Enter' key. Control key sequences are not used because that would interfere with command line editing keys.
'''Parameter mode escape sequences'''
{| class=wikitable border="1" |
'''escape sequence'''
|
'''description'''
|- | ? | Print some help text |- | ?? | Get all available help text |- | ?s | Skip this parameter |- | ?c | Cancel this command |} Note: If the current parameter is considered hidden ('''ncx:hidden''' extension used in the YANG definition), then no help will be available for the parameter, even though it is accessible. This also apples to the '''help''' command. Any command or parameter designated as''' ncx:hidden''' will be treated as an unknown identifier, and no help will be given. Note: Skipping mandatory nodes with the '?s' command is affected by the '''--bad-data''' configuration parameter and '''$$bad-data''' system variable. An error, warning, or confirmation check may occur. Refer to the CLI Reference for more details. Note: If there are any YANG defined values (e.g., enumeration, bits, default-stmt) available for the current parameter, then pressing the tab key will list the full or partial completions available. === Using XPath Expressions === There are some command parameters, such as the '''--target''' parameter for the '''create''' command, that accept XPath absolute path expressions. If prefixes are present, then they must match the set of XML prefixes in use at the time. Use the '''show modules''' command to see the current set of prefixes. If prefixes are not used, then the first available matching XML namespace will be used instead. If the starting forward slash character ('/') is missing, then it will be added. # these are all the same value yangcli:fill> system yangcli:fill> /system yangcli:fill> /sys:system It is important to remember 2 simple rules to avoid common errors in XPath expressions: # String constants must be quoted with single quote characters.The expression [name=fred] is not the same as [foo='fred'].The former compares the 'name' node value to the 'fred' node value.The latter compares the 'name' node value to the string 'fred'. # The double quote character ('â€') is not allowed in XPath '''--select''' parameter expressions because the expression will be sent to the server inside a double-quoted string. If an incomplete XPath absolute path expression is entered, and the script interpreter is in interactive mode, then the user will be prompted to fill in any missing mandatory nodes or key leafs. # complete form of ifMtu leaf yangcli:fill> /interfaces/interface[name='eth0']/ifMtu # incomplete form of ifMtu leaf yangcli:fill> /interfaces/interface/ifMtu Filling key leaf : Enter string value: The '''--select''' parameter for the '''xget''' and '''xget-config''' commands accepts full XPath expressions. The expression must yield a node-set result in order to be used as a filter in NETCONF '''get''' and '''get-config''' operations. One of the simplest XPath filters to use is the '''descendant-or-self''' filter ('//'). For example, this command will retrieve all instances of the 'ifMtu' leaf: xget //ifMtu When interface (or any list) entries are returned by netconfd, they will contain the the entire path back to the root of the YANG module, not just the specified node. Also, any key leafs within a list are added. This is only done if the XPath expression is missing any predicates for key leafs. This is different than XPath 1.0 as used in XSLT. Since NETCONF '''get''' and '''get-config''' operations return complete XML instance documents, not node-sets, the ancestor nodes and naming nodes need to be added. # reply shown with --display-mode=plain data { interfaces { interface eth0 { name eth0 ifMtu 1500 } interface eth1 { name eth1 ifMtu 1518 } } } === Special Parameter Handling === Some special handling of YANG data structures is done by the script interpreter. '''Containers''' Some parameters, such as the''' --source '''and '''--target''' parameters in many commands, are actually represented as a container with a single child -- a choice of several leaf nodes. In this situation, just the name of the desired leaf node can be entered (when in idle mode), as the 'contents' of the container parameter. sget-config /system source=candidate '''Choices and Cases''' If a parameter name exact-match is not found, and a partial match is attempted, then choice and case node names will be ignored, and not cause a match. Since these nodes never appear in the XML PDUs they are treated as transparent nodes (wrt/ parameter searches) unless they are specified with their full name. Parameters that are a choice of several nodes, similar to above, except without a parent container node, (e.g.,''' --help-mode''') can be omitted. The accessible child nodes within the case nodes can be entered directly (e.g., '''sget --target''' parameter). # this is not allowed because 'help-mode' is not complete yangcli> help --command=help --help-mo=brief # this is allowed because 'help-mode' is complete, # even though help-mode is a choice and 'brief' is # an empty leaf yangcli> help help help-mode=brief # choice and case names are transparent when # searching for parameter names, so the # following command is the same as above yangcli> help help brief '''Lists and Leaf-Lists''' When filling a data structure and a descendant node is entered, which is a YANG list or leaf-list, then multiple entries can be entered. After the node is filled in, there will be a prompt (Y/N, default no) to add more list or leaf-list entries. '''Binary Data Type''' The YANG binary data type is supported. Parameters of this type should be entered in plain text and they will be converted to binary format. === Command Completion === The 'tab' key is used for context-sensitive command completion: * If no command has been started, a list of available commands is printed * If a partial command is entered, a list of commands which match the characters entered so far is printed * If a command is entered, but no parameters, then a list of available parameters is printed * If a command is entered, and the cursor is within a command name, then a list of parameters which match the characters entered so far is printed * If a command is entered, and the cursor is after a command name, but not within a value string, then a list of available parameters is printed * If a command is entered, and the cursor is within a command value, then a list of possible values which match the characters entered so far is printed. Note that not all data types support value completion at this time. * If no values are available, but a default value is known, that value will be printed * If a session is active, and whitespace followed by the forward slash '/' character is entered, a list of top-level data node names is printed. Once a top-level name and a trailing slash '/' character is entered, pressing the tab key again will print the names of the child nodes of the current data node. Only schema-node strings are supported at this time. Auto-completion will not work if predicates are present in the absolute path expression. Command list example: no NETCONF session is active: yangcli> cd fill history mgrload quit run connect help list pwd recall show Command list example: NETCONF session is active yangcli andy@myserver.example.com> cd get-schema recall close-session help replace commit history restart connect insert run copy-config kill-session save create list sget create-subscription load sget-config delete load-config show delete-config lock shutdown discard-changes merge unlock edit-config mgrload validate fill no-op xget get pwd xget-config get-config quit === Command Line Editing === The command line parser is based on '''libtecla''', a freely available library. The home page is located here: [http://www.astro.caltech.edu/~mcs/tecla/ http://www.astro.caltech.edu/~mcs/tecla/] The complete user guide for configuring '''libtecla''' is located here: [http://www.astro.caltech.edu/~mcs/tecla/tecla.html http://www.astro.caltech.edu/~mcs/tecla/tecla.html] If the file '''$HOME/.teclarc''' exists, then '''libtecla''' will use it to configure the key bindings. By default, '''libtecla''' uses '''emacs''' key bindings. There is no need for any further '''libtecla''' configuration if '''emacs''' mode is desired. In order to use the '''vi''' editor key bindings, the '''$HOME/.teclarc''' file must exist, and it must contain the following line: edit-mode vi Custom key bindings are also available. Refer to the '''libtecla''' documentation for more details on command line editing key customization. The control key sequence (^F == control key and f key at the same time). The letter is not case-sensitive, so ^F and ^f are the same command. The alt key sequence (M-f == alt key and f key at the same time). The letter is not case-sensitive, so M-F and M-f are the same command. The following table shows the the most common default key bindings:
'''Common editing key bindings'''
{| class=wikitable border="1" |
'''command'''
|
'''description'''
|- | ^F | cursor right |- | ^B | cursor-left |- | ^A | beginning of line |- | ^E | end of line |- | ^U | delete line |- | M-f | forward-word |- | M-b | backward word |- | ^P | up history |- | ^N | down history |} === Command History === Each command line is saved in the command history buffer, unless a password is being entered in parameter mode. By default, the previous history line (if any) will be shown if the ^P key is pressed. By default, the next history line (if any) will be shown if the ^N key is pressed. In addition, the '''history''' command can be used to control the command line buffer further. This command has 4 sub-modes: * '''show''': show maximum of N history entries (default is 25) * '''clear''': clear the history buffer * '''save''': save the history buffer to a file * '''load''': load the history buffer from a file By default, the command line history buffer is loaded when the program starts, and saved when the program exits. This behavior can be turned off by setting the '''--autohistory''' configuration parameter to 'false'. The '!' character is special when editing commands. If the first character is '!', and it is followed by a number or a non-zero character, the line will be interpreted as a recall request: * yangcli> !42 == recall command number 42 (same as recall 42) * yangcli> !get == recall the most recent command line starting with 'get' Refer to the Command Reference section for more details on the '''history''' command. === Command Responses === The command output and debugging messages within yangcli is controlled by the current log level (error, warn, info, debug, debug2, debug3). If a command is executed by the script interpreter, then a response will be printed, depending on the log level value. If the log level is 'info' or higher, and there were no errors and no response, then the string 'OK' is printed. yangcli> $foo = 7 OK yangcli> If the log-level is set to 'error' or 'warn', then the 'OK' messages will be suppressed. If the log level is set to 'debug' or higher, then responses from the server will be echoed to the log (file or STDOUT). The current display mode will be used when printing data structures such as and element contents. If an error response is received from the server, it will always be printed to the log. yangcli andy@myserver> create /system Filling container /system: RPC Error Reply 5 for session 8: rpc-reply { rpc-error { error-type application error-tag access-denied error-severity error error-app-tag limit-reached error-path /nc:rpc/nc:edit-config/nc:config/sys:system error-message 'max-access exceeded' } } yangcli andy@myserver> Refer the the '''--log-level '''parameter in the CLI Reference for more details. == NETCONF Sessions == The '''yangcli''' program can be connected to one NETCONF server at a time. Run multiple instances of yangcli to control multiple agents at once. Use the connect command to start a NETCONF session. This section explains how to use '''yangcli''' to manage a NETCONF server, once a session is established. When a NETCONF session starts up, a exchange is done, and the server reports exactly what content it supports. This information is used extensively to customize the session, and report errors and warnings for the session. === Connection Startup Screen === If the''' --log-level''' is set to 'info' of higher, then a startup screen will be displayed when a NETCONF session is started. It contains: * startup banner * client session ID * server session ID * protocol capabilities supported by the server ** Includes revision-date of supported module * YANG modules supported by the server ** Includes any features and deviations supported in the module * Enterprise specific capabilities supported by the server * Default target database ( or ) * Save operation mapping for the server * '''with-defaults''' reporting capability reported by the server The following example shows a typical startup screen connecting to the '''netconfd''' server: NETCONF session established for andy on myserver Client Session Id: 1 Server Session Id: 8 Server Protocol Capabilities Protocol Version: RFC 4741 candidate:1.0 rollback-on-error:1.0 validate:1.0 xpath:1.0 notification:1.0 interleave:1.0 with-defaults:1.0 netconf-monitoring:1.0 schema-retrieval:1.0 Server Module Capabilities ietf-inet-types@2009-11-10 ietf-netconf-monitoring@2009-11-20 ietf-with-defaults@2009-07-01 Features: with-defaults ietf-yang-types@2009-11-10 nc-notifications@2008-07-14 notifications@2008-07-14 test@2009-12-26 Features: feature1 feature3 feature4 yuma-app-common@2010-01-25 yuma-interfaces@2009-11-21 yuma-mysession@2009-08-11 yuma-nacm@2009-11-21 yuma-ncx@2009-12-21 yuma-proc@2009-11-21 yuma-system@2009-12-27 yuma-types@2010-01-25 Server Enterprise Capabilities None Default target set to: Save operation mapped to: commit Default with-defaults behavior: explicit Additional with-defaults behavior: trim:report-all Checking server Modules... yangcli andy@myserver> === Server Tailored Context === [[Image:]] While a NETCONF session is active, the set of available YANG modules will be set to the modules that the server is using, if the '''--autoload '''configuration parameter is enabled. If the :schema-retrieval capability is also available on the server, then the operation will be attempted for any YANG module specified in the message capabilities, but not available to the '''yangcli''' program. When the server module capabilities are analyzed by the '''yangcli''' client, the entire YANG module search path is checked for the specific module advertised in the capability. All the modules are partially parsed to check the actual namespace and revision date values. The following fields must exactly match in order for yangcli to use a local YANG module, if '''--autoload'''=true. * module name * module revision date (if any) * module namespace If the namespace URI value is different, it indicates that there is either a bug in one of the conflicting YANG modules, or that two different naming authorities picked the same module name. In this case, a warning will be generated during session initialization. Any data returned from the server for YANG modules not currently available will be treated as a YANG 'anyxml' node, instead of the (unknown) YANG data type. If the module contains YANG features that are not advertised in the exchange, then those data definitions will not be available (by default) for use in '''yangcli''' commands. If the module contains an object with a 'when' statement, and the 'when' XPath expression evaluates to 'false', then that data definition will not be available (by default) for use in '''yangcli''' commands. The '''help''' command will be tailored to the modules, capabilities, features, and module deviations reported by the server in exchange. === Retrieving Data === There are 6 commands available to retrieve generic data (i.e., an arbitrary subset of an entire NETCONF database): {| class=wikitable border="1" |
'''command'''
|
'''description'''
|- | get | raw NETCONF operation |- | get-config | raw NETCONF operation |- | sget | high-level subtree filter, using the protocol operation |- | sget-config | high-level subtree filter, using the protocol operation |- | xget | high-level XPath filter, using the protocol operation |- | xget-config | high-level XPath filter, using the protocol operation |} All the high-level retrieval operations support the''' $$with-defaults''' system variable. The parameter will be added the the NETCONF PDU if this variable is set to a value other than 'none' (the default). This system variable will be used as the default if not entered directly. sget /system --with-defaults=$$with-defaults This parameter can also be specified directly, each time the command is used. xget-config //ifMtu --with-defaults=trim The '''$$bad-data''' system variable is used to control how invalid operations and data are sent to the server. The '''xget''' and '''xget-config''' commands are affected by this parameter. If the ''':xpath '''capability was not advertised by the server when the session started, an error or warning may occur if these commands are used. If any data is received that '''yangcli''' does not understand, then a warning message will be printed and the data will be treated as if it was defined with the YANG ''''anyxml'''' data type. === Modifying Data === The following commands are available to modify generic data (i.e., an arbitrary subset of an entire NETCONF database): {| class=wikitable border="1" |
'''command'''
|
'''description'''
|- | commit | raw NETCONF operation |- | create | high-level operation, with nc:operation='create' |- | delete | high-level operation, with nc:operation='delete' |- | delete-config | raw NETCONF operation |- | discard-changes | raw NETCONF operation |- | edit-config | raw NETCONF operation |- | fill | fill a variable for re-use in other operations |- | insert | high-level operation, with YANG insert operation extensions |- | lock | lock a NETCONF database |- | merge | high-level operation, with nc:operation='merge' |- | replace | high-level operation, with nc:operation='replace' |- | save | High level save operation, depending on the default target (candidate or running) |- | unlock | unlock a NETCONF database |} All the high-level editing operations use the '''--target''' parameter reported by the server when the session started up. If the server did not report the ''':candidate''' or ''':writable-running''' capabilities, then there will be no writable target, and an error will occur if these commands are entered. All the high-level editing operations support the''' $$default-operation '''system variable. The parameter will be added the the NETCONF PDU if this variable is set to a value other than 'not-used'. The default is the enumeration 'none', which means do not use any default operation, and only use the explicit '''nc:operation''' attribute. All the high-level editing operations support the''' $$test-option '''system variable. The parameter will be added the the NETCONF PDU if this variable is set to a value other than 'none' (the default). This system variable will be used as the default if not entered directly. replace /interfaces/interface[name='eth0']/ifMtu \ --test-option=$$test-option \ --value=$newvalue This parameter can also be specified directly, each time the command is used. $newvalue = 1518 replace /interfaces/interface[name='eth0']/ifMtu \ --test-option=test-only \ --value=$newvalue All the high-level retrieval operations support the''' $$error-option '''system variable. The parameter will be added the the NETCONF PDU if this variable is set to a value other than 'none' (the default). This system variable will be used as the default if not entered directly. replace /interfaces/interface[name='eth0']/ifMtu \\ --error-option=$$error-option \ --value=1518 This parameter can also be specified directly, each time the command is used. replace /interfaces/interface[name='eth0']/ifMtu \ --error-option=rollback-on-error \ --value=1518 The high level '''save''' command is mapped to other commands, depending on the capabilities reported by the server.
'''save command'''
{| class=wikitable border="1" |
'''capabilities'''
|
'''real command(s)'''
|- | :candidate | commit |- | :writable-running | |- | :startup | copy-config --source=running \ --target=startup |} === Using Notifications === The '''create-subscription''' command is used to start receiving notifications. The '''netconfd''' server will include a element in any notification that is saved in the replay buffer. This unsigned integer can be used to help debug notification filters (i.e., if non-consecutive values are received, then the notification was filtered, or dropped due to access control policy). If any replay notifications are desired, then the''' --startTime''' parameter must be included. At the end of the stored notifications, the server will send the event. This notification type is not saved, and will not be found in the server replay buffer, if replay is supported by the server. The '''netconfd''' server will not include a element in this notification type. If the notification subscription should stop at a certain time, then the '''--stopTime''' parameter must be included. At the end of the stored notifications, the server will send the event, followed by the event. . This notification type is not saved, and will not be found in the server replay buffer, if replay is supported by the server. The '''netconfd''' server will not include a element in this notification type. Notifications are printed to the log, using the current '''$$display-mode''' system variable setting, when and if they are received. Notifications are also logged. Use the '''eventlog''' command to access the notifications stored in the event log. === Configuration Parameters That Affect Sessions === The '''--server''',''' --user''', and '''--password''' configuration parameters can be used to start a NETCONF session automatically at startup time. The connect command will only be attempted at startup time if the '''--server '''parameter is present. If all 3 of these parameters are present at startup time, then no interactive prompting for additional optional parameters will be done. Instead the connect command will be invoked right away. During normal operation, the '''--optional''' configuration parameter, or the '''$$optional''' system variable, can be used to control interactive prompting for optional parameters. The '''--server''' parameter is saved in the '''$$server''' system variable, which can be overwritten at any time. If set, this will be used as the initial default value for the '''--server''' parameter in the '''connect''' command. The '''--fixorder''' configuration parameter can be used to control XML PDU ordering. If set to 'true', then the PDU will be reordered (if needed),to use the canonical order, according to the YANG specification. If 'false', the order parameters are entered at the command line will be their NETCONF PDU order as well. The default is 'true'. To send the server incorrectly ordered data structures on purposes, set this parameter to 'false'. The '''--user''' parameter is saved in the '''$$user''' system variable, which can be overwritten at any time. If set, this will be used as the initial default value for the '''--user''' parameter in the '''connect''' command. The '''--with-defaults''' configuration parameter, or the '''$$with-defaults''' system variable, can be used to set the default value for the 'with-defaults' parameter extension for the NETCONF '''get''', '''get-config''', and '''copy-config''' protocol operations. The default is 'none'. The '''--error-option''' configuration parameter, or the '''$$error-option''' system parameter, can be used to set the default value for the '''--error-option''' parameter for the NETCONF edit-config protocol operation. The default is 'none'. The '''--test-option''' configuration parameter, or the '''$$test-option''' system parameter, can be used to set the default value for the '''--test-option''' parameter for the NETCONF edit-config protocol operation. The default is 'none'. The '''--bad-data''' configuration parameter, or the '''$$bad-data''' system variable, can be used to control how '''yangcli''' handles parameter values that are known to be invalid, or usage of optional protocol operations that the current session does not support. The default value is 'check'. To use '''yangcli''' in a testing mode to send the server incorrect data on purpose, set this parameter to 'warn' or 'ignore'. === Trouble-shooting NETCONF Session Problems === If the NETCONF session does not start for any reason, one or more error messages will be printed, and the prompt will indicate 'idle' mode. This section assumes that the server is '''netconfd''', and these debugging steps may not apply to all NETCONF agents. '''If the NETCONF session does not start:''' * make sure the server is reachable ** try to 'ping' the server and see if it responds * make sure the SSH server is responding ** try to login in to the server using normal SSH login on port 22 * make sure a firewall is not blocking TCP port 830 ** try to connect to the NETCONF server using the '''--port=22''' option * make sure the netconf-subsystem is configured correctly in /etc/ssh/sshd_configthere should be the proper configuration commands for NETCONF to work. For example, the '''netconfd''' server configuration might look like this: * Port 22 Port 830 Subsystem netconf /usr/sbin/netconf-subsystem * make sure the '''netconfd''' server is running. Use the unix 'ps' command, or check the '''netconfd''' log file, to make sure it is running.
ps -alx | grep netconf (look for 1 'netconfd and N 'netconf-subsystem' -- 1 for each active session) * make sure the user name is correct ** This must be a valid user name on the system * make sure the password is correct ** This must be the valid password (in /etc/passwd or /etc/shadow) for the specified user name '''If the NETCONF session stops responding:''' * make sure the server is still reachable ** try to 'ping' the server and see if it responds * make sure the SSH server is still responding ** try to login in to the server using normal SSH login on port 22 '''If the NETCONF server is not accepting a certain command:''' * make sure the command (or parameters used in the command) is actually supported by the server. ** There may be features, when statements, or deviation statements that indicate the server does not support the command or one or more of its parameters. * make sure that access control configured on the server is not blocking the command. The error-tag should be 'access-denied' in this case. '''If the NETCONF server is not returning the expected data in a or protocol operation::''' * Make sure all the parameters are supported by the server ** the''' :xpath''' capability must be advertised by the server to use the 'select' attribute in the or operations ** the ''':with-defaults''' capability must be advertised by the server to use the parameter * if using a filter, try to retrieve the data without a filter and see if it is there * make sure that access control configured on the server is not blocking the retrieval. There will not be any error reported in this case. The server will simply skip over any unauthorized data, and leave it out of the . * set the logging level to debug2 or higher, and look closely at the PDUs being sent to the server. Set the display mode to a value other than 'plain' to make sure the correct namespaces are being used in the request. '''If an operation is failing unexpectedly:''' * make sure that access control configured on the server is not blocking the request. The error-tag should be 'access-denied' in this case. * make sure an unsupported parameter or parameter value is not used ** is not supported unless the ''':validate''' capability is advertised by the server ** = 'rollback-on-error' is not supported unless the ''':rollback-on-error''' capability is advertised by the server * if the request contains an edit to a nested data structure, make sure the parent data structure(s) are in place as expected. The parameter is set to 'none' in the high level editing operations, so any data 'above' the edited data must already exist. * set the logging level to debug2 or higher, and look closely at the PDUs being sent to the server. Set the display mode to a value other than 'plain' to make sure the correct namespaces are being used in the request. == Command Reference == This section describes all the '''yangcli''' local and remote commands built-in when using the '''netconfd''' server. There may be more or less commands available, depending on the YANG modules actually loaded at the time. The specific NETCONF capabilities needed are listed for each remote command. No capabilities are ever needed for a local command. It is possible that other servers will support protocol operations that '''netconfd''' does not support. If yangcli has the YANG file available for the module, then it can be managed with the high level commands. Low-level commands can still be used with external data (e.g., @mydatafile.xml). Any YANG '''rpc''' statement can be used as a remote '''yangcli''' command. Refer to the server vendor documentation for details on the protocol operations, database contents, and notification definitions that they support. === alias === The '''alias''' command is used to set or display a specific command alias, or display all command aliases if no parameter is given. It is similar to the 'alias' command in unix shells such as 'bash'. There are 3 forms of the '''alias''' command: # '''alias''': display all command aliases in memory # '''alias ''': display the command alias with the specified name # '''alias =''': set the command alias with the specified name to the string value Use the '''unset''' command to delete an alias.
'''alias command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | var |- | Min parameters: | 0 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''var''' ** type: string ** usage: optional ** default: none ** The ''''var'''' string must contain either a valid alias name or a string setting an alias. There cannot be any whitespace between the '=' and other characters when setting an alias. The alias value must be quoted if it contains whitespace. *** A double-quoted string can contain single quotes: alias get-eth0=â€xget /interfaces/interface[name='eth0']†*** A single-quoted string can contain double quotes: alias get-eth0='xget /interfaces/interface[name=â€eth0â€]' *** An unquoted string can be used if the does not contain any whitespace: alias gc=get-config Positive Response: * If no parameter given: ** The current list of command aliases is displayed. * If a parameter given: ** The specified alias is displayed. * If a = parameter is given: ** 'Updated alias foo' if alias foo already exists. ** 'Added alias foo' if this is a new alias Negative Response: * If no parameter given: ** Not applicable * If a parameter given: ** Error: alias 'foo' not found * If a = parameter is given: ** Error message printed if value is invalid Usage: yangcli> '''alias get-running=â€get-config --source=runningâ€''' Added alias 'get-running' yangcli> === aliases === The '''aliases '''command is used to load, save, or clear the command aliases, or display all command aliases if no parameter is given. There are 4 forms of the '''aliases''' command: # '''aliases [show]''': display all command aliases in memory # '''aliases clear''': clear all command aliases in memory # '''aliases save [alias-filespec]''': save the command aliases in memory in an '.aliases' file. # '''aliases load [alias-filespec]''': load the command aliases from an '.aliases' file into memory.
'''aliases command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''alias-action''' ** type: choice ** usage: optional ** default: show ** '''clear''' *** Delete all aliases from memory. This will not affect the aliases file until the ''''aliases''' save' command is used, or the program exits and the '''–autoaliases''' parameter is set to 'true'. ** '''load''' [alias-filespec] *** Load an aliases file into memory. If the 'alias-filespec' parameter is not given, then the default aliases file ($HOME/.yuma/.yangcli_aliases) will be used. ** '''save''' [alias-filespec] *** Save the command aliases into memory to an alias file. If the 'alias-filespec' parameter is not given, then the default aliases file ($HOME/.yuma/.yangcli_aliases) will be used. ** '''show''' *** Displays all aliases in memory. Positive Response: * show: * ** The current list of command aliases is displayed. * clear, load, save: ** A status message is displayed. Negative Response: * An error message is printed if any error occurs. Usage: yangcli> '''aliases save''' Saved aliases OK to '~/.yuma/.yangcli_aliases' yangcli> === cd === The '''cd''' command is used to change the current working directory.
'''cd command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | dir |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''dir''' ** type: string ** usage: mandatory ** default: none ** The ''''dir'''' string must contain a valid directory specification Positive Response: * the new current working directory is printed Negative Response: * an error message will be printed describing the error Usage: yangcli> '''cd ~/modules''' Current working directory is /home/andy/modules yangcli> '''cd --dir=$YUMA_HOME''' Current working directory is /home/andy/swdev/yuma/trunk/netconf yangcli> === close-session === The '''close-session''' command is used to terminate the current NETCONF session. A NETCONF server should always accept this command if it is valid, and not reject it due to access control enforcement or if the server is in notification delivery mode.
'''close-session command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | status |- | YANG file: | yuma-netconf.yang |} Command Parameters: * none Positive Response: * the session is terminated and the command prompt is changed to indicate idle mode Negative Response: * an message will be printed describing the error Usage: yangcli andy@myserver> '''close-session''' RPC OK Reply 2 for session 10: yangcli> Reference: * RFC 4741, section 7.8 === commit === The '''commit''' command is used to save the edits in the database into the database. If there are no edits it will have no effect.
'''commit command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 2 |- | Return type: | status |- | YANG file: | yuma-netconf.yang |- | Capabilities needed: | :candidate |- | Capabilities optional: | :confirmed-commit |} Command Parameters: * '''confirmed''' ** type: empty ** usage: optional ** default: none ** capabilities needed:''' ''':confirmed-commit ** This parameter requests a confirmed commit procedure. The server will expect another '''commit''' command before the '''confirm-timeout''' time period expires. * '''confirm-timeout''' ** type: uint32 (range: 1 .. max) ** usage: optional ** default: 600 seconds * ** capabilities needed:''' ''':confirmed-commit * ** This is the number of seconds to request before the timeout.The ''''confirmed'''' leaf must also be present for this parameter to have any affect. * '''persist''' ** type: string ** usage: optional ** persist ID used to start or extend the confirmed commit procedure * '''persist-id''' ** type: string ** usage: optional ** persist ID used to conform a previously started the confirmed commit procedure Positive Response: * the session is terminated and the command prompt is changed to indicate idle mode Negative Response: * an message will be printed describing the error Usage: yangcli andy@myserver> '''commit''' RPC OK Reply 5 for session 10: yangcli [mailto:andy@www.example.com andy@myserver]> Reference: * RFC 4741, section 8.3.4 === connect === The '''connect''' command is used to start a session with a NETCONF server. If there already is a NETCONF session active, then an error message will be printed and the command will not be executed.
'''connect command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | server |- | Min parameters: | 3 |- | Max parameters: | 8 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''server''' ** type: inet:ip-address (string containing IP address or DNS name ** usage: mandatory ** default: previous server used, if any, will be presented as the default, but not used automatically ** This parameter specifies the server address for the session. * '''password''' ** type: string (ncx:password) ** usage: mandatory ** default: previous password used, if any, will be presented as the default, but not used automatically ** This parameter specifies the password string to use to establish the session. It will not be echoed in parameter mode or saved in the command history buffer. * '''port''' ** type: uint16 ** usage: optional ** default: 830 ** This parameter specifies the TCP port number that should be used for the session. * '''timeout''' ** type: uint32 (0 = no timeout, otherwise the number of seconds to wait) ** usage: optional ** default: set to the '''$$timeout''' system variable, default 30 seconds ** This parameter specifies the number of seconds to wait for a response from the server before giving up. The session will not be dropped if a remote command times out, but any late response will be dropped. A value of zero means no timeout should be used, and '''yangcli''' will wait forever for a response. * '''user''' ** type: string ** usage: mandatory ** default: previous user name used, if any, will be presented as the default, but not used automatically ** This parameter specifies the user name to use for the session. This must be a valid user name on the NETCONF server. * '''protocols''' ** type: bits (netconf1.0 netconf1.1) ** usage: optional ** default: '''--protocols''' configuration parameter setting ** Specifies which NETCONF protocol versions to enable. Overrides '''–protocols '''configuration parameter. * '''private-key''' ** type: string ** usage: optional ** default: --private-key configuration parameter setting ** Specifies the SSH private key file to use. Overrides --private-key configuration parameter. * '''public-key''' ** type: string ** usage: optional ** default: '''--public-key''' configuration parameter setting ** Specifies the SSH public key file to use. Overrides '''--public-key '''configuration parameter. Positive Response: * the session is started and the prompt changes to include the [mailto:'user@agent 'user@server]' string. Negative Response: * One or more error messages will be printed. Refer to the section on trouble-shooting NETCONF Session problems for more details. Usage: yangcli> '''connect [http://www.example.com/ myserver] user=andy password=yangrocks''' yangcli [mailto:andy@www.example.com andy@myserver]> === copy-config === The '''copy-config''' command is used to copy one entire NETCONF database to another location. Not all possible parameter combinations will be supported by every server. In fact, the NETCONF protocol does not require any parameters to be supported unless the ''':startup''' or ''':url''' capabilities is supported by the server. If the server supports the :startup capability, then it must support: yangcli [mailto:andy@myagent andy@myserver]> '''copy-config source=running target=startup''' This is the standard way to save a snapshot of the current running configuration in non-volatile storage, if the server has a separate startup database. If not, the server will automatically save any changes to the running configuration to non-volatile storage.
'''copy-config command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 2 |- | Max parameters: | 3 |- | Return type: | status |- | YANG file: | yuma-netconf.yang |- | Capabilities needed: | none |- | Capabilities optional: | :candidate:startup:url:with-defaults |} Command Parameters: * '''source''' ** type: container with 1 of N choice of leafs ** usage: mandatory ** default: none ** This parameter specifies the name of the source database for the copy operation. ** container contents: 1 of N: *** '''candidate''' **** type: empty **** capabilities needed:''' ''':candidate *** '''running''' **** type: empty **** capabilities needed: none *** '''startup''' **** type: empty **** capabilities needed: startup *** '''config:''' **** type: container (in-line configuration data) **** capabilities needed: none *** '''url''' **** type: yang:uri **** capabilities needed: :url, and the scheme used in the URL must be specified in the :url capability 'schemes' parameter **** To enter this parameter, the interactive mode must be used. The shorthand mode (source=url) cannot be used, since this parameter contains a string. * '''target''' ** type: container with 1 of N choice of leafs ** usage: mandatory ** default: none ** This parameter specifies the name of the target database for the copy operation. ** container contents: 1 of N: *** '''candidate''' **** type: empty **** capabilities needed:''' ''':candidate *** '''running''' **** type: empty **** capabilities needed: :writable-running (still optional to implement) ***** '''netconfd''' does not support this mode *** '''startup''' **** type: empty **** capabilities needed: startup *** '''url''' **** type: yang:uri **** capabilities needed: :url, and the scheme used in the URL must be specified in the :url capability 'schemes' parameter. **** To enter this parameter, the interactive mode must be used. The shorthand mode (target=url) cannot be used, since this parameter contains a string. * '''with-defaults''' ** type: enumeration (none report-all report-all-tagged trim explicit) ** usage: optional ** default: none ** capabilities needed: with-defaults ** This parameter controls how nodes containing only default values are copied to the target database. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''copy-config source=candidate''' Enter a number of the selected case statement: 1: case candidate: leaf candidate 2: case running: leaf running 3: case startup: leaf startup 4: case url: leaf url Enter choice number [1 - 4]: yangcli andy@myserver:copy-config> '''4''' Filling optional case /copy-config/input/target/config-source/url Enter string value for leaf : yangcli andy@myserver:copy-config> [smb://configs/myconfig.xml file://configs/myconfig.xml] RPC OK Reply 12 for session 10: yangcli andy@myserver> Reference: * RFC 4741, section 7.3 === create === The '''create''' command is a high-level operation. It is used to create some new nodes in the default target database. A target node is specified (in 1 of 2 ways), and then the data structure is filled in. Only mandatory nodes will be filled in unless the '''$$optional''' system variable is set to 'true'. Refer to the '''fill''' command for more details on interactive mode data structure completion.
'''create command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | target |- | Min parameters: | 1 |- | Max parameters: | 5 |- | Return type: | status |- | YANG file: | yangcli.yang |- | Capabilities needed: | :candidate or :writable-running |} Command Parameters: * '''choice 'from' '''(not entered) ** type: choice of case 'varref' or case 'from-cli' ** usage: mandatory ** default: none ** This parameter specifies the where '''yangcli''' should get the data from, for the create operation. It is either a user variable or from interactive input at the command line. *** '''varref''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the name of the user variable to use for the target of the create operation. The parameter must exist (e.g., created with the '''fill''' command) or an error message will be printed. *** '''case from-cli '''(not entered) **** '''target''' ***** type: string ***** usage: mandatory ***** default: none ***** This parameter specifies the database target node of the create operation. This is an '''ncx:schema-instance''' string, so any instance identifier, or absolute path expression, or something in between, is accepted. **** '''urltarget''' ***** type: string ***** usage: optional ***** default: none ***** This parameter specifies the database target node of the create operation. This is an '''UrlPath''' string. **** '''optional''' ***** type: empty ***** usage: optional ***** default: controlled by '''$$optional''' system variable ***** This parameter controls whether optional nodes within the target will be filled in. It can be used to override the '''$$optional '''system variable, when it is set to 'false'. **** '''value''' ***** type: anyxml ***** usage: mandatory ***** default: none ***** This parameter specifies the value that should be used for the contents of the '''target''' parameter. If it is entered, then the interactive mode prompting for missing parameters will be skipped, if this parameter is complete (or all mandatory nodes present if the '''$$optional''' system variable is 'false'). For example, if the target is a leaf, then specifying this parameter will always cause the interactive prompt mode to be skipped. * '''timeout''' ** type: uint32 (0 = no timeout, otherwise the number of seconds to wait) * ** usage: optional ** default: set to the '''$$timeout''' system variable, default 30 seconds ** This parameter specifies the number of seconds to wait for a response from the server before giving up. The session will not be dropped if a remote command times out, but any late response will be dropped. A value of zero means no timeout should be used, and yangcli will wait forever for a response. System Variables: * '''$$default-operation''' ** The parameter for the operation will be derived from this variable. * '''$$error-option''' ** The parameter for the operation will be derived from this variable * '''$$test-option''' ** The parameter for the operation will be derived from this variable Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''create varref=myvar''' RPC OK Reply 10 for session 10: yangcli [mailto:andy@www.example.com andy@myserver]> '''create /nacm/rules/data-rule''' \ (user will be prompted to fill in the data-rule contents) RPC OK Reply 11 for session 10: yangcli [mailto:andy@www.example.com andy@myserver]> '''create \''' '''target=/nacm/rules/data-rule[name='test rule']/comment''' \ '''value=â€this test rule is temporary. Do not remove!â€''' (no user prompting; request sent right away) RPC OK Reply 12 for session 10: yangcli andy@myserver> Reference: * RFC 4741, section 7.2 === create-subscription === The create-subscription command is used to start receiving notifications from the server. The :notification capability must be supported by the server to use this command. Unless the :interleave capability is also supported by the server, then only the '''close-session''' command can be used while notifications are being delivered.
'''create-subscription command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 4 |- | Return type: | status |- | YANG file: | notifications.yang |- | Capabilities needed: | :notification |- | Capabilities optional: | :interleave |} Command Parameters: * '''stream''' ** type: string ** usage: optional ** default: 'NETCONF' ** This parameter specifies the name of the notification stream for this subscription request. Only the 'NETCONF' stream is mandatory to implement. Any other stream contains vendor-specific content, and may not be fully supported, depending on the stream encoding. * '''filter''' ** type: anyxml (same as the or filter parameter) ** usage: optional ** default: none ** This parameter specifies a boolean filter that should be applied to the stream. This is the same format as the standard element in RFC 4741, except that instead of creating a subset of the database for an PDU, the filter is used as a boolean test to send or drop each notification delivered from the server. *** If any nodes are left in the 'test response', the server will send the entire notification. *** If the result is empty after the filter is applied to the “test responseâ€, then the server will not send the notification at all. *** It is possible that access control will either cause the a notification to be dropped entirely, or may be pruned and still delivered. The standard is not clear on this topic. The '''netconfd''' server will prune any unauthorized payload from an eventType, but if the itself is unauthorized, the entire notification will be dropped. * '''startTime''' ** type: yang:date-and-time ** usage: optional ** default: none ** This parameter causes any matching replay notifications to be delivered by the server, if notification replay is supported by the server. A notification will match if its value is greater or equal to the value of this parameter. ** After all the replay notifications are delivered, the server will send a eventType, indicating there are no more replay notifications that match the subscription request. * '''stopTime''' ** type: yang:date-and-time ** usage: optional (not allowed unless startTime is also present) ** default: none ** This parameter causes any matching replay notifications to be delivered by the server, if notification replay is supported by the server. A notification will match if its value is less than the value of this parameter. *** This parameter must be greater than the '''startTime''' parameter, or an error will be returned by the server. *** If this parameter is used, then the entire subscription will stop after this specified time, even if it is in the future. The eventType will be sent by the server when this event occurs. *** If this parameter is not used (but '''startTime''' is used), then the server will continue to deliver 'live' notifications after the eventType is sent by the server. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''create-subscription''' RPC OK Reply 13 for session 10: yangcli [mailto:andy@www.example.com andy@myserver]> '''create-subscription \''' '''startTime=2009-01-01T00:00:00Z''' RPC OK Reply 14 for session 10: yangcli [mailto:andy@myagent andy@myserver]> Reference: * RFC 5277, section 2.1.1 === delete === The '''delete''' command is a high-level operation. It is used to delete an existing subtree in the default target database. A target node is specified, and then any missing key leafs (if any) within the data structure are filled in. If the target is a leaf-list, then the user will be prompted for the value of the leaf-list node to be deleted. Refer to the '''fill''' command for more details on interactive mode data structure completion.
'''delete command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | target |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yangcli.yang |- | Capabilities needed: | :candidate or :writable-running |} Command Parameters: * '''target''' ** type: string ** usage: optional (urltarget or target must be present) ** default: none ** This parameter specifies the database target node of the delete operation. This is an '''ncx:schema-instance''' string, so any instance identifier, or absolute path expression, or something in between, is accepted. * '''urltarget''' ** *** **** ***** type: string ***** usage: optional ***** default: none ***** This parameter specifies the database target node of the delete operation. This is an '''UrlPath''' string. System Variables: * '''$$default-operation''' ** The parameter for the operation will be derived from this variable. * '''$$error-option''' ** The parameter for the operation will be derived from this variable * '''$$optional''' ** Controls whether optional descendant nodes will be filled into the '''target''' parameter contents * '''$$test-option''' ** The parameter for the operation will be derived from this variable Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli [mailto:andy@www.example.com andy@myserver]> delete''' /nacm/rules/data-rule''' \ (user will be prompted to fill in the data-rule 'name' key leaf) RPC OK Reply 15 for session 10: yangcli [mailto:andy@www.example.com andy@myserver]> delete''' \''' '''target=/nacm/rules/data-rule[name='test rule']/comment''' (no user prompting; request sent right away) RPC OK Reply 16 for session 10: yangcli Reference: * RFC 4741, section 7.2 === delete-config === The '''delete-config''' command is used to delete an entire NETCONF database. Not all possible '''target''' parameter values will be supported by every server. In fact, the NETCONF protocol does not require that any database be supported by this operation. If the server supports the :url capability, then it may support deletion of local file databases in this manner.:
'''delete-config command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yuma-netconf.yang |- | Capabilities needed: | none |- | Capabilities optional: | :candidate:startup:url |} Command Parameters: * '''target''' ** type: container with 1 of N choice of leafs ** usage: mandatory ** default: none ** This parameter specifies the name of the target database for the delete operation. ** container contents: 1 of N: *** '''startup''' **** type: empty **** capabilities needed: startup **** a server may support this target *** '''url''' **** type: yang:uri **** capabilities needed: :url, and the scheme used in the URL must be specified in the :url capability 'schemes' parameter. **** To enter this parameter, the interactive mode must be used. The shorthand mode (target=url) cannot be used, since this parameter contains a string. **** a server may support this parameter Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''delete-config target=startup''' RPC OK Reply 17 for session 10: yangcli [mailto:andy@myagent andy@myserver]> Reference: * RFC 4741, section 7.4 === discard-changes === The '''discard-changes''' command is used to delete any edits that exist in the database, on the NETCONF server. The server will only accept this command if the :candidate capability is supported. If the database is locked by another session, then this request will fail with an 'in-use' error.
'''discard-changes command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | status |- | YANG file: | yuma-netconf.yang |- | Capabilities needed: | :candidate |} Command Parameters: none Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''discard-changes''' RPC OK Reply 18 for session 10: yangcli [mailto:andy@myagent andy@myserver]> Reference: * RFC 4741, section 8.3.4.2 === edit-config === The edit-config command allows a subset of a NETCONF database on the server to be changed. If the server supports the ''':url''' capability, then it may support editing of local file databases. If the server supports the :candidate capability, then it will allow edits to the database. If the server supports the :writable-running capability, it will support edits to the database. It is not likely that a server will support the and database as targets at the same time, since changes to the configuration would not be reflected in the database, while it was being edited by a different session.
'''edit-config command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 2 |- | Max parameters: | 5 |- | Return type: | status |- | YANG file: | yuma-netconf.yang |- | Capabilities needed: | :candidate or :writable-running |- | Capabilities optional: | :url:rollback-on-error:validate |} Command Parameters: * '''default-operation''' ** type: enumeration (merge replace none) ** usage: optional ** default: merge ** This parameter specifies which edit operation will be in effect at the start of the operation, before any '''nc:operation '''attribute is found. *** The high-level edit operations provided by '''yangcli''' will set this parameter to 'none'. This is the safest value, since only subtrees that have an explicit '''nc:operation''' attribute in effect can possibly be altered by the command. *** If the value is 'merge', then any missing nodes in the database will be automatically created as needed. *** If the value is 'replace', then the target database will be pruned to match the edits, as needed. Only the data from the '''config''' parameter will remain if this value is used. (Use with extreme caution). * '''error-option''' ** type: enumeration (stop-on-error continue-on-error rollback-on-error ** usage: optional ** default: stop-on-error ** This parameter specifies what the server should do when an error is encountered. *** The rollback-on-error value is only allowed if the ''':rollback-on-error''' capability is supported by the server. *** The standard is not clear what continue-on-error really means. It is suggested that this value not be used. It is possible that the server will validate all input parameters before making any changes, no matter how this parameter is set. * '''choice edit-content''' (not entered) ** '''config''' *** type: anyxml *** usage: mandatory *** default: none *** This parameter specifies the subset of the database that should be changed. This is the most common way to edit a NETCONF server database, since it is mandatory to support by all agents. ** '''url''' *** type: yang:uri *** capabilities needed:''' :url,''' and the scheme used in the URL must be specified in the ''':url''' capability 'schemes' parameter. *** To enter this parameter, the interactive mode must be used. The shorthand mode (target=url) cannot be used, since this parameter contains a string. * '''target''' ** type: container with 1 of N choice of leafs ** usage: mandatory ** default: none ** This parameter specifies the name of the target database for the edit operation. ** container contents: choice: 1 of N: *** '''candidate''' **** type: empty **** capabilities needed:''' ''':candidate *** '''running''' **** type: empty **** capabilities needed: :writable-running * '''test-option''' ** type: enumeration (test-then-set set test-only) ** usage: optional ** default: set ** This parameter specifies how the server should test the '''edit-content''' parameter before using it. *** If the value is 'set' (normal case), the server will apply validation tests as needed for the individual data structures being edited *** The value 'test-then-set' is only allowed if the :validate capability is supported by the server. The server will test if the entire database will be valid after the edits are made, before making any changes to the candidate configuration. **** This mode is very resource intensive. Set this parameter to 'set' for better performance, and run the validation tests manually with the '''validate''' command. *** The value 'test-only' is not supported by all agents. It will be in the next version of the NETCONF protocol, but is non-standard at this time. **** Use this value to check if a specific edit should succeed or not, allowing errors to be corrected before altering the database for real. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''edit-config target=candidate \''' '''default-operation=merge \''' '''test-option=test \''' '''error-option=stop-on-error \''' '''config=@myconfig.xml''' RPC OK Reply 19 for session 10: yangcli [mailto:andy@myagent andy@myserver]> Reference: * RFC 4741, section 7.2 === elif === The '''elif''' command is used to define a conditional command block after an if command. This command must be entered within the same script as the if command, when used within a script. It can be used zero or more times within an if command sequence. The '''expr''' parameter is used to specify the XPath expression to test if the elif conditional block is true or false. A false block will be skipped and a true block will be executed. The command prompt will contain the string '[F]' while inside a false conditional block in interactive mode. This expression string should be entered with quotes to avoid misinterpreting any whitespace or special characters. The '''docroot''' parameter (if present) specifies the XML document that the 'expr' parameter should be evaluated against. This is optional, since only XPath path expressions need to refer to a document. Even if the 'expr' expression is true, the conditional block will only be executed if no conditional block in the if command sequence has already been executed. if command .... [elif command] .... [elif-command] ... [else command] ... end command
'''elif command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | expr |- | Min parameters: | 1 |- | Max parameters: | 2 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''expr''' ** type: XPath expression string ** usage: mandatory ** default: none ** This parameter specifies the XPath expression to determine if the following commands are within a true or false conditional block. * '''docroot''' ** type: anyxml ** usage: optional (typically a variable reference is used) ** default: none ** This parameter specifies the XML document that should be used if the expr XPath expression references any path nodes. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. ** elif without a previous if command will cause an error ** elif following an 'else' command will cause an error ** invalid XPath expression or invalid docroot reference will cause an error Usage: yangcli andy@myserver> '''elif expr='$x > 4'''' yangcli [mailto:andy@myagent andy@myserver]> === else === The '''else''' command is used to define a final conditional command block after an if command. This command must be entered within the same script as the if command, when used within a script. It can be used zero or one time within an if command sequence. The conditional command block following the else command will only be executed if no conditional block has already been executed in the same if command sequence. if command .... [elif command] .... [elif-command] ... [else command] ... end command
'''else command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: none Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. ** else without a previous if command will cause an error Usage: yangcli andy@myserver> '''else''' yangcli [mailto:andy@myagent andy@myserver]> === end === The '''end''' command is used to terminate a conditional command block after an if command block, or after a 'while' command. This command must be entered within the same script as the if or while command, when used within a script. if command .... [elif command] .... [elif-command] ... [else command] ... end command while command .... end command
'''end command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: none Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. ** else without a previous if command will cause an error Usage: yangcli andy@myserver> '''end''' yangcli [mailto:andy@myagent andy@myserver]> === eval === The '''eval''' command is used to evaluate an XPath expression.. The '''expr''' parameter is used to specify the XPath expression to evaluate. This expression string should be entered with quotes to avoid misinterpreting any whitespace or special characters. The '''docroot''' parameter (if present) specifies the XML document that the 'expr' parameter should be evaluated against. This is optional, since only XPath path expressions need to refer to a document.
'''eval command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | expr |- | Min parameters: | 1 |- | Max parameters: | 2 |- | Return type: | data |- | YANG file: | yangcli.yang |} Command Parameters: * '''expr''' ** type: XPath expression string ** usage: mandatory ** default: none ** This parameter specifies the XPath expression to determine if the following commands are within a true or false conditional block. * '''docroot''' ** type: anyxml ** usage: optional (typically a variable reference is used) ** default: none ** This parameter specifies the XML document that should be used if the expr XPath expression references any path nodes. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. ** elif without a previous if command will cause an error ** elif following an 'else' command will cause an error ** invalid XPath expression or invalid docroot reference will cause an error Output: * '''data''' ** type: anyxml ** This element will contain the result from the XPath expression. A node-set result will produce a complex element return value, and all other XPath result types will produce a string return value. Usage: yangcli andy@myserver> '''$x = eval '$x + 1'''' yangcli [mailto:andy@myagent andy@myserver]> '''$sysname = eval '//sysName' docroot=$backup''' === eventlog === The '''eventlog''' command is used to view or clear all or part of the notification event log. This log will be empty if no well-formed notifications have been received from any server. The '''eventlog show''' command is used to display some event log entries. The''' eventlog clear''' command is used to delete some event log entries. If no parameters are entered, it is the same as entering 'eventlog show=-1'. The event log is not automatically emptied when a session terminates, in case the session was dropped unexpectedly. New entries will be appended to the event log as new sessions and/or subscriptions are started.
'''eventlog command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | show |- | Min parameters: | 0 |- | Max parameters: | 3 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''choice eventlog-action '''(not entered): ** type: choice of case 'show-case' or leaf 'clear' ** usage: optional ** default: show=-1 is used as the default if nothing entered ** This parameter specifies the event log action that should be performed. *** '''clear''' **** type: int32 (-1 to clear all entries; 1 to max to delete N entries) **** usage: optional **** default: -1 **** This parameter specifies the maximum number of event log entries to be deleted, starting from the oldest entries in the event log. The value -1 means delete all the entries. Otherwise the value must be greater than zero, and up to that many entries will be deleted. *** '''case show-case''' (not entered) **** '''choice help-mode''' (not entered) (default choice is 'normal') ***** '''brief''' ****** type: empty ****** usage: optional ****** default: none ****** This parameter specifies that brief documentation mode should be used. The event log index, sequence ID, and will be displayed in this mode. ***** '''normal''' ****** type: empty ****** usage: optional ****** default: none ****** This parameter specifies that normal documentation mode should be used. The event log index, , sequence ID, and will be displayed in this mode. ***** '''full''' ****** type: empty ****** usage: optional ****** default: none ****** This parameter specifies that full documentation mode should be used. The event log index, , sequence ID, and will be displayed in this mode. In addition, the entire contents of the notification PDU will be displayed, using the current '''$$display-mode''' setting. **** '''show''' ***** type: int32 (-1 for all, 1 to max for N entries) ***** usage: optional ***** default: -1 ***** This parameter specifies the number of event log entries that should be displayed. The value '-1' indicates all the entries should be displayed. Otherwise, the value must be greater than zero, indicating the number of entries to display. **** '''start''' ***** type: uint32 ***** usage: optional ***** default: 0 ***** This parameter specifies the start position in the event log to start displaying entries. The first entry is number zero. Each time the event log is cleared, the numbering restarts. System Variables: * $$display-mode ** The log entries printed when help-mode='full' are formatted according to the current value of this system variable. Positive Response: * the event log entries are printed or cleared as requested Negative Response: * An error message will be printed if errors are detected. Usage: yangcli andy@myserver> '''eventlog show=5 start=3''' [3] [2009-07-10T02:21:10Z] (4) [4] [2009-07-10T02:23:14Z] (5) [5] [2009-07-10T02:23:23Z] (6) [6] [2009-07-10T02:24:52Z] (7) [7] [2009-07-10T02:24:57Z] (8) yangcli andy@myserver> === fill === The '''fill''' command is used to create a user variable for reuse in other commands. It is used in an assignment statement to create a variable from various sources. If it is not used in an assignment statement, then the result will not be saved, so the command will have no effect in this case. The value contents will mirror the subtree within the NETCONF database indicated by the target parameter. If not completely provided, then missing descendant nodes will be filled in interactively, by prompting for each missing node.
'''fill command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | target |- | Min parameters: | 2 |- | Max parameters: | 3 |- | Return type: | data |- | YANG file: | yangcli.yang |} Command Parameters: * '''optional''' ** type: empty ** usage: optional ** default: controlled by '''$$optional''' system variable ** This parameter controls whether optional nodes within the target will be filled in. It can be used to override the''' $$optional''' system variable, when it is set to 'false'. * '''target''' ** type: string ** usage: mandatory ** default: none ** This parameter specifies the database target node of the create operation. This is an '''ncx:schema-instance '''string, so any instance identifier, or absolute path expression, or something in between, is accepted. * '''value''' ** type: anyxml ** usage: mandatory ** default: none ** This parameter specifies the content to use for the filled variable. *** If this parameter is not entered, then the user will be prompted interactively to fill in the '''target''' data structure. *** If a string is entered, then the target value being filled must be a leaf or leaf-list. *** If a variable reference is entered, then it will be used as the content, if the target value being filled is a leaf or a leaf-list. *** If the target value is a complex object, then the referenced variable must also be a complex object of the same type. *** An error will be reported if the global or local variable does not reference the same object type as the target parameter. System Variables: * $$optional ** Controls whether optional descendant nodes will be filled into the '''target''' parameter contents Positive Response: * OK Negative Response: * An error message will be printed if errors are detected. Output: * data ** type: anyxml ** The data structure will mirror the requested target object. ** The variable (if any) will retain the target object name and namespace so it can be used in other operations more easily. In the example below, the '''$my_interface''' local variable will have the module name 'interfaces' and name 'interface', when used in other commands such as '''create''' or '''merge'''. Usage: yangcli> '''$my-interface = fill \''' '''target=/interfaces/interface optional''' (user will be prompted to fill in all fields of the element) OK yangcli> === get === The '''get''' command is used to retrieve data from the server.
'''get command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 2 |- | Return type: | data |- | YANG file: | yuma-netconf.yang |} Command Parameters: * '''filter''' ** type: anyxml ** usage: optional ** default: none ** This parameter specifies a boolean filter that should be applied to the stream. Any data in the database (or non-config data) that does not match the filter will be left out of the response. *** If no filter is used, the server will return the entire database and all non-config data as well. This could be a lot of data, depending on the server. *** If the result is empty after the filter is applied to the available data, then the server will send an empty element in the *** It is possible that access control will cause the to be pruned. The '''netconfd''' server will silently prune any unauthorized payload from the . * '''with-defaults''' ** type: enumeration (none report-all report-all-tagged trim explicit) ** usage: optional ** default: none ** capabilities needed: with-defaults ** This parameter controls how nodes containing only default values are returned in the . Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Output: * '''data''' ** type: anyxml ** This element will contain the requested data from the '''<'''running> database, or non-config data from the server instrumentation. Usage: yangcli [mailto:andy@www.example.com andy@myserver]> '''get''' RPC Data Reply 20 for session 10: rpc-reply { data { …. data returned by the server } } yangcli [mailto:andy@www.example.com andy@myserver]> '''get [mailto:filter%3D@myfilter.xml filter=@myfilter.xml] RPC Data Reply 21 for session 10: rpc-reply { data { } } (the previous response will occur if the filter did not match anything or the server access control filtered the entire response) yangcli [mailto:andy@myagent andy@myserver]> Reference: * RFC 4741, section 7.7 === get-config === The '''get-config''' command is used to retrieve configuration data from the server.
'''get-config command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 1 |- | Max parameters: | 3 |- | Return type: | data |- | YANG file: | yuma-netconf.yang |} Command Parameters: * '''filter''' ** type: anyxml ** usage: optional ** default: none ** This parameter specifies a boolean filter that should be applied to the stream. Any data in the database (or non-config data) that does not match the filter will be left out of the response. *** If no filter is used, the server will return the entire database and all non-config data as well. This could be a lot of data, depending on the server. *** If the result is empty after the filter is applied to the available data, then the server will send an empty element in the *** It is possible that access control will cause the to be pruned. The '''netconfd''' server will silently prune any unauthorized payload from the . * '''source''' ** type: container with 1 of N choice of leafs ** usage: mandatory ** default: none ** This parameter specifies the name of the source database for the retrieval operation. ** container contents: 1 of N: *** '''candidate''' **** type: empty **** capabilities needed:''' ''':candidate *** '''running''' **** type: empty **** capabilities needed: none *** '''startup''' **** type: empty **** capabilities needed: startup *** '''url''' **** type: yang:uri **** capabilities needed: :url, and the scheme used in the URL must be specified in the :url capability 'schemes' parameter **** To enter this parameter, the interactive mode must be used. The shorthand mode (source=url) cannot be used, since this parameter contains a string. * '''with-defaults''' ** type: enumeration (none report-all report-all-tagged trim explicit) ** usage: optional ** default: none ** capabilities needed: with-defaults ** This parameter controls how nodes containing only default values are returned in the . Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Output: * '''data''' ** type: anyxml ** This element will contain the requested data from the '''source''' database. Usage: yangcli [mailto:andy@www.example.com andy@myserver]> '''$my-config =''' '''get-config target=running''' RPC Data Reply 22 for session 10: rpc-reply { data { …. entire database returned by the server } } yangcli [mailto:andy@www.example.com andy@myserver]> '''@saved-config.xml =''' '''get-config \''' [mailto:filter%3D@myfilter.xml filter=@myfilter.xml] \''' '''target=candidate''' rpc-reply { data { … data requested by the filter } } yangcli [mailto:andy@myagent andy@myserver]> Reference: * RFC 4741, section 7.1 === get-locks === The '''get-locks''' command is a high-level wrapper for the operation. It is used to lock all the databases ( plus and/or if they exist). If all the locks cannot be obtained, then release all the locks that were obtained (all-or-nothing). The entire time to wait for a lock in use is set with the lock-timeout parameter. The retry-interval parameter is used when the operation fails with a 'lock-denied' error-tag, because some other session has the lock. If the cannot be locked for another reason, a operation will be attempted to clear any leftover edits. Normally, the errors received while attempting to acquire locks are not printed to the log, like normal commands. Instead, if $$log-level system parameter is set to 'debug2' or 'debug3', then these messages will be printed.
'''get-locks command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 3 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''lock-timeout''' ** type: uint32 (seconds) ** usage: optional ** default: 120 seconds (2 minutes) ** This parameter specifies how long to wait for a lock that is in use by another session. * '''retry-interval''' ** type: uint32 (seconds) ** usage: optional ** default: 2 seconds ** This parameter specifies how long to wait to retry for a lock. * '''cleanup''' ** type:boolean ** usage: optional ** default: true ** This parameter controls whether the 'release-locks' command will be called automatically if the entire set of required locks cannot be granted. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli [mailto:andy@www.example.com andy@myserver]> '''get-locks lock-timeout=0''' Sending operations for get-locks... RPC OK Reply 6 for session 23: RPC OK Reply 7 for session 23: get-locks finished OK yangcli [mailto:andy@myagent andy@myserver]> === get-my-session === The''' get-my-session '''command is used to retrieve the session customization parameters from the server. It is only supported by the '''netconfd''' server. The session indent amount, session line size, and default behavior for the with-defaults parameter can be controlled at this time.
'''get-my-session command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | data |- | YANG file: | mysession.yang |- | Capabilities needed: | none |} Command Parameters: * none Positive Response: * the server returns , , and elements Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Output: * '''indent''' ** type: uint32 (range 0 to 9) ** * '''linesize''' ** type: uint32 ** This parameter specifies the desired line length for the session. * '''with-defaults''' ** type: enumeration (none report-all trim explicit) ** This parameter specifies the desired default with-defaults filtering behavior for the session. ** yangcli [mailto:andy@www.example.com andy@myserver]>''' get-my-session''' RPC Data Reply 25 for session 10: rpc-reply { data { indent 3 linesize 72 with-defaults report-all } } yangcli [mailto:andy@myagent andy@myserver]> === get-schema === The''' get-schema''' command is used to retrieve data model definition files from the server. This is part of the NETCONF monitoring draft. The server must support the ''':schema-retrieval''' capability to use this command. If the server reports a module or module version that '''yangcli''' cannot find in its local module library, then an error message will be printed. The '''get-schema''' command can then be used to retrieve the missing module from the server. The '''ietf-netconf-monitoring.yang''' module includes a list of the schema supported by the server, which can be retrieved from a server that supports this module, such as '''netconfd'''. yangcli [mailto:andy@myagent andy@myserver]> '''sget /netconf-state/schemas''' The preceding command will return a container with several child nodes. One example entry is shown below: schemas { schema system 2009-06-04 YANG { identifier system version 2009-06-04 format YANG namespace http://netconfcentral.org/ns/yuma-system location NETCONF } } The , and leafs can be used as the corresponding parameter values for the '''get-schema''' command. See the example below for more details.
'''get-schema command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 3 |- | Max parameters: | 3 |- | Return type: | data |- | YANG file: | ietf-netconf-monitoring.yang |- | Capabilities needed: | :schema-retrieval |} Command Parameters: * '''identifier''' ** type: string ** usage: mandatory ** default: none ** This parameter specifies the name of the module to retrieve. *** Do not use any path specification of file extension; just the module name is entered. *** The name is case-sensitive, and must be specified exactly as defined. * '''version''' ** type: string ** usage: mandatory ** default: none ** This parameter specifies the version of the module to retrieve. *** For YANG modules, this will be the most recent revision date string defined in a module revision statement. *** If any version is acceptable, or if the specific version is not known, then use the empty string. * '''format''' ** type: enumeration (XSD YANG YIN RNG) ** usage: mandatory ** default: none ** This parameter specifies the format of the module to be retrieved. *** XSD: W3C REC REC-xmlschema-1-20041028 *** YANG: RFC 6020 *** YIN: RFC 6020 *** RNG: ISO/IEC 19757-2 *** '''netconfd''' only supports the YANG and YIN formats Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Output: * data ** type: anyxml ** '''yangcli''' will strip off this XML container if the command result is being saved to a text file. Only the YANG contents will be saved instead. Usage: yangcli [mailto:andy@www.example.com andy@myserver]> '''@notifications.yang = get-schema \''' '''identifier=notifications \''' '''version=2009-06-04 \''' '''format=YANG''' RPC Data Reply 24 for session 10: rpc-reply { data { …. entire notifications.yang contents } } (after retrieval, the module can be loaded locally with the mgrload command) yangcli [mailto:andy@www.example.com andy@myserver]> '''mgrload notificications.yang''' OK yangcli [mailto:andy@myagent andy@myserver]> Reference: * draft-ietf-netconf-monitoring-06.txt === help === The help command is used to print documentation to STDOUT. If no session is active, then only help for the local commands and the standard NETCONF commands will be available. If a NETCONF session is active, then the documentation shown will attempt to exactly match the capabilities of the server. If additional (i.e., augment generated) parameters are available, then they will be shown in the command output. If the server does not implement some parameters (e.g., feature not supported) then these parameters will not be shown in the command output. If the server has modified an object with deviation statements, then the altered object will be shown. The '''ncx:hidden''' extension suppresses the '''help''' command. If this extension is present in the YANG definition associated with the request, then no help will be available for that object or command.
'''help command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | command |- | Min parameters: | 3 |- | Max parameters: | 3 |- | Return type: | data |- | YANG file: | ietf-netconf-monitoring.yang |- | Capabilities needed: | :schema-retrieval |} Command Parameters: * '''choice helptype''' (not entered) ** '''command''' *** type: string *** usage: mandatory *** default: none *** This parameter specifies the name of the command for which documentation is requested ** '''commands''' *** type: empty *** usage: mandatory *** default: none *** This parameter will request documentation for all available commands ** '''notification''' *** type: string *** usage: mandatory *** default: none *** This parameter specifies the name of the notification for which documentation is requested ** '''object''' *** type: string *** usage: mandatory *** default: none *** This parameter specifies the name of the NETCONF database object for which documentation is requested. **** Only top level objects are supported by this command. **** Documentation for the entire object subtree will be printed, if the object is a container, choice, or list. **** Documentation for nested objects is only available in parameter mode, using the escape commands for help ('?') and full help ('??') ** '''type''' *** type: string *** usage: mandatory *** default: none *** This parameter specifies the name of the YANG typedef for which documentation is requested *** Only top-level typedefs are supported by this command. Local typedefs within groupings, containers, or lists are not exportable in YANG. * '''choice help-mode''' (not entered) (default choice is 'normal') ** '''brief''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that brief documentation mode should be used. ** '''normal''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that normal documentation mode should be used. ** '''full''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that full documentation mode should be used. Positive Response: * the server prints the requested help text Negative Response: * An error message will be printed if errors are detected. Usage: yangcli> '''help help full ''' help Print the yangcli help text input default parameter: command choice helptype leaf command [NcxIdentifier] Show help for the specified command, also called an RPC method leaf commands [empty] Show info for all local commands leaf notification [NcxIdentifier] Show help for the specified notification leaf object [NcxIdentifier] Show help for the specified object leaf type [NcxIdentifier] Show help for the specified type choice help-mode leaf brief [empty] Show brief help text leaf normal [empty] Show normal help text leaf full [empty] Show full help text yangcli andy@myserver> '''help notification sysConfigChange ''' notification sysConfigChange Generated when the configuration is changed. leaf userName [string] leaf sessionId [SessionId] range: 1..max leaf remoteHost [ip-address] leaf target [string] leaf operation [EditOperationType] [d:merge] enum values: merge replace create delete yangcli andy@svnserver> === history === The '''history''' command is used to show, clear, load, or save the command line history buffer. Use the '''recall''' command to recall a previously executed command line, after getting the line number from the '''history show''' command. All lines entered will be saved in the history buffer except an '''ncx:password''' value entered in parameter mode. When '''yangcli''' starts, the command line history buffer is empty. If a history file was previously stored with the '''history save''' command, then it can be recalled into the buffer with the '''history load '''command. The''' history clear''' command is used to delete the entire command line history buffer. The numbering sequence for commands, starts from zero and keeps incremented until the program exits. If the history buffer is cleared, then the number sequence will continue, not start over at zero.
'''history command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | show |- | Min parameters: | 0 |- | Max parameters: | 2 |- | Return type: | data |- | YANG file: | yangcli.yang |} Command Parameters: * '''choice history-action''' (not entered) ** '''clear''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that the history buffer should be cleared. Unless the contents have been saved with the '''history save''' command, there is no way to recover the cleared buffer contents after this command is executed. ** '''load''' *** type: string *** usage: optional *** default: $HOME/.yangcli_history *** This parameter specifies a command history file, and causes the current command line history contents to be loaded from that file. *** Special processing for this command allows the history file to be omitted in idle mode, even though the load parameter is not type 'empty'.
yangcli> '''history load''' same as: yangcli> '''history load=$HOME/.yangcli_history''' * ** '''save''' *** type: string *** usage: optional *** default: $HOME/.yangcli_history *** This parameter specifies a command history file, and causes the current command line history contents to be saved to that file. *** Special processing for this command allows the history file to be omitted in idle mode, even though the save parameter is not type 'empty'.
yangcli> '''history save''' same as: yangcli> '''history save=$HOME/.yangcli_history''' * ** '''show''' *** type: int32 (-1 for all entries; 1..max for N entries) *** usage: optional *** default: -1 *** This parameter specifies the maximum number of history entries to show. **** If no case is selected from this choice, then the command ''''history show=-1'''' will be used by default. **** The '''help-mode''' choice parameter is only used with the '''history show''' command. ***** If the '''--brief''' or '''--normal''' modes are selected the the format will include the command number and the command line. ***** If the '''--full''' mode is selected, then the command data and time will also be printed. * '''choice help-mode '''(not entered)This parameter is ignored unless the '''history show''' command is entered. ** '''brief''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that brief documentation mode should be used. ** '''normal''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that normal documentation mode should be used. ** '''full''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that full documentation mode should be used. Positive Response: * the requested history entries will be printed for the '''history show''' command * all other commands will return OK Negative Response: * An error message will be printed if errors are detected. Usage: yangcli> '''history show=3 full''' [27] 2009-07-04 09:25:34 sget /system --nofill [28] 2009-07-04 09:34:17 @myconfig = get-config source=running [29] 2009-07-04 09:43:54 history show=3 full yangcli>''' history save=~/my-temp-history-file''' OK yangcli> === if === The '''if''' command is used to start a conditional command block. The '''expr''' parameter is used to specify the XPath expression to test if the if conditional block is true or false. A false block will be skipped and a true block will be executed. The command prompt will contain the string '[F]' while inside a false conditional block in interactive mode. This expression string should be entered with quotes to avoid misinterpreting any whitespace or special characters. The '''docroot''' parameter (if present) specifies the XML document that the 'expr' parameter should be evaluated against. This is optional, since only XPath path expressions need to refer to a document. If the 'expr' expression is true, the conditional block will be executed, and no further conditional blocks within the same if command sequence will be executed. if command .... [elif command] .... [elif-command] ... [else command] ... end command
'''if command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | expr |- | Min parameters: | 1 |- | Max parameters: | 2 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''expr''' ** type: XPath expression string ** usage: mandatory ** default: none ** This parameter specifies the XPath expression to determine if the following commands are within a true or false conditional block. * '''docroot''' ** type: anyxml ** usage: optional (typically a variable reference is used) ** default: none ** This parameter specifies the XML document that should be used if the expr XPath expression references any path nodes. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. ** invalid XPath expression or invalid docroot reference will cause an error Usage: yangcli andy@myserver> '''if "$sysname = 'localhost'"''' yangcli [mailto:andy@myagent andy@myserver]> === insert === The insert command is used to insert or move YANG list or leaf-list data into a NETCONF database. It is a high level command with utilizes the YANG 'insert' extensions to the NETCONF operation.
'''insert command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | target |- | Min parameters: | 2 |- | Max parameters: | 7 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''choice 'from' '''(not entered) ** type: choice of case 'varref' or case 'from-cli' ** usage: mandatory ** default: none ** This parameter specifies the where '''yangcli''' should get the data from, for the insert operation. It is either a user variable or from interactive input at the command line. *** '''varref''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the name of the user variable to use for the target of the insert operation. The parameter must exist (e.g., created with the '''fill''' command) or an error message will be printed. *** '''case from-cli '''(not entered) **** '''target''' ***** type: string ***** usage: mandatory ***** default: none ***** This parameter specifies the database target node of the insert operation. This is an '''ncx:schema-instance''' string, so any instance identifier, or absolute path expression, or something in between, is accepted. **** '''optional''' ***** type: empty ***** usage: optional ***** default: controlled by '''$$optional''' system variable ***** This parameter controls whether optional nodes within the target will be filled in. It can be used to override the '''$$optional''' system variable, when it is set to 'false'. **** '''value''' ***** type: anyxml ***** usage: mandatory ***** default: none ***** This parameter specifies the value that should be used for the contents of the '''target''' parameter. If it is entered, then the interactive mode prompting for missing parameters will be skipped, if this parameter is complete (or all mandatory nodes present if the '''$$optional''' system variable is 'false'). For example, if the target is a leaf, then specifying this parameter will always cause the interactive prompt mode to be skipped. * '''edit-target''' ** type: string ** usage: optional (must be present if the order parameter is set to 'before' or 'after'). ** default: none ** This parameter specifies the value or key clause that should be used, as the list or leaf-list insertion point. It identifies the existing entry that the new entry will be inserted before or after, depending on the '''order''' parameter. *** For a leaf-list, the edit-target contains the value of the target leaf-list node within the configuration being edited. E.g., edit-target='fred'. *** For a list, the edit-target contains the key values of the target list node within the configuration being edited. E.g., edit-target=[name='fred'][zipcode=90210]. * '''order''' ** type: enumeration (first last before after) ** usage: optional ** default: last ** The insert order that should be used. If the value 'before' or 'after' is selected, then the '''edit-target''' parameter must also be present. * '''operation''' ** type: enumeration (create merge replace) ** usage: optional ** default: merge ** This parameter specifies the '''nc:operation''' attribute value for the NETCONF operation. The insert operation is secondary to the NETCONF operation attribute. * '''timeout''' ** type: uint32 (0 = no timeout, otherwise the number of seconds to wait) * ** usage: optional ** default: set to the '''$$timeout''' system variable, default 30 seconds ** This parameter specifies the number of seconds to wait for a response from the server before giving up. The session will not be dropped if a remote command times out, but any late response will be dropped. A value of zero means no timeout should be used, and yangcli will wait forever for a response. System Variables: * '''$$default-operation''' ** The parameter for the operation will be derived from this variable. * '''$$error-option''' ** The parameter for the operation will be derived from this variable * '''$$test-option''' ** The parameter for the operation will be derived from this variable Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''insert varref=myvar order=first''' RPC OK Reply 25 for session 10: yangcli [mailto:andy@www.example.com andy@myserver]> '''insert /nacm/rules/data-rule''' \ '''order=after \''' '''edit-target=â€[name='test-rule']â€''' (user will be prompted to fill in the data-rule contents) RPC OK Reply 26 for session 10: yangcli andy@myserver> Reference: * draft-ietf-netmod-yang-13 === kill-session === The''' kill-session''' command is used to terminate a NETCONF session (other than the current session). All NETCONF implementations must support this command. It is needed sometimes to unlock a NETCONF database locked by a session that is idle or stuck. If the '''lock''' command returns a 'lock-denied' , it will also include an field called . This is the session number that currently holds the requested lock. The same value should be used for the '''session-id''' parameter in this command, to terminate the session will the lock. Note: this is an extreme measure, which should be used with caution.
'''kill-session command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | target |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yuma-netconf.yang |} Command Parameters: * '''session-id''' ** type: uint32 (range: 1.. max) ** usage: mandatory ** default: none ** This parameter specifies the session number of the currently active NETCONF session that should be terminated. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''kill-session session-id=11''' RPC OK Reply 27 for session 10: yangcli andy@myserver> Reference: * RFC 4741, section 7.9 === list === This '''list''' command is used to display the commands, objects, and oids (object identifiers) that are available at the time. The '''list commands''' command will display the local commands and the remote commands that are available in the current NETCONF session, or which have been loaded with the '''mgrload''' command. The '''list files '''command will display the data files that are in the current data search path. The module parameter has no affect in this mode. The '''list modules '''command will display the YANG files that are in the current YANG module search path. The module parameter has no affect in this mode. The''' list objects''' command will display the top-level objects that are currently available in the current NETCONF session, or which have been loaded with the '''mgrload''' command. The''' list oids''' command will display the object identifiers of the top-level objects that are currently available in the current NETCONF session, or which have been loaded with the '''mgrload''' command. The '''list scripts '''command will display the script files that are in the current script search path. The module parameter has no affect in this mode.
'''list command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | none |- | Min parameters: | 1 |- | Max parameters: | 6 |- | Return type: | data |- | YANG file: | yangcli.yang |} Command Parameters: * '''choice help-mode '''(not entered)This parameter is ignored if the listtype choice is set to ' the '''history show''' command is entered. ** '''brief''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that brief documentation mode should be used. ** '''normal''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that normal documentation mode should be used. ** '''full''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that full documentation mode should be used. * '''choice 'listtype' '''(not entered) ** usage: mandatory ** default: none ** This parameter specifies the what type of data should be listed. ** '''commands''' *** type: empty *** usage: mandatory *** default: none *** This parameter specifies that the available commands should be listed. **** If the '''help-mode''' is set to 'brief', then only the command names will be listed. **** If the '''help-mode''' is set to 'normal', then the XML prefix and the command name will be listed. **** If the '''help-mode''' is set to 'full', then the module name and the command name will be listed. ** '''files''' *** type: empty *** usage: mandatory *** default: none *** This parameter specifies that all the data files in the current data search path should be listed. ** '''modules''' *** type: empty *** usage: mandatory *** default: none *** This parameter specifies that all the YANG files in the current module search path should be listed. ** '''objects''' *** type: empty *** usage: mandatory *** default: none *** This parameter specifies that the available top-level objects should be listed. **** If the '''help-mode''' is set to 'brief', then only the object names will be listed. **** If the '''help-mode''' is set to 'normal', then the XML prefix and the object name will be listed. **** If the '''help-mode''' is set to 'full', then the module name and the object name will be listed. ** '''oids''' *** type: empty *** usage: mandatory *** default: none *** This parameter specifies that the available top-level object identifiers should be listed. **** The '''help-mode''' parameter has no effect ** '''scripts''' *** type: empty *** usage: mandatory *** default: none *** This parameter specifies that all the script files in the current script search path should be listed. * '''module''' ** type: string ** usage: optional ** default: none ** This parameter specifies a module name. If present then only information for the specified YANG module will be displayed. Positive Response: * the requested information will be printed Negative Response: * An error message will be printed if errors are detected. Usage: yangcli andy@myserver> '''list objects full module=test ''' test:instance1 test:instance2 test:leafref1 test:leafref2 test:test1 test:test2 test:test3 test:idtest test:musttest test:anyxml.1 test:binary.1 test:bits.1 test:boolean.1 test:empty.1 test:enumeration.1 test:identityref.1 test:instance-identifier.1 test:instance-identifier.2 test:int8.1 test:int16.1 test:int32.1 test:int64.1 test:leafref.1 test:leafref.2 test:string.1 test:uint8.1 test:uint16.1 test:uint32.1 test:uint64.1 test:dec64.1 test:dec64.2 test:dec64.3 test:union.1 test:container.1 test:container.2 test:list.1 test:choice.1 test:xpath.1 yangcli andy@myserver> === load === The '''load''' command is used to load a YANG module into the server. This command is only supported by the '''netconfd''' server. The YANG files must be available in the module search path for the server. Refer to the '''netconfd''' configuration section for more details on adding new YANG modules into the server. After using this command, the '''mgrload''' command may also be needed to keep the current session synchronized with the server. Use the '''revision''' parameter to load a specific revision of a module. The server will return the revision date of the module that was loaded (or already present).
'''load command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | module |- | Min parameters: | 1 |- | Max parameters: | 3 |- | Return type: | data |- | YANG file: | yuma-system.yang |} Command Parameters: * module ** type: string (length 1 .. 63) ** usage: mandatory ** default: none ** This parameter specifies the name of the module to load. * revision ** type: date string (YYYY-MM-DD) ** usage: optional ** default: none ** This parameter specifies the revision date of the module to load. * deviation: ** type: leaf-list of deviation module names ** usage: optional (0 or more instances) ** default: none ** This parameter specifies a deviation module to load prior to loading the requested module. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''load toaster revision=2009-06-23''' RPC Data Reply 27 for session 10: rpc-reply { mod-revision 2009-06-23 } yangcli andy@myserver> === lock === The '''lock''' command is used to request a global lock on a NETCONF database. It is used, along with the '''unlock''' command, to obtain exclusive write access to the NETCONF server. The scope of a lock command is the lifetime of the session that requested the lock. This means that if the session that owns the lock is dropped for any reason, all the locks it holds will be released immediately by the server. The use of database locks is optional in NETCONF, but it must be implemented by every server. It is strongly suggested that locks be used if multiple managers are likely to log into the particular NETCONF server. One or more locks may be needed to execute a transaction safely: * If the ''':writable-running''' capability is supported, then a lock on the database is needed. This database can be locked at any time. * If the ''':startup''' capability is supported, then a lock on the database is needed. This database can be locked at any time. * If the ''':candidate''' capability is supported, then a lock on the database is needed. A lock on the database is also needed. ** The database can only be locked if there are no active edits in it. ** The '''discard-changes''' command may be needed to clear a database that has been left edited by a session that terminated unexpectedly. ** Note: There is no way in NETCONF to tell the difference between an actively edited database and an 'abandoned' database. The server will almost never clear the database. It will only clear any locks held. Use the discard-changes command (for other session's edits) with extreme caution.
'''lock command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yuma-netconf.yang |- | Capabilities needed: | none |- | Capabilities optional: | :candidate:startup:url |} Command Parameters: * '''target''' ** type: container with 1 of N choice of leafs ** usage: mandatory ** default: none ** This parameter specifies the name of the target database to be locked. ** container contents: 1 of N: *** '''candidate''' **** type: empty **** capabilities needed:''' ''':candidate *** '''running''' **** type: empty **** capabilities needed: none *** '''startup''' **** type: empty **** capabilities needed: startup *** '''url''' **** type: yang:uri **** capabilities needed: :url, and the scheme used in the URL must be specified in the :url capability 'schemes' parameter. **** To enter this parameter, the interactive mode must be used. The shorthand mode (target=url) cannot be used, since this parameter contains a string. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. ** If the is 'lock-denied' then the will contain a leaf. This identifies the session number of the current lock holder. Usage: yangcli andy@myserver> '''lock target=candidate''' RPC OK Reply 29 for session 10: yangcli [mailto:andy@myagent andy@myserver]> === log-debug === The''' log-debug''' command prints a message to the program log, if the '''$$log-level''' system variable is set to 'debug' or a higher enumeration (e.g., 'debug2'). The '''msg''' parameter is used to provide the message string to print.
'''log-debug command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | msg |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''msg''' ** type: string ** usage: mandatory ** default: none ** This parameter specifies the string to print to the program log. Positive Response: * the server returns Negative Response: * An error message will be printed if the '''msg''' parameter contains errors. Usage: yangcli andy@myserver>'''log-debug 'starting strict foo'''' Debug: starting script foo yangcli andy@myserver> === log-error === The '''log-error''' command prints a message to the program log, if the '''$$log-level''' system variable is set to 'error' or a higher enumeration (e.g., 'info'). The '''msg''' parameter is used to provide the message string to print.
'''log-error command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | msg |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''msg''' ** type: string ** usage: mandatory ** default: none ** This parameter specifies the string to print to the program log. Positive Response: * the server returns Negative Response: * An error message will be printed if the '''msg''' parameter contains errors. Usage: yangcli andy@myserver>'''log-error 'sysName not correct'''' Error: sysName not correct yangcli andy@myserver> === log-info === The '''log-info''' command prints a message to the program log, if the '''$$log-level''' system variable is set to 'info' or a higher enumeration (e.g., 'debug'). The '''msg''' parameter is used to provide the message string to print.
'''log-info command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | msg |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''msg''' ** type: string ** usage: mandatory ** default: none ** This parameter specifies the string to print to the program log. Positive Response: * the server returns Negative Response: * An error message will be printed if the '''msg''' parameter contains errors. Usage: yangcli andy@myserver>'''log-info 'sysName is correct'''' Info: sysName is correct yangcli andy@myserver> === log-warn === The '''log-warn''' command prints a message to the program log, if the '''$$log-level''' system variable is set to 'warn' or a higher enumeration (e.g., 'debug'). The '''msg''' parameter is used to provide the message string to print.
'''log-warn command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | msg |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''msg''' ** type: string ** usage: mandatory ** default: none ** This parameter specifies the string to print to the program log. Positive Response: * the server returns Negative Response: * An error message will be printed if the '''msg''' parameter contains errors. Usage: yangcli andy@myserver>'''log-warn 'sysName object not found'''' Warning: sysName object not found yangcli andy@myserver> === merge === The '''merge''' command is a high-level operation. It is used to merge some new nodes into the default target database. A target node is specified (in 1 of 2 ways), and then the data structure is filled in. Only mandatory nodes will be filled in unless the '''$$optional''' system variable is set to 'true'. Refer to the '''fill''' command for more details on interactive mode data structure completion.
'''merge command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | target |- | Min parameters: | 1 |- | Max parameters: | 5 |- | Return type: | status |- | YANG file: | yangcli.yang |- | Capabilities needed: | :candidate or :writable-running |} Command Parameters: * '''choice 'from' '''(not entered) ** type: choice of case 'varref' or case 'from-cli' ** usage: mandatory ** default: none ** This parameter specifies the where '''yangcli''' should get the data from, for the merge operation. It is either a user variable or from interactive input at the command line. *** '''varref''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the name of the user variable to use for the target of the merge operation. The parameter must exist (e.g., created with the '''fill''' command) or an error message will be printed. *** '''case from-cli '''(not entered) **** '''target''' ***** type: string ***** usage: optional (target or urltarget must be entered) ***** default: none ***** This parameter specifies the database target node of the merge operation. This is an '''ncx:schema-instance''' string, so any instance identifier, or absolute path expression, or something in between, is accepted. **** '''urltarget''' ***** type: string ***** usage: optional ***** default: none ***** This parameter specifies the database target node of the merge operation. This is an '''UrlPath''' string. **** '''optional''' ***** type: empty ***** usage: optional ***** default: controlled by '''$$optional''' system variable ***** This parameter controls whether optional nodes within the target will be filled in. It can be used to override the '''$$optional '''system variable, when it is set to 'false'. **** '''value''' ***** type: anyxml ***** usage: mandatory ***** default: none ***** This parameter specifies the value that should be used for the contents of the '''target''' parameter. If it is entered, then the interactive mode prompting for missing parameters will be skipped, if this parameter is complete (or all mandatory nodes present if the '''$$optional''' system variable is 'false'). For example, if the target is a leaf, then specifying this parameter will always cause the interactive prompt mode to be skipped. * '''timeout''' ** type: uint32 (0 = no timeout, otherwise the number of seconds to wait) * ** usage: optional ** default: set to the '''$$timeout''' system variable, default 30 seconds ** This parameter specifies the number of seconds to wait for a response from the server before giving up. The session will not be dropped if a remote command times out, but any late response will be dropped. A value of zero means no timeout should be used, and '''yangcli''' will wait forever for a response. System Variables: * '''$$default-operation''' ** The parameter for the operation will be derived from this variable. * '''$$error-option''' ** The parameter for the operation will be derived from this variable * '''$$test-option''' ** The parameter for the operation will be derived from this variable Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli [mailto:andy@www.example.com andy@myserver]> '''merge \''' '''target=/nacm/rules/moduleRule[moduleName='netconf']\''' '''[allowed-rights='read write']/allowed-group''' '''\''' '''value=ncx:guest''' (no user prompting; request sent right away) RPC OK Reply 31 for session 10: yangcli andy@myserver> Reference: * RFC 4741, section 7.2 === mgrload === The '''mgrload''' command is used to load a YANG module into '''yangcli'''. The YANG files must be available in the module search path for '''yangcli'''.
'''mgrload command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | module |- | Min parameters: | 1 |- | Max parameters: | 3 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * module ** type: string (length 1 .. 63) ** usage: mandatory ** default: none ** This parameter specifies the name of the module to load. * revision ** type: date string (YYYY-MM-DD) ** usage: optional ** default: none ** This parameter specifies the revision date of the module to load. * deviation: ** type: leaf-list of deviation module names ** usage: optional (0 or more instances) ** default: none ** This parameter specifies a deviation module to load prior to loading the requested module. Positive Response: * OK Negative Response: * An error message will be printed if errors are detected. Usage: yangcli> '''mgrload toaster''' Load module toaster OK yangcli> === no-op === The''' no-op''' command is used to test server message processing response times, by providing a baseline response time to do nothing except return . It can also be used as an application-level keep-alive to prevent proxy idle timeout or server idle timeout problems from occurring. This command is only supported by the '''netconfd''' server. The server will simply respond 'OK', if the request is well-formed.
'''no-op command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | status |- | YANG file: | yuma-system.yang |} Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''no-op''' RPC OK Reply 31 for session 10: yangcli andy@myserver> === pwd === The '''pwd''' command is used to print the current working directory.
'''pwd command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | status |- | YANG file: | yangcli.yang |} Positive Response: * the current working directory is printed to the log or STDOUT Negative Response: * An error message will be printed if errors are detected. Usage: yangcli> '''pwd''' Current working directory is /home/andy yangcli> === quit === The '''quit''' command is used to terminate the '''yangcli''' program. If a NETCONF session is in progress, it will be dropped without sending a '''close-session''' command first. This should be taken into account if the server reports dropped TCP connections as an error.
'''quit command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | none |- | YANG file: | yangcli.yang |} Positive Response: * The program terminates; no response will be printed. Negative Response: * An error message will be printed if errors are detected. Usage: yangcli> '''quit''' andy@myworkstation> === recall === The '''recall''' command is used to recall a previously entered command line from the command line history buffer. A command is recalled by its command ID number. Use the '''history show''' command to see the contents of the command line history buffer.
'''recall command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | index |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | data |- | YANG file: | yangcli.yang |} Positive Response: * The specified command line is recalled into the current command line. Negative Response: * An error message will be printed if errors are detected. Usage: yangcli> '''recall 7''' yangcli>'''sget /system''' === release-locks === The '''release-locks''' command is used to release all the locks that were previously granted with the get-locks command. If the get-locks command was not used, then this command will fail with an error message that no locks are active to unlock.
'''release-locks command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | status |- | YANG file: | yangcli.yang |} Positive Response: * The previously granted locks are released. Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: (showing 2 locks being released) yangcli ady@myserver> '''release-locks''' RPC OK Reply 8 for session 23: RPC OK Reply 9 for session 23: yangcli andy@myserver> === replace === The '''replace''' command is a high-level operation. It is used to replace an existing subtree with some new nodes, in the default target database. Only the subtree indicated by the '''target''' or '''varref''' parameter will be replaced. A target node is specified (in 1 of 2 ways), and then the data structure is filled in. Only mandatory nodes will be filled in unless the '''$$optional''' system variable is set to 'true'. Refer to the '''fill''' command for more details on interactive mode data structure completion.
'''replace command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | target |- | Min parameters: | 1 |- | Max parameters: | 5 |- | Return type: | status |- | YANG file: | yangcli.yang |- | Capabilities needed: | :candidate or :writable-running |} Command Parameters: * '''choice 'from' '''(not entered) ** type: choice of case 'varref' or case 'from-cli' ** usage: mandatory ** default: none ** This parameter specifies the where '''yangcli''' should get the data from, for the replace operation. It is either a user variable or from interactive input at the command line. *** '''varref''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the name of the user variable to use for the target of the replace operation. The parameter must exist (e.g., created with the '''fill''' command) or an error message will be printed. *** '''case from-cli '''(not entered) **** '''target''' ***** type: string ***** usage: optional (target or urltarget must be present) ***** default: none ***** This parameter specifies the database target node of the replace operation. This is an '''ncx:schema-instance''' string, so any instance identifier, or absolute path expression, or something in between, is accepted. **** '''urltarget''' ***** type: string ***** usage: optional ***** default: none ***** This parameter specifies the database target node of the replace operation. This is an '''UrlPath''' string. **** '''optional''' ***** type: empty ***** usage: optional ***** default: controlled by '''$$optional''' system variable ***** This parameter controls whether optional nodes within the target will be filled in. It can be used to override the '''$$optional '''system variable, when it is set to 'false'. **** '''value''' ***** type: anyxml ***** usage: mandatory ***** default: none ***** This parameter specifies the value that should be used for the contents of the '''target''' parameter. If it is entered, then the interactive mode prompting for missing parameters will be skipped, if this parameter is complete (or all mandatory nodes present if the '''$$optional''' system variable is 'false'). For example, if the target is a leaf, then specifying this parameter will always cause the interactive prompt mode to be skipped. * '''timeout''' ** type: uint32 (0 = no timeout, otherwise the number of seconds to wait) * ** usage: optional ** default: set to the '''$$timeout''' system variable, default 30 seconds ** This parameter specifies the number of seconds to wait for a response from the server before giving up. The session will not be dropped if a remote command times out, but any late response will be dropped. A value of zero means no timeout should be used, and '''yangcli''' will wait forever for a response. System Variables: * '''$$default-operation''' ** The parameter for the operation will be derived from this variable. * '''$$error-option''' ** The parameter for the operation will be derived from this variable * '''$$test-option''' ** The parameter for the operation will be derived from this variable Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli [mailto:andy@www.example.com andy@myserver]> '''replace \''' '''target=/nacm/rules/moduleRule[moduleName='yuma-system']\''' '''[allowed-rights='exec']''' (user prompted to fill in specified element) RPC OK Reply 31 for session 10: yangcli andy@myserver> Reference: * RFC 4741, section 7.2 === restart === The '''restart''' command is used to restart the NETCONF server. This command is only supported by the '''netconfd''' server. The default access control configuration for '''netconfd''' will not allow any user except the designated 'superuser' account to invoke this command. Refer to the '''netconfd''' user manual for details on configuring access control.
'''restart command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | status |- | YANG file: | yuma-system.yang |} Positive Response: * the server will drop all sessions and restart Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''restart''' ses: session 10 shut by remote peer yangcli> === run === The run command is used to run a '''yangcli''' script. Refer to the section on scripts for details on how to write a script. The script name is the only mandatory parameter. There are 9 generic parameters (named P1 to P9) that can be used to pass string parameters to scripts. Within a script, these are referenced with local variables ($1 to $9). The run command can be used within a script. Scripts do not return any status or data at this time. The run command can appear inside a script, starting a new run level. An error will occur if a loop occurs or too many nest levels are used. Up to 64 run levels are supported in '''yangcli'''.
'''run command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | script |- | Min parameters: | 1 |- | Max parameters: | 10 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''P1, P2, P3, P4, P5, P6, P7, P8, P9''' ** type: string ** usage: optional ** default: none ** These parameters provide a generic string parameter passing mechanism for each script invocation, similar to unix shell scripts. Within the script, the parameters''' $1''', '''$2,''' '''$3''', '''$4''',''' $5''', '''$6''', '''$7''', '''$8''' and '''$9''' will be non-NULL only if the corresponding ''''Pn'''' parameter was set in the script invocation. * '''script''' ** type: string ** usage: mandatory ** default: none ** This parameter specifis the name of the script to be run. If a path specification is included, then it will be used. Otherwise the current script search path will be used to find the script. System Variables: * '''$$runpath''' ** Controls where '''yangcli''' will look for files specified in the '''script''' parameter. Positive Response: * the specified script will be executed Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error, if any remote commands are contained in the script. Usage: yangcli> '''run connect P1=yangrocks''' runstack: Starting level 1 script /home/andy/scripts/connect (commands and responses are printed as if they were typed) runstack: Ending level 1 script /home/any/scripts/connect yangcli andy@myserver> === save === The '''save''' command is used to save NETCONF database edits to non-volatile storage, on the server. It is a pseudo-command, mapped to other remote commands depending on which database target is supported by the server ( or ). The '''save''' command usually maps to either the '''commit''' or the '''copy-config''' command. Refer to the section on NETCONF sessions for more details.
'''save command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | none |- | YANG file: | yangcli.yang |} Positive Response: * The server returns one or two responses. Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''save''' RPC OK Reply 34 on session 10 yangcli andy@myserver> === set-log-level === The''' set-log-level '''command is used to configure the server logging verbosity level. It is only supported by the '''netconfd''' server. This operation is defined as '''nacm:secure''', so only the system super-user can invoke this command by default.
'''set-log-level command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yuma-system.yang |- | Capabilities needed: | none |} Command Parameters: * '''log-level''' ** type: enumeration (off, error, warn, info, debug, debug2, debug3) ** This mandatory parameter specifies the desired server logging verbosity level. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error.] yangcli [mailto:andy@www.example.com andy@myserver]>''' set-log-level log-level=debug2''' RPC OK Reply 29 for session 10: yangcli [mailto:andy@myagent andy@myserver]> === set-my-session === The''' set-my-session '''command is used to configure the session customization parameters on the server. It is only supported by the '''netconfd''' server. The session line size and default behavior for the with-defaults parameter can be controlled at this time. Since all parameters are optional, they need to be entered in the same command line as the command name. Otherwise the '''$$optional''' system variable needs to be set to 'true'. If not, then no parameters will be sent to the server, and no session parameters will be changed.
'''set-my-session command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 3 |- | Return type: | status |- | YANG file: | yuma-mysession.yang |- | Capabilities needed: | none |} Command Parameters: * '''indent''' ** type: uint32 (range: 0 .. 9) ** This parameter specifies the desired number of spaces to indent for each new XML nest level, for the session. If missing, then the indent amount is not changed. * '''linesize''' ** type: uint32 (range: 40 .. 1024) ** This parameter specifies the desired line length for the session. If missing, then the current line size is not changed. * '''with-defaults''' ** type: enumeration (report-all trim explicit) ** This parameter specifies the desired default with-defaults filtering behavior for the session. If missing, the current with-defaults behavior is not changed. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error.] yangcli [mailto:andy@www.example.com andy@myserver]>''' set-my-session \''' ''' --linesize=64 --with-defaults=trim''' RPC OK Reply 25 for session 10: yangcli [mailto:andy@myagent andy@myserver]> === sget === The '''sget''' command is used to invoke a NETCONF operation with a subtree filter. A target node is specified (in 1 of 2 ways), and then the data structure is filled in. Only mandatory nodes will be filled in unless the '''$$optional''' system variable is set to 'true'. This step can be skipped completely with the '''nofill''' parameter. Refer to the '''fill''' command for more details on interactive mode data structure completion.
'''sget command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | target |- | Min parameters: | 1 |- | Max parameters: | 5 |- | Return type: | data |- | YANG file: | yangcli.yang |- | Capabilities needed: | none |- | Capabilities optional: | :with-defaults |} Command Parameters: * '''choice 'from' '''(not entered) ** type: choice of case 'varref' or case 'from-cli' ** usage: mandatory ** default: none ** This parameter specifies the where '''yangcli''' should get the data from, for the subtree filter target of the operation. It is either a user variable or from interactive input at the command line. *** '''varref''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the name of the user variable to use for the subtree filter target of the operation. The parameter must exist (e.g., created with the '''fill''' command) or an error message will be printed. *** '''case from-cli '''(not entered) **** '''target''' ***** type: string ***** usage: optional (target or urltarget must be present) ***** default: none ***** This parameter specifies the database target node of the operation. This is an '''ncx:schema-instance''' string, so any instance identifier, or absolute path expression, or something in between, is accepted. ****** The escape command ('?s') can be used in parameter mode to skip a parameter completely. ****** Entering the empty string for a parameter will cause a subtree selection node to be created instead of a content match node **** '''urltarget''' ***** type: string ***** usage: optional ***** default: none ***** This parameter specifies the database target node of the get operation. This is an '''UrlPath''' string. **** '''optional''' ***** type: empty ***** usage: optional ***** default: controlled by '''$$optional''' system variable ***** This parameter controls whether optional nodes within the target will be filled in. It can be used to override the '''$$optional '''system variable, when it is set to 'false'. **** '''value''' ***** type: anyxml ***** usage: mandatory ***** default: none ***** This parameter specifies the value that should be used for the contents of the '''target''' parameter. If it is entered, then the interactive mode prompting for missing parameters will be skipped, if this parameter is complete (or all mandatory nodes present if the '''$$optional''' system variable is 'false'). For example, if the target is a leaf, then specifying this parameter will always cause the interactive prompt mode to be skipped. * '''nofill''' ** type: empty * ** usage: optional ** default: none ** This parameter specifies the that no interactive prompting to fill in the contents of the specified subtree filter target will be done. *** This causes the entire selected subtree to be requested in the filter. *** yangcli will attempt to figure out if there can be multiple instances of the requested subtree. If not, then '''nofill''' will be the default for that target parameter. * '''with-defaults''' ** type: enumeration (none report-all report-all-tagged trim explicit) ** usage: optional ** default: none ** capabilities needed: with-defaults ** This parameter controls how nodes containing only default values are returned in the . System Variables: * '''$$timeout''' ** The response timeout interval will be derived from this system variable. * '''$$with-defaults''' ** The parameter for the operation will be derived from this system variable. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Output: * '''data''' ** type: anyxml ** This element will contain the requested data from the database or non-config data structures. Usage: yangcli andy@myserver> '''sget sytem''' Filling container /system: RPC Data Reply 32 for session 10: rpc-reply { data { system { sysName myserver.localdomain sysCurrentDateTime 2009-07-06T02:24:19Z sysBootDateTime 2009-07-05T19:22:28Z } } } yangcli andy@myserver> Reference: * RFC 4741, section 7.7 === sget-config === The '''sget-config''' command is used to invoke a NETCONF operation with a subtree filter. A target node is specified (in 1 of 2 ways), and then the data structure is filled in. Only mandatory nodes will be filled in unless the '''$$optional''' system variable is set to 'true'. This step can be skipped completely with the '''nofill''' parameter. Refer to the '''fill''' command for more details on interactive mode data structure completion.
'''sget-config command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | target |- | Min parameters: | 2 |- | Max parameters: | 6 |- | Return type: | data |- | YANG file: | yangcli.yang |- | Capabilities needed: | none |- | Capabilities optional: | :candidate:startup:url:with-defaults |} Command Parameters: * '''choice 'from' '''(not entered) ** type: choice of case 'varref' or case 'from-cli' ** usage: mandatory ** default: none ** This parameter specifies the where '''yangcli''' should get the data from, for the subtree filter target of the operation. It is either a user variable or from interactive input at the command line. *** '''varref''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the name of the user variable to use for the subtree filter target of the operation. The parameter must exist (e.g., created with the '''fill''' command) or an error message will be printed. *** '''case from-cli '''(not entered) **** '''target''' ***** type: string ***** usage: optional (target or urltarget must be present) ***** default: none ***** This parameter specifies the database target node of the operation. This is an '''ncx:schema-instance''' string, so any instance identifier, or absolute path expression, or something in between, is accepted. ****** The escape command ('?s') can be used in parameter mode to skip a parameter completely. ****** Entering the empty string for a parameter will cause a subtree selection node to be created instead of a content match node **** '''urltarget''' ***** type: string ***** usage: optional ***** default: none ***** This parameter specifies the database target node of the get-config operation. This is an '''UrlPath''' string. **** '''optional''' ***** type: empty ***** usage: optional ***** default: controlled by '''$$optional''' system variable ***** This parameter controls whether optional nodes within the target will be filled in. It can be used to override the '''$$optional '''system variable, when it is set to 'false'. **** '''value''' ***** type: anyxml ***** usage: mandatory ***** default: none ***** This parameter specifies the value that should be used for the contents of the '''target''' parameter. If it is entered, then the interactive mode prompting for missing parameters will be skipped, if this parameter is complete (or all mandatory nodes present if the '''$$optional''' system variable is 'false'). For example, if the target is a leaf, then specifying this parameter will always cause the interactive prompt mode to be skipped. * '''nofill''' ** type: empty * ** usage: optional ** default: none ** This parameter specifies the that no interactive prompting to fill in the contents of the specified subtree filter target will be done. *** This causes the entire selected subtree to be requested in the filter. *** yangcli will attempt to figure out if there can be multiple instances of the requested subtree. If not, then '''nofill''' will be the default for that target parameter. * '''source''' ** type: container with 1 of N choice of leafs ** usage: mandatory ** default: none ** This parameter specifies the name of the source database for the retrieval operation. ** container contents: 1 of N: *** '''candidate''' **** type: empty **** capabilities needed:''' ''':candidate *** '''running''' **** type: empty **** capabilities needed: none *** '''startup''' **** type: empty **** capabilities needed: startup *** '''url''' **** type: yang:uri **** capabilities needed: :url, and the scheme used in the URL must be specified in the :url capability 'schemes' parameter **** To enter this parameter, the interactive mode must be used. The shorthand mode (source=url) cannot be used, since this parameter contains a string. * '''with-defaults''' ** type: enumeration (none report-all report-all-tagged trim explicit) ** usage: optional ** default: none ** capabilities needed: with-defaults ** This parameter controls how nodes containing only default values are returned in the . System Variables: * '''$$timeout''' ** The response timeout interval will be derived from this system variable. * '''$$with-defaults''' ** The parameter for the operation will be derived from this system variable. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Output: * '''data''' ** type: anyxml ** This element will contain the requested data from the database indicated by the '''source''' parameter. Usage: yangcli andy@myserver> '''sget-config /nacm/rules/data-rule \''' '''source=candidate \''' '''nofill''' RPC Data Reply 35 for session 10: rpc-reply { data { nacm { rules { data-rule nacm-tree { name nacm-tree path /nacm:nacm allowed-rights { read write } allowed-group nacm:admin comment 'access to nacm config' } } } } } yangcli andy@myserver> Reference: * RFC 4741, section 7.1 === show === The '''show''' command is used to show program status and YANG details that may be needed during management of a NETCONF server. There are several variants of the '''show''' command: * The '''show cli''' command prints the contents of the yangcli CLI and config file parameters. * The '''show global''' command prints the contents of one global user variable. * The''' show globals '''command prints a summary or the contents of all the global user variables. * The '''show local''' command prints the contents of one local user variable. * The''' show locals '''command prints a summary or the contents of all the local user variables. * The '''show module''' command prints meta information or help text for one YANG module. * The''' show modules '''command prints meta information for all the currently available YANG modules. IF a session is active, this will be the list of modules the server reported, plus any modules loaded with the '''mgrload''' command. * The '''show objects''' command prints the available objects or help text for the available objects. * The '''show session''' command prints the session startup screen information for the current session. * The '''show system''' command prints the contents of the yangcli system variables. * The '''show var''' command prints the contents of the specified variable. * The''' show vars''' command prints a summary or the contents of all the program variables. * The '''show version''' command prints the '''yangcli''' version number
'''show command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | none |- | Min parameters: | 1 |- | Max parameters: | 2 |- | Return type: | data |- | YANG file: | yangcli.yang |} Command Parameters: * '''choice help-mode''' (not entered) (default choice is 'normal') ** '''brief''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that brief documentation mode should be used. ** '''normal''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that normal documentation mode should be used. ** '''full''' *** type: empty *** usage: optional *** default: none *** This parameter specifies that full documentation mode should be used. * '''choice showtype''' (not entered) ** mandatory 1 of N choice for the sub-command for the '''show''' command *** '''cli''' **** type: empty **** usage: mandatory **** default: none **** This parameter selects the yangcli CLI and configuration variables. ***** The '''help-mode''' parameter has no affect on this command. *** '''global''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the name of the global variable to show information for. ***** If '''help-mode '''is 'brief', then the name, variable object, and type of the global variable object will be printed. ***** If '''help-mode''' is 'normal' or 'full', then the contents of the specified global variable will be printed. *** '''globals''' **** type: empty **** usage: mandatory **** default: none **** This parameter specifies that information for all global variables should be printed. ***** If '''help-mode '''is 'brief', then the name, variable object, and type of each global variable object will be printed. ***** If '''help-mode''' is 'normal' or 'full', then the contents of each global variable will be printed. *** '''local''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the name of the local variable to show information for. ***** If '''help-mode '''is 'brief', then the name, variable object, and type of the local variable object will be printed. ***** If '''help-mode''' is 'normal' or 'full', then the contents of the specified local variable will be printed. *** '''locals''' **** type: empty **** usage: mandatory **** default: none **** This parameter specifies that information for all local variables should be printed. ***** If '''help-mode '''is 'brief', then the name, variable object, and type of each local variable object will be printed. ***** If '''help-mode''' is 'normal' or 'full', then the contents of each local variable will be printed. *** '''module''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the name of the module to show information for. ***** If '''help-mode '''is 'brief' or 'normal', then the name, version, namespace, and source of the module will be printed. ***** If '''help-mode''' is 'full', then the module level help text for the specified module will be printed. *** '''modules''' **** type: empty **** usage: mandatory **** default: none **** This parameter specifies that information for all available modules should be printed: ***** If '''help-mode '''is 'brief', then the name of each module will be printed. ***** If '''help-mode '''is 'normal', then the XML prefix, name, and revision date of each module will be printed. ***** If '''help-mode''' is 'full', then the name, revision date, prefix, namespace, and source of each module will be printed. *** '''objects''' **** type: empty **** usage: mandatory **** default: none **** This parameter specifies that information for all available database objects should be printed: ***** If '''help-mode '''is 'brief', then the module name and name of each object will be printed. ***** If '''help-mode '''is 'normal', then the YANG object type, object name, and YANG base type will be printed.
If '''help-mode''' is 'full', then brief help text will be printed for every object. *** '''system''' **** type: empty **** usage: mandatory **** default: none **** This parameter specifies that information for all system variables should be printed. ***** The '''help-mode '''parameter''' '''has no affect on this command. *** '''var''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the name of any variable to show information for. ***** If '''help-mode '''is 'brief', then the name, variable object, and type of the variable object will be printed. ***** If '''help-mode''' is 'normal' or 'full', then the contents of the specified variable will be printed. *** '''vars''' **** type: empty **** usage: mandatory **** default: none **** This parameter specifies that information for all variables should be printed. This includes all CLI, read-only system, read-write system, global user, and local user variables. ***** If '''help-mode '''is 'brief', then the name, variable object, and type of each variable object will be printed. ***** If '''help-mode''' is 'normal' or 'full', then the contents of each variable will be printed. *** '''version''' **** type: empty **** usage: mandatory **** default: none **** This parameter specifies that the yangcli program version string should be printed. ***** The '''help-mode''' parameter has no affect in this mode. Positive Response: * the server prints the requested information Negative Response: * An error message will be printed if errors are detected. Usage: yangcli andy@myserver> '''show modules''' nc:netconf/2009-04-10 inet:ietf-inet-types/2009-05-13 ns:ietf-netconf-monitoring/2009-03-03 wd:ietf-with-defaults/2009-04-10 yang:ietf-yang-types/2009-05-13 nacm:nacm/2009-05-13 manageEvent:nc-notifications/2008-07-14 ncx:ncx/2009-06-12 ncxapp:ncx-app-common/2009-04-10 nt:ncxtypes/2008-07-20 nd:netconfd/2009-05-28 ncEvent:notifications/2008-07-14 sys:system/2009-06-04 t:test/2009-06-10 yangcli andy@myserver> === shutdown === The '''shutdown''' command is used to shut down the NETCONF server. This command is only supported by the '''netconfd''' server. The default access control configuration for '''netconfd''' will not allow any user except the designated 'superuser' account to invoke this command. Refer to the '''netconfd''' user manual for details on configuring access control.
'''shutdown command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 0 |- | Max parameters: | 0 |- | Return type: | status |- | YANG file: | yuma-system.yang |} Positive Response: * the server will drop all sessions and terminate. Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''shutdown''' ses: session 10 shut by remote peer yangcli> === start-timer === The '''start-timer''' command is used to start a timer to do simple performance measurements. It is used along with the stop-timer command.
'''start-timer command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | id |- | Min parameters: | 1 |- | Max parameters: | 2 |- | Return type: | status |- | YANG file: | yangcli.yang |- | Capabilities needed: | none |- | Capabilities optional: | none |} Command Parameters: * '''id''' ** type: number ** usage: mandatory ** default: 0 ** This parameter specifies the numeric ID of the timer.It is a number in the range 0 to 15. * '''restart-ok''' ** type: boolean ** usage: optional ** default: true ** Indicates whether the timer will be used if it is already running. If 'true', the timer will be restarted if it is already running. If 'false', an error will occur if the timer is already running. ** Positive Response: * the client prints OK Negative Response: * An error message will be printed if errors are detected locally. Usage: yangcli andy@myserver> start-timer 1 OK yangcli [mailto:andy@myagent andy@myserver]> === stop-timer === The '''stop-timer''' command is used to stop a timer to do simple performance measurements. It is used along with the start-timer command.
'''stop-timer command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | id |- | Min parameters: | 1 |- | Max parameters: | 2 |- | Return type: | data |- | YANG file: | yangcli.yang |- | Capabilities needed: | none |- | Capabilities optional: | none |} Command Parameters: * '''id''' ** type: number ** usage: mandatory ** default: 0 ** This parameter specifies the numeric ID of the timer.It is a number in the range 0 to 15. * '''echo''' ** type: boolean ** usage: optional ** default: true ** Indicates whether the delta result should be printed to the log. Positive Response: * data: the elapsed time is returned and can be used in assignment statements Negative Response: * An error message will be printed if errors are detected locally. Usage: yangcli andy@myserver> $time = stop-timer 1 Elapsed time: 2.003345 seconds yangcli [mailto:andy@myagent andy@myserver]> === unlock === The '''unlock''' command is used to request a global lock on a NETCONF database. It is used, along with the '''lock''' command, to obtain exclusive write access to the NETCONF server.
'''unlock command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yuma-netconf.yang |- | Capabilities needed: | none |- | Capabilities optional: | :candidate:startup:url |} Command Parameters: * '''target''' ** type: container with 1 of N choice of leafs ** usage: mandatory ** default: none ** This parameter specifies the name of the target database to be locked. ** container contents: 1 of N: *** '''candidate''' **** type: empty **** capabilities needed:''' ''':candidate *** '''running''' **** type: empty **** capabilities needed: none *** '''startup''' **** type: empty **** capabilities needed: startup *** '''url''' **** type: yang:uri **** capabilities needed: :url, and the scheme used in the URL must be specified in the :url capability 'schemes' parameter. **** To enter this parameter, the interactive mode must be used. The shorthand mode (target=url) cannot be used, since this parameter contains a string. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''unlock target=candidate''' RPC OK Reply 38 for session 10: yangcli [mailto:andy@myagent andy@myserver]> === unset === The '''unset''' command is used to delete a command alias from memory.
'''unset command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | name |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''name''' ** type: string ** usage: mandatory ** default: none ** This parameter specifies the name of the command alias to delete. Positive Response: * Deleted alias 'foo' is printed, where 'foo' is the name parameter string Negative Response: * An error message will be printed. * An message will be printed if the server detected an error. Usage: yangcli andy> unset get-running Deleted alias 'get-running' yangcli [mailto:andy@myagent andy]> === uservars === The '''uservars''' command is used to clear, load, or save the global user variables.
'''uservars command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | none |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''uservars-action''' ** type: choice ** usage: mandatory ** default: none ** '''clear''': *** Delete all global user variables from memory ** '''load''' [uservars-filespec] *** Load global user variables into memory from an XML file.If the 'uservars-filespec' parameter is missing, then the default file($HOME/.yuma/yangcli_uservars.xml) will be used. ** '''save''' [uservars-filespec] *** Save the global user variables from memory into an XML file.If the 'uservars-filespec' parameter is missing, then the default file($HOME/.yuma/yangcli_uservars.xml) will be used. Positive Response: * A status message is printed. Negative Response: * An error message will be printed. Usage: yangcli andy> uservars clear Deleted all global user variables from memory yangcli [mailto:andy@myagent andy]> === validate === The '''validate''' command is used to check if a complete NETCONF database is valid or not. The''' :validate '''capability must be supported by the server to use this command.
'''validate command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | none |- | Min parameters: | 1 |- | Max parameters: | 1 |- | Return type: | status |- | YANG file: | yuma-netconf.yang |- | Capabilities needed: | :validate |- | Capabilities optional: | :candidate:startup:url |} Command Parameters: * '''target''' ** type: container with 1 of N choice of leafs ** usage: mandatory ** default: none ** This parameter specifies the name of the target database to be validated. ** container contents: 1 of N: *** '''candidate''' **** type: empty **** capabilities needed:''' ''':candidate *** '''running''' **** type: empty **** capabilities needed: none *** '''startup''' **** type: empty **** capabilities needed: startup *** '''url''' **** type: yang:uri **** capabilities needed: :url, and the scheme used in the URL must be specified in the :url capability 'schemes' parameter. **** To enter this parameter, the interactive mode must be used. The shorthand mode (target=url) cannot be used, since this parameter contains a string. *** '''config''' **** type: anyxml **** capabilities **** This parameter represents an entire database, using the in-line config form, similar to the '''edit-config '''command, except this config parameter contains no nc:operation attributes and must be a complete database, not a subset. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Usage: yangcli andy@myserver> '''validate source=candidate''' RPC OK Reply 36 for session 10: yangcli [mailto:andy@myagent andy@myserver]> === while === The '''while''' command is used to start a conditional loop command block. The '''expr''' parameter is used to specify the XPath expression to test if the while conditional loop block is true or false. A false block will be skipped and a true block will be executed. The command prompt will contain the string '[F]' while inside a false conditional block in interactive mode. This expression string should be entered with quotes to avoid misinterpreting any whitespace or special characters. The '''docroot''' parameter (if present) specifies the XML document that the 'expr' parameter should be evaluated against. This is optional, since only XPath path expressions need to refer to a document. If the 'expr' expression is true, the conditional block will be executed, and no further conditional blocks within the same if command sequence will be executed. The maxloops parameter specifies the maximum number of iterations that should be allowed, even if the expression continues to evaluate to 'true'. This allows constant expressions to be used to construct a simple 'for' loop. while command ... end command
'''while command'''
{| class=wikitable border="1" | Command type: | local |- | Default parameter: | expr |- | Min parameters: | 1 |- | Max parameters: | 3 |- | Return type: | status |- | YANG file: | yangcli.yang |} Command Parameters: * '''expr''' ** type: XPath expression string ** usage: mandatory ** default: none ** This parameter specifies the XPath expression to determine if the following commands are within a true or false conditional block. * '''docroot''' ** type: anyxml ** usage: optional (typically a variable reference is used) ** default: none ** This parameter specifies the XML document that should be used if the expr XPath expression references any path nodes. * '''maxloops''' ** type: uint32 ** usage: optional ** default: 65535 ** Set a maximum number of loops that can be iterated for this while command. The value zero means that no maximum will be enforced. Use this mode with caution. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. ** invalid XPath expression or invalid docroot reference will cause an error Usage: yangcli andy@myserver> '''while 1 --maxloops=10''' yangcli [mailto:andy@myagent andy@myserver]>''' while '$x < 10'''' yangcli [mailto:andy@myagent andy@myserver]> === xget === The '''xget''' command is used to invoke a NETCONF operation with an XPath filter.
'''xget command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | select |- | Min parameters: | 1 |- | Max parameters: | 3 |- | Return type: | data |- | YANG file: | yangcli.yang |- | Capabilities needed: | none |- | Capabilities optional: | :with-defaults |} Command Parameters: * '''choice 'from' '''(not entered) ** type: choice of case 'varref' or case 'select' ** usage: mandatory ** default: none ** This parameter specifies the where '''yangcli''' should get the data from, for the XPath filter target of the operation. It is an XPath expression, either contained in a user variable or directly from the '''select''' parameter. *** '''varref''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the name of the user variable the contains the XPath expression for the XPath filter in the operation. The parameter must exist (e.g., created with an assignment statement) or an error message will be printed. *** '''select''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the XPath expression to use for the XPath filter in the operation. * '''timeout''' ** type: uint32 (0 = no timeout, otherwise the number of seconds to wait) * ** usage: optional ** default: set to the '''$$timeout''' system variable, default 30 seconds ** This parameter specifies the number of seconds to wait for a response from the server before giving up. The session will not be dropped if a remote command times out, but any late response will be dropped. A value of zero means no timeout should be used, and '''yangcli''' will wait forever for a response. * '''with-defaults''' ** type: enumeration (none report-all report-all-tagged trim explicit) ** usage: optional ** default: none ** capabilities needed: with-defaults ** This parameter controls how nodes containing only default values are returned in the . System Variables: * '''$$timeout''' ** The response timeout interval will be derived from this system variable. * '''$$with-defaults''' ** The parameter for the operation will be derived from this system variable. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Output: * '''data''' ** type: anyxml ** This element will contain the requested data from the database or non-config data structures. Usage: yangcli andy@myserver> '''xget //name''' RPC Data Reply 42 for session 10: rpc-reply { data { nacm { rules { data-rule nacm-tree { name nacm-tree } } } xpath.1 { name barney } netconf-state { datastores { datastore { name { candidate } } } } netconf { streams { stream NETCONF { name NETCONF } } } } } } yangcli andy@myserver> Reference: * RFC 4741, section 7.7 === xget-config === The '''xget-config''' command is used to invoke a NETCONF operation with an XPath filter.
'''xget-config command'''
{| class=wikitable border="1" | Command type: | remote |- | Default parameter: | select |- | Min parameters: | 2 |- | Max parameters: | 4 |- | Return type: | data |- | YANG file: | yangcli.yang |- | Capabilities needed: | none |- | Capabilities optional: | :candidate:startup:url:with-defaults |} Command Parameters: * '''choice 'from' '''(not entered) ** type: choice of case 'varref' or case 'select' ** usage: mandatory ** default: none ** This parameter specifies the where '''yangcli''' should get the data from, for the XPath filter target of the operation. It is an XPath expression, either contained in a user variable or directly from the '''select''' parameter. *** '''varref''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the name of the user variable the contains the XPath expression for the XPath filter in the operation. The parameter must exist (e.g., created with an assignment statement) or an error message will be printed. *** '''select''' **** type: string **** usage: mandatory **** default: none **** This parameter specifies the XPath expression to use for the XPath filter in the operation. * '''source''' ** type: container with 1 of N choice of leafs ** usage: mandatory ** default: none ** This parameter specifies the name of the source database for the retrieval operation. ** container contents: 1 of N: *** '''candidate''' **** type: empty **** capabilities needed:''' ''':candidate *** '''running''' **** type: empty **** capabilities needed: none *** '''startup''' **** type: empty **** capabilities needed: startup *** '''url''' **** type: yang:uri **** capabilities needed: :url, and the scheme used in the URL must be specified in the :url capability 'schemes' parameter **** To enter this parameter, the interactive mode must be used. The shorthand mode (source=url) cannot be used, since this parameter contains a string. * '''timeout''' ** type: uint32 (0 = no timeout, otherwise the number of seconds to wait) * ** usage: optional ** default: set to the '''$$timeout''' system variable, default 30 seconds ** This parameter specifies the number of seconds to wait for a response from the server before giving up. The session will not be dropped if a remote command times out, but any late response will be dropped. A value of zero means no timeout should be used, and '''yangcli''' will wait forever for a response. * '''with-defaults''' ** type: enumeration (none report-all report-all-tagged trim explicit) ** usage: optional ** default: none ** capabilities needed: with-defaults ** This parameter controls how nodes containing only default values are returned in the . System Variables: * '''$$timeout''' ** The response timeout interval will be derived from this system variable. * '''$$with-defaults''' ** The parameter for the operation will be derived from this system variable. Positive Response: * the server returns Negative Response: * An error message will be printed if errors are detected locally. * An message will be printed if the server detected an error. Output: * '''data''' ** type: anyxml ** This element will contain the requested data from the '''source''' database. Usage: yangcli andy@myserver> '''xget /nacm/groups \''' '''source=running''' RPC Data Reply 41 for session 10: rpc-reply { data { nacm { groups { group nacm:admin { groupIdentity nacm:admin userName andy userName fred userName barney } group nacm:guest { groupIdentity nacm:guest userName guest } } } } } yangcli andy@svnserver> = CLI Reference = The Yuma programs uses command line interface (CLI) parameters to control program behavior. Many parameters are supported by more than one program, such as''' --log.''' The following sections document all the Yuma CLI parameters, in alphabetical order. == --aliases-file == The –'''aliases-file''' parameter specifies the yangcli command aliases file specification to use.
'''--aliases-file parameter'''
{| class=wikitable border="1" | Syntax | string |- | Default: | $HOME/.yuma/.yangcli_aliases |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli –aliases-file=~/myalaises |} == --alt-names == The –'''alt-names''' parameter controls whether alternative names are searched when processing a UrlPath search.
'''--alt-names parameter'''
{| class=wikitable border="1" | Syntax | boolean (true or false) |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --alt-names=false |} == --autoaliases == The '''--autoaliases''' parameter controls automatic loading and saving of the command aliases file. If 'true', then aliases will be loaded and saved automatically. If 'false', then aliases must be loaded and saved manually with the 'aliases' command.
'''--autoaliases parameter'''
{| class=wikitable border="1" | Syntax | boolean (true or false) |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --autoaliases=false |} == --autocomp == The '''--autocomp''' parameter controls automatic completion of command and parameter names. If true, then the program will attempt to find a partial match by comparing only the number of characters entered. For example '--log-l=debug' is the same as '--log-level=debug'. If 'false', then command and parameter names must match exactly.
'''--autocomp parameter'''
{| class=wikitable border="1" | Syntax | boolean (true or false) |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --autocomp=false |} == --autohistory == The '''--autohistory''' parameter controls automatic loading and saving of the command line history buffer in the '''yangcli''' program.. If true, then when the program starts, the default command line history buffer will be loaded, and the previous set of commands will be available with the '''history''' and '''recall''' commands. If 'false', then the command line history buffer will not be loaded and saved automatically. The default location for this file is ~/.yuma/.yangcli_history.
'''--autohistory parameter'''
{| class=wikitable border="1" | Syntax | boolean (true or false) |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --autohistory=false |} == --autoload == The '''--autoload''' parameter controls automatic loading of YANG modules, as needed. If true, then the program will attempt to find a needed YANG module. If 'false', then YANG modules must be loaded manually.
'''--autoload parameter'''
{| class=wikitable border="1" | Syntax | boolean (true or false) |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --autoload=false |} == --autouservars == The '''--autouservars''' parameter controls automatic loading of global user variables. If 'true', then the global user variables will be automatically loaded and saved. If 'false', then the global user variables must be loaded and saved manually with the 'uservars' command.
'''--autouservars parameter'''
{| class=wikitable border="1" | Syntax | boolean (true or false) |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --autouservars=false |} == --bad-data == The''' --bad-data''' parameter controls how invalid parameter input is handled by the program. * '''ignore''': Use invalid parameter values. Use with caution. * '''warn''': Issue a warning message, but use the invalid data anyway. * '''check''': Prompt the user interactively, and check if the invalid data should be used, or a new value re-entered. * '''error''': Do not use invalid parameter values.
'''--bad-data parameter'''
{| class=wikitable border="1" | Syntax | enumeration: '''ignore warn check error''' |- | Default: | check |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli ----bad-data=warn |} == --batch-mode == The '''--batch-mode''' parameter specifies that the interactive CLI will not be used. Instead, if a script is provided with the 'run-script' parameter, or a command provided with the 'run-command' parameter, then it will be executed automatically before the program exits. This mode can be used to invoke NETCONF commands from Unix shell scripts.
'''--batch-mode parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --batch-mode \ --run-script=~/run-tests |} == --config == The '''--config''' parameter specifies the name of a Yuma configuration file that contains more parameters to process, in addition to the CLI parameters. Refer to the 'Configuration Files' section for details on the format of this file.
'''--config parameter'''
{| class=wikitable border="1" | Syntax | string: complete file specification of the text file to parse for more parameters. |- | Default: | /etc/yuma/.conf |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli testmod \ --config=~/testconf.conf |} == --datapath == The '''--datapath''' parameter specifies the directory search path to use while searching for data files. It consists of a colon (':') separated list of path specifications, commonly found in Unix, such as the '''$PATH''' environment variable. This parameter overrides the '''$YUMA_DATAPATH''' environment variable, if it is present.
'''--datapath parameter'''
{| class=wikitable border="1" | Syntax | string: list of directory specifications |- | Default: | '''$YUMA_DATAPATH''' environment variable |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli \ --datapath=â€~/work2:~/data†|} == --default-module == The '''--default-module''' parameter specifies the module to search for identifiers, after the '''netconf''' and '''ncx''' modules have been checked. This allows a specific module to match identifier names first, before all modules are searched at once. This can avoid a collision if the same identifier value is used in more than one module.
'''--default-module parameter'''
{| class=wikitable border="1" | Syntax | string: module name without any path or file extension included. |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --default-module=testmod |} == --deviation == The '''--deviation''' parameter is a leaf-list of modules that should be loaded automatically when the program starts, as a deviation module. In this mode, only the deviation statements are parsed and then made available later when the module that contains the objects being deviated is parsed. The deviations must be known to the parser before the target module is parsed. This parameter is used to identify any modules that have deviation statements for the set of modules being parsed (e.g., '''--module''' and''' --subtree''' parameters). A module can be listed with both the '''--module''' and '''--deviation''' parameters, but that is not needed unless the module contains external deviations. If the module only contains deviations for objects in the same module, then the''' --deviation '''parameter does not need to be used. The program will attempt to load each module in deviation parsing mode, in the order the parameters are entered. For the '''netconfd''' program, If any modules have fatal errors then the program will terminate. For the '''yangdump '''and''' yangcli''' programs, each module will be processed as requested.
'''--deviation parameter'''
{| class=wikitable border="1" | Syntax | module name or filespec |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | netconfdyangcliyangdump |- | Example: | yangcli \ --module=test1 \ --deviation=test1_deviations |} == --display-mode == The '''--display-mode''' parameter controls how data is displayed in the program. The qualified names (identifiers, idenitityref, XPath) within a text configuration can be generated several ways. This is needed because sibling nodes in different XML namespaces can have the same name.
'''--display-mode values'''
{| class=wikitable border="1" |
'''enum value'''
|
'''description'''
|
'''example'''
|- | plain | no prefix, just local-name | sysBootError |- | prefix | XML prefix and local-name | sys:sysBootError |- | module | module name and local name | system:sysBootError |- | xml | XML format | |- | xml-nons | XML format without namespace (xmlns) attributes | |- | json | JSON format | { “sysBootError†: “blah†} |} When saving configuration files in non-volatile storage, then it is suggested that only 'module' or 'xml' modes be used, since these are the only deterministic modes. The set of XML prefixes in use at any one time is not persistent, and cannot be relied upon, unless the namespace declarations are saved (xml mode) or the module names are saved (module mode).
'''display-mode parameter'''
{| class=wikitable border="1" | Syntax | enumeration: '''plain prefix module xml xml-nons json''' |- | Default: | plain |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --display-mode=module |} == --feature-disable == The '''--feature-disable''' parameter directs all programs to disable a specific feature. This parameter is a formatted string containing a module name, followed by a colon ':', followed by a feature name, e.g., test:feature1 It is an error if a '''--feature-enable''' and '''--feature-disable''' parameter specify the same feature. Parameters for unknown features will be ignored.
'''--feature-disable parameter'''
{| class=wikitable border="1" | Syntax | formatted string (module:feature |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | yangcliyangdiffyangdumpnetconfd |- | Example: | yangcli --feature-disable=test:feature1 |} == --feature-enable == The '''--feature-enable''' parameter directs all programs to enable a specific feature. This parameter is a formatted string containing a module name, followed by a colon ':', followed by a feature name, e.g., test:feature1 It is an error if a '''--feature-disable''' and '''--feature-enable''' parameter specify the same feature. Parameters for unknown features will be ignored.
'''--feature-enable parameter'''
{| class=wikitable border="1" | Syntax | formatted string (module:feature |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | yangcliyangdiffyangdumpnetconfd |- | Example: | yangcli--feature-enable=test:feature1 |} == --feature-enable-default == The '''--feature-enable-default''' parameter controls how '''yangdump''' will generate C code for YANG features by default. If 'true', then by default, features will be enabled. If 'false', then by default, features will be disabled. If a '''--feature-enable''' or '''--feature-disable''' parameter is present for a specific feature, then this parameter will be ignored for that feature.
'''--feature-enable-default parameter'''
{| class=wikitable border="1" | Syntax | boolean (true or false) |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcliyangdiffyangdumpnetconfd |- | Example: | yangcli \ --feature-enable-default=false |} == --fixorder == The '''--fixorder''' parameter controls whether PDU parameters will be automatically sent in NETCONF PDUs in the correct order. If 'true', the schema-defined, canonical order will be used. If 'false', the specified order that parameters are entered will be used for the PDU order as well.
'''--fixorder parameter'''
{| class=wikitable border="1" | Syntax | boolean (true or false) |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --fixorder=false |} == --force-target == The '''--force-target''' parameter controls whether the candidate or running configuration datastore will be used as the default edit target, when both are supported by the server.
'''--force-target parameter'''
{| class=wikitable border="1" | Syntax | enum (candidate or running) |- | Default: | candidate |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --force-target=running |} == --help == The '''--help''' parameter causes program help text to be printed, and then the program will exit instead of running as normal. This parameter can be combined with the '''--help-mode''' parameter to control the verbosity of the help text. Use '''--brief''' for less, and '''--full '''for more than the normal verbosity. This parameter can be combined with the '''--version''' parameter in all programs. It can also be combined with the '''--show-errors''' parameter in '''yangdump'''. The program configuration parameters will be displayed in alphabetical order, not in schema order.
'''--help parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli --help |} == --help-mode == The '''--help-mode''' parameter is used to control the amount of detail printed when help text is requested in some command. It is always used with another command, and makes no sense by itself. It is ignored unless used with the '''--help''' parameter.
'''--help-mode parameter'''
{| class=wikitable border="1" | Syntax | choice of 3 empty leafs '''--brief --normal --full''' |- | Default: | normal |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli --help --help-mode=full |} * '''default choice: '''normal * '''choice help-mode''' ** '''brief''' *** type: empty *** This parameter specifies that brief documentation mode should be used. ** '''normal''' *** type: empty *** This parameter specifies that normal documentation mode should be used. ** '''full''' *** type: empty *** This parameter specifies that full documentation mode should be used. == --indent == The '''--indent''' parameter specifies the number of spaces that will be used to add to the indentation level, each time a child node is printed during program operation.
'''--indent parameter'''
{| class=wikitable border="1" | Syntax | uint32 (0 .. 9) |- | Default: | 3 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli --indent=4 |} == --log == The '''--log''' parameter specifies the file name to be used for logging program messages, instead of STDOUT. It can be used with the optional''' --log-append''' and '''--log-level''' parameters to control how the log file is written.
'''--log parameter'''
{| class=wikitable border="1" | Syntax | string: log file specification |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli --log=~/test.log& |} == --log-append == The '''--log-append''' parameter specifies that the existing log file (if any) should be appended , instead of deleted. It is ignored unless the '''--log''' parameter is present.
'''--log-append parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli --log-append \ --log=~/test.log& |} == --log-level == The '''--log-level''' parameter controls the verbosity level of messages printed to the log file or STDOUT, if no log file is specified. The log levels are incremental, meaning that each higher level includes everything from the previous level, plus additional messages. There are 7 settings that can be used: * '''none''': All logging is suppressed. Use with extreme caution! * '''error''': Error messages are printed, indicating problems that require attention. * '''warn''': Warning messages are printed, indicating problems that may require attention. * '''info''': Informational messages are printed, that indicate program status changes. * '''debug''': Debugging messages are printed that indicate program activity. * '''debug2''': Protocol debugging and trace messages are enabled. * '''debug3''': Very verbose debugging messages are enabled. This has an impact on resources and performance, and should be used with caution. * '''debug4''': Maximum debugging messages are enabled.
'''log-level parameter'''
{| class=wikitable border="1" | Syntax | enumeration: '''off error warn info debug debug2 debug3''' '''debug4''' |- | Default: | --info (--debug for DEBUG builds) |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli --log-level=debug \ --log=~/test.log& |} == --match-names == The '''--match-names''' parameter specifies how names are matched when performing UrlPath searches. The following values are supported:: * '''exact''' The name must exactly match the node name for all characters in both name strings. * '''exact-nocase''' The name must match the node name for all characters in both name strings. Strings are not case-sensitive. * '''one''' The name must exactly match the first N characters of just one node name, which must be the only partial name match found. * '''one-nocase''' The name must exactly match the first N characters of just one node name, which must be the only partial name match found. Strings are not case-sensitive. * '''first''' The name must exactly match the first N characters of any node name. The first one found will be used. * '''first-nocase''' The name must exactly match the first N characters of any node name. The first one found will be used. Strings are not case-sensitive.
'''--match-names parameter'''
{| class=wikitable border="1" | Syntax | enum: exact exact-nocase one one-nocase first first-nocase |- | Default: | one-nocase |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --match-names=exact |} == --modpath == The '''--modpath''' parameter specifies the YANG module search path to use while searching for YANG files. It consists of a colon (':') separated list of path specifications, commonly found in Unix, such as the '''$PATH''' environment variable. This parameter overrides the '''$YUMA_MODPATH''' environment variable, if it is present.
'''--modpath parameter'''
{| class=wikitable border="1" | Syntax | string: list of directory specifications |- | Default: | '''$YUMA_MODPATH''' environment variable |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli \ --modpath=â€~/testmodules:~/modules:~/trunk/netconf/modules†|} == --module == The '''--module''' parameter is a leaf-list of modules that should be loaded automatically when the program starts. The program will attempt to load each module in the order the parameters were entered. For the '''netconfd''' program, If any modules have fatal errors then the program will terminate. For the '''yangdump''' program, each module will be processed as requested.
'''--module parameter'''
{| class=wikitable border="1" | Syntax | module name or filespec |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | netconfdyangcliyangdump |- | Example: | yangcli \ --module=test1 \ --module=test2 |} == --ncport == The '''--ncport''' parameter specifies the TCP port number that should be used for NETCONF sessions. This parameter specifies the TCP port number to use when a NETCONF session is created during the startup of the program. The parameter can only be entered once.
'''--ncport parameter'''
{| class=wikitable border="1" | Syntax | uint32 |- | Default: | 830 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --server=myserver --ncport=22 \ --user=fred --password=mypassword |} == --password == The '''--password''' parameter specifies the password string that should be used when a NETCONF session is connected during the startup of the program.
'''--password parameter'''
{| class=wikitable border="1" | Syntax | string |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --server=myserver \ --password=yangrocks \ --user=andy |} == --private-key == The '''--private-key''' parameter specifies the file path specification for the file containing the client-side private key. If both 'public-key' and 'private-key' files are present, the client will attempt to connect to the server using these keys. If this fails, or not done, then password authentication will be attempted.
'''--private-key parameter'''
{| class=wikitable border="1" | Syntax | string |- | Default: | $HOME/.ssh/id_rsa |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli –private-key=~/.ssh/mykey |} == --private-key-pass == The '''--privkey-pass''' parameter specifies the passphrase used to unlock an encrypted client-side private key.
'''--private-key-pass parameter'''
{| class=wikitable border="1" | Syntax | string |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --private-key-pass='my-secret-passphrase' |} == --protocols == The '''--protocols''' parameter specifies which protocol versions the program or session will attempt to use. Empty set is not allowed.
'''--protocols parameter'''
{| class=wikitable border="1" | Syntax | bits |- | Default: | “netconf1.0 netconf1.1†|- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcli |- | Example: | yangcli –protocols=netconf1.0 |} == --public-key == The '''--public-key''' parameter specifies the file path specification for the file containing the client-side public key. If both 'public-key' and 'private-key' files are present, the client will attempt to connect to the server using these keys. If this fails, or not done, then password authentication will be attempted.
'''--public-key parameter'''
{| class=wikitable border="1" | Syntax | string |- | Default: | $HOME/.ssh/id_rsa.pub |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli –public-key=~/.ssh/mykey.pub |} == --runpath == The '''--runpath''' parameter specifies the directory search path to use while searching for script files. It consists of a colon (':') separated list of path specifications, commonly found in Unix, such as the '''$PATH''' environment variable. This parameter overrides the '''$YUMA_RUNPATH''' environment variable, if it is present.
'''--runpath parameter'''
{| class=wikitable border="1" | Syntax | string: list of directory specifications |- | Default: | '''$YUMA_RUNPATH''' environment variable |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli \ --runpath=â€~/testscripts†|} == --run-command == The '''--run-command '''parameter specifies a command will be invoked upon startup. In the '''yangcli''' program, if the auto-connect parameters are provided, then a session will be established before running the command.
'''--run-command parameter'''
{| class=wikitable border="1" | Syntax | string |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli \ --run-command="history load=~/test3-history" |} == --run-script == The '''--run-script '''parameter specifies a script will be invoked upon startup. In the '''yangcli''' program, if the auto-connect parameters are provided, then a session will be established before running the script.
'''--run-script parameter'''
{| class=wikitable border="1" | Syntax | string (file specification) |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli \ --run-script="test3-start" |} == --server == The '''--server''' parameter specifies the IP address or DNS name of the NETCONF server that should be connected to automatically, when the program starts.
'''--server parameter'''
{| class=wikitable border="1" | Syntax | string:IP address or DNS name |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --server=myserver |} == --subdirs == The '''--subdirs''' parameter controls whether sub-directories should be searched or not, if they are found during a module search. If false, the file search paths for modules, scripts, and data files will not include sub-directories if they exist in the specified path.
'''--subdirs parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdump |- | Example: | yangcli --subdirs=false |} == --time-rpcs == The '''--time-rpcs''' parameter is used to measure the round-trip time of each request and at the session level. Echo the elapsed time value to screen if in interactive mode, as well as the log if the log is a file instead of stdout. The $$time-rpcs system variable is derived from this parameter.";
'''--time-rpcs parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | FALSE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --time-rpcs=true |} == --timeout == The '''--timeout''' parameter specifies the number of seconds that should pass before giving up on a response during a NETCONF session. The value zero means wait forever (no timeout).
'''--timeout parameter'''
{| class=wikitable border="1" | Syntax | uint32 (0 for no timeout; otherwise number of seconds to wait) |- | Default: | 30 seconds |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --timeout=0 |} == --transport == The '''--transport''' parameter specifies the NETCONF transport protocol to use. This parameter is also supported in the 'connect' command. There are 2 values supported: # ssh: standard NETCONF SSH transport (RFC 4742 or RFC 6242) # tcp: tail-f NETCONF over TCP transport
Note that '''netconfd''' does not support this transport protocol! I
'''--timeout parameter'''
{| class=wikitable border="1" | Syntax | Enum (ssh or tcp) |- | Default: | ssh |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --transport=tcp |} == --use-xmlheader == The '''--use-xmlheader''' parameter specifies how file result variables will be written for XML files. Controls whether the XML preamble header will be written or not.
'''--use-xmlheader parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --use-xmlheader=false |} == --user == The '''--user''' parameter specifies the user name string on the NETCONF server that should be used when establishing a NETCONF session.
'''--user parameter'''
{| class=wikitable border="1" | Syntax | string:user name |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli --user=admin \ --server=myserver |} == --uservars-file == The '''--uservars-file''' parameter contains the file specification of the user global variables XML file to use.
'''--uservars-file parameter'''
{| class=wikitable border="1" | Syntax | string:filespec |- | Default: | $HOME/.yuma/yangcli_uservars.xml |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcli |- | Example: | yangcli –uservars-file=~/myvars.xml |} == --version == The '''--version''' parameter causes the program version string to be printed, and then the program will exit instead of running as normal. All Yuma version strings use the same format: DEBUG: .. or NON-DEBUG: .- An example version number that may be printed: yangcli 1.15-4 This parameter can be combined with the '''--help '''parameter.
'''--version parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli --version |} == --warn-idlen == The''' --warn-idlen''' parameter controls whether identifier length warnings will be generated. The value zero disables all identifier length checking. If non-zero, then a warning will be generated if an identifier is defined which has a length is greater than this amount.
'''--warn-idlen parameter'''
{| class=wikitable border="1" | Syntax | uint32: 0 to disable, or 8 .. 1023 |- | Default: | 64 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli --warn-idlen=50 |} == --warn-linelen == The''' --warn-linelen''' parameter controls whether line length warnings will be generated. The value zero disables all line length checking. If non-zero, then a warning will be generated if a YANG file line is entered which has a length is greater than this amount. Tab characters are counted as 8 spaces.
'''--warn-linelen parameter'''
{| class=wikitable border="1" | Syntax | uint32: 0 to disable, or 40 .. 4095 |- | Default: | 72 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli --warn-linelen=79 |} == --warn-off == The''' --warn-off''' parameter suppresses a specific warning number. The error and warning numbers, and the default messages, can be viewed with the yangdump program by using the '''--show-errors''' configuration parameter. The specific warning message will be disabled for all modules. No message will be printed and the warning will not count towards the total for that module.
'''--warn-off parameter'''
{| class=wikitable border="1" | Syntax | uint32: 400 .. 899 |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 499 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli --warn-off=435 # revision order not descending |} == --yuma-home == The '''--yuma-home''' parameter specifies the project directory root to use when searching for files. If present, this directory location will override the ''''$YUMA_HOME''' environment variable, if it is set. If this parameter is set to a zero-length string, then the $'''YUMA_HOME''' environment variable will be ignored. The following directories are searched when either the '''$YUMA_HOME''' environment variable or this parameter is set: * '''$YUMA_HOME/modules''' ** This sub-tree is searched for YANG files. * '''$YUMA_HOME/data''' ** This directory is searched for data files. * '''$YUMA_HOME/scripts''' ** This directory is searched for '''yangcli''' script files.
'''yuma-home parameter'''
{| class=wikitable border="1" | Syntax | string: directory specification |- | Default: | '''$YUMA_HOME''' environment variable |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli \ --yuma-home=~/sw/netconf \ --log=~/test.log& |} yuma123_2.14/netconf/doc/yuma_docs/yuma-yangdiff-manual.txt0000664000175000017500000010041514770023131024064 0ustar vladimirvladimir
'''Yuma yangdiff Manual'''
YANG-Based Unified Modular Automation Tools
YANG Module Compare Tool
Version yuma123-2.13
== Legal Statements == Copyright 2009 – 2012, Andy Bierman, All Rights Reserved. == Additional Resources == This document assumes you have successfully set up the software as described in the printed document: Yuma Installation Guide Other documentation includes: Yuma Quickstart Guide Yuma User Manual Yuma netconfd Manual Yuma yangcli Manual Yuma yangdump Manual Yuma Developer Manual === WEB Sites === * '''Netconf Central''' ** [http://www.netconfcentral.org/ http://www.netconfcentral.org/] ** Yuma Home Page *** Free information on NETCONF and YANG, tutorials, on-line YANG module validation and documentation database * '''Yuma SourceFource OpenSource Project''' ** [http://sourceforge.net/projects/yuma/ http://sourceforge.net/projects/yuma/] *** Download Yuma source and binaries; project forums and help * '''Yang Central''' ** [http://www.yang-central.org/ http://www.yang-central.org] ** Free information and tutorials on YANG, free YANG tools for download * '''NETCONF Working Group Wiki Page''' ** [http://trac.tools.ietf.org/wg/netconf/trac/wiki http://trac.tools.ietf.org/wg/netconf/trac/wiki] ** Free information on NETCONF standardization activities and NETCONF implementations * '''NETCONF WG Status Page''' ** http://tools.ietf.org/wg/netconf/ ** IETF Internet draft status for NETCONF documents * '''libsmi Home Page''' ** [http://www.ibr.cs.tu-bs.de/projects/libsmi/ http://www.ibr.cs.tu-bs.de/projects/libsmi/] ** Free tools such as smidump, to convert SMIv2 to YANG * '''YumaWorks''' ** [http://www.yumaworks.com/ http://www.yumaworks.com] ** Offers support, training, and consulting for Yuma. ** Offers YumaPro, a professional version of Yuma that includes concurrency, external database support, sub-agent support, multiple northbound interfaces, and more. API compatible with Yuma. Availability: September, 2012. Licensed. === Mailing Lists === * '''NETCONF Working Group''' ** http://www.ietf.org/html.charters/netconf-charter.html ** Technical issues related to the NETCONF protocol are discussed on the NETCONF WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. * '''NETMOD Working Group''' ** [http://www.ietf.org/html.charters/netmod-charter.html http://www.ietf.org/html.charters/netmod-charter.html] ** Technical issues related to the YANG language and YANG data types are discussed on the NETMOD WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. == Conventions Used in this Document == The following formatting conventions are used throughout this document:
'''Documentation Conventions'''
{| class=wikitable border="1" !
Convention
!
Description
|- | '''--foo''' | CLI parameter foo |- | '''''' | XML parameter foo |- | '''foo''' | '''yangcli''' command or parameter |- | '''$FOO''' | Environment variable FOO |- | '''$$foo''' | '''yangcli''' global variable foo |- | some text | Example command or PDU |- | some text | Plain text |} = yangdiff User Guide = [[Image:]] == Introduction == The '''yangdiff''' program is used to compare two revisions of the same YANG file. === Features === The '''yangdiff''' program has the following features: * The conceptual YANG object trees are compared, not the actual YANG statements. * Two separate subtrees of modules can be compared, not just 1 file at a time. * The differences report format for differences is easy to read, and can be configured with 2 different levels of verbosity. * A YANG revision statement can be automatically generated instead of a list of differences. This will include an English text summary of the differences found. === Starting yangdiff === The current working directory in use when '''yangdiff''' is invoked is important. It is most convenient to run '''yangdiff''' from a work directory, rather than the installation directory or within the module library. The '''yangdiff''' program can be invoked several ways: * To get the current version and exit: '''yangdiff --version''' * To get program help and exit: '''yangdiff --help''' '''yangdiff --help --brief''' '''yangdiff --help --full''' * To compare a new YANG module named 'foo', with an old version named foo.2008-09-01.yang, the following command line can be used '''yangdiff --old=foo.2008-09-01.yang --new=foo''' * To get all the configuration parameters from a text file named '~/yangdiff-project1.conf': '''yangdiff'' --''config''=''~/yangdiff-project1.conf''' * To generate a terse differences report called ''~/project-X-diffs.log'' for the old files in the '''/public/project-X/modules''' with the new modules in the '''~/work''' directory: '''yangdiff'' --''difftype=terse --output=~/project-X-diffs.log \''' '''--old=/public/project-X/modules \''' '''--new=/work''' * To generate a revision statement differences to ''~/foo-projectX-revision.txt'' for the '~/work/foo.yang' module, with the old version in the in the '''/public/project-X/modules''':
'''yangdiff'' --''difftype=revision --output=~/foo-project-X-revision.txt \''' '''--old=/public/project-X/modules \''' '''--new=/work/foo.yang''' === Stopping yangdiff === There is no interactive mode for '''yangdiff''', so there is no need for a command to exit the program. The Control C character sequence can be used to cancel the '''yangdiff''' processing in progress. However, this will leave any partially completed output files in place. === Configuration Parameter List === The following configuration parameters are used by '''yangdiff'''. Refer to the CLI Reference for more details.
'''yangdiff CLI Parameters'''
{| class=wikitable border="1" |
'''parameter'''
|
'''description'''
|- | --config | Specifies the configuration file to use for parameters. |- | --datapath | Sets the data file search path. |- | --difftype | Specifies the type of differences report that should be output. |- | --feature-disable | Leaf list of features to disable |- | --feature-enable | Specifies a feature that should be enabled |- | --feature-enable-default | Specifies if a feature should be enabled or disabled by default |- | --header | Specifies whether the module header data should be compared or not. |- | --help | Get context-sensitive help, then exit. |- | --help-mode | Adjust the help output (--brief, or --full). |- | --indent | Specifies the indent count to use when writing data. |- | --log | Specifies the log file to use instead of STDOUT. |- | --log-append | Controls whether a log file will be reused or overwritten. |- | --log-level | Controls the verbosity of logging messages. |- | --modpath | Sets the module search path. |- | --new | Specifies the location of the new revision to compare. |- | --old | Specifies the location of the old revision to compare. |- | --output | Specifies where output files should be generated. |- | --runpath | Sets the executable file search path. |- | --subdirs | Controls whether sub-directories are searched for YANG files. |- | --version | Prints the program version and then exit. |- | --warn-idlen | Controls how identifier lengths are checked. |- | --warn-linelen | Controls how line lengths are checked. |- | --warn-off | Suppresses the specified warning number. |- | --yuma-home | Specifies the '''$YUMA_HOME''' project root to use when searching for files. |} == Comparing YANG Modules == The '''yangdiff''' program compares YANG files in the following maner: * The cooked object trees are compared, not the actual YANG statements. * Whitespace differences are ignored. * Non-semantic changes are ignored: ** A uses-stmt can replace a set of objects if the grouping is identical to the old objects. ** Refine statements are combined into the cooked objects, and not compared directly. ** A typedef statement can replace an inline type statement ** Changes to YANG comments are ignored. ** Sub-statement order is ignored, however object order with a container or list is not ignored. ** Objects can be moved to submodules, and include statements instead. If the '''--old''' parameter is missing, then the module search path will be used to find the specified module with the same name. If the''' --old '''parameter contains just a module name, then the module search path will be used to find a module with the new name. The '''--new''' parameter is required. It can represent one YANG file or a directory of new YANG modules. The '''--difftype''' parameter is optional. The 'normal' report mode is used if this parameter is missing. The '''--output''' parameter will be used for the report file, if it is specified. {| class=wikitable border="1" |
'''symbol'''
|
'''description'''
|- |
'''A'''
| Definition has bend added. |- |
'''M'''
| Definition has been modified. |- |
'''D'''
| Definition has been deleted. |} == Diff Reports == This section uses the example module below (test/'''test3a.yang''') to demonstrate the different report formats available. The old module revision is '''test/test3.yang'''. The following command is used in all 3 examples, except the value of the --difftype parameter is changed each time. '''yangdiff --old=test3a --new=test3 –difftype=''' === Terse Report === If '''--difftype=terse''' is selected, then a brief summary of all changes will be listed. There will be no indentation, and only the change (Add, Modify, Delete), and the top-level definition is identified. // Generated by yangdiff 0.9.7.473 // Copyright (c) 2009, Netconf Central, All Rights Reserved. // old: test3 (2008-10-19) test3.yang // new: test3 (2009-09-09) test3a.yang D revision '2008-10-19' A revision '2009-09-09' A feature X A identity testbase A identity testbase1 M typedef C D container test-D1 D leaf test-D M container test33 === Normal Report === If '''--difftype=normal''' is selected, then a complete summary of all changes will be listed. If a change line is indented, it indicates a sub-statement of the object in the previous line has been changed. // Generated by yangdiff 0.9.7.473 // Copyright (c) 2009, Netconf Central, All Rights Reserved. // old: test3 (2008-10-19) test3.yang // new: test3 (2009-09-09) test3a.yang D revision '2008-10-19' A revision '2009-09-09' A feature X A identity testbase A identity testbase1 M typedef C M type M range from 'min .. 41 | 45' to 'min .. 41' D container test-D1 D leaf test-D M container test33 D presence 'not a top-level mand...' M choice f M case f1 M leaf f1 A if-feature 'X' === Revision Statement === If '''--difftype=revision''' is selected, then a complete summary of all changes will be printed in the form of a YANG revision statement. The current date will be used for the revision-date field of the revision statement. // Generated by yangdiff 0.9.7.473 // Copyright (c) 2009, Netconf Central, All Rights Reserved. // old: test3 (2008-10-19) test3.yang // new: test3 (2009-09-09) test3a.yang revision 2009-09-10 { description " - Removed revision '2008-10-19' - Added revision '2009-09-09' - Added feature X - Added identity testbase - Added identity testbase1 - Changed typedef C - Changed type - Changed range from 'min .. 41 | 45' to 'min .. 41' - Removed container test-D1 - Removed leaf test-D - Changed container test33 - Removed presence 'not a top-level mand...' - Changed choice f - Changed case f1 - Changed leaf f1 - Added if-feature 'X' "; } = CLI Reference = The '''yangdiff''' program uses command line interface (CLI) parameters to control program behavior. The following sections document all the Yuma CLI parameters relevant to this program, in alphabetical order. == --config == The '''--config''' parameter specifies the name of a Yuma configuration file that contains more parameters to process, in addition to the CLI parameters. Refer to the 'Configuration Files' section for details on the format of this file.
'''--config parameter'''
{| class=wikitable border="1" | Syntax | string: complete file specification of the text file to parse for more parameters. |- | Default: | /etc/yuma/yangdiff.conf |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdiff --old=test1 --new=test1a \ --config=~/testconf.conf |} == --difftype == The '''--difftype''' parameter controls how differences are displayed in the '''yangdiff''' program.. The allowed values are 'terse', 'normal', and 'revision'. The basic report format is: '''[add/delete/modify] field-name [field-value] ''' The ''''terse'''' option will include the names of the top-level fields that are different. The actual differences for modification lines ('M') are not printed. M typedef C D container test-D1 D leaf test-D M container test33 The ''''normal'''' option will include any changes for any nested fields or objects. A brief description of the changes made in a modification line ('M') are printed. This is the default reporting mode. M typedef C M type M range from 'min .. 41 | 45' to 'min .. 41' D container test-D1 D leaf test-D M container test33 D presence 'not a top-level mand...' M choice f M case f1 M leaf f1 A if-feature 'X' The ''''revision'''' option will generate the differences report in YANG revision-stmt format. For example: revision 2009-09-10 { description " - Changed typedef C - Changed type - Changed range from 'min .. 41 | 45' to 'min .. 41' - Removed container test-D1 - Removed leaf test-D - Changed container test33 - Removed presence 'not a top-level mand...' - Changed choice f - Changed case f1 - Changed leaf f1 - Added if-feature 'X' "; }
'''difftype parameter'''
{| class=wikitable border="1" | Syntax | enumeration: '''terse normal revision''' |- | Default: | normal |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdiff |- | Example: | yangdiff --difftype=revision \ --new=test3a\ --old=~test3 |} == --feature-disable == The '''--feature-disable''' parameter directs all programs to disable a specific feature. This parameter is a formatted string containing a module name, followed by a colon ':', followed by a feature name, e.g., test:feature1 It is an error if a '''--feature-enable''' and '''--feature-disable''' parameter specify the same feature. Parameters for unknown features will be ignored.
'''--feature-disable parameter'''
{| class=wikitable border="1" | Syntax | formatted string (module:feature |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | yangcliyangdiffyangdumpnetconfd |- | Example: | yangdiff --format=c \ --feature-disable=test:feature1 \ --module=test |} == --feature-enable == The '''--feature-enable''' parameter directs all programs to enable a specific feature. This parameter is a formatted string containing a module name, followed by a colon ':', followed by a feature name, e.g., test:feature1 It is an error if a '''--feature-disable''' and '''--feature-enable''' parameter specify the same feature. Parameters for unknown features will be ignored.
'''--feature-enable parameter'''
{| class=wikitable border="1" | Syntax | formatted string (module:feature |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | unlimited |- | Supported by: | yangcliyangdiffyangdumpnetconfd |- | Example: | yangdiff \ --feature-enable=test:feature1 \ --old=test --new=test-new |} == --feature-enable-default == The '''--feature-enable-default''' parameter controls how '''yangdump''' will generate C code for YANG features by default. If 'true', then by default, features will be enabled. If 'false', then by default, features will be disabled. If a '''--feature-enable''' or '''--feature-disable''' parameter is present for a specific feature, then this parameter will be ignored for that feature.
'''--feature-enable-default parameter'''
{| class=wikitable border="1" | Syntax | boolean (true or false) |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangcliyangdiffyangdumpnetconfd |- | Example: | yangdiff \ --feature-enable-default=false |} == --header == The '''--header''' parameter controls whether YANG header contents will be compared in the '''yangdiff''' program.
'''--header parameter'''
{| class=wikitable border="1" | Syntax | boolean (true or false) |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdiff |- | Example: | yangdiff --header=false \ --old=~/saved-modules \ -- new=~/work |} == --help == The '''--help''' parameter causes program help text to be printed, and then the program will exit instead of running as normal. This parameter can be combined with the '''--help-mode''' parameter to control the verbosity of the help text. Use '''--brief''' for less, and '''--full '''for more than the normal verbosity. This parameter can be combined with the '''--version''' parameter in all programs. It can also be combined with the '''--show-errors''' parameter in '''yangdump'''. The program configuration parameters will be displayed in alphabetical order, not in schema order.
'''--help parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdiff --help |} == --help-mode == The '''--help-mode''' parameter is used to control the amount of detail printed when help text is requested in some command. It is always used with another command, and makes no sense by itself. It is ignored unless used with the '''--help''' parameter.
'''--help-mode parameter'''
{| class=wikitable border="1" | Syntax | choice of 3 empty leafs '''--brief --normal --full''' |- | Default: | normal |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdiff --help --help-mode=full |} * '''default choice: '''normal * '''choice help-mode''' ** '''brief''' *** type: empty *** This parameter specifies that brief documentation mode should be used. ** '''normal''' *** type: empty *** This parameter specifies that normal documentation mode should be used. ** '''full''' *** type: empty *** This parameter specifies that full documentation mode should be used. == --indent == The '''--indent''' parameter specifies the number of spaces that will be used to add to the indentation level, each time a child node is printed during program operation.
'''--indent parameter'''
{| class=wikitable border="1" | Syntax | uint32 (0 .. 9) |- | Default: | 2 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangcli --indent=4 |} == --log == The '''--log''' parameter specifies the file name to be used for logging program messages, instead of STDOUT. It can be used with the optional''' --log-append''' and '''--log-level''' parameters to control how the log file is written.
'''--log parameter'''
{| class=wikitable border="1" | Syntax | string: log file specification |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdiff --log=~/test.log& |} == --log-append == The '''--log-append''' parameter specifies that the existing log file (if any) should be appended , instead of deleted. It is ignored unless the '''--log''' parameter is present.
'''--log-append parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdiff --log-append \ --log=~/server.log& |} == --log-level == The '''--log-level''' parameter controls the verbosity level of messages printed to the log file or STDOUT, if no log file is specified. The log levels are incremental, meaning that each higher level includes everything from the previous level, plus additional messages. There are 7 settings that can be used: * '''none''': All logging is suppressed. Use with extreme caution! * '''error''': Error messages are printed, indicating problems that require attention. * '''warn''': Warning messages are printed, indicating problems that may require attention. * '''info''': Informational messages are printed, that indicate program status changes. * '''debug''': Debugging messages are printed that indicate program activity. * '''debug2''': Protocol debugging and trace messages are enabled. * '''debug3''': Very verbose debugging messages are enabled. This has an impact on resources and performance, and should be used with caution.
'''log-level parameter'''
{| class=wikitable border="1" | Syntax | enumeration: '''off error warn info debug debug2 debug3''' |- | Default: | --info (--debug for DEBUG builds) |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdiff --log-level=debug \ --log=~/server.log& |} == --modpath == The '''--modpath''' parameter specifies the YANG module search path to use while searching for YANG files. It consists of a colon (':') separated list of path specifications, commonly found in Unix, such as the '''$PATH''' environment variable. This parameter overrides the '''$YUMA_MODPATH''' environment variable, if it is present.
'''--modpath parameter'''
{| class=wikitable border="1" | Syntax | string: list of directory specifications |- | Default: | '''$YUMA_MODPATH''' environment variable |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdiff \ --modpath=â€~/testmodules:~/modules:~/trunk/netconf/modules†|} == --new == The '''--new''' parameter specifies the YANG file or directory containing the new revision to be compared in the '''yangdiff''' program. If this parameter indicates a filename, then it represents the YANG source module name to compare as the newer of the two revisions. If this parameter indicates a directory (and the 'old' parameter indicates a filename), then it will be used to to search for a file with the same name as the 'new' parameter. If the 'old' parameter identifies a directory as well (and the 'no-subdirs' parameter is present), then the modules within the 'new' directory will be compared to files with the same name in the 'old' directory. If the '''--subdirs''' parameter is ''true'', then all sub-directories within the 'src' directory will also be checked. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path This parameter must be present unless the''' --help''' or '''--version''' parameters are present.
'''--new parameter'''
{| class=wikitable border="1" | Syntax | string (module or directory specification. length 1 .. 4095) |- | Default: | none |- | Min Allowed: | 1 |- | Max Allowed: | 1 |- | Supported by: | yangdiff |- | Example: | yangdiff \ --new=test3a --difftype=terse --old=test3\ |} == --old == The '''--old''' parameter specifies the YANG file or directory containing the older revision to be compared in the '''yangdiff''' program. If this parameter indicates a filename, then it represents the YANG source module name to compare as the older of the two revisions. If this parameter indicates a directory (and the 'old' parameter indicates a filename), then it will be used to to search for a file with the same name as the 'new' parameter. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path This parameter must be present unless the '''--help''' or '''--version''' parameters are present.
'''--old parameter'''
{| class=wikitable border="1" | Syntax | string (module or directory specification. length 1 .. 4095) |- | Default: | none |- | Min Allowed: | 1 |- | Max Allowed: | 1 |- | Supported by: | yangdiff |- | Example: | yangdiff \ --old=test3 --difftype=terse --new=test3a\ |} == --output == The '''--output''' parameter specifies where the output files generated by the program will be stored. * The default is STDOUT if this parameter is not specified and the '''--defnames''' parameter is set to 'false'. * If this parameter represents an existing directory, then the '''--defnames''' parameter will be set to 'true' by default. * If this parameter represents a file name, then the '''--defnames''' parameter will be ignored, and all translation output will be directed to the specified file. * If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path * If the target specified in this parameter '''does not '''exist: ** If there is only one file to be output, then this parameter is used as the file name. ** If there are multiple files to be output, then this parameter is used as a directory name. A new directory will be created, if it is needed. * If the target specified in this parameter '''does''' exist: ** If there is only one file to be output: *** If the existing target is also a file, then the current file is over-written. *** If the existing target is a directory, then the output file will be created in this directory. ** If there are multiple files to be output: *** If the existing target is a file, then an error will be generated instead of the output files. *** If the existing target is a directory, then the output files will be created in the specified directory. * Use a trailing path separator character to force this parameter value to be treated as a path specification (e.g., '~/workfiles/'). * This parameter is ignored if the '''--format''' parameter is missing.
'''--output parameter'''
{| class=wikitable border="1" | Syntax | string (path or file specification) |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdumpyangdiff |- | Example: | yangdiff \ --output=~/diff-files |} == --subdirs == The '''--subdirs''' parameter controls whether sub-directories should be searched or not, if they are found during a module search. If false, the file search paths for modules, scripts, and data files will not include sub-directories if they exist in the specified path.
'''--subdirs parameter'''
{| class=wikitable border="1" | Syntax | boolean |- | Default: | TRUE |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | yangdiff yangdump |- | Example: | yangiff \ --subdirs=false \ --subtree=/testpath |} == --version == The '''--version''' parameter causes the program version string to be printed, and then the program will exit instead of running as normal. All Yuma version strings use the same format: DEBUG: .. or NON-DEBUG: .- An example version number that may be printed: yangdiff 2.0-0 This parameter can be combined with the '''--help '''parameter.
'''--version parameter'''
{| class=wikitable border="1" | Syntax | empty |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdiff --version |} == --warn-idlen == The''' --warn-idlen''' parameter controls whether identifier length warnings will be generated. The value zero disables all identifier length checking. If non-zero, then a warning will be generated if an identifier is defined which has a length is greater than this amount.
'''--warn-idlen parameter'''
{| class=wikitable border="1" | Syntax | uint32: 0 to disable, or 8 .. 1023 |- | Default: | 64 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdiff --warn-idlen=50 |} == --warn-linelen == The''' --warn-linelen''' parameter controls whether line length warnings will be generated. The value zero disables all line length checking. If non-zero, then a warning will be generated if a YANG file line is entered which has a length is greater than this amount. Tab characters are counted as 8 spaces.
'''--warn-linelen parameter'''
{| class=wikitable border="1" | Syntax | uint32: 0 to disable, or 40 .. 4095 |- | Default: | 72 |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdiff --warn-linelen=79 |} == --warn-off == The''' --warn-off''' parameter suppresses a specific warning number. The error and warning numbers, and the default messages, can be viewed with the yangdump program by using the '''--show-errors''' configuration parameter. The specific warning message will be disabled for all modules. No message will be printed and the warning will not count towards the total for that module.
'''--warn-off parameter'''
{| class=wikitable border="1" | Syntax | uint32: 400 .. 899 |- | Default: | none |- | Min Allowed: | 0 |- | Max Allowed: | 499 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdiff --warn-off=435 # revision order not descending |} == --yuma-home == The '''--yuma-home''' parameter specifies the project directory root to use when searching for files. If present, this directory location will override the ''''$YUMA_HOME''' environment variable, if it is set. If this parameter is set to a zero-length string, then the $'''YUMA_HOME''' environment variable will be ignored. The following directories are searched when either the '''$YUMA_HOME''' environment variable or this parameter is set: * '''$YUMA_HOME/modules''' ** This sub-tree is searched for YANG files. * '''$YUMA_HOME/data''' ** This directory is searched for data files. * '''$YUMA_HOME/scripts''' ** This directory is searched for '''yangcli''' script files.
'''yuma-home parameter'''
{| class=wikitable border="1" | Syntax | string: directory specification |- | Default: | '''$YUMA_HOME''' environment variable |- | Min Allowed: | 0 |- | Max Allowed: | 1 |- | Supported by: | netconfdyangcliyangdiffyangdump |- | Example: | yangdiff \ --yuma-home=~/sw/netconf |} yuma123_2.14/netconf/doc/yuma_docs/yuma-user-cmn-manual.txt0000664000175000017500000023370114770023131024033 0ustar vladimirvladimir
'''Yuma User Manual'''
YANG-Based Unified Modular Automation Tools
Common User Manual
Version yuma123-2.13
= Preface = == Legal Statements == Copyright 2009 - 2012, Andy Bierman, All Rights Reserved. Copyright 2013 - 2022, Vladimir Vassilev, All Rights Reserved. == Additional Resources == This document assumes you have successfully set up the software as described in the printed document: [[Yuma Installation Guide]] Other documentation includes: [[Yuma Quickstart Guide]] [[Yuma netconfd Manual]] [[Yuma yangcli Manual]] [[Yuma Developer Manual]] There are several sources of free information and tools for use with YANG and/or NETCONF. The following section lists the resources available at this time. === WEB Sites === * '''Netconf Central''' ** [http://www.netconfcentral.org/ http://www.netconfcentral.org/] ** Yuma Home Page *** Free information on NETCONF and YANG, tutorials, on-line YANG module validation and documentation database * '''Yuma123 SourceForge OpenSource Project''' ** [http://sourceforge.net/projects/yuma123/ http://sourceforge.net/projects/yuma123/] *** Download Yuma123 source documentation * '''Yang Central''' ** [http://www.yang-central.org/ http://www.yang-central.org] ** Free information and tutorials on YANG, free YANG tools for download * '''NETCONF Working Group Wiki Page''' ** [http://trac.tools.ietf.org/wg/netconf/trac/wiki http://trac.tools.ietf.org/wg/netconf/trac/wiki] ** Free information on NETCONF standardization activities and NETCONF implementations * '''NETCONF WG Status Page''' ** http://tools.ietf.org/wg/netconf/ ** IETF Internet draft status for NETCONF documents * '''libsmi Home Page''' ** [http://www.ibr.cs.tu-bs.de/projects/libsmi/ http://www.ibr.cs.tu-bs.de/projects/libsmi/] ** Free tools such as smidump, to convert SMIv2 to YANG * '''YumaWorks''' ** [http://www.yumaworks.com/ http://www.yumaworks.com] ** Offers support, training, and consulting for Yuma. ** Offers YumaPro, a professional version of Yuma that includes concurrency, external database support, sub-agent support, multiple northbound interfaces, and more. API compatible with Yuma. Availability: September, 2012. Licensed. * '''Transpacket''' ** [http://www.transpacket.com/ http://www.transpacket.com] ** Offers Linux based embedded operating system distribution which uses Yuma for configuration and monitoring. ** Offers support, training, and consulting for YANG and netconf. === Mailing Lists === * '''NETCONF Working Group''' ** http://www.ietf.org/html.charters/netconf-charter.html ** Technical issues related to the NETCONF protocol are discussed on the NETCONF WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. * '''NETMOD Working Group''' ** [http://www.ietf.org/html.charters/netmod-charter.html http://www.ietf.org/html.charters/netmod-charter.html] ** Technical issues related to the YANG language and YANG data types are discussed on the NETMOD WG mailing list. Refer to the instructions on the WEB page for joining the mailing list. == Conventions Used in this Document == The following formatting conventions are used throughout this document:
'''Documentation Conventions'''
{| class="wikitable" border="1" !Convention !Description |- | '''--foo''' | CLI parameter foo |- | '''''' | XML parameter foo |- | '''foo''' | '''yangcli''' command or parameter |- | '''$FOO''' | Environment variable FOO |- | '''$$foo''' | '''yangcli''' global variable foo |- | some text | Example command or PDU |- | some text | Plain text |} = Summary = == What is Yuma? == Yuma is a set of programs providing a complete network management system and development environment, which implements the following standards: * Network Configuration Protocol (RFC 4741) * NETCONF over SSH (RFC 4742, RFC 6242) * NETCONF Notifications (RFC 5277) * Partial Lock RPC for NETCONF (RFC 5717) * YANG 1.0 Data modeling language (RFC 6020) * YANG 1.1 partial support* (7950) * Common YANG Data Types (RFC 6021) * NETCONF Monitoring Schema (RFC 6022) * With-defaults capability for NETCONF (RFC TBD) * SSH2 (RFC 4252 - 4254) * XML 1.0 * XPath 1.0 * NETCONF Base Notifications (RFC 6470) The following programs are included in the Yuma suite: * '''yangcli''': NETCONF over SSH client, providing a simple but powerful command line interface for management of any NETCONF content defined in YANG * '''netconfd''': NETCONF over SSH server, providing complete and automated support for the YANG content accessible with the NETCONF protocol * '''netconf-subsystem''': thin client used to allow OpenSSH to communicate with the netconfd program. This is documented as part of the '''netconfd''' program, since they must be used together. Optional programs in yuma123 not built and installed by default: * '''yangdump''': validates YANG modules and uses them to generate other formats, such as HTML, XSD, SQL, and C source code * '''yangdiff''': reports semantic differences between two revisions of a YANG module, and generates YANG revision statements Although any arbitrary YANG file can be automatically supported by Yuma, the following content (YANG modules) is built into the '''netconfd''' server, and supported by the '''yangcli''' client: * '''yuma-netconf.yang''': all the NETCONF protocol operations, including all YANG extensions to the NETCONF protocol (RFC 4741). This file contains meta-data used in the yangcli and netconfd programs, which is not available in the '''ietf-netconf.yang''' version. * '''ietf-yang-types.yang''': the standard derived types library in progress for YANG. This module is being developed by the NETMOD WG. (RFC 6021) * '''ietf-inet-types.yang''': the standard Internet address types library in progress for YANG. This module is being developed by the NETMOD WG. (RFC 6021) * '''ietf-netconf-monitoring.yang: '''the standard NETCONF monitoring module in progress by the NETCONF WG (RFC 6022) * '''ietf-netconf-partial-lock.yang''': the standard NETCONF module for multiple concurrent partial database locks (RFC 5717). * '''ietf-with-defaults.yang''': the standard NETCONF default value control module in progress by the NETCONF WG (draft-ietf-netconf-with-defaults-10.txt) * '''yuma-mysession.yang''': NETCONF session customization operations * '''notifications.yang''': the standard NETCONF create-subscription command to start receiving NETCONF notifications (RFC 5277) * '''nc-notifications.yang''': the standard NETCONF notifications (RFC 5277) * '''yuma-proc.yang''': /proc file system monitoring information * '''ietf-system.yang''': IETF system group and common notifications * '''ietf-netconf-acm.yang''': IETF Access Control Model * '''test/pass/*.yang''': Several modules are included for testing YANG and NETCONF behavior. * '''test/fail/*.yang''': Several modules with errors are included for testing YANG compiler behavior == Intended Audience == This document is intended for users of the programs in the Yuma suite. It contains the following information: * Introduction to YANG and NETCONF based Network Management * Yuma Configuration * Yuma User Guides * Yuma CLI Reference * Yuma Error Reference = Introduction = The Yuma Tools suite provides automated support for development and usage of network management information. All management data is defined with the YANG data modeling language. All management operations are encoded in XML 1.0 and performed with standard NETCONF protocol operations. == System Components == [[Image:software-components.png]] The following external program is used by Yuma, and needs to be pre-installed: * '''opensshd''' ** The SSH2 server code does not link with Yuma. Instead, the '''netconf-subsystem''' program is invoked, and local connections are made to the '''netconfd''' server from this SSH2 subsystem. The following external libraries are used by Yuma, and need to be pre-installed. They are usually installed by default and do not need to be installed by you: * '''libc6''' ** unix system library * '''ncurses''' ** Curses terminal support (needed on Fedora platforms only) * '''libxml2''' ** xmlTextReader XML parser ** pattern support * '''libreadline''' ** command line support for '''yangcli''' The following shared (or static) library is built by Yuma and used by almost all of its programs: * '''libyumancx''' ** YANG parser ** YANG validation ** basic NETCONF support ** XPath support ** configuration database support The following libraries are built by Yuma, and used within executables: * '''libyumaagt''' ** NETCONF server support * '''libmgr''' ** NETCONF client support * '''libyangrpc''' ** NETCONF minimalistic client support * '''libydump''' ** yangdump translation functionality The following binaries are built by Yuma: * '''netconfd''' ** NETCONF server * '''netconf-subsystem''' ** thin client between opensshd and NETCONF server * '''yangcli''' ** NETCONF client * '''yangdump''' ** YANG validation * '''yangdiff''' ** YANG compare The following sample netconfd module instrumentation library is provided as an example. These libraries (e.g., libfoo.so) can only be created with the Yuma SDK. Refer to the [[Yuma Developer Manual]] for details on creating server instrumentation libraries. * '''libtoaster''' * Server instrumentation code for the YANG module libtoaster.yang. * '''libietf-interfaces''' * Example server instrumentation code for the YANG module ietf-interfaces.yang. * '''libietf-system''' * Example server instrumentation code for the YANG module ietf-system.yang. === YANG === A YANG module define the semantics and syntax of a specific management feature. They are similar to SMIv2 (MIB) modules, but much more powerful and extensible. YANG provides the ability to define a detailed programmatic interface utilizing all protocol features: * reusable derived data types * reusable groupings of objects * RPC operations * database objects * notifications Network management software developers creating a new management feature start by defining the YANG module(s) for the NETCONF representation of the feature. This can include any mixture of new operations, data, and notifications. Existing YANG modules can be augmented as well. YANG provides complex nested data structures and choices, which allows data modelers to design management interfaces which closely resemble the native data structures within the server implementation. It is easy to get started with YANG, and there are many optional advanced features that can be utilized as well. YANG provides many machine-readable constructs which allow Yuma to automate many aspects of network management software development. [[Image:yang-modules.png]] Semantics and details that are usually only found in 'description' clauses can be understood and implemented automatically by the software tools. A YANG module can be a single file, or it can be split into an arbitrary number of files, using sub-modules. A YANG submodule is essentially the same as a main module, except that the namespace URI value is shared between the main module and all its submodules. A submodule is referenced with the include statement instead of the import statement. Submodules can also include other submodules, except a loop may not be formed by the include statements. Conceptually, the module is not nested. All definitions in submodules appear at the top level of the YANG module, even submodules included by other submodules. All YANG modules and submodules have revision dates. The example shows a simple version number, but the actual revision strings are date strings in the form 'YYYY-MM-DD'. Yuma programs support concurrent usage of different revisions of the same module or submodule. This can occur via groupings from external modules within the YANG language. Only one revision of a module can be imported into a single module or submodule, but any of these files may in turn import other modules. It is possible that a different version of the same module could be indirectly imported in this case. Deviation modules are normal YANG modules, except they only contain deviation statements. These deviation statements are used to alter (patch) the YANG modules with implementation-specific differences. A deviation module can contain any number of deviation statements, and they can apply to an arbitrary number of objects, from any module. Multiple deviation statements for the same target will be combined by the server before using them, and all deviate statements for the same object will be validated together, as if they were all contained in the same deviation statement. The order of the deviation statements is irrelevant. Deviations modules are processed first, and the deviation statements save for later. The import statements are ignored, unlike real module processing. Since deviation modules are not identified in any way, Yuma programs use the''' --module''' parameter to refer to a normal YANG module or submodule, and the '''--deviation''' parameter to refer to a deviation module. [[Image:yang-features.png]] The YANG feature statement is used to define a conceptual partition within the module. Objects that contain the if-feature statement for the corresponding feature are part of the feature. If the server does not advertise a feature in its , then it is not supported, and all the objects that are part of the feature are not supported. Multiple if-feature statements form a logical AND expression. All the referenced features must be enabled for the object to be available. In the example above, leaf 'YY2' is not present unless feature A and B are both advertised by the server. === NETCONF === The mandatory components of the NETCONF protocol are defined in RFC 6241 and RFC 6242. [[Image:netconf-conceptual-layers.png]] The NETCONF protocol is used to provide secure access all YANG content. The server maintains a database which is accessed as if it was an XML instance document. [[Image:netconf-concepts-top-down.png]] Data can be retrieved with XML (subtree) or XPath filters. Changes can be validated before being activated. Databases can be locked to prevent multiple managers from interfering with each other. Custom operations can be used to perform complex actions and perhaps return some data as well. [[Image:netconf-concepts-bottom-up.png]] NETCONF can utilize several secure transport protocols. The mandatory transport (SSH2) is used by Yuma. The '''OpenSSH''' server is used in the '''netconfd''' implementation, and '''libssh2''' library is used in the '''yangcli''' implementation, to provide all SSH2 layer support. By default, TCP port 830 (netconf-over-ssh) is used for all NETCONF communications between '''yangcli''' and '''netconfd'''. TCP port 22 (ssh) is also supported by default, and additional TCP ports can be configured. NETCONF security is session-based. Privileges are granted to a session based on the username provided in the SSH connection setup. Access control is configurable (via '''ietf-netconf-acm.yang''' RFC 6536), based on group membership. The access control rules permit or deny access to one or more groups, to a subset of the YANG content. Separate defaults for read, write, and exec (RPC operation) access are provided. === YANG-based Automation === Yuma is a 100% “native YANG†implementation. This means that YANG modules are used directly by all the tools to control all aspects of NETCONF protocol usage. There are no lossy translations, or complicated configuration steps, in order to use a YANG module. Simply load a module and start using it. The automation concepts will be familiar to SNMP developers who use SMIv2 to write MIB modules. The SMIv2 language contains enough machine-readable clauses so a client and server can automate certain aspects of the SNMP protocol implementation. YANG does the same thing for NETCONF developers, only 10 times better. [[Image:yang-as-source-code.png]] There are many more machine-readable constructs in YANG, and more powerful data modeling features. The complicated YANG features are optional, so traditional 'DESCRIPTION clause' based semantics are still supported. The more machine-readable YANG clauses that are used, the more the '''yangcli''' client and '''netconfd''' server can automate the entire NETCONF protocol implementation. [[Image:netconf-development-costs.png]] The YANG language includes many ways to specify conditions for database validity, which traditionally are only documented in DESCRIPTION clauses:
'''YANG Automation Constructs'''
{| class="wikitable" border="1" |
'''YANG statement'''
|
'''description'''
|- | '''config''' ''boolean''; | The '''config''' statement indicates if the object is writable, or read-only. The server uses this information when automatically skipping config=false entries for the operation. |- | '''default''' ''string''; | The '''default''' statement specifies the mandatory-to-use default value, if no leaf is provided. Unlike SMIv2 DEFVAL, it is not a suggestion, and the client can rely on it. Defaults can be specified in '''typedef''' or '''leaf''' statements. If both are defined, then the leaf default will be used. |- | '''deviation '''''deviation-target-path'' { … } | The '''deviation''' statement allows any YANG object be customized for a particular platform or implementation.. The tools can automatically support the altered objects, based on the sub-statements within the '''deviation''' statement. These changes can be of any nature, even those normally not allowed in YANG. The intent of the deviation statement os to accurately describe the object implementation, so the tools can automate the protocol operations correctly, even for non-standard implementations. |- | '''error-app-tag''' ''apptag-string''; | The''' error-app-tag''' statement can be used within the '''range''', '''length''', and '''pattern''' statements. If a value is invalid due to the corresponding error, then the field in the sent by the server will be set to the 'apptag-string' value. |- | '''error-message''' ''errmsg-string''; | The''' error-message''' statement can be used within the '''range''', '''length''', and '''pattern''' statements. If a value is invalid due to the corresponding error, then the field in the sent by the server will be set to the 'errmsg-string' value. |- | style="border-top:none;border-bottom:0.5pt solid #000000;border-left:0.5pt solid #000000;border-right:none;padding:0.0382in;"| '''extension''' | style="border-top:none;border-bottom:0.5pt solid #000000;border-left:0.5pt solid #000000;border-right:0.5pt solid #000000;padding:0.0382in;"| The '''extension''' statement allows a vendor to add language extensions, and all YANG implementations must be able to parse the extension correctly. However, only implementations which actually understand the extension will support it. All others will simply ignore the extension. |- | '''feature''' | The '''feature''' statement allows a module to be conceptually partitioned into mandatory and conditional object groups. All objects with the corresponding if-feature statement will be present only if the feature is supported by the server. |- | style="border-top:none;border-bottom:0.5pt solid #000000;border-left:0.5pt solid #000000;border-right:none;padding:0.0382in;"| '''if-feature''' ''feature-name;'' | style="border-top:none;border-bottom:0.5pt solid #000000;border-left:0.5pt solid #000000;border-right:0.5pt solid #000000;padding:0.0382in;"| Construct containing the''' if-feature''' statement is only included if the specified feature is supported by the server. Otherwise, the object does not exist on the server. |- | '''import''' (by revision) | The import statement allows definitions from other modules to be used. A specific revision date can be used within the entire module. However, it is possible that different versions of imported typedefs and groupings can be used, if one imported module also imports some modules. |- | '''include''' (by revision) | The '''include''' statement provides the exact same features as the '''import '''statement, except it applied to sub-modules included within a module (or other sub-modules), instead of other modules. It allows multiple sub-modules to be combined to create one conceptual YANG module. |- | '''key''' ''key-leaf-list''; | The '''key''' statement indicates a set of one or more top-level leafs within the list that are used to name a specific instance of the particular list object. All protocol operations, such as , can be fully automated, based on the information in this statement. |- | '''length''' ''length-spec-string''; | The '''length''' statement is exactly like the '''range''' statement, except it limits the length of string '''leaf''' and '''leaf-list''' objects. |- | '''mandatory''' ''boolean''; | The '''mandatory''' statement indicates that the choice, list or leaf must be provided by the client. It will not be created by the server. Most parameters are not mandatory however, so the default is 'false' if this statement is missing. |- | '''max-elements''' ''number'' | 'unbounded' ; | Specifies the maximum number of instances that a '''list''' or''' leaf-list '''object can have in a valid database. The default is 'unbounded', if this statement is not present. |- | '''min-elements''' ''number''; | Specifies the minimum number of instances that a '''list''' or '''leaf-list''' object must have in a valid database. The default is zero, if this statement is not present. |- | '''must''' ''xpath-expr;'' | If the object containing the '''must''' statement exists, then the XPath expression must evaluate to 'true' for the database to be valid. This provides referential integrity checks among related parameters. |- | '''pattern''' ''pattern-string'' ; | The '''pattern''' statement specifies a regular expression that must evaluate to 'true' in order for the corresponding string '''leaf''' or '''leaf-list''' object to be valid. Multiple patterns encountered in a nested typedef chain must all evaluate to 'true' for the object to be valid. |- | '''range''' ''range-spec-string'' ; | The '''type''' statement can specify the range of a numeric type. Since typedefs can be nested in YANG, the range statements are nested also, and constitute an AND expression (i.e., all the range tests must pass in the chain of type definitions.) THe keywords 'min' and 'max' indicate the minimum and maximum values from the parent typedef (if any), not the built-in type. |- | '''refine '''''refine-target-path'' { … } | The '''refine''' statement is defined within a '''uses''' statement, and allows the specific grouping to be customized for each individual copy of the grouping contents. The tools can automatically support the refined objects, based on the sub-statements within the '''refine''' statement. |- | '''revision''' ''revision-date'' { … } | The '''revision''' statement identifies the most current version of a YANG module or sub-module. Multiple versions at once are supported in YANG. |- | '''unique''' ''unique-node-lis''t; | The unique statement indicates an arbitrary tuple of descendant nodes within a list, which have to be unique within the list. These nodes are not keys, and can be nested anywhere within a single list entry. |- | '''uses''' ''grouping-name''; | The '''uses''' statement inserts an instance of a reusable '''grouping''', replacing the uses node within the conceptual data tree. |- | '''when''' ''xpath-expr;'' | The object containing the '''when''' statement is only allowed to exist if the XPath expression evaluates to 'true'. This provides a SPARSE AUGMENTS capability when combined with the augment statement. |} === YANG Language Extensions === There are several YANG extensions that are supported by Yuma. They are all defined in the YANG file named '''yuma-ncx.yang'''. They are used to 'tag' YANG definitions for some sort of automatic processing by Yuma programs. Extensions are position-sensitive, and if not used in the proper context, they will be ignored. A YANG extension statement must be defined (somewhere) for every extension used in a YANG file, or an error will be occur. Most of these extensions apply to '''netconfd''' server behavior, but not all of them. For example, the '''ncx:hidden''' extension will prevent '''yangcli''' from displaying help for an object containing this extension. Also, '''yangdump''' will skip this object in HTML output mode. The following table describes the supported YANG language extensions. All other YANG extension statements will be ignored by Yuma, if encountered in a YANG file:
'''YANG Language Extensions'''
{| class="wikitable" border="1" |
'''extension'''
|
'''description'''
|- |'''ncx:hidden'''; | Declares that the object definition should be hidden from all automatic documentation generation. Help will not be available for the object in '''yangcli'''. |- | '''ncx:metadata ''' “''attr-type attr-name''â€; |Defines a qualified XML attribute in the module namespace. Allowed within an RPC input parameter. '''attr-type''' is a valid type name with optional YANG prefix. '''attr-name''' is the name of the XML attribute. |- | '''ncx:no-duplicates'''; | Declares that the '''ncx:xsdlist''' data type is not allowed to contain duplicate values. The default is to allow duplicate token strings within an '''ncx:xsdlist''' value. |- |'''ncx:password'''; | Declares that a string data type is really a password, and will not be displayed or matched by any filter. |- |'''ncx:qname;''' |Declares that a string data type is really an XML qualified name. XML prefixes will be properly generated by '''yangcli''' and '''netconfd'''. |- |'''ncx:root'''; |Declares that the container parameter is really a NETCONF database root, like in the operations. The child nodes of this container are not specified in the YANG file. Instead, they are allowed to contain any top-level object from any YANG file supported by the server. |- |'''ncx:schema-instance'''; |Declares that a string data type is really an special schema instance identifier string. It is the same as an instance-identifier built-in type except the key leaf predicates are optional. For example, missing key values indicate wild cards that will match all values in '''nacm''' expressions. |- |'''nacm:secure;''' |Declares that the database object is a secure object. If the object is an '''rpc''' statement, then only the '''netconfd''' 'superuser' will be allowed to invoke this operation by default. Otherwise, only read access will be allowed to this object by default, Write access will only be allowed by the 'superuser', by default. |- |'''ncx:user-write ;''' |Declares the user write permissions that will be allowed for a config=true data node. The bits parameter contains the write permissions that will be allowed for the object (create, update, delete). |- |'''nacm:very-secure;''' |Declares that the database object is a very secure object. Only the 'superuser' will be allowed to access the object, by default. |- |'''ncx:xsdlist''' “''list-type''â€; |Declares that a string data type is really an XSD style list. '''list-type''' is a valid type name with optional YANG prefix. List processing within will be automatically handled by '''netconfd'''. |- |'''ncx:xpath'''; |Declares that a string data type is really an XPath expression. XML prefixes and all XPath processing will be done automatically by '''yangcli''' and '''netconfd'''. |} === YANG Compiler === The Yuma programs all use the same centralized YANG language parser. The complete YANG language is supported, as defined in RFC 6020. The file naming conventions defined in this specification must be used, along with all the language definition rules. Definitions can be contained in modules and/or sub-modules. Any number of revisions of a module or submodule can be used concurrently, The '''import-by-revision''' and '''include-by-revision''' features of YANG are fully supported, Refer to the section 'Searching for Files' for more details. All extension usage within YANG files is supported and saved. The application data is available to all Yuma programs, including netconfd server instrumentation. Refer to the [[Yuma Developer Manual]] for details on writing YANG files and using the extensions built into Yuma. Note: The '''smidump''' is not part of Yuma, but it can be utilized to convert MIB modules written in SMIv2 into YANG modules, which can then be implemented in '''netconfd''', and managed with '''yangcli'''. The freely available '''libsmi''' library contains the '''smidump''' program. === YANG Module Library === The central system component is the set of YANG data model modules which define all available management information. This set of modules is expected to grow over time, and there is usually a high degree of reuse and inter-dependence between the modules. YANG modules can import other modules to reuse any definitions, and to augment objects in other modules. Each module represents one unique XML namespace used within the NETCONF protocol. A module can be partitioned into any number of submodules, each in a separate YANG file. The submodules are conceptually combined, and only the entire module is accessible to other modules. '''Directory Layout''' Yuma can utilize several directories to store files used during operation. By default, a 'root' directory and all of its sub-directories are searched for these files. Several different roots can be searched. Generally, there is one centralized root (YUMA_INSTALL) shared by all users, and one or more 'project' roots (YUMA_HOME), which can be shared but may belong to a single user. The Yuma programs need to find and store the following types of files during operations: * YANG modules and submodules (*.yang): * XML and text data files (usually *.txt or *.xml) * command scripts for '''yangcli''' * command-line-history file for''' yangcli''' The search paths used to find these files are discussed in detail in the System Configuration section. [[Image:default-directory-layout.png]] '''Module Revisions''' YANG has extensive module lifecycle support. Each module or submodule has a revision date, and multiple revisions of the same module or submodule may be used at once within the same server. The YANG module repository is the authoritative source of common management information for the '''netconfd''' server. However, different platform implementations of the same data model need to be 'adjusted' slightly to reflect differences in the feature support available on each platform. Yuma has an extensive set of mechanisms to automate the maintenance of these platform-specific 'special requirements'. A single YANG module (plus 'patches' and deviations as needed for each platform) can be published, instead of a separate version of the YANG module for each platform. [[Image:cooked-modules.png]] '''Module Naming Conventions''' YANG module names are usually lower-case. Hyphen (-), underscore (_) and period (.) characters are allowed, after the first character, which must be a letter. It is suggested that only the at sign (@) character be used as a separator between module name string components. YANG files must use the suffix '.yang'. YIN files must use the suffix 'yin'. There are two forms of YANG file names: with and without a revision date. '''module.yang''' ietf-netconf-monitoring.yang (no revision or unspecified revision) '''module@revision-date.yang''' ietf-netconf-monitoring@2009-04-17.yang (must be the 2009-04-17 version) These naming conventions are important when Yuma needs to resolve an 'import' or 'include' statement in a YANG file. Refer to section [[#Searching for Files]] for more details on YANG module search paths and the 'import-by-revision' feature of YANG. === YANG Files === YANG modules and submodules are text files encoded in UTF-8. . There is also an alternate XML encoding called YIN. Sometimes the term YANG module is used to refer to the conceptual module, whether it is encoded in YANG format or YIN format. All Yuma Tools programs will accept either encoding format, however line and column numbers are not correct in log messages for YIN encoded modules. Instead, each XML node is given a monotonically increasing value, and the XML document order is used instead of line numbers in error/warning messages for YIN files. The column number is always '1' for YIN files. A module can be validated and checked for possible programming mistakes, by using the '''yangdump''' program. Many 'reports' can also be generated: * exported symbols (--exports) * imported modules (--dependencies) * object identifiers (--identifiers) The '''yangdump''' program is also used to generate other files, derived from the YANG content: * '''XML Schema Document (XSD)''': extends the NETCONF XSD with the YANG content layer definitions (--format=xsd) * '''HTML'''
or full file output: hyper-linked, color-coded formatting of YANG modules to support netconf-central or other WEB-based documentation system. There are several options for configuring the output, and all formatting is done with Cascading style-sheets (CSS) (--format=html) * '''netconf-central''' documentation SQL database input file: supports the automated online documentation of YANG content (--format=sqldb). Refer to the netconfcentral.sql file for details on this output, in the [[Yuma Developer Manual]]. * '''server instrumentation code-stubs''': the instrumentation callback functions, used in '''netconfd''' for activating specific YANG content, can be generated. This procedure is described in more detail in the [[Yuma Developer Manual]]. * '''canonical YANG''': a YANG file can be reformatted so all statements are indented uniformly, and always appear in the same order. Objects marked as hidden (see the 'hidden' extension in yuma-ncx.yang) will not be generated. (--format=yang) * '''copy-YANG-and-set-name: '''A YANG module can be validated and then copied (if no errors) to another location, adding the revision-date to the file name. (--format=copy) === NETCONF Managers === The NETCONF client is an application that initiates and utilizes NETCONF sessions to control and monitor a NETCONF server. Yuma includes the '''yangcli''' application for this purpose. It can be used as a stand-alone tool with any NETCONF server. === NETCONF Agents === The NETCONF server is a server application that is always running on the managed device. It listens for NETCONF session requests from a NETCONF client, and allows specific users to access specific subsets of the available content (operations, database access, and notifications). It processes all incoming protocol operation requests from the client, and insulates all the instrumentation code from these protocol operations. Yuma includes the '''netconfd''' application for this purpose. It can be run on several different platforms, or easily adapted to embedded platforms. = System Configuration = The Yuma programs use YANG to define its configuration parameters. The 'ncx:cli' extension is used within a container with the same name as the program to define all CLI parameters. Some parameters are shared (see yuma-app-common.yang), so they are not located directly in the container. container yangcli { ncx:cli; // yangcli CLI parameters defined as choices and leafs here } The following YANG modules are provided, which contain all the configuration parameters for Yuma: * '''yuma-types.yang''': contains common data types used in the Yuma applications * '''yuma-app-common.yang''': contains common CLI parameters used in all Yuma applications * '''yuma-ncx.yang:''' contains YANG extensions used in any YANG module, including Yuma application modules * '''yangdump.yang:''' configuration parameters for the '''yangdump''' application * '''yangdiff.yang''': configuration parameters for the '''yangdiff''' application * '''yangcli.yang''': configuration parameters and local commands for the''' yangcli''' application * '''netconfd.yang''': configuration parameters for the '''netconfd''' server Note: * The '''netconf-subsystem''' program does not have any configuration parameters at this time, so there is no YANG file defined for it. * The '''openssh''' SSH server is configured separately, using the '''sshd_config''' file. * The '''libtecla''' library, used by the yangcli program for command line editing support, has its own configuration file '''~/.tecla''', to override the default (emacs) editing key assignments. Yuma applications can accept configuration parameters from 3 sources, checked in the following order: # environment variables # command line parameters # configuration file == Environment Variables == The Yuma programs utilize system environment variables to customize and simplify configuration and operation of the programs. These environment variables typically specify file search paths or default directory locations. The following environment variables are used within Yuma: * HOME * YUMA_HOME * YUMA_INSTALL * YUMA_MODPATH * YUMA_DATAPATH * YUMA_RUNPATH === $HOME === The '''$HOME''' environment variable contains the directory specification of the user's home directory, and is expected to be set by the system shell before use. The Yuma programs expect (by default) that sub-directories and files contained in this directory will be readable and writable. Default value: none CLI override: none C shell example: setenv HOME /home/andy Bash shell example: export HOME=/home/andy === $YUMA_HOME === The '''$YUMA_HOME''' environment variable contains the directory specification of the current Yuma project root directory. This is the path to the 'netconf' directory, within a Yuma source tree. Default value: none CLI override: --yuma-home CLI example: --yuma-home=/home/andy/swdev/yuma/trunk/netconf C shell example: setenv YUMA_HOME /home/andy/swdev/yuma/trunk/netconf Bash shell example: export YUMA_HOME=/home/andy/swdev/yuma/trunk/netconf === $YUMA_INSTALL === The '''$YUMA_INSTALL''' environment variable contains the directory specification of the Yuma installation root directory. Default value: /usr/share/yuma CLI override: none C shell example: setenv YUMA_INSTALL /sw/yuma Bash shell example: export YUMA_INSTALL=/sw/yuma === $YUMA_MODPATH === The '''$YUMA_MODPATH''' environment variable contains a list of directory specifications that should be searched (in order) to find YANG or YIN modules and submodules. It can be used to extend the search path beyond the default locations. The syntax for this parameter is a string containing the desired directory paths, separated by colon (:) characters. If the trailing forward slash (/) character is missing, then it will be added when searching for files. By default, each entire directory and all its sub-directory contents will be searched for the requested file. This can be overridden with the '''--subdirs''' parameter. Refer to the Command Line Parameter Reference for more details. If '''--subdirs=false''' is used, then only the specified directory will be searched instead. Note: This parameter specifies the exact directory locations when searching for files. This is different than the '''$HOME''', '''$YUMA_HOME''', and '''$YUMA_INSTALL''' environment variables, which specify a Yuma root directory. Default value: none CLI override: --modpath CLI example: --modpath=â€$HOME/modules2:/usr/local/modules†C shell example: setenv YUMA_MODPATH “$HOME/modules2:/usr/local/modules†Bash shell example: export YUMA_MODPATH=“$HOME/modules2:/usr/local/modules†=== $YUMA_DATAPATH === The '''$YUMA_DATAPATH''' environment variable contains a list of directory specifications that should be searched (in order) to find data files used by Yuma applications. It can be used to extend the search path beyond the default locations. Data files used by the '''yangcli''' program are affected by this environment variable. The location where the '''netconfd''' program''' '''keeps the file '''startup-cfg.xml''' is also affected by this environment variable. This file contains the contents of the non-volatile database, which is loaded into the database when the server boots. The syntax for this parameter is a string containing the desired directory paths, separated by colon (:) characters. If the trailing forward slash (/) character is missing, then it will be added when searching for files. By default, each entire directory and all its sub-directory contents will be searched for the requested file. This can be overridden with the''' --subdirs''' parameter. Refer to the Command Line Parameter Reference for more details. If '''--subdirs=false''' is used, then only the specified directory will be searched instead. Note: This parameter specifies the exact directory locations when searching for files. This is different than the '''$HOME''', '''$YUMA_HOME''', and '''$YUMA_INSTALL''' environment variables, which specify a Yuma root directory. Default value: none CLI override: --datapath CLI example: --datapath=â€$HOME/mydata:$HOME/project1/data†C shell example: setenv YUMA_DATAPATH “$HOME/mydata:$HOME/project1/data†Bash shell example: export YUMA_DATAPATH=“$HOME/mydata:$HOME/project1/data†=== $YUMA_RUNPATH === The '''$YUMA_RUNPATH''' environment variable contains a list of directory specifications that should be searched (in order) to find script files used by Yuma applications. It can be used to extend the search path beyond the default locations. Script files used by the '''yangcli''' program are affected by this environment variable. The syntax for this parameter is a string containing the desired directory paths, separated by colon (:) characters. If the trailing forward slash (/) character is missing, then it will be added when searching for files. By default, each entire directory and all its sub-directory contents will be searched for the requested file. This can be overridden with the '''--subdirs''' parameter. Refer to the Command Line Parameter Reference for more details. If '''--subdirs=false''' is used, then only the specified directory will be searched instead. Note: This parameter specifies the exact directory locations when searching for files. This is different than the '''$HOME''', '''$YUMA_HOME''', and '''$YUMA_INSTALL''' environment variables, which specify a Yuma root directory. Default value: none CLI override: --runpath CLI example: --runpath=â€$HOME/scripts:/usr/local/scripts†C shell example: setenv YUMA_RUNPATH “$HOME/scripts:/usr/local/scripts†Bash shell example: export YUMA_RUNPATH=“$HOME/scripts:/usr/local/scripts†== Searching for Files == All Yuma programs search for YANG and other files in the same manner, using the same configuration parameters. The current working directory is included in this search path, so it is important to consider the directory in which a Yuma program is invoked. The search ends as soon as a suitable matching file is found. There are two types of module searches: # searches on behalf of configuration parameters # searches on behalf of YANG import or include statements The first term in a path specification may contain special character sequences: * If the first character is the forward slash ('/'), then the entire path specification is used as an absolute path specification. /usr/share/yang/modules * If the first character is not the forward slash ('/'), and no special characters are found instead, then the entire path specification is used as an relative path specification, starting from the current working directory. ../more-modules/test7.yang ./this-dir/my-module.yang testmodule.yang old-modules/version7/ * If the first character is the tilde ('~') character, followed by the forward slash ('/') character, then the file search will start in the current user's $HOME directory. ~/modules/test/test.yang * If the first character is the tilde ('~') character, followed by a user name, and then the forward slash ('/') character, then the file search will start in the specified user's $HOME directory . If the user is unknown, then the path specification is invalid. ~andy/modules/test/test.yang ~fred/scripts * If the first character is the dollar sign ('$') character, followed by an environment variable name, and then the forward slash ('/') character, then the file search will start in the directory indicated by the contents of the environment variable. If the variable is unknown, or its contents do not represent a valid directory location, then the path specification is invalid. $WORKDIR/tests/test-all-interfaces $YUMA_HOME/data/startup-cfg.xml Note: Whenever Yuma searches a directory, it checks for the expected file type, but ignores the following: * all files and sub-directories that begin with the period (.) character * any directory named 'CVS' * symbolic links for regular files The following environment variables affect file searches: * $HOME * $YUMA_HOME * $YUMA_MODPATH * $YUMA_DATAPATH * $YUMA_RUNPATH The following configuration parameters affect file searches: * --yuma-home * --modpath * --datapath * --runpath * --subdirs === Yuma Work Directory === There is a directory ('''$HOME/.yuma''') created by '''yangcli''' or '''netconfd''' for data files and temporary files. It is called '''.yuma''', and it is created in the users home directory, if the '''$HOME''' environment variable is defined. This directory will be used as the default location to save the '''startup-cfg.xml''' file by '''netconfd''', if no startup file is specified in the CLI parameters, and no existing startup file is found in the data file search path. This directory is also used as the default location to store the '''.yangcli_history ''' file for '''yangcli''' command line history recall. The '''$HOME/.yuma/tmp''' directory is used by '''yangcli''' to create session-specific sub-directories where all the YANG modules from the server for the current session are stored. If the '''--autoload=false''' parameter is used, then these temporary directories will not be created by '''yangcli'''. === Parameter Searches === A parameter search is started on behalf of a CLI parameter, such as the '''--module''' parameter, used by the '''yangdump '''program. A search of this type can include directory path and file extension in the search parameter. If a filename with a file extension (must be '.yang') is given, then only that exact file will be checked. The current working directory will be used in this case, if no directory path (or a relative directory path) is provided. --module=test.yang --module=../more-modules/test3@2009-04-01.yang If the exact filename is not found, then the search failed. If a parameter based search does not have any directory path or file extension fields present, then a parameter search is the same as an import/include search. === Import/Include Searches === An import or include search is started on behalf of a YANG 'import' or 'include' statement. A search of this type includes only the module or submodule name, with no directory or file extension present. An optional 'revision-date' statement can be used in YANG, which means only a version of the YANG file with that exact current revision date will be used. There are separate search algorithms, depending on whether the revision-date is used in the YANG import or include statement, and whether the imported or included module has a current revision statement. '''Mode 1: import-by-revision''' In this example, an import statement is causing a search for a module named 'foo' with a revision date of '2009-01-15'. If a revision-date is used in the import or include statement, then the module search path will be checked as follows: First, find a file with the same revision-date in the file name: import foo { revision-date “2009-01-15â€; prefix foo; } If the file 'foo.2009-01-15.yang' is found, and the current revision statement in the module is equal to '2009-01-15', then the search is successfully terminated. // file foo.2009-01-15.yang module foo { namespace “[http://example.com/ns/foo http://example.com/ns/foo]â€; prefix foo; // rest of header follows revision 2009-01-15 { description “Initial version.â€; } // rest of module follows } If the file is not found, or the most current revision date is not correct, then the module search is repeated for 'foo.yang'. If the file 'foo.yang' is found, and the current revision statement in the module is equal to '2009-01-15', then the search is successfully terminated. // file foo.yang module foo { namespace “[http://example.com/ns/foo http://example.com/ns/foo]â€; prefix foo; // rest of header follows revision 2009-01-15 { description “Initial version.â€; } // rest of module follows } If the file is not found, or the most current revision date is not correct, then the module search failed. '''Mode 2: import any revision''' If the import statement does not have revision-date sub-statement, then the module search path is checked for a file with no revision-date in the file name: import foo { prefix foo; } If the file 'foo.yang' is found, then it is used, regardless of the most current revision date (if any) found in the module. If it is not found then the module search failed. Note: The first instance of 'foo.yang' in the module search path will be used, even if a more current version is available, later in the search path. === File Search Paths === Yuma uses configurable search paths to find the various files that are needed during operation. '''Module Search Path''' * If the module parameter is specified with a path or file suffix, then that filespec is tried, relative to the current working directory. If it is not found, or not the correct revision date, then the search terminates in failure. --module=../test.yang * If the module is specified without any path or file extension fields, then the module search path is checked, in order. The first step which produces a match terminates the search successfully. If all steps are exhausted and no match is found then the search terminates in failure. --module=foo # The current working directory is checked. No sub-directories are checked, if any are present. # Each directory specified in the '''$YUMA_MODPATH''' environment variable, or set with the '''–modpath''' configuration parameter, is checked. #* If the '''--subdirs=false''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. # The '''$HOME/modules''' directory is checked. #* If the '''--subdirs=false''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. # The '''$YUMA_HOME/modules''' directory is checked. #*If the '''--subdirs=false''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. # The '''$YUMA_INSTALL/modules''' directory is checked. #* If the '''--subdirs=fasle''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. '''Data Search Path''' Yuma programs can store data used during operation. An example of a data file is the startup configuration file used by '''netconfd''', usually called '''startup-cfg.xml.''' # If the file name has an absolute path specification, then that exact file location is tried. If no match is found, then the search will terminate in failure. # Each directory specified in the '''$YUMA_DATAPATH''' environment variable, or set with the '''–datapath''' configuration parameter, is checked. #* If the '''--subdirs=false''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. # The current working directory is checked. No sub-directories are checked, if any are present # The '''$HOME/data''' directory is checked. #* If the '''--subdirs=false''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. # The '''$YUMA_HOME/data''' directory is checked. #* If the '''--subdirs=false''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. # The '''$HOME/.yuma''' directory is checked. # The '''$YUMA_INSTALL/data''' directory is checked. #* If the '''--subdirs=false''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. # The '''/usr/share/yuma/data''' directory is checked. #* If the '''--subdirs=false''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. # The '''/etc/yuma''' directory is checked. '''Script Search Path''' The '''yangcli''' program can store script files used during operation. # If the file name has an absolute path specification, then that exact file location is tried. If no match is found, then the search will terminate in failure. # The current working directory is checked. No sub-directories are checked, if any are present. # Each directory specified in the '''$YUMA_RUNPATH''' environment variable, or set with the '''–runpath''' configuration parameter, is checked. #* If the '''--subdirs=false''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. # The '''$HOME/scripts''' directory is checked. #* If the '''--subdirs=false''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. # The '''$YUMA_HOME/scripts''' directory is checked. #* If the '''--subdirs=false''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. # The '''$YUMA_INSTALL/scripts''' directory is checked. #* If the '''--subdirs=false''' parameter is set, then only each top-level directory will be checked. If it is not set, then sub-directories will be searched. == Configuration Files == The Yuma program configuration parameters can be stored in text or XML files. The '''-–config '''parameter is used to specify that configuration parameters should be retrieved from a file instead of the command line. [[Image:default-linux-data-directories.png]] Any other configuration parameter (except '''-–config''') can be stored in a configuration file used for program input. === XML Configuration Files === The XML format for these files follows the structure of the NETCONF element. Each parameter is stored within a container identifying the application which it is being configured. The '''netconfd''' stores its non-volatile database in this format. XML configuration file contents can appear in any order. The following configuration parameters affect the generation and display of XML configuration files by '''netconfd''': * --indent * --with-defaults The following configuration parameter affects the location of XML configuration files by '''netconfd''': * --datapath * $YUMA_DATAPATH environment variable Note : The IETF may standardize this container format soon. Do not rely on the top-level namespace URI. Any top-level element name , in any namespace (even none), should be expected to contain a complete NETCONF database, or a subset of a NETCONF database. The following example show some database objects from the NETCONF Access Control Model (yuma-nacm.yang), in XML configuration file format. file startup-cfg.xml: my-admin-group andy barney fred my-system-rule my-admin-group my-rule ietf-system /system read create update delete permit Rule of the thumb! === Text Configuration Files === The Yuma text configuration file format is based on some common Unix .conf file formats: * A hash mark until EOLN is treated as a comment # this is a comment log-level info # this is also a comment * All text is case-sensitive * Whitespace before or within a line is not significant * The 'end a line' (EOLN) character ('\n') is used to end a command, so whitespace at the end of a line is significant. * To enter a command on multiple lines, use an escaped EOLN (backslash-EOLN) for all but the last line this is a command line this is the start \ of a long \ three line command this is a new command * A YANG container parameter is represented by the container name, followed by a left curly brace ('{'), zero or more child nodes, and then a right curly brace ('}'). yangdump { # set some display control parameters log-level debug2 warn-linelen 72 indent 4 } * A YANG list parameter is represented by the list name, followed by a whitespace separated sequence of key leaf values, followed by a left curly brace ('{'), zero or more child nodes, and then a right curly brace ('}'). ifStackEntry 11 42 { # the key leafs will also printed here ifStackHigherLayer 11 ifStackLowerLayer 42 ifStackStatus active } * Configuration files which are used with command line parameters may include program parameters for multiple applications. ** Only the top-level container that matches the name of the program will be used. ** Any other top-level containers will be ignored ** Only the first instance of the desired program container will be used. Any additional containers will be ignored. // test.conf yangdump { # common yangdump parameters here } yangdiff { # common yangdiff parameters here } * Configuration file parameters can appear in any order. Only list index strings need to appear in their defined order. * The following configuration parameters affect generation and display of text configuration files * ** --indent ** --with-defaults ** --display-mode == Bootstrap CLI == Since Yuma programs use YANG to define CLI parameters, there needs to be an initial bootstrap CLI phase, in order to process parameters which affect the way YANG files are processed. The bootstrap CLI is not as flexible as the main CLI processor, and the syntax is more strict. Parameters must be entered in either of the following forms: * --name * --name=value If parameters are not entered in this format, then they will be skipped until the main CLI parameter processing is done. This may cause undesirable changes in key parameters, such as the module search path. The following configuration parameters are also bootstrap parameters, and will take effect immediately, if entered from the command line: * '''--log''': log messages to the specified file instead of STDOUT * '''--log-level''': set the logging verbosity level * '''--log-append''': use the existing log file (if any) instead of overwriting it * '''--modpath''': use the specified module search path. This will override the '''$YUMA_MODPATH''' environment variable, if it is set * '''--yuma-home:''' use the specified project root. This will override the '''$YUMA_HOME''' environment variable, if it is set Refer to the Yuma CLI Reference for more details. on these configuration parameters. == Configuration Parameters == Command line parameters are used to provide input to Yuma programs when they are invoked. They are also used extensively by the '''yangcli''' program, to represent RPC method input parameters and database nodes which are part of NETCONF operation content, such as the '''''' parameter within the '''''' operation. === Parameter Syntax === A CLI parameter has 2 forms: * Parameter contains a YANG type of 'empty' or a zero-length 'string':
* Everything else:
There are up to 4 components in a CLI parameter: # '''prefix''': consists of 0, 1, or 2 consecutive dash characters. # '''parameter name''': name of the parameter. A partial name may be used if it is unique. # '''separator''': either consists of the 'equals sign' character ('='), which may be preceded or followed by whitespace, or just whitespace with no equals sign character. # '''value''': a quoted or unquoted string, an empty string is only allowed if quotes are entered. The following example shows some ways the leaf 'foo' could be entered as a CLI parameter: leaf foo { type uint32; } foo=7 -foo=7 --foo=7 --foo =7 foo 7 -foo 7 -foo = 7 --foo 7 --foo "7" foo 7 === ncx:cli Extension === The '''ncx:cli''' extension is used in in YANG container definitions, which represent the program CLI parameters, not NETCONF database parameters. It does not take any parameters, and is defined in '''yuma-ncx.yang'''. container yangcli { ncx:cli; // all the yangcli CLI parameters } If this extension is present, then '''netconfd''' will ignore the container when loading the database object definitions. Only the program with the same name as the container will use the CLI parameter definition. === ncx:default-parm Extension === The '''ncx:default-parm''' extension is used within a container with an '''ncx:cli''' extension, or within an 'input' section of an RPC operation definition. It is defined in '''yuma-ncx.yang'''. If no parameter name is found when processing CLI parameter input, and the '''ncx:default-parm''' extension is present in the container or RPC input being processed, then the specified parameter name will be used instead of generating an error. The value must be valid for the parameter syntax, according to its YANG definition. This means that for the default parameter, only the component of the complete parameter syntax may be used, as well as the normal forms. container yangdump { ncx:cli; ncx:default-parm module; // all the yangdump CLI parameters } When invoking the '''yangdump''' program, the default CLI parameter is '''--module'''. These two command lines are equivalent: yangdump --module=test1 --module=test2 yangdump test1 test2 A string that does not start with any dashes will still be tried as a parameter name, before trying the default parameter. If the value used for a default parameter conflicts with another parameter name, then the normal form must be used, instead of this form. yangdump log-app test1 Even if there was a module named 'log-app', it would not be tried as a '''--module''' parameter, since it also matches the '''--log-append''' parameter. Note: the default parameter form is can be used in conjunction with shell wildcard characters, depending on the shell. yangdump *.yang yangdump --subtree=. These commands are equivalent in the '''yangdump''' program. = XPath Reference = The XPath 1.0 path specification language is supported in all Yuma Tools programs, as specified in the YANG language specification. There are also some additional variables and XPath functions, available in all XPath expressions. A custom XPath implementation is used, which is based on the internal data structures maintained within the program (i.e., object tree or data tree). No CPU or memory is wasted converting these data structures to actual XML documents for XPath processing. == XPath 1.0 == All functionality defined in the XPath 1.0 specification is supported. There are some restrictions, which are specific to the YANG standard: * The 'attribute' and 'processing-instruction' axes are always empty. * YANG identityref leaf values need to be entered within quotes or they will be interpreted as XML qualified node names. * The server may not maintain consistent XML document order for system-ordered data. This affects expressions which rely on XML document order to be precise and completely static. A NETCONF server is only required to maintain XML document order for user-ordered lists and leaf-lists, and only relative to a particular object, not the entire document. === XML Namespaces === The XPath implementation allows a more liberal syntax than the XPath 1.0 specification allows. Specifically, if a node identifier is unqualified (i.e., there is no namespace specified with a default namespace or an explicit namespace declaration), then all known XML namespaces known by the program will be checked for a top-level element with the same name. * '''If XML namespaces are used, they must be used correctly.''' '''Example request using XML namespaces in an XPath expression:''' Note the text: '''xmlns:toast="[http://netconfcentral.org/ns/toaster http://netconfcentral.org/ns/toaster]"''' This 'xmlns' attribute does not have to appear exactly as specified, or within the element. It can appear in any legal manner. Refer to the '''XML Namespaces 1.0 '''specification for more details. '''Example request not using XML namespaces in an XPath expression:''' If the 'toaster.yang' module is loaded within the program, and if the 'toaster' node is enabled (e.g., not removed via a YANG deviation), then the XML prefix ('toast:' in this example) can be omitted. == YANG Specific XPath Behavior == The YANG language requires some minor changes and additions to the XPath 1.0 specification: * The 'current' function from XPath 2.0 is supported. * The NULL XML namespace is mapped to the current YANG module XML namespace, when processing an XPath expression within a YANG module (e.g., must statement). * A NETCONF database is treated as a conceptual XML instance document with zero or more top-level elements. This is consistent with XSLT behavior. XML 1.0 requires a single top-level element,so external XML documents representing a NETCONF database always start with the element (config element in the NETCONF XML namespace). == Custom XPath Variables == The XPath specification supports system variables to be accessed within XPath expressions. Within the '''yangcli''' program, all user and system variables available to a script are also available as XPath variables within XPath expression evaluation (e.g., if, eval, and while commands). For example, a variable named 'myvar' would be accessed within an XPath expression as '$myvar'. === user === An XPath variable called 'user' is supported in the '''yangcli''' and '''netconfd''' programs. It is equal to the NETCONF user name associated with the session evaluating the XPath expression. It is provided to be used in data rules within the NETCONF Access Control Model (NACM). == Custom XPath Functions == The following XPath functions are added to the XPath 1.0 Function Library, in addition to the 'current' function from XPath 2.0. === module-loaded === The '''module-loaded''' function tests whether the specified module is loaded within the program. '''boolean module-loaded (module-name [, revision-date])''' Parameters: * Parameter 1: ** Type: String ** Usage: Mandatory ** Purpose: Specifies the module name to check. * Parameter 2: ** Type: String ** Usage: Optional ** Purpose: Specifies the YANG revision date string for module indicated by parameter 1. Returns: Boolean * true: the specified module is loaded * false: the specified module is not loaded, possibly not known Errors: * Missing parameter error if no parameters provided. * Extra parameters error if more than 2 parameters provided. * All unknown parameter values cause a 'false' result. Example: yangcli> if "module-loaded('toaster', '2009-11-20')" yangcli> log-info 'correct toaster module loaded' yangcli> else yangcli> log-error 'Wrong toaster module loaded' yangcli> end === feature-enabled === The''' feature-enabled''' function tests whether the specified YANG feature is enabled within the program. '''boolean feature-enabled (module-name, feature-name)''' Parameters: * Parameter 1: ** Type: String ** Usage: Mandatory ** Purpose: Specifies the module name to check. * Parameter 2: ** Type: String ** Usage: Mandatory ** Purpose: Specifies the YANG feature name defined within the module indicated by parameter 1. Returns: Boolean * true: the specified feature is enabled * false: the specified feature is not enabled, possibly not known Errors: * Missing parameter error if less than 2 parameters provided. * Extra parameters error if more than 2 parameters provided. * All unknown parameter values cause a 'false' result. Example: yangcli> if "feature-enabled('ietf-system', 'authentication')" yangcli> log-info 'authentication feature is enabled' yangcli> else yangcli> log-error 'authentication feature is not enabled' yangcli> end = Error Reference = All Yuma programs use the same set of error numbers and error messages. Error numbers are 3 digit unsigned integers in the range 1 to 999. The number 0 is reserved for the NO_ERR constant, which is the same as the status returned by the server.
'''Error Number Types'''
{| class="wikitable" border="1" |
'''range'''
|
'''description'''
|- | 100 to 199 | system errors |- | 200 to 399 | user errors |- | 400 to 899 | warnings |- | 900 to 999 | informational messages |} == Error Messages == The current list of error numbers and default error messages can be obtained with the '''yangdump''' program '''--show-errors''' parameter. The default error message can be replaced for some error conditions with the YANG error-message statement. The following list shows the default error messages for all error numbers currently in use. $ yangdump --show-errors yangdump 2.0.1404 errors and warnings 0 ok 1 EOF reached 2 NULL pointer 3 malloc failed 4 invalid internal value 5 internal buffering failed 6 invalid queue deletion 7 wrong init sequence 8 queue node not header 9 queue node not data 10 invalid queue header 11 entry already queued 12 too many entries 13 libxml2 operation failed 100 cannot open file 101 cannot read file 102 cannot close file 103 cannot write file 104 cannot change directory 105 cannot stat file 106 buffer overflow error 107 cannot delete file 108 cannot access file 109 db connect failed 110 db entry exists 111 db not found 112 db query failed 113 db delete failed 114 wrong checksum 115 wrong tag type 116 db read failed 117 db write failed 118 db init failed 119 beep init failed 120 beep init nc failed 121 xml reader internal 122 open directory failed 123 read directory failed 200 no config file 201 no source file 202 POST read input 203 bad drive 204 bad path 205 bad filename 206 duplicate value pair 207 page not handled 208 page access denied 209 missing form params 210 invalid form state 211 duplicate namespace 212 xml reader start failed 213 xml reader read failed 214 wrong XML node type 215 xml reader null name 216 xml reader null value 217 xml reader wrong name 218 xml reader wrong value 219 xml reader wrong element 220 xml reader extra nodes 221 xml reader EOF 222 wrong length 223 entry exists 224 duplicate entry 225 not found 226 missing file 227 unknown parameter 228 invalid name 229 unknown namespace 230 wrong namespace 231 wrong data type 232 wrong value 233 missing parameter 234 extra parameter 235 empty value 236 module not found 237 max length exceeded 238 invalid token 239 unended quoted string 240 read failed 241 invalid number 242 invalid hex number 243 invalid real number 244 EOF reached 245 wrong token type 246 wrong token value 247 buffer overflow 248 invalid range 249 overlapping range 250 definition not found 251 definition segment not found 252 type not allowed in index 253 index type not found 254 type not mdata 255 meta-data not allowed 256 top not found 257 resource in use 258 invalid value 259 too big 260 missing attribute 261 bad attribute 262 unknown attribute 263 missing element 264 bad element 265 unknown element 266 unknown namespace 267 access denied 268 lock denied 269 resource denied 270 rollback failed 271 data exists 272 data missing 273 operation not supported 274 operation failed 275 partial operation 276 wrong namespace 277 wrong node depth 278 wrong owner 279 wrong element 280 wrong order 281 extra node 282 wrong node type 283 expecting complex node type 284 expecting string node type 285 wrong data type 286 wrong data value 287 invalid number length 288 value not in range 289 wrong number type 290 invalid enum value 291 value not in set 292 extra list string found 293 unknown object 294 extra parameter instance 295 extra case in choice 296 missing mandatory choice 297 wrong config state 298 unknown application 299 unknown data type 300 access control violation 301 config locked 302 wrong config state 303 max-access exceeded 304 wrong index type 305 wrong instance type 306 missing index component 307 config not found 308 extra attribute instance(s) found 309 required attribute not found 310 required value instance not found 311 extra value instance(s) found 312 target is read only 313 invalid pattern 314 wrong version 315 connect failed 316 unknown host 317 session failed 318 authentication failed 319 end of comment not found 320 invalid string concatenation 321 import not found 322 missing typedef sub-section 323 restriction not allowed for this type 324 specified refinement not allowed 325 definition loop detected 326 default case contains mandatory object(s) 327 import loop 328 include loop 329 expecting module 330 expecting submodule 331 undefined prefix 332 imported module has errors 333 pattern match failed 334 invalid data type change 335 mandatory object not allowed 336 unique-stmt test failed 337 max-elements exceeded 338 min-elements not reached 339 must-stmt test failed 340 data restriction violation 341 missing instance for insert operation 342 object not config 343 invalid conditional object 344 using obsolete definition 345 invalid augment target 346 duplicate refine sub-clause 347 invalid deviate sub-clause 348 invalid XPath expression syntax 349 invalid instance-identifier syntax 350 require-instance test failed 351 key or select attribute not allowed 352 invalid unique-stmt node 353 invalid duplicate import-stmt 354 invalid duplicate include-stmt 355 ambiguous command 356 unknown module 357 unknown version 358 value not supported 359 leafref path loop 360 variable not found 361 variable is read-only 362 decimal64 base number overflow 363 decimal64 fraction precision overflow 364 when-stmt tested false 365 no matches found 366 missing refine target 367 candidate cannot be locked, discard-changes needed 368 timeout occurred 369 multiple module revisions exist 370 XPath result not a nodeset 371 XPath node-set result is empty 372 node is protected by a partial lock 373 cannot perform the operation with confirmed-commit pending 374 cannot directly load a submodule 375 cannot write to a read-only object 376 cannot write to this configuration directly 377 YANG file missing right brace 378 invalid protocol framing characters received 379 base:1.1 protocol not enabled 380 persistent confirmed commit not active 381 multiple matches found 382 no schema default for this node 400 duplicate source 401 include file not found 402 invalid command line value 403 invalid command line option 404 command line option unknown 405 invalid command line syntax 406 missing command line value 407 invalid form input 408 invalid form 409 no instance found 410 session closed by remote peer 411 duplicate import 412 duplicate import with different prefix value 413 local typedef not used 414 local grouping not used 415 import not used 416 duplicate unique-stmt argument 417 statement ignored 418 duplicate include 419 include not used 420 revision date before 1970 421 revision date in the future 422 enum value order 423 bit position order 424 invalid status for child node 425 duplicate sibling node name from external augment 426 duplicate if-feature statement 427 using deprecated definition 428 XPath object predicate check limit reached 429 empty XPath result in must or when expr 430 no ancestor node available 431 no parent node available 432 no child node available 433 no descendant node available 434 no nodes available 435 bad revision-stmt order 436 duplicate prefix 437 identifier length exceeded 438 display line length exceeded 439 received unknown capability 440 invalid module capability URI 441 unknown child node, using anyxml 442 invalid value used for parm 443 changing object type to string 444 using a reserved name 445 conf file parm already exists 446 no valid revision statements found 447 dependency file has errors 448 top-level object is mandatory 449 file name does not match [sub]module name yuma123_2.14/netconf/doc/yuma_docs/files/0000775000175000017500000000000014770023131020411 5ustar vladimirvladimiryuma123_2.14/netconf/doc/yuma_docs/files/yang-modules.png0000664000175000017500000012163114770023131023527 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+£KIDATxœìÝX[ÆñÙ¥ElT°ÅVÄàŠÝÝñyõÚÝ­×VD½vww7v·b+¢€Jˆ Ëî·°”JEýÏãœÝÙ³³»3ë;gÏ9£®P()£žÚ¤'h@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @"¤û-‘HR{ð (ŠÔÞ„I÷ø“Ѐh@4 BF Ðé¥ï9Òél-@¿ ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @~«0÷IEíÆ>Vʬxs©‹¥ô<í§£Íw‰Z6jsêõ†ªúàYd h™—"äýã[7<}ùÊË70è‹Lªkhb’%‹™E¾¢ö%l²éü‰˜HwÐ@Æöd^õÂý·G‹Œ»qy|IÝ?\x¦_qÇùÑEýzëÜwµ³Jò‹âóÙ¿óUYö.¶¬×hÇΦ’>cø“% {ÿ&«>çÝçUL¾µ0äÖ°B%gDo«N£#owÕ4HÑsF|¸`åÒÕ›wì¿ú&<ѵ$Yò—¯Z»q‹¶m–µÔNéëd|h cÑ´ùgÍÜv½Î„EïMhóo½kÿ–ÖKуÜF¶‹MÏ‚a£eKZ'žEÀÙù[ÞÅ¿%è€ë!Ÿzí,~¼õöõWõ;Û'ŸÆ×(EУSú˜rØ3ëú?½¸s¾òo¨–MýcÇi]*‹Ú¯ß$@ºC€2|ÝVÏßa×ýdHTñá”6ܘVN?Ù&TŇS#Ú-ŒM–ÆÍV,jn™Lb”¿?æº÷ã×·…Ÿœ·ëU«ž¹üëE~yüØcm××ÉòK›}o-êÖ¨×VäWýFè“}ÓÚïs™7öÒ™ %´å6Ò#4ñhäé²ê¿¶]GŸÌh3®á­Y ’Œ£ò€CÛ/~SÌÒjÕ‚ÆÙ“kpx³Çõhèw7_]°ñéÿFÒümæ¿qмaÕÇûei5Â÷äÈšÎ3nÊâß(±¬Ð¢U“ÚÕ+–.b“ËÒL_CñåÃû·>>ož\?}ôСCGÎ?û»vèíkÞa@€2"õœ–/Þa×á *þ=ŸÓvt£[sŒÐrÿãCÚ/óŠ)šµ[=¯y²ýŸo™A]0ªX]çÂIŸ¨e÷¥+ïœQòg²æÃéC¶w;Ø.ǯè4¡º5³~­¯Òs6§ÿ¹obgøuO5ã칕…JTªßu”þþÚ—I“fï}ö 6A ŒIݺÍÒ%ÛmÛîýU|9¯ÍÈFwT3N0BËýŽ ê°Â'¦hÞiíܺY“ïÃê¾fÑݘBÎÿÍv1ïV|轨’ÇšEׯ.­,zÚ0ËJ¹½Î¿ŒZ 9:lÊņÿUNÙ¸À$(>žÝdÄ¥¸Ñ‚zŽSŽíVÁ$ùW¨‘µt»){ÚŒ¸·~pÛnKþì–24Q©åh¹xù·–;£Š¯¶ÖøÞ"§ïc£Ü÷Ð€Ž«ßÆ-þ·n¶³i †ß\ºòyL¡@×Î¥ìô{–ÚóJTùÝ–yg§Wªm"®³¤Àß.už6^®Ú¯EW 8ß?ÿÏ &ür{F—¹/b‹ÒãNï^:ù.áq¤E:,¹Z­Ñ½|eÐ@F¦fÑtáʧšn ˆ*z-m?¨ñ½åµ¾Çò÷úwZ÷>¦hÕcýL§,)™A#ð‚Þ1…âÝÛÐTWkÔ»jß§£zJ|Üëzì½s‹lâ&ã;ŒZmm—SQ]&W'Œ>Ònc=³ŸÑCî³{Øì'qõ—™±y¤¨ôCÓºvÿ^?¼É‘¿¹uöìµû/¼?|‘è™å,TºJõry é¿ñéŵ3®»¿|*ÕÏ–»pYÇj¥sé‰{—!o\¹xýþ³×ï>ËÔu³Yå+\ºBY[sñSö)BßÝ?ïvñö¯€AÛÈ<—mÉJ¥s'¶ýi˜<ØûÁÕ+7Ý#ß• p©¶aVëü…íË—+l¡#î]‘QVtõ¶ûó×ïü?…(ÔµuuõÍ­råÎo[¬H“ß0à €?‰ ddjææ¯ns²áFߨ¢ÏªšÜ_]/®{FÄ»½};mð)æê³aZ"Ý<¾¡ð=éº+ º ©Ð«yžÈH½vgÓ¢.þzÔuÏ›¦Ý¬E†(uëV³ý[j골҇-çwšüÃ#÷Â_lžy4$¶hÑÝ¥{ÁŸÛø“d/\íóö¿£*œì~gT!͈€k+G »è¬Ï·kgwì?{Ѥֶñã~èëcóF Ÿ²þƇo×¶ª9bþâ± ó$Ÿ~åŸìš3qê[®¿OèîleZ÷1v@£B):ÍP|vß6yðH—ƒÏ¾ë%n\ªÝ¨ÓûÖ°LÙ°Ã1K³3ªeëA×Ï*•ä‡rcpûÙÑsÆTÙîïÖÔ$EO“˜0ŸókfL›»bÿƒ ÝmfߪϨñLö]‰¸±qÚÄKöÜ Ltë2µ·î6°gí\Z?³ÑR ÈØ¤Ùê¹®éx¼îÕ\Íï×uê×ôÁº†Ù¢bmÄÛ=}ºlöY7ï€ ““h_„÷×ÃÁÑê½ë«ûIL«÷i`t`KTtP\˜¿åy§Á6bÛtŠœÓ|aÃmªüñdÖ-=w´ú¡ÆLÙëknÆ ôè]&-]ÍYñùÎüÖ5úî÷Mð^·¹m‹žº}üäÔªQ? È| «ÛxÎïg=‰ôúèÔÆE.L?·HÉ$2^„mšŽ9–ð3FywuÓØ&›8Oݹqh¥¤‹øòxõÿjvޘȼ€n¬â´}ÇèCûGš'UKªS|º½ìŸ=7<ŽH|ßë›Ç5Ù¼°îÌÝë–7Nì]‘\˜Ö¬î¨“ßÞ|ã‹çÕ]ó®z•jíÜ1;W»Ò%4ÑIÍjÍY×õˆsô A¿Mz7ußÔ4»Z„Ï®Þ]·Æ´" ù‡l˜PÑ0e¿SË<¶Ïs‹™ÓB¯vgóè 1®Ô³YÖ-+Tm›w­qï3¹˜ØF6©YíÇÚot=ªz|ÄäóUIá¦Å'÷»°ýv\1oóFùR¯ùù;a/Ö´iÝw\;¥¶‘©Ž, H·NÄíõ;ußÝÎòã‰UêÌ×EרD3Ôï×x5» «; ä½%%œ{#ÞìS©î¢§_ߪmje•U;ô½§§_¼hþîȈʽŸuqΚȩKøË5m*tÞíÿÕ†Vòd7ˆðýôùÛ¨†ÿK“kÖ×ÞP5‰÷!uE¼?6´F­9wå_ݪnbi•ÕH[öñ­Ç›€¸\ýöÀJŽ~nnÿVN(C‡?[ҴƨS!ñoS3Éi“ÓÜXOMô1ÐÏëùëI¤téÈø¤Yœf®ï~Øi‰j–çÛºölZa“ÃÙžÿÛÛTVpøÆqå’ž):NØãõ oÄŒö®ïÂÝúeº·µZ1÷uTáùÊ¥7G.(ŸòK‰GÓÈßͥǬ*‹U}¬½— X6èÒ ÑMÙ—''(bKFåkäMC?˜{¬éÜÝ#2=–é6yBŸ–5ŠdÓ”Dv'ö¹¹Ëeà?3Ü¢?›ÏŒ>i_kQSUz¶p4yT÷&•mŒ#¿¾åA¯.l˜Ü»÷²ÛÑ“Œx/ïíÚ÷΄¢ß¿Ð°'‹š7‰ŸžÍª q™Ò·i9+Uï^E°çÅm.øœ9§z4¿q+»{‡{äMà¹?»Y—xéY¯ô?®®#ÛTˆ®Lˆ||lÙ˜ÞC·>S„_ÝùIí ýåîìuã¥çlŽ=GëÙªº]V­è]Zù_Üê:nøœªßpäw¦Õÿ_©[š[|ó’~‡‡‹KϦՇ̙ܧY9kݯ¢¶ìã«ÛgïÛ¹eÍZ7®¤ch 3šTŸ¶¾çÁjÑ× ÜÕµY§âwvÅ6~ÚÙ8¦Œ^JÿCÿrgù²G1ó=+Å¿W§X—NùæNVõaöÞ¸à¿åŒ‘$GOûkuÇcª@rcÒȃ¶4LÁÔzñÉ?>½ïWÌ]>OZºJˆÇ[åë,ÜïÀñYµ³Ç}K´²—j5ýp‰ëwN•Š}×´ª±1@ùaéU›~j×à2FqoƒT/gå¿—¸Ù–v˜/ùïúÐÅ¿¹x»ìÕúîƒÏÄ61K‹=q:ºkHÌëZWì8çTç•kͬºíËÉÝ7Öû¾ìåêc®ÇÅÎ믬k›+~뾚QZƒ7ߪ^ªVÅáçCƒ|Ód»kÈ­‡ÅÎo˜­ÑGÖô(ñÍÌàÊw¸RçÙG4šT×iÂ娎Þvô}ºæò_÷v ¼´üЧèe­jK®þ;O'ê†9íëþ­ü;Ïóéç̤ m"@™ƒÄØqê†~«¸¾Œ*]Ü|!ö¾¢6Œ(•òVâO—®{SÈÙ®»ý7YM«PûîE&GO°Ëõ¤oÆf¢[ÛÔr´˜9dJ‰Iª¤¸}М[5§–ÒSEøû§ñúújdÏc’ƾñôj¯>ôUzŽ¥]¨›ë€9ö3¢»x+S^Žî{w )“@u‰Q¥‘.M–Öß©ÊooïyRñë«Ø|¹é2îTl|6l¸þ𔪠öóPËê<çèò‡¶ÿ;¦êr|ìÜ[-fÙõÆßp™|.öª4FXùuzŽÝ0ýRCvm½S¨áFÿîMm^ÛO½]–žvbcÏ"‰í_j¦cv­½U°Õž¨÷øýÚI»'Víÿ*?áoï<ŽéN£å4 EBé9>©žu½¤W†¥±ÿNü6ÃÊ“6Ú_qö³¯o.9eãÐ)¦ ·ùÛbsi®‹|׬«‘·EÜ„ÐÁ‡]x7èh)þ7|í¢ýf·ZPo³ªGÁ³9ƒ7õ<ÚÅZÄ—VD°p\IÏL?¹m}¼~ÖºG!‰¯ nY«÷ß•LQ„‚Cg¶LôõèØµlf=#fš AЬ>m¬cbÓjK²TîRMgç^U‚ó8w?PQ2þtAׯ=çJŸÕ,‰ÏC=W{×QsìF?P_­[rcÂÒJñÂ^Ðõ%c¯ùnÔ|ΠoO¡â‘f­3yRÙM½®(]%•„?ß8óXÌ9…u¿…}MÏ*j&õË·GõÓŠìÜÊSïÛ·‹7Pö%öR=êÚÚi´Ë €_… dƒòã7ÛWnúãØ›¤¥§­ø}Nœüía×ýŸcJQÓ?¿’ºu¼ ¡enó¶{´í›Wü·Ô´æäñe·ö»ÕW ìÔȉg›.­–²iB")ÂCâ.?(¨k©'÷ȇ«ÆL>™TÔ+¨Ý²ë¯ ж[H¢O¶vîŠÔf{F÷}ШÒÃùÛ^·ñéÙTÊ#ìμ>Þ… æqu‡<Ú7ã€V õjŽG³@‡þåGÿ}IUzwtïãJq-ÚÊÚŽÇÖfÚ¸·c–$ßWõ\MúUí×ö”,©•þ<™çÁõ±—ÑÌ×±s±ä‚ÑÊ_¿ŽåäùQ¼¸}È=¸]ö¸9]4ÌòfUžæE-[tÈ«z‹8i^ ÌD¢_vÈd‡é-ÎF—õýÛÃNÌÀ:™çn×ã13þÆNÿüiü ¡… ×?î1Öî&ÀÐÈ×Å¥çŒJ Tío—\2øòÐOä,ÑÐŽ·u²PYšj5)]>G’9VËÔÂ@¢‡æu,’䙃º‰•ql!È÷sü>Çrÿ;çb[²…"M+%Û—\-»cc;áRt÷8{÷ƒ¼dLk«<àÎùW±ÛX¶qÑä&”šUhh'œº“ÌjØÇ;Ä,”ªš;%G¦eQKAˆ ÐBà£ÇÕâ~ÔšUl\D¸¤ê¸ôqWËÒõnNÛ«Yy+‘`> LF]3^»˜TC]Ô0¦°g›æ_‰)ÄMÿü½¯&„-[~gȜҢ:0GÓ/7bF­m«¢ø­ÉÃ÷wÚÑ$…—7TÓͯYñ›T™ê²ä6Mú X¢e÷Ó@–<¦I¦m‰¦¾Žò]Që“…FÄ?Wó¹ç[0.Z,kò_ü¥ìô…Ñ?5xÝó²GoL˜÷½Øþ‚Ué¼É·Üªg-jg(ÜIð%©%ÔëæóØŸ'>ms?Ib ÷G™r‹-käï2µÝÌúë£ç½Oë ü3±­îüW5ÇÊ•*U*WØR—Aƒ@FA€b!V-‰m¶‹?ýó÷¾žúõº…—'¬¬jðÏ©fÙtæðɇǹG•>í<ëF­¥S4äQ#k~3Ax] ÷y Œ’úÒ3jpBþm+u„çâ²9ÿ¹‘àú?%ù.%’x÷§`íDï‘zÅ…×,¹³¤d:@ åz‚ ½ã:`|][®”\”Z=KNAHSZæÿÒ/ùµ’þ%üë}EjVwÑ1¿¿Š7t5Àýäfåß¼¨²û«^ã–í:´pÌ+òºëÒ4€” º¾xõËØ’nèá)CÎ&‘êBýâšœ}·Íw›åX/éÞ²‰Ð*ÜǥݼZëUçÅÜAzè–+_^RÃü…M…+1AÉãòË>¹ÓÒ•ÿExÜø6ASG#%‚DS7®§LxH¼¬øUm)ªMª¡›†.`Eú)á+:þ ‰~‰þûžTÝ4uÌäùû}ùönù»;GV*ÿ&ô(ÐdÜ<×ÁÎVií]bh)£øpnþæ·qå÷GÏ9’âGÞïzämÖ?táb‰I‰+nêu!ª Fø™QÜš¯¨aœ|rÓÉ_ÝV²ê\tøûpñø‹Ðª \b$£“(cnl!ìÛ†Ó„)‚Ãb ÚñbòWµ©ša“û$äáñ*K$jZqÿýÙN{t{XÑ×éIšq‰vÓ÷µ›ì÷ÀíàÁ£§Üθ¹üü›Æ÷°Ç;GÕ:rlƹýƒK¤xöui @ŠÈßwݘüz‰ ;îºÛ³y”´O=OÇ9}§—wQ ^{¿jÀ¢!×FØ&û0©YÅfÅ„s1Wó~¾}÷³±Ed0cú¦ndi(ÑŸÿK™ ${®\/¶`d¯çË×µy„ 9’«Mæÿ* ™U„¯c¸"Ù¯ù™y=ÔsÅö*ñ¾ë*ü¢MÃÔΩ½òo°ÙéååÍs'ŽسmÓ¾;±×þ :=´á¨Ê÷çVÈ„¿‰@JDxíŸ{$‰’Sàò‚MϺ/øc9E¯ÌЙõ–¶ÜUº;eØž.»ë'û(uëzJöp3ºøhñÂkTÌl‰E3{KAˆž‡ãÃÝ;ïeŽzÉ|÷‡{ßx;Y¡`YÄ"î¬CÓ¢HŽØÚ<¯= Š$ eïî>H¾´T]#vdÈÇy2«‡ù¾ü™«³hZÏ÷ž\=çVÕöwY©å.S·«òo´ëÛ³. ;¢_-zrìž?Ô± @ê"@Hð[çÍ46ãîÝ_8%}!äÞëkäl¯šZ¸¿dÕýþÓJüØõ´¥ÙNYdÿ(Õ\»ðvlɶs+›ö$–š×ü§ºÖ飪¬órõâëcWúÁ+kÙöté4÷¯Õª‰=<æ \[¹lò`ÑxÚ€üǦ=U#.n=ÃÑmTÉLÕõT»`ÿÌf¯TMqÚeë‹}ó%ñK@Ø“us/Æv¢ÈV³Mü“í Ìf¯RÕæ·kÛl‡ºI4¢Êvèîæ[›'vaCÙ«&ß-ælß½Ô×§<úöÝ[Y®Z}A‘mg­úoéDΊäëxkZW,®/; }"@HÎÇ‹ÿmˆ‹+öÝçÑY’űGmƒ};TM‹{\¿ÿ«Y ¯„òõ\íf÷›ZvÖ˨RÐÝ[)Ú#‡);']*7æjtcß§CËÙóßÜ! $½%2¿[‡Nþ\Sgš SrÀ„j‹ºžRý¸³]“)9*gôÝ«—ûZ«óáà˜²ö_û—ø¶%X×~ਊ‹¢gEO©×µðåumr}·W(>ßœÕ¤ÅÆN¸lPªMíUí½vŸÖþ¤òß]àDühUg§¡W“ë#<õ\mç w)59º¯Š÷ª†Uµ¶pi–'É>F÷÷ÌŸ¾:°Ç¦™ãŸ4|:;¼ÿÑ¢=ú4/“-©³KyÀÙ¹.±eT+\-ý7€t‰ i ¿SóvƎג”ïÞ0§¨/‰Iå ŒwlP œ 92w¿Wã.V?ÚЧk?dV£%Ívµ ú¥F8à_£¶ËݘÜå}xR“BÿZUnÕºIíjJ¶Ém™E7òZ%Šð €·¯Ÿ?º{ýÂɃ;·ì½õ3CÕÒõœí–Ì\S´ïU„»4¶|Ñ«ã\&õnPÜL|_^_Ú>wDÿÙnq¯X»ÚœEmø¬Ôót^8÷`ìoÎj=êZŠ ¿†åÿnl¶!ºÓ¬üü¼m/ÚÈÿ£“†I³ÕŸ:ºøîa·“_5>µ¬5g½’»K£~;_ÇÞ(}nãLå_ʪP/ÔvîêÒíDM›ž[w<¨ToqLgÏ}ší› ä°±6–|~ëùÊ÷›Kè½{Ë߉ô•Ö):dÇòËö]÷EO(tå¿.ÿëbd](Ovýˆ€×OŸúÄÔ¦Q~ÒÊjëZN}œì&Vž0¯É¶V;£çÈ“=X7ÀyÝAËÈÌPì×ꜫۦEºë¸zŠx¢m×ïÉÏõœÆÙÚW‡çôPþ ê&V¹-Œµ!Ÿ>øúø¥¤J¤O¯š üS.J-sg731ÖSÿì§|GÞ~ýþZ´_áâÌ @:E€™Ç®y§cÇ9iTíQ+»øÆcýÒÝšg_µÈGUº½pí£ž“›û,qš»»üovõ˜–Ê“Ù÷Ýîî´uR¿3Ž‹êA«‘·VŸ1“F¶+mš®¿2ÕÌë.¸tÚ²uÓ±ÇãõªøôæÉƒ7ß­›Õù߆9˜&þakæí¼ù‚¼³s·­ñRl çÃ[_…Z­2#îd±h]ʶвùÊC¼«»ÆÄÐ@ß÷ñŠÛ¶q­ý~ÄÀÕ™ ©qűÇïÛ±ë̳ñl)O^'ö ë\†Éò^Ï?$¼£i•ì½q÷ìÆ?p,HÒõÿ~·°'\‹-i;ýãôCÝ—uKtimµÈ%:<]±ôÖðyå’š¥,I#‡±S«­ízJüåí$úv-§k:ìKWoÚ~à†O ´rØ;ÕkܪmÛ&•sëfŒ–B53Ç1G^4Ù9{âÔ[o$Ø99«}«Þ#Æhl›L÷p囩kû¿M÷+4š4x”ËáßÍ&aP¬åˆ³:[i…¹'ôðI*Œ=õ°ôŒƒ¦î|øÝ۵ 6:cöðúyu$!ïzøÑÎÓh†Û_^6ÇuɆ#³Z'WÙ¿jÕkÚ¦}Óʹ¿ügT{Óýû÷îÝwè¸Ûw—ŒÇ´xã®ý‡nW6+ÿýéG0É5<•üeÞbiÚŽvWŒþùgÕ)=ÇS1'¡{4l†ÞW Yºu—“¡]~xkÔ³m»­)TõBB4?ö*eu8ÿ²]EÝÈÅЇ'šÔÛ{ðYˆj=ÃVßl*¨ŸÂ×þñüª#³–ß½{ýzëTϧa±ëi=Èk敇u½òÕKÐ*TãÀÓ ßÕÍ—œ–‹xíŠÀSêÍ}.L°,þ¬<«Š×«ÓÚRPh!Ø?,¦ß„üõþ³—"û0k94É¡÷m]zíókîº&¼\¾ß¿G_3u!âå®s7"ïSwüÛ.ûXFîyèʽÈCGG“¯Pm³Šy„ÅoÁýÁƒÏNùŒÅÖ,µp,b'¸_\þ°¤ýÎf÷Ú:™pÊ™¡ dŒ…b,~ƒ˜ßO¾»pºD*‰½'æ¾P÷£>Q ÆÅ­¿ßQ$z6¹­…ûÏáÑ1Ÿ ¾fFBÈÝ#ªõÍ kÿÙŒöôô»¨=k#‰L¿ë´šqdÇ!üÓå±d,òø öœÖbûuAËH T½ÎvRêÞR»,éæg($oAc¡ …TñÅãê¼PÇÂ0öducU{nˆ—`„`$ÿüèjG7°Éúg3‰<ÔË[uLz´ë=2‘•â.ƒ”ÒjƒO [4î–zõE#—hm-Þå~°ò V¬ÜlüòZzDè ƒ #a,c¡ªä² èE©VBCª¡ÝY",4²ÛGDX êçuMÝ?I_¢7Õ|ØžNÍÚµÕ´óäzåÞ;74_à—¥E÷õݲ[(Ú/Ú>®ãÁPAø¸ªã¶f÷;Ö1ãÊ Ð2*ÆB1 œTC?ú 4"$òÚóßæEyX¸ªZÐÒÖVîªr5MÕ‘( ó“Ë/ Q×Þ¡Õ²Î].ß/h{v®]‡k~¹ªž\\Ò"òu™´YÒv›íÊýÊ£óÝÅŽJ=XS,+­Ð@fÁX(ÆBá·SÓÉm­)\‹ì£(¾k¾•øD-èZg‰ìâ!ÑÍi¢ºçóKÿÁä9RM e‚VnjÐsßáçt°ç´fO~±¿«Iµ˜ÃDݪÌâ¥×mÛÜþ$¾ëW÷k6a}C"t@€y0бPøÝ4m-„ÝÊ3®[ž2Áî›cGñéÉKϨ%;g‹ÈóR©®])áš2^¾?q'd@¾Æå~OÝiJ.ÿöPE#O%3a·— ž»(+göS‘(¦ësµÿº(–iŽm–m}Ôjwˆ2©oê²¹ùƒ®Í9„Ò=4$„±PŒ…ÂZÖq¨(õ¸ =·õÕ'çñï”v[ó,ò¸RË÷wã¨ãJ³H ;“¥—„ð£.7^ÕwÈ•ìÑ#Q7ŒlÕ ï_)„Eî©å¬U*ÿ¯§‚pÛÅífצe¾ý¥)åbº>7ë¶¾{¶o{:©7[ءɩ¥;•§¡þ׺ô±¯°©ÔŸý‘ ¿ÂX(ÆBᇨ[—3èhù™ïÞ¯Þ¾ ßàÅbó¤Â÷èžû"wÖƒZ´É½CV®5¨èåÑwÙÙÍ-'fÛ3¶ y̤ ùþ4òWÉ&ñ"›ê^u†]vÕ¡(÷yäã+æDQËÖaRýc­÷… /Öooz`E{“¯y„Bª–ü®Ýõ9§ãñ¥ößÏ„©¤fQò¿ö'›]ÿ ¶­íÕ<ßÖæ"õ"!@@B ÅX¨ÌE®Úqå¡a_wŒË¢¾ˆˆûzß–‡É"¢"âî‘h•›Ôwí—=F–™ö`t¦åLõÃ?Ý;v~š«û[AÈÕ¦óÁ‰¹ãZ{µ,ïhµâº=¾²Ë“æd_háPÖ<«¦ÌßÓçæÝ@‰,jŸ+÷?æ.aÕ;J=ûZÎÿ®>,<žëj±PßÚRKäó.Dbe®š4=äÍëû^yËXjD爐è-Wž±ÑU͸ÅÊž7jΛy3âí®M¥wíµ«œ»@v-iX¨ŸO€Ç3ýÞC¯Ï‘Ôeƒ"B½Ýº´Ùxò‹^³)ÕퟳݰbA¥nŒ¹¡|o¿ììºv¡Eë6eLM™Õ&Ý"@@B ÅX¨L#èüæÊ NßòWåæwŠõža‘ëïíƒæ–ðæ´hÁÅÁªõžî/®sÄ¢´Ã¶Ó-K=ÜãÜøÄYÏè˽š3Eo…e—ƒ—׉êQ¡•µýúqå[G^QèàÌuë?EÖ¬abV¢AÕa=œº×ʪûunÔ²©¼ã¡ÕÚéG–ls¿úÒûì!o©qÖ uÊ/Yå`6c‚Ó†`áÝ厥.h?ÈcmýÈFîò›O‡ìxó…€/aAÓÊ *NlS¾y™Oà LûÏ[;»µ|ŽmeçO>XøXí—®½‰>'¸½%¿æ³ü6vôY,²áZjVhÆ¥)uWu]{÷ÌwÎÝ :Ù-ÊT±ë3¢RçúI¥çÏnk U½ð&æ]ÜÞnÜövjÅF¸<Ù:þìð2³ÎÅÖŸüï¦O÷ú:Œê+H‹ui¼Uü•‘^  AŒ…b,Tf¡W©ÕM¿V Ý“{ú…éÓ|Œ}Ã3¯&U©D˦^ÍÅõj¦pÔLswžÑ½óŒïîXï¢XŸ@íÆeª®<_uå··›-ðZ²à«[Z_ñlÌsk;þÓBù—ÂM¥ïØñµ¢c²«©çr8è ¶r¤qhHc¡  #@@ õ=ÆB€@€á0бPð{ dŒ…b,ühc¡  ˆ@€D @" Ѐh@4  RS„÷í>ÎË=0hò_ÿÝÍ“¸ìüïxø¯ñéܪÓ?¿~ËëÑóá‘·It òÍS½aù=J–0‘¦ö&âg @*’¿Ú¶wÑ]eÌôß9þÄÍömÊëþɇÿ¡ïÖŽÜ¿ì½`\¬XË¡Nöõ#‚^Þr߸ôÖ’Ëw–LÎ3boïI5ôÕR{3ñ3Ð IÍJæ³^¿}{›œ¢òá¿‹~Ó>/¶1Ž»¡êAWÛ”X¾ÝÿÅÔºKs?ìÿwnÚ¡Ó14HM-o\+|ô…®CÝü–âfòῇԮ¶•á×7iXÛOsxû€×Bè£I®ží]ré¤Î¶á @€Ô¥–Õ¾x[ûÔzø¯¦¦_¦Y•òUô¿ka–ZV²±^{ ÂëSÏ}d¹òÂÒ->:øe2ÚX¨oÉn^ßwÛB?§»·êÒLíÍÒ&-ón ['xšž¾¾jéó§ RX:ÆG¿J† O˜Ï“ÅÃ6_ûú‹²P0µ·¿Ô‡CË Õ¹öVUÐ)~ømOgÝ÷ƒçþ[ziçQŸ¼Ë§œioݘþñüª#³–ßzýÂŽ‡Üªœn2oÒ™À0ÕME[<»Q#oülêtÁ™«îœ»ÿ1DYT×Ξ'›mÑÝf5nG]y=Ù9ɇ+)ž>ã²èêÁsž/""ë06-Q¹H«œzÔΦ'‰^+Èm•uÕKª‚Vуïz–÷¼·nÅ¥­ûÜ/> – ÚyËŽ˜Û´k íŸé¹, Q-0Š0]#@À¯’1ÇB)¾ø˜¹µïøÛ/j’äWGúc\»›OXË#½&ÕZöQøòáæÎý‹'ØýL®º7WÌjŠÀSêÍ}.L°,Ø{Adz6-³ãz—&Ù£BdÝu‹É 6¸ê¹våÿjåÛßÚHMZÔ¯ÓÁüþ’·‚ü¼ÛŽ—äÿzW–:ºàÞg½bÚe‰‹ ê: ä¹ç† Ã΄ 9ª>Ò²Btׇâ­Zç ³YýÕŠ ?\ùC.Žš™žÕóL¿>phÑè^Gµ—ïÒhséš§_nYãœ=Û-—ü†‘§†=k늶:‚2@+ãô¤'ÌŒ5$QÕønë1³åšBнW½m5ÖâG»/…?9æ¹`Ô¼‹UZú… ¢ àÊXc¡Ajb3óú K㨠üŒèLÀjäúŽýò¨YM&ö99Z]¢üÜßž¾þ³ò&»!õëgm‚•d­Q½¥ùÕÅoÃO»ú²¹S>e¬ÐÏÝ¿¯Å’QÞ‚à9wñë³¾škBæq}Î1YöΙ%»7…ÞÙù2Bù ÚêÇÝ*1.0jr•æÉŸbÊ<.œyÂgÞµy¯¢ñC¯ÄÔ©þÜÆí }áºmmßa½ó~Ó¨¬“»©*=G®­mÖlZÇõ+Ý"„'§¼?µÈ’ìs'(èÕª­”ÿªWª;¸Lšù ?„ H”DÛÀR;µ7©FÃ~ÚˆC‘ rÏCWîE.::šhÄ_EÛ¬bañ[Apðà³S¾ÈyÕm:Õ®1aå‰0áÕŠcgFwu6ŽÍʲ‡«O^²Oê“K/O¯Gv#QÜ»yѯxmÓØJ4Št¯_$ùGË_ï?{I¡\Ðrh’ãÛ§“è9´Ï¯¹ë~˜ðrù~ÿ}Í’ÎCR#ó¢Ù7oAñ9(8BÈò#?É_¬ÛµLyZ¡‘oæŠJÌ¿‘ÞñÈ8 %ü©±PÈdžž~µ gm$‘ÉäñîR36RÆÉ!üÓå±d¹[©YÙZïÄš áÃi{›9u0ŽœA —¾SsèЩ`Jâ‡fA'KáÈkáý¥ºdÓ\ô¨en("¹†ºõ‰Z0.nýýÓIôlr[ ÷Ÿ £c>A}ÍŒ’©M¢¦:`)߀¯„=9ÓeÀ“pÁ¨Ýæî}Rôò‘¦ñÈ8 %ü©±PÈ\ä¡^Þª“;ï‘v½G&²RXDÌ¢D§Ò ªù×x*Dœžqéi«Z#Ï<n'6øh7^^"GŠr°Zþÿµ°|–Ë#AñäÚ°z׆ZÔj]¾s·Jì ’ï…ñÅãMxÔ’ŽEB¹[ÝØDu™À/ÿÀÁèwŽ:ˆx@íM§Cô­¼¼Éo}*ü!h c¡þÄX(d2Šˆ/Ñ?Ž˜ÛÓ©±yBÝ—Õ´óäˆ;%Ó²sVùP·sráþ©7ªÏ/¯)È?˜}ëcÇ!ÕôRØ™^bl3ëò˜vNüïþ3å|ô>¼d—òϪAÓíkþ*gœd5rYPô6Kµ:¬¤Ñ}QÂBÔgÙ¿-ÕÊ}¯1á3“v/ocFßçŒ £b,”ð»ÆB!³‘¨ëFïwjÙ ç.—/¿]¨7Yjpk‡53Žß^Ìðŵ9'#ŠN¯ZRÌôR#«sú¶Ÿúñö‘›6\\¹õ¥¯ ¼Þ»£z+}÷ýs&‘b¤úÑY5"D¦P¾†oî—‡…«¨-íßן)üÍí~5.z‘kر>ÿ:Ðöœa dŒ…Šö+ÆB!ó‘jZDþ\&AÏ}#„”hAbRÕ¹[®k³<„O»ïô,XnåÉ›…ֶ˪‘üc¿«K˰Dƒª%8N˜v³_Í%KŸ ÁG¯}Rv´mâû»šNnkMázXd@¹ãÛm–ø©† èZgÓµ:åÁîçÚW[¿SRrÑõÎÝ‹h1‰MFB€©0êgÇB!SÒÈSÉLØí%ç.ÊÊ%sžMǪçàܳú¼äÏfÍ¿\~­¯Ió¶õ-RÞØ+÷»÷&$¿UíØä)ÑÎSjÖÒòÛ«_ò>Ü~#’Ђ¦­³…°ÛC>Üò” vߎÉýôä¥j¸³EJNƒERøÙ_ßyÿ5»š‡4vÎÎxƒŒ† 3a,ð#ÔrÖ*•ˆ×SA¸íâv³kÓ2)ŠœÒ\­j7¾hOðxÖ†ÇB–Áƒl’î·üµÐ ƒæÿ×uÌÁñã§–yV3Að´Ìô“N¥RË:¥ä¡ç¶¾úä\À þòÏnkžEWjùþ®cü«Ù«í«7?ÿ±A다´Ë7Ò4€Ì„±PÀѲu˜TÿXë}¡ÂË£õÛ›XQÅÞä«ü*PH¿»Ö»ÔÌvXgã= "¯"”têQTd÷ˆOGg_{Þ¨Zþ¸͈×'ï=QþkT Q¡djS·.;gÐÑò3ß½_½}A¿Á#ŠÅÖ¢ð=ºgÀ¾ÈãªÀ mrþÚæáˆWëV”îpã}¶"#ª gÖ¸Ip-©NÑÆeÌi™N¯Ð2ÆB S„‡©ºõÈCC•çaßíIjÆ-Vö¼QsÞÌ›owm*½k¯]åܲkIÃBý|<žùë÷zm|Žï¦˜Ð*ݧF±;îÎÃÊäI$ñ*Ÿû‹jRuYX°,~6Q(®l.\ܽÏßövFºá÷Ž]˜>ï…B0h¶ iõ˜3ÏD.Ñ*7©ïÚ7.6zŒ,3íÁèM˙ꇺwìü4W÷·‚«MçƒsÇoL~¿~ä²pU‡¯Yx2ÝŸdÏ>ˆóûîÞÔ÷_M¯s‘æÌÉ‘^ d&Œ…¾xrCå¦gî}P•¼†ÙöŬrïî&äŒ?óŒÔ¬ÐŒKSê®8êºöî™ïœ»ÿ@t²[”©b×gD¥Îõ¿OÏ‘4ò—á¼·õõ2Ãj~¼ß\µÅ…k^¡Ñe÷ÝEuöæÌ×{GÿKkÙk9ÔêþÙ+ˆޞ‘µÌÌË´¬5}pÍv¥õÔ’yxÔª•µýúqå[»Í^xåàÌuë?E†_ 3åa8¬‡S÷ZYucNH¿ÜØëTûà…wªtì5´`海˯:Ù±¡‰ÿªÖsûíx÷IµÞ­ÍyõŽÖ™3`W¯lÌ™™ d*Œ…¾eT½íÝ€¶)ZUÓØñŸÊ?µK [^Ð*‘;õ*µºú&±;Ë¿ªMÿ«Zu'ýðh-›z5׫™ôZ:¥œÛ ¡{L;oŸÔ9™çˆO«Ú&WÅ&@zD€©0 ð³Ð2ÆB~@ÃX¨H¿b,T}5´DÓ8‹™¹Ú»DÖðvaõÑs§zx‡(”¯‰yžr¥;Ö,m£-„ЛþíuæmX¼èe+Þ§ÏßͲEîò×Ç»n:æõ)\uŸ–e³¾ƒ›>^ÔçÀ_™ê¦ÿÝÆ,^û·<ð⑽;x<óyïõ1$jˆ¦™eþ*Uêôp´ÉµbØÛ‹ÿí>sòÎsïÈ’ïÊI½VF=4›ÓÈ]Í-_tMªþ¨7!äýýÇÝß{úØ78rE©®¥uþŠeÛV)l­3Ž/ôñØá³« ê•ûÌž“×ÿÜ¥ó¯Ý¹òüÝG… kV V­ý*[ë‰ùúúÂ_­Ê­lM$‘s¡Ôw°X³[ùj>>ó²Wžº¢ëé: “y^\ýÏê«>‚ ›«\—Z… ˜h a!><¹vèÑ;Ù·«+¾Ü;´ Çž§!‚q§–½ ˜iyž<ºïÐ'‡/Ü›>¼c c­’m'ž«wµÿ¨å”Y’oÔ”Á³Ä¥U©±ýÄqö‚n±Ð-GÛ½CªX(ïÌ?øpÐ{ÛÆu:À6F|¸èvîtH.çª »ç·²ÔU¼âæ²óúÎÍο鿩­­¡Djg+]î/kí-Ó.|P&öjÍÛÔ5Sîw]ól‚†M$ë{Ôò–®5´qîìÚþ^÷=½}ûí'JØ©²%­ã§MkviS¿·? ²›»çÔñôðÕÐ33ÐÖRž<|Qû>Þ¹~¦Æ—ò&"ðCž»òN·D3ݨì­n]¶ªÝîM!ðÆ©›Ím*é2™ :­R=ÝÙ+*=›”ù{CûlqAWö2äâ¶gßÌ­øpoSÿÈô¬W³Ç¨É% £V/êXÔ*bìG.][¤xï2fRAjT¬Kà g> ŠgÛnøÖwÊöõ> ðpêB¨–ƒs©ìqϨ¦•D§‰Vé¶½'•1Œ^=oî¬~Oº¸}|{v×ÑÚ›™JÕò9”¿ñÛ/Ê­“§P‰ªß "L´~EðóÝ}"ßi‘&£;çÐVÝ\¤dJ%fþ;w›ßµ‰®F9Ç4/Ù¾.HµL å³0”Z0*Ð`á@Ûºj‘w(Âß\ßÐmÙÅwBè…ƒç=J×Ë+j¿y¹óƧ,öŽEµc6פDË[Æ=– !w·¸®`oFG>€ßFE¼ß³å„—rAÝnPó’Ù’ hïö̧‚…sb†±«K µ.mxôÌÇÐû‡úٷɪ¼G«pêyÏì~.OœyâØÌV#^=r¿ÇÜà «t´MÙUBÔ KWmXÕ.~‚ÔÊUÐ\pû(ïïø…73ÕJñkþŽÜÿà¶ã¯• †•TIϪץW¨{Ë’Þ ö=1çbõUÕ̾ àz¦Ù-TéYI¢‘þI·Ëc”‘÷Ý£—¡¢´âÓ3·ÓA& rÆuß–Uª^DëñP!üÒ©»¾%+&ÿ€ ƒ6Éüní~¹ QÀ¡¼aòé,"àޡȸ-˜°ÉöU–ÔÈ‘ÏL8£Œ³Þ½CÚd쯫‘­B§BûÇ>” þç×?®3¹p\„0Ÿ‹›<„Ü «Û¥0÷JMª8×øæ65 5åË…ˆ0¹"eµ$ö¢îìzYƒnþ’ù¿Ý‰Qþr%Ôo^ ÎÝñq¬ž#é7I¢“3‡ð8PP„†É½^EðõÓwƒÌœäˆž!1*TÍQçÎÑ/‚üÉ©Sþå[~Ûod\è´)øÍý—Q 9 X¦dÐ[è»ÇžQ †&:¹<"Þ]êº:ÊOY¦Œ£Bä‚ndГU©]ÆèáÅ@!øÄá;ýlËÇ4 †>ü?{÷ÖÔÕ€ø$$$콇€€ TDÅ­àÞó¯ZµUêj]U«m­uֺꮫκk€(ÃâeÉÞ;ûÏRe$ä{ŸÇ›Ü‘›{OÈwOÎ9÷Æ­dªÓÍèU^¢á•¥=y%ž0±1¨:Ô…aæb@B²ɈMdõ°ªmx Uzå õü¢ØOØ–=Zi•d¿7Ǹ—«fPx)!I'îe ëg¡G ´Bâå¥çóÅS¦:2$3Ai~A¹xêÕ?Ë:ýórßVS´{4 ÝI¸Ï¯\Êj÷…ø~ ‚ÒG µ<&t“ãn&ܬ¸°“w"BãS’óKJ8<Ì+׈_œ›/0DKO£š&ÒT SÉøœü´2>Ѩ:`~NÌû\ÂÚ00èƒ %Ü }Õ{˜34€Š@€VH|V™d(9*S]–\(àr¤ƒrØúM_öYµm>¨šÆúïb(Íb¨ŸãÞ¿âø$õïëIcÆ80‰ çñÕåú#üZèÊ8ª„ ìѹMS/%r‰žÿàinͬt˜t*)‰Ý3îÐKÙ6QÓÖ9,離ѨÕìE)}?Ü2n…öJøy·n¼à›v_6ÂE¯êðr¯9XHHNèéä‹0 4€j@€VH5š$óY2EC ]]z&麖®ö&2 ÓF5mÛ·çÉ߯–‘¬Ð {§uaf_½ú”k5l”¬Azu™(=çáó6ôy×ê£XO]ÜúQÔÒMr9¢ºóÊ VÀ+—6U¡kÒëe 9^΃S ÄidOw“ê>)ã¸À@Ñè{…A7^Îrh©U;ŠZ!©éJïÎQSÂ%šµ&Іž“ráòYÅ\"K€&Í}®åò"ó¼ìƒ%«yMê`-ë ɼŒ'Q⎎f=ÛÕCûª¶‘“$ ßTq~TiVÂ/I/O¨XÔKû nrøíçÄjŽ›áŽ?ÝÁ«­é¿×2… úÁõˆ’]>â- t E×ÂÖ$æ’›RÒÓ¤šöïS7nnI¼$$;îUßÞR¦Gæ±í¨ú­‡4§<‰”ÅE<+wm[q h"ÈúPÔ„ƒêîÛº>†af§†^È"͇»[ÔP%O·ò÷Ð=.ôš{ÿ¿‡Yž¾fHÐM´‚b:Nêmuùìk±îÏ ¶Ó´{[¹)à–V¹7Í|Ø ·Ã»•’œƒì3Ÿ9aD3Í÷²œ€/ TéŠGÑîàïcòðz–è͸.VòÔäÒLì-5HfÉ>ûo̘ îâ/`åÅE¤WiMÑÐÓ7ŒÎ½ÿ<—c+Û0yTÿ‘½­J* Ùt½ÇŸ}­ßFh~ñ“ílj1ë=·½a}äçgÁ÷Ò‰ål£?#êÎ< oÞʾó—7þÍöùŸ©œ· ¥ƒ­¨h}–&¬ùùa1çÕÅ€7œìmµÕØ%y‰É¯³)’€Zú*!-×ÈÚPÔ…ŽjÒnâú¤Í³ƒ’¹…QkWEí´pt5ÓÕ ðJK 2s²ÒÝ÷,èXå|k4ë>Öúúo)DÝÕß¿òíüÞðÊ9’þzür¶pBšWµû 1zp4‡ä†nëcÙʈ^Vñ2—âÖÎV—äNfRJ†}s3EÔ(Å¡ƒõy$Ÿ<=±bTTK;;§XÿËo&‹Ú `û¢vÚöƒ›”ÿõ¾{±ÿüúEZ¿qmí-¼ÜÔØ³×îbÔ~ù¬A®ŒŠ{Ê“žx¢Âûásy‚7µz>»(%)jÿ½¢Õ\·(+[ÏÔ˜QmDpJóRØFVT’+zÕ”£W#Ýzµhn¢«‰zh€& ZqÑL‡¬p¾¸ÿVĽ„œ—/¢_R4­Z÷û|âPí Ñ›ÂŠHÑå}?_>î½ë×Im¢êäöÃoü×wo?OJN‹ IÊaoçܱGßA>nUÓ³ˆši×»ýüZVm¾ÁŠ_·a×ùÄüRéã´mËgngºwøcH3³ùìùÓ5Ÿ:Q\˜Ë3usé¾" wÃØéÏ"üG§Öõ?í°ø—èj†WNKûñ؈]ZS_®[õáí‹‚µ÷äã^'®Þ¼ò8heX¹(«iZÚ¸òë2®‹«µú›æ$íØôÛÞø"I€~yêGŸË–ý¿˜û£»fzÈîG&KÊ&Ék—Ì:Øz䶯»ÙVw@ þÞw[4Kú(ò絑Âÿlý—Zit’ò°‹fD–U|*ýÖîI·„ÿ›NXòãl[Œ ÐD!@+4ª–‹Ïðµ>ë̘|}çäêV ™8w+ü'ÇkPŒ¼f‡x}`&£ù‚ï×,øðÊtC÷é³Ü§W~ºÝ®íª,«fæ1r§ÇÈJÏÖ¼}Éj˜ºMøŸÛ„š—¢Û|·> º9–>ÓÎøÔòoéºÏ Þ)Ë‚ÌÓ6‡ËºUhB ä€ h9 @ÈÞ¡P(¬·ºPMÐr@€4€ ä€ hÂKxük}'T > ˜ ‚0T›T> ˜ ä€  (j®lÃïÔ šð¹„ "h #h9 @ªŸȇ~­Æ U†Ï(h9 @¨:T?È@±TýµW8ø\€BA€Pi¨~4€ ëÌ´ií{äƒêg…Uñ×jœ# |.@q @× |’T4€ŠBõ3ÀÇA€PEHÏŠOòk5N@Eø\€‚@€P9ÕÞÐ d„  BÐÿF¹à45I¸‚ýt8†5ߎ€  B$U)bø ÐX‚½ ÐdQ(Ó{T4€Ê©zC/4€*BGÕUž¾uÈÓré#†ƒç¼¯šé*Û5µ€]–•Ãæ½yH¡3MŒj±VIF.÷íŸBŠº¦©Z7ûM4€ŠB†PQ4­.†)!±~{'üàiòÕ”fº‘=+.ôÛi·ƒ£Ó“òù¢Ç-Ç>ê×R]îíG^›:'226ýu¡ø¡ççÉa¾ÖÊv4 á!@¨®FÏÐhIõ —ˆÕ étÕ­ã@ûì“¶¼nìùXL—Gn÷ ìô_Ü~Zúìã·£Óqй»ƒHYâ·N«êáh°nLü¡ûÁ‚jæ0¬¾]üËgô Oqc~YÛnibù{Ëéqcž®Œ:ß3øDÐИЛ êzSAcct;°–·½èùÍGõ |,}jLȱNîfêUª¹i®K¾/[Äz}ûr¯î—ŸÚw;}¾¿_K]MT‡+$h•Öè•ÐMUS§eßþ~xØveUJ_=üÒç3³,Má>Ü}ë)ÑýòÏÁC\5ñ#ÂB€†ÞT·ƒÞTõEý³ï&.8²z]¼p:gã—AºkT³\ñ½KÓ—è ›ösw¤g…†  êTº½©*@o*E¥*qg EMY.ò´í–îë~¬Ëõdáô“‹_þá<ß¼òÇ”•öÛ´-vmô0S–÷¥ª @…¡7UèM¥¢ÔÔhJ Õ˜ôÚ—R Ý΃ö~Ñ{w!!‚ð%‡÷ Ÿ3;bLæ'<²üùlõØ ÍŸ44ô¦ú4¬Ü ­×í{t'¦Pt]AcšÛ›¶jãüÕú¡cí~þÍÅó4ŽxIßà„Ï}4Å+=½6lÀ¹KñÒ+Ý1s_m¡ýþ†)ꂸ3—o¿w985½Lø˜aíî4`bÏE­š1¥ÉºðòVÓ~YÒœOfÍé™óמпÏņƕò©޾m§/:««]y!ìðñ‡Wn¾ŠIç¯QÝu[ó{_Ë*'OÀ޼µiûýKw’_剚 Ñô<:·óu¯€¾¦ZU3½€w鿆m÷.ÞIIÿHBÕÔ¶u0 =¯¸çSÅq ƒ÷]Y¿;꿈œB¾°àê´hßjÔ×~óG[ëÊt)š=~ÿ¿3Î!„ý|Þ¬ûÎv°zs`ø™|÷œÝÜoçL \&*>hPíVЀЛê#•¥nð_5ÿG×§ë/‡\] I^BRྞα›3h¬=Ãmé¼ÔÅù{}N¹÷ÞzŒ–=/ÆõdGö0Ûyƒ_ý¶·öûF¸º™{ù›QŠócB_Å?ˆÞñ zמž¯ð7%Dݾ3rS’þ^ðûä£EDøSÏ…# Ÿ§Ñ)¢­òËânÏíö,dªÍ³#Q‹E›¥Iò·èÑéó}cùQƒ<*6ùee˜´é‹£¹„n1zјÕít8…Ñÿ†¬ßvsþ…›[FO¼¶ßljYÝò„éùy¿…~ÖÖÚ¤¬°$3%åø¯Éqì·ËÑ?åPT| ^­°qé6±t»yhWZuËÂVÞ¾·?pZèž¶Vò$)ªqëõ;Û^Y@HÉÅcó.¸¬#:Ä‚ò;ËžÈ×ýê”;-96zSÉ‹Ÿ|øðÂ[bÕ#ðÊèŽÒZS÷1cØNûß-EUûP×Q ]t?¼y÷1/û۽¬Ü ‹¶Þüš}mØÿ¬ž^îd+šEÑ´²òn¥IH‘ð ºÍ˜z¢¿“%S8‡›—|pæÖ)Gò É>}ÏcωEÝ-Ì™tŠ <)æ‡[×?ØëÂüþêö¦^UPºä7Q¦Ù¯‰˜û]iKà¾C½'9֮τ¿ø™›>Øä(íÑ+ — ·ˆÓ³ÎØãKŽÔ—]ØéEoWj¹ôч¢*~ñ¥™[EéÙÈëTÄäaæâ çþýÝø-ÝO>¸÷KÿæÆêÉóÓÕ|Èè­ýcÆ_dRúwÀ™/»Œïe@){t5`[¾Þð€•ÝT¼´+ hA%´zS5 ô¦’ëÑ鞨éD‹VÚPô—üÜ%Òì“ÛµÐhïµfX°þös#HÙ¿ç7FµßìU©•±V›.ö­,¥ùf`3é÷qgÿÙv®”ðiæ½zÛ¿éxJaÚ¶þqK‡½=îæ’Ò¨ðN7SɆ¸‰as7f '̦ŒœÑ¦â™§õ¸yhè3¬W[Nœ½p¦ƒèÜs⃿ْ-Úµ>£6 ÓoÈà‰ ^tHT£î²`à@ó·‘bÒ³Çh³û;28«ï'ŒìÕ\®}RÓóǨ}×þú¯œôà/Wtz¼FïàôK±ŒnpWùÒ®4 Þ@oª‚ÞTòðEmÑQ¡9î}Þ–Qzëi[×ÇëÑMÆÎq^ðùsÉ;{:c—uÍMr)Ú¦žVäÜ BŠ‹‹…I¿B¤gÚÚXa€&¥¹ì7í&ø)nß]ª3|‡YUn­@Ñòï¨~&†Mv_È ˜mL#¼„3w"Eóh]§º˜7h;x~òå{Ñ¢ Ý®] Þûx1}ìÉŽ BbŸ<)îÕ\_¾íÒšuܹ6¤Õìx.!‰›Ìe á·]3n)úM#ÿák.hé ŒÂ«ÌOm‡ÅÏ55Ï~æuÝ4W½•Ÿù'‘üÉ\âR©ä Š^$$‹§\ü,DW•TM—¶:$¼ˆ¬kÊç4¯¦WmUut(èöŒÉ?©„Ü +àv0Ff‚·P@‰ 7zS© Vȼßÿ˜òÃ¥QïÕ3ÌLŒ É% cmñÓš®h>Ÿäe¥”ˆL9ó8Ù§w‹C¬ŽÛŒÚu}©FµìçëCM á³îO*òsÖ©8“_|ó@¼èS¡Ö|j?}ñ§B½õ(ƒ]ay„´)2i o³ZË~ 5[ÿ¶Ž Rãy¸éfÔ”á^èÕ o @À;Šß½©DЛJÕðŠ‚6„¿ÒÝñÝe"/å¿èÂÿõœ‡´”ºcSr%ðžÿôkl·µ.æ’§ùœôgéÙ²_ÛØOv^ú€ˆZ ý:¼ŸqÝŸ šMûó‚¼×efí?¹õ›ù‹ÝÞ¾+AvÐÙ9çEEÍyÞ¨q¶Òâ¤ÛÙ^›°¥ ÷ö±Ñ+LÏþØÂìMù°JóÊ«¾B áUJ+ß•¯Ž=Ï" AÇ]ÜÓÅÓà½BÎç ¨j(°ª” zS¡7•êîsu5ÕÓ×EO“S}5dÍo¯DgÄÖá=¤×4Çñþ~¿ì¿RJžoÞb±MÛÆ’!(-IÏ,§X›IúÜ–¿N‰Iu𲤋Î…H›½[ñS‰‹•¶ž&•“Ÿu)dgPŽ€¨{/™uêk“ íÝޤØñYì÷¯±ù\é<ûý’ÉgsyÒ Þ»9F‡•³¾Þ4áHâ÷^«Ÿ,í9¼ƒ‘6§(újðê-±„47éÒ »wµ½ Ëù§Æß÷ùël67låFóm¾íÍLÔ¹¹ÉéQ (\ñ2yé÷b íW{æwáˬäoq$à%ŤeH‡”/{õ8=YÏÄÊŽ–²@€¥„ÞTèM¥2ž Ggsû^âŸß=Ü Î‹ c3¯Ñþkæ÷ù¼ÖÛr@³ñ>vƒ5÷ÛÀc!yeì’BbÔyÏŠqÞ#½Š9¯þ#°o÷¶:Ñþ÷ŸƒgÓ˜Í~ø¡ç¹GA×ãÃN_?—_À‘n¹Óhÿ)s{Þ^ûíg©$øXçA7äJrsær·™k-šM=9o³GÚÂ^Û·†æ—J–‹»à®qŢÛ>=ë7ôÚídiBLÚ¸Jkåä#ów÷·¨`˜Œ?ô“÷XÑý€.­ûëP‘hËtcá‡za@¯iþ&•†0d8u>õÔúàš+;OÄÞOH»}9ªoÒ±Ÿ÷Î}¾Æk—÷:\J2Ã&¶ ›3~^âAgmyÅ%׫}'Ü O)—^<üÛQý”±£ÓœS3¾w]>P[®½»ªÿž -ߊÌ|r'æ !æ^]\f-î4i Üé¹ôÞ ¯nÿ>)«ø\ΖÁË·ˆ&tú\z~¼~ƒö†‚ J½©Ð›JQ-{w_Ó»» KRô½ºí î¶·òóÆ[Swn­²´º‰u¿)µoW«Ó˜¨œ1Õͱ[²fMµëx¾•4¸Æe8 è³c@ŸÚ_^LÍÈnÒÚi“ÖV™qh“àP5[—ùPŒ½—<¶–×V×ïúõ(á?wµfšíGÆ”Ž¬“MA#Âß@P"èM…ÞT” zS¡7@cC€å‚ÞTèMÐÈ @‰ 7zS4>hP"èM%‚ÞT @Ðr@€4€ ä€ h9 @È@ÐðA¼´‡³üþÜþDgØß™f&ï Û?qõºÇ+º³ïÆ‘_F™ûê×þ»ìž~;ÕõÐPè:³sgxcï4YÓ¦µkì]¥ã;:2Ü5蕦oGKù+f?qõúAuék­ûþStÏÕ?žœ“BXÏVnI¿©™Fãì(h¨š‰§ûÿ<kõº¦¦í5¢‹wí*5ÌTËNN–$%•”ë/Ó¹Íì‘àÃP:dÕÔzSUÆÏ‹ [6ûĶœ^ôk©ÞØ»Pf_m[í5-mmÉTqQ  j‚Ò £&×›ªvú‹ -:˜R&|Т±÷*È¿ügË~á’îÓý4Ëc/Ýùc×ÝÓAé»Wݯ+­Låﻲ~wÔ9…|aRÔiѾը¯ýæ¶-QòâkÏÍ;žq+l›é8hØå]EKüÔûýfDDgIÐvízùf—Ã~[y«€-yªÍ¨øÈžƒ+7hëÅuû݉),>¤1ÍíM[µqþjýбö4Ržò³_« Øñ·6m¿éNò«<žhúF[ùºW@_S­7Ý‹Jnî³év7Oò€ÑæRætïäè¿öÜ=~>64®”O˜ö]Û/Þ<|ŠóSZ.óËYå’)ô"„š!@Ȩiö¦”e_\w|ö²‡¯jè ­€ôû~•Î}eÆJÿ? IY~Ôé ;V^ü'ž/™ÛìÍb‚‚W«l\z‡M,]çnÚÕ–û jݲ°•·ï휺§­•–Óö§¿ÿxtkóq1ÂË$j§Ï_Ýðµ}—¨–#'?9)çÜ»Á[oÿåv€‘pfç›k—–¼šÛjõ¦ä*{V–ºÁÕü[]Ÿ®¿ru1$y Iûþ=x:ÇnÎ Q€fZ/­au"ãÀ¤M_Í%t‹Ñ‹Æ¬no¤Ã)Œþ7dý¶›ó/ÜÜ2zâµý>NLÑ‚Z]¿HNêzpÖïÓÏ–Ö‹%=EE0 õLuÔu(¥‚òW7oMíœËˆ1Áæã#4+-Mr¡bÒÎÖùj„ «¦×›Š—9¡óáp{)}÷yÁÏ®ëvg5ö>AUt s#ºx*qÓ.‹¯æ~5»£µ³Ö«©ŸíË•,À/¾4s«(=yŠ˜<Ì\"û{ôwã·t?ùàÞ/ý›_«§F¨ûM0‹Ù™AøÁ7O%øÌq|¿ ò‹‚¶Fk¹-aø.ÐèÕ„~òáà oqˆUÀ+£;J›>¸ëÀvÚÿÞ‚Õ¯.Œüå¡K~¥gšýšˆ¹ßµ‘¶ê;Ô{òcíúÜHøû€Ÿ¹éƒMŽº¢ ;Š–O+ " ÐÂ8½rνŒõéñf²O¬} Ÿ”Dÿ²/cÌÛøˆóâj2K4¡7r²µ"ý>Š@vM«7!T§uk-õÅ)ª45Ð ÏúûC¿±—T²¬˜õßRšhUN\ð¢Cŧ\ hþ¶ –bÒ³Çh³û;28«ï'ŒìÕ\ø¯m÷íl‹KÒIÞ¼#%`ý{cMp#6^åšøõ6®µ,°Nà _ùE+íwÏRô—üÜ%Ò¬ö DnbØÜ¢Ë5³)#g´©z)F½n:ä ëÕ–g/œéP©RY$=‹–fX=¬ë¡½7yäÅõ´â- k}íj•$í;ž/üŸÖ©ÿ|/…ù…4€ê¢0u,™½ð‘èž«_Mð“/ß‹Mèvíj@¯¸ÓØÇžìÈ $öÉ“â^ÍEãÓœ¾èÛsùÞkl’´çê­¥SüôßfeîÓýÿ…󕳚iÉðò¾¨‰ :*4ǽ¯ÑÛÐ[Oغöµù)nßnÏðfUùå(Z¾ãÕÏİIÂî ¹³k+T=³6¦äf—”òˆáÇü¼Ãõ×™?…—ôæëötÂøP+”PèMEª7(vÜLñ„–…ËåW˜¥¦¯'Œ“<Â)z-ü$è‹ …š…Û÷cµ®(!ù‘«Ïè5A_8K·íÊTóðE Y²z‹^–äJ ɺۿ#wõ¦Aþfºr$WVlPºxBßݦêËQ´œìlHL¨:”PèMEª7(>+5Mri–ö½ËÌï?°›÷f’¢Ñi^7ÇãïÆÚ»qcü[ˆ®y7¯NgÝía%SVsürÌœÝë7=#‚á „/Ôµðë=é«NCSÕ}(¨tº´- ›Å^#×[ªåg?]Ôó÷mñŸž¿{œ1Ú>ƒŒ @I¡7©¯ÞT \(4Mi©Q3uµëÐ\†_Ôô‡ßv~¿ð’`ÝÓe'Ýt_…oü×fM·Ïä~‚ªg=aãìñ¿>¼yôpèÞã Ù„¤œ;ÕcŒvìÛ"•®-ͪ¼r®@ø*Íç³9’ jÂ`Ö_k$Îë‡ßôܶýU³…WgýÒKuÏ ;hhЛJª.zS²¡ª[ˆ~l`Rò2›Gd ЄbÐÍï«fáëIÑ?§“[tØû_½åÁÏM赯[e[ ]AÝ<u]¾:ê›>;wÅ‘Ò+_´_ÚêÃ¥UMÃÎFD°Em4 øÄªò>sór$ ý5m åiZ-;Aiìñݦ|¶=bÒ´Ö ArA€€¦½©>µ7(!º}'còO*!w ¸j¹Ê’Ò°ž>ßný¬Â_ÿ{˜÷Álƒ‘ÿh!{e/?'úu¹£µómò¤0íÛ®ßå}²ÇÝ\’ÿð5—Ô ‰z+? òO"!ù’¹Ä¥rÚ¢ ’Î.~²\ÄÊIsëÂ@¿ á.}/õ3Go44!èMªHÍÖ¿­ã‚Ô8Bnº5e¸—L‘“ÚlLßÁ‹¶Ÿ-!Ï×~N çÏsª¹ÝòûX!ó~ÿcÊ—FéTŒŸ 3cBr ÃX»æTJµìçëCM á³îO*òsÖ©8“_|ó@¼èS¡Ö|j?ýº.òܤ“GzŒ .46òH×Ö²6ùx44!èM*‰ÑÊwåÀ«cϳHBÐÀñF÷tñ4x/¿òyj•;µS[-œ¤v«èî!ä³^mäl¾Á+ ÚþrHwÇw—‰¼”ÿ¢_ÿ×sÒ²–­ÑlÚoœä½.3kÿÉ­ßÌ_ìöv+‚ì ³s΋>ÎóF³­Ûêa^Ò_{ÚMˆÌ2m½¸;¹uàæ­j—¢j´êåk†šiø hhBЛ š&‡-i”Ãg±„WQUÊšþ¨½Ó#ûü¶.Š—qæh»3ç\:Û9›3¨lVNz^b|®öÌï—YUb‚ÑnVO·­§ºßB/û$^ák—I†Dç²K¹ƒƒ@p{쬩ž¾.zšœ‚è«!k~{% :#¶ïñæºñƒ«SVÎ>øzÓ„#‰ß{­~²´çðFÚœ¢è«Á«·ÄfÒlܤK+ì*V¦s8ÒƒÀæ¼ø\ޤ¹Ë©¥ñ7þÒQÝÌè_çDx1­I­=|Í0&|44!èMMNÁ‡;¿/y”º°ÕŒ¥†ÆgN»¸Ü¶â¸1Tã–kï®ê¿'hËÁÇ·"3ŸÜ‰yBˆ†¹…W—Y‹;MX5=‹Ð½ûáµ°¯nÕOKIð±n£BÂSYÒDZÿ´Ñ8«kÛ|æ©oiÇð\8ú;ë˜Û÷ÿüîáqDf›yö_3¿Ïçí´ÔjY]üñb˜Œ?ô“÷Ø›¶Ý»´î¯CE¢ðK70~ˆôšæo¢ùær²,ò\¯¾—B2%é8õ»3uõÞ÷ßÄÁ¹ûÆnþæTf‘d¹Ç´‚úmœsf†)Æp„z… M zSAS£×ãóþ'Ó¢êú]¿%ü'ÇÖ©ºc·ŽùÀL­Ncî¿þÐLbÙ»ûšÞÝkØvÍ«KQNúìЧæ¥4Ú ÎTÝ£I'WNªå5*bt?ºEpTŽª… M zS@½C€V2nqqIùÛ¡¹(jZÚÚh ðzS@}C€V2ܬ :Ÿú*·Tœ¢Í¦/ÿq²9N#¨ô¦©‹ÞTð¹™ù¯_¦¦–‹g?|˜¡i§gnÌTÇ_U y)ºÅ˜/Œ!Ü„K+FœÍh¸×åÜ:¶eñÍ4ƒÏÆnû²KM# (æöåÜ™Àm+—>.ªi ]WßØÎÆ¾]ëvý;¸4c"d4ô¦"uÝ›Šs¹{—s¡¹ü÷ž}<¦}°d’aé¶/üë±r´hú A¼Üˆí7_ ¿ûÓ£ÎIò^äPÇÝ›ë{ûò¡êùÏ\ïÇ+Ï|yuÆú ¢§,FŽìæ þ´£>¿õÛÕrBXÇ÷ì€   •…Bmè¨FÓiÖµC³OÚ„ ôÞù‹O8Þõ´ýGaZ¶7#W…“‚Ìô".ÑÅÈýM´r¢ÐèÊ7,'ýæoa%„hÔ¾¨ÒðÞô½ÒÒc*ß)€€­ðe™Ñ'¯Þ|÷"O<4E]ßÐØL-óËórãCöݹþ41­\ <ÃföÚuا“h¤VÔá_fÜÊ`WXAËÔ}Ö¬©#LEeŸ±lËÑ«©EÉ<†åˆÙó‡?ß>ëâ‹lÉè[Äjî/KÇW5« ôʹSOãÓ³R Ëy¢§Ô-»téÐÕÉP¼ ;#ôný÷èešèQöÞ•3öŠW5íõý™‘–I—¶Ô´}ñA(ÏŠ9ýïÍÀè¸ç٢ѿUÓÒÆÑÇ«ëÿº¸Ú0ÞŒNÀzþ㢠—J%hgmØè{çnð¥ðG÷^f ˆ¦±³¿ÿ¨o:ÛhÕÕ˜¬ôÈ,É”e¯zн©T…BAûoh2 79tÿ×ûï§¢Ù¬ÃdWga—ç彿ü,“[yqAYôå­gãʉ~Ç^£g:«—$ÿtþòÅ!ÑkMì©Ïøì+î ¸ÿí’Ý!ÂŒLi¾dÕü¡†ïÒ*UßsÅOžËK.X¼í¦ÕÿÎ-è"¼Êq~`?Vô‰Ÿ¾ø7¯š}äå‡Þ¼s£¼™_·ÁÓ­-5Y/nn:qúØ“à×ßý_+] ¡2MÛuèmÃü{uH¾0±w9®¿èËM3S:¡;õ«qûC;ÿï†Ú™3y¹©OÏÝ8yòÑÉkí~œûÅ SÉ8`ÎËV¯q÷è7GnÔ?û%'fÓµŒu˜ áÅC™ 4ûùéCëÒéË7yÔAØ”?¹vúªxØ\¿ÏGa4î€oÓZ!s@Ó€’ M ¾ó˜ $îô qz6ðšzx²§é» ËM(=_ihA~ôÑoEéY«OÀ’Ÿ?“thkÓµ5ïÇ?‚òîþx°µûL/c*¡ê¹Mî¨r«âODfìeú~1ä>¹Âbøúµ5÷ŠjŒPíþ7s¥×›.tv&9/&ß,̸}&¨o‹FTš^s_þëœ „´†}Kn•:~pû‚Ò—ÿÌjëaKvøYIGÂhýY¿Në~Ù|"'|Å=ÛFzˆGb¦2 Z6·Ð!ÂMôœm›ÛÊJS|Ï9çuÄá¯þ Í$¬KÁ‰í8ÈUôì‚‚‚lɾ>»¼(55þVpБè\B3ë=tââžÍµ14Ôaæ@òe‡2 M´ââeýûZªp‚æ2oäg¦µväež;&ºÃ„…_€Û»á ¨:-Ç¶Ó ºUÈŠ ÊñgB ýÚ³‡Ã­^òüÚ­]G´ªxÛ5~ε«±lÝ.[É– ÕtÛuÜÍE§Â2šµ0#7 Éz”ÃaTí]+dÃϽtâßá„nç9ݬ*Ž#GÕj9môg·E•f_ÛÚc_wãJ\ËÈÜBóÍ›)t+Ïa³n†ýðœO2Ÿ%°ä ÐÜØ¥?~WÍ󺎃{uèa…ô õ”J/4IÐ ‹›óàŸ$ÑÝÙ×[†áÑxyÑ—Eq›9;™¾—%éVÍÉ-aœM M+g"Ú˜nÚñ‹–~|Ê%¹Á‡ž÷ûÙõí]Î;=ôh"±ÜÃEÆÜK5èâ׳Òsjt5áó Íÿ¤?›¼¼Gg^ж éø™cåý¡è9vð E…pÉ“;Ò»ö°ªù Q4l­tÈó"`°ùDKžaL(ͦ~9 Õ›!6ø\VaanRJ|胇gOÇ=MÌ\û/ßÏÇŸ'¨ÈР¤Pn¡©Â¾Â*}“ ž°r¶”¥Ó+óy²xB×@ƒÂçó*Ì¢ijO4WGóËùDS”©z]úzé= - ¥×}ÓÊûM 7ëé[ÉT§;šÑ«¼DÃ+K{òJψ¹8û—ô_–|ég€‘ ¡~ CƒÒA‰…& ZQñòÒó%#¤˜êÈe¥ùâþläÕ?Ë:ýórßVS´{4 ÝI¸Ï¯\Êj÷…M¼•G µ<&t“ãn&ܬ¸°“w"BãS’óKJ8šs@=A†%‚² M´¢â³Ê$CÉQ™ê²äB—#”ÃÖoú²ÏªmóAÕ4ÖCiCý÷þÇ'©_O3ÆI9¯Þ(×á×BWÆ(({tnÓÔK‰\¢çã?xš[3+&JJb÷Œ;ôR¶MÔ´uKú¦h4j5{DQ{3ö2·ŒÛH¨)Úž]Û脇RúøÖ£2÷.š³# ¡A) ”B“‡­¨(j4Iæ³dІººôdÒu-]íMd¦jÚ¶oÏ“¿_-#Y¡A÷NëÂ̾zõ)×jØ(Yo¨ÇN½ºL”ž‰óðyú¼kõQ¬§.ný‰(ê é&¹QÝyå -à•K›ªÐ5éVñK×17 D  7/¥DÚF ¾ Cƒ‚CùU€­¨Ôt ¥5™9%\¢YÛ™¢hèé1 ).ŸUÌ%²hBÑl1Ñ×èjP)p 2ÏË>øX²š×¤Ö²’ÌËx%îèhÖ³Q=´™¦j0I¢ðMç—ñH•f%ü’ôñ„ºE÷ßxCÀ-—Þw†Ðµ/ǃ A†……’ *ZQQt-l Ib.!ɱ)%=Môj fêÆÍ-Ƀ—„dǽÊáÛ[Ê”'éN]{»{B¯^¿dúZÓs±›žÌQ”_”'¹ïÓY/ùUÃÂÕ½ÛÊ)L In-I-á“Ú  H›`ð92mGÕo=¤9åI¼ ,.âY¹kÛ÷ÆÁä?}(jÂAu÷m]û8Ùõ„—}õÚsIcS¯NN–ãAõ CƒBAi•‚­¸˜Ž“z[]>ûšð_¬ûó‚íôíÞö p K«ÜÇ›f>lÛáÝJIÎÁ?ö™Ïœ0¢Ùû­q|¥JW<Šv“‡×³DlÆu±’'ÒLì-5HfÉ>ûo̘ îâ/`åÅE¤WiMÑÐÓ7ŒÎ½ÿ<—c+Û0yTÿ‘½­J* Ùt½ÇŸ}­ßFh~ñ“ílj1ë=·½a£äg'óÚñ?ÖŠGª&–ökVe¤;€ú„ åT ´£9ô Xš°æç‡ÅœWÜpp²·ÕVc—ä%&¿Î¦Hjé«„´\#kCQÓ[ªI»‰ë“6ÏJæF­]µÓÂÑÕLWƒÂ+-)ÈÌÉJgtß³d c•S®Ñ¬ûXëë¿¥uWÿÊ·ó{CÀ+çHúëñËÙ i^Õvì3ÄèÁÑ’º­oŒe+#zYAÆË\Š[;[]’_H8™I)öÍÍQ£‡Ôç‘|òôÄŠQQ-íìœbý/¿™ÜE‹ò¡í‹ÚiÛþmRþ×ûîÅþóëiýƵµ·`ðrScÏ^»WHˆQûå³¹2*î)Orhx≠ï‡Ïå ÞLÔzðì’܄ĴBÉ#NÆõ›ÿ%¾ü‡]œ‘ö*ìaì+ñØZ½WNâƒBÃC††F‡*Z¡ÑL‡¬p¾¸ÿVĽ„œ—/¢_R4­Z÷û|âPí Ñ›ÂŠHÑå}?_>î½ë×Im¢êäöÃoü×wo?OJN‹ IÊaoçܱGßA>nUÓ³ˆši×»ýüZVM€¬øuvOÌ/•>NÛ¶|æv¦¡{÷€?†4c0›Ïž?]ãð©ãÑÅ…©±îàuâêÍ+ƒV†•‹þL«iZÚ¸òë2®‹«µú›æ$íØôÛÞø"I€~yêGŸË–ý¿˜û£»fzÈîG&KÊ&Ék—Ì:Øz䶯»ÙV{@øùç7-Yþ¼búéÓW^ŒÊ02±ëÔÖ¥[‡N}[3ž¡± CC#BÙÕ„­è¨Z.>Ã×ú ¯2còõ“«[fâÜu®ðŸ¯A1òšâõ™Œæ ¾_³àÃ+Ó Ý§ÏrŸ^ùév»v´«²¬š™ÇÈ#+=[óö%{¨aê6ánj^ŠnðÝú€êæXúL;ãSËk¼CÕ8ï2/Ðø¡¡Q ÔÊB€h ¡¡¡¼*C€h"¡¡Á ¤ŠC€h:¡¡ Œ @4)ÈÐP¯Pº4@Óƒ õå @  B††:‡ð4@Ó„ ue  "h€& êJ@%ÐM24|"”€ª š8dhøh(9ÕB€hú¡á# Ì|4€J@†¹ ´Ô@U CƒŒPNj†  B¡¡V(!µB€P-ÈÐP” Y @¨dh¨J€Œ T24T‚ò ;h… o¡$È@u!CAz4€JC†Vq8û@Õ!C«,œw€ƒ ÈЪgà£!@×™iÓÚ5ö.|4¼ƒ Ýäáü|:hx2t†3 P'  2dè& ç ® @@5¡›œM€:„ ÕC†n2pê4|2t€3Pç  &ÈÐJ ç > @@-¡•Î@=A€€Ú!C+œ/€úƒ 2A†V"8Sõ d… ­pŽê4ÈZÁáì4h2´ÂÂyhÐ 7dh„3Ð` àc C+œ €†„  ZAà,40høxÈÐÇ á!@À'A†nD8ò>2t£À1h,ÐP¡Ž6@#B€€º Ý`pœ4Ôdè€# Ðè  .!C×+[E€ u ºžà¨(h¨{ÈÐuÇ@q @@½@†®C8’ ê 2tÀ1P4ÐP¡?Ž€B€€ú… ýÑpÜ4Ô;dè€# °  ! CËÇ @‘!@@A†–Ž€‚C€€†ƒ ]+Ň º82J2tµpL”44dèJp4”44dè·p” 44dh‚ô  „  1©x†Vå÷ ¼  ‘©l†VÍw Ð @@ãSÁ ­jï )A€…Pm†>#™ÕH;Uª ÊHÏJ E¥ -IÏJMò*Åe¤ge‡ äm†®˜ž›@â|ûšÀ{hP,•Ò³òªô.š@s@€ÅRmzFÅ-(hP M£î™|øàJ  @€EQszn2ѳɼ•… ¡ÉÔ=Þ 24€RC€…ðvŠ–i¹³ ¼‡ D–­àjØyDg€¦N 1ZI+¡•qŸàC @A)cmtÕ½Ethz @¡UÑÊR ­; ”€RÔF¿Ý=Dg€¦ ”ÆÛ­°•Њ¹WP· ”›‚WÊÖÅ|㊹WŸW• @À{„ß” [·"ìlì]€&‹B™ÖØ» p ä€ u¡<}ÿê§åÒG Ïy_5ÓUªæ vYV›÷æ!…Î41f¨}ÄvX%¹Ü·?âPÔ5MèÔºÙGPÐPhZ-\ SBbüö N=M¾šÒL÷#âgãaÅ…~;ívptzR>_ô¸ÅàØGýZªË½âÈkSçDFƦ¿.§hÏÏ“Ã|­•êP@Í  .Ðt:ŽêÖq }öÉ[^7öÎ|¦K#·{vú/n?-}öñÛÑé8èÜÝA¤,ñ[§Uõv(8ÿä±:G¦eÛŒŠìé€/|€ºƒÏ€’Òî7§‡¯…ººZ•Û–g^v+Š#šÖke¢‡úo€:… •a å ÛjþŠþݵ«Îà>ß´~¡8=­Ö¿ohm¤TÑ4€RR³¶±bTó<;îö䅯Ľ!é½¶ŒkŒu ꕪ¬w¡PÔ” pR¨ê “Öfô*s8Ù{¿<,®~Vï:j×#|ÓÔ9|¬ ¨©Ñ”5@«1«¦R…Ck³ü§´jžç'<4ç&W¼HóuúØ+Á{P>ÐP 4ƒV ¬Ü ­×í{t'¦P4|3inoÚªóW뇎µ<úù7ÿÏÓ$íhM|ƒ>÷ѯôôÚ°ç.ÅK|Ö3÷õÑU[áRÔqg./Þ~ïrpjz™ð1ÃÚÝiÀÄž‹Z5cŠ’uáå­¦ý³¤K;ŸÌšÓ3#æ¯=¡Ÿ‹ +åS5}ÛN_>tVW»(òBØáã¯Ü|“Î!4·AÝÖüÞ×ß²JÏ8;>ðÖ¦í÷/ÝI~•'jÂ@Ó7òèÜzÌ×½úšjU ôVÜ¥›¶Ý»x'%Y<äUSÛÖÁ€ö¼âBœO<Õãﻲ~wÔ9…|B:-Ú·õµßüÑÖºŸPÿÍM¾ðM¬x‡(^«'8á[ ^࣠’ÊR7ø¯š‹£ëÓõ—C®.†$/!)pß¿OçØÍ4Öžá¶t^êâü½> §Ü{o=FËžãz ²#{˜í¼Áÿàæ·öûF¸º™{ù›QŠócB_Å?ˆÞñ zמž¯ð7¥êö‘›’ô÷‚ß'-"‚ÄŸz.ñ°P¸"Nm•_w3xn·g!Smž‰z\,Ú&Mò•Å-ztú|ßX~TÄ  /ÉÊ<0iÓGs Ýbô¢1«Ûép £ÿ Y¿íæü 7·Œžxm¿³ºå Óóó~ ý¬­µIYaIfJÊñ_“ãØo—£⡨æà¼Z5`ãÒ;lbé:wóЮ´ÜQë–…­¼}oà´Ð=m­>îË™—|Æ‘Àñ´Çà}3ÌäÃd‚ ÕC%t“ÆO>|xá-±êxetGi­©û˜±l§ýï–¢ª}èF|º0èÖø îc^†w·{û%Ãʽ°hëàͯùÑ׆ýÏêéåN¶4Ц••w+MBŠQw›1õD'K¦p n^òÁ™[§É'$ûô=='ut·p0gÒ)‚ò¤˜l]ÿX@b¯oóû«Û›>t‚òÐ%¿‰Ò0Í~MÄÜïÚH£cߡޓ‡k×çFÂßüÌMlr”ÞQP~gáqzÖ{|éÁ‘úï¾ÙéEoWúSE%üâK3·ŠÒ³‘שˆÉÃÌÅÎý=ú»ñ[ ºŸ|pï—þÍ/Œýˆqçøi§M?/©Ó·\~ —+³–à£!@¨ Ö£Ó îì?ÛΕ>ͼWoû7·ñ£0m[ÿ¸¥ÃÞwsIiTx§›©d+Üݹ³„fSFÎhS±â•bÔkàæ¡¡Cΰ^m9qpö™¢ÀʉþfK¶h¿úŒÚ0L¿!¿ 9qÁ‹‰jÔ] hþ¶¹ŤgÑf÷wdpWßOÙ«¹œûÄÏ|øí´¨ñtË%“满í3@=B€€B%t&à‹Ú¢£BsÜû¾%˜ÞzÚÀÖõô’t“±sœ|þœGòΞÎXãe]ÝlRmSO+rî!ÅÅŤ_!Ò3mm,‰0@“Ò\ö›vü” ·ïŠÊ)Ãw˜•VåmiùŽwT?Ã& »/äÌ6¦^™;‘¢y´®S]Ìô&#üäË÷¢Eº]»¼r™Æ>ödG!±Ož÷j®/×V‹.Í=t4h•Faèz êæ1¨ëòÕQßôÙ¹+Ž”^ <ø¢ýÒVuÿÁÉÌÈO¶0ҩí¦ag£N"Ø¢6|R¥ú–›—#ɘš6†¢&M[ISRœË# Ø‹ªna!LÐÂ]-y™Í#Ÿ E¯VLü/QÕ^•2Oïéw¨õ™Þ6 Ú] )C€€Ú¡ºÉáçD¿.w´¶z×\–´o»~—÷IÑqù_s‰8@S¤£&óùupòùáñâ¾{jžýÌët˜õV~äa’ÌÌ%.•¾ÚE/’ÅS.~¢1:¨š.muHx!YוÏi^Í= «ª£CA·ïdLþI%¤àNX·ƒñ'| ³ÂWíÛðR<©ßvׯÖ&Õ§qÖã£2ÜuxÅ ò @&ÈÐM +dÞïLùáÒ¨÷*‚f&Ƅ䆱¶øi MW4ŸOò²RJD¦œùaœìÓ»Å9VÇmFº'‚jÙÏׇšÂgÝ9žTäç¬Sq&¿øæxQµæSûé‹+aÕ[r1Ø–G8A›"“ú6«õ˰Î…š­[Ç©q„<Üt3jÊp¯Ê£îɪ4*pâjI×Aæàc›} ³²ÿ»W®îª…®…uä€ ÝtðŠ‚6„¿ÒÝñÝ ¼”ÿ¢_ÿ×sÒRÒéŽîØÅ”\I'¼ç?ýÛm­‹¹äi>'ýYz¶<#EûÉÎÃKÑÐË¿ïg\ÇiŽfÓ~ã¼ ïu™YûOnýfþb··ïJtvÎyQÇ=çy£ÆÙJS¦ngÿym–>&ÜÛÇF¯0=ûc ³7߇Vi^yÕW¨³CÁhå»ràÕ±çY$!hàx£‹{ºx¼—}ù<U­¶ãS–²~Â¥Xñ¤V¿q[‡ðÎ…üìÄk/‰nO&¾ïê>P +I%4EüK6b´òîsu5ÕÓ×EO“S}5dÍo¯DgÄÖá=¤£pÐÇûûý²ÿJ)y¾y‹Å6mK† ´$=³œbm&¹ƒaù딘T/Kº4Rˆtüæa+~*q±ÒÖÓ¤ròs£.…ì Êuï%³N}mòæ"GÒ‰Ïb¿_¢ø\éPn<ûý~~|6—'ཛCatX9ûàëMŽ$~ïµúÉÒžÃ;isŠ¢¯¯Þ›AH³q“.­°{WÛ˰œjü}Ÿ¿ÎfsÃVn4ßfáÛÞÌD››œõ¸€Â/“—~/¦ÐÎCW\w+ÿ¡àq¥{.éªø6áªéÚ;=²Ïoë¢xg޶;sÎ¥³³9ƒÊfå¤ç%ÆçjÏü.|™U w™ÝåÈÑÑ’i­nØWÿ ©tn¹lvanQzJ΋ˆ˜0Bš›k ý3@B€9 77 Ï…£¿³Ž¹}/ñÏïnçE†±™×hÿ5óû|ÞNëmØ¢Ùx»Ášûmà±¼2vI!1ê<ÈgÅ8ï‘^E‹œWÿ‘FØ·{[hÿûÏÁ3ÅÍy™Í~ø¡ç¹GA×ãÃN_?—_À‘n¼Óhÿ)s{Þ^[òÅS|¬ó r%%*s¹Û̵ͦžœ·Ù#ma¯í[CóK%{wÁ]ãŠE;ß7F·}zÖoèµÛÉ,Éœ¤«´öXN>2w?q‹ †ÉøC?y½¹aÛ½Këþ:T$Ú2ÝÀØcP·…½¦ù›h¾_«Ëpê|ê©õÁ5Wvžˆ½ŸvûrUߤc?ïû|×.ïu¸”d†Ml6gü¼ÄƒÎÚòŠK®WûN¸žR.ý´<üÛQý”±£ÓœS3¾ßa›jÜríÝUý÷m9øøVdæ“;1OÑ0·ðêâ2kq§IkNÏDÔ*%µèM­wÉÅŸ]¬í|ëZ  .á ‚¨–½»¯éÝ]†%)ú^ÝöwÛ[ùyã­©;·V·‚º‰u¿)µlW«Ó˜¨œ1Õͱ[²fMµëx¾•4¸Æe8 è³c@ŸZ^û 5#»Ik§MZ[eÆ¡M‚CÕl]æC1ö^òØZ^[]¿ë×£„ÿdÜÕ÷ÑZý°BðÃG­ u@Ðr@€4€ ä€ h9 @È@Ðr@€4€ ä€ hhh¼´‡³üþÜþDgØß™fÆhØÕë?/*lÙìÛrz=~Я¥zcïÔ5hh`ü¤ç¶?æ’{zÙµ¨ñã¼5rõúÅN±cá±ESÊ„Z4öÞ@ý@€€F5þ¬¹5II!DÛÓÉVî äO\½¾ʲ/®;>{ÙÃW5Jcï Ô+hhh:¾£#Ã]ƒ^iúöw´TkèÕë/-rBçÃáöSþúîó>‚Ÿ]×íÎjì}€zƒ OÍÄÓýžµzÝ£8­‹Xk©/Žó¥ñ¨hÚ >…©cÉl쀆‚  rò/ÿÙ²_x†ä†{`Æt?ÍòØKwþØu÷tPºÃîU·Æëþ¿½{Ó±N8~ÏŒ1£qL˜Pä"”D#)TtPÛa³­R¡­Ôt°½µ¶]Et°¥¶[o½³%i³œ*‡´d[„DQÎÄœžÙ£Â8Ìïmß·ƒÏçšër?Ï}ôüs¯ßu?¿'¾xmîÆ©£ßöèœI³ÖlŒEQR…FÇ7î~eÇ=jm±eá•ÇÞ;ê㼎\¿ëy¯?—Q¿hê‰ØÊçžèxõ¬y_îØ |ӌ׳N~û¼ûOÞSüV³î‹gŸzøÎ÷¢ìµG¾6tô‡SæoÜVø²LrºÕ7kØgع½ê–‰¶}öûŽûܽPAÎâ “G<ôþø)Ë—¬Ë/:FjÕmŽìye‡¾«¥|38¼%ktív3Ö¿Hj6~õU­–ÏûËc3žýë‚鋾ŽEÉu3Ž¿ùÞn—µHŽÿO|æÀω€8à¤vîóEN7®ÜéÏ£­ëç¼øê¨Á¯½¼8V¼ö°o6+ذäg 4%':¤éu÷ž›qx™µÌzû»ƒßyï‰ WL옚) úç·==²Þ/æo¢ø“.ZòvÛC¿»±ÄrÁ¯þqAï5ãFÕ9{î‘ÝùNߪ…+ÛdÝ=h˒뱼ĕm]yO§? œœ[±uÆO6mR%Z·ôÓ £ÿ6öÅ5u®íZÐɵíc÷BÙ«ÇôqéÓk£Äô7õr|Õ ¹çýmÚ°³¾šu__¾õDëÛ‡ŠS2.]þiÆØþ\õÊ×QöÂ[Ûß4gÖ†ä*•ªU([!îë Û–dM¾¼ÍÚ¤W_R[B»Ð¤Är5ª&n_Z6â‘ô>×õpb­†)K.?zôÚâ b›Ç÷YTÏU[¾0ëWçÕØ‘g¶8³y¬Q×÷—}ü×ê½Ú«RBŸÞåŒKªÏxU›šõÂÒÖ×Ößõk}±MGÎÛœÒ<óü*ßÝrÊ$–ÛÃý'¶ü©§2'çF5ÛOx£Ç‰å‹ß<ªg¯Ãs<±Ë†{Þ½0ù·M¿õþ¢z.S÷®Y×ÝØlÇ ÌÏmõ«sž9îô·—þϘŽ5ª}0¢~Å¢q踔ڵ[7.taN¾vR‡´Ôĸí‡ùê¹¾C{ŒYm™wçèU=oK7•3°3 p€«uË“¿¼¦nñ kåßõŸ4¨L\aEæ.šzÓ“› ßjrC—.5¾‚;øÔö=ª¿?jUî„!ï/½ C½ÂÛHù:¿þð­ŸGÑò{G}ÖwØaåv:zÞ²YÃß̫ѷãiiûýf]ö‡/.Í/õŽÇßʉ>}ìÍɃ.ë˜úm+çýó‰I3£ƒû–RŠÓÄŠ#)˜7gúš£:Wýö ‰G^ÑåÈýïûìÕwf.$µ=¯æî§‹Ki{qý²/Íω–>úêÚ¾Òö}ÿ‹¯T½Yµ(ëó¨`ó–¯ó£*?ŽÉò€  @I9‹Þ^½}!¥v¥¸¼¼ØN«R+æd~”»iÅÆX”Z4”›Þü–^)oÙ­Ÿ=dÜù.IÝœ[–=øÈê„¶—\Ú¨4·›²:½ñYôåŒ3OÌ2¢kßNÕ+”kö‚‰_l_H=ªvÉÓÅ¥4¨S;š¿8Š>~ó‹-Ò*íçhq ŃÔ¥¿à@! (!–½òóâ‰.>¿¥I¿[ö²QNþ7‹qåNº¾]ý1¯-Šòß¾{Æ¢ž=@Q°.ë­§¾H>÷Ñ5KÕÁ õÝóÚG‡ø8*X83󬙙Ó;õjÕ»ÏIç[aÿO!ço]¶"wûR¹ô=uw™Ôʩ۶­\»!?ªdPøßДP¿uÇDqÕ3_¹ôÜê{z|9!¹nÍïž$NjÒ6³Íë}¦Ä¢ù9»ý­ÊF±¯ÝóÁÆš7œ’RʉKm0ìÝß¶¸ãÅßýiþâÂ Øøù„‡_*ü«ÕµÛócN;!uŸ‡‰åmÙqÍñI{º¹Å'&îx%';'Vxý¥»&€4%Ä•9hÇoBµ¦uN¨WŠyÜR»ÝrÌÀ3fnˆÖúÏÛŸo^qÉÌá“ò›ÝÕîèƒÎ_©Ö%Ã\üÇsߘýôSÓvéWQôÙ¸Ú÷,¿àÕÖ‡î㮟X>©x)[^Aáÿa·õ±œÜâê()ÙÜÎÀ÷! (!¾lzÑÔm9Q´å“¯ò£ÒtW¹]Ç>‡Í¶,Úôò„—7:áñIs{ÑÁ‰û߷ı’*¶èÚ®E׌;†Ì¹æô‡Y}ýÆ„± Ôxï·­„ruj—få=£±!ÕÜýšóÖ­)þÙ”ƒjW y´`w€’ëž”½¼2Š6LywCÞ û™³b‡rµ®XgXÿ¥Qlñ°Þm5ö«Ê\Ø%½ôƒ½±5óVl«_«fò·ƒÇqÉuöH«çÛÏX­Ÿ»"/ÚG@GewL^^Eë?Xž5Ùý÷ 7-\ZüÓ+M:¦—fJ€½Д”ph§cêß°rQÍ‘5ç²n-K•œñ‡õì|öM½²%ú×°§þUx}ƒ}?·¼«ìi×?ð§Ë~;¾{…£;©úÁiQ´6JJ+¿ï?䌶­ã—M‹eOyöÓMVØyelsÖ˜ÅEp$Ô»üŒTÐÀ÷! ؃¤Æmwy³×_³£¥»\\õµÇN>¶ò.ýË/ˆOؽŽãÓgöN}eäú¢GwèÛ,ðñüMï™ùÉ9§ÔÿnÒüÏ&Í[Xøo¥†ç±Ÿ£•©}üðë'¶ºúË'žyÍÀ››{”‚¯&¾rí_‹¾cØðúî¿8ÔÐÀ÷" L¹9ÅSDz³c…é»ûú„Ôî_5ûôû‡ÎÉ_õÒÓǽ4®I›: k$Åçd¯ùbݲÅkË÷»qæí5“vß-é¸þ§6ù‡QbÇÌ–u÷R¼…çÞš·})/çë¼ïEï=Óô¨ý/?¶m“Jån˜÷æ´»î_RU8d·öߌfïu÷¸¤»bÄ%ÿ½ì––C>tj·ª–ÏÝ4ïÍ©Cî[°*ŠûEïñ¿«³ó`znîŽ!'w×!–—[<ùu~^né§‚ŽåmX½~Å'+WnÛþróWsç®:¨N¥iÉeFâ; pÀÙ0é©6Ý&Ï[_üjefã«UIkÓïŠ×î8tç_áŽO;âî8ó±‰÷ýÇäÙ«?š2ÿ£(*W#½åÉMúß|Rï.%ë¹HbýV7w×kVËÌÎKŽôn™úL»îÓf®ÌÞñzÁËÍʽRñÐzý^øÍÇ%›ÙãÆZóßyoÙŸoœ{ÏöDNJ«Þ²G§»ž~Ñq) ûÙ}{­'|ñ“ÿÕªWÖ=¾7~è_žÜT¿‰•ÓZtm—Ù·Ã>è›Ý:{\‡Îã§­.®ã•76ê÷Ǧ­FOúåٕ׎îuï5/¬ÞT¼ÝÏž2ñŒá×¾tuµ}ÌE=ÿõSN7}ml—wWLíyüÔâŤCšžye¯€'Â/ pÀ©Ôþ¬»°T›–M͸²{á_ÀÑã+öœ0²ç^V¦œÔóý{[rÚ)wvÊ>޽ïÝwˆKjpÖé£Î:}ß[•;¦ëÔU]÷´¦jïç÷ÞÏ9v—Ô´ó´5w~ª4Ð@@@ 4ÀO^\Ü?ô%@4ÀO[AAé§)à?@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ ~n÷C_?g?·€€ÿSh   ÀO>  ~èKàò“høÿ$  €€€h   €€€h   €€€h   €€€h   €€€h   €€€ÿÀ…7µ;žIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/yang-as-source-code.png0000664000175000017500000013451114770023131024671 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+¸ûIDATxœìÝXmð[ÐÝ! ˆ€"¢b*¨¨XØÝ¢ØØ…Š Øñ©Ø¢‚‰…€ * -Rcû#†Ô‘‰þÏž‡½Ûq{w·Ýþ÷Þ{ïÑY,¼¡ó» 4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 ;@>}š}'&&FQQQ@@€ß5 Fbb¢ªª*û¾­­­‘‘¿kT»Æ }}}·lÙ¾C¡PX,¿«¤•9ö膃 ÐH5º ׸tdd${‰ó»PwìôÌItqqqü® Ow€VSSãì¯4®½(Sä”””ø[5îM£Ñø]¨T*•ßUàIãЕÑét---~×jQPPÀïZÔÅß UTT>}úÄïZ@-´´´Øšßµ¨‹¿-@üVÐ$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ ð×Ë]ßZÕ'N¡ã1U©ü­@c† EX¹ß?½ ˆŠMJÏÎaPE%eddeåU´[·o«£(‚ŸÚê¤~yó*(8,2.)-;I—”UÒhÖ\G·…Žº”¿ëõ‰‘ùîõÛŸ#¢Ó²~PE¥eåäTµ ;uÐSÆ÷à ¯ü0ž­æ=.()¬|¶¦(ÏÿÎJ0¯ÙÎÈ’¢øÀã¡—ìÕjüÊd=œªÝý`bYYÌÚ;ò¢•…ÇW,ÛÚVwI÷Cjs‡z˜ˆ×ú¯¹AKZ´ÛZRWk¿„K}$xzÍ´àk‡=}ÁçELAµSQd›w1ïgc;zôàNªÂ¼¾Ÿ¿+/æ¡§«ÛÞc—ƒ’ªŸJXµµ±©yïVƒ˜éË!L7V‰/ÏØwÔëÂÍдꦡ+ô²3yêx+#EÁ†¬48hø{ êÌðÜqAÖƒüââûµ£6|¹±ƒOÿÌJó_n_–ž IëƒûGÖœž VêÃg¹ɾæ~#~ ½JÝ[¥¢w/82ïáíß»XÙ/nšï°É÷Ó¦„?½¸“}[,¤3ÈaÕšE#diõ_¥F¤0ù‘ëÄQ‹¯Ô¾ðrcßÝ;Ǿí\Níy1éŽLÔê33øÌÚÙÛï'Ô6%#ñ½ßþ%ì›xÛq›<¶L濾&€¿4üÍ´§ÝyAÚÝÜââ‡M£ÖY:w¯µ ••vo™ýž²p$=쿽ÃUkIŒÌï·Ü¯dT|¬à®Ç¥¨35ëþEc>[³êÖèýeëµÙ—•´wŠõ¬³‘µOú“¼°«Îc®ºy¬ x°¶­p}Ö©aÄ^œj:ôHÉc²~Guà÷af¼Þ;iðìó<ìerË òœÛýø~‡G®Æµ?€FþnZì¾ÐrÒÍÅŰ­£Vr1‘¨1Ž2Sï,³/¦´(;âÈ.åÚ\ c¼ÝoæUzøÅ.¯ðÉË[üÂñܯ…Kz®1¬·´Z˜twyŸ¾[_3¸¤¨ÛŽÒ¯§I¦ªò├´ï ññ1a¯îß¼qã†ßãÏYeSç½y—Oü£:7ÄÕzx…ô,f0dæ´Ñƒzvn££*)Àþl±ò³’cÃß½zðèŽÏ%ï'‘•?ðçc&ß_ѧ·s`…oŠv¯Ñ£‡õïiÜ®¥¶º‚¸+?39!.öÛÇ·}¼½¯\Œg–þ{ð‹oy,ãÚ÷× B€†¿]cì¡}ôÇ^çÄ¿/®£¬ƒvt“ªöW™r{јƒ±¥Eyû£VJµöW(ørfç“ÒöE)“ž"OîÆß=p8xÁÖv¿’5?lYt~Êuû&õÑi‚•´me…ô¬h±l·ûÒ!ú’{šÐD¤•5Ù·mMMZA|yÆmýúíW>å×C5¯Â˜sóV½`–•­wÝ8:ÃHªÂ£ŠËk¶íÁ¾ ºÌ‘âwl÷Žg˜•殜÷nÖ–Ò³R§};—XéŠs¯lŠ „‚:û¦ÛÆdÐäU{3C.¹,_ºÉ;œQi†ð7A€†¿]}Ôýç[޾’Y\ŒðµÜúÝ®ÒUFhf²ß±ÿÅ—•ÆÛ1@¡ö>Ìy¡ž{ß•4&owSšÒfñûâR¤çÞ—«t%} WÕT3öqDñÝÜ›K6=¼»+oçÖ€•ñØiȲ€ò³ÅÌ6ݺ´ÄX¦öw( ÐÁ~“÷¨eïO8Žžràï֤±bD]t»]Þžl¸á¶×¬Ö"5ÿ]FÀ¼Ýæ¸fæ£Kl#ÁÊz¶fˆã£ò5-l²öÖÕ]këúO•кö²õÜÇÛí­—øþæJ!@ÿ€ÖÄnß¡ þvÓ‹‹Ñ{ì—Ø¼ßkQ962“n8Œ;Zv®ÊäãÛûÊñp à×)-èNš`¤/>³Óâ™Ï‹Ë‰g<n1í'CîH.Ewª[ÿp›CœÊÄî]ðŸÃãùÍ-€å¼Ù:qÇײ"µíêû>K;9ÄL•0»ÿEë=WTþÉ+=ÐûmYI¨ßêiµ¤çrT!‰³ÏK#”ì:akXy¹Å¢Û×WšVØê'49ÓÅ>!vy  ÿÀ_êŸü „MeèžÃ¶÷†žM-.Æ³Ðæý!ËŠá˜ùýÚüñÇ¿—Õ¦ŸØf!ËËéOvyÅ•ÚL¥+H§YÏ6Ÿ;ö~ñqÜŒ+î·¾÷µU$7UºÛªÍ=ŽM¼WÜe‚õb­“Ÿ½×@ùºèÁŒ¿¼d{y( vÜzz9©ô\JP½ßüYu®FU ³b?½ò!<*15=3—% *¥ØDK¯M‡Žêâ${®0sâB^¼xú%:1…=+º°¨¨¸´’ZSÍæ- ´d~m¤àû‡è²’VW}ÉßX¹ !ÏŸ¾ þ˜öƒA•VTÓnÕÁ¸SK¥?jAVþ÷Ч_¾ÿö==‡%"§ÙÙξ—jõ ˜•Ÿþúy`pxd\JfSP\RZAC·•a»vºŠB¼¾1æöê}þ:´hÙdP…%Ô›·jߥs+‘z[6Ìī˶„–— ÖžYÇ{z.AS0ŸçXûdõ¹®Yy‰ÁýŸ¾ ‹MÍ%„¥”š¶lgÚ­ƒ¦Ä/õýjˆÐ!@ÿ‚¦d½ó註ƒ½8CöÆë0$øèÀòî…‰WæŽ?Y6 oÓ9'«éæñVÒ]÷K©%Šñ¬áZEB¹ßœ¾"÷¯å=˜wÓÝ;fèu’?dtõÛn4Úü¹¸”vfÁŽ¥ê|æ^Á×ÓÛnæ–U¦¹MÓãïXµÉÁ·Îž<}ÑÇï޻ΧiÞ{¢ƒÓŠÉÝUj­kaj —óº­û½ß§W;ˆzGK›‘SÌì×T¨.UfåÿàêÎdÖó¨ÌÌK®ë6ï>óê{UO+v9{Ù*ë5ìö¤^0“ö€s_}á«O.F5~`ruÛo/c¢ûùÿ¡?³ÇøêÞ¾Ùü’fw½ ¡oW´,L~öߪeë÷Ý‹æîÖMí¥=¤—•Tå)H 8éºÍãðÅ×U¾-‚Ô1·=iúäa]šTóã{nuÞñŸOHFUOË·1gÅšÖz¿~Î#êüVŸås·c^ëú?vPëº++ôÜÇån×?W:CAÚÈ~ÅÖ-s{©’ý²7Üh” áßAUèî9îöOÎXÍߟ74äø`ÅâX[˜à=gâé”Òi›9œÜPÉ†Ü 㮹û–þÜ ôœ=ˆs²E®ç+©kgŠÃëÉÎ3_Æ;êmþi³ÀuøžÁç8‰0ÌeÑ™é¾ãÔêԠĈ¾æùº¼¨;}vG¾Ž¯ÅJ:g©l{·šÜ\&'üÖîY·öïæuÕ}¸fµ¡—™úÄyØ€w«½ÄEéì¾½¸äñ"ÖhdßqÊui˧‰+ruD|’Áj)_?¢0éÁæQCWÞªáš,‰/N­rjWßͽ›òtlä7`¦=Û2¬ïÒ;•÷RXUìO0Ó_í›1rΩ°O Ì»lÍýÓÏå¾_ë'YùiV曃3lgžüTç%éÕéÕCNï°íò‰]¤eÙ0b®~Q^ÔšâЕlësmêw]ç|::¹Ï¯jƤL <±Èâü§>Ë•x­_Ã.p€Æ þ%TyK×ã“üú–œ$˜|jüì¡¡§†*Ó ã/Ížt¶´™h¾èäZÎ3"Ï{ø—žq/ÖoN_¥’ߊ´éÌa gþã´/½Ûë:gƒ!ÙfOª|¿«ÚŸ[øª¸”w{Ù†Ç6{»×¡ß3ùÉù7åÅfíµù|©4f~NÅŸgЏ‚ª¼”¤¸0‘›žøí[2×ÐoŒ÷ûm\|}¤êá >ïÚkŽ\îÇh2:JÒb4FvFzrì—è´Ú²:/èŠm ¥‰·%9=Ïo…ë³Þ»üz?ŽÂÄësLì ¯ø¨°œšš‚pÞ÷Š #ÑoYW“X߇n}þb6y÷ ¹ôn÷cEãöýeýœŸqÞsÌ­÷}úéaª”ª–ŠŒ0óGjr\lr.Q³Âï·÷²t}W1ÓeTÕ¤„ ‘1©åk6áÚ"S³dÿ]ëéX)OÎríij ®W§ƒÕªßu]á9ÊxÂå” Jªéj)K¦D‡I(^¾¹ú >iÎSýx4RÐðo¡ÊZl;1Í×b?g”ç´s“f5>ÕíáÌÉçË/õ–z­î\óHÑåò?ØXZ<ÛœëÂÝâ§VûoGtqáËᯗïêÂû¥ÄK4Ÿâ6Ý¥û>Në¸ý,$Ý”Mä„Ý )Ï7R]z5«ßTPGtî¶¶Öƒôîj¤×D’ûŒ+Fúç'—»¬Ý|õ+§ÞñÇÇÍd|~x¥ÍJö]¼¤<=Ëõ\äºaΰÎê¢~ÑQoú^½xÆó˜ÿ/ä]ñvcûËœð*Ý×ú¸Ù¬[‚«ûê‰f¿Ð#4?lïð!܉J¾Ç"·Ms‡vVãÌ”õãÛÓsnKÜ–½îN›úï}§7kàa="ŽLZ]œž•Ìg¯X0ÁÚ¼zQÛ¬è7w®ëpí”±²ž¯é[!=‹uZ1wl¿v*"å§²b‚ŸÜ¼töä±cwbˆÊrÞm·ÀæÍf:-™9¢§¾Bi¯ifvÔӳºÞáYb¾u4Ù(äÌp•ºí^ä„Ý )ÏŽ]ú4¯×îõ»®sƒ·›È•žÅ:Ìpw_>ʸdfDaú§[WÎ^|ö3«à©Ó„°ÚIÃ/p€F þ5T™žÎ'f^ïQrÁôK“†oóöRÙiý•^+;Šñ†rÞ:ø±´ d;Ó¤BPÉãµwlàôaŽóÚõdc‹*ºˆÖŒ"aâäÜûè¸[œˆ¸~ùõ±gó0´7fFxpryQ³‹¿„`¿­Í?w0i&Võ;¡Kiw·±›íØ#£M']*®{æåå»C¬Ö·þ)ú§º‘Yr_¨Çþ¾SµªˆtIö¦²o«<¾…gñ0n_5¤ÌV.ïpjÑËÒ½‘ü·‡g÷8(kv¤¶^|çþfsî£öQu“q®÷ú÷uèj¹³$‘æÜu˜æ5°®zê*/ºhMH[ºß;;§-×N&M\ÍhðT#®)³VÙm,Ù‘6Yíë½ÒLþçÚÒÄ›ö™À¾­tûø Pô§OeîÛ­#—”º¨h½ÛÏszÛŸÆ+§Ši˜NØîge½~€ÅÚgÅ]€Ó.ÌrºßçP¯ºô¼`¦‡¿ãꔬeÜŒçaVxP¿ëšqtúÊWåY×öÄó㣛rY¢IéZ:žêidi²ôq^vRmGaø°À+hø÷P¤Í6Ÿœw½»{Dq1ûéé'eϵ^{r™ï­Ä™Ïö.-hØOk/Vñy¡c¦l(:õ’ûݤ^6ä»ÌÒšØn[´©ízNRO?¿Ð5¨Ïf#R¿ëßùú[ (kÉðý»/¢eÖµÖ‰("zþ;yÿåñâîé¼â§Óâ Þ~*íQ dá`[UzæFS׫y’ ¶˜{îh@§q¸ÏýbÆ¿ºz€}㼂ló:wëÑÛ²¯v*µŒ¦óÚmõ½²H%9ø„ï&ó*û¼ÒúºÞ<ô¡åä[œ·›{{ÕŽ [—öõðx¡½ðÚ™ é¹ Œ¯ž;"ÊŠ: o]_]sObª„ž¹YŇ cÏ9n.}¾ƒó¯™ÕH“ë¶òÒ± ½ÞÅûRß­¿¼Î|\®=TεbéÊÚ²õøM©ßuý#Ðmã²ëµè.¿v¸bz.E7ZtéìÛƒ½Rªx– _8@cÅ÷Q> Hv]ïµÐÇdû犷Û䵸-ïy„•â¿ó\Y.Õ4Á R³®@3Ûò¡øº_‹³§JþGF¸õ¼í#v <Í9ªûÙÕñÔÌ›ÕI|} ¤”+@ˆÉ×:6\Þ§.Ç?ÖÐ?•®j9{ª©ÜïÿÁ¤Èt›f£püPq¬ùæw?f³‘6wFfæç”]†.,Ü¿à‚šcO)®;vý*P`¦„?÷eßNº¯ (*&cæ,^6gP ñª½³_î;Q¶F­qVÃçƒÞtŒû W}§N1êøþÀµLew€4ÉanËŒkëôzØíYY¡&³¬&= \ÑÅ=½¶Ý*M›êóöÌ©eÈmšŠõúyÚÞœ>ŒG‡ï}cOþLÑÂìd®oЏ‚D=þHÖïºÎ~µß«¬×‹Ôp×…?ï¾s¡*ôß°¾Ó©YÏk6†?  ±B€†E¢Ë¯%W;o)ï¢Iíà|bAå\=f‚¯»OVi©xøçÊÑÕ¹„fø{œ=·ùïU®Ï†5ÎÎ{^|¼6ÿÞòu‡èÁ{(aä–_~  ÑkûÏÜGVn¸[ÓÏ­ž°Ý¤†ÐìÀªÒJ™ 8Yõ듯¹ *hùf ìŠâûÙ·öÞˆíi[‡]’T-×ÝŠœxû‹ËÎÃ7Ãrª÷äØrëc[MžÜ0¸òØy¹½ËÇb ™9Œ¨¥W³ îØù]œ¦pJ‰7¯|Ê5ý¥ëÄ“$3x&w7ÿªå¹r®|ß´ÍÂùx=¥€ ãÛõe]@´ÇM0¬ýÀPóAýU7ìŒ-º_øæFè{eÒ#Íþ¦ð®~×5{n·Ëæ&g3ÛL¶ÆšÒ›™g>oô½ê¯0ΧÐX!@ÿŠ"Þiцn[l–”Ŭ7N×'sbãÛe÷Û¥£®– ÿ\ •{@h"pωOÓWé×a í‰n3·šîâ´9%Z°ßñÙbžr¦sÕŽ‘ǨçŒ#%øÖ… ×îúVV2jZkßvš²™>PÒ.ùð]³]Ã5ûQÛ l]kDb¥Ý,ß1ma×_½.§:f¼½Rz_Âȼú! ¹ª¶V%ˆâÆtûeÌô Ïå³ïyRÃx¸?ËIÏa²××#Í'n¶ß6èDÉLâ|Dzo2-{öíÝì«©©içVª¢¿1bÒ¥uºÛ±os‹ Ìœ„° çïßô¹pöÒ«DîéÒ}g÷_Öæ[W®ùñïcË Ò­ jß$ ¨é‹!%‡>bßÇåÊ Ö­Ôªií'ØæÅ¾‰*‹m:7©Ë€‰y±¯¿”5gž³ ?T`z\ƒýÉ'ù_419®oJÖ÷Ìzû¦ÔïºÎ{_>j‰Z‡fµ7ÓZëKo«¼* Á¿ÐX!@ÔInÈ‘ýeí5ÜÃ?WVq@èèã{ž­=l.QÝÔ5 ©ݶtƒïjÎE†3/;ºZníÀÓ) Íå âKI© þk*ƒªéë/eu§Òe1 ¿íë¤1#°ÊéÉËôšÔ}ô‰¨Ú§¬X‰‚Jõ¢ÊØ{Ë-¹·Ã ® žz÷4ûæQ<¢aï6vöcmͪô£¾PE”ôŒ±o—íHaóÜ[ºá>{ÿÌ‹tËÚ8é±åFVS–—¦ZötìhÇ)¤Ç¦WP¾þ‰+ðÐÂÈHûV6¤:¡ -_§¡ö)ɵOU“‚œ‚:´ È7W(ÿ¦0â¿$×òMáYý®ëŠskÊË%êé²2Q]€æ×h¬ ê"ûÕ¾£e%Ñ<ßM‹ÖÐb“—\Þäœtn§¿‹ÙÀš{,VC¨Õ7{Ëœ_º¯;žœugJS¾ÆTÉæ­äˆç¥¿‘Ï"rçhò³»bö˵ý¸Ó³€VÏQv{š´Ó×ÖPU”“ •,"FÄ®Î:Õ{.êÓvþÕ0óS›WnØéó±Rdfâ[¿ÃìÛÚéºCV{¸;öUkˆ‹ÈФ loví<¹ÃϸÒßì;öa‘øXåg@‚"¼|((‚¢åÕ/ÈmÐÐB¥óÐÀÈÊç~Sb‚uêJÃÌËÌ«}ªúG•jÞZxVzvhÄÓ¯9óšÕew·’ú]׿&ÀÓܨ¢5|îùµÀ+hòXivžN(/÷ÛçêÇógù¸û%ôY§Ž«™^ëÖ™œšõ¤øÀrÁƒký‡ÿ×Kºö_O‘æ=[RŽ<*ùN{zûkžùÏ*7Fä‰y[BKK’½]n™ßI¦ºlÆ"<G§I·µßrÕ~Crˆÿõë7ïù?ððìËOÍmùŸ.®°ô»µõ‘c[žÇúþ%tUëíû†\|±´kô¿€ïk[—ŽèKaGŸòÚñÖ‚ÇÊÿ‘_Væ)ˆ5(Š ÷›ÊίS§ЄÊžZ:|³D·a.#¢ÓSŸzÈ¿dt匿ðÜ^õršfý®ë sã´ýÖöA`pͬrõøµÀ)hÒ˜ßo»{§×>]uòo»_þ6|:/-ǕѵƹÎÝÒÅÓxûýˆÃÞE/—µ¬õߨò&à ‰G¥WóþrþòçU­ër2c}`|óÞ÷¤4KÛžðrèTÓ•MiÑiÕ?û9}‹1ì›cÑ?¦G¼~tÇïš÷¹SWß–Í"ûþâÁ+ºï0n˜xŠ\·q¦‚}K³K\p|>¡VrD‚.¥*I%Ÿ¥”ˆAÔºWSÀž®¬ ¥úsÿîUéâÚ•° rë½ ]Z]¦ìM%}N*àáMU1“&Reý âÞÅæ ”ç(²ÆÃÛþ¥=•"ÏŸÿ´¾a=ìkÖﺮ8·ÈÔ¢Imsc¤D¥Vÿ,ß8@#… @Va¬Ï¿FHæÁ³]§>OZªW·(±Ž‹· <`ç“]\z·i‰÷Ä˃jý/ºúÀñíæ;¼.)~Ü·ç¥Ã.þôâȽYÖü,Õoš¹|ñÌô/ckš Zt)ÍŽ&±oNî ÝÆZ-¹Y’¢£l¾»ÊÛªNÝhÈRÔ’%ˆø’R^V^y¬T6P%ˆ’±ÒÞ½ýÎ0«e«\R6x"¡j Rq/ˆJ`/MNûinF.“¨Y~RD-W× O¨IÛ¦T"‚óÒožÅä÷äy¸˜2‚ªm4Ê—Ì‹GßòÍ[6Ìþ]­ÿÄDàË’â׃;ž,ù¯ùsê~V¿ëZPÅ IÙܾ½üüƒ0¨%@3ß…Tךàçhœ H*øzÖãqY.ÑYýþÝšV¼´O1ãNôÒÚÞ$x¾sÛº¦*Þ²ÜÀgg䶬«‹¶¾ìi_ë hÙ9ö^:ºôJ ±ûš÷`¾~# 3£ã˺[*é«Ô¼ôX©gµŸ¯€R·E^Ç^4³:ω9/üÂr­:7ÌEü˜?R²ËK¢2\C‚Pe »ªÏJbËû‹“fiÖÜ·§0áÁå²½¢i7C銓ÓÄåÅ‚Óa$-*‰½œk:Ë4÷ó½wõÞó•"Õ¶.áÿSúpæÆ7G=m²;‹TÙö}tˆÇaœÒ'¯K_·lÑ@ ºé°ÅÛ^+¹ Ê÷#ów-|¶¼Õ¯vã¨ßuM•14U'8sË~ù]Öàî5î3“ž^©áy>.p€F €œ¼Çö¼)+µœ0B‡Ç£»T¥>3z Ý¿ÉÉ+G÷½Z¹¯®‘j9ÓmüŽÞG9§:Ez,8ÖµSíP±qvh~Ë9œS,|æ8r«™ÿŠv Ó¸:µµ’2"Îl¹^ý…JxG‘í4¸qþ)§”Ñp£Wä~ºñ”kphuCî6ca½Á½å·æ RxßíìWÛ¹5…Íü°ã;ž–íN(ö±Òù)× *µP* ÐÁ÷>ç ®aÌÄì×GN‘…‚ZV¶:+Ö•d±7.î/&{>Ø!¨=ddóÕëJ>­¡nÎ÷gé#Ó ŸUªÒ MŽ-®­+Ù Þ®°[×óÉÆ.¤Z¡ “üw{ Œ_h"YúHý®kaÝÁòÛpæ–|i—ÿönj8¤Âˆ¼èá_ãGž  1B€ åGÐÁÿÊ/²f8y¸6Ï4T…^3úˆÞ¼ÊiØJ8½ó‘³I_Îÿ« EÚ|͆n'§=,>Ÿñxåêl†Gi·ô¿9§ÌvFrŠ…¯Vš º}~a'é½/MB­¨Õ™³'uûa,£]ugdDŸž·4 ¶~¼)ÌN.o•«Óxv¬”ûÝ>÷š5ÆX™Çõ^éµlWHmeÕYŽû•Å:L·W;¼£ä ϯV-¾<üìp•jV&#ÊËaCpYQcÌ4£ŸwÁÕMÚˆáœÿ1gnè`ZMvÍ Ù3oOÝ:ÇÔB¨Åã 3ŸrV\ÌÎñkl_lëJòjÞB-&.îºiê#NêKò¿phÐÁAŠ 2ΰpkÇà Ošl/ýªoî5Hü¶÷RcÞ¾)…ÉO\ÇX/öÕ;3zW¯ôú]×âí§P=²«ä*&çl_l¾±C5{äÌï7œVÕxo‚¿  ñA€ #ãéî“å‘£ýM‡¦)²fÓûI\½ÀiL÷v¿ý½÷0¯„R ½©ýöy›;¹D—²ßñT©n›.®è¼òEI[TæÅõï®Ü½cÑ`=‰škÂHºq·~š+Åôû>¯8…×k–^æe«ZycĈó™×kÌÕ*/íW.óáÒù7[OŸ3¼£bM™–™úp‡[ÙPx´V=´ëÒƒ•é³aòÊ +ºNv˜;eÌ ŽªÂ5¤BVV°çÌA“nþ({„ÞmÞÏpiç°¶ÇÞI÷8ûéí‡lÒð]ÑYªÒÚ`&Ý^l9Á·lf½×Ío[ùMHê.|á:§“~ÜžiÎcž¬¯ÜvÊúññÈ‹Å/êgߤºæX7—.ÛKÆSÛÞg€„ïe§îòÕ¦1fæ§"&fêå+‘Þt´ÇR7£ %½⎠6:{Ím˜V) Óƒ½wn9š>ýÔ6“:àa£HtYwqË“ŽKž–œüùãÁ ýGkì\4@[´úuÎÌ ½¼}ùҗʾaz?=[¿ëZ´ý‚&{KFä!>m8©Õ³ã£šVÚ"±²^» ±õª}”g~.p€F€w¬ä{Ëι¢t™6XƒÔWˆ"Óuº•ô…“œSÙrývøÄÚLT«k hûE.Öû‡]®%`þTq£e×®¥ôêçö®4;Åù®Òb£Z×#‡ôëalÔJGSUV”N)¡!;5!úËÇw¯žÜ½~ñÌ• ú:ÝŒ®n5»Çü ÷8Ù¦ž³3ÊþxÈm~ÝÒ _˜õõáÉ-‹íQüæä5E’"ªëÆQ˜pxÖÃf´°fg7t@ã6Íd¹Çfå'¾½vpâ•ç#K’¾¸_õW¾áA£CKÙ7šš±Uïî]»št2h¦¢ ÍYlùé±aoŸÝó9uhïù7ÖŽáÊö•>2t ûýÛ<[Ï}À‰Uù«º´~±Úmýl«6%× aåDœß±lþvÿò5 ÜÃu飯>;y‹ÅöŠ×•\1xc7ã¨mnNã{êJ¿2#-ìñ•#.kœ}¾²Ø/ÞD]Py6¿J¬ÓšÓN7;mxÇ)æ<^c¦uuÜÊóÆök«\¾×Q˜òÔïâéžÇîÆö¹žÈ Ùñv+Îo÷7Zø³hX¡û†7»n¹`ùüIÃzêËq%EV~Ê×w/ŸÜö>ãuÚç-{)IØÿÅ~óQÃ…W®}ïeéò¶tÀ˜¸k5wÖé3Æ~h?sãvúÚêò¢4VAVrB\lÔÇw®y{{_{WÃNIý®kºÖ„}k¶q â¼Õ„3£[}}²sDz‘]š”,áÂŒ°ÛWÎ^t&¼h19Zvr#Bòs42в–}cߥJ«j*ËËH‹ÑÙá&:<<¡bøVóŸ[ßz£0úéù=ìO«ÛŸöYÖ¦ªvoA™g/„˜ÜWÚcàÛյî®%$šè¨KS²¾E%ý´û ;ûò™©ÕõŸ•ìºÖcȹKÆ8c„wè{Ü’’—¤þHNýQðšN9µWtA÷o<½r(âWû]Nìn} ¤W-‘õÊsÉÏ%MZ­™ª´`áôä¸h®wVÕÇIXþ•»Y-V?..Ê×u:ûFÐeÔ4U¤…Y¹™i©IñÉÙ¿!¾Ñä-¶<Pg5ß»ìJ8DnØÍƒ«oäáß…;tתt€¢~×µHëE=k?éjÉð2ÙÏwO4Ù=QJ½…–²xa*û³_öEè²þpãv›?ÕRi~.p€Æ€WŒÈK÷ËNÃ0Ÿn©L¾ñX¼Ã”áÊGö–Œiöfϱ3×Õ6þTõõ¦¹MÞÞ³´µ‘gT©ösχZœ]?oÁÖÛ¤zÁ 4³œ³rýrûr¿¶ñ6Xäs)ªÇà}ËË‹ûWq2ù;nžš.sô0©™3Ób¿¤Uý¶„ÚÍöº¼Ý¦k®EL¯_?÷7²kŸ–ûßš s9¶ož©\u/KS°+à¾êÈ¡«nsiÏŒ ‰©4­BßN.éVí¼Š®ù>üðq=W?åm1/=é;W‘ÒrÆy?÷~ß—- õVÈ « Þû< åd;‡‹‘\¦E‡¥EWû_?¡J›¬ºl¸jܤm¹0Øñ0µº¹PÕ›JÖKß]ªT‡y?ôðZ5{ûÃïµO_BÞdæ ãÚWuq ú]ׂÍ&œ~œÐwÊY®½ ôo‚*ì u\výêB•½Çy¨;8@£ À£ü°“»^–•„-fXÔ©û²hÛ‰#Õöº•ü…ÿw h©GçšF«EªÛªÍ=ŽMºWÃ%ƪûWq}»-·†.ywõð£§Î_ Œ¯áà®P“ömFŒ=¤«f =@É «Xíyb¶Îa™«_Då—Ô¶œ½jó*û¶RTFDMó‘êw*ÐÇçÊ•«7nûWºô ¹66“æ/u´ï¤ð ›=ŠD—Õ×?9e„=¼rÙçæ»wᅫ©i8)½^Cì§Ìž>ÔH¾¶W¥É›­ôû:äâöu›w ¬²ÃªBû³—­r°iYKwõ¢Üg¼êÞ‡[,Ü|ñC¥Î/ÂzÖ‹·n_:¨™%—÷PX'T™Îó/|vÿ¿­[<Žø~̪z*éE‹iÆä^’U?/¬e½Õ¿÷T߃®îûOú}¨v%‹4íÔÛràÐQc†vÕ¬ÓI¢U¢JÚïx`»ìù¹ýûŽz]¸ý±Ú×P2ì=lüôéãûÈÔ´¾ëu]SD[N>ll½Þq…›ï×JmHÚ-Ûê² ¯šP~hUÿ^%þ.p€FþaRƒïÕ~©¶2‚-BYN¿þª"\¿±\«zF@gq0k1ÉÙÑÕ'ÞÍ›XçÚÐe[Û8îdߘ9 ƒ‚B¾FÅ%edçæ³hB"²ÊM4´ô [iÉ ý†ñ¬(â-Glõµ[÷öуgo?Ǧü(•QjªÛ¦s׎ͥK7OtÍ9A¬9ÕÎDX¥Ý€)ìÛj‚`dFxú)ìsd|rFæ<]TJ^E£y«vtäëë-Ð$uÌí±oEýÄÓcÂBCB?~‰IJKÏÈÊ-¤ ‰IË)©h4kѺ­¦4©þ9T‰VÃÖœ¶úh|ðó€Àà°¨„ôœBšˆ”’†N+£.Z)‹x BMû¯¼Ðiò‡§þO‚>}ûžÅ ‹É©é¶517mYÖUØÈ%ŠåRÓlèZóÞ°æ‘y•k¢f>ÓÃ|æŽì˜÷ÏŸ¿ýXô¶„Å%e•5uõ[·mÓœ‡uCkÞoþž~ów±?¨/ƒB?GÅ%gå2i¢â’Rr*Zzúúúzšr5Óùk•:^ž.Hûú6ðí‡Ï1‰éY94QiY99…&:m;µ×Säýõëu]³g6rëëâß=òx“šCK)5mÑÎÔ¬S³Ò¦aÁ–+?²VòÐuáïï¿yóæ?~ð»"u'--]Ðu$((ˆ ðB€ €h ë(??ŸßU>@€® 33³eË–a;hÔÈï*4JÐuѤI'''~×ø€h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €è¿Äû÷ï¯]»ÆïZÀßlÀ€ü®ÿ!@ÿ%|||¼½š™éñ»"ðw KKKÙ¼y ¿+Àh°€ß >过¹¹ÞæÍø] ø;9;û¦§³ø] €?BqƒÅ3³ü®üÂÓÒÒ6oÞÌïŠ@M È17×Aƒü&ÎηÒÓù] ¨ 4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ P‚•Ÿó=9¿°´HV¢ñ³Fð'B€r2ïÖïý,šYé ¥vßO³‘§”?Âʾ:|Íà ¬ Ó‰õ9¼òÚ™?á“W÷|p›ÿ®}¯iq‰&ZJFZÝ{±ÒÑ– Ô454ryáOçO{øø}|TZñOÞàзý[ò»Zð‡ùb 4&='~+Ÿãi³qv@Q6ní¼äÆŒ¦ª’´Ÿ£%ElÐùm̼„Ð×ÓLx+ö½s»_W Á?&‚ÒT:ù$v,ÈH pÝÑ}m|ÑC-Í·ÏVfßa2ó~üHˆJ|÷"üÞóðˆwá>ž·Sä,—ópj§#úǼhüX?’îèy>øá«¸ÈTFñcT1™¦Í”Zªwéßu†µ¢ŸëȬœÔ§Þ/.\ûðøEÌLj´´¼’Ç%ÄUdšj)èµRkÛ¡y+]m±zÛ, ë÷ôzØ“Èßh¸Úéc}Íþ6ÐPTqe-*Aã’lªXEz.CRj¦ (Dâ²ÚMþ ô\Š" )­­!Ì)ëNžÙJ²âŒ”hïW×¼‰`%ûnÞ¯wÙìâ-;ë&8 ¿Ž•òè†÷í$‚¦©7fªUçÒR…éñß_û¿=w5$äYȹ7Ê#­E¨ü®icd>Þ}nƲgïrBHÞ|X‡EsÕuÕ%$ ³R3£>|{ùäã ¿ÀÇ~!óõåz×öü®1ü[ jA—UºzF?»'ãz;O°Bým,„?³1‘üãö q)øúp˜…÷½<¢ÙŒYÝ U¹CàÂÁQS:î?ηÚñ +;v·ËœkÙì p»9¼6µo!^Åö¶0=îÒÚÿF¸¥ç1¾Žð¯C€àE´…éñ»9±mÎ=) ˆ~c7¶»¥©(¿«YÞsç+ìôLHvس©bz.&¤¡?ÆLèD$?ªÆG…é&qÒ3ÑÚiáýuͪk¬ I©ØÌïfè~%Áªz €ß€WÂ-Í®~ÜÊ)–}ÿóÎ+×ΦˆFh¨«ü”{÷3‹î4ÑЫr ÁV›©Ÿø—ºo°¾ûœ™|¦(=z¯¨6=sÐäš´U ü–M¡Ðþ¥åd!@ÿä¥ÜÜumÛ‘·‚3rÙEº°²–bËÖºS\lFjUüdd<>âçrèõÝWÉL‚’ÐëÔÒvF_G;5Éâ \Úƒ-ú¿LàL,ÒÆ7af_ÑÜÐëv¸x3¾Ù¡MÆHÖÇ–Þb¢¥ùšÃ÷‘ºÿ~ÖPÛŠ£r°ò?û>pÛûâú£o_S‹:ˆÓ¥åÚv51Ãbz?ŪNqa¦¾y½oï3ï[áA_²‹O¡J(Ê4m¦ØÊÜÌe};5:É“‘ž[|çÛ×O™,]ÙÊÛªêØù‘cº^üTxÄéUzÑ=ª¹S÷ÖµM/ª±úò´L½ßКBF·j¨~}Orb·[nr|P ib¶ñD+}Y"5"Ê÷Èíc“5¬¸s!+ý릮Nò ÕV vؘ5£§½Þ¶æÙú‡ÏúN{úŸQ:!ÝoJ|¾ß¬õ–3ˆœ´×}ö­¿vùsÉ`{Më¯ÖTEÝmˆû¯Øw ¯Çÿ°•(o6ÊKôœà6þT ! b·t„s'9‰‚Œ÷·Ÿ¸ìñwôñw·w稉÷Aaöýµ{¬Ö‡g†#z»nÐÔ”"2c^Ü [àZtE¥IÃgµæpŸ"g1h‡ÍSëKy_ÝÏ›»dv³¢Š1ã_;,g§g‚n6Ìc”\Åúô¬t/sîóº  ~}ùòåöíÛS§Ný/BÓžl¿îø¦•/‹ÂeAèK§¡/Ä{Ùu;ÎxhW9±*›3ë­Yª2¨ÿX¥àý ó±ÿ…‡æ7)ÌÌ›»Þg‰.&K¯ç—®NAÔóä’»Mš¨ÔáÚ%dÛ2X¹–¸§g‰‘gŽ —.ÿ1ÉÏ)ÓçÊÓ f¬Ï]ߢ¾Ó)­U+mkÅ;öݾ"K­ø ó¶  Þmݺõܹs—/_þí/&ª¾ÜÏQhÜÅ>i%d'Þ9|•}¯¦7}Ù U“u”+æÈúlVל?WeÿŠ8‚ø¶c_ôt—¦Ü—kaD¾r½ÅPžÞ·wé%f‹39"›sOLALˆ|ËÙ¶Œ‚Ïç¹'±ïõ±Ý>DºÖHT§%'¬dà A= UÂ/šø0À˜áìf5ÝRI²Š}öüðû‰ÅwÄÔ¥( ÷ÄiÒE;ù…DAfL“n°~g¬üìÒ^ƒBB‚%/›z³øB†„tõÊß)Š˜Ž¦:ü™ >ÞŠÏž+/Eä_‹.~JÎÄ@´rÕ)jöS8wy\PPîݻǎÎ~~~,«{÷î h€¥Êj/º²qÔçn®wyǦ—>Ίþ¸wÖÇCz^¸>|jÙv¢~›è:ãûõZ{øN>õß­N“úJ—…VƇ£w_Êëç4-mh€ fîÂ’IEª¿>WõÿN²-£0âÒ£À¢çèfSõ•kß´þú€¿4ð­ùä‡\Ü>¬°—K¾\"©b9²Ë„)¦Öí%Ê ˜y±qœhqËõg/¯zVÌü†¨qÙË¥Çä”Ü•••á| s"c Šï‰¨ToéÒ2œÍinlJz!!Eä|â¼/Ѧ²5GÞÔ`íÚµ***vvvRRRü® 9L&óòåË[¶lyþüyÙƒÎÎÎ W ½I÷n.Ý»9§Åß=pìèS¯'iœã[oîZYŠ jW2ì|=7+ÐT —»ã™M¤:_f1Vºd–¹ç@"­ÛØñze9¡Z4(‚E—«-zë…ÌZ§®„l[Fî;?Îôòf­„yÈë`›ð 4ü*«ö8‹¦ ßJQ¤u\ž­l»öâºÝÁŸÙa2#Îwÿ%öMÍjèyÏÞ9­ ¬Âœ’8”–x·QªjûFÖjÒ€›*Vî×LÎÝ&íJ®Ådd—Ô“*TÕWŠ* PÒV‘Ÿ—_Ü×±tzºhm7çiAýÆŽÛºuëùóç2düøñ={ö¤Rñº¼¼¼'NlÛ¶íãÇ }]­­­¾>tiå>S¬ûL¼÷Cóœ›nõ Þ]Ÿ{ÒôÞ”âŽÈõÞ¬@1]hÞÜóZ8Qxk@øK½¢]sVªÿ“ñÂ6‡Ú6)khˆ ªŒ:{G¡¨GVÂJçïÕ†l[3ëc 'Kè(ð‡þÄ6àh¨#zÑÞ6Aä¤ešŽ}æ§å„°ˆHÅ@E•Rë:wÌæŒ7~§N>=|6"‰ ¢¯\è9B<ÔǤè|p ;_r¦¥)¶Òì¬ý²¬8ßÎ=¡Î–J%ý©â%ƒOæu‹þ9Ô2ó JþIX¸èMH”L_žSÅô?©}Aý´´´Ö®]ëèèx²˜††Æ¸qãØ©ºyóæü®@222öïß¿cÇŽØØØŸž¢Ñh7näK­JQ$Z´Ûx£YÇIlŽe°7!ON~N,«@ù-Í BúÝ–t½1哾·+°çÎ.‚3ãÚö Œ&f‹zpùÖ-M»ÈGŠ4+::6ÏH–÷qýòm…ù%ãpÓEyÙþm:À?Æo/4>t¥Qté¨)¹„\ÕWÑ*V’EÈéÉVu%‚"$ÙÖʼ­•ÙZç×óúì?Nüðó=ÖÉ©%  ª†ÍÞbeI*$þ€ù*À7µøž´á´.¥#!ÑD4Õ‰WùEíéL¢ÒÖ“‘šÌù'QuÙâfM ÎôéÁñ B“§¾5-¨?ÃüùóO:õêUÑ(ÙQQQë‹?];àÏïáá±wïÞ´´´*'`ïûéëë7p­ª@—²rÚýä‘…3þ{r¡ ø{šhÒC—9ö™N¤ynû°æ¼¡ä×—®w [o1/í7R¬!Z4¨J] u‰È¢ñ—?‡¾Hd Jæ Ù¶ &MÓöÃ(Èã¥ÃÈئüó§üôBcC×êÝ\vSR A$û½|“­cRm‚f|ðzBÐûôWæÚ3“ßÇä6WkRÞëŒ"¬eär Ëùž)DÚ›Q” ´Lå‰Ë±ì ùèY:£³<Ÿ?¯Œä“+’Šï,ìoVÞ}B°e_âr$A¤}cú?U“•ñ­øž~_±’éU‰K‘á/5¿‹Rõ šÇõG ÑhìÔ©ƒQ~u†§ÅеþYYYŽŽŽžžž¹¹¹ÕM#,,¼fÍšªP~‚‡Íñ¨Us]:W½  J+·S&İ·…‚œmÀoiV È˜÷Òô¥K$‘yÙ÷â7½Î‡ï¾hqÌ^¡Âyr Ò¢!¨Óij›«ŽoØw¿ºž6v²Lm§ö1>î<ä˜>àœ“º0Ù¶ Ѝ† 癬ˆ”B¢Ö—úóÚt€þ”Ÿ^ht$:[LÕpþLq÷gn3y´º©x-¬´Çׯ¬K äº,´à~>ïÉ»'­¼n+Á½RR'ˆBH^œó0MÃÒ¨ù¢Øp‚xãæÿzÒÐŽ54tÿn¬¼×ÛÍ{TÔLAi;ÈÓA•ëÐ"Uµ7jäfÞ£³Q™}u%¸ÿ‘™åïY4ä3AÓžÚŸs‚NÑôÝh ‰ÐíWoLš4X±ºVÔŸ¢]»v ,غuëOçääpwí`ÓÖÖæK á_&...&&VCzf›5k–ººzUˆUúùÆýÔÍ•ª¾h4#'6£è¯˜®Ji˜ý=Í "j35]æDÌÏ.;Ÿu9–$3|ô •Ÿ6/ Ò¢! 0q›ÉÖ>O Ö“õ·ž°5¯qzVΛ‹ï_èö)nA&Ù–AÕ7’ ^fÄ÷;os´«Ô•ˆÚ²ã–WM|ƒÙéví“ov;Ww×(;'Ž•yi÷ÕÅßG’ö‡ö’©¸i*̼¹ýåëÍË[^ £ï¾cÿ•ÒµnQò[!Ô²ÛúA·F^Í#"n#wí¿îíe*lÓ™…,j;"©à{„çòc3ŸBiÙãÊþF£<]½“ë›]¶%~?z~×<Çe†etÓÛájQ¿9Ý…¶£4J*OSï´cÙöb‰äC{ >9dtÑ ­¬Â<&MˆÆë‚ús¬Y³æÂ… Ÿ?®òYN׎ 6¸»»Ï™3§ëàââ’™™yðàÁ*Ÿ•’’Z¾¼šsÃ~fø½¸Ì%J²U<ÅJþì^ÑIË–4KwËS³µéˆ~ƒ—îõÎ&>¹œüDÈ:.Ô©tŠrôhPdz óšõÉbwugÔâV/vµ’¯¡¡ 7éÁÇZkjqeɶeØêËx–JÜt ŒÔ­i-™èOjÓ~C€†:£Hö÷¶·»ì_øî°—ùa/!Eù檢‚ÌüÔØ¤ˆ¤âãøbšóŽMÝjSùŒhëùéVmBçLmßM_J´ ýý­'[<¾²‰a»†ö,ÛrÓ¤mÏ ìã±íuaÂ¥S.]Ñ睊«,DÍÏKŽOüœ">{ñË5MŠƒYùœñ@˜yEÝÙxkše¦Ç$¼þðƒSÊ }¹cK´Pù“™‰)Ãn>L,>^Ò|ݾuíõ*·SP„:¯Ÿ{,Æm¬WäòŽÎ!N½†v–/È|ë±³{hA45áú:M®­ Ñª¹S<†ï‰- ~8®íÃjít¥eD¨faVRʇ…£î¬riÏó‚úcˆˆˆìÛ·¯OŸ>5 ÏÒ¹sçiÓ¦5d­8(ÊÞ½{³²²N:UùÙ%K–ÈÊVe§|¿c·ˆpÐS­Ø#/âÕœqO“B¨»õæ>åßÔ¬@•o¹d‚´÷®â~áí,¦·®bç¼Z4¨b½ÜžËs~(1b¯‡iθóîÆ­%«:Ä™›rÇãÚ©8B\ I·eHvµ\Øú™Ó;‚ñð´Ý:EïUzJ¥±ˆ•÷#µÒ±Š?¤MþÐð+¨rÝû]0~ræñ± ( K .%S@J¦U÷f½mºLغe>¡öKì«?|ypñ›íÅI[H^©£åÇ>öĸÓ6U¾ÅÖ€Mþ»é~ì݃ÀÄGÁ!씦¬Ò±»þœe¦¥çô»'»}ð¾äŒ Ø%-g9ÉÊw=íÚZ ‘J¯]¦0& î‘»?¸ {²zéOS Ȫ+´djfÙvăÖ5 Û,¤0æÄê.#ý·ïy~}Ûñ™EñQ@F¾­•ù’éÓ,DZ 26»¢G?óØpå^DHTôã¨hvWj®ÚÁ¬Ã–ÝíÚ³Àh¼/¨?‡……ÅØ±c===«|VMMíÂ… ‚‚Ô0Öð¡Ñhð÷÷ÿiü •yóæ5l]¨ô¢áÌkK]›87`Û¶‰š¦-?55øQБC¡‘LB®×`ïsæ:Ü_—ßÕ¬ ÔaN/Ã]Þ}—tÔªòàÖïnÑ(# ;ì€S¨…Ïì7ïõ4à©êÿ8€Ÿkï‘-JÙ™!TVB¡¤HIK{IÚ{mí’TJQIÑ (+- JJeïuÿ=Ï¿aÜkt¸>ï—×ó\÷žñ½§ëw?÷wçwV¯^ÍÃÃÓðzm€Szõ£ןݸ“˜ñ8àfÈ‘ºÈFá•”è=b ‡«édk‰?O/i£nvÃeƒ®8?ê³ÄZ ¡ÀÛv=¿£pªŒr¸å`|3þ쥤»¢Wû^Ëýwjh~~I™.=ØiuÓï§4Àø§ÌÊ`_§¢Qà ß-7_H‰Ÿy?4“EH¬¯áacÑ­ë,ÎY±tb=Æ-HóUâ£ï@g€ ­ODDd×®]..¿ªÑÑÑÑÒÒ"¥$š‚‚;;;YYYŸÒÒR ‹˜˜ÚýŠŠŠS¦Lùûõ°ò‹HûaxͶèV`}cßhRvÝ65[Ó¶¦Œ­Å`_«ˆœëÖ鮿ŸüL~»¨~õ­ÐÔ€ÎÚĘ1cüüüBCCÿ»gΜ9ÙÙÙÆÆÆ—/_–‘‘!±6èœh/?kkk==½}ûöQ(^^Þ7n˜šš>}útÓ¦MllxCz¡½€¶rðàAuuõš30-,,¼½½YYY·mÛf``п² „NäóçÏVVV´½eË–ÿî ÷ððptt$±6èp  ­tïÞ}Æ ´t"//îÜ9Zz¦Ý¹hÑ"MMM‡õë×O›6ì¡SHKK³´´œ0aŠ+~{HLLÌϯÞïé„ mÈÍÍíÊ•+»wïùïÎAƒEEEÙÙÙ=}úÔÛÛ›½ÝÍf ÌäåË—´—Ü‚ 0õ8´hhC¬¬¬aaaŽ.UPPˆ‰‰;v¬¥¥å… ÄÄÄH)˜íCšÍ¦M›&NœHv-À<  m5tn–€€@PPК5kôõõ/]ºÔ»wï¿\0=Ú‡4{{û}ûöaˆ3´.h ˆ ´´´¼{÷îÑ£›žA €N´W”µµ5Ùµ³A€’9::*))ÙÛÛ?{ölãÆ´TMvEÐá…„„Lž<9 ÀÄÄ„ìZ€ !@ù455ãâ✜œìììΜ9#((HvEÐ?~Þ¼yW¯^íÓ§ÙµsB€€vATT4,,lþüùÁÁÁÊÊÊdWÒñãÇ׬YC{-ihh] 0-hh/ØØØöìÙãããcbbBû¯ ÙAãíí½{÷î»wï***’] 03hh_\]]UTTœœœæÌ™³xñb …BvEÐ1¬_¿Þßß?22²[·nd×LÚ¾}ûÆÄÄŒ1âÉ“'Ççáá!»"hרT*í³Vxx8-=KHH]0?hhºvíJ C3fÌ022º|ùr÷îÝɮکêêêÙ³gÓ>kݽ{WXX˜ìr S@€€vŠ‹‹ëäÉ“ÞÞÞ†††ÆÆÆdWíNeeåĉ333ÃÃÃùøøÈ.: hh×æÍ›§¦¦6räȵk×Θ1ƒìr )++5j-C_»v›››ìr A€€öÎÒÒ2::ÚÞÞþñãÇ{÷îåàà »" _QQí%!""âëë‹—üeÐÐÈËË?|øpüøñæææ8Q¬“ËÍ͵µµUUU=|ø0+++Ùå@§ƒ -:¯_¿¾OŸ>AAA:::dWäÈÊÊ<|øp²+êÔ>þleeE Ð^^^HÏб @@ç2fÌ{{ûgÏž­^½Ñiii–––&LX±bÙµ0 :øøx‡§OŸúúúòññ‘]QçòòåËAƒ-X°—Š€ :# ‰;wîÐ\ß¾}ƒ‚‚äååÉ®¨³ }h±±±Ù´iÓĉÉ® ™  “âàà8|øð¡C‡ú÷ïúôiKKK²+b~qqqvvvûöístt$»€æC€€NmÆŒjjjNNNK–,™7oÙå0³ˆˆˆÑ£GûøøX[[“] @‹ @@ggll3|øð'Ož:tˆ‹‹‹ìŠ˜PHHȤI“.\¸`bbBv--… @tïÞ=**jòäɦ¦¦—.]êÚµ+Ù1•óçÏÏ›7ïêÕ«úúúd×Ð  jðððøûûoݺÕÐÐ0  oß¾dWÄ$Ž?¾fÍš°°0 ²khÐ?P(”%K–Ðrž½½½———««+ÙuxÞÞÞ»wï¾{÷®¢¢"Ùµ´h€_ØØØÜ»woذa?Þ¹s'ÚÉfZ¿~½¿¿ddd·nÝÈ® 5áàwÊÊʱ±±...VVV¢¢¢dWÔÁP©Ô¥K—Þ¼y“–ž%$$È. •!@ÔCPPðÊ•++W®Ô×× ÒÔÔ$»¢£ººzöìÙOž<¹{÷®°°0Ùå´>h€ú±°°lÞ¼YKKËÒÒrÿþý¸ö=*++'Nœ˜‘‘Žk¤³B€h̨Q£”””FŒñôéÓuëÖÑR5Ùµ_eee´ÃEËÐׯ_çææ&»€¶‚ Є޽{ÇÅÅ9ÒÞÞÞÏÏO@@€ÄbŠŠŠ^×zõúõ‹W©?fÐî)..ÎÏÏ+*(())VRÓzó2™‡‡—››G@PŸŸ—‡§§\weE¥ZrrrìììmQíøˆˆˆøúúrpp´úöÚh€¦‰‰‰…‡‡Ï›7ÏÐÐðÊ•+ m×åååñññ‘‘÷nGÜKNzž›“ÝUNQLV¡‹Œ’X·þZ}d9¹y9¸x¸ù9yøi7h¿–æ•••—×Ü(. Ýø–ñîzÜK¿ ðÏi¯¾}Î’–ÑÕÓµ`J£ªªJ¡PZ^gUU•……ÅÂ… YYY[¾5€ö €.ìììû÷ï?r䈑‘ÑéÓ§---Ûtwqqqׯ‡ÒBóãGñÒ=”{h«XÏ´š×[H\¦É¼K Ó´Ÿ†­ª¬øúñÍûä¸sa·ì(-Ìïolb1ÀdèС=zôhvÁK–,iöê4¦M›¦¦¦VYYÙFÛONN>ãÖÏÿ,…•SÍØ®÷ˆ#×qñ¶æ V6vI9Ú¡íxÚ¯¹Y/E\ŽŒ\»~“¼‚Â8—Ñ£œœ0÷@# Ó¿ÿVßæ—/_|}Oûœ:ý='WÇrԄͲJÚ­¾—z ‰w5°v¡ýŒZr %öÖ…ð³+W­ÑÓë3uòÄáÇsqqý2:h2½zõj˶/^49kˆÇ~y­þ­2"¹XXÙÔú ¦ýŒ.+y}ýÄåpwsÝæÌž5Ó97$)))$$„ì*€™ÙÚÚª««“]üš ¡A‡¶†½UÄÄÄxnÙvÿ~”±ã¬5_ò µ—ë²srë˜;Ð~ŒFÎ ;³m»¼âøñãÎ÷À¹ÿtíÚ•ààÓ¦¦øs€6“š››ëééIv!ð;h&tíÚµàà“¦¦½È.˜SLÌk4è-ôôéS7÷ù©ïÞ›ž¿~ái.²+ªŸTÏ^cWùä~ýt÷œ·¦¶ÎèQ£6nX‡ ›ÿŠjf&çéÙ¶g”B§ååÅ™—GvPhædf¦àé9˜ì*€9yy±¢Ao¶oß¾­\µæBà%ëÉ«ÆlÆÂÚa!1éán[M\~óÄF%eÕU+W¸¹Íacë•´´€Ceeå¡C‡W¯Y«7ØeU@ ¿Ù1†Vðp÷í†C'ùîYpðÈÑ=»v ŒOéÐI!@´¹¤¤$'gN!i÷C‘R=;ðð*Zñ3½CŸÝ¿6y†›acGáüBè„ Ú•JÝ»wßÚõìݶ™@v9­CÓxH/ËàýKÕ5µýý|MMMÉ®à¯B€h+_¾|?qÒûOß(&#Ov9­‰ƒÓÁc—Šá`‡Qc¦¸Nذ~;;;ÙEü%ÐmâÖ­[.ã& ™ä±nM‡8Y°ÔúZæ÷ÄÓä›}û_ºp¾%Wè@˜³M ב#G—­\=iÓyE²ki[üÂbÓ¶GœßÓ·¿ñ• KúúúdWÐæ Z•J]ºl¹ßùÀùG£˜lØFC(Ê€Ñî"]å­m‡ž8vdذadWж ZMYYÙØñ“R?.<þWP„ìrþ*Mã!‚»®O™1ìýû4w÷¹d—І ZGaaá”é³?æSçì gçà"»tWÕ]pìÁN›‚¢¢•Ë—‘]@[A€hÅÅÅÖCì8%{MÜp’B¡]iºHv›$jÏì%ÅÅ›6n »€6 ÐRåååÃFRùºŽ\°§3§ç:ÜüB³vߨ;Ëœ——wù²¥d—Ðú Z¤ªªj”³KN%פõ'),,d—Ó.ð ‹ÍÚss÷ S~>>7·9d—ÐÊ šJ¥ŽŸ8é]Vá´mÁ,,¬d—ÓŽ‰I»í¿½aª‘  àøñãÈ. 5!@4ßλ➦¸Œ`cç »–v§‹d·¹î¸O駬¬d``@v9­ ™"##7oÙ¶ødÙµ´Sâ² .+O wùôñ#111²ËhÐÍññãÇ‘£œ'¬ó–%»–vMÓxHú?q#GݽÎÊŠQ.À  V^^>ÂÑÉtô<ýd×ÒØL]sÈÃvÑ’¥;·o#»€V€ À°%Ë–üRc‘]HÇÀÂÂ:aý™­ûX4·¶¶&»€–B€`Ì£G|OŸYqö9¦|¦¯ ˆóòcÓfLNI~ÎÇÇGv9-‚ À€ÊÊʉ“§ÚÏÝÎ'$Jv-ŒJó½Í–­X¹w·7Ùµ0!jYÑ—ìJê¿¿R8xÄEØ1-yóPËK¾~/¯ú÷W ;—˜('ÆïÃÏ °}ÇNV>qk² 醻ïØ4Z}œË}}}²ka6…‰·§y$&¦|ÎȯMѺc?ÄË ô5KYêÃyÓïG'}NÏ­®ù]yXÊ3ÌT ?A€ú¾š¨´ãTæïw³Ëè]z:eHú¾È®øvÈÆkέ‚ªŸïä”ßn|ÛádÈWzka·vûv]] övUfÜ0­ã¯Káâ‘é)¥e dãÔw¬•‹úd¨ŸÏxwû¢‚v“[#0}öQ|‰ß‰¼yófËÖm‹OÆ“]HGÅ+Ð…–¡]§L{ò(žìrÚ;jIÎÃàøÀÑñ/ßçæ–ý¸ŸƒŸO\J¸{1e5m=;%y^ _»+1vDIÚ<ÅÍ»3êÙZUæS·AGþÃ?bÿ<ÿéœó™4CG ÷ËÕËÜÿ¾9Qþy“æš•/[qÃÀ< >|J'3öoK¸cc˜PÓ»!¾×,ÙSÙø?ÛwjUñ·à]1)?ÝÅ*¥-«Ï/몚í˜#ÅUûhuEyÞ—ìâS®ÞzsíŸ7×|B÷³¶3mnˆ®Ê 9ð²¢îvIòÞÐ<ûqBø’´óp›7ßbüi9² éÀú rNõ=xðÐܹnd×ÒŽUDï¿0sYìó‚à5sÔ[4WVI–ŸŸ½ª0§ ýŇ„/Co&FßL$áw vê6ùi¤:ý•ƒÏi­Wö¥µ·cØÎç.oý#К n61]wÓËã"ª "ëV u½IŸøK?í›ä-ßÅ1Ü@êÏtIaåçÊxN%¤õÜljÒsݽìBòÝêr/Á¥n¸tšÀŸ[®ú.ãñÇYWQsʬßÖ¥–¤&ÌxìT:Qø ÔÎU&5HO²YßlV¦?9ø€ÚÕTúkä§r¢:bïÓtgS9üu =^³<€ìB:¼!³<7/2uênnn²ki¨EŸöÚîRD{îíæê¿YW…¯žÆ¶*/óòºã£wå•UÒ³UÑÞò2ÄÇÁ§«Ø­}÷?·ÍhMxçF°XÎPæˆH)'ˆ¸£Ï>L6«7;ÄÞõI#*Òî}è3³{=ý³y1Ñ· ™©}5}÷dåSQâb-`´n ·‚ž÷á'—­ò ¢($âÖ7ݱÍø¾¯ê]à½G„Ü=vÑf{ÎäD|Äù·FK”0̰SX±z­åÄelí;zt²JÚÝzé:tØÃcÙµ´?Uy“ë²#¡±rAÄúž ´U¬‚RÃçkî¾RöÿSÃo<*1A-ì±­‚ôßk´ªÞìÞª±W5&Ù^“Î?6;­B77È•ZJ¢®¤›ÌíùG>¦ÝÙ•ð­æÖûýg¿NY*ñÇ÷jÅ1'þ) ºÌ'Íxï[7Iñ‡,Œ‡_ ¿ºŠ‘PSaÎˬ*B‚ñyÖ¹cŸXM\‡©)h8s–Æ?ñÍœ·Q‘ŠéÅÅÅ=yú|ÍêKdÂ$OY»Ù}ÐôéÓxxÚùH‚¿ŒúõÚù)çk²#¡<äôгcV‘®Úbºã#«˜®–‹nË‹d5çÅÚõïKUé_£-ƒ(V у @c(¢ÊÓ-9B¯–ćãAßfÎÿ-W}z¶+äljɇüã1\ë·t™÷Î'¬”è1À¥W3†¬±*Î_–Ö¼Ò««ÿ=y‘[R 9bYJ¬ÏK6‹ j’¬œ]¦õ•;xó=A¼õ‰z²|´2³[±z­ÕÄålì8¿uÈ(jÊkïß`Ñ¢…d×ÒžTdù¬|”Ws‹Ål¥‰WSËót[4½@¹Ýÿ­|q(Ð/› é^£] +W»=´@6h`…Çd†ßÕÇ…ñÌçÉ{7+Å_Ú—ªw·#ÿŽöþÁáD›}IÐyqnæòîú*·Û¶ðÅ«7u·ÔtÍëšÝ¤Šg>ßñiìµàc©9G»ß4Õ›ËSâSÜ¡8{³zšù‚°C’ƒ×ý"kvqxäÉøÐ¨©ß+ §”–Òˆ)ƒVLU”âhæò?”e‡í Ùæó,*9¿”ö+—dqU ¥©Û‡;÷ÀxëHLLÌ+¥êJv!Leð”5×-uwŸËÁ%?”§>:‘T{‹ÒÃu ô)Ý ›Þnéǃöl¸—W^÷«†Ó›Ä=k*ò£}nn?öøÎ£ïùÕÁɯ¬¯ê4sÐÂQ2uuEúÈšÅäÔ-Ì©q=k–ᇤÓÇc®¦'ˆŽŽ|н¥4ÔJó’£ß¦>y¾Îóg‡Ý ±6¤4cù%Ÿv Þ¼ð^…@?ÓM~j½º9ïÓoøÜò½ô]Îúµ>rLFÝÝÏ­Kº§Ú÷üâ+W®8::’]K;AýóìÇœi=”{Ó9=(=¸dVFn]Yôn¾ª×®õí8ïÝæ!;WF•Òj󽇛ödË~òxÛÚØ ÷ãNÞ˜þð¸NW6‚×tâ‡tS_·½³‚‹‰²×+Ì—>~”ÇÕEPœŸƒŸRœG-}yošQ6gÊìñ²,5gÌ(*/Î?ÏÿdAˆôÞyÀ°íˆ…KI¡‘v©•Ž@YÖ)×]ÏfìR£–ŽöÒá¯ÈOºõ`ûÈ…×"wšpûd?E®ú–'¸tÇÚ,$#ÃG”äe}üàù!µ¼9‡ ˜þ‘q‚=§ å9ïWL™§.|]°òÿ]¢E ‘ÇÞÓmgæÕ #¨U•ùYß’¢ŸØ~+4àTÖó<1ƽw3Z傸{³FÎìñïü,²ÃÍÍæùDT!wn}U)þçVYEz)™ö`÷|^Apj\Éœ3TøÇÔÒï—=¼e•D™ÓíÅ)u–f,_ýáÌ™%÷*ˆ®æ7nŽêûãêÈZ£{–+ždü)BýJJJΟ?¿Ìÿ)Ù…0¡>6®GŽù @ÿ«"=îû›]»þþ]S˱±s×ÛîV^Ÿ³¯&Šô |4i„dmª­¶­fµ²]üßSË_sd%(¼²²ýT¹ Z€¦Åé w,D…jg2¥–~»0cÛ¨S¹DQÒ&Ÿ/£WÓjg•ê§i_ùíýz‚ hÉôé8‰°5޵ôáŠ=5i˜­Ç–GóküØŠõpÃIöçô¬"ÞŸ?5HRüÉ.…£«©¥QKvצg~瀕¾#…þÊ?øFDÿ64½‡ ˜44—þÔÞ¢~Ñßâ婄׋‡ªÕ5PÔâÈÝq_Ñe3»‹ô⛡vci2A'íͳûw²ä‚„¡¹„Ú2ùF[ÆŸCp­’ĨïtÎST~\Yºžû%úÏ›Ýo˜Os¾^£G|’-c4Uçÿï¬RênƒÙ#®U¥){CòF¸ 5Öb²°rþô×Fá±kúš»Ö½$¾ù]>µFu¾+ãË—=»ô¾Šö¨’²*ßO )­Øh’(¼u\ºt©§†°¸ Ù…0!wºgddtíÚ•ìZÚƒêïï4{¼b¼œëM©ÑKý i7z-:Tò¿’"6Ð|”Dü¡/7¼âß´ÿ%/pË©ˆý{ —¨£×S¿‘UÄë»™…«¥º4³–V8•i±ówÖ\[KbòÈÙ?¿ÓPD,†zh¹ìÝî ¾s—Ì©= ¾âM´ûîšóÞ9­œvŒj25ëp¿04Ÿn‰èÃ_"5æÜKë 5/¤ê/I»‚K ­¡“z±ìb£çôX:óíîˆ=OÒë&¼+M8•”CȬ!Úø‰õ‡` ºj£è9·¶û“ú¨Ô²¢¢/éYO¢“ƒ®D/Š^J^5öèJuFú6¨Ù¯„”öœßï—y÷(¼Ý´¯ÅçÕ÷÷=N7€±á%\ÒçÈ®sû@O‡æÌumbíú–§V×\f–šôøáw-k‘ÿÞmØÕ§Ug¤hÄá£'ô‡Ì$» æÄÎÉ­gétòä©+–“]K{P]ZüãTgvnÖ¿•Ÿ«?„ÆÕŽ:05þ¥eæí׃8DkçSþù§ÐB^¨±­°Jhˆ‘™µ°ˆö$º4óó{Ë@õÇk÷cjÆ0sèÊûÛƒ^ãq —“ˉ÷Ç®eϘKkE«Þ_ŽJ¬yŒÍtZ/:®Ð:‡ ˜44 ¯¬ëH¡Ãûr ⛟ÿ畞2œDuZà­[å,Æîz=jY{s ÷ã·Ê "!Â?Õh¹ Q˜~*¤ˆÐ¶µ—k¢•ú%×([}l}]М]{òçEXì÷yµeÔ¾U‘976ìÕy1%Ñ¿ ½/jÖ­ˆ›Å¢s-¸²3ó~y¤‡î ¡øÚaHŒ8ûÚx…*CP,ú RćL‚xu÷k‰›(?ÃËs([H7?_clûVzí²›1XBýέêÝ»wIII£½ìÈ.„i q=ºÎeùòeÊßJŒí…ƒƒvjÒ_UEõßÚiyjDVí ^YAJeåÏûeª‰PETdäWM\tõßßZ4¡\Ë@YJXí%i !-Ù?d ¯¢œ,‘ü† ^†.š+*H”>¿Y·¼¨©/ÁÖ:\Ðá!@CóphMÔ“ÝwëA¼÷ýgLo–Ͼ{ÒÍùCŒýb‘Ps·å¼u¹Œ 2ødÎß"[™øðÚwBo½v·¦^w„à*•—R[Oµ¨bv ¥å§$)í?ŸC|½àç>Võ‚]-\UÞ/ˉêífË·7¸Ðçc§2xÉ69çÒÏØDDEhG‡ Š?åVüMUóÇò¬ SF{Û¾ë%A}°dH©ÁΆ®SûÛëòã|·V¬mfÓÛŽœš~E599Y]ßš°ËòD͆Â/ÅœºÖ6ªË>eÖí*sy¯9 |P]^Uÿ­­ÅG ª$-£¢ö·T}Ý lBÂu]Ã¥Ÿ²óªÁê—u!˜_QŒŽDÔ¾ š‰KÍpŒÜ­-ï "=þt²rå½#¯‘ñæÿ/Há5«+rùÁw‚H;uïÑ*ÇêSϾ=6 füeÇÚ}ô¨ã=ÄZÖ½Ê"ª<Æçü™bZãrøMž–pÓ+•Ÿ¸W­à6q‡ïŸ·"=~æì¸/´§î?^=¦/#B³ü7}WEeÝ6.ORÜ»J{Ý¥õû“ßÐZõü̇/Ó~dì.ž²4B—^K]¿yKiÀD²«`r*ú–aááдùÝ E ŸšøHýøñS™N—¿0×'µªäGP•X#‡vk n.ig¡]»siÏñò¾WÙæšôO]Zñõkí5Ï e!:Öjdy §€¶™¶é:¯ÇîV‡¤Å7oø¾Ö_ÉØ°løÅÇ¥»+ñ 4wF ²Þ€Ó\ËÊÊ89ÿîÕ•ÚEýiZWÖL™øîØõÜñS„›j/+_î=¶0ÏöÂJÆÆý ‡TÍtqåQôö[Av"lé`å–“å •׌ÑÈ«&þ诩Ìù^w-Ù.5C<(<Ý~|Yø>»Šhroíìp‰ðæ ÍÇ¡ ëª\3W]É·Ï¡?×@ñ÷‘¢lJcÍz¯ xL»ù%»ŒMiÚ –ÏŽIÍ‹»8HÄaW3V-}ÿãBZºRÂt4}e/㎧Û5»78o»ºso©Ýw3i êõ;7³4œëýR¯Õßž¤}©¹Áª=X’Ž¡._ý=)£TA¦ëÿO}¡põÐÙ~Äð¢yL6‘û4£’@€n›7Ãõ-É®‚ùñËÊ÷Љ‰155%¥€œœaazÆsµ=v±IÛúmµzEPlíÔ—¯Ñå©%O/%Å+Yµà”CöýE‰ O‘›WiÐÔt@m­¥G€Cu”F¹O>T½~{6Ô‚×ïë®$ÓkTÍ,<½tø‰„‚øzûY©‡|=×(ü­¾öu¸€<ø§‡`u˜$³tÁÇšÛœ½æèòçë‰]N×Íøâ¤û5»©‰%½á²¯Â?sw«g¥ÄÓ]‘';ôù}î·ù1·JUU%½³s´ð°HÛ÷cI{P]^0Hé—Ùª #O½©ÂÁ*?ͦnæ~u§^ÂGbsˆŠ°]‰éC»7‹ÚöpA‚ -Á*7ÜHgÁ¹D‚´·TÏ¥øh‹qïÅ}?©„`0]E¬å‰®2ïatÁÃËÃXW6µ$5qþÐs1µùYfòØ•ztÌ«Püá¸6¡î0¬ñy÷xdÆ8x8Ÿ¶—‡û½›h¡Ðø4×5ª?]Z[3D¯Ïš¶bM¾ý5°|UAØŽ„·öþÿlª>ÞIzMû¿ ’½JÓu@#R’ŸUîMv‚¬²Nâ=2Çš¿}ûvK­ž={’œ¤YxîZp¡l×ÈcYïîé_2áâî¾õ4ÔÒìÛ{BÎf|- Чªñ†¡áÎWˈ÷aClj„7Ñýõë¹ê**Kó&efå”®–.ýEdz¥Ž}‘£eG€MVç‚0ÃmY_O^Üç¾p™æ#õ[X°ÇÕšs•8éöã9  ^ »ò9QyÿܨõâÁ«•%þ-“ZVœSúûNÛðpA‡‚ -ÂÖMkšþ¹qcæ*40åEÔb ½PÒÙR•é饖~ûšò¦¤î—Òä¸í›ÒêY]UœõÁ/‰ tyþEÁêÊìôÌø~\¿ªôÕãC{¾ü7Z]Yô-'%.%8<³ æ®¾ &߬ÞX—FízÅYY‰—nøg"ýy²ÞäI)òÕûçB­ÌýðµHN”ȯ^ýôöö‹==tUäü%t—| 8ò GA  ?µ0÷Ù(¯ƒ¯¿”^³g{HÕóLé]žJ;§¦•â6M׸— OE^Røƒ-{ÞQ ~Ç}昅£>|øÀÍËÏÍ'Hßâ…¯Ö(íɬç kŸ8{åŸG¨–}:a´ù@BÅ/Ëuíwäņ¾®®Îµ\ºêNÍìXœ¶3B®önø·ú«ß¾Qã’ó¹“ÏúÊÚuCŸ*±úÃ7ÍS±¹¿Þ+¢µýÅL3Ñßv@-ÿø.â|bÔ­Ôäg_2?ý7Ç ·¨ ˜Œˆ¬ª´‚VwMc¥Þ}Äþä&ÙCõæáF×j í"I³wq<²2ÅâÚœùa·OžÒ<j5Nˆi7Õî||¬ÕE9ùi¯2¢_„\}“^û¯ ÈùS€®ª,¯ûš­î¬¸Ÿš!jEyIÝ9 •åÅ•?½ÿ³ 9˜•hµgÛãª/—Ïê]¾ÒËHNI’“¥¼ìû眴7Ù|s'¬íZ×ìTü˜0¨º¼fžæŸ^OÕ•?&n®úiN! oß± l‘¯+‰ôùšë.™Kòç}íj{ÓOK¤ñÆ©%G€Âi°a®oÆ®ñþiËûxý³r ƒ_EARx´×î”/Ñ}Œëõõrÿï8æ”^8.¾ßéào•±vJ2Ö—ã¨Ìþðùñóˆ^í{-÷ß-üü’2]z6°Óê¦ßOi€qM\Ë»{Ör|LÂÇÒñõéyŽ@QEÀÙîfN>ýÛP¦ip t“Ÿ8o“^í…¯DU¶Æl¶=¶Û÷ù½Ä¬¢’ÿ!nI©>&½Ü–õwZ³ý’Ä+Ö×dÕmþÓbå9žj†>w& ÎöqövÌ*¨Ûø“s=yÃlvz\ž-ÎA°t›0ùBʉ¹û_}(ËŠ‰©Ö1Õš4½‡ =í?ÿÇ)6Îo¡säŽq×·ö+¨©™]XTÛÎlÉ ‹éƒÅ~; ›SÑ(ð…Œï–›‡/¤Ä¿Ï¼šÉ"$Öׯð°±èÖugЉ¬Ø :±ã¤ù*ñÑw¸€é!@C ±ÈLZ\4©ñe8ûÜ’ÓðìÒ¡ÙÍÛ=k×~¥ýš·nÃx‡]ßG×€=~Õ‹¹‡éÚ¤ÚÁØI¦Ÿ Ãðò,Ò–¶X {£íKvvvddäðá­0=a[xñâ…h7†V¡p‰Kö[å:%x㱚ÎÔÊG;c?ºÊ68‡GéÇ #Ÿ I£•›´{6ñeAõç ÈgT!µÂÄäJ¢òÕ¹àìA3Eû„ÄÂ&Ð]¢«-iHõ–Ì|œN¹‰ë¦Åk\2ÿ½Ù§° Ë)òÒîæë)ùGz¦?½³Ô<àAvÍ_꟩î.Òõ@-Jyzzžï±°FŸGƒXÙØ%dä^½zõólд—GLLLó6ÈÆ÷Bf’fãQ³5ÝhK×ÐpÁÎqœxpt|Æè&Öç2éDûièqn»è/õ^’SÄõâ׆Vc¶ß±À~G;o#GàNÅ!V‡†Xѹ8«ˆœëÖé®[ÿxÀoÕ¯¾š:\Àô þŠòò2†®pËèò-7]¸páöíÛRRRí6@?ON—Ó`|=Ùñ>V7 Ãjέ}kã¶¾§×ÉÔ×U–¼É?ô;·ùÅazMµ©øvûp¡?fѺ7Ó­có âŸý Su£§Ÿ—§›ë½ã«©Q|Õo½¯ÂîI&ï_Ue$¬ð æó¯äø»‹æÿÙ^‡Â«ª=iËÛëaqtoû7R=TSRR~Ð%%%99|ôn5¥¥ t­ÏIš–¡8 **ÚÖ…@»… ðWPŒÃŒ.ßî}ÿþ=88¸.7WTT4½Ù^¼|¥7Ê¡9kòèÛ®™?u_Mô{»Á7Àyñ8•ß›ÚòîlÞõcÀXaMžY[ñ:>(‰Eç”–¼±Œ•dìÅÏ‘|ïZÊÀYštµà\Úƒ6mz2zÅ*Q3ëäeÓyŽõ^èOÕùsüîÔFX™e“§5˜ž`—•“ቧkËõ‘Q~ýúõÏ÷ ®ÕÜí1`Ù²eôtuëêêÖõ@÷ìÙó/Tí4´!Zn®ëo¾sçN‡ÈÍÿÉÉÉáiÖª.íõì/zÑ’.5íÀŒo™HÿÜØV~™}í5!ë¶¿¯TÓpå;¿iœª3 ²ðòŽ˜ rqËw‚øvõ؇I{zÐwñ vù“Ý‚Ö퉧e¯¶‹è1°;C+^F ªí›eUœ2[¦É}Q„”ç^ËÖ³yï+ÜüÂÙ9_›µjÛBn€?!@@ë£åæË—/_¼x±Ãåæÿäsñ 4½\½(Â*sŽÜ›G‹¡‘·ž×Üé"ôo÷-5ûZàžˆJ™Åcè¹ÎMÉÇ«¾ßxlGë×L^ÈÞc¢q-Aïâ«ÿ½äM=tù›Þ@ N©Ñ§#Ô/<«$ª^XµKõØé¦"tå‡ ßÕÝÔïg EÇ”¬¼*6jôô'.>ܯoš»vëCn€F @´!jYQVAíÜN•¥Ÿ2K+ø¸ØîÊèòíM]n®ëo®¬¬lrùüüü-[¶ü…Âçèè(//ÿÛ…\ò¢Â.Þæh‚`0ÛíltãDTAä$l^nÔû¨ª¥âõÎ32¸Íít隦»4ùHb¶„®£á¿(Ø„¸)í˜ðªŠ(Ú›’3X· ½Ñ–MvúÄ…k<#hÿ6;‡ôÖ«‘ —f'¿ÿqS\S¨í¯ÉÃÍ'˜ž—×ôrm¹‚ ÐFXÝý1#uë-ßî°°°8×ÊÍͽråÊ… ÂÃÃËÊÊYEHHÈËËë¯UH?Z¤®ªªd礧‹¸a,]û,ñ~7õE9A|?~úؤÕn’1^›29Œó°kúÜAjAEâ“UþÙQó~ܯî—åáw£2{Ûu¥»s˜]Ôþä˜;ª¾±%‘ºbæ™m=ìÁ®,ÊÌ®»ÅÙ¥Ëï3C·.þ‚rz ×¬YãééIÊ® ƒB€€VF‹Åãký—¤ÃÂÂÊËË›^³Ý(..Ö14kùvX¤&¸¸û¬Ùö š¡ý]/ptVÙmîC:Î$j†{DDD勺¸þraŠˆÊ¨aÜ÷Ï•U¯.eÛº‰Ò?-k÷¾«&:NL¢Eè;Žíºr‘iºª¢ôGÇ<+çßIÄÍ/$.Ùµí÷S..úNÆø4´•Ÿ“tÝvááá"Isrr>‰»ß[b~lø5µÀ*A¼Š:ùŠ]2ÖéYíêW]{àŸÒæEß~½H¸ôH5þs ñâ@\út› \½Eb츗֬¼RJËôc.™>w1èR_>facc¡ÕPSGeUÃÛ«*zýá{ùïÓ.²Jjè s2¼K‹ò?ú@ÿò$B€€6GKÒjÕ%退€[·nµç$ÍÅÅU]]UUYÁÊÖâÁ¿ªV­~0f]mí»ti7:»;«?'_¼SIT…M“køú~/¢®ýcé¦ÍH•¬BV‡ÆÝ‰Ý_å¡uÞG£žQölÜ´ŒM»U–“WIûPQÿÖ*òâŽÝÏL{™[ôoŠæ•“Õr´_«#ÌÉÀåäK‹ øù[2èàïA€€¿ç¿$““CKÒÑÑÑdWÔ >>Ò¢|ÞfNý v3Y¾u™…´›Ò²²|töÊV¹‘X%>ꤣ¡ÈŸ«T}>ë¿Å?Ÿ–p¯ù0õ@O†Æ °Hé,õÑK‘@[?Û×gÛ°µKÿ¼j<—ˆºqµ&@S³’ò+FðÖÒ¹¤}ç9Dñí#ƒ-Õœ«7îBŒ‘8ѹNia¾€@sç ø» €ÂÂÂk‘]HƒxùùK‹ Z%@7Kå÷»‡ßš#ÇhÉÔ]Ë¥R}üoeÑô¹ˆg^=õ‹ž,]†^ëòbþZª/ s=£ç]ýû"¬Ú¶âÄ£š™ì>ÞNÏ_)%Ò¶3Ù•  :h€zðññÓ"i»¯HM¸ü„PØ¡)ÙÀè Ž^}v½u6ƒ ržDõÚÈŒtõaá7òvzkïÕ/‘ÿÄs>ûgëîb¤¸þÒk‚¨~ó¹­t›&hÚÑ–B€€ Â]ºå}'kï•iþÑóìDl¤¹».xv_A”Eïù'ǶÝBÿÀ"ªæqÚè¡UÔ·šùòê™ ›MÁÈmÜ͹§‹ˆÊWGö|èÕ½-'«(-Ì–Âè êÑS®û׌·Jºfdì¼ôÓµS_ u“î %fW£-²/’ò+n߉ÌÐ.Ëh1EÀÂaãÌ丘 ¯áÎÉÃ#ö\þ@dl9~Èfé\ž6ë…ÎþøZÁغ­¶Ъ ê¡¡¦ùâ9û.}}#ߪ-Ýèì\Z¦‘—¾õí… ß†Îg¸E§ðèzNu}ûù´úgU[9¹|àñw_üL=ó|f,ß•·žM-Í)£ëê“ úü>EEÅ£E›ø[ ꡪªxóX‹7C­*þ’ý&%ÿÇ”}ùßS_äðÉ p70b¹º<;5ý®WÜ7‚WK¸àc¦ ¬TýsÁQË‹¿¼)—îÁB|©9ÿïÕîðÛý,ôÔÅDøjãmuEîÛÏkÆwä¿‘£¨)ÄÏQÿ)‚ ³ý-¢ûßúØÀ3àè¡¿ö©ŒÁJï=¯¯º®¿¾^ÅÆI]§”„87µ<7ãë›Ä·Ã“<­0NÐ0åb¸›šZ]ñî¥ŠŠ £+ ´ýé]JK¶Pž¼`ó´™¿\Ëüýíù·knˆ©¯<Ûæ·«pg?+sàAÉßnOÝZ³¨âà3φ+ÿ:ú¸8ü°UbáÏw¥ß[Ñ÷íÿ²‹—íÓ¼2nlrþR6÷Yº™ ‡L½¬Wß8i O_»‹ŸOÜú¥¡gBá—¶Ù½ÐjõÇgãïÞx•äwýÆ–âºAÓvNaYñNÃåu¨êõ•lÎÌÙÙŸÓ…»ˆðñý9›@{„ Pž={~ÿò©¢¼”£™§Îq¨íX½ƒ‘5ºhí)>LÏ‚<–Ó#þ˜5ã'sï¸0²_ §ú–õ [šXŠMDÆd퇑-Ó+ó}вŠj[l - @Ôƒ­§‚Ò§7ÉÝUuÉ®…ù}J}®¡Þ‹ì*è… P?3Sã—ñ· ÿ‚7‰wÆ/šEvÍónË–›dWÑaðós”5½ÔŠŒ|§­-KvPhæó :ý¸òóKÉ®¢ÃˆŒ|­­­@vƒõ «Už{¬Æ/&»&WQ^úêÉSÓódÒC†Øåæ~Ëi`"@¨PNN.Ù5tZZr.. È‚¿š 2$77+'§±!’ð3 ¥KNN6ÙUtZZÝ;Iƒnff–ê2¶¢¬„“›ìZ˜Ù›'ѽÔ4É.¤9ÔÕ5¼¼v‘]ümÐLH]]ÝËk'ÙUtxüüüjêš©O£Uõ-È®…™½ˆ ·dIv @€hµ•E\l8t›zpkÅ”ÝdWÀh€ :ä¨Ãèas¼(”®|-óíÓ»ìÏôõõÉ.€Ð ÒÕÕàåN}|_QÇ„ìZ˜SÌUŸ1cœÙÙ›sù² @4fÚ”I—®G€n ÔêêØS›n\#»Æ @4fìX—µëÖ—åsñ ] ³ù'6\JB\CCƒìBƒ Ð11±Ƈ7>•ìZ˜M|ˆÏô©“È®€aÐM˜1uò–ƒgèV•Ÿý¥ºè›³³3Ù…0   ƒ š¿pÉó¨ #[²ka·NoíÓ[]HHˆìB† Ð …²iÃÚūת÷·Á|v­"ÿû瘫'O¦$“]@s @4ÍÞÞ~ÕÚõÏï_Ó4Jv-Ì ì¤çD׉’’’dÐÐM«é„^¿vÁеÆCÐ ÝB¹Yq¡~§_üCv!Í„ @;;»Uk×?¾{IÇÜìZ:¶›>'O™,!!Av!Í„ @ …²·Ã(—^–˜ºÙÞ>Iºåâá$² h>hzµ|eÿ2§ÅûÉ®¥Cªª¬8ï9mïî]ÂÂÂd×Ð|Рر}«J/uÝÁ.òšýÈ®¥ã ?½UE¡»““Ù…´4„„„öíñ^°|ÚßD6v²ËéH¾¤¿º{ÖûÙ“D² h)hÆ8::žôõ =¶~èÌd×ÒaTWUúoœ´nÍjYYY²kh)h†ù?ªÕ[·»º¡¦ñ²ké.ï]ÜM\ÐÍmÙ…´h†‰‰‰]¼0Øv¨ä±â² d—ÓÞ%„¼Œ¾ò$1Shs@€h϶,uXpü!Ùå´_ŸÞ&_Ü>çîíp!!!²khÐÍ4cÆô‡1±g7O°þ Ùµ´S%…yÇ–ŒØ½k§––Ùµ´h€æ;tp¿‘é€à}K†ÍÙBv-íNyiñ‘…v#ìlÆKv-­  ù¸¹¹ÃBCŒMÜ8Á?xÒJ²ËiG*+Ê-¡«Ös÷®d×ÐÊ ZDDD$âÎ-Ã~FÜ|æÎóÈ.§]¨®®òY1ª§¤€Ïñc8q˜4@K‰‹‹ß‹¸ÓÏÈ„“‡¯ÿ°)d—C2Zz>½v¼(Wå9ÿ󬬬d—Ðú ZŒŒÌ[a³ÜÞ)*0ãAv9¤©(/=³aRO1®£‡88p¥F`NЭCAAáø¡½ƒ¬m¿zë0ß›…¥Óu¾æ~;²h˜–²Üñ#999É. ­ @4æíÛ·Gõôô¤gaYYÙ˜QÃF]<|↳œÜ¼m]^û‘•þú ‡Í—Q›6nh|ÜóêÕ«íííuttþZm­  Aïß¿777_¾|9ý«ܸ~mê´{fšMÛ~EPTªíÊk?RŸD_6r«ç¦É“'5¹°–––µµ5í3ɤIM/ Ð!@Ô/==}àÀ‹/ž6mC+²³³Ÿô9¾ÙÓkë½QKki£ ÛƒêꪰS^÷öž;sÚÒÒ’žUÔÔÔFŒñðáÃ}ûöa°t8Ðõøøñ#-=Ï;wÖ¬YÍÛÂòeKMMŒ]ƽx:Ü};;'wëVØdN÷]3V\€ãÙ“DiiiúWTQQ‰‹‹›ì\àN÷¥‹-\0Ÿ………ÑÕùøøÎŸ?¿k×.}}}___++«¶( - @ü"33ÓÜÜ|úôé­0€€Àù³güΜ™;Ç¢¿ý´Á“V0AWtöçôà}‹¿½}zëfh Ϥd===ggç3f¬X±W]€àÿ¾|ù2pà@WW×… ¶âfǺ¸˜š˜,X´dƒ“êÐYžzV£;hR¬(+ óÝy~ïüù‹.ùps·Â‡ccãøøx''§ØØØÓ§O µ|›m à‡¯_¿ÒÒ³³³óÒ¥K[}ã²²²çücbb¦Í˜xÀaÁYåÞ­¾—¶C¥RÂÎ]=°ÌĨ߳§[wÔ²””Ô;w-Z¤§§¨¥¥ÕŠÿk’’’BBBÈ®˜™­­­ºº:ÙU@ h€uéÙÑÑqÕªUm·CCÃ'‰ >'O®X8´‡–‘¹Ë¢îªºm·»VA­®~t÷Ì6v¢ìÂÙÓÆÆÆm±vvvoooÚñ±²²Ú¾}û¸qãÚb/mêÚµ à`SS¦éíGLÌëÜÜ\:礇¶† @|ÿþÝÒÒrذak×®më}±°°Lž4ÉiäÈcÇŽïXîÐEZ~ÀØÅ½ ­Úá ŽŠòÒ˜k¾wý·K‰‹n^½ÈÞÞ¾' 2dôèÑ111;wîìh3ÜU›™u÷ô4'» `N^^œyydÿB€€Î.''ÇÊÊÊÚÚzÆ m§üüüóæÌ™}þüy¯­K¯î[}Μ9cnŽ>]h_  óªKÏFFF´¬Fb eP­œœœK—.<½ÏÓm³a½úUÒ1áië¾e¼}ù(âùÝ‹ožÅ ¶¶Þ²f1­Z¸oëý6‚vLêÎ)3fŒ‡‡ív;â4tRùùùÖÖÖ†††;wî$»–„……'×ÊÌÌ<zÌoýD1éîò½M{j›(ê˜t‘h­}}~ÿâõãûïžD¾zÉB¡š˜š.ž5ÞÎî"Okí¢å àèèsòäIv1¾:£ÂÂB[[ÛÞ½{{{{·Ã®M))©yîî´ŸªªªÇGÞ»wëöés^Ó9¸x¤º+‰Ê(téª(ÞMQ¢›’ ¨7¿P#O¡²¢¼¤0/;3-ëÃkÚOö‡W´ÿf¦½451™4ÂÜÄ{­‚‚Âß|v éÚµkddäüùóûôéˆ9¼ =@€€N§¸¸˜–žUUU÷íÛ×ÓóÏXYYõj-˜?ŸJ¥~üøñÕ«W©©©/^¾zq7êæË—ß¾eäårprqñðòð póòËöTM{õ¼´¤¨¨ ¯¤¸BPøøºÊvSRTÔPVTêk¥¤4‡–˜EEEÉ~rôâàà ýKùùù 8öÇÙٙ슠³C€€Î¥.=ÓäáÇÛyzþ ­ZÙZ´ùÛC%%´¼\”ŸŸ_PPP]]MKœ¼¼¼‚‚‚|||äenEcÇŽÕÐШα}ûv¦y^Ð!@@'BKÏÆ “““;zôhÇJÏã®Õ:•›GKK+>>~üøñæææçÏŸ—––&»"褠 ³(--1b„””Ô±cÇÚú‚ ÐF„„„‚ƒƒ7oÞ¬¯¯ïïïobbBvEÐ!@@§P^^NKÏÂÂÂ>>>¬¬¬d—ÍG¡PV¬XA УFZ´h‘‡‡3}™tg‘””r–ì*€™ÙÚ:·ÛêÒ3ŸŸŸÒ3s°´´Œ­}âÄ Ú?.Ù@'‚ÝY\»v-8ø¼©iû¬ :´˜˜÷¹¹Õ¤\̯I´ôìääÄÅÅåïïôÌLºuëvÿþ}777}}ýK—.©¨¨]tЈ™™Š§§ÙUsòò ÏË#»ˆúTTTŒ3†………–žÙØÐâ1NNÎ#GŽøøø˜˜˜8pÀÑё슠SÀÛ 0­ªª*—²²²ÀÀ@²Ë¶âêꪭ­]7œÃËË Ÿ” ­¡•æT—ž ƒ‚‚ž™^ïÞ½ãããÇgaaqîÜ9III²+f† Lˆ–ž'L˜››ŒôÜItéÒåêÕ«ëׯ×××?{ölÿþýÉ®˜40Zzž2eJVV-=srr’]ü=,,,k×®¥h‡+V¸¹¹‘]0'h`*T*uÚ´iiii×®]ãææ&» ÍÇi:&&æÈ‘#¼¼¼dW̘G]zNMM åáá!» M=}ú$$$Œ3fРAgÏž#»"hÊRΛ~?:észnuÍïÊÃRžÙ¨`Π4tx ,ˆŠŠºuë–   Ùµ@{$**ºfÍ==½€€²+jCÔ’œ‡Áñ!/¢ã3^¾ÏÍ-ûq??Ÿ¸”p÷bÊj2Úz 6vJò¼”ªO±CÔOÜÈ©g;|}†=¸o£ÑÈ46e™žÆ›–ÇWüñ{ïµK¬‘áj›šéÞj¸z™ûß7'Ê?oÒ\³òekm: hèØ–,Yñ¿öî,Ê:àø;3À ‚€ŠŠ (*’š¢fx¥¦xeŠÖšamæfº©i&»^eÞ¤«e呤–yežx¤&^àQy_ "ŠÈ!—00ÇÎ0x£ðWtý~žžÝWf~Ž0|yù¿ï»sûöíÔ3B¡P|öÙgútîÖ­›¾¤ dꉞuæž¹+>p4G’”έ^oüñOO;;KMVZæÅSñ÷žÞyxOäaIr¼ppâÌF– ¦›R›¨RSö†ÏnóÙUýûP´è³'¢qݪv¶Å‚Ò}tôœ‘72ÏlZÝæõ½‰ú·¸4ýiÇëÁµíË—|%„øÌñ¥†€P†………mÙ²E_Ϧže@—.]¢¢¢Œg¸ûú믟¥ƒMuÙ—ç†LÿpC¶þ;û †.›Ô¨¶m;k5éWÖŒ_Ð'<]¥¾õ6™²¢“¯Wá9-*Õ©ag[â«°±«áãPø<–«àSS žcfÀÄheÕ¸qãÖ­[·mÛ¶Š+šz”µjÕÚ·oßÀ[´h±jÕª5jÜŸëׯ—±É4é«Þ1–¨T?løÎ 5ì°ÒAQÁ½Ç¿[úÏúU¥Ö=Õ ïWgn" ”I&LX¹råŽ;8& ¢lll~øá‡¹sç6kÖláÂ…;w¾óÖ¤¤¤ÀÀÀµk×Ö¯_ßT Ò][¿üÝ冕|ƒóÀ5R8Up‘™:FÍff™L!/ý÷Šg  ì™4iÒO?ý´sçNêìƒ>hÔ¨Q¯^½BCCÇ'—2J£ÑôîÝûÂ… ú7î߿ߢ,|—ÌOZv(ݰ%oörýbݳ©:ö—™¾&]Ll>3ËÖ,«†¸²ðÒw˜6mZDD„¾ž+UªdêYP¶>|8$$¤S§NK—.urrúä“OvíÚ¥¿éСCS§NýôÓOM=cñòÎZx¬`KæÚÖ¾øÝ©2«j&¾4cé̬Ë;¿ù÷ð¯c6FÅ_H3œÐÙÂÁ)à¥z}µ{ÿÕJEœ®C§:·q׌¯¢7D]ŠÏ0ìÌ–ÛØV­áhqæ4?cÏ¢Èéóüv(%C+IJ;ß&uzê0"¤J &Æ3Ž€P–„‡‡÷Ýw;vìpss3õ,x¸¸¸lݺuôèÑ7þ׿þ5cÆŒ[7M˜0¡[·nuëÖ5áx% KÞÿWáIؼ|_¨Xjgy{’JcfUÒâÐð·L•,ÝC>é3¹‰“]~Ʊm{§µkÄú]³Búoÿ¾¹·uQ÷—¬ý£Ó¨UªØJ9ÙI—.ýüEü¹¼ûFL¿0)xfXTžäQwØ—=‚jX¤þqdÚ¸wG¿yྠ+PÏ7þý”³fÍš;wîÎ;+W®lêYðìP(S§Nõôô:t¨Nw{•­J¥êß¿¿Ù/äÈ¿R¸Y¹²{Ù¸ÈcϬËÝ7f¶¡†-¼¦6²~á»xµGà€î?5n¿3vùân•þ¯U¸´Z—5jVA=Ûõý9,¢—ÃíѼÄ̈{î9´6kãà9†zvzqÕ¡¯¹ìpîÐÙ_ëÛ5&>bá»k®ï[Ë>ÏÌùEn›3gÎìÙ³õõ\¥JSÏ‚gMVVÖ¼yóî¬g£²°C››mÜ*ïR^Y&v@?öÌê¸Ãf^Óo¸¾Óëƒúw¸Ì©]—/{ìë¾FuaÖŠˆ!£×0´oþù=Cg%ë7”í{ÏxÍ¡ØôÉ?·ç“%Yú ¿»tq»µ\CæÒ¶Mˆk̼«ù›'ÇÄöjW“†zŽñ˜)]^ε”<ÍÍ?Ê,­]œ•Ïío¾ùfÆŒúzöôô4õ,x 0àĉEÞdö 9´¹7 _',Ë)ÊF??îÌÚKëwï7ü°£lùZåò÷Ü(+ß²_-«5Çó¤ØùëSßâl!ib×D6ÜfôžŸ[ñ/£ÚøMÑ+´íƒ‚ï:ÂÐÚ¹¹—4ïª$¯÷îÝ«P˜ç°2++}‚rR“¯5õ0%ô˜3«NnI,ØphàyÆÈÊ{W÷”ŽŸ—¤Ó[³‡8WrFïïT׺½žwngRÁFyÏ 2µúÎ †…)?3!C+9p,á󋀯“ ýßç^C.‰?Ð{Eʈ×Íœ´}kcVm8µ'&átìõëªÂ·[ÙÙVrw¬æåâ[·J@ãZºúÔ,âÀïGdí×fÙî6R^âçþcÃNÿgÕ¢E‹ÆÿÛo¿yÁ à1]¾|yÊ”)¿OttôôéÓGõtF$wô´‘$ʈ¬«7î;Î<=ÞÌšœ¸„ü‚­rîöEüTcáàhÜ5œ{95]#UÐfN0F°·K ²G«º|Å8Ô•Oý?`ùŽööïñ\" ñäØkÝÂÍÒR!/ŒJMÖïÓ6­5¬[“Õ{¯[è­3zê´y7²Ïnܱp¿©F}0uæž¹+>p4G’”έ^oüñOO;;KMVZæÅSñ÷žÞyxOäaIr¼ppâÌFœP´4-^¼xìØ±Û·o¯U«–©gÁ³ÉÃÃ#..nóæÍË–-[·n]NNN‘wÓ›åBËjÎÒ"CŒê.]º¬jXQiꉊ÷x3kÕÙ…Ñ-WU1rKËÂâRœá\ÆçOƤt©çñ$Bëttk¯È£z¼™å–¶…ß=4¹†kÞûXm^¾qµ¤´¶ÖW®VaeüGSç«J²`DfaS¸XNQ©nõ¦5 e€Æ“båæî&°`WfSÅÕÅòêGs~ÖÔúÿ«³ÿxwÿîêФ¯zÇXÏRý°á;'<ðJ³Š î=þÝÒÖ¯¦¾4î3åçŸ1bĶmÛ|}}M= žöööý $&&._¾\_ÒÑÑÑ·nÕoϘ1cäÈ‘&œ°HVÞMÞk°nÄŸúÍ ó7^ë]Çâ_}úóG¤w^æYìï–ÿÇ'ãÚÇöû{ymÛâïü05³¢\uO+éPžaFºVºoO°:-%­`ÃÆ³¢a‰‡Ì¦ª£ñ–¬ØTT쇒[¹N­§ÿÙ'k$E! ñDÈËÙ¸ûVqYÎ`åáéë–aõdvèÒN››S¡NÉqmýòw—œhÉ7ø‡1¬g#…SåÙ h™LñL¿zgddè«åž7®\¹rèС[·nõóó3ÉTxι¹¹ -pöìÙeΜ1ì-;vl×®]k×®mêïfé2`Zó©í÷&Iº½·F÷éÝìáy«Ëùsõ±Ÿö‡ïi²îH•½`Y ¯I5³UîÒ/q’týxµäwOÉè2ÏÆÆlùup7œ£Cnã×ÐN:˜)I×¶ÿ•ûQñ‡ªXzµp–~¹,IéQÒÕMI%ÜÏ < òªï}Wì1^­v_lõ$¦‘$õ©y«–¤JR…?"?iQØ¡tÖ¼UØËõ‹ÝKcSuì/3}ŸÀh™ÂúÙ]V}ôèÑnݺýöÛoÕ«W¿õÆÕ«W2$22²^½z¦ 0ðöö[ &&FŸÑË—/ ŠŠ2³…2Ƕ¯/ûàL»¹ÉÒÅíoŒ¬3§®óC"77ù÷ÓùŠúâë0²¯l>©-×Úª4~¨œ™åZ6—ÇíÕª¢~¾˜ÙÁÇîÎ{j³v->oX¡¨ù^'‡‚'«z½ý¿=&åo ?|±KËjÅ´¢jdžµ>¾|N’þ ßuäž/Þ{ª<€€†9ËÏØ³(rúü#¿JÉÐJ’ÒηIÞƒ:Œ©bçë¬*uËœ Óýu<Ãp ˆ…µ›W¥:õ}þ9½G_/ÝéÅ¿ŒùæÈú}W½º°©õ¦‚Ç8~ôÐþòÎZXpPIæÚÖ¾øï2«j÷­ÓÕåßü{ø×1£â/¤ÖSZ88¼T¯Ï vï¿Z©ˆ} :Õ¹»f|½!êR|†ag¶Üƶj G‹3÷ü˜±Ï?ÿüÂ… ­Zµºu’_ýõƒ>Ø´i“¿¿¿©§n{±ÀôéÓõŸ«§OŸ6»ßÈË· ¾BÞk~Rì׳[äô_9«Yý¢~q¦ËMÝ>{ÃW$[+á€ÎýûÔ¡,©\…ÒØýx3[x6™9|Kà´¤k߯œ3tÄhÿ[ëuÉ[Ö~´Îp  ÏðÞoT-œÔþ¥ŽÃë;*©wÿ2¡ÒÚÿúºÞÌêFZî½QY§åÄ.[û®SI±[ºôsÚ°àåFŽwý¥µ¼¬œtO 3¥K¿0)x¦áJªu‡}Ù#¨†EêG¦;0qwô÷›î[а²ñ“7çòŒŽ“Füžoß<èó%uý*Ji±7/Ú±:¥úG]ûzYØ{ûþcX%»/û>A’œ^˜ùU —þ•VníSë!Ÿüºäýž8ÎË÷…Šô*©JZn¸r¬¥{È'}&7q²ËÏ8¶mïô¯vX¿kVHÿíß7÷¶.êþ’u£tÕ¡J[)'#;éÒ¥Ÿ¿ˆ?wßyžJúü˜±'NO¾glè3gÎüóŸÿܸqc@@€©§Š P(^yåSOñ–_ÿ6ìd»õƒ‡mÙþýbÿ7µï×$8¨jj¶¶ mvZFÜ™„ƒ{NmXwþbÁëIå­€Öå&_;y¾ðô#¹NþôCf¹ÛïW§ÉSßÈÊI½–~>jo¬$½à`yçîwMvƹSi…×¼‘vêl†·Ÿ½m wÐ?òÌ2eÓ‰C"ÂßZ÷é‹“O„µíÙÔÉ6?óØÖ=“g¼*IÕÞÝ8¡úíÇJ«úÅ4ÿam²úÀÄ™n_¹·lâêb¥NO íÝÚv (ÁA„ù£S 7+Wv„k—èr÷™m¨a ¯)‡†¼y¥ÙW{èþSãö;c—/îàVéðZ…{[t¹Q£fÔ³]ߟÃ"zÝq¥Ù¼Ä̈{î9tIŸ³öùçŸkµ…‹0ãããu:ÝæÍ›5jdÚÁ€²J¦¬Òs[ÏWGÆü¸úØŽ½{þ±þúÍ¿­ììܪT¬Ñ±i×U›4÷iÝÒК˂ë-ÜœvÇ;9ºuà[û ån.áP]ù¢åçŸÆäß¾-9æ ÿ}¿0nô¾±%ëKñ™ )]ú-Ø×ð[»Ó~X’iø­¥£s@×V£Þo7°£‹ÍÝ»>”Þ/­:U%bJä7+NÆÄ^Ù½éŠÜÁ¥Y§Àoµtž:¾ÝÒRÒþ |Ôox\„þ›‰Ü¹öÔý“:/Ø2+âèND?¡ÿ»»¹¿ø²ß‡£[„v¡žŸw4ÌQþ¹=Ÿ,ÉÒoø}Ü¥‹Û­_œÉ\Ú¶ q™w5óä˜Ø^íjZ¨þZ«Ñßàã[çŽPd>c>{ù°ë#¤6%¶pJy—òJñÐê¸ÃfN×çúN¯êßà2§v]¾ì±¯ûÕ…Y+"†Œ\Ãð·Ë?¿gè¬dý†²}ï¯9ûeYâçGxò§æÌ™3Ë—/¿ó-)))ÎÎÎvvvz€±°©Û9è³ÎA%¹¯Â£é¦Ô¦ø”çŒ~ÄßMdæÛdJïàöó‚Û—ðî §ê¡S†N½ï†%áº%E=ÀÊ!hPoýbSáù`Æß`ñüÒÆoŠ.Xläx×:ekçæ^Ò¼«’tòĉ¬v5$]Á.Lݱ#ûR¼êt+u-ë ìò isožÖ²Ü#,sÓ^Z¿{¿aoˆ²åk•ï=øDV¾e¿ZVkŽçI±ó×§¾?ÄÙBÒÄ®‰:l¸Í"è=?·â³_àù1[“&Mºç<»zÉÉÉ­[·Þ¾}»Ù-0à4ÌPÞ¹Iå=+ÈÔê;Oµ¤p0,LÐHù™ ZÉÁÊ·‡yIº¶¿s3õäð®ïwt-ꮢdV†ÓéX“/|¢'IRÜ’X°áÐÀóþ/1YyïêžÒñó’tzkböç RîÑHãýƒêZ— ×Kþü˜é±„ÿý÷Ò¥K‹¼)11ÑØÐœ‚`¶h˜­êòã ¸+Ÿú þôwÊ3ì¾TÔz·ÏG󧇟–tgŽ >8ÊÞ½cßÀжèÞÈî–.ß$wô´‘$Ã*ެ«7î;~¯8šœ¸ã¢ÀrîEå¼…ƒ£q×pîåÔtTA›u:ÁÁvÞ.%ø’x~ÌÔ_|¡V?ð²III¯¾úêÑ£GÌx:à9F@Ãüè49…Ñê:jíÛ=\‹Ú'«°ö*¸ú”ÌÁ{úÿŒ_=aîñóúGe\ÙüÍýUºö\¹ø•¦vš!ËjÎÒ"C@ë.]º¬jXQèh­:»p~¹²¨¯0¹¥eẋRœáüËçOƤt©ç!ârKÛÂàÖä®Mxïcµyù…G­+­­õ9­ÂʸŸZ¯*É‚ÁçÇÜL™2%/¯ˆÝúo¿ývXXXµjÕžþT” ó#·r7œ:NßXÙ'k¤’¢LiеU@× ñ“ mÿͷ礑›#Î6 «ó(ŸäVÞMÞk°nÄŸúÍ ó7^ë]ÇâVV«OÿoþˆôÎ+Â<­åª{ZI‡ò k4ÒµÒ}{‚Õi)ÆsFÙxV4,ñÙTu4Þ’›ª‘ŠýPôü˜‰„„„… ÞóF}:¿ñÆcÇŽ5^K3G@à Yzµp–~¹,IéQÒÕMüiªM9–[«JåÛ‡Þɬ½Nÿ6pe›ý©Òõ?ÔÒ#´dé2`Zó©í÷&Iº½·F÷éÝÌö¡÷×åü¹úXŒOû‚=ÈVu:¸K¿ÄIÒõ?âÕ’ß=è2ÏÆÆlùup7œ£Cnã×ÐN:˜)I×¶ÿ•ûQÍ"®QxÏp%~~ÌÎÔ©SU*Õ­?Êåò¾}ûþç?ÿñõõ5áT)CßyñüPTíØ°ÖÇ—ÏIҟỎ¼ÓóÅ{Ow‹jïðÿÍ}ç?{ÛݹVéêâ,I©’ÒÙ¶ðÍ2c“j4†%%"slûú²δ››,]ÜþÆÈº1sê:?dWonòï§óõWÉ’{tjÙ\·W«ŠúùbfŸ»Îl¬ÍÚµø¼a ‡¢æ{ ö6[Õëíçøí4)Køá‹]ZV+æë²äÏyILLüî»ïŒÛútîÙ³ç¸qã8c Ì! aŽ”uZNì²µï:•»¥K?§ ^näxW½j5:¹ñÍšÌ-3þݽu­Û'ÝÐ\úíØYýÿWðé^»àh=…Òͱà´tOíº¨nèS²O{yù¶áÃW¨Â{ÍOŠýzv‹œþ+g5«o_ÄÞa]nêöÙ~¼"ÙZ^fÖ³ÉÌá[§%]û~圡#FûßN—¼eíGë +€}†÷~£já_Êþ¥ŽÃë;*©wÿ2¡ÒÚÿúºÞœQ§º‘–ûÏ9™6mZNNŽL&ëÞ½»>ýýýM=‚€ÆS¢ÍÍN8—ðw†ñOꤓ çü<ª¹Z[™y ‡Þ ÿu¸ýìiG4W×üØxͯ~/U÷qSÊóT)‰iqçSm<8Îx%U.ú§º N~ø^£–~lòÓmÝ;eöd÷úœžmŒgá•oöZ»Îª¥‹Ãüǯnãf{#ýZåΑK8=<2-+¾þmØÉvëÛ²ýûÅþ?nj߯IpPÕ:ÕlmÚì´Œ¸3 ÷œÚ°îüÅ‚ƒâ*( Z’)›N‘þÖ²¸O_œ|"¬mϦN¶ù™Ç¶î™<ëäUIªöFèÆ Õoï8VzŒXÕ/¦ùk“Õ&ÎtûʽeW+uj|â‘£é2ã ßÒ£gT°7,Wx~ÌERRÒ¼y󂃃Çß°aCSÀ£# ñÄi®Dwk°`õ;ߦÞ1xŠ÷`ý†e¡Ã~YÃæ¾GÉkOÝ?©ó‚-³"Žþ~8éDÔñ’TÎÍýÅ—ý>Ý"´‹±•F…Œ¬r|wtÜw#ÿœQšJg×C:NÑþËß<O^µÿ;+N.2÷L¼*iÿ~màzU(É.Z™²vHÏm=_=óãêc;öîùoÄúë7Ï!aegçV¥bŽM»6¨Ú¤¹Oë–w4«Ò¥ß’±}wÍø*zã´–dÖŽX::tm5êýv;ºØÜýÑ•Þ/­:U%bJä7+NÆÄ^Ù½éŠÜÁ¥Y§Àoµtž:¾ÝÒRÒþ |Ôox\„mIŸ3räÈ‘;v4iÒÄÔƒð¸h==ÃÔS” T~óÍ7M= Д‚zõLž<×ÔSxh@   4 €€Ѐ@@h@   Ÿ#û÷_ž2å°©§(3ìíËgdd›zŠ2c×®ø€€Z¦ž€§€~^_¿~=-ÍÔs”2YÅ´´TSOQf4hÐêÍ7ß4õ< ôó¢^½z“'O6õe   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€Ѐ€g- \\\L=”¦œœýÿ–+WÎÔƒ@iJKK3õèY h­V›œœlê) ôegg›z€AÙh•JeêP:òòòL=B‰”í€NJJ’Ð0õ8v+çRSSM=K‰”í€öôô4¦³\.' Ê"}=kµZý†›››©g)‘²Ð …¢@^^ž•••>£M=”>ãï4õ¯r¦JŸ>ÕjµµµµF£±´´4õ8%R¶º{÷îÆŸW®\¹R©R%}O›z"(}ÉÉÉ:ŽS x&é»ùêÕ«úínݺ™zœ)Ûííí=jÔ(SO€çHÙhà)# 4 €€Ѐ@@h@   4 €€Ѐ@@h@   4 €€ЀðB¸æ¶ÊY(IEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/yang-object-tree.png0000664000175000017500000015031214770023131024260 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+Ð|IDATxœìÝ\ïð[ÐH ( Š`*(¶Xˆ`a‹ˆŠÝ…€ÝÝ ¢ümìŸ(v  Ø…AI)]‹ÿÁ` ;Ü8ÏûŋמívûrÛçž=÷›Ïç 6ÝT&Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4ÐTîzäÈòBdd¤¦¦¦””Ý'66VGG‡¼<`ÀKKKº+*[åЗ.]ZµjyÁ`ðù|ºËÊ„A޼€]q *©Jä*w€þòå ¹Åé®ÊLÏ‚DMw-"©ÜZWWW°¿R¹öZ@HäjÕªEo%"ªÜšÅbÑ]ˆ“ɤ»‘Tîý'6›m``@wP†ÏŸ?çääÐ]EyTµ­­­ýîÝ;º«€2šî*Ê£ªh‰B€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€Nø¦f†SŸ  –¾~¾ÀDúïÖ˜r¥o­n§3ò.+¾q°½â_Öå ôãgƽ yõáóרø¤´ S^IUUMM]Û¨q3scM9fE–ûöé“ço?~ILIÏaÈ((«×®kljÞ¼©2Þ1€@€”ì÷›;šM¹›“ßläõô¡·…¼Èwç'ÝšÒÔvË—ü¦b¯ÿ½>5T·ÔØÔÛnFí|c…m‡3_NÚ×dˆøˆ9ïW›×ŸóªèUº“î¾ÞlSv'gfè‹ÕùµÊ9\þ~ªk ‘“ûóåù½>ûŽœ8÷82§Ä¥jõZ¶ïÞwÀ!}¬tdEý{¨â$†žØ¾ÙgÿÑÀé%,¢ÞÔ~°‹ûd×®Fò’ªDývES“ùoÄ·Â{"¸èTà^TzÐ’!m<~ÿƦneç5_,¼¬Wð²æ "Ý™ÿ3hþPaz&”|w9—žž þÛ[ŽÆ½&íü¦‹1½†j—?Dl›î7åö$#©r¯¡Dü´·'—O¶üÒ7–Müpÿäòg¶ŒqïižÞ³œ-ÕXâ,&;òêÚ ng>óK_.þÙÙÍSÎn^`é¶i÷ê‘ÊH\¢I<Þ®fÿÛ‚ËZî>n³}OàŸ„ )RFcöm9a:603¯ùfùàÅöOWZ+–ÙyÉÿycÞÐíÂd©ÒoÏŽþ:e$F^ÜÕMg“½.'p󩯃Üë–ÿßœ÷ÐÛóê=ÔÄÚáÊOÝ1Æa±/e/ú›¬÷ÿ­ö߆͞n-2—K1œ˜Ë ú®|˜!ú]RŸúŒ¶üïܶ‹ûÇ7­Q¡]Ñ Y ýšª±<ò"[WC€&Ð’#eàâ·íDÃÑWcÞ¯ìÕ't­Mé¡‹÷ãúìa;# šjƒü¶öÕ*«Ã•yfÓ•¬?®~¼õÐ×ùsèZâ¡›çtôn"ž´JâÆÎïÚmu§è• V9vïhÓ¼‘quE)7ãgÜ÷˜˜È÷On^¹xñâå»S…Kg= ŽÎ&Ä 9‘'ǶqÚû¹èu ƹ uèÒÚÜXK‘ÜîüìŸož]Ø·Ó÷ú^Ábѧ&XÇ$]\`­\qZ±Ï›xŸ {¸KµÅð‰nŸ³K^"ûÓ‰}× ZzÝGu×+åkÙf “” @H[øî'L‡_Ä¿Oë‡x8„nl[ræâ%^›5Ì7ª ©>tßfûZeŽWÈùtt˽‚ñÊ6åîÆä]~í³÷åôÕ“5߬šu|Ì…¡µÅ1h‚Ÿº¦·Ý/éY³ó¼m›æ:š*ýÚÊ’SѪKþ˜˜·î=z‘|tÃ’%ëξ+%5Q-&åÑ¢ýЦg ÷½V9›þò%CZE¯I§¡äÏÌ%÷¶Žwžvâkþ-Y÷v¦zbd] Œq’±ÈÍ–Î¥-‘tæÝ¾k7ó5lfnÞÕó•€8!@H[o°Ï®ã ‡œMÉk~Þ/õ¬ÒsûÝcãLK9<­a3õØSó;¯ å ®JúoÌ­oL6þËùé RA€4Ví;wŸx2)¯±}蜾/vtþ36òâ/N±ï{ASÛõëºÕaœkzˆÏÞOú£GYš*º[Ív”׎=ºùöªÖÝU©}EͨﶡLJ¾»ÅD혾gÚÝ©õþ®£5ãÙj—áÂ&ÓÜëæ¹¹ÍË^ˆY£Ñð];8l?«ý·o]¡«\·9€±ÑÂ+GJMÏ%Ôl¿ôbÀ—ÆŽ‡ãWpîÍq?âxi¸®Xk€4€ä±´¶ïpÃ騼f”ϰ}_ì¶û5óâÎOù¿¸‚¦î¸k:«‰r˜XÒ½­‡¢ MÇ®/Íf9Ll?yøÍ¼‘Ég7]ë6@“ÚgL•¶ž+:ø»ÜÈ2Á¼ÈãòÐC½ÔËØ/æôœuï ×ßbõ‘ù”Òsi½îS'”» a1ó7|(l›->4¿…¨Å°´úlÞ7ør¯C‰‚væµëžôÛ`Eaf nrøãÛwCÞ|MÊ$äjê7ieÛº¡†´„âòÒ£_=~òúcDìÏ´¦¬’†^=³f-­Í´åÊñˆüì„!ž¾üð%:1%ƒ'­¨¤¢¡_߬‰…E}M™*;”˜Ÿ÷úþÝà¾Å%eðåjÖµ8´“N »”âÝà’X!ü-h€ŠÀªå°eßàÀ>‡ý–1~ç9¾Ü׫px7öìä‘ã šu&\YÂ0ßðã7ú‘ß`´šÐß ÷C]«û¤nr7ÏçM-‘ueÓ™H§1z»HÙzƒÖÍXf¹âc^ëçÑéçv^Zî#÷r¬¹’)ljÝ0¶mÃ8_Ö]*,FmÈúI¨üaLõnK¼[šü8¿á·á¶×¡n¢<_YW6Ìž³âphòï·È÷š±|í|§¥ÏFõL„Ù1w÷¯^¹qϹW<\.õfƒ&-ðžîÐ@´}‡œØׯټ÷dH\ñ (·w2zœk¿–µs§ëÎ ™ÕÀrí×_—‰ÙÞRa{1÷5˜ÿìõ²&2¢Ô!AÅ<’›ðpç¼%;oDðŠ,ÉìdäØÉ^ù·û‹wƒKb… &Ѓ©ÙkÓþ×zîÌÕ÷¿‘Sœ^ý¯f^¬å~?3ÉåHbÁ²†Ó.-å@â¸Ñç7]*8ñ‡Tlj½û1jvœd¯|þhÞ¨þ½-G?œiLu†\Óéëûoï zò~í¬£ã.(ßPNÄùý!…Íúã&¶ ï .²¿'…M}—éíDÚW)‚]wЬn³\Οø$éüÞG)ݺ*•~'nÜõ¹=z¬ .þ8Ȭ÷ç–÷?wd˜ÿeŸaõþ~†~Ê3ßñܾ㖼Lü“#^ŽG¶÷\súÀô–*¥}»ÀKz²s¼ó¤Ãïy¥,D$¿¿éï}óÈ£šqç»—±-* Þχ«úu›{=é[ø¼ß& ï—Ä @œ * SÝnýÿF_î–`Âá‘^vÒbqcNM}¬ ™¨7ëà"%ÑçËñÍAsZ(tŸÔ­Vþ‡(C¥µ{?£{}…a;ö¿ž´”rÿS½û2Ïf3i3ëÚ¼¥wûîh'biEñîVØ4ìï`DßQwd1EŠÑuØz^eª·wi;˜¿íS‚N½JïÚ²´QéÏÖôº*¸àÉ’ÓªW¯¶ #9êÓû¨ÂúˆOÿÞ*ƒùäÐý¿pλ:»“Ýú°_ã.[UGWCY–“üýKäÂTöýü¬Ö¶ AAËÚ”À8Ñg&µwØùî·«™Ê:Úª²¼ô ÑQ ™ÅÝ“%úÞëìBÍz»ÝÉynà/S„“eæ%gþ/ùY¼\+1C€¨8LµÎkŒ½Ôy—`–矣ÝZn{ÛÝõøÏ‚eÌ=äe-êé9²ßØþ´ ¡Ògbû"'îVl1vˆîžyO{}Bæo-5àKªÞ˜ ãÖ¶Û)c½kšïŒ3(weﯿ* Ê-;Òø]}æ‡ÀW…¹D±e÷úråX CµY¯†D`Áä'ßÇrZ–rΚ>£½âsÓ3«Áµ[îhTCvxé_îø/™òîöKzV´î±`òðîÚr…#R#_Þ»rêØAÿëÂ)Ìe-V~â¯$*í™?ûž‘—žkµŸ¸`ú(‡öMõj°rÿ؈g×/¼,œyE¬\"+±C€¨HLÕŽ+¸_èžÁ¤S£ûlúü”ðbÓ…‡¶ù¤Ïwû¾-hÔànóËL¹&.#6.ŒaŽ>´õÞ²–³Y&F •]ö¸*èa|ºdþ…áGûˆ0µ^Q¼ä/ ›u[ˆí¼,Ôñ’Þ¿ˆ/lÖmeT¾4Ç®eÞH‰+šú5ø[QJ€æÆ§‘¿å;¬pnj㢳}0åë´·ûaç–ý[ŒùO°Å}¸ÀýÀ€+£õËóù|µóá\šÛ.ïgþÛ,ÛLýÖ£Ö]¶wXÒ³ó¢‡yCJ~ž˜àq³ëîN¿J}à9p™pŠDBÖÆëÒ™…¶ê¿§4–bí&]G‘? 7¼½õTžÆ§WŒ²"r_´*v›n›d^dŸ–¥¨kÙÇͲ )Þ .‰€ @T,†ŠíŠƒS.´Ûô9¯™vÿÈ=ámœg)zœKy¸ý ý¡c›)üz»ŒÉ°±–æOýãÔ¦ÀøN}Õ)º²jX3k¹ùARO:>c}h×–”:msâ>‰¬RZª4¾óäÄ(r [»^Ír#¥Y_“ ôðrC¡Ô{hŒ80¥q±såÉÔ}à¿°Æm7 Ž¹Ë¾±dËó!k,)'QnTÀÌ/óÌæ+¯roTÒsŪÙvá)ÿЃÎäÍQç¿äôâö#Šž1‡¾ÚÆÏ¦ñŒ«¼Ú”И5´·¥Zô?ÌhÆù£¿¤ç߉wƒKb… СÔfÉ¡çlÖ}üõj‹å‡f›‹Lù‰A[„¹´þèQN$!e8 pBèôK›ÎGÛСþé*ÛxʺA[{ŒÒþ¸~æa÷+.zÞ<¸é‰é…-uŲjÈzw`íÿÞ;¬V€­c7Ñ­uÍò$nZÑb5•Êû.È®Q«È¹eøi?2¸d¤)yy¦ÍÒÅ]Jž×›¡d3mßCn¿üÏ7dÑŽV{Çs>Zsµà”îzS¶O*1{ °´–L1:#øš‚sgï¸aCµ„%f½Þ»á¡pèMí‰~^­«W÷¦R¿ óZ•:æ_¼\+É@€¨xŒ-½ÍùÏzUáÈRfó•¦S™K÷ýÒ¦s£Ïò¦þs!¶^‘ ¡9A›2Ùú=³f×¥ÞVǦ<Ê”™}cþâÛN>DÏRüœÌÂÓlvY÷Ì|ã·pi ¿”%È]¾M¹˜’0Ø2Eƒçdd—V0!Õa²ƒn©ÛžY«ëä^5.ÎYùýʹY­¨øÉùvá€p¼…шQMÊÎß2õz÷ÐYº%ïäñÜg_§Õ*˜%ûÓـ¼¦3¦Z‰:6¿ŠPíã^ô¨‚bˆwƒKb… !Ðt`(ZÍZÚvÕ€ü#«‡eãL©d%ηӛ®̇&œþùÌ¢BO·x7ÎÓ´`H¹lp_Ýz«à±ï»§ïšùp¶È93¤d‹TÇÉ┚4%LlÅð9YE’8!%WúyP̬Ë% 3õ “¢“9ãP²¢ž}î\Ôhj]›¾™éQˬNÇóŠwƒKb… )ЕPæ+¿]ÂŽª¢Ó?ÿé× ¡#þ·ýᢽík”´t)X:Nkæ.½äõ:¯•rzæÚ§v«›‹4FWJ£ž:A|ÊoåÄ„ÿàÊ¥½ù(Û_ÿý<÷ÛN+ýñO‹]ž )õz…Åp¢ß'p•ò¼fǾ‹-l©hÉ––vÔꨊÐ+E.F>K‚FRT™…(hNâ焲—*MNFŽp»s~~ÎMNh©W³þgrŸQ£¬ž\ñnpI¬$ òI{²sßgaK>ëÒòY·K oY …]Îñ[‚ÖÚöR+Ï`V³I†n¶; øˆß8ãà„ëcêˆð&ÂTªgV“xT ¾<üœ9©.]ã4s‹Q'~ùùþÇt¨çÍãÄ>{QäôÊúÍõJºRrR"ls†”\a?/å,ÄËJÉ*{)Qñ³3 ‡¨H+”>@¥*b²ËêÈï—Ä @R *þÏ;[Ž|/lÇ]Þ¹þ²È÷N=·éò÷Îå:PŸ¡Úiñb›Ãîå Áȹµ`QPÿ=D8 ¶\½Ž ~wòãàÏû׳Ú7¦ë\*rÆ2÷ÞÎ?KEꃋï2ºRÕÂÿ|îua³–u3ÍRßNEKÃüœŒÂ}‹–¹‹`°d kh¸òí³9õÿ¢Û˜!-Wxïì´Ò‘¬žÄ»Á%±Bh€J†wmÓ™¤²—+IöµM§¿õ'JÏñŸØ#ÖO^Õrƒ`¶â8¿i;fÏkXæÝ˜ê6ýšw N ýéøéžËs0£80kæs;4¿qòè›Í-(θÌò»S8zºF;GÓÒG³$~ù‘SöxŒr1aCY»Ôq.b«ÔVÎL•EüMüb«è©Dþ+-þc¼åW7âÝà’X!H 4@åÂ:·ñr)3$‹àáÖÃGÏmP¾Of…³×ôòx.-¯¶|Ηӽ˼[¯×H‹©ÓBò›ownž¶Õ†¦Ql½ž#-¦L-(æëÞõ·øw¡#½'üÈêK…_¶+÷p±*cHÒ˰xN{ÅÒßrù)ïŸF4µiQÛÇÖiªO‚“\?ßù–ݾaù÷Rdj›×aŸ=õÉÏFfwyÞ•jB¼\+IA€¨TrÂm¾Ë+h{½ó6¥_} “þ0Á„ÐÄË]~/§®4/ß —™Z}VÍotnà‡©ÿÍZÜqh™÷’28³ËÜ!§ˆˆÚ9}÷”[Sé ìºft™3´ ˜Ä3¶Ì~¼°±ÈÛƒ—pÅsÑãÂvíÓÚ•™¿_y8¡néó–$‡œ >¹uÛšQWÊ‚•Œx7¸$V’‚ P™d½ñßþLØj8j±ˆßª3kußQææAfü¼oç“…;[—~âéÉ4tß0rc—}‚‰=¾lžî߯ªì´û®œVïêÊ‚&÷áLçÕ¶A ,ʘ(L2XÚ}WL6¸º&<¿ýÂsÈŠî÷¼›+ŠR ÷ûÙÉ#N– Óqé,f#ɹ±åô·~n¥ á}¿¼éœpf¼Z]{Õ£:dBÚÈѹž×âümüzÃÊ›ãýºª–w KØ0^°8?Í=[»é±ëær}kÀ’‘%ÿjÁÎ[frVJÐâÝà’X!H4@%’ê»§ðÜpM\û‰Ü;ÅÔè4¾«ü•ÿ§±þ~dË•6Ý( [(ÄPiï½´íÁ±·ófiàÜ]è•&Âijrs÷L:l»å‹ É}²Ð¶Ìµã3¬Th8ó°¼å<_·ƒ} L„-î:Xçî·†ò¥o^bg÷~‡â„W°¬Wl¬+ʼ»¼{^לöÚ•p6o~òý³/f4õ‡Œ1§xo’Œ‰Ëì6ËÝòGgÇï9Ã)Ô··f9§–15­ÕR÷û‚Ĺe¤÷€ÇkÚP?›·”ª¾*A¶YÒÇðd^{Ūr²iñnpI¬$ òH¾¿í pˆ,ÑlLߺ¾Qg¨ÙŽë^ã¿‚.Τ3›®Åué'â™PþÀ®3tÝ”Vk?çµÒÂBK_<¿å¶ËO.y`½ðqþÑw)×g[›.ܶqVŸ5J¯„“z1ðkùŠ-¾ÕŽkæÞm½òeþ?þgÑö…߃–Ð͉¿¿ÝÝyJÀ—«jtßuh‚È#ƒãö; 0øß”F¦ô¬{‡õÞ$\³tû…Sš–ç|7ì:C6ÏÝ`¹4‚h¿>íeŽßÐÏ Ôñ)ܤ—g¶¬Ú—4îð›¢_K°ëß0mmËuù³f¿_×µgK§=Ú©—˜çx)ïn=•³±Õ+ºMdêXÕ%vçït<Ùw!b˜«~Uùìï—Ä @"ªÊ›@ÕÇO¸±ùdbA‹Ñrlj)„¡Úfœ½Ê‰ƒ?óZ™—7ž‹êë"Rßiqä›ÍZë°«ß锲-R‚¢å¼óç;ußVð=~ô¥%Ž&ËtÛ rvìÞ¡•¥™q]5y6#wF·´ß#>½ {r/ðÂÉ£gCK]5u %›ÅFµèŸÐ‘õtë`S¿Žîc‡:t±17ª%ÏÊ­#)òõ“[—üvø^û\ôŠRV^—2->³jÊsÒÓ§Y4{²v«÷èFù½°¼ô¯wÿ·tòTßPá¡¡Ìæ‹· +oÄ”·Xp|]åŒÛ‚Ñ:ü×;û^°›>êè~MkÙãâg'†‡ß»væè¡#çž“[·ÆÀ‘LU§`å}ÄãŠÕÒ0A3ã®·­Á#.˜2¼»y‘3ÇpS£^Ý¿|òÈýþQ]/Äþ Yµlû5" FÍsn±é:iPǦŠÒÌüU0ô›6Õ+£ûÿß$Þ .‰€ @T¼è /¤´XmÇõÔ¡~•ZºõU?è'8‡ïîæ€ðaÓê•wš,¦fïMOÏyVö¢E±4º®½ý¨®‹Ã”“Â+yw­!D[ÛdÈÆ}ãÅq¢”Þ€Ý5ú8® N©‘vrÍÄ“e–RË~ã…ÿM²Tµ ßhì^ç«C=æpÞ˜ÚùÀTyãz:*Œ”èOo#~Ý Qëë0Ýô/fŒ“5z60µWg¯»B¾^Z?Žü!تºuµUdù™)?ÄÇ$¤‰¶Š-¼.ŸŽmçà“?.—H}²Žãþ9KE×PGEš›ž”/{Bó¼H ÷rXÖÿtþül‘W·Í½ºí×E æ?{½¬Iåœ'O¬\"+±C€¨8_Nm¾)œxXªý8;-êÇŠÍÇô×òÛ#h=ÛîÿÖ}q£r‡éc7¸®ë¸;¶ìEÁTn6ùøëÎÇ–L™¾úZTÙË’2´›´pÉü¡ÍkŠí­KJ§ÇÚ;ï;¯ïæuþ[Ù‹ç’oê²a÷ÚÑÍU©< ù¦sÎ_H·ë¹æiÞÈñô¨÷Ï‹ùÛë8ï½²g8•¡9ÅaªØx^{ÙÄsÄè5·‹vÜs~D|øQÒôê(û±µûìxô ¡ëÀi'‹Œ^!¸?#Þÿ,ie¿ai9îùÏ3¾×â;”¾±¨4Ä»Á%±B3h€J!ûýÁ­Á–lçñË5|YÞÜÅYwdžüOà{|Bçn¶¦~¬Z>†r[ÏüGßÈ.{Ùßïªh:pÕU§9aÿíõÙwøøù§1Ü’–©Ý¬s¯¾ƒ† qlSWßòËèÙyžûäþ$`Ûæûn…g”°œZ㞃]&LcW¯\s‡°4º¬¾û¾ýš™³W™öû­ÒF=¦-[ã1ÀT¤©@Ê&kà°:¨‹Û%ßõ›v¼ü&¹¤åäêXu±ëå4x˜S›º %½¦˜ªÖSO¼íwsÏêU›ý.½M-~)“NŽCÇŒwíTÌœØLµv‹‚"œ/îß{ä\àƒgoÿ§–òŒWFbÝàY!ˆ4M”ûÜà‹þ¬tC×|¿T¹æë¿ñ×w‹”ñì—üÙWÇÖs Ìr)w5lµÆ}gn!xß߆†¾zþ5:>9-3›Ï’‘«¡¦U[ß A£&f5e$?8–­ÞÌÙk¿³—_FÌë'OÂÞ~üó#%=‡)£ ¤®S·ž©y s#UJ=Ãlƒ)ÏøS~½N¶N…=æýüðèÖ½·_c“2 95]cóVí[›i–ýWyÅ0DØ$ …zݧnï>u+¹yŸ‡¾þø5:!5“Ç’WTR®©mÐÀÔÔ´AÝš²"n\Ýöî›Û»oL‹|ñèQÈë±I\–¬¢’šVÝú¦Í›ÖS—.uUL%“ž“V÷œ$ÚÃý jÿ\…Š{ʨï—Ä @L €~L¹Z [ukØŠî:ˆÜR´ÌÚô4k#ÉÇ`«Ô³±¯gCù~¼ìtag?[VJäÞÆÜÍÛºgÃÖ”°Ø•)ÔnÒ¡o“bYY%Ö .‘À_B€¨¸)1?„ã”´1Þ€.ЕCæ§{çN$˜z´pŽgš @T¼ï—7ŸžA¼~7sê§ñ@€øWñÓ¿<¼û:1ýgÄ‹›G¶íº‘TpC£‘Ž"žÁÄà_ʼn<:ºëœW¿_­íºÁ­þ_Î å‡ P™hõÙvec'UŒß 4@% §Yß¼µÝ׉cº—ë<. 6ÐÿªrÝ$ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €èòˆŒŒôóóKMM¥»€ò:th£F订òA€.   +V¤§§Ó]@ù©¨¨ @—t9IKK#@TCÐ @P€ @t9eggÓ]кùB"Ó]T#Ð@ò7™¡é.*=>ŸŸ““CwP½ @ X,™¡³³³M&“¡¨(EoIPé$'gÓ]TSÐ@?eeé•+­è®*™±coÓ]TSÐ@?EE)7·†tW• 4Ѐh  (@€ €h  (@€ €h  (@€ €h  (@€ €h  *^vð–ÛnËÃCby!Ýë´ã¨ Gœ‚BÑðZTçN5è. ªC€>/Ô—ªˆ‡ÊùôÆeêÇ0‚`™{¸h6j(¥ ¥¨ªšÃÒ–•eŠ´>‡“Ídˈ¶0ü௤‰ð{ã»û›ÂöA—í¥+à9‰)ñy,¦Y-tU`‘—Ž N¤²†Ì§šØG7b2f´qWSi|P‚·M€ò॥Ü:öv·ï›ƒ÷Óó®`t)èÐM{¾Úó™ÿ…ØÏ)!§Ð¬{ý9Ë-ú5`3ònÍŽŽÚµ8ÔçTô‹ï\‚`ªײwµX4EW_F°Þôý6F>$SëWW4ÎL¿¿áTBl«¶ñ¢]­\ñO·ßç”ÿ@Ác²Ç­š¾ó!üœ—ÿ{4ÉûípCEÅnLó5½cºµ{I0íÎ ?Y‹Èøžpzý]òG¹qQcŽ¢ÛH =Ò"A€ ‚—ó1裟ï›Ý‡c¿ç_%ßjˆÉر&ýÛäv?§‡†t²zü0‡¼(cd&›ô2éÉÉç£×=í9Ý”•õþU_«;’·24UU’“Þ½Þ7':àJë§çÍꓚɔôbÇFÌíðøbªRvìNä½7®øußÙh6«Ý"6þñë,r–¾zs™æZLÆïUò£Ýh=âsRîe©::üàµ×:\UÊë¥fÊJ1äš· yª½Ïçõ.¿ˆa_6N&¤Ìz5ãf2ÔN­f… D¨¼ D•ýá•“ísQùMõæ†cÆšºÒ6T,H°œä=nyéYF{{XñƬœ/ïìÍn^J‹™7%Üù¼Ö9—»yéYuNýòv2L^Ö­¹gl×üL»~ìáº×F*°&[ÐùؾÛ×]u´˜™çGëåŸIÄ~ÚñÈæøºžAïY¿Œ&K/»{.òL"ûÔo…fýؾ@žk.yj¿ÀBŠ>¢éÕCy72r‹ejXÌÚa0scúãSowí|í”úòÜ‹©ä¢ú¬ëö«­ðéP"¼EˆŠ›øã ™ž™J½¦5™äV¯S}iÖo DEìœ{A®kSgãÜ¥êù\’}ÏcÈ+IE}ó¹ÃϽ²K³™móácÊ´™nÑfí;|Þ-¿è„õ4 ;“kÎõÐÓÊ}“–mêÿþ‘óùS—(»˜sþcîv'‹±æRä*Ù:úóÆ+Z’üÛ’ y«AäÏÖo1Ç÷¼Ú°öCHjü“Þ_l$€ª€"^òó_ t´Õõÿ:n8+*1"ï‚fCÙüëXzmôõò.¥?xõ-ï‚¶¹’°Ïš©¤d¦A܉%x?b² M™‚uÉ×h¨ž¿r–’¼Aü N6Ÿ/BÙq?có.ÔjT£àXzÍk°ˆdn±PjòýË_ÏIaíÕ4€¨¤ fù±yTø›¯[¦‘?Òì¸5ÖUUEðnÊϸüÒƒî¯7æ÷÷2¿ ef2 ³ùcœËP°þ¢k$+ú½&>çóíO{v¾ò-Ï-×b@C÷¦øh( Þ%DÅRךêÓkÊ–ôǧßíö}ã=ùÅÙ°ÉäJ;®¦Óf5h¥­¦C±ñýEj:_]67¿r?}q𠇡 6ØQ]Ÿ¼‰ ¢B’Sòo%x?†åuKÖÔÓ$xRêJ5 "’|¬—©i| ¹¼2¾§Gfð’o|¾ÙçÝÝÈükjYÓÐe ¶‘"Õ´Pí @#$$$!!!**JGG‡îZàwÑÑÑÚÚÚêêêæææt×ÕTî¸áæäÏ–˜¸³þo|}ß_ýpjíí¤¶F×»ë · BCˆ¬«¡û^éN7cç| Ÿ5öáé$‚Ѳ͈‰úcÛ1ßâq®?Ùô@wq+i&'ãÚŠ§r×*Õs¼¶š˜²+[[«‹ñü‘s-Ä÷™Þ\óÜ2o/õæõT¯wÑäߢ]Ëy´É£vlLb "èbôîÝ;999--M^^¾è—¨@;—žž®  P£FÈÈȲï I2Zýg“?­cŸ~ñßý)F•AH)¹ù6?Ô28˜;£Ñm¦²?_%çN'¥¹dGý:²ì¡»[ŸhqûbÒe6þ~&*òq‰rWU{ˆíÖÞ"žFP²j“¼jowÌàÇÏ·ðßQ_.õ]ŠŒ¹<‘(˜²š`((Û¹XÚŽ0éßVQ=Î!@#...;;›¼ššJw-PŒ”””¬¬,º«bjZÌÜn h(4³¼ñ\e…G¨ÿ¥øO¯² y…æ=Ì]fîX?÷ýVƸáéWÊÛ½C|NE¿~CFk¶n3Ý,æ×Pc•ú Kª3ªó´»–}zˉN”ê¹ ÛË—–Néò62`›5Ú»GŒP½ @CJJŠËåòx¼2:0 &“I>GtP"ņ†ËN.+áVi©>äOI÷–î{Óí··YsëÏ|ka“mÕ:Šßºô»LëɃ'w,hó£÷…pòÖ¬>g€¿‚] 2:“šî* xä^`÷†îBJÆ(HÇ|7z®ç¼9ûê”S?¾¦«oxÐudÝäuÚ‚›ä5ìêãà¯àm´ C¿%“%ÎïV¡È}š¯oŸâ;¨È+̵ô]6=S/fmäOâ+™ž­ÚkóÞÇ<‰Ìýÿ±]fÝMMŒ-@u„][jÚÎërŠÊtRÝe¤&Íì¢ÉÉɦ»€RM±D^q±½Å’¿^“á#˜Ú¹&›H¼M°dŒZë-Ý<¨ÏbÚ]×@4€øüLKŠªu}‚ˆË€È (@€ €h   *àp8l6>Ô "ཪ‚† .\¸pðàÁˆÑ ix—€ª`Ïž=^^^Ë–-óððpvvFŒÉÁû TíÚµ»qãÆÍ›7ɽtéR2F<˜…óÈ€ @@ÕѾ}û   AŒ^¶lÙÂ…  „ â… êǪªªtWUœ F’1zÉ’%dŒvvvf2™t×U4HDRRÒç<Ÿ>}"‡‡‡ šl6;&&†îê Zè˜G£—.]êéé9pà@Ähø{ÐðWÒÒÒ„ù8< (s8œºy É#h*++Ó]2T/‚}íÚ5oooAo4b4ü%hIffæç"„Y955•ŒÈu ØØØ.Ô¬Y³¤U}ýúõçÏŸX;Ñ9Ï•+W/^,èîß¿?b4”4ü";;› ¸fåÄÄÄ:uꃲƒƒƒ¡¡!y¡V­Z”ÖשS§Q£FI¨~€RtÍCÆhao4b4”4@5Åår¿}ûV4% .|ÿþ½víÚ ܭ[7ƒ<::: ã/4))©{÷îC† éÝ»÷Ê•+Åò‡P%ˆÑ—.]öF;99!F€è ª8>ŸýéÓ'aDˆŒŒÔÔÔŒ¾ wèÐA˜uuu%tЬ¬,GGG+++ooï°°0I<M¸Ž<ßûZ¶ïô†-èàM©ŒìSí÷9„¢áµ¨ÎjT@yÿ»ßÍÍ-+++ à_éáãóyƒ)Ž˜”ñòÃéh1¬§bËîvdp¢äª!Ĺ…+@}ú F@QЕ/úž‹Îþa¸rÞœ¬“ó¼Þ&š9¼ ¸°Î~ò#N{æŒå ‚w¬yü.'§f1Ê~Þê–† ü¬¯'¯ì\{ÿÎÄt>!S»Në‘Ý&̳¬£€O ÉÉÊÊúíH>AVNII):•²µµµ`Èr)S)W~~~»víºuë–šš­…0Ù‚žÐØÈ]Z¿jd©ÄçpÓ÷uº›[UçÙ/o'ÃäeÝš{ÆvÍÏ´ë÷Ç®{m¤‹“êïRò2Çê^óéaTó˜ùÊ‚¨9ÿLŸñúÅýq’÷¸å¥gíía=Ƴr¾¼³7»y)-fÞ”pçóZçJyˆÜ2¤ò‹|lßíë®:ZÌÌó£ŽõòÏ$b?íxdÓ©‹¼ãŸed&,:˜¤ *£V¿ñ¥›–Me¹a^M'çÜ{±ïuÃeMJÞD%=ŠmÚ¶±yé¹¶YÐËÖí”Î×·v ‚®?:é€Éí±ÒÅlaFeê~þ ™¡{öìIÆhoooAo´½½=b4 @WfLiA7ÒÏk§ç_{›;ž‘I0ddý@1öŒK–6ïÓ¸­bXÐÓÄíû\ãe¶PËýHË~·bÃðáò¢ª†¡FƧw_—ùÜ êàjgCšÇËV999ß¾}+z$Ÿ ('$$èëë ³rŸ>}´µµé.Y"Ξ=;þüÛ·oëêêÒ]‹Òô ¦ÍTÉÄ`F}ó¹Ã'¯’êÒlf[™Ü ¦L›émÖÞ¸ÃçÝò‹NQ¯ft™Ë”ùܨˆýs/ÈumêlÌÊ][#ŸK²Oây y%©²ËÐ/XS͹zZ¹oÛ²í]ôTýßÿ r>ÊâR¬?U¶¦×Ãa^ä¾rNÎéÑ?iCy‘Ì'2ÞÅòJ­·øGÉ4øzðEîÍš=ê5WʽÀÖ«ãf͸Äð¿èø1uþÜÂvsW.d\îÝ»· 7Z06šüM6£ºRcå “x-ÜzÃü€IúŠ\‘þ\p%?F~Tè|צR'ád§…Ëoq“ùoq“ XÜOw—æ¥giÛÁG.ÛêËp£÷m0êeÆSkO´Ø:D¹wU(.—ùÛeÁTÊd&LŸ,œJ™¼ ££ó¯B'ynnn.\¨W¯ݵ!­b¥—Ÿ6³"¾å]Ð6WR,HDL%%3 âN,Á‹ø“M(–½LÙ:+*1"ï‚fC…‚ÝS–^}½¼Ké^•õZ¾FCõü×KIž ±?‚“Íçÿ°üø[Ï'M{vôiæ¯ ð¹%Ü¡ôGÉúþó{Þ•±¾g|½Ë縈¬‚]d W ÂMîzzzzçAŒ¨æ «†zgŒ¯£D~h±XDzÁ•Öíçõ±Õ:Œ7\që=Ÿˆ¾œ6¼bÜ•û¯roj9«EÞ¡R,mça[Ô¿üä0d +{§‘$¦Ræca×2™ž544c”+f*åÊâéÓ§ƒ>t襥%ݵüJš-ógìù5PæwÏþ–J^¦ô8šwßüŒË/)ëŠXFÑݯ²Â[Nøë¾Þ!÷“k×ñð¬×L[*ëÑ£AKE˜x£„Ga°˜‚\Ì6Ñh%[4#³ÔÕ•Y„`7 ø-\ù‘ÏCŸ>}ìííÏœ9CÆhÁØh2FÓ]УZÆWRõ jÉü~¥R}ùŸƒ YõÄûd‚øñ)CÈÆ=û!X¤n킸,£jÞKµ¢êý— §R.š•…S) ´lÙ’–©”+‹>ôìÙÓÇǧcÇŽt×R]u}ò'ˆ¨ä¾ºl^ìãýü–{!eXS[Z¤e"Ëz im5‚ ïñýEjzþJ¸޾8ø†ÃPPìXöC| üÇñãƒÞå¦g‚Ñi“íb'r­¼ððÇ”WSô¯¨¥¢•W§’uÓ]û´ÿ˜ð%ûÅ߬½’ c´ƒƒ™¤OŸ>½`Á‚E‹!FTOÐU‚”ûÏ.·°·‹Ïã º³ÇôûÃDè;«ªŠN¥,ÌÊE§R&5nÜØÞÞ^0ú‚ö©”+‹ˆˆˆ®]»._¾œÜtt×R––îØvÌÇ·xœëO6=Ð]ÜJšÉɸ¶âéƒÜ¥zŽ×VcL–‰ÊJ•ñî;—Ð/æM•][o¸Bd] Ý÷Jwº;çkø¬±O'Œ–mFLÔ/ó!DÁø¥ “ÿ_žÏÉýï‹\¾*!ï^J*¯Sd°kë4#ž½$O‡ùVk°“ÿ3Ækàý+™2õûZïœZNfHÆè¾}û’IúÔ©SdŒŒîÑ£Ýu@ÅA€®ºR_Fþä4Ï}ŠyÉobRs¯cÔ4RdlõÆ*‘Bɾfó-òæwÍŠ½²éÑç B®QËANêUhGrr²pèEÑ ,K”ÉÌ'˜øâŸJ¹RHLL$cÄØ±cGEw-"`+ÝÝúD‹Û“~,³ñ÷3Q‘Kü2k±ÝÚ;ï }e/Ã×4#ÿ«’9DÚÆ6Ç®6­5ïdÇ!º¿ÆS)%7ßæ‡Zsbg4:°ÍTöç«äܱRšKvÔ¯#Ë.ë!²Eøc˜¿•1cEݦDÜ3‚w¦Í ¥Ÿ÷£ãÛ™uJyy=…wmì§Ðæ}¨n.i÷Mö·{þ6é󺇖[Êg¼‰ÿ”šÛ5í¸UYž(ýÀÄ*ˆŒÑŽŽŽd’>yòäܹsÉíéé‰ PM @WaÏ®ì¿n5§›"#)üغ¯yWév°R`Ÿ´ÝZ6 ¾½%8WÞïÚÙPŽsâÄò9¡©£ñŽVƒi®»|222~;’O 33S0w²p*eAhVUň1KOO'ÓCÏž=çÌ™Cw-¢’1nxú•òvïŸSѯߙ–­ÛLwà‹ùÃ5ÔX".èåÐzóÀ«³ŽþLËNý”PK¦¸·U…f–7ž«¬ðõ¿ÿéU6!¯Ð¼Gƒ¹ËÌë³E,£,¿—Qìñ©c™.3_Ýüšöà³ã˜Îç–ëgïIé:åkD\â‹hn9¾ Pnk}ÿ‘Šç°£7~¼ N'ÿŠfNõç.³pj@þ¢¤ü*ˆŒÑNNNEc´···Ýu€d!@W]¬º²—íf™h²¾DÏȽFc´cϺyjFm==éõ…û `€ö CõŒOÓr¯·²Ÿ7¼fe8€žËåîÚµ«hVNJJÌ'œJ™¤¡¡Aw±ÕBvv¶£££™™ÙòåË鮥XÒ}oº;bIZGgªùSêK]†!¯:þÈ€ñGʨ@±¡á²†ËÊóÅ/kný™o]Fý[Ýèßê—»M´û6QØpAùQª-L¶\2Ù"Z‘Õ“Éìׯù/pâĉY³f z££ª0èªKÖÚiçŠ×›çßyD¦g¥Z6ãûÎõ6UVÈi¸pÆ“‹;V?¸÷$þS!«[§õ(» s-ôé;W'88ØÐаÊO¥\)ðx<ÌíÕ£û÷ïïää0sæLAot×®]é® ÄºRS±ðáï*éF>OJЀíƒ3CÆ`€Ãê’ªM²È¬æëë[örP!&Nœ}á«2| IdŒ8p ™¤É=mÚ4UUUOOOÄh€*ºÒskžßáçüË×§üô¢ãÏëKZ¾¤ëJçííýèÑ£ëׯ“{5t×ð¯Æè£GN:UMMüOéܹ3Ýu€x @WndêæÝ¢—ù¹"ûO»tÌD¾ÄeJº º;v|ØÝÝüAŒ¨ì +±?S¯ðš]e-YúõeºtéÒ¬Y³._¾\§Nºk¨È=dÈAƒ9r„ŒÑµjÕ"ctûöíé® Ê({øðá°aÃÎ;׸qcºk¨LX,– F:thìØ±ZZZˆÑ•4PÖ§OŸƒZ[[—½´hx<þYâZÀ?ŽŒÑä.¨³³óáÇÉ­££CÆèvíÚÑ]ˆ (ïÝ»÷ºuëÄ;-WTTšŽÎ1®àßÇf³1úСC£GÖÓÓóööFŒ¨ @Tqqqdnž3gÎ!Cþ~m|>¿Èe"3“û÷ë¨tÈ=|øðÁƒ|ø¾}û Dî/y{{‹q"v  Š·gϞݻwß¼ySEE…îZª£Ã‡Ϙ1ÃÙÙ9,,¬¤21»¹¹uêÔiĈd˜öóóÓÓÓ«à:¡ÂHKK“O÷È‘#É=pà@333///+++ºë¨Ž  gÏžõðð¸}û¶®®.ݵT;ãÆûöíÛéÓ§E‰GFFFAAAkÖ¬iÖ¬Ùúõë‡ZE]„1zïÞ½ýúõkܸ±··w‹-è®  zA€€ßººº^½zµ^½zt×R½ðùü;wzyyMž<ùÔ©S¢Èb±æÎÛ½{w2=Ÿ9sfÇŽêêê-èEÆhr/ËÅÅ…ŒÑNNNM›6%_6Í›7§».€êº \.ÇÉh¹ÓŒ›“C>tWQ-“!ìÈ‘#äG2ݵT/oß¾ussãp8AAAå;(|ÊȧÏÃÃÃÜÜ|×®]={ö{‘ðOÆèÝ»w;::"FTè2ðy¼§×OÐ]@ùðáCïÞ½Éìõç!k 9dh^½zõÆ.\8aÂ&“YîUÉÈȬY³†|GŽyöìÙõë×+((ˆ±Tø‘1ÚÝÝ}ôèÑ{öì!c4¹ûäíímiiIw]Ut1ÈO/6›Íårq‚´ƒÁ`±X˜m@""":wî¼råJ{{{ºk©Fž>}JFmmíàà`}}}±¬³]»v¡¡¡Ó¦MkÚ´©¿¿¿XV ÿ2rßI£}}}ûôéCh///Äh A€.Fvv6‡ƒÑÿ(r¯†|v Å.>>¾G&L1bݵTdÄ!îÚµkÅ~䟒’Òž={Μ9Ó¯_¿‘#Gz{{c&ïꀌÑ'NtuuÄèfÍš‘O½¹¹9ÝuT5ÐÅPQQÉÌÌLMMUPPø›ïRÿ5䎑÷eÝ…”ÇKKKSTT”••¥»–*%==½W¯^={öœ5kݵT7oÞ3fL‹-ž?®©©)¡G!#T«V­ÜÜܬ­­ÿ÷¿ÿ5jÔHBÿòrÒ¤Iä ÌÇLJü×&_fä®b4€!@ãÀŸ?Žˆˆ¨bx]ºt)##£oß¾tòW"##k×®]·n]º ©:È=+‡Æ/_¾œîZª…¤¤¤Ù³g_¼xqÇŽpœ™ÎOŸ>íççשS'riúôéU©_JAÆèÉ“' c´•••··w“&Mè®  *@€.F×®]é.A"ÉOn777º —Ë5j”¼¼üÎ;10¦YvÒ¤I½{÷~ñâ…’’R…=.ù,wèÐaäÈ‘çÎÛ·ovA«99¹)S¦ïüdŒ¶³³³±±ñôôDŒøKÐÕæbbb.^¼Èb±è®¥Šûþý;¹µŸ?~èС¶mÛV|dh \¿~½••ÕªU«ÈH]ñ5]„1šÜUîÖ­[ëÖ­½¼¼7nLw]•4@õåéé|õêÕJ=2¾RØ¿ÿìÙ³]]]ýýýiÁÏd2gΜigg7lذ3gÎøøøHnø5üƒÈ=mÚ´qãÆ bt›6m.\ˆ PÐÕÔ¶mÛ>|ïÞ=eeeºk©ÊÂÃÃÇŽ›pùòåä(®F=|øPpT¤0kau#ˆÑäËrÇŽ]»vmÛ¶-¹/L(A€¨Ž<¸jÕª»wïjhhÐ]K•Åãñ6oÞ¼lٲٳg“y…Íþ‡Þo¥¥¥W¬XÑ»wïáÇŸ9sfÆ 9 þòòò3fÌ?~<£»tébkkKÆhSSSºë¨þ¡7t¨.\˜3gÎÅ‹õôô讥Êzùò¥«««¬¬ìýû÷ëÕ«Gw9ų±±yöìÙôéÓ-,,üüüÚµkGwEPÑ1zܸqÛ·oïØ±cûöí£D P½<|øpĈçÎÃÀG ÉÎÎ^¶lÙŽ;–.]:f̘|n…]»v?~ðàÁÎÎÎdÍ222t|Ìš5ËÝÝ]£IdŒ611¡».€4@5Ö»wïÇ[[[Ó]KÕôàÁWWWccãÐÐPºËUÏž=É‚Çß¼yó4mڔ‚M¾ ¶mÛfkkÛ©S'Äh€’ @T?~$Óó¦M›:wîLw-UPZZš‡‡ÇÑ£GÉ-Ü¿ºË¡L]]= €LÏ]ºt™>}:¤0³aõ¤¨¨8gΜ &lݺ•ŒÑäëaáÂ… 4 ».€ 4@µgggG~.:;;Ó]KtùòåqãÆ‘iãÅ‹jjjt—S~C‡%ÿ ÁùVöïßoddDwE@2FÏ;wâĉ[¶li×®b4Ào ª¾¤¤¤Î;1büøñt×RÕ$&&NŸ>ýÖ­[>>>dÈ »1ÐÓÓ»víÚæÍ›[µjµtéRœ»´:#cô¼yó„1º[·nõë×§».ú!@Tqééé}ûö%?üÈO>ºk©jŽ;6uêÔ†……)((Ð]ŽØ0Œ)S¦ûäN×Ù³gwïÞ­¥¥EwQ@›5jÌŸ?Ò¤IänUÛ¶míìì,X€ Õ4@UÆåruttÈO>ºk©R"##ÝÝÝ?~üxòäÉ–-[Ò]ŽD˜ššÞ»woéÒ¥[·nurr¢»" £ÉÜLÆèM›6 bôÂ… ÿÙ)$  Êâóù...<oïÞ½ÿødj•¹U}}}=<<&NœPµÏ‚.%%µhÑ¢=zη²eËœ·²šSRR"só”)SÈmccÓ³gO2U#FC5„ PeÍž=ûÝ»wׯ_¯Ú!¯"½ÿÞÍÍ-33óÆffft—SA¬­­CBBÈ—S“&Müüü:vìHwE@3AŒž™˜L&ÝU(òU´uëV{{û#Fôë×oùòårrrt4SVVöòòš:u*£É½,òåáááa``@w]  òõõÝ»wo`` ¾p‹ÐÐÿ³w'p1­oÀß™©¦Mi)’"[Š¢¬YCÙ)!*¥²TÚ-W$ݤ"W²ÝìK–¸dMè&)år»¡PÚ´©Ô,ÿ·üC´Msfy¾Ÿûq§™3ïyf=¿÷sÞ“jgg§¤¤”””Ô­[7¢Ë!ÌøñãÓÒÒœ tèÐ!¢+ÄcÇè•+Wâ=xðà©S§BŒÂ4‚æÜ¹sëÖ­»{÷®ššѵð½êêê7îß¿?88ØÆÆ†èrˆ'''sìØ±É“'»¸¸øúúŠˆÀv Ž;®_¿ž=mhh8mÚ´Õ«W so<øâ@ Ü¸qÃÁÁ!>>ëi»;wî,Y²DOO/--MEE…èrxˆ¥¥åðáÃíìì† vøðáž={]à ì½råʰ°°AƒMŸ>b4T IIIóçÏ?qâD¿~ýˆ®…¿•——ûøø\¸pa×®]D—˺téallìïïïää3½699¹ 6°G£~…JAAA\\…B!º>S[[‹7Þ;wîô÷÷_ºt)t?8NVVGçÓ§OO›6ÍÁÁaÍš5¢¢¢DxŽ’’Rpp°§§'ŽÑ °¶¶öòò‚ øhøN$)))W¯^#º>“””dooß­[7üÂÇíjæÌ™&&&øÙ666>xð ®®.Ñ^ÄŽÑø_vŒöööîܹ3Ñuð+ à?ááá'Nœ¸wïÌRÜ"UUUk×®‰‰ …ÙÖ¸£S§N/^ŒŒŒ5jÔêÕ«W¬Xãý QÊÊÊ!!!^^^8F÷ïßþüù8F«ªª]ƒÝ¸êêê¼¼¼ÚÚZ2™Lt9œA¡P¨Tjvv6Ñ…p ~™$$$„*G=ztË–-‰‰‰JJJD×ÂO®]»¶téRccãôôtüž!ºáâàà0fÌ›ØØØèèh˜¿ ü ;F{zzþþûï}ûö]°`î†]߃Ý8ü‰ŒŒd0L&SÀ~"ÇßMD—ÀtÜÃ9rd\\ÑåpÉ… ¼½½¯^½ û4߇V­ZuëÖ­Ý»wOœ8‘èr„T=nß¾û~¡¡¡óçÏ'º"À»TTTð›×áݧOÜõ—ñ•D×ÀÿA€n\AAAMM g4­®®Žèr@ãÈd2~urrrˆ.„Kîß¿okk{ñâE¼E!º¾qæÌ™åË—Ïœ93--MZZšèr„…Bñññ133ÃéùüùóŠŠŠDx;F{yyéêêBŒ<tãÞ½{G"‘èt:Ñ…€_a2™øeªªª"ºnHOOŸ:uêñãÇaR°fŸb—çÏŸŸDFFâíè¢E‹à<üHYYùܹs055õòòrww˜ øA»Âß;wîôññÙ¼y³ŽŽZµ b4àÐð"&“‰³ ¾‰ðìííûôéóäÉ8Û¿[¼xñèÑ£ñ;ÿâÅ‹ÑÑÑݺu#º"ÀpŒÞµk××íèèˆc4œ/ ´+Ðð"ìììk×®Á.¡?SQQáëë{þüùðððiÓ¦]à šoܸjdd„#5Ѿѵk×Ý»w㯅ÀÀ@£—.]êææ1´Ððœpt¾{÷®¤¤$ѵð¨Ë—/;;;;6==½cÇŽD—8‰L&ãäĉ,X€;H‘‘‘0[h>£#""|||¾Æhwwwyyy¢ë‚4¼eïÞ½QQQ·nÝ’••%º^TXXèêêúàÁü,Á¬Ø¬oß¾>\¿~½žžÎCS§N%º"ÀO444ðw©ŸŸ;F;99áï ˆÑ€ƒ @ÀCΜ9³aÆ۷owîÜ™èZxÑŸþéááammýôéS ¢ËíKLL §Ÿ)S¦,\¸0666,,LΚ8ˆ£}}}7mÚ¤­­íìììææ&''Gt]@@€€WܸqcéÒ¥ñññZZZD×Âsrssœœð¿8H 4ˆèr÷?yòÄÝÝ]OO/::zĈDWøL·nÝöíÛ·zõjvŒvqqquu…]¿@A€€'<|øÐÚÚúÔ©Spò¼ï0Œˆˆÿ+Vœ={VTT”芷IIIíÝ»÷Ò¥KóæÍ³²² €Ó³ƒ–bÇh???üþÑÒÒZ¶lÄhР Þ‹/¦M›†#Œ®}çùóçöööøÂÝ»wuttˆ.iòäÉ©©©ÎÎ΃ :räÈ€ˆ®ðŸîÝ»ïß¿Ÿ=ݳgOvŒ†N@+@€€`oÞ¼;vlHHˆ……ѵ𺺺àààíÛ·ÿöÛoNNNpZ €)**ž8q§çqãÆ¹»»{zz ÛA+hjjâ½qãF£Ù;u@Œ-"N˜0ÁÍÍÍÚÚšèZxHrr²šš¾ ®®Nt9€·ÌŸ?äÈ‘‹/¾xñâÁƒ{ôèAtE€/á}àÀ¬¬,öhôòåËW®\ ª‚f‚ a>~ühnnnaa4ѵðŠêêêuëÖ=z4$$dÞ¼yD—xT×®]¯]»¶cÇŽ¡C‡,Y²NØ ZGKK‹£Ù£ÑË–-ƒ š4Ĩ­­ÅÑyàÀD×Â+nܸáèèhhhøäÉ%%%¢Ë< 'ftÆÏžäî?þ€s¹ƒVÃ1úàÁƒ/^¼`F¯`éСÑuÞÐéôùóçËÉÉíܹFΰÒÒRëׯïÚµkòäÉD—øFïÞ½p_4<<|Ö¬YDWø˜¶¶6;F³gêÀ=´åË—CŒ‚ ð—rqqq\\…;w?!S§NMOO‡mh)QQQÿI“&±‡¢qŒ†£Á@[à}èСçÏŸoܸÇhWWWü%--Mt]€·@€€ÛüüüRRR®]»&&&Ft-ËÏÏÇ[&œ›cbb† Ft9€=~üØËË«ÿþ€Ó¼ƒ6ÒÑÑ9rä;F÷èÑÃÍÍmÙ²e£ÁW àªíÛ·Ÿ={öÎ;B>ÔÊd2£££}||–,Yrøðaqqq¢+|ORRrçÎS§Nµ±±™5kV`` œï´;FÿóÏ?6lÀ1ÚÝÝÝÅÅb4@ B¥äö¤àÏl·+'zI]Ž@;zôèÖ­[…ü¹—/_:::~øðáÚµkýû÷'º PÆ—––Æ>ßÊÁƒáÄï ízõêõ矲c´––ÖªU«ðLJJŠèº‘ @²˜¢†¤L9YQIfkO±±±^^^ׯ_WSS#ºÂÐéôíÛ·oÞ¼ÙÛÛÛÕÕUD¾‚çÉÉÉÅÄÄ?~|Ê”)...¾¾¾ðNmÇŽÑ™™™ìÑhˆÑB¾Sø“ÁDä6Oá Ó'ðUGê?wïÞ½%K–à Ý»wo¢k!Lzzº½½½´´ôýû÷µ´´ˆ.¸¹sç6ÌÎÎÿ{èÐ!mmm¢+‚@WW÷رcìC =<<œœœ$%á×[¡š0òm;|Šf¯÷§3¾¿=/é3óÌeéµ3RõX¨“±åïäU ù‹-|ƒ‡hJ}I×ÌO9gþÚr?áaqQ»h˜,šà⫯hdZÑ­»û‚îܼý®¤¿I¤ÔMLó5·œ /ìǼµŽÓ§OÇß¹FFFD×BŒÚÚÚM›6íÙ³'00ÐÖÖfîÜÑ¥K—¸¸¸ˆˆtà½8¢OŸ>ø+ýéÓ§6l -„ @ó²U”u¡ôú9¿ëÏKê¯B$ª8;ÔæÙ¿´\Loj¿áÒé·SJ/?3PžŒo¬}±9láê—4|QNIS©:ûÅë›"nÏ>rm¬æ÷ë¡å쉰qzZQß~õ>’•ÏßçÜNÜqûу«·¹¨@†n¡¬¬¬I“&íÞ½[h§HLL\²d‰¶¶öãÇ;wîLt9@¸àÄììì** §j¢‹¢oß¾'NœHOO߸qãÖ­[===—.] ‡® Ð|„Bf”\iæwr¹º4ªÒØW2ó%§úÙE´â3cÖÞ¡—Ÿ8aC =û^+=‹œwìêHu*=/zלÅÕ gCNÞùíI+è9÷7,c¥gÝ ‘ ÓõåHŒÿîrèù§$·#W§º™«‘¹ýÀùX~~¾™™™ŸŸŸpžß¡²²?öÓ§OïØ±cæÌ™D—„WÏž=6oÞl``°mÛ6KKK¢+‚£_¿~ìíïïìåå1Z@€æGZcW9iÈPp¤¦ ª/WµèÇ¡‘í¤¹ùοL”w'ùãBé¿îgÖß :Äs°:_ ¨Z-W|]J#‰kŠ~Û4£èÊÝTzý2FþãÊÕ‡s²œ–õ­Ã ²˜uÿ^¸óqʼðhó”••?ÞÎÎÎÉɉèZpõêUüÀGõôéS999¢ËÂNDDdíÚµìó­œ?~÷îÝð¶„cô©S§ÒÒÒ6lذeËoooˆÑ 4Õî®BýþJm¥Ÿ‡†Iâ; Ëúý‘†Ä Ÿ|`/Ò­Ë—¸L•Ó›òeÓQÚ°ZÁcö²:=¨_‚2IªGg9”U‚˜ï3ÊêPØ‹£ªªªÌÍÍÇŒãããCt-ÜV\\ìêêzïÞ½½{÷Ž7Žèrø?ƒGùúúöïßÿþý¸‹KtE@ à÷;F¯_¿ÇhOOOˆÑ‚ 4•ùq ˜Ag2¿\f2è Ö+R3?ßÂDÌîösLf#(0úÜ4ÍÊÊJ]]=44”èZ¸íرcîîîøá§§§ÃO€‰‹‹‡……áþ­­­íäÉ“qÊc¿gá}æÌ™ÔÔTüóòòÂ1N%` @ ŠÊŒ·¥4Éú”QþO~eýu$…Ò"HD±_G„**ÏÊ©edMöü©à¯íI¯ª‘Dß!–c6#¢¢/—D¨ôyV SŸ=34³âùÛúc¥ËYÑï× ¾ƒ;,‹-¢££…êÿ·oß:;;¿|ùòܹs†††D—À¯˜šš>yòdùòå=mÚ4œlÖ®]+* #€ÃpŒ>wî\JJ ûCoooüf£RØðaÙÀ >J7ñ«½n÷R¦¼Î{_]’ÝŒÉÝê÷áé1|Ò¢ß^Óœœ£zSS±:û¿õ×Zø.T Ôæ|ÓŽºÑš) —>­Ì¸l«œØM[ìÃÓ‚²ú[:šEYP‚üükAAAñññwïÞž…_¼x·4íÎ;Â|šÀ§fΜibbboooll|ðàA]]]¢+H__ÿüùó8F¯_¿Çh;;;ˆÑ| ´ 7š¹gó³~ I8=˨;M÷Y¯+Ã>¬Dí½vÕ±^qÁe—!q5 “Å]|ªãWû]K"êŽN'{ÞŽ ¼{ónÞ«§IÉõ;hö³)ƒ¤`»_ŠˆˆˆŠŠÂ9RVV–èZ¸‡æ­,k×®uqq!“áýøR§N.^¼9zôh??¿+VÕÎW€kpŒŽMNNö÷÷ß¼y³¯¯/î¹ÁOv| 4é80’¹÷g72¢ê–sv[ÎiüfµûœiÁs¦5¯Y%Ó1«ñm(Vøœ:u*00ðæÍ›xcLt-Ü’’bgg§¢¢’””Ô­[7¢Ë ­ÆŒcccƒ#Nttt×®]‰®¦Aƒ]¸p£ƒ‚‚pŸÍÖÖb4ßÍg}‰LþfJ fÕI4‚ôãõ?[žS×¶7n8;;ÇÇÇkii]K»«®®Æßû8a„„„ÌŸ?Ÿèrà˜=zܾ}¿± ¶nݺ`Á¢+‹£ÿþûïõë×BŒæ; ù N±_ókÃËø$ÉÙnWNô’üé2íq°=|øÐÊÊêÌ™3ýúõ#º–v‡ã…ƒƒƒ¾¾~ZZš²²2ÑåÀa ÅÛÛ{âĉ¸s¡¨¨HtQ@` <øÒ¥KIIIìÑèµk×ÚÙÙ]hÐ|ã»äŠ/n¸³^Ãë¶|¯oûc$Ïž=›1cƾ}û† Ft-í«¬¬ÌËËëòåË»ví²°° ºÚÑ€’““qšÁ"##'OžLtE‹N§{xxäåååäähhh]N{‘‘‘ÑÖÖÞ½{÷õë׉®¥jjjŠ‹‹UUUGŽéììLt9\šoü˜\¿^³·©%9{=øêÍ›7&Lø@ëââbnnž‘‘¿è‰.€vG¥RñG{Ê”)‹/Æïÿ­[·JKK]”0ª¬¬Äöºº:|ùþýûD—ÓîRRRˆ.¡eH$“ÉLLL„ h–ÂÂÂqãÆ­ZµÊÚÚšèZÚÑû÷ï]]]ñwúÑ£GGŒAt9p~Ï?~üØÍÍMOOïСCÆÆÆDW$tèt:Žh"""ø“ Ã:¼¿:4è*¸ 4­QQQann>}úô•+W]K;:xð ———­­mTT”„„Ñå@™ýû÷ÇÆÆÎš5ËÆÆÆßߎôâ& …Bg!ºÐ8Ü«ÁéY_ дXmmí´iÓôõõ‰®¥½¼zõjéÒ¥………W¯^ÕÓÓ#ºfaa1dÈGGG##£Ã‡÷íۗ芄;CýSDhNSÅ˘ ýÓ'ôå7)))bëá>д þŸ7ož‚‚Bxx8ѵ´ ƒÚ¦M›V±Ï ÉÊÍÍ ÊÏÏÿøñ#/̰.++‹ƒÂàÁƒq€ÖÕՌ󭔕•ÕÔÔ())YZZNš4‰èrš@ѱ·'º €jŠ‹_ž<ɨýþ\lÂ6´“É\¾|ù‡âââ( Ñåp^FF†½½=•JMHHÐÖÖ&ºœvtóæÍ¢¢¢7oÞ¨©©]K‹¥§§WUUü9˜F£á¾Üµk׈.äWp «©©‘/..n]#¸“SWWg‰ooøí„ŸäV¿L!Ð4WXXXllì­[·ug¯¦§§«¨¨]7|üøñ» |§–Æ~p…=Yëàüûr<5ö,˜,¢¢¢D߀ @³=zèÄÄD%%¥_/¹eË–àà`öŒKT*•;å †C•¢¢âÉ“' ЊpV`°ÀÄXíg_2™Ü–pgU\\ük'‘d<“†“ÉÉÉW |4M‹õðð¸uëVsö—MHH(**úî°qÀYì§çª7oÞ´.@·eX4îœà—‰ƒãšøó·`§(#]¾Œà£@+@€  wïÞutt>>Dמ€oÔÖÖZYYuïÞ=$$„èZÀ‹ @ðL&ÓÖÖ_ˆŽŽ†£ÓÐ(ÐüŸ««knnn\\\[ÎÁºi Z]Vj™ ‰ŠW¼É¢Ój9Þl``àÍ›7ïÞ½+))ÉñÆ 0 @7­®¶öñ3iw/]øŒA§Õ}úÄÙ6#""8pïÞ=YYYζ ºqEEE þbÖÕÖàÿ«üREEEâDPPP||¼²²2GJ€ƒÝ8F¡P˜L&ƒÁ ºðS$‰L&·ñh¿7n,[¶ §g---Nºq t:Ž$ãq8:ã—I\\¼Õ-<|øpîܹ±±±ýúõã`a` 7þüººº’’¦‰.4¢ªªª¬¬LEEe„ ­k!##cæÌ™QQQC‡ålmÑêªnz›+«<ÃLV &få0ЛÌBt ½yóÆÌÌì÷ß777'º€Ó>ý÷:ò`NIñ !@sh Œ ÇŒãééimmMt-çÑ_ß,(!ºÁ²²2ssóÙ³g/_¾œèZY]yûXÖ™›E/ è‘:t‘1S{±™¬4 Õ½üw‰Sv>BêvÃwÏ–d…&ÚŸøõ©QUééK|n!>êÙ‘k%ù5Hº«âÄ…:Cž>ò8_ƒD7œ0\óÎm^ú?õ˜¬ïòvÇÞÜÔ÷tŠ\Ç öýG‰<;‘±ëdAn’îÖi¾{Ÿ©Ú"Ÿ‹™´ÿ®ÿwèl^jvýÄ­ªò¦³?WÅ,ùÚà°@í‚ÈÈW‰ÿÕÒDÅuÇöXá ÖMÑó^»,þç»ÿ2NÊ$ëëŸ T’"äùL p©­­1c†¾¾~@@ѵ ­*vÍýˆ ŽDzJr¥R_•\Úñðeí°àiÍ;«ãÓ-Ciõ—%$äå§7ýýXµ®þO ™BB$2;m•&?÷9\&ÚI„Œèô¥—·.º~äŠ!TryáÖ%)×ËPéÜçKôåUVn’”Y’xü=Bšš¿…vU&·Ç“'Ì @aÁd2]\\ÊËË/_¾ žGÏ:–õL|º{ïÁÍ:9fíÙQÑ3n#$­yýÝØ1Ú»<ÐâŠHF‘ŽãâAbš=¶ÇôÀhÕu•>U2Éäz‡PéÇRjF€f~xZÈ Ë$=Ë®šõ{t 5§¨æý™÷ò=5&÷¤à·A—!ÊVæ#DÜ}TgœmÉ:#ä$âò«QÝ»B:‘ æ½bUg8ZF²þ}CRª¦³­8YŸ^kfô¥AÍîVƒ©õ4ù ƒD¯ÇסêêüJ&’€÷Z{ƒ „…ŸŸ_jjê7ÄÄĈ®€¦Tosÿ{WžbW»^ƒe›µ)”ì$-'WGQ‡‘&34Ý ºðsŒÚô3ÿì=™—UöÝõˆÙ¼û—¾©e] vS¡|þ°ŠŠkw"¡¼ï *KJ³>Ÿdq1ö dÕÄÙ[$²„(¾¦7GÃ÷b|Èe·Yç~=îÛFòÿ­©ý ©¤e?‘$åp¤««?y2œŽ @¡°uëÖ‹/âô,))„? ~76ÙŠ¡Ê–ކ‚¶ªÎÈ:÷ãèÕ¯ˆM86¯}¸g2ˆDÄq­êthº”€ÞUáD‚z› 4F5|¬ÌJtb :pÖ"åþÈq5²èñÿêŠÐ™}èÌMôë5QG#§!GKÔ‰•~Ü…£É5V¾@û"ÐÕ$Tô©þŽfh¥ Ò•áÒ"T˜ù—S}þø@GHaHëÑ2òâŒçÑOb^þ¸à×4̤Óoë›wLcé›üCï–Lùz¯o>[”ÏñŒÔÕ°“ŽLÛH2õ{3×|½{ã«í4|‡Þ¾}{bb¢’’ѵð„U¶|4Tà}|ñ2xÝ“C— ^U $!e`¦í8p–Îç£çkóÞíÝy6ïé{¼¡%Ë÷T±°è¿RMʺ™QuÐøÈ¢‡éeþ¥tÞý~ØÙâ‚:Jãžþ{‡ÚöªÞ®Ì-½ž"g}ε Å“¦ÈÔe{¼.4ë¯äÊJ¼y•—9§Ðæ^ƒ;~^ç7ý"©_®¢¯(ë>̲Ô7¬M‰/ΫF¨ƒÌ(ëÁ½Ë‘ØEFYŒ[èc˜´“á=7ùfÜ–gzh ÚøvÍ ä´=eíÁª¦‰*³Ñ³È'¹AÖš_"£K^èòÓϤ¡NHõ42dõ?ks‡ J¬¨¿,¯Ž¤+QNº°]€ŽnCê?üâÕä?¦![[”/)£}ÐÛ‡èÑid“†F!]‰ö}6„³6ýz}zFd'w­a¸B¯~ðÿ·“ÈŸ3ju)ýóÀn]MVQÃ&È2ª¢¨>Ñ~z]@gj³¡ëª_¼oÞøuãÈë¿-jqJë5»Ïª~?ìtXYцÆG@€.66ÖËËëÎ;jjÍ9D´x¨²å£¡®*õñÿÖcOíÑG¼,£ìÑ™Çs.åmM™ì®KùôoætĸR|+I¹§\Çò²ÿæE{çüË$åRmjý(”$;T¼ñýw\¥ŒNg‘‚×´·‰ÿØav{6X}ˆjŸ¼¼Œúm4IMOIMYAQ„ñoxœþÊ|¼AíЧó¤N5 ñ%7öÜ5I£gÜêÛSô‡½Š#ÇÈ¢òÄ¿MLR3ð2j*æƒE_^skÏ]£ÄI‰ƒIÕ· Ån¡ðíj«·7 `ŽoÑÑùV–C>ÇÐ,uDËCîsPb5Ú‚&ìü²X%º%‰ŽÜF:dtÜ …$#T‚Ne Ã!õ-\Úð9=ÛD"}Df ”ÈáªNB›®¢ó–­Q‘ŽNlúœž#O"})DÏGËg ¤Ñ–8ôÇ ppós.fÔ•ª¼e)Ù'²Y×Ðh54D‘•”'¡·LTx/ïßù2}$™÷þ»ðÍ"I¾‚ª(DÌÇÇßdÒì!ÎÀË\lÓ—&IQ¿“ªxêÏ,ì­ª$‚*3²6ï-ªí el§;]¹9màôϺPö±´)SÛRøh Ènß¾½téÒóçÏ÷ìÙ“èZxGc»pÐk÷&oØóêNzUuýO„ÒƒÇk{nÒ›©YÕøh¨0ïÈA+ßïÀJÏTÕÝ铜zRê^¿°èsëÊÇ|ß•/­.uºh{•žå¼o[Ž ’Ÿîøœ¹¥ôcü}ǘn×IQY„½]+zû·Å„œ½È5—Ÿ˜r¨dG<2>9©‡Â ½ ÊüÎOuR'¡šbÿ£eRrTyí~Wné§§ÿv²ÿ†òºÄ§ÑÏzoêÿc¦úå*’ŒÇŒü¸Ë‘•ž»ô¹a2BÑržOÔ¹Ÿ–²üH¯»ŽÒ"_[(xû`ÜÐä̾ú2LIІŸ…èbfýê4A½þ‚ˆ*ZŽž•"’8j8v¼ÐõbÍ¡ka‹v'£*„Þ½CõÉ«MeÝÑ-ˆêŸ 2Ò³Fz‡Q*=¾€J§´l´|÷_ýõò&H—µFŠ šÞ%¥ ôK¨tR´X$±¾Ã: øÃV¾Ã5)¡sݳôº¾æ UŠ«éÅÛW§þç¨m1T$=‘†Þ¿Zµ°PK‘ö*Olh‘»õ?!°™Åº«Ï˜ö˜Îü÷_ËÜ.éoóÉ=äQEvªQS_>ãÇ™Ó.Èî)OÏý¯;‹ôVqéBAuÍiBLC‰µvÑkwûbÍ>šë¼TáÝÃ1 ÀJOOŸ={ö±cÇ ‰®…ÇÑ’×ÄšáÐGêÔWyPGf~ZaÒ©”Ù—ÞïMÙØh(ÑõŠþîÍÁ¿ë/HŒ`Õ³>¹Šjôˆ¼"þ¨ˆA’”}—™P¿Qgà1œÊŠSÔaÜL`2îÈ+¶ÑRþÿH®‚Ïš®êŸOñQ¶]åýûÕ½ÊþDG?üN/®ðÛÿÕT÷áCUÞ$¦)IBåLTý¢§¸_ J6¾Ššî9GY;$(OÒÄÚ³V¤«†ƒ)þ6óÁá¼¢%=;ý;+ã¦kP¿_éÇ‘n~W[ˆÞ³.ÈwC_èTôÊwËQQŸ/W‰È"ü„UÕXÖǧº”Ϻ^QI~yeÉRHS¥– Æ{TümÔirÕ%ˆ»J΢ag¿-#½¯C 0’ÈQdµéWxºïRIAqi¦¨¼™Ÿþ"#æÕ‚䈇5e/+KhbóÝ9‰gþy«¼¬²º¸³ŠmNß{ëtíóáz‰ñ«×ìÎŒ¹]^Zó©B\Éj½–Ö…ûK˜­ÿ݆$Òw‰ÑŽ®YÏå?y]ùâ¢*Ê §¹h®Š5+@“¨Æ.½G<¿K§VW|»¿4h3áÞÁ•••5~üøÈÈHSSS¢káyUÅûÔ™ª¹N{¦T?ƒé;ÏÉ÷®3HOÓÉa?ކ ·OïJÞ°.(÷–ÿ|¥ë0õ®¬KU2sYTõd¤¿Æ)™>J(¡1Þ|ȯmðSªd‡Þ_„(2’8–}À½™ÚF>bÝI[îöäxJÍ··2é¿ÞÑò'«øô¾”ã ö—Ú÷í]^¾ùÔ³Ó× /ÖѰ«ànx™ßýÿ'( ú(¤Ÿd"fcý¸pSk$‘û£tCãû ††”ŽH9OTb¸ãàáŽß\gá?ÒâÿÉNõ:Õ«ÁͽG\±ÿfy’ô·Ë0?];Æz‰%Ä$ñk&¡²åÊ„†ËST5ö\Ñhx µWï˜+½¿mTTÛ¬÷&³o¯d“þ¾Aü=ÓÃnÄ»o®¢jtõÝ×Õ·‘ûƒ¶ƒ Л7o&Mšäïï?mÚ4¢kád {o×7G’~Óé3m”Švç{³¿Üü“ãÍ…ósÀe6tÿ¿Ø7}>úˆDú&N‘“ÿËŽIÝËgÓÇF‰ôRŸk(Þ0 Seþ-&"À³‹*"Åúý™QIÂ]1Ö#Íý ŽB$ 4Ù²éÄTP'V …ÏÑÇ/-0*PëÅé‚EQnKÖ¨¢ðy©¾Èoý?FÞì,ÙðϽ×ÕŸ´zm[ßE™‚êÞæ_zQUG± Lœ*˜ @ASVV†Ó³­­­ƒƒѵð qy—ßÔ£ìs>½ qz‚¯“40ë±ÄSÏÎD¾#¾#¦*ß¡„Þ?­¬b*Š× zÖñ§Gÿ¡‘¤äçÍPTGõ¿Ñ¿{\^ñùVÄ(-M¯?‰j*¨¶fcÊ,ºý¢>=#Ò˜í#7ÌÄ­2^¾ü»MB¥c'V2FöF«Jµ¥-~FQFStPèsTû]xY? -mD·*©š2¯éÈ h¦>ÊLAô$“Ž–öGd:J:€Ø ›…d¾í~4¹FЧ‰^d£ò[èö{4Q1+Ð^_ô ©B~óxce"‘¤¤ûÊ:•JC?µ]œ««Â|û´¼÷¡ÅäÙ)vܨpƒ#(UUU'Nœ0a‚ѵðrw» Ùƒ_íû#+ö¯¼ûÏkèµUΧ?:Ÿu÷ܬƒSaøä"]º.ˆR£O×R£3ÕÜûˆÔå¼ôt|x® ‘† ³Y¦î8‚ü÷-þÑöj†Š‘iÕ×7§<¨¿«èd'UùæmL¿Ì·\ýâ=©SX§V@õÏÑê³ ßþ^̺†QQÙš“&ˆtQ›Û=É@%çÒÏçªÌëJf–æÿ6÷þ_5TíéF{\„e¾t4} º²eÒQØtBUàäʺÞÉ©RÐ?M¶@AfkPüB”X‰¢lÑ…nHüÊe‘CÙ y@ßïsÑÔqƒ³}ÑÅ%èu%ZcŽ¢z¡O¯ÐÛ*üöA¦^žyIlˆûÐ@ͬcqoËÒ YZ²açÙ º©ÂN7‚ 4µµµsçÎÕÖÖ&º¾CêØ¿»çŽîž8‘ÕTgÆ?÷šŸWZ}ô÷Ü-æ=ˆ®ÇˆÊ8ìôçädZÁª¾Gv銗f–×ÿ\/ª¼1B[C\dþ&§ß+û°ÉøÐ^% K²XY·‹õÈæÍœz›¬ÜGZ•ÓÐÇmÃN\ ²js·¨ð BÉKÏ;-Sz?¯hDŸ1ñŒëŽ—g¦Gû˵ìQˆutŽèpDÚó²WÖÝþ Ô—¬þ§(»²~hzÆNYaIÏ,½Ñžc(:]LDo³ލº&h‘ ­ÞÜÄÔÑÖ“èd$:{½|Uro4~6²‚d{½›\£ô@tàÚ®%£ìÌúz›Ö/`ªÑHk€7ˆˆëÏê«?‹è2÷@€‚ÉdÚÚÚ’É䨨¨ïv4¿Vûï3{»Œ»Ù”…g§¬,J—è;±çlݤ¸Äúy™ß†Â—’2п™ÖqóšÔCWв3k‘¤Ô I:>›ôfh×?9Ôž½ÏeÊî^ÿ8òlÞ³Jêç£2P›ë2Ðo¡’|sÆ#©L3Ù1÷šçñÒµ•ÙÅ*úô;{¢ÆÖ#óVÎljdÓ%c/ª×2çMaÉÓ¼&Ž$l”ìp£ûI×­M?~óCFr~3µ}6 œ©#t/±dw䌜»it$Jþö1mt!ùûÅD•мÕõÿ5êÇF~±F6]ä޼~¾€XB÷E ÕŠ+Þ½{wñâE Epg hbjÊ=+î~‹6þyÜH^MŠY’Uø8ß">×SMwJ¾ õ=cj­&ì]éÞš›NknúÉ­b;»Fâÿ~vo±é·¾‹¼âzF¯˜Fÿÿ[RÎ騧c –˜=ôæì¡ßÜgÙÄÜeÿÿëÛ6›± D’Ü+üJ¯ðf € 4wX$%…êÇg‘PX}sF— vŸÎ{ü0ï9¾F\¢Ï8 [/ƒec%qwä»ÑP*|mn°%|ïÈ‘#LLL”•æóãý‚X“c“䎊¶¡lC¿?éÇÑP@ˆA€|ÏÌÌÌØØXII‰èB  @7íÏ?ÿ|øðann®ŠŠ • 'QåEEEEuuuÊÊÊ–––&&&D—AºiÞÞÞoÞ¼!‘HMœx ŠýUVVB€@»‚Ý´ºº: …Â`´æl€›Èdò‡ˆ®tÓè,DWšÀd!º >ÐM“––.**úú§¨¨(™ çæä¸oC£Ñ¾þ©¨¨H`1 [F\\|òäÉ;w&ºðYzzú;w`p è–333ÓÓÓ#ºðÙ©S§îÝ»\€€ þÀ¨­½>u*ÑUÄd0uuDWA$ÐàôOŸˆ.ÐàUt:=E)œÎŒ7±_—êêj¢ á6ÐàQ G4ÄšìŸèZÀO‰‹‹]·A€íܹs^^Ðp^Ã`0ØÝ›^½z] ·A€’””|úôimm-Îj yûu‘’’"ºnƒ €?ää õë‰.B€¼}‹øb)¢«à <™Mt ‚£ªŠÆ`´ËîªÅÅèâÅöhZ4 Å‹o]‚ aÒhœ ÐEEE555d2™ÉdÀ!dí‡Äòþý{¢ €o@€ôñ#?ü’ÍŸètz«ï+!!¾ìd‰34çŠßÃO²˜˜ÑUÀ7 @ÐŽªªhÎÎ îî÷‰.Dp””pæ ¢¢¢ ˜«ýH$|)J«[––Ư“…J¥r°6ðFC¬KAAèZà hG8›••Õ–•]øA_<Åçpê¥Óé""­ßИ˜˜ìÞ½»´´ôÇ;wæ`míª¸¸xß¾}cÆŒÑ××oKÿ;*++?~ü¨¢¢bddDt-ð ÐpØû÷ïaÚÿöÆ~†ñV?//¯u-,_¾ü¿ÿþÃwWUUåhim…“"þWNNîÇ›pÐÁ¹ÿëM ãáÇòòò:::\-±ypy8–õíÛ·ÕÈÊÊÚØØp°*îÀ½ÿþ[·n½zõêŠ+–,Y‚ÑE8 4¦  €£3…BiË®Ÿà×Èd2~z%$$:uêÔºvìØÁÙ’8w  nfföã­¼qãþ÷ë5………&&&óæÍ³¶¶æb™  øý9™åñãÇ¡¡¡ššš .tuuÕÐÐ º4g@€€Ãºté"..ŽãÞˆòìA9µµµ<[[sÔÕÕá. ~6;ìêÕ«q n4=#ÖÁ8a7¼FIIéüùó#GŽÄïºQ£Fq£DÐ<|øð›7ovîÜi```jjêááahhHt]€¶‚ ‡…††z{{WWWãxÇ›»?½|ùrÏž=ì!^¿~½e˼ÿº@TTŽq¼¹c[UUˆˆ¨««] Ç$$$œ&&¦á|U8–}7®iaañÏ?ÿLš4éîÝ»pðw¬_¿ÞÀÀ`úôé¿X¿e?™ÒÅÁÁáåË—³gÏÆ$¾Þ9GàQ(”Ù,‰‰‰aaa7n´³³[¾|9Í1@ ùIÍ‹˜ÐÝ'ŠtT¦Nc…W²¸¢ªŒÌG²¢µþwxD{{~ÃÖ¿²ð š“ì-úöè.!ñí\¨ð2ƒ±`ÁGGÇ#F4¼^JJêÇqM//¯W¯^͘1$%%>|8--í׋5º ÇW›6mÂù'éèèh×Ú1KvvööíÛû÷ï?yòd777===¢ë4 h¾ñ)ûÊ­¢†WH ¼p£Áßôò·¥¬ ½æ-³Ÿ¦\¿oÝ· p¡ÀË~ÿý÷ÚÚÚÕ«Ww=•JÅÙº®®NTT´áõááá8@ÛÚÚâlÇÞ[´‡OŸ>á'yÇŽŠŠŠ¿^òg»p°‘É䘘˜Ñ£GùøøpºLÐ.455q€ö÷÷ß»w¯¹¹y¯^½pŒ633ƒO<tûc]´¸þ)þ¦\qr§îí°Ð?o¾(¡‰)÷Ÿì¸ÚÍ¢‡$ûk²®èÑ™}Ñgn>ú¯¤!ŠŒú€‘Ó;Zí$†£ñÛ?­§†f±›{4Å0ˆlv#ˆæþu÷‰Õ]Y—Y2& g­OMÿafuÎÍC{_Jxú®Š‰¨Ê½M¦Ø¸,£!Áš¸¸*û¯¨=G¯<È̯ÂÛcuƒqó—/›¦ÛÜŒ>¯âW¤yOh÷ïßÇÛéäääFkdBwìØ±á•xIÈLMMŒÝ€S6lØ ­­=kÖ¬&—üõ4&))yúôiccã=zÌž=›s5‚ö…?zÞÞÞîîîÇ_³f§§'ŽÑóçÏ'º4@ã @·?²¨8{\¯äAøÒ]‰UêJÔ’¼Oig7:ÑUϬ5”&Õæœö°ÙœXQ¿´¼º¦tå뜜” ;R®?ð:ºmŽºµS?}Í¢”ìúf²²¶®Š¼NG j0L‘ïm¨ûáŸÌ—åõKtÒÑU’ÕUù1Ö¼8à´p÷Óúó¶Ê¨iv¬ÌÎyv#Ê'!ÅíÈnkMRÎñÖ!©ux3¬9ØD±$5é¿¿OÚþûéD¤•:¹Éê5ñ@Äš~*Úᨴ´ÔÚÚzß¾}?;(PFF¦¼¼ü»XìÂ… &&&ݺuƒ™†ÛãGþøã'Ož4ga «««™Læ/†'ñK|éÒ¥qãÆ©««Ã‰åø‹¨¨è|–›7onݺ'iggg'''%%%¢K|4)ì]iRæˆÐK«G(Jüg»^ú€J®ŸÊð0T~iÃï¬Ð©iù‡‹¾ ™Qž²s±Ã¡×ÕI¡›®ŽŠ0W6]½SMvÖ¼èwõ´ šÕ‰\ßÿW!9Àm÷¾ñÁSmNàDÛËa[”…ù›Xèoϰҳ˜¾Ï±³ÔÅhyÜçø'V§î ‰ŸÚãJ\ŽNRêVá‘K´Åj³öεܗKK;vñåL瞊MÕ€ÛÏoêH4ñTÁqˆíbÉ’%æ,?[@ZZº¢¢¢Ñ›ðÆûòåË8C«ªªššš¶[¨®®ÎÖÖG¥æŸ†ÝÕùõ‘ýúõ‹ŒŒœ6mÚßÿ ó¨ð£Ñ,ÏŸ? ÕÖÖž3gŽ»»;/O+ €‚ÍMÚ‹ìMê?ïh0u¨Ì¥Ëå¨*ïmE]·Ä³© |­ˆ‘ã‚2õû.“eô¬íô¯KeÒ_H)2Qž#³ŒÂ3ë/P‡,œ ^X˜ˆê„Õ៕ÒIâ]ÄÄz:üË!&­ª¢¼¸ˆÁí¢HB¹Lôáõ‡úÐÝtûEM>“_? $ Ó¢rÜž={þûï¿#GŽüb™_ï\«¥¥ciiÃY;Ô(¤6oÞ¬¡¡1þüæß…ÝÕirj ‹×¯_Ã<*| 'æ½{÷DDDŒ9rРA«V­ÂÁšèºõ @s‘x—î?ï~J‘R”B¨!z½¶ày>ëJEµ¯{“¥Ô4åPj b¼Ï.®Còœ˜¡¶ðÅ{ÖùnÊÔÏ׉©è Wù|™Qšr$8ôðµ>0¿¹ƒñíß?m¿éò%@ÿä©hÞz@ dddüöÛowîÜ¡R©¿X¬ÉkMMMÃÂÂ&Ožœ˜˜ƒš‘žž¾{÷î”””¦mà×]†–/_þìÙ3Üí¹xñ"ožÐ4‡’’Òºuë¼¼¼Ž=ºlÙ2111£çÎûÝ!¿.ƒÍE_vå¨×èˆ2ó›ùù™cûiŸ‰Iª´wg<œv¤ÒRag?±·¢DmÆn¿ýÿ¶eE_VÇÒð4ùTN¨ªªÂÚ-[¶4ùãosb™µµunnî¤I“p†ÆËs®LaD£Ñ/^¼yóæ–ÎþÛ蜃?nnnŽ“4Nê-¯ðqqq;;;[[Û+W®„††úøøà—ÕÑÑñÇãÜšx¢*½:!T‚PáóÜÌ^b¬@ɨx•URA¤‹¶"‡DµY+*Éʯaê°VT›û×±¸W5$ M#©‹õé‘ =~[j*KBôwï"ZÔ¾X3H.g hö´² .lrÉ:ülè†ðf;'''é3gÎÀ f[+((,Z´¨¥wüñ¬7¿ÀžGeøðá8Iã¼ÕÒu^C"‘ÌXRSS·mÛ¦¥¥5oÞõ)‚ 4ñ( CgêS2Sèô¤}1éC—ö—&ÓK’ü‘^£ä°YeXIôË’ZÔ©5sQ”‡NÑA¡ÏQíÃè /‡XkRiù7¶î¸U‰Hý¼¦0XK1*Kk˜H–ùáaÔÁ¬kê>VÑØ-üºróàŽ“'OÞ¼yóÑ£GÍYèòòòæ,Éž5Û"###,, ¿4­˜ë·™]¯dee/_¾{›]‰Ä•uM,¹,­.NBV!AÅþÛN=Ê/HO<}ó6—uçó\Bî|ÈÊ*ªe6£†f<Ðîêêꬬ¬pºÕ××oæ]š<ˆð»…/_¾lllܵkW˜º¥¶oß.))éààк»·è•úÊÈÈh×®]p ¨@Âo‰åË—»¸¸ànRXXþà¯X±ÂÖÖ¶™g@ë@€æ‚£#““¿½JL{Å…ä ¯U'%îâ®Zµ ¿ôÐq€S @ ˜LæÂ… q€nŹÊZ½c€µµõëׯÍÍÍoܸ;üBvvv@@@bbb îêäæ¶~6H11±S§NvëÖÍÑѱ-•Þ‡ß-+W®<}úôo¿ý†3´»»;þ–oÍ|ø°u¶Õ#Јur‡¨¨(333˜úg"##qßfåÊ•moª-¯ÔWZZZGŽ™3gÎíÛ·{÷îÝöª_ÁòâÅ‹mÛ¶õêÕkÆŒîîºD×_‚ ß+++³´´ÄáµuS £ž úGbbbgΜ>|8Lý£œœœµk×Þ¹s‡#Ãómf5jTXXØÔ©Saÿua£­­¿+6lذwïÞ±cÇ0`ÕªUøÑuÀg @À÷–.]jff6}úôV· ++Û–CÓЗ3Þ 6LWWÎx÷“É\²d‰««k¯^½8Ò GF Ù¬­­Ÿ>}Š_¬Û·oã.GÚüBQQqõêÕGuww'“ÉnnnVVVðN ™ @ÀßöïßÿìÙ³´¥ËÚ>®©¦¦váÂ… &¨¨¨µ±5ÁU\\ìååÕô¢ÍÓö®NCóçÏ·µµ=|øp«§¦ü‹J¥âWñâÅýõWhh(ŽÔ...ŽŽŽòòòD—¯ƒ ÃÑÙÇÇçîÝ»m<¬¾»p|Õ¯_¿L›6 —“C¿{÷Î××7>>^D„cß´œÚ…ƒ ‡æ}ûö™ššúûû¯_¿žSÍþ‚ßXÒÓÓÃÂÂzöì9oÞ¼•+WÂG€_€ ¿ª©©™;wnPPPÛw R© £®®NTT´MáÍðºuëÌÌÌ…|çÚ¥K—.[¶ w*8Ø&wá`“””¼páÂ!Cpl‚³ 9ü^ŠŠÊÏÏ7116l˜««ëðáÉ® ^~µjÕª¾}ûÚÙÙq¤5ö tÇŽÛÞ”““SNNŽO}äÈ‘ÜÜÜÓ§Os¶YÎŽ@³á~ιsçÆŒÓ½{wcccÎ6øN§N6mÚ´zõêƒâ¯999üU3cÆ þ€€Ï|éìÙ³W®\yüø1§”‘‘)//çH€F¬k°œ:uJw®ÍÏÏÇ™¿@mÑÿÐÕÕÕL&“³Ïj¿~ýbbbfΜ ûÞ6ÜõÅ=aGGÇ‹/†„„xyy¹ººÚÚÚâ/ ¢K€'@€€ÿ¼~ýoÛbcc9¸1“––®¨¨àTk_'‡öóó³F;;;ÛÛÛ8°=gwudee9Ûì˜1cÖ­[gnnþàÁŽ7ø™L¶`yôèŽÑ7nÄzÅŠ]»v%º4>C§Ó­­­W­ZehhÈÁf9¾sí×É¡ÕÕÕqÜç`Ë<îØ±c/^¼ˆ‰‰i§öÙ]öȸøeÊÊÊš1cF\\Lg200Ào霜œððp==½ñãÇ{xxà+‰® Â@€€ÏüöÛo222xëÅÙfÛcçZòΞ=;lذ.]ºÉäÐEEEîîîçΣR©í´ Žwu Æzùòå{÷îm§Uþ…;Ã[¶lY»vmTTÔÌ™3ñŸø‹hÊ”)d2™èÒà6Ðð“7nDGG?zôˆã;·S,ëÑ£N“x{ñâEa˜GÏ pöÇïpjÎÁFQ(”˜˜8©$øÜwuu]¶lÙ™3g6mÚ„3´›››Ð1 „høFQQÑÂ… q€VQQáxã:tàà>Ð áܼÿ~a˜úìÙ³©©©h×µpä¬7¿ÀžØÎØØN* ~ADDd þ\oÛ¶mýúõööö¸Ù©S'¢K€ @À˜Læ¢E‹p€;vl{´tyyy{´ŒáööíÛ©S§&&& êj%%%Ë–-;~üxOjÓ¤öëê|¥¦¦vþüùñãÇ Éï -†³deeáݧOü1wssãìôçð Ðð‡°°°âââ 6´Sûíºg-ú29´ ¶råÊÙ³g6¬½WÄ… 8pÿþýsçÎMHHÀyº½Wø––ÖÎ;ñÔž={ÌÌÌúöí‹c4îƒ á,–@H@€€$''ÿþûï>l¿s´ÇA„ß ´´´´µµ=|ø°€mV/]ºtÿþý'Ožpa]\x¥Ø,,,^¼x1iÒ$œ¡aö_Ðòòò~~~111ÞÞÞžžž®®®ÖÖÖíwL-D ¯«¨¨˜7oÞ®]»ºuëÖ~k‘––.,,l¿ökr蘚š ØäÐeeeNNN¸W€£-V×Þ¿4„“Pvvö‚ Μ9C¡P¸³RÀïÄÄÄlX®_¿¾uëÖ5kÖ¸¸¸8::***]^‡ÃÙ˜1cfÍšÕ®kÁ±ìåË—íº Ôà5AšzÕªU#GŽäÎê¸6Í>~üxWWW|k+‚a,Kfffhh¨¶¶¶¥¥%~#á D×@€n¼ÝòõõÈ=8ùÔÇi4ÑU´£èèèÔÔÔ¤¤¤ö^‘ŒŒLYYY{¯SRRŠ‹‹>|¸††Æ¤I“¸°Ævõ×_ÅÇǧ§§sm:tàÎ+ÅF¡PØ'ĉˆˆ˜>à&]]Ý?þø#00p×®]ø4dÈÜç1bÑuÐ& [†Á`´÷ÏÜ|õüùsooï7npa‚Unî ¥¥uîܹéÓ§_½z•¯Ö¯¨¨pppˆŒŒÄÏ×VŠ»:¹¹¹\[jpBÁèóü‚»y¸÷El1 ,xúô)þ싉‰êèèÆIXÞ¿Ÿ[UUU{{{%%%¢ËܺiŸ>}Ÿp& ѵ€Ÿb ão1¢ áüƳ²²Ú¸qcŸ>}¸°:.ï`dd´gÏsss¾žäÁÓÓsìØ±ãÇçæJ¹üJ±±Oˆ#}!´yóæëׯãoHƒAt-߈%ºŽa?½ø³Ù¿ÿÉ“']àÐMc0lÒ„_¦Ž;]ÇxxxàÔâààÀÕqsšíë$wïÞåÇÉ¡oß¾}ùòenî¼ÁÆýWŠ ÷y¶lÙ‚ÃArr²²²2÷ ­SRR‚XÉ]ˆà«««ãæîU€X ›Ö¹sgü‘¨©©k¿IÄ@[àW±vÖTUU%ºΈ½téRJJ ×ÖØ®'ˆþö$ü894~®ìììöîÝËýèOÈ4›µµõëׯqχ;»Ž`ÿ:º]}ÝŒ=R@s@lÚùóçß½{W^^Þ¡C‡æMµµµ¸ëƒ…`œ)úÍ›7ŽŽŽgÏžåæ€:‚„Œ„‡‡ãÍw“C¯^½zøðáfffÜ_5Q#Ðl¾¾¾™™™666'Nœà£×K˜ÁÏ\ƒ?›„ïe¸tÓTYˆ® :nmm½råÊ!C†ps½ø«ŸqM …cjj°víZîÐ 'Ož|úô)!k'ª«Ã†CsTTÔøñãl2o!A¥¢Q£Ì#ÇA'O¢ü|¢‹D€ oÙ°aƒ˜˜˜×ÿÚ»¸(êÿãŸe¹EðQ4ñÆ«2ï#å—y+ZeÞg™•úW33­´ÓÒÒJÍ«²4µÔŸ?SÓL2¼5M˼ð@TAîeÿ³³b .̲¼ž8ÌÎ|çãìÀ¾ç»ß™;6·«Ë+ëÍ¡›4i¨œ<èRCö%$$ 0`Ö¬YE‹Õ¥‡pX)ǧrþСC‡ÈÈH???+V®®Ò¬™´i£wdÛ6tE€ìÈ/¿ü2oÞ¼={öäý@:77·´´´””—<Þ´¨7‡^¿~}óæÍK—.œ÷dßĉëÖ­Û¥K½ Ðw‡•ò|ýþûïúÖ:"@öâÊ•+Ï=÷Ü‚ ôêÕ³vBëu'“J•*-^¼¸gÏž›7o¶Û¥…‡‡÷ÝwÔ±Ý{ hÀ.˜Íæþýû+ñ±uëÖzÕàíí}ýúuo¬œ?/^\¯2—””4`À€O?ý´D‰:–¡è„„å€á>Ð ° ³fͺxñâŠ+t¬ÁËË+66VÇ:tз€L\¹r¥[·nO?ý´Þ…Ü<ÕÉ7ÏÇ@€ô·oß¾)S¦ìܹSß{!ÛÃàZ{æïï?iÒ$½«°hÔ¨Qrr²ÞU@ÁE€tf2™z÷î=cÆŒ *è[ ƒkó‹ 6è]hh@gF£qåÊ••+WÖ»z È4 ?{HÏŠ1cÆ”/_^ï*°wh75kÖLïÈЀh@4 Ѐ h@€4 @  Ѐh@4 Ѐ rEÂùz¶¬ûUÎÇ‹¸Iõ&Òw¸ü§¼”ÇÌrb£ÌþVvåA'o©ÛJF¼(A…ÓWN“ƒ?È—?ÈÞã’$b($A ¥Ïp .§®.÷—|ù…ü/\.+{Jݶòòp òÖëÿŠ‚… l/ñ/y¾ü‘j™.(q'äÏ-òê¯2r±ô ”3ßK¯i’¢Dß@iRBö‡Ë®dÀß²l®”S³É‘ÏeÀ"ËDñŠ–T}åo9¼YÆý*¯-—®þrã   '”‡KIórîwÙóƒô=(_- ýþÏ(0ÐÀÖL²zŠšž]åÕ¥òt9I½ £ºKX‚Ìš&­?–Ð âá-Þådæ\©â*ÇçHÏ/%õ ¬=)/TVÒ·¬þ¯¥™RÏÊ£Ä]Ä+Ÿ¼,áfù瘤•”eSo¦ç¹ËåÑBbŠ”]%üoùpƒÌë*Fÿóp|h`ci—díË„[Ci]Î2á\Z&Ì”?¯‰Á]\]eÈW2D‰Å©{].§‰KËÀ ³Èé«êúNâábù7j½Ì)/-êJõròÊ‚›§FȆ,ÅšHP!Ë„ÑWºÔ”ð½rh\ ‘âNyýÿEAC€6–|I.ªÅ·ô™¾‹oúôµ½òÁÇòÓQKh¾]šõgWé>Dþû¶\¿&ß¼+ß(s\¤zcéÒG:ב”h‰V—Š^)MWÞ¹þ¹˜"ÅÝÈUh`kæ»þ½Cêyù¿çe¿É2cà ©^B’ËkóïXÆ¿³¬’•«ä—r𴤥ȟÛ,_û>’ñÅÄÚÅl|Hž¬!·w7‹ˆÝÏÈ}h`c.%¤„Xú‰£K¢Y\ÕgDl” §Äà! ©éY¤þÿɰ`Ëàóç3h¤peé3Fúˆ¤%ËÉpùd¢„Åʆ¯äÅ·n6^¨¦¼6Y¸hy àÄÆÆ^ºt)99ÙÕÕUïZ–u÷º»»ûûûg¾dRRR||¼Éd2¹Œ*·Xw¯‹‹‹———ÞµØ/c)éPU>>&ɿ˚“–Ûn¤FÊ'ïÈÖ81Ô’n.wÍÒEm¾* ¾º9çF¼å{ÊyûmÙNÚO“!Aâä*Ë$ì ¥OÛ©”´ ”¿NÈõ­²í¢´ñµ\b8g¼ìL–r-äµg-¹Š àtïÞý×_MMM5 J¤Ð»ǤÄbggçâÅ‹oÙ²¥bÅŠ™,¼{÷neÂl6;9ñNv®Pö­òÝÏÏoÏž=%J”л{å,]^—Ð~rÄ$ӻ˲@‰Uò®:ÿùñRÝMªˆü%rä¸YbɵG¤þ —ðwdì_2¹Ÿ”»!ë£äË>²±¦øzHL„‹´4üdo)î*ÝÆËÚÁr:N^ï( ªIÒ)9o'<–ôŒ¼@€FþÖªU«C‡Ñý™«¬»÷ᇠ½÷Ñ“'OÆÅÅF“É”÷åJV2´ò=>>>ó%OŸ>­<_Ê’iiiyS[¤œ+*::::˧£€ó¨.³—Ê¢/dm˜œ;!J° j"ý†KKõ“P¦½'oÎ=‘rè Ôë"3†KÊj>M¢®ÊñËbv“s¤Ô—²|‹ûCN‹å²ÂÀÒ¹¯t¯oôìõˆ,üZf!?í–G,W¶4\^ïÿ6 tÎ)qa̘1çÏŸˆˆ(W®œÞå8&e'+{¸lÙ²uëÖUöö½ lÚ´)ï«*˜¶nÝšá|%=+y‚¸–«”¸¦ìd%C+{;ó%%½‹¹Jy:RRRô®ÂÞyV>2zÈÿ ™óijºËúî·ýXX:²|ÝwŒ)cmQ' :ç”W²Y³fYÿ€†……é]ŽÃ²völܸ1ÃíîŒ ‰!7)O¨ >êããsîܹ¼­¨À±ÞžžžYºU–¹rå ¿¹Êº{ÝÜܳXèœ3™LJ°pvvV&x¹ÊUÊN¾ßCÖþ6ä*ëá­>f%i»‰0òÖ†îs°ìPÎ|\]ÅÀóa;I‰Â_{èaô©Ð»§¤·TU¶–.”ËÕ(JN¸m„§µ: EEV‹4ͽš žr"9]·°,ùEªÖ²e=\Ýâ­wôG€~ Ö ýïÏÞú•âxÒDnüÛ÷–Ýkæ^AO¬H=yp'´í¸ˆ|¨w Ž$Zd’H²Æµs¥–*F’îD€¶e_Ñ»GrFdŠö ËЀh@4 Ѐ h@€4 @  Ѐh@4 Ѐ @”$ÿûŸüý·Þu8 ô®:!@P $'Ëöí–/ˆ €ÃŠŒŒÔ»„‚"...&&Fï*GÐìR ‘m"^"çE ?pki"3EÞ‰R\%Ò9ÍA KEþ%âóÀ5¶àäädýž––¦w-˺{ ƒÑhÔ»ä4`#-ô|Ȧ"¯¨A"Dªgo-?‘¢"¥•—Çì-¯Fç " а>>‹yÄÙÙÙËËKï*GЀ]ÊYàÃýD§OŒ”íµ–jÜÊa5=ödòäÉ­ZµŠŒŒôRé]NbbbfÍšÕ£GJ•*ÝšêîîÞ¢E‹[sÌfóæÍ›÷íÛ7`À€âÅ‹ëPèý?¾hÑ¢%J”hÔ¨‘Þµ  a#-lÚÿŠœ>Ƕ_d¢Èf‘õë¥öÐMÔ¬æÝEv«?é.ò®H‘ôƒÓj°úµD¤g6¶ØâΣÚ$2Gd¶È!õQo‘'E¦ŠTIy4}¾¢¼ˆQäJAê‡N>(­ëH„H¥÷eíØ›¯-›ÿ#C·ˆxÊ¢HiZXL—eéòÝJ9¦ŽÊõ*/M{ËèñRÁ3½•4ùók™>CÂH¢H¡ŠÒy¬Œ$>ê[Y¯Ž 4Ué]Å}%%%5kÖlÒ¤I£G¾}þµk×|||ÆwûÌW_}uþüù'N\¹reƒ ò¶Ràh ×ä}às`a"MÔ‰²"õD6©A6Lý*¤ÎŸ)ò²:QCz±Y]à ÈV‘ºêH˜?ÕGˉø« äÀë"ï©5Õ§Ii|…È:µãYi¶¡Úý|Y]àa‘Rü}½K¼Lo*³YÄTi(Þ&9ºKB§ÈÖ²a½¸X~%öNîê.ök,µ½dÇFùn¨ì=+ËÞÏ,WG¾ô /T¨PaÔ¨QÙ\~àÀ¥K—îÔ©Ó¼yó:v옫µ™à<;ì!ð9Œd‘¡êD5­z‹œ©ªî®ÅêC‰"ߪ''UÔè.2Iä-uo+»ñ#‘"ÖîªIꘈY¨N¼"2]¸&Ò^­®œ)U™+R<=a¯VŸ8Ü.ာ8f™è¿S&Ô³L\ß*ƒ†KšQþ¼$þ’rL^WwŸïp )^9¿@Z ”£Sdõ@ 9ŸÅêȇfÏž½k×®;w †»òöö¾~ýz†kµk×níÚµ!!!.\2dHî— d€m¯2éžT^°‹ü®ŽŽÝ¨^´´R$E9Gí3«œ­QW<¥¾›lõizb륦 Qß}~M¸¬Ž²U¾WÙ£¾g}kJ¦Æ;gõÒ(%:ü&2\ä¨ZÌd‘پĪఇÀçH”½÷‡:ÑNÝ™ŠuÿlùFí¡wWWQ®ª_éëFeÔ`¬ºŸÍé?úÝökr?ÊAîªN,VŸÊês·#Çÿ¥‚ÇÉM¬ÝÄ«^“ /JƒFR¡…,;üïç×Ê_êD‹g-éYQº“Ôq’ð4Yõ‹„ÔÈbuä7aaa“&MÚ±c‡§gƒp”Hm6›ïoU¯^½_~ù¥mÛ¶gÏž}óÍ7ïÍß@n#@Û«Lº']Ò_È•dÐR$Ní¡<­f¯ÿˆåÈG8k ÐëD^P'L项„ÈgÊ'5¼."RMd¬šž•™ËÓÊ­M<¡NÕQžsԙߤ§kjo\mµ ÜÎæ¯€»˜>ñ¥úu»S"I""¿¨‡ýÞ{ÖÍð%x¯š€oQÎ çeUƒõ$gú›ò¼:Gù½h+2&ý­dέ–¼ÔAƯ•«›äM–9.þòxw<^ê–²üxùèÍ%¿o"ßß¹îÙ=bè™ÅêÈW.\¸Ð³gÏE‹Ý~á &+VTÂwÇŽ#""æÎëâÂ0ä)´]ʼ{²vz¯òBÞI µÊýE¾V³W¸H+u8¦’Ø"Ô7‡¨Ïs¤È.u­^jž©%çõS{‘­ïM¢ŽÝ´ºµ eõ)j7j-u޲¹ÏÕ±EÍ뢮K€¾‹Í_wëΪÊÉ^ý;*¡>zR=SÕ.ÿ7ÔÏ…«Ç­m TGãÌSßù9¦¾Ï°Zýâ&ƒw¸íN5Ý6ßEº­’Z«dÙùu«œ¸")çeÓ Ù´Df'|Ř€*¶—ÚwÞc¡è#–G3_ùGrrr·nÝܶmÛû-c4M&Óýµ*Y²ä–-[” ®Äè+VØç=Fà¨Ðv)ûÝ“¯§?‡Ô-êíÏDÍgÃD&¨#@ΩÆÛÒ_תߋ¨Í ÔTÝ^Ó>½_í.ÃÔžéÊê(Ï“–ëéåu~=u¤GœÈq2ß=ì$ð9Œ[éH9be´À6ugŠzø”:q2ÓÏéA[[ %êYîf‘çÔ÷aÞéXà2Œ7ûQ–÷»,—’äpÄ ¥ÚSò†ú %]”°2ú5¹~Qæ„Jp_)ts©:cåƒæm#ÓÕ øþÏWFŽYªT©×_=“e”4|òdæ¿Æžžž+W®>|øã?¾nÝ:?¿~½òÚ^e§{ÒSMcVÞéÉé½Ôx­,¿^ ÁÖx]CtVJ°{[½M„¨}Òó3:<Õ¨-j泞ۗTóý­9qêàô]lø ¸2ê¡{XíëPß]¹¦WRluQjú’Ö›`\RC­Uœjø[=ù<¡^rPOý-h£^$vÛñ+Ã],x:—”RNr*M.ü(L’ºÞrþGùöÄ¿ œš+ãgÉwùb‹ån¾Ò¼Tš {Íbêê×Êr¢®ì矦˅FRÚE®ï—^‘¤âòäûÒò÷,VG>±páÂ-[¶dxá`ÎÆÙ³gO™2¥I“&ëׯ¯ZµªMš2G€¶KÙìž¼½ÇåÞ?DJÈh-ªFçî"¿ª3_¹5N,U½Ð*F½(ðÞwAïíÔqÊh[¸‹=>Gâ*ò…Hsõ@}H½ãòQuG)óg© 4K_R9WüA=°›«W ƪ—lîO›åA”U[;§žy6Po¥r\%ê0hëoJô…›ŠÔùQ]«€0”>$|•e”RòR£¬ûGžh"¡éZú5¸arÑ,]ËIÚâ™&§wÉy³¥`pËtª*o’g>–ØUòx9©QZþÙg¹ù‰K#™\År)Hæ«#?ؽ{÷øñã·nÝjóÏG|ýõ×Z´hñÃ?4nÜØ¶÷"@Û%›tO:©}Ø¡jøÞ"rC}¶Cn[`®Ú9-j/Úõ6ÇnëÒÆƒ°‡Àç`š©GòD‘ŸÕIžê¯ÆTõ†¢~_&òj¢ S/Ó|G}SåeõäÄ&Ÿè¡nú-õù²Ž°rW¯7«^hk¢ž&}¯¾t¥ ý}u’'ÊÄBòÙR‰¾&Q•dìf©·R ÐI’œ&îuäÛ]2ë- Ý*Ôûœ»ùZ> eð$ib}ÛÝ õ>”«ÉôOdça9)e¥M_5^=,g$Y¬{wéÒ¥§žzê‹/¾¨V­Z– »»»'&&jj¿oß¾~~~]ºt™3gNHHHÖ+ `ýÏ7lÕ=ÙT½AÇyõU_ÔOM+þÐa‘—Ô‰—ÕáUW¯2|AýÀcÆAÝ_~·õž9õÔÂûé¦~ÝîEõë–ì¼Ï¿5Ó‹¨w–üøþ«{ªwŸÔúàéˆô]lùúW#9þÁ¿?yו×Vß¼uæ}šÚƒeáàŒÌzuØ/“ÉÔ½{w%ã*7;Ë»¹¹%%%iÝJëÖ­7lØÐ©S§sçÎ >\{™@v íR–Ý“Ùä©&³7Óo©6"}ôE¼ÚßlR‡¼©€þD]r¹zÛµ~¶ýÏ[òƒ1cÆxxxLš4)·7ôè£þúë¯mÚ´‰ˆˆx÷Ýw¹E4r Ú.Ù°{²¯‘E›ÑT0«ãª­‘z‘šž­‹}¥Ž“ªÞÔ¶² ÿ3€mÉ’%kÖ¬Ùµk—јoq>ôÐCaaa;vìÓ§Ïüùó]]]ó`£(hÐö*ËîÉ»<œU‡eïôÛh(gãÓÔ¯Û¹ˆl¿sÎÖ;tV?ÆåvžwFù»–@äÀ/½ôÒ–-[Š)’ýµ .›ã+VlÓ¦M½zõj×®Ý?þèííõ:€hûeNßÉ`Îéüd‘qé¹zº>Îjy›ÎpÑÑÑ]»výì³ÏjÕª¥iE''§´´´Ù´‡‡ÇòåË_yå•fÍšmذÁßßÿAZîB€¶SJ*½•Gs2}U½ÓV„˜ÔaÏ,wÁ{Ð6µL 8“Éôì³Ï†„„tïÞ]—ŒFãÌ™3?øàƒÆ¯_¿>(((ëu€ì!@Û£»’¨2a/o¶æ{«7Ù˜ðÀíh™OŒLš4)%%Eɯ9X÷Á{ o;vl™2e‚ƒƒ—-[Ö¼y†r hF€¶G÷ÐûEÒü2P üøã‹/Îñ…ƒ8ú.½zõ*]ºt·nÝfÍš¥|·U³(ÈÐÀ–þüóÏ^xaݺu%K–Ô»–›‚ƒƒúé§:œ?þå—_Ö»ä{h`3111]ºtyï½÷êÖ­«w-w¨]»ö¯¿þÚ®]»3gÎL›6[DãA €m˜Íæ¾}û¶jÕª_¿~ÒNÎ>‰0Kåʕ۾}{HHÈ3Ï<óÕW_)[±ù&P@ €m¼ýöÛW®\Y¶lÙ¶ãî˜h“’îR´hÑ7öîÝ»M›6+W®Ôtwjà4°õë×Ï›7/<<ÜÎ?üÏÍÍméÒ¥£GnÖ¬™Rs@@€Þ!ÿ!@€uüøñ~ýú­^½ÚÏÏOïZ²æää4]Õ¤I“uëÖiýœ€ È7ºté2eÊ”FÙ¤A//¯¸¸8›4•‰‘#G–)S¦U«VK–,iÙ²enoŽ„ rÎl6÷ïß¿AƒC† ±U›F£Ñd2ÙªµLtïÞ½T©R={öœ1c†ò=¶Ç@€97mÚ´Ó§Ooß¾]ïBr¨E‹›6mjß¾}DDĘ1cô.ùäÐO?ý4}út›_8h0lõQÞÙQ³fͰ°°¶mÛž={öã?ÎÙ§'¢@!@€œ8uêTß¾}—.]Z¶lYÛ¶ìíímÃòÎŽ2eÊlß¾½K—.=zôX¼x±»»{^nù€í˜D΋œÐ» G’úëšÍw]b®Ú¬(»éâãã»ví:nܸæÍ›ë]‹møøø„††öïß¿U«V«W¯.V¬˜ÞÁ~ ØÎu‘"¼ùiCr‚Øy.Xø¼bJ¶ýgãå_Æ «Y³æË/¿¬w!¶äêêºxñb嬠iÓ¦6l(_¾¼ÞÁN m'A¤Þ58³ºKµÊ·Í?ÒDR²^*5õ¶>RåY»‘{tÙz÷2)ɹU ¶O>ùäСC;vìÈ¥ö• ›œ¬ÏÑk0>øàƒ€€%C¯Y³æá‡Ö¥ Ø9´MÅë].ê]@ÁoPû8ͼ»k Nåä$111Ë;ã&$XÎ;•g„§#)¼Ù¬Ä»””lœb:¢íÛ·¿ÿþûJzöôôÌ¥MxxxXf½Œ1¢L™2O>ùä·ß~ÛªU++}"@眵§‡èÛ¬{ø~}oîîîII–7Uy rõ)prrÊðÑÂ… +;?ÏnÚZ0ÄfNS"…——WæK*&::š« ê{-®®®...z×¢ƒ³gÏöìÙsÑ¢E*Tл–ÜÕµkW__ß§Ÿ~Z9[èÓ§ÞåÀ¾ sΚ'x•Ê™ìäÄÄﬤ`²îÿûåㆠ^¼xQYF9É)˜y"¤¤¤(pÊ•+W¼xñÌ—|ì±Ç®\¹âììl]%oÊÓD9’““•S_Ãmƒ³­]¹·Žå¬XyÔ¶·E³!eÇ*G{`` ···Þµä5å¹ëÖ­ÛK/½ôä“Oê]K^hҤɖ-[Ú·oîܹñãÇë]ì:甿õ~~~W¯^U2œ‡‡‡Þå8&åµVyeuss«\¹r† DGG+O·Ê=ÖÝ[ªT© ]°`A׃L¬\¹Rï2[«V­¹sçÞ¿Þ{x˜˜wß}×ú£2­œ˜=zРAz”‰û>|xÙ²eÇŽ›Û*T¨Ð7”ïy°­LT¯^}ÇŽÖY™9s&·ˆ†:ç<==ÃÃÕ_oåŒÜn{J€u÷ÞïvB»víRž‚øøøÜŠëî-\¸°Þ… ß9rd›6m²ì¼ôññY³fMãÆ•ìÒ¤I“¼© Yš7oÞo¿ý¶sçNCžÜÚÅÙÙùŽk”õSºtémÛ¶=¥Z²d ]fôòõõÕ»„‚ާÈ/BCCþùçýû÷ggáJ•*}óÍ7ݺu ·ù‡t ”ÜüÚk¯íر#Ëø©páÂëÖ­4hPppðÚµk³L‡G€äºk×® 2dÑ¢EÙ+£uëÖ£G ùå—_x‹I_/^ìÞ½ûÂ… ï7š.7ØÛµ°...Êüúë¯7nÜxÆ zW= ¹näÈ‘:u Ö´– 80pàÀï¾û.o† à^Ö •g¡}ûöy¹]ooïëׯ)R$/7š9å œ:uj@@@óæÍW­ZõØcé]tC€ä®5kÖlß¾]‰Â9Xwîܹ-Z´xÿý÷_}õU›†ì3fŒ’bßxã ½ ±Æ +]º´r:ñÕW_µiÓFïr 4 EGG?ÿüóK–,ÉÙíÜÝÝW¬XѸqãZµjåq(JF ç€ÛuîÜÙ××·K—.S§N0`€Þå@h@.z饗ºuëÖ¬Y³·P¶lÙåË—wìØqÛ¶mÕ«W·amÈÜÞ½{Ç·eËŸ¼ßº‹‹‹=ÖcÆ •²]»v“&MÒ»ä54 ·¬\¹2<<ûì³   ] ðôôŒ×eÓÙT¥J•;v(§vgÏžýâ‹/œÉTO6 W\¾|ùÅ_\±b…Mî›Û·o_%ˆ÷èÑcݺu|˜En3™LÏ>ûì3Ï<£dh½k±k¾¾¾[¶lQËï¿ÿ^÷}Až!@rÅ /¼ „°FÙªÁ?ü°mÛ¶ãÆ›6mš­ÚD†”¬œ¥¼ýöÛz’xyy­^½zèС-[¶\»víý>´† °½åË—ÿñÇ_ýµ ÛT"Ý÷ß_¿~ýÚµk÷éÓdž-ãvË–-[µjUxx¸¾=ýö?„ãggçùóçOž<¹qãÆ¡¡¡•*UÒ»"ä:4ÀÆ¢¢¢FŒñßÿþ×ÝÝݶ--ZtõêÕÁÁÁU«VmРm‡âСCÇß´iS±bÅô­ÄÎ/"¼— fÍš5cÆ ½kA®#@llذa¬_¿~n4ôå—_vïÞý·ß~ó÷÷ÏMX111]»vUò_:uô®%_R{½K@!@lé»ï¾;~üø’%Kro;v|xÚ´i»víÊ_]¹®®®+W®|ì±Çj׮ݥK½ËàЀ¬¥¦¦öïßêÔ©åË—×»ÍJ•*¥dèvíÚUªT©V­Zz— ß#@²öþûï+VlРAz’CuëÖ3gŽÞUph@:ôÉ'ŸìÝ»7 Þ¸KHHˆÞ%ph@fRRRúöíûÁ”-[VïZÀ. ™yçwüýýûõë§w!`/ЀûÚ·oß矮|×»°#h@Æ’““ûöíûÑGùûûë] Ø4 co½õV¥J•ž{î9½ ûB€d`×®]óçÏÏ›ÁÆ KKK˃ €M wSâìÀg̘áçç—›+R¤Hll… ¸›““ÓÒ¥Kƒ‚‚ô.änuêÔ9xð ››[RR’Þµ8,ww÷ÄÄć~˜‹Gû!@€Åüùó§Nšššj2™ *¤w9ŽI‰ef³Ù××WÙÛJÎA JzV¾“žs•ò4)ß=ªw!€ý"@€ÅÏ?ÿ|òäIggg%Cë]‹Ã²îÞ˜˜˜Ã‡ç,@[ûž ƒÄm^n2„Ý dŠ ‘‘‘NNN\Ê–«”Ý«ìäÄÄÄK—.å¬kß3é9w±{¬ ÀÂÇLJôœÛ¬{ØÍÍ«äkh°¸cܳÁ žžJÐÓ¯‡+))ÖI%@»¸¸Ø¤ÕÖ¡¡6iŠÔ7~îÙ3-ýi 4ÜÃÝ]”Žõ®ÃL˜ ááz¶A€€{R­š<ñ„Þu8éÓõ®l† h@€4 @  Ѐh@4 Ѐ h@€4 @  Ѐh@4 Ѐ €cH‰ßºâ\„O©®m} ô.pdhBÒ?§ç~u&º¢{ë6h W  ÿH‹‘™Ãä•*RDVí‘ée›ˆW79¿L ë]te:ýsT´ÞEòóå•¥–‰ Þ2 ¾T/$~å¥èu)]Rœô®-1'Äm[züÇŸ/ÿe1.S´ùSUú·õñ2HÊÉ¿?"R¤ÜÀfŸwóT_$Sw¾ºyò~7ßw–>ü¨ÇÍ6/øsñOÑ‘‰âP¢MŸª ÿØó«ŹÄ[ËêÖKºü’…tÙ¿€ã#@€0§‰³|×=úä͉‘ïÊ 2–‰¥§r³,‡”ÿß×ûâ°²ÇË?\²èµ«ûOE¯ûô÷“ÉM?ñÌV iI¿|øû´°TË´‡G±´ë?Lݵ¯tŠåG£“Ñ g'ë«ëµÝÇ^ý&ÆÅÏÙIL¦«×Ö¸Oþ2n^—èëë"ñ)q§"gO4VXX³Ž§%=Yôû¨ï㔵J•¬ë‘°oRÕÎ#—Nïãã~«Á½[t)¡L¡bnÉQI‰G6~5Ícþ+Å=ÜÜ«×òŠ>w]YÈà^±ª{‘Š.Æ\ØyThÈsi¤±¿ü.Rã=™•$=&IT ù{ŸT2Êþ¯eâ Ù|@D W”^cåARÔI‰NÒ¢¨e´†Õà²2XdÉß2»ò¿C8 ¥7ô¾l¬'£FÉÊý’â&ûÈœ¥¦×­Íß+@ò™ [ν¼\Êv©ûA¯Â®)±ß ûö¼ùÈêsí+—ÍF )g"YÓsÅJŸM«XÑÃ|eÇ¡çß¾ >h°œ9¬çB×$´ûäñ¾U §—ü6ì+%Ç­_WôŹÍ;”NÛÿ鯯nH’˜¨ÿ˜êÔ2¦ž=ó©šž‹w¬?÷…¢žsÔÿö š~åä’£[ÚÔoç™Þ྘F“©¡›ÓõK Þ»)F®ýqlXñG‹ù¾<ÕÓ{pØ÷E'}Pª`<›€NÐçœ\ÅU¸´IžÙ$Q–Y"f › MÞ³Ì/ÛXêyɦ2{¨„•°·¤³ÔýDí•?¯Z(÷ˆø?—Œ›úIZN¸Êâï!§$ìKùIþš'>†¬¶’wû@7®?YRQ™HMH‰»šgvò+&r^9E¹q-U² ÍWÿ¸¤†eÃÃ=-#: Åëv(}á» ÷,[¹|ûÊFe¯—iXÊ﫸H·zZø+O¶SÕæE=6D&HÊùK&³8Eý~á”Z]ý–Þž–°l(Ù¨lÕW™¯m>”ܶAzƒž©çféZö.Öú1—M›S$!!2Î,\3ä%4ä=ãÍ!ËQ›¤Õt92B¼M’rBº¨¹¶Ìp9´É’žÅ©øó£*5õ1%\üêßÇ N73jÂ5ÓÍŽÝ”Äã—ooÂÉ»´‹XmÒé(“¹ŠÚ ’ð×Åìõ_gÌ©H9å8HV^•«u«1ºÖ=—ÿÅÅ>@ãlŽ úq-ôo7¢1}@sµöR¿ø‹•xD4ÝQÁé¶¿í†;û)m¸•üÉ|3§¥\O²DÞ˜½'–P礦&¦ŠÑdz˜AΙåÒŽ ?ç]ÃÓµãŸ5w n6«Q¼¤Ä^ó¾ïÏžx,°¢{š²ÌÚ{@k`(ñ¨_y‰=-)a+£úT/]ÒYâwÎåäÂ… êR*;m(é_ˆ¹q-EJ¹e±8€@€ûàts¢ÁXYÔ<oÅ~\k6-,ÅŠ\ÿô•ð_ýSþ<”R³cñø5WLW>™°ÿŸ¡U:5r>–*Oîs©R‰ÔS\ÕpÞ~ØrÛ k'³k…rÏ=1}ŸÉü÷ßÃ{F”)b:éT±˜Ä>À§˜8—-7¢ëùÿûñưƒ}zŸ¨\ÌñOB‚2¿ºïð2FIÉN®åKªã°/Ÿ5èJ`À7Æ–.áè#r À>”i%5D‹¬š.$ÀE®í¯Hbqéò¾¼R'?mÅŽ9•íòÈ„«|¹.:êʵ#.ÅÚ¾öh¿æÿEíþâ÷Ę“qÑ©®Ïzìy÷#ßm½—pÅßwÀ{UkîøÝ SRo^®gôxrB½ÄÏ,ÙvýZbR¬{Ég&Wª´æ··£Í9—lp®9¸Á§Ç¿ZyàtÜ_WÅ­„wÓVýzø–u“lhƒ[ãáÕŸrl[„)õRBìã¥ØìƒkUùb”4ÿXbVÉCåäÑÒrtŸÄ)óɬ*ùl+vÍÅ£ÙÐz͆Þ1¯Ó›wú÷'ŸÎcu{ÛÃÕ›‡ºcyƒ×˘“~ZªvO{¸z:)ß}? m}ûòÆÒåg‡Þq‹·jÕ—„V¿³Q—*m«Om{çL+¯»TÎ*l:ðŽYnåÆ0>ƒõØì„Aš}(áÕdâ'òóaÙ)že婾2u¼Tµá•}y³‡fŽ‹ž?åèŽÓ I•ªÍ˜\¦”QRÎE®ûËò[Õe\õ®@®#@@Þ+"[3¼gƒ“Ô,¡ƒï»^ý™bžyÿv2jöá÷Åü¾¶­ s†B^5‹%­ØŸ*»þÐ?"È×|îëW”=ïZ´ßÀ…¹»àøÐhbpm8ªÑ;Ç—nˆ:|.æ`”8yyÖ¨ïß­÷C JsÕP ÐÊÙýѧk>ú´ÞeÐЀ h@€4 @  Ѐh@4 Ѐ h@€4 @  Ѐh@4 ¿ú_›6z—  "@À=âãeòd™>]ï:ÈùózW6C€€{¤¥IT”å vÆÍÍ-))Iï*œÁ`0›ÍzWØ54XDDD(¹A™ :äë޽téRÎZ =ç~€, À¢D‰Jn0&“IïZ–²{SSSÝÝÝ‹-š³ªV­zìØ1—””ÛÖ†[\]]“““õ.°_h°ðõõ-\¸°’”pæéé©w9ŽéÆÊîõðððññÉY GµmIh°xã7BBBbcc]]]ÝÜÜô.Ç1]¿~]99QtÆ õ®rŽ ¥J•zâ‰'ô®  Ѐh@4 Ѐ h@€4 @  Ѐh@4 Ѐ h@€4 @  Ѐh@4 Ѐ h@€4 @  Ѐh@4 ÐÀÑô¹sçJ–,©w`K Êw½ [ºzõªÞ%ä£è´´´Ë—/ë]ØÞ7ô.`‘¿tRR’Þ%À6’““õ.![òw€ŽŠŠ2¨Ì*½Ë€f·â\tt´ÞµdKþÐÖèìääD€È”ôœ––¦Løùùé]K¶äïm4UÉÉÉ®®®JŒÖ»"°=ë{šÊ_9½ ÛS¢sjjª»»»ÉdrqqÑ»œlÉß:$$Äz¾ráÂ…R¥J)yZïŠÀö._¾l6›¹Å‡¤äæ‹/úûû+Ó;wÖ»œlÉߺråÊãÆÓ»  ù;@yŒ h@€4 @  Ѐh@4 Ѐ h@€4 @  Ѐh@4 Ѐ h@€4 @  ЀÿväÐ`¦B,pIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/yuma-tools.png0000664000175000017500000021335614770023131023242 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+ IDATxœìÝ\Mïðï½í½§¦TRV 2¢’•Èž!²÷.;#YY!›ìM™²ýlI44¬†Æ½ÿ{[ŠÖ!nåó~õÊ}ÎxÎ÷æÜÛçžžsŽ0—Ë%(aAP• @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0Pµôýû÷ƒ‚‚xÞ½{§ªª*"""芀¬¬¬øøxMMMÞc777sssAWT¶ª ƒƒƒ/^Ì{Àb±¸\® ËÆ ‚ïô߃ PEU¹ Wµô›7ox?qAW¿Ž—žs]LLŒ k)—ª µ´´r?¯T­O-P  È©©© ¶’rªÚZHHHÐ%@Å`³Ù‚.¡\ªv€þ™°°°¾¾¾ «€2DFFfff ºŠ_QÝ´††Fxx¸ «€2èëëó2´ «øÕ-@üQÐ @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4ÐPE|9ã¢æp$-ç±\¯‹Ñ»ZH ¸"ø7!@0€ eÉx±ª•é˜ÿeæ5ͼïÞ˜ÝP²Ü«s?]Sßnõ›¼¦tûO÷Ñ›ÏoÉxîS¿öôgסſwaƒ4Ù×!@5†ßaPQÃáÛV¬3ârFNóÑœ^ Úß^ÐXª\+s?^šÞ§ =“lçz"=@U†_cP6ƒ![W¬ãq!=§ùla¯¹ï.j"Í*kEîÇ‹Óú¬}›ß”ïºy]7M¡?W(ÀŸ‡ å!¢?hËšƒ&îgRsš/–ôòîtß×Z¦ÔÍI>?¹ïúwùMÅ[ü]ÔŸ+‚‚E¿‘C#3J^"ãÕÁ­çó[ÚN´EJ^ZÜØTªÌC ÊGX§ß¦õëô;õ5§ùʯ÷ÌÎ÷W4“+1vq’ÎMê»ñ}~S¹ÏÖUÕŸ+†ªý´Õö¥-ñéhøÖs¡y 뉫6´Â+*4”—°v¯€ Lzû’ÓŒ\Õkzç‡þ-å‹ÐœÄ ý6Çæ7Õl_ᬂsÔ @€€òªÑ}ý¦ƒ—ºú”ÓŒ^ÛgŠË£uö ?cNÂéqý·Æå75ïXæ „ø Õ40!¤áº6Ðí¢ë¾äœæû€¾\mr,Ž9NްãC~SkØÎ¥öŠ‚ÏœÔw÷¯\¹ýøuÌÇ4–”²NíÆÍ[5©)Sˆ’ì/¯o_¾vçidü§oliU=SK»–u¥=…ì¯ïÃ=zòìeT|ò§/é\I9ÕúÆõ[˜iKW©‘,Üô¸'7¯ßyÿ15KXR^UËÀ´±•¥‰šø/ ›®ØÞJÇI‹yrëÖO_EÇ'ñþ „Å%%¥åÕ´tõj™Ô3ÓW(eL8@© €!µÎ«·öºÐiwBN3vK¿q]omÿ}xFvü±Ñv%ä7uGíZTÂ0äƒvŠ]/ç>Öžp'Ü×\¼´-§ßhÔhYÞ%=šHºäªðÃY¯W6ª9öAnÃxþÓ3j‹f'ßœ1ÞkÝ•Ø&u»±ËÖÍëiRøb"ߢϮš6uáλ\Z«í´Õë½:é—ó2ŸÝ·+èЉ‹㳋_F¢V›AãfÎÜ\C´´®óåÉa¿¹>köÞùPÜlU‹ž#§yë\»ì«±T|o¥ËN¾»{ÑÜ%Ž>úTâ2ÚŽ.=‡Œ÷tÒûý-À¿˜b«¶_¹­ÿ9çmñ9Í;Œq}²£“jÎaÕ츣£%å/[sÜ®ù¥œhø‡q¿>Xݳõè Åν´¢wÝ‹ÿ»àÓ"çøxVìé)Î.~w¿»tô³k‹¯ž˜Ô°¤ˆÇMØï¨îv¡„Ü\ íåÙ5#ÎnXç±ûøÊnz•4¼e'\öéå:ëlñ?ºñ·öxuÙãïàsh÷d›ÒÿÂP±½•Ž“|mQWç~úôƒ´··¯ºõÞ¼§CuŒ.f €9¶²£ß÷‡¼“÷ éút«ºPvìá‘îyÃ;xjMÚ5ÇZVPñ9ãõ¶^=GŸø~ R\NI"+99…ó}™ìÿ–tèW÷é‘>šŸÏoÞnõ‹‚9lIyÑo‰Ó õ˜ziŠó¸†6´)!àq2ÒŠ¦g–´Š¦²œ¬´8¥Šû6±P6Ïz´ÁÍ*õн-•ðÊ~Ùñ§FÙ8¯{Ytª¸’––Šø·EŸG|È4[ë÷ÁW–;¨”4 ¦B{+CfÄ×Ö3.¦ž&¤ c¨£&/%”•òùSâûWÑËúŒP:hølEû¥;=‚í7ä^åùã~wOW«=Í®x>PpäÏxênï&¥_)úOz³m Ç~z–µ2Ψî­ÍTEyÅp¿ÅÞ;¼|üð%—ò ýzrÜÌ ×¹æ¦g û ógxt±5”ç¿?rR¢®íš?räÆÿòîd³iäÊÑæÔ-ùÀ±°Ns7·ÎœÛØš×)ôü³>E\;è;Ççøkn΄Øý‡w°:ЭrEèŒëºu)œw•[NZ¾p´k-‰œ'ÃM}{}ÿò©ã–_Éÿ¤ô|µK:‚‡Õ,fTqÅöVnbðä)ßÓ³R«I~óGum¢-YäOÖç¨ÿ®?´wÛöK¸ø5ü hø5l…V‹vzžj™wŸÁO‡Ý»¨ÿàpÁñÞ:³vϲäÍ9ÒßıLÇœ<çë¤þý½Ž%¦nÞcqpƒm깚›Š¶õh½;™W¹TËÅO´û·ØR:¶C7\ª#Û¸Ù²¼¾aÍÉë­‹¹“9KÆÚçJDcëš%œn(,gмÿ‚fný¶ô¶q?œs““/G¦¯yÒq^)yüoËŠÚé1ñrÁAavÝÉçCóƸäaIj[÷÷»ØÎaœ­ãêðÜiiÆyìnÜ_KèOöV¦Oa›NÉ{,Öríà¡úŤpaYFÎCy_^«Þ¾üúó5dÊ‚ ¿Š%oç³kÌ©æ+#sš)׃®Ì«;g×4sIV@Êiëé"é¹€xí!+Çù5Zò&·™ËÒ5<ŽœdQÌ€m–œÍôå]:ÊÍfqÁGÃÓ­s£„¾m™E±$ŒnÞzÙqGN„~¹mÛãeœ>ù¥Ý[î}± ðÊvÚ¼°E±V„TüÎlzf2ølî —ôs^+î»ù6’øs½•)3îAxþ1ûqnÅ¥çÂØRÚFÅ|( 4ü:–¬í¼ÝNX/‹(:¹áÂÝ“0Ë>‚ñä¥ÝµKz—“¨Ó½«ö’ü‹z‰¶Zäe§PÂs–¢í –‡Žå¦³7Wâ6ü«®±šy¸¨ìØ”s9Š·!¡ï|Ì *Ç5ÕRn¯ß]Ð2ŸíÛU³ä£Àº}WÎð«3óIn3jdž»sl %ÒŠí­lœŒ´Ì‚îÄÅ+ÕÀ¨V àw°dšÎÞ=åx“Åá“ØíoV ލš ìaTÊÐq=k#¡eoóÎ'i>ÌA£”Ä%eh£OÇòÂ]ìóø Rûa¢¦êD¹×s{}íuúøÊ ÓŸý~¥ !»q=ʇ,jÔolÓ™CÃr[ñgŽ…§Û|?4_±½•ƒˆrM¢ÜOs)g×~ßÊ­”ÄðË à÷°¤-'Ío¶ØíJ^[ªó‚au*Ã^…ÆMk”ØÄ”4dˆòN%¬igVêÕö„´ä ) _K¿ŽCVÒ㳞<õÖO_¾‰IJ+mñ”wïS8$S †âr’\-8$Of®6eÞ|]HÝÎ¥…å}°xsåáGNÃü‹ÂUloåÁV¶v1£°G9χ»7no±×ˆ®MóÎW¨(ÐðÛ„E æc‹W‚(È£¨§Tú;KLæûáME}¥RÓ6KTZ‚÷¼r¯€—õ-›[ÂrœO÷·M1yíµR.yü£´Oi¼~+ÁO-#öÑû‚†|Ýz*eÿŠÑ0¯#MO¾æ¶Þ?ŠÉ uñ?Ñ[¹ˆÔäÓgi‡y?ü˜àEýx_ &­Ú´´³µ±±ibª)Y ~ÐPÕ!@@5%,&\ÆqGV¡ùåXºìMf¼ÙíÞ¼÷Ψ²—,";“SRÿ»²>½ÿ\ÐPÔS,ϰÞrDy‘÷ÓûOY¨·òa+;¯;»<±Í¸Ó…>À$?½ÄûZ•³€j½6í]º÷éçfWÒÅRÊ„ P1RnÏq*œžEô[õêÞ¾•uÃ::šªJrRâ¢"By1<+Ò¿‰á¸ª´$ÜÌï'ᑨ„Hy>°D%¿ß‘<3=óûGŠí­¼XÒ ÆÑbϬù«O‹÷™ŸøäÒ©Sg.^º|éòWŸ‹.“~h†cÈÙ%WOLl ÈË•@•„ ‚S8·pËÌWÜÌt¦Ñÿ{²Þ]-?Ë»íÜ=β´;td}ŒþXò\A–Ó”%Ê»NRdïÇ]æé ™¼å ršrß«Tlo¿@D©Ž}_Þ×Dâ'‰¼wõ|ÈÉ£û÷Pð£O Üi†íãVÒ¿³ø÷ @€À°…E ÎËKÿœÎ)cñŒ„B᪲Iyz¦à𳜓G åRÇ×r>=»ý¾´CTÝL“(ïÊ>øe'UÆo‰Ì˜»ùçüñhši|Q±½ý&a9= gwÞ×Ì•qW–÷ë8åL^ŠŽ ð¹àu´£"BÐ 0BÒÊRD¹·÷û•ð¨´{¦G\|ø­”ù•ý%:¶ 8µ:¥jå&‡í½[9Î,‚­XÏV›näeÞG‡þ—0B¯ôËÈeÇ]>RðÁt›Õ“/tô í­Âˆ¨5›´{û­šäêH»ò"½cÁß÷ªhQµÚjù:óñň´NKŽ1)÷¶ìazy )ëhzVäÞŧ~:½­27îÔFyY`î,²C—ï{í6º´[¼d¼Ø±âzÁ'Õ¶ _t®b{«@,EËN¦tàznë3ó‹}À¿FTÛº¾4½Ìý›ý»}[îÎolSÂ`Ôô'kǬ­„ƒò Éhñ:焎:wå}VÚ%¼¿fE™VÖx‘j<¬VàŠ¼ûoßñš|¤Û¾n%Ý¡1+j÷¸ù š:}=Ì‹Þy»b{«@Ù)‰) I%\ÏB€Á‘1ïÕ\üà©ôœFÌZE}¯Ík*ûãhTnêó-í'ߪ¤™3—T'3:q'·qoöÔC]w»iþü›sbLë¾Ç¿üåêÊO¢á¸9-×¹_Ìý(ðéPŸ. u‚g4‘û)brÎMvœšßo3wlƒÿ€P±½•éË•©cÏÔ6ª›…jiƒ§9ÉWV,/¸„ iKŒßf @pXÊö“û¨žÚŸÛ|¼ ™UÔÒå3´2’ÏysÊúøâǶøÎ^tâ5—÷~UC‹õ.:³´GX»ãÈ–c^Ì-/ywó”ç›–mg”‹î쯯¯ìZ‡djj˳¾Æ½Jø!ù<²wh £›+¶·òùòìÜ–9¼/ÞC¶¼¦žº²‚¼”pæ×Äè—/ãŠnL£ïæå¸0… %¤Ù-ðôó˜VÞ×Ó Mýö)áC¡&Ëdø•N¦ÿÛå1!n6éÄᨖÖ?ÿ>í[Ìóû1ESv^qfÏ0…­·:&„ÔœýÃB5{ºzKü>õË»OÞý´¬ŠÃ‚ƒ»¦4S*ùxqÅöÆçãûW‹‡žý4¨Aܸóä%˦v¨)ÁJÿPÜꕉ°Fǵ·ŸØÍ7Í/$òç; Š8ŽôòñêÓ@Žù÷«cDHÙnVÈë.‡–Íõñßw7±¸ETõ9Íkœ‹‰L™# *¶·É9í¹{âıcÇOŸ»ôÓ­ Qªïâ>vêÄ>–*ø¿oðÛä:],û>‚¥Óm7ë`»©‰Ï®_ºv?ü퇯YÂRJZF ¬[ؘ(åÿ1_ÜÜ7Šë[Z7ÂúcþãŽ)ïF%›¬‰á®)ïÒrÎe—ãI²¤Mz, î>7æÁÕË7D¼OJÍ‘TPÓ5ªßÄÖ¢–|þ{®°Þ¨ûÜQåÝö¯øýÿb˘v½·«÷ÖØÇ7Ãî>~÷)-[HBNMÇÐÔ¼©¥©º“±Ñ›LÛé¥<)–¸FCç!¼/o¢¬/ÑÏ= ñ&6ñó—Ôo\aI9e Z¦ -Ì •E1j~4T"Jµ›w©Ý\ÐeT^’«oß½¾½ ë¨, u3»Îfv•±·’ Ëh™Yñ¾Úüé À¿€h @€`€h @€`€h @€`€h @€`€è_ñîÝ»-[¶|ýúUÐ…üº>}ú˜™™ ºŠªúW\ºtÉÇÇ'55UÐ…ü:yyyè_€ý‹DEE þAÐ @0€ Àô/ÊÈÈt пÂÎÎnÚ´i¸ŒTiíÛ·t Uô¯¨Q£ÆÌ™3]4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4@µuîܹäädAW¥iܸ±¾¾¾ «f ª§€€€aÆq¹\A¥‘““‹ŠŠ’••t!À4@õtëÖ-^zÖÖÖÖÔÔt-P¼›7o~ùòåãÇÐU 4@uæéé9uêTAWÅËÊÊtÀ4Ð @0€ À4Ðÿ¦Ï§Ú);ŸÎ,vžùº¨›Ã´…þrEñ5´·VË£>žk&&èZ ª@€ø7I5Y<:ƒŸ•oáëŒ9a8oÿÜÆ’üYŠuU)=g¿YÛ¤qÈ‚ˆ£?]Ö¸”Y¿Ùóï7´|m{3MAU4À¿IH©A+‡Ü‡iÊÁR$ªaaïø›é0åÙùð4Ƴ~³çßÀÉÊÒl=p¸À €ª Jþê€×ÈY›Bž%sØJõ\'­Z3ÑNE(ëí¶&^Œ¸ùßb )"îçK#LZìl´-Ø~¹Í˜û¼ÕåXZãn‡û5’Èë'ëÍ ½‘…g™Æ×3oÌw'ç ›¼îÔ“$/â›9_´ÖË!+à‡Õ z¦Vq®!«,õé®)ž³C#SIΤӸëg´UN¹ÔO»åƒIA‚‡Ï½l°ñ™ÏÅ&öyC8¾œë¦ÙæÙ¤îÏfÏÙóè#‰ê´¿nǼv*ïŠÖÿxBì’b·ÿh(7ùÜ(Ûnò›¯µ“¼p€§}ÑÇ—Çi÷Þ°j—ÉС«„M5áü·ÔcÝ—ö;×õnªä–`Ût±~ÐÓmÕä e\am÷3aq³äÒÏy߳лÝ}º,ˆ°õÒþfšôþÊšaî.ݵ_\(²zញ²‹_%t¨ö§àav}ÖóÞþ¿Nº)a«†z::Š>º1MOXL˜ûrËœë]×^\bj¢’p± /–°¨=Z:þò¢á[Š…oh7¸KßÚ/Cú.@9åp'ëⶨ‹_©ÿ ¼Ú ÙïÌŒ©53d债bDFÃ×n¹ÜjéêûÃV[Jèô X±ËtÌð­]Ö¦y,yßnÓ¹î5„Ø$¯(-Ll1yee©"±Å Í·¥¤žë¾»ž¡Ö±7Û:’D5k.?gÖ+FUED\¤¤ž)£øU„²cŽ-ØõÁbmà,W!¢þ;Þ§.Oz•œ­Çb¥ˆX3§»¡ÿ$Ÿ¹áÄe*¼_f}Nô=4=àB|ß~Êß yUüÿäÿT.ÐÀ@\\ÜðáÃß¿/èB ‚©¨¨¬\¹²fÍš‚.*‘´çsT»9ä_›B¶~ÇBۮފ϶ÔÖ°ÑwWÝÑ-ZqSZm|ÔS‹Áo“Rz¶0êÜNÓ}Ƕ£ûwvrlÕH»‘:‘â/Â'^Â*)·.>áhôjlY²¶óØò¥¾â}SnbUÒiƒÖ–êyÏFT£¡¡8Ý“AÊeoþÐÀÀ AƒNŸ>Íår]T¼øøø7nº ¨D8©I)¿ÑFjc‘ɺQÉ™¤Ë ¥"úÝF·œàzRØuD&ñ¹ÔžYº­×Þ¾`6oÉf@o.)4¸p㊡ K9µ‘%_ü*”’˜Bâ²âìb×’—(éˆ1oVÁ:l1i1JÿœÎ)Ïe‹ßTCÐÀ@ll,/=»»w²´4t-Pa>|éï¿/!!AÐ…@å"$­"M*í÷O5ÿ>•%¦¬Ÿsà˜ûùêÜq§el­²žwÍÎÏF†U1=‹h´³–÷•‘ðèü¿ c‡·«÷rEi»Š¯”’?Dg¿«ÄrS“R Öá¤}J+’¨KÛâ:ëF—@µ… ŒµjeÑ«—£ «€ sòäÕ5kö º ¨t$jµ2eïxž¤dT'ï丬¤—‘ÙZ*â¼èÉý6§¿ÿ·þÁ‡}¾®Ó±ß·ûK­ò#t)¤Ê™UrÏôõåùS•:Õ—c‹*›9ZÎ Þí|ëaR¶EI=s¿¾¼Pì*"ííjÓŽ«W¢3­øƒ5R¦: Ò}ße<ñØë7b³šÖäWö-òO3Øz 4E Õ_â³­¥0à Å`kt˜5@Ía|¯YJ+7UN{ì7|ÄnÕ5O/Óÿvk~?¿d×C‹ì•U¹¾«Û×îÝo¾Û½E– ’”þ(øÂm 3c…By’]hVÍv%ôì!÷bÓ°.çm}6Ír©«Lîí\úMwxce!vbI=g”°Š¨F§i=&wôê=U{y_ão·ÖXr…=Ë_[”>”ñÌß,¾°–ï€báÛÆ®|-éäÓJ™Í‹ýù¨«…oÖ;´˜"ÿÌTBÐP–bÿ+ûGÎl±è3‘˜¦e·%g–ÑN½;»ÿ’Çm;¨ò‡6hº­^º±ö°¾ ºÞõiØnr_ƒž+],vu?û*È^æ{oBê…gí)¾g9¬ ñ?vI/Ëi)üÁÆFöCvÏ5— –z =³K\E¢ýÆÐM2Þ›ùe”ãÔ£3ê‰SjÏ»æ %ßÌi­ëGLÏÁëh`Wu~4.T@ÈÝïb¶ÿ h€žDãÑÜ?MfIÔê¶4¸ÛÒ&KšÏÊÿ½-¬çq1Ý#÷±T·í/»m/nÂ5ŠÌ’)®g"¶¼ÅˆÀÿ,kõò¬B,éºî×Ü~(ß: ¾ÐPé»>Â’¬3È÷æðÍ¥жø-À?€h rü4œ XÐP±²"üzךQh [RU«¾•E§~nÃ:ÈU“ pÞ¨áþÊåÌ™Cm$ËZ6a‹•ã ‡-Bâ|ÛÊ”±,T~ÐðGÔ2vB1"nÆ—QO:8õèÁ…͇?<¸¹b¹Btæó­õëžèûpÏ4ãî· Ðð'°ôÚwöì(]Ð^¶áýáÉc»­ppU|t¦«AÙ‘˜“xëÏ2ÿ`‰¿þ–¸f—åþÏ:8»fæ‡Ý9w,ã¦=Ý»qÚ’à³÷âSyû¢’^«Þ|ç;וJäy¸É_qzm«é¢6§>¬t’)aáboÌIÜnãÐ?Êíüaã=cÖm KÈ"i—A›7uU:³ÞsÆáó¯ÒHJËqÂôm^–ªü+¼fÅ^Ü;}öþCW£?qHHA§Y—®³v·SÍ¿1BöÇË~KÆúž¿ŸMòzíG™¥Yd{I7Ïš´ïâkÞ–„õìûX<·]}Ùb޵gÅÞô›¼aý±‡¯y["1õúMúNíÝ]OªÜ·AÁB€†¿EHµ«—Ó¤³GO>ýê`)Cœ˜ý3›ö¼”mÝcé~k#™ôð“;&®šÝìdø>»N;·N=è”çE†Ò¥,ÜJõç˜ÊçíÚq—FŽë³xm„)= ˜ÝyÁªq—ÕDÌÙ¿w«Ü‡cǘ;u’Ýñm­$ƒY8‰1uöÙ5µ‰&½¿~Âkš_‹ÐØËwÇ5“åÛ¬—kÇ·žü@¶­ûöñMt816¬èy !cÜÏÿ[mm»ãEm§Û'Xkr¢®›=kvã°Äû¡ýMÅ‹–éçä9å±ÑàEó;דJ~s. `iÏAÑÒ‡v¶—¯&ãê;hø{$›ÓÑ«Ï">dYʧ¿yÌ­kÛndÐø5øÑѾumîÕŽ#üßÇV.††&ª"DâZfÆæü1ЩKYX±„íe5Yè=ʼnu&vðòZÆÜ×ûê iòê´øÈŽK!ïÒmEÖ?-Ödûレ9!¶yc ñÈZcƒ¦íïsÉ]U(-|Í’Yòmw sRà(¶kiÄ­Ûså¤ãÌèÍ£w<—my tŽ«ZÎêvMl•â <¦w>ÖY®ð‘eNÒ£C÷I¾÷„åãå p±ttndê{>M<‹C„ ¿‰ËåÞ¸q£iÓ¦‚. šC€†¿‡%&)ÅÛãÒS¿ñÒ"I6ãwuN¡ÙÂò†zâtïSìWý˜‰K_¸¤äiÐÉ"„‡¨¼¶<‘„¥zÞ¨ a ÞF¢âS2â#<%¶]'G‚~„uÚ9˜Ž}vçHøWwUéøG¡Ñ$lߦ‰B~×uqј·4™÷0;þnÐ]up²•ËJOÏ›¯Üºm]zø¿C©Í¥ Ä–Ö4Q¢G7,>4qt{CQIÖèU“Á ™™™&&&¯rèééLOJJâM\]Õ4ü=œ¯IIYD2 Ò9!–ûõõ¾E«÷ß¹ÿ2)…S°Tñae´p! yñ‚ã¿l6¬dþ¨fVÎ.‡›ùáí"…š*’…Ž‹(×PfÑã˜øÏÙ$–“D$¯£ ö}¾°RM9"~€ÎüO”2Y]â§^'¤q©p€&龇=c»¯ïÚk>IÕ²iâÐÞ~ààV”ñJ„ß’ššª««›0hР‚ô,.Îÿ#‰oA%ãr¹BBBl6þþPÅà×6ü5ÜO÷®>%R·4Tâ¥ØÌw:ö~QØzÔÈfÊâ¬ôk£9_ܪŒf*'7sˆâܼ)9';þ8“¼³òR}uÍ´ kf›z…®Ý§Â/JLLä¥ç”””Ù³g{{{L3fÌÉ“'ãââX”Ž÷îÒ­[7---AÌ @Ãßò-*ÐûÂWR<ÐP’‰_Ÿ[u1CÌqÞ‘•­Ur“#'ñNZF±«2Z˜)Q=5¢¨ˆø.dØÌoâ‰$µ5d…HX^M(æíGÞöò'gÆ>IÌ}$¢ª«Á¢ˆT)S«úå<Š$$U«YÛ1¼/NrX ½Íúy/x\î ‰ƒPÀ\dd¤±±qFFÆæÍ› TxV­Zµ0xàO@€†¿›½wĨ‰wH¡ÛÄ)æü?+s¾¥|ãåQU…üܯw÷ù\ãÏÉÌÎ=öË›‘wœ·ì…[µ[}ºyãð©wmhåfØÌˆ#ÁÏHľ›‘4ouSk5zpëLX²]îI„Ü/Ïwåhþa+5èÞˆ®^ß·/ÂaŒaÞ®Óíëßlòà>µ ûà­w;hâ’WM}&Ì»6[¡¾•µÚú»Ÿ¿¤á,B`îÞ½{7ær¹'Ožl×® ËøW @ßÀ…?¶CX£ÿªþ묷µ›7Ç¥…¾Xòã«þÞû®²[µžÿà cXÊbáÇ\‹{2Õ¹¹¡œpꇻ¶­a7™d«×"0âääÄb±n޼ɋт.à‚_ÚðG¼Ú¸bDACBÑÔÊyîð~£»Èä\™¾GÇ~ñôë;|I´j?ëØ”Rgßœ›xê˜þŸ·_á9tWÀúE=v›ûßõ/}á› ŒÅ‹­£\Xò-Ƈ]Öœ2c>'>sIDՠ͸ÙGg·«/™»€¨éäUÁ™>Vè¾›ätÇÌ<Úx_ýŽ×Ò2y š%c5âêM­Y3wxNòáŸ%©iÕÑóä‚>í~“!¬×ùÈuaï»wLšáË¿d‡¨ªi#Ï€¹sÝuD½~øº»»‹ˆˆ<þ\___Ðåü[ ¡b ŒßË_ž%Ù V}ö=èStb§Co;å?6Þð°Õ†‚9¥/\KÎíÊm·ÂS$ÍÖÄÜ^Sh‚N׻ܮŨ4ëx¹W`IÅ +;x/sð.<É*ýûà¶‚…‹ˆ‹±ë²•Þ¸=0¿!×°ãŠSW”´!€r˜?þ¬Y³$%%£¢¢”””]À? *6l؆ ”••###¥¤¤Ê^*4@•áìì|êÔ)}}ýgÏž‰Šbà€` @TÇÒÒòÎ;5ºuë‹…ë† 4@e—‘‘allÙ¾}ûãÇ º€4@¥–’’¢««›˜˜èáá±~ýzA—Е/7ëè褦¦Î›7oæÌ™‚.ø *©ˆˆ“ÌÌÌ-[¶ 0@Ðå@h¨4²ãš´óxnîý¢Ö2UiÓœÏ/÷-Ûpàæ' ©ü ¢*Æf½zΚÐÂH*÷L¯¬¿Þµ&DÕ^±mTM¡zH»ígd±;š$»Ÿj%ùõ’—v‹S’£¶¿\UG¢ÛOºqdÖ¬Ý{/D&f“¢®]×îótµVÆÁ«¶Û·o7iÒ„Ë冄„´mÛVÐåÀwÐPi°%êöì1,ÞX;çÚ\™Ï·Ö¯{¢ïÃ=ÓŒE~nþÑM3ÂI¾3¡©ÇŠpɆݻÎmPCŽuíСÍÞ“ö¾w¡Ÿi‘{$>[³ûÇÌšE·“zkýéè_,û1ÔϲeP„œiÿ™“lµ¸Q¡Ç–,±¹–xçÆpsÉ_ìîôéÓÎÎÎ,ëÎ; 6t9P4Tœl[ÆjÂD«üvâ­ÿ=Ëü>»h³¢± ošNôÞ•+ÂÉÜoçõqßoÇ=hdW÷ní¶n˜zªã±.òù×Sma’pìáı ¥jnò}ÿýÉjM4ão|d¼ý¬÷ÛÇE°ê,»±q¼qÎöÝ;´–êÒ" héõþ{Z#AWI›6m2dˆˆˆHxx¸žžž Ë€!@CÅI}0TÐÆ“_ßtÓËÙ³¾ý·Æ¤Á–×*.ÿ‹œa“帉4']m¿*z¿Ñ+ÇïÜ‚7Ë-°éb\ƒýq ’œrÆQDOŒjë8è&ùéµ­¦ w«õ|ÿ³ü¦¨Í©+d)gèÂáY³‚ö]|EBŠzö},žÛ®¾,›—··Û8ôêzºþñIë7ž‰úL$_»Ù蕳¼Ú*þ8~‚Šáb´nVBD‘¬©¹J‘ƒÊl9‡UÛ#ÊêjHºZ¯xý^æ×g\wsè¦æÑ–óá\ÐñÏšƒÚ«®/6@g½[iÙiì#ËÝoý{ªŒÊȸ3Á¥±_Z¿-ýGŒñɬ×ß0û,©úNú¬€Û¯ß¦rHÃ8ªooï¹sçJII½}ûVAAAÐå@1 ¡âHtµ“Üx$ôöÇnzʼܘý.ô¯I’>Ü>ó:ÃÚ”ŸðR^¼™!bÝÓDŽÅ’àMøxþ¨lÍáÓ¶iZHQHn?l…N;·N=è”çEJbŸœ¦4 ¥y q?ÿoµµí޵lŸ`­É‰ºrlö¬ÙÃï‡ö7çíÚq—Gv}n?köíÍêÜW¡Sº,™Ûe~ý×¾]TJM•,FëŠèØIú^Þ7eKϽbq™-£^ó§áÔòÍ]¥¦î[uoi3…ÜE³N® K7öèc|{]±õkt^wÜл›Î'w凜·ù´Û‚âHÃm¸­nSɾ­Š¬ñön,—äÍjI!=W9ƒÞ¼y³²²ò›7o$%ñ€J *¤y÷z¢ûŸ{”Öµ…$q>…~%ݶŸÃÝ­'¯&Ï2U¢Œðw’ɤ§•‹¾ðdJxúÈ#;GkñÇ5gÇç÷#¤hhh¢Ê›&®eflÎôœ^´I”½yôŽç²-„ÎqÍ=.k×ÄV)ÎÀ3`jpçcrºÉNÔðÞâÛ[™ØXËÅgü¾#3î~’ÞÅ®¹¤¼ë²”'oîû²çŽÀvz[5Ì›¶iÙÈήQËæ&úrÅéf+5í*{tïž3ñVÝsÊÎzsaõUNÃ•Ž†b·K(…]£}7;a¯ËoÆ÷pRÏy®©÷ïOzS;7øé©¤?=8bq´åØI–å8ÿ*GGǃ§OŸŠˆü±þPA ¡±š¶mDaW‚£Ó[‰yvàÇl…}7ÎŽ‡Ÿ|ª¦”{!8Žê÷±û>Á §³Æ/$…ìø»AwIÔÁÉV.+==o¢rë¶uéáÿE¤v2È™`Ô«UÁ  C¢¨ØÏœòm¡Ü늨÷ØvÀv𩵃ž¹±}ÙµíËøSõZt˜8¸‡B‘×Kºéˆv5¶ï]y4¾ëPu!Ê ßµÿžPÃ-®"wJ,…­f5¢hè™Ãgãújð~tiwÏÇ’þ‚þúENP$nÊ£ƒ½Z.»"ßf×þÆÌOˆAáp85ºÿ¾¥¥eXXnÓ PÉ!@CER3ïVƇÜo¤ú$ôÚWÑ6:MÒô¿y_zœÒÒö˃#OÈhv-Þ~ÇÍY¥` TÌ‘Ú2e~ˆŠ'Ê™¬þóaÖ× iÜœ-$¥Th 0›Í %\·œÏ„ɺ,Q­æò¾ˆ›þáÍ­Ë·‚œÜ°óÐHÛ°'çwú·’-œ†$뻸Í]}&b`?£ìˆ-›Þˆ·õtÖ(u´[¾õ[éÓ¡Ï$öî¯ÂþòdÓád2Ü£Vá‘üÚ÷8ðØÀeß™ÉÝtðÒ®2222 £¢¢:vìxôèQA—eÃoY¨Pªm:iÑ¢ÐÛ;×¾§ae¯+®nk¥›t68"£îÛ w¹jõ¾e ýÒ±6–0/wKØO:6»öZHNG.¿Ïß9Ž÷ku‰«è5så}¹NèçcÖöð†y×½[:¨îKL·Ÿ§ÑÜq·=í1íóÁíQÒ,•Ùô¹Ônšuí¤paϦ°˜>dn9–ȶ]ÚR·àåËù¶hB›w%œÇßÞÕ£‘?W_¿~ÕÕÕMJJ1b„¿¿¿ Ë€rA€†Š%jÐÞZcÞé#÷ÞZŸx+m=™oÅkYÙÈo?}ùmÛÿ¤©Ø¹Ô®€±"ªº,ŠH•2µª_ÌÑ[î§ßßDypÓc. ½™i1b@-©"sØŠ–ÍÌ„ŸMLü’MªE^gÂz]{4›8wÇÖ‡6 çãUÛŒ°‘);¬Ëš sUÜxäLŒæÆËÉbMF9+çºç¦ÞZ0¢¹×³ÚcWYj­Ž×tÕ‘ ££“––æãã3uêTA—å…_¶PÁ$LZµ’ßwí@ÈëlóñÆü fHÕêÔXxßÞsb?Ê´lSOªÌ>rð‡r²8Å7ÙJ º7¢«×÷í‹pc˜7’!íÑþ±~ñÍ&îc\±Ï©ä9 '/ó1J­àÕTºP’Ïz{òØÍ,RjT[í§™f³1b]÷ÎûüYg¨«…ty6%Ùh¨CM{·»¬|*EÚ¹{›¼ëp>œXäìõDo̺ÐeŠ8ô\u¼|ùÒÄÄ$++kûöí}ûöt9À4T4i#7[±]›÷¼ÎÖ_Ö4g0K¦qGýÌÑ;דh›¥&å»U¶¼oÁÈÝ«ŽµV7±·,Ú´®'¯ÑUÿuÖÛÆÚMˆ›ãÒB_,ùñUï}WÙ­ZÏç_µãÏ>Ç’¦³·÷ºÐf÷«ŽÇ]:ZëiÊ e$Ç>=¿ýÔ›t5ûsëÙòöcZÈ;„„‘îÜAâ?/P‰zúéïñ™µZø‹l¯ ó®‚—útáˆSX®Z‘Aë# -Î’2¶ëÕZ—r¨$xAYXøûûmXX˜ —Ë={ö¬½½½ €_€ %mÙÝ”}â.GǪEÜQBÍšêRøvƒžMË1\O¤ÖÏ¡{¼Ö/ê±ÛÜÿnS¢ÍzòÂ2V#®ÞÔš5sw€ç$Ÿ,"M«Žž'ôi§É¦rž)XØJ­ÇÝ ·\³tßž“ó~ÍâOU56u™á>q¼£yñÇ„Yr6Ý{ª‡¬ÓèÖÛ¨ÜWL¿ï ]ŸYo²4܆7Í;ŒÏùüúú[Þ¿ë'-þaq)W.Еƒ%Ktuu{÷îÍ{|ìØ±Î;³Ùìû÷ï×­[WÐ¥cÐPáØê}²û™$Þ`t$wt‘I,9·+·Ý ORzûöÐü–°f« [mø>û‡&C .þ!.ÅœxõsçD ®Ü’‚u‘M3\—X’µl'm°TÒ|>aƒñ{¹ã Mª·6æöÚBä:®+¸Ê‡´ÝÜdîÜŸ:1™y;³È$¶zû0nûÒ¶ •—Ë |ûö­¼¼ü»wï<<^½zeooŸ””$èBª³nݺmܸQÐUÀ¿*)ooïÄÄDVA×R=q8œ€€è ´xñâׯ_cýs¸\î–­[yoZZZ‚®þiÐPI厶Ÿ¹qcã–-]KõÔÉÀ 33SÐUT+Ÿ?æ}ï?eJë®]]Kõ4ÐÊŠ“Íûì'èBà_‡ •š’ºzš5]ê::& ºŠê G÷¡’@€`€h @€†êIQQQÐ%@õ„ Õ“°0ömøAÆëƒK¼çÌÝ‘äyëÅŠÆ‚.ª,„ âÄÐ@MzÙ™Î&ñhê¯MoºQð>’ta¿(;îüÊEwmØ{7tG…þ·ÒNŽá±Ìè~Kwîßzâ›ýÀ>îS'´«!€ß?¢ú®Ç^Øô÷7ýÈxA ºÒÉD²äMMe~Z‚CÇšÒÜÇ´&–šü< *A€¨4XÒä0š’Vå÷f!µÖã} %ÂÂvfº¶ºçç‡JlÑj?ů¾ÜÝן|ü¦‰”¼>çãíaj.ŽÚ¥, ¼YDÝ·ÑÞÿHWôÏnèá~zn<ƒÜ¬É‡÷¡š«ÂoÒÕ Kžú¬tCHÙÑw©[û½Gµ¼£³šP9×K»¿¸ï¤›œ +‡Ÿ¢¬¿²©”þ÷nã©NAêþ ÙÔ¯E´¦“gH‰?5ƒ6×£u±´*’¬Dèì\Ú¶‹ž¿ãÏ‘«MNSɳIæÜùŒKî;ŠVÙÐJ/ çOÔkO7QSµœiÏÚ¾Ÿ3HÙ‚úú‘ÎbL+ÈV.g[™tk%­ó§oøï½µœÈÝ—ìŠÉ©C8 °•Z/Ù=2¤…ÿažèYã‡ýíõiCçœKQSáÄ}Ôìá»ajKÅ×ÖLòô{%;ºÃm«™ ]˜>bî±Äzý¼¦Oçf"Åýúúqœ’©,ëËà•K, 3Ÿ½iÙHëìó>#½ÇȨJ¤¼±·Ê×½¾ +#b×ôa£–…6Üx¸ëþ+ƒ¢:…Ü]\ç{ÜÏ·»´ï8~ÆüY= þðñÙ_TæŽGÙôh#­[E·Ÿò’lmržJÃú’;otÐ㜥\ÅH¤;IR%ì‡y7øË¤Û«)`-Ýàeo’5¤–ž4b$) —V‰e&¿}šÓÁ%~·¹»='vޤ))‹djS÷¥¤ò×€4ü„ô¨wKòº@—¢©‹NÞÄÌtò9)¥2tΕ¦¥z£i±I¦ÒÕ¥4€âehQâåm–ñ`â1šro!/Š>B“FѤ!tì)dӾ޴ü™O¤ôí>mìBÒ¼DH87¬sèæ(òÜ@u†Ðì¶$üžÌ¢i)ã19Uß÷a¶|ó…{&œ¶Y6xÐV«îzìï³Òú8÷ÜßêÜ£Õvr™á¾µ; «ûú@Gk Woyg·êø&)Þb¶«?„–Ѹ·«‰‹2#\,7¹Ý»?½¶L]×®&ën9/Õ\öù¢¦íW=¹ÜEMˆû寴†M[V}¾§£ŠA—{véþØe딽C‹TÇM¼rC|ìÙ+³¬åÙTY•±ã±è¿©ü¬×›æ® Ý ¤uèa,L!Qšwüè‚,-ÙGÚª$Qò~è¬ÍßK¯yÒèMT³Í_KÊD·‘ÿ8ºó†vù‘d)•ì§÷èÊ`Z~‡F_ KEÒ–ágñýhÕ5j:“z·!n$œHçcý¨Õ÷ !²O’iß>ê81ïÍ/j/EõF’iôŠK úÒB?RÍ9TjiN÷õéÒzúÚ…dóûàÄÒëä¤Á¬:”z¯¡µWèu*I>¥m×H¦ù.!Y‘=5ªI]\‰$óìe½¤eHº­Þ@9çÔ‘µ¹8QPµéó·KÆjΞé'-zöÛ`wn¸Xþôô§;·>UpÚdÁÿaˆj·hUcÒŽ#OS;ÚþðKIH«ƒgó‘ý7Ž6¾÷ÅžMÿ¥?ý²óù„ùuY¯Ž„¨õ? ÊN¿»ií}‰6¾mrF‰°dÌ ©³xºß¹Õí{ªææb]ÇnT©Þ¶&,ñüÑ ÜŒÈ]㦵}AeNÏJÚñdbi©/I¹PÀvRÌyæö$÷–Í¡kƒ©…éÔ#1þ~hÐ?:+¼Äý°í$¢ç´|‰¶¡5¤’ó*0oI¢ÏhÙ*:7:J”\I&™7 휿´h5 Ú üßîæ½(ÎÍyQ4§Æ ¨{}ªú[ hø‡È¶ ¶ªtd=EŽ¢Z¼TñBv™PGS^ˆ#£äQha!%Ò”¤ç‰”šM²£…ZþcQÒQ! §¯Ù”xƒ>YõÈ 9¨•2KÍk&]¥¢&½òR ¿G:Ïå?àTïcr,)‹™AsN4ðÓsEó]ùE2b¾§„3S»u”çýl¹éqR5õdŠ ²luÇ­Åܽ5:uOhƒblßúh†Ôѳš}‡*±ècÔÝhR®¥’?CDµ¶›ñßûŒžªâ… ‘â'·œzlkï­ì)ÃÅ«@zæ+aÇKºDÏx;^’ΠŒüù–=ˆÂèÂ#ja÷c7¥ì‡<ñWé5/4»“rÁ/B6=iÙ] ý:6-­’Ÿ%Ý x^1nß_¢FÔR—¶øåŸ@å ÿirDGÑÑ4Á‚2žQð+ª³†´s²WêSÚ:›Î…Rt<ôgn‘زT8q±Yy |~Ãÿ®¡ZhQqª£CÇžåµ>¾àWÿ7G€JÔŸ¼{ÑñzS§ôX4T‚[³`ºv×}ÍÅ‹,ûíÇ•Ù*­G8JuܲÿAÓ¤k Fïq‘ÙÔl÷¦½ôÎkõõPȉf,ÖÿOܼ‰%Š=q¹Î¦u»/èÛó–wýÊň’v¼Üýêº+Yÿ´ÊûXþ"?üJß“_ò¿kiYK¾&¿™÷¢(©’Ÿ}Žüi["TC‰ªhø—ÔD†‹èô:nAowÑ{æÂÙ™õŠÆ›Óma꾈Z[’¼Q ù6£›åì7'CüÙØ…š¬œÌÁáпIÜtÜÎeGÍÆm  :‹øDÕLÔéü£È®¹xiI—‡¥ØÜ³ƒœƒÿ¤™µ¬W-P5Ú[;`ãÔYuÍÆ—ç¯*¡k®E·_~È Ê!’ÿ,†#\³¦XÉ}jôéV§å½}s­v»6Ù¤”e+5vÎ¥J,W‘‡ù³¤~LÏTÖ~˜»üy˜›û„qmÜŸƒ5—²ÿÕ—T;ÐP­¤¥¥¥§§ó¤¦òÿtþéÓ'iiiÞ)))QÑÊyŽ=ü]Â5©g3š{˜-¤«{H¢Ù¨ó§¿ßO·ÓÉz?Mìš8±”þÓáÐ’Hkò¿Ç'šôž½ýÞR0ä~[è  ‡£(C‚Ôè_ jCÍw¾l‘+;Q“‰§ŽóœéÐ< †lÚ»hvëM}Û)±)#òàò­O‰²¶Í_":rt7#‰ì—ׯ‰¡Øó{ÃÍúk+šÊf…Œë<„³Þw¤­ry¯U]yÈÙ o¿ZCÑ=I'ÿAÄZÚMýf‘^ÎØþ‡NÞŒRöÃKH±ÿ¢xÜòN¥åEÞÐÝü±ÎöõצdIŠDOöÒçNyàSïÑ¥XœDÕ4T+VVVׯ_/vËñ[òÛfc‚?)Çsª´„ôhâ´˜†v&!TCœ"NÒ†5Äv¥á¹;–0©Ê=£ Íd©C–¥î‡¼½t4y¬¢‘î4¸')dÑý@ÚðˆjÏ£êD‰Ìj7§õimMÔ n-(+‚.'5Uúð¥¤QÓU4T7ÞÞÞ…B‹ŠŠN›6M€õ@å#Cnýéø2ÒDFùÃ$›Ñ2_Z¸¼ZóÏ5´@~þ$±n ¡ÕΔr“†ë”Ú§ ¡Ì¡tp6]&Rµ¡Á‡IdÝþ”¿›,WÒ:=Z½’ä7õø—Ìk£CÜê}ø£XTo!m7 µËù,ÏæíÉzd·€<'ä]‡Ž—‹;, ¾tГ‚íhWÛ÷ÃÜÞùÑF}Z»šflãÇ\E3굕<ú’x‰ç –LŒúŸ¦¬á´’¦-'ic깑Lüi|0e!AC•‡ Õƒƒƒµµõµk×r›ýû÷Çágø;ç­¯û€BGÙToM(ºœ;rÿÞÚôÓoýV—èvþcQ½ÿ³wp1ÿÀ¿wÝ5®=U”’–²Š!¡Äß^QFJÚK[ÈJ(²ú¡!;›l2ÊŠì´µç­ÿçîD% Ê÷ª÷ó÷{x|¿ŸûŽ÷}ûÖ½î{Ÿïç°U‰ØÏË™4ìH+Xó×låP#9ìÀ-wÃB¶µ$›8ñЩÛk1¶}ño· m†ýgV§¥Áóð;¬ÿjlÏê?©døåZUq6&‡-‰Ç–Ôn2®¿ íhÐyxxŒ;MÉd'''¼ËÜe@ÍXÆ)7Ü^wô€fµ3K°ƒ6Ø#Ìcˉï‰ùÑ"FÔg}ªþGÛà‡¿:9¹¸€ö 4è€ÆŒù ½ŸAy÷Hë>ZÓ »‘‡9¦¢¤ûãå½ÀÿÝ4 “ÎÇn ±íÑ…Óÿ7ì¢ßŸ—6ºc’Ä¿Ü>èäþúääÒiÚ5Рcòðð011ÞÏ &+=ÀX׉×ÇÌTÐ yƒÆþªÝ\sšÈš’ühß¹ 0ç¿Ý>èÌê%Î69iñh‡3´w AÇ4f̘°°0eeå¦E—¾~÷*Þjí Zg; ÓÂá¤Å©€ö4è°,XÐôB-ü‰;vÌyÈÊÊŠÓ# àèèhnnŽZ\\\Ο?&|||ÆÇi¹zõ*…BÙ°aÃèÑ£Q‹§§gRRg]]]Î2?DÛÑÑÑA-þþþÉÉÉœ}ihhpö•žž.$$„êéÙ³'jAéÿíÛ·œ‡ºvíÊY½+AOAVVµlÙ²µ  ´e111Ζ‹‹‹EEE—/_Ž–D-»víB-h‚F£aì&Ož<™0mÚ4ww÷Þ½{·ýÀ] @¨ãÎ;ÆÆÆ%%%¿>TVVæää„&P DA³²²M£ÀŠþEËoÛ¶ µ„5kÖ –ׯ_ÿhY¹r%jAqvÏž=ß¾}#‰+V¬Ø±c Ê?Z>|ø€ù½{÷PpÏÉÉ!‘H(¢ðzêÔ©ýû÷çææ¢‘Ñò‰‰‰èßÌÌL>>¾üü|´#GŽDEE¡µxyyÑ^ÐvâââÐvÐ2(¾lÞ¼yß¾}h™ììlúÑvBCCÿûï¿ððp´ ŠÝ¨N__ß°°°ˆˆ´´ÖÇQËÑ£G·oߎZÐ2µ¯Ä£}âÄ‰ØØØ™3g¢­®®þo~:n@úúú©©©7nŒŒŒ¤R©µâ\K^¶lŠ(1£ô‰Q8¶´´¤Ñhiii§OŸF-¶¶¶“&MB)eSQ ŠÂ#FŒôR˜´Ï†&½<ÌKMA>Ïk±ÍøË9cfÉ~OÐÏNGÆäV˜ë‰4ù{QwkJ½ñ80ü[ A›! Ó´LÑÿeséÄW¡냄"Ζûýãí1åQX&ç}#üÌ?Tâ§ÜZ‹p^JjGj¡¨ü÷ˆÑ²yJ饿"tÉgÖK‹˜.?s§Š÷)0¬ûp?¾Ë“š~Ò6ø£ÈÔÀkQ£9—âFõ“ü<ÅÚsÛùE!“ÙËг¤Üזּ’c=ÜmÉʵ;®9ß¼ö¢|ô!Õ¾Ê2|F‘éÝ_ú@ƒ6A3Ùæ6'Åé ¯­R€”ÎÝQ#Œ9pøyÑšôÆOã)’Îe€²'Õ+ßzÛªX—¤óuÖÌIÞs¹ÀÂJ’½Få“ý—¾bòŽ‹Õ)Mþ^Ôßœê Õ1 oö‰Ä¾®þŸo…ç3FF‘ÕbAäŠ"ìo®‹÷§Šaä7'É¥ÆUœO)+R)ws°7‚ö)ƒ§Š‰ñ³ }æ¿ûÃþøsô”å&Šìi:OêYô CÕQƒÐ܉I--§b˜ ¤ÀŸægFÎõ ÉÙxv?ÑêêÊïb†=±»OcS+'÷f7ô7úÇçܼâªÒ––WÜÀ—<Ð&ÈŠc¼è·èÐñ=G®'^:³éü™M¨•W~äüÙ>ÞÓ†J?§Œ¨ù‹ÙÛØ¤;'»ÏÃÿ(qϽlË ¬÷†e¯"NäcšÖs5y›û{ñsktp¶ƒVG`Šð‘Ì$O7¯–gVe‘oíüÆC53büÍøo|}Ê&õ¢<)¾±Kñ„! ú¾sá¼ÒRØ·ÒïW¤ "Tý¾Øó;B‡ãiÓ†0¨é|±^˜(»§ÇŸíRŠp|®X…m…šáóY¡½÷1A“Òá ñ@"ðŠóaXÑ×R ²Zî›ôé´±¿Žìò>«ŠÉ <"¢”ŸŸAˆ¬ODè^@Û!ðË _´ý1«s_?¿qåN|TÌ¡ÝÃ.¼¿œ¼Z¢ñÓ¸æ’Aº›dM J Z1rîd\bÖ¸¹òÄ’ñq¹˜Ž«1ûV‚fý^ÔÞœî  t±úLŽÚ#°ÏZ5"/CqHÙÊÐÒ1=˜XÁ_l——6íð7š£È©½¢á˜ J•ipAÏH)ï«'¿i ³ì„WHÃ0!µÊIzû¤¼o~¹%±Yø–lÏ{0X<ê=ÉG×´*Zî^)8¸•€J¿ØÍ—W’3¨½zþQ‚æ!£Ÿ.ŸQèf¾ºEÕ~Œþ½à× ðJkôŸ†þ_6ûËfŒÝã}~ÞÖ¦NcΟC"‰øóT&ˆX½°71oö<Áû{oäû™Éq2Bs~/êl €Ž4h Df‹b‹â¯ øšY»O§(êkQíé¹y§æÖlI®|óן½˜yºTÍŠÌU{é‘Y§jÍ‘•*–ÆT,mxìš‘Íþ—ÉT4/ö1oè‰îCVnÕk›KÊ¡ ¦a†"õ^Î+R"FOº¡è¿e†ìo> !ɨK°/å"=ô‡JýúF‰Ù¢h}ÌÊÉ1ñÏ©£§ÏëS÷r0Qdð$Òž¼ü¬*ÉFOãßÑ6Mæ\ÄÞ»™¦Ò{Nó™i"ÇY~/øhÐVääå9™_¿¶Çv1Ö—Ÿ—P‘opùz€;ðv_¶Étûø„p³ ª‰~ktEj‚2³ôùÉ…Æ;ïfªŽS'c¿ëÒC”:vöäÎŽ‹é––ªßï¬Jݹ)äK?× ÍC†u?ƒÝäAÛ 02/;¬>–Ù¯RãÚ"=ÑZi–š}&â9  #ÕE±±ÓXãwß<%¤¹ÄRfïöÓ §ÄÎSLlt¥¿o¾~/èp @ƒ6RæˆÙ.§»õ/´ÄÄ>Œûv(âwË.D”4¶OÜV`hsÓ^oÜîÑF&z Ò¼•ŸÝŽ>õ¦˜ ¼,n«S?¾FÖ')M ]bHÀ¦S¾y. Ì_üüÌ1÷OˆÓù ÜÐäˆ.$ánböê|è>ECEe£ñªÐá´.Êà¥Q«ŽÙ®¯|cÚìáC4¤EHÔ‚Oo¯8}öuu—éÞ^C)$žFOãßâë;ßDyk¤«#O‰Ä¸??ÃùÛß : РõÕK™hºÁ«¼\Ýþå±€}ÿÆ—\‰ Ðg妷#/o8}áâÖ+¬NøyÕËíÖ8N3Räm¢›&¢ï»÷¾J˜ËæXk³H† +õ5ݸu£ÝPyžßý¯Â¼fGçĆY;á<4hmDQè4£cA;.\ˆÚSÄÿ…_B}Ð(g—9k-5$X»4v7‚OsÂõH××ôïÃ?ÿð—¿t@ Aëû5eþ.wrs{Ŧ¯M-¸Q¤÷÷ƒcÜYBÎôÓôûL7³‡L³ÚkZìpa±Ã¯k$ÇÜdŽ©Û&2íúÃZ‚GÞÌ)ÅÌéK iþž&ÿÛmò¿Æ–ùýiŒ5|&³ñ*»¼zèÒ þvktd hдhZ4-€€ @ @€°uëV …¢Â¦  @$¨ހï @ xÿþ=ÊМi^^^eeeN˜622255m|]@ÇhÀŒ3~èêêê×l=zô°··Ç·0î @¸“ÉúŠ¿H?¿„ÈH¼kJÌ÷ïßçáá¡Óé?•••¯^½ª  Ðøº×âã3?~lã;©Ú?pÀ¥´µµ_¾|ùôÖ-¼ éȤ¥¥ñ.»Ðh´½{÷nܸñË—/h–@ üx¨{÷îW®\QTTldu555ôïÝÄDô[—Úiñññ á]èì @¸TddäĉËÊÊð.¤#344Ä»®@§Ó<èééùñãG&“I$ûõë(&&6xð`´€‚‚ÂÕ«W•””ߎ»»;Z2??ÿ_Ý–¨T*™LÆ»Š†9RBBï*@gÀ¥øùù­¬¬ð®td ãøñã®®®oß¾åäfMMM??¿I“&ý¸öÜ£Gêêj”ž•••›Ü Ú¢E‹Ú¸ê6WQQ1eÊ”óçÏ×¾¨ 4€Îeå„„„ 6¼zõ M£˜¨ªªêããcnnþkd\»v­±±±ŠŠ .¥â"<<<111&&¼k€KA€ÐYœ;wnýúõÏŸ?g0(++++{xxXYY52ÆóŠ+þe…¸«¨¨ð÷÷GÞÞÞÓ¦Mƒ‹Ð44hkåwW 0;^ôûºY_¼é¢Å÷ï*bdGOê¿ö帣Ï"FÀ(t×®]³··ONNF¹Í***:;;/\¸‡‡ïÒ¸NXXXVVšxöìYll,ÊÐxW7‚ Ú¯²¹«g¯{†Qxo×Öóyrfë÷ä\×à‘(ǽç!5}»ÑècæW.Û¨péý4àŸ)**ÊÍÍíÙ³'Þ…€æº}û6ÊÍ÷îÝãŒ}&//¿~ýúåË—sííq¸«¨¨ ü1ëååeff¡ø÷ÐQºÌZlð}†žÁ‡´ÂØùKMÅ÷7™AcIÜñ¹Œ‚'—ßRÿxmîy"àOÐh´ÔÔÔ»l÷ïßõêU\\hî÷èÑ#;;»[·n¡Ÿ šíÒ¥ šµµµåãû‡ŸtµO»víâ\~æxöì:çQ†Æ±$¸hÀÙÿMê¿&sþ‘ñ«7ßÊþµbâò ô ]ÃG«U¤_ÚåµoõÔ,¡¤Ý¤d>“ó§¬Ùr>¤+óãyŸ…Î! ×õº9Aä]¸Õ Ÿ´^³œvŒë%ÉSøöæ¡M;—›~¥$m#Q÷’p#{;nû)l£ÕÚ³Âóöì¶T–RÄXפ¯nou$G}ú†~:]°ìGÇ}ݧÝùwÁCW¨Á'ÚGGÇzßy¶zõêFn5xIKKC)ùÂ… ÕÕÕhV\\|Ù²eNNNð]-õàÁƒáÇ£‰ôôôäääÑ£G£7!höõë×xWw ¸ûÚFùóª··-TªéœXù%©1Ø|A˜ÇdYÖ>Ãú0ïë:_ˆzPÃD»w¨Õ!,Ñ]‚€½ÎÎ,¥7üD@ûRVV¦¡¡ñåË—^½z uKw’J#!š_‚ï݅Çd¾±1×Sç©ÈJ9½ã@6Îr£®è”g4{/¦ÚÂr"ö6nß•a]{ÝKDi–×ÂæûÖYÚ}[;E[‚–q?:(ø±ƒÛ8"VÐö´¾ŠŠ --­÷ïß«««?}ú¾nãCÇÓ¦M¡¡¡¹¹¹h–D"…„„ôéÓïÒ a ·ÒußïVæºÛfF&¨4lÆêýS(§¾ÜXwnãœUŇ—ý~U’‚UÄir°ÿž^Ö»X#\ñJ©™ê`Ù£þ]„ï¥älÂ|ÇÙñ6‡¢6,‹ÓßxaD/²ÈÓqŠý#¶,?VÂÄÈR×lÙogÞKàww®–žžŽÒsUUÕüùó#""ð.§AÇ|Û¶mÁÁÁÙÙÙ;7>%éâ]44ø§xºÎMüúË`ºqÓø¯¦õ[‰¢–í¾\/'[î{dY3ýuzÝÇDMb¿~ý>-ÒÛÂë…WƒE»XœùjÑܽô¼2¡îwsñHê.‰[òë–~"€{=zÔÊÊŠ3aaaÑäòàïQ©ÔÝ»wûûûgdd Y===£‡ŠwiÐ\ Ôüùó÷ïßÏÏÏŸšš w§µ5:éííýéÓ'Œ5è$QGG'00ÐÐÐïÒ@Ãá¶~ 4 Ó©ªªêÛ·oZZš²²ò‹/P†Æ»¢‹Á`DGG»¹¹½{÷ŽÉd¢ÜܧOÿ &à]h Ý@# @:—?öêÕ«¼¼ÜÒÒòÈ‘#x—Ó1¡¬ãââ‚Þ¥ i ®®îããcffF š^¸h@'knnŽ&"""æÏŸw9ÐéÓ§7lØðüùsNnîÑ£‡··÷Ì™3‰Dø~!@ÇÐY,_¾<,,Œ——÷éÓ§x—Ó¡\ºtÉÁÁXƒr³¢¢¢››Ûܹsyxàë…h@ÇW]]=`À€ÔÔT…—/_ÂÝQ­%))iݺu>¤Óéh¶k×®ÎÎ΋/&‘àÅБÁß8@—‘‘¡¡¡QZZ:uêÔ˜˜èƒû÷îß¿róíÛ·9¹YVVÍÚØØðòòâ]ü  Ù™3gLMM™Læ®]»–-käëw@Óž>}ºvíÚ›7oR©T4+--½zõjÔØ:Ѐ e»Í›7“Éä‡öíÛïrÚ«/^¬[·îÒ¥KœÜ,!!ammíèè=ahð‡JKKoÞ¼‰wSAAÞ%´{4mðàÁ?–““KKK»¢ö'==ÝÁÁáÌ™3UUUhVTTtÉ’%ÎÎÎhïÒg A‹ „>ÀW·)È|,''GUUµ¸¸x„ §OŸ†NÏ-òéÓ§õë×ÇÇÇWVV¢YaaáÅ‹{zzJHHà]p РÅP(111INN†\Òv´´´¦M›†wíÒ•+WÆŒÃd27mÚ´víZ¼Ëi7233œœŽ;V^^ޱ¿ÆyÑ¢E>>>]ºtÁ»4à: A‹ñññ:u ï*~ëæÍ›‹/~ðà°°0Þµ€ E@???‰t÷îÝà]N;››ëêêzøðáÒÒRŒýùÒœ9sÐ1ìÚµ+Þ¥÷‚ :šË—/¿~ýº°°t§B§Ó îܹ#--ýæÍè§Û¸‚‚//¯ˆˆˆââb4ËÏÏ?cÆŒ€€%%%¼K€v4 ÝËÏÏWUUE¡pôèÑ/^„oþ’’’7îÞ½½ÃD³¼¼¼¦¦¦ÁÁÁèèá]´' íÛÍ›7GE§ÓQ4trr»nT^^¸mÛ¶oß¾¡Y2™lll¼iÓ&---¼K€v 4 óöövssãááA1zذax—Ã]ªªª6³åää Y‰4zôè   ¼K€ö 4 ]b0£Fºqㆄ„Ä›7o`µ¨TêÎ;233Ñ,zw1tèP”›õõõñ. :Ѐö§°°PUU5//EÃëׯ£ŒˆwEø£Óé{÷îõññùòå ÆÎ̓ >|8Þ¥@GÐÎ~üÈd2‰D¢¶¶v@@€±±1Þ¥@‡О988 ˜xåÊ•Q£Fá]n FLLŒ³³óÛ·oQn&ššš~~~“&M‚o8€¶Ð> ˜8nܸÄÄDQQÑ7oÞHKKã]ÐAHHHذaëW¯8¹¹gÏž>>>æææ0xü3 í@II‰ššZVVÖÀïܹC"uº¿].\X¿~}JJ ƒÁ@¹YYYÙÃÃÃÊÊ r3ü{îEÐî00ïrþ©k×®¡gœœÌÉÍݺusvv^´hÜ7 8‚ :^^^¼K­iÇŽ666(;ž;wnܸqx—óܹsåæ»wïÒét4+//ïàà`mmM&“ñ. hÐሉ‰á]hL&sÊ”) BBBoÞ¼‘••Å»¢6÷èÑ#;;»[·nÑh44+##³víÚÕ«Wóññá]€Ÿ @¸Qii©¦¦æ—/_úöíûðáÃŽ}åõùóç(7_¹r…J¥¢YIII[[ÛuëÖQ(¼Kçs@# @¸ ”:::ÕÕÕ+V¬Ø¾};Þå´•´´4{{ûóçÏ£gб?}ªªª.\¸wï^¼Ëiß¾}óððØ¿II šåçç·´´ PPPÀ»4 4gGŽ™={6šˆŽŽž9s&Þåü•¢¢"Ÿ={ö  4ËÇÇ7uêÔ   ¼KÐj @ð4wîܨ¨(~~þ/^(++ã]Î*++ó÷÷ß¹s'gø^^^”›555ñ. @ëƒ :ˆ„„„yóæ¡ ÎÈ}úô!²Ý»w.þq§ÊÊʾ}û¾yóý€RSSQ†Æ»¢CO!88844477Í’H$CCÃM›6ikkã]€6t'N”——þü9g–óº……¤gîôáÇ^½zUTTÌš5ëðáÃx—Ó2ÕÕÕÛ¶mCÑ9++ cçæáÇ£Ü}ª®®Žw9M£ÓéðôôüôéÆ~c¦££hhhˆwiðt(Ù¸ººr.Bs¦ñ®ÔQ]]¢çóçÏ_¾|I¡Pð®¨1 #::ÚÍÍíÝ»wL&Qè홟Ÿßĉñ. Î @ƒÅÜܼOŸ>)))h.?s•ÏŸ?kjj–••™™™8q‚@ à]QÃPVŽ‹‹srrJKKCÓ¨NuuuT6×Ö àƒ :Î…g ¸üÌUΜ9cjjŠòhXXØÒ¥Kñ.§a§OŸF¹ùùóç eå=zxyy¡s Tx—€»@€ÍB¥Rß½{‡w!MC)MCCÃÃÃïBÚ!!!ôf£MÇ*YµjUhh(™LNNNîÝ»wÛíèÏ\¾|ÙÁÁáÉ“'œÜ¬¨¨èââ2þ|¼KÀ¥ @ƒfñòò »ŠxñâÞ%´·oßNKKk‹-£÷]ººº?–——ýú5 ëm±—?“””´nݺ‡Òét4ÛµkW''§Å‹£ wi¸hÐ,œñn§N5nœ>Þµ€V“‘‘ëí½‡ó% ­.;;[UUµ¤¤ÄÄÄäÔ©S\Ò%f;;»[·nqr³¬¬,ŠÑ+W®äããû4íhЃk-Yb†w Õ¤¤¼õöÞÛ[¾té’±±1“Éܲe˪U«Úb-òìÙ3”›¯_¿N¥RѬ””ª Eçöøõ‡pÐÊ6lØàïïO"‘îÞ½;`À+yùò¥½½ýÅ‹«««Ñ¬¸¸øòåËœœq¬ @{ÐjètúСCïÝ»'##óæÍ\ÊHOO_¿~ýéÓ§«ªªÐ¬¨¨¨««+šÀ¥P“ÉLJJâütâÜ/Ž~FÒÒÒx×¾ëß¿¿¤¤$ÞU€Ÿ @ZG^^žªªjaa¡‘‘Ñ… þýèoŸ?F¹9..®²²Í /Z´ÈÓÓ^u¸ ú¡x{{£w! 1VVVx—¾C¿,=zôHOOÇ»ðh@+¸yóæ¨Q£ètºŸŸŸ££ã¿ÜuVVÖ† Ž;V^^ŽfQnöññéҥ˿,4ßÕ«W †ŒŒ z“ƒw- ÅÅŹ¹¹Ý»w‡Á¹ŠÎíbÙN4àoyzzzxx ×Ú;wîèééý›æå幺º:t¨´´Í XYYtíÚõßþÒæÍ›gÍš…w´\2Ѝ 4àÏÑéôQ£FݼySRR2--MBB¢­÷XXXˆòzDDDqq1šåççŸ>}z`` ’’R[ïà€ Z -=Χ]íZDŠL7mýA“ÿ7cÙÑòuÈŒ¯óº.|7511v ¥©eó"õÇ-Hy!;xlGü¤º  @UU5??ذa×®]kÓO{KJJ|}}ÃÂÂP€F³¼¼¼“&M VSSk» ‚ ZYÅ«íúña³º¤ðÓ‹gçccOÆø_r*nÑp‰f…hêëýÚ}NÏI9ºA¾Ž{Ý»wåfæîîÞv_œ^^^ŽRrhh(Šéh–L&oÚ´IKK«ö4 4h]¥‰S¬M~có¦Ý_ãVOßnkE' }'Þ¡gs·ŽþÍ ÝTÁÈj<÷ÓŒËqêGWíÚ7† iN]°o¯¹db˜µsÜåw˜`·qvNÜ˰zв®þçäq<6éKãW403÷ð9B¦¦7½ðFHàêàËs蘘ÒD›U®òuö÷í^œ«kô±«ïÑžx$”ŒæÌ ðš -ÒÀµvZÖý‡Ýa )ïÑž0>YmÝ9޶î3•ÛÕÍ!L&ÓØØøâÅ‹¢¢¢oß¾•’’jÅS©Ô]»vùûûgff¢YЇŠÂú!CZq/À_‚ ÚŒ¹Ûxû‹'ÏD¼,5,Œ12»èY^§±:>DM¸2íÌÁu¡”´c#&ŠŒp´]+h}¡ª„ªP# –ù5¦Hüè¤Î¾n»({vÀÎt-,%ÜcÊÆÐIÙ7ºðöó<þß~ÑÜ„ukVx9Ú8u`´@þyÿAãã3µLü;êÊc_ïœvÛ2òZÖä5"(ØÒÞî\kèðLd쨵ºŠŒÌ+»·XžÈ«Ù³øÖ¶!þѿ1Ênˆ<ãÓÍWwóŸ\›«Uï+¢«?„Œ·^ÿ\m‘¿Ï”¾b</…‡Y.ø"{h¢X{é^\\¬¦¦–=xðà[·n‘H­ó„N§ïÝ»×ÇÇçË—/h–H$80((¨-®m4ø(êºêØÉ¤Wé¹´Á¤ÊÏ™}†MX½Ö¢++:j0“LWžŒ¹U8zªªª¦ Ãø»õV×aõ.ÞÈ¿ò^ªéë¾~<++®[d¼öÌíj¯÷ËÍ•x0L~Û䀸ƒ×/dT#‡­ÿ§•è>Gžb‡Äÿ¡çêè Çg__(ÃS‘¶#ðMlì¡èeãÅYŠGŒRcö±ôÆØé˜úeŸíÁ×"£N\óœÖ…½úÝa’Ù*ÖáŽç§$L­}e™ñ-5ö &fe·yív—ÁãLh_®à§1Pdl³#ߊžXÊ­Øôò):‚µ " ÉkJb÷Nîˆ]g;QUš—€QzÌwëÑ‚ƒˆ«mÛ¶ÙÚÚ¢Œ›˜˜8f̘¿ÙÊÍ111ÎÎÎoß¾E¹™@ hhhøúúNž<Æ:õÐ žÛº%26ñö‹Ì2:ú”Vd4m±Ý¬ÿâ+>_#ôº.|>51+vLËGÒ©|¹ÔÜ.êY&bváKL‡‹ç·þîе`?™‘úò R¦\ÈŽë\à 4ø¥ß¾Ñ0LX\ˆýšÇ,}Ì?|ÛñGOÞ~+cüXªáïõmÑÂßñˆñÿˆbD"š¤ˆPj^n ì&ƒIÍýœ‹aâ=¤)µbYª«{ž™SLÇø¾e~Ã01Eq¾Ÿ“${ˆb+@Ss?å`XõY_ xŸWÁÄjhL¨_pœuÖÌ>Ófù`‚=‡êO4š¿hô)nÿDEÛS§N §¥¥ÉÊÊþñvÐF6lØðòåKNnîÙ³§··÷ôéÓÿý—~ƒö€Yü0xÚ‡K…˜x?ÓYv:Êâ<%“/?êû¿£»m¾»ª/wß@Pz×smÔ3Ú(çÈ•C4µý3ÑÎP_ûk÷90'åéu^¼kéàx¶wÜþâ :fÑ㤗&;XU¥XjÆnS«åWIClV ï­"ÅO"TÞ¶]´ørC«¶há–b¿ø2ëEqæ÷öÍŽõdoÚ÷Oà!¡g#`dŸà¡QﵑGT‘Õ£ÎÊDIƒç>Ï|{ûÖ™³wΟKÚ±áÊ-·k;=ô¸7”––jhhdddhkk?xð€Lþ“/\¸àèèøìÙ3ƒr³’’’‡‡‡••|K0h=;~±1JÏÝç¹´Ó¢§@Í/ÉÆ7Œ˜½ÆÔE7u³¾P£Á½8³ÆY¯gÖÂ/Beˆ$.{[ÉÈpÆGj=p<Û=РíU}Šp¿RŠÉ,š¯JA‘øý¥Ð«Õ|ã¼ã·Js^ù*ª\µE ·¯´R û”žSÆÄ~dXjîÇ £(ȉð`$±.â–ù¹í¯ær25ëE>gŠ,Ó]Ž€¥— jékË5óµŽG°§ÁØUè?FÁÝ£¡aÞë®,½1IžË^*9RSS P]]mccÚÒÕoܸagg—œœÌÉÍ]»vuvv^´hQkÝz:´ªçÛì}Ã4ÜvYö¬}G.A@uNxBa÷³Bcåx˜ßß×Eͺ²u½ëöØÛK1’TŸñ ]‚ݦ«±?h¢f]Ýæä±36)=ꎪ™µ‡ïÊ25ç$=ïFÈÊÕÁ1sh˜˜ÆD› º£îпÝÛãêzìêKö¨;Fs¼æ40êýK¸®ÂÒG¬É›Ó% ÉøtÞyÊo÷ÎÈܯ/??cåù}ó¼¯f;žwÃü×ÔÍ,{ùŸ×†ÀÃg°‡$Òmåìó¿ïCÑó“¶®ZtüaV5ŸÜ éëB<ÕzM:?’µkÑ&êgdE •›ûÉæÚ¹¡§ìÝö$¦c¬`»u¯ÛØ.<œnßÇGâsâp6÷Ìx‘zO¹ñC÷û½S>l¬²:ÕðÈçDË.?Ždå#;µ!eÿ»üþÀhÁæv¬Ñ1íöʶYKΟèÿŸ½ÏÑ»•W=K§m›— dÓ×÷ƒ°òr\¿£«\÷ßͤa¢šSöíµ–LtµvÞsù]&¨2Î.쀛{ ¦FχF©#ªÉã ¸¼’¶Å¬øòß ›u0ñéëÖë°^UeU(ʈ×tœ`–&ó»Íz„Jç\û%°¯À`ÍZø/eúÍÐÆîß‹;›1f^7ÎßbjzüùWÙhºšZ@VkHìكĻ#872K^:‰4ë‰%ûÍ€%Ý9v,Ýx•ê÷K³©ÇW‡ä8,š­Q«ßZïaôºÀwz~ó¿…M×ÖÒ%,¹¸¤‚+ï" _¶lš8yò¤©©ióW¼wïÊÍwïÞ¥ÓéhVNNnýúõÖÖÖvõtRÕéñÇÒ1LoÝâúãÙ á¶þ³&ãÛ%Ý1»s.öÜ?V…üõÚ.×€™S+ŸÇÿ¯kÑyëAã÷fjýÏïðNö¨;Ü6¬yíãäö¨;Ô·;':ÜëµvŒ"ãÕÝë,OdÖl™Y|ËqȰà7V£¶°G݉ðp7ðnÖ“këëWÉÓÅüÈcõÓ‹FÚ=êpe¯‘„xOÁüóË»wa²/†&ùØÐä—‡E©) ¬ÿÄP\ËÄ6èøx5áò´3AëBçd§3“!Rßî0ew›2bÝžpcÅŠ'‡¼ÌLDyª0²™Øtý|döøE +͹F>ܧÈ|¿ÞÌÆËl±öûx3iéɇîD8Ž_+b}âØBUÕúWþ?tï}Ùr½5Kn콜3s–ì÷¿…ÏDÆäV,×.¾åÐÜÃŽ1òù ‘)è gÿ·Ðò…EÀÊU½8îü?Ç£rÄ^œÕ•#pÂIÛEŸg\L×"¤„ϲqý¤ìS]x‡yOÙ/ú5aé ¯™ö#P¬nâ| 4rHO4u$ Ë5ž'ÿrÿÇÇÌì%ÒMÃ> =©f(«©;¸Ñ…ûj+Qþ<|’n±Ø9*z‘±Wž›ñ@iú§¤7÷t¢Îò )’¬ÍR4VÚª…9'ZL‘ò[1PžúåbØáË Ø×2Vß’ÜÜ黆X=Â.ÛsêHe¾‚çIÛÝ%GúÔ ‹)¾´„¸ðÛÙ/M†«Š’Ês“OØ™IÔµ¦À}¿…{÷î]ºt©  àË—/š³JrròºuënÞ¼I£ÑЬŒŒÌ6>>¾&× ¾Š÷w>`X÷á:’-ìçC}»wÍîO¢ÓÎïž$ÉzÓ;u¼Z¾êø¡ÑéfO¬Ýû…oLTbÄyöf‡ÄÿªçêÐ Çí®/ìÆSñtGàmš˜Å¡h/öæá#FõcöÑöÆ(ì-§ï³ ~-2õĵ¨šQwŒ†I~V±öt<¿(aŠdÝKád µ~ýz²zr‰¨ô #ŽU¿ÜØÈÞ°»¨”=©\ùöíï¾oª‚=~Ñœ•Ñ!]Y[02Ôa&)¯<v«ÐlªÀ“ÐVñѱã%ÐÆŒŒFô˜®>íFù~knãõOf/CÏ’s¿l%ÇÚz·%~kwÄ;ߌ{Qn6BHBµ¯¦ ú]¦tëÝ_ç×>»º¦ö?qÅÒœ{.çXXqtù“ýÇ¿bJŽ‹û‘Ów5ÿ°W¿kä ÏçaÝþ‚•*{ ˜¥ÀzXÉ>2üf÷ig6JŸ¾^íûa§iúF¬gDÅunÆÁ¦gnWy½÷6WB©•¸-ˆ¾~á]¥P³Î‡†iµÙˆF'h¸ï¥´sïölYñcF@BKßÄkùÿlÍTDä\á~Á'W—,™³|FQ=Ñ5a½…àÅ—|âWÍ-Žº½ÂzÉQ·ð0‹#:Û“·7¾ðýê¿^ j6‚ØÈµwoȯwþÏÛòt1#˨ŒYãqÒc‚ö÷?û¼Z¡ç©~v¡Ñ+fÁD»›¬r99ð˜¶éí *ëÃcaýI÷»¹º ·¶÷cÝ%)¯oj}fãì ¿ôÉ )M‰¿Crw>rÐÞ9˜5d¯ŒÖëp/¯…Š\ø·sâÄ‰æææ‡j2þ>þåæË—/S©¬Þ|’’’+W®tpp P(¯@#˜ÔÒRtB J ¶0?3r’bR1Ò˜YCk A|Üá<æaV¯Š=ìQwŽ“û±Q²âK­ÕÉ⟖.ì&”s=êÎŒZ£î¨MÚÝ;(cºs=êÎìa¢ÕµFݱèƒÝ½›Z>eD—Œk¡ç$5¶÷zì–Þ–&ÝÿIžçɺCIª*Q°ÇùY¥tZñ½›_1²±…žÄ÷â‰]ŒW›IÅF–×Ðhý“{³úÍÝ¥¦>^iUi KË*¦7úÌ8oìÐ5¹÷JSãcø¯%m9‡Õ®,9âD¦¹q®&9ç`ó{y¾»¥¯¥‘|ÍÃD‰&Ø™”K/J׫‰oÔš<¨æiðJ±q2*û=,‘Ä»³q*©ÎyÔ¼Â~wH¹ïcGÐB Ak!©¬ý¹¶9KÅõg{6»nãäØÏ“k¦Õw§ŒÞýã•Æ®‹ :ãæÃµ[(½wd>ÜQ«GÑ<™iþ£iƒY7fEü®X’”±û&c÷ÚMú•?;ÅMÝ~aêö×%JÍ¿÷p~ÍŒhÓ-gM·ünGÜDVVöøñã,ðæÍ{{ûsçÎUW³ú£‹‰‰-Y²ÄÙÙYDúñV@àCïÝŠ¾¡÷¥-éûCÍ{“Ã9Gú×w~ÔÜ·ìQwä뎺Ӄ=êÎö¨;¾Õ_—,ÙCÃrÙ«¿aº3­¡Qw²*jÝGÑpaMíý´¥U${Qf–¾<æï±íøµ'osê IDûö/Þ]¦VñüÝ)b‘¯j h¼~v€æ‘¬õ‘^ÍxE=¯ïoôÐ5½wÂpÄÎÜ“˜e5WžXò`o\.¦ãjÑ“—šÚÔa¯]F™ É]º‹ÿ|[Æ#"/FÀhù™%tLœóÔy„~ÄI¼Ö N<5ƒ85¯°?=¤€ûA€4×ÇQn>uêT%û’ ŠËË—/wwwor]Z@@eXìôË+72¨ƒz¶$AØ …Îhè!Ö?¿ŒºÃj‡ÐŒQwÈìQwB'þyd¯Ÿ™ßâ§TK㇮é½#"ºË¦ÉŽ@ ÚT~Ï©¾16&¬~„–öfdjæû:VsÄiìkçd™®¬†–\þËót  ËÉÉqqq9räHYYš¥P(óæÍóõõ•““û4Ðáñª/Û4ûøÈp³™ª‰ÿ­Ñýñ©;³ôù¾…ÆÎw3ûŽS“þåÚ4QjÈÔÞØÝ»á§>ÏX¨È~+½m¯c°‹×>9e1{Ô=g3fÌëÆÙ\uzü‘W¯Ñtmö¨;ƒÙ£îüw·`rͨ;D)Õ¡Ÿ(9”=êÎŽcé–«T¿ß¹P‘ºsuÈ×ÙõÇ‚ÿ¥0ƒÆ÷Þ$FU {H"™ZCí`ID§Ò™dÙ%±ÔñÏJǰ7ÇȽz<«¹¯‰úÕ›QA­ñ‘ê!5zèšÞ;ëè XbÙuïö §¤Î ™ØŒ‘&6oÅædN@~zð̧¥ÖÊ쓃žy-ö†õ§ÕÒa0þò|`ùýñíh@})))C‡-))AÓüüüŠŠŠx×:¢¤ñ¶Äm¹†6§íõävžn¢§*Í[þéѹèSÏŠ šËâÎ:õk ¤{.Þ²|§Ñ®E£,>:LÕ$g܈ð Ëš°eaå-¶;G….2^˜çf9Pšö))ÂÍ=•¨ã4…=öEg¥­v˜s´Å9¿#å©éÃ6_VÁ¾–°GÝQšº~×€Õ#¦d{.©Ì_ðüÌv÷IÄi†>͸˜Üsac{oú0ŸÒ˜‘Ò#¢×¹ñŸ¦Lÿt(8,ÃxžÒËý¯O»b0³ÿŠ¥jû}ÃgÌ’ZnÐ¥ôÉßÍoûÉcW ¿¯ßDýåMíŸ$ÖM Ã^ ݧf¨¨i4¾¯X­ë®º¦÷Î"Ðwþÿ”·ú¹:’J$f­0øþ±C‹{™3¬žÌ7ßIÿ+vúŸ®LåËXï5ItÑinJ-Cy>4~ízíòHòf»SÌvc¿ÑÄ¡ktïßñjº¼bºüÚÞèŠD¹ù÷˜óÎ7õ#f0)½æ„^œÓÀw«þz(º;2™uqZžÌ\ެš:¤MOÀõ @í³äA°ç5ëý[&°{³ú÷ƽĈúÃ{üÅXù€ß€ ¸='\wÂÒ×F—¾ú ·‹]3hw}^¬L ñÐÚ<‘·îçuÌœ³éÓÜŠLU»ˆüøþ˜ê´ óYY_1Ò„½½×]ƒVuØ*us:&:¾gŒ§hòèÕÿÍI *‘=ÓUíg¿:fqZAôá¼KwËÞå³>–$PÈ=z‹Œ5—µÉÿßÌh¯yùüëg\1©ö];Iƒòíñ1?×›´®KÝ'ÉÂßZhÀˆ},-–å¨+°#"õõ~í>§ç¤Ý Nþu¶MwÝüÕHƒW)™&½Iü|Y·ÇX韽1y…E’«~¦g„S¢ó¾bç‚ìÐà…àAg…õíÖé×Ìç?¸õêçX¦õf[¡ö®[‚(*²ÊC2É6ß/¸p _M fÐn„|¼VFšä×MOôçÂŒ‚âƒh|JöüŸ/f_É3—ûåEMNlPYa¼×W“ÿúÿö[™¹—>°Ò³¢tÐ…Qµ6²Ú®lÿÚ´ç>8õ ŸÉ÷׿ٕ••üüðÉ/ø[ùùùIII×®]»qãÆÓ§Oétº¹¹9‘×DÛAHkvȹÙ!x×Á­ê÷–à¯@€­¡üÙå{º:¼¿?ƒ3PÕÓšý"ßKO½õÁyûwfþÕñòöIC¿W‹×7?cÆù}¢óö^Íîw<{ã·ñì~_Ö};nÁ}ÖòNúN$õé=_U3Ë;ôlîÖñ¬›³ßîŹºF»ú>†ñH(Í™à5A[„ˆòvÔP㹟f^;§}Ê>lOâ§b Ó0°Ýêê6V¢[£kwálẢzÝÜMŠVù’$ì5œ„"AÑ/éâ&=Wé‘j%&ŠÌ·ªxŒ¦‹ð—Š]Ì;z¦jò"þú×Óù—Ø^oÈñÚ'qÄV°áQD«*‡–a6!uÒ3ƺÙJp^€ )®JI§Ñ ØÍbggúñãGyyù¿Ýè¬Þ¿?eÊ””””zßR·bÅ w÷–ÝÜ4h ó”=ñ×NW’BñžqíÖ{Œ‚å>L|_=D‹Õ=¢,åêýjòKMQA5>ñ±¡Ë/ߥ&?H»ÀÙQ|ò¡ÈGÛ±‚Ö'üªŠHòwú1+¡ÊìžY|kÛaßhŒße7Džñéf‚‡«ÇÀ»ùO®ÍÕâ#ñ£“:ûÆJó×F®÷É2ß][oèeæ£ý>ØLºÑë^„–®K$é¯QšpûíYŸ/&'”ô°âm^ùâ’[×ÔíÇ\]yêpSBjF? ô„.yGbr_ÍVèSïú.)j °~x‘sÔ‡¨qšKÕ¨µúcÁ•,Œ¨#;Q±L”ùߢÆPÍ´dÉ’>}úäååZþ%· ÊÊÊfffÏž=«ÝØ·oß‘#GâT´РUPtföå=þ"!µÂ|$cÝ{'4öÆÉûÏ$¸juáÁªÓN?*À4-õE X +‘•¥U®Œ?dÛu–žS³ UUMÔÆß­·º«ÓseÝY £~Ùg{ðµÈ¨×<§qï¡;L2[Å:Üñü”„ÉìÍÐóåÜ#ƒ­¤X—»Mõ[{,ÞùqÜ‹J³”¦ŸJ‹Ö%Љ®q•¸µ6ßk›˜#Ïçø|Ò„-ÝôÅê,Sñ27æ&;[Z‹µ)Sù„åM–ï=ä—KÅ‹‹ûѲråJë€Ö´ ‚¸ÞØØÝ›ç¿TŽTã/yuâ£÷£éŒƒóâ^.é"I˺r>Óž=âçxõ*–&rpW ='9:ã5?L”VYù½QÊpl,åVlzùdη~¨Íý£ÓYZUÃ>e7ó¾¥®+>LÁ}\ñÚ¸t; ›ÐsÍRpˤ=8”Ÿñ/Ÿ,ÀÎÄîã¥5Â>_=TôMOBò—Ì#'¹aU¾yÐWïñpËÿ³w'ðP½kÀŸ™±ï»¨l Q‘RI)­%*ZH%I¤Ý’V¥”RBBRQB›v•Û^ÚSÿ¤¿‘DÈ2ËKö-ŒüŸïçÞîÌ{ÞóžçîÌoŽwÎá¬s€Ê‹(ôB¸‰5'”””åü¬ñGrQ´;ן&ìM›6g²±±3fÌކþ›nß¾WýTDDdΜ9,¬!„Ú hÔ>H’š3‚ó¥§Y›•$^'Þ)”q!3ì—|©ÇÍWEº?ŸÇ¾%Ïa½Øªî¹Eî#Ú’Û5ÕUž“ž PviUú³„Ó¾ý¢14‰W´ÆÕ܈D}«Ô–Üë«-ëÙtWôÖ¹‘v‡ ¸ÚIP¸vr¥æä½I‘áü”¼ Þ&OŸ>?~ü?xyý^#B Ú³gÏŠ+H$Òõë×íììÞ¿¿`Ánî†g÷#„Ðß4j'lc§ö‚í‰~˜¨$<ü*¥m(ËÕCW[öû•„Ô²Ÿ®?¡I.™Ôë÷¤"©mßu#ØèÑ‘ÛÐ5ÞS¥Î[1IPF°ú®µmݶ¬Kä唢çK"§tÝ”IK?Ÿ•Løðeîø/µ+1µçnàòy\3Ý¥ÎÍÊܽý»®O­‹SsÊð÷‚‚оQxªîK”ž,»C•Â>¾sçÎM˜0aÈ!oÞ¼i¯aÑÁœ9sŽ;ÆÏÏŸ––&**zæÌ™‘#Gâü „P·µŽ>“u¤6]ŒMþ¤sîŸÎ*z¼åRÔ!~ñÖ§qÏžÿן¦Òº‹-7ˆ]BVЩżjÚêRõç)ÐºÔ ¨KŠOE—·€Ó&ÉZšËKNy~º›ýzž¬FCS«9•zxXŸþigbßa5ÎÓ³K MRøìŸ’ùBÄE£òŸ‚Àh…ŠåTÁëŸÚ/@Ó?ÞÔÔ4&&fóæÍ64p]„ê “Éƒ~þüyß¾}_¾|ÉÁÁøÍïß¿ÿãÇeeeY]Bµ ШÝp÷=Z(êΩKiωšÎÊŒ fð*NÂuòꩬüc¶pã²T2µá§DQ™ƒ!énTTªÑò¾•s„½ŒvÚ­·ÊvŽrûîÓŸùù$û\6ˆÍ6Uç²t|"&_oýù¨§úH¶†ÎxUmäÌ/½Úþ…³æd v.³•bÑößN8§õò—7ïGªùâWzþõJ{ïÄ©S§„……ÝÝÝ-,,Û{xÔ­äçç+((|ÿþ}êÔ©±±±5ÉË˳ª*„jw QûáS2×å÷&tppXµjUÇW‡Bí4B¨EîÞ½+((hnnþíÛ7aaaV—ƒºzž4iÒÅ‹ÅÅÅSSSùù[4QËÈÈèÿûߦޮ¡n€@ ¨¨¨°º T h„P‹pqq%&&êè訫«§§§·a„‚‚‚ˆˆˆŸ?¶{m¨mØØØèŸˆz÷îݶÕKKKUUU?|ø ©©ùàÁƒ–Ï_¿~½AqqqÛ¶‹ÐÐ!CX]ª4B¨¥´µµ.\äää´gÏžÖ®>uêÔ›7oÒZ0'u__ߌŒŒ6¬˜““£  PXX8þüÖ®Nÿ$Ö†"„P!Ô ñññ{÷î;wî AƒZµî»wïèéyÄĉ"’OG…\Vvñر¬¬¬6¬ûðáCú§) …ràÀ%K–´{m!ÔÅa€FµÎË—/%%%uttòóó+n“ÑBþßùk×jèêv\y¨… óó"#Û°bHHˆ­­-‘H¼}û¶.þ(BÿI B­#&&jmm­¯¯÷î]V—ƒ:ÕÒ¥K8ÀÃÃóþý{))©æW@¡î4B¨Õ¬¬¬‚ƒƒoݺEÿ×ÖÖ–Õå Î@¥Ruuué™dddÞ¾}ËÅÅÅêŠBˆe0@#„ÚâêÕ«‚‚‚vvv“'OîÑ£«ËA«¨¨¨oß¾™™™£G¦ÿè³qBè? 4B¨-ØÙÙ‚•<|³ƒƒ`ïfxAß.` ®¾ úG=¨ïÕ«Wd2yÛ¶mkÖ¬ißÁB¨ÛÀj$éÙ³g êêêyyyô§¿I9˜mî×áf˜ÊT6–ÿçß‚ˆhðÃU3XÁ{ðCÒN81²ùa»)#•Ø^cn<¬NÛ#à.±àº \B|,S Ê|ï€æJXo¥O!Èøè+°[EX/‡è©àw†o€ÙcöN¯„k™-ª¼rÓq°é8^ÄÌ…Õ{ØuÁûð}=Æ44Ó@K€ÑŸþRýý$¬{ Ž'a‹¤EƒûXœ §â@¢ÝõéÓ§+®³qéÒ¥qãÆµ×°!Ôý`€Fµ99¹;v¸ººš˜˜œ={¶ã7H]gà¹QQ`¼²òÅ,ý$¤X/ž_ðV°uweĪ Oåáfš‚@ÕÔ,Xx&0oI-a³ýáÀmH+ž7vø-Àg a°˜šð@ůKŸUvØêÅì0†hÀLuF‡¢æÃÒÐa~9ÏÊ"Œáy)ÄoiúžÈÃÂèw?€–Ðó>c£…°(Æ÷fô—v…õ·aÕy¸˜ s•Úåh®]»vûö휜œ)))ôe»Œ‰BÝh„P»Y¹reXXعsçâããé-IIIŠŠŠu«BQ0Nbàã2P䤧Z¸t  «pÀ¢8XT£3I¤yàm.S@ ú¬­hIV=æq€wPHÜû mÁ ÇL"F0Z â‹+Ÿ~¿ÙôPnþ»‡ÈBxN‹«W5áʇìbÀÀ5Ī^“ùeÿæý¬Ñ •®zLÕIçáþë?Ð4ÍÈÈèÊ•+’’’ïß¿çããûÛ5{öìÈÈÈŽÞ B¨¦Þ½{ß»wOZZºù®¨0@#„ÚÓƒMMM³³³ýüü6oÞ|þüù»×7˜/€Øí÷\´ ,>€ª?ôæ`,,~¡žp52²Z½Jí 1€‹Xã)¡²CÁ¿Œ¥$jtåUˆO©|Vð‘ñoñØ¡§(@‹4‘8ªoßÈ<ÇÌ% Õµ™ŸZ£Z6ɹ€Wš±J~&c×jìA'&&>œþi§3æÞ`zF¨óeddÐÿŸ>kÖ,VÒM`€Fµ'nnîk×®9RZZº´´”ÞòæÍ##£ŽÚžÂè».„%Zðé|a‡ÅÓ3ŒÉÀY±ÁÌí0f(ñËCŽL¢uÐ_Òb`wð™‘\›‡C!Ôf B톓“sùòå666{÷îõññùñƒ7óòò¾~ý*))ÙìêmÅæsáì.èµ”ªÎóèÁ.غÜÇ0¾k¨5vïî(¸¿öM‚¢°D¦É1¹Áö”ÛÁiO¸EÏ®#Àö °/ƒGùÕû s/y œØ k}O,ƒ ß~pN2ñ…Âé'á³ ÄΆÓª`±Fùs"PÚ:ñ—[vLƒžàÿи@}!¬Ù"í1!„P+a€Fµ3>>¾õë×ÛÛÛïØ±cß¾}EEE¯_¿îÈ @d¾”Íœ¿›`  œp©ÝÏ.Øü~\/ËŽ¾ ªsÈÁò˰¼z"‹Áš«j2I ìbÁ®æúF5VﮉàÚÈÒ:›æ—j·ôX–Ôj¡Ò@Á XÕ­!„P§ÃêÂÂÂÛ¶m[¾|ùÖ­[SSS :hCƒ«®Dñx¼|Ãíµ¯öТvÚO8º  €çžÁ"•‰ù±-µ¡'W;ŒßªvÊ—–ôG!Ôi0@#„:P=üüü(J‡ŒþíÁ`ñaWô‚[ß`ÍKzҭΔô”ùgÉ ž ÿ ,²ïñ¥sÀõ®l¼ùÍã- Jlñ[ùØšîƒB¨3a€Fu¸Ž¹=‘žã<ñŠh0íCO“ žý£ö颰n2ãQ¡_lcëÛuü–´S¾ &IÄ/¡~ÿnœ¡)yO£öî9sùÎëÌ"úG0.qe-C³…..³ªÿ*ð¨™G´¥¼0¹ôõÌ8þ?/–ú%dxO›WÓ.gÅŒ­3õûå…ªF!?LJ¦œÛ»ö».9ãèTë |ó^‰VÍh/y¶NUc[ˆÌ¹òᨡ`­5ÊSwk*º¼ÑÙgSÂ'‹ÕœOþ¸oˆ¼ãUÉ)Þ•'aCßàS—ï½ÊbÜE“À'­6|ÜÌ%®ËMTùŸ@_òÏ‘EÓ]Ÿç€é¥ŒÓíq€šò‡?‹N®!& С¿TÙ±±4Ùní :xüÛ‰R­çïG+xäc6vÕÕ ¬a<ËES^˜ôóß'—¢oµ>~0Â71fù@Þv¸‰L§ Šî~‘e§ÆÆXHÿÿ”̘eŠ$çÚ9VôwœýyooP‹¿ŸÚuy×è %ÝÜc ×Î{h(Üèa þ¸·}šÑúÄSs.ûœøÆ=9Ôgù¬¨]§?M["[ï­ZvêèŸqÁ6Ö/öê 4¡)_¢æ1Ò³Òâ˜Ë{¦ÉrV/ðÙsw»ñèõǬ-†iÝ\¦ØP„¦dæèÚ;Ï3iõÎRÉT"[‡]†šûðBJy­¦¶WÛ±¥v‚Žê4 Bu)¥¯ö¹F}øƒ–Š5ï‘HàîkÿCöß8)­âîæåY7ö­ó<“”šO’p_=S{Ï­úïnM/­‰VôÌgü°UÿSZuãæ6}az¬¢|¿äææuãÍ72DT ­Öx{Y© 0åÛ­ÝN>§“³É ¤2yÙN7é¦v‰(jäsÄú¤ðEÎF"N¤gŸ„•v'óÄ­Ïï4ªŸüéôÎ ¿f,5žÌsK,*doø;7Õº§y†zîàLž¹oÁ–9O½‡òÖß௧»WÇ€úö¸Zé™QаöêèX¶ Ô~ú"õ£7%#pXïEoÏ%›Ñ¹o “›:’ÔÌPméùŸ‹xÏÛtã«nô·[ÓëDÙŠÙ%Yv §tÝ|üÞçàê9ÜrÝ>ßÅCëFXZÑ›“^kw»’ü¹˜SDUFÏ^ã³Ùzoc¦ÇF—u*œë8&^È:ôilýjyZWªPVø©¹é×Îh_îz/“ ‚ý¦­;l/zÙÍ~}еEÀÛg¼K@˜»¡D¹CÔÖ¯KÉMÚ»|ÅÎèGYeœRZ3VîÞ¨ä­:%a½ò ¿Z1!³îÑÈ9?A ¡_>Ô10@#„êJÊRc£R†¯\¨Vÿãþ!ŽÛ‡T=£§P{­ Á™jÖÛŽ& _¯]1*ñß[Ovë КZZûôuYZ؜ѫ’zÛ_¸º•™žiÿ[££ëóÊì-á{t¤©é·C<Ýæ ¹—õ4qµWùûSǬº#0nC¸óXêÇë‡VZžÊlr¯ˆbãw±º8ù¨í*ë7‡Ç®¯±9šCÏ»Æ×šÇ\ú6Ô÷>MÂÖAW˜Ÿ´ÄªwˆoÀÁ'.û†óÔLìë?å¬ówX>Ú¨QwâBé»Ó1é@é:W‰ê!õ0ZåÖp™$Éé‘ÉÊçlG¹<Öð¾l("¬ÈßÌqægç¦çûI›—‘¥—„+ÉiÕôDvzŸ¯'m,_[xŸL>.Uú:z½õš¥ÙB)q³jÝÕˆ’={¸eEÇqgô%þâwçw®ô›§÷™ÿ]”ñÔˆ»!k&,ˆ°?eÓW¢¯ dyÝjys–´®T;= }s´ý4ÇûJªáEà\“-«§|=+É¡»1úE¨à—ø•ÆK½fºê§…®Q[½nù{c—;<ú+ƒd~=ð2$H*vnvb+G¯{4þš¿Èt Bu%¿Òî~©)ÚÜWËÞ8gpŽ ¿bU1µx¤WŠ¢“ßÚh—›V?›Z:¿ÆTä¯ç–žËc¸wBÅyÂòÔÃŽ>o¦J 7“d\}C]ÑO}ì7®I°7Jóßq‡,dqÂkcòH} ÚõMÀÓ@‘Õˆbvž}Á8tÁš)Øœ‚³Ä­ÎÕ‰ÏPôhÀ;qY¢ÅHCm*ûºÝsËëøøzsÙ¤gìÛsøŠ­×‚½3ï®Q­“}|˜ 7r pkg(°‹(ih( Ò·&ÐGc°¦0ó8¿iêH.`N±)zZâð>±OóªHdt*”÷ŒôžÕ›1„œë‘ÀÛ²fç·D¤Îp­ùµ¿_ÿ¾¢ еr8±Û¢'££áMZ’¼C\Àÿ~˜Në;°Ÿ}Oyzõ¤Y1ë·Nµeo¶´¶ÔŠïPòûm Y=qR^f¥»‘ñù;¥^i›¦ËÑc’ü÷Þ±>7/}(­QÿC]+ÖýõÔÏ›ñ›s"f³»¡¡¾Â e³à!Z=šHý£:h„B]­¼°°€W”·¹üLÉNŠ}D}›ñRÕ]Ùe&Zª9=yû,ßèKK çkV4Qóïn46 ,4 ½4½'[ÕÈ7O<£9º‚e%%•+‹±÷þóòç —‰Àfh>¬:Õr)M›&»igNÓÅ'확05ÈÌ„>Üœ³»&ÔŽÏ´Üë»"²@e“MfLãPšm¯é¾üÌ®‹_ÇÍêQ/ ³ÉZìéç¸Á&Àìæò¾5­ô'ãº%¼"<5Ž!­,ïóç¼²ßß;%òôéÁÓ|Ânú8.Îléo9I¶‘ô\m ¥aõw(‰"C&©ÀùW_º«Ñ‡oøÆ¸¤5wS´¯$çfR ¹IÎPªÚT­ª'‡Xo!î1#zTþ>° ËÒ·œžý³‘ëq¶t]ò×û·¿»‘ÅðªÙ3DI#'S±˜#ÅíT ê< Bu!!N€ü/ùdÆ=Ñ›PžóžžX…¤yjœeS#À«ÌŒÜ¬M,- 0téÛƒÓÇÆ'ST7{Y(pÔùŸl€²Kf=ê_Ô!-« ÷ãw!ñg}ÙEDš Ðô¼$1y×þñg,ý}&Š×¯”Ïñ;ã AÕrœP~NÅP<úVC`ÅÕ]QÍê¿a³+Úo U[½Ú.tê»^Õí.!Aú¡ËÏ, ÿ~Ÿ/}½]wÐŽk¬>âxÎm ±f¯fÒôq.¨ˆrñ>¢Í% vIYá߉ž$ -Drnf0H+|µÝs_tâÓ÷ÙEÔßÍÍ•ùg¥’ø„¸ª×až/ç®þøA ’è 4j#5´x]ò÷é¿9²5~s¸dµdàHJûT‚:h„B] w]8÷æú­ÏåZ ^$¢3fÔ½”Æl!š^Zñ”ú&þæ(…äÄ –ÛÆßñ\ym<‰žX¸ ýâ=5ëDh’ ’`ÙÃzù…F%Së¶5ˆ( ''@ÿ9:ç~Ëþ9ês›ñ^{ “ö¨½èÉÞ׋6l`:3g¿e‡ÝÃm\±$r♑ÕÍÜ}Gö„”kw²Èƒä*ßè9äçž’Ï ‘%/v-Øt¿ECsǹr¿ØHÍ_WP« Êȵ۠üÃ!cÍ%7Øt–m?8´/¡èŽ£ÞÂk\jaB ÖÚ|§èo!„PWÂ.g<[u͆»vÞZ0¦îÜß_/¶Žž¯¹#jy/qeI€ôÔŒ"T_º<ç]6OoY±lM,¨8¥GîŸ|ÓB' X´ÑÔU7y¿¡#Ö²K(K µX@M{„T½9ä½…2?}+¨ú¾\YÖë¬?Ûíâ§ý_ïXŸçZ_ž,}wpÞòø ÿ‡ké6ô51n•G\#†í\æ{ulÕ{:‡ü4kµuëžxûÞ³Ú£[q¡;¢ ê8SÕŠå…¢§—¶8@s4yœZ~S›òÌ´< T­@þÎ8‘Ï.ѳæåiÑ~7J8ÇGÇî.^±-jÖã_¥]jÇ J ¼ÏÈ«qé¹ÒOO>±®"Ôv Bu)Ê‹wÍß?áH é̾—O®VýgZá«Ã6Fëïe¯$ÎD =suxp?èÂgóy½*:•¥ÆF¦‡á u …&–òUL àê¡(ÎÁÁosôèfÍíF¾8aÑ“ ˆ¢#f†¤»þQ©–Õ“‹½<à´;Co•Û™¡:’ðüáÉ{yS™_"ÚÏäˆ8z€nòK„M¢ý¸åö¤ì7.6Ñ®}‹Q¶»â}"}ol1¥ Ïðim8²ôÄHûõÜâ5Žá^;Ã@¿©³ú\>æ0¸æµâhEïN?ùYïäocš>έ¹öó£çÓÙË3“%31æ5@ÿñj5G –þ¤‡e^ ‰ª9´Â'þÛî0ú—S˜?2FÑžîo¿R;{!CDáåÃØç…*®C͹êÝößœ&êP Bu-DQ£}—÷åŒYvÎu¸Ô¡Ñ3& ï+ÎQœþøâ‰³Ï ýŸ¹°Žyá6vE›=Ž üll¾¹['§'…¸{¼$jnÚiÒƒÈ.ÙÔR¨yÕ9R“ƒÑ÷ öÏ›uPóò2%N6¹¹~«êx;é›|ݸp”«âwý2qnφÖ&Œð žÊèHd€le›ð˜=WƒòÆ.Œ^>Dê ‘Åd)>BÑ×÷É·/ž½û‰Ì®:ïpÀdÑEè¦3ä¶x7%¾ob]°Îz˜DÉ›˜M+’(‚fîrlðµº§ÜØQâ[BN¬ô»ÝLžò1)Â'à³Ñ<¹7¡oOE]×›9D —@J¤ßa¥12ý ' ªý‚v+µcðj-[¤º5Ð|–ôÎ%z’…O#·ú¾×†?Ú4›PÓGu$ Ð!„º÷£Nùz8q)jïõ2zô@ý%{V¬Yd(Sõ%+‚ШÝ÷nɯ^¿o“eX Ø%ú]çi¥ÎÓÜÒ:'íˆB£¼ÏlLÒôp4Ý4üÞ&->~í­Iú¸mð ´7ÝFà—Ó6Þr~‹ËDÆe$Hj«.&”/qñÛ»t¦/*OZ7d¿ºq¯ò6%èòÇwÑ÷qˆã\•æ9³÷±\¡·vÁÍÝÇ?ÌZ)×àDCï@‹¸©'¾×häV±=™¢o}ÀÇÿØ…è]— ¥qŠöÑa»m×"»i"-NMÉ–ï1¯ÎŽÓÓ®oð´X÷O>«×ˆ…AvM“$ÖúYðëùÄùü\´u·Õ˜]À×gô<·øý¼Qÿ^]xfͤ¹î,ÝbwÜ*0ÀÞ"Rÿ“qõ"c;•ÚQ¸5Ý/.³[àiu8{°Þpæç2•ùmšÍ¡8¯é£:h„B]Q ¿¹ÇQs¦{‘ÄõœBn9…´v)Qjþ}Úüš-<îÉT÷=„µî¿´pƒ³IyÄժͨ¤EùŒgøÁ,ÚÁÚmìŠÎÉ4çFW!õš‹\Ukç u%ŠÏ¥¯ÓJàí;ÙõÐd×–ö› ñµz—yhüHDÍoÓÌ[2.•Æ£jåwÅʯN{­ŸQXÛ%ê¹Kí61Ÿlª:ôÂôPSÕ¶²Ôú<Ãü3iþ5G”Yò„¶¤=júœr¦;/›î¬~NNõ-¢kÆ…6Z?›´i£: h„B¡?P=¼Î$ž:í´Ÿ}–mL8wa_e;9=ñÌ jTàjÅ8Ͷ£Ž‡!„B¨­è)¶:¿6ý˜À#-{îBÖ¢­ÎSTx¾g“YÐs‘Ç”ÄVŒÓôcÔ)0@#„BµIäÊHÉ ®jï ðêè ý­á®–‡È|2CÉéà!j¸cã4ÜŽºa€FuMEOæö¶ Ïk¼ƒ¬urŠ£WãÚ53^[Úë…Éþ¯g†ówÞfúëÕŸqÞmÔϬ¥Øªv5€‹sv·eä¶µ£Ž!Ô5qö¶Úá¬^Pñž@ý~ûè–Øï½f/rÒ¬¼@,Il@¯&oSÇRä·Û-„M|ñl¾2Gó½Y‡B¡ðññ™˜˜xyy)((°º„ú;`€FuMl↶³ +ŸPÓ¹èZÎØÜÙ\°± >QÉT"[׸Œ5ÿá…åÍ÷kdíÎÛ@¥R1qppèëëoÞ¼yèС²q„ú[a€Fýµ¨™gµ¥7~vØwX$lÞ¦G_u½¿Ý#¸ÓZÚI¯ý;ŽÝOþ\@UÑš½Æi³µ"?¨YçGHy¤/ ¼8â–«û™ËïŠøU&[ì ^8N’žY)Y׎­ò8÷K>€[TÝpÒš gªp×Oím…7‹1Ó㣋¿ §?ÇD¿œó:ôgå¹7öù{¸ž”ZH¢pßA¦ö‹¶:hJ°5º#H$gffÒs3=C_a"‘HêêêS¦L!´ðnu!ô_‚!ô×"°srüH:¼Œ,¾$ÀKIN•q7Ê·èÙó,ã¨:ŽË£'Èòÿ{~ç¿y¶Ÿùc¢LEìô—½¯ñÞÓ“‡¹9zX>Äú™. 2]Ø7-v´à»c ý^©›l?6j 81ïíƒ@ÏpË_ùÞnš,Vû”p[1qÖLðŠ˜q*jj_ Æ={©ù öV‚¿©Y;;Ð_¾Ý X»Â.ñß '»ñ7¸#GJJÊŸ©¨¨È××—þ 99yêÔ©ôx-//¿jÕªùóç³³·ó„™Ç·ï€¡ÆÐ?'³º„î4BèïÅ<;Zô´Ìáý&Ç>U/g¿2_Ñu­fžØ=®'‰þ|èMZ’üޏ€g?L „™](YbwfK12q/;gÿÄõ·_R¾ý)ðÏÞãì<Šq';£3I_Áçt)WýûË5¹•¾å%8x$úR©˜]öö´sp6çX¯Ë!Ì[ÙŒ4PåJ1qòÛí´€»¡a^^Þ Ld29,,lûöí©©©‹˜$%%/^ìââÂÏßð7(õõõ›=c}úôéŠZZZí\=B¨4æW cccgÍšÅêZº С¿]£I²5^ËølŒ;¼±Ær6Q9HÎÍ-¤@E€ñ£%«Î(s÷x÷­€Bä““…—qža1{-&æ š±»ZCmr+õf_P³“ßQßF[ŠTÕÆÞc¢¥‚Ó“7±ÏŠ ohGX‰Í†‰þ¾{ñâEäääL–––îîîÒÒÒ5W™?¾APP‰DjlXº„„„Š4¼hBëùóç¬.¡ûè"¯Õ!ÔVñ^¢µ^Êh…o®l÷<øî}ö/êïæß=H‚<¿gdˆ„Ê8ǧçtfsîÌ ‡Í4ƒ@ï†Ú“ؚͧök`öBó[©œó>€_Aš«ÆùY61!|ÈÌ(¢4¼#]@˜ÈDüèÑ£ 6$&&bâææ7nÜæÍ›û÷ïO×?~}:މ—¥¤¤*úDFF–””?~œƒ£©ë÷)**6Ñ!ÔŽ²³³Y]Bwƒ!Ô­Ó¢OÜ(á½kïtáŠÙ>ñ|·hƒjíëi> ˆÈ¨ñ¼UǵYùÛíê¬^<[«ÜÔÎÑh„B] W¿eÇ]Q³Ù>síø§~úBô¬ùëùÎY«µwE¯ÓdÜL’qÒuÏ;ÐÜýøîоՉfƒ£™êÄP5æÇ›ŠUeT‚Üd[{cÁêñwJ;³jÊŒ}Ì$_^^Ò§M¥ 3@×Þyži½;³7‹J¦Ù æ²SGÿŒ ¶ñ°~±WO ÉÝsT¿¯‰!/Vú ®‘€iy·÷GçH“Ͼÿ­¢ÌÖÔfÔ܇RÊY]êJ0@#„êjØeç†F\ë?m¿…ó„Á¹“·Yº=ã¹L…“Ù¡ü[j6€°š¦t­óDQ#¿‡©[Ed¥øšH.ySß„”¾ó®¬ÛpÝ2ÒH¨VgJFà°Þ‹3Þž!J6£sß&ñdÝØ·Îó@LRj>HÂ}õLí=·:èK0ßF©™¡ÚÒó?;$ñž·éÆWÝèo·¦7¼y†zîàLž¹oÁ–9O½‡6u[e^õY#ﺅ|à<’¯ª‘šsÕïlü‚É=*t[åÛ­ÝN>§“³É ¤2yÙN7éZ‹¿ßrsó‹ºñæH"*†Vk¼½¬Ôˆ@þ°wh§—c"?]¶”¬þŒPòØEiÈî"ëkia£y]·åÕŠ‰-Yv §tÝ|üÞçàê9ÜrÝ>ßÅC˜So¨Yá#¤æ¦;\;£q|¹[è½L2ö›¶îp°½èe7ûõA×>oŸñ.a&wªr¨e‰Gœuuºü®GÅqo°û8IRÅ´“Œrשp®ã˜x!çü&~vè?4B¡®‡$i¼?jéÿFûÏY¬"·}Ók!³“GæËU,攡Áãµz«åÉ ãe«oïD~…–L®%õšî>ÛõÊáó! k­A’œ™¬|Îv”Ëc ïëÁ†"м¹ K´&gªYo;v`˜4|¹æ¾vŨÄÓ±@`ç¦ØI›—‘¥—„+Éi5ŽÉdÑɾþS.ÌÚ1‡å£O ¡ \<ž×<ÊïÖN½‰•ó=(_Îû])Qöœ£|ãà‡ò÷¦ŽYuG`܆pç±2Ô×­´<•Y½é‚ÿ­ÑÑõùGeö–ð=:ÒÔôÛ!žnó†ÜËzš¸ZKÖtÉðv·‚¯eϜգ2ÿzvâH-]2œ¿à«_·NÔÜûF*;ý~=icùÚÂûdòq©Ò×Ñë­×,5ÈJ‰›Õ“D?æ\ôó5ÎÑöÓï+©j„sM¶¬žòõ¬$‡îÆè¡‚_âW/õšéªOõüMígÅPñÓ“ ÝŽ<:,Cû»Út™—éBõ´XSqñ©wCÖLX#`*ʦ¯D_>@4B¡®ˆ(<Êû´ÛÍ!›LZp!`ºôï{žÄ&í;lõÂòèÖ‰rÞRšcÇŒÒ×e0r°¼`Kß×x” •ápRÊ«²!­•ØE”44éaT Æ`Ma({³Å98ƒsløå«Š"Fhq¥(:ù­v¹iӋļó$=-qxáØà”jlÒ3öí9|ÅÖkÁÞ™wרr6ºû¢£ÍDâN=~&ó\/ùߘ}I”A{gõå¼ñGÇá×3ÿwÈB'¼&0²ùH} ÚõMÀ¼mdyêaGŸ·ÓN%†›UœcÖ7ÔýÔÇ~ãšÛxÑž“—ê³YÝ º–m1»"A? þrkj°§lrÝZ§ÃËÞ4qT瓈ŒÞ…òž‘Þ³z3˹ ¼-kv~KDêŒÕU—f§ä÷Û²z‚½¯ÌJw#ãówJ½Ò6M—£ï¾ü÷Þ±>7/}(Ñãkj§¦V •%åq×g¶c[½ì¶9ûÇ®¿}æu±©>ŸHßý$è?)ž^ýiâhÄ„!„P×DàUfÒwÓË€wÐèµç°ËX„½Öµ8wùjø®„ð]À¸Gä¨ù+7oZ4B¼Ù·7'?/½SÉÏRj3=)ÙI±o€¨o3^ª:³ËL´Tszò8öY¡M¯ªéÕý-'É6™ž™Ød­v„ôsÜ``vsy߯òApøÒ9=Ã÷í˘n'C‚²wÇ$“ôŽ˜É²?®Ñ­õÇ’}?1Ø Í‡U“‘KiÚ4ÙM;s˜KožxFstËJJ*—‹±÷þó²ØDŸWÒhéX®ÄËAW¾ZZIÑ(EOBNeA¿-sû±gmfÝ–ÕùšÌ––†ÕŸ›ˆ"C&©ÀùW_®V®lT›ªUµb½…¸ÇŒèQ¹Ól²"éÙ?˲7UØÔþÌY£%«¶Å!ÞWà]V¥‘ú¯ÃB¨K¢üÏ}ææDôŒn_°³ Ó=» v<%põi»•þ •ä¼}xëzBlø¡ˆCº—^_{²t3º ~ýNà—à#5Ù <ç==Z +HóÔ‘]LAŒ¯23è«òrxñ>¢-zWeW´ Þª¶zµ]èÔ+v½ëÆ£¾Ð¦¯Ÿ×¾“©ó]•(/¿å·y’T½r[yÊ¿ü $#^ãì7»¨‚(@sgÿÉ(»dÖ£þü’´¬_4à%ŠY:‘ïb\Ðå¬Ùs¥‰?ŸÉM7 EŽò—Í­[³ŒfŽ*3@³KÊ ÿÞ_’€´ȹ™?)PùyŠÄ'Äõ{Þ ã¤50OÕ"‰Þ@£ÒšÛ©þ£‹òüþÆ‹¾j½þ1a€Fu"-çË—Ï>°ºE?²º„¦Q¿]tšáóOoûëq;D÷Pw_2ç î5G¥OظÄUôÌèÿYìb½¸ÿ¸ C›< ,%šJдüäóoz (Ú\€æ8´:YŠFc¶~o„ÈFjé/8û-;ì6hãŠ%‘ÏŒl´—²µ½†×Š€°7ËÖ„§ N 4#BAã…¶è8Ðêî #)’+ÏÃHìôãÁmèï©Y'm’•óZ€ ¬g?U8æxðåÌ9óøÇçuwšÊ²¹Ùukn¸EG•P«v•‘h -=Ê¿7ÕüNUW„P‹`€FuNNÆ ¯u¬.U¢GŽ.:¡“œqÜÖòHf—ÿm%ÌKp=æ?pµ“…ÁÿÖ àZÉ¿7OÇ>(½tÞ€ÚßÖ#Š ÒŸ-èJnÖO H4ñWú.Ä#¦zÙÎÈÓ\1âÊ’é©E4¨¾3IyλlžÞ²¤Ú¹°…¸5Vq¶s™cìÕ±Ê.7ÝQoå‚£¡÷F|;•-a¾tD­ †´í8° õÈüô­ ªO —e½ÎªÜ¤„²R‹Ô´GH5v£a‹Í$Ž…Ð´±tÐÙ<α˘'Æ -Y·JóG•ñ<3-•O€Ì¯WE˜+KLÃêm¾$ŸÖ†#KOŒô·_Ï-ÞX’ôäåFÜÓ£¶n*È“±[¬U{c„6¶Cu$áùÓ÷ò¦2¿D´ŸÉqôÍ8DÑ3CÒ]ÿ¨TËê Ú¿^pÚ¡·ÊmŽJÅ \¾Áv–=ƒ÷‡ÅŸ»PÀ7iÙXqb‹×­ÔÜQ­ÈÏŽžO_d/ÏÜ JfbÌk€þãÕZ{Ì›)L¹C0N{WŸ¦G4B¨³LgbuÄÓÓsãÆéé颢¢¬®¥+z²}†ã­2Í­'× ®>¯Ê©¼ôèÞ3jK·™¯3zºW¨g¸Óõ±{6jËŸ5›c¬£"-ÀV–—þ<ñTø…·%’3"¼FÔÈX´ç‚d0Ò­üç×w.žŒy” â}›öhvþ0æ,Ûìq<`àgkdóÍÝrˆ89=)ÄÝã%QsÓN“ÍimA`„WðüSFG"ódîC3\n"dtü({-è_÷Bp<­:Õki:8ª¬?aa"µmé(éòÔ+¾×zKÀ—ŸŒélrsýVÔñvÒ7ùºqá(y®¼Wç÷{ø'ÍÆlþý÷ îó­å÷ns[ÃöSdÖR½ÊyÖ-[·eGµâ²zß·N±.Xg=L¢äM̦IA3w ¹Vg—f +nv}¡^B)‘~‡•ÆÈô3œ0P¨í?vÔ=`€F¡Ö)..ÆÝah?n®îùœ]Ç÷¸KÿZg,Ùû, ÛwFÕnŸ…óøÁÇì~øÎЧÿñKá›Oç“=¸$”µ¦­ß°ÒÙRS¤V.þ´riõnI5mk¯%®Ž¦ý[š‚B£vß»%¿zý¾M–a4`—è?vEhœ§•úŸý5‚(bèh7õÄ÷Æ7-8b™eã¥ìg70ÿ›(ÚšãP…SmÕÅ„ò%.~{—ÎôAåI˃â†ìW7NøUΘb̯½5éA· ¾ö¦Ûß³”Ó6Þr~‹ËÄ×Î~V ”·¹½%3.ÿ\}[‘–­[Õ¹©£Zq®—WgÇéi×7xZ¬û'ŸÆÕkÄ »¦I¶!¼6YXó³A8çm±;n`o©¿ÿÉ8 Ð4B¡®ƒ ¤ï÷æ×à2v¹…WŠV?åQœäzh’kS£±÷q~AsnK‚Æ×j_$®çrË)¤ÁÞQóÛ4ó¶TB7>žK;ÞTO^í™´ÖFhÁq¨‡MÊÈ#ÖÈ£f“QÉï1‰ÂZ ÷_Z¸¿©!8úmH¡m¨ßÞäºD©ù÷ió?oò¨ÒQi<ªV~W¬ø…¨Ìy†ùgÒük4d–<¡-i¾°†~|Âf7k~Á‘MÚôÐ ÓCÕ‰þ{0@#„B!Ô  B¨¥øøð¾!„0@#„P‹u›ëo „ú B!ÔÅÔ-Pׂ!„B¡VÀB­SZZÊêB±h„joß¾)**²º „B,ƒ!„B¡VÀB!„P+`€F¡–"‘¼12B¡ÿ Ð!ÔRbbb¬.!„ëa€F¡–"¬.!„ëa€F!„B¨0@#„B!Ô  B¨u²³³Y]Â_ïÀ±±±¬®¡ÿŠÜÜ\V—ÐÝ`€F¡Ö!“ɬ.á/6xðàÇbbu-ý‡„¡C‡²ºŠî4B¡Î³xñb…üü|V‚Ð Ï„ X]E÷!„P§7n«K@¡?‚!„ZJDD„Õ% „b= Ð!ÔR\\\¬.!„ëa€F!„B¨0@#„B!Ô  B¨u~ýúÅêB±h„j¼B]ܱcdžÞ§OV‚º- Ð!„êV¬¬¬ôõõoܸÁêBP·…!„BÝMII «K@Ýh„j)NNNV—€Bˆõ0@#„PK‰‰‰±º„PóBYY««@Ýh„Bu74Õ% î 4B!„B­€!„ZÏl!ÔõáõÚQ‡ÂB­“››ËêBÍÀº¨Ca€F¡Ö¡R©¬.!Ô Ð¨Ca€F!„P·B$ËËËY]êÎ0@#„B!Ô  B¨¥zôèÁêB-‚S8P‡ÂB-ÅÎÎÎêBÍ#¥¥¥¬®ug B!„j Ð!„êVNá@ 4BµNaa!«K@5…H$R(VWº3 Ð!Ô:%%%¬.!„+a€F!„PwƒS8P‡ÂB¡n…@ ÉdVWº3 Ð!Ô:eee¬.!„+a€F¡ÖÁëË"ÔʼnD*•Êê*Pw†!„BÝ =@ãhÔ¡0@#„B!Ô  B¨—.]ª~úöíÛÀÀ@úUUU]]]ÖÕ…j‘HÄ/¢…!„šÁÏÏ¿yóæçÏŸW<½ÏD˜˜ÈʲB 8u( Ð!Ô ú›±———‰‰IÍF===}}}V•„ªÏÛÛûñãÇôEEEômnnN,..îïïÏêÒPwƒ!„šgll¬©©ùäÉ“êwwwÖƒª¯W¯^kÖ¬©~Mÿ÷À¬«u[ B¨y'¡'Ož\ñTGGÇÐе%!„ê°´´Üºuëëׯ«[¤¥¥çÏŸÏÂ’Pw…!„ZdÒ¤IÆ «˜ýìææÆêrBu‰DOOÏŠ™\]]¹¸¸XXê®0@#„PKmܸqüøñZZZôY] B¨Ó§OWWWöìý±„„„«+BÝh„j)###µkײº„PÃ*f[M:•þØÙÙ™‡‡‡Õ¡î 4BµÂáÇ•••Y]B¨QS¦L:tèû÷ï—.]ÊêZP·…!„ZAEE…Õ% „šB 6nÜøðáC>>>Vׂº- ШíâââÜÝÝ) « A ‘H›6m266fu!!ÔÙÆ—iG 4j»ùóççå屺 Ô¨¹sçâ!ôßÄÍÍÍêPw†µ]AAý_ÏÐP.ü–FSR\ì9oÞÏŸ?Y]H{rss‹‰‰!¬.¡¿F344ôõõ%‰¬®¡î4ú#ô43ÊÄ„OPÕ… Z óó»YÐLIIÙ¶mÎB¨UèÿÇ111100h—Ñrss©Tj»Œ†ÚÛ˜1cX]Èh„Ð_ ¼¼œþ¶ÍËË{äÈVׂÐßÁÁÁ!''§¸¸¸½6l؇Úk4Ôî PqlÔ 0@#„þììì3fÌ`uýÖ¬YCÐí8`ZZ @^Eç„t5äòòôþIIIau!ÿ! B!Ô"€#wïâ´½®&+=ÝX^žÕUü·`€F!„B¨0@#„B!Ô  B!„j Ð!„Bµh„B!„Z4BÍ¡R¯Ùô(OÝ­©èòjt\Þ5ã:—(¼aÙsô áUÉ)Þ”[s{ë‡7q‡sÙUÉ/ì®ë(º¼ÐôK½¿L¡îëä¯GÎJZ¾À?óÚ—£9ßn#Jþ9²hºKøó<0½”qzëv´«£~ ÞÓæÕ´ËY1c»Ù®µ%#pXïEog\ý5 BÝhô×¢ˆÚ¸Êe¼óSÜ/vï+‰þ>%70{¥Uæp£…ýᵞ…ŸçHVìÿÍfz¦¡$µò}öG7z& =Í!Q& Qh0*s©ÁO"PóE篒Ë1øÇ·‡çyþ­Ìã+â;~²wøcž\2ML.ÏjÆ™Kj+^þܳû]nVÔ:œŠV;|Õ *î¥Fý~{ç–Øì^³7:iòUübÄ´{±Wu~âùnÑUŽZ#> ˆÈøƒ ïmtN6XÄA§Ÿ:þòüÊßnWfõâÙZeŽæ{×éOä`é¸8{Pï­Šú `€F-‰J̹Ï{¯þôÓvîwBÆÓž®'¤\ÇÛÿ¶'Tìo±©ê}ñT-eûÁ*®ÇÚ­Üež©“„i@c»©â˜Ä®0üóæYEbÀñâ®ôþ(¥Ç¹oÍ(ä!Qé êç{©”òé³?ÊŠqQ8£ü•|S©šãþ]¯ZVúI$(@‘@•­[Ý1û?€MÚÐÖɰò %;’ åŒ—:›‹þþI–§2þí9ªß×ÄÀ+}sý^Ÿ–w{tŽä0ùìûßÚV¥ 3@×Þyž©H«W¦’©D6–ýA„µ[g%jîà )åmíOÒvÙ«Ýuý·”gìÖ4qy ¢³}SÂõÄjþ&’¿ìbìøÃ:9ÅQƒëwOÐtM½?³Þ‘J9OÓòÍþq×¾lÍŒ2/®îÚsêÚË×™%Œ.œÂÊõg-·u1‘áÅyÔ Ðè/GÍ/_¸:c‚ 㔳„ÞçÙ‰BÞ §•ýàÿ!¶ëßà÷ûfç 2^þ~èH²Mó>ñsܸÈ÷Mâàè÷Ñn®8óUXS¹ˆ#«ÿ®ë=¯Ž}g̦ùôÑú ïcÀ€/­Ý¯ú¬‘wÝÂ>ð ÉWÕH͹êw¶@~ÁäžmÐÊÌxx{=±³û–0‰'ëÆ¾užb’Ró©@î«gjï¹ÕA_‚ùúLÍ Õ–žÿÙ!á°ˆ÷¼M7¾êF»5½fð&Ø£ÙgÅç¹·ÒCõx+›>î"ïøïœÄOGõ•“³®í^å7¾àî¡nhµf‡ÇLf0 |¿äææuãÍ72DTè˼½¬Ôˆ-Ú:c€o·v;8ùœNÎ&ƒÊäe;ݤëìvyS;H_z}ïj·ý1wþ-6±l6ø¸ÏPâ¡4·_Ô¬ðRsÓ®Ñ8¾Ü-ô^&ûM[w8Ø^ô²›ýú kŠ€·Ïx—€0wC R³{Z1ڲċ#κº]~WŒ½qÜìnH ×–^ð€Ñk ç:މrÎOà/zsÒkíŽcW’?ÓßJEUFÏ^ã³Ùz?~ÐŽÔéŸuèÓØšS8š< MT2N’Ôêß¹î(÷Øæµó¢ šù}ùÎtƒ*{­ÆÂWÙ5žSó÷ 7ˆ|ǯ¡ø²ÎØEÄ4¡‘‹ÇóšGùÝÚ©7Q¸â'OùrÞïJ‰²çåÛ0$Irzd²ò9ÛQ.5¼¯Š+òæ&,Ñšœ©f½íØaÒðån˜ûÚ£ÿ½õd·ý÷ÀÎMÿ@÷#ió2²ô’€p%9-ÞVn´,e÷ÃÕ¯Ôm·3(NÊ{{5Ðs§åˆ ¾·“E ÿ·FG×ç•Ù[Â÷èHSÓo‡xºÍr/ëiâj5zhh~ëåïL³êŽÀ¸ áÎce¨¯Ziy*³Fjn‚}ã;Hû~uÙ°±‡r†,Ü:®û—ănÞ3‡¼,y;«¹ý"°sÑßÁ¾Æ9Ú~šã}%Uð"p®É–ÕS¾ž•äÐÝý"TðKüJã¥^3]õÓÂFó4·§ÌÑâ¦'ºytX†ö!vµé2/Ó…êi§¦FÜ Y3aAŒ€ý©(›ÿ·w'`1u ÀÏlíû^ZµX’ˆ,•H²´ˆB^$k’ʾEv‰’%²e-•ê}¯%é#;¯5d-%I¥š™ošJû2‰Äÿ÷x<3wîÜsîÌtç?çžs¶œ¶3íèèŽÑL#·õG-uDs_ï0Î8Uôq˜­U¶ÚúäpC_J5‰²•ý#OT¢Ö×ìó…`ç ±w½ŒëŽÐ­º´{{cÇî§^¾í*ŸD ;úNª»FVéo`fFèìCI[¿{fi‹ÚC]Ýú9·wÛ»tó‰ñ~¶2h** ¡…£  ”ÙØÜ/Î}ÊÇ Îq© ʪéI”¬wœ~‘²8«ÂA‘-![D!‚™Y´’çPD¿Š—~WQ²?pÖg*вËWg´—"1éM½?ðعé/ž=«2P+÷Íç¢Fl‹*mâf'ê:càHùâCÑËÈÍ ÌÎþ£´ùÏ7ªz )N´ŠÕ‰ivêb I ®ôNáïrz·“·y±©¡À#-÷€ùG=/8+Ó¥øCúåv¾ëÓnšŒz¶_Ö‡+‘·‰ÄèM=L¸ éýZõÕõÈ(d&ïróM³ ±ãî ékÞ[úµæ´eób'ÆXKSê-=ïΖu—Š$YnYü#£O_ÓNl=}"TºBAÒö:vplnð¬ Wâö1±AC¸}hl,uÞk[†In× Ýc~j·j÷\K)ΓU½–Xø=~éëòç>ÃÕ9²–LXå{áÔ³|c‘úö´dkéŠK/ûŽV,®¨ò¤Õ[¢^<ö À¶oÇvrü„)wèlPÜ:çþ}¶^o'×#~­Š×5ïgÀNÐpÞþ¿¶6Rt)íÊë3+ô™¯û)~Çë¨I®m_ò§Òõ^Çwkdè„•ƒn¯Õ­ë÷¤°Î¨>w‡„_õ^\á$RÖ™€ølaƒ[E”èÂÉ„H¶6Pªô §J÷¸½JJQQéªC€†ß…RvY5ægRÚï‚]e)›ûœ²C%…ʮ̹W^Šƒê¯(qr'ͦÚE¼Çô1­B6ûG§ Ÿ¤J#n½E3Þc§Æ¸Ñ4%03¢j_çŠßNÎ3T9êºß¼u'ÇY¹ìº­Ô‘ž9¨"êí¤É•hﵑþnƒ;ÊòQ8 düÝâÒSw¹Iø,Æô/ÈÏ/]_¦ŸƒIü_ä½\ë¾eá¤ÖÒ™WâRÝ|Dwɲ?5ŸõﲃÙßFÜ#ôþ£z•%XŠäÀƒ™ìƒ¤¸kÊÉíŸî0ò²ùdT$ì×K¡ô«.©&EÈ«ŒÏ7¶§F™}ë&Á'«-KÈãôl&çU¬\¨HeÑ Ë*, Kk« ‘[ïÓs˜Dª®~ ~Çk« 1¥{lò¿Që–­sÜ¿¬­k²EûLé)<â߀ø™ÆƒÄÊN"%ü[ÐÆÛ²ÍùðÒÕøzuð93wÕÀÐE=Õ¿Ù©¢ª­pÕ¨4üžØ’rEœãbJ•MÊš™ÙÔ÷ŒB1–„|!!Â)+áðÈËãÚ×úñ¡H›8ØI]õOÌ8€{æù224ÙÆ”jùI$Єտ%Nwì_妾VÆ wS“.}Mºöé¢$ŽŒµÂ‡~Sš5‰ðÝ‹o»f*p¿‡r“•G¯—e xa—+¥¥C„ï]×å½\É·TÜUBÏ6W©ÞÈÖVË'‚÷o ævÈ)ÉfYåÎ|&¤…]•êÚµkÞÞÞ'NœÐÓÓkîºü %ckûj×–‰tm\€&ümÆNë´|Öö}gÌÏÞòJ|Øs*Én‚ªrqS»J0b³¹K(å‹J§5þ„UÚxáÉ×3ž^:yüDlìÉã[æGnñî¶$îßù NÔ4ˆñ6¨,iâ:ÅMØõ•ήZ÷âœWTáÌOÝ;HáCf-gŠš…Ö°=% \ø,h¨ÁÔót£köïè¦)#L§|¹äfìr¶!U)þ¯þw¼5ùcÑÕ¬¶¯‹nç¶Õy»Ù…™ªµ‰)âzÓÇÈ…l>b>I•J ^ÜšD3^j§F¯x‰¡:`߃NÝyèÂé3Ç7ÄßÀYʧd2~Ì »^²» Õ!@ÃoŠ.÷γ¯ì´ j“wòýÕ)ŸþQ$:V6“ÿ“·i^ñ¡V6ÓÓLvò95×úDÃ\Ißíÿ)½!m‡¥šˆò¥êÖÕ3ìä¥w_l½H"e¸VQnŠÔî3ü:äzn3ìZ£\¿~}Ù²eÿüóOsW¤Åa¨w3öš°ob¯Ìð ¹Ó{I4a²á“m#OÈ«ä”/lòíbY…ïg"¤¢&FkH›fI­Ø]‰ùù]NÕµhbZÆ#grþ­ff%®2ïµÄÇ+ÒåhE IÎÓíÙK±QƒÓè*’„¤½Î, e¸¤ ýAùØ€ºwPZóè‹ä×e}I•ñUPA©aûÕ0 ¹ïÝÓŠ Ÿ 8ŸÏ?ðh”ÿpÙ’j³Òoä}mÈs›â‡bt­I‹Wî1wîʽöNR®m5}—aÚ;7‡¦ŒŸ­Ê¼÷wp߀½«ŸD¢Èõ™8ó° Þ%Ý?w9*$â@кާžŸ½9ÇL¿g  hø]Q˜ÝF>Ú&­¼ùœüª›TÎ]uÝ·«ìßô—b—<ÚÅþÑNå­ç^¦± [J){Ô¸§“{ä ÔøÆÈ83¹p¿rÄßñ„-§ù~âÔçŒ#º×ó~önñîæÍ›ÞÞÞœè\±­ðÁƒòòòõ>—ÅUr»°‡«à~Ã)ôÛ87jh¯¬“ˆˆHff#/·ÜThJƒgZ[å“¥:iŠa“â¢ÊÐ'W¯ì<‘:b\éø±‚ä¨CŸ¹½~ƒŠ¢‰ÈŠrïUZnY"+xq2*™ îÃìÏ×¼ÖÝï±:p¼fI3MRßÂH~ÉÍ쬯’Ö#»„Ë[Â’gj—6âåÝÛêî—bœ]ç~ñ†*Ýë;÷”Û:\Ú¶Îúú™–…åäʺa°snnY}‰sƒYÈdW_¿RM¾ÿ‡Rü3v¹ìë¼cÖÔØAÇ:׺V«ivÎÚþÏÃã²·%n²Ã\‚Z½™ä ŸlÛÎvœSÆŒ2bÀΟXSG)$h¨ Z,ŠpVpP•¡\L3¯׿ݣZ<±¨åùÔ¢Îý^ìì×À->é3Ý?ÎüvŸÅˆóÊ(ø¥¯*Å ¬·oß¾wï^•äÊɲŸ>}ª÷éT*•RvV¹âí†ãÓZÂâp"i³|B‡Z.ËNŽ ô{Z)‡QDtíÇ[(×=ô¡å¼Ém«iÀD çÌ%Ž]e‹^%ì^²ôÕÀg½µBƒ^,Š„¡mrñÂÒ%‡µæ˜(|}üÏ:¯=Iî›\ü° ŒÀã˜;.¥<˜çÔG[šžûæføÚ­i´î³­TÔÿ ˜»Íh­{_ë·Ë\L4²î\º%j×oEú' ¸ºéo_xÄÁZqõt¥Âä·o<«"GÞ|.ýÄÕ½ƒ 9—MS·šo›hêðrŽM;FjüîÕÛÓDíuÕ”­k¿xDÿÎ=¥K(s‚û£C»tú©¶ëÞÏDvåî#^Kû¯±Ó`¾H8à»=ÕbœúýIáaçŒGé«óUZß´B©ïÇáÁNcöÌ>Ñ}ý:·¨mýkË3 ¥án½&ß›¨ŸþAn„}å“Hìü—7#¢îšÙÓ«üCŠ*Ömˆ&}gæûô\&‘B\‚Êð‰€–­KY¤»Q9 6ùòGûö(Þxç=ò·™­ ¥ô `N+ßïÞ‰ˆ“_'L˜0vìØýû÷¯\¹299¹d¹ŠŠÊË—/›·n-E¼× G…ÃÛ§Ö©5l½Ü·È³Ê"ûŽŽõhNþ5ñKŒ×˜»p³ã¾l6aÈuè?ko´·“¾PÝOü†¡5%ôð3çyÁ£ ·"Ù~ØL¿cÖúÃâòŠŠ?Ìtu—¨ËŒ¥ 7íŸ=Ê·øürº&Óv„,w.Þ¾ž«®j.^´qÇ4ÛÕE„ˆª÷ºòøJÏAJ ìöɯ;çdláTÏÿé#7ñ6V3wFw Ô›WÈnÀR%ûù'žU÷˜ç¿~JD>çn[ ÏÃ~ËF7G×½_<¢ˆ~מòi[9é°ÓŽíÓõ ¼yÊ7Ú÷óäU~Ný6M³q‹c„Ã^žq96Ïê¯ì«WWv®´þµ=>ßÿŽC9!ÃEK§qÙ2m‹ lmëP•;ZÎ [œ¥0iŠnåv~ +íì÷°´Nùmã&ö¯ð¦ðíñÝ÷‹ˆx¤g¨ hÁ8éö[ÞýÁ·YœÄÌê2It&3uLG‹Ž©‹–*L+²¥.K}Ïc·„ŸN§?ÞÉÉéàÁƒœýäÉ“æ®ÑÀÐô¸Ëö¨ñ!Óß؇kx€¦:õ&{jý›î¹5½µÂñ¡g¿]¡örkVñ¹%µ5vßᄏƵ)Ò#.²GÔ½EM‡€8‡€Š‹,¾–Aï|xÿþýÍ]€&@•2Ÿ±Ã!nؑگCé5ÃBáp¸â4ËN"QÅûù…<6[¿åÔ©]Ÿ¸bjchºp‘“‡cÛ:/ì ,hh©ªgÖÚRlÓ,§ðóº_'F;993¦¹+À†²ÇÝë5œí¡J=|®Ò9¦jk ÷œ—ÆžWaÈг×ÊÜ-«±AVc›ºÆðC€ø5b8 ”@€à4 ¡ù>=` ½éžÁœçWF¨—Ù¶q…>tcj”q…韊’V;´]ð¢û– »m“çÜ«m«ªË„.nÇ …)~Öž•WSï1p§÷h+ þª]¾ÜüKeRHVíÕU{ë‘[§Z®Ü@¬´˜žJËïZ¾=ÖCô»¶ÍšCÕh8'@ßþ7!s¸zù _n†Ý-äÄë‹ÿÞùblòíŠÌÌó‘/Ñv0“amãÜWük…CÇêó‡Ñ¥ú(T2ÝÊÅwL§âÕØEy“/ß²uȱÿöÝß0V¥òÈj~§uúÙ¥W­ûpqÿʨÊ£'»”N6F“ѫð'@€†fħ];ñ¢ðGl~gÐÐÌ(RF»S/ÅG\OóR½vú*SÂq¨jñÅâÚ›õ‹8úè³m·qqß Qí+4>¶^pdFOáª[TÕ®|eÍÅá3¸«±™Sž] «{vϪ-QóÚ󞢋Þ=Í"D´µ’@…§ÒeZKPȳ´”éÏêxô “ˆT,PÄØýØŠ÷#í²ë´‹ˆ©ô2ï9xĉ¶ídp­´iÓ†“ŠŠŠøøø¨Tôp¨‹Å*((àüÕôîÝû'Í|r"OØ~zŸÁB7eÂbüC^:/n]Òš[ô"´«ÆúôI›Ã;ÿ;{Elbjëá8~óF»®âÔ†¬P^LõŽ…ïÏù,<ée.¡KèY[äëb¯S|@fyº!W‹Ÿ¼¥-ÿ¾AïŽ WÙrí#[XéÇ{).}5cÇÉ^ñ³—;ýø ç+ í`ÿ`—ò%µÅX—ß447ª¬Á¨.$þÖéËï-Âïä w®ÇÍÀ¢mFô`„]8ÿ GçyØ#Bë<º§hù‡"Ý} ñàú¯Â!ÕÕÜh`ùj&Ž÷0lÜ‚¥›­-lÇsVåV€]¥ç~ñ’Òé±k}´ê¶¨âÆ ^Ïx}éd‰ØË'‡Í óîæ÷ï¤l ÿ1 ÜÜÜëׯëéé5wu~u }úô®ÚÐðƒ}}¹wã=¶œµko1QÚp'•˜ÛÃozÎáŽr!T†'I¿ õq|0`mè¡ÃŠŽn;o­i†è£è­hõ¬ XG¹¬ì33þê”ÕÕeòÞÊŒ77¶-Þ7²krþý c•²ŽŽçÍ2r›yÔRM4÷åñõ›ÆML jrà2™g¹Z±¸¶Ê“\=¶Ä-¼÷ ׬¯ƺüÆ ¡ùÑUöÓñ J8pâîsÒiNGéÒðH•ïkÖž;›‘Cô š¤]Aú™“©„ȶWhDW ªœq}rÿÊÎÿ¥Ž¢\RŸ‚”¨C/_7{}a¹ÖuÓ«Sc.dÇжÉí¨i€…óÇ%Žíe‹Òvo_zjàãn­@eÈ×õh¥ Qøe^ÆìŒ¼”ò|žSgmizî›GákÃÓhf[É7éße~~þK®W¯^•ÜxñâçvVVVJJЍ(®ÒM„™za}L.io1@"ç]ÉG… œº’Yg„½èïÖºôÀÌW”,ÿéNS¥æû´\&)í¾Vû µ)Ê|R<EU¶Æ–vÎÃ×x‡{ü4#¯üL`ý­Àu{ùRRš˜¸PùžB-îs]Ú—c]~_Ðð+P³$½~Û{¢ÒËB½ÂqE@eˆ¹Äº]‰†©•F•ãMÚ¾EkÜX«™ú#{K•EëÔ^kË£)wêâºeìB…Æ}ø)¢&~{ã56/Üì¸ï ›Ðå:tŸµwƒ·“ŽP½VBSwÙp™´pÓñÙ£öçsðKéš ß2ÅY§i¬OŸ>533{÷®ê峑ž  >Ùà"'T>ê®Tù¡Gþ»ŸM^¡]zt«<&„Íb±«,«w…j(Å–ͬÞOŽS­g‘C VŸ§wœ±fùŽnÊ2Â4Ê—ÿÜŒWœmÀ.5ldKí5ÃX—ß4üzl=ÅÞZ}¹`ïà3ìàÊËÊw¯{ԻɮV3ªêԃ쩵P¤ŽjA—Õ–&$-ùu…!(¬¼ôWY_Åó9ŸÏ7ðèÿá’%±Òå}mÈ.Õ=úT‚±.¿#h€ßŸÁÉ“' ““ómá°aÃ8ËzöìÙŒu€ßûcüÁ}©DqÚä)Ö+_ù£«ÜÄ1¾±Ï»öìÊ]pçÄñWvÓ4¸ù“™ùŒ­ºNÔÕ»BUT#Óän⎋¯G Såf›œKþÆá|³÷å+àä]9¹²nìÜ›[ö\*Þr“Û¶\ܔ̪±ñºîq/5t"¬ü’4ûXøq þœ”3xðà’ÉÉÌÌÌ’’’®_¿nddÄ`0,--7nÜØºuëæ®f`eß Û°nGøÙ+Jfªä—mÓÃbÔÌÅžÖ:Âå§L™Y·Ã7oÚv*ñ~zñj )-Ã~6ãÝfí­X>ýXa²Ÿ–ç}³è¬³Cë½f"Wþ“=“‡{†ü—EÄlO¥D ø‘dXi{z*M¸k}êí±šËa¦ìè®29ÉþÌ›°~?µ·ëÍî­œïÛœNì/Úøj4[ý¡XïÿYþQuŸÚ¶Úuóøô\FèùÄlˆs€»@.{Õ%Ù ¬ºË<Œ ž•À·›è ^¡=·¶ÞÖZ>CËzÓÔ£æÛ|LÒæØh0Roî^‘&Òk¯kû¶Ï»È®Œ9âÔ™3-áÀþí©=Ç©?ß›t6ì\ç‘]…•%y°KµŸª†¹¥¶h¥ÍÖ9²å}¯ÉÏë?Þ@€?…©©ixx¸µµuAAÁŠ+8‘://oåÊ•1\ÂÂÂãÆóññ‘””lîÊ6++γ‡é¦Ç¢GNñvëÐJœ–ŸñøRäŽ]KmCc×Ý:7›{b™õñòj›‹â²‰L—acætR“ å¤Þ‹>ºÞåè¦@÷¨ØõƒÙEžä$.óù¯ÈtáW£võ¶OýhT=G·)U¾{Fâ¤5úzûœîޙ߆÷m5¸UKiºúÃWøìÔ†s…¤«Ã_mkx»šgN¸pðð3{Î]a#·›k‹¼,x’Ãëå²hëSù í±µ­PS#qªX?ÿ½gÕýæù‡L‰(àÜmk1æ°ß䑪4в{´oîäUú "Êfã&ÆK;ãr~žÕÒì«»§¯´=ì¹}Ú‚C}çÜP)@×=²¥¾Qˆ?m¬ ü|ÐKKË#GŽÌ›7¯Gâ«/ ®àzûöíœ9s¶pÉÈÈp»3-ë ÏL ½é11ð»qy–ö·oñ ®nvÎíí]:ïÄø[fÊÁ1Åå´™»ÑF½|pýÆ€3KZ½iè°V÷â½Ú6æ*-„™ö‰ÞÓ<ÆÙÖyq¬êXE,*½‰ÏèR$zzú7EÖûk'Ö¿Þ÷U£Z)MVýˆ÷â·ÇÐ}‹=ºÖ‡irã㯎/>mÁùŸ-ÐÞiö¿N³k[½¶* #Q¶½Î¶­\ i³9+¯Î©¶9ªXOÏ5ÿUoâ¼þµsÙíÎ îÚ.(¨Ê–kÙB‘î‘Ý¿ò21» ×˃õOë?4ÀŸÅÆÆÆÈȈRy<»¼¼ü>®û÷ﻹ¹]¸paWëÖ­×®]kggG©{üû¯¢039ƒI]¥Jm`Ti‹€kÉ«¤ÔE(ä˵ÕsçP»múÛßV½Òj ysŸ¨]·tÆÆ.›j|äPiÞö¹¤¿Áâ›í9Ï¥[ü“k%”~~óï­‘ ÉŸXœïamcÛiÞ«\ûOœ¶·§ÒøTר]RkÇùœÛûhfüðRwa­[(Aaç=92Ós~pÜ‹\¯ÔmÄÜ×î’TR½ óÕ‹„˜YDhRmÍæ­]î¤/F--èœÿÜÅ‘—^溌ž¥ó"ß%ö:BlnGî\Ç Úò/àtâÝqK±ÊudfÆû¹ºûFÜÊ("mÏX¿X©Ú+S^š ² 5ªVŠpµ'Öúb¦‡ôRüëÕŒ¸“½þž½dçéÇÙ¤¸"nþÁKÈ—]ÓòËÃÐåó×ü÷Vjnñ…€Ûšžç»b¬ž(¥ê{‘®Ø[íM›~_U~ýÉ‚‡+:¶_œ>òä‹Ã%ZÄßü8ÐN\®í!]]ݳg‹¯ítúôiØÛÛS©Ô.]º”´[ÿÂøU{uò ›»Ê1tÑ@µoƒæ UTµuÉiÙ¼‡£Òˆ€å¼1Z5œh¦µ²™?R&vÏ©àÄOC­$x*œ&?üЭ6ÿL4ñ¼Ñií¹`s)I-á÷±S -ƒÓtÇ®>¸µ»ysyß’ù³Lâ^Æßô3erjð1aÅŒ"¥©ÛCtÔ k˜q™õ>vZ­[)Ù£›‹­ï©9-ñR(|³v^ðL³wIÿŒU®rvöÿæõö}ÒvôÊMFJ¬Ww{/×51ývÜ\]Ö‡33º÷z×ÕeÙÞšŒ7qÛ¯Ùõ^þý¨±ŠÃ\Þ=ÏrB¤Ø´ð0gm9m‘*•,|ºuX¿9—Ä, ñè¯Êzq.ÈË1<­–—©–‚öÝÙ]½”Ê3*×õRˆ2¸3*»¿e¾xÏõ]ªìgQsmg,·uÑe[|±fÚÑÑ=£™FnëZêˆæ>>¾Þ+`œqªèã0[9J•÷BE+g}_—sÑ)#'«–¾ŽOÂB’ˆü$×ÞHÏ€ 50`À½{÷˜Læž={¼½½¯]»Ö³gOƒ1hР7jhh4wkD‘±Ú¼Ëé®ãþUƒÔ×*ôïojÒ·¯‰iŸ.â¥Ç:öçÇ7Þp~(˜v¨å¬Âí-Ú‘=^Kýj%Á[/†”N§NZâœíŠivêb I ®ôNáïrz·SÙ$À†´Üæõ¼0›ï¿ÜÎw}zÀM³æ¾2IÛëØÂxîökª[R„—Nñï«A½ÄiLŒ]w(ÙqNåN–…É»Ü|“ÄlÂãBìJ:›ö5ï-ýZsÚ²y±c¬ÞÏ z%n4„Ûònc©ó^Û2<àH²Ãì6ÚÛqç:VîÐÙ zè¼;[Ö]*’p8pd¹eñEÂúô5íÄÖÓ÷!5]2¡ði-…g9L¯ZJå•ëz)ÊfTV\zÙw4wÊ8åI«=¶D-¼xìA®m_NÏ{yŸ­×ÛÉõˆŸw2gsîdήÑÛÿ÷ÑÖF’Rå½``™u>rKس ^%—.þú8tÿ¢6{Jת¿àO„ µ¢Ñh¹rssW­ZÍ%,,˜LÚ­œPéúÆÀ³J˜µÀ¯ê'$$T2Ü0==}öìÙáá᛹deeçÎëææö 7¤(÷™¸Šó°óß%]‹?t ȵ÷©gonî YÜ.ú9ã3“(Ö4LŒ]ÅÉ—ÂÒB œ"¡v…ïžr"¤dk%¡ Ý ™Ö2r?-¥4×Qd5¥k=×·n€¦·j#S¾º”:'š½Oûü-–nêI!§ìª_äyz6÷Q UÙFŒœ,üðâCÕç2¤[KRC€.ÌldA z1ibÒfT¦rgTf•õaç< [ã½ùhÜí§_jœÌ¹Ò{!bè:A=xÍî]ÿÍßd(˜ÿèð¡gÄp˨š:þÀx   °Ÿëþýû3f̈÷âÒÔÔ\·nͯ4Ü" ÛÖØŽóoŠçØ)ì ò‰]z\Û@‰\J>ûßÇ9:²5T5/él›¶ëÚêû“R-“³+NL¥ÓêxÁ´ZMS S«l•Bcp~šÄxT‰Ð4qqÖ”Ú¦A®»j‹ckQ-›¢PYPC^Š:fT.|4Ô`êyºÑŒ5ûwtÓ”¦S¾\r3v©8™så÷B ý׎k¼n»ºÂ°û“ƒ‡_0L}lTñ \8Àwá|}™üj'ñá§ÐÕÕ=wîçFll¬§§çÇíìì¨Tª¡¡¡¿¿÷îÝ~•Øù//DD]-4›>N¯òx<ªT·!è;ÿ}Ÿþ™o¨ÃùÀ W‡<ê©S5$³2Ï„¾%bö“6oJ]ødÛÈò*9å ›|›Â¥ðÝã B„TÔÄÐÂÝ -¤¿úT>éqчØ„O^I”V©1C®"…$çŠéöìU½é•¦Í)èEòë ±¾¤¿Êø*¨ */Xço"º„Š$!i¯3 ){٠Ҥ׸2C¶Ž‚êš+å;_ÌÂçGÎçó<å?\¶l2çuOæÌhíànBݼCÜÏõß´¬§Æßvc†µU£d½ú/.<äDR¾¼ý彊“–ºó‘ċøUýÇ|˜A†šRÅ3^ˆÜ~ã#]Ï-&Ê­rÓ4;92Ðïi¥Ž]ûñÊuwüfh9orÛj0ÑÂ9s‰cWÙ¢W »—p'^_ÿ$À ÙB*·ƒ¯|Öª¡ã²æé&_ð8zÕ¬ø"q»Å#Õ«ÜéêÌÝf´Ö½¯õÛe.&Y÷.Ý’@µë·‚S˦©[Í·M4ux9Ǧ#5~÷êíi"ƒöºêš£Kpç:>°K§Ÿj;sËŽ~ž¸ºéo_xÄÁZqõt¥Âä·o<«"GÞ|®Þ·£Î‚èO«”RyFåïy1ùÕû›È®Ü}Äkiÿ5vÌ |·§ZŒS¸7)<ìœñˆ656PdúͲ—‰Ùë}˜(͘ÞC¬¦uàÏ„ MIHHh5WZZš——WDDD—¬¬ìüùó]]]äpCªt?¿kÍ·¬ßrøTÈŠˆOEÅ äÚÚ,\äåáh UÒrI“³ÜxõéÀ€åkwEï]q&˜»PP¹³™ëf¹.fÊÕ¸½Ü·¨êDb"öë Є"aâ—¯1wáfÇ}ÙlÂëÐÖÞho'ýL\ÿŠ ó 9ùyHÀ±>Qîómç½Ì#üÊFwn󳑯Þ×€"ÚsUÂUÍÅ‹6î˜f»šóÒˆª÷ºòøJÏAÜ«ÂIöóO<«î1Ïý”ˆ|B•lkáyØoÙÈ’Vb>­q+'vÚ±}šÃ¡¾7T Є_wÎÉØÂ©žþÓGn$âm¬fîŒî¨?46¯°úNRë(¨j)•gTþ®SÔØ7Ú÷óäU~Ný6M³q‹c„Ã^žq96Ïê¯O'½jyZw׿T÷nx¥9Á¥SMWåûó°Ùì©ææœÌÍ]¨¤°  †Áð#!@À¡¨¨xëîÝ»3fÌHHHððððôô,nhmmýc†R„´¬fYÕ:EpÙjêçì8§xš¼]½U&&ê.;we¶vÕ@ÌÐô¸Ëöà¡xñ¡gY•¾Åh²Æî»ãÝw×PéÙ#êßdí[ «»Þb»roö»0jsý›¢Jºžr ¬ùQ†‚ÙœC5LƒÌ-JÉ6è®mPm¦+Z,²XZq‘Eþ·×¦<é:{R ªVJ•'òöbJÚ]¨(¨’==êMæY>™3{l»ÆÓÛ`ƸÆMíþ›QQQyùòåÃë×›»"P3--­æ®Â~,==½¸¸8Î'NÌž=ûáǶ¶¶T*µ[·nþþþœÿ›·z4EËi¦ŒÄóþ›.O ì#þ[õreæq#àoµW?Qáó^þ/Dm6ŽÒøe.ÓØœ®^½zñâEŒ{ù5Ñh4SSÓæ®Å~’A\L&388xÙ²e‰‰‰Ý»wçããÝLYH±¯µ±BK?6²>?¾pö±ໄ´ÑSÂÅ‹yTvõüå» {½Wœ#&[×[ÉâòÅäääìììš»¿„–þ%- F›Ì•››Ë‰ÑÛ·oä)n(&ö³k w]væ8ÍeæÆ ¯¿‚ôæ%Xµü]”1ÝfÁC"ÕÃkÓDÌþÁ#öÇó^–£/²ÅÚÛo84¹–‰ÖàÖÒ¿$ ¥ZËõæÍ///N†öç’••]°`ÁôéÓâ소Vƒ|N òùYÅý|mç?`ÏoîZ´T¹Qñ¬QÍ] ø…!@@3SRR:tèçÆ;wfΜ™0kÖ,--­ƒ6w*A€€_…¾¾þ·á†žžžIIIÁÁÁÐð«A€€_NÉpC6Ws× *høEQ¸š»U!@ð€ÐÐb°Ùì%K–HJJ6wE~uoß¾mî*üΠ ePUU}ðàATTTsW ÅhݺusWà÷„ -C||ü¡C‡rss›»"-ƒˆˆÈèÑ£›»¿'hh¤¥¥g̘Ñܵ@€à4 x€ Àh @ð€Ð<@€à4 x€ Àh @ð€Ð<@€à4 x€ Àh @ð€Ð<@€à4 x€ Àh @ð€Ð<@€à4 x€ Àh @ð€Ð<@€à4 x€ Àh @ð€Ð<@€à4 x€ Àh @ð€Ð<@€à4 x€ Àƒß-@§¦¦ÊÊÊ6w-šR^^çAAÁæ®@SÊÊÊjî*4Òï Y,Vfffs× é}ùò¥¹«ÅZv€þúõksWšFAAAsW¡AZv€ÎÈÈ p±¹š»:À³oqîÇÍ]—iÙZEE¥$:S©Th€–ˆ“žY,熂‚Bs×¥AZv€¦Ñht®‚‚>>>NŒnî4½’sšœ£\sW éq¢sQQ‘€€“Éd0Í]iÙÚÚÚºä÷JZZšœœ'O7wš^ff&›ÍÆ%†à·ÄÉÍoß¾URRâÜ6lXsW§AZv€ÖÖÖž;wns×þ -;@üdÐ<@€à4 x€ Àh @ð€Ð<@€à4 x€ Àh @ð€Ð<@€à4 x€ ÀƒÿLthͤø-ÇIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/database-edit-validate-phase.png0000664000175000017500000026621514770023131026507 0ustar vladimirvladimir‰PNG  IHDR‡ø·êÐ< pHYsÄÄ•+l?IDATxœì`ÔÈÇ¿$ë»uw§ oqŠ»»»;rÈár‡»înÅ‹)…B‘ … P·ív»–¼d·-Uh¡{ïæ÷öqi2™L2™ùÏ÷„EQ ÐX¿:@ rAªŒ@ „¶€T@ m©2@ ÚRe@ ´¤Ê@h H•Ð*#¡- UF B[@ªŒ@ „¶ðªüùóç>dffêêêbV¡IB â_ŒJ¥’Éd\.×ËËKOO¯\ç~§*ÿõ×_6l`±X8Ž_ @üÿA[ª”Z˜W­Z5qâÄrþª,‹ Ý Iòûb@ âÿ6gee•÷ÄïTe©TJÿ‹¾7…@ Dq4úH›Ëå=ñ;UYWW*#Q:"‘¨¼§TÀlÇǧR©~<*@ þ½¤¤¤=zôGLÖ Pe‚ Ö­[÷ãñ ñ¯æíÛ·´*ÿH h¾2@ ƒB¡øÁ*#¡- UF B[@ªŒ@ „¶€T@ m©2@ ÚRe@ ´¤Ê@h H•Ð*#¡- UF B[@ªŒ@ „¶€T@ m©2@ ÚRe@ ´¤Ê@h H•Ð*#¡- UF B[@ªŒ@ „¶€T@ m©2@ ÚRåÿ8äó‹›ç­=-ÅØÒ«ýò¥£Œð_.mEžõùq`HÆÆ £ÿÆp×ÚÎü_®Š'5îuðë8 ïSs§,¡—w â¢T¾{t/2SÉ"pÀ")‘c£Ú?£v"}ûÅGŒ 4OŽâ[Ô«íÊUo#ß©ò@Ñ•ò¯NÂ;Ôh5eº]èÝ“ ïM°M4ŸOúÕ©Ò^YŸ/Ür#øeHè{)ópGm Ù2Úõ§\Œ¢«À7¬|ï«8>üÂÁÁ¡/Ÿ½ ÏRˆ\ÞxÒ§¶Þ$€Œ~ä¿ù½ÁO?¤È˜v³$– ~ FíD•ôæè޾ŠË`þ¶î¶ÛZ€*[D™@/Ê€ÁÑæX¯ëß ÆÚ8»×ômкC.-ªéqÙ´­ðO$¯lè™;76w®QI°ïϽI2 XÄ¿¿©A“5ÓSgÅërÂ_tù霖î_$4¯¾xÏÙÅ×Ö¶j1ù ­š´t„ÊŒü­[“¿®% XsiÛÄf¼y˜öb¡SÕùQ%´j=6èôFsná½yjY—%‹„Æ=of…6)IoW³ÓÚ½$óz{,:ÃÜi±0™×ú4œüƤՕÇçZX°¿%Ço ¿ t朳×ëMRÀüO{ù´¢ÃÀõòÒ“¨cîâÓ¨Eÿá#;ÕsñXùYͯÜuçñ®ýØ×£ÙÃhñ?’XÄÿH•ˆ¶{ßÜzõ!âÄÂé—>1{Lìjú5pc1U5¥”ËÄé)Ÿbß½ztšþý½fˆêw8tÄè-<µH™«ŒÔæú±ü§ž ö ¼}ùÊ—™ô¡JmwkÃbÆ'}ßxv܃€«<>F…²¨ði¸lñškqôƾ™3v¹åg+úÞ[`pŸpó¾oHXø³¿ÿ¨¶»ngΪãæàæáiÂ-v†·³éaýÑ‘á¯÷lYq58Á£Óœé½k»{zWû>/»BA–öj¯zµœö†6zã.·l»9'xbñ䔌\ù¯xÙÌ»Î{R©sä»w¬^á¯iâ™´éׯR(²ŒäÏa!®ÝDÿŽ gÌ[4m`ÃBv?ýè´ºÕÐR*ÿ"K·Fô.€GsUÙÕwàÎýã$©V:R©çH¥âÌÄÛû–ŽœPY÷Nnzpqß–n‹.íŸhTq)ùð¦W$˜‰SµNÕš·h¼W£ÊüŽc—.è[ÂS"¸9O&è×ÝQ!5(†³ ½…³¾ÒcZÆübëÛÕmNÿ:TV^©5)ޓéí×¹£—Ni§ðõ­j5´ªQÍåÜ_SÜþ:°¨µ°ÜwQ&pœ"é „ÄW;ñÿ×=Œ ŒªÔiLÿj§ù穲Ûâý{ª‘¤ŠT)ä2©4+äøïGïNº3pǘ̓ÛÇûiUkño©rÅðÅœÀ0œ©¯Ôe'›Ëê÷w ï¸Ñƒý:ì I%sÄLꨯwmÝ þbeò˦.Ut&_hSÿÿƒJ¥ÊÏ‚Íár¹%j!×½‰9l©U®×õ·QGƒO‡g6;ßצ¨¡LÉSFT5~9åùÃáUË­[—¥u~oòHºóʃN^-¿>îñÖ3a`4dÃÏ’d÷çÕí¶á³A­¿ýÇþ¢ªD¾¨÷@ó‹aóÚü¼kô0åÇ úÇb󢯣vÌÉi7ù”Jß5káÞõës~^Jÿ*ÿƒÖûûé Ç®ÍçŸ}C—òûG ®ÝäH»‰2öÙÞ‡éЉ]¡vòÐë&r÷¥ªdŸtÉ”úÄõ\Ö]yºZU²ñ˜±ëØ[¨Ã+w¹YTkÝÆëññP È‹«ÅMmiýµàŠc³ÿ̧ísê–÷BåBXvHüìŸz‰o»{ã=pìô+…Ð¯Ç ×•§žÎÉíÙçbo qú…‰Aü€TùŸ…°š»kã™;í‚Ó¤²£»Œìñ´IY»ãŠC^\¸J^ÉcLâ¿8ƒƒeÛ´u ^ÙÏÀpftOi ˜Òü¹þ3æe~__Kox·ËN‡Ê”ŸöM»¼ñH«RØþçœÇ`оGk­œ²…³*ªÞ¹:cN"À/–AKª"ƒçð™Þ¼} *#~ ¤Êÿ8F~ÇçÔršz‡Ù¦‚×­}ÑdF•b”âôŒ,I¶\¡ÂX‘®¡¡nI‚µaî=æ¿§t9Qæ¤ÓQIe*EÓ34Öá}­ÇÙlµS=;=)-3[ „@×ÐDÿ³W¤âôÌ,‰L® 0_¤gb¨óᑉS“3²”*`qy"==á×Z% IZRšX®Â¸‘±‰Á7‡ù~/‚Y—‚¾@–š˜J?F#¸\¾Žž!W K?Ge¹.’xxÒEfFÎùž†˜eÁÍÇm¸¤ ·N\¾)lI)òC³vË@8²]'R(ª¬ôt±D*W(1‚#Ô50Òû‰ú-ËJKMË”$¡îÏ1æ¿~1…Tœ‘)–Ê$…qø"#SÃ’máŒ+¤ÑÿÅÙ_·•ÉŒäÄ ‰ ¶P×ШĢôCˆìò_̘Hº¹]ôî Ž0Te'%¦f+(®±‰þWƒ*+#]œUÖ ’¤%¦‰¥*úÍâñutõE_uÆH3’S3%JŠà‰tM uQG¸¶Tùà8qyý©uÕz aû§ÿišW2r’߬˜>óNTRrRRrZFŽLIðtLLM­}ç­[^Ï&W•â'wlܾsgºúÏ€©íÚ³U…sô§í9Ö”©­©wÎüktrjR"­¯Yr%ðt ÌLÍÝöß´j„a)ãÞ»€½—ì~›˜˜¡Qe} ÏÁsnXÜÙ.>)M¦ž@ÇÐØX—oÐbé¾y M‹„•ÆÜÿcæâ€71ññIÙJL¨gdfjV§ëÄÅS;ýȜق”iÈ•~lÅìÍž&%$¤dHT@0Âb`Hä|,êð~¨^¿Îæó˜#¤ºµ÷ì˜çh  YüÅ=[¶lßöQðÉâ­ÿ‘E*‰¡ûü{Y•1½^ëæÔ¸0ñ!½…E®ßøÇ ß’Z)oÿù ŽoàÖ¡Kíâ®Bü~ÍÔ)WÓ““’S3hå#¸"cSSKÛ3WÿÙÌE·ŒIy¼w‡¥ï ôD|.A)³ñš^^áS§ižÕ“qêà¹Pà:×íxò U`¶ÿƒ‰~_„<õî:ç&“ÒT`äÙdØÄQ6BeÔó€#û¯¾TªÄ¯3ЍrüõõêOŽRaºf®=‡M÷6Ç‚.K›—ƒ<3æôÞ3H¶}íÇ·ò´ÕÿøøäÒÇÞ¼xzã~ØÝ7—뛕ɔ´m·~»ðºÿ¡%‹¾¢ÿı¤bArn.ïÛtÖ)æ(nÖ¬ŸF ËLúü1âù¬’c•ßß±Á?xÖU{÷ר†+_úáÐ_óÎ>~ôøÚÓÈ+þËZh4ïÝÊöÍ~ÊeçÎwÎ|z'–y”Re-I^dÊÄU=ëN;õŽVE÷½'·©ÅÍ|³wõ®ë!O®ŸÙ?ðл=½hHGÒÞ¥å–:·Gñ*•`Ç,îVeÍÉ—8‹Í̾£c̼ºa¡Ce[ÆljUèˆcé zX ƒ>=>µdãÑ7/‚é ºýúrCó/Fóûýƒì¥7¬ë÷3¼ž>d†>¸|âÔýTŠÒ¯,¬ÊdàΑ-‡ïËܽî€Iclyþ{v_¹ýòþ-ÿóONŽ]ÕÆÃ*Û^:u2.¥€JQT•áÔ ø´¼µÕ¬ËôŽV‡Ã/vÉw$”gRQm´ª7V„ЕlÀíÓ‘I«˜²Íhµ¾||[&˜ýùgÉc’)Š’Èab@ü𯆹»ºtëY³èwÒnwšw7y[ó2%„oâÛ¦·¯Ë *'ôÐäŽjI¶j8Îÿ†Ê;Á—xm>”d.S$eÚ`BØuúy{zôh×µ¶÷©WÒ›ûç?ݸ-‡¾§ÙÔlHÛ;ÚtÈVZßê­»}gt/‘¨è¥Å};õÇDã÷®í—›w“çΜÖÀsս̽}jÕr~?¶VŒP§¢^†ˆS5Û]y z™Õuô¡Ó^vÖ–&"EFìÍk~_s6ƒØÍ½7OOc_(¾l9L¸ùym“¼¦:AUpó¾!íNçywS¶·Ðì–¿»ØaÂ~À8ͦî»ögÏÜÀS~_²Ç­Æà"iHº:£#ÉPgÜþ€ ½4Â>vòœã#½zlûxu·î^ïO ¶„€Tù× cîÀ¨2H¤YRº¡«Ê,ž®gÛiªê'³í7‚Våh€OÇO¤®ñÍ_C,Ë5Åäât€¼J6¯Š™YÕÔ¿caoo~Ä[çÓæDÄÕëò~}ŠwÇ陸5¬ëf”'"K¯óv5p;ö9ÎtXŽé9´ë“Æ_ 4ŽC£&uZT+·v³*îM™Tqûv„b¦—Z5ÄŸÞ b¬˜}Æ2á•zvn°®Ð¨©øYÃWD0‡îß:Ñ¢ÀŸßÖôXtáX<]4îʬ-Ë×Jf¥%'$$äÿMpùq'üiÉ-E¥{çþÁH2`m–o/"ÉÀÌ.e)ŒœU½É˯ i®oC)É,x¸ì÷@ãÓuºë†~aÙ@Eì ŽYß²ÐØ"ÅÇ£;®d³;naYòéGäÞzäÄZ…¡æ=»ô;L?ó”S§·5/¡±VÒRÆ*îé¿UmÛþu¨°$Óˆ³K펛{ywn2Z¿à>y ¯*§_=¢>}~›ÐÀÖæËó”È4/Ž2»ÐúYÌá´ÓÓ\§¯Â«³dY¿‚yg=|æÌýíf'Bòám»Ö/úa×íÅÃ;¢Õ9kß}ný’& ÌjñeRž£Gõ†Fõú£“~öøû1Ó¿øÜq¶Ð­õˆ‰…W65ë1¨R߃ᩧO'loa¦Þùâá½Øtã‹Zwh[0° j›no •©ÐaÝW3 “!Çò$Y Ñ}ÕŽºÛ<¸á²iMK;oZó襚E÷²Œr…Pœ@Ki9T¹>lœ|W-¼Ü¶kËÜkN¿œþGŸõ/–8GÝ ¤r$ò²N8Û4KÝæ…ú“yK¬í*[Bb*üæIª„•O–±|7¯J‘#‘dšÖkÎ&Yl³™‹g9‹J¬QI•JU¸²Åêôôƒcgé­´hº¥øE•ù&5Ž\ÚV4‚¤QåLqZS\U™i´ºœoºú#ÁùeJ|qç¹L&?ì†t±-³¨†_Ux²¬;·C)¯*¨ù׃Tù×@*óì\œÅ¿١#ÈkÂRå.4…OòK^Fã[4ëëºaYÔ‡ »²}¾:†XÈʳfò¯ÅÚÒV/mÑ?X?tK½£–òÝ›·)ô³Áí 8EëXÕ.3SPbƒã Kynë×gLG‡ü1;åêò?.”l¶¥|zñö sÈÐÙÎÁº¬c þ 8¶3{×Ýu÷(³ýhîÊ×SçxäÊ87îï4žwÏn•¿:™¹¾¼`‘DñÞ¿Ã5[­F5.Çy%¿š.»¼=ž’‡"Õ¬†uõ‹Z› u HUIã2ÄRˆÊ5×9réÄ©¦ P*rÒâ^Ý}Ãx8ö æ/_5­gµ².œ£—;³Ž*Ë4ù¼ ¢¾<$#}ºµ"“en?©–ëßm]‹Ž1È ©¼{#L³UÓۊ·t@H6©€ø¨d¨Rò Ä? Rå_ƒ,ósî–€§Ã/Z/| ¾ræÊÝW‘1)™ÙJ cQ o¿ó:Š«Ç/Ýyó93[N—èì´°´ïŠÈع: ”@%'¿Ï¡  ¶‹£ƒÏœ»ü:2!5S¦¢ØxÖuqÑ!Lº–>Íš›½¹–øx\Sû5»/^2oH3b3êSbœ˜©¦¨g;¦žS°šÃ0L–®1¹Aš!-ç°«5ë>a°o¡‹U úãÌ¥C§Å=ŽSo˜éÙþÐRÖó¨éǽÅ<%é¶i{g^¨)ÆOVo ¼]³¾vzßpD&¼¸yúò­Ððè$ú# É/sT„©¤ŠºýI¥ÞÒoêYú¤êR %ýOºü:6>UªPa |ýøy9 ïDinD¹ØÓx=Ó]0yŠŒ uò¤Êò-ÃðéðúÕþ$jµ1yÆÈŽ>ú‚ò-åCõ•›Jxyó´ÿíЈI¹ô¢X˜ªš:Z­zúQšðüx§Ê§]šX¾lN‡ªæÅJ_Ædh¶Î õ5Ï.raiz¶:A”,»b×>@|'H• ɹâ‹ôt¿øØÂ¯m5tb@¬‚0°ñt´Ôpqf Oqyó‰Rf\Ù2{Ø„ÍikÏÆÕÑÒˆÏea€É³¤¸f•ærš°ÔÓs%r‰2Ï—úÚö˜QÛnÇרÍÍÞP‡ÏÂ1—áÅ*Œg¶|ÕÆÐ~“n½øH’dÂã£Ã›.°ï3~Ú˜ÁÝ깚凔ˆÕ}LßÔ¶ª‹UÉ#±H¹›ówH%UÄ7Uíä„/Q‰¤‰áêo ‚oÈ×6¯Vcý<*0+3'=\æ7°=mgG®¹ð # ;jÿ•YÝïïü=vè8ÿÈlеªìl¥'äÑ/‰+rümf|<©é”Ñ·7,GIJÄгGOßužƒ»«™¡‡YÒ†”ËË7-äI)¹/ Û©J5knÉÖhŽ~UnyûQ}ïKîU§²™ r>ë'¼îì;lܥɷ3È¢±ÿŽYF®~›®T(Þøoêè¿ ÌªO˜:epïŽÞÖy„”§Ë4cÞq 'w7SQÉ­‚ïlöýë!*¤Ê¿å«›¹ÃެLÌ s«é;-W¦R ßfÙmÃ]­Œò<¸ÁÞXò\àéÆm&_¥‹_“'þžÞÒÖ(·÷,õÕW¯Éß‘d2WÊ ŒÈõO¿;Õܯ{p ^½¯^æëj+̵’zÚOË. r·«7«\½¸oç¦7Õ.µì‡VŒ;¶yùà-7·÷ÍuGS¹U(VµÛ´›«‡~GJ˰éÙg Ý‹:91¨rmqœUÌ®¸ßè»È/P²´Ø §ï´ßðó‹;‚MÿPúÒRò }›ÎOPÒ÷=ïîîqÖ&yªôªæõ¸¢§R(r›eìrÔ/’¸Yý[®¹ú Œœ×ü}´¿_e#¡¦uAmÜ|ìžå0—™wH\8ñÔµ‘é”e‹8~–óD¸Ø·éÜx%šÎ½·k¼‡M~½öÁ< oÒzî“®\8µkûÖKÏâ™] Áëë·{Çôæùi†£PyÌ$Ú.ؽµ³G±hÚRå_ìö<‡”c•湦MÊÜñ»R™âã¾ßo• ¶âeåt´½˜¥–dàuܺ¼kÁáL92Å÷õædi†™‚_ôÛå'¶ïAK2èÿþÇŒf^….Rš-Î2rlÞ{<ýÛ¯ˆ?±fÞ„åûãÓr”â¸ýjU©›>NÝçÅãiܯTZVm’~î$J¾§OŸ6Eª(L³â(‹—Û—¬e}‡kágÃÒ¯×{¨KàöºisðÜùc><¹$8ó7 (½H§/¶;1;wížQͶ`7dy_°¯Âæñrn’,EYOŠ =ì*óÙµ:M' o_½ÀŒ%©¢¼~f®Ž(×’M¿¢&8°øknç&cé´]ñš Ú5£š]ÁY[¥fкr—Qôo¾"í펅S§n¾*“+³>¿^Ò±AÝèÍ ™v¥€¥™ƒ®ø”’YJ4-©ò/àÝ–yÝxúƶË̓ôçWÔö¥m#ý3ÏÂNÜÐløô©ôC}!5ö%#NnlíÅqEÖÓ¨P¦YT¶-:®óÛ°Í»ý¶½Û„)s:·Zr9š®¶Ï?7f_0CC >­ù@Å$$)èKþƒK¼ú{îÊ—n›Öô§M!¡™;ÎÓörªäc¦ô~ÚRŸß Îi×~è‚=3Sä ¹~òø ¿c«¢ öœéî¥+8ìúgõ Ë:>F?ósº––;rŸÄÉ»—É/zV³ð™¹wóžDlánwo”òi˜Jÿê¥v‘~C“Aµê•Û™Ì6p³æâ˜·z·êz$$UžyêÈó¦c¼1àÛæ61C£Ò+6ɈŸRåœô{Wæ.¹lÐgï8·¼ý2q–¦ÃW§¤EûÊ…8QÓ¢çVÔàaùˋ̀3 §Ü×c^R%•©'‰²Y\î·Ó+—| ‹%=Ýl ‰ÏmÖ¶íW]Z>‘ƒ"æYt “koã /€ŒlH}ðêSŽBóéaêûŽfݨþÃÀª¶=oHˆú˜¨´±Òº’âм­ÙÊ‹±©ï'·è-¡„ãÆ¶ýšžÉ³³4ݽû™^y̱•q•é&Qž8±Ò»èTïQd%j6xºßXt½ ˜v¨ÃÛ~ŽéL}¼û*tíúÃþ#ÐD©} B~Y2(%6<…gYɤ°;ݼñÂSºþž R¥'¼W‚7¸¾ž¹Nü÷ÇïH–¶ú‰ßöDTZW×üŸ#OÙ4mÆÃÏê>Kó.wþîðåOWW3òc\² ô Ùˆ¥¬>•··¨‡OÇ\32+'ü­Z”µâË[•‰˜}›ƒ™ÿ²ô:h©¶øøýPŸÅÖlúŽ™WÕ*K^ºœp½V q½¸ Þ”$g°¬}º4*6¹ ¡.¡~Æ ñÉ Ò²¢b¿Æë7¦ö”ÁWé­÷k¦Í¿R³ˆe^ÒÅ8:¹ƒ‡Scb(°-/U¡òö“Åžg“©=àÜnz#ëÚ”#Ñ]{UÔÚš?:ƒðÜ J‘«¬…ßÈ çg–ö¾ß8ñÈ "ûõôŒt4$ÎäŽÅwíÖÜhúQf¾aäòY·g¬oTQ É#~ H•+ ÇK{¦éo¯.š5gã™'ŒElTÿ°ÿV¯‚µ•ž[-΋d)¤Ÿþmï«“ÃóÔH•~{ß¾¨’"Ô5ËÕÂG—Þ@Ëz_87¯ žÐ¡ËÖ>1½NÞ7-’Þž9xæë>, ÃÙEœ ÊwSzLû¤Nâ¸C;Ü5;ÙüJæND¨äï7º4¬aþ°,êíÕã!Ң݉8ÁÎz}zïë­Ó«²„rÞŸ¢vÚ¹tì;]¿ÎŸ}|æ]£«¡³C:N¡¬ä[¼J*Ï2•¹w†ÄWNÉŽŽÿ¬Âœ-4ÜÀqdçÇžŸ¥[«Ç ³1>5ɯ£^)):œí›ˆŒs­”§×ßBE´6[°kí´®¦ÌµÉWgwé2Ÿq\zÍÞ1ôËPuæžÞl3êLQk‡6ܳػcÛ•,uâ䘨ˆÇwƒ°Ñ‹Ëâgq¹yR¬úôîupp‰þpJ‘#~xâPŒtLòWNcûÍÛþÇùó‘ôtj+÷ •vhU×”§øzëҕ׉¹ªŒ… ëË*¬Âß¶i×T8þ,º×ÂMleŸžŒ¹MšÚGïûªCnã¥3íý– SÑuΨoy&MgNªµv0ÝÎ /M­o¹«Q‹šÖq¯ï%«TÁ3Æã‹ípf±sý+F°Š==Vž=W´-ê¼ôĶ» GFÉ!ôø<ç ;š÷èìëlªÇ¿||÷Öýçi¹íñü­Ý[ÕrûÝÿ­")䨗ÍËö­ëè*’žÞ¾–¦°U¯¿Ek‹S(u¦ê™P›€’žçÑ#~f7ɧˆØtóI F™bÆ¿ïØw®IïÈtˆ¼·U¥CŽu»tlXÙO¦ÅÇF¼}ôøEãWþžá[–‘`œ/ ÕbÏç+°Dpv|ºvßüõÕÍIÀ–ɽÇì£_M£«Ç6Ê/"Xç÷Ƽvܤ„ôÇSZUZæÙ¸k«z¶†Üì´„÷oŸ>¸äºîã­Ú6|â¿ Rå"xý)Ÿ„†„¦Èòöø/­á¿´xH¶ÐÀÂÞµš­{Ï© ǵv/16ë1þ»‚êÍ8ñ2Y,‹yveÏ3Ð3w¨ÚiÆý#Ëï7Äߥ5s¿ërãq[o¬í”i¯%ÇFûÝýt×fùÍ‘ŽÜ—)Mô?\s{Ç–SmŸí3ÀÅñb£c±¾÷ïgvs¥cšW2oøÎ+Ï>ƾîß rAS—­cRÉ»N÷é›õ©^0µ‚3¯ÏÜwýõØ$qVtà¡¿yúîÕ[¯ÝuÈv¥Cí ka‚*§œÚµÈThUËÃéejÚñß»þýK$VN õÞ·g‘q!ÃE8òô'á¬!«NÞ‹Šûœö.hÏzMO<¦gjcëÖdã¼aß’dù¹ÅÃ7\zþ"äEžzf\8äàÂoœfjòed¦óŸ¤í¹îü³ØIQÁ—×3ß–Ð1µõh8e»L³8‹ÌÙ2ÊùÔšóŽ]m'½qðÜÕçO/‡¨§ž¥\ëÕ±ƒOÍêµZwÒ¢*Ó8°~æñ®ë¤e«‚öÍí¿tMm›÷˜÷#îãêÃV¹.ïæ1ÿ·jßl6èÄáG &z–˜)ûüòúÞ—ô½ØWi3öÚáõí±®Wd'l0£z£ÖÞØÒ?õuÀž}'Ÿ> ¸Üœ6½˳ukÖòk3ªGí€m[o¼ }~ï¼&fòúèÖ=šTõôj:p|s{F8,댸}YÙï·Mo"Þ'fÄøïYçOï%„¶µf=õ¤w—“t»-z ÛÚê·G Ñ3©¼i宓׾|— Ly}æÀkŒ§ïäâ:kíÎÑäfË>[âÇ×·ý«V»¥ëôñQû`-zŸ_¼¿íòIbù›ã‹¡‘¥O³Éšv˜¡w¯Ð@Þàa ï¿ûŸvëÈÊ[G˜l‘µk‡©Öü†$g<š4nñ­C¢òçÞöÀp3w7W×&ÇÏÕ¤‘N?·~ÃÙžD«Ç^ÄìÚYѨiëáƒz91Ï.þ½÷ôuÿcš !GÂ;tŸ4»­=Çtàñ#N8HgPÎçÐë{Cé ²«ÜzÌ•#ÞuÀº\¡3è$“A#×\ß:ÀÈʹº‡STbÒŠA V Ê¿4ËÌ®RëN¿];´ W ãØlºf:bô‘/b?%$¼¸¾ùÅuõ¶±•­­wßMË{ IÖ*ÿf5:L°ëÌ!ð¯ö×b¾PWßÐÌÒÆÎìë=:Ü!»4šù1] W giçäê`F·¥õ¶\;÷^FàE*sx®ù'tÛtßsÐã÷ɺÉ-Ô7¶±wr´PϾ5ôÚr>pTHh|Z6É2±wq³7‚2{ñIÿ1Œ©B*e`šû9GB`Öwîö¾såIãâ’RÒ2²sè$Ðæ ßÈÔÂÉÍͨ¤ïû5˜s*¨×³Ðwñ9*Š` L¬ì]Ýí…äÌ>u¦]›`>Z'à ¸¤ŽAëóÁOc¢c“RÒÅÙJIa8O¨onãàæh®6Š<@^¿e‡:MŽŽx›œ&V´¥„óu Ì­l,ÊP}àN¾ÆVéÆÂ¿ž5… TJ]÷/³}Õ§éÝt¥×Œ·ï?¦eåP°ùVŽ•<ìŒå)íëäP8sž\fn‚‘ñzn5›xÔi6`¼fJ7ŸJ¥Rº_ªî&ËnõxŸIÆ×1²²sr¶1ú‘‰;z6~§÷w[¶!<¬^[îùŒy—ª è×RÇÂÖÉÍÉ‚®¬Ö]?™ÃÒ¼`\G‚ùz‡žGí¦u[ šÀÒM©R*r ~`Ӻɘ[÷{…½f¾ý+£sgé[º¸y˜ŠðˆËfs*ERú6‡†Cû™wkt~ööC¦TI_ÞÐÌÊÅÝ~ñÈ”I—.µSÏI¢T*°/ÿµf_ jÿðM\šŠžŽ¡…c%{Ó|Û“ëÖéн6qaÑ2%r £0‚§olfëèleP†V²Íš÷Þ´ÇHœ(ðQÉ Ò1±*=ã(+ÏvC¼»ŒÌ}:JLÈÆ˜{ ø†U¶¯Ö´Ë,õAŠTÉs²E„ÆIÏê¹ùnÑ3ÈÑÕÉ’¾aëµ7ÎFJ™œ •R®#e^­Çߨ×+6îó¡ì,©Šbž°P×ÐÚÁÅÙ²„)ø tœðÊø¸ˆwÑS3³UL3˜%Ò7²²up²*Ûâúˆ¤Ê?„U½N]*8JŽSÕºNU‹î5õlÖÞ³¤àß½v£Mo\hR­n±]°ÞõZ•ðµ¹¼«›X9Ò¿²'×Ô¹šŸsÑ<»º‹¯Á¹zö•è_Ùã‘©]5Óï¨ÃòlÚµÄöٺѿ";9Fö~­í í²¨oÿmƒ•åX­~9žï7áè^y <'ö•}ì+ÝkèÖ´Cá[4uªÞÑ©zÑpy4éÞ­lÉ3tõ®ãZl·K£’1ZºÖ·,7ªÔºuiï f[Ù×¶ØíLµKeúW¦ÔA`ß¶½ý÷œMº–Z1X¹Õµ*ú6¤´ òëPì,®®‰³‡I±ò÷5 ­]è_yÎ@üÓ UF B[@ªŒ@ „¶€T@ m©2@ ÚRe@ ´¤Ê@h H•Ð*#¡- UF B[@ªŒ@ „¶€T@ m©2@ ÚRe@ ´¤Ê@h H•Ð*#¡- UF B[@ªŒ@ „¶€T@ m©2@ ÚRe@ ´¤Ê@h H•Ð*#¡- UF B[@ªŒ@ „¶€T@ m©2@ ÚRe@ ´¤Êñm222$ÙÙØ¯NÈ¿JWGG$ýêdük@ªŒ@ ß@¥R-œ;[òé—ƒêÌò!ÎÎ1w­ºtÅÊ_ è C ˆo@Q¤Ç/ö52Öåÿê´üËxŸ¾öýç_Šÿ]UV(>D+$†Q«5(‰Š×oUSs›«º1êıhΰ «ÙЦ·òÁlÿ)…¡Ï ¾*;G‘­P))¦‰aô;Lð8,!;Ï?ƒR®Ë”2õÔC ùl–—Eh^:R•–­P̹l.×€[Ì«C©’ÅrSp!# È4Inx—kø­ð TH*&8†qX,•*ÕœÍ$Œñ>Q•÷'A° •†ŸReÄ¿ŒËw­!`STîœ\LóuUÁyÖ1Rb¤RF)Ôžj6‡`sWW€†®FvbEaXløß—²ªmYÇK ÏVÐq­auâßâêÊÈØÅÄ@•»®b^C…,§ê&)Üÿäê'ˆ謴õ—ƒ†Iì­ Ìù,F‰³¥‘ŸS³mܯöòÒŠÉÁB=ÿýÖ¥PÏÅÏÅ©œYdB†ÈÚyy×Ê ²´>lJOßÐÉÞºª~+íÉM“­‘€ÍíêÖ¢Š[}½ô“>ì¼õôQ Xºû¼žZM¯pøŒ “-táø½‹{«*ŽDâÇ‹Ï_,¹Ÿì^»êÌvµ3ßx^—ñÑ-¥<ëàƒh‰™íO% ¤RðAyú·¶î\T ~H•ÿ¸–­íA³zU^}@©ÿªÒyp}`ÑûÜ¥$(*÷‡³ –µMosL©–‚Y“,áù‡¬îUø)“ ü&yã  ÷îRc©#¤ò. Þ ”q‡š-q ¹¯ß)“LÜvvc´ù­%mê B=¢")qzÊø‹ïä$ÙXdÀí:ß6iÓöhkK‘ftIf¦ÆwZëïý:óÍÂú–|ãáÍtßx÷)##6:ìFœ{g‹U·2yèÑø¬c=½…—ó k¦›–ñ)æíÕîVç ;¦/²šÞÂSÞcéæª—¾ô~róú•x ãn¼õmÝ`C!]2dâø—¢ßW©º®·%(èFF΂­$RäŸ ReÄ¿ûÊËJ«o¿hûg±€à!s&ôéÖĆ–`splæúêÊý§ó†$¶îçîVI×ÐLÈãâšÕÊðÒÇza,•Ä÷@áÏl “Ïœí×H—RÝ`T{°uut»U·äЕ:ùzdËnu'‘R® @‡Ð5µÜØÐÖóÄ‹ßn¸ð3•Mé,î¦?ähôžO-ls/AؽРJ¾3’oNÊÌw43’R: »ë;½7äcw+»üDE¾zì\gVJÀ„tõÕ0Ð| 4í\’Ò¯ä¾TÀcz€(¬°þÒqz¶¨jÆþÙî? ªmÿgÐuLö¹?®ß¡_m’_)ýåÚ¾ • Ã_-MN¼¾q2üÄÂXà ÜÍ›Ž¯[Ó‘Ð ÃF * ÙñKQ rn¡–ä‚°8í½ìh LHL û¤U÷¬ÄRBÁ%SIÌÃÛœ"*äudj£Z†ÌT¥AõÓoFÿy-ì]K;'µbR Ùº ´Uk'l*zy&¼wýÑ+®‡E´²wQ÷èP*ùÆ')+úÕLÛZjºE–æ 5n¨âP˜›“Õw®»‹(H•ÿgÐu ¯ñ_!ý_x<óâ—MI t}øøòIs~óÓÇÁQûgdénií¨ÿ+SŒø¿E•y5ÀI ‡—48ŠÑ6,-[L±²AÑ×Q «kðQ*ÎT€!ã ¢¤``'·U;Þ”u³>} žòéí žÅ_Fì­%ˆ(~Pg÷?·½þ0ëVC&|Úç·Al³%&ì]_iˆR_m¤"IþÉ UFüÿ‰ŒùúBŒÄ±Z£k‰E$3lT´ALbΨ3è»Ûßi_gýÅu3Â_d86ÔýÕiFüÿ©Ìà°ñÒgÂÓG©èŠ˜]Rœ`(HòK?.EzTrojôöÚÑo|Û¸³³w}îת‡ ”osPéêâÑÒøÿ±û¯êµõ¤ÃnÔ¢‡ ¡Öb*#þ¡u—Y!DIézÚëá¸ôᣫ”w'“ˆmO,ÇõÕWÈ)JEJ³ ÏŽ¬ƒÏJqÕ!?,¾; ^f*%¾bê9B.O %Y8…^E r²“Œ8\~ÑÅô {˜]»ûaÕƒŒ:O–ŠÝ#¼uh«¸´D`zúƒ<-üoøó~ú6½§‹2Ü#ªÓmPô¥<í©rQH%H²sÇúâðøÀUm KŒT2ãgb±A$‚‚ÛìtÈÐׇ"-^•$’܉³t¡ã Ï*PòHÈÓmauü8ðEÀ&A,MÓ8ßD'ƒÃ>¯ä ³Ò È&™$q ä'%‡Œ¼¨4Á ÅÉ]!dg1wJsY± Çÿ†ë—AÞê]ÌDJ¬@â1(:©˜")œCFžyŸÜ¹Aæ¼¹ø!µ{UÜ3UÑ×ß+uE¶Î… e Ë[L}¡_þ$‰B¡‰D,ÖÏ*­ê¦ŠÞiñ§Y8L±EÔ¾Årç­•pV±sK‹³„³*©TÊårñ¯™²?EKh¿œc‚³ÉVúDáæõ)Mj¬+070¤_À{៤`Á/ØùŒ$69 †™…§ Œ²{·t{7ánàNÉéƒGö4¯O`õhá<æÎçû÷ïtÊÈ4¬‡)¨PCT›Aª\i<\» ‡ÖÀ€ÁS¡A}¨j«žcƒÁ«på<ÜÌ€!] ]GÐÓ´l1àH¡[3&üÒ+ÐÎäùo<™±à V¡9ôëÞÍÁÇTdîQZÉo;!N mG@÷®`› çŽÃºcÀ5€ÖÍP@F*ÄÇA¢¶…¡ƒÁ‡"#“øLn wÐï/˜Ú$·sŠÖø”«ÐftöJ¤œIy¶5ôðe®N7¬Ÿ¾„•›!ô*\: Oc ~'0Vی̉ ¬5LiÎ?öÔË E~|ñ692”ob®í=,=¼ôpŠÂå’ q¡w3èÆNÀ¦‡¡œ\AÅ1Eh Ôc Ó3v­›äXIÏyª8*–Ûå·úž¶yÏÃåñq÷î%¿Ï±<èLh–›iíœ_Ú‘vüøñY³fµhÑ¢U«Vô¿FFFvlR`@X:4mmlº¤Ð÷Ïžd( Œ”«ärc†ö&ž l-D¤B…Ë>ÆÜ»‘"£ß’”IU´œêXº×·s4#”’¬§7bd$Á2°5®ækÎϯi0ÈŽŠŸ™ƒÑHkWÃÖÅIÀ´…Äi×?¥gÈä@˜WvªSKDíj­`zõêåààФI???Š¿€‚jÜ¡‘Wе§"ÃFUæ+¹Íd¦¹§XvîùÔ>öÆÆÃk˜Ý x¾=Üs¢3'¯‘Nײõ€e4¤™3§` …"ÁÚccÕÇ}CbÞ¹ù\p&˜S¾Ò® Ïµr§Ã÷~åZçœË·Â#~5H•‹"´†nÁ–„Që@Q|ì![Ý¥ë‡:íàéQ>zÕTO‡U‡'¸±ÄÞàðÖóhaÏ‹‹gÒ6GOè?øÊÆò/-]ZD?_ƒÐ, ž€‰ÍËkS“*ð‹ºC¶”bˆ>ïëüٌ¹°c"(Щd`Tyä<ð"™&7}(ý ‹ûõ6bÉ`¸©›•¾ƒE–•ÕalW§•dóÝëÛ»øØv¬ˆÕj`-®®JNX_m.—©rG\ã8‡Çæñ 4s(’mdZ­™aU¿JêkÁb±µ`lK||ü¾}û:$ iýX°`AåÊ•+*òð€g§÷Æs µµclXɶòÉô)á^}ºv5ø|aӃ󛃻¯mïë@qLÍë×—þ1.eYiÔÒÊäç”ûžýµîqã•]zÔ¸7ra¼¸mbÐþv•õY…¦­QÀ³2®ÝFxqÆék‘xó =\ìY¹ssºÞM©½£ozÏhQÙ…OýüY±¡¡¡çÎÛ²e ý`5j4cÆ ŸŠ¼EêÛ_R¿ÁŸ·+%%êÛÈ’O¿ždjÊÞ«O¶…RÓzÒaXýz¶Ì–œùשœ¡ÍfÔ1g–¿INXpðúo8»g·ïaJ×58pXlç±é† r¬{Oßñ! :8±‰¥b\Œ)º¥£Tæ†'°üðÝzùŽ{þ`n{'¶&<ÿž®bX,>›q ³sÃçÙ lCLµ@0Ž8•ÐTåŸRå •àÞÔî“ÞÂÒÓ°x'¬ù3(0œº íÆB¸¾.ÆC¦LÑ犼aŒ€ÑàÜ%5rS¡ÞVA³A Çù²_WîªVõ`Ì'¦hk'l‡Í+é†óu¸_9‘¾9—Íⲋì-‹%Ôû¹…"+++33“ \ m"æoÓš?5ÿæ&)Ï©«T*322N«qvvž4iRóæÍmmm ñ½‹b”J†ui ê^êËÀt»F¢üBp9"úQ`<®‘¥ž™áoNĄޮnyîµ´ºÅé±qcqØ–ºB3'WÄáþójmc­ÃÐÍÀøz<°èàbœÃrt›­:ýÑEAu÷7Ðø` ·óv†wõUõXdI¥å+wò]@ólåjΨ1445jT·nÝlllŒ¿#Îâ±p­¹ÓéܵçËwž.!Ù8ÐmIO;³ƒœ-8j£ã= íÛ7K®=ð8™¥Â€ÅÓiZ½züÐJf,’‘Øô÷C¾ M•l»t³²û¢ÖÎ"ãJ;úfײÒg'½{7ýò«Jãjì¸æ«¯ jÕp Ùç¡ûC_¦æ\Ýáà嶸‹Ž¡ËÎ>YÞ6":|rTÔoþ¡ÏÆÕ8[®¾<¸emvÔË!Ÿ+Û];vu¸»Ë’ö¦8 ñ†3O/D¦fÚ›¾¼ßü/Ãá-«wwÔAãÄ~6H•KF×üjÃë8ò†{3.—€-‹ Êh°eƒ"_±0ˆ¿Ï]`ØyÃÙgpü,T¥Û¿½mÎuî«ÏAÔ=Hì &yƇ,eÂgÀ²Ál7lÞ–©u P›*êîÎ…oF›©Âv my?» †u¡yåÜžuD…óüùó'NЖ¡J I’ª±,»Žç]½{ÿù£˜õ=8%ù6ÕCçT`cîÈ‚·â I%äM‹“¤DB;]~xjZl"imýÍÔPÆÞNZEì¾yæˆíÐ~6ÙOB¶_7špЙC©Êë  ÚåË—•åiK ‰DRdgjjêR5žžžô³mÕªUË–-´×€61סEÝ­òšÖ1ÝòÈo°ÈVΕ6»¹}ÂÕ4MHÐwØ=щéEÓìgfûa]üjjJ©‰“Óž .÷4õPû]‹„ïÔ´cS`ìè¸{‚sð$x4ìÝÏ£‘žnÿêŒïæ7׌ã`VaÒƒlåŸRå’! èÛ6ÀÝ#Ы*0È ‚ƒ8ÓT²/Á0®Ü‚—¾]@g¼ €¤N`\áÏ•VY{°Â .2¤`"RïÄás°\™ËqÜÀÙ]…ÀéP›¯.õä×Úd‘ctÑ£ÏÖ“‹P§’äŸH}5eË´iÓ¦ñãÇ—xH,‡„„²Ù¬ï¨,Yѭᦻñm¸*ß?t3.~¤«.QjT y¶¸\¶ú› ÅB©TR9‰ãl¯L É®3µQèíÓ¼kaî¥/;¶#ºùŠ6\•¢üJ•2  „¯x–‡ÃÉÉ)uèÄ«W¯š5kVîÔ”õíBEË¡¼”;§ sñ‡’!}bÑ~–r†gªƒâÉ+Cš?¤Ê%C—z0Ü vAØg¨açÏB“É`£‚ìÁÙð$&Uœu«˜ŠŠã]¹†ÕÛ m¡‰èê÷ ^œy´( H&Qd«x~“[´jmÁ"¿ÌJ}±¼w™£ÈÊ÷^õ;õv(U$^ʪE •\×Î=?í=úì@²ñ€¿j”MÎKàû&ôXxxxŒ=ºiÓ¦ÖÖÖ?e<6Q6*— ¥¾ t‡¿7ÃC>p=ÁBX¨è³9pg;tœ“Z«mYZ˜•°¨3\Ûû‚ÙW ¥"ÀÍ¡¬‰¡E7þ¤“àe¦ÆÛDÊ6¿ ›¤L=A‹{{ÁÖ{ð& êZ~OýFßrÓÁLº•2ãÀCoǯ†– Z}i›xܸq´`TdÔYYa’Ì—†Ï{©8È#®<¬Ó§&^ÐséÞ²Z¯¾v<›ÏÃTr•Š,4MGßÖa̺Z:,\("˜£ª¯ ¦QVxõI¼Zs»ãç>ò¬]\¬ˆR]¸?úÙšššÒ iÓ¦ÙØØü“—F Jջߠq/8:~[ 3ö§àš´© ;_Á’µ “¿àͺõ°ñ.¬lÒÒÖÏÁ #N܆9#¾vi,oéÚð °÷0¨è6øÓD¤Ãº›0ä8èsA®Yç„]§ÀÖq°-š *ýê%^KݯÌÌ¢$àÃËåp·L¨‰Ævüb0 ›ª¦Â#NŸðQà¸ôBu®¦I‰AúÝà?¼|s䵸f•ËY`‡Å² å%uÂâÁ±9J¥\ú‘V8¢=KmV³º!YpT6Ei> ù æâíß¿¿zõêÿøeˆ¯TùT® Ž&í-Ü*P+ñ„¸ˆ.P×$â\ÃÁ·X€;ë!½5ÅŒO˜¯žÆƒ³@ ¾ŠÑZ™Þ=ƒ,FuÇB=ê•Ë*¼ñð°*8›úŠdR_?\6&½{&Ö¾Eí2v;ÿ IFh!H•¿nÛÁû ”×Q7$™ G^‚Hæï€ñÝG0š—òV]{0zôÿ Ü?¨`[ ²"¡]#u÷°Zmió·Í€t˜=ÂHp°†å£ Ï,¨ÃÖµ3Èã gëÜi'"3ðé×{0c­•´$³!p l¹ º,øm.  :Lƒ@‹w@œ¸êÀÔ~жôª,9Þ ·‚ Í ¡÷0èØZVcl#V*ü¾Â?«+ÌéPh¦ÀkÓ÷p§Lvž2­ ¢è¨)Šù’2Uð¬BG™ I¹_¸eÎÍÝËLì)âÀðë02³˜rcê5;I•–.µòuTÙ²´”Ô»—3Ý&²Ó¥”¾SOš§ÒÅlŸj¬7wÓŸD51é±²b™9vâ䬸èL]3]>–ä\%—S”$ûóûtC¡¡žûi`R™üIœ  { Éøj IæÄzOU)0BÃÈŒâÌØ,¹ð̬OQú&"=ö¯|¬DTù(åÐh64Q€¬`û‡ns 7ÁTè´é)WûŠ)%ùš†€çM8TÈ€´…ÝJXà€œ%gF„Íß DÞF…T°¯Hxõ$•2לõÚÚ¾£ á8fÕçöþË+>Û±¿}ƒi“«£h0 AОíÓge ø­[„«wîΊÿíÜTéÃû¾9Œþtø!p«:yY„R çQpM#*÷®çætúó¯oÚ‡O˜¬6Ïð¼S­*µÏí_’4w|àk^šd)¤.öÕ›ÙçŸæi$Iz„zy„>QÓÍû‰è.¬ Ðjû­ì ¿š¿¡@ e! ýŸ}ý‚Q ž§|êúú<ëe ¦B‚UcÓ¤pkÃ]É€vmÎì‰\•ÕÙEYh°9?¿“DNË•vµµ²g´s~:½÷Íà®ûøUFJÉì¥Á#¾pÕk׉ÐᜠC‘bàÒ¢àÚ±þà#q+FïÞWeHWgN¬C’¶œ†÷   E±`0LÉÁªŒ±iMÎÁ |ׯ=¤jßÈ%×·_®;¨:ñô^póÌ(«5(*yË!þÚ=cú‰xä[ëŒTŸܺ§{‘ FÕ`Jë[w6ŸøþXµ¦=8𜄸øû)ïŽMüe©ŠÁ`0"X•1¶ ‘õ %ÙÁ½†jù¹ÊœþõVÅa ÷ M$c®FàâL‹Ûf‰¢¯ò¹¹9Ì ¥ÄB&$Æ×½9õÜŠI竬¬Ïœ<ºéšË¤Éî–tŽÁ¼‚VoÔ²â}LS”JF‹«\x.[ǰ—3<ËjŒ ’34ÕWÖÀªŒ±a(òá™»>õkÐH+»zËNß¹~9ºj=ï"ªJH•9iL‹z´ÙuÞ- C(mäòïn‚‘3r%“›œ¶ïÇóʾU¸='À‹-Ó<ëWîÐúÞÚÈË›~¥“¶'¶ÿ¾›Í±(c¬#Ù{üó©þ¾ÎJA>6«rxø’ä”ßvýè@úˆÖaLVꊫÚ]3º5+åh‹ƒ¿0ŒÍBÐDö™½Ùás<¥ Š`œ»wq95/þúáGuú{Z9Ž|ô:® YV—#x¿öñØšîqKìc'A·jĺµ§Ëáíªú\Q+@úâ{oX´ü¨éÍcÛN®:_}bçV„ÑFÖ¦c*&$©¹rªÃÚkCG½µ°žõOã/ ^¼Kp²ª‰ç­¦Ȫ4¤RÍÆÇî©ËÞ VeŒ­BДöÔ…“±Æ;7ü-ª¨¸QXIòÏ<èÙËC%ɘC/Ù¿ûw`djXN¯/²XÚ÷§î|dŒZB ¼AÇþ÷*_Žå)ºè §rï2@}éw¦e;K2ÆÊ7œz¤áí»„Ù‹ >ðªæ&»tüÈÍœ&1N外;*dƒ;Ö4§nÄ”-°*cl$Ê»~©1¹×Ø. sÚ\JJœû|õÒý÷/'ÖiæÇÒG‚b(Š@¾²¡XSCÒ 5†ç!åušÓ{âûKŸŒmeŠ"òòY1‹Ã³·³ @ÈþǪá…Vw!oœÍLº~=7¼Kqùƒ1[€ db^^Ÿ¿ž‘ã bUŠÁ »ÊX•16A i7ή¿­ò3ÜNÑ8ˆÙ«ÄðŒqw%Dí8¹û&Õº‡™11:ôDº}üà®TS}\Bàn¦Ü½–`ˆ:ûÐ1Ýλ²s^L‚`ÓÓnžJÊÍ„Ôô {£¤&¯‚7rIo¤ Á…·M²rDeEßAW1Þ=ó©dçSÕYNaÍÆX ‚i壸übÒÕ4¡‹xéwŒ¬s€¿+Å?'C¦,€Uc{£õ¨?ž$ M懫¦i•›cÏÙÄpÖÍH(°“Õ«Mý·@ I¹‚zr2!‘Q^-ßø°%²GH•LáØ¢MË}ýÆþ臼hΘœ¼ÖwS3…b>’4%W0o¶ù°Ÿx™’¡pDHŒÈÆíÂ{üwî?—Œ Wš_6Ÿâ:G(­?ž¹•¼Ý Öh¿ýu‚õ>[Ô#¹qñ_“ 6£Š~Šú§¶ [\gs4ÌËRqUÙÝÝ}Þ¼…ÖnÅë#55õÎ; 6´ÊÕÇŒë¿3˜¢¬t¿ ÈS§8–û¯uÚ¥L÷n~~ÅŽ§—”ˆˆ†F}‰ú=ÖúëH¹éÖÜ4V×›í?àM+6ƒ± W•K•7׬YÓÚ )ààÁƒ ,@ÿZåêÕLXåÒå•J5æÑÖnƒ± X•K…+W® g¥í¨2re8pèСœœÔ0k7Sf˜1cF³fÍZ¶liA›QQQè†DÝV ÚÄ`Ê X•KŠ¢¸gå5²,ËnÙ²üöÛo'N´vs0e$Ÿ¯¶¸ï?@·â½{÷,X`Y³Lù«r© P(ôz½µ[QÀÉ“'¾qãF\\\Þ/Çæ.Øxréñ»—³A*—91ËóIWª4£Gv^òüŠœAwââïŽÄ\KÕ²âç N-ê†Nhìã'§€ÕüºõôïÇnN7—ÖÁÞS{7¬§*ô™2g¯=ó÷Ù{—³ÀÍÛ£u¿Ýë„È UÐ&OZvvãÅû1zP©jÉ1¯gg×÷º6WÛEVD™ý§S–ÌJ$' ïù]ç§þ0áQ|Ü¢½×vÇdÄk8Ô`B-—úûú|?°qpú•7W\Ù•êÕ «öóІMí‚ÔÌ]´sò™Ww—Žõë-é e±0¿:X•K‹¯‘yeî˜(\‚…×®] ·V“0e†aXÖba³~üñÇücä(Ÿ:uª «2­7°óˆª•óLî7*@Ó䤤ÏÛÜ~vÚ©ï»FHE/’7jgþ¶ñË(§UïµZâÌ0H3Ù„gýµ§Ê‘€SS›×”ÉÇôí04D+û1aöèŽï©ŠúÊûOÞî2$x­Ï’¬Å»ör¢Å …‘¹~ÿ^~;W7øÛðïœÁ­åb Îì´Ôo×웼xcÜÃæÕ` ûÄlTÔŸÒJM¥ÑËÎ?œÝÀ™)lŠ¢.ÙßpEl¿Þo¬éíälŠoÐ=x”4sùÁ+™õC=ÃþýȽõ×›Nfîÿ¤­“F':ÖC–Ô½ý’.A ’,É%«r©@Q”Å÷“¼gÏžMNN.R8þüåË—[£9˜2†L&³à\̯¿þZøtåÊ•C‡µ”ñ׎8>¬1%3Y0 ¥$\Ý<‡4úuå½U¸ˆJ$ôÝËG¿¼¬ûß´VoU’"=Ól€‡‡÷ìáíÏ}²µÕúà”~ùvôHn Oï¨Ì¿ 3°À>=' hÙÇ-¡ÅµƒcϪ^?\H?/j9BU~˹øz¾›Üj×¹5»©''(H¿ÞiÅ­°¶Ío[I .žÔ“òõöݺR*!ˆ±U œ(ñ¯1pNèÀ˜óîO»µÍÛÿÙÈ8°"—¬Ê¥‚íìW.V}W¬XUóš9~üxZZZá’hµZ¹\þ¬·”=H’g‘’IB”„i>XÿÏ–hP òdž”+A©rŒ“?zúØ[•š0Ï2÷²R†³µìÌÝw9¥ÓÈÆOdŽâúí÷õ[)Cœêxï:2aOr·~î`05Žo>ÿäUñ*šïƒãÃ5{…þ ’"!=uúš£DƒæËEI¶ ?¤€U¹TP(æ…¦Vç›o¾™1crÜ÷ìÙƒú -[¶DÝäý”ñ…6˜×º“Ÿky5öíÛ§ÑhŠ.]ºtܸq±o=¥\ J)BÚëS6Ü÷¯Ûx¤ ! ›¹+ Pn÷tÆ0’rW¡`êþ$¾‰e‘f )9k3‰TXÐj´92—Õ“Ûôö$ÀŸAœÌI‹½K;UW’öŒWcz}ä‰Ý{7"Mb+h7Þ1€Bæå¨(Æ|‘‰ ‚`“ã:þ¸ã„ÞçÂHàleqk9«r9'þ8::Z*•6mÚÔºíÁ”-,µ_ùÄùë¼ ³hÑ¢²¯Êú‘_­G‚F/N¨véÚnWç@…¨Éâr:rA%Åmx ™˜¬EH7XÊÅDv”³ÞíÔQ.Ž1?LHZ´ítŸ/WŒÖkqc'‚7]…!ϸàÜÚž&Fýf ç†Ø‹Ž¥7j®l ì#­¨ 2És7h¬6}Ø'd¦ÄLÞuoMG?¬%–’¥‚egã,‚^¯wpp°v+0e Kåƒ"¢}ûö-[¶D###}||üüüÐ=‰Nsss•JeÉ/a=$ó?îû^%ÅÕã'Ãÿ¸’£ L#½P€+Z®˜LÇ‚+Žu“žrž•â“0½ôª]´á –ÛËÅ2WG»%5¼œ>ýkΟ»ZúôéçÈ7µ>=˜­nöpöއ¨²µàÔ¹+‰Mš¸“”7zo6èÄíç¸ïÅ|³Óoíäo6Îý÷ ¿GÏïØç„cJVåRa[SeAðeÌËb©ýʨŸúù矛³²²† f;¹[,ƒ@ToZÿçS7ÇDžü£–Û»!J“*;ô †rÒxÁõÉD›Ç>ÊÈðêèF¯»èÒ3ÈìBäÏØfIÚ”´x•c¬YÁ$Ò‚¸ hE¯JÒ9w“sÁÇélìÞó|ëzÃ×¼¥XZÃ=%jëÂèGç’t<$@(û‡Ê~< IÎû"Wãžå IÞ„³À0.Íýd`¤¿ÝáÂÜ-ß/ÝäîðÖÇA$^íUr°*— ¶³_9ôsµÁVa* ÇÙÈKaŽ´œdôØŽÛúÞš‹>mêO ` :õ¬-™{ñ§ÛšÅuÔ«®H23%~ßM¶f׈ê |qÏaRسåÄÁí~xÆÏ–æÖ¯>x§_ïÿy>K —“Œ@3®*SpcÖôÙ£‡wjX‰Ì[’MÓ_õõ_øK좛i¼<À(DôhXûÀîÅçcÞ«]ÛŽ,ä…S̹£»OÈŒ¯“?ª!ˆƒâ‚ stÿ{Pݰ…g¦ÿ¶³É”Žíq׿¤`U.”J¥ìWÎy<åj±+æµ ‘H,¸_9ŸrÑA$Чc/%ÅÕ^2 Èhг ñ^8<¬ÑÒkþô¼ñ~(äê½ë¬ìÜÑvõæ3šúÊQ}ÎpûÒµwVÕÕªÙÁŒ ³P ;J“‚¢^‹&ck¼6TaZË-•Ø™.¤’KAÁäUÈIZ÷ˆë!.Ì"A&UKˆ¼–ÈͯæìÝ}vÒm}¥ðZ]• ¡Œ1 Gì*ß “FŸ'·œàñÆ ?cWï¸Ú)ÀÙhE¥-“·ÿùL‹ßؽk6ñQ‰¾A{ûÖ­oöÆ7íMIœ¡ ô Rvr8‰³sͺg×ÿýf¯eç¯Nmâ"qx¯’€U¹T°xô`‹€G°1/KiDtÏÍ͵³³³¬M+ è~Y¹kúÁûèpøìµ¿†øÍÞ¢¾œ¯^{xPì‚㑪›7Ö|ئ««´_¯ÎmëÇ|¹õT¥µ;29qú6ØÏgÌ€¾£k8H@Œ6wÙŽyÇÅ”n£g,CäGåDÿ“­Bb¬OŸ¼dËŠ ➎î!ž¨ *hýá覛©¨´ù˜ŸwxW7÷÷‡öø²‘»„áömØ3òplŒ^Þþ{ÝŒM›Ú‰=#Cü!k/ï×"y¾YiRú˜vu¿nêã[¹úµï*­ÛwaÒ¼¿/¥i7íëîÔ¬vΕ”ú¸‹m½|<ãàvr'i5¡’ipµÍÖ$çtÛzÞ}lô¸î ç´ð“þ×|8æ¿Àª\*ØÎ~å|xžÇªŒÁX BöΠ.ï 5¯Š2…§ÖE%¢Tó¿:ýÖtFÓÚfpò \0±ò‚ü]À¨²ñq jZùñÐn,nu! o[¿Ûû[º¸/ª£ÏåS,§ŠT0ÇÁ6ŠÃæF¢uŸŽÑýM‘´óÛ)¯ ¿§TóS“L>.!QõëÖ¢_ïÖSá¼9À5ªÚ‡çÖË[e† õ¦?ÐDמ݅þ”)Ì‹}å’€U¹T°µüʃÁ€G°1/KiÜÉ奃(FÔÿ{º\k(ZÆ#…~ª0¿>Ò6ý3$Äz<ûK „ÿ0þ±©O>³U¦´Æâf.4Ïð7PÀÆ–¸–Q°*c0˜gbñüÊFƒ#Ø`0Ï«r©`Sù•ÍpÇ0‹ì‡© Xj¿2ƒyA°*— 6•_Ù ÎÞˆyJcui?y0˜âÁ¿Ò¢\ìýÀTtJã6Öjµ*•Êâf1˜òVåRÁ%™çylÆÆA÷LiìñÃ1m0˜gU¹T°Á'~bl„2ºýx(ô z:ûæ?!ñsç%ÁªŒÁ`žIi|øž={òOÍzàââRrIÆYR^ ™Lf0}ú,_¾¼paÇŽKn_¿èá8ÎRÖfÍš•̲ìþýûkÔ¨a)㘂UÙÂÔ¯_ßÍÍ-))É|êééY½zuë6 ƒyY‚èÖ­ÛÊ•+ó“+£;944´ä–ÍiñèÑËbÁì„„„£G.YµjÕ|`㘒ƒUÙÂT©R%000_•Û¶mk#³h8g楨[·®‡‡G||¼ùÔÏÏ/((È"–ñ}ø  ®Œ¥|åÝ»wçææ.9wî\ff¦½½½EìcJVeËóÖ[o}úX·=µZ­ÕjKn‡eÙ]»v=-ðK—.4iRÉícJVeË3räÈ÷ßLƒ~5kÖ´vs ÀSz˜—bìØ±‘‘‘æãaÆYÄfÉwbù ÕèF Yn¢é!I®\¹ò;wòK¾ÿþ{óAZZZÉU÷l“ ªÊ n&&†͖ɬݎ2‹D¦µMD.«° þå¶=ë*¿!§é²1,L²|M•ÒàvÕâ‚@Ñä…È;{vû—UF̘1càÀE 5jäèèXBËÇálÛ¤âª2ê) €g¸J‚å¢åc^äî¨í啜i¦¢?ai†¼'…"ËÕ3­oß¾#FŒ(Õ«gÏž%·Ìó<Á¶Mð·‚Á`06 rg ðûï¿ç—Èd²† –Ü2^vg³`UÆ`0… ˆ®]»®X±"?„ˆ———E’w™7Žã©e«2ƒÁØ.õë×÷ôôŒ‹ËÛ0‚$Ù"»p9›«2ƒÁØ.ÞÞÞáááùª+ªII€À‚‘M­Ã`0¶ƒ½½½eÓÐ!=¶`ÒŒÁªl%`ãá÷ pó¸ô†imåA¢99À …*’ †Eë!* |GÁ”z ÃÃN˜2IJeł֨5àîd‰P(ï¿ÿ>vm+X•­„´Œš?¶‡#÷4…•þk | «×@`¡.¬€Ô:Fƒñ½àR2P¨êª² þåb¬A°Ñ±.»vñX’ãèvÓx”Ëaž .lÞ¼Y"ÁÑàÅEÝ 6jÛ¶­µRæÁªl=ày(xR± •A0è¤ÅÔà9 Äï†Ãè÷<¿6Sª]ÉÄw¾ëº¯¼bí¶”›6mÌ<òOóêÁ¼PÑrS3ÿ¼z«rÉÁªlä  “ʃN'ÎS4Heb‰N#žÊ—>€­V¯.GÁ;?À# Zù¯Jd 5õ ZqZÿ¸²DÚ” O½ ,8‘0öKøìíA“ FA'—Éó®Ë@§Çbyð¼^kD7s9»‘J$-CÜ{Ôö~bÚ©B¨ˆŽ–X»å¬Ê–!ã>Œ gS@áSfB·ªpí,œ 'Rà‹_ [5ø¬N† ïoÖîIa¾°¾7i˜¿;‰£Ð†4øõX X$ùrÈÊ‚*¦šR ,ß 9èúLèúû0õC8©…sÓaãgðÅnèæ)·áÓÿÁéûȇêíà³ñà!ÇÂŒy¬Î›m4ߟ¥T©¦ Œ9Úì\ž` Áê ‡Yã åQp1<ñØª× fœ‡~ûM7zŠeŒѰ_öBeGÐ>„Ñ]ûÊ„ƒÃRaBo'‡?ƒÁ Ÿ¦¬@Rü…_ŽÓÛUõ•ê¢îŸ¸'øeóZLƵ»¿|}!‘òiÕN‘ª‰»òHï2æëz>6&òÌ/KbªÖ"²Órî^Ïðz#|ääªJ–+¸Ã9.nßùy‹£'ßÁ³"ê„(µi‡¾ÞI‡NšZÕ×SZŽýi æ¥Àªlx$aðAmøú Ü a®s–¦Ãºvâ³ÀÀÀ1¢£Ìòàìi<¸>9ùËAÇœÆ^cÑÐçO”‰«²E)}üÜÒ³0è3Ñc6êAå-~…©,O¾B(õ0;KÏAaUŒœªY݆ê˜#G:6s¸w9±VÛPаŸŒÁ«²%hÞ VN…™ƒÌ |U @dÁ¹DðnjREcñ¾mQìÄíQç@ËÊÀÞ ¬‡kñ ó{Óêkã3T–_ñyzæ6Ô¯\Ì’o ¦"ñÚ݃g¨wÕ‘ . Lù¬Mv°â­,‘IŠVŒ¬Q(FBÝ[/°`×t¤ÇÉïFM¨s5§z+» ¿rƒ) Ve S¥>T÷}?”U .Pë~ ܹ ±‰ Ì…[¦l©HhôàC²M›&‰% ¥DÇD" ªB5{X3ZϤÄA–䨎< » îA —΋#Øæ-X¼iWBZ"dgGB£và¾6Î…¦_B%'ÐgB& .x­(æ? Œ¹¹è†RH‚"†|ÊQ~\Às©÷S F„ýx§®ù‚0&d$i9‡Úþ*â©{ç<êÖð•î]·è”«Ü¿+-îÄÇ`0…Àªlax¯œ>kƒ`Z3­#aÊ70t¼Ùj½MÅÂIïÁ¬Ñ°íoØžéÃgào;·haî'0j*´÷…9óàÓaHWP8Á­€SÃÅ/`À°)pb$¼ß *ÕnÄUcS߆‹¡m |Ðæ-†Ë{að4\~[³¾†á}@!¯ª0p<¹‹Uó,AåÓë–G÷j(½½ïúC <¥4CÂãÙ_‰J"‘p·×ÛwQ>hi%Æç)WIfûòË™r¿w†¹<02†"`h‰Œb5âDŠÂÝ)¬ŽÃ®1u7•aMÆ`ž«²…áŒPóc¸AA®.¯„gÁ£º&¦—àMé%ÞÿJœ6Ö`Ê×0m6‚8FÍ 0ì]1ô¦À‹ Çгι,Þ&A°F ¾ÃmM/­;-úÓè‘f0Àè!@  ×ŠsØÃ~wÑ‚Q'¾äK¶çYà90ðP6æ¿\‚ú LØ´åä/:L®ßõZÒŽu'U: ¨@Òpç§í.hs2ï‘¿t©(áx0Gǹ½nÿÇÛxƒ^p«WõË¿kyÈ}̃5+oÝ1Ê´;Î.Iè8"Ô 9ÖŒì7œö<ði^“t/6ƒÁT$°*[Þ¹E–•ò Õœ±_Õ램e,²“Ø·KÅ +l-·Ð±^üïE,`0Å“L£÷Ú4O Ïê9â!]Õ±F]ޏ ¿ZãoVW o~UÜüxH»ê€vãÇ8'ðoÔsH­i¿·þ@Q$!èœcyŽCÕùø8>blu–Åw&ó4X•1̰úB‚©1˜ûbð-B ܪÍ1‚¶˜)œÞ€|h(8xÞ¨ãÍoG²œð Ë=ÀIHO?üP1p˜Ä€ýd ¦8°*c0˜çA@N|Ff& lÖý¨,OWCçùÈË&Æf#ÏIL¯ööxjM¶ÖxdÅiU¨oæ•í¨)¼Cƒ)¬Ê æù d“Íšð›¢ç\™ y>œßžÑ‚"7›·äÁHÞèú0‰ Ô8¤ªÞ €Á< ¬Ê æyàâéRÌ+ÃøÖöñ-T³xHÒ³†¯g^,ÉÌ3ÁªŒÁ`0å‚ õìxC›«2ƒÁ”WˆŒ¤ûƒ~»Ð{d§áT^^Š,^cK`U¶!þB^¢ôbO…'l-bƒÁT8"%5å\ì#>V?ÜK)> xnÇÅØˆP? Nži£`U~ÝðY°ÿ DEƒ<ÞzC æuôܼT0 h û¶CL:TiíÂ^=ô‘`„KÇáÔUP{CÏž ÃÂŒy%š’HÅ4QÆ\ƒ‘7JHŠ"XAÏ#¡i†$N£)f¯ÔK\…2™¥ }Ž+g÷*êKhÑ=Ó„:k£¹÷MšÊMu8NŒ $˜2»IiS !hÚ”†S·’¤ ˆ$Äšâ©©^âÄrdÜ\ ÙdLÕx.?/lpͺ7¾stFH ýÞ„u7Ï 3‡ÅÅÔZq½ÉˆÁÞ™E—CÞˆ¾*1%-ºîì—X•_7¤ šu†ûoÂ_ZÔH4ì©Ã÷÷ W°³‡V¡Ð|¤q³}õW¿ Á@x3Ø4.@§^X•1¯A“‰‘gþþ3öA¼®Õ/ƒº)G.¬^s7»ÞüACkýzò`ä½DŸZ³æT/~íõ‹\…"s/\_ºêÎÕóÆÁzD8å((,!²¿]~ðûóIÙœ Tª:¶k·ªƒ N“êùéæÛ¡î.ìe'á£÷íªy:º¸BzLË);˜–mÓ–T]ú§?n þ(áäÌŽ j6¹:…r˜qþÇsN=ë×nTõÑÇÿ\í4+yxó°>í´¹;yÏÙÞëýN ¿<ƒTO:¼ø¬Û€êaó*{~¹ñüÐYõmU«C‹úíBã>Ù|¥Å ÿ„1> ôY6ºùO6f#¯˜ªµl}Z’èú§:î§®ÈWÖ>ùűc¾Z»NQëÊ]}䯭ëv¶þdÓª™}ÞòôÿkB}—qûç¬;òF¨w¸·œ-o¶Ve‹AҢܚƒN <è´`0 )ÉbRd$³  ¼àýLI@e'†êLÑ:óÏË”â‚JÓ5@›+FÕ6ƒ.$“‹ƒO`'3ꊔ"ígÄñ3­>a^ÞÈêõó(H˜õºü[XŒÄi0ZàfBf ºBÑ;Ë·cý‘ÍF»{Õ²ã  !*Õÿ–¤Ð_»uÃñ#ŒãÞÁ Ó©š9°ö‚/ŽÏÙ¿¡›O¦ýFí?îÞTM,DqíFoÇŒîá>êÙ­=›ø0QË{ÑZVŠQík ðS€”ò¼{ëÛŠ»E4Tr ¸î;µõ~|øÙ ¬FoD}¼½Ñ˜…ºWÀgäê]òÉæ 7ÎÞø#‘øôËF>”´ÐµM½Ö'7Ž[u»ÿ'5i=Ë=°sÓéΚ¬ ñÊ##˜çƒUÙ2 ÇIÌQøæG8wOL)ëäã>‡®u ë>|øœŠSP8„Ši j(_È`Ôè¾®'‚½?Lœ =BÅijÁ¿7Má]೟ žÌgÆÂW³açY0 t„¶c`ÎÐkŒƒöAÎðù4ð“–ÂG€)[¦Õ„‚y̆ÈS /1$^t …W' EL™Œ Ûqž´&z‘(æBy yá&Ø"¼víZæVÅÁ4ï‹þZÚéÝH5›c *ÆÅOeúãPÙÍ5àáÃÄ\Á¼ñ[0°p°„RF²Ù¼ýø)q~WF<þ¨MQ9Ç£jr<¯Cwqq5!%H‚^r¹¨p)1˪W*o=‹\¤T‹‰ak›jpúO r40¥VeK@Bô^èÿ ¼³V´J;æÂÉ(è.&‡¨Ù¾2 Œì #ÆÁáÕâ2‹ç"u†Ïÿ©¶,éoAÒrS[Ì €ËÁS˧”‘Pï 8r ¿C@Õ±prØpùwˆáÖ(4j˜´ ÞiÙ¹ØW®Ð4‘vþο+n¥“2¹t)YI.5fÏ ¡Á°cüŽs¤›šÌŠMÒV31ÌQñý>¬W»²‚"ù‹¿ÛqÍàbOëS‘îoÞ(ÄDÕµvüzþnŽÄNI²9ºDƒý„¹M žDÉ]¿6ýÝ3ÊFUÛ®]Ï_RV‡K^/¨_ÁOWAòÉ $AÒâ²,S E#ßSÏsÖýa8äPRêñ9I0)¬®D+ù0/ Ve €~u} n½`h䦋Cƒ·À9Mv‚áÕÄO™´ƒ@g8ñ2¿@ÿß³2z‹ß™{ŽÁ¦…0p™8ýJdŽÍÇíþ8.*ƒuQ0Aͼ5Ùúô$ƒ”9£Df\âõë\£ŸƒiA hi›!5¥J2'¯yµõgoVªñn/W£ñ•7Ú‚lt ©Kcþ8ÞÂCA£Ž`ä@NËÆ´òšú{ìáhM½ÊR ø;g¢̈úRÑA?qÒ¼àóñþãq|Óÿ⌙ÿºéÿÈÂÕÿk®m6H˜ª8 N‘Õ˜³j’$Yðýo„zúl½µæLú°6v¨ãŸ—t>Iß°OMgËŸœ ðC¤ôÁªl xq ŠzrñºufÔî°~8Iá½á¯øW1oþ)Ê$°t4üEº¿!Pû~…÷ç™^åÅ™ì"˶óŸg÷9?ÀŠ/@N>eS¡øä4­*/GÂ<^*¤«—Œç.1qýü3‰JjÁ RŸ®Ë{ò¿ ȡΊݷ<æ¥ïä ê ,ÐJ9•r󯦭9c6¶®éH3Tê™Å7ÌÖH1$¯3@¾×õxrøØ›¿‘Ÿ)|u|yCßwZËô–XàmMXªSÇ-Zµ¬ØB®©LÂÈÅþÕ¥sË–­ Z“¼Ñ4m'£IKÚ»¼×£ã¨nE‘r C¨|¿ÿtÈ·â¾qJMQuk×¾ñCMq1Cñ•ÜzµA¹”¦y¯é“.nü&Q5ÇêÕŠªI$Œ’T kç2¸Mú¸¥ -Nîrþ't]Š¢Ô²'H,ÔŠ¨·£V­\£©µb†A|¹²c³ÉMÐå†f°$—6X•-€‘„>oÁ¸U°¥´ ÷"sFàHpR3@ÔÈш1¼÷>´)žeòniSŠcøˆ/™Ãòâm/M3¢|1b²`ÀPKÀ…†GñœJ=$¥Š—FŒ< +fÀº0¢ (‘0#GEr/q©¶h\ï60f3,øšî„ª’‚}V˜ Aúú¨Hˆ¿vUS»¡”7oF·+dei@íî 'AÏÔKu ó*“4™}#N ýhíÝ,”N*Qù “ GG7Ô8ݾŸ¡|uÓrp#0}GøWU¸4ªuïàÌÈK5:Ws*ù¥…LRÜä<¡”K‹lÈ (Ê^AœSŒTR3S.“LOK =º)Ú‘)8¥¥YÁ& Ò^Qä!_Ìu ¿*{âíæ2R-—ªŸùŒ…Áªl"FÁ',ü49€|ƒ [hQ> χ«[ zCS„»døé'¨ÌÀö{¤ŸÝ¡[7¸¶6\†\%,ú ú …7èÖîl†Q»ÅmÇi1FD@6?‚‘3aP_­¦±èニOÇo7X s…ÝKÁÑ\½ q[¨LÁ¡HÖ@äeè ^~§`ø[ðÕЬZ9ÛŠyQðoRkííóö9«S+ÌNlR¬Ö«¾«›³ 2¢öí iàa¸¾÷f6z¼+h©”–HÅ[–3 màxR*JÎÈϲ`਒)o._œس™§ŠÌvq–AÔ®mº†KbOÞJ™B}Do·FÍTÿÎ\e¬×¬©‹’2rU•=ärS˜GŠ$ª>SëG½uhã²[ŸL %Ë]N æ¿Áªl(ôž}>*(ÓK°Ðò87Á|_ä a |úB‡7 ÞÅJá‹yOÄäÝVœ<î ‡z~83ï¹×úÃÆZkö0vµ{Ü€ÎS ËÔ‚—0^å2òÏÞûçÛ9gÏ&ƒ uq¬ß·^ß²ö€Úo\Ú·û‹mǼ»OªY{ûο&ìŽéí•v †8:}—rLÝ:ºè•¿GeœŸµs}ÿšíZ»´oÔ4éæ—m—ejÊήñèö½z{2<ï\-¨{רu ö\qpê85¢™ûÝËʪtÜPÙ~FWו§6­>|d1O*•ajõ·ËZöÕE 7~zÜeN÷4-KCüÎ3³’5cçÖs,Ë ±1˜—«²%)Vð …TžUç?M)/ZGxŽY¬ÇA(U»i;)®Ïx5°Ï)/8œ"ž7h¹æGѨcià‰÷S$!p¼Q ðþqëúe:5pF#o_5pÈw!4eZê+¨ŽAŒ-i9µk‡/Ht5£–%¶ŒêO FÑ Ž™3á#Z4|—"MoaœÁ“Ö„"³<Ëé5,ö¿#µÄÙ–ÓiÊUN æ¹`UÆ`* ‚Qg,–U ¹Hó`³õzó‘¾p%NÇ>±*ɳAàX‘Í¿D޾pNÏjõO$,Òå2ËqÚl¼öSAÁªŒÁ`0Œ­€UƒÁ`0[«2ƒÁ`0¶Ve ƒ±Exƒî^š&KËÊCíÁhˆNÍÍÐûTLBVªž•98‡9R% ƒ&pìÃôœÄ£:ÈI†£ÿY¬Ê c‹¬1>-}Æü}q½ºÞîà‰ä39;û‡»6jžøf@R\t—eçÔmûÞàT’ˆ}Ïçæ$˜}À®YÓ=ƒÂde=ÐiÙ«2ƒÁØ"”Bݬš¤š¤˜‚£‘REƒj¾uà$E “ˆ`w7¼d¶b i•`@²(ì'ÛT•)‘ÀeËr¨ßÓ–‚$ ©Tzóæ‰Dáåå¼gO’ƒƒ–¢r¤ŽŽŽJ¥’çy–å8޵…Ç$ ÑÑú6mÜ¬Ý Z>á~&ÍXù‘Šn`Þªù)šLKÊæ|l~KM‰©lxXhXS$p†ówˆ>F–{A—•@‚͘b󊱆¸‚Üé ]T¨p9˜.j~î â&4(ú=n Ç[¶³v•Q*¨*Ëd²Ù³k4ºçW}- >BfffïÞ½ZµþÁãe2ùäÉN¯Ñh®^½zâÄÉS§N¦¥%5oÞ¢mÛ6{{{ƒÁˆ`YöùÖK ‚Üݱ*[t'7¬Ý*úÜÝ'¢ÁYµZ• Ö‹åŠ>g"¬e«æÖjÀ !p›·îŸu>Õ¥Z³Õ͹¯6žOó _ÖÝ—âs–®=¾èrj6OT¯Võ‡7kU¢ùç 3¹©÷'üpp÷CÚÙãã^}ýä¨ËNPƵë÷ÿx5MÏó r™> QwO1„¶ &cÔ¯Úxü§ó‰Y¬ P©Z¿Ñô‡N…/Äê²ç­<¼âž®aãšß´ q¢lÀ¨`TPUF÷¦““£““µÛa9¿gÏž3fÌ¢E?7o^ô™Qoøð¡è ''g×®];vìøä“)þþþ-Z´hÙ²exx¸ô1Vh:ÆÚ ï}Ú´Ï¬Ý Ì CPÝ;Õ_ybÓæƒ{eͯz§§·w…ìé 7NztvnǺ’ç3êˆd¤é»ôn:t«w~óë ß9àCä:k÷^ÌùvJ¿fÎÔÁÖµüöăï[ùPÁgû|Íz§g>è+ç¯Eî‰8“ùCkg(Ô·§)2[#tØå›šrÐÛüÀCy¤‚ª²íóõ×_K$’={ö¸¹ý—ß©R©ú˜@Ç)))—.]º|ùò¶mÛÀÔÉ@ÞvPPPM¯©õ æeAÝpžsò ;8µ>hÓO^×Ùqüté’Iõ]IÅô~õ–~¹ÿÝNòÿBxð©Ü¦š#„o†wº3{í¤¥×ÞYWnPÎú°³›3<Bý{éš|TÂéüœ"_>¶±/Í‘kTk¢ž,ÈÌH2Z·tý ¢^ÃoB¥X’­Vekòûï¿Ï›7oõêÕ¡¡¡$ùó‚...­M cN§×ë‘'}áÂ…Í›7ðÁYYYmÛ¶íܹsÇŽiÅŒÍ!.Ð䬬aUjfdAªk:“Àsâ0³‹S†ŠŠŠIæý\žk‡çÅ `¤¬RY¨«ÓÆ+1· õj3ôýk§ûÿ›Æ“¬!—‰ès º¿®åHe޵Üh1m&*bÜf´À(˜u@Nëúyã¬x÷½ÝÄùfŒ•Àlëp÷îÝ3f ÉD.o MÉLØÛÛ{{{wéÒ• ‘Ž41mÚ4ww÷:uêÔªU«jÕªÎÎÎNNN¨¦%þ c x>[\T%•SDÞx5EË ÂÈõ/µÖŠ S²Ž„Ãml©[÷õ€¾~²¤g½g]6]ˆËá(1ƒóc÷˜*H} Ž^ª«³£¿?‘°¨‰«…þ<ÌKƒUÙ |òÉ'QQQãÇoÒ¤IiØ—J¥M ãÌÌÌhGMHHHLLLMM­V­ZãÆÑÕ‘Ï] À`0/ IU’#çUŸl@Ϋ©D¯Mx¹ÜÎŽàÅWs²l¢A Jÿ`"kÌåL{¿ðn>4hôÙúÇ#Ô”$DAR²ïk¡²¬•ÂÂþ]ûozÍÐ5'úÕíÞ\þT:Ìk«òk)b§NÞ~ûíÙ³g¿ž+"ϸ¶‰üƒÁpâĉíÛ·£Î¹=ˆ¶mÛâÙh ÆHúÕw}çì}÷´­C‘µ½Ò]%8âÍn­V\ÝÜnÑõœOª3xjÙ`U~Määä,Y²dçοüòK:u¬Ø‰DÒÜÄ·ß~Ëqò¡;¶jÕ*™LæãããåååçççoÂÃÃÊíÄ`Ê#£IÑ9Æ .%ònz[;ðÖÝÎüúŸHçä@'Aû÷‘Û56[Þ\-ddMº•$œŒ–†¹ËoF%_΄¤G)ûï:×s“†º«®<ŠZyTÍj5‘çnfU8ØÞáÝ^›6_kóÛÂNKMå@·çâýêõ¼›·\µcü¿;/_ñm䥴“PR¥ëÄ6¾î>ºË‚61íN.WÃÁ±WçgŽtß XÒÆÇGNYû«p`U~œ>}züøñ'Nܺu+?k7§Š¢Ì Ž5MJJ òžcccwïÞ}ùòå›7oÖ­[· OOOk7ƒ)0j×ß&ôPÈè¼ Fºo¿ŽÒïç9Á~Έ ªj™åiÚÝÁá›{p$å ”Ðé¤P~øAñ$i'“2jå²w{ÄgjRµ,'¨k…Tªân§B¾6K4lßì\õšI:^®8ɦ&¤g-#00ƒuiÝ6ãAŽ‘€aOg;• È.¿MíA”‹(T÷.-"Zsz‹ÍYªP`U.]rss‘KºmÛ¶ÈÈHGGGk7ç¿P(~&+ߣGsáÉ“'wìØñÓO?%%%µnݺU«VM›6uvvF·T*}©uã €pqrt))Aôvwöv/T@Jd•=ŸèÄy<ùN•¢ŠJñÔ% ˜×ÇçNîù6‘U/7'¯'÷`úº9ù>õpöŒÕÀª\ŠüóÏ?kÖ¬AŽæ¹sç¬Ý–W¤¡‰¯¾úŠçù‹/^ºtiáÂ…:9ÙA¸»»W¯^½F•*U²vK1 ¦<€U¹´èÝ»···÷ü¡V«­Ý €ÜâpèØh4jµZƒÁpïÞ½C‡-Y²äÔ©S 40ï°vc1 ¦¬‚UÙÂp‡„êÓO??~ü€¬ÝœR1¦`&HŒ'OžŒŽ‘'½gÏžQ£F¥¦¦Ö©SéwÍš5‘3íh3Á`0˜?+-Irrò¸qã‚‚‚þùç///k7çµbÞ…õK¢¢¢¢££¯_¿‰>“„„‡&Mš4jÔ(44ÔÚ-Å`0Û«²ÅX»víĉ·mÛfæ­°PbLaÁ¦ûÎ;;wîœ4iÒñãÇ#"":uêÔ¹sçÊ•+[»± c[`U¶È)üù矑ÅÇÇ[»-¶ñ8½ Y§ÇŽoܸqôèѯ¿þ:==ÝËËËÇÇǯæ±q ƒ©˜`U.)Ó§O¿råÊçŸ^8~æ?¨fbÔ¨QÇ%''§¦¦&$$ ÏpÍš5W¯^•ËåíÛ·oÓ¦MrÀ`0¬Ê¯Nbbâ!CêÖ­ûÏ?ÿX»-eŠ¢|¸mÛ6Ô×9}út­ZµÌ›¤ƒƒƒ%&ðª1LÅAàù\käJÂØ1$:G§V Ú^Bq<§Ñ³FR©¬$¸Ak`u,O3´J‚ã†XüŒ{“7{öìK—.!ñhÔ¨‘µ›S®ðööcGGG_¼xqóæÍÈŸ&I’aäI#?»zõêHÈ‘H[»±L©A—þÏÅûsÿ:£íÙå^g/.+{Ç•û þ8q½E³”U322ÖïØ÷éã†~YCòÊ™$xƒîĵ›Ã~=ãÓ´é¾Áa2œ’ÂÚ`U~i.\¸Ð³gÏ™3gNž< C©RÉú´yž×šÈÌÌ=qÊÆÃó#cN×¹.Ík5i™Ÿ†6ve³ý°±r,ýa0è}Ö£öŸoa›·Ùñ^ÒóÇŸ›^ªÜ!°`œbȘØeƒÖŸÕSa_Ù*`U~›7oþùçŸûõë7räHk·óB&÷1Jë|ôèÑmÛ¶Íœ9ÓÃÃÃ×D@@€ùÕ´v“1Bà´s7œá›vXÞÊtœÄÑí½Ž¡ ¦¡œ°ÚÕ* CB_)}únl¦àa÷|›tÛºU;Tc€òmagpœwñË·v÷PØyÌèí¢’aßÔÓyÝ…»1|• »iõöC.¡©Ýƒìäö#{„'Þ `RŠÝ¼û䣪õ—ÖW‡%Ù:`U~&èQÞ·oߦM›®^½ÚÍÍíùoÀØ*...=L ãŒŒŒää䤤¤»wï:tèêÕ«©©©7nݺuÛ¶mñX7¦!NóhKôëmzÓè4ÏU ©VY Ðkjʸhãþͱ¥”?¢e]^X<…DTQÕ¯*\|ŸÍ{Ú1ÔÉCg~¹’FÐÄ­ØT¼ €M^pkØÌQnžTæYp®öq#ølt¦p«ÿÜ:í†úà ?àØÒù0Ï«r1°,{àÀñãÇÿõ×_uêÔ±vs0–ļ,$$Ä<Ü0 »wïÞ¾}û˜1cœœœZµj…ºQ£Fr¹Üœ‡#?BS2Þ Ëp•ÜQ¤ií•À厞¹æpµ–7¦„Rtö€wÿ:ý æiF És$#üþãòwsBî|ÔÅOÉÍ_¸åƒ ¦^F} €?Cº£‰ü-ÊGþÏÞY€EѵaøÙ¢»¥”FD TÂîÀ° ;P±ýìVìîîÄAºØþgwIÁú?ý=÷¥{9ûΙw†ÙyÎ{æÄ•{IF¬¸¤Ì÷Sf9ª“›¾² ªü%Ïž=[´h‘ŠŠÊ;w”””*ÛÂo‡Š½%¬^½:++ëJ¤étºtêccãºuëÚØØhkkW¶³„jˆ!¯¤¼ÈH:9eÓéÜä7“v1fpyT-ø? Sæç&úŠ*Jy§>uµ4b‰Àð¤ý¸©OYE ®€#~_\NuÕ,ê\a¿zÉöÑ»owµkSEZ°+¢ÊeX¶lÙÁƒ£¢¢ÌÌÌH„ô¢ªªÚL•.((ÈËËc³ÙTEíìÙ³aaaŸ>}¢"é¶+ÛYBuC$¤ËéúÔDÄ‘W ›¨óybñ?gÄÿé²"0è`1å5óëšÒi)Oâ>íêSÊ›@8|>NÁ*p%]s¸+³ýݸ']ê¹*ÓÅ:’!R暊à³úù5ß1÷DËoSFZ àG:~1D• yõêÕ¸qã /^¼XÙ¾ªr444ŒŒŒÚ´i#Íq›´o@sËúš²2"¾œŠvkSA:Ÿ*6?•+0Щ1°¾nÿëgû]P_ꬪÄ$cþkˆ*‹ÒŒ;699yÊ”)ÎÎΕí¡JÓBÂüùósssß¾}ûæÍ› .P7OzzzZZš™™™›››«««Ae{J¨¢ÔªS÷üÝËïRãØ|ž†ºÚ6r4¦ÂÒ1>­bÒÙBfãî½mkžÏ|øœ‘Êå´íÒÄ“I{”Ìv3ù”áо‰ “ù<)WUMQÃÄñþèZ2¯½Î¤bâÐ^Þm­5\>h²cFúZ<üœÈ¡Õ´² t0»Ÿ•—ÉQÖQTÔÙ6Ño@LbL‡/R•Iw•÷ ‰AAMÁ`¥s„ Q='›ÖBaf"GÙœ¨òÎß®Êïß¿oÛ¶mhhh@@@eûB¨N())Õ“IÅN(áÎ;§Nš7oÞëׯ©ðšºµ¼¼¼ÈÛhB"UMMoMͲ¹"MmÝ@mÝÂ-ó ×oZ·Vé]–¹‘õ¯$/™*D$dÊ«ur/n¼Qï]K¿h'K^©y=óæ¥ ª¥RËɪx‹á\׌D'•Èß«ÊTp³~ýz*Ð‰ŽŽ655­lwÕÆÐ@ÂŒ3¨Ì7n\¾|¹_¿~”f›˜˜תUËÐÐÐÈÈH__ÿ»e„¿“¿T•Ï;7{öìQ£F‘!ª„ß»*‘ŸŸ/!{öìÙ¬¬¬¤‹TR‚]Ùž„*Ä_§Êyyy“&Mº{÷î©S§ÈÀ'€¼¼¼±''§â̇FGG÷ìÙóÍ›7ÒÒzzzÒÒdZPá¯åïRå;wîÝ»·cÇŽË–-«l_5öBCC©ô£G}ºvíÚöíÛoܸAÉ3uëzyyYYY}·(PÝùóU™zÒ]¼x‘ JæÎÛ²eËÊv‡@ø*,KU‚žžž““ÓÈ‘#©Ì—/_ž}úÕ«W---½½½Ûµkgkk[Ùž„ÿ“?S•=z´dÉ >T¶/¯G:BšJ”@¥ß½{wùòåE‹%$$H–Ž“600044”““«l¯ Â÷ùUyĈ©©©“&M"á¯ÂTB¯^½¨H:999I“'OöìÙóêÕ+*S:þJ:Šš@ TMþ(U¦B??¿®]»._¾¼²}!* *’Ö•@¥===¥™TU5::zñâÅTTFŠßÅH‚H$bKÈÎξsçÎæÍ›¯^½* Û´iãåååááQÙžVu¸<ÞÁï?³ÂÊe‘’,+—ë\eNÊÌIW$ÓÇþª·*§¥¥­\¹òÊ•+?VPP¨lw„jÜ(IÐÕÕ¥bå=zP™™™™§OŸÞºuë€LLL\]]­­­•••UTTH$]š  àýʪٕÚþOU­jê9™•ª*…Ã7ªDþª±*oÙ²åÀ~~~Ó¦M«l_„?55µn¨trròk »víÊÊÊÊÈÈàp8uëÖu•@¦É333?vLe{Aø£¨®ªìïﯮ®N©²t|@øèHhذañÒyyyW®\9tèPÿþý©à,88xêÔ©•í&ðçPÍT™z4PJ¼lÙ²!C†PÂ\Ùî ÅkH«ªª¶—°bÅŠœœ*Œ®lׄ?Šê¤Êñññ-[¶¤„YKK«²Ý!þv”••*Û á¢z¨2Ç£”xòäÉÇŽ«]»ve»C Âo¡¨ò;wV¬X¡««CÞ"ᦪ«òÔ©S>|H©rÍš5+Û@ ~/UW•_¼x1tèP77·#GŽT¶/@ üTEUæóùcÇŽÍÉÉYºt©]e»C ÂD•SåtèÐaùòå¾¾¾•í @ ÿ)UH•“’’¢¢¢nÞ¼ùðáCMMÍÊv‡@ „ÿšª¢Ê—.]š3gNHHÈĉIGk@ üTUvtt<|ø°¼¼|e;Rò4&ã»Í‹ø“A'¹O¼´ì˜s/Öøß\$èß_{æÍþ£‹è ÿêb ¡šPUT™Ls_ ïѦ{ëîíöm3Áû“½×3¶Ím%Ù'_ÇÚž&÷+Úö†s[Ô”ýŽÙñ½Ðoú¿zù  »ÁoƒV꿸X@¨&TU&!Ú´xÅáÌ%ß4¯£znçšíWü·Is]¶Ü~ð½‚`õUîc,¹Ýßíg÷³¯cE§Ÿ.ÿ;|ÆôKÝá_–"åÎ;ÎÎο¤(Âð þþUì@B¥@T¹2É=7"pȱ˜,_È0ítóü¢ã¼FmÄcúª«*v_wtµŸý£½á½Bצq|vºœmÐÎ}Ë]5…§B;tŸ ÚE-­±^!£­>^Žˆ:Ê·?eA¡RÆß?8yüô³Ï’øÇ­=øâå¹õË[‡Í{pà¸ÔSN€ncáþc·Ž‚¼1Ž‚lúÎG"À€#‡qýÐÒož`üzp¹; Mxja×ÔŸˆMõwóOáÀSì„» ò’0g3î¦PÅCQa£áüõ‘”XŒ]Ž<"°PGfà';üñx¼ôôôgÏž­ZµêÀíÚµ;vìØOþAÿ¼=ý|n;¼Øžõ›TåãñnÁ¹Vß°y3Ò5°ÇÅ®äU¡ÚBT¹ɘѲå5§­ñ—éàD-‰rdüWî?ùT‡úÁŒÃ!i<ýðŸÚà_¯Çj8|Bã«Q][Ï96)Nuü»5©W{HÌÆ /Ó¢d ‹‚GK4ÝxÎù¸³t çL“…I_üÞ5¬KÆÅ… ž_±—q›„þÉØ: F…%¡N3l4’X‡ ûq;æ¶X膮w±h>œX¤âÙÈKÏ1O7B!~).Àâ(Ü7Ç© â¯nB>÷[CÛó[£Ý~Œ™…Ž?÷:#&&æøñã.\¸}ûvbb¢4SQñW·®ÿ}ä>=¶pÃQº«ß[ÑÆ]—» ki«óòd䪣…qõÔØðÒêI“öž“K8Ö½aowí½Ž3: ðd¿9½v}t¼°þìE½dž_°á0:qdmÙu¡v}ëëg. Ml=k«=~6='„¶4þRHãnïYºíJ€§hÞ~ÚÈf×–Ž™rê¾®L^cÏ©Cº bÏ.^v0›Áä‹´ÆŽr3PÊ~wÕÔ¾Ëï'¾ëÛ¿éˆÙÍÞlØt1¾ÕÌ•>zé'wüsêÊ;÷€¡]½9³hÕ‘,.¡ç>qtO=Eò $T-ÈY‰d<‹£qM^gŠ A“í3r0NG^_?¯È†îèà$M2ŒåèŸR?Pâ&#ÈãòD™ås…ÅÍzkGG$h8ìë!í5¥ÜböQõrm~yyÈLEªF47Æ0I&G(n Ì§Š•“lk`L«B{=}dßG½”þ$Ç¡ÌXàò!•Ëá¥øH"'ÒÂ\[Aø½ëÁá‹?¹T´þCªÌáp6mÚ´bŊׯ_ ¡ð» üJ6í;wxæiw=ák¹Øüz]F~>uù°UÁ…–õðï½ jç«;y™ûĈEŽJ`²â¶ôtÂ8¤‡§†E3Ñ–ëS&.êeX·mçZã톜zÚ½‡!=†& æ¾Ê`èù…óšûtØ2} g3Aæ»yª¥]t¾c·e‹/k ž1|ä¶taÛ†ž­”'oñ™ºbF&u>{vR¦~Ÿyþ¶ç–ù6l¿„ý`ŠŠ‰SG Ö­Ú¡+×ÖW¥ Õí.œª4r¥¾f›ÿè1ºgíºvsçôhÚ¯ë¾ëÝë)®ž›–¯§¨\i×—@¨¢Ê•ˆé¢Å†ÌÒÔØ4pX¯¾M팾O!÷ÖÎ9S6\e)©©+æ]ç M¿_lâùWlUyS#µ¢ º®»S9+{4»‰–ÁhѾ®p·®@ “ßbå¤0¡*‡”80ª7™,º»"&nCѱÚ; žÑ¯ê\ýöíÛ3flÛ¶í6/^¼X¸p!¥ÖÅ9"‘HFF¦ü¸;:.+[Aï6‹UÞ˜F£UhLeRå”/™É¬à'F•\>Sºzq…ÆÔAИ:Á/r¨³¦|(ïU&•iffV¾2Gg@¦iÏ]‘³hy™Yœøñ¦Û]ûq7TZû´ÉŸ°îIŸP9:Æ”•§$YlÏ,v•Éb–T†Ic6ì¶cí\VAfB¶‚ÜE -‚:™ÉÁ"õæÞžG_bžk©ãæÅž{%TS’Ud©ª/X0B–II®SN’ó6m::DA_ž‰:öM™Ó®|Ì%G.+O—e€ËdÑéÅÎȈ+Й¢ø³×Òäû©¨Ê)( *”Søöéÿ=D•+«Á{{ܾ}ýü†ÅšÏXpûî8Û2Ïß¼øc~ úž}>µ… ßáqì÷K¥QOI*ŽþNä¨U‹ÆãÅœ½„Q‹ÑÄ«»•µ`cR$’kãp±š^?†Ë·~îôœ[!ÊÏßãÀ1žÁä°û%utuuÇïéé¹}ûöÓ§OWh£§§çååÅãñJgòùüò!5¥[_˜}Ã’ØfRå”Ïd³Ùå©(¿¼1U(_2eö5c.—û…ZSf_XR6”eéÚI±qzzzTTTy÷¾(@Äç²yPQPSÇÍ‹™È>4ËçõJyϯf§»%‚ñEéùÜ\.4äÔ äNm …\®¸…Nù(iWes?ÑüîNMZw›2}Œ3 ùe X¼Ä%c§¼È¢ôû.—öc>¹4îAµ'ù7µoÒ¦óøÐñ­j“@™På ª\‰pSRøúÖ.©ý;iGì~1ÎѤ´EAüX˜ºYhIí?ÔûT×ÃRaï³—¯aª÷ƒs!§ —úâf‹±âb˪2/‰ù06/ p¿£ò_ʆ8'‡+n÷¦þ5oˆ™!¸øþ¶`ýYVRR²‘˜››»jÕªåË—'''SÚS,`êêê”Á¿?¡>º õ[·BI‡+î§Ÿ¬«•£ü­CÁR·;ú&þÀšùsçMqYwàXâí6e¢ý§Þ†Ž5—ßÝÖßñ͉yÑ®ÿØ‘„B†ÁŠ»)¾Û͘>§uÝëÜéo¯ý/ý'~-D•+‘W]ìBg¿<ÒX•zÞeera_W ":‹!Ç÷S+©¨c«…E/>vhS+éå¹<‘‚¸­‚Æd)áãƒ7ì|5.´Ôåe¨ˆƒ%mY•ë3{À´ÓÂÆÏWíiQC…ÆÍILä˜Ø˜–ix½z©&èf-Ù ä^ê@º<¨Ø.9zŠS„!Á‚ Üiû(åB>‰©È‰_-S–ooà£1\ļ‡9êP˜²#ºÃT^üàÑ © ú7%™òŸ'@ÂgäÊB$åº9)…ž áÎ;‡¾råÊ7¨ ³|tHø·¨XX³üáV¥[Ï©Š£èí†8ê¥Ñ$[Ôüÿ¿±àeÆ$,:…,î4bt[£ÉÇÛyЩû¨°Ùþ間š<%‡,™Í†ò„F£3Š}@PxËIZÖ©8çõólÓæ½¦7ï6³­Ú¬£¯‰*ªD•+/×xÿÚ5¨§‡@(çÔuåF?+0DC· ˜na¸µóÔÍÛ‡w ²¦_p£´­ƒ§Mj¡+Z¾Jß7÷°ïä7lÁ¶Ð:†G<Û¶ª!üt8©ËF$LY4ÉÊ}Êík&#†Nlh3%'¯oéÔ#dÁô/âF5¬ÞŒõq(¬j€(]†mØßÇЙp®™ý0£#n…÷k¸8¡qM0n `-V„yk4½Žiá8P3†`„†lA—p´ƒ§%äØŽ9#QŸ‰‘ÓÁBHƒ¥#ÂZàÛ/¦õ í9¬YŠË–‡Ÿ»9%°ÙìôôôC‡eggÿì߃PyY:ÁR(|n=gzýú¡¡‹¼êŒl[;'îuª²YmYK‰áô ž¹‰PN¹F}“¸ñÑG®{ÛÊ&¹ý4wùÂrârĵ:ñxcjt†œ¤ž('Ë¢!Wö¸Ù¯ÿ™qÈeãœvàqS3”[Û©É¢Ë\<ûfL ‘¬®­&wñ©Ó]™j‡öåjH;Ü+kËó3ßǼÉÐQgèij¨jˆvL_Õ+¼UvÌ™«© 7Ezê¡ÑU¢wdP5Ítf³:ZÿÙ•$~¢Ê•ˆÆÄC÷&–Ëu꿆ßMñfßU7ú®*Ú´¬(iÑnršhr±YÔÎÒeÐj5<ò ð[·qÇ>÷/3Zر²dS¿®4+Ùìá]”RFäâR»5ǵæ%[‹fýt Bð·\(‡Âç ü§vùE ÇÿW¥$dßÜ,:øëT¹èÀÃûaÊí_T&E>BB°ûõ¯+P´^8åƒY’ô]±Ÿ°û*ꪥTYç’Ê|;,ŽU°W~Ò½¡C6õ9ØH2ðWBT¹š‘÷}¼°ûTu0hÂÛãèJ„†âµœ‡—UEûÐ=Ý$“ÿÒhð ÅñYâ?ü¥õ™ÇŸÀÒD÷Qˆ˜ ‰Œœ_„HÊEÍàŸeС#ù1úàø ñt™–~xº çàÕ3ác† NgÀƒ‹IÁX}¹"4 ¦%¨¥„cÑk1TZãÚtŒí‡x{ÜÞ^ñm÷îZàm>êk@Ibá(‹ä§9‡î‚Ç€“Ö¯‡­d¡ú´ûââG±|jÖÇžh¸ªŠóÓ_a@w~JÍ|ñx&5Ç’ç½mf=EX] Óú`ÙQd àî­ë F½ñQqϱvfßEúñÊ–_ƒ›€¡Ý±ó&8LøŽÅºP¨IDTIAXq<:Ô$±1MìáÔX¶ ª“;©wÑÑ×2q®1Fˆp( >*%%¿=9åÏ3²±æÿqo„?¢ÊÕ …šØuw”¡ß sÛ‹#:ïa¸µ ò{¾"É«B»Å˜—…‘/!ºV˜wi1šÍÆñðª A,ÚÕG‹Ü\Y:â“0ﺟ d„yNXŒÁMa°ü` S|D! ßN€ÖÇcÐÆÈ@GÃV2/'÷±xIIïõÈ¿.>‹Ù­agkOÑ@ h>Ç$Ëbu4G{d\,0²1䦀7ÈE×a ±q ;P37fJ“…žŽ¸ìޤ±ý(4í»°=b渷ý'ñ ~•Oh`õiȽ$¾Ík¢½®NÑÂ<˃6õ^†qÓ˜{æNÈ>D¤do-'\=š-"¯a@¹U»®G†ÓX#92'8¡ªó¯›“CT¹¢„Ah·O¦¢ž6xW±R ¶_·¯þŽ|®¸ÏQ1«¦¢A˜X’)ÆXÛÞx<ΪðŸ–ô¾0%ôTÄÇÁ½L´‘þÕ°cUaÛ,7Wü™#YÈøùœNÀÚe…·T߬‡Øõ¨)%3Ìœ*žôìò­{.;Wü[ÏÎ…bÃ$™b€_áY„­À,klÛ·aзÅh»Â½¬upöU+€ž·RàJ/¼J—KŽ• ¡ü‚Bã·×qì-݃dm_ ‹¥ýñŽ‹ü|ñ¯aòÔ:xã³uŸ[‡{|œ +¼]{`ÈräLÀ5¸”ƒe‹Å’L!¾à¥àJ=ÂrÄy9åËŽ¿ñJ­TdhäqWÍHºÑiÚ§]‡ÉWB*ãöžiK–žBG;‡}±RõÛÓ3V ˜¡õKüe]8aæÆ#NQï×4"oY~ D•«%M§Bs+VÜD”7¶m‚_~jÿl\b£³qI†ª•X3î§ÃY÷wcò ¤q¡$‡@}ê—(‹É~Œ#‘˜:= \®Èñ”ôc¨)&HD‘’-sð%Ò¤†öO¸ýZ5¡¥X´màÃqí"ë¦LÂõ÷PRÆ«§`hH 蘈Àa8µS& ³g~&$Ru Œ±Â´b?-ÁJ¦ÔIÍoö5{øNlܹä%ûÐiÐ3¸+yÞÐì'N°™q9S–%¢\íÈMztÿIN:WX)ª¬îÒ­_û=«–$–oˆÊJ|òà¡|¦ÿN•EÙ))rZ:24ïA}g-‹ŒËýZ›WU$?5‰«¦«ZM䮚¸I(‹‚1ú6ÂÉXÚ '2°¸\CèwŠå‡QêÑO£‹Ã_® —à€w1NÒ=ؾHÜœ;`Ì;áôL錉–8~ Ë)‚ ÷%Àû‹uùø¿¡MéS‰›tñbÔXBôuEÞ<>,Ξà‚UŸ M6ÃØ§Îb^OL4ÂñûhZVᄱ*oÿˆ.²eòã~ø!#ŠÛîN}†K™\H$]¶â~i¬âˆ$÷eYÌ|¶æûüWǯèáðøÞ7Aû ‚~¦tNÜþ >­]™¥KË=Ò±aÄš˜Ëâ·dŽP:ƒõsN—§¢ËX¡]©ÓýZúÛ$ŽlØÀòÚ»±Z?íc¥@T¹ZBc¡Cg,Ž‘ÿ@Çú?ò)­:ÊâøMZIF¾$ÒµÓÄÇóÈgÀ·hÄŽôõ*uïsDhÞ ]0&õšcÎ!D÷,rFò©£yàø]x—«EC#Ü(@&5¤Z—ˆXÀÅ´Ï8—‹0×B3n©—À>štFc_Œî ×&˜¾:”)SC*À±;èÒèÿôÊD[|Ê—^Ã¥ôx0: $­ÞOÒa£ñ– ¨(á6›+ø¾%áWyu€OïãoØ\‘bм‹‚Í©;hOh·1[îÓè²ÞS7­ì×X|{ç=ߥûö‡Y<­Õ¸cÿŒ­‡¤S=: ¾_À¡kY}f¼ûÃÞ~c7?¨ùðÝÁzªâ_cúµU]-|›Åá+Û†oÜàj H9Ì·õŽÇ¼A3Ÿ- ;ý–ç;u˶qíeJÕbÓOLsïNk;`”IÖ¢ÈÝŸXvó¶»·`ìÜÍ/så‡,?4?È•ª˜Ò¸·ô;òžƒ<¶Eç{Ö Ö)uNÂÔÇÃÚ·Ù’RoÍ–¨ ³»Ý»ßõªþëO»ÌÒn÷hïs2AqÄôW—̸ü‰±gÃÀ&Ô.œû‚&ÜÏ¢pé´Üü¾ëOv*Ñ;Qòí½Ýú\ˆ-pPPóÜúò'ú|8>Ñ'tåé—†#î]`!©£ç=Ùå×côÍ$¡²E›u6·,Õæ$âçná9:êÉÐëojmò›r£ÅëûSežîéÖcÔD²y«µ¶zê‰-ïlº›-£¤ª@{ÿVåHî}—‡»{öøTÃçì¹-úŸöù‡~fõ}¬Ž¤ä‹KƒûFœå è †,Û8©“sfÕð Ïð…ù Noí뱦KË-1ï…æÚ[ºL¾9J±Ê‹^•wðúÂl:6AÔÅ2Qï×`Pê¤!ŸEE †®›ÛÆ”–r±m>4:Š_*¿R7ã^~}#dÄ"›ºEd@ç k;l: M:4´ ¤ÁÈP\&]V,ö)é(`ÃÒÖêØ:CNÂBü|äQƒÒ'†¸—úÿHÍ.#~‡”mÐ1|fõÄù[¨-~tàÔä*ÃË[ü6·ðø%2›A…Ä\ñ!Äï¸xèØ [®‹;«kBÄ€±‘X/Y"ä%!_n¬`§‹írVj ›š`J~ ?â¤Wgh.dž è´†Jàä‚ÍD Uøxcô:¬˜…æ3¡ÆDR²¸™€QôêúÑŠ›%™iiÈσÅ’7†îu±õc&W(Rø‘?*á_‘âØäFàóO—¬ñyóè5‰€ùõ©.ýO7üðé°zæ-G½&Óìgº0x·Þly ù¸;²÷Oø è×n“:+.v¨!Þ®›}<^Ô¾õê¹GL DâÊoöÅ©5<–F<ÊfÇȺ³Ü¤QS™—º™X¯Yß?Òjj|¾Ùâ£wbOñ2n@¯-tå‹Òðšqvéãé/eý§ìóíµ:$h”›ÛÌ]¢Nöy´¡OŸq †øî6Uf"÷}‚ÙüøËTl~ÁšÖ|vCÏåâúD!ìŒøÌZ½Üœc)Þ2X±"ùˆÛ~ªâJ«á²s“­ÞÚ4VýÈS÷Ÿíã;hì À[ÎÙW8w­±6ùmOm<™¥äijhPºMÇ¥Û–¡ k®Ô~öñx-*#å-‰„öâE‡û&mÛd䊗õwå~8ëìÖ«Éþä”6*‡›yÔëôüãÚEmG4¦Rß™Sfîj·Ѳ)|b¸ì”›Ýƒ\w'¦´U»<Ò²©­Ï‹”ÃÌu^.#ϼKj©Îå•zM#øB¨8û­ šm±5W@=¾L»- {í{VZ¹¬i‹ùòÏ>Æ[ËÄuÒ3©ûf¡ù™žK2^ľ×Döôñ{øL s§¬8Ú?ìcÊ`Õß|Cý"ˆ*W[ 0»‚ µÛ3àÙ [P·6ZcÝDøÌ¢±ðmEYägiŽSË¡@G¦èㄾõaï^ ¢Áá°oõ$4s†¦"²ÓÑt›ŠKe:ÁßC;`g „.Þ]6®–0Ô…®º„`d+\]³o‘ó£cÖp¨}S÷”ÜÐFþÍÑØ3"ÐÈg–aÆ8ì’ƒ ét¬;‚n’‡MÄDtœ€«ëи¨à$'!«°y´²ÑÜ ZJÈÉ€ý`,n)®LòÆÐ­¨ÿ~Ó1£¶ìÄÈp·€a 蘠s•°p¸±}ÜX ê:ßrR± NîÀà p¬#–s}KNA/GhµÇÑH œ ›=Ð3‚g=dû†£Û,Ü9‡­1ˆÝŠñ,ñŽ&öhƒÙ8Ó#¡»]Iáîý<ÑÿqŸƒF£æ¸¢ƒGúË sÓ{Þj+nbj2!˜¶|é½»÷;Y¿û¾fúbóì¼|Q)ŠZåDyùœ¢üwS§]v ºj-¾FzZôÙþpÖÄøŒOïÏ?cw­«2}V0uîùârDyÔ¯‘¨2áwcn·f0ü1ãzÝÀîVj[ c¢0¦œ™¬.6ÜÁ†âí¡E‰'*ï°³xÓ'_|iÒh²ü˜‹wÚ:Qv¶¦ýq¾–>ó šW²Y”Øö´ã!;ÅÿJÜôÀÑr§sòñ:IáØ·»Wßd ^ ,Ù\¼¶0ѺÂæ—±Œ|Râsi,¼¦»³®¹Ÿµ’ YþÝÐ,ׯìY«Ç µýaÞÃæ¬X0 fÆËW"ÜšÚòúrJ|„ì4¶zg 5wnŸ¼B ˜óš‡E®oMsø'ÒÇbPw¹ cüÇ/X<­gÒÅr³^g¤± ëÖ?é m]ƒÏ×ßP?i†P"ĈN«x” H(ä Äm<’V&´/¿@@cÐ!’î÷nر»n'*©àAŽœ?]*WtyÄ ®ç°Ž×Ò@„’®BI%‚*PAIY9o©ß ÀKˆ*4R×ønCP n JZ€ÒSß#ó¼µÅyº4a¾º¾®Œ°‚N%žøS¥†FNòdŸ©cqIj¯¦¯'“ôäQ>ÌÍl~T–Ø^òX¯¢ºšŸP .eAV’fS®NýA}]×ûÙ(õ¯ã½zóªÎFÕ©[š¢ÊÕ !™BhÈ‹ÇÝ?‡Õ оàÄ^Ä$WÐÿªŸº÷{ÕxÈ'<žÓÜw"!Œ¡“SeøTž,¬Z¾LSV¾Ðùm¿—y šwÖëÍN'ÅïÛþ Ý7òZݽ}ûúÅíï¾ùp¤)ôîóo¬hTlCé–zëÙÙ9}ŽìرaIXí:wžÄìµxˆÛááÎí[×. hô2þÞ®ñ%…2,SÄ/'âSªøÅx¥CæÜnžÓò&ç?ÌDzŠ&O";4}çfí9ö_}?¢ã•&Rtj3&Ð$Ô˜Fˆ{ò¹H¿Oó M¿-ÕtºÃß½ÿÁÓ`ÐÐòîõ¢’,ö‰É"ª«øè´r0e™|žKÈ©+óË ¼;øì—÷tqL~•0¼º½ "ª\ÍàÇcä&Ì€UÃá6%+ Ñ ¦Š~9TåTé_÷—üUÈ*@GWZÉ.¥ÊUG„˜ÐÕYÁÅ”Që÷þÆ™MGn¸§1ºWãNkípÕÿ…OsBYò߬=GؾŽßˆ¹ÝòÔ|/Æ2G4V>»Š~ÅOF/yç…äî­m:ôëИ®l¿äeÌÓ ïÕ†w°gß®~¦Ñè›éù¥GÚúëÎNCqM˜Çùðâ£û0‡_ævAæÓäT³Îm$òŠA‘H@sôìf›°dš½·ÿdÿ¼¹Îô¯—R~J²nت°v5x¼æ^cu5Ê¡w—þÍòj;È&m:RÞáÇîY]cG¹”µ‡òu,~±®h×Xvý^tlÄÕ›GLOÄ“Äå4VñËY«Æ:ô#·÷döx~¼ô7ѯÑÒµ¥½Æ®[íº“bGùOcV­«>ž$0‘‘è4kÚ•ú‚÷VøÙRÿ=š¦ð7­l'¾‹"ºTâáeìW^r:y牫s%úñ‡Ã`œ*oéeȼ½ëœaÝŽ5” çmð3î9e©B¿©)Šõ[:ksÎ- ËPŽèj¡ø|ï)Y3ŸFfù¾Þj˜G5ÑÁŽWlFj28‰i™D™"‘/*_Xžž-Mg » mv/ÿæÔZ½¬Œ<.'#3OT“Ææ›´ëÓÇqÅ|¯®¾o÷Ö—ÉKMËâóòSSRkéiåg²!àgeq¨Ivz¼Ì´Ì´·ç^»ìs\CF()hY íÑØ¬´k×6å¥^>tàB–¥•“f6‡‡üÌT@ŸÇËÈ.;'+/ªÛÔ·ƒËÊNu:oŠk¥ÊÏÊ:4kªYˆ8YY|>Ø©BÁ¬Ý¨£¯Û²Îµ}7­o-¶8x6[ÙC»gĈù547ùpýA~ÑÎÆžž¢Ð-³f®éPOõñ¡ Âü”t.4´fmÙnEÐ8ýq]…ÙiLëf†Ÿ®Gì Ÿà-Œ¿õ2ÓrŒ‡xšZ9Û–r÷¯mcgYõûQU®fÐu±ïfe;Aøý¨Ön;¡ve;ñg#c¾$àìñ-› ºõÌ3³|ÅÃYývÅèï=xéùñ£1Z¦Î”ÔOnÜŒ>Ç/íÚx• AW®u×SÈ]ºÂïÒ¡ïè4–ÛÒÃ=ÛÉ%ܺØ:§Ç‡[7 4:ÖÑõ½ýâêÖݧwn»(’kvõv E†ˆ“ù ½ö‚ U?ÝË­_ïÍóX¿¹³^?M´7¬Q$]œ¸»TûÏë¨tïIœz=á{ƒö rïÜϵ—C›øùi Û²á¨åÔ·ùpûn=¯¡»·©KIûw?Y¦õì ÙGØú–2^çè%å<¹ñÄÌ$ûY’ÚœY^\¾¦ÕÔ-.Ûuþ|;Å9u-ß~È0o*íÑV§¶7þèÜÞF^ Ñ2Þ\ôèjœv·‰j‰.«u‰<šuûÕõ÷úfºyÙý&ÌæÝ|“â…˜ëZýæµV¡}ÌÔU·Û}üê¾Çž^‰~« åФmé&:‘÷*ž9aN8#åâÓx[cMšZÝÑW;í?ö„²—§ì½”¨ÚJçíotöì»ô,úD\-¦ «¨¹JÆ9âæ~ë“RrõÚ˜ªc3æÌ ÇnMUZ­~Ók÷é§ŽQ5´óq¤éÖ ôyvlÏîÝ cα³íÄÇ<·mÚxìÁ£«ÏTëÚZ¢ÊCT™@ üi$‘‘‘ü+y{O{Ï/s-w߸têAê_©%Wï WïRµ\{ôq-½M»^ð°zerdÕwP\°»opù–-¹šÎCJZGüY§;– mÓk”MaÚi²ô0ÖCJÊk4´UQ:Ъäd,ý†´(J7éÖG2Þ/ÏÚ£ÖóÕŒÂnмDÏ›=_d J«2 Õ~ÐÄö…iã‘ÅcýµÛ›7l_lDÓ´êÚߪk¹óE—±kd׺l®†e—þ£»”Í3mÜMzýSN.UÒŽáÔiS§Â ×&Åç}GïQŽ¥ÿÖ=‡Z£ ²öÞ½í½Q] ªL ª"\.—Ãá'rss³²²²%äääHÒê“ÚWOOOWWWGGÇÀÀ ²Ï£ªóùÓ+6³FÑDY¢×÷ÎŪ6m©WùÒÀdŠgLaV™>1ÿ•é Â_ˆP(L‘œœœšššR•NLL”‘‘QPPP” P„4-ýÔÔÔ444”æËËË'¨+û̪îÇø]šÜ¢õAUEYnAKÕpá¾Uf2•üòUÀ~¹ |]ÞGLÝå8Ûÿ˪þyU&üDâéªEBЙ W¡¾Bxý(*(`’•¢Âñ³…‰ÒŸ<ï³Jb¥ŸR¤‰åpppÐ××§Ô',Ðñ_!§W?bωÊöâKŠÖóξ˜÷}Ã?¢Ê„߀0³Â0c#zßÀÆ™{ìÇ%>¹rü¡°[`3¥ŸßùôFÌŸ‹†‘s?†´¹¸4l6»8!mdæJ¶0çP¡°–––f...¥7‰è_ƒ¨2á7@¯ì܈ÿc&ø× gåro“9ï2åM5™YW·ô]`Ø#°Ÿ¸ê†òòÚôGÜu\Œýi§þTARR’4–¥‰¥ ‚Z:®!A]B鄱±±ŠŠ “É”‘‘aQ:]ÙgF TWˆ*~d¥µŸ ‰ø „]—Ê«2ûÙ ¯~º}¢–ºë»Û&?øÇ¿UÀ'ÿ«±+þœ_ÂïÛü¨01---22²I“&-Z´øþÿ‘HÄçóe‘æPŸÅ¯l¥‰Òop“““)}•¶ëééIŽŽŽÒ´ŽŽÎ÷M ~5D•ÿRž#r#âÙPPG«hSSœ)üŒÕkñ< úìzEúžÞ„c·À¡Á¤>ƒað½UƒïÆþóÈ,KÎíÑS²x¥È9ϱâ0^¦BËA°øæ‡N`ÙÜz‡Ð!¨aƒðÑ¥oO…º>—ÝØ¾d–ïð ©t…&OGF^õo^_Úwôi4¶œD6Zæè s‰ ä¾ÇÖð$N¼.“¾5zÀL[lMâèfœ¿¾"y¡«~p¤oÃf³÷ïßàÀèèhJ·lÙò+JÃáp2333$H™ÒÓÓ³³³ ½4­8M}¥"AUUÕÂÂÂÉɉJ甞;‰@ Tˆ*ÿp>£™+FH}œšŠy7$ªü–¶p›‡ecp} ìëáé;ÔUºù w/À ¡p­-º}c Âý0í ö^@S}<>‚I› ×.fÐðè:xÁ(ËÇb漺˯¿®ïÏ9Øe†ñ³QSù‹{3çåÙžÞ7R5Ç î~˜íâ—¾zb—æûB¢¯-iùr \¦cÇmxhaýœ{óVˆ¿Š&-aŽÈE%cÖ@œµƒ™htˆ^â#S¼ˆÖƒâõ©.­Çê~ÿêêLŸ>}ýúõ”FRzLåPŠ(¢ó#ðx<éàÚÏe‰§2©Âõõõ¥ã|(¤ *œ­W¯õ)}Gû…o’×·Bµƒ¨òßAÚ%<׃·”Ðed’Å™ó‡àƒŒ„2à=ÍgaÐA\é‚w`5u%qmøN$|s~ê·ç1w?¦Æ ƒ…x³Qøñ ®ù"4 _ÉÄñí)…Ô‡î‡|«4EIõª:Ô¾|]¬RÃÀ¡ûÔU3C´÷…„/Æä[¼ÚÎ:j,^Îbõ&°lÑÎJ¼:òÀùHÊ÷_>Ÿ}ðVz4eŒÃsIÙ"`‹a’ 5Û¶ƒ ='bB0jþü›Ðœœœ—/_®[·.**ꋯD"Ñëׯ=z”——GEº¥GÓ®•B¥åäät%HE×ÆÆ¦eË–ÒMJtÚ-P!ªüw \Êoб#E°:¨Ù¸Q6|½@ˆ×¢ºÊ‡Â nú˜?ÁÉè×ÍaõÍ’_]G =- 7i y’M>§0Ÿ¡Š†nX÷ðMUþö æØÂçtW½Ì4*´˜R_òMë†X‰ý0°:RMñ¢O{îÀolÉÞfM`RüF¹Ô:wŽAÀn\OCM½ŸðåãÇ”Ÿ}[2è*77W Uá§ ªüwvGÔÑÛ5]1½7vÇgY4­qè-‚ÌJ™fbÆqLë°Ð>cVàxºxB¬¯-fîJÒ&ýƒƒ=+ø¶¸·/—£Ïüïø)JfÞú¹ÛröxŒX ®Z¸tÂÂhñB%trÀüpd–«r ÌÔ·ÎpQ.ãÄ÷Öw/AMM­£„‚‚‚ôôôÔÔÔ3gΟ¨2@ø)ˆ*ÿð²0죠-ÂòýÐ턦L4[ŠHW o„)¨£‰Ø7pé®-Ät9 jŒÌ»Xž€Qž`~]¥Ì=0²#æ¡ÙyôñOò9½z‚΀’R^!Ù¹‰˜ ®/"¾7~׬98³±z=Ü àâ½ЬÓ/ÂV÷3ë1džØßA‹p´34L0s",´À;#7n*âý;8| ^âÂ+,íÿ€ Ê/\'¨Cܽ‹õëÅéW¯ øò¥Ø *t>Q4Œr‰JS!µäí/ С^¼Ì·Ôm>e§«•Öÿã¡Z£å6oí>ÅË´5¾›^ÎmOep’¯÷´nèËR{5\^^¿_èZÃëQíÙ½ï^\¯‰üKóƒ›y5L»”0½‰œ¨àå WçÇsöî[n®U81íçC×~D•QåW»#ïºôéiR¶»C¹ãˆ†t÷_2Ý,áBTùï  $]¿¾ø_i||¾´ïÕ«Ìæ7V’oÓ¶$ݾý—ß™ã#±ù…Ý ,Š'0‘4b÷ï_f’½4ºº_ž‹ õO «1üÅpxBˆx…“ÞÈê4èÝIuÿ•¹¡—v*íÿèDÕì1È!'æÅ‹gŸ=6íZÛÖZåÙΉ-z.Ð Ý4ˆ½äF櫬ƒôÓK…ŸÔ0Ð£çÆ¿NÓ]µÜ×V•ÒÜ'#Ì9S«¶)ïo?v§õ‚‡õY±'yûKï1EãÁ„ȸoÏ=]×{ò.eC£üØW §aÿ, âÞÜÒcôV5scfêý»²ÓcúÆì ëq«–µ!'þ6ßeÓ¡¹®?õÇ­âU&Õ>Ÿ_¼¤‡tMhi‚ÊLKKSSS“.­¨¨¸xñb*ýâ¨@M¾pÊ÷çûÆ :*³'H¹¢1ìOS àÒ¢ ø÷ïåÁPEOþž£]»Í_•­cÛÄzÝš·ø)w‡ ÜÜ÷ÆË¶j—ÇÉ7íÜ k–å’î“w«^K8å¬ÎÛ ÷÷^—¥BPmëÝ®ĉ¢ê0pxß5ë&³Ä~ OŽ ˜}ÉîUÜæZÀîÁzý}?)sÖáìóÏÖQÆ“w5X æ}†[-Üî¾xW¸m/µl:û·Xyê†xÚ>Ù:ÂGÕÛ»¹V·‘óÆN›sl\‡Îg¾}Ä´”}ýnã[.ˆbøMÙÞA_ÄOŽhª7`Jí§;¦ÖõÖÜiÁsMëàÑ«õLn±r_ï>µÑžÇÓZPNáì`ÃŽýG?¹°¡&=yá ñµ½ØÖÕx•­V'ýÓgê·ÑÍsÚzU“N#ükZßÕ“M˜Ö}tí¹W–8ŠÞì2°é>©C;«õáo½ö¿Rø´o{ ðnbÈ|……ÿôT÷Õ‰3´Ÿ˜a :@T™@ T&B¡Pð222’K‘””Tœ¦t—Z}}}CCCéHq++«fÍšQ J‰Ö º âçvTZÌäå±¹"͙璧4×.m âä%%%fÅž ¾D¶ñÜuAšÈãñ©i?öfV(  ¨oª¬®Üs\ͤ›ÓoåYG(䦤d5kj!ûÅå«N½VóØê,™)–'à7d „%mÚÔe)ÚøôÏ… µG*æf$ÐT̬6Çp”ä?ݘºdß²¡--]e4n¦xIq!ŸWîiO9$,U¬!§ ®ª$#ƒÚZà}fa-äSæ ŠâX›ÆÔé9¾Í¸~÷’sxj2”GP¨aª¢¡Óu„QÎÓùÇÒm¦Ô)|Ô¬¯»á•˜Ø#ýôØX47–Nê£e¦Fÿé=_œ¶±–Ž~ë€HÞ´'N}¾–BrR²,]E_ 7Ï~hdXëÝÌàu»»Ô«Ñ%Àøl¨«³vbÐñz+ÔªåÕî{ëÌV7ˆ*„ß ‡Ã)½&tñ*Ñ\.õd/|‰Rï ©M‘HÄ`0(qU“@I¯Z)~ù"•B &ü4Ë+ïÑkÇ^çNܞؼ]éVoQܳõË"TåýæDéÔ@c*XÙÉàQNR_<}N‰eaèö­® óDŸoߢ!CÑhãæ/t¯ÍŸÊe+èü¨ÇÜÜL‘€{yóº$>G@£µY0¾¡œvãý{g̈˜h4í£C×°Q¡¦ß/èK*˜Õ½LU#d¾ã¾\š…“ú²J ÌÂŽ#”°Ò¸yù 9=G™ÑÏ'»ê#íæÑ|ÇI-ËO¯/JÍCîÕc;3ÈS7D×1³ ›˜t¯³ ޾´‹ÍHšiðØå+Æ4Ø·IvæÒ¾®Fãff.ÛÕæO𝇍2@ø·¤¦¦ÆKH(˧OŸòóó©øUºH¥===cccggg*­¤¤T¼&4%Ã¥ÓÿýâÐÂñr# õ‚wYÚ0bÀj¯{#›—,%Æ0w™¾Ð@®ôfFºm~ð)«©SÒ#’†øˆѽ—øF£¹Œ*¥¾z!ãíT‹¤ŸÿJ•.—Ÿ”Pµ¤Ö——CFQ•Æ3ô7: t¶C—Iû¼C²5iØg_÷«Þ¿å2òÒ?BWEõ¥Êj›ƒó˜Í+d!ÌŠÉÊ+*ÈÐX 3#WÚuêWg¿ CFÉ+òÚ‚åJMÝHŠ­z v/½ˆ›Æøˆ-#ç¬y°*Ð}bWÇNiýÍZ/ØØ|ö:ö‰¾¡!žžg~r*ûª QeŸŠ0%æÉÇúvFU¿M…†”¤QrU¹ ;R‘+§Å›”²fggKcܬ¬,i˜K%¤!/ÇÓÖÖ®!:777= ”Wâéü LñDïEC̲×`ÜÄi®¬5’´Š;bÓh凸Ì:å·Ó®ø®c3{è(þíxoG‹ï÷…)!Öբ᪵<03"úÉz¿ú2 Q›-§hÚÍ­æá£ /$µõÐeò òùE»*h(“Ÿ_À¥§¥s©RÄÅvmlòÏÖÅG?tnWKžÆçæC†õ~åœ+-§÷±Ö2qps2>ÎåƒN£Ñù”b E¥û+3©Jh…Qµ ÐèÒ—!îãF«ðWCgJëùGV·ö\ «ÌWl^T äkùxkÍ<ú4ÉUß„º¯Ï¬=¬ìØÁª¦‚ˆŸ³bNd»ˆýá-kQ6ÃÍcóå™Ôe÷».¾²ºžµún:v¡cýnªr n^®ˆ!ذñP‡ÞÁ†ò*nþmÕÆ£e?>õõ‚™>ò µý;(¹].b¯ÞU&TODx™„Ú5+øJ_ f H¿Ö¶ý›0ÁýÁ¦g5Ô«êÍ~ðàÁ7~þüùøñã¿[Æ(MIù{gWÅò·ñgOÝ%a ¢va`b``Øbc·`€‚Ý…ÝÝÝ^[QPDD®ÃÉ}wÏAŒ{ý_ßË~?¸ÎÎNížÝ}æ7;ñ™2m(µ¥ÜÊ<Í_øY¨†rX,622²±±Ñ¸ó¡‚ýÖÿ/¼^µ(dû+ÄíšÈì9r|uc ÿƒWT§U¶ß0ï2Äó«*%y~öŒyî}Ættú²D(!pÜp÷îÚ…Kû÷< k‰yDU{ù_®¿VáLÙôà ¤kýéyµq­$2«ºcro_wÇ@[3C#'×.Óf èµró_i}»V¯TÞÖ®’ƒe rÍh¶“úØ7XËqK‹¦õì…Ùx1gâÊMËGtÚâ—1`@G»²æú•ûLšÓÓ\ûÜü.ªª¥Lý sñ, í´f ¬fwuýСkpàTãܯ°¯Ž-Ûrá!Þg-«:¤:ÙöÇÊpYè¡×ZH7º $mßwׯKmáK›àÙ×´™Hüǵeþp-6^_ã1â7Ì? ß·}óª\q… Ý GÌÔq“7;î]FÛ]–ÚðA*HC}ÅŒn5W«Ç5±¸ÚöMûìÜjüpí‘«9¯b—m²Ü«KÊøXգ߸ª‡—ZëÙUuŸ¶`¤ðíñέwۙ맽{ÙhÊ6ogÝñã6mµÙÎÿæõ€Ù«œ¿×“ïÿꋊ¡TC‚$¾Ó«òí- ;†ËAE|>0Õªû®q»¯ ©^ÞδvüU]GX/б}¯ dIS‘~/.¹qãÆ3fÄ«§B300P(ßU4‘|7A”EK©»¦1™rhº(k¶”¥ °´´ttt¤”Kmÿ]3ýßDXaø¬PꯠŸËÀXr`Þ^‹¶Ý|‹ÆÓ eQ}|hØøïeâìµ4Éké—}‚[·w@dï€Bôkx¡¹§³^Ÿ¯·éP^ knš¹=/ÔüÀ¹.m§9»ïÍ)”Dß‘_M ˜u$fV‘òTô˜pÙcBþnÈU2$Ïx#¶¸ÅÝH¹M¦¬ÞФÐ2l]ç¾ï:·€Q¾ÙÈ3ÍF~™`ñ¹Æµ6>87 ]ïT¥ÞnkQoë¥AÁ]GÝý<ª`HÛFÃnG +èã¸h_ÿ©…^¸]Lÿ+”Öç°´qwßB·<¬på"²ì`…g)0«23 mŽ&M`’7˜*ܽ€gï¡PA¨‹ªMQõÛ“^Å?Á©¿ ²DÃJxùï¡kEÏ‚™ñ =Fb6,«¢eµÜdoÆóX¨Ø(Sn5Á×ô_ÍÀåKˆN¦¿Ÿi qr'Lca‘9Iòy|«f"œ‡uëPÆîu J¸^ë9·Ž¹,ž×¹m\|2§Wý‹æƒ7žîÙÚT™„ƒÇ‘-@W$½Æ‹÷`i¡ns˜æàî=D'@lŽÖîÈí¼«Àísx °`î€6õssxráoé5>Ä:ˆ8‹bì ÂOõU©T÷îÝ;zôèªU«RSSóý333óu‘ “••Eù»ÍÉÉ‘H$š†e;ËãñŒó D×ÙÙ™rP6.µý/µ¥ zΑêwõ¿ƒ€R¦üÅ"I>\|&õòìܦ –^Ýz•E©`&+F•Kzæ8;;àn y$ÞôÂõ†èÞYîðs‡" ËÇ`L#<;ºWµpÏ3 ¯ÂžYØÅʼnNßL\ÛâÕè6¼QF¯.âÌkT© §²Ðã¯3¸UQf%m°N…Õþ`Â"TØ…rZ”q‹¶Íð¾VM' ¾H‰×ß–d SÊê„° ªV¥O­0IwÌœC”ëÛ\w‡¼S­è]›LKÃÎÀ^Ëë£VÔn‹še‘…-'P¦j9ÃÈŸáÈ 4 ÆÙ1t"{†`i6–Œ" &#âxÀ².zŒÅëQNˆã±æ=b£N’7oÞ¼råÊW¯^QúúÕ!™LÖ³gÏŒŒ Jªuttôõõ)ëYOOO¿”E«««Ëårù|>/|wé5vÿ‹ˆÄb6XB­?e .U›à‰µ¾´8¾ž*úÙ“ÒLÝ&~+¤ó²˜š§VþGËøyŒKÆv°ÐƒÐ'·R¶-ÆžƒEyèraÓ#ºÒúuBÅ*v»Ûbß`lzŽã—ÐFݯÑa,F”xŸˆ àLÉd† ‚ºu«™.>ò±y+-YŠg°¯…mû1Ì ó/ ÃA4SOÄch =u²·¶âÌ„œECu¯ËѾðZ‡Ä¥(aFjSkX‹Á3GݺEšY°HÇ~çÂ&eMr ŽvÝqeô6ß>ïìí¨›ÝÙ™¾ã‡úÃ[mº·ó@×ÇØ·uD´ï錳›‘9Ô‹gÎfô<ˆFTòu*  eùÌ;§ùèÞ˜Ž+œ„Ííq,>_× Š',,lìØ±Ÿ?.!Ì„ 7n,‰JÃP* £&y ù¤«˜Ñq²Ë©Àò@ÅÓ½ï¥èÈú¹N8ǧÑÏ.¬&0m}b_Dÿ¦e‡(¹*©ªbËÁ7"'WúéQå¥F•KlM{‘)‚û@¢^p‚Ìû©e…&öØ·šVå­÷Á«€fyC ô=°ºè‚QEQAš;0­ÍQ™ ¬AJ•9Úà ‘“Né0ª±o2jd£;œòæâÎÊ¡¢B”7°„îä)…ìoœ¦£Ïù0ê?Å•EYò¼—.x\ž_Ìò ï!=ƒÊn©6áÌ„æTka™?ô¥tI=<ÕÅ”ÐkjpóÞ#”]J]ÍL)~Ö­[ÛØØ„‡‡?xðàþýûs×Ó,eï2’Ì@C”]rñé’Ü?bꪶK/}Xúý`%P¡¹ïõ—ßü<ϣʥ ²à7¡ÂÏ9›‘T1tßfJ+|—É‚îOΞSh ™»à1•Ω¿Ð¿3F÷Àd´ƒ°ÙhÜ3±jú.‘‚ËPw,~úäŠÀi:oÿGðíªÂ*ÔƒBs¯Ë²tƨž/B»80â:hc†+9å88°’jhû£¥100pU£T*srr²²²®]»¶{÷î#GŽÈ幕žØØØŸ=I†RÀ¿/É ÿKUfP£T ' „5>BIÿx÷ -Ê‚œX23çbckŒª nÜ_ƒj ázJ.Z¯Åô>ßO ßYq¢ŸmgËGß{Ÿ@‹S1{F¸ae]„Ýseôj B‡æˆÛ‹_˜º@3eEg5”Ïǩɗg†R £Ê jd™†ž´Ö5³Æéó8ƒ.eþÑ<’á3Ý!°ÀŒ]xzÏéAH‡Ç8lÈ€çÏtÈ$U Û×6%BC1j ¸–˜‚çq÷1PÃÚÃfŽöþª$ct”šjjüüü(úï¤ÃÀÀð€QåÒ …Fäô»F¸ÖŠÞ›°›¬Ð»>^ÌBG(Sð,#:ÿݼ(­[æ…*7ѩޟÂ1à€ºæëëx“ƒžbØØ‚E)eëaiJ4Õ-]ð~w„).5 üG‡X°6 ÆÍÑ£ žŸÇË8lô¤Ê‰#oß6sÔ¹0°ït¯Bd^œ`ÖlE÷µçÖiøwÊ!”ÐÜÎÀÀP*`T¹tpe#’¬Ñ™‡ÀµèÞ¶ê>KlÞì÷$¤Â¾%ÞøÂPÓâk‰çéXµçâéyz¸n§~%%þöV=C¿V[‡žÃ»Ù®påc]¦öÆò¨Ýª·¸-82 ë’À5Æ®»p/OG¯16ã/kØp!ƒPâJ*_LJ(¡{³g0ûaÃ4í…j5¿î Š,G›~¸yZ|8¥`c2º5ÇÁõ(?ïVYŽDÖÅØö˜¶»gÛåOaoáaaË x_E};R)¢'i޽NàÆ£A|†®–tóþ3¡]Elæ+ ïèré ÉP¤[bÂx$Ä`²íÃaËEæST1csƒ99!%ÇÃÅÃý1œhK(/cÙì’·sÃ27˜›ÑÝ¥NGÕ¡h=ïÞ¡QClõÇË—¹ÁV­Â²e¨_§Îäút耧ÏàÈÆ“ˆÚI÷ivp€L9uÑ÷>âP’*ëØ hëO]NrF3ºœÉÚHŽGUlÚ½}릙hçØÍlïR¥ýác¹1ûzáúè ¾ƒ^ùžˆ@ .ªÈJWL¹ÈRñq8áÙsí).{ði.‹‘d†¿£Ê¥ƒ·oqê$""ðáüü0r$®…ãv<š‚A"nÞDÅŠ1OžÐ›6Åàñ`oM¯`=C4İ£à¹aä‚b†'2Õë±#: ºwÇûÚÇÃ[·Òž©©tîÝÚ5ðôÄ–-X´ˆÎwŠ'^V)[›\ w¨m}̯…£áÅ|²•gß¡Å/ôÒ& !›^::HH ‡:5pÇO´O£F8z”ö|N_®×¯€pä†ÀÖí6ü÷I;têÇ/U«N=ÁéÆN쌨[+V?œ¸|1—e†¿£Ê¥ƒÐP¬^M;¬¬P«6ÎÃÓ=ô4·`òLœ>…]»0A=).IââEZ¡)eŠÊL•š„À¥í…¼ÈDÍ.V„Ð’¬ I©š½Ì;¹Ý .¤ë”ZÏRÏÅ[«-½óç£aCZ’)Å¡àø¡S'L‡OïPµ º› ®+v4*<šK_ûþJÕ(IÖ$˜žŽÁƒÁbáÅ‹ÜrRµ„éÓiµ^¢&Z¡ýG•³zuÍ\% Ý'_¹8w?áSKb"2âɻַ’܇áNõÝñÜÛoÖµ`4™áï¨ré ¨À*íè¿Ì9È–Ñ‹@ðEhYãËQmºrå×)LVO¿Ïå}íO1nü—ˆëÖJªJ¬XQ(¼—ý—O…–8ùˆžKD©¢’祑÷úWZ¨œë×*gÙ²_ŸuçÎèü¥›‰-dGüúeB¡¤‡6³8„XàÓuÿ|YJ)Œ*—V´ôñ‹3Úþ„ÚôßO#¦—t)G¥Hüø.6!oTÁÁZGšüáíÇÄ •QõªV²ôÄØè÷®aÅŠ6¼Ÿo7!å’ø¸÷±I9e*T1Ñú¹ZivR\LÜG©ŽMUë&ªý(’Ÿ?ý`RÞÑH‹‘Œßs‰~’TJR¬Ý9¹×±§ÛÚÉÒâ…ŒŸ´«RrÆjž"ûüØzã?t~þl§ÍÏ·õ*UƳ55Û¬¸4¹©Î÷#ˆ*ÍH;<§þlQdëˆŸÎøGx½¤rµ…3öGÌí\ùNó»aT™áG!Øü2NõÊrñÙôº£vÕjUµÇQúËŽØÀºš!?‘ÿkºÅâ‹+¸TÔ‡ˆóÓƒë}[{cAüo{ŸWšý¤›NEF’ÿ0ªÌÀÀÀ !ãñ•ËÏ?f‘J¥ÀÄ®a£úFÄÇKgn$J¡T°ìë6¡D÷»²¤’¤Ü>}äbbšØÐ¶Q‹†¦”F“™áW.½ü$J¥oWÝ­ž}^W·¯<ŒJ¡ìoŽŽyÝfM Î¥—ðüêÅðxs‡:®Õ­ó oÉÛG®Çè•1Er\\ºÊØÎ¹Yc§ÜÙv‚ƒì7®„¿Œ– ,\[7·VÈþüâ•ÇR‚­"´ë¶p³Ö¦2—¾¾}鯷iT­Â¤R“æ.¦šbÂ/Ý|•@°xöõš¹X0Ö•in߉ø$sÒ/§g©õöîÉ;¯3l\q_ß}ù)ËÀÖÙ­Aaë¢J9sî–„-Ô#æc2_×ʵEc+1G™wãÚD•^ýÎoïÝÈ1¨Ù¤¦R#Ï]~*U@`êÚ´‘…v®$)_ž¿þ$[I*Tì··O&Ô›1Ù!êÊã»ÖÝÌc®ßz¯Õ®] ,ùÖ…‹Q) žŽ…kËFæê˪ʌ>þnšTA ͹¹šis3b_¸õJ¦Tr +º»¹ÿøUføÍü Þ00|Uâ¯6!Im¶Îë ­JÚ7gX–õ£>ì³V±v”Ü hÞbïõÛÛŠëðø"+3þSiÁ“ìšÛ{ļ¾OoÏ3ɼ9dÜé9‡Z~>áÑl\Ÿ]çw0)Ýç×mô¥r[‚¼Ù§gx…ëEÏ·ÿò¨D^Zµ÷­ÛÂâ‚OGÏâõ·%2¯5#ÚòÒoòô5ï±åÚJ6-ʬäG·ÎÜi/ý‡huØú(¬;õ~yuÍ…—õz»Û\Zï;v[LÌáAÏvÌöX%ݼ²¯Îû}ÝGeE\ÀâÎŽu›¸xºÖ‡Ó=뇬}|¡‘^Þµ¸þ¼ƒ×o†Zq $ŸlY»¿íÈ­£š›ÞXÖeʽ– 6¼Íß+1ì¼W…ìµ´Ÿ¶ Ý?¬ÅbÐæQÎü‡Ohóú\3$úµrKh¿dPSÛW^ÐuìU+q@ûA¦/ªg. ÙQ¹N{í_žÿ£Ê ¿U$º{aÿ- ¾uuþíÒ|‡›»1g&ÎÆãÒ#4±û·KÃð3$©R©H5_9TêÁòÝÔV*•fff¦¥¥eee¥¤¤PÛÔÔTçãCÏ«smï aY³ÝÔkiM讓æCÖ–w±á)m›ñz•˜éhdPr‰tŒ;÷êh xTÑ©X£—Ï¡)‡:V]¶³n}Êú4m%šú×õ‡Šî9®Ï™w)àížV¶´Ië0»çIîò¥,67#|é‚sŽ›û|Õw‹«ogo¥miÜ¢gOÀÓUn;eÈÞ‰q=mAª”ºÕ[Ž:ˆ V#æBÃ;Þ£{Y œë°©-ìMuX‚wͦõ=ƒׯœ†k°[5gT«tÜ2N=R0~Ne5ÇŸ÷t«CH,ª‡Ìº1òùø ¹¹²x&åË€Ãf$Cëòº"XtñíÛœ…}RŽŠï>}>¬y™/sÍrÄ•ËAm{tª¯¶Õ-:µrlê!ŠeM k6nñ’–.?Û1rÈÅœÎqã<ŒZöG·ÙzïžtÑË:<(ðR¬ÝÝ1îÕX(3qÖ Ç¥¯2•®eÊêòa1z]@+v¦7$‘ç‚7enORS—]s^×i•f®¿3wfÅÓ›n¥ÎYÕ¶¶3¿JÅ¥Ù*ƒœ¨uç“ g5r­bÄ ²¬ª%ü–ªþŒ*3|å'¼Ãîç{G³ÊaÛfž=ë”#”há’:«S×¾ý»wìÚ½£)ç0ÿ÷À¨2÷YÜ1¡XYýWâfKPÜ£šË$7T8‡‘¶¿X°’Ù=‡»co‹‘ýíµš²Ÿ£ÛÜ9ö?zTž>}:eÊJ„._¾ü?Éð·@© u ”=š‘‘AmÓÓÓ©-e¤R¶iZ”'Uù ‚%%%Qn±X¬«««ÙŠD"===¡P¨¯¯Om©: µµ±±¡ŽjBjkkS’Ìb±4Û|GþîWîï–Y!ËÄ—¥?5ò›ØÐ~«þ¸c‡ŠÓ.‡N>\Ì”6%@°y`©”²;k‡4››vòîú:ú±×§MKÐ,Õ’“¾€—;EëµW!ãÄ_¯kKVšÜ÷j YÉYpŪäRåWÞÊÕ×±¦`Ü¿Fî¡´Õú.Ž©Ò(ÐþˆNó'Ö˜ñâž¿IN†\§&=ö¶!Ud¿A£Y¢­ªÈïÕUހŕgʾö—J¤*9ÁÏ«šŸÍ'Ó$”³Aÿöüå³vwngzgÓ<¥›‡‹é…bËé X±gßþelúö½F¤KUcXÁ7_·Ø4{ΠEs/;rÒ·YcÏm ÓmúŒfŸŸp3ùÉÕâÿç0ª\jÈL»ä(A°`T6ùqñшM¤gá‰uë?àÈBDÇâ{húY°w‚¸Ä–Ÿ¤ˆ‰‡œJœ C Øæö2ÑÏð94Râð( ÔËCÇvvàQ&D,vnƃǸ-ØJHIÊä‚¡ ,´ñù’ àЧEÕàa&¦}(#ÂÀ¶VøÁÆ2…B{õêÕ… ¾xñôŒ&e,ê/BY¥r¹\³U*•T4Û¢>ÔVc¼Ræ©fKé¨fKY«·”S!)}¥ŽRªI£”¬RÚYpKÙ¦ööö”èꨡ<©`”ÿo=ÙÄÀÜ n?—£zþgÇijËoÆ»UÎX¨äpñ£šœ«R© 2d•êšœ:wTßûT#KJˆD,Z2©„´,k ²V>KWZ‹¿z k·n¼yeƒ 3Cö›ïY©¤œÞ¿Ì xVå ¿YŒ'«v$k×ÂØˆˆ×Ób©O!+3Dz¦gèÏÀ+³ šÌ ÷ŸçRÁr‰,ÇØØäŸoÊN‹KͲoTîk¾~Y]}y\„ÍèK %$Ç[ÔUCržzlìþÑóû< Vº {zl¤n¥š8°¥áÙ:&ÆúŽ)ÒR󎣃¨¿uÝt§î»æÛH_¡ç4laذK¼*[Œ>ódhùüÿYU.%¨°#w4t@ÔlzŒ5ÑÞŒ–ä3+0x|ÆÀš‡£ ±? —‚ÈÁƒëÉ‘ðw°r,I•/ÂøÕh7õðî>.¨pi-íÏ!ð`/6è )§÷C«=.n…(WNÓoŒ÷OhU®è€«0p-ì½q| ­Ê’ø4‚Q –6ÆÑ ˜s]‡Ã’…¨¿p5ƒ±¤[I§ùê!>A÷ îh¡±UQUí ñðî-%îîÄËÆ¸±P} ‡vÁ€@|öÜF×aذñà(¡~pF† î¢{ ZûÀ­Êé©ñ@´76öÃÑ ÌÙƒáã!T ò6GaÞ Léöa¦4oûö퇾}ûv|||¾?eY~ç÷,¥£”éIi!¥ˆ”pfò¤$3;;› @ 'ŸÏg³Ù<O³åä¡qs¹Üüm~¾Ê~¥,W~”¿@ Ðl©ð”-ËUóã%ÿ£p¬ß©žNpÏ^ûBG:™‰³’â82m}^^:ŸÚ¬Áó“ÇÞ“B-mÍí$â`s5«‡²„.U§T¯Â¥T"W±µÕ¿úå­kžó<¶ (ášnܹ#Ñ3+±_œ~˜ s¡ˆzíê•÷èViâ€>3lœVÏVO–ö1Qfa)°Að¹J”·wþ™¶#fx{¨X¤¨l¾¦’ Y{85GxÖ¦§¶òÙ,^î7B‹ªÓ&¡µ 'îþã—tù7¯ÝQFT (lÙÂræ7³$DÆ: ”3¡ÂÕŸÜÛ¸{Èüm¶µ«b’ñé]ŠØÚZ»`ƒ˜zšùBu×3¾€²ð¹BÅɰÀ¥|Š^P[[Ýûõ…7SœLË-[È¡lb¡æbÁ`ÔX…æí};¬—ÒŸÚ×4hS5ê@äšÞm—ÇY9®Šˆª)’7.ß5o[›Ë²©¼ò"•kÝÌtrßÕgWŒ°ÓQÄ¿}Ç-co¸¯÷pÕéƒCè+lhTÖÈ$çõšI{j¯›Ó „˜ÇÓq²*RMÿó`T¹”@é±z”‡žh„ lÚöc ÈÂb_8mÃô>t¨nå°¿1¢Þ¢q4oÁbTî‚¡5JJøÓ]ŒœŒ–—i¥'C±:·MA¢Ö@ø×¢Ý‰ãPÕý;âX7´®ŽqGÐÈ ÃlèCæcÑN(¢` þÀd*BBEÊèR s^Àg*šYÒ‡&×CðP´u‡Û·æXà ‰;L ˜µÅ ÷¢‡ßcôQ\|7µ-êÓn¡yÇ´Ð,©kê>¾h´MÚ`ˆ+WúOð¬~T<±u1+à¤n (g‹[éØ8–tn‹9G0qlÕ/ÆÍ-1`º¶€½~ñMNNž3gΚ5k(M%‹4ŒRòyóæÍèèhJM“’’4ͼ”¸jÜù ¿”e˜Q[J2)Tc‰Rf(å°´´ÔU“o°RÊZ0#¢èâ%úÿ‡á[5¼üðÔ >C«›OQBPµÝÍ[–Üà]gP{ÃíŽc/mg²¨{›7n…< ~ô³Brxæì6«û+M =ŠO¤÷DãY³&9ø¸O[UÙÄ #Kj\³×ÍËjˆøö“gìnÞßV/¨ÃÌu^Õ0j™ïîç{T¶ÝtñÚ„ÜìËÀ-Û Kè²i›–'!sóºM}kiér?ísqî¶oX» _ê:,ñlù°Š{&f¤gZ5yr’9p:pòº÷ɲÅãj×›^ïõ„]¯omººlÀȰ±»»{:Ù·œÑ¿aÙÌ–]*O÷€‹A†@‡„ÞäÃú«›Ÿ:ox<²s7g3 Áup¼y÷rë¼eo¯O™>ù)y;±/K;¬Þ½…¯â1Åûüæ¥Ù‡g„F"cõ¢#êw(S°e˜@ÂûJ|YJŽníC·S¬ìýíq3Î =cr¿i›·û;‰Ù¦WD7éÚÂbšœ” œ¾Ð«,Ý ]®×ÈN w.Ö'?¹awhxäQ ¤“#,/D• g~ ßÀgߟ¯’Õ®îõ 9oâ÷™=Fæù³ÍjŸx\ d›fîÈu×½7st1…T)Ȫc×>šÓ¾ §‰ûä[&çíÙyûå¬gÐMyÞ,ø§ÌÐ8:÷]€Â°¦c6\³¡˜yv®A;oíÌÛo¸?m\ž{ÊÚöSÖ*˜Ô:ñúVÕ‚éX×]q1¡ðœø°mëw¯­ßW‘?\;]e¹,z´¦2±a@ÅÁþâýÏ“}w ¦WÕóÈ#Ï$)¨¹éNT¡´ô=NE|*®„.Œ*—ièQ¬¾˜Ø|­? âh¡U,Àâ¶´n™ ¡N•ŸH9%š–Õ/½E è×FÄbCd¼ƒ¢¸áËK`´ û®Ðª|å&úÏ)>/£Š´ü¥%üDñ Bây „¸ëŠíçjÊNÌ,æˆS7TñÃØ­¸;þ'|©ø¬8&tÿÔéÅ¢ ÙñãÇ{yy…‡‡ïß¿ÿðáÃÅʳŸßׯ* 2Iq·æŸ•,- øÑ‘ù¼hÆjÁÌèüö›‘˹ÿ‘F•Kïvâ@"F¡‚>­9¬¼»—à"` N·@]d“°o„;ÏQÛø'RQ f!Dɳ©”ReBßqÅtš±€K [‡ÐŽx˜†á¶Å'’Lo…¿ºª=’d©®”DÝßÓPü%b>zvhÙA”¹\IõÑú•Y¢˜®f,ËPM¥J•zöìIù\¸paÓ¦M§OŸÎÎΖJ¥š휜Á¯-XÉðß…Tædå(å9YY9ZbÁ+BÙ2ʨÏL—+u¸??•–~ ׊[¼t›dF¨Ò£ÎyÏ95êà›ßQÐ?F•Ké±”~‚§™–HøEfHVÏF­lö(G-žÄ÷:ïX;ƒ“ƒw0¯n1Gó+¯ª<ºƒæ›Ô «MCVá{¯ÙxhµÂ!°è£Âš™_Úg.ª×+©<¤æ_ñÅî׽™“¢EýRØÔ³HÝ=œÈdò¥¬<øtDÐ ô˜†ñS‹¦‘ËÇs´•P£h.ÅÑL %Æ7oÞ¼qãÆÝ»w¯_¿žššjföq1 ¥Ï×÷¾³ìÉŽÙ|ðÞ`¯†¿g¼ÿßEú!üH|Ý)>ÄÝ}ûGv·àýìaŽGèÙÓ{×oY0—Å!H®‘ß©GÍ+•žIU.Ø´æcÆ* «Žcë‘¥G­ÐJ ŽÀõ“xUÉÖµÆèÙhVbC°y¸°O}Á²‚ã7Æ/:µ@GÌo†Ä¹Ô^¼C›vô‡%©ij“a~OüU¯»Ò{†¶ôѳ[P·ÄŽ(¯¶}ËÕ€keì?gG¾Î‚«6SïcøVÔóC'«’Δ'„ÈW‚ð„ ¦ §B#©ºnDp¸6@èb4´‡*‘±hëIWN AŽZ€³žÃÍæíÑ» ½«E÷sl¸ƒZBØW‚ û~p‡ høÕ@nlõ§ÞœGpŸ ÷™t§0R‚¹ÞØ‹‹7PòÔþA4P#—Ë333ut~jÕ †RIcﹿìß…oå2z¦ËßKCT¯›o½Â]PJ‰$ƒQåÒ‚a#ìõÃÂ-yóæ!: ·awSô¨†YKÑw=šYEÒÆtì4ÃLÔ®‹åãà 'Á#¿‘2¡ƒíÑbÖï@·´¨÷òU;;cóNl˜²½iy®Ñ ïŽAÓKÓÄÁçºccy^ß–!<ï](#z"žxôoޤ÷`fçïœ)Ïsfct(zôÅÔÕ_©2ÇWã1 BÆcÛª>•þlÜãØý?£‡ö‹ä“ÛÈÝÂý®cQÔì‰Õ ­½;ŠJz“¦lx8#;J1úoǬ>ô{D!Áûh|¸‡€ØØî‡~+.—«¯ÿ®Û ÿuU.%°pG…—°.ƒ6µÑæ.í¸üñ9¯ ‰º/Å€ˆy†7éñ¶µ+£ûB„=£¿Á¶´üvÊ$àà#Øàêƒ/­À›Pò‚óç¾lÑ gÏÒÂc¶¡Õ ÌœaØ nÖ†áñH;ƒµÚð–eòY:ñm»P¿r®Oóæ Ù¸p¦¤ò4ŠGC‹;BRÅtiì/€ä•Ü&úŒ44ïÂò;1`†º‘ìÚi¯À‹òX¶Œ:.²Ææ&3ñ(N}Àë®9áZ1y‘DIFHý W\½N{XXàãGÚÁçCJÏu  šÆ::HW÷HvtÄóç´câDº Ño1b VàÖ%\ªŠˆeX»÷ï£F Œ›‚ÑUq4®] ¢g¼B¹rˆŒDêyˆZtøU=|HÁ·ªT!¿<}Mèò8!&†vP"zù2ÒÒ°ý £Ë°bFŽÄÞ½¨Þ“æ`ÖÌ+–õ×*Côïb_#,j·NNxò„Žkl½!¹ópä¬Ü¯æTD.‡R N¯†Ø“ŠNÁÀÀÀPF•KmÚàâEÚQ¹2®_GãÆ¨_ B‘Œ> QA=^ÿ~Ø‚ûp cÒ£–ß¼¡íKƒVøÿÙÙxyËBh'’’dJM)Ù£$YÓKŠr»¸ÐrHI²Æ‡²•ëÔXœ[c>®‚„°Ο§ŒK ¼x~=ð†ãOØp¦Î°àÖ-:=7¼‹£ÛÛ‹UÒHŒI“èòhŠAIrµjtv”$«mh,_Žzõè‘RÇŽÑjÖÄô…jÚ³fQÉ=ç§Uî±1*,ØýPHW(I®[J¹äö§+ÁÔécÛmU#犖ô„[t²lvÂ3Ü7CølfEK†‚QåR% Ô@ižOÞDl8†IoA/ì‚!°1Ƙëâs [ð jaêÔÊuhÚ®)³à.Exx!++ܹ“[*ÊG`Œ°ÛHSÀ¼<ÊæÍž½V=/$Oœq}X\<½ãü{• _‰E‹ ìáÃBsv¦U¿ ¾¾ô_v0¾½[¢B€=Êу¦únߦ\¬Ôî…k=ÀeCôöÄ_’5©ŒéšÖwF’~F•K%­çë©Ä95šRQŠ[å 0 P»Áÿ²DßAˆz_•4ïÂòMPûÇA1000” £Ê  Œ*300000ü)0ªÌÀÀÀÀÀð§À¨2ß£Ê ¿‡ìi+'§ïÜ£pìUfÃ6Ο9coqñ˜:ûv §/Þ¯ÁÏÏ­ÏÀÀÀðë0ªÌð{YéN ã<û^¡!,Ï ê;ç‚I¸TÏŽ>Nf¥‚¯Cp~væúC™¬ÌÒbëð~!*aŠ€ ¨ô¾²\Ÿà6w ãÐI½ZDr ôô¾,»ÅPŠÈ|¶qùº±3Bf¿Êg¯…Ä&ƽ]ß?0¡úŸv;ÄžîkÕúф۵ÿã+%¼:½t€×¢›–ÈðjÿvaþUfø¨ùN6¯~ý±õ„¹«DJÓF ‡¯ç›ÿ#Z¶Î#Uk‡‰—Ý/§ Q~q»ya°#œÕc¶ÉðƒÍÐ/y-ÈßI’ÄdÙÿ·hUî;Ôg§ÿ¦R¦ÛxýúÕúËi~•ˆ}«/Tê5¬Ê/ ¦ÿ'x²!àfãq>èʨAµQ›wÄ—ÿ¥ŠéŸ)ߺæp§a>ºßÓ(“ŠîƒÛÕXt7ûþDþ.U.MÈsTR)=+‹C„‡M¿ê%™¤B6Ÿ edî!ñ#í¶ ©*'‡ž²ŠÅ%„""ßèUÊT9Pª&’¬¼'E¥`Wõ4ª èõ¢”/‚’÷¾4óJSi+YZê5£H)‘J=ÝWÀ¨ß&ŠlU¶l.ÁV‘rJáYO@ðK|ÑP†yÚÙ„%7ÙÓUY„H»¨)dP$©„!“@)G6å¯G9+wÚʳ¼–u—O»Ÿ\ÀŽ3˜“ b-°è%oé)JuÁù èˆ*2²éNjςT=W)O!ÿûõ+är¹D"9zôè™3g¶oßþÓñ~R)ÉΖ)©:K óÔL*¤Y©Š$d©irRs¯“2¥~Óî½9ߘ›³dÜ´md¹î:µò¸²—/¹Í暎.¹âI¦?Kž4RÊ-ÃÓ!¤Ýg7žl<Í‹Ž’™2Õ+3N_¡ %üÊÇ™„ mà©ÿJó™¸û¡ÞMÒ}&uÉR%ÉM_59Û¤ªÞäñvffР´é\Û2ÈüóZ¢5t©Aûjäëƒ §=+gà]Cù!Fþúž\ÜÄ$`µ–Ë7s$3c2CçI)mm¶+\«’6µ+]Ý(fjNÙ*}ôûlN_`¬7e-‡GU˜?§t¶ˆŸc+ܵœ-(þƒ4¡m­íÝ!aëu¾Çb“¾6El‰ë.ˆ ¡ÍÜØ°†FMÑ«:¶ùbe:>ß„‘ú™Xó¨±ÍàÓÁÁQÁª)Æô®ÅÙ}umýá<Œ»‚G×QU=[è…ah>5n¡Nô÷Ä_Çà·º H³0 mÆ#óÀ÷+Ê&ŽŠŠJOO'ÉÜé¾UšE5JÔYk®U³KIIÉ¿iiiš BùgddÈårMÛ>uÅ …Æ-Q£qSÕ©TªqgggwêÔÉήø¯ŠìçS†nèsöɨº–äãe"—¾‹ûõh¹w¸ß^âFêîºBêÑyzºÑ‚Ъնgñ™€º5X5=ûWž¹Ðjlà‚…VáÌþtoŒÏ–÷_ûT¦?\nÞy¼Ÿëɲ-}š¹øö·¼+UÉö¶F£¦Ïiuj•~ê½ c áUQWyg§®×ú¾Ùã{7ì».¾¦‡Oß1íOíŽV¨,F­ÞÑ»%U7x!¨xäôã6jÖïÜÇÖo•ëÔàyUégª÷Àî3ý#Ø,VzĹ‘cŽÏŠzÓËšn«9ì­?hVÅça3ÜÖwZW­ãï‘SˆyãÊX:t®oƒžù-Qlm‹^=÷‰¬ÖfðÐq-w¼º8yÖq«éë€'VUkÌêå¶ZðñÊèaÛú?Œ™èBŸà¾ËþK8<³íº6Ð~¦nÖ2±oÕÙµâ–T±ú‰Iè6Òt䩵ã› 7ë¸4zhԱΠ3ýÖ‡¦uC>ôÚuÒ¸R½þ ÎÆ¹/]3O—PÝ š5ý ÑÓ¬ •Wv5çöo¿,¸C/iû­—zQgõzÁýu·%ÿä÷¯Â¨r©€L¿›qF®µ·^n×HÔ±mÒÄõÒàA|z#\¡nåVk&¨¬—¹=D9tÅ·Íå7i»>r;ë*Þ>U²8WŸ¥È‘?{ w«ÔÍï„ý'sõò:˜hË¥ÒÐkKPi>Þ¦¤®¿Æ¶l˯ ­9ÈïîÍñ÷ɹvE§J+µ¤óYBúj®3¶kR·“9) ÅæZß>[ÍÂÊbå`×ToŽWO¡`m@UPð: ¨„õ‡Ñqs®$.x¡…04âH§­ˆÊž°Í30šŽ¥ôמ¡Ž¹zí(m…³9é¢Çhl™H+}õ"—••äääÈÈÈ7nÚ´©Øs¢ÄæóçÏ™™™”[©TækU¾4LãÎrJ¨,4n*<%K}¢ÒÌÉÉÉOI—&Í|·úZ’Tøüd5ÐøSq©Ô4»TÊM7_’d~vÔ.å–Édù§)Õ¬$¦Î:_2 ƒŠžŸ›ÍÖÖÖÎÿ”®«««qS[+·Z¦¥¥Åáp4q©0T,[( [,óù|ÍYP5þÅ"‹9}9Ã| $þÕËL¾„gÕóKwSn¼à6ª+Ì»È_‚¹§IuêE+OÉoŽÝSÖ˳k÷n›Ú*üC¢ÔLOEÒ ×vßÖ»ÃÓØd’óþ̃[ƒô÷¯^ ø H^©Ý³U¬÷ƒÄ,¹‹º[ÀiÑë«Â¢’!Â¥’¯—|¡›ŒYÚúb¨œçÓ9'Àº7b"_åpÒuËÊ#bÓdH{~ø™°uÊ:¹1 D-pmòœév½ãõà³^¾|É•¦é ÙξFçrŽ6æÁ£:ï²XÚ¨¢MC7­¼è*úþbg]þëÊt`©óMÓ±S½ýœqhÃшz 5g¥*~­šÿ¯0ª\:Èx¥Èdq tý ô+°2¯ËÒÁÿêÅ×e ˆä'ôãð­ÔѲ4 orîªW)&,–làV©‚ìkÒ u~r%†xi’ŠecÈÊ¿ *²U*ejlÑG0«ÁÊ>¢È,º¬ä‘w€ök\¨{ÖmD­ÚP}Æ+ ƒÙO¦¦Âƒl˜ˆèE)r‹g c *¹˜µ(tÔÆtTªë|}ˆ’ÛÀÀÀ’ ž‘±~ýzÁ§ ”›’ÍkOã†ú-XÐÍår5ºµ&åë†ÇûrKèëçyTùn*¼F5R—žŠ›/T˜|7•]~ ©|©]Mvw~1ò³¦ÿ9½Øä©±*H^>¼'}GO8oÓ&{gaÈÎ4A‹_KP–ü"ma^—}–ØYrd_ei["óTAÊÒâ¨ÊÏÓ¿n'¨ŸÌ$hËÖJ õð¡à£ z´mF¿ ç^ãz—ÓNHR°Jºm –4õ´Êóš–ØTR_æ(U(ÜØD~W×’L…Ù1woÜPf+Àê¸e­U7ŽJz }[­“— eìëG·ÄÑT„^Ó×èV. .<°×88tF»ªñzÕ}g­žß¿æ—(JE–< ÙîåæÛnëF{ ùùTðÄZß®šÿ†QåÒWL™@t¯®|äT½XÀ*ÚqJ¥ •rptJzÈ8ê(˾‘ŽWÿB¯S ‡Ú%•Å©%A½ÜéÞ*_^ tÙ¨|q½ºr2À¼_OÅ£ïx›ÆÞ«7ùö—ý¬Ö³@i”L¥xÍu‚zÝŠ¸Å„U¨­Äb»ÄR4þü½{÷._¾üñãÇEÃPVà´iÓÈß޶ýÿ…k`˨a§ž-ìòjOÊ[¸bI\̯%(4­ˆÌëyw·"ùôµÅ‚¯oùçHëhSO¤¾5ð¬y7ï†:Ì\²zU†ÇµqÞNÀûݼ‘%|Û  h±qy¤Ç¦+¡£®)ÉSÞÁTWÄùùËlž.O ~Ùî=¼ v[Ú")!UAêÿHQ}k=°+ÕoÓ¿{Å‚¡9eêOZZoRÐö³¾Ž­¦ôè}¾@¾\6øv]{x©&Fè@–‘žNÂè¿wÓ3ª\:Щ%°ENTòÖ]TD\!+¶äSuͬÂ!3Ÿ£‰z‹ŠS–|„å‘öìù×K ]„&ÈŽŠÊ÷ u¢x¡ U"׺²WeEF+³ÀÑ´ó½º,çqEå\Š>oÊð“ʲuøz%ÁÔHQÜ{GŒzB¿@ÕÒ y&¨œ¼‡…¹>ìoé¾:íüwZ7g~„<¯A;ë>RgkäŸuþ)Ä>¤N5¾QpÊ^ÔÒÒ æÃ‡»ví:}úô«W¯bcc •“ÑÝß ß²U+í1‹w]®=¾.Ÿ¤¥°tÍ=TY4wDÇöbIZR¶œàæ¶<Ó­$J—E·›[•@=NúJ%ÁΓ(ƒòí›ê. »ÿyžemç\~ÔªÉDc)Õ _Ð\ÖÎÅgìšÌ±1„-ësýæí¾µ}Hc-.™–ÊÒ5ÐùMžêg*+6ZŽ*òO‘Ÿe¬²\ºÁ[‘MÕ=)C˜ÇãR†2˲uòp!»ìx˜6¥¦.qhÅÇ6AÆb¤\º“7·žÈaÅ.¢ÕêV ¼|µÝU›y(hå¾¾uLÙ*YF¶\¬%6­Ô¾¢´]ÀÑ竺VaC–*ÑZÆ¢è×oÒ²ôØéS%ÔÕ¡_)b7oÇì]û÷h9ÉVŸ/ÍL•ñõ´åÌxÔS ·nn¢ýT ‘‘‘¥PRµu~3ךرlùÍê›sè|ÚFí¼Êößhßж,ÄHÊÈ¡Nÿÿë °"0ª\* Dö:CZÄùËv»*2*|Y⪗º[¾üü¬\ÎÞå—ãd¶£s‰éêL˜ÖwÉÇ™Ö&CR_d>Ï"Lù,‘:ý"4º«gÃút2ûñ¶LD:jÏ]™Ú¤G꾦ý@ùyZ(Y¶§NÛ*¹¹å Ó%ßîHx¨ås˜§Sâ#-–ò‡GäÍZ’¬2=ü1² &¶Eæ{œz ^Õ܉½´ÕR*Tõæéqø>ëÃЃÖcim,;Ùî@ºõ…I ´rÉËŽ„æM‘r£WaÔ.‚æ;qöXˆbG¶ZYYMœ8q„ ÑÑÑ{öìÙ¾}{FFF‰¿Ã?[\1øPhïƒÊ­Ò240¬ÖÔ{ÁâQîó×/IìéSÓzª™mÍF R8Rÿ¾Ž›f½ ›z2 ì-þˬômbâîî<´w­š› ®[k…¹./ùs渠-•¸H÷Å«ëçâr§ïŒ }쌉÷uw\EåkîÐÓwÆØnuýŽîè>ÒÙa­¥MÕ¦úq¤üÍì¥G7Œi¯õÿØ2£Ê¥‚#}ÌÒxJbGOÉ90qѹ«ç‘g8³…гÞïjKÉÌlvÕ¶‹Ì¿skóÚ®µÚj¸ØïÃþñ„AyQ牆®´‚ñzì·TýÔïý ¿Ùx]÷ªéköÄŽoaÖâþçÅû ­ôîĸ¥ú—v¼‘2²Qfý>F‹Ö-º[^6IœØÿí’$[$ð˜gëï›;Œ˜à°RÄÔ;©ÊÌ„¸¬–ߣ޾c0÷3·rbìãݺ‹Nñ, ô˜£Ú˜¼N‹¨º¼Fïíß}r˜>k|Ѱ/:tÁ¡U¨ÃE€#&¬„–3Ú s v@ï•Ö.ݶvµpÿ† °# B»ax1Pwz¡.á;X[ %3L9ˆ‰èŒ2"pô Þ_A‡~è]éÛ¿AتiÙ²åÆïܹSòI3üCš¼ûnd!?n¹ñ[ïŽßš·»!·@ƒ)[{OÉ÷…ÏÚpŸµEdÙ¹ =umhÑr Ùlñ®Ý- çÏqj?õqû©ýÆ…½öe·ŠwPºwPîΔAyy ¦îÊV»eÏåùQlÇ_h=þ«Ü+÷X’ÐcIÞÎØ{䨝°õíFo½µ€ÇÔ+à¸W@áp„ Á€à÷‚5{š®xB;†±s¿—Yýò,Êsñ*¶Ý¯má$lƒÏÞý*wý:3È™ù»=çî9¯P~åÎ'Ÿ~1‚ðßQåÒ_Ø3¸LÏàbŽ($œ¶gm–Ôÿ‰ÄTÿ6ié> S&æú¼|°04Ž µîJûœ<ÇÚÚÛê£~] —¥ûR¬^‡´d8èel°1’V/M¡žY–…›IXË>ItË/=çÁá±õ¥Ž†]zbü˜ÜŒnÝı3X0§„Ò Çœ+;æÇ¢£=$ë=æjcÈä\ªœwî¹jßû.Pû¬ Frò˜„>0®H•ãþ‹§A¦„ ýBÚqtËôì‰ð-¶ªM §|dœ%õ@å¾<ªóM5:°`¹)ºŽÈÍhçN¼}«=}ú­ph ½Ÿ™ªN:?šáÿT‰ôß.Å?Œâg{•0QeõÇOij!R‘Ú.qË]°¾þ¾JJHñþ÷&­*â¯Ëáæ†K—è“§ 4ïß#8cÇâþ}¼}‡nsSØÙaÚ4të##ôè…+Wè(Õªáôi: cc|þLûèëãÍZml(ù¤¤‹´d êÕC™20ÇŽÑW§qcìÚ–øø‘NÊÐê:„©)âãi‡nÝ‚L†vípü8XˆÜh½p¶Eoà „˗飽éÚwÁÉî¿Ø«—á¿K ä° ®HôoäFÈ›ËûÏ|âýW`T¹”£RÅǪ¤*2=F‘šÎÑÕA^»±x`˜°‡¬˜b-vÚxÿ!·SÕŠÐÕ¥ÿ(=¦°¶¦ÅÉÌ ®®Ø¿Ÿö¡Ä¯ys<~Œ€¸¨¿¸R’lbBëëË—´ôØ ðùt‚”ni’}ýb1™-Û×Z>#GÆeñºôfÂÒ A•euqŠíúIJ¿õÑ™ onޏ8ÚM‰.Uo 4ÕÙgÏÒ>£FÁÓ7obâD4h@ûP’LKOÇíÛtD J’º„Rin9)I¦|¨ŠEvv®êûùÁ@_¢0|6êM¼q´±96KL]V­°u+HœÙˆMp­×7 ÉP:¸¶`ÐLëœ)'¿òóÙô?!cÒ3Á“f¿³ÏLÙ·ïEמ•ô¿ƒ¡8U.Ý ù™î¤5ø¿öÎ.ŠæãÏîuÐÝ ¢Š H€ЅЉ¢"ŠŠØØbçkbwwv&ÒÇÅþwFT0^ù¿Î÷ƒçÝìÌìÌlüæ™$h…/ÞñmÊFùb<ï+¡^D•(Iƒ-‹ªÔ…Tµ‰Ë]ÈÏ‹+']RRªº”®/Q YÛÎÏ'’ï—ïk†ÐEOâNݘié%žqMãk‰ûFf J’ËÒIÚ¯ä_Åt>\Õÿ§OU]J×»(¯»”EÛ´)ddæÆ‚Á5býLìá%à…Ìâ-(moÇ ÝhWë¤#þk¸„~ò}oÿW°Ú®hWµ{Qk*ÿÝ`LV‹þµß4ª±S¿æRsŸ_€k4•ëÓ´†ž¿Ï·Óóéüòz=ZÍbB µ©2@ u¤Ê@Ô*#QW@ªŒ@ÔEª®fŠ@ þ*#ªC*ìÌÚsIÌÕQÛz™£øëbÎû”½aZÞÉÒFžZ –Ðd¤Ù/¢îé^î=5"oƒÃ.؉j#%H•Õ³¸ƒÖÒã|<O|cKš€¯§0a3†ž˜ú•+~)Q(ÂØÕ©=AíïĤÁöN0( rÞƒAmñD¯3Ú,×v˜ÿ}Ïâ? ReÄW!D¿V+Ä,þm»”g¿N_uVyzÀ—{>åÝ†î‹ p)´p u˃iáÈwû]iù óá÷• ¢z~E«Iîë›ZÎÿÍ ÿ4?yòão'Ê9šÖf…Û? ReÄ Ñã Ùç%*Ó«;¦­´aDs°¶: L @ËÆiýÛ)üwˆŒŒ …-[¶üÓ ©£Á©åcçF`î»bzýè~ÝÅH®Íé2&ªãÅ=ο(uòœ3nÎNÂ5âÕáž?u¸ì'¡®¡¦KÎÎjý±¡Rå¿Ìœ¹¾i«Ž‰Uš¨OQðñ¤ £³BÒ÷Ý• Ú¢6º{Ù0.ý4çÓy5{ ˆ™£=Ó÷= LÚ©Ï •oÓRneH¿B¨,‹PöhÑ«q¾D—Æ!$ñqŒnËÔÇöÄ«l.LÏ]⑼âsÈ]ý°¦Ò˜mi³gŽVŽ"TLÉÃÁ¶É™Go)ÍÉg¸©Mø½Õ‹ŽÍHY¸œÈÔøÜã£Q?Õé·I¦Ë«'°øpää‚“7të–Z ýÞžpð!Lš ‚§pü|Ì€­°|UA/L9cáì;`¤fÀÀù0£ ƒ ið–ÿP ¦lÈx<0Õ‚Ô7°2æ‚þãàí¸“6`õpV‡«{`ñD¸¡/C½\èå9 @Á« ˜º ÆÖfsjIJJzõêÕ©S§¶nÝššš†Tùk`4nç 57·Ù)üÉí¤—ôñ¢ë›}Õì õó$<¸Y;jý'ä¬ÓiÂê[Ûl-’ülã¦:j×Cvo#ÐÕ‡fvúrõüºRå¿%ùé‰Ü†©‡8ÜžTm‘]Ÿ×ÉM è«>Ú o0HcÑ0jEùÔmq–þy=z*º˜Õ(bL_iù¾¢MòÓ•¹mšSw»ºÛÒ†ßn²‡9@Ãy²ÖØäM&y×)DÞ§ W•S)–ŠÜ¨qé{oà\*]¸å ÿÁÍ€âM1$G$…?V?ø‚oÊ!^îüØ}@²z m_£¯%‡Ùm¶jêÞ;ºè^úå£GÄ@—!0o+¬çA§DX< ¦„SÓ`—78L}àÁ˜Á0 ‡¹maíLä 816IáÍ]Ç j 4ôKKè9ø,h3†‚©ìL ÚÁ}¸ j¦ÐÇ ŽØÀΠ5ÛN”$“´ê;&ÃâCÐTr$0r1t“ éÛÆŸ×ðà h "I‰bŸ_‰˜øîBr¢Ä#~‹b×>Úòýd~+Ú¯$Vx±k«asòâ´~ñØÄGV9©©‘;×&´ í¡ÿËòJ.Ð/¡ïòmëëûÛÇ ®ï‚Tù/Îë08óðäôUÑ:cê“O›àľ÷ Ù!>è`¢¨€2€¥ŽB©PP‹ˆ™ÆŠ[ä-Ý—}j©j'-Èû”ûB¢âg(;¦*7fQ( 0&ÁÑÀ…™RquÕbqÅ÷"Aþ,!'gÃ9L³#S‹A䃊!]ƒ!<{†ðõ7 AV¬ÉÕ½N9- Ú† Î쀬נÙ"ž•&AHmˆ¬o º²í^¶°üdPÏÇö=Ðö`‚ÓVÔ†Q1àeMZ‚ ‚¶Àµ!/`ù;ï”i ‰ÃüS0¯xåo[XØ&®ƒ…CA™ûá >l° ŽÈkAgU(“<6àéµPeRz%ILLLxxøÎ;Åå…VÉéN¬›š¬à_ì9F"|²Å¡ÕX]çžV;ÖlˆÍPwäÎòn&Ô1INÄïÉÏpBjë»tïì‹ztšy♑kßyaëú4•—æ‹h\6»·f˜GðÏ™¶•î -80µw¯°|CÛIëO„´S:³væä ‹¤Þk®o%¸ÖÆ3\¤$—Ýv<¤c½òt’pã˜N#Vë/:{cb›ûGæ í7ó£Áø·1K• “žLöíñ$›I—÷]q`~úóòxÉØD›®VZe«²Kîïš:tÚî,‚ÈI4}úÜXÓe'ì~t)J~g؋ީ1¡Yçy_ô.cê¶\{hwWcfÑ»-ŽN“3äòSóºÍÛ³zdkQÂ}ÿN}æ§wç)ÙÏ:q3È9;êø ŸÑ×>‹|£iÇÆ6W­¶ØEŸîøtj³/ƆÍx°föí÷EgFÌlðaæÜð3OtÜCnœ™mH‡¢ôØ™ÃúlŒLgâï»–÷·¾¾8°Wç}‰{¶Í<<}xj“—6x<Ù:zÐÜSº$))'ƒíòä]?òIWWõ2'6+îïiÁ®6%‚§{öêy‰ÑîЂÞë¦N–óܳ;´ÅÅÅ>Ö]KÄF&í^=Q—•Þ·Ñøïœ{w;uüsßcÚÆfz+U¨RRrâ^'»1Ÿ9ò…iY-ƒþÙ2µ{qs^棭†Ì~’&.ÌÉUsuaç| ɇ…#{­¸’ÀÀ˜]fl];¢Eñ-ÈV±rqÈ Xò°Ï*—ïܸ¤Ê ô–^\ã¹yû…Ãϱò"ò_×S·–m%˜•¹`´ Ó€m¢o$”É\»J9kèDöÊã‚c«%æÁ뽄Ajq#>^H›?]ÄlÄÒU%²îKŒZ&9µ0™ ð'™sGa„0œÕr0ǸA-#©KØ“÷MXég.¼"Mþ]0êµv ÿqÐÌŒ:¢d N¤®ÏƒÌ!pl"t[wÇêg0›¬ «|ILÞK`zkXx–8Á¬å0i —BÌa» -A].ÇÕzmï‡N™2åÒ¥KßðsêÔ©”””?¥Êd¥aàÀ¶¶¶_b5|/"‚94Ǡ͸+£‚Ïî;Â#h¬ôˆÀµIÍü/:¿J8£A|ìa`0¬ÉÛˆ#'_X$z/&%™ôÅöü[žWŽÎlÕtph§ý嫃xå–·çüãûâeô˜ÒŽº·;Œê±né“™[G)cËP ;ñª{S…¨ý£ö_8"c«zYR_5õøæ*ÜÞ#tvûMƒ^ÍÏ_Ü»õ³–ég:µPòYh‘ tÕñ §Ì<8èîùÈÙþÓ>lÌNÚg×nÑÄg©­°;MœÜúDÇ2‹ú-¿ØÛ° :\ÎjºŸ+6:ökfôØîuétþ7Rks^èØOméó /ÃÏ»|tt“ßôP‡/aè5ß»gؾ†qu—m—_Ä_ÜÕ¯éŽÓ‹Ü\òz‹C§¹« _ÚGcÃ@ç‹Æ+Òú‰c÷èX¶Tn”ÒÐ}ïÊîû\/Ü|Iø/œwí?õ„_“Á‡÷|Lé­1»X‡ÐÔ˜X~ÌÑæmg º•|Þù8ÔÚֹ룸󫿯µñ:Þ†Þëå­x¥À…³¢“ØOµi»€ö$1ɆUlϸºñåÉm-ZzÒËÂgÃ=G’liãÔ{¼©ñÖö£"„ùî3nÌQ’¶ÉiM‰ìçæf$—}g…¹cðÐ ñ'ÜUÈúòÂéã A´{X³]Œé) £!þœ™q›iM²ÂìdísLމ¾uêî}i«\ª¯ÎÔ*ÿ5`ƪcÚdû_Üy'=5ëy‡NI§´àÈ¢ôý:1Û8d}W|%wñ¡ZÇLwV ´yvB˜6Y²a kÀSŒªæä, ʹg¦÷hyé»-9k'×2^Ã\³©JØÆ_{›Úƒa ½Ò¨W¾ã0ØÜû‹C ˜äۦÞ“°6 ®Ž†ñ»aQ0t m:”×ktC÷F°t…ÛúpÃRæ*_O0^ë'S†ýµ³s­hÔ¨ÑÞ½{_½zµ~ýzÒV®Ö››Û˜1cþ ­ÌçuDr®@ Êê¶6æšrÐÎÉ™µöd€¼˜³*Ö!x·P#®íŒ‚v?‰ðìÚÝÕ©ÿÔiŸ‡oÕ‰Û°èàzÇ2„­$¯ï(è›qˆªû‚´˜¸˜ßdÂæ÷ý ™¯ÃB? ]i+;nÐÌÏ@æAAÝŒ#¸P¦h²ãÒB1Q‘´°löAöùu×8ËV´"¿rš8ÛhdïÞûzÂŒ²þ‘É  §Tz¾ žvÎÂó “šì—I÷d_ ¥°÷ܪ14·Ë¿¼qhªöpR’É#ÍGxáû£_ ê5hN=6LÕú:Dv–ˆêíŠÈÄä‘iU„œ÷§N²‹énHºëtë¢g.¦ôè[,“äƒÙÈ¥¹™è´õ¶„MÚ¶m Y¿¦øüœŒl(|~Jx¥é—^¯©©pgDLÈ‚†¢È«yôíÑRpi]taxMζ±¥rw…wó_ €÷ô{ùþSì©×G㡽ðÙ«Gç5nZýµÎ+ƒ†QŸ¾Ý¬ä 7Ñ[“{íÚP•vûÞÍ~"™Ü+(HhFT^4퇎žÕsûÚ—5³7ð5Bæ ÒÀËË’%S°w˾4ƒ S)I¦JnôÔÅ<ÚÍ®û³zîA9è6r°¦íØfg'ó@S’W†ŒÈ÷H•u¼ó …P½Ü]CrŸÚi,–½£©8í-¡Ð€Y}TMaößÌ[dŸ¹n×^ÓQ™r’äe¤‚ªW î0ÙÃ&-tƒ•=Œlf~JŒ(è_yùTC±ý’.82&ØÑà|$À—ªLÚÜ£Á4‚&«@•ž­açb˜©!³Ëýà 0¸ìŸžó k(±©Ò  ¥6üpÇ/ƒÁP•áèè¸cÇŽÝ»woذ!:::33“4R‹ýp¹\eeå=Ã Ù·Üï%XÊú~ög)XRÈlìJ¾ëé½ú·²aÖ?O69l¸Úÿè?º/~˜¶HùÎ~#›UøZ³^‡.þ“¦œ÷ÛÓxöYå•,Šã:¹tÚÒãùyQJlԨŅŒ›HŽJ‡Ä©ÞNKåä¯"eskå mòâ¬<9&»$Ùq¯„R-Ó’ËŠ³ÕäËýÏûgñ8ÙYIpÅÎá"&!ëe.þ9rkPȶšKA𨚘¸ð3™†r¥Þx\$¾ü ÂMãÛŸžÏ¢òU c­VÖD‡±™dó²‹ÈÖ·ü‡AªüW¡«8½çÇ~8bKäÃ骦xÚ¡ì+ä„…·IhlŒ[RíÅ8`V“±[ùAÉ‹öÊÎ.¹¥hòLEeâqDÞ£~^¢àÐ)óKºß0:4Vü0+“ψðÁJAÔTš8Mxë–TDè$på' Íè²$mQ˜z€/C“|ˆ‘j9±Ô¿ÕŽqÕ þAáÇ[Œ+ibŒ &§ó¨W»´sË Þ\õ| €žËÀß‚=#‚@Ç4ŠË„‹{ÇØÑ‚úÕÍ”—ÀÇT°2®yÛQ`>\U‹KÔ¡À©³0Ð ò_÷”½Würaó©UMˆâø!d.èt„¡¾ó ö•ññãÇÈÈÈ‹/?~<99¹Lžÿo`ñØRÂyÔ¾Ó3­*:3œõU=3{Ê-žËž®žDãá O\êpªÀ}¿f5‘pµý¼¼¶^Ùg™ÐÕÃ¥øý›¶§S—i#.ÄOqWûty™E§‹Õüb,%\EýEûoyYW·Ÿ7ºSIi§>G'ÅD¹™ßÎ%ƒÎ]¿‘3Ë„—›˜5_ûò’Ÿ¹8鬉NP¥²TÑØò€µ8q÷œam;‚¾„@®" T§þsÕ¯e¥Á UÚŨ6μîàï9°»ÒÇègÓ·.%ŸÕ8: ÔÞ¿»¤Ö³•l–¸H¯ÛÆ{;[Ut®r›Š 3AU®Â O^–'žÎýiÓ_žÖ„w­8^”„ãtS²S+ ]ãÈ3€7rùùîîRb Y;`pê¶$Rå¿ †ËdN‹ Õ&¥8§×$… Ó5È6i­6‹§±#}°+~Ί H?ørˆ´¡¾ÊakxzßgœÛ¶írU·2뀯0k™ ¶kr7S¦]/¥q#Ø[<[Û©ï>!=<6ûšDÊ8œäÇSY´‚-o«±.$~øœSœ6>rÌq9Iº‡}ó¾Ãb½ãz)ÁÓã[MŽ Û}„ÊÌVßÎ Óm>}»ÄÖ†¬îS5fO­I_¶4úõ£ù îâ#`óE`Ì8¸º¼æÃ6L €µ~À×¾þ0·IyØÖÓ¡i4(6å-!Èö‡ªö©:lþ6e gl< -zAƒ=Ðe1Tõ4©•c…¢jgÚà¤Öd Ž—é³v+súq”—õû|ShZÊC\^V‰®0õ³bι³å£¤…þךA0ÌØ¸13~ËÁ¼™^e¾©¯ß¸›±.ùµ°HT%yÅIáë·4%–íζùéÕ0¤bPmÑO»ôè¾_˶ßò™þèJ´å¶¨íÍÙb£Ï3Ô5nÊK ß±¤mbèFîÆòwomŠƒV_Gsî±­ç¬r —¼yÞˆÀÝ‚ªíJ…"iq‘à\g«†ØÞ¥‡2Çx•Ý.ŠÎîìü›®K»÷*¯4”\LIvnp,µk™ê¤Ê˜j³• ~}Õ=ñez¡ðœÐ(þV wp-æ{ÐìÇ]ª<çB½­Ö•Â2òoJß6“(Tîb¦¹Ì6ˆ)oö•6¯ì;£ùXkckž \§µöí¢šû§‚{nc{®1Æ^`L<ÎǨÁÖçN tëæ3È?ÈÿK­JBÞ‰TÈH¹û0¨fR2{S6Ÿ„l亃\i»ÚÁƒä›½žS-¶²z?阅—gr±hŠsàÊ¥1˜àЉüuè ùŽŽ…˜W ¨ÖÍÎgA@»xz|óíY1;2œë0÷ÙËÈD¿Í°°¦¿zýAé±1iM-UC÷„ìïºÞs ¤oëz¸3mÛ£…¥]FƒÃ,ƒ†õonIÞ,m;¶æO;î5áëä»oðÓv¿nØÜ¦tt¶yr+–ÍèP?ñޅ¤»‘¯”tR?eåå&½ŠNÉn ®`Ý©ÃÓ€ñ£EÏ,µØ‘Qiù…ob“ò›k6œ¹¦§ûè¾Ä›Ñ-Í”¤"†}¯¾Vem¦ ×Ë•5,å£@$k_và‘-;µèëå¢ÌÆ¥ÊÍw«ÿú@ïŸ?ϰ0Sæ³wîïã~ÈÇÒ5zR?}yGÙ¸Gç6ž& »'Ôxݺð}d†4ýþÝh‡.MTê[©Ó·†ÏX•Û®uï­':úµh™4Õ¯‘yWŸ™¡6sÎ:ö:¤«aÙcGˆ _ÅÆƒ(ÿut\¡®Ê«çQéDÞË'O3ù–Âĉ)þ6&SÚköÖNþÃû¥Œic©*Ñ{ökÌfæå²_¾zxÖ׌î³0¹3–ü4…È~t?ª}=—À)CÏzlèð ÀÇ\q´ºöh;'bxß ½ â:ØhE4k¾v*2 ã“_ÒZ/5ü¥7óï©2â{î$ }9÷†‘¢ÒŽó¼Š{WüîúÒ‚ÜuÃr.½&¾Üº ææ¶..›;ê×6<<ࢬêÓ¡RJlk Q…C~!=tî 'OR~:zÁ¸qð¹,€à`ÈÍ…yëÁÕî^ëuz£~ìczÉŸjªÊuB$o½êŒM•'%o"Í6~ç| ˜9§ÙOôxû¹@,å*ëÕ/›ÝÎw;~ïº9e1­½N^··2øÖ)ÃÎ_}Ã3*3¦év»Þ¸/á¨wíêÑÕ癓ˆäGï8=ãâB™ Ç[Ô»Ÿ”G¨šv¶oÜ/½HN$$€çìít?öS†è*:fÆ•[²[ϸžKÈ*RU“5Œêõ½ô¬ÙƒÇ¯3 D8ƒoÐ@º¡ÛíÜy7:_RT<†LÑjÓák#G%e`,yC‹ú8¦ºæÆõ›ã„ EsïÎ"Sé, 5X}äëÖ¯Ò t•qŒ;lÁ GïÇqÉ9­a`©RßþG¼cž½–v5,7Î%EËÀ³çGsU¨i‚líFÿœ=ÃT‹¤XÙÙs4žˆ$6þ‰²}ý>U4%-S3y èösÏž$øJ%{}¿!N듺­ÕÇ„!¼¶&¨û˜¦{Â÷\ñyôÝ»ÄrTŒ›®|ðDÆ›¢¸Di¾€fÓ†©ð+û±¤ÉÑERˆ"1ÝÖQ×7Xû]HràY,df‚\C°×(„ë¡‘#ü«C‡DÝ©2¢RéûÉ£§± ´œ~íXI©äúê¤%‡¯*b wÖ;é!‡¡þïú]ÂœŽ¾žùS‹ôæÜ¯%°iÔù­`Ä/©2âû`*æ,+¿Âο\£!ËÜãü®ÇÒw…nœ_ì\,…Ø— e4ž¿ˆ³áYØ~smŠßMìÀPr‹‡°âÐ9ZsÛÁÞœêÖ_F ÿY*#þägúõƒÀTÎ[IŠ`g D¤Â0Ðj77šUdÑ;kðHµ«CÔÒ{ nàá2pŸ w¦×>0ø¿©ò߆Xxb^ʲÝ“E7qU g+då­ Í½tJÌj¡±y=ízúŒ ‚÷9¤W†ÓXùck<± Õˆôý·¯È´îªç`¢?\| ÞÁú-УyùópaL] Ÿs€Æû>°6l +.‚~O83òÁ  ð8FÝ€ <>Î}€ùÁôÌÜFúpå*ˆäÀÜæÿ8°>v]„|m8p8 `"ÜûR ì½`Ó¨~µ!%Úzχsc¡<¤€oP †5=kz=Äÿ#H•ÿ6è¬.¡q?¬*TŸÅ¦¶ŸQäû¯Ü=cW2y4ѓ׸Û|ƒíAp7ÞÅáS¾‘áŠ.5‹™Ã³‹WŽ?g©ulƒAšˆº ¡+‹ú{ˇ͢ƒ(ÿÁ3Ö„ šÍuàó?qͽS¸&š~MªDÁê·FþÓ™ÄséÔ%;åùfaF.õSü)½‡e6k¢ÁÍ#4ÑÃÄ&v‰t‰^_K n䦱¬÷k×µ*K?*»òª˜¬Œl[^Á¸% ôúëÂ@Gæ É—¨]]¹Ãä¸úZjBö=°™ÃÁ2ˆK¦‚ómáŸÅ`e©B3ØxÔù<ìÝ@IZO à° "®R’LžØo ¼w×5`Bƒ«À,x$ƒ®&…{{IõYhé°Ξƒv=!ë=\~Yë*#ÿq*ÿ0ý6ñV6ÏØw–?Ó›úýbRVbw;jsºJñx.n#¶ªFnÌy t©ùŠ=l¿5ì}S÷ÅjûP‹ÖJ¯Œ+Rõd*’rHç ^ÈãËbÖéÄf‚$î>M¾hÚ%‘´ÂÏ|¢ô—8ò`Î#¡Ê‹P*1Œ&Ê“m?,:,žàEÿFWwAaaµråVdÒV±&7à ¨çKâá³7\N¥$ùýa¹ÃÏQ’L¢`ӆʀ”€²³ ÈŸåÉÀˆ0§'ä¥BêSÁjøo„‡S©°‚+p‡`ÙöuM} Uñ~àÀƒ˜(È…ê;UͨÏÏQ é áèàÛUçï+ˆÅâ””míº¿s(©ò_ ÓA! IBØ‚‚`o» {ÎnöÔØ’[A—26ïA<ÆÃE“q½j7¥ø*¸n[^WíÌù‹|N0!3kÎ îÊíÅëRBò”ÀÂw98W,¬¶CžEŸß4qš‹|N­ ¹€«HÅÔˆå‡&.Ôÿ9™Ô^‚B`jC{™~Ý{¤˜wp,÷9ľ;Ê £ú(³˜¯”æJ`/L˜§BcÒø®› xGi*Œ'Ÿ‚¢Pÿë}ÎtY€‚,j[x&Û?[B¡ð’ŒË—/?}ú488x~ñ^Xâÿ¤Ê)l÷)¬5^©ké÷Ë*ðÒm-3߈ø´. Ý·é_H¾áSÛéç×N”I+VE¾]ïìóË2%i/Ë–›bÐ2n‰ä ÛѦ?Ôßd ˜`¦]Ûˆ‰¢õÁäUsN‚ vïÞ=sæÌŒŒŒ‚‚‚¯Eиqã¯B u¤Ê+˜šâÐö¾ë2hè3-qd)bÒœF)i‘ˆú$°æÅ©¨ó`ý8ð;|YP…A#³r4ÈLRYRH¾7å ‡å3 b ¬cBri{8 €„¬’ÁgÒoÈúD}*êRA¤"ˆŽ=KPñ† :´m۶Ç?wîÜ×bøðáƒšššŠŠJqÇ15*ñ'@ªü÷‚·š©¨·®Ð¹C¥´¹V¥›b§‰é“{ŠZ;Ñ”8âœ4IÁ}Áó<ådÁµKâ\ nª82ÍŒ¾ýb§÷Û̳¤ êZæ 7î)g?'É»½¼s\žFà ~~Ið¡GúDpï‰4„WOñ›3µTXNÃX[ý?µ‹ç5©O×RfBÑÝ“ùÏ[ñ¬cÅB Wfc`κFßW‚1¸¸<¥š ‡¡:ÛJE“‡«j@÷Þ’BWå‘*×oŽÇTin>!%0¶MMó;©à¸h)Î`L.M«ÆkW„ãhìæHú"` PT!Ôeyî·ƒ _ \9PÕÿ%P˜J¨«bò­ ê%ЕK­!VÉ„^ÿ Ф2;2ý1ÒV» £ÞQ¸T´ f(–P<è Rœ3ÂXÅ ÖRØup;ð±¤~ºÁìÎ0#š‰Æªœ^;;»íÛ·¿ÿ~íÚµK–”ϲ"5;44”Á`QP ©Ð999¤ ýôéÓ¸¸¸w2˜LfCVVVä§‘‘QMË Qˆw—÷‹œ¼-Xß÷úÄ­’S÷øðä8â«ö\y·¤Ê5˜šY•;ãª1LÔJ~(Ê• p¦óJ¥Ù ¢O©Õ<ˆàª:4nI\Ó¸Jĸ’ùWò‹kY³CN«’GœIÓ6-Ÿ‹ÅmPa^ƒ¦aHÓ¨à¹(]”˜Uíp,Œ-GSUÅU˜ª_ü&¤|Ö«'Ÿ ?s&L˜@¹ €£b1\¹bä,µõñ#à†Ôª$Ÿ?ƒ‚(€ö… `Õ˜LHM¶¬Ý~ölXºTÞО<‘%ƒ¶máþ}n¯^6P.b èê*åäùÓLJrÉÊ–õsøðøpò ìyÅ>fàá]U’K2‹a¤”.^¼xÆŒ#GŽÐD”ŸtéöwwgúW¼h7z<$Ñ´Q‡M÷¯wÿ£­EH•µÃÙí‚Ùm«FüØ¢ŸKŽN/µ¥=¿Ò€VÓá]• ÕeÓ¦’:GûöàèHÉ$¿´C: ²³©/ ¥æAXXUR¹óò¨¢ µœ„”^Ò¥¨èt(¶V­¬ <œRå2%óö†¨ÉËM…I»aÙ9¨W¡Üu¸3¡†uŒzõêÍž=["‘ÔP•«+މŒ226©T*)%...::úöíÛ›6mŠŠŠúüù³¹¹ùÖ­[ìŒ2~ichÑ›ñg4½8°Ó¼¿E¡ð;#å¿ôyŸns”'>sTû&øpiÚÞŒE“{þ[í¡Hü/ª ªŸ#(C\(üöØÉO§ÆÍ¾ìÒ¾ó7ü(¸„lrZ85hQ냓þ`õ ©2¢–ŠUmÇòŸ¹‰¿7 »Öña%íÏÅÈËWõ ðEs]R«¸VŒ“„ÿŸ³Š'RƒëªgÔ²/Ã0:ýW>ÝÅ–wYÛµŒŠ>~üøüùó¯„Î:³|òÜÝÏ8 ÜÜäÏR× -Í>yõ›­~íV€ã“skFu÷Âtb|Ô¢ÜSaûLMë·<Ü0fÜʧ ç÷Ø>}r¤íø-­ tù‰€]W}Í>ù‹L粄¼&ƒw¬£–÷jj¿– Î&š¿8óäæ‹÷_ö]¹¹ß‘ñ#/¿ýxßÃén«žÛŒ+јöêLŸV/&Jû,;¹~t'<õÖ„N›ˆŽ—̺0uÐù$\ÏzôAaщӾV•®Zá‡ûÁ½W<4¾–Ó¢ðFа K¦]-|×’•wiå¸ ›+(1““ñQköŒq«8VÒNí>›ÅY>P¶G !<8¹]ÏÅ×ìC×Û=Ø|ðòƒ<ý.+&u|xrÏé‹×?í–{‘¾õ¨BN>î?fÎ{±¼4;ÍÈ#ôŸ©mŽOnÛoyd¯Í§ÍΟw³knÂÂ[Ǭº£ª«’÷$2J°úf ’\ß°ö®œ+%‰²bwP{£j’‚سþ:lMrØ0­Ç‘µ‹•¼욨·|ÌOÅJŒ<‚Ó¦]+™Ç†ÙùnQïêm™òüÉ›Ï"½Î[÷mèhVµW·Ö®Å*e+#Áûç¬V!‡Ã‡)1‰O·wŒ²!…à0$y)ɹG^ ³¢å¼½0ÖZLž›¦Ùfâöý¿¾xÁ¦^v©¢¦ Çzù–Þ±¸’Iä]\ôÏ%Fr2ÝížÑ®•»¤™Çæ9§¢€%gÊOß¾ÛîÓŽÃö=äÜmöhÿÀm×FY¦ìš2xá¹4U¾(M¨¶m[ÇúŠÅ©pŸàéÑíÄËÄ€¦Ú¬û©2øè˨öг%ý»Oþ|"ñq[u€üW~á×,ûμ9ì"ƒ|ËÒµ Ójv`‡ cä³, »¨^—ñtžuó6KØàr©HÏuô†z-8’-ýÚÞÖ[xûèHfNT;k+¿6=÷° ë¿àÜSçþ ‘›»…ÝX:Ìg¤…ÿÜè{ôÖÔÊóÎUÍ;œˆà¸m4³h.OŠ˜¦µ®u›½ó6·ÒNï5w–·²&4Ð ><àTߊM lûiƒ\V¼Ê¡“ Uu™2=`×Ñ02‚¤ º»³#9ÊC Þ¬´7óÚâÙùFå›â{÷ßHÀ¡¸ocy‡Zlu•×q ì=7÷™i‹³Ÿ¼Ã×\ŒÅõoê<ã’ï~Ò¼ÏZЫ >9ÞMúöAƒ^Áî«'úû¯a#—îPX{´Ïµý æ4¢AúAÕá’Bjœ¤[;{ø_\óé•·6|Xïb8 ¿sÌ­æÕm©×~å”–[ÇÈ7èÑ(6žwjtÇЛ.1±›tA4¿£¢ÏŠÎOf®þÏŽûíƒNŽ´#„ñ‹ÜLú Q|vaµ^ÍbêŒÛ|y@;-IÂ~M™ƒû93£vU?"êHª9øÍ–Ž»“„`E¬èÝ=¥]Äݹp¹a}÷€6ݶºËUw×ñÖuº3äéÇñÖT«ÒL,2‡‰Oç×u¼‘ü¢»¼^ag>xX‹§g+l G¼Ü9Ã#ôã¥ìû­ùð`–¹óÈiïn é4kŠò”»«‡“>.Þ¥ðAVd&œ Ð0muÜé|™rÌÚÈeF'e4ÕÖª.UÿH•Äo%>|[$§Ux›âX<óõÓÌŠÄå Ž±„(³—ŠDb®A«úäùóšHÂÖ2±·¶ ÙX€øÊ€Ë‚æ+02]ÑÜT9âH ôÐ"#WÓQãsAÇÄÞ22‹©˜ZQ]TXM«8ÛuúÝå‡vlß~ #!*šm6A‹¸Æ ^ªb‘ Ìgù"¨ÚeP’hY|"‘¨¸#çá½ëy:ÝÝÔ@B€¾sK:ù!>¿‘b™5)þ”–¸f¹L‹Š¤P44SP`€‚£ƒ>¼ÕÒÕÓR`4TWº‘‘*!Ï›un[o¦µy†Š¾™:\>þ ÆI èXÙêèÁÄÅöN/ÍÕ¦æ²4ª40¥eNIÐ{þðJºZç¶ÚTztZÐÒ#Þ½Ïm®R­ò‘¥-9k y «W}â´:„“r$ãæöõŸ^ˆ•Ìt‹«H5ÿ`,ݱ³»w¹û6¾@ϤÒR·¸N·þšbÒg!((bÂ)žuèJ~« î%5ÓÁ§gÿåÛø:ÙŠÊ—œ¦¹.ëÒ‘—à^Ýòî„äÖõsYºÃGXsJʾ¤ßLzÿîõ|ÝÒwi “NL(jP>¿¯àÜÕ»„e_G>åÁ¨e aø»¤lI€¼%„R`áiûÏ=¥5 µaR+'˜¸8¦MxS$áË:­1Žš*¤}ÈT[\ÿH•Äï¤ ;¥(®bð z2?¥$òÀ€Xª‹Ñ¤»ŸÓ—£ kÒ ¬[n~:¤]œ‘ ‡aþMT*¼W ©PT ¥¯ôðWM-V<"=.²® :LiR½¾í•«¬¾£n` Œ'Ÿ¹™Õ¥­"ñòü=‰²·%yy©ñzÊXœÒ1Ìoüzj5ëk¤§–rwCÀØR2,Ñdl¯F˜¤ÒIéªÆ ˆÊ/U^€^zóÄ{ß™ÛÖ罋'XN+H}òšJŒÊŘñ! g7ÏI¾LÕZ4ú i©ÕBY™@Û«j[9AäægÈ < Š |Ü@c¥ ”ˆ²…yðùØ„qo¤E¤–ÿh/u¼+ó *È klo÷Ž÷€òþÜxecÀp& Wøïv™W©2ø°å”èœäk©É¥IDERƒ— ?,–xò}‹RHÒè¡_ P¤±u{Žèêéãš0øL°ÌZ+*È&d+®"“_$ÁX%A0YüÅ b+p¡(!-S*b‰‚ÒÃiР~SnÒš/CÇZpHËX$ÓÈ앟n ¡Ò×éÔNÜ%1ÊR[’]YþJNUžU·. ©‡®ÜàØƒËÄÅ¢"‚ÎbÏq(‹9ãaТwk¿©#Š%ÔyI»–†™Õk®>w]Ô¼ üâôЙ̢¬¤MSC•¼Ë—Ùæ¥i1õj¢2þÚº‡Yím1B"Î(Irɹºù¼RcO]²"Åe׆: ÌÍ’]fnæfe‰òˆDݼ“-,›wêí–î&dpB"’å«Ðª‡FÞ©+Fw¬À*¾‚Lº8?19ƒ¥¬­Â« ¿­aCgÆ’õ»?MôÑ£Scá%Å„“ÎI\·ñUèsvY±âÄPÙá:ØXc;WE| ðѧT6ÈÓ1V$IÉë‹+¹Ûš…‡‡ŸHÜY©D,%¥C# Ar¨ë+üÉ)ËH•Äok³dÛ¬Ìc­7Ø5T–S4rì:Ò­eŸ!ÓúpŠlÝÌÙ>÷}Vbúé“ÏüÚ‰#OÝÊŒW8¸ï¤J÷ÎMű—÷_ø›|~ó¾Æ}½[ð@1øÐ–ÛÞ¡­ÝnÙ«)jÖï?i¢³bÎÉw@"8wìzëKûö¼„ü£;ޏ mìØ¹¸wç¬^^¡“}µù•ßu­S7¥æ•Ìœ²j5\?4À¥Ãf¶¶’ÜÌ”7O¿èÑ{±ÿèiî§Ǽ»w3ê2¶í´n¾.ÍÛ´q2åedÁóuáÃG ó¿5£yƒ-š¨+«5nå1¼› ãŠéMíÍq8y'ŒÈ·¾øé…“3AtpëK‹Ž©NŸ)§œunÔPpôÊ«ä ö髯۶6Ó›u ¬›o ãõ–ºJjz¶¾ýŠÎœÏÃÝÛŸkv±ÒUdær¹¯Gqƒ¸4Ò¾fÉk·¾`Ï,OS7Ÿ—ƒžii¯©¬båÜmì§¼µz79ð,Ò˪|UQbÔö£ÏáçÎ3~T1Öˆ­[®{éîèÔÌÚ@AÕ¸«ßØnæ Ù»tÖ Å¬¤˜K/\Öl^ Ç„”Ç^yŸßí8qµ‡6Á]ëMá”qÚÝÆR‡·ëWîiºtȪíÁC‡»4YÑÄDCIÝÐÂ{È„–3…wé?ËåɱúÊÊ:Ö¾S‚ôo-×éÒÐgÏý+ØàX¯‘3.]ek½·eKË7ˆÚ±íf;{gáóGÝq¨\Và¶®ž\ÔÏ>q‡wïU>Äiä”ÐDZv ÷8[«¨h6ïÔwHw‹Îã†ÎóÅx ™ã;gñø8ßÁMlš;X*©è´öâÓºAquE{!WÙ¼¦òo| ¾Reñ›aÕ›¶ýÂ8A¡DB™— 6‡|ùÚõY‘Þe¾„ÀèL}ü¨¥b`qyLh¿íNÖ?É`’~˜¦N NeÍ#pƒU<æGÁÆ÷“^Bª‹•Fgr¹¤-§ä|!ki6³ÙtFÛQóã‡ÍÁéddì5×R–‰ÉÈø¼j^t,«þszqKGAé4ø<Ý«HBИlÖôÀE"‚Jæ´ìظÅiQ³Û°v'bS Ф4]é“g‡ÅãpƬ8=$¬P$‘’¶3“ÅaWž·¯Üq€§Ò¼ƒ[ŸζŒ^ßs~JçÙ@cp9,#µ¾§2{8Åáщ^g?y¦›Ë!ÂÄ}ÒÃW£ ETç0Ì>‡)ñݘÝoá˹::r*ÄG´å‰ÿtcmëΣOŽéᥪ9bñ1ŸYåé!‹®ÛÎ;]{eæ +&Œ®n>lÃûAëH3—Å)®Fè´ßqùY>™C‚LƒÃåâL‰ê{Œ˜îeœ'&æ19\6e?«Ú´_|Ðm,&›Î}àÑ ÀÈŒp¦ $/&0GŸ¹¼¦ eã´ht*62 ¶ÓØÛÑC ‹$²+È"ÝhÇì¼Å?:¡êné<“©ÿÜWP(–™ zðè" Îáã€ë¬<3lAiÙ6è9bY·ÁKhTNh€[ÌØvm‚€ H`TÙrY½·gvÎdñò¡¾øÀÝ™B åNxY ÂùÅû´^`¡õcÛ¶ÿ*#ˆß%r•ÞtäÛ/_6±›UÚGHã3*ôÏÒY|¹ªÓF™l³R.ùž•+s`qË·ÆfrxÌo¬9†Ñùr^€×/õÎ*Mǯ”&WŽYºt)«<ÕLžÜ7Þãæ»Î†6ê?öú¨Ë-4Éʯ<‹4n…¼pä’‹áóBùåð+fçö¶ÅW¬}¶R 7S®¼&­ V‘:DµWI!LJ-ªß¦«‹y¥Ù>Y3àWFcrä+œ†SÖ¤ .+Wq>Y·ªt¥y…ÒPŒÒñæ”@vÕè1‡Ïªp†‚¬làšõ:ôË¥êq‹_>UŸ]¨jÉ"àV,;òÚ²øò•.YäË—„'Ó/Ǩ: ûÎ|ÿûÖ?Wý€õ ¤Êñ{a4yl­æäá~›6´Óø5KàÑèXÊýÈ,è.“š‚½ '¦6ÒQ­ºÈ Þ_»—༰žr-OM¹,>‹1x¿M§éÛ¨‡¬^kW¹|±bÏ¿Ëû뫃V'í¸s©­îŸMReøý˜ºû²‹½•'¡´ý š Y¸ýóHkU ›-±Ý†„DTý:Y\ _‹97§wÚtG”;¼ÕàÇ ×†v«~œùÏ€Ñ:yüìú_œÃ¦ˆÑJ²éº¤Êñ¯ XÏé74ªL±gèžž¡¿.Â/!Àyî…̹¿óuƒê3ù UF DuüG6¸ú?©2@ u¤Ê@Ô*#QW@ªŒ@ D]©2@ u¤Ê@Ô*#ñkàp¾±Êkøª,‹ÝÜܤRéÏG…@ ~ ò …?ÿF@ ?C~~¾l?ÐçU¹âYÉï—/_þ™D ñßãúU9##ãÇ"ñŸÃ0R’³²²jðUYII‰üÄq5\#Q- ß÷T™Te---‘HÄãñÈÁE‚@ ~‰¤  €Ïçÿé„ êy$õ‘´Z555köU900°ÿþ………lö¯ßÚ @Ô‚ ŠG{ýäHñ“–*©Ç¤*3 ]ÝZo×üƒª,'ãÇÂ"¨4_@ ˆºRe@ ê H•¨+ UF ¢®€T@ ˆºRe@ ê H•¨+ UF ¢®€T@ ˆºRe@ ê H•¨+ UF ¢®ð?·ŸÔ-gèjµIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/yang-features.png0000664000175000017500000013300014770023131023666 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+µ²IDATxœìÝXTYÆñ;tI ‚ ØŠÝÝÝݹúíÚkÇê®®±v·»vwwcc`‹Š‚Ò RÃÌ (1—úÿž‡9wÎÜ9\&Þ9sî9jr¹\ µÌn Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆí´D"Éì& ÈåòÌn‚R²}€~&4  ˆÓtv{€l:DN Ð@†"@" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" ЀhÈxîäüá“—P°TEGßÈÈØØÔ¢HÙÊŠ™i«dÐý†¼Þ6¸Õ€mNò Ú? ƒ œ(ìõŠF¥GÝ)–™î`ÿwE¥o.÷¿:ª|ý•ïcŠzm¶;îc•ìËÅ—k¿©÷ß縲n‡£ïµ3‘(yá¯T(>éyüMV#o8®¨¥—âMCN*YqAL[ÅtÜFø=;¹éß-{ž¸û1<ÉZã¢5´ìØ­wïöÕ,µ”ý{R&ó½0ªQÿm®é¶CÀOC€r"bC·.;h;üjXtñéŒ^ÿ´¹÷O]¥n,÷³›Ò'.= úþ[ß3ùô,È}¯­Üû9þ– “ËO»·éc‘úÞ[×Õc7º6²ˆzª÷$yÐËCsF™sÆE‰º>N·­Œü™¨Y¬í˜iOèYÉX5íM}¾|ø¿qéY½t÷É#»Ô.ii¨¥Ø·DÝ´¤rÿ­Læs žI×kŠËù†Ý~³ººòŸÓ Û"@9“z‘_·¬}úì7_âj‡>ºç&¤1@Gxܼð!¶Pjð¯•IÏ œK­@¿ ëÚö;¥ˆo—ôþ³ÃÃeu ’ k2Ÿ úþ÷)¶hÚgËŠvæ)ŽW»wåÍØá j5Ò¾yÉ=ú²ã¿›ž]P1-YóÅü ~=Õ': šäA¶m‘ =›5ùcõòÉlõŽ4QÕ6ÌgùS²Bí¶ƒ¦ áž÷ö.5kñ±WaéÐŒ(¡Å}HÑ.^Ñ2†©2ÈÉÔ¬{ý»þ@©ÞÇ£‹Î+zMéðdUCÃD#´Ìûì¸~Ýc‹æ¶-k7å1Ì¡Ž[×>‰-øßâ¥æ¿–Ÿø4ºô~ëÚ{Óþ­“ò‰€ß±¬móé†sôÅs“æÜj¿ºNš't“Üø³Ó·¿-¨[ÎùÓj¥üªç­ÒgÎÑ^<Ý1¾÷¯ÿ¾HkK"IÜc/ëédÔ4€ŒA€r6ÕüÝ×m8h×ýtÑuMŸIŸ®mòcl”yÓ‹GlÑâÛ77Q"Ù}}ð醴±…âƒ~©d«7¬ÚÄaw¢ËŸ÷®¸6¿vK#q$Å[ÚÊ©ãEc>­»qÌÑEÓÖKühÁÀeïâŠ*¦_91¹Š˜‘*yÊô[·a‡5Ç,ÒþÂ×.Q%?@6C€r:U‹Îk6u»ÜyŸotñÓ¿}Çu|º¡EÂp,ó<9zÀvÏØ¢Õ ›+ìüo®Úå[(?¸Wq 5Õ#üÞïJtB 8¶ü¼gónfâ2¢ŠaÝisnx9zÈ„üîŒ?ÏöÙÕÆ4õASæ~dÒâ×ßö_uÁž)¢Òs, ë–£‡§ºqÒ6ó³ì«Ûó»w8¾qý쮢¥Ÿ×ºhéÊ5ª—¶Ðõ'E|ùôêéÓç/œ>|öõ ‘«ë˜å/T¢|•ªe¬õÒcÐLf“‡y:Þºqï©“‹§°\ÛĦz÷>“/“>GU¹—»ߺ~ö‰<¤jZ::z†æVmŠ–*W¦cu€œ‚ ä|ªæVnéu©ý.¯è¢ûæ~c:=ÛÒæÛðŒˆÏÇ~°Ó+¶XpäÎyI óøŽÜëÒòþ1IÍá] E%„|-G6×¾r28jcè¹åG?vþÕZdS³î±xÜ?•澉.ùí»lr“Ù©>s/üÝž…ç¾-óg1xéài8·1•âÏøׯ:zn2èuÙugƒG¾„¹ßغ`Þ²'žüp]$ÓÊ=FNý{l‡É~4÷~v~ßÎ=‡Nœ½üäsDâu´‹68æÏ©ÿ«g‘Äa y0¡D¥Ent_SCwM"• MyäøO9͘’ïÁúÆ]®*.[»ÿjQ¥dÿ­!ã‹W^3+L½>v¾«!}·¼ráÑ…³O-©ám¿qÚ³Ö]v•Å«©Ò¸H§Æí ¾»}ºU!Â×a×¼™ Ö}êŸdmëª-:öüuì°–5“¬ [ @¹ŠY›å[û_h½U1W³çö£:?ßÞÞ,:ÖFx9pOlÝÂcvÎNæDÃø"ÜN.?ó5¦ ÞhD[ÅÉ~“F#ÛœÜ$ä7Wî};`|1±}oÚåÇ.麦ý~Ey½hÂÞ!gú[¥ª_TêzrëƒoÅâCFT=,;³Éý7´Û°¯’ˆ¼Q¼îï™ÞiÏšÖ ì[Ã0Ñþz¹×þùº]Jf'тί~~ýÚÁ»Ž/ïj“ÍžÌÏ~~—æ“/þdå²ïºþÓé¨Ê|oÎëÒzê%¿Zìr÷ðŠ»Ÿ*õlÞ?ãv€ì ä*¦-–lt¶yÌI‚Þ»Œè츻s>Õ÷Ã#í‹íEŠNØ9£–¾rßYKßXa;–W·åÈææ1©@bX{X—¼{7*†„]ß­æ×C6'6“¡ªòŸeTæ}¡/×tî9ùRpüm’˜A3òù9½Žjø›õO½›ªQbÌ uU¥AþÞŸÞºú)ó} ÜBŸÉƒÏ4Y¯˜@Íoÿ akî®{mØÿÄõœ•˜¼kzõägŠþ&ìÕŽ5±Ãö#Ä[¸[¯êàÞV—)–Ú{»éßSVÕ½DzÑ_—YTobŒµÛú1ÿ»=NtW¶üúâóoÉÉ Fã™ӥjÜù²4¦þ'[˜¶9«øìa:èºó†ÚÉ,;üdq»ÖñržYýaNÖ£‘m^͘. úpkßòé“—\T|à {<¯íÿ*=ßÛÕ"ñ”«V ^·nÚ¶nZ§R‰üúêñþßRÿ77lZ4cîñwЦºoï?´mÍ]¿‹ÐZ罕ϲÞJ„ΛsNÏæ FLûK‡å­óD}nùâúèâ©gÅâ>:¥×Q•{Ÿ™8é[z6i4aÉì‘]ª['œZEðáѵ3Çíݺ͎)¿œ€ ä*FæívªaÌ:ƒþ‡uPþñḯºmÿÚõWU]eß߃oøïelÁ¼Û°Z Æ–j—8 È²ÙŠ1Ìn»VÝü§F“H’§ÖŸóšné^‘OfM9Õoo{%¦Ö‹OàôÌû[ѦF¡t[—ågy¼ ç¤¸Ù÷Ì:¬>»uH…ïæ­VÑ-Pû—ÅgÛu˜ÕºÉ ûè3/ýÿóJ³ ¿‹yDç^{S¥VaÝÄ¢šA‘zýÿ©Û­ßæÞµŽ>lG¦¬~ÞnVÙì1#Ô5ªÑ†-–_Þ7²B¼‚ªzV•ÚÿV)¶˜~GÕÿö†Ó±Sj6\÷Ìo…ùˆ§¦_ rëß"¦­pqú¢ÄÔ‰²84›H ëÏÝ9êT½åÎÑÅ [{nÆ]WvÆÎ?*)ß}h¿f»kl¡@ŸÁ•¿ëCÕ,Ùwp™Ù1Bû^~É«qGSÑoªù»-œ0§Â,ER÷?0nÉÃfs+i‹ÙE¸§“×·’z¾BFÙèu/âÓþñsŸÅTªÌ»¸kX™¤þzU“ºÞö°D£ÑÎsÛ¬#3ôÿn íBõë¤x¯í¿lÜyåj‹íÑÚiëÖgSS8×/+)2îäÞéù{éxTÃ=¿Š.¢ÙdL·ÄÒs|*ºÖÅ“ù¶@v‘ÞH¤‰~Y»Æ¨µøMÂÍçìšXAù`*÷±[¹?.—ôK™â•zánß&„þzfùI·vý-ÅŸ¨UvÔâ«ÚìQŒÒ~³düîaçZ‹xéŠøêóõ[I×4Å ÚB_íX´ýeHÒÔ,[Œø­¶ÉO˜è-üí®…çcG%[Z32Éœ§ jÑaÖ¨"GÿÒë›.{ö퓺ÓÕ$FuwÌ»}Cô0v—³W>έT${ÌÁ¦ßeé5“*ŸžGU·6š–VN˜ü€2Ð@n#ÉSãï]“ŽWŸÿ*n“J•y;Æþ“&ó8³üÄ—ØRôôÏ?VR³Ž7!´ÔnÅ÷½/,þ5GŤÙì¿«íu'z¸jØå)3¯uþ·¡rÓ„D‘‡‡|[~PPÓTKé–!/6ÿ5ûRrS5—Ðê>ègh©Ë©q‹<éÿK¹”¿ Ð,Ú¶•åì•Ñ˱G<:íøµO¾TÎ8¢aQ:Ÿ (Î}wó]ÈØì Ú‹??ézTÕM çü\}9èüÚÓŸuKŧDÙÈ}$zÕ&Ì®;¿[ìœÄºþb+fˆ«ÔåÈò a±{‹þù*ñ'„Öìx5dšm*&ÀP/2pé°µW)Î~ôØ0výxû‰JOä,Q׊×:i¨4m«˜üLO=½œ§R¥æ“Ó°,k)ÑQOðùÊ7¢aâ]îRŸg磈âgO>1à`'%—7TÕ1Ž×Åäõ%ÛL(úéÁÛ¸ÎóÀýMÄOáçï < 7Êün2|âš›^‰ß(1ÁþÁ²ÈŠØûÿùÌKLá4Øt>ªêEÎí³°íŽ˜ƒévf^¿È£Rš7mX¿NíÚµ«—¶ÔÉ€8h"…<ß¼>® /þôÏ?J8!´ëö5ö365È“ŠûTµì¼pòì3Ó£KGÆ/rh± ŠR§<ªç-j*ocJáîï|¥‚Ar/}í.~¿à†á²®Z¡‰ÖÏ0Rgï”k%'<8ü»¿$ìý®Aõzïøxý$E„ÿpH²&½¼)qObÚzíù¥ÞMÇœŽ÷Ä×ñҞȟÑÌÊ5mÓ±{Ÿ~Ýê'5ù €l‡ @œ ûë¶8Ç•tBÏÌ™p-™N¼Pïo]Î^ûWÚ-ªßÆ85ßnk–¹´ÏŠ;áçݲq;‡_üµ /a*úEK›wbCÓ{{ç‘6Ùb%BYh`hʵĺ7£eüô¬^¨Q¯îmÕªh[¤€¥™‰®–†zì²'RçUÕ‹yœ¾ Èp*j)@Nÿ£*Ñ«0úøë»çþ5{剗Áß_-ûüøì¦ÈŸCŠwš¾bùøæV™³Ž€ôD€ †ÜïúÊ=ßÊžg×-9«ô­¿œX~Ö£UÏTM !1jšTó÷™íýÜîÔ©s—í®Ú]µ°NØ«CS[œ=¿àú‰ñ”žn@ÖD€ ‚ÌóÂò£þ)×KJØ…åG\ºQ¦çøGj…ú/ù}~¥ŠTÏÍcÖN¸÷G©o¦bZ«K9ázìjÞoy3­ljNfüÙÔ óBL s{ò)THSÔ“º]w36vÛ±kLµäVôú¹ú%}mÄŽò†ÈÃC¤)Õ%jBê&¶MúFþŒ¢Vut~pýâÙ“G÷ï>þ8îP]™Ø~jgËjf‹/A$… @yŸN,;›Ì ÉJ°_µûÍ É%R—Yt«N\Øæßî'‚¢KOæL::ðHÛo¥fÝf@ÅÑcÄ_®[so̪ZY?ÀhX–/ Še#¿»×]”JCîr<×ýlÐrpÓd¿ù¿¸÷)õw–45õÈ;V,¡"K¡z˜—³OºÞ:Õ¤©ØTm=(òçÏå×–ök7é\LŠþðïÜKÓŽ¶KÕH&Y€ÒÂßí[q#.ñ›þôÉߥ• !sÛѸ@_ńгõ›ŸžW!uëÚ©äk?J™SÓ†}9>aÁ½F}R¼•z¡îã›Nî»xƧuc7Œº:ºXVï„V1®Ü¬˜pãµ¢ôj×á·K•Lu£#]ÝãÿšÛZ$ÿ“ûÞÞë Ìyƒ’o1P.K) GQÕ3ÕÅê×~¼"›”ܹ !o.?Iß!Ëé{T•¢n^w®mw ·; èø¾{öuH»ê©™@A€ ¬ÐÛÖ<Š+•ú¥G1%G«˜7ÚHóÊ9ErÞ²îþ_ëj§rAcÍRÖXÖt‹bb÷+Æn«S-åXtœ7¦èùyNŠb„ýøž êÛM­˜ÅG¢jéÔ³èô™1­v\:ïÊÐÍ͌ңÍ)uýJ÷Î?õÃép‰PÕÔŠ|‘Æì4T‰­a^Ò<6@‡?»ü&¸}23=ؼ[ìŒ!)6 ÃŽj2$ÆÕÚ—ÜR”>ù§ï¸?€’¾>üoã·ÀËý¯k¥ûíTò6ÚLçÜqÅšÚ{V^ŸW«¹çÿ%FbØàïÙuw¾=™¯ôÆ_Óƒ”XúM»âä#w×_ù^QŒ¸ÿWýöšŒ«f˜•'Ó,9pb9¿]W¤-¯­Æu~ø_[³T-u§šÇ*ª×Yñ!æÃ…kŸ¤“ZRêºgÔäÛÊt' êFŒb×+ôó.@Ö@/…ªa]«¼žà¤XÇòã¾Í³«ÔNb8MÈó5£Ö¤ÿ8’ô<ªJ‹òŠ+è˜0ŸÍ ('àÖêßÂLå_;ÚˆÇ,1®?¤ežãþG—_ðlÚEÉ•P~ V°ÏâQs«-rŽ.=y¨T êÎ94ëvõ¿îÆôý^œXÝöÒ_«—Mh_"…5ö¤ÞO_JïŽP¥¨ì½bòÒJ³c/»mnß@sßÉ¥] %;&ÂÿÙÑ•ó·øÙ½°Ö·Ž~]Û–e„÷…O>ÔeW7Ëߤn'F5î{û§nÍ —þ9 QqÃ臔Ôïõc›ý=ïÄ»Èö«å·’|t ÒWºÕ¯Û›fÏß4{hÉ&]ºwïܺaÍò…5$ñwðùñÉÿfOøëÀûØMF]'¶Lzñ!Ù€dn§–Šû ZµîÖ–bï~ß:šîܬX®MvcÅþw}ÇMíb*fmçþYþȤG)WO5o³E×îØ ì0êkÜF™ëõ] #”Û…ZÉÞ˶ ý™' jÙŽ>véK›&ÓoĦÙg– ‰üÔŒ¬l, µä!~¾^îÞA)œô§jÕcÅŒ•å§8Ä|~ð85­í©iš%JYª{}|óÆ=./ë6Y¾£ù¦R<ÀêEúMïðO×#1óÂ}<¿zòùÕ «šòÈñŸrñÆËë×™±¢Óþ‡bæC”>ß>¦ùö1‚¦©¾ÊWo߯ßz þº{­ÎØVË]Rj†hévT_\Ø<#ò'ò¢Š¡¥M>S#C]µð/Þ®NN ?„Xôݸ´93pÙ@ʤﯸwÚ“zƒ!-ò‰ï<Ö«òk×|›×º+JÖl{9lf™T/h¢QbðÒÿ-nÛ©4ƒÊ¿pl²oÖ¨± .ˆ_«^¸ÅÈ¿fMéSÅäg¿pªÖšváY¹iý-¼¿+\êëêäëšÔ¬ êÿOÒ*3áÄá Û¯{ùm[¨Ûˇn «™¶^vn÷£-›”iœj¾NOój3óºòߨZvÝtú¥[£é·âωêïå¯()5ôÀÙå-=ÿ«ô~EI·£ú™ß§·~‰?²4+ŽØudqÇTœ¼¢zrs˜%KbPwÚ܆Û]S=ÛîóÏwžôäø¦·ì>pÒÁ=™5÷4óWnÒ¦cÞ½;Õ±ÑɼŽC­BØ5ýíÌK–¯ßyöE@Rõ´ VkÚ¢Mç^};×±IäL55‹vkî=¯?sÌKÎ:ÿøWki1bÚÜi}*¨H•mšŠq½v®=OoÝ´çÄ¥Û^¾óø’â†*5§]~QeÁØqs½øaœˆV‰,žÜ¶°¶$Ä3±›§—4Uƒ–»Nœ8vìøé v?,=IùŽƒFOß§Z^Þu§2+´¿œò"pq4Jýé(ÿ3í÷ª]e‰‹|Ibר›øL>QäîÔ¬^ ˜êÖ¨—í8~eä,ØãåÇÏ_¿ûàæ&WÕÔÎcœ/B%Ê”+]ÈD3Ýc³Aë3áÊþ8Ý¢-G¯i9zUdƒî=t|óÁÍûKˆLUGOßÀÄ¢P [[Û6&Z)4W¢WªÇ‚3Ýgº=¾~Õþñ›O>_#ÔuŒÌ /_½NÕ¢†±ï j6#ÊG*Û4ý’­G.h­tý(š[ýu°Õdï·ìn>|åâùEª¦kbU¼B­µKÅCÖª´èƒ|Qr»Q+4ê‘|”˜;N(MGU¢eQ±õ¯‘?ÓAèúâ©ã«×oÞ»{~ •«é˜Z(ZºbÕJÅL5µä$h¹Š¶y©šÍKÕÌìv(-ªÁµ[—ª–}D&¿òMº—o’^mJ5u“’õ:•¬—ÙÍH‡£ª–ǪLÍÈŸ¦éØ(Y ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4 :•$If7 Märyf7!["@" Ѐh@4 :•8k w"@" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @"  sH$’Ìnr8¹\žÙMr&4d¹|}f79–D28³›äXh@4 >8“È"Ðéfýú{™ÝäXƒWÉì&€h@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐ $ öô‹ˆ-JÔµòšjª¦b?¡A>RyÜ~4tÌLÔUÒ§I’EÈ%ªIß €l‡ È@¡N·F¾vã©û?YT¹D{ÇÇ­JjˆÞχ‹¿qpptÿ¢+÷q±¯k•Š$ž2yà³{³ÿ8½éäG¯¨&k–<òþºbºqW²'4 iÙ6Úu­‘æþO¹é¾Lý~òÔlwìv;!øýèbs–L¿öý ÈáTãÇîšVZt [ÁuýÐý‚¥x‡²4±Âµt¢.†¾¸Ø©Í±SoBõô{Œý¸»„ž’xÀÍgmxpé¾w€L4ó”¨VªÛÐæã»[é'ߣì¶~ü¡Íg^Ù¿UÜï§ Å†Mˆ¹®Ø~ïñ]ŒS}4DÖ s©ûÉ«®‘¿Í æSô£ª×Z<¹´–V&·+ë’ù\±{#7,^øË«·R!âÕe;Ÿj]LøÞÙ™~Ëá>®öNX9pw  ?½ñ¤."·«©K¢¾Œ’;ÙÝÛàåÍ߬_îzðäKÔMÔïÒÀÇ‡Ž·t”=¸ß®‚v¼=†~ÞúËÒ»}u‹î“{Ì«f’'<àé…›‹ÖØ?a·¼{ÿ‹[jÓJ¬¾ U¹O«IÍ­¬ô„à€ Ï®®ûæº8…ÅÕS/÷ç¸Oømª5iЂfÉÆ'˽™¯¿"æ4¹ÿ»9m–üy=L°,=vYÇú…Õ|>Xø·ý¬kw¶œ|kc¥üɼã©é”kY{rÓÎ;v9y\ û®îÞÉ2úå@M¿zÜ)„©>JÖM s ÙW×»^Q´t5bÏ}Q713ÍÐ;•z^8ãrɳÊ~%©—ÃÁ÷Bé^Ý¿Yü»ýWApÞwϳCsóì÷—ñHtòç¯QJGA£Üðßö·¶.f©ù°–úºl±jÐ.?Að:t§ÂÆý“k–·(œOK]"ùðì¯6«=‘ Ž—Û7ßÞ ö¼ yÈ­©+¢òŸZ¡ù÷ÇN,³üIËŽ5vØS¥Ùç½[›ç3{¸´¨¾$¦þõIË£ÓsžžûþÜÖÕðÛÓ)Ì=pÛ•ßÍ­¢šÔš„õÈÌ/æï–}95bUTz6©zðþÀNù¢_[Wh]NV¢Ý]—m›þעȉžI®»¢nP³M…ȃôÔá° uÊ5+ß¡hÂê↸ú²²BŽä ¸ôÓ–•½süäópí?éþÒ—ôÃÝëoTŠÍ(oYѪª‰½· ¼½zû]ãöÅxV §Ð-[¯P)˘G´š‘õ/+{=²æØWA¦–¯IÓB±ËøI´ ”™¶¼ú¦F·}„¯îù‡70S W–¾·»Ä3ò‚ù ®ÃËÆ_a±ã&d®'®ÝŽìY·Sþï²–èÖí[Tãð³0Áyà Ÿ!¿›ª ·¯;D]§Vÿ7Û|²ÈvRd.§ï<º _¿¾Q‚“µLkÖy‚ãóç_š1Lý]ˆ<bëÈNxÎfáo¯Xuóé½w^_£ËoþUã`Ìu/ù¯RÜ+w„Ï››[Î]¿üâ½[ˆ<ª?ʼPõ*õû7«RL+^§µÌÿÖÙcŸ¿ãîù)@qBº†©eÑzõZ ©_Ì8ºŸ$ÌãÖê#W/=~ëUòÚ4kø¦è›š5™²§æ³ñ+N8ø+NÍËÓq✩E¢ó¨Ô}÷ÚUëžz*¦ˆ´ª®[ð¿*š‘ïÖOÿ˜¾ò|Lç¹zýß-.­äöôÈ•k'¢¸©FË¥sÇOŸ|}ææÓù“û7Ž{[‰ð»ewýJHÁæ Ú.je©#÷|m·ôÐýC{žßø8zwïRúAEˬJõ¦ÖZ{çÝŒ¼C݆]{µŽê?‘蘛éYüwA‹Ï7æ·Ú朠™jùzŽœÝSþåâ’ñ“^Å{ïÖ)3wáš Ïv÷\qÍ[ÿìòà¿«'ÿ{èóþg[Mùö+%ôÅõ;Ÿu*t)¦ý.¯f]­í‘ÝÏÁßáòƒ®Åjë0$ˆ~ÿQ1G†¶…~"ýÉj†FŠþÜO>‘Ÿ™ d_^~T}X%‘y6O~ÛúÌþgå~OwŽJŸºÍ†L]Q1ŸSÙúe­"¦­>ç{{Ú¶2åGT5ý6lP³JﳪÆNûTØ&¯÷ëvןkY¢‹‰ŠšA‘ºd½ODß¡v¡’$8‰P¢¦–Dœ•¨j¨FfÓï:¿Tõ t'-9^¸V¨n“¹Š7×x²}öß_SÕþ…8r4®\¿lìç U£ Ý‹ïþJ&„<Ùëø¥få< ID®'“ÅLš¡¢™Ø{…ŠºzÌ`‰°Ð¨aaþŠ)àÔ4t~ò{‹<"8¦©æ“ŽèhžØ'`U­BùÓð¼{4‘õ Ð@¶B€Î">;du‹æCÊ}› U%OÉžUôÏ] }væœwå^y£¯QÕ¯Ò }Ûø R³` sÁ.@<{‡w1ÉÀ… ‹µ8½±©âĬ]Ã=GUE·?eòÀ7vW‚ŒÚ×-ðmø¶ŠAíFe4_=Âo_~âU±– ¹Šº^Ì“="Dù©÷ûT* Wt± šZZ‘OYäçãè¢4<ô'/à$QÓ‰9uAÕ¬´Mõ"ðô{4±õd'èÜ Â÷ééOQLŠ3KÐÍ¡ž¿ˆ©p52»Ýr é•7z诊Q½æ¿ÛƒªzÔÀI™&û~ädÆÑ,ØiÛïQĵ?Eò¯÷¯< 2mÒ.üS$%Ö×~|.X½¾|Ù§FwÚ@ޤªmc­!Ü‹eà/~辕úzûF_б6ޤ Ñ)`¤¸æ‹³O„`ôûTU4,,"tdSƒÞzE Å Al}Ù :7ýüÊ%ú‚¾‘¶D&‹? PMG;j~ØÈŒê"t²ffLßöË÷?³lTJ7Èß+Á5¦MJ뜻÷U>ì¿ãÑ©•EvXwÈ@¥š[GÞ ‚ßC©`ûÝû…<ðµ³â™iÛÜ"ê\eÛJy„{‚àyñqȘ"J­²3í¦\–¶çê…j› G"?hû_·÷—VψI-D Ñõd'è\@þÕ/fdâ»#×>’x¥iÜÛ—ÔÓÉþÀõû·Þ¸ºø…GÈå?œ°ÿS‰mòdÞϮܕ ²s‹ÛžK²’óõ[ïšv*N‚Fî¦bÙªn-•÷7e¡×÷}l^¶ÊÂÝ_º{‰™4C³TÝYmÏ÷<*8ŸkÛ×ääÆz•¼VÈ"ä*ªiš^GìÑ[@6B€Î$ê1ÿgu}ËÒ…ò&4Ã>ÿ;*= Å;[ÜÌ<®ö‹Ft/Q¦ÑþEx?<è,ëÚ¸EùD§Ù 7u:s悯 œ»òvdá’|³ŠìH®x¶ÊBÃ~3#“ÆLå–ð - “FÄ\ˆøvD³ú¬ß·}\Úo×û)Uç=ÿ³qçê&záOÏߘ·ÜÑC öúåÔL›oÏMËñûÞ­µý¨—Ô~Ö’|k,êV3Ï«!õqqðÄ_¢ø$ìë~çY€Mý虢ՊömÑüŸ-g¿ ¯–-·X£gm©)ÿäþ9Dbe®]=ä£ë³O…«ZªÇÄÌiL˧*ƽ¨vÛ4̡ي…"<ï®rø˜m›âù4UÂB½Ý}ß¿ñÑ1ñÞßù“?Zø>f:¼Ð÷/¼¼­ÌLâÏg-öhˆ­ û @çm­È·!Að÷ü"’  Ï|ˆº`Þ¸¬I–¿ |ûS$u¹wí•L9ã$ùê…«V2»pñsd‚~xù~P‰z©ÿJÈ A7öÔiwå¡"7žQnÄ‹‚¿·¬‚Û¤&kWÝò‹™ÒéDyí³Uêî¿Ò½Ò‹£Í;^¼æª¸æÃ’9º-î¿¡UôÃ_3oßÓkô´[¼æÎ©…ÛwFíYÝÈ´B»“†4Ü"ïw“¦k«sð…Õ¶ùg×ïw¼ëìví´›ŠaÞš­j¬ß\×tÁŒ&;¿ ŸíûW²ÓwÜûmÅõ¢ºikì¹:vô™=7}ƒÃ‚“:íjÍìU£kÕÀÉÅç­vÂ®í«‘µ•³O•>ß²ßí{®!1Ÿ í-ªqдh±1‡O)õz¥bZrÁí9­7ž[¾íÉU‡Ïϯ?{.Úù,ªÖ³ùGí_Ú&ŸžC¯›Ût­[l/¹÷ª¶ÓV ‚Nþ"£ŽŒžS%¶óXäÑ]@6A€Î 4L‹X ß ‚—Ó;oY!Ë$¿/”ú*Þ]µŒÓ8­Rì‰Aé1|Zéö§Dêq憛¿K=“$C¸fþª .îó|;}ºçy`ª‰Ž²*ÝÚ=x÷Hì›ù7çÏOô6•Û_ýÐ>¹J4‹µi¶®M3%Û jbóË‚Á¿,øáŠKå;Ù»aÕ›n4ØôývÓUŸÖ¯J°¥ç—ž)Ü·†aý¡Ý"”lj<šuÖü¼F‰Š"†èú²tn f\¦±ÙÁ·ŸÁõµ·IªF¢¥­èg ú$ŒRˆŽ’˜‰Meáߟ¿'ÑÐÒŠ^;÷ëGß0¹ ™Æ>¥ÛŸ‚°O·Nx E:—·H¦[=‹ úû¢&½–Þ½ôȳr]s4[HÇ\¯ÿÙÛ¿Û k#@ç jù:µ+·sÃ㯂÷¶Õ›óè×¥`Âßä2¹DE"¨å-d©-|¼Ž^xÖ£_y“¨JòP_§s÷Ý-Ñ6Љís÷•OxóøC>4ÍŠY ÏœÙýã§íKt®3Ë©<4ÀãÝñC©•mòÂ^Þ¸ã.Xþ^Á$Ù‡½FñêŒí®úDîõí• ^µz›q†<ÄO±™u@6A€Î!äá~Þ1s½û¸ 2²ÒU1UòVé¿èòßϹH,˜ó`½EÑÒæúÚ’ˆ¯AþŸ½=Ý5nœÚ¶¨š W´Y“‡»½Ÿ[kZ>³,e¢ìïñÖGR®J}Á/@ÿüÁÕ£Pó¨>e‰~áêT^9È„ûgv{PÒF3Ìû‹áÿF ¬§+Q7«1 ô©¿Ÿ… GL°342ÐÂüƒ$FÝÇR¿WŸüÊÚ*z§eRE¬ŽFÈôCbU¶ýI‘…º~x°åŽ¿ [D?ÐÓËÀÌT3ÑŽeyøW_×0“ü*‚OTƒ\wŸw(פD‘¼úYtŠléã»y9ÑÞâŒÞ › @çÁN»ú-³{³*¬àvyu§ËQCË5¶¦CA-ÅV‰^µÎ“—½±ýÒík¯>¸¸9Ýt‹šÕ¢MñšZ¶«UN‘>%ZE~?L{çÁ}O=¾|rŒ0+gÛpæ¦ÍŒ‡Ýé —=>¸°õ¡Âü3¡³‰ŠªqÍYƒÝ¦í¹rßWêòö­¬@ñzµê•ÑŽNÄ*F­þ7îëþ}[ï¾ñ—FF{Ë¢åÛ¶«Öº²ï¡¿\ñ¤¯—Ì›´Ì¦Çáß-¶.ZèSÌ™MoM¯uT7цóG¶-™`¡@¥ÚŸ¨€G+[®ys‚”à0{Cä¯-þÜÓÑ:á4u!öë'wŽ¿Éýê†_®Fþ6ë7uÚï²Ìy•ÒÙ)6©\›ÑÛdè@»h¯ý«z)QQ-oñúc#’­¤n\~ØÈòþß\åßuU~¨«j^¡ëú ]ÝŠŽM·þ»õÿ~{þž î%<hÊô¥S’ox ¥Úÿ#ýò#o(õN¥U}ð²{"wr4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4d‰dpf7 2‡\.Ïì&d‰D’ŽxäÞ„Üz$d"4àgȈ°«Ø1ÀOF€d¸ôíxþ1ÀOF€d¬ MÏqˆÑ~tº<¸Jf7²–Ÿg‰Ñ~túàe¾ós:žEŒ¡Ѐô—‰é91@!@ÒSV ¬ÄhéŽ H7Y¡ã9QÄhéˆ HY6=Ç!FHh@Ze¯HJŒFh@šdýŽçD£¤zÙ4=Ç!FH4 5rRè$F… -»w<'Š @Ih€892=Ç!FH ¬Ü+‰Ñ’A€(%gw<'Š Qh@ÊrazŽCŒð4 9Gb4€8h@’rsÇs¢ˆÑ4 )¤ç¤£\Ž øÑPÄh ×"@ ãYb4  ßžS‡ ä*h@Â_Ú£\‚  ã9=£ ¹é9#£Œ>/‘@Æá x—шÑ@ŽD€N7³îðʈŒòW5>¡!ýÑñüÓ£† ¹éùç#F9r\æ"F9r:ž³b4­  · =g5Äh ›"@@ÎGDËʈÑ@¶C€€ŽŽçl d#hÈÉHÏÙ 1ÈÐ3²/b4Å  ¢ã9 FYrÒsNBŒ² 4äĬœŠ d)hÈ!èxÎñˆÑ@A€€œ€ôœ{£LG€€ì •;£LD€€lŒŽç\Ž d 4dW¤g(£ŸŒ ÙQ ?"F? ²:ž‘ b4ð  ;!=CÄh C  { A,b4AÐ9™\&TT$™Ý iGÇ3R ¤;tÎ#}»ÇnõÜû7ž|•EuM;:¹®Ff7 @ª‘ž‘vÄh  sš°³7œöѰK‹yK-„Çwç}æâ'ËìVe:™×®]½{¿ôM®ŽDÃH?©|eoÒ¯|õš*?«q@Òˆ;H_Äh ] s–ð—ö³¦}”ª½ºV3‰Pߨ{ï›Ë™Ýª,@Å´WŸ³=#‚\]w5Ûðß‹¨M6ÃZt+­uI.—‡ø¼ÿüüÂË;7_¾»ùòøœ³å¦ô^4³°¡jæ6¹ÏÈ Äh Ð9Šôý¶»¯#[å·Ñú¬fÐöÈàš:š™Ü®,B¢ª›ßØ<¢ V ]•.Íy¸ËžãÃ{=pÂÏÙ9³ü˜EÝôè‡F&!=#££T#@ç$²W—¢)èhkÅtJ4ò™Xfè†û쩽j_Óßöü“/Û³–¨[÷hõÇ‘ç£ö… BØUoüº”7&Aãg#Ðàg"F©@€ÎI¤ÁîŠA¾ŠWŸApñÒ†»ázMÒýe8‰F‘&¦Â¾‘å½|ÂcºïñSÑñŒLAŒD!@ç$òˆÐðŸ{a^‡&> ½Ÿ{·J&=çR_O—1ÐHÉDdÒ321P:G}~wùd‡[W\?F—Ÿí¬q6溺=.\-­[SêñáøüëG½}ñ>42'jä5µmZ¦ó¤:MËÅŸtBæuöÚúuo^<õtqþòUµI˼|»*gT«h]1ÄëäÔ ‡¾|ò>ªôiÎê:s¢ojUkÛãŠ7;nÛ`};Áè×_þ[@+úΗï;í•k€â^4›]š8»¡Fä¾nv_>zß—˜­íûŸ=RT+ÀóÆûÃÛŸÛ¿0šêü¿Væ1íO‹°÷v>ŠK…»6å ‚t¦ˆ&?e" ²b4"òAŽ j”¯ÖÀzÕûúžvú’— XV˜¸Ü6oô?WÕÜJ'¦–ü‹ýÕ±.<ü*äm^{ì\›üZÁ¯^ÿw畇»ÜÚÿÛ_]ôcû[#¼ÎÞ9z$¬TÏjCg,VH[öÑùà¸sן´?å±êa»jÆAUÓºaù^E5– {à)[Nêg¤ùŠ›ÇÄÊÀlà•‰>: ²:ü,A3ÕlFõ=4Jîw`s‹®ïâM­§Uk亂+œTØzÄ]uýtyæ3]#ׯVS¾ý©&ÿzïÖª¨Ђ`[÷á¦êiÝ!_üÁUñ34ÏÈ‚”Ñ<€‘  s5 ë:!ÔãÅ‚ Ì«·/e0ýɼ^-l•> zôܶÃÖ$:lÖn_¼hÄòq{üOõ;R³vŸæqݸí†m—î({ uiK¿‡µïx»Ü[½·~塆ªêyʵ)ñÖw£ DhÝR…ëuHp¡D]5‰.a‰šV"רèDwS ÷o¶­ÒkOͲL´oíÓ?8UíO‘<Ô;ÐË-,fç¡!>ï>?>î°cé+wA­`·¦­­Y΀E‘¡±ƒð¬,ÅÔ—*@ÎF€Î-"\7?™­³¾3J˜ÄuÕªèVUÆxÏ]Ÿà×[÷ø5c}ªqÃêCÚ5ŒGµK²îx ÂÇ[~aC µ3®¥åêÏÜTÓRñÈÌ߸ۊ ª±íWF„}ïå­Ù®WaP­.J5$=#}%zn/±Ù‚2½Ñ<˜‘« s iÀÍQòÙT²J25 °îú‚óÙÏ_ÇGÏ’¬bÖ¶nÏïö ¦¦™§e‚4Döó^!µ-GÜéuA\û•!)9½ÛoUbzéåÒ`ß WnÎ:^ÝxîáÆs‚u±Aë; l©Ï(¤‡Ÿ73a~ŒÑß=°ÉÐÈ=йD°÷}§è FºY„4ÞUêZz‘!1\zEy²æ¼éß~U³šÅê4ÿ~Žº¾sÚø\¹6¥ÍE—×[­qÞ5lvÏ´­’Aæ@ö’htŽÃã¹:w} ôþ}Éñ|wó‰W’Jãú–Ã?½?½öþÅ3^;øF]‘¹/ˆbÛŸªÆ êÏÛó¹KÛ'BÐÅÁ'[6íQÏ”ÎC¤EŠÝÏdd;Šáû‰^Åã¹:—™!ºxÝE[l]]O5QôÄò/·. «åE¸`Ú¢Þ%JÖÕÑPßL«pôÉÏls|"ÚŸvÃUk>9ã'¯ß ©×.‡|#§cðr!24r<t.¡¡3–WC·pU+«dþwöJÏBùækÖ)7³†¿žVôèÌ¡|ûÓçîôlÌ!2@ R7Á‚6£8aÈÈŽø^¹:wPÑÍc¢#_ÁÛ×/\H&€J]œ®¼ŽºP K K$«ýdÊ·?}ȤA¡15ôÕè@Dj%2ÈÈñÈÐÈÁй„–QùB‚Ý3Ap{ÿÌ]V¦P’ó%Gøù+ Ô5×L[·kLrG¤Çë§ÒíOò@/GWÅE½’¥µ3öÎc%šžÉÈ”˜D†FNE€Î%Ô jv6YñÌ[Üw­uk· R£z%ÚZŠõLüß}• ú)ôAǾˆÊ¾Û!ÑÔÔ‰|tI…€·!rA;½¸J·?=D|Ü{ËA±¢UÙ¶å˜ÈiG†@Nòãã9™HM†FŽD€Î-Ô n\wɾk_·…;¦ìûç˄ӳÉer‰ŠDÐÈoUXWp >mºyû÷õòEu¿ÊƒýíyæüÃh==ýèÑî—œÝÃ, ÄÛÚÆ‹ ·_²+—¶œ/4²™žâ±& þòÞñ«ø¡Ôʶ?­äá.NéÝBývÿ6(¥“ÒM€D(³2c$©ÉÐÈyÐ9‡<ÌÓÏK1×Û¿÷レš<#ŽŠe™¿Ž|ÙòúËð/WF¬½2ݬBU=IxàWï~®4»]òkY5Á `ßÿÚ-÷<Œ·z]¤²¡zPà‡gþ*5Ë”4<}…P×O¯\¬ÊYGí[bT e‡«2Áát¯ÒÏ«”ÔqÊûWÿ™ít$&­&Ý<À)DðÚÝ|þSS]yˆï߉YqEЖ~~ôÙ³Fþ¼ŠÞiYD¸"VGHÃ"yd*Ûþ¤ÉBƒ=]ßù(JÒ‡îì}ù­¾,,ÌÏÅóùùöŽÑßõ­{oï>¬¥ã7ÃÌJâ@¥ZvÌ[ü»“ÁÁIÙñy‘S s† ÷ó«n9軾È{û±%ìA=o­š‹Î7íC•6n¾É¹Ä±…·Nžp~éôùá™Ï‘uLJ坨¼Æ€•ê*Ò§D³Ü‚ÿ-Ò=¶rõ«÷þ_Þܰ®Y¬û¶Ú½{Üiúìž<âÖÙß œ+³vìC UÕ Zïèñ®ßÉWüÜ\žÊ,+¶­Ü©ºVô뤊yßî뾜^2ÿÉc—ðpïPÁ¦`½_l›÷-cëy®K {/!âá˜õ­ÇYÝ·ð‚M“Ö{*šùì\7­‹ú… tÛß÷·Jñ‡O(×þÄÉ<·lîø‹sX¼MÎÿž[ü}5|Æ¥[•®Þ¡|»Þ…,ux½O†\¾>³›€K"œÙMH¥YwÈ7È(UãM) !@ç º'=Ÿ>I‰Šê–6—Fþ$[IàÞ?}ëýóýæ&Wf6ù¡®šu©Ñ—KNt?*Z¶Ã;nÞñûí…Ûœ’·I°e݈‹ë’ox ¥ÚŸXSòt}€Ø[üˆ ˆ@€D @€Œ"ó:vò÷wÞæµx¾k§âÙ-v|y÷wñM§Ü¹F£lýwš”Њ·)ôóæ:k×Þ“&¨—¿âº*éeh+ñóe·G2È6¤þÿºã,žÿ·Ì­Õk­”o“•èúûÓÌÉŸ½–î=Ï#z“Zå¿þ3(Ÿñ³4išýrwzÿ €ÿl:×ÓrXÏ%ÓŠÙ˜«3›SND€DM§Dm}áq€ h”¬k˜Ââ\Y”DË,o­¿::ºf£cdQzÉã¿X'U=Äý连B¾ÊSÿ)U؉3r*4HT¸ÏžÚ«ö5ýmÏ?ùR›}5+,¶­¹ÓGc›:uòdã¾X‹¾›ëœ­qÝ5ò²óÙ +l›‘O3‘zaÏÿ9~Æ[«á&•IÏ9$BpñÒ†»ázMÓ¶uÝ’íË—LŸ&e&j ¦xòÛ*ÿÈËïfÙßó×>%U¿«öüÖœ¥~ Ûn¯—?- ehð£0¯Cç¿Åh–ŸÙ±ý-GÝ#?\|\;ġхª–ñs”ÔïÔðËN‚ňÕ,ÈW9ÿ`²šˆàׇîÜáhÝý£Oô¬jšFV†ù‹™UÚbHGý¸žOùWŸ[ëoÜýêÑC߀ðÈ *yl,Ê·,ÛylÕZE5bÇ„9ô[4d{°¢ ÑºïÙ£Ö§ÛþôÚ_¹Ç¨R¯zcT.®}‹¯“S/:øòÉû¨Ò§9«ë̉¾¥U­m¯Z–Ô¢æÖ8{mýº7/žzº8ùÝ@- óòíª œQ­¢yLßkØã+#[\yà¡(]:vûh#ÕT´'©Ç‡ãó¯=ôöÅûPYämóšÚ6-ÓyR¦å4c{|Cnv_>zßEA³}ÿ³GŠjxÞØ`xûsûFSÿ×Ê<•ƒ+$F…‡ÿWþJÛGþ‚nwfáÞ‹{ëÇÞ¯Ü÷ÄÙ•W"¬&¶ïZŠt•ãñ/ +‘y¿û¯ÃÎ×Cí|ÍÆ¶RÕPOêõòã݃Ï÷ð°­ý¿Øæô`VãCg?j¶eû¯lVº †Ôãó-׬=s}í­¦›~™þ‹IôØeJ[ÆúpQ«ãv~Bص C­Ý^¸©é[èêjkJ|Cä¾ëþúAmß± æ‘Á@UÓºaù^E5– {à)[Nêg¤ó˜XÅ ûð:{çè‘°R=« Q°X!mÙGçƒãÎ]\Òþ”Ǫ‡íªG%Tr Öjlº[› þD‘í‰!ÿbul£ ¿ y›×;×&¿Vð«ƒ×ÿÝyåá®·öÿöWÅaѪµwÒÍN *l=â.„º~º<óÎÆ™Ž®11Þ0mÿ‰aËt~9ù`Häá¿1ò̵¦]ë›Eý±ò€÷ÿx˜¯Êü?,µÓvÈÐd¡ž{ÚmÝx3B0²ýÛ¡[+›¸¾æ²íG”5.¶îblYè²¶ETzV­ÞlÛ¥ºEu›‹7è[©þȵ#×ø¸Õ¤Øð1u4£òІyÅ6†Bd`Œ ¹0°Za-µèàööÙÜz{O~‚OÛUö¶ª‚zžrmJE¼õÝ(‘Z·Táz~<‰P¢Ý°íÒåŒc:`­K[ú=¬}ÇÛåÞê½õ+5Œk·ª¶ú÷#…Ŷ'šÌëÿìÝ\Óéðgc£Fw#!  Š­`×)vŸz§gž}Ö­çýí:»õ¬3° 0,Â@%¤»aù_ GÃ` ƒÏûåëåo¿xöl°í³‡ïïù½ßÐW”žÕGŒ7øqÇoœü(\Pí¿©õ×ô,FUnµ²O§c'½³ROÏ}9üqkãbŸõ, sIZ¢0¬§¯³¸ñc8Ÿ$„²q¨Ìø©‚VçÖSú[k:QNÉÎÒ”:LѶèÓAÁû&<{þ0²åp«òCEÕF_‹„'‰ ª9|¢T‰HÕë×qd±u4šè’!|ÂÍã *nAÊþp3Ÿ—X47)’–fFäY !á·ræh©•Ó°’Ñô§c«ÕµTÃq3oßø˜OHÚ©Ÿo0Ìß¼æÍØåŒs ü¤êˆì„‡¯Å æfJ© (;;øº§¨xÁØ@¯äÅý(tÓ–êä¦0U&ù¿c·ªèòTªäÎÕK¾2S²?¹ÉaâMu ŸWøjÙt¦*á&ffóˆZ)õ"5€®=à@÷«Ž·Þ {ø>àè{bºÐchc„ª†?k€ºŸžŸ#^ÒÔ`•ûÍOOOÌ/±ÔTKÙ“ªf" ÍÜ$ÑlL™×pb"nì¸{3òCXFz&—ËÔhøæge&Kž™ÐÛÕo—¾“¨5؇bö­—. ½2QtCÇeÁ"#9»J9T 4@ÝÀÏgs$K :½ÜR]>ûëž4ZiE½šRÁH,'›ËjYvSåwoš»÷[Ñéå6e¥½•Š2ƒJ2?.kvé,﨧àñÚvÜxÄA«´Ç£ ¦©[«©†nÜÉPue¢hÂ<#CSUœ;Ø  @Ô T¦"]²”—Ÿ_nì¥*2 öäpyÂ=‹×-8¹Ó¶ÑÕè2~ÎO81N”ž‰sÏÝ—:˜+ÀNWeŠk k„ðEÁ‚ŠUK¤øÎð+P7PÕ5 ”ÉÛBbã“Ù¤œ3Ò¨êšú’=32²xD·x€ægFHæk£ë›+Ê6@s£Â¼?ˆ̆Ø•˜Ü®†PUÔ´• >ÞäÔ4A€†ï ¿‚u„²nGâýŒøðçQ¼&veŸ§¢ÛÖø<'$:.>X ²vä³ ñ’^[{ºlûÈKK—4­¢¯X;'ì‰05-‰O°ð«EDp¿‰%&‹ƒï  ŽPPk?Éœú,‚OO¬|ßÿ¸½fYAQA­ÝD3êóH~vÄùmÜ oä'}¾öHTÂAmïÚθªY“RPÔËg)Ë (1%gË¥ÎáV-AÓÕÛÖÞœLHÜÉݱëqµ?ø® dûjFÏý»CÔíœ}r²¾bÅG|7Y>Gwz]ʵ¡7WŸé)Î)‚ìKƒ–¸˜]d;ÓrÃËyóìð9"KTýQ}~ÚºûP0I?uzªÚÀÕklІh‡+ Ó¨¢={Þ¸÷ŸÙWóáëößµTø9þKn<ÍY¡3z³“AUG‰©ªª,qMsܽð8¶‘ÙפÌ06±R!QÙ$æÐã'3¸ˆº'ÈMu:8¼†  Eh–“»vÜ|æA‰Ýp|¹ù˜%SŒXEš€/ Pq"Ô¼ñÉÌÒVxÝ€?òìåÝo8„¤x®¸ûb̨6Ê󽨺û™ðáÃŽÑ;Vˆj“ý~ãÇ:ª0¾½«QTú_ØÌËËð[»½ãÊ(õ~£®ooÙÂ\™·=ÙS6úåæÈŒÞÿž âÚw~ô¾ËΆfFJŠ4 Ÿ“Ÿ‘ÎuÚõªWc%BQ5™|cpB×ó·nk“0~¶c 7>ÞÿЃÓw²ÑèytÌäV…ˆ<®$àò¾.|ÅgóE„(šf½;P}ù$ðÆ(ÇׯŒ¼¸lÝ¥ãVõ3ó‹†Ï¶4ÿbžÉ‡F-4èÙ™‘ÁéÔ¶Mk’ÄT’ÿ%æ}”‰“)CüÜŽä|Fn›WøtG)ûC5j²ôbÌŒÞßq²¼§ïö^®×¬¥¶¶*…“™“ö%RqØÃ)›~5|§ q®è^ežv¼œøôO¡YlÉ͌԰·éªX,%¼&hÙ¨Ùù/@ÎPu\™/_„Y´…Y]– 2ôìŸìåxÖ_6²‚Öˆ¶ÅºØç¹Q‹×E LÝÎqk«…œPch& ç÷:îwÏã^Eƽ~‡Q5ibÜ|D¹“š6þZ¾ÀhÔlU°iÏ]ÎzÿÏ/¯rDŸD ,K·_;šÓ²m£oSáqÞ.;2kMdª$SßÂôµ;p÷!{µÈÀ%Ý¯ß Ë—ì÷~Æf· ¶³ïŒj£@hê?ñyìµsÞéì°¨ ¾‘K¿ƒZ3)ªÓú_6ª\þ{çûˆô¬<Ó¶6õ=Býi÷àç>žß­If^MvOŸâ¹ôp´xš7‘ðÅ;:,Q4hßf—;åÒ÷‡P4ºö<nwyƒßµ«áïÂ^ÞL]ÑÄÞØ¥g›ŸÆ7ïXžóžO9°po|¦¤‰`¯aÌ»,+³agÇLj.“rpNðo{¦lNÈ/¼.Üo^S?Ñ‚®íŠ£ûT¹lä4@1¼ÛÖ7ýÛþIð§ªf_µŽÃŸ;z}VîøƒµQíhU-ŠvŽm pœó‰+ŒÛ-¼lkGæ·˜,ÈŽX3ø´OžÑRÏ!]‘žk]ÕéçîÂîHQÑî0ߣÃü šk¼jâ­U¥m±h¾öCó²£™ÚϾo?»ä†ºÛš1nkŠ¯îæ½ª[‘S®m/£é*õGˆnd1x‹ð_9»0]÷L¿»§œª‰î¸iƃM5×>Èh€"©oW¬ ÏU·¯^3 º-œG·M—j ÍvÚϛέ˜%:ý,yû˜‹ƒ_ wSge~νù{V½¡¹mŸ²ØUñ8h€Â¸o÷œ?žBˆú÷îÈwÁЛrdèY‡Ó9„Dܳ´Åëm6ê~̹†íNÑôËÉ©uú„H€Ú u?õÕ‹=»ý/Ý{ù)[\aHUÓÓ4·Òsìä¾qµËO°?ÞôݲûÙõ‡QŸSE'(Ñ4´›uh2bj·)½õT¾fû6íô$UrC±éõ„im¢‚þ9øäÌ•P¿°>aZº·ú}ëà ͘â‚Eλ£ï}qÕ/Ytëó gæ ñ‘šsž¯ÞÜBT<É ^¿Ì÷v@LhXR|¦¸|SY½IçI‹ûMucô.ïËŸ=·¯öM/8¹¨é°]­hUèO!œŒG‡om<ðâ^@r†ðnÕìZÙ›ÚsÞpÖ×]ÓnìoÜçy¼ä†’óÍøi=•óB¯?ܹWœÕ¿|ǰ*S—ɰv;²5Àá×ÂþGþ}hÉàeŒýG L1uóÚïjŒ h¨+xÙÞ+wy¬Ë$t§Ý7ÿia¡N2câŸÝ ó®úl>îî‘v6âyjUÜŠŠt?6ãïi—rHþ‡Å]½Hgj©ë©1Ô(9邼Ï>¾“:¤(†þ:ÖT˜-©,»çê©Í>y$šm—ͻڈ.OAeÚZ¼UrbBvx™éÚrÚŸý:8ëhòÓì>»ð¬ïL¯7A·—îî¦"J¨L“%>ë—džk¿vKT‘‡(e Ò?ÿÕwó’‡lbä8wë@w+ZÊËVø¯~ðôÈÍÉ~›KB­Fï‰qìá·~]ÝkÉM{áyuÏêk?Ln`.ÅOB¡ÑÄŸ¶]>Õ›KHÊŽá{#ÕC½óŒ–xé†Òg1h¨ ¸o·ïì±ú#‡(<¶äôíoó? ÿ¥óý%-Ï}½-Èó[¼]”ži–ëæ.hZ°cïmÆ8íÚÃ;üߣ= ô^n±f‰ÂEÅÔ´½Va|]=ç^7 ñÄ‚¼¤³S6 ?šF²ƒÖޱÌA Û9 à&…¯"D Y]û7+y!UÅn÷Õ #õ ¢dÛVêáÖïŽKÝ÷ûó?:¹›{O¥Ñ•Jy•ª?bü¬ëÓwˆÒ³vËó㉧Ü%?4ûÁ‰oçñ,êØ¡_z5º:R½à4Eº’¶dšˆ-û 'Î8³­‰­ÊçI.‡S¤úiÐu&qÖáø½dû(í¢³§ÑíÞìOIGG5qs£$Ùœ¥¦&óÒê2Ñ-Ûë‹1¯ýÓ¹­+˜W¤º¾–>wÞ9ùw—Â3`S‡ÚæÝˆ‹yÂ(jüé¡!ê#B× >ŸP©U®ª©æá 4|wT£>;*„?à‘ÐMWnL˜Ð_¯¬tÑží¨ùùÏDfö´U+¼‘ŸåsT4™4Qh4©F•SnÁ„É<·p]•®¡$^HNNæ“ZÊÐ f½š[Ï #äÕŸ·”ñׂ¾–>™x|²^ñï0 CvtŸg:!)ÏÇÏhÑöTsƒÚû"Åò?öÙù¿€GorDS³¨è º1uQÇJÏÕ]ÍÃêÓV[¿ÛâÏ’ülp桃F;+‰g^>_AQÐL[mþͫ͆„Ä#çvÌš÷»Ó·}A’×¥9WDçÜÙþ6l”YUÇG 4Å5Í‘o}"¹Ím¿¾I*j´j¢HÂòÉçG[nu9àÁmp¢>;û¶'âP´ï¸ºßí‘WòI¸W¿1Ú׺µÐ,òÐø<UAˆ¥Ïfîwöµ(9·‚¡Ë΃-î H#$íì±_‡6:3TöÓœ€?éòµ™#ž~ÒuXp{è Û’ŸÓì·¿,ZcH¯µ[ ÉëgkçG¥U~òj^ñ“Nž=ú]jyûPš,c{ƒ&l»unm§ˆ¿Ä4TÐP0š/›é™²}è®^ðƒqÍL53q±ÕÐT¢Rø¼¬¤”·oy£î.ÛØ‚N(Š­WÏ<½eìɈ?Z® YÒupkmUNfÐíGk·…Æb>êçë«, Ôr8’€Ëgs„!¡ÐÇŸË‘¤—ó-STÚþhMóùÀ%‘sVzv1PÍIO4þáÖq§Žó;X\¼N2Žö_x«‰™¹";ö}L$ÕjXseCòR_¥¶i§)™æMÀaçrÅ rÙ9Ü"ïµÒõGAcØ¡i=¶oxÁ‹¿pÊõÂe‡¶ŠTv~r\jÄÇÕé ž¯0þZo!¼Û‚Æóó‹6^^~lèÛ-£NÞËUòW—ªeîÈj×õ·æK…w‘ë9áØ.Ñ£Zjkã ß2ÆM¿»ôiX.!‘!û·ÆöÙeÊ,ºóÎõ²h.ÍjöÎvô(Ä]+ùß÷+Ý|5¯×¨:£~¼5’—ýåËÉö¿­²˜Ök˜£xn€››—‘rçÝÓÇï>?~wå¯[NŒÞ¸Êªêî9† u]sàÎ%_FûoßõäòýðÈ/"¿ã²¾µ‘«»ëº­nÃ[|½Ÿ¢î˜ãËÛŒôÙ´ëéõ ÿψÖiæÑiá”n“{é*Mt¹—»õ¾þ8A’)cØMÿŸc›Ã÷Æõ×L9÷ãòs?*8-þÝÿÏ"±ñ §Óñ{…VeÍì¸x&¡:/_üd…I±ŒÕ@S¶kÏ"¯…O6£qGu܈cÏ>ÿ71¶`‰Óiêý.Nn«¬X¢¡RUóð€¢ b¬¥_PF3ópÒ³è“#àD¾òë¨q„ýú¯«œçl¦Šqè†êƒvíþþ«pGŠ¢Mß{úö(/¥æâ=JÛ¢ýó¹Õ?—u]sÀ¦ßl*¹Aøa:àšÇ€â«‡ýÆVdųèe4]¥þ14ܧþ+gõ.£ß¤Ž.g‡R©ºû"Wán4óŽwÓ;JÛ8Tb³mÓŽõ ‹Ö²èСÄt'ü¼÷÷Ä%ÊJÌ‚O Ã@Û¨’mWóð*㤜n¿ãL÷I§×È}©5…n:¢ÏïCf~Kf?Úñ1mˆ3.ÑÙð @Ô)t•Æý—º‰›')Ñ¥PªR;SÍëHq÷ÞgÕîµw—5‹ÂhÔM‡œýéF”Â!ZÁop ä…€—Ïù~‡W ;ÉsÁ« BÊ.¯—?|î×Ó.Yª*¨nˆ êökï½¼_Äò$7­·Ìýg¶¦$žå‡<Û¶(ÐÏûK´¤T>øÖ`Æ­‚Ã:ޏãëÈ*·åÊμ²îá%ÏOo#Dg¢2tuº7¼°Cw§Â3Nð“n=Ø»çã۠Ĩð¬ñ ³LC}g×ñ+[¹|›$Z’xmŽ÷€œ+üÙ€\B€¨K”è%sÍдÃBòãßþNˆ0kê·îooJ/åðRUæp~Òû }EéS}ÄÈcÇ´ÅhßßÖš·í·Óé×Ç^lÛþÇž†ß†q)Jûm9îôõü9SG£´—íŸ&G=ßù¯{‹©â¹ÝèjN}íyŸR ã$!*öVnŠœDH¡+”1*L¡1KÙBSWU—Ìøðø‚ƒë¨Óm›6ÓVzréÇq¹Ò÷¿2ùÉ™I±ì‚ç'?/åsÂë+Ç·¼#4óaÝ—înë¤^´˜œBci £•x`]MEÕõ4Þ—C·o³5ѳÒNû[ò£ª´œÕDëô³”ÜGO§u›£%Þ¢ Õ¹õ”þÖ…⨒¥)yšLH´_{ªF9³'Ê€“ûªCm$Ƹë°íw(|iú_I<ÿÑÛú”²^µÙ„vC~²·Ö(q*&U¥ÓÁá?ÏzøÚöÆ.=Ûü4¾yGIú¤(:­ÿe£Êå¿w¾HÏúÀ3mk3üXûÑ#ÔŸv~î#àùÝšdæÕd÷ÜýS4D韦þÃñŸÇ^;ç΋ â¹ôk1¨5Sœ+©úc†ïɺ±yÝ›×QNr>±0wûÙ¡ç˜&‰^CÚø'ÞË9{øÍdÞ‡1Vë-ÜŸ)éi°×0æ]–•Ù°³c&5ÿV>Q¹þ—‰ŸxäðÀŸÃÙ…V…ïóÚT|7ª²–cÇÖœ=F[)ÍÇ,Ó!#Ô^œw“•˜! Ðõ4@ÀpÞ>åÚö²¶š/ Y¾°Êm«Töpº‘Åà-ÂåîÄPw[3ÆmMñÕݼWu+mwš©ýìûö³KmŠÊtøuà__oÕ÷º o‘5{¦ßÝSn¯Ä*ÕÿÒQušðð§*XÕýäl›>—§}•¤È2ÒDt}… +†E¿¦öÌWþíYß»3PS d…Ÿ÷zÓG\£É›ìµ1]o!@È/Ñëɾ•¾—^j½ö㸦ôŠy… œ'ߦµïyèb³&º|®ß  *(”Éß» uÎÒV˜qH¸Ï† ß»PÓ @j‚j_u þÁë á@€4€ ¤€ rB¡ ü ŽÃëêh) @H@ ÐR@€4€ ¤€ h) @H@ ÐR@€4€ ¤€ h) @H@ ÐR@€4€ ¤€ h) @H@ ÐR@€4€ @Æ(J…+Amu@Æ @Æ„á¸Ô P? @Hd¯üAhÔo€\C€4€  F”UÅú wÐR@€4Ô”’U¨ß€z@ ÐR@€€T¸ŠõP? @H@ ÐP³$U¨ß€z@6J½ê|ƒç§úð%¤Ž@€™ÕO‘o ¦,m…o u4€ ¤€ h) @H@ Ð gJJ¶ØJL• 5äŒä’fß»Ðp!@HäOùƒÐ¨ß€… h) @€\*«ŠõPÓ ¤€ hW%«8P¿µ@ ÐR@€9V¸ŠõP; ¤€ ЀØ)Ùùßþ^C¡k¨¨+•2§:” 䛤Šõ•Ä~·õÒ–s1Ÿßfd‹_5÷Î?2‰EýÞÝ+Ð ˆbÓU£­"ùϯjé—XÊü¤Ë×fŽxúI×aÁí¡ƒlk2(d}^a{èzl)[MÝ>ífÇ,´*?áp‡Ý»Ÿs‹ìgì²ç¹ËåæÕnäí æªU}Ð!@Ô R¯5 5¡Á>ÕzYã¦ß]ú4,—Èý[cûì2eV|LU©Z®ˆYµ(!)pË¿³×Æ‹WÑZlŸ¸f‚–r‰1qE½ŸŸ-—ñbÍ‘©ÿK4š6ró2 }ºp¿æ²h@Ð5kïÞçß» PoMžìú½»õMÙ®=‹¼Î „Ѹ££ÆïÂÔÓm·tð„K»† or6¿ŽþÙP«¬Ýóâ.íK$-¯±·Ò È´€ÊC€€ÿ(6Û6íXϰh-‹Ôª24ËI9Ý~Ç™î“N¯1¨lþV6s¸Ã­6¿—Ãý¹¡Ù±•Š¥ìÇYsåf2³ó¹n-J_™4P)Ðuš€›‘™Íþï|yšššŠbí¿ã ø|B¥â“ a «4îïܸŠ 2îÞ;ðŒ£Ú]ºÃ”[uZ6ýͤéÂåÏ«/ž9ñÇÆ Åöa‡øýµ%ѹÿìþª¥&{™4P ÐuZ~äÉÎÜŒ O˧hó?Ö.¤YKoú‚¼¯ë?z–%ºs%}·í‹G»”6  ÁNò\ð*ƒ©OÉ£(:¯ØÿÜ‘KqÂ7ŸèÝS»ÜiiT8¥pÓ®ÿz?ŒNßṴ̀¬ô"“F*†ß€:i5å×ES'äÌ’±wÓjõ®ó#nY»'\µëÉsMI˜ïÉÍ ™üòŽ`¹0dõ͘Jµn+fë¿Ø•òqɉ¿3ÑS2®¥ØnÄv*Ðu“‚½xá°/ýæT¯yDwæ¸íÛL•%+û7íÑŽ7xœx™®æÔמ÷)õ !­boå6 Ò'þwߪ¶õmóÜ£lBR߬ý£…ËþF, 'lóåsÑÌ.ç»6¯Ìi2i <ÐP?ç˳$ÑS…Qð·O ][O§â#©š¶¥½¯p.õ W€(Úÿ½•f)ŸÏP‡eÅÝ*| SÌ;ë*ÿ·–¢ææ>eJ¬6]f÷C5nº`kàЉŸØ„$¼xpüôé/×­Idt0Ë£²§ýɤ€²!@@I¼ì” É’T×'¡Ð˜ª6¬’Ù˜óøŸ-ÅÃÏ4Û¹?¶1Äç€üዾ >^ˆL÷°Uÿö"VÒ´B_¦÷C5ç1óðö…÷—vêç ó7¯¹F3v9KsÚŸL( ~ $íx¯âÆýÎl(e=/ùÉŸgÞsD‹T§Ác$âc\bLFžx4˜¡cdíæÖgŠ»VÁç ç“﹃žNÊßþt~i›ó ¸LÞ¼¿¹ŠÔ½ã§Þ<ñïSI|¶´ÜMOvê€ÚC×êÿw»Kî#ˆ ê_¯9ÿz©ØÛôÛ|ÀxûÆz²ÿR̰o½tYà蕉¢:. UáŠâ2i 4ÐòMtcÇ”KayD£m·áÓmuÙQ÷¼®Ü¸öáæã u‹ÆuÕø:DÄKóóyègÞ³SÿÉÖ&FÊ‚Ä>[<{ø ÏO® Î/›N×Éo:rŠ¢æÖëðKà •"üNœúÁówá?–Û†‘ËçšT颅å w2T]™˜%\424U­Úi2i $|–È3AZЩ٢ô¬ÒcÊâ?]$“6uojÂ[¶Ó+õɲcMœ§·üo¶eŠ¢ëèé«[~ÕÊB7ùÃxŸŒø¼zÛ Ñ®VбrêD;&r!­fìàÞL¯ÊoüôO¾– f7ê÷Ó(#Œ>È3Šªs³Ù7§ÅÇ=9÷ææÑ€;Ï„/ï ßù‡ëÌÚò“:ª³ Á@€c¼„Ëžþ¢Ë«öœâôíR„ªÖx¤+ËË7#?ø¦Wr‹Qºâ- ,×Ný;9%R4·Ó'>„$¾Næ Ñ–õ5™>gŽßË/ôYÙ£ÒsÁ@]Faèºýjè6­ËtÏk3†<"œ'k_DŽêd)õ¬ur @~ñRƒnˆ¯û§mkS´‘nÜH‡ø “q¬_lÞ(ÉœSTM·ž]‹µ @Wæi>á±ùY÷Núæìšç’Ñgݱ?õ²ÃG+€<ãå||Ã5uf1¾ÕAPhFƒ{ý>6xÚ±\—˜O ¡Á@€_ù ï£Ä ,M% ŸÏ+´‰¦¬$|us…;-O”kÖ8~vÈöcþ’‹uýù Eȵ¬È­}üû¿ÛM¯ð‹™¦ÝH…\¢¢¬,ùÿuêK>›_û}¨-ÐrK“–ž'^ú|qEû‹¥ïÄã~[æ&†ùŸ{à÷ñKTZv6‡'ª±®=÷ÊRgˆÇŠdI÷qÏïñ²VÛqÓí0ú P—ä=Ÿ~dÅ?1 ú0kK»ùʦîíÖ]n›½àЇ£Å³¾‰„/ÞÑa‰¢Aû6뼺٫ÝÚQïVxð³ç_z$¹°¨†­q÷E=~œïlÿu.yBSÿáøˆÏc¯óNg‡Eñ\úµÔšYé·Nðo{¦lNÈ/¼.Üo^S?Ñ‚®íŠ£ûWX”&“FÊ ·(JêêLá§!!é‰Y\RN€æÅ‡¼ˆ-èwmª]³3É òÞ\=|:Y¼¬ÒjÉ`õÒ>7ù™/–üíßaê¤>šø¨]L×S®î(cãö)×¶—u Zë=ZϨøh¦ö³ïÛÏ®b÷莛f<ØTŃeÚ@9 äC§‘yù‰¤°ÏÉ|K£2³(?3U2S‹Y³57âÆŠÛIâEå®c†vP+}Ø)/:àQd† cÓ ‡ äM«IW½óŸùrç\dû™eͳLa*IÎäÉŽÉæ“šôeG9rS<ÔM”GÏw.µ,[ˆýös6͈EG€9„ Çhƒ<œNxC’í^JÀnŸè|Bâ^\9Ùf‘£èvn„÷Ñ=á<…ÆCvsÑ¢º^o_TºùjÞ0ñ“Þø<çI–9~÷‚’šµÑE‚¡¥­ð‹Pÿ!@Ô} ª¦ÎšäC*!Šæ.Z%^µÜ¸k¾_„ÿë›HFA´ÚmZäÈdV®õjÞ0ñS¼}> 4l­²ÞâÞûû>)­†hS¿w·à{ß» P äÓfÞâ?Ú†$³lš6Ó(žÒø9_ž%‰÷Ral£Ðµõt*Ùv5¯2nâÝñ+ï·Y´bš‰ü½q“ÏGÇQÃ'Ü4Ó?‡ð3ÏôÔ—¿GU€·{¹@S3wom^ê&^vJ†d‰R•?Wóð*ä<½r-„£Ô¦öîR†¸‘Ï~¤Ú¬t6r1i©íï“LÈ'ß'Ÿ»ö·Á[*@C€w{y'àq¸ßïðªáÄùl÷Ï&D©ÖïY¸q7Ç3ìû·U§*©tì«ísT˜ “_Œê=ß’Qñá ï ê6·ëÛf\ûTrç®Y2JGRjÁùä{nÇã çŸ“rÄ·?_Úæ|Áa.“7ïo®R~Ë•>œ—òññ¯‡÷ßFÄæ „ošú–­]ÝÇõpµa´æ§ûݺ|>$âc\bLFžøü:†Ž‘µ›[Ÿ)î6Z_ OØñ~;/úÞ{ý)Vt+éÐê_‰×ëuûãtÛàyÛ¯¦KNÍS¸à¯ÅÄy”wj÷Ž=A‰Ù’&˜-÷¬ÿÅU‘œ ß—ÿ}»`üœî>sã&GFvlÐEï×^„„댹:¿¥Òý¯´ü/¯%)va¯)<šnد£áÑ‹ÂG“vÓ7üWK[å²伺¶{Þ•÷Ùm—ÎÓ[[¡ w u4@ÝF·é3ïfŸü ³Ëº“Zt•eâèÑ˾O²ÿÚ3i„è¶9ÏECœË¨Z–žX¹Ã¹A7vL¹–G4Úv>ÝV‡‘uÏëÊkn>Z·h\×o5Ù¼4?Ÿ‡Þyæ=;õŸlmb¤,Hüà³Å3ÀótÈ£èÙ§FÛ³Äa•ÊÔsmÝÝ”ùïÚÇÂûTé5rûƒdÂIˆz±ß÷Úþ—‰óïiKÙÿÊÊûði‚r³!6ÊâD3mÕÉáâ©BÒï¿jÓ^¹ôPÎO{¾éÊ{Ñ0ÎoóõŽÇ4ÂYšP: …R/OF¬¯ &hy  H/9`© cåÔ‰vLäB„iTÍØÁ½™^¥_Ô•9\tj¶(}ªô˜²øO–8l6uojÂ[¶Ó+õɲcMœ§·ÔùA)Š®£§¯nÉ*Xae¡›üa¼OFüƒ ^½í$“TÐÔulÆN¾*¾O%ËÆÍ:9‰B£•‘h) a6-öù« ª®"¹ÞKè–»ý¯K#[}Æ›þ\‘#}ÿ+#/Ü30S«…{Ó¯ùWA³ÙpÛ—¿ç“¼7ÿ†fµm¡Vz{4e–ðŠÿ’ ¬¦ˆ ; tH™rÊÄK¸ìé/̹İç'Ö·ÌGUk<Ò•å囑|Ó+¹Å(]ñ–k§þ ÇGEs;}â“AHâëdÎíš½°¡MïñË»êH¾gèyt>}IAºþWŠ ó£w¶fÿŽfÿ•oSÕÛwi¢øþu>á<¹ÿ&É¥^iíQÕ\–Mî³ÑëmŽA›é½ŒQ+ ¥@zÐP^jÐÑ‚¶­^‘pºq#â+LƱ~±y£tÅu¿TM·ž]‹µ @W†I>á±ùµš Í›IxIw¥èer¼ßdëtó0¦ZKQoÜÙ]éµW.á¸?¥ÍðÒÇ´ôœú¯wê_¥ Ò3€A€€²ä'¼/°4•(|>¯Ð&š²’¸—š–Ç'Êu³ Aæýçg†ž au±WÉNO*²E§›£²×óB"Ï>ÔÇ^F ¥Cz/Ðõ— 3êåý𬒳Ô)¨˜unfΪ 6 rÒÒóÄKŸ/®h±ôxÜocËÜÄ0ÿsü>~‰JËÎæðß9HÛÿ ñ“ƒ½Ÿq ßkS?¯2w è÷¹û [$h¨<¤g¹ƒ Pñ_z®ºšPÊí^öM+Ðܯ3D›õœ¶Â¥ÔÝ©Ê:â‰;¹¯/o™t=‚KÔÛõê?ÙÉÜXI§’ìЃ£ŽªæÃ¨*iú_üT_ï|½Î+†8¨—œiƒ—rëä©›„$ûyFõ]d…"g¨¤gy„ e Ðït–‘£¥n9A“s{…(=ÛÁ¿mê¡ÿmø5K!®þ.¤èeð’_ž'6C»örÖ-í“£vó¦hªÁ /ïO3¬—? 7€Ò3€œB€¨¿hVýV?ïWåÃ)JêêLBòIOÌâ’r(/>äE¤hA¿kSí:S¼PùþW7êùƒ÷ÄxŽ“Vo›t«–ÍõîÜM&è—÷²íÜTjñÒè žä4”…¡ÓȈ¼üDHRØçd¾¥Q™%üÌTÉÕ ™ZÌjŸOH‘äN¯ÚÑ¢Òý¯nüÍG±Äxˆ[ÙWT4nÙIýî™tBòƒN‡dvhYQ 4dHÏr ÊBÓjÒUïü§B¾Ü9Ù~¦EY9S˜J’’ßì˜l>Ѭ87Rˆ$%ó9ÅOá£0˜LaD呜èT¶€(Vg·Òý¯;Æïj"i4ØÙ°œqlºq¯f¬3¢y¯¹Ïî½JlÑQ¿è3ÁÏK‹ËašTëaüCzwÐP&šÁ §^çäc;L;ļèŒo¾€B¥𮥑IÈ%I—îë,¾æ  ?5Ì+ ®´hŠ’º²¸6:åÙûŽ™~áªE=SNøWnøÛ nÃ’$VA~Füç,)«©+Ûÿ ±ß=zGŒf6Ó.÷=“aÛº™–oаáOÞw’Ú.4û47éÁìÕÇŸä sv£i gýlŠÝP!=ÔÐr@ÀËãHf1æç±… …B €“–\0Y[nJ\L¶¦‰ ]ŠÊ §êºŽÛ¹u¦W7ãÅú¿^ì5´vÔg)Qx9Ùé ɉqŠ.îgM#ªÖ=h¿<•LRüvõ6²×¦ç¦ÇJ¡8¹š±HZá$D~‰·l¤_),«ÖͨïùäíÙUÃ^4¶Pd'giü2k¼› …®×æ'Çë+‚Ù$þîôù>šêJ„‘ž™MÑ4“Œ sÓÞǤ5µÐ4Æçq%±šÇå Ÿš¢Ä•í9øìÌ/‘/ŽÆs¸ÂtodíÜÏ£Õ-,R=—þäN¸6¯]¸ÕbÄ…™†G7îõŒ)xn>y.owIÅØºóºýÓ®\ÿË’ñêïÞ»‚ò nþ¹>PøŸY¯%§š¦.Ïï¢_s ¯Šó=ð³¯ð½±‹—Í4£«Xuê¡þÁ+]´‰“ž–- Ð Ò3@½ P§)6šÿǺùemµuvǨ*·­TÙÃiº¶îs…ÿÊ݉®å(«Ü% sÐPÏ!C€l!@€Ü«pÊadèï¥d~P @@=‡Ä²… ò “ @-C€€zѹî(\ÅŸ ÔÐ ÇJ?#¢@MC€€zÑj4È+È I~LPo @€C&€Ú‡ ò éYŽà‡õ 4€ ¤€ h) @€Ü+çJÎ2QO€Ã¯=Ô´:øk_w @@}°ú)Þ衦,mUG£ê޽Ͽw Þš<Ùõ{w¡NC€K4E|ˆ|xí4DnzFç¿Û45–ªbí¶ ø|B¥ÖÑA~€2 @4Dùá'Žžºžš+®€²XºqÙ`Mjíܹ /úæÕ³‡¼ú%ºs%ƒÎ;—k®X;wPmÐ ÓzÚÌ¥Ó;äÔÂQwRkõ®ó#öo\µë³Z÷aÓ癑ÞG7ÄgòKß7çÝ~õ’JÙB³ùaéñAæ…S7;úÊO«Ï‡pŠì§×~ÑÉöFT»‘‹ã+~ꇧýC^‡G„Å%&æòDÛ( -=Ó&]úuêÜÕL¥–¾‚À÷… —èL† Z¡Ò$õT“A¯…øÇýìuh×gž‚ÃØå?´Ó¢vF9^¡Oi¢{/eoF“¾G'¥Ä…ݱv”xÍañ²YƒÍÔ™%:˰zîààÜÔ·{×®Ú«7rÖ‚iM´ÂýšÉ¢¡ü˜'ç÷zge³Nm|?ÐP{¸1—îEÿ7°2RGwíN;–;3•Ê>„¢¨eØlê¤Aw—z~µräqü@sõ²öμw&–ètš4»¹‰E¦H0[ïÝ<§£ò·Ûî#è{m×Âùϲ^_Y7Wgóκ ? Ï  Öð³ÃýE LeÅ‚”I¡ëèUx ÓÜã¯>F\.GßÜw¨Ã_ÓÍJ‚Ï Û{ôašr«­ƒ‹_5¢`Ô̶XÜWÐî5|ÐgÇÞŽÿ¥¡íÇ6‘Å_ îB€€ZÃËNÊ/P¤žzƒÙ´ÿÔÑOVœH.Ù}ЫÏÒ¾VÅs ç£×þ£IôÖãÇvU/µýj6BÕ´j=ÌÚ^«dªÖ°sÕ$ïR I ~•Îk‚1èú @.ÑK«–…VpMCŠ‚°AZWï Ÿ+¹?*ƒ.íTÖjNs¦t÷úëv¢°Ï§W=êp¬«~á ÊKòýób$±³ÒݘYVÓÕjDѦ۴Ue´«Ã”,ä§qétš,~8Pg!@@xIïïî»zÛ+øc´h²;º–¡Mû6½&õíÐX©Ð,/õÁÕS§Þ||û%]23…¢®©}×.CfusÔ)§ì°»G6øú‡Åg‹o89ÍþdA®³þ9ÕZµý¡¨;þ¸¦ý“I2 á>=¾ÿšËZ_C¿ ýÞ‰£þ\ƒ‰ú4*/¾Ê¤‘œì‚‰;”´Kž™õ 4”BóêÒŸc΄æ­Ž}&̳×gd…ߺzúò…+¾¯œÞKëë¸-/åÁíÛ·ó¬ûv5ËÎÒD…ÿöæÿN=>}ô¥OÔŠËœ5Da[eÖ|ˆ‡ó€DŸåÿø¥¢×qÒÒ–’&t¬Ë9…°( ËmôÔž/ÖßÊ!$?`Õ‰çí§·Òµ/Èz÷ïJÿlÝ. §X1k£‘b8)aéâ–µ õõ4” H}¹o¢(=«õ³qcKMq$tíæbÎÿí¯«ÉÞó÷7o>¿£Þ·‘V*³ÍøÅ›Úi¬°±ÑK ~'-öÞñ뚌B+èÙ¸v'„õi#huÓfÝ\ «C4Z/ý©…ﮀ\BÒývoêì°¦‰*…qèàxå¶;‡6aU¢¸Z&–÷*T<­Ûº«®Sï!@È%º”Ä¥£(P¿.ÐißææE{þë#º¼ŠÉ¹­ ¿¨j·ß^ãªWZÞ« 7R;O0o êµï5¶gs=ý[êT´q2!wÒ‰{™Jkø_¢äwš¢À Uõ!»ÍXî;iQ›´³ûÏß4^÷Á¾ÝÑô¶S&÷Ð)³ø¹&ù*ï݃Ǣ©=fpT£cº¾C€€b¸IÏ/FŠtšIƒ S;â%LÆQ¾_ò&¨ˆÖ)ètí? X 4ñ ‰|ÂËç dß?½Á“ÇŸ½'€GHâÅùGÆB¹VãW»éIldÒˆ7áÞú;)ÂÝbÌìaÿoï>Àšºú0€Ÿ„$ìlPq‚ŠEÜ£¸ÀUꮵjµ¶Ÿ«îYê^8Pq#ˆœàDÔRQ¬âb ²—ìMÖ—‚æ2\¼¿ÇçáæÞÜsOLÞÎý_m¤ç&ª)M ‹-¨j)Ò¸\N¥M EAxàNVV—(~®´ÈÐôç¸ߟˆÄó؛ޱD–‹#µÓþ¨ÂI>ív0„K´{.vdˆÐM4TÅ/ÌÎ*-EŸžYQ+£§bl™“~ëäÍ»A±o²ò ˸<>¿†«a¶¸à—Ûsw& ohôž3Û¬SëÝ/÷ÎÑ«^”iØ.ðše¯òM4ÀW©aæ@“ò9Ét:³b4Ï劷š\·í;õšb!]IÇ@ŽÉ„íPÏßðŽäÍÞ£¦þÖÅÜXUE'O7:¸½Þ!œë\±YÞëzÌ–`šØ›)îL–ÅÓiÑ\…U§ÖêÓ?÷Þ±eSRô{/ó™ÓÏ¥Ÿ›h¨†Å’„A¦ªIGsý¤…²ø³¿ Ó3i7ië‘ï2d¾ºœhô·‹ûöšûïÓüS,Fn>>ÁZ cÏM 4TE—×Ð'¤˜ì´\ù@€æ$?¹+\0ÒE¯éŒÀòKã=w,\L8kÿNSªW%‡¯4T#§ÝÁˆÜ$$ýå« n[£ZOäæ½Í-(jÉ7•1X^~Øöõ‹wD7Ÿ¹jã’NˆRM^uhZø¥™…ùeåç7Ñ*Úò¬/ëcŸ_–U˜WZq©¦¨*ÿYÆ·ø<¡Ó1´öÅb²âCœ/C/¯Ë,œ¬,ùßÀÐï6TÿÀ¶dBÞœ??|…ym—å£)*‹7å%Ë0˜ïÆ e$S«Åõžß•¦¨Tš)S߇À-/b-9JZ£Ö;#péÒ§‹ºoÚ½jRs9üÿh¢  Iá—†ï¹äöwRäÓá9CÄhuêŒÁ:_Ôg`YÄ¿í>ɱáy…¢ÝæÀÿŽº¨|ºÏ/9sgï¦ÐûÏŠ„óWµF^½¤Js55L“‰í¸Þ/$)nkÖ®YòSKå*ÃÐ|Ÿ&ˆÞL]sS’XDR<ÿ~0m~wQd~IF˜ß½øoo4;#àçùë/åª÷ý±y~õØóïES6é9¬=F¦¿ixu I¡Éu\5æÀ*ö³™[§,üܽ©‘¬ÅÚñGÖ’ÒGWFv ÎøÔG/ _ï1ue’ÚÿÜ®GÂþ¹ðEBNM9¨ vµù‘+)5laYô:Ò¿uåËÒtîûö=âT¹ŸA§ý:]°®w#á#­•ÞÛŸ[qòöÖßäü:÷ÔÍšÎÔÜ:à•qÄ/0·„-XxwmnÝK=¢Ž?ÉÎ Z6?h³±¥•†"SŸ™šžœ$7ÒoçÔ¶L¢ÒaÜx»îi$ãæRëÇf–ÚÌ¢¬„ˆ Zç­UIF.)Kyܦƒ¾œ¨e~Yfú[q¼Âôø„ÞîµàÏ4Ñ*†Í®¦éj(¼ˆdµ§<\5©0ï¿ GgoÊÐÿyì¶•­Lt„³¬¢‘ª¸™A!»ç]¿& EÍë÷„|Ó /[¾Þ'*#_2_(zÅô>kU » _lt aÙ9šj©ûCº\ÞwÁÿúóÈØø°Ûñ‚¯6­Ì­ú8N3À¾­è‹ M¾Ã ×M {÷y<ŒÏËŽ ãÚXÚ=r̰fFß{Ìç>:2ÇÆ£½ë‘½?dì¸Ü7ªLrüÄ‹‹í/ ^×f]†môšÔF^úž—¾Z½`ÞøÒÊëü–öñ.hv^~c•ƒîGSyƒ4M4HpâŽ?Œü440QÀ1T}gÚ)Ôve šœv³n+FMós;üJ¸{è¶°¤)zµ5_’êw0ƒèÚü±¡­™­Aã—ÆDžXxÙÝ/›Ïü¢¦å4†l½®¨jçºû²ëGî$kÐiôzÁ¿ÞIF¯ßëûýQ}õ ?ÿAUVè/»qå~¾aµÎýîºÏшŠõ¦×õ<0| @ŒWy+[¸  /'If4–®¦þ‡÷RЛèÑ=à»{Â+¹½¹¿~³Õñ5º5%î²—.úgÊõñéoS=ø6H#¼·çÎ͘•¬?ÜfͽŽ]ùcz„fü1Ô4ˆqŠSÅ‘“F£4~«`Û{åÜg.{„sCc×ùž;cB›êC£e/ƒ7nÏaõ¶`˜R¯w#4•ž'++‹¦”üÓF àsA€1>·”]§i²׎æsÔ/UÐHÒ¾YûÞèRåÒœœ+snG½¹{­ôjûÜ©o#4V3eÔ €O@‚—rèäO3¢r%7YÍ'>|Ä\¥|(““Ñõžßù˜ð¸Rž`s3­v:ŒZÜ}€¥¬p4´(i·Ý¡a•kD0´ôß}ÑÞT–┃'ŽßÍ/?JÑæ¯©;ÿ§]çÀ÷‘Δ?¢·wìþ<#áMA‘¨krz::O]cÛIçÝK_>ܹäqp`b’ø’/F±Ê'zös#¨½ÊÇúCS7›ãÞ1Ðñ©àÙcßñßìÕzëøŠÒ{üìK»¹†‹†9·ýЧNƒ4Ò”°˜x&>üß ëMŸpZÅ{ìèW:‹'ïYoªþî’_ð haßOŠH3û…›L äŠ#ÏÝ;x*ðÉéÿ‚Ϻ¬øAEFÁ`ÞÓã|Ï×íìºTÿc…¤€Æ† ÁÏ¿wsþ¸— &íÝ`ªViþ-ïmäæ¡ÂÀª:fìñ“í4E›ì‡™·äîüíL|íì'8èÑ\s`÷ïµÃ}Ó {˜ÚÕ¨y•™ºìØðkQÄt£u˺§gé;#F“ïã¸ý¤¥†d…Q{ýœ'ö!™ özõ²™-y” =£îà )M _Jˆ @«ëtÖÖˆjñd¥Þ;‡ÚûûÜ/$$ûÙŸËl:¹·P¡±£·]ðI’ë{®Ÿõûç6R# @ˆ_òÃç;Õ|î`#5И „ÃÖôõ5ÚcOÍjµ8yÿœ]ãC×ÄÚ°Ê6–©±>y˜EÈ›€ô¢_5”…ë¦S»¶ÚäEÈëƒOßÌïײbGqê…9Ì~z×#Rë ]󱂯j-0ÂKð§„ǯ{?jC×›äô‹Ç®-ÿðÉñœr•ÕüYGž[ÇZÏl¬Fš&«^u  ÎðfM/#rk_Ÿ›ôÎÝ:h¿?8\œ-ZPWU¤ñ¸•ÏdÊ) îÏ&œŒüB.Q¥†©Å¸^×ÖÜá‘×}ŸöüÝVÒbQè£kI,ûý­4ê3ˆJ±3ŸSsø¡—Ú„ âydè±Hb´ØÉ¹ Å›i ±àíš6~nâáq§ÎÅŸòî½sªjµäÉ+ÈÏ,-½º>ZázÍ­p8ï†se”»/l­pçUÉ pKüÙÖT4á¹äù¾ÙZíG÷’¯Ï^ªa'Ç]ÝzÓ?>*:/7_¸ßÃÎÕ°Úv]±òñø5ÂZ-Ñ—ûL4hhÒ²üF}šªg"›ô¦”2ÿâ ‡qU‡ˆÙlIqdó[޶«qüXFY½Ù»·SšjŸný›½ºArÏ?øo»‰½:Ÿçs¡D{F×Êõë/…Îð ‚oýÜ+0œM´öœµ¦u[3Eä¿^iå÷¬~½ø¦Ao=¥5‚E}=#¥º}ehF44i…OâÚ® u¶¹uÒyzLYAÄ_ »œi§QyšÅ”LÂ`)šu14”æmSÙðÇ©j\sH~Ä™k…v£so…üS 5yz—§¦BúΔ¦Ÿš$LϤ£Ã>¿îÍ+*Nç*ɉæ@Ã×ÉÄ9•Ÿ44ij®Œbʤýä4÷Юmÿòò}üvú›¬¢P‘LèŠÊš „’™Ã&RhÂ0›Öµ¥k@4á<Üñv„yèÞ×eÖƒÌë;1YúÎp¢£„ Æ?´ÖÇú að¸<š ½Î ¨çî_hhÒ”[š²„ŸçLÍ‘î}/X܈&EW§_òr˜­zùǼœzGSrç!)q/RyL¥ôc˜YŽíq}Ý]ïþƒk÷¹·‚ˆõ‘ PDBêÎpsróD Š:²(ÕõÂ/Šò9²uëÅÀ9Â?](6sþØÚnR—@¬çî_"hV‡n+—>ùiÓ[’º~U'ÏÍÅš©j7Js׋LBROïKqúË@ªO~å Íå”CӯɷÚ<¤!jKÝš¼œø¬»ÜØ"QÁ4Ô‚—qeË”Éç^kõYua혖ïÿ¦¿økŽóú—êÃÙý§9yþ÷ª¥·ãr¹R·_ÏÝ›$^æ'§ßï‹ìÈ9üyçluŒÚq $˜­— ÿñÔ!ïx’ºûü‘ sæÙŠÃÃtf¿Û¼ï”Í'W5Ÿ¸|–¾J•A]>O{ïïÒ4µ~Ýúk…_|KŠbJT&ØuÖjÏ@i;Ã204S$ …$ùÈ?ÿþ2¼§®0½ó‹sŸžyñ ¿¬¨ÍIº¶î\d1! ·w»Å8ï´¬ö•ŒyzÙú—†íÊ“†4£“^¦ÙçùÓ‡–êKY=woš¸©<ƒ$%*KnúÜÏ0R³Ý¿4ÐÐñJËÄåÜxl¶`Ami*Æ3öYú Ë#Y'&õ{د¨ø]¿Ã ßäyƒîE° çî \¥mÕESS‰ÆÎ/ÊLÊIŒ—ýñÞ¬⦅*9OV½¸%W°4p®‘"Å.rK¹â.rK8‚ÄûnN¶”Qm>qºÚ9$í¿ß £Zب1 óã_äÒí:´Q'Ù¤4192ÁÐÒˆUÞ2¿,#ç­¸F^AN\l‘NKV>µùÜ¢´Ü˜Weâ›yÙÑá¹J-TT(•ï«G#¼Ò’ì伤çù¥¢›E±©Q,=}%5eŒâÕ€¡ÖÞN‡ ­´i‡ýâ·ý³¶¥—V^÷&øw‹`áB3óÕÿlðÑH^çF81›Ž¸¬LÈãTY›qØ{òañ"£™ãˆc[jaJxж[ñ¤$öìª'ÿ¾/¼ï:ÂÌU´Þà§+FÜþÑeÇÝ ñCÕœzê_7+Q-‹Þ3ÒÚ 8ÉÌ-¥a·÷öR $ÿö„!}2Åkå>9k/ŸsËãô©Ó7îDn‰<>J›.uÿ¥Véu2QqвZ‚½eÍ':·Ú²#Š4/¯'k,íj½Ž)//xÿ4ç½Á¦Îûlk€p×øð4 n’oXÊw;ëŠ?7ùÙWo~¬þÃa3œ¼_$Y‹YçÞÌ* qíÝóxrÕM uó>“¦÷—tþ×+‚©ç´akQf`èX*}¬eévç燜4pWHÑ0iͺ.Ær¹/|lóÜÿàŒoàéÓ[Fꔇvêõ3žŠ:޳h…u;S^RèñÅÛ.ÚxÇ?Ê3deñEíe”L{9º´P\õ‹o*!êÃooÀ$„®lbªÚÂòzàÜäóÃÌV<©ÒSV˹ûîÏågŸj5.¤R}å>'ƒÞl»¿¬‹Ëé4R’ôêê¯oÆIêïéSì¿´Šžøø%«|?ÓFUô¦Ál1x’ÍŽå¡„d^÷¸¿ø»µ¼—pÓ/üooð[ÁRìÙÿ7lMœ¥Ùè 'þ°ÿÞ㣠5„_ö`å„g´ñÆZâ}¾h y¹÷GºVý)z¾‚°ºy/Ç~&RŸÇ&Íî¼Ì ?FÓ§ú;ü=ˆO`ìçØ³-wÈïTŸi+{ws®[qZ#]¡×Ê£ƒ›IÆu­¬ô“ô>“‘xöO;Q‘ f³Îƒûqb“¶"ÐÊm¾sp2¯9tí­¶ÃO¶å_1ºƒ~¶]=-„C oºg~ï Uã°6¥ª&x ¢¢5ÊêŠ8MöSÀ;@ƒ)‹üÝãçLtJ2\NÎ1·Û»Ó\ Ó7 ±0™ ñÛŧKZ¡ÑL:³J. WÌ[–aЩîC»s^Ûq^8E¢åœUßëW–®ÓgÞfÞžÅ÷ÜΦ˜o,Ú@×ë3a©S/]Æ»6T[Ûµ g2„5cÒø³šW„s½ü„EÑéÚù´ŠM2Õ61hå[h‚~–o¡3$YÔbö¾C“Ä]!† ¦ïÝ!K§'Rè¿Tø9a'/æêOk­\Ñ7¦Á Ÿúȇ\/&¥7O¦ kPS:¦ë8ºïŠøõнB3çõ³-噸£WãC€ht%3My~Ú“sO‰¼jÇiC·ýÕ¥ƒ>ÈjÂI¹å)\еíVuÊ®¬I'câ)HÆQ×£ ç« ×Éè™áR­†¬0Mó»„ûI/ $ßaù?‡'ÁBÿ¥ÁÏ :}=ÏhÖ$s¹JkišvÓ•¯{çî£#¾ÉÎsŒj nLƒ¾K½ûÖéñ@Ý @4 ¦Åιwv~î^|Šãî¿-¨é©Ð¸œÊÕ»™**LBØ„‘VÀ%ª_æ„„ï?/3hÿÝb“Ÿzªä¦¥TÙbüCUoaAè°ý—bgÌþ K[“ €"^Ϋ«¾aYœ÷6ȨZÐQã#±‘Wž&¾ìeøÖÔýæ°%Wã$¤,9Ôç ÷Å€ÿ^¼NËÎ/eóx|~Í;}TûÿQÜÔ»·Ù„{xDËõÞ)ÂÛëÕÔå)T•‡Fƒ ðUbÕpâuÜò™Á4“A«Z8™V1çXF†Våp¥)·×¹ìˆ­¡=Ãl4YÞË)´m5ûäíšfWË(2ýáçý»mxßÝOÙDÇaö²Uý­Ì4•Xt’o–Ͳ‡Â£0ªt›&90eª>~¥MÕ)ƒ&é]ô$¼{ø’>3êÑipR.ŸæšÌ8¼¼æû/*'ÑëEžo Iò:µx5 B àÓ¢±ä%çɱ4Ût¶2ý@)Ø;E˜ž‰åÒ çg¶¬˜À«-/šýYPè¿48I—>%+fŽí_ã%»KõBvxN&$ÝûÄÃÖ=Uêw8hÐ@‘l»Ø ê¼;]Q[G"B²â³Øä”x÷r´p¡Å¨~Æ_Ìô_éû/²×—=%í6÷3¬¥P l['GƒÃ’ÉpÊé1T §'nÐð‰Év5!W^’ò04•cS{忤䈔uê_àXR¬ŽÏåÕwµÔý—BY´§W$i³Ö±¶üLˆ|ëQô¸¥ 2û=Ao;5C}ÌÏ à«Äb4DŠâÒ$¹T8š^uÎ.½|0MF¸‰rèÚw—5ý~”骗±„¼Úw0r²«Um×Îc*¨‹§üf¿)$•»G¯¨v\µÛtÉ\e.›&SíáÈ«) nsHΛ·¥4VåËú1Ê;J£ úY>Ð]qˆzôÿãJ"¼OÄ‘+†š(Ôþ ³,Ç Òq;–&ˆÛ·Ž]K1É¨ê· nA³¨ óöÆŠˆÖŸ4|j¬Ö.‹mŸ}µ€Äo™äÒüÄž™–jU2!Ë^EÖÀª"‰)$qoÎÛ2XW\øÅÉÁ^£jš-£¤­.šx;8±Ì¢eåYò¦ö-ÉÍp ܼí†ÝÚÚâ_^qzÔ«,nãôÿ£ŠŸxž'í\ðBòG Ö9æ!HÐüЃ> ã~­taGvü1Çþ¿\/ D®ëº ¾K;(`†G£C€hºøœ¢qA NQ1§J.à—d$Jе$FÆfë·T—£0¼ù‘Ýeô÷ž6|ˆ[;ãâ¼W›Ûu6ÓV¢³ó³Ó’cã]‚ü—Z°ˆj×_¦]Ú•@ҼǶ³1d¦E¿H¢Û9uT'ÉÙ¤4)ìY‚uW#qÛ4u›ÑÝ÷‚8äñênWz¶V,NËÔ[îéî¨Nc™ŒYÜ{ó”Àbã6Ðê–¾®"¿8;##¦o.A.I ‹HùÎJO^ÔÍgtnY)·zd’¶ÿÀ+Έ~vÑõ|*Qïªþ66EÛLO±Æ\Æ/ËKz]ÔÜ”AÒ„¯Õó]ûüì\zt0ÑQeöü0/azöÿÁº7gžr¬¡”4,h€¦¨èßù£&}šR ¹±¼¯Î eCû)'ý—tâ…,ì:úð«Rɶ8Ñm=‘×ï6ýtÀÒN¯P(ÍîtÍ~ËoÆô;±åçåŸFGD"§ÓÖªÛ÷SN3Hœ>iJ]]Ï{*.Yév3*7ýeh™™]ï™ÇÏmøý¥»A\­iüãé‡Vƒ4élª¬¾™†Bƒ‡B€øªÑÌGZ˽ÙãGkT‰þ ¾j¼Ü·®àX®Ø2Xe8>h€¯;ùšû†µ;Ž>1YxéôB ¹ï à«ÄÂg8ëH{dÙ¯¿û÷ÛføødðTÀ·`…-Nš&GE¬‰û6ÿü¹ûФ @ÀWϯïey¾:øµøŒ (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (øÖ4FûÜ]€oÙ· 4Ð @PðÕh>Ÿÿ¹»MÈW >%h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (ø?/-7çÓ3zˆIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/netconf-conceptual-layers.png0000664000175000017500000016324514770023131026216 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+æWIDATxœìÝXïð[ÐÝ‚¤„Š(ˆŠ‰ˆÝI(`b‹ ˆbwcwa v ((ŠtJ÷ØþƒÑŒm‡ìøû~å¶»w÷ÞÝØ÷Þ½÷Åb z}¯@C‚ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @BÃÐïß¿?}ú4û—èèhUUU‘ú^# Á`$$$hhh°9r¤¹¹y}¯ ;@ûùùmÚ´‰ý …Ba±Xõ½:@ZYcÿ‚ýÿƒ Ð@5¸ ×°ô¯_¿Ø[¼¾×jž9‰.66¶¾×E ;@kjjrÎWÖY ”) rjjjõ»&jØšF£Õ÷*@Ý R©õ½ iغ::®§§Wßk|„‡‡Ô÷ZÔÆ¿ ÕÕÕCCCë{-€===v†®ïµ¨-@4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4Àȸ3LÍúrNñïrcF´’®ç5aC€àƒ‘ö+èÝǰð¨„ÔÌ쪤¼¢’’І~+‹¶MUÅ©µ.—•›òö »àˆ¸ßÙ1)9åÆº†ÆfmMõäðÎäAX{@0ø˜†bß6›¹W|HsþÓ/>ø7¤ä¾wmÖzó/΄ÄÐÛñ—úÈR¾àÚ‰}6±5?ÄÝ´ÙÒ¯µ+ƒ›v£§hÔ”· ^Ÿß·çˆï…;_Rk*®jÒsØøi3& 6Wôe¿ß_Øå³ïè™ß³k˜EÙtðØ)sLë£/Iá[Þ_ºï”&?þu¨³ÙÅxÚø›T~Ç­ ZÑ3ÀßjµÓé°ýãùú"õ½"ÿwÌŒÏgVÏsôzÏoNF§Û{]Ù?Òf7ølšÕE÷Öʾë9wÆò+á,Þå&}¸êcÕg™ùŒm6Oj-G¶Qõ_ÛwÂÛ#P~Ÿïª4â1ç÷FsÃv¶—¬ß jÄ|±Êíî¸ýù7‚þ;˜éïvO2ï|$¹Å2ß]Ðõø^Ç'[:ÖÐðˈ»½bè°/rH”úvßTók×wÞ::ÛT†Ô^ø‡öðö@-!@¿}ú¸öXÕJ¼ŽË7³µ³P´YU¼™qqGšB» óf„ç×½7¾eT|PL¿ç¸q¶ý{tlÝ\_KEZ„•Ÿ‘òêÞõ+W®Þ|Ç,]üó«È&e2tÖ »¡½-Í IÓ‚•ŸõõÿÍsGöì¿ÿ«´P"öÒÜöq¿ýo-k/G& ×÷¾£)[iüyã¯ðö@í!@O_79ŸŸ~Ó®1­NKÕ¶Y³“|¯>šj¯%Û{ñš#íJè‘{J&d:-òÙÛƒDãcÎ'ï¡}+e5µ>Ë÷lwl$]1/RDeT´Ø?F¦MsÛ|Éséâ W¾3ªXŠ•ñruÛŠéY¬õœC'61®”ì(¢òZ­zÚ±­}¶cöÇ %Ïä=_a=^óý…Iºd"éß´ïjGh{à @–º1OËͽãºáù«_[öoae¾Xe³èI^ÙâVß½¶¬³"ŸøI•1¾úòÐO½ì†ºúq-9-`‰íºå½žåìzzv–1Ëé*ξ5[֫ׯ÷…œ‡Ò®M·ÛòáC~ÑõŸÙwÂÛ#ª£Íðîÿ}ØÎ[1»:>u0ø§/ÆÊû¼eòæoåÓÍœïÝ\a)pŸ š’¥Ëõ`‹GEEª.’ó~Ó´ú¸sšgz.AU²Zwëܯ–6§’80ž¹Î9mã7A“g€ügöðöÀŸB€.¨ò]ÜÜ»›ò°¸¿1ëÕêå·í|*ÿ³ì2®-Ùô¥|Údõ™5‚gµ4+ûEÕJŽ»¼Ôû{ùt‹5¾KÛ Ú%—ÖhˆÏ‘±·úþæLçÞ[æõÆÖÛ‚çÿƾÞ©‚•üòù›ÏaQ ©Ù º¤¼ª¦~‹¶-š«‰ÿaðÎO ~æÿü÷èä¬BUf­;uµ0¯ÅŸ\V~ò÷w/ß~þþ+öwFSTZV^EÛ¨E«Ö­TÅ]Kfvlð«—ï¾U4«€*.«¢eТM‡ö-Ô%j]ÑÂôŸ¯?}÷5"!-—PÒ4lÕ±›esÑwÊR˜úéSð×ï )i¹,I9ÕÆzMMÛ¶3Ñ’®Û.Põæ/¯£0ŽO¢è½“øåùÓן¾G&¦å°$”tÛ²ëYõâ fûµ_}øò#*á7{ËÐÅ%%¥åÕ4ut š·2ÑSh€Íðÿ \ѵF{-\oîV<•zÆiëâ^ëÌêú‚´¿#âüæëåC2+OÜjß²nªÊøuÎË/·lRqÜ–ù&dJ¦*[¯]ÕÎwÁ«’é¨ÃÞWúZËóüHùöðöHfFð¥-kÜwžy“ÈíiÕvcæ-qsÚŒ÷Ùãç¶6M>r&š®ûòqY3QVvèÅõ‹–x]û–WuvÓ1n[¼ì{¨ ö™\xr‹‡Ï¡‹ï¸®#AÈZ 7uÖ4ÛkÌûùqOnÞ¸õàõàtnO+·=Ù*§¡Mk¬(·:æEÝñvqu?õ¾Z™b†nð\:¼©Tµòrß975÷Œ¨ü`Ü®R»¸¼ªÞÒ_Ö·+™J¹ÐMÑ6€ó»ÖÂ7¡žæ<‡Ü·‹ŒÚx•|ïÓõüoÿá \æ*Hþ|÷ìÉÓ¯ß~”PȽ$ ƒÞS—/›ÖU½aÄ\uÌ}ëÒ¬GÉ`ñÊ“þ8l%H§°ü¯Ìš/+9 –z%üâ`%n‡ØŸ÷C´0ùÅA·%k÷<ŒbV˜“ÚSߦç`¹’©Â”·¾×lÞ{åSZeKhµë;lÌt§9ýtÄjœ þ› ¡¦N[FìrŽó—å›§ó™Y~ywh Ñ7½*ŸÔ›îØ™l[gM%GÝ8ü¦|R{ŠSWÞÙ·:ºîhgkç‘·K²XÚC/3¬ûÈò^¨¡ï;áíŽÂ¤÷±ÃWÜMªy–„W§ÜlNí°v¿èëb)ð€1‘÷ãÔ¬>c„q6ãÃ)çž7ïz\´oU=`VÄL{³gö˜ù§¾1yÍ•þíѱUN¿TJ¼ÑË1ÁÊø°öÈ9'CkHNE’Þœ^isz×Ë'œ:È RÑÂÄû‹û÷ßôšûp8yß®oqýôøc·÷7ø‹ÏÚXIçú6ù€Ç–)–óýîιw÷îžé{mÛÝ– ê¦Žâ-gØ›y8½/žH:ëuwsW¾JöûݻʾBR»°—ô,œã“™úb“­õâûÕc1‹Yz1 3åÙFÛËÔxO¦9‘¯.ù¼Š1c=±Qû„ jBUî·Þ­Í¹…œ˜woɺ§Ãvw•mp_ÏòÃúýìì»òImÛMëès’™üì܇òIM›QÍÉ' ª²ÕKúí%CJdø_ ÎîÓÏ'ö¾Þ)R˜ps¾å€Ýß+?*®¤©©"ž—™\¡Ý8áö’Îbü{[«rúÁˆ>7ÅÎÎ7®ì¢›Œ‹üþU1§ÝqìkßüÓ>ës9#öÊ|«¡{B«ŸöŽì˜}ñÝáaÎi(QWui2Æ¥÷’±w‹gξéy9jÈtmÞÛ•úxë±èÒ)ýéóÚUIHÇ'[^È®ác?¨4à>ûh*Nά’ü\¶wxÏe+½‰h Ú†ÚjòR4FVzZr̨T~§ð‡ 51˜î=Ë³ëžØâ©Ø½Žû.4ü×z„å|{\þG\¦CŸ:k7Ëý^±déýŒ$jQ E¡ÍÀæÄƒ ’ÉøÀ× ŒºüÞ¹ yß oDþ·Ý#l*¦gåîÎÞ o¯ÉéjÉÊŽ|~Î{±£÷ã”’B¶müÉoV¾/âè„ ?‹Ò³ˆ‘ͲUNãth"[”3Xù A7÷®XàvµôbÒØƒs=ç}ÚÀµ_ +óå*ëJéYÚ|Âòe &ôk­.Q– 3£??»séìÉcÇîGs)%'Èkð€ éDµÛœå®sF÷0V)í5ÍÌŠx~vÛÊÅ[î'p¦?n4Í<øÌu^Ùèû¾©+“ŠÒ3­é8Ï«§öЗá¬3ûדck8üPÊ’ÎOÙ¾ÿ¢fe'?â­7þ`m$þ¦;Òµ»Ž9tЀÞÍ›6–­x½)#-ìÙåCž«Ý¯ýäd®¸ãgêx~DÊÐD]Ô‘ªÖße¤âÝãÅ×b0Ÿ{¸ŒçH–Ìø›žÊšvÛ:Li^õ XXÇ'[øá© £ŠÓ³šÕ¼eN“‡Z™jÉЊÞ2Qîßü\4Ž+ÙÏŵ<=+õpÞ²n¾m{-ÉJ霑ñá±ßµ‹gŽóo mðÿ… zòx·&e±ÿ•ì¾%ðºCËŠcÉP%uºÎ:ð¢W‡í¦_ãd§ÂËæœygªößø1Ã~¸?kÛ©‰÷c™.§ßuâú.#'g9õRñÝ™2./Ý´ímé„‚­s_ÕÊï1aŸlú oœ©”ž+)ˆÿZÚÁC¬—ãHné¹"ª”–‘pá?øoiï5zÇÀÓœo´Ã¶,:5çέçÀ)ÌJ.îV‘©³ªfý®X²ªlmK¦Ë¨Uh©de¥ulä‹ä†¹ï„¶G²^ï9U6e¾ÊÓV£æ­H׿mÙãåÁœÉˆã{ß®ÞgÉçcTѶÉú5ž7P•»Ïê'sù§í+&ðãïB‹ÊQ ïË!ïe·Üi<ïðJÒc÷±ãÁ_»¥§ Zö»æ×˜N8hêC×Úë_YW|á#ãÉ¡‡‰ãíx\.Eí´nMo¥Ÿ§ÈvZêÙÿÈ›œ„òëøþw«ww¬¯.u€¢Ðeæ0•ãŠOê"o?Šv7×o]¡'@EôF;[»Žá\ÍœzÑëV|ï1Ü’Ì—Û÷ÿ(КäPõòñÉ&k뽤#+>˜ù9¥­ß]\¼¡õÉ¿ÆßþY õªÔgÝ*‹³ö/‹û«å?\ºæñð}Ý…×$üÆ*È-(Ÿ¢‹Ñë¬buV2….VñÓ¬ 'ŸU㼕5È}'¬=’r¥|Ü Z7ÇÑ|z5‹Mpè°|F g*áÎÕÐ\ËÖ<›N¶ã}™¨´qCâ\Ië\ü×ø¢R€Îÿqõ\ùø¦ ,jjCãyóDYý‰“[ñ¯bƒúk¬Û^Ô…(üpëK¶]£j×|•é¾`¨&ÏÏ ªZŸen–œ'Äß¹þ=¯c«Óí Qõ‚ó­ÈÏg?sþ¹-H©jýG)Ý>VÜLw×ë\øˆùMª¬äû[N%”N™ÌiZõKáŸl CæXq1¯”ˆr‚à¼Ó²îî¾Óc$Si€š @"úS¼çl¶ÜÁé¾Àiï¢.Mÿ`\T£5ÁA+x^…òÿC¯ðQÁÈcNÿ%³y%!"AâN pß i0|R~GH“á–|ûƒÓufL–´Aÿz”ÊlÍ«éK¾­¥&ï C“ÓRe׳7³’2+_åÏJ}§üÚÁf£úkÕ&¨¥¼\ú»Œ¹•@C¯‰j´Ô ˆâ€B¤…„¦v¯ñÖ-†¶ç{õ‚lëa­©ç8Wˆý|ü9ÕJõï=icüþ|÷Â…÷Ÿ¼úð¥è^59¼Æ^ÈŠŽÉb2 ­?lÔQÖÒqšî±MáÅo¶þ2£zðÂè+ž×²J&h]œÆU» ªOvÒ7Ø’W¾.ú*¨Ó0"ðg}.j;ðÝ&·¹¶4ÿèž-ð߃ ‚n¿ds߃ãü8_˾_·øú¤ 6ª íS„+š”R…&ÌÄŒ:ºˆ&¥X±ä„ FÍóòT˜ŸQaRJA‚Ì–opûNH{$?îSLÙ„|ËV*üÿø‰¨›KÁ%óÅ|ŠÍ'ñhaVj¢Ì/ñRÅeÅK4#¿°ò©A^̇ˆ²GdLÛ7®ÍiJ^Ì»e§[çz‘»0-6Qc!9ce.`54× 8ÝeXÑŸãò Õ¿± š™öþèÒ¹.»žñ¼ªœ´ö‰Á_üö©¢.ë(n2ñÍ&{Îà˜aûw¼rÝÛ¹rTÍÿ~rË“Ò÷«ô`çÁÕz+ ùødSk¡Ã{Œõ¢1Ц¸Ûy :Q²Mbý6N`ÿ(4ïaÝ»{·Î–––í[hH6œ} õBÓî±xßJÎÐø—y¾í»¹mîÙXFDÙ@… Jûì1â~$3¹:y_T)9ö»äÚÜÉ™ÈOM(ŸRÔkDî.Ó mß i0ÒbÊou¦¨«(Hë®{>v†çL¤Å¤ñ>¤· ¥ÂU[Ö©‘)e*ú|Ó8WŒßáɵY®\ANAÍmþŠ:‚ÜÙX„=A”ô7çl·¿.@çÿòÚu܉þsVRXÀ¬³ï¨„®®ëH×ålí:úvñÈ>ñ¾[n°T±³DÎǽ;K¯$ÔÆ:u¯Þ“BÈÇ'QtÕÿ;“S•ì¾ëÜÛñV…óŠ”/N³|ŠgPmÕ{à°QvFv«i hX‹ùÞv>}OpþúýܺðäÜûÓ«OÔQå Zª/JÇ}þ3Ǿ‰ ·ªå_²¬A eâEé_èðçaÙ„>Ÿ{rÃHøð©Â]nµÛj‘ # kß i° ʯ"D%D9 ¡ˆJ–·äòþàþc¬üŠk(E¢£N̼Œjw¯C"m8ŠˆD…íÆ'ðÔ‹¬×«ûUL–"z=ÆŽØ£Skc}m U%9)qQ‘Ò{¹0Âw´7tüXOkZ{B¨#U­ŸË(¥ÛG‹ÿ’d^ó¼ÓRY#3+íÉÖ#e½¤ fp¹{ŠÐÏ¢u¤ ÒŸ™"mæpí›Õ)÷ë¶_É©ú43áãíCìŸÕ³ŒlVúl[dͧsü'ý­Ÿ¡ð÷¡(ô\³¦Ó©¹ÏŠ¿¡+X¶ÚÄÁždoMý÷‘0ìaL=à_2¤zàíï¹=y_+F¢äæÔCKJÎ ¼šÓ§-éaäX)¯¯)ŸTkßF•ôÛ¶aí;áìv¨+o;Í,Ô±ò³ËoX-".Pè®=ŠhÅ5ÌøRÑÊ…ÐÄÊæC>¸Õå%o‚¥aöÉJ…í&ØÉÊÿã× ûMeo*ÙÞžwÏ8X(Ô”»X£Þ’NHu”íä0]ïèÆŸÅ/°Å7lœsÉÆLðóºPöJ;‡ÉÕîžRDÈÇ')4y3»M×ìÖ%ûß¼yç¡€À‹é•çɽ¸¬ïí»›Ÿ\_dƯgü× @ƒàèz·,ØÔÁ›Ó¦‘xØq·óë%Íëy¥þE±ã3¿tàÒ_çχ®m]'£P•:Ù¶"—ŽšuñÌW÷¶d“ +Éÿð“ò®2]mŒkÓû¢!í;á캜†,A¤q&~‡ÿ¤[A{¾² 9ºéÚS#º¼–BÙ&…%Ô¦ã]¾±\Ù á±A1yD”ß¿RX­ölerêu°Ý*FßÏ*ÈåÕÛ†yeϳҼ(?ò„¯£¯Ûd0R£Rk~ö/%´:Š›Lwl»qÁë≠û>ÎãŒÎøuÞëvéÍýÄû.´åþ—ÏÚQ2î5žý³ˆ(êèþîÉýÛ7®œ;uícÙÉzä2dYçÏ[;ò¾8þk  ©v.÷ºÎ¹Ì:hƒë•)—Õó:ý1ºfÿ)m‰·¯K&îßúÌõ`wò·p)YkÀ¤ÖöïJ&#m Xv¬©†_ÆÏÓ›ýÊ¿ó”ë?Å¢½@Š4 }'”="ÚÈDƒ J¾aN ú˜Èè&Åçï_AìÛÒ+Ù4LÔ…û5®Xc3*ÎiyOÿð":¿ùñRD5LµË«ùêId¾U]Ž™’ö9(‰a%Í{ñ2¾½-»`“ÒØ¤Ñ¯•.ÂNœ-“›žËä3{~R…Ÿê²¾Ü)kš•ë7ÓJ™g'WfÚ××1¼fø+ ¯ŽE¡û¹ŒºU–#û>ÿ]N·Ý€©ìŸåÛâ{Oìz§$EGìsàve°"¡¡4Bm4dÓR“ëË8#e^sÞüº‡]=¯Ó£ëغ py£äö‰‡v,|±´Etã ëŽ\ØÛÕ®ô¶¿O,ÜîòjEKKf&ßq[ýª|ºñDÇ®µîxÑ€ö0öU±Ug-âEÉG÷§‹O“æêò¾Ca|ÀåòÎ3:]ZÉ ÷j"ŠœY#Âÿ+gêë™[‘‹š’r˜ªØ¦!ñôg*Ô÷Ò—æÍê0¡_ ü=W—÷(.éï.½+˸º]ZTx¼Â¥”,&¿4\„&­,Eœ±hR#’Øï'^_Ãä†= âÑѶ0#*®ìi5cuÞí鬔À3oÿ¾NÜ|³ŽTվΣ•o)¾ºã÷Y¯ÛÝGH½Þ¹ï{é ÅwO©ia!ŸuFD­‹³ï±WMŸç4—缺ý-wpû?½“+üK $±æs¼'mí}„s×/§c-êy•þUmІEÍn¬)‰.ÄÇe£Öôx¶¾©6ÏÂ$ÿGE&-ìT±…˜¦>Ì}Þ]Ÿ%ÓŸÜÆ¹÷{¶ª­´ %Æ_]0édù5ëb=Ö9ÿÑè gß cˆ7Ò[Ùëç¢ÎÂGÞgŽ\À+Ÿæ;¾õyYªPí3ØPØ·pÕ<ÒpÙš’pñÁsÛ«i>È~k,ªo3Æ`åš’4óÅ{ã£Ù‡û(ÔYÃYÁÃí—#mgð¸•{Ûõ²qÕú 4¨šÞhbâìÅ9,rÓóHТjÍÔJtÁç‡a9Cx\Lõîð)žàמÍ?³éfµkÌ–:¯£lG‡éMޏ”“ëçuñW7Í-'ãJŸ5™7³UÍ{GÈÇg]¢(Z iAœΙJç7 üç @Yy«U뺜œù¸xÄÆÓ+³þ]œÄ[.:´ðd'¯ÒûÀ}vï9HúÞ•Åkt,L~¶eüP¿¦gÆ9Uî¯IHš/Ù?ãd¯}¥_­é3VãééÍ%yf0û»õ³õM,{€ÖÞ}×XÍ?ÛÔ hß aHµe§yhkÉèjoÜ\.8;B½† Àˆðu\W6$¡=~¦9Ÿûx×±f“;®›óœw¢·OZ5ò•Gg’wŽk6Å¥ó†%瓎NZ8üýþAªuµ£™Ï–¯¼7üPßîæÍJîîr«,i›nVíœODA[¡ô¶wia?Ó™VÒ|vª¨V'Siâ;§?MôÙÃo×µµ¬áÌ"7x—ý.žÝh2šE-²œÚˆ{c­¹ÜR¯#ê´ýâ@AÚÈÿ2B®£x‹éŽíÜç9Æzá¹ÑCåjé)½ëÂq<¿7òñY— ³’³Ê&$•0žT† äÑuì¼ìÝ-<˧²‚Þóž½A ÈtXsqÓ³v®ÏK†ÈXÖÉøÉª}ÛèóȺ̌/—½–.^ù[ÑçASn%+ôð8·ø©åÆÒ4–rmVë.ŸŸpݼ††hFÒó]sÆØŸûUþL¿½¾sÿä‚%ξ‘hí¸ºûî©9±"í¢Ím¿eíåª}.2“î¹ôì—]:-Þ{ƒÙÿãË[ºîoGÏ^%£`óê3@Æïòò®Ê5Æ fFhÀ[‰NÝ´Ê ºÎ8ŸÅÞæëJºŸÄb%vö†·­Ïô´ÏW¶o:’6ë”G'Þ§ ‰G‡4{qÍÞ¤ú^Èû~hü meÇ­¨Õ ûª·rfÓ±Ð%”œ¾9r3jü4m>E2æc»Š_¸É¹H-v×Ì㟭­þ}+;äðä^.¯ø¤A)ã~&ÄuÎý@ˆw«_´õ©Q}±×í{Ž¿–Q퉆@Èu¤ëŒtéï2‚Ón¶Ï³ìô2ƒó¹/¶°O¾2/v¸ÓrÖüíTyýEe¦<Þê]6²­Ew}ô߀J ¡6$Û8{Ýk{¹–Ÿ-ÝæFñ½!p1»U »>íP$[-¼z#±g_Ï¥¯ÇÞZ5È`£aŸñvÃûYulm¬¯¥,Icd&ÇÇÆD„¼ºãÊ•+7ÞÄòk¼¡ÈvZsëLŒå¨c¥ƒ¤æ½Ý1Öø°»Íœ™vC{w2ÓW“¤ ýåM€ß¹Ã»÷ß ¯8¨”ˆÅÊÛ¾“›ÔMOÁ?ÜwÿGu¿GèÚv{=޶\À‰ÐùnZ¾Zé½vÞ`Ó’Û–°r¢Ïo]âàå_~šx÷-»ÿ´í_`R«N/¿c±.ˆ3™ótU7½kW,³ŸÐϬÂt 3c‚Ÿß¾xúÄÑcbúÜL¨ Ùû¸õ²ó^þæ sêÉú²gD“›}–:Lµía¬T¡y•ÿûgÐëg÷®œñ=}ý#»Ê2£&ñì KS’,LÎÎ~àØºÍÏ«¦v×/i¯ïÒ1ÂÃ{ù¤Fœ{1R¿=½zØsÕÆë?ÙëOo¬I‰Ž* j@×<¯»Ã䇜RÎ2Ï 9àíÐߨôöÕ…™?ŸÜäâ¼÷Uñ»DYW")¼Þºqúk)ÞlêZ ö™…°ëHUµv£|óP•6çÔÿ…vÂ;>R˜xhݦCëf7ëe;jÔðÝ;š6Q¬8ì:+?áãýëœWœ/;TáÒ¯†ë"á? j…ª:È}¹ée×µZ:÷ýùdš>MMœ… Ùhʽ6jNìp%¶ìÁÜowö¯¼³_€ÅÅÛvÕ«á&"Z#J« ±ñz]viOVÐEy=ø•ª6xëÍãóÍeë¬ú¶ïþ¿ê|ˆÎ9{!ØràžÒ&³Èk«m¯­&djÉS2ã##’ª¤£y—ÏÌ }-_íQ¤Û­¼}9¡ëв‹²2ßuµ9êJÐä5›hÈ‹f§%ÇFUXMn'VâÆWdìµòié|~[f±º‚¦®º¼8+7#5%).9‹\ÑŸyhÌ]»Õ¯Œ¯'zpÔ04ЧdÄþ‰ª|J¦8ìð9'cèOX9týˆË%£™EßݹøîÎʳè-ýðe}ű e;¯ö±97úbÉ0ŒàãŽÖÇ 19eYjvrJvùI“ÎôS»%úo‹$jBÓí³z»éÒ·% ÅßttÓML½isMyZNRtXX\Ùæ•êµí„õ¡¾Îõöv!÷×Ò¢Ãbû¢-ü:Êt´Ÿ©h}XÅÇ gÌm+Pó°°ŽOR2¾Þ;¼šýÃþ•*¯¡ÛHYA^ŠÎ>úþ=¾ò_õñ½­1T µ$Út¦÷4¯øÏÚPåÚÚ_üÚÝ×mžÓ¶Ç‰üç/¡ÜiÎ&ŸuÛÔx£vhÐèïùä[¯M³g¬¼QóG{%’¦S¼xNmË£ÔZiPû®®÷MmÀŽÀGc†»Ý«pOáŒèoÁÑÕ Q±^á¤k¥ÿsÿLºúÝ/›Oåx±B/¢05ê[j” …På;¹ÝûÜÊmâTÇÇtc¤D}O©©ª–Ž,ÏÊR$M]oÜÌî;ÀãmqÛfvÌ·\:ëŒ9tçàÝO;hl^sK¸æ‰à_„Ð4FºÛcåóÜ æ¥%U<((ÍgŸ¿½­_â'Þ…‰›8_¿Ñ}Èž Eņ¼­<›ò€­wNÍR8rHàµü‹»ŽbÆÓœ,ÖÏ}YþH;‡Éͺ\8Çgm1Sc~¤rï9/Özžïe¯aþÆNÚP¿ ¡¶(r]ÜÜ»›ú0Ÿÿ¼ U¶•ÝÖ€‘K^žÛ»çˆï…{!é5Í)¢Öª·í¤Y³&õ3Qä½$¦Õ×íú9oÎíôÙsô\ÀÏš¾2Ul9`씹öÓûçæW mßÕñ¡)w[qû§ÍE¯5î;ξMæ6‹J›Ñó–¸9k.S?_ÛRÚ;\±}tpó&ŸÃ~!™Üç’oÖÓÆnúìi=k\\oèfÿÞ3üöoÙ¶÷äí¯5n5 ‹Þ};~xg]¾WJÑTzo~úÍÊc‘˦󟳪>+ªßßq½Çò‘Æ|š¡*v]í5æÖÑC§¯?üò3>“ï­ð¨rÝ~m»Ùi¡ûůÕÞ;âM‡ºlöZ<¨‰%W€-ºúà]¯ƒ»­q\²åvxõ—Õï;ÏÍÝÍÎLŽÊç_ÚßIÈu¤kÛ:õr}¯ä¯ˆx_g[~}Ù+ÊñÉŸ\¿So¯_¿zõÚ­{þÕn=X’é°©‹ÙY¨ (78. ˜ˆ¡Ëg– É…èZSäM^ù¤É yÈÿ.e‚U³çÆþ9TúóãÛ_ãÒ2s h’òŠJJ* Í,Ú4U­¡Ë/tå6cV³òpNÜ—7o‚BÂ"âR2² ¨bR²ÊºÆfíÌôHtø7öêtPeZØ®:c»òHÜç—o?‹ˆOË)¤IÈ©i¶0ï`Ñ¢‘„ åÐõì?°ì¯‚tÓé¬Ó‚Î-¦i5ÇÇjÎÖ¬èO/_¾û•P´ŽâÒ²ŠtŒ[š™(‹ò]KŠ”A?‡]ýväć¼}ýþKXDlrf.“&)-+§¤®×ÔØØ¸©®ÉãX\§ÿŠsý—¤~ðì]HDBZ.!¡¨ihÖÑʲ…ª˜ eQe› ˜¿yÀ|2¯,Æ~å ý'}îÿì}hdb&ƒ.¥¤idÖÉʲyYïYqsÏ–'ß²(ÒÍGoöµ&öã“€Ãb~gŠH*¨é™¶ïÜÎ@¾ôÓ‘®;ÿ=‹÷JÊô¹”]w} êòWwuä"?öCXÙ9¸âˆE}xÎ}ëàø$ù6¤ˆ«·0ý³’ Q_?} ýö+.9=#;E—”SV×6hѺ¹¡o/ø/C€àCD^¯MöO]—K•hÔ¢ó€ëºÜ_îŠD#“nCMºÕAQBC•jܪû°VÝÿ° µæ–š[ÖÍ:£ËtlЩK”ˆR³®6ͺÖIYì8eÚk”i¯:)ì/%œ:f¼ð9P:Â=¡=Ù¾SMwO„ŽOAÐe4M:²zÿŸ_þÐ@ 3ñŽÇé²¾2¦ xÝ=à_„ dä}Ý·òFéHéâÖ‹yß=à„ ‚ËýºgÆê²ujL]1°I†ÿhà™úòCl‹U˜“öâòîÍ'Þ•Þ¨F¼û’5ÜXà† G/?|š˜ÇžQÐkÝuà¸9NÓ{ëJPÈÕÂ^ì0*(N ˆ8ÒEªÆíS‘ˆ’aë1ó–90–¦T}’”¼ï§=}Qæ4£ÜŸ•$¨ÔKV 6þ!=â^ÌÙž2ÿ—רoÐ 9·–ÝpÄZQÈ9.çËž±=f_Ž«ôXüÿ“ëüOn?êùè’“YI(eÄ\sêg³ý#£Âœ)?_^ÙÎþÙ:þø½ývMÄê¥Éßû®yì{þõãg›:ÿAòÍ~¿ÕiùÎØÖZS§µ“£ñŸj„$éØœó>nn'Å{¶öB® V¨ò …&)+YÐon«x2û…CëA¾)¡9ïþs·–œ˜K‘QÈzìз$=«[»¬u°íd ÀLº{t“Û¾i÷ Zlñy{Y ‘ûqcÿÁÛ?Í)Ûiö:—ñÝU¨)!§¼Vl}˜Dü<>¾·²Þ;/KÙ*ñUZp©…&!/Ð"̼”,œêñ<‡ö˜wpÆ+'‘Êó²˜L‚J Vç|ö½Ë6øCÐ tÄâ~åýðœ³gÚÓ…F¢¼f•SVQQäú”²xÉ/Y œöTš¤"{îÒ‡ #,ÞQô›œÍé×gFipþ¢´èÔ¿_‹ž­ì_(©þŠÉï"+Åeý‡¢'%zîysc¦AIS³±…õ€¶¶-í®¤?¼ŽÎ{>¿IùŸEAkÁ£ ,¢¢6fÝþK&«¿ħ[Ácžôטü‚ Zl|¹#ÏuÔʇ -<¾½[T«ócí]³qß%ÿOñ¹ì?àŠ†–ƒ§-Ymo­Mû±Õ\ß1ˆSø»9:ô9´Þ×’ï ”«y‘òÖvVVèÅÍnžÇn¾Ï`Ûô›äºa‰mS)Nh €ÿh¨{T£…î&.N÷r_»ÙŸ}Ý®±Pº°~?ó}Á,úMÁª¡•þœ‰5ã7UBAªø•YÉO=È-úM{îæ‰•‚]k„û¢µWV„Äë÷¢fÏÕ-mëýÿÔ¢š(.*ʼnê‰÷–¹÷0¡hE8Oæ}Û3Ìbö­Ô¢GT [ȧ‡„~ó?âêîÎŽ·Wm´;tmð9©èYM³všª­•é<¹1רxCd¿wïi±ìEûWý*iŸCß\\?ò†¿×Û»NÆâ‚”ð_ƒ uÉP´Ýµò¨É’Ù~NËï8Ô§jrùiI‰‰…•£ŠÉ)ÊŠòë´óþgñ‚2mºéUKrti…²?pùÑo¾DBÚÂÚP¼êœ¢:Ý»¨!‰ñãéÏܹºµ¨E­± RÃlŸçù½x;ض’¢½âæ„{½½_Ï7—-dPDFı)󊃬±«ÿ“ ]¨Ì”€Å»y„dÝwœyvȽ}·õ•š›m 'Ó¥WžÎÖ¦±ÙÏc‘SCîMÒ¤1~œQœžÅºí º=ÛP¬àבÁ-&ûe=YbaÌ.×ù” ]÷›ே BÀ"ÄšÏÛe¿Ûr[Dâ‘9›æ~ÜhXÓ¬/¦5U­úXi÷Þ˜9iÅ­Ê„¤¢•眅Ù)9Å¿H)KsiF¦É¨²s ;@g§æ0kQ‹Ç#”ª…ýŽ'⟎S­ñ€Û"t‹ÕÛFiÒò_•L8yÏn£À^_š» Ñ~ûž.ˆô^½¨‹BQ}© –wöÿ„Up8 y¢MÕZÇò]d¬Ṛů'ÑÇyŒaÑYˆˆÎ˜}~Êo’É&"1|Kè_S þaÐ é˶:6äLJØæ9{§Þ0¤ÔõH4)eÎuzñé…¼ç”.™3=&¥ úÓŒôØôâ_سUÉׯE1¥–Cg®Ø¸Ô¶©$Aä—<&jd¡UÞ®žý.²øu3ý²±î¨²ú-Tˆ' 3ês\~Õ-À"„ṫ¨âÇU›7.mšÓꥷ‰*-åû“3˼uà ˜®Ø6Z“û@ük!t´FÖ3»Ò_0÷×l ´^ÓQŽÊH¸ç¾6°èIé³»*R(1TNWðÄÐøü„Í…´fœ¬Ï­ñV°[C‹›¸\¿ÓÇfçç”Ç>sûTzVy€ÏŸ>ŠÅ©RÔpî¥Ño~•›óæ Ó°ƒ•æ5³¿xs‰iåN dj!|tm»;.´›u+íóúNª‡›I&~ú^Üó¤ñ¸C;©P ¦j m:ñƒADmíÜ쮩咋'ø-B"ú3ö¯öí°ò5#p¡‰úNcåÔà°ßE[¬Ý=AG\’_ ©õ¶Aê4Uu‡{ç«sžp¹|ïÏÑ5ïxókÄaÏmG®>zû£øAš|“öýÆÍs]8ÊT®|xšJïMO ;îå}ðÒý×ß’óÙ¹XR­y‡^CìæÚÛuT©ùE„^ ˆμl¸kÕ†}—ùú‰ ¤4Ûô5wùÒ m‹ZΩjCwøŒ²u>ó5+?òG2!Fç»EªÍЇ›¹/ß|ÌïÍà4BR³mÿÉ‹×/¶1’àEþ‹  îÐ4g¼fͨö0]oöãüÙ‚ÌY)ˉ¬5>-Ö¸Û,oöÿ‚DT;LÙtfÊ&ž3 \ 2UàUreòÃÕxAŸ¨F‡}ìîÏR$[Ì>ýeöi‹p–“n>rý…‘ëkó¢¼Öà_… @4 Ð$ @€ @4 Ð$ @3Óƒç*ìt¤…÷DÑÊ÷`%>!Mb²á¹²e÷cÈÿmkCÐûß5YÓ«òy'Í?y"äÆ\8&'O­ð#ÿLÛ ÔF¾_‰—¿Dúû”ÓÞI÷îdýˆcE㱊4é Ûgv£1CÅ%©Dų¤‰Z¿~0ò;+#Y'[@H˜W;|\óY~gœ~{~w“û?¿Vþ·äõ¶‘7>²ò>r»ÿOÛ“•œ2]ùÇÇÞîÈI ùµ@h ©t‹Íºƒo|»:?ò~ï&}4(eÏ0cS7ÏKËQSÚä![ánf…AÛ’bš<•qÏ+Ý¡‡‚"·˜›vò׎I-–õ¢Q¸¬É.ÜPºûõ¹ódk8>ù¿V k+S´õ¾Í‘éªô† Ëny¯°ß¤d{ŠÇ%OU›oäc™¶Í-)0”³iºUPï Fá]²Tå/"¸V“ÓE„Be%ÝŽqsLøRT¾ö@ÕÅÔ-8å V8Ô+h*½£—nÿ[ßoN‹ðE·%}ûÔä¥m^•û1çå^óÎb©*ìL“¤©ô×JòÝ“øu¡VË*g,¹Z®ƒÒ–m?6¦ùL3.ñ'?4åAAíÚh —^ÔF²V´â_ãç÷Šþaª<ÿ¤–¡ %=$ýâªø¥–!z]”¯?¿¢” ?,™ê™«;®ñš­Ò*Ìüw‡bvOú×|Ÿ«DQ÷„‚¼sCB}žÑ:,××[”žqaÑÏû±\^…ª"·ö¹îŽ~ádU6ŸUÖR“`2žÍùºà@A“ ×í’R&òƒŽÆìp }ó«éÉ-ÒRÕ7ß×bÕ¼¶Nòë߉=žêýFrÁ Eš†LFÙr* ’¯Fº¾“™v¸¹›6+êr´óüXçé’W/Ë+Py–ÌÙ<ªY¶vßâͧuuÒ±ÑdE\÷Ùë4]âÚeŠ …@½B€.EU–s<¨øtpòšÅò‹é‘—ãèý¯kv¬œAs^'^%-TiQÔ^+1tº¸¯[ò© “¾ÕšŠé"½¶k]¹ûkÿ”„ÞÏ5«úryáÙq¡ÞUBæÏÓ_¤†´~[µÆYÔS¶s7‰ã˜b,v ¬«¢aIž¹RÚì;VÒçÛ¼—´\dÐÆÕÑϦX)yŽ>cÉŒÖݰFN–ý²]eÚš£Lc‰ê×åÑéÚ­ÄØDRT¿µ¤Ž(Qð%Öû@¾ho݇”TŠûϘw—ýúÉË'úÞB£!šUkÁ÷µø¬­™¤–û1ª¦™d3‚•Q¥|fœÈôçšýÔ‹^WuFãq;Sw=Nù™-¯ ͯä ]}ªW“Å)ÿ'ab0Ö°¸^½%ÒHý‘­`'páPŸ +P µrlºÓþ°…!ogàØ^)ܲ¯¼’“ ñÙS9Íq*ÍÜ"z¥ýT-Ót”–lN¶]³vÂ>{±*Ý< 2 ‹zI(R+ŽâQ’Ÿ˜Âb•MS©JÚ"â<¶„®¸‘å¿*îÁ6Õ.­è"B¼…Òôä+Ï«(VÂÁ”¯¥£´t>3¿dvºÅh "0óÁ'¦U7Êï™ Åb¤”liÜ5’ï®{,‘ï˲Rž¤þd‡Å©rÊeÛBDÔrŒ„×ÛìG˜C4iUæç÷Z¬ßþ¼×–ß™)¶+ë°!J×V!ˆPFf¡ % v2ÔF¹W“²òEôÕ âgAVaB‡]•ÞÙK«Ó¥ŸÏ(r®žr UzµF§¿Ê$Œ;Ȧp²š¤Lÿ¶Ä–{ñwÃF7©ÞÜKÑš¡3÷Èg×_׆ÚhVzNTžÆÞö™±…EÝ`K_áçÆ±›ó+Ì%½!±iž=1$»hz®+X²<ÖÅ,–3í%Ûe¤òPIùjÃwðUsQ¬Ôoìøø|øÇNՖЉc²jz8ƒ]F*6™½±A ¿³—¥kjTì´M‘oB§9IQìÓ ZåýÀâ÷Z|×–OÃu(×f·^ªb⣪_o»eöïΗ* P,f(£I¤‡ßÏJbHj”ìªÆDÍm‹Z¦Ùaú»×¯/Yczëe†7çç}¸•öÔ/íÙÄ÷Y¨ï½«Ñ²Æ@ÈÕHB„Þ¨>ú3Í«´ƒR¤hvÀdU+EpjÂ¥dö*‹É-3ò{- •ÏÚ ¶ZÜÖ”_ÉB¨…@B€Pvöù¹„”¬Ã µ&âÏË=?)2`Bðb3n÷Æ3k´Òù÷DHˆí+lm=ù-¢w¾ó T\ع$IËö0æ<Ï”»)P€.ÆÎý­G©²æmê»õ¢ÊþI"•‚¥8Ž2+ÄÏ”LAŠRÞÔ”îòre%ZZŠpíI £E#ˆ‚ø¤ …çüˆhÝ囊DVTTÅFVVJ(ƒý”š­ÚËQø½E‘ßÚÖ–ðJváP‡ “p=šPž£1|h•a餧Åxþö}ØØt[3!Õx¹îÈÓ!gçĈUkMT|ø6ås½’Nù©yGod›JI1'4íÁÛBþ«Å* ö‰:öYzÞ%MN8fd-­è–•¬ÒPK•¦³~XDA.‹(^Ñ‚ð´Ga!Ï·(–ìP…æDfÐΔ¨1ªÚ%-ḭ́]‘'£d&¬PÔ• (YH+9Ág2Ó‡Èqº&g¿Kög‡ÚnîG¡EyNS°b#"ëÓþ´Ä‘Jªœ¾ ùy|s QÙ^¦´ê‹ò}-9KÞk˻քlÉ«Yç…@=A€³à±GJ&!6v¶dµC5˜®jàåï•–Ø_I•ÛÒYéY”îY'û¥„Nùã²=µví/œ3=ųíÇóÖ ]:‰+KSrâsC§<Ï/tP» ï±(Tyñ¼€ýI£r&—ÑR"rc²ïoJH¢IMPå¶äEw`éÞ†xç»ç”øD+z~hÚ±EÉvíŠû4ð,JTWi‘kâ”M‘3†Ìœ.ÓX¼0ìFÂÞ™Ôá²³‹£ž¸¹ÊhÓÄ]§.Ro<ÂJ„–~Á;CM•H̨Ö7ƒNSe§ö¯¿O·Ð·è«¼pAâLŸ_ó¦2¦‘T`ä¿?³÷Ñlmc«F\*Í÷µhüÖ¶ÖÈ•\¥šý$xß¾[x« u ZŒ¿O<`mU6ãÒÄL×WÛ%züíŠãu¹— ÛKsùè4§ÓUnKGÕÖäB·´sž ~7SNÜ.n3£kšIu×>CÞH‘oÇWŠÆtýÃ"1;·þÞ:6>¿xñ&VªËŽi ©>¼´ˆ˜í½è©Ñ—Ç}¹À^%cùÑ[ô­|‚1 Y|‹¢µÚÐì˜~ô.ï¤6q…!©+Õm½Áœ…rœç1‰‰· ³£No‹XâMH7•³_¿ùŽ`'?&£z‚´^ùöø¤ s~øuÓ>ÙG¥Í–fûõ¢vm]v”½.EÙ±G fŽ—¯¶¨@¯Eá·¶µFªäªÕä …¸ÚP— «”Z×fIåÇèj¾,µ¡‰0\2!6.¨Í¸êóPé]O™¾>Åei C¹ {å&¼‚TMå㬠sPiM§hùLÑdYQ}EçGŠÎ²6-`QTšñtíÓµk*œ¦.;ã²ñŒš ¯„¢b£sƦBkó²’S¦+ÿøØÛàÁ9>wù§1£’&jýú5ÂÈï¬L 7,€´±~m uTé̇F: ûVÌÿLEþP¥í@‘–°^ ò»µÞCÿ1øð&ãóÍ\ÿùþzÿLEþPåí@‘—²Û&UŸëõº+ù~ü¶•IÏó2™!!bÔKqÒf"I "Ãzêݸ£¨D-7?÷`«Ï»ã|›tÈMžª7ßÈÇ2m›[R`h!AÐtª.: nÁü=Y#üsÑÑÃÅ¢EúÜ»!ÇN[¬¬Ü»k¢žL‰f¿E®™L¿Åšs&HH²?; Ú>4vµ¢QŠ×ê§Çב.9­w´Ø9WL¤úJ×T…`ÆÕ¸bÔŠÊf&ež˜yâBöo!ÓLn”‡ºJ ›†›\¥"wMP¨¬¤Û1nŽ _Р׍º˜]ëâ‹2>íÙí“òú £ È6“°XsÖx )*×á¾ý{7£RxURÍyFžf¿=W$Ų9ñîKuWÌ¡®ˆØ¹?#:‹Ð—µÛ£7½úg­úv¸½·`nÅ.¯¶EïÞ‘òñ“ Ó ú)OõTïeD¥ð­ 40Ðìäó5~~¯è¦ÊóOjªPÒCÒ/®Š_jYàÜx\÷(· þQ 6Ú%Q§à[ÊBq†ª™L={6ð"±âMÅŠ8“AˆN˜%)Éž(^œ'2ý¹f?õ¢ÕÇíLÝõ8åg¾¼y+q1‚Õo-YÒu8'ÿKÂl¼Ê†-Šª´¢,̉÷zþ{23mäeeN©¾ì–°r¢ï:‘[3¢>Jʯ?¨¤Îuñ,J†ÇŠeËK~M8úŒ%3ZwÃ9Yö“]eÚš£Lc ®—ÂÑéÚ•+ÂJ..ü'ab0Ö°ø#±·Dúƒà©?²ÚHŒ°$Ï\©aMöSP,ޢ潤å"ƒ6®Ž~6ÍÀJIàí_Àâ]T7N5ÓÄÿÇÞ}À5q=pI{…)È”©8q¢"Gë@EA©âªR.Ä…¨8@DGÅ…h÷¨»T­ZõïÄ­ˆ‚€ È†$ÿ—„=£ ßO?ôòòîÝ»3¹ûåîåòë¶FU褤³·â®ïäj^ÊѦÛÍPÕÝ5ñ‚ÿ§+Ïyí[3¿f£qäËo^|É*D‡¼9UÛ˜†§ÜœS»sIÓÆ-JEh­Ò¾x肤„ž:! 2¸„T® /7áÙ„R,U)mYòè}~—(²ˆ¼ucßÙé.«^¬Î—<}Õ7Z¿wã*8Õ6¥P]Çø©ÿd$F9Åâ'M•{è'„'‹³ÕÚªÙ6)žŸm¤EÈ‹üLºÖ„Ÿz1-–0¬FËËçñò kHt)C®fœ»ÇënS~»T½ýùI[«kʦ¹°ÀBÙ‚Sø[MRžé^òj…/l†‚>ÊKûô•­†‘óáatt’S*šU±¯áY¾aM+X_9r„þåóùµÝ¨¯.^¼˜žž®¤¤TÛø6  ‘µÖñ_–?wAÂìÖ DQª•­¢µƒÚ`{Ye6\²®’Ñ~)‡î4òlÏÌ‹M=ùœ4Û¨¢[j SQBº$2ÃZ«YÓ~÷I:s!+>‰Ç+.-®Î`6_dèvøáÖu¹œ1Ƴ~–¨|Ìðç4UeÇøé/ htl¤^ªm¶DcUBÄ ÐL[–UÜ6ƒÅ,^4ÿÓ|ú÷ÊÐ;+Ìõ6‘Ç'åþV½ýkjJ ™ò,É♂3ºÒfñº †e>¯h³|éF«ž¨Ÿe7ég­`M ×-|þÚîÔK,V|ú€šbJ´™or|jîÿN|üûäÇËÇ’ƒ¢’C;hmþS»…"³‰«†‰_ü‰M™“Ú˽Þú–­0qˆ$«æF+QðvóNX¥æÂ–h¢âhýfÉÁ”{¾Ì˜½y2ýu»4ú²%ðßîOº‘Ãè¼ßhæ0 Qøã%fæä–ªRwÌíÕ%¢ä!>òíÜ¥Š;}åd+ɉŸÑT• º4ÿç¿K)•£óòŸ'~ÙJUÒ¾Š½¹9Š2-º°«9ƒ^NeÛ_meµM‰Fc¶¯ÙhÕcpLè)7þu©óë<Þû¸ü<v#ÍÂ’ª^`Ÿ¿‰ n@€æsƇߗŸ¤ª#˜Áb™ÚÉ«xge¦ ¯ü³ØÝf(É ù±™Ü‹—è±CAé³Ï| ¾1Ã㟗ÌûDÛcq4Š2Ÿ»1ñàŠÆðßlyéwŠtÝ©7Äeô÷Gw¿çÁýšyv«äTkMMU×)Õò*$ûÁ¾ŒôAJ¢aÐY·Þ_¤ºêßÓ+·"ÕSêÂiJ2înL‹wÔÐ+ëÂ{üzw¼Â/ U dÊÖ®nûóW×”þçõ§ØWl´BUmåÎÊF$ónè‡wj„𤋮Ëñ£¬“Ù³šþ±Rêiõ/0¨g LeéÜKa)wâ³]œtUIÎÛ¬³+“RXr¿ (ª¡ÜKÃVíßT¢­áÐ鳇oH°4” ‰MØ*ÝAOºC?íÞ œå)§g¾íèÇiÌͽ½ëÝo6È9ò(íÏsò=Ô>-öøÄêo0o¤$Ì­— ~áßõ®~'•ò®¾©ÞfÕ3iKõ‘­’ƒ#^ÌÔj<¼;»àYúë>ijäO¤òÙÊ®Hû5¬7Ë@uæœdו¯ÇΟà®ÐXšûìXÒæÌ¡Š“*ÞÄ®Úí/Y}SY5ô¤Ü’¾f£UÜíZ”zÆXÝsRòäM¯&ŒÌ3DZâMÆ¡É)òJ>Sd$¤ÆÔ+Ð4W¹mg¿Ý¸>u½Ó;Á­¤$štט®=È´è̯‚œÃÉ#kòt\ÕL«>M[ž¤ÔÏËÕN9§ü1ùùI½Ý}dt¬uÖøs}}ßy÷zGä¥Ú»h­ R‘‰ÌûÇýûØ`Ÿ+­è¢¢!üGa()LÞ¡~±gò¥ÈÊ*e£–l5M x™qB³ºŽIÉŒ9aR0)>" nî:"o¦äfÔ4èÁŒ“¼‚J3dÙÙQÓ‹†Ájéknô&x]ŠŸ}"—öÖ@Îf¹ñdO%õJcõÛ¿º¦Ä=}ûU­Âvع½Ô…&«C€ù&ƒø ï|ÿàчvš¾kµ{ o^ó êh"H?f®º®ºÕUl(Ù.Ò¥O¡2T9[ømËVdõ¼Ø¶è^ u{ý}ö¥°Zz6‰ð,;‡›Ñq·ÊªÐCï8_¯ª>×пÜý–KwŒ°´ÇG7_úy;˪o±P~Eª]ëÂî5s× r¯¢óåT¿ý«nªâö—î¨wªÌc4šÔôƤ’¦¾f£UÜ;ù¥~…Ín?Û0|vå«Pã êèÏPð"u}@žìݾ†8eðƒC€®?åÚ§;w3Žú$\' ^«•8¸c@]ÁM{°g{ÔÕËR¿á#Í1kßa¨ûhO§¦*\ßt5øÜ3Ÿ,ø‹È5ãL߯oo„ÓÏu?ýÆÎ¡½Ï| œÖÝœ<Í 9¬O¯bOí?åûË©M»f\ˆrl)÷µï×üG¿·jqÔùîÞ¹fßà'o¾mkP» «Áè{©mßÚî”Å}wÁÝŽ¦g-—=ÁÁ#ueŠ¢òòµq;]ljX;pAó{ëZÊÕBxï¯ÿ›ÿõý­@-C€€ú%ïþ†€ÈTb¾hí&G]éRO0dôœC×~Ðÿ[¾‹O¿ëN Ïï›ç³?*&þ#°8zÖöÃ||GØh°h¦ ïb7&nÄ…­ŽÌ ;—Nˆ²¹µGÀBo[^¸U_WáOTÎ3·š'Ùåxr@?Á¯FòRÿ9¸paDäù)„¥b`ëì²rIÿVŠÌ/j ê+h¨Wòâ£#ã i1ÓÝHºÂ“ …f~ÍŠñÞŸôkß/:ÁbÀŠÝ^µÉÛ+G½ç®í~!ñÒÍéÖ Òtÿ÷îÒ”alúÜØÚˆÿüÂûUKì—µz±rЮíÛ¼<\£ä&ðs3Q1œÍæ§ÿ½¡s×OÌû-÷ì¬Í‹ûë°ÏBŸvWßß¾0ÆBJÜÖ C€€z%ûí•—„è·±T­éK½y¯BfDÇKu ?½ÈY[X¹[»öÒ/§EÌÝ?ú¢«” „û^kÑvÿQj‚¯ê Y1#2zþ­ƒòímLšj° ‘Öinf)µœ¿Õcç#Å.,ª)lͦcWÕwF“C½N><ˆˆ×ÔgП!ïÉûåÃ^»Ã%ŠÊAñFª«ÌŸæ®öüNoãs§•*=ÏÄ‹O£ûêÕpÓ“‘ Ÿÿ«,‚ÞÕ~ùt°ÉŸÅš áçgeä"§,WÓ­6¸I·£¦Í ¾ZÅQ[B¯¿Å´Ø£g¸Š~KÒÔ©gñM;Øê&BâÓy„”Oçܤ›7‰¤]¿®J99……j½ú´ wÿŽz–5ÈH¬Ö ^C€®ïîbšžùíæ8t–6•©XÿÊïÁˆªûþ×H¿âOUWÀ—±óPOmÃÆ¶CR^YŠÉ hF­®f~òëdB8MÔeK݃­ÖXAî'$¥s…Yrª²%é–ɤUù¼Ê~ä2?9.‰~š>5»QÅ}À‹”l¾‘X­@½†W3~f=ÔÊŸ¡ÚS¥²çy÷ç|vs e¹Ñr߬w߯@ô+Œu…ŒN×&äèÃë—Þ´7®öÅ)ÌÍürù•_XÂ(S«f –‹.ÜvÖaór𥤧TÔ nv ð#@6ÉÏ¿±áMhð‡Ûϸ<ÂP4‘ï1Yë×) ʉ‚á52æ¨þK$×§˜tU*™O4¬â¾`òÍP©7ìþƆ 0˜ü”So½§']zHÃ7Kï' ¯-Z4ײC8øïϾ X”réJnÙ¦¶*.«´{›3k<ó3sþ\òfÇîôGoèœ %s…~^:“‘‘zžô‹ñëg½ V)$š—³µåýM‰œÀ—M:+Â-¸övS`Ú‡\º¾æ ¼t&:ËÈ1 ×(yŠÉB•DŸ¥ŸR»69{‰ƒ;@ÝÁÖ8ª‰×‚§kVßt éÀ)÷NÉ~êÛsúaKÈõ½uÔ 4 ‰{–”É'Åw…ÎO~•Dˆ¬®–¢˜?¶ÂÖÐ×bgYrV­´*ŽÈàü²µ€úšβ^žë±%¿É/—Ë©‘¼»;ÞMüï+³Ý+•—ß’úkÜãuÿÊzœÓï ÂÒ-;š©®´ôŠAP¿—çÕWEªéjHÉ’tZÎ’8s*«Û }{~Üáwa 3ÜeŽDsÊý–a~컩¶ož·R›º[×D‘þ(=Êçݼ.ù2 ­Õªí37ÿ̨Øy‡ø-=tVö“’ÍÊYáò(I¡¹ß ΨñÞç’.Æsìõ cCþ“´cˆÊxÖÊ‚ˆùŸ×#7ÿƒQ—¬—WçåÝÚöv“Ëû‰MCçȰÙL6!ŸbV°‡…èÈU¼Ó@mb›Mœ66Èc{è¬&÷MoÎ)ŠÂüŒç[ݦ̿šÒ²¯¾:›05Z;´"×þ9xüMoÑ[/ÿYôÉX¶nZó0 ÁÎWøˆ©ÚzD[s%2ò™Ýo&…cG²ïퟶ6Ézö¸Ñfâµõ´à'Â’×mÉ“ìm°q›ªºðPlÙCN2öÞšÀ7gãê%í9ó÷-u<šÎ'l £ÞÓ}ùôo%+ ]-¶±Ëäñ{½CCüFî± ºÙ©¥²„‚Õ¯1×t.Ø:yÖŠB´­N>¶|tm¦ø­!@ÔcÐ4@?¥‡B íÒÃŽÊM$$;%žË#b•brزÅó1,få‡kYkÿeùs$Ìn@¥ZÙ*Z;¨ ¶—UþŒ;Åf=LûÝ'éÌ…¬ø$^ÉuaÑRäe\%£ýRÝiäÙž™›zò9i¶QEWp“þ‡'ùôï•¡w:Whóm"/ Ð u)%¼6 Nc*9,Zâ°¨†ZêÖNÛ.9m«ø CÉá¯eË8CC‹¿t(¡ÝsóÝž›Ë¶Æi?$èÔ oÓÔWIEC%Êç[¾ðÈ÷½OÂ2%ÚÌ79>5÷'>þ}òãåcÉAQÉ¡´6ÿ©Ý¢Ú/îë?¸ÐÊT”j3Bƒþ7…˽ëûØÍ;a}”z˜ »êEóßîOº‘Ãè¼ßhæ0 QŸy‰™9¹%5$š¨8Z¿Yr0åž/3fožLÝ.DÏ0TÌh‚ÎÍQ”iÑ¥’EˆÆpÃdöìÙS§NÕÕÕ­íŽÔЄ¨XsLI潰ɪ¢/ä‘¼Ü {rˆ¤¢m+ú¸ÂÈå „ß¯ç‹ýýz>÷A`|ø}ù)Aª:¢_`a±LíäU¼³2Ójøñ…¼OôyG£(ñóy±ïÐ nÑÏ6°ØÝf(É ù±™Ü‹—è±C¡øFµJ]8MIÆÝiñŽz…¿üÂ{üzw¼Â/ UôÅ] ¨÷BCCÜÜÜæÎ‹ P#hº ŒÕ<=’'¾šâV0ÎQ–Sw{ÛÛÍ÷ˆùÒÆÝ}Îü, eBbS#¶JwГnßá³Ì`*Kç^ K¹Ÿí⬠«JrÞf]™”Â’ûe€dµ#¯Ú½8ËSNÏ|ÛÑÓ˜›{{×»?Þ(þlsäQÚŸçä{w–Õ–'ʽ4lÕ>ñI%ÚJÚc¨Îœ“ìºòõøÁùÜKsŸKÚ¼1ƒ9TqÍÓYŸÝh8òòò6mÚ´uëVÄh€!@ÁOŒµ]kf¼!aþ.Ÿ0Tš+:ýn<ÁYæ³n,)õórµSÎ)L~~ÒFoWÄçoS†¶»ÑvöÛëS×;½ÜG@J¢IwùáÚƒLk,k­³ÆŸëëûλ×;"/ÕÞEkmŠLdÞ?î6 x™yÍ|r&Qs#ydMžŽ«š©l™õméknô&x]ŠŸ}"—¶f g³Üx²§’:«¦› ÔàÊ•+'Nüª&à?—‘‘!š@ŒøÐ",‰6Ó Â¦Uþ\·³moT73CÝ^Ÿ}ÉØ‡-ü¶åZèy±¤¦ŽÚN~ѯ¤0Yf®º®Ÿ•S˜Zª;øªÅ3¶ôláY¶†›Ñq·²³þ}eG¸HK–o‹ÕÌ]/È]¯’•QåTèÿç»uëÖ½{÷¾tn¨<^™áG4F9rÄÒÒrܸqµÕ%€º ºá*x‘º> Ovˆn_Ãÿ쎓…þ«¥Á·¡¬¬üñcᯊèèèÌ;—FgIÉòŸº@ºâ§\ûtçnÆQŸ„ëDÁkµ?Ù5CtøLÐ Ÿ{c擹fœéûõíñsÜP}}}>tÃÃè{©mßÚîÔ·oß–À®àsᨠð£Cz œb@€4€ Ä€ h1 @ˆ¾ Þ‡[^]'ÈM¼znl9Á/`rÓGìÙuõòƒ”L.!Ò³ö†ºötjªÂúFK͉Û>aŽgø“4"güw»…Ã'<²=óÖ¯—Â×7Í{»Í¥±Ûó!§OGõ–ýúæj 7)´cÿ’ÍRz‹:ñGŸR«ÆÏº¹|¢•Oîô [|»*0k¯Ëu4| Üäýn3VÇuÙù`Œ0=óÓoìÚ;ðÌÂiÝÍÉÓÜÃúô*öÔþS¾¿œÚ´kÆ…(Ç–rßàgæ3®†ÍRÐÃuû”VMÛpxŽ#'&™éÖÆO’ç?ú½U‹£Îw÷Î5c×Ââ«é S¦E©ÍRf‹µ’*3CÖÒkuè•á.—·½ï;\  rÐðõxIGü'DåöØ:g¤ŽàÜ2÷Ýw;šžµ\öÔ•)ŠÊËׯít?&bíÀÍï­k)ÿÕ æ¦§|$¤ëäÑ.öŠ‚Çž3­¾ºÍ/Â{ýïØüÚYve;ÃP°*µYÊo±r$4GOßn²|ÂôþÝ÷tSG„¨ 4|µÜçA³Ï~ÔqòÙH¸OÉ»¿! 2•˜/Z»ÉQWºTE†ŒžsèÚúË÷QcñécZVx~ß<ŸýQ1ñy„Åѳ¶æã;ÂFƒEs`x»1q#.œhudVHØé¸tB”Í­=z÷Qa‰F&ü+hö¯á=„ewh¯ý‡’± Ü1þÓWŸ¹‘˜/¥ÕløÌ‹Mw4ûùr÷£gO¨,ºs?\Z»jšÿÙ[I\¢lðÓÔßj—z–Ÿýp_ØÜU'ÿ¼•”Ewª=G¹ø/ÐBAx)Û­úº^Ôšgn5O²Ëñä€~ UׯQõ+Nªßh:“8ÿuoáŽ×3žÙ–Ýb•m ý¾~ì6/è´¨ymœÌ¨ó àkeÝ<ö„˜.ÜZ4ž6/>:2ž3ݤ+Tf(4óðkVôˆ÷þ¤_û~Ñ Vìöê¨MÞ^9ê=wm÷ ‰—nN·V¦»¨w—¦ {d»ÐçÆÖFüçæØ¯Zb¿¬Õ {u•a{ö˜]ÚÝóaë•![l9†²gJ–SðtãôžwdmœÃB­ô²íZ2k€3—HȰ+=­Zð4xF¯Ùwû¸…Ïè¨ÇK8·y½ã”â~&ì_ÐÉñ"·óÈÕû;›*ä<>¶sf õÙÇ‘=5˜œA»¶oóòp’›|ÀÏÍDÅD¾úú5mPFõ+NªÛhŠ;CöŠšeUØbÆ•Ží–i3a°ñ†ÐÍ!g5¯Ï£¿¾høJy]N$Žýu OWf¿½ò’ý6–ª5EżW!3¢ã¥:†Ÿ^ä¬-¬Ü­]{é—ÆÓ"æî}ÑU8B—û^kÑvÿQj‚3¯:CV̈Œžëàƒ{YSÓÖÆò BLÛZ*nRIËÙWÞ)PîåÑO…Vé`kÓx¸ÙìX"ͨôpöã«õwELìÇÔ°éaÊoḔˆ>伺Ïoѵÿ”ˆ# úiÛËœ3pÊ¡?þþÐsˆ KÅĤ©›iæf–‚1ÐY÷««ÿyÛµª·zWÝFsÓ(ß™’Í"Q~‹UAªI×þC_zšÛ¼¥TUµ~\Ððuø™±—ßVkÃÂüÌÏÏÊÈ'DNY®¦[mp“nG?$L›A}K¾¯&¡×ßÎbZì¿Ñ3\[KLzß´ƒ­nÂ!$.1W}Ëïîýõ–°íì:©æe¦¦Õ4{å¨í9UôäÞ…x"aÛ»#§(_K뢵tušðl§Åkc—šABÙÄ@šÜú˜˜Á#*?'ˆ[¿R•¯x ÍMCéóZ¯ŽtckfàÅÛ±Ÿø-¥¾Áw=hø:ùé/’ áhiªdHÊ+ÓéÉ hð«vÖäׂY›¨Ë– ilµÆj r?!)+|Ì’S•-IœL¦à<~ *HMH¥-ësJ?•Òo߈lYyO„õ•õJ×—PmB³¨(@~Æ‹H¿Ð ûÿ½ý45³$½WÙqëW¢Š¯q£)}ý-’š† äBÚ‹T.QÃa <ìàëðó3ó‘”‘,{2:]›£¯_zSÐÞ¸ÚŒ0òË¥J~a £L-±»%øŽ"³Ì¬å–«^!ÛòyEÉ7ÿÍæ£&—èOªïÒè€^ê¢~òÞÿ›WUkâÖ‹äwÛh¥ðr3é–S“Ä}ì* _‡­d¨FÈ“„¤ÜâìÉ6›8mlÇöÐY#L6î›ÞœS”êøÏ·ºM™5¥e_}u6aj´vhE®ýsðø›Þ.:¢¬–ÿ,úd,aÛ7ýš»D³5m§Jî]¿p'£³µ°!^òµÀý©„T¼/ˆ€D#‹ÎšäÎõÓWÓlD_"äz´ëÐ{Q}'+§Á)5Áϸ¹â²à™|nÑ©`Á— ÏYVý/õY­Tg¾?ïÝ‹O„adøÍ~1 AA€€¯Ã3·Ò"1¯b^ä Q)ü!SÕjÃéÙɽV墵¹ýð-LÔ%²âbOD\º“Îh:Ñÿøakõžîsȧ+Yñ¾qW”¥wÐyËg‡„:!R:­~Yà¿[jµùùŒ*"¡¤ÅìÀ“ù+<#~±‡(éømÁ¡v‘­^ÎÎç…Öþ‡¦}š°m­ó¤5DÖ¨çO Ï)÷ç«3Ëzý6&=üÚr3c—Éã÷z‡†øÜct3¨úúçûñ¹‚AÖ_¶jÕn4v™Î\÷©î›œ|AO¸e{’÷<æø¢9±› îaPhøZrm‡3Šò ;tÇsZ;™’r¦¢‘â%‹ªŸ›©ní´í’Ó¶ŠÏ0”þºáP¶Œ34´ô÷ç”n*¹#Kcü㋟“Ò¶_½Ñ~uñcî³uÙ´”#[Ÿ^ 5»EkìÊôÖ*§¨qŽÕèÈ;£ËÎ0(êõ ’GÚ=7ßí¹¹ø¡Qõõç^Z|È™ÊGHÔ¼âUo4Ѫ”ëL©ÍRf‹"ßÓ7ï[vîœÛaÑOˆîüIæø€J!@ÀW“6öXÙ#hX¤×>Ç“.š%»F»Â þ2õÿ«òŸú˜ü»guabæ¦Íx*ˆŽ]»–¯ÿßËO¿p¸6â3@• àÛ`r,WÝ¿¶Jô bB­*³~¿r†¬Åè„þ'TÅÝ7ê¤ÚÛz–„äί©{?<h1 @ˆ@ ÐðY\]×v ^âó¿ö×Ëêh¨ªªêû÷ï·o?RÛúJQQQJ ¿j 4Ôàï¿ÿŽŽŽ®í^@=Ö¿iiéÚîÀ7ƒ 5033›3§š»üX Ä€ 5¸{÷îîÝ»k»P 6¬]»v5ר'  ]»vMOO¯í^@=¶aÆ÷ïßc44ÐPQzöóó«íŽ@½4wîÜìììÜÜ\èoˆÏçoÚ´)..®¶;Òõïß¿[·nµÝ ¨£ ëœŒŒŒGÕv/2z·°°¨í^Ô?ø!|™yóæáVÐßœ¯¯ï‚ j» Üš5kž?®««[Ûººnár¹:tˆe0µÝ—‹n[{ þzùò%ýÛ·oßîÝ»×rW(ooï‚‚‚> @C¥ ëÑég>Ÿ¯¨¨XÛ}i˜òóó³²²¢¢¢  ¾³±±Á¥¡ïdÙ²e4@×v/ îB€®shzf2™ôSomw¤aÚ³gϨQ£p‚¾4€ Ä€ h¨ Þÿ°nË?¯=LʦÙ*M,»r™ê9¶{c©Úîœ7>´£î„GÃϼì¥PÛ€ïê>^ZÌÒAý|þÊ jm9Ïn­¯ÌÊxsïÒ¡ýë&EnœrðôºŸµjgo–ÿȯU‹Îwÿ7×L’0å[8zLLj£+Y+}€ÿ4ÔuqáN}}þÊj:%ú¤ÿ ½’ÓÍë7œ_<°ß² ÁuîÆÌiömÏCó xL fM•Þ_?›_ôˆ¡lå`õM{u4Ôq™×VÌ=™Éìxdí =vég$4zøDm»i:êø¯ãnÑCÔ˜/7´3ôHò@›}³–í½ú&‡H7îä8oú‰í”„i˜›úOØÂ…‘ç¦–й­³×Ê%έ™„—ð»•öØ7SNnUYé²ôü»®ûS. S!„Ÿùpß’¹«vÿyëM]¦ªyÏQ^þË~i!—¸ÝJÛõš ÑyæRó$ûOÜüºwé!ù‰ç7Ìó ŽŠyö‘GXkûÉ>¾Sl4„;^^bx­1qS/œèrd–wØéÇé„(›ÿä°Å»&‹~lH<»vö¢ÃW^Й‰L£V´§«0—Ã=j4ÔmÙ÷÷J$Ò¼F5aW|–¥=Ðk„Úñmn½òqÈÏ&[V’wûÜŒ\¹ïÖ^­Üûçÿâõk$åØCN™é{uîêÿÄ|ÔòðõµyqmóYèÒîjâí s,¤Ø2tÞ1˦hO 75h/GÀMØ?ª“ã!ngÕûû™*d=>¶zf ‹õ…Ç‘íº²Í«Ÿk”âä‘n&&òdoIÏxïONnßoK‚Å/+vwÔ&o¯ìðž;½û…W—n®µVd[šî€ßž2ì–íÂí7¶êñŸGϱŸºÄÞ½Õ‹h{¥ÇkûÙιßjœßîÁ-ÕYi΄ú¬vì/ÿh×Oj5€ï ê4~úã±èn¡Pù©W¹fvÍȶK±×ßäþÌa3™‚Z†>{V:é²è¤Á¬í¡é=¶|׳á3˜[=ü)9p!|¨¦0…ÚØvU}m4y±×Éq‡ _'óvΔ§»<ŒŠÂzö«ûü]§D¬ÙXОm/K~Œá”C!°bÒ²©†!²:ÍÛXšIn|I·ò…ÌØ/Õ;üô6gmÁŒ¤[öÒ±ÆÓçî÷¼è¦ÃUã&j-ºâ?JKðPgüŠ£çÿuðAÖ`³¢nåQë×Íè./¨×«ï ÿ?²¥óy„ @Ô.h¨Ó¸Ù÷ÜS•cU^!©Ä‘¡A÷C6¯¸¬¥£­vQu¦J»æäØÝ3>Ž~q“HÚ—“Sø´Z¯‘-ÈÕ¿£îe j.,hî8@¿Ô©nùN‹Å,.µ< UYrë}b—¨TÝí¤˜è‡„iãÖW«¸ßl½þŽÓnþý¿ 7¥ÂÂÖN=5‹jHª›¨ò81Ë”7hªJþ9ä³2*Àã§–ê’ "k1ÖÛ¢ÆÍÿh¨ÓXrj‚S°é‰éD«²»[ðs?¤Òˆ­«^’°Ùšúœ’¸ÍRÔVf‚÷ ©‰¯’É;5´‘L…V^$fó…š¡n¤ZfÇÈÏxéç³aÿ…ÛO“2K2:¿únç'?M&„ÓD[¶Ôys¶Z5¹ŸŸÎ%¢Ù´sª²%'”…çÏù<Ú¶¼µÿÁe‰#,ÚzQ4îbk÷“ÃØqömÕ*Æÿ1h¨Ó fVú$æéŸÿ¦Î1kTÉà…̇ÆÒÀÙ¢£×…—Qf´Ÿ'ˆ¤ ƒÅ¦±ZÆ6ð°e¹ÍR2UÍÔ`•ž9ÿùæ–“ÎKtžê·3´ƒ‘šœ#󲇵ûÙû-\r¹˜Íç KÊt¯Šï2U­çŸx=õéåÇŽŸWg“y¼ò325¬Z‘kÿ„ãàRøÁ¼gÑ{b‰¤íðVò5¬2ÿÓ€™«îwZ4ÖHÔO§•]gMï›éiÙø!@mC€€ºŽÕÈ~Ëé•i½çìt6;¾n°ƒ];CVÆÛ1‡÷{–'×yáñýî†eÆk¤úþüKú¼_:jä<ŒZ:=†«4Ô{¤„cLàœMWN³ün±{wCé´ûÇ‚mŒaíµŒæÔ¬Š‹–2èÝ]}ù¶ˆ™‹zû 5侌ÙåòÆÎÅàáïDž³ÑNQG™Ø=[M{é5íÑ¢dN¶±ÛzàãìÜR¼Û©ÄÅló^ti¹tõàÊF¢”ÁQ“~|8,ôrü/çn&ªYooXœÀê8k€.vÛµ {b¨ûŠífŸ|Ñ'rȾÓÛVDçÒ"Yͦæ{{Lwj¯ZnO&×yÕCÎ-ð9ïÉG¾´N÷°à5C„÷­S°ò¹f´pÁºÐÉö+ èc«Ë-÷ì¯Íª|L…‚µÿ!ÿO|×:÷ZCäzº,<4R.òÕ÷ƒ^Ƥ_»üëòñ{CC&Üct}{©ÏPî¾öê%Ã9ó7,uÜ‘Î'læ½§ÿ~Èǹ•lÍ+,aà}…½hþú³œü7 ‘Ò°è>94|‰›)~& Ö!@@ýÀRní¸x—ãâšk_¶™sàŸÎ•<Çä´w:åTñ†ªÃ_|‡Šõ­<#ïx–-t‹zíV4Ýfó]ûÍÅÏÜà/ÕeuëiÛ.MÛVi'+[gèÅâï*µq]Üu}¥ó@­B€4€ Ä€ Skì?ü±µÝ hؠĀ h1 @ˆ@ Ðb@€4€ Ä€ h1 @ˆ@ Ðb@€4Ô¼yuí ·äê¹™Š›Û{Þ#Ä2ðÙ?S›”ßeߘaÚ~]˜ÝFŽÿ¬°ôæÆ=',h&Y¦jÆõ]ñâ´ÿȯU‹Îwÿ7×L²æÚß CÞÒëÐ+Í\Žo{ïp-V­õj‚ u/éÈo¢²{lÝ8R‡îµò……»7}w!tÛÝ™þm¥KªòÓþ ÚŸ¬ÙÑ0韔Ïlüýõã±ùß¡×â’мv»Éø ÓGwß3Pg¡¡¶ñÒïF®Yzàì?²Rêfìœ~[è9ØTŽQÛ½¨MÐ?.nÚíÈ€õÛ£N_~É%DZݬ½íPwOO§¶*¢“_¼„íVÚ®wŸzw°l|hGÝ †ŸyÙ«ê«ÐßEîý Ù>êLó©WjŸ%×ʩە…á›®ùléVÁQ1Ï>ò‹cbm?ÙÇwІ°wÜ”Kk§LóÿãVRQ6ÿiêê…ÚeÖ¦ ñìÚÙ‹B_yAg&2ZѾ¬Z4œ }'¿‰‹­6Ì^`·¨¹ÔwÙ˜Ÿ‡—vÁ³SõÚŒ˜èãѼ±+'éñå¨Ð­‹ì÷\uëÜ, éšh¨ LüôþC{Ï>ópZtò´4ä°>½ºyjÿ^ß_önÚµîBÔo-Ë]`Ê·pô˜˜ÔF÷[\â®WÌ¡þȺ¹)ì 1]âÞZ¶t1_¹Ûľr‘—V[÷çˆ^°Ü·ÇÿÌ1ómv~S©šé{uîêÿÄ|ÔòðõµyqmóYèÒîjâí žƒv]ÙæÕÏ5JqòH7 yr‚ÎÁ{²jÈT¥Ÿg„Õ)xrxåì°ÅƒÜ[¼Œ*8-Ì{rrû~[,~Y±;¸£6y{e‡÷ÜéÝ/¼ºts­µbÁÓàA½f_Vì³ |Fo=ÞËs›g:H(éK^ìÚ~¶sî·ç·{pKuVÚ£3¡>«»ÄË?Úõ““ȵ™0ÎxÃâÍ!·gu,³ºÿ)nü¾Yë˵ÿ^™nR¼§vâ1Ô­Yÿßy{Ø^M¼³Ð¼Sâ›]Yù¶­ˆ úGÄ}ínGÓ³¾Ëž3Á#eŠvË×>Ùéj3&búÀï­³*óÝ+†²•g€Õ·Y~¹bõCÎãC'‰Žcÿ&å>o1U»{ U9´/àtRßš‚#iÁ«¨ 1Ü6N&RçKêå?ÛêáÿHqÈ áC…ÕˆmWÕ×F“{wxp˦R„Èê4ocI?Ññß *ð^Lxthš(7ônžz®ÅÒKQ÷³†v—'yBfl‰—ê~z›³¶ð”t·í¥c§ÎÝïyÑ)qãªËÊ#wE,é'õÝlz´æ·hµ”†a^ê?Q·‰ò¨õëft¾Åzõ`cáÿG¶t>®!RMôo¼8ðøá§¹[â4Ôšü”gI„p,,µË¼ï˜ªvןùªèkÉ U\ÛQ¾Ñx ¿[i}3åäV••.KÏ'jwÕó¶×ž×§5‹£oοž¦íÖfþröÅŽžŠÕ5X®µw]÷§\¦òßn€"Ð? ÜûfE¦óE‡79—¾Ç1q=üAÿ¸|-¿ìLÜrC8ªÙÇ%†wÑ7õ‰.Gfy‡~œN×±=¶x÷Ñd‰†…”¾bž|¬Ÿâ·òß\zzúÇuuuk»# ÿSìå—„ÕËÚ°Âc†R§_G7ßp(~Øx=É{¼;øËzûP}ö¿%µ¸I#nI»Ñ]•òrr ÕzlA®þu/kpûÊ–Úvüðâ¼.©Õ\‹ï>q…­ÅD?$L·¾%ßócëõw´˜vóßèÿ}´}y!žHØ:tä}0•62DéêdÑ#¦¼ASUòÏ!Ÿ•Q?µT—dY‹±Þ%K–nbm ¼øwì'~K) 3m°^¾|IÿÔr?ª$¥×¥µ¬ÿáÈ9¾ŽûôÕ/>ÓB˜ zM òUsmgŽ`€ƒ-CßEb–M-Оnªk˜²¤ÛÔK[Î&pjT˜ ³ïìˆxM´~ÔI±†¥ÊµfÐ^®66 €ô'ïYtä3B:Ít¯dC¡‡_»Âü O=Qý>Nš¾¬Þž2ì–íÂí7¶êñŸGϱŸºÄÞ½Õ‹h{uõŠWÌë©ÔÔÔ€€€ 6999Õvw®üÔ4|r 4*;+ÛÊÝÍ$pɆ}ÏÆÎ2åÞÛ¾å‘tŸeÊÞÂ"?ùI}áŸÚH¦Âü/³+}³8šòÅ0˜,z¬çóù¢Öž ºÓD[¶T¸e«5Qcû ñï“_¦ÒOŒzê¥:ËVm¢JHa€&òÖþ—%ŽX°lhëeDѸ‹­ÝOcÇÙ·Uc-MZÓC.$½H- Å…ÐàÜ¿ßÞÞÞÅÅeÞ¼yúúúµÝŠj6lu¾ë¸Ó·¿ÁJ-ËÞ½{t·±éÞ£[[C¥¢äPõUaÞ%™·s¦<ÝåaÄœ’~o3ÝùRØÙ¤‘£D :ëöïûß/á­êT¾5€Z„ýãÉ~qå%!úÝ,U¿ô>Y5ìã„u¸‰Z‹®øñ+flŒžÿ×ÁYö6ò*&e¯˜×CIIIk×® þôéSm÷åÀÏËÌ#DRN²ÒÑŽRf¿Ln½dzÈŽ‡S禇„Ç) µUc’ôRU,6}ÊØö±,¡YJ¦JUœä­òܯð ~¹ØÍÅkƒ_þú¯€Wê!SÕzþ‰×SŸ^>qìøÉ“'ŽmœµÑ§ƒ÷…?}:) ÛfJÉÑ÷]k^ù– aÉËË ýý÷ß]]]çΫ§§WÛ=*‹­7rǃ®ãv‡í9túLøš“ákh©¤A÷±3—-ÐEQõ›¢ÄÍè‹ò.SÓî×ÞÒN‡ýùÎÑY‹¾¥3on;Hš.ÓTºÆ‹Eƒš—k  !@ÿpøùù„È©Ê}i~þ¼}\k§žÅ·,T7Q'äqb:÷k{_ÛÞ¼yãïïOyYYYÅ…ÿý·±±q‡èôµk×nß¾­¬¬Ü²eKsssZr÷îÝØØX:ѶmÛ&MšÐ ú0>^pŸbZAGG‡NÄÅÅ%' ÎPÒ#¨º:ÝV‚Œž‘‘A'444äågé333éá–NÈÉÉIJ >xÐ l6[Jª¡“e°eéê¦W•'ÙÃ<¬gºîüýj—”I¿vQ.—}ÙfZ ò,KѪ‹V%)<«bQ5$ÕÍ4é?Ù³øL>)þªm~òã$ú¡PW_E5›CHÂëúOU”ò$–o…¥hl=â7úß nÚU_Û.ÞKgFM¸ä¢-è/Wð‰A¹ŠO õÕ™3g®_¿N'úöíÛ½{w:qòäÉ .”.‰ŽŽ¾zõ*|ø@'¼¼¼è¿ÒŸá¾’NHKK‹&îß¿/Ú2ô}M½mÛ6£çÍ›W·Æƒ1¤uºó¥ÿ~Nò£ë—Ό߼kó”®§œ½é¯Rõ·CÝHµ(l0ÕzýÚ_þÄ¡°Ó‰£Æh3?]ßr0™X.i,اÕt±¨yùÖj^…?†¤¢2M\ß~, ÉâKZø¬}KQU¶äðÏd2çáªRÐ#ëªU«èA.77·ÜSôøMã/=òÑ!­CSBAAA=,XŸŸ¿råʘ˜ƒAKhúÔÒ¥Kiæ¦%Æ ûí·ßx<ž··÷¹sçh =ºO™2…NÐó¨„Î2vìXš×§NJ-Y³f³³3mÙÉÉéÔ©St¡GŽùé§ŸhOèßcÇŽÉÈÈÐØÑ§OZÒ¿Qþ8~ü¸(Œ1âÆtbß¾}íÚ †ëÐØñôéSÑŠÐOtbüøñ/^¼ ô£‚¡¡¡h5èßàà`55µÿh£‹°U 韼LÊ-ɤ¥±´úÍNfX¤ïÒô4½ñÛWÄTí2¢-‰¹²1ò™ãoE·Ⱦa›—žô ¨éÚŽhŸÏ”`•|¨ep¬'âDíÝr:a´‹Âµ-‡ß3»®¶×¾Ø?ëbQ™ÖjMƒÚAÃg‘1êÚ„}xîÒ›üöÆ_’ ?ó‚xÛÅÑC¦””=W|ŠfÙøøxzXÍÎΦô¨I i$¥‡v|ïܹóþý{|ÿ÷¿ÿÑÃ<-¼rå ­Ìb±h¨íÖ­Ûýû÷i¦ÇcÚøŽ;¬¬¬èãGfffÒ¹–/_®¯¯þüùC‡Ñ£/-¡™[KKkÏž=¢ôL—5pà@š¡7mÚDÓ3}H»aggwæÌ™7Ò¿´{t.š§OŸ>M£m™V Ë4h+((èĉtY4кzȧå?~¤iƒftº ÑjÒ§èF ÿ÷ßÿO34CÁÜJŸÄ<Žy‘3D¥²Ï2Õl¬l·÷*1[âÚ¼’cçlê¼ršÍàw‹Ý»J§Ý?´hc sh¯e4År•u” ‰Ý¸Õ´—^Ó^jèÛØm½GpÀqvn)ÞŽíÔ âb¶y/ºÇ´\ºzp#¦,gŠG«ù#k­øµ»vþ³?CÖÕÕ o?‰ÆxȨI?>z9þ—s7U‰¬·7¬ N`uœ5@W´?Îyó„Gô;›)4¨÷}ñÐ×})Òx4¤Š&ŠK蛋V•з†œœ\Åú‚¤é¼B4¡V,a³Ùô/WTB3k%ŠŠŠ¢òŠ%´ýâÚú·b mMT¢¬¬LßVJJJU•п´„®/ýK§+Ý™Pô5—[ûëø9¯.þ}-¿ç¯.-Ê~neªtø¹¹DØŸïsT«¿¶SÅŽ‡jìÞFô@í°#iR½§}k¡†‹E¢;äÔ Ð?¶ÁÀQͼÜ]³ú’kH/N¹ctö]ßž?¶\¹ÞA¯Š1?æ>NCCƒ¦Ï™3gÒD»uëVQréÑ£Çøñãûöí+zxòäIúwÈ!Å%ÑÑÑô¯³³s!:M*ýëááñ³>pàý;wîܾBôX¾ÿ~ZB:PˆN~¯shÈä‘{l‚þP¯¡? åîk¯^2œ3ÃRÇé|úŽhÞ{úï‡|œ[ 6Œ”Åì'ó'yü:bQ2ð[Ø¡vA­žÌÎ$h ÷è+ìEó×ïœåä/¸ª/¥aÑ}rhø·Â®ç=?~ü Ñœ8ФaÍ¡üîÞ½K'lllDC(ÑåZÒ¦MQ‰èü4}›T,i&D§Eç­é§b‰‘~öLðsïÆ «ª„~¤5j”ŽŽýK§Ë•Ð:¢±U®®®êêêô/.WBëˆ>3Oš4‰æiú—oÎSº$Wˆ~зýK§é'júY´xûÐhNßntPG†p0x Ìž”Ð:Ëü‚w'¥R{úü×Ƕ]+ ªm-µ4õª½¶Sñ¥ˆ|ÛñŽ·í8|Díxºü€©½‹~u³†‹Efßmmćý’4›¸flP¿í¡ö#LNï›Þ‘S”ù÷·ºÙÍ¿šÐ²¯©ºàûÒ•Ïÿ öqâ\1¯Sh¾ ž={ö’%KvîÜI#,x ±RTaˆPéYœ„J—x•.ñ*]â'TºdP隉ËuïèÑ£¥*((:tHÔI:-!!¡­­.ŽI§é1›æ°°°ììlZB§eddhΦuh€ær¹4èÇeº¾Ÿ>}²³³ûÏ4‘k;yœQ¨oØÖ;žþíe¶ÑŒ»ü¥kX'ðƒK( <[vÈ“ÓÞ=è”{PeíKhÛo¾k¿¹øñ_|‡²8C/–ýn KÝzÚ¶KÓ¶UÚ] -»EÑv‹JÙå”ÌÎTjãºþ¸ëúJç%Y·Ã¶í\ïõ‹­ =°³¹¶¢D^ZÜ Â?ÊѾkIy Vµ×vª$Órì/†+zI|RqúÕºä*(¼ýs±j/ÕçoÑ@ÃÓ€DP †\‹)‡žv?°nepĩȀs‚á²Ú-m&­Ÿî5ÁVOº†±—_¹“,sÅüfŸz ELLLvîÜI~ ÿ>µ©9hæÁçFý65²÷§Æ tÇU1eÚ9Ù¡5êå{ÄТE‹§OŸÖÉè\Œ!k<`Öæ³ª«SݵÂPu¨p=GH²é‚Xþ1¬²5€ZÐ@Cð˜ŠÍítXTM ­±ÿðÇ>Ѓ?¾ÔsbíãÊ]/ż>³°°¨¹|=‰Æ#·¾Ýµ§ëÐÕMÏzµ‘kP_°àgÜ\9Ôí¸Þìó[†Wö„:w×g4ÔLN·U÷ VÕv7¾†¼åürç×v7à3 @ˆ@ Ðb@€4€ Ä€ h1 @ˆ@ Ðb@€4€ Ä€ h1 @ˆ@ Ðb@€4€ à«ðóRã^¥äòERjúºò¯k*ÑS‘$_4#šª•¦èŒŒª_?hø*yÖöl¹üyÑC£…wo¨±äþ’æä‹fDSµÒQŠ@!hø*lýQ!´úÀ=b)·Ð“Õ®¹„MÿÿE3¢©ZiŠ]Ù?=À ¾ S±ioû¦eË?£„–}ÙŒhªVš€Ðb@€4€ Ä€ h1 @ˆ@ Ðb@€4€ Ä€ P/]»v-44´¶{Ñ0åççóùüÚîÔ]ÐuTZZZmw¡aÊÌ̬í.|-MMMú÷ Pm÷¥Áb³ÙòòòµÝ ¨£ ëƒÁd2¹\®ªªjm÷¥!“À+ê±… &$$¼|ù²¶;Ò9ÒÐа¶{ubDÝ¢¨¨èààI“tm÷¥Á¢™{j»_NJJjëÖ­µÝ €t³G¨¶{•C€4€ Ä€ ßËíÛ·ÍÍÍ¥¥¥k»#ß4|/žžžƒž:ujmwà[B€€†-ÿÙZKcÏ{Êe´Ztäæå=¹»»ºšr:mû8Mó™ïÔR‘Y\ÈM»°~{ÔéË2¹„H«›µ·êîééÔV…õW ~‰‰‰9wî\ll¬»»;NBCC‚ ?„&îþž­e ðó?%<¸¹=dÚ©¨þøßNûF¬Jkòr?¼¾}bGø*çƒÇ\½¼¼£ƒÎœ~ÃhïÙg>NëNž–†Ö§W7OíßëûËÞM»Ö]ˆú­¥îã °hÑ"ú÷íÛ·aaa8 4ü?›9Ûuã3« 7/Ni®P³bÔ–o=6,<`¸‹‘®¨,EÈÇ· ©8'IHHØÚÚŠ¦ †¬¬lñC€†~ ÍŽ}ú©ðÞ|µ[‡»n˜3yÏ £ctËïKEmKJ±‘IËJ…•dŒº6!Gž»ô&¿½14À ~8 +ï`ÇÝ?ïõøí ÝþáÊŒÇ(µËc ÕÌkÁÝ5«/¹†ôâ”»ÕFö]ßž?¶\¹ÞAé áB€€S­ïʵ½£]Nšy²Gø5fͳˆHšM\36¨ßöPû&§÷MïÈ) ßüŒû[Ýìæ_MhÙ×Té AC€€’„®ã†%šÎÚí>ßõAHOåÿ³wpMœÿÀŸË€$„@X2e**ŠŠ'ÖYn)JU´R姈[q[.Üà¨Qq´®j­Z«¶µj€[ö²3î—€ 3!ŠEåó~õõÿ'wÏ=÷}îò“Ç“»êÞ·™¡Ûcã/“ºMþyz£í]‡ôic£¯–óúŸ³!?Ýˤ~wüÌœ’À— ê(5;ïm3··Z4vÑè{kÛiV7BSM&|Úù躕[B·n¸T [Æ3nÚiâú©³&¸šqð€/4|ÙØV¾÷ißJWñZþð„þ¡:-Ëc]°ohùÝ@€  h @¨@Ð*@€P4€  T€  h @¨@Ð*@€P4€  T€  h @¨@ÐP“$ÉîÝ»¥R©ì5MÓéééAAA²×fff={ö¬íêj4Ô$&“yáÂ…ÐÐТ· &L½8pà@­ÖPc  †ùûû=z´è"t;;»áÇ×bI5j˜ƒƒÃàÁƒK.B“ÂHÍ`0j±$€„ 5¯ôEh\~†/ 4|º õõ«ä|ºè¥®gnÊÏŠR¶ÄLG¼×†èªVº’mHUý¨Té‹Ð¸ü _hø k»6]ö¼ø­Õüû·‡„(]òpqcò^¢«ZéJ¶¡:PYÑEh\~†/ 4|¶¹û¶cŽé’¢wLí&f''§¶ ZÀápzöìÉfcžÔg 6}ÿý÷ÁÁÁ4M×v!P (ŠòððسgOmªA€¨Mÿý·,=[[›êêjÕv-ðŸJOñêÖ­[µ]¨  6Q”ü.Û‹M9²gm×ÿ©Ó§¯õí;¥¶«€÷  h @¨@Ð*@€P4€  ¾lâgkÝ­§=#º½~ _ÔWQj•äåFKŸ73î[ÙLí]Ë*Ø.Þ»ùé¤î{3«Þ—QqWD’ºáàî°›×%gKáíZ9òúfÚȆ:̲õ¥<ÙppÇÑ?ÿzœš+{Ï4pjÙßsØ´o[ÔWWu EC0ó¸ñ¿æœªË¨h€º!å¬×ì¯9 )% ÍG{û4U¯°˜Y¯£‘}ºÏ:Çliá{iêÕË.'šôZ4Åž_Ô'S»­‰,ZЙ·ö êx1›u9ÍÞRÈ|ó*üü‘óËGߺß÷r؈¦EHÓ®íèß+è÷,¢×¢“Ç ;smFV̳«'/®›x)póÐã¿LûÚ¨lÜ®Þ(*S·‰ [ÉØª .0êß5ûäŽÅ FÞࢡ0|RVnC}ûñ«X;`ŠëÛW’×êeÚ¢ã÷¾ÝuKõ(IøÍ«‡,=yܲe¸)·xÕ²µ¯÷?:dm¿y¬k*Ûøõé‘=ƒ~ϱœtbc@ÃwwýôßùöZ: _½û×F7z·¢š£P<€… Ppœú¨ß™½qÌÎoîú8k|¼<ܸ!4•Ø/X»u„iéé×Ì#hmºùü¯ô˜´ì}î_?l:—Íh¸nmÃ2—†Y:]®ÞuÛÍýLð¬3ýO Ô.ž±ñŸ@h€º@"ÖuY·¹ã™‘{¿]ÕóÖ"[îGÚOAô‰ÐhBšøyYUœlLi6òYÑèí›Üç‡N¦N‡Yîõ+™XÁÔï7«»Þ™SvÞËØQø@!h€:‚iãNøÊfx΀üVcw¾ãæs¹Ú5Ý7Ûî»)ßnòÙ4}˜ÍæÃS ‹Ã-õ|çØIso&7íi®/ŸÝ¡æè;uÈŽGü§Œ·Ü²ÍÝŒW’¶%×~˜éy*‡Óuβ®U|ÇðãŽ@høŒ¨[{¬Zç˜Yü´ßW/;‘hâ¾hŠSñSÐôÚ~þÏE¬pl²Çãþ¿³íªºIÀ£4Ú/žÿíÑI»fbTvý,,tíÓŠO"$L‡Í ”]íeè¶ÝøËŒ¤n«~žîi´½Õ>MlôY9¯Ãφ\½—I5ü.àÌœ··ŸcvÙñ‹OZ÷À}ngÖuÚ£‘¥#+öùµS.=i´wæÈKÿ›V4 €>#,c×q¥ž‚Æ=( Ðý¾÷ª[Õ*©XÊ`)¹™Ö§C^-#åï3á¢Ú®ꆎóÊ ¯Nöÿ%µÂªW{¶L«t›ú#Úkf ü«„”F“¡'Ÿ¶8ºnÏ–¡þ.-ãé7í4xýTÏ ®†œwÿƒ¥-G{Ñ&tݾm‡oîúár¾lO§a›îsý‡OÙHWYNQ0 €¾,Ò¸Û3éÜN•žK~Kèp$ùê`BèìLJÏ^uà˜Ù¿}ºö]Ýg,ÕD“"Òø½íF¿ž|ùlûŸ¦ûÿ™Iˆ¶}_Ÿ ;ü¿ªÇ$DÿëÚ ¶ºñ"CJ×ÐÑÕcÖªÃìåÏ¿ÜØÒÒ'~ü¹£ÍO_zèfLáÔo3bÎÆußµÔ* î¢øß6ÎY¸%ìÚ3ÙÖL¡‹›÷Âå“:°*©6Þª·õÓ3OåkæØ«ÏQë}&ét/Üs >ËÊ÷0í[q9C¿ßòzy5ZVŽi6ø6=¸ªµ ÕЋ‡.¨F?Ú¶#-±Hq«êŽB¥!¼hø²Pl®!é×–NOܶ×Ö¢•üY ’¸#îmFœ”´óY}¤—­fNäéÕ~ž.1š‘¡n›#û·0áÔ¤Áw\çï¾µÓŒ~~b¦ÛäÅn^Ž/N¸iE®íå:ó¡ã¸4Õg¦E\ Z¸zDûh~Äþ¾z ›'Û]Âá±# _yøÎ!£üGG掚õ}—Díð“#ëS)ç¼[õÚç0ê‡[Z“Ø{ügOí|ùÕÕÛk]T…jM7Ö¼ ×˜0÷Ñб66¸‹À§¾0”üÃÙwó&=ÝïcUŽårDRB„Áï/ËráÁ•#MåÛZLßô»ù ÓËö?2U²ÍwG´z÷½¿ìò0.œ7Ú±K+N¸õ”ÀÙG¦]k¬¤Úœ|uBx&›;a4À' ¾HGô1/õÕ#~›E'¯•þ1Kׯ‚Gî¤ÄgIHQ€&ÍFv­Wüå(5}}B"ã3% ¾EC]òçÉ…+Ã6øômª¯FžÃ·þew×t„«qñ¶ –}ìÉéûe¸'xLÆö4*ùÖÛ¬÷‡)·ÿ9ñoÖX­Ê«€O4|‰(}«²_>¢³‡®X¸ñÈå»O³ß=˜~ׂ)Ðå½û¶aá…eZ*[Ïw 8¾4~ؼ¥ƒš-%ëö®=úývœ[ ½R‰—]Ï\È,Õ“±6EÄ)q©ñ/“60æ•úŽ#[¯EÆEgJHÑ,éŠÕÀ§ ÿdÀ—ˆÁb–¾/‡èùö~Ncµ›¼b_³•ž‹Ê¾îãâõk™ª¸“C×eîÙ¨ÉO¯Ÿ=}æÜ¹³§7ÏÛ¼ÐÙÿò……mÅ›Pe¶¥¥òä-[V¸”¦ËöGÓ…KJmR®Zø´!@À—OôâHàoyê=œØ0X¿(ªJãÿÉÍW¡ ¦ÀÚeØÿdÿý I»¹Üµ½ÿ¿° W=ßî îEš„Þ^„§¾L%„mP_ÏP­!¯ŸEgÓD£8"‹’" ᙚ˛ӕî >iÐð哿¿‘…e ƒâ©tÖíÍ?\—½ˆ$Š3,ýæÖ¿UÛü°é[«¢¯ô1…Ž=ÚÕó¿™–[ø-ÂBÿî;ýz‚·eá?©’¸ËaiÜÓA` êHþú3øLÌPO“¢x]ðìÄÁp¢æ:ıÊlȯMKÅÒªV@-C€€/ŸºE÷ÎúËv…ø-è¾b¥äåµýÛbzxZ<þ1âhè%—¡vU‡UŠ«Ç‰<t=úÑ,Ž6º¬œØÛGWn‰c¶žÞÇTö/èÛ- R—=*sΨÖyÖL½&Ñä?܂ŦƮ÷ÙÒ%p\±Éþ#Zê‹__Ûå¿àÃiÉê†U<Þ…¥m¢MHøÁÀ¶Ý̺öjªýÙ<>5’´ÈÐ w‡Ý¼þ(9[BGh×Êy×7ÓF6ÔQö4ÁjÉ{½{ÂÌi{Ÿ¤ ·óguË jÝ{B„ëÅØÝ4k¢ÿ÷&IüT*/4Ôš.'ÞLX¾Ö£Û·êê9ÿԦᡯ.zŸÕgtÆY¿ª7eYx¸Á^0wý¾é#òd Ô :{í]<ÖöÝMæ4Ú­:6ðÒ¼…Ãç<É 9&í½‚·¬XO|µ;¯½yÕræÜKFìɤ Û q÷©?ž\èáÈ«j‡jÖžËÆòÚæ=ü`§M·¿B€†÷BgÞÚ7¨{àÅt"lÖqä4{K!óÍ«ðóGÎ/u~ë~ßËa#šj|èÔû¬›Á¾{Ÿˆ»ŒÙ=ɱ¡£:aHšŒþ]¢imÜ€Qñ£c“Ÿ=îšmÇ& n-Vu4|®˜foÓË/¥t‡þN-¿”!l;-ô^¹‡ ‹[üšUvpЕ’/ÿi5³þ̘õ J‘Ò¼F<++SßeÊ®«SvUºaeÕ²ŒÝ¶ßwÛ®`wÊH.{õ¥g#σ[¶ 7åGåek_ï3~tÈÚ~ó?X×ôÔ#ÉLÎ ¤ƒ÷7žnEÌÔl;ͯí‡õY ©DÊ`*û=Ršò÷á¢âwÔÇ© 4À¦àáÆ ¡©Ä~ÁÚ­#L9¥VP\3 µéæð¿ÒcÊï#Žÿíðœ…G®E>jÞÌÅmðÂåÃ:Îð¦ìmßcôëa—Ï:þ4}[ð/¯ Ÿrïâ³a¾7ñÎÖ½'ü#oõû®aöøù×s=sÊLœd\Û0uõÅ[ñ"u£FCü|Ùîiôõõβ–=2Ö;õŸÓ÷êë….E¥I^nô°ô‰ûæòé}.9?¶íùmÌÐs;µVzîø-¡Ù‘äàÁ:²ßssž½êÜ…;‰9ò›¹[tu÷ XÚ§‰FÊî¶=Çü%ïeŽ}Û9jíÏÄÏê^n GÕ#U0̯jfª |y ¾,Ñ'B£ iâçeÅ©°’Òlä³¢QáKiʹ­zˆsèóÃY…šÿÙöÚΗã¯ÞžZø¨yVáSî¯Ná:á­†ôóË3ÝV-v[êøbÅàƒí~^ÒyÚãf+·íp­y„ä”Úøéæ©]¦Ýãuòjk–±ñô>ZŒ|Âⲕ]M¦X…¸¿»t²Äxâì½¶Æ­ä![wd^›W$톯>ÒÎV3/òô>¿À….1¼ÈÐNý÷ïÞ5ËgL˜†÷ÑcmtløäP™ŽTSÁ0Üô1‡ * ðeɽñ’óæNº Ã_Á«m¾'¢Õ[ïýe‡qaËŽ-[q^ZO ™}ä›+c Þ^|•¤-Øà®Wø”û?ø†ž˜{çø#±['ÛfÖ|Š•m §Â)’Ò5D®¼'Öþ*$̧—ެ•³k§úCìf„¥|êuÑ#î#ó&ØïcRüÈ¢¼Wé&zO ñ^_^­k7{úZ¿I'ý‘Þu MCYCŽIc;';6‘$ª0Ò1êU3Ï­S•ßW€º à0Œ¾ý“þ¶¶«(åd‰ÑÐÖP8ÿ@’x·ðQóý{•äl–YïSÂÿ9™5Ö øQó¶#»–Ìd`ëÛ yŸ©ä>‹â„¿ÇvmtÞæeF½¶SÜ´ÃvçU{V#ú•zà'¯Í¢µ×•ZÏÒ¶±à;ñYR¢£¨#%#Ó佇 u4À…Rãk«’‘”!–EÁ*›‰’¢ 5¯_öQóõ 5ŸøîQóLÊŸr¯85.UÖ¹¹PýÝ2uóV†d÷ËjCh¥[æ7:ëE芠Gþ¹û45û]²Uþ8"¥#•{¯aB… ðeášth@~~ü÷Õq+ëªÐWþ¨ù·K¨ò U%ï†b”Ù´Ü[%Ì2¸Ålïç>ñ7V»É“ö5¶Òã°¨¼ë>ã¼~­FWÕé‡ÞÕêh€/ Û¨Ÿ{ƒYóž®Y}{Ì6ga¹`˜ûtyש§œ|B¦[>j>±ì£æ_>jÞè5ÏÔÒ×"äiôÑ»eQ·Š_SEWxKͼI*ýÄòD/.þV Þsɉ Ýô‹ª•¦ü“[PbÔô•@EÐ_¶ÝwS¾Ýä³;hú0›Í‡§6gD:ëùα“æÞLnÚÓ¼^}Ná£æŸ‰éîiR4{AôìĹpÂvbû·ˆf6l©Kü}ù^V;—¾¤II%¤ð¾ L®<_?ˆË)Ž´qgODRåc¥ùÙù„h‹§aÐY·C¸._#’]I–­Š+›´Ì0höñF u4À—†¡Ûvã/3’º­úyº§ÑöVCú4±Ñgå¼?rõ^&Õð»€3sl¹lzìúá[º„Œë±8Ù¿GK}Éëk§üp¬P!@|y(&CO>mqtÝž-!7B7ü-ŸëÀÓoÚiðú©ž\ 9ò블vgß›WgÎ=¼dÄÏ…š·ê>uáÉ…½«~Ô|õ©;ùo:V°lƶ Ÿˆº‰ã¨yÔWÛÿ–Ux™eý݇ž/žµc^«mòï÷õÿßÔãBûÿ“+®¢?Íf'§¼™°k­ÇÄ5„gÕµïüS3‡k\xuqéñYÿ¹÷ú÷Þãùm[1ü Ó¦¿–ýò¤Â‘⛂ :h€/C`5tÁâ¡ 5Ñw¹ëêÈ*5¯5ô÷[å5/ôî)÷ý¶–¹OÓ`ü­[ãKÞª»­Þ춺ä½äÙº\ÙRaÑÍ.8&Æ–î»m~qo÷++UØö›Ð{ß”]Ø?,ªñk»í÷»n/YSºÅ#U6L€Š  ¦Ñ9¬ZtYÃûÇi½‹æ'I¾|üa4íØ@­¶‹øPÐPÓ(ޱqÆ•3?_ê#ZîÛÑž—y't÷üß%õ'x}mˆ‡cÃgj£þÈe7ém~ËOO&&„oæ0diàêén«ðB€€€â9|ã{ößÚ® æ!@¨@Ð_6ñ³µîÖÓž•ZÂà˜8¶mÕÔÐïXi}¤/õI“w·í9æ~çó _Uù„A€Ï4@ÐÀkÊ´fê„ÐoÒ_?ºw.ìØ¬“Ç–wÿÓñquª¢E?:6ùÙãþ¡Ùvlå­¾\ÐueÑw€w?~Éû5ÛcϘ2dcPA:~l¥<KSþþ#\ôKø\ @ÔEÇØmݦ]áÚ ^ZŽ»¾ê`ôˆzŠö¥C¤©Âîm÷ië|[Î;qîÙ§…CÀ¯¹±T½Óg_„ ËÑË‹Õ+ŒêZwÐM°òšunÀ©ZxT |lÐu¥ÎÓe¼œ|©ì¯Í¢µ×•ZÍÒ¶±à;ñYR¢SnSÅK.A[X_íík†Qç®ÈÝÇŸgÍ0S¼9ƒoÜP—üyrûÊ0?Ÿ¾6újá5øÖ¿¬¡$ædÈm¢Ö£W-q^ÞÛ­õº}Õ„Üÿ#ìYÎ'š::U@€¨»¤Y©©bB4…|¦ü-õ"tEÐÆ#ÿÜ}šš--iEWºmµ³ ìô˜ïÞééRDœ’üFB´snÎopÜ;~Ø–¥ƒF.%Öí[÷èëúí¸®-ôX¢¤×‰„œŸaÈ­PÓ‹ä\šhà4|dÐuqçÚcB mte)W³½ŸûÄßXí&OÚÔØJâò®ûŒóúµ²M«Ù˜Á JÇÙ·o”òͺ.cÎF {zýÓgnœ;{móìK›:ø_Þ2[ƒ%+–ë:ýÔBûrš©e† ð@€¨«ò_ïZp)‹ŒûÖ†'‹Ä/.þV Þsɉ Ýô‹b¨4åŸÜ‚J7­nã‚”×RR|s qjl*MÔêéq^Wos¦†µËWÿ“ý÷ƒ4íæ.×öÛ–ø]ò:bnD‘g9mp× ¨ Ðu}øûÉ~ÿá¿™Nò[WHó³ó Ñ0òÞ^Ä¥³n‡þp]¾F$)œY!¿~,N·PÞø­ÈýgÆ7* Ò„«—Ò¸§/ÿÂÍé7·BüV=oóÃŒoßÞ¡š!tlÛ®Þ¶Û™oò…‡µ ×n„†>ëñ?›·÷¯Î}pdÊÚD—ã¾±Wÿ˜‡ @àc‰Ý²eË´iÓ„Bam×B¿üùÄ–hy¸¤E9 ‘Ͼt+…è÷ö=·³‹aá,eu‹6õwí Y¿ ûäA–Ò—×ÎlKêáiüøÇWGCÿvÖH`¢IÈ˃'m»6lí¬°qSG3©T¤uÓ–ûz¦y~ã¬SyeùÔÛb­®ó‡iðï«©ƒžzä©ãA×ÍêÓÑF‹•“tûèž-qŒÖÓ;˜rŒFŽÞÚnÏ”NÓ ìl©žöðÚ¦¡×]»-Å#á¿€ Póþþûïõë×9rÄÄÄdéÒ¥µ]ŽÜóàõß—¼áê8´í³xâ(7+­’YšÍNNy3a×Z‰kϪkßù§f׸ðêâÒã³þ7:sïõï½ÇòÚ¶bøA§M·7)nü×"Í\!õ\;˜²ÉmV\.Q3i7 x«ï@ù½ç”ìë¯eNÜ`-˜{pßô¹ò[m¨8´ðZ¼x¬üŽxjm¿¿ö—Éüyƒ¼§ÿ ÿ¤qÛ~Þ§—}ÓÛS:à¿€ PcÄbqXX˜,:߸q£hI‡j·$ÙÏz+ßôouZ2„m¿ ½÷MÙ…ýâú¿¶Û~¿ëö’5VŠ“IwnM*|ÑíJÏ*ï‹pš÷[¦ßúªJm5pÓù›àã@€¨Û¶m[¾|yTTTé…Ÿ@€€† P3:vìPn¡‹‹K­4@ÍhÔ¨ÑÍ›7e1úñãÇEKôôôìíík·*¨qÐ5&999<<œÉdJ$ÙÛöíÛSìð¥A€¨ŽŽŽ²Ož< ›9s&æo|‘ j€D"±µµ-((8}ú´¥¥å´iÓìííëׯ_Ûu@ÍC€¨:tHLLœ;wnïÞ½‹–ôéÓ§vK€àCùøøÜ¼y³[·nïýÌ”ó÷î=©Ùªà÷ìYtm—ï àƒ:thãÆ&&&.\xÍ­¬¬nß¾ýÓO¿Ëþ«ñÚàÓgccSÛ%€Ê  ZØlvm—Ÿ%©Túe߆âÁƒîîî'""âýFºwïÞ®]»fddÔxmðé£Fªí*@eРD·nÝ.]ºTtO.UÉ2¥³³³¦¦fmòQÈR¯“““lŒ?æñxï׉,|÷Ýw5[|TРą ÒÓÓk» øŒiii1ŒÚ®¢æÉ~«´²²‰DgΜ±°°¨ír࿃ JP% k» €ON›6mRRRüýý{õêUÛµÀ @eÞÞÞ·nÝêѣǢE‹j»ø¯!@¨fß¾}[·n533;{ölm×µ@<=z4—Ë}üøñ—}ƒ¨ 4@uedd4oÞüo»Ÿ;h€j‘H$–––b±øüùóæææµ]Ôh€jiÕªUZZÚ’%K¾úê«Ú®j4€rãÇ¿sçNß¾}çÍ›WÛµ@-C€PâÇ ¶´´}:jÔ¨‰'Öv-ðC€€:! àøñãMš4Ù³gOmן7h¨îÞ½kddôÏ?ÿÔv!ðÙC€€:aÿþýµ]|! T€  h @¨@Ð*@€P4€  ê&©ø¦×£I»$í~tX7ZYf¸ïÙ QÜomŽìh1Þ.-ø7fp³øXÂê}¡ñbײ[ˆò8=X÷€h¹[Û«¥Í(µJ\p¸åýÕé†ÃëÛrÞí"ónZȺ䋿d?—ÊÞS|vƒ6‚¯&ŽÀá1HEÒèäѦ¯^ ±=ªÉ«‘#ð‘HE§ÚÜ[üP{s¼UkÍOk_OR– Ž:}OBÚ7µ¶6úŽ'’æ¥÷ü^wëK¿hñ?ò¾þÐuƒå¼Ê¢ßé'§&GýÚ½ÁWÆTÉi\úªI¹õtW®~—ž ‘ÜßK˜Ú ñÅ5™Sº u*‹¹^mòt˜ëʤ*YY²ÉýÏ&Í}“MX ûëŒj¦®¥&M‰È¾š²õbJ˜»åÎÝ:†ìòQ|nýÔæìÏùýjÅ£a{tÿkh®ößï]z‘,=Ó-çZ mDZÓ'íxÖî0ßÛgZ6ÔŠÏùß!ø ]Áÿvë^ëòÃÿÒ[†uŠ®)KÅW§¾ºœÉú:Ä¤î»ÆÒ¤Ì}!bõ¾ÓyQsC.Ei6¯’͵[½I?16¶Ï}Óæ‚ªvK'…¾”§g[ýÕ¿˜v)ÕÉ”õÙ?ö‹ÜràåœÖA“ÕËý€¢´5¾Ù ñ¡c®]RñÃ3yâZÛ='!„?ÄW·«ŽüýÇ:žµ<Ì÷õ™– µ Óêa²`TÆÿö¾^{Fsñ×,!碗–GYÿ¯«Ô%f:áX¹L×ïµ]xYڡɇöæ÷ŸÏ)™˜§1~1,qñ2ƒ+5¸•î27÷ÀÌôlÂ|²Lz–a5”¿Œ¤Ãîm}ñ´–†HtkcLЖô»Ï$RYç6ü.ÞFßOÒÔ©â§³ò}UQ-7V~ôË[dÍÔý‡°kï ƒ¿=žœø”±F/ã'Û¶ÏØàŸ|3R–³™} üvµ©G)îY£ì"*fÑŠA'ŸõŸšxõ±¼³¾³v9õ_½Î‹U~¾º²“G[G=ëfyúÝ’ òv6}¸5^ø²A›¼*‡é,M­¤ìì¼ ‹cöÈŒˆ‘í†Ò²×ì5ËÄ{—Gþž9ú¡ï~ÆàßÍì\ôq¥_¬:#·ù&‡Íß«Wø |a ê2«í‹ÞgŸžÝç±E*sãØ”4}Ý kÊÎcÎÏûi]6m 7´“ÇÔïmš|p[Rø4Ó&å&ÏŠi­¾¦3¿Î˜»êåÞ '4«$þD¦]zM ûÚV2˃a(5¿Z…‹Â&»Æ9nºïÔ m!CaÏEÇAÁ0Kª{ï7™ÙÑ×ÜÍ„~}*!08Î׋ûÓ ¡ªnçŠÏ׺GõÝ»Dû_J¼-t3{{øDOÒNGñÍ´ •Rõ0V(["ºè>ç$ÝÔÇde/u^NþµÕÑ!ž‰šW¸± V‡ –.=9úmlï{¦M5e;JZ2'G½‡ù¢ HÏu4@ÝÆÐÓšºSç~)‹giÏbEˆgõþÙ¤mÙ š{+éX$1œ¦ï ¿^ËàÅ9èŸrèªqãž.³Ø®MO^x<&±û Ãêåw—ÿ2'ž£Ž\ÍÊ/.VWæŸé‘„Ùk½©{çÂŽº :tâî;&UѲ@XS]‰Ÿ%¯ÈÓØ hïÛ9ßN®|­¨û+Å\gÝY—äÿ›¸ç:­9Übùb-l·5[6#ÃãHÅïå±XfM9BÙá©Y5癫Ñã¸u; Ôº[lÞ¥«_8Æ©‹†Zøƒ51§Ùö7)? ¥ûRRm3ž©–läÏ^H¤ÑoÊõ/g{Ý0ée$߯Áøúî›Ó·üžö"G[ÈWÖs©©>‡)Cõÿ‚¸EX´)Wwnæ¥G;®¦?Ï:ÆW»sÅçKÊvôÕâý–ZÐϯh ýúpÊk¢6ê;¯¤ŒJ‡Y íT®ì¬¼ç4·™‡þòµ:…gÇىܵ|}e[V–›¶@vuSüÕ)qÁRƒKÙgÇGßãi/Û©k„dU'à4ÔyÂ>¦ Ffú?›Fˆö7ÖS{±Ê„[Zü÷š”™8¶èr eî®oïõÛšŒÔ¯Jý­¼Ó\wöª”Á>±K¶ ƒþWþjœèDþWwFé»xˆÒ ’Òhºä=ƒ¡kÆæ(LØ\ Žɾ²0þÒ—¦,6E8º^ª^QWtâδpBµý†Ï/¼mÎrÎ%7³.=vîD¥þ™•H(ç¡‚⸫f«ÝÅ#ÓsÕ‹Õ"µ_.3{ž0ï^_µ_Þà³… V‡5¦íŽ¿¸NiÍ Ð–›Õ“±ï””4Òi£-I+Êj<ÍÞ-ÉÚ‹ ^ ‡7¨˜(Óñæßÿø0p櫟úÛ¸™”Y§¦Í”ýäÉŠ“ÈçŸïáÅŠˆ‘« Jµâ/O²ûJáL ž‹IÀRÑìyq3šÅº£«Àe¨Þ7ž¶ê>¯º+:ý‰Ho º×®ÂV±ñRš02_Šeã0Ô/uÈØ¬úº„T'@?•mË21.=i›ÒnÀ¢Hnr´ì× fÙó@+Û—Òj•d;†€Uê—J^ÖÛ_j”‡êÄF†Í+ù¥€¢äãߧsE§žÏ:FíÄŠä“÷ §µb„§ž{NmÖ1-5¤êa–—ó8íÇ…‰/çD'J¥%KK·¥XŽ=õxçº|áhëé_³>ìÏ*ðA€yªP7’ÿYZݸüÝ3è×ûâïÈÒî£ØÑÆ±eWåÚ•ç¶´’YªD3l§ÑÏÍãÖNLíp¼Ì ŠÕm4MHæË_³“Å<ã·?ƒÆ£ÍWµ”féÓ5¯vüYŠYÍçÚœ™œÿïÙŒ?Îe\?´),)ÈÙhûã&UÞDÕ® ¹lY¼b8ZMp*(¾-“’%¯ŠÑ‹&i……•¡Þ6/·ya—•Reû¢Jª­^Y•Uª¬ç¡zçŠN=£Á›Ñg·fOl¥u 5–­ùÝÀr·9¯ñó$_§×·Xüa+,»9«kkP$;+ÀåÕ_e›IÒòžÄÈ_¤ÝÉIÈÓÒú¤oQ5ÈÉ9º9h¦ì¯×€Sjy~ÞQϨ«Á‰f™7«ìÙêÍ LO½:jõ ›Ö¥~Ö°-µû8Äl¾ð¦Î´oã¿‘ k£¢õR­cQÕ Ð…d¹¿ù0Ù“$’ûË#ÇúÇ­Óöd— bTa•–ŠŸIZVuºÒ[i' wùyn“öìJ/.jšÊ²™(!¹Tç¢çñÕª]ÛŽMHvtt鋬tZ¤X¶ªž9³Âî(eû¢t”Uû¾>^ÏïßyU§žÕ@g„KÌâãÉ–3®*àö6moøUѱGoåQíŽXù fi|v^~ÙVâ‚Óc_]%Z³BÔö½D°o¹“8êh¨Ú›«‰?Ç=oãAÊÝ–Ž¯3.áj@êÁßê;~ͪ,30ͳê«^ú^kjœAôޏ&‡ôaò‹åÐe’bndÆ¥ÛåeÑ’GÑ{ò'mÒ5)ºÎdÚöàëøçd§IË]¨eðY²„ÿìµ(&ÜÂBE/3.?#D[iW´`€°!ɺ¿9-z„ÙÛ+íÒg[¢DkŽš¯cÁ¥tù:$÷Ñá¬ÌþZES“sî¤\‘…Ú*®DRò; ÑE—‚u\„¶$ûApFÒPÝ¢¯©‘‚üËóˆšÀÕ±â5SåûÒj¯¸Zåǵ*ªö\z˜5ܹÒSÏdwôÕâÌÙND³ºìÑÔªv¢-]vÁYgL¡Añï6´4|sü=Ù ]ü £cv¼\qžtØg6p(Óê ¯Ï·ôj4­ã^’‡Ï4TE*ú}uZQ9‘W!#1¬½ ¬¢¯¬ÉHê­kPÙÖ”€ÿÝÝ‹=RÎ¥bþn¹ ›é–`‰·WZ@Ë{G{]ÚqôøTnB^Äï™WoHØœ¯wš¹T¸÷BÙ®Úœü«ÁÉ÷¢s==4MuI^lί+“™£ú”ÿ{=¥­Ñ¥¹s%nÛ!Îèά‚ÈŒ½~)”P6ºÂ9 »R³Ðõ›™4feÔø¢ ^šõ9’g§·oÎb L,Lo'ýáŽI[B^øÕÒ™-~–ylÝ›z$éM…¹2,¦,µ‡§†ìä8›qœ{êMóIšøjÒXñ¸<¡¸àî®Øíˆý’ú+»hªt_LeÕ¾7Õz.7Ì^\ÅïV­ójœzín®zé?-L%ÆCÛT{úFÙ²wÓ.KþÅ/¶õ a}IþÝý Çb_[äý‘vá¿{;ž~l’¿Ïfo‹9ÃÕd‘¹Ù‹!'"CFDw¸oÞF§ºû„Ï4TAü&+©‘öðµVù^–Jh¥]1›.·ßk³e]ò ·x !< Nˬ½§iÝxލsGŸµOŒÙðzö:·ÓlÕpÓ#ßsRqÅ­¦þõ2½óÉǼŸŸëdvà+ýkíƒ-£·lŒ›»GV ¥ÓX0òGë \N…M«µ/JYµïM¥žËSI€V±ìjœzM¡£Õ~ZS`2F϶ú“’Ë•}ÞdM€dùòÿn „¯ÞÊÓhí&nhÁŸ^éû¼Ì¾jÁ÷‰úWM°p›ŽAa”¢´4½÷è_éš4ÏG+t¶ÎpøÔ!@‘?Dpv|‹Ùe—±¬ë¤ëU¹ S­ßU§~oߨ»ßoá^± ƒÕñã­C•l͵ѵ]kTµ d˜èí£KݘƒÁ´c8Æ´:ÛªYéL¿¬3½ô¢N·ªÙƒÙÈËl“—YU3ãO4_UçePúnæ‡ÝJ]'¬æS,‚§(@õ÷¥¨ZfÇ_[”4.sÖêÌØâ×·(÷éÕìbv†®Þೇ _ñ‹Ôõ xM{Zb62|$Ðð% “ÿzsï~ÖÏ ãþ&š³V—¿£9@ÍA€€/-¹å÷dÞïD£‘pês7+\~†¾«çÕ=k» ¨ T€  h @@M‘ŠNµ¹·ø¡öæx«ÖšJÚÒ)i^zÏïu·¾ô‹–’m|¹¤ÑÉ£M_½b{.T³úOüøpOR– Ž:}OBÚ›¢­Ú(YŸrmPw @À@*~x&O¬¼Ý—®ìq ´5¾Ù ñŸAgÇIáñÕíªSÙúOùd}ʵA‚ P‡ÑÙyÇì9#•Å9-{Í^³L¼Gqy‘ƧŒ5z?Ù6°}Æÿ䛑²ÈÅ´èkà·Ã¨M½·wØ•&gퟵÿXNª˜hÚk [m¤_Ù^¤q)ß¿|(3H=†ÝÛúâi­¢ ƒN>ë?5ñêcyÿf} fí0r.ê_"~»50íÖc±„P{Í>³L¾óàjTþt :åׄ ’¯ÞÈÏ’ …˶uÕñ\eÜÝžA)ëêíH'Ù4K ˜Ÿr?Ž&Zœ.s,æ{3oν9øML6áZ ¾ÙféåÊb(µDh,ûAi©ë5&þrÀ›Ï¥­¬4Aq=•·m§²ÇAýnâ§Ék¶ð‡XoÜ®¥%ßN»­x`¯´ü¯¦s PÚË QDÒºjÝ-6ïÒÕ/,Ò©‹†Zøƒ51§ÙöoÆ3•_rf˜4ãÙ •Ÿ,:¥&Ï”’ÂϤ â)+¯:§ ¦!@ÔY| 'í&”ZÀÔU7摈QŽ„¼½.ÙL§Uñ„ ¢Æ2Ó'$Rœ%‘½¡SÿÌJ$”óP AÉz[í.æq{“T©¡…žkƒ’íÙVF„¼eKèÔ+iá„jû Ÿ_ -x»šå<œKnf]z íÜ©üµE®G‹d_YiƒKS›"]/y‰;«×•ƒ¶Cqšdë©ñ átãë½ý)IišË^¤½QrÐÌâ¥U·ª/Ц^KF¨Ö#5´Š·ô´ü•¶T6ÀŠè´ké/dAv¬–^I=lµö#¸knç\þWÚߤÊ©™3¥ìcSÍÃ[BÕö5 ËyœöãÂÄ‹—s¢¥Ò’¥ô» ‹ó.Qòù¶tÑj:ó¥XW õK%$6«¾.!ªh†Í+I9ÅdíN"’ý߃°Il¼”&å'þò\L–ŠfÏ‹›Ñ,ŽÔ].Cõ¸ñ´ÙÊ»z[ Ÿ©VÒ)C~Q—#d”ŒM>-ƒÐÒ¡+=hDÑq«JQegµXIWiOe§†eb\ú(QÚ XÉMŽ–HÉûäÊ:SÊ?6Õ9¼¥©Ú & @ÔUâçI¾N¯o±øÃVXvsV×Ö HvV€Ë«¿ª·y%&i%-UG1زÜÅp´šàT.PR|[f%_c°šÏµ939ÿß³œË¸~:iSXR³Ñö †\e]©”µ>ð )P˜²‰´ªXå›T˜©Pt|Ê‹.Loå{uU…k#ž²áõìu„o§5"تá¦G¾ç¤âŠ ZMýëezç=’y??×ÉìÀWJ4¡˜M—Û﵊ٲ.y…[¼DV­…F§eÖÞÓ´ô+ù eìeµ›»y}êú‘ ò{A¨³t6˜»×¸¿-%Ëxªt¥„⃖ý—ýD3…Û—=ûv—š$Ì`:o°ßj½qCÂòcRÙ[‹õ–¯5în&o£p€•½kíƒ-£·lŒ›»G" ¦:#´žàÁåTge‹Ü¢0+¨v¦”|l”^ïæe?Hç•¶ÇÍ ác@€¨³̦Ó„L+»p¬Õ™±Å¯érwñev½ÒâVé÷F‚ñ'/ݤ‡Ó-R)JßÍü°›ù»ºÂt Eý3˜¼Ì6y)¤ïÛ1 cZÕZ]Q*á´6;O—nLNlxkâÛ7JQ<®òÇa]ê9(lv«–{gT>E¬ˆÉj>Å"xJåë:þÚ¢ŠÓTy‘5y¦”|l”}& )W›‰²ö4€  T€  h @¨@Ð*@€¨«¤ÑÉ£M_½b{.T³ŠÇoW’æ¥÷ü^wëK¿hUú„”Û—TtªÍ½Åµ7Ç[µÖüŽ*ë;.å[ã—OØ\8.øÀ_4h€ºŠâs{øè§6g¿×úÕŠGÃöèþ×мÄþ°}½7ÕŠ¨h€ºŠÒÖøfƒÆ{n,?<“'þoöõÞT, z êªÒÓ*8ñ)c^ÆO¶ lŸ±Á?ùf¤„¦E_¿FmêQå7,œêðPþ2fz »·õ…½ò7ƒN>ë?5ñêcùæf} fí0r.ܼì:åׄ ’¯ÞÈÏ’ÂeÛºêx®2înÏ(¿'Ù†ÉYû'Eí?–“*&šöZÃVé—m@gç]X³ç@fDŒ¬/JË^³×,ïQ\ÙˆÊyñ´¯ŠÆ¼R;¦héë¨u³“ÿy)%êl‡¡†Ó74*ÚWñæ Ç%?ŽÝ˜vë±XB(½fŸY&ßyp55q.à?… ²0Èf0 I95óŽæ¸Ý ýÍèè1Ó'ÇM÷â:¡-,òúZKnXlêõò’@U¨ž©:dÊ–ÓOâý&3;úš»™Ð¯O%Çùzq:!,·¹(ç$ÝÔÇde/u^NþµÕÑ!ž‰šWô+_$WAc7VIù·c§=Pë3ÇÂÃP>Š;¢¾Kb†ý¬k@”l®h\º’gEŒ ȳp¯¿x=__ZpgWìVÏÇ÷ãÍäbv ÀçŠIãÙ^7LzÉ/˜Œ¯ï¾9}Ëïi/r´…å¾Èb™5åÕ á©Y5癫:¥póÄ-Âz¤MáõØîÜÌKv\Mž#lQvóÌ?Ó# ³×zS÷Î…©µ› C'î¾cRu-Kò¥[æÿ›¸ç:­9Übùb-lMGÍ–ÍÈ0Ç8Rò¿Ü‚ç4·™‡þòµ:Lùg'r×òõ•mYYnÚåŠ$Yy  Š»¤£Èð+ÛÂJzó5Â,9—pî™Î(cEû(—øYòê€< ‚ö u W:¹òµ¢î¯Xs}œugÝ?uð_B€€ÍtZ•LØPc™é)Î’T{óz® J6g[òB”]as®G‹d_YiƒKS›"]/‡ŠÝÑ©f%Êy¨† ¤W[í.æq{“Š›ð5&œ´›Pj¦®º1D¤ˆr$DP¶;%™ÅKëv)“ÝÂC®åýùH:ÊVÉæU‹NÜ™N¨¶ßðùÒ‚·[³œ‡sÉͬK¤;aÀçŠ1,λ,GɧîÒ´ › Ù¼’JQLYW•mÍs1 X*š=/nF³8"Pwt¸ ÕàÆÓf—kHg¾Ëbª¡~©|ÉfÕ×%$éÝ‚œÇi?.L¼x9':Q*}·iåV§1³>GûÝGJ`Á¢'‘fžÂÍ«þD$kwcнvJŠ—Ò¤’Éßð C€€ÿƒÕ|®Í™ÉùÿžÍøã\ÆõÓI›Â’‚œ¶_0nRö¢q%é&’wÑ•ˆŸ'ù:½¾Åâ[aÙÍY][ƒ"ÙY.¯þªl·ÕlÌ`–™GB½aTcó*ÇeÈeËza8ZMp*w±™âÛ2‘ž>7ÐPõæÃ dÿM’Hî/ë·>L?Ø“]*`Rš¦LBD É¥rtèy|É:öHâ­<ªÝ+¿Á¬¢*ÏÎ˯t‡Õm,Š/È’·—ÒåWÁiBé3â«·yeãÒ[i'KÐùyn“ölL×øü!@À{‘_š•ÒRå Ë¢%£÷>äOÚ¤kRtû &Ó¶_Ç?';MZöŠ3¥ëÌ×!¹geö×*šs'åŠ,@‰°àl ¦Ð x - ßOöBBõUºH¥ßú7õh=7óÂVñíãy„pÛ90 þT¸¹¢qÑ‚†$ëþæ´èfooº!}¶%ê@´æ¨ù:\U"Ô.hx,¦6!á©!;9ÎfœVÎÕÞbhsò¯'ß‹ÎõôÐ4Õ%y±9¿®LLfjŒê£Æ,Û–ã¤?Ü1iKÈ ?£úC:³ÅÏ2­{SÏ€$½)šxLw×.KþÅ/¶õ a}IþÝý Çb_[äý‘vá¿{Ë2E6q;ž¡´pÎH=ñî~/ßÌÒu¨'}}2~ÍUš?Èè+ Gñ¾ÚqŒKÍB×ofÒ˜•Qãˆ&xiÖçHžNܾ9‹1H07±øü @À{PSÿz™ÞyäcÞÏÏu2ÛRý'”±—ÕnvìæõÿoïîAã,à8Ž_ïš—ÓjmõŽ4äiŒ­Z}A‘„€ƒ]”¶©ØÑ !n‡¾¨ êTQª““/££âì&¨â…#éBŽ{ñÉ…ÖÆjr9|¼ôó“Ü=<¿g ßðdåSåÍGRôí?útñ«ƒßöÇÀ}ù—¿«½öÛµw»œ9pìàKŽ>²ðãë_7j­‚¾ë‰¡‹êóóå7Ÿ-gô=þÊ‘K ‡óŸW¿›¾~åùŸ×¾?þâ-#?ùf—7¿z¨QËd¿0|áÉëç~ºòK#Ó×;~vdîÒ}›ÏžÛíZçwº¯Üøüñ«£‹ï_®¼=õ{=YþàÝO½õÐù™ƒ…Ü_oøßÐwªìÐ7oüç’û}Ô<¹ýõÜ3ßžüáÏÞW˜ùljäæç;Ÿ¾íZÙܱӥ÷N—Ú™;rï¹/Nœ»õKÏ=öçªln|æèµ™íçœýòÌãG·ÚùÍ™â§ÍbëàžNÝ6o×ke2;ÝW6wbzxazøï_º‰€€ 4hÐ   @@@€€€ 4hÐ   @@@€€€ 4hÐ   @@@€€€ 4hÐ   @@@€€€ 4hÐ   @@@€€€ 4hÐ   @@@€€€ 4hÐ   @@@€€€ 4hÐ   `¯ôââb¡PH{@'­¯¯'óù|ÚC:iuu5í ÿÒ^ èF£Q©TÒ^ÐykkkiO`SwôÆÆFÚèŒjµšö„¶tw@///ïki¶¤=€°›9·²²’ö–¶tw@—J¥­tÎf³ %õÜh4’ƒ´·´¥»:—Ëío©V«½½½IF§½ ó¶~§™|—K{@ç%é\«ÕúûûëõzOOOÚsÚÒÝ=99¹õóÊÒÒR±XLz:íEW©TšÍ¦G {RÒÍårypp09ž˜˜H{N[º; ÇÆÆfggÓ^À¤»þc4hÐ   @@@€€€ 4hÐ   @@@€€€ 4hÐ   àr!ÙÃ×IEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/yang-to-internal-data.png0000664000175000017500000012176314770023131025230 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+£¥IDATxœìÝX[ÇñY$EEA1°PÄn,ÄVôU¯qíîîîÅŽk×µ‹k ‚(Òû.¥¨Ô ²€ßϳîìÎîþg˜ÝýÍÙ3g”¤R© m”ä] Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4 !{è7nxzzÊ®¼|ù2_¾|ÊÊÊò®"DFF¾yóÆÈÈHvÝÙÙÙÖÖVÞ¥.{èC‡͘1CvE"‘H¥Ry—Ѿ9Ùtæ!@dSÙ.ÈeïýìÙ3Ù—wH?YzŽKt~~~ò®%M²w€666ŽÛ_É^{-øêk300o%i”½´¢¢¢¼K@ÆPPPw i’½ôÏ”””Š)"ï*ЧOŸFDDÈ»ŠôÈiÚÐÐðþýûò®©(R¤ˆ,CË»ŠôÈiø­Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4à|¤™AÝÝ_b¯ë´ý××ÃASÎÈÊÐ@Ž% }{ÿÆõ»Ÿ>õ.èó—H m=½Ü¹õ MK•-],Ÿº‚¼  ["@#[ °ÐѲ߹ˆøI«q׼ƗÑHóÃ¥A§ûÙT_ô,~R³ÑŸ]íS||:ÓÝ´Úª7_§s9íy¶³II_1âÁÌÒŇÝM|“qßs> +§ÞºzcX‰23ãkUw:üzW­4½fTàýkWº{îØwùeD²sIr›Ut¨ß̹]»¦ŒÔÒºù¾}×J£"ÂB?½óõìáÝ[ÞO>H¿›ýÝ©¹m,wí[³e B4e ‘ )ê¸zùŽ’ÄÅ¿ÇsÛvº1ß^'Ùo›è€cC:¬z•0©ßÞ}aƒTû+D<Þ²è|—›NeGõó'üc¯û¬\{gàÌ2¿’5ÿ›1dûÿ´/&¤ŸoÌj\ï»ôœ¯Öˆ% †7/©ý}OEuÝü&²K‰ÒUw%D¼½²eÞ¤IsöÞÏ€2$Á¨Nÿ ÉwŽ ~zéàV÷e Wž|ùíÖ']íÞ†\Üݧ¤z¦Ô@44²%¥‚mW®ØnÑnopìäÓ…mG:ÝZ\C7Éýþð Žkü& :¯Ÿß0oê}˜Ã|Ö-»•0Q¨Ûœyÿ³z;vêÙºeWÆ®¬*z˜+£*&¯Î=½zdØÔ M—TMÛq)~<7ºùˆ‹ßŽÌU}êÑ]Ã*饾„Êy˵Ÿº§íˆÛ·ûßÊÿ~µb)j™TrZÉyÀ¤³‹zµ´ãyÂÁGú6jyuA4¼“d>4²)Å­—¯ÞqªõΠØIߥí‡5»½¬ÖÏ_6Ñïèäþ:aÒ°Û†9uó¤á+)äúʵ&Šwíb[R³W…¡½.ÅN¿Ù²ðÌŒ*õõÄýÂ*)Þ}^ƒ‡ÍVÇójÙÀ5Îõ7ûµƒ ¿xÏtÿäë¤Béq'÷ /'¦ÿ¤‚–UÇ—k8-Ýk˜¥?"ŸoèÖïl ×M+`Øä,ÊùªÜz½ÜØÚŽS®EÅßödqç‰mï̫ĀÔ@”¥¿0”(¶XºÖùß[?ÄN¾ZÙaP³Û«ë}Ž£ßîïßyÃÛ„IãžgÕÊ– ó‹7ù%LØôh[\EIÑ©ÃßOÆö”ø¸wÁÑ·uó‰kRе;­Æz×c»LH/O}¸ý¦FúéoaŠöß=l΃oÏ_~¦çHQé9JÁúý{§»ŒL!x{u÷Êu»WŽP,XµmW×N­ªÕÌÖ­sáïîž?uÁûÁË÷Ÿ£”µò.Q¦rµ fº™ó±âw÷ò¥ë>|ß~ŽPPÓÎ[Ð̲lE;KCõtïHÃßú\8wåöÃoƒ¾HÕó˜Øµn_Ó(Õ]ÄŒXQŸ^Ý¿}ûù*UÖÐÉW ˆ¹M¹òV5³Éð2 ¹«MØçyß²Õ¶ñ·<_8xSÿSÝ §¼*ä¾ìr/4²1E§EîmO4Ýô.vÒß­ã€æwÜ}ëžõfïß=Þ%Lîë1=™n?¾;±`W—˜¤RïVEbB@þú}몟Ü{²²°# ö¼lñ¿‚"¿” ºÌ4ÅvڣةÀ-ç¯59ÝGîE<ñœu$ôë¤ay=Ìÿ€±d£^œÝ0QvqÕ/Û¢³«kg—:–¹³îGYä“e‹ö¿7a>Ùçæ¨*Òû;§ 1çŸ?£ªeÓfìÜ9ý Сׇ˜ÛÎ~þýþK+æZšÄ«éí3ÅZ5©‚ÂýÏ­›9}þš}w?&u·~Y—¾£Æt2OvG,©%ŠzïµfìˆIËÿõN4§BMÓæ5›è$û¨t­‡Ä"Þß9ºÕÃsç¾ÃÿÞz•ô<êfµ]ŒÕ­ša6xo(6›· ö?Æ¿­£ÏÏq¿×iœeÉ__ö_Ü¢rÜÊDɺß:@(äk´`]§c ×ÅÕüvCç~-înhš/6ÖF½ÞÓ×Õ3 aÞ¢<&§p abQ~û ‰ŸPvìÓ8î`?IǾMtöo‰í5"=¿hËã΃‹‰í¡n3pn«¥M·Åu=y0{È–ž‡:§«•&Òwÿºëß&‹÷ìS>ÿØ+‘(þ¸–Þ]Ý1[vé­eÑ ƒ««kûF¶ùU³~ߎ°Ç›{Öiëþ(é{ƒ½7©yàè¼Ó;ûYçÊè…‘{¯ú˹—ÇýdâNŒwW=Ç5÷\ÚpÖî+ꦥ…?:ÐkF˺ÃýüzÑÒ$æ÷ËëAún[½üÎ'RX–X_]ÒûèŠe=6ý³ •I’ûY‰bæã: 9º"¡×ÙýÍ»°üñ D¹/»Ü älNA¿ÞÜ ]×?HðýæÎ}Zøln‘_1ÊWŸ®[Z‘³!*k§-D>Û¾ðT˜¹ê÷­k#$ºUzµÌ»eM\—[ËÖùôœt+_Šן2¶ì¶AWc§ÂŽ˜|®Ù²ji,-±è÷ç·{›,ÚÊÉ4'·ò(í{æ¡õöõînë6ŸyöÝ)ƒ},"»¨˜8´“éŽ-*ÖÈ¢};"_nsmß~Ó×CZ ÝÜjŸ·ÜP¯ŸÅí•u¿ïnôÓDòÞž¢ÞZ³ÞÜ[Ñßݪ¤gdœWG-òãëg/?|‹C¯÷©Rýý©SSª¦–¡Ãî-mÑfø‰/‰o“½xlr–&›Ÿi=$ˆÿò}€“hæ5Ò×ÑÖTBƒÞ¼xñ>Q»väíΕBv^wK}øJyËU¦sóü+–%¬›{{νnñã>vÆ,û/lQ9tåiF€F¶§»Ö¬=ÕZ7 Tà¶®½ZTÚl¦W·í ó˜ß4Î.å‘¢¿ ¿¿q鵄 ݦ}¸[³|vÆkæûÆN<^»òúÈÅEŸ´KÙìózή¶<®µßŠ«]$º)[øòàøÝoE§bÍ¢9¼GAË´F— 5ºŒ_ém¿‡»›ûÆ}·Ïþô¤ÛXÙ¥c~;—ή]:·®i®“µ¾°Ÿ¯ëØñIL2R.Þ|ÔøV,ªS¡4üÍ­+Æü=vo©~üÖôžÝçöÔDÝ{ÔÊL,.¤ó¼q_nÍiÒ0QzÎW½×èa½\KæMh´þüüÂÖã†Ï=÷{NôÍé»ÙÞÝÒÊ0¥uøÔ­ë ߨôlàÐgÔÀ.N6µcºÅúz?p§XÒût¿²~ T¨š³³Sㆵ«ÚšÐVNô& zt~÷ÚÙ¦ýó$î}â¿¡Ó_+mo•ÕSœ†Ec;õe{öIîŸxð¥›qR?.ýâ²ÿÚ•Ù9€‚žãô½Ôˆ?Ï`Ю®-;ÛÜÜõõå’c6)ŸæŸÃ¿Ü\½ê^„s¯Ê:‰ïU·víl:rÜïÎ~›ŸŸR±–Ž ’D«òèéµÝ;Åwt¼6iäŽ[š¦ah½Ä¢?>¼óþÛ¤IÅ"ÊIP$ªùË6$»Ìÿxÿß­ëÝÝ×yžóý®5ÌßËsºìÒC·T“Ž®®®íêÛäÍó_žÄŒFnÒiÓñmïïHTòY;ÙYÙ¶‡m£5ñã?Z9ÿâw‡_è0FèÍ™m†}ë0ŸÓ’Ãëz–þa”p…\…ªt™s¸‰Ó¤†µ&xÅ縣÷è“uV×L¡çS˜oÌF¨[oÁ¿[û–N´ª¨ilÛ´»m2Êõ {M;ó¨\墹’~ç(é˜Vë4ÅÞ¹£[»*]wžW‚w\r·É¤RY|WSìraÏÝø© ‡ƒ¢k|¼¬Ü—]îòF€FŽ Ñ­>Í£ßj žÆN~¾àyþë}¥&xŒ°M{+q°×Ò ¾ …Ú÷(›ëûûUKtèa59~@軜xW³™¾èþŠœg ™ZzR\RÚ>hî:ÓlE4!âíÃwߦ”óÑûãÞΊÚÅku›\«Û¤Õ¯.ÿã±ÎÍ}ÃÁ»Á‰g¼µwáÙEݬVû.®®›ÙË}<ÕêK­lS4©@¯˜¯þôÅN[šíŽ;CÐû£;|¾8TøõSiD½Ú6xÚø …rÓoêe•ܳ*æ±³ký s—=±kòíúI»':tJùŒ?¦ƒöoù.=§E¬õ"Õ«¦ú:uó.kò«K»å¼•D÷áÏÌõ ѳïÑ,ï†Õ±Ç.¼8|òå4[Ó_~ýwSÒÑ—íóŠŸúø%µƒõ’%÷e—{ÀoB€FŽ!Ѫ8~Ó°ìfÜÿz“B¹éþ“ýúЂ} _[qÃ?ÿ<“RÁDBGžZ¸ýY»¿‹Š+)ä©3y|…­ý.Ŷ3…ÿ;râ™+k¤m˜ÒˆÐDGÒ)©*¥öÈÐÿÜÆL>‘°‚¹Zë®Ù1@¥¤[¢niu{L]ëëµg£»»ûÆÃ÷>'º?Ê÷œÇdÙ¥kž2Í:uý«¯k-“ôzœ.æ]Ú[¤¸Aj–t,&l‹ïƒÿú¿×¯èÈ6~=¥¦i§.Ö©ÿ£jÖ¸ÑäE1Ý,„(ïƒ>!íó';¾‹^Ó^‰H£L^*†–ù!îàß'矄ÌâNAI-Q¡‘)½mS!÷e—{ÀoA€F"Ѭ0d²ý çø£a„\NSz–Óá.òÅîÇž-aøçŸ($Z¸¶tãýžcK¦£­²©ë¼^3«,ŽëëùzõÀƒ½†¦y g‰râïØÈ°_ùŽÍi$êÆ]†Ë.‹îñŒi’ÞvùuâÞ_ß5·Ï]Ç[c~ì÷Ò-WÅ8åTÔ)˜OöwÛ7úüîSº›¿úxó@BoZAËÖ!Mc‰©•2„Ø-Ý»ÿ!ªFr?p(Ø4*%~ðÄ ^‘wŽîرÿøÙËÞ>Ÿù¤Ø`ûùå«ÏÑ‚¼~H›èˆ/‰öޕՕ“¯VîË.÷ù @#gQRIô=¯ ¬$êƒ:üÑæE—&¾ ÿü³ï„î­Z}sÈÜré骪i7bf½5íÅEñ“‡ïë¼£yOo¨¨‘;QSb†D­t‹|uÜ}«÷ÇèäçP6ªÕ¹µul7YQ3ÿ"¥Ü– zÍlÐkÆç»Æwê4û§Ôó;å)ªŸZ뛂š¶ZBpŒ úåÝ¢°W× cÁÛj‰01Èïc¤l{KúNËÂé®:ÃÖCtÐu#{]zþ]Ò÷'åKÐÙÆ—¥3\DлD¿œhèª'U­Ü—]îòD€„Þu[ñµ©.ñðÏ?û~@hß K½&¬M×p ŠF-f Ÿ|hœOìTðîÁ³¯Õ›Y.M‡<*ç5Ó„ÇñSþO>D :)½£ušÿé¼Q/–W(ô×µ$ç!ìцaÖ¤4‹ÍÂÆ-­µ”ÄÎü«¢?=>³cƒ››ûæSOÃSŸýwKCOA’hŽ øU!2àéûÔçJIÄ—ˆäëÐÌ›žs5gÌz¶©kµvŸ'ygò¢"R:¿KVñîáëD%ê›þ¼»!÷e—{€œ xŸ¯.wúuJ#ìÐÔ!gRø’{ÿ­ÉùݶE§fWo”;=M¦ª–}çµ_Xoc\Èy2Gïãÿ+œ†w¦‚¶™eáRB8zæõ4´¯I>¡8áonܼÎÝ}ÃîIÆG=›¦®ýø>£Ã‚:OvRP’WŸùÏW&ÔOà”‹8¶mÝȱr™’¦…ŒòåÑÉ¥¦¢œpöȧ‹íŠ ¸)§JE yxþé·)Ýâ¦:?ìËË}Ùå^ wÀ×ÒÀ³‹<u“}{xùÜÃi~ô§} ¿nÐ&…¡ ’'Ñ«9qbåͽÏÇvÁˆ8=j©Vkjê¦ÆÕÍ-$ngãÛs/{æð§±×àìî¾nóÉ'IÅF‰aE—.]];;×(¦–C¢¨úísÞbú=ïaÅsÂ1\‘Ï6ö›á“0¥]{öÑ-ý+è%÷7• ‘rìá$VÈݽ^‰ÎìX±Ø÷ýÃä¾ìr/ÈÐ@Œè·Çì J}¾ä„[°ûE«žii9þ™R‘NsÿžQq^\{Î[·ˆ\a‘êÃô+·´Î&œÍûñöÝÆ–JÏÁŒ¿.—½û{©ûï˜9ÂßzÚìîæ¾a÷õ$œU‹:¶ëâÚµSóJ3yØ ¹SÒ- #ã&ün½ rB€Ž|±gùù„X¦ë¼qÓ€ z)ì¾Fú&oóéšÛ®Dûò%ª|Ž%¹/»Ü ²4 ÄœibßüÃ)Œœ^‹7?ê:Ü<}Ù$Wù¡³­l½/îÀ¡[S‡íqÝÝ8ÕG)lÔ¹Lÿ×ã'ï-_zeÀâÊR/ŽèOOÎîÜàîî¾éß$œmˆ]»º¶oX&_Ö8aæS1²)$ñgƼ|öE¸CæŽ<ò[|ö9òµT§~ýü‰úïÊ«ß_T†ˆz¹kâ†7ߦ-Ú61ùþSEîË.÷€¬€ BÄ“­ Ï}¢Ø¸Û·Æ[¦¥/D´ßÆš…:Ä -ÜYáv§ÿôÒé;Ë–Bþ¦3FZíw‚ÃOÿ ™yű}ªR.ÒzpíáíN’ñjùÀÕýN÷/–ýãQjbœcz8ﺞôùʵêäêÚ¥M- Ýø!—èð:it £™ÄRÈ]¶N1á܃¸©û›v=jQ"»o"QÁ¾þ_÷˜ J¦ün•~¸¸åZö8t-ÊÏ þG¿íË+UÜé‡ÁèDz‹Ú¢rìÊDÉß-€Xaÿ­_êýuÊ¢‹K±4ö$V0¨ó—£êÉ#qß&OÝ—_³¼J®T” U‹^ó:ϯí7°Ç³…×W­z†Í¦0;:ýaÜd”×à63«ŸU&‹eÑŸŸžÝ±>¦‡ó¿“ü½@©Pµ¶]\];µ´/’+玔¥¨ª&ûäŽÛm ý–ZÞQ1mÞÆlÜÄømÄgÞô“¹ÕÑËA[HèÇÐ×AäÓ-3|IiŽ,":ðü¤&.[ PStÀìÖÆ)uÖϘe¹Ee|@6D€Bn¬ZóíàÖÝZ%†á)ä­ùW#ÿÄSûµç¢³Ó+×MÃñI‘è:ŒŸlïÑãLìз‘çÆŒûœ†ÃÜÔË _ÓwsõEÏâ&£®Ž©ÞTõØöAtsZ€Œz±¦¦Y·SIŽE—˼^×.®š–3TÍAÉ0Êz…ôÎìôèÉÇhÍÿت%\‡VÚýl\@z·®ó 7V5Η¢TÔ2ŽiøŒÛs}~ìÌ«È2É 4Ò׳ßð‹b2¡\D½÷ZÖ§m_Ïljn3à>ªÜO;ä¿aÙÅmQ9oåéA€Æïã…%ߺè•ý_3ý˜%¹«÷¬¯õÏŽàØ© = ޽­Ý2gBù‰RáösúM«0ûiìÔç[7ÒTŽýÔ“.Ú¹—„àãCíJž³dþ¦æ©œñ+òýƒ'Ää*/ÑáAïLÏ1gäîâÚ¥m]«<9àÀ¸´R-\ÁDXw„«î|;t+”âG¹Ráv ‡Ï³ßqÕÏ­©ƒêÖýóZI±¿QTÐ=‹f¸õÜ<«r:Vù}r•¬o%ì»7q}üð-79ý¼"ýöõ«ÙáŸàL®N„è—×ls_¾`éá§ßMÝËöMµ×Ib0ã—]ä•cV>ð+ÐøÃIßÿ»pç×ßL%{4M9‹üH¢WµgÝq‡™‡ž¿ïU3×tM‰FÙ!³V´Ü-ê;G¢i;bÿþ€šõçÝJøö;4©y‰)ÆU]Ú4¯_£’­e1£Ü1§®F|þðÚ÷ñ½[WÏŸ8°sËÞ)žÏ$«R(P¹Mg×.Ì´²s;j:)Toi%\Žë/yú•ÜèëâhS$¯¦ŠB|ÞRÈUÈÆ¦ Æ×ô¥QfÔö9§l‰k6”ú,oUô@½#ûwméX2ñ·4<àÉ­+çíÙ²ÉsßMÙÖ¡ÕºsVìÀªT°IŸý»üw¦ÂÛZÛ~¾·z^ÿÅö£>=9ã1cè—cßLú&êïžÊ§'߱œ£·÷GG†‡…½{ýêùû7¯ß{Ä*Å:­ß¿¬C±¤wp2~ÙEnQÙhå¿¶h¿ó|=i®¢}φFb™vÅîÍô=Üâf‹>·pÛ“ÌÒÛª¯ñ´Ñ6»‡y§>kbŠyëÌ>sÉÄÕ©ßN߯7FûžÝ4KvIÛS(•h7ßý¯,|¢D¢šßºfÅÖ®®›U4þÓ£û޲iÇqNSZ펛îåÑ%Ã.ù~–"#½}¦X'ê˯V²ÿÞŸÕw.!É0VÕÐÜÂXWñË»—ùl¹j-ØXwm½!"ßU$øôÒI§Ó>»ãÐÅKǶ0Oá8†Œ_v‘[TöYùÀïC€Æ-òÙ®…ñƒhÈ(;ô¬—_|‹¦f¹ÿµÊï¶Ì?nÊ{éú{½&Z¥û„&*æ=æu›ã¸úMê³~GA§ìßÛ}jmÔoàÌc¢FR.Z¯ï˜I#Û—Ë“¥?”Šö>êÝ[ÞUd Šù›¯ùgì»FÏŠø­BA·òØcw¬Çvê:ëLâ"?ø>üà›Üƒ ΪgœQ³²o×óM—ßûv[˜ß½~ßϦßpþ‘Í=õÜ×fnu¢)ªÒªSÏ~½+¤¾›áË.v‹Êa+H‡,ý… üfá<_ù:¥Vë¯Zé꾬QÚµñ²yñäáš•7†/´ÓHoQû±Ój¬ïúo’‡Ë¥üPÍ’­gm1ìÖ?kWºoÞ¾ÿš gS-P¶V£f.íÚ5¯j¢ñ'·çfK ¹«M8åÛæàºµžûN\ô¾÷äõ§´œíM­ˆÓÌSµ»Z5wÁ Ãÿ}Ln>õÂj×kÔ¢m‡UM²îx&J†M–^¹[}â€s?ýyñULëõ;mlûÒ: ‘O3¿º)¨éæ3ÈoTØÔÜÂʦ\%ûêÕÊÓó»U†/»Ø-*¯| C ‘³è4ýWšöŸ U,FûHGÿú«ª—›ûB:7©{”‹ ½#*òé” ºžsMw5J¹K5¼Hv‰þòúÞwЀh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" ÓãåË—nnnŸ>}’w!é×¾}{+++yW‘ý ÓãÔ©SÓ¦M ‘w!é§««K€Nt:©¨¨ þ@h@4 Náááò.r@€NêÕ«1‚aì@¶Ö¨Q#y—- Ó£@£G–w4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" 3ÃíÛ·÷ïß/ï*€_Ò°aC+++yW€ü 3þ}ûöìY[½º…¼ ÒéâÅÓ¦Íw!È:“88˜N›ÖPÞUé4}ú¡  ©¼« K @" Ѐh@4  ˆ@€D @" Ѐh@4  ˆ@€D @" Ѐh #Iÿ¼}•0)QVË«¯ª˜Žç ûü: RúõyT4òåQVȘÀ/!@)ìá…þ=Μ»íÿ<0:fÚ¼©ÏÍ%TD?ϧkÇ»¸vÍÇÿåÇØ]¶ý /{ãt$qÑÐYMØÉNcj¬JâÕ#/Œ˜RF9ÑM‘w¦Ì,7úYèwóév>9Ñ­ºj2Ï/õ÷˜_¨ý²«ê¥v<ïÝ\_’\)Q¯¼Y­=ôá»õÛõ¾³Þ:ßm¡QÜêOt=òý­¹šîš¸ËIó‡†~¸¸çÊŽ}>g/¿¼÷400,þv¥\š†ô ›å·*U°l%3‡j&fzÙ/0ª•tÜtÆQ÷Ÿb=nô½ô?V¥&{/6¾<ë_lê‚—Wøeè¬FÕaÝ̨eÁ÷OsnpèVüM.ç=«Ø¨ü'•,Gü2<ì噃µjü¯ˆÃÎÖ-¡­‘BìŒ Ü¿ô^DÜõ/w rê ›\ÇE#»ƒÂÞŸŸ·Ðqòë¸ßy¸õvšàÙRû»QÔërd^çðß›—ÚUÚ|Æ¢Ù¹ª•-¤¡úCvŽütnÉÖ^#½nÊ’¶Rnû–e÷-d^HKK)êS`°ï}ßËçï:pùì˲y uv!µ4­4€ÌC€ÎŠ4´JÔo¸nŒ·í$?!¦Qú¨ÇãÊe ’™[é½úô‚v·UM,5’mOŽùüƲóÒÕÞžz.DŸ\äý¼Mu“”¶‰jî<æEÔe›JI{í»g!d»ëÏŠµ3þ1xKT4 šçË£,:úæ?¥gé'ß…Îsûü,[>«6Ï´³ÒN¢Øè~{goí6énŠË 7è,K¥ÌÐNC6MŸõHvýýÜnG:^il£žÄ|Ÿ.èåñY§yÉ5RIÏ‚õdÇé«‚ÉÒ…MÎ9,ôø —Ony\uXñ´ô”P)3¢«sà¬ñ·!øf÷®ªî¯R8í›Od g§9ýÆôñ(1dà™éÅ’k÷VÐ6tÙü↻›ÒüÜ™‰…išŒv«áYíß²ëw÷w[RþÜàü?淰DZgªæ+ç–6HuŒ†ð7ž«_)VëÒÔÒÌÆEÛcÙGAxµr½_ÿÉÆÉõ˜NL¢QpØ&§=Ö»¯K…#›:¯*~䯼ʩ?NFúz÷æ;c{H›Ö÷ŸlzŽ§š»¼©Êæ4=sV%‘(2d9:+“hWm²¶ÛÕÚ«eIWze”‡[‹=Š$ÎeÑÏÖošpS(3½MÇ©çµ0/·{Jµ&YæWTÍݽ’ɲÃOá±ÛÙ#]ì4ÒTšUí3n”ú4Zˆ<ù÷Ú%Žƒú›§a ÷_=öFpÌ5…jclR}-‰ºÃÌ+”õÅ]‘eHÕÒ¶o²tÖ&ÑpœÖ¡Ý®%ïe1ôþ ¾—í±+Ðá"úÍÍ!CÖ]ÑÇ0 MÈ7Ý.<Ñ,µ¨–¦BÌ`•»[é#¯.-¿ädçÆ£õ”Jþí:{×ødúñ öÇkŸ©k™ÚCÃ^]çwÕ¤sm4ôQÈckU/ Õ|<¸8_ƒ[ñÃxHŠo; æë;Ö\ز×çÂÃhu3{Û^šõ­®¥|mŸ—ÇVïçžÜñ”´¬›8ÌXT¿žÑOåHÃ:=oÙåg_<ù3 ³’nžÒU­\þªÕ³~¾\?÷’‘†=?_½9P¯©ƒ½^lTÉצwá‘}žÉîØ±øáÜêVz©u Ž§jÐ{½ó.KÏ3áBô•mgZ^kœTß쯤ï.Þ~wµˆyÙ~~ãë»uÚ‹‡á_çS¶=èÕˆÀµ•‡u½ôÝ"¨–¨¹ÿaMé»kŽ+NF‹XviГ©æŽ>.Yœß¬zQ¥€×g÷štæ’û¡Ö؈[Ò/¯æÔ›:øt„våêS6Z–Ì-|xúüÛ±õ;ß› hB€ øúÌúò;µ^ÜðN‡ýa‚²¥ç®nÕ:ÔÒ“|¹y´çÒ@='9¤zì`ŒàK§·¿ÑnõWí„§-ØÌÑ¡¿ÛÉH!xÿ‰co-[åKk´U1«æ¾àZÉ¿îË º9nõä#&—ûqÀºD"ž]~µ€±QwËh(PÑBC¶|²º¬{wßÖ°`1#5ÙfùáÅú>‹»n „w;/•^³mx%âùÕ”%ÒÐçwÆ4Z<û–TðùwŽWÝ  Í÷ÒÐ £Ƥa¥"3®Z*¾ÖúÍ*º:y–«sòé–uuóç»1Ï,~øÙ>̰±éY«ÍÖÑë[é~{;…û¯?yî‡q “;'¡DY–ùÅ,wô§}Ǥç<åw\umž?¶Á¹aé†ÖÑæM.¿X¿¶[=Ó}mt…èÃNGn])¾eÛÆ¥MÑðbîb^|C€Îu]–8»ßp"TüÏu›XåÖ õ½ø¨š¯šc“ú±ƒ2ÒsËnWýŸí·¾ІV}ë)ŸÜ!„ú,ÚÔ¼‹nšO[¢X´[ç%;Çu;!~SÛüÓèZ‹JZÉÀ耧Ÿã®åÊ—+… ýËr•ªVÄÂ(~“VÒ+ØeQÛ=»—î ¢•òת]$á4~µBVcØ­u¼ „\¿á/®»rä3¯sßÊ®tmÕ»Tâ¤/ÉS«ñüfœv…=Y°mýßÃúYéÎõ[ðNvEµŽóœæº™ù^ŠxxnøÆ˜õ’C7Îÿu ä­éØÚàòòׇ¦_~Úª–©RØÍO£bº·˜[$ê"Ñ->jrµkÙï<5dèìA©p¥3Ï[üý(RžÍ_7P-·çùhÛm;¤áØAiÀý¥ûC‹¬\*qwI®š}Këì»$DŸY|ýY‡EÓ¾9(åé´¦Ý ÷˜am3ÒúÆÂâºI‡ãèШ¸kÊꊿ1?ÿD¢™¯laïAøô铬„DqQ­PA#ábÌ Öá ý&¢}÷¹Ó‡YÕ¾y;ÅHrÙw0SÙu'\xºz_@Ï¿õ•„¨§»Î^‹¹O©z÷’ù35‹F¿8xévÌíêÕõ¾;XQM¿raùkAð¹{÷S-S]A³|ÒÛ×/¼·©ÿ­ÿŒ²UÆV™Y29:»P4ëÞaêº C¯Ê"ÞëÕÓ_ fuWôÉŸ–áçdñéͱ“‡Côÿ®¥à÷ýI‹”­«{yk  \;¹ùý( ÛƒRA»U+¯[´ó–eúÅk†6»¼v®¤â¼DEE–Ûb’iTD |£n?ºî)ýáfEuóŠ… ª§/x'Œ%'ýñY% ’¯÷$ÜæsÄ?öŠ®MÁŸ×ƒ$W1“‚ÂG‚pï¨ÿç¿õu„Ð[‡ãæ×¯n©–™;1‡ež|{%WAIdd⵪¨s”f”üòc´ «b^ËH8ì+¼½Ø°RäôyMzÖ3Ðþ=YÿÑ£G¦¦¦¿å©ÈbÐÙ‡ªaŸ5 ÝJï‹ÐB³ÓÒzeÓ6öœthé½p!z¶ÃÈÙÉÎä¿zß éÅœ:[¡@ëv«wÜo½ó‹ ®j¿µéíÎ “x¼‚®±¬Ð˜^Ÿß~ ÿùþ„*ýŽŸîñÔçÞë—2­vž2•ËNYW(½ZŒ¨/Ï^Æ‘¡n˜TÆTÒÕÓ½ú* (JЉþtïe\rÕ*–7sßGÑa¯üâV¤ßÈ’}F&3SxL»¿¢Y7—«gÏ»'H\ÖèÊ0mÃzm*vù_§²ZØýùóç•+Wž>}z—.]2îYÈ¢ÐÙ‰º©y}>1ȺeÌÒÚêé{sééh³¾çÔI¢…8âùå¿z_z-O×¹>¶m¥4†ò8Š:-–vj}rù–Axs±cßÒw—ýÜ&®\ØN_p ÐÑ/_ú‡—ÉtpS¶ì×åh?AþÏÙhÞ¶˜þ½…WÝÞ­@f$:òs|ºWPMêm¡ ¬ßY"<,¦ÛGTxPÜÐsJ*™ü6’F%ìˆ ÛÓ¹™AR‚¢Z‘ØU'Ñ-6ÛkLé ;'.¹óHö¨~‡Vì’]Œ›´Ø¾®¶]2ÝnÄ*T¨ÐéÓ§›6mzýúõ¹sç*)ñÁÈÉøžËñ¢Ÿí:}I0žÛ¯BÓ¤~¼Õ93íÒ<_Að¿¼ôBóJ5Å´AËršAéEîvÇ›x½„€-î=[Gþ8‹‚AÕRf³‡²«ÿ»аdþÌíïF Êšñá?*4¦#ÉEF‡GÄ5P ªjj²h­¨·:##ÂÄ ?—$Jñ;!Šù,MìLSÙÇPÐ1î8÷ïÓ>z¾¶ÙãÂÚ­Oe,ß½;]4}öU.”AŸæææ^^^íÚµ«S§Î¶mÛòäÉ“1Ï @ÖC€ÎéÂßl]ùB(Õªq¡dº¾ªµi¡;oA  „ìZ|?ÀÑ:·¸|+ÉÛÐy}gŸîea|wÏmj?ö7TÍ+üÏzß°›²ÐùhÍ‘ 6Ó>ÜG&RT7)¨"\ é£-üÔòùáý‡Ø+sÇtñhÒ‹»çÓÓ€(A/—IAÅÐP– e¥~~ü.JH-@Ç‘¨j—nâPºIõ Ó¯÷«³båC!äð¡õ*ŒÓñ=e:::{÷î9r¤ÝÎ;­­­3ê™ÈRÐ9\ؽKk|„R³­ '{fie«6e üë'‹cN~SªM’]R  YwŽk·ÃóWûÅŒ—šÄ+äë6½âÌß Ñ''œ¸Þ²y9QE2‰ŠE]Ca÷3A¼ñ"R(ùÃ[Cüà鋨k%ëƌѡ QÒVK¸,oß `šÄ9 &‰@::ú§Ý 1”‹TÑv¿„ ³^A‘vúÉ¿£ßß~jf\à[‰ZÛÙ++nÅ/Ðûe¤qZFAAaúôé¥K—®]»ö’%KZ¶l™O@A€ÎÙ"ðH0šÕ$O²ùYÔKUpÊÿï2AÿoÑÞ@çÿ‰nMUÈ]b–GõCާ|“¾_’»n«ÿ»WÕáñávcKyÍ,¦›Y}›ÓLÁ¨}e…gç£ÃÎn}\·¸Vâ;£?Z÷(¦ ‡¢i÷q-è*VÎ%õVz}"ŽÌ»ö¼±}áTßL%혓HF Þú~– iŠÜIR,TÏÖlÈ«‡‚à=ïÔõ®-Ê'{*ʰóƒ-é:怳Vâõ­jW_U}Íßògpqq177oÞ¼¹··÷„ d©úw¼ òB€ÎÑB^¬Ù Xµhj’b$Ö0nßT{ÙŠ‚ ½°äê“εÌRˆÛI“è:4óè{»ú¢÷I߯ YoÑ`°yíÖ¿»?gv…÷v,¨TJ;‰ý%äÓOݨ3‡RÁ s©8ëÍ[÷í‹û aýõhGé»#{üsà^ñAÎm ÅÇAíªõ•ò}Kˆ<ãÙzb¾=cÍ ÞOÒ°I5Å›UË'ö¢î›æã0³dþ¸õáÏÿ]”ˆRU-ì'5>ÚæŸ0á鑯òì_S­¬Þw!5:Jª7èvTð‘9W;Õ0ûvìf”ï‰Ø“«ëw*!ú/FeÊ”¹téRË–-›5k¶qãF--­Ô@6A€Î.¤ÁÁÏïø½‹›üòä–ÿ ¼r+'Ó¸' yóæÚÎC›ü„wj®5F®›¯nÛ£B5o‡ã*¶95g饳6l Žyfe=ýÒM†õ¬Õ£^Þ"½j±ª;þ3^?ãðŠm>—Ÿú9è§ ›·RƒŠ+ÜìõgN¨å"¼ñêdë5 Ã gë‹kÆ4rWô<6°ÿ!Ïó¾„þ(ä©Ú¤òĶ[•^|ú?!üÌÖŠ¶UX4ù€åÑú/^ñ ß'ðÞb¦²C߬؀½GZÇüôK̼8µáš# Öß:}íÍݳwî ‚z~ÃòÕJöQ¥Kã¸ô¬ZvXë¡ÆwÎ\z¶j¨÷œØL¯ªoP¾u½ƒë´/—ëwù¨¬¬¼lÙ²•+WV­ZuݺuuëÖýÍ/@f @gZÝ i%湚Xœ¦£Ô´,¶®Háþƒvb^W¢i×üž´y*s)ç²nRCvóÌIËUÅåú{—¤î1™q~ÆŒ$S¶ééçMSzR‰j±Fu–7ª“Æó˜t™Ù£ËÌŸîØ8Oº1‰g×-ï°öœÃÚo×_üjÅâïniséE›T^[E·ú_βKòs(Õ®1£v¬ê_ѽ{wKKKggçþýû2D¾ÅðëÐ~»*Uªxyy999y{{¯^½ZMMÜpãd)h™ÁØØøÌ™3ݺu³··ßµk—lRÞNh™D]]ÝÃÃcÖ¬Yvvv[·n­R¥Š¼+ =Ð2Õ!C¬­­[´h1qâÄîݻ˻D#@ÈluëÖ={öl“&M¼½½çÏŸ¯¬ü»†£àw @33³‹/¶oß¾víÚÛ¶mË›7¯¼+ ­ÐäC[[{÷îÝãÆ«P¡ÂÎ;Ë”)#ïŠH4¹QPP˜4i’M½zõ,Xàâ’ä¨Þd-hrÖ²eËâÅ‹;99ݼysòäɲT-ïŠH €üY[[_ºtÉÙÙ¹I“&:::ò®€d d úúúGŽ8p Ýž={ÌÍÍå]I#@È*”””.\èææV­Z5Ù¿ 4wE$ kéÒ¥K‰%œûôé3tèP‰D"ïŠø@–S©R¥‹/6oÞüÆkÖ¬ÑÐÐwE|C€(PàÔ©S={ö¬Zµê®]» .,G€E©©©¹»»ÏŸ?¿bÅŠ[·nµ··—wEÄ @ÈÒú÷ïoiiÙªU«ñãÇ÷ìÙSÞå@€åÕ®]ûܹsNNNׯ__´h‘ŠŠŠ¼+üÑвSSÓ .tìØÑÑÑqÇŽò®ðç"@È455eÑyâĉåË—ß½{·­­­¼+ü¡в ‰D2nÜ8kkëzõêÍŸ?¿mÛ¶ò®ð'"@g’‹ŸÌ˜qXÞUétêÔýÒ¥Íä]E¼fÍš+VÌÉÉéÆÓ¦MSTT”wE€? :34jÔ(0ðý‡ò®H/›¢íÚu”wßXYY]ºtÉÅÅ¥aÆ›7oÖÓÓ“wE€?:3Ⱦì§OŸ%ï*€%wîÜ2dˆÝž={,,,ä]àOA€])**Î;×ÆÆÆÁÁaõêÕ7–wE€?@öÖ©S' ‹–-[z{{5J"‘È»"@G€íU¨PÁËË+.C»¹¹ijjÊ»"@NF€ž8q¢W¯^U«Vݽ{·‰‰‰¼+äXh9„ªªêš5k-ZT©R¥M›6Õ¨QCÞr&4€¥oß¾VVVmÛ¶9r¤ìº¼Ëä@h9M5.\¸w¦•¥K—ªªªÊ»"@ŽB€™˜˜œ;w®sçÎŽŽŽÛ·o744”wE€œƒ gÊ•+×Ö­[§L™bgg'ËÐ*TwE€‚ Ç’H$£G¶±±iܸñÌ™3;uê$ïŠ9@'KÏ'OžlÚ´©··÷¬Y³å] {#@Èù,,,¼¼¼Ú´iS¿~}OOÏܹsË»"@6F€ðGÐÓÓÛ¿ÿˆ#*T¨°{÷n+++yWÈ®ÐþŠŠŠ3gÎ,]º´££ãŠ+š5k&ïŠÙÀŸ¥mÛ¶%J”prrºy󿨱c%‰¼+d3h[[ÛË—/·hÑÂÛÛ{ýúõšššò® ü‰ Nœ8Ñ·oßJ•*íÞ½ÛÔÔô‡-ZÔ¾}{===¹”ÈÊÐþP***+V¬X¾|y•*U6lØP»ví¯wÉ"u¿~ýnÞ¼¹jÕ*9VÈšÐþh={ö´´´tvv6lXÿþýe·Ü¾}»cÇŽR©tÍš5íÚµsppw€¬… àOgooñâÅf͚ݸqcÚ´i²+ÁÁÁ²ÛeºGÞÞÞjjjò®… @(\¸ðÙ³g]]]-,,‚‚‚¾Þ~ÿþýI“&M™2E޵²4ÄÐÐÐ022JœžãÌš5«uëÖÖÖÖr©   ÆúõëçÍ›÷óíÝ»w?w¢bæWȂР\ºt©GÉÝëååµdÉ’¿ÿþ;3KdYh:??¿-Z„††¦0ϨQ£œœœ *”iU²,4€?¾¾þâÅ‹ÝÜÜ8‘ä<Ÿ>}êÕ«×¾}û2¹6@D€ð§SVVnëÍ›7²$}ëÖ­ŸgÛ¿¿§§§‹‹KæWÈRÐ/_¾|b]½zÕÝÝ}óæÍïß¿OãTó®,LË.ÉÝñÖ÷ÑÓ;—<œœçëׯׯßà¶nÃû¶µ[wšº£`ñÒûÉÑÍWÀ®~;Ù¥õ°¥>^ǶÝZºò®HYÁÍúÍ®ØØuýÂAËV®Z8oN½z4 øC à·»}û¶s›vªºFý–Ÿ2,ZRÞ够¬ø¿æ¼yf_מ}+–·]½r9Çø à7’J¥‹-?q’SßYu’w9ÃÚ¾QI»Ú{– ·².½iãúêÕ«Ë»"ÈThø]^¿~ݱ³ëÓWï­¹×ØTÞåd$%Õ敨X¯Eë¶Ýºtš4q‚²²²¼‹€LB€€ßâØ±cí:t²kä:`¸lq°`:XVª;bãMSº®Teç¶-œ À"g~¦€|­\¹jÄè±®S¶³­&ïZ~/-½¼Ýgí9¹ea¥*ö{wï¬P¡‚¼+€ßŽ I*•1rã–WýŸ½û€‡òàþÜq³B’ %%%¥¥AÚ{ïýkjýÛC{§úµÓø5´ ihªDƒh‘†=oÿ“TÄq<œÏûåõû=îžû>Ÿ{:îsgÜ“²Ý6JB¡PÚ ˜®Q·A—nnöííÑ£Ù‰* 4€Ä°X¬!ÃF„¿û4{ÿCE ²ãT)k§î*›¯Ž™Ð#&æãôéÓÈŽP‰P $#33sÌøÉŸÒSv\§É1ÈŽC‚zv³ö=Ø4£kFVÖ¢óÉŽCŽððð+W|‚CvùnÝXYY‘CÚ @H@vvv—îîtË+Q(²ãF]ÇpæÞ{Û&·ÏÉÎ^µrÙqHpùòå Î;;‘ Opð—ÔTÞš5kÈ"mP *ŠÍf÷ìÝW T·ï¬mµ¹=‹È3U'mõÛ>ÉEQQqÁüydÇ!AÛ¶VkÖt";@/¯Ð´4²CH#h€ áñxýNá2F-?D¡RÉŽS-0ÕêLÚæ¿u‚3SIiêÔ)dÇ0h€òÃFŒŠþš9ný*U†ì8Õˆj½©;o¬ÛZEEeذ¡dÇ$h€òÛ´ióãç‘Ó½oËÒäÈÎRí¨ëNÛusúGss³-Z@bP Ê)((hõÚõs=–c(¥šÒ20¼è@ÏÞ}Ÿ‡=­S§Ùq$ <>}úÔ·ÿÀáË|Ô´ ÈÎR­Y;u}õ¸wßþ·n\—‘Á^. P ÄÆf³{õéç<àŸ†ÍÛ“¥è:vÉîÝæxÎÛ´a=ÙY$@lžóLÝCæ¤f Re†/?¶n„}Çö.]ºt!;@E¡@ˆçéÓ§GŽ[xâ%Nù\vŠ*ì7atdÄK%%%²ãT 4”BÀÎù–Äæýø–BcÔѤc7F¨µ¸\îˆÑc=¦mPRÕ$;K ÓÐÞ¥~“¶ó.Ú¾u ÙY*JÁz÷ðŸñwï‡'Ʀòó¾7ïù¢kCœ° j« 7É(iµè2˜ì 5RÏéW °:xPóæÍÉÎP~(ÐP †¥Ëñ».;q•õ’E¯«n¹¼Ïϧvþ×û³×Ύצ״ñA*½ÿ~íºõs=!;HM¥¨¬.ìÐ#ÇŒ{öô F#;@9¡@CõÄ=}Ñû%‡ ’}—Þ:ÈA§٭ìñA:Mýgf‡ažzFd©Áì; ¹vÄÛ{÷´iSÉÎPN(ÐP=Q5›4Ð'>}"%;SCÉo®ìñA …„„<}¶dÁ)²ƒÔxÝ'­Y=«ûرcäååÉÎP(ÐPM1ú‡†4 ˆVpêf¢Wþƒyï·®k¼Ý"8ÂÃú×–,¡ñ¡Y¸xiÇóeåðy«¢ Ìl -›ïÞ½gÆŒÈÎP(ÐP6Š µŠ)SÇÎf°]…†¤D-]“£bQIãCíñøñãgÏ_.YìKv)á:féêéǧ €Ý§ æA†²¡È0jÞ?ܨÝg}’ B…ì Pó-\¼´Óˆ²4œ€F2ôM­Ø:íܹkΜÙdg 4üAÀzw5hã®ÇWî}ŠKo *(«É¾)a~Núýƒþö…Ý|š”Î':Ó¼¹E¿‰g÷×W¦DÖÛ‰v[v¿æyÃĽ׵ÓÎ&yU„ŸpúPçÉOÿÌ ÔÈùZP›Û½¶­¸“ÆÝÔ¸ßûÐöÆE^ªÜÏëß¹þ4!òÝ÷/ù'×SP±jm3n¡ÛÄ6Êù3r^>¿pOØå‡IyßE_³a\˨ڌ›}YÙùoãç¯ö{¿;›½Ÿ\½’wlYU ÛÖV&v˜ÐEKñÇÕ3²‚´ N}Co|õë$‡¸ð£ûƒO]Š|ø.›O0ê;7Ÿ¿¥÷h[Æ/›ïYÉ;®¬?øâ^DznÞÐ úZÍÆnè9°>~$«£ÐÐд\AO·‘d‘*®c–\Ý7oúôirrøX5 Þ­áW¬¯‡Gnq"YXsí†tõ쬯¯Dä¤g}ýôéÔš¸wìßg¤E¯î¾iÑ=6¡×hæ–žÎÆ²ÉÏÂÖ/}´âîãC~ãîoZWÑÔ;jûâ; ŠÈvñVC¢o;þ|ÝQõúŽzÙwdÒÅÝF=ž[y¯º;ACxgë u‹²¢gZxmŽ+&#'á•÷¾gÍì'­tkm£©ÆO½ë}Úóôi/ïÿÏ»ƒ"• *›š™©Åüçø¡x‚Ðh²i—C}á{4•af"K0ôýuüŸ+¦ÛÞ¯æLNzxàƒ »‚f_ÚÚøCަŒ¼GÄÅ:™º}Ò…l‚õv¡Ë¼°§i u-¦“’&Ⱥ3®u2=rò0ƒ:'a£ëêÙw8ʎΫ|Yª)1±~ø&ÍpG®žöìݧoå„ÍÏ’¥gÜ()=ûâÅ‹}úô!; €xðn ErïynÍoÏ̧é«úóõÁNÌ8rûþoçæg^²#¯=kØŸ}:ª—N~GìfÛÍšoîþ$îÈ1® .T‘!¨ºn]‡iGìùBðïqœaòëQ{üŒ€ᙊ֞}Ô.Q–&_òË“ªhî}yô@í‚MÁ-›«ÄÜ]瘲w~È‚¶Îõdet­=¸ßc–„°@+ë´ïaûÛA„%Ž/È}¸p[ÞJ­¿öé̹ :S—ž£š¹é›pB{tßÉ‹nq¤htpÛÒó¡Ç9VôÖÓG¦yN1þí°Jy£†¢öœ77C³W/gŸA<âí­Ï™‹uÕónf½ðá ï53·P*2´ªÙ•mBµq6êÈ××׸q 5-}²ƒH¡¦í{ŸÙ4=>>¾nݺdg 4âÅœ»š7!ë<ÎR§ô.Ç»ö8]¾œ·ã7Ý©WÝßóP†šÈ‹`1û.'O˜¦ù÷ŸªŠvc-"è3!ÈÌÊæêù«TÀÏË'{˜dÓE£p%ЬƻYU$8Tš=ÿhÞ}"Ù)¤.߬c¿C‡/\¸€ì,b@†B¹/ýó'41ʰ)˜ýîö×ü E ÷—Þ*£ª"l‹<‚“ŸÎ'Tó¶ÔÊèZ/¨xãp‘êu±O‡aª=ë㮽_eœ†0¯¯FVd€h%¨Úü™‡¢hjd@D¼'ˆ×׳¦i–vzçþÞ"gÞAðÿD| îÖ’ëµÙ}‚«¶2¶;WcÑÑÑááá¼ÜÉ"µZtùï²Á ̧PÊô(iÂûü¸‡Íþ+ßþ6M‰Y·¾¶UÓúm:Z÷r7mÀ¬uk  zª•ªNæëxQ fšÖ)à ƒÏJø,:¨ðóË)%l>â³y?&)ò­fµ59|åÁ»½.øÝWó¼ý#)A7Ž%2zî³­[öÉN½ç´ïtăçßâ“rs9¾@Pú£Ê‚—ó1ž“?%¯[\±•UUSÍŸÈMHNã*bw_“1fìÛ°ù5!xâÙ=ÄSY×u Ãȱ­<ì˜8B­ºpá‚m[>XyŒ5çðVVµîo02ºÍ/µç¤§oÚÒfYþGw‹¶§èæ¢Ìç³²³¿Ä~}ùäÝ­Çïb^¾»|øú\Іë¼>Û51U@  4üÀc§åæOÈÊ)”åu!à圔CÛóˆžÚÅýB—aÔ¯ûsGaº¥“gëkcïñ‰ˆ[;B]¶;Èüô+Ÿ¥×užÓN±Œo‚´‹ÛoXù”Gè6š·ÂÝ­™¦S–JäY1þaÙ†ø >7«àIQéÅ­*V°³ ›Å~Üã1EÕtãÿÙ.ó]¾3â½pYéŸýöœ~é»÷>s¸c U¼/V/WýÍÚ ;…”kؼcÀõëµ°@ç£Ð”U2Dß0L­ÇLj¤üëÜäO¶_œ½ôyŒ ÉoÍóóξ×û{ˆ±É$~ Èȉ~!s9¬²ìFL‘U(Ø*'£ÕȨEƒ2\¨PFµ÷‚¦³»†¤©‡×G-=c­²é&¯ñÚ¶MÊz12nøÆƒyí™Ð_wkÒœŸ{}äè*PŠî*QNTšRÁÉ:x¹\áh¿×Y>›#Ú@MÐŒò^š‘ª¢?lÓ´¡kÒŸû‡ž8öðÀ©˜ïñéâY—J‘— ñCYmp8œ‡îu™{„ì RÎ̾ã5ÿý3gÌ ;H5%«®ß{ÉÄ.ý owäL"!ˆ êÙqÿQOGåò}Þæ½ßº®ñv‹àßÏMTuªC†?UÏTPMá½~ )ª‰¦2c’y„Zi›7¨rºygfcDÖ‡ï<¢,𠍵í<¶^ȆDÆy?ß8ón†ÑR§¬W9ä$œËßñÚÌ®{eœ2YFÞÈ@ŽxÊÎÛG#OÔýýIqS’D—MQ0P¯à¾Ëº²­{[[wçe^aÓ;íÙûŽÈö÷;ò¶ù" üTV>Ô«g¦¨¬Nv)gÞ¬ÝÑ#Y,ŽÚRŠBÃVGoæ$Øœ~ üå?l•Ý‹µõÊqtAJÔÒå19*’ÏX£2ü©z¦‚j oÕðUÁ²)“É ˆo7^äÎhPê>´ú­4‰ó ‘vïQ·E)§¤( ¯?i¶Ñ†©1ÿý†íŽ|Wë;ØM·ÌÛr¹9q¢«ÌdVÊ0å,:ëç?Dê³8.aùûõ 3ÞÆˆ.½bÙY·Œç ù?)<>×D¿îÏ£4)ŒúM7ìu8ãœL¤>ç(ÐÕ†¿€ióŽd§~ Êj ,ƒƒƒI ’’¢¦¦FʢŰhûï’û ëï·_¼:kJ-q7Bs£vŸõI&ˆÒŽ€®LÕ!ߪg*¨¾ðV …ä¬úYªí}”Bp6‡Æº9•v6eCצ&sÞÄóÍAa£{Û—©QRë èÒcž÷…,â͆coõÙ³LÅØï—JSA:))‰Kè—Ö¡ ëçñ¸eݹƒª×ÕÉ‘úñŸuïTlFg3fÑ;ù™A‡ßçíÂ!Ó`\WÕrxÖƒYÛwŽþßÕ~Ì¢èÚu4 "™ k*•w¿¨7n9ZUî‡ ØÉé¬Â—…¦ÊT‘/åÅÎŽzpèP"«à;Zݾ{Ú•åœ85]»öׯ’U ×­[wêÔ©¾ùìììHÉP6² G¹¶]zà6— r"÷ÜÎìݯà¬ÜÏëß¹þ4!òÝ÷/ù;á)¨Xµ¶·Ðmbåüßåœ×‡Ï/ÜvùaRÞwÑ×l×òª6#dÅ&;ZÙ) +9`Ç•õ_Ü‹HÏ;¦F–¡S_Ë¢±ÙØ =ó/³ú· ‹¿jØ5ä‹hy¿/“:+äF^½·so°o@¢ñ¾e;>êºüÍgÑ^tuœîÇ qÌßÏŠºÑ«ûÅ«ïEñÊfÆŸ0Wú%?åyØnïG®¿{ö!+ÿÇ‹ÊÔR«g¬Õ¨­ó†VYÇJJµxÄ5oWqšzíß¿>‹Õw†*ü–ç¤ß?è¿a_ØÍ§Ié•Mgš7·è7±óìþúÊEßJY¥@¬}øI¹µë¬Æ½$¸wOö_®ua±¹öˆ€•’ûûüt §n×^b1nC5®ìoc§öKÿãóT™ßßü©šž#U/ìHÍû¦I‡ ˺ûFþ"U›[щw,"úþf—}îù¿Óœ¸{ONGýÑ‘eè:jù;FÇFÅr›š•éÕ.kÐ|Ó¬‡õ_¿:³cúìùÖ…§_|¸0ãRÞ1†f³ú 2,oÓåel ùàÑÎäçyxŸn†¿þ_ÅÌ£¡8k*YdÄË!æMÊýpVÔ–£›ÎÄFG¥få¿8-öx§ö÷ŽŒºN}ƒÔOžÿ*|…qkZQ  Ì›†Þ!s_ó>¬Ígll\›4UËl€ qû©p’z51»S´á‚“ðÊ{ß³Œfö“Vºµ¶ÑTã§Þõ>íyúδ€—á×ÿçÝA‘JP•M͇ÌÔbþsüPA×›}vèÇ£¾s­Ø¤³Kש¹v9nr\bØË4 7ž”ÄÇéF¶Êyoê2ªýL í´m}ï˹ÍÎ]´lmd¦C§²YI‰)ß'+M™²´î{5Ò›Mmo½ãì ‚ÖÙÓ¾~ QÀa爖Èegs _ªòNsZ¿C¤îáéoeXÎþü&!–jܯ©ŸM䦄>OqpTËûMOQl9ÄD6è-—ˆi½Ì×EG);í[Ýnþ>6”’ÆÏÛ7¹ÅŠiGâ7;þq½×«Eí{·ÐPâd„_¿ïµ5ò‹ð7à ‘W—ÝÚÎሺ;ŸÍþ–,òÃçrD¿6y\ÎÏz/<>ÙÈ&rê8;'KNZøõk·E fŸ½]pŽj#..N^‘)¯Tþ?è2¬—O>´œ`…œêiãk13ð¿_<9e@Ї:Mæ]Ó+ÿžŒ–qÇÉÆmZdÞ8_ìC¤•N} ÿ=‘d§ÈSÝ›´Œbã¦Lâi†p29ò[ß´°ÕRͽ/øã„H-›«ÄÜ]瘲w~È‚¶Îõdet­=¸ßc–„°&*ë´ïaûç¡r¥ ò—dü¸cÇ<ïpˆº.~þý[l¶0Иmz¨0})hò:¢·„›÷êŽ9vZK}3ÅèqM&ä“¡—ð·? M–V̯OnÔ¶V¼ç =,:9T£pÃEÿ1íh/²?S†Tâ.´ÔgÁϼ:eG^{Ö°?ûtT/Ñu|»Ùv³æ›»?‰;r`ŒkƒËUdÊ´J4(Ðð ºië³QúGÖúï9ù$æóÝkŸ©ªuZvuØsÐIsݲDz‰¯†7}4cè¬GÌ”ò6'7\¼ºÛþ€­G^Þ ýúê^Ä+aÉÕѵoc9u~«‘n¶ç<4‡ù/|jïÙEùÏMY÷O¶í÷ $áÇŸ±#Ï7–¿ lØ`ÊÙV5£1{]›8÷öÕ×9‰á±lãÎSGúβ7 ÜræÖ>÷Ã\§yžƒ£ï¶©'K5>útäi;ßı¾ó›:ÛŒ__öÁIû’ÇÏ_ u†ú,q´q×ã«ëúdä•_šš¦­{[Ï Æ»Ö)<kNèÅ]®>ø*jÇ sͧ¬iäpðæðjÉn™~ök†h¾g'ºnšqn²¦gÿ¹úwüwîóù ž®©mßßuíìNCš)âÄTÕGddd]ãJ=œˆ›ø¿ w9¶gKl×]ÆŒÊ\Zõ¦e`’Çf³åäªË)·«k“¦ª*Dþ¯–´Ôt.¡—¿ÂhÚæ“WÚtªS¤Í)ê´3%¼ âý‡Öß»oŠ ÂzáÃvJ3s‹"ûOPTÍ®lª-îï6ý>ç×½?¨-Ÿzs‘ly.´ÃO ›± o¿;Yç>ÛiüúÚ¢™»›iP1ÿ,8ïîÏóÉÞd9ÇÍM§ð=R§½Kí'»¿pü¼žÄôíÐ@V²«$ ~'£a4rÝø‘ëþ¸Ãg³À§¸È©:Oì'ücTå~;”p§b«OâKºSHÖÐÝ㊻Çï7÷›Åû3MÍcã,¿Ýú÷ñóQè¦Ý;íîÞéïsÉ7u¿ÿ¥ØkÔiŒ<³bdqwèul·¶c»R–hii©§§WÁq¢¢¢4 J$R d•Ì[©/Rò÷†R¯.½‘2²4m}£7oÞ=tPPPppp,ýïK©fMš"ËøÑœ¸Ü£;dõ­çþóÛœT†\þ>l·,g&­ð ~Þ‚ð°‡I6]4 ë.Íj¼[ÅNñM³óš­<ä'\¾é—·ó!¥ÕØÆzN%ûÎfêWÑ~s…Ï‚wíqxÞ„²³³Ú/ gh:Ö'v~|õ*³CÕÊ[¥ (Ð U¶lÙríÚµÖ­[ ‹NïÞ½uuuË7ÎˈH-£Æ’ÍöF“­ v~• nÚºµj­?|T·¾EdddÑ“““’’R‹ÎÍýãâ6iáKk×®]ššš•¬8v·`’N—«F/9óz„ÿ'â[p·–\¯Íî\µ+x®Ï cG\ù”?¡áh¥ð窢0õ‡Œ­êLÂTïn‹vÏR4P¡pù\"£šwy[ÁɈOçªÕp•ÂO(Ð mø|þ|Ó§O/w“Žzý¦YÿÞ•”ðÓ¢G‹¿ì'Bùã \饡oþöíÛ¢·¸æ«‚EÏŸ?¿,›ºíììD[ « U øiñ9“êêj…ïáìÔ{>AûNG)7—#à Ä¿°T…‘13`ƾ ›_‚·!žÝC<•u]:ŒÛÊÃŽIÎßWx9ѱ¢KË*ÔS¯65ø¬„Ï¢TŸXNYPÂLlQW) R«h“vrr5i²<6%%EIE£²–BFŽZk´wæDøóðt–ð{%5S§ÆÝÿqíÓQãG!`…›7îh¶è¹nS¯_0þr5øüÑ»âRS³é ×ÙëZ›ýyÝg^öß 3>ÏÝ‹‹OÎO"ËP×W¯kªg7±ÏÄžÌø;ÇLy•\äÔºÖóïNèY/;høúÿù|)Xª¬Jó53¶ÌÖ­Ø61†’rê·÷B¢ªWo.”ùÙï•hŠÞÂU;ÿ´øÜðóŠ/¡¿îÖ¤9æ…oë9º ù»/—‰D!¨*úÃ6Mº&ý¹è‰cœŠùNŸ.žu yÙѰŠ•Æ,8Œ“–#| Õ£nR„m^4%£ÕȨEiñ­^«ŠÀºÉHOO÷öö&;ñþýßJXa“.Ü&Ý«W¯b›tfFCùçí’!gÝuoBלG§û8~)q.AèˆõQúº&FŽf”ìø„—¾½¹vgÓµ»¦OõÞØH=¯qÓ›òº21x]×ãA©ûîù q‘ŸiʺLEy%%Gñ=tÏèXÚ™‹ÚE~áó“Þìõعï^.!¯ßifßIöšJ”Üo¯cŸ ¸òŲӨžj†“§ŒÏ ™µfÂ6á{6QoÑ¢ã+ ò Óùèò›ËîL²<ömâ¬ÃÍŠ9›ŽØ„k;==£âãTP5íÍ?d< öí®j=Þ!ÿ¼-œä€sùûÔšÙu/÷‰%2Ⱥ²­{[[wçe^aÓ;íÙûŽÈö÷;ò¶ù¢*¾ÌªŒ¼‘¡ñ”Mi‰\¨zìõ@•ÓÍû¬)L•õá;(­@‹T—U E`Õ€d»iÕòõw§,³ñx¼Û·ogffæääLž<™ÁøýrY™ ÅJ+Ð"2ò´¿d¶mÕÑ©…ó°c_ìè¾ëøKÁû­Þ‹¬—o•_¡©tí& ê«ÂM¨›M œÙÜXA6oc›€ýáéê6ÿ^Ž'r®]½ø¦ÙØÂkÓ³OºoÙ÷€G¨5Y:¦«Qá;A3)ö¦«o&U²[9jÐ¥uÇ£‰kŽŸ:kèŠD¹ Çý÷Ò¦—Ïj‰´g!y%•Ø´4‰ UÕ¼7à&û_ð÷üI«Y]EÁçæÄ‰~ì”™Ìra&AøIáñ¹&úu^ø‡Â¨ßtÃ^‡3.ÁÉDêóx.!‰¶Wp6;Ÿ_úfq9‹ÎzĹá§û [)lír7hqZ*ZýVšÄùa­¿÷(ÛB³ä•RE«Ê«$CUUÕËË‹ìDxxøß7B ÙÛÛ‹Ú’‘‘Q±3[8Ç¥Ñå%ŸO,2ô_ö–3´žv¶O˜ÙéH‚óxIàëý,K¨¬aTOÔž…(rÆvS×]ò†O| ‰dý(Ђ$ßS;„í™ 6ÝÐßÕè×·yí6ÎÌÛEn 0'øt¸Ý*0÷aëÈ;­o¹Òr_,Þ½=ÒbñËŽ $¶Š ÌŒ r¶@/Y²dÍš5¤,Z VØÆ}Óïå´bëvx†^Áî Tšªè!))‰Kè—V º ðõ]´ Š9HqXfmß9úWû1‹~¢¢k×Ñ$ˆd‚®©ôóæâ3”EV9ox>‘òíS–€Püû^T½®NN21wyDäÆK×Fî¡õ·ùKL%ÞBK%cèÚÔdNÂ;‚x¾9(lto{Å’æc•BÕC€Ú¢ÔÞ\(;;»©CÛªÈ$.ÙÍ9Ÿý_Ÿøôìæ›^–6ÿ%NQ2­«N¼ùNr’Ù|"ÿªu¼Ô»Û"òc¢šôìöçéóèæöõ]­È¸…–n+þy6zËwÞƒ3Ë÷6Z]ßoÎz–Û•áÝÊ}EûbÈ3UµtêJn<1üù÷‡ê†ó-æð‚#“öÅs„ÿí.^ëÚ´°uÑU›[щw,"úþf—}îÊyÿtNܽ'§£þè§2tµü}šc£‚b¹MÍdË3HIxC>x´3ù¹¥—÷éfxÞ©UTÌ<þ8åqIÊ„fÒF‹ðO$xo–¬‰l»ÎRG4*Ÿ“ø:ñ;ï§kÐ|Ëüv+ˆ¤'½]Žõl£ð˧ÅÏ¿ÐàßR‰·ÐRÑ-œV¸]x‰Eĸ Õ¸²¿Ú/?I|ž€*:OW)¤…Bõæ>}ú”Ú› ÑéôgïVf®r£*˜µÑ ‚¾DòË·,l)›Ã¨TÑ[óÏó‘e%Ü{‘?Q¯Aƒbã§(Ø´èùûm ëå£]Xw<š÷lêºa´,åyž³»0%ºý+7+=1!N’#Ö^Zü—°¨‚ã1s#C¶¬ýDÿy''ãkrTèÛ€»_ó®\G(·Ù÷r;ó_6‚Ê;ÍimtþF ‘~¸‡§¿•a=:ûó›„Xªq¿¦ D|6‘›ú<ÅÁQ-ïãE±åÙ ·\"v¦õ2_¥ì´ou»ùûX‹1HÉOFðød#›È©ãìœ,U8iá׬Ý- ˜}vôv)|­•˜ÁFƒ"à°E/T>‹Å¾zÿX„¬ÉP×Ϋùgo¶lÕÝ¥d Gdg%~Í¥èk‹®Ö—ÿ)"ÁØ^–ÿ`¹¦‹§ù&oë»+qw¸í݉†úMÌTÕä©>/ó{rToÐÅìhM%îBK{2ªýL í´m}ï˹ÍÎ]´lmd¦C§²YI‰)ß'+M™²Ttß²­R  4H¡òõæB ƒÏçñ¸Ùj·‡Ê4PÈŸ¤'ä𠱯ÿÎOKJÕ55u•2¿P˜Æã¶¿ÝúF‘ù]ÅqõÂú ~óÎÍÎ`2+y§ój†ÜÉìàÍì"7½}°dÞosÑÔ êØ¸µrvµ0Àªq1ç3¦0{]›8÷öÕ×9‰á±lãÎSGúβ7 ÜræÖ>÷Ã\§yžƒ£ï¶©'K5>útäi;ßı¾ó›:ÛŒ__…B‘cbŸ Ýγÿ\ýˆ»?þ;÷ùÆü³ºÐ5µíû»®ÝiH³¢¯Óâ3·Ž5î}'FC¢o๙éÊÊ•vÞÀjI¦®Ã,IŒ$kèîqÅÝã÷›ûÍâõûc^ššÇÆY+6H1¨zÛ­íØ®Ly‹Ï0øeÊà2<˜¢jßöÀý¶~¿]sGžÅ?DFÇÑqµð«<©Ä]hŸ…ðçOÕyb?áWÉsˆ³J¡Ê¡@€TY¹r¥DÆQd2s³2ªa°Ò °‚&½[© FÁþ”¹9¹Åþ•¼„å¦\ØDU‘ã§±9§»ÚyŠ›wâÈÍÎPQ®][  æB(†’SXéÈNQ ^òëôü Åzõéå(°T "2› >'$± ½²½ ð>=Y9ðüœ9:ÜÑù~RvÄú‰OZ\h¡%±­Ðµ­‹ 5 4@1ÔÔÕ³Ò’ÈNñ'nú«‡ùšfتa¹Îk« Û²që A|yÇml^†·Ö—3ƒÜ7í鳬.]{Ñàg3Žee_9¾îLÃuýU$´:73MM·víÂ5 4@1Œê}‹ÿ`f×–ì ¿âF‡Ë›PèÞÎ^³\Çñɨ´gJ}ò–O|öYÑÃǦ˜ãÒŠ°¢ÖìÝj:ÿE‡ü³>+µÚ8Äåòž›i¹·'œ¼í<ÖEG":ùÓ[§.’  Ò¡@£q#‹ ¨(²SüJýÙwÂù7Â)ºÙ”õVå=‰U{P¿‘[Ví ÒNìžÀ¾Ò«¹Ù¯ç¡p¸š¬èŒ\é·ÏÍ]–Úþìt÷ú2?o;{§uðÙ©¡+g¼´=VZ/“ĘȆ gT|€*€ P ‹³þû$0Åå‹&r¹¿³'àfsDWaàe³……»S(E‹õ¹²;YWKSž!ËËø{ÿÐÝà¡`<êÊ„> Šì},àýXƉøl^Á=lÞÏsv(Žõ›Þeïépþ‡½í=¦cc`¨§È%øVêÇÄnsïç},ä ~â³µýnñ&ΘçQôŠÝT­þþÙ±ú/ýä‘Í—.s¯à9¡|~|ôë† Vh€ª‚ P aNˆŽ¬È9!S¶,>úñkzAq}3}‘Ã%çë.ºdÏÝ8ïàÇo™³F/\済¡ÓªÝºKyBή§Ïs‹;—^…ܾµ#$.>'ÿ°²ªf :Ík5x–C#;ʉZ¼yÚª÷É¢šq¶ãšñ°»Ø(Ç>XÐñ¿›ïr –>uaëõV3'ö3Íû½/«ßÄ3tmŸ›§>yò9ñùûÄçEQÙÀª^Ó½fkÖ0'b¥ãá€giyg)öÞÜýšÅèK“‡[Ñò¶Iû3èÁ‡ÑóʼÖc¶úïΚܬÜ'ÍNNŒUS×PRR*ïU  ÆÆÆI_8ì\š\9¯ó,ßlÇü«%œ—–ضàÚ¶¿¢ž…]EÆ׬`Oå½!‚Úp{ù$¼{ÙØÊ²âãT h€âµuvzýäFE ´°eöËÚ0]nïCo›3©‚ƒTè*½v­?Ù)¤œŒ •Çã—>TLPÐ[[[²ST….;ýoͶNÃæ–ïá¿5Kát±[m¥æör×h;÷ͳÎÎÿ•ïáUº*tïÞ=5õKJ Ù9¤¬¬,—Ë%;…ô³±©7xð`²ST…¶mÛ¾<„ÃÊ¡ÑåËñð? eISZo/£÷Ïî[6j¬¢¢R‘Aª tU°²²òòÚLv “Éldeýîù}‹æÈÎ"Í¢_ïÒ¹#Ù)Ä€ P¢.:<~tºR½ \8f+Ù)Ä€ P"7·îÿöÐcŠ×K›€„}OˆNNŒkÞ¼9ÙAÄ€ P";;;eEùwawM›¶!;‹t ¾tpР4Z¹¯Á@h€¿7f”ï¥ý(ЕAÀç?ºrx•ße²ƒˆào† ¼tÙòܬt†¢2ÙY¤Í«G×uµµ7nLvñ @üM:uÚµoÿ$à?§žcÉÎ"mž\98~ì(²Sˆ  ÆŽ^ë}Œ@–¨ôä/ü¬ï$;€ØP Jѹs癳=_Þ»Ò¸u7²³HÀ£ëì›X©ªª’@l(Ð¥ P(«V,»x©U«®8ŸD¤'%_:t(2‚ì •"88níÚd§¨ñ˜LÅŒŒ,²SÔxAAQ¶¶-ÉN!…P Jçááñ¿¥Ë_Þ½lÝÆì,Ò àК#GèèèDòºwïžššš’BvŽšBQOIÁçÕŠ²±q}ñ¹¶o¹ÛÄ•dg©1ø<îñ•£–-Yl``@v€ŠBÛÁýÿÚ4±«gå`íÔì,5ùís µT¦NBv @[:uÎ=íÚÍMgß-²ãTw!×O½¾ñYhN¡ Ò ¾Æ¾õžÑuøàþ«V®À~Ï ÝP ŠÇår;¶bÅ __ß²?JYYÙïêå±ã&l›Øv܆‹*šº•—°úx÷ìÞþù}×­Y5zô(²³T:h€ßñùü'N,_¾üÍ›7ÖÖÖb=œF£:¸õ¯uÛõŸ·ÇÚ©{%嬸|^Àa¯;§¶Ÿ‹¼&Z´hß´Ó`a“–•£WM†¢2S¿? <ý,ðDÂûW&MºtÓÒÒ²êcTs(ÐP{=zÛ¶m?~,zcË–-;tèP•1(Jç|)))¾¾¾‡Žî8¾jŒmÛ–­ÜÌš¶QTѨìßã?¼~zûå­3ï_»vé²vÉ\aa¹¯ìåÔP(ÐPK¥§§wéÒ¥GfffS§N-Ü]îKVœššÚè|Ÿ?þïÔ©+×öù,QG¯^ƒ&ÎÆ¶mL›¶QV×–Ô²c¢Þ†Ý~ôæi•"hãì;€šjììla{¶°°Ø±c‡¨zŽ7NFFFø_Éžû¹â„©šå›5s¦°ßúôéÍ›7ïÞ½‹zý&êÖ=ÿׯ¿ÿš‘–*Gg0””噯߼ÌÍÉÊÊHËÉΤ%¦r]C3SÓÆæ¦f-;™™M6fMMM²Ÿ@M… µ‹¨= äž={Šn¸=z´žžž««+‰ÙþN˜Ö _ûöí»+'GØ—³ÒÓÓ322ø|¾œœœ¢¢¢ŠŠŠ’’ve8h¨E„í¹GFFFÿþû=téÒ…”T'Ÿ•ª 4Ô¹¹¹½zõÒÕÕÝ·o.å† µ›Í¶g55µƒÊÈÈj0èª~åÊ ²ST®nÝZYY‘¢x¢ö¬¤¤äããS«ÚsDDD£FÈN mP «ÂåË—/\8éìlJv€Ê“šÊ_³f ÙAŠ!lÏýúõc0ǯUíYh„ þþþ8µ3€d¡@W‘¶mÍÖ¬éIv €Êâåu=-ìÅáp8ƒ ¢R©Âö,+[»~ã}øðáÞ½{—.]êß¿?ÙY¤Jíz;€Z…Çã <˜Åb={VNNŽì8UíüùóÂÿ ?9 @H 4H'Q{ÎÌÌöÈZØž‰ÚÏÏ/%%EMMì8Ò¤°=><55õÂ… µ³=ýúõþýûDþ.྾¾£G&;€ô@i#lÏcÆŒ6Ha{¦ÓédÇ!ÇÅ‹ù|¾húøñã(Є RE Œ7îãÇ—/_–——';iÎ;W8}ûöí„„===óHh¢öüîÝ»k×®Õæs·eddܸq£ð[>ŸòäÉ™3g’ @š @€”¶çI“&EEEùùùÕæö,$üüÀb±ŠÞrüøqhIAi lÏÓ§Oñâ…°=+**’‡d¾¾¾¿ÝòôéÓׯ_›››“’@Ê @€4˜9sæãÇýýý™L&ÙYHÆb±®]»öçíÇ_¶lYÕç>(ÐPãÍš5ëÞ½{***dg!ßÍ›7ÓÓÓÿ¼ýĉ(Ð 5›§§çíÛ·oܸö,RôüE½}ûöéÓ§vvvUœ@ú @@ ¶hÑ¢€€a{VUU%;KµÀçó/^¼XÒ½ÇG¨8h¨©–.]zéÒ¥ÀÀ@uuu²³T>üòåKI÷žë“L*¤’äëë;mÚ4+++²³îçˆu‹ï\šùîû— ~ÞM *V­mÆ-t›ØFYôÉ :hÐ68Eôzã«_'9Ä…Ý|êRäÃwÙ|‚Qß¹ùü-½GÛ2¨åšÿ'û½ßÍÞO®Þ‹‹NÉÛr&«ªaÛÚjÀĺh)RD3q^>¿pOØå‡IyßE_³a\Ë¿]mFÈŠMv´²=©¿ÒèËÊÎÛVÜI+8Xã~ïCÛÿöãR¦¨å]¬ä€WÖ|q/"=7oh†N}-‹Æfc7ôX_šl/^¼8yòäk×®Y[[“EP©T>ŸOv i#ÍïÄPFœ„WÞûže4³Ÿ´Ò­µ¦?õ®÷iÏÓw¦¼ ¿þ?ïŠÂb§è<".ÖùÈÔí“.d¬· ]æ…=Mc¨«h1嘔ì4AntÐq­“é‘“‡äõ@qç/Àúzxäæ'’ šnÿy¼šk09éá6ì š}9hkÿá79š2„óQ•M͇ÌÔbþsüPAûNG)7—#à ÊZ„%†—ó1ž“?%¯[\[”UUSÍŸÈMHNã*¥ÊÊ{Rú;“1fìÛ°ù5!xâÙ=ÄSY×u Ãȱ­<ì˜r¥?¼&¹{÷nŸ>}Μ9ãääDv€²B®íi·ß°ò)Ðm4o…»[3M-¦,•È ²büÃ*ÌÁçfœ1ŽJ/îUI¥Ñh¢)6‹Í–Ì¿ V¹OJ¢Q‹EQ5Ýðè¶Ë|—ïŒx/\Vúg¿=ç„_úî½ÏîØB•Rú5Áýû÷…íù¿ÿþsvv&; €P k9nøÆƒyE“Ð_wkÒóÂ×CŽ®¥ì;dH•¦Tp ^.W¸Üß;"ŸÍmõ%èŒ?Îý›J~R’ŒZòBTô‡mš6tMúsÿÐÇ8ó >]<ë2@)ò²£aÍÿÁ}øð¡°=ûøø¸¸¸Eš1™ÌŒŒ ²SH›šÿ> ÁI8÷5oÂÌ®;¹g–‘72#ž²óv|Hãu/žÜ”$ѵH ÔKÙ!¸²Ÿ”£–†BW¶uokëî¼Ì+lz§={ßÙþ~GÞ6_dQ³rŸÝ +ü¿Š™GCZ9‡%ÛóçÏ»wïîíííææFv–ÚBII)++‹ìÒº–“wšÓÚèü"ýpO+Ãztöç7 ±Tã~Mˆøl"7%ôyŠƒ£šb~äpD”Ïæð ¢H;äs9¢Ý,y\N‘Ž*Þüz‹ÓŽÄovüã{¯W‹Ú÷n¡¡ÄÉ¿~ßkk䂨7häÕåF?wŠ (¶b"ô–KÄδ^æë¢£”ö­n7k1žT‰ƒØhP„­›#ÚxÇegs‹ü¸ˆUìU'<>ÙÈ&rê8;'KNZøõk·E fŸ½]jæY8ÂÃûté²mÛ¶ž={’ BP k9 Ó±Wй‰so_}“Ë61îKmÜõøêú£>y§©iÚº·õœÐa¼k…_ª#ÕpøèÓ‘¦í|ÇúÌoêl3j|} E¶lO*ún›z²Å"ûà¤}¿! ?®@y¾±üeÃSÎþ³ªYþà2GÍ ½ØA¼U¡içÙ®~ÄÝÇÿû|c~ƒ§kjÛ÷w];»ÓfŠ5ôš*cƌٲeKŸ>}ÈPQ(Ð kèîqÅÝã÷›ûÍâõûå†û_Ü‹{¸ÆÈ3+Fs»»˜óÿ@¡›vï´»{§¿ÌòMÍcã,ÞQÖ'Uò žÄ(eée‹*ßTìU¡×±ÝÚŽíJYzMD§ÓÉNQëÈËËçää@Ú @@U@{&…œœ›Í&;€´A 4€P ¤–²²rZZÙ)¤ 4€Ô¢PjäIª9h1 @H-ÆápÈN mP ¤–‚‚Bvv6Ù)¤ 4€P Ä€ µ²²²ÈN mP ¤Fãr¹d§6(Ðb@f E àŠ*„ Í”••ÓÓÓUTTÈ =P Ä€ hi&''Çb±ÈN UP ¤™¼¼|nn.Ù)¤ 4€P Ä€]E‚ƒ×® %;E-¢¬¬˜žŽ+W  8[[²S@1”””233ÉN UP «B÷îÝSSSSRÈÎQ›P(ê))Éd§¨EllÚ<˜ìP Gv ©‚]¬¬¬¼¼¼ÈN€ ͨT*¶@H 4€4c2™Ø@²P Ä€ hiÆ`0p%BÉBft:Åb‘@ª @ˆ@ (ÐÒŒÉd¦§§“@ª @H3*•*ÈN UP Ä€ Ídee¹\.Ù)¤ 4€4STTÌÊÊ";€TA 4€P ¤™¼¼|vv6Ù)¤ 4€4“““ãp8d§*(Ðb@ 4€4[³f Ù¤ 4€P Ä€ h Gxxø•+WÈNP¹ºuëfeeEv 0h ÇåË—÷ùl·h¢Ov€ÊòþÕ—ÔÔTÇ }P €4 ¬Õ]G6$;@e¹ù…ìP)P Ä€ h1 @ˆ@ (Ðb@ 4€P Ä€ h1 @ˆ@ (Ðb@ 4€P Ä€ h1 @T¾€ R(dÇ€¿C YnL´ß¾ðàG)™|áw²ÚÝÛ{ÎЖ#;”€Lì·/¶OyöQ¥^Ï%Íô‰”{[Bž%pxùwñ“âN{Þ¹û‘a;½ãˆîÊÒ÷ûZêŸ H+ü¾ 7ýÎúg¹³‰-\Z3(„ŽfrÌ뻢û)AÏîF »tֳÑq[Ô§“›Uâ¤þ €ÔB 7öýÝ÷Âÿ+Ö3 åïúLQwm7Ç^–‘?­hª¥J¤¤ÝLKFjÐJ!õO¤ 4YÙï“ò&d‡RääëèÜÍhl?Ï[/2QΤ…¶ •¤ŒÀÿîë·êœîìMêþÚ’¥å @­ƒ @AöWVþ…Zü©7¨Jföf嚟‘þ…§¤«J~-d|¾rä;GQ·¸;ËÿH„ @-ø üŒäГa—N%0†uŸ=Tì=#ø_.…>É E’sH 4ÔB‚ì÷±÷.~xñôë§Ï,nÞ-†ª¢ºSׯ¼çHCU™Â¹ßŸ¼¹y1&"<9)#ï$sT%%}«ºvî–NÍ™r?6³Ÿß[4óC¶èšþ$ßvF_ã_{úðst¼°#Ó4lêwšdçhB+x+íÞž§Á¿Ä|æäŸzn˜Ï¹‚Á´FŸwmBOñó ô{‘ÃÝVß~é ÍÂT"<Öǯn|zû:55[XÄ)tu¦Ž!=ýÅ·>¡Þ¥í¤AÂöÌ‹÷ ÜuôKšh UÓ™Ç[ç«Çüw᳈Q‚ѮӪE:Â{rßY>?&Ct+Ý`ò™v N⣷w.x’¦1»÷ŒŽy»›ð“®zõ&%1!3#;ÿc]^¯±A«Á¶NÖŒüíÞ¼/a—.} ••÷]âË5®/óUpñîÙË(½ô'XI+ ÂP  –á³þßÞGGQ'ÿU_étÒ¹ [$ BhŽˆ!`@AN”cDAUW™ñÍì]ßÌÎè¬ã:OÇcßègß‚³^€Äk$Ü  È‘Ä$ݳ“NUÛI'1~&±‰ýý<ŸVº+]¿ª?ú}SVýêè²O_]^îúÔ‡Í_Ü»WœhtÕœüüäŽOKKÊ“¦ÞÓÐþÚÏ®]¾Á# ‰c~˜;gHœ9à-ùüøúü#ïm?²qrÞò5O×lºvÂSo:v¼¼þíŸð—½ÿ‹g ŒÖØx‹Á¬ø4¿ká›K=†ÿ™2ÎÞ\qzSjî c2]붯ÜÜjÊ܇ǎì~+¶lè»9ù¦çÜäu®úÉÇ*ÚÙ ­Á½þß׬Þç }n¼oRvf¬¡¡®hÃ×V4M7(ç_é—Ø´úÔ»g>ý£úm?[ñ×//øC¿¡-ªUŸ|ñöMGÕs¯ÇæÞðŸkr¿øÁ+5ˆÆúÓ[þñÙòýÿ(i9SžÒºZÐUòÙǧ×/™™o ýIòþîÕ› ßÝ]\ò_³ï£„þ&Ií“»Àj~eÇv§ ýæ/ØÛê|£=U'L—ØÁn<øÐi4€¨,[µáåå¡Ê4]ûØìÅÓãÏžô}Ë1Éï=»¹õgÍâõuM§ï=÷O3¦e´|[›8ðº ;Ÿýåׯ­/§$<¾ÄÞ|ûŸb²§ ìg¡†1Y‹¦/mmþ ÍW»÷ù5Y[/¼ÅkÖÔäÜÓœµ†ØŒñýBƒ)9jÀP@›RsÒG¤~ãze½Þ¨oûZ³Àéå›êY$ÝúüÔ™Â+Ù2Çôíã{ïÕ>q¬è`Ù)g?PÑ.r-´bз³ƒ1!!ü²ëÓón¿aòð»¹âÍ êÏÿ]sŸ;Ÿž˜“ÜR¥C,®ûÌí)øóW3_r¤èu‰ÃÓFk]Ë„´%Ñ1¡_››/ºƒÝzð ÓhQDsŸZùzÓ9Zݵ9 ¦Æ_˜Sz{ÞUÖ‚–Ô²¢U+š.d°ÞœsCÆù_•JÜè‘?˜xüµ-תÝ;æß<éê6ç5½Òãc[C1YGÝ?:kÝ–£ª¨Ø[ÕxO¢¥ó»áumú¸®ia`Ö˜ôóvB“5ëjóÆ“^áÞ±³~ò¼øNßB˜<óñ “[vÐ2kÞ—¶nMŸÜgÒâô¡‰çí»9apªøÌ-DI…Ë èo¿Õ+úà šhÕÛ¿üÂZP2g¥}sÞ´Göü…Þ¤¦ïE­rûÑM—-]ŸÜö©ÚJÌ éW¶„s붺ëç[;îT%.¡o²8êš×çS…¥ÓU«ÖT7_¤l°'´ù#Àœ l!ªO4D|—>\ßïþ[–´þ ³¥MûA›CÓeÆšUUtÂ}ð@ТI t{eóB\Æ€vn)S,Écg…ýe»«›,©övN¥šR{%‰b§å{jç[c/±]E oLë¢97ÔŽ>¨eSN5lD]ÙhQDõ¹Êó>˜R:>q©úÝÎ`xÍDK;wžéâ-á‹ü.W±ßíyMÕš#ŠEÀåiP…ù¼­«k›¯í‰–îý~Ôÿ¤pë¦â¢¢Úêj¿?¨uY ^Ù ЍjcˬizSÇÓ#Ÿ[S1èÛi8Å`h954êýŽ.¶WîHýAq¼è gÖ„³“KhþSëKš–RÆ]×}ƒÒ<ýëš¿ª¢WêŒE#³Ç[-:EŽüîƒ7¿èô§_áhQD§7·ts°¡±ã5u1-kªþ ÖzYÄ9š?>G*Œãwp:ó5?ÍqìÛq¤±ü'w›~>üšÌX½§æØß?_ö±7ôvÆ?å]ÿÍ9=ºL°ôÝ‚¦zÉsÿpã´ô³ò•š›¯î¤+üà ŠèŒ)vƒ( Q_êŠ>ÿÔ™zÙÂkú«=šèݶáÔ:OxB7“-Þ‰†3¤:|M÷—%Û;ü¿K·¾¬XfÝ;zÚ¸øn|jH þ𖚦…´þÙWwÃÎ_ñhÑÃÐgl’ØâÂ{l_}pXÂÅgZ ­™( \¡Ô>SýÛfš÷Œ3|7bhµ.éⲩ¾3ë ¿h´Ý¹lêH]C­GUÌ&«ÍÓ~8Ÿ½‘® ®Tú*Ã*Œ‹íž~½â>€¨G@ˆJbnV¦Îy\_¿»ÿÐ-G$]ì<­’8.k ÎU¤Žor{Ç^e>ÿMÕ{,üÀ?}¸HLŒ¦ŠW~úârWÜ­7M5™…ÉÚñúŠÎli¾¸¢¶®Ê+„¹ãµ/E§·4? \T×y‚"éûß: †ªª—ÛîWöÁ@TÑÙ3nÿÑáß¿Q%jNüùÆ…OŒ›iº Â45 5=·Og8ÁϽ]S·f÷æù3g <ûm©ÕíÙ·r[Ómnö9c#ñthÍ}ò­?•ù…ðoØù†®oßã¹{íÅh‰Iΰ;²-çN°l#Ä®j¡~ýÑÿ•d=Ð7!¼7j°ætuì|wFKÿQ_ݰkȼæi0´`åÁ¯öžúF#ë Öæv//=Z®¦§]Vñ^É ÊÒï™víº×ó«Ô¯ —?Pø¶=9-Íb‰QMk¬®+;¥åj6·¾i­'_U5x^aj¾F·+<ß„ß}ºÎc³Æ™.(B-ð[>ÚZh b%éê®3žØæ÷¾|÷¾vw4%uÞS7Lq„¯…ÖÙ¦e}£àp£(_¹î‰ü˜äÞÍë«©ò+¶„ðÙ䀳²Ôeëß+œâÚ¹}ñ«ç6Ü”ygV¯‚Ã.áÝñäŠÃ½RŒê3U•Šmt–I8}ÂWú¸'cx\ÓŸ&JLÆ4»n™*Ü«îËß?*1¦±¡®÷ˆŸ>ž§\t¿ƒƒA@ˆ2˵Ï~jjÑÆü¢ûœ_—W•W†ŠÍÚ7©ÿµs–dÜÚqFkîãs2¦YŸâÐ;[w5ß¹¦·Æ§å9¦ß:lâXëÙÜõÝ÷ÒcŠªÂVµúÇo¬y÷òFÄ{¶?ýɊ͵ÞðzÇvþê–C×L`Žå«?~ôòûÕþ–ðlú·Õ›BeÚÛ6ù7Óos|w¾ð›c§ZòZˆS{wó^³Ý>é×Óouè…Þ’óä,åÕMËó+ƒÛMwñ{OìJ[–çˆkúIg¸øùÀÊWî9äñ½">3oÐÈ©G9óüñf—ØõÜ»ûÿlî’þ‡þøëÂ’ðlÒ¡}Yô׬Ö̹“úIÊÙ©ÿÌÃÇ<ò[Ã[¯9tÚWsÂHµ ›;qÊRö|²w_™¬XýÈÊüaãÿã…Á)z%yÆÄûN¼»úëJí‰Ã"}DÚøÙ6Ý¡Ïv°ƒÝ{ð;¸ö.  醺-ôÏ%WT ¶ñÃï?¼ãµŒY#¾rd{ïÄÿÕ¼ñí½‘¹tÎóK/ú¦ìÜ_¾“ÛÁÕªÊÂ=5–©“{´â=¨ù**¶½ô黢êä–#c£ÃÝ«Ä:w¿è¸»í'YïxçÞ;.xeüùíù|ºä¼Qåjûòä™/Mþƺ†¸Îñ`›W/±ƒÍCD@@£Õ”¼³tÓÖšÔÅ/ôKl{6U1Ùìÿ9{KÁžRá¯r5Ñ3Ú@t"  gQË?ÜUP!t£³E$Ì”ÒÞ£üC@@Ï¢Voº"Ø`i÷A×!ZÕî“¥¡ÿÆöÍÍêø‘å€oƒ€€žEo•¤l¬ðí?VXyõˆä6­ÖìÙ÷ú;C«93,!2C€ï7z%iúøy›Ö¬úü«×{ÆÝ–1$Ë«h^_å×Ñ­'v¬º„œG&Ý5ÛÊtÐhèa”˜ä)¿Ÿ7xságŸœ>òÁžÕ-sÙ¬qööɸî¦ôT+?@w!  ÒŤM¾æ‡“¯‰ô8 Ѐ@@h@ H   4 €$Ѐ@@h@ H   4€ˆ9qØùÉ[#=Š(b¶½õþH"Šß_1xJ¤ Ð"cöìÙUUU‘EtIIIq»Ý‘Er£X¸pa¤G ëÐ"#;;û™gž‰ô(F@h@ H   4 €$Ѐ@@h@ H   4 €$Ѐ@@h@ H   4 €$Ѐ@@h@ H   4 €$Ѐ@@h@ H   4 €$Ѐ@@h@ H   4 €$Ѐ@@h@ H   4 €$Ѐ@@h@ H   4 €$Ѐð} èââb›ÍéQ@Wjhhý;666Ò€®TYYé!|Kß·€VUÕétFzÐõ<O¤‡hÒ³º±±1ÒC@×ðù|‘ÂeéÙ]^^®4ÓšEz8v6çÜnw¤ÇrYzv@§§§‡ÓY§ÓÐ=Q¨žUU -ôéÓ'Òc¹,=; õz½¡™Ïç3™L¡ŒŽôˆ ë…ÿŸfè[.Ò€®Jç@ `6›ƒÁ ÑhŒôp.KÏè¹sç†ÿ^)--µÛí¡žŽôˆ ë9NMÓ˜bÀ÷R¨›ËÊÊúöíZž3gN¤‡sYzv@gee=ú裑¢HÏhà;F@h@ H   4 €$Ѐ@@h@ H   4 €$Ѐ@@h@ H   4 áÿh€<Æd¡EŒIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/5-phase-rpc-processing-model.png0000664000175000017500000016007514770023131026424 0ustar vladimirvladimir‰PNG  IHDRmàx¯Ír pHYsÄÄ•+ßïIDATxœì|GÚ‡µ»b&Û"Û2sÌáÄ1„™© mSJéîzß•®tm¯Ì µMš¤avÈN2%ffË(Y–,†ÝoW×ql'½@ç©n,íÎÎÎ̾ÿygÞ™%cF0 È÷:<À`à`à`à`à`à`àôOG1 «¯¯ÏÏÏ70 “É@†ð0àp8Påp8QQQ2™ ‚ >žØ?!Ä/“––ööÛowtt Ò÷Ëp?ƒ;Š¸ŽŠÅâwÞygΜ9}?±ß¥F£imm5 ý=€û§ÓÙÙÙÙ¯Sú­£‹þžÀýÏ®ß:J£Ñ®^ÿ‹Å‚a¸¿‰pŸàp8L&S÷¿q]£P(ý:} þèÕ‹Åâ—_~ÙËË«¿‰pŸPPPðå—_êtº~G·L&sÊ”)!!!w’÷~øaÀ§ƒ…+  £  £  £  £  £  £  £  £  £  £  £  £  £  £  £  £  £  £  £  £  £hsaÆþÚõ»%Aˆ{pò‚)ñ|&º×9÷Ì®*ÊL=~¦Åh³Û‰DOŸ1)XÁûc0‡!ï̉ôÌ“Ýn³;ñODþ#çMíÆ¡óÒ;@Gû fÓ––6Ø{9aúø+9´ÞËíª+®Ñ¡·ú&SLO p˜ÈuO‚¬†ŽÒÌ“{æPjÐd渤h\Gû}÷ζÊâÖËB¦Ó™._ àQÉОgÌÜVWÑ¢¿åé0™Îdq]åH¹½QÁP‡Mß©Õé &‹Åî¸\;0B¦Pit:ƒÉd²ÙlÆmjí¨)jìºå}!ƒÉæ …|6þã=ýuÁœ]]Mƒá(}½Y÷Kç°Çڤȃ‚D4¸/çÛÍÕ•Ö€D~¡rr—rSéTÌn¬/ÉKOO/oêbøN÷¤àô/Öh°ÓTWpfß‘ŒV½Ãg쫉IÃp½Kù|hy@í、©8õùåÿÈ©×ÞÊLÒ¤ }ýñ¼Q>½Ø‡YW¶ë›½g«ëšš .“›k2B<™xÊKä¡‹:*q¸_ LX\È-pØãÏÇNÈõ.)*Ë©5Ù·TâûÌÚ¡:¹sÝ–´U£JÕÜf%n"S)ÈUiÁ0„Êà Ý}B¢ã‡¦ŒêżAuyéßmÜYT­j¬­×Z‰N4# QöFB¨t¾Hâ?tÔ¸‰Éƒü¤4¤1ÆPÔbè(É=“žq!¿°¸ª¶±M­5Zl$ˆ„b.¢,.Å"‰Láå­ ÿô¶-?oßs´°Ñ$ â#¦ÁôÀè„…‹f²ܰ;¬cmá¹M_¼ÿÝŽóûס³’Ý>X#e?D£€x \7×MææT¹sˆç¦ð㓦,œEsyn¨Ói³š[«rÿþ?¬?~éÄÎOœ±·÷¬Xù•碲…J¡\"¨‘3‰ÂÈRߘً‘QH¨Óa·™Ûjò÷nüüý¯÷Û¿Eg&ñ=Þš#»j&ð^Q͹mï¼ýùî´«Ðþó¯,Ÿ31ÜGÊb2ÿ‚p_Õi·[,&m[c.ž/~l6½ö“psÄÉDR)Û!ç»1„1lìÂÅI ¼àtXŒ¹éß}üÁ/©y;¾ùHo&õÁ3¾<êCT¹¢‹‚ƒ…´5Õ-2Ò›E½/¬“«6å"™ŒãTð¯d 5œ;zòâ¼ ”¤—ZÃl%gË©q`ÝI‘=‚"ãc#…}<(Ü-õÁæDŽš¾æ…9œ[wþúoqYÁû‚ ž&…Jc0"FL|m-*,;\Òqáà¶=ÇÍƾ_&þ$ð1…Òmg($ÁŽJ\ºB—y!×¥–Šs©‡ŽM9‹wëм]I@$ ?5hôÒU΂܂ué•Åé{¶H¾@ìj\©›‹N}öá§[Ž^´s”‹WÿãÍgxò¨xå]MW{2•Æ&†vù2zQÎùÍ¥¶ÛŽ7ôp_Ý• ã÷D¥3qÉ³èÆº²ÒòŒúöŒ[Ì´"1ˆÞ›I~øh’ÉË^ Iⵈû˜)ÜY÷á´ÌdÉ%\}us—¡6ïıS³GúI·Ek{QêÁ#ªX¡´¨j5ÿËŒþ—(„ëý™nÀ)ž‘#†Ä/9g×ל<_òä”x÷/;Á°›_ÌÐ8¯Ý—Z0[[ae©ÚääòúÚ¡ÀË‘ç1jdä¶ôJƒ]•}±I;S$¡C¸ŠZZRwlØžškè‘c®~tª'v«:Å+fˆ}$ f©ÕÙ_ýcb-xxBl”ßùúB³¦âð™ÊEÃýéj¼ØÝ/`¾wÔÂ'ÂQÜ(ðgƒa¨­KmFx‹ò öµ¡¯/®Õ]K˜­ ÷å^Îj©+)×9/Cuó ô`µm 5•¥¥¥•õmË= $">>ZƧßÔ6ð»6j[k++***«ê› V”É)”¾|’æÄ¾í{RsØQS‡.üQo]mdåÄ”„#gªN™ -i{ö—OŽ÷äÞ”^€u—N>]ê;buRŒïozÄþצӮVÕ”–—W”W¨Ú;0•+pS¸#Ö[5b›Q[SŽWTiEm“É %žAÁ^î< ¦wÐÑû µ,f;J]º€OCnª/Ìa¨=øë×¥Ùg³òTÜQ#"Õ|YâüÕÿXûd¤œ ]IH×Xðɳ+6f72…2o…ˆMÕµT—ב“yúÕ§æú\67fõ©ë¾úqKv­^,WJø s—º±¶±ÃèñÔ»_¿ºLƾlIq#Z“{tÃ÷?í<–c¢ ¼½å4§±¡º¦åÄ%Ï\ùôÊ”HOê]˜óðEס¶ÿ¦{J=¸Ìþ nÆÔl4¹ÖüR9\&• áÝŠ.uYaNaay¨Ÿ·«/ò ݽÁ|Ìaé²Xˆ•˜!ÂVmΉ;ÍÊ/Õ>VýúíÅÔ]»öž<©ÅÊœø¼ÏØ¡JBr˜;rï\·qó‰¬ Ë]©ð`Àö¶ÆºfQ;uÞ’¥ó&úŠ™ðuƒv³öâ‰ÝÙzì\±ÆWxɹ4È mW©TZƒç?äß?¬[0È£ûpÔ¦¿xbÇ?l<žU ó¥^r7’©£¦ªÎÉñ3mÑê•Æù‰Ä1ÌÒQ³{Ó×ßmÚ]ÝE–{z pWGkC}³™"˜õïïÏ‹sEVãͤ3ûЯ_|·étI‹@ê#±ÆU}c{§5òÑ¿ýðÖ%k®ÎÛñËþ‹¹9YEu6+lú{[?š#å!xÆŽ¦ôÝ›v?I|…Q‚'=¹d0¿à|zFæ%•oóV ³„žã—<û÷ç–»_ eÇ›rÅ…Cë~\¿?í¢™á€wè¾½±¼¼¦Ã`2™l^ñ3^yù¹±1^ýªS„í—˜’¸aof©ÚR}1õXæ#QЍ›‚ÄìÆæ´}ûÊ,^«&LòR{ImµiîlLÛýó†-»Î4Yb…§”Mƒ6‹AßQ[U?&ô›Zjï¨Íùõûï6ïMk¶ÐäÞ^|š³µ®¶Yç :aÙª§f cßõ/ ÐÑYº:›ë5\·ˆ¹2b2“Fg±¹<ûî¬ sáз46ך1b[ßp›uà. µ7•g§ñ[¹ï óc™ˆ£©¢0ýdZiSÅî¾òô{cÅXî•a\ŠZUm~£g?µ|Q´Ÿ—¢®–²ß¿ÿñÆ¿~ïð }qF ƒ áÊ]š¾í½>Íhá/ö¥E“¤<ºÕ¨-Ï<ùãWÿmÓÐ+Sƒ¸àªr|øæ[›RKƒÆ=ú·Ççó¢8E¿ýêËý>W[aî[kGâ]ÿ;úš* óqÅAƒ£âÅý\õƒY»*4®õ©ò™Ÿ™‰U1-®»a(¥ómGlšºÆ¶Üê‘©Œà/žP1añªˆHÅëÿ¿í§Ú™Ç?ÿwUmQ^s—?ÂÑe¶v/æC-9û|û­”Ûf-[¶d^ŒŸ rjTe©{¶¬ûyÇGoUµëL¯®žï# ]¾}‡ñÌÞõï½÷щ ûÈ‹—ÎâÅ ,]SUîï?þ°«D¥·^êÀPsá‘Mïüëý½ùƤūW,á) ™ÕçöþúÅ7ë7}òNÆxïïð)¨M{jû×o}ð}#3êùWÖÌÉ¥B&]kî‘߿ݴ½Íh¿Œ…aµÙ‡þóáö—æ­\³dÖx¥˜é°ªóÎþòÍJõ:b²"ËÃÇ?ÿj\ÞÑoV¾ôiy»µ£Ëvuš)ôž¹j­_õÚÿÔkrÓNHÍž÷¤y1tØZWšwòÄéêÆ’mß|¥ ÿÛ##ºkï<5å¥þç­·~9V$6oí OŒõgSH&mÃñÍßÿ÷›_Ëì$¾g@ô ?fŸXˆ08)!â×Ê´Jk[Õ‰cióÆFøs¯·¥hkáÉýÇseC—MHޤ#e·Lªÿµ‰ZÕ©¿}õλ_äv0g=ºdÎÔèLrØ­ÆÖÂo>~÷çƒE7\Ãtõ—¾ÿÏÛŸo:J ›øô‹KÇ ãP°†Â3¿~ûñ/{Ö7wYì7góùSÃ=n€ŽT—¾óëªôÚÛÔ3ñÀ#T†Ð]:(6~ĤÉcC<…wn•qI«É9}>»w™˜âДÁa‚›:aª<(é¥7V˹bò’ä0¶¦þôÁËïþXÛU~>§mAGxyÈ B(BŸaSfàvlH÷T&uŸ1ëâo{2‹ZªÒÓòŸÉ “õ™ýûrÊZ$#g-Z¸h¸¿{ó€ _‰º:k—íÚ ‘àڿeÓž“©^‰«ŸYµ8yÓeëëíÞ¥*¹Xôóù½[w ½r,ëNÊÃLšê][~9QÐ ‘™C§ÍžNëÝÃíiGMB m©?XJû¢³¶Qms CÄáÓú:+wWÀ3e)<}2ûb-._9Ü­»t}ýý½$móÓþþ{ï€Ðµ³c©xAár’»ïÇÏ?=šß”²líÚ—ÆF)hWj$4<ŒÖVzV•v5g†¦ÂŸÚ˜ZØæ»øùVOˆT¸‚hü}œ¶ÚâÒu'R·nNHí5.ئ©?ºë`u'>aî’9Sü.÷nü=YU¥W+Õç?|úb=?ò‘ÅKK uà+³µ^ú¾Êå«QÑ4›ÌG)qUî{ÿÀG$õ÷÷¤ãmœhóc^ü×Õ6Y;ëw~ñö?>ÝÒª-IÏÌ~bæP/WçµjÏìÝzðLÊQL~tùâ)£ùÝ»:È=åkàªÒüÏv]¬Ì̸PÚä;Ä‹ÚO%åÊBÆOµç\M‹Åœ›vü\ÁlåpÏ+Ûma¨µ#íà¡Ü6Ö#k'FºÓ ]ωà@×ÏÚÄ=ËŠSÛ¾ûü»ìF[Ø„%/­})iôjæ Š—ûF˜]çöoÙðÛ 5ð…ÇV-_œÝtÇXÌ]Uy§×}·é|M‰ê–0kÁ¤áÁ=èïî.æóè—¿`*ÇL¼uGÝ%uk­†ÊÄ»±ÄwÛ#`ùk¯ð}C¯­y…É|©’G¦œ}³ÊèrwP[gyU~ž˜Éæ°®¯BK:õ±¿ùë˜âËñ˜¶òü¡ã§[MXHôˆQ±ÁŒ+:„0Ü†ÆÆ„¸mNo®>Ÿ›ÝjHòésl-†Zp œŸÓp‹át˜ô Õ¥™Çvî:Þbc0ýÙÕË¢d}Úí #‚06«©¥ê⎾9’ÓHB¸1ãfÍž8Œã²Uv»Igv9aFûŸx£Óa7wiK³Žùãï—Zl0Ó3iÖ‚äåFo€-‹[°|U¼’C|êç¶h™±Œ&AÆü´ÝÇrj1†xDÒ¤ÄPñÕlCÂSFΘ˜rðÀ鬺¬ƒ‡ŽÎéË'£æöcûvÈlĘÊÄI³ÆD^Q’kºÀ/&:8¸áòÍÚŸžz¾Ø‚0™ ½:œHáû ‰ 3OªZòNå•,H$™:ʪµ¼7Çâ3¨W«¢ŠC<ùZ#'èò軣³º¶¥ËJ2X\bÄËÇ!tјy/RV?{07¶y&#hüÄÑ›vík-34Õw˜Ìv’Ë5´Uç3‹5F”%•Äǰ¯íÝA¢» òõeS.Ôe™uí³â¼¨ýÌL 3~XÀ¾]j}Í…Ã'Ï–{\v„QMEÖ¡c!IãG†ã›ó‰`6Ca?kÓih>v`_Zi'‰í›4yÖèéíŒ fi-9~,µ²Ó)>81a˜èJ_¦°ƒ£GEÊYjufö¹‚æ…åð»Ë=èh¿*‡½òï0'Lf0™t%ˆëž ÷nT%ûþêÍ~=¹ý›“|𡞜~ •`¶ºÒœ#‡tÔÚÙ®*Î=›~êtNQ#,TNŸ·ü¹ÕóE·\’q'd3pˤvÚlΫqID¦ó#ÇÞp¨kÂÛÀ{ÿNûe¼èDǺ½øì#Ò9 n,×Ô„xøG{\Ë­½6¯°ª¾#Q½<ì릇 Xà.ˆé¤fcc]›Þ`%ñúÚÌìÆš ÿyù÷ψã]:è°šz½‘2råÂÅóçLà~;ÁC;šJ>$‚][eaNZZú¹œ2+…7|Òü§×<9"@èšÚ#töò5<ú'Ì\‘wþð!ÅiÖ¶6æg>•qæRi+Í#hÚ’UÏ­ž)cÿ±ˆ\%Ú݀Ȝ‘SçE¡ ÄÖzFc´Ñݸ~‘þ7 î«ùE„ø ³Ôê’œ¼š½’'4¶×geät:IOïȸA7oZ Aá_{1Hâ'tåÓXž×Üi"S¥Á‘JÖusfLq— iL2Úa©*o3Yl„ʡØÍTŸyàØ…As®-!˜æ=Âïê%ˆEŒœv–eîÙ{TñèÞÝ¢øòù—-³Ý„L÷¥û•J%¡Æ`rÝE6®ó×/ÁsÈcÒ(ɉš[uV'qJY:$91>µðѪ=wøpÑÜ@!^žNkgÖ‘íçêH)k§Fxò{YHn3iúY›‚®öúœóEf‰çíθmïs¶×T–Ô $D®”JÝÙ×Z:¹âÌX¤9e˜°×«©ÿµ©­-Uð2uxøÈ8·0G»º¹®ïN`mß¼÷Ïë÷ÖwšÔ窈Ag›Áf6ØþÔ'àáèè]ƒXï/ Jå»ñlMgGS^f¾vv¬{ß…&Í^ýؼQlF(46‡Íáð"±XÀ%÷¸Ìû–éôô!æh*H߸nÞ£gÕ(KÀeRÉ®¹"gg‹ÎH‚®íbŠPy#g.šSP÷ÓïiíU™?ËÛ·ù/ÿð1¦MŸ˜æÃìÞÀÓiÓ:]âx‡·è„¾þ†«aÕ=(Â$ò“ÁýÙú¡º%ÏY¾j ±á"†ÙrÂÜZkÿy¶ÁRxúÐÁ“IKSnÿ† ˆ›²xÍS3Å\)©L6Q|¡ØM, Ý°À¢Óù2…T‡Z³¶«ÓæÄHÞØ.ì>qÑšÅSc0L¦ÑñLq¹\ÈMÄg÷²êñ8L­F‹“ˆðeò=”LÇEŒ‹õÓÙÜeÁ=-«¡Uc±K‘©\AO§®ëxa¶.­AkŽÓfª+Ì4WÜ7¼Z~¡ „êï͇‰í‘½f-[–_¯þõH^uîÑÏ Ïþö£G@XÜ„é³'MäëqùÎ`Z̸Y‹rʾÝrH¥ÊÿíÛ’ÃÛ×+|CF¦Lž1iܰè@.ýO±B–,&Ì‹•V`²´^,¬›íI¾rGg]q}½ÁNbɇx‹hÚ# ‚ÈÊØä ±›JZ ,õiÇŽ—M2ˆï(H;t²È0lÍä!·;émâúߨa³t¡ˆÕ‡rCFƒFOH$ªm¬ÌHÕßÔÞ0†gD„'$ñãÐïý+\€ŽÞMpéáçIÃ%ÊlÑ´ÖuX1÷¾/Ѐ)ž‘IÉ)Ý‹îˆ.ö] ŸÃ´•gþûî?×ïÏ¡Œ^ýôê Ã#åb.ÆšŠ­Z´öl³ùÚ±xoÀoôkoòbâöH=q.§¨IÓ”×JŒ3ïß5tñêžšŸ$f’‰ ´{ÓPθe¯½óô,~=„.à÷wúëJF JxÂŒeóOW~u ­µpã‚Cƒæ ñºÑƒÒ€QI)òîø•[#1¹bŸéR ‰d¬iiÑ™Bꟶé"L÷ KJNfÂÝ™àêb¿Ì5VÁÜS¹w/IuK pé³Ûºw†‰SnsQb(%¦ó<ïåo~õTbH;Hã%äÓ“²ø¹¯¿ç=tÏ‘“§²òÊ5ÍugUõ9gÓvîHYñâ«‹’Ã8D¯ bÉ£×üí­ÐAñû;›•_ßÖVœÙ\ž—yxÏîyO>ûܲ©R6å®ÇŠ"táˆi3†œÌM-l>øËÆx_Á„X_\,,ºæ›Ú:ßÁt6uÎèPÅ@ì!¦ØwÒŒ¤­§Ê«uÖ¼S'ŽŸ™ãÕuðàñ.qôÄñ#D·{öP›xÿÆu ~BoûNÜpÔŽ¾8-~Ú“½½Æ‡Ùã…Èx·é€:zW!–Öw›Ìæ°Ùú·}\÷î28w;W¨9çØÞß÷_ÐX%Ëg¯X½d† {€sZ„ܺÌ"ò‰YðtXÊŒ…¥ÅE9NN=v&«¤"ûð7ÿuxÈ奄R`˜Jé¶±v™Êå D=[Ü;¢ }f.YvêBѯ§«ê³ölø)4T¹6RBïÝ~[åà>ýíœK*Ï-82Z´£Hƒ:ë‹ÊkÛ Jîmö3"¹Ö¨T®DÁéWаKЉº½³B›é~%œÝÚÓ;­pCkwºÈa1Ï CþÂFcV‡Ùd»Ík° 2•F¡v§o£røaï/ÅS÷4zUèài Ë‹òó3ÏO=ž–UP—{|ÛG]!gR„´{*”§›ùD`âÔ¹e%ŹYgIÍ8_X[þÃÍ|©×³3bïúÛFñ~˜ÿˆ¹± ûð«c§6¿þlñ/>263ê5UåzVà¼ù žyzàmBNø¨)#CwUŸ«·4fKݯh6Ëj‰œ±bT¨ì¶í£ßµéŠjv N9ÎË+în—E˜Baº‰Q+.ü¡ˆäòît´`6}Yq‰C"çôô"KÔNÌ3-ŸÅ಩÷Å´=jÑU6QHÅ¥ôÉÑÅM¤›WØ3`ð蔳fíüñ£÷¾;ØZž»ÿpöŒQAMÈá³É$½ÃÒÒÞi¶£ýîÇê@¸¢‡Ž]¾dNNñ§%㹃Û6Çöy,™×ßðÊžÒ†)üa#†î=Pª×UfÍ®âs»-°¶¢Ão~}(yÕ;ó£%wœ‡~Ca‰½ytÂlNƒÖôÇwÉcNcG§QëÀ½†›'¿˜Á‘Jˆ ßFMs‡S°z©&ˆÂuã{'˜Æú&ïÞVÞˆ¥Ó E@¤Ü/|Ô¸É3fe¬ÿôïveWž;vìôâÄpéÕM­ „"ù ‘úÄ 3söÌý›>ÿ×ÇÛšŠwí:3?9ÜW@ïõ2ý‡È7j̸ˆ}Gšyn ƒ½mf+îŸ =¼'Ï‹Š*1ïdÔ¿×+29iè¬zÃ|aïfã9c#=påÌ$)÷ö3:¨MGÂEð”­z³ºÝà o›C2‡-Р:‹M­íÔXb ýèhÿ@5;ÖÞä½ôÝgpk~sϵ[š+¬‰Æ“û‰ïÅœv“Ig' &£w¹C톊⠚<È[ÄpíÝÓ˜\ïð —«ÓŽœÛWnl¯©é²£ÍÛSâ·›ÔhSQY£ÚèÉæý[dCkèÔE\Èúhã msѯë7ÆÅÍìuœ_VÄ›>kBî'Û›uµû·ï™>ÔwˆRØ›]ÅœíeùYÙÙ‘–[­eøs¡1Ýã¢d¿\¬µZô Zt¨×MÓ«Û›Úˆ5©~Á^bÜdÂ<4Pt JÝÑÐX”WmõÒ*!2ßßG¢A]6KÕ…<Í‚Á|Ú-åÍfê(+mp íÞm‚&G>lÜ2uiÆ©¼,®½QeFIL’¹¦´Â.ôñ÷`»Æ³a*ƒ#ñ<ëÑÇNHßr©]]Y¡¿²â]³i«¶ÿôíî\ÝŒþçG†Þ¼¶énì;S…£’“¢?r¬L×QŸªž6|écɱʾ¼¯ßµ ãµéãçÆÈiѵ¶ªJ+Õ‰^ìÛ<ÙÍ]ª”Ò/élmå5•µmƒež@Hï:@Gû DÒ·64g¶=žðÅ m•Ç3j-V”ã4|DØíÚøÿâ¿4b]…M¯Õj;œ˜¼—>¸Ý¨úýÇoáÁK×>2ìênf f äRÜ\âi18„7‘½"ûIòÕMm%ÓrÂáÞ7±‡ì3Ü‚ç,Zz!§tïŦ¦ì=?|ðB ÿÎßÖ !LŤ¹Ëó «×íÏ.Jýõ»_‚äÏÍSpn™2†š«+›Œ”|6$¥0QãÆ{ïÎ-èЕf_ROäqmí<†9ÍyY¹EzÂï#!t‹)’Çâ~¼¬M]~lßöI‰¡Cä·vÂ`ºo|¬·x[Kƒ±úüÞS¼ƒo5}¨o)úú¿›ã{yùß«¯ƒÅ}@¡Xî΄IZ2…áR.‡æðÖuÕ¢ ÿ\5ŽK½zÄàJ<*1òÌâ l¥uï8­Ç·møfÓ=cÔH?:ò缞 Bd‘‰ã†:WyÚè$Q=ÂÆOïÝ·WÉ 6¹n^1q~; ZŒu¥§Ž¦Í¾ÄÖ«G Ábÿð(ÿ}¥¹Æº¬C‡‰|DÁ"ƒ­‹î.@GûD¦QaTU•“SÑ" “ЩÝ/LÀœ›A]{`óº]™5C–4uAJ´÷}ò#ˆÆñ’ú(¤Vmñé‡&Äyø»1qdz³¹:-5½Å`º1Æ×Ù^Yœß‘š˜-û øSŠ9ìÆš²¼R‰Ì…ÄGˆß!®rÈÔ ÉgJ·6´•nüò 1™=&RÀtm¡ƒ¢NâÚv “Klæp‡ù‡)~C'.™—ž_¹¡¶ËpöÀæ ÿ¼h´ðÎ# !Xžðì+k­¶w¤]üýÛ÷!»aå¢ÉaÞî ¼ž/(b¡©Ýn3é5eÙ©ÛSÏvÚiä{4Ç!Œ áÓf$®Þz>ãðŽÉq‹Çs\‹Lœ6K}~ÚÖ=©UF²gÜØÙSSÜ]3ÖU8fêœY'/l8V–{xËBÎÊe³cƒäLÊåíwˆW¡X-$*›Ç!ôU–0eLLñÖ m噯?ùœ ­LŒòåЈƒ‰×µ8VÆæqéT2 ³7äœÑÊ¢"{ ˜dbµ›4EÅù•Nš›"(<€ˆ1s  Õi™‡&Ž‹áïNEðÜb¨Ã¦ª.,h5èÜá1"&í®—•ÝÐx&õHAm'IPüóçÿ.ô—³iä«ò{Ï ŽÀÓ7(,4HîÆ§ô+$þF¨\Ϥ‰É›äª±°ác'Ž îãTïj““'O˜¸÷èųíÇwþðmÇâñƒ=¸ صo‰¡¥Y«3ÛgbØ•5@E8v”½é%ùÍ­~ùQ).:RÆ£#ðåµÛl$2‹ÇcÜAüÕ:Ú?p¥‘)-¾ó¢#kìØ‘ñƒ<=xÝØPzñÐÁýûdhŤGV¾´v©Ÿð6á0˜ÝЮֶU–·\!˜SÓÒP^QåÆf2YŸÝóð †Ú,FæòÛ/0‹ODÊu#6”!¶s7iÔZ‹Ó• ½«M­5I˜L 'vøðá;w^ìHÝü™¶2ÃO&°ëÚTj2“…‘ˆçÎfQWU×ñ|•üQSÖ®Ïÿao™?wFL€Œ†YêŠÎþ¾áÇÌFKôijÆÇ°]ûÿ‘î“/Ë­jþq{ZcîÞ7Ÿ/Øà+`!f½¶YÕÐdöX¼úù¥Sõ"¤6üP-nfk»¬®ç³ªjªªkEƒÅás.·‰>ž—Ÿÿþ–lSKá¯?ý(e$Exóø.Ój´-55ÚËc­¦®ÖŠŠJ;—Éd°…¢Û쥑þÃæþß$>ë×mÙºùãdÜŸ2~Rʈ(O7ÞKÂ{HzMsY^fzzzfN^U£V–ĢܯóvXõugK9Þ÷è¾/{KCMyeµˆÅ`²¸|^O[%b˜ÃfÖj:Ì®AN¼y4·´ëD›Å \ O‚`¦dÐcÏ=§1Ù~9’úé[äÎúʼn±¸\YëŠÎïÚ¼é÷åòA «V¯˜ï}ùì6î±g[µöÏö-ÚóÝ¿³m ôóUðYt’Ó®×´Ô4;Ç/Y³bþH¼Í1݃–­z¢®]»épÁ¥Ãë×Vœ‹4(ÐGÎ"£]íª†3ꙵk&ö"Ò¶koú2ªfLNRˆ`›®ðܱŸÚ\ƒŠ'̘7q˜_wáC¨µôØO¯¿¢_0Ö/&lk©ÈÞõóºãeþ ‹Í!b"N»Åh4¶´]y­‹YÝÚ®á &ƒxÃýÖmžxÔZ×*e¼ùèÛÔ¾"*þ…é¥H»X¥­Ú»á‹ƒÔ×=ÓµTß=*aêSO?5yˆ/ù–*‚Ù ú®¶Šò\¤5•U2²œÍæò¸LBôz`ÜØ„È-•Ic§N u¿:#ŒYºNA¯ªîÐ[]Ÿ z¼}×ÔÛD\ž@À ÷»6ñ'!jÒÒ••õŸm(.?ûÞËkŽþì碚–úªúÖºòzb!£«ª²²ÑŸ! (3~â‚ÇKk¿X·£²öì'¯­<ú[tDhˆD@·›ºÚZ4ðøÅkžz4ãòLFck«ÚÄ,MÍm2–˜ÍfÝÅmÃ>€ŽöˆÌœ|å[«÷ðH•+’MzâÕÉÓgLÅ»n¹=ScÇÏË]·w߉Œ¬Òʺì3u9ç2…ÁK}‚¢G% ñ¿¼á#D–™÷÷we¡Ñ;Žœ:_\Y—q´?–Ê`{(”ÁaQÆ$Gzà9ë9iþlìlvÁñ­ç÷mÀ[ †adËMùÌߦ͟7+ÌÃ¥(7d:œ¥µ>ÿçI›#6Ђ©<‘lÖӯϙ¿`BŒɪ>¶mkê¹Üв‹x·’A!5çþöæ? ý‚Ã&Í[2Lf;°á§½'Žþ±Í³L¥¿nØ’q6£¤ÁBg0ºªN}ú¾3eÎÂG'âÑÅãYÕ¦Ó²~¿ª ½éÕ©xVVS{cEê–ôv–ì£Wãä=¿ðÇnP§ÿüéºÔœšº†šªNvúÍ—žöRxÇŒž²bÅ_•A,ypòØ1¥4xò¨ˆ+{ acûñ-_n<­j¨¯®m ÍÀq~ËÏœÛë\B! ¢F¸‡™ûøµ¯Xr/F`qâ‚gâ’+ëÛ,ŒBg»Ë<}” 6ÅìöeÂJ“ë²0Í-Ç“L]±vèÔ…-­j­No%ú¥x~˜"w™ÒÇ[È¢Üps"ö‹ä¹°±³–Ô7µvv™‰79“©\X®ð–{ðɽÎIA¶äè%ìA=}‡pež7îK OYüÉwÍ&Ç•"ƒ…R?)Š«iÒŒ– ÀdžÒƒÛ—9[ܧ` dqI³bGOjoQᎠV×e±u_ˆp\p/’ƒ»B‘w•i·[ìHfz…äÉ ´‡=( C&döð"-˜Âò Ž! ™¾ô†œñ<=n²_0™®ŒótpÜŒÅ5 Mm3®ôx1b‰·Ò[Ì¥ÿaÆ /‘Ì;îß¹ O¹ÅW®L¯à¯à›Ïñ‰ãuãgKê€ÿô1K…)ó Áúxüµ,ÁdEÐpEP_Ghü¨1ã¢þðyʸ¾fõ6ùÁ¡0<<ýñŸ;JFÄÊÁ”ý; ¦p"Gމìë5 2ã4Ȼϥç’p®wPD_NÁK‚LãúGúÜÜ`n8†Êä{ùã?·I ¡0Ü=}ñŸ[&Ef…H¿u Q‰=Ô;[–0VÖã7v}ý–¯>ùxã©È…¯¬X±LÉ£Ü袎q R’¾üÝ }k‹®Ób=. †šw\²÷­ó溈#ñ‰‘ÜøBó‰ãÓ—íwmâMŒæáäñ‡$‘øOçÀU,÷Ãn(Õ'<Χ—jÜ £à¡sÔž?°iÖjʰgRÆ{qoÞlÁµêU sÒ©x€Í¡3zÜPè @GÀCf­*)¯lê¢Eñn¼žüLÌin½TR©5b¢˜¨ ¯¾¼M 蠣ࡢˆÅ1—Ü\[U˜—ì7DÀ vOë#ºv›¡£é̾õöŸ¶ ÍŸ1$PÂQè(x耨A£'Μu¾å×›>ý—Q5g쨩P«A[S’{âèá§Îë™ËžzlÍ#ÜîþîЀ¿@GÀC[µâ¥7eQGN^HÛþåî¶îE\0…Éå{Hä ‹Ö&Ž0fh”Œéî £à!×K€¸åÏEL™×РjéÔm®ýI „ÂdóÄ…BÆ¥!`‡<Àt<¬àbJ÷ð ÀîuN3@G8@G8@G8@G8@G8@G8@G8@G8@G8@G8@G8@G8@G$F£Ñf³Ýë\<<Éd6› ^~  £€ƒÁ°}û–ÖÖ* 4ເӉ²ÙÒY³æ»»»ßë¼À üFÿhÔ‹Oz+XXùÉʽ‘_LBä^L›è™”¡JÍsŒ˜øêcR“Ì£÷Þž ¡R8v‚üضZ×}ÅóÑQ *[@ÃŒ¦ý?æ¿ûcSüœ°Õ˽½¸P{yó7å¿ý†¢Å¯˜,ŠOö›w¡éØù¦„¿ðqowIS­Þ¼±ôûO.aTòë+½Å}S¤ŽÚæü\uu£Ygt’`˜/áÄ vó]é•c˜¡ÃPpQSÕh±8H42šÎ¥5;½Öv1S—{¾C­·»µt••BN.Ëê¼Ýõp™¬hþò¿ÅgQOO6Ãi¯®Ô‰‚Þ)PÆ„0ÔQz¢êÛªŠdoÃÑiªY_;ÙÕr¿1Üt±þ‹/J/u"J9±XËʺüfEü{µ˜þ¿(¨‘ººº×_×Ñ‘#GFEE¹»»³X,þ³fYBvÕÞ¥5ë˜Ãb5Ø1ENÿÊiµ7iÂe]îöÀT2“IÁlF½Î‰Ý­`XK‘¦°Î*ŸÆñ•Sºå‚ï¡)Îë¬n° (—¾¢–Œu eæu–YÆî¤Ž¢(¤Œõœ²Ä?RN³iu_>ê“õ1q’àBÈ`Ú¿®è‡s^‰~‰§˜Ž©.Ôüí•ÌÓf¢!4-v¬rÒÕÑ|ÇðÉAÿ\!cQaÆíܧՖ½§lÓ ÓÿüÄ41µçªJk…QŒðŸt5m_~Rx@Ã}çèIC¸¤ÎίÞÌüâÛb7¥àù‰¬ÛK»à|ýã¸Y#x§íÔ–ÒCïš ~1gΜQ«Õ¿üò‹ŸŸß¨Q£pA Ç••B¹ËËD0ÔY›Û´mg}Qƒ•JC0 UÖhm—chÉ š¯;9­ÒÒÖiubt\ÉF«¶Ã‚0hR å® ;†ª›-3IÀ¤°¯Ž‹@•CRK»µ³ÓŽb”?œQ¸ —Žv9tí6\ÕÁp¯x uTìë¾äQ7¥/“Ž@t˜äA…»ôj³#Ùu†ô4Ž!Šw— ˆaWŸ(·à`öÁ#ú¢*ë´•I¦»:ýT™Í¦ôa,Žä´¡š²N½™Ì`Qt„E!äç©EŸ€:k.ªŽeë•óG  8‰-+þå÷’ógÔ­£hê ½ÑÁa°)t™A&'Î 1Cü>/v0,%%U‡ …ÝŸàB‚ îŸu+ þ›L&C.®ÿ³û`ü°ëÿD\ÜôçÕdñ#¯º}7¥sÓ‰W/w•ëÿìå«?ùG]4 øo‡Ã¡v‘››»iÓ¦ˆˆˆ!C† 6lРAb±¸;dìŽÁŒum?|µ­‚¶òÕÈiCùLØqôC¦ª«ûk&Ÿ=aºüè' ûv׆yøøpÑÜCuYeŽ!)~#Cîæ„d÷Ý%q}¢DÑ@¤^î"è"à~àÔQ›.šUUf+j·ØôN·¬./u8õf¼ûÓ¨H·•˜Š;Ôj ˆP`QŸº£nÃw‹_â‘BÆðQ€ÓêTåuhºH¾[U©¦ƒPh¬Mk§!¨¦ÝØEBÄ\Ç‘–/?+0·ûŒŠÈ%L·~xø- (j2™pUëü7®1NçåÁhü[üÏ«º‚~ýŸÝÿ¾éÄÓÁ÷øgw²× Aðü\ý“ìâJ† ¾Iž¯þÙ­ëWÕÿÇõ.fw²*•ª««ëjâv»½­­íøñãiiinnnAAAS§N3fÌ]R «ÍmKËÐK¢æN‘*9ˆÓj0á«ÕƒÐéɇϿ¨Ûrªæ½úV:™ds"fG,ZáA¹kA1•J%uYœf;‰teÀßa²éNŠ€Æf÷<™Žšl]&¡ñݨTàÞñàé(f·å­^¿¥¡¥(ä *„VuuÙ/*‹®¤¥åjUFk4Ž`Ævcs“™.dù*©‹ƒéÔ¸i!+Ëm?ïkø×sªo}ø#’³æùމäÀ(ªm·ã2ÓXÒ¾ãWk÷œ6RìTß¡B>‘87de½cË‘ª¿©óôŽJñœ3×gh0“Ò71e±èÁÁî“'O–H$×JàF éåOÌE/ßìÄ›dÕéâêa7é1þÕM^ít«õÕ»“Å¥W£ÑxýÕq‰e2™x!àþ(ê‚t‡`˜Ùì4Y1.…p½/çẛFíö’ Õ%mÉ«Q“cX0 bq(\•˺åí %Üý롹.†â×êöCaI˜PéA®k0ªÚW_ C[ʵ­z‡_,×[ÑS†¶–wÖV›$>îaA·‰*œŽb¥M?}”›jrãýA“¢ÙÔ~À 9Y¦ïþšÁgOœë}¼²rçoå †¯?ßyþ`UV#îÀ ±MÂEK«ÚÒs¤£|¨Úm©Šˆñ¶Ù‰Ñ ƒF¿wcéÑóNºW3UÃèvú(4ÄÓ.ô“ÑÌᑇ­®ª£¼N ¢ÁnîtÚí–R!d˜Í†-ú®â fÂ4ºÈß}ÎDéÇûZìoôY(wc’ êÝ»Um Ás“äA"¼‰uw0«ÉÑe´ã·nRë~ßÓ˜§gN_꫸{Î1è?šŽbh]qgv¡M6ÎcL_ĆVŒN¾&I0™7#pbšúh«ñä¾ÊÓ0D¡Ñ}yð¸ñr_þ-/±\—.⌞Â’è5kNÛú2¿9^ŸV/g‹eT2U›P*“ÊcÝàfâ—ÕèàHx)³y#ÆyÍÎoùüÝÌ­iõçªbÄ€æÆCI÷¸4ŸÏ ‹2dHtt´T*e³Ù×&ß X'›¹Èëýïk_{B­³ðn—˜Èí±Úìƒé!ƒ¥Êó;Öì£\›&Ó©ƒÆø½ñväHªïpÙ°ð¦ô-+ò#†{¯|Ü?ÖÖÛXDâx°‡“ú¢ñ͵g~Mžòè$á§ÂMÔ²£ûKÿ/¿Ñ 5Õt5¨Ï¼úÈDwúÕA[Ôq)½öC‹–JB[ºêZì3V„?±ÈS¶4î)œŽ’N”°´(êèžu¢ûuCŽVë©=Õ§›iOGÎaàv‹É¦Ð(ùºøJbzÃlÄùØ¡=ã0Ys÷U›#•‰aL:›êá6j˜`ãñf£ŽEY¤XÊk¬ÌVTšÝ#˜Wç²0ŒdÓ[Nl  VŽò£1¹ô8¡1ÜÙ:“ã.Ê<Œ¸»»¯Y³Æ××7** ÿݽŠôOÚÍ"ä/Záå.V‘È ÷ÿÙ»ð8Îó^ôï”í½`±XôÞ{gï”(RU-ɲeÙŽ%–çú8ÉÉKü$çÚŽãšc;–e[V§ %Š‹ˆB€@4D!zïeí;3w (’bÑùþ¤‡±;;óq0;ÿy¿ùf&)ݶÌä~·P‹ý×zÆ$kÓ⃴IÑRröZ%×”£ò¥¦Ó\x{[PjhPxnØþ]t¦dœeDÛƒâƒoztDÐJÅC_Ë2'‡ 9@ HÍRKRøÜ?©7ÕŒ¶t9…Í#%Û¯ÏHT^q¡1I™­Ê„x Éò±IFk´.+U£“Qx<†ÐòZm9J’–`yhœ:Û÷Ê[êõÌH×dE‡ÃáõŸë’Ñ;a/}«µô´Ç'=Û~LÊÌ– ´˜Veæ'«Ä©3‰E¬û\eÿñ“ŒQ*ŠŽWko¸3âYö±ŽçxåÓ!!:Ê10q²zZ¤Ë –JDTXZÐÞ=?;Ôý‹_É&÷Eˆxçp²Æ(C˜Ø[÷^[S ¨ž T’¶®‘ÊsM¤!Ý"´w~×@’dTTÔ7¾ñE½fôbС,ÛÝæ€5Ügá`ëÀÐçýglyk›þŸ_·­ë瞌àd*î ‹Œ+i~ó|¿ª¼ß÷h )¥›‰6XDœÓÞÙÉ͸otˆ$ÓËÂ%Ú`í}k¯zKª‘go–gߨ½dX²ùñ§#”wÜ¥Z@«-G0¥=þlÌàï»~óoï+ã’ôÖXuPãXMQ×ùë›÷†}ÐÔ\SÖ×pz.«ü—yz\\`FØ~”}_†GY¬€'¢aÊã~¥é/§F‹§Øë€"RöÅþ×"eólòl BheYu9 ´B¾WLVp€Ý ¥,J“Yª!]f o1‰J¹”IEqaO=X˜­¤Âý{dûÈôÑïU®îùðTÄÆ$¹%-äÿü—jàÔG’Ú-Ö@ãMÏÒRñºg³"#¦í>6IG¤B# dYñ… B/ ypªõïÿÍøÄ‘¾Ç'ë©Hk(žêœÞ½#&ãs²i‡¿™hVËs®ŽÁ›Ï¬F)l¶@¸Êÿ÷-A0w‹©X ˆg˽ÏG;¥®‘²±Sƒž°Od”ФÀ3ìhêÿó»Sª a¡ÿo†È>{}NŠÞÿ§Š‘~1éÑö½7\¦T/¼Ñì¯ça6œy–Å­8«/G¡zŒ.ˆôp0ì‚h5<|8œ2äð¿uºw0ˆ{ó—vÒêý2Ÿ¸ÝV9ÄkùaGH„Œªœ±œîg0ò"B|H?áé«ì­V0ƒ7ºÍ`’§5Œ†Ó U«Æ›,Ð=õ€“);‚!D!-ﲃ0 ûÂÁå·;Áæ•0dh® Ò P; ]6¡ ö x Ìó-IК Ñ9sqx‰˜2ܽÅÎ îÿá·GF±˜à§§Ü6PÿƒÇÂׇ‹òÊ$Ñ ¿Gžu{k‹»”Oùþ|y÷ÿý-¿mWHj.Bh¥Xm9Ú9 6x4,r¨†×Û¡ß‘*øF²ÿÝß6*Þlc¼ìw"‰ˆ!\tÂZ|ty¸ø·FÕ÷Rô›F_\¢¾^Îó‹ú>ê;æâoÐ÷H¸:»Ð—ìÉ_Ÿ÷wF©`g0Äi i>ìVD«à¹DоР?ª¥²Œ°-¦<ðç ðrĪýý‡1í…³ã a@²ÚVþÝ…«ä{¾”š±eª£×éïiàý'Ú fyD¤R-¾Æ˜ IS³òÞ¯¤ßó•¹iµﻀР²ÚvåaJPˆ.vÁå˜üi*0Kž=:ÿrœ=ŠrtTÿßÓÃ’"uz˜„É5N15C¯¿;‘±?>éâĬÇ?¥uöƒbJöÕ¤/?ó….S@ÉüWÏS_‡Q7ÐÄÅ‚RˆR!}<Ä07¨éÙx赺!8ŸŒ†(hÿßÛ¬0èÀ]B-k´Þ½¬ ¨Ðdshò2¶!t#«po~y×Ù\V]B“ú áO~‡åß|åWuï)hšàÝ.ÖK3ŸO{èáÐx3MWÞò[! P|ö&à×'äŸõÊ•öÙÚ«Z%ħæÊ{ác§.B­«0Go„ Åâìûãc7„OØ|Ÿÿ¤'I‘2£Ó3bì C!´ÐÖXŽÎ"H•^¦Òß|B„Bè­ÅE!„– æ(B!4˜£!„ÐüaŽ"„Bó‡9ŠBÍæ(B!4˜£hUâ8ÞåbNßr7d-Ö$Ë⮡yÂ/Z•òý÷+*&–»!7A°òÑ23ÃNL„-Á_Z“0GÑê£Õj÷ìylh(k©î?oÏsf³Y*•®àÖ!V«u¹›Ðª´RrÔn÷NL¸—ì«Åä¤×çc—»+ŽH$ÊÈ((Xî†ÜœËåjoÿŸää-QQQ¸y#´&-Ž ;¥ÒXRB¯„ý áÀ¹BÒËfã%’š^þ_š·‘‘!M—»¡Å²ü;hŠ¢òó7›Í,»"¢‹˜Åq× é’" †@N·ÜÍ@óG’äŠÙœB oùsT-N¯Ó.wCZx³ÝÔ 9FD-†åÏQ„Ö6ÌQ„Ö6ÌQ„æ(Bkæ(B‹ Ï"´¶aŽ"´¸°EhmÃEhá8#„Ö<ÌQ„—£Ø¯‹Ð†9ŠÐâ¢iëQ„Ö0ÌQ„öë"´¶aŽ"´ˆðü(Bkæ(B‹ Ï"´¶aŽ"´¸„õùðyã­Y˜£-.¬GZÛ0GZD {~”çX§mtÄÎŒr†¼“9¦&G§\Ú“BL¯„ç"´zaŽ"´¸„õz½w8žçݶ¡ºÓ¥E§êØ ŒÜËwÜXSÙÛ‡Š]¦ÄÂ3ãCĉiŠÐü`Ž"´¸î¬_—çYïôÄPcmeYE݈‹‰LÙ¸i]VˆÔq®øØ%ªˆ¼ÜHê\íy‘ÆBÚ.Ôô’!AFnf`ÒÉëÂR¶o]iR ùhï=S~ò\[ÿŒÛG"Sì†}›7îU芋J½ü»ò฼‚¼Ä¨`µŒÁ8EèvaŽ"´¸î _—Ÿí©)/ý¸¤jŠÔçnÜñpNj^1t|°EÒvêÍ÷þpò‘ûã§GúM‰{Ó•½¯ýáãu<µ-Óì¼PõÚ_«l}þûßz"~û÷?zµÒ½ó¡7G(;O<üqÕúüÄð¤‚'ã3‡:K‹N¼û⯎Ænܼ131R--è @hÃEhÍÿü(Ï;Ç;^þé÷^íýÜSϦ™u úÓj‘P,j¹&*­ðKÏEçž‘Q­¤,0!o×CÄqÓƒøŽ9xðäÎ3½ñÆ‘ÀÏÿôñû¶è¤T‚UNU9”³§WIJ™ú`HLAWã±w^ùÙw¯{ú›_Ý›£¸£Nc„î.˜£-®y÷ë2JczáÖQQK÷¹ªšÈËL 6ªEÔeG)’ªÔ‹ÂÈzš…WH†aD"¥ HÎͤÿ~wo»¨¿åB¯h[J”RL SÈL ;7³ÙlÑÉó>÷Ì@GcÅ©òŽ)Qîæ-¹É¡R3¡Û€9ŠÐâšçõ£B!Ë(³v<›5Ð\¦ªæøïËÅgoݺ!Ò¤ n^.´D)b=n¯Çéqy9Ñ¥SŸIËåþ/>çµwœ«8v¢¬sœ5…Åm{xOB¤O‘"t»0GZ\w2Έ D*cpÖ–àÔÜuõ§Nœ(/ñÂÙ¸ÜM÷lÎÑÜø“œÏ6ÔíV c S§OwôŽy3Cé¹o<ÏÙúÎtèpmç¸Â·ç‰méQf1E˜ Ý>ÌQ„ÑB\?êO7FaÌܺ?1gÓ…ú3˜vs×ÎQžçx^HÑéÁÖcU27$'ÄÄZÒ3£^;þÖ<«,3ÒDçv{Ýã¹>úÁMYqafCÝAóºÛaŽ"´¸ê> AJUÆä ,>gË©Úî‘i;œ/*­Ý½.U'ÈÑWvèÏС²ôµ™_ÿòArqàÖgþö¿~ñàÏØl5ëô¦èä¼{väoKȤ) KP„îæ(B‹kaï (¤)M“@+¬‰[þåyks*,*­‚!Xðn"£Îj·íÜA´Rh!}¢†³ TfïùRTÖ®¡‰ioMÅÈ´…i*ÉEhA`Ž"´¸ç¹i„²g4áBx<0>Z¯R˜ªûó¦†×;G“Ï7’»vÁÔ¼óØíÀ0Ć ºøx]S;ŒŒ€× Scðøã P,t«ºaŽ"´¸%G;;¡£},¨¨€×_gÛΟk-ÿ­Ê:¾5²£!à{ß#„·Ö­ƒœ€à€˜øú×A$‚3g@"Yà&!t·ÂEh-Ös¼ÃÂüÕ¤Áàÿ»”–@{OÃD­egÝ„ÌÓ}aÛ}ZƒHL`µú' ô'®Ë ˆÅþW²³¸=ÝÅ0GZ\‹õÜ´¹c VY÷æíÜ{é•«†]>1BhAaŽ"´ˆ«½Ö‚{¡kÂEhq‘$Éû¯éD­M˜£-.!G…?½^/Ã0ËÝ„ÐÂÃEhá èÈÈÈðð0˲===ƒƒƒ•••s]¯ jµ»aZ30GZ'NœøéOêt:}>____qq±®‹å?ÿó?“““—»u¡ƒ9ŠÐÂÊM³ÙÜÛÛ+£s¯LLL€ÿr•0ƒÁ€Å(Bk æ(B‹"...%%¥¿¿ÿÒ`]’$333 x Bk æ(B‹B­Vß{ï½EEE333s¯9Š£Zc0GZB^766ν•ššº¼­B-8ÌQ„Aaaa¹¹¹s9*Äjzzz``àr· !´À0GZ,*•J(I8`³Ù”Jåúõë±S¡µs¡Å"”¤B=}æÌ™”””¹{2 „ÖÌQ„QDDDvvv}}}VV–ÉdÂ+^Z{0GÑZ044PSSͲžånÈÕ8Ž•JI…BNQ\IɱUQòy[xËçãYN˜|vW]Y³Kvïn‹žfJ{^§gDÓé)fn’¥Âì\.ÿ¢€ $8Þëåx’dHÞåñ¿LQI€ÓÂâüaHŠ$®˜Ë{}Ç‚¿½„Ð$ƒ¯œ!´¸0GºH«‰Îá?ÿ¹³¹ßíeõq³vïƒa[2T¢Ùdb=žš’ÞÃG‡ú&},'„?3êh"v|!‰q;‹Þmñ× U-nþåúî“Ò”máOÃòðv9T¥an²@s„~gíäÁÖ§¦þ{æ[ßkúóï[¶¨ ‚ɪC~ùÛNÙæ¤o}+9Åìÿ"³ëõž1ÏË"´´0Gºˆ IsŒùþB¨æ„$iÚ¤"I×áb9œ6ûÙv©SFjöŒ©<@n2Igº\½}>u¶÷Óܼi†^šÐÿ#‘¦fêÃ,Öèä *rhöã$韑0+Z"^wOhá»/Ôõ—7:35Äɽ\ô—wZ£ôÜÂ(¹lÇ#ÑN‰‹Q„–æ(Bñ} ‹ )1C“,ç2ÞG8Er1yyáKb„"´ä0GÑ*æõz'&&´ZíÌ‹ç:ÊzÞx­W½)å›ßNŒQS¾ÇÐ+Ÿ^B"1¨ö)¾®½þ¥_Ÿk01¾úÊWlèß~5:ͼß#¡üu¹}¼ˆ”*i1MÉe¤óMØYž¿¼?!´Ô0GѪäóùº»»KKKGFF~øaÿõ•wˆçz“Dv€*XC‹(‚'‰Ë«;‚$5Æ©¶Ä$‹I’ŒÏ ŠŒ×ŇJ饨y÷„shÀNk$áÑR¹,1rÏÙ™†sÓŽµúfƒB‹s­2Bd ÙyäÈ‘ÎÎÎàààÝ»w[­ÖÖÖª;ž1!“Qb’™¶óJ%¸¦]ýSÇ_<ýhžøåOÞk ¿¾×*" ‚¢ p¸Ç(³ùo‚@04Ax½S3Þ/y–m®8ÓàŽÉ‹.Œ•0*"o%òsEo¶}®|0O-¿y¼ÿsC“BKs­Ç ZSSÓÐÐ@Q” ÉÉÉ4M/LhdXª>#Ir䣿ï|Ëc¤Y§§mÔãñÑÝmS}£j½‡™ô_˜ü¯.û³òb/%¢´õ–û"Ÿ~Ôj¤©à$Q6zøåfhء߽30Ä(ºA±Êy¼ƒ»›ãyv¤ß1¬£šËÛòloëøñ£½bb¸süÐëî˳_‰Ž äì‰þZË̯^íýá·E9:‹‘ˆŸ¢rƒv¬7¨ R„–æ(Z„Ôf³V+ VõrïvŽèùƒu.3ø¹<“PÒ ¶[!X‰Zőވ²!«ž²_w‘„Òãƒ(5ä@Å@˜Ò_e~Ðy&þÞྱáÞ—Ö:.™R@þÅyÂÑ>ÿizØt½vú'ˆQC„ÒŸ¦¡Å‡9ŠV¯×ÛÑÑQ^^Þ××±ÿ~³Ù¼ÃOƒäþÿç¡õLÜïn #KÍÔ;ØÄ;m ‘2†àÄ|]ñHËŒú+ßLʹßDQAŸNO‘Ä®è]!ÿëÖ DéC³ã#¶¶~WGzµ±9æxÌ(¤®œ;ƒýÿ_rvZüÿ#„–æ(Z)8Žëêê:|øðÈȈ <òHhh¨H$ZîvÍ! q–ù7qYÙpC§£¶jFxI$¥ïŽýJ–1!J.¥,éY§»±z¸Ù)Þñh¤Xæ+>Ò;i}’ü&c–BËs-?–e…ê³²²²¹¹Y¡P =ûøì”IDþÿì[ ÚNJ*ÎÜ%üÕ2p¡„9Š–ÏóÃÃÃÅÅÅB‚Æ={öÄÅÅ1Ì ½=ÏRåúŠ:~@Ýæ(ZB :>>~æÌ™ºº:!8wîÜ™˜˜(•J1CB« æ(Z‡£ªªª¼¼œ¦é 6¤¥¥ ºÜB¡ùÀEK‡çy!AëëëKJJ„º3333==]«Õ’ä‚=¿!„–æ(Z".—«¡¡¡¬¬lbb"11qãÆz½{qB«æ(Zt>Ÿ¯©©éèÑ£B”&$$ìß¿?00!´6`Ž¢ÅÂó¼Çãéì쬨¨þ´X,B‚øX/„Ђ9ŠÇq}}}ÅÅÅ]]]ÁÁÁ=öXxx8Mãö†Zkp¿†˲ÃÃÕ••çÎÓét÷ßdd$Ã0Xƒ"„Ö$ÌQ´&&&JKK+**”JåÎ;“’’Äbñr7 !„æ(ZÇÙl¶ªYR©tÏž=‰‰‰ ýœí#\#ÉŠÞ¤I’à8~¹[q½½ö™år·¡UcEïtÐÊÇóüÌÌLMM ^¯7+++//oiÔ/((B§+¨©q,åBçah–åYöF÷^v>£RáLº%˜£hž„õù|ÕÕÕ¥¥¥ÂÉÉÉéééz½~Ynª•øå/߯3Ê–‹px!¬·ånÅ͉DŒL&¿ùt!ÌQ4?n·»¥¥åÌ™3}}}QQQ[·n]Þ›*P­Vk—ké¡»æ(º=,˶¶¶;vlttTHЧžz*((ˆ¢¨ånB-ÌQt«¼^ooooyyy{{»ÅbyòÉ'CBBð’P„Ð]w‚èæ8Ž*..>þ¼Õj}ôÑG###ñæò!˜£èxžtbbâäÉ“çÎÓëõ<ð@LL >%!„.ÁE×&„èÔÔTUUU]]EQ[·nMMM•H$˜ !t9ÌQt ‡£¬¬ìÔ©S …"++KHPµZ ŠBŸ…9Š®033ÓÐÐPYY966–‘‘±aÕJ… ŠB׃9Š.òx<çÎ+--ŠÑ”””Ç{ Ÿ³B7…9z·ãyÞív···Ÿ:uª¿¿?::zýúõ&“‰¢( Q„º)ÌÑ»ÚÜSB‹ŠŠZ[[£¢¢ž~úé   ¼ !„næè]ŠeY¡ú,))488øñÇ Ãgœ!„Ðí½ëÌÝT¡¬¬¬±±Q¡PìÛ·/>>ža˜ånB­J˜£w—ÉÉÉ'N444ÆÝ»w *“É–»Q!´ŠaŽÞxžŸ˜˜¨›år¹ óóó…G!„ÐÂ]ã„õx<•••Ç¥§§gffâMBh¡`Ž®YB‚:ÎæææòòòÉÉÉ”””¼¼ôÐC111B ŠÃqBhEÁ]qxžªÏÒÒÒ¦¦&½^¿gÏž¸¸8|Î6B­L˜£+ÇqÕ³„àܺukRR’PŒb ŠB+æèJáp8Μ9sòäIáï………r¹|¹…Bè&0G—”Ífkkk‹‰‰¹”‘sO ={ölII‰ð÷¬¬¬ôôt­V‹ÃqBhUÀ]:v»ý…^8xðà?þã?nÚ´IHJ!AÏŸ?/$èäädrròÆõz=öâ"„Ð*‚9ºD„Èüãÿø«_ýª³³ó7ÞHII9vì˜ÃáˆÏÌÌ4›ÍXƒ"„Ъƒ9º<?úÑÚÛÛ…ß}÷]©T*”žûöí "f-w3BÝ6ÌÑE'„è+¯¼r)D}}}UUUÂ+iiixS„ZÕ0G—×ëýàƒ~üã744\þz[[[oo¯£ËÕ0„B stqwðàÁþð‡õõõW½500ð—¿ü%;;Ûjµ.KÛB-ÌÑŲì±cÇ~ò“ŸÔÖÖ ?REÎR*•f³9((($$Äår-w3BÝÌÑEÁó|YYÙ/ù˶¶6!/M&SDDDdd¤ð§P€êõzN§Õj År·!„ÐÁ]v»]¨5÷ìÙóÕ¯~588XN‰D"‹†¡i‡æ"„К9º(„BsÛ¶m—¿‚Ù‰BkæèbÁàD¡»ÁÂçè죧;;;;øŸùjA’TLLœÙl^ð4eYÖãñ,ì<×*Š¢ðysè®åõz}>ßr·bÐ4½Ä×å/|ŽºÝ®cÇÞon. Ñ.øÌWŽã;;m›7?zß}û|æ.4eYè{<ÏEïÜyD"YØ9»\.›Í¶°ó\„5©R©–»è–¸ÝîãÇ?nkë É»«Ó‘繸¸è Ö/e”.Ê*æ8ǺuÛ·Ç,ÆÌW>Ÿ{ûí&–u/ÆÌ››ÏÔÖþ$!ÁNQ‹1ûµc|œëêÚ’““¸€³õx<‡?ó•Ì>4ÓÒå,Æî‰õùÊŠO”|Õ¢\à8Yá\¯Sø…¿þfDDÄ‚Ï\$’DEå&&æ,øœW2…B=0pz‰Š;c„nA€J/6Xï²§¬“ÜäÔ¢¸mir÷žXÍ"-bež& Ly½Þånº#˜£¡•€—‰(•øîººÚíeEÔ]ôï]«0GB¡ùÃE!„æs!„š?ÌQ„BWãyŽõyY–ýô~:IQ4MQ—Îa_œ†ãx~î}aE’ŸžãæœÏçå„iüS$AÑ"ÁyÝÞëÞ#‚Ij5]W†9ŠBèjœ³»¾âÃÒòãm.½%X A‹ !9y…;ã‚tBÊñœ«¯¥¬º®ªwxÔåñIKäºÀÐÔôôü£f.K9ÖÕqîhE]Íðø¸Ç'D))ÓFoßû˜´óýWŽ–qB$ásORêƒ\$ÌÕçq~áõJѪ‰RÌQ„BW#%Öè¤Ýo¿wb詽Ï燩.”}ðÎ*ªkÿæïþ9N ÍÇö»÷Ktñ»·mz2Ĥ#¼3ýí§‹Žý²¸¢âé/þmJ–à½ýõþÝß )x|ÛÞ ­„í<ùáG'{&v‡N²Aëö­ËQ1\kÑo~Sö‘uÿÓû·g‰IwKÑKÍÓ6nUÝUs!„ÐÕ’–+ ¥˜ D:sXhDXDHèLOÙÏ^¯ªã‹ÆéÎ7ÞxaBÿð³=g e¥PJšÃŒÌôÿ󝾸ôãFÚÖÖx´aT¹/gg|¨F˜"ÐÀûtbµ\É&f›£#BÃĤwZ¯‘„TdP¼$s³Èk^Yîp0GB݈¿– Z¢R›®xlfº«÷xU—'Ó–½ì“Ó¡PbKü–¤ðß~pòPç½»f’É|ãÕ¥ÇßÒî¸7<Ð("Õñ9Û±œÔ­Ï'¤š˜}š 1÷éÙ…PúèõY<-£1GB­%<çµ÷vøäÁ!è?ßïàÅZS èŠûf¤X£7ºë»&í9ÁA1©„×{íMµ&&祥¯OŠM4H‚‘]ï~`¤H"[ŠÏBÂE!tîþös-Ð×Qw¤¤¶;eÃYáæÊnŽ$ sÕí§JĈdÀ9ì´1úžgÿNûñëÅåEG”|ôÖoÒ7>ù¹§ŸO ¯¥ûV­”åœÓµmÕgGÆì#¡I¡eIh¬5+à}ºÂynà|ûG;EáQûv…¨˜Ot9F‡KŠÛ[º¦B,ñßk‹ )•Q›’š®b¨Ù¾ž·×Uw7uLθy‚¦Ô:mö†¨(-{®ª½úìðè4'ÓŸÞ¨‹$ÃóÓv¥k˜%¹w—Ïç;wî\PPF£¡(Š$WÍpµµDø-P—ìGè.çvŒ¶4yûUŒH±íÉÿHOÏ·ªÝR Ås§ÛÅËeßžõzüãÇ%/¾œöðVÅêŽ{S+%G)•L_}ÍÏßžùÜó›óB˜ösÝï¿|*tcî·¾™¡¹˜cœÇ~â­Òüû*7ך¼s¸ôÒ D±Vâ)=X|²WÿoFªˆÑÞ¡âWÎüÏ«æç¾µã¡Ńs¨÷W?:^3%ÎÏ5‹Æ{޼Þ<&U~eg R!ê­«ùù釟۸>N3Û9Ï^(oü˜ Þ’ªY´Ç·\azzú»ßý.ÏóëÖ­+,,ŒŒŒŸD½”&''Ož<¨R©(|:Ý™˜˜Àõ¹J‰e–ÂÏnOˆ0"b(ˆ¢‰¡~Ÿ$½ìrQÖ51:2 Ö'‡jäÂO3“]ƪW(u¥ÖªÿÿÝÑ|ÁáÛ„9ºðH‘$2!$5V!±Y[“ÌÔùÆ"d?|ó¿þR–º-öK…I3=8rv„Y·'îØ©Î¢“ƒ…¡aÌ'úD uFvh°IÌŒ©7îNÉ2‹Y7-Œ~æ¹ßþ0|K–Þ@su )šzðùûŸyÄ*£ Τª±)(R$ŽˆIQˆ6oGʃنÙ˜o >žYºçLr×ÖÖ&”¤ÇŽ3ÉÉÉÙÙÙ©©©:î烾&8ß 702 ƒ¤( »,†º ©&] 5AJ*hijCxð8àL)Lˆa]>(¸4oÖ M Ð=.Ÿðý³0«Hÿ¬æÞ÷:¡±zGÀÍ‚LÑI ³Ãùf°9áÒ0u‚¥‚à ÌÂ7åêVó0Ð gZÀ )1pi0­NÖúúY ‰LAs L ºr@¼Â …i Á‚Ô###ßþö·I’V{^^^VVVLLŒ\~—=Úe¡ÿâ¿0›ÍÂúŒÅõ¹Ú"F,b˜O‡Ð"kÊÖܨ·›Î~Ü>Z˜`TÌ}ÍyÎÝþعNoÚ–{ÂŒÎ3VêÍQÍý{òcüŠ„Dª’ȤJ~u ǽ©•’£—V7IRrƒ..ÊDOœiv³œ,ÄEçÙN‡ÚòôÞðÞÚ·KŠ›{÷†„«®Ñå:;’”0«FNÓ—‡þ­¼×阙îž2YµbZ,/¸'Õ%‹/‹ÊÙO’É;lNKZìnD2ß•ät:Y–½õégff`ö 3³::::–‘‘‘;K¨P=žë³”}§;á¾/@ôì+cðÂë`IMù@z`|ÄÿìÏR×ÁÎÍråc:Yœ8/¼Ñûà;ÖOF 7ÁÑJHйàX)èsa[ªík€<˜æ_Ðï_ƒ€Dؘ þ_Ó]PÙ q1`]¸gb %i__ßÙ³g_}õÕ¸¸8a×?¨AAAJ¥òÆŸå9v¼sôBÝPwç ©”‡Ä„GJ';FÚÎÛC´9kSNA “Mv¶Ô÷õ{4áºÈ>:ÕÓfóRk’9:ÙhÒÓ¼ãÓmÕ­-Ó2‹.6=@((‚sMw·ŒöÛ].ÿ3¯iF¤4)ƒ¢õæ@ Íù†[‡[ê†{ûœ24! &Y/ÃxÇH]Qï$/ ˆ0Ä&iÜCã­õÃýƒFΈ?93±œ,"(¯À$—,üÎápüéOBôÒú´Z­Âú¤çõ]žç¦»÷ò{íE‘21£b¨¹}ºÓå¶¹|ÞÏ\†H‰Åf ¬oÌîq³Ÿ• WHC ëbÂîqùoÒãQ"f4âËv2Â×Óá²yýï ûF$ÒÉülá9Îæt;„׋Å: µ¨IÑßßßÒÒ%aK¥ÒÅ8 Áó¬sf|jÆ à›šš6蔊KCsUAy?úìoÞ>þö›¿ŸY·Éý¨o¦¯ítññ”)O<ºçƒ”7¸'Ο¬¶)éû¢‚Ì„k¨¾ô½.t}~–˜/¬4ÏŒmtÒ6%ü=Ó#““F½nU Ô½h%æè,^ØšmSv% P]Ü"}[Å©AKR^v²lWŽægå*ZòB2Õôµ¶!!¼{}UÏ4£Ú‘(„&$e , ­ó×ïtvíØž¦6ÌîJ¸+?í>ða[ÐæœÍÖù¯¢ŠŠ ¡¸¼õé…ܾüŸÏ×:ëý÷ßظq£Ù̈ÅÜU$iN‚= 4ÖBŸT†ÙBÐÿhu@Ñ¿vÀ¥³É>'œ«ƒàDpœ‡Êj(ˆÙ'«’BB:4UAq d@Z$\~œ"’Br6Ô”Bí ¤åB„ÉÿbA¼ü„'ÁýûA*Ô¬n˜‚ï ý“ð§@Í\,…ÖDô9!7ªëá|X`î—˨ c+ìÍ9 MJ¨®‚ xà~PÐU5^0…B~&üåmˆLý€lößbo‡àz}…Â6ÐÕÕ%¬Õ[ÿÓsÜÅÕk·ÛÏœ9SSS#ª¢999Âúq¹\×»Fœ )µE82Qòz¿$3:'\­Ñ3bÖ{áT[Sù¤ó̸O*Ù¾ÙH“¤*@8ÖvÞ¥µjCCÄÓÝlóñ–æ)Uø†zv•¤L+×É9› -И¥4ÁÛº†>~£©Ï%MÌ·ÆG(%g˜h:Ý^UÔ›ÿ@|j’J¢7÷?в)1?B-•þÌ™™ðèM‘ :­AêÃdgï‘7úcv%¤˜f· ~¢¡û|ŸÃwG}·MXŸÕÕÕµµµ¯½öš°>…(Ý´iSZZšpt(‘ÜÞã»9·½¦¥ûõª¶c½ÞÄp£QD¬vg0êïωYo–>÷ùŽÞw«[ßl±štá*ñ\w¤Ë᤬‘ßÛ!uN®mµ¦»ušÎŠÔÊ€›ñðsÀùQ±"{ѹ®×ªÛë&èm©!›ã¬›"ôš¹a©¼0ûéó]oÕvë™  x:?öÞ8“IBù\3•ç;~[ÒÁ™,û3"vEé4Ì"öd ÇyÏ=÷\hhè† „ãá€/00P$-à"xïXGKeß”:.VÒ×pô$çÉÉÈ3«˜¹ï2IË"×?÷¼%»º®²¦è¥“nÏìýŒôá…ÏíOϳêÕBâòŒ&!ó¡×éúÒWΰ,I12Mؾ§žÍO‹˜«G=3= gŽÕôM™b2ÉÁŠ¢“\nþæ£vÕ=Jnæ(7=a¦új›õç%lŒQÓs_õö®ú!jÏ…–Éßûâ±Ê÷NônKT¤—¯u~¼¿ïOÿ}ìc) v 4w»v?»åÉ{ä¤ÿˆ- !îþ÷=ª:öqEñû•AÖû²>÷Xj¼ùâרmŸúà•Òb©ozúìûÔú;z”¼°eßÖq¢ÍfS(CCC—¿È0Œðbtt´°ÏÏÏmž˜¸Ö< ÐÖBøùGPYQ›ý $äÖ@ ´»ááDøt°SÐ8[ïjΞ–íjºÓ~QÂßo ” Äa°ã~èh‚âcP¿ £.†1ï…–F ­pO&4ý*ê 7T³)kŒÈ!úáÒÕhÂw$!0 Ä,%ÄôìËÂ+$å_ëÚ…*Ð_§›P¼ÒÒÒÛúŒÏõ \"Äêä¬ŽŽŽ'NlݺU$娠ëÞm…–ˆÕaÝ£k ‘ˆ ô2¥Z“¯quÔ¼ßd•'FËh™X£—Š$vQ&W3òsR–ñÂÛ#.HR}²lv¬Ï!³#cµ†ä\3玶Ô4x¶|=-/]#ò…âVµÁ(žúeUÑ»b½9)Ô$ÑêaÝI”NL°ÞÁóý5•6SVdV®Q.ö—BÙ¥Õ ;\!Ë•Ö(ÝÜÞ^'ñºúHúN_ ‰% +êÖ×êèè¨P’^µ>›šš8 TTsêDõâõbE^\ØÐPÏ›|;ò Qø\κó~x¼¡ÍN†<)•¤F…€cø÷Õ› ÂÿŸ‚ Ù#E¾·µíåÿö¦ÓjH®jíl°+¾´=#^ì«mhýÿ>>×é€ÿso̮԰ú¶Ž’aæÞìèÍþzóâ6D™|czL€Œl}ãL—7êÕZ±­ÑRy °b ¦ëâw[’EŽá·Ð×××ÖÖVRRb4SSS…ýCnnnRR’^¯ â;_!2Æe=þ?w[\’ÑWÖŠ„ÈY°3,ÇÞkÝ_— Å1»‰Ü.?7ЦE—JuFž¹áéôuOͽ+|š1«±ÇwÅåèôØÈk¿;RBs úì¼ï?™“:[{pžºòŽÞÊ71ÕÐL€Tªõœ>Tßòp„>TvÙš'ÔubJ°qz¸øµÖvmüóûÓcŒsƒ³ R$NÞšõ¯™±_j¬,m>øNýïÿý­A'ùÃç3Œ³{F" ÌNÑy‡m”ío•{ëÓÉd;:I’Ôh4!!!Â.fûöíÂ÷DHeáÝ·ßþÝÔÔµ74Z ië ¼Nž„ÍYªÞ õgÁ˜ a†OÏS [lGðHˆOœ:gj!qûeA{Ç„e©!+޽ u}ÒÙ Í5 `]‰H „ÚrèÙ ‰fdJµ`½Ø¾«ç&Ñ€å3‹à¼ÐtÆ-°.ò3§`?!•J…Ø3›Í·Þr¡ôñŧ§§/½"ü"´ZmpppAAÁÎ;#£"~ðæ×ëóô#@/~ÿ¥¶’Cmº'ã­Æ+¾zÍDf…” ¶TdäBÔþƒ ÷ðDG/¼9@å? ägzÆ›«FD–˜Øsq‡æß¿©‚ô©éŠ—ßïo»dÐ:Sžªë>Û`ÓÆgäÅ×è/›=gE…Ç4o0&Ù-tê ûÄÞÞÞööö[ÿ× ëó³½ÂÎW8dæãóù$‰Jćò·z/8¡Ñ"ššEOJ%ŒJ*fdbCZèg»ßïnsÄEJ)ÁìCÁ áM\"Ÿý.KÂw¨ =»§)WAÊ¥L€Z±=+ª¤©ÿÏzjm‘{5í_#¢Dä•ÛAIútöÈ?è}©ª+Q.£¼Ž©¢¶ñˆà­V…ôö»&¯êˆº©¹ã!~<O߬#GŽ á€{Ë–-›7oÖë —zVæÇß‘˜¾a‰+l4ÍÜ oÞŸ¾äu΄‹D ùËnÅå¨BkØûÄÆû3r -•‰åÒ¹Z”w•ÕŒ£­Öëññ.WËÇ­lF|z„ô3Ó\ƒH$šÛSßú?]ÈÝ^xabbbîG!áär¹p”)¬[¡ŠêÑ@³ùðW©s]·>Ï«Ì&Š×åã•2©öZ1Ʊ¾žžÁ­©0XØñ_ãp H Iò<ë¾…[»’"é½91çûÆ[Õr Ìðõ$Õ©Ú¶&—ä™$óµlÜ„p]\\|ùÑÛM577{½W •~ª©©yã7vìØévSáá·Û4+.G…ã£Ek ºâä‚°q÷6vôQæ¿ù§íù!sô\cš´ù¯?,þøÂçvY#—W¤þ‚–*w<^XW×ÿ»ËRÒÌÏn5Sœ¯£î|=ð-A¥gpœ5.JSÞtÅõOsC”D2yB’œuLŸü¨¢ãÖEË— G…Ýô3Ïowø)ƒ¼øøœ: …ÑP_T ÄZ.«Øx¸mðAÃà¥Áj„ÊZhîS ,l_ÏúkËK]…ÂÍçÁÃaóŸ"ÕAËCMìÌ…€Û9$K@­Æßõ‚ê…ȪÏõë×'%%Íý".½+äè¼çLÉäI[cú»¦k?h6˜å™¦+Þ%%’Èk@imSU_ZºZåžni´›sBu2rîÌœyn/HåŸ ÿýÚ„Ò‹uøÇÆÌ¾Â³ýµ£üPŸõ¤bäWoK¼»­¢ýÐÀ 0“íñ‰W6»!áÀî¶þás+PˆOµZmµZçÎ4§¦¦†‡‡ *4LÈjž?lÿ¨­Mʱ.GY}{‹Sr_^H¬ü²Í‚çÇmö¶ívÕ´+2u‰êk\HÆóÜðàÈ…q‡Å­>~Ñ|—„Roz¬0¶z ò'[L¬©¶—KN‹Lºöh›Ðh4 ¹­òQX¥Â¡žPÊ_zEX½s=Xs›®Õ|øpñm7ÍËŠËÑkbŽºâVMLJz¸Lz±ÿ‘ÌŠÝ_úêÉ–ªÆ¬Ð,õÕ@2KÈþÏ”W½ýâ—e&Ü›a"gº»ß*4™¥911ïm=ÝXqÁ“½-,@-˜Ý"yÞçñy„ƒôÙ]×ÌðxÙñ–PKìÒ£Â>åóŸÿ¼ðç¼/³¾ÃÖdÈK€w+áD$t÷Bò>PÒŸ'°.h: #vh< -„¿—Tߥ5á+{=<B-OKou‹á}05"g= ‰êèƒÚð¨àè!;}NÐp¾jÛa{ÜúIklÚ ÂŽy8 ìÚ[ýÔ- øñ,쌄¿ÜÉ/â:„mÒ¿'fèwg+µ¨¶h//|„8ÑF$¤¨ŠêÚºBƒ¦†† õÖhùI.MS" |.îêz‰Ö+¢¤øÓ.HŽV)‰é8øRçÇ¥ª§ãƒ WÝÁ±Äæo1SWWU¯T¼ˆÛ¸°ÇŠÎíÛ·çççÏu±(ŠùÔ½ŠÃé(oêëàζô7sÒ/nËür†Yyy=Ê{Îwõ½ä³Âat÷äæ´«×Ói{§¢¥<5íÃRÓWò#âåôMcf¸c"Bž-ýÎáöŸLßSü@„r>):Bu~[ioo¿t`¤R©…â^X½éééÂQ °éNMM?~jAó°Rr”wÏÔ•7•×Û2öä{g˜É˜õy!*Šàù™¡‘“GϾU4@¤ÕžÍI0ÈDà³M5žäeI×È{¯–‰ÝÉiaLýG­ƒ<ÍN¾õRÅô¶ø¼DC\AÒçnùñ ¿ùeÉÃ{ÃC-zWË?ÿèˆIFóṲ̈=dCÁÓOÇëx[ʼnƒZ»Lê;ò—¢–CÌÅa1ö™ÖQ2A¶t× _‰;œƒX ùàÈ/àý×!4R¯8}83 °ã Ø•r1\'Áþ#8{º¶B‚ñ:3åÁ9§k 4Âne“áÁ6 õõ ƒ¤`Ž }¡\:øÒ3`½xDÚ =üûKP]í/·¼%ŸŒ32Ï^v3Ò]<¤FÀ‚Ü+CXÿ[¶lÙT[€Ù}A’¦”õ;Æß}¹£œ·‹ä—_¯CPryB¶ålMÓÙ¢ŽIÚiHŒ2¨/e©ÔJ zj¸Æåá•’O?Ƴìtÿ´O$ ÔKD4øÏC¢€pCDŒŽ M:k9¦ßóP”銣MR¡—›BT (4jÆ£d8—ÛMˆ$â…ç!”øßýîw…Jt®]Àu+“È÷äÄ?`UpÿZ:8<ã&¯j>ÁäÆG|kUìsWžíàåWo%B ïpûX™¸ -æ+¡i&™p4Â];GyËûOñ|Ò~Z,ÛšUPÓsØ..Œ Ð/áÞ”$Ia­ G{sË„£“¨¨¨¹{¶à­¸–ÞJÉQIÓ¢ÿê_Bžå€¤)©Bjš;ÊJƒ.cKzDn²ÿR9½R6{TM+”±Y ÏÿWä×Yÿù!¥J¦WQâmÙ ë38ÿ)RZiT S‚Hóð·ÉÊÁ¤Æ Ô+ŒßˆŸžñ¸}þÃRZÌè •”ž‰N.:ê¯>Ó³"4Æl•®¢ Sˆ™ðTÈŽ„»aOè%—uZóÐ×Ó ÈˆÙ'=sâxÈL†Ú“Pub7\«k× ŒöCm#Xr¯»\aÖ>R¨`rŠÁ™ Øñ$ú‡àzg ®Œñl€¹[É—1ïCmôî€XÃu‡ }vA¾ÙÁl•|¡Î %â–WÐÍ,ònˆ i&jcL~ÿ̇ûÉ0ù%)I¤Z⺋>jväDìݧþô†”¡ÖE¥h»kºzƒõñÊOò‚wM¯žRD†G©¯¸¼ DJeö=1ScÎS·”k¥[ïµ*/¿Vzî$% ÐJ}ÞîS]ãzsjœrÁ×€PzÞn½u«RÌÐJ¹|WVteר;ÍiVÝS±ªO}úG$Ñ2F$—ÐÒ¢<"Æ>51H*#sJ(dêG ã³ÔÌMÆ@ð¼Çå<7hµôŸtÛ3“Ц}Òÿ9†¥ÛOÄÅÅýìg?KHH0›ÍJåÂÿÊÐmY)9*|™5FµæZõIÓF‹Îx Ó›CôðŒ\­¸ô£Ö¨Ö~v)¥ÐêæÓò‡™ ÁfÌØOnÄûoEt¡>,‚aº:ý‘&cü7UènƒÈX¨:"H‚þFhìš„’÷¡Cíß79ÞSrœ©„¶ x¨> ŠÛ¡¬ xºà//úçó€m Üì6ç‚p 25UePÝÁ&è‚È5éžòw;Kä0Ý ‡³"€tAc=œ®' Cçà˜22 @h ½mPr x ºà¥?\]̳ÐÓ!÷-áìf8ëvø„ã1Öú>9My^›óº|.—–úÏn2UÚŽèî¶é晫?N©4ñ9–ꆮ€˜€@ÃÝ„¤L‘²=fh¼©úp›Èb*'Þ1:ÕXÚÞåÓî‹7‹8¯Ïåð_êóñ‰d”$иþÁ8ûDí™w›”IN†NL±N§Ð@Î5íšuÌ D`ŽŽs#|NÀÊY“·ƒPéODÖ¿uö…S­)ÉYºON‚úS„c6ÞS‰DìóŸmo3D†Ç~Ò@B‚^kàÑex78<|¸ÉñY¯¿rhû²¬±ÐÐP¡¾_¾qtçð×°"Ⱦ¢=`Ô|ò%'ü·P ‡}OG‚RãÑÙqý`²Â®§aóçü÷ó“HA£2žø<ö™A‹b)ê`"¾ø<°Ð è€ñÀº=»ûŠ)i1¨4 U^<ë)ÓB|.„¤#öš»ý>#‡°øZ™C2 Ñ˜^á°Ý ›ôÖb èT³3¡Á…÷BöÎÏük·‚Úº0ºwŽg}CMõ¥ƒ„Rì¯+HH”5ôõ;\“gψ’³T¡ð!5‘–M÷NHÏrWu{õ€Dkþ^±9Ç(¹êj=‚ÔÅíyVÚxf°£¤åœƒ²‹Tfý®/Y"£"ÎÓSßWwjB¢flÝÃUÅLR¾Å¬©#­…¸]ï´Ÿ?z7ªeloí´BÏ 7t¿×Ñ÷IÓ9L“®Ž[ž ©6:9Ý1éåÁ×98Ù¥…«Å‰ñÑ_èÿΑ–ßTnHˆØsaÌÍ×;¨ÄÅwZЋáR“6K¯Juy½Ss¨?%(‚ò%Ã#„!éáúøu¬ïÓqj*§%†ã¡¹Ñ$…³SóÁ¦.Þ|§Å)µ8F½î¾ )7î_ÆRvKýX-ê¢M¹É“˜|ˆu5÷ÚEô£x¢HLQÞòæ[ë}¥C¶–r8-ãtlD”.oQj‘\‘EŽžÒÒêÉüÒ¤sP>kxåáááYê’à„ä£Z*˜ Î)|0Y+õÒÎx“ðW¿þ¿gKòó“¢T”*2.7Ò¤&k˜ÌÂí+¢M\v~¯£UáiJ‘Y(Õ$¤$ÆS© M_¹öëH ÿïüçß IJ{fRb²LD±êè”Ôœ éC-œÕ¨OÊß%Å­½ÿÐÙQ?b¥§’Àa^·¹wŒMZóÕíÖËÛËçu”‡‡‡‡ç&ÜÂ..%Ѹíl«‡¹m·n#¸=±0Öîð†¥mU±Z|f 8)G$ktÏp·ÕŨf~ÊíoÀ©°Ì];¬üÝË9øñŠèý&‘ûZÅé!"jGári`‹(ð:ÊÃÃÃÃ3–eiçhKC Z“&¿ÃÞ5¬s¨öLEm̲GÕwNàâ¶Z\!VÜ1ãí`¸@“³öé­MÕïž|éH\ÚŽ˜òú¶¤åߌÖJ]EyåááááùïäpÍáw~W.bF{¯uX‹vý`늼髎ÖÎ3‡ÿС–y¬½ívyħ֔ÓnÛää°c²õ≃Yš¬"µ»{:M “Ò¶ì|¦¾åW¿ó/ÃJIøæåéqw—à€×Q¸LŸ©¶5Wž¸Ð«ØøØú‡ÂÕ·y„B±:,"-5Âàè¯sº?¥r–ºÒ7~?YŽy<˜8hß×¾¹®(‹Â¿€ŽNmbJß³sCñ¿½øF£î›ßÛ´Us[ç,s¥£^/ëñÌå¦À M{o,JàáááYL`„@—Z”(ÏÅ&ÿÎù³gOÄ?¼]#º¥8¡ˆIY–Ì&$ j$¸—vc·ö—“%æ<ôÕ§Ÿà6A‰Eb©à3w `ݶ ;P ‰äf.)Œ'¥e™>Vjcôó±;ˆ_˜ÅqQEÅ€ÅBßýÔ¹$q–e½Þ/º¡A"ÚÐ0VPàϽéyxxxæ…©ý´E†å›Ÿinª{ÿПââ“vå'·ÅØâÜrh‰.&ZKÛú.—WHcÖg˜¤¾EѦ¾«¶°twÕÇmDdanÁô Ô}sqN‚€.º…ÿu”¢„……ëƒÌ'#IrJGÌ!Îϧ22rêÓyxxxf† õYë¶}µæÚO?z÷å¤è¿KÔÝLìÂzh—Çãšê^ËpkUUyJĆ/U<Ë2´c¸áZù ÉXèçª/þ×Q‚ ’’RÐÃï%/:Gjž%m¼µâÂÉîQ÷öœ9öº-CZtTdú–mëϾzæÂ{o¿°:'·u4vÛÅ÷ùÿÏ5©/1ë¶ ôyCò(ïpg饒 †õ”~|ܘ“µ"6H‹ú@–qöTŸ¿h¡)oOÉ›/þB„c€tÔmîliO‹Ý $ò8F›k/T•—;X!Ó}ñØÇЬì•1F-ð霌ëòúÁÃÃóˆ(‚Sò÷EeíaY H¡D$"q MžþMú¶q/NÊåJÖ·ö©OM—”T¯—ÞÄ­?ÉÞøcnLV ”©Ô¾-œ0\¨ J\¶é‡Ùk¿÷é·ªŒaâOp"eXLž:4}åöŸp a(±L£ |>^wÑa‚þ~ñÕ«.?ï0}ß12Nç\¥¿£=ŒÇµ`Óÿ ‚Çíegn Îsÿ€mPä§7̈u!áºOži>¯BiVÞñœÒƒ?÷½7*Aʵ!ò»Ÿpð:ºÈˆ‹Ëîìü¾ÃaYèŠL‡€`Yv².—JQ÷«EQ——®V«ýX+b¡l Êå4ø½ä»ƒù6[lfW°Ì4§avº‡­Î9ýˆ@cØâtÑ‹<Ù1¯£‹Ž„„äÈȿ˥/¡i PbŒKKKQ•òòòˆYøÝè½B¡Ÿ;)‘P´yÃÖˆ°h–]ˆ0º…ÓQØ`cˆN§»û©_d3©ƒBÏcº‘fþGñ¸9<˜Ú¥tÞqÑ$n“J¥sT¾×ë¥iϘL…—Î÷OÉëè"iƒD"¹ûy_’ÎÎΫW¯®ZµJ«½Ã^èó Rô .8ŽåË—‹Åâ…®Îmà8‹~/ÙétÚl6ÿ^—ËEQ‹`+4Ôª W­‰ŠK`˜°N8]õ!“É‚‚‚æ¢d¯×]]}nx¸s. ¿ fñ tóíð:ÊÃáñxÚÚÚ–-[¶Ðáèèè8wî²+###º:óD]]]MMÍž={”Ê;O2}YÌf3²ÒÓÓ5š/05µ  %“O±Ð¹@óÖvvv#Ëoþ?}M“¨¨¸˜˜¨¹sñﯣ<ã² Û¯¬¬¬ªª y¢ÕÕÕKGG‘)311á¯ÒÐeìêêúãÿøƒü ðu”Çï‚‚‚|ôð{Év»µ.? •o­?IúSŒæ¹Cãu”‡­ì ]°Z­%%%ccc¨J[·n œYÛ9Å—<Ä_?RåÒÒÒ3gά]»633Ó¿Ï¢`Ž´™¹‹eË–-þº1{{{ûúúP+¥¨ÏÙ¾> áï.$Z ;KäUÝQ—/_F3 såÊ•ááa“inÃDô ¯ì¯ŸõtüñÈÈHqqñC=4qË>î¯ÒPƒG·ùÑ£GùË_ÎÑ<ñ<Àë(G ˆ(LÅÚ¡›ª­­Í÷´¶¶ý8ÃÎsúŽ~Œ²inn¾zõªËåB׳¿¿_¥R-…kÈ3ø >•æp8JJJN:õôÓOÆEÚJyåáð#,H´ätÐMuúôi³Ùì{:66†Ü©œœœ@‹Ú ü8®‹~GÔ7!ù„©A³ªªª¤¤¤ÙËÃSÁÕÈÞõKCE… ¢{|hh¨´´477w‘Nâð:ÊÃáÓÑwI[ZZ***húFª §ÓyöìYd¨.õ£?:<<ŒtÔn·£¿'&&ÊËË÷îÝ»x'Ÿx ?ú£¨œšššææf«ÕzéÒ¥§žzj‘ÆÄñ:ÊÃá×]XE7²IoêúŽ\»v­©©Éh4.`Åædæûe~•pýúõ+W®øž"×™&èª&&&κŽ<<œŽúk+-—ËuòäIdê¡F[__îôYz÷eáu”‡Ã—6haÇu'''‘Ž"ËtúÁ¡¡!$ EEE ² n>A?_LäÄ_¾|¹¯¯ïæÔCUUU%$$,ÒÉ'ž€Â7®ë—¢ºººÊÊÊÜn7L­G_AAÁbl¥¼Žò HA‘ß¹sçNt_577k4¹\Žt)¨Ýn—Éd ]Á¹Å_ãºccc§Nòx<Ó jûöíó¼8ç¾Ä_1‰¨d7£;Ý÷Ý㨕šÍfå!™Oxåá4,,ì¿ø…Ï'{á…RSSsr¸½ÐÁ\¤B 4H’ô˸îµk×fü”Èêáu”gö ¾‚¦éÙ7TßJñéKhŽöõõ)ŠEç’ò:Êà W$ùþ@>(êô‘ezßçÞÄ_f>ÒKd‘øŒ¢^I¯×£kˆDzxx_:דgŽðM@̾œžžäN?ÒÕÕU]]»è¢vyåá„8£é w*@¤}ÞðWœÑŽ;6mÚ„Êiii9räȾ}ûJÖétKí’ǫ̀Í~~Ôãñ”••µ··O?h³ÙNž<¹S˜ñ:ÊÃãºÓA7êlvL[Œø+Ÿ‘d ôÇèè(òéCBBŽú£‚<<~‰3r8Ȉˆ@Øívš¦Q£E íÄÄÄ¢k±¼Žò"HN–šŽÎQJ)Þåñ/¾€¸Y¶U¤”Ï>ûìÓO?Ê9tèÐðððž={Š _ŒÙyåá4ÕÄ—òw¡+2ø+Îè&¾Òø Qÿâ¯u/z½þ樭ÆÄÄ,ÞT!¼ŽòpH>£›,Íq]ÿNQ£¢!Âë(ñãúQ4McŸàÇbç^GÔÁ¹Ý‰¡¡!‡Ã‘žž~3N•ÇÇŒ3òï~/>Ç0â¹oð|~,Ðw³/êû×Ñù5šŽŽŽË—/755‘$–˜˜¸à:êÛŸ2 Æu—š?ê‹×õc¾ÒøGyü‹óÔû¸¬=þ›Wl6Û™3gªªªbbb{ì±àà`ÔÍH’€j;qÝEm¢~Ydë:žÏÇ¿üÁÔ¸.|2µ´Háutþ0›Ío¼ñÆÀÀÀ–-[233jT€ì›æÃ''KJDá“8#?þ¾¢u÷Ä€ø}~Ô7‘ïÇç^Gç —ËõÎ;ï Ëëé§Ÿ_èêÌ$ âŒ|öéb¿µ¾,~7ó}¿&¯£<þÅï:ÊÏò|Q*++»ººöíÛ¶Ðu¹¾vì‹sYð}ÜW÷ú¾7gžüòÝ—¦[Ï3×øÝàCm~±ß.ξŒÌ999ñññÛ\üۉφ@¨Ã‚àGK]CäÖ/µX-žy`ŽâŒõ-Ïë蜃ZI{{»Ûí.,, äA6?nÏ;K|Õäk5GøqE*g Æ<ó̾Zþ*Í7¶Ø£ÊwíÈ-èïï7™Lf¡ëòyŽŽòþ¨_X‚kpyæ_r?œÜI@ysPƒÕjµÞ©ù}¸æžñY»~¹üޝ{ò£¥¿sBñÌþÕÑû^Gç¤L‡éèBWä.ø=|àž¹™'l¡+2ßø×Ì÷å(öKi<<Óñï,þl!‹»ö‹$ "QŸjÊâÂÒsF}ø–ú«4ôkò:Êãw|6®Çu{Cåut©388è“O‹ÅÒßßïÛYW,-”‘xä7¹7nŽëÎ>…¯Ë3wøwâ>ˆ‡àutIÓÛÛû÷ÿ÷ÅÅÅgddD*•ú´sïÞ½?ûÙÏärù‚ÔjI­µZ­UUUN§ÓåruwwŸ;wýïv»£££ÓÒÒx!ä 4ü;‘d^ܵç™%*•J¡PÔ××û†sGGGÑÿHMF£L&[¨ZÝö驿¯~õ«òòrô­m6NÔI¡nÙ1)))³ÑQÞå™ nÆÝŒœåÝzÜ..id._¾Ü`0 Þ<¨Óéòóó°VKj\7<<<>>þĉ¾§¾ïŽ"gt6˜QßÄÇëòø‘ÉÉÉÞÞ^Ô®úûû­Vk]]zŠÚX\\Ü,#ð:ʳèÉÉÉÉÎÎ>zô¨ï)R¯¤¤¤˜˜˜¬Ò}p_}q$ 2e^ýõñññ›Qß„tt–%/©ËÈ3×\¾|ù¹çžðx<@­K©Tþö·¿Ý¶mÛ=[l>§6 6í¸x]ê„„„,[¶ìÌ™3N§¦&*V¬X¡R©° ˜áù]çÜÜÜèè芊 ß‘H”——§×ëgS,ïòø—øøx¤š•••¾§HJÑÿÈàFMw–Íì>¸ßy]ê {`åÊ•/¼ðBoo/L ê¢n}a÷÷­]"㺓Ʉl—ºº:—Ë…ž¢ÞjõêÕ³¼à×½ðø‘àààåË—#¯ÔgpÔ ˜1›bù|F<÷ ‰‰‰©©©ýýý 䥥%$$,ìxà}`Ÿ~)$ɦM›Þxã ß,52ð“’’f/¾m4üQA (jÍš5èééñ‘Éd999³ HôÝ켎ò,zÔjõÆÏ;çv»Ñ²°õYj‰aQ'‚ì˜äädŸŽ.[¶löY$}ùŒûržÀv¨‰¦¤¤ÜÔQ£Ñ˜››;û[õ>°›ùÛŒ„BaAAAhhèää$º1¼ó]jãº0Õ%-_¾üüùór¹™2ÈC}™¼Žòø½^_TTtòäIßÔ{zzzddä,Ëä×òÜ' ÑJHHÈËËkiiA÷Æ’òdÊ   ËÌÌô‹ :;Š¢f_ÔJ—-[ÞÞÞ.‹7nÜ(•Jg_ì}XÎë(‡V«]³f ºC ÃB×åÆÊÅ~k}Y²²²222¢¢¢ü²èˆ×å™ ‘—››‹t4(((??ö†šoPw±>ñ:ÊÃÚ1ÒQtc 3s¡ë¾J–šŽêõú­[·Fdõû¥À¥6ÍÌ3ètºÂ£G"›/,,Ì/“£ü>Þ<÷ÑÑÑ÷AÜÁ=€®üöíÛý5Ëû£‹×Qž{‡×Qž{‡×Qž{‡×ѻ²´ÛbsÐ^/‹ U*±D§xxxxxîÀ‚é(Ëx&F†FÆ&nš "…ZkÐk„÷$S^—µ³½ Tá‘Fîï8ÿÑŽªßÿðЉsnãÿøOßOQ.Õ™{ž™,€Ž²,kj9ñá[ÇK›i\£’ pÆf›°ãQ«÷ìÞ’ªº«réC= $:•¥».½óÜOƒ~ïß¹?Dê·drS`ÚÈÌÝ’Í5G_¿Öã^Š™_yxxxx>“ÐQÏdÛ»/üóü¥vùW¾ó}¢ * g,£½ç¿õâÿïš–žŸþô{YA¢ÏWRÖ=|òÝCxòÚÕé‘B +Ta1ѬNA~_*„a¤P®T‰©û<ß]a½`1ƒÕ ˜‚?7CµË“€l­$þµjxxxxŒùÖQÖë¬:öê^üH·õýͳ'è%¾ãiÂÎÇöÛ‡š~þâŸ^JÌûß__¥~¶n±Þ‘ë—ß}åõ¤Ÿ­b¹Ä†Œ-Ïýû2k4€ÎÖ¶¨ñØ ½>zÆRáÇÏ@°Ú«¡›†ÌLPNKZθa Î|gGàÉïÁú¨…«ñ‚¥­ŽÞÖ±>·!Ù.ågñyxæyÖQ–¶ Ÿ=Õ`S|gÃÊÍô­.1Ò´bÕÚØ—Ÿ<øñã…‚î²SgˇBbUg_ß° “¥m|`}Aë®-~ýÅ—_¬ì ùýDq\~~¢­«¦¤ºÃ”¾å«OlЋIÎ_è¹|þ|y}«ÙîÁ¢àøìõk‹"žò³‡Þ?\Ž…Äæ'ÛZªk;<òðîZ—Káì`ÝÅwŽX^–%Äú¼u[×çF ý=ãºHÈ!1j>€v÷Ôc‡KGà”¾ÙÆ[ISp “ £ ޶ƒËû™¥±,˜`Ä ‘!°„:}–µ–Ÿè&‡gg¨(¿~sÇðø¹—[¢Í •.ò¼ß<<‹‰ùÕQ“íuí¤(<5RCÞîpb© 5QUí%]Ì3&ôò¡7aàG×åôªÆŠóÿúщšoÿò}c­R«S)ű:>5gmAjRR¤KÚþîë‡ÊƒÙ·N@ÛßÿïßüáhKþƒ{7,ºvñÍçq¾îëÿãý)‰G¼üÿÞù¨ç+ûäà4÷ŸøË;WºíÚ_?—kÀûkË*ú™m›Öé™þ7^üͯŠÛ4Ïÿ2?L1¯W)P™]%@Á6ˆa!N=3ówæÝÂë„‹Ga@O!õoE–efgWýB¯wÓ*ᯤm)›â5ò%?ûÀs¿ÀºÍýÝíãf›2nE¼VŒpxç|û£.—uhÌ…‹Ôj‰ûT"”J$j‘³gdÄIÅFJpIpÎÚ§ž}BO1c­ËžûÞ÷¾ùÖ®-Ù+ÂÂâbMBB•ž[Ÿ &0wh°øÖˆ.3\wæÍ·ŽH—ýà[Ï>&!<Ù ÔðÕ¿}íµCEËŸ]¤V("·<õ‡UKWý̯««®µMæ´!Yk¿–”DyÇ:Ê>8òßå#¹a~Nü°0Ø—/B—Âb!/ôBh©…êGÀò<˜h†Ê&pÑœ"'@A:Lßbèkƒú‘ƒ1wd¢*« w X †kÀî¹ñA-P^ 0ª(È% •gá½Ãà6Ð9+ Þã=PY C“À’Ùé §;7è=€aR£zÕãé‚ µ„âr—Ùí^ÃEbÜãòz½,NBòRY—ÝãeÃqJL8xÝ^½Œ‹$ôª×M»Ý òé1ˆ¸p»Û?†eiÆé Ñë¤X@‘Òo“¦=Ü餤(|ɦ‹ãñ#\¸'Í ä\4&ÛÒ[wì÷ÿüôë½>W*Ü;ïó£,àÞuwºQ§€,ê“nŸ tA (ú‡b²ò³“޼VZÙ3¾"Üè{;*†ëfŸ[«j®Ø×¦fš¤$:’éÒŠVÊþëß/5´<¾Rë{'E $fˆ1)¡Âãâz}L•Ùßßv½Áãëq°^Ô1p_Æèb Wa‡£G`ÝS°Aä847Cr È„p­@Ðz¼ðX“4íÝ8(4@wÃÉQH\Ñ °ôÁë/BÀú£ƒÆ1¸<>u* ýí0á„”,0·Â«¯Â$ ®  ÐÅ@N&kÁÚ ¯½ 6¬[#•ðÁK0üصœ«Õý„Ûl½^ÜU~º[±.mëz“»{¨äPcó€0%Oëëî°¦àÕ{âô´¹ü`ãÕz«!5bŶˆÈPj ®÷òÁOHèÊíÑZÒQ{¦³£Çޤ”QÁ)¡yk‚å·ÝÊìd[߉W¯; úŒõ‘ ±rKkÿ•ó½£ÇdAºœ-Q¡Úî–x αÆêNoJr²Läw)Ťº¸Œ´œÓ‡^ñ0~.ÚïÌ·ŽRB©FA2f‹ËÃu±·û»ÓevQ‚`µlf”'F5J½€¾:4꼋®±ž¡ñI+Ë4R_'ŒÄY¤Rtÿ Åý©ßdj7†å"5ú¾öÂ_N¶™â¢uRìZ[¯››ÅóAPu÷óæ±òV@Âèê« {49òh(ÊäÛ'æ@¼"Ùà÷`hèÛ¯…Xä¨r#g´¹.ÖÃÖÀÆå€n)[ܘVÆ *Œj%L€úôus¡¼=·™T±± `áê¸Ú ;w@F8ep¹JÊac.¨î¯=.)…,*I[w´qÄìE—F¦‹”•Œ3«Bb "õAçÞi½bRîÜ’’¯¯)a(¡Á$0µ^LHe!9!AJoùk5ýâu{“à D_EûÙcµV°y­öæ§°ÏØ ]mÊ\&aG†/xm̱î 96rò@ýIšÚ³/\.âgþ—&,C»=ôÔè0^†A=$ARä'‹†¦=†ëž1‚ I‚@¾ ‹| ä|z½Ü(†“x&«Ï½þ~½N&(·ÏÉ£ÓiÛË°È ÂXn¬…@þ· Á jÙèˆÇåñ2¨HtÃíôÇyOÄŒ¶ÉzÝ.ý‹«*N¥=^ô.t.)Xð±•yÖQL,×D&˜<C-ýfošé¶0 –1 õ;ÕAé±Z1LÌx/˰Ü89sëŸB`ÜÀ2·:~ÎfýRŸ9PÈÒÝUÅü¯±[~ú=)w¾ó›Ž‹Wk¾üwœ rÀ'''ÅbñÝO_ЕÐEBA6¼qê;An„ªkY1xÝÐ×W›Àã·F,p·œ†¶6p±yüղYï…˵`s퀞a£gÅz¡³:Ûàò1è<¬†]`$¸½Ï@ý%¾5 ‹z‘„ ÅÂ$c|¤È*ì8Õ68é¤BŸhJMì¸Ò4Ü;® LôL2ZM\¼Ô94xíʘbCnTœR"iAHSyOÃù¾‚|ÕØw¯»£¤½µÝ“¸)>&„Âé¾6ÔzÍ–.&/.¢Ä2¶µ~Ìj ‘ ñûnÜœçîx]ã×Jß?xâ„]©eGÇGݸ*)odž•E* g鉺K+®5NXí@ uay+WoŠÔ*Ü–ÎK§ß«ïêszh\hÈ-Ü¥±—pø‹árÁ@ÖŠ]« –Éoq°îñ¦²ó¯.MÉ[*¬m1çoz2=ØYvþðµÎn‡›¡¤!k÷f„«/üåàÙ:.Dá÷ é»Ö,Ë‘ßr¦˜‰î«§ÿt¶n(,mã†;£”®º²ÃçJj#òv¯_¹R'/¬”Î¯Ž¢NDœ_´Útâµ²sWÇVÅInU€ñXë*‹ÆåËöo‰ÖH=·é(˸íÃc}Œ0(:Xv—+†S!AZ¥™œ ‹aA†‘m´c‚…jÅŸ5PÈx†»;”¤ãŒkÖßö.—«««kÛ¶mþ*Ðo`@H`Y!œ» JÁÝ8< 赕Á« é!Ø’ cÍpùô]JBŽÃÍ(¾}Y]ðÊ‹ ̃ݛ‡ÚÓ3㕦ΧÔ&X»rL7ŽáhDþù¢‹§˜ÏüȥɫCkþ»ýZíº:[m†äH•›°ÚÆÆñD5Er"ˆb‘T.ò´OX]Ì”Ž2ýU]cn«M’«àÌFd@NŒ;G\’–ÁÊnô×é²âUÂ9™ÒâYBulBœèÈ %=ÆUk¶§ãæÚâ7?zõWNÁ>¼<²ëüï^þ°*cÃ×we%XzJ¾ùüõAÛ_?ýpߥ¿|t²aÓSßO55UWœnAT\RpˆÚ(]±m÷W£ÃLòÛ& PWÒÿ§7l˜X”eò¸mVsß…‹Ïm•íÜûL„Ô|ñíøóŽï}ûo’’£}¹Ñ³jõ©ôpÅÙ×ß~¥ž•üǃّŸ”†+‚â£b£ÞzÿcAüƒFƒAJ¹JÂ+Ô…'diZDaþÇu1B¶üÁÇž¸Rû—_£ ö+ëÒUäì³§ízùÇo¼wF“¿uÿ#«u¢ê|/@V4Ýßp¥¢¦=<wf˜ÃXdǘ{°xÒL‹o_ë ¢ròrb?l­,mÊMÑ íC—N]ð˜²V¤%ʨϸä.Š„¤«¿­}h$LNY½·;µ÷˲HD‘3ª×ëgSÎ\AX:ä§À±‹ðÑeB°’s.ÛÛ¡mv'ƒÉØèÝãi1äyèXÃ4¥d`|{a÷6 î<)‡ƒJ^Ü ë´àwÅBòÉwÇp2(9$&¼³©´¯Y,³™q Ÿ#û¹—ÇãôbaÑꦊþ²‹AëÖK§nqLœ°:®(U>m´Œ4Z²`±uJMlZúŠ 9•„w´Ü핈HB*"Rë &…XäõX-û'CH„L¥‘É¡ŒI`ûî5Ö¡ëDË+Kè”gCŒz). ‹P½uôJGÏpd˜Z(”¨”IY«C¥t4üúŸÊ®\]›y³Æ8%M]ŸwàrKݨ١U¹{:t±«Ò"t°jnÞóa˜$$ëéþOü•§ü®³<59.LJ¸:kë›ñüì™§6Ä© Ö·ðÐÝ]WòþÛB©g¸²¬¸WVøÕo<‘h`’§s~ðÂo\õÙË3U“•CcN««¥´´F±"S·ò‰¯>ùükgûOÎå馑–Šs•øÞo|mkNÈxgqËÀ¸Ëê½VU7™çìn¨îš íÞ¦kÍkV&®ÌÒ¾þ¿üêC­ÊÜ>ÈzÍ5—KZõ0ÙZÒÖk¥í=å¥A’üÍÍâëv»ËÊÊrssåò ú%äP°Ž_„lI1ÒL/€ÓÐÑ QBhoçÝÆW1â“@z JŠ!N F&¦æ–‘ËK‚…î.ІÑv°ÒŸ¼…à¦Emc06ÉMÍÆ&ú”œd-d@»“€Z²„5Ã(µ2)ÇxíÝÎS´6aY¬^M"‹S,“jÔÌ蘋f€B÷‰Ãiµ8©P¹n …2¥Elت’»®”k Td&Š•*¡Dèéë´y’åþáYÄ`œ5…áRC´^­ïêm1Ç6vXT:¥”ä^ %ZS"9z²ÏâÉH̉¿xæ¥ßýðræºüܵyY¹·•äµ·\yõÏo¼76â/×§?òìÿŒ¸õ¤6(Ìk®è2[ÄÅßêåº'™+bÓ§yÐgʃbµéX³™f§M†a"ul^ÞÊËoŸ¯hé Ž±Õ´9ãLmC / †¦”•?üiò¶ú꺦ö1ó¨õ™Á©{Šöf¦'idÓ¼ Ú21âÆ°ÄÂÝe&„LeE #óx’Æa©.D/ŒŽ7îý‹]c‡[¥R¬ÞóøÌòê¦1‹M‘ÿݾ¹,;Q# Û&Ýñ«úî206‹Íê´Ø ù»~”Æô^Aè²oþüß.TÙIYdbz¸d˪’«N9666i…¼Í_O¡I¡Û2aq#ýß¹ÒçÏŸÞ±c‡@¨ùñ0O…eYàIƒŸ+I@Bä”ÁÉw¡; ȤÐÒ­áàí+¸0¨(#µ½@OBý/„È\سΔÁÿë‚`#pÑ;¡© Òó (ŠOƒ½trÈ ¿º!E Ùñp¦^} –¯„Œ xt7+‡ÿþ=µ 1BV(Å÷YŠ–õ2.»I ã¢].¯Xèuq9?|«\„´“öxqÓnËŠ¸àˆlSøÙž6· 4CÇEa 1ª’ò4W® ´§)ÃõxßÕÞ¾1"y«I)Ƭ42? p¹2cm䵚ÚÒ“z}œ.Á×sílk™œ‰ c^†TIu:äN,]+…ç6¦VJp£g,ãeØéQ$¨»ž ÿÁM黟ù~Е+çkëξ\r¨yß/žZ7­ReHÌÎÛlg8£›’˜tR18¦—aÅqÙ»ö?¶WÎÙs\Ô@ dÇ;n« †sSŸ ñÄyZþºÄógË/ǹèIyz|¨>@&øf¿ôÛˆ•†ôÂé…Ÿ¢ 2{í3ßúŠnfŽ@Œ”è—o{bùçÌ9Š yëÑcÆáØœÍèqë¹nCBΆiO×ÇåÜzKvá'/åå|~E? Ã0õõõkÖ¬1_öíó‰,žù!¸¥pcíúDøÆO¸AZ’¶o;€>¼Rxò'@ã ‘r1½Æ¿‚M,Å Upytw=˸x"‚•˜R ¦x쇰ntjðV̘ð‚Ú!ìý:&µ$èü' {#X\D2ÐéÀÿÉ’ÛÐÄõÊ+!òöŽ^¯—¡~`hÈ-–1½Í£ko³‘"ÂâèC>A²B@`â`]úêØ 2&”ò-õ"ÄÒ¬]éØ©Žº£õW=,A H+X­%ìæÞÖ1P Ü“–¡!—Z% ‹¶´ Ô]”æ­ .|8Mpª£áxc“„R…h‹ÂÑå Œ.ˆgá¡-ýfû„<$Q+3DK®8&l.P¯Ë¸Ì#e¨A*uغØu»“Ö®í.ûÅ\ª.ÙšÁiOðpʳfoÌšéÅÜú\(Ö”î‘A7&œZ$ýɧß^×dß$íRÅ ­½í% WF­ÌMO=PúÚŽì¬5_ÕI?k–n¾ äýGÙÿHAGIIIiiiAAA^^^àÏDɃn{ŠlSM÷ð¡Œþä1'«7Ñên{.SÄ­§šOf„ejîqÜœ(–h fZ¾{$¨A¡÷ú Rƒ*c‹Õ)Ï ¸åxBÉmíA4mƒ 3C”1BDÍh@Üt“P@Þ¾²vJ>§/R&Äòé3$3^åYòxýͧÞ:ÐO²^‡›-Øþ“m)B OÝðýG/U–þéÅ2–ö8±è=û|4X.“ÂÅ ï¼ÜtŒ¯W¶ê¡MÕ¦ŒÌÍÕÍGŽ|øÇ½›VªÄ7GXÏDkëõz\Èu–\­ÈIŽ“ÈÂ6ì{Î%|¹ìì‹íW5*mxbÎfµ!„kÈ´µ½æð«/]ÇÚánØû?Ö¤EMö×^©­2ã*¼¹¸*B’,Ƙ¢œ¸ [za¸V8#*©£Œe ­»w"ÛfBl»RQ­T«ã´·þrf³ù•W^9{ö¬×ë]¹rå¾}ûÂÃÃyåáá D±)vÕ¶‡öé¥NŠär•PÀuV¤$tÕ®eYÌSy) ‘D!“p¹m“ ŸMßÉdÆÉr9r>SV}û¹ŒÇ½€ ¥jÅm¾&PFf®ûë*ú.NP”X%¡¸WU¡Ë÷~-ù»Ë¨@P©RBá\L))LÙøÐÃÛåA$r…’"0ƘP´í‡y›¾Í"ãS"“úŠÇpUDNR^µ0âʈ̽•¹w¡ëñ¥H$k×®AÎhqq1òP£££P—‡‡g Â’rÎdÏÌFRRµV:ó P¦Êf$(©V?óL.)3Þ€!a–jÐãÓuÁ)…FoR oI.(Õ¾ä"¬k¼«s 7h”mW=ÚôÔp}@õª©£‹riEQÙSÐ4=00pìØ±ìܹ3##c*õ ÏÂÃ2ôx_ãи™¶5]omU&Ç‹©…š6ã6Péi3ÛìÞÆ¦¶®¬øHÁÌ”€Üi–¾’C§. E†Æ/߯“–rVmîH’ }â‰'Μ9sêÔ)±Xœ°-žû–q;hiúò¯DÓ2ÆisºñÂÍœ3´ËæÕæ®zÊ«\v‹›fwÓÇ$†”¸ÈÑ 4*yEz\D ­Øâut®‰D6lèïï?xðà·¾õ­€ÍÃÀÃó¤ÀIIXêôXèŠFGÉc²wÅdßå4‰>móŽ´ù©Ó=°`:Êú¢i/Ã2¾HÜò[ŸÚ^àΣ ÜpDW͹âÊŽ³.¦pÇ®®«çJ꺆푙ëÖ/ n8¶ª¹ $1»Ûj’ÍÆÄb½­æò¥²úaP­Þ¼3'Zyö@ صk×+¯¼RVV¶fÍšÅsä¥[á¡oÄ…Èy½ÜÀp+“h¼,¸ûŸjŒ—+‡KrD.áF<<<‹…ÑQ$ScÝe%—ËëZ†',4RR†ÅIJ®Y¶ùÁ-Ë“Ew\܆á"©R'xá­Ì‘ôÊõz…FÁt¼õ§×L+‹VI=åÇ\èJÈÞ¶nv::e&©ÕãUÇ{vŒˆZ•­¼7 T«ÕÙÙÙÅÅŹ¹¹J¥r6UZ(h;\¯á haÏÃ/€†*î©9ÝÒ1¸|Š{aÓC°6îMIi Wæ±£ Ë…Ý;@¨©Ÿæ–¦mf·ÓAr±ZA2¯Õìr;½¤Jª’à.›ËnuÓ¥Ó‹îÕÌ@·×fv9ŒT'•ÌÌmÂÃÃs,„Ž2®ëçÞþ—ßý¹ËúÀ¶»¶Ä”B§y´­±öüñãï³Ê‚ì$“äN»|c¸D“Ÿ—®WQæ©Ð/iPb~n²bjA.%dfE).v;Šè£Æè. 2>6»‚°øøøƒŽŒŒ,R%%•ígàÜu0»TCT<4‚Òp0 ‘Áð§36sŸ»/ór‹W/4kfîrºd`=VGOuoÉ{×™‚”½{£)«­«¢§äP«dcöÎMF[Ïhù‡5×é GŸM ÑßcTˆkÒÖVÚvöá´§²Wê)^IyxüÁüë(3Öxú…ÿç³C ?ûõOžX•ps³Üe…i!²·j˜Ùí°r—í0æ™L¦ÕjGGGcbbº.÷ˆP²Obã1 Ä2¸éíÏ †ÿÒ 2Eb\öÁ¥ &PÊ¢ÒŒë;ܺH«ˆM7Ôjœt1€“úM¸Òè™ÍÍ!ÒÊÃãÕ˜­ÛµT­žYÃ2‡Åju{Ü¥I%Ä·Ílµ¹iZ¤ VX»uÂát0”F§¼“3ôÅ>Âëæ’Ÿ»= Ô…‹a³ùyÖQ–uOœùèí7‹GVýÍ/¶/‹¥¦ßbTDúªz‡RÀEC[û[ê[{'­NÃåúð´Ôx…¨Wî„þæêÓu6ƒ+õa‰É±j9µŸû§ËŒSPÄT™,í˜hkjêG] I‰TÆè´$ã´Î†uŒôTV7LºAŸ“!$Xûpgíµöq›5!ñ ¦;ElAHGíö€Ó–ó0tö€ÕÅ]6©’b¹}ÍFº¡kˆÛÁ‰‚í) Æû¡â2¸i * ä÷K òÛºÀI‹."LÀù@,04ôuBßxN‰õ!4m 퀎f±ƒ>"ƒî³TõŸÉ¦M:ý}ˆùvÖX—“gŽ`'‡®—œ~ëÄ… Ñûþó™¢4[_ÍÅ㯟¾ÚÿÕÿ³'Uß|õð‡ÿh‰|öÇO?j” !¡í©9qèÅâZÛ¾¿ý×¢¸°@‹Îý4óí:'kÊJ-”>¯0M)œ™ïLa@ƒ{ÝÖ«ïÿñ…òÉÈHLtT6f>ô7?zjµV|× {LJ®zçMÌ<ÞßÓ5j§–ï~æÛ_Ý«3ûŒ23ú›seŽ¡Æ÷ÿü§ÃWú5áaZ1ÛßÖ0@dýÃ?wZ±¬¥¿áõÿ󯽚¬ÝkSB­½¥ÿõ»?\wh¢L s÷µ!ñò_ýüÛqÆÏLºät:ïù¢Í ,tUÁ‡ÇÀ­€ 8F ÓûŸHTœ·7KW3ŒI௾awó8't65#ƒÐ3iëàám Bc·Q“}Ð8 > Ëb¶Â¥£pá:hM ÂP7@8<9m³×œ=ƒRب†P®=Ìé…˜cXÖ9níhp¸i#peTHF†s9š.÷Mp±„X™m ž¹þÓÐv{sI[“ÝI³„&J—®UI¯ËÕQÖÝ3ìæbÁH)%(.VF"‹ŽñŽu޶]Ÿ4{ЧÄd›¦E³–®áêK^¥":;ȤÞÉäᙦ NÍMo.>þúˆ“\ ÈÉO*¿xöИ‹ˆuI©yÅ'×8j½ç •žk2?qÆq·„ùÕQ–uXÆ:ÛF…â´£âÓV·SÔN“•Ç?ôtÎÖå)ØxåÏðý÷¼·{{žV¬¸Ûgàj}ø¶'¿³"Jçžìøà÷ÿð÷ÿù[epôs/#?UæûÞÛ³=OC2'üß_ÿ¡l×s¿üÖÞ5Z1>PøùW¯Ó´÷V©^{}eµ&iŮǿ¹.5gçNz¼øÊñŸ~?yÍ£?ÿÇÿ‘*_b:Õ¯r;oOØÀ‹ÜMâ†çÇÒPW 5]ð7yá ·À—^«ÈrQBN¤ô´À¥ZØ·VdÀx+øöC‚-¢_ÌNÎÍQ¼Øëwƒú=8ý!¤'@z,îáF¤g´— ¥úD«0‰Áë°]=ØP3$,Ú›¡cˆQóøµ/Y2·1Œ×ëi9×~æ¬9ïñÔ¤(aóÇÎÎ&3wÃp­šü$µ3: 0ËM=xiWo—Õ@Ѓu= ¦ü4YàÇqðÌ-¬óZé{gêzøús; IÆjh³j¹ ?£–?`XbòN„Vœ:ÑÔPgÙ²ò. ¡4¦¬Ü¸?DAŽ6ù¯çuäØÇ)QzmüÚ-Aºp;ØWsñÃò’‰G¶+œíG?üs“pãO¶<“$gyr]µZDøÂ2YÆÕVUÒ„%lݳ!+>"¶x¹ùÕQ Dê Œ‚|ÉÅS—Kêˋԉ§ÿ"¬s¨ñBM_BfFMÙ•ÚÏ÷þ~}AJ$k'’{KªÀºmccnÐj”bëêg”IQr½RÐ>4nuÒ3í\¼ò¡¯ÿ oÔþý¿{÷ÕWפ…­ˆçv¯ö‚4{óãÙkìh(}ã÷ÿ÷åÃo|°}wÆîÌÀß'Õ‡R 1\öƒ–lH0q1±HÛoJ{€fÀë—ƒ‰ýâS¾¾¤T´Z€ †ˆh ¹ _¯—ó}­–Ov½' :¤g¡²Òõ\œp}þ¡ãàÈʇ0'üöE8r B÷€.зËû<0œË)u±·l¤£-*̧ƒK<3àî @Ó¶I;«Êº§cÒê•EÆ©T Joøð8%Ъñ6³Óê`ÔÂÛæPpBš¹.•=õRuùÉÎðЄM€šù<óënj¨§Éà„Ø0nª†Ä­Öºq½béi¬©ºTìâ†W{úeA\èÏ]Û+&‹D"PhŒÍ‰ ©¯+uìWãƒ%—J¬À:ªZÚ]N)ÍzÍ×GBJ1馜Qpn'˜ÚjNº*ß©¨.k¿úæÛç îÚ¼L*Ç~ªLš BeaaÛ~¬¸þ¿^üÍ¿Œ5¯K‰2pî)O‹³8=ÜX¿ÝÁ‘«v>²éãsG^;™š¸oUHWÍùN4xölŠPxJËêm’„ÂųÔÃ!}=|? êšÀââž*4\Fm|]- ×AfDG€C æI0dÁ.hYNn_;d4¸<`ˆ‡o<=0ЇìPX»2RA-& žüT·r3¦‘a€Qzùð»Ñ­ÆŽ+gú¬r¯çê«/½’)º~¥Ø wËùTï\³iï3vòÌ™Ïä’-ÁÉ;ÿîáGW'hqK½S™G>ø8LùhÎæ¯ýJüö;ïžûàÀyJ¢ŽÊ/ÈsN6ŸªíSˆ™Öò3µÙ†7dÒ ÷~üî«zÙW„Wüáß.ãAI »¾û³G £‹j6Bt÷¸ !§aùŸ<Ë̼Ó;õ›~ÛôHÿÔY„’ ¸Ç ¦}D yk ïöóã§»rïþ „P·.)8#Âj¥¹EœR¢ˤdâÖ4C¶ÍEc”L(Æc&ÍX ÀÙà#Cb%•âxZØÎ˜`–$$rdREÉ{“]N7㊄L#‘M- Éy8Üèp³¤ˆ’ɈÂQC äbB(׬x<7mÔát³ÈàĔ\I:tâÇ“#q!¥”‘d„qûß®ñ°@IÅ¢@í¡xæŒÔŧeQeoŸ;³}½Z$5W’ÅH\ó°Å:ÉŽ Øiú‹–ÈLÅ‘z]ý×.µõO&-_©“IãtŒ›'Ì ÙfGB‰š¥\ž’z¸ælyU~aJ´$¼´G0”H’ªððè´ -åïÿÓ©¸´‡òcsÞlÖè`¤&"clj÷:=È»doìöB’J(¢îDÿôþñáïxG‘„`\_ýk7FRraß°þ˰\Ü%bŽm|Ÿa¦Â(„2¹ ÙT°Ííñx¹SpP,•ˆ|ê&þŒ2•rCÖÚ] ›\UU„I(̽vý$ð8)T(¤˜·èû¿{ëÛ¨u`„H&aq‰E;<^®ò8ª‰T" Ì_˜g¡áfIõr¹þöƒ¥ ¿5ë/ÓÝáTÐía¥2Þ!PõušÐ[C^R…èæß¤X¨ ½m•‚HL©o½,2D‰€‡#có~¸wðäå?ügó± ^©2%e­O KX‘óQÙŸžŸ¬5é$V§mt ¦¤ä˜¸÷ʈƒÅ‹b42{õµúq'ƒ7üÿöî.¦­2ŽãxÛÓÓ7ÚèKKØ€ñ²°&F·¨3ËðÊÀÙ"‰‰wcŒ‰‰‰WÞšMÌn¼àf^h¶ÄM¯¸0KF–“9Ä!êFM ŽB ­t/àÈXÖh¾Ÿ«–ôœóßËéïù—>ÏóÓ­Pi­ßŠ´úFn^þös5=÷×Ƴ¡÷ÔË/ø1þïbÊ ˆ“V½Šò⛑C‹NÄf±‹¼ýAÛBÚZ^êÞž›ì¤5#ù ÕîÝÝݽþç%Þ=­Þ=žEB[ç©^ûÔå ïõ„¿‚Åll >||´m‡Y Ô¶j×¾ºaÿš3öµö=ÍŸB/ä(òÈQä‘£È#Gw˜TÊ0=}ñ íÆ´²£ÞEldvÖLê]€"EŽî$n·f·¼vÍ¡nÇuÜŒV«9™\Êl¿ƒ›­¾¬¬ló—nÆh0ÎM/NÜšR-:m‹j\‘ÑoÀò÷ĽsÛý:"Gw’ªªð™3& ½ Ù˜Ùl^zú™Ú…e·Û+++·~žPU¤uÏÑÔü=ü>“ØE1)¦T2¥ËÕ…P‰¡µ©ÃfcÒp9º“¨ª?þ…rNmmí;o½§o +ý¨®M¿Q«uÓM(¡§tz9›ýY¯²»„êøõîÝß«« 4Ú#GˆvPÓ4½«žÄd2…ÃÁ±±ßFGÔ«U]Y{+•Òí‹ Š²‰t>Ø80¿ÈQ(*b´v55é¹vîý¨aå %nñWQ€ ‘£Pl,‹Ï·ÑªÍÈryä(òÈQä‘£È#GGŽ @9 €wîÜÀÀÀ±cÇ‚Á W½‹l9Z F£±¢¢¢¿¿ÿÒ¥KçÏŸokkkii)--å×¥°£‘£å÷û{zz®^½:<<|ýúõòòò@ Fív»Þ¥d£%ºO—ËuäÈ‘öööñññX,¶°°°¼¼¬w]Iä¨DšjšÖÒÒ’J¥DˆÚl6½+H"Gõ¤®Ò» €<ç\ZZZûxhhHúÚ6E? ÀÃ~T{öìÕ«WÆÕÕµmÛ¶•*U*R¤HŒ1ôñoÞ¼Ù¹s§L¹jÕ*mäãÇûÆNNNr„ããã³téÒ.]º9ýâÅ‹µžÎÎÎ>|û @´B"^¯^½LpçÎ ”$I’ÀSÆ‹¯š¿ñþÆŒ#±8kа"‡:ŋ߻w¯âß#ž,“i¥K—–C£°­D;„` ‚mܸqâĉڰ³³óôéÓÛ´iä§&L(A¹aÆ 4ã "œ¸»»k!øÌ™3çÎË;·‰O:õ×_é$@p‚ˆôîÝ»~øA)¹Öž¬Ëž=ûÁƒOœ8UCx«[·î?þèé驸_ñ6aÂë—ÄÉáP­ZµÂ¡zÍ‚ˆ´hÑ¢hÃùóçïÓ§OpKpqq)UªT“ݽ{÷È‘#>ôððH’$IªT©Š)âææÜÙ9¶(òññ9vìØµk×=z$Ã)R¤È’%‹T,V¬XÁ-ÊÛÛ{ÿþýZQñâÅËœ9sñâÅ“'OÕ6%_eíÚµe“á%K–Œ3&fLË»h©á²eË´ázõêÅ?³sà÷Qk̵ýÜ»wïìÙ³7nÜxùò¥”#k#iÒ¤3fÌ–-·÷"B0‘L[û~þùgk¡'Äüüü/^<~üxùÇøÝ¼yóöèÑ£qãÆïV¦EÙiùòåîîîïÞ½Ó^8pРA'“à2dÈ?þøC¿RP—0aÂFÉíÌv^^^C‡={ö³gÏLÇ;;;W«VmÔ¨QÙ³gÉ’ØM–W ÁŶnÝZµjU‹“mܸñÉ“'úG‚5 Ç~ŽZcÁzÀ›ü™øúújæ7K ‡l?>|˜7oÞÌ™3Ož|H" B0aöï߯KÜ1½ZèݼySÕõë×õ1NNN3fLž<ù“'Oä]½ íðáÃÅŠÛ½{wúôéú({H~êÔ©“ä0í¥‹‹ËªU«*V¬h6™¯¯¯»»»¤Ó‘iÒ¤I:µ ܸqãùóçÚH//¯ºuëJþøî»ï¬ÍT’œ,£ÄÓeÌ”)“,ããǵe|ûöm£FÂô4™iÓ¦M‡ &Ã6lxö왫««Ù4RŸÍ›7kÃÍš5³¿éݱßc$Yc!æ¨íçýû÷åÊ•;wîœéÈĉËÁL‚ Þ¼yãááqûömîaDB„` ˜†à¯¾úÊ%Ë?x zÜqvv–XÙµkWùǬ‘Œ2a„)S¦h¹çßÿ•ð{öì‘)î({HhÙýñÇÚKI$›6mÊŸ?à)¨'˜øñãwïÞ½eË–é´1’98зo߃j/Û´iS°`ÁlÙ².JÞmܸ±žçähDJ“ÅÔ;qÊ2Nš4iòäÉÞÞÞ’;C°\öÓCð»wï–/_Þ¾}{³ –.]ªµ\j‰ÙÎbû=Fª52ŽÚ~f̘aš€[µj%ëáË/¿4F¶ê3gÎlÙ²eÅŠa¸H‚‰ D˜þùG¶˜óBLÂÇáǵa (+W®¬]»¶éòÏþ—_~)Z´¨D­Jù´iÓ:vìvEéÉ“'ÕªU;räˆö2{öì’2eÊxJ (#GŽÔ†Ó¥K·cdz¾§ÝJ”(!N˜¤FãééÙ­[7‰ÔK“Ú=zTŽ+ÖÚµk«T©b¶Œ'N,^¼xƒ nܸÜå –¬Y³ÊÊvì˜Y…Ï;'ëYŸÌÎû=FÂ5\Ü~Leƒ<ðsrr ëk+Ø DÓœ8qbG»}ûvíÊ*Å?[÷êÕËÆÄ’‡æÎ«Ýjàúõë;vì¨P¡BXeÛÑ£G«U«¦?ÿ¹nݺ‹-Š7®Å‰/\¸ …kÃMš4É›7¯’¥nݺµk×Nñ¿jíÚµf÷¡Ûµk—ÞIÀÍÍ­{÷î6J“ÏΜ93¬ŸS]¿~ýN:½}ûVño ÖÛ,“fà ÈZ²³@Ç~‘p‹c·///}ØÅÅ% ê ¬‚óêÕ+mÀÉÉI2£ŠÕž:¦iܸ±µ(©‰?~£F¦L™¢Ö4ñ8°(Ö¯_/ÔÃD—.]Æo£yË–-¦µ ²üråÊéÃZOS»wï6--vìØ6ŠŠ/žTuòäÉAÎ44䈨fÍšÚyv9>|¸¶6|||–,Y¢MS»vmû#—c¿ÇH¸Æ‚űÛOÚ´iõC‚eË–÷Žu"!ˆ0 &ÔnMêçç'0d<LïS+J—.äôeÊ”ÑÞmÔáEY3}úôN:i·}•ƒqãÆuëÖÍöG8 (P ÈY˜Þâ@¿'NïÛªøß¡,ÈÒdšpˆt’¥´|÷îÝ;w–/_^ñOo=Ò'°¿4Ç~‘sÙϱÛOÅŠg̘¡ ·nÝúÊ•+íÛ·ç¹@”@"Œ«««~~pT¾yó¦>œ;wî §Ï“'>¬Ÿ4wxQõîÝ{ôèÑÚpœ8q-ZT¯^½ ?eD¬u¶Æì™Š_d}Øì¢~‹ìY¡W¶lY777íö ,ÐB°Þ"C† ö¤Oc¿ÇȹÆìçØíç§Ÿ~Z²dÉË—/ÿ'ç1bäÈ‘… ’ƒbÅŠ}ýõ×QëÉy€¡‚#ÿ€õ µg2‹^¼xa6Æô9aö,c²dÉBýy'N,\¸°LS¬X±Ï€c‚#™O¿]ÿñãÇ›7oîb]\\´ö*;»›^Þž0aÂ0**°1b¬[·®víÚÚm$ËÀ¢E‹bÆ b¿$ÕÐÎ>‹mÛ¶…ì©:Óe” dªóôô Íìì—#Gޝ¾úJ6 ÅåHÝ´ñE‹Íš5k°Šrì÷iט»ýh’%K6räÈaÆÍ‘#GJ–,9}úôÖ­[‡~ŽB D Áúð† ¦L™â'''MšT?iûôéÓ éí«ä³aT”E•*UÚ´iSõêÕµœ´|ùrÉÁ+V¬°}ÃäÉ“ëñ¢`Á‚ÁíÖi&I’$ú2>yò$È>‡ædzp¹»»k!ØôZ®ÜÀ±ßcÄ®1oooí2ÊsìöcJþ~KøSüï¦|þüù;wÊ‘žþlH©yûöíåØ&_¾|Žš)€#&GŽÙ³g×n¶çÎÍ›7W«V-ôÅfÊ”IF×Ù³gƒÌ(2>¬?6ÖáEYSºtémÛ¶U®\YË%’jÖ¬ùûï¿Û¸—ÔJïK}õêÕPvµüì³ÏôëÆÎ;¬e k 6ìÚµ«Þ Bñ¿mmýúõƒ[Žc¿Ç°XcqâÄy÷îâ%æÆŠËÚ”Ú ŒCñÛ5ÎÎιýuëÖíĉuêÔÑ.1”œ0a¢E‹Âb¦‚… D$ù©Ÿ:t¨dÁÐ7.\xÏž=ÚðŸþYµjUÛÓïÚµK.R¤HeC±bÅvîÜY¡Bí¬-[¶Èzذaƒµ¾¤¥J•’Êhûwïeˆùúë¯õßÊ€µ‡ŸéLï’Ö\]]å¸hÍš5ú9BÁsUû=†ÅK”(‘Þü,6.=uêT¥ÙæØíÇ œ={vÅŠµ—zÃ0€ˆE"RÓ¦M ððáCÅ¿[ðèÑ£ûöí¬<==åƒò]S²dIý±·Ë–-9rdœ8q¬}üÍ›72égMßu`Q¶}õÕWGÊ•+§%!–L¼yófÉF'–0ñóÏ?kÃ’-zôèš#‡Ò¥K›.ãˆ#ltÆeÔnßnÜÝÝMCpÈžÅàØï1,ÖX† ô|öìY!Øtm„Œc·;™®ÆHõü<ÀÈÁ@D’,2kÖ¬š5kj/åsªT©Z¶liçÇ/_¾\¿~}ù¸i._¾|ÆŒµÖ<;vlÿþý­•0|øpýüræÌ™Ë–-kú®‹ Rž>>òº‹¿téÒiHÊœ0a”)S>|ø ‘ÿâíÛ·Ó¢ì‘%KÉÁeÊ”ÑKvöìÙ’%KîܹÓìü¸Œ”ì¥5àI”éÙ³§ä-É1eË–5Í(ÞÞÞ’]þüóOÉvGŽ‘)åÈ!ðL;tè°xñbí> ò9,éÞ½»,¸þðÛÛ·oË2J’eÌœ9óõë×C¶tȱߣÃט$ò”)Sj7Ù•+T¨0kÖ¬*Uªh\‚ïüÑ»wï7nd̘Q ×+2Ü~ÖùËž={íÚµ%ÊËñ€éMåä#ÇŽ“ã ™F)Ú¡©<G!/vìØk×®•`¡7†IÀî/nܸR¤H!ÿæ‰?ý¥‹‹ËªU«ì¿ëˆmŽÝ~t²Jïø ü–$l™—i¿‹ D"PªV­ºzõêE‹I°Öý1]ºt5jÔhÞ¼yl”–&Mš#GŽHQãÇ×Ò¤IÝ»wÿî»ï$Ø®˜‹²SŠ)´{DhO––À%Jìܹ3Gަ“µnݺnݺ&LXºt©µsîRùòåËËd•*U²q)ùüùóC† ‘£ífm:gggù^F={vG,\„qì÷èð56vìØüùó÷íÛWÖ©LõêեΦGAúíçÂ… ›6mÚ±cÇÑ£Gõgˆ˜I”(Q­ZµdÅîè ‚È%f̘ ý½ÿþìÙ³W®\yðà——Wœ8q’&Mš:uj ¾û?X#é¡©¿;wîÈ?i)ÊÃÃ#I’$RNáÂ…õœáY”é9wÛ\]]µ^§AN6ÌŸ„˜S§N=~üøÙ³g²'Nœ!C†œ9sÚùØÅÿÞ ’Û¤¨½{÷^»vMŠŠ/^æÌ™‹/nzqžý‹$Ge9Ü$”0Xc5’ÿ´?)M´|}rücºÍûøøYN°6³Ðl?9üõèÑCæxõêÕË—/ËÛË—/½½½]\\’'Ož+W.)ÄöCDB0IÉͯü9¤´tþ"[QŽ•Ù_èˉ+Vpïï9ð{t쓘žßŸ£ ´S(·©vV¬€0E€á‚`8„`!†C€á‚`8„`!†C€á‚`8„`!†C€á‚`8„`!†C€á‚ƒÍÉÉ)¢«ð ??¿ˆ®BC€á‚`8„`!†C€á‚ƒ«/¢:B0 ‡ Ã!ÀpÁ0B0 ‡ Ã!ÀpÁ0B0 ‡ Ã!ÀpÁ0B0 ‡ Ã!ÀpÁ0B0 ‡ Ã!ÀpÁ0B0 ‡ Ã!ÀpÁ0B0 ‡ Ã!ÀpÁ0B0 ‡ Ã!ÀpÁ0B0 ‡ Ã!ÀpÁ0B0 ‡ Ã!ÀpÁ0B0 ‡ Ã!ÀpÁ€ÈÈÉÉ)¢«ðóó‹è*–‚‘Ô òSW#Db„`!†CºfFtÍS„`A£kfTG×L0CŽ`´±sDBpģͰhœ ¢‚`8„`!ÝíZ­ôª÷ñeŒ˜Š[&¥j3Ž·²gúÖê‹J¦ìÁ(Ð×Gù¹©úYqÐËÁµ5õò™Rï ¥@)eIJ#½^)us*9 *ãÖ†á¬èŽ 0†¹û•ôŸ«ïÞ*Çv*ã:+Ï+ù¾±÷ã~~J©$Êâ“j!§ö)Û–)='+%k„]}U‰\•¾3•î5•Š•ÕFNí£¼õRúÌÛYÑ!` I’+ÉR ×l¥<¸¥,«äüÊÞßøGñ|0üê¹ú»h%%u»>ëë£6?‡ŒäìJß)£Ú)ùK(.‰•¿Ž(«g(ƒ|\!BŽ8+È#såý[åù£OF>{¨üò“rt‡òì‘’ ¡Rð[¥û¤€¤[/§ú»VÖkÇުM³;V¨íÊ)Ó©ñºyÅÉ)`k½J-0I2¥Y/õåÒÓʘŽÊù£JªôJ_ÔíØÊÅ“ê{NV¾©j¹ª=§(õ¿P&õPzOW§/VY©ü}˜¬ÀHÁÂg¥Ü»¡ÄŠ­$MñÉÈþß+·¯(#W(鲨xl'¥[ 5¹ªoÍQèo‡ÕäºãÇá‘m•›”Á •,_(§÷+CZ(ÎÎêqfÍLeØ%mfåÌõ¥⎣”¤)ÕɆ¶R²æVºMT»*CZ*C[*Û((©Òw–Ò½†âñDytG™º-LW `„àh„66Dœ•FÄ’ã¨s‡”e“Ô=dœxŸ¼5xú;…[Àïºí”þß)Oî+ÉÓ( “¨#µ­WŽKÙ´Hé2N)ZQ#ÇHr`¶rÚÇ\®’»ÈÇòeŽyŠ©ÕÜÕ\½¹òeá/eFÚ¬“C¯ÒuÔý¼„fkÓBp´C¢ÎJ#||_  ÕÇ[qrVsg· j#®)™`ö åøŸê†÷ÁWñõUG¾ñ´Zæå³êdZÕÈ¡ûï³”O^æ(ðÉôYs Èq”ø_ÞO^¾{cuF¯_¨Ç]qã+»Wu¶Ü` 8ÁÑmlˆr8+ð1n­’ö3u@öTÉÓ(±ã˜Oàý^i]RÈÆ(Yr©­¼‡·ªK6h;̉>މŸÐü«€—[MI™6^Úx€èø.êŸÉì½JËbêî±^{[µ`BptG"3ÎJ#<É^N;Qfl·®(¿lR÷?š£1‚(SkÁ}ýâãmXï(rt·q2m»ÚÌÜô'eJouGjgó+ÁÑmlˆ„8+ÈÉëµúÛ5UÀKÙ¢Öÿ¦˜6К5Öþ/Ú®|öàÇC¬¿«íͲCs”—Ï•á­ÕöBeÕ—-û©í#Ú*“7;l€!‚£/ÚØiqV‘“8É–0”òãHõ‚Ý9CÔŒ{Ḛqe¤m]{Ö)E*|3•ve”M •*M9#À`ÁÑmlˆü8+ÈIþ‡,T»~ÕÉ®~­Mz¨]¿®þ¥6ÄÆK ”¨®.¯¾;¤ÒjÀÇOõ˜¬ÄM  n®îÝ2©iµ~‡UißzeóbõĤmÝW¥Õ¶†ñ]Õ8®·[&Bp´C¢ÎJñÊÔUNXÙó˜½U¾¡úcjþ‘æݴw Ýl· êí’ƒõR'ÉÛâøAó-Œ„àh‡66Dœ•„1B°ñÐÆ†È³Ò€0F6ÚØ8+ ˆÁÆC0<󲳞¦U Í¢Ñ!µÍ8ðÆÅ‹ËÏsËœY“){öÏsçîT©Ò×åÊUhÔÈöÇclj“,uêÍúÆ?ÿx¾|ic‚$É“k…§JŸ>oñâyŠëÛ°aί¾*U³fpçeg=M«šE¢=B0‡1=ÿk樷wŒ˜ÁÞá8ü¶ö©µW®˜µÛY«y»¡C[öïÜj#<ùx{¯3§æ?è XS´bÅ‚ß~»bêTÓüàÖ­ ]»žÚ·OÒa™ºu{Nž;n\ÓmÉ×ÇgöàÁ,xúà[¦Lºt©×¾½öÙ÷oßNíÛwë’%/Ÿ?ÿ_ž}v¬Xñüñã”éÒÕlÕªyŸ>NNNÚâ/>yrl§N;–2mÚžS¦|Sµª6ë­K—Î1âÖå˲¢ ”,)oIUCó}‘!€ƒiçÍF† ‡¿À5Ÿ0aÈŠ ò,9åʹsž/_~]¶là·¾.WnÖÀþ$òjc$·<¸ßìÙçý¹iÓ‰u7Îô#ã»vÝðÛo}gÍÊ÷Í7G¶oÕ¾}œ¸q«·h!oêÐáÄŸŽ^½:…›Û’ ºT©²üܹþsæ ûá‡ß¶?«\yzÿþR«I=zXœ—å‰Ý»¿~ñÂ%qb™þ•‡Çñ]»Ìê)úÿýí+WF®X‘.K Äa»Õ¨±ôôiÓ*9p@Ÿ~dÛ¶6m¼pa–/¾8½ÿ-œÝ{÷ÖÞ•9v5Ê5UªAîîC[¶Üöàäcɾ?7iÒ°sçI6H5¤Xy)&v.)ÉEKF½¯ipx«aè+]Wu8ÓÏÿF9¬ygÉÍøúøD‰ã„HèÉýûò;eºtßJ!ƒ··Ç“')Ó¦ÕÆTnÒ¤LݺŠç„»woZ¸Ð4\¾|þü÷Y³Z Pé»ïäeÍV­þ>vlÁ˜1L_<}ºqÁ‚þ³gK`•·ºOš$!õÞ “$Q‚¹åHUåë–üjm^RÃñ]ºH`­Ø¸±¼µoýz__ßr œÞ·Ï´œÁ ÈoIäÚïºíÚõÿî;Y«$õß´h‘,lÑŠ奔|jß¾•Ó¦é!¸zóæy‹—ªÍšhÓFÊ‘2o\ºôáÇrõëk­ìãÖ®•Udçb‘û\D°à¶†Q—„g]­ØÕ>hñ¶YáÖÎ,?üxdÛ¶‡¶n;ö·µjå+Q"5·V¸j[;%­˜tɸ|æŒ È»RH’dÉ?nc^œ•¶F»ÿƒŸõ®ÎÎÎúp~“ G«¦O—-Dséôi Í…Ê”ÑÇ(Ujíœ9’w/9óÁ×7kž<Úø˜±b ]¼Xñÿ6ƒ[aùŠå÷Õ¿þ²6¯äiÒȆº{íZ-ïZ³F&sM™2ð‚Ï4èøŸÊæ'uŠÿ5‚gzùìY™æËÂ…õ19 ”.áX{™-_>m ±««â· ù«P!—ĉû5jÔ cÇ¢•*eΑC‚»¼@¤EF nÛ[pØìÄå#áÆâYWk'vµy [±~{`³fWÏûeÓ&·Ì™mÙ"¡!u¶V¸j[;%Øš™3‡-Y’ö¿þ¬ç•·xqÎJ[£Å²7oþï¿„ª{xû¶üiËNFãâßJª‰çâ¢|šµ}K»²eõܬ%Kùrµ·âÆú ß¾zUÊ‘£2kó’ÜY¾AƒI=z¼ûV‚²l}gÎ4+ÄûýûÖ%Kʻƌɒ+—„òÃ[·ŽéØÑÚLµúËߎ>Fëêãùê•öRJ0^;¨¿SÙ†1bÎ!ò—ûÙ_Ȇ»H‘P¯ R GU› þ>vÌÆõ j“X»vG¶m‹3fù† ‹U®Ü­zu½9*p3UúÏ?·ÑÐe­4ù”+Kì´}ùr‰‹Oœø_Þ¼òòÜ¡C-‹½jUé:uÌ.C±8¯\çaÚ‚ËGBéûô›³juî¬ßÊâYWk'vµØ>…­X?‹]´R%I½¦M+Pª”Œ¯Ó¶­Œ_ÿÛoÁZ§È­U;yš4öŸ%/× ž*¬Í+IŠœ•¶æ³œ9¹ºܲ¥DõêfoÛµ+O±b¦ñÎë¿Ì'Þ¼~-¿˜tûÖúàÊQMæ9LË‘ÞÃ;wÌ>b{ÿøã«Ò¥ù7¸Zœ—ü–}Ýè”øûÖËK^~[«–Y!²W¼uåŠÝɾWs4F 3Õíõ‹úmXoƒToÈ¢E²§•?=9BëZ­ÚÖ{÷´Du„à¨ÊbTË~ý+-m2~`³f’wÇþþ»|äyó&õè²¹h ]6J³qe‰$Uo]¶lT‡¿8à÷რH’ÿ ʧ lÖæ¥µv„ø:. %‰hi?ûÌtL¢¤Iõa‹g]mŸØµ} [±~[‚‚ŸŸŸ|D/‘ÈFßG¯^-ÿï­¶ÖùhkL«gmA2eËÆYikä8\?ß°S'Ó@ylçNù™°~½éÄgÌõõ×ÚðÅ“'å%q²dú»rÔ-‰YcJT«¦‘ýžsŒ² ü/O8wøð… )þ ¥*U’-CÛèŒaF¶ÞÏŸÿiÊ”Ïsç¶6/Nš"…eÙÉfV¼JÓ\—‚—‘öR6?mÃÖkbV%©¿¬(Y|ýˆë¯Ã‡åïÔôo3°kÿ}÷úu9Ô—Ï~]®œ{¯^?7múôÁ½«¥‚£*-g[Úß»whË–ž“'ËŽLÞj?lØ?'OJt Ù\d÷j­4Ífga­Õ°ÏŒõræÜ0þ[/¯GwîLݶM{×´ÍÚ¼¤>ÖÖC×ypùHèÉ?H=¶Ÿu òÄ®íSØŠõ³Ø·¯^Õ?búqkÇwùråàÊbáoßîY»¶ý磭1=ÃnmAg¥mhÕ¿ÿ™ýûÛ”*ÕaÄ9lðõñ‘ø8kà@9fÐ#¦Fv)òËÇ_GŽHjlÜ¥‹é»’e72£ÿä©SËÑšl?ò…Êñö¤%+WiÒD¾ÉÙ²y¯˜:õÄîÝÝ&N”Í@>¸gݺ"*dÈš5N¼xfuóxòDR£lçîÞݸ`ÁêéÓÛ š¿dIÅ—eq^ÚË7h0wèP Á~ý5ð"ç,XP6¹ù£Fý8räËgÏdØ{áøq‰¹Zã®V%í S]4W×-Z,=Z¶U9¼?ºsç–%K¤þ¶W¬„þQíÛ÷ž>½PÙ²o^¿Þ¸p¡}éWQ!8ª²Ñe±¥íú… ò[oÅ«T9drÊ`ÍåþÖJ³qe‰ÅónÖZ %|t?~J¯^òÿLvÁ/ ±=/kë!Èë<¸|$üyb×ö)lÅúYìÿü#¿µÊš×6jb1¾[+üîµkvž6;Ì“Ðo­6NÇÇŠ›³ÒÖÄŽwÚŽË~ùeùäÉ%cÄŒ™-oÞþsçJŽ4›RŽ®6L»ÈRh[ê#Þã—_$/J!ïÝ“à[ªfÍŽ£FioÉŽè—Ÿ~ú¹I9Ä•(9qÃùšd÷U¸|ù©}úÌ9rÙÙ³ÿð[ùßMBñ?€—}æä-[dú ç%¾­]{d»vrŒ${×À‹œ,uê! Ê|ëdÏ.›G“=$R_ýë¯á­[ÿ´¶‚¼ÎƒËGÂ_'vmŸÂV¬ŸÅÖ®”úçÔ)9’ÑÆŸúô&Sö°V¸–emT[Öîe¡wǼôßöÏëö•+œ•¶MŽšöì)?Ö&(S·®ö0 ‰¡foi_“v¬"ë¿ý°aÚÙ$3qâÅûiÊù1)aZ?OemŽÖؘ—âßpäÓã%³Ë7l(?¦Ì?rD¨üý÷úÈÆ]»êUí6a‚üØ®§éKY'-û÷çy1ˆ®ÁQRp/‰Pþ;åjÚ$æñ_Ó¦µf*s±Qš¦,»—/À¬S¦M+õ™=hÐ#GžÀÚ¼þ>vÌF±¯ó0+“ËGBC;ÿk62¡õ®‡6Nìj§íS؊ͳØùK–œ7|¸|;²îýãs‡wq¬>`î\kÕNáæ¦Gi§¤å0Odå´iß%‘ ‚œW©š59+Fî]¿~lçNù‚¸C"`4„à()È–³À2øßNáÒéÓz“Øþ ´kÍT6æb£4W–k/œ8±tÒ¤é;v¼{ûVeÙzõ²çϯ¿«-iæeí:}. =ýü¯©Ñ«VY›ÞƉ]í¢ø Oa+ÖÏ,[²dD›6?V¨ [EÉ5ºŽß³víà.‘ÅÂ&Ib­Úñ$(Q½ºéYòÎcÇþ6räΕ+‹T¬Øaøðî5kk^r ÆYé0"ßÅÍË—¿ïÞ]»]ã Gg¸çßW“4E -gÖ ‘÷eáÂòO4c¶l©Ó§_7w®v=‡øüË/-6SÙ˜‹DRk¥Ùh“³X1‹­†ñ\\†¶lY±qcíÎV’„7o¾èÄ ©i›dq‹óªæînm=X»Îãïÿ&àò‘Ðòü¯µ³®6NìŽ\±B±t Ûl^ÖÎ,Ë·`¶íY¬¡íš[+ÜFµ…éYrÉXòcV‡ôŸx¦ÖæÅYé0"‡(]ƒ ûáÓ—þ¦lÝj± êÀ¦M6ʾlÙ°V­~¬P!^‚U›5kÞ·ï€ï¿3¦”`±™ÊFûœ”`­4%¨«=ÌXl5Ìš;÷Ã;w¦ïÜ©½ì>iRÝ9æÑzàÀü%Kš6°Yœ×±ÿ>˜=×ypùÑ!8R³Ñ:e» ÊâÇ]S¦µj•~ZÁèÑ1cÅJî'‹ÍT¶çòÖËËZi¶¯ö°gé̤psÛû_oÀ—¡žW®ó0†ËGˆöÁÒ©R¥‡wîôŸ3'CÖ¬—Ïž]4n\ÅÆµÁ^@x"ÈÈ+&vïÞ»~ýWÉR¥’Ìjñ– R@x"H²Ô©‡-Y9KO„`!†C€á‚`8„`!†C€á‚`8„`!ItrŠè*ˆ¶Á€ÈÈÏÏ/¢« :#";'''21Ç"g¥Qœü·=0‡#ù…`a‡ñhc34k„àÆ.ÌÐ ‚‘ À !€¡Ñèyð]O„`@£@ø#" À"!+ük‚ÄŸ €ˆBŠükç$/` "!CÚ?xÚº3üQˆp„` ÌÑ$ èø[I‚ð@“0 ð' 2!á‡&a[>€È† „+š„a@lð"!B0h†A°ˆ´Á@Ä IÑ›7€ÈŒ D$š„-±UˆüÁ@£IÑ 3€( D 4 #`…‚È‚&aDilº¢B0¹Ð$Œ(‡-@TD"š„…°¡ˆ¢Á@$E“0"?0€¨‹ D^4 #Òâ @TG";š„Ùp`  QMˆ$8m‚(ƒ&aD,ÃD'„` *¡I‚£/Ñ!ˆzhFxâ  @´D¢$š„8Ö‚(Œ&a„±Do„` j£IÇ‘# ÑÖ$LjAè±!0B0MÐ5¡ÄöÀPÁ@ôAׄ› £!Ñ M¶ÆD¢!š„a'6†E¢-š„a €‘‚èŒ&aXÄÑ‚è&a˜â B0`4 C¡L‚¡IØÈ8S„`ÀXh6 Ž| 0B0`D4 <`!0(š„£=ŽsÀB0`h4 GWÞ€m„`ÀèhŽf8ª{‚¨hŽ8˜;‚ I8Jã‚… à4 GEº@p‚˜£I8 áˆB† À2š„#?T ÄÁ¬¢I82ã{€Ð M‘ _„!@ÐhŽ<øÀ!Áìe±I˜Lnh"³&a-–‘ƒÃ+‹ Øô&a„³¼K0„B0€0ËÁQ«Ò,Áë/#Ã"˜5®G­ Q!€cD¡¸%Z²i€0E‘?DÚ8G†¸ «Ñ!@°YKÀQ¨18J`}@Ø!¶è‘ÌLƒ#ÃEõÆuˆZÁ)D† ÆA`\Zc0é ˆ ÀÐHÀ`L„`!†C€á‚Ñw ‹fèÍÐ#; {X2Ú^˜<š1Ú|âDD×R°`D×Ñ!Ø‘üüØÅˆ““wÃ÷nUè*À1Ü2lŒè*@D"ÀpÁ0B0 ‡ Ã!ÀpÁ0B0¶n=;p R­Z8V%|õí«lßnïM”e-•/¯ŒÆuÂ!8<Œ·¨gÏ_æÍû¹yóêfoU©Òy×®ãgÏ.Ë–-£>òÎG3VýðáÃéÓKóæýŸéôÚ½igÌèÓ¶mÓñ ö]±b{à?xðtÒ¤¥›6¸qãþ»wïS¥r-Z4O×® þÒ‘KÖª˜,lƒå—/gw <.¬´ooa|Ú´a>ë1cÂ%®Áá¡K—Æ‹oéÕkJ¥\]éã׮ݽyóÁÁƒÛ˜&`1oÞ Ä+¦ LžÜ3p?ÿ<³q㊉%°=ß}ûNÕ¨Ñ=qb—.]åË—ÝÙÙéäÉ‹'.-V¬¥$òfÍxê|”(‘’3gÄÌúôi%s戙5`X„àð3fŒÙ³û)Ò¼OŸ©³fõÕFzy½íÒe|öì™z÷v7ØÏÏï·ß6T®\}uË–5dXÆHн}ûáÞ½³Í2îÎÇnܸ'Ù×Å%Þ‚×­ÛS¿~9Ó ’'OÒ§Oógµm['K–tÖæ8mÚª'O<6l˜¨'`M²d‰/úâÅk7·?èäT°qãŠåËîߺ”?öyó¾~í%¹ùôéKiÒ$5ª£L`ú‘ ® 0sïÞ“/_zJ±5k–2¤­ÞP=þ†aÃ~½sç‘Ôvðà6fó2ëð`» Ä•+·úõ›þçŸÇ_½òÊœÙÍݽZMäÃÚJG)YRÍš’ùÊ”Q\]Õ9gŽR³æÇ,ѳC%OµoC¼xÊ¡CÊܹʛ7jtVüób«VJŽê»~~j •`ºx±òÙgj˜nÚÔjg qñ¢šª‡ SnÞT³ï!jŒnÞ\‰_=Z}Y´¨ÚŒ­¹wOiÑBà}ú¨¿ÿùGMÞRÂìÙŠ³³rì˜2x°ZçþýÕºI5^¿á yøP­ƒ„ì~ýä?‹rô¨ºrîßWç D „àð3|x‡ßß-Qòøñ…—/ßš0a‰âo¾Ég6Ùܹë’&MTµjñ˜1cJâœ7o½YVüûW̘±ú§Ÿ&¯Y3ÆÚì6o>1c‹}‹Ém»ª'O^ôòz»dɰK—nJ…[¶âíí#ÉÛÅ%~‡£[´R±bQ½_Çõë÷Šm‘6mÊ3ú¤K—òäÉúô™*%H¾wvvÞµëXóæƒ+T(2gNOÏ7’†%½²,‘c†"EšKÈž5«_êÔÉvì8*1ýæÍû2ßÁòÓOj3ð¤Ijî7NÍ]»~|÷öm¥P!¥G%CõeîÜÊ‘#Ê–-!xÁ5ûʧ&T_f˦Ԫ¥lݪ_­†ÎÏŸ+K—*qâ(ùó˾]í;1~¼ÊEƒÊرʅ j†ÖÌ›§¶ËJ%Ýܪáë«N/•‘¬¼r¥7®2jT@ð„òª!í÷Ûoj4Ÿ6MMÀBÒ¿§§ZO‰à©R…°L <‚ÃO¢D &OîY·îOÓ§¯^»vw’$ ÇŒéd6ÍÓ§/Ö­ÛÓ¼yµ8qbËËÆ+Nœ¸ôÎG.M'‹7öˆ¾ÿ~ÀÞ½§J–Ìoqv’³‹ "ìZóèÑóÓ§—Æ‹§D‰ü‹mÞ¿ÿôºuãkÔP÷¸;6èÔiì‰Ê—ØãŽ1ïõk¯M›&eʤîq%aûúúvé2~Û¶#•*:ueüøqW®¥5 (#cÆîqGŽüM¢ùöíÓ$ËËbÅò¼zå)ë§oßéÓ³ÇZÛ·«?f2fTÖ¬ –œÚ§sS§VþüSmaM`ri†¤R-˜êÒ¤QΟ>~\MœZ®®jÿ ;åÊ¥&`–8õ{Y¤ð?¥÷êÕljS»Lh X¯˜„àS§Ô,qY¢¶^íäÉÕZýõ—½51uø°š°µúhJ•R–,Q»aT®’pFWuê”®V­D·n¼½}–,–4i"³ -Úôþ½·~ÉZÓ¦UÆ_¼`ÁÆ~ýZ˜M)ùxÒ¤e]»Ž?qb‘³³sàyyz¾I ^ÈêY¸p.IÀÚ°–8¿ý6`›6­ºÇõðø¸ÇݵëXþüÙµ¬©^½¤„à}ûNI>~üBÁ‚9õ®iÒ$—|äHHö¸Û¶–„­ÕGS³f© –ìÙs²Iö¸B«Hµ­™Ø±?y)9¯|yµµµzõ¯š”eË”M›Ôžoßš—óè‘7CÆÅ¤S›¶¿×S¬öÒÏïã«Í̦´ ,ãųgj¤ünHwïZ¸µÜÇ!,g„àð6mZ¯ ª-šÛ¬[­æ×_ÿÈ!uÖ¬ž<ñ—nn)räÈ<þ†À!ØÉÉiܸ.¥Jµ–ˆøÎk"I’„¦QU¸»’‰õ—6ºÞ&Jôq«%l=Åj/?|ø¸Ç½{÷qÞ¼Ÿìqµ |Ç}ôè™DjÓw­õE’ÌèÚµ»ÚMâLݹÀ$L¨üïALãë«Ü¸!{`åêU5õš6AL™¢,Z¤ö.èÔIIšTFÆ>ð®¼4ªaGf$?éqÙì]YŠ+THéÒÅ|¤iÛ0™‚Ûvâ>}úÔß:rä¯óçÿUÔãò²foYìö cªW/ѯßôzõ̧_~ùù™3—?|ø ·Ò¶K—ÆÚp¾|C·9ù <^›¯ŸŸŸÙ»B±Ç-S¦D³‘¦mæ~ûM½:mµðüùjÿWÝúõÊ_(ƒ}ãíýq8eÊ€¶X——ú;~|×0ujó¶XíeJÿ^uI’(/^|òeomº‡öðbF¯^}ØDZ„àHä×_ÿà¸zõè„ ?ö2{ûö]­Z=æÍûÃbßß1c:çÊUÔ¨ùߪS§ôîÝ'6lدõå2¤–‡W[Ê4k‹½}[}©õcNž<ÉÓ§ŸìqoÝú¸Ç•|lš‰µöo3òðxeöô7ÿþ«ÞN¡ukå›oÔû9Ì™£öŽøì³€wß¼Q'þ8ñåËêlŠÐ`\¨zo2ÉÁZ÷™¸BõFzhM‹¬©Â…•Õ«Õ»4¤ùw«¿¿þZý#‡Ú9XæÏ¿»œ$`©§.aÂO´öAÛ3º~ýãŽSTZ¶üx« 2#GžžoV¬Ø^ºtÁZµ¾5{«råb«Wïš:µW„æ-Ù²elÓ¦Îøñ‹óåËnöVóæÕ'MZÖ±ã˜|ù²™eßcÇþv`Í+T(ÿ\½Uðºuê-Õ6m’=›R¼¸Ò¬™²c‡z§ˆ6mÔ;é.\¨ö¬hüß99Iý¥ìÜ©&K u°Ÿ»»²m›zÛŠ¶mÕËïΟWfÍRS{~ÿ†”Úµ•Ô›š}÷Ú-oI„ý÷߀Ï,¨^ð·h‘’7¯zb©¼ ²D2£ÎÕÃ¥N­Þ‹MJKŸ>´õ !8²X¾|û«W^-ZÔü–»{µõë÷-_¾í‡j~wР֋m:|øœÙøøñã®_?¡bÅŽùò5îÒ¥q±byâÅ‹síÚ])jÍš]X;wnèš÷îí¾lÙ¶jÕºÒ6U*×#GÎ8«jÕo´¦ë6mjoÚt Q£~ݺ}÷úµ—¼%Vëõ¡ø_o·fÍŸãÆ-*^<ïéÓ—.´µÇíÕ«™¬„*U:ÞAbý©SÿHiY³¦7íÁ !väˆúX¡Bêp%¶^¼¨¶ÇôÿÏ;¶z§ˆöí?vŠ0@:T½ƒd\IœcǪpOŸVo‹&y·~}µåxÊõSÚÃ2fÎüØ— yseÆ eøpõî ¡ ‘)Sª}6¦NUïã&1Wâ©ä]‰ìš%d§­.‹$×téÔVí3g>†àž=•wïÔ,+5—L?b„RÃÂ?¥©R©3’j£Þl8yrµ?´héRm 2"G¿þúG’$ 7 ”É“'™7o½Å,oõíÛ¢wï)ßò›+'MZºnÝ šžžoR¤Hš?öY³ú}÷]¥¸qcþH¤M›òСßúô™Ú²åÉñO%ïöï°Ç­V­Äôé½ÇŒYX¾|‡,YÒ ØúÀ3zž<¹ç›7ï$Ëúùù•*U`ùòŸ}fu›>}ªÃ‡ëßFÇŽc^¼x&MòfͪÊ1@Œìq„VàGµ™qwWLI86ý”¤Ï)öÄë×ΚU}œEMš40Y1 ¦¦Ïo+UÊBÍ3dPƒ©5uëª?ºòåÕþÍš)”_~ùdâcÇlUFf4r¤Õ‘!8øùYØ×:4ÏÚô±bÅ|üx§ÏöêÕL~,~6aÂø´’ŸWO‚©éM$jÖ,¸ÿû_íh×®®üè/6,?ujÀ×Í-ŦMŸìq}}?îqÏ(kÖ +V°Ç¡E€á‚`8„`!†C€á‚Pݾ­ ¨>/#V,eÿþ°WÁ‚ê½ÉLov,áYUSoߪøxóFY¹R}¼œÎËKièëùåËÿ h Œ‚ÃÕùóÿ+Öbðà6]º4zj„1ß²eÛùù);vL‹;VDW'š›1ëß¡Ã/N—§Aýôfo5q?¶ÿÀ“]ÛJdÉòÉCîßûU‘]>øíØRâ‹/>y( [†ò{Ôˆ/›~ŸÑt|Û§Öo¸wïVUÓ‘¿›3÷Ú®?ݾóæý;ßä)â|UÀõ‡V™ äOêÈ%DÔ·d‰rîœÒ·¯zCßHeÌÅÃã“ÄQUW} H‹ê£@† ù8~êTõ Ì3g’€•‚××Û:uz–(‘ŸIÄŒcÉ’a¹r5è×oúر#º:ÑÜ-?[³öî°‘+”O$ÉÇCŽ-[H<íÑíf X,[q+~ü1c:-[ykØà\Ë;þR­šiºØÚ9ú̽åñD cþÐê³\_$rvv:÷׋Ùs¯m¬}¸<õë¦sÈÒ!2prrRÔû‹û…¸„—/ÕßµkïS>>Ï ;§O«76Uýâ õùs³g+e˪ϟӪ·j•ÚB¬=™ˆ*ÁágĈy7nÜß¶mjDW$ºñöö‰+„[rÚ´)þù‡=&}÷]¥¼yÿô¢µÐg$ËŽ•»Z̓#F]3*·6òÍߟÿýy—Ž>7›^j±båÒߦŒÇyíº{ûçŒë“G/–üìYI¿\Ð/‡µ™>}ú¾U›éÓÇ[³²¨ä`mä×…\ëÖN[¹ÚÞ}ÿ*Z$Yº´ñº Aðöñ‹Ó)<çh4!ÞŒ üdàÄ åÚ5µióäIÅÓS}”Z©RJÛ¶J‚Ó”/¯dÉ¢>3¹IõYÁ ”š7¯úâÔÇ7kf5¶Ê\~ýUùûoµ¿AÊ”J¥JÊ?¨]ôú\¹¢lßðÀ¶PVUÆT¬¨.¬> ÚÃCÉž]íYáå¥>ûíÒ%õéÇ;ªØ !øàAõ!ϲtZÛp¦Lês˜¨…N<<^ýòËòÆ+fÊä¦}Ôßÿü£žñ¿xQmuößZïÞUÞ½Sƒœ*•Õ2OŸV._VzöT“¢dÐ#Ô„úÍ7æ“I„•™'Úí!^<åÐ!eî\µßm÷îkÚTͬíÛ;¬ªò®¤íaÔ›7Õì;dˆÚHܼ¹Ú™aôhõeÑ¢J¢DæõÔň¡߯Չ¥L)ÿ·ß”رí_Ù@¤@'+VìÜÙ¬ÙÇÿpËJ”h%¹pÍš1²§6ì×2eÚž8±ø‹/>»~ý^Ñ¢-Ò¦M9cFŸtéRž<ùOŸ>SOž¼¸wïlg瀬 /½¼Þ.Y2L¨ÄÙ–-‡x{ûôéÓÜÅ%~‡£[´R±bQW×DÁ8ÈYËðµkwGî”0aüqã2'Ož¬µk—¶¸ÔM›”Ü9aB×ܹ³Jþ3faÅŠÏ[ž-[F{–Ѷë×ï¾}ûnË–ÉéÓ§²½>oß~X¤HsÉÙ³fõK:ÙŽGåxàæÍû2kù ¤óºuËþúëºÇŸ§HA'Ña…{ÿ”}ó–’#·l,~íºç¬9×$]È5ð”K—ÝNœ8V¹²©bÄpJ•2Îò·ÍB°âßÅB‚ã°æÎ*¸±ëÏGéÒÆ³Ø÷·@ ¿ës½xóÆwÚäüÿþûºw¿¿ºõ<çãó¡c‡Ï$ˆÑ§ÿù®=Î~[*¥ÖµC¢|ÕšS§Š3fä—)SÆÙ·ÿÉè±—îÜy3jÄ—ZQvß½û°dá×nnjì>xèi§®§‹K>rX.Þýí’à“½q:–|Ý¿kmÐE‰ ,¾kçfœ3g@ì“!ÑËK™4IqóßîrçV|}•ñãÕè)QñO®3fÑöÖ-eÃ5˜Š9ÔæÕU«,„àÛ·•B…”=” æ%sÙ²% kõ‘ºi©êóçÊÒ¥Jœ8j†Í›Õ°.Ó—,©¾Õ Úß÷Â5vÛ UíÜY Á²ÃnÕêcÝ€(„N¶o?"a«xñ¼ú˜Ñ£|øà·ví¸$IÔ+lóæÍö¿ÿÕZºtëðáíGŒ˜'‰yÓ¦IZ³q‘"¹}}}»t¿mÛ‘J•ŠjôèùéÓK¥Ì%ò/Z´yÿþÓëÖ¯QC݇uìØ S§±'N\(_¾pp'rÖ(÷…ÅtéRmذo×®ãÖB°,u… …[·8ÿW¸ð—³fýþáÃ{f¤¿ÿ¾¶sç ÉâA®Ï‘#“c€íÛ§I–·ŠËóê•çĉKûöm¡hYöÙ³—¼Þ Ay»¿ÒOD• xf{$t‰9lð?´=¹`áÍ-[$Nk@_ ž?¿eÛƒ†õÓÇŽ­Õª™vöÜë÷ï¿M“擆Û8qœûü”ýÇΧyZ¤p²Àå\»öº`A ÛNOž¼Û±å›¸qcþÚuõïwŽ{öÛÜ‚ʧ–·Zºgî?ðüÙs%K¤—S¦]•¸¼lIá”)âÈ˯ º¾~í3{îµN?~îæ¦v·¸tùÕŠ¥…]þë¾|P–-S6mR[Žß¾ V5CXU“ËPµso |ò2È?w©ó AêŒ'V›¢W¬P;rQ !8œÜ¿ÿDB°é˜»wIh³8ñÝ»óæýd—¦EÕ{÷ëc%ú¸Óúè=†µ—>ø…`â gíêšØ¬(Ó™Y¸pHÆ}å'NœØEŠ|Y£FÉæÍ«'Nìbç2ÚfÖuÁöú¼ví®““ùó;wþ7ë”Ášu`b©1²M`-%8¼SÄÈa¹ ÞU°@RÓ>µ¦–-¿•6m¼Ì™<{ö^QiâfýÜeŪ;C°Tùçþ9ëÔ?¼jõÀ7_“”©çTM—ngV®¾£¿¬^Ímæ4«´›ÆV'ÿ„­ß‰" (|x÷þƒ·7oyi7n3uÿ~@¢IæúIÉ'ßåÏ÷É, k‚ƒ,б¢ëšMW ³ø¡wײ§ßVà,~jÊeÑ"¥jU¥S'5°Ê§dÌáÃáZÕàZ¼Xmiž6Mþ)¨×Ûýò‹Ò»·ãç„)Bp8yóæ]ܸŸü´x]Ëb±³³lh8vÖ_ëêÕu{öœØ²åÐÖ­‡ºv0v좃Í”É-ô32›ÒÆúeÊ7®‹ÙH½mXûjÞ¾}oç¬#Œî¡Ð·vZÿä©çÿ\Rã`®¼ÛÍÞ²ØíAÆ”/—jÔ˜ªVIcöVŽì ÿ¾ðRŽÓôFâžÝ³ýÐ2àp´\¥}¡[ŽO/–|`ó~‘)R4å:}ÚJ-«5ÈfIÛ¶Ðoº©S+~2F{iÖ¸kÛÓ§Ÿ¼|öÌrûñúõê}Ç ú8ÆÛÛÂdÖ8¤ªÁråŠÚɸvmÙÉ«/ÝÝÕ[[”.­öl¢Bp8‘˜e–±Ò¥Ky÷î'M¯^yI,tq‰Ÿ!Cj½‘RsûöCí#a]O‡Ï:F g  Z]¶l[ãÆý&MZ6iRwÛ3’D«uÖwîê˜1ó:wn?ÿ»¯°kÖ´Üýäɳ”$I5[¿¤O§>+U²,ëÔ©K¹r½{ÜüÖ­×­Û3`€o’$–¡~4©S'í:z¤Œ¿Šå7eV­¾^â£dU«øØ¼T®lÊ5ën ÿ:oü`¿Ž‘%KüæMÿoƬ yó&²ߨAúÙß_ê?èdÞ<‰l²ïÑßC>³2®ÌÇ)üç]9wþI¶¬ïºíÞsg믷ºwÍf·ÓsÞ¼ ¹ÿüy@Ü .Ëœ|¨^üó ¡¸zÓmÕJÙ¸Qñó³}Z.Y²¹vížC†|&#ÇŒ™àçgù1¹¾}[-Z´±fM¿¡C;¤J•tÿþ?¾újf¥J—vû¯ñ¸pÑ)R$YµjÇöíGl›5kº§O_̘±ÔËË«AƒŠ!.¨lÙ"Ë–ý:vì¼’% =zfîܵ!.N§>奟~ÚX½z·áÃ;eÈàóÛo§eYÙ²¥×ºJ?~N úŒB»Ž&bã¯j媿Ÿ“ù¬]#]Ú¸íÚd >«0èÔ1ËŠU×›·<Ø÷‹’¶Oœx4fü™Ì™âÏëª&3lßq»s·£¾­3Ý»ÿjÌØ3ò':vüa˜g—K™ÒòìÛ©S-8Ÿ=³ÄÙ¦M-q6Tr洄щ-A“d¿Ã€ǯ¿¶<Ï!ztKr3Ær_ÚÑ£ÊØ±–g/4h`É—Ó§[~œbÁ;!Ø%E5èÈ˳Õ>ýÔvEòä±<6xþ|¥|ùw¤"?ާá¤råW¬Ø¾gÏ1-MfÉ’nçÎÙ}úLiÔ¨ßë×–wغuF–¯ìÓ¦M¹wïýúMõõúøñ3Ém=z40À=‡´ÿrá¢'N°k×wÎèÕkâ;¼½ã*”cåÊqê³Øô4yrïçÏ_JT•XV¦LáŸ~‘9smýÅéÔgúô©öíûqÀ€é]ºŒ~øðIêÔÉ[¶¬1xpûèÑß}Ñ(ç'2¬=1háâk ƬZŶw¯bù– UÒ¤±~ZrÍn–—ºvÉ6|ä)›ñÙ²Æß¶¹ôìï/mØxsÆÌ Ïž$K+_¾D£Gæûô“t±c»¦Ÿ}š4q׬(ñ͘3ý|üøuÊ”qÔK׳GŽèÑíwý­Q-õÀþ¹¾ûáRã¦ûsåJ8jD>ÿyW¬Cphg—1ÂòO“!ƒå×+ìR¹ÍˆªU-ÿôç )vÊÛ V­z?ܼ¹åŸ«Šj3ÆfVeÊè­]áÂÊ¡Cö_êÞý} J ‡“† +Jœ;wõ%Õüù³­_?ÙîôÙ³gX¶ÌÁ!ÍrÅî?‡(ɈÖ?®V§Në B5±þ¢m¦´;ÆZ¶l¬—eCgAiÒ¤X»v’õ˜€€ƒ!.W§>¥$‹´ûÒ‹¯–.ÝZ£F©”)Ãþ@Y„–õO£Y[½¼„£·ÄŒáõÇï•tÞÞ©cù||üø1üºe“ΔpÆ´¬Ÿ#Q¥²Í™2Å›ñ­ý/Lì–¶ãgYäŸöç·3.Ø\åÕ™!Ày„àp’$IÂîÝ;РvZ/XD³fýrÿþãÁƒþø3àr¿n»µ`ÑÕaCòª?ÿñêÕÛß=È‹Ûà ü‚ÃÏ—_¶ùùç-]»ŽY¹r|D—ï\¿~{ðàY=z4Õyvàr>©âlÙòÏ­[/»wÍ'NôÙß_¼{÷Õ7#2‡üN€‹‚÷wœeËÆ”(ÑfÒ¤Eݺ5Žèâ@yó& iÓùòe>üóˆ. Ì%wî„óç=îLÛÏ,{²eM0}êÕªÚ>Qšñ~Ã"!8\åÍ›Eû-_D¸1¢oÛ63¢K“*U2¹ü‹èR€y‚`:„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`€))Ñ%™‚ž/000¢‹ r!ÀtÁ®äåÅ—mðpi2¬‰è"à„`—á»6x<6r€Ç ÀtÁ`áååÅ¥n0B0XpD®ÁðƒÀ<ÁÌŽËÀ`B„`nç(eFÂË®‘çb°N4$%€( Àí$´EÚ«­‘¶`‘¹ÒÀ‚DŒH{93ò\ &€û‚„›<IR¦bU’È“}­ÏÁ‘°‚„®h†™MV‡‰Âà$B0÷²m¸°Ñr°õuk…š'‚¸‹Ý Fn »ýIˆÂf„`®ç(œלá¨>‰Â„`®D DaB0× „E,¢0„ !€³^‘Q ";ÂVäd…> °‡ Œ"ç¯K@ÃóÔ@!@¨ª¢úH@p„`¡@ŠºˆÂ` À“g €Š &ÏCB0‡Iž( ÀÌÁì ™Q€9‚üaÈœx´0³!x‡ø - À<Á,øå X£GÌŽ GˆÂ<!0/ Œ ðH„`ÀŒ4-¢0CÌ…g…x B0`¸ Q€ ž°wàÑ¢4B0àɈ¿p7- Š"ž‰D‚pF Q !ð@üò" Q@TA< á‘Q@äG<‘ Q@dF¢"!ˆì0¢0€pF"/̆( Ü‚Ȉ3# „` r¡áTDanE" { 8¢07!ÐÇ£…¸!ˆHÄ_À8- À…Á@Ä ÂŒ>œG"¿|8( À„` \Ñ`®E6„` œÐHîCZ„`Àíh˜ðA`!p#c ü…AÜ‚ˆXB0àbÄ_ òàÑÂ!.C+ DZô‘`ƒ ¸-+%…hÁ€³øå j! PÁ€óhD¨ˆ( ˜!`^DaÀ´Á`A23¢0`B„`,ˆÂ€©‚#)íï03ša ü±ß&A޼s 6·"œà6„`¡Æ7ƒ«žL‹ ,ø¦ÂDòo8×ò œh!Ò"")H~¢“#ÀtÁ0B0L‡ Ó!›ÀÖ¥JŸúïÿŒCI“Q©ÑRiÕWÙ¾ÂòÒÒSJÆœ¡˜aÀeP Ë{Åžg..­µG÷”úy”Âe”‹Þ|öX©—[É]D»Ü‹lßíRÒgµ ¼|¡Ü¢Œí¦Ü¿­*eôíJ™ÄÊü#–™ü¶SÙ¸Hé=Y)]Û}åµH˜Tùr†Ò³ŽR¥‰òqÍw#§öS^¶ç}’8±Ïr½YŽ×®òè¾2¼½%ü¯‚åOßþ–Ë-#:(“×¹lˆ 讃ðĹB0Þ“| Gó9£”Î#-÷#Ïjɸ²d\9àª}¶¯P>¬ü> $LªÔn£øcé>‘%r`‹²~åíBc»Z²Žß¸wÆŠ£ô›¡t,¯¬«ToáÊ!bÑ]á€s-VÁøW2eè\K϶º9- š÷²ôl;Âr!6n<åãZJñJ–WçŒTÚ|ÿ®^“•8ñ”!­-Gü4-iµA'—iç*eÝ|Ë÷’’¶5EËY.¥Œó³Äqíº5¢:ºë p®À !ØÊ×S;8°Ú¼T©‘埵9ûß[÷Bkâ÷n@Z‘ã-ÿôçª?5’¼íŽ<ÇÎHx6ºëÀyœk°BÐ]á€s-ÀLÁ¢ºë p®˜ !@„¢»"ε3!<çZ‚!ÀtÁ0B0L‡ Ó!ÀtÁ0B0L‡ Ó!ÀtÁ0B0L‡ Ó!Gy[—.íS¿¾Ý—¼~=F¨?bu†KOʘ3§ñ—BœáòsçÒgͪ?åëW¯~þöÛõóç_>}:àÍ›tY²”«[·Å_xÇÚµ0R¤×Q¾ðÇv§[…€GößíÚÅ(U³æÆ7’¤H¡3200°LâÄó±;1ˆü¢RH‚ŽÄÉ“'óñ‰èR8廯¿>yè$ËL¹r©cÒeÉ’·xñE‹þ2kV³ž=ç±bÇ^“6#/Ÿ>ýôÑ#G€ÈìáÔïë%YŽéÚõäÁƒ)Ó¦í=eJ©5ä¥{ÿü3é‹/lÞ|ïÖ­x )[¶çĉ>2¨o¼yõêx?¿ßvW¾^½Þ“'ÇŠÇfæoÞÌ2d¿ÿÝ›7ÓdÌØ¸{÷úŸ.ãïß¾=²C‡½6ÄŒ«ì'ŸúøãË)³Z:}zí6m´¬Ê^ À/gÏÊÌu ¬®ã¬;f~õÕñ½{ãx{—«[·ÏÔ©jí¾Kgíöp°Y?wnùÿ“lÙÔWµñŽ*dÃÂ…?ŒqõìY™áÒ¥å#P×È ÔªK’"ÅÆ7¢E®tÿ~Å”)¥ÆŒô“ >Cw9ÓiÇzŒl½²ˆÚ¾¾ò9F‹Íø¬ ƒ¾4±Á¦0¡gÏ.£F%M•jp«V_ûún¼yÓËËk@³f×ιxqº,Y$)JJîQ»ö£GÕ·H:ülÈþ³fýqàÀ -â%LØ}ìX›ÙŽóó[ýã_ΜY¨T©ý›6úüóØqâÔjÓæ«–-Ï?>iíÚ4™2í]¿~ÖàÁ!–ðü‰’Š>¬R%øKZ^Ô/𨎥ÀCçÎ=uäÈÀfͤÀ=Æwô.ßþý ®£]fÏÖ®Ýûö:|xt—.úR°dÉAÍ›7êÖmâêÕ<7ÊŸßíÚedAãÙ“'r²!g Ú˜«VňSBp–*8ënEO?Þ½víÔ¾}唦ëèÑ¡Uˆ]eèKã*îÝ«Ÿ'Oá2eF,Z¤|öøq½Ü¹s)2vùr%ØINô1äÀR£eËV}ûï6fð¼åó ä”û§ãÇmÆËið§9r|1eJƒÎ]¸8G“q–„ˆì!š.,¹ÖzLãnÝ>>\®ÕºµD1ƒþˆÏ>»sãFŠ4i†øûËPÿ¯×±ã€¦Må%õ-Õš7/_¯ž ¤JŸþð¶mkçε ˆ’Y™9³íÀU›6•?ë´m{òàAÿÑ£?ªZu߆ }¦M“6IÆ×íÐAƯúñGýòßúûoù_»m—ñ×lÝZ ¬†`w…¸ŽŽ$HœX ÖÅQ…$N‘âíÛ·4S/M²Lid)ž¤@‰¿.[f‚·-_.#nÙ¥ Îú3•&ݻ߿ukÁ„ † þMˆ¾»ÊЗÆU&MúåŒ=ëÔ©Ò¤ÉÇ5kª#§öë÷âÙ³~Ó§[O©ä¼|ñB¶½±ÝºÝ¿}»×¤I:3×n0þ}…få¤úÜñãÙòç·¿nþü˜±bI! ·Ó$ÀÝÁB¢UÚÌ™­Ç$L’DÎQ¨:(iRùÿåóçò¿„æYƒúõ×{·n½ °‘ÏŸ>U§üÀªC®Â…þö[i-¬çæèÑ7¯_ÿ¯|ymŒ¤Þå³gß»Wš y‹6^²Nˆ!XMðoßêOc°À9 Z2uªXÚw…¸Ž¡â¨B2æÈ?Q¢þ7ìÒEÎ2åÊ%™;ÌK‰¢>¬\yá„ ½§LQ?h©ÿý›6}6x°‚_½|)©eóâÅò¤L—NN!Z÷ë§Nì¨wÍ….­›„Í¢õQ1(k¾|¯^¼GΠìÎÇ·dIïøñ§lØ ½¥kµjïßoÖ³§V<»ýalÊï¨tz4ASºvm9ùÕ±£ìÔ²»Ø¿éôéƒýýmN3¬Or¤†o^½:w̘®ß|£s†£Ý`\¹ºuGuê´~þülÿýaý‚’k„C$Û-§I€»‚=„O† :*bÄŒiý§„Ôׯ^µ/]ZB[×Ñ£³äÍ+ìÛ°Áú›ýøA;UqƒžP¦ÅM•Ú0t zŒƒ:FÍ—×Ο×Þbýv}j.üëÂ…ì Ú l–æPç]!®c¨8ªñã¾}?Ž1{èÐ ={fΓgÀìÙù?ü0Ì ŠŠŠU¨0{È9A’3"ùsïúõé²d±>m“˜»{íÚ!sçfÉ“çè®]CÛ´‘jlÕ·¯¼†Þ5ÖuÚ1øöK§NIIš2¥£ùTjØP>Ö'ÊÆ&Ó?~ðàÐÖ­Ö_)Hö5ÒF§=šBUOÎ äÉ3±W¯¾ß~+•\¢ZµjÍšé¿EÎHÕ3ùˆÝ a}À7?ÿ¬¦†úúþyèœô™6Md3Û8ÞÞê×—3ŸÎ£FiGÙøÿ¾xQ½ê¬{ƒ,E^Mœ,YË>}¬O“B{‡M©tÎÍ|ÓLŽlRrD¾zîœ í~Àê¦%%¨G6üüÉù?ÞR¦6ù#/¶¹•íòéÓòÿ‹gÏ´1O<±<™sçN”,Ùö+ÊÕ­kóү˖å(THñÆ ¬KõW3Äu G"­TÌX±†Î›'ÐámÛ$KùÕ¬¹áúuæeE9Ò¸–¬^]Úx5o[¾¼Ü§Ÿj¯>¼{wí¼y? ê^¥IiË—L›&ðÎaè]£qÔGÅH~°sõê…'ÖhÙòÅóçŽæ#c\÷î’_Õ¯¹w®Z%'?6<ºs§:ŸËg΄ØF§Ô ìöh2X &‘0I9EéY»öƒ;wnýõ×ÔC|ËõË—e7L’"…_­ZŽî7Ðn\øûîÝ2F¢dñãå¼hH›6CZ·¶{B"Óª~ý]ûjhÝüùÉ||ÔÏWÿö†e3f [° m¦Lêâ4¡½‹#_ñâÖowtgð$ ðH„`“z”ù’¦J¥þ)í½,Õ1ÇöìÉ[¬˜:|êÈ9|KHµžCö‚cÄŒ)±Ö OŽËÑ¢GÏ^ € Ÿþí·ÜEЍãû7 èˆ#FÃ.]¾ûúkI9jNR?qb`óæMýüò]:5X`YºZ`ýÕ qõi‹Ö¯i·þ¾t©T²ŽÅ*VlÕ§4QwoÞÔïíyäôfb¯^=&Lxýê•DÆY;v\û·ëÂÙcÇ䣱n³eã‘Ð)ÑðâÉ“aè]£qÔGE»pkúoý«—/½ãÇÿ¤]»N#FȦâh>ÉS§.ôñÇëÕ¼uÙ2™L’6eÞÿý/Äþ0:5 þi·GlÈ®'›™œkÉf¦’ •œ!/ :É7®£;äà ~@ÃÎÕ93ùºm[»'$4ÓfμnÞ(]ú‡áÃ¥±—œ·cåÊãûöY…ÖýúI³Ô¥J•¶ƒIc+vìƒ[·NéÛWBŒy|ÿ¾~×øû«>yè¾q·nú«id‰—0¡ü¿}Å ëkÉŽ*¤L:£>ÿ¼ï·ßþ¯B…çOž¬™;WZ©”iÓ\–Ç(Q­ÚW-[JÓ+ÁN"…|ZV{’¨µªRåéãÇOƒj8´½k4Žú¨Èù‰Ý¬õ­—ÔÒ¹rå,yòøâ|*5l(ùþÕ‹ò®ý›6}9c†õ<å,Äþ0:5 þ¼G“ñJ09'‘}9Ž·÷¶_~‘#@ð ´ÚIŽ|R^Ñ¢I*UoŸÕ¿ßÀ†vB¢nBvOHd†¯çÛoút Ù{ׯ—Í^§½ª³8ëS>›y†ù.³A#'i€§"{ˆ¶¥J©ö`³+™Ïйs§öëW7gN‰ªÍ{õ’wþĉáíÛ—ýä™@ŽÝߦö0«Õºu{{1{Mš”0iR‰z·¯_O”,™¤½.£FÉøa Œøì3É1cÅ*]»¶ÄˆÞVß};"OZ»vÙŒ’MgðæMÆœ9} hعsô1B,p·1cMš4 iS™Oõ-Ô;z×îµk ®£]’ò‹Wª$³}ûo¯_ ‘FÎOä¬`D‡Ò<,QbòºuÑþÛ—Ã âÆ‹÷aåÊÛ–/—4 ~d5LH{¬Q‡e¼¼KqлÆ&â¼~õ*øBuú¨Ø-¤ußú>S§ö¨]{ÃÂ…Uš4ÑŸO¹ºu¿éÜYâ¯ZN›µS‚úžïc°ì–vëÞ]vÿY;vø–(±túôàw@j'9rHIž:µìøŠû l£èvÚ1RàÞS¦ÔË•kBÏž_ýø£þ|*5løÝ×_Køý÷63‘ÛísÒX ÀˆG÷ïoß¾FË–RÉò§oÿþ›/Ñ¡ÃäuëB|oˆ7H(aíR³U+)Ö%Kß¾Õldqa(¤þ:gqŽNÒÌvÓ̉ œÄO”¨HÙ²i3e ÞY³×äÉqâÅÒºõûw%\ú× S'õ%G½k¤‘î6fÌ#GJÈø°J•NÇ÷¬S'øBuÚ1B¢jç#ä½U›5ÓŸOÙO?Ù±coï’Õ«Ṳ̂¶¯¯‘þ0:5€íÚ5 @íÀ­}KÐoÆŒŽå˯;·z‹úïÕ¹s Eš4Ú V®Úû-ýd:ušÖ¿¿õãC¼#´…4r‡ÎÙ £“´P­)E‚å…Ø!ÈæÓ±¾2gýR¬Ø±{Œ¯Þ¥dC§wM³ž=åŸÍøôY³é´£_NuÿùHÈØÿßNÉÖ3´ÛÆf‰ŽjÀf2¶öàv®Zµnþü‘‹[ÿEÑråj´l9ÎÏO«vÕ.û âÆ‹÷q­Zê sFŽl;p`¨ æ?¾|^küýµ[âô¼7¹ñw¿ÃAªÅzŽÎâ ž¤‰ ˆÂ$§Ú=1uLÛ¶mµ/:á>;wž2eJ—.]쾪ó¹è¿€`7B°IT¨PÁÏÏï›o¾Y°`AïÞ½ (àòE|þùç/^ Þ©ëÀê@‚ ÔØ±cûøø¸¼°1&ˆ£Wu>ý76íûž$J¯£Ý³› !Ø<êÕ«'!X ºÁå!øÌ™3³gÏ._¾|ð—nÞ¼©ððH…Ï.¡³ï{Œ(½ŽQºð€»‚Í"{öìêÀ©S§\>ó>}ú¼yóÆîKÏž=sùâà£åË—(ÿ}&—T£¿¿¿,ÚfB\GƒG°Ù¨ÞÈnndïsÕ¶Ü®]»jÕªõàÁmŒlR]Å‹—áQ£F7îÎ;2'NœN:;Ö™ÅÁÌÁfñøñcu@¬F¦—vkúôéü±„¶œ9sÞ¿_šÒ9sæ¬_¿^Žz©R¥R'«¤T©R2\¬X1ëƒQ« Òæ©ÝOå¸f³ˆëׯKL]Rš %èy·TŸr°uëÖL™2ýóÏ?²¦’ø¥•úí·ß7n¼ÿþU«V]ºtÉÛÛ[Ú'9²ßºukÚ´iÒÉáÞ`5ž>}ú£>’Ú¨]»¶´²ti]d&yóæUcÜ_|ѳgOõÅ#GŽ-ZT*PJrõêÕ©S§Îœ9sñâÅRÂ>øÀz¶gΜ©X±¢4*Íš5:t¨¤:i#¥T‹-’êÕ«kSJ\l'íAŠ)zôèQ¸pai{N:%MûŠ+$sK%%¹}ûv… ¤% ¾ :Ÿ‹4Õ:o4¸tgj/lK9|ø°4öR¥ÕªU“ÓÙe¥|}}¥å–8Úzví¶dp?Òa¤6BÜA ®{¨Ê¬³ï‡jqRòråÊɶ!YPÒaæÌ™%ŒÊH XZ§#Œ[-ŸcüøñmÞ;cÆ Ù=eËWŸÎ¦Ñ_GãG°Ù¨^77¾÷9¿m;"e–Œ[µjUõyrJ ®…ªo9ÌJåüõ×_É’% ó‚B°YH£ï7n\9Žk!Ò¦M+ FŽÝÒ`ȹ¾:Òú1‰'¶þS–‘Á§T‚Úæ Hû!‡ÔuëÖ©C’¶PšLi*Ž=Ú°aCi9´Ç$i͉a[·n-‡ã|ùòµhÑB)¡SʦKôùå—_råÊ+V,iH† &m°ÔúÛo¿-X° :+i“¤‘–Ì4oÞ'·mrmß¾½‚åÏ:«¤ºjÕªE†“Áf!‡]%èÑ›Ÿ~ú©‘éåð7~üxë¯á .¬ÈqßùòH³¡~?8dÈë‚Ê¥U¨^½ú®]»¤9·¾b¤’£áÔ©SÕáÎ;?~Üf9|kA_ôƒö÷÷—¤õðáêU«j“ýïÿkÞ¼ù´iÓÞ¼ysòäI­‰Ò§^;É‘#‡õÞ»wï~ïÞ½ß+mž¬š4'çÏŸ_¶lY“&MÔñ’¥rÐ0`€õôíÚµ“Ä s–6©mÛ¶JÐȰ H£µOëCqç—n¤öœ_ŠÄI»wï>vì˜õxãõ¬q~[rr? CmØÝABµîÎïûÆ'û¸|R20pà@ëî>dÙó,Â7*¡ÚÞÜ]á’q“'O~çÎ… Ž5ʺ’å|éÂ… “'Ov~)09B°)8p@=^tèÐÁ`o­Ì™3ËÄÖc$é{ë[¼x±”WäXoóRùòåcÇŽýòåKi/ƒ7!Ö> ¢¿ //¯ìÙ³KüáIJŽê€ÖW$D¯_¿–ÿ%]©ýÿÔ‘eÊ”1øviE¼½½Ÿ={¶bÅ -/_¾\ І)R¤°žX{‚¯||jb˜3gŽt&Ó°aÃà3÷õõuëínÎ/ÝHí¹dÕ«}6«ñzv$ Û’“û‘“µ¡í ¡Zwç÷}ã‹S#—TlÍš5ÌY‡»Y6\rS"b£ÒªíÍÝ.s“Ó€ &üý÷ß[·n­X±¢öÒ‚ |||B¼!Ø3©·?þüܹsÒÌH–#²œX7. s{DûÓÑýR¡rèÐ!ù?S¦LÁï—öCÆŸ>}úðáÃÎ/HqüÓSÚxãkôÁìÝ»÷îÝ»Ý&NœXºtéP•$FŒÙ²e“xôèQm¤:œ*U*››¸µ cZÏÈ}ûö)A÷ˆ$Mš4øÌgñ°q~éFjÏ}ëh¼žu8³-…a?rUm„yÝöï_Ü‘#G” kŠïU0ȇ,.<‚…óF¥#ÌÛ››*\b·„`˜;w®‚e£’3–-[òksp!Ø3Y÷÷’á?ü°S§N 40>‡;wîLš4iÍš5£ÝqåúõëŠã»ôR¦L)MHhow ­ó´œ9sÊÿÇ—c±…ÿýwi$ѶjÕª}ûöÉ“'78u•ÿùçmŒ:¼mÛ6G½ôÔ ¨âêÕ«JPÇ;£+ãRÎ/ÝHí¹o׳ 9¹¹ª6BµîÎïûÆwíÚ5ÅE·»Y6"ðæ¾5 Õöž'OžbÅŠ8p`ùòåOž’Lù÷Ú†D·]»võéÓgçÎò§ýû÷ïÿÍ7ß|÷Ýwõë×72µ;ìóçϵ1jU/^|Ê”)vߢõxùò¥ÔéÐøê¸óK7R{î[Gãõì*ÎïG®ª ãëî’}ßøâ$Ù(ŽoV3.Y6"êæÖ55¾½…[…ûúúJ–š\¶lYË–-• ¾… –|ìÚÁœÁž©H‘"μýË/¿”£›——׆ …B'I³÷èÑ#õ)NÁ©ÇâàO2Š ¤iß±cÇ™3gæÏŸÿý÷ß߸qCV¤I“&Ù²e+X°`ˆoWÛEë{€¤É‘‘Òj†ø©I…èTš»¹dé!ÖžûÖÑx=»Šóû‘«jÃøº»dß7¾8™FÒ¤óßž‡Ã!ËFDÁܺ¦Æ··p«ðFùùùÉæ4wî\ Áê £Frßa*„`Ø¡>˜FNµÝwtK›6­m=TAí2!ßûlsäÈñõ×_÷íÛ·aÆk×®}óæÍ?ü`änå¿þúK êu§I•*ÕÅ‹oݺâ{Ó¥K÷矆êá©.äÂ¥ëÔžûÖÑx=»Šóû‘«jÃøº»dß7¾¸dÉ’I¬1òp}ápȲQG0·®©ñí-Ü*úûï¿}||´1êÏt)V??¡cåÊ•ê•fë+5kÖ\½zµ¬òÒ¥Kµ_7°K*dÞ¼yoÞ¼Y¸p¡ÍÉÂóK7R{î[Gãõì*¡Ý‚sUm_w—ìûÆW©R¥7Êq`ÇŽåÊ•3¸P'‹-Ë­ûåë³»ŽusëÁÙøöæü¶m\©R¥²gÏ.gJ³fÍ’S&õ™÷€K‚aGþüùwíÚuìØ1í†\åß^nvÉyùÝ»w?.íŠÁ›xªT©òñÇïܹsРA›7oÖ.=}úT}À¾´ˆÒ@:½*.ööí[i$z÷î­Ñ~gÕú1–v=zôhĈÊ¿ë®oÚ´©Œ¿|ùrŸ>}J—.mÝS†¼±|ùò[·nýòË/‹/n¤ ² 9¿t#µç¾u4^Ï®Úý(8WÕ†ñuwɾo|q-[¶6lØýû÷¿úê+É::¿V¨/TÅΛ7ïþýûÿýw9Ú¹'Ïî:FÔÌ­gãÛ›óÛv¨´iÓ¦oß¾—.]J™2¥õOŠN"{ŽÇk7#_¼x1Mš4ÁÔ†ö "›ë;w–œÝêÕ«ççç'ÙåСC .”Ú=õ—£ÒüùóoݺU´hÑýÊ•+ûôé#‡lõÏ-[¶ÈÙ|Ž9d¼4B 4BBÛ¯¿þ*“Œ—cýæÍ›Û·o¿aÃY¤H‘þýûk‰gܸqÒnÉø«W¯&MšTý-¨Þ½{K{ ßóæÍ+¯¬Y³þôÓOꯆJ›!ÇÍ™3gþüóÏ]ºt‘%Je29¼úúúj?>¤ýœ½L£5a„¹rå:pà€õêOO‰­[·Ê|Z´h1iÒ¤/¾øböìÙÒôª/%H@æøðai¡¥ñSŸQ*êÔ©#3ܱcGˆ¿D?eÊ”íÛ·KÈZ¼zõ*Q¢D2áC‡¶k×.xFi6&L˜ Ÿ‹œŸÄŽ[!U*kj½j*©Ó§O?~Íš52Ä;i˜3dÈ ¢ZµjÖ?»%%”¶G›yóæ-]ºtÆŒÒ>ÉH ²eÊ”‘I j0–-[¦eÄxñâÉö°`Áµ‡¢Îç"Ðy£Á¥;_{F–Ò¨Q#)ª:½ÌS"õ¨Q£:uê$w=Ôo)A½,Z·n-Û˜ñzvá¶ÚýÈ.#µâb|sɾo|qJЯãÊq@>>©4Y©Ø‚ 6iÒ¤yóæêï?ËÄ}d£Õ©¥P[æ&'R6©+I„2±>™þÊ•+²µËR€5j 6LCu‹¨*xáõ÷qƒÅ÷:jhŽ9¢ÓÙ|¾RÕ²Ù¬ZµŠÇõÁââÅ‹¡š¾v Ú±¹~ýz»§L™2øKc‚èC¢a× ú“Í † F >¾Ný…ÚÕ9ˆÁ‰%¨w}!qmh§”v¥YGøqôªÎç¢ÿFƒK×a¼öB\ŠÄh»ãÛÑ™sˆõìÚm)Tû‘#!ÖFˆ;ˆÊà6æü¾ªÅ)A¡Óîêè1Ãv…ªØŸ±)1Ëæ[•Î::y ‡*xá%§êïæ÷qƒÅpÔÐHÒÕoƒldÊ”©P¡Bùóç7þ D„`À]Bu§ À.9–.]º´_¿~]xB0ˆ¼V­ZõàÁƒ°}è €HdÞ¼yÍ›7W‡ß¼y3dÈ6mÚ¸ü‡ÍB0ˆDÚ·o_§N vïÞýÊ•+_~ùeD ˆ ¸Ø;wÔ§OŸ>|ø«*/^¼(UªTÑ¢E>|üøñŸþÙúGvW!®T£F 퉻wïNœ8qêÔ©8>}úˆ-DÙ²e;qâÄ©S§ (°~ýúHøÃIð „`À•Ö¬YÑE€¨íìÙ³]˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜ŽG…`//¯ˆ.¢ Á€„`˜!¦C€éDíÑE@ÔµC0„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜!¦C€é‚`:„`˜!¦óÿø4C>HIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/default-directory-layout.png0000664000175000017500000005777214770023131026102 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+_¬IDATxœìÝ\çÇñ'aÁ{¢Ö½÷ªZµ®:kë¨V몵V­{Ϻk]uûwµî=ZkÝ{OTp ‚ƒMòÁ + ø¼_úzå.—»‡¹ožürg®V«ý˜§v€´„ È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@†4  Ej7 V«S» zIóHIh@4 !½è´R{€4z5ˆô £"@2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ hÀ@B¼OmXòÛ†Ý'Î]ð:èãx›–û_lo˜)õ¦¿wZ¹6Ú(=vüúèÓuuìS¹E–îÿ@@Š @twM¯&]×ÜS§vC€Ñ aJBïN/Sdø„&1³qÊæš=gž‚E‹—(U®jÍÚ5+Éj™R Œ“êÍ¡õº¬yšªHÒæÖGšsÇ+2þÆÕÑîìTF€Fè÷ü‘æß­ 'voƘ»UmÓ¹×€þ«dO•³^ðy}—E§g‹íGôoS½XN'k3i„¹˜]j´Ë|ÿW+kÛ“‘³÷9}aeÛ]¾ém}–Ú Ðû*Ò 4Ò¼°§ÿnœ¬ù7´öà_k_Ü^‘¢‹º±zåí@¾!'NϨ’Â-ÈÐäm}…u¶øû<÷z|ïÆÕËßĬ8ö9>»c‰í»~ß½¬›»mŠEØð§yjÜ{}_žôlFÙúöµ–ÝòYf¼F24LWΆÇÅ_ŒþîÑ™½›W-ž¿ìسc®ë^ùUÀéýŠÛ¤HEð³ËÑ‹·)R6§EÊ,6ýK[1 ‘V™eÊWµGÕvƒ&üýkŸNC¶Fw¿;п™G‰óóêfN‰oèÃÞ>§}l›Ù–¢€”a"[1 ‘ÖY¸Ô¼ùb…1Ÿ×›t!Î=äü™å¬š<èÂÐ"åg=‰¨õ?ßã_eNxþ:/½8¬h¹™ž1G>_TÅnQçÿéòÍI¥ZïÖ×ãN„aç•/0ðJä@щ7¯Œ*fþú¿ßÇŒœ°äèS•ΔÊú[×oîøÉ듼SÅ)ôåéu³gÌ_±í⫸'p(\§e§î?ôhS%WTð1ÌÆR½»±}öø) 7sÁ.;ö9fPËb ürÖd®|û=êÍx9èÜõ胕uô¹wgÈ­ÉeÜGÝŒ°o¹óѶæYS5þàºàQ¬¼W…¶¬ÁÞyLþHé†Y®Öc» ;¸ôEÔð ;Œ,ÏÏÐÔï.ÿÖ»]Ÿuwâ9ÉEð9¿qlë‹šÎØ±vp§äWg¨}¶4ÎÞîHK”Þ;¸°ïÁ¥‹{­ÿk^Û|œ/ô#këË¡òûoZ›F#ûÇzF­úäëÃîT*ÿóKzwì¿á®*‰ÄÛ»ÇÖürl㙬¯váЄ2„ûœ˜òõW£úÄ?É˳Æ´Þ° Ñ”më=ªgÑïØHhMZ}Ös@™ƒ/Iƒ>›gœ^«u¶Dgpiñ¢›Úׯ‡ÔKÝôl˜ÜÚˆ«ÂH[ÈÐHGìÊvm}éâçQƒ·wþór„»[ì¯KÃ_ô¨ßxöÕ˜ÉÄ~|R ¸£0ÓÌ5±e33ó˜*$0æköÙr:;:Ø[‹ ÿ—Ož¼þøTص¥íªl»¸²Uvø¾W˜éß³T 1ún}Y‚o/úªãˆ#ºãQ…;êù9ù;•®0ïýë´\rç“ÑJÇœùsd¶V¼yííõ:(žF'kc…¿ÜÓ¿zÓÅ÷b޵Îêæ–Í:øUÌôåþ‘5ªyí;9§Q¶D™Èš´(ÐÑãó‘_”æ°g控-¾Ï“ðLÕ~'箉¾KÁïûULíªwÃàÆZÆÚ²É’ÞX€x ‘žØºYÙfñNíIúΑ»=Ü>=•^Õ¼©NÐq©Ýççá}:Ô+žM[MªúàùïæycGÌ>ü2røÊÔ/{”»±©mލwû,_ ‹JOþ»;7Ûù;Bçî?Z^=‘Ûšç©Õ®]Ë/›~^£\Ñ\:'…0ÿû§v¬˜9nÊ_#gþü.½¿¬ú¿¶©¡­ËN} ž*Lü†azm}y­ì>ä©4C×:ýF îÖ²NéÜ™">;½zùðžë…£;¸ °S}¤~æ—F1Ò³}¹Î?ú±óesØDîð÷Ï®Ÿ:°}óº5kë\Ê/y+äîâ¶­u3–sÝas&ÿøUe·ÈÒmuÀ“·Ì1hÎIm½ùí_[u(~mß.aNtM*]›x´Ërð_iåü;ç;]F%ø‚êÅž™[ý´C~çnßÖ$ÿ7ʪ0Ú–MŽ´ñÆÄtŶPµübç¨!ÿ{üUuícôñ]™ÞqøéШ!—– ÷¯þ¡ŒCÌn@¥]žêÝfíoÞrBÓãþ ‰å·µïÏÇ.¯ï˜Œ^E¦jSNÞ¯P­€]ܽ޿Žku™T³]番wßþ:bÔ»?-¼Ñ|Âg¦ L]â[_®à§›Á©ñ¼£›û—ÉôqÛ›Ù»•kѳœvа;ÕûÓcÚOº=h]mì¾£k;š³Íìs•jØMóoôœÛ'.Ø&X…ª§0ϵ½†žˆîˆT~æqøØ”:ºßã+lsWë2ûh“Fƒj4þ5*áÔk}³}]ìí×cMÚÔÔ#ßÓIão.^riмJñǨ°G›g ‰°j8´­2š;Àކ^Fܲ@F•êo9€!Y8rUŠÚŽ@Ÿû>¡"—ÎÉ)ÜkËÐ)×£”¦^ß§d|÷Ü0ËZsôö5—ŠvØ)]èùÕš ;Æ×é’+ç›üµk$:‘¦h·ß×;Ñøé {oõêë£ù‘"%¶õ“¦àÝ›bd¾Ov§ {¸zÐÜGу…‡Ü3¶F‚ŸÛ”™ŠÖ©­ßß’°À‹sÆY-Öî›\'Î*X³lfX~˽ÇÁÈîþ Ccæ^j7³|"·¯IlMZ—ì9°Üô¤gkæž·®‘Sj(Ð/fMbèƒõ3jÏ#¹,êoЉd–£å„wN¼1ö÷Š£¯¾ý&»ñOÔŠÌ5{µÊöÇréWòOö{6¥\Áôu/#Ixë'‰C›9#«:$”_ »Sß\1ç¿èâê\ýVŽ­žœo=äøpnÉÚ§ÑCå~™Ù&güŸÍó~;oÔìâ?Gõ÷{þ±ô¸e –/%¾&…Eþà ï¸_Z›~Ûfí}ñyǸ·÷g~ýív w×)¶’ BÜ «ÂÈ[ȘÐH_”æÖ:g¢Ð 0Ýz…=Ù³6ú«ñ‚]º•J¼ÎΪЗMrNüÕ+âqøå½7¾Éž¿T²ÌQ"»‘—™zxêaÐ`´>ÜúI’¹EŸ: _ÚÁ°;Uȃ?·Üž°ô•èú6¬ Û;?^Á¬ö ‰Ô¾Zé<°ÊÏ=OG½<ðç êeãÿ¦$ñ5)¤êßaí³î_#õÍœµåQÛþbŸ¥Ô¯ÏÞðR;T²o¯Òi­‡4ñÜ€«ÂÈ[È ÐH_T¡¡‡,l,t;mÞ^Ù£-™ÊÕÑëq–9?Ë)„”u„ÿí;oÂëà.a¾×nݺûðßg/ߌ¸7F‚=¥žy}P‰L&ð µÉKpë'…²t³ÏùÀdÐJíwéÀÇßkß$wŠ}pRù^ùûIôPɯª'zé4³ìµ[§£þúÇ'¯ú©ÊÆûýŒkRâP}P|k¦=’ÎÏ[y³gì_„?Û9ó¯ÚVÔÜ©©}¼4Èn¨Uaä- dTh¤/¡þ>>Ù:Ùè¼í{]|¯Þmið×Éqñ÷~¦9½$£}*ÿK«êë±èTWbýT  J“@’±ÔŒ"¡­Ÿ$®%òÚ%¼—v§ öºìÝkž©tå\É¿Žµ¾Bž_óŠpú¬T¶ÄO9Ê·7¢Jf¼®y‡ˆìñuT&¾&£X—ì9¨ü´ç¥û¿-8;|i˜É;äÞºÙk©}óaÍ“ó»C3än Uaä- dTh¤+¡>÷^è|mï\ÐY§C&Ì÷ÑëdÎ>049E!×w¯Õi­gâSÆúé;§·~’ØgKìûÃîTa~OÞD?‘-ùí—!Ìßëã­³äˢϢ-4ÓEלû{ù‡Å?iâkRË<_ûa†wØ/]åúÅúÙG'WÿR·ö#ðÊÒ…ÚŸl ׯ×MÝ»§è2ôn˜Uaä- dTh¤+÷N=ú8äT¤ £N¿Ž*ø]p¬W¤œçÆ}¡{rµÈ_ïëöÍêU+[¼`žœ.Yí¬--´÷ {´ ráAWR©¥iU‚[?I”扥>ÃîTêK;ËŒ†jÝòK }­°´ýØE”ЧËÄ×äÇI]¿ðhŸuÿjéƒÉû¿fþåÕ¤ktϪÚÿ﹫¢  õLý»§D3ÂnUaä- dTh¤'7þüOçVgÅêÖýEÂÌêãï>õöåáER®‡/ìñÚÓ¢ïµëðù̃›VÊ_¨P‹°ä_@"£Ixë‰aw*…&ßD„|IÁÜ¢°Ð]´~_µ¨CB¢,¬õŠfúp¨6ðûü«§>Œxvböúû†E­UÕË}³¶FwÒWØÍDîžb´ܫ”¶,Ž ‘޼¿°rû‹ƒÅ[Æü¹Œ¹S.G!¢¾Íô¾ê,R.@‡=Ù¹ä”ö”éÔníúA•2'Ð=æ÷Ô/þgµtÏjêDO‹êРtýEl"[ßH »S™;åÎ,„ä€tk‘RÑÜ1§Cô¢}ù†é±èPÍtÑŽ9 v>±.ùý  S<' \]°ìJ¿ÈK‡=þ߬ýÚ;˜[7Ò&õïžÅ(x„ä¯ #lYÞy4Ò‘ðgÛÇÿñòã°û×ÍóÅÈ2–9Kç"ê;O¿³? ©“à r éÃÍѽSŽ_ôªãœ`¶Sùß:ç•Ð¥yÄU&"oô6H•Èä!>:çÄt'Ñ­o$†Ý©¬r•É«"7åÛËÿ= ©W4…vQËì%s~üC®^yVÛ.‘óC¨÷íïÌ4r–Ìa¸¦FTÿ~áÑ~¯”=WÎ?5fu}|kÕܳÚI²´ÒØÕd~ZkŒ.•u-ž#á õ›Ó›.$þE«™½³Ò턟§fþ ]‚8èþѫɯÖU|ì{R«;q¦=¶¾‘v§R8–iXD¿9tkÓÞ'C‹&ýà²6–2K©¹ÅQ9ëÚ¶|úæKøÒeá/Nìˆ"oÍRNŒ³J—ÆÃ:8ï]%]ÌÂwó¬ý3ê¶µ;·pÙ=íÒ-C ·¼d2Ê%¹«Âð[Öï<&úÆ$€ô@åwjBó›tº9 šÙÞíÓDË‚­;;>êÄssÎÔc½W6Ìœâå}‰õÙ„=Ú4mO`BSD²t-æª=…^?z?°E…ø‹~?\\¹AîÕb3³²Ö¼gD~ô6Ø4Ntzn}#1ìNe™¿y»Â£ÆGåñË3çí1¿Z&'scYmñ¹ó¬‘—_ ?6góÃv?&ÞCîþ1÷ßèèÒ°yaÃ^èÌ¡êÀï ¬š"Ýd/h߬mk»Í^÷\ûlÉ~½J™êÝS u€GKæª0ø–5ü;I¾± #@#­ ýßâ~_÷ßø@g\¡A«FUˆãæ³Vžó¨1¹çß‘oÔ>«»ùêÒo_º?j™er‹è”Šì‡ñ?ÆcGÛÍmsÄów„y®41új"Ï·½Êz•[—ø~PÅ)ý¥Bõ3§ÎÈö绨§Ìk édR7ç4ÊþQ2W…¡·¬áßyLîHi•*àÙÅ[V-™·hÿ£g#§&‹wM®wä0ÏÛiþˆ9å&F}?é½²E«Í»ç´ÉŸ`çY¸ÿõ¿N[åÿÆÕ’˜ìŠQR슼%‚¸øËˆmmÖ·Ëûð óÞ5 þ·½‹õDœ2•ûº–õÖ=‘• Þ‹zMýöÔ„*±î䡸½²[³†èÕ±Ê[)ŸXyžçWíyúm<©ò&’¤­o$†Ý©Ìóuž3hf•YQ îÎjØ4Ó¾?×rŽ7‘«ÞÝ9qÁ¦ZíÜŸTŽÈÞX6e«»¸ûÑÈè¿í›Ö“óìU9öÅU>‡<wÛ ¶þ|üÀ2†ï6ÏÛΣ‰GÛÈÎÚûËfFßã•vyJ—Îm›ìk”­o$†Ý©ì*ý²ñç•&^ üç—Úùÿê2zÔ€Î_”Én½bÃß{Ýøwÿ¶kW¯9âÕpÏËXZþÆ2ÏóÍÒ«?ûñDäßrzL•ÏÎŽ3¡_óÒQ÷tQ>=ý¿¹#Î:þ±bƺîìÅ_¥bFéÒhXGç=+>¹«_öNƒëf1ÚG$Ïmcú>Í¢gÿ§u±îVÒDG£à:’¹* ¼e þΓ‚o,€¡ aºÞX4á„þ“»ÖóX°hÌWE»c°uñy߬ÁØ´ç/Ï}³Ðüæ™Ýòåp²V½ó{ãóüõC^…×Ì­Ãüq¿–þéBTwÌ‹=c¾Ü3Æ*GQw7'³@Ÿg÷ï?>Ú5˜·¶ÑŠÆÃ.':W‡ãæ·ÞÒa[Ô5ªÂnü1¨Ñƒ„•£³ƒ2àõ›€}?y¿ß°Øvp“yO♓ž, vÛrRÛQ×m{vpሃ cN’ÿ§Ë7'•JöÕ׌´õÄ ;•¾âØý;^Öjý;±÷çWo½z¸0sr+ÓÉ2<Àÿµ÷SŸù+î_-&acYî³yëêÍ–h»8Ÿü5®Í_ãD¦\…s;)Þ¿xâéóIê+ÒoǦžF+§ÈTu@¯‚+&Ý×W¸g_cVè]úßòKzO]©ÊˆÚH¸Žd® oYC¿ó¤Ü `(h¤yyª·íòÀ¾í*¹êyõ¥Sµ1‡®—Ó¥ûŒ“º×W {óôÞ›§ñ½(w^‡dõ²Y—¶k»gÝKnì}û’wÌÉœ›Î=°á‡Ì«Vè5S³œmWì½í]oì¿A:cƒý}^é *Ü{ÿoÿ¼/^œäÆ\`öÖ¿ÿ5Æ§Ùø¿e mò·¾‘v§2ÏÑbñ™Óî=ÚÚöXgt¸ßÓ»~ñÍ,¶¤l,3צ NËÙñ«1‡tîQþîÙÝÏbM›­Ñ¤­ë†×ÌjÄj «â=WšÔ÷ÌÇ1v+f’)Ê(¸Žä® ÃnYC¿ó˜Ú (4Ò¥µ“‹köœy u/YºBÕšµkU,œ9 ý_Öù[N?þyÏ}¿Íž·tÝþ[oã›Î&o¥Ï7ûêëo¿ª‘Ï.™¿k1ÏÑ|ѹµÇ9{ÿ£Ø7"³,ظߘ)c¾)㨠{¤÷L•ŽUǽUaúà!S¶ÝŠõ°uÑ–Ógø²€"èU\/—O™¥Ö¸ãO;î]½bã®#§/ß~øâ}ŠÝ5ÑP[ßH ºS)3W¸õv›c¿OŸ6å¾ÛïãžÊ©XýÖß|ß»G}‡¸g’”eæ\{ôþ‡­·Í?eÁæ ¯ãš$[ùýFŽÔÊ=“±ëež§Íàƒ:ŠªØ±n<¬ÉVÇå×™}²W…A·¬¡ßyRóHS}#BÆdQØãºÚ#ŧ°+ôÅÀE_ \øâö…s—nÞ÷ô~ý>Hefkïà˜5Gþ¢Å‹/š/«u‚EŽM÷éuo\í"íÝ;Lß×~¼÷•¿Oüwå¾—o@¸…mf×¼EJW®Q±“ö€4Ï×ÿ’º¿ÞsµÊÛdôÖ&#^ßú÷ø©Kwž¼zfn—Õ­H™juª»G—àZ—›é©ž™à|25Ü ×£t(Ö´ÿô¦ú·P)°õõøÍó¸¬ôEb§úÈÊ­NŸùuúÌýðìÚ™3oÞúÒ?0ÜÌÚÞ!Kö|EŠV¦t!gËÄf•¤¥ÌT¢Í/›ÚŒ]õüú™Ó®ßõ|±`G×<…K”«R©Dv›Dÿ‚ä®É(!Þ—ïG×»gi;´¡‹¡3»!w<ãàQ ²* °e?2Ô;OtãŒñÆJW÷êMÝ«§ØÖ9J7h_ºaçj‘µX­ÖÅjv¦H"ƒîTJ»\¥ê¶*U× 3“Ca“½dí–%k§ø‚£½ûoþò‡Ú<ÝT3»§ÄÏ8¸AW…!·,ï<ÈÐÓ¤zu`ÆÆè €Ò?šîÝSŒŽU˜4À$ßZ6v·ö¢ÄÖF˜ÖÝSR«01h€ ºµ¤ç¸è{âåì>ºYöŒz{:V`rÐS ò¿sæ²w°Zøúþ;O_{Q{û ë†SFV‰çvÑé«0uh€)¸0¶Q½q\ýϪΜÅs™ÖÍ»‹U˜:4Àt™•ü׿ï Pò˪L `zÌó–­õeç=z6Èm’·L9¬ Àô ¦À¾îõ†Ôn…I`U¦Ž È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4 D …"µ›,jµ:µ›& Ѐ h@4 Düj c"@2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 ЄB¡Hí&I¤V«S» 24  È@€¥0}íHUh@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4  U½b¢k…ðÒŽ©$ù\z¼\ˆï¥¿1VçUÁBTâªBÜ"¿v¼Ÿy…x+=n*Ä_B(t^.De!ÎK ±[3íSþBd•&˜%Ä`íHO!æ ±Kˆ»Ò fVÅ…h)Ä!²é÷×é.ñµYd¶á‘3„Ø#=ˆä.D[!† ‘IˆP!Êq#Á,âù+'²©‰þ-Q ¤Mn,Ä)é±…”5ÎÑPˆcBÔ¢³K¤Ð6Yˆ®Rþ‹´NJÏãtÒ³ÆQm@Ô8 Ä !²Ç³èýB’"l|6ñuÌ1j!®KÿæJ¯­¢÷ŸŸ„ÛpI ¬!ÚAÍ»u˜7…/Ävi¥YÉ\œþ+ 4€ÔsY›žgKqù)¶Ö”º¥–2´¥¿ QNÊ‘#¤Ü¬”º?‡I¯*(Ä:sS ±PzCo)Žï¢{üKï#Ä!ìâzêŒNzî-D'ižO¤ÆhÚðAˆæBÜ2D/l|mPIБéy‹­¤ŽêÒŸ³[úð ÙMˆ´Ÿ:4 ±^zpXˆÏ´#3éÌPÖÊÄ õ¼Ò>h"D ô ˜¤Î׊R¯ª4)s‘…$xLÂWzjYÌèé%ÄéAo©>á¬TÀÐ%®÷¹ìB<âÔ½=$Ö³áB ×>Ö$æÚÇ„¨%D!©¸©Ôœ ·!T[¶‘SZVd™‡«TÓ²Zˆ<ÒzÐpÒy‰öA–¸*Lô_9€ÄðÞ õ¸jŒ‘Â\¤1§QHu¥ÐÜWˆ•R‰°JëÄœr»6s·•ЉÏJµš„Z$Ör5cJJåšåv"WÌgßñ·ô ¯Ô)þIc~‘þ%_Âm°ºŠJÁwœTôì,Ï®îeÑåC€zJK}º'„Ø,ýRÿh[©ÚLg2MvüUª£8'D}iŒ£•:Ó„ ±TzPTê*¶ÖŽ_WÞ “º´5ù5@ªYóÙgÚÞåŠ:ó1¸„Û ”zâ#ÿØiÒ¿‚BÔ ]š á"sY²V 1h©ÇBˆ?¥âÝUÚ1+¥…¤2ƒb:S¶•âæ?B¼”' ‘;æ¬nK?ïRí²¥nB”â¢4ç±r°ZºœÅ!æIô¢°Î³ÚÎ:#JÑSW'éâ!I–p4êI¥Øc„Ø' Þ—þ­–ÿ Ä9á^ÖÊ$† U9J‰yžÔë!ïz qOˆ/„¸¦Sâl!èÒÒãüBôŠ5ŸUÚ-´ýÇßHñ±TQûŠ !F ±F*Øè#ý¢.úšnÑ õN°åáúÿ‘ñH  ‘* ±W ôg¤_[â 4~‰ôëÀéz/h•öž+ 4à 4’ s•Ê|IךÐÍvnÚÙcuš~.Ž©L¬™/‹'#f“2è÷RAðV©k62\æ’®,ÅÖ!l¥‘ùµ5Ĥßðù&éÏÔ¿ ºl¤Ê Í¿‘RGr!‚¤ê‹q:?L@ÒV ~h©g‚Ô9šU{1;!Õþ~©ýÜ{½çsFº¢E|¶IŽq=õ­Ôù}Mº.^t²“u¥Â o©¯wpÌ—¨ Ñ÷œh.Hw“¹)¥äv:‘*InHZ¥ßü“¼rñ @H=Ò… H]¤‘—.âí³¹ã}] jéŠl‘ÎKŽ6Mªñõâ¸t忨¬¤ÊZ1ú&ÄOÕV‘:}»Jýß~RÐ_(ÍPÄsé$ˆ³ š¿SŠÈßJw?Ñ|¨È,]zöÖƒ¥õë~NÚÊñ‹UL¢´OlÇX# "@H=ݤ.Þ[RB¼¡ƒ¶cõ©¢C¯¥»m é>Û%c>ÕAʈBJ½Íâyyu!¾’Ê't•–îTÒN  +b]"C££öjz» „X F‡hof®ËNê´VÆ[ÒVNÁ¸fõ2± ¸¿7€ ƒ õ8H}º¿H—yŽÌg*éÚ½¤ìh–ð‹µj¯›Ñ_ª!Ö•[ˆÒµ–KõqÞ¹ZCgH 9¾ô»YÒõ@h§,!ÄçRî/Çœ’.Î6ô–ÊçH¿Œ®Á(,õÕû.ÜÉ\9€¸ ¤ªÌRgê<ér_K—†°ŠgÊ,ÚŸñ}¢£ô/NJíe+¢‹k²üÚ›fB1çJÿ’Ì,ÖcI  µwçÖÇréß'’¿rdM’êžÔSÿ¥…˜­_­ í @0 ŤËÄ'4!…„8Úm¤84Ó`­÷¯HUh@4  È@€d @2 Ѐ h@4  È@€d @2 ÐbR¤v0mh@4  @¨ÕêÔnBJS(>­õNÉ•½ô¤-4u @È@bGO‘ÖÒgœB29@4€ !DçHÑmN I§Å¿ Ò4€t.½<$¤é£"@H·ÒktþIR@z“nª5äŠü)íc#@H?2ltÖE‡4@zAª5d!I€‘ ¤mDçDQÚ†E€&Q­!Ò`(hi Ñ9™HÒLhiÕ†•hi‡` @\ÐÒ¢³ñpkC‹ ÀtQ­‘’(í= ˜"¢s*"I@ÂÐL Õ¦ƒëß@œÐLÑÙ4Ñ! Ÿ @HeTk¤$iˆD€jˆÎi¥284€T@µF:@‡4€ ‹ EÓ’4€Œ† %P­‘pkC€q3nm Ý#@0ª528J;¤Wh†Gt†.’4€t† À`¨Ö@¸þ€ô ÀˆÎÐÒÒ:4€d¡ZIF’F $цBi€´… @ª5`$tHH+ÐôEtFÊ I0qh‰£Z©‚[0Mh !:#ÕqkC¦† TkÀQÚÀD Ä@t†é#IH]hQ¨Ö@šÃõï¤ 4¢3Ò6:¤¤04qQ­t†$ e ŒˆèŒôÒFE€2ª5qÐ! ÀHÐ@FAtF†E’`Xh £ZˆÆ­ H·ˆÎ@œ¸µ!€d"@éÕ€>(í4h ]!:I@’ H¨Ö ‚ëßÐHۈ΀ÁÑ! ah ­¢Z06’4€8 ´‡è ¤0J;è"@iÕ@ê¢C@$4“B’284`Ò¨ÖL·62&4`¢ˆÎ@ZÁ­ Œ† ˜ª5€´‹Ò ƒ @¦‚è ¤$i }#@©j ½âúw@ºD€RÑÈèÒ4 ¨Ö2&’4> Et (íÒ84B¨Öð :¤4Š Ñ@ÂHÒ@ÚB€Œ…j rqkC M @†GtÜÚ0qhÀ¨Ö`@”v¦‰ Ñ€ñ¤“B€’…j )‰ëߦ€ $Ñ@j¡CH]h@6ª5˜’4*Ѐ Dg¦‰Ò % ÄQ­ M CHh !DgiI0*47ª5¤ÜÚ04ð)¢3€t†[†E€¢P­ Ý£´04@tá¤ä @#C£Z@Çõï€$ @#ƒ":@4:¤YÐÈX¨Ö€¤} ‘Q@”v @#ý£Z’†i Nh¤gDg0’4 ‹tˆj 0nm4Ò¢3¤nmˆ Žt‚j Hy”v c"@#Í#:@ª#I#C!@#­¢ZL׿CF@€FÚCtG‡4Ò74Òª5 m!I#]"@#m :@šFiÒ4LÕžÐ!ô Et€tŒ$4 “Cµd”v -"@]œo %­¯CÞýcKëÛÔP2òzจ-#ï ÈP«…ã"­ @2 Ѐ è¥>·4µ›€¢¨Ð+µ›f¨Õ=S» H! ŲÔnBšqŽZØ £BF*òN7Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4 IçbNæÁ·Ô²ÕÂÛ*[ëñа{¬xhߪSÍŠö £·01þîÊÜÂK-Ì[øvÛçz¼"d{U­ a_àWƒú™ŒÞB •ø­“yXÄ®Þv‘׿JúîêÁO6Îüó¦sÝÁ=‹;rNAŠ"@#=™;{çBŸÜ¹[Ô @´”ÖÙó:d~«Ì‘ÍJ©÷‹.müófï²®Ý{w43bë€XÐH9ÎìðIíFLŠZ¥U7>:"óuÁ××ó6J‹€D ¡7uðͽÛû-=uäY°°q®×²å´Ï„B3:æ4×ì³î̾ï…0sp­ýyƒ©ýjV´}=·Ó¨A÷"'zÒ§Yï>J÷¿Ž lfÏô™èŸF R¬®¶¶ëB¯|u§ãªþ]|ø]€¹m•î•×Ì)èøï¥ž}¯ì¼"œ²´ÿ¥öÒþÙµ=d!Þ^KÇ_Z¶ÝûÚ‹p!”Y »6ïQvÜ·W–ŽùmÉö WŸGìfy‹7ü¶ó¤‘õŠØJ»™:ðζ5cfîÞóŸ×;µ°ÉåþE×.“GÖ/jñ¬Êû¯j9Çi·S×,žß~ì¹—%Ü=êÖÃåc ‡÷ŸUsŽ?#DÞ¡Ë6=ðóŒýGï¼SÙd¯Ùí‡ÅÓ›–°S„=X_®àì«‘­¹85¯ùT³Ïç¼>PÓ>á†C€†žTÞ‡WUsáæ¡Â±TAÛû[—7üÛVcš°»›æ—›y/DsZ/P¬‰óÛ¿ÏxÙº®úÝÐëK*äù¬p Ÿ»×ý"^ïV$Ÿ[–<ÎfñO¿¬~aöM¤¥ÒÖRzðòÉÐçÎYfr4a§—m)^ÙÿqãiÞLYEÈk?ßM÷Ú•ê°¼®¥æd|÷F«Jï•vi—™Þúß¹ë½j¸÷–Õ/ì.QÄJíý¿£Õ¿}(2¶¥JZÝ_z¸ánKU‚ IÔÛSg«W¿t]óÈÍõËŠ==¶ädåSΜªPÁN36ìÜÏVŸªi“"{I— NêçW^ùß…¶»_,½þEÏüú5$ àÆÏ5¾›z[säd-Y¥¸Søë+g¯ÿoâðݧº¾§u~‹ KSzWu-T3ef·ÙÞ_¿ssÛ¤»ºp°Sqk¡´´‰<Ü^ZØñй—šG±öL¥•]ä§Pϵ?×{k]£EÍ/íOî¼ðüä¢_jùØÝØP×Ù&{•Z¼O<ˆøVSéR¦¢«KY'óD–‚«é!ú ñZø«”ž-Š,Ú< wns•ÿ>ç-1Íóõ{_Ú9ØfÉSß²f¥-î.Wê·—¡Wެz\{ҨǖYõZsæÿi¶GïìJòt\|Ó?¬=‰”£4<ƒûx=êÙòÑDóë+|vV“So,½QkQ»‡½Žœp¯ë¹Ú¾òõüº9ìÂÞ¯ùî)=g~¼ùäZVJUð‰;kÏðûpøß^òê¼p„”ž-s,ºÞ´w!¥êõÓ>¥÷,M°‰ñ[ØKJϹJ¿^½–ƒó¼Ý¸èñÃW.ô_[ìd/{ó€×¿¯Œh“ÛÀ–·çd³Bíç5¬é?‡TŠk—‚ÂóÛR% ¸¼s¥&¤ —§·Í©h±››×´ï•ÙýK¯T¹ƒvö”Ò³UíW÷·)löxÕàÝN}ø{Á€­öwrVš™En/]ý|Î7úsWœŒ± ¥2rõóL#/­]ÚJ„y-«ßº×‰0ßÍ‹W¯9¼h½eûݲº=õ‘¥¿ÛùO›<šO½ÿNI aùsò DF^Â_ßÛý,âU¥rGì6JÇ"Û¸.]ôâãD–ncWÏñÅuð›·o½UjË\Ž ñR-ÞÝy&D¬sw"Ó³s"å9ú![Ä\ çm‘ÿìõ‡BØæÞÑÁR³·V,XÓþÖ–÷âͽÔÂÚûɲ¿#j1,>/?´¦ô«'¥UÁekÌ<ú·Zub¥÷Ë:Á»DÌѪA©…¤ç³æØÛiéÏ~In\¨§çºk\šªàñÀ©ò\²ëlÄ«†ÃŽèF6ÏÛqÔ>ç›>a Û–1jþ }3§·{fÍ©ÁÌ\ij°Ê_÷™Ômž£UïR?œ¸ þ:÷Ö£h–Ø5 6 0,2 ôòæå+éA¶¼NÚëÕ™ç(”ÅLDÔ~j©|.ê?ûÀ¦[ïb¼I uxÌá¤N™m¦NÒiÙÌÜÙ^“Í1gäof1æ½æÓd¸fÿ ~öú‰4:G‡è+Ê(Jd¿ª§o<Ÿ…F2Eí´‡Œ2G){3á§sÈÈòÂ/òëËßvÚýó¹G¯žÎn“¥ïØ<+zx¾ñy6³÷³™šñ–¶å¿(øý°2Ý«Ûðvðþ¬ÝØföØõÖçнý¡c™Óý‹v­†lQÝ%Ôërä^šÅ=—UÔ ¬\k4s=Ë"%r[ÅCæ"¹¢:޶9sf|…xõÀ?Td±”×0¾~añŽŠ¤S«ÔºA7Ôëd«Þ[ÿÖ¤—Ò?÷¨TÞÙ2øúο?ïår§ŒN©ˆþ’7*+•JEÌ1Ÿˆùa/ªÄY¡ø8±ÎQ¢WÅóÙðãDª0u|ŸfÊÈ`^,OûJÖºÀÌÙYºŠ—2÷F*>úmù½?xÿ{;(<$àüΫçwÞ;¹£Íê6d‚EÎî;vVܱ}ù†Ž]¹ýZâusçÜ›;7\Üqy¤KÔþ«ŽwGÖ²´³Hô—}*cF­ ‹üô©ˆ¯#¡†káJ  ˆ ½X8esâ™/îû¨óÚD¼ë…=¾öJçQjŸóÿF¤a¡¬?´Ëøzv ¡zèµ3þYÊ0!VnÎy4‡ƒ^ß¾S;[K9@åçw5âQ¢@Ö<9?D2×ߨ³I‡LøãÿÞÅø¡Baùü.èæXˆøSø“ â[¨¥«Svi¡•K/]•Ã.î©N¥ò›Ÿ˜¦=A7ßöøæÌ^¿ÀuÓžÌø²H Ã,S©¯:ÏÿªsÄnöâááó¾ùé”ß‹½Óöõ[û™fÏ÷Õü»æ¤.ñ{[üdÓ¼½·v%›þøUÎØ=Ç ð¿~ïUXûˆãDå{ë‘Ä8…kA§xoÃ|ÙÅ™ý†C€†^Ì 5Ì!.{‹Ð³ûVÜ/1´eØóóã·¾ÖF™ Tþ~!*a'Þܘ¼:²;9ì]€&˜+¢ºòÞßñ Ù-›0]fÙÝzÕRž=¡ ;|~Þi·ñU-•a‡¦\8ñ¤EÓÞ9\r¼o˜W\~,B_Zq-÷ÐÏÌÃ<Ž_ò.Æ\,¬‹æTˆ»jñäáúóåÊU·Œ˜fÑÛøjžË­} qùºðÝquçׯs+Õ~ÏǶÿ÷@U‘V•— Ìj~÷fî×O>0ë¼½Ù/-”Ö6%n[üÌÞS=åFÁ Bï.›Ð}Á¥ÖM·éYÑ^iíZ°qçÅG:¥ÙÉÔÊ\š•³/ŠƒSÿºÑ°S ›0Ï­óz ?æ/U7(wi—×L?ÜxA#'¥ÿÕ³nI£ ·®äq2Q(¢~iøÊóE°Èc›pÃØÿaXhèÇ2Wßî &Ü T=òè౤@&ÿ/­Š9‰[~ÚoêÙÊ–/-_âÜä©5»ø]½ãS¶NýǨMžÿÕvS d1>aâÍÜîc.4¤_Ù§o¿ªWn] enÿÍòê[+žÜëÿfRµ5+‹9Ù¾ò½'}¢ÌÕ©ö‚/­•æ–}GçZÐãY ê•G©µKJØø_÷·*g'¤樓¹Âºn¿¼™Ž?z'ÞÍ®±~w9û·Wߺ¶Êá°Ù;"DÇ>á[:õY\ju­+·ýuÊ·~r9ÛÀ[>ÞGtM·^àh«™ÀÍ¥ð»“<ã+­ßT9‹›Ú÷Þ«‹žš'¬Ûsã l„…[å<ï~ØóLý[¥<*—rµSùß;{ÛS³¿:7Ö8«•k«ßÆí«2öFØé9%sl.îüîÆýˆýÙ¢RïÅsÈ æùì64n¼³XóÇžFŒÉÙýÇoóIÅHYJä1ÂÅÓõ5Š.]½ÛºayÞ]‹·aìÿ0(4ô¤ÌûeÏcÁ›ú.?sÎ7ÐÓ7K“ïúL­p©VŸS>"ü}HD_²eÞúÛ§ú7÷ıç~§¯˜×kõý®¾¥Cvú6œyåé›§×|Â\ºtœßpé°Ï?„¾yà/2ÔLÿ.þéCé0€)³*ì¾ã†ã¢_..Ûî}󖝿íÔ­¼[û¾eêœ-‹TŽœ·ÛçÇOõxïÜ‹ÏöM~j4µÞ£Z nG2AQ ÚµuCóþî=ñþ…W!½ÌÚNk:½æ£j:ü}pG€cÍÊÿžq3úꦣo®Ÿ ¶vå¿*2bRÙ¯ŠJoæ6YGmküùE[½/þçqA/k›ŸçýΣ|¿\ÃbSú»£g]Æß²õØíÿŽ?ÖŒ±v-ðù·-<ƶk]SmÊ^r¥ØªŸ§ïÚwþÙ aëV¼I·®“FÔ-b+{Y¶•™rÆã§í‡5éÙ!OãÞýýR%KdVfi¹`Xû6ó6Ý yòìµ°Ì”HÃ"@CoJÛJíºm×MgTéWçºè šçoÐîhƒv1^Õ®ï½'ë=YçÙĦR„e«c=cÄUs‡WzÆø¶ÙÖe¡wÏ…Ÿ¼,gÎË4ÿ♫ҲR¿:gûÕÑ•÷•ºö§ÓüXïüõtF¹>RWŽø´aB‘¹b±_÷û5¾e:97»Ñw³ãy0¥Sùæ³w6w7Sغ·ë³µ]Ÿ¸Ÿuª{L}N¯‘B¨T–…; >ÔapÜ‹)ÑfãÍ6uÇ%Ü0À`Ѐ h@4 ˜”xJ¢“A€d @2 Ѐ h@4  È@€d @2 Ѐ h@4 :E)*ôJí&&G¡X–ÚMLN…"µ› ^h@4 mtjµ:µ›˜œôt\(tjUåþ]ѯ•õBEÌêØô´2386%V  ‰©ñ3¯Ø Cö€C€Ùb§ØË¯Ñ ú¤ ÄhH1h!£sœË%F@Ê#@€^L':Çn1RašÑY1RâeúÑY1Râ¶¢³.b4bH»ÑY1Œ‡ QÒGtÖEŒc @@:ŒÎºˆÑ`XhZúŽÎºˆÑ`(h¤*•ßÙ%£×Ÿ{è¥løÇñ_«Û¥vƒdœè¬‹ ÉG€F*R½ÜÙ½é´ìÛ®ý綦é ×a)»ôÇ;fÏY¿nñ–k%{žù!·YÊ.©(cFg]ÄhH4Rêåþ9;| m-™É¡óÒ©!)Üýl™·åˆéEBöl¹–²ËEj":ë"F@Ò ÓµïîÎÍŽ8<³‚Mо6邟œ½¯²(o­B™)Ñ\22 ¢s|ˆÑ :ݽ¿fÌæ'5¤ôk“Aø6(âDïá¾7Í]åÛnZ¿’Ö)Ø.¤3Dg}£@è´(ôÙîq½~ÚüÈ,“Ù‡W¾VõìŸæ¸êÇã/„Ýi[þS¡ë· t·NkôÍæö9œÅ‹oóv˜¶zFÛìW í3zÁ¡×Í·ýÓóÞ‚‹·ÞÎݤŠßvík ¶Rÿåʉ ¨»œð\UÓæÍK'ÌX´p뵋Ÿí'üÎÒÎ-lº¥(ÓªžúŸƒ—_†*³–n3ü×Eƒkf·†Xå{zñˆŸzfîdúæ¹*—MÛÚü;zÌòÿùŠ =Ý+—t²pnúÛži•w70e×3[W‡Ð—žÞ/ž>Ïþã¾Åc¦ç°ç¦ 9yÝ…»JßMš7«—ËÞ±FÎùÇ­óO&ªæ÷{ÏN?­9÷ºÜØ…Mï¬]½ã_Ï@ÛB-Æ.šïìš•ÿ;º|÷"4k‹‘K~Ù¿©õ½î[ÎÏ.Ã{y[Ñsÿ5bVª—ÿkž¿m·>ßÞ¤ëŒi×v–Ÿuò×%åÚ6ìÛË{nÀÈ%e¼vè¼öm–ËŽü'ÍB™¹rïi“ßîÛ:"r––Ez-_tlo½mN¦mÝð—cèýãÛ´ô¨ïmwëpŸqµ7ðò¤úÕÇ|pøìÁzš­~w²_Y¾v•-ßV!,O­M5~ÿog#‡ˆ)?œP¿Ã’ËnŸû>Ÿ…úÃÙŸJUšj–-—ƒ2æ ͳ7µ¦liQüËuïÝj—v²6«˜_‘wèî]“ªfÒœç]z.]þðD™©>O-ªôYØ~dà¥%Ý¿Y4ükå”ù=^ùã¿cãÛö_Üojׯ¿VJÉ‚¤(¢sr£ aè´'äé™›– œm#r¥Ò¹ÙÂß>ØÄÝùkSÎcfåBV“9•¬š[ì~xÿM˜pŠ|Ò½ÇÄ1_gS„Ô(pÏÎâ¶ÜFXf+”7‹•RXl6nY¿ •æÍüýf÷I¥¬bM¨ö=8fê%Ë&ÛGÕì¢Vd*ßýûúqíyA÷vþùDRß-"ˆ+ìŠ7©™eêê¿ÎúŽpÏþI†ÊlMæüÞq_‹ñ]f6Yj>zgíEÛ¥ô¬#GÍ/׈x¡ûè‘ uñ*Ô¨eã²}Ù†µ›¸}ÉeÏ@A€NˆÎ†BŒ€ø Ó«üõ«fZðG³Áƒ{uþªv!WÇx¦´t«Q9ê¡Bi®‰’jUìir•/.üdh6E–¶š·ûø5¿€ìA/Þ‡GXföÙó¸ØÞÙw&@¨]Ü!ú,l[®_ÿ¸f¤i£&b‡kç  Jkó8‹¤•ÙšÍ]Þ~_«15Z7ÿíß­%â/’VZÙY ¡ Si[fic¡;Œô‚èl Ähˆö(³5_qü7×Á—Oønó„ïò4Ÿ±eÍàJq„èpŸS¿Mš³õ’Ÿ…½òýÍ»B7JƒÌì2ÛñáÍË£Ý+6Û¬mñÅnŸ=Mï_½"—“í§}ȱYjݶðÄ©kþ¸ðøÊá^û;ô6Ó—?ÔÈϯ •.M~ZlÓ¨;Ï}ƒb2@FBt66b4è"@§EJDz=í1ÏçòÎÙ?v›2¬õ˜Úwçþd¢Ðû‹šÕüÑoäé3*;(BïL+]t„qšþþu€P:¹d«0jË–ÎAÚ~^«Üåm…±Ïf/Ä»ïÂ5I;‘Ù”·wñÙê¿«wÄÕAbž·ïæ3mrÄ÷²0Ï £ÿ*Û¿ÞÓ_Gt_Öâh¿‚q–`#}#:§$b4D"@§=¡OîýP£y1 çÒmƯx²«Ðà ÔŸèÀ[{.¨\º5ùÌ!þËÄÅGi¦aÁaúƒž¸"Š7)յȗmª|ò¬Y¡e­ïÛsÂ{¤{ÞDv7•¿¾ú~Ï… qSkÎÐO.ß -X¶@¦ˆÞìà[ z­,þëîŸ]6ß*öÝÐïW6;Ð3ûsBtN-Äh p¤=aÞ»'LyQfó7y,„ÊÿÎ¥ç¢P w{…™}V;ñϹ3OƒËå RØšgÍ—Eì;²õŸ§%«X=?¿ÿô !Üâ™å'¯µÊUÆMì:¸hÓù‚Ms¨|â+Dθ_òhóØ%³¶ßÞ9î`…s£q‹íÚ¿õLËF´,›ÃV© xùÄß>wÛOguwiÿ7žVœ6"<¿½™æl¬0·s)Z³eËjnÖ ¡~ýgÇb-ÿ ª¶ôáñž¹ƒÏMùa_ÃÅ;+ØYˆoÏXáÞoÐk¿ØÕ57{t@t6Ähq#í1w.SÜë—îÓ]³Zø¼ÏöÕŠ#JZ QÅcHµÃãûqšXáÛyÛµ-?|Vד½f7Ì=Û±X“<¾(kµãð¬ïªšÚÜùTˆ7¿æÓão3aÿÉk[÷›?è`ç9=*lËZ¼^‹v_duâòŒž#s­˜Ü<2H¿?4¨qLê÷/ž‡ê±áÔÄ®ñUZØUœpüX¶a#ç÷¬0¿“™ Tøâ‡é³;^œ9bâ._tأ÷{êwýi`ý‚ÝfÜÒzîú¹gu_?¤ÆÜk‡¶´Îá^Àü¯wîn'çõŸ³?޳~ò2ña–÷Ó͆gjG¤gi¼æ©UU„ÒòÿíÝyœåðñê¹îûR ˆàÁ)*^ 1fÕ7q¯Dˆ$Æc³ÑDQ²›d_³ë‘ˆïFC¢QófÕMŒ®*ˆ ((‡‰¹od˜™Þéáˆ0ÃL?CÏôt÷÷ûñ¢ú¨ªÎótý¨n:]Úä%ÿ<´ž–G:g  äMÓ«>ƒî9}î9¶trI´rÎeѰþ5ë«6ΟÒãÈKûóù/tËKþy¿L #3‘Œr€&ȶ÷|÷¶ç7F›§þpüÕó®¸éÊ;5ðãtûjõrWãñüXìåê…¯õ8dhéÈáÛ¶lk=ðó“_ù˸㢻ê¸ÿþž'5ëI+éœéd4Ý4AZ÷óãïxDí“åþNŸ»×/Úgýå Ü¿©Ö“Ò9›Èh [ h EÎÙJFÙG@i&sŒ²‰€ÒF:ç d ¤tÎe2ÈthVÒ™d4¹4ÐL¤3µÉh  h ÉIgê'£Ì" &$IžŒ2…€š„t¦qd4Ðò h Ť3NF-™€RF:“Z2h™4Ò™¦#£–F@D:Ó<öŒ«O9 ¤…€I:“µ/HËh ™ h ˜t&íd4F iQd4HŠt¦Å’Ñ@3Ð@¤3AFÍF@û%É82h¨ƒt&£Éh I h`/Ò™¬!£&" ]¤3YIF)' éLö“Ñ@ hÈiÒ™œ"£”У¤39KFH@CΑÎÉhàhÈ!Òö!£FФ3ÔCFA4d9é I’Ñ@’4d-é £ hÈNûÔ³s?Ù_FGf  !ûHgH•Ú¹ hÈ&Òš‚Œö! !Hghj2ØC@Cf“ÎМd4 hÈ\ÒÒEFCŽÐy¤3´2r–€†L"¡¥‘у4dé -™Œ†œ" ¡¥“Î)d4ä -—t†L$£!ë hh‰¤3d: YL@CË"!›ÈhÈJZ é ÙJFC–Ð~ÒrŒ†¬! !¤3ä Y@@CzHgÈe22š€†æ&d4d( ÍG:µÉhÈ8šƒtê'£!ƒhhZÒHžŒ†Œ  ¡©Hg qd4´pRO:NFC‹% !•¤3Z{ÞF>ùö"£!½4¤†tšTí Ò2ÒE@Ã’Î@³‘ÑÐhh<é ¤…Œ†ôÐÐÒH; é" !ŒtZ ÍO@C²¤3ÐbÉhhN&Œ £¡yh¨t2ŽŒ†¦& ¡nÒÈh2šŽ€†}Ig kÈhh þF:YIFCj hHÎ@Ö“Ñ*š\'œ"£áÀ hr—tr–Œ†! ÉEÒ ’ÑÐXšÜ"ö!£!”€&WHg€zÈhHž€&ûIg€$ÉhH†€&›Ig€FÑP?Mv’ÎHFÃþh²tH! µ h²‡th"2>I@“ ¤3@3Ѱ“€&³Ig€f&£A@“©¤3@Éhr™€&óHg€BF“›4™D:´@2š\# É Ò …“ÑäMK'2ˆŒ&hZ.é ¡d4ÙM@ÓIg€, £ÉVš–E:dMöдÒ ‹Éh²‰€&ý¤3@ŽÑdM:Ig€$£ÉtšôÎ9NF“¹4ÍM:°‡Œ& hšt N2šÌ" iÒ€Éh2…€nrû´#Qî½&Þúk˵1Р|AÌ‹Úrp$)w^ó"Sh   €€€ºYÅããÒ½ 4“Xlrºw!c˜¹Ã¼Hžy‘;Ì‹L$  €€€h   €€€h   €€€h   €€€h   €€€h§üÑSî?÷Å(jÛÿÙ?;ö dR¹ø¡·~ùvñ9×>ª]ÍŠªòÙwL7iÉœ•UQTô…Ç.øï/¶Š5é^Ã^jÉfžaŸ’#mĬÈNšF*éÞ¶C‡ù=Š‹ó’{ÀÖ5?»vÖ]eû|mШv‰`ØñÞ;—]ý—yQ”?xÀ—u=âð|õL³ª5&›Az†}:Ž ‹ h§è´‡¾²6äÛ,~¬l¯5k7­®YvÍ17}½M~êv’Q{L6ƒ´ û†4¯ŠbyÒ 9šÆ©óÃÜø†¹ïÞzÓ¼ß>·¦l[TzÊ…GÿxÒ QmúÙð‡®™·ó>«'ô»wB~—ËŽ\õ˹»žköå\ùíEÓþ¾Øœ¤TmýÕñ¿¹äµ(¼çÖ¹“-›ÿQeåuÐí쯻åªÞ}[5¸WñÇvΚû ûz·»Ÿ£èkøu¨ØXÇ‘®:eõçsiõ£†3óΪëΟ=ue‡Ÿ¼{Þw­\ðМüßÅOÏÞ¼9Šò;¶;ùËGýËjo^’„øÇ~ñÁsÿPE]îzÿ‹úíüt²jÉ<Úÿª5QTô¥?}åáÓc ëcÉÌëøŽ¿žyåÿëìÓ–ÞÓ¯{ÞÇO\úȦ|­|ïç3{jað˜¬ØxǸšŠmÕãîyg\1 Ç_=ä…§¶¬øÞUK.øŸOõ¨Ú<å²—kê¹Ãu/ž=é¤VyUÛ_ºþñ“²~Ës¯ŒÿíÁÏ^RXÿ^ýÿ;óÅók ûŠÿYÏvÿÐùþýÅoM_ÔÐëPrn³¯¼lç£V.õÔãf/õÂð£‹+çÝü»£nݸcÆüûß>|âQ¾cEÃJrQŸE·/‹fß·tù¸Áý ¢ÊþzVâ¦^U°îÇ Œ±†æõI›î¾ag=wºí³oVXùá’‹~æÁšÅô3ÍH@“;–.}`~b¡ëŸYšX(èÓoÜèØs/Æ_ýuÙêËû§w÷È^®¿±O÷Ä;Yñ)—õé0åÝuÑŽ÷ßÛ^VŽÉÊ?øUÍ™¾õ玾`@¢ û:ù©â×WWÅJJ‹ãQeÙ²ÉÓã‰õ§ŽøÎ‰­'ú¼V'\;섟N¯zé¾²5÷mp¯j@ýÛÍÿkýGѯÁ-Ö[¾¥×Þ>xD‡êtºùµ¯Þœø„|Ǻu[ËÖEEýKbÑÆx´mQâC4IhÝiÜ7Úß~ÃúhöŸŸX>hB¿Øê½–˜4¥—~­cëâü¤ÇXÝã¹|àŠ'þ’¸¹`ì°ñCœ zöýÞ¥Þ¶1-‡K.ФFùGë?ªYXyïãmîÝû¶÷W}°½¿O€i%Þy×§ù¥%Õ…¹.Š*Êãñð1¹ýõÔ,t=¼Mñ®uù}NèÛg÷¶._³¬f¡ÇÐÒ¶»¯uå•–éM_U}°nEùî€Þÿ^ÕVÿv·¼<¿Þ£ØÐ![ü›¢öÇôÙS-ñÕ/½uå5o>üÆÇ{?*^ÙÀ³ÀyŸºðÈcoœöj|Õ}Ú:îÓ§|TQ½zÈá_”0Æö3ž·¯Z¿²fe·#Ú=óûŒ<(?ÚXÙÄûФF,?oçy¸`Pßó)þä•„üÎÛåG[Ó³_d»¼¼¿}ùgïpƒÇd|WnÆÊÎhïÛ«vmï ï¯êz¶ú¶ÛàQ,kÄ÷(*ØóÔ;–¼}ÎØ×¦WÇN¯~7þàS#znŸ9óïÿ9è·v *èsð5§Í8ÿ©ÊÙS>(ûr›§'ÊvÔ„þÕa cûÏ»§È'¿­Q=oüæ' I¢ní»GÑGQT:úè{îïÑfßÛ+ßLÇ^‘ËBÇdQŽ=£heõCæoÞï\ó‹0•‹žÿÀ;±6¿úíCúöîÜ·æ ?œ³qÓ®;DUë×Ï«¹&VØ¿S¢hq#ö³Þí^pVýGQ>?|‹u‰¯~qQ¢l¢ØØ?ùÖóª÷¢jÉ’Y©ynrJ^ëÏ]sHû§¯Ÿ¹èÏ—¾´¥:4z|ëïÚDñ²c…K;EÑòê±`ó–x—Ö5“eÙìMUMr$PMjôê}þèÍÑÚÇæ=¾¬ÛWúäÅׯ¸ùüWžþ¸ÕÀsFÿâêö»cvÛ¢*£¾M.tLôêsѰhîœhû3sï_ØûÚ!;–.ù§ñ¯=¶!Š{ÂÅWGùÝ{?)oÖKUϽþï¯ö¾õ¸¢¼ŠmÏþèWORxæ=:6êß05°Ý õERÿ€É̾ªŠ]×Á7¬®HäȪå“þuÍÎ[6mÖ'hÂÿÐsñ®˜xëÚÕ‰/÷ñùÕ#0~àc¬ G÷SûDo-‹v<;çÞ7û\?41Yn½Û ICеŸðó£~uÒ[Þðþ…?8ixɶwV¿·9qðÜ;Û•Dy]‡´-ˆ6VD[~vÂ#ÏÝí{ÿõ™sÓ½Ëd¹à1ùéq÷Ž|ðØÙ³+Vþ㿹kpñú…Ÿ.v½íçû%Þ,ÛþÃÿóûQӞܰnâñSîÔ¾dÕÚÅ5Ðë“ï<«8/*oÌ~–Ö·Ý’‚z"™ì¨}¤cJö½O¬Ë‰‡­z3Šfãñ~_ºþ•²Õ' »iÁs›ªžÿ§óæÿ-stä ’Nã/owç-–ϯžEg]Ù«sâop©ců¼¹×Ý__¾-¾úûæü|`ëÍ‹6µZ­õ%Aš›€&eÚ8ú•™ípÓ¼‡§®[0{kTÒfÄy¯Ÿ8ì¼ÃìÛßùóŸù§‡×o)ßüÞšn­ =š^à˜Œµ1|ê[ítãÜ)O­~oayõýGžqØõ‡ž;p×xm5àðǶ»û‡s&?Zöö;Õ•[Ð{Dïó¿9ìûuéx?SQÿvë=Šd’=–Ìì+:ìÈGÙvÙw¾°tË«3ò>sùgÿ8©oùnúÜUK?Xµv~™IHòò]±ªû˜ãcšlÉnuŒÀâ¡£ßÞ{]à˜Œ¢¶‡÷ŸøûþëÙpÏžWO®þ¯‘{UpLÝþÞíÖsI½uió7ÿ/7õKÇíµî[§/ûÖßþT׬‡:Åvælï¯ÕvÏÊÆŒ±}Çs^«ÑßþÌìof÷Ÿãe÷ÏI|³:*êz‚¦ùh@lwDÄÇ5Éz¨_S@ëë_u*ßü»ëf'þy`^×ë¯ìÔ:…Ï/á»Ï\õ躥[;ßþêç.é›mßðÈ=«7•t9} ¤¡ùm4Võ9uÏÙ4U˼¦–“_†ÚÖ/»`ôKÏ-Û²j[âOƒ¿7æ’CRúÅŠXáà[¯øéòõÑÒKå§;ß4éíǧo˜õÂæ(¿Õ¡cú\òÝ‘ÿxVi«o ê# i”ÚçÎýMC×C2šnZ߸õP­ÃÁO—7õÉëuêQ¿<õ¨&Þ 4@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ t³ŠÅ&§{ Å1/ 6óZ2 4Ð@@7¹x<žî]€Ǽ€ÚÌ Èh   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€h   €€€ÙбX,Ý»@6˶€€&%  €€€d|@Çãñtï9$ãš“€€h   €€€h   €€€h   €€€h   €€€h   €€€ü/ ÷Jµ¸KrIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/yuma-root-directory.png0000664000175000017500000010507414770023131025064 0ustar vladimirvladimir‰PNG  IHDRŒøO +Ë pHYsÄÄ•+‰îIDATxœìÝXkà™M:$”TDElÁBñÇÖ£¿y¤sij••U!P&ѭ爈ˆÏŸ?ÿÉLŠì¬ƒÖ¯_¿¨æPÚ¹¹¹-Y²äOæPd!––VT³(„BáΙ0B€¡Ò …`(„4C!¤ ! ÀPi†BH0B€¡Ò …`(„4C!¤ ! ÀPi†BH0B€¡Ò …`(„4C!¤ ! EEúåCPDR:Eq+[W×Wç)»€R! ð E$©ì"J\ѽêôczκäOZkдJ‘Ì 2§çÿÜOÓCƒVÜØ>Åùç[aù‘Jó~Õ’ôÄз/蟋G¶»$ l;Lš<ºWG—júy§5IJó+T%,¨%Yý–lÀönª¯“º²ËÈ BºÌÓ›î}ô©Eƒã1DÚ•ÉC×;áÖ,ÏðM¿»¢Ó´ëòASS'š2!€”ŠkåÜÝAO,¡d¿Pâä/ŸÜ}•(êÃå55ÿxïÝÉ X³óT5x!$4ÔùÜ?߬âUoÝÍ^Wñª)©8#9áSø{ßÀOŠgcü.ÌúïÂÊš-ßpp]½\S«ö]º£Ñ”ŠàW·­ôǵ7¶¦¶:‘”FZœr¹= ¥BºP¯¿Ú{ÙÝfÓ£ ê亱§»ÝíZ+×ÞÚÔ÷£§¯Š’È—îšRW£äËdãÞ«/ªóãci»Çu½éF†”}:Õvìµø].Ê)®Ø˜õ]wx^­\‹®í™3ËÍûégŠ"â® ©×PðfTíi¶Qu;£ê%T*@y€.LGÏztôögÄß‘SÖ´¼8GçÇno™tø~= [gàæ‘­•R$ãH%¹R´þ¼(¼ÆðÓ¡ô/ »§ßözÖœ[ò•#*WMãÔtrÜeÈ“³‡Œ\ì—J’ćÑN¬ß\oU±¤+(WÒåKcÔê §6¹œN|¾4·Ý¢.çØ}{öÍÒžSÏ dCê3¶m±Ö.š}Aä1£´kW|øœªY©zë¶- UrÿàÒ7¡±l3§6m­*üú+*Šûðà‰ohTLšˆ­gTžI3+ýâKN•aK;½@þv=¿@4·ûÉÈ¢·o<š$`ëWkìÜÒ¼ {($n< ø˜˜IV0®Ö¨¥s­üÆä¨«*®t"ùjÅ}²Vƒ~î7¬õê5˜Fÿk•çAÇ}óø sÉûË~óÚƒ÷‘q,*mÛ¶1ÑÉõY'ô¹ýäCd¬˜£i^£^+Çš¿¨+=*àÞ£—¡_’ž¦‘EÍÆMêêeOœ]ŒVöáކZAK÷½ýÐ7(6U¢¥oæàÔ²–‘jªÉ>þãËÛ|ƒâ3(S‡Nb~ !]nh4>ã3àáÒd‚x4·ùº¿£Æ×•¯_þ½[Í‹‘RwÁÝi ~\édD ng¶ë¶„Pµ8öøMÏÚy¬ÏLÖí²šÐ³'aÃÀ¯“’MÔê>ÒƒÍ_¦Ý´Nx±hLß%§¿›T¥ÓÜ;ݺëË×žÂø@¯ÙÃÆm¾ýýÌmúxžÜ2ÑJ+/jl€Ï¶õk¶ì:–™ó)õªmVm_7 y ÕÂFõsU™Äù`|Ta—Çj:-:`·Ç¤Ië.‹~|\¿AŸ-–tjP…—φPú—Àý˧LXy.ÇkÒ±ÿgóƥݚTå›0Ó«£íˆóÁÙ¿¦¬â°jÈ÷UÚþðéY±EI¿þ¤¥®{ú®xEoIœ¿x*Ô­—µî×O'eIÛÆnWÞÐß¶Õwü&~w )ºS“×ü­l°S0uÆðýíÙ£¬»–ý´Éæk/F¶2ÈúU”âïsdÆø çߦÿ¸pN»IëÖÌø×Ú0PU§¾shí”™/br>U±NÇ™ —ŽílÃ&Âþ!+ÿZq«vŽKÐX}Ã%L¿{@˜yqû¼®Û?ÿ8Ovå–+7¬Ú¾¶:;ç'úòè‡Þ;éÆÃÎÜñjólÿ¼>#—‡¤e?í°—ò©édÕàþ‚Pµ¿ì{ç/Ë|¶à(ñW§A«ÓƒÝæœ:²°KÙÚw¿€.Gx <ö»=ê±ø¦ˆHrãÚõÚs5ññ“Ž}–©óôËsísNC’ªêt#.ÔÔàæx$GE…CfŠ)M•í¶º–›JhéKÞîm×eØ­ˆggž]Ø£é›E×Ï6‰¹Ð»eßSï’rÌüõ¡© ?Þ9¿¥¶^ŽV¢`}ç…Ÿ¾ýÎUQc‹Ò3å;kÓ>\ÑÊæÈ˜õ‡ÖŒ6,òïxFì×5-O#“œcn¬ê4hæ£paö$ŸC ä/=öÉ¡ž.uš¹ýÈ’î¹³=þÎúÎÿN»’ùmB.%ç|âËc}œ.·sõ:îÙû»“Õ¨Ÿ*ýùÓ…æÜy¬ÅÆa!ñæÑ½ˆkÝìp%Hžš"i4y9.Á"9<!ô“¬íÞq¢oüwÏRêk­¯çêqüá··§ÂfÊßñ¥Õ£NXqèâèÆy\'~²_Ï1G_F}}„ÅWåJ2oþçWç&ö¤ºÏUɳüƒÞ¹Œ7'û vòyÜ×Gø<–@(;ƒ]ê3±“ý¾«Îï£]ÌæªÒ_ý^PÑˇ4wÛù䇧éG«î˜ÎN÷·ß#2^­¿îÿ—eã ,þ·Åsz“ªZôšéùq£W… ˆÄG»ùߑѭrÌ•.[ÓªÕÄIc{wp2ÕUaÉC¤4#òüÖEçï‘JolœåQ¿ÞªAŠöÔݸS{²›SÆ kæÜÁ, Ø×°Ç̉òˆQk°þÔ®¾*séºbÞ¬ÙÝã* g=zvçß½8Ïñ‡—tÜ©ÇÔ·1òýè|‡U'wtªÊ#¥™qÆöXxžnt&_Zñ¿¿9F>ÍåS¨ :ú¢·PxnÕèÿêÖ_:¼ ëkÜ‘l5b9û¼R-c3ÀT‚;î7ØÖà×Ó|•vµK«=á™DµvcWÍß´f%Ž4íåí›êfòÍIäô¿»/¿)ßݲœ´qý¤µyôG+M¹7x¯íÒ2>Þ›ÒÑÑô¥_gÓçœxÞÑæŸ'Å1uÍÁž{çýÛRWE¶i)¥øÞ8¾zѬ“Aùfº;)á çcG#§ËÉéÑøjäÅÆj„øk4“¼¯‡¨x¿~ý‡œ|‘ ÿÍbú¾½“;;¨’RQZÔ‘yÿöºO×ölßÄzãð#ÿäùM»wpá̂оÊkjŸ–ª¬´OOݪ k°»þ3|û½4‚:·`Ï— óšüóé)gä› gu­úï4” érFÇnÿê‘•ÿZL¯r®OèÒ|U„boi«‘»Ö+¶‹®„‘!„Zï•—Onšýf·#*"Q·…'ETÌŒÞÿPb–Ë„ÖôÍn(h¶¼ò¼4­áÈ­™ÑÑ·Œn¥ûÃLÙãnZXÕ8ç²4«÷³¯ƒK#‡6?¦%­Ywd΀†ríŠ,2]©Ág»M¼«VkîÖ=ÇUçâ¤å V„ÊZÍnèËWÛ¬¾Ö¥ÙxÉ•ÈnMÿ^rOBP×6¼ØÿqûjÙ‡$i<–ÊZµÖ€§¾{k±¿NXÁ¹ÐV ZºÌ¿E7ïz ñèÛ½¦¬ÎSÓ䩉ÕT‡VIžª†Ž¶vIô!RÁÔZCïA‡4áçÿ… jüÆ´‚ˆp‚ßc÷ñ¹²ÒlÞ¹bèùáå[ä ­[oÀÍ›{ë|×ZÖ´ë°íaj“Ñ †o~š°zéÎ6ë«~ý`©8¾“ ­jú÷‰gçÚÿxšÍzާƒÞÊWy,5-ù1iÅôlMm¼KËÜÞ7ï¬<¡9FÍO?ºÚÁ,{w‘¦æ¨­÷\êÿÏ~ø‘t‚øtôó†º,l£›{éé\³Î7üO;g'¿¶¶ËÅôÖ£—4š>á‘€ˆÚ2óúÊ­sí1ù’|€3|ÕPô³Z!¤ËÍ6îgßiêv› "ËNR&ÔZ-=¼¤e±.T»Ëæ}ß:ËßCû˜¬<÷1UDÑáSm®o ÅvððÆSvû$ ˆ7g${vøáä)ŽAî„ΦÓdìúΞ…/ŽŸ^6À¤0û“ßÞ¼~52S¾W“ $©q/î^=wáúëdyc·b‹ã‡æ˜ojôó“g}eí1Êv,·Ê5϶󎎼×pã­OâÏïv_yÐ~”³âñÌxßcGŸÊ–¤YÉÓkU­\›-fŸô žçå0I|èÞó7»Õì Ì+{IMcõ¬½õñbwjµFó¼¾%ô÷â÷/]›,ÛQ\qÉÎmuòêÕmðÂu{.´½šâséBh€²>¸W7öÞ ’ éØm¿p }žmRúI«ßÙž Ûî~R¶³œ$-Ùþ-¡³Y ;ìõú]ÿµ/é¯ÈÙ][&·ž©“{+I¥êêÃ[œó>õ;t£ëÌú‹é˜÷ž²tÅË…9B>ãŠçÆHùÃÜÙuò˜Ê<„tyä8íä´+¦ËoeÈ~ÑjqéàøâÚÑEÝuñ?yä¤iƒz|>Òô`ë%3LsÀ²éaÍö‘Ë }!êPë7²¶Q ‚i"ÜÛ?m€‰Î¯'È%áÄ$—y?¥iÒæ¸Ï‰¿Lr®²¿¼ØõD~ª“…eçöõóhT<£aÝ\¶ßÙ# Ož~&嬘EüËwåºÍ:vqÊëÓ`ëýÛ£Ó¦kÓ$¢óŸfL頦̔fk¨de‘ UøóQsë»jržÝÞI?Øí/àך8À.ŸþGõëüccz;4€xÿìylJ YszóÄ;8…þŸthݽ½M‘í¢Þl?*ßaq[”{£KÆ¥÷Äê^ƒ3¨W/GÄ‹uôs®T+ÖmÔÖÞ(¿E¨Õuã°Øó‘´þÐ˹£í¿Ÿ@*zø2ZJ˜ËgqçŠb NòØX‘OhÓÌ^M•–*?ñ‹Q–ù ^$)™Y—S«hþ¢7ï\ìúÕË{’Ï>~U ÈkgN2ó¸b›Í%c2å—D´otzßêY_›'×äí]VÍ]t‹nÔíKŠm^óù]jWѲ†qeÕÀ·THDDBjmýœ_6 £•ò=®Yçïÿn¹'==íÂaï¡ö½¾þ©d~xr⥯l¨rË¡p8ºœBH—O²nš©¬!‰¤˜NþJUW“•g¢°U²¾€ª•ò¹óÇמ.¿;ý7Ë»Ãã;Žô Nü|á‚”_ŒªÒ¿»/M¨™!µd„iqA/níXåù ŠÈð=عiؾ3g{Øå\GùeRfd—ÿ*Õ´jE;™IÁÂ\¾ôÉ7{B{Ë|'4ª\‘Í!$DʇO™J i*%2-ëô Us/ŒZ5£|ެ†> Q Ä_›ÑõÚ/g$ŒIûz±€èóyƒ—dÕ¬ò{õüÔ§WY'Š7È?¹f…Ššô7!ƒÄD¥æºþrZü|CÆÑ¥§…ÑÁ×Q¢ çÎ}šÕÝ"û‚ÃWw÷û¢¿ö,§–­~wcÊ„40Áoß)Q컨~Ÿõ²3—Ø|óš­FLÞ®i=ËÊFZÙ±Ÿ|ÐE»Ÿ¬+ò\á^P|Û:u¿þÚ¼õßC&ϘٹÞÒó3Bïþoļð[k*ý˜”™)Y?ÕŸ´/¹êÙ{ªÓ3³k$gí4VÕÊBžšZÖû”‘YÜÛU?˜’u(ºŽ]>‡óÃæåwÐ"-ûÒ6Žš¶&ŸEýô“¦±T9_›ÌRì« û©äuQ\¡e&g…®ªvþ]¶°¹ªlÅk $y´þYlÞÏÛöÜjÎÃì-'FP¯Oíö]º ©âd‹”³«ödÊÎ_ä¹ Œ~zË-„4Lþ)úó•iñˆ×ßS¾ZVéæyÕ{RÎSÒhÉß.f.:¬ ;÷<­ÓúZ´Xüp]—eýÍiøýójºôÊZvˆ=5.#ß}삤ä¬wLS={å­¦Gg@fö„ù¤$SŠ»Kiª)õ>Q¯_ŠP¼½UzÖÎ}›BRÕU#ägŒ×ZøÊµ@ÙŸÝ+[UþfÓ_Å´ú ,²Ã7jzêô—H‰Éÿë$¤Š{kTÔòëLàÔÇÏï1í¢»HY<÷Ô¼£éWôÜCÖa ý¥31²eAzuƒ² ! ?G’òÖ%¦$â¼ï?˜“\Ò9óàè'yÓ³Ú€ còåí½bY´aó£ÛûU踇|<¿ÇÞ!á¿;ÇÜ´®1qFvÆ|ä‹‚Èû\!IHð©üÔ¯iÎÿ:¡ qü½l§¯éßòœPöá‹â^’¬*«äµÑTRû¿}Îlø(ß– k7r,ÔIyyªÒ¸ ±ý =rò´ØuXAÖMÙ¯˜cbgLÜŠ¤SúÓëwT&EõN˜×§? Ùõà%AäêêGŽJŒN”ßM­¢I~rý ÙpæB[÷~„äæÌuGO¬"Þ1~½"ù;¯™ËüŠAñAHÃO±8ªÚO¤g&&Ò­œÜë á}¿Pqž7e(>±1)Y\©Xç}]°ôÍ:ŸbhIËéþíåÑpÏÌÇôR"ÖNßÝsß ¯oŠ©c'±Þ|øòh_¨¤Uå<šUÒ‡Wž‰ä—uiµ°7Ì“f]øÄ*z¥œðhW°¤­e^>½ù<3CöFk8Ú~woE2k8EI‹«‹±ïÅ>[3s¥ül&‚ݱC7óÜnV%—úÄcºÝšùlõ•¸a~§‰nÑàob[&%õ{t2VÜÄ @E‘ÙgJä}SšA³®ZĽdzÓêѦ»‚AMóÚ•ñîUD¨,OyV榺…>[mКsZoÉ›fŸ›¸‡_;våãÌöUrN(þ¼óЉtY¸«öh×ð»†4©®ª&Ÿ85-g§ªEîÙá¹C†/ Uœ8¯ßÚÕµwfU0´ñø¥)‘ðÝÊ¥GÚzö.ø¾c›†]¬ ¶ùÅPo¯ÙóØÕÕ± {ËU ÔIBÖþ'Êg‹ÿYN]LH¥O6®ÐÔµI®1¨3›—|”MNÖshnR¡ðÅë5šÞ³ÆÖCo‰¨nãgT‰•o9´rX¥Ðs„2! ?§Ö¦A Þö’̽3WNجÎkåÄe½šž~÷ÛWÊþ)s;[®Z‘LowbÞáž?ìXN <øï˜õq~Óú}:·Þ°ùz(ûÒmïÍî«ÚüÓ­ùím·%QNlñî˜ý‡GŒn±÷E²lL »~};žÍÓ©Þ»o›«k®Š3ãf ïòöLƒ»ò81¦éÖG²wç=©ôúϱ}ÀÖtŠ8ì>ºKûÿ³ùáò÷Ç šŒ=*›ÉVýgìˆ?9½‹£fÒµ{×KN¥Fûm<à/Û+¢e3¼]îÍ(_Òð Výþm1û¥!wÚÞÈÉ}Õô¦–z,IfÔÛÛkæ.|ð…eWÛìõ›pIÞ¬‹ÛvéèÚWÜ'HâÈ?Æé3¼&u¶×Va‰Ò}¯l›ºèH©Û´÷î/ÅU€Š‰Û°n;¯¯ÄËÅsOMèÜUW,õQóÜŽ^âó!ƒ ?î c;cí‚ö¦*¤$):`Ï‚ñû_¤É¯¿Öà¶«™ñw)Bªü;ÝíÐÅ—ߥ’‘gêÖr]»°s=s–$9:p¿ûÄÝOåjþ3ëÀ_æßÇiåÔØX‡+Š:>צ…ÿÿÚÔÕW—t.&u:öíUÓð÷vÀFžØï]±ü²<Š’d¦$D…xñúã÷;Kôk·_u`ÿ+^Þwsü5ºŒ[4ôæX¯""õШúWw÷?¡]e.K"¦§Æ¼yxóØÞí·ß§u›>ã‡IÕïÞzýiÛƒñå?¬çĘ¥£»42ÔQ#¥’ÌÔø€GWvoZû0úïâsÙ!MvžÒ…ðÙO¿†ëÍœ¯Œv±5Sã²(J,Qs=ÒEñÆ5è6sè_×] #“^ö©]ûÊâ%[Ö ·ŠÒ#έw[qîüsá8N½8©ÞŸ­NI^—ž ×H•Hå'yT¶oéT'¯.q šý„¹cÛß iÁÛ«ßæû¬¡}Ç)“Fÿówk ùÍ™‹áL5íá[NT¶ýßøQûèàØ¾eý}qÙb‘èûo[ÊÏÙRæÖßwïFÕa}ÜOÐïÖ¥Ó/m$X:uEßΙ è@«ãÆ-ƒüFî~E"Ÿýk}¾>¡:°ovHê•W8§;´ó‚Ó "r—Û ]ô¢8,Qöy”ÁÿŸÇ±í3šÿù‹ç;ôYËuöãDůMM7-Ž{­B©‚.§H›Cb6‹ÃþõÕ<ÚuGÜý`3¼s¯#¾_²Oä&¥æãÖ{¯hŸ|q6‡Ãa‰Eœ\=–°9’Í¢8ùÞà‚Åál6!áä7I—)û–JX9/¥6œxày5û!ÿ›wB kxPBH¶üJ<¶žÖ¡JÜþuŠ>ÇrWõs²7†ÅaIé÷æçÎ?4{­Ý¢L'áÆÄ /LpÈZ¡ªUk³çaxÏÕ†ÏÚO¿_²)ÅbÑs£$ÕÛOÙëåÞÀ$ï+jTª´Üv7¢ÇúIC¦ïŽ–H¿M(•TûküîmKÍóÜ™MÖþwcl»Á«.>pîöÛO‰ŠËÎÅ,µüßø¼^5Éá䌊àišš[Ô¬S¿Y«ö=ºu¬nð‹žYYl6ý¡KĹßuzîR"þÉ—!{D¶c7„ü3|ù´ÑËöÞÏ ¿Ÿôû ‘²w‚”’ª5;Œ˜8}D·º¹ßD–~E'ýûž]=jâÜ[!™²¤ôûHEJÙjv.-w¯òÃZ#v=mÑoï²Õ^—ïûÆÈ{¨%¤b±úgϳõmçŸ év|~¿+’2eõÐ3e±é™ë×ë½iûæöyµwI–ì‹Ë¡ß‘‚u¦.é4ÛeŸ|©.3þ3+ð„Pf!¤Ë'VÛ±›SÇn.øê¦M<ÜõîyÀÇd©k\µ~KÅ [«½{J†{îI(¶Ý娄ŸÌ“¢tÜýâó˜ò»1ZyÇmúäܹÊVï4ãpÚŒ”€§/CcRX|-SK›Z溊 õú_¥úMA{K¡(ο—ÂþýöëO&¤¸¶ S©…y?ÉÑê4uWýøâ]èç4©e`jã`Sá—plõv½>MôŠ~õîcTªÔÒ7®é`«ÿ«nËùëÍØè=cã¯æŸŠâ¹=¤0“æ 1ùøýÉy.‚ÓøAZþ—€ç¯h7gÏÝ9{$áo}ßG|IR*Z†¦UkV­ô«¶%«f§)>¦â>ú|ˆMÎäð5ôMªÔªažÏ9nœ.ƒw¹ ÎUqÎϾNÏùþ=ç'„½yý>")]¢¦S±ºƒ‰f¾ßÛ®ëR©ux­?¸sâ¹b öüU¸¡iø-jFÖMrí§ÍÇ/cñ×ÁùãùŒ®Y³~³š?Ÿ°À-™cþtÂÍÔ¨ºƒQõ‚.ý{•,ëÐ?…™ò÷ývo…XD!§c›Õp0ûÝ;WÉñõª4hZ¥‹%ò}StÍk55¯UøÙþ\ÂéÞdì&ëFêeC™ƒ`éÝ=߯Èö7TÒÏI·0÷W…²!  4â´ÄÄ %J ¸{|’ç®4)A¨ŽïÛãý}C¹Pšûãë¸MÑàJi)éò®èÌ;znž@! B@i„I_D©™Š,ùêÚ•œ&Ý?6°¸oð¥B@ijñÚÒNÄeÉú,3®^ǹI­|n· åB@iŒŒpVvÀ`i†BH0B€¡Ò …`(„4C!¤ ! ÀPi†BH0B€¡ÒE………eff*»(ûx<ž¹¹9›ÍVv!yc\HGEEõìÙ3<<œ$Ie×e\¥J•NŸ>Mç´² ÉãBZ ÄÇÇþüYÙ…@ÙG7…B¡²«ÈãBÒ Å¸æp8***ZZZ8& ÅŽÆž5F00¤MLLŽ;–––¦ìB ì£›…fffÊ®"_Œ i‹U«V-eW |Œ iP@H0B€¡Ò …`(„4C!¤ ! ÀPi†BH0B€¡Ò …`(„4C!¤ ! ÀPi†BH0B€¡Ò …`(„4C!¤ ! ÀPi†BH0B€¡Ò …`(„4C!¤ ! ÀPi†BH0B€¡Ò …`(„4C!¤ ! ÀPi†BH0B€¡Ò …`(„4C!¤ ! ÀPi†BH0B€¡ÒY¢¢¢ÂÃÃy<I’Ê®~A ¨¨¨ØÙÙ)»€â…ÈR§N˜˜eW¿áÖ­[Í›7WvÅ! %%%EÙ%@A‘$IQÔ—/_”]@ñBHdQSSËÌÌTvP tBóx<===eP¼ÒYèõ¾bÀÐPuà@+‡¥Üz 76›¼víÓ£GY è¯@Y…ÈÉÈHmÉ’†\.þ:ˆ-ßûÒeVC¹Q ÅåJ•]äFJ¥h=C9‚`(„4C!¤ ! ÀPi†BH0B€¡Ò …`(„4C!¤˜ÌçA)A°‚’ÿäö“§ò[zÜ`„4ÃQÏv<ßù*“ócRSi…vµ:¿ÙøNeøœ:Œ¹?>->>÷Þ¥XÝ9›ì+“¿ì„œ¸ã¡ç3iûµº5ÑÊs‹”! ÀpT€wÀ¦ i¹Ÿ0P5©q÷Í·êN£lr†4!yxâÝŽ0“1* uc|ܾ;Ó iU:¤‹¦j( i†cõ?ß¿¿l€í7õ€ÝФV Ú\Ÿ[UñÔþ†Wéÿ8êóÛ˜ÀÏb®–Zm{ ’"HÞdïîý¼ê,ºMŠR_¥&¥IǨ²¶•© ùÞmªÎœ6/dž¦š²@—HBÞ%D|JH–†žzMmõ_·Å X ¤˜ïÇCÅ$Eùí@5ÛoÍñÛ"“èAžJïéÎûVæR œ¼½âõHz5 ëÝïîùûI,-^f²PÅØÐc¯óÄÖÚßÍ‘õpì©6çÄc¶t\?ÂèìÂËC="b®%Jeóÿ©{hŸ}EBRB¯¾ƒ(m¨¯ÿ)†’W<±§:Åmñi<öÝÅã/_N©Ü@‹ä©ÒO±ÙuçäûK÷“Wõ½9I›ÈŒü_ý›·ž$ÿқϦÿåqؤà‹çˆ¸vN⋵ÙDæš>W÷Çľ W4Ç¡j%@H”vÜ[íL ´Oc× °tQR²”È>²LǸ‰™ýw~{ÁÅ!Ñ–Žvf^è^Çœð<[ÆÁU·¶ î^zÜv@R»&úö“šÞmh¨BKðåÀ7i€ÒŽg¦*ÿŸÅQËã"*²j·:>§U–­x½sù“ôØÚê½ÜÙþAE?yÓ±á­ýsƒWŽ26Ìóªh‰„­¯7mKû¹^"åË„fçÖ= ?~.¡^mìï(yi€2’ÞXçÓfzH‡©NªŠÓY\‚¬¡®žgB“l*6ÌÙèjŒsƒM´¸b[LHØÆæ*Hh¥@H”’L1ýoºðëžgR$»|Z$’*Wš–&•¨ˆÅ²ç©Ìú_±„d¹Œk²%R2{õ½ ²©I‡îu­µUÿqÎâ ‘lö1Y±Ê¾sMFŒ~:¤ÿ[úq­jFžçšn®‚ÎÈ”! PJPbûõÿQëé6­HÞ!¨¬×±ÿ^ûOŸ"Ù>oÍʨqò§è_ùÛ’Æm“Sª#Öt±†­˜‹ü”±Ï£$mÎ’‡0ãR›uî~¬—ÝQ¨bnHhå@H”²ÐæzDðÝoÒïý:,[±ü§€s–æ\( B€¡Ò …`(„4C!¤ ! ÀPi†BH0B€¡Ò …`(„4C!¤Š)ÿQÜS’-–üþ½¤XòêÇ›V)þfó¿[F‘ɽr rÞ>ë·±²ß(„4@Ñ“¦e\¹eù·…¥+píAG“Z÷q˜;Ê„û9Í>óm>.D«¶ÉžÕud÷¦“^¸¶‡÷î ÕéçÛþÏŒU|õÓy|ü¿+‚Y*ììß)ÒÄÎdŠ›]½JìŸNø3¯/¥X›6¶àMåB È‘á/‚{÷z¸äãËÊDjpìƒ;±•ÖøÍv´”W¯f/ñý)Ûb=;U]ÕI‡Žíèý×'zÇôlÜ£xZæÓ“Ï^<WÊ””ºw;òЖЋ‘Ûbé,B=¸ýõZ»»4¶¨XÔÅ”Yi€¢E½>°éÈëd‚ôÙégÒÌĈ%»]3‡G„? ¿~?Q¤®Ö¦WÕꊑYI?_¸ú%*Uªmª×µ»‰ûû(çM<Óæ@å««?ÝÎ!!¬çØpBC{õLk.A$†ÆzŸŽT2ÞKM±~».¤WïY«“nú‘Ã!±ºfý[Q§½£RI~‹.UmµRO Mg×n]í/>Ý.&H2ìYøµ‡‰I"BǬB—n&X¹·"x·{Mn «Uœœ¼vô9×_úÎŽ‰ßj${R’vël„_X¦”Å1­U±s+}ŽlW6ß÷Ά¿ü˜!&ØÆÖ†ÿ2äÒ¨Ça‡>Lì+ï7¤dk¡ýÞ€(bçßîôN¢®ìðg TÆÉ2š¿ìï´æóç Ù°ÖJ»·¯›ÒA'xý²I½Õúö<ÿ§‰®Í„Ü®¯óí¸¯„U©Ê‚9=æ|è7²)ٺΙo|7ø9.¶MŸÙö~€HçțùW<®tœõdÐÁZ§úª|wŠÙqt§Ý7w^éÇ&¤Z•÷lªÆ%D²gX²¿^Nöb’”m °I‚Åbsùñ%¬¾÷ðóNµÉó¯Ï¨¸¦gÚí¹=N¸{'îÝ•ÐgÿÐ$ÓúU½N:77–Œo±wýÅG^ŸlÇ™äx-T|djÄG©˜$3"?Ïš(!È Bä=Èg  Éà¦wwسDñï?tsì Êÿ\º»Ý/Ý®[ÃÞ Õˆ”ñ-¯?y§ïñjGG8ß ·˜üßæŽ{–ȉoeB È‰3å1*HúŠý×d›nÕèQ„X·±£öcI1ÑÂüÓOt³Zrrù£;êDZ`ÝŒ}¶ç£¨o-î÷-Ú •,³¬Ò3@(Qu;ÕÜ^‘Ð?G$ß´Ÿ »š¯Ãu¦uÑ k°­¬Í"S¢Ò BoÆqgí͹½5]ò*”xѧŠÈÒ¢%ÝO/Éþ…§SaÂÆÆËÛ©„ý/—Óªk5!$¸jš~úroPft½½àØÓZMV'¯ms¯[AÏ„=keeï„(Süǧˆ”#i€@jð¸òÝÞtcW~ÖXB±,¬$âè¨4 I¨kví¦aäÀ÷ljzXZ¯u û:|w^ôGåû¥¿ÃãªÈÇàÊþÄ9ŠÉ8lYs[ÞæŽlSéôµ ^¯Éu:·à§|*!ÈÜes§k?ªžzüÿ&CýXÚœ®]LyôlEÒLBJ7åùjYg©òeTº M"›Š[QŸÇ’=!¡á P8i€¢'L©H~µtÙ'ƒ" ôj³‰[ªÚ‹v¶mZ‰KRC£ªzªy¬•Ï„üaV\.‹Í"¤1‰ñ§‘ñð\rk#Ù$õ(øZšT×Ù|ƒgaü•5ò e很™40׬l¡QÙ¢áš#!£.ÄÌ]|s…%[]Í¡ï¢0-èe"Ѻ½¹ñ*(™ž…Q3óF>“¦?Ž#:›Òáü&01“ *6ªÄ¡_°ìM¡äï ®–((„4@‘#Õõ鿬ÌõÃîp†ZØRyÆ4YW-5®75r`·+½ZiùžºøŠð|Öǵ.§ Ýž˜XêèêáýKšÇ}¹¦J©yoäX0EºZ‘ð,zþœ‡‚'ïo|T#ˆ´óK_ XRÓÆ„óãÈ”¼îÈ#­Îšž¹°òþ‚žU6V›±¼æî&xÜ C+«}Ž9èGÔ¬¹x|¥jÎ56»{nOß‹ ‰q‡~¦,¬ÖO«@ÏDÛ˜Kÿ{nÕóÁf£æÙUfc§7À¯!¤Šå<®yãS×] 8VC¿–âÜ®<òSZ×õïÛÚ†LXöHZ¡ºñöÇ­†Ôeç™Ðd®9p-ÌÎímоߓËÛÎh}vô‡Ú¿Kͤ˜ s´º³ÛóЇH©”0´yv0®Õ×›–øõ\ÐÚoµ°e“~gÞú޵¶1ù~‘_—)%4Œ—î¨}§§ÿ¢6׺&uªÛØáÝ{ÝÑîÚè+媴ßl÷òÙb¾v@„ö¸þw÷lõ“¼Úì^mc®J·›)“Á­Gî8µåiØ¡Ê ²9i€¢Géתú ÊZ>,&ˆ:ÔjR> !8* ô[x„E·³åaL6ÖJ‘L )‚;ïÃøy²9 é ºzþ-òdˆ)‰eŸa}ZgOK¾L¬©8Nž5kŠœà7~BÖ#Š’¤¶=š'S­¾–¡QµÊÞ{V{e9Ne½:ùh|3¯›¼²:4ýú8Eðt6?¾9«‡Ôœþi€â!•ç¥Â÷ûu¿a6ñ/¯G"esËÌë™ï§•f'•52©h?¡(k2÷rØù,oxç^bŽI¾Ä ózœÊg|ÈB€¡Ò …`(„4@N"õùs†ŠJáïÉÅ„Ïg§§£g(GÒY¾^¶œÔ¼ù™|z!e¢?£øxÁ÷¿*±€€È’––¦ ¥aa©Ê-~ŽŽg¡P˜˜˜¨ìBŠB ‹Tо*K E/£|>ÿ—c”ji€,sæÌyýúµªªj[õ³X, ÷ïßûûû·hÑ‚ÍfÓÃqqq 6TWWÏÈȋťk¿1½9• ««[§Ne×P¼ÒYèVv Å"44tüøñaaaÞÞÞ666ô#gΜ¹}ûvrr²ŸŸŸ§§§²k€¼!¤Ê&º‰|îܹ›7o&%%¹ºº6kÖìëSjjjiii^^^ÏŸ?_·nH$jРAÛ¶m­¬¬”X0ä†(ƒV­Zµÿþ¡C‡N›6­råÊ9öf«¨¨¤§§ÓuëÖݾ}û—/_ž={6sæLúÁ¥K—¢a Ài€2‚¢¨äädºõ<þüÒ­äüÆÔÐР›×_544l/÷âÅ‹Q£FÑílwwwz´)ò…( è|¥›ÎñññuêÔ¡‡ž¯ªªª¹wpp¸wïž¿¿ÿ¼¼¼ŒŒŒúôé£8Œ J(Ý‚ƒƒ ```°páBkkk:€9 ŸÏÏ3¤j×®íáá‘’’âçç7qâÄÔÔÔ­[·b8€R ¤J¥ôôtºÉ»}ûöwïÞmܸ±nݺŸVSSó—Ý€Ðã8::^½z•ŽêÉ“'W¨PaРAõêÕ344ü³Âà7 ¤J±X¼nݺ;wîØÛÛ6¬Aƒ¿;•Ÿ´¤s°µµ¥£šÞ¸råÊáÇÙlö„ p2@É@H”&t£ÙÓÓ“nÚ;vŒÃ)ä߯ššZjêïõ{j-GøúúÒ!MÈO wpp(\P@i€R €nΞ9sÆÉÉéýû÷tsöOæF’ä׎Ê—ÝÍ›7_¾|¹dɺŒ:4mÚÔÂÂâOê€ü ¤íÕ«WÓ¦M377ïÞ½»···––V‘Ìöw[Ò9ØÛÛ=z4""âÞ½{sçÎ¥#Μ9hX9„4I¥RºÅ<}út:g}}ýVr¯_¿;v¬†††«««ƒƒCQM(WÒÊôäÉ“;wŠÅâúõë¿yó¦h÷Bÿ½ @P¬‹°±±¹yó&ý¢¼½½wíÚU¡B…ÿýXü„4€rÐífU«VU©R…Ïç—äÒÕÔÔ ÞŸÉŸ¨%GoÐ ë™3gÒÍ÷5kÖüVÿhåB D¥¤¤¼zõŠn=GFF®_¿^Yq¥¡¡‘™™Yb‹£7AèWzáÂ___7776›=xðàzõꙘ˜”X ¥B „Pµ|ùò4nÜXé=kªªª–dHeggwöìÙk׮͞=[*•Ž?žNë’¯ T@H;:Š6nÜèîî>gΜãǺ;Ï"¤­­]èNÇþœ……Å09ºaM‡tjjê¶mÛÐ @nÊ_Y”a~~~/^¼wï^Æ ?þ¬ìr¾áóùB¡PÙUÈÖ>>>‹/f³ÙNNN­Zµ*òž[J/„4@±xúô©««kµjÕ 0räH¦]€¤¡¡Q2'ŽDÍš5÷ïßI¿isæÌ‰õðð¨_¿¾²ëP>„4@Q‹Å>|˜6mZbbâ®]»{ç MMÍôôteWñccãÎrïÞ½1bÇ›7o^Æ ÑÉ(”gi€¢‘œœ|ìØ±ÐÃnnn…¸ÍsI*ë¤ ÍÚÚÚÇÇçõë×»wïÞ¶m½¡Ó£GÚµk+».%@H… Ò =aÂe—ókŒÚÝ'OOOzÓÇßߟnRÇÅÅÑ¿2|Ó È!¤ /22òÌ™3kÖ¬4hŸŸŸ²Ëù tK:!!AÙUüš–––£ÕS¦L¡(júôéööözzzÊ.  $ ¤ ãÁƒ[¶láóùM›6}þü¹ššš²+ú=êêêQQQÊ®â7Ô®]ûòåËÁÁÁÞÞÞ‡RQQ{ölïÞ½111k×®-í=o0ä:éÂáñxööö/^|÷îÕ\.·[·nŽŽŽæææÊ.  ˆ!¤~mÁ‚ÏŸ?oѢŌ3jÖ¬©ìrŠóO+kkkooïÐÐÐ;wîÌ™3'99yÖ¬Y8¹ Ê„4@Þ(Š›7ovww_¼x1l6[ÙE^']h•åú÷ï8jÔ(‘H´|ùòÆ+».€"€ÈÝn>w¯o“&Mâââ”]NÑcòuÒ…V½zõëׯ¿yófíÚµëׯ§sÚÅÅ¥lìù€r ! ðƒÌš5«ZµjÆ ›>þÞ½{'NœˆÝ¾};nKü݆¦óIÙU(ý>X[[Ÿ?> `ñâÅžžž]»vurr¢#\Ù¥ ¤¡ì¢›‰nnn/^¼èÒ¥ =P毪ú]‡~‹”]ƒÔ¬Ysÿþý‘‘‘÷ïßwwwOHH˜6m𣣣²ë‚r ! e¢;Ï 6¬^½zÁ‚K—.UvE …–tžŒ{ʽ{÷nüøñ_¾|Ù¼ysýúõÙl6‘@ÉCHC™òèÑ£Ó§O9;;GDD`­šúAKú笭­w°¦·ö蜮]»v»vílmm•]”/i(#>|8mÚ4›#FЫWUUUeWÄtŠÇ”]ÓYZZnܸ1..ÎÏÏoÉ’%>|X³f N.ƒƒ†Ò---ííÛ·nnn"‘h×®]8Ù§à×I+»ŠÒAOO¯¥\HHÈÈ‘#é÷ÍÕÕ•ŽjMMMe—eBJ«ÔÔÔÍ›7xzzb?äoùz´² )e,,,._¾tìØ±Ã‡kiiõë×XCñAHCé#‹çÍ›wðàA#FЭ{.“.4++«Y³feffÒi=þüÈÈÈåË—7kÖLÙuA„†Rƒ¢¨Ó§O9r„n¾ÐÃÊ®¨tÃÙÝHEEÅÖÖöĉïÞ½sss[¼xñðáÃ5jdbb¢ìÒ ì@HCépýúu///==½¶mÛÞ¼yç…ý9´¤‹ŠµµõñãÇÃÃÃ/_¾L7¬éö¨Q£p5 „40¿¿?Ýn¶²²Z³f©©©²Ë);Ð’.ZfffC‡2dݰvuuýðáÃŽ;p8ü!„40T||<Ýz¾páBJJʱcǪW¯®ìŠÊœ8VLè7¶FçΣCzÁ‚ëÖ­kÑ¢…³³3ÝÚVviP*!¤q233çÌ™óèÑ£AƒÑU«VUvEevw+ú{»gÏžèèè§OŸzxx„††º»»;99)».(eÒÀR©TÑç–-[fÏžíéé©ìŠÊ8ºÍ‡ë¤‹[¥J•:Ê……… ><55uÑ¢EM›6-Ï7ƒß‚F¸sçΙ3g虋‹K`` ›ÍVvEeº-aæææ—.] Ú±cÇÎ;---;wîìàà ìº€éÒ d>=z´££ã¨Q£è5ŸÏWvEåŽI—<++«¥K—&''¿}ûvÅŠôV©»»{‹-”]0B”#%%åÕ«Wô +##ãôéÓfffÊ®¨ÜÁÙÝÊ¢¥¥Õ°aÃO™2e¶\ýúõõôô”]0BJZbbâºuë‚‚‚èvó²eËlll”]Q9ÅårE"‘²«(×è?z •nO9räøñã|>¿oß¾¸À¾‡†’C·ÛÜÜÜNœ8ááá1}út‡î<•ˆÃá…BeW²ÃÕS§NtÃzÞ¼yC† Ù¶m[Ó¦M•]0BŠEQçÏŸ§Û  TvE C·ÛÒÒÒ”]d¡?úo„nX»ºº®\¹²gÏžtTW®\YÙ¥2!¤¡x]¹reÓ¦MUªTi׮݄ èÖ³²+‚,ôgA·Þ”]äD7¬=qûömww÷ÏŸ?Óílܽ£ÜBHC± [ÏÏž=ûï¿ÿ,--7oÞldd¤ìŠ '6›cÒŒejjÚWŽnX3fòäÉË–-kÕª•²ë‚’††"{á©TzòäI:¤•]äÏç'''+» øºa}öìÙààà5kÖlÙ²ÅÉÉÉÅŧ[–i(2³gÏ~üøñ¨Q£,X€«ªŽËåbwwiAoìnذÞ~õêÕÊ•+CCCé¿5ggge×Å! J"‘¤¥¥mܸqûöísçÎ¥× Ê® „Çãeff*» ø úúú­å>|ø0mÚ4„ty€†?rýúõ³gÏÆÅÅÑ+Ž÷ïß+»œÒ'555$$$>>^GG§„{Ò¦[cô¢ýýýÅbqI.77ºzSÏØØ{_ ¨jÕªÇWvPÒPHwîÜ6l˜‹‹Ë¸qãªT©‚î< çÒ¥K#FŒ%Ò"¹”äBóD'´T*ÕÔÔ¤·”] ý]E'%! ¿'99ùéÓ§ëÖ­£W¬>>>•*URvE¥[ff&ÝŒ¦”uÉ2Ý”WÊrscøYl»ví«\÷®PâÒåïí¶½µF'×ßý¿'ÿjå7Rˆ×ÝjcÞVr°xÞD‡”d}åI$ÇyŠfëjtíVY›oŸBŸã„ÚfºX—eøtJY‡­|ùbùŸ0) ³“K,ùÀ×[i‘ò·o‘Èk|¶ü‡Æâðyôš<ŃÙ´”DÞ!¬bnBzŸ£Î&´µ¾¶µó$\Ö×…¡¹?9vô}~-Ózí«äÒ$!IÙðtåí]ÖôÔ+éò !¤á+Åø—]’åíþ?ŒC»ì×~¥¾ñ@=ŸÅ޽4µ»v¼ö¹qÝŽ%ÆÆÞ—\ºÙªÐ#¦„|š3æÞÚ‹ ô°zUãe›šÿK‡KRAgŸuïÿØ?™0s©ëÙ>k¦©~okØÝüd\/íS#5‚¹ÿ É€x»õÝ^5üaÙ"ÁéÕ>#çF ž©ÑÆ#ÎC5 iæÙe7Ǹ…§Óãp;Ll¼y¡µ¹f)¸+£¤%dħHÇDLÿCÒ?Š¿ÉŒ¤Ìød±˜"8|®!ŸG‚Ta¸Ï‹SIâ~Q©Ÿ"Ô+™ª²„ÂØ8a†"Ø,M¾®úÿ‡"€Alò޵~!ƒ™KjVøéþÌÏ¢D•õ­ôñÍQ"¶nzí±`ÉÇ)ó÷½Ø×·©KT¯.µV š´9ò¿yA½mÅÁœ.Üú¢2lq3'C‘÷ò‡cÛ‰½ß{¦þçž²„VŸ¼¶‘­0ÚmN¬b¦,6KƒþOƒ¥Ø#å÷ Uçý´ôC7fžëº2Îe\“mmÔN­x8Ìé õ¡oã ;Ï rØxMw­8ÿ3gßh¥ç=Ú0Wåv ¿N]Þ jí[«‹$Ÿþ8ì„û¯ºþ÷üv H/M“ªuë|r¶éóÕ·ç~ 7™Œ¿|Ò½ê»Ð&^Ãol8• kaSÕZZïÚ娬 îúZ`U 2â”ôã[_ßåY_b“H“DfÌ Îڟ칺‹n‰Ö?R´°:Ϩ;­¿á2ñÌ=?¾Po»ƒ‰°ÂÞÍg_÷¡(ƒ·±ÏMX¶õu‰¤Ô ´ä )Áù6ge¿ åƒìmæU¯H·µMuÝbâUÐ#¤WÅG±DL1Q ‚¨å¨§8<¬e¤[‰ ‚ƒbƒ¢SèL®æ #œSµ±&Q°&IR›J‡GdTc£­²‡8lmM7"¾ùâZmÏÄ?xêÿ—­ýpÃ^ö«—;8VFNI$Úÿ‘ *›j¦ÅUgj6fg$ý1gdª¹¿4úCb``ü•0aŠ* R_GK }ŽºìÃ'¸ªUuŽýÿ„¸Ô M~p&T$û:Á1Bå¾$(*é2ƒ{þ~óao¢DÚZì¤dI•5ŽÞjÙ@5uõ`ŸåÇ"¢Ód#iYTòØã<º™fÌ@—.“Í-›Á7ÃX›wÞçxêØ<”êµüf“ͱæfúÚ†dFXlp Q«CƒÛçëî|ô×¼ôLNÌõ¹½³ú“[µ¦þïæ©[‰Ru®(M¤fVqÙç±Îùž! Å@–Õ¥Ø2")Å#Ù›Iª²m´¸BÅÙR¡€n^4 43Øt«7B œLþþuv²ñ„b©üÿÔpAî…±5Ué8®¢Á®N¶Y>GO²y>“uˆ´OÿÔ¿yåAÒg-¬JÌϰ^}S3•Ð/ïÝòèÞBGxzñ“‚h7³Š­aLEyt{SÏæ¬Ð©‹ÿßÞ]€E‘pŸ-º;ETDTìî.TT, ì ;±°±•³0 ä,@Ï8 éܸ·,"R ðÿ}ßy0̼y3³3ÿ÷¦6I4¾´¬” Ià°w—¾¶­þÅÎ!.ßüíG½öMUVß²4Ñ‘¢ðm…ॠsâf­l—ŸJúÏi|Ñ÷iä/)<òN·ú›çE.;ÚYy¯p7!!“RÔVª¯’öxjм )Šõ×ÖÝmâã3(%]eõôñÝ›Ê4P"_g°Ùƒ&õ$ää¨L¦~Ýóÿhü÷>9)CÀ‘•4Þ¯  ™ÝhQ'äKZJ†€Áb©ëÉ×Ó—ªìµT…ÝObÚ©¾Cn¸oºï㪼n¹.ûvü˜Tn“ÆžK"†mü4¶÷åÖ£[œ{ MùÖšò?ˤ¤TäÌ,äòª¤«h®«XæE«±ø²:'mNf¿‡@°tAç¬xÍ6ôãnÈy %0 §(ƒÛßgˆ¦%CºEäü̧®ØØe¿j'3ª<„tu!H°³ðÜ¡°z“¹©:óS"/ƒ¢Œ[( hà°&ìõI¿UÍÌ4#ÂíÖ|`w·ØÌ¤ü©¸À«ï–m”2—IéZ<%«²ñzÏéFd ÏЦ}o—ËÞgŸ J l»'"cù†{^Âi¦›ø»˜æí^@ ¤« |ÝZîøõvO®èä¤N›†^¯ÿ-û¥ÊZÌã9ï^–o $˜õkfpÖxl5ÕÝ':ìÎ.*St Ž­®{íˬœ—67t0Ý%ç,8íwñBHW'¢4ÍŸõ5 yðò[=ûëÒs:•)™÷ôµà÷¢¸”„4äÅ>Ù#ÈÈÄÕe€J††ß øæÎ#Î9ß~•! ¿Ëz3deW„Ò4… )„4M!¤h ! @’’LŠbUv-*‹Qè ä! @nno%+»ŠÃa&'ã•8…BHÐéO.Xð°²kQ™‚*óþœ7oâbbÒ*»*5•›™‰ïÔªi€Ê”s²·ê$T¹ ëæ!û´|§N^•X“J‡+ !M;L&ƒ¢jÖ>µÈ5‡×º ‘„¦ùªHO¾Üžþ‰ ””TÙU¨AÒ´coÿˆÅªY¡Åç ÒÓkè™´îÝ»Ÿ8qâÓ§O:::|~­„œ¤adÍ—ËåFEEéëëW|XfdiÔ¨QÏ·D¤¤¤¨*uN¾ü(**Vvj„4-äÞó·lyV‰5©t$**» Šdó˜1c*rŽöööóçÏWSS#?ß»wïãÇ&L ²>„'N}šü\u„_ÜÝÝ $++›3äË—/¡¡¡íÛ·'?9òàÁƒ•WG(wiZ˜2eŠ¥¥%—Ëe³+h‹‘ÜÎÎÎ××—ÇãEDD#ÂüùóI_–„e«V­räH…5 ¼ùøø4nÜ8O¯+OOZ¤W¯^þþþ³gÏÞ¹sgV~!-§™3g’HÎ3œôª“““s~µ°°:th¿~ýHœWl¡âà\C­Y³fÆ 9×&9Žèl³(¤ÉaúÅ‹ŽŽŽ$ª+µš ÑÑÑk×®%9§g¬¤¤Ÿ|òñX¼xñúõë—.]ZQu„l<ÏÎÎÎÓÓ3ÿ-222y¶—­­mjj*ûöíXG¨8éšÈ××7""bàÀ9CH™„tîqH?ÛÚÚzU–Š®ˆ[÷îÝÝÜÜò?9#//Ÿ¿'-BB}êÔ©~~~¢“«Pa444Zµj•ÿOrrrùUsæÌ!-ª³gÏ>¼B* !]ㄇ‡O™2…t”swªX,Vž&Nœ81`ÀìüUÝþýû--- » ¸°æp8¤sÖµkד'O—gá².Z´ˆìtÞ @zÒ y’÷ºuëH[JKK«C‡RM¨8éš%99™$ôµkפ¥¥s'G䌌ŒüãŸ:uŠô§5j„gg«(Òñºzõ* ÚÂF(âÅJJJ—.]9rä‘#Gêׯ_>„ß 4ˆ„ta"Ò„„„„¯¯ïàÁƒ×¯_oaaQÎu„ …®Y\]]Gÿ€Ëf³¹\nþ{¹åää<جY³   œ ØP…Ìœ9sÒ¤I¹ãÉC^^žôÞH+­À¿êéé=ztþüùgΜ‘”¬YßÐUñ<<<4h@²¶°È~Z`HSY»*iŠ;öÂ… $ËË­ŽPÑÒ5ˆ¿¿ÿíÛ·É>œÿOù¯Iç Ù|ãÆ ÒÀ?wîÞqQµ>|˜Çã 0 ˆq’““I§¹°ŒŒŒHž4×Ê¡Ž-))éôéÓÇŽ+z´ÜwwçAvÏ9sæŒ5 -ªê!]Sˆ.EÿóÏ?>U%z<º°i6l(z"óâÅ‹åYG§Ò3óöö.z4ÏE‡4Aúâ)))cÆŒ)â´9”Ѳeˆ "//_ôhEo©þýû§¥¥ :eUé!##cÖ¬Y¤]Ä«BD`ö×Î;7î-} ²)wîܹfÍš?¾i•¤ à?8{öìoß¾=ztüøñbª#ürýúu²íرãcŠžš+âNKK˯_¿nÚ´iñâÅb­#T„t@Ž­mÛ¶-úÞÑÓ8E\x¶³³[½zµ½½ý† Ê¡Ž N·nÝŠ-ð1ž<ÈvOMM-N™+V¬°²²ªU«V—.]Ê\Aøåû÷ïdϺyófqFVQQ!ûiÑßC5cÆŒ‰'ž8qÂÚÚZLu„Jƒ®þ<øàÁƒ¢GûcH+W®\´hv~š#ÛÑÆÆæíÛ·Åy©'9Üq™37)))ww÷æÍ›?~¼E‹e®&dsqqqpp(æÝ^ÊÊÊ?~ü(ú}Ý,ËÍÍ­[·n¤ÛÝ¿1U*Bºš#Çë#F<}ú”ì·E)''WØ#³9ÈAݺu;v$Ý)ò¯øª âDúìÙ³¢ïMù£â‡4•õУGÈqßÃÃC[[» u„loÞ¼yþüyñ_TØKâò`2™·oß&;iƒ êÕ«W¦*B¥BHWsK–,9pà@q¾¤ŒCZèMBBÂÇÇgÈ!¤íœ¦¡ëׯׯ_¿8'ºEŠsM:‡¼¼WwîÜ)Yåàw'Nܲe‹¾¾~ñ'QUU-æÈ¦¦¦Ë—/'-¶ÿý·T„ʇ®¶Þ¾}»hÑ¢   b~Û`¯. Éæ¥K—Ž9òüùóE¼(*˜µµõ´iÓtttŠ?I‰Nwç éòùóç+V8::–tZ  Ýâ>}ú”h*2É/KåÖ±cÇùóçϘ1c÷îÝøâѪ!]=‘>±··wñßi@zTÅŸE‡æÎK1d.8™F'Ož$±¤H‘ŽŠŠ*ÅìHBÏš5ëÌ™3#FŒ(Åäààà°gÏž’NEB:,,¬D“L˜0áÇNNN$­K:;¨téjH rÉ’%µjÕ*þTä`ýâÅ‹ͨoß¾™™™dFÎÎÎ|Êi–yzzº»»—tÂ’^“ÎmûöíMš4122277/] 5Ö_ýÕ«W/²êJ:aI{Ò"ŽŽŽ¤Km```iiYÒi¡r!¤«¡7n„„„”ô« ‹sww~ƒ pqq!½ê’N bDòrðàÁy¾7¥8Šÿœt~'00°K—.‡nÔ¨Qé ©nݺõúõë⼺$?UUÕRì§Ä½{÷ºwﮩ©‰oʪZÒÕ͇H#ýéÓ§%íÚ–èštn7n?~ü† ìííK19”ݵk×È_³fM)¦%=³ÄÄÄRÏZFFæøñãä#çååUàg!”””={öœ8q¢t““íUÌ<ó#[Jô dg/] Pñ°SU+iii666¤']ЋĢ—™”n¾G%]j__ß®]»–®(5²ÕæÌ™óßÿ•nr’¬|>¿,¨_¿þäÉ“I?þòåËe)§†8yòdÇŽ544J7y‰ÁÊC[[ÛÑѱ}ûö¸>UU ¤«•Ý»w5JOO¯Ó*((ÄÄÄ”zÖ‡²²²ÒÑÑiذa© Rظq£‹‹Kñ´ÍCt°e¹õwذa_¾|?~¼››n!.Bpp0iÑÞ½{·Ô%óe&…iÓ¦ƒƒÃСC=<<Šùº¨\éêãáÇW¯^%ÝèÒM^êÓÝ"jjj¤‹@þþþxU…yûömhhèºuëJ]‚(¤IgºÔ1/BzóÛ¶msvv¶³³+K9ÕXzz:iÈúùù•eUËÈÈ”¥1M 2„ôÅÉgÏU éj"**jäÈ‘/_¾,õY,yyù²„4•õu¶·oß3f̹sçŠþB=KKË .”å /®¦²îX}úC€™™™¸Š­á’’’6lØpéÒ%q˜sw·¸ ÌammýôéÓ#GŽLš4Iì…W{÷î%­^ñ¾3•ô¤ÓÒÒÄX`ûöí}}}·nÝŠ—ÒBºªzõêižÿ›(‹£ŒoˆÌMÜÜܦOŸ~åʼ3R,>lii)Æ+ˆ¢ÏxOwçØ¶m[ß¾}%%%I`—Gù4G©¤{úäÉñ+'''Þ&V®\9a„µk×._¾\¼%Cá¸Y%%''“£Þ;wÄr):‡¢¢"騉±@ÂÐÐpáÂ…¤©€Ë“eôéÓ§cÇŽ=|øPŒeŠ.ž4•ÕM÷öönݺµ±±qóæÍËc´•žž¾hÑ¢òxÆAŒ×¤s#íéøøøôìÙSì…C©!¤«$ÑÓêêêâ-¶Ô_°S´îÝ»ÿþôÿÜÝÝÅò”WÍDŽËƒ :sæLÙß:’›xÁ*ÐÙ³gmmm>¬¥¥U~s¡OOO’vå±È222bïI‹=ztìØ±FFFe|øÄ!]õܽ{÷ùóç«V­{ÉeywÑFEzÿ7n,òk²ö–.]ZŠo .Zù]“Ρ¯¯OÚ”/^$Q]~s¡•ÄÄÄ-[¶”Gáåqº[DEEÅÅÅ¥C‡>|À›½i!]ň¾Æàõë×åqꘄ4éòнX‘É“'[[[‹¾,¯œfQ}ʲvíZ±—\®×¤stÎRö÷dU¤íêêZН÷.±ß8–[ݺuIsjÚ´innnå4 („tU’šš:}úôk×®•ÓÎOB:$$¤:NaÉÍû“ ²GH}|¨ë”iYiFB|’j«Q'wÙk–f™+GŨa½/®+§¹Óh„G¿Nµ+gÇfp4ôêÖçÆÌí=%Pf쀟!ýã­—í”­ {¬é«])õª ,iý:õ3“/O³*¢öú –Mhûy*! ù°L=ü/5hýn3>'Ê›á8À\‘ÏMýòÐ×ëÔΫ·¯OÕv{/Šz¾|úCÕ9¥Ùò L›kÉG¨PT„«²kÕB &­¬Ê!‡{¶t›~ãÇ÷T̺|#•ðvߺ•+6Ÿ^1´•ÿò³^Žý‹ûJ|}èÉ6«N½¬ãtK;ŸfeÎè"ð>\<ö¼î¸! ?XJö²]?ùï›.÷¾Þ;¹ñéŒ6Í5~?÷žìï´ÅÛöÐSº'ôO‚RL~ÝÕOmÒèæe»êð¦#ŸÕ>£ú©§\³¾ ÔkÖ‰öââ&y{ùšõí¯U’Æ5T.„4BÀýŸ›™ñÛp…ú¶›<Ú·P´±ß{íÀ‰£T)Ny¼1?’çgƒÉ.ÇO_üF«qÔ·.*¤ILëmݵÄÍô¯„÷×&9ß ^ß;×ß'G Ôž¸hÓò«¥x1%½¼gºí¢o³Æ‰7¤TžŠèµ™qÖÛFBJJŒ3) Ñoê@»ù;"¤ ûÞËÆÊéïo}ÒUBJ£Ñmóú[{%Ú{Ùœ' ”3’b£bâÓ2¹ ¦„œ²ª¦²\Ö`~BtTdhxJš@ Hûò!â'™Á–¯U[‹|þxdš˜øäÔTá•o&[V^I]]™Ã „ÌïŸÃ2 KUS[IV‚d~øò#…Ëf1(iMCm¹ë–žðýã çuÏt:Dÿ(‘Î×4¬#_HVsšÌñ]v ÅºÏ6Œß>ö³qvZÅßvw)i™ßÞ:’Ùcr"?F&P,&Å3¨£Ij.ÈŒûøñ;YT&ƒ©¨QKUŽ•‘ð)ì[:›ÍäH)éë(%‘vIBR&’UÔÐP‘`PÉñ±qñ)é\KRUKKA2§Z‚ô䄸ø„”Ôt._À`IÈ+©¨«ÈÿáPÊMŽŽIHNã ×ëGRÚïæ'ÇÿˆOLJMËä Â:©j¨Êg/OFBô§Gëý?JÔŽýðù“TJ¦†¡¡¢p{¤ÅDÿ ›#ƒlRK95 ™?'ÉqÑdQ3Èd¦×Á¯zóã¿~ŠLÊ$›LÀQ5ÔW"Ÿ^RDHD2‹Ídqké«‘Í÷èØ4ŽR-u…ìº%ÆDÅ’O/k=¨i(ÿ~&CŸL–Š"Ÿ3 I)9%5E‰ÄÄ„{ö#Ó%¾ ‘Mà(¨éj(1³WnbTô¤Ô 9äIÊõ ôs‘R|úÎb1¥äÔt4¸©ñß¿ÿà)j+§ED$òYLá‘+`kÔ2P’`d˜,ŠÏ•PÐÖÓ”/^³H¸!“SÓ3x|á†PTUW“6Yx‰ßC¿Å3„³¡ø<ž”²¦Žšƒ—õåSBEÖ)W»Ž–èCÉK'K‘,¼HÄàHÉ*«i(He-Ÿ€›& ŸCU-5EiJÀûõ%:!ƒEŠR««£(ÈLMˆ÷\iû5£þ—Ð÷!ÑI5m]åªqЍ†CHC©°eúôî|uol¬Ÿÿ¿é†¦ÂCؽv«¶ù…I·²0J ~­dµú°³M3ŠJô9ä|Áï~xÅË wY4K™!§ÞÕùà<5Šzuyq©k›šêªD^õÖží°mÓ´ÞIw<].¹%¹âì£5–¦?)èîµ›~¶ìõ¡Úø-°jÿ]ص|‡GFù¯Ç¢¿˜|µ…ÇuP(tQš¯¹6ãzƒ=O¢ì‡LîõïQrDLû¶b‘Sƒ9¯i÷kI {ì~ìÂÞm‡¾RýÞ . ûmï¼==lÛ:åð—ýuâß_]·pû1ß7Òʦ£‡7ðºúêk2™VRÃØvö ýÄg—¯ß x–ujB²ÉÈ w=ì”E¥óÓÎn=çÀ;S3}ué÷wþ~Æl¹æÀ‘=êVíÌè—ëìçr¿Éiؾ±žÇýñæßÌÜcd„Ù[ 8þ’eaÞPKŽ÷Äçb¬v?çs#ëJ 7–÷å»O}§¨Ð Kæ¼¢Råf8ÑG•â†ìkÓz5ÇÈ̸®./"èrpl¯‘ó÷ìš_«ð¾öý½Íß|â@¯Uã:’ ž€—ôˆ,tö‚qßùyŸò¾´ëðÕ Ý5)ŸWy§}¾·Õ~§Ó·™ ýOŸèwv‡ëå[Ï£¶ÇœËfPï¯8Û9º<ŠP°hQ'îCЇxõÑŽG¶Žm,*Nº}•ý¡SÞ‚Ú-êÈ'E…ÿÇ2Yì;ÕËyå¶%w|‹ƒ¯‚”a/› Ó’‹{~qþŠõÞ¢´j¦Ìˆzò¯”QÛe.õÞ%õßõ›vßx߸óì¥#»yø<|¯³þ¡_“;¶v›o¿‘P21ßÖjú´VªŒÔGgÿ¾sÜÙ)¥ÇôÕ“fŽéרXWž3ÂWŽx8Xв™±¶dCŒª'›öù¹ûþí«œ¯(ÖàÅk,»õÞÝŒ‘øèÖ%ß“Î÷äg¬ß½fÞ`U&qgÿœ5»¾ç627–J‹ xø®VÛžóWm·j©&d¼ö;¿Ó~ÉéPîøM7-êΤ½zxëÆ}¯5[Ï šlâ=_”þéùA×{Ï&’í½cÙ"e&Ë|ÊÊUƒÍ‹S}¨\i(¦±q]i*)9ísÈ7©AÊ;ë 3¤}òùo3I7%jù‹-“ZI7NÛ`¡8dþÚ~CouêÚ;8ºÞúÓçÚKrÂþˆPü÷¨^ ÷ì·ëËf³YÌÿFÈ»Øo1°C;=åÞã§w¼xSZRtSK¹çèé­[ËÛë)[hh˜Z/sˆ‹ô Ú§×jÖéó“%¹§çÙÏîÚ:ë$°€’²ÈóÆ’Öº|&[p·£Rç…“\Æ…ïÒ(¸Òi;&¶[}9~Ô©0÷‘z\Òéepïo˜Ü{ÅÉ_£ðž¿’Þûä‘•š€O±¾<Þß¡µ­Õ¨#ÃÍ ë£Þ°Ž,þ=¿¹ 'zœ[(Ãå1³Ö?9\ÒhŒ÷ýZ,>‹ãкٚCë÷wäheT`¯1tgŸŽs¼ë [õÎm±Š)‚ÉK~6DÓüZör‰Ãmk«\;|õµ¼¤¨Ù†VŽK2ÏùÜ Kòu½Ô|ªýáIcÍô^^I|q~„Ý æ¨'!î$x¼¤Os‡µÙ6ÎB¡qÊJÒØ£2÷Ìé6ÿhèˆã<¬ øU’$0[tÍ”A"“™+6ëwŸ¿T¢‘¤èàB5dB}ôþ}BJ;ªƒàŸîb09,¦ðü!ƒ!Ìý\ýn6‹ú™ãùÜ}ÏæiÃ¥wŒÛ;eË? äòîAþyçDfJÊ)×íØ®žŠpyn:i¹ÃÀ3C©ç€ÞM´³"Ùh Ãæ>>¶×îÞþDµÎz¶›)9ð¯ÍzMt)VÖÍÙŠ-;Õ¦„ýó6ƒÒ(è9ž”Ç+_ާ:m=i%ìf]ÝgròÜ×ÍÑ·?p¸‹†p ùOM׬žõñ¹ÏGjFÝ¬Í ZKä'²‚X?×G¤óv=] ÑDê}ɬyþžO°Œv¸GÉé.´›«!—}É™)!Áü=Ï Þb ¼Z׫ IË VqºÔ;2Õ­ó.AQT¯ÓûL…½}K±ÖˆÎ=Ý:~Æñè’sãù/·Ú ¥Úmõ°^`Ö›béöêÓÆðIÖ Ù¢ù2ÙlÆÏåù×{ã…™†+N[ꉰXµz¸/ln¸èþþCncZÌÊjp '3é0qߦyä3—¡h÷)T˜É:#Æuœ±çÆ»÷7ÿyÝf ±¨ÖŸ}ÖE´ìg¡Y’Å…{vÒf‹Ö©²¶yCe*ôù0jŽpÛ3•{Mµkö©+7Ç]›}ǧÓk³æ!lŠ ¸çŽïzùßa׉jÙ›˜e2éÂ_«[ìðÝ|! ýœÎ¯fõk;0rÖY7ì_Ûh! ¥$L'ÑaAö}þó‹>%3°GÎMdÌFµ5%8TÆgÿ(jª~áåhÔm»³(/‘U6¿œj]…Ù‡÷x_o5âÎÈIZ¶ÏƵ*Ëû@Èpy9»—b=²„)‰i•d9=òÇÌäŸW‘lÓ–-s “u¦€_@« Ë£ûÉêgeQT%Ùª½zªæü&­¤(AŽð|~цªE·¶¿~;v+(xª˜ÿi) jÍ››)ô÷?ð3SøÂàbªÔ;’´Òÿ½v‡|öÿ9 ³©¡:É“ÔÏã©ñï]œ)ª§u»ß–Z¥ïù³ »w1ð„)dÐÀÚ¹Ö>@zÑýÇ~Ϥñ¤e²ÓŠÇM'•‘eR¬\gš;Ù5l²òß½žVd‡tÂÕÞ½ç¬Q,ÑÐl•¿ö JJYQx+B®UÚ®ë°Zê§?|8xöÙÛ¦ÂäçæÙxùœÆ¢µ”äs—lÀ~}{©€é¸aÔg/Ï@ÞœÎHÜj ! ¥”›Èãò)¶„‚9Å|O†¥ÌÓbÌû5 ƒt¥éYâÂ$ tZ±âøÕaQñ¢·¦dMY¾•/€|»Ã—çÕéäô5<Óá¹k±>“T@ügççþ·gWWo¼t7ø[\ ÷Išú0(‰¢dÍt”Š-óÕÕ«¶¸Þyú66)ƒ' |õ‡U›ñõØZûîޯÿ§fŠÚJRTÁ >ŽÊH§d8µuÅsRÜçòoâLÆÌ_…Ÿ"¾@xÏWУh’UtòÜ0(¡©«J,î¿—ä_å꿇ªfCw¡Ÿ#¢3x:2E\ãyNf+{ßwÏuRGŠz¹mWxí3VÍJxotæ×¯Ú´×÷ɛؤôì ÁúµN¥M:oPgß'_O/›¦ƒ8iߎz?½õ h9™Ñá¡äÿêõÕ+”£Ó„ øþæ} ié–¬>P• ¤¡táa_ÒÓ)–’ŒA=rÄ‹É>ô¬¼íù›É:Tz®~0“#­\ø!-óõæ&SCYºŸÔ½I-EÉ£­•õ "oÔµåô†ÒijXѳþâ³¼q¯ Ò=žò;Ó¢Žº¤DÊr#¥M¡…ž/ì³¥Šz ßgÓ°^K¼šMr¹qtt}M…Œ/†5o+¹ÈzDßëߨӕ$ãG/Lìiª¦ `S¯ý‘Ï…Œ-HËÈÞaÍ’O“FÀÍ~ŠÔ\^½§ÁÍÈõ)bIȪPiÂûâ¬â·à2Ó„2ó¾Y„%)<ðñ2ÿÔ" ¡îó'hu{<ÿÀ‡Çþ½æ¨L¯Í}ÕŠ=ÿ,··[uwÎt‚³ÏëZ ™‘Á#Í,¼r¢ºx~Çu~G®Ü¾•élF¥~#ŸT*WC"!Bx…‚ªr1Þ ÖnìB=·y«—‡O¶ß|#}^Àˆ’mþ—[ÎQì~'ü•}½  !?Øa¢ò‘#þÞÞ_RÍ}œ•úŒ®ýóQf[I“¬–ĤH²–¥sÿ)‰ÞkPôé¨êÒP)‘=ŽüC²¬×Úíµ„¤:hPo¢¼®¾_kRüo¹Hx–I±•Ôä }@Š)ŸW@Ù¿f UB ÁÌ 6'ïM21/®,˜4âv«ý¼3G-³ïj9ØVù ãó5=úXÖ/ð}O¢Ì_I$ß@“A½Œ §(½¬!Qo¢Eãd¤g¤@ù|ó}øzå&Ù¥&fõÌŸg¿ŽÛƒü–÷¹¨#VK,êáä®áoX9Ò3‹ó»ü/ß.û+ŽXé\®hÚ<µÈŸ"Œß~Îý›j}MŠz÷òöÇô–šY}¥Ä÷a¾£°~¬ÙÜ¥µœ§½w^shtIME×.™ì¬óÀ fÖÿ8êºÊqóv,5$ë¼iZTHljî…c°Y,²ù?ÂH¼f´«Mò"é]h,e,<ãi¾ÈA{Ï´»N6ºg7¨3>‡„%Ê© qÑ~ü|a†lVˆ!ȹیѠ•1i|x°½Wÿ7†Ò1ZÝœ6M3ùã‚G…Jþ?%"<…2–ÉY9Ør»µ;ùÎß¾£þC}‰ŒèQ‰¼„d²T1Ñß)Š$-%%zbšÁ”þµoI1øÂ²Ø²97-SÒì¬Û‡¢’¶\1`Ù8¯=^êÐVêÛ³GŸ4Ì4ÙTLì›Ð´¶õ È–Á×uWû/»dÛÁȵM—–†r_^=~øÏ+rPNŽþ˜LQ ýžÖ¦§v>ßÈp¯…iò›€Iµä”HZD~L êdmO=ãzRòŒ¯ïõìþ­±rË|ó¡ÉCÔν¼²Æ¼­¯±RfpP¸±>YÙ¼¯_£ 꼴óQÃÉWÖѹ¸§O{c*æÝÛO¢H3*åÓëhJSM´Â…åórš{RÒYƒTö¹ƒŸ+ÚlÈ•ƒ<^¼=ÌÔ°U—NMë¨$ÿôâñcóõÿî¡Êk¹÷ô¶V}çï³é|qCC‹Mµä9iñOü?-}ò|Œ&«ËhcjéëÅ}{>h§µçðjC³‘‡×_ì²dC‡Î?6;NÖcFºo]îäû¥þx·Õ6ÍE3—ádm*K¶à…d÷›g¥²Ã7V§ùä6F…|v~‘˾žÊ^X¦^¯ ÍŽm ²i\gk³”·Þ%(“ ñ5*,Žª›ëTµ¤JÓþ–î}YÏÚ±Ño cFãá‹O¿2rïÜžVÑ«í†È'½uYaïþ–ÛsÅ¡‰íD­[ªÁìÅu­‚O.7¸{ÔP[âÛǰ8õtò‡ØŸù”žpQeÛÕ¡\ÞŒ7kw¡gOI-.œ™[š›ò¡b!¤!/÷ñ€Ú}1ä”5ëk ž_u ôúyÓ5S¦v“–‹]lmzå}è…­:ëÔë.#¶:îýûù‡§>ï™z¦]¦îóŸÔM˜Ð+ºõp}£¦f\O…;ÃT3SÆhÕ ÿ©u(é»^Uš¼î\LØ~kKïóŽuX5›ýôÄÔ¶×ûÿǵ7ÃÀ6À7uê¢ÃŸ’ÓåëôܰõҘ摖æC_&07ó>°wÁι }IS46QŒ^©WkËØƒ7ô4ä˜Ï Ø2i“×·°7Z]†ü‡s”×-'oðHJË›3øÛzÖ[Àë´ûOëÚ¿Ç”™ê|2•=gÿõÿ,i ËEîsǽY7a×sÿZ'_½ÐüXû‰µk Rÿi¡ßhÒ¹ÇÓÒ¶vŒ‘31i?®©¾Iÿew4·é9îvxf}þÉ®æ!“½8Ô{îr¤í²£1¡!M{Ù=Û:+Ü¥ç¤=a«»7x´øÒž™MóųÏÒ ÁMv9: þðèz„Vïñ+·-‰¹xOÒ[§z½¹¡Þ 'l¼›i»ïvè—ðèÎ÷^YØsÇ”î§b­šµÛîq~TK v½!>ûÆ;¸G}}[{Ђ1&”ŽYè“´Q³_G…E*wÜèåi%µ«ö@÷·ç¨'|ñ™¿½ Ñ`Òëͬð|ðî¾ï7ƒ–ƒþ{áå\3çgÆ[/ذùѾ¥7Þ$(‘Ë߯§{ ß¶K[4¶ãÁÒ4Ö|è¤g`:ÉýÚêvÙÅq4\ëqvãÚ}ç_¼}|ý-Û i×™]&tξF]§û¼¨¯]68yú>}ýÄÿ?¶’IÛ^[nžé+l Qìý]> ßç÷ñù«ÈaÓ&hË’ã£óâ¿ãzœYºv×ü±ƒ3,m“Ž®·ÎÛvÍ>þø°Õp‡ccãÌ·§Œ <{.¾ê6#_ך´Æ|ª³A7}é¼Ê-.pژɃ¢ŒÉ¦Ý««w°ßêƒû&uíxù{šíž›ï#>Fu·ûò’~®Ó»¿kmÞzã‰óãÛþì4³duë³òhêâ…Mò-?bOP»ûí7˜2â)eؼϩÇk­Z(ÿE}äãÀä‰3]ÞÆ¦Ij·]ºÊ}r/æ„F]þá^hkteÕ¥6Ù=ÖÞ³ÿ<îâ«è¡:cçu+ü-|@#iÈKÀnéõù{é¦m4dÇùJ¤ÌoÅ8<‰±åZ˵¿~ïïÞÿ· ºÌ½þxn®zž¯?æüÒýဋm2ÑéÑD§bV»×\ÏOs .ÈöRÒà¯ÿÊ5 þf›s~kõþËîß'°}f›§ w¿ÿòÍŠÝoÁ¡~¹Ö\¹>ï ªR.Œ&fŸ0;÷ §}fýúEFÞÞ«¹žˆ£ýãð{F–þ–¿ “i>ùâƒÉ¹¬ Y•ýc«#«Jf+z­Ì5¤ãé·Ós~™m±½€‚~j:|ÉÙáK ý³ªÙ¼­Çæü7åÙoÎÎ7TÑ|Äîs# œ ¥G˜Mu‰ý˜Àìl5U¹è+áJæ®W]ó—Öùk—WîÏŒ½k€}Ó bb¢T¬7•/ào„nç©Ç:O-bþÊÍl.<ømaÜ^~Êý+G¾îú¿ý×QÐBòÿY•y«W™TÙŠ—šº:2î®ß5äåÍr‹ #îÔ¥ÀN[Ëu.På ¤ò ÷sy­hÓ«‰•2{ŽgŸÝ ýª“²ør~áÅz[fÎzsÙñfF§• Kø6Twi€¼¢žŸ~:ð¯a ß\wúCk­?OS É/æÛóSfɾ¹|ðhв{¯Õ¥jêù (B /yµzÑ÷ŽÎ÷“i?yƒ¯óœòzaˆ‚‘©dˆÓ¢¹ªÆ}v>Õ0ïSgi€< F¸'Œp/÷ÙhYÅY•÷\ JCHäQAçœqjþ! @SišBHÐB€¦Ò4… )„4M!¤h ! @SišBHÐB€¦Ò4… )„4M!¤h ! @SišBHÐB€¦Ò4… )„4M!¤h ! @SišBHÐB€¦Ò4… )„4M!¤h ! @SišBHÐB€¦Ò4… )„4M!¤h ! @SišBHÐB€¦Ò4… )„4M!¤h ! @SišBHÐB€¦Ò4… )„4M!¤h ! @SišBHÐB€¦Ò4… )„4M!¤hJl!-'''®¢ªIIÉ2– ¶îÓ§OÙkeÄçóÓÒÒddd*»"5ƒÁˆ‹‹+c!¥i@û×ïß¿—±* .±±±•]È+OnGéC:**ŠÊj)”b®5ÉJòobbbI',}Hkii•zZ€B”Є‚‚BI§-}HëêꚘ˜PYÆY,V©Ë1âñxIIIŠŠŠ•]È–žžNR’d¥ººzI§-}HwïÞýÞ½{䈀„ @@öJ6OWПÏ'i&“©¤¤TÒiK¿'“£€ªªj©'€¢¡¹ @SišBHÐB€¦Ò4… )„4M!¤h ! @SišBHÐÔÿxÃÐW<ÙIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/create-an-xpo-stream.png0000664000175000017500000007330014770023131025056 0ustar vladimirvladimir‰PNG  IHDRK%‘My úPLTE # ""#  ##%9() %- +,) #=- 2*"4 4·"©²!­ÍÛÒ× × Ô ßÈÿöÿëí ÿúäòÿ (D)C %<)2'/%1%2&;+R3b(L3b2_ ,K%FEp9l;mCo0„25v6q 6i5f ?qD’+‘.‰-ŽE—3´5¯7¬(Ú%Ý 2Ð/Ô#Qƒ!LŒ IG“DEE'()=>@BBB***@BD>@B&)-@ACBCD9:<+/315;*+,-0\*:J)?Ã+B¿%9É%%ÿ55ÿ--ÿ>>ÿ4Kb)Ns@[v%Ls%S;Lt1Lg5Rp-Y„:`’<\¤@b>_ @b†/I¸-F¼7V«1Lµ:Y§5R®Bf™^@aPEh]`cPRTOQSWWWeef`bfVVÿFFÿ^^ÿFl’Ns–………lorz}svzprvv{€u{‚iiizzzux|mmn‚‡vvÿ……ÿuuÿffÿnnÿv’­…ž¶†9•+B“““ˆ‹–™Ÿ™£”¤¤¤‹‹‹™™™”””¡§¤»””ÿ¤¤ÿœœÿ§¿–¬Ä©#6³%³³³ÃÃúº»²¶½¦ª¯µµµ¨¨¨¬°µººº¾Âɳ³ÿÃÃÿ¬¬ÿ»È×Ê"Ä'ÎÓÚÈÎÕÔÙáÒÒÒáááÛÛÛÏÏÏÅÅÅÕßëÇÔâÒÒÿááÿÙÙÿÒÛäßåìÛäïáçíÿÿÿðÿ55ÿ--ÿ%%ÿFFÿeeÿ}}ÿnnÿ……ÿffÿvvÿ¤¤ÿ””ÿœœÿÃÃÿ³³ÿááÿÒÒÿÙÙéïøãêóôôôèèèðððÿèèèèÿøøÿøøøððÿÿøøðóöÿÿÿ7¥œÛsIDATxœì½5ËyßuœD¦*º¶’²“(ü¨Ê7¶ /±UÈU„’lÍéÖ¯@@W"  $@•SwÓ^]oÉÊB²–ýXÙu’(OöQÖRU¼kw|\d*UC9Q3²®/ÕU<ÏÓ=?Îî9gÏ»;gœy¾zÕ÷ÌLwOïžÏv÷™ÓÏ·'?óÉá”ûmêÇß7œ~|«-}:úkþNpò›øSCéõ÷mõç~ßgO‡Òg·ÛÒ§£ü‹ƒýNO'“ßüsó¡tºe–NŸJKŸŽ~ð/ö;3Kã³ôØ[útÄ,=ö–>1K½¥OG[féôuãûvytyõ³ôä´e–Ž& è(œOÏV”<›\»Â,=9m%‚äÒ³óÃÉñ|~qv¯/Ï.ÏÏéåü Rfi´u–ŽÏÎÎæ/&''“É$9;N¦Éñúª“)¼JN/°ß:g–vA÷2ÆÍçûI²OTr8?JæÉÞ|>M./’#˜)AÊ,í‚îgŒ›COt‰ç“došœã j:…³GóýétÂ,í„£Éä°é—.ÎæKGÉÅå#é—° —«>´™®g`–¢nbé<Ìoø -ei :9M^œN¦çûÉa2í±t29ÜýÒÙ^£ö—þ̇“äâž[úttKÓ Ntà­>k>دÑêçKÇPøèd~tt9?ÿÎñ‰ÓIøïÉÑéÉ‹Çð|él3[–Ž®ÓM¿éõsÌRÔz–Ž’Óùþ?fMnÅÒíõ,Á‡di/ÁOšô™3I^ÌϦx|”ÀLJ{ì¼î·¥OGëY:™Ã7–Ž“C`i?¹8I.ðYX²¹¿Ÿ9ÏN&§G““‹dz>žßwKŸŽnãà·‘°t†|â„ÇGø#ŸÍã#yK0–`¢qžœM“ù9|Ø>=…OÝóãÉ9ã3ð˜…0KÃëX:Ň€ ô@0•MâR%õ3fiwt,Í÷'Ô/]4ÉüØ9Ü;†OÚGÉÅ£céôèèúÃÈÄ,E]e‰¦ÄÍï5þ»ªUçV±Sîù)̦¸œ ÈšNaÖ„écc Ú–L^¬%çÞ[út´ÈÒYoqóÆ%ñßU­úšwé³JÑ.Îðÿôù —œÓ+HÏÏq½.ýçžß¡å,%‡óËé^o!1µù2ŸŸ/k'³Ôj‘¥éôrþ"9‹°–.»÷>p,ŸE8ndé¶z–¦Éáéå|Þ-$†ÿœNÎÃñ4I®±»ý–>-°t{œ°;²3ç þ"§““ù!ŒOGÀÒñä­+kyž>Kç{É)jïMñ_8ž&—×VÆÜCKŸŽXj¾( ‹°›~é &7ó“sè´µ£Cø%Óóǧ‹¿Û` î;Mz ‰“³ä8/›ÕÝCKŸŽX:'–Nçav7_Âÿ@/4Ë“ ä;ŸÂoxÇú¥3èzç¸T ]Hýà_Øà·õ?ÿ×þ» ²M&íkùüùÚ$ÛÏoõçÎ7iÂzþ|“lÛÝóéè6ùeý'ÏŸÿ÷dû‰ÉF·|þüã[þ¡†ÒÇŸ?è&윞?ÿß6ÉÇ,±n³ÄJÌk(1K¬¡Ä,±†³ÄJÌk(1K¬¡Ä,±†³ÄJÌk(1K¬¡Ä,±†³ÄJÌk(1K¬¡Ä,±†³ÄJÌk(1K¬¡Ä,±†³ÄJÌk(1K¬¡Ä,±†³ÄJÌk(=KUåíê«Î®¹8œ˜¥áõ,ã”/V£…QÝêŽb–†×=±”›îµÁׯ-Ï(ºóz›³4¼¶Â’¤B¤Þ+%JïK¡rL0„ל²R)” ¯Ô^(%­–:-‡¯}Ne·$fixm…%é¼Ñ€”ÕÆWª¦ö¾–•/U 8iãDì—JÏ4žËJ ôKp:§RÂå:•Ûꛘ¥áõò,}uå×Û_ ,Aÿ]ŠÉhIJԿ8 =’P`¼#–*c2îJME  ,!\x–®þy1KÃëåYúäûW[þð×õYÒ%²d±q (qn Ku®ü"KÄ‘Äix=üÅ, ¯[°ô©Õ©?×±¤`T«a¤s’Jå8pÁHiã2:+ªJXÝtŸ%•y-ýö&KžYÚ†¶Ât4F+UÀÜZ©æƒ™SBcª²Ìj ˆP)•*a’­4²À…‹^«rËO˜¥áµ–|üà[©ÊÌÒ“Ó]Y:ÝÛKø`ðp¿öúGÌÒðº#KÓd9KO¿úå¿ýw;œ˜¥áu7–N’ä ±jqb–†×ÝXzñâèi±ÔâÄ, ¯»Î—®°ôþüW«þΗ[NÌÒð˜¥ßøå'¢¿ñ˜¥¡50K¿éIôK¿ü+_á~ix ÌÒ˜/HžçKÛÐ]Yº<{R,<³´ mé¹÷õ¯÷]e×-ͽµªMWü*Ô4Y^ÛaI_û‚_ˆ\õM¥wh9ª.7|À^Xÿ©G*ã7Æ/ÇRiòܯ¸ÑÊ+Uæ3ý._<%m…¥\_+/1¹ÆÒ]¿ts›¾MÝ’`þóR,Yi²,üKª6ðcdK–;—7íŽCµu·5º&•%­ÀUå¨_Rqám_¹i©þ^;üN–^iÈ uXpFÑ2_ƒ§ÃEHÛìÊÃ!¼•p+í œÐ-Ð ¹5. Å|:¦ÀŒ»ÞBâð=¯];‡3¶†Ä9lˆ…¡T.dñÖaçë4ÀÅZs…¹áN[d ×n¹š b5iêÙmß®G­­­_¢5º6+p=­½Õð§IënEƒƒòNV^ä~ß0.b–Ìá+¨VõšBgXœ.è"¦˜=\„~É\—©3\'Eý­ Æ¿á^¦Àö@¿îÒô™‘%i­¨„2 WÑtáŒÊ £U•J\µ'1‹jVUimðOC§®T 0Ʉν¨ñ'©‘ýÀ¹4JB«>U9Ö§qÕûV#HÛ\WIë&Û¸ô«¸Pÿ*K©2ãp¡%®ÞXŸAÿ‚8à j Kß8ƒ”„ù– ÙÃEbIã `'h$ ©¤¼W‰o&\‰w ›1®€yr-¬ÂtÓæ ®ÁR9®ÝC–௠Éb%¶PYj«’ÖgÐg0SÂÈ-üi (‚\[‹(RK¹"ç ëåYZ·%ôÛ¿sq.¼í ÜÀRéœ Ÿ¼r­–pñ®&† ,°dqñIš-°Ô,ö…aYJ3]ú,Yb‰î¥µ«Â{Mw¹Â’ÇqQQ)á» ]8#C;ˆŠÀ*dqz-ˆgª?×8ta§T{¥e`)4¹†˜XXÊ`Þ­®}|û+xßJu[ª¿\J»F×@WàÒ¯½Š–&ÁßkpTønÀ«£^ ]`‰ÖófP*ê³²ÓE§!w! ÿ([–º`I/à^ÐÅ—‡b0i¡–F–rê—l\oN ݹ9ƒwÔeË’§ÉŠÐ#¢ËÈpd ®µ²nú%¬€‡~ î5`ˆ×êà•Ïž®ÔëŸló½K0okt3ê2 \«ñcM*pØÀà¥¬× Wäz ßÈ’eñžS0iÁe¾} Ò1 Ùéb¥4äΔ2X½ÏBÌ® Æ è^p{£-. ¦bØ6T7_’0_r@,t`ió¹2œ‘P£p%ýK©v™h²È)¦ŠXjfrøt`¥Ë…&– á2 ,¹TÖNX8,d½½ˆÕÁ+§«§ÒŸº%KµºÛºÊmjá™@\ÀE2d¦ýxÎàPSã“¢ð)K»O`†>»A÷ƒWjÓö4¦e2›…çK…)K¸lrx ÜéƒkX[aɧö±²Ô¼çýçK×{-9úYÛaéñ«ÏRïó9ŒÌ ½î{Ì²ÍÆ=}íG~±w´ÀÒ‹i²7B–X·ÕìÙ³t8õYºHö`b–Xk,=ëpê³tyt9ŸN™%Ö¦ ,58]™/%Gã`é=ßüÍŸaÝU?ú¬ÓG~ä¿X`é,éwK»ÌÒ{ÿég¬aõ­ÿfŸ¥+(í2K<Æ ¡YÒýìâ—$‡G'ÌkSÍ:üâ|é8ñç8ÖÆšu y~Vɺ‹fHžYbÝEo.1K¬¡Ä,±†³ÄJÌk(|Ã?ôáUzÛO¶ù˜%ÖM:ø®W¿¯Ô¯·ù˜%ÖMâýãXC‰YÚDBRì@_²—öäF-/ 3‘q |kx^¼Dž„˜¥ ä„1àýs”Ê%YmcsP/¬ô•Ñ’¥]KÞ½ì SãfÉá>QNgŠ¢”jBDÂ*Œ´Ê­:xShÕºi( žÒ²9¤+µR)R ëÆSæ„e …¥áWˆñXZj_à¶ ;¾ 7K^£½ÐhSïjÙ¿Ñ™Y ܬ›ø8E~Av&C…+P…Æ 8è— àE‘RÉEå¤ÃPfô"Àí`(Ê! µàùP]s¦a ³è›XòV ´µÈ\¨´cÉ…¸rb)4#ïBÙïY¿e²F?ÕæÛQ–2­3æG]’Úéu<ƒc +-KUØ2d  MàZ¤Ôh¬ãÓŽ%tŸ€—y¨a%ªÒZ‡~)Å êû,¡ÏNž#c±_B º¦ÁdÅ‘5KfÂ}!Sh†y8c°q·keUI+„?l˜ø¨æ3|8#=Ni59•K¥0Ý”*…Q &9d©¤(kiR¥:–`ÚdDkè³jT”T¾iJ~a-KT¹µX’í£¼àf ÚŒó%|iÂ}½Ò5ãþ~…×4n–*˜Õ•È)¤Û¶Ý>œÁw¶¦øn‹^“{£.:<\°dBé ¬ÊXÊÑÙ¼´M TLc-t»P>Þ£÷yÞåT²¦×mˆy¸™ UYzYÅûâ]©§q³Ôs¢ˆfë¼)ýÖ“v'¸ÊÒÙYʺþ š˜dkžø-úœ «*¸¦<ÿÙG?óKÝÑ–'gãc‰u[aLS‡Ó•ð ³ÄÚ\1Ö2â´ÈÒtŸYbm®npÄi¥ý½³±°ôÞæ>ÜF¥þ‡¿µcéEòâxr<–Þó{Þñë®úX¥wþПûG;–ŽðQ7{S°6Ö¬éKo.Œq—gg£é—˜¥!4ë@òמ œOGâåÅ, ¡Y’ççÞ¬»èkHžYb 'f‰5”˜%ÖPb–XCéà•Ïž®ÔëÌksü¶W>ð¾•ú™6³ÄºIc` 'f‰5”˜¥MTš<÷/»I\™ BB–Å€‡ðîëW²ªZ¾ž³iÝŠË÷$fiYi²l™Ÿ€]ÃW)ã&ßë*6K½)‚íÄõ…ƒ+Ëâ(›À'×ød±f|Ü,UΉ #m@@8cëê1àUEÛïºÅ8”2ñ)®¦3K‚Q#ø‚6ŸÇ@à‡Ò¥±–Ê`ù&¥ª]ÝÝÎu,…h i°Uˆk(UÕâ\MŸØ ð´˜;-Ãm((ü¾4r–¤µ¢ #ã0`­y÷Â)ƨ)ô¦hâãÚ÷F˜T ·„¢ÀÀ4‡Am²pB§B™—±‹ °B‘Æ4Ô‚·+°¼‚©¨° Œ0A÷¢à¨mMš*LµtN  EŠÃÁ}©kh‡¬©uÐ|MÒÐô þ…ÛÔúþ¼*ÆÍ’/„Èc$¬¬z[Ä7gÔbÜ®m³8xßÅp§ã¹ sâvmÏO ÔZáCjòuIq»Y·«âÈBrÓÈR¦ñ5æ¢K…C.Õœ{eCüpU\ŸŠÆuM›ŸjÛ9K/é'€ŠYR ½Xô˜àyôî’] xè—êXÃB 8Õ‚çWù ´V$ƒùŽx&°wÂî;%8w,QÃs%D`)4òÞWPøÁ·¯{VÙµbGYÊ©_²Ñ= ó9iÎ\ñ9ñ!˜×Sį•®ñ9Áó–ìMZ–j´ ð¢l}N:–¨L³+>'‹,• KÐ#Õäܤ#KäUÃlF–hY"•¦_¢F }¡¼ãþçK²B·%íµFÏ¥ pF¢—š5,¥Ú5¶Jµ€‘¯vdÌ„ÆG:´r¡;?9à5ôY µ ÿRJå­,r™/²D^J¦¹Ì ri ¦KÍÏ€ó+rc*[w(nLèB «^cïIãþn7‡á¥ÈE0j˺`ÜpßËíß`*žàdiß.G»Hz dðs”±YÊ‘…[ª³¦ .ck¡ÛQy‹UaÁ<ìÆ©W3*X“æ˜f>_ÊÛ°Þ:TSæylÉKò«C¯¸¼ßØûѸY ºî°ÎO€µJÌzÒv/Jë5Ö1ËVZe©nÕÜ==¤ Î úZßN`‘¥Óét|,±n«YßN`‘¥£„Yb½„BLSƒÓKÓéÙÅÇ8ÖmÕÆZNWöO’‘ÄZ¾çw}ógXwÕöí>ógú,œÏ÷Fþý¿o»F #Ô;ÿè7-~Ž; K<Æ ¡Î3ÃwûcÜE²9Ýc–X›ªa)„ï^ù—°ŸksKŸøü[áèª7Å€Ç,±ÖjÖäù¹7ë.zó­þ³ÄJÌk(1K¬¡Ä,±†ÒÁ7|ðC^¥·ýd›Ybݤƒïzõ ùJýz›Ybݤ±Ç¡°†³ÄJÌÒFÒ·,’]ì™–òÆ…ãùÛ€››"—œWÎWOû¸ÙªïÂ9—fº£˜¥M䄯*·$BÈ­w\Íå ›°WåK*6åZ厶 Ì/`.ã}Z¸›?Œ|¸ø¹‘³„àÚéŒÖëk¥š_¬N1:Ro”Ê­:£Í™u·];\(¼’¥ÎDm‚c0µF«²Tʇ¾K:{]*xCj±´{·,³ÌJeT°ÙX×RpÕŠž'ÚZô7Ðx²À`Ú¯YiÙk‹Ã­äSyµ„lu* F@@Q…÷”h¨’ÑKj&f¢ªòá‚FÎ’×Æh/4Fâ“aD¼,KÁ–¼Áh@Á–3›aF’…®Š6•Úážßiš+Šø—®’™×Yc(j€[d‡4Ô"- ÜÍEU‰‚ 61è…7%ö!š|°Ö¢V¥ yá*¶GûRúöµ¨¡ýa—h…R8Å=ÍáÇ’á>p“ÐLŒÁ/!ìW?€ÆÎ’—2„|‹^Ð+:‹¯}¼”f K0k2ÑŒ¤–¸y;²ä€cRE\F‘µ!p·a)Ô°޵„aŠX˜ȜvO³‘Yoº—‘%G4£ÐÕÉ…ÅòüEH$«Ž,2­é§@G ›§¥FšéU¬Ê£%Ð0ZÏßµæùRþÕ&ßN³DV¶oN¯ƒ¡@ÝšSD–š°Ù:SÐaµ,ÁŸ½LKѱ`IóPÃU–L%]^5§ðy •á;¼Œ%“¹f—úØÚ”nïã-ðt)p`Å›ÃPZv,šY2ùï ó;=xþõ|åsï¿­ýeWY c\ Ý :Ô7¡ ó)ÉEÀ¬a Þƒª™Ù›Ï¡ßH®ÑC¢×/a­8º…ú,Q-äQ’Byx?qJ”õYÒ5úæHt4¡ú\ †%Ì Y†Î:‹Œ5×x‹‚šº«Ü ç 5&²T j&d U•Ã…r<ÿºŸÛä ¹e g?¢0'-àR¢ùÅJ8Sç%Rœ¢Ó3'D›E š`kEÆqBÀ<̘éÉ$–¬B±E…4Ô=‡ÀòS ¡èc@ôd µ2k‡ é;Þ±ÑÍO)”¦[ˆöó6Ú™ÊðLËÆfb&ªj@œ¥ ëOäªr«>À/?¿jðx2›Æ¿”˜%¿ÀR†Ó “É5Ec–­4«¢ºÍ¦¬å!ûVš²‰ÞìÛ \eéüä|„,õœ(¬CÙu«c–í´‹êÞØe¥z¹ìƒkÖ·¸ÂÒñdºb“Ôf‰uKQJ‹Ó"KÉá|ÿˆYbm¨&>îµÏÍü–Î&'G'#ã¾ûÝ_dÝU_ê"À§+,%‡Ð5¥ï»—ûqéÛ¾w¥£ùárC]céãßÂ>'wWßçäŸÿ«ó¥±°Äó¥4ë@zëêÜûp2ÉÇ, ¡Y’¿ö|éd,sofiÍžõ ø¹7ëözëó}Cf‰5”˜%ÖPb–XC‰Yb %`é‡?µRïg–Xëàùw~ú“«õóM>f‰u“F‡ÂLÌk(1KI‹,³Ë¢ÈÖ­¦-qEÿ ª0žöšÂ9H7‰6‚ljiC Áaó²6B`»ëÌ™¥Mä„Ëó—õH•sfI™+åó%öa 7\élj˜kqq?Ö¥ gD¼ÚÚíìÁ;r–,îSjKG¡ePƒ¨ Ü’ÔTU*³ÀR/ nF ¼äÇí3SЙÂWyi¬…l!ÖN×Tƒ/ióåúfOÖË1pÖRÁÆ|ĤX9\’¦Ê³ [©´êß?¼® ä–RÜí»6Âbý¸G*\ ,ÁO"=Õ@™rÚ x+{ƒœ%/ ŒOT¸m3î¦Ü cRãÖÉRg…ÀKÜcÙцËiÌR WH_¦•€Œ¸Óri1DÎ:™2u¢l6N5À-´Ši¨ʈÊ5¸-s(#Œ,2Œh«ÐI -hƒfŸ–ýû·µ7n†Šr ïJ¬ÂœUˆî-îåj€LžÁÈP1| ÍØY²RÖ16_únSyög_ØTcÀsxo},ˆA—1ÞòRà6ó1Z»îû 4›Ê‡`ÿ:Ö‚ùªêʦò:cxFa¢ÈÒÄb0íßÝ¿«%C–’/a’TB‰: Æx{kpD= ù29xøÊØYòBD?×÷ [d þõý(ö…’ªõ¦€ÓªVB÷bÀsb©¨C ~XKÏOÍFýp°#ÆK9…Þ"K½û·µ¸ÈRð@gO!¹K*Ö€­Ee0êáçLÏ¿q²F?ÑäÛU–2­3´•Arªn–*ñ ¤~©À¢e© ;̓lA~ÄÚZÔ™I{.ÀýáË<Ô°ÀÖ‚e ú¥”¬ú,QU-KXE`©»WKÓ/yrKñF>òL ,)*j€Lx˜U[àü迳²ª¤ÂÀ¤ÇÓšiIe0‰ÆnF ß°T ÓN©D £Lrh ÓF”xQdK0m2"5ôY µ`œ$á¬(MÉã©cI¥- ¼ CŸ6JZhQÿþm-ŠX’ v“yiR¥ˆ% Ùd¬2ÁazÃgË[kä,ÁÇוÈ) ×vÁ¸ðÖÂ|gk ‹µhÑg±7êâuÃK'\Qcù:är>8úå¥mj úckqx k1g¼Gûü§¢3tҬTmµpÿ¦Ìµí¢{Zþ/kÛÔ™ê|ka¾#g)¨ç'¼åš¢¾ÕûßEÌ’÷ñ‰^xŒ'ÖÙ=<´7Å6ïÿ’š…€Ý¨ÅXKÜœcšX› ãP:œXº<::LØO€µ©b|\Äéê·?áǺ¥º}À§+,]$§ã`é½ïxǬ»êc};×þÔ÷,°´¾Ë,½ç÷2Kw×KŸø¯ÿðK à»ÌqC¨ã0|wqŒ»2Ä1K¬µšõœ)®Î½Ï§çÌkcÍžõ øY%ëöš}¢o(À,±†³ÄJÌk(1K¬¡Ä,±†ÒÁóß>ê5º¬á4úØÖ`b–XC‰YÚDµ«hQõ2­¾ôÓnIÌÒª¥Â¥åéJ±š˜ë¥ â"åò‹äƒ‘²%HE–¬ÙŽ!Ä#Ò¸Y²²òÂ*‘ø¸mãØÎ!¼RÂXA»âʸãmm—ë ä+V ë­†—NkoR!Ò˜ ‡û|+WnÇâiÜ,yCa»dJB±Üñj8#í°·Çè×¼Ý)¼ KÚÌ[›SH4!Äwܶ!.W´S8B<ÀOz9Kd'€†ÂöíÂ^D;Œ‡^h†1² kˆ<²dg•FÁ!ôD& )nò^cTmn¶özÒ:øW^ùÀûVªµ>ÝU–j²9Ar¨Ãi|Ú3x"ÍZ–º8H´6q®ÏúÀ©JkW–tR` €ª«nÃú]ÕÁ+Ÿ=]©×Û¯Pv•¥`¿”zLKLƒÂ‰&G€[ݰ¤ÊÞ0è|FaŒ ¡¶…/àPa±¢OŽT¾ÚñÉ’G–N7ù:nGY*`ÐI ‘*e}­0 gÐ8^hüL!•h³8œcCªèJÞt:¹RFÃL ‹…G¾ï’Hãf)èú »> ~“¦‰$¯v{Î݈YÂÏëíKèI@Z­a)fY[û"K¥ÜÕné­ŸþZïh¥Ód„,±n«Ù³got8-°”ì¿HN˜%Ö¦ 1M N‹,ϧ˷Ûe–XKÔÆZN ,$Ó…ð]féwóžòwWOù7~ú¿ì³4Ý;Za™³s,}ß3ÖÐúc¿µcélr6ŸNÇÁqC¨óÀðÝþw>9¹Hö˜%Ö¦šu ù+ó¥Ã$Y0`–Xk5ë@òמU®²¦`–XK4{Ö7àçÞ¬ÛëÍ·úGÌk(1K¬¡Ä,±†³ÄJßðÁ}x•Þö“m>f‰u“¾ëÕ/ä+õëm>f‰u“ƇÂNÌk(1K›HÈk~²—öä.ù]sbÖí&öÄÅ,m 'ÐÍD.ž£T.Éjãæ·¾^â.ЮßüîOFãf z¯Î(šI+ÕØG„3:UÞ(•£Ÿ@–!7¥©@a¼“–Í!]©•J…×Va°@®EŽÅSo!515¦0¸is*Òóª7K^£½ÐY©J_¶±–tFfh àz±–YŽI¦¥ŒGá TAÛ4C¿Tc´m .\ ©1h-€è–B­Ý¾òéiä,y)C ÷•p»<Ü#cwBf¾%‹®@:²”†mÛ+—I‡½_®CŠ{{çäTQk±c6_ïZ÷|é«m¾]f Ç­EoŠpF^ó¦èí…+ãññˆ®H6ާù’ºÎeZÂaŠ=TH¡žÌR©›2|ãºçÞ;ÿJãÊèÓâ4ghìê{æ8_µô ŸNÕ +Ô/?“¦E -$ÎÀBŠso„êë“Pãþ>g?¢J‰Âç¢s" g$YJ¤8E^^¢Í"C®x®8¡4>°2†õæR)mÐ_Gù"KZùÔ¸Y z ?Õ³å5óè`QèZS½s£[³´@N†óòßZ©˜%åt´þä"KµØ§Š7ûvWX:=#K½©‹%‡.»n2³Ä£ŠŽn°E± ·Ù!_ÝYßN`‘¥i’$ãc‰u[QJ‹SŸ¥ÓÉéå‚Ñ ³ÄZ«&>.àÔgé$™'n÷{ÞýEÖ]õ¥.npê³t‘ì'#a‰ý†×?ûM ^^{£é—¾…}Nî®5>''sž/±6×ÿ¥dº—°Ÿkc­ñ…;?<\0`–XkµÆ¯òª˜%ÖZ­ñÑe–Xw³ÄJÌk(1K¬¡tðÊëŸZ©÷3K¬Íuðoÿô'Wjç÷c ¨±Ç¡°†³ÄJÌÒ&Ò"ìExMë¢%Kuóþºnr¸F ¯¯ðµë·»g1KÈ —ç×½|c°T©rÎܰ´Êçk÷N•x=B¼„Ûv ùúÛÜ“ÆÍ’…N!³¥£P€² ðáLYdPU©ÌK½,p¡^òÜë3ÜJ¾Æˆ‚*/µ&‹±vº¦|Yc|AHc-¸µ3–ϱ ¥‚ES9ñ…)‘¥Lbö\ép »+cêD…×2ý(6a7KqïfåRåSít3d„3Rg…p™ð¥°YÒ˜¥®¾L+#Œ™S¥—…S™“Y!S'Ê,v¡¨P«˜†ZðvË›!-Œáµ,raKá´¤¨a7¬§;@³œ2!u´Át™úú1x]Œœ%K{Ê“1€ô!äÕžÁºlcÀs×ìnißS‡Æ%´¿|‰^9uŠï®‡*i†·Pn޽˜†Z°Kå©ÇÒXPÇ£Ó)–D–pÒ$©Ë¢;„ÐB!„\<¸³ÓÈYò¬Fîbÿú~ûŽBIÕ°„çLЕ ,a™œX*êPCði¨χòª ä7 K˜E‡ÜÄL²º5N†Ýž:]”B=üžõ¿c²F?ÑæÛQ–2×9¾9²ê¬¹âáýÌ[– Küë·…&pm-j4CñiÇúÀË<Ô°ÀÕi­C¿ŸöLÚg)C‹‹ýÊèp¬>!Å3"…˜?Àù±geUI+„A—-H›ÏðáŒDC£…oX*…é¦T)Œb0É!ƒmDYK“*Õ±Ó&#ÒXCŸ¥P ¤¢¤ò•HSòxjY¢Ê­…|Ä’ŒÍ wðR`R,”É{üµ­Õ¸YªàϹ®à=Á˶Á¸>œÁw¶¦˜\‹ƒ{£.¢7\°~TwVeð˜‘Îæ¥mj úck¡Û…òñ½Oý.§’5ݸn wîjSü‡ÆÍRPÏ= x¯óXŒX›D”Ë^Š6O?QŠZØ×ò’Òѱ”uýA4žÈÖ<Ï^ô¦VUpºXï=hz)4§Xsëšõíú,]â¾ÍÇI/Úr$,±n«Ù³~ð@ÇÒñt,%ûgÉ ³ÄÚH³gý ¦Ž¥äú¥³ÉiÏR€Yb­ÕìY?ز?_"–ÎÆÁÒ{ÿñw¼Áº«>Ö·xã?øÇ˜%Ömµ–¥óÉñ|º7–xŒBkÇ8è”'ÇÌk#E–^»:÷žÏ÷àÜù^rÈÏX› Yzís³xÄϽY·×¬É3K¬áÄ,±†³ÄJÌk(1K¬¡4ò5º¬5öØÖpb–XC‰YÚDµ ë½_Nv“Õ²E”vù»°§Ôu=ðPÌÒª¥¢8”ëÖ„^ãV—ò†Ðl(¿lï9GáׯÜzMHå’Þ§˜¥ , ä’ kÞX Œ«äú¸µuå—²ä›í¢ëe*±…å™ Œ›%++/¬Ò*l–Û†t„3бx¥Ð/@ê¸ß®j#T:â(YšT–¥ÀwS¡¤uPR§JÖ!n×RFƒBÒP ¦ËãþóB{,ØîÞ ½ܼa‰®{ ÝôK¥Ä½é±yp×P§hRIAì¢Â-ÈïñwÙhÜ,yCq»äJ‚﯌WÃé0Z¿¿xÞnN¡‘íá\åK…;„Cã…O[?PTˆ&!¥Z ÍSŠÛ-u³Mxª£¸nY*^Ç\ÀÐík2ª@'šÆ‡cX/f„2àÌ4r–ÈO€þ˜mßO œ‘áÅ¢ŸÀ$Î~…L󾟀ôµÑ²‹o|NB Wý&=?ÜÊyÑO å‘%t  ›OKÐEìw2è°šÆÇƒh,`µØNÐÕZÜû´&Ÿ|#¨ÃiŒ'Ú3x"ÍZ–ú6“¶ó9q®–¥5KU„X¢Zðrð#H3´Ö°”blgŸ%ÀF=að04µm°‚±5 £Š‘{2ÿ¥”†žÓ pF»‡#Xݰ¤ÊvÄZ0øiš!#Á ÒÎO2kè³jÊ`ùBTp´ÈfmƸ®ë ÒJ¸p…΢»EÚ6¾ip ³¬üŒ*Æý}\Ÿ†ÒB¤JY_+LƒÂìàE¶Á¯R‰6‹AûšJiô›tŠ&ÇJ•$!#ž…鳪c 8ÁÑ1µÀíbù «Â‚Y¬^;¥Z'Ëgï*cáŠi:OhL¥BS»ßðÉq›7KA×ÝÖù <2…¦¶ ~H§ f zˆŽè[@Z­a)fÙJ«,ÕÝö}áhýÓ¢ÐÔ¦ÁZnÉ4c¹VïùµÒ¥cb‰u[­Ü‹ðýbÊ,±6P³Gê[tÔ±4M¢2K¬MÔÆZ~âóoõY:$ÏœŽ€%ÞS~õ÷”ÿÄçàJÜn“î:Kß÷Œ5´¾í›ÆÉÒÇ¿ûÝ_dÝU_ê@zís³«~£a‰çKhÖäý5o f‰µ¹fH~‘¥³Ë.e–X7kö¬o(ÀϽY·×›³þ³ÄJÌk(1K¬¡Ä,±†ÒÁ7~ðC^¥·1K¬Íuð®W¿¯ÔWÛ|Ìë&=…5œ˜%ÖPb–6’ƸeK©×i)o\8ž/5¸I±)rÉyåüê]À–þƒ‰YÚDNøªrbÉ…5 àŽ«¹¼¡b³Üèäæö ®UŽ{97ŽË0o~„팜%üCÕNg´^_+Õé£ ÅÈ¡ 2Úœ²45ŒUR²Ô™¨­Bp ¦ÖhU–-[–t:÷ºD†Z,íé-Ë,ó…Rl ','P­뤭Ž€5ž,0˜€vqVZöÚâ0(+WKÞÂ4 ¢)J(þ¤b›=œ%¯Ñ^hŒTTh÷/˃$eo°w1Øcf³6ÓBWE›Ê íÐy M1œH8']%3¯³ÆP Ô·È„i¨Ý J Ü¥0Ê‚ 6{ÚÞ”oIÁ›XkQC±Lcà¥*°=Ú—Ò··¨E í»D+¤° ñMÔ å )àßM)Ô-úÃõ;K^Êé½N¯½\‡fM&3ÖRç4F‘§…1©ô•ËÈ="î6,- ÇZÂps%<,¥"³!ÔMF–DÑŒ2Fé¾Û†Ã_„D²êÈR!©ß B_S´@œ|­ÅàÑOÏßµæùRþëM¾f ß´+æx…XêÌ)"KÍn¸u¦ ÃjY‚îE¦¥èX °¤ùs ÌÝcéº9…ÏS¨ cð–±d2×ìR[›Òí}¼ž†~' J‰Ô4òZb¤pySÔÝ­tðüëÿøÊçÞ~ÛO6ùv•¥0Æ•Ñ"§qÄÁHêRá»§:ÓœðVÍÌ6¥÷8Åsø—žk“FW›h( Y‡ú,Q-XƤPЦ9=–È'\%uW¡V(– Ì ù€Î|Õ´}y4Þ¢ &†î*7±A²±ä ÷·ÞJDæÁó¯û¹M¾ÛQ–pö# ¡”(ÐD4­ÎÔÈD-г ͵ð™ú6Y”  6Åø–pÞåPHÓ“I,Y%„,b *ØRPj!0K.aZE– EÐ ƒv_è(¦i  `oKÎJµ,C#…Òt Ñà$©vÙÜCoí¹ÀÈY ºþH®*·jºüüªKƒO{×(¦-wzk„Ì’_`)ùŒÉ䚢1ËVšUQÝfSÖò}mžE–êÖÈp½ùVÿh¥\rzr>6–z³‹F[n­›r̲vQÝ»¬T›d·½t`ßæYØê³tF›¤&ɲMRwš%Ö-Eq(-N=–öÑJà,y±t#pf‰u]M|\À©ÇÒô4†Æ%K6g–X×ÕíŽ8-Ì—Kû“ÓÝg‰ý†×ûÞ«,íwÓ¥féã¿›}NÏÉkŸû¯®öKû“În—Yâ1nÍ:fWž Kç“IÂó%Öfšu ù+,ÎO@'ÌkÍžõ ø¹7ëöúÚçú†Ìk(1K¬¡Ä,±†³ÄJÀÒ_9]©×™%ÖÆ:xþÏà}«ÕÆX1K¬›4ú8Ö`b–XC‰YÚH¥Éóêe—åÖ™Ù`¯Ò|IžpnÙ•å¬]¥»a5w³´‰¬4!üÚ…5ïb)1,ò†ŠÏ– –p‡½4[e+–×Bfµ®!·1¿¸•FÎReáý„„–o×Ý"n[»S‹+°1fÉoŠ^–p!Ÿ¼¦3V5c6 ðÅHKÌh{i¬Å†òèMAg+º'ɤT –ÉsŠÏR áÝV§†‚‚iðÂÓörˆÙ»¬M%¨HjìšbÞ+‹©\6D¤7…·¢±³ä1^ŒüÜR?ø×÷@5þz±è'0ÁóÞ(Ñ‹ýRkXð Z:?¤mÑOÀänѰTk¨™XêÝ¿­ÅE–r-²6oNä±aq€,Ѽg‹:xþÛ'kô3M¾]e)§~ÉFrhzƒ’dŠ#\ºlYòm€YUCwä"KÈ…C[€ž7E-±›e¨a%ÌùlA,áä¸Ô ,ìËZ–Ð’ °ÔÝ¿«¥Œ,GFؘ7£ ²XM™bϤ·¼ÝüÈ¿Ãù’„ù’K5L`\Ú|n’©ƒ7E‡'áJé–Rí²6F>˜‘ë„ÈœN3 YËΛÂ@˜®„ú,…Z Œ( < A²Èe¾À’Àév(Y`ÉÁTÊèªwÿ®E,581o¨ ©ÆËíì[¾¨‘³”Ã`Rä¢¤î¬ ì–¥AG7&j0a­2ž/ei›%§!$Ë2ì~ Ž!™)C.êä)„0WÓX‹Á‘,Ë §Åª°`ƒ¿s‹eàJnðt ׳ª†L½û·µ8z¾Ô'¬ÔÆZN×ýÎF1ƽç÷¾ã Ö]õ±^ ø;èÏýëWYº¼CÜî{ßÁ,Ý]}–ž½ö§¾ç KG—ói²û,ñ7„:Ï ß½:Æ]$ûGÉ>³ÄÚD³ž3Å•çK{'óù‹i‡ÒޱÔ_`È, ¡Ù³¾¡À˜ž{÷2KChöZßP`‡Xªl]5ëÃ7â´ä1¬Y´´Àö×¢‡ÊÌÒðÚ–pbXHˆ $i?>\ò(ÒnàšEcp»š”&2KÃk—Xòq9¡¬}MÀQ¸¨(¥lh¬ã×¶ÌÒðzy–~~M˜ï+ÿ³D %jAK‹ ®>¤]Šœ–Ò,_Š2KÃëåYúäë«ýþÊ×=0K´PÂQfaž„,Eu{Êd)°Tãòz³4¼žãº5º?Ñäë±ô©ÕìýÜC³D Sí4í6JÛq¸|Q–9®` Ë»QÌÒðzùØÇÊ-h¤…„íI\ÆhMšgB‹Ï—Ò¸¥-³4¼v‡%ÔªÕ6äÖ5ÁÌÒðºKÇá‹»éÞr–~íïý?C7÷-‹§FÙfb©È, ¯»°tœK‡“é–~íïý_þòW¶ÐâaÄ, ¯;°t”Ђ‚ÓÞ¢Þ†%éËÌÒ¸t–Î/Ž¥éÉt‘¥$fidºÓ| YÚß›÷YúO;@ÿçÿõXõ?þÿÆÖ~§cÕYJ’d2i‚ ~îùŸùËODÿÞÖ~§cÕY:::J’Ó¶_ú¸ÿʯt8ñ7&Ý™%ÐÞÕg-NÌÒ˜´­g•'fiLÚâsoÀéQ°dcD5-Ñ­ŠpÀ, ¯í~‡òÿ ÛØÛ©ñ_°¨`%Â, ¯Ýú>n™JD(x€¢¯_ÚšvŸ%a£ý(Ú¬•ž×UnM»Ï’lìGMJÖ¢‚c¶¤—gé§Þöá•úw¿þQ²ÿp¹·”ˆQð"b–†×-|tóÕzõ]’¥`?jLÞYå2KÃk÷=™EíGa¾¤ Ï—¶§Ýg ML‹ÆÔx0KÃk÷Yò‹Þ“‡²5€¥²oüYqìÀÖ4––ŠY^Ìk(1K¬¡Ä,±†³ÄJÌk(1K¬¡Ä,±†Ò#aiݾH[³4¼î‹¥j}Aƒ[:ny Ï1KÃk+,•EvfôypBÊq»3´@ò´ƒdYCšëËÜ´vÖàŽéb»[ /ˆY^[aIê̈¢”¾.SÞÜì·ŽÍѲM{¡œE& £“ ÏH²· ;Yß‹˜¥áõò,}õ“«õöWKa¡¾Îq›W—IŒ#B;­ª‚+^Ôè#©ãÖÛ¢ô´Ávض8“[Úñª˜¥áu‹õÞïÿÔJýð×µ,QàGXÃhµ0ÁšÍ†…È’iXr-KµÙú¶ç­˜¥áµ•Øœ¶…£Eû´O¶FŒ #+¼º‚¥{à<³´ m%™jEÁD½ŸÖFX#ŒJ—³t/Ûž·b–†×VX¢ñ-îLÒÍîRb¥Ipg‡14$ì[rÃcƒaÅ, ¯;±tŠ''GGG—×âãV9Ú>œ~íô˜¥áu–.ȯ2]c)¿×kýê—ÿößípb–†×X:IÐG÷|rÚöS2n·Ñ¯¢!T‹³4¼îÀÒ‹èåuýë÷·üøÁ´•u•O@¡_ÒÆhoDUËZ•¾lvåÚÂËÌæ¢ªp#ñ0’© O…×ÒU¯@¿TÐnõ©ñµŒ5ÆÔ”jô´jô~Ø{Ò˳ô ëÖ{ÿ«O‹%/%ŒBÐa¤%¼ˆvÍῘði†}OÃ’D/ÞØwÑk\w\F–ÒÐïÔÎÈXcLñ:â‰kÍØ)ýÙW_ýÂJ[ܯvùv4n—XÂþ$ë,vCä§°Íwq¶ÔgÉÄ{ôYr‘%á6Ð÷˜BÆcj 13¡w}Êôû?ø'>´Ê­ûOþ¥.ßn²Æ8 ‰±ÒáèSžšg'O K€ZÕL˜èu-hT–‚saYª%ÔSã­ÞšûŒ$}}Çéê™ô_þÁ.ßN²„³Q!Dês³æ ¡”(胛BáXr±É^—çÞÒ[§B‹ºPcLqaûÎO»I£f)èú ±*H¯ZñúŠB]½b÷û$³Ôg)ÃYÉV²ÔeY[ù"K…HïÒÒG¬7g ‡=–NO 9?9KU×ÃX‡²K9^Ͳ¶òPc[ï=…"ß¿fÏ^û\§Ž¥KÜþx2mIŽ…%Öm5{êpjY:J&ÀRr8ß?b–X‰X}ì§¿†‡-K{'Ð/MNŽNF1ƽ÷Ýïþ"ëŽúÒ³VoN½ù±”vQ%;ÌÒ[ÏXCëßy…¥£ùa2–üùæoþ ëŽúÑI0mZì—p¾4–x¾4€f=¼¿2ÆÍ'ÓQŒqÌÒšõ@ò ,à¬ûdsofiÍž-<`âçÞ¬[ë­Ï-<øf–XC‰Yb %f‰5”˜%ÖPúŽ×WGüf‰õúÏÞþöO¯ è-Pf–X7é‘ìÓtïb–†³ÄJÌÒÒ"Ë®lq§ieåµmï¬Æ+«—wÛ]·d–n–.ϯø d´W^ÏŠW"bK¢ \ ·ÜÉ`‚Q³d3€ÂÆ­îÊ.  t´Ë]‘ùÚ[¥2 ,õ²1…¡`_RN™)‘¥Œb˜J‘áéÂW9†]†Ô 4>·Å}n•wo5K^…ða«»°³]P.U¸ ^ ¤-…¥ø8È’¶Y\)œ–ñ¨ÀC¯µ®8ak-€O š´P”yLî1¤óûÜ*ïÞ4n–¬”uÜêNú.Ž cšD³ ^©ÛðÜ9é›,˜©=Ê ¬Â"Kh|BaQáD®i› ŠÝÛVy÷¦q³ä¡i¶ºóWüôÒéù Pc_A,µá¨LRQb (J_+¡¥ÃqÈÆª²´Lïu«¼{Ó¿?Y£ïòí&K™ÖYÜê.ìl„ÛÉf<“¶,U¸ÉtÌB{ã5ðuVÁõ¦_Õ: 5;rèR¡ì½n•woõ÷qVV•´a«»°³]€×dX‰4•uÃRÙÛ O8ÜOÆ#x ¼®ˆ%+Â0üIŠ*¦ÀÒÎZ猚%˜ÏøºŠ[ÝÙ.Wäøš¼p cpq¯»¢ÍbÑj©ó¬óPœ§+M.WWÀÅíñ(Å«é.öI¨Q³ÔóˆÞëücÀ+:Z?‰Ï”š'K•ÙÕn©ÏÒåù8Yêmu'²5&‹Þ9­·Ê­².™õ^OI³7BÀnT¥=ÜÙ$¡MáFÅë¶Â8”N-K§{“éüòèè0a?ÖfŠñq N-KÓý)m‘³ßn”Ã,±ÖkÖÅ#N-K—k9¿HNÇÀÒ›ÿÄ;ÞñëŽúØ‚ÀOÿ‹‹q»½pf‰uƒXzísÿÒ–ºðf‰Ç¸!4ë4»æ'Ðâ˜%ÖzÍúÎ ,íïÏççÓsf‰µ¡fÏ ø¹7ëÖš-XŸ2K¬ÁÄ,±†³ÄJÌk(1K¬¡4î5º¬!5òØÖ€b–XC‰YÚ@µ ë½_Nv“Õš%œ¸äü¦ëIÌÒͪ¥¢8”ëÊëçáÆ—rÍõX~ýú^‰××äXÒ¦³t³Â²¹äš·YÐéÈõñ”ëÊûöž! ³^¶¡alSú8Ì FÍ’••Ö¤¸/®J4o‰Òu ‹‡KÚ ©ã~»ªPI…R^ÉÒ¤²,†¼Áii«ÞLÉ"'þ,Ô¥t-©«…ž*Ô‚©Åò7íµƒ[¶*xW_Ãiì—JÙ„wwhR‰»G[eµ¸‰ÊûШYò†âv5î ﯌EFû€;_.îžw[……Yý ®áu©p‡pÌå<€Yˆ&r êG\5ôQXm!b-æ)–7ˆC¦ðÞ ¨¥ÂrPÒѧ+´{êîbŠõàÆ÷>{‘Àãf)ø À{a²¾ŸzT+=½M‹~“‘¤%\#àÇ©‘¯–4«¢^']U!ꉜ µH¼Hå‘W@#  d‘*dKž\Sâ°bS‰´ãË\=žÿ|÷éÏwùv“¥š|NJ„Fzä'\$–° ÂÛ\µ,õÃ*ëRd‘%çkYgÀR€E‚uX¢Zð²£ò9·YJ›€Ï†%ìwt¼d„®,¤x·B{#ôcº·'sð_‚A&%@A4JáUˆÊ«²aI•½a°Dœ¦Ïað®gŠœÒ–%Îz6aå}–B-B,_Ãp•ê>K9ÜUg0.†1.ö–ys‡Â§mŠ]Vî‹ûüÍ­Ö¨¿+à³QZ­Ták¥TóÇ-R|ýA¦”A“ÉàW©D›¥P æ@JÃïpbí¡pYY¢-¥BAÿ–b嘋œ,!µÀíbù«ÂYSy‰óu(©éƦé.ä†ì1ÅY¹¼×ßÛ:š¥ ëxÖù <¸d/õËZÿ`b–úïü僴ZÃR̲•FYª[­ŸûÈ^><<˜ÞZ°è³t´?Ÿ_î%Öm5[°è±tšL1®éprÌ,±6ÒlÁN ci?A–’ãù”÷Ûem¦Yÿ8µ,c¬å~r˜œ¥‹÷”¿»ú{ÊNWâv÷“iòb,Íž±†Ök¿s¥³Éi°Øu–ü÷½ûÝ_dÝQ_êô¹Ù?³É‹‘°Äó¥4ëä¯ySL“dc³4„f=üKç8é>;¹àçK¬Í4{¶`(ÀϽY·Ö›³…Cf‰5”˜%ÖPb–XC‰Yb ¥ßÿÁ?ñ¡¯ÐŸüK]>f‰u“þ쫯~!_¥¯vù˜%ÖMy k@1K¬¡Ä,m !—û øu1iZÊ×çK—k;Šbº~E9¸bW›U¬[Z|b–n–h7"—\X³n7\½)FÊ/õ1¡ßKClø´Œâ&øé¡6…5KØh—•v„AÍ› 3\À¯S…¡Kú dÅ¢©fão¼`ÐO +Ea‚S`”’×F¥N b-1"#já¢ôº¤`ÜP Æ$aùÌSqkt»Ý¯ÕçÔ°ª‡& gµ•á” §¡¶ØTJuhê¯+uƒ¿Ê–4j–à7õZÕ²†ßÙÆZjеÌl.ªJm¬eÖ…cÊÊk‹q·¸U³£¸Çʧ«ÄÒ™jü(b²6¸w3]±H3â~‘ˆÔIWÉ8FÁýਵ› ês¬·‡~I“w€È±Á¡©1uºôü@^ãfÉK‰^Þ§‹1àvy ¸1¦qÁª¬[?cRÈìʰy8¼É®õ¦èbÀENµàíü•ð´e ʈC¨kÐ¥ZhálžŠØÔ˜º\§ŠRl'èj½Fþ|‰XÊñíî{S໻̛M&šùM©eѱ„Ý‹ÎÒÎO x6AwrÝ›‚j‘xy…7E(íz,…Lì—‚ƒ ²Ð/…¦†ê |ŠôA¦Lã~îÆ8 ©¶0’´†8ðN¡gÌK=sùÖ ð¿©q)^@Ó›œ tÇÖ£› ž9=–B-æ‚Ê#³¥Zd «L#K¡z$NKdP“—@)bScоÚ—3[ù÷q8û…èå• ücB£¬‚>¸i!NÑ£——h³¤hÀeÉä ÞR]L‘Ñ ?šãgw%„,È‘‹ˆCW b)ÔBŽ^¡¼A[/œ6·,)ô÷jçÞ¡z õã3¬1.TBÁÈšR…hx2j–‚^ÂO`Õ³åçW 4kíL_²@hjÛàú!C™¥>KÍL²uÏc–­4ª 3£p´Þ£k‘¥TÞ«}Λ v}–NO09K½Ç…}¶œ]7NÄ,ÛiÕíÖ÷b¶—Âr¿#ÜlÁN ÇÒe‡r12–X·Å¡t8u1àÉd:?œ^&ûÌk#5ñq§–¥½è—N’9ÇZ²6U/’qZŒµ¼Hö“1°ôÖ}ØLo,ú Ì_L÷ÆÑ/ýö9¹»ú>'¯}nv%üäbÎó%Ö†šõ@ò×ü’éÞ8ü—˜¥Xúègbøn¥““ùüüð°E‰Yb­× Aú¥öŸ{³n­¯õ@òÌk81K¬¡Ä,±†³ÄJßñÙÓ•ú‹Ìë%ô½òÊÞ·J­ËÇ,±nÒÈãPXŠYb %fi•&Ïýˆ/ÖÙ ëkIùmöÉ5½tñ|¶nfµÝý™¥ d¥ Aà×.¬á«”ÆÜ´Ï”ÏnóþÊ^ºx^‡ý8—Ëm7²`Ü,áúh ­ß®»UÜጭ-.ºÆ¨"Ì)zYÂ…Î;\^ÓÜù½†c|A¾j‰m/µÔPËW!lWBªÕÑ‚ôÊÖ¡Ö—šÒ†óù¦Z8cqßh<…Q¡A–jÇÔ ZÛnÕvb1GÎ’´VTB•0¦Âg¤PV­ªTf YZÛaRáS•! ­ÑV@¤FNèTh#óÆœ€jÀ EÓP Þ®Àò ¦¢Â‚M€yeR¯±ÄÆ'Djé$äUÈR.’¾»…Žg„Ëdæs²h$ë&Å€óo¾-èÇÍ’/„Ƚ(ðÝ‘ð×/ãÕö  ¨¼ ·mﻣ îÔcäm!p2‚ï:c8Rd)ÔZáCjòuIAàYJÛÈ‹Î@ û€¡jXò¸s¸7HL‘æPmÛZ¼"\8Lc<&†6u ê¥p[*–oÁ½bä,Ñ~µï® àþñð/¼%TŠïF4˜àyܽ[–°Lè—êXCþ†T žå‘¶`!Ðîz%EˆyÙ±Dm3XV…xôvb$ðdw Ež:¹ J©ö4Þ#Ír!ä¼tÄç:ø—'kô3m¾e)§~ÉÆ7¦7¨ö ù%•-Kžæ/¨ª†îÈE–gáOÞt,Õß,Q†X¢Z0ͨ<…‘ë>Kp÷‚Ü*j±ÈR½ ¯ëÈRkñ“c…áL` í+tl¢•ÆTx2IÑm(û :xeÍrŸúd›o7YÂù’„ù’K5LO\Úþ¥Ó‰à®D#ÀRª]ßÀZÀȲ™Ó) • ݲä äÐ"ÖÐg)Ô©N©¼•E.ó>K¥rh§”9“© †#¼XÀTHÔè…Vx$ckáu!ëpí Bìn$Ë\ؘŠÐoÉeÜ,åðk/rQRLwÖEv‡38£( ‡Á™Gx¾”¥m–œ¢³³Œž˜Ïz(c³*”óyª³¦|ìSÆ4ÖB·£ò«Â‚ykÈCw…›W±"7¼ÆÍRÐuû€MKŸŽÂ3¥öÉ’[ê”ü"Kç½tL,éî¡‘V(­×ÄåÇ,[i•¥ºÕ¦®eȾ¾JÝ¥>|æL_ûÄçßêv,&Ir6?NºX˱°Äº¥0¥‡SËÒùää0JöÏ’f‰µ‰b|\ƒS×/a„ÜâFàÌk:?©?_:žŸMÎFÁÒ›ÿä;ÞñëŽúXßNàŸÿŽ¥ãÉþ|4,ýSÌÒݵÀÒG?ógZ–%˜5ϧ{»ÏqChÖé—zcÜE2™N÷¡S:œ3K¬M4ëä{,½˜N‘¥ó½äŸ °6ÒìÙ‚¡?÷fÝZ³. 0K¬¡Ä,±†³ÄJÌk(1K¬¡tð;Ö­Ñý‰6³ÄºIc` 'f‰5”˜¥MT»Ÿûr²›”X½©üÒ+K3¯½Ï¦ÕÜ]ÌÒª¥¢8”ëÖ¬xÅý+å +b¡üõ›ÕÙ˲Á½0€r]C–Ý`;b–6Pxoå’ kÞ'Œý­äú•ú+Ê·+ýûK¹WÝ+Dç…‹å’­–Ö­aFãfÉÊ wÑÖ*î€ÛÙ‡3бx¥„±‚öÅ•a“Ü& diRY–pž¶à•ÖAI*Yçă¥Œ+„>*¤¡L-–7PPhÛ`‰ÛüSJ…°Uã½ú÷¯q—`ì—°ÞÚ]ãí„7)n!Sƒ»™+—«íDXv7KÞhy‘Å]´C`¶÷ñŒt^Ó6ÞíFày»W8…FR ·ö¢[*ÜÜ /|Úú „ BŠž¤”j4OÛMåu†PðµF; ´¡p´õ·Ä{õîßÔâ|&ÂÅ”Ô<ÅjÐ@ؘš\…}xët»;;œ% cÅ1aû~ጠ/z~ÐILâÛ!dš7~xæ,F÷üšßPÃB 8Õ"ñ"•ÇncÑOÀi âŽ,y šT„Kÿþ]-*°Tï’ò:C>1ë ºRè­ÎþóíŸþäJý|›oGYª%Ìhˆúƒ (Í<‘f-KýÝpm#ZôÀóµ,mÏO À¢ŠPÃKT ^~Å›«–: ´,iíªÀRïþ]-‘%N9y)òZKºŒ)Ü#ƒH ³½0KÒÁ+¯j¥Þ¿óß¡ˆ”†žÓ pF8‚Õ Kªl‡A|»aðÓ4µÅ‘†`Piç' ¡sÉd¬¡ÏR¨R(ƒå QÁÑK¸ÂoA´À,¾ØYõîß«…ƸKY[“Âí o›ÆÔàçP >ß²Æý}\³‹´)†ÊÖ½€Ùp?EÁ‹£^³ •h³¥Œ¯”F@‡sc è-5È¢§Ú ÀºŽ5à‡(ÓX ܆òV…³X¹¥ÊáŠé9vZÊh yû÷okYUÎÂO¡a\ÓxÓbS¶=ë7KA»ç'ž´O–ªíúT6b–:;6ßš¨5,=´ŸÀ&÷_d©¼é‘émõÖ‚@¥‹Ã= þÞ?K¬[j¶`'Ðc)™îOçó“ɳÄÚH³;ž7Åô#v§ ³ÄÚPM¬å;èKoúÅùÒe²??¼LFÁÒ¿Í{Êß]½=å§>KÓäǺ1°ôÖ3ÖÐúèÓ±4MÎæcaÉÿ»ßýEÖõ¥¤üÈ/öƸirrv>–x¾4€f=|o¾tбSf‰µ¹f=|¥Ë3öKg—Ìk#Íz y~îͺƒÞüÅ…Cf‰5”˜%ÖPb–XC‰Yb ¥ƒoüà‡>¼Joc–X›ëà]¯~!_©¯¶ù˜%ÖM{ k81K¬¡Ä,m"!—û  èŠÝ(ð ². Òn´æÒÓ³´œ@·¹ü"È5ÑH7 •¬Ùì­¹ôd™7Kå¦Îh¹¾Vª F gtª0t)G?,£¨Çnã?«TJáFÚxe%†T–JUXÀ†‹ÖhU¹îE–¡C@(là&¢.mÙµÔøÂÅM¼Ÿ ÆÍ’ׯh/4FV›Z¶±–tFf’ïz±–YŽ)n]+]%ð ôKF¤î­±@tpQfd_„HÜPX—B»Ã,‹\ù°7z`ÈpÞ¹d<%œ%/eô¾n—Ç€c¢ Žƒ×)íû^F–¨‡ ‘u‡—moû¢a)xâ@V£ŒQÚW.“È’´)þȈÌ?=|׺çK¿ÞæÛe–ðý]ô¦gä5o Ä!L‰½F(\dÉ×Z˜P:—lÌ’Ér™–Ô/‰ó\?I’€¥oX÷Üû'Û|;ÊRãÊèÓÒ„3Gµ¾gŽóUœ0¡ M®1M‰¥0aάÎ|%‘ÂR-g©Š…M µ!t¦4)z‚üu´MŸâø†÷÷q8û…PJd‹ÕÄ·†3ßu´ìrÊG//Ñf)á5À pî-}&»¢Å Q—S KªcIÓ0Jæ_¬áN>—4ƒÊÓÝž¬ÆÍRÐKø T+^³<³„ê‘“aça²uÞ1Ë6÷dôæ›ý£K§'çm:*–zV²œ]g³l¯iOH³±ÔiJp³]JÇÅë¶Â@”§–¥³äÅ|: )³ÄÚH1@n‰ŸÀ<ÙëRf‰u£ºÀ¯ú ìONÛt×YúþßwOQöãÑ;ÿè7v(·éγôžßÅ>'wWÏèäÙG?ógÚ~irÒ¦»ÏqChÖôK}/¯É$IöBÊ,±6R`é#?v–ï¼)Ž@'!e–XiÖ·¦àçÞ¬;èk}k f‰5˜˜%ÖPb–XC‰Yb ¥ƒW>{ºR¯3K¬Íuðí¯|à}+Õ­e–X7iìq(¬áÄ,±†³´‰J“ç~qOwÓK{ª2Ÿm¸¼Úµu¾ÌÒ²Ò„ðžbîÕ¬Nxmí5Ä–Öª}¶z]ùãǪq³TYô“°ž–o×Ý"îpÆÖ÷WÆ$¼)zY0¸ÒµuñŠu¹€ÂeJQpîF{-[WS5ö^¤e,\û k¤ŒW“W¾€™ÌÓ19KÒZQ eT ÀÑô'áŒÊ £U•ÊŒâã ‹jâà¤Ï%nõ®@ÍR•£QE‰÷¦ÐQi#s/•‘oѼp¢RÞ¹€’d„Djd•caÚm÷~v˽»ÆÍ’/„ÈqŸm ð®­xµ=£à…ÊÛpÛe‘x­;¢+P7Ç­âÅTf´!3†ø ¼(2›X+q²ö5ïb¸o!pEÁâX8Eºkõ4¼‘³ä…ˆînßÿú~¨˜EÆâã]‘>ì,Õ)ڞЎ·YJÙ%UïDû¢cÉû`ã4ÁZ GY2x„{Š ý4"¨~Ûºg•?ÓæÛQ–rê—l|¯ÉŠÕžA^tÙ²äižD ,µ:tE’U±äqè‘ÑHi-n` ³ÎÎÊ:ôKh3P×™Øh"ÿ4îïPp¾$a¾äR ³—6ÝL8#±Op%ú–Rí²ŽžB¸LÆ£pÒ”XŠÞ;U-ŠBÖ"sÊ,gIW¡°N;ȨS+a¯á5v¢xB‘æãþn7‡éP‘‹’Bº³.°;œÁ¡0¦Æ'AáùR–¶Y°Ç1e;+W2“—ø|)k²Yc`&eLIà TÕ¼€—µ¡ixç_ü`÷¶pl*kž–åɸY z o Ö1KhyÔ½T(½n²³Ä£’ŽžÆç¬áõµ¾@¥S @ôpl,±n«YßN cé"Ù?˜’ý“ Ç¡°6Sˆizçý,u{¤]¢+ÅÙåeÂ,±6Skù­?ö³‹ó¥3ܶù"옥 öZ–K.¬c«¸køÊŠoŠ}£{†Õäùqrkj3±×²f}ƒî¤q³d1Û*!ûN(Ñ„…3бx¥„±Bê¸ß®j#Tn«diRYÒö¹>JZ%uªd–2¬ú¨†Z0µXú!´Ç‚Mì]8ƒ{ýb¿¤¥¦zsLTW¡(© Í ‰­‰-+ ÷2²´RØÐHªgK7KÞhatMa¸Ä g¤£¹{û€çíVáØUì ¼é@d©p‡pc(p¼ð©1ŒPTˆN!¥Z ÍS,op3paÁÔRá(édá-$ö'xS´Â*éÛ2öKÚøJ9ü#¡ûèšBE]h¤ÇÝË[dÖÈY"?Œü¶ï'ÎÈð¢ç'=Õ$Î<„Lsb ˆÂó0]¤Û?¾ ð 5ï†Z$^¤òºô!Š·‰ d‘*ĘK–¼NÂPÛVˆ3-ú/–èg f„ÖfÐÉAͱ‘ÐÊ­m5~ðêÛ?ýÉ•ú…6ߎ²TK˜Ñ9Ôáˆ”æ žH³–%Üi·©Áf0¢–ž¯a(1KU„X¢Zð²£òñ›«–RÜÑ·Ç’G¯ ªÛÛ¶Â5,YjÝŠQ¿YmÓ€çà•×?µRïßùïPDƒNJCO‰iP8#1Ôp«–TÙƒNÑà§é„·/#TÚ²ä5úåÈXCŸ¥P ¤PË¢‚£>K9œÑŒžaŒá±@ŽåuÖVx% ¥bÔº m)4? ¬¶T>îïã èîÓB¤JYt9RÍ'çpûxQ`†‡J´YŒR81Ñhà`¾M½¥†¬˜Ï!TkÀ@`ÓX Fû†òVeɪ)V^âôJjº±n‚RiW!ÞCu/¨6šì°¶ph'L“r˜ŽS#·©q³Ä~ÈY‚¿ßŽœh Ö°´è'0¬lp'xÖ] ·~¶Ô±tq¸G»§;ÀÚP³°Õ‹išîO0Êr:9c–X‰bšZœºýv¡;šNçóÄYbm¨&Ö2àÔŸ/]&ûóÓäÅ8Xz÷»¿Èº«¾ÔE€N}–¦ÉÅerr6 –¾ïž‚ìǤoký¥³ùÑd:LGÀÒÇ¿—û¥»«×/}äG~±ë—¦Ð%ŸLÇÂÏ—ЬÉ÷æK§+@N'£㘥!4ë@ò}_¸3=YjQb–Xë5{Ö7àçÞ¬Ûë­_ì1K¬¡Ä,±†³ÄJÌk(|]%k@|½7k@=…5œ˜%ÖPb–6‘!vàšÖGP4í êm[Ø­ü_rE[¸²Y·¶ë2ng³Lfi9áòüeýR圹!DÊçK¢Œ*¢èºk…pÕr¤¯+«Ödl›=¬¹À¸Y²•ŽvG-»=RÙ²À­JMU¥2 ,õ²ä¸yjªòÜë³ÿ¿½³ ‘$¹îxíÁ Ã^ >HŸ¼¶/^á“|›£?n’`«"üuñÅà«Î6XCИ–,2M˶,Œ™v$H79™Í4-ÙfgpäÁ/øë`t0¬›L%ø½‘YY=UÝÕݯf†Êÿo—œÊøÈÎ_gFU×{á+Žˆ¥½mYû|‘‚ųNjèëŽÍˆËTKík)_rÁ «T7ÓzŸWœ÷÷T)»TZÞ8›kˆ;ËPø¾¢òã‹¢-l‘ wÙH«txι ¸ºÉæíRеtµ±þØ'ȶqµ‘ÁFÚ>UŸŽ’7©¹º_nxì§œIÙÄ5ivãT”˜·K‘I&ŠÀIŠbê£]¤CÓ-©»Ù\;Ì‹`âÒ³sþº÷ð­ï¹T¬¯VÁo«|QÜpýÒ!éU+uû¡õ¸vÈ”Iäòýo|o½6iZ.ž^¥h”9¹î Ç¡¬u]ºX~ÈùNVó»/û’âã’Nã¥åcº;­Næ6^÷e=8ë4uédñìjyr6‹yÀ¿øs¯)È~>¼ÿùŸz¶Vé©üûd9—¾ð™Ïüx(9Qé¾þGã}édq~uõòɳy¸„gœ—k‘þušËk±XÊxéÉâ .½¸\‹ÔOsSœçW/Ï?ÅØìÇå{Ó„øÜÜŸN2SÀ% \ZÀ% \Zœ¾û·Ïvòe¸öçô³ïþö;YÝ.Û˜y P.-àÒ^Ô¾,Û»~—²+öùNä¶ ðÛH]y%Hœ¶ßô•Ð n¢þÎÀ¥}ÖE³%9@¸áŠÔÖûÛæ™£òÛ’SÜFêŠÝ²= {fp}±û[íûUq™»ÔrP-š12F„®éxB8¼mùN‰;r.Ìlé8¶…?‚„ïÆHàˆ²^¦ZB,?¢´Òfj_^Ê2Ä•Ðõ]j$ºDkv£/txÉÑÃ1ø›çÝ´F›»¡ ]ŠMÉã4Î\m;=‰Ð·&­úìw͹»dC0­qÇñfÃmÆ:oªÞŒÏ'§è]:Ä óhK¼YîJcLÇrMG[8Î-ËMæmY§Û˜ÔÐS&OËX —é¨ãjb¸[cLÎý«øXÇ.•Ö;Û¯›È8ÀÏp$'¥ˆy$6.žÍ6_´.çç¡TK%+9 Ƴ'Àˆ«å&Ÿ¹K}eLÙÇhVÛ’Zi7½¦ßqZºuà.-lièº7K…9 »2)”—ƒ-Ã$¡@¬2}\J-\¦,%p·w‡¹YI„¶åØÝÒ .É}®«‹,ESµcoÙoÓPo+‰ê¤§—å”)|lÑÆÓ¢%UژɋÚ)Õú<Ädû¸Jû;·ïoî.íH(À¯%œþ ðÏŸI‡ä–~¹Ù%2jÁÛ{ïŒmÆÀÝx_êR AàR‹ìÞ™PÀºÀŠõvâïñ\–£ƒcU©·¼Ñó~]ê C'É%âiY9·fH¸1uIªõÃIÔtìRZåšö<}ôK7|VùÁßÇ«K¥Ü—B2gLtbå"ØD'ñB a³mG¿ºMr‰¯W÷³v©ãýÞÔc¢“µK|4ªk‰NF—øÊ ÈlºTpþ€®K. o8ýAÃû»t_âs’΢ ñ´ì.)vœÆy’¤¥wÃ9™;¼Ý;}ôηöù#Ê‘ºÄã%Ûrº¥Œœt)bóÆüc¦KZÛ~p)Ïš!¯RgèÉ×q‰FÒd¹ä6ª× <‘™TÃÔ¥X •1UÙPÊ©KYÎfrZ'R²ªãÝŽó-å—¡ÿ%¯“M½­8TÇ)•Ä%s‚µ’›©‹§e·¹$™ ¤ZÖNN‚~?JÃMÇÕ;Å “KíóÇÝ#u©¤{Uš˜©­XGãZɹƿ“çU£QDü|©È§‰áJN*)èx~OTø:åã§FežC |T–©Ïyà Î8¸*.X×NºÒzîD勲mÇþŸÇc+_£â*§&h)MÔý$HB9)æ%<™ÊM^tñã/Þ/rÉIÐ"p¹6®Þ…™»y5}€}]9àR/IgÇ—ŽÉnzë’9H·‚ÔíöíÖñðƒte~8M'0uéÕDY>^ÇZÎÅ%pO.§é&.½Xž<%¯žn Ü…KàUbLÓ ÓÚ¥—g/¯V«Ë󭓤Â%ð*c¬¥è´1^ºXž],VËå–ɛͥ/üìÏü)x(8M'ð?ž¸t±\]],Nè1wü.}ñ›¨a†¼ÿù_]b•®ž/ήζ$86—ðŒÓàZΜõ}i¹|rvNC¦‹9Ü—à’×ry.=].99ÅÅr1‡ñ\ÒàZŽÁW?«|1»Ï*Á=¹–ûŸ{%àÐ.-àÐ.-N}jqã¤_p ÜÆìc€p h—ö¢kÚ¶¿ó<_aŸ©lÛ-ßÏÛ¶íÙ^ÁÞSæhª²\Ú‡Î:‰CyuÇ _¯oŒ1ö–¯ßSy¿%j(nãX–ÉÆ]“Ê”›7udÒ%wSJŠ|÷®½€KûƒÅ¶¸ÔÜpi8¸¢ºwU¼½üè×ô«Ü~ÇÝGN·Þxnørø°+¿çŒu3w)pHtpGÍ7Æ™á§hœã)néé…Æfr_âC†+J;òÞÙÚe¶«i»l¡C¨®,w¶‹Ùì{j‚îQqk¡2ŽË“ ¹1™lõÔrα¿Ö5™ÍÈ‚5aÚ~|ÍGò}‰ë GF:>îP7veýÂQ\86N=—¦cÏ©¡Ø“XÏÝ™¹K½—‰À zŠI¢M»m#3m7=G¸ñ5Œ±–äÚ\DB#%œ ·tx™K„-í·UŸùb tûÇ%×Âe|ŒÛ­3>*…i§^qR‚?+Çõ1ÌvÒ~ª…É|œÚ;þ&ÐZëxsáÆ®¬_¤iÄcãÔ:Ij:öœ§•v±¿}qŸÂçî’äàÈo¶æà]“|tX¤Çˆ±ô,H1à¼Ýö¥w“|C€o¬a#œk™äÈêëù8Æ2/Ö±Ø5ç4¡ÓöÇZÊä’—È9¶3¦"°©ûã q)6η5ª…îTÒsÚ•VûÒ¹{<çN}îÏ¿º›Ž;V—:K#qI~áÍ ŠÄæÛ¸!/F—¦3+‡Â†!Ÿo¯MÙMò H ïªXÆK2%3_Î .qoé6\*8ÁèR–5mtiÒþXËà’„ËÅÌI¥ëow‰£×}ÎóPÇž³K²ê÷OG±Áé£wþìk;ù­£ÿŠ©ø¡“Ë£§ŽIb~"äüóÏ3~’ .¹z| Ò["~øe1IDCŸÓ¡å:Ÿ@Æ)klªaêR¬Åð”Ê{zØ´´mÃ%Çe83“ØB£øÊ²`“ö'µˆK1ö3´ôxlèÚpž€m.Å;¯4Î镨鬈=ïMW«{þLgþ÷¸ŠF yer•í&³6ãÀ\þ¦]Fû¢¼ÅŒ‡xçxh’ñïpCc[.ŸùxT/™i\ìºT•¥e¬¥q.çòtdÁUqÁámXQsÚã o¦GŽÏBI²OÚk¡ÃW”6Ssœ‚ËeýØ•é /·^iœ{SóƒØó>sqõ¾ÌÜ¥ò ¨—úu:¶~L`o(ú¦ó ²ý;ò£i:©K/ž<æ¹™™¹îÉå4ÀFLÓêdñäb¹\.Óöâò½i ÊÚ¥ç«çW+¶èéò9\ûpùÞ4@nc¼ôryBËÕøˆ;f—~ã×¾Ê?¾7 ÜÝpiµ|Á·¥9Œ½ó5Ùω_Xç •8`÷ñã9¸„û’;ïK«åù”–ç³p ã%v—žq¬'͹€K`?Ä¥Ÿÿ«ïƵI^¸ ‚îK•ฑ˵H=>÷áGß®Á% \ZÀ% \ZÌý{•@¹ßè1û8 \ZÌÉ¥b?—ô™“KÓ,pIŸãq©-‹ª <i/œuÃúÀû<Ï‹ÍS ÿ‚KúKÍ+oªÚÊlÄr 22õqiBc‹Ò–dÁJt3\Òçˆ\2ñ)–•¶OI«8È9kš,ç}5OvmB }ƒKúÜÝ¥¿»)Wê§Þ°K ûdyÕYÛ¤ä &krÞO.y3VÁ%}îîÒW¿¶ûo.½ó†]’ä/¶í; Eä0ÿœçwç}t[òüØ‹qŒpIŸ#sÉæo¼‘Á·¤È=—²Ü´ì’KÙ'à’>Ç㑼—”2¡ Ãr¬dÏ8 )çÍÓgLpIŸ¹ô|²|ó.õýÖ<¢Ìj²’µÁ¥𗞯–Ë“«´|²Õ¥ÿûŸÿü_õþÞL¹#›Y;|â퇬pIŸ¸ôxùâ|qq¶|y¾|Õ%éã?~Ý.í\Òç!÷¥çWÏçdÔu—¢HpifØÏt®<À¥“Årµz~²|z²¾/ñx铵NxÆÍ‰‡¼[ϯN–«¯¿t‚Ksâ`Ÿ{‹NpiNòo(ŸüàíîÞ‡1»|(ž&#‚KúÑßãvÐ SY¥IÙbV}¸¤Ïñ»ÄߣôËyÄ—ÅñÄìÂÒ{7™Z2†ˆXpé¿K®¡ñ¶“éke~8Œ—Åñ»TnÎhTǯ^Â%}Žß¥Þµkñ¸¤Ï \ê7\êâ?pIŸ9¸´ ¸¤\ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \ZÀ% \Zèºô¥G_yPo^_yô¥7Ý…£ã¯ýê÷9n?—¾ÿͽÄ| ø·o~ÿMwáèøñ·ÿi¯ãþÕ*Ù§….ß<IEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/rpc-invoke-phase.png0000664000175000017500000022103614770023131024276 0ustar vladimirvladimir‰PNG  IHDRŠýÙÊ? pHYsÄÄ•+!ÐIDATxœì\ËÇÏîÞ¤[¤E@PREPìîÀÛ¿ÝÊ{úìîî»û)&¢‚…Š¢`"u¹¹ûß½„¤‚OôúÞù~øèÞÝÙÙ¹»{çwfæÌÃ0€ ‚ ˆ&ÁûÕ@A$/(Ï‚ ¢q <#‚ ˆÆòŒ ‚ Ê3‚ ‚h(Ï‚ ¢q <#‚ ˆÆòŒ ‚ Ê3‚ ‚hÅ–g•J•œœLÓ4¡¦$Ê„ ‚ ÿXѤ(JGG‡ÏçëÄbËóåË—û÷ïÏÊ3I’<6¾A¤`X­”Éd¬\Nš4)00°Xç[_?|øͶ›1X7‚ ‚| ¹|ñâEqO,¶<'$$÷Aù/óîÝ»âžRly.Uªû/6Aä›dÈ¥™™YqOü§ƒÇ¬ZëééýÃLAäß„L&{ùòeöÇïhÓþ#y&báÂ…ÿ$Aù—ñäÉ“råÊý“žæÚz–H$ÿ0Aù—‘’’òsÀ™Q‚ ¢q <#‚ ˆÆòŒ ‚ Ê3‚ ‚h(Ï‚ ¢q <#‚ ˆÆòŒ ‚ Ê3‚ ‚h(Ï‚ ¢q <#‚ ˆÆòŒ ‚ Ê3‚ ‚h(Ï‚ ¢q <#‚ ˆÆòŒ ‚ Ê3‚ ‚h(Ï‚ ¢q <#‚ ˆÆòŒ ‚ Ê3‚ ‚h(Ï‚ ¢q <#‚ ˆÆòŒ ‚ Ê3‚ ‚h(Ï‚ä‚ ~u¾†"_Á×/ƒ (Ï‚䆀ØÁ5›l½÷Z&‘ªF©÷]wdU¯š¿º\ß‚ îM¯Yuf£’)Ør+díõ²ó½~u±ä;AyF~61›FO½’¦#äñù¼Â6"]cK[÷J¾Õ=l L#÷pñœù/¤BG‘dþc{§Š>5}]J±`LJÜ•+7<|—ð>U¦âö)ÖÕ71-eaemW¦¬³‹“ )Sö®qö^2ÇçñÈÌò3´R©”K$v=¦Œ«aUÄ"ý"¬þÚ~°Ë³'G–N›µïÛþ”Êä?óòw–ô_ò€Ô²wÊß&øZ¦¥m\ܽëÖ­¨›û˜ó íaõ^¼¼º¸Íˆ}l±!å§A~,(ÏÈÏÆÀ¹¢õÝs7o„]¾|?¥ðd$Åê·@×ÂcÆÎý}ª˜ç9Ji”)cýwøÕ+×½Id ËA 4-[õÁ­MËê”$É£#Ørñ…J%—+éü (Nhy(ÅUgÅž¡ýõoH ìlíµnŸ¹ò÷åÛ_Ë3 Ç3¨àíãSÅ·|©oœ­PFÖN>Öe%a;æî¿¦bâçv—¯hvëjøµ›OÒ NBò…| Œfm^1¬¾(k/ßÀÚË×ÚËìŠ`Ä>@}Ýòû€òŒülŒ}¦ú°·ûWZû‰ÝÐ2ëñj“¹4F)K{ÿúù­°Ðí;CÎ^}úáÙÕ¾>g—^Ûö¿*9_VJǼý ¿ÚxrÔÝ·Å=.‹åWÃzT0‘˥㹶î9wéaÜýCÍl‡î [à_!+›Iy±iÊÀ y'X Õ+]®~Ë€ÞÝ:կ欕• 9.êÒ…Ó»·í¿zÿÞÓ¸DIdLÀ·äYX¹iöàCßz^kÏűûü–žºÔßûGÜ¿ŸS ÕSòØÔï;·~_vã@K²í®åªO8súO]™$=éýÓû׆lÜqìÚ«Oo6opåï)§w[‹s(ñ/*6‚üXPž‘_Fª,³%xB=¥+ÐSÒ72µ(çéø¿Q‡‚›·žq f×àæ墯70) “‰*3R[WO‹ý=c³²®>Œ<»¨Gãáû”¸¸sCK“ˆÑõMsœÞ«a‡Má¯ØM«ÖÓ.ZÉR'OþzVå›vfÿ†~zzm|ßFkn¥+Šþ ¥)Ùmq¥$µèç!¤eÝk’/Ö uEB}}Cs§êM»Œºµ¹~í~wSeýÙ`Tå‡Ë›üÒ’"Èåùõ°æ:” ýVSBzm¶ÚðšýðñDÈöÁõ‡ê|µ·’¡ód£SoØæ?˜t‘:~ǦÕ}êPYUï'ôÎÐf^íÑÆçUæÜ•­XÃ×gÍUI1äùaämÉ›Tê¾wâÇñWØíG+‚¶ýõ¦KÆ‚üÆ <# eY½í†e±ô½—ÑÉé £õí“r£]¥MKêâA0·#ß&) Œ2ßù˜³«V¿Ïmé¸ìXÖÿëÚÌAð,í@++n’Á¡ooÓñWÞs›ñ!{ã»ô/ý«K„ ?”gD£1(eÀÊ3¤%¦I*V±‹›ƒ±™3+½IìÖ«wïÓdå2åY¶ï¯?ߪ·ÊWoUÛŰ9‘žÆÿí–bSÜ %„QÅòÔ› ·£Pž‘(ÏÈïOÈ£ÈïqÄåó™’®”ɳ{¿¶Î½’¹Y¾AÓff€È¬L-³ï(B(¤‰ìK7¹PG_D1 izʇçN¿ýFXÚ¥Yû¶mŒø™s‹˜ôÔT¹*ï Aò´u´³’È““$Lwe†¡t t¾X4´R’–ò"òÒÙ°›±o“¡A¯ºõkÛ›j‹Ũ äé©érUÎBhiëðsÌbh¥T’xÿbè™k÷ߦ‰…}µz «8•ÖÒíN!÷ÝÔ¡eöÖäÍ\ý¥h¥\*K{zýôñó·>(Äå«5jY§²‰?OZ®Ìé’O/£®]¿ýìebR:©mìä^µ~ƒªÖZ¼|%½yùt赨Ø4eXÚÚ¹‚W¯òf&â|•"=íSÄùãçnD}H§ÌlœjÔoPÑÞL,þЂü«@yF4𤷱6ºâï©Ê$’äÌîhƒ,z¹sÕ‡ÌãÂÊÊýÓRÕ9ÿ›½þÎÃ8¥ús‡%a­”G6oÚr:2!;ÑÔ1}ë Yµsn¿RÜdë´Ý“‡,8p4òYVÁù†Nå+Tk:oþ${±zOÚõáþ¸›ðæIäãdJÏÊ·JÀöÓslÕ‰VÎøsÑÊÝÏÓ@·´}™RzÊ”·Q1ñì!‡†›<¨©s‘ÊÎ(În¼p×í§‰ì'c[g¯š³,ò+“5¿IölÕ„‘SŒ§ÁÈÖÙÆ€÷6æþ£€oå3zòÌà^uÄ?lºSbB–‘ 6³ÌÿrðuÓ¯[½iýÖ.'Ùý— Lݵ‡usåLüñþẕÚÝcŸ©ïTÁÑT_ðùE伸T6ïÆ#WîœÝÝ GÇ óæÊÄQcæí¼¬‡òöZ¤ü}Ü“øÏ\iêîýp¶qΜé䨅ãFÍXy’}y xý(j¢ ´ìkN˜>w¬¬…‘ÁÑd>„yÁýO |«µ5ø®·5>6s‡Ö)%ÌÞ¥´lä°}CÙôI{äGf¤~v"4<xZÏñsÌðÖ“v½ëΟ÷"wÎj¬ÖfÝ ×/ygÅÆÑsŸsxîNãÎqð~šÿÌñËpØÉÊ3òëah[Éår†ah¥,éCüãÈðû7-Þtž=*tj¶ñÈþJE‰¦™Õ«…=[­{Æi?£[uÐðÎYBœû1«‰£WZ‚x)dyfEóûôi3vÇõTóæN\ú—@(¤w»¶î×\~—/vŸœ³«]îø ŒâèáïR ûŒi_jü¸­K¹ùc`7tU‡¾ ¯ç%|Ú:óʺ5Õ¾ãqæâÁž?zm‹Qoj÷ڲ£ÀDª|1ÇŒÛ éoÛcE,£$^?xUí²”­±ú˜Geaîôæ•í"Ù¸[ññ¯ž~”! M¬r{"ˆm‚gm{X6Ç$€·§‡‡¼à6*uæ‘ûE3òëÑy0ÛüOX¾ bÙÜ"t[ ÿ5Pž‘_äíŽ zû‰¬©­¬Z+ ¹BÉ~öì:oߢÁöFyy ‚ˆ2Æ`ás܃3û6Lž¿îá+nÌ‘÷7O5±ÎN©ÑYòÌÛü§À·±Ð¢¨TP‚DžḚ̈ú¢ne½çq’±{ÐÌ%í–ç )®L}lK(c1tNó/ALïoÛ¯Þ°íTר€KñµñVžÙ¦Þê³)#ë~%üiú…IÎM¦”ë¶äð¢^ùÝßÝxÏùÈÛºØjç­[Ü}KÃÎJ¸t"ª¹|íËçƒ$ù––|¸{ùĪ9“×}Ns/ Õhü¦eí‹Ñ×+]F=€QÈrM‘X7¨•?9•Ù=¡Rd?’â\¿¹ôò±CŠºþ9^MÊ¥Q‡œ_ìÕ•koR¥ìF¹Jeu¨<½?¤«¯ Üå< ®œ~ž?¢CùwòŒüzH¾¡‹G9Íu8“tòk÷3*ÎÚËŸXÔj‹ ^uvÒëœk—ÐIJ¬³w“ky›äTa‘® «7]š¬¡Ë&-ô¡¦òÔÙ^óšÜa·Þ®øãØÌ5Íô²}¸4këKè°k@Žèhiç"ÞelU(Sp䡳 {€Š'¡Šé5 °…8kæÓm~-¦(íûíß<¸ ™‡—qÏÞs=è?ísÄëºÔ»‚Š_¤g>‚Ï?òÍ åÁÙñb|Î=Zf––e;M\15 ˜ Ï/>Øÿ¤¥Éï?%¥I¤ kÁQZÄ‹ô|iÊV(¥Ï$Ê“ zµ"’¦5,kgc(Îoæ©¢Ÿ¿Lå^/Büîæõp>ñ%ö†<—fV¿cPž‘¼ <#¿±ië3a«õ3?©vwµï´í%[‰þ=²áúZO‚ŠæHÄ0úþ£GúZèÐ@ðø"=#cc#S[— eKåÓ9ƒò¥ÈЈäWìÿÖùrÓhDçMp®7ã!»yjÎÔ„fs³ú‘SV [ VÇ×±û’˜N{ùù³zKhmXÈ0»y†<˨P#ß@(Á׸=mš=L¾ðFøÆÚ±oωoÔbÆ<=µ¶á©µ…ÿÃ'i¾d.J•mööáÙý×|x{häÄ­Ötµ‚ÔÕæBȪ€ŽON¢+{ÑPž„_jÊŠÑk]ú&(Ù¶]DçÚÔŠœZ p|/öýg4Ý1ð8[g^»º;".ÈÏêÛèLìõU[6Õí²¡ÿ,ÉwbÞºo“%Ûã%txàŽW È#o!hÿ輓@Ç×!ô¡£ß$çËFMÜøŒ “Zîâü‡…A£³ÚÌn9õÛ¶pÓþá×hüÐjõÓýrš5¥L­õ)HSAüóø$‰ÊTCâóñþÉÓŒ~ë2¦E(2C«h s„˜¥ŒKÛ7è·:T˜æ×s»èÇçÏH»vikn.&AFClt\š’çuÞF¯òŒh*}NÏÙí6â !¿7mÄâÆ›†úýÀúÞ·ãì€ea;¢RègG/:re^»ožòúa¬\"¡†ôQ–Y´ áö€ƒÀùÐõ›æxFá2fVó‰©Zm|øóO*î…ÞSMhÿÞ=™Ñ´³ hZPü*Fõ¥W\ø¿ƒ‡Cœš‡'C™‘³ÖÒÚ1;£“‰ ùæ-­º{ùTl’ƒÁo2d@d)­¼°Þÿ\DŸÛ´ócÅ©¼òìw¬ÒÙ]{ûÍ4PÉÒ2Æà=Êi韓iÅ¥§ÞÎ ´‚Ê3¢¹¸Ý|ÌzÚYÎ[iÛ_ã{w­eQì% ƒgìºnß²›.Ý}u~Ç—Oû~ý”g/_¦ˆ4æGc⿴ߨƒ«_sËaïÿ(â¯_P2¾ß° .SþŠ›»>aDß<Ý·ÒWËŽ«—á— îü;ÀÂ+Õxú]ZŒÞ–®L›:vzP“ ¶YË:œª¶p´|û vPàÔ6þÁ°Œí, *”ðøÌ ÔËõŠiåÚŸ^Þž6.|h§5y¦©Éå)õ¾ŽGF„¨R“Ö¥ V$xØ³íŒÆ7ÿúM D#Иšùï‘Ý;HQp³˜4ø×¢WûÄHÚößüáð€<¯9: ’*^ëZìÜíÑ+q}ßng_KÏüQµÊ‹%›'ùÛ˜j sþ.hiZJJò‡K[§ »E‘¹ RÑ IâKá èØ$²D¾£_úNóËÆª×ÔöëzíåÆyËTïV£€¸Ój 'ܸ˶çC&a@Í>á‹Ü 3ˆå’¤3+Æì¸ø†½^µ€!í|,r–ŽÌ,A9; ¨zCƷܺ+ê<ÞÜxR÷èYu²™L[>h}åQ ¢ÙUøt$dªgi.~5C+r¹,ýã'ÆÉþˆqT¿ÅHRSS>Å?¾wírdRFz¡ƒcMÔ\Пâß»ý4êzÄóŒ9ÇðìØ–ºqeÊ:¹Vô°Öckcù›§Ñ׳֧zóàâ½ç:.eÌ2Ê,ýô*êÒ]IÆò‘Ÿàçæb¨U€L»×RÕzߥWŒwÎn…ÆV#¬{„ž“öê>øÜ“•>î7:µ¨VÚ@‡‘~ztãô¡K1ú-†ÌX2g`ŽqkåÛ˜»×£_ª°•ÏîÝ~ðªRë¬o.pۯꮡGØÍ‡³Û p:8¾ue#u«Ó}døQ¿ׄ«¯eò¨-Ü·Y”q2Õ©d©ß%ÄLÕ­>õUXð×åY™~åÎç<Èô§Jˆ99s¾“µ¥MYwg#q¡ßTò)î~XT¦ ÜÛÛß}èîXÖHK^™úüáƒ[Þp™ÒªØעߘ»Xè±Mä¡fmðê%‡´ÓcÊYͰ4%½MHUŠü§¯šÐsñ3öqÝ™=w]˺ T²¥Y]éî~myÓ†UœŒô´T’O¯‡ºò´ËŽZ{¤wŽEV(Ÿ)Wð;M¾û‰N _^«üZ«²eu…*iʇ·ñ ‰ÓÆKcOüï«÷ù‚òŒülWzÖÙ›kOê½Cûp[”Éä7'u°ÍqP+pÕæ=›Ýr݆̅Õ/¬f7<’o îQoϵœ¾Ni'×Î9™±I6y¤:îT´òPúŽ=Ç-:óÕƒ‹;¶î8vúâÆ{²C‰‘"ƒ2N5;÷¯Z­fýúµÊYj}³]þqÆàóÆåÜ÷î̆Ag6°>kî^ëSþpp×Ówg_åÊÆžîÁ¼ë¥ø-~ðdš¡Ó_"wÄïoá»ß´zÀå“;ó]ZdS±KõŠ—vFvÙýëå²­Ýïäÿð]Ëf.Û²rIÆ¥õ­\; _0zHww#~õ¿;¢¢çÂ{YŸTVŒv]1vÂùôªlRy`zNÁG²Ž&® ªµ*ÈöÐç-ÕòmÓpøÅGg¶¯\¼vÇù›1ož?dÛæ:¥]t5`@o?óoÞ¿øc]ª\ʹçSlØôQaêM“yg¯Ž¬ëPà‰ÌûŽfo²?¿\]Çs5” Š»¿ÚRø~d‹º B£³’ÊCWt]þ§d;xåû]¼ç8~@ÿµçž€âóG¹S‹ÁcÇ ï]ÑZçÈ“‹Ÿ¥ƒêéŠ1}VXûüraõÎS ]Cö:éêšeÇ3ò3°ñì>qõ¸ÁJåP®uðú=Ž­_ºtãî‹wcãb¢Ù7ÃÀÚ­Q·?öïî]Ö´o<AyF~: ¿ö¦8“LÒ퀬€v_MúúyŘgJPB-]ïf²Å(Z!Lçx5ï«%k9m—lÚ®1 áœÎùbÚývÜì·£() žÀÀ¯kðÑ®ßÊœa<D2 ;Ìk3q›|⶯]H»tã¾SØ¿¢+ÿå­ýÃÿï9“0 xÍrÐ|þ‰¨ù^Oý¢;Õ]söñš¼Ç˜%ÌÆÜ;EFÕÛögÿŠ^.¾Žuë¡sØ¿"Ÿ‚ (ÏÈϦØnÏßë'­!þÕð•’ —þl~q~öÕ¿v=MxÈ”gAÑ8PžADã@yFAåAA4”gAÑ8PžADã@yFAåAA4”gAÑ8PžADã@yFAåAA4”gAÑ8PžADã@yFAåAA4”gAÑ8PžADã@yFAåAA4”gAÑ8PžADã@yFAåAA4”gAÑ8PžADã@yFAåAA4”gAÑ8PžADã@yFAåAA4”gAÑ8PžADã@yFAåAA4”gAÑ8PžADã@yFAåAA4”gAÑ8PžADã@yFAåAA4”gAÑ8PžADã@yFAåAA4”gAÑ8JJžÓÒÒ:œššVBùÿv899Ö¬Yƒ$É_]Aä7 ¤äùáÃûkÖLoÙÒC¡P•Ð%~#$yh¨Ä××G$•Dþ÷îEnذF[[X™ÿ‹‘HdíÚøùù•DæJ¥’5O ¢$òþmb±øW—ù~T*ÕæÍ[ž<‰ù/7-$’´À@ooïŸ|Ý’’gšf*W¶1¢1€¢„.ñ‘ž.™0!¼äò¿páXLÌòöíù SrWù—ÁÖ6×®)•<:|pÍúæ–& ý}(J•ÊÜØvöÌ9|>¿„.AÓttôÃw>–Pþ¿0•*zéêê–D欕yút˜·w[ý’È_ó¡(Þ… ‡>ôï‘g¶Ñ R±µ’Rý÷_G&S2L ÖÑ$IÙÙ——B./¹‹üÛ (øø‘ý+)åˆyþTÛ6Ñ»µJA—Ð%4œô4yäÑç*]bê ©))“F³âË(ê?Ú¶£HòöÓW­zü¿A%t ==OÏjúúF%”¿†ÃãA|ü3•êÉ/¸ôÏ¿$R”¤ú#ß Å#y|ò?Û¿Í““Y²_žf]jA½rÀÿ¯Ve&OÁÑ ReÀÐP¦JWSBÌðù-Û2p9gÅgäúµÙM•ì»,ì®EÊ~·¦3 <#‚ ?Z‘$0ÕÑV¤>ˆˆPBÕVe(vg®4:润fz¯®LXEÌ*0„Ñ#[”¹¨¤©W7ýõ ¬û{•“§Ÿ{`hmFÑyc¿‘B#¾®’Ž} f®ƒ'¼¿lß‘=³í™Íwo8¼{ç~~åíyÿÒ¸¹(ÏÂ|¥b™¬ ¡úO Œ2·O=©ÞOåN̨×Îʳ|Vž”ì…䬙UP2`¥ëË ‚ jØjúÝš -òä‰ož¼‘Û œ¹±™«9ó¶¨ [†6è·É«ñ½¨èë·.…ì<2gä©þ³ÏŽ­ï¦ú ÇßDãäùâú5}Ç^ô1Ó•ž yö>ø÷òÕÏ­ÐÌíõ;êö>­Û¸{̉š‚œëbI‚lݰ÷q¼äË>-ÓµçLªãdüE¶ÓãžM·oí'ï$ µœÜËŽß±W ³°ëLw)L²´ÕÁ¿'´pÊ#ü%˹sç´µµ}||~æEA~4lnÔ~èòzfzb=#!/s‘1¹ÈÆ òå;É x®Z^ö9Q‘Ê#l-õ3ç.+ oáV•ý«4pèßCÛÔ ™·¡o£…úÊßÑ÷ëhœ<× ô°‘)a}¨õÈÞæùÉ?Åm˜¸5¨êè“ ÆïnOBÖø¾Rz鉱6ùêú©SqÕ›çwÐ2Ÿ¶yÚXÿÉzMc7=™ÙÝA õñ…ð¾m¶–;üâÑÃÞN"6)ªí±ÄtpPTêxc‚i¯Øvû=+Ï5z¸_ߘ°9ÔbhÏý‹üx™Ï]µÜ†ô§/¨6yòdssó€€€F¹ººZZZñ\‚2–Q§UÜ›=ÃOýcPÑ çz³"®?A*Íûº$@þ R@ž‰ |!PX€¡A©ä\+…îcžlIØ£… ‘<ð¹YnëˆÍœWøšJ°¿m>ò¯ÄF.ÿÁ#OkÖ¬ùüùs5ØÇQBëì" Ã<þÜÞÞþWùð Œ-ÌLD úKõÂ×võª(ˆ¼s,VÕÕ6kt©ocß}H•ëínÈõl~|¸÷£a+'Sõ¥ †µý{Õ¾ºäÙg¿pQ“g®³:™«˜• … ”B#óþËÚoß6çøšÃwûŒðÒÉR–øâê'›íSDUG>;yäIÓesTÝ\tRªŠ³½$2uǸÀ©VY³#«ö½±òA÷…•اË?yðº¼ü£a^Æ„œ{òÚ&]Gµ²d–!…+ðRÂð2m^ÕÎÞ Ÿ=Ê‘±”}BBÂÂ… —,YbeeåææÖ¥K—N:}ýD’‚¸³°h¼Iæt«”tê^6Ü! ÛgÂÅx//2 Œ²"øP¬ ‚«– ‘C¡Ix殆÷R®JÇüzCWWgü&àIaQ0DÄ«]bhÒ Zø9ðN=šÂÝDÖ\Ð3/_hÞœM@šoT[3ŒJ¡dÍ¡˜G±;Ø=J¥D’YÛ%Q$kø¨“Ñ$õå,¹B&Ï|D$E DE MË$ŠìEŒH>%Pd¦ÙÂÐJZ!S©2¬‚ŠÙS2r£•r•\=½”=E(âl!Z¥’¥+â E$Y€áÃþ¾äÊ«ÙQ©R%__ßÀÀÀÎ;“ätEç’ƒ5€ˆŸ¾Ü);j¹ã…|ƒVþ#·™9sýøõ=è̉U\;áüљϒþúk 6Û àuø¼«¥«¹6±«k¶ññé]¨ø‡%´²àËýÖh <çDý)ý2ZäyZR :ûùÏö"›úÓò9{óñ{Sƒì _‹éƾ$Û”BœÙ3Mð´„=ïXÜÊNV$[ßmìèÐÆò rs¥"a*¶h ðÜóe2YqOQ*¿¼w*•*VÍÑ£GÙ:‹éáÇ{zzÒ4ÿWÆZ–õ`~ n Ǥ0'˜I3õŒ­„ÆxÄT†q 81ËŒ‹G" Ö†s²¸6þªéÙ•/ ¦aÅtðmú.°rh§gi3p7I)„³`Ã$Xxæ\‚¦: UÁ’…Ð¥3D}†—ÀLã!ü4,_›CàTÚ8¯ƒ{á“àÞ=xšOÀQüåêšSY¡Öw€%aÑZ¨fÉÙûÃ] jÞŽàêMúÃØÎY93°&’‹}׿A†TH$’GjÖ¯_ojjú¿ÿý¯W¯^ì†@ â«AG§ ¾zåzüçT L ¼›{tlL¬z÷aT¢Œ-÷J“—¸i©h‚$Òï>\¶:*&*‰2ÕwiàfùþQèé÷ìÒ61ti[­WWZ­Ðñ·XqÿiºŽuÀ?/+û¨i™üñ‰¨S‡ž¿x™Î¬¥)°r³¬ÕÉÕ£¼."¨”„nß»õ޵EÅ–ÆÕÚx¶hgÅ£ég§nmœù(3O§¾S¼áÖÕÍËž¼zϽ‡œ•ýLx‚N Û×(W‚c=Ÿ?UÓ­[7Ö9r¤»»;ÇcmŠïΓýA¥+è‘=MÈç ©N¶xcÇŽeß[33³Œ—¶Ä.EË¥DºŒ}…T2IJº”dmÉöaßxÆæ±=ú JZ>¼sKC!©HŽ9¶¶÷ÂSi#ןkeE)TœíNAÊÚ™ÍôÒVÕTô†x!×ažmS³÷8»þv«Z_­Ì###åòb¬fÏVåo߾ͳóýû÷“Ô°­½ÆשS'mÝŽ’Ôm5½y¥3¦þñ´ÅÒ6­m™T)üoµmè¬#÷ž¤?޼}àYç¦lYìá>>ìK[£F ö®ŠD¢~ŠN ;ºñv\ÕÀ@*úà¢Öž›t,¥õ¥Îbë¥ Ý¶_np/ôäí+Ç(X…§kã1éÈÈV¶YÕŽœZ]f~úîƒK¯¬c€gdÛjóÁ®®âÌ& ɱÇö‡^ÿX¹_ 0wÏ}c]»]ûZªßuXZcå™àsÒ¨ Š—3ºíŠH£NéPNÑX#¥ÇîÕšëÆ¶†U•úµ(½zÇè…/9ækÚ¦ ò žÈ'Ùê\%‘8´l}kz]ËÌššæZo¸òGýÅGÿœy}æ¥[óuµ<=wîïênòå¥9¾j»Õ®½¬9ò&ÑysÃöŠ"§M›V,•M¼`Á‚Ë—/x´mÛ¶ƒ ª\Ù{ãÆ¥11…fR½>”Ú·×ALSÈ£§DpoH½ÀÑ$Lj køÇÂêX±NFÂíËp ºØç`þ~nXžÎùÅ÷ 7zÂŽÉÐå<˜Á—¶{Üuз‡­Áø ì¹þYòL  E[àó3‡½³3Ì;`Ý zQ £³š«êý\Û…"j´äƳ¿Â† Šõ=ØOtttaG¯©¹yó¦µ%ÁÿZŽ"]!“Ò™2¶m 2‰2]ÊóQ×næ±S‹®UðmVÙ”TªØ­PÒr.ÅÈäFͽ}W¸¶?¦E3#!‘y÷¤ŸCV¿ñÓFW"e%›PÉÏm¾qó™¸û®:Þ¥•D®v”ä9Ô÷ J—-Zxi³}éA­µdé¬e¨‡rH!?ñüåõ»”]'Wµ1IªºÚS(¥½( ×/©âF/ZFÙ6vü WÔ›vêÔ©/^5µúÍ/pÿ«W¯V­ZµnÝ:WW×öíÛ«TÅ{AÊ•Ÿ¦“ÄÊsM/·Íþåø¬ËБg{í½ldj´À× (qëš}D/Ö<ü8¸c;V™â*ÿ”Ðþä;n‹'n[«RUqìúǶ¯ÑÇ’µXèûgyìù[¬¯3ËÛf\ áñû/n¥ZNêàm!ÏíÙ¡—Jî+*:”š`ò™žŽ¡ƒûaд ”}uý»Z×ÙÔÔ&ó þY„«a[ì]mذáðáÃÝÝÝ`þ*Ò¸fÛQµ2:”Ôß,o÷«ÐR ŒÜšº5Ïzò?vyó¯[¦Z£¾ÕgþÒ3|é^d@¿l» ÿµË|w2.ôÛj3h°<3GVl-µy‡\IZº•¹7¦“«0«O]:;Ú¡gÙ«×OeŒŠ)UÙŽ,Þwu~pU*OHkÆáq=´ŽÍÜÚU<ÝÝÍ…9%\dbÞkjÿ^Sû<:{aüÈãGÂ.7a³¥v¶A^¯kÛ «‰úñòÝ£¿Öy^TLLLŠ{JÎÖ6+ ìïÇÑѱwïÞ=zôàñ2ŸÏûÊ’,"'rƒ©×áÌèQ“aþ'˜¾:Aމ†ì;}ë0ˆZƒ«6h×…õ—áÜ.h?¡^öWdöN}®¼†¶f™Î | Žo¿€2‡žaÞnx8*ˆ3²ÆZa°â-‚/ƒɵªy$l‡qư³¤åõÁÏÅ¢E‹Šû ÆŒsçÎ<;ÙGcaa8`À##£y ç¼{^pÏÇ×`¡ÓdZí¨¯~Ëz‹)Içökc„A½AV×f? ½çÕÝGœ1ÄxïÕSý²Ý(…’K-ÿœüøògpðñ¶ZÂäÈì*›ëê?zñ¡E ý?Ö<‹;¶|«´ïÚú6beþ T}¾ú¹"YE›Vr3¥é"úÜ¥¦¦&&&ý6ÅŠ¥Õ»òÍR>†µ6Ô?’â{z;êí}wé}*pæ¢ú)dÿ¢2ÍB g0Ø'«µž•€QçF’<÷Ú^{Žïxž8ËÛèÌ>£¼£¬Ù t&õóÝ=ú̪S7ºy6ªjÀ¶ž)å›»£¯’ƒm¾Ç‘sØd’’’ŠuûÆæü(Q³I¥¥åàÁƒÛµk§§§Ç|¥r)2 ]™d¾ýÕ ~-‹“Ão„ÆÊ34îí¿oquAf åÙµ¯êYÌæx“Fé —p‚$Á ´õ$fÆŠ×G—ÊÓ€Šù¤P«Å­ú쿳büÚ½uFµwÍT;F¥¤)¶çjîrõêíð]Ñ5xÐÖÈÇëjWdžÎãóÄB¾˜ ]LUè¨Ç˜ØÖ÷OuBÈ{666nÚ´i½zõüüüŠ•k~¶KZÁñ³è b\ß šBK+.´^6ŒŽÜ‚ `®k× ÜfÁƒ;›e‹ÜB*:„ŒÕ]ho?QŠ{¶¤7à˜ v¸r^•üž€­þ )æÈqÄY˜þŠ{1Þ…ø/<äv7fÛì£ñôôÌÞÏ9|uø¹ph±…uû>e­Œ^7ß`èhgQžn!•Ê´Š§³IÜåÅQ-vû誔 .¾ôhã-V1œX„,%)! ´] 4“Çhåjòù±é>3ú™;)>½ûâÒUï:í ,§#O/è'y<-]¦?Ÿ{°ñ­ù˜ÖƲ"W…mÛ¶-Þ=P3vìØ<{XÙhÖ¬YݺuÙ»meeÅÊÉ„°cß‘s.(¤$'”×’€|ïîÅRîíÅPØ[’d•;½ˆâÀ>%§ã}ßTXÕq_Lì@2ýcï%7ëúWÒýd’ 3é‹Õ9÷•~ìׯ_S3jÔh…â«æ-Rbh®ß¶t³a¯¿Zj«²5˜¨rÝ2°“äðÍê%eH3o6£w/Gn3wx•ŸyתT©rêÔ©êÕ«óÙ62ï»®¬ÆÆÔƒ‰‡àÞ`ðáÆ0˜° i®T)ïà)FZ³55z0¦tÙáOÀ±Ò?s‡+Ö”V¨ë ì¡C>v-²}ÀA‹sUsòOs¸¹žwóbF³·÷€€6ÀÞªÇRØS2¦4­¶áÝÝÝÙú«E‹B¡0ÃÁþAÐ@8ûû´¾øòà‰ˆsUÍšùå»Å7ЯägñèЃ£—<»Tå«ÒÞÝŠ¤[t1ÈÒ‚V(ØÇ«eÀËÿð>OD*•B*ÿrw,;zë’œGJ sÙ\Ý ˜AøìÂaµn³Õ¿2Ma6¤ùû¾E¢C‡#GŽd  œ®aÅrÈE‘BöµZòæiÇ%DF¶ƒ+•΄ðÔs…DĹ»'›{´3-ä§ÀãI"££ìgUdcV©´õð™\5~ôÕ¿;{Xz=Ì­^Œ—ßókc_È&MšÔ©S§Xg±ò<{öìÙÙÙ±wÛßߟµ>GšôEBþ9(Ïð9…$IV&¨Ü­av¿âÜñ'ýÇ4á>eµMUª °;:ÿñ“°;)v•µÕ§PÎ’àñ)õYJºÖÖ¹á5Gl»ÒýÄ;ö2õóÙ‹‘}kW±ÔcMHZú)aÿ†HÛ]ìy_ÊÀj? _Ö:I|õ6ú¥ä§%`[fÿ<•ªéY˜¹VV†$G¨i”«f¯//À‹(èÚŽûÈZ%l¨°átöùvk^óŠ#OlKý]*÷]Ì3Ç’•±òÛ á&u'r)0Ÿ!䌨šéqVDôŒ¡Œ=7Û¼ È(®ºcëØ;ÿ§S§N£Fb[o?2ÓÜ(U¢¦ ×WZ·ö{tzÿM5Ê ×°œŽÂ¯§5Sw œUóBÍ©³[ºyUæm:ݲþEm¶ GË¥r#v'§y³ê{/ôØÂy\Nl:\ëÖ¥l=NŠ‹OjÚö·|U0´…zÞ²f=€ú€È]‡°uñ®´:¸|9eE_X.‚ÚÚ5¦²PIáØ~.O½"ßšÏoáY4ˆk‚!Ðêöˆ¿© {ÿ䢀ÚMìÝ1h<n€dÐ.Öwåül¸ÊUàl*û!‘йηF­ŠCñg9†– Ì'Wš7æÖšq7ÝÍIƒÜá]šV¸³ëñ˘[Ƨ·+kn-ç³Ä™¡…ºæÚðèQ’‚ÊÛû JJKR*…ZÆ¥ 2at›u³‚4¦æèZ’ÏG¾âlÔ·¥¡,Oç5APA„a‡6rž‚!BR!+©pŠ Ãܾ}»äÂ’Ôõ­²/À™Iz×aÎИø—J/ó\Õ!ûµ„Ó¶ä\ÃøTä¾£ûòüf¸WŒŽO¼-å󅦫‡—«joj@}uˆ•$ó¼…<+û uL½¨[ÉÙù×ͪU«VÇŽYKÈÕÕU(,ÜËù¹hœ<;TóYê³<×¾Ì÷ݲfåEì߆\û ¡¸yPsö/ç ÖÃZµ–ÓÖU[Ð/òxEHö>Û&ßhR`Ü×ßÀþý£ï¡qbhSÜ‚oahå܇xtÂ0Ü•›½oSØvmzøX…ä ŠÏpå>Ôó/jIØv`ÈLx.€ S€’rmìE/\‡®}QdMÄRMc¨2®ß‡§ñàa^ü/ Ücg¿Íç{]ëþø.ú‡¦+•kÕøÕ–®=7p ÌuP¡ÔYá@·;!ãÿVÕhàkɨr<#¡žCýGçD~tñÔ¢sH(ñ!õ3cèaÆ£ÊL'W¥‚f¸Ð¸ÚuþhúzÐësŽÛú·®@æéuÈð•%Ä"31Щ©{¾hС¼økêßÛh+éa¬ Ô3™Û´ü™Íz­‹ XA;¿8{ëä*÷MÜ9¿£<x |ûZ昞ÿm&àel‚®E)C*G"†´5eå4 ?³™4iÒ!CŒŒŒ~ÉÕ‘¯£qòŒ” ®-Àu ˆÚ€½(³¼=X!|vvÌ¡¼yúÖœ|$¤ÇÃg 7éûùFØîUÜÀT—/B²ñzÄêŠ[)‡¸;ÀŸ†ˆpˆŽåvF\‡*`'äÒ'|i*Ý\P\>¿ƒ[aðÑ–ì/®ºKy·îÁéÐã<+ eô9ßKV§<ƒJå8yÞ¶d~PÁ´¹9ðpã1Ü ·®å+r—f«Å„ˆ¾Â]ýÑmØ#úÒ§ûê|tRãü9 ŠOñ8a#(Åç©„\Ø/õGR¦îç¤_c„ï£ûÇÂãòõRÓ*Òεe刽7u{ (Mä^Ï–á tõzúäâ¶ñ×õ&¸Ú[iQ$0rù›û/÷l}¨íS©¿>+ÉøÏõ*±NV/=B1W†õ8ƒ#g›ÏÁZÙüÃA½N=»8"üòsVfÏL9#kìܸ¹{YÔËï¨çc«94ïæ æ&£02žqàÜš/»^–+óš´’¬ÐÞó†åey2 ßÞ¶ß‚æwN=::ãLrŠ’áÌ.BÇX×¹kÝjµÍµ•*2íÓÁ÷ï†qóvŽ>ågߨ˜dô\NLü38zMÿî~ÕëW6E)9×°ˆ©þ2n8ç &IWzTøýÔ™Ç7`%àó„l›U*¥xlÿzgÇ ÞÞanS'ž x}¡Ú†ýzÙ£þ„òÔƒÏzžêh!B—€ÐñAÄYŽH Ä|w³)m(Ô¾¦*ZóôlšpDN î-ϼHÀ'Å|*ŠçùÏ(¡AeŠ­.D¬1“cùȤ™¡ ì/!_ )iê,ÊŒðÿ%%·o$´µ¸üÙâsüežÛµœ­»Øª,{­tIVð ´ÅœW k]ÉeÖ4PžÿC°oª±zNV®€·$X8€Ež¤lkØÊå‰S-† _U`çœwOùÂÓgt"²Ú/6—³Á3Ž((m yüh¹•ÕõÀ¹BÞ”âþ CÓÏJR»îšõ‡Õd«qn.M³õWà4«Î„z+M+³ÃÁÐ*ÊÔfÒ9;Z¡Ê;m‡aÌ|ÜÇWe>åæª±og_¿n$‘UWr‘º•tæ¢&ZÆ­ÆÔi3Ž«¤™Ì‹rùÐJưZ•U—ªrEáâ€Wu÷«rFÅÅKù·å'p;üR÷ôÄ‚Sa—œD í×b€9 Ú– z¹6Xå>tKŸúnÚŸ^îºóVWL™¾} Ãds•Š ¬A‚*}ÅæÓ‹Âßꈈ¡óöo°³˜ÚÑÏÏ03”]úËÛõVߘBRÌsãÀ…Ù† û Ú_{†¶vÐðHÝ(ÏòkP)ò:¤+å…u{2…båóëú¨NPh¶ùËu„QȾT]´Æ·3ŠNÅÊUïùUÏüÀÐ\PYõ0‚k•šñ5êpf ßÙsVë£qÞÜ2F%عÉÀîꎈò¬Ö3­ÛT¼2Ï l˜²Bú%Æ•@ËdùØNY-Du@ ÙOm=— 85^rÒËŹÁÀò —¬[:X$ƒÄ7÷öÎn3£¿cüŒ;ºß¼{ðÿZm+÷95dUD¯C†Ý©øý炵Ü\X~éž“ŽÖeœ¾6º¯Õ‡×‘{f·Pö‹nt¯9pË®ÖCƒœCï×Ýrú U2Hr´žUrðè¿ýd÷¸ÉA÷Ü£[\YÛÉPÁ¾À*†W~àìqÉ#·K—/ž¬Çp]Ê3‚ ÿ%TJÔhâÖ@ÍÞ¯‚Â-n!7¹¢Ð\wjÑF—Yi×üÖâÃÞàÔ4®éL«äéî^j•r 5lýùÁW".ö©ï* ¹i“iê.i†V±b†<“âR5j×ÉX+#+£§¥ËAË̽ï_óöœosð‘€jÃ%²%ç ²*HyV#&‡ ]?/¨†ã^G®+\,¿9|Â⦠cr(ÌÕU£@yFAJõP°X ÊÓ­À@æÜ{Uò›$Eó-ùm§ŒTº6&ÏSRŠd¡Aä>dí€mþ+ÌÛº¦oS)?<ºm\öZªßBœQžA’€â‹uu¹)ò¤„%‹SÁ¦yÝf¹gá‘"±žžB¸5£î²WúB]Î\ÄÔ縘[s±¦ŠÐ/­J§Þ'zÞpܸ²ç:ßWe ç½é°iv­|(5”gAäÇqfx5Ï -W)Uúåî[RÓN7w°¹”EœVñ¸%´ÓR“›5ÏïÃIðø\0´O1¡CúN’‰Ë¶ ,ú4{š1î:tñ™Ý6 ¬qÙIE_!ÿš6³çH>¼d l´r¬‹ ¤)ßêå`ïM;+až54”gA¤ÈXï!ªt˜Ô4äøáýÝ÷Ö‰êàa'IÆ,|ž±‹5²ÛsúÝk¼±§{Ž x\V˜¿9ÜÌ&m t™w©Rîý*uœsn…îôµ~%%Ïzzú/^(Æ;¡*Š›]‰Á>WíÏŸÓ~ml#¹\©R™‘dA3)AþE°õœHÈ $)¾P<)ÈHûÉóƒ¯u¶rÙŸµW/±Í¡€R'¦x¼Ìñ`‚„¨é†mI† =¦N@ðù"6¹<ǘ1¡H½†%s!൫6óñì©gü*¹Wôà @½l(P -ÊRLúcÜó²ÓfStêÙÍ3v…p 6Ìðr÷kÛgvåÒ7®L·uä´¥ç"RŽôjè*d¸Yæ C+RcÞ¦7àÖÉn*bVOwíæ1vëÌŽ¾ik6¾²A)ø]"Ý•”<³-EK˼‘"A3 ”öè»d_ߌ`h\ìX9ÈiN2ÝßëÁÉWqbÝ;÷]Rph59¤¨ nàŒ†]gd¸z±JEfè6²ëœõ]ÙCùÏfSÊe9üËøeûLÙÓŸ€ì|TŠßF›Çžÿ5ðx\yæ÷é·ùåPwÓù±0ê' Ü/Ëá¦üj¼k•’û+äç_ J9hv\í¯õÓ¿šV\¿ººßxã ¬ ÖùC’ ŽŽ%U‘¤¾€*¡u4ÿ·_mA~!(ÏÿÚ·ï–&"Iþ·“þl@(çlÝ\OËår>Ÿÿ•¯jU¹¿»U¤<èhëD‡'? ó®3õ“ IâW]:…\©CÛ”°B3 ¥*I"•òYUFä¥ñòH‰L¡yK²!?”çææ¥ÆŽù«KñSÙ¸qcÛ¶mõõõuA ¥[ç® ◹볶 ó‹G;óÒæ%êÉÚ6å¦ß~ù«Úé ·T³@*—ÿŸ½³ˆ"ûãøof{YºK@D[°E=»Û³»ãîïÙ§§wž­çÙyv{vžØ…"]ËÆÄÿÍ.¬ ÆÂ.ð>Ç3oß¼y;;ó¾ï÷â÷ŒÕL€~å×iâ¾ùz tŸ‹íÀVø|¡Qš±mÔî¨A¾ÖââÞnÞ¼@"1Ë;jþ€Ô‘¢Œ¶´Iò""þ«Qïà/å“jµZ¥2­eïöìÙ“””tåÊS–ç|eذa:t¨U«–ÓŒŽŽ~þüy¡»¥ ÆÎE‘ ÿˆîÝ»c¬!¨ê# •JÖˆc8<=k¶k׾ோå“fff …i-ÀöÇ í®]»Feì¼++«¼#}%<øý÷ßOœ8að”1…$е5;#Å,ϘB²ðîÞ½‹vþûï¿ôôt™LfìNjÈÌÌ”H$ÆÎ ¦XpçÎM›6ikÛäÒ¥K¾¾¾666†M¶àÁòŒÉ‘H¤VÁòªÅþýûuÖüòåËÇŒ)^câ´H¥R¥ÒÀëÖ¢‚m—-[VlÛ$0 Ç˲eÖ¬YMš4éׯŸÁS.`°í¾R©<~ü8–gL  ÛGÃ0Ì‘#GEŽ´ùÁƒXž1En±tÆT¼ÈÏ™3'WÈ™3g~þùçâ6µ™Îœg¢íuÖ‚~ësçÎ 0 ¸ÝRLÁcðF ¤¤$mëšZ­>}útƒ ˜xÁƒå“¦àuAKLL̵k×rÞ¿Õ”===‘#£aÀñzèÇÝ¿¿~È¥K—’““ííí ’>ó)PÙbتÿÇŸ={¦Ýß³gÏŒ3 ˜xÁƒå“2™,66ÖØ¹à˜>}ú‡ÑÑÑH³‹›<#ÓÖPcj^¾|yëÖ-ýW¯^EFFbyÆä7oÜž7ožnÿÁƒHªK–,iÀô ,Ϙ¼1‘5—æÌ™£"‰ldž nß¾]¡P ãÏÚÚÚØY3†úQ"""P'Wàš5kªWÇI1… š¦wïÞ­ò÷ߨVˆÀòŒÉÓ™÷,•Juû|>ßÒÒÒÑÑшù1"èVjhØòåË? \½zõªU« ’>ó) ;±ê¯¿þÊ‚c,Ϙ¢ŒX,6yÏ:†1›Þ(pÞóСC{÷îR»~ýzrrrHHÚ—ËåIIIųYS` J¶«þHŒs… çùÂ… …·Ë3&Ld\X.”JeqZlÀñzMš4Ñî zXjjj×®] ’,SDDD¼zõêÃð]»vayÆYLjÞ³Žâl:ƒæG‰7lš†¬…Áä ²ž U¶ddd´mÛV¥RQuäÈ‘5jðx<ôHK$š¦Ñ¾A®RÀ`yÆäIÍ{Ö^B’$ £‘RŠni¾.ŒÁä€}454h÷«V­º~ýz¼$¦èc:óžõ1Áîð‚é¨ÁÇëaëS4HMM-M,Ϙ<077—ËåÆÎEnй ÒÇàk y.¤m€˜B zŒ333 ž,z5ŠÆ“Œå“7&¨…&2×ËX䇥‹n©þÔ5 ¦bš ~ß–gLÐA•)æòlšãõ0˜¯"?”MãvѨhbyÆäD"1¬ã=Ì÷cðõžA3•¼h4 b èyËz¶ eý6°yòdoooãæ­xªˆ¾|ZXXˆÅb]wVVL!=è`1`‚J¥²È¸ÄòŒù8‰‰‰¿ÿþ»îðÎ; ±¤-Zd¼Le Çb¨Fè+‡††FFF¢_!===55U¡@³@DDD¹rå¾3}ܸ)xòãEÆÛ˜"ΨQ£ôåYKXX˜)ÔLSRRÌÍÍ #ТE ô»èU*ÚÚÚÚ~¿6c0FÁÊÊʰSŸQ-S$0A#‚åóqìííkÖ¬yþüyý@d½™B¿N~øÌ*tëÖmìØ±¹Û·ooÄ‹ç-ÅG¿~ý&L˜šáÁ•+W6vv²ËåE¦ýê¬A»ß½{wC%[œÛ$0*[þþûoÝaŸ>} ’¬B¡(2«ÍbyÆ|{{ûÐÐÐS§N¡wÉØyyO1’Aƒiå¹|ùòÎÎΆJ»UÇ$ãÇ×É3zŒ ’,zŒ‹Ì¸Q,Ϙ<øù矑éO€åƒÁ`0ŸD¥R*•PXä9ÿ` (UA6òcyÆ`0 ÆäÀòŒÁ`0ŒÉåƒÁ`0“Ë3ƒÁ`0&–g ƒÁ`L,Ï ƒÁ˜Xž1€UÀƒ‡ðîðÝ š?ð ù âsŽÌrùº@1ï?€¸xà•€Z~FÊ.€à‘|>I¬ZQÌ|µc0…,ÏC@‚•œ˜{‚àð4nþþ½]0û(Œ™ Ò M`i‡¦ÀÁ83þ³i äƒJÏ97i‘LH¨Ô •!æ6Çž»}ðPt|¢Y×¥uܪ97Ã`Š Xž1€B opµžÆ³2ÉÂå ð0.¿„rþ9 h³$8[ÿóË6"û; VŸ‡m€)žK}N^ºOÞ>tdw7æÍÒŒS€Êã—_JTBap¦ŒÁ‚,§"…ÄÉ,–gLn+–§ž,¨ f€äPÄ5Ys‡4Å Ð.pÀ‚RÔ¦ÃÁ@½·PªÄ{ó—'‘H>ˆï_.\õÚÐjPª¸Db8³½†‘} #ï¹|!\Ɔ»n‘o“e)ÚÜÓV$CµÃK(Rh›Ì˜b*Ö”1WÎ\½Ÿà:¤±§Lmà6)Ãå“›Äg0y¼Ì‰#tÍýáñyX½îÇB×_ c0¬;®s61ÏÆN‡š®ã9' ú&ü1 ¢2 ë_ÐÉ (–“Õ{aþx– s ã@èÏÅEÖvÄX¸bRAM@½.0ªHØ1æï–„_øa2LhÈÅ ß 7À[” †ŸÆCYóÔ ŠèöZ8öXÚÝg }O‚Jø<‚ ÕChº‘|+)5Å’žPÄ#X–aXµ‚k¦æ ù|I²Œ2“;$ù<$¹3XZM«UÈ2 „€/‘¨â£Î¤¸zAD|t=ôCÑ*%VrL¾ƒU¾éüy©YøV~¥’&MëÇséÚ¼$–gL!Ķü9ꎆZ- E9 UP¶t<³ËCP Òáù[X´ü-`çTÞNžk}ô,¸V†I-¡Ñ,H£4v§—Áˆ¥0d%l«  ‹[ÀFÍ)H2n…CèL\R/@Ån`áƒÃ Ã/ðßcw‚»4Ö³ Nü?íƒõû ¶-Ìj=;æmPRb¤{”ÿðéÔó®Ÿø÷%Õ,dÎ(_69aͰ—î)Ê4«Z’ˆ>âur&¿â°¾ìï½²vî½ Ü«úu˜ìg®üoÍ•Û&Øy_\ÓC¬ŽØvóðÁ7IèÇàóݪ•ißßÏQ–£kòbvŸš9ÿ…•¿Góɵƒ]È´¯7Ï¿~/JAÑ„]`ÉÎ#Ë»Ùà!1ùÒæ˜›+_ˆÛ‡øYåËP=Udigç$r–@¡Ë3&7ŒlêC[GØ{žt/s _ÁÌÿ`Ü¿ –MÀˆ9àˆÞ ¼+l…*°Íù±4(ô†tQ°uxaÕ!=HAvÛ8úˆ‚“À¡P*° ?€w1œ-®Rj*Ѥ¥rÍã”vn‚fó¡¾+ÈFއe]áú](Y¥ànKCñ,šL%ï®;.औ°¶8¿úㆧrÊ·S¥ÊíÊ__qÿ’³WÂ:ÕèP}œ8õ—ñ!=KÉX#®Õ?ðùéWaÓj{Z‘7_ñ¯`ä_­½ÄÊØwÛ‡ý;o†jÒ¯åÌu+Ö#»äOžQ!CÂÚwtД:&zé“T›Æ3—º‹”)Ûn_¼ÈlæÔÒXŸ1¹á¹,¨¸ºWÛ¦)ÈÌ|?Ü„'±x$g¼ª•Y]WYáb®¿ …£w_A•àé¸qcü§õhh ™©¬šÍ)¡$H$\¹¡ÌäŠ ’XwQ’ÇõÄñQúš/‚bÊÌ8+\MO“%F“¥\}ab3ò4SpåUVV È”s]x¦–gÌGP°0p"láÁ§*DœY;‘pJ‰žø[{`ù~PòÐ[¤‰ýùª(ÊD¸™ =*qïÝ›BðAùFÿƃDü>L*ž¨àòðûY¢éŠÎÈ4Èw5YXJɼoècY7а.açYÊ )e•Šv‡O=ŠIdhKÚ¼NÕªíÚ½ùuÝ™¾A&¼aÐÚ‹O¨“ÎýëÕ¹®™™¦"Ìíô:3îù³wþJd½û$A›s!²tå?X²jŠ6êÊëé¢îÝ$,M -ÃŽ-I J;ñÁä›1ÁÅõ_õÿ#Í)¸CeËÓ§Ǩ,ë:eœ'Ÿ“CO}÷ÀÌÙK?ˆIØ•mÝ{ÁàuD4$óääÜ©þö N)±t©ÝcÿÏÁ÷ûìs#.íäPÉ"Ú{ÖéÇ,¸N1ô›³?Mîqâ)oð”eS7.Øp¨ùØ=íB7È Ôï$”ñ·§hvôEªÄ¬TÃ+jxóÐo‹ÿ^ü$Ãoäô2Ë3æ#°X†@+GXººU3· Ï` Ô ÁÁ0>Öî„j.\wr—qy¥…j¯r@ºl)úÈhÉØËÐn„͆ãm@BC˜/|´ÚJ« `Èø©*h'¡Ê¯"”^p½ *FÄûcž¬J·+K#.ÄøÖwNݶ.¥Ú¯õ¥ ËÊ“Ÿ© Œµ0+Ãð<:•ª”õî3‘û®Ú÷ªZp02q(î ™ÈúwØî$‹l ’$ÝÊYP8»b –qð”ý«^Tï}BRqõâ¾ Ó#wN›úS·n/7¯ÿÛUÈ^ÛܽßüÓ·Æt­$ˆ¾¼ºG—ºcvý5¤üáŽÁ£g÷Þ™x°2ï͉q=·¼±Ú~ËÂ~÷Œ]ݯ_ž’Ûzæ»×Y´dVÕê¶/R¹zƒ @7–åËÌéÚcF÷eþ©ïtsÓà}|R7& ©Ðõà¦û¥[,÷)3ûOgcÃçOïÝý‡ûk’½Ï»È!èÅó/ÖogU£û°¦åi5·žµwí…Ng»Õà§™FÕË3æã ·£qØû'¬¿ Ñ twä,'Q:l:>µ¡Œ=¤§C¦"ïtP¡Î· €¨®áˆÊYÆ?~$ôiòdÎ$fôLqýˆ|3°ˆxþiíNÆ'Çd}‚K9ýóúÀß/*tVÜu÷ŸâGP*–÷ IÍ^»— !åáËR5*›ŸžvÂgm«ÊN¬Æ^g´[Ú6ÄœÖþ^hmw &'¤g¨c—ežn0qWxÄøN•ÔÛ—m+3èLëR‚Ä–íóÛØÕçýõ´gKIƒ8°w²áQ™`^yê<¥:Ôr%çµHž”’bJ¡˜s{˵NËårDõº/ø¹m<9^É#öõGTѱ®ŠÒ¤»ï¢¥{VïïûwEJʺ\¥ú¥J8Þž &\­6hÉî¯t­¬Ë8ª[d:´ÛÀjÌ¥í·£úUr‘(n_×ly=¾é4ËayÆ|’òÕÁ×– …nÓÁRȉ'Áã|¥¥‚³/L…¡5Ô°‡›`L-à)s|ÈpÉ&(Àýƒ'‘Ðô‚sÿ° 0‡j%a×_Ýl7±~‰—Sõ2–{.]YÍw©Õ±¼PÅÐ,ÁJ¬½x—’=€$©Øx5˜»Xò5÷ɰuÓ¡|ãÄ7;ž?´0¢Ôoådai)€üþ#E¥ ÂD:á0¦ KÓÈhØVi-ƒC/cß@ú«kiа„¥ZÓÄŨÀ·!AÍy¬háQ»¼ý¯£Be;;LÒ»¿Ÿ‹e®WšÇ&mùcàÞëO5G´À"d⢥š©,C©T*Hí¬÷Ï< ˆ…uÊ-ÕÌ¥Q¸“2^…Š mL.KÜu-ºÂâwQ7Tle}“\­„šCgÊŽ >tç~°k…+gÏùtíëÁBÎ"ʘ`yÆ|‰´„7 EC ÎSK G3³ F?{ˆ{ÂE»xܪ@Â=x)"¸n¶pë÷Ñã«ðļl ó(x4štÆ•ÁL ·â!å1œ¿å!Àúu…¶U¸)àɸŸ•l B)8³þ7üËA×Pè8 ýM›@Ëú`.¡4k NEwä6ç–D¨iÀæ‘IÑ_;FŒOò´ÿj»IpeÅŠC{yîtëáS¦“·Tcê² Ï¢fgÛ%Ç^¼iáVÒI@¥$Ÿ[óTT1ÀË^ÈÀãæM¸S¹zXž1y‚žÛøˆ$°p²°%d~îoÓDöGêä‡xxZ PÁb^2tد¡ŠÔw—W·±~È¡Ö]»肋!¬Z ZÔ6{öz–i:Wg°"GœK‰Œ‰Rôþ"פÜI÷cô¶ömWk5jå¤?×\­œÈøúØÉLªeË3æÓ°À·…Û,mÎ ãw™÷‡–AÙ;^ ïyÒÁI?î³uåþÞGpç¶ vîÜ_þàYá ([î} –ŽÜ_±€ÒѱåhçÖcë܈.–¦·‘MŒ&X–óB³®½ÖuêCè3Šb´}É”‚¬·´W}¢×MLŠ¥ÕÔ­9„3ÄY³0ŠBw“±,Sö—½ MS”š©²ð|0w-AÅK ^ïG’\‘Éj› MªÜ˜‚ótaãfpð/_¦ˆ½šÔýu憢Y7’‘¾¹k”d!y|mõ­”Ê­CË e!5ʆ[$O3‹µJ‰HnvEäRM‹‘öJÚ/%éÚ¾Y‰ƒ[/\¾ÿ¢e;º:w"O(àéç H"öîÎZW mÀõ¤i+šÜjñ¯ÔÌ×ùïµÃ:ttWÛ…g:`yÆ`L‡/ý*ç!‹LáObÔñ¹ÄIø~ËQ Z™ëPÿÔÜWÇ`>MÔ¡íso˜1׎ÞòüÓÊ*®K šÚÂèQÓžVp5OzvìÌ›~Ÿ:ÚBidæ®UãÕ·ägÞ ?W½ÍÂFÞÀKíb½æŸyíß^«U­ÕÔ0?©þ£Ì¦Çœ=y2T÷®î:íåZÉ΄ÀG{¾j7kxã!u-Ì$Rû.Óë• 5ç%_<òWÚ]3EÒãS3Ú9Ò±Ouõä™/ º°í´]ý råÄ\«w,_zr¸UX˜—¶ ÏtÀòŒÁ`0˜ïÁµZí6ÞÖB†èÚi¨ƒ­•«fQ"lÊò³‰‰‰Jš!x?ö±r´’‰‘:öY¼ Y†‚›‡Ü¸ÅH{G *³z³·¿ˆOS©•$W5“YùÕúmoð¯$/2³ÓÚ¸¬ ô Ù;&Æ+Ô2¾ÅR++k2ûD‹€*-ë—µahÛËÞÎÆ”À·*U¹^Ÿe5~dyB©ÔR õóâJ¾6îvs*Zpk ˜Xž1 ó=ð­=ÝͲLÏleeYHl]mÞGÔÄ ùO½PM0 kwwëO\‡Nñ°Éq‚&Xhnïdþ±H™­»»»³.>:ƒYÙ;d{ eh5Mñù|žòåʳ–£UdMgÄv6Xž1 ó-J„œxJÍÄnئñá¡,‰¸AbÊZæ'–£¥’üsönÛ†õ¯ošZc@]sSqä©–g ƒÁ|5OÍ6}ÚݯFã^[G4s§Œ«ÐdÜÚ6lò<Å‚ñ5ovÛ<­{YõÇš$Õ›z-ý*ßzÞÌ1Lsœ–g ƒÁ|5,Öµÿ·çüÿx$047ëRat뙳€Ž«wäó¸Ù”ò“Ö3iSaÕÑL‹#W˜Öˆ0Xž1 ó- …NO3v&rÂÒ‘þ%ñ@þ%ÑŒ –g ƒÁ`L,Ϙo‡I‡£‡`÷\ˆ‚ÃKÀ–€KÇ`û\çÁª]P^ »ÁìÍ kç§‚â[ÍÖLldÂÛ˜ ¥Ä"¨‚ ŸÌ;½OÁÊ3_>IJˆWÛû;º;‹ ÷0LÞ`yÆ|;¤ Zõá x(â¼ú¨Ó ¤ÿAø=nq*ôpuì{Cºø»#dip¨íÁ‰ÛÀ7…UXË—Šàñ½5£ÂV{y˜³¤H(H~µmö-ßù¥m ‘¥4uÏÉM·½—‚’FUëB€[–‘-Á³«pþ&¤©€‚«?4yŸÁƒ´pì2d  R#°†¢³1Að…<>/Ë«0CQ*÷Ýx"¾ [ki¥þÂÆ}‚ˆøb)׺ÍPŒZ™uŸ)ò´‚ËÒŒJùþþe}¤ig–R19\£Å|‰ò@«Õ¦ä›S8a2c/‡ïÞóÛ°pÿ¾‡ÿZiKfÜ¿¼e÷ïýRåVíŒ(/}{nÓÄiËÖ‰;Ü ŸRþ{zÇRîí_ºlÊî‹ ‹.Å¢Þ1,Ϙܤ¾€Ñ}h?·^:ì_;þ×ÒßÂÞsÐk È0k2l¼ ûƒ<­*ôn$=‚¸ p²‡ð]°pÌÜ M]¸•ž7ÿ Γ ±Dƒ`ëz(e 1Ÿ ÓÎÁ IÐÈ¢¯À”•Vã}‚èͲ’ÁΠßBp7ã–Ê(*=:.~çœ Ϙ²*¶iå.úŽ^m AJ]ê·ªÜ9쪈«E|YåÆý˜C¿¼o®ésmÔºÇú]ë2D¼ïì³ hרƉ=O®Þ1,Ϙ Óùð<¸áWFOÍ= ½†Â6ͺÎR'=\A@B°',: ¯§ƒG^ ²ØU†î!3š†Û ÆO„›UB»ß T(°ïf«¹ežKÛBâY»L†nuA¡'¨þ<Ç»ÉÄÁØÐy 4.*ÚD',~5Èn¦R6öØ{˱ÌO›:»™³¯ÿ÷Û¾TèiÏ£”O.É[,îXÕCðfïáIãÏ»oiäë$È;Í¥y¥ŠÚTH¹¹nÑ…9RÇþQÉRÀÄG¦8u¬;ìxõäíOÿÅšÿ2ÆÔÌ«×þœö¨ÖÔs›Úi©Çf~¥Ò—gRDªÕ¢z³6/CÊ3±õŒù4×í’Õ?¥YnNÛE’ﻥXMwúˆ÷½c\¸Þ;ÍjJ#t¢våh®“K××F‚®×…ëÿÒ+ Í…ˆì4†aY½®0ÍJV›»ËÌÔÀòŒÑƒžö„CQœ6s.2èæÍ=ý3ˆ¿»¶"3 n<å<ë~Q MpµWŠâz©I1”©'À#”›5lý TÀÊ!C㩽Náç¹ëÖ í"Kj!ü<øTVbL:LînS «×ô]d ùläª çcEgT±Pr9áX£tãd†¡•ZüÜ0w’U3vU}¤þ.òƒ/g{›JÕd|p/]ó'6qòüçžùûI<–¶ +.éè)åE=Íd]%tæée÷d­ëun`¡JU)©òcÅW¼ì¡(ƒé–ß-Ñ«fÝRÖfÌg@Uü̘›öoºÿ:ž¡[`ýÆ}Ü$ªô¨ã»–ßzŒ„ÑʳA§Îmm¿,A:5bÛ² ·_&Ьýë5ïìiC1H­UwŽ®<~ón¦šbÄ%ê·êìí¨[‘îZ}0ürŠRÍÙxVú±kÃ@]j’olÙr,\.*Q·óØ`{¾É¶ucyÆä)e@)è×4E"à áðÏ0#VÍOsX÷nÆ~}êpzÒhˆ=-&C«©0°6(ß®mš ¤È¹dY@[‰$»O`ÕPx™s`à©LºæûUŒâÆ 9OdéîÎg5úÂj¾UXs`U,OÈK¾±ö—É*’d•ò/gY–¢†$9eRR£!aSwþyýÎS¥Hoå4WD$›ñîî; 0ãú›ã'uu÷"«¼ãgìzìš™ÿ˜22TæÓMÀ ’ïÔ³­uÏÓ~®ËOºsf£ý6-ÜK¤¿»I ý»¹âþ”õ;Üž\Ðü‹zÇž\å×jX×#ñôæ1Ö®˜´ûF;O3>?n÷üù¾s.6÷¶x´5¬ï€K·îò· Dª½cÊüvÝvòü5ÜÍ^_˜3rë‘NMtKÇsO´ƒübøýKÿ ²1]m,Ϙ\R°ˆK×Nʆ~ Ì?ÍÇC)nvÓ7Ž£d@®YÆI›÷@¦9ôo ²tàI²·HI¸ߥi¹üà¢ü”ì“3 ó$øeÌíÂ÷E†Q1,72ŒÏê%¸¶;’}¶îÔìÕqV¶oT^ªŠ‰Óâü×§Î"[ýbCÐiI+ú¦:†ý6×SÆO›W{G”6†J*✳°¸5ëYnÊÜ••>UìâŽF†_+Õ,ؼèŒÅÃ’§Ú?·í£ÀÙ×5%T² ÎCFï~Â=12çбcš;ÙKø¼J=Î…¯Ã4÷ÈkK}•!šùffBíju]<¦OXÝtçp‘ÒºëœSÞ•ìyjj>B¶¨÷£¤Ô{q쉩“Ž>m2ãZËòV %ÕëYÿñ]]Y†žhÅ˃ãþ·ýÇUgª9ÁÇÖL7!°p–½¯”òÚ$pò *ÒTJù7ÕõI’NL”ƒÈÁZ¬Œ~øHn1(ÌŽNUÊ…ê,¡eYRfað:æ¹GÛ¡£Í+öïér$e×þWËî óâÑX¡1žââ¿W öèBÛ¸EƒÄgt®wL$•=9:{ñµHžÜ ¬ë—öŽQJTy¤( 6~•ìßø(sxE‰Ôš½¶hÒ¤8¥”ÒAÄK°Š —n¸×©i¥íS J ïçÅã–£BÙá«“ïüÒ¯_ÉÙOZ¹@ºÑ=„ç–gLšÿ@ƒfðÓ(eêTPJ N4°„[ÀßTÏa÷Õ¯HS™4'ð{ÀÁw°r÷®Vñ†ÍWá×9ÐÔNì‚Tˆ³,x4ƒ!;aÉb í댄O!¤M–~S4(Iè>NÔ‚™ë`ß$(D³?Í ƒÃlö\yíÀ›2I½Ú>ŸÏƒôt¹¾ÙkOÀÜßöœm¼Ü¤l"ŸeºœæœÈ¼‡±m¥ÊľµÞ4­ãÄcôíZÛŠX†_»gð•ÑçþžúdæŸÞ¤7qc>›(é=A@«äiŠ4+[³÷¯H$ઠ\Ý›C¿TÆœþuÎ{'›—·¤‚Ë3&7.•`ýfؾ Â÷Àix”†–m¸]#×ÙA `ÒHØrVþ¥lá© „ðaÃZhÚ öÃIBË¡uÝ¡‚=LŸ çÎÁÈþ ¤Á®lÞ þ¶ ¦ þ('„s·áD2´›¾ËàÄR°aŽ0h#í€}á°e-,!¸ˆ_ÃɇP?ÎnŸ^ |>a`sWÃàþ`^øÇo³cÕ¬Fû™;6š}ÝÅ»¤™XÌ#¥Ö¡Ý¼ªÖr¹tóáü¡DH™œJúÉ•hW°Œ{‚ö!ævLŒ…%•y/! înt”£›Ldk%%2®{*°É^ž¾kÞ|pˆ» 0"ê7ÿ™pæyu[>¨ÔtZlBäóŒ wqµAu£þ¸8­Õž µm,"‰À«¢‹Tñ. ™ß‘ ™Ž>Žå½$Gÿ ߸AÔª…“¥˜,ì·cp3w;€·iòܽcÉ»–œËl=±—«ïÛ½ 1Tº"ÀÛU¤Øvð4Øéýƒ©¿²)°ZDF§@•<"0^nô›Yé¿·ýiÊøåô &M»w Ë3&74V>0h¼fÒ‚fR2©Ì<`Ÿ\C7wP¥mÖdnŠ‚f‚’p¢Ôj“uˆ¢Q,xW‚RU ¯¦…½–(qí;£âA§1Ð…àA'Vš }€k¿¢ P¥g1kOá.ÇÂø™\~P68Od0eŽ&' wJÑ Jɯ:2¬TÛ”Ä$ÍO$°t´«Y×&Ç”.™œNó¤bw³­Òh¾X*t+?º^)@$OhWÞkÈBðùBº¡Îa5)'ONRªiÖÆµ\Ån–öB–fH±yë9­B¢‘ù̳v·lÔÔ76‘ˆ¹îó’%z̶m“!ÏäºÁ%R{'aJ4Û{acà Pª-Q¯,²› Ÿ$ŠÆmÇô‚‹ªüPíþ7¾OS7ôš3š™T×MCÉ0Œ¦wL‚žÓ¯H6Ûƒ «J~rû®¬ù,oBν÷´‚Éê“d÷މª–÷'¶:°ír«ÑÁü½c >µ*›{ 'ü|`Å´5ÏÖ>[¯¤…áo‚áÀòŒùH2?Ÿ‹õ]EéFUè¯dÎÒœšêÃhüq~ý•Û™œý@ÌéäXæ=gNŠ ,Ë’VnÖVn9‚oçmg—`æïè¨Ýs2׋'sµé§E…ÖnèO/¥lSBlcîe“}®¥.–aæRs©~:%mÞñí½íì¿á›aŠ %j:víá½5|>´? &ã-kU%´zã&ŽÔÖ<„=™7á{¯ÝÖ.S ¡é6K©2 Z b•¬BÍ)©R¡¦dA …DJJT\Šd¼>¸¤ÃQyÓF‡¡zfuÿ²Û®þ=þ× &ž©ç­N‘23]E;¸þ0y̾ÍsV7èóvl›ºUd<Õ›WòZíÚªU*5wEžY›;÷Ö¬÷ûª••'•‰E&ëªË3ƒÁ` †}@Ï•›J9¾çñ£/I©KÉàúJÒjñ5g¬·îˆ¼uÍ·z÷ß&V=p#âÈÆ¿<¬ØW#[ñ%'Žì©Û¤eòÕõwÜzuvãEýÇ¢EÏ2vÕ¦Ï:qíê©Å³v CÜÚsÄîݽ,j j Y>Ýúï«Q7#«ü8ótȶ)·­¹êö[u[‹žË_]}òú­+çöðÍÝ‚jt•½;ù ;K禑G÷µi.x•ÒuXrfÔö§;wllfª½cXž1 c0ÌÝC:ô Éá Œ±S¾£ëY®Ájú×ÏöÿEd EÑ tP…ï)¹ù‡º— m£ç€,«wŒtiôão5ý_èÏøŠVÚ%y •ü²¡ëgÂuO¿áM²:ËÐU|Û ÛF{ÆTµ°>>eÊ”Ñ 6Rwdó²Aûùðu1¦N÷î·nÝJQ› ¡PBQFîã–¨äÑ­[÷»"–g Æh¤¦¦ÆÄļ}û6::í$%%©4¨5 GGGjÕªuêÔÉÓәņº´­­m} ºtõ¨¨¨Ξ=‹ò&Ô ÐV$¡Ì¸h@ʶX°‹Œ‹b –g ¦€ˆ¿{÷î;wAŒÌÖçÏŸ#ÅõÎ&$$ÄÉÉI¤ł̞µ†òåËëB …R©Ôn333Q†‘‘}æÌ™'OžDFF¢p•ÝAAAh[ºté‚Ì-SäÁòŒÁdï*5 ÝB†oll¬VÏHÏÐ>R_­-Ò¦M›©S§¢Ccg9Ä,--µ‡~~~¹"ܺu Õ6®]»¶víZTá°±±ñÑÃÝÝ]›‚®ÂQà߃)Ä`yÆ`¾¤ÁQ^¼xñòå˘˜§k F[¤jÎÎÎÕªUCbŒvajì,žrt‡¨R¢m¥GÛû÷ï#;[ÛP¯ÊÆÖÖÖËËËÓÓÓÃÃme2ÃLcÃ`Š$Xž1˜¼IOO¿­‹h{ïÞ=;;;ÿlZ¶léííTY;„J;–Š(~NÈÐðÔ  É5ÒíÕ«WèÖ]¹reݺuhçõë×èî•/_i| ãeƒ1-°KÎ_0¦è‚~âJt!r¹þ\;…ýèÑ£kÖ¬Az,ÌF;…]ß›)žý…)°ŸÏ—iÐ"åÖ£õQ³zõj´“””äåå¥ófŠâ[[[k«˜Úºf¯Œ)`yÆ|#r¹<*$½È2ÖyµDå*àP åìì\»víŽ;¢ëf ¦°ã¬¡Q£Fºøøxm Ú9r$==]ß¡)zJ”(¡ófêêêjÄÌc Xž1_DJJŠÎ¥å­[·|¸±ó‹Á`¾d+hèÒ¥‹.ðùóç‘‘‘?Þ²eË“'O´ã1uËºØæÍ,t`y.|¼xñBëÒm>|ˆ*Ñnnnºø¥K—Ö_ƒÒÂ`Š$^të}éFh‘ Zûëׯ‘µ­õfDZçÊTÛueÜüc>–gÓB7à“a­WKT;Ö*±v‡jÐÚæ¬^½z¡-^m ƒÁ€fº¶ÖzþT„ØØØ[·nݾ}{åÊ•¯^½òññÑN¾(«ÁÞÞ^ò^<Ƹ`y62III1z¤¤¤è»´DòììììééŠÄØÃÃC,;Ë ¦PâèèØPƒ.$!!Aë·àÉ“'Ç—Ëå@ç½áää¤ueжxºvƒå¹@A|çÎíø,T‡ŽŽÖúB[ooï:uê Ú+z7tΆðàL “h»¥+V¬¨=dYVëÇTëû/##ãùóçH¹Ož<‰¶?&"0›   R¥J7ÿE,φ™¼J=kýY>{ö =ܨ–Šì`­WËüíྠc: õkÐ…øûûëGP«ÕZ·ù/^\±bÅýû÷íììitm‘™áææ¦³.Ð^]û{Àòüí¤§§ë{µŒ‹‹hÐ.tÃç󵳇CCC»té‚vðB7 ¦PƒÊ·Št!ÈÔÖy3½}ûö©S§TÙhûé<5hšJ¥R#æ¿påùKILLÔ÷jùðáCwwwÿl:w<$ÉØ«%ƒ)> Y;€\Bç™.ÈȾpáÂÊ•+ïÝ»‡T<00ÐÏÏoÕªUxèÙçÁòœƒwïÞ!#X;'AçÕR;-Ù¾Z%îСÃÔ©Sñ( ƒù\ÆI9 ú¢¢¢…ƒµ9O°<çàøñãÏž=Ózµtss ÒÀë¹b0ŒAðÐ`ì\°<ç k×®ÆÎƒÁ`0Xž1 ƒ1=°áÉBcçSÌÁòlLîX¶jÿå$%iç]¹WÿÞ¢««'Í™÷ä®p@¯^îÆê$S½ÝµtÉ™G @0^µ‡îâʇ¸gÏûmýÕ¨èñ}wÙû¶êRñúÁÿæV¥* Èv}uïêmÇ^$ÈY¾¹WåÆC{ü`-Îå@ ÿž€[ïxàmË¡ƒ°æ¨aÚ}«ÕÜ öü{Þ) ¡lyh©ñ¾~þ9Oùðë:°qs$1P¿™Á£ëpì(x0¨È>¸D«@}ÚÁ›°q\Q€` œ° má›–æR«ÕGŽ9~ü¸B¡X±bÅ·$ùz^Úz¯\»¦®…·bÛÙ9 K¤>:Ñwäò-ë[;å”»ß'Ã}˜ßè8˜} Bý¡nu8tèê0¡КNìˆ;È4‡As,‚g?Á¨’P­6Ü> oʘVÀ‹¡Ó (Q›“çRAp鬺=4òüá%ì?Ý(àê õd°M mZAˆ9|½ý|íÚµ¹sçîß¿_ëì·Zµj_æÛ ÞL™4Ámc§¦Ÿô¤Ç™£¯æöä§?8ýc/´[¶á°Å ó<á[¸±uäâŒAŸ“ç"p'1†˳±HX¾áðs‹ÒVB°êñû¦'12Ÿ/E6.)²´²’i" ­+Ï[âæ„TײN¥êkmx]üdRôÃñÌÌ---ÍP4©ˆ¥M7óEø°™[KŒ¿7³Mth]©EßÞ±ÒÜCÆÞÂÎèZì$œi2ð„À#€jv4±ªÖ„PsP*Ì%pá'Ï|’3‚IÈ$\1"«Ä@B^öáÇ.ñy$|®è‘ˆAö¥†óÛ·oŸ={¶oß¾ 6 }ý°Ë@ƒ{lnË®c#Ã&¯¬šúËœÃ?m?ÝÃûõ¸ÁÃOGóŪY¥Þÿ,îsö—~ûn<ã·«r­Zã_[{ÿÚ·×å Ñ‘‡æ’‘;¶°ý~…ÇìÉ÷ö÷íÞbcb•Zï^ò‡°ÜPëÇsÖ]‚!¿ýüd÷šÓ/‚m8·¸»$§ôdÆ\:zÜ™—´ä)¤ÏÏót­ìqùï:õ‡Ü´öoê+‹xô*…uýyåÖ M|P|:ùî´!ÿ3Håù´Y¿zrʦÁ¡ý–»ý±÷§´ ½§_=šºqÀ°…7íÍž>Šm3w÷œv¥ß]ÝÙ¯ýÜ—±²¦ÕjèG­Ÿ¼æ|µ—ìw”"»å×aó¾6“@R† ÙÐß§ô¬Î‹¿ûSǺóN%ôœ9;ñß'®GztXðßšáÖ¹¿µqô ¹ç„âĤÌþs÷Ö|³dÀ/‡ŸÚDÿ;ðæS=Ô ¥*éÉ‹ðֶͮ݋ÊÙdlý)ï;é|kk׳cæò4³ÂÊ[ëàã€)P°< «°jŽó—LlÑ÷Ý€Þ½[W÷.û±å¬xB«¨³s¦½Ë È®1y¥ûòñ…{I¼)Ýüti´éÖ÷ƒõ9l¡<vla}h^Ž\˜êƒ´Ìض¦‚˜†¤LpÈóây]Â@DGGTktOÌÌÌ>S–šß0GµÇÒM¶Ÿ^êkGŸÕ¾áðåÚÍw¨ÒnnIÇ·¾³ÃÒ–OÖÞy¥}\þ›Ø¢×j»Ë¯.”CJÄæ µ›‰+FO ˜;»Û¼Ê+¼ªu[8|ðÛ+«j†Î:÷gÿ–.zƒòdÏîóÛ]f'T…ôWá×ßðÊ÷ücäÖù›ê.:3î.B¢¤ñ”éíp­¼áÀÀ9?–iÿóÔÅk?w'… œ=zôÝfgŸN,샥¥ÐxºFQ˳±àýð×™ÝVÝûÏšÑfõ 3·°]gþmäÛjÜÓ͡ízWOo*oǬÚòÒüR‘«sW‹÷®@ðá¯,€e3aêX¿ Öí€FðgËÜÏ#‡¾c D'˜ÞˆT¸zÿ+¾û‰KH cÑÊåòk×®!…F;ŸŠÿÏ?ÿPר––¦Ýч¦éÔÔÔ\H¨PdµZ+ŽÂ?¼Jzzú‡‘†IIIù02ø—¿EÂÿÑ•FQ-A$ÊÝIJ÷¡ûwmýã£)›››(½¨J¡[Ðe~îܹ^]¥Š–”ð¯Q©, ?êtÏcéU…rç lÊ–uZ¸ç>„Y³Àª ÐŒPQlö%Eë¥Ã€¥}åÊþþå€>E¡GÓÙÝÁR%ýªxÀâ„\Dù±y§T]ûÕÓ>˜»¶´þ_ÿ­{c÷rVªY¡ÔÌLDϼaŸv3ÿݼõU·‘±ó÷ÄùOlÆ —dÍ*z­>ð»ªU”U™êþ¥=üKkZ•k¶ÔæM"µ†Ì$ô¡ø*5,•©DõtO¨ìªÙ“ùïÚÕÞZNs·,ýBjø+.º>qU PÑ@’ŽîNf ð«æ ‰ ªœï¦€k Ú½hQ¿UCÜkÕq×Ümš¥Õʬ6õé­Ý³v’€R!G¥Ô(•ÏßIžÈÌîÙ‚QGºîhìå?hØç:Lá˳1oýÛÞÖ¿)®ìœ?dàø><Ú2.ÇçŠÃc7%´6 È•¼ŠÜgÂà‘Z8K!ùQŽŸÕB5̘SSa瘵T‚¶9ËýØËðŸ9Œ«ôí]]^¢C‰oM+>>>Ë—/_¶lÙ¾}ûvíÚuýúõäŠãëë»dɃ\.¿ÉÌÌü0Ùý*Õ‡ ðÑ*ª¦äª% @TÿÈÈÈø|Ê(Î×å5éeÐáë't»bê ­Ù½† CÉÙ¼ÏüzÞ=Ž¡™Ô<ë4w²ˆ{•àœ#š…£=â^&"úÀë=ÓºGY³ L…õ½€Î™5&eãäá;P®®¶oož¶úç2ÿV­Z;e ÄÖR‹øG1_”y²ú®}ã;›å([¥ÃØùŒ«á•£öêôò‘ÓwK}J9˜¥^Sˆ<¾ì-£€ÿËÜw‡Lëìo&ðýaü̹Û•ƽ8E,ÏÆ"aîÔSý¦¶·qÕv¿,º²ÒáŠáL!`yYF­B%.CSš×O,òu Ð,‹*Ù¤ÿ‘ŸÏÕ«r+ø{Ò¶ßö|fyXøó ü\й#\=Ñr ­´WÏŠB©ÐmB õÊTöAv™Àp‡êl!hN'>q‰ÏÃjìòKÛêµÒðîÝ»çÏŸïØ±cÅŠ:÷«…Çx|´…üSÍæŽŽ†ì&øjÌl¥À«Ûó÷UCËè”QçsFÒ _úþLÖ®æ@)•ÙSeR ÒbûZÉI ¸¸Yƒ8Å À¿õÔ¦W~ÿ©üMޏ÷æ X±÷Ö•0/Ñ¥¿î¾›£ÖûA;ƒ…!|™‘ÝB«ÓU2÷/šo…nA‰3N×ù*êÁÂ^õºLwz´føûU‘?që‡3Oæ†D½X¾‰úx'÷äŽK5ß|84úåó“³ü±cs÷'‘m?z.¦ÐƒåÙX1G&u‘Y¯XSœ|éÏÅ/Û©gÆk`ˆZ3aCÝFn–ÕëÕêé Ó×Lšío{eÍö3U3Ô\³±Ä«¦PýǵÔ5‚Bå*Éx¦œR3<©wíéÚ·ù£›g½s“7u‘ñãÞÄ•oÕ.È.g×éþ PɪÙËëpE ½€½3ìßg%`aåÀíøstö‡{wà]:8RYãF½àñpÐ%–»aÓZ k"sŽ>äÔZþ‰K|§’ ¾OãîþPâKGo;hž3gÎÉ“'ÿøãK—.}Ø2Œù6Ý!­ÖÚ>¾/¾¿¿­`š%x«ReÕ‡D2QZFªBŰƒC˜¯€KqpG ¥¾ñɬ¯ÙÓ?þ¶0ú¤?¹pòjLRFøÞã…U€íÄsOuœÛ¸åÝZe]¤æ.ú ªmë\>@²d@iׯýúõôí0X¶àÿì\KÆŸÓIw ˜ˆˆ‚‰-&a ""¢"bw€Š-vww"¶ØÝ­ ! Ý'¿ÝC+ ^õ»zÝÿ;³³3³sÎî3ïäGÇÏͬkãîsÂê=öØËJéèÅ7ˆV9qâª^_[µœÏ§/>CfÂåËO»õ6¼~6<)Ž]lçÚZ­¸õD5pÿš¾þ+¦0_ë2SOxæ±ô¤G•‚siïî­]Á'‡‰è³ø¸·!á§<~ïŠ³Ž³»tþØÆÊX Ômç1Ô"öÜ“·xwñÈ Ë.ÖV¦|‹Á™}ökkS‡þæ%R?ï9~ß·«•QûΜ ¥c|šT3eÄÝEZÂÃáví{oØîä7zÄŒæfJonœ8¬]ÖZ™°×/^ziÖµ ÷U¿uæä[ä^5—°.{¹Ö×U+:¥¤W£c—nª i/·áúzëÜñëyŸ»Õ3>9C"“«¤£'d¨vØsý”ÁUQ!uŸ¦Ütï“gñ)Ù4Ž’¦’ÿÈÉt¾&á/lôòíð 1ÃæÐiö.þ2ŽP¨A˜àÛUGÏÅħ¦«C?]}]²žËâ[¸o}ã$e ÔY ^½Î×Þ¸2x*Ê%»eí߬›–-&,â½=õt5ˆ;k9êXdï„<9[SÏP5ä€íèÄ<CE[w¤KßD1WKÿfI²…êJM7&'§ËˆéîmÜlðóòGAA¡€’ç¿I6RDÐúr7ÂCŽÑ0®ò¶?ø)¨Úbó\©æOÊPt ô ~ö|l)ÞÄÀ¬{wy›`“7_ QË^üsX†˜±wÖþcOäéy}§_³å¢¥Í¿JžÿŽùãu7vù™qf?Eï)¸vè7úegãÛ{bW޼( …ý¡ô“"ÌçÝ"8GãþÒo‡¹Æ©¨ù­¥Êÿr²àÃêVÎó<ö'#;, ô ~²21>!S±·}~þ¿n¤•å 6yR¨è"¿®ìTĆXŽºzà”odç¦ l?®ãõ+hä§KƒL„x"Ñ\µ’ZqKòO~ w‚žnA&åR$Æ!-‡\'ø¾ µõC‚b÷‘PÒfùÒ–• 5[x²A‘d òdk#9‘¼kŸL…MéG²x‰ª@S 4 Y,Y¹à¨@O‹lOOÄç4Hedééê‚ûUS‘ɤOHÍ&¿ 0häâSr$`°¡¥¥2š¿Sg÷™¬Ý}€=e8SPü*(yþȼ„žxŸµѯ988<#.ãÐ&Ø7-»6/ Ç7 x| Û£Ðg̹83Ó q'¨Ñp% ¬&س äòúrø¶†Ð &B\>ˆ´–¸´Ò÷ÔiÖh¦¨Ëxc…sóÿ'ÎؽUÌá逕C±;mš"ñ9"Þcuš2ñð$:E­–hi†»Ðd– -»!ŠÃ†™˜»Cü!Á£¸‰N„Qx×¹˜ps;’?Þ†çT4k v:6ìE»X7àËéIèR Ú~˜8mÌð1¼aØ5ù8º5#tj¹–ñç§8z„Piìß}S t@„™}q‘‰6–ȉÁþ³è73\I |~ #7 uSаê ú-ÂBÅÞ%gfcæu´oI,6>EäÜ9‚»9`½Â–°õRìSX&rÄG!´/%ãÒ XÅ`º7¶_Å”¹dÙÞ»ˆ[ X¸þ]±‘Za»ÐJ ô\\X~k°åú·Æå%·V] Ë­óȪ‰ýðÅBÐ41f÷Ä1C,‰N-ÁÉF€#Þê¡q5Ü;ƒxl;“Ò—È^žÞð,ÕÀÕ˸œìSPPü8”<ÿaXôÆÄã²yßžxª³Ýš–{ _.¼v®Û™ôÉ<îS0ä–ô$Év¨ÕεpƇtšÛž¤ÑÖD­½ðp’÷a×G$ßÙšŽUûÈ`ÕÚ¡k3ìÏÁÔÉäÏèã2 ߇ýàX ²tjŽž£¿v}aHÈssÌ›Š´—8ÿò²Ý× C=Hyv‰v„ÊÆsÌyŽy§PSìdØXbëBLë lŒù+`U‹¼0þÖÍÀÜøb£€ãa¹ [ÝÈcYŽ@’+ÎWÜcUø [w8×G™Ú¢w;lˆÂ„i~s:f¼À³+¨­Ø£Ày¬ýÈݹºÔ€F,_ sÅ^‡/c[¦8AU‚±Ó1ðÆt"’‡ñò®[;C}:80½âe¯h0©‡^ °èè2(™£Ÿ¶_GoÔf€A‡‡Öœ€Og8à\B¯‚[WÑX-‹ §%¤6çD çXØmÄJw2Êô^°i‰Î5pk\Étðú<Þ4ÆÃ%ŠŸpeÖfáÅr !¶‡jSŒX„þ%3'ÿððEZ&êØÖ®ð(((~Jžÿ<<†Ão,ÇÐ¥ çàêKŒ üÖ5rŇ¼ÀµoD2ØÛ8Õ›¢®î¯AŠiL7kާ7!¡áU4y6G¾´OÚ_[êbØàÂXåÅ)¬ÝY‘¸º2Î!“Ô 6B.T,1Ðò›Ù—Gj3A‹úÈyJ6±r§.ªrHyV3@Þ[ܸ.I¹@D%âa2p~!ÂLpÔ­À'/'ïê?®_˜¹ŒðÂÛ@9ò\”™¢[\¼u¡_¸›PCo°‚q”gý½"3Ãç"MdCL„ ƒ»sÚUƒIU V:ºJ ÿÂA#o6¿¤)qk™Š^vÆ Àª‹X¶-F“íù§î @‘Ö™cH‘À¡{AÊæ°$®Z„qÈßk“ÉBlÇš[ÚL°|= Úàé¼§ƒ› ®ﯓ·S¢…[›ø™(rUƒ_Ó+þç"ŽÞÌd¥`¥øâàåÅšrç`à„!ëã†d= æ—>õú€K¾1?‡×Uù¡úÅHX9r”ßÝ>GÒBº;8Å?‚’ç?zcŒ¯ƒY„Ñ“½×HÒ€Iy»ñ•ÃÛÏä§® Ð͆÷SBÈÜ^tƒŽƒÐ´*>§œoæŠ1ÞËtÑ¢)Ãî«fÍ—É Ëñ9†ü$ðœŽaꤎþRÅ(¯"S[^Âqr*"à?BEç+­8ñú93× qj@˜úÈßW’8À"‘Qñ°g?,­+Ÿ<ʃ6¬¢INš¤È}L&wú“V~ Y¤Y’ÂLÒz.`µ u¡o&õ*'òïGŠâ™ÆU¢OÎìÃ{¨>B¬:ÌF&‘Ÿ†E³ Í†<•,mÅ寢×:ÜMÛ¨­h‡ Šøyh¹ˆ.øîVízÕ/:MäÙy"â.y|jw“Ò0TœF/0a7þ¹á*šu/¯~[6­.cL…–eÿà…éº_=øN®OÈð ê¨Î°pávfê+Ο¥ ’ÓóæûM«Íû·3R9þ¬Â¥(`t(¦µÁÕ·PŸs_ð¿sWQΕ¢è½+’ƒN‡*0Ð 91eiõj¥)Îb¦‚1k0j)ή%eÆý .ÜBíÒÚx 0¸hç Ë/½XY20lÜöÀ³7é:?gS‹O÷tO×Ï£i#t^†?Ò“Î&ýõš¡oÇ”"DeF$%GÒ¨oi¯ 8ÅcäbLºˆ­Hï½<.¼¨®9 úü0Ù²˜RÖÖÅ?ÎÂ¥Øå†ˆ—È›€“ TèCþ·œ#.¢-’‘ªœÿj¢Éñ‚ȇðÒ†ÇtÄìV„RœeV“ TÊ“Aœ”HɽüÁÐ…m\üÚü«YKde>pªfÆù}G<Ò´ÇAóv;Œša®]B#âö÷›¼uªGÂ!—HåØj4§¹Ÿ?1bÚ¿ÊBÉó‰R+ ÐGp”ŸãpûÊ^E/|ᶳ”#x…fÕîOx•Ždcl´l•‚78»°ìãK¡ÆE'?D ¡€w±¨]8^(ÿ7Ô¡.¶^ÄÃ×°4ÿwX1)D–¡",Ì@i!ÉfŽƒ¡úcÔH¬oOs°”Ñ€…ƒÇ±¢òò¬xÏ=!}¬0'–Tè|¯Ü;H$Ø Òd|& —ÂÌ0Š^XR\½Ûæ0l„ÝGñª‡cŠ ëO¯±èõ…£¦M û ®¶+ðlR—ü¼óvù}Ä)x›~³‚q^2 Gh*cÁ>XtÇPGlr&ý;›aæ$çA¥\ ƒ®©¢LØÖé Y€ ¼@ âôw³||ne*« ùF‚õíˆ`myú‰E~“÷}4©"øð$ªí„ÍÁê3½wò€U7ótÔ¯£¥s7lë`’³p„gXW}7Öøtx¨ðÕÖ¾öƒ.Ò§gÇL# OôéÚ8¿©ÏóÔTX¹‘±Òî‹'ö¬)~sj°s—mqu&yu¾~"üÖ›¦ã^˜Ý©¤ÌÅŸÓÙeòûö#'Ä©Y¯N=ØÔr–vóºìø»·Òí>º¡©^ ã8õQ/«ÖÏë{,œ2ÖÞ$ršÀÌM™7’®7f^îkç·å¹Ãìej7÷„_È®7æÖÑàjÊò‚|B.Õ0”ÄÜ;pî±ÃÒÇ“Kh³8#jùè¹ÑÒÄÅ.=o9û­Ú†FgŠ?\Yö6õ#ÓŸ—ªÚvÓÆiÕ’›Û§ûÌ?§YÍXSMszKJ›ù7v}¼fdgïemOÆíë¬qx¿Ã˜ŒîKîÉ—ËžŸ?,ø”ªžvê»7ú³6OéšveëØB#}Ú篴\_¬5aðʤlÚ¨îÝZ®×á؉”’ç?úŒÂ–@ØïD¥FÏ*ž[×ñÞb!š W(BÆÀE1©wËlêÉâVjq¢ÇLP8œZÀV˜­…ÊÇ¢‘3ˆÈ h¬ÞˆÖˆ¿c/Hã8?˜Hˆ¦ø]\Ž3G0y$z„CKîžðXžê ^.^¾EÝÐ~™‡"¸ªÀ¸æJÐ2†ÇZ,´Á¢S˜Ý HFŸPn‡æ`æ !° õFãýe„Ç‚eªM&Ř¾Xñ6ÊHz7I˜ÖlaV’#î| §ׯpi3òöé·Ïã’_zQfy ò–ÉÖå|kž@tp膱„ °d_$[³-ÁùØq¡à™'«^t²¬jtÆŒî˜0ö¶pÖÃÐî„.ƒ°a:Œxx÷4+Ø–Ì­Zu#îD£gÍo~§¿9±wV¯|[ëãõ%<ˆ.»GsÜÕÇßZóÌA‰»ºiqiÓá™=coÀ²ûë_¼é ÈÇ·$êüô+ç]á=|´OŸ%¿uî¦ S®iåâÒ6ôâƒüzx£K§Kº3.ïM|k‘»|«öé¤÷öý ³ÎË'ÙnÇì0hÊÄ Ón­ömã?óáìN%‡@täï¾xŒÈbèÌé£ççnèf1vÉ¡ËÏv‚»;†·°+aB'=²Jªí¾ð QÅ~Њn½ïL@“u‹"ypîh•‘ÛvŒê¢ÈΨ@¯å[W2 kWh0"ÐÅoËÓæî³†¦8Ùº–û¹ã< '^w{ôÚNúÄÕv|«œ„Ï%Ú×ÀR2òÔu➘qû»“«Éår©\µÎà©“|ƒD›»ÔôŸ²`²ë]|žÛ×KÕïô–­ðñTm û‘]ûmëXü˜i¶ðí:ýÚs•¦½o²éÌ©*ZíÔóií%7 áf¿ëÓ¨ú}OÚ>ä¾ÜéꇙÍÉq˜û[m™ÏåJk—Qmç-’=L‚æ=GºÕ³™O¶ä‹3žûœ×qÿÃImŒÄw(Ù8Îï-13Ú1âÅØªÀû]›S”õŒïSku{å™cVH· %Ï*¶mQE ûU.tMìð‚ç,\?ŒÙÛPC¢1w0LUÈI´&­ñ Í–!—ñ´ÙÁªÆùâñs´­‹9³>‚ ÒP½ .ÝFuEÏd'w´ß‹þ6hæ‰] ðä¼Àµ dLXÛ#h)„I>HTÂÅÕpÉÁö©­O™q ý†“ãË<;#`x—0óørôsÄæ¸8 ç³!}ßõXæ‰5ÇÐuZóDv‡÷tðC˜?ú÷A&3œQý4}D® Ò"`ÛÇ/bÐJðu1u9,æBÍG¡q‡ŠÊÌÆŽáÝ úaÇRYãÎ-ø ¿7!Ýèä…7ócÅ•°zi¼ÚÄÀ Lê€açÐeÎLE÷æènŒL Xꘆ1ÕÉh·ÍB;r–gh¹ò,ËÁÆYXr <ì1Ä…Ì!ɽû"t-nÎBðsdDÃ}¶Ž©neºÇ0¿]‰XxØü˼a©†´l6EØ;ØU%‡ÑMöFÌõØÔƒ .… Ãy qÿ†FkK°UÑ¡‚¾jžÚ´l¨Ã¿{áHîš-ÿØJºy7æLÚî2Ãɲe·&Ä{ûÖÃKŸÕÚ4RÉÎÊf ë4àd]õ2ÛÞZ›ŸñnÑâmõ¦ôÒ¯cÃbÐ23tðâÔ´uB5mض‰¢ÕD$-jZλ°èB¶í²ùºdÜ¡]UZèÎ%2pxšÊ|ÌŒª p=í«É ‰Œ£®­¡FÊ`3eÚsަ–P¨bfÎʽ–##«VPµõê–™™ÍcÑ•9‰róÓ¦1˜/ÖM^µöäd^q„RyQŠn -UâYS3°¬ÎÉLÉÄ™ ±IÌΦùß§~ U܉z'Fë’B&Q ‘ ”ËÙ*ÊJä-ZUU–>IÎ!Ž’Îìx£4ÙD?##ƒÅV3Öd]=þ~qwl-ÃZ¦U@ü‰Ãû\ϳée)ÊÊ”ä1ŒŒµŽŸ|ýFóÄsAÏ¡M5 ªLR²í^*-<Îùx*<Ùt¬;3#“«]Õ#ºy9ÆNÇàåL½vu­eØo ÙN(&/‰ó€/Îý®Pòü§’”Œæþ¨Uéð.kÈ¿bؘ¸ ¿ F3Âé×ÅN§¾^eÄ©R gKFuì¸öe˜9{0§r9Tj†³÷K¸­á8¦ØUc ¼Jä¿FW¼J,vößTppøf‰Ìq>²T}¦’•„oˆýKùØàȽ2BZ»!²pˆ8kµðpÒnLú*°åp|þ¤é¶XÏ…ŽÂ7»À¯~ ~kÈ¿RÈ*ñWÄÜã(‘êØã‚}…ùcY„Îè^ÛcUØ»G“k ÔÏA¯þ-!oCfÚ+{È»Ÿ¿!dpNV"Òß-š9ƒAÈ>zʘÆF„Ñi¿oÿ´±ASõB<¬Ç­]:ÑÊÄmçª'³—÷SõÍjï5gãâáU ÅüB’#“Àà æñÔÕHþ˜„ÒsÈóç>TÜ+/9$²¸cDþâ@vÍôI/D,EA½oÈ(AĵOáO“ú[VjÔ( 2ŽŠ‰±äÈÙwë[âóõ3qhfÛ¢ò ÂßGò‡LdœÙ¶4î¡4Ëž¾ZéVtejL2¤N¯›¨NÜ*½¶Ë8ëê©1/¡Õ ‚Ñ_ JŽ”!fÛ²ùaB&h4'¿±æMª¹÷ ‰¦iiè©aã;eéœA6VG9(yþãˆ~% ¨Ðpô úøû¿=f} |ù¨ÏvM …êïðUK08*ã…$ÊBŸ=XýS×ý"ôö¯%°|20ø,6ÿÅx Ãz)—ñvêbÏuXW¦Žÿ/½rªܶçeË1cBº ü)3uþ5XJ½Æ¬èè9óãýÃ=ºxúØ9ú«¨ƒ_}ÊÜy_¬Ooá8íH§ÑIñÏÇÙ7´©Õƒ‰vvÞ‹l]¦Ä¾¼àÚÉqH›ž§ûäOXSh•𡤹9™ç¦$Ë`jøóF潜÷ƒÙ=èbú¨žv#´ö-ôþê~"1wV]ÉvìÛR»VóMjª|ÌU¯ÝL=uJè½¹“¨‘j'Ó9Fäúñ§š{×Ö7iÜÍ¡á Y΋U›_wÖÍдa›Vô.¥‹ÈÁ—4R=H«Ûvx#ÆÞ»—²P[|º-Ñ ìkŠü@DÅ÷®¸  ½¡“3é…Ç…q’Æ1½àC$ÊK銩ïòìäl^8zÆFJÂ‹×Øôê¾Ú5ÜÛ¬(!zþèPEzù1*âR|æÅ/ 99âàå™6j¹â®Eô<)8¥:ii šâìl²@\CFC/™IE]Q«]wµÄýç.¹7ï­ÄaHD¹2&—]ÚÎ/€·ißêÒ½çöG672¥b‘”Î6¬ÓI/ÝwÃÝ­ÉöíÕÈ¿ß ü›+k0 ÷'|eåÃv[r²É…£1©?{'´ÿ#²¼”ånWš[2Ò#_鸄vÖÒâõ›?î\@[˰f u54Ì›tõñé©Éã_™ï:ðiMzÆÃ‡Z³—7‘H_n›äòð\#nnü‰]HÏ*Îo>|ë%Þ_{¬iÿN¶Ãö¾ï=o¨÷ =^ÞýÛ¯|לf iÂÓÍãòîÓ/&w=z. ¯î¼jÙ×V¹P/’î8~%9Ž¿wïùÍŒãw\̉‘í9ó·nÒÎ]§Ä9¯7o¿8©¿íðÞf·oý´Sã& ß½Û£yêÞC·¤“öÞñu·j`k&\5¬™¿òÑqu37nÞŸ*}¼a冮N©'.©œÛÒw¬ííïæ!sÇÁ®ãºrѳZ/䲈ú)ƒ«fÙÍ{Ó’ ÕÕ‹GT,mkÍߣë37ïq­rN^M‹VÝs´‡u+½ÈÝD&v3ʳ±Ñô½ó<š]ÙVË@M«JƒÁ“Æ4,ñ˜¥=:¹ÿ\\rαõ{Ìúõi)€jàÍ×ûNiÛæZ=-U]s·À±­­ºO<0¢C£ð6ÕkÕ•¥ÒòggÐ…:½zÛ÷ܦåÖM[ÙDƒ«ϾrìVÝ*xó¨~£z4ÙÔÐD[Õ FsßÀ¾’›ëûDœ¶0PJxt©Á°eNU ÒÁ±Ê¸€Á={ºŒŸçUª;ü÷„’ç?Œí0¥Ý·ƒQPü0kóg/eØtlØ9ßü–a&‡¯¬ã1ç@ßI¹äH(:ƒÅáò?­>ẉ“è,Ž€ÇlØÙˆ²y|.KÒÜmϽ¾29Åáp™ uÞx¨U6aú‘v'[ {rš5¼×G ^ ›ÏdÓ®8áºTÆ`óx%l9µz]¶^O•×°¹l†ñ¬ëÉÓä`qø<¦‰ÿ¢#~ äL6=³ë¬ý)r‰äˆÄé>ž„•Êç3gí~5MFcó «-¼$%#ç3å~3ûL“3˜L—+ó;œ:LJgqvK·À§½ýiL¶€#Q1j¿îÞ˜îOÞ5‹Ëeøx®”ѹÄÍÒèÍû‡¼tš)’ÈÈyl"QNÀº¾y"r@ CNë·õ"ÅS$# óϘHÉ3ʼnX,Î+‡ììì´´´ôôôÔÔÔøøøY³fq¹es¢ÑY‚¯öä"ÕT‰ý…O¨TJh¾ºPw¥Òëä>Ê_¬œGgñ…ÅW}gA®UœÛ’9KäŠÉQ*Š­’Ä*‘ƒ’‘ X%òÁ9ØOAÑA|UªT©U«‡Ãa±þìákÿh4yúÛï%0WhÂÝ>›?Y°®pbþÿ  L6÷Wþ¡ä™‚‚â_ 77—°kãââ>}úWá$>“’’ÔÔÔôJS»víü]]Ýò¬aІv½Aû¦¿qª¦•ÊàJòdµ;x{¸®¡æ¿>Ö9÷ðÌ!^ç‘xoñ¦a=‡ÔÿsÇ þ(y¦ø¤EãúdˆÑ¨Œ£×臇xö™Bto‹Þ€‚„PÙìì쬬¬¢Ï|ˆãÄqþ'²¤“F£ihhhR¿~ýöíÛkiiå;éô?``í :§…GÈs;_Àí9uñ÷ogãß’gŠ_G ‰‡Ñ 6}ÂÀŸ+ÏÒ;¦øíá›ò¦> 4ñi9¹ÈeÜèRò\ òòò3—ø$ŒÚñ >þÌd2UUU•***ÄAþ'!½€S›Í&ìÝ"'ÕÚLAñM(y¦øpUPS1gè{%P’“7ЩØ_O™Î<“µ¶®ªšs3¹‰ñþöf½U¾£OJË5È_ý¯n³“J¥‰„С_œN¥Édr¹\¦ ÿ è“°bó¥÷Ó§Où¢[$Æ„Ozz:aÎêéééëë}6hÐ ÿ˜°qÿí;£ ø/CÉ3ů„õú}c¢»Ý×g2ßÞr1¶ªýúÙ†f’M3Üç­3ô9~nŠÍ÷%¡X“óËZÜÅbqxxøîÝ»y<ÞêÕ«U2¥ÉÌÌL/MFFFþaþJ¿‚¨:äV¬Z!U«Vµ²²RWWÏwFðÿ'óeBÉó_B&Ħ‹ÈcÀº'V.† ¹å6OìÍH§Ã¶Ö‡ Þ„»4wâÀ¢ÏB,ìù­È“1n(v^E®ªº…SþÏ-Æü­ˆÁÂë–£zÝŒ_d4N¢^_œ]Yòç)´t¾ø¼ÝÙõÁÃ'l‰JÞ¿ÈaÊÆk/›W+Xãä\ŒY…„lèÕÂeè¬XO-ñÆâôCH€*õ0iœm«`J±ÌÛ!……v}±x>~ÖÞróçÏ_³fM\\\NNNË–-R¬ Tö“‚˜˜˜O…ä¢JLL … KW£ÆÆÆ„KÔ˜åÀ`Píû¿/”<ÿxšã˜ >%’â4ÙQ0áb©;¦&ã]4²Ñ©L 2*)hnþ§q¢#âÎ`ÈóoÄ,~‰Zµ`ì÷qä¯iïP¬|],Ïr=l=Œ×çðþª–Õå _ƤFø´îë“)××7hå•aÜnÁ8¯E ÇÑ–Ù™Ì2öjµ]D º¬ÀT4dãt0>'åùÃ94l‡­ˆw'/ßۤ<“ÍÚqÐê€Ã#{®ÃÑønœ†>ûë4+Enn.¡šëׯ.éOhgI§¤–°}“’’òç %• ߇0 ÝÕ×××ÕÕÍ´lccSt,ü ,PPP|/”<ÿ Äát2L­ z\'¬†X’(Lß‹UÏ3Ëû£Æ*œ‰‚Ã+¼a¢VU2¤ž[}#îižø …c ~J=ý˜W|¶]ÔU#÷³Ü²V¶§<ʉHAŽ2²¥Š­ŒK¡V·í‚—Úwk‘êêqê­ÛÓð¶}ÎÿZD‚‚âgBÉó߀\L°`2\ÅÄpRtÖ¦Þ!cƒm0ŒA6øŠÓÈ="Ò“ÀjŠÆô¶B_?Lô‡EÅã£q.‚Z„þÀ®ƒa%Îí [µ.”Uñ< ¨Pž+@ÉÄ©› ‹5lâÞ–› è[wÌßÇà K'¡NŒ˜7èYÎÁ¾°+±£§ zä/pMÔQd‹ †­ÚC<Žý¾¼–ñìÙ³7lØ ‰é-3 a7nÜX(êèèp8FnZ øüâàÅJžÿæ?€ÙX¬; ÷Fhƒˆ³ÐI'ý÷#qn'êÍC×Ù88 å΂C*!´¯ÄmÊž°Ê"·È‘‹øNèuzŒØÚ£”W½‰¸WC±|(&÷Ǫ+ð®ƒl@Pº½š^æætlòæDÒ2NU€ŠŠŠ³³3ñyéÒ%Â>Ž',鯃5iÒäû⥠  („’翯𚋧—áÔ‹ö!”ÜÛ¯cËgAU„AN"ö-Æ€É84 ½ËÛ§Vê|È"AØÞóÍùqôê;ŸÿȘ´rÇ)w|z¹ý06¢ð4ñÛŠãT¯Ô¦õÅðx¼V ˆcBžß¿ÿäÉ“‹/†‡‡'%%#d›0¿/j ”<ÿ D¢ÛnGš°æíÐ]QyÚÀ˜½ Û— ›†¡ Øâž:v€n*²,…p·ÄÙÃX{c[T”…ˆ«ø”‡ ïoä”°°ÉUøåßVšQ®½Æ€nu¸9cåyˆÙd‹·Ç<Ü « ÇN_@î»Ü¤fEa*Æ@­­­·7yw¯_¿Þ½{÷Ž;^¾|™@É3Å?ƒ’ç¿!ÖÂSŒ6¦HyŒªXiGz.Z7_¾@Ÿà1 ¤ïá¨à¬æf¢ ×Â` Ê3¸oÆN˜Óáaa>ÊõÐ ÊF0ÕÇÕ€&’ß`ë. Ù†þßZ2ײ7R¦Â[6õàêõJý>5SàØ>ÀÊÄæu˜4›Ü Û)cÐ@=`¢ e%˜Û¢Wk(éCŽ][¡ÆFÌ,‰ÀœÅh¨™(y\+UW¾|7¬Æ?ï®^½ú„ÍdRÏÅ?„z}ü ¨áÀiäHÈyÉ)ƪ ™´õÄkgÜ~‘Œ\†ÓØDÑ@ÝçŽás:drÔ™ƒUædH¹å`’óz ³bðâÄð”a\ô¯å…Kvˆ‹CFŒŒÑ/Ú‚ŠâɧiØ!5š†PªìsÄzy—lZZä©X|µMÁË•Œië®!ð’2 £AI&uÉnqsWùýV´ØxdËQµ z/‚›\©„Îäwìí¶âÆöƒ7R†Õ¨°FR944¾³Åœ‚‚‚¢”<ÿ ÐaX½@##a¤ÐæÔT¨©‘yy`+ÆP=x…,m®ºe ¬ëB…Ô,¤¥•1‹CšËù1ß¿úŠ®e‘¨À'%ÖŠŽí¨(è bÎÉ,{ –œÜØ,.,ïí©èÓ¬Šò`ªÈƒX ‡¸+s"ªªy ÿÊmTÄÂÂP»#X,2Ãd!qÛl½ðQ¯Ž?A›)(((~JžÿnÞÄ’%¢ëã=ÅYïÞø¸¹¡¦¢ë53³ÀÇÕ•ô!ŽuuÉÏÛЪe-ÏÉ­FjÁUãÆáØ1òàåËŸAƒ`bB6t¾Oïî% »,ýËf¯C3ƒï¾»çÏ1cÆwäÁÅ…\G…€æ|Ÿþýa`”7Þqþ„Ö‹v¡:‹)((~(yþ 03ÃŽÇ+W’zFP»vÏöí¤a}æ‹Á¦Mxÿžô©Qƒtºâ¼KÙ1gd€Ï+8ž7/^‚W3Ïií>Œää«ýkèÿhIÒʯ|ˆû}õм»š…ãÁV¯Fd$K`è9-DG§œ¼QPPPü¡äù/à‹NÐ"¡*χÍ.–.(FS—§›ªª¥œE_^Ìt:~ú6GÊÊ߇/ÂðùD: GÙÍ¿ ”î$îÖÒäOuݲVùý‹Å;wî0`À¿ p«51Î{ÙŒk,¦BŽyµæn^ŸlÚô‹…j êtdWwáSaùoÎ |Òwþ`ãJ¤ªdî6dðö+¹ŒRéÐôÛÎZl½oçÍÀ-g|Éj@îçÛîfú×|µ¢?ƒoèî¤t,Ø®GϬР‹î¯ònàa—©¿´‹Äφ4²}ÓcÉÁíƒ U Œ¬{!Ó¾sçó_KÊÖ µvcÆé±¿ô? %Ï¿â\Hà•»Ë4É«óð=ˆÅ_ŸI9»¤®ý¼!Ç&›é5­ŸpoS—–9¾÷_Wb;ËïBŽœ°ùåN ÿÇH¥R‰Drï޽ŋïÛ·ð¡äù!—I²³²%20Ù—C‡4';G$•sùBSž—Gœ#´Á¸äVæiÑ~j §a—Þ4FQëŒ\”“+’2x¬Œ¼â•kåRQVV®ŒÐV9Ãp˜4iÜ•Q‹¶¶µ›,Öe3Ù\Mq!¹WÍãsY?(¹$/+'O&§‰ÒÒÅò2öQ—ŠÅDæd’‚mÔ¹š6Ý:qö_} äïӦȻT”GÔ$ØVŽ Ælº´û<ºôx0­Ë®l›Ë‹´™@`Ùo¾Wv©Â!3+gpølznnƒÍç²RE^‰B`(òZPe‰³³sˆÜÐä`òxlÂ'WÄà ùÄ%âÜœœ<9#rÉðry^N–¢`ó „®(º¬\òn@gñ„<ìáaö²¬‡d©¨òù̾KÍŸ %Ï©ïqð28èé‚Ìû¸öæz8ÿêº0âÙsH¸0oŽM‹/I|‰£§JîAiX{@¿|ÛóÖ.„¿…N[Ø+áÔE|ÎC&èÖ.ãÆCdÊaÕê!¥©Ø»o>ƒÎA½NèfYƒ8{ :üA*3pòºÎÆÐFå¦xe?–ÎÆc.¹œ§iS¸t@‰w—r›€û×­BçNì|ëeëºímÓ¡ëÎõm_]–Šm[‘ G³–`%âæCäqѬ'šª"ü4¾SŽ®äN]ù<:Sw+ƒ¦)z÷€–bÕðw·pò"Ò%`«‚‹³°f3 ~Þcqþüù'NÄÄÄü´x)ÊA”þz¦S—ùçÞ;/;êÙA“öqÃhïi´vîXÓ±ê«~m”›Xiç½Úv4nìÞpÛkàÈ¥Q÷vôi5äY£uiW<‰Ÿ@Ü­î£6i˜×7Ò$]ŠbÒ ¶?\˜2pÖ{›¦Õ>Ü<žXoJØâ®g×®y‘/ ™€æmÆy;‰nê?f«Ž¹#áÙ ±õö=sªöbî³9žCÅë52¯Â'¿UêG–'4Ó,ÛÚd6¿L*>- ý oÝ©š~én†S·R¹ïoÌðiò®Ùܶ—ï1uß»Ì9Õ{àøíÆÕx 7ˆçìÜÜÎDÒ„­“¼‚Ïæµh^[EÀ¼²|AÕùgƒ«¿wqð’ϺpmtËONŒ³w:Èñýôa¹yy™Ç´ã&õêŠ"|tÚ½cûÞ^×Ñ[ŒšóSïïÿÐ/êÂàÔ'ggM9˜ð¹úÜÀ±-G/ò²ø{¨(yþ Pª‚†¹¨?ËÖ£®¢ïbÊa˜­G¿Óð›kc<:Š~SÑq&ÿ>–è¿C{ í=]@k *åÆ_½)Ö/äUâu6.®ÇM ZÁ¼6´ØØ¹Y‹qðÚ)c`}ä¸cvÄ\¬]…òü‚Ü뱎‚=‘õýº@cIEÚL`Ñú#¡Ü ýúA †Ò뤛{½†Ì|-©âãÔl[n—ÎqkC†÷¿p`ßÄÆ]Z£–%T¢3¡Ðز ’ÙhܦÕÀJÅö‰Xp1ÈHîÏ@çmX·5XX4¦–èTëÐÓcO mÜ E „^üiÚ¼wïÞÐÐЧOŸ¦¥¥¦óω”â[pTkÏ[=esm”4}-rÚªz¦º3úLíX\µ>3WöéXHÈ:i°~e˜Ÿ­Sñ·Mc˜4qjW#ð£0_ ãgöòTö=¾s|Âø}6ñÒ–gUM;-ÞÔ¸ ÿý:ö›ß/èÛÑÙIsîÁ^³B'™çSû  8¼~rFòýö6 ¼öû†¹è;xú!nDâÞÆ|ˆ?ܰ³ìƒ°PG’k«Œ‰PYx¾W™íNéw}ÎFÃNm {ø :=¥ Û§xÕ[ ïc²ÆÐ~`àWÇÈö&÷>§r|š®ñ’~Ú^«.½]ÐýÕÁ>ó¬‰ùàªOfhÎ/À0¶`o=ü›(0zU«^½ê†EòOê§Ù®5§_\éÙ±áõêÙùñp¹±ü©õ¬Ó‹ÚŒŽ¢¤€z]»!mµößm¹`ýJ½2þ (yþ `0¡¯°1u#XàÕdé W0†·/j+î4ÁìÙïöjÓÕgbž›ââjŠÇª…ª>4ø´ÇÚ¥ ÷ âч‚œH×dk¨y âÚñ±=[ú¡VuÔ2‚ìeÁå;'ãu–OD-â-cG‡ s$„¤¨ ÂB`©¬ ^m#U–~›Ö0ØÏ­¾ç»ø ÂD^¸páÒ¥Ke2YÁ‚‚‚rrr~(¥¿‘Häããcddôט¹Íl>&`Ûôûk±¢"®§òRl£Â5vhöùcd—Ÿ.£ÓäyÙÒ/ߘ29 ›ºãmÑ]ظ£ðDjÆÍ¥ñ QS>%åÑ¥y"â9ù]|áç·¿a9ª*G}øÀÌËRW懟y éö /yÍ×6V´åHe2¹eÂàã¹#­IŒÌää\¹Á²[Ñ#lJ=9²ì´woßDE]˜0f¹°Ýü ýÕ‘#‘’¯ƒJA$Lã µ5Uõx õñ0ðjªJ¯:±Óò$l-ÃwWÞÈ ¹qóbV5O'ýü+¤²âKKFCþGÊóç3ûc”Æïß¿gçJ4ô»gßùÕ­íã5«åæ¦ëÓ‹ËP.•”IÉ€i…ð‰×šŠ2B›8´~µáÚ MuY.Ä9`”hã'Ñ[¹ššš...fffçÎ;}útvvv™ÁìííÓÓÓËÇ?- Šraw0nz×ÑË»ïK*4Y¥R™(59rnΧ¤<Á$NLaE3ùoN©Ì$Æ=Õü÷œ>ׯ™‹ ‡––-¢Ñ褿\,’J²23Å"Zb||žœÁbNXâtQJrQ=ƒ°•‹YÎáã‡w 0Ta‹s2E,€eäÐÈl÷öù'¢»w1d‹²Òr%4ëË×5܈µÀ¿íÊóý› éx.X[Ñ L6EŸ/¦ÞôÓ»Ìýæ¬?âm¤ZÐÎ-‰º´!<ÒµtH&=ÿ‰7„ù€Vš>á+¯Ä¶µÕçÉŹ92¦€Ã²nÐ’½hŪ~£ë«&v¶˜l#.å©2ß½ù˜—ËDúçl ¦Ø2V£½“~Òöc§û5¤%`‰²3%LÖɽ[jÚ ²Ðá×îØVË%Òü»£åäæˆÅË¥?ò(ÿ¹PòL¡àÄaÂd†u°Øhl C€ýÏŒŸF<]O1? -¡e«q®'‘ò{îËq8Vߎ¦¹ ò_øë?þÓÀd£W RžÁWÑQ~`&î´Áû¥e«Ü†ÁïAKKkÒ¤I'N|þüùÝ»w:ôãqRTLÍFÎõõvôYØ£ÀMgÛ;zÍë2ܼáÆ{¨2ÄÏÏ®Ú~“ñxÿDm ]®8RåvÈ¡§)ŸÞ¯ 9Zݯ»Mð%Žœ^fbR·~N¼4ëÙâM——òœb·zjÏú›·nUO›+9?nìÆmsÛw¨/XÜ­Ñ«^î«BÆÜýØ5°‰Í^#U½š­ÆOm¦Éî½pí½$w÷†µLLMëÖ¯“€¼¥c¦Ùl™o­T˜ãô;AS—žL@ÂêÉS²í݆z×T5™½sþá6ÖgøôSËz°â*$ÌÓS'Íîè6ª§E‰c†éŠk·7Ì[èãîÄæ „FNFJJR¢vë1%mÿìá![îËïf/Þ7!ÐY¬A[ÞïïåÔȲª‘–z•úC&Oíe¡cáä·üöÝÀVµ7Õ¬QÓº¹HBW#¦Ý ‘3{yXœ[ÑÒ¾³06)ùÕîuaî£:Ô™z`½Ë€i ÂWWÑR­bÞ~\p /á–g¯½FFÚ9±oj _ØL½zï!‚ÅÓíZÅ8_1oàWÅþPòü7#ÍŸ‰W‡áŽkÑJÑi·tÚø¢F$æzC_)Q˜£ueúŸ+€­,Ìt‡Ý=Xiàê!¼Ô‡“¢uëö¼MAK5èë¡èlÔi‡…óa\á¸RƒúˆÜŠÃݠÂeCðråúÚRD[`q/H¢±ý¼·â·ïàC´ŽB“K¶„sUÐÍ3\AK¸Õ£YëKªCîÝZjúó†±vYîîî?-RŠr k5ŒˆýXÒôӘhé˜×Üqµ²^޾ÓvxšÍ~ÑovÑUÛŒ¼9²È¹ ð`RXÔ¤Âã yKòB¯§‡µèsêQŸ/3¤Ö øÈÓà"çʯj…ÊÖ–l#þJúUi=6M>¶ÐÕ¾§[@·ª€§má»p³oy§ðkµ_vI¾¬„M«ÙòSO–Ž£ïµä¬WÁIf_z©è¨ç”&*<¹è*Ã&ƒ/¿\*ÿõ]ü¿L]©ñ´Ù´ 3ø‡’翀ħXþ.x´»xèg^x⦇4te,¹ŒÍ ¼›ÇýX³—B¨«6p«VQü—Ö!Al„l‚Kkl_ cäÜÁCô­…ù{àä„Èãx7 ˇbÎ dæBÉwîÀHñóë±½/!ÖÚt„ ‰qf š=ÃÓ3¨`ˆÓ"<šŠ5óÐÖõm*S ²ÏX±XÞ¨?-å1Ö^“:–Ü‚³3^níIÅÙ(8vÃÊ‹q›±q‡Ü~50Ñ»lÇH…Må;V~à3ÍÊR&9!<'³Ý©†­–¹\UAúƒÐ]7Lnö·.qDA¡@þ#ã:)J@Éó_€–9‚Ö£Z5r&Çòr¾eHˆ.–™‰š5ÁbáØÛa™îÞ…›bÞb‚GEñ·Žt#Lš€´D B ¢¾4Vߺ7‰Ps‚î­1«áMTV/ŪU0nÔµñÖóç‰[k¹÷t¨‰Ö¨Zf”6Sü½(ž&÷oçå¿%ÏÚÚHÈ’-$e˜ ~(øVx~|>²³É¶ZB›ó—©"TóáCÒsêT<‘f¸—¬XÕë äqqæ9ΞÂã'øø'bÜ8\¹ Ìš…½{qü8Z´À¼yX³Uª`Ø0XZBK‹Tk"rˆÉyKšÖdd::ˆ'¸|šÂÑ wÞ—q/2.Ä¡µÖ÷—­Œr`³É$(*Ó¯ÊaƼ{ÇLÕÕp³\º÷^"=_È®Þà´XíÔâœÕþ!ÕC´¿?SÿhH_çå4ÿW4gšCçS]tÿÆñ\?JžÿBTTÁ´!B“z;#ò!&6Ä̧X°·?£_orž1¡Iùa.]€àrqâé$lÄT3&5õ‹aODx>^Xµ–têÛ¨FŽ$…i‚Þ½IaÞ¿={’g £søp¤¥‘$,ư‘  $kÓ'ãíCtvA.úºÂ²¾D9 eÌ.ýþrpR¬šBhs¾!ÃÎÎäÓrÊAªÄîåëý¨ŽÏ,ÐBƒrñàI“qÞOZìýR³Ÿ`À®3íLÿI®((þ+È¡þ+PòüààPʹwD™ˆG¹ ˜ »öœṘ MÌ]»J]Ҷ•±V•x &þJ’¯Ó% -ålí‡ë}‘š‰”lã  ­Î/X(è‹r * ùÐ +ûö•ò)] ª-²Œ·­—Gª£Ñ!T…¦†'àY¨ùó³KAñ‡ñ7n[ñ+¡äù¯„-DÕßiYU튺™˜,èüÛ™   ø; ä™‚‚‚‚‚â·ƒ’g ŠßJž)(((((~;(y¦ øƒù9‹ˆRPPü~PòLQ9"B1q.ßÇÞp6ü… E_„ØqÃ7bù _˜Ð¯$ö ¦b×EÁ¸î?9òœûˆSᨱ w–HœäïÇqšïßêÛ›RPPü†PòLQ9šÇÉNÐ1ûå ¶Â¶VxMƒ´Â0+FšŒd´ÊØLR.Í‘3xtDõ ×Ýó|Žs-Èód´Ÿ3‘+=œÆfB¿vÄ1õR[kþ,xVØs½ŒYA –ú„SÇwiÕÎrƾy.ê®_NAAñBÉ3E¥É̬xÏ蟇?¸lïbä¬Á”2v¹9ío3-ÁåÈ®‰»ÖÑ®cpgÓè!ëcÖìÛÞHÿG×㔉ì× ˜ç¯–Ijó/jyeBüMåçÖ ÷­Wmp5ýC#ZÿšŒPPPü*(yþ KÉ%±$2r) „ egAB¼¾y`È• |6DyKAg’krÉÅÈ̓TÂïY>W&Ff6@ ±b ¹•¤@ˆ¢Õý$"äü¯½»‹bkãþÎö.K7‚HX(ˆ"& €* ¢*Hˆ ‚ÝŠ‰…yíîîÂFEÅn0¤ka{w¾™%ļÞOÔó{|p'öœ3ƒîÎÌì!Ù3¦a³R·N,¡˜Ì4&±´J¡ àƒXJË`»†>q¥Å¡è Lº a… (&·ZQõ{F·˜cÀÝéÜZüÁÅ,™Öpä˜1†*˜\:Q“ ¬J{¨¢Ri1YÂÇìÇË÷ƒUiQ‡ÏjÖkrâ˶bu\\ƒËaaè’5‚Ôn(žÿ‚4Ø#§ì?v2èÞò]`n8ÔÍ€q£áp&DÂË»pí:‡¹=?Ùc¡‰Fï@Z Åó_€SBçC}&¸Ì†ð_š^\EþÍJ‚ë ûh2Eо=LqM…‚;àØ&mç°o­K§Ä †-ã ôh Œðé+£`b "~zWxä×€Ñ%” }<®u{%.D(º„r¸µšWŠ|†L€eoÁ_18t¨.„.†;Ë€õ™‹ÅJúÞâÎAy0ýÓGmR²ï¬¾&¶jµ÷¾~¶*c—´—‰ŠÝáèîfÝ ‚ú•Eà£BA€1ìz7f‡ øcÐo;öB×(À‹`òpHw£ÈÄ@³0pyÓ‚àŸ$˜M´Ê*#6À¤5Œí,&ßõp| ôn3&‘ñlÓœCÈ ÕcÁ†ÒêR`qìÖ…ì]@t€q Ìr—pÈ?Øk4޸› @î»\ð< éŠZx'¡g$Œº ³ì„Þ .â(:âO  A6ZÕô¶älòçõƒZz@`óA©b1JeС8Kð¥Ö!Rë xþ›\M»–pñ(ì½,ÿ¯U¹AWÜb&—}|”†XHþ¤QÈÛÇÈ»µkÊ.ìÒƒŸ<§i»ö?öN Ü?ˆ¶©2ÿË'¾åžw ƒì‹¤çȯ•èe‡L’šÎзÝyG@'VÍãĆÞEß Šç¿FÚUØWñ‰0ÀB&C—ƒ òÿ½ykÆï³?ιÀ 4È+Ö ÄL°-}†õc¿UÏ€gÓ`Ð7çsibÿUqÔÉá#÷ŸUã¹´^ʺØÒáÆ+»²y9Oˆú ¥:HõÈSî‡o‚»cY-”ò]ÔRqŸÜÅÛÐÉ®¦RÅb _: ˆÃ%r S*ýæ_¾|ùüùs …RTT„aXaa!ñ“ÇãÉår¡P(&Þ.‘Ðh´™3gþè†!ò#P<ÿ%r!bŒßE¾\t®´¾+àD(9Ée)¾NT>¾$#ƒ­lŠ T PË;eÄ:ÄÇ:Gù+U‘b T~z•IS|sˆx¥KÃÁ{€–jw †î†®Ó ‰&PZƒ§Œñƒ­ÐÞÞÜ„×hïõ'AX7P^ ¦—J~oɦ1|!•ÔÉ;·ïƒtÈ4ÁèÛ†ÎÄ€Ã$V¬Í¦¥â7¼­šÎ${·ªu —;L™£´`Lw •À«thÖhjäÄŇ et]ÐT&÷«|7”î•ÒI®â§RyMdç˜F~k‹Á† pð‰„ ÝÀI`ÔTpõ‰ZíÁNÖ@‡íÐÅ’¯Á«\PV”à:,ClX­džÿÞq Sã²ÂE<^Ihöv$ë,Iì1>½IõsÃuØe{HåÙ³g߸qã ûÆËËë›ö!‚ ?Šç?wŽ@?<1ϧм-Ü9D&ÜÉ0pM…Pc˜´€è¤AwX¼žÆÀÖtäA÷Ñpx&Œõ„' ^ ‹mÀæ=LYOÞáî waâ@¨±wšzüg@°?< Ùë`åò$öð¹°!z,„ƒFÐ×^ó€£ aþ EЪÀ–x Ï–dÊ žΚ°¨Hu`IG¸s † …¾­Éïj7s9ó¿r5\Û&8ìHh¾6œýÆxNÞ ãV}aùl8: Vß‚ìУ–M‚Øxðn%Ìîíaò>P3Ã`épÐ0‡ÀqТh;B-„9C\'X;ÖM&÷Öü``„îà †Ù!Ð&‚F5ú¹À¢]€ï†ðS ¡@À88=ú,}¶€—9ÀÐáKaN°bsµáø]îƒí@¾AP¯9œè ÃVÁ?Ap÷õ?r5î3ã>nWvƳçyÚ‚œ¾9sþқ˼;áƒ]MUKW°±±IHH˜6mÚôéÓ?·s¼½½¿e"ò¡xþãa`Ó‹`ß(~ Ð\fAî,rÉŽ­)‡¤Ìò5qH±…{Á˳lFÌ1èt†|ÀHWròb0¤¾‡;‰ ¥Ss6ŒœàbågM†BVh•<"È?ŸâšÁÆË°±bz4ì.mÊV°çÖ÷mòÌsðçbÍ|àe§Wpí °ÕÀ̳ÅF˜J4æÉUx’ ïÍ–µ[ZºÞу°±ìÃV™‡­RÌ9¼Š$À÷®òª[ø*¢ö]káîp)pKùyvX¿æÒb€>¥gÀãŸÉáÀQ@wŒ$»¿ ΀'`ú÷sÏÆ:÷àî=PÓèZ¶UO¯ ý°j³5Y—5úÌNÈ@†ÓHoãUÅÎ;¸Ärh<pJéõê[{·Ÿ6¶©â<€f¿×i, ÛÅ:œê÷½ñlooúìÙ³j‹TUUÍÌ>ÿEsAþ(žÿ:À¼yäçu£FPúùÛ¸1lÞL¾°¶†{÷ÈͬaÍòK?DWiÿ~rN¢:x<˜3‡|QX €Wo81†í“>¬ÿIíó¥3Ï߫Ƨ[¥ÄÓRà~úÝ*œ¼Œ}|+|σÎïÃɊ§Á°aðálÙÀóç°?ììà|(žù,¯¯௙™Iô¤×­[WXX( íìì<<<¾±AªAñŒ ÈÏ¡««Û»wï^½z‰Åb·gÏžÎ;3™Ì‘#G½j*=WA¾ŠgA~& Ø Á )))Dº}ûöÖÖÖ:t°´´¬[·®ŠŠÊ¯n&‚Ôv(žù™ššÎQÜÿôéÓìÞ½ûÙ³gl6»oß¾¥×­©ŠgAþ+øøøà8þøñãåË—ûùùyzz†‡‡[YYýêÖ!H­ƒâAÿ+ Ú6mºFáÌ™3±±±………Mš4!f9maañ«ˆ µŠgA~™. ÅÅÅïÞ½{ôèÑ¥¾uëVç· †ôüåP<#ò‹q¹\ …>}ú“;vìèׯŸT* ðööÖÐÐ`2™Ø>G& „B™0 …FgsX4\"â‹ÄRN¥3•8 ‰P@LÉq`r”Xôª·—ã^_ŽaT*ƒÃaJ„|1±&F”De°8ÌO;ÿoáR±X$â4–‡ù-¥K3ÇG¹ýÖ}ÂÆØ §¿9—‹…¡XÊલ~‹[ìqi OaqÙŒÿÃc„ Ï‚Ô.ý233Oœ81uêT*•J$t³fÍìíí ¾³0<óñÕ­[OŽ=.1í¼*vq`&%¯fGù,~Ôt\ô¸ˆ@‡Gç·ÄØñ:N??¥{å7‹.QqŒ“vÓÃ#‚Ft}trãŠÀ°Åú‘î~Aî8?q«Ir዇—Vôt=T/òá•_]_Æ_m¤¡#;-'’ã8ÎTÖ6ÐQ‘]ügƇÝ:§èÖ5fð:MmØç/pi~懼 ñO†£¦£¯©Dn`QFjf1SK›+)Î-âãt%}CN->þ¨Å3‚ ¿ ##£{#]·ÅÒÙ£Où,¶k™Ò¾ïÂ]Ý”«.—×µtjÑV}mè´´ž+ȱWå¢mnGD,ô¼Æ?Kþ.>®}߆£— â$'^Þ»÷®ö†3gÚiƒœ·wæ9·4ûwi,û¸ûDnäú­¾¶š žZ¶5ß§ƒ®*óqº”nZ:ð¨ôüêàÈÃ2—&oÎï Ü÷ÈÎp­Šzøï®ž¿.…{çí{mèìd]|fHßIÔæ=¬ h/Ê0 :´Ø‡Æ/¸ºÜ½÷‚×~ã&(=ÛzIyïÔ°Ò‘Òyooï^1_‡÷ìm3(ÚB3Àããâ$¯8’Ò,>aoGe¦ÌÜ÷¸¬cÿvÜsk6iX»f\§*c­KÓ–ùîÌoѧC½’”KG•—ìXagÌzur¡}ÀòÆÁ3ÜUò^Þ<~ü­Ùº}[{5×zqrÆ€÷]ÝøOã/¤Yì=9¯ž j#EÑOè»9®tuU˺³>mÁÉs.”„;²Õtì=`70Ä(íxÏ6C¹#N\]éJÅe [ƆmËõr³a<ß½÷‘ç¼m<ê–¼¾>ÆÇûœyÈì6ÊïÆ¯?ø´×Âø½U†x©ÍP<#òW0í{àØÖî[߸onYÃb5Ó‘ž×F­Y|jþB%Qþí„ µåõµw~©£\#jSÇnfz ôõ Æ…éꟾxÿGþƒãc§ß^’ñ²§Ùƒ³-Òí3u^—Có™éW¢Fí ¾÷nt3òróÉãÎËÈCqAÒ”ˆÃ¡ †Xë‚«’JÓàéÁË;ÒËêQiÜgÐÀ=Ó…±Ñ#úŠœúċÒN$"\ÐÕ¼Y›Þcúxŵ5ðrm+ ݆Nìc4ð̞䊣 FÝ›«*´31ZhÑ —CãnA“ÚR¤Â(ízgŸ¤v4iüpÏÊÙûÕn ç[c0Ðø–úðèAîtüxþtÏòñD焱íÉN°vwåÀ¸®ô³vt3Q[ÞÒ?zZbÁœåœÑK¶öØÜk怘öK®Oõk -ZX;ïw¢OµF¦HAÂú<í‰ír½¶ÄôØñ$ÿ‰]]špn麌Hvß »Z©B:‹Œ0qÑà #6 ¸øtd+²YnTKËÑÃ\N·hæåÖ^5Q»søDwÀÄö®&#W®ÉŒXóKGÃý(žù;ÞØyG­ŽÖ«€Ðe]®Íª6R‡\Jk22°éăgÖ,繌{¼2œÛqƒöV…ã@¡—ž¡Ö­§ïr¿{ræ­²c{í²³«VÞ]ò¼fò€ûüðC†“‡YÙƒN+âSðîÌu¡NÀ»§7…o˜<¾²žÞ|+}ß /¯Œ ½·öz‘ÎP·Òî5Û´A}ÚþuwâÚ¶!—c“…QF®~UN?T. •N^Ì¥±4tT D,%RÿÞã$±†9ïþí›ÎW‡œ'<0(?ý€Kn%%M{¶.;!O³riûdñÝ’ý”pœÜ´Òíewî4rü³‚7'æ*fÞ¸qƒ.L#‚öaü+ècûI#qG“çwÓ8œ¬ŽL\ÞZœü û¸Ÿˆ—ü”#–ó ÕJçXx»Ê§î{ú¢¨…-ÙH •Ѷ‰&]&.–|÷/ó—AñŒ È_@š3uà8ýy®Ò6Z»Ní6Ö9qc•p)0»®4ê°íØã{†cVk/{ý-Ï2+ËŠ/¬P~"^pêW\¸¥°µ¡(U$ZÞ[PUçP«—#)Ê ò³>¤I8f·oŸ¾•Þgë)ÉãQp.§<5©Le:‹÷!ÿ6á3Í&Zˆ‘ „…€‹²ÒÓ0X^ŽjmÆ®´¢\ÀÏ%•Š,¡³5 —GÄ©RÕ©à‰„…^^VZš0öظ=*õëR»,qMÄÀå©#g o¤œÍ—cJ_ÜÉ’‚÷Àd3)åw‰±µ DU.aÈq¢”ßiÄzÏ‚üù’ö;ÛxØaw# Œœìº3rY÷…=2"íª%ØM]Z?ΫÃ5㹩-5–T öêTäEÚø¡4¿º*WÛв‹d ¢èOJr_ƒ®ªXÆŸ\(‘T½mš¡iBzsçnL¾aU} 5§  )9)*ΖèY|ï÷Ð>Q”¹Z€«9ºõ¨ù‹^U]­|à}\Å ~~*ÔkÄ‚ê×ì…Ù)P·½²¶© `æ¶½]L>[©èýÊÄ^׃zÛò~#/ùbYz4QR‘DZ:)Ï{-¦³UUö×ÞþïP<#ò‡“<ß2eŇ9G}Ý+µˆý«Îš:ÇDDú\Ye¤È`&£2È›¨)ZÝGöR ;át&ذô½t `4ZÅ“:Tòl)«RïñåÉó¢~3«ÔG£ýN*£4Þ)4â5|܉‰•{3yßM· 'µV(Ú³äDƒ®ó ˆ°jìi)í4{ßÝõ­™˜ “'§©ÐéD« »vQ‰˜³õ|‹¨,Œ_+çjq+}fÓ˜DÃÎ,½OËrÜó^g7&KÍhyûRÒýéÁÍ€<_MTŽÑk:Ú 1±Â"žD&Ë1ƒ¼ªMc”o(4²´V­ÚsæÄÌ<2¯[}¢WÊ+,a©¨Ñ+¾Ï„Ñ;«ÍYº3eÆSòƸƒ›n» œId£¼¬ŠÒ}—¾}ù­^ѪýÍò×íØß«íȺª aQžˆ¥¡Ê¨ÚH £àÞë—Bh"ûðâƒÓ$[BìRÈËçÉp™XLa0Èm§Ðȇ’°ëv÷2œ¼ãú[{Ÿ&Äo{ì~޵§McòØ‹FÅ0ZÙÕz:ñâ÷ Pø$þÌ+ygG¥Zýµ,Ï‚ü±ä‚Üí FMÙöÞÌ78탥š>€ôöá;r¦<ïÖê6öoGMj*;s ãþƒþ ØSǺZuì3axçÖˆõÒïÄ,ˆ]ÿ²ßÇùr¨ó º¼râ¢#BqkÙ’K\&Š…Þ~XĪT§ìÚÎÕ3àÅü¨õ&ˆ./ûç¼ßµþÔÀήæN»÷O ït‚¥Œ—ä³Ì'í[Jö¤ì×o×wdé:õ[¹´•Êž\»ø¼ßdçÆK,÷2Üle¸†ºfsç1±£>ž–¦, y\‰¡ƒ³½Ù³Óâsó'÷mo§ÆÅr‹Y‘[އ˜‚ôØŧ¡8{Ñ„Øú‹Ãqé•÷OÿpZû©––—D­lõz[‰ Î ]6äÙ¦){Ó/íÙvuûáû&óµÙ©¬®ªcÒ}pÔ¤7ÕJ…èw 9½)ctï–›U”…Eu:®XØI±£s°A®)\qfF†zëÅÆö$"uÒÁý¯„7k°TCM¥~ë^sVLož÷pV•FF„ Ùçïe”`é>xd#5|ùòƒméTèÕðþ¡aÑ‹Ì3×)ñöóœVD»4Zr`mXôÖ+8 q¡HÓ÷ÄÞ9¦ Ⱦ÷Ïá‚Tîü¸¶Þ†wžL-Î9½íÚóz1®}Ž+Ï;v%ª[£ÿò_ß¿…âA?…­é?e+ñ§Ò5ù3mÕj7%ŸR>u Ÿ]öÊdÌÒ÷c––ÏWqØô>bÓgÊ °Z \pmà§óq Ÿê³ö䢪O‘Ѷô:’äUe–²eµFZø-*ô+ß †.+}Qg¯Ø7múÝX±¾¾m¿}úU«^Û!4!³bïÕ9˜]~ºýØ›ÑmZˆ¥µºë (žAÿR¾@N&ýê†T‚ß=yoÞ¿‡]ƒ_Ý”¯@ñŒ ‚ü|›‚aLNmÊf oQK£ZFÏ÷×Sþúº¿ŠgAä§nš:E¨fxl K½ óÂ<›Õ–‡i²M<º~þ¦ñÚÅ3‚ òÓ±­¹<èW7â·†âAAjÏ‚ Rë xFä¯òÕÇp"H­€âA?ß½c‡OÞ”Êíx¥á¯n ‚| Ï‚üáÄISzDÞ½’ž}-pLñ¯nL-’±NU˜û²{Ûš¡ó µŠgAþpI+Ö tz³ú.ŸóÝÃ7ÿY “oîx áÙœÐèºeý?:MQ6×N(žùƒá’wïx\Â+±(tòYr©€/Èq0‹Ã&‡¹©°„/’Ò•Tè2_Œq•Ù•Ÿ¦KEÄbrDBƒÃaQ1‰ù% •£ÌÂE|ÎbbB¡cr89_(!þ¦S)R‘@ ’׺© %%— ù%")Îærå¢ Îà*U / r‰°D ªT‚Š‘óÄ2òáÕŠvJÅ|±ŒÆ Ë%ŽÑ­/+— ùÄBr£èL6‡I~¶Wm¤œÉdÄo½S2Â×¹ˆ©ÄÅäN½û+ãR!±s0“‰ÉE"‰£-d”o¿DÄ’mª<ïàܨçö«g¸©W왨¤X(¥Òé •Hå@#šÄaRÈùâmDAU&;§0¹\^u3 *¹ß¨Tbç›D¡Ñ9JVºAb>_(#w…Åæ0h¥ ’‹øÄ/Š<Ê¢28J,Zù®“#`Ò˜¥µ“Ï%w>9†FcøAP<#òç’ oœ\¿ã™¤HxmFDxÝŽQ3úêš;bÎi[ë:¢ŒG÷>Ï_·ØÁ„óüðlç¾1êcâKNÛÂz^°×¼¢QÊÂ÷e˜µo@½uòV§é;¦ôµüpe“w¿wnÓÇé'ÇlK[ûOÀî(¿ýÚƒçÙ)mÜu)tÃi£¤a#à†–ÚLÁý»É– – 299ÇÝkÎuÏ™+ï-[™ä“›<[^IáƒãA½W”°ñL­Kþ#–²ëÛ*ó“ïgÖ[`±xw”ýUuŽî€¿þðæ]¾Õê›ú´Ð\œ°eRÄ–”ÍêAþ«„'ôÈ•kúÙhTjdÊÜmobæzïù'á™±êxÞ}Ϩɜ»K¼½¦µ•°Å+3ȧçNºÇÜnæ·.¼ð¬Óô³§&µ#ZÅ{,`è|v£6z´Ük6$;m‘ŨWÞÁi–vê>QØmpO’Ï$¤jLY·#¢kÝ÷—·õ÷ŽÏŒ¿>Ú!õÑñh·^˜¡Yo–cŽ–o榽W‡D »µ0xkq³á®Í3ÓÞÜNJuŒÜ¸eŒ=&y½4dèÁ̺6æj¹odª¸mX9Ê€MI»¸¶ÿÄc[7ffß8œüæT?àÝ0hø-¬…µ>?!>eøêÝƒìŒøïo† Ÿ†™Y¨KÞxÔüÁÕ‰ß0$g­ƒâA?•ÝÆ=tøªq73œcW/Ñ âäÒÊÀ‰·Vg=÷Ô&K7z©‰±z¸*¤‰«ŸMã˜ÜFN£|{7l|µÒˆ‘ò;kfFïäÞ-ÞdM…×mL§Mêãrآ㠷v›[ iÑò¦£›·¨cï÷ ÒÙåf*YéáùÊréÙ5J™‡£ {¹Ôï”âës®›Û÷›9¦kË÷*ˆ¬nã= ›Á©%–vñ˵[z…?“ÿÖßÞlÈÖ°ëý‡ÛN[¥Ýo⊮šD ¯pÔ ›¾ÈiïvÖð ƒn½¶$³3>Ä ÇÈqíέ5®ÒÈ[†Ý»ˆ7Gw±bbw²JcÿÖÌiD7[©qˆõ÷Ç795RIvtdÏ™sS&185ÒçŽrÔeSU1Q#Ñ«€³Ï Ô*5Û¨SH϶Ónº…,Þ@°¿o½¾áÁηŽYwèÖ2$žAD ¥^s//½3o9xõÍ|(­×Ê!}áá˦.]jÀ’<\;Â:¨»­W¾ýŽQc¶Roó6¶ œ÷: µYð·C¾FÛW/}ßuÏ¥)–Y]ö~ p2jø¢Ë—ékë\˜lÚsÞ†Þm§>¿´ú8½Köò1D“zž|þ›æÜoÚlAo%ÇÇq™âõ£¤ Yº´K—ÐÚù8½ŠHÌËôår\L =:G½ûˆJã)áüK·àu»«ä¥½Ò„: õYVžÀ‚+'0Ô´•T4í½ÝˆŸÈq*WMWK]WËD§Ü¥Ž”eèÚÛÑØõ6¤y’UEKÉQókZ½•KŸîþ@ÖÜK/ýíŠ8_E]=éÌ `„ã@g•ð¬ê=Ú%4àA&¸/'JÛl7)늷ñwçmHJIWn¤+±HJìyÅõwyÅñLŽS˜,.‡E¥‚¹ž*ˆ øälÑ˧|-{M:ÙjŠ–º|H)¬Ï@G4‰Y:P3Û+¢»´Mü³—¢Ñf€y6P„ÕIì…¢"är…F¥‘F¡P‰BiT #6^Q9•6Ò··ÑláÙóQèˆv «’º1^Ü=Ëã´ìy)x}pÙáþ!öEë$ÝØ-û§ö²Å)Ê)GbÌÇ`äÁ MÇÌ:8öÈw½Ê-§2)÷VE9£\ý6¹ØcÛÖ%¦dÖÒ:„Ïðly>ÎÁÍ•›ž›÷b÷ú ƒý9·«n&)?ñèpÿLª0ÿeªdîþ£^*#—mÉÝÍ­ž¡JaÆ{e﵋ÜÌ0ßžêã¶ÛØ@÷îEëˆ-CÍ@uö?3󆄴µ˜oª¯®ß¨OhÔNTaöìH÷M MñüT‰ã´Nœÿü_ÚÅ3‚ .ŒnÙièÙ’¡•fq:.K¬¾¢•ßÂ,¿…5Â4 \|*pq•yu]FÝÉõq•Öý<íWåMzí–ì½P­$Ÿ7}Ô\‰Æ'%pÍ=öÞ¬r2Yž…K˜óÄ5ë씪¾›Ö´Ç¸ø㪕Y­‘„VÃöȆ}œê;rUÙËAC×–Ï]t+kQùkõ<Æl‰[诸ZŸ9§u½•Óæ¹/Ñ©T¦L-ƒæ¬÷³¨V»•gL!S61wÒò²Wõ«m&AËÖcņùZ•¯VSt}çð­¶(ÌÝPm·É¤­7&Uפی›ÝfT÷ïÅ3‚ Èï#ˆòo/(»‡ƒºÌp¾—ßI§Û­½nì%¥újr±ì›¯©×D*—áÿª€?ŠgAß…­Dd#ƒ«ôõU%5&<ºûšÕ'& n¯ö\œáw4J«b9ùÜ5.“†1”¸Ÿ/ä+¸, …ÎTaü„æþaP<#‚üä{£%å³nù´Ò‹^4m˜ýÿu^Ó­¯Î ëía4MDÅ¥œºöóçô¶¨”Ä\ŸÖ}Ãàvxæê™^ìÏ—U£û§ôŒMÈ6wè¥|pßxýŸÚúߊgAߥ÷ª½W}}½Ÿˆ¡ç¼ùʳÏ.ơݴ“¦ýxùúÞ3{ÿö‰ÿ#(žAò;4ñû@ñŒ ‚ µŠgA/Àÿ¾Nb ›,xsaÙš#9ÊmÆOè«ñÙµþŸ-úó¡xF©®äõ‘‰£æm8Vr@p¯ÓwS±8mûªkfÇŽÚ´y‚W­¹“¼\iÝyûÒó§:™)"8ïa6òÊ©+=‡¾ïy©OËê1׺ÿç‰)}Õ¹Ãë ·;\Ø0oöÔ9œÁÿìŸÖï·½ùû¡xF©NɤûŒÐ›Ž¬”þ§ßÇeÔñ Ÿ#Ú³—/©UDiIŽ„­*‹K'ß­Ib¹¡^]×3d¢üãUzaŽàß<Æ:ûÉ}¨ßL›¸\ôþ]¦¾q]Ú§›/òU´è…Ùb : ž¹}R\±è»¶#-ù­–‰ó“ojÿP<#ò'û$õjŽÁOç2éÕ‡{ª¶ÎÏÊSêÂÇÉ'‘üß±­§¼xúq2çq&‹i©@×1$ºøÓ7_þù¶}Ón‘^ñjã™ÿ¶€èÁÂsÅçvM¯© â'V*’Á®Üuþl]¥mKYá3ôáñøuÌ_¶#ÿÏ‚üÉ0É“ÐÎ^»ŸÓ$ŪmBŸUçjŒU— %ö>Ѷ¬¸›“E&ÓOߘâPúœJÉÙØ¡CbŽIY\†¸¨4«+u Ýœh`Ý)*n_x{l×̨éóÖi Û|v/£øÕŒ¡}â®d2ªƒ–ì‰éi¼l møöç>ë÷p7Em¼òÆÌ}ÂéݳM<Þë7pô¹d‰º¦Jq2ÕÁµ¬ IÊ™áƒB?çId¬®#—¬›ÒCxbVç“y^ó÷t‘ßÔ;öh„{ƒjÛ˜~u©× ùé±€cµñØñÆ—ýšß_ß½÷A|Â;‘Y§Qû,l¦øJ²´ j#½›’3_ê?úÔËb ¿XݶÏÁ†mºä`ÑöÄ+ý[¨[2pø¶ôbé&=¥O^ðKpõ˜ôH/†ÿ8Š,R.º°zTЬÃ|LÎãA·•×vø™UnïéZGç‰9.?_>`Éž¹~­Ei·B<¼¯ò²ÕTFë¥Þz(d¨°– 8tßb‡KØÖ—®>S]å5ëž×±E0¤×´Œ&+ro“W(qiUëzCn½Öiå»õÈzG]Úû£ { ›ÓaÒ­]3e×ùöŠ>ûÎ.Y|˜ztA¿¨û%t}Æï£é[»r³âc{Ì~^„+Õï¶îÀ–.u0Á‹Þ^‘÷s%BL+zÍ™(ßÿïï_@ñŒ ÈMUh9?ûbw€G(–ASºŸŸ9>aÝ~É9†“–ÆEoã5¹käÁ–:·g;¸,áÜx™k«ÂøX=§¸Ê%á ²éZÖSƦV¡áíÉ-úN¸oúôX_&^6¨Ý‘:±y™~¢‡›ëXµ×xV06.6öx·Ôª[v$D¿Üêè4iÃñ¾3{[ ’O´jîc¼äAa0™Ž›[a»¤dø‹3numÝC+öq–¿)À³Áuw)Úsqá¤íSw4Yxøx³°ÀуX¬Oú€ÒëÞ#ûߎlIM¹¼áiNžÙ°}‹Ï)-ÐéueßvAb³ƒM+qòƒeƘxeõF楶4ëe¹áEF€9QØŽñn)ò6‹¬<ƒy’ÃL™D ùäb¸(óLzûj´´3ul,%²IR"-“zªL è¶^³Äg£ËžÓ·c‡[|ÚÈG͵v>S¯ÈfBè5R@1¿òƒ½‹ÅDø‰ ñ,J*Þ;îr·Ù—0É FÛÐí ª?7LÍ2tT3rÿ+é7¯#ÛŸ'&C¿D(&úÝÅÄÎP‡±\.æKAXK ”KÁ>0ÂÜÚÒ¡X(­4$Žijs)T0Ÿ@Þ$ïæ ¹4 ä%Ê&tyi ãeù‚S(PZUVÚC0ö2ø¤äÂô iªYÞBvÝ&ðþ^¡ ´±2©˜4:› â—Œ:(f1ˆÒêØùÌÓgˤv]GÛoh2gÉŒàn S¼¥l»èæVlÙѬŒbÐø¤‘QjÆK0Z1B¤’š‘b§}}¿’Û–‘ôMuê–5™¡Ùð“Çræ<Ú7:béKƒ&ÊHÃ5¾år½Lq¼¢¤¥JGU[„ãŠÍ" ©SŸhsÖë,ЍF.Ë*̆Ôå -vûÃEúfõp¥Ž»WxX…ôc®Ý'jîÂɾ^ÐÿŠgAþ\xɪ°^‘ÏñïF°?ºŽRâîĦ2ˆ˜‘IÄ_)S¿]tWë^kÂöØôômWML% z0À¶Òxïž×X…ƱHþÉ|SÄŠÁŸ†KŠI§Q úU4¶†žƦñ¬¡Â 2œg^ËpýÐÞ±SÜû OŽvª‚bž0±>“ûI#eëG³¡¤PB¤øW6»&l5È%"þgWœwmÖÏbÍÓÉS˜Ö›Rv,Tù·ðc÷Æ‹Šˆèæ¨s?ñ…àÇ0†ƒÙÄçO"ªÌ7?RÔóþ¡}»WÏäü&7qËÕ/šý¡xFäÏ%á¿ÊL«ÓÁIqÊU,ÿ|K4lÛsáeÒ9!4bŽŸœX†î=#X{ãpßѪ/ (›gàÐF^|øp€m›¯6ÊФ %eß5é4GÅpE=úõ;jä-&†vŠsÅ.]`ZwÖVÔªog˜ô[¹¾ÒÉÙ§V$Ú†¸µ2¡9û‘×rpª¼~æ¹Ë8W·©•hÙ·†°ªÄ¬¶ ½^µ¯pb?Õ¯6¼ r©vôÔ”^¹sN kþrxúí‡Àö¶$Ïœ‹$ÕFĨŠ,TÜSM¡|çwŸx×odÇÞµñšJ§ˆä2Ei:ícÀ~,£™Õm‚%o8'èTQQa¢óÆ^ÍúŽlÖÁ<Õ|êÝ"!¨þßû(žùs1¸ÖÛÄ. ]Þv²^Ök¹€åˆeYy\ÀË.é³ 7¿X‚¼|ÔMbW÷26'|¦[ƒ—ñ·ŠA˜™^Œ›p«wÌ ‡-u ‘7j]W½l¥ÙÔyŽNÁƒ´%ózXiñ²ru<šå‹%ÀËÉ\›ÏË,AQ~^‰·íÜÇ­Ù?î¶Ã¶ÄÖ–¿?ÿøùÙùB¹aËîAî ‡n91½3öz¿ï"Á˜õQº˜øn¾PR\˜™]`¬¯VSïîÃúQaMç™±‹ãϼîéÙœ „yiïÞHòî óØ9ò˜ yøÓFv·è³îè°úöiKÆÛ©H„b³Æ-Ø‚,ˆs33‹„Ú\Fqf‘H".LÏâijréü¬¼b±Ã2ºlíé‡'4²›Ô# ÷n£Œñ³¤ ¼:™|lš‰“ƒÒ”]s'5ÖêÅÙÍ™rqzz®UGÝÔ˜K}³mÕqM-kÓ7±çNœ±3«cÙDX$•AQvŽDªF­„—™ÏIù¹™B±‹ÁTQ•ñrR’“)¼ä‰S[<¦¸bnÒ¥sî²S˜·¯K¿qæ..ÑÎ,˜«Ñu­Ìsv^>rô¼U=K‡þ~NköõlÚuì%I‰„ãhS¸#|¸Iƒ•­4äg÷\oÙj¢ç?øø/ xFäÆöŸ²œQÿÐËÇ÷õ:öß¶]õð{Hy”ðN#pþvÊ›ìÆjX¦~‡ØE­y÷ò;XÕñÝû¬Îî# /â¯d·è88ΨmÉõ«¹F.ZŸ|Rz®>ªòÚD“ùqŽã؉–{Ïß}pî<Í Q»Ù«lîĹ ©üÄ÷…Ž…II–ãÈ5¤é¼¾në'®ì=túá¥ó:Û…¬Z~-…ú2_¬£¯?{ÿ5Ç}{÷ïØ‰c¬ù—î¸47¼¾‘o¨ŠÜäOO=yýîcL|RZz]F©ŽŠŠš¶Q“Ö-MÕ¥èÂ.Àÿ1h’œè•ÍêÏzQöÅVAnMÑp­d%E ­×ëPJ™댺öygÙj*äߎ•õõzÈÚuwœÀ{,)íÆí,mºõé×ßÖÚLaZÔåÄß?²eÓÎ}GϽLâ5Ž˜ºy—#ÆOÝÏB]¢6 ðo@€Qr/ðàûñ³ê ”ᘠüO¤T>Þ¿*ïçµcÿó<ñ¥Ò13cŸ^>Ì~l˜Kïšpq€r-”ª‚™úüàbg5W¾W6fnü³³›g±rÍF-÷÷ÜAFPKtTq¸^ð\Ó)ò]`á F @ƒHy¾9ä¥û²&’•™î.³æKÄMJMOE9žÉ~*¦£&‚'ËscC'ZÚïøHñϘ9y¬š(ü9fÊ£ãú;©ü€¨Œ´Ç!®woö¸¹¶êTh-vly4/ m¥5¹ŸŽl¸žW%âF®ã–W [„õé•Ê|±ÖΡLz–5è4ixßÎmššh+ˆÓae§ýŒ}ûôÁÝÈÃ…Ýú”%¬Â‚˜?¯ÌëÞmÅÃÜÒ/Ju>|PïÎíš74ÒU“ge§þüûåõ½ áaa'N?üÆ,úóç÷¾d±ÚÉÑ„Pt€ÿKÐ  {š¾xÍy·?(ryÛÎ üÇÏ~³wã£Âçê,Ò¯?L«áþ=ò¾vóºÇ,V· 8³sŠ…b™zrš„œª~³Nì‡ýÄ9ër½8»+pýúƒÌ SáËx¶Î®g™ô¬Ñ}þ¦ ³úÕ—+ý¥Ò$äÕtÙúMÛ÷ïµ1õűÕsg/{›[a‚ð‡ AÐtû»è_t¾”Ã~žx,àjb§¾uøÕ–e> Þúºð¹ùäQ  ‹å~]w¡¤>¹‰÷…}SKóÿ1e3[·@[—µ©Ùh*+bXiw œq£ä•j¿øüÉyVuüÿŽ.of¿ø¸ëÍ5Žv³"j¸ÿh Íž®½e/…ýf?O;å6Þv˜ï–Åi÷6ï.j ÚfêûµRÈ¿+ùaØ“â!É^ '™W’žKÐ%å¥j¤PPeYÏ׎Y]2Ü`æ…Ó ,mŒÁP±ô Ñ: DBí7ª4ˆºJ'—Êa{~±Ÿg_ô‹=+æÜ:ÏY>ûWøZ%MúL_¾z®½©¬ ɲ†Nõm jd®Rñ2Í4µXý¹ì‹ß‚ÚÊqùTƒ¹Q/Ëu·”÷ëá¾KVn{–Ì£¨ì ´n«ž†M˜æÔ«ž]5Ô(h2Í'Œ6ôùÀyþdÓž×΋͹l#Kw_§<Àµ“Š`õ+¬„Ã=5_ª¬ßŽŒ·ç§žß¼qÒ¾“~úÕº‰Îz¿r÷ÿv¾ãþnjÔþ™]NŸ_w-Ô­‰@;v^rêò%CŸn¼Ha5ä©b¥Fm2Øiï>‹1áÁ…Ù®:¾gZ[%Aª™Iw|õ˜}±â^“Å”j2~œ‰ßâ‚& ñ‡n¬²ê©TéÜd½ Ùô¬h@y€[gî  ög‡âÑLî×ÓÁ÷J &xXQ­}®L^Â5ŸÿìœçsŸø{û½îèáºÏÓ²Ž`•Á¬´'†uq ç>ÙoW×o|9êÂ%›üéå~;3ËvÀÚ‡Ü{‚‰9ç3Àü–ïð™ÍìF$ïÇÅÙ½{ûÞÏæúnVtør‡ð#vÝ2˜ïáH -œjÛÔÌ \[[ª2˜¿n­d;ïÏ;Êørï˜ÿ½X‹a=FiŠ^ï¡ðA€Ñ!i6jR#ŸÙÏ9Ïßo{2{}« Íws?-î¾Ns˜S;B~ 4qfvFÙÝ MNM[UQANŠd&Çùò³ÔÞ;÷ÙæÁíÒCí YM :r¿ë踯¤.ŽsÛe©œÄÄߥ«(“ÏyôtkølKA÷Ä\ˆ©7m¢DžÎÎ[{§Û²¶ÕÐŽ#ïÇyÏ.=×>-ÛQ‡˜²¶Žš¢TnÊ÷O_•,àï§fZZÿ¼zu™Ueûì¬×AöÃf_Ê(ý§›=Îÿ,‘41¥Ùâió_O9á!¡û µJ&™þhËŽEZÜÛs«ôÎìPÂJ¼uèQÉ Þ Óê­uË‹?íbi»ñmÙW¥TttÔ¤²~”ýMÄŸcÕ>6âúºj•þ&²?„ü7Ì5¼äBJQE:÷ׯÒ+{^Ôʾ#¿<î¨rqZÇÞJšy³ÊY?“J/Ãô«³l=š?ÛÜM€_FzÔª>޾÷‹ú‘Ö46®«DK‰}[êJã÷»G¶Ë ?Ø7\GEoM-œêÚÔÐ üGÅc¾¹d”Þ"å¼ÛlßeÞå2Ýú3”õLô4”d¹¿S’ƾIZ¯¥Ü!@ƒ7:µõl§ü&Í1{7Þ]ÚÊZ¾ìÙoöe ýÑ›S¼Ã•˜^ÇÁƒíúÚv³²0­«Pú²ªÜäw·Ž¯^ìsòCAÌù¶{Ô”¾íŽ8TK„þ2räÎ>I¼þÀy‹¦°mk¨À™.+;þééÍ \½Šï·}êjçgË›UýD½\󑽕÷ìûU8øÚǺÃ÷µ~ ÇZëýAcH’ñtM?ÛR{kuk§ù³œ†v6S“,œ*ó÷çÛ‡üÎ^{± }:óÉŠ¾ã-^tÐâ·?î7=&?*iØ8Ï›6ÆÎ¦©.§l^ZLÔÅÓÏM$Äõ\,gŒ»™ÿÉ™çüÃ㌩Ë÷;I¹¸?®hÀ€ûJ"´ÙáWð …Œ¾ô¢$µÈ·íο¾”ªìèKDÕN3×-wµo£S°¢°Ò¿Ü>¼n¶ÇºëÅëÒ†CÍžEL6ä߸àSȘIŸ8éY¡ÕïÅ.Cº˜«s³²¾=:¶nÚ”•W îÒNy̿ԢçFû‚ô¬Õuº÷¼I­LòÛØ²—ÿ­½ÞÎÎ[£r ÆŽÛæìçúdqãÊŽ!Þn·0“ž¦ÃW,×ÙH¾ ä1Ó?ÝØµÔÕ}{TaôM82jp›Wg4à2É[8Õ´)¨©øOŠ'Õ|Å{Ö BùN„¬Ÿž³JÒ³Jç™k½]µÑ•)ÎsS>G]8z0d×U\ "D‰˜®K'·—9ûÌ„#××tì­\zc™ùlǶ¢îëÌ&Žn(x  É·÷¹þ®e{C$‰)uµ¬Ãà‘;†[Ž;ö“óRêñ¹/ú-­t-€Œ±ìõGí»¸y˜a©éÑ$Ô›Ø-mo1É¢Ïö¯¯½Û²>rÎNy®„¢õ‚¹-÷ϼ_Tß™ý$عS°«f Û~½ºZ[¶mݪ©‘Š$¥}Pæ“•ÃfE&¢nx6dr3…²K’.«g9fÍÙ~vKm».¾“ö<éèÔùWºoë§ÝAV gY+õô»|È¥™|Éx 9‹þ-8Oµû¸u—º‘¿ƒÍ»¾áè§®†¼7\¬Ÿ—7K,2Ÿ<Ú¬â÷'ÔÙ3ùíÓRín Ú ÜŠr?ï™4ãZq-*½±çÅ+…-* ÑdtÛZ{¹w«žÞ¼–qÉcÒ¾>£tø…°ÌOßÙÝÈíÔ…Õ½4K¾)š¤¦ÅP߈fu»5q»Q°ðB†vÙ÷‹µe;ù^>6£U©¾ÊÙËßjâæ«f -;¬)̱o6>ðÜÔ^–ÿŒå%pNIÉtZîÞX¦ÔWE—©×qò¶;]Û:´šp² ÃçÝ™ç´gð¹qzåÖ§\8Õ²)¨¹¸V·T…’#·I-|.Ùi󽈉\BÄôZØNd?¼ü¿¼MSFû >h)tÍ^n½d/ŸàìSOúŸÿÑs°zɦ2íÞ¦]Eר´tfD¥>OÚÀÚªÒ‘hÒ¦c¶ï½r­çîüý6$äù¼ÕÕRï'itaË0CnEf¨÷Z`wpÀñ‚3Ì?Ï}™aÓºêiI¢ëá‘­G-}ÙóÛƒ“[Øüzã–­Û´ëЩ[ÏÞ]škUÒ‘@^ìá>Ï è-W\Üçijk<†J‡Çv=6–¿Kü±kéñ%6£øWM?u°LÜ,‹®ÖÍ­ŸBÄ¡‚k¤í}3y¯¯žù-ÂÿtQ£Z[ç¡+…<;ËIx[ê Ó4ªS›ëŒGë^.Nˆ ý÷D,·áÚZ€¡Öcí¹m¯Ž?_Ф"ó‚×úǃW·à¿zÊöÚy¦Lz.&Õ`‚ŸÇÚ+? þŠcÇÀº“NÙŠK¬£)ZÎ]7pKßЂ|õ="ìMfûæ•ÿÕF9ìV&=“4·çäÓÆü 6$Ù——nx2|UÙßx /œ?ÝÔð \‹[ª|9ߟ¼)j®#ÙÕc0·ô\]V·~%QµD M¥“ëåùÝÙeßk?¶¨B‡•t3°¨û:1×嫪©Ê& PÛ½-?»|9{å«…Q5ôœd4Ão ïÀOWí4¹—üñÃ9!6òIb^kþ)? ý‘û«/9réE®}0ßÞ`?öúÍ#4­ö#\<ç¸ôm ǽV'çý¾Uç‹â„®[K%K3´ì–º…yç_„”{#øòŽ|.øQ´nN;¾m´iJVÎCÔm-øî_oÛñlæ*G5¹_Žû_*ª™“èêf§Sa%úì*ï÷Ï’8ˆœš|5®ï¿ïoÚSÁsDVÜ­]sív­´œ±s¯wÿŠCå~9½çiрѨ1M*ow.iÜ··¶÷ÎÉ`’uæeº£&ÏÎç”û;ÙTÚ“Š\«É#õ¶öõyצû ·Xq›bλÅ‚Ë÷që®^a%…Ù +'3§dHLR¬úZ~f¾+éZ‚aíÁ¥ž¾ ‰ú#ÝÛΟY0îÄ›LK>Á Ç ­Ï§á“”~ûúŒ5_ ¯ ï8¹¿f¹²&–äDa>ýö:>›hðoT%ÞÉ•ËqSitî®}äO¦ÀïçÂßfµ+éQ­†Οn jx–JÕP‚>?~Ÿßx&¶ó`>,¢DŽŒÅÄÑúA+>rž?Þ¸7ÚÉ‹sÂ>/î”ßÙ M”ú»òè™L¹‰ÏÏ=zêâ{Q/ß~ŠKÌàwq÷ﯱ¿™DþO³ºRKKþíMŠºêìýtAbúV-WœÓ¤õ»¹tsYŸ}ëì™s—¯Ýм{?êK*·q“n®¶kú,àvèÔ†e+´Ržœ~Qô\ÞÂF ¾ý$´k’¿Ã&ɯßüÊëÄëþ4ô¦} г³”ùØ V/x•?(àæJË»³Ëz²¹èÔ6QäÒ‘ËýàEbvB—*•Ür³r«­Kofâ“ÅÀÄÜÞ²²~MCÓz€‰,\rŸ®?Mb6çY‹©Ü²m]¾™SREKž½Æ Z›óí›OLYG©x@_F#»6•öÕ¡Ð|@súák×à}¸þ<™ÕD½è⻚]8¼)¨ÙX[*ºjûæ$² ßÉ”cCZöyäë5uP[?¹è  @ƒè‘l8z’ÙŠ9ù»‰7ÛƒŸy®¶*Ý}Æî=“U†™ü8dîTÏ [|úu-/#9ƒ½—ýÓ­b¨ZY-6]JAªh·”›W­÷?S2é8„ýpå 03¾G?¾{óʹ𣇎=ˆ/=^r„sï9M£ÖY•j‚ûè}qEhêá®Ô['$Ç¥ä²÷»ÜßÔhTO Îd%L†Om±ÀåAÁÃü/&t³/ŸlÒmÙYÜ}Žã”6\.o‘ÙCV¥TåbÚÔjëÆ+ûÛ³Øâ¥ÆMÔ*߈kY˜É‘…ýÀÅ>‹Ë&š¼**ëè«ðŸ ­ô-ãë¨ðýmÐ$ä¤Ù_tAÖÍͪô—¡hf®ZéüÐäM,´Éµ‚†¬¯Ï¿eõÂ$Zà çO75¼ cK%n<ÖÇqUß=…›å¸ˆ#Ùå†{tëdmeiiÙ¦‘¶ .у "HÂh¨s«9Nù÷ø¼{Ó½E[Ú|ÞTxÖÔ5É‚úE$ÙŸöë8|ÏçÊÇ,#/§:îå'ÈùwZ©1ªýö¥Ð¥5LÛõe?ÆÎYŸüì¨ëßËŽV~Λîͬ_¼ÍMüøóÏ>0'#‡÷üÈ© xït1½®Ö£®æwï›yÖÿTœÝè²çŽËt_g2~|SnMCEev ®j¬FÈû¡ÜoïæÅjÙbç&ǖܵ®Ž~Aš(‰³ÇcÇø‚äØä\Þ£V¾¶—^×[€â•¨SO»=‹³G#¤°¥sÁìh¡/þ›‚^…²¥¢«Ún<¿îg73¥ª6~½¼t€ýðÏA½I·>†8ŽlÍ«%€Ú‡ ¢HLÏÎ¥“ëÈËœQü¡À›‹e.l-ê¾®Á„Ñ(÷‹ñûþâ^¥Ó³¸Açÿ†ôéܾ¹™‘ž¶ºŠ¢¬”„xQ×þ¹Ú˜x<©†Ùu EóÁ+ÎYµßr`HqôŒÚ´ë•«wqç}̬Tî7ˆ«t1A'C«·[O™«w¶Î»¶!ô“£K©îìX?/û+ê“—4wr¬ÏõL´ÈÌŽÓR4n¬Fî]úñö‡ 7Ã?î3Œƒ•SrÙ‘$¡Ò$dJhN&Ÿ &dâÍM\ºÔ씎”"¾pjxš\3÷“Ñ6û}xo]á‚ fü“³ÁìÇâÉõ.ô÷›Ñ£’f&µD]³—k/™Ë'9i)9lùʬEá·…ÓÔnGA8·/Üãæû²hH¡ÛêóÝ[+óŠ:,’û/ÝóJLÛnͦ¡ýC‹šF¿?ùcq㢾Oh É’­Dï£fÕ¯†NIª‚¦ÚÙÍN9¼ð1ƒöEOšß°hU`~;ãw¦¨û:F}î¥Ù©œ´Ig3ú¶«…7ËH‰<û6³‹}¸UŽKf:›_…d VvzɱťÊ•BÁ·‚µ;&—šÒ1YÄÎß´SÃPjæè{ÒÑûç‹«§OŸ»|õÚÕkwÞ§”'ûM輞gϯ¼>£YõÝG j A4ÑT9ÝÙÜËIK™×Ö/|™ÑÑÅžr÷u¹_Â6Ý*ÊÄJƒ÷ìóhͯþܤ˜$êþ‹ÑT:Œ²”(Êqœ&¡:… Ä”ê*²Ó[á;Oc³ˆðv؊휇jîÛXp›áW[ƒŸÍ(êÎ.÷óqÿËE‡RÝ]ûðê×A”f§2´:íš‘«EM—>9òfió&Õp_1EmöiÁ@âÇÄ’ö ¼å°Ç+PÔ®ž¶$5!ñÓ¯œÊç'‡=Zñ€¢V©Ùñ…ó7­ÀU"®bÖuû1ƒpšÓ||tãâÙSa‡÷Ÿ|R¼Qþ}ųÿ<«çëÛU×åºU#²[Aøç)¶sª¹·0-’îáÚ›ïh¹úýò\qõ³b¯I6ª|[Ñ1“_Ýå7Âÿ!Iuƒ:„-ꬴ¬’j7 í¦z„vKtïÆ—l›†B;*k1iŒÁFŸ‚+?ïÚ|Ñf+Nsøìwî¤hçÖ•çW,R³S1Þc[’‡÷ ?l]kÖöNÞÉ´„¦¹vÉBxúäG®µl%»‚œ¸‡Eɱi›k‰ìBK~þ4!×FŽÿü°R£ÿÈiuÍ5KfGÄÎ_µÿ)1EýV¶ãØù~߯¯ÙoÖ¹Âýy‹Ï%¯°~\zÙ¨=Ð ²d-&ŽÒßèû±äÅ~®]¨w_——ó­¸Ù †™ÿú$֯ȃE¶g a¦'þ.’Q.uÍ;½N‹î&äftÁЛ}ÇÞ{6l ´=¶¤ÙèÉæ>³ º¼Š?xÓײ»-óåÎÍÅ]{i u¶ªØÃ]њʈÕäië9øTá U~ìp˜~g.õKÊ¡×ib¥Kg¡7¦êó¹µ[Þ÷kÇ‹BI½M”D÷R®a‘‰Sõ+ö^ZÊ£c˜Eú•êHOÄÎ_±—ºÌÅdòQ`âfîÛuϰߑ‚Ê÷Œ{g£3ûµ©Î›ÛP… ¢KÊlÌD3ß¹ÅÑH}°³%ïh$˜Ì”L¾ôÜ}Oó¾ëÈÿ§Ì7gn—êZ·Ié*4 £ÃŒ.y[0ôrÝŠ+Svtçuã¸'n8dj›YSîä$‡ùqº³“y´yçÇ¢êšÜ‚_-¢5;•¡kô]>£Á©%¯ ‡ŸÌ²¤ó­em)ÕBç%\ =½½BÑ+R¦ý»©® .èó ïʺC»ò»]Pvôîõ·‹*Õ»÷3©–ÛÛ׌œËŽ4±ïóûY¿ðâ^£{ãÒÕ"¾pþ‚˜!)Å^ø]‘d¦dUK‚æ´hjÝ¿9r»`(…o_'µD˜„©çÃ4—âËxR²U©q`Èëpj *¡?_¸›ÛÜÇŠŸsÀmvd5mð…ƒ•xeÙºw]¦Žh§)`½TΧ}s‚Juïר_›2÷A–l0ÖÓjùÄû«„ÑÓíoí«.¤›…‰éÚ¹vq~1¿ÁvÆYÿÓ±ê(nèc6y¬ÿü"Z³S)©Æ3‚§ïm¿æ]áðsŸ.}å.„Ín'X5gÞÏ[kGØyF˜>­T‡p²-';ê¯/ìÆí—çq‡C¼Gå~Þçá]|ƒ¢7¢*ÝHÖ"æ­ù /Ø÷äq7oVÊmÏ3ÅÉzÃ'4+{3?_8¢¿‹+ë)ÝË5ù݇¦\uÔÉçýþYržLFýÙ°!@ƒHcHÊÊýñUS²f½ÌIxÁ=8È£E³Cí¬]qÕÏ wë2â$×ûôý=Xٟ½Ç/ðžg5ÞÃuˆ¾­´¥øÔN±Òž‡8õw.½ø±nåï],Vo¸ÿìuÞ…§©ãvô·‘ZDjv*G“i2ýÄ©]z®~RÔ—LÜ™E}W˜táhß˦]s3#]U+'íç÷¸ØÏ¯ï]<vêAŸs)bzŽ›W…4v½V° ²#½Ú6¾·pÝRç~M ïEÇʈ‰<²~Žûš«%=LHuZ»ñ?ªì¬ˆ¡"“÷3=ý’GóV,×ɨ°ú“™þùænoW÷­3‹Æ¥·\8‚Ë#ê GÄW`††õ sr¯àJ…ÜkÚ÷~ì2´sS59 zá ]V¯iSÝü㛼„È`oß`ï) º2ÄÞ¶S»¦†u$J߬%;þÉ©­Þ3)>îQvðì¥h2hø0t†ú/ÞÐtîÃÂ@ñý´WßÓ^’Z¦ u” _ß½ûVœ—e»úíéÜsf”° [½òbn b?Y×ñ@ø®7ð#Rfî'.¥õéºðfÑ’ú±v2ûAÄ”uôµ”¤X™©I¿¾ýü] S¾Óðº!þ_˽,ÛÛ­ÿË½Š‰Ô쀡ÚÕ÷Z¤Î¨~îaÅw¼!™Ñç¶.<·U€?—jÙѠ‰ §CG_XöÙTÔ:äËÉŃN.&òuMt•hiß¿|N(wYßùøÁ‰üš‹£IÁÃÎ;.¾—›ûj{×=î2Ú&ÆÚJ´Ô¸÷¯cÊž]ª3`ÇáifÜOp‰úÂíXÜhäB»eÇ {Ûûz>pöùÀ²£Ìz¹¬LŸŒ©¯.ìXÌ~°ŸÒ•´õ5U••dÅØÇ„1oß~/»¬µFl_×=p€Ð!@Ã?AÊ|fø±Ïúoz]òZVÜëÇqeGSµ]nÿdåÁµ[ºêD“5íÕËäÙ™èß•[úÏ ­ÞµÉÍR…g]©½×…çM¼F[u=±Ô빿bÞþŠáõGºõj JN¦é„±ÆþKß–y±Ž½«ŠÀ{UQšÐ[º…¾ê´ÏËyšßõ•_Hµ½“¯¿÷¨ÜîÄа ˆ¼¢=ÌÞëB©ÛC§~~QþØ„µËŽîÕ÷ú!*h2Mg:ÞÓvÕÃüSNé±ÑO¸tKYoXð¹í#yÜn‡CÔŽH¯À ÍÛOz%ôYr£J-â˜I±ï“¸÷%*ÙÜyßñ54E~=„4ü#Ä´úÝa½ÄcÎÚ³+ÞiP¨§³——c3EzîÇÚ/]õ¡É·]xúÍü”èë'އŸ»xéÒ•G_ùÝûWÑ´Ë@Ç Î“í-T+ßHØ­¼ÚmbÄÖµ~›÷ž}•Âk<éz­»õìcÿß{+ý¹ÖGÒt䔦K§—>M =Ü©­Ï?àJdfGPt…&Žë¯ žs÷ðæM;÷½ðšg‘Å5št4zòäѽ̕ù}¯ Uëg? ]³Ä'àÐßÜFQk1ÔyŽ—Ç€†òË9s†Z·•7£mVÍðô=ò¼Â¤„Qoe«æ6“«ìhK䎯Àô:_v&$ø@ø¥È¨×¾§ñ¸¿«b¯ýÃÃOœ8yæÂÕ ·,E¥é€qî³g8¶VClÑ€5„DÜÄó9˳:¦$kœÀ¤Î˜&×pèʈ!KâžÜ¸vçÉ»ØÄôÂ: øøü1Llg²œ›'~ùâåë÷_’’SÒ2óè’²J*Zz† 73×W¢xÖ™&kÜË=¨—{@Æ÷×ï?~ùîsÜÏ´L&CFNAQEËÀÔÌÌÌT_…Ï•‹T— WâÆÓ³¦ýáD8Dcv(‘Ðh=Ü‹ýÎIúðäá“Wï>~ONËÈaÈ(ÕQQQ«kÒ¬u Su~׎–E—o4hÑÁA w~{~7òáóèÏß“3òÒŠz&,Ú¶n¤)]館-™6q¬ÀÊÇ+ Ø÷BõvRõz/8Ü{NÒÛ»×n=zý9>9“H×Ñ1iÖÎÆ²‘º¤à§ÿk}áPÝÔú ,xñè l]VÚòß–š”VsÛ ìÇBBrSc^={ù&úݧo?SRÓ³Xb2ŠªZzÆš·²0Q•@« )Ðð¯ao¯›vÒ´«°ËQ[hâŠ:fmÙjž.]Z£¡¥mCËjž¬°ü³#®dТ3ûQ=S£Ikš[Û™[WÏÔDƒ˜’qû~Æíÿ|B¢¿pþƸ1yóvìG7a@Ð @P€ @4Ð @P€ @4üÅjÿ^6Ð @P€ @4Ð @P€ @4Ð @WÅׯ_wìØ‘––&ì‚T£££¹¹¹°Kñ÷A€®Š«W¯úøø¤§§ » U§¤¤„]ÐU$!! ðB€ €h  «(;;[ØE!@€® kkë9sæ ;ø«õéÓGØEø+!@WEݺuçÏŸ/ìR€ @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4@¥ò’¢ŽlðÛzý}–¬’œÉJŽÿž&Y·A+{ïsÚ)Ò„]>Ñ’ýþ°¯ßªm75>{¶¨‘¤°Ë# 4MAA!99YØ€ Àëw”ߠδ§ìØ{oX%Fþ«Ìß/9J™ÕN‘!ä"Š C‡¹ …m»ùo§ÇÔÔTaj 4/y?.ùÌXzêÌ£Ÿ„(<ôòƒVAPÊz³}æ¼ýO_ŒÖ¶î×kärßþqæú ;ùi%e©¢ IVNr’üäÈ·Ëãû«õ».«ª A§±_Lúñ+›u9öôX¹¿s{žùv²§‚ 5¬ßÑ'7ø¼ú!WAYN’ž’Âг´Ÿ2Ó¹‰,­zJž’¤0)2:°Lí,mfüqǦö—:|¼¦(åQVòÕ¶ÌAG.l°×()]V¿ËD—ß粄Xº²X‰§Fö¹ìvquKiÊïÖ”š¬œÎ @ $óy óô-¡gŸübgžFÝ:L_=¿»ëkØÂ~‡Ž\~«Öªßõ+‡é%•J-zÓï¿ZÝ¢ÂV=çõÊ f=%ôz–}»t²Ñü|îLþø²fº¶´³laÿ“ïDdóàj燴ëénÖåXZÊçРΡV…AT²þ8ÿÃÏ4]6ôèáÁ*ìˆPßssëÎÒ&­ü’N~>ÔE¾h)gv<Åy"ÕlÁýëKÌ9'³s?øµ0tbµãõµü¿%÷<,}‰À5yßO¹tì»1cȦcwƵ¨“¿Â0“£‚§öïgº}Òñký4«£äg¬ÃkmY³gž•—“ËÌÍÍ8š//æð¼-_‰ÁÜ}4*¬Çò|üšU|YXrÞíò:ô¥ƒ[UÞ­t1z N]sïÿ$¬Õ,©FS·EŒu]Ú¦©×S¦æ`ïyÝó÷˜uûÍñˆ7›5ddÛ7§G¬‘RrIÍFš¼ \ö‡ˆÞÊÂú¹ÿ“ÔéØÃˆKK^º\=S¹üg¹ß/¯›>oû½4YY毟,»Yk–0—£‘ìw;'ñØñ Íry`×»w¿ñ!MÆtèºãÁ cy¾Õ ðØ;óý‘N ö½¦«Jýþ­Ò}c8%ÌÄȳç]ø*¦$óë[ÓÀ1hÚî9Kfg¾qhqAÉxü¾Ãî ‹‹œ—pe¥³[Åwy»œœ¯§Oš{è#CžñûG¢d—€óëtBç{.¼ÄuíóÎÒ¿îl^º*(ðè³FAŸîMÑ+ZÓ¯-í×èìù‰LqæÜ×l˜ÑI]¬âÔέÑÜï2Ösß‹\ë±.. æØë&_ ð^°é’ÒÈ›Ö8µŽ-÷'g}wº–Ÿ#×Å•õfËÄáswÝÿi±0ÐöÍžã·?gÈ÷_¸q†þ½];œ¸ôä{¶¢Å”'üÔÅ.ƒ­… ¤›LY°ßbéE7§ý=ÂF舱’®Ì¾¯iÐÝÿtË-EézÖÍ\}tduÄêÎC4JUÀ0¿…ûœÑ±5ˆ;õ¡v / "š‡ªFªËÎe¡çàL1ˆJžÚOCB±ÕÁcDŒ×Él†Þø°]yßBl¨éÌy·gYøoÒÒ}œ¹T¹‘$MGÍ´ž;ü¼÷֗Ö4–üÓ’›NZ3U¶ügüs²cÌ#DË\›ÏÑ+íî|ëÎþZ<žÚPš–ýåШæCÚ¾a¾89ZOÂhtЖgZ¬ù¯Ô}ÖαKÓî®vptõ4ð´=Ÿ·TiìCª+vmˆzél*þuŸ]ƒá݈áëÍ6âO–u±ôúívñÞùÎ* ÂJ½îlµ†fµvÓ¢³¾wøÁúòMª6s*¼Ë¯Øe6jy±F:øI¿‰ªÅ`&5ö{–|ÿ)˧ŸÜréNþtå6S|—§D]ösÅõ†n<ºK_&ã]„Ïðs:¿a>œ¥t¨üÔ²û{l ~¥m@^Ãq3ìõÙËZ­£“ëƒý—nÚ0µ™T^ìîò’£Ò¿Ü±—×Å¥Pâæm®5[‘#ÞÖ)pÈœŒÇ›Æ9Íúîã?qþWŸ¤+K\6:¯ÝsCk´ @ N¦ù¬]³¶ZqÚÙ%´ëA›û®cÎvßq·oŰCSêàÞëí¨MgWþdïlP¼Œ³ßì\Ý{žÛCû!@ÿ¼ÄG×ïLìë\!# Mª‘{ˆ÷ÆóŽNt>Öá àRèJÍûÛä?ãû[£‹‹% TÓÉL¸}ì!:V-Õ*®Ü´“!¹}âJ¬Wcƒ?,9MJḚ̈«ê/lï¯NçYQÊ}ü¼Š/öH ™?o͆Ðç ý?ÜwÑ/\XÜëG%ÞWRqË£U’—þ+ýŸ´¢ï °~ž[ä÷ZÉ~ã(v eKèÚ-œÖàÀ¼%›_ [Ö¤èëÔhÕ©¹û}í.M]ß>ýžc¯Êç- V¹åÛ¿Ê÷iÊùÔí=ÙZ²ÏþÀÈUM2½V<–è}l^'•üo“&ßbÜ„.”·æ‚›#;æîË ‰®ª2œe@Wí¸õ·´`ë‘¬Ž‘¶<ûÈQÞØvá6σM¼ý7<š4†ëÔdšOžX?`Ñ€[+ÚwSdèÛûXŽ{I X^‹kµMw…ÂQ´:ôíi¥ÉžFÃs‚FÅ÷°ëÙœ3²áÌÁÞÇ6E}Î Ð h*d[Ì ™q¸Ýêã‡OjþúѰÐ[”¹îUešM™j¶iîýõÁ/',-¬ $©‘ë¶3&†·”UÅÏ~³yd·ƒ¯hÍtfÝ<ŸCWi:hÖ† iØûѬè3œ\øÙ/ôæÄ·«6ÿ9âÊýÅ “xž¢­xxÔ¡‹«ò¯㞇 Cì°’ùjG©<¤ý‰oâaeDZàæþUFC!'þsÜ÷˜oš®×ÌD%=2÷صä`S¯£œY DÓo»Ùsbi3æC šNöhߞDzÿS1Tçò®¸ª;’EÇ>Ë&ÅõÝ”KNÒ®Nrßãøôúå>„KMd&‹wE©“Ûø\'"Õ`ŒïψЙ%Ƨ~”oÅ-÷BV]Jóýe¥eñžFæ»K3‰n ½âð%Q·•™, ½üø³‰fùm]B–ý§æ2+N±Ì[™o¾È!ÚMu W IuCr*úõ×/ï¦Ck3…â¯XÆÂÙ…ý_¥Y¼Ø’]ÚÉìîc•å9mÒH{kc EnäORߺ¹¬÷ÑûÏÒrŸš„éH×V‹œÃü/%t šýrÏQ¹±ÇÅ-¯Å•˜Û]¡üŽŽ.ÉYÎÌ⯀&!-^4Œ @ДÐäÚ,q;båwyçç[ámäymk%M=,çO¸¹eÃÝY[:pª™q'}Î5œ¹ÔHâLU?]¢þ¤mAWÎtUêá{tÿIÅœg– ²óì'ûꢓ¡¤ÉèU¾ÏÂZ¬¹¾a“…C÷©“âÖ§ó;E›Åå,ðÏ\Bxå¡ úa†!Âi9P:ñ=‹­š~gv—¡› ·¼¾?A_œõûÞÜ&­W0Ôê*ÔäÕGU"ÝdFÈÂCÍQY®#¥T¿n~EY^J¼ŒÓA'.HM'“äØÿ‰KKp&!ÃÉY©™LBJådŠ%'¹É?ÔxT…kE Ÿš?qnãg?¨:“oýhýüQ¸WÜV½º”ɺ ò‘}L’EŒxl/ó~'¤±g\N²d{@g‘ìEš˜Îþ"ªº"338«Ä×@‡vÇó;GaeÿÊ«SWM"7íçã”dþð"x±éjý‚¯nÕ˜æ½méØCKÇêõ[ux×´ÖTC4]’s,’–£:–ÛÔèD¬žý´nÓ‡EøŸŽëçð5äLÝIgt€×âúiG¢š"š‚ňÁú~+?ÆÚzcq›uxìÃ:v3ú¸ß<±oÍÅåVýUiÙ¯ƒW½íØKžöÇeP3®WG’N$ú,Þ⼿µßêí/Ç•œrm8ÞÛë?5Z¶•atÆå™<"ÈÐéy敇–άz*Â-ñȽ ;ñ…4˜ÞE‡“ýh²f½;ÔYrò^âì†êî„Mº™ç®‡-–àrÜj«·Qä޼)¸4õܰ—ž´ 5%£eÿÎæ–ÌØñ:›ýŸ”¢tù·(•œ$Øÿ—Q¸Vfð®ùÓã6>S°êL¾õ£õË-•Ò·ÕR]ZŒVÇr¤¥Øåk—ã3­*œL`¥>“µ¯¯&_î«cf$e"¯&û=tÐe”eÑñ »ãQ&º§GF±ÉS¿§æ•9J¢Œ!'x±éŠÍÇ]ï—¶ÖuŒÏÌ^ÖÑ~¦Ô>/75>ÐëÔU`pZ+iB×è9ÍNñüÁ Ç^ê>½Ú`Ê‚’†7\ÿĤLy,.€š€í 5¬ß|ÆïÑŸØ7}ËÉcf¾¹‹2÷Zhšj—éCÕNl_Ógœâ­5ÁŒ‰gZ±÷{ K‘®ß½©¤ß©«Ï’YMÔËD¢n Ã;;xEXÛHgyæ¡ßºUC”J<4º{o›UÔ]+'3—¥y^r'Tœ–ðsµ\~d¼Ko¿<¾£Jtnó–}r)‚ÔtrFÓn¢K#ÞýÈ!FËùñöû?¦uÙ‡SÙU.9‘6éÜ"I¢âÀµ"°ïš?î‡ÜkË/¨JêGyãQ[YÕƒ1FÝA+ݽۮœÔ%lxݲÉ2+úPð¹îƒ{ui.póþ§ bTЕNvÌÝéĤ[3¥?X‹%õÚÖgø=¸ænTz:ÒÆ]›KmŒ8}-nNÃz° —2´Ø9_ΟùmÕ¯´¸jÓAK‚¿„O{ø!eJgÐÙ¿\ûÁf=Í"æ}šËÄœ?Áej­ØGù4¥ŽîŽZ}Ü窶^¹ºxãÊ£e4¯ÅP ©`%ߘïbx7 ã㳞;¶Žž?üY€5ÛøÊ·q`¸}ùµ{^uÑõ¹`湌Ӝﮟ:†¬2;ÿþ•ÁäRÅ'‚¤¦ð< ÌëL(íÂ?’ÆL¼WìÚýpòâ6 y±g·^H‘ï;ÙŠÇ‘‰Ðɶ˜»{ÖáÖ¾G&yÊ‹›ç=¢¸É„uœ=<+ëW«Ô-J¸¤¦Óѱd¶ƒš“ë¯ßù–Ûº|‚f&>¼ôžl¸¶s¸äD²¡óÚ†\ßáRødŸš?îu\_,÷·üëGù¶öåUÁYErm–Fìˆé2fd§ ه׎lZØ?7++.2díÁôÞt•n ]M[û/ÝõºýTS)’¶dÝkù¾{&4ø“›XÓU»ÏüOµ÷î‰ÎÝŽ¬ÑJSŠýÓüþ“¨ê¨öXìÞàÌ —Óå·Ì¶k®%Cg¦ÇI–Ó•“S‘%7ïßɲÐÏˤÉÈ”Y¦Œòï \ìܸSK}¾7;ä¨'N˜Éo#ÆýÊÑ$ë6Ó!áçƒ>0²Õb&¾»þ$‘m®óÂJ¾mÖ¦Ï#GÒ>p›ZÁˆœK M_ŒûoU+¹Ê PvŽ”x,.åÚìþЂcþ<;}t¸Íö;ý4ÄéÝVnw<ÙgOШ…Þ¬³Ràš$ÍÆ¹Z,w¸ÖÝUùCï½5(Äʬ—[|_Ùέ´[Ò¼´Ÿé„®¤.ËmÚ|"ˆ‚¢¯³Àü΄ †¨„éæ‹Ïl¼gé=¶ó% Z¶X½©‡îzÒ™»ST Ûrþî™GÛ®z›šcRùعï7ñjxtoJºù ¦s;@‹éÿ·xØ¢¾ûý¶>¿Ü¢ÌrÎ|¼ê&K®×Øú<"µ’Wĵ"0o.Ïš?®ã'N:ŸÎ¥²\»þõ£_)² ‚³ª¤ŒGï{aå¸iMи6¾9òJ²’$;%!>MJ¯q×)%‰l+ï+”<æõkºUQ‘–œk0bw¤÷œo1÷ë)ßå'bI œ³JÕÛµñãõó¶¼`[¡Þ>ú]ó"¸¿µÊrÝì^Úuzn¼¶KÉeÉŒ6ZÙÙR£‘å@·U¾ã•å[-½zEmæÿ‰-ýÙBâʆ-{M^é?ݲ­çôö—L­¯äÝr„ß± ‡2·e’«ð.Ÿb—!¦ÚÌ,v‘UÕ*é ijöÁÇg›Kš™³¿Çù‘ëÆ· U1ëÜp/öwµj✺ÁËûi)·›ìb·ûÌèöÇÒÓ3~'e(µñ89¿='•ëÔ I6åÚrÕ¦cIUZRnŽq_\´/Ç|¼}!$aãÌŒ³Ú?Z³4à1{ûwbñ¬zÓçe^¾ò{J ˜çWw™KUÑÝö€È@€T^\¨ÓÄÈA‡nw©SÐܷך­CN 8è7jÉШUm+Þy€MLÈô®žÃ/\8©½òuk9.cðü´øëû/æõœWÙx™®½È&f½›r­çA´L¤yæu&”R¢V˜‰×Öoø1áôÃ¥Mþ¤¾®ÑäZ/Ø5íhûµï+—•xïô'é–‚0j¹ÿÈÿåR‹RñÕû4ßH{¸ÎcñÑ ÷ØO.2àN×îõ¾_Ž8ÎÉ.ÇFX4PàL—•—“™ö3ö[2é6šËU\iî[* wlôl ÏÄ3cÌj÷Ã×ï[¯ô\N·Zÿ좛‰0›/2Ô:/ØÕy÷7i Vë?¤ÐUmfo³©¬sö3¶´ŸÁw”Jj: Ih[;­·vâñ®à%'RfS‚ŽM ªäã ñªäUQÊ}|®/ÚÍßÈ~”{‘Gý(ÿ:]BøWp‚ú}ËsÂçç:Ýè½c‘š¨uÀPt¥$ ýÂ+œß£+[ÎÞyföβ¯VL-2­W]l]ø\ÎÂckX¹Þu½6Q-‘Bõg÷w.ß"DÂ`ð²ÐÁËÊÌç-¯³À<óÀaˆˆë•ÏCöGìx$VÚ£}¯ÅÇEüèQPKÊÊþvnZëžÛÿLE‡TÿšóÖx‰)ª-Çž)2w àéä/ÄÌcýÝ ÷ý¶±s_v¿k]ÔÆ€½/UoÒª.¡K¨q½4þßÈ´Ùð‘µAØ¥4MBV†Ä?z—ÝÌ ¿½3åÙ®óN9Û‡J¿%µú/’ñ|³çÒK)$íò¢IîO§,pé òÐÝCwÔãq3–ô4_+£ EËÍø!]¿—×µ;cÛânð÷’•åvmyµúõëײeËkúƒ€*##£¹sçÒhØýßB€þ‹H7šti’€—{ýE$t{{ìí%ìbT›èèh1±ßÃ>üÌ™35ý)P5ZZZcÇŽv) ¦ @T3ccãZø”OŸ>±ÿí;z´~ƒµðq  [®\‰vA !@üź dek+ìR@‰Ô¤$v€v) f!@T¿GQöÙQùxP‹Äèÿ9Œ577vAே PÏž=ÛdgjæOaÊh7ئm/a—‚ãXØ‘#¡‡Œ›h» P"æM +[ÒÇÇGØ€¿´ E=ر?@Ø¥€2Äâc‡¹ ¥>)<<<ìÄ mSÜ"Z„||ù#5%MD4›IS~ã-„] (qãèaþO @ êÄɰð“§PŸ$R¾¼N’dª«>É¢M£6 BùhàêÜþgÂ.ü )@}’¨¹zø½°‹ÿh  ào“M– "§ž¢@bH[yaþ-ÐPM>­ CBÈÁ(RO¢f?èébNzn9 nOêK×ìgT€ Õ‚IžŸ&¹µòQ¿ã8ÿ:L#ëÔÊç” ù˜ßÈ8-òÍ…ø[?/ù†ó¢~2ci[Ð Uy¶•lô'÷_²Ÿ…Äv6™<‚ÈÒ 3ŽŒÑ&ÏóDz—$â½É…SD6‡Üó#È“OœÈaÜ‹Œ[MºÖ'´‚ÏË!÷7-Aäñ;vö& &¤“™êLêˆñ+Ië2J—¼ÌŸÀ,Îd×'+ÅZ_XðOC€€|4q äç 2뿃x鑘ãd¦ ™9œ8N”i$j6'ë'KÖ5&yL6Ž&O¿‘-³ˆ„Yz›ô"—ÈÊCDWH3É]â´™˜M ‹º±Xrd™Ó’d?'¶ºœêê[NÄu1I¼ƒˆ*!OCH€yð‰ì]Kdø”ä0Yöˆ\OÖ= ®—Hë:D  ¡¶!@@)ÌodÂmÒK‹ó\}"H‚®“éDþYµšÈ [v‘:tλ]‰â²b1¹5žØ¨½&DY’bÔœÓ:÷ Y³™È9 ›‰b~sûúd@/ràé>“×dÝ6"Ñ5FþÔ:‰Wd?¹0ô“æ]’bÑŒèæW9ë4# ”…±Œà_‡ ¥5#­Šï&AôÔyCÒòHâUòŠvŽD.›d½ßz(!‘äÒ3bc]~2‰7È;BÚüW˜žÙz’‹¬Âçñ7ÈvhGTE N,‡‘5É•(Ò¯-¿’T”“HâŠhD©Q¨á«á߆ ¥Ðˆ½Ô ;þæ§Þ¤hο·íIû û3 ­ì‹ãkªqÿ”_o9ÿêh—ù+%CÎ`B §I4Ÿ’Tôi-º¬dpüS2Ùœû˜Õ@çüÛÚŸLªpS^¹úåÓ3-?û2™Ü§V0~ù<ÌÊ¥â´*£5œ¬lZ» ƒëQžÐÕlܸqòòòëׯvAªUSN²ÍT - ½òщ² çߘ/¥*§™äçg’-M45ˆ’iþ»1eª®å÷¶¡QO é—&ÛtnHñoª šKHHü¿hEKÂΨOIÌ0¢WÔÂø]ÙCF. úù—ýÑhœ”\P¬Ôž±ÇßB¾&šùy#ýÞˆÏ$GW’:H}ÂéïÇ`¢^Ð :›\ÙÇiëܵ©æ€ h€ê—““#ì"T7†>™1‹Œõ%íȤ ¤®ywŠl$t{2¥ O‹u%B^‘ÛIk=Òº™>…8m$“†’QˆØWæCäÈ"gÂݘLw%“ü‰ó82~QÎ%ƒÉæg¤ÁRb£IÈOáÎ+Ð i²œì2"AëÈŠœ©ÈèëeÄiza?tì\Üw9;‚u"ÖdowÒÚlÔ'üÈò£„Љ~²|-é¦W8µkÉV´Ì á4ä¨cNþÛI& R<¯ЦB¶Uˆ®¯’ûÅtÎ]Q&ðœ‚Ú@rp`™WZy’]ž<Æfæîd«{UJÒñb©RÔ6h(Ñ‚VxMßëo|  @@!v*-ΣËs€Ú‡ åR)û9×Z^Q{ ö!@”ñúõk]]]aàË—/éé馦¦µü¹S)¯œ*j¯Ô2h€BK—. 7nܺuë„UŒM›6ùûû;;;{yyIKK «À 4GDD„““S£F˜¼n@]+ètº­­íóçÏÍÌÌ‚‚‚zõê%ÄÂ@EÐðOcç$vXù“àÿ@\\œ»»û;w<==Û´i³sçNa—ˆHKKÏœ9óîÝ»ìLß²eK---a !@À¿èùóç‡ómÞ¼ÙÊʊϘ¦¦¦]ºtqpp°±±a0µVB¨ì$ö:°páÂìÛ·ORR²àõøúúþáÄ«<…[·nééqî6Òºuë½{÷îÚµ«I“&^^^S§N­½Ã9f£MÞÚ‘óLjL-}&Àߪ ó×­ç.}½9þ·A«E›ª"i€è)ÎÍ/^¼ðO~üø±9ŸššÚÀ…œ¤sS¯z^MvêÕÏ?°j5qâDv† 444,~½C‡™™™>|¨ò”­­­ÙÿVy ìÂtëÖ­à9;ÓO˜0=¸råJv’f¯ŠU.ØÿØ» ¸&Þ?àÏ6Æè±@1P@Q±@Q ì@°@ÅFE…ŸØúW ;°»ÁQ[ º—ÿÛPDØÆÈð}¿|á¸Ý=÷ÝØnŸ{îÙ]ð! Þ‹?CM©x—€X Au!ªt²po­žuÿR,Þ¥ˆôéÓ'¿S¿†¼*‘›ËÂ%IórÓŸF¾|¥Ùz¨³"¶¦üç/Î?+´©ÑU6 ¹¹¹AAA{÷î4iR¿~ýNŽVD_¯ÚDjÖ¬–òÏ;×§OŸ!C†3 ¼‹ª <ôêâà]å]4ì„$ΣGŠ’“™™Ù™3gð.§ö@ðBÕ”›Ëªb’æýH:ô4.…Ge ÓÙ æ†Z›ª8nÚý—Q{“³ˆrDvN•Whní¿B‘Ä+|³ÿÞ±#sº2íÈ]2ÅÀßyœ½úáÎîA¿ÏóÁA‡Û¢•ÐöO¨ÍŸtNࣇÐÚ9èÉ{„䙚¹™«Hl9y Äîèl4R+pÂB»Z¡-)hÃ{dôDô‚|á ‘WÂÙÊ!Šº|)pÑËhËô8qb#ç@4qR "^ £ƒR|ѪÖhÕôâBJ¨ë\´ÀÝ_€Âv /¹ˆ¦†oEã|‘T+Ðu$$ Šsóû÷`׊ªA-/ f£ßiLìRµ¨†rsY•KҼܬ¯r&S#L´å;ùEèØØ]4–5Æž¥ü§ÖÌ{Cõì=oŒ&ÈK»tcyHž’©Ž–<¶œœ¡—ýЬ¨•Q„îë\]¥ÚO&ªõ´›BÉ þñíyvÛÎÆƒL³Ÿ½ŠØýñx¨ºÙ6s-á'@Áëgk}ŸgÚwšvÐ@['ï庋ç¾qéî­F­ÑUŸxöâg²¾“ž™Ä獵z.Ç=´[—ÆÂµþ:5óÖ½vóµP!ó âž®ß™Çæ "WÖ­Û+Ö™Ðg‰»ª\aúåg"¦çÓö:´éÙa%«çõÿMU›tÔúxC©ó̶-2Žï%5жGªàÅBWë2Á"íѯFö}-± ,¾AU¢ºˆ6Å>#ÙÙÙ xöìÙœ9sÚµk'í_Z–())ÍŸ??66væÌ™åÌÍ~üQ’%ò;€Zj ¬t<͵E´ÔIºõÆ ÿ—Èy.¡>žBw¢‰?Ññ3Hå­Ø–훡a]Ñ«èÆgäÖäO%oÑÙ¤:™§ ‰âÔ@Kï¡M}ÐU&ú/éi"= DcV¡fÃÐ’uHƒ‡bw£-#Ñ‹´}6¢PöJL=‰–~BS.¡tÜížRO#Š }_Ñ:´c0j“ŒÚ1«ò´P hÙR‹ ‰ŸóôÅIÝKê¹çîìÜ™ü%›ÐrJçÖŸï]/ê»jÜcz»VyŸî^H~ö$5£À´0¾ÈÊT¥v#’ess}R‹Á«RÙH• ~©š}fŠýú‘nmm#ý"¸s玄 Ëm¤8Ikjj®X±bôèÑf&ªé¹Lbh ¿«Fi¤­¯Œî$§frÓ)¬w‘‰Hy€“:MÐ[FTµ56¥йþáÇHU]ñý©ÒP·3ì`'Qj¢y=êèûo? …š“}Ãó/$mo_}AzÆé-{4RNþÑäÕzšÇL~yjåËUM’×Å q´oüû%Äý™ÿ©¹k* >IòƦC¼Y4l1Næíò”[öwQl†äU:zjŸ^üéò¼ÖýŠ–Õõ´óîLE¼=õT™n>´=6ééáÏYm[( { â_?!éO2>l‰ w,þÓ¦˜îÆ“'Oìß¿¿øË‚u”••Õ¾}ûæÍ›w÷î]IgÜËz€Þ Ôgæ ü½;²ë‚öCrli×Äÿ„<ÐCá/NHá5Zz]HDÎZ&!»ˆ~ EF"—€ßAããaô!¯‰ˆ-aA2jÒ ©`:Ò·Œæ¼C+W!…h{8RþQÛ8"¥O(d1º;u6ÀËD“w£Nª‚Û#¢ý.èy!:µ5ÂVÜ®¯B÷’P»Öy‚(hÙR‹ ‰Àh]N÷½¯ýTfÞ¬Åß¿=LmeשAn\BÔÁÏg2?»Ù›îÃK¹sòúËýûšMQÇñëõ;7«ÍàU¹lÄ·TmQ×T9°ûøéÓ§±ÃÅ‹ Ê]$;;;==] |)®|F Úµkçîî>hРfÍšIž™ÈT3ùÛF a2®0qÙé©\ìo«XÜcO¢0håå&—[…tˆT&ÛÖp9ÂÐÅKOy”€~“ÊÿNr–Æ[þ¹mdáåþaõ‘Ç[æ$Rô¬§t¥GS"®HDo÷=¼Ú¬C+ Q®‰¥àÕÉÏøùê BÆj6 Tu&}KKÈåü К7ê£+¸anj§ýôÕãoͺë¿ðͱ¯Ê®}t„-Hn°ô–§¸MQzöìùüùó)S¦6oÞ¼ÂÏžÌøøñchhèëׯ±];¨£˜£^Ó¬‡X´QÜenFŒ¿yãM\Z.OZë#ÌÒ~ä|Œ½†} J“›«~Rª"7®–v*ª6ƒWå²EÌRµ‰ÉdÊÊÊ’&IOœ8Qòiì¶nÝ*®ºB¹¹75åÖø˜W¹*™‚XßÒÒÞA¢¨¨‘лÂÔ,>*:¤Ã*HÃ>÷)1klSÍÍÈÌÂS.n¿›@iæeëpóÂõgœ¾ÓtJ¼Í *MŽÕ^·ý󉙟Oªª·q2îáÞ¼1ƒÀÍÎÍÅî~ýdµ×‹ß³sÙ,9²¢„GAU±õPÞðëúéôÎÕH©Ÿ®½Qíæÿ{m•iP MMÍ`ÓI“&¹ººŽ5ªÎuE³X¬½{÷b/ìE‹a$33SÒÜt{´*Í™fµÆÞÈÒÙ{ W7¤,õv¤‹”K<ÑÌf‚c™ß¼‹Ä–Èc4:‚N>Gþíë5º„LÑ*RRÆ[ÁÏ{E 8ùš‚øÂMd jñî wìÿŽx& _Ù<¸8¨f eKíwMIè^U…ÉÄîç±¹¿Ê(TìÓ‡ÃåÖîÖ‰ÃæžÝóìÆ©x‡+å"l6[rG£ô”””ª¥ŠªÍàU¹l$n©*?ôʨh’–^åróoù?£ü¢¯##Ÿ]LÕˆ¨ðWäðs7ßGÕØœùàÝ­ˆOíý›ª‘XϾˆgQ,†6W«¹M5(øóHîeçòøT,…d]Ü”Ôa…JqÔ&îÜéëã‹I/%?ÙûÉåŸS¶uøÝ]iÞaöúf ec•™RÔ–ªƒy«íן_ˆOÑIíZBjkSå_9b¬("‘èãã3`À€©S§:´èÊ)Unµ–<|øpåÊ•VVVÏŸ?×ÕÕݶm[yK‘ÕDZž§!ÿç."£2 J¦££yþüùÉ“'›™™ùùù©©©ß›˜˜xÿþ}¯›ÃårmllJžJ/--mÓ¦MÏž=Ã~öíÛ·bÍ™Èj°àŸ/½XŽÆ,D뎣­]„´äøi.JÏ)½,;åp‘Ÿ§"ë=Âö¶TýîâÙòŽ‘‚{É-Ð{´$ ½\ŽnG š²Õ.§¤¢KR5YÀD¶"N£ÁO­Øó@õ-Kðêš×½$óHd¢¾‘ÑŠ+òóóÏ;‡#i’t…Gðª\6±TËÎr²ÐU$2I—»–›Û·o_ÅÜü‡ÃÁÞÏbî,Hˆ¯^½Z²dÉСC'NœØ¿ÿ¢ñÄ·nݺw£c5®«BîÞ½›žžîëë‹„£ðOž<¹eË–1cÆDDDÐéÒ_â6 ðWÈwj\ô¢#!Ã^Hu!ÊMG†`¸pâGTÀGEßT`¿G×Rþ·‘gèÎgä&IŒ%ì˜(ÁÿLQÜz±-ÿ~}’Pçˆ6Ú†^~F]÷"%B9%-(èäæýöJ¶È¡aèóÔäÏû&q3:ðy-@M+ø´P} @Ë|º¦Êé^ªh4Ú@¡úœ¤k7xU.‰[JÚµÖ–’IºÜ™_¼xQ£ÞÉښڄĤs/X›7—+øú8ñMBšÂûøq;Ÿ&e ŒkïTIE»$yy ‹fö½µ…ÃP tMì%wóggUEnYMW®ª;¼df'_ãÛþ¯£ý/s|,;X*1ä'7/5‹®oNçz½ÿ(ÍuUSÁ™1”õÜü¿\þ!bÛWýYº "âsòâ#žëµéئ‰Àc¾ý5ƒ ÚÕDŽ@•ïìÕèæÊ¯GïüL-ZÐHVÖ÷Ú¼…¸ .¤Bo†º,C>þHƒ„à›? eHíwMq$v/Õie“´4'#“eµ¼¡ÂÙHKòRà7¹QÿÎ úwþ;¥—WñI@ >å¥Sô'ëdñçûsÜŒïgg\Œ¾ÿƯ‰µ2"ªê Þ1t°Ô+Sê`9 ûWb’Í$›I¥gc˜ Ym4¤ôâM=;þýMI»çìÞ=g—œA×5D×UôšÉz´›Ü£Ì•Jº´ӥ̷ÁJ YÛ­8V‘±l]^›R"ãÆÃÒ³¿¿ÿÑ£G===«Þf¥a.,:»¸¸¼xñBKK« -‘Ñh´Ảɩúhæu4³ä¤^èñŸ›¤fèàŸÍæö¡kùï\˜1xd‰Î¡òÔpC‡Ýþ™b:m'bN‚Úùï†]¾ºøïíIèqéW<UZ†ÔjBâ$DÆFKê^²lñîEÔAA¿Î§ƒwý0éëD~ò¿—_òJ¿¼ú~¦[k{åg÷'&ä#ô1áHÑÁ’õòö—·Ø¯ŸÞ]›oîbnkHŽë±8I×õ]»Á •+ž$/J˜Ü=¼èFد¿S ùûÄúc7&O')«·nK¿øá;^z~qíÀô*ÐÔÔÜ·oßÍ›7K~§°ö5 KÏ;w.VYÆIFëÖ#úÔ»Ÿr‘ @Ë’ÚLHDy#ÏŽF’»—t¬FÛ[•ì%p˜âà0¥d+Fƒ *ñ»I ÷yRVPÛÜÃ|5¨®àUùuÕJˆ©ß°'ªø)úç¶Ó¹¦ØnŽÇ GyßâéÜüÐcym‹9{¬t)¥—Õ¥¢¯ÂÓÂñ»‡¿¾‹¯üH³é–'n] àvUàž\ÍÌÄ\X¤®øõ=ΡG®D*p Ô7 eNm&$±«ƒð$“j"x‰$^÷¯ÊT ©7J=!Øí¿ï ýÌÆŸŽ>žÔÖ¢ät R´›jÒˆRzþM=}~r„·c¹yxTÑôÅû oäh›T½|E½7ÿYo}œ¯:\ñÑã4ÿR0EÓ 7ýò— ®-[j3!-YÖ8@†{€@)5¼DZ4ösÞС*ïZÀ_ùp ð4P'õíÛ7!!!/;ïBj™Læp8xWQ"‘Ø®];¼«54T?‘’ò®0æL&Þ…TM.?¿ï*ªAòË´fð.¢Z…††>œÍfã]HíY¾|ù«W¯”””ÂÂÂÞåˆÅ`0ŒŒŒð®Ô ÐTÒÛŸh Íñ®ü•’ÀnÜF ï*~èꑟûŠê¡ÄPÊäþÞÈÈÈPPP P(ø–T9‡ †wÕ K­ZµÂ»ŠÚõøñãððpÿ«W¯Îš5 ïŠ@ÃZZ$"åW/6’,;+÷öÕ'‡÷ÊËÉÇ»–jð%>_¿#>«îÛ·/–$ðY7£k'ýáÇã]Åoæææ!!!xWQý–-[fhhèîîŽw! Áùúõë„ V®\É`0‚‚‚FÕ½{wkkk¼ë hi¹º ÈÉÊÅ»Šªâ³R¿_Ó7LËKû–j`o£‡WR}G2+&&ÆÊÊJ–Ø6GŽ100€ jŸÏ1b„‡‡¶íÅ~ÕÑÑñ÷÷2dHll¬‚‚ÞÕ†´´êG`úñãDZcÇà°¨[8ΤI“ˆDâÚµkmllð.§ázóæÍ³gÏÞ¾}››› ©Ô¦Õ«Wgffz{{Oéѣǃ¦NºsçN  è†ENN®°°>|ÔwôèQ|Ë “É÷îÝÛ³gÏÀ»té‚íÊ6iÒß’¦#GŽ`?óòòÎ;РÖ<}ú444ôÿû¶]rúŒ3¼¼¼Ž;†mðª 4X …ÜÜ:?Ôš;wî¬Y³÷„ç„=z4V –ž­­­'L˜È`0ð®«a) ИÇC€µÛaóôôœ>}ºŽŽN©»ètú’%K|||Ú·o¯§§‡Ky Á‚ݰԉÓgÙqôèч~üøQFz|±Ä:—ËÅ»ŠúlöìÙ‹-ªŸwîÜùòå ¯—©]ÄÊÊêÚµkQQQãÆ³°°À>>MLLð.ª>+ Œâ(Ozz:Þ%ÔÚÚÚòòò?ÎÏÏ/ ±ô|ïÞ=[[Û>}ú´mÛ¯"AúÁQTTÌÎÎVQQÁ»ú k×®]³f Þ…Tƒ£GÝ©Q¥ 0ÀÙÙyãÆ;wÆ>h/^¬®®ŽwQõPÉñE`¨ÖÖÖÛ¶mÃn„„„$''—º—@ 8::®X±Ò@CºÁ¡P( 꺯µ)>>~ôèÑxWQ=ŠÆoÝ–ÁQ%Q©TooïE‹™˜˜úùùañ®«^)ÕýŒ` aƒÝàÐétì“ï*ê¡ììl77·œœ¼ ©Åã7ŠÈæ(Ž’ÔÕÕÃÂÂ&OžŒ…é­[·þ÷ß À»¨ú£l€F0ŠЀA€npäåå Šåp8\.WNNÇ’ê>Ÿ?zôèׯ_#á¹ñ.§ß("Ë£8J255=þü… °½aÆ5kÖXYYá]TWvüFÅh° @×S¦L‰/,,ÌËËÃ~¾{÷ÎÙÙËÍl6»¨»ôÖ­[vvvx—Y·­]»¶8qÖ›óo”œ"ã£8JéÝ»wÏž=÷ìÙÓ¯_¿^½z—=ƒ,žÈîg£8 Xÿ¤åRUU½|ùrÉ)%{ 544:vìXëEÕ+7oÞœ={6ÞUT§Rã7ŠÈþ(Ž’J^xÅÒÒrêÔ©3fÌ Ñhx×U'‰ ÐFq*ÐõŸ——×’%Kø|¾È{œœàäóUñõë×Áƒ—¼00ÐÄÄdÙ²eC† ¯Tˆ¸ñE` a‚]ÿµhÑÂÎÎîÖ­["ïuvv®åzê‹…¥ç”””’åååñª§Z”¿Q¤nâ( ý¼ÿ>VüêÕ«áÂ+"¡ûÁ(@CºAðòò )JÏž=k¿žzcöìÙ·o߯»Šj&rüF‘º5Š£›»wï:tÈÛÛÛÚÚú¿ÿþ300À»¨: oß¾:t(þ{sssÇŒS<¥n”€jºAððð˜2eJ~~~©évvvõà‚yxÁÒäºuëÊN¯ëCbDŽß(RGq”D † âêêºfÍ,Ž5jþüùÊÊÊx×%Ó,--KþúøñãÌÌLGGG¼êYºA`2™Xhˆˆˆ(5ÆoTZ\\\ÉN¸’ F-SÄß(RwGq”D£ÑæÍ›7zôh,=-Z´hüøñõàÜ)j |f4^^^etÿþýq)¦®ËÊÊrssËÍÍÅ»ê'aüF‘:=Š£$]»v½zõ* ,,lõêÕ½{÷Æ»(u膢G5úúõkñcccZ9>>> xWQ#ìííKž°%$$$33sÅŠ8–T£ÌÌÌŠ/¼²~ýz,F›ššâ]Yº¡ ‘HÆ [¹reñ¿QiaaaÝ»w?xðàµk׸\n©{q© TZïÞ½·oߎýYÝÜÜ-Z¤©©‰wQdèÄÛÛtµPRR%ôíÛ·;w;v¬ä‰rá4Ãu™Löññ:thpp°‰‰É !¸ð ‘ @7 fffmÚ´‰‰‰A—ï®: …Ò§OŸÈÈȈˆˆƒ¾yóï¢@å)++¯Zµjâĉ³fÍ266 ñôô$x×@¶@€nXFŽY {öì‰%?¼Ë©°˜•——ghh¸HèÑ£G/_¾Ä»(P%Ç¿~ýúŒ36lذvíZ¼‹ C @7,žžžþþþl6οQ]\]]KþÚN¯b@5rppxüøñž={Ø¥K—º{lPûÚ´iÃd2±Ý0¼ ÔÐ ‹†††““Ó™3gúôéƒw-õAdddãÆ;uê„w! F‰ÄÑ£G{xx¬_¿¾mÛ¶ãÇ ¬Óçùµ&66¶®_S èÇËË+55UUUïBêƒàààÍ›7ã]¨YXb.¾ðб±ñ’%KFŽ _åâñxx—¨) œ¾}ûbï*ê>Ÿbkk‹w! 6]x%66vÆŒ›6mZ³fƒƒÞEÀè‡J¥Ž;ï*ê,@;99á]¨UVVV×®]‹ŠŠ7nœä ¯`¯Çñ þÝÁi¹ªËFÑÑѺººx×jÛ€œ7nÜØ¥KÅ‹«««—íðáÃ7oÞ„A>Pÿ@€ 2âããét:¤ç‹J¥úûû{{{/Z´ÈÄÄ$00ÐÏÏ›X;ãg: !ºš&ƒ„Ÿ“ŸžÆé}>åtof•WÄúpbÍÚƒ¶yi¾åãÉzÕ:|Rrã¼Ô«æ®:uÿ;"¨5nL-ÈÈ(dwõœ´`„•±ô z ˆŸ“•U(¯kÖÖ¾ÏÐI¾C:hR°µ$˜·`ï¹ã—â ‘ží'÷ù«¦´aF°’Î_¸/úÌ…Wj]ú÷ñ\²j¢©|u>¾ÚÂf³oݺխ[7¼ 2Û›:þü¥K—fÏž½aÃ,:ëè脆†Ïàçç§««ëââ‚c‘¢ñ2mµààãä¯Äžûnl´UÀ» ¨ @KM¾õ‚Ç·–˜Ëa79Éë­[L{n÷¿„›j‚`˜ÿhºmhy H‡ÚÔ5ð?CÖ¹#5q5;ÉÕºÌÚÖ¾­e»õ™ƒÇEvWäç'Ÿ™×¿ÿȶW?ÆÜY`I+3¶/ÿË““Û—ÎZâ½wå¦9çÎwUÓ¶ò`_»®Ê®×M}ÿwxVËâ+R[ ýo¿‹óÓÙöûo\g¿[Òt³fÍrssñ®¢nèÑ£G÷îÝ÷ìÙÓ¯_?ƒ‘••U|—Ë^›@kÞoIب}w¯ ¾5åHO%óiºí<ŸèÝcž}È —‘ '†4ªçŸ• % ï*jƒ§§'Þ%Ô%E^155-{m‚‚77·[·nYXXHlƒ¤ÑmþÞŽ½§™vÊÉúx|œïq»ßZÎp̆#—ÜŒ–y;R´ë.=>ŸËæð8.ÿï4Þ‹kO¤3W¤0½¶…°èüëeæ©Q+ "bbb¢¢¢°=Ò7nˆ¼èýû÷CCCóóómmm±ØÚ¯4X ¥¤ä|8 ‘Å}d‘šŒ=Žêc„FˆÖ¼CS´ûÉ«ØïìžJb"QÙnþ®©ûmÖŸY°%Îm©…ô=cuQÏž=ñ.È® ðù""cff¦““–¡›5kV~+”æ#w¸Ÿ8ÿø8¿ãö‡Ý+ÞËO;ëÕ÷ÚÔ+«ÚÒ°ßHZü,UTá§G‰<е<{+67¦”§ÚW @E|úôiãÆƒ jÚ´iÙ|lggWXX˜œœ|þüy‡Ô&ÐÒ"’%>WDŠ [r¾_[ë?o×£^z*¿¥ëìÕËF˜3ˆõf›Wÿ©‡_ZèÆ¿séÙ6QÍrÐì›gØ«•ÿÁXttÏ‚ƒ DuùÜ\µžs·®Jœ>rÚÖ{Ùò­=ü|ç-ÝJ!÷Ù[Âýèµ|ïÖ™]5He—eΨøùëøìö•N-gY…VÃê®_Ÿx"úÓB i›g9»xÂÜÈ÷$ERîÏ4¹î›.ïê¯)Ó×x»páB~~~ñwÅ@m©ãΜ9sùòeq÷~þüûŒ¿{÷®††F¹MÉûíYvÜbαq¾•ˆÐìÄð…‘Ÿì§J˜…—ŸU 8«e…Ú­êJ¨ˆþýûco,%‹ün ¡“'Oª¨¨Õzu Aƒ]}ø9çwé¶Agã“§“MhÖ§Ho«Á6oxq§G6¡NعùúùnÇ•{…‹8­ÄN>¿dë¬îß^_ñi!±ëšŸy}zg÷ÿéo|ïkDùrÐÕxXÔ"aÛÆý¹õ§Ÿ¿téÒ©S§–÷ÕªèjSxõiÒ³nR4©ºíLÐñkOÓy­´Ët—Ð {ZÊ­?{ãe&¿•¦øƒ¨ïïıQ#K½ßÁWN³…:û6!Ó³Y¯@/í3[7nyê»¶½ðõÞ]öU(g©FÒ<˜œ#ƒ u(…ù|¥í=6^_0¡‹ÇyYyØçM™ŽÍ+]†–kÞ½£â¦}}í g͘à5°‹–¨ï)ʼ¼v¯ø^~zB_ÂÜ;žtï">+«ª«AôÀ0ÚMõ3Ý:ï«o.Œ°½í¨ªÏy#j¹KIƒá~ôMÑYê*€õíå7ì¿&Öz؇!›@Ä2Ÿ'"Hó¸|"™(|šˆ.»oìК¼séèÈ¥£›¸¬<>£½’ìœíÕ«Wppp—.]ªÔŠ Ì­\¸,"¥õ„Çãû) páÛ=> 6]Nu9~gü»M+·œøÖaˆéÓ'Äs„NPhé<+ÜÉïÃÕËfu ¥:,ž;ÊNGTŒ–0:Ÿ‘TºžÔ×/6És^H\IbGä‹<:Ï-;±WÚÞùóJŽ|ÐñóßF.˜ºâ̺“ýãã·ïŸS´§\¸Y'/—9hР¢_¿|ùR¦Ÿ>}úäÉ“¢««`8иqãòÚ£µž¾àH›¥ÇÆù°ÛQb–Äöªèü7æ¡]L™Å[4z_?áÆv~O"`oeÁû\Š+ØÅ@¤*`/Øl_çv³AMÀÒ³¹¹yÉ)))aaa7oÞ„Áð²«6$††¢püÂßÊËÏÈGHQCAd>ææ¤æ!¢²¦‚ĸH¤«Ð±¬Y'L×/ó×¢¶9»k÷ñÿNÇq/^3ôçÔÊ–bUø‘I‰ýùêù»9z«ÉJºØ§\jrý{z^ÎçÏ ÆïGMT²»ùÚØõ¿ž\3eÔŠ™n »¼]ß®Üá"¸ÈÉÉÉËË+{†² à¤>‰]•aã³ðL\æôÝr-G® }yÒzõ­[Û¸÷œ<áÛº¼ ;ǧ^3tž#îûoZÓîS¶wŸøíöžAfœîÓÏÓYWîŸO‰£óËÖ#é(šÛª}IC—‰Ã¾™®Pß¾}‹~MKKÃbtQ¤ŽŠŠRUU5kV9MЭf‡Ïl»üèX?§õ‡•³Ó^5ÜœŸ‚Æ•éež2;–­=ö4ƒÂ sâß"d*Uƒíb(ž«®ífƒš ®®¾qãÆÀÀÀýû÷S©T>Ÿ¿|ùòéÓ§›™™á]h  @WyýîV´MwÈGúE]·¬ÏãòPË­•E¤-H¾ÇB¦N–JáÊ5±1$­rïmÎ4ý²ítúϬÚsàØ•®;·ŸVòR5÷3ziÈ Ät]2ÆPÐÓI7`GßwúÔÞ‡ÿÙ8”xˆ¬·6Æ›øZ‰fºt>×ÎŘFQ·´d÷§33b’óøíhµRsE1 ,úTzq~Ö)íw+Ï:~áeO=9ñÑdlð¡–]‹w ÁáQCçGû¿w„ý_T»ñ.úqgýˆ~M–õˆxtÐCÄyºEÎ7,UÏÛük3Å…§Š9èO?"Ÿ"j<+¶B‡ìEôÈxwòÔ'dìß½±`ï `êd¯²÷ô£´@“²£©j–˜{ýš-ÍR Ös÷Í>Ò>ôè„YŠ£aE+¼Ó^Xã ¬¼ïÙܱ7÷µŸ’1çþÃ¥˜ö›PK£@i¬lµui7Ôww÷ÈÈÈmÛ¶ùùù8q¢°°oA€®6µ‹¦µß°4<¡Ód,(°?Ÿ\²6A±ßþqÆ"¾#Èz¹hëµÁQ^ú’ǾÕ{Ϊî´o¼o£«F´Ó–çå§OEêU„ä JöZì_qŸï³«­‚´KU/~þç»û÷ ÏlxqçÀ¢ÑÒu§U¡Ý®ømë笰iùøfš”Ü/Ï/ïY>gšv{ÐïÇùvvéŠï­#‡7¡ ^æ›§)È ¿I%εW;öíÛçééI¡Tr´2i·äP¸â¢¾žß&-œ?±g ºÄGJÕµ6E9¥ÆÏOé{[ìöR ¿ÞÚ³bñÚKhØ–Èy®å^å¦Äè|ÃRõ´xð? Gá™búç? 7"¿‰¨£ó¼J²ÿ{Ð_0²{˜œÂ?g잌‘(O{*wÜ(*J9RJ¡íü}3Ù¬|—Ínù{REwÚ+„fàh%¿å¹›ßæ˜4-ñA‘ÿú\ Os”“³‚«¨tµuk7»$yùZ1Ô°lÙ²ÅÜÜÜÒÒ»qóæM FÃÜ@€®~ÆÝÕ³Ö\н÷ûå^`?磭®žl.Ì 킯_Vž>ÏÅr‡’!ó§ùˆ}÷ƒ‡ê–x‡ç\žÞÛN‘ÏÊùžÂ6q7¸¿V©÷?ûÉÕköG¾Gˆ²;pv¾× ¿^½·Ü Wö[ÐAg<–H´Ìlݦ® «òû¼Ìòfã:nímT2UE-ÜãÆöµÿ6Þ¨ÄK àÕ¦){¢¾`7ÏMéïjÝÞsñÏæ›åþºº"`uÔÁ G\[h zùœÂ6EÓÔÞeÓK£;7ú;·œ¡ïÙWÍ×­ ŸÙcÍOÁÈ…Æm{{mx0ÇÃôÏù»Èê­M¿Ù™ü§¥FÍû•£1p÷‰@sÙ¼ËÓ§O,X0|øð*´ARi=dùIYÏ®^äj¾ Õ¸ù üú1*Ô%úgè|N–¸#ìñó£ ƒ6Ü õ|c–tÑ¿äèüI> ¯ æ ¿„ù¢ÎWý½œ›{Ëàð}1w`r¿^Üq9K±ßD;™Ï\âí„Ï8ÖiMRñI;íy 5tçñÃÏ…mšq ôŠîöÔ{-žf|>ÄÏÍ_q{ «•ÈËûñ)“¨ÒL]¸zìÎgs¹”'ïc»xÿ,D’°ÒŠu1”P·v³‹EDD@€®vêêê›6mòññ™9s& Þø‚]1åNÛ;‰½ˆ3Y»{`Dw 4™½Ö]ŒèưJS×ÀõØ¿š5±áòˆ âVÚlÒÝ_eO1OPµ”e™ÆK7óÝ~Úw»ØÚHêÝæïé6_Bõ¥læ)}ƒ8Â> W®\Yî9¤@Rn5xiÔ €—Ç×,r7[h4jþ¢éÌ•¤ìIù3tžÉ}„½?7ñÂæ%Aa÷UÎY}/¬}…NgWbt~j©Ò%…wÐ_â8~ÑGçEN¬ÈÖŠfµøü–G¶Á£»]ÕbX䦓#ΤSº«¸?¯.÷ÿïø™·Ï䆻¾v¹zNWõßûE›E{§ïS<³„v†Í,ÿNW–L6TnëéïÝôÝå#o±&v̘šå={VÿF?Ï-Ÿ|& \™å9â\÷‘3ݲ÷ü·'¢ä<í–Þ¸®1sΆñm7 ÃÞ¬*-Úö™øß†)³W¼5aMO½5JÆNgõ±’;qeõ¸YM÷þçª[ÎJÅWËùr6tù©Ïe„ÍY©<ÅâéºyÛã°úãÁ+Ûû™×•Ýì’<==ñ.¡~4h¢¢¢££#Þ…€†tíâqE]âWÆgÄRµâ5]6 U_{$%s÷ÅÇúÇX»hX›½þ7Nx5–"Þ×nIy„ Ž“\ôj?5Õu^Øã]mÕ+ü—0:_òQxª˜ƒþFä‹<:Ÿù1ãRžˆCö¹/íæº?Ç‹Y*é,번¤ÑmAx·¢ï$0íÖ%畜"v§ ØañŒÅ'øm)y#§…û±%'Íßâ:ÿŸyf—{ïÎØ[ªéaÿ{9ìåû÷v9+Eâª%ë:/:ê¼èï„ÞÿÃþýùÍåÁزˀ«W¯^x—hP –b‹ó+^·eÏ¡C‡rssÇŒSÝ ™¦n‹Ž¸ÎJÍ$J3B¡ÄÐyªº®È#ì =:£Óš'¯4+ÕÝúÏè|v©;Ë9 /æ ?CWìˆ|‘GçåSvUõ=ëí6¿MqŸÛ…r›3HØ¢²‚¦‘½«k§Æò2ì€zçåË—gϞŻ Pš³³³¹¹9ÞUÔU kGþ«m³–^ÍB9ׂ&L{1i_Ñy|eM©‹ÝÙ+\ÓÓeÏû÷ï333k¬y"MM }É‘KƒO~F(}ãô™¿ÆÎšì ùûE"z輂è#ìþ:t²¢FÏá-j\QõHè/®$;qãøE‚Wø^v¢Ñ÷Á+þ`àLŠ{ÐßvmÀ¨UÓŽ¸­;¸îQÉGéo·îå•©-áº2Ô®Ó§Ï?~ÒÖ¶jgÐÕêñãû+V¬À»º tí ™MØ|uÂf¼Ë(OÙ+.×Öôt3mÚ4~—Jmî±ì¸Ç2÷ˆ:O}„½2D­Bt=’ú‹+Iäˆ|qƒàENt-3À@ÂA~NìÁʘ ?7õ*@Ìg¥DÏhß;l×™O“EŒÄÔ0‡Ù³!«ÉM›Bx¼šëªÿàƒ€r|ÿþ]UUµÒg¯«‘]ò¿ÉbåñàÒµªt9L­›ôðj‰wà¯Zë4š6mZ^^^UZhi©å2¶MuÕªîö±Ox— >€ €)))\.WWWïB s @ ž={233áX? r6oÞL§ÓGމw!€Z·nŸŸw€ºjòäÉòòò ¨¯ @ BïÞ½ñ.P·â] ¦@€ ´Í›7wéÒÅÌÌ ïB ‹ @ð‹µhÑ¢˜˜¼ €Œ‚ À?¡C‡ôôôð.2 4ÿ ‰Ý»wÇ» È.Ðü•maa‘ ''‡w-Q øëÁƒmÚ´ô @ð—££c·nÝð®2 4¿½ÿžÃáà]dh~;tè\¾å‚ Ào666xW Þá¥S"×Ñn ßÍ`íHKuV)‘”ó {NÊØ&—Ú!ãéÝ|MbÍW *4¿988à]N8Ù7fÝÈôéãb@»¨ˆ*lÏ lÚúÝ;Ò.¢Ý'7Жór·ÜÕš, Th¶mÛÖ´iÓÞ½{ã]Hmàå¦?|ùJ³õPgE,2ç?qþY¡ ÞUuhÖ®]Žw•Çû‘t èi\ Ê ¦³Ì ´6U#ïä¦Ýµ719‹(GdçäYy…æÖþ+I¼Â7ûï;ò1¡+ÓŽÜ%S üÇÙË$®êWô£ˆ}o¾’­B\ze¿>ú6®ÐÀ»_Öñ­~²)ú=tÑË/‰ß؈,¯goêækÖR¹¸=~AbÒ¹Ýq1ñˆŠX9¢’’Ť®CìhplÐPHÜ–F„­£Œ‚ €@ddd«V­ð®¢òx¹Y_åL¦F˜hË!vò‹Ð±±»6h, jL' ü§ÖÌ{Cõì=oŒ&ÈK»tcyHž’©Ž–<¶œœ¡—ýЬ¨•Q„îë\]¥ÂATïÙa%=0øÇëÿ= ›ª6é¨õñQÝÙ~2;3hkOWßc„5“Ìúrýé¾í1ëßsÃ, Ϭ]ðúÙZßç™ö¦4ÐÆÖÎÉ{¹îâ¹o~Í>7€zŒC¼³¹j«\r>ŸPH@XggO÷ä(`)•E:0TmÞ1K‘5¨ztú! Q´ØN3²‚ýYjÂÍ?Ÿ|6ˆ¹j/%„x,¾¡[ÎÒ•yfL„ؤ#£T gÑ C÷&졉¦ü`ñ›»fo Ïm¥(\5ŸtHqîbZ|.ŸX@à0¸.ÛÓ‚{ðâ¦LD߯уÂé'/P¾òõœrÂöç´-Í&ÞZË\¹E.!)ÐùrÂ×BžT Рœœœ:ž1D5=—I MaN¥4ÒÖWFw’S39éÖ»ÈÄ ¤<ÀI]ØÍKTµ56¥йþáÇHU]J•VªëiçÝ™Šxzê©XãÁ4²z+-=]lâ äa;ò}ÔÆèWçžë$Gàdßßðü IÛÛW_žóÒ[öh¤œ\±îøƒG¼å§>xßçÒÏ#Ýyd.1vÊÀ¡ê1¿~öãP¨Üa©9m4'š{gN_Å£¤“O-Pž­ö&ÿ×ÙEly.ñœ·ÚøSœ÷~L´âç?c j£4à;ïö±M ×}_*õ½Ö¤;ä‰ùÎ~Yýgï®R =ÁœVÈÁ6žù¥›îûùx8—Â#<™¯¶â â#T fúï-ôäßiR–ótbìffP¤âÔµ×–p¨Â‡sÝG}èN‚ËžÔÃ^¶@–ÜpuU-“ @ƒ†ŽÇãß¹s§iÓ¦x×RyD¦š ³ø7 ËÊ\»Ée§§r±P­HÿSI ¡¼üÁäjY·¼Q]ÁÐŽRÓ Ô¦Ý4)ÑŸ’îe²;i’ÓS% ¤ß¤ÅßHβÃxËê¨бßÒn%1§MÁÒ3ö;‰g55sòvÕóo Ow,îÖUàØtf7g ¤Ç´7#Á@õÈJÆMßô®)ôåGˆêã²½­øØf‰Þ*ϧ«âø“Œ _ ¼š¯„Ûg\~aSVMI§Î1ß\%ç ô·kr_g‚-W°!%ò[ÏÈôÏ´#nzÉ„ ZÎD*?¾NÎásT ˆýš¾`'‰â˜±d„0=™4tIII–––u:=c¸©)·ÄǼÊåPÉÄú–†¶ðEE„Þ¦fñ‘Šp‹Ì*HËF¨‘³æßý$&C¡Ì”B,«22³°ŸL:œê4 $¼Ökد[òØn¹¥#‡Q<‰ÊµíÄ[N=Gp´1<Œ Ìrµã9I~MhõZ>‹Å6\ùß÷ñu yè ééG‚WË’”¸ŠØÖ¶ñ„¿2óHˆºÖa¼9×¶ Ÿ¤Îî¨.izYD‰¥l#ì þ~]>! 7–Œ{® @ƒ†ÎÀÀàìÙ³xWQ5ù?£ü¢¯##Ÿ]L±-oá¯Èáçnþ¾ª?°9óÁ»[ŸÚû7U#±>ž}Ï¢X m®V[ï~‘ð÷?> x B×®]™Lfùó* ¤¾$M¦¯dàdêa[Ò‡ïØGôæFµ9€IÒß –‹¥f»ì÷]œlÁ£J×û«áš5ÇA-ø¬¢GSEÍöùC§äNÂfÅN/÷áüŒÇfâk5ãÁžW]4h………‡òööÆ»*a½OŠùŽÔGŠê· [·÷™¶îÄ“ £b)dDRUï½Ø®»-½º8¸™9¹ØÇY3¶¡!+3JKÍÌä lx@}wõ* \ÅKQl‚ùÂäâãí7ÿ:áÁ«Äz lŸ+ß{EÓ­N;~/5z#‹)nzy ò8‚¢ 0x£n€Ï1Р;w.<<¼®hÄáp¢Š¹³ !>òÑiƒk7]1™Y¸¹®®aηû¿ØHÞÔNI0PYÓª9úüîa’‰‹!lyUÅW7ô%g¥¹ˆ[Ük›ùÛ¤ñZh‰Ý¤¥ :ž¹F:|å&‚Å3?ÙˆWÉ/„ùú½óæöΛµVna{Õ=Û·§9«‰™®ZÎÃÖCþùùŸ‡d|ŒMWWwþüùxWQUdmMmBbÒ¹¬Í›Ë|}œø&!Má}ü‚¸O“2PFȵwª¤¢ž ’¼¼†E3ûÞÚÊ‚ ®‰e›?;«*r ÈjºrÕ±éæçÅÅ=›/ocÛÇT¸!3;ùßöí™ãcÙÁR‰!‡8¹y©Yt}s-cjÿâ”p9LP jöƈ’t‰œ;Žû»·tû)å;™Š Ðyä+H³°_•ÈjŒ( ‡å¾Nå4­x‚þμÑ!{„‘`EdM–‡+wÏzb>›ð1\QÔôòÔéÌRGäW©?GåkÃAÖA€ Zûöíñ.¡Õ›{ú}Ûµ=9bZ2M¯‘Í3“¤C¯âvÌEÞ‹­,¦uê0ç΃¸/i%—¹üæv¬ÃüyM‰DµîVöÑ·nŒ:­dæn;r¨IÒ!D^Ú§Q‡R±[ößÞ¯×stK­¿=¬g!g–Pø<6;#ßb`goïf*>è­ÛlP:½;þþšKWyXÝT5ãF†µÕ¯þ§T î§„Ëa‚Ê ä-žH¼¹ájj`7™Gx¶AiK"ß6,ÇVYÔ<â“ÕJû¿ñ÷äX3Ù"wF/úŒ‹ÌacѲ€->;ôù2´g«”Ÿ_ ì/ÔMKåíwä7£!^ùÂE¡Mn5$nz¹èír}mèA×”ÆÍå/ÀÖ!>Ý“û Eòx€]T¢ §a÷ú:t(77w̘1åÎyûöíŽ;Êîwê äFý;/èßùï”^^vn|ÊK§èO8ÖÉâÏùã¸ßÏθ}3þ_keDTÕ¼cè`iWFTíÒfL—6bžÛw˜…¸ af4dµÑi×*¯6/N™}wÛ¦¤¯¹$“évæïïÝøú9GÕmÒw¾mWCÊŸeùÙOãOîy÷…C"ð MßѼÿˆæÚr•X#Eˆ<û¿"õ™+Çi´ãð)lW‹5úà¯éžœ:”3ä§XkPyV6­Áwø—¿;W°•"sýE^Àܸ9$œ‰½F5[±œ}²gu\ŸåJ03,›‰²~,3gEö‚üò@…çØ„'ŒÀ þâù…JV,ƒCŠ}t˜t>7©ÛåìݘׂÂÿ%r:"žËÜT¢A7ªÜÊ@ÆC,?cÌ[À_TЈÊ{6•8ƒ¹e£ŠÓˆ¢Ãî3ªÀ@ %>`BØžg ‡Ç“ Dƒ]‡U½Ó¨÷ú¼ÿ>33Sš9ûöíK§Ó¤'OžìܹsöìÙRÎ_XX(á^—tíÚ•J­åa,ˆ&s§$+vò1¼9óMÔÁ”&c´$~~ÂëÈÈlJë}­¨Ä«™Ëa@M©C±§¨ýN£)¿—¨LÅßîÿb#ySÑ÷Ê(--­mÛ¶U±CCC,4÷ïß¿C‡DbÍú 2j÷â”üÌÿ³wpMuoÀïš1“nENAEPD¤Q° EÅ»ƒ¿…ݨ((ØØúª Š„¨tÅÿ „V>ߟ—yvïγ±~÷îœ{>«^œòýá{qåÝÿö<½ùmÈ»‘D25–ÃHv¶ž¹VúÔþû+|YD<«’EÖò´ ÐRàüjn ` …ßOÕߥ‘³DîÎ.KI9~®\¦GCQz#©Wkúö,Ö'ŽÁ`,--ÑÜìééihhØ6ÕÐøº8%FÆÚl$úU«Éf¢½Íĺ›Q-Œ,Œ¸>D3{¥Ü#öø{ÒávÖG¾Á³D5èI«Ï.%°YUUe ‰Ž^=‡ïÐNt>mݱcGqqñ¬Y³š¾KiiiŸ>}ÐÐìî¡Ñvµµ•{ÁöŸ§€haÂדFÜviè,Q5’å\W?Q}ç4÷âêêêW®\i£zÚÂ߯ñÄŸ§$ó¼Ä¨Æ 1&ô'Ø Vã ­°°0A—Ðü[ãa}¿ü8ú`ÚëÏx‹ÕîýŠß\>þ&¥Rg¸[QÜŽFrgWddžLyšZz +#c2±·¯-Ëf~»t|zV!–ÀfÚ·ï;ɲ».—k_ƒè7ÖiÔ±ÃLE„Ú Ÿ§¢Ëž_@€.pÒ¨MeddÈÈÈÈË×[Fèñow+ïd=’¶üë«}ñ†²šÝ”ÞßÂʰ›TÕÈB6L~^h×}Úe´wFYrÄ¥óÙ 6Â.º}gÍ’*ãû/õ–%Uæ_~6:¤œ|À¾3×¾ÐFÉ:mûòŠ>/N êÃ`0T*µu/6 …œ4jk³fÍ0`ÀèÑ£]H³ dw5Ûá=‰«BC>}ðê·ï…ÜÅ6=ÿ„S>Y[¹æ½…—ìÔW•–…`…wö¼+£uòp—åœý–h×ÍGùÌ’Wï–™»Q¸öÕ@#ø{|Yœ Åœ•©â ´î“FÌÑ QÇ Ð[OV],²Ô2MWIÔ>RWTTôòòt-!È5Þ±zýÕ8C;ê´ÿ¹;>ÿËã×¢­Ùñ÷ˆ„df=Î aóò‚è+Èý|Kå©RHvÞëRÆÏýg_H ·F@H@€‚>i„S÷í=×W`Ý·Š;vº„ìï¼Ô^ÈSPXÄ™‚J‘¬7N„Y½òêɺÀ?Î#3«è$¼4üú šà/øW,^¼xüøñ***‚.¤ù„{÷ ¹×|k`!cëÐ(õšyÔ„hðOøüùóæÍ›çÌ™#èBZBàk¼óR{!w<ÊY>·°ÔYØ+E‘D¿•”–1 üÊ úà¯ø'Ðh´³gÏ’H$AÒ"ü_ã½ieý±;M‘³|Vú£LwÝ?~±`eät•÷oß¾üf`¯“ˆ<ÐàŸ€Fçnݺ ºŠâûïMQo!w<µûdý;3^]žq•dfm&#EB¥e¹E’ÚÆízª&¬ù|<ì>&ØÐ¤#Ç å”aÛ+©QY-B ø{ýúµ··÷óçÏ]H ñwwVÞ­Äø£¹è­w‡îLÕpÕ©ÖåVZÈ]ÒÜjæ&™3‘©Ö_¹ÎBë&Êé«ZûuÑF0²ýìgâžÅÉ<š‹ Ùvº=õ\F(äÝJª×Wˆ¿;wîôë×OÐUü¾®ñŽ•íÕyt¯Î<îmx!wŒ”‘žï:=.kAŸB_«I}­ê¶sé«áÁƒ ÄßèÑ£Ùm0˜Ïø¹Æ;÷¾„T"ÓŽÔoçµ}+¶ók¥z q ˜{ûö-ƒÁÐÑÑt!…Ÿk¼—=¸Îµ¯E; 0Ñjýàáfxžõ´ñm€ˆzúôÁÖ­á‚®¢HIQKJŠ]E+¸ÿ–µµ¹ «a ˜;zôhaaáªU«]HËñs÷Eþñ[®Ö^Ûò×¾K&Ðe\jàU[´D‘››k^^Aee¾  i’’ñx"––f~~~‚®B„A€bÎÆÆ¦¢¢BÐUü~®ñ¾µVz®µoõBî‡? µrç•kÛº rŒ×¬Y-è*hM ˜³··t m‹/k¼‹ÃBî@k ÄÙÒ¥Kû÷ïoeUïâ-ˆ­ŠŠŠˆˆˆqãÆ ºˆÐ@lIHHܹsGYYYÐ…@¬@€b«°°ÐÐÐPÐU@Ü@€â©  @[[ûãÇd2YеþE EÐ%Ú h ž’““ é—ÏÞ1I‚®¢`±X‹%è*ZAzRN×.ÛèÁß¼yƒÇÃ_XÄüï Ä“m5AWZó]!ýþ²±  iWWׂ‚AWÑrØœ¤¬Œ¥¥…ÞFs!ƒÑÀj<"Cßñ÷÷o£õÕO ƒÝˆÌ—_N]E+ +Êy¯à,:Þ&çwìix›ÄÄD:ÞµkWþ”ăû˜Vr/þ“khEù¥…ùe]ȸrºæñÔ¬´Òõæî›þ<§ƒ€ŽªŒW¯á…$žEF~¸{×]”Ÿ´"Ð õ“F5233 Fgkëü|qX}T«W·FO]¼x±°°P°zð@ŸŠRq8K÷!ïÃËÔûÝMú ºŸ(ÓÏDáÐ!U—ÙÌI¢=Xº¶eð _ÑZ膈úI£èS@Ódhh¨  ággg:]À§ÛÅã̓ºsçÎû÷ï…í¹|0!ªO¯©Sµ]Ë?ƒÅ²ÅbÜ3´ Ð@ ™›› ºñ!))YZZ*è*êR41ñ=}úˆ«ë°³gÕ¬­]Žø#IKW º ÂØeY×î:püâí')ïòkNpà( êí;ê™ZظŒ™è©SÒwÓ÷b~:ÐGgÄM.“ȽòöhéšT$ε°XõªþVÓe/Í7"!Ìowöm9rõÁ“Ää×™Ÿ «ªï”TÔ1éêà8a óv¸Öx=€Èƒ ÄÍÚµkÕÕÕ}||]ˆ˜ Â9c ÍÍî{÷8pä­[²: º/¬¼;«†œõ;‚ëÐ;`Ü2k}5BUá—ôg·ÎÆž¹œòðrl’ž¯»Ûò½pjÃo”ùæ¿OIØ4Øs}Ú‚µZ}e¿]5j­¤#a¾2•1/'3ét¨ë¸xΰFEßý§—º˜vTøÑ{囨¹KwCh¦®CgO·ÔS•bæ¿M¼rd×ÉÏî\n=çôÙeŽò¢h nöìÙsøðaAW!>$$$*„u𫞻;½¸ø€£ãè{÷¨êÍžSšŽ(%E/)t@$UeíÜgþJ¤ãÄ3·7ºªÖž–?cù¦÷'ÆZ >Ø*{aˆíÚ›ZÉ"'@UÌ»ªQë„£(uêlÙQ A8ZZÇÒ\G¡Î&¥¼ÎgïOûÝ4kFô0óaÇó®0¤Ã«Ëã:@|ú×Á;ˆ›;wîÈËË º ñA"‘*++]O&~~E?vq™ A£5¾h Çf2]E¥V/Bs0B²måŸ9¸IÓ) —Ô¡w­²WkÁö7­“¼ Þ«¬>ò©¼±lcbÀ†.°ÊÀ?4+YYY:ttb…H$ |FfÃz„†Vrv~ý:ARRÐåˆ'<‰Wá-AãæWÎ µÎZÜWf”4rµÑ8,ýû½ZNÞjð8›ž õ¯Ú£—*òü3‚|¼qÿ £‹¨üüø`0666wïÞ…% Z™L.//tp\¹2>0ð„¯ï'°°ü[à\ÆNˆ?ˆÂ‹UQXsäõáaZ1[W¶þUüqªWÞ¶Æ^­‚¤;vÛV®÷à(òR5·J¾•2!@ýëàçÄÇçÏŸ]\\ =·.ÑXvƒñÜ¿ÿØ A'GŒtð úOA$n8—±ƒ! òä=:ãÇL:r¿ŸVSÖ³oÙ^mŒUQüãSiE©¦Ì"¬|y˲5ûÎÜy™ÃÙ/­¬ÕÉÀ¤×ص«}µˆ¿7«Ê¹»/|ힸëOޱЯ ×µï‰¡3‡šR«Oƒ\ªï“S³1ÙãbÎÉ~’E©ç÷lÝu0îò«ŽEøïz]ûø–ªã¾úBìDN'Ìϱ#úMŠIþöãƒD)£‰n³¥´F×{Þ&(µþ‡"4šššûöítâ†B¡áeìêÃàp^ÑÑQŽŽWÃÂú„‡ ºqC”–†I„ %pÊŽV˜ÿ³$稿^ÒéEËçMp7•k8}´l¯6V™Z%ºX(6ZIyò:g«™ ÔîWr6”eç¿}zq_DTÜ»!Ë~hváÕ®óï”!ªý¦G¬êÕQ"/ñĚŇ—Ý>²ÿbìý½ƒÕð­ÿ±/ôM—&™;ïþ‚”|·tDz¥'3~ж§ØmUº8ÚU{ØÅrÁöØ•us¬æïòpªÞ_xGåžÔÁã”ñö¬ÛÚã[«ëV|qEh &ÊÊÊ"""æÎ+èBăa±XX¬°Ÿh HJú_¼ig'Ñ®mX˜ Ë Úcv-=ØeÁœÔU•3ß+f>¥“ãPÿÀá^¶(ܯ´l¯6UþæJbõi^ïQæM¶`~8<14¡Q›rñÒÆn?F~xøøv§wþ{+Ö÷ó“]9VÎçÄ“Cƒ”«Okð`ÊÔs?ú!*pŒs³¾*œV‚Œ²œDõ>O6ì2;ýè”n溔ã,†çqq*nó•.îÌAXw·x;"DçÏy—¬œË[.”PÜBÿŒÖ­Öõ¿ 4gΜ¹}û¶ «OT*µ¸¸XFFFÐ…4Ž$#ãwþ|¤­-UMÍ4 @ÐåˆXH´œ¤ùÜK ¤áÞ³Ï~þÑRúæZä"ôk„zï s–,c§Ll¥½ja•ç~ÉÎæññYÅ·’fŽL+}¶/æúßcÁL+îk)y÷ÍþÝÞR¿[1´žó–Oxªô#ÝV¥G†úŽÞ0œµÄMù× ¬‚ãÔ¡JGwä”_\}ô­wˆöIÍtî¡È©?&0ª- Þ4_=dMªë´)†;ç¥ HbÄŽçÖZÖ¾HãÝñõW*•'Ìî+iý®ÿU ˜èܹ³¶¶¶ «‚GUW¼zuŸ¦õÜÝ]Ž˜€¥¼ÁßÀÊvŸu:sØíÖoÞs*¹ðg;ûãí“nìÙ5åÄùunªuIËöú…~ÅOOµµž#ëàœÝÙBè¾f都\£æÿvrüý\÷þr¿N˜“Ç/6þù˜.NæÜPîÕKý3ÆZݵ9’z)¥$D›çõ9É–«]øq›ØiÄÇ%×èÈû½ëæîGûn+_íßø¢¿,øWðoÝ®ÿQ ˜èkѵ*•ZTT$g kÈêèøœ<íîNQPPïÖMÐ刼„.cþ†¤ÖsÜÚžãV¼º{0jÿþ#÷>³«ï©JÚäîL{ú`‰Eý-Û«fWËEGwáq'=}÷˜ÓùM¬þf稄*DÅÿèñ`½¦Ìg”Ôëcˆ\zŽ|‹Эrõ†%œõ¨uç–§ßL¯¾ÑNCóç\mMµUHÕ·OEL„Ö¤uq*ns}å®ÈE ޝ>½¦O ÚÝJŸlÛ•Ž³Û;Bï×ûVîú߈ƒ;vÏš5KÐ…ˆ' Ãf³]Eó¨Y[{EGG{x ¿vMÑÄDÐåˆ<‰—±­OÓw»Âiì²í¯N®¿ò*g òbŔãoÔžüö×{‘Ôº9»öã²!GÅÓ›?V"lóÛÅþ“oVÈ{îMØ3H¥i‰’ 3fsÈž^^#ì7ÇB]…R œ}GŽåi©ø#ƲJ>g—UßJk(ÉcúƒÎlòï^ŒLA:–¥#U7ÿw0Ý'¬:0³óoEþB¸ÇSíWé­Þõ? 4­bÑ%%%U"‚W`ÐrppްማÛÈ„MMA—#ò°Õ‹bppFªIF---!èB„VZЊ ݬFwõ©÷ßË£©ÐÈ Ú–íÕr¬ï×ÂlËÐð?|kϰf\LCë¹öa¢ù’Ð¥[/eФ(õâÎ9è—ºûšã¦[Ó°»ªüÇ•åtCOíÈõrp8i-µfä4’áØPÛ•cï0‘—›·<²ÙFa}9·îd‘ÚÄY½k]L» ºþÁ«ÄÁ´iÓ]‚8ÃápÌêkççsNØ Ç*T*MÕ‚®«q&~~•EEQŽŽ£ïÝ“TPt9¢ %E/)!‰ÎHÁŠŒŒ$‰ Wq_ý¿ž‡˜hÒËÈ­BœøW{5_Õ§SS=·gY†^9¿¢bs±2fë/¬ÊIºt<úðȘÇßäãéY>ò©gGhb‰’?Ê&*YYk·Fé{ÍõšéSˆ|>°æÚâãnÔ¬˜õ׫LÂ'ý1ÐÓ]ÿ{àU"ïÅ‹íÛ·G#  ƒ~öìz£°°Åb¡ÿ555­½ÁóçÏMDd\D—‰‹>~<âæxõ*QB?UUU‚.AÐÓ6 ó~áŵÖÜG#ciúÊHÂ'ôLÀüÝ^­‰]–º' ÷¸8ÌÀíOŽ7¦´¸ IÉÜ}’¹{Ð’ÕqSïJGÊ.­Žz3l¾EE}ve’›ù½ i‹ig:¶}ÌÚwHñÉð¸Ö‘›ž¢üµÿ˜)ˆm‹®ÿ9ðª‘Ñ«W/A">ÌÌÌNœ8Áë^---QIÏ5V¬(ÏÏ?êááwáŽØ6§ªÄ«ªŠ^RB¤PrÓÒÈrrhKEõgrzzpLÁ®ÈI½wáæÇUÖº®0 ?q¾St·W«aå&,që·ô?ÙÏ­ê§Ü‚¤ÄÌM~Q¡c¦&ñ+xc$´¼Öî <|JúT‰HhõÐBN¾Dì;¿0¬›rm& ›Í´ZüaÝ]»ù MTf;ïínuGn·M×ÿxÑÄÐÛ·oYµ.8•——W\\œ™™ù«EII‰Biô2–¢¡¢¢ÂÚÚÚÎÎNÐ…ˆ??¿E‹ñš8èææÆçzþžËæÍ1ƒŸ9r`TŒâm6{»™Ù·ääÚoƒÝ]»þºÅãgæä¢2 r˜é7RŠCue¹ÜÅ.|tèç ãÒÎ#­¤[a¯¿G<ÈÁ{o‘ûÖ§G&¶ôÔsɽ.[G'ž¢X{|1II[ý£ŒPä9K4½tf½LG¤ ÛŸþ_ãW—n|{Ÿ¹aO•"ik'¦!š3gô¤Õ}mÔõ¿´ò÷÷¿{÷nÆ;vüº––&6}“¨ýÔ@«èرc=îܹÃõ^Q Ðhh|ìØáþýÏضMÐåˆ FÇÙù닼î×´³#ËrË6ÔC¿4.0\fWHoÕ??ª|<üÀw4Yö\¹ÊIÓ{ýª÷ýºÿ¦ØNo$áÀö®[a©&}ì”LP̯—×Ëô Öù]|ÕÇëçß ßeì=õ9ëœ Æ/s[ï{¦y»Æ- ý¹½,ÛýqxÏb²±¸f??¬|ßБª§¶T/@c2ÁD¢þ6mÔõ?´òöö® 177›ôÌf³§L™.)ÙØºª ™¸h-ƒ#‡ÆÅí··¿½b…ݼy‚.G˜Ü[³†×½z"x‹çoçÂÔV› âéÔÝ´ƒ‘žÿáå“ûö\}ÇBä—ŸŠ êDüû½fÙ×wŸ~,>È(üð1¯R_–TïìªÂÏï¿ý¸®yù×wÙEzí©5C©+3Î_þ†~ÿzaUHë„È4ØH€Fÿ@=šbdv-xÜ`;Cɪ/ÉWö…ozÈFoùŸCÍ9aœêÈ“Oú¯yV•?¹Kü"C[+]e),½8÷˧wï¥&ßùo±Iõ¥?XUôš–™••ÌÆÂ¥Kð4Ó-³Ÿ#ýB}µ¸qi«®ÿ!ðBˆ¡ÁƒOŸ>ÅcÙ°!C†ð¹ž¶óàÁƒëׯCzn èû=8©¬wéß~ýú­?ä?H22—/ïíÞ,+ÛeâDA—#ìML”ÌÌr’’¸Þ «<‚&!/|’ÞçüÙ‹×n?xúâYÌÿÎíªù­‚¡(ëY š22ht])ÌßîÅÌ>êÕÅÿÔgæ¯Æ­±Frc í1—w÷ü1X¿òÅ ûžóüÞñóNW­"ÛsÝÝ+Óõ[ëiS,C#f«_ºýèñîÙ§ÖU/TB’×µ>s–ÙßkgË;þïÁÛ{×lŒ:›ð4=åÎÅ!+XõìÁÖkgòš_†HÌ—íh;9öÜ’Îdî½#€9ýú>ñ íÏõumØõ¿´RSSëÖ­¯“Ðh¼æs=mÇÐÐððá®B<Ñh477·ãÇ×i÷ððH=­ERAÁïÂ…ýööRÊÊú ºagêï…[€V02j§­Íÿz€(ÂIkÛ Š~µé^8Ÿ“Ÿ|ÝŒd2ï~~@{G²£›Øgðª}ƒÃû7i[¢j¯‰Ð/^÷Ë8ìx‘ßÌ‘ŠXeŸ‹å¿mÑõ?´xâ5ŠCœÆo Õà Ðg$è*ÄV@@@ÇãUOk‘ÕÑñ¿pဣ#IFFËÁAÐå5“a²ë}œã7ÿ8Ðâ‰×(q¿QTT´páBA"¶Ð¬,//ÿýû÷_-vvv²b1oLÑÄdðÑ£q~~~.(Ã1oÒªªZŽŽ™W®Ôi×÷ôH= $ @‹'^£8Äiü†„„¬SЦˆD¢Ï–-[~µ 0@€õ´.-×;÷ï?òömYA—#¼LýýëhŠ’’ª•• êaZlÕÅ!fã7&L˜ èÄŸ¿¿íí)^çõÜÝK²³»¸ŒLHRVt9BÊ`àÀsAAU¥¥¿ZôÜÜ0X^“àŸZlÕÅ!Nã7’’’$%%Åéx@8Y[[ëêꦥ¥¡·õõõµÅnÞ˜åøñåùùœFݾM’‘t9ˆ(-­ïáñâÈ‘_-º0ðσ-¶êâ§ñ .DŸh>X°`"ú×ßàÅ6,¬øÓ§cƒùž9C€K"rcð+@ãÉdí¾}[hqV{‡˜ßèÚµ«˜ 'Z~~~èá ›Ívuut-m¥ÿ¦Mq'|}‡ÆÅÁBßõuìÓ‡¢¤TZ½p·¶“š¡]hqV{‡8ß@̓•äøEKKËÎÎîÕ«Wݺut-mƒñˆŒŒvs;äºcúOA$\°x¼‰¯ïƒˆô¶®øG@ÓA€gµGqˆÓø   Y³f¡ÁNÐ…ü+üýýÑwN¬OÍrúŽ?ààpmî\ÇU«]ŽÐ1õ÷G4‹…-öjFqˆÓø÷ïßÇÆÆFTŸ ü¾‹Ðƒ1AWÑæ’’ÃΜÙ׳§´ªj×à¦-!öÏP±´T04$Q©EEAׂZÌÕŒâ§ñêêê÷ïß'‰‚.äB£Ñ\\\]?H*(\¹²·{w²¬¬‰ŸŸ Ë.è W¯kø5€ƒ-æjFqˆÓø‚‚Xö´ªººß¹sûõCô¶““ Ë"¦~~Uee‚®Bd̘1CJJJÐUÚ hñ·|ùr±¿ñâÅ OOÏŒŒ AÄ™¢‰‰O|üW×agϪY[ ºa!Ó¾½ K%k×®t €6ZüÙÛÛ º„VóêÕ«áÇ º þÐÜìyÌË+ðêUy}}A—@¸@€¢ÄÛÛ[Ð%€…®›[ߢ¢ƒNN£ïÝ£ª« ¢VÎÑ~¾W«LÖ¤=™Ù‰ ˆD ýÝÉõŽÞ›l¼ýý£ â|Õ€ A€îLüüг³»¸ ¿~]R^žßݳ¾^ÙûŒŒGª^ì9–>e¾ÌHk±½gØÿtéçc“] @üA€žºÏœYž›{ÄÕÍÐ|^è›ùåRd†ã†UïFÏz¸ïð«YËMIüì^@Øyç]oL½¶¶ ,wb  !Ž+Wž5êøÐ¡CNœÀñïÂdÌì‹‘Vs=†¼ß5ëá託ó×v–àWçS•µ0æƒÝTA× ƒ @ëb~»¾jæÚ¸sžå"ˆÌ ˜Ôo•šÁ˜•i{gÍ‹¾vþÚÕ^îýW†{dož»1þTüƒ!ÓÚIü\@š]UX =áAúʯ î·)òT"ƒ6|˧#ˆ¤œ¢úxlFy~Ãù—3ÎT´Ó‚¤ã›7î»YI¡I‘ʯ9%$5}+¯åÛçt“ùñÀìÒ7g6‡o=v+‹Am'EÂÒ‹Špš=¼&ΚìÚ‰‚iÊ‹ ¨ã¼Ùj]ÿ\m#½ 1 Æ}Ïžcƒ¡1zÐÁƒ|Zè›ùù|dF—9]eÕ GY#Ù›¸´³ÍŸ/++ïÁö°¹Û®~ÂÓÈUù_¾³´†Ç\[c-É«½"óø‚ G^cå%JKåœæîX?ÒXŠólª>[2~nÌ[œ4®ô[ÉqËÕ½ŠLnõ®Í«/FÎ 3æí}\B¡°òsÙõ'»Àô›Í¬b° fý¿Ó"ˆ 5b·÷y¯ùqcƒãìŽy7+•ô‰57ôy *(vá­™B.²¿ºÙKéw'XJÇqÁv×2Õ¯&3;vÌÐíiº ÷7û5+c6fï…¼—†¡CmÒÎÐÀÿmå$e#åú7§waÅYèûìÙH;»;«WÛ†…µqoŒg"³:ÏíÑ=XÁ©:êŠ<|·ëÁ†ž}¨?¶`ç]Y¸:‘è?¯·\õO#m9z¬#‚ãÕþýòʽŸ¤="õ8?5— ½H®Ñ[¬µw"||”ZNì#/É9½Œ•wݺ»”Œ£?ãÒX§Jž5ä^^¼ñ5Íkûp4à¢ÍD ÏEÓõÎ[º3Åwů‘ÜJV½»sÎ8¨Î 6Û:%ýEN•÷‰šc–/¦€¡ÛvL§عçxp8 à¡þ‹%Ô˜/N¬_}^kùî‘Zü½¼ú÷cÂøk6³æø[+ˆ×…­pJ^±ßÄ"<×ÐÞ¿"ÎdΉ±“›D‰‡LrW"ÊX‹Gð¼"NsÌ©(æ—ö»>!Zs¸*Õ{|i»UÍ«›«2­8[Št™6Ú¸î0Z’ÞðY½æú]Y¾;Õw© éo+׿n¥NÍì]h‘ddüΟßß«—”²²ùˆuî-ÏË#ËʶNOŒw§"³,æÚÊVÿðqjΣ»"Þq'ßÑ¥]Í¢<íâ£2¤c/Cê¯7ˆdçÉÁHÙƒ=\ÛËG¥T!ªf?~$ÅŽrȹ7¯óNšZŽÝ¤·tµ­œ=}| W/%aqk¬û”yÔPþ02±ѰÔü5¨feHAân$æ³L•ëÁ)èAW1ƒÕÈÿüD5KC´Ó‡×xðVú@# @73?)víÂe² FÏ_4Pów†åÏÐH#k?säËeÁWP…-›ØM ®nÅÁÌ{v,bÞðÉõRš `$Œ¦X~ÔdÞ‰q“ãíŽ5}\ –fáa_}«ÁÿC±|Þƒè‡,D¶g?m.á+Õ^¯ú#Ö÷ûñÉ¢nÛE¡~ Øvf½;"·Þœ¾ùy¡‰Ö_VŽ‘P1ìX§­É½ëUí ô˜zìÆ| û¯UX9³Á¡›·M·“Ãq§[oHn£ª«û_º´ÏÎÍÊzîî¿ÚŸ21ÿÓtÐ-ô\ô?Åç·ßÌí?P¾ú ³ä[ ‚¨Ñ$ë¼¼ÚYåùeòi«w·“ÕCÖÙô|¦¬š ­X÷È[»•¦/ß³lT̲Qšîkb£¦wåÚ(ƒmJ_ÌÒïœv)Òï_‰X² ú¿eI^ ½ýW/NÃÀ ›ƒÑÖ,\ýÅ|ü‚¸TW=é?ÿ”§Xp8êÔÌbRÇÿ‹?lÊ™ÅsâNcxM”ÁP: ˜åüîúî³»…Ì\2w¤­ MOÛÉ+”He Þƒ‚†&è4o~¯ÐÃ¥½_ÞùóÖmŽ{i°)ë¿`Õw¼fÓ¯9óÄ.³`ꪳŸ$•¨U_ßgç|ü¢<åb‚¡°¤çÆ!QK™-<1vr¼mS‚hÙý©žçGž^fÞ¤çAÿœø–‰ *ƪ EÑ¿¼üŒ~“ë(Çí3 ‚¼¶<‚¼ùü"›Žhýzg6»r¤äÖÄ¡ÿ…œœ¡[§“&÷Nì>~϶›âhýÂODŸ‘©Êº°t°çlÇlÊ«kã%š2$—dut|Nž<:p š¡5mmÑ–6\ž1ƒÍf§ÆÅ ò×=Težˆúâ—vÒCî烕}¨fÀËÛ¯}óZ}Ø€“R@ŽŠsŠÑŸíŸ ¯v¬d;Iô(fö©‡!Úõ~ïce,Æl»1fã÷¤Së§Œ\5kÐÂ^o6Zqm$7¡/´]A*K*ÿ„Xåå"­@ùë¡îmúàÐD ›†‘û$:|Ñêø› …gSújIr À…²}gï¹¼òÅ®á+§O=9䚟R1¯‰;ôØèD ¹½ã”]޲ïì_µØÞˆá²dÞèžj¤º•uy‡’ k“ÿœ£ÓH¿Í›Ÿ´M÷×ÐÃu~’„þÈUá¹ãfÕ”ÝÐL#Î)¸Ò‡aŽ>;:îzýߨvé㹦]WãÔ¨ü>%Ù(²éÌ‹b,–ü¢®•ÄÖU«>Ç,ú*4²©Î,ãœVDÈ2 >o7Ðo2‘Ûf¢$'ÙVW°þÈAͬa~SXòW½× *è´—%a’¶ë’]“£»n\»7u˜GãCrùFÍÚzСCÇ ~õêó#G״ß_¿¾4==öÐwÇõörµþ_Æ*9Mt$ݸt}ûÅ/Þªè‹@Öéc!±ýâù„ì9íký"çÕNÒ´ÑÅm|rÿMÉ4mÚŸ¿%ª>\¹Pjë®O&È› ^ùá¬Îô§Y…ï ®”Õm,c[‘1÷%¡íhAÞr÷¿wåˆvÍüRúÇG)eH§¾æ´¿þœ®MštãØE·¦tíI›w1ÙI£^@ý3'Æ„3'FcjˆõJÿ̤lú0ω; Ÿ`d7içÛq›®Žüzwc€›æŠ¾Ñ Qåš+¸…’Ñ+t«ïû=GçMùY¼û%6s~Ò²YÜCÛ¤¥^Õz3ˆHEú©ÓýŽêœô…¡ºØÉ®>pæq^˜A½!–‚F6Ÿµ ¶ó²cƒOÚîæzè$å}<­æZÅ—}ûÝhòCc%¨œWüÓq¼7£—Ò¹}€Ž\:úMB†\÷®fUŽœäñª{¯®@×ÉŒ´ñܭ䊩Éå'-爈½=zÐKJ~5~zøðíÍ›ìíÿæ‘éob¢‹ûoïþçóÃ*89S.JØ~î“ßX ‚‘ï·dšþ…ÕÁƒfHï ó´P‘IJʾ~(”ÒàÑ®æ4k˜¼ËÁq“û_`¥,Á*ÏÏÉEäÕÛÙç–­Ê1ñ×$ ¬Â´Ä/ˆŽ‡Ä—½õë|tÆ»†¾‹¦èuÝ´,êu÷Iè§–nx-ívh¬~cÃÜqRräî>VvîÀ¬ÀpyO`äzpúß¼ôÐd ‡¡Ú.=%½hÕdŸì‰ çOpêÈõüsí=è Ëb°Øoïòš¸£ÚàCTd44QæˆXùùöþUK6\Aü¶ÇÌóäžžÿô+”²uk·Õ,;>Ü×@¿T^s†xÌO*ÕàzHM˜ŸTÏŸ30X<úL•?¯ØÁ®ª` IÌsÊ@IZ„FÍé²òø˜`—Ì7•Ðr°Noø|r-$µÎZ8äíçÙ•Hýæo¦jªAÞ~ÏøV…hÕÛ¬ê[ú7ô›º™ZýÒŒÊr'Ëbý@3z¯Gi‡¾Kó+å|’ËOŒòò—ÇŽÕNÏ5DD´<@3¿]_3gíÁC)y&!ó?ÍYèÛ±fhNÅ«½ Vǧ3Ð7úƒîž÷ÝF,ZàÕÞjÙ­› ³æl×e“‚ÚuìÒÂÿ6ͰåÕî¼=!мt¦µÊ8ô'«dÔcÐÔ5ácÚñò憟ÛüOIŽXö½DÁ+òd˜1%‡Kc½…g Ëo^¥…Ìs7Û-#ƒ)üÎÐ 8ø`ù05Îÿ´ŸÎ…¯<ý=àÚ:güò)&‰óv¥ Ï1nùšBmgÏè~mé$]Úò.C§ù*=8…n™¿9dÖ÷1³'Ù+âj:åþà¬w'׬?ó­#2,´IÖf°rûéí‡_]ëfXVÞÓ뙢?Оë8ç&WŽ &¯7àZdË{G˜%¹e–¦HÁr§+õž+òó£ÝÝßß¹Sÿ®´3gòÒÓeutZò¸8‡°=èW½;$ôG¯Ù~ÕiÆ+öš~àÞôu7Çòh§lº°©n3¡ãˆGÔmåÚXÏ”â¹\î¯6`Ññ‹~78‡íC¿~ýSuÉÝ‚%¿ïέSîÞÞ3l#úÕxÑð· @7Žf:tYüà™Éqëy-Ô9QÈ@c™Ïø60q§áž(Ã.͸¸méâ­ÚyÍYwk׿^Îîw(Ar›ÓoKæ'q=ÜÃPsÞŽd‹%¶?î±|”Ãu%*†Žo?)æÑìÁ*Â;‹ˆÒeþÁY'lÖ¤Wuj|kFæö¡ Nî'Ýðv8µÁÿ›¶ÜfíÖù'‚Où©ýùô+ßÄD^vŠh|‡aK|»EoÜý|ÌÊ΄Ί”È5wÙR® Géò˜ˆØ¼Ê¹ø‹Þ+²R舡‹™äÇ+§ëÓ­3$—?J²³9;ç<Îõ^6‹õpÓ¦þ›ê¥TbtsádŒ½—œðš‘rrÃ"¿ÎfÜ:¨Î;´50q§a L”‘)¼Øuj®ç¼­ÿíí"ß’Ÿà¯P"S¯¤†'è›9?‰ëä$4ôà?roolfem¬¼„ˆÍ߯žºÌTȯüFªë‚¨é'º¯Ïl|[vÞãóïÈ]šrN^ÊzÙÅ}GöK]höc%Bveöƒë½ÅºTÿ@°ò.[OÏÏè»Üm¸æéc-ÛUoÅ*LÜ;Ñ}þkÓs‘<ÆÎ7·r®ZÚ;ýmÌ¢ï䆯jcÞmktH.¼:y2÷Í›6HÜ·¯÷Ò¥4ßJBèñãÇÁÜÜ\Ð…Úè–ÁR -Šõœ[ˆm0ã`åyNÜi¸ƒ&ÊHHi®òRAº¥çZk…RÕô~91•ל!ó“ä¸MNBC×IKÍ Cô7;ƒ·¤|´ cjIáÐ]1xŠ¢ž§gwõFF=´5æ·ë+gü/îì›$’¿ç+ÏYëæô–ÿñ&ÁHÛ,:05ÎéiÍ?Yßoþ/lCüéOèíX7MÙZ…3Ëò‹”f®þ‘… î­›½þʳûœsž÷ÃÜ7°pÝ$㟧r%tFI±õß±nÛhëð*i…„Ћ¾-‘Ð4é3qœÎã l;Ûe·3úìù_Ä”^[+$©8VeQ1¾CÏKéû¶G;ozåHEÊöéóœàT~1d—¹‘SØêñ ]A°ñÞk)¹âl+ͦ—ä|©Ò}o¹‡ŽQÚ”!¹üÐeâÄNÜX¸ðùÁƒlV½QPè{³¤äéž=ÝgÎäm@xtíÚ•B¡”Ô% ÿ–,×ýs™³lù©_sb¦Z¼Ú6S‚Y¹ºWÄ|w.w–÷½µkÓ]ªÎ®«3ý…÷,i…F>×ç†K(aþ¬¼ö^tj²Ï9C\ç' ç69 =U\ÚõrN._µ?ú ÖvOŸZäço”¼ŽçL£°þªÚ#×N‹q$âqíg9Ã6"ùÚÔN‚\Z§à° Êa÷;1TÛˆ¬²ÿÀÊÛ‡í±o|Qh ­ûÌ]ÝŒcŠÎ€ÛÌh䑈ª½‚"zñ¸·é•#†·ÅOÜÖHwÍê½j¿ˆKÑR´q§+ 2ššžû÷ÛLziúô·7oÖßàÑæÍ6Ó¦µÊ¢*@t•••5¾@4Áï÷¿GÔ²"nÈŠß Ê!;o…ìüýonwÌêMvá2ý…×,œáJêV^ƒÇŸ÷6k~÷ÐÃ= yÎߎ~ýþ·×qOÞ3Ø%Ù©¯ £/~ÛÒ¯æ,)›þåòô®Î[÷žý0‰ËJ@„°˜lQXÑ]ÙÂbø¯â㯄†æý9¨£ðýûVZT€0‚œñÏ‘PÒDŒÌ=£æ¦:=êõsŒ‚!*šZ©!X¢EØ® Ä™þÀºnnOvíº¹hQÙ÷ï¿ÚlØÄh ’0DŠ$òõÙÓlº¹Võx VQrÔÌùO:N¼äªZD•¿Ü9{Ùõ"¤äÆâñÓ^L\l''¼WU©‹Ç[™ –°|ù£Í›™tÎ%v>>x€~©Ûغ:­´ØÕPÒ0œÆð£'³g.u6^/I•À0ÊKËɺý&<eËùŠ,²Ñøm×Ç7sdµÐ ÑœÖ®í2aÂ… _=Êf³lØ0øØ1A× õA€{¢Jx#j¸,<æ²P€$=O<{îŒ õa187WwcccA «£ãuäˆõ”)—BBRãâZ¾¨ !ºÙž¿H:sö´ «À Xw7>Ǧ½G"Ξ9¯cªÄÏNAþg±Š ‹W­Z%Ø2ÔmlFß»—|ôhÚ™36!!n %nDèf;s"b“PÉ~S^\TÂÿØÔÉLÉ}Lg>w ðòCÐ%ü„Áûú"?§îÂá–’Ã-€(‚Ý›„ÍãS¹oÿa~ɇßÂFˆ·¢4Í€f€ @3@€  @Ð  hÐ4hš4Íhe4MFFFÐUÚ h •åçç º@‚ @3@€þ÷°èï.¥Ü»òên]g‘Ï´žÄfíœïåÕëŸþ»ñµTËjñNy\ý–¶*@@€þ÷`‰íû›ËI|¹›ôµ;·ënâm._ôàÊ3ž-â ´X`ßš}«0¨¿»œþh[ E«4?1&ù¥¢ù°Òhd.þâBR¥MóƒiýÚÄhaÁúšyxqbÊQ [™_E1Öõšin(W“p™y’ãddaIت’2 ¦¨¬ÒØrÆ*i«2íÐý±ïKäÚ´Ø{x‚ÎŒcí$šŒ1¬Ü/··gÜ»ñùC.¯¬Ö®“ñǾlæ·ÛIÇ÷§gb l&¡}û¾“,»ëš¹Ùʼn©§ö§¥|bà0¬J„¬ÝÇØ#@K™Üü—@˜@€¬Ò¢Ï$ƒ©ÑÊ$¤*ëEø˜g{7),[¬.‰AʯŸ—Fôqž7Z‘Œeå]¹µru™Œ¡Š’ºI7ÐnXQüšxŒc„§gó†p°sSŠˆvz^v†e¯^Çl{&<Åd¯¹*sWÑí;k–|Tß©·,©2ÿêô³Ñ!åäöe›¡ÙeO®™•FÚwÎ:i»ìUÊîé·W§VÎ]£¯o:ˆ2È2Â+§á>QJ‘ĹMPUÖ¦!w³r ê’zzLFBè"OÆr6”í¡o(ùáéÍw_GȪþªSy[]k»ê«pÊT$¼zùåK¢*ƒ ŒÂ;{Þ•Ñ:y¸ËrÎfK´ëæ£|fɇ«wËÌÝ(ØF—Q|wkZ¥Ã$?Î#©o0dhÚŠ¨ggþë8ʆ#G º @ ,U΀úë_šR™,z“Y•ŸËDCµ´äÏØ‰#H‘¤¬¼‚ÓÜZÝã(œ`̪b²ÑÞÙß^~B}¹Ÿ×¸#ÊS¥ì¼×¥ 7J£×½cåç$¾E=•ß6°rŠ’QoîTÙ(6ëÊyB´°`æ~¹}8õéËRO@èÙy¢\}ŽÐN‡¤W汑vÕš^‘WŒ ª2Ô6ûé1‹KKÑo¯ž¬ |ñã|3³ŠNÂK7­Gfa1Z †B&Õ:Y•’”DÜÏ 4Ž·~É|Z8”‹¾|Ñ ÚÜËP‹T~ñ?Ÿðã>¢¶—õaúíè]g´—ÃÑߟ{‘J'˜ Ó’k럞±uèÆ”6î@´@€ ô·™Osù=]9.Œ%-»È‹8ùdÓÈg<‚“•w^bëØC²ñ±È-…•¢H¢ßJJË¥ùïvEÊžÄ̤`õtY\ÍDBœ„„‚I;ge爑äÌÊ+HIøÖSVšY—S#ýí‚„Äv=UÖ|>vlhÒ‘ŒcЋrʰí•Ô¨M¸„^º{n¬´ø#_4G+KáØå¯_ÅÄÌ­]-àm …V^Ë'8{ﮬèiYd U_#{ƒÌ£/SvÏE†/±0™ÖÝzÎ݇)Ÿòjïs5íÎ3ûùó4¥±X9G »Ë·o¹¼øŒŒ‘wÃH¸†R*+ïVbüÑ\ôÖûÃ÷âÊ;»;bŸïz-™… ù×6'’'š™*beûÙÏÄ=‹?’y"4#A²ít{까PÈ¿Ÿrõú§7åò!íø†rcw#£Ü´?[Œ{è%;[Ï\+}jÿý¾,"žUÉ"kyÚ…h)À;ŽÏ`™w µAœ¼ªGÏ=·ô ´ýy³âCY>A{ü‰î&´¹˜Ysnú¥Ë ©iÁš–4+«1t÷°¡Mí +Û«óè^k7uÓýª[R_«I}­êîÝÍÄýšW»©~ gª…Q€…QS‹­¤µ–yÿ8Ѐæ€-D&9FÕÜØz-ðwkå÷AÏf 7¼2~µãhòæ]$/½ËA¼¢ênÄ—ygå^¾·sKæçRœAˆ­ñÛŒû·>ÌcÕ4]ç÷è­ûk!wvEFæùÈ”§©¡—0°22&íÝ¥ÒïJyòšŽdL<ÍQ/?ý8þDúkº‚õàÎ^ÞŠm8ï4hÀ߀-,Ðôü+ÿqÛå|{41Ï2½Ää_íÌòðee“9û-Ôu÷U#"Ÿé<;"Û8,]¡Næy?:|\æ+çd;…P¶üköóâ.=õÛ'½ŒŽ|.o´ÓX©úFÅ«¤ “ŸÚuŸvDGíˆQ–qé|6SÒÛtäz5ù ç.}Äk»hÈáql Ùó%Þá½Ôá¼f›ããV£Ýq=ÄêíkKÊ={wÏž¬OŘNSzš̸³æM½oˆ•iÙ‡{³’žäTb¨&Fþ‹, Û‰ðŒ‰Î;S©Ô›7o º@›€-j'`¤úŒòï³Ñ݌ƹ÷:³‹Iív"m;Õ@•Pwû-—|‚¾U2x÷…!‘%ÚäI€¶"°eÞm«/ c@)¼ümö×ÊêÍ(~°éù'œòðÉÚÊ5ï$¼d§¾ª´¬ê‹ÁHÈ9…?”|zMšÙZͬˆ§¾}ìÄ+=§ÆÅ}zôÈ`à@U++ VˆNªóñ@«‘îxb1ØYÁÕn*µlö’œìG¹¦¶]:딦¼Ž?òñÜÌÂÞ:zvÆCû³¾\{zêfò¡ƒš‹§È‹î¢KÏž=ÃáÄê¨ ´P¨?£v‹óÚ¾¤ÝIG¿ªÄãçËI0ØD%‹¾FZ¿>Rÿ½=ÚBSÝ?:€ A/óN¤J¢q‰Éàt‰°ò¿<~ Úši¿Ï’̬Ǚý¼­gèýn]ìÛçd4,§ô·¥99wÃÃÑ/)=ww}OO­Þ½q$’ ëâ÷Ïî0 bý¤ØÇȾ7‰Ób-<=~øÙÒפ³ ç.c#lj­´”¼R–h5Ð]S±@ÄA€@¸ Ù2ïš"gö¬ôG™îºõ°ó¯Þ?K7ä*OíiÓùîͧ¿°èiƯË)H¤¾Å—~¾Þv]|ILlt6›ýéÑ#ôëú¼y=zxDF¶]=5ø| Õ@wb€èƒ_o5¡[æOí>YÿÎŒW—g\e™Y›ÉH‘FiYn‘¤¶±$ûëCÇÉžkÛË ±Š¦1(X=yå»èŸµg«Iñå,4†^…T°ÊóóÛ® FeeS6#JIéh2l˜vß¾>\ÏZ¼»kì Äüv@¨ñw™wváÃçqµ—yw@þÛóôæ´!ïrD!ÈÔX#in5s“Ì™ÈÔë¯\gq.Ó!§¯jícÌ8št-þMECùv‘î\aΣÿèhÆ*¹rks…žÓx K•6ÑåfˆxÍ>«Vµ]oÎ{Ïë^ Ý¯š›õ==ë–n;|>Ðj¨»±`\4@<@€@¸ñu™wŒŒµÙHô«V“ÍD{›‰u7“2Òó]§ç[w÷n†>Ý~ÿKFÙ)ÔÙ)´©}‹4 £Ñ£𛆠!ËÉñ¿þh5Ò×C,¿.Ú+ÿvRüÎÚ‡#÷Ž~5puÁ?Ù—ü(Ý(ÿ꺅ƒÌíhïÏÊx]ŽýŽÝŠ8AW.Ë > Å£øÖì[…AýÝ›¼2Z|^æ{w­×.Òð¬D¤`d„æfô‹Ö¡ƒ€*ªÆ×­Fºã}ˆ…´³³…~Õj±Ÿbo?¥ö&zƒé nz%  Å«4?1&ù¥¢ù°Òhd.þâBR¥ «Ï˼ÏäÕ]+Ýu5k¦PÕÕ}|Lýý•Ìx]ßXÄì@KœÞ61Z¨±¾f^œ˜ò…E”ÂVæWQŒu½fšÊÕœCbæ=HŽ?‘U„%a«JÊ0˜¢²JcË«¤q¬Ê´C÷Oľ/AkÓbïá :3Œµ“hÂuØ™ç#Sž¦V D„^ÂÀÊȘLìíkKFxVÂú~îöÖÍï¾U´ûª!ÉŸ2²«¼„†á ÉFh|ºö‚ãó2ï3èî¯ÛÅ# ÑÚ·~ãFûž=…joDì´Äæ K …«´è3É`j´2 ©Êz>æÙÞM Ë«KbòÄÇëç¥}œçV$cYyWn­\]&c¨¢Äùx™¤h7¬(~M<Æ1ÂÓ³ÉC8*^%m˜ü¼Ð®û´#:œ5xeÉ—Îg3Øh²æY V~€Ý¤²ÂÅ;ŠXjÚC,©xú§›‰w=Ýø–¶ÕL]ð+‹6>/óÞpwmÑ.r¨è— «¨Kœ´ê·€°-Ô°r3(AUY›†ÜÍÊ-d¨Kèé1m ‹<™s +ÛCßPòÃӛホUkÙÚ¼Œâ›žÂ)Ÿ¬­\3È/Ù©¯*-‹¼0<+ùµ?^ÞTIC }GQd†ôñ6~óå—çŸèíN‚³Ðm–y5à@ ø ´PÃRå ¨¿þ…Áa9‹$³Ð›Ìªü\&e¥%þùäÈRV^ÁinI_¬ü/_#ˆ¶fÇZã.HfÖãjFxòª„+ ±½ƒ"áò‡Ìû…UÝa=‚Zк @ 5fî—Û‡SŸ¾,eñ„ž‡ ÊÕwàíäpHzen©Yã^‘WŒ ª2Ô–þH™…Ehô¥R¸^©•g%<à¨R)üRÉla9B ´+ÿ|ù&¢´¹—¡©üã>áÇ}Dm/-êÃôÛѺÎh/‡£¿?÷"•N0¦%×â)ËIâ\×"k¨’Æ@œ@€^ô·™Osù=]9.“ý%-»È‹8ùdÓÈg<‚“•w^bëØC²Å×ÀÓ¨Ò’—[XÈ@Ⱦ/®„+faI)‚P;á1ñFˆ1 á5€¸âujÌI¬Ë&O5¡¶úä/×ÊÜ7§)Zh!ï²Òe¸ëþùÆh°nÙ¾W!†¶2-›Ð¢NâÏ5wâ´ðÂ++*c22Ï¿xhi¬Eªøü_FZ‚(VßÇ®HÙ“˜Y€¬¾‘.ûcÍ]œ„„‚I;geç§Š‘äLÞ+HIøÖSVšY—S#5r5;<µûdý;3^]žq•dfm&#EB¥e¹E’Z T»,%åø¹r ›ý á øEGGC€@ŒA¾^Xy-Ÿàì½»²¢§e‘5Tm|ì 2¾LÙ=¾ÄÂdZwë9w¦|Ê«½ÏÕ´;ÏìçÏÓ”Æbå-ì.ß¾}äòâ32FÞ=F #á,in5s“Ì™ÈÔë¯\g¡åôU­ýºh5T‰–dÍÞô¤Õg—جªª2†DG¯žÃ‡wh‹ˆþI>>>­ôHŒ¯ Ûæ/Ùu%«J’Œ)ËýZJÖìA{ü‰î&?¯:Ç,È97ýÒå„Ô´`MK‚•Õº{ØÐfv)e¤ç»NÏ·Þ TòÉr®«Ÿ ¼£ µ°‹îÎîn¿ßpÛÍçãL¥±h$+JÞ7|Àž7e¬Þ²BÈ$ &l;ᱯ›ê¨Gç=1FMj"l6³ŠÁb0˜ML DÄaÇ}]®Êï3‚žÍ@oxeüjÇÑäÍ»H^z—ƒxEÕÝžØ ž×…ÐEwÿ·#Cyô‘áÕéá¬`ì5º{Ü×ÕÚØyç]oL½¶¶ YÐ¥pÓôòþܧäû ³˜‚-Ôj/ÏûÇm—óíÑÿ5ç ™ÞGbò¯vfyø‰2Œ²Éœýj„ºûª‘Ïtž‘m–®PÊ_\ð¯bWä~+Gr_¦æ1»þ:¹Kpè'V§z«2¢Æ|°›*è:xhzyBþD@k‚-¼j'`¤úŒòï³Ñ݌ƹ÷:³‹Iív"m;Õ@•Pwû-—|‚¾U2x÷…!‘aÔŒŒ‘m{äþ‘Ž#‹v,ÓK“̲‡Áá®È<¾ hÁ‘×Xy‰ÒR9§¹;Ö4–ÂT¾Ù?3hÁ–«¹îqwÇ¥oY³ýØý*sã»O‹%̇Ož·p”)¥4)rYøö­G¿ö[y`ǬnÅñMyœ“¹7ÿ[bBâ]1=cÿ„¡!ûž”ôX¹µOÊÁ¨“w²J$õ|6œŒ§OFª>[2~nÌ[œ4®ô[ÉqËÕ½ry7ÿ7yêÒ§ôŠ4oË«4€%>n^^«Óø,C§ò˜=×ÙÃÞï·#ç?ܹlͶ­'’¶½{³Ãã¨}GO_žC—é›ÕÏ?&GJEÉÉ,jï~`w{b½-#—É^ºnsÜKƒMYÿwàä-FÎ 3æí}\B¡°òsÙíÍI©“õŸŽxêûõE:¾ÞiOí4bMxò)Ëu·7ïèìí4i|vUÙŠ=òã §$Ò\¦Ž0E"e62Ä56¶rá®P{ü­ &>NDY#µGlÛ•|ÕrÝ—¯4§Ðý£–•¡5¨Ø¹—o|MóÚ>Ü úƒ¢†ç¢éúGç-Ý™â»ÂôçYý¦Ö4 ¼0]¹Qsv~¸»mÅüÕçS·ûŒì‘q~Ч»)Uˆª™Æx$ÅŽrÿgï>àzøÿ8€ßwÖ·½‡–J‹DE2SÙ%dŸBFEÈÊÊÙ¥…ÈÊö¡ˆT’‘íÝwþï[¤ñ-íï·z?=rß»ÏÝçsßïéûº»ÏÝ!‘)IÙÌÁ¢Õ¿Û‰Š&zè?CV8Ë]9°' nÁNSjÙ»“GK'Ÿí)„”¾märKBó_“Í!u±î#²÷”­E™‡ûlç1–š²b ˜¿¼Òü©‘Ä­‘ÖXñC¤Ê_—|¬kMû×XBe›y7IÉ¢÷ïA FvNC.¡/MŽ+E”MT*»dMõ„Ð;q9lC¹šDûGßA€†¢lîì{ÙÖzžÍÁè“Ï ÄsŠäë¾q}ÂÉÜ#šzKBQšXgà6ýo¡ÞÏãÛï¯=×ïÝÁ‹ó®uE3ZQI#—ÓTXi»c÷˺ûñžä=]ÅÎ/8н—XÓmì–YSVæãÃw†Äå„)ØÂÄiÐn«(³A…I;d`)bh–/Ì.æ>x¡q|Hœ‚ØÃaBSu+/ÛÃJ˜Oµ•9x¨¸„Q§"ˆ’GÄ7†}—µ\–X75tëå·¬ëwô¯Sâö ÀR»œ&ÊuwÝÇÕ?3>bÇ¢i¾ËF¯µLñ7m³‹äê^ÓÒœ/„‘ºß¶ß¢Ü•1O½{‹bÉ[Œº®hÐŒ8ai)+,û›×Ù%¹%""-Ô¡î©ÒY@€SIRð¡_ƒ&éªW~YsJ~ÍG”Í´EÈJfÚ8ÿçÿK)\¬AkØef8yû•$Ÿ^áúC|À‘C’ås‘T½œ¦a|¹y­ÈÂN‡B2»áØ—+šî/ÒŠ9¦”TŠÅaf³yGÅ[dMKÞ]}Á–™6Ü@´‘K kXw§ì}û©Ñ)COú¶Ѳ1nÕ·´Ѐ *~±mÅþžþ³-ä¹G¡éé—×,¹Šrd¡!‹¼l’ÔðS³Ø\Üæd*Gf—ädd!RJâu_s†ë·t¶úißμ£=…*Æa¥¿œ&a~ôöÍ0š¢B@ØyÉq?M{]îAœ°¤ò(öizY5V)¦vo’¢±råæþ Ï5Fȳ³S¼ÊF…Æ6 î5­¾jÍ£‹©I QÑ!ÒõÍH?ž_É@%ž%K1ÔªW\b$m¼uíµÛ;0É|~W2ÂHذ3Idäé™:õÜ,Ð4ƒý”˜}'(™ß iX,†Ý!.UùöŽnÙG…ß­ …`DMç.êüèdCDZQŠDÏ+¤Ž¿ë9V•ûí-14à~ mᆥ½åg!U¶[ßÑÿùmqG>yûD¤#Hηe™®ó­d*û»ÍZÚçйéS»VÆclS–S¡4ñ€ÛÊÓQ±èà ïñ£ŸMÚࡾé:K~R>‹ âvyz‹ õñëµP_ïÛ: Ý­²’ÄâÌBé1ÇÂWè—gGa3%æ·7Ìצùôœ°x¢lLÍJIz v»ÝtÞéÚ3TRo ýøa’¢ï7k¥â±õÆO6û„}AÌ€ekpk–›¿Üî½7A .­_®ºd•#.xÓÖK_$¯§¿âÆ<Ötã°Ç»ëjsß+†)Tmž“Çv—³w VÞ!¦3|ŽÇ°î¤ðÛÛgz¨žÜê Xµ¤ã’©ªïo§ ãa÷ÿò§.÷°7õ¹{‹ææigtXL “—Éìât*Æg÷9̯‘[êm¤5IS¤%fÞ<ŸÀïV´Ç`°øÝŠþ¶X¾ŸAW:s²;…#ÓÆ•¶ Ÿ?²X,yyytÇ3™õCk™úç ßd-R» ‡Ãb0Ñ ˆÅAˆêãVyåDy”×Jí©R­4€«Ú£BJd¤Å U œì˜à_µ-¦ ë EA€ ³ˆòäIå˧OŸ–””:t¨rŒ‚‚‚­­-?š]F|<úScä—Ç‹~þ’é OXm ‹ïO„h:‹âââÙ³g×U9ìååðTûð3ŠÃf¿¿vÍhêÔ¶oh_Jß_í¹}OèÝÝi± Õ~'ìûÞvÝ®ß|›Í&Èvµxûž¥dðHYʉ¥óÖ콕eúhÖû½~áYNwc=ŠV™”!,/…d|ÈWuÜrÒoœÜ'^%WÎÖ¬«ôÃÅ5óÖœMÂJ‘‹Š$¯:°cš¾0†ñ5rýìUAq"¸¢_Ù$ë½·ŽÚË@¤ €ÎÂÌÌLII)==½®cÇŽmËö€ö‚Íd¾:s†ç¤äÈHÐàŸÈ:Ó|·dE….«6– â¨F-IòÇ’d{n_Œ÷²§‰%ÄARTî1g¬”í¾ÃE¸chÐt"ãǯ+@O˜0¡Ú žý7*”åå}~øPmÀ€¶lèxHj–Ý…|BbrXfÔê9–¨h¢‡þC³èý{¦¼ï4‡]k!¿KVWúñÑ[¢`¤Lþ]“Œº$™’T¤lÝGdï)[‹2÷ÙÎc,5eÅZx@€N¤ž^ÐðTš““téR=Щ AsaI¢dî3âËxw£`e>>¼qgH\.A˜‚-LLAÚQ™7vIN1‚|Ý7®O8™{Ï=‡%¡(MÄHÛ»wXÖÝçˆ÷ô ïé*v~Áî½Ä 4h(Ðt"uõâ€þ .ÅYYVëÖÑ Y £¨èëÓ§èo)==fI ³´ýÉÿú•ßm‚«,ñЖw#VRüG9fÁÏB+¡(Ê«#u¿m¿E¹+cžz÷Å0’·umðí±Tq*‚(yD8w›5ÔAê¿@¯45úu¢oÛ]Œ×¡KÞ]}Á–™6Ü@´ñ÷&©˜iãüŸÿ/¥p±­ÊìŒ/7¯YØéPRFc7ûrEÓýEZ1Ç”Ò*·¦h:ž½8 ÿ ½»‘ÌMÁ"Hí.Ë•8ÅoŽ,?ðYÖiŸ³:ÏËQñ’jHTtÈ£t}3Òç×c2D©-ÀJ ^6Ijø©Y l.ns2•#³Kr2²)Éï‘Þ¾ÆAST;/9î¢i¯+ é4h:—Ú½8 ÿ ØY÷·{ú‡…sûò„9õÐ)ï†Áa1J ³¾ýÈCl\0_Â}|OœKAÿþvÿ/êòeƒúÌYèpêš‹yXqqIQn ­·[xÌj[i4iÓÓ‚¼}"Ð=üœ=nË2]=æ[ÉPM–owy0{Ç`åb:Ãçx ëN ¿½}æbö¡ÕKâÆçð-Õêò°p?¶pÃÒÞò³„*Û­ïèÿü¶L•2Öû¶ÎBw«¬$±8³Pẕðú$~¿•  @§S£ôß4V²ÿ²èO}eV8¬¨:Âuw˜+Ï’Ä.ã7†ŽßXmAmòñ„ÉÇÿŽ˜¶à÷€›Û–ê³TjÕEÐuÚ}ËiwŠ\N>q©¯ÉÔ 4N^ÐhÐt:U{q@ÿ Z æ Ã‚ @gTÙ‹úoÐ  **ÊïVZ h:£Ê^ЀÖÍï&Zh:£Š^QQQÐh,ÐtRãÇãw+€ö4”™™™¢â¿ž¯ @urÆÆÌ’~·ø 4ƒQQQáw+@;£9t(¿›ü€F€Ý|ÌŸ÷÷¯^èfƒJÁgý,¢¨ô¿óìæ~7Ö¯hߥÛB#¯½ÌB•%±ï¶™Pj–a$m5ÑYþÁªö9xüÿE†µŠ4ûgø£1Ñý.Ä+÷D­†í½pÃé{ï"e2|¸Ýb?Oî»Äʸî½tÇÅÐo„LFNróß:¹ ±êœç‡(O¼Å0ðK~¾T‹P9¾Ú‡+6:(1hœ|Å›^–|t™ç¹ÛWo§(XÚ sÞä7]»â™µ¬Üø`ÿGB¦1„Å…‰=?+«”¢Øµû€×W«Z+*@€n&Nþ#s«zûレe(‚E³M~Âñ©#ޤ³Hb`ÄI\}²ÏÐÅZ¦þ_‘ÏÇw=ZsjP É ŸìÚõš;Ð}ÕùYJÍZ ‡Å`²™L§9K阰R½ÎYÍ»4¹«ýù‚ÉRÿ5h¿÷¹p²CÖnËþéxýÑj}RÙ?o}IÁ#Œ×G.¼_´Z·2åþùpõ¬Ã ó?‡Î\jñ;B“´gìst׎!Áã%+ªáÅûèvKaîñ3± iŸ4§øÓÝ#˧oOó„ÝâÚû^;ûÉ1ÿãç÷¼-䛼£öÞw£Ð?oñõ÷;òHÖ+!a]·š:.ÐÍ”ÿhëT¹g§–§gVTÌ óÐM^"';ÒÙöη·õlÞwÓ?PT-»ÿº÷òⶨm'ÈbÿN`ÿ¸â{MiD—ï‘i-P NvLð¯*á¹Ö®ýÀJض{Ø%§k‡fo·É„Z1šSðØ{Å+‡'y„ ÖëÇR­wú~š±ìÉñ3ï–ùÖ,CèârxÜÕ1«Cg. íwaïCÿœÜ»îÃÝ¢Xc.ÞÚ3Föo UuÀlO—°-¶ŽõètÛC{ßkÂJôv]¯-~÷àí-ðnÕÇ­òʉ8ò(¯š&!!!22’ß­hFŒ÷ý• @7§4ëW ’õ&1›ÕKñO¡8Áiê3\©kƒ¾ôû¯ÅšX ­ßâa笠¾-øÓ˜]*7zò ¿”ážÿ½Ó"º†¶Z»ö§8aïÖ]<Ú<;`êã%]¹ñ…þnßâˆî~±}Ekwb}:–jºÊ~üçCËž< |³z[rÍBd…'6†¬ AóïÍJö<ü Q[±f„l­©¤®³w{Z?ÓvÒí¡“ì55nïH;¼u AaWÎGhõ°äwC]Ú똟Y¹Û·úò»!@P@€nŒX7 Uäw¦YOË?àãj©Bá~ `px ÂÎ}â?ºÇÙ·LIËé ®Y9F9ïþ^Ÿí{DÓœ7Ø>Ï„ò-rýìUAq"¸¢_Ù$ë½×·ˆXô߆ôÒäq&·hš®gƒër>\\3oÍÙ$¬¹¨Hrðª;¦é cÊ’Íš¼*06«‡×¾ɧO†ÿïs UÓÞ+`©Ú³Àãç/E¿Ê ‹õ˜{üÒîQŠu|ÈTã¹óõ¬ŠÝu,q¦·Áï¯ã‚˜Gq³®ôžZ£43ãÎÎ%žGŸ ±s²8ZË·otÒ'~81g‚Ûñç…}7íôöT`øÃ´BjWÇáÇfé _š¥ïޝöܾ'ôîî´Øy·.¨¹v8ž‹Æ e)'–Î[³÷V–]è£Yï÷ú„g9Ý]oÐÏòÔg\Hßóùê…gÇ_uQÆ| Z²ºâÞȪgþ`}»z,µçÊ^ŠzÓ{#Ožœ=·¡‡µf1 ¹Ûâ“>ç :´çéòCý„Ñ1ìï—}oè.óÖ ^«V’SøtµåÀÝò{žÇÍ×¥`è_‚¦vŸ`–Ì~{ÙÅeÿ¡„[&Ûü¤ ^~bºwáÓmãÆí[ä6vôÕ1R²Î4ß-YQ¡Ë¸ ÁIYÕ\»Â§+ëX¬ IËÅoKB„Éö{ô7xþìﻊ[ûÃär·ÿŽxëµõ¦»Çµáû¨ë¼Ò]B¦«x”d¦_9–j²ª ƒVž ƒÇn1ë/Ìc¡ún.­EóT˜EÍMÿ÷‘…  éæÌúv¾æFK³¯w;GLêÞBjl+ø3 3ëz‚‡‰ýó¢]—qÓ± >Ø3°»¢7òL¡’¥Œ3í‚Q]yec‘özà²=í5¡aøûM¿Å«>È–§’qù¯8ˆdÅ^;<ªDVæÝZ{G*/y•¬XJö}o»n×o¾Ífd»Z¼}ÏÒ2øz–_{GîÖQ{™òw®”ç‡}&Ðp › +5x÷ó¤Aþ>[öžþß÷˜s>1ç6ùÎ ½µÛ^P»Ï™¥½wÝù½7›Ûˆ¡©åýù³œ)§»q¿½èéOKˆƒ¤¨Øò¥Øî;\D©yPˆ“ycÓѯ"öÇœ»rgQ>Ç’d{n_Ì6«Á¢¿‹È÷9ÔB]†îš•{÷Oý¦9Äahwnaõeã}ÂÄ.Aê ÐNÉa©íâG—În¿½ÉÂ^ CO:æ÷~ø¾a2ØÂêÍȺ±Î?‰6&`ªnùv¢²ƒ—»ÎyÏ ßNܨ]^DÖt@}it¢ÂÒ…Fû½Á#õhVïb+ÏPëºú¬$¡[¨¿â•(;¡ž«Ï>5ðày×IbÄñüÔ˜_.K뱪¯8ú†á†Nï…ÊHdZj¡á±ÒÃwe¿aê¶áñk",÷‡µßô\®½ì5!œ‚˜U–ƒ·–ß³©Ÿ8úÑç„XJŒýÝš÷­ÖÞ2‚äñ,)R¾‚Šc@H µ$5Êwòè•“ÙobVé‘êZþâZ;r¥åWZpòîºñ>âЮ7€‚Ý0BÚö«íWüò¿àýWo¾šà8­oêÕÉ 8bWçE¦ëDìŽÎ4JŠžx:Dxz¸fùw$©‹u‘½§l-Ê<Üg;±Ô”­ýTåÒÞ2#åßI„$£.‰D¦$e3‹Öüè°$!ô[‘Ídÿ¾dC¤þ¼®ëË#e½ÄQúÒÑ+Û"Òmgˆ=Þ~ 7ëš)úµ\=@—¦FÇ•"Ê&*•™Ž¨hª'„„Þ‰Ëak×h‘ÛŒ‚ÊfÔ£ÞÅÊUvDE½.±=ÈYzp ²?ûŽÉ™+ÏÏŒù)âXZ÷UwHÄ)Ñ yúôÒ‡9ÖÃÅyÍA1ö\ÜÃ;dæÂp‹ÃUB6–,Â'ŒFµŠõ󿯥»î¤f—²œŠõò½km¤ÿ†°º6Úz¶óZþ±…•,zÿyKÊOìsØf”¶ÝudBÔ¨µ£íÿ/¤¯ƒ¨íKûØkbgF­Û“Bµ»äa!^{¾ºvxx4ŒgÉŠ-¤¤¡ ‚Ç "š#¼Žx\0ôÙ½çåâî1vžsÑñ>úЀ#Ð2 @· EÙÜÙ÷²­õ<›ƒÑ'ŸL¶§!xÕ1î6K&Fí¾úÝnÜד×g_û}_8¬´Ý±{‡eÝ}ŽxOòž®bçèÞ«zˆf—p¿ð¾î×'œÌGzKBQšØr÷„é½h¦úÑMwœ~g­ì{KÏc#7ö°«be¢‰ZQ˜ô7¢a)bè7Tavq¢ñÅvºN‘Xi‹1úÈùjV¦Ò¼{õ2ÒÂŽÅå|u7Ó\þ{ = ýUp5ànÖ°QR<37µûòÀUA=7]t]8ÜŸU9š¤Ð] ‹|úñöQûûg'c³6°ïày}èSïÝZß_¨ÚÂêØh±õlçÄÊ||xãθ\‚0[˜˜‚ Uö›°2Ã×-Õ¹à™üƒð;€v±×TšróE¢9 Ï£¸uïð4±$IͲ»OHlBËŒŠã=W];r9âÍTš‡S{8Lhª³nåµmX ó©¶2—Ia¬ìPw±›ö„%*¿¾§3wLe0ÄŠuwÝÇÕ?3>bÇ¢i¾ËF¯µLñתºx,UœŠ JOÜ4Zé£"éÍXÔcÓâ;/O~|8¯˜8ai)+,ûûµË.É-Aã·´ÉmjÕõ/ÔÂøøcdhr¸½äŸ(Ãþ~zŠÓ·ÙOá½Ï!d²êÔòà^[.Îö!t\1#Ù×Éwïá+ïËÌjÞÑS±x^y‰çFkJ©o;o̦î·í·(weÌSïÞ¢Fò£®+ªLf~>·ær÷…Ó÷¬˜qÈþÎöß§§ì51ó¾ç#ˆ¦´0¯öÕ»ÃÓ´’X’(šéxÏU׎\ëq€ß @7SIRð¡_ƒ&éªW¾“œÒŸ_óe3íß41´þ‹§È_Øç»x•T¯­Û*1¾Ü¼Vda§C!HÝpìËM÷iÅœjš¤b¦óþ¿”ÂżN5^mÂ’A“oݺ¬°5©^•BÖ°îNÙû(öS ¢QqÆ•žþôm1¢ecŒ¶êkSk®± &úûàÓ™Ö;¬$«¼9XÙÁs­Iw®GDýç¬PGhê¹úÔ²3¿÷ŒÊí §4~ËÂ}wíY>?x‚|ÃöXêØhM)˜:·óF)ywõ[fÚp^;ËÞí}\oOäj™ w:Ó—Îa_$3`ÙÜšåæ/·{ïC‚Kë—«.Yåˆ Þ´õÒWÉßë鯸qa?©?ß•¬_Ñ›–l ½’OšâðÎaÙö•¤hýÜ\ÊØWtß(|±Óm}È­gèà‹F=›à³{‘¡©ÏÝ[47O;£Ãbb˜¼Lf§S1>“9_#·lº”Ž ¹ûVúIù,2ˆÛåyè-ú%êã×wç’nO·ûž8—‚¾O‡ÝÿËŸºÜîÆÚã½X´±ô´ oŸtÉ9{Ü–eºzÌ·j‡wûm¸ÒăËÖE<ö?tøíNÇOMlÜ·,1ÿ}Ñú¡ù­ÜvêôÛlƒ]n«¿®\;Q½bªôÝÑ5›ÃÞ3Ñ€³ÄÎá¡™ !7õùõªîïx…1ó:ù_èàUk%kϸð®ßå}ÛÍ2÷¥‹IãÙ%¹YùˆL·‰kŽšd\ë’¶º6Ú ¼¶sf½[Èr‹ÛƒÉòí.fלּCLgøaÝIá··OŸöÑT(!ðÄgó%¯¾ÒuÕYïï¥PÄ‘âë³GLùàµeÍÕöß•£‚°×„WrXb»|Ò¥KÎhìrì&Jÿž˜˜õ{R;&þÊÍË3ÙÅŸqñüÅÓš†²|©𔞜Ï*Åùúúò»!EIZJ‹AÈÊ#l×xf¡ñÕk'¦‡ÑKKè%}·y'VŠ£u wq^¢ñtùѴ«zN¶í7™w×MÄ-ÜÍè°¦0“¥è4ý„O/îAt¢îò±#œ93xÅ1¹¾s†ô펿{û¦m¦¾ü¯u[Çn#Òs E›ë¢æ˜šK–‘è¶òþ4‘…‘»z/Ûˆ dY£ÑÿóÖ}²ãê½tɹ³cY¡ƒ‡•‰Lcß*A€Ú=4@óŸ››û¼\Õ‘$ MÒ®®®Nï¬#HZ­˜€þüc«n½zrµ2BŠ»ç:ðœCÖ_ïqw}•1r:.çV¸ð*‹Wëí•ÐÛëïóñ¿LNºÖ.^kɆ¢k¶ì–Y[Tkoœc³‘gãh,Ð@»§©©)$$TTTÔÂÛ·o:tèZïÍ­hVÏßÝŠÅr:Òx4ÐòÐ8Û–Õa±Xƒ˜˜˜ú‹ÉËË{yy͘1oJ@ÓgeîìHÃ4h …¥¤¤4-¡6JvvöË—/ããã_½z÷æÍ›z S(ww÷+V ׺srÃÔHœè0Ï£¹ík>žF£—srrÚ±c‡ºº:ó÷FÇL&³öB¬­­ýüüºwïÞ†  ãƒ „Š.hV~ýú5ú;%%ECC£â³½½=‚%$$ê™ÝÀÀ‡Ã±X¬Š—zzzW ¶IÛè\ @|ÀápÒÒÒâããëê’¡¯¯O$¾@ …¢­­˜˜ØÌ+ÿÿµ€¶PO—Œ©S§Öî’Ñ}úô=zts®4h UTvɨ¸QFJJŠ–––A»d4ÁáÇÿˆA@£A€Z‡ÃùðáCÅåjtɰ²²Z¼xqc»d4¤gÚh )êé’áââbhhØü.ÁhÊ.¨W¯^½ÿ^KKËÈÈÍÊ­Ô% ˜ @|ø€Á`*ºdtéÒ…ßÍ´K Ø9ßÜŠþ{çgQÓuu¥pünè€ø¿™9;;·u•€4 VÜÜ`œ±T~ÌÍ—ün è¸`3t lÌ‚{÷òæ ³Ó„ ÕÀfÁÂ.ʉ Jx#cý¿àÏ…r{qðc#%R=뎕Ü{!g…ÏÏwÇŸàõ$TúÈ~¾‡ ¥™—–=øŸº…çyuq<§ômœÿ‘bA0œüýÖ§Ë϶aœ©,ç–û•sn%”“V=$:K&«_ÇÝÌÚ+ƒÁï&Z èNË &¬¤²Ý\aw˜  §AC¥eå1•¨úû Ô\„6j¸Ë-(ÑWGúåÅÝO?]$ M«Œª?IúòCÜ…ôüžêbÜÅ"¥‰ïžã4æêþÕž&¯"^ÊPVYý{"$6¾¯Ëǰ=7Þ\}®3ӜԺ-¦ö'"ìRe©,böÄŸˆä81î' YGoâT:\̼‡G>Ó´ìí$¸GGÉâ}å.¯ÿrëQ±ñH!l“ÞË—/ïÑ£‡…BiÍzš«lf ( @ º¶;-[Ž‘^ëìùÉgüò“…5{Ø+EÌþuøíó;ß³UÕ†-2³Ñ'2kÏXqÚ IBTRW´ò‡¦ ›²9Y,4]ˆT¾S8‚0ÒŠKJ¹£›XE_ÏBîø7±ßÕ¬±§,9äÍa˜üŸDÔÙž!ª”!Üøòáy sbÃëÂ’»Säd—‰`‘”SO£Õz[v"`I*FÜÆÉýõæ+‚èHKþY ¢”¨0ò=;©ˆ9R¨U}qíÚµ­[·Š‰‰5ÊÉÉÉÊÊ ‹mÕÄÞDk3¾‚-èÚî´,ªŒ×ÙsD^}JWÆ¥M§ Äú÷œ0NŒ»,IÅá‹pYq¥fk,l”±¼gäžvoÕ÷¦Ý`eýxp&ñÅ›"&O@èß³D®|Ž .‰CÞ—eåsñò7‹^š]€ b¢Íù¯Iï;^êÆîÌ»—súÏ‘Äe}¹“,1pÉßôu¶§…àD……$ïG«ñuaÄUƸÊí:”¾,=BBªÇp›q]”„1¬‚¢"tò»çÛ_ÿ^ƒN‹´þß0vyêËËË;QNIIiÒ¤IS¦L100hõº£³mfÀG ][ž–eýªãì9‚“ÜUñtì×k~Lè^±ð’Ä”d)­á Øzgè;õ+lá»H×y{,õ$¹;AS®Þÿ=¨1¦‹è“÷Î}éµDUGÿù:‘N0˜ÔE²Yÿ51Vú†‡î¾ŠJLu2—¼“”ÕÃDöçó¨¯=- ƒÅ4¥. ^i‚ù·ØëžÞL{~úáó[¿ì­^1U¿÷r5¡Vhm= ª¾LOOßZÎÐÐÐÙÙyâĉ mÛ"^:ëfš,!!!22’ß­h7FŒ¡¯¯ÏïVZеåiY,•Êóì9wÙòªý»Åž{“óÁpLW¡§Eþ”a"ŽûÇŒ€þñË DÊ©«¶$óþT“^ó\²w…?ß=í%à$¤†®·°îKmfŒ˜Â !”WoÝï¢t•iâ)MnX{Z+¯°ADÕ(œï›T†¬¬háªh1Íôí¾ÈýÉ×_Ï×¢¢S ‹Š™ˆPcþn•––~ûö "¹Ý h4Ó»w¯^½ZºtéòåË 0eÊ”1cÆ 7±MÓ 73ÐLW®\9¡ÕÃ’ß i>½‰ÉÍÍõõõåwC€-èÚò´l]gϹӰTÃÑ Áo¾Å^ÍÙU†P˜ñ0‰Öo!ûÏ“ÉDº:é–&%…c‡ïv¨XGÎ(9œÆÖŠWuБxùÎÿág Cµ*}xêmOK`~Éd d= 1|ãëbO½ô\z¤­(·Å8²ÖZD"½Œ“Ô–E>üøæ—®•|#2YBB„ Т¢":Ž _„Ç‹ˆˆ /) ™Ì ~¢¢¢8‹ÅЉ‰¡/I$•Ê íõ,ŸÅbÝ*7wîÜQ£FMž»Ñuq˜Å‰ç^)÷èÓC‡a3Ò~ËÅH Ð%aˆäþÎ ÷ý¾]\ñ?ÌB=u ŽIÏÏ(ƪÊ*ŠÖ·«Ö³gÏÔÔÔÚãÑ€WÑ7£¤¤¤´´ÈÏÏGÓ0›ÍÎËã~S–••#åê²æèr.\¸ðùóçÌÌ̆”oYl3>ƒ-ÐøqZ–ÇÙs­þ$ôK#"ciIŠ»öåÁ«|£«ù]ÆÉV¿ÉF3vrX©.Ž ¿=”vnqEYÁlb7+Ýçß¼=¼ ™º¾»ÁbóÞ+=yû5»ê<·’¾´Zí©"‚ÅJZwïwãÁƒ³7Ö]ë6®¯Ë$®ï)–j0QM$ ±é]­;zÝíáØ %¤Æ|M)A/Éw–èÛé÷Õ&6¬6züæ+6ƒQÌ$«é?uª·{O}ën$ö,.ì|:ó§ÓO%*ž®%K@73 ]åaó‚ƒÉD‹Á—æÓËJ‰»QK ±ZŠ{vöCÈòÔ`!Hˆk÷ï:ÜE¶aoGMx<^\\¨ø]©S§Ö_@HHhÈ!#GŽ´µµ•’’BǬ÷YÛ´V5YçØÌ@P@€lm{Z¶®³ç¦ÔFjЮ½}}âå¯R¹qzÄÏØ¹að öýר÷ÿ;fˆ³ÅŸÁÒ/Å9Ù!æç³r3"ݯ߸Ÿ˜¼PÅ„†`%”'ž4¡I5SL,|CמÖã<›PÉd•ídƒZOê­ ±ì1òG9°4E‡ÍŠ<+A—fc:߯´ íkŽ’’žãåååÑÐlgggmm]Ñ „Ÿ:Åf‚´@kãÓ²už=ÿS€ ¡i®ôöjÊç±#T(˜Ì·¬ØwÛùïØ²Ì%ó^.AƤVŽÇѤŒ{R¯Ê@ÆÖ,_¾œc‡Ùiv— ÐÜlooojjÚ"#¶ –ÚÌÔ´@kãÓ²õœ=ÿ /ÖË^âê>†ÙP¡Q3vnUSoµááWUÑ(³r¼û ò‚Êñ¬’-!Å9ƒ•'¸w ¬1¯"ùö'$ÕF1¸a£RK=.™¿u^ü7zÊ«€;¸µ¦ŠþÐx<¾ÿþvåºtéÂïFñÖ›Ù?>÷ÛÌ =‚-ØÚö´l}gÏÿ4H¬›œ¬ÙT¹Ú±ìÌØyÕ8fŒÿ=L5²Û¬Ë7B“>ô4¨:ž€ˆXü§«@¨Y~ïuÇy¿Ê˜u×…!QZ°'NYwÞ1õVWÔr5 ‰tæÌ™aÆý³«4µßÍ Ú#Ðí€ –ee<ø.2| 4l8 VûS¨:fè6ÒáøS3Þ•áñ«%ÉLQ¶»šóÄnÝ»0µÊ£chòmwg0 žø¯êXéçî^ÈE‡žûFå›h_¬-ÛÔGC &2™M°.œhK š€œ–Å“Êb6„Ç`©ÚŽæ®£Eø„€ t;ÖV§eqŠÓì÷Nkõj #!¡+yã±s?†x\ŠŠÍü…Õó¾çh$ÄïÀh‡Ãiƒx/Y²DXX¸µk0ô¬»;nG¹w+Aaåç•c”±»àî” ÑZ¾q®ÖrXƇç'}oI•ôòº°NÇþ»àÞŒ½'åæ=¿2"(¯ž^‡ðhȼyóÂÃÃ555544ÐßêêêÃ’’’-X˶mÛZpiíQÒjÅhúë[ Æp8l›Íd³ÊŸˆKP7™îU|ïHja `ÿ|s6¼H9DJ ØÍ€ÃÏ@pA€Љ¬^½úýû÷>|@_ºt)55@'UÍÓñZAAAО7Ù®àDoå5…ç›Êøò1Ó%c¹Òí"Ûºm Y @è\ËYZZV™ÆèÔr÷ïß?qâDJJJnnnTRUU%:Ö ÏÛÏ{§„]RZ†Ô®, DBB¢W¹ª#‹‹‹+U¿~ý:<<øþý;š¿k«F_R©T~5^01>Ø¿ýNô¹eižÔþ>@ ÿ~¤{··OÞ± ¢]GY»íÜSº,aWð#/òÆ£¾ãi8Ú§Ý[º2’Î, xVFâägqT†ºmì­!üçé»õN 5A€xC3±a¹ª# ÆÇ+ŽU£yúþýû)))iii’’’µS5šËÛ¦©hKÐÛ¦®"¨Mµ0ëMô²šð*¦+C¦ËSééQQ«G‡ÍIæÅ ÓwsöëÉÑ?ÖøèÊC¸Ï%à~Ük¹ã¼¼ãé8«. ãK¬W÷Ã.ÉìàËærxîÔýuO •ÁF4@Ð*Wu$‡ÃIOO¯LÕaaa‡®±X¬F©ZQQ±Å[Õ³gÏ~ýú­Y³¦ÆAtADQ’–Áb²òÛ5Ï}¢/¼´^iVã¶œ¼—Ï%Q­ú ù}MP6žå.wÃ32ôm¯y†¸z§B¸­ ¶1 ¹0Œr9++«ªã333+Sõ½{÷Ž9‚¦êüüüÚǪUUUñø&~)ÿüù3//ïJ¹aÆ­]»ÖÌ̬Öªõá,U(>/ŠÙ543=ú ×U©—1•§ /cãŠÙ†”z§ŠbZh µH•ëÝ»wÕ‘………•©:>>>$$þñ㇒’R›ë¡Èdò?k©¸‹H…kåŒÆè¾}û¶ü*µ, ‰ŒF`F!“Ss »$³ AhR• Z¸8›ÎAHõN µA€Ú”°°°Q¹ª#étzZZZÅ‹èïèèhôwE×êǪÑß4­ê¼hÉUÜ(7pà@//¯þýû·ú*5«  Áˆ*’k3ÆR¥Ñ]fIÙßQœî+ª4û©´6Ðÿ‰Ä®åªŽd³Ù]«+RupppÅ¡kP5Oß»wç2£ËYYY­]»vÀ€m²ÃHþJGûu§Ôºw^ÉZ™´7õí':¢Qqž™ñô[)"cfLÅ ¸z§ÐÚ @ ‹Åª”«þüYq¬ Ó·nݺ~ýz= ¹[®_¿~hŒ4hP+7¹8¥oî>-á4q„:®ÖTŒ˜­cWßóÞ1ŸÌ­TÉ3=þðÎ êÈé:ÜìRÿTZlf@;#S®òJAssó_¿~Õ?˃lllÐ’hŒ2dH+4Š‘uwû­«AY‚‹XZ6±‡lÜÿ®žû‰N s¿P4uˆ³½N\}ÌBã«×NL£—–Ð J(únóN¬6Dz]½ºÛçU>ÂxæqxíUÛUÖ½dÕæÝuq w3z ,†)Ìd):M?áÓK¦"l Õ;€Öhߪ^DX¿Ç»¸¸Ìš5‹H$þ»tã$­VL@þޱU·^=¹Z!E‡ÝsxÌŒ•nëþT‹“Óq9·Â¥Ž ëŸ @k‚ ´cùùùõ~ÆápÚÚÚÆÆÆ†††ÆåäääÐñ›7on¥öÌêù»ò¡XŽ Œ 5@€Ú±·à®ÈÊ¿ ÚòãhŠ­Ì¯‚0 @+ @Ëøð6ƒAúw9G"ÊJünE HO,RíÕâ'©ŠÍb§%fÞ<ŸÀ¯´ _VÊäw+š«¤ñéM®¾¿ÛÑ 233GŒQ™˜555±XþÜÈ­FrE‡ynƒñ-±6Ô4-ÀÖÖ677—ß­h"""˜~·¢È[N™2…_µÓ¤¨B¢äâ:¿Ђ8lNIQ»ß§ú’”+NUšwv ¿[Ѥ¾¼gÙné ª t:žŸÚnòÝÚ{ÁRA  €NnÉ @s@€  @Z ›ÅÆà°˜º pèy¿²ŠY^b¢ÒRB¸¶hÐb @š‰Sðæ¼ÏJßc‘¯3ÙèK¡®³¯=?ÐOˆWѲ÷'Ï>ü(!ñsnyŠîê“øÊS‡Ø¦Íš 4 YŠ^øX›­}&5vÛÅÆÈ«ƒsÝCÞç2ë(LÖ[töÁ"„þn£¡îê¤6m'ÐR @šž´oúÚg üÀûÜFÉ`Kn‡ò»U@k‚ hº²wâÑ•LuD±ÜxÕiá1C©"|nЊ @ ˜©þ} öØÄ¼ÙhHâw[êÄΉ‹NãPÅ©¿/Äå´ºð³M@kƒ € âäÜ^·áY‰˜ ¿R?fÎ犧0`pØÆÌ‡idy@@€@Ñßð8 bünÈ?p¥Œ¦Ì‡! -Ý ™Læw­4MQtÏYÙêÔïgà’F\ýaöåê©£§‚.ßúßû6"ÒÅrÒÊ][g‹Ö<ÒÊÈxt|˶#¡ÑÏ?å³Ñy¥»ö²?wùÒ †åEK“Nzz ¹ò¿OÜÂi›ŒÈ›ÊgSr‹MÞaBù³VN|è€Ó7Æ}È.ãŽÁ‹È(©ªku³š»Í{”RåÿlNqjÔÁç®>ŒKËá†]ùPPþúÍ2-â²ß ëœu¬DeŠÞ_ ؾÿläÃø/ÜF°T)ue|rs?®²Ï7özû¿üðMFiù{ ×EK×Àræ¶Í»T¿7Þ?Þv$÷ÚáA…)öQáC¨ù‰Wì;t*ôÆ;uÿSÝvM9”TVe‰¢šv›¯ÏÕäÖÃúì2d~PÂ/zÅ4áns¯=Ùo!ÔUùxßIŽ×·;ç΃ @€¦²<ñåóÜÀ…ÃçEä"e<*¿|þ,!/#BÁääq Òîœeñ™”xÉYùïÿ2NÞ“M¶W?,F†¸ïòµT'gÇ…ø­;ãýà쉨àÿ«ˆÇ‰j ˜â®)²xÞ‰¯"9jÇ~gnÄŠhkþé Íʺ»ÞÁÎûaB1ttßácª&†)ø–ôìvHàù› _ô–®ý ËÞŸœfírî3BЛ°bÏæ^ª"ŒŒ„['¶íXz%À±Û'¦i‘›´:xqÃaÓWØ8}<=Ï-$AœöùV¨˜$Û»òÂÊ "&S<—1RFJò³~¦Çù¾|OoÞgP’°}¨éÒû¥¢æs7žª'ÁÉùø"êø®ÀÐOjnÞUtÞv„6ìÂúîëó‡þ”¤¿ ÝpÀ{CxêïG¾¨ õ xW´îœ­Æ¤¨ôÓè{(íîL•¿,NaÜ©×ã³.V³ÐH{0GßRU7ïMüâèèÈï&Zhš+¤ÜÝ\—† ‰‘°ô¾=HFàÑå”~žÓÂɯHѵǓ×êýŽrìÌ« l¹YJÒ1äùéÑråWݰaÈêjwþK ³ëоW&ÊË›Û:0?|Ü€ h€Õµ¶w¨~aÙ»Ývƒ½3ñQ/Î;©UæÄ ® eµL/þyÍ)øŸçPnxÅ÷Þò<ÚÀZ1zØ(§é {ÞÿñÂô!rZq;-D1_‚|[´1 /V"Ü-n8ØÞA³z· NþÃåƒËÓ³ôÄ —ãÿþ¹¡¿+Üÿ¨Y÷f}93wùýRDqQÔuÿ>Â#í'šÓµ¦V+ذ·;– &'YqÈðùÎCz3ÝÏ/êc¬-3«ûÔlîHœüHOgÙ¨ƒûÑþ.n5Ö—qcïµB¡‘ËÇþ‰Ö-V5Á€æUÓQ­ˆ›÷6êc7o±<=å I¹“X¸V¯¢;ãý±§Ñ¬‰è-[?R®òñÕXiëÿ&Èž?QµùüÇqnõþ§dÿu[…¦go¹m÷$µêÝ(]í,e¿`~:í¾#±}þŸô\Q£ä õ»F:„¦ù».z¼@½F• Zb¤ûÏŸ{‹Òà]ÛG+¶ôßšÂW¡OYhó´è ÿ‹¡õ÷ô™óBöo´mÒÛn¸êô±ÿºTŒRܰp÷j<†ûv÷Z¼Hï ç[‰ÛuàÕœm»Ô Ü7ü⎛ers­ö…e£)3—L¼‘X> h¤\ûJ&Œ–©2•Š I7“Š©ÿënµWçŸ ^_W> nÙM´Ž¡R»ÒC®¿B~ŽèS¶yçú9C»ŠòxçZämÿ '?rÕDÉÛ'³Ü‹›/ù rVü=[Ñóý‡ÞãúuéZyV …« 8 @Ð&Ø…ß¾—%®Ò£®â]ˆIgÕ›PYyiŸ+"®*Qï^VÞ§¯¥åC¢ò¢+_•š¯–ŠùHAoZ Š "ä˜afîAäŒ3Œxÿ¿5 göœ½Ïv¹×7ÛsöA½0ê Ëð׬û²_µë’©ùÖ~©°¡øZùA\jÕ5ho”´s´«¦¤ÒÞ¹ü¢© AªºÖÆ›A_xÉàí\\¬3Ì(|:~ï¡ÎS5Óæoþ]:ÙËÇ6-š$½Zõ‰[¹ìýÓ½Eº½2ÇÀašÐÃvéEú¼ÄïÎëæ" ç×'¬½ÜrÔ„gºÙ×V¨p`ìê…Bм¦-÷öÁ!µß*X¥S£ò9Š.J±·æÕ•Žnå%uE%Õdƒ¶°ü (Žœ¬±È°ÂÎÁTgIQ±Á Ç/£lÜiÈìMƒ?É9¼yåòÔeKV¤å qæ› aý›ûîï¥kaX¤Û«PµŠú0j|ï—ÄÙeq[>ZñÀŸ+foÕuˆýØ­÷jZ£jÀ½1¨J×-¤xu]ˆ‹äé„yqJÕ¸µ·‹øE:ȹôóZѺæƒH%½L%/Ÿ½¤-«è’‚LÓcS\¼|ª»tø®Ù«½Õ¦­‹§òuBmÅ*›wî3ºsŸ·§Î\=æ…—Ÿ×7ÏL>1pr€ƒeºý¶ ÕÏh†ù¬ˆÏWÖÆ®>²ä³ƒöaɃÚT:I«T ¸'0¦õÃÉ÷I_±6]ʾ»öž/ ñ5ëßžK@x{±&MˆœíÛNk»µ«ùâb×€ð@±v¿Ù‡N‰À*WN¤.Û ¨ºF‡E(ÕAîb®'·üzyl›&–¾Pñè‘¢¶Z:UXáä¿xÈʰä|‘}8»XHÚ2Ý~çŽoŽI†Ýñó¾ê–ü‡ú•-ª|±NÕ€{C:P?ì½_Œj;!ý¤‡ç,88tVpm¹UQ¾ö¯^[Ré3•gïáO©Òvêű„©‡¦ôu¯éò ©ä°îÊý?®íZqðJxF·¾iÈÛ¾lwé%ª'‡÷¾mvÚ2\}µ§zqj(ú~Ϊ¬ˆá>l®þ<®÷ü¡‡6¼Z© ›·i*D¾pmêf:-YÝ^wv>ý?ìûA¿u×Äoñ£~ÞãÇ=ý`Õ¯Vª`{h ž8Œ˜1{À·WÅ©¸ˆÁ>ë¿ÙE])»ôF¥ª,…©Ü<ÔR,4ˆ¬­Û³´Aþ7çU^çNšÛåãtqqyTX£%©3_ïTù(F]±ÁÞQ%ì¼Îß-îÄ…/Ç%ŽÙ1©cÅõ¹†¼ï'ý¶tÿq³z[k µB'ë:ùˆ(ÙóÚ4¿uSžm~c¼1ÝuúÜïþóGdLۛݣ;³uà éÏÆÏD>RþÝ.‡²iOM´çºÄ³¥¿<6vd‡Û× ´VÕ›#@f3êt¦ûãôZ]åå Ú›ïTL!«<_]²öÀ ½âêrÖ¼ÓuÍ?Cƒý=Ü”Ú+Ïggþžåöήýu(½÷O¡~bP¨Ýö%âÀûÛ¯{Äíúù -ÿ±9¥O…KДM«ó{½’tTŸ¾øÎ‹GywzÌßSíl§0è®æe?^2pËáÒgä)ÜB¦oLÎ~nÈ¿ù0¸[Æä÷¢BZ»érŽþ°dæ§?æá3pÙ†i»š}:¥Où¾Y¾RÛÕÌã^lÕ¶É­7$:¶¿êó´îÃÖåi÷NóH |êqÿfÅù§ÿ{ðÈ9…iYä‚cûÒsZwnîdNŒ4÷½Û¾Ó–˜á/?ØÂEwþèKc?Ûkî/'Î «˜®{· ƒN[~žÅÅú; ®]cÞë˜8ñWá®à[í5ÞÖª`c Ó€9 Ly¾×¿~Î5…žô‰íœ>i?déÖ%}ÕYKô³êäS¹C1»ÆõžýãšÑ~¥Ó–ÏÍÚsêo_Ä}šüÝŽ'3vmÊÂÙ# øéð˜IoFGt(_9CØy¿‘òõ±!ïÎÿétñÉ={ A="ÞÒØ” í[õ›àÌë)Ÿ%}õͶ´Œ¬Ã»³ …kó¶víÑ?vîð×*ž0íØfpJz·I IË7Ľ•r¥4üÚ«};÷­9vÄ‹m\fŸŽç¾·ƒ{.8vc931¢]¢.-»YûÃŒ®å³ÝŽ~o­:Þ)96vÑ×?¦Êع1Cù`›'zZ´txÓY>Ÿú—ÈM}#(uìàŸ2“{Üò@îºpí¢™;±ÕæûÒ>Ÿ¸.¡,Ž;6õ~íƒØñu­ô´Äºtû¥­#C£ýËô‰tM€Óä‡}çëõSƒœ«©½ìï¡íàIáSüÒ_Ó«Ú5ê¬X5À¶Ѐ9œƒ¦íΙVÝ;­£Wžˆ®å“ž=FÍ‘^w¨ÀÞ+2a[dBo{tž!½îØP…«ßK¾4¡öRfœNhRFaÒ«ª&ÁѳVFϺí”cÊ?^3;Ïž1±=cêZüNÝÞ8lá‘‚…òš ôè¿©°¿MªØ¸ ŠS¸UžqÍ~óöРsI)°"ÿ±}÷Û4hÀ,U’Ÿ´]í¬*û븟 h8ЀYn|5E@ö›·€{ È@€d @2 Ѐ h@4  È@€d¸ßtvvv³fÍlÝ °¤ÂÂBé§³³³­–TPP`ë&˜é~ Ѓ!//ÏÖ­Ë»víš­›(Õ°tqq±­›ËÐjµ¶nB4ì››«(c,cëæ@¶Š8—ŸŸoë¶ÔIÃÐ^^^¦è¬T* Ð ‘”ž ƒ´áááaë¶ÔIÃÐ*•Ê®ŒV«uppb´­[–gú?Mi”³uCÀò¤è\RRâää¤×ëííímÝœ:iØ:22Òô}åܹsîîîRž¶u‹ÀòòòòŒF#K ¸/I¹9''ÇÓÓSÚîÛ·¯­›S' ;@ûùùi4[·ÿGv€ê È@€d @2 Ѐ h@4  È@€d @2 Ѐ h@4  È@€d @2 þ—†4ê^Q‰IEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/netconf-concepts-bottom-up.png0000664000175000017500000020512414770023131026317 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+ IDATxœìÝXïðwÝ * X¨€Ý­ØÝØ ŠŠ‰¢Øý³þ6v¢b€…Š "Ø…H§HÃöÛ€Q›2ý~ž=»q»½·÷n÷Ý»÷Þ“b±X*FêO@’ @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @Ähö§‹"Àb±þt*Dâ4Àï„ @4ÐümZRúž€„Žñ·h±B€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ $HÞ£½ëŽºžÆ¾+ÝvÏ_ÇZ8ÄaÆ_aÞûdRþ}k°Ç.²ºH@ >w@òä¦| yùêݧ¯±É¿Òsè j5jhé›4lÞ¬®¶½ÒËeeƾ{ñœ½àðèÄÔôš¬¢ª¦Am3ËÆÍ«bW@\5P 3îêÜYœôLˆÙüÍ£žA"Ñ5»­YÕÊ{ú㦗3‹f£>ÃÇO›5±‹‰­"‹¬¦ÕWcÜýoì©>­÷¾î´€O;Z(üÙ•-çãþÙÛ¾sï׳¢[UóFVräûgìÛ­“Û\Ç×îµdûöÅ=ký®_Ò+ñžKH5A)5Ÿïf¿kòƒæJüGNšŒ²–ûfÞȶ÷D·]©aç=/\sñcn©`¥>]Ñc z–µžvà¨Ç0ËbÉŽ&£fÔ°ãHömÞÊGÛ§s>ÎûOÖã¥]G[›b­NÕW b«¡Ò_lÙðœw¿æØ™­T*üLý.N+ÊÝAòRÂέš4ÎóQïÔKsW=é³Ë¿“Àï k9n¦õj§—ù÷¿îZs{w/Iÿ•ò_-ôíjG>üʹ›ysÁšÇ}wØ—>±ìïÂúõd¹Ã¼Y…ÈÙ®¸uÙÕ^CHö¤+[Xq¡ß¬‡Gö[àSæ’Sü \õª¨×³ZÏOO±pz ”–­Óé];uZ”Ç}(åò¤»ìîÎ2«@ný;ªO|5 +é®ç¡¼ ³ñcë‹êw†ªå õ7ëÈ7o¶²à¤Ãè3‚<íDw¶-€RµLoå<ñqþ!)õ¼ÇùÝ'¢‡$@€–4sG¯ûïçž±¹kÎÎLÿꓱ²B7[ÿ¡hºÞ|ßkKí*Üg‚QÃÎåJXóí‡e¤K>%#Ècâ¾þ»õ—Þ<)0=óÐk´[uÝû[‡ñÜr-˜vÒÁg´ÐϺ¿£úÄW#B1con¹”Ê›05¨"_\+ަØdæò΃oñú£Å¿ŽÏ³WDŠß¡×m’-íñÃüÍ|°íô—Ñs$îøðOB€– tµÖnkÛ—óùÎz¶bÉ‘Ç{iþµ'º0c//òxS4]Å)÷Šg5†V»ÙóJ-9úÂb¯EÓVîÇÛT´K.C·ïÖCÃoô:Î뇙éëºñù@¯æÂΛþ ªO|5R+3&ìéã硟"b“Ós¥Ô´ M¬šµjn¡#W•ìöÈïqð‡ iyÒÊÚµêYÛ¶inªV‰Ï@VvÂÇ—O_„~ü•˜šÁ”QRQÓªinÕÐÚÚ\[¶¢Ed¦G…={úòMþZ¦åÐåT´ŒL­š¶la¥'_éµÌûùåÙý‡/߆Ǧdù†f [µµ³Ð’ïWf{Mž¿ù›˜šÉ’’SPPRÓ1¬UÛÔ¢a}cu‘$f¬ïî»g[èc"êK4UK»ZäVÁ7Ä_q¿òجЧ‰i[+ñ”™™þ#èþýÀÐ/QÉ4EÍšõšµéТŽr9oa^ê—@ÿGÏß|MÉ¢+i×¶jÞ¶}³ZŠ’ô‘ȧÊûC·ã¨fäá3ÎDðg>Ï\X ºúC€–RFC7Î]Ýdí'ÎTò©9›vZÕXÔg£U¹ágÖ_)’YsÌæÙ D³ª¹ß¼7údNjŒØ4“ÒOÁtÍ®+—ÛŸõŒ7qÐëþ²ã]Õ„}RJzõ‰¯Fø1SÃÎor_»ãÔó¸²þ­m3lÆ"7ç~õ|áÉý²¥i§W܉º«Þ¼r­'ÃJnõ¼E/È*9»r£an›6Îî W±£UNlÀ±M¶8÷²Ì¢b֮߈ S&liPnÉŽ~xxýºÍÿ] ûYÖ¿5›éº|N¿ºå®eYë˜qÓËeÁÚA¥–)kÖkîÏÅê*–µ¼Ì—óë6ñ /þ`ôΖŠ;˘Ùxqð›Õ ‹F¾ÈKzq|ûú=_§”STBälºõ6iδîUŽïèaAz­N½ÌÄ0ÒÆ÷É) ù&T…mµï9«*Õ$Š2(kÛËK <à:Çm×ýè’së¶uÚ¸kå0 þfEÜÚºhᚣ/J oØeѶÝn}EôÕ#él[þÜûFsŸ¿÷l"ð+óÅ<ó¦y?M¶9“è7@½Ä¢ÜïJbèµë_<ã^T!ìø•osë¢ ºúC€–òæl´³¯7÷XõÁsþ©)>cþÊ®R¹?®xV4i<ÉÙžj[gyKޏzðyÑdÍñsÚ;%HÕ:¿ëüÁ7xY,åê§©]»?ŸI¢«O|5R(/ÞíðKoÅ—?Kì³n'¶w]{]EÇŒÉú|bJ—á‡>•ýßÔàó;^»åånvCÁ:fÊóÝS‡Í<ñ)h®ŸîY~ïäÓqW»—±M°Rƒ÷M<íØû¼òÿüä2‡“;{n¸ptNË ¦–w{ae‡“õáÊšAWNŽ:rcï(SÑ}éa&=Z7°§ër¯ Ã“ñýÙù­Ï"› ë:F·òí‹?_œzœÃ»/Û¬g=ÑSÀJ}ÿ¤( *˜Zi•Ÿ_ĵ­Š“˜ÊÌúõjÛ°Ž³®”½Øh¿Í#Ü ö½³¶gy¹Ñ×ôì¿éE©/²7×ö¯ÿÈãÁ•ùÖ1LH÷;i£öítÈ[n7¿o¿'SI9:üà%]³ûj·¦Þs¹ 0ËwѪ‡ýwµQ‘„JX‰N¿,š¬9pP]µ71yM: ± ž(èšíÆÛIݸÃkKõ;–Þ¥¥ðÑï%¸úÄW#ÿot« úóã#j–Ì…ŒŠ¦…kΧ=:ºÞ-¶Ê õšf5uÔ¹i?S"?G$ øš@MÆ»kcd³Nõ)Á.TÖÛƒ«¯®Ž\›áMÊ;ÍWÛj%ÞóJU“(Ë\–ì/‡‡›u¥è÷9Õò¹IIü»W^ðúÞ£¼¹0Rÿçí9mzl+:‚½3ªËd%$óÄŸî· §³õë=«Ã—ªºß•$ÏÞ®¥vÇp—|!(u‚¡šX ¢ƒ-Q¤M'yMñl³;Š3µÇyßÜ€¹fÛ/=î„}+·ì"²v³ÌüKVjÙݼ2-Y4õ¦½,ÈÞdL@`lnËÚØ•$¶úÄW#Ùv rà?ºk¶ŸïµfÖ€†ÜÞÀ¬ôï½½:{ÝOâÍðn[ÿ¡–¯}¦Ôüæ…=úK~z–6wp]>gTÏ–uTòC+;6äÚž¥³Ü.œLõßtϯה٩†õëéò®ÅÒ³R“ÑK\gîn­'_x˜Ïûõ#ôÑÍó§9rûGKÉÙØ§'_zÖn;mÉ‚iC;Xjô`¦…?>½eÙÂM·c¹Ó¯ÖõžØ$ìÔ =AAæãÞ Ëâ󺌺#<·¯˜ÐÁ„w9jfú·GVÎrú/˜—Žâόܢ©ß¼zü_~ä¬×}f­#Ô.qÇJðqYP”žkt˜¿iÕÌ-ŒŠ…žÜŸáÁ÷}.Ÿ;uøˆ_Õo^làƒÈ‚ Õ†Ö"¾˜eÖ»[¦ [X8.¹ÓÊžÚe8Ql«•yÏ+SM¢,s™¾7ù[~zV±™´jÅÌ!ëkçwûeeE¿<ï5gêz?ÞÏ¿®:/¹Ó´Û®Üô¬×iî*×ÉöfœóØ›ý£c«fÌØÌû‰!jÿŒ-³^­hð»®Y9UÜïJ£)Õµ«Mns«)3èΧŒ^M1t5‡-YhʶKÖu>4æ÷àõbåâk£Oõ­êålÙIãœÛôˆ ~ç—«;ÞÝ™ò5D*Ž™ò1„¯‹žq«:¢úa¦|xÍ÷ScíV&•»jª”Nãú*$¤ ×[xà÷,R‘ýG«¡Õa¾ÛãÊœ{%¾É—~tò<ÿÂ&0z—Û÷x¿øòÐŒlÇlºÛ£«³}·m¼›qÇyòñ^B:Ád|É]µÇ¿½gX¾ÃMF»a¿¥çl›LnÒë?^Úý´wsÀ¢CíJ·;þ p²:¤pRÎv™ÏÅ¥m5K¾.CÉ a—qìÛR¯wþ/J$ñÌWë‡-(è„ ÝoÇÃS«¯1ºbM»qoôé·²g§O8¿ 'Ÿ¾ä^—ýìmyñùM³ í7\qjÀ?– ]¡V›)ûŸtj9ÈfÒen’É{â:íèà›jVñs?%`ÿõ‚1dÛïyæãh\FÒ’R©Ù´§#ûæ¶õûÇ_êUØÊ3>?ø\8aÔĈr´Š¹·ÏSF§D•1sÒ’£¿†úû>ùÆ×”®?øÐµåÍÊþdã¶*6b,sæ·ö³­f_õõì®[´MÑdu› õðilйáìÜm>þðÐŽÇ“ØY[±½ÇÝóólT‹^ž½ÙÛ;îñ³TiÖz#/ä¿ß³ã¹ËnÛj=Š è÷;YƒÆµä#÷g›ˆ'o~2›ÊW÷fø´¤a Þ0Mã•ï8S)gæn ê²¶ÊW.Ê :³?¨¢3Û´Xà$Æÿ‘/­Iéšhˆj+-±d=Ó•\²´¶¹6!:éK4ûø[±Oû?X}Z¬‘‹+ ÅW#l/½–Ý-<¼«ô=곦]™ßZ]7ÝÜÿÖbâ-îO¾™¾n›ƒ{ i¥‘m»Ówï°:e­6C»ûºíýNõ¿ÀýÅ5áÖÙ7íš—X\î—ÃΛ¿NšÍ½um™àþßtåºíÚ(/Ò{ÞÚЂÿ7[wûø´úå›Q£õÒóG‚ê½È‰¨qGV^po'äê¾ZcÎxÏnPæHŒ²¦Ž^iÐz+·oöÝ•Û^Ø øt*arb^½/øÙ]¶“óà²Ò3?º¢‘yUÂP^ò‡÷…½¤ôÍ5)7@§Üñr½#l&i}‡ s—ÌT_¹œÜ"æmU,Ä\fÅKÏ…äêMÚ⼩éúoÜɤ(v”6˜|éì|›2všªÝb/‡½½Ïq¿—Åø\|Ÿik]ÝO²ñ~'«_W‹Üæõ7‹þ‘=R§º¿ÿ:hÉ#×`öÆ¡Û{äþÜöiÓ¼ÓnŽ7ú{j2/-¡h¸¢¤¥,²UËKKä_²¶Je—,¥¬Ã×RÉJKʨÐWX}â«BÒw(œj²Üs ~ùo¤T­Q[\7Y.á]ï"ü{^¬Øk'(›™ÌÛ2®üÏèší§tW¾àÍ=jG¼JÌk^<ªf½9àõ¤ð§}ƒ—Q»/ÿ ÔÇ7Ü*È0F³wÎ,7=s1ôú­œmrqçÄÇÜîÆ)à<ºí*÷Î5Êý?MÅv±g}ƒ¯qsÑ·ÿí{¹bW«ÊýöÂÅÌÎ(hK'RrrboW͉ãû§f¤!žNOÚõ›µlÙ¢qíò‡Ró¶*b.s]— CÊýð’·2Ðh}ÁÀ„ÈtXçÖV½œÝ‡¦a?¾½ü¹K¼­ôAh ˺úŽ˜Oäû´zM Bx:îC{'C€®ÞªõqÊF¯ÑeÕòæ§g?åô§Ì¾»Øýþ€½íÅ×$ü›±r2sЦ¤d¥D¶b"[2MJ–ÿ ž“‘Í*wÞR$¯úÄW#$óÝÅ¢qm‡ éu)c>Ú©åÇîTìÍKï3í4TÕ7Rði¢J–̈÷ îDÌÛ˜R,@g¾ä]4~G£¹NÍ+qúZî÷kG »€˜Œ×Pxx•5íÝCÕ6N¿ß¼àëoÒGê–{]iéö³ú ü §ët™ÕKùï{BÌÍ+³Z•5ÊYEIkÖÑb÷ãÜO»µëzd‡ÁRYÕåýŒ*$LISL—7‰}yiûæÖÂéðÉuýk—~ļ­Š…˜Ël1n¨¹€ I®¶­9cãwÞ¹¤Òm¦tÔ_ÑÌΘ\â…÷èw±ÙD§:wƒý~ÇPÒ*ú²’Ÿ”É"¢?[D Z"I›Œ÷š¶Þn;·ûfÌþ9{æ=q©[…K ˜»‡…,µõµ *‡&-Ç÷!Ÿ›•K!œþ¦%³r³ø%‘–§v¥ I«>ñÕ3ñÕƒ¢‹BÖ`'´?8C·mKÀ;Ì~»’Ì´.·uV­™¡àw…¡j¤Í^7nm¦Åÿ*>n+9èfѹƒõ†ô0ªLãçÏW× .M”›´+#š•&£ß@Ÿî‰s)ïÞ'åµW*/zXõk!ôìëþÖtoîŒ_°jWþÐL×´í_Ÿ¼æLüpÇž»Ü†ÚÔ'›¬?ìyzÖ±ÄXÓbÞVÅBÌeVoÖÒ@à![CO™Þ©„uÚÖØL ¥Î7ìD©}±Úý~G“QàÛD³R³˜þ]þ h ¥ÔbÑúnÿðáþ<´jᕱgÊ9q\Â0kð5ÑýŠKÙ)CQƒɱ©¹åÏ+P^jL*ߤ¢:Õ“=$«úÄW#Ùѯ GW j j ÿ@’Ökb©DÂx#EE¾ŽÊ&ºå5‘Õ¨#´»,]NE® @çfçÿj^øˆr£•ùŽ’ùòsá×­TïNÔ.L‰ú™[î¡TÕ²¾¦Ðw¦lÖDŸøs#"ëGht6Ñ®J´éøµ#7ô>ÊkÛŒòY7š}S·èеsû¶övvv-¬ôD¸5³øª…&ºN—–WÓ7·aߺŽr^è=£ãàýÜs“¯Nî·Úæ™»5ÿobÞVÅBÌeÖ¨-ä,š¬rÑs5ŒkÜi2JùŸ£Ü´™›Å¿/æFÞ>t:ø§€!Ø¥õ;Òð76ØŠe¿ã߸Y"k¦±A€–T ý®òYƽºrê…yž/º­/çÜqÉ"­iªEHÁY÷¹ÑŸr‰ªH6ÔKŽúÀ^re®äL²cßÇMiëRî¬'QÕ'¾ÉM‰,úq^£v…z·J³çcÇxîDJdŠ€oAémRì˜U²xÉß“ '´L¨Ÿ¼ÆYHâׄÊ<¯HNFNùSZ¹V¶4{6Bxa¹oZU~§köÜuË+¡³óu¾Qm’ÞÜ9ɾmå̠ݰs¯þCFŽܶN•¯ÏL“Udsá¶(ðͨ<ÙÚƒ¶_Y÷Ìr!oœø°uΧoã„BÌÛªXüñý‹ïªÀÜåý#ëÓÿ8NôÜF[{l(ʳ3„Ã~ÇÊÉà»&‹Œb%~jß ZrÉZÍô¹µÛQîÑùËæ¹Ç¦ßžTKòk”®jÚ@‹<)8mèëã/³ë”wUjKV1µÒ$O Žù_J'&¯!XJnlðk¾+·ÖlF}\-‰ª>ñÕûQÔFF^º"ßCŠýЙ“)–8ÅÃÊæ/ž"µŽ:˜Y©e_xM4¤+ô®Ñ¤åùÞ4dPšRc§ËÚX»tÕ¶+ï2Jþ›ûêÆömÅs‡e[·Ìë*¤+@Üsvyãp¤Å§‰é·}Ùzã];» ¾Å 19~;.EŒšV´GVómµL’Xf !†ý./-¾ðjADVC£zŸC Z¢ÑÔ;º»Ûž˜þˆs@Éñw]á7迎T/M]ýÈ›u°¤ï÷ãý^÷3àÆÇÌŽ¢9ù†½d úû¼%ÿ ¸þ>£K3ÊCK±’¯¼)šÔiÑT»2û‘UŸØj„}|)jÆÉ®X®ce§µÓHËU(TM†¿xiTNå[C¶hó°X÷.x¹(‡‘¨X.Ö¸U±c¿p µÆ#=.\•æwíÚÍ»~þ~þO>ÿ,>Oöûs®ÝnÜZÿàʼƂ¯•^>)MÍÂf¼äˆ¤Á³WM­q3r«`¼Á××^§M«¥Zøßê½­–IË,!İßå&}O.œ¨QɼàwB€–hRÆc6ÍòhéÅk2î ó®ù‹,þp¡ªŒ¦ÑjPcâÇ|;sæýJ몌Pˆ^Ãv`Cr¿`Èäˆs§Þ®mF5 ²âý>(ú]S¹ƒe%»^HLõ‰­F¤TõU Û¿&V¤gA{¾Â U}Ñô%)›”š‘zañâ?ÅçT¦ãƒ”šjá áQ!‘YD¤:ñ[RŠ•Ãž­pBUO¤ošt ËN£Ø·y$¿ÏÀ×—n߸zÑûÄåW…i ížK_WûÐÍ­ÊJD )º&Š$ˆÛ:—ýãS¢¨z•z!þÓØHVÄç$¾WªæÛj™$±ÌeQl}(uˆê³øÓªðŬœL*=lİßeÇðõ 4h¨_Ç Žê°s@(Ú¸lèµwÈîÁ%dÍ‚‹ã/ôþÃeª2)Ã㛑¼É/û6?Zð_{ê'_•±d£žc­g;½äM†Øäïz¤ ¥VßÜ/'×ûý&¯Úc|óJôá‘”êWÈèÖ×'„7N@rÈ«¸Ü¶ŠB>“r¢^œáĦ__OŒcÇä_ŒN¾r[Þ?ù‘Ýú`)2új­ã³ß³Û‰rÀ””ÐøÜvJ‚ß5Vꇅg“Ñ êëŠëM“R­mÓsû¶dKÌ}¯Ñ}Üä¥èð½kï¸]ì£Q©MFÞØÖ˜œåŽúA"^|Ï"Æb9r±²Óù[·órøOd«æÛj™$±Ì"C—’.<'1óg¦€39²ã¿ ìd]‚è÷»ì¨à/…›ŸN˪\»~hIG×í뱸þWîÑå×åùë;ŒüÃeª2©Z]zº ¾Ê»|GÜA§ísŸ,¶A§©Úƒçv^0²à²‰Gçnsy¶´A…—ÌL¸é¶âYÑ´Áç6Uéu!)Õ'¦¡k4´7"OxGø×çÆO¯-x ¯¼ÿ Eýgjµn¨&ÆÃ Mµqsâ÷–;õöÔõïóêšPm>¦k4íbF~àN½?~þ³‹E=¦’°‹‰Ók ÂåçËó/ DíÖVe 'Æw¾‹),m'­Ózþñ#Ïêô9Ãm|ÏxvãCfŸ•ºŸ”^‹šä5÷ä…Ä`vl#,VJVÄËï|“*:üùHÛj%ÞsJO©æû—x1”4 á—”ÏþÈôKaæ§»!”ÎUÕ~W(ýSÀ·‚û «¦ಕ@´ä“µ˜æ5vsçCÜs¼¾msľù.R•Ñuz¯™Wïª;/ºW®CÜ;¥šï_b%£SO§ @ç„Þý”ÑWÀù.i/ž§´xíw…2?ß}•Y0aÙ«©è>!@l ÿ4µvËWµ>6ù>ç÷ŸÜ‡K—¥Iþðër æ˜{ÌvcÁuàB×vì­ä{qa«Šµˆä%<Ú4ªŸ‹OÝS#æ”I¡É¢}ŽÇ:í-øi-ĽËpý‡'-b1ýܺ<^tQaF‹µ;‡Vù­–”êO(6›2ÒðÀfÞbÏÝ\. :=¨¼ë•å†w^Z8YsÔä&b¾6²l½qέVM{Ì *?¶]>øÙ{Š—”­7ÞÅ~#¯ã|üá±síë­-ªZf>Z²ÌwÀnå\U˜õóñZ—ë…ãdÔ1©qYßù¤ÕkªÂݺS>}ùÉl§Tå¶Ç¼´„¢ajT~<;šzóÁÍh×r¢]ú³ëï3ºÛˆ¸Ž•´yê¾h­n,Š¿†È·ÕJ¼çTŸRÍ÷/q’1²m¤D>rû£ü8}ðŪfvå|ùÍ Û9{gdÙÿ+—hö»¹Ñn&ø:}; ¾È!T¨¤¿‚T­‘g¯mîù•3•$xv‰@Sné~Îã‘͂ǼӘÓý]m-,ß»m~OY—™úæÂÆÅ W_øŸWê–µdõ¼>´[Wp¨Hº<źõëƒG×µ(§!:7þñÎiÃf{+zH¹ûžãÓ«rùÀ"R}â©ykçíwM¸Ëm]L97ÒaMMת¥ŽJÌx_—nã|Ò ¦å:»;5ûÏœRµG{9{¶ÜÈûÃÆ.=•}.,i£Ynüe¦¾÷!oÛÖ¨hÛª5bëB¯&«x¿GìÛNöôU¯Æ›÷òRB/nó8”2åÄ[Á9&îð€ÁŸ\ž]¿t-d}<0ª÷–ÂíV¦ÝÒÙÊ|Ódk5¯Möó¾>?t-bÔÄšåRï/tºÙ`ÊÌA6Ú‚¶fÒýÍ^¯ ¦VíM*_[t£lÈ眉È>_³mDØ‘<71èä GÇ­|ä5tž×²dÜõ¶Já=¯ôSªùþ%NÊM†·‘;{Û¬µsòºQV–þÉŒ•þîà¸N.Ï*ñ›‹(ö;f£³!58˜á BI€ý—Ph:߳ߞR…ÏZ–ðsnÓ#„^˜´¬éÈås[W|þÊ¡)4œ{éj\Çnž¯ lQ×—÷6]gÖeÔÈÝÛµ²¶41ÒT`°r~%ÄDE†¿{vûêÅ‹¯>öYHS±u¿~*ÒnÈ‘‚§¬Û‡[\ë0mòÈ~m›è(0òÏËNùñ湿÷Á]û|¿ò>+Ý|Ùããêˆì^Åêû]ÄR#R5GîÙp¸Á,î!>;À­eƒg˼VÎèÓˆ7+#"àÌæENýŠÎò‘k¿i—šÿ+@±ùò“Kn6_Å;¼e<\ÞÖøò˜¥®³GwoÌw¼_‘aoœ;yôð‘;‘]®Åòhv[»žÙè×dî}îJ²ÞìTçZ·9‹& ì`É6Vvâ—ÀG¾O?yå{}•‡Œ8€£†B^Bzúgë¦Ï=·/ŸÐÞ„×$ÉLø¿U³œöþ0Loæ¾cT9y‹¡Óv`}òŒÛ?×’m ™C;42ÖR’¡óV’®X³Q##Z^|ÀUVM­×ià!z¶oÕ¨Žÿ Ù¬ìØWW÷­š¿ôLa‚PäÒ]§  ƒ“ZÑžr»¼÷¾¾Ð´â½Ñ£|·¯b–jògæf¦ÆG| ~|çñ§´bÿa4]yxv ]ÄÛ*…÷¼òO©îû—øÐ4;¹ŒÔ¾¶Ÿ7´EèêÖ­Â7x-ÛÁœ{ù¬Üä/ô\¾îÊöf%e`HûQá!Eµßq±þïqÁgªñˆ–’Ûs柂ý· k÷^»¤Ñ…Á•zvfЙýTÚ=ÕŸã,öÍÆÐìäá`8¦_çÄÌ7÷-»¹¯O—kÖÆ¸œÑè¥ïPÒêë°1°ðÔ‘´sfœÛ l©:}6_ûßÌ&*¢\ýªUßo$Ž‘1›vúl˜]¯Ý½C¾_^1ðò ¢l`f¤Fûó=<¾Ä•:Ìg\8åHùt¾J¢)Ù,»q!¶M¿½¼nÌä×óà / 5Ã:új2yé) Q|e,ë‹•œ¥Ó¥;¿zuZö°`¾pŸMSØ·üÁÓjë©É±2S““â£Ò¨6m2ùÀ°[#W<ËÍ}{Ô©ÓQ'}3S}5ZjÔçwÅ¿iô?è=Dzܖ-i“ÑËú­t7àÞ[;ÞÚQ|ãÅÁoV _˜úÖ÷à ö}—®¦_[WS]MQŠýÝ)âãǘâ¦7ê?¯®•£Ã ÷¬Î²oröÖ׾͜Sñê¿s¥Eg–µYpõê¢r~oí¶Jý=¯ÌSªùþ%F*ö+¶:x=ÇÈ/7ìÎ]ÿçLdU5Uèé IéEßëkM:±KaNþ><‚‰l¿ËÇJðÛ¯à8då8ÆíÏ’úï!Sw²×ÄöÇ ŸU’ÐU›Í>÷¶ýq·s¶Ü>?¦í4­«Æ4U/¿EZ¿‡çƒ<¦:.»ZÁÏM…Fã½ö{Nh&`©•%9Õ'†aèôÜpOØ7_¾Ë^§þøö£Ô¼Z]WŸ=¶ ußÙ:&¥×w×Ó‹‰CœÏñõâ!yÉ’#*ººš­›ohC·16Üç0+7)âcRyK¡ÕR¸¦4…F ®^KïÖsà NëYzä‡Weôæ¬5ìÀÍÿF×”‰ºÿ]v‹ïåþ€ú!ÌäÈÏÉew"•µžqüÂÆþºU­/ºv÷ùƒ4nå¼u¯öü8c‰(‡ä·´ÔkÓÜ.‚®›(Òmµïyeª©ºï_bÃÐtàú»¨Ëgò=š•Ïÿ¹E³˜zæÆ–îq‹æPX²èö;öÞí³Ó—W@©6sGšJìÐÿè¿MµµÛÚöG&ÜÍ>¯d¡«4¹Ùð¢§Þ{v:~Ö÷ÝÏòæ”ÖiØyàØ)SÆv¯¯^‘[Ö¨›Û•ÏÓž{ïØºû°·ÿ—R×$æÑhÐsøøé³'u3­ìåÔ„’¨ê}04Û.½ñÅáÜF÷µÛO¿H(k­¦Cg,rsîo¡ü†Ö¢«·p:ûnà½ÿÖ{l=èóîWÙs©Õëè0rÒÔ‰Ë\θßz¿ÎŽ>û6mÙsìÆÛrß5ùZÍ;wë5`ø¨öµ…ž{ÇÐê¼þá‡væ¹xœ M+ù_“Ϋ7,l)| ºF›~î>pòÊ€àw_b~•uÙlÕî'^\¹réÒåë¾~¥.=ȧF£þœÎÙ\K$Çöâ2ÝìèJΘ(o÷ì žë%‚3 ŠZFuê6´±ëÐÝa`/ƒŠ\?Y”Ûjßó*>¥úï_âBWmåv÷m³õsæ®=÷¶ÔÇ»\Ý~.ë7.ì]Gž–Yñ¶Qíw¹_Ïl¾Ãë;¢1lIƒ¿â»Ë?ºº’6s e¹P|’”Ñø;YãÅ·|ÊTûÞ~ ¨Š’Ñi>Â};“üåÕ‹Wo?}ý›ò+#‡¡ ¦Q£†–YãæMëjWäX‚”fÓaË[v0#úÍóç!ï>…G'¥¦çÐeU4õk›Z6¶il¢Ní÷Ì¿£ú„uЕ­.?5pÙ¡èЧ/B?„Ǥdä1äUujšY5iÙÜJW^袤Œg³fW|”:œüÉ:Yѹe ÛMÛÚnÚæ´¯Ÿ>}ùæSDl~å”T4tk›[6hÜÈTSFhiЦÝvvwÚžóîE`ЛOáQ ¿2™ %ÕzÆu---ëÖ®Aq;–«Õc©wEÉŸú?zù.<6%“Èkš5nÕÎÎJ[–²è*õzÎ\ßs¦ Ó³î9‰}[Æ>ø§F¼}ýæý‡Oߢ~¦¦g±¤T5õjšZYÛ41«À›A‰¬åÔÝ<‡ûäç ˆƒžw—žìQ^¿±ï "ØV‹–%ü=ÁSþÀþ¥ÐbGk‡ðù¸T{ûæ‰ìhÁO–½sœí±0áíc¿GAï¿ÇýÊ•R¬ahÞØ¶Eá rM<ÃYžT-‚ý.3dϦç¼û æ/n[•ë Àï… ’GZ͸iöMÔË¥ËëZÙ÷´²õrÿ~¢­š¼ný¶ýê·ÍÒÄ€®hа}ÿ†í«¸y »žv¢)‡”š©mS[.Qè+*Öožuþ-¯ÆÐë·f¶‰Ïºüμ)Þî'Öu™.žkVXµßVË ‰eéõÚ8Ôk#ŽEWa¿cÆß\µë+÷¾ÆpGÑ ì¿4HùFs7 ÞÝ÷t2!¬'«Ö?½«µòŸ.@ee¿ÛçzŽÛ™a»rU§ªi ¿4Hºfuk[ŸŸšÝ¡è½.GæûO¯#ùÃEÀ?‰wuÉ:î„Ä|áöqÆØ’% 4H )ãq»–ï®ïÌ K]o 9ÖKó/:ë þisÎqOÂ5œ¾ÏE²/[óOB€ "k5ÿÎÇ>iùÃøJ©#v€Db1j9^ Í"„&«mf^ê‰Pí!@€D‘Ö0©¯ñ§ P49ó:ºPÐ @P€ I¨^,ªû”€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4Ð @P€ @4ЕD£Ñþtª„Åbýé"H$h  (@€ €h  + g­ü› (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  (@€ €h  à–óa}cóa‚fi²+üé#Æï*‘„H>ßNÝÁ¥A¾‘§;*Wn!YOzx£ÙŽ£*M´å+ÿUDRrQÈ‹òb1øl Qt&l}¨fñR•(§èŠMa›gþz{iû¦=§|…|ÿ™Çþ‡¼ŽEóN&ÎqÖD£’»„HV„•ñõÖž ÛŽ]{ü5)‡”lдEóZ)ÓI^ÄÞF“Ÿsgn¸éc ³‰tá“Sï®Ýþ‰œûš|Ýo§X4kïØS¬©–ƒÏ&µÞoNÔÅ®eC€†*¡ÑÄw؉Äb±þt@B¤mž³dG”µÑ„‰6ªb‹)%_…® [K]ý'COKŽ.®×¬fÂùSØé™(ôض¹Ÿ.ãRÉþx93ÞìÞaê…èbżñ;¶ÊïØ¶Ãž÷ÎÏi¬T‰Ï¬WzкŽ-ds§hÒR¬œ\NÉÜýŽŸw x´²¹<ÿì¯þw5bæ,ジ“|Ô'QÀÒz[¶v½6úFò™).·:ê¦ñ'·¨¾ à&m2ëaܸnèOâdÝûx!†3n?vk Ëy&­\Ù¶¶êƒÅd:½š}ÛÍ=~!Jà¢(v©WQézò« üô{d¿Ý9ïX~1Ìç{ ÔgäRsˆ­œÚæÕÓî;uã¥g½®.+Úšª3cCnöpÛû$5Åw^ï…ÍC·µV¡\;U^/fÌÅ9nœôl<ÙÛgCseÉŽy¼kBO§«I¬uó¼§Ü]ì/\‹˜6½67龜œ¸ÇþËÞ©óÊy†Á õsÝo¬ú˜ð¿¹»upµ©Jyáo… ÿ2ºœš¦ï~šº<7*344µ´8³âÏ÷Uv¸œNH³_O«ÅýÌÌý²Õ¦Îì vtíÛ©®‡ZéJH­y÷NÖ=µdɻò5[sßµ~´•"'c0S‚ޏ/Ý|âvpT!Ê&íF¸¬_3ÑFÓº•ÿh›ûîóþ!ÑìÒTjÙt5õ¢æ eç֯Г«Ý6¿þ‹]\ ó¶ƒç¬[;ÉF³4fÔ![ýqO±Z÷t{Ö‚!ËîÆZmøðrž)CP¨¾ –~ÙmÉ’ÝW_ÅåÈÙY¾søÊ4Á+•ûysçî"^²ßÕiŒÎ—ãÇw/«Ø&YŠÁJ{n½›ç‘kO¾¦²ˆ¼AÓîc¬Y4Ð$fKéWI8Ó·Œ.ÙQ÷ö¸¯Û{ÞïuL&ûȨaf×g⢳»Ö”Í?ÛêeÌÒ#ì¦ÍÅ9s¼ÎÅæÈØŽ^±gÓøúJBV¶¬·6-pëŽ7ù›Ÿ­Ë Ù2ß}!]òb¯LiÖ{ÿwBê:ß~äÙAƒT¸r…móùKÿ¾áÎðü{ª'O Ñçn÷¦¦V¶=º[ul8ûI íÄÈìÖ*²T×½øz)Và½-!'æõ×Üü;ú½Æ÷ÌOÏl2:­¦ï÷–:"[ÓÔ¢¹­ð I½^æo¯¼~øFä”É5ógÍ;u‰ýµ@¹YK¹À‡qe¾ólrVŽ.­V;>f…mßöÜig+Åòf„4@ùhšÖ¸|$¼úÑ› ó"o Êÿ¯æÀ­Õht¦27…Ñá§‚}ß^½•®\|~çØ6ñ*a'úëÐ~>rµ³[ÊžÇж·Òß›÷vOnñ(âé#÷fŠéKìíÖ½c'Ýú-›©åE¿zöô̪AWö„^s4–.U¦ìÛº7™ý ›¬:ôÐ}pûõÝSì^e†Þ›mÆž.£Èm2‹óuæ{7–}¢XËPzÍ…½ WVˆ{—>O³ê˜hËÇýÈøþ`τֱÊo½éf Z©šò5[¶±ŠòÏ/›acCmkM™Œ2Š-¼éAk;6w}’ßVÝÄJ+%ôýós«_õÛøìHíÒ¯"EJ·zg}ØÝ¿ùÔëù¡‹®mf¥öóÝû~‡øyßÜþâêtsYnÁbo-lïzý—Y]}ùØo?í›Ø1¯öûýU3(Ö`zÈÿ.Ääo]-'v7¨Äï¬_/=ú8ä§g¡'}×wÐ`o`‹©T®°å'>:þ„™ÏdÖò~úÅ‚‚lÝi>ÑäÕy妺õGúÞ–ŠÐÒšfz4ò…E"ÿ›³¢ÍÎy}jæ/BJ·ãôyóäh•æ}-¯l{vèfÔĉ† ’ùÎû|;½Û÷®ø°ür1ô{LlI?&Ñç¾Þت…|ù³Â¿ D}_ÿYÕ¬o€h©´œ9Úèˆ×÷¢}çÏòÿe0bŠû/ÁmæcE«/ zº´‘<Éýº·£ùdÿœÄÓK¹÷rfí˜Ì 7ÓýB·µQ¡å†èVwÂíW«fpTäÙùƒ:¼ó²Q`/&ùÞüžÓ}™Œ×AqyÆú%Væ›ãÇ>(ª«k˜;ùÜsk$—²¬~C÷9¶z3euCv–gHqËëÐÙ+0lf•¼ÜœÏžýË/ÃäZ%B^¥`¶ì7½¯Gíè¦Mÿènk³<„¤œwûï}_çdÁ+å°÷†I ‹Æë¾ÒhñŇSó[“£J;ëÍÁ+›ûù?GNz–m»3äÆT3Ùœo‡úXóI{°hΣ¯7n+õ*çK¬inø‘ñ38éÙr߃5mÔéÌ$ÿ…­Únx—vÛyò‰¾¾c•xïg¼ï³>—Â÷ôÖ¥Ç_gÙëH‰=½ëéæŽv¯©Õ`N„¿~~&æ=ZjRî`ËÊù~bJ—üuVè°åÎ!†R$û­  ¬tå “ô…Ó¿A¹i[ãR äRJêE L¦¶î%1„¼·Kµ½Óuû®œjÜqç’þÐcP#¢jÒÂÞÞ¾uÛŽÝzvl¨-SìÓˆ¡ß¥o aŸŸº=nœAîÇsg¿±·ûáM_ .—VËîæäñ{íw?"§…™°oðïA€H¾¡ã /×7 šïwäIþ×E“qo™já4¾ç©Zý§¶šâïÏ"a—c¢Ž½ÎT»ÇðfœN£RF}[Ðoû1þç?ÊŠÛ`qtñ²º3úµkÕÔ¼çÃÐrË#×hÙ“èeùAêWRRtTK¦Ž|d‘Ø÷±ÙìpÀ7«é¯©MÕù>¿\T†I£té”^¥Pc—…´óS’Rǹ­–}LÈÛkÏ“œ-©­T1EÅ–V²²y‘7s¾ÌÈw™?Ì,Ý¥k Ûë£ù<>—¦PG®ßêó¢|ö>ÈO‹ÒWÌkÍéñ@W·Ÿ³ÄÞsÔVŽÿAÿ„1= æm´pIwÝüæf»ñÝÔü/‰üúú99¯µ,µ•ÍŠx‘ÿW¦Nc}ªÝk™I÷—ts÷Ž'ÄjÁ³3,ó7·œpŠ•+ôE2R29w4…<•NqÝËUÎ{K”KEpºz‡­O›¬tÛ°ßçÝ/BR>=¹Ê¾Þ¸¨4›²ë„×pÓ¢¸+g6À¡æZÏðÇoÇŒšrñô'öûÞfœ½†ŸòÈXב&ïs»²²4”‚ ˜ŒéˆÙ-—L `¼öÃqŠâƒ#òû`Z9ŽªW¼uNÝÜ´ c.MAßXø'÷9:â§½‘Äî³SÜW|Ù_ŸGІN_ÖëÀÄ+Iñ¾žS}=ó_P¿i÷Á“æ/š`§]Ögt^¼ÿ¦™ÎN½ˆ+ž™y%ò¢Œys#^ ³cÞ *CÖ(Ý’?RWìUT-hòÊHW16S'“‰ÿ’ 5„âJ•]l¡ÅÈŠ ædQ¢maPÐWÖȾ—ï~f¸à×b/áÇËïœ;zM »ÜÒUL¬´ÈƒXÂŒÎ.Ð u, V–¡¢«B{]s³óXr ¨­lvbÔÏü¿jF”sYº¯û.¼ wk£^¹B05¹ý>Rc~–w¦Õu/Oyïm9Ô°™àu}ÂÆŒ˜·O>xôðÁÝën½I%?wèªl²V¯pVy‹¡ =½"yÕâë‰wìoJ­Ç·Õd ÐDZÃH½û’”Hî@yÅ!@!eÔß¹«ÓŸÌÀ#·¢Å~ÐfÚà:%â3YxÀg1s¸ÑƒF§1¤¹­hRõzi^ƒ¿E¡i­Ê6žpá³Í…}ûO\ºyïñ»„¼ìÈç7?¿xâþ…àÃ}uJ´Àå|ÙÛ¿£Ëv„7è³ÄmxS=Ŭ§®CW½*£Ü2в‰PXJ>µ¢¯Â^»ÂŸÌY,Þê³$ÔVª¼b /FÁ¸‰"?±ØXLÞ 2ŠºÐ¥Ššc‹àImeY9™ÜD&£ ]™ÞOF-'ýz¿vÂæO[ÉR®\¡d mêÉ‘O™$=Ðç}F—¦ÅówÖçk—¾™wkkªœÿvT¡¢ù•ûÞ |’¼Že[öÍq‘gzè¶Ífûe’ÏdžºÍ/šIÞrø¯-?žž¼vþ[(ûM²×N‹þMèÂiÒòÜŸØÕÅúËû©Ae @C×êâÜ_ÍçDòÓ#Wî˜ø§±?;ÛÎègTò4%4$.·½ç<ÃÄ·oSò£é˜èªèCˆJ —=‡Ú”uNC­á€ù[°ûÌ̘°Û\F.¾žsÌÃgCï1zÅ~DgÆûΔ„ÑqË÷5h$÷Ë—%B×@FÇRX*õ*ÉoÂòÚrN(c¦||Ï9w‹¦kª)Mi¥*_ ½ú„IJWíux:ËZ.?æd}<µåØÛ šbýQ³z } YÃ&59ïLäË©¬&rÜ1S’ß…äŸÅH¤ë4Ò“!…•Âʲ“÷›WvzåÔ/×óüË Íoö3~5-xÙ¤½ƒýfšR¬Ü Pm9®£üÕ«äûî…Gg\T§¨§IFèö}çä£Q—žê­EEEWë׋­sW]zù&¦ÎŠ[Çëtš‚y÷žufû…’™šÉäмÕðþú[¶G^[¾–ýÍ—a7®ƒ6Ьœ N_%š´\¥¾çÀß@(ššýì‘ú'¶GÞ_íŸßãvfw½ÒkÁÖß¾½«&=%`ûÆœ‡94×2ÌbE‚CI⯋ß[ 7’f%?\6Äéff óþ[{>™5aûýÏr£ÏßYn£D—Ó©ßmô K×ëXe6«2s¹-Ûy)ñéLRƒÄÝZãÌy$;õW¹¿5KtP†ÝN¨½Ja,zî±ÁoÈ–üq žlßð”»Êým?î-d¥h4:w!qïc²Hͯ_Ñ••2è:Úš½$Y·Ö ë2ÇJ>'üìüÉ .¤ZË]cœ„¿ C·ëä6RÏüsso»o èêÞJ•žë»ve@þ?•zNm£!,=eØ;žJ  }B~’ä;H)(ËHéܼf«Ïì€ÜÇ ¦í{mÅÊŽ®ÕsÝëk®/Y¾ŽÍ:½^í2¼M=MFÒ»»‡WºìÈÿN£Ûy„] :{Ý'RZ÷ª¢Éª1‚/ÞyÆ$ÏFõªñÓsfïfÆêR1oý,ñà\^QºQG3yò†ï9òõGôÓݾ3:ògþÀã:êÐI¦ðWÊIüÎù>¨bßÑ&Õf÷Y÷%;¯k;Ú«ˆ~Å@â @T€Bãɓ̶¯øðã5û¨ªÒ{fGÍÒ¹Jª¶ò‰núë™I} ûž‘ÿˆþQµ¥dhÓvÍ9ÜfÓ»” #j×\ÓD/ãíËÏ¿ØAª•Ãvs5Cb–:å?XîÍkžjÑÐP‘™øñÙËpvöÐ2¿›NÉ<ºVëH ;ENieÖ4ù±_|›éSwÜNÍõÜm@ЖC+,Ê(¿L]e(°„¾Ê²^ÜF>F-…£õÎY˜²W9<=ÿ½ñìUVo!l¥¤µ­jJ‘Ϲ$b³}½[ìÛN½ì•mé¸oÅñ–ËsæÖ×Ûa©™ö)ÿ:ÒÍWî]KJ:Zè«HÕ¹ûY›)×SBWÛj¬g®÷úcBþ FØžßÄZú*'ÅßZC¡+[œ¬¡µ!9F²?¿ŒÌî©Z‰ËtH›NÚµh§õªw·œœÎw÷H¥r+D®¾Ë• ‘]v„æ%Ýß:íþÖbÿÕì¹õæÖ.ù—裺îU&]gâáí×[M»–”ýbç¤;Kü[±½Ç]zB±Žì§³sw ;·שb¥Êþñâ3çë »²d IKyœÿÝM%!W$ëþ=ìÅÆ„„ œg!S~Sq@BÈÖ3³éŠYÏIþðÏÓíÕÊh—Th±áÎZ_—Åûn³Ó³Šy·©ëv.ç¤ ¢ÚzÃã§õÜ–n9u7440š(60fáêEêÊÒÈõî3w÷gï½|â—?$˜œŽUçQã]–Íè¤[º•[¦®ÓùÓÑãçí¾þ#à‘L‡I§®¬éýß·.³¯DĽz•YN³M`(¾Ê”¶Ü¡8 ï¿d}Úiñ¾»éÜU^»ƒ»ÊBWŠ®ÓoûÖ!çŸz›–ýýs‘-ëpT•¥)6]z÷U½µKÖñyþ9,…½^ÍzŒ[¸z¡':VäUdÍ&_3Û¹|ÍÞó÷ß¼}ÍNa†M; ™¾dñèfº ¥<µ$ÒmÚè°òîZ@Ü"‹ÊŒÍ~Íy»'ÿ×~OÔÏóÓ\nv8ÔBåVŒ”~ŸíÏ¿ :è¹åÐ¥{/>sΤc¨ÕiÑ}ÄŒs‡4R¥WjÝE@®îÔ‹ï›óò:pþîó·Ñiœ ^NË̺MŸ±NóÆÙëJ—ºÆ ;A÷ÑÞ½/–Ör\犕*/îñõ÷ùwôÚµ6à?×VáÚð—C€àR´ÛÇÚ/`^oÃQS9Ã?—ÂdÊ™ õòêUÖsÕm&mó™´­Ìåª5¿éâøM,§¬ñ ¯»ƒŠ¿ÈŒËßgMõ¿Wæ¯ç‚Ê@ýU†Ï*¸g{gØæÒ/&l¥h VSO¾™z’ï¡2Š-|eÙKR²¼úìàÕ•"£ßÁi/ûVfIÕJ¿Ÿr=¾²<øf¡Tƒ Fõ×Ù½;†<Ù=rôD#F/Qâ‘R3ÐTÛíŽdí.z€Jå²ÍË´âž ZÅ­WØz•~oË ¥i3fõñ1eÖx>†¡c Ë‘ïE»½1,¾ÚUh±#е£üùó~\Ýÿ$ÿŽn¿œ‘)ÙåLædf³_K¹ 8°!À¿Gš‡œvh.'BŽsîÜ&¤AÁƒ¥ï‡]ö7ïnù#Ó[.œÙ&ƒªPl6s†åî¥aÌGë÷‡ZÑ ì«y‹YaSj‰+ÂÇÅÑ úwÈ|½wC@~Ù­fÎlÊë“õñÒ•Ä¢c%ûÄÀ_þIj|÷ £!Z÷Ù}¹Ì¹ÿ•Zo%d6çÎBÜ yÊù×IB–r—&!­9Ý?¬ æ"d)'šgpBùBÖ¢^ðßÎßWB²8}! ‰'d*çqvžîNÈü‚ŽP-!@”ψ®„øp¢ó`BpœÆÇ£³ YšpîpÇ¥RØ¡‘¶›_¬fÁ'púFï'ä&!ïÉæô¢fß.ÒW\ëU„ P>:§+³g´;„¤qö˜~ÅçIá´7sGNäL΄†3´àt‰.OCÎÈl™œÁ:FrzB{pNIÃE¼ Ê’Ï·Swðc×ø ßÈÓËîPè Uœÿ_“õñ¤ç7šýç8Ú¨–w‹ŠÌ#V•¨D1—™™¸mŠãš3/cs QëuáÓ¥¾B/I^A¢ÞbÅXT1B€Èž}B"9ƒÖÎ0sz¥æYOÈvNÞÝ^ðˆ'@[qN¼À9ш“Œ‡p‚rΉƒ8-П 9Ïi‡–#¤!–œ1:p‹£+èÖRWÿÉÐÓ’ÃW ªKÚrFê œnÐÈ^’J¥ëɯ‰¿óYL&¡ÓE:Ä´XŠ2B_ˆÁ<ո˜›ø%žsÇÚyíÒ‰•dú·l¢)*Ào‡ ÌN€&œ¾Ëö¥þ«ÀéÝáÂé€A8­È;9×d!œ‹ª<åŒpw—3”{Μë¤ÔåüWžó¸;!gÉÿÙ»x(ó>àÿ9\C2r•P©!)ºoÝǪ¥¥’·”R6éØtèÐ)µ]»…ÝÚZ‘£E”F)‹$„B$ǘyŸyÆ5ÌÌ3ã¨vý¾ŸÞ}ŸyæyþÏï<óü<óÌó ü™±œ“Ðãð¢Æ~j޲ˆ…Zc/B=6'¿Üe$…PÕóí†~þ{KqIÔ›s#åbåÐX‹¨£/½‹˜X·"‰Ä*M8²ÖÙçr|n¥”æ°%;öÛ÷—#ñù‚›ýùÕõ=Û÷ýŸUÆF2š&“~Üäã>Ç@–'3X /V^à0 ;lÀú>ò¯Ú4ß3²Àpïë¤ ½%8ß…'ym;p)âiçÁ=º£»íñYnJ'ckµÐ°ç<÷gCÔeƒ+{/E¾*aÉh°ó:¶g‰¡,©uÅbj‹b¶{ŽIÉÇÞ$É똎·Ýèí>[Ÿ†G.t]VÞ¹a?bí·;5Ì4ÔÕÕ/8¹ ¦¾î} ë®ç>c%­Ò¡®¢ŒûóCØÔ®MZƒ™q€ÿ2š¢a£)¡zŽ®Ç"²*¨jC—í ò[Øõ¡ïÊÕ{C_~B æÿt&`Í®dDÜz|‡»œqÙ{ûþ‹a ÙØÎEý‘ó\}w­0•Ë;f.‚¦nP?q +´¨+å¥Â{ ”°Åªó¢¼|OG?_‰%Šz–Ó—»ïX7A[JøÐNhä‚ÚA,0Ô¹è¡H5à[‚tz§ð¢°­¿Ö¹)BzøCRø2Å/¡D¡ýø?ð­È ´1¥^Ž`fE=ùÀ2Ò 3s#ÿü‡ûÖÇè¿Ò+G’FŸSÿ¿0khãµ™äÂósFœ¯Ä_T½‹=¹ÜšÙ#í´µBó-T$ï²6ÛÏyt<]×P¹”ñ*ñº÷¼›Ñ¿<¹ãÚOºq9òæ dIYîÝ ÷. ,@ 7$ÿ»ÕÒÒ—Mj ›f*—uÜÁ<6çQ¬×Y©.uÏý¹°xÌ'ÚðS§ÉÝ}’}ïèVEò©—lT[WlE‚ÇpKß4laµþC‡(Ôæ?{üè÷soÆ0þZÙ“*t]N]hÜÜÙœOwO~´ÍH1³NXë;ÄÔ|¼êè5u“jkеÍ=}Kt–K\šŸ) +¾$jã”Õá,ÊóäÂZÍOBCÒ¡6Ô¥(üñô?²¦©‘‹nÚõ›TˆÕýXâßOü­Û­ï ß,„Œ¶„>pÒnñõ>E}VËeª_îÚõÌZŸµÓŒÊð2À[>5 Àê(#ÓI·â®C_ë“ù¨0ølÒ¡Ñ#e™°õšVùâ⯯eétE}—ÛQÛ¤«R<ûôJ¯‰=øÊÑ[ì˜u¨O…5uO&M#gùKxôü5ºæþy{Þˆµ×à„Ï̲wƳç~›¢ïûXÑɬâ˜Í#÷¦}ŽXïpiFørüÇ€pÕiÂzyi‰Àvxáè=_¨ˆXS¾%H  ¿4ù-ªK™Üáé€ÿI5«)ºèÉ?Ÿ¢³ª¬µŸüþ¸¡akV+º/¿Á‰û°®'3!"[PyÌD])Ô@#}·Ã«Œå±Œc€Ý–©‘¿•¡ÏY™¥µˆç®µ¹açð§Wʌ߸PsSBgá‰ÛJ‰EL­—4ϯE(/ cèíêçdBÇÞ¤`9NMö_ñÓä*“ ‘眴¦vŸ¾ÒœÍŠ;S´bZýZæ.öd8T' ǘ6Jý3á£Û”ÖkkÈ=g™sa‹§óÌQ&ú£ö=`p ªI¿(4$[µÆó›F›=&©qGJ£ì'҃Σò¬Œ’Z¤,FOÖ¹)LÖ;šp.‘Ñ›6£§#!Ú¤M {c’47Bîäoå¨8=ç35¹Dƒ°õêIyÆç{bÉ_Myqq~^1[²—: ¥³QÁ«‚jÞaŠ3ÖVRBšZµy·OÜçÜeSbÜŽ #ð«+Èôá®Ã÷ÙÞg×Äœù°trý²@¡„Þ?Ï®`–æä%UéWþúò I¶¿íÚÙ=%Û§’ªýÔ°­`•1w ´’mþ~ ÷6Œ¨”‘RÈ-Çùä¯ýøò%þÜ’ª®’€¯ê‰ŠÅPÎÞxhöF,½¬|ŸqÆí‡-·JÞÿºûö®ó„ëvQ›¢Dm=VQô9NÖˆ(ÖÏxÍîFBÌÌL6ÄÌ%°©÷N[ªN4:¥´ŒµñMä&¥—±¥¹7I)IKáüXIô2R—äÜHS\B#çÜ×C¬vh—šðu@ èäÈÝ,f¢ûÏÓO`¯d†ÍÀIô¦ŽRÜôúä1Î"Ú“ÆhµæçÿTÍ K£ä$TuÇ70u¼«¡LMöµ›BJiè±¥.íVªæ¸ù†è)} ñ }k±¨»»äç|—°Ênú6»»èÔ/øtEþ”È¥qþ¿<ÁgÍ2£ 8ã(¼ØCSâ×.ó¿—!½$øîO¦rdiÕþ—Ìí·õV,±Ù‚Œhü·Ù‰Dæ¦K…¯ÞW!m¾k4_Fä¦hQ[Åäž®--ª`¡n¨ðŽÏî§øœê²ò,í3f#êëË7µ(׺PÔ&8XQÇ0™^ã&xYt%3 ÂwýÇySnŠ“Uëžþ'4ò]Ûä„·ÕÂkÊ.sœê÷™DìÆž¡_íÏ2Z€´¸E'øw’Ð=Q=ÏfV`£Ød® ~§aÙ³,¤ƒnVbó}äd=i‚B­»ò䎋C=˜qÿë¯~¤ŸRIê?œÇ¬H˜ý|l‰N;~K¬:æzÎjZiÈâÚ>Æê_^&epÜc1Ë_Ÿ†ªê£öèri¢Fh=ê›Ô·ø"5–í¶íAEŸZQ¬‚Ò+s<ÿŽíe¦}Å| –,ëcúã¤l,Sš¿q¢ª´ªÐD!¡b¨MEL”s`xŸ;F–î×/,Ö¢-s^¤¦hQ[¬»<öÝNÌH-E4­!“í6{ož%b)ò†ºŽØûðQŸíÛ^‰d0ò± ™Ì^ºÙÛ}¶ jÈiæ{ïî wÛr2Ëÿäõ':ùýi¼¢üKx±F[#kzy½•͹÷˜´ªá8[{7Oç±jXž+t]QUgúš?gã•—Ÿ«ßf|@R|Y-—­)ZAäÖ“4p ¾šo¿áxTö»¸XÉ1+®Üð™V}úÍøu7r Ÿ=Ï«VÙ$VÌÉ4µ(¤ôBRõŽþäs"øÞ‹—ϱ?µLÆÎ_í±eÉÅÖ_Y,,rÞ•|Î ¯)» Õо ¹Qç?²ÏóÎ#kØ=`Û5_RÁ&ªù7æÒƒvg±w7¼l±I®ï<ïkó¼ùoš¸@Âå"¦›®8|{Åakr°XÒz üÂøµS±dûý¡ö$<$êN3tºüÂé² ò9Hü–¼Ý¥öryÆæ¹š†f~$}¤åªb´žTϹ~‘sytþó­sã+qbæ jj2f‡sÙ|J’ÔãrûÇw%!c@øbBz™¸P‹P…Õ”¤²8–½˜8H:$ЈhÐ&¢ý|à¿hÄ 4üç‰xy+à ZÐ$д3R“§˜Á%.À$ЈhÄ 4b€1@ € @ @ H $ЈhÄ 4b€1@ € @ @ H $ЈhÄ 4b€1@ € @ @ H $ЈhÄ 4b€1@ € @ @ H $ЈhÄ 4b€1@ èjsN˜wwHĦFüö!fŽbl‚UšpØq¥ÏïIL„¦†üóÇ ERlç¿¢*ýò¾3/”l\WšvÔNÍ–) EŸÜÜðÜ«Ö]¾j¸í¡eüí^#QZµZÆÜí,»O»wп}§ø·‚ÚGMÆi{—Ë)QúÙzØ›õï+ó_<ü·ŸŠä®Gòw_¶Ü´+¥õË€¦¾F‹‘ij:tú'Šº²4¹Ý6ÚYvÒÿ@@û`~Ì,Â'¯ßµm¹f;Ù,"“Û=è bÅô…q1$¯–M}•“Ÿp9ëc{o´ýwŸï éÿ H h%‰8² ›Ín×p@»`•&ym;p)âiÞ„ºèŽZì¶Çg¹)\÷>»œqÙ{ûþ‹a ÙåQõGÎsõݵÂTáSÝ·¨¸„ZÔÈòRá½¹èaÀv¯ãÁ1)ùXy$yÓñ¶½ÝgëÓøŒ V^à0 »x„ }ùWmšïY`¸÷uÒ†ÞaQ±òÎZhØ?BHgCÔeƒ+{/E¾*aÉh°ó:¶g‰¡,I`±õ­-мÐuYyç†iüˆm´ßîÔ0ÓPWW¿àä‚)ÍaKvì·ïSpÐXw} ·ÚI«t¨«(ãþü6µk“Ö`fà³ÌÕºwI$Vi‘µÎ>—ãs+ë‹í/GXKBªÙ¤)¡zŽ®Ç"²*¨jC—í ò[Øõ¡ïÊÕ{C_~B æÿt&`Í®dáƒD@b B!Ñòo1ÞV­z¶µ¯‘O&§F¯’Ýô$8óJÿ°¦Ï¸ËF4›°üëãºô§‘›\3í›2’h£õªó¢¼|OG?_‰¥Šz–Ó—»ïX7A[ª„ÿî³@©ÅnÂþüêúžíû‚þŠÏ*c#M“I?nòqŸc K"Øј¬<„› öÂvFƒ÷|†}C‰z¤h@ÐJX,J þ=ØŸb·ZZú2°I­aÓLå2ÃâŽ;˜Çæ<Šõ"‹Í­~}x’ñºûÕØqÓpÌdµ‚ûÏïw´|VɈZ¦bbmZðäñ‹bl9Šöà!ŠCÔj=F[ú¦!DVë?tˆBmþ³Ç~ß9÷f\㯕=%šoŸ,)+‰O†o]YÀ™EYª‹¾Vö…Åc>цϘ:MîFè“ì{G´*’O½d£Ê·XÂúV$x <•(*Iw£w6Þz«\Ï@C¦àÍ—w±'—[×öxñ³öP+üç”#Yk©–Ê`¥f‡#’ ßeêNÞ‘ ÏÏq>¾Q…Ëì‘vÚZub3«ÙP‹ÛÆz&HöìJAÌü¸€%3Ñc¹ó9:=º¡OJR®¸L–˜~j´3Ñ`Yijk.,»ç C­|qñ×ײtº¢¾Ëí¨íFÒU)žýz¥×Ä |áèýKxôü5ºæþX†gìy#Ö^ƒ\ñÐi–ë -—¸4?SÖï%Q§¬gQž'ÖöÔhñ%5…ÊÍl ÂãÆù%¤®1–¯eÖdì³"Sêòá|º{ò£mF2ˆ™uÂZß!¦æãU@¯©›TùK’@Õ/…Õ×6÷ôYÁÁk~"h«†º…?žþGvÀ45rÑM»~Sƒ QÁÕc‰~?ñ·n·¾ƒ|³2ÚúÀI»ec¨Ïâ³LI0÷ÝÒÈ`9¿ÄÒµƒ)ŒÃÓÌ×E~ÁŠ=úè€õ¨\¢ÀxU<VÍž´úZDf­ŒÏÚiFexà•Ÿ`u”‘é¤[qס¯õÉ|T|6éÐ葲ÂÉÀ¶ BáÒSƒ_‹µ‚оÃya7µÀ̲wÆSÛ~›¢ïûXÑɬâ˜Í#÷¦}ŽXïpiFxË݇O!§Wâ©­ÔÈ£);éIÕ¼ œnhwûó}÷u×ÞqCø&~”#ªW‚MüiH0º„îŒç½’[¶UÕ³? ;¤ƒ t H h“VäÐ=Ÿj²oüúœ3¡2yÑyNŸR»O_iNŽˆfÅ)Za«&mäŸï‰õ`Myqq~^1[²—: ¥³QÁ«‚jìÛ¼D²÷ÔoÎ…-žÎ3GY˜èÚ÷€!B,½]ýœLèX"BA™DQ5&æ.öd8T' ǘ6Jý3á£Û”–ÅJÖ×ÖPHð5éEŽÊh³Ç$5ΡFi”ýDzÐùbTž•QR‹”Eíþôݯ2–Ç63ÀnËTÈßÊÐç¬ÌÒÊ^¢7Ž Jê'LÖ;šp¾ã×›6£§#!Ú¤M {c«JšÎ!wò·rTœžó™d  ¶tJÔwbßè¡6ïö‰ûµØ„ĸFà—;éÃ]=†ï³½Ï®‰9óaé"ÂBksÃÎ=æLȌ߸P³»Iè,Å—LZ $x1¢¢õê[ÿ-5E^M¡b„˜ÕüL 2}Í´¸ßÇ#‰n:t„ʰ«º¶JôÀ¸Dì#šN/üÄ*EVIŸ£l !Í­•gN9¾y¬V$1 ¯¶tJûÎÛ¯ïªÞ%½Å'Ôé6\kL–×5TF÷ +‡‘_Mœ•Wå>ÍÁ'TújJ×Í“ê>|jw|ª"îÑ&êhÁõªº‰ÏµÛÎ(ž6 *ÐA  ˆ˜CCöü=#Q$¸ß=SûL™oÖ­é÷Ð¥Á])¨&ó„µÛ}&BšÓ=¶/2Q—­z´uÁÎg‚‹”è¹,$Ã4ää©K„E=LûP[›z 1ôÒ½§çf¨ ü~]RVª~4FÕˆUËj_lVM-wý&g\›+BÉ‚?¦*rTdjcíø›ŠdãÑ‹ÔxC1š«Žð>ªÿ{£þ›ùÆ:`õ"ñÎÁ‰?Hx´¥S„(>?ŒXLiXGôÏg ›UW8E¤Òë?@ >H 7!¤^B7Ñž;£Ð¸›õHè @ð•@öü“Tí§†Ð{„äÍÝ­ZüÞŒ•}Žs Cëƒg¼fw#!ff¦Q©…³7š½[¿ò}jÄ·¶Ü*yÿëîÛ{§-UáôDQ5QÊH)dŽ–ã|ª×~|ù²”3¤ª«$à7F"”,0ø]çEŽê룹 é£bnŸUÔšA"VümQ¤ú¿<Ê Ê°¬Ž36ªÞ>y+z„­#¥e¬×+7)½Œm,ͽŸEIZ ç'­H¢—‘º$q!’ê4ÂÖxÿ<»‚=/¤*ýÊÁ__~!Éö_4‹xémÛÄÂií°3æ6[’¸GÚ:¨@€ö!ü$4dÏß“šò’ââf}E’Ó7ß=e !~¡o-u—`—<ðœïVÙMßf÷q—,&÷­ðyúÎ;š‹ÏøOS©¥%tWžÜqq¨g3îýÕôS*Iý‡ó< ³Ÿ-Ñ‘¦m¢„ |ÂMÐdÚ¼3¢m4– GÚ<¨@Ç€€öÔ2‡†ìù߃ÔuÄÞ‡úlßvðJ$ƒ‘hZ&³—növŸmÀùq¤KðÕ|û Ç£²ßÅÅJŽYqå†Ï´êÓoƯ»‘Søìy^eóž–1ÚùXÓËë赨¤øhÎíǤU ÇÙÚ»y:UùrU‚¨ÑÌ÷ÞÝî¶ådvÀ–ןèä{ô§ñŠB2á%/jT‘Ugúš?gã•—Ÿ«ßf|@R|G¢,ÓÚæâ"è#R.^„ƒdz›â'Q„-FVu6ü ¬ÓÎËO K2s{ÏݱgDð0NºVU^Å·¾H¤rHé9„¤êýÉçDð½/Ÿ#$«e2vþj-K†(Šº3dM¶E>ë³ËcOÐíÄŒÔR¬q†L¶Ûì½yž¾~…M´}glÑVÄ=ÒæA:$д³¦94dÏߊÖÊöJ‚…ÈtÓ‡o¯8Ìÿ]©žsý"çúñÌsþó­sËѹlžUÉ &öûCí÷‹¢‚Mß#<ªz,–´Þ¿ð~ÍßT,AÉDÁ ŠÏF¥íÎbïn|M3tºüÂé² ò9H-—!,VÄæjXZX5[Ô‚ÚËåÛ¥éšù‘<ö‘Æ×„ƒ„½Tø&ÚÔ)üZ¬Efk/$®½Ðd–E{К7r³ˆ7Š“ÔãrûÇÿ]³ÃÍv~Hr}çy_›çÝšMˆ0&‰7ÑÆ‘o[öˆ¸ƒ | @Ðþ¸94dÏÀ$ÐtÈž€ÿ*H $Ðð¯&ägÀ×;c' 4hÑa : ¸vÀ$ЈhÄ 4 ó*ýÚ>ã.ÑlÂò¯ë"ÂUé—÷y¡dãºÒ´kë®_jVBIð(ú¬h„俆ç^µ%„ÖDÕåU›s¼»C"gRfBЫ›¶¢+½1¾Û´;µ¨ÿ/éOÖI­_Œ/ULJ‡ºùÒß”*lkÆÇ²9v§ VùË?ü÷\¹›òöçyÇ2ª}ÍÆÎ^îº~¡q³ggTç?ò;t.$òñ«BÎS£%è=[M]¼ÊuŸ2$1ªàª+ÑdI>—ÆdŽÚ\í©íc²:`ýËtD ´¢ÌoÒû€hÐ~àÚ×N«ó|nW$põ8’7¸û²å¦]E~˜`û–Њ2;b£‚}ùÛukø”À Š<.¾¼8¾hŒSH>ϼ÷/¢Ýýëásû¢‚]ÉqC`æþé:iÖágÌ&KÖg> =Œý;`{>ü佤¾EÚÃ×í\PLSÓ¡Ó?QÔ•¥Å~ä{ûÞÿv Q}a\ ÉûÆ%´¢ÌŽØ¨PEA«|Ÿí1vÖüTÚŸÓéÍf’(4šyíƒB»î_äñ.ƒ§],FHË9âáöÜ4—$Ñ…þùžËĺìY}‚ÛÏ.s†õ¦³ RîœÛ½ýD|Yiø†i›Í‡GÈ“På3ßÉÓ?ã,)?Ìi§›íè~Êäâ´˜K¿l;Y„2ÏÛŽSê™ô‹%O´¢T-H…v9ýÌf±™L”ÂõÎ\ò.g}üÖA@ïC@: ö翺;{ž½›QŽd{ŽYî³Û‚sö¼ñ»v9ã²÷öýòˢ(êœçê»k…©13ë®Oá.—´J‡ºŠ2îÏaSå¯Ò ÿ®Ö½K"±Jެuö¹Ÿ[)¥9lÉŽ€ýöýñ3¨­ˆªk›7ÊÁ*MòÚvàRÄÓ¼/uѵØmÏrSºÐnj:Rùoª2ö­:¾üÁÿô%.'ÙUIYY‘ÿ{Ò JÒu“Ÿé2Üójš"¶BýìÚ·§6ÍæLuu9áÊ| îÁ¬woÃa“'Z\ßMåcJnõy‰÷¡nÞO9oÊXO¼éлîTsï~f¦ ™3à‡ÐR”áçrÎùáªú`E­A-±òÎZhØ?BHgCÔeƒ+{/E¾*aÉh°ó:¶g‰¡, _&p˜†]«Œd4M&ý¸ÉÇ}Ž,·÷‰V'DP¾°QÍÊ;7LãG¬)úíN 3 uuõ N.¨i9>yZ¸a•”P½@G×cYTµ¡Ëöù-ìúÐwå꽡/?!…ó:°fHW²Ðx ïå²"tqÕ³­}|29e¾JvÓ“àl‡÷*²–­ØŠ}¿U»-$ЀN¢6ï÷¥–¶×бI’úÀþôŽ¿©Àj\ úõáIÆëîWcÃ1“Õ îG<¿{ÜÑòY%#jYF{¨•a^ £[’¬5ÈTKe°Uè*¼Û'ñ-Õ?"žŸ3â||%þ¢ê]ìÉåÖÌi§­jĪíåüYñ)v«¥¥/{GkØ4S¹Ìð°¨ãæ±9b½†>ÃJÖÿß®þn®á• Û×]]pãÍùZ™ý1öb<Þuºkš©Ás$“2Xu;™ ]ß2ûÃ3wñ j¯Þ³´7OúGí>w׆ŸC·¥!”pöVÎÊU²T/,ó‰6|ÆÔir7BŸdß;ú£U‘|ê%U2"KÊrSõÂð­ Ã# 8«aÿ«z}ÜÆÌéV 祊ž¡Â§´W¯£7EÿæÿäY‚z_ØZ7WëK¡Šä]Öf[ãk°躆ʥŒW‰×½çÝŒþåÉ×~Ò„«^>Á¾&!Iã6EÁÍ£·Þ*×3Ð)xóŸµ=^²ny±/¹a•ÛÆz&HöìJAÌü¸€%3Ñc¹ó9:=º¡OJR®¸L–˜~j´tºð=—Lƒø],¶V|"µr·­ 4 s¨bÙŒgÏ’#2œzK²>„­2šа@å‹‹¿¾–¥Óõ]nGm7’®Jñì?Ð+½&öPà Gïê³Nü­Û­ï ß,„Œ¶„>pÒ¦ Ê§;„­Â›hPø•€J‚¹ï–FËù%–®Lažf¾.ò *¸zôÑk«L±£jûFÇË£ê´#øaXsu4ã°•<‰™}f¢Á²ˆg;×\XvÏAGÐÁƒÅTœsÔó\÷§·]="¦œßü2:Õ¥E……µ¼óÈR]å%E¸î¸*79_·‹ÉÈž-ò9ª½!¼êw‰¯9i’3› 'Ý|IIÑ#”QZ!B2+gˆYÑ‘)Üô‰OwO~´ÍH1³NXë;ÄÔ|¼êè5u“¢P¹Ë„ÇóKH]c,_Ë¬Í ´vÆÙ~›¢ïûXÑɬâ˜Í#÷¦}ŽXïpuF8ßÞgfÙ ^ëÒŒðªO¯Ä³[©‘GSþvÒ“ªy8ÝÐîöçûîë®-¼9â†ðÕÔ"ø“‚™!¬ü¿«W ß×ꛢ(üñô?²¦©‘‹nÚõ›TˆÏcØøäó{߆U"³VÆgí4£2¼‡ ðÀpj@€ÕQF¦“nÅ]‡¾Ö'óQaðÙ¤C BjR  ZÑÅbjÅ'RõË]­ÚmA+@c:…Úü{738Rc]ôæœ\"wåâdà‘V·„´‘g|¾'v¬)/.ÎÏ+fKöR'¡t6*xUP­Ç§ÐV¬"¾ÛáUÆòØAx€Ý–©‘¿•¡ÏY™¥µãÛq"oɳ²oüúœ³„ÊäECä9)-µûô•æäˆhVÜù˜¢¶j‚N¨±‘T_ç£ëŽYÌ. \µ{õ3_=¾ËÅ/7Pi>¯Ùõ'‚±¾”rO›Óe…ŸÙ«­(þ‚OÈ*ÉñIû(]TäÂ芒/ _FˆZ̽¹ÝZäûÞ?X¬ÂÿÏsû2œ ªŽ“…cL ¥þ™ðÑÍ@µqùÞ®~N&t,X 9÷ö‰ûœ?$ÆíØ0ÿžLîê1|Ÿí}vMÌÙ˜Kgñ©rÁZ£?{ÌYRfüÆ…zœ!$¡³ðÄm¥Ä"&‰ÖK‚x£‹Ô®>€Ü0!åK³ wœF›=&©qò¥QöéAç‹QyVFI-ê"8ƒ7Yïh¹ÈCoÚŒžŒLlˆLÚ´³ÃKšÎ!wò·rTœžóYj¤È»•À ¸‹…µ?â¼Ô´z·âƒÐ)T¾.Ä'” 4ëODJªÔ¦ ´úÓ µE1û׬ß{åI!ïeXµï0ÓŠUék¦Uwµ­D7:BeXéÕX1í· 16Šªß¿|Ï/8i){’w­¬Äœ*[5ÁÅ’ä†n=bé{ÆÇ¤Úp©µ¤¾Y÷ºÜ¨ê]Ò[|B}nÕ¿dy]Cet¿±rùÕ|hµÞd}ÌÁgªômؤºŸÚŸªˆ;F´Q¤"ô·ªÜ§BÊlj6ªi½úÖ_D‘W“G¨ë±j¡Ÿ¦ÓKïHlˆÈás” 4¸A`C›SÎâµE÷DÛ­ÄAH ›?±÷ý6í¶@L@ƒÎ ;öš#ĽŸë„ Ô ,„#äƒ}¤á/CšÑ[¢kò°qvm㑨&ó„µÛ}&BšÓ=¶/2Q—­z´uÁÎgBJjÅ*Q$?ŽI·^hÏMˆ¼QÎ+Š7Ÿ¤ö™2߬[ÓÜ’¢4˜ð~YdåÉ»÷Œ ^Uýx›Ë5),;d6[dÄobæ´v“Ò2í#þ©D ·_}o›TeüõÇý‰#{w!cKíOCÿT /qÁ)哆Ëñ,Yýæîýø”þH]i”Öä-ª€øß…CR¾åYé:¬ZVÀc³j¸¹?‰÷¬ ¤¬TËÕÙM3&vݹr…$üö¡‚Öb×½Áf ý#LðF •/ê¨&S›FÄ¿`ꯤh\+„Ô¼,€y"îVâÆ J7iYS`.ÞŠ}¿»- $Ðt° „\ð‰~Ù#Ô÷‡ÓiI(é*!ô¡÷Œì öüÑUoâ3ê¿·gEŸã«Åúà¯ÙX ÄÌÌôZd+V×WØ’ªýÔ°¶BHÞÜ- ÐJü_Qul{°ýùç¿6îÕ~–¸ºµ³–¹yó z{|óç›+z5Þ+ã ÃñŒ qLÔÝöÄÀiJC—Oêz­ œq=º&Úm@c²Í|{Õý—ל)‰‘Ô›§¢TA´»p4(e¤2GËq޼µ_¾,åÌ#©ê* ¸^=}ƒj"|Ï<Ÿ¸a™ý»×ñ¬†XLnŠT[ZTÁBÝPáŸÝøÝÏPuY9÷ˉÌ=“Tøê}Ò–a•fš•@# [ü¨Z–)öF1TÍqó ÑSúâúÖbQw vÉÏù.a•Ýômvw1".EÊpÝÑÕVGÞ•§¾âóvMyIqq‹3z$²”\Wñ‘‰¬<Å×cð_[“Ø_ÂWûÜÛm‘U%JqZ也ݎÄqòµq‹-»‘Iqü®–¬{PSûx“ùˆWÞöãú«PŠÓï_Ù»õ—»œ 8HFÛ.Т 1«Ð O÷î‰Xä?A‰\çÿË|–Ñ,3º€3›µ VÔÇ1Lf„×Á¸ ^]ÉÌ‚ð]?ÇqÞ”›âd¥H"å¶è\µT´J— FÉI¨êŽo`êxWC™šìk6…”"ÒÐcK'm” ’TÍ ÂÊw!Õ~¡®ø{®èw±„²½f¡·×/&z[Ê3³¯{ÍhCÍwmâÝ–]æ4Õ-î3‰>þØ=C!ÅnH A2BÛŠ@ˆs[M„ã—d4|oËFè2BûJÀ_*"4¡]) 4 ¡èúÅVàÿ.!4¡„Ž#Ľ…§{žWÉFdÕ™þ‡æÏÙxååçê·PCl•B¡«4Ó¼)¢ÏàVDÕ¢L±7Š#u±÷á£>Û·¼É`$ä#š–É쥛½ÝgˆüC$²Ê´½»†ÿ±ê~OêñGÕ˜îŸøfîÙ}ÿˆz’ÿF¢ÐË|ÒbçMÿ›oÔµ1Ñ£(ÛýàÍù_üNG$¼þPÍù—jß¡cgü°zݪBn0Ö¾U ™ï½»+ÜmËÉ,µ’ןèä{ô§º4_)=‡T½£?ùœ¾÷âåsìCGËdìüÕ[– Qäü•Ä¿s‰Ö"Éšl‹|Ög—Çž Û‰©¥Xç™l·Ù{ó,<Ë"ZAù„£zz›[Z8v«ÖÖÅdÕYgÃÊ:í¼ü¤°$3·÷ÜÝ{Fã$ÐUåU¬f%µjßo݈h„ÂŽ´ø„&žàÊ#”žõ^ÀߪDèWül´>ž1K#䉞^cyó/ÍǧˆðùöU ´é‚>Q‚Ðü·†É@w0²‚™ó¹ÇÎçšÌš^È>Óð¢ç\¿È¹~<«8ÿùÖ¹ÉKš¡ÓåN—›Ì!\…©e QÍi%=hw{w›¢jóFqdºéŠÃ·WXp­• ì•-fS{:Ý«vj:‡ïb‚ÉZž*dŸø¶”æHG?ìqA*Cíw_±ß-x ‘« hIb,–´Þ¿ðüâU°iÑ)\’c\N`ÿøɧsEX‹sŸ‘¾ó¼¯Íóð6Ñ꣭|)¢QÍ^Ê»¿ñ)4j/—gl—¦shæGòØG^íV-+Ø,þ- ¬‹ñ µ×^h2Ë"‹½G@™„­Äß¶Û’TDzó} ˆ h„ÂÒeü¶šh2ž=cºã q4BçñK2°Œ9Ÿ_ƒßßû׫~Ý~’±C>qOÄGá™÷ƒ­Ú$Ðõ¾~â$þ¯©,„ª’A(ÿà“ëò=?Ã=E½¡"„¸gµ°|zBë/À÷ h„j¸à¯Bf¼o)áïf"dß V¡í©#ô¡BË\†_K} ¡0Ĺñl5B¡ø?¸E4ðo 4B5<<Ê¡@~ D×?^á B³ñ‰LŠˆ÷¦•øÀ¯„Þд¯p'ÀWFxÅ0ø·ƒ.ît  –Â6»¿) ¿o†!þóÁ„Þâ@—à¿ Ä_ü‡€ '+Âÿ[ˆçÁ\åü¶ò?P0~Z¡‰ø3Vb…?R ß H ¨§Ëo愎!d…P)B=2Fè%žK"ä/0¢~IG„®!ô_¸ ÿç€ßXc oZø[ïð BÌñá¥ã?UDøeÐpúøîA ‘øeÍÛŠÄ•BÃ/ÕðÆï¡ðÿ^EhžÇâ÷åðAè4~gèB„òZ”&ƒ—ã…gÛÜÛwH#4!7„Æ~Õj u ¥þñ™"t[ð»sñM9ãÿ4»6Clá~Qcí æõžAú›R…-b|ì™ûås£…? ¯$x}Á2 MJÿ°¦Ï¸ËF4›°üëãZÑÄUé—÷y¡dãºÒ´+ÑS§¿ŠÊ‹[“"›ýòâž«¾dÝüšüG÷]ó !ß’ã–ÅêîÈ}gàþô„õºÏw)‹ZÒcôùø´Ò²ûY§,eksN˜s—ñÛ‡˜9üžê(¦¯0¶Û¾‰ï®sA§ 4ˆŽLSÓ¡Ó?QÔ•¥áz›ïSEòW#yƒ»/[nÚU¤çæu4é®ç}~¸%‰õÈ}Å›pÇžœÔ˜ù&Èa#'{F[Ïm4’ù߸Ƴó7sÖ¬íY„þüôÂíæ¿al :-H €„îÚ…v5Üï*â]O»XŒ–sÄÃí¤ð™$‰.äHÁó«'?árVǧ2"c³XˆLny&NÐüNà ãbHËK§¾1éþë/ìºÖsbuÌÿœ.N¹±´;éÝUg—ˆJì=Ýç6Ó‹gX%ý•³juî!º"åÒÍB„aVÛA~gc›Ÿï²sA' 4 3 K+(I×M¦ËpÏ]QhŠJÊÊõ³97XÁ‘H¬Ò„#k}.ÇçVJi[²#`¿}9<mñtmQlÀv¯ãÁ1)ù_°UåuLÇÛnôvŸ­O¾²?¿º¾gû¾ ¿â³ÊØHFÓdÒ›|ÜçÈr—¯Î‹ ðò=ýü=–]Qõ,§/wß±n‚6–ç³ò‡iØÅ#dèûÈ¿jÓ|ÏÈý¯“6ô*â?¿7¥49ÈkÛKOó°àºèŽZì¶Çg¹)½þ£àYyg-4ì!¤³!ê²Á½—"_•°d´GØyÛ³ÄP„h'àsÃ4~Äë·;5Ì4ÔÕÕ/8¹ †·IÙŸ_üêîìyönF9’í9f¹Ïn Îýo¯{½…™Œu×§p_$­Ò¡®¢ŒûóCØÔ®Dqòé¥rÆeïíû/†%d—cEQäþb7á59•¦ãÙ–ò¼ËË›ÍèwcoêãÀ°¼å˵(¨2í·à,­>­Wr¾E‹Jxcò.ÛØw·7ŒõLìÙ•‚*˜ùqKf¢ÇrçrtztCŸ>”¤\q™,;0ýÔhy™pÌ´ ‰p”ÖãÛ¹JýÖÏéögДpöæ»•Ü º67ì×dÎ*JsœGª„D5xA ¼J#ƒåüKצ0O3_ù\=úè€õxùæKV<=}KG–K\šŸ) KJ¢6NY΢pÒ¦ föIâ8yU¾¸øëkY:]QßåvÔv#éªÏþ½Òkb¾pôHT)?|”ê»&hïïýÿ_‘Êé¤jÆ{d‹?Ë(ãgôÚ›šx'ßÎN“™~ýÚlÝዌe“,"¢oG}5#³VÆgí4£2¼‡ ðÀO °:ÊÈtÒ­¸ëÐ×úd>* >›thôHYD&3›ZdÐÕiG„ŽÒÆpøu.fèš%݃üÞ6fеùwÏ?æ¼£¹ØÑTŽ|[xHëÙ¢/ÐŒ¾ÛáUÆòØw€Ý–©‘¿•¡ÏY™¥µH¾y¦E–âžú͹°ÅÓÀyæ( ýQû0•[›v?´ËŒß¸PsfLBgá‰ÛJ‰EL­—Dîí÷9׸JŒÛ±aþý1™>ÜÕcø>Ûû욘³1–N®/©·«Ÿ“  ‡Â{¶q>ʼñësÎ,•É‹†àç8©Ý§¯4'GD³âÎÇ­°U#¾þ‚dîb?@†3AÕ±q²pŒ‰a£Ô? ~&Šv‘Jcnh´Ùc’瀣4Ê~"=è|1*ÏÊ(©Ö¿w3ƒó¶ÔX×½9Ñ»rq2ðHk] óió<ÂVm'NÚÈ3>ßKºjÊ‹‹óóŠÙ’½ÔI( ^T7YŽ¥jQ¿n“Ôb¢ŠâsñT=S}y>— HëÍž¥½k_öóï—.( ½ú¶¢•ÝpÅhѫ͗¨=ÞŒÉzGÎE&zÓfôô`d"D›´i!§·$Mç;ù[9*NÏùÌFM.Ñà?f>º5»+>ªÉ&¥D•’¸Ò±¯ßÖõ4¹(:(žs®Ý²2è‹ðÞÏÊkk ³‚´ŸNù‹%ð$Ó×L«îNcÝtèø£oj«kù<)RzÀjÏ©g–ß(. ßç¾q2$“IóVlt_f©Òòãµ*÷i>¡ÒW³þÊk©îçvǧ*⎽Å'Ôé6\K–×5TF÷ +‡‘_]Ÿ@Kê›uçwõn“ùÕï_¾Ç' NZÊžä],+1§ÊVMF´àéú½ê’M£'Å|D¨0#/+—(Z¤Ò#­W_¥º")òjò#Ĭ®­*|ͽ¦WÙ ¡A$ÕjSPZm«Z¸¥ªwIbÄY§¶(fÿšõ{¯<)äítÏ P)ÁOez°}éÑÜú—ÌûV]šú§m‹3à2}ÌÖÚç—ó0è~žyÖ%ì ‰ö#•(mM [ט4^ x„Y%9|޲··(rœ9åõ»GÃ!@À˜ùXÓ¼lF)a­${/^7ÔÃ1Žpö¯w+eïÅržk¸Ò¶Ö¯õ ´€òsÞ´9ÐIA ¼(’ŸŒ$á·²è¹,$Ã4ää©K„E=LûP[›z 1ôÒ½§çf¨6ÏŒØl6Ïÿ Àó.»î‚`¥1IY)¾a5™O¢Hp·Ní3e¾Y·¦‘P”ãw|}Rɪe5„ÃfÕp[R“ór„ÑbÈÔÆHüBoR»¶i*f !Jœ¸šÌ6Ön÷±,LsºÇöE&ê²U¶.Øù¬ùr„•j¶ý²‡;–̦T—^ö—p{*÷ó­5ë‚Ç]£Ö¬2ýÍÖô;øîÑå¿‚ß0°>n7J™üF”Z %Z7S]Fc%±Š“xç4C8fê‰0J‰Q»Û¬Ÿà2ÿveBмyšïWa3MWÍëÕôÛ!µK s‚Ú‚¢0pöÆC³7bÇèÊ÷©gÜ~Ør«äý¯»oï¶T7gT PBïŸgW°Ks²ªô+}ù…$ÛÑ,cmì-„r“ÒËØÆÒÜ”¤¥p~ˆ$z©Kržû."IÕ~jxiòænV~ %$øu‹”2R ™£åð_g}|ù²”3¤ª«ÙS…0ZBJºJøƒíß3°"ƒ7È›ø ÏRb´pKRZÄ­Ê‹U}Ž“=#ŠõÁ3^³»‘33Óƒ¸2±Ëâv,ÙϹ^EqÁ‰½s§’å—„N*,½îø¿[#ÏOUæ­‡Œá"ƒþ¹ý´ Ë)–vcTȨí 4©ÇÛNÀ˜Qjñ«ZÑF)²òøõ6 ·/•< ºqW7æ3–ÚŒtžÙ'¿’†–|;:%H  •ª_ŸX¾Ìÿ^†ô’à»?™Ê‘¥UûO\2·ßÖ[±l¾'™©š– FÉI¨êŽo`êxWC™šìk6…”"ÒÐcK';XQÇ0™^ã&xYt%3 ÂwýÇYUnŠ“•¢8WIQ5ÇÍ7DOècˆ_è[‹EÝ%Ø%<组UvÓ·Ù}Üň*jðO÷î‰Xä?A‰\çÿË|–Ñ,3%ujÛ£¥ª ¯ƒž¾A5¾gžOÜ0€ÆÌþÝëxV«[˜sN”ÌÍE _½¯BÚ4ŠÚqãd1¹ç'kK‹*X¨*¼ã³û)7œ²òšº;Ÿˆ‡]þèç%¿üƒMÉÏ<¶Š2ã„=sÿ˜õ[ɇ‹+ÜL=9Ž÷ª2ýÏTó?šŸû ÛÞ0;kU2ªTxMyIqq³Z$äºÊIðο1[ÿ˜¡·hjÂQÊûÃÆæÛ0_aøº4.ùçÞóöJ)âüÆ`Í$õfgù‡¤¬UM»8Ìiª[Üg}ü±{†BŠ êA Ú¤?uøW‘Ô2×+s<ÿŽíe¦}Å| –,ëcúã¤ll§Pš¿q¢jËDKBwåɇz&0ãþ×_ýH?¥’Ô8®0ûùØiÚ§ü¯™:Þ*exS9ÛGŸVø<ýg=ÍÅgü§)“þÌ‹op«Ž¹ž³ÚŸV²¸‡¶±ú——IåØ|‹YþúœÔCÄà©=º\š¨ÚGú&õ-~E©Æ²Ý¶=¨T2Q´"°zÛXÿåá_XÝj7T)e¼–2ÖDOÞq?]Äna$¡b¨MEL”s`xŸ;F–î×/ˆ'YyÄ#”€¥Ì Žïõ.y]dµÚºìHD3Üaâìä➪e—?Þi»—óímâáC³¸©Yuú£Ón-ú³<ÿô²íKžûõâY‡6ð‡™ªG¿Ç²C »±üjÚ ÎN·åƒ¼ù<ß[üÆl-Ac}j¾$á(åÁ§ss¯ § rX¡ç¿ãõ»çØ>"?mµR³d]PH’$¢j ŸÅ>ÅFƒ¤fqG>Æüë@ ­%c´5ò±¦—×ÑkQIñÑœ;GH«޳µwótÛüºVIÖd[ä³>»<öÝNÌH-E4­!“í6{ož…¬¥ôBRõŽþäs"øÞ‹—Ï’Õ2;µÇ–%Cž“ÔuÄÞ‡úlßvðJ$ƒ‘mËdöÒÍÞî³ dD¾>Y§™ï½»+ÜmËÉ,íןèä{ô§ñŠävŠ–ªc÷[Ô—u«w^Lx_šý^{ò–Pß1!VcÏ¡êòÊZñ[˜¬:ÓÿÐü9¯¼ü\ý6ã’¢Š§¤KðÕ|û Ç£²ßÅÅJŽYqå†Ï´êÓoƯ»‘Søìy^åtñ:âs‚·íîל‚Gî?º°ñÊŠÆ\ÿƒg×ÝùòÖßþç…7zó¬…eÐÓUŽŸ, µÇ·¦â±ÇÛNð˜ih”òàÓ¹õ¤ú,]c²cm"âÜþyõð7’8Ðh@g#kyª}ªå|›¨æ_©HÚÅÞ-d²‚‰ýþPûý¢o$×wž÷µyÞÞ–ÔãrûÇ÷M> O¦›®8|{ÅaÛ)xKZo_ø?±£åXó&%+˜9Ÿ{ì|®É"Ó ÙgÄ ² ÍÐéò 'Þ§²ÅÙŒTϹ~‘sykìüç[ç†쥼+´'MÉšîJcïâ÷UÛ>¬Â¾áeÛ¡éj–'Þ³›DL3?’Ç>Òð’¢µ2½Rx=ZÚ˜Í:«EßQ{¹àë€@K‚®«@o1fè3ê…l†1è(@ H $ЈhÄ 4b€1@ € @ @ H $ЈhÄ 4b€1@ € @ @ H $ЈhÄ 4b€1@ € @ @ƒo†D"µz6›ÝÞáˆhðÍ`I°(94Àwhð-µ.‡†ÓÏø† ߘ¸94dÏø¶ ßžè94dÏøæ @ @ƒï‚('¡áô3¾@ƒï…ð²g|' ßA94dÏø~@ € ß—–'¡áô3¾+@ƒïNÓ²g|o ß#n Ù3¾C@ƒïdÏø>Aý=ëÑÖ “€¿(€ï$ЈhÄ 4b€ú»¾vfp1<ðý1@ € tbµ9'Ì»;$bS#~û3G±6Á*M8ì¸Òç÷¤&B SCþùc†b‹ksJ‚GÑgE#$77<÷ªu—f/EÙLUúå}g^(Ù¸®4íúo¹ôGܘ[Ñ,__§èÊÒ?¬é3î²Í&,ÿú8q{¢]úñ_1Úq­ÛÖ5@ @@ªÉ8mïr9!J?[{³þ}eDȉÈ45:ýE]Yš,Úf*’¸zÉÜ}ÙrÓ®”¶…üµˆ³øÍòíuŽ®üþƒ¡í:g­¿S@@b~Ì,Â'¯ßµm¹¦h ‘ü„ËYÅÙÊÆÅ<ñƒû¦ÄYìf›ÅBdr{žöí]ù-tü`h_í3´þmµþOƒ„c•&ym;p)âiÞ„ºèŽZì¶Çg¹)½þ»œqÙ{ûþ‹a ÙåQõGÎsõݵÂTáSÝ÷­¸„ZÔÈòRá½JD‡Ñ_ÔÖÅl÷:“’…@’×1o»ÑÛ}¶>ÄÌ8`¬»>…»bÒ*ê*ʸ??„M•¼ŠHµf~u}Ïö}AÅg•±‘Œ¦É¤7ù¸Ï1å®^àå{"8úùûJìP¢¨g9}¹ûŽu´¥¸m–wn˜ÆñõÛfêêêœ\P#¥9lÉŽ€ýö} òY^PK’ù4‹¬ÐMô—#ö+/p˜†V‚¡ï#ÿªMó=# ÷¾NÚг´mM÷u¥ÀñI&覺6䌅¿º;{ž½›QŽd{ŽYî³Û‚s‹ Á÷ˆ"—ãs+›o”`€ñ¹˜AøF‰öb!ýÒ¬r¬¼³öÐÿÙ»¸&Î7àO6ˆ ˆ,¡Šà@EE\(ZqϪU«Ö=qüµ¶n*•ºµN¨uUëh­bµµ*h·¸‹ˆTp!ù_Þ d\pÿ¾?íq¹»÷yß{“»ô£¶˜õ´ä(Íõquu@qâ9wbKåR#'w<¢8(OkÐtïÙ©Ôߪ¼mÈõÖœäßûøõÚ®èÄ"ÇjUlÿ ëÞ|LO%õvž×Û‰Sù²á/Ç^²?2Y¡ÒÏ®ü`#âï` 5å{ id±i1eI{6ylÑ CÛvV»ÃO%ZÖ×?ÍúâæŽ¥Å:ºé­×ÐCëxDþ2zClúml/Ò;¯;¥8ŽíÕQzÞJóžÞ—/(8$Ð Ð ¢À¼k^°´•û·žhË_>YW–ao?ÎÃ£ã—ø[‹¤IkZVunÚÈ q3zyé×M×,mmKxŽÞ{pŠ·Yæùà*ÕB®gY¼îÒÐé?EFwé^'4™¨fðî#ý 2ìóüìÏk¹·vr{e¯÷®˜~pl›á‘2É…3©9e:­üǽd¥ê³‰¼'…ÿè*¡çGõîÂóQWšðó`–‹˜6ZvþŸ@Óì›ëÚ{õÛûìðÄQÛ»ïi¸»ÿ–T}x†¿­Xö(fB½Fs¯<‹úfÈæ‘}]$$1RV5-òDû]IaíÄi{úUn»!•R¶-?¹ðw͘_žª§%«i&Mz‹8¾0 Ñžs—{„”ÈØf â.ެi#}qò…iº÷íTêïŸÕøÚ°Y1ÊŒ_:¥h&–ÅG–7‘=ˆæÝ"LW‰ú;Ï?=sŸvX-8™ñ¿’ø%íêŒ:ð‚+tWh“ô üLH¡ú¬ÓßøÎKÞâÄeä÷l'ž9þ½·9IWx‰É~¸-h]HÛñŒµv­œäuúêÑ:°±Ñ‘Ògû7œxܦyq.A¾ûϺ8Åaì» ­g­ÑÔ¼§†÷åKç½Ðz Ä!µìyÑ—ÜÇb¢óD³‰ŽEM Z‚Ÿÿ€GvÒîM ö­{Ô²Vôl£2í×GEËb‰IÔËÁÌ;øØ½`îÍ4ûé£G÷’ÉMÊ9ŠèºœR®¦dqïçù({rãÜ¥”ÌWß°ŠÌ*Wu³âɪŦÊѬÛ'WñEãz>žçý_Ä»¨É¹±þ„bÁ¼ùØîŠ*»u_¹×îdšTdQÎøîÞ•‡s+›Mý®!û&XlÛ`LPƒy½˳cÖÆ<èÓÃþõ‹€÷„ VŠ·»Æý[Únøå=MLHÏ¡RùKØ’j´ñ²,ß¹{ÝîåÇ,ô±åÒ$‰±Q¡šŽÇ[?•|­šK×i*F÷íIPlaÚtÌW塈K6X!,èŠÖõw³<Ó><Ç-VÓš; UûMjtà·'ô,ñFFV² Æ_¨ä&oO(X#×Ý¿ª¹bÁÈ­c`½¡11rºøgÜÃqJ¿ïu×ó=qõj`zàŸÌÇû~9ù¤y“b9Ék+R¦ç`Íñâ¾SÃÿò…y…€ –Ãòc¥-DÝTËå‰Z°ú!÷‘—½Ó%…°éûl6Çc”êMPFTŸm_™(œh(Q댈%Î&ŠØý@4’íRH1çŒDt€¤!Ñr"/Ý1Ÿ!úžü›ÔÑ“hKú7}Í6˜N4‰-¤Ubÿåªs’Hó³>|’²î_Vvá”U~–«ò>–xòvf/󜴘ù#¿™»õTjÞ)¡²mSDŸŸšòyã¹_ÄÚ 8œ¸Úç«T³ªÃƒÛ®¸ûQZä¼ÀÈyÜ'ŸV]8ÀÏ^û‹xvQ“y÷ìm¶`_ÉYõ»i™mË(«»\9{˱º»j’,‰­Ý½JÑá’ÝŽ¿—Eö¹é®E¹Jv¯ ”X;pO¬GDÒ,­m#¬%_ÓQD&ÿ¹SýiâY»Œ*âÂ5·~* mUݧ);õZ*[_ªBng0q¬æ*¡+9ÚÊÓßyò0¯TÛE9ÑŒKºq/ÌO¸p³r^Þ9- ƒPè³óõ„5²­gùW³óIdáTÖ–b¸wÀÔ„‡ÙTÚDµZ×Êä­—´{“a-,þÙõüÑÞMgž6©Ÿ±ö˜b#÷¾ýª˜“†,¾ScÀË—®º?$Ð`°‡,åå¸}‘÷!S6%Ú\5ú›­Ì¸Ôù1[3ž(‚hÛ˜{Íy5ñލ)[æv”)¿yú…%ÊÄŽ0š¨ÑçªÌ;‰¨ Q¢v,ó>DäOt‘¨´¶€ù±"_6@¾‚­äþ}ÅÞÈrq®.Ù€z;ÿ Ù3¼&’+;µQÅ6Ýj—Tÿ&WbW£¸„²o¬ì0î0×{ÛMéáãh™y|òWÓÎiÆeìLðݹjõæ]^y“u÷døÂ“á›í<»¾Cim_â`5r¹<Ïÿun¦þÇ«™—"IžÁA±ÑëA.‘¾/§ Þ’:Šà=w¯™Xš¾Þ¯pMÇãmŸJC[ÕÓ¤vºå9º?ÔØy8“×)ˆHÛ * é`j@O(P#Ërd¹¥ÉeÙ9¯*’g›<]‹·^¢þm­wm{œ¶ç×óOܯ®Ulé5øëŠz¾€!§FÀS„C »Ë¡9>Ú¾MÍú̽R÷WeÏÑ,Á•±¡ë¹l$x3Q_öЫ‰wDƒ‰¦ÅUek¸z›(²ŸjsÖ²úÕœ3¢‰lP™³’hKë×±ì<Ÿ,ö(Ǚܚ%߈α¼y›jÃVöeãÜkÙÆ‹ˆª¦àccRº²ûźθ°uþCŲäèõŠì„$‹Ö„t.)"éAzhÕè—Gò_„"±©ÖyìâÎc¹"_Þ¿µfÜדþN¿¿iöÞ¹íú8jÿ¶»¼bâXÕ‰}¼½!é¹¼†™"ȼ¾uѦË/D–UztªéÊÚäîéëOä5Í”÷8H¿r^q¥—óv4Ñwpdi[’ß¹Ó£àM÷f^àxŠ UíÜíˆîp-Ïu†Zì6æ™7%躈Pçéõ¿Îeùº‡©‹à¦¿Ðîí é Â9#þ|ªôs+E•óðòå Å:Qiw;vR/‘©ß%¶ýòðþî­1µÎU´²ÏÐ.å´’÷Ôâ)ü@ƒÁ^¨ôÿZ[2Ña¶ÐŒM± –ûŽ!šÇÒ_.Oí“w’ôPö§QY¢DìnÄÆŒ¹¤ü)Ñõ¼wKê¯ZèÈöåú“çû”ÏeÆlî—bz‰rD¹ Q–ÓsÙË 6?ä7¶æQ¶Aë#á’ý4ýÑ£|½HdlåÔ¬›§‡;„ߪףŒ±<ýßàn£#^–ôì8{Åèª2©òceNFÚs•¤Ô}3fŸek²ž<Í.’Ȳ®­8 ôP‚Yïû𵛕®Ò²w—Ê“ÿ>"W ¸‰Dbå;|êÕû™äjÁ·Ë“˜‘­þwè)Yù-økéç_¹9·è]ƒÎœ¦Ì}³Ö]l>ÆË<;iûØ!ãwf¨îò>#Zñ7:#•F…,ŠmR¯¸Xš9óÇXÅ®Vmý5hQ›|1›yK9ë?wÞÚö*dÓé÷N%G_H|­ÊÿiÀÈ¡As7:{“²£f­¹Ðò»ªÒ¤ßCV$êÜ^çÍßJ‡B;O¡Ãxz‚#kuv¡-ìı¡?±›ew§Ú¶:ú¿aõ*Vgpgû_V¥ÜÞ2mV*wòÄ †wtÕžªñžš‚=À@H Á`¹Ÿ^ïëÝìŽj¡ºZR˽¦—bã·U“¤•,X"Kl ‡r ›ÛÌLmÍS6ìûf«Ú^¹¯-Nå’Õœ\¹A®bÿÔ%rŸÒUwßûQ5ž]œège±ýÜ5?6üíAô°åcÖûÏ¿’±³çg®3j:¾¸|:ë–&õ:…zZبá—ÞÇ¥$qCë5Ø^>ýhtšÿð€'K£žH#‡´ì|fÑò6…ÌÄ¥ŽÇ“¡¿Ü‘‡ÔvÝZ§š‹¥ìáõ§“¸g…]·±-K+R c{/î=6AJ·6¨¸ÏÛo⦱O.èÞ%çØùÏ*ò(ëRmE»^5õ׺ÁqÒØo«8.­l—~ñ?ÅÏ7×þqyo73‹¯W‡n÷úwFüôúök+zZ¤^¸þ@±ŸsÏ5¡íJ6D›?æogvÔß’ë¦VØpôž»‚µ6_Óé¨èO%·KÎ!‰KñôÏ©xƒ6«:üû¦¡#_ÈNŒ«æ´ÂË>#þšiMg:uGûŒ ýÇ×X#WÁL¡æ<=ÿ¼hó³b›[:…Wô0ºyñar0»×g:«hX½,}vsZµäîýØë¤¸>pdG]s-xOMž` ä `0–Ú¾ä^‰Ùht¾‹ˆn5R[£õc{¾æš/Lb½7ñ©V®šR¢uûÜWœŠ,QVg§zTÊ.XTÊ`—-jK Ÿ4Qñ†s¯8åûE[ÄÇÇÝ# ŸÎ}&LŸØ¹‚â9`RaôŽm÷ú·â`ÒØ#&MmÝ=£]ÖÏ7›Ú};õÜ…ä—|ó@ `î=ùÀ çeÛž>­¸ÀÞ¬´W³^ýÇhêÀú²¸ô¡‹»}9vëågY·i1ý»ä^Ä(Ò1ßUdéóýsgÍÙ°÷dÂÅ ®ÖµZ÷›0}B'ö¶kê1dçEe?ÌX¹ãХ˸7}Ÿ¦Ý†Mê]«„¡+óÇ\Ì‹kÉÔ"mIžs§Okó6^E*óÔW3$ÞþÙž?h#·~¿|1jø´_ãîg$Ýwm=)|V“þMצQÖÓ—šWòtCï`<…òôþó¢E¹ûgFŽ›´*Š{G´öl8kÙÍKèûühP½Ì«õëé¶dîMŲe«Mõ|"å=5z €a@ƒÁгIÉ{Øï§ld³ r½`7¸ˆeÓ$ÂU+O³Wù‚žÎ†Ÿ9å4†ŠÉ`ãÍʱê‡ìObY¯æ±ÜT¸›$­ÕJ¢¿ØBev%âWì–|v…>8—ÁqòÁ<‰m}-Ù;h‰öGMËvYp Ë‚<ëFüykDîŸß•ëØõ5›ŽÕÇòòýIbŸþóÃûÏ×¹¿ÈÂ+pË¥À-êëôìÂÿù‰Ñ¾«KYë|YUê:}{×é:6qj2z%÷O×îù«À1«>;Q>ûõßš1ó´$éo%-Eðœ;-G žÖæmº·|*yCâíŸò>ywÐhCEеG¬?1b½Úªö©ò5:ëÀÓy 9qè`ú ÕßøÏ‹2™™ÇW "¿Z å1í]‹øëÅ1«1'Q>GëC‡å?5<‡C ã>Ïb§œ]ùÇ}xîÁÒÍ+l"›Æ¥˜÷ìÅ.Œa— ƲÛÏI‰fªXè»Ds/+¡,˜PÕšNÚŽéÌ"‰'ÚÉ2þ2,‰ïÆFÐ;²›{pým9ŠMÑ®Dth»Ì×&ÃG/óú®Ýw¨R¾Çì=lº÷0$€h¢ ËG;±¹‹Ù?umØ¢ÕìÀ v³çŠl̘MóRŒR·+\F,Á g Ê;j:õÒ¶¥ »E´? ã3¢šl†ÆS¶žË¼Ÿ³ñæ–XOeƒë‹Ø˜úoì¢Ã¾… à}'½µyìOÿûâ·NeðqQ˜÷°éÞÃ>~H A öl®ó<¢]lÞ3±ñÚ:D#Øø®r®–›ñû!•Ëlûm”Þ…ܵ`··dž·9-Ù=ïtݤ!ûí•ïÙ¯®Ä±};³_Nñ$«ºGÇ:–=»7Èzvÿ!ìîÑ…‹à½fT¦ïÞg}ßu¤÷°éÞÃ>~H A8gö{Ú¦}½æÄfë™æu0ïŸFìÍê,Øñò‘±Ô6Ò°cß«mËyìŸ:cö³,úèœß Ÿ$Ð  4€H áqð]§ôm;EYu‰¼»- Ø»'¿Ìë[æ­¹d×qÌ`ßâ…¼d¡Ž™±+À¶Ã~9YtŒ¸÷G3Þfza¿io¢'=¦f»½çý³ð ©`¾môÿ©Ü%_K~ôÍïhø”‰-ÜlmKK™ö ÔoÕó3 Ç-M®QfÀ@ßâEt‹²7qÌ·_DÑ{z‚–v{¢z÷ô7‚¶Gó·$šŠhø”Y·Ø’øð]¡…\&#qfü¯;5ïES8/ÞÀ1ß~oÀ»ï ÚÚíÝGõÐßZÕhI4#=$Ðð)ËûÝ®eòúúN}Už}>ÜcÝÐ1Ë£Ÿ9Ô0gÂîÅÎ<|nøåÇdSµÛkÂFÖ*.&’%¯­çÔÿ8‘Ûw·TØ4wó«é2s׆ýB–Ïéíe©ü&>+ù`XȬ•;¢/Üɽð–ððk?pâÔQ-\MÙòäuõúqåzÍ:š9¾[ðsgów^(ƒ<=ÌÍh˜¤ÙŸ"ÚZ?ß2}Êü_#â’žIJx6ê:fÖÌA¾6båqrã¿á>fÌ‚gR²Më÷ž6¿Å”E5Ý¿9¯qÌâ­"viÓÄÁk÷'<%˲MΘ]OñsŸ¯oÝ%׆UâB]EèÚÅFcHP&ü,è ‰;¾þf©b%ÒÖxNkæ¹É•¼gÜPóê™qÆŠCðOtÜnZæèëKÕTKXÏ®þ1gʼ K|"'sgŸV}ÇϘøeÖ{õŸ8Cú¿!§^$ÊJúsJPЊ=çR³ÍÊ4èóò¹ýªÓrjò·kÞGÍ´µä¶ìùŽ Ë8³!äû…›£Î&sϲbî{Ž›3c ¯- )'íHØ”;bÎßãY»ù6ï5vúÄΞÊ„$x @ƒ6—‰æ³;(+íÏœ¨6Ñ@¢îoýg®·]b?µ­ùÿï3±‰… [HÙû]Óà8“²Å%ô\z/6¬÷tÂê—°ÛnŸ•¤ÇÒÏoÝÚ²ÚõÕŸ[‹Ä¦Å”IpÒÆžM[4èжÕîðSI‡–õõO³¾¸¹ciqæµkþ®(ÀÞÃËæñ•«×¢×þ-"ôԞឦŠr-•å¦FNîy …[²t­[ÝæüÑø4Å^.Õ}]ìkØe][Òªæ¨ÃYÜ;¾W“Ö)‡£.ì_1ÔïÜËøƒ£9Üëø÷Mø|òßO=*8™§Ü|qçȪ9Ÿ]úѵ®¿WrLÞcj¶BNòï}üzmÄ-Š«U±ý/¬{ó=6²×è#ª«Ž"ø"/äYà9¾Þf¹º:@s¢6ïiµ)@÷*@»åÇ×—„×T1ßaf@íÉDz¹E[w¯RñWOþ1½ëžèŸNíSYÌ×åxû¿Ô SŸy>¤yûã™åÜíÍSXu8l@Ôb—ëâ ð-Gd®µ%óŽìË™ìç7+ž[t©ßÎ×êFdÄÁCê¹}üHH-ËçqA üf]ávw¨R·–Mνs'Žÿ>­ËžØ°ø¿—5ÖZ,|‚@ƒ†Dy×pŸÁ£Ù¿õìÇ­ÞV$ÏYF˽ò xÃyí[+Þw#å XÚÄÁǧÕ6ŠŸ^«j÷F{1,ÌYü@÷çû‡T XuRw¬=½øóF–$–¼½g;ñÌñï½ÍIš¸2ÀsHLöÃmAëBÚ~k¾¡ÿ–ñT}x†¿­Xö(fB½Fs¯<‹úfÈæ‘}]$¹å¦DÆ6[wqdMë©Hvqr¥ê³‰¼'…ÿè*¡—g§nºfik[ÂsôÞƒS¼Í2ÏW©r=ûÈâu—†N¯fªä‰ö»’ÂÚ9ˆÓöô«ÜvC*¥l[~ráï+ÿq/™÷˜š2ã—N`Ù³I£eñåMd"†y·ËÝàå¥_u…quèt­EèÙåUä…; <Ç×Û,Çh-æ;­£…w®´[>Ò$¾¾d%¸¦Ò„Ÿ³ìٴѲóÿz˜fß\×Þ«ßÞg‡'ŽÚÞ=¼ ߉ãk¨Q/õAFÖ¥ÛíþN^ÚÒ^ü4.¤¾ïç)cÇ”Ÿ¯v˜\ÉDP#K;iiÉôêÛd]Y:„eÏÎÃ£ã—ø[‹¤IkZVunÚÈõºûóZ.{&—ѱWøZp5K?8¶ÍðH™ä™Ԝ²Nʤ~xÓ@C^‡Ô²çqD_Ù'šMtŒýà¢%Doç{¬xm?F¨Nùráƒá->9>ß õQ|áíÑ®CÙ øD­Æw/Ͻ—›øvmhµê·§ôèúígr²|ÝýêŒî_Õ\±`äÖ1°ÞИ9]ü3.µû³•‡s¸µÆÍ¦~×}C,¶m0&¨Á¼^‡åÙ1kcôéaÿú åÇ,ô±åÞ£%Æô2PfÞÁÇîs?ûé£G÷’ÉMÊ9ŠèºœR®¦dqÚ¦Þ‚Z9(^àí÷oi»á—Gô41!=‡JñÖ<çÞ¡= ŠÓ¦c¾RÔ˜Ä%¬tŰ0´ Ñ ˆ¼@gçø¼ÍRLwV¤ý´>U•·%6ÿÐfNò^¾¾ÔZhMsîF¬?¡X0o>¶»‡âD»u_¹×îdšTdQÎÌ´šÁ'NGCëiØ©©>nBS{EpV5[G‰.ÿuòÑÄJ¥‹ú²¿ì¤Ý›.(ì[÷¨e­xî•i?¸Ž8*ZûKLZ//å×A·7N ®0â‹Æõ|<Ïû7¾ˆƒ€hP“Ãòc¥-DÝTË剸—å–C?ä>¾³—M.ã aÒ÷Ù6lŽÇ(Õ+ªŒ¨>Û¾2Qßå¶Ìf+Ȫ¨Š cÞÊùjÖD͉¦yI‰jªÖsÜØÔ‘T¢Vì˜^D¡,¼"îu°Ñ VÐÕ{Ð.¢l+º™ê W‰¦ýEô„MJá5ƒÈ][AØ(»®ØàS`áVΆeK;åW.¥*8™±‰•bÍS®gåÈÕ>¿Ùz–WÍéY8•µ¥îÉ’šœxW9ʱº{îT±µ»W):œB²Ûñ÷²È>71ñ¬]FW6Iй™1óG~3wë©Ô¼¿$,ËÉ÷ËÂå*©fH¬¸þûˆ{VeåßJ«¬Ôk©¯jìl¦ Ë±š«„®ä £‘ç©…ágÁ°ã YtœÖ‡ÙÂè‚5‚šÌ;§ùú’*6¸¦™wÏÞf ö•rO·i™mËYgCåHb 8BñÊUU‹­ËzØÒQ.â´¤TZØ4¿¬û—•ïZ)«ü,Wå},ñämÑWÃۮ¸ûQZä¼ÀÈyÜJ'ŸV]8ÀÏ9äBg5YzJ,­ü"ïC¦lJ´¹jô56+:-s©óc¶fgïsND7‰Ž°Dü*›)D4‹mÃåÓ6Dçˆ~g»sõ]‰ê²=mP{ug|5O”ÍÆNRµ3,àl¶ìÅŠøƒ•uB[AFzc++¬Qფú^ZqqÓ«5FbQÞ5ùÈrd¹ \–­L6Ej£grõ|AþjJ±H’çh&–¦º¿Qɾ±²cÀ¸ÃÜÇKçöASzø8ZfŸüÕ´sÚâ7z]°Žxù©,Ïyí£»°Zz =~š…÷´ª5“Lª/.`#hÅÛ— ¯©üÕ±är-± ˆYGCz‘øu˜rù«C‰ÄoâËN‘ÄXù>fT±M·Ú%Õ‡ä%v5ŠKŒËØ™à»sÕêÍ»"½ò 'ëîÉð…'Ã7Úyv}‡Ò˜ÂJH AÍ]6*ÌñÑöjîÔg¿*{Ž&ògã͈æEm&êËz5¨=ÍåþìG´%¾Ç‰üˆÖ² F-` ÜÛ°CaIêJ¢’ª,6œ¥Ô¹ÇLaãÊÙÀ°úw€ºpfÙ³)Qö`©<—F?cCãÿhôœ/6MñçS¥Ÿ[)^Ts^¾œ¡X'*íî\ÖÞ•}Os÷ôõ'òšf,#¥_9Ï>—óv4t€M–½^‘ˆ$`ÑšÎ%E$½q#¨ÈëalçnGt‡‹9>é¹¼–¹"àÌ›ÇT Œ7ù›<¾ŽÓjg"1Q¾>Iy’£üî+óÖ©[o4HS—š¼}éºÀú™8Vub¯©÷/p§»;fæõ­‹6]~!²¬ÜÔÚà˜µ7TY:²Ø #¤_ºø §‘¥";•e\¿ÊÞ_DåíÞÀ5{&¥+;°f´®3.l¿¥–M$6Õ:]Üy,ÌËû£ÖŒûzÒßé÷7ÍÞ;·]GÜI$Рæ…j¡„ÞÍ’‰³.‹mÈÄ,Ç&%s©gŸ¼ó’ƒT­?K 9 lGeÞ°‘¨Qc6;â_ƒC]À&g“*¥Öï.i&6 Ã-¸±u.¹·P¿5—Џp±Á'êìÜ9Q=B[؉3bC:ÅVywªmçh4ÄßèDŒT²(¶EH½âbiJäÌc[µ ô/¡sM$+ûwêÕû™äj&“*?àæd¤=—qûR÷͘}–­Ézò4Û° óÓBs #‡ÍÝèìMÊŽšµæBËïªZH“~Y‘˜»_&šEEäúð¿0)öÓjkbUÁIL×dtë_O×ô³–&ý²,¡Aji·|$- Ú—t2rnÑ»9M™ûf­»Ø|Œ—yvÒö±CÆïÌ QÝ¥Mû~â´7TñOÍÉÙs£»-jRBôøXèÜãÊÝ;úÚdš§“97ëæEgãéáÎá·êõ(c,Oÿ7¸Û舗%=;Î^ÜæØÿ„J0ë½cÿ¾Vb³ÒUZöîRyòßGäÚGéáS…Ôä~¿¯w³;ª…êj‰²5).OJ!º­š$­Ä½vÙ©m£Äm`F̦M§©.[4aó’DzÁiý¸-Ëðm£î®j¡’Úʺ·/LlðÉ2ú¬Øæ–Ná=Œn^¼Å>Œ: ˜Ýë3##ñ׫C·ûý;#~z}ûµ=-R/\ xܹçšÐv¥t'wÆö^®F” ¥Û TÜçí÷íÌŽÞÇeqCë5Ø^>ýhtšÿð€'K£žH#‡´ì|fѺ©•tLÇ1'þ±±§KÞ/¥Íªÿ¾ièÀȲãª9­ð²Ïˆ¿fZÓ™NÝaßó‹K5üRo fiÑ…g.òz…ø}eÞ¦v(ð±uV}>¢}±èO(q~·=5]Ÿÿ¯tGëm1IÛÇrþ µ´[O—|Á¸òõ¥tÁ4v¼jê¯uƒã¤±ßVq\ZÙ.ýâŠß1®ýãò¾µÌŸÚå´7T9ÇTýµn«üjCâf±1ÀñJå¹Ý“ž+Ö8öW´³àú¶Nš'‰6©0lù˜õþó¯dììù™ëŒšŽ/.ŸNxÊ­¯×)ÔÓÆ…<ž ýåŽ<¤¶ëÖ:Õ\,e¯Ÿ8ÄQ»nc[ùðáB j\Xîø’{©c£ÑæyM`3©­Ñúa<߀X÷Cˆ|‰V³™ÓWXVÎþíT]¨‹ ß7¤yÿ”k,ð*plðɲ¨3wÿÌÈq“VEqOkÏ–³–ýм„â `ê1dçEe?ÌX¹ãХ˸ª.>M» šÔ»V }*Å¥¿]Üí˱[/?˺•ð€ŠyÞ±-µÿw+&݉=bÒdÐÖÝ3Úeý|³ù¨Ý·SÏ]H~i@ÏLS-oFný~;øbÔði¿ÆÝÏHºïÚzRø¬&;ý›®M£¬§/sL*paÜÓF–ýøüEðíbHäúð¿}­û´–î´6r‘eà´-§RÓoÜ-ßevÔœ†;ê+ȩ̀™2Í# Ý4´/é!²ôùþÀ¹Š3ƒælØ{2ábY¸ÔjÝoÂô ¿bð‰ÓÕP%ôáNF#å4<—®«wÕØ6zҪϕ»Ï\úª…ãíä¢â ç=^qÊ÷‹¶ˆ»ÇUÙ§sŸ Ó'v®À½íyO>pÂ9$dÙöƒ§E+n=cVÚ«Y¯þã‚G4zWjø˜!5ÅÙ~{Øï§l$¤ö÷’Ø“(– ý†«Vžf)©2—MW]ÕWN5ÿÁÕˆ³…—lþô×ì8³Ù5 |å©zñ6[9aîTÞmU ÔÞÊ~/Æ’èo,6øØt<¨þµl¾?¹ÞUnô9yž{þZÔYš,_ªy$™ÌÌã«‘_-ÐVŒ‰S“Ñ+¹†ñŠÈÂ+pË¥À-j«º,8Ð%o #þ¼5âõ_šÇ1«>;Q>[-~cjÛÔ±þĈõj«Ú§Ê×äþQ–/ "$¼»¨)ÀY0å;¾¼OÞ2ò7‹ŽS ï´r­ô¿'ÿ·QmU½DùÝÇä R³Ý´D¥·/iÙ^£¦šDV•ºNßÞuº–‡xcVyël(Þ#ôÈ} ®¿¿ûB-1è’j©²–'Nþm;ƒ–ì´DKq܉õé??¼ÿ|m¨ 5bv-Ý_,ËÌÒÍl÷üG–=›÷ìÅ.Œai%·²ñ©:H a7f¾ÆFyØíí|ÙÈwKv+º#jãĹyê}ÕE„šŒÙý=®±¤ÿ$›b‘D´,ï6ÎD5Xº¿]zèŶB”ÁnÁ1Z£ Cb€OhÈ« ›¥Ð‰æ.V ÁæjÃÖ˜°¹ ¾,­OT‘ÝWŽÍÃSŒR·3¬ 6f|‡Ý]® ¾ÎòZbS•­—jãDÞìÆsš¸d}»ˆr3åM;mck”ù.—d¯b¹²”U°2K£•ë—³'A¾‚6|ª@ƒ†öl®ó<ös$ÊkÊ%,Á~»D™>z°ô6F{™­ñ!NÔ[u£h^æDØO±lWÝ|ÚŒ o#jªÚæ VâV6ùîÞÊ¥û‹ˆ¦±<þ.›eÑP•@gª¶ña÷rb7߸È.ml­öÃ(ù *f@lð©B Ú8³ûÄi˙ˉݪYçœN¢ƒkªçaC4ŸýÓÅ‚ý bžylÚ6³yÌù¦2kε¨Äb "¾Ø^Ñ1w>l8­BCÁ' 4€H @ h@€@$Ðï=C~ÕÞ$Ð  4€H ßGrü0*Àû 4€H @ h@€@$ÐPdD¢×?;þ)܉ïS«/(! 4€H @ h@€@$Ð  4€H @ h@€@$Ð  4€H @ h@€@$Ð  4€H @ h@€@$Ð  4€H @ h@€@$ÐаÞH xpÙ¤!Éèûæ àý‡€_Á’Ñw>Žû† ðžC `¡Éè{’†~ a¼Ï@Êðdô½JC?аÞ[H 0$}ÓÐ4l€÷h€¢ô¦¡hØïha>л[| a¼‡@¦+}ÏÇq?аÞ7H  B3ý ÒÐ4l€÷ h€ROF? 4ô àý à”Éè—†~ a¼'@Êš†~ a¼@¼-9·WÖ)3ä¤bѼņ«{z¹HTeìn^²Ý¾ªòÓõScÜ :\æõ-óÖ\²ë8f°oqQúŽÆ¶¢‰¬ºDÞÝPŒH–·dè࿟N‘Ù´Ýùß®âù¶),¾ häkg^oäDdì °í°_N#îýÑ̃ [8-¼Ä»¾ßЛ¯5¼@Ã' wvMogxþÅ?c&G¶Y×¢oöÏÏ,´4¹F™}‹s™¸ØÂÁÍÖö±Ä±”™XñxvÂÏýGo9O$©Ü+¨í*•ÌE”o›Â⋊Fþvæõ~œÁa ¦­“¿ko¾Öð>@ ðN¤m6kĹ9¾–ÜÿEü¯;“ÕWX·Ø’øPíoéÃil¡Æ73¿èÌÞÉÍómSH¼1@‘Ðhg^ïʼn¶`Ú:ù;öj ï$Ðoƒ›é½›™ ó†­øï·ž&:¶ÊJ>2kåŽè ÷_r/×%<üÚœ8uT WIšîßœWnuz˜›Ñ0I³?lËîû­ýÊÇíØ27ÈÅhùmN=ô•‘Æù³«Ì™2oÃ_ÇŸÈÉÜÙ§Ußñ3&~YÁR1’'¿eú”ù¿FÄ%=%’”ðlÔu̬™ƒ|mÄ$åáÕ̵0e»Ê’××wê{Œ¨ò싾ácÆ,Øq&%ÛÔ¹~ï©aóûW±R„‘“v$lJÈŠ1çï½ Y»ù6ï5vúÄΞ:† Z)ãTœþSiÊWbÅ”EZÚ9¢mqýÉ;…ò•RTëÒ¦‰#‚×îOxJ–e› œ1»q[¾þ‚Fp÷ˆhk­·ñ= LUuÌßÉíòÞª³§ÛëåÎnþ|NTkiâQ®ìˆÒ‹}Ë:Ã}Jéò×Í­­l^§Ykqþ ®¬‰Ñ}„ݱ-C*8NäöÝÁ-¶ÍÝ|àjºÌܵa¿åsz{åv!YÆ™ !ß/Üu6™{soÜsÜœ}mñEÏ;ƒàm{~;³Ê¸1‘/㦌ÚöÕ œe^[ѱvàßéŠÍí=¼l_¹z-zÝøèß"BOíêäZ×ß+9&^1ø&v©îëb_ÃΈԆ½Œì}|SN¸ôˆûCâZ£–S‰ZÆ"Ê?;åù™™µ'ËæmݽJeÄ_=ùÇô®{¢:µoLeñµ%­jŽ:œÅ½_{5iír8êÂþCýνŒ?8ÊClÎ_-ö ÷äÒG±‰…òDʾ ŸOþû©G'ó”›/îY50 糫«Š¿ˆ jà7ë ·©C•ºµlrî;qü÷i]öĆÅÿ5¸¬–é⯔±!ñäÏ6õgf@#è-ñÒZÛ™K,y*’§Ã ®÷©%ù÷>~½¶+:ȱZÛÿº7ßc#{½Þ´w!1ó÷Ÿ>::¹:¾y[æ›/Kþ¹áÅ­Ýsg°2ÿ͹±éŒb»/G4´ÉSœH³Öº¬£ûJ§(?I&mìÙä±EƒmÛYí?•thY_ÿ4ë‹›;–æRdùã#“ýüfÅs›¹Ôoçku#2âàŠ!uŽÜ>~$¤VA¿Â‚BB  ‚ûR|ÊÞîÄI™´Ä—Ë‚×W™xöùÞ1AQmÖ4·Í·…4iCÿ,o¨<>úð [±ìQÌ„zæ^yõÍm"Wþã^²RõY‰DÞ“Âÿ t•(_ïoå÷Sdt·‘îuB¹Œ¶fðî#ýØPUzÞR~Ì2?ÓFËÎÿèaš}s]{¯~{Ÿž8j{÷ð*¿nºfik[ÂsôÞƒS¼Í2ÏW©r=ûÈâu—†N¯æØ‰'¾ZlîÙ×EB#å ZZä‰ö»’ÂÚ9ˆÓöô«ÜvC*¥l[~|a€ß…Ÿ×rÙ3¹ŒŽ½²Àׂ{¦¦Ûfx¤LráLjNY§ü> U).—á‹'ßÅsú‹û§§£Œ·¬ô•xráïZÚ™èå%½É×(+ÅÉŒ_:eÏ&–ÅG–7‘=ˆæÝ",wž´u—g§ò4¾g×tÚ;¹ž 8Ý Fö.³aÁ­×ùoνý¿œPìáÜs¨¯U¾fÖVëqz Þ« @~Ïvâ™ãß{›“4qe€ç˜ì‡Û‚Ö…´_Á8ëÊÒ!,{v¿ÄßZ$MZӲ€¨sÓFnphˆ2¹wÍðÖÉÉ´Òˆe£–û-JJ]7löðs³<ò<ž“¼wåánÁ¸ÙÔﲯiŶ Æ5˜×ë°<;fm̃> EÎ݈õìܼùØîŠôÅØ­ûʽv'Ó¤"‹rf¦Õ‚Ý æ‚Í~úèѽäGr“rŽ"º.§”«)Y\ÂÈ|þZô°ýÑÅ{BP+Å›’]ãþ-m7üòˆž&&¤ç44µdc„·7N ®0â‹Æõ|<Ïû7þÍWJG³hÝÔ:<1ðׂŸ‰K'C¹#©íºµN5KÙÃë'N'q™³]·±-Kk¢°•ªTtm؛ݷÒõ—ÀK£7viø¥ÞŠLí ¬̪ÿ¾ièÀȲãª9­ð²Ïˆ¿fZÓ™NÝaÓUÄ¥xÐÒ=¾Ù‘o—zyæ4¶ÿ¤a§Û¢úA¡S¯Ý¹À,ëv#ìt~ìÑr²zºHø`ôY±Í-Â+zݼx‹MŒv0»×gŠdRaØò1ëýç_ÉØÙó3×5_\>ð”[_¯S¨'²çw 4À–CT‡è$[nA´‡(wÜK™J² ~"cØÑ¶]bW­iL¤ø¹¢»¤¸ FF´„hûR™³“hAÞ 7DlßnîÌ»†Î;"fê1dçEe?ÌX¹ãХˈ,]|šv4©w­Š.$.ýEèân_ŽÝzùYÖ­„dZ—s‘¥Ï÷ÎUœ4gÃÞ“ 3ÈÂ¥Vë~¦Oè¤ø’Ñ;¶ÝëÿÝŠƒIwb˜4´u÷ŒvY?ßl>j÷íÔs’_Ê ˆ¯0÷ž|à„sHȲíO‹Vܬ´W³^ýÇhê í…¬TQ¶a5‚–v6©ÀS‘ö‚+’‘[¿ß¾5|Ú¯q÷3’î»¶ž>«ÉNÿ¦kÓ(ëéËÞ4»G1/n—TA_Ȧ3 H™Vì3Ògêÿ/äv_o`£ûk]O ¾#XÔ™»fä¸I«¢¸ìÙÚ³eà¬e?4/¡üŒ *ÞpîÑã§|¿hëøø¸{\óéÜgÂô‰+àúÁw 4À[ôQ$K£ æ9K[“‰¨%¯D¶DŽªïlˆF³…ÊDý‰*ilPH†ÄºH\ÇÉk¬6*x(+Pss§&£Wrÿ´Ldá¸åRàµUóLi0¯½ä®|IÞl:Ì7íAdU©ëôí]§k)Á´l—º,ȳnÄŸ·F¨ýÉo-´…DfÕg'Êg«mâÓ~xÿùºŽ ©P•âG@q¯èmZ@³IÂWyAEhÛÔ±þĈõj«Ú§Ê×äþQîÁ»‹ýýG['WW„§ûÕÌ —^ùoÿœ—–'¦!GÉÌ<¾ZùÕG”ûÚúZ²wžªÂ[†àíFtލ`—ÄSþßy#6¬î¡já¢Ú6($Cbø˜dÝümüÅý¾Åu'Œ¬^QßÂÞ3H Þ¢{l„xÑ·:¶árÓ¢ìŠkŽK‚G±Y5‰Î«6sck°1àÆjÓ'Ú±e¥AìßfVœæüŠ«DSˆþ"zBĽ–·b³>*°‡ä,!žOÇþ,AÔ•h&;‚!1Ó[S6ɤ>Ñ16FÁƳ¹Í²ÙÊ0¢*ªãç°?W¨J´&jN4ȳ§@ô¿»×uëN*›–\yâ’¾e&N…?¼—pÞO–F²Ìõ+"g ®ÕVýÖ2—q>fkƳ,sQ]–•¦±G«Ùk{úú°©Ï—ز+‘ËÚ5a)gÜz±Aå?ØäìS,©]Â2]åCÜîQ,‹=Ç ‰AEö°Zyó).Ôωž²8o! `™½rfHÑ,¶À5š àw¶;mYþÆ(òÌ7î¤r¯–ÖÛY6¹–௠x/!x[¤DËX:øœe‡k4í¯J:£‰üÙHí¢¹,sÝF´’]q¨Ì)ÃY~¬é'¢nìšEN0; Ö0³ìÙ” îz°ä•Ë•Ÿ±¼™;ò&6¡™K÷™±ã„°ìöªa1è¯Èf¢¾ª©Ò\"Þž 3sö#ÚÀRêãDÍX­eÛŒfA;`v¨3H àm±ý""«p¿YÄs-ÓÓჀàm‘³Kú¸$uÑ:¢á,yÍ•Lt˜-pécC¶ fÓæ±¹l²æ ä.Ñ ¶Ð\€Ñ^–ÎZ°¬ú[™Í~Ê–ûWNµcŠaÇT‘ Õ‹P–@›â¢ÜE9J½‘M,iÌú P[€¢‡à-Mf™â#v5á¶F)÷g媫­´&*Å2×ÛDYEÃ]Õ‚úýO¨-ǰ Oiìhà ‰ááòu;µ ””*G¾²´^yw 6Q{,‘Ÿaa¼1H Þ..œÃ.ï;A´å…Ò¼hÍS øÁ6CÉ5ÔÝ`‘¥lŠövgºãDÓ W:õŠˆu¬W@äK´šÍœ¾Âëpöo'Qa" P@¼u½Ø,Ž l<5GµÒEµpš¥žÊ„2]5q¢œjJCá9ª.¨´•è2»¹žµ*¡ç"ìÌn<~V¤Ñb¶ð’ÍŸþšg6»Ùn8 ïh€·Î”]MèÏn@‘Ë­‰a™b,Q=–ÈÎT=È2Ñܬñ¾Ž ø áLTƒe·ûˆ.²Ë“ˆ†°ŸE¬«6AYy«T–°*)£åÁŠðºÆF Øî|ÙŒŽ–ì!G žIðÆ xüØøîvµ5Flº‚/KdëUdÉëöPO6æJ,ÙUj@äÍî=çB‚­b¹²”ݤ2K£•ë—³{B+ eáeÙðöo»o †T„— +ñ»^64~%úĆí1ü ïh€wAÌnë¶Ku3f%–ËþÀ†]/³5>ìf½ÙO–p¾`w©ÛÊ&?(ÄÓׇÝV9ˆÝ|ã"»˜¯µÚo”l#úŽe«GØ\íD?³›‡¤²;lŒ7 ÞŠðâòøìöyÛUw1c·õGÔ´ µ("H Þ0‰ê'ýò)«íÆNì^Ë+uÊ‚ýF`¾ßÍ>˜÷ÏÚ“òm T)ïø·º.쟺ì_.Þˆ¯"šÛW×Û†ýâ|Gxw@€@$Ð  4€H @  bÈ/äÀ' 4€H @ hø¤ÉårþÔ  4€H @ h@€@$Ð  4€H @ h@€@$Ð ˜H$*ð6r¹¼¨Ãx«@ƒ`\lH ðQB Q°ÃÏð@ $4‡Fö $ÐPp†çÐȞ࣠ÅÙ3|L@Û…ì>2H ¡°pSø¤ †" +‡Æð3||@CÑÐÌ¡‘=ÀG 4õÙ3|¬@CQRæÐÈžà#†Š²gø¸! tQÂÝÜ ŒÇ||@€@$Ð ~c0÷õ“…™ð5$Ð  4|\rn¯¬SfÈIÍŒK”¯Ýü«‘“Çu©RLüöãRɼ¾eÞšKvÇ ö-þ‘O÷ÉØ`Ûa¿œ,:FÜû£Y±wŽ!Òw4¶íMdÕ%òî¶€bZ×€gV–·dè࿟N‘Ù´Ýùß®%ÞJwÏw¾púàMA Ÿˆì‡×ÿÝ2íß-¿ÅŒý©‘Í;Ê¡ŸŸY8&hir2ú—¼›ް섟ûÞržHR¹WPÿÚU*™äáÓƒ>VuV_ù³½-[”e>ü/fõwæ}IWþïçaqßz¿‹˜^Äÿº3ùí)—ÉH,þ@ó—:øøXê+}x#-Ôøfæ÷ òIñci øX!€•Iq»R¥J¼ú£TéÓÂ.ï¨öãD#.?ÿÖ£¸bµ,ã̆ïnŽ:›ü‚¨˜{ãžãæÌèk+&YòúúN}Už}>ÜcÝÐ1Ë£Ÿ9Ô0gÂîÅÎ<|nøåÇdSµÛkÂFÖ*®ÑÎJ>2kåŽè ÷_r¯±%<üÚœ8uT WIšîßœWnuz˜›Ñ0I³?D´-®;M²äµõœú'rûîà– [ƒæn>p5]fîÚ°_Èò9½½,E²äuõúqa{Í:š9¾[ð¯¹×NWÞX_l¦Ê£ËŸ]ýcΔyþ:–øDNæÎ>­úŽŸ1ñË –"ým¥ÚûÒ¦‰#‚×îOxJ–e› œ1»žâž4¯îH”ynr%ï7íyõÌ8åÇ—ü3t/ÐD¼‘?ß2}Êü_#â’žIJx6ê:fÖÌA¾~)‘“v$lJÈŠ1çïq±ˆ¬Ý|›÷;}bgO íÉžžâФ¾†4,ÌzO®þ3›ŸjÖ7ÈÅhùmN=ô•]¶žî§·ßÚžohPÉ!ªC¤eæ(Qy¢¯ˆÆq¯ªz·,IÔ‚h2÷:®¶ò2Ñ|¢½D·ØŸæDµ‰uç^ùо†067QæLöó›Ï-ºÔoçku#2âàŠ!uŽÜ>~$¤–¥‰… Û:eïwMƒãLÊ—Ðs齨°Þ_Ð «_Ân»}V’?H?¿utkËj×WnÍ¥™×Vt¬øw:·›ØÞÃËæñ•«×¢×þ-"ôÔ®N®uý½’câCsb—ê¾.ö5ìŒôÇ ºØ´˜2ÙMÚØ³Éc‹Ú¶³Ú~*éв¾þiÖ7w,mb© ;5rr÷È)Š}”»ê‹mÏpOSz~ff@íÉDz¹ lݽJeÄ_=ùÇô®{¢:µoLeS¾8s’ïã×kû#nQäX­Šíaݛﱑ :1b­Á l"Ò_ñµ%­jŽ:œÅ½žy5iír8êÂþCýνŒ?8Ê€o%žÇ5ð›u… Ì¡JÝZ69÷Î8þû´.{bÃâÿ\Vsÿ,½ÅM} ³þ…žY#{Ÿß”S'.)ö¸Ö¨åT¢–ƒqO÷ÓÙo…´g €@ƒ®M#ú(–ÈF÷fˆ~eÿ5`kVæÝæQ4û·žh‘ÕŒà5yvFÂ%£ü§øÃ¼QwoE’ueé–48Ž_âo-’&­iYa@Ô¹i#784¤¸‘ò<í@âàc‰ÓjÅO¯U5ˆÛþbX˜ÿ²øîÏ÷©°ê¥îX{zñç,¥Iú`)BåñчgøÛŠeb&Ôk4÷ʳ¨o†lë¹ò÷’•ªÏJ$òžþo «„².ÏÔƒ›Æk´X¢ŒJ~Ïvâ™ãß{›“4qe€ç˜ì‡Û‚Ö…´_úUØ)‘±ÍÄ]YÓ:G*2&ý±mîùuÖσYÒiÚhÙù=L³o®kïÕoï³ÃGmïþ§Ï:ýqfÆ/Àr,“FËâ#Ë›ÈD ón&ìÒßI,MZõ¹Þî××J{¿Öž¦BÛ À@ƒ6Ü‹í«™£D\ÆñÑQ¶òg¢oulɽ¾®&šË–G`Ùvnö<ŽèK¶åy¢ÙDLj"‰&-ÁÏŽÀ›s¨KI-Ý«tçåË;9poÖÙI»7]P¬±oÝ£–bø˜ŒÊ´\G-‹ý%&mP;Õ>ß õ±â÷hסlPü "‹Vã»—7á2 ß® ­Výö”]¿ýLNfÉ{WÎáv0n6õ»†ìËo±mƒ1A æõ:,ÏŽYó O§|ÁðÅÐËAç÷ÒuF÷¯j®X0rëXohLŒœ.þ÷p\ÕåÇ,ô±å2: —†äÜá‰-åóÇëO(v3o>¶»‡"1vë¾r¯ÝÉ4©È¢œä&Oœ¥îÚ“ ØÀ´é˜¯mCâ’GV º"ì¤iO7„5QÎÝ=13­|ì^°â#ÕÓGî%?’›”sÑu9¥\MÉâKlª(½½qRp…_4®çãÙxÞ¿ñ:·7ó6¬¸‚×·ð1ëï„÷›?.ü™Íáj´Öl -Ê<í‰Þ$Р‘jæ(•& #RÌDh$й[–b£Ô;Øp5÷Zü˜åÇJ[ˆº©–Ëq¯“,‡~H¤|ÅËaE¬`é5Çš¨9Ñt"Ï7XEø$U±ióݪٰq¬û—ï³µ)«ü,WåÝ0ñäíLUmáVN¹ƒÄÒNùI© NflAb¥Xó”ëÃY9rʼsZ9Sɱº»•*u[»{•¢Ã)$»/+ÍC/sU±õ,¯šô)²p*kK1Ü*5áa¶jÏÚe^ç¼±ÝL|x›­´¯älöêqÓ2 Ú–aKÏþ]¬?Îâ©×R_µMîî&ŽÕ\%t%GG ôQ ^heÞ=«§"Š)Ì1óG~3wë©Ô¼Sxe9†ü|¬YÕáÁm× Üý(-r^`ä­º;q€Ÿ½¶7TÊ+D} ³þnSáϬO U·ßj(Üé(:H A_¸¹d#¥Èw¢/ò>jʦD›«Í"šÅª°)"çˆ~'ÚCOT¶h‡ORî]8^œÖ¨ÙâD¢«§ÒÌ-TO$1V.UlÓ­vIõ/ß%v5ŠK^ÍÛÏ4Áíñê­_l¤º9€HÇucêoær™ª<Íë®xcÐI–#Ë=–\–­ÌeDjƒ“&–¦ZƒÓ›üÕr¹–LD@œj»Ës´f5¯·Iu¤=jÁ n"½ɾ±²cÀ¸ÃR"çöASzø8ZfŸüÕ´sZ£ÐÆ¸ì€ ¾;W­Þ¼+âàÑ+r²îž _x2|ó¡g×w(/C‹+L}Õk®ZÊÛ°<1ó•xZãðºÎ¬t?5^ÑÕo™BŸ>€"ƒôâ^ëˆF©þì®{Ët6ã:[®K”ÁÆ•9>Ú¾USŸúüœh-[M´@u¨6löÈ$ÐP¯ïÂ0uõÀmMWß“;ðç¶‘å_›”®ì@tŸÈºÎ¸°uþWg¥_Xž©KMWvÀ»§¯?‘×4SÞº"ýÊyÅQd\ÎÛÑ„îä ‘'Ý2âϧJ?·R¼†ç<¼|9C±NTÚÝNÇuT¼±¹¹Ýs"âþº!é¹¼Û óúÖE›.¿YVéÞŽ'N©»)jw?žÛ½»ëoæÍc ¯/5IL”o8ORžp/Š83oºÅ[Q¡MdâXUwE*7µ^¯H¿H°hMHç’"’Þ¸ÄC^›jÇ.î<–kÀ—÷/F­÷õ¤¿Óïoš½wn»>ŽyæWÈÒ¢'¸KÔ°úb.¥·Diâcž3kCž×ùSöxC@ƒ6%µ­ìL”ÿûgm[r}jÑKÕŸ%46ȇ{¿Q^|½‘¨Qc6sã_Ãcà'²ù|ÆŠnÛ¿Øú(+fÌàõ­÷t3"#çfݼèl<=ܹ üV½eŒåéÿwñ²¤gÇÙ+F» -EâÐbˆ¿Ñ‰©4*dQl‹zÅÅҔș?Æ*´jè_B$º+VfX©Wïg’«_ Þ:K;;wNTÐvâŒØÐŸN±UÞjÛê½ãÍÞ%£w :sš2÷ÍZw±ù/óì¤íc‡Œß™A¢ºËû ã‹Ó¡As7:{“²£f­¹Ðò»ªÒ¤ßCV$¾ŽÀ¸T'1]“Ñ­?~=\ÓÏZšôGȲÞVÚDFÎ-tWdiÓ>ÊOö9iÏeÜëW꾳ϲ5YOžfë¸óÃkY×Vz(Á¬÷Žý?øZ‰ÍJWiÙ»KåÉ‘kñ–I'¸Kð5,oÌ<%å;³0à©aÐqøÚàíA †ÙÄæ1óÞxî 6£‚j63±1ý̈‚ÙíÒTrùt+¢±D~… @¸TÛŸ·ÚÝëïg/£F ÙØbwß2F&†-³Þþ•Œ=?sQÓñÅåÓ O¹X¯S¨§e .ÄÈõëÕ¡Û}‡þ?½¾ýÚŠž©®?P<âÜsMh»Rb’Ù{¹Q‚”n/lPqŸ·ßÄ?~уž²>+¶¹¥SxE£›o½P¬q0»×gFŠë ›‘ÝàUS­'ý¶ŠãÒÊvéÿ{È=l\ûÇå½Ý,Ìyâ4«:üû¦¡#_ÈNŒ«æ´ÂË>#þšiMg:uçÕd ‘Ýç#Ú‹Þù„ç7pÛSÓåñùÿJwô·Þ£ˆXÏ„žÓ¤ÁØ]wEúÖ2âMq\Î7´^ƒíåÓF§ùx²4ê‰4rHËÎg¦vÐ{†M\êx<úËyHm×­uª¹XÊ^?q:‰‹Þ®ÛØ–¥óçÃâR ¿\œÐúò5,ÌúK43æ9³†àj¤pÞö\´nj%Ã(,$РMî½5¦-f i:²çÿT[±‰¹£.,3~ɽα[×å›<@t“¨‘jüe‘/»‰G+:‹(œýÛI¤ÿÝ Àpçn¡sÂ*?œýüŸ‘[š…í,)ÞpîÑã§|¿èÿíÝy\åÇñgw¹AEEÍ<ðV¼ò>ÒR33Só±äGJÞŠwÞZ^™¥ý 5-†‚Gž˜yà* hˆ¢È¹¿Ý”sf'-Ë>ïìÎ<3óÌ3ÏÃ~fg6†„‡‡E ×Ú]ûŒ ݵ‚¡ËªÐBXº ÞrÖmé¤i+‚öŸ;F[×Ú-»{Ó»N!ãÒ}{ñÂîïŒÜxþQÒõˆ{ÂÒL#[‡üÙÔ›½gz°ß˜•» ÌÞ½­×Œ¥“Z’;ªT7míñ!§*N7kÝÎãgã 5©Ó¾ï¨€Q]¤Ø¦TO³Ò}¿ Mî=uCØí¸ÈÛ¥ÚÙ:£ù–¦-WßIñORûÞeuð[¯©ßüsÿÊ­òÝfîžÕ$¨¡1ç%Æ'Ê\ ¶‰ävD#|‚6E÷±<4òæáƒÍnÜ>­cÒ×Zß~#æÔ™¨'ä°uõ±!ÇJøû/ÝzâÈ^ã}(¬Šz´êÕÏoâ°–ÅrÿŽ´¨ð6§v•V¹Î [T:²&Qê~&QlO¾Iˆ¿ yyzoÉBl"Z:ü¦es•tÈ Ð9”nµ±Cz~Êz!f™eø¸ï)Ý䮤ô4–"ÒÄj™Iݹw ñt%ôL!:*þOÈNç:(L?(¯9æe‡îOš}šÖÑsà¢åQÜ¡shöókfe}Né}²N±©·$J¿$ûbÅ›û¬0üä];‡×7ç¼¾É61ÿ:ä/-ÍÊí½yÁïÍS¬¶Éu3Ôήһ›ß È{®L[¥Ïw¨;lí±ak³Lê£_•­ÀÇë¼>KWõ³L¨¼Ò¦s’ÙË2Ýæ…tËÞlþ¿>ìé}ŸlórVIëP»ßÜ­ýæšXµ›Ë؈ºýUjXê,»EÅ#›‹uÝE·ô9W&ÛýäúmŠí)²¯ÇÄÕª !ËAº»ÜÛÒYáAÒÝ3Lì2ZéZޤÿÌ’nl÷¾”Ë/1EJÏ­¤K¨/Ig #¤[àyJç­ÛJÏ2<(û_]€—„ %oJW$ÿ(^/ć&/XEº£‹t;Ž…™'˜Ÿê MÑJ{<”¾ã]WzB¸­t+H©ÌHN?€¿4”è„X,DE!’…øH:m\Ìäe;I×:Ïb›tŽ9}mõ¤GvÏ ÇÖB„á/ÄæÌ[G[I[ñ¢å‹ß€çD€F&ô…¿<••.áÈ*¿’¹•îîœë:Íl„˜+ýPÀ5ðò ‘þéÓ°ô‹é7hn|iÝfÝŽ\Ÿ>X$n{ëÂJU>»ü‹o9ó,%óT´ÿº!‡zO>+·µZË"))m!-þü¶Ås7îû"×4±•«Æ*³hSưªÃtÿ¯ú04€­M±ÒŽŽt.E¬þÔû$>þu¾ï¸%Q5KöàYÐ䇭áŸå…å„ÿùŽ î°¦M¡?ÿï­„sËßoîµ%:Û´Ûçö~5uïW‹ÖÎ ò­ag¨EÊ­ï}ÛuYt*%K±äØ+G·.2üÌïõeðÊÊZ¾¼½0ƒ/À¿}›o®þ._DŸ–&´ÚçûüOß°%ê¹Öð*x-ùwö¢òÝuCg ;5ËSá,l½Ï/|ß)çS`5:{›ävÞÉéÿÅ{|ħfÇ ±B¸Û}hBÕô˜«1/PH'ôöûµÍHÏ.mü¦ø¼Ó°¼cÚÓ?­9aÅ‘‡qÁ#:Žª¾¨‰Åéí;-:e,fßÐkª_¯7*ÑÆ^Ø÷õgãç‡ÜW¾ìÕʩ̉ÏÙç8À¦ìE» ÑY;˜púù`0â…#@ÿi^Ý àïNÿèÜW£‡M\½'"^Ø–i>`ÚÌÆ™yqP®ÿϦE­iX¼ï!4€WLjÔû4êµ9ÖðRãR­Šão=ZïpH“YBka›þ9<¶GpÈ!=æGÿààØFf„Þ¹6ìèiw%xWèòÁõÞ8zÐ¿Ž­xüëôuÇI6Ìv,çQ$.üâñïÞݱ÷³_~ò­`]ª~S¨}áw«r­áéê\ÓÉ,çFmÒ7zg爖Ã,ÊÔ‰Ç)ч{¿-ŽÙ}x£ôk…Ń{÷Ooôio[íòçoØkDÒ¥Eíj ?døÀõh޾ػÏìY>¤Ñ©'á¡ÃÝÌŸ®ð§QoŒý1Þ­Bqë;×n\9 Eêk?oa¼ôS~ ©Q›z7êg\‹]éâiasº½ñSYél½™•¹F±MòiÉÜä«!Û¶•­äç*UòqظÆf\0T¬X•úuR£O;úß©Ýv ÿaPCÐW,I“÷QN¼´¼s]¯ï':»y8<¸pñÒÞ5Ÿîýv×â_vx»çŸ¡µîŸL¯âçü$lÂðMïmÿ ÄŸu¥þ÷ƒŽH¢ÜÇ“Þ.ž­cZVº3º¿µ£­Nèï}7|ÏãÄRÞ³ú”ÏVq³’ݦ˜²u¼¡™ÂVÿxÃËûµÌX÷'ì…∖íNÚ<“BÌxÚ¿u§£‰eË9[ÇÜL¸~ °“;ÎÛ­˜N©Ë÷"3…qdz'ÄËC€ðjI _2Jú¬µh¶4|—Wy‹´{»†Vo(·ŒÎ,=æÝ >Üj^ØÙjÙ§¦$GÌé,}•ðÞ¾¨©½&%rUÛ ýwŸšúÑúþûû§~1HÊp–Í–žþŸ—›eòµ5<úî|t`ôðÍ=þ׳ˊÿ•+\©ÆŒ«BT³õg¯R¹cDæFï†\täêÔºfáuªŽ3lñl``Ó¥áW¼Ê=Þ3¸R‹•Ñ"&hõ‰…o4³}rnÃW—l ¹ûì PÝ*ñôÄ*Õü/'\¸æÜ€jOW|¬Ó¶ÈÀŽÅ´wwô­üæºqgÓ²£ó[´* ä×PáâÒ±é鹯”_ö­i—zë»>Õ»n0NÑhŒñûÂ’Áù·ÉàÒfy¶¤&×ç½|5*ß”kÛ þ+Ûò.)²•ìuë‹Õ†\"\}_˜çicH“÷CGvðNÓù5&µLqÝã“ ž@—<ŽrJäÊ~äô\ùÓ½¦5uÔ¦ÅîÕ Ùì vÿgð×oèš_¢LK)ôÎÒ‰k«Œ>ùx§ï¸ÝVµÎyÆ3IqwcbR³OÓZ,doaÂùÉÄ[¿^‘–-P»Y™\ÞÌÎ1=$Ý<~ÉøWа«ÛÆÍ*g1‹Òo4)".Äñó•'Þ¯Yÿ½0âˆVy¦'''Ë-’¹æ¤s7:þµ¤­³6>Ì¿¡ç¤Ó".hÂß[ÉB~£åe{Q‰ ãÈôNˆ—‡ à•’½‡ôÜxË–¾ï•7žÕ~ÝÇ«Bภ&,]ÞwžWmGñÉœW¶uÆ8ɹýûu¤K<ÍJvTO»{oÚá/÷Ýnóhí1ã\ëÖ#{¸3ˆyé+v:¿›¢±)k¥îN2µÿ3¤¶ñ ·Žo•~E›vŸö0VÝÂóÝ&v+¿±—o<Ò [«êDO4|š&ÇÇÆFGÅê-ʺhÄe½¸sñNÖ‡…V5®]1ãow§×ûµu\÷e¬ˆ¿q?UÐÉ®!µàþ¿—7k1f°ô 2³âF{•Û0å·ôõ&GʵÉݽŠ=;Ýü¬%ó8]&_ ‡]2mk~Sn®¡å*ÙË#ýùõc&Vööë j»¿>ççðguÓZ*•µsÅc85o5yDéÿñZÇÆ¾ãÏéu@Ÿ¼oõ¾{}ÞwÎ/ãê…e¥aK‡/k´ 2fÍЙާf¸å·#*8眖û2¡|¤%ÄI'–…M![™¯Ñ¦>ŽM^Ø:Ù周tœí„0èÇ÷²þÇÔ½Øß­p®†h°þöÏ=s´òˆVyó7u(ÕðÕÒÙ¸óvµ}Ò`Ò‡‡„8ÿÃñØÑ•ŠÊ¯Á]®%_Þ 4Žž«â¯A€~‘ô5ÿOÒ¬,l-Ÿ>ÇGgžž Ì*vè^·pÖ„ sªYP{HŸ±‘çþ³Y«Ë< ˜3´f™·­È<’¯¬èÜÂï@Š%:›ð~mÛÄ£cß›z*× ÍžVÌž\Ö Ïc!}–=Th“¬ï³´dnJÕÐ˵­ü\åJš—é¿%ÂsËÊϿ޶+ôÐ…{©I·Žo|ë×û·œ\ûVQP,`šlÕÓ§eVN1Ij‹´Ÿ9ëõ ¡IÇÆûl^liaˆª¹K5ùöÞ¾wLü^.–®ž­ÄoOÄã°Z×¶Î671â‡m×ÜÛ6+_Àµ~ñÛc‘p8èt|»ÆvÙŠ%]ÛsàžôʽY¹\x˜°yÝ…ÃÂ>÷YégòÑ¦Ž‹,L]D£}6ôú4}æDå5Èõ¢eEÇÑ ê„ø3 ¼RÌÊ9 qSˆÛá‘õu¤ç<$^;!÷%¼X­\̰!ìëù®išãæZ)Wã‹ qÇPàŒa+5­¤­\Þ¸à«ó Û*½>îZFö HÚݽkØB×bÁ*ÿ®†¤‘råʸ¸s§²…3šîڣ̦»våiÓÉ·É‹ª†…KU™¶íÑIn®¡åK*WRçP­ëÈ…]GªòäöÙÝ«ü>óãýÛ_ÍÜ9»c­)òeéZ«”´õ['.?ÔײJ¿áÂý §¥sûæe«»(w ³Ò½[Pu™G?Œœí®üõ ÖïÛÂzÇŽq}ù¨õÃv ,û¬R á‹{¾5âpŠ(Ùk[ØÜí lÝüPÜYå»ô£½~UŸ%í”ë›FvIÚ§fƒÛ¸]Îz*î…©wáPÑ`\˜¼Èýsgï¥6“N°§Å]¾h¼´]hŠ•w27e ùö¢é_š2Žþx'Ä_ƒ à•bV¬qëÒâä5‘¼{ƪ3mGTµI‰ü¯ÿò«ª×S¢Uwq2\ü¾eÞÖë Þ/i®¿ÿóÄî>»žvï™ÞYv‘‰o¦ŸßÖ•¶YßÂå»JåÍ®|lœâÒof¯×Ì”úpû•{xæj~½Èª¨Â8R× ñr ¼bÌJ÷ý64a¸÷Ô a·ã"o—j?fëŒæ[š¶\}W$Å?1ý?âš‚Mf:ZqÂøCÂÃâ…kí®}FŒîZÁlk9Uqú¸Yëv8g˜[§}ßQ£ºd$ImÑ·/ìþÎÈç%]¸',Ÿû×­EŸ MÑýF,¼yø Eó·Oë˜ôŵÖ÷߈9u&êI§ç]CJ©‰›B}ä°ñøGQ¿Ûu»uJ­%µºF¥Ö„6yA;’h#Û¶J-/_ÉêcCŽ•ð÷_º9ôÄ‘½Æû8XõhÕ«ŸßÄa-w÷ÖJ²Éã([º ÞrÖmé¤i+‚öŸ;ÆP_×Ú-»{Ó»N!Óÿ€Ò:wœ=½ñ¶¡òøúÞ aV¼Óâã׺­ž³`ͶÐ_"¤¯ êÊÖk×sاŸt¯žñà¡+ÒjæÏ×:ùÙ¼/‚v‡]º—dh_›¢•ê·|ëïá4(*Kâ³ #Zq\ès¦†Ebò]äf\³ô[q¸¾ûù¶š›|Ƭ 1¤g{÷¶^Ó—L’þ°PêÃ÷+|r¬bþ½Ha©ë„x9Ð^9Z‡ºÃÖ¶6ˤN1úUOßtÍq&Ú!×”Œ9z\´sࢼ7£±«ônÀæwò©…ÆÆÃë›s^ßäWË\5+ësJï“uŠM½%Qú%ÏÞ[–é6/¤Û¼l«öýõaOßèûd߆U™Wõ3³LP\ƒ¨÷ñú°×g¾K‹Z3MúöWçéÜòm’_Kæ \ !×¶ -/_I­Cí~s·ö››ÿÂJ²U%¯£lQ¼¹Ï ÃIk:×AaúA¹&›•ñÚŸäeJÉ|Ø6úL ‹¼ÿq櫆{zÌÏ£NÊ}X¶É#u/ Ñßõ{gxPxäãZóoý°”ñ©z›ÃŒ³lê¶uWqŽ^mh€Dc_¹I±è9»ï‹úº•_Úðµ´KûßLªiÀÌ6&}ý þЀtZçN«~ÙUcü´•[\<zMèË5jÿ¡Ÿÿ'ËÊ<ýþmЀ§,J´±ªÕˆ—] ø[#@* Ѐ h@4 P ¨@€T @* Ѐ h@4 P ¨@€T @* Ѐ h@4 P ¨@€T @* Ѐ h@4 P ¨@€T @* Ѐ h@4 P ¨@€T @* Ѐ h@4 P ¨@€T @* Ѐ h@4 P ¨@€T @* Ѐ h@4 Â« 5ÍË®^e¯Z€þTh@4 Pá õzýË®þEþñø+ Ѐ h@4 P ¨@€T @* Ѐ h@4 P ¨@€T @* Ѐ h@4 P ¨ð7šç‘ìÿ¹¤IEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/netconf-callback-points.png0000664000175000017500000015030614770023131025624 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+ÐxIDATxœìÝ|çÇñ_’ºQ¤Å ÃÅî2Üe0d0ØÆø3¬ .cc8c‚ Œ!CÃuȆ wZlX½´ióO“:µkÓ¦òy¿òêëîr½>wOš|ïÉsÏYèt:4æ.‘ Ѐh@4 P€ (@€ @  Ѐh@4 P€ (@€ @  Ѐh@4 P€ (@€ @  Ѐh@4 P€ (@€ @  Ѐh@4 P cè³gÏ®Y³F?áéééêêjiiiî@­VûôéÓ|ùòé§;wî\©R%s—(q;@ïܹsÚ´iú •J¥ÓéÌ](äôè´C€È 2\ËØúîÝ»ú#nîR ùôéÙ˜è=zdî²$IÆÐnnnÆó•ŒuÖ€H‘A.wîÜæ-Ieì­ÑhÌ]˜†Z­6w’$cè7YXX.\ØÜ¥@"îܹlîR$Gf Ðyóæ½~ýº¹K€D.\XŸ¡Í]ŠäÈlHUh@4 P€ (@€ @  Ѐh@4 P€ (@€ @  Ѐh@4 P€ (@€ @  Ѐh@4 P€ (@€ @  ЀhÈ|v·ÏÝtS€a:[·?üZßAñ:ÚÛß¾SdÈyãLÉ)WÎyÛ*uKmYd7“*)¯  tDëu÷™ó×nÝyðô•¯°ÚÎ9GΜ.ùŠ–«Z¹¤«:ÙÛÕ>½vú”~Ã÷¿ðñVYÛgË•¿Pq÷ •ËÎÆ»@R«FHsÁ7¦W(1òrôEnÿ¾2¯fâ­"gG¾]qú]ãŒm»]O~o☔í']µŸé•Gtmjù·G_MÞ6âRe‰ç±>ùâË[ÁOO®_´àçUv_yß,\Ë4jÿÁÇ}?lSÉ5É­iÚg7ü8oÑòµûoúdzJ®òmºõé?èã&EíT‰n/Ö]ÎÞ‡ï.­m¯ô×j5‚Ì+)¯^+§ÜŠ•®X­n“6ïuz×=;Á@FÅ/Òƒ? ]6øðÀ¢–æ.Hš õ¹´vÒYž$¶¦öéÅ] Gêz}=oZ¿:¹>ZAž{f~Öwìæ;º„·ûìÜóÿ1oL¥¾ß.žþaÅlJU3[Ý¥^äýäÖiýcÿúùûæmøùœ&u~Û>ñS׌çÅúº9ß;lœÎÓÿØ­ªÙ™·@€‰ ‘.„Ÿ8~O÷•-rdÆO’ø„zŸ™ÿQÛëï+û5ß³ËÕýe¡Ç_Çf׈§áWûx׸ví¿9 `«§}TiËÖv,ÿ_yGEµ‰ê.õjxÓ£ý³ÞwßvpÓ¾ymò¥æg±ÊÆ¥`ÎìOCõ“n.t<L‚tâŪÏçl8±œ‰·kS¡Sª9’ú‘a󶻡#ƒ&{•žúÞ ŠÍ 7ü¼÷yÄ\æ½›H ýѦdéX­L¡ÏŒiÒø›ÓÚè ­‹6êÞ½S‹†5*–*ZÀÅÁRäóüÉ£‡÷¯Ø»uóæ?¶Ÿ~ñë—NÜ­«áGhÕznü´vÇ¥w¢/³/Ó®_ßíתP<ƒFDôêÁÕS·¯ûyÁOûîFlTýþYµÇ/îS-›’4lîºÓ䪟/å¿©W#ÈŠj÷Pß5ÚG¬Nðêñ¿Žî;tÍ;ÚÒ«óÛµÈ}êÈ„Š©×@ëPwÑÕg‹Rmó@ÖD€FºquÚðõŸlï‘_cÒ­ì0ù‡q¥”vQÕ¸¾;ê»wZÃkóõŸ÷Ÿq¬9lÞ† .Îi×,FVËÝdì‚ïF¶)á=/ª¬] è%Ê×lýñøù>—Ÿ9ú‹¯7ßÔ¾±Á:Ÿ&µè==[Wì¿tå´®î1’Êʹ@¹F=ôa_ùþ]=6Ü æõÑqM?p;»áÃBJ"izª»äIµA•¯ÉIq¾zµÏO.ÙëÓ%—CÂèÎMüdA·£C‹ÓÈ@Ð0³|µ =üûŽa2p÷ȯ¶ý¡ö›×–e.:ßã; ûëu䛚“ölS;G"ñSíèÞqÒ¦vƒþžÕ£ÝÈqnÙëШNSÎGõzvnùãß¿õsOàò@ —šC~;]aÌ»ï~s6üÝkË'Ýç×úsPñÄ¢k¦©»Ô« 6‹œ•?ZôwÉ5ê̈¼LùÔìÎýone[s– €"h˜—ªDß9-n¶_l¼bëáü¡K<þR,S·Ä¼¾4»÷ôQóoß»}\­$÷™Ðä¬5bëåªß/·²Œý+g§}üC´þ»eÆí^“`z§ÎYÊŽuwËvXý̸@{ddÿ5vötK0@fšºK½â¤v®=nѧ¿Ô]ø8|çÖ?nM«\ÆÚ¬¥ f¦v®3~jƒ}þ4ô7Ö˜4vWU­reÚë\BŸn5íJÔ|™Ik''=«…Ó¸Ô<ì-?Þ4zÎͨùÒ“W®’Ô.¹šqâÜ•<}¡!YØØÙ98çv{«P±RåÊΞOÓ‘ù avÞŸõùW•¦Þ2̽Z;tîïN©`ê ÒÒ í½õÓ·F Éœ«×ÜÁeM³«Ú»ëfí ŒœÍÑ}öÀ2J¶¬ÎÕôˉUV :>ÿ`ÙœÃV5uNðã9Ô]êÕˆHðóK{~ûuÍÆ­»þ¼ð4$îul‹5îã1vÌÇuóšk,é×vÏ1rêê³Þ±Ÿ±.Þêó¯gŽîX2áqÖRo7ƒŸûuöŒyK7žù/×o×ý£~wªž?¹AòõµŸ5~Iä©gÞN‹öþü±{ê-góVÕ‚ Cž=ðÒÊ›ãB‡ú\þ}öä©?¬=ç!p­ÒuÀ¨ñíÞNèT9 w"ŒëÞ:ÿë¿6jÖ–¯c¯îX¾ëøÙ³7ÌûF’ <3¼d¥™÷b.|ücuûã(XáÑç®|U.ÆYCÈËÓ«¾™<}áæ‹^ñîm*ÍÚwýdhÿæoÑdó!@#°-?tö{?¶]g|Ǽ1søÚ~;{%Ü} ƒÒzn_z"j¶ð'µ•¶uÆ·åÛ–Šš-ØghÝ„³ï›, ½?¼éðλÂ?-½¶-ýǧi§„)£×]êÕˆîÙºfy:ï'PF ¸¹ç‡Ïö,œÿéª-ß¾W(­ó@Èû¾hÑbÚɸǛy}cë×ïm]óÁŠ]‹>(ÏYEjíf¨×©ÿë:põЄÖò¾q`ÅÄkþÉù߶扼Rãü#¯ŽMïØtÔþˆSËJ÷lÿºiî4ù`TYÚE?™ ÒÆ²=äÙ¡©Ý:ŽÛó,þ<=±z|‡Õß7ºqÕˆZIm( ^ÿ»º_“n?ߊûYŸs«‡7Ú¾gΡƒË™îT#ôå‘o:µ³?Þ{… ¸â÷y'VêÚ´Wž ö}24Òu®æ_gÝçÆøzï¨)·Ÿ_×)£~9/Ý‹#¿‰š-Øé½’&JL¡Ï¬;5ëÖ¡K)åͨê\õûԲص?|H Ÿƒ¿_öoR=‘ѵ2vÝ¥^èë$( f¬T9¸äË•ÍÉÁF½žÞ¿ÿOž|øŸ›ÑªÇ´“CˆØæ)V,¿³Êûá¿7úF­õï/=k¨O­ê^0Î/ÎSc7µ6¬ßnÁõX‹ÕÙòΛÝ&ÔÿåóGŸÆù«ItwÝ€&Šü#¹Ûý¸we¿2ivWí‹;/¢ÍÚå°qLBžnX«åü›1É&§››‹Íëÿb×§»FÕ®ùpçá9M]LòòÑz®ëӣǪǑ Ânaoüâ…_ôÓ¯ÝÍ—º¸¨iÌà®Iz4Ñuð­…ù3Fµj²,^0·³½Fëçíõüá¿^%v¦¤4ÒËbŸÌé7³î‚G†¹G =~úüØç™nX§€û/G}9Vo_³žb7£oÙ¡zóɹ _•ýV¥dÿ…ðÙ'ÇN>ÕV/”Ø»DF®»Ô«‘pëvîÜ®uËÆµ+•Ìïý*C­×­#›–Μ4uËmcËãã_zý¯uõï¥Y„¾¹è£ ÏÂÒ³¦d÷™ßOú¨aQGc õ¿û׊/ Yr.<¤=[ß«sµw{;¾³ Sî¦Î÷Ÿ‰Mc¤g‡J=ÇŽÔ³yż¶‘Q-Ä×óÒ‘Ý¿ÿöëŠû<ïz¨×?³:5±7¢›€EyÍ;¦·È›†Ÿˆ¡ÿÛ~-jÖ¹DÇh94èÆü÷:DOϹ Ÿóõ ŽÕÜl Wçÿèº9_xÌ9ü2|…kßµßýâÎ~ERþŸwoyÏž·ÃÒ³e‰c&ý eõ"Naµ¥ zzaûÂqƒÆÿq©ò£%ŸÍpñëh½¶l*~ó¯îQ|'BÝó#FF¥çœ ‡Ïž2°Sµv1¹ÖûÞ¹Ã;·l\»|ÅÁ rŽŽÌ‹tBåXsì7îµÇøzúËÑÛ{®më’â/èîmÿÙƒ$~µiS²Ïde÷Q$Ôëæ…h ×(bªa«B½n\Œö=o¡E“wW‹ÜÊ8É…ˆo´ï¼ÿZ Ðf­;KÃáã»NfÿáÔ«ÃQ™zøVåšEìãÞ‹lEëöúªNçžËº×úèwÃ=y|6þár›/˦QGŽg~úŸv fÛ:¤lôÁZÔvoÕí·øø»Õß«òÉã÷é!ÇÇô_Ùy÷Gc¿L¾›¾ÇÆwùêBä¬MÍ ;7«—+vÜÖ8ä/פ·þ1nεC§í”œ÷ÝÛ8¸i§W#zL¸´þnϪÏʧí=püN}7åpÔ¹›EÅoGýÏjï­ütØ¡È&fuÙûL­ý_AeW f¯Ù¶hêQ»Ùwáçû=>]ÕÊ=¨n?Ôÿ,ÔkÕ¾…]‹D«%••k¹vã6Ö¬ôi¥VKÂÏZn-š{lÔÏõS<~¥×±Å;|§­,<±³oá8Î,œ ¾Ó²¯þ1~Þý›¾Ùé¿s"@#ÝÐäïZ¹åBÙ:óŒW„ýùåwç»Ïx£l¦ÝMííåsïDÎÿ|Ïö ÷JW;–¬_/ÑDõ>9·s“ÏwE´ÛjÊ ü}ç¬ÖùÓô ÿÕe½[­}Ù¶ÉÿEqœ™3áÏÈøìÔvåίëÇy"©qi:{÷â«¥>Þc¼B0pïø¹g;Ï|'å§Öõ~Ü»¨k‘¸þ¯4®Í¿ù¾ÝÚö›ŒÝ|žïÙp% ~ÕþÉà'ç¯Düíw=:Ç•ž£SÛ(‘„7&  ‘ŽØ”<ëýï[­1~¸Ýš=luÿÝ} džiˆßó¨áÄÁÅÑd»â÷"ú–]’»e ÇÜÑÚ’t~/Ãú·&¥A+cÖ]êÕˆªìu>mïòËbC”¿¿ë€çÔJEÓ(Ï©kN™Ü8g¼íx*§š£g¶ø©óvc´¹ûËOg&ͯ‘Ì[N'i7__Y:çxäµtù,› xDÁ=ØäÑ´ã‘vr6Ÿ³{ÍàJiÕa?$ÐëÉíóGvþ¶`Ö÷1»”ùu+×Èjð;¹`åƒÈç*MœÙ)_üÿ‚o}ðí˜Ùîc/gïý²ðô¤EµRš-‹û¶wÑxÏJÕ¹ôkî¸i±Åøá±ó/Bª¦ð.¤¡AÁÓ66ç*ddaéýó Y‹:g“)«þ6øÃ'\П£'Aê5 §1]p`pÔœ…µ…ÉvÌd[VYXG5ÁA±GˆO†¬»Ô«E¬ò–Î#bl ¿}ävàÐ4 Ж µsKðC@»É VŽÛãғÝ[o¾®Q.¹=LßÍ ÿX5îCùχTu4Uè¼Oû~c×í©Jõ߸{n;·T;Ò×Ç»[OÊŠ¿_?²|T nàµÍQãnhêy¼ŸH¯f«=‡TÛ÷˜qîéî?®Öª˜²ï0Jöî‘ðEÈî ‹ËºÓÆ™'WŸK ´e®".úoôߞù;6ìœÀY ‘¾Xí3§ÿôZßÛgž,ºpØñ%S0Bn‰É—/Œ+e®!vcRYÚDû(Ô¾~cØ*óoY§}-QŠ¥­‚idÀºK½‰NûâÒž ¶íûëĹ+a÷ Hh$?χ~¡â˜&½;K·«–èåNÛWT¯;dl´½}ø’—®œkܯ‰”ï¦îÕÙÝQ׾ݥEÓÄ[]°çÚuøîbD‰œ›ÌܳΣ²“ÙûÐZ–þhɆy=JFK«¡/Îÿu;Ñ2k%z1&O½öîr,¼ úîá ¯B+¦hx7çʵÜþ·Ód+યã{…ß3ß¡ÎU³}9vÑ0ãý{—Ê­ÎLÿY§êá×LééCµQÓ›-é¾Óø­ñÙ)_lýpCW³Ò™‚Æ>g´¯¿}ÿó1ÙLûÑ·üÔGÿº ñyâmÖ>»­’#Ÿáê.õjÄ(ÔëìòÑŸøñH#ùÆà O«iqв¹—É•„+D‹WÊ'‡Œ] tž—‰kì&hSíæë‡çîEžÂ8–¯–ß$'O¾ç¾k_a~T£nɾëw×± yOª5y«tøpÀ0nU]bUAÐã‹#gœË–‹ý|,óVrwËáC>¼ø(Hò¤¤ :g‘\‰¹¨mœl"´6($å'ž–ÅúLí1£õÊðjz´ó›žúGöR ›6nP¯v­Zµª•Îg—ŽßJ ‘îhòuœñÅ”ŒwWöÙ4læéfÓ+'³ãeºb™«˜‹È¿ásÚÇÿ>×J6“üÆÚò£ú-'ë^»AO¯?šËQ8²û»e´ºK½‘°q†W}T·ûÊ{‰¯CHphª´ƒ¿)Ç[I¹%²¥~5‘ð>¹^½ôçf1´ wSûêþËÈ—¢‰¦¸¤ñ\7?²Ã±ºâø¿öO¨áœI̱nÿ! ]£uCPi,mì³åÌS°˜{…Je 8ÆÓCAëõ0ê–9 åHÊA°Ô¯§?S0Îë(%’Ò—Im “¼^Õ¹ZÎß3çycÑNÃ^^Ù¿Fÿ˜gXÁµ\ãVí»ôèÙ¹^|C¾i‰tȺôÀ9=æ5[i¸`_nÏýü×Ïö}òVƱª³+ë"Ç#Æ}¸sôvÀà")ÿɰe§b¥sÉñˆOž;GoùKÑdÜ™MûôÜÅh7t.X¹€Òþ®«îR¯FÄï䤿Ñc¥eá†Ýº´jX³¢{Ñ‚ù\sf³·±²Œ¸„öÎ÷ÕŠ{œ7ÉßUÀÒÖ2 §G*KÛ¨ÆÚà€à˜iɤ»©‹v%™XÙ+è>”T¡gÖ®<ܯJë4ð9ï»Æ&§’.8úAHR‰Ê*ÚM ƒƒÓè ÌÄT†l¹QõÔqS¾Ûz- öÓ¡OÏïZªLêW¢Ã„yßkšH7 •¥×Ï5dmªì&O®¹ú³#†oÔƒ™tð½%”Þš:ý±-ÞÐ]½ø`ø ÞÇvÝ l”ÂË}¢¶\J½4b\Yßc;®4©¬xd)ÝË“[¯DÍæ®öŽ«â·ˆŒUw©U#Ú»+O‹<”NgîY;¤jöxo"ZsÜ`í4'} ‹ºÑw¬ÌmâÝTéóbäL_’/`MXñ+>ø§ÿøÆÚk?¶©æ³êÐâ®…ÒiüÒŸ°D;Iª"ý™‡´:²IRèNŸ4ÎzLÛÒcÊóË·oßýçÁCÿ×;æ:A×7Ži¶kÏô¿¶«f÷Þ@€FúdQ¸×ìAÓªÏ16mý·Ìcþð“£J™¹P)¦ÊQã½ r0üâu¹»~ýõ/+&{PƒèÔ9kv*'‡#FM~°qíÕ©••&AݳƒËþŠúö×±n÷äô¾ÈHu—J5¢½¿yÁ‘È+Ö:¯\åQ5¡›>h_=x•Ò?™ /î¾ ŽÕ#ÁúÕ"g²åÞÃÅÔ»iá\ »HøÝŸÝz–„Ò%کʈmÇs½_·ÿã÷"÷éVÅÛçðêOÞNW¨YdËçy^Üy»ËL\‚£ß<[>“õB2Ëœîï~  “°>-wÎüµo×¶ÍëVo9ùò;0¢í˜Ú—æÖp0c1‘µeøÿ3dZöUFÌhµ¨ËV?ÃÜ…¯Gnµ™Ë”bn-úT–Ó'Ãgoÿ4÷ÈÈ% L1­E–V<äLøì½¥³YÑDQïööšé;#oß ÙZô©šŒ^ a2PÝ¥Nø]ÙÙ.›­ù§õs%Øe3Ôëêɇ ­J¼.]x¦­ïð§€ÎçÆéÈ©ò—É­áÖÔ»i¿Â[j¹cü>ÀûÜqÏ †)Å%ŠÊÎýëN8ö©ûÁ*cgîg›?­ÚÊûÀ¦Ï+™lœäg^]8ÿŸ¶ž}"ÔÁNG\A¨—¯LÞtÚ¸ž<Ù Uiù‘þ1öÛ'‡çôl3rwxо·hêþñ›ÛäHo5ˆ¬‚tK§í´Ñe¶Ž1Žlä»eøô“ {˜¹L)fñV§-GtÞ~ûŽÿ– ùþóã£K› Ó€E¡ÎŸ7ÙcOx~±òóïFœW6É[}¾{ü¤Qóù{yÔMvÇ‹ Tw©Q#!>Gž‰ävÏ›p ¢îå±µ§ÍÒkõòæc/>+”ð0)Þg~?yÇéBuJGÙÛ仩ÊV¡I 9xÕ8wuíŽûÃJšlHlëÂ=–wtªßnÁ üÏþá5{ïÙ>±N¢Cù¥)uŽrµ Èñð}qãßÏ>+”ð t!OmŠêyõVriqdâ¢]f¨ M`Å$³Ì]gøª'Š´YoìÔpb×À6ÕR~ßE 9ÐHǬKõŸóáÜÆ?¯ñº;oèŠÚUÍ\¤Sçnýõ°··M r~L—É |U]Q›gȳƒ?,·üðóšÑ[ˆ5yÛOTxÏŒÛáóÇwŸÚüÈÄÊIÙrÈ“?}øëóÈyë†S†§hôŒŒSw©W#FÞ ¦íµÓ¶¿qÅTšþó»M÷;õMàÏÐ'»¾Ý9°aî&­ŠÅ—’M²›V…Ût.>f²1áʹ™ßžøx^MÓ}Go‘¯í÷Gö:5n6ý¬¡§Òëã_Ö«ë½m߬æ¹ÓÑM;lJ¶mœkÖRãÁ!æüv»ó „N#‚nü2÷h䙉k“6ÅÓîNð ÐXÛè_WÆaÞ¯M’ Ãú\Um[ZÖ5Îy§xÀ ùÐHÏTÎõ'N©ó맇 —¥kÿ7Á/}Ð%“MÙaK?ÿµæ¬ˆ;®]šÚ¨µÃÞÍ_$ql­çGfÐnÄÎ’k»Õ¡èOÙUõSß_ß]ñ=ù…ÉMºåû{MßRv gÁÐÇ7ï´ê¿ÈšjSìæ–²CêÎä5¢qt kŽ5¶ÎÞÛ{ø¡¶b‘xÞkµÖ þ☉҅b¡GÆNØÛqi³xîæ­ó>:uÄŽÈÔ[°û'¢ŸT¥ÂnZ¿ÝۣƔþGkz~÷áÄÎ'fÔ6áý,5¹M=ð—S«cÿ2ì—îÒ·-jxÿv`Á{f:ûÊýz¸->rà©ñ#6½÷Û{yãõîÞ*)—"g ~ði¥”ÞÇÛ4,³ÌqßI¯[·½Cë;˜¢a<Äï¹_äŒ]NƳƒù ‘¾Y¼ÕcÖà©UgÞ1Ìù]8›ðê‚ʱúäÓŽTy4üÊyÿCcjºÿ5qÑwÃ[M ë†ú\Ù4kô_mºÖêR2®-go8cÝ×ú&âõå–~ë\\¶rêû¥âiˆÖ>;úcÿ®ƒ×ÝZäØ|áªÏLÐ÷4ãÔÉkÄÞ½yÙzÊ8sfâ;­êœïÍw[í£­ƒ}°Åç'ÒÎË;v®p|Ëà2oîæë›K?hýmä êþ¸Áåc~[ž »iQ¨ç™Õg…Í}cV“–Ž;7­›+Þ³¯PŸë‡NÛÖ¬W ©¯Xu¶j£wwz¯î ÆÎ´·—u®ê³äð/}ÒGÓ­ˆmEI æô§ñÔÄkc_Ü9¦Z¶7Âbè³½#šõÞé1oÓxò é¤Gƒõ[U Éâð³òS?oðÁÇã>‡¿²»l¿ïUqM¨C_ž;'r DMéEÓÉÞ"+"@#½³{gøÌv ;mJfʸ·qüg’ÞÉѺX‰Ÿ§z§H•]¹ÏÿØö_£f3ÏGŒaðhÇÄÖž)Þ䃛ׯQѽh\v]°ïó'Þ»vbß¶Í›7o;õ(±6<•SÍÉ;Ö>¬ÕeEÄÝ€_Ÿþ¾›û²©úÚ£]㚊æ¶Ó„ MæåyåÔ¡ë–Íÿiïèc‹YV°kUï"¦iKaÝ¥!׈E6 éý§q@ß—ëºTò»¶xÎ%"î]â{ûð¯ÓF _xÂplr²}v'Í»qhrÚ…<÷÷ßïQñS3¿ŸøQƒ¢ám„¡þ÷þþeÊ !? ŒXW]yòÄ@©²›öU'®»»ê” ÆÙ€¿'Ö+¼¥×¸1ƒ{6¯í¾>!¾/ݵqÍÊå+ö?l²ýiÒ´žÊ¾ìÀ'{ÕíýÛ#Â'ë?ªâãspý òIêñ”Ú, öX8cyÙA‡Œ:èØøêeOL˜óå€6åÃï.£ xplýÜQCfŒ~æÁìù)ýâÈt4¹ëu*#'Œ—Ah}R³ÅÙï7,_ØÅÁJ~ŒÕöË—/`8u yvlé”iK§üïíw;uéÒ±eƒå‹äˆ>¸.èéùm?M>n}ä)]ö÷F4ÏM 4̆tOíÚzêØò›FžKÖož]¿XIÓgù2C=Òâª"M®w§:æÖ«ÍÍ"ÞØýÓ„Ý?%á×m*×-ÏM- t^|ÌÁ¥m‡Y'#¯ðò»°qÆ€3Ûjî6s·ÿ2°’“Év?eu—¶LZ#·÷çMú®üèÓáùúÉöñ­··Î[²”›³&à™ç­[#ƒ¤ý»ß®lº´Ùð4?HE?]ÚuOI'´Ú«+‡¼»rˆ]¾âÅò9«|ý{íAÌsží—­êþf÷çTÙM•C• »6=­ÛnÑÍð%¾§–ì°|¤hœÝŠäs¶ ñ÷zþèÁ³¨$ž¬Ó=›b®<îèX¿Óck·×®!5šyïÛ:&MîS˜«âýÛp¹V«ýŠîo™ÔiË$qÌ_¼€³Ê÷Éý{Ïb‰”°im_“]ri–E{Nh÷Õ{›ÂGqöÜóÃ{~ˆ¹JáÑç®|cÔHŸ«{—MÒ?ô“jç|…òäÊîlo¡?k}póæ“˜;œ÷ƒ%sš2̈ Àªä§s>žÕpñÓÄWÍHÔÙ*ÞxµÁªñ†~{ø¿Ä×—«fÿió¦ôz'ÞûUè?ºòµ˜ù×w§ý¯ï„m÷ã]+»ò}æ,žùQå¶š,ªîLY#6e†oýý^ƒ¶ ®E-{ýèÚÙG±~·åÜÝ«ûeÿyiÊJž,*»ò#·m÷oÖrÆiC²ÿÃçãgî­®Kw/éY(îh–:»i‘·íüŽ•ú¸‹ÇÆh}‹$äÕƒ¯$mIaY ã£;Þm1ç‚á àïñuê{ïØóMcó·ãjr·üþØ|];Žßuq¯øxÞ¸ìùƺ.M¿ÚðëÈ:9Í_êè4y:,Ù2þY«É%ë+¨ÐWÿ}÷¸‡Ö¬Ú4«}žôµ¿ÈjÐÈTÙꌟÚ`ÅG%¾nÆ¢v*×cî¡Î£þY·pÁÏ«6ì½æßš–¹Ë5îôa¿~6/“=)ÿ·ÖšßúoÿSë~˜·`ùºC·ãûæû_÷I½í+–­íŸ:“Ýk•»j÷ñúÇÒàW·ÏŸ>õÖϧ^¾Á;ç9sºä/^¡ê;%]ã鲑‹\ït°¼ë„e¯œ:uáÚ­{_úø«­írå+T̽B• E³+ ™£î’ÀD5¢r(õþô]&?:ÿסãço=|ábi—=÷[%ÊW«]¥˜sÄû¯E¡gu+“c“ßý{É%¶ŽEáÁçtƒc.³y«Å¸u-F½ºùÏ¡#g®Ý{ê(¶9ÜŠW¨Q¿ViW뤼èLº›ÑY»Õï?¯~ÿ¹~žÿùçÌ•[žz„hlœrä)T½l…òÅrYÅUÀ¸v3^êìµ& œ¨¤\qK•W¯Ú±t§‰k;Møùñ¥޾tãÞ“°C`›-wÁâ¥+U¯Z:O„WŽ¢#¦çÐp·nMRÖT;½Ýràô–‰Ô»Ê&oÅ–ŸèD´>®^¼rýÆ­»Ÿ{ûø¿ÖYØeË•·`±Ò«T*w…f@€ÒKçÂï4Ô?L½]µmžÒµ[–®mêíf~¦¨}8(ÿn—òL©Â¹XÍ6Åj¦` ©·›jûüå´/×Àô[Î0T¶yÊÔkW¦ž¹Ë‘,ÝÊÔÐ?›» @‚Ѐh@4 P€ (@€ @  Ѐh@4 P€ (@€ @  Ѐh@4 P€ (@€ @  Ѐèäðôô\¶l™¯¯¯¹ |=zô(S¦Œ¹K‘ñ “ãàÁƒS§Nõ÷÷7wA’ÏÙÙ™ èd²²²"@dAh@4 P€LAAAæ.Ì€õêÕ5jÃØ€ ­U«Væ.B†D€NŽüùó;ÖÜ¥€ Ѐh@4 P€ (@€ @  ЀhHu¾¾¾ÿüóŸŸ_@@@Ž9Ì]ÄáñãÇyòäÉ–-[•*UÌ]éR݆  ¤OÏúi[[[sqПäØÛÛ[[[ß¾}ÛÁÁÁÜÅ®  Õ½|ùòõë×ÁÁÁÞÞÞæ.âæããbî‚HïÐêôZËÔjµV«5wY7•J¥60wAdhHuyòä±±±ñõõ5wA/N§?ɱ°àc@âx§€´–-[¶|ùò™»wåÊs@C€€´V­ZµÑ£G›»W¿~}s@C€ @  Ѐh@4 P€ (@€ @  Ѐh@4 P€ (@€ @  ±{î_³ýn¶:[¿m¯2wa Ý!@b ¼¹þ‡%¿?/îÚ¢ÞD€ÄðúÎÞÃÏÍ]HÇЀ!×Òíû37æti8G]yê–™5ì%øùÙ-ËÝrøÜí—A"'·25[öèÝ©JnKú¡Ïw|ÖqÚ‘Ü]¾Ppÿâ5ûÎÞ÷ µÎ]®yï¡ýš²¡@fB€DPY»º—/ôüÜ/ýŒÚ¥XI—ìųi$øÁcûÍ>î¶0»[!¿û÷œÛ¹ð܃šÖ¾€>C«-íŒIúéž/=ümÊÖªQÃöèßןœßôÍ /ûåãêdW›uÇÀ”Ѐêœu‡ÍÈïÔó£UEŠöøê‡¶¹Õòdë´¹†ô\¨Û·ß}\ÞQêsnQÿÁkîžþaÖ¾Ú³›¹¨E­662ë^8v_² gQk y¼ehYç´Þ.ÙѧF·|ÞÈ4xC$$ôù?[.„ê',*÷îRÎ1¬%YíX¶sϲk¿º ÓžßqΫé»Ù£ºh”êÔ¢°uØ„&w¶¥gŸ;§“;G®út-n2 4 !AÏn<5Lä,–Ï6"«íòv– /%ô¿;/´’Ý2bmÇná½5T69ó:Ê9o¯GÞÑ×€ Ž H.úLhø„:FËrhh¨.j}mˆqŠÆg™ +—¹E^Š<»áé¯+aeá¾÷ÿ}6a‘¯XÎèŸ$~·ÿ}RÑV¶Ž÷½{~aËT9ògãÓ@&Â[ :•¨}0^Ý$¹mÔ9ª´.¯¹z.$äôò —«ö.m¯yyê×—ÃÖ±­Þ¶œcŒöå[kVŸ~wH•l*¿Ë¿¯½nXT´ÎÛŽ´AÈDЀè4Ù ¹jäaˆü·~`Ï“EËtÿrl“áƒ}:û¸ß_>k·£ ›õ«ÛžÞa«æj4wAÀ<ÐeœÅp½¹ æA€(£V«… #@  Š©T*Z dYh@4  ˜Z­ ñõõupp0wY ­ ŠoyíååE€ Éjî"€ Ši4šsÌ€ PÌØ…ÃÛÛÛÜ3 @Ó§gýÏsÌ€ PL£Ñèš» `h€bjsÌ€ PÌØ:88ØÜ3 @³°ûø @ÈšÐÅ4 £pȲÐxý'þ‘ƒM¨ÄÉEì5æ,QzAYòZ–‘Ÿþ–+÷Ę¢§\‘1o›¹Pægii©ÿ©ÕjÍ]04i…ÈåýùâÒAö¬’Öæ.Oe#ƒVÉ ‘«_I©±æ.Lú¡Ð*•Ê×××Ü3 @™•öžŒ›/"÷6ÊÜ3òcus™Œ>@™»`h ³²È%µÜäü©SÐÜ¥A&c…#44ÔÜ3 @#ƒ ¾%µÊJãcòU9s%Ýr”oOKÓÝ’£ŽÔΗœ p/kë°>A~~~æ.˜’NöM”ÒØÜI_´rýš.-– ,]¤m÷änƒ“Š‘7bQ«Õ¯_¿6w)À ÐȈ‚®Êˆ•æ.Dº"g×ʘ1²ËIΗ²6&Ø$9KSÕÌÃÒÒRg`î‚€ ‘±^ ‹‰¶Ê]Ãì×ååkÄ›‡\Ÿ-¶«ùß’…sdõv9{[ÂÆ©µB¤ùû2´Ÿ³XÉG:ä‘ßýÃç>[/ÅÊêr榉Øç•údÄ©›7¹e}%]Þ–ßž„ϵÝ)›šŠ÷Yüƒü²Q®‘;‡$·:üÙ'Ë´™²q¿Ü5Ü—Â¥¤4î,#‡I9§Ø[}öÌ'Ëža£ª©¤˜»ä|,G%¿ï OÏç§H³/åQÄ^snÉ"1¶so·|9C¶ü%OÃfóHñRRïù¦«„&í '^f%G q~Ò³€üò2|®åvÙ\]¶ÿ"¿ü&{ÊËPq,,ÝFÉôÄém&éõýOÝ”ù³dÕ69w_Âúøª%WA)ÿÛeÒ«/ó°±±Q©Tþþþ‰¯ ™‹ÆIôbŽÒÿç°Ùö³¥gá° Ç9JÛÍåÒèCÑçI÷.òÝ7ò–£<¹(?Ï”ùÃdþ·²tŸô.nXÏQÖÞ“³¤ÍÔ°¹:I®âR¦¸´,-/ÉÑ›²íÙ¶T¦ü)£ªIÒc^gYûXæí’ Íä±Èƒ32yLÞ>–°>ì†ÓÉñ¯¥áXÑ‘¦Cej=±y!fȯ_ʪŸeÝQé”?jÍ33¥Þ}ò—Cä·V’×Jn‘©ãä¨án3wK»ˆ¸_n¬<+LJJõ9qíâ,©2Lä_I3wѽ”Ó;eî ¹[H¾ì*V‰ä$–9‰G ‰ìåçûò¿Ò¢¿>™Ëá1RàŒ<²‘¼®bë(/½Äç¶,ì+÷¬åž1ÞÙ’úzxc}ÇwdÌH)ï& ϟʹßÂN®bKzõe>úhîR€ ‘±Xæ•Víäß;á³¥I»˜×·ù•f†ôSmšì!vÆ¥Íåƒ>2°²üxGú4•âg¥¶¡iÐ2§ÔÓ'ž©¢-ÿ­­#¶¢“;¿K“Žr#@ƶ’ŠW¥EÎd8[1~ójޏ"kI…r¬¯ôz¾Â³íÒÊ¿Þß +;ˆ±ŸmÛ–RRÖÜ—žK­­’×°Ôÿ”t4¤ç2_ÉŽÑáY¶Fiâ.ÅÛ„eÊ_·Ê ¡Q õlmåM!ú:2l—í”ok„/lû¾Ô ’^†éDrÒËœ”#tj{©XSËÃv6G=Ù»_Š8‹JÂêëßuR·‹xŠìøJ®¿/îVá¿¢èõ çý—41¬ïÒUάüÑÞ"¯úÈÇ.’¢C‘©»p˜»`hd*ZY9Tné'rˬÏ"Ò’:§Lš++Ú‰ïmºBŽ ˆýâ·Œ~Ÿ•ê ë'HùIúˆ$£—Kã˜Á4Ê–¥ƒÃÿhþÉ2o¬¨ôÑ/X–~¡ÿ "î2©µD-µ‹ î"kHÀNYsG<І-<ù½Ü6<ûñû½°¹êKcGYçø<I¡Äþ«}ÏË?!aûØ TŒ]®;FúNÂn*)sâG ¹œ É[Æôl(|‘N2­žô8(rC®øŠ{Ãr¥¯‡à°†dk™;+FzŽ[rEf`cv^D 4€¬‰ÌDû@~:6áPGʾѷ5g©m%;ƒäÄb¹×OŠ$öêwÿP*N’3"ç~‘ƒ¤°éþ[lß‘v |_~½6‘§ž¸ÅŒ®…kŠ,›ØuÙÀBäÒyÃVR"WÌÍYÉÛ®a]ºå¡< J<@‹ÎЯW'¿•6Í£z§Ø–‘‰e/¼‚2Ç/ò˜ŒZŠ—9¶_/üE Zéë!øŽ,>ö”U=i’'ñ¿i’C‘Q©Õj•J`î‚€ ‘™ø]‘+†‰üååÍ!TöR¥€ì¼%rM®ùI‘l‰lÍ"·ÔÊ+g‰\—›RØÑô¸)Æ^µÙ ˆJ+ÚhOY:‡5‹üg¸XP£­ñ–*y³Ý6¼)7TB’ðº]Iqѧñ-åõ72©Ÿ”Tr­›‚2+ت ¨#þ^ä1Púzð¹ W O©'NIhO·‡"-XZ¦ð;ÈÀÐÈL¼îŠñ e§¼q½¶-ÄÍÙ0(w½D Ðúõ çÑhñô »èÐä|Šqƒ+£ÅntÜëhƒ ‰ÐBн%rNäµÜÑ>zä –ÿ&òJ>ë8·ƒe1ùÎCêÍ KškG†=J5“ž½¥O;qµJü×”ÙÜ”¾þ»~£KqI‘ÈH‡Âôlmmu:·ò5 ‘™EÜÍÂ:ŽfZQ‹mD£™_R>öÕb£ƒS^º8G|ÿ]b¤üÜ>î±> ‡ÿŸ¾óØþ!úßøu§|üIÔ?¯÷)Ùç6Q¡—LÊ¿´JêΔ³dädÙÖAX®ì”Qú‡›ÌX/CrDQ™ÍKéë!À+|ÖÎî͵ã…éi4FáeeÎwvdUÖáÁaÝ|co*9Ø1)7Å߈۬e‹k8‹”³ŠjV®R¥Z"ÿ®­eF#°OþúL>·“á­%¥Ü<$_ô’§ú-T—¥ÿKò•Žj)ßSv~ OÎÉúÕ²|©œx&ò@†7”\WäÂ&+³y)}=XDž/%íözèP˜ž…EØþ2€¬)k½ã#³ËV(l¤ï‡qu<ÕÊ]ãm8ìä­¤ôú –›Ï 9Å-iM’JÙç /ðóÃúË&òÿh-ý·ˆÍûòñ2¯‡Ì‹X¬Î-]&ËÔáRXé­òT’»‚|VAúO’ƒ¥Ó¢°¢|³BºM¨ƒ²2›•Ò×Cöˆ3‡çwÂv-ѳ‘ t(LÏÎÎ.44T«Õ&¾*d:Yë™}©°ËãNŠxž ëü+ê|äÄ}Ô»”Šëþs±„<““†»èYV·S'@Û–Â"—Dý%µ‰ôá}Væïš?ʶòè©©%{nqË¥ì>/!ÏåB ”ÏÕ«Ae#gJÏõ²â…xž“×oº””ÙŒ”¾²»‹«„5çßÜ'Þ’3±ë3С0=KKK•JõúuÒë sÉRoøÈ4"ÇŠÙü¥É'ŸÔ”“GÄï/9ã#õb^ö÷ì ümøÊ¾V_ÉŸ„aîoØfÒ¬âY*y, JÇbréfØÕóÏÈô* ­ìwNÚ6’S9åpqvçÜÉü£¾G¤Årv»¸FÏÝÖR4—È ±ÏÞRßAVTfóRúz°+#³Ë¯/%p·l¸'}»Wb:¦§V‡½|BCCÍ]042"‡“±†1JÈìn‰+¦}(_Œ ?A]E¦µLÖ­¼“ÂZ>ýRfw_‘­å­mÒï˜e ÚÐT*ëÿ'Ân+=¸·4)#ÑzXØJ®‚R³¡”Ê‘¤?ût·¬ýW‹Zü@¶ß›¨ßNŒý½ã=ÈI/³Ù)}=8ɨÏå×±a½;v‘⛥AäYŠN^¾|cûèP˜œ½½½>=‡„„˜» `hdDÙkHm 9¤•ÓC¥ôFyÛAÿ'ãvI›œâPMv¬F=åÔh©~Y†t”Bòä¢,ýFö>yK–o—ªqõ߸ô›üüRrçkxž—ÅßÈ¡ç¢v—M›¥TƆ‹Oh°›èB^‡uÃ}ó?._gÙtZšÏà'2 ²Lp—*%ÄA->ÏÅó®Üs¿NJYë°Ëþõ§£â-rzCØ#N-fÈêÏ#Æ0Ö‰Ä0þþ1VÓédPiÙ7P:Õ‘¼vòø¢,›&ÇuâÚI¦7 | 䤖9iG@]äÖ‚$VÛgPpì =¥¯‡ÒÃä§òÉf :. óˆ{)á"¯_ȵ3ò("_ùGž’܆çŠE¦bm¶[ c k"@##²((+×IÏArà¾Ü<&¡•¤u©1®sÑäRuùq–¬Þ.¯4ŒÂk)…+Èg#ÅãS)Ooæ—Gdâïr÷Eø¬[ùßXù¢ŸTze^$/éW[^ Ÿ»4RlÆJ‘Ú²n›TŠ>¦‡>O—;-eÆ·²õܼ,;/‹ØJ©*Òt ôé¿ÜºË tî%çâSoûpñ¨(‹«Ëàú²ìdX˨ј²2ÖIj Ý_‰ý;2w„ì:,'~’ͳ O[K‰*òÅ4ÞCrD´ &t“Xæ$$ ñïÊWGÂsó¥b3Uz.“¥måÞ2iß.…M4þ@â^%=1÷ñDJ»pp'BYHùå¥üb¾¿o[LÜUrÌ_–í“FíbÃüXÆ¿''ô«5’éÍ2¸iæ>žH)}€Ö§çààø¯h€Ì‹,M'/ŸŠq [ï‡â,N‰Þ¾ÙL4dÑ,©3TÖ´—;=¥{c)â*!òâœ: «W…µOïva±î"$Ϲsç~üñG1ôÙðòòÒOxzz†„„øúú6nÜX?û2b˜lWW×íÛ·›±¨ÐȲ‚oHà òWÄÉû{K¶Þ’½¶ü½7E?§•”õÈüeó~±B‡x¶’%¥Ö`y¿·´.Ï4RGáÂ…W¬Xk¹V«Ý»woô%ƒ JÃr€yðq‹,˲¸ö3w!ÊQAÆ,’1æ.²''§V­Z­_¿>Ñ5Û¶m›åó"@×­[·DtŽ9êÔ©“6å3"@×¢E ggçW¯:¥U«V––éõ:04 qÖÖÖ;v\²dIëÐ@A€$I·nÝж¶¶Mš4IËò€¹ IR¿~ý|ùò=|ø0Îg7nìànâ)G€$‰Z­îÞ½ûŒ3â|–þ²4 ©ºuëg€Öh4­[·Nûò€Y IU¡Bww÷Ë—/ÇZ^«V-³ Ò @·nÝÆŽk!ý7d)h€]»v7nœN§‹¾°]»væ*¤=4@"EŠT¯^ýèÑ£‘KÊ•+§_hÆ"@#@”éÖ­[ôMÿ Y  LçÎ=<<´Z­q–þ²4@WW×&Mšlß¾]?]°`ÁJ•*™»D¦Ðźwïn Ð4?È‚ÐÅÚ¶mkooïççGhY ˜>=ë£óÎ;ëÖ­kî²@Z#@’£[·næ.¤5ÞøÉѤIs—Ì€ i§}ûö»víÒh4Z­V­V›»8ˆM_/–––!!!&Løâ‹/Ì]éÒÎÉ“'T*U¬[a#ýÐWMppð¥K—Ì]éÒŽ>=›»H„ñ1÷îÝ3wA¤_hH;666bhã4wA/cí¸¹¹™» Ò/4¤Øýž­DTf* b ÑFÍi4ó@zG€3qÙ)ânîbÀh©ÈD_s@F@€3Q‰8‰d7w1`dgîÈ8Ѐh@4 P€ (@€ @  Ѐh@4 P€ (@€ @  Ѐh@4 &ˆVÄ2u6®ß²Úð€ ‹ ˆpWd™Èb‘EÚ¼ñ¬ÎðS•²?qÚ°åî"‰¸§lS`&hÈòüD~ùIähĵH¨HM‘ã"¥E¾é"òTäšÈ"ÿˆ¼%²Fd¬ÈŸ†5ëˆÌ7¬éºÈx‘í">"¶"ÍE¾)iØò‘Ù†GY‘O a:‡v’ Y•>ø4äæÕÑêãì§"µ -ÍV†%ÿ‰t5¤g1Ä_kÃÄ=‘††ÕZ‹l9,RWä²HnógEªŠ¦õ©ú’ÈF‘m†æçʆŸ‹ MÝD­DúŠ4Kµ~#`RhÈ’nŠÔy1[Ù›ßqˆ¶Ž±§²>:76„c'Cª6.Ô‰Œg˜^døÝ"?‹Œ4ôrîkHÏÖ†ˆ\ÜÐ3¤´¡{°È.‘Іæê¹"¿‹,0„ø­†‡þOï3$oHßÐ%½0¤g}ö0äÝ ®O;GLÛf_:{GkÕ.mýÚñlÜ×Ð&½ÍÐ> ²¤"† ø–‹\54B{ÇøÔÐlë“Á*®‘7B£µ%ë'B ªˆY‰9ñ&¡Ûô‚˜Ý¯;‹”Oή@#@@–”ËÐwù;‘M†ë÷‰üax8‹|,2\Ä5Á_÷2´7;L¿0ÌŠá BK‘¼ë\4ecª^kHêö"ÆÈÓÿiψժ¢|—˜Ý¯ #@@fmH®] W’ôMCwä:qËtÃðvjÃO£†¸œßp™à‘=†KK†ìøÔ²«†Æ›`X9¯a(è>"…Sqÿ 5 "y ×ÿ0Œ1·8®Kc±0ô¾Øl˜¸oX’Ï0D´¡2de­HÃÝR.G,Ÿoø©ͽ =…÷d3!@¢©dèb‘(;‘ý†À½Ï0ÛÌð[‘—!¾#rÞp›•†ô¬_¹…ÈWc},I•‚@š!@âq þ§B CÔí…R"L^ HЀxé"zY¨b©¡ó5ÜWåÍåñ­ÏrȈЀ¸éSodÞ1­ÏÁ†ñžã_'žiÈЀ8ÄJ½úé¨Vd‰gy|듞d.h@ÞL½ñå`¥Ë £#@  Ѐh@4 P€ (@€ @  Ѐh@4 P€ f ò™ˆ£¹‹£»†€$ @Hׂ‚C^…FÍ:ZilUiñwu¡¡/‚u!³*•*‡•ZcÚ¿¡9lÚ-Ò@zzùîÓ __ 1dY«±uÝ>L“äï3å²÷)Ÿ ‡Zü}Ž]5³U§t³þþþ&44T§Ó¥¸ŒHjuX5{zzš» Ò/4"!¯ý&Ÿ|ò«Ÿ¦i©|s XZ™»<™ŽºB±¼›‹é®\¿×ú¶6-ÿ°µC¶9U³IhÐGîÏö3ÙfU*UHHˆþ§É¶ˆT ?ñ³³3w)¤_h %t¿øÕW'¢ÝuëÕå|.Lü?2ggg??¿   KKK†—Kº£¯N§¯s—@úE€RB•ÃÉ&=±s²Í—âï÷‘é­^½úöíÛ<È›7o† Ðÿgï>à›(û8€ÿ.³M÷žP ”Qö*{‰L‘!2Ê‚²E6" *²—€²d¾€¢,²G™e´¥{ïfÞ{—¤{%Pš–þ¿Ÿ¼¾—»'—ÿ]BúËå¹{>|¸yófkkë™3gR¼Z­þòË/úôéÓ±cÇr±½)))ééé\znÔ¨‘©k!„”] ßÈÊ=µRù”ùHôÙU¦ÿðDž!¢Þ{¯Kž®=mñžõ˯^²,eqW0ÌšäpêÔ©ÆÅÅ1 ãééùË/¿‚¹f'Nœà’÷ñãǹ=nܸY³fQŒ&„¼è#¬S+åGŸÅ}ÿ<ÍÒ»Ò>o‰”ŸÇ> ‰=¤¤öµ(h‡¿:zɲ´+J?ÎJ+p‚NLLœ0aÂÞ½{•J¥D"™2eÊ×_ÍM¾†êÕ«:t(00b4!äMB^¦¡–§­L:Ÿ¤x’¦ŠQi¯f%Ö°µàm?È._?AV}+<ñ—ÐÔËIŠHíudR±…0:>#Œ…‡§ëæj\˜cŸ†Å}’òw‚¶Ezü»'ãµ kYy¶µq} ’b"»\K‰ÑÝXléèÚV¨y“´=$åD¬¢R¯’èV×Rõc˜Ù¬ª-þ/4ùL¼"HÉm ãleÞÅÓvœ§¹sAÉ#19egHÊɘôûé?ƒ±=db;›Y>®Fö‚(¶ÔîB}¬úZhüÆ©“TüYa5ó•ìFºJ,ó<©A;Ð(~¾¶*^­Ô=ÊÒñŸV6•r®J£:¿!4õjŠZÎo¨ÀÉ\ìmeÖ¿¦COþBl†½dÅ•mÄ0@JL¸ßµ4…þžùª·ÜZÉÓ†&J»‘¦Ñ0/;‹AÞCí…B: :ùPDÚ¹øŒÇrŒ°–³ÍôÚ¶í¤ù^Ââ÷¼áï^ÆŒArjÚ¡Ðä?¢Óo¤¨`Ül-GÔtl›¿ƒ-“¸98åt‚âÿ΄P$òµ“½[Év “¸ £Üš è¤M!)ÿÄËõ…„Bws‘0ÍàÝ÷f9zôèÇœœœ,¼½½ÿý÷úõë¿Üª ‹Ñ‹/>|8ÅhBH¹C[¦¡”§ýú"5ÕÚrPuû¦V"¨®„Ä.‹Hš›ö¨©ç|‡ìÄê寇}§Ø|˜kGk¡X¥º¿",ƒÿoå°µ¶…®ë­¥Ìü½*b‹ûÑû¸°&¶øÂת¿AU™Ñ=s­].uq3\ͽ´?\ šÌ_YȀ̄ÕÅ%/ŽË¸î)yžúP{ef!£ßq"ㆧ°ÿki_;Ç‹hàž7øÝ«Ü}+xv‚J$:Šn¡‚eÃ’þ§D÷Or6Ö(÷ß ›ÎaFòN5Çé6" úQlòƤ%ÑI[]·×³ª’ó‹EV{êºÛvºŠ¡RÇfÈ=•¹Ë»ØØØQ£FqZwàyöìÙ3gÎ|õ˜›£?ÿüó?þøƒ‹ÑãÇÿâ‹/(FBÊúÀ2Ð|~—ž™¿…6²…Ƈþ*Wíz”:¶…µGf‚ºû$‚s|æç66óx]3sM𨠒Sþɰц ÆÙVÖ™U¾¸"’´r¶x¥Ó°P´Sò-!’þ^.Cm¥U…³/F%hÛZš7—1kSXdë;¸½%Ö?ŽÕ¨N<¢ÌHˆy_z²®Ì6s[žE }ÂÅPA—z•~pe>ïxÚ¼+ îñšJå³Ýéû|†[þÜÒ¥«®ï…“EG+t¾ž5ËÑ|ƒ›îø¥á;<ûÙÍ ú†½ŒO6[š:6ÒJ‹w]͔磲YÌKfxÙÅîc˜›IYpupéXP«²Ë*'i)?ŸZ)ß?b1•'-–4ôhd%©,p"òŒ´×Â7rߦR7&Ú~gŸ¹†n‚áï^Aco÷-bëÌÔþïã°Ožq):c}b@m©™¾™æÆãpmz–Noé1ÊJÿ µw±úÐ%úý«I/"¢>‘ŠÔ2³Ìlõ‘.= {6ð\î*Êþ  Q¤†%]+¹ë@—}{öì9r¤nÀ—ºuërwkÖ¬Y‚ëçbôÁƒ)FBÊ5ú¨2 ±Äüc‹¶â³„â2üÊ¥‡ôŒPµ‡î¸:cû íoÉVÖïZä mÂæž2‹ˆ”TÈĨ‡V½Î«CIÆÖwÊûMôiåÄïäNŒŒ$Wžu­å:).äÇTćÅî÷6®›yê¢ÇüA\¡½ÃWn"qî5Ts6sˆÂ++¸TUZò·aüÁÒêUí;åè¹looóŽ$å7{öYr¨«me¦w¸æA¤\ÍwO1÷ÎÑ”›«n¿“CAŒ(»¸=ð²5í̼3«Š¥}k;þqJ #ní`–ÕßFj&›XÛê÷+É ÐÜKR«ì¢—ß„bJò°e÷é`„­«9ô ?ªBT‚<ž•ºi©ÓS=ç{Ö8z:~l•kí¶ös\RÆDj^Ũì1Xûæäê\Ä¿îÇY.¯õ_S™ñÉ'Ÿœ>}ZwàyÉ’%“&MÒ ËWâ(FBÊ5ú2 ¡™Å(¯<óý¹JV•9įZ©| í`+6Ûåb©ØH"“U ˆÌ_wÅ™EÖ­á¹¹ØVqŸÊÒïsßbTC+‹¹Dx–ï€&žù/÷&³¶û¢šÚµ$ÿRg•ʆÇ$?â'„ÍírÿYˆËð›HITÙV—ä×½†lJê ¥Eûì¯ LJö5 ªßˆ² aØ‹e F(®+Å©4þ¿©|w÷ìER3‰3À}T²šÛDÕ¹h®âKJӗĆG'ÝÔ.kê,É××YØÔÍL™¦„|O´ê#/îÍɾˆL ÐnŸ_%s§Š:DàÖ­['L˜žžÎ…×fÍšíܹ³jÕª¯ûI)FBÊ)úx*ÛXV—à ü›®›©aÁ´Ô¤G3gÈ£€çqÊŒÊb hG˵‹D- 8¢Åˆ$ïy¾¦bØ 8Ýi~B7."³¹ö–µˆ?™¬:BÍeäÜáL51bP$¼ÄN«eÿ‘£8琢%Xv)²LÖåÞ²÷O)mBþsŸÄèNƒÖ6/ \™…Ô iÁÀÓXeš—Ø š‡±ºö¢æ–‚ ˜Ÿƒƒƒ|ùòe¥Riff¶bÅ ÿÒl¼À­»RÅhBHÙDL&¢Q] OÚ‘v=Y©ÔÈ5ÐÔJ(» p_…\•Ì"gS)”qÚ gKa ]O­$‰Ä"[ ŠïªätH†.? =Ä¥QX6R®{jÅòóO—ÒHÉÇ;¦äv8ãåá8ìEØ–T°i)Ë®§,IÚ¹Yöõ´îl-4(.S¶!ë3Sm« Õ?¯À¹ ¼/‰t—í–k_e.@?ÓŸ*¬")«;óõà¾0®^½zÚ´ir¹\,·k×nÇŽ&)&OŒNLL¤M)³è#ÉXUÆÊ+a?'ñ×%SÝþ-‘ƒ€ý÷NÈœ<ç| ¥ï;0§¢Y$'Ÿ•[õÏ>[Ms7<=™Ÿöq*‹]6@YVÛ§˜M×E`ÌKûªº¬>ºC<º‘s—‚ïÀS×Ó·äv8#6ŸÕ¢’o`ìO!iÁ>€Ÿ ‰ãn®Î?×µmP|ˆ6¦ì2ÊT›ýf“ôfýŸFÃ!g5ɺö…œú¦â¢êÀoß¾­R©d2Ùš5k>þøcS•£¹XìØ1ŠÑ„²‰>ŒJûèyŸž!™ÑÌÕ?ûL5sþ\Æ;ÕrhsA1÷F¬™¯m'+¡H¥¼7íŠZC§,Êâß|V£Ñ_KX  %,c‘ù²º”š2™?ã3–ÒE_Ô¯Dw¸@$é]Ëíýê1)ÿ OÞ!"¢b?¾-ü³±•{1«1¦ì2ÊT›ÀÈ2ßlò‚~ÙÑht_êø7§6Õ3úEXVQöºC½æ»ï¾ûꫯ …X,îÞ½ûæÍ›M]W6.F8p À=lØ0®fSH©è(@—:Vu>RÛ1ÔÂò­‚:hæ!‘Ù¬oÉL¾}*)ñ³K‰Yó­-?­nïï$.ƒý78J…R7†­…ˆ¿²#ðä6–ÿÚ z,JëœGÃ8KµÝm¡Vß¼Äw8#Öv¶án“k¤.¸±+ é1ñÒ,ÇÄ,»,2Õ&po63Ý›M©*àÛšZ©Ò½®æf"m/»þƒPªdQÊ]ŒJݽ{÷ú÷ïÿðáC.F[YYmÙ²¥wïÞ¦.ª`£ !eèR§Ñ„ëO¬t(“ÕÜO:£1›×Ö­+£ŠUA 8š‰ìÊ`¿â“åÚ±ñ_G±6- ¼$ˆä²³úrœJi[šúO;1¢ÜS_KP©mÄÅì¹Úá )r¹Lê’£ ÔÜbV«ãüµÞÔ÷3XóòYvYdªMx;JŽÙTü~ÎwòfjZF¸v¢º£„s2ÂêÖB$©¹ï}’5Ãd…'Yî©Tª… ~óÍ7ºÏ\n^³f½½½©ë*†.F?yòäóÏ?Ï£uWê M1 Ð¥Ža¬u9B©ŠgQÜÈÕšûAƒŸÈí*y¼/X@âXüêµÿDzêW/õ¥±ªBµ×ÜÊÙë㈳“u3&ú ‹'ÏãÎxº¼-)r %‰qw´ðz¨î%x:Ô/*ǽà [ÏõáÛ=+mrÍ5Œ¶D"¶ã¯õÆØçi…¼dF•]6½ %õîuq²nÌD_gÙ«òTGs‹œËXõåPí ’ŒÙ}wv¦†«¹Í‹”D°ç‚Rܳ†1z£Ü¸q£_¿~ÏŸ?gYÖÎÎnûöíÝ»w7uQFðööΣ'L˜uÁ;ŠÑ„RFºÔ Dõ-¤±HOÚc³ÄY?^x|ʱ”¼m5òÔ¯¦gá13`^C*åÈ]f"›¥YK;‰mÖLFà¤û;’‘~9­cšîÑlTTì íoäõ½í;fe¡™Õœª‰ï=U@™2þŠ`i}‡^V‚ÜŠU°Ìë¸ ‚ÄÂzŠSâ§Ñ¤'Œº#ÚXǦnî¿¶º«—½Ã‹¢>ÿ<%ÄÙÆ+;A³q©Ï¹ÿ™wÎzi É /»Ì2nJîÝ+4³œ]%áƒgʸ°Øí^crŒ¥·(šïé\µŠcÏÌq -íìFX¦|Ÿu|̤'âuÞæŽYC»k4‰^"§üËå_~ùå?þ¨T*¹ ùÑG­\¹ÒÚÚÚÔu½ŒÂbô¢E‹FŒA1šRj(@—>A³ªÖžQ‰/ Þãù9K©»€NS„Á¬‡µüíTɪ†¶"Äüc'Á .¨äÇCäÇ \ŸT6³‘Ë]4r3Æe¨!_|!䄽X¦QÇIí¶Ö·°5¾Ð¬ñ\Eâè‹äD™Ð† ›*Õƒ˜¤µ! @õÊ®k«HrhfêTw[­ Ÿ¢P§$M»ô•™Ä×B¤-œMS¨ž¦²=›Ušeý2±©˜RQº®×Â7$±1‘1ïGÆW·“V•0–—«ÂÒT²Ê«ó¥¿ÃÁ²ýµ&XMzî·lbL· éC<-šYŠÌXÕ£ØäõArÂnµZfÿ¡/ü%3¸ìâ÷€‘”…¯M¿ˆeóôjÖhô‡5,›ý c6¡èwoÖ* /I•=OРºÛòŒ°ÏÃåË/½x\ͦ›XƪÅ&­ J<Üœ7U—f±"Œlätûrô_ öÖ“°Á’f6b{›¡¼—¬btëW*n§¨=­ËâU#‹péÒ¥?ü0""‚›vrrÚµkW‡L]Ô«Ê£'Nœ8{ölŠÑ„RCÚ,l~k$øêQâéTMtŠ\)3k[ÙyuK÷Øðãqé6ã›ÿ‚–Ù:ö³ö`D½x EL V¨ [ýôS;»‚߇/^¼øøã/]º¤T*ÍÌÌ–/_>f̦œ×jJY1zÚ´iGŽÑ]®¤°}úôé³gÏrÜ«ÊЄ|(@BHÙ²yóæùóçoÙ²%Ï"–e×­[÷ÙgŸeddˆD¢6mÚìØ±£R%C.²Mx\ŒÞ·o_±1šû£›àv8ehBH~  !¤ ¹råʸq㸉­[·8°K—.Y‹¸ØÇ͹yó¦J¥277ÿù矇jºJ˱œ1úÿû_Î=bĈ .è?ëP†&„äGšBÊŠèèè>}úÈårÝ]ÿ»wïZYYi4š•+Wr O¡PˆÅb.UsñÚÅÅÅ´Õ–w…ÅhGǼƒqR†&„äAšBʵZݯ_¿/^dÍ ž5kÖ„ ú÷ïÿàÁ•Jeii¹iÓ¦¾}ûš°Î7Œ.F?{öìóÏ?çbtBBB|||þf”¡ !9Q€&„2aÚ´i§OŸÎ3sõêÕ6lࢳX,îÝ»÷úõëíííMQÝ®jÕªºíççS`ÊЄ,  !ÄôvíÚµbÅŠüó¹Ð¦P(lmmwìØñÎ;ï”~aJPPPaéY‡24!D‡4!„˜ØíÛ·GŒQDƒAƒQz._ýu±mtšûïäÉ“_E„2Š4!„˜RBBBŸ>}ÒÒÒŠh³víÚ!C†øùù•ZUÐéÓ§Ïœ9cHK.=O™2…›  MH…EšBLF­VôÑGOž<)¶Ùðáï]»&•JK§° ÈÃÏY(CRÁQ€&„“™7oÞüaHË€€€Å‹sí_wIÓ¿ÿþkàáç,”¡ ©È(@Bˆi:tháÂ…†·_²dIïÞ½6løúJª°Z·nøèÑ#î¿?Ô*öœBÊЄTL  !Ä>|8tèP.ÒØÒÒ²mÛ¶:u²¶¶~Ý…UX¶¶¶MµrÎLHHÐ%i.Rë²užTMšŠ‰4!„”¶äääÞ½{'&&ÑF$µhÑ¢“VóæÍ%I©•G²ª³‚õ¢E‹;C8p`À€ …B(ªÕêÒ®›”R©T.—ûùù]¾|ÙÔµ£Q€&„RŲì°aÃîß¿ŸÃ0õêÕÓ…æöíÛ[ZZ–~y¤X¦jîë÷Êr¯ înDD„nÂÀHÄ¥gî¿wîÜ1u!äeP€&„RµtéÒ}ûöåœS¥J•N™œMUy6669ï) nB£Ñ˜¨"Ræq_·r|é"å hB)='NœøòË/¹ GGÇŽ;¾ýöÛ\hööö6u]¤„yyyI$]†&¤`Ú_'è7ŠrŠ4!„”’ÈÈÈmÛ¶-]º”ËÍõëצ®ˆBÈË M!¥ÄÅÅeÇŽ¦®‚˜ÂˆظÑÔE²!1ÎΠ_'Ê9 ЄB!„4!„B!F M!„Bˆ(@B!„b Є”K7nœ?~FFÃ04ÜF¤T*år¹L&ëСÖ-[L]!„’DšréâÅ‹!!!@£ÑDEE™ºRÝ«sòäISB!¤„Q€&¤\ g†®À_–q¯—¡i( ByóP€&¤\rtt¤ô\ƱZB¡ÐÔ…B)a  )—$I®û2¤RÕBrS«‘œŒÌ¯7b±Ø´åB)q  )ÿ¸ˆ6iúö5uD+0þþ|†&„ò†¢MHùÇ0¨\Mš˜º¢%‘€ºmBÈ4!„B!F M!„Bˆ(@B!„b ЄB!„4!„B!F M!„Bˆ(@B!„b ЄBÉáp'¼÷7 Ét¶ÐÁgË¶Ü È±k9î;bê(Ø0¦,U“ˆŸÆ`ñïˆR¶8x +¼s—JÈkAšBy± ¨Ä¿zÀÀÕ vIpsâ&yi71uÂaÄHؘtØ §›ðé.~Âw0†û¡¶EÞR‹¦J‡ÀÜ –„äFšBy“°:‡-±ñV?A/›W^¡5v=Ï5#à7„¿òZKDÜ3ýÄ”%éÁOä)µh×g¢× òLjAðµ-ñêÈŒ4!„òFH}†=›±a.êâ­0óت7·áË•8u 逕7MÇâ‘°Ó.fSñë,ÌÝ‚§)°¨Š‘‹Ñ2Ïzstá^ŠöÞ¸£›^";#ö Hé,íÇWËqì2’Y˜{ û'X< 5-²›„ŸÆü¥8p‘| ñi‘³0¹+¤\ÉáhåŽË€ï78Ñ S§âÀM(¥h5ë¾G]Ëìªtü=áì|Œµ>¹ºp°)Ø>_oųTØÖ‚ÿô<‹v?b‰ ƒÈ[ø~«× þþÔ öHñè]B!„”gšœÙ °óræ,W …ÑÃÑÆ†²f£õR~¶g+4³Ä_'°v4.¼À…ù°Pã÷¡¼_ʸ¡®Ö ÄÑÂÅ2æhÑág¾kGÃfpnTp”¸¹~³¡ä¦ìPÇ °ŽžÁõ“ð5ã<^ ¿±\ æ×ãSIñø fœÁ‰U8:R $ÚõDDÇÙHñ»9‚Òqa:©ñh#lDhÒ Q×q?žoV¹Üíá*Î]„{†`èí´%Ü5Xþ!NVÓÞñ[š~‹ëí°~-¶œÄØÄݬðî0Œ‰nõge„ä@šB)·× ý8„eÞmÚ£Ç`@{XfvìU<Ähmzö€Ÿ`Í x3jŽÀí…Ø1Ÿ$b¦6=KÚ#àªKøÃÉ ºútB7¬ÿµ±ô9ЇþEå‚ú@«žb”6=KÛãΟð‘"h+ê CêyLÞ‡?AŒá´éÙgΣ4ñ˜Ùß>Ä©)Øù>±Ô>ù ½c]Ob0ÌÛ¢µÿ­äÏnüî/ôŸˆæ«øfs`¸;´kÌ&¿‡ÙºôÜ×Ï¡‘%Âö£ÁÚ9 øÎáb4êƒ5}°2¶`í:œ Æ‘ù›eœ: ?™ñ/ ©(@B!åVÜ=>= ¼1å3Œ„ÖyÁ]íDøôÌ©Ô Í8£Áö³è’„§Ú¥oOåÓ3Ç¡ÆÖÄœ‡FÔü ÷£Àêî0põsW´÷ºLãÓ3Çk Ž;"FY5¾eøqœWóó;ÏC[;~B`ÇŸ˜¸|0X%¶œÅЙko€9ݵiÅûaÛv O¹ lÀ%6"Îá‰v¢ÓhhÉO¸¿ƒ±ÞXð$oK©|³r›VcùoH¹Ž¥;T0  !„rNóGÀÓŽÝóvá| ŸØÐr?êù5¼Èœ®é‘9%AýÊ€1úúWè°#ûîˆóyK?];kµR´y7»Mè ýDCod]&ÄÚN@ð"ŠÌÍnÇÌ-²vÕO(ÔýH?Q·jæ³HÑ´*/@ë¤<ÅŸGpôR Z=©È(@B!åVµ~ðÀ/ÿàÁ1LánÖè5£ýÑÅWÿ^˜Ù“·Ö;ðsÈõXÇFfY–Íž¯6,ž!km9W[tKíýÿ39º…rÆÈKòe­;çãò—Ħáܬ]›£¹3úBê¿A EšB)·Ûbýßø)Á†8õ‡Wò7Û9ÓFÀÅWß²ùtlm—÷áÏ“ôÁ`›jƒ¦—ŸWCûí`·ç^íMýÄ]nµô«Ýý¤Ã¢.&}ÏÆú7Á6ÖÜ„‡üágNµú3_‘c5ýD@PöÖ]}–Ý éV.ÇúíÍLÕÍð×âèß!»9!¡M!„”sR7ôŸ‰þ3q Û6bÃ6ÞÂò±h;Ý:£— ƒ+Ò•ÄHøý?E†zƒ1mà§–ân7Ô“!øw¬}^ä“1èÂe4"å¨\ÐaZ®hÄ_æ'—â^Ô1Gð>ŒžD Å| ¸vE;Ϊpj>.uEK¨¢°döÁ–Û%2¾¡[TB€¿–àV74´@ð~¬ÎñõàÁzÌݦmÙ#ü1¼ªZ¶2Br¢M1X‰ lf"4êyÃ1pmŠéÜíG\?‚{`'„¤&ÖLE»ï‘xU*£±Ü@ i‰U5`&Æ—ocä_Ð\A}wÔqFÀc4öÀõP¾DÁý/ĨSü¹‡/Ц´ÆþðÌ}-±76ÌC‹¹P]B]7ø:âž¶Û±Øk†h£Gel\…fc€VΨUÑw«}ì Íèé”÷z/Ǭ>æj·Ž½†F.¨áŒGÏÐÐ q™ÃÀXTÇð¯0t8Úz•Ld'hBH±J|`³—(AÃ_,öÿÂѨc¤¢ q¬î£¿×ö[üW _þ€p52O|0‹f¡¦9¿tØ^¤OÆÂß™ˆÈÊøâÞ:ˆ·· dØZ€÷W¡_ì~EŸz ˆ š|‰Ûµ0gŽ_ýDþI{ ♨‘yÄÚg4îùàëÅ8pîraMÞÆø9Ò%6:¸Ãö u"íFT*â,1ûÿŒÂõÝ¢ëLƦ’z.R±P€&„®ÀÍrŽvÈc¦âÔsˆ\1bV ÄÅ¥ÿ-$Á¶¾ÞŒ‰MÁÄà=/ü/ hŠçù¡Ëx*üØ “oò£ B÷Üq6ë)ê,Å*9úÏET<¾j1héŽÿ¯Ï±«&æ|‹AcŽ¶Ã°fêè~{-|ü3:F*&šùã¸! m1áþ–­ØÍ9îÚâtîcѲ:Øu»Š~Rµûa_¿¢š¸¿…õÜ­Àeùž”Óð°ßäšã÷ØŸŠz”À“vð7= ¶.ÖNXÁªÄr:©€è/!$Ÿ¢6cT™#„ÇÛs!©Ê.RE`Ýà ¶¯ƒW wðiÔDGGLé‹ÿm®âh(ÆyñU‡áWíiFŽ}Ñ6ßÁ`Aæ dÑaà_ÚóŠ´/Rh/)‹àx+ mÞCOKºŽs«Ñ.÷vÂEPÔøg4ê!¥ˆÍ¼h“ûÂ¥7_é}q iqéë¥ÿGÎ/–ù¡†ùKl!:  !¹;°™î84'挺Œ…~X„zsø9ëÖau?NÁߣÑiŽÑ–èØ-&¢Ò6þTž-G1jÿÁñ·~œ…Ac`™¿ˆÌ§ˆú WàÞDX«Áˆù­›ÏF`ÖM|Ù€?’½¾FŸEÜlÏÄÅŒF£ŽR*¸›•kM9}h–ŸŽqé™éÐÏ!TÃ-f}{êõL^hBHnÅl–­ Æ4áæøôDÕ9xȺc`u~I³~°ÜÀŸ¨ø‚?ɼ>ÆÔÆìû¸º¡£à%À™mÚó“¼1¢~Øye=Eu¬ËŸ•·Sds ¯§¡÷XŒ9Ë?üW1 ¹˜ñÏthÔ1B^§œ)Ú#Ä-.Õù\Þ9}B;´îÁLŸÏ¾Ç}Äå=tMˆá(@B RÄÀfYd^°ÕæZ¡…þ(²SM˜i'„–ü.@«|le$4sÆ€½Šc¡cmøfuF¡–ò c¶±Ž~ZR•¤<µ] Øf—¹ÃŽËý@ôSe9/lü³œhÔ1B^üÁ´°¨ZÖæb Є܊Ø,‹ ë¨pæ/¡Qæd¾ßF+õF×Oq<ÛN¢ŸÎËù™ãúÓóXbQð•74êìÃɬºK0ƒÆ?£QÇ!„¼ Є܊ØÌù¥V+p”Þ8¾ÿmÃßÞüA_Q{¼_‰_”³b/›€h,µŸ`qøÑ8.Þ¨œy>bãŸ}R«iÔ1B!¯Š4!¤ E lÖëåÖÈ Íd¸ïDØ9Ì¿ÃÏè2n/}©[Xv «ºBˆUßéçõñƒ§¦¨ñÏZ¦QÇ!„¼: Є"4°T/¹2YCøû`ÞcÜMà/ÿ<±ÓËŒ"ª‚Ýp¨DAIçç¸Àà*ü(‰EŒ&Þ@£ŽByu  !†È9°ÙK±+ÅЉ˜7‰Ÿtì‹6¯0 ¬9þ^‚é_àT:ŸÅ»Åê¯a¯í†QÔøg4ê!„@šb¬|c}‰ªávžÊš#¼ÀÓø2»[ÐåŸ Š<4ø À_ ZfÀøg„BÈ+ M)-Š ÌøŠŸ´ÀĆ9—=ÒXÊ^X3F´g hL!„¼ Є×/á4÷GH(´Ý•1ë'TÍþð1zt1. [~ˆ°=°2ú±„BÈ«£MyýX9ž…ò#l[×ÂÔ0»iö’—±,ÇŠ™ŒByu  !¯ŸÝûP:4F!¤Œ£M!„Bˆ(@B!„b ЄB!„4!„B!F M!„Bˆ(@B!„b ЄB)ÿ4‰øi ÿŽ(`‹ƒ×°Âg€Ìq—)A  !„òU@%†˜)¾å›êé&|º‹ŸðŒá~¨mW/Ø%ÁÍ ÃÖ J‡ÀÜÐÆ¤b£M!„”_,‚ÎaËFl<†ÕOÐËÆÔõ˜NÜ3ýÄ”%éÁOìznÜ®ÏD¯3äƒàk[²Õ‘7 hB!¤J}†=›±a.†kï 3²x´_-DZËHfaîîŸ`ñ,Ô´Ð?0ü4æ/Å3ˆÌàc€OkŒœ…É]!Õ.Õ„£¥;þ¼>Ç®š˜ó-þy9ÚÚeðMÇ{^ø_ÐÏ/ÂK—"Tø±&߬q,Ý Œž¯PWR+w\|¿Á‰f˜:nB)E«!X÷=êZ è`Ç÷ÖÐñ÷„?°ó1ÖúäêÂÁ¦`ûL|½ÏRa[ þ Ðó,Úýˆq$ïXCÀ ò¾ŸÀßêõ‚¿?uƒ=%%Rz[B!å‡&gvcÃ켜9˃Faôp´Ñ~¾¹~³¡ä¦ìPÇ °ŽžÁõ“ð5ÃãµðËNpqÛ§’âñÌ8ƒ«pt<ŸVR}’Þ·’Ðæ=ô´Ä¡ë8·íbpo'¦ôÅÿ¶Wq4ã¼ø–ê0üz“Ÿpì‹¶…¸}¥ª$hWug#ÅîæJÇ… è¤Æ£°¡I'D]Çýx¾YåFp·‡«8wjì‚¡´Ó–p×`ù‡8YM{W¤ïúÒô[\o‡õk±å$îÆ$îf…w‡aÔHt«‡<ë#hB!¤œ\ƒöã–y·i?Œƒía™ÙoWõ£´9UÚwþ„A[QgRÏcò>m‹á´9ÕgΣ4ñ˜Ùß>Ä©)Øù>ñä#¬nelfÝÄ— øÌë;aôYÄíÁÖù˜4•¶!Ør£Æñ9"âo\Ñ>dÐ.šàU«²Ô—ózƺžÄ`˜/¶E#jþ[‰ÎVøî/ôŸˆæ«øfs`¸;´kÌ&¿‡ÙºôÜ×Ï¡‘%Âö£ÁÚ9 ô]ÇÅhÔkú`e8lÁÚu8Œ#?ò7Ë&8u~²xÉ4!„RNÄÝãÓ³ÀS>èA¨a·AØ }–í2Ï©¯8îˆdÕvçÕüÌÎóÐÖŽŸØaê, V‰-g1ô#dŸ…ØÃëi'Dè=c΂þwÓ?Ęژ}W· t¼8³_oŒ¨äg¸¥½ >˜ºúÂËò•«ê‘YRÌé®M.ŽÞ Û¶)xÊe.±qO´¾@CmÌwc½±àI¥nðÌBÈlZå¿!å:"”Å? ©0(@B!åŠæ ާ'»çí¡vK?QÛ#s–mÞÕO^Z£Ÿhè”­½áD/ €¾ÿÇ®l3lËÜÁ%Û8 ú)” šŒ9cÀ^űPŒ±À¶ |›:£PKŠ _¡ÃŽìzFœÇÆÖ¯\Uf€æÒ¶cæöZ»ê'ê"wV¦èGú‰ºU3ŸEЦU‚´NÊSüyGO Õ g  hBÊ?µ{÷âáCS×A´bbžnê"ȪZ?øà—ðà¦p7kôŽÑþèâ«ÿ{ÎêýfO(×ÒÌiF˜«Fµ¬º˜Êh#u¥Þèú)Žg`ÛIôóÀy9?s\¿B{ —TU‚¡…1ò‚}YëÎù¸ëaÓpnÖ®ÍÑËÜýF¡õß Ù(@RþqúŸø!äÍæØëÿÆOá8ø 6lÄ©'8¼’¿Ù6Àȱ˜6nõô-ïƒm¤ ‹rìþÒaQ}ë—ÞÛX%òz9ÕèÏÕÓI @´ –Úœ÷‰Ú™.Þ|Jfœ0¥7ŽïÄÛð·7€VÔïWâ´ßv{Þ²K°ª—æXM?¶©¾†«ÏrµIºƒ•˱~;B3ƒuóüµ8úwÈîeNˆhBÊ¥ÈÈH@Àj™ºR† ét4š”8©úÏDÿˆ¸†m±aoaùX´€î]шK¢ÀÉ¥¸×u̼£gðñ·ÅLèv"œUáÔ|\ꊖ6PEaÉíJ-1¶]®£³¸…e§°ª+‰Xõ~^?mm&Ã}'ÂÎaþ~~—‰pË};'¬êe¹µ—ðC€¿–àV74´@ð~¬~š«Íƒõ˜»MÛ¸%Føcx?Tµ(pe„P€&¤\ …†1öGLRŠþ·ž© !o*®M1»ýˆëG°qì„{cÃ<´˜ Õ%Ôuƒ¯#îiûøŠý°fÌdظ ÍÆðG—[9£V DßE¬veƒ6£§S®Õ‹ª`g7ªQB´ßÝG`pýRYCøû`ÞcÜMà/ÿ<±SQ1÷U«J(|Õ3«¹ocä_`¯¡‘ j8ãÑ34tC\xv‹êþ†G[¯’IíäÍEšrÉÅÅE,sáL p¦.‡äÅ}½Q©T‰ÄƦ GJ‰û`uý½&_âv-ÌY†ã×p/2Oô†E3QCÛ…×g4îùàëÅ8pîr™MÞÆø9ÒyŽ Ëšãï%˜þN¥ó¹ÛX¬þöY=¤:ó&ñ“Ž}Ѧèqû˜«êå‰0lR'bÑnD¥"γ¡ñÏø <»[tÉØTROGÞp  )—ÆW­Zµ¨¨(.=ÛÙÙ™º’÷Ý&""ÂÃã^½zÅ·&¤$1¨ÝûúºÜý-¬çnÅ­F£Ïü5 ð™azðØ‚/ÿ\bUÙât¾¾j ¿ûM®9~?ý©¨G ì0iÓÓ`ëb턬J,§“ ‚4!åR-SWA©¨A˜ñ?!h‰ y›ÙåŒÉ}æFéÍÿ¼ ­1.Be›y}Fæ‡æ†l!Y(@B!Ä`  ¹?BB¡;?vÖO¨Z|–àRlV®5Ñ´†KÌÚ(} >ÕYEã!@¨0cÓ3öÔ噇4!„B ÆÊñ,”—Ûº¦þ€ÙM‹DŽD íâ¿æù†•³Œvœ.=sÿ°Aë˜>ŸéYMמ¡‹ƒQ€&„BH–‚:çd÷>ÆÍüÁ´°¨úšçKŒ]!…¡M!„Bˆ(@B!„b ЄB!„4!„B!F M!„Bˆ(@B!„b ЄB!„4!„B!F M!„Bˆ(@B!„b Є7«HŒŽMSgÞeÄÖNŽBSVTÚXy\dœÒ¯~êÓì‡PCB)hB!„BŒ@šB!„#P€&¤ÌR>ù¡u½Ÿ:_ XT_jêZ!¤LH@;œ,?DØXqs䨵÷1ulS—G* Є”Qlü©¯ç_I·élêBÊ?†ÒÕ%)«€J q¹HŸ¸zÁ. nNÐý»N»‰©sÞ#FÂFU:æ òä5£MHÙ¤x°vúŽ8ÀÆÔ…¼±™ØÔ5R±:‡-±ñV?A¯rñqc]ÏsÍø á9î^Ÿ‰^g0È#Á×¶TK# hòFIø£­{"uwÌß;y°«,éþ±?¯ß¾ÿăjŸŸì¢?0¡ŒüwË7Ë7îÿûZP’:ÕôëÜoìŒÏû×·Ö¶Húã]çGåºÆLûߣÿêùçöM¿ì>ü×ÅÀxÀ¦zÛÆÍ[2±½³Huýȯ¿î9üç™Ëé9Õï5ᛟfusÏ—ÚØ´'Ç×­X³óØù›Ïâ•Ü ‘m•†mº;uL÷êú£?™={ݾ#ƒø{Ï70[¬ï9åê£ï›˜Ã°ú “|¢k×iº;•Æÿþ£Ï™­;ÿ8#0VÆÂ­A‡>#§Ïöoç–y­7#v©A[—“:þÖþµkv:yþæÓ8í®Y9{zUó©Óaìò½=³>Ÿ ßX65ðØšïVÿvôü­¾)2ÇÊÕ*‰ål”q{a·nóÏ„+µ÷œüÿ}¾¾•ŒŸ”?ø¡Ï»_{’¤kg=àïÐ-Kô}eCw‹*üÏe_­=yíîýÀç‘É*~–Ì­n›^£fÏÛÎ…>ÜIQRŸaÏflØ„‹ºì)Ì­1r&w…®›™&­Üqðý‡|0f*N=‡È#–aÅ@\\ŠñßâAlëáëؔ͘[qíù"stá^ŠöÞ¸£«ì¼DvÆŸ¾ˆ¼…ï'ð·z½àïAÝ`Oÿ H £·y£Øvß¡øñÏñ »mˆ@ú‹ûç¯]0ÿàýH^™ÍØÄË‹ß}kÎù4¸wºrIûjfq7÷}ûõ¯ Îý¶õøÞ‹›úzˆ`Ýýp܋뻧õ¾3ìÕ¹*õ½Á=V$fø\¦I <³yj‡ÓF5xøÛ;)üjEºOªèÛûçv¿¯ºqm~CóÅÉÖé“ÁûöŸùÓR?/+eäÝ¿¶._½æó#k~è¿ùÔÖa>f\;¡µOǧV·útÜÖPÀ¡÷÷«‡Tåâ¬ÀªFu©áõƪËîà€ãß èµ„û«òsß¾Ž>êú´y§NFDÀ¿oýyÂÑÍ;þstVsá»Ôà­Ë¤Ž==ïý^ Î'üþ€©ß/lVņI{xåÔ¾m»NÞ ñýü+}R4bc³j€U“gÏèÚÀÓéI±Q/níYr#P‘õÜfõ眛º¹•çˆÿríi­ÉG'³1¿¿åòáiÍky_ÃàÝÂQ†ý¹fãÁä¦Ç-œ×¦AU;Mè¹5ŸÍØ»nÒ‰cwOÞ\ó¶=ýˆMòÒ¤àÌnlØ€—3g¹bÐ(ŒŽ6ÚÃÏ7—Ào6øï–v¨ã„€GØ¿GÏàúIøšáñZøå,ß§’âñÌ8ƒ«pt<Ÿ¡è¾{GÇÛs!©Ê%s¨"°npÛ×Á« „„;ø´ê¢£±í­smcŽí~1Ú’6ƒs#´Xˆëí°~-¶œÄØÄݬðî0Œ‰nõ@?F‘Bš¼qÄ6®º¤vmÅz_ÿ©»&µlXÃâÒ¨FCãt 41Ç&¼Ë§‡û®íèãª:â÷Þ©¯®ÙkWȶ!#»µ>2ÐMÌ£~‹Úv@4`Qüî½ï4òq·âþͨâon›ðîˆß¸xûtÿïoÚ{©eßj®Vb†ÍþãËwß]~‡ÅýUß]ž¾½ƒ¥¾*6ùâìn|¶5ÿæÚßÓëÉt³»÷<üý‰M»¬~¾{xWWŸ›+ÚX3b·Vᆵzú|>À=ƒuíN⦅ë$BCë/t9ø¶o_ÕlÉ Hß9~¤§]fÏLéòÁÚÇéÿÎywB£¿ôp¶KØ:Ýùƒ{uYpA »ÞÛ®ï\%kl“þ#' pñiö»ñË&ŸÑE›žî¹±íÃaUñ yÛêó\Z –²øù——Øûªï–¬°è¸æÈŽ™‡¿[ú¹??×zMDÈúY»¿è0Ö‹>àINkÐ~Â2ï6í‡Ñc0 =,3¿j©žb”6=KÛãΟð‘"h+ê CêyLÞ‡£m1|‚6=ûâÌy´³ƒ&3[âÛ‡85;ßÃ'žÙG²cþÁ¨ËX臀E¨7‡Ÿ³nV`¬7þNøÏÕ-7б‘íÛçÚ"¡Öÿ ‡ÚXúh€Cÿ¢²ö_X£>XÓ+Ãq` ֮Ù`ù‘¿Y6Á©³ð“½ÞýL*ú|%o°ú_ìØ<¹ªîMî1âsD —‹”›gîàWøN›×Ó5+Ïœ:Mîï²kmdúñ¥»ž8Å;×?ûzíZÔv×ç‘]Ãa?­>tð½ÃiЈj½Ý¹yæømŒYå_ý0xó[Ûâãj„²CuÝñUÐŽ©ß?á&\F|7¾^ÎoÃÛóVöÞöþ”g?LÝ6é„jÅü«|©ú Á…Èͳ*}Vü>÷Ÿó"fÇ¿Ìí<µzžÃ5ïRc·N±Ê\L„¨ýò?ª’{d@óš½Ú;4zc•O6Oþá·LÚeåw}Š?Ôû Jð}•‹á»EGìÒqüÂ÷º8å8ÐlQ³£ÖDO.>—S€&¹ÅÝãÓ³ÀS>èA¨a·AØ \ÑNt™Æ§gŽ×@wDŒ ²j;ŽóÚ_]:ÏC[í×nöÞòÁ`•ØrC?Bö7Ï&Ó„¿ëÓUç€û§)ëŽÕù%ÍúÁrR¸@ÿl#Û†Ÿè(uÀ/0`B.`Ój,ÿ )ס|‰=GH~ôùJ*ó&KÿûƒŸP…üñë]~µ}{Ï\ñЬj«ªX Üÿó^Êï"O>a,«7ñÀáÇ@JtŠš?ì’½šÊ ܹ?U@Z\jf/Õ‹#.ñã%[¶íSÏ"ïºÚn#9p\+™Tt‚.™ú eæûÉ„Fó&ÞnmÿãŤ‰U /&{—¹uê°#?Ïà– Zû¿ãžï¬e³éßÍŽÑn›á«|~`ãu~¤ý¨.®¥7u ¾¯ ß-z"ÏžÓ?ÍÓJd&áò´Ê •„@óGÀÓŽÝóö »¥Ÿ¨í‘9KŠ6ïê'/­ÑO4ôÎN±ÖÞp¢€PY¿•ɼ`«} - ûΩ&t?á-ù9\ V+ÀßÞØ+…¤<ÅŸGpôR| !E¢M*šôÀÓÚ »J6ŒJ¥Ê±HlkÃÅ%”Ñ¡Ijý§y¡ú+£±lžLŽ%™ËR¯ðhPÉ y1>Í*áøàáɇ©“ªy&|IÕ_‘‹_k7Üý˜>±ªUñ1vëRŽÞÖ.òjU×6?]ƪÁÇþºIÃ76ùΟ´ó«µ¯cmŠkq½úëbøn!ä¥TëÿüòÃîf^Ã1Ú]|õY ëÓ,ßÇZ.¹–fN3¹ßØ‚¬»™ÿ¢ÌÉ‚þÛ¾XlÎíÁÚµ9z{;£ß(4 þ¤dP€&Œ&%,\w Šû_øÊ¾(¸‘J¡.òԉA¡Ú)k7ëþщl=uÇ%3‚Õü•L õúë9TuàÏGZXhŠVÅž‰fìÖ!ñY°n켊>5ÞðUF? ÕÒçäã$)¸ákõꯋÚàÝ’Ev~Çš{_¸›œ¡ÔhŠÎ=¤‚sl‹õã§pü6âÔ^Éßl`äXL·zú–wƒÁ6ÒW9vÿ€é°¨‹>õKo‚m¬µ ùÃÏœj `ŠyHºƒ•˱~;B3ÿ94À_‹£‡ìÞÞ„¼2 Ф‚a•éú«1Ô˜qhko—‚>O…VUK¶­F‘ªR‘TTÀÑØ\ÿË<×P“«CH^¯¿~D¦ÿ;¨ÌP’ÇŒÝ:È3ÛsÏTä±%Ã7Vž¨Ëð"™Ì$Ÿj¯þºh Þ-º'L¼øU§ö ¯)áÖmæ‚y=›Vs¶’ü×Ç G_|‰ †Ô ýg¢ÿ D\öذ ·°|,Ú@÷®hÄ_'—â^Ô1Gð>ŒžD ÅLèv"œUáÔ|\ꊖ6PEaÉíJ-1¶ÝË)~e ºnш”£² Öcî6~†[KŒðÇð~¨š·g!¯Ž4©`˜¬x(q®Ó¬¹A§Ú½2ÔRß5P×55o@Ö(Òõ'¶H­ÌŠ>Dòúë×ÈSôW¿6³17äxÑ[gf¥oŸ‘˜®A#†¾±r‘D÷´ª ¹Iúþ¾úë"0x·ðäw¿ʧg4XöÏ¡i5³ý%ºÉ´}  )צ˜ÎÝ~Äõ#ظvBˆ½±aZÌ…êêºÁ×÷ø“ƒ!öÚ!0“aã*4ƒÄ´rF­ˆ¾‹XíÊmFO'mˆu*OhS ZcA3 ÿ C‡£­—‰2=©(@“ F`áæ&Ҁا1J”N€ÚT©$Ã5îI“ÂÕðÈ1UñAñÚ Y%/ëbº^¿öú•Ñ1Ú ‡šž2Cþü½u6U*ëÚ‡D(P¥ðM0|cÅv•õã‹}§„]©_ëõÕ_¡Á»…£ 9qà1?Q£ï»UËÈ礜’ q¬î£¿×äKÜ®…9Ëpüî%Bæ‰ðh&jh»ûŒÆ=|½ÎáÁ]ÀMÞÆø9Ò´¨ßÍ^/Þ_…þ}±û!| o0]MV ©8(@“ŠÆ¬jëª8ÀÅ”ó—#TÍ‹¸ÊD ²¨ÝÕ¯¡7C2à›'ñ°É¯„h§|»Ö.î·Æ×]¿:ææUí{â†Ýjvº±['«Ýµ\"Ïü¢hQ³ðhðÆ ì|;ãjxêvÒoC’?£kĪ5%ÐsøÕ_Ãw ÷¥$!D÷¥ÄÚÅŠ¢)I j÷þ~….w ë¹[a‹mq:÷?'Q5ÜÎ=GÖá9æÛ>ÿSÈê`×}ì*´dB^ Ф¢WîöAõiÀ­knŒXÖ¬4zÇ Ý{ø·\½ I=¿çFr×ö¹.m¡‰9óË¿|'aëQ=²Žß2úx§V¨rý±xÍõ«BŽþ¢½œE·q Š¡Æo×~T[á•sjÜÿnÞ#v¼ç\XwÃ7VV·_g»õ¿Æ#ãÄŠ}Á=G dFj­ñO_¤²(h¨qc¼úëbøná¾0˜Û까}«‚'ehB)] I…#­=zAÏïþ/Ï¿í9Øëè¦1MìršK¸ëœ¨ÒGß¶¼Å·£·~¶jòÙYõ³ŽíjbNÌ™ò?þò 5>ûþ£Ê™•-]í´Yƒÿ>¬h\#ûpäë¬_vpæ——¹À.h6÷›wœ =cÝØ­Vúh嬕M vçoYmþué ¹·UÊ5üH†o¬u›YŸÕûuΨÎMì?ßçÐW]2?ÞXy||FþªÍ«·«Ž?@}zî’“–uqÕõûФG<¼£6pÓ³½úëbønÔï®SñlóŠ?'mìåÊo+›r~×ÞÔšB^; ÐäÍ£Q*tB-—« x“ Ýûm>x½K÷oo(#Lhz`®o›f5\-Šä؈Р'Á–Î_ýºžT·*¥Z¿*Eî\¢Q(ôKù–ÈÕú eöƲù‚?¶…vòÛµ/šµ¸7çÓšW±TFÞ=¹yéE^ýrl¾_öQKÆ®åÇmDgΪp}jý:ûߪe™íñåŸ;z9QqÒö¬ß_ÝÅÞJʦ„Þ>¾qéš³±øŽ?xhJík(n—»u5þêøþ¸î®¾«X?´áú±•4ªáng.b4Ê”˜ Tºµ¼‰¹/–´Îçû6\iå(FqyÁ[®«}ÛúÕp’ÈãBÞ¸Îè®Ëÿ¿€È* ]Ìø+©>xf×EŸü™†G+»º­v¬änÁ¦ÅGD%1ž5t#9d„Þ kÑÌ]w.e ¾¯ cðnMÛi#«üá9"yÏóϺM¼¤©á‚-û5¶Ch<2^\¿Ò¢U% ¾d}íº“,s¦ò"B)hòFIü{L›ÖÝMÐÝ ˜QÛlŽ}µ6öר$4^)µ˜ø´×%4@©¢|¾²M½‰WÒÎÁ§¼0¸ªõ`²m±èê¹iÕ>ù@©† Pª*O¸œ4ÁÜUgÐ, @°€ À4 Ð, @°€ À4 ÐP±ô‰-]$’ö¥}$Ër7§gkK=.YÐÙ0j—ãYÈiÏRzâ@“G5ÇÄå°‡ PZi¤À´‘”K.åÈ6ž\u‹¦‘|Ÿ&Ï¢Ðhè0²æå¿½*…¸“½:@6Ð¥–.Ó–M´é$ýö‚º[›´q+ÚãoÒ‰|wQ(›íïΠîiàp:jؘ¸h€R$éíÛL§ë†@Ê#?5¬G·‰Äéõq²7VÑšÆ4ö.‘% ¤®BÚ3Ÿ~ÙE·™íªP¿É´`8ÙäxŒ7Ûmíü–fo¡—‰déIÃ~¦¦Ùž¤MÌù%¤þT¿"=2ltÊñ‰×Ž¢Î’µ†îo§ïWÐù”B$«H§ÑÏÃÈV_—Cá藱̭vw>œv$;d0 &€’N“H÷ÒÆ´ûfú*8‚F¡îÕŒ†]£Ô‹t#–ºØ2ª‚ië]fÁ¶'yIé×¶4á s·fryCçÓºQô0•þž@‚|_[M¾¢/2‹WªeKëЉ,G…ôk§œ_â|?jÒŠB/Q$1“Cê5$§ˆ¯¥k3©ùBf{÷fÔPJçÎÒº‘tí5]›«ËüôñºÛŠ6¬£-Ò£c4^w“Q×Á4bu¬mDÍù@€(ÑüÖ’×7’~÷ã~4rõ÷"iúñãΣ‰TI´ýuiϬ 9ÓÖé;Š„Ohçs²µ¥*éïH,§Ùµh®][EOFQQ>¯.÷¥úô,ô"ß³TIÈ?®Û!Ó6©¹¿Ä³Q´á ÙW§…þDuéèU*Ë#ÅS©OÏeÆï¯dÅ¡ÀÍTu(=œG>Cid9ÒeäzÑÚ^´"”o¡uëéb _Åܤ èü%jda’®…R  D‹þ—IÏÜŠ4i HU¬²nàܞڊ茜þÜA íI¦¦³[ôxЈ$–ÐÍ0æž2‘bÂ(FK\u©œè ½Qå Ã.ÓKý§“™ô¬cÿ ®J³þ{·¸nž/‘íˆqàqz¬_èü9“ž™J»Sc.]ÔÐŽK4üËw_¹Rÿï¨ÿ·t~ÿ–î¢Ä»¦4¾ór„ P h^ЉãäîN²Îæ:Ò7èÌ1Š9M÷©YmÑÏô¨ø5Õ’0s0.ýB“–Ð݈,-’Öˆ×xž¶PµLú*!Õ)K”!@³}‰ð§i ›ÓÆÌùß!ù—$ɼ2ñ%9N'ÎR’ D«Ð†ûÒ¶¿èéIš¤»YQ÷!4r8µ¯‘ž8Ôj4Y£øHÚõˆ*>£úÜ:â æøò« Ôv©ˆÊt§>'WKúg&Í{Ⱥ m†,¬VgzˆíKðÒIWëBì3=äð½=Í6™.ï£uë2Ìüv¢~#¨.æo@a!@”h-iÃú5”Žl£›èü :¶‚¹ÙÔ¥a£iêPrâ“MsêaG;¢éø^úøitOk@}+0Ç€/nc¢­.–®ÜL½í™t¼šÅæÕ+¦-ø’öcb&\ÈéæË [° çi §ÑÖV9lÿˆV,¥ ;(8=µ7îÏœ‹Ãû“w3¿  ¹’÷ òžNawhû&Ú¸üÐÒÑÔ²¿þ<Ð2Ñ›vl¤×{h¡~E‹1T–Ϥ[•áh±š"“‰ì)âOZô@¿FA‰FL&viA刈Î/¤Ç©¶ uþ™¶Éç%„Ä5¤Þ —SY *ÓŽjê9Ñ‘åÔ”<{•¼'Rª=}¶ˆ&Ö¥§hövæ®MièpÒ<-MЇé J¹|LÓt·Ut÷8mÚG¶é3 #·Î|yD4®‹~.—Zö!Ã)9F5¥ƒ•èúEj5†ÖP‚ŠFv¤û+iNõ¼^P\›¾ÿ”†#Í-ªãF5È÷9Õ/Cwƒ™ë¹hy‰åT³,1_E|M-ªQÝæth­L­~¡¸#T¾,Õw¥§÷(Q—´›Òê*L;–•hÈôÕjYŽpåo(Ð¥ê÷¢ßz½[!©CËÑ’fÙ²}꘶¾êDÚFÿ[GÁtMHÃ÷ÒÏÝè÷špœ"Rhj~/ħÁû)eÍÛEáq^–¾;JmŽÐ§[˜Ì©j#^BAÓW“wÚû”A¥k’C-—Ð?Õèû•ô—/Ý# wêýÍÿ–ªê¿?XsýnòxtÄ´ØŸg_/¢¾Ë™[Fcÿ ±îþñd6™ïqmhì6æöNwÒnf÷{žÐžÌu5N§‡çöÃ)h X@€`€h ŠØ®]tü¸¹‹€ƒFCJ#.@6h€"–’ÂÜ ¤@€0±€€¥RÉáp´Zmþ[C©¤ºÿòx¼|·„4€‰¹»»ë‚‘J¥âr¹ÆÜåÀ‡ÈðæJ7HÌ]4€‰ÙØØˆD"Žž@ 0w9ð!’ËåºAâææfîB   LÌÛÛ»Y³f*•J©TJ$s—¢¤¤$KKK©TjîB   LL T¨PÁÜU@QA€`LàŸþñõõmܸq5Ì] @ÑB€X¹råµk× ðóÏ?›»€¢… &ðòåËÈÈÈW¯^™»€"‡ À4 Ð, @°€ À4 Ð, @°€ À4 Ð, @°€ À4 Ð, @°€ À4 Ð, @@‰&š°~«BžvãÑׯ»—cÖ’À,ÑêXù»»Bž$Ÿ‡>œÁ“¥ŽW¤ßáµOÄ5GJ4¾ ’‡êmG”Z¢Z¬ú6àòÌ]¼wßóÈ_u$.ÜÜÅ‘7Ç"÷÷s´üñOçUpx=GÜæºœK©·#Û6Ì2r}èC<nƒÙ.»gkï è»AmÆB 4@±¦Ržú>þ¿¢À¤U+ä=Kò4@±ÆçÕhΧ‡*"n­–|¡¹Ë( àýS*·6ÚÞÎýô|!_¡q®t?Ò!9ÈNÒº¿€3t±GX@€€"§{¦Ty íi÷ãÎǬ¾¥•¶3C)\ÿ9ÂYû“¼Z=dµ Ñ¢÷H1R"˜4¥Øû‰gFm:ÃwÏ}lm}üP(vOKˆ%’š£®S¬3PýYxfÝ#ÅHq<`:Ð3í›3±+Ö¥<~¬ðW'©˜UWaƒîVcæX5tÎ$Ô‘òc+bK}ø¯*I­Ë\ÇJ‚Šöꮫ4Äï{Øm´.í¥*ÍŒÚ}0ù^ó”×?ÕøYÿdwë#Ïjø»ojÍ“Cq»|’®\QEëÏCÆçÚ»ó=* ›Œ¶ŸôY¦Y òìÏQ»v'ßy¬N%âHùÕZZôšhûy;¾¨€áHsÑ;pè¾´s#ˆ{¸Þ•íÕW•;2î]"Q&/éºå=[6¹NÙÍC ø%<¾5ßV¬_º·§†ÕÐ=ÖÕHnDtÿJcØ@™ücW&†Úôw9êc騙Ö=,ª©GìQѪ¹k7Wý¦Æ÷çÛW—ätuµêØh]z&çñ®[WŠ- +{H»5Ó¶ý*}›üöˆñeçÛ¬±Ü¹FÕÂa5xŒª‡ýÎ.þJÔÏ“&ºnŸ/±Å%‚ÞhÈ…CkëÉ=$væXT•”§ø¢ÀëJùh¾!“%ߎÛÿŠY¨6LZ!Ãd®ƒÄ»÷ä~ ÝK¸l]¾¼é?TŽ8µL>ˆÓh©C·,íKm½xf{ /ó¡naY‹éíoU‰zLÚ«³c}8Ô)äY”ëØ.ÛlíaøÕZÆnЪhS—6`sÔÑHæ‡Ï±p|›r¸¼¦¤{â#S’×ïQvžÄ̾0Y&*ÎüÃüß³µÐ"Ãj«V¶“GÉŒ:.Ì¢ìüz€5V;×ȱZHÆ#ë)ÀÎ"åqì豉C]ö/µ@zx  g—n6ƒ³¬ãsºL !e*iÓ×Åú*ôó/9®Ux™ÿ‚s«ñ˜­Iý_QyS¨Výµ*Y®[àŠ½»d?ÄÈ­3Íþ›H~¾A‘_Q6Ø+jÊE¢×IgŸÙשkº /MýGxV¥º¸SÁ,¸ˆ»gj_ä)v§x]Fõ;£Hš$°2ijôŸNínaó6ÌI„~4î¸?›²sö¶Øb·s«&—Ëà1®žB`U@ÂÔ6Qþ½®¶tDzx¯  P´UZÈá ékTj-™ü `IŠ¿êʉ«Øäð¸e]™·1íp¹Õ[ è¢RWæ½çmÝ‚0ÍSŠò¦Ÿ~Á–/ãhÕª  ¸2]DR’*B¨&+žéúÓBм]~HÑÛCûÈígαjU5§™¦)›U»F0ÕÎ-j…<…ûÕÉ?µysškµç7©KÁç™@Á @@.”!©GÖÆŸ:úÔO“ UiH›Ã¡<Žu%˜©¤ ÷ר)cDÓF?7|ŒWÕÍô¡T§ IÖ/Ùòm õ«Œcå‘vx6.D£!žÉÓ &QýÆPê“èÑ9o¤Ò2©Ø„ý)ôûÕzŸWÜK"ÿ½QÃ÷FI«[t$óbYËɨÝÁ¦lc»s«E!çÁcL=…Àš¸Ô5Ÿ‡îÔíZJøuŸmçî@A!@@N®GéóXINm&ͱ¬U'êÖ¦L®q/ó–– ¤M$I§ÐÓÉáìÜÒ«hãåÇÎëçÔ“µ.kú­•k”†%!GP¸æyé34ª<·+(­R›Vj›õ[-¸9lÕ \ô]gÂþ´jåpð¾hÕôŸ3J]‰O’w«»ñÛ.q^2Yl•S.Û´Xí\ãÇjQÈ>xŒ¬§X¹Ï;ôN˜¨¢HþB®½:!âD×îe ûƒÐ\ñûWL ºö>Gm*¼1§èçqfÄu²œµDrslJÊ•ÈQS8ó¦ZVwá$ú¥˜ñfß]8OÙlU¾>b戹iu¥jRu%ås§MK;ùߌSþ×câð^Ã’þ]õ¦×ªôÕ\^mo»q lZ{ÉõÛxÖ|7 zœLªˆT{!~›Eÿg˜Á­àÉ)xÏ×’çdA¤+5J£¤ü’¨éûSè,j;FÔö»©‡"¿î@ÚË ü?·­˜çÎeY¶)¿sYŽUÓË2xŒ¯§˜?ì¤S/Ý0ä8uS༠bÁiñ’.¼¢½hÈF«ŠÓ/H³^!/Gšxù±µÉ©ÍÏŸ°ä†ª$¶å¹ºóÄEú÷ÜBز&½Ežz#H[¯jASºJýèº> DŸT+šŠÅ‚žô§¯.*¥ÜÓÖõ̧TÓô§Zýì‘¶\Ý ÉãpGêÀ¯ÒLòqõw§¶àJyVúÉ !RB"ÏB|ìîú¹Ã¨¯×øRìî°/dN¿,”UÏ|N«Ôjœ;öõÙÙ ¤¤LÐD+¹ƒ®¸¯ÍÀ¶?uTÉuÚ‚V•ñ¨§–ŒêzÞÚ»¸²+G¦¸º%vÛM"'ËK¬ÒÜ<öˆñeçÛ¬¹sÙŒU¦LyÚQvujÖ@r{ˆÝàaUÑذ;労…2CyVâñk¥Çº$Æ’rãW±nÙÕ‘ºó Ї[q™õ–‹Ö$¿ŒS?»£-×ÔbÐv›¡ýù×Ú%Þ¸HêëQýËFÕ[[nï(>Ï]¶ògQ¿7>r‘q2jØ$ѹM™.fðù½|œýEúü­RøÉïk„ºÉ4.àé/øî–sî–ïáç³#éÆ-EȃÔı䕫%jÔßþûRCø5°;ú@rþäëWågV'Î9Ʊ­"î2ÃjÈi]‡§gÍõQÁcÖ+â ÷|£:ˆ£m*ˆ¿Üï:¡~¦6mÛÚô·Ø·$îðñT_?ÅíÓ æâ‚ÕE ;XbÕ¦vÚÆlúSôlBȸ-ò7‰ië_Ì ª9‹ëÖÜzÍY»Ú–¢+l\Τ>¸¿èh¬:íçu™a?lª¬¶]ÆNÌkQ¶±=À–q;×ȱê1ññ›;äaiUÒ“ Õ¦òÊyY¯9fû¿ÿåüm51ËÁÃêߎÑ8òp¸÷ÀÄ€ÃkÈ¿óxµs†ÛÞ"1Qâ…7=»&ÆyÓK[a„Û‘õ⢟o3tèÐÍ›7g_¿G/㚥K—N™2¥È x  GB~Ûù®mçg]Ýù®ÓF?H¹ú”Ûý€Ç’Þ™§¨5a7~ìy.Š¢öÇ?Z!i&cVó=,¿ûËò;S•*àÕl§»å¹Ç®ŽE_ÝÍT/ú·é:»ëŒÚTà&¸\wË{+ãûÓ¹Ù*÷k«rk‡ßbœ}‹qF–÷ɯl=Àš1;ר±ê~eu.-¬Îý!ËÁÃâߎž?£ÃgÎç“s|HÚÆéoSŽ1ooït§wïÞï¡€÷ rñvîm–º³¬=éÝ/>¶sÅs}hy–íy\—æÖ룬™å„`æ²v²üÛ/Š" ¼þýKïOçÝŸe›èÌô§ÕŒéñç¦)(A•¥?Þ¯Ö­[ÛÛÛGEEå½Y£FÊ—/ÿ^*x  'º@ù6Jæµ,WìžF_œTk´i“˜3o£õ_òºíTI+~æ’[;Ú—›£Öý­1þK_\{ɨ² bãŠd·9mëJSž“ãöJ•O3æ«Oí’bZæÒ‡ÃI¨Èêªq6N‹—_¤ñë57¿Hø|;«J¨÷\hõÓ`æÿ–N¾‘‰JuyŸ¡?eµŠÇwÅ´ú˜¯K¡å;‹ó7 ¤B€€l²OcÈmbƒV¥ÚÓ"`Ö úgKJdÛwNÛ^©¾ò}ø¼[DÉôÅ6œÜÛÑzEÙì×síðôÛQøZ J"hZƒNÜÐféÏ4ºþü!SæîR(BùÎâÀü (©  0xüìOµŒºº'¬£¿là@IÍ < >¥F«^ÞI¹°;ñv0q+KçîwP©xÄ=3CBq’ï,Ì߀’  GRÛfóKÉéµñGŽ¦ìš–“v¦-޵‡ Zsëoû[õî&´Á¯£¡?¡XÉcæo@ †ßÂPh<;Q—™Ž]fš»Ž’ý ÅG³80J0h( MM»'ªÐwÊðVœ÷\ÄáƒØP J¹ß¶‰#7^}ü$0VÛªÎ{òpf5áû-‚oWµ†Çëkç¶­:â§%jPaøÐV¥26~»Š4@É!®1~×åñ¤x:¿NõYÿ™©¾SÓ~cšvkyàÈÊ`3ÕðaÈ{w¨CëÐí¿Ž½Öü¹kd‘9*,¾Ð{`^ÐïŸ*pÿ÷k¥úqŽ/kbaô˜4ÀûÇwø¨¹;=|M$mв,Ž ²ƒÞ3C€€ì”/V6¯ýk»¾óë  YË•wow8ûÊ®e—nY爣ÿ rí‡<{ È!@”8[¨´1çœ{+ź Šáò8¥òïäº;Ž z læìÿâ,Ï~ȵ÷ IH¢úa¤%jïF¿IŒzJ@"íWP{ªS¸ßr…„ PâpbAaž¯xºnšO4‘µ)Šá ù¥=@³Ý&íÿb ýðž¤hIR\þ‘jiK$ù¨ÉÕʨ­Õ’’C8ó h€âL›äwrí²ßv¸ò (^£[Áµp([Áƒÿ,‡mU¡gÿ°îÏ;Ÿøù‡'¨˜U®µZt1sÎèVÎúß©ÿm›9sýÁãט{¯~®+þYÿT÷I·ŸýÒ@b\#™p„¿Ã?»v÷©«ÃRt÷¥îu[výjâŒQíʉ3ýÑfײ<ðìꟖlùãŠo8sÊi¾ÌųrõÚ^×.à™áÏ¥2üê–EK7ºp'€é‘cÕFíúžþ?ï:VF¾bÕ‚‘»#õἺª0Ü­½üÅ݉øFõ^´É/N¯_¾v÷É+÷_Å(™^±)_¯E§þ£'êTÉ2½³Îörép8ÙpÇcÌU•/nÝ}êÊ=¿(q,]ë~ÒkØ´™Ã[¹fùuEì)ïj÷…6–ô8~¤ƒEü““›ÖlØqèìÓ ›ü/}éÌ-ü8¬ù<—ÞcÝIy|²#ÆpGÔå䛣M‚Nîø}Ǿ?Î]÷‹ÑÌÓëóoW,Z/ó®6rì}ðRTt*žö&ĶZš»ã¤*èOuþ›½%WP—Pª"#oµ"ñ€)a8[r¿mƒÛ~½;HÖà‹™Ó;Ôu—RJ|Ô›×ö-¸ç§Èº¹2äÌÚMG>ðͼ9-êzÚj‚/¯2}ÿúñgO>þóþÚOí¸Ä³ªÜú‹É•d¿ÙLdÿÙ/¿ b"WV¥’ÈèF2Ñ^úºå]÷šuª7ìX…“ì{ýæ‹û§ÖÝ?µá÷ 'Î/ëèÄcS^º”ÇË:6üߥT«f£çût¬a§ñ¿{zËŠí‡ÊOúémˆÑÆÝü¹k›YW’É­Ãä ¼*ˆ£ï\òãΟ.ïÚzzÿõßû”Éï÷»Œßâ:³.†ÌJº9¹z“åA_0ÿþÏÕÛWÔðžñëÂFådÊðÇç¶.ýmíÿޝ]é½ùüÖÁ•Å̆²ö{}O/ëß}Á#¢ 5}ú8Tþ¨Vå]j¦†ù^½îwÿÄš±'6ïž÷׉o[¿ërcºÂ¦ÓÞ0Ū3cêuÜF)¯ïš»î§¹G^¤år,vtžýkï±î K¯­A£·ëüÍÑX’_žÙÆãÞP±«“L"ãÄÄi^]\?¢E èɱAé{Ú¸±÷!ÓhèVí‹§£éW;j¡Gq(˜¦éÖé¤ Œ ])”¡z2Zê@²TšAçtïD¸ÔÅŽæY“Œèl}“ÄVÔWFU0÷L xÒÆ_™Þ^×ì»·½o†,§xš°ý·«9škÙzíqŸÎi@š6ró¿Ü|mXІo÷~÷Éèr|k³®=U/ýçé‚‹Uõ¶=zfÿ[~d޺·Ç•»Rx|F×+i¯ì5°ÎÓSCÊòÙ¶¬Ú9zú¥T*3þô™•M¥†•=úh¦¨üÕ»¶4‘'ÇveŸ}ÿƒw|z¹è“z—]ꨫvß´}аŽÍpÍëëg¬Z(ÀîàK$YÕÿ9Ð&\ŸÙ‘yu~ãEw.L«vR·NŸ}9¤ç¸Ûÿæ¿wH—Ê÷—·Ð_R`_ÃËËS¼àQ*‰º =ÞÍ6½™TÿÓÚ÷^÷<åꬮc?zº­³=—eW¬]ìõá”î,ßPcøä=ã›Ö«bycÄG_Eg¨×ã0‡Þ+@Wp-=>jV݆tšì¼~:wáÓ 6޾+^îÕÊ{[0%š¿åYÿj裱qcïC˜Bâi_"E¦¯ùHJ¬©“~¥}À¡¦!ôˆÃDä-ݧQºw * ¢X ˆ$ !ý,¡fÖd“DºŽ;DÞÖiaâJ‚þ<úRœCz~“HýÞPœ~¹šãéëdÒdÞÆ?ŽºG’.`[ éÝN¡ëñÔOA§ÝÈML•Õô\ÿŽÌEDºah—çöå9$Ñît0žv'P ‚™þ¡»U¶ o+êiA6Åeâ | Š%å‹ÍV¾Ò-ˆÚ¯XÖ+ßc© së1óz´wÌpøÅ²jëÊ´6ŒèÅuy¶ìk’Fø¢Ls Ee».>¸ôr•Éw(åÜì_î XÑP²åć‡þÑý åTi]]ún[ŽM«™óFÝuN›n¬ôÛ<Ã‡É 5¦Îéæò6'sÛNðvÞ³.<åôÂ=þ}'UÌýGfÕBv‡ ©|&ÿòB·à@J% ¡ ª´»¶"&AzKÉ#ÃNà~t5ÛÒ%;â)¨g='ò‹§†ŽtÑŠRR¨CEM ï%$S/>mVÑ£ ·¢2R«èˆœiÃYšÓe-ùDëÓ3‡æxÐ@iÔ4û5íμͱ²à’PH›Ý¨:‡þ‹¦.1¤J¥ƒJšâHe¹Ô-–Ùp´‹þ°·–Vå±½þMˆ†;Ò0z˜D{âéP =O¦yº—†»ÑôR}–(h€âHéxÓ]fAè5¢½‹q§ñâ»w›61ë:±P÷‡NCÊT•&Ç'A#‚Š&yMe’É룇ž-jXWIJe­†¹£}|øzT÷NöoÿPKjü±VÚ²*èÔÎÇÌ‚‹——{¦\#ölæI뉞œù7qRE›ÜªdÕBAv‡é¨^ßxƒù\Ú²Wí¬sY9ö-¿l!<|ZA·65>klÌJ\ãë±ÍwèÁŽS¯ÇÓEàÂw¦¤ÁÂN¥ß1É8Ì…)»‚k]¹¶] %mbt²šìô»Õˆ±÷!Rkè±>=·±¡AVÔT@y ÒÁVļóЧz®dòîH©î Dô1—Ni(NI)D&…oŽ&’Óßj&ÎÆ¤Ò}} }¬H¨¡'JR¤OÎò©2Ñ_Jý²„ºê‡—G_[ÑMph¼;×ù/NCot/*H›ãñ*Ç©ÏFo¯{÷VWÊÜf«èt нßÉx,8:UŸ†‰š9ÐDK&¸©(ùn¯ÑÐé8ÚOwÒ×;ˆ™qô•2Ó©  z›@U©r£>‹”?^ö“Z¨î⿎N­úö`Yœ«7÷OG‹¢†&=p’ÔÁ’[–¹ÖuýrúËáÎؽsÛæ}·"‰^›Ú¦¿Ã“ã_—}—…N56Îã«‚¹á°iõî0)®Hš~ŽAý â¬QH£HQ–D2±¡A#O”–ÄÖÝöÚBwæ;&B93uWäü"y½øê»yÀj:Ÿ@{ãéŠ’ÎÆR‚˜vô<ÐVêÁ§*ú-†btCFB^¹ôŸG-øôDEªæR‚Ä̗WÇgÚæíG jýhPÓÚØ´5Iš´ÁÀ_Íä—|·W(i^437šÃ£nVÌÙ  ÉLâþ·¹Ø–M;õX”´’lóûú¿2èìáçÌB•>] |ªZ“4bhé͆ËmØUõ”q Þ2Gä\¯û˜zÝ¿™³ðЄö}6øQò™…ÛŸ>«º¥««1§…z©¤d>.›ØîÓâY—÷° ;ºRãCâÔT&kjTÅ>Ó¶ð(gÌ„]e„Ÿácnûªîfæpa;ó]Ó&B93uWä!÷±÷!OåH£{¿×Ɇ¹EÉépÙ…“ö9©Z¡ÊøväNùêЦ{Ì‚¬ë˜6Ö-'^›ÒyÍÐû'û9eü(Vä\Ñ(š,¤ºç Êvì]iª¯уåkï ]Üu\bÕËÝ‘\ú?W<·ÎÛqo_Ó$]Ùw/¡ƒ—,ペȋۮ2óxÍGtÎvH6;UЉmú3òYvü¦µá@á;3IÆa^LÚ90fìWíÝÈ/ã} ö Á™×Ì)Osrz®¡7\d9þ9³ºÖt8óÙOnež*í!¥ÒLkÈš®dxÊ@gæöN~Ûh€bɪŷSjïœõˆT—ÇyÏ­|ô‡ÖÎéÿšµò˜˜ÔÌ[‹Ê4ªeI~Iôjóò3ã7uwa¶Õ¦]Ù³ÿi¶‰§<©‹­~Bjà…‹ŠúU„i$7ÚäךŜ-Väµ`Ig}~fݲúÍÙe{_öWéÝÁ>åë '™ÉÖŸô¬Æd4Qõ‘?uûeÀ‰ä¿¤Û—åNü>ªm¦p£Qk¹¼¼Ž³jÝîÈ[nýŸ;¾Çç¿LYÚdÉóˆ­SVO¸ôm·ÑDž5éæU¦üòyÙ|ã*äÈŒïoêâ*·áìE]Ò¯uRøÎLc’q˜'ÓuE.Œ{¥‹–NEÓCýâ(+Ê~î@€’  xÕüßÁ·š ?©¸ùS—ßj´lTÅQ(úïÞ£PŽá+ç1Oþñ /_ÏY̱n9uXù#+ý)|[÷3µ”%…>ó ä6íWß–‚c(õõÝAMšy0_éãØ6ý¢ÿâ%Ý\§æ¡6Õ¤Éae¾?ãÓM#œ´sè>÷™;;ºFk ¾26àÞÉ­ëÏúkÉ¢ÉÌGWLŸ,̦<†VûÏøšuÏѧe W eØã?·,Z¥Ë}N}V/nc¸>/Ï­ßæ#wÛwZrO~xìLJg×hÑ°Š‹”«Hˆ x({åöµó:«ØíýÏ LN1¬W%'«2ü2εÿ»ç:5„#müÓ©íÁmíºó]Ã&ÿΚػqy©2üñŸ›®<NTîóm'ç6ÊáÀqŠï¾ [c*9ÛÉDÚÄà‡§7-\{)Џ5Æ9:©ú»Îaѥ…Õr¹:ÛŸ“ŒC¦rï=¶]¡U*Ó V(3‡xâÝ#ï1öJ M|M×T­ïœJ¶Ôëý#€é!@W¢ÊÃ>­»}Ñ¢õûÏÝòÿ÷ò©¹6›vþbý–‹k}º3–ÞìüªþÎI_þ°ÝKÖlñÅ£–£§­9ù_\Øã;ŠJM;ŒÛ~hJÿ²çÚøë¢Fu}Z˲ӛ¬}uyT9>¿ìW>ûŸ ¿æï ¹ßšú^݆ŒllÍáðoDÜ`Ámýqö¯«7­Þç§?ýÈ¡JsïC'Où¢‘CÆkH³)ϲÁôÓÜÏ\þçÖÆiG—©Òšmè=cÑÿ¦~ñ±Ý»+M;´]|ÿËïKVn?~é®ß¿WNÿK$q©Þ°U‡qßÜ-Ïô̾ãwÇ¿#ví·åvHbÚ3ŸÌ¬-™eU¶ù؃gç,ÑýZÎ¥ÿó ¿ôñm2à·e¿í>¹d˜Oj¶žõº™>jÒÈŽ-r~z̵Í?~m8F.u¯×yô¬‰3F}ZVœy{cº"×?N; ‚ïôêâYvZŒÝbNýô³¬vtÎýÀ¿6¾a^½Ç¢+Rîþði§ù×Þb²ï´ªâ5m¹°¹‡mà–í&ôK0lw\Ë%9wxŒ§‘c¯”ˆ5¤g.µµ¡y6d‘ÿ3Jh€bŒgßpðâƒg{À'Fë“e•°l÷ù'ºÏϺe¿¿Õý²=]àÑsÙ_=—eAëtª»åù¤e¾[»q‹Ú3®U7¯ÑËu7#‹(d Fï¯[Á«òj(×þÏDzrשëºNeñ›Žkoîð’æ¿!#¿®°n³îQ̺|[)ô8\•O]!©?÷jøÜœ)?øÀóÁ9=Àbì•x\ÚZÁÜ5˜4@i¥H’›è"€¥ 4@ieªkh”2Ð, @°€ Pºhå1oÔÌ’*>$4^)µ”¦Ó¯4@é¡|¾²M½‰W’Óî]\Õz0Ù¶XtõÜ´êùž×  JAå —“&˜» €b€h X@€`€h >,Z¢`%YrÍ]çŠ4æ®Þ3h€‹RKÞ!„ Dj-¥jÍ]¼_Ðæ§ÕfŠ`I8¤Yl)•Js—EÀüx<‡ÃtÎ’¤¡ár8º—œœlîB È!@˜ŸZ­Ft.î8\®F­–Édæ.Š4€ù5kÖìÑ£G …B(š»(¥RÉÕÓíJs×EÀü.\¸`îÀXÐ, @°€ À4 Ð, @°€ À4 Ð, @°€ À4 Ð, @°€ À4 %-@;::š» SJIIÑýW"‘˜»SЉ‰1w TÒ´F£‰ŒŒ4w¦—””dî€Q¼´\.7w ` …ÂÜ%¥xè7oÞpô´zæ.X{碣£Í]‹QŠw€öðð0Dg.—‹ PéÒ³F£Ñ-¸¸¸˜»£ïÍãñøz …B(êb´¹+0=Ãgšºßræ.ÀôtÑY¥R‰ÅbµZ-Ì]ŽQŠw€îÙ³§áýJhh¨“““.O›»"Ó‹ŒŒÔjµ8Å”HºÜîææ¦[îÑ£‡¹Ë1JñЕ+Wž>}º¹«€R¤xh€÷ €h X@€`€h X@€`€h X@€`€h X@€`€h X@€`€…ÿ~VC“7ÖìzIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/3-phase-database-editing-model.png0000664000175000017500000024266414770023131026656 0ustar vladimirvladimir‰PNG  IHDRˆøFዱ pHYsÄÄ•+EfIDATxœì\K‡ß=}Ý!-© " b`r[»õrmžv'v]ÛOÅn°;ÀÂ@AºáôÙýv]ŠŠ :Ïåwݳ;;;»³;ÿyß)A€@ ¢vÀøÕ @ Qf@ jH˜¨E aF ¢„@ ˆZf@ jH˜¨E aF ¢„@ ˆZf@ jß%̉‰‰±±±999ÊÊʆÕP’¨óÈd2‘HÄf³mmmUTTªâw ³¯¯ï–-[ Fûžxø UB©ÍëÖ­›@ )ÐGÒhþª³¾K˜••• 3@ U£¨¨øUák¬W6F›0a‚L&«©¨‹¤§§?~ü›­Öf:¾iÓ¦šŠ @ ˆ:JDD)Ìß|:ÇŒ@ DM"‘H¾çt$Ì@Ô"0#Q‹@ÂŒ@ D- 3@ µ$Ì@Ô"0#Q‹@ÂŒ@ D- 3@ µ$Ì@Ô"0#Q‹@ÂŒ@ D- 3@ µ$Ì@Ô"0#Q‹@ÂŒ@ D- 3@ µ$Ì@Ô"0#Q‹@ÂŒ@ D- 3@ µ$Ì@Ô"0ÿ>äEÝÎg14 €Àij Ú8èýêD!H¢ftóæÓ96#„"µiÇwÒøÕ‰ª½ˆ#Üû FÃ0òMÆ”­Ú8`¿:Yˆ"ÄoÜ‹-•A lÕö[2(ÿùí T)Ðit:Ї®ÑÐÕ^÷$¸î„¹–A€}c”sÏ:ÿgO}È’Q¿›î"ÿ]“iC|#zCfüÛêSÄöñ“2ÈŸJÝÐéW§é{!¾í=ýò.‹ºwvÇàWÏŸÅgËßd›õÒpz \».RÝ›­:\?.Ù»{çä\”A¾Òðiô/VñÛÀ+ç¿ {ñ(ñߢȪ0×DØÞ9ÍÇÿGn)Ô³qéÒ×D_‹C“f&½¼qcë¼[[çë:móÞµ#µ«QØ-½ñÈëMLÜû›k§í {³î}[s¥‰(?=écXðó“;^œÜ±Ð Å€å+—u­_æ|±ô‡Üâ/B«ó´‡AnƒmYqá¥|Ÿf§A5AFP{© ?7=9!êMأ뱮Ÿ^3 4lÚ9rŒ÷€úÊ__‰ÿà¿Y±c9ãä·×ÑbînšûßóLjM§]—f<¼b5ŠÀù©÷=ÅØÌ¯B±´ò&¼Þ­çºR‰^ìòáþî¸gõ³9öÁºy‡CÉ«ëfœîé:Â¥¶8N¹¦-ün½Žˆ ½}~íÞ³Y’‚ÝI›]ï¿Å­:-Áÿ±ÔbUfw›íÛßÉ´¾UƒFÚ?(ÉUgÐ×"ùR½ûÏ sÍ@gql&_XÜB‰Ë㲨‹K„ù)Ïÿqiôc~ÀºQMÓµc÷wýbTLuãfm›˜Þ[X Ì,µ6[þ·O—á2™X$ÌËI=½lä¤÷⃎ëø,?øáØFê?ü4®šmÓ6䟋à^‘0›/þßÿšÒpœ kØ.“ŠE>?/!äÚò™“N…ä§¿¹í;ãζ-‡ü^í¥ÿÝ)øŽÆ…߆Šsg€†/7 3·åÎ §E¢Šå5“Ö¥EûY5qaZ‰ã­‚ûásž[ŒÆ EB«M¹Š1¸f\È¿ö¦ØÞ#Å áW—?MwkúE£Yš{à˜Ÿ@Rü›ÛsÒ„¨‡I sMA†j‡Q´UJvј¥z-ŽÜœ{ÖràÃÁ{–d6¬nŒxQñ†aYšÐÈ¢„Fg0Y[*?&{;sËŽŽ5½Ò“Ùš³ÝŸ.#ës_•Ÿ ª¦^íõÏŸ¾—Dê¦ƒ×æy6ü|I}o…÷+0^³®ÕœÑ~¿USÖŸ æ³æ›;*³H´mô(a†Ü¤ €f€¦ÝíaÅ3ùæÝ¯ µcÍD[÷±ì¹òØÖÐV.’ÛO¯¿>q›g¥5¦jñþ{òA½&Ŕƪͪð£`º´mf™mòî :óžSòõó÷sZŸøç>ÙûçÒšövV®þ) E=¯.=îïu~ÃÑ$Ÿ!Ÿ1ïÓÿ½;I¹Ó¿ÃäÎüþäVéÇ=Øš£39U~í¸pKQ«‚síÛÑ1åÂ3ª ñ188–7éJµ¡áœä´, ÁPÖÔQåV],áâ쬬<¾H*ÅiL¶²š† ï3†¸4+55‡/ÍUPVUã~îm"rÒ“³óD© ¢¦¡Ì­Î~3ͼ–wÞxõRi<¶/:µÆÓN¡B‰ 7;'W ’àÆâ*jj«Wv«Yÿ¾'%£î±ª«üœ¬Ü¼|‘DJ` ž’ª–šâgSÇ䨓8Ì¡DÆ`+¨kj~öéQÉÍÌÊΊe8AgñÔ44XUWpaZJz¾HJ£38<%5åÏ„B”‘’ž+”Ð,%UMU…åz1Ÿì6>Ó,ÈIÏÌÎË:“ÅSR×à|Ã[Bœ÷?—&†±9ß™^9âܤ”LAWÖÐQã}¡´ÌËLÍÎãKdäëD=t%þáÕ£“»–ÎØv½ÑÀÿuñZýÀê:¤›Îâƒq|òþ8möÕ!Ü«+=?cõ'àLò¢ÁÝYí+H²RÓsù"Ð8 JššªÕQ*ƒròÅÒ¯È )?+5#G$ÃÈ"BC[ýûZ•þ 0ÿ"ïÆl(¶ñ0®¹hqi±_Á©ÄÑÊ£ZDŸß°içýð„´Œl1ÁPTÑi;fݶ1ÍË…M»:oѦ7Iéi)©éÙ¹b ÎTPÕÑÖ©ïØ}ÃæÙÆ¥Ky¹zzÅœí1âS³ù@g)(«ij¨©kXÍÚ¿·}c%ìôêEÛÎDÄ'§fætŽºŽž±ÍȾ~T‡Q†¶EVÍ/E= ~„î:ž²xdÉ¥ð‡›§,=’‘•™š–ž“/Ä¡¤®¥¥®ßÁ{Ùª‘M‹‚ ÃoÚ±mûốä̰[Ý:ñè„T(r˜wÈ×­Àí‘ý¿éÿø=KÈ [z&Ÿ¬1¹êšZ:ÚV£—mÖ²ªŽgJÚô¸>ÿ OLÏ.fU =§Î¯žß¿Bň Ø6{Û¹O™™i©©™9| \R%4uu»}q^yú¯Ÿ¾îôãO Ÿ2óÄòÚ•º††z½†n¾ëçš–o#Çol›¹þؘ„äŒ\ÆTÐÒÓ7¶i9coÛïï4Wî6JºÐU¢ÊiOüf¯Ú“˜’šÁËÈZ®¢²ª¶:¤Uyi]×'28L!wÛ½{²¥üˆ$æQÀÎmÛˆ“’1Á£““I¤Þ«vôw†ä[݇-LÏÇ8ƒ†‰9f+ÖûºÔ/¨°ÅMoÕ=®®Ä&O+wž{zZǼè;ëÖl¼öâCj%ÌŠ*zmF¯Ø>ÁµÒ»{ztÅš}þ‘““Ó³I­ã*ªjéêjòdQ!ÁqY²VSúoò•õ‚¡×s™«ÊÐ+Ôp¤T¿qg|£=5+ ‡ÇÝYv9‚£ÖbÐ Ú›jô’Æ3O¬·=àYJâ§ôl¾ c*©iêéê4ï=eù¤®U mú“#³Wï}]>ƒR«¾Ž8ñé’Y‹¯‡Å&&¥ò%ÀSVÓÑÖuì>nÅ¿}Û®ª5æNÂÞ™·åjžûǘÕ\¼Ò‘E†¸¶UÅã\âñ¸Ž³vÜL «·tŒ,eR‰Œ€èð±.Ñ’wW&š— K$…Ü?~ör.GÅé¯þ#;·2Q£½8³a屇¯ž?<{71áí¦’‚ƒ;sÛ­xN´º›ÙÁ†–÷)èÊù+÷ù´Çn©e…Yš¾mx› ~¯XVí½&sáåGÚ°÷æ«à›þ‡ýÿythédÔ ¼®®–Øò‚*éøì‘}‹{å‰ìÚrù5Æ1°0`BG+®à½Ÿï _?¾þ,úÚ¥ån¤Í˜{wGÏBȲ$758è))RžÂ/zl—v¿žGÓ²s1qb ½¼è»¾¾[„‡<èøTrûÁèæ•êÛ»žjÆb&‹I§Só&QýÊ%1ïÞ<tk׿C÷Þ\l]¦üÅßø;y%ˆ©fàÞk@—6M´Ùy7v-ÚqëÞ‹à{'ï§$ßšTÊ<çoég5éD<ƒÍk;pÒxÇz‚䨫'O…†$ §æ—fáÛIÍì¶¼’oŠsŸ¿½ÛZIâíÞàwéù£K~»Þþ°¨bשo¿Õ«ÙÄä¥Ä½žUÈ?7ͽ×ú@ \×]†u1×’ ²Sãß>»Á¯ºýˆmL£ëç,Úv)üicžSt77úõéšN“ÉG ÉDy¯Ý#k«2±$ÏCÞc\Çõ¿ kn\>ï»iShœíSòŠ[cë÷]¤¼gÃÒýw¤8AÏ1›÷xÒò“oè &†}81áÛDÒ£oŒ-û ‹—önµà"YõfØt¾p¾‡‘ýäÂF߃¯¨”`Öçœ[3ä%yö­ÓÆšÏÉ'Hߨr¿çâÖëßwŽ…&I,ÙÕ”-¯4iÆ“þM›Ž:G±‰ÇmmÙÂÄÇ] zýðÞÍ»G†<Ú[Á¹”~ºGÏu÷É-:àóˆ¡-J2èfÅ * #dgK§1RLQÃÌëï©NŒ«Çüî?ºëéþÃËÿÄæœêƒ„¹¦!$YÙÒ> p~fâ‹[‡Ç½J~Ÿz/¾p¢O ^GtûLxá&«ûËŠ/Ÿ©/Xpfš¹¾®º+÷Ó›“[–®=ñ”,‰ï/öpX Ké6/B–«oLÿ»höÏ>»Ô·uYö"7÷Û0å¾iÁþì+Ó(UíYç¯êfR°sê¿ËßíêoùÏéri¸½nØd¿×p¼wþ7ºIÁÎ)sçÌko½üvÖ±aNMêœÑR©FH9ô›[10È‹ªw÷¢ oI<ƒN›Éoïl(Öê¾^]{5u8.¸ypÑã1mZ²”ZOü”5²ÃÝÍ^K–_Ý;X\å(¶RÅè¹-ðìØbD¯.ÚzíæåßOX~z„ÿÄÊŠºçW–ts4ÕW£"‘æ¼¼yjÕ‚%'ž| Ò.õî6ñÙM† %%0AH€Õøæ§ç­‹l.Ï~£ìûëŒ9žÂ¿=yéÅ!«»¨ìÙÞTe 5Ø:Úª0†ÙK6ÞšlÙálÙNº’,ß¡ÝIUfªXl¼8®EÁ›5kB÷¦®þïr–÷ñhóün;£¯u=bå•Cššš€U=ÝË Ã½HUÆ0›ó/Ÿ\l\º@Ê;¬©4$½lxŽº±›×覷·]:Rf¿•ÇÓxRËòÿ1VÜJFMïÇ>´/s*C߯e¨Mðû'BãÞ—•nÚ¼ç‚ÄÎßD2Ù›`¾ç¸“ìêéi(sr_ß°`Õé2ÜÍ)ýî {êZª~s{óèE¤*cX×¹ü—zîíÞ{Ì(¯–-z'IßG¾ø!ÔÐù&Çzýé \çκGغ¼+vFk³r$xâÑÍ{EÌ¿vÍ­¤j^žô}ì[ŸOÌð¯c7úX¶YLš¹4êÂâν Û×ÈAøì‰_“Rõˆ»›Fö!Uì»Í»|z‰Ié Ê÷ÓR\Ñ«!‹8кñ˜òì´ðáå…F{ÇOûgKÇIÇ?=ØÚuI×WkªòÌ#(0×4¹7;5KòÚº Wô»lìØá­mô+s73ɳ–n¶Z3Õ¢² –#—ùŒ*ùifÞØÉ!=Êhçs‚Ï}ñ2ÛŵdhWM«IßÕÃËÎÉä0j¼Ú²q¤¹}ö‚Àg²ÜÍÅ÷ÛGÕ1+—¡- J¶ðþ·ÓÔSâÒuö܀ɳ/ÊÈÀKש²Ñ í¿=åd߲ͻÅl•ÑOYô<å›Ù’K éÙ6öìðJéÀ zŽ í·?!>½OieXxk‚ìœiÇ¥âl>h•ÈENY¶Y‡N:4(¯u;úÂ@™Ttãf1Ѫ’[SnÑ­£Y±-ÍPnì>ÒÏÞ*ŵËíwÙ©v¯¼ç³Ý£Ø,ÃT M]Fn]¶`wêÝŸs|³£G>x$íâ!ÿŒSׯyHþÃt9ª̫Ö~éFçs³J÷‡N ?¸î$墷é±Ê»E)‡ªãØÁ½®.:&NyàRP»1m+¦þsÈøÉÙùlQ^ÑÐ6L–› õ«Ÿ}Ågê)Ònesì6í)«Ê$¹‚ª†ü„’J÷ËY"ù9.ËÉP©B,”ȪˆUP4üÚÐsÍ¿JÒbV¿±‹ßºÞæwäý¼º”ïڱبŒÚºôN™’f£§x”ŽŒeÚ¶ ûà3ÏDQw=ÿØÄ£ÒOô‹0»Nÿwå½Y¤ª>½}9,~|ó2]FÀ¤Ýo¡íú¹N_.[ç.m¼›³µò7v~ÀŠ /©ˆŸ™ttá­÷c©ß*Íýª¼78Qah K»QK[x bYNFn©r 3ë2õa— Wåi“%)ÌDn iŒÈ…YŸIy舼ü\‘¬ôûC€ýå¼2#5bvÌ •o´š:¬œ{LÛ ‘‰|J…×ï'e*ª?@™Ù ÅZF³KZb Ö/†”ÍPbq ‚æ‹ÅåV‰þº+—+ìTV+©#LÏ¥ÊìŠgD…òŒ®ÛrIOÇÖko‘OxÇÚ›¥„™Öjþ± (ßeŠÅ¥úÑ BœŸUYv´Üº”¤¤ç•“$åÎO>t.½ãù‹’ä­Fõ,[Ðlë³ –ýƒ^cÚ~]ÆœT+)n±*„9lý„‚)cÔ<×»Õ¢U=pY¹’‘¦ßÖËtóŠ÷ŸHÁ/z“ïÜÉ•gÛ5.Ÿ~Åf¦†§àzx|£0ƒU³>.–¾—©V«„y;îŽo>¨ÔÁ³ÿ>j. z4©òü"d©ïŽÜ§ªÔ`ìÐÕÁ´b€–ûš)Ÿ}›#=wå\òŒnŠ”Ô¿^?á…ü(™ApWD|á UçSvq°×)÷2˜ôseº@Vª"\ÏëÞ )s• a®ae·»QE3]‚¸ðç×N[³µÏَݧìõ[cÿõo£8ûá¿S§)HÅQ~jÂûAw¢ ZÍšÚ¸iUW㯈‘ÉàQKOɧÌúb`–§¨\.6„yòÊE¿'¯ò۽ЮÈëU¡œ>Zðh®-Ê÷ãéë*ª1 U*LÊËÉjô -‹L\lUL^Iò*—›QS^ E#h¿n¾ÂV[ÑÖÞ¢råö©pø»A¹Ãe“Íã°*ìÖ²×’^!5ùņ>S-¯®Så ¥ž;)ÈBݹ¼©¥e©OgÐ@‚g†% È }Õ=°m£!-™ùK–óq§ïÚ·•‡ÎØ»'º`ËýïÖ_uŸ›WØðC”šèøÙ…ï³’Ûì"~~~õ«zå¡k7Ú¶éåHªcÖÁGÖXTøttÅîpèßÇÑôË%@Züë÷Tbµ ´5Õ*±TLŒ4t '_üêMß@QQžA…E™»wå½Þ*ò1úmj"ùcÆ&zÞžúúpá¹ñáiô*ÿŽ#ŠAÂ\Ô)?1®QÖãV·l çÞÎçÚ³ kGÔ¾~bú×ö‘ä‡ïÝ^j‡b“N}}æþëÕÌ”ý•C=K*Šˆ¼wæÂ­Ç‘± Yy"h¸ð}bù0ì!Óº{–‚4ôÔ§+›Û œ¹|ÑTg½ŠclïöEÇ×8hìdaefŠ–Is²åæµHZ¥kñ;ÉKÍ.ÚT®W~(ž—pùÌ™Àçá“3Ò×OB¾mâß옧çü¯½xœ™+–LZî=á7ÍñPßÁ –Ú  ÊŒz<ñ?q-èEôÇä'òhþ‡ê•ù¤$1©‚‚-+£Š.õêÃî?½¯÷®rË |Û°ŽÞþ £ù­_»vƒÃ2€æ0â–ßbgsâ¹X{(_àWÞ;ésàÅ®ƒ’)¯®¬èÙgi`àµâÒzï6ê…EäµåÎîór*Äá¹úr£>·ÏÝ¿}÷ñ÷r›úÝ­ý£nùÙuýûÄÁ­ÖÅk‰7Vgùµ«]~ì\®J£Vi#6ªäˆDTôŒXu¶R¶rÅ6÷9é2ц ÃÎv7-жœ#K&œzNuµ²:£³Ígç!û2ŒÛþí°ˆÌOa~ÄÙãOÛq¾»}ö;™ÒÒ½“ªÙÖ@…+AÑÈÿ*KŒ¢ ¬´ ª&-Î 6£ÚÖQøvÑl<Æ^ßãSgsöƒžÛOÎR`tÉÍÊÿúóè‚1Ó½ËÁÚÚ=¼ô°b±P\ÕuulZŽžGþmO{ÛwîäU§©n^ñ!×§Nœær~£›¬€+*~çù±ÉbÐÿɾÊÜ‹O>&^µï ¢Î.ÃüO^§ì4·‰£»:”ê’VugÝÊÚt0œZ=WcÉÊYmlJO³*¬Ì­%¾wuã RüpéRªãKsˆïlÒýqE¢ªVÜc™;»O'ÿÖ <¶xάƒ÷Èš•84`Û?›;Ü]Ø‹ ÂS/ºñìøT€[g¢i[4.= Žã…ë°¸ò®…äS½ëæ¸V3·ÙGÓ›÷_—r²“ÙIàjè+I>¥äÈWZÁš?|}ëÀï· yö>㜭 &ŸàØ¥óË{DÏÜ›Hë°|†}u;g°”´©Þ‚ä« QozE£Y&È œF]eyeMžArró$ÕÍ 6—šDL Dv^™­ÕšQ QôØj\š÷ôöSÓÖ®ÚUyeq _RЦÂPáÔ²gårÁ4}¾aºm «v+O½š¸Ã­‡Ohºèý»Á²ºXªR3„«c7(o²ìÙÛŽœ¤´Ò`dòÔMmì;tíÕX¿Šéó¿ú"Ê]þ¹5xù¥¦^;=¾ßöXP3¦[õcWÖ²Ö 4Y¦S«CV<“Ÿ‘(*è· g«%Wd5} K ¼N Á Щª1oeQQ3P jħôTjP[-+íê è±Õ²üäU}Ý_MXØ´rŸ•,/7-YÞ®‡éÙéVoØÁO#?½°Ñfx.@ùôÇÞ¹ÜÌ:-ÿ^ÚçhÏ÷A”Ÿ“OZ©šÝšò¶| 4ÿÉÞ«Ð@'ü3„œ[tã¼ú¯ã´¤³âý’¼”‚ Žòwé2Ȥ|‰ÜóÁb²kh&ÿÈËO Œ`å>= ´RšS0¦ ¸êÕYÿ@ðâÖózíZj—õ::\9oÇþiÁy+ªÈµèìþO¨£ÏN=€–­j$ýÕŸxc;ËN WQ?è »˜ÁTAáéÓæÌ¨ÁÙj"¹QÃ<ûù¨uàî–a&?ð:-ÿêm¢{8*ùüªÃ£B«7§Öù=¡ÆŒuúg„C¡Ó­<¦פé¼üà›¡Ãm ¿VaüKÿsW+ —Â8Jì í«³ú­.;ïí¾ªRÈìàÖ°ÈÏaâ±²“Ñå+qugø¨G,±«Ä&ÿ¦¬ùìág~w€×Y¥¨o›©÷®®Ëš¤ ùÄè ®çvŒ/ãM ‹ÆÿVQiªxyŒÉ%åê#rÓâ~| gæ™©Š…4rÇðIñvFZlZÉÀ*²¾ÎUÖ4kàäÖ¼²ùß¾îØå§, šÄè®Ë~e»]ëß#w_˜•!œµô`ïó3ʸŒD!¾KÖËÛûMÆŽ]rê„~Ý:61×!_;~räÅÿ­›¼âPR6ù5a½w¼Ü>¼aõce•èý+šªŠ'ʨXHÒôBsŽ,4ŠÚ-Ÿezc©eyz™ßtéÐĘñüÉó7ÙjMÍ8#$C3J¹®™y¥}ƒŽ«·ïÙVÞî$Šß9cäª ÔHëž#gÛhˎꌽ'.¸öMƒ´—G»Úœ4mÞ³G[{M‘•œõ6,øñ ÇñO,iSñE¥FlçE†G+`EÝŸ0 rß¿¹wåä¾C7S%bªo [{é‰[³º6,§€† :9YÍ¿úV’úòhCƒn͔ũÁwï¿Í’+*ÊÕb Vɽ²ÔuM- !î#÷ K·K'tãåzù"kø¶eûŽ;p<ãé¦Á*ç·ukk%IzxçÑGº‘£ñrA”_w/ì\“÷(àfÛöVÚZŠlZV\Èá5>3vQb®ÖùÈ‘¥ZÅ™­à6ÞKyÜÉ\3ÒÙè [{%IÄÓ‡!Ñij¶* È”’YÊ,•5´„{¾Ž3·m^Õ©e7KS^Ì<ø\‚Xzkf(Ž˜­ÑhûÆ-{¯”’×g—;_ÝШ}ß¿šZ(ÑE© Þ†¿zø2}áñÛÿ2©FΔzQÅi/_G¤ˆø«8.å§>]u ž§ØÃuX»wÑ#ÏEi2Ü9±žß†Îý{9š¨‰2ã_ݽ÷4¼°ò•Ÿ‚›Á(ÜA¾™¥÷ӘʖÍBØk<ëØ¾]’¦ ÑÄÓCž%z®YU8#;yfÊ“µizÙ·ƒ^òáT,‹>ò[/uPÍcDcXùðì×G¶¼Æ*ú10ª‰ë{ø?ñ÷(o£V‹É "Á•ô–l4k…ãŸgà°zS«JûÒ—¼p•œ¯Òjf°ß+óA~If»ÄúŸZçRª™Æ=Ø= Ç¤  ²ÄïÿpX£RîNã5û?ìµ0UA»&ÙàÑ¿—“‰º(3áEÐ2ƒ G$’ÅI© 9L;rà©Ñðã©dÜ;Æu<²Ê¶{×NÖª²¼ô1ïB‚Ÿˆ†fÄú–¸iŠ‹,Ò£BЃ¨È𱕣‹Uf|FzèÚ©ƒ×N-}®YÏÄ٩ѸeÛ†5¯ž;îHïÿ‚_n‡¥7翺ýnBz^Îû@¿÷\5ýÍzìÚï§>_³ÙÿrîÛcŠ^³.ìù×@™üÐØž¾ÛB¶y“è?Éý¿´UÍÖ5¶lØqäž=³Lʤ—6dÿÃ.Çæç'Þ>ç4®¾‰Y¯6Ss³m™ƒûoðR=ÓxÄâó†)9´v Ÿ´×£áîâ(0žº…}ÓAóLi_:n̴׊”{C&m{ñ1>9-çiÀ¾§òÉ×é\U#SÓv£föhnŸ%íú–‰[/„†<ý!£p—øVW[›ÏŸ:£”³ßºûÂÀ“´¡‹E¿KË>·Û÷•“Ê&¶-W_Yíß¡åpãƒ1V{.ß}bªãÉͻ¾w¾àôˆCƒzæ¶nÜȾÛð1ŽZÐ9#ÆÎ;ÿ`òýˆ”ÜðkÓF]ÃØJz†n½iòúΡþÏ__¾K™2eh÷S-œÚwÝ«ñmkýüO%Šä™òf‹»×‡VzNÞE-yvÕïè™kW.È'ÅÅÇ–‚ˆÎCgLu¦üª³O_‹8âøcjdÕÞQ¡JBM‚ÐźÃý¨›-«žvT’µaÑÿÀ/^Få“‘¼˜¯ª¹ÇÁ¾QëNƒçÎ(^ÕÝŸö;F”™3KøêИ¥Ç^¿ yS<¶1s„ŽòjÇ&NÍÛÏ^µ a¡uL˜<¥Õdìü=/Ãwµ0Ø^ƒ‚–¡mk'Ÿu»F:—ïcÕmAà)ú…£ßHË9¿{Ýù j±úòꀎÝ.QÙ0c¬éµô¿ã³{0©¼å ;–¨`3rå‘;Ññ‰Ù±/m-˜z”µ ¬Z¬Ÿó\•s.ïÜuåÙ‹ «…ÃÝ­:û7nذýèéžVô„H˜kŒ­:uûía)‰)©éYÙ9ùù|qÁˆŒÎSVÓÕ743ÕûŠÑBŠÖ^ÞS±Hë¶ôC8ޏ 3ÓÿÌx×íSÔÛØ„äì|5´˜ÎVÓÔ1457Pÿrdž¦¹çˆI=ÉTÓ?×vK£Ö„VÑÐÒ526TüìÀ\Ós8÷~ñ&6G$Åhl5z66ÀÓ}.]ì.is瘮ûŒ»wÝ_DÆó%ƒ«¤­gdim¢ OJû%ÁƒŸ¿~Ÿ,’äõµ L¬­É[,¼x®w6“ZkÒ4•Ø…ùhìöÏ·„Ù) Ÿ’RÓ3²sùäkB>>eõzÆõ- *鸮hâêw%ðå«7)9Bc(©ë˜YY¨q@˜¹íü¥ Ê4%mQ1«YŠ©Í÷¿5þS\BRJzf6Ÿš Œ´ ¹jZzæÖ6š•=iµVょÅDDÆ'¥æ ¤€4&OC[L‹v5Våc׳÷a<þ…¬)¦l «P&rË^óuþ.!%[B*¥¦]ÏÊÆF#k{ãÂ8!;y“˜Ž iª4èX¿±{ïaêN­É(•á2³È9ªÚ¤ÀÆÏÃßçŠq:[AS×ÀÒÊ\™I$2”Ìí[™7i3ðŸYr»–ÉÁÔ¸€Ê¶½Æ9õŸ\0\*‘Š™T¶äÇmЪó€6݇Øùä‡#Š‹_te³Öîö†Ç³–ûÏQYZF˜ ibøu+6¼ÉÈ~xìÄC—±UVCil•æXÿ5„F+ìî@~í2g+0K¹}µ\>y’àPöC£©YvèíÉ ó^l²S%ù“ßZIO3u›z¥õˆÈȨjÎ;)4GQKÏÐÂÒD¡Š<·è9÷‘»wxxÔ§”,qAiÕ³j@fÞö†ÿX!­0ƒ´­KݽÏÂÿyŒ{—š‘+Á Òªæ(ªêÖ327+:©¨kÞ¦³E‡nf<\2©L&©°jrÁŸºæ‚ÆÔÐ5"ÿj *õ&½=¿<+}%0ëyô«jeIžc[÷ϵJ±Tl›¶±-·£[:»W\QRIÓÀFÓ ÂîÏ¥LßÜ–üûšS aëÛtÑÿ’ö•(è[µÒ/?Ç MÃÊ£s忨š4jgÒ¨ÒCº–Mt+< ®i«•,PGE»>ùWíÔ2TôZWÒÄQkÞÖ£BXºº¾)ùWí¸)W‡Y³oš´X©«gMÍvÌÕjà¨U!2ºM‡nåòÞ½Ÿ×#S¬gíZ¯\çDLÏʱ‡U•_€KϪ¢eÙ6ÿ«ê?æÓrä®ç.dÌëVYº.Ý›ðÚ ^žKb?FÉ—ªª0tE-W^U^§8KÕÙ±ü”¾,Ãf½¿àÝ( GͲ‘s%+Å~†Ê3ˆfÓ¡ëg>NM£ÆšŸ)•[÷¨j•î?$Ìñ-|<ä5`ãs°œ²£RU–cllªÄƒŒ<ŒËâT·â 3@|›S ¬ZmfàÒ‡¸8j<“geãX7'7Cü0#Ä7ÀTäPM¡©¡±å&(F”½cëÚ ÔMÚ÷íUEŸM¢H˜âP¿zÄÚ®Ûò®õš¤ºdòà¦õKz]g} »pê»7_x!à6è¾ûìq»Ú¾²%¢„@ ¾Í.[ÓèØoÉÕ-Snîú—ÃdÐØl&.K¤R‰H Ÿ~£ÓŒý{çÒWú±kx!~30#Ä·ÙöXœÄŸxýØÑË·½‰ù$QC™é£ú6Í\Ýz÷ë¨ü׈¯ 3@|4M·É¿_Äïf@ jH˜¨E aF ¢„@ ˆZf@ jH˜¨E aF ¢„@ ˆZf@ jH˜¨E aF ¢„@ ˆZf@ jH˜¨E aF ¢„¹ŸüÃh¿:!u‚À›4idddô«‚@ … a®ÃìܹùáÃêAüê´ÔQ°„„œk×úlß¾«F¢{ðàÁíÛ· ôYD"qwwoÖ¬Ù¯NQ÷@%H†NÇ{öÌmß>ÇuRê&/_ÂÓ§5¡ßÑÿ}Ì60Ó†?»¦D>Ø¸è”ø}qH˜ˆo s†´“% ÌߌX\“±qØÖ ´ÍêÕd¤u €§†I£9¿:!D 3QsT%‰ÀÿôŠ¡Ê"ñí aF ¢„@ ˆZf@ jH˜¨E aF ¢„@ ˆZf@ jH˜ß†QÓI`|qìnõC"ÄŸLæ¬Èók·ß’*(±0BÀçK©’ã¨êÚ9¶ììÑJ­èžÄŸ®¯ð½˜ÏVà00‘ _,[ÉÌ®¹G¯Î¦ åãL}÷ðêaQñ| ÑèJúf–¶öN6õ˜?ùöê„B³0ªC^ Åx\僤ì,¡„ïCd4€9 îL´îƯcÐY,¢\‰ìW'@TEfeS·ùKM7;+Ô™³f}{3Ž(7åñ¹­ƒÏQj<ðñý6Št2S»åÌ…ÆS[¶óÈ:—Wcú»Gþ‹Ç­š=É`ùʼn.JEñemì:Í/Êe¨ÏÄ^Öõ”ó“c‚®œX0pޤϭ'¬ÑÄ‚U€Ñ¡+dŸ÷в %ÌîÂØÅ0Îzè”ÑfŒMÝ å$øÆ@¿vŸf 4þÁºAg1è!ÉjÖ³@ªrÆýGÇDG2Æ\îiÔÖðD Q'…™Æäq˜Z dùÍVmÖ®sç†\rg¯~#†·ïbã}¸õÌ‘iÛÛUñx*šJl&i)ÛºxtýKzxMîÝHÕyÆÜq£=î3WÆ@;»µåê':«n¼ÕÁ°ð¶Ztè9}íDwËü?X!¾ l.ðXòɑ夦&…ˆx襀— Éá—Y°Ê‰xX{¦YÎb]W`2$ǽ­:õX3Åš\œ„ÊÔ[;ôHW/$¨ÍÔIa.!+e[vžîL¿ôôäYR˜5ˆÃeÒ‚ šÓ?“Lf,—nn«zsãü5O$MFnšV¬Ê%4= %ïG¦¾¶ÁQ6ƒšðY,¡ˆšñ˜Ë¤"È“G)+ÈUX,AEá¤AëþpÒÔKŽÒYÀå:xœ’U—ä~6ÕðLþIÅÀP‡¸\8³Žçêé“ ’Å‹ †Kχß[TpœfØÂÜÉ‚«Ì¬ùû$µY,þ½Ÿñ;P×…¹,‹KYoŸ/z”,ub³ÒøB¦Ÿ¾zŽz¯ž•=®×âÕ?(¥µV†CÏ”¡× ˜4 ¸ðï(¸øš ‚ sáÒ|Øt„aÐ{ Lî¬Rö/FIxO‚ûïÁc/ø:‚@J*5 %‘W½@üd~+a&… À`¤—ÖçBåD$’÷­¡«¨ Î}œд“õÏJb-F,ƒéÿAî8'PÊ™:ü=ÞÔ‡½‹@ƇÄX~Ü áÓ5hí Í¡­qÉéÌz°z!ô^´‹!Ç`Ô˜¹F»ƒG¼`™ÜsAûI‰0j jâ7Ф3ì4‡Y½`ÈrxúyrSA „£ó`s\x6<Ø:þ g@½ßÖA¤„½¿¸üÁ³tÖ裴ˆ÷þç™§fáÕ—z7>&"‡ÑÐfæ'…¬”Són=~/Ñph0hz#k=fBHÜíwƒ„Æ×6·ÖÇžxr?„ÏæÑe|a¶Tù¯±ŽMíJwˆÇ˜tAà“³_uhØÁÛÁ^—öéá›Sûb$*\ºD˜#àuÙ¼‘ ç÷­!µ—:.̧¨(ß¿¹¾Ç»ßÌ Ón×–´øÜ)a¶ÇŠ£­‘š(=(1@¡¾öOIlíGJƒnáÂVØóÛS­¼—®Ãà¡ Ì¡ÈC×€Š:HD fª)‚ vNIi‰³àèV0CN&й%áe8tJj”[ÑÌÈêRÕ$-Õ]X ÙY ’€0ü¯ÃÀ#`« B ßáð. êÃo*˜ž³¥{«°g×¥tŒZ>ÒÜþñ•ø‡i «ÎöM»ØçD¿ßêóèÚÛF#œôF¬p¤VOÅT„‰³6&`wZÒÆÎœ™p$àÀ%|Ò&7cȤ!«Ïü·äÆV·úZ%Æ&ò.œHj¾ ‡g{5Ž ?Äï]òTe¼§OwL&¹½ðØÎMï6n´cü¦¨ÍÔqaÎ|ÕÅšEÃ@"¡Z#uÜæDœ\n¥R1ž—•š˜q÷ظ)Ë¥jº }·›)BNšŒj ¦1ÐàÈB¸Y0Ù ý µ (fëLènL©2 ‡g§`ç`@ÖYðån\ OøÐÑð¢nÅ…³yVnJž èªõUaJºôèP“=ÐD5¨ã¬bsàâµn¶ ±×6¶¸X–IcWL’wnû̈³\®’F¿Ù›»öî¤/OÅÑÑáAxnl:€ÁONyí„©Ìé £Ï@èGP?Š-@“KéèãÿÁ„m°<z[€$Z·ú²4"“–½Â ’Cáï0`p.@gKVŽ`ÂA˜l "¼àTðAúÇjEa£1µ%Ñí&:+zxýYîXwÅ›×3ÚŒkÍË0Y^l(qØŒ‚'O4uue¦e‹ N&¥6ì÷$¨æJèJ‘ææd@¼»ø:‡A>u ÑôÝG«ð°ß¶„@Ôfê¸0ct%E{¯é+ìwiß¿¾ŽÏë^^ ˜Êƒfï™á¡‰a4Z)C­lTß};ðñ•h°¯ÿS^k‘ ÁqX…—AtÆ]¢le ‡Ð(mha ùy T£¼&lUЈΠ<Ø¥»k‘?Sã ƒNv Èœ²*ìo¦èE€·ð+•î?ŽRÏ—1´M][ìˆHnaÍWèoÎË€C§‘_5ii—•OÁÃ,zõi HàXLì9|ý¬ã OšTF*1ºÝ@§ÖêxQ½‡Š 3ñó©ã Å%•âØ3üTÜN/^pº_û¾ŠåBa4:VÞcMãiöòðÜxôÄš€i;»V|G§¸Àð›ÿ¶}*\Fõ‡Y;Ád$4W¡êq­B¶ið6 lG™ÂtÕéšIÖxÈj ‹MÇD‹+·{Ù &J)5lfX_;<üJrÇý- *žR «¥Ã͉¡/zº©K“’ù¾cZÙØ6PÂäñåi¢ÉxÚ£–9.õ~rÖßrêðzzZ!¯6ݺÆkÞÄš'ÍÊO•²­ê—¯à"ˆŸ@Ý-ádÔheªgLI{£‰ûœ }ü—Ú3ðŸÎwö*PÔ"_\>9žåº‡ñ&C\'µ3¹8vÙ‚±ý›˜© R¢ï_>¾zï3–uWæŸh:¨Û½ jCZ`d ÜÜ, 0nHÑ–8º—ìoÕf”ŠGAæî†¹¥öÌ,Ú˜æï\ø¯PmÇ@ð˜ÂŸ2hXÂ’ý°ä;ï§®€Ëžì¸B82íj×9­´Ÿ?yöNˆ‹Ãü§8¸§ÊùEï„ Ý|óÖ®Cs.#pMÏNJqommhíð„LÆk`7s3ïØú‹£çˆ &Óʽق‰6êLLø.Ôo÷;D'>ñžïÈJç“U®ð·VD9ŒYh7fo—s«ýçœÂAÍʰÓ'«_ý<ˆ?“:)̹q7|&¬¶ï>“í›Ýÿ´Q“éë–5Q" `µÙóWÄqNå'ïé1àå†-Ý÷Œ]mÖ´¿¸mx¿#Æ—níaRQhÕ& í?çöa¿3[çz犌ÆP×7ë½dï†ÖmššÖÉG„¨“ÐèN#;¸Œ¡zBà2\"à¦í¶ ¤c!IEbbàÏ¡ Ãq±P*‘Wšp1®ïí¹™.åçã¥ÝÏ,§·¡Ó¨æ}™D&ʰ-ì^hLÆŽK¤BZ4]÷Ä…tò°Ä|âÂõ=í~uŠ~„°{)¹ ù2à©A«A°d€üfÃÁ‡@-Ó«S—ƒ»y5¢"­ç\X< üŸ¦}ÆÀß]€N“ 77Á–‹ÔÂŽb&ôŸ£Ü!R’¯î€­' Mt4ìÛ¦•Š QÇÁg3iÂØµà¡ ¼ªk×1h :ƒIÃ0(œ¤Z®¾ÆdÑäû¨ù5%âê5cÀ`1Ø\&&Ÿm»¸½£a ºPŠ.•4 P"¯.?Fà©ÊPºâ…aL6ƒŽQn׌ð#ˆ/Q'…YNaA|WGDdžSzÀ‡æp:ôÙz†Ü‚¥#“AÌGØx4àº/Œï‚þ—ì)²˜ÏK‚– Àk„À‚ …•½¨¥”CŸÀ˜ÿAKˆ÷‡6¡%¸š‡ á`.üï4Õ‡Ä è³pz‰ÝF&4À´5,[Êøï¢Ê¤|ææ>ø_ð'Y).“„†Þä•.:ªXÔéÇGÏ'-ÉÐLÌGÎw2V©F„RÑ£}wO½LÌÌ—1uõ{NkÚÜNÃ!+òýϳe 2€€«Õoqë&†t§ºŠ½>õòÒ•Oy'ÅšÎtŸéÑÖ¾$><%yó¸+opöÞŽ]ÿÒe ;øñÔ]aþ2ŸîüoË©;oâRe4Že«~s§y©cqÿÄu—h*\IžH¯IÏs¨¥Ý™=kÏÇ|‘DÁlÒÊÕ­´©sŸÙ¸íè1üi7ŸÙÃŒóÞ,™µ(,cí6|ɰ–Ƕ줰jÝÍïlF­50Xpw ÜÃÆ‰ (l Xt±d1.!Ën˜¸´Õ)²‘À'Î"$ó, ù`<²`Ý:˜à ú¶ ´ ¨•žuš“R ¹@§Á»8ðï…Æj“ª `\ M·‚j¯ÎÀÞH˜µd ùm*cbÑ•5W.%˜ÎÛÑÎX“)ËÍ;½9X$&+4BAÝk­£½1‡–ùa¶ÇõK'õ¼ÿ6ør„ÃÜÝ¡ŸO àçß_wÝoú Öö®Ž¦ `p\'vhê¢Á”doîwÎoeDƒ]Ø2"êÄÝGaÜÖ.-x˜LöÚïê›$  3†Ñ2?fЛ9Ì™`o¤âߤ6„@Ôv~ga~á;þªÞz¿^J‰×û¹õ5¸çÇ¿0P½õˆí´'ª­þk²æî±®T8ͶK—pZ¶µñåÉr§øeŸ÷p¶žÚßEïÓøþÌ6ú?O:µà¿½9æÿ½ ;3),Ц,btœï;Fó×ÞaÍÁÂÕ`PT#-(hű 7ÀÅ@§CÈØv„4/cT=HÓ §T&Í2òÏÀà Dæ±e O?ÉB`b.·­Iù}õ–Ò`gkÊ—;’Р['ÀŠ–>J|–@ÿ“`È,Xtø÷ã'¥ œVÛ‘EÄ—Ýnˆ[™N˜¹[} ÃD)G‘§L>\Aµ|C ºªÇc€¢Fç‰Ö—†¼|û,ÅÁTOÅD×Á”¬ É0&C™Ë‚ì<иxÆí½‰†Ú4¬Ç“WÌ´kK|Õ¤A¥Ž&KÿpæDF¯.Z\Œº‚!?‹ßY˜ëu»~”· Yªéu÷ìe´wõCè Ò|ÞPÃÿ®Øÿq†«¡<äûÐkÒæ} TYöhVçoÇîx<Þ4ì-¬ì¹mñšgþU§äp퓞Ypù+çïgþNn=H;†‘;µ] ƒ WfÃì;°ÓÚBä-è>ö[¢§4n¼>þÏÞY€G­taøK²Ú­»Q-´×âîî.å¢ÅÝÝ‹»»;ÅÝå÷¶ê]Oò'»¥»Àt¡ä}xJv’Lf“Í|sfΜ‹žk^LŠ2ØÄlšC™Lü‰ÉÊÜotõB¸fÇÌ¿Ptü¤È"áNŒª”«ˆ~{Ï­,×2Òënn¿|ðtŠ•L.Õ¾<¿5s–9;ØMLP±}úæÖ­/9MVÈØ»¯5„¥¡A”ÿP\Žé½,%WؚƀêÑãy‹o?tô#…ù™KÖfyzLzûAf!3^%k)@¹To[yÅðeÃÏ_RVÂí>»~]Õ›Œ§íÿ{dvy‹å7žiQ*TD_‰dÑ€ûh™-›³˜û߬çÀvß;èW‚Ã×G_áU \ÍÞ¥S*ì¸û ;#%ÊÿoR2‹¤þ3»Ø¢z¤&AŸú¶ @ÀÙšŸjuû »~8_™3›[ŽEñç(ÕSfbzþf HŠW=N•Ýàõ”åLÕä'—Ï}\sQíÒþr)?fϳÿ§›@OÓ|ÓJDibV ¾äQ±im™X½¢iôy 9‚" WÏp»Ù4§o.ýÆ ]§EÖt;³jgŽ>M‹Y@ ÓȺÂLз¶ŒýkÐüK_ëYÐüêó~o÷‘eÊ×õž¸wÇÐyª²ÝäºÓ+J†L4ìJ¹òXulǼ’¿Œ5%)VXYI%lš§*›VuIåYFxt,ê4ÀßñgükA"â+n††Ts1tqˆWÃVµÆp4âmoÑ—Dš—Vz:w:˦©SâÜ>¸5Cvg¸Û–ˆ¨(X¨”¼0O/PÖ˱y*ªŽ……!7NUÄÒ´ m­`ÑvŽÖDe¿,a4³¬ÔÊÚôõ[ªŠÞï¾Ah^¿Qq@'­cô`ÿïJ„æiÌP¡®ÖDüóäw1ƒž¡‰·ÚËe+·õ‘áÞÃT†É¦÷Uðwœ{.…kúø9ÖlùhѼcGÂê•t1¸å ü|² 0ëµ:‘D|oIxPÛÅgž;Úµ WÓÌëRü¯9¯Ò±-\¦d‡•7fîîVx{¿ÇÁ…{X¾SQa?óسð<âôã δÁ£Ìþ&™£ƒcmLzŽIóqi¼]`a W?´nŽVqv*Ú6GHÈ ƒ¾[V@™WoI8º.Õañ{/òÕù™=¬Ž@øÀö¦M­Á³[x€Yí ¥Q²,6îE«º ‚½=ÿû;° î(”Óú`ÂR4l€ÀP˜Ã>UÁ‰KüOD¿,|ªÁn-õƒ.JAòÛë+w°Ï_Ìl×”£»å…òç²”‰¯–:YIm¬e¸{þ\œm°ìÍÝgq€‹˜sÍ%™ˆá"‰Å¤†X$¢¸FŒX$‘PüØ<Í{«"‘\!ÑÆ¿Ù>û.\ÝB‹Øb¥#´·®¼ õ¦T/¢_&êH3R"!Ú¼DK×Kó/mð–.fo.'õJµ–’gsã~íü4*’@žšùóÚ»qàÿEaޤ.+´‡~y²œ0kÎT/·kÕ‰·^"(ïFÍ Úÿ©*þ½ÃHïaíB–u?ujçæ=cîÔßÑê­ýkæã âj·ÄX%EýYñu:”GÁ&Pª ÑAD0S@¤Cl­îþ‰$P˜¡Sß·,“!h:w S(@z¡þ(Ôa gq¶Ÿu»£Šj-o’bXXÆY8ÄâPjù#9íoÃ;i4ÈU Ê!EÅŸB) !| Ú1Éxf½°f/¿W,‡ø·WebYÅA•­–žÛ1~ß-(…¿¤ýöÞÞÕ*Þß4zÏYkë’åÉm…c[.,•Ç‹îÇÜÑ€ž{då3ÿÒ¡äîUw.ÇC½òÔ¢(ŸÊm-r…»³­Ï†¥Izîº82ÈÍ‚fçF]½gÍ:5l«Yhƒ\þ.¢û·îÌŸcß©¹·OÝ’]dW7-?vbCʤyHaçðõ_æ÷€*Šw«P¦·1Àh=ÍGÿ`¥å†Ô®2Š9«SÓ¨Ù¾Éê îy8›™aô|¸´Ÿ½­ˆ$¸zF§Ñ³ÎžMÇ{S"ÞÒå³Ò³â·}땞߼,ÁÏsÖ3Tîº"‚ÑêÕ¼ƒ»8°NáÑ‹=¼¸×F«er…z45\E£ÔѰ븥 ÷‰å?ê‹Y@ ø¥Ç.ÐRÄÕd,£R¦¦¦¢#1:erü…ÇSDe8ûªB~§a{®Íì;ª™ãÉÅ+?¬ÄÇ…Eac»»–Ÿñ²öú•È5|J§¹»æNèÔZôºeùøøß[6…;U ±’p–È,ÅøÐZ½1ôiFô^“áçNÔªÞonrù䑜t«’3|zo­Ö¿d•Ë6CqXmªö£Ò üD~Ga&žî—¿Êľ¶ø§Iç÷¨Ï uá˧ý[¡çÒþ§¶ùt6sP+Uãa«Ê;åÙqôt•œinÇù;Ž·]¸`p¹÷s ýfœ}ž³_ÛãþZ5 fŽõ:mÿoûÒ5×påÊ‘^—ö÷_{¸cðïxë~u~GuaÝËNˆÖLøÏCX–pŽXw=b]zRåzCW~tQ²Q¥«ý«E ·.³÷u™ý^âÂ#þÿeø*~GaþЏćCÏÀO%prêHß ûåYh:²€€€€ÀïÎï(Ìß…j_·Ê#ŽvÊw¤wǃ…Ž/ðÎJñ»~wþ8a–ZÉu.˜o¥W|²ã„ý»³›º<¿R)?«ŠfÌü4þ8a&óyù¬«ž%̬íí,~ok™ A"É$¥¤(Ìš…Úµáâ’—ËŒ7P@@@à—âÏ«–Äæ.îæ_>ìwÀÌÌr÷n³›7¿´ã‚$qüxjr²ÜÜük—5 (~´_¯ÿ‘³_†! üÜbb4Ö?$+ð¡·M²:9áÿ )þà Hâû×)çî0ûÿ5ž‹ÔdµXèZø¿øó„9 Þ­L™ê‘I«ÿˆD¢˜˜¡M›†;99}å)$·‚`úˇ~uvìØ)QUªTÕëÀº ,ËäÈáõýù Ê»kÿ£”ûú/úÓ`XÖÆÖ*>.‘ü޶ 'É:N"‘üß9¤$ËêT+ôŸ. ð'#óoŒ••Uþüy¾|ÜÃÂB‘/_ð× óÏ`ÈþjµzÀ€Þ&,ÃçhÓºmëVm~Tn\³†eÙÿÛlý¢¢¢&Nœ8}úôï¹úêÕøÓ„Yàà燛´¶}ýúõõë×ãââÞ¼ycgg÷å2—ØÇÎÑ£G"EŠ4hÐàGeøõDGGÏœ9søðá666™u?A˜¾­V+2©»§Êœ1ǵ>lÅÊL†Q(&¹ôÁƒ¹¿S§N5j”I ð'#³À7`t¼2a:¤5DnŽŒŒ¬W¯IfÒøºIÐëõ¦j­ZµŠû;}útA˜2A˜¾F#›r 678…V©T¦2(3‡””“|Á¨¨¨þùÇX€½{÷V®\9óË ð'#³À7@Ó´ TN-È|aø/816Fû¢(Š«©…µ†2Îb6áÕ¹'.4ÅL‚ ÌÿW/=|øÐøQ¯×Ëd2Nž ‚8}ú´———IK—Å1­(𙙥¦¦ZXX˜° &‚0 |Š+Θ1#ý£F£‰ŽŽÎ—/Ÿ`=ÿlL+ŠœÅ̵ÃLX?A˜¾@xxxFa6Rºti+++“”çÏÁ´ yÉårÎbš_™ Ì_ gÎœŽŽŽ¯^½JO‰DuêÔ1a‘²0/_¾4®"5À¥pêÈý•H$™¬ÓÜåL5m]@àGf/Ó±cÇ‘#G¦tuu-R¤ˆ Ë“…Y·n]¯^½Ò?¦ORš5kV—.]2³$\³àƒà䙃 Ì_¦qãÆ…¹wïÞ&,LÖ¦dÉ’VVV‰‰‰9º~ýú™\333Óº… ü±Â,ðeœœœîܹcüØ©S'Ó–' jooÿ0‡……q “KµL;‘Z@àEf/ÃÙp 4 s£FLµ$óŸI’•*Uš3gNÆÄQ£Fe~IŒÎ_™]A˜¾ §%K–\½z5gB5mÚÔÔÅÉâ´oß>£0»»»+V,ó‹!X̦Bf¯¢|ùò"‘ÈÍÍ-88ØÔeÉâ„„„ØÙÙ½yóÆø±M›6&)†¹¹ù=ꙃ Ì_E¶lÙ\]]=<‚ ‚‚BÄbÑ•+7~öµþÎŒS©t"‘ìæÍÛ:Ý ÉøÊåò”ÛÀÙÙÙÏÏïîÝ»aaa¦j ‰D"•Je’K üáÂü£×ë† ‰`˜¥ÒÌq“6Ê?›)×ú|!~i‡éÓWþÜH’¸};®mÛÑ­Zµø!þlmm .Ì sDD„©ÊÀYÌéãÜ™‰ Ì¿1, ¹<¶yó,-M]”߉˗#)é熸ڳgÏáȃR©ô+5$Aļж±µY»~µRi³••H$$AØÄOïùIpå¶²´îÔ©³°F–Àï… Ì¿7,Khµ–´ÿ2a ¥½ûw¾&¯zº9åñ à)f¬}sªí¯³4óSËö9²@˜l‚ ìy^¿~CA˜~/aøéH¤"—lÙ¾¡gÃ=À’e_cõ—€§8+'~¾†€ÀEf_A•¿{Cü¿Â, ð !³€€€€€À/„ Ì¿‚0 üBÂ, ð !³€€€€€À/„ Ì¿‚0 |‚äøhv—²:²Ö¤šÅûî¼Z­Ïš!UìYcECPgWõ»ùä5·É$<ÞwÓuéŠ.Öœæä½ã³Úö[eßbåž?µ†« ^Oí3äIŠqÕ÷¸%mK¶]r«@ýÞƒG-,èïÌ$½¼pxëôqSg›»ñÍóz¶†£T÷U)<ö¨®r×cós’½~paÉø>ùÝ"ª?´­_I®¤’ìͶž¨5¨hèØs°?2¢D¥Õ½Œ_.w•¾ªtæk‘:.jr={.eïéÚuͳmM™=1ÔMf<ŒÐÆŽì:ò¹ú—«©¬\èÉ 0ehÕ$EaÿQÜ.Šz>àn¢ú ÎÇ!,Of„V§bä ˆ¢räq‚¹ëwáì ܈†G6Áhþ ,ssÓ©ÝËï=ÖHÚ,m”×Ëêu )¦ÈÌ&FLjüúÇO_Jy縸ˆB¥aX‚› ,¿¦íÊftGç %#ÌŸ=pdÍêcÊ9¥ïÿF§W¼:Ö8Ø+¸T©’éçÛ¼)¿â ¿€£ÕZªZ©P!cºå›lœjîU O¨¿!A9Ï*>]ÌøFEl‰é¾úÚô&¹Òr±³­Ù.WÍví[؇$Vƒ`µoF×/;ö(1dß¿#+¦-<`k[eüæ*•§4.Õ»TÙ¸{&T7ÎzZ­ é?Ó|Aç…5k•¾µqöôÒÑg°0év¥a©©M¡°rÅ<Òm=ÕÑ€å4›ÙúÂL"gOqʦ×Bûv΀çÒEäi^üÜóbã.8ºA©‡¥ó{ãEcT-U2>mþüªM|>¿­ƒF÷6]ÊwDs52wc4Zð}$ÌäàµZ‘Å?|4÷À)Hâñ˜A -̤ðÈ µC£þkøz¼[|B,…˜J+«ò5ŽžC¡r0cþ`Ù&Èà&aN o†nTòâG¯.\ÙÿZ¾ñ;kô£U*µRss2Ê"¦T›:í¸Ÿ¿ÂœY6»†Þ¢HÕæVk%Ù÷\]5»ÅŸûˆþS13z­$ߤMKŽ·[£j‘¨‹ÕÞ® ÏâCë2~Ì^´y+%¿nŒÔ·ÂßýéézÂ<,“¾lY£K½»åƒcã'oyd^qå”tU~‡s—~­R Š{eæ¤ÝO­[îHWåtJöVkò®m ìnZ¦^°ÂpEŽÚ“*ÑcMŸ& ŸXàý]¬\™t\©Ò48v™ûè^cÜp¿±5šôFó‚ÐÒ†þþL€—øZ2¼~s‰¢TŒŠS7á7cл ö<5 ù¦ ßÔ°ƒî£žRRÕS°æ0Rõ0³AцÔ:îÑ©ð÷0l?- Òèß®"¤\E£ÞHpÆô¾8´‡¯Bî‰þ£j77Ñ~ ’5¸ø7ò/@¶æŠ SC¡ë4„Ùñƒß2[gbÅ>(iÈ@¿@áž(MüÁªl€Öê5éï ËZ{g«Ö„ ðéhRòÖ¬ÃÏò•«XLöÓo×4{ñäø3„Ô³T«d¥{„z¢hëPY¼•³â3gPתӪè?ü üɘZ˜9XE`ãAW·žy°GƒqEö°ûšv¼.éy¼ÈÝÅ7ØðIdŸ½ŒýMzæÉÏý·zí¤7@øˆŸÑ-Øg‚qãÒ¼©É »µ-ü©œº•1ß¶æaä¹Kõ‚ÃÒ¾D…"Æ´˜·ñ¹5cæ7[Ø%ìëúèUŠ:{ᢙڡG‚zƒf½"—àñÔ GüJt†>íÊ!¡"¶DÂÅ ·æ æ,ƒI­@¯‘xZ Q©0wÇäÑØQµûcL#¨’>Reb-ÖÄ•@¬Ø 9®­@ËH nq"þª€äªØræ©Õ•+ðýÒ¾y±hJ7Çâ=(S~þ;ƒÒ^8 6¹°n1òWGÐ_XÕšèDèwµÖð2ÌszàïëX{ìp|Ú,Áˆ‚ è,.̬^Ÿ§ÖèY’¢¶r¹„Hëvah.]­aH •¬1&£×@V¸FvJʵ„å£WoK õN‰}®·t±’ŸºU,£LP+U|o)"¥rsÉßS†N‰çógAHRKk ß+¤RÇÅéX‘ÄÚ†R%j4:îêbK™ˆ­Ñ$<áZVÐ$¥Ä>ÓSææ Vœ#LDˆß>"®¥LP) JLRDìÅ÷–·Ÿ˜[¡´Yàåf¾_SÒbÌ„ÝÛn88aÌ–Së}îP‰ÙÛ…ó.O š+iÝo¹Ð›s{Ÿê9%ÿyXòÉC)@¶Bî²Oîö/škNŸ¸ÿô]_)z-Ú7ó’oÓÅ}»•®~¹©ççïtøß‘m—F¬ŸèôÙC<"1OÆ¿ß\X…|±bºlÄ‘þ¸$ÇŒ®P葜jXÚÈX/²iËü&µšßЫ‘œÌo±’ô §'bw"&ö„-‹$%rVC+ƒxŸœŽÓf…óÊ­‘bÀ`loˆ«1¾=”*þR-:¢€ (s0‘‡$ 2Ã.C{J2oss-*u†NÍkœ½·FU !þ5m6¶ßChah2Å7Í$‘ºw쉻¬•o³Wÿ¼ˆ¦š+è&b B9áȉgâ\̤dÒ=%HÚ‡:ÕÝý·×-¹ã2ªqŸÍͽ÷¨kOοVäkè"e?K¡ÄÌ…9ÇÞfýóØË@GŸ¿û" Ü°Þ,­>>騹h©.k69ù깟šù4vc^ÄùûìÑÓDùÖ^š—IÏ®?}ôʬÚÀÒKÛj^Ç]¿Ä½txsûéye•7g.ÛÄc#Œ1ë²¼^€5C’ôM—¶lyå]ÎË^Î<Œ¼yí‰}¯-,tt–}€_âWfæ¡«Í<îÛmZÓÎuª-.ÿÔ1Œnf#Û9Écu:¶òÖo¼FÌýXãë2âÆs­xcàSX¹[scãR îÉŒZëÕd銣E›,lY¢S‰§ó<>y2APÔÛ{þúÔþ+/»}ãwøND ŽÞû ž=Ë+™: \‡…¼y!üJ#ž`q` ÖKw Í0h:öŸƒ™¼aô=WKѪ5_\C3†ÄžÜ#¹ õ[›[§å¥—Ó`…%¾fäCj†”ÐF×{®¹8™Õ¹¿)¤”ŒZtxçq¯¹…½¹›ùÜjø_ÇV,óáðráá'tmg” Í!!¥Ô‹Ô'GïóíVj‘;ÌýÐò; b[ëÂeìVlJö+T±¨Œkj}p¯±(a×¾%[“šO­Z(§” )”ÄØzBJÅ.\{TÝaV™`Š…7>ÿ˜‹R¹ÝŠ”u2/#„>ޱ¬Ÿî7¡¡¢aåŠ-GàaðÌâý³hh‘BóŽW2î¡ûr6éh´|&i¦5gXÓ¼ÐòNd"£_®ËmˆIC:w]1?Ò®Õã{ú+E¶h\ ƒç`jArÀ¹50/ƒÚÙ¡5ý¡Ÿ…©Gw%šÉ_ß‹VŒHJ0xs=F³›ã̳yzxÉùÛýþÏü-•´&ñerRªÞ˜BH$Ùü¤WÄÈ]]½|̸|X†&¼ƒ;wa mÊ‘-qföî.n>&ÜÊgWÌ9õèrt‰Üž†Ì‘˜ä~B„ÌÂË çÔzþ¢ì'ä5cat©Z r'VÏBb&“AÏ5zI"³ß _‡_H˜9Êv™Ó`U‰µÇöœ[  ùñ›IXÚ¹º8óY®•jÖvüÆìÝr:cï‹ç/T( ýܹ+$¿‰Qé?¹;îow{8ZBßeÎgÙá×âÈ̈ íöâæÚæ]q`W˜·œ%Ärí yj¿ñ;|'¬næ8šˆGQ°Êà}#“Â^­ j|ýœj–@Õ¨Ùñ] gC{Yâ`2^¥ÀÝì]¥ÌŠáe…Ãɼ'/ÈܽKÆî9Zò=êÊÿûë(Væ{dÔTh…æ°de‹™Ðib’AÊIZ«×‚Ñõ—‘ØØPIÉQJ˜IÌä¼n~mn£90éбë©i™›9ÞZüE2d–r>ãøKYÙ„&õe+u–HÅÆ& Kî1&«>jçP|Udn6žÔÕ½·¢w·Ô_¼•¤p+èNýs¯LƯ%̰X¾¨Ëæ¢ÃHY$‘^îgÙ·ï«[õ%ƒøÿZÏ{§|U-`U´žûÔWŸMªUÉòãÝ,­£ ±ˆ‡5ôÆèG‡ï¦´ñ±úø°+ÇqY•òÏþñ.‘oó£ËÖdkµ¯a“‰(‘Ù» R*•Ë䆞zY•÷«|M¹ z5Êcùß8q ¹+ñ&,¯atjǶ 8s Þx}e|ܵ^ÛÒ:ƒ¡l˜ •QH1j×Áß#±/rÕæãÀ`!éXÔåÒÇàæ]äâý½A eÁÝ‹„®òÉ¢ŸØË&cÐPX×CÙP½5ôiYx†,ï´L4“úö°¥ÞÉÉj¹»Í04ó-’F“ò†3ë7Êp»ÄD’Œ"ô4÷2¤Õ”VLqé¼–?Ájù kbõ=7›‘Y·›[z\ÝÈ õ“zØæòî½(‹œøl‡•€ÀÀ/&ÌÜ{^dØ‘¡û‹<³,÷ÅÃU‹Û¶eF­ÏöU•CѦ#²Íj»½Ï¸˜Jã>v¹ºÚ7ÇŒ¼W–5µ j9Öotãõ¶,­ÒõÃñè¤SO§RŽ¡Âò~î*î-·MÚâÓgÇܹ”åà_,¢­o{tºƒÅãqë¼ì¸Š¹ê j„F Ù=,€«ÅàbÑSÞðºpåÊàÊy]ÿNâÇl¦7å´ ÚŒGµh¤høâɰwàãk²ºÏDÃh¨´ %°³@.|¤… ‚ aúF¾ßXaΙ8¹Qq|´­#>¶Ï´”Ž|Åï¥$°²…ØJU{£P SÁP(ÕNŽ'‘£P ¬¯ ÊæV Ô¨? u¸ìÁØ {El 3ÌH“Bb"Ê B¤Ör°ŽpwE®öSžÍÝÝG—Ðw, `l0Ÿ‹ø½ÑëÄ…zäÝÕúü¢Qg[vt±Ä›û¯u9¼s{JËôÚßùúÊé—7ÏaN+Ï^KEJd"RDK ~ ”T,z+ >žìµçQ.6¬™¥‹£(cÏ¿^K†ö(æÝàðü¾G„ùz™A­Œ~Áæ,ãž'¢¨WƒÃ{·¿èÔÞ“Œ‹]4éÜ|ÃÊØ$%áý2þZ MID|¼~:2/éR9e¼´LN©Rô”TÄ•{‰\D‘Z]ôë§e‡‡ß³àýBfkU¨ihˆ+¡ܲþTL'ÌŒ2êyÔóWq©¯ž¾ˆziaíh)[‰Ó€Q·–§ýYº¤è¨Ç1I\u®»~xå2-“ÖÏEЯoFÞJìaÀX›ó&ùɳW|4‹èQÑV”¹££yºÝ+¯>áèÏÖ5"šå¼X¿[‡&…ü]‘}íÌá%³æ_J´be()o0yEÖl1¤t±³á][Töq”¿y|mÃßc—ŸõÞühR/ðÂ_Å<ŒUj’Ÿß‹~EØÚÛIÞÚ(r¿šë{Wéi,©^“œû NÇB“ôøñ}/‰­£«¹Ôt"Mña¼2Ñݪ 1\²½K´°NÛpQ¼w¨ÌÞ6o?½ßôàÄ[”Œ¼EøaÈÛ« 4L|“?@P)^5¯„­;lÛýðjÂKòõýÜn¸z ÛdØí„Êÿ.B6[~"r@+Èž ¶.^tã³%ÕÉR‹ã‘P†ÏV‹ _x·ÃÞ‹÷Ö3¿zãÑþ- ŸMS^AƒË7͸ºrÀSB*ÍQÜ¿Z~ŠÕÓâÀ|Cgš­™qs~-|½*u´x»yÌyÛ:¢]랪lÍ’çZþØ¿Z½\á¢Öîº8ÿ¦SÕ^a®Äû‘kLY»wÛRcïìóû¦ÛÂÆÓ¹Dó¼b‚aí<"ÖWÙ6ù\ŸJ'’Ê^®ÐØ.6 £üçÆ¦UÑ–vX=âl­¡q«Îˆ6“Ä?Y½)[½’š…#îÚ8)þ]ºwÒyïFýr=™$ò¬Eô¦!GÊõ,RÀË­Y3¯Ë_Å>ŽçGèn<¿¸ûQ›ÍõóÛ0B‡¶ÀŸ‰é„Yó|ÿÆ/lÂzÛG/\¶¹rýæ…|¬Ów:k±l¡ö‡a]‹ä;Wîµ­ÜfJ J¯Q>{öü]&„mĨ!¦%³q®lÞ{2Qï;aêTV}uÁü۹궮›ë=7±ü–>o6âÈác×ÿ=·óïC,·ôèÔÉ+9º5ìÓXWYy,C›çðn?χ4UehF¯54bYÚ&8gUA¼+Íè¢Xk‚¥­K†ò3,­gt:&O—*#ø©RŸè4f¡´•Õu£ŽÄ|ש:²$ Ë0:=SnèQ–$¶M¯ÔdŠs3а{”gõõ¤™ñû—å ìº:Oð®»ÎW£žž|üO‡°¢Xµ¥´ôÈc¢EšìÃË'd·æ²M±‘Ví´—¢È"›‘,Ë,Y&ªÐï@èÅ'6t¬éÀÏ\«ty;o¾x;uÊeUòÒ¯÷bûoôzýöíÛ .ìææöå£Lé„YîתנÏï–•lÙ¥¤qÓ¶`×A¿.SÂ9¤LDH™/GZz”­Ý¼ì—“¹æ®Ù,wÍÏì9‹\ìógÛ×íaܲt/ØsÈW~…ß›”`$ç…8ŽßŽn*™ß - æ—Áóî RÓRD¯g½yS$h¨>K!ô¼÷8#åOO Ú‡·¿9óš;€ãu-Äžm`Å`L˜‡ö‡y™tFɚػŽâïš…õëÃË­êð,Mk”ïÔLõÖé_«zOâhN¥ùtÎ*YD±%ó÷4ánh§ày“®t XDY¼pgc‚fÛÖX»tS}±J_aòé=£Ë(bSKM=s²KA‡‹/õqªÃmóú|èp5:*·cñQÇ/WñMuPT¹uz%\ÔºÒšu ª³ѾÌòégÚrÆqŸÐýçšv7X×léÄ#­8›¼S s;xŒq¿jn³ qæ¿‘HåëëÑ¢E‹•³€ÀÏÀÔcÌY 6c/Ëk§q# âÃN€?Háô'³q™|àÚžž­á¤ÜÕ°¾¿0 ÉtZ~1Œ,-Ê?– ÂfçT™äÛ5ìô3m:—]Á=¶igÛ™¿Jå,æã˶¯±N'-ÚÖÈ<65!›÷¯yÓ-±>¶»Ç”‘¦h/6Í]uPd¹ñ'¯4âvqZ»fi­vµÖ‰Õú­S+°ü4p¹²åów¹õØŸ9R3ÿò<óu@‹‘…/-¾Ô§J)7F÷C§vîÜyݺu§Nji ZµjÍš5 ñôô”Ë?ÏH@ÀdÂ,ð{Cëú-Qþ‚eϵÎCÒ kX”Jš¬]¼­·Åi°qÐ>ÙÉ|é†ú|Ê+>…ÓÝ[U}oÖðçNäT™KçþîV’x.Ò0™;rÕŠÚÜ¥£³ ¸ÜokÈœ¬%<ÝÚͬøø^Âô±|7ˆA5ŸXÕÍY¢ÓþàÆA;vìÈž={bb"÷q—[[[ÎŒ.^¼xýúõ ½]›N@ÀäÂ, ðòýÐ"õ{®íœ²Š4†âÝñœB}àNM í”OeË2¬ØÚ·€Eƽô§ý^8Þ½{7'Ãé)qqqç L™2ÅÝÝýÞ½{2Ù§#ä d&‚0 ü)-Z´S§NsçÎý ³§{öì)•þWD@LCf?N€§M›¶}ûö/^dL ëÑ£‡©J% ð‚0 ü6ð¶¢%…(Ã’†ßR@@"‘œ¸Ò§—°þôußæC_4—ÈŸJò+j,tzPo¯ÂekX§D nÑð³å§,ûçã‡L]+ñuâד6ŸEü…Y¨qÿ9²@$ôuÿ"‘zK§í¯ÊW™3Y¼vÈR¤\SsýIkŸ³ŠÙJ3[š)Šâ”¸FãÇç>’$Ù¢E‹üùó׬Y³oß¾µjÕÊäòdDæŸHž¿fïÉíãV¦‡•™‰™â+ñ–­Äõ§  8û¡i3ºðéÑ×°t5îÆ@b‹ õP«xëÂ,?™/r8sb[Ônr~PÆañ,ü“©yë£q^\=‚ðZf]êþ‰ë&=ÄŠ¸ò˜¿®“5Ep6°©X±'n!¸4Ú6ƒÄ+4šlÖ û6âòcØä@xG¸Éu3gðâ}lš®‡O#Ô·Áê-ÐØÁÒ Ù¼ÒËIìYŽ]g¡a °Còx5DŸ@dÍE¦~;¸Ó“»‡žR½,TzYõ>Á"=ë_Ù—9Ë>órb‘DÌj”?«ÇÛÕÕõøñãbñ»˜8ÆíÅ‹¯[·N¡PüÇé?A˜.jîWˆBõòä½sö;÷2gH§A¹\-©Ú‘ú„„Ãó/\&øëj´¯ctÕV õeÎ/=wò†Za)¦•ª×)feÂó+hzùƼ1½rjÐÝùÁé®Ç&ÈÛN(ê+‹¹òï!®ÝúòÚÓÏ÷À»×ôz±wô™Ö΃þ®èbÆ’óhïõmžI<¬å$ýòú³— νwURèL°IñâÅ·lÙÒ¹sçråÊuéÒE$úëISbâܹ•zŒYÿLÉX»ø”ï0sjÛ@£nž\Ò·ß”ÍOSáS¤áĿǰ†îú¢RuGE±Võ˜P,uFŸ^«Ÿ¦î9ûœ‹h©gÅŋDž8QG–Œ:së+-åßhܶ¡Õ8Ð?Øß«Ï¨ç²v9[ö1¸eñ˜­ÊEìÒ“NƒÏ_¨··ÿÀQ±A׌,þ±€¦Üß7¨ß˜ížÐ„ØÑ+ |ãA£;5Ü2í¡CFÍZ?ž±ñ mÝkL¯:¹ÒÏÒßÛÙ½ï„ýWžè v´$“ÞùK?‰ìÛ{øÖ3÷[¿&]‡m_æç†â¬%ÞEÉÆÐ« bFÃÖÅÁ§G>A6ÐBZr{íò p-9Œ«:2ÐêÀêù!ç9=x¤‡ƒê½1¥?„>œÝ ÷vÈÉ §3†á™ž×fÎæñ-6%°÷Âj€Ñ¤]×2;*ŠÐ£œacÎ'²:(‚ùµ(ß…¾\Z‘5jhÅЩacÅå_ùšñ…Ô¤ðër_™kpwÝBšU‚f3ôÉ¿í~æ;zV;9É™¥—ÖžÔ¦r-âÄÌÃ'ɼã&($„.5ugÄÆYsÍ'õ ©•Ë{Û³÷zC¼E:úÍõsúŸ™ïä< …}Í%ï”Û“ìeš]¢g‰œEíwV †æs3×ä%Àž]xhç}ïQ³óÚ+(Μ½±éxª¯·Y¾‡î2½B —„`é¦n=ú„Ý승+׉š»€u/P°\ ´qKî;t2:Ô×Ë9_ÎÒø3—ïhQ¢j1«gH³œ¥w_¸%2ZÔô“'K¦_7¯U%¢‹I3ÿî83}jt²Ž…‰b~øøølÚ´iÕªU•+WÞµk—{D 31¥0ß[\©ð_/–;Ú H|`bóz+nòÂLè·tð«»ÚjáÞƒ TƒW,hshÏ›ó•ƒÛº“;Ÿ¨ðÖi­w×=«³¢îJ‹Ÿ¹±±rΜ‘OK_½1Ý(Œ¥ÛŒ9`wÓw´ÿ*ƒ*¿>¿8WÑv6Mç»Ý,qßÀ‚ JÜŒ;´©ÇÜÍ´}áú£¦wª1üÔ kêkžôqñžlêè[~™ -ll¥z¼aXݳîXÔêÍü·º,>x´Qˆâäß]+ÕͽÀ–£kËH¼Ü=0[µq9«Üx¢—¹îö¦1å;ÎJÏ3þÚºÐB©Ú³"oµVG/X»ìõ˜;Wû‰ó}8ÛWÀR–a- ¾GŒ!]!~«|œ*ÅRÒöýLHH #q):>CEêy`áJtȃ÷Ðy¯ßOáef:o0ùüßSG$o‹©´Ñ_B®i’ªý.•{¡^Aü½;Ê"5¶.‚¨(j¹A›Ìe‚ •oÎEªB†x;J¡5Ä¿ ªQ!$Œ2æÂ]žÎæ«Ó±„T‘«ªû©÷žôóó廄YB"–Ë)– \ƒÝrä”\Üt;®EQK©sõÆæÓÖ?{ÑÞ?» ³ëŸù „Ú3´’Q&ò½Æ†oˆÍ¤RúÍ™}Ê ~ÞŽrBg¸®_ÕB¤X»iXŒoN71Ã%’¢ F~äŽk÷n%8ÖJ'EVö23š£ŸޤF8øeù'ÌÐ,wßßÖnüŽP¿Rª´p󶱌ŽfÍmR$¿ˆgòZ˜l¡0™LÖ®];__ß   N¡ .lšrüy˜P˜ãçØÐp\ã" æ°ÙÓ×ò]VšcCê-|Ôtâ¹vaÙ¹SGu]t¬ÿˆ»+Ì«Ê&ók×1oîØ Üîßÿ.+öm]²ÐÁ¥‘3æß^žÓíóˆN{ÊÎYiÉm²š¥ãzÇØwºµ<œšlõg,l² Ù°y»•U«øÛ³þ¼+]‘¸sôBë;¸o©Ðd¾cÉAk¦¶3ˆTP‡YcÖ”¹Å–¸½]øê77íoSÔ‹ÛQ¡÷º£¯Î—7è`xùêÖ/"úLaìlÞ5ÆßM ¯§èo ³nB×'Š61k»ð Dך´¢õ¼Úcçßí]5@öÓú»¹ÐÎÜŠç#rd4‰HCzœ*Í%›_ú)…_×Ýâ#ÿjÉ*@gNe5 D«M3°îÔÖð7ç«N¥kϼëʦuЫxSøIÝôàǬî Ü£ÁK6‚D®Æ7Ç÷´NôZ†cË}<½€§8UÃö’PYÃb&™äø!v¢t‰’˜I ‚ÔE%¾òXŠÞ>/VbeÄD¥Àÿƒ“$b+¹ñqIZ‚¥ÝªæwZ{tÇéÔ~Õ$‡#5Uû¸Zˆ•ÏÆÕ=ÿöŒÎÚ–HŽâ»véùC,“ÊÌ=Ss©TD~7œüÛØX‚ŽKR\tÃpñW|EŠvR¼8ñDY-—…\ûôÊSÂ=Ä™¤Y·­J–,yæÌ™AƒmÞ¼yÒ¤I¦-ŒÀ‚ …YjkŽ;Û×­¸Ø¡]~[ˆ:7癣N±”Ô¯pþ´òæ°”‰ÿMDUKÃ{níaôýÍ9f´?gž5ФÒ³wÌi=ÝIÕ±%Ké–kñºÌhØ›`Þ¸bºùçU(Öœº–„†*ÛÎÓ•Oµª6 û¾*0Wöz¦Cñ*­ß™ŽòZ{W“C·hú>®¡¾ï¼B‚šþ%›ÔwÆ™×es_½z[ëÒ¹‡ÿÛ]tÆeèé'G¶¿–Õ,çø6Á»H0–ž»–Ä|Î3õûaù…+ÄŒÅxÖ”A# 5*-E•‚˜wD=€³b¥¸¿ðEˆ%Þ_•€—Ø{WáÔÜw6Ö¾ŠÁy)¦ôD»¾î—¥2å½³H ªÅ„åxÒÞo¯«'Ѥ6¶¬ÃËX¸:ãÅaÄ‘ …è{'ñèÜ.]г>Ô†±®>×ëAg‹,!æZw¬Z˾»C¼‹K?õ,J–%%¿Ö¼ÁÊ€ñÍD#sp .*;¸àŸ§ùr¼´w©áw§Ì² ÜÖ(ý"™”HUq9©3.ôÄmЏ‡oô¾M×ó·YD~W¿kãÚsQññÍN©}CÌÂÊߣǼc‚9Îáàà°`Á‚uëÖyzzžý5Ï(¡Ùáí=Ðèz*’qåÉ-ûÉv'QÕ{â\_T+ºµànË?uïPʉj½p¤.FÄ\Ä„£è9Ö\½«1ô'âÒEj\Ý+6X­&-OsÔÊÅÏP¤øg-"Fнpªj–FíÚ𰃘€{n”êˆVç1w&Ú×û³æ!_]4(ÂÙ\xòŒ?ñÁ}¸úAóÏ ãQ qÿ¿ëÕcܹO/$<ÃÎZTâÑ=x冭"[œŸ¡9 Tm‰Žõ g~s»™eIs»ì\½”X¯¨©KóÀæMU…·_êDZÏ™ò4fΞæü8EF•æu\²8w°Ű:biþBž‘§ÿ]±Š Èç-6j,AÊ-$ï]Yaç#Çõˉ KÙ:£ï6±U?bw’R«ãÞ"Rûðe$ÎÎß5¹ˆ`´××\“6«=±¿£eX=£Qëõz†ýe^£F *Ô´iÓ=zÔªU‹0¹W§@ÖÅ”c̶ÅG»\óHäÁµKþتòÌàǯÍãÕV$é¾öÖ°0Yzk™0¼MÆ1B6›3¬wÈ ¡ënÔêë²ýÙ¸)oÛ³†7Ǻñü‡×d2 gq-û _~Û ãaŸj®«'öS¯%?m…wDúŒé`8Å¢î´GKš¼_žŸþ†Ûúaò"Üþ±I`HØ8"À›¯Ûm}1{ ®ßDôKÙ1 üÜßÅëàŽHzM*¼*b}?ØKßuqëHT+ŒÝœymmð¸þ V¾³·ï î  Ööð ‚Ú­Ä¿Çñì ï«ÕcB‚!VB¯ƒM&އXŒTK¨#W7Œ%¡Ó@kŽXwðýˆ,âàN ‘u;LJ€XÏO\»£D0žA·œœñǵû§1,ˆ@ô üÝíf–”Zoè²`åñ¿‰•*9[ÉIe\ igídgY¢¡óüù¯­X ‡ôÍÅ››Vć´'Óú ’’YH)‰îú¶ë7žX4é-ÖÒzÃoØ«¬·Ûß÷yU­Yϼ œEnQ¢©ûœE'çŠÔ•«¸Z›‘ª¸TØYí™ïPÇË'ÎçjXÅ^ÿ*fåä{”_Hˆ%!¢$’»ªT."Su”ŒsŸÄ"‰„Ѓ’ɹ!–‹¹ µ’ËļãÉL‘:݃G+ö$[æ¹¼pˆIð¯‘ÌÎ*´º¿—£ûeŸ··÷®]»&Ož¼lÙ²•+WZZZšºDYS 3§bv9ò×ãþµïµ¡gÍÆÓLÙ?©«h6öÑ„¹¥b™ué\dЄQ“w—*  ªh™1€ºzËpã:¿Œ ù^?Ûe™òr²ål‡gWž¢Vàû»l xR‹nêR“9“$mO—ø4(œËA!s°ž_y†¦Žg ‰k.ߌâ„ú¾~¿o…kÈlZäér;*ù.å*³ ,Qº"XåÛÖ˜ÌËžH‹Å‘è2¤ÿ1›Åü ò†£„þ•Ì*»O‡Ó ¤ƒX@ 9rÕ‚ü^þ…¨v(\´ Fn¯„{C!¥(›w#¿t>mXZü%ªðçï€2ôŸ›oƨµn‹K§@¥‚¡ÍaÌÞ,Ãɠ~™šýÿ„ ó´)ÓÃáꦕç'®ÓC*ó-á_?ÜŽÛ‘§u™®Ö—7¶[5'ÙZ…ET¬VÕÙØJ”túDD±c*¥Þ<‡gøâ*Áîï–Nflœë—“-}åëíB}8t‘áÂAMKõ²½¶qù¥IÎB"ó)æ[·£Ô7°ïßòu“öµ¡eE"¿ŠGtÍi+!’Î_Ü´. :,è¦IÏ\wfœ>ðˆkÛ]YêdS%ðÍÒÁw ’Þ?rÛü~-zù^}øèi® =¯ç‘jƒ‹çõÏÑ¡éÃ9«Ÿœ¼žöÖsõÃÁU"6WÍ.5YTíáÄxäÈ‘gΜ Û±c‡‡‡‡©K$1¡0Go4±Á†i9ømIÕòùÅÓöiµ’bUB0ëø™}kS[öùÊ®1‚²¨Ö2|Ö™ù ›Ÿ²àªì­äÏ‚%;öO[þ°SËìßT62´\{Ѽ[[ÆÞê·*ð½ âz]J‡7»m— Í% «5åZ“ÙþσâlÁ|£vÔZI ,¸¤ÝóÀ½Å*ƒ‚e•B—DUiUðBÛPI’†ÑÓt¢¶·G¶~½½/5'¨´j½ÍƒØàȸ±íË8Eø“J=-!Ãwßmv"š©àHü CÍ(R¤ÈæÍ›ÃÃÃË•+×½{wa¢³ÀŤӥöOo߷ડ t÷GÏX©QÔì^E&¥FÏ©¶¼óº¾Å4/"—¶FÒÝ+çäaC»VrYšsjF™Y|”Q¼j­ìN‹Ÿ%ëTry—JÊ›÷>~Ÿ%‚¯t(Ó6þé­ËÉASû×´0ãÝ ¥çô2góm7æë¹ºh¾˜Ní„zØÒ©Qÿüßtä€K¦®ñíÙ6<È{]ƒ`óS #Êú·X5=Ì)2Oÿv•"G¬¯V‚éÕ©¡ûêàÊÅIü,accAÔ ÷¸1»:w Ë}µ[Ïò¹’žß¹Ÿcü€zò_i¸Š«¹#ç!ò6gù Ó ôÿ o›H$ƒ}30o,|KaxOè?£Ê<4”þxUâXþÓÓ~>ãÕZ™¢Dh\[—–½ D xS fÿ€Ô#® ÿ‘ãÉ X\áeøu]H^ò)÷fÁü*¿Û’h~ãÁ^ãY 0f>&´Æˆ(ÈÔܱ¾!|úu3¡}h­^õqpµÏ§ƒ÷Ô¤&kÙ 1VèÄ”(VšÍN¢»yk§:`p>­ùt—ÁBm!©60òlÛ<òxuàœ çZçqþ'Öþöëçù\<οH¶“ÅØ™u>ÖÇVͺô”3›æTI–‰š6Þr¢K–$×^7'²%×´êVrÙö‰å¹<TX5o_SNeÛ7Ûv`p JÇ”ŒØ·s|9…R/nµŽs®i9XíÆÛ·ªøø{AFÇa¥C.=MU¤Ø}¢ËääÈ‘cÛ¶m+W®¬P¡Â®]»>"& ðcBa¶o3¾÷ðECr-§ iP…Ž7_ âÍHËN;ßxMé:tΆ^ÇVÊmsToÛgx%GúŸå~êìBö«Pl[å)óçvÊ™{Ùþ%r.)>Ù÷}…ó(×ûÅeîý&lœØ{- (UoÈÄ.¯wDÔŽXkáâ²µ|@t»!+‡4Q|Â!˜ÈÛcgTá%ý§.7@±gH™ÎƒÇð._–=¶<ôž1¨Š›$ÖÚ+_¿•ç7+`0%å‡ï½â3¨ë¸•C;pÈ]elDûÓO§<]^¾™÷Ê•]Ë»ïôèŠk~c·Mí»––ø¯5`tó_J•ap+Ù W{ñ]Ö´í;‹™tÄš H~6”æ?gœ0ðÝFh ( c„!˜ Á@ã¸ö1ié*¿÷rø{ç5ÒÆá7Év–²ôÞ¤Š" ˆˆ ‚X°×³wñìíìg;Û§ç©gogï]±÷" Š¢‚‚(½·­I¾ì. (<ÕËó³$“Éd’Mæ?ï”w¨s‹”*KSÅŠéêHY²$³4Uñi?ºN.[-ƒ÷XSjE|¯Qª¸H"“‘”0ÈÔXïû„ȬŒ=ûò[«ßÞŸÜu¾GòI¿ZB 6%¨«Ã†«e•P*«Ÿ;¡ÅÎôúzG7v Lçd7£>AÁú±ÙaÃ\“Ý)­=¸½ó”¦ÿ0EÒý»ºæ™jP¿Ú– ýgÕß@%µ&t8³DþÃÚÞi–ÓFjcù“ÑüÌbj㥿õTm%:Ü?ÿiþvlÄB±Õ¢kO.¸ùXÙ Á¬'¶Më«C”àˆêŒ«›Í1b„“““££ãž={|||¾| ÍWP‡ÂÌl>úÏ«£«œÈœº‰úS)ÌiðµèÁŸI­ÿÑ'ý¡ ‡›®½w^î])ÈvÍ‹Îk¾&‹†^ÃÝVÅT­ëômÔŸ*Ïr°ä΀%å»Ý{L¨x”ß°ë¶óª¾v e Wm “ò–mÕ„ð3m²ÿ)H"ùyA£î®@ ‡e{6×c–õï`º- cKZNöv6À>ÓyË)¯¿1X-³Dî › t¾?Ì!J•©£ìBÉ‘M)ÁƤ8¦ðeÍÏ,¡âSö1«DŠ(ÚÊyÙÂU÷GÈ#I”_%ÁD•!üŒbeH’§Éša”î‰e36ò"Cß,´:¾Ú7S{+–¤X¦b×ñòòŠŠŠš5kÖÉ“'ÿüóÏŠ«bÐÐ|?]׈ŠÄ44ßµôu°*Û«hm"jêÞ½)¿`…R[,)ÿ (õÅ*.&S(­"~å¹$W„, )K•Ê*i5 Ö–ï'(RÙ“ˆUÑPþ--­M›6>}ÚÞÞþêÕ«ÖÖ?þ š:å§fP¸äúÔ;UÎÛéÒ¥‹««ë Aƒ&NœØ¹sg Séž4ª -Ì?6"_Y•ì+×TYX, GÔÒÔæææÁÁÁ«W¯Þ»wï®]»è‰Î4ß]&ýØääÀÎÀ©=WÛÿ.„ªÚ-MAY5dß¾ušŸuuõyóæ…††zyy={ÖÊÊêËçÐÐT†æ&“9sæšW¯ÞÔx×zaaa\\\ãÆÉݶÈåò„Â’/Çû‚°Ùl‹õåØÕ¡Y3¼}{ÿšMó$iÎÛ<¯†s^ ò‰Ç•~tCòðQ )*(ùt£~-Ò¬Y³óçÏÿúë¯~~~“'O¦':ÓT úuù¡ÊP77WêO§œÕ«W§Oùß°aÃ[Û†-[¶¬ëŒT?Ÿ6'Îd¨p—ƒJ’K„%<)«êQª¦¥¥™—›ÿCº†FÀݱ¾žžn\ÜÂÂâôéÓ{öì¡ÞXJ¤µ´´ê$4?"´0ÓTD"QÁYË—/ïÕ«×(Ìݺu§þÔHRb±˜]kc D"eÞQ ôûºŒýYa±X#FŒðôôtqqÙ±cG«V­~Èú Íw‡fš* ,'U+šŸ>}šœœ|ïÞ½ââb5µµÑK^^ÞŒ3Ö®][KÚL=äÍ›7/\¸P_¿*gïuǹsçÜÝÝ ¾3..îÁƒýúõû¹úJ6lH=Xê‡S®èLû£ù"´0ÓTL&«ñ®Üɨ£¢¢RRRlmmë:;ueoq¹ÜÚ³ºV­ZEýKéÇèÑ£ké߯­[·ŒŒŒ¾F˜ÃÃÃçÍ›§R Ša7n¼rå U½8{ö,=Ñ™æóÐÂLSªf1 …Â;wîP%%%TýŸæZE*•:tˆÚ ŒfUf&“Ya¥ÔO‚ãøíÛ·bbb¾CƪE@@À…  0a„nݺÑ#Âh>ýfÐTe1«Tó»wïž>}ªÜÞ°aÈ#ê6??%”I§ÜxòäIjj*e¡Öm~*Âf³©zãQÂLÙ£ÔÆš5k¨êEíç«ÚXXXPÚ¼nݺ>}úìܹ“²¤ë:G4ª-Ì4U jshhhAArûñãÇiii†††Ÿ?…¦ºìر£|{ß¾}Ó¦M«ÃÌ|†a_c1?zô(%E¾Ù¥K—ju”Ü¿ÏçÏš5‹z¥ÏŸ?ïèèX×9¢Q9ha¦©ÊòP©v¶­[·VÜ]µjÕŠ+ê*3U"N{~&¼¨cç&ܺÎÉ·•œœ\¾{úôi•f‡ó5óŸ–.Š“••õðáCooïZÎ×·Ó¬Y³°°°aÆùøøL™2E¥>7š:‡~hª€²NTÇÓovvvHHHŨœ0¿¾³pMªß+Ì”—››[¾ûúõë„„Õ£„¢è-fJ¹?®Ü.**º{÷.%~ª<=ÉÀÀ@9ѹI“&gÏž566®ëѨ ´0ÓTJYÌ6lø „’JªkÒ"ñÂÜ1S‹‹#%®¶>‡æç±Ôµ9DQN‘”ÅÄÄ"1ÆÕÐÖäI‹ròÄ(Ã…"[C[‹Çà¸ö¿wU£R’ sr 6‡ eK #`¡@È„ùBê`f\XˆØzhKÕj½$IòðáÃCRSScbbTG˜y<žX,þ|œýû÷WÜ þí·ßT§~Y%ÔWF;¾¾þþþëׯoÕª•Ju!ÑÔªRøÒ¨*e1WìûTRRRrãÆfRš¿gˆçÄâ!k›&^gæ,Yæ{[:ÃüÅÜn>¼IgÔ»åÓzv×y ø[‡Bð³wÈN/j9/zå¬öÏ;'íüâ⌔³³ì~‰‹Å/Úå=gçù´çÆ Ë˜}kö´û'ú.†'fv?µma–ºxÞá¿ï=«©l×ééé·nݪBIõÕ«Wë*K@ÉÕ½Ã*‡”—CUݲ²²¾f†UCU€ÂÃÃgΜyôèÑåË—kjjÖuŽ*óÑ"÷ÕBšþòÌé#mFôiòJ‰´w÷¼â´¨é·_ô+Éztyÿ•g>GºñkýbÕfš*PQÙ¡¡¡III‡Ÿ9sfêÔ©\nÍ´S–­«“­vzó1K挄ÔIÿ¬bÁ6qkÙÐà1Iêxvt·\ë×}dk³~9íÛ¼È*iÓ´M=3IS†¶op½Õº×€4pn¬­—&7vôÜýlÅî]&ü:Ú¿4çÎèüú[¢æ¾¼ÐÅHðpjfØ«–ƒåôå8vìX•áuõCK$’ÏDxóæ eâ¸}ûö9sæÔf¾j uuuÊb¾{÷®Ï©S§,--ë:G@à|PƒS ¶mšàõ µu¦]c8Ýv·úà¯fÔÐÑC&QÂB!ð8ªå¹…"%%¥mÛ¶Tm¬   99ÙØØ˜ ˆÜÜÜÌÌL==½ºÎœ/ö1?þüã Üš5kjV˜ßœÿ;pÐã/?ÕHóå‹$c{>£Ô–”fÆm˜è=ùvŸœ·kß”8õδhÑ‚RåÞ½{Oš4©W¯^*R9¦°öíæm`ôÅA”"Ö0Öü(ךêì*ûú‹n fïÓðâ°1 û.ÒÄ`«qX˜ ŽC …™¦ pWB¡œ’’’âââÚ¿Ž| ie½E‘»4‰ãà¶ä–LŸôÊ^µk¤S#¹¬)¨zϾ}û”Ûyyy+W®\¼x±r÷kf(}¾8*{÷îÝfee]¿~ÝÏϯ¦²a8®W«],! ’fŽž6'øDc­ÒÏ„©g;a¨Ïï«ZUš¡ßÀ䫿nQ¶ò­[·Ö­[ׯ_¿;vÔÈDç¬Ýów>ðiëyóÌ-m[ýlýŽ›~u»³cѺ‡í«Ûœ=í¡¾¯Í»K7x£ÖnéÆŽ˜=ku‰skFTp¬Ìþ÷ Ë=e—Ÿe ì<*©Èݳÿ~¢ÞLrîN²÷¤3ÚÛäŸYµüVSšú4‰°aÝè÷ ñDü†Ëâæ¬Øx6KÞ7öòÀœõ‘‘'l5mEìÖȸ¾æÿo²Ã£µGdžÐwêŸ=êà ýgö ¥I‡Ë 7e@äŽû^¢’,IÇ9¶³B_þcsX~Iôß Ýx…—ý¾-Eİé5gbë cè$/þž»>•@‹Õ½Ìëü¿gêšk–î bo^Gt[>?ÈPQIÆDøŽK¯ »Žœ1ÐßµhqœÃÐEC½ê°¤…™¦ ¨YE,æº@ÓT]û8š˜½Í!ÿò1 (¯6­¹]òz;´‘Ž%ƒüw]v5MÅqËTÕ§b‹±ê¼/FùP< 6j«—â¤üq¡‡w‚?8(#ªþ]£æõüÍñ•A_;©šËåNŸ>ýÞ½{ööögΜqwwÿWyÐõîŒ ºÖ¸ç ÆÒó8÷väk€fZç—F¬ÎŒ·3òsŽp×°kÿJBƒ¾=¿Ìpò“=·hMž×:t]@+­ÐYa¿öp±öôŽZwpþÝ›¯w´xЛ=ÙÛæŒŽlùn_ï[³ÚtM²1}ÿÊ­ðj|ªGXèt‡Ì½Ï$¨‡“À˜°r¡á±@³^K2ŸÌ0"p¿±tt`Ý;Ƙ¸r¡Ññ&½ÿèöt*¸{¿E›ZÊvt5ì?‘wmCßâ,­ßþ7‹±³Y³Ñ›^î ý¦¾{ôv vbá•pãeNmä[¼9g³hc®›NbÚ€RiÎÛ¨qüÉzé„Lµî9ž{c]/£GAE¿,Û3¹OPÃÆ‡Zµ›ÔZ>—1\»½7Åy½Æeç›ôé_—ª ´0ÓT‰JÊþÂìDŽM+VÈ­gv>Nz£÷o3gʰø6îží Þ¼Lº—Ïmàþ"î­Ã2²{ù8Ñë`ɸ•Ó¤$±©»AèÕ—ùoÛ·\=¦ãÊLmØÓ¬(<+ÅêVŽ«%/;‡4kí­¿þäž8R”üb‚ÓôøýA*¤Ì ÄO5çñx¼r35øèÂÇßhÛè‘‘é¬í[W>çsV×Ozc'1 xs@œ¿]öGƒ¼ën½[v9b€c߬Ç’õ,7žŽb0²Â¶4X¾)þE >;åúšþó¯9µp×JŒ`2ëQŸMä¬À%B“W§C-ßüÃpÞ®§Ñn‹[±y¶aÆ9™«aÖ¬&‡Í±ølµÇËË+&&fРAM›62eÊ¿ô]òtZ6óònä«}tÞñ•«8$ÉÖ1¡~ußŽÎøùWʵÓyÚ”b±:öª¿þÊ›J-$`|}5”oÛÀy9‹„§1qf>c¨#NNfyÇÂ…P:î#íêšGz›n•;F%q±ƒÂn…edK (]RZ‘gÆc~O^˜.ê¹ ‹2°±ÎZØïl†¤¯ûîï<Àó ²(SÆ´åe]»çʰ.óûQæyòÙíI:¿“CîåÙêd?|; ‹¢3áí©¿c¬Nv—7PyŒ—ÜûPÊhªªÞÊWÿ…üRŽ $XºŽ 3WÝÉØçV¿K]w7ý‡ _š¯‡* UgTöwåêö[°±|Ým·‡.Tòú¹¥Ù@ÅÿõþÚÕA±Ñh_Gå‘;š+cÎmÞ¿4òü•c—ï•ÿ—¸zç»_®]›K•!Ò—gë QVQíÝÌÏÈ׌Êþzˆ—«æÈ>óh¯ââ¢Õ ¢Íòsµøpã¾ÅœãŒ1 z7Ä^¶³±×ÜÓ£­Î…Å÷U;3ÿ¨ð\ô&Žl~ÌB&Óuîàå¼[Þ¿,Mš9yÓÆçë½yøå§kÆ2€úS÷×@ N;Š7œhnÆ+¼ø—¦÷ú_YPЃ]ø«žÆÚëSþjý…·@CCãøñãûöíóðð8yò俚½F’2•56QU{ ©è³Àå&?Zäƒ@+„THŠPœB£Rr´6‹¿#ë?zg8¬ü–ÈüîžÁ}ÿc"L(8åç±zÜéã™Tx™Z+",ŒºLò®6]Î>¾ÇR‹M2‰^ÿ[!êëV,l2}â‚ëÏ&° …$ÏÔÜLÌ»òXÛ\£4åÂ|!ð4ÅÂÓ#D%RùÍ“ÊI&oú¨ð ý;z»-:>w·½›Í·>åƒfš*J¥ÿ)a®]HY^vJZˆ$E÷/žkÒ¯‡Êªraa!‡Ãùr¼ïÎ×Ìcþz _(ÖôÐW¬¾hkÛ€ºïàÕSŲ42#yOªØÆI@…6_‹K•ÞÙá—EZM 8 íRv»Sº¤”–üôçéì….ò´¨²žú—úlž/ûßÅ,#äŠÖ£‚ä2†ËàÝË×¾¶_ß#˜8Çλ&þšêõ<8  C‡+W®üö‰ÎHiŸõ/.‘B  \£•‡J#¾ÿ©p¶2Ny8R®‡{îœìß鄞MŸ¨Uïg0"º†º¬”ÐDð°Tìbh~ø©‰Ç™úºZÙü2a¦4J†Ÿ»Ÿ®¯+È‘‡#J#Zq¡‚Ø»|3Éý#Q¨WK;$žRn/|#²ë¼üpç®A–=ÿŠ˜°ÑÆÍÈ`›Ù}Po1²³ffÄd‡.”$D°õ-ù¨bœWù- ïiз‡Ù”ñÿ»ÐwdÏEßò„kZ˜iªà¿Ö”]»XM;þ×îs»vr˜RÐëµõ—‹Tã¨ìØ‚šµ˜™Zº2IAy ­äúè¾Õß¾Ù u'Ùr:þalX<Ž”âÊ ½˜›(Æ`Hr%Špe_¾Ùéýëãñe§¤?6žŽ€ÒÁ„pبÄdîñƒ­«Ÿaccã[·nÍœ9óÈ‘#Ë–-ÓÖÖ®Öé%q!‘ iâ°¨ž®^¦v X7ÍzçÆx\œ˜rõIr^ü›Œ´ÛIùÎI_IÞF'ÅE¼.HŒ¾ŸW¬vóAâÛ×1‰qÑ—Ã^¿,¸ÿª°}ÿU쓸B—ô¨w‰ÏB#3ѸàH1³ *+zynü¸‚YËg6TŽWÓñ[3ÎeL×.)½Û˜¥¦$Æ<Ì5ósÍŸ8r†aµÔ”Ĩ\™»«à·ÅSNLmиpYÐt£¶ê©…© QYEVf!§6myz&aô¼U–†cæù]à r³’ž&Eìµ™ÑÖèQ’UÐ`»®Ÿê1©g§7îÖºèjªlˆÖ üga“E¿-·Ôyt,|ȴüÔû_k=93¿~gü:ÅúMä³ø·7Â:5ꪫÝaq›!S.è­Z÷E_Ýç€F‘H$jjª5×ö‡ÆÆðDÿºÎÄWP\\¬šóWúÊþJÔZδKh{èîÒ‰Íå“Á\$‘’”¶¾Œ|‡²<¨2‘‚r¾“2±H\ËoŒÖ€)w^­éfÃq&  CQ‹ šVÖ²ßç_°ÖJIJ¦Q‚æPróGä©°0¼ ½ {ê ]¸0¬õü¦ )F2Ðjtçkhhlذ!44”²ž?^­‰ÎóV'’Ò1®:Ô¹Û½G>%2„Ëat/ãªk"ÛB;L-5ŽÞ„‹éAˆ†¦šmxú ŒÇç°»¬ŠmK°5ùÌá[ã[C EÚO{2QM‹Ï¶Ÿ:˜£!ÈHÖë2cŲvõH´x[‡iëº]œm¯¸,«ÃÒ“¦æIÁa÷é=hkÜJ(”"<µ‘]•òùjMŵ.p4µÚ% ,”ÈûŒ’ò5®—¢ŠÅ8Ià=Mhò†…Æw.–ajü!=&âšÜó ŠIíÒu @ Ÿ¸ô”×oùa°¸'k5Ÿ¹ÿHnH·nC´|Tª³'1åi²8䢳‰$K]ƒ…ïºû XjŠo´Ópß«²aª°à-Ì4U ²–“ŠSœžø*UT¯¡=û°Ì%„o’„Lûzz*ûd©ß]5AYÓkÍÀirìæÚ¡#›o"õÜÚݼ`í"ïÞ.ÃGX"/.Ýû×H}#ÇUëô3µô‚È\Í‘ÁõëØpU= M½¸·]có üȺ}6³-:u$¯OM#½Žmt4žÞ,Ú;éh?;^#Zû;ƾ‘<øI{Æ·Š®ÝþäòȾ#së{u[ºv–}5×¾¢~ooï'NtéÒeÒ¤I øÊf-”­¦«_VÏF:úsu•Î5X:<Å[_yD] §T)»¬Õ]M³,%u=Ž:à%ßé<7HKWZß\¦Q¡}ãhëWZŽ©]v¶ò’LO™‡Â5Ù•ìL]Ç@™uE8[·r/ÆèUU¡DXšÚº.ÏÓQv`tKãks¨K’Å™y =bÇQ‹IûµªHç»C 3M)”ER>w™Íf ïÝ$¨ì`]U£89b`÷%k£Â}Œ´ó+f…ØoZÒ¥´ ðØÝÝ‚ÂǾ¸ö¯:iV|øššš”íXq€nÍþ4%%%ª¹T"†a8þQ#ó¿ÀÀmàùˆå»3Ž=š¡Ø˜9JþoûŽ¥‡þ:®Üt[Ñ­R×ãù³J·´½Ö]~Z)õUQNך®ÜŸÿ8v¾rkÇÊ1«……ŃÖ¬YÓ»wï;vhiÕ© ìv¾V/œf.(Î|“f8oÕ¯æu™Ÿo9=´Évv³ö£—5¬ë¬(¡…™¦”ÂÂBe)¯•MÉeBY[[GDDÔuî~ ôlÍ øÊJD˜‘”iU®™¨š ±“1ûQ_™zýzA3ûªÛ©_dâĉk×®-ÿi”+kQ«V­šÁØø³i=gò‡8¤$b˜KÅÈ”øÔ·Ò‡ö1 騱}xe¦~ŸU«V‰kT¢ßáß»Pþ©TªRMÙ...VVV¯_¿®ؽ{÷ºÊÊbhhxóæÍéÓ§>|xéÒ¥ÕèL£jÐÂLóJƒ›4iR.ÌTeü¿R²xºêŒä|ô1(LÊAuMt¡¸Šé9¨÷/}´çOüí¿¾7R²yóð^‡þ'úù›ÄœZMVµ[]‹ÇÈŒÍM¹¾"VxgÚ‡6é'{h‡Ÿù¢q‚,íáâYi𨠩ñ‘Ä{zzêèè¼{÷N¹KÉUm´[R¯Rõ0&“Ù¹sç¿ÿþ»<ÄÄÄ„n°­ê‡Û¸qcHHe=;vÌÖÖ¶®sDóíÐÂLS‰Ö­[oÞ¼YáVW^6nܸ®sô]àîé1zͩ޳=^nÙ‚uYÈo,å¤IHKVŶA£À%¾#&\µÛø‡|¤—‘Ú®›7tìâ^ç2Rs‹ù™……i)Y¸¦AzFž¬DšÅo<±kAÐ:‡E„IùEùÙ¤¾µvö®Ó^Ù†½•Šus11d…ß¼‘äákâ=ÝbܰÍÇÚÙ뫘Zh•_XSSÓËËKÙÉJÑ®]»ÚX—S"‘¨”Ç7E¨²Ü§½½½©©iÝæJe¡ªwÍ›7¿páB‡F=tèP\¼•ækP¡FhÛ¶-e(+…yöìÙuïê=sËîË—E<•yÌ]Ð-L­ÿÛöy<$·Pj Q±Ûí½ö n‘§ÒBà”]øÙ›O_ G.ßPÀÀóDØø¹Ó…¹%"µlã{Ö"ï`Ú©kN'/?º÷ÀÆ{É –Ä¢ùÕ“Æ¡Qáh‹…»W¥ ³Qû‚·“âRó,=]ÚzþæóGÑ9vJ˜+fqÒ¤IåÂ<|øðÚx ”0«ZQîçç§¡¡‘™™©Ü¥ÌA•ª:¨ ÆÆÆ«W¯îѣǶmÛ >ˆPPPz/ ¯A‡-ÿ˜ÖÜ»Ùw˜@¿â4•àp8”röìY¨…Ù8ªšk›î®å{êF~=ûW³hP¶ÍÓ³ûeh¥–ÕžŽ^ÊF¾Ý•† û¬Ç m¿R?Êëéú³/=¤gë=ØÖªÂÛÛ›²›óóóëׯoeeõu7U=JJJTjð(wïÞ}Ë–-ÊÝž={Öm~~P:uj`` »»ûîÝ»©ÊMÅ£çƒÏ¬]<§U#Yúlùéa`hHLÒÄË»uíZëתí ÐüpL™2…æ¡C‡ÖuFªà?>âtܸqK–,ùí·ßj)}ÇUjº”ê~•ÂljjêààðÅø4J_½z5xðà{÷îM˜0¡|ô€D&íl§;³£Hia®Ll%ˆ$²ïáè†æŸØØ—>B>^9õ[¯º†aL--½ŽAUšêÊ`0xé鹫·É´ú¸¡O•9räòåËkc¢”’¢¢">Ÿ_K‰3666žžž÷ïß§¬À/Ff2™´°rØlöþýûOž<Ù¶mÛ;wÚÛË[fä«H¤\•ia®&Rœ¨jîEÍC óÏÀÚµ¼}{ÌÔ´fÜMPF©»;33skHÈöI°f‰­DP?Î_8þÜj˜½yËf©TR'U¡PäÕÌkñ’EVóŽ3©ÚŽŽ®àÂÅóçÎ&šH$îÙ£WmLÖ¢¸råòÍ[7™ÌJåUã46‘O¦OKO]°pþçŸ<ŠbúºóÌû!ÛVH0442dH NZÃ0¬gÏž^^^þùg›6màûh Í¿€æŸ55èÝ[ܨQ­$Ÿ•ZZ%?ñ êÖNœ¯ö@§W¯âN\ÜÙ²›LZf‡t àä1ÖbÑúÙ–lAžÅ$Êáµ$ÌGNìÏFž™XêV %AËA\ÏÉ X+ª¾NV~P$y÷lA×.] ¿¹:˜˜˜Ü»woÚ´iÁÁgm¬-j6qšçç-zÿKP¶ŽT âÓePW§D ~â¡!Ô­QO E«oõ"ˆž±Fý&FR‰¬ò¥ê (R,,b‰jk€›Ë²±Õ³tÐÿ \"ÂuMxº?¤üÕPrzŒ¢Íªàñxëׯ é7`Ð'­Zº M@ 3 Mµ!ktuàD~çµ{ ª¸“YÕ×#ˆŸü±×öÝ)':ÏŸ33õÌæZ¿Í¿€fšJ•ë: ? ,6‹§âÐÂLCCCó_‚$éVl‡f‚fšï Åa~ÒhÁ…1q¸(Š~‡$Å%™bP‚¢,Ã>WH’À ‰P†“€0<î'‡V“2\X"£…ihTZ˜ih¾+‹žùφۀ1äºKÈ”}§(¦øqaý[×QÆñ®ˆML“(”¯þ8Ê쳦gs{JwÉœ'q§w>{S –Âfi›ü²¤…“#ëØÕ±k“•‰“„Ò٢ܥô›ëd=cys‹–fšÁ5Æ*ß&äÓO„¬¼û•ñ?¸îç[íUõ; …ùCdoCþ9šS$"Ô=&MlW3>;hh*€°¸í×õìæÆ@òÅö›kv¼ö^ÞHK&.#ßž8EÊ´›4Y¸U0ºçmAC—©kܸò‰kÊFÌ;ž'ßD²#¢ÏŠm»À°žŠ’3üšZ„7ÐÇ1a:¡Ç¢R "J{ûgÏK%ƒ;ý5NO"&qaî?Ó£qi¾–Ÿ‘~ùEÚ‹l¡T¾Ð)ª©Î³Ö×ò°32g+tÃ@\úâmhr~¼i´Z8š»s@‚SUØ„×âòÅ€Èx:S[˜q?NâatüõwÅ$có.‚¸W¯Ï—ÅŸÜÂLí£øÏ®½-RÆb}óV"ïDG™Ü¶^6Dô“c)ò™TVX,&¡DªôA€ hÓ®uP”gZ˜?ÓwîÚM0w˜ë¶ˆÃia¦©iH©åjko‚Jå¦.)S´+2\"FqœÔ°5h æà2™¨D¦œ•Eà.·~Řy š†¼DI~#5´mã#@%Ò `¿%êe³¬ÌZpÄÅ2H$Š3q\*’I$$ ËݶÿTcFÅø²’¢å‡¯MïÖN;h2™dWWýîË/Þ•jŸšh¯†n|¥ëåÛVŸÁbv^˜ù_8¼ã#%ñã‡oîwlÔQÕsØð# ³èÍË4};ËóYW„¥n`d¨¡Æ”»m¤¡©i)aÔÑ Áq‚¬àã,ýË©gÜ‚*nøÀ')–äJ1]׺¤¼qš­Áƒ·ÏO^3t×àñ &-¬äî¿qR½©} ’¨ÚÓ>‚9µ¶!eôÄ#šêƒÁ#'/,ß}ž‹&”¾a|KKÓæÒ¬ñÅ8 LfXy)ܤIñl¦XªTPó.m-o¨­º:A_}{s=‡c¨Î`_—Y4ýà« 3êcâ2aD±ØçQqÅL¤<.WG2’ŠÏQg`À7`Í8?if¥øq/¿(db åòxÊøêš\†2L_“ËA¥~}5˜„)(˜êk;!€!]ít^<ŒC5ø…9jY“–·®çݬ¯¢”†æ òAZŸ€ å¥N™b£ Œ§ÁæÈðüG1{ßêN릫(”+W‡Wn,~bghåhàâoíê&±¼¬$) ûÓ—þ„bÓÐ|â]üà«™ÆÖF; @VîöŽ‘Ĩy³¹2`ØóË׎ñÖu²aФïωyN^+¢§šÝ¼µbðƒŽŽQ7ËœM÷Ö$8L5£R§)C(ëgÞ l^ï÷K1•/N ´º×Ë]ÿ ô¯x‡éæÊøTBYËN%÷oi»ðÒóOdéДÃ-ûÚ”ÿ“$%á6-=æRš®’Ä!Ì%ïBG´bºûÙ 7ÙªÉ;ŠønlZ•i~~·MhE•XRn8¶Cy#ÇØhüþža{ž9žøðeÚÃÓOõ½N]à¬Æ ? šÚ€|ô2#S Žv¶z^IÌHS×u“oï½—GYÂŽšµf⨃ƒ>Ä$îK#æ™È["Ig¼§ùöÈG‡Î=ýk•"‰;só¦iÓÅfi¿|}{Œ§Å¶ˆˆCg£ÇŽm¨&¾ ¾uÕ¸é"óÌ…Ÿªç’ Ðúäji,>ßTtüW] ó)ž}Oêëi ÂŒ±ñÌ­G&ú+Žq×·L˜±9gp1<=9)1=×oþùn%;Çw_p÷ò;87@yí¯†ýiüÉäs6Ž4k󹀯½Ç-œ1/ÐM/ýéÉ“'o¹–Ôrò–#‹Gêào§vi}-“Íå±s“â.ÿùç/o“£²Bó€4vqžü—' ÃcŽä#Ë’¥æ=ÆÏg´4á^üõÏïß‹ZµÛháh™D%Kš2©@(02â}BÉ Es)%ÄÔØÍÖ#¯¦ñ(3yKY­õ<œ~Ùµ/>&,ÓÞOƒüw®‘+þ°ãE¥Tqy¬ÝÑx¼'!&$Ý¡.)ƒ®+Ø«E§×ôÍÖ=u)B™O†í‹_è­”$éè:©ËÔ®ù-$—ñÍ\pæþ¼NR®L4m³¡^=m¡+מ<ßâ–¸ÙÉȳö(ùÙN`í1›Îjª5ðWH“±k;¸É- ÆݦwÝ»#ò•UA,qv‰ÈÂ/øø+[½F¯á之ÜÏoXK½×44ÕAP„d ×z=¥¨”@XlT*Æe2‚1db™ “&v1gß9Ÿ˜?Ö˜ô4ešš‡PN>úÜÒV:)”¨w¹\×KPíÕ},ömy½áy¦_KƒÛ÷b‰Múio«~}ÉD{u_‹=›6=Ëhãkrÿ¥´™û-üÝÏøº×¥0w¿’Ù]¹ÅÕ67±†O¤`Á„ìëKî¶.ÅkÜz¸/¬=vçÅÖžÀP6Õ!¦ø¿DÛ.-6…™´piä-j_únñÎè_7T.ŽÃֱ߲s“2&ÃÖ™2¾sÒãE$üLÂ\qxYUøÇ“g+ªª#¾¶ª"|x-¤ê¯óóÓu¤ª8ŸHªâå>õ:üð³ƒHÅí±˜Z, ŠŠKè8ÐùÅáÛ¨WKgk…ñƒ©¡Éu ùÑï–F5A,58\€”wÅ€TQ@Ê»mQNS=ä\>^$ëPÙhB  0Ÿ*e›¾Ÿ›D2™ncïá¯ÿ9÷ÆKkÿã¼ÿ 1€O d¸Ž›wfÂÖà—IÞ‚‘ÙK(EøIk¡u)ÌdVôñ3ן'fÊñݨde*ÎÏ“;Z/_ aQ/‚T(ýLRŸB·E`;;ý-/·žx“Çb „¸¤ò¸sùsÆÐ÷ÆB™t¸—I±êAa˜Ü‹K™b@à„L¢6a,6ªÜœ'?;ãié+¯Ó¸ÀŠ°Ô’ û9÷ï´tû¶wÎlñô–[ú >qfK¯G—¶7ùeªNyrês#hÓ– =œµD·öþoÿÏô#˜¸Bý³êªnPnM1ùÐÂ:õi§ÀZ§‚ƒÎ€ —ÁÀ\ÜÁ°BWÉWo(Þ öÃ’Sॠ¬ ub*s'PC ÛhðŸ=Ü÷æ ÚóöÂÜýИ ¹ð<v/‡wbh6ëƒÑÃ$†Á¢¿å3lý¡”Å+%ÌO¯» ´´®.¿£`è"h_O~ôößpÞZ¹AÛ< îaì/€)O8¸RZ¨¤0S…vZü¦?cã_dRï}È’3©Îfý~w3ÀHÅr<ükul‰J"ÏxVþÃR)×Ç•zÜ–Íï}¹~hD±Dn«hXèûÍ ôöä•/ŠòO¯Ž|•AUxa÷•ùO ü‡»¹ØÕñ´üiÓ¦amm=uêÔAƒÕlâLŽìü°#òô&¬ma¥Í(}fŠe'm^ü$é] "âb)ÎÖÓvïÚ °“‹ªd–ä]òàÉ“<)‚HŠŽ©«Õ÷·ï8ÀQ— 9»×¿Î*&E4t5]Çø´«Ï•½·*»»ôÒÙðBêÇD™˜ÀÖfØ|gM‚ Q4ñB葃)y…žÍðé.šŸt’Z#äååYYYùúúŽ?¾[·nµq ÔØúDW“FÇbÿ ¯¿ÒKç½iËdñOW?uå›·ð{sÿ´1£4S“”N—¢Ê‚¼ûWæ%`Sƒ{ I³¾c#ÁëÐÐäÕ³üùè—ÔwF™s>wwUx@߆õFo°SXr W"8!âòÒŽÀµÝÏ>Þ¤Š9–”&ÅE"\Ï­þ˜¦ 1Fiû=e–IÅxEƒ˜àjv™Õº;e(¢òÔ)9”Gøô,­ïCqq1•‰'Ož )ë½Ø}ÌjR_=‰Ü&Gr¢bÏ¥éMøMW˜'­îDYê‘V÷ö+ž"•J·)¨W¯ž‡‡GÛ¶m»wﮡ¡QÝ4«@‚7öó}å–{÷UFÌëÔgŠÎžšþ_ãmŒKŸ7[`ttö/ÏâÓ¤dÝJ—? McÛ» |uYŠÎ'23;óžÈü¯NŸiêjÆg0‡uoîÀ2d*ᥧf½Ö4Ý:ÐÈâ)Bs{fvvö=¡Ùê.ð">ÓÄÅŒ1wmnË2PÆÏHËJP7Ù2ÐÈ’)%&–ȃ7› ÈKNOXä«Å ¸¸(üU®¡—û¶f”ød>z'0¢„¹žHmQWyãZéªáø‹Èç¤_cùÀ¡°¸|„Oöq~S/Î=x¬‹·…¼0FPK&/¹Aîÿ”‰`R"'_L½_{1ëN‹[k7Ù»ææØk—Þ×Ì# ®Ä¼¦Ê7U¨åS9Ъñ­kp18 ÷­°}?Œô†LÞIœYæà)i•£ÊÏû á`Ù +رR†€!Ôôü{D(tìÖÁÉW0§(Ëj& .¯­ÎrSx’7LƒçoÁǪÔ#–ºþ'R#¡ FŠLVm‚  ¨ÁÖVP,ûĹ4ÿšÛ·oSL+”âQÿRÛ”už››« ¡Ô·°°PÎåVÝ£ðDÁ¦M›ôôôêÙ™{™éTíó9Ù7_ó{ÏäåZÜØw.ÌspTZÕO/ï –â\] Þ¼y'Cì)»ïý1¹ŒKe¨.e2¼M“ _áGY&û!þ-#ŽÞÞtýZË>­ @¤··E7ØVˆê~>LsëÖ­V=·ƒÔ£þ80^Á‘#G†îçç7sæLÿjfç#d8‡¯ÑÚE£õLJ”OJ~ÇL§zfNõ*-mj@¬Í̬Í*„$ÇÀÈOy:‰Ø9XÛU>ÏÒÌÔ²b|’ŠoXßÖÞÚÖ¾RüΞ•« Š ¦º^OO½Êáªk.CÝ 3oðØ®kn>îepÆÇâÜDJó/¿Ó¨K ®±sc½öm±°Â ¨©ÿî §4°Ô®Áñ 7»{whådÖ}éæ.&_¾W¿~‡NW/ùÍó«8¸ÀvöŸ sWÚ4ºÛØ”O¨©SÇÞ¼|púÌUsFÖó×ÅÿââõP߆Ζz*ks}$àlè7n‡ý¯`d=Àpsxu«ÔIŒPk,KƒÍ®ò/«Y?ر6„Š–ðM£î¾%M#yìe*`Î¥íÕHü3þi 4ŒØuZÛ‚¤š²š‘7o…Ä‹ ìö¥µehþ$I®\¹²Z§|ªYÊÀÀÀÆÆ¦•o«çqô[jRéÏÒð†æVl¤¤‰‘Æá¤è=Ï š4â~ 2Â@E9ù S&I|4€YÙ¹®Ælò«t•D˜ÜÖAQ3œ˜u¯ÁNoÑ‘K‘<‡é5àsNØ>•hii±XÌ/Ǭ‹õÉaSšššööö...êêꟊC£jÔ™5¯á?;ôå ˜”<¢Hb^ÏQGòîIBŠT׸À³ÒÑ{·à´Žº¸¢ó)?äàø©KFô[ÕåɼÎë¯FŽ~š“–Ë×5s2úº‹¡jÃþ8î9Ç仫é¬sOÚ=N+±4Œœ]l²ÞK(‘Æå³L¦¯½1!H“Éøñ=fàê`×RèwØQ°76µ¨…Á„ókÁp(¸êD [@}-¸º2ý@]Zó]ïTyEÂ{gäTqýä<]¡“)H…Às…þ°{$ ójšì”ý-‘e-ÕuêOοïüa³Ù}úôéÕ«¥æææ”ºŒŸ: AßT;!yu/Ù¥ysRFòLœtY¡‰¯bœ\Ì?È!ÊUg«òìȺDž»k€"¥Þ’²Ylu_Íz‘°ë÷'¤¾i›Fj2âk‹H=»__n Ž;¹Twq !Ä¿¡µI*•öèÞ]WO÷ËQ+=qâÄ[µj5dÈOOO çgšúóS‡Í옞¥õd$P&©}K+Pô”¼Xá±2ÇýÕ®ú1OA9³‚̹|`ÿæGÑùÛvªæji òÙ3Âüw/ÓŠªžÁJˆš‰­ii%Qü¾WYPst÷v,ÛSoêcUó·©hÛB«ú°ë1\ˆÑ206ˆ+šÂé°û9¼¾ŽëJ H !ø)ô·ýB«©hG®F!@Q†üsP]¤nDAömpSÙyO= êÙ5jôèÑ”­ü9 ©kêH˜)Y3§ô£˜5 Μ‘oÄÇ+C d­‡±þ_ÿƒ¼¼Ò8ÃÛĽá–¦º|ù™ ~ýŒLb£vŽÏ0…**àB¦ÛÒëëÛ|¿›RadŒ»ÇÁ©ÅûzPóJbîBN#] ˆDaË2 nô^ !g çÔW:ªÆ‚g;!§#´Ðþt¤ÊPz~@,AYx‰ràA:¿ÆÜRë±ðæµWÆ£ÂCg‚…¹"× ùY¹¹F ¿ª§û)ƒA«×OóC š¾d [@ëE`MÊ=¼‡€Û÷à—>€IJG²â2pê¶«!ü¤€é§;§Ø(Þ'Ÿ=ü9O}Ê?Šÿ8jù7슇_þcTÞL0à]h4]”Ï*ÑkîFpî\‚žNÕ¨¡¼¢¸) UÉ‹ƒýáÐÊ™溇Íf¯Y³¦Æ“eAÁÙ™^óûõrA•¿2Â@£˜)[ƒãÂR]Ú˜a•GE˸V)›ˆò°¸XRÕÑJ1%99ׯeô²­¼hˆrumòû·Óhkk߸qƒÇã}ï ÓÔu$Ìj•ØØTÜswý®åêÙËV5Ü>—ú´-]{Ͻ||ˆo¥á õ©ÆÐìÿ:b)ôù¶L‚¡Má}#’HM…Û/ ˆ9bÐd)lVÒÅÐÛ–<€àÐÃt iÙò“Þ&À+–B (J†™ÐŽ9òNî(/Þ¥S%d¥Bj–<~|4¨säãp 3!ô<œ~'ÃäÎò™‘¸ò³`ÿ6°% £VªÁùÅÐÎ Î%ÀÍ`p€!P¦!‚·iQ(?1+ÞiQ©/Q>伓oP}ôèýkw 4àçõCð#Q -~Á¶[äÉ&ˆ)RÕP뎮ZÁagw¤,³@„r÷ Š‹#(‰|0ÞKîD1g\¹Qi¶7‚  ¿kÔé(C>(Béä«$=-%«²(#ŠÄ:¢H‡øŽ~Ö”ÓÕ¾ÛåhjÊU¿ãäSTáMSƒàÀw†«ÀJ«‚«/JÒ²àE,´ë¹IfZlES° £Ùf€,ž'@ÓF÷ ÀZÞ!Š€»å) 0vØ1!=^d¬Ùr#øÉKhæqÑ€[(âÇB„â’(õ;@¯Ù`gb±<²bxvÀGBRèñKGle'Bž̶“ËùóÀ×.D <õ60»-%ÝýÆò©3òÓàaÌž#wÉû¬Â]Àˆ&P홤µ‚a!d%ExÅF”É`³ÑŠÒEâ¸|± ÅãPzè¬4y_î0D&•É=²8 Œ¡pI„ —ˆ+yÞD›­ð8‰”zš”*‚RiʧåÈýSÊ”y,”xuæñÅÃ/¤óg<|Ìø,¹öŠ“n_Ê êˆâ›··¬vöö×ÏIzp¿ äìê0Ûf>>zXÙ³D qäñè‡äUÈ«ÃSô›¶µÒTNìBÑ¢¸¸ScK¤P¿ë÷|Lñd©Ç˜—ÆmîYñÌOH¹3åÕ !õI]Øô ÞѰi[ >ƒ®Ò| **Ì4µ%¢Î•»Ÿ( Ö°‚vƼ•zÁd³8W>Ý»Š©‹ï4«F•Bš|2²¨¬%-€m߇—c5m ÿS)«`hÝ¡rBDi%ÃÀ:ÛÀ§¨kÑïÉŽ|±nFX‘]“e[¢r׎HâÅ;{6%fRjM‰+!7õœÌÛñlêÀ–‘¨ðYäò?bSs$Ôï…+ž‘†•Q«aM¼5ÑâÜ#‹Ã#ÃÓó„ÀÖÕ°r³è9ÆE¿ÜvB /öBM‰båff` Ÿ™õˆb(ú)WÖòED¾ªùø¢44ß -Ì44ß•å>z„õe¹iÝëèKo݇™beF.•–(|p2\,™­öS="n\Œ:ó8 À߀-IIùc\"”±Ù¼Æ}Übï§ß:ú¸Ÿ…»®D$wÏIŠ‹¥".öAó)Ã…Ð` ·cø¥ˆ¯Xtoi‡PF9 +>>átï`§OŠÄ´mGC£BÐÂ\[2‰P,•3Á8|.ýœÿë L¬àÊà kç^ºvë¢%æ—˜hbÙ+¹;*.O‡ƒ¼‘J„"²ÒGùd0’À0-5 Źù8è}yå"†ŽîÀaÖs¿¼ºõ¡óM¼=óèÇå½ ü¾3{hhh¾-µEþãs«]Øù×¶d§­dôȺÎMÃDŠ.mɰ›í2™]~áÿy(õÙ;‘¯-§j÷d(ФŠ5_] ùhÙ3Êø–eP7ÖǾÆ1‰“:žC¤í¼wîŽÝo8y>Èü&N»§©KP$?#ãttJbÎâòšÕ·hiªûêü›"LÇzLìâ£7l£A®TuF‹Ô2´0×ZîÝ4hšrnÛvõϯþMóADQ/ïÈê-tgÊ„„q·Æº‡nŸÌòûÝ ¯ÜŒŒ21Ÿ…äæœXö EBú rÓe“äûQb(›ÏâÞÛ~ÿÞ³‡Ñœ4pÊ|þš,H…¨ÛDÿç/N†,a´Ž{£ú:hM­FCóM )ñ/üW=hÕ7`z3^ÂÓGENÙ~ž«ÀÂX+ä𽣯NØð ‰Œë¤çô›½©ýhÐÂ\‹øGòiþ“ €G^HÑôoH$æ¤JIÀù¶6pï⣧-زŠFó‹ó÷§ßx€¨ žñˆž¶ ±¤Ük$Z˜òj^@!ž®vÝšªK¥$òÉKXÚ$ÂVo3ÂåÙÂÇ!w~;dm-ÓÔ)¨lûáÛžþ}Œ@JZ¶ 8žº½õñ'£¶4äq5™[ÇúêÜæ¨0mý©<ª4ýïÈÕçN?‹|qÉšQ‰©iVeUšŸ¢°82wvË ¹˜)ßG€a Ïx•qöpVƒá¼×Oû÷ )öl e0@&–‰+’àÚÌ9ÜRƒ ˜,—ÏHþt½Að‚!‡«Î¬¤ÎVS3CuDŸ<›†¦ÖAP§| |ø¥Ÿu#[¸·˜ì)_¼Qn×”ˆ%|©æ¸®Z*䎠ö©Ka~wuEïËî¿ÉÇÔ ÷úýâÖ1Šù Á‹L\ý 1‡gá5ûï-3:7$sžïà·ïQ¾C—i!{ÇŸþcüï'2šë& yË`lC§ÕÓ6/f·öãÃÓŠø.ý.þÇU“eE/´ö车vóS¶ï˜c÷b£Cóñ/‹X}Gh-œ4tíëÓª…“6{Ì”ÍgäKH®¦‘sûß‚÷MT.¼t䔿Bòjm†ÿ±mõƒ²“ˆÔ{ÓÇMú'8<_†hZ³r*LÆs_.4â¯wó1ͦ]'nÛ± í®ç§Aó“ßæé› âÌ,›µC$'>}’‘q1òm¿C¬rÂã^åÒ ¿Sò‘Û_²u1&µæNêðvL‰Š©lÃ!Iº-‡¦®A@\œ Í,'A29<ˆ’ETZU™„ÏÔAFêL˜ÿÏÞyÀ5‘|qüí¦'„Þ;¢‘" ¢ b;{ïÏÞ=={¯gÇú÷¬gï½W¬¨ˆÅö(H'¤íî6ìMÀý~¼#;™ÌN6»ó›7彌S#m-¸9ê|77×wµv&WI곉ð¹íj¾åÜùÎ77 ­ÕÒûùá+šW\õÛX¼åæžVÁçÀÄL.7œ~®ÓPÇæË³B¯D±¥Ë4rëzáX†¸æÙ+W7V@m_Öív~¾×<§]K¹àÞ. ·çƒè;[Ⳬa>W6Œmš"0Är²?tK%]^Yµæ`QËéŸõ0ß9<0<0ËO n4ïáħO·óI¿s ¤u{Ëýg®ßÚYŲ®Üô¹ûÀƒNÚ ÓŸGõ¯Sã@á%‰éVÕóŒãØËIad×:Öñv»ëÞ^o&LÃ8üÙÉx—À`û½ËnAÛÚŽ¢CRcïäÚV’ÎV‰6½1—ƒãJ¢˜so c£ÆKåmœÍÅ‘Ž (ˆá[Õ£eqX.‹.…\žy3Næ&,àg㘺k€©Ê‘LdLíAOhŠ´@IL»`JYžx6¼Òl÷ÇЖ0Ëo<ñÜ»…xqìªuܼÆÏˆƒ+_]›´ìˆþÛw£ÍÌÀ®S»¯]»rТ™Í™2\·ö[}rkyuþH˜PJúwRnEï ÝÖ ¼ÐO5Ñ&ýoñïi«ÕÑ0bw=ôÊïìÅÑnF\0ª¿iVM³^'Žæ”…¬AÀ SS¼Z¹,¡Ä(6%M 4æ•^û؃STA!•Ûõë°é_ôЏ½©ç«ní·Oë臮•o»£†ê-_°¼ÛŽ©ÍNÎìq;»ú…Ó+j«´ÖÄÚѸˆSðø¥]w>ªxøðøJ¦<€ZëgÕ3êvðtŒW{Æ1AYEò$ñÔžû᧲̒cMlüëxpé¸C$ygÿÝG)¹ €# Î$•ó ÜÚ‡òǽû_ö[ÏUj¸²ó£ÿa¸2ååæÅ·“R‘?ùo,áÚ¤r£j"µ¨â²¬Ó«ïÝ:Oï~äG¥Ät(iâÒ_€ZÆl¶"rmÔmd¦gdÞ[=6ݳ‹íò&2&ƒv Hàšw°ƒµO$P£mbëù½Ç`b(Â4¾T¡-aæÚZˆ)Ùå‰Ã·ížÕΈÏñôrE©¯žÅ>OÛ•(R*É#0¹Rlf×/l2=æ ºÛb& êªÏ)¨O×Õ\­>_™¯5¶l6p˜(—_ º\lö/‰‰dôc{’ô’DɤÏ¢øä»H]¿ò{|}N†_åàîu{Šáš+wzG¡xÓØvm] € b¼4r.7€T¬½ø¬O5{3îï%ÍÚ[üe¸òHø„Ç1‡6†NjÓþŒ„»8´9lß}ÕÁ‘åÈÂ6ûœŸÛŠCFØvf{’²éÉ9kjõºÁÏß=¢£˜ÛT[¾uOE㸨,ŒÊþ‚0Ó^oé}Ÿ.°ÁXªøpꑍá¥C©ޢÈOuîTõ1õ^¸ñ€¿Å{÷öt…>_›¯ 7Wqò$ÄÅéܽ«^ꮃëŒX,¸qƒªZõ£+¬¾ú9õ[JÎBÏ^<š·&Aæ<›³9_µ3»ìAQ”&ÚƒRA™ÛÚOêdŸˆ Õ°’‡W¥Eê 8”N¶ ?m 3%É’ðô <ÿZØ·ß—.3×ÇuílcŒÃˈxY.?´éaWûw²‹÷Œ öNZqËcÇÇ‚ûÚÜ×6¿HÏ”È1SaÑ›ý‹?2WìTžçÃïT+þËÙÆ” ò$ `öXúô€­OdïÁ1‰yïß+Š©/²¬ã³2²e˜•žf¾~ý&ž<Àbé\s¦ŠcˆQ1L.—³Ù¬¯½C>Ap°"$¤ó·~ŠÏ㥼”Ys_[ë¦è€Â¿2ÒoqКø,µU}½ŸT>—-¸røylÄ»ŸTþA——Ôê°‚âHäŸ E}¤Qþhâoƒ¶„Yº{ìh½y+T›Õ@`XÉ¢õ8zNnvÜǧÇKjÜô«‡x+ _ðOÝ‘!³}§Üó}ïe‹Ó|@ÐðACßÛ?¤Úg>þ!C—Àº¶çOø'¼Ý¤ZEÇÑ1ÏFu¬ñÿ=<¾K2|Œz£S⎠OëЀ‹•o×ÉðÀ– «ïOQ‰~´X"=®jF\•‘Ý|@íA}Î-=t³ÁèàoªÏñööBÿ4[¦.³yóæš5k:99}9«¦ñð¨´vÙ™ì{Lm€¤Q»[P7Ë£RÅŸTøØQŸ<îòÙ1²Ÿ ŽaÚõ dhd`bb¬Å 0èZ›c&S®t®ÞC´kNyd~ïýÉ^'z¹€˜·r|×~[ÛúUê7°Ÿ·çÕýÈë¡ÛÆø£Î4‹^ Êúp&7 éW>r•þ‚±ÅBø:õÞ5p½ãÊ¡aÃ6÷‚ÌW÷¦ý9nQ +ŽzsÉ'kÇ5š´`Þö3]'7p:ݲ[Ë 7>•s5¦ÎâMíí[‡­kçÔsZß9®‹ÕNº¸ºå°=|ÿqÓWAŸk2q‹ÏÑæ£*[Ü9¾Ž#+.üàÎX_‰ºTÛ;G­· SÇõÂ!­*ãÙ¯ïÇ'·ÿ{~]·غAttôÎ;çΫíŠc̘1óæÍÓŠ0³ÙlŸÊ>¿þ¼¿ æ*´] -£-aæ5ÿÏø ;ç÷m>Y¦®ÁgNªgE/Úré»%Õ«ñŒe»îœÞ}{×ï0c/ä¼X4iÄ#ÿ?üz¶ïÑqæêîE\jᜦíÇ]¶s®]Ü)5Ƶ^þ<`ñ¼=aQÛ×Ý4)çÛ¡ÏàÊä‰ÞéÕo€ï×é~õ13gúš~¬~»|¸víÚŒ/M¢5‹Ùʯù ¿æ}ϤZ—¥ÛºO³±dïˆOçP·÷öº{C`Õ}Â’îŠ&¹®;Ôô«ê(ö?77WÛµxOjjêµk×ЋÇ¿|ùÒÝÝý‹a`•0k|ìçÒ¥K wïÞe„™A³0ÂÌð9èý?!¼ÇwsûöíÄÄüÝn+W®T[Ï _„ÃáÈå:ßý!fÏžþ¿k׮͖Ìð›Ã3Ãç@v†B¡µÈròäÉÂæuõêÕŒ03|%ïb¾}û6<<½8v옮õ_J;Œ03|ŽŸ1øÝ$‰¬“ÂC¤ÐçÏŸ¯S§Ž«ÄPZÐø¤Ì‘#G M›6õèÑCƒ…ÿZ0J)}™!Sp"T›äQ?ƒÇåéÑaC‹í'Æ O"M•È¥JŠvƒ³Äž¹Wí“RÊ^eʪ g8Ž‹ô„æÅÂQ`¤Bú*3ÿD(ƒX,2-îÒK!—¾Î’«|P…‘ZP+¤/ä™8zÐ&IeÒ»Ü<ßÙ„‡}¸C_M‘š#Ë–)Õþ÷X,œÏaë‹øb6¤gK2¤ª“`˜€Ç³sQ]• yr¦TªŠ*ce °´Óßb„™á èŽ)ñúõë¢)6l`„™ákÐìþo¥RF84_ºti)f #$n=}øÞ[…þð–®ú EZfÖ¹¸±Õ䵚ZàêÐOÀ!OŸ¼2ñú;{KwÒÉììì+OÒ,Ê»-iça‰T2/3<úéØCw^Ë ft÷(oÛÀ’EÜÊsÓãúí ã™ÎéèUÅͱŽ)»hIVÚù«1½Ž>Õ3°\ËŽO‘999‘÷Ÿ=æ™o[}¨‡Q±Æc½yå4/J–WW¶ `)Š 7‡ùèÁ_‡bŸPÂ*Ff|Ä›w—¼qhÙ|_5A«”‹×¯ŒÈmRÏ¿·‡}SO3 мŒÁ󆛻ÿdßÜÛÖ^ }Œ03|.—+‘H´]‹|fΜY"åÚµkYYYúúúÍÏÀPf ºôJOO¿téRáaLLÌ«W¯lmK†Ã)P$[ߢ{MlÍåXIžùè–U­U³W¹¢ B£¦àúƒý \Þq á¹Üú·ãiYœ¢rUœ!èeÞ¿ýŒÄæ]ñu—ï§ešö v³UWJŠäZv ”Ï‹ÏÕ³èUÛÍT©,ž20³é”5âäS[» -«èÓ] äIAC;âVͨ¨ Ë¡.]H¨WÎúì“Ä—Ò÷ÕñÞù²$áŽËâ+.ÕÂz[° B·õ*òjë °õ«Ur© Œ¡hTÅ¥M9= )`áׯE¿v¯ñâÏJb­Fžd„™áshÝCa!¨)<}út‰ÄÄÄÄØØØ€€­T‰¡¡Ù¡ì[·n.BD Óùرcýû÷×Tù:&r´öÀn½ÊÍL—ƒ!=l;_LµµóàkÂ! â`˜¨œÛöj·ƒÂïnŒqùÛ]øe?š_9lAdEŠ*6¬(„+r¢˜+Z ‡¬Ç¡ôþU©ÉŒ¤¨Èè×5kÙFEPäÍ8rç­Ìp]¿*–lyáI1ÀlÝœz¾á©.¿ð4èüгÓ_˜ä-Æ?Qæ—À3ÃБ}ÌëÖ­û01''',,Œf†/¢ÙõY¡¡¡EQçõüùó}úô);opr³_R”ÐØ„K_¾Ûño2äàîZÁ‰VQÙR`6p1æÌ“7ÃÜ4eg²ØôL0êò€<ï±K$ìQ]\ ÙE~A6DJñ ò*g1Õ‘ÝïEbÄë¼övjÇSXnvÆå„(_»™>Òb%cFÖƒéàPì·R.Ûvû™ÁÎÆè j»Íc„™ás ;CG†²G©P¿>tèf6›¹‡¾ ºO4µ]*;;ûÌ™3%£¢¢ÒÒÒÌÌÌ4r ­AQR…R¦$¤Ù)ãÖFeãÆSºøèÓÖ%õ,3 œ¥Õ61ê¸ÄdJE«!aÆnÝ»Æév§(9m& zwl8¡²i1%•½©1Á¤x¶úÍÝð ¥½½­Ê#&ÉË|¨k'ÃÄvý < #vlÚ;ìJNÿAÆ,t`ˆiÔ>‡Ž˜Ë%J¥êŠ1ž¿¾ îcž7oÞ‡‰=Š/åÂŒåå% Z}FÈÚw'…ãä»,¸—P •’¤åŒÅúpe4Š„D®É˜g”—›ßÅþú)—˽L\´÷œéQýmc›t²ä¨+üàÅ}[Ï“ölS€¿Lc—y’ø‡µztš"d"ë+l_ņÝ\œÜÛÛÝ^½â°ÃˆÖã+Š@K±ã a„™ásèÚ>f5ª°Ì­Ëð hp({ðàÁ=zô I‰}Ïž=çÌ™Ãb±Ð¡³³³FÊ×”@h½nHCB¾gû®çŸIUŒ°VÛÀ˜¥ˆÃx—&œ_l(]ÕÜìT€jBîç®/ŽÁç•»x ÇXŽcŸÇ¯äR~YnÌäã]·<é8±"&#ÐÏy6þÙëÎCïª?”‘†ªÿ`Òƒë+²‘¬r¹K€—ÉRºØŹX«q£š3}Lò^ "§_˜¼>ÌwdÓ†æZn^˜ÖásèÔ>f†ï†Çãåääh¤(+«÷!iQ±~~~\.÷3ùK´¸’XóÆõF9ïü¡Tb#!Ä|ìõÙø8YFœ¢ÊŒS‰ IpêØãŸRfŒ¼—h[ÞΈõqm¦Hå­Çï*:Y¼ßDÁûgÔâ ÍE@¦¤gf„ŠS扥ÎLëdÄV<§¾xÜ`Ñ…»cþ7ÓWDb=ÓªÖóð©ÒÑ©ÄØ4)‹J$ýì……_ÀLÌE}ƒÇ½ÁoÌþk½%êѰjÖœ¯^§ö`„™á èÎ>æBòòòÊRSÈð øIq¬µû'Á72ÓÄõÔ¶ø>;cÏvuGš(vqív{Ôƒ»aÉš˜ãùƒ½8²w#O&áfN<Í>6Ì­Z>M¦-:7i˜½Ñ'2ÒÔÿºÈÉRð> "aÀæÈ³ßÜÉ“ZvF ,VæÃ;õmýÍø…þKì¬ìD Ï¢7=ód,hH]÷-[ãFžòØßÄû²X,H{Òx;çíT(1³¡PøÖk'k°÷NðˆÁ¾¦„Ö aføÈ БÅ_EQ*•egù+Ã/á'mü+#³*¨óÍÆUö.Æe³@©@Fsåº5æ]޵±ºÝ`(#‡´‘üooÓ9GÖõ¯ÓË®õâÙ€ugw*ÎM®ï#¢ý}Ñå¨ äqЪž&^ÇÅ]—ñE,•H³qt6LAí¬KA$Ä=H ô9X~u¥Øt…0¤—w£ïô\ùÒÔ1º((*wî{PµA76¼Ÿ Ž÷5Ûôôù‰ˆgƒ:;ƒªÖºƒÕ¶j¸¦µ‡“žxóôÙÔmQ)xuõ„9Oµ+šƒ¾2:©’Dÿj× š‘>þöµ?÷èíPSjgßTé¿¥~&ºi0®‰¾•ŸÔÅ$¢ÔߊÈ`ÍzÑc4KßÂß »ýÜ#›© ‚?fx£ëKomØ}öº«ûòv®bB4ydvwb—œ _µ[FaÀ×3hR÷ÍAvlB K‘×s×#Bϼš^öã¶’X¾Ù‹³Ø.®"+ëÅ>ûžÚXY‘dJ­ ÛÈ»˜Åæû;rYøë„¨‡_Vv¶&d/§‡fý%i ÔÂâ¸Vó0”6~Rg®Ô«2м¸¾ÝÚ þ.¨#N¨Wb rÜ;µœjjÛQA¹Wª¸ÚÛ ]i¡öA©ò7B ·­#Üá£×„Φ»J»Gy~<A¢óÚ¸øë%ÞWW€ RÏÈlz¯¦Ó‘1­NQ/ý¢À? ê¶@Ú/¨­ÚXW€Ðx`›àïÏø¾¨îMkuoŽ©‚ùEÇü¿ñ­TÞGÈüÅè¿F˜>jwtp([û :‹ÅúË¥RiYè#"YR|lÌ)“üƒgüôN_”ÿóÓ²_Ì@þÙ§»°‹ð•µúT~ ‡;à£ï È•{ã×Á3ÃÐA› 0xÃ×£ñxÌ «ýJ;Œ03|ŸÑœý yyyÚ®CéƒÊf(-0ÂÌðtP˜3…á;ÐÔ>æ¢0}D†Ÿ#Ì _@UÃáh» ¥¼“> #Ì¥ƒ´´´Ó§O§§§8ðŸZ£ …Â/gb`(ÎÏØûÎ e3ü aÖiärù©S§Nœ8‘˜˜X¥Jooï__‡²°è”@,k¶@©TªƒÝÖ/BQTZŽ$å]–ô£+±µzÇ“Âç°Òsò~͸ #̺KnnnÇŽëÔ©3~üx;;;mUCí2Iwä™ >Ÿ¯íZ0”>4Þ¤ê¦û/âåís°¬BëŽ.)Π-ùÛú<F¤é;z{xü‚s1¬£<þ¼^½z;vì@†²vk‚ìŒììlíV£d¦ðx•}w8¢íZ0|F˜u‘ÔÔÔAƒ9sÆÑÑQÛuÑ9ÿ—:U†R—Ë•Ëå欬,&"¸Ž““ÃçóË‚ßreäk”1¦N:dÈ]PeºÝuj.-77—YüÅð W³½ºR:”­ h¼»ÿï¿ÿvîÜÙÞÞ^ƒejF˜uŽëׯ¿|ù²qãÆÚ®H>ÈÂ@¬íZ¼G©T21¾CCÃŒŒ Ú¸Èþfvî}+111 ,ظq£f‹EÆL5aføY :tûöíÚ®Å{PÓSt9«ÖçÕt3®ÃoˆD"_ÎÇPôüj|ßÚúõëe2Ù¥K—jÖ¬©Ù’µ#̺EJJй¹¹³³³v«qçÎöíÛgggçåååææ:88¨Ç¼½½Ã´»öJ§V¢1”"¸\®ÚÀE8ºŸÑëMe†²¿ÔzhÜ]ÚìÙ³Ñÿ÷îÝ;aÂÍ–¬-aÖ-’““µ¸3ª[[[6›*£>DZ¨~Q¹re­¯ˆÖºÉÎPЏyó&²¢ çb,--Õ/üüü"##p­fçÞ·‚^Í¡¹uëÖ‹/Ћ۷o§¥¥k°pmÁ³nZ]0MLLªW¯W"=$$D+õ) c¦0|=*T¸wï^‰ôŽ;þø ^dy3˾tÅ4k1 êu¶hÑ 3z–‚‚‚tÄ8@ÝíÒÞføÅ ióŽ;ÐëÆkêþavî}ìX¯]»–Ãá(Š¢‰OŸ>}öì#Ì e¤Ç†††èîÐv]òAfŠÆë2”mØl6Òã]»v  ÞɨÈó·‚zE…~~¤îÇŽ+|^(Ý[6Ã#}X½ ¼ ´Q]†ïÝÕ𑻚á[ ÖvtF˜~Š„*Ýaa4^¦:Æ ó14ë~sau#È+wã@«áPá-t¸ô…2Që÷ö˜šÆXÕ Zƒ¤ÈgÖD_{&o¿½gK¢4DÙø6RRRgÏœÆ&ªîÇo 2ÁÓ³%ÃG­T©’¶êÀ3ƒ d ‘P 0…>!PÁý#ÃÚrY1©þÒÇ0ú?ذ0ÙoßN0h ljz4zôúÚ¦Nµ˜Ó22râ£f5tSh:RV©ƒÏaÍ?y?6þáï*Ì”2;#S"§Ûi gñD†B6PòÌŒl™\Ia8—'Ð×ãædår8\‘¾ïÇv¾QJivŽ$ONóX”47;["eñ õ¹ÒœìœÜ<ž¡‰˜÷=—…”çeçæHAhn$*½/¦š'–Èp`s@¤Gß"¹Ù SÐö®>RÈ‘€úáå A$ø |.H£[õ\dÁ3Nƒ Q[¹E|RIO6+TAtØ<ëÑÕò8wî?‚w©ÀÅA,¢O¡”AN.=H޳AOL§3‚ÍðEŠL@¾W^HÄ~.ñU$&tþ[‰xÇ– ~·Äa)EÈc›ðá·fà° „\íNZhQ˜sLíûoVÙË‘Cæ=:yÕ|á“íAöúԮ݋ƻfÞyýœž-êT8{hÿ²¾#¯xµ_1¸Uí–*šþPDÅÛ'§®nßï¾û÷Ú¸ ߨRw@ZÛýÏö¶xpõØF]œvÜÙÒÁë;J–&=X3«ó˜ËÍrbBK¯2³pˆ€ÐÉdžqÓ A}0¦àæ ˜· …þ!l쓟5ä<†³‰0}T·/òy p\9K–‚x*o9HÑIØ:¿ßJ äCZ ཫ‡ç ôT*ä;8!“¡OMxv N\È€ÍëÁÖÚÔE Ì›Ù6à †èË`V&þ ú¿}Âðy0JwêaÂó<$—ãêÍMx‘šrbß+BogÅK~’–£d™V°ªÚÀVÈÜOžÎ`Yš”7’>z˜­`óýì}üŒð"Z…±°œ¸á—S•,¶ÐHèR·¼£Hrëô‹Ä¤<޵ePk>YªÕ™A'К0ËÎOî0çÒæ§/;ÛªŽ}Ý»çÐ/øNú¾½e\¬K§žm „=‡&Ìù¨NÛ½;þøy¹ÖÛvlæÐÿ(ÇùæQ§µ¿Í°+B6 ,ŸúÍüAÂþNá:T®Tâ–ZQ¦QàÓ†¥Á€Pàù äTò‡`èßø °¯ ³j€-¸r¸^ Î^jöðþ’Q@r¡^ 8ù¤³é%`8â—Â’zµ—¿-m1G§ÃÑÈüìÎ0nT°>Ïk¡-С8Ô„à+qFŽLJߤ§ÂU+82<Ȩ uFƒG5è^«™áÓ`™q ±æä Kâlè‰×”‹–¸â&f~'Œ‰óiãíëe.~úrß‚3á§}F†z ì„6¬x «äPÉ<1üîú‘±Á#ë¶mböÞ‚&)‘‹]¹[w–­SôÚÔÄZ\àRÝâúäõWâ2ªÌ  ´&Ì)qO•”2+Kú²ô±uû“ë¤ê·(JÝ?-¼ÃU÷úWÜðT^j\üs‰’$ 0°«àjmˆ,ä¤Ç ¯Þå¡Î3ÏÀÚÍņ[r¼©è… >"ýUÌÍì ËÐÜÞÕÙR}rÓ^$Ëà]Pý0^5àÕ ¸/!‹>T(>VBá xlØ 6UÀÙ’N/±jÆÊÞ¾‚˜ò@@æA~hxJµVu#p”pè,8t†øz€£J|ñÈê ÓבAËP.n>1ÈÂI„‘¤‹Í™ÕO w;.ÒGmŽÀ¿‘‹Ÿ Õȹ<'gƺû‘×Ë×õ Ñm­oÖ }‘’ªÒØÙTºiã–øzõMMØÒŒ n_ßÏfÝ™ã—$ ›|ñPjmoc…F—4€Ö„Ù¶yûÁG4ŽاGHžƒÃùT“Döù£_–÷6¶™/oI©|iYo\ۿǼºÛ8H£GÞÖbõÕÅí,?S‹®^8—i’ò$bÅö»­§lY=¼jýOošu1Û;ÐUïÀйOV\ ʸÑÍ:ݰhÓ§©»2%~{dæÖõ¡ï ’=û«õŸvc´q47x7§cã§Þ½x\(«";Oo+^øLbpâ¿•UBwMnáA¾Ù¾ü\Úš¥[”Ãÿ&¢ïýŽ¿ƒ.=aÉ8{„¯@ð'¸ èõ_nÁÎ+°á*TæÒô¿_1–ŒáôA|0äò„î„¡›¡•ð8pd:d|´pô+@³fšùv ¿,ºoÉ%þe Ü-Yww\‰ÏþH>t_ŠhoR™¢äÍLQ,‘õ?³¥ìÊçðjÚœŒ;+éWéQXZ¹qUxôâFM ÍUÙÆþƒn¿ë}ýð¦›vî:0Ù6 ÷Uô›ï+K™°þò[ƒjÝÕ‡ú® ºÒ/¸f^QG\爤Os œøàÙû4Þ=ZÜMOŸ¤Õ¶4¶63¸~,‹€;ñ¯0¡ çÐÕ˜ÿ?>ê¬ñÀÑßçè‚r©[ëzŽÝM†]ôÈ/Ȥj5Á‹á½ú¦ë\;(ÐÙÿ}_ñWB‘ ¨CÜaÍQ¸¡„U3@©²–ªZ6>àpØ_6—„T1ƒýÉô*01“ÈŸÄÇ€ÌTž¾pÕ ðb&‰Ê2¦cÏ`@ð¡Š!eOàôø9{Š(9*ÎÀPŒ÷vÿÁl§?|\츔‚øtFŒÊ‘H‰x%ïg SfçP 0â%laŠÂlë;;®Lxvâî…Ûiýk›’„ìg|†ß­ïcæUmѯj‹^½þ×'௹kcæLóøòg>‚B’‡Éq¡añTêÑÉ%ú˜²caE¶÷Ç>¹ •JÔcŽ ©ÖÒvø’ž6ÉFzêóå•ü‹%\6·æ_ʈ%k.͘\ÇNd{o¾±qÅ¢©¼±¨³ãØ®¶žfŸ9-IêÄjc© ÚM€Õ!Pn4xàùÆ€±% ï¿è?Q'á @%¾jŒš B.'.p!°‘¬òé™ã4)tŸÛ‡Áäe0ºòàø}À9ôòlޏü· l;Àókp+Ä,zy(/H€31à„ƒ­´ gAŸ0> (xñÌ+ƒ£¾v¯ƒnC’8A`,ÝNÉÑ8µ Œs±ÔðgRœcçfRB˜Y|,ùÂ3p.ç Â>èR ‘EûÖús¶Äî°*?e8Oñ }~†/ 5aNXp{to8¥Ý¼Ñ+ÿ˜×£¦‰Òަ뛛xl–X•ÛrðÒÆÿ4Ø:fU H\×}ŽÍž[ðÊ;Yë}nÛóçÏ tÅ…4†ÃØ• ”‚² GC² ã$è>j–çv†ã —€Â¶u¦}ƒ ž‰B!ÝèQhÔA¯e2:³kØÓŒGä´NEçJÀ«5œëD産lm!„E;É“ƒ¸lÖUq®»µÃH}–LèÙq˼ZŸ¹,÷ïßoß¾ýO½òß„TR2E.¥ÿå“ ùójHŠî›ú`¡€Ü"‰Š‚é8B9…¾<ðÞ­' ’œb% 1VÊákAÂÉ7¨ÑYô—ž"¶rµ¤»ß8¥ây·ðôwåç¿++u†2 Lê÷ñ×# \~)èn©©—c]¯aŽ! ÃYyÉϲM›W@6ƒNL?1”´&ÌB¯ö[NLô«ÿïàÅÿIX±çüН*’S­Çâø‹‹æÑòjJ¡ ¡þ;xØ¢-ižëߌX_˜¿Ù„-Í&|P0ÛtâÑg ŽÆ,Wý¡(—S¢ZL)šñÔƒêè‘^“½® ý`Üë¯ùr¹<%%ÅÎÎîk2300|‘ân è׆ы Âh¨¢+:àâ,Ú%'=§F–ðwPðqžs=6Æ´BU[Ùµ…¬ÆÌùÁ,ÇfÐ(Z_üUjù¬'Õïsþn```hhøå¬ ß²§/o| ¹°éfvsŸ:"R©R\6žqõ~رÈÊÞ³œ_µ‘‡«#û#ó%ÈZ~ûr󔏿Uº6¡ºý÷`(Û0¬+dffþùçŸqqqÚ®CÙ…®e«éíZªŽPïZ­Êô;JÒ Šûàõô:LýßÇfŽ)¡×¸Áÿå”Ìü2ƒæa„Y'HLLìÝ»÷É“'õõ™=@ ?ì“cZ_RH«‘‡Ê>Œ0kŸ«W¯Ž=zéÒ¥žžžÚ® ƒ.Bûâóè…¦Ò²?¥Ï³vH$r¹<##cõêÕ/^qâļ¼¼íÛ·W¬XñرcNNNÚ®×·døñqÝß@Èè]^žƒÙÿAÜh²&{Á®Å°; ^;Áñ%À%¿Ó:KF$ÌÙ‘Q0í40,{Ï Ã/ÃØœÅÆAIÈè^dÁ!AÊi×"‡Ëb±0B®TüXïÅa¡’)’TH‰²v«ª=Ý« T’‘ÿT£^3K•N{ÿ!@ý½9ì|—C–ÿ)¥’ŽêJgV‡ÍUÒ9ÙlÚo±lªP6\µ@ ‹†÷o:œTe¦0ÀsÆîŠ»îá.²ì$G‡•¿ˆ òÝ$¡¯ÀÁUŽ(º†¨Br]\T¯=a&³Þ"´s·1âj¤<"7íURRšRßÓÅŽýõAªHYJò›”·izvåíMôJ¼)K¹?sÜäË÷èù ݽzð£%|"FFF£FBFsHHÈüùókÔ¨¡¹3üt(%”osM AZÃ}hÑ r•ôÓÔj4(`Ø­=‹aŒÕƒÖÝ@VÖÚ9†_ù.íØž˜ˆÝOrëûÍëK²Ï￾éa²gùY yÙ)C/¹–]qHÛ¡íõˆïÕfJ¡¸½ïêŽ)Ç 7‰ÉOÏ(m²œ­Ç#ÞNÍQ’ ‚"E&{ׯaÂVH2þ=¹6>C¢$)œàç½°i¶rý惽/¼ÙxÍò•­½ø(&‹ð©Qcuuþ¶ q»ï%'Ë¡[Ç&êÙ„<ÜêÈ+¹Aŵ Y«ÎÄG¥É¼*oýÃôè•Ûï$>—4¬»§Uy}Œ8%jÌ–¨›z¦×¦´óçå.Zslí[žyÔê:wÄß­GÛsŠj3EÈŽ„ÝœuõUŠ”þ ìœ\–tðö7áÆ\¾øÇæØDgxÓÕû¯lËñÎ[P…¯{aÁ´'ÌI[ݽ5ý'âÀÄê wOHsoìÔ~µcrü‹¯IÉÓÞ½˜U«&9;lÇú%Þ<»¤w”Ãä ë‚/¼ôóæ1œ:Ô³gÏÑ£G׬Yó§Gó È“½7…I%H¤ïR~àì;@Ú,•2®•~Üĸùè`½ÛOŽri³ ‹ë­mxïᜎÎ75o?Ðýâë<Î=èÈî®ö§¾'wì25óI*Wï:68Á*j|G_Èò–í9—.!ÁH¹|ëÑ1nO¦4±³dI¯:Ï=Ô„àÝéàØ«[½ƒÑ{#@áåã} šOLô®{#ÆX6ZÒ4è¯Öع°Óýöß›dݸe½~±ûV§Êl]üvxy¼z’Ðyý­!6uÿ«W­w3ö݈ómŽÝ¹QÛ¡ž §NÝêËcîw¼åòÂØvµmø>kñŽš«e ê{Ô¨1úaÊß7ÒÉìG¶«š|˜Pê¤o_í ³M¿çÑþ¸K¨2‚kbççå„ñ¾ñy`‰]=«»[AûÃ0Ídô¾ë6‹ô‚Z5ÖP5?މ‰Éž={ÜÜÜ"##-,,~ê¹¾†»AÔs:Mü28ýA §bSE?U|ËJ±Ò ÞRB}x"¬À7¢.>R º Er‚ ß*„’u´QŠ>ÔÈŽdŠPenk3&ÍMYw%³E¿Æ¾b ¤rÔnvkXãre’¤-Q’΃]í¸$ä‘<#ËÞÁVÍÄĶsªHE ÅFN&aånïˆ?ÚZûسÄq?G¸ø<… Ü•„‚$qA gS}¹ÒžWÎGp+ÝÚÊ×IܪZ9α;)tÔN(iÿåP ¾ÎHX¥%´µ™K½ò®`Ä‚<p ‡¶vŸ´àéåÔÊ­Ùès(¥¹Ÿƒ©\ö7`2]ÕÐ’0“Ò‡w®Ç<˰彳r3I‰9{þNªuõ Ã×wï¿L˜»×õ3Àò¢Ï…Å§È Ì,Ýýj8Hã"oÄ¿N3÷ª]£‚qRlÄÕØ$ôÃØyÕ p)ù5¹w¯œ½ŸÊ­PÝÍÖýPÏoŸ»þ( cñܪ׭d¥ S!}sõbÄó %_„ÇfAÉX¬òw7"ï'CVÔ¹]»ÞTháëÀ'Þ=<{ùn¶‚À„VAuÍ8)ywýÊ¥Ä<~µÚI·.¥ ¼ê8|_WƒÇãmÛ¶-44tþüù˜ö¶I²Ø|–¯ à³A’ oìáôrôXÀêžp’tñÒ^ÛæSÁJ£¿G,› ·b!å-°œ`Â$r¦'zÎ,‚UwÁR ’dx£³fƒ§)­ß¹ÏaÝÿàZ ˆùôš²D6­,R ä· eO0¬½F@ãrÀ¸]úAO ‘+y“’–¡ çìÜÌõqR&{~ïí»L%j¥ù&†.•y_§–Ьì¸Ko3%Ï@ÏÑË̈GÏob@$ÞKNJ‘£"X"Qy_31Ë﬒ŠW1o’ÞÈPñl!ÏÜÙÒ¹èZŠLyü2QÎ7×wªh"`•NÅÆpyî»û€µ7åJ ±¾JÏÉL¬ƒ~‹4 žÜ“`KÌüaÅ|¶`ªx!és«Ât\ªè›.ú¼<ã’<…ëµ)Šob&†ØØ%݂ϫ›hI˜1¶¹…¼—oKÓiáû¦ÖäqaýôNwü§Ï©c‘rߘ¥7ÿúïÒÒÞn<êÍÄNýœVF B×+¥­X°jÊÞ‰ç&Ö3kþh{üíÒ¡-¦.½º¤a±ò©ô%çÛŽ[V_HL=?2¤þì7¡sFà ‡ÚÔøwóý“ìGÚ7:&ê¹f|C1W!fAÉ ç8ÏØÂÒ€„‘¹­­­¡R6¨>¤â°Uݪ™Þ=0/`Îñë—æ˜rÅ&É«w?Z§Ç°*¼kÇŸt»yzÀç¢G}–*UªlÙ²%##ÃÈèƒHÏ¿ ÈÇд„U£@ÊWÐvêज़Xô'8 7j¶ë¡™ËÊ£<tC¸¨³«úÃðipê?0€~˜Ù<€õaÿ)ðè xÌŸa"8¾LQ›˜sçÓkA .Àá½Pe,Lî|’QåßLž–ºeä™tOϦõMYy’ÛÛÃ5iÖ®‘(çuâéÃoj¶)Ç•dì›:¬~ýQý;ØÀ£f:%öõ+¾€Ìξ¾öj–ÀaȺZö,#2/Žvïïo! ®mº¸ÿŒÿôiå0êÅÊNŒ>z²iÑÁQS<ʆ>Ìe é#LÈc&6›GAÚÛ7ÉYb…‚eW8œN[ÇzCÛ–[³;áT}×åDž°H®ªZ"‹Þ:¢«¿‹ s!ª›8ÿ)rêÔ§é€e»çn<øþ.Ðù–/Óeè¶gÔœ-à#‹N¢ŠÆ1ùýk•¡G-Þ{cM÷*t¢4K |Ÿ:þliƒŽºöü‹%0>='Äâoøò ™ŒBwMÁœ*†óX\Yæ9Îøvvv=Òx±_ ê5pôö‚]l¨ICʼn­°û ìŒWt?¿ƒ]›¾ý–Æèè˨`1žEÃâ=°è Ô1gVä—†sèÿÓ¶rðR ~¬ˆt˜9þšÓan]zUÃo ’\Ô+/,úã\>mIßœµoW’Íà±ÞfBEê…‡O¿ãIåpõpœTH,üÅúc );O®åfÅŽÏKˆÈ¡›'¹45xloR&aê×Y_ìX£HÎ#¢N¿qkm ¥}K3Îî×¹™¾áÕ1ÛŽÍQl°–—Û’vÞÖv¿.Íy‡#ZLZŸ"#qž¨M£?æÕ·¦”ŠýǯËP’™·þ<"^åGôßpí>÷·Ÿø·éó¾'î£f¦ßª‹mÉ­Oseä½N;õÿ«)œ°åúi (öžš.ñínœ5ìH”$£Ö]„^5³#Ï;.2¹Ëÿ¦÷¨ÕÂÔrQm›†Ç¯8OŠéÛ¹ñ’ªúï/2AT ¨}Žº=ê;ºç’Æò÷©Ù¾’%½r%ôf*H3šþŸßÂ×ßXtS˜‹y,ŸVÕiÞœG.£.¤S¬+—IFf–ôãñöé[Àí\T+™‘¤;öÚ:iäcË+UNèÕ*,@ÀÔŤ2’´´´,(”20v„ç±oÌéCüËs³òbñ•¤GÑÊ%Ï{“þΉîœkŠ¢’““5^ìמw{Z#îAP5zûä‡Ð·éF`)BÀ¾k®gCz,½¸²¢d½£mq[ÚÒy¿þ‚#°»!WoÄ*Ph)Àø¿ ïƒC ùQð3ÐÙþ.ïðÑý˜-)>@áììçûNIª«`o)`³X¼opkðJ®È%HO¤O¥nÞüÆ<¨º¯KN…ùãl—+æÀ[¥B®Äàƒ±7¥¾¨Û’zÆì=°ýzPP}GãRºîë=GعMÃÎYt?=·rÚÁÆõèÔ¸Gˆê td†ÒK?Øm[7':àªåÓ(6O©°™ö7B‚\ ˜åÅZÕiÿ!¨&wøƒÎ¦¤~¬í¸Bö^‚öõ¦-l‚™Ú4 é ¡$éCJ‰ýѮէOŠÞ-ÑõÁ9µkU¿Q¿&¨)•ôlAV ªý²nÝ|7&rÆbþ"OH?4\‡…L_‘ú Xn¡ÛÇ¡+ö=Y¹ß>[Õ1«æÔNÃvî™Õ}*`×üMRÇî½ w„B.àp)¨Ò9´ï ßVÿ}~b0]kT[á³ÛÔ]×ÀÕ8+éi¦Ø¡FÍf†ãú÷]ØeÇÈÚø»˜„w Ñg§ŠÐòx,àå×ÊbÔ˜&Ë»þs<±GskH¹¹ïØçõGT1'´•WPw  Å%Ùè÷i AÛ`ë$° AÀ#áåsp® &)´'¯æy2U_YÀj©W€LúÑòé Èо9er ôü4Klx}f,…Ê]ÀIɦÀØ|úT†¸Ëð ^Îv‚¦°zÌQBë@³!%ŒÝA(P-×dɇ‰3á~X¼†®$þ1‡? ¿IpMªºÃð¤ÜÞÎÆIOÐw NÉd2ô§{q¸Hš¿eÙ•úéÃ0ybÆ›t…a‡ J¹ÃX,TÎæÒÂÔ¸^êÜÛ—R_'+,ØHbè•P;ýŒLLÄ¢º3ƒït [÷ß‹™SœAZúoU$oÊùâP¨< •LT@Ñ1-z“Õû‚ hö¢Ÿ-–M屫¹м‰}xŠ Ïæ}°ªàS_AÇÐŽ0+ß=\8iÐ ð—œ\iG×wóOÜ‚7Óºn´\ïòtåôðøù›¢Ûöö1SÕ±ò€©ü]êq >ÞrÑÕ´ !N6lœ0õÿ3öÞ?.¨ÛôdWßá»É8þˆÍ­éªgldðæäj½ˆ“ËÿêºénÒ€6ÍÝMd8¿Rƒv,ò¯Ñ÷ê®­ÔbWjýw  ¾zhŸj7ºô¨¤¥{.|Šñ]“¯ô_¼âo».›bx:Ù #)¹Èsåù°v6lEò½I“‚æ)AhC`t4ì WnÊ¿`§3 A;ajwˆî¯ŽÓÆöÑ ÁÙ0õ_z’øØD0í6ïÐâ 4ñƒT p  å_0¼p(°÷…Mañ 3¾Ó¡•l]|W˜–€ÃJXú?Ø:p=¨Ù Æ‹aü$ÈXô7Ø.» P²àÑ.èøþ÷/XŒ6ÿ¦ VÕáW]™ÐEÚ¾¯‹­)W™“#çyùYûˆÈs{îÚŠœ%_^ÏÂ9&ôNz¼@uóÿä«0®J §©‘u'‘“˜‘•vð×ß™ºÎèmJðd~öøá¨{góŒóRÃ÷<0DŸ ePñïzž‘ÇV>Ù¤_%7'!&“¼}Ëhe ªyNÔsÀ -[ pY¾ôÒÖ`»ž9ãN–áëÐŽ0³M*Œ]6öýþÔ•¯ºM»2­Dþ–“O´,rˆóMz/:Þ{Q±³~;ÚDì¾÷Bü£ó˜¾©_“ ñGã²]lìX²;w3kx ŠíYÓ.ç൫B/ßJb]Ý7Ë [èüó¯ûàÅ'Ê4²<ú_1(:¸Saš<«0k±\¹ÅG–ŠçåB‰’>rŠlz  ®²BJÿ+Vlv‘²3¿ü~(‚9X5ûÛ:ßè¥Ð-G/ª:8„üãD§ô(·E£öWA'’$eÙÞ/ £úSÔÿÛ»óÀ®ÅàgfîÜ5û& Ù,‘‚»k)MÐÖ¾—>Ôúk«EíO‹¾Zê¡¥Jkyöj©%¤j-%b‰Ø)‰ q³ße–ßÌMDl-»“¾ïçîÌ9s’{s¿÷Ìœ9Gषª:¸qåŠM*6¨¥u/ÊQ¥žoŒoIn)ÊQAò8bœT´Hé›hج°é-?+ˆUk…µ±}§å¥%W¯Z—‹ÏÁ e˜ž)­äL !?,9©„´Û#sLüS’S”Ú,^â*E|lÆ ¡ä¸7ÂcGøKfA0(‚^1вõç$E£ú•\,z,¾„aX¨û·¬=íªD`€W‰O%Y™”™)2ýâ7r¯&'/>q7ÝÄwíÜÌïúùµ§nÞP{Îlæóè4σ¦n_º°à÷ôË÷ØqT£Ëðµ}3¼J"U="¼×åjìÌlÒ¾I#ƒXµê [ûª¬:_±]‹v¡šôĪ?[Ƶ-U0 ¢W`Å–¤GïºÔ»£j¯Ø½þ’‚‚9ãÈÚ 3gü{sÐ5q³ÿK/Ý’ºuݺi=GÍß½fXÌK)’»sxÞ„icï]›“÷¦ÃK)òEŸ‘A®_—‡ÁQ,š&J¾WÄh$&Sèo¦)š¡»¾UDÜÉ0´b§ ~AÅ”ü‘(yfAê‰O•ê(„±ÝîVÖy f÷¨·F¾¸x÷éû#ÛZ7|0¶Ò'skêÿt·g¤ö}½Çˆ¤)#/¨_Ú¬ò¬?°o—Ù‹«íú.ˆŠŠÚ¿¿ýO?iŸu\!¤?CNNž½+òT'Ô­ÛÄŽƒ¡‘_.%馣›î©·-½zÒ«f¬Þ¾ï‹™¿w[¯ôÍÈãèR‚<0«’Ã$ßÖ8P©ˆš!Ôý¡³­ÏöjRD«a‰µ]læå‰t |†‘Çû-|98ÛúbÅ"¶´MÖ‡~c¢mi_QzÊ">ãübÊ`Ç`æ ró¬¶É¬µV¯“Óã\È?øa¿…62ç©XƒZjpYóò 8é—˨ô½Š’VäX(AOsy&Nk0°4±ä›ä;)µN¯em£Ú Öüü+O©´\ÁãïÁš››/PŒF­²˜Í‚(•ç ½?ê½`5I‡mãK-žý»x½QA¿h­È[¤ò"¡U½^[¸1o)È3ÉoVé†Òœ¦yªÈȺ«Wÿø ø¯Š»{'CŰöüX‰““Svv¶?ÅÕ¤z¿pVc×Jü)šÎ»tªÕ’ƒ‡2=öNl|vÿ±‘GÄã3Û†«¬?ï8ðþ® )ÙœJïÐ9ºÞœvÁžÏ2Á.ŸûÙò-ÿ9‘šÁ3¡aaKûE5p¦¥—#ådRÏÕ¿ŸÏyž÷«¾~pTum:/ÑšhôŽ ÉF GhO_¿5ƒcš–<Ë\Û·'bù9­wùCÛöó¦ËÐ0öû 0niZ{^Ô[Mti «Žx¬Ü¾¶E`ñ¼Ä”š4kâêNürüûI'Ž¬Ï®™ÜæMLm÷¤í»ýzÎÿꃘ¼SÛ÷é¼ÕgÐŒúì’UG&mŽoÍmì6ê[¿‘Lúé3¦Èï×O  3¾ŸøÎôæèÆaÎæ×4Rõá™fø»çæi1þGzäøáƤ};w'8Ç~øMo¹*YGÆö{Õ)¢’sþäIç.×F~Ù‡FöyV]­f°‡vÚXøl½þù»=צUŠemÝß|ÒªOºÕs/ÔwÈ-¯poÖøãq·ø„Y>LúÒð_åïï?uÚT{מ *×JaÒ’ôõ¯WšÇÝË`hj×öí~Q%|Ü·Yy ýòÓj™ÓüØÛ•Ù¿,ÐB7‰n4©›./ãÖØ%;šw|\LM’ce† ìÒ'Ü•d_¯1tK¯ÕžÇúWTqüæ~ŽÝK6~Ô=6ØäçÎû~ÇÍlŽÜf[ËÙ|Æ(´oÓ|I×0½`-C©LìÌLШūº·ò%äL"[sÛ/)Í«?©õ­Ñ¯mðä“ÔG_έ£!·~[1jƹå·O·q%yÍ{8tÐñ­«qµãºµõÞ•Ñgt˜VIºòYŸÕdè½eñ„öLæñè:uÞYÿÞJ÷o‡N?¶0õZw©TqÚÚ™~y¯êcÕàZ»¾Dû¼Ÿ²aJ½n³/ë]ŸÖôî´úfÄcsÊ’s|Ehã7&´ËŸ!¬ìÕùû mŽ^¤"Y¿Yó}"E‘s+g}ðöhþ²Z4¹Ö0`ò„î~ð8ûͲËåSÖ|î®âÞþõ¬ΔÀ߆Àåšyžè{¶ŽhëÎö¬Åkhã‡ÛR›Çuhæ*}hš€©±åk­HNŠ«Tç/“Y«¯íëìe ‰Kå•}¯f_‰¿ž]3̹vDÕHyê:8;WÑÐgÆBëÍwæmOkÒ-.Ö‡%¹&©uܽm#3ûà %&l;´B²â5_5¯àÞ7Oa¿`vŒèñÇ…ó—tšt‘¡8ÎòÈó¶QöÄÂC×Sniœ襤ÜaU¬[`¼zé t‘Ñ;¹º8»6iBò7¯¸È´×«ÏŸ;§2g:é4‡v¤ OȪÐ;Χ°È'ß%'HG¢½žU³ÄËÃÛ@²syé…=µ0þ®WÏ7ÊV¶jõrÖÕ_Ÿ˜±@µ8á¶_÷>A¶ß_X?>q‚÷i¤¾q)Ŭ*p "\¸“Éû¹WQŸ˜3ì³vŸôhÚ¨†î…¦1P2yr‘è´j’—y€#=\ÔE×zÁÙ«ENÏëÏœCLiGó9B·Z¾¼wˆ;qÔ¿ýæŒÙ_L¬ññù޶®›Tâl=Àß‹­%U²C¹m¼' fþ,å° Íç¦vž»¿Å;]65,GT9~—\X¬øXǸ}_) ÉY~ÌÒ½¶ûœE?7žúVwO±lÇ&v fÓ•Ï,±¾ubd¯B.-gGÿùw³¯¼í{÷­ð'¹Vp%T•¨6}û†wÒZ¿Ó“¤§æòÜ=»¼½iUjÖí¢EsN†¥À+Ä›°fWšÉ¾FH؃iÆQïBTº¼Ýûáï…Lä#Wu|/÷jB›º1ƒ¶ÝÕÙóy+PˆѺÕ!ä|ÎýSÇ•g¼+·ú9z˜Ó´õvjÑ…9ò3¯œ%ΟV0È“È ÷û‹"cp­AH|šyj¨ãc bÑD§öiÐ6ßsKÒΠ˵ÓÀ»¬%³‚™’_ÄÜ›×8RÅtóÒ] ]‰e¥4eå™{éÂV¥JM‹–œÜkçOÖwþ¤xqú£õðŽ˜òcÖ”û‹m8úQñsš CþµuÈcU§ÜêÌÙvnÎýÅe+‹MX7á¡-uÕ†¹ôxeœ j}—Ž ^ZØVøÂε¬ÎixÏvÙÂÑ·D¹7µ _®[/ò‡u‹N–J+½j­«]ûÁ"©±íƒ"ŸS´í%ri¢(µÉÿѯë?ÒEEu¨¹´p‚ )² .#ztÑûþÛTÕíÓBåëÚR œ•„DÞ^VO¾"ÍÛ-);4ò”¢H¸'¥ŠOÊéá•â#ÛØ‚ó‰Ñ)ïxSì©Gª$ë#”¥3ØÅÌ ‚`P3€‚ ˜Á   fA0(‚^­«W¯Å_ÉÉ%L«†aa:’~ýúöKÙF«Ø°aõzÖÉ×ïäZ=‡G¸çñ¢MSon½hLÏcÞlêO•É;˜ !˜àÕòñõ>|¢Å/ÆeáaZÞÕË3ôôùú›.M©^³žS5Àsíîø/4Ã#]È£3?3AtpusÊ;ß{cj–áþ*á9fÎP3¼Z½®œ^%5j §òQkuÞVEl‹"åì¨wÑÐ õg“ÿ> •Fã¡WËÓb”ºÂö…`€"% ZEºp¼jyxj‹|&šÒ¨ SŽ¢­±Þ?­f‰Š.*­äúâ§ ÎsÄÌ=xJ”ç®"F•ÓlR†ÒÁ /€"–œYß&|úÛ-#/jµºæ-Ûl-G ¦/Ú29éžE¬—©}cF„:ÿu(R´ÚtkôÌß'eZtN}Û6ø¼u #%FøiÃÎ÷ö]»k ˆ¶ÏëMæ¶ò׋rúƺiÓÞ±ñWþ(à)•¦FÝúûTyP K_Ý—Ðì»d‹»ïôaíú{ÓehRf³²Ð4­~lÆhe‘Z·ù·â&n9U©Þù…qn:1ýàžŠ»s ãMË•{†m³»ÖvUùySøÜý­æ½Ví¯¢F*¯ Kè4¨Õ˜·©SI'»­ÞqÑÒvoGBqgn3' èê¯%I{©é{"» ®d |ÎøYÿ™nô?2m`=/–»œ±ò.G•h‹|«®]'réÀ(7bA‹^œ^¯¿wïž½kð§haç–Ûòôßu®í&ZHU.¼æ—œ(Ÿgæ “‡·Ô9Q„üƒÕâÑ+±šá/ÊAUηY°«”¡~-›}}7­ËOg’;†vØ€z±¤RP0I¼™k"ÄpñÔù¹¹!cbê9‰$׬òôý¬­£J,êïESääþ£SÓÜõ­"WO,K©LÌJãíím4í] €?%šºš¯f]ª»3òd‹Rô1N}" áäKÍ{÷šx0-Ÿbk¾…<ë)@Q”ç`¶Í&I*øûñRb¾ê(žK<1v×¥fJÏX.–±m{6ݘK ­üYùÒ²„R¿î.ïk[H»š<ø›Ä®ÿ׫œêIsP*‚YYÜÝÝSSS“““CCCí]€§E«Ü ¥ä^YÅ]»¤ ¿áÛ­]ˆ;>ëÖºœ:ëR¢×¤c/Rþýîù#Gë,Mž=¾×˜ªÄ|­â€-…s2óòDË”†)Y¥¢=µ${Î×k‡8ΜóÏN-ø2wß‚Yq/^1ªyÅ^š•CN\ì·øšÙÊ¥gMA‘?u ""Ò¬¯^zô†@r¾M¸T;&8 ZPùi㾊7ÇFŽm`ÊÌÕf³9;;/]º´AƒóçÏoÛ¶­½«ðŽjØ$ê·u9Áv[1E©šµòuRæFp¢|ó'ËP3:5lÍÜ·«&%-E± C9Vœ1,xš(/ªäB¨Ø6-;´øûE± ­²µ­]|vÌ BZ˪èi-¥’)–¥G*V ßT5Ô*ˆ…û0ÒáhqÁ°Îó¥biZ#rÄ7삪¢­&겓ÊÁ¬X.\ˆ‹‹;xð`ÿþý¨2?ÌüÍHaɰ­T³ªâ®Ø {?eJvÔ¢h ýð>¥V1äI:D‰’å"e£-YšÖÒ¦Œ@0+Úºuëâãã.\xùòåÀÀÀZµjõèÑÃÞ•€WÁ¬hÒ·ÁÖ6&“éðá÷oß¶wàÕB0— Z­6::ÚÞµ€WÁ   fA0(‚@AÌ ‚`P3€‚ ˜Á „¡(Â2cò³´ÊÞ#l#˜þשYÕ…,ë¤Ïئw²ZžBŠ1[9;ÖAJåÃWÃô:{ÖÁŽÇ%ð¯PaÚ¼ÅÙ¹ùö­MS4Ípœ=ƒYÒš¡k׊°cÌÿ놩Y£†½kEÌ ‚`P3€‚ ˜Á   fA0(‚@AÌ ‚`P3€‚ ˜Á   fA0(‚@AÌ ‚`P3€‚ ˜Á   fA0(‚@AÌ ‚`P3€‚ ˜^&NWšÝ_Z0s#ÂË*ž—ôh6›Kù¡¥”——'Šâ ï^ª`.y`éñž={JSÀßÏó†t©‚933³4»üQ%¥²Ñh|®½JÌ®®®Ò¿4Mã 6À9;;?×ö¥ f???«Õj0¤ï¥) Jçù‚‚{Wä¿G)¥†«··÷síXª`5jTÏž=M&“V«-M9ðRˆ¢XØù«4O ô¤ÆªÉR0³,[¾|ùçÚ·TÁìhSš $ÜÇ   fA0(‚@AÌ ‚`P3€‚ ˜Á   fA0(‚@Aþ.Qo‚R}ÐnIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/create-an-xpo-profile.png0000664000175000017500000006466314770023131025237 0ustar vladimirvladimir‰PNG  IHDRí_Ý<OizIDATxœì½ X×½ï?=Émr!’*šhŒ ÒÀƒb¥_rÀ¤c üAM,©Tb$mŒ$r±jìQh0 ©5ñ…?Ô4‚ñ@ÒǬ†è•HÁÁ¼hhâ+xšž´½_ö¯™;ÏìÍfÃöfôûy|æY{ÍšµÖ~ÖoÖÌžuó§Ÿ~º}ûvåºÆÛÛû©§žrw/ú#õõõKÒÓÝÝ‹¾eÔÝ£ ÜÝ â~þóçŽÕsw/ú–Ÿ/ûùÍø»Uÿõ£û¦º»3}HvvæÃ?|×]w¹»#ýŽ·ÞzkØwÅ=–èîŽô!ÑQôø Ëúõÿ±»¼ÊݽèCöí­Ú¹sçÍHAâiKžswú⢭îîBÿeĈ»î|Ÿ»{AH_q}ÿyŸ=óÙ±#_ÞìînBq zœBÌ =N!æ†'„sÓKç¿ò+]NêÓÏÚ,yíZû€ž½kźщ¡“®ï»„ÒSzéñ⢭'?<ðý{Ô›é+©}£ìí^öΪQåz¿ûL!=¥÷ó*S§Ý¯4âî››ƒ‚Ç#ýñÇÍ×ÚÛ‡ø­(kÈ_|5j òê?àé)i)2çÚZG#Çj Hylµ9„Bt85?.žqG{ÇÂOLþÑÔŸ-J{sÕKˆîg?ò>"óÀþ½Ù/çÍyüI'O•B®Kzïqèâ–4D ó.Y¶"%yî¡?€|Ÿq•b™ Aº`óö”ä9H ~ïÐÁ訸^\Ÿ“<~<œ}#}ïäû¦MžP]ùj‹™5û¾iÿ†HB/}ó zœBlbؼ ˆ™·ýõÍð;„®+|¢±[Ø[Äà÷ÿ—$ÔÉn5 ÝKΕ+W—=óBrm&!„F>wˆX‡‘as8]»KDŒÀ\±ÌÆ·~âE Êd­|¾`ó6ÔƒxÜÀNCÀ/hˆïÐÁߎ¯ò;í»æäŽKŸ6AH/¹eõ/¨Øºþ~^ï=ŽHY5ìíßûÞ¯^ùͲ%‹bfÍþÉ“ ££"^úÅÊç_\%SÞÏ¥/F~ú3‹ùÉ'§›ÿ¡»'Xxv>ªˆqáDC½Ì’÷ºŸ¤/)µ·« à‰?åði÷VïϦgeÀîê±TG¨z·"ñ±XĸÎÓÅ„¸ üÙ«Ë@þ8—=Óù^Õ7ÊÞNIžÑí?ä¢W-öÒã ‰ót9õ|€Ì¸øDÄhÙ/ç]¹rõÚµö…‹ÒN~ø¡—×À9?é9Ðû‹ÏÏŽ™¸ú¥l”™:I=V›†ñË€†`åï¸sxøŒ=R+jK÷ròÃY/®øeöF;eäº*sÕKÎ5ô!$.c†3õb ™/t¾[PBÒ…?}‘üÓgŸþ™b‰_Þ©x«ð7¿~2婞O‡ÄQ¾½ýJÖÊPöôûáìÉ?šŠèåQ—/]Ä› _sãÉ¢cù YˆKR’ç" Á„…‹ÒÜxÿ†'½Ú]¶üÅ計­¯m–œC`+“`òqÞÉRrÀO(;!qÞà!¾¸ä’«.É‘‹­My¹Õ•ïÀãHã(”2ÀáŠæV!ýQ£Æ¼Qö6® ÷í­ÆŸ.‘qãà®ÎÐã¤÷@ÁOüdÁk¿+ÔfŠ‹±àéy­½]»K"ëk×Úë?ø@ô-9„˜ŽÙ<41tÒó/®B„ðýï'>ëÆ©•Þ{<%yÎÝw–YÎ?nÆ%Æú—ÝÕ7êWóiî«/¿ÄÅ‹:áNúKŸË8ô§'?<ôƒEÂéÇþÏûž½sÖ¯]²lÅ}S¦áªóƒº£ï:xO`PEù[÷#NÇ!({{ò¦JΟŸ=°oÁæmî>Båöï}ïåœÿP,—›ÅE[ñwn}ɨ}ººOoïõÞã¸æ-}óIa“!V„]ø¨ ¾„óçÚ}ä!ùÂg¯ÛÂC®ÁÝxåB´@Ê^^‘<ľÞþúfDßøKÈ\õþ*ËÃEr§%1ŸúèDÁæíÏ¥/FIdf®Z‹™½ñ¥_¬”œì—óPãýÔi÷ËS§¨Çj„ôð÷|Ûm>GÔž;׆¿Ïå/d!sŒßXÙ+‰êÊwä#¤ß§qv^eS^®.@†¸w–!!Ï "`M"2\J—¾¹Á—ºW±|{HfT%ݯm~5rftŞݒyº¹ ‡Kåƒù yÿ@¾Ò%À¿êÃE­u¶¶¤õCŠr=§"sŽ’V§ÑÕ!ý ë¿g5Çþó¸†ã”ÇcfÍFð…`Y¦D•o£oŒN©Àµó§Ÿ|¬X‚wDdðuÖÊçŸøÉ‚æ¦0Rá,´ž’<!˜by:íõ¢7Ô£ öb@ÃÕ7h‡Ë`àôùBÈõ†S2ñî»G[憾/9‰K6mò|œ÷Dòk¿+ÄU6B*ä@â©Î[ž»D™œõk%Gʯ[¹ø™eJg–ŒOFÆW®\E+ÚÈŽBˆàì¼ ®‹¡Ú¬•ÿ|1BfÎò>¬!C|%×2òîQŠezD½—)¯~ Êß¿svI8#„bž;”×ÕJZžùýÕ+¿0À3ÿ•_EÎŒÖÝü”¹‘ªw+6åå®ù×p·ä\»Ö~î\ÛäMu¾?ÄÅ|üq³ÜÌàª{ÄÔ¨÷ê ®®ž¯ÛþúoË=ëD/Ú2ê!<ŽÈºâ?Ëä)…'’Z\´õ¡ˆê?ËÕ÷È7³aüœõk§Mž X¢oü¤.JC,¯æ,}.ý_o%=EîpÈz Y+ŸfÉÏu·. 1Ï¥/~íw…ê_²¼lÕº˜ˆÖ¶Nô¨9HVt¿Ç7¾6jÌ?‡¬‚ÍÛ?³LŸÜè˜î]Žr÷…¤ß7eš˜ZöF<9nü´9ƒ‡øªoÎè wPÕéol}³ú&züO¨(KõxWËø)–¤ä¥WÚ÷|vUžßä$} ÂH\]w,%yŽúòmyg²ÒÝ7ŠÏŸkÃߪîeÎØZçX׃|ùá`[ÖôÞ㺖´u»´W(8%ݳƒÖ9êá81I« Òßã7ÿ`ðÉ÷MÅå”ü*ñçhg?5 [ð³§$ÑUy¾‹ô5‡À›SḚ̈byïÛË9ÿHåÀþ½1³fwõ¨Å9Ësz’FØêéáù¨fÕ뜷«Hñʼÿ¯ó/‘Pþ+¿Âß<Ú’7u÷hyz~/Ÿ8…èøÐŸÈ5©¼¶pÝêLûËøÉ<›:±hÙ?7¹hnúHûQ}Cò=AÁE¿ßñ@$"ü‘Û©AÔüPÄTüñcHHœ÷DòO;Ú; ïŠ=»å‰)óìÓ?«ÿàŸ+# kCôŠåKCø¤¾u®GÐãÄ)p9™ù‹µ²šÄÖ×6#xI˜ó¸ýeüââáq\É–¾ù†ÜÙîvÙ?Búy ­ú”ˆ qq9p ç¦¼\õy¼®@"ꗇ%ЖWV`œPËHPôH­ì¾¾W±sOÎY¿6|Ú½øËW/X„'NpCþæ‚‚ÇO¾o*¢òkííö—ñÃ{ƒ¶¿¾ÿs–-Qéù²„‹¼`ˆ2µ‚˜ZVóÁåS}±¾MÔ1@¾£¿^yƒ‡§‡<ôqB‚ÅriÖìxÅ©àŠsÙ’E²‹¿ÿXr®­uwé!žw|2ÙHËÓ„Ú…dYys´$ï~\8¢ÿƒ_4þæä²¿ú©Óîǯ¸Ûeüdñ?–ˆ»§Ëþb,²F<þasü#RVßÚ†À.²#qÅtãjò¶Û|È\µ¶å‹Ï‘ømÁ¯/]º$A÷c I‰ÅÊ'š+–7†.øÙS…¿ù5®6¾¼^Þ"…£:ßiî;ÔñþæqYDñ‰Ÿ,X¸(íoWàÿ¶º,€by>ýSŸoéY&†ïÿ`´¾'(øDC=þø cõ(v–ñS,¾™«^R3»-OH_#¯ 9tðþö&K„<îå5ðõ¢7*öì¾v­]žT¾]Rø'À2™.ùW®\ Ÿñãàqã†Ýq'2w—WÉÃéb´†ò}õÓMázH¼{ÔÏ¥õè]R†y‘8Æ™ÓA§ÑõËú ­­­ºòËÓõž¶#S^B+ï¼óx²¼÷™£ýÆÊ U`s~¿ÿ£}O–6SûQ»tŸ ¤»-OH_cý—¬ý;”¿Xõ™ë„5Óæê•6kîª*G0ÌãºUí¥gò¼$P_lûÞ¡ƒÛ‹aˆCc”§§'v)ßNóg­\!wu1* :3ª{„r½âêûœòl|©gê´ûñ1:æQ¤—-£.LŠ‹¶à‰!çU!¤[ ó¸˜Wý˜’<Ñ´õ—0?ùä4¶ûöVc>ãDzЮ¹N!Äq óøœÇ“S’ç>—¾xÞÉÐté›oØœA ~¢±~â;ô…çÓKH²Y›<Î) ÕCB¹.1Ìã1³âdVD¾×÷Ì’Ÿ§>ý,D¬~[sòÀþ½ <¡XVüŠx R[FEtbüN6!„ØÇÈùqYÓK»¤2¢iUÄjB÷жŒº*—ò"„1þ>gWïí%„Òð{ù„bnèqB17ô8!„˜zœBÌ =N!æÆ ÿCÕ‘ÀÑwÜ3ê×7MÜûä:æó¶‹‡ÿÜü@X—Ç¿º²ÝNÿé ½õŠŒåègÿýÎGíÿë_¾“:ÅÇã»ÿâšF/]¼äš†ÌÈuÿÛ'78¹9½Y)­wüæàWþûïî¸52p kZløóñ!ß»ýæ§žzÊ5í 8Ilÿçïÿ¸ö?Ê/] lܘ{×]w¹¦-sq#üö·oç«o\Þzë­ãÇ»¦­K×þ&áí_ÿÃãVýyO ýÁœ9snööö^±¢›Õç äÓ—ŠÞ?s‰ysçL ñsY»Ä&üí“ë›™\ÓÖg­ÖWg"1êî‘+V<íšFÞç$„sCBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8!„˜zœBÌ =N!æ†'„sCBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8!„˜zœBÌ =N!æ†'„sCBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8!„˜zœBÌ =N!æ†'„sCBˆ¹¡Ç !ÄÜ\ÿojjòóó“­±Õ¶··{zz[-!„ô”ëÜãuuuéééUUU/¼ðÂòåˇ ¶wïÞøøx'«-))YµjÕôéÓ/\¸€jCBB é-!„ô{¼­­mãÆkÖ¬q¤pqq±bÑú™3gœo•ÌŸ?éÒ¥]ÈÈȈŒŒœ2eŠómBˆ}ú…Ç|||öíÛ·cǎ“'O"sðàÁ"_8ñðáÃH„……‰µkjjòòòΟ?¿`ÁåÛ å‘“è233%GmóºuëêëëGŒ¡ É\£QV¾Õ= c‹cábÈZº‡ÐÇ®\¹»¶lÙ‚-êÙµk—ÄãÒ\kk+ΖÏÉÉA¤—þ4 !7ýÂãÐ_xxx~~>Œ ›744 355zõöönnn®ªª’˜ô™gž X½zµ:1 ‰gee!øE„Žzp8*BG д4-œ[YY©•8YµjUmm­‡‡Ç† ÊËË÷ìÙƒJd„€cbb¤{ÈÁ^T‹ÎHPzP¿t i }Ãé ¨Döºö‡J¹QèÆã-?ýôSÛ;¶ïå‹/ß`]þ¤I“$1}útI@|ŸC-HŽ˜GÀ^ZZ:wî\___ä †¸¥LcccSSrdΕTWW[wÊ–ÄŒ3† ‰#-3L­Ö†h ©Ý‹ŠŠÚºu«umèô ýAmj>. Ð:âôÀÀÀžý˜!Äaºñøœ9sV¼˜e`{Üôyãá/:†t[rôññÑæ ÚõôôTãÜÄÄD8]>B¾j1g Á€íÕ«We„¸xñâÈ‘#øݵk×¼yó¼¼¼/\¸» î‚‚Ñâ x‘†ôãããëêê’’’SRR"óê¹púôiåÛÐ["~OMMÅ6,,,''ú`µÌÌØa»‚’þ#ç´8!Ä5ô‹ùqÄ­´%=tèд´4$BCC£¢¢‹²“““%¡‹måâââ„„¹WYVV¦æ`$@%¨\mbذa.\€¯%Š˯[·‰1cÆÈ,vFF†Üê\¿~=Fm÷䣋¶çP6p‚æ0x„Xèß!„hè×ZUùö¡•x ökÐb£6a]ÎÕeêžeÔvOÒêMKù(OÂæêD?!„¸Ã<ž’,Oˆ›0ÆãÏ¥/.}óÅÏ,s²ëÐØøùùÉ“‘7(ðøÔ©ÊðáʬYJ\…Nˆ‹qÖãÜüxâlCºBÌÍÙ³ÊÆÿ(tB\‹ñø‚Ÿ=5á? Ÿv¯óU‘ë ×â¬ÇGƒ õ8~È¿(ÿøaÇWÞÇŽ*í­N¶Númm¶ó­…NéÜð¼Š×ßþºê“?ÿdŸë›&îá[¡¿8|ô1Ÿ{ß÷¸ÝÝ"äºÂ ¿rÓw—Ü=}óÊ…Áþwº¾ub<ˆÇgÛ½Gòm<þ‹Ÿ½ÿÖ!Wu‹7xüïÊwŽzÜ~yÂD%„_X¿.øì3ÛùÖóãŠ\Ö)BnŒñxPðøêýïakHmÄÜðö&!®Å°xÜy‰÷né5íújÈ‘—º4í–vûÔ7!n¢_|/_±¼¿ÐÛÛ»§×­¯VYY©X½Ë@ÚÚÚºZcɲeËt/¸QðöV ¾ qýÅã0r/¾©[_M<Þw´´´tµ,œ¼ç¶¦¦æF\Ë ¿Ïš~C¿ð8‚ñÈÈHÅòþXݺn’³oß>Ý ,P×oËÊÊúüóÏuë«©ªKµ¡¤nxЭ§.)W]]½qãFÙ¥¾Ö\]à [Ô£]NÝ…^I„ž˜˜XTTt#zœâVú…ÇáPÕºuÝ&Mš„Yw-""BVkSß3®[_MÐ.Õ¡C¸ê;[ðQ»JJªKÊév¡ÂÀÀÀ1cÆÈ»Ñ4U—…CÐÐP¹ š8q¢ŸŸ ®.$D!.£{çæüÒØ&ÛZÎ(Þ¾ÚíRºuÝàqYw­Íò¥Ay¯7¶È„XmÖßÒÒ‚mtt´|lmýß…÷u«Ä)߮ن]ˆµák9äÂ… òrÚ 69rD[‰bY«TTTÈGŒC2³¯+F!. oß¾ÝØõ9K÷}ðÅMw)¾Ý–Ô­ëæë뫳¤º¶ƒ5°¼v¥7›È*qÚœ˜˜˜¸o¿;Ž]29“••5wîÜÄÄDÝá°¼¬Püýý»;Bé+ºñxRR’±í}ú¢÷¯ê¿Ñ7xðàöövIëÖuÓƒeµ¶òòr¥ëu8Õµx€ˆ,«ó*aaaºUâÔ£d׊+ûËoW®\Aæ”)SÐ !òt£, ‰~¢'üeµ9Ó.²L!®¡_ÌÇÆÆVTTÈBݺnMMMjØ[TT„]ð;Ò¢`ÝújêGYªíüùóÈ×¾ÐMèV‰S×lSwá(hixÍÉÐùóç766bTS—…ËÈȈˆˆÀ”žž.#ÊÞ½{á}WýÌ!äŸô ënTjÍ EªÙÒu«LèÖWS#t;ëQèV‰Ó>ñ­ÛeÝœ¶oºµß˜ÜÜÜ.N‘BúŠ~áq€¨¶¤¤ѱ×uS,OLâBž"'„WÒ_<% 3®ë¦X®ÔyyBq%ýÅã„Bz=N!æ†'„sCBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8!„˜zœÜˆlذ¡¢¢"==}Ïž=ùùù©©©K–,éÅò°Zšššrrr´µ9³j«¼7_ÞW¡¾B±¼¿«®Ê æz×15ô8¹Ù²eËŽ;üýýÏž=«X^’¬¾q³× YÊ*$$dذauuu•••Ö«Å:(÷úúzluùvºš––fóëÐôûu=NÜ ¤¶iÓ&‘]MMÍ•+W¼¼¼†ZZZŠœ¹sçª ŸÊ’{ .´)AÐ%¯ë“BdÛ¶mS,¯˜G$‹`\±¬æjýâxµ!;«u£æ3gΠŸj˜Œ¨=1b„¶ØÎ;›››µK¶Êíå]lIIIr"ÈAj–j«Õ‚`g§¾ÑSÛU¸>** õ@ýØâpÝYdeeÁþê„äúƒ'î.»xñbFFÆâÅ‹333‹ŠŠ 3™ñ¸zõjxxxCCCyyy¶…ÚÚÚèèh›ëqC[º£dÖÐÐPH<11»Pì‘G)++›1câqlO:·ªÞT‚õ"""l6„®¢Ã8š–7c«X¦>¤ ÅâëI“&7é & bÒÈÈHœ Æ $êè%@ùõë×ßqÇëÖ­S4)¢cŒ7è9 à꡺ºZ±h?.t@JJÊØ±ceå,œ»õY “èÃÆñ3 Úáùu=NÜÌš5k ›ð”ø¾“÷¦íÛ·¡¥XIÞDVWW'k‰X×£; ‰åË—ãHSÝË˶b™ýÐ- ˆV¤fyå½6”VA?åÝ÷qqq²ÊŠº¢,ò¡Tµ$*AØŽJ^j/§ »d…)x\.ƒÑE–"3f B{]£ÛæÏŸ/§ 6l˜ :ô ›£rŒ[¨}°> ”ÄÏiØРAv®6ˆ¡Ç‰ûA?ªK³"R–ÄèÑ£¶¶¶ªDŽ,®mî(5ÿÈ‘#êÂRP›öM÷Ö”Xtcc£µÇºš6 ªùp¨Í·.C ²°‰LnÀª(‰cwíÚµhÑ"”Áè‚ðYW—µ‰ì…¸ÓÒÒd¥ëUa­ÏB¦tP?® lž1†oþúÝ?ïøã¡Žïä-6°ÖÛo¿½¹¹ÙÛÛ»«ô8q3Š¡]ě˖-“HyçÎ" „±Ø–——K4 )‹ø¬±>Jò!¾ƒÊá•••vÖl‚æ`|))ÓÖ6‹íÞ½[¦¶éËs#rë^Ö®®eŠ™`¹|ù2l w£<òñ€Äe'##Ãúp///u™\ü0b!BGä.w5eqp;g‘€Â¿wuîÄþöµ÷mƒþüÅWÆÖ:946ô8鿤§§KÝH°,·+/^¼%AÍØ›’’‚¸òôéÓ">›õèŽRó.\-~D™—°ÉêÕ«¥!T‚¡ÅæýÆ•+W¢¶ÈÈHY©U›Ãê oÙ²E{ßUןBT¾jÕ*Œ^Še’15F´‹°ËºQÔYË)ÈP1bÄ T8DºQ[[‹S®¯¯Gë³0é;ý‰ãÐãÄÍÈ*ÕŠeþAžÊ€Ýd’Dt qC¾---ŠfmktG©Õ"RF¨+Sáêj|;vì’2Ù­FÐzêÔ)OOOëuûøwâĉ}ô‘<ú¢æ´··/]ºT¦YÔÚÊÊÊ윸̉+¿Ë ¢]äH%ÒCYÈ) €,ŽžK£dYÅ2uŽJPC·gA®3èqâf´_iÑ®°ª-ãkAÒ”ͪ´Gé¾)£«Pý(ÅÔÂÝ6„ý,Ø<]mö¿X¤Ý«mWò¥‡Ú‡,u§`]¹Z‰¶6Ò?9® ÛÁCŒù5Ñã¤agþZ8vì˜ö6¦ƒGõ› qõ¾_ÿZ9~\yøaeæLc*LIžs¢±ó§!C|ß({Ûù éqÒ¿èöi ¹gèšžðÑ._V ;ÿÝ~»òè£Î }ûë¿…Ä×çä!½lÉ¢üW~•úô³Nö'„‡øê+„>+.aÂ~<ÞÀŽÑã„Ò3œú€ž"ñçÒ;Ÿ1‹Ot¾?ô8q-Ís”¿Þ¾$õÎAÝÝBãã»Ü¥úîAøk¿+,ؼÍ[ô8!„¸Uâ1³â ©'®eÌvlròÓ¦„8õ²oB\ÆÚµŠ­¯Ùv¢Wù¬U)ú÷îk+}s'$ðý{¶¿¾ÿbfÍžóø“Nö'„ž1`€2{¶×›ûœ'ê§N»ßØþÐã„â¢ïèèN}ßrK/+yþÅU†vªzœBìqë­Ê¼yÎê»O¡Ç !ÄK–¸»ÝABˆ¹¡Ç !ÄÜÐã„bnèqB1ˆ›n¹|é¢ï [­õöÛo·³BBˆaÜüÝ¿†ÌpâØw^yڥͺ²1B!†CBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8!„˜zœBÌ =N!æ†'„sCBˆ¹¡Ç !ÄÜÐã„bnèqr#’P__¿råÊ]»vããòåËCBB9PWÒñcµ544lذé¥K—–——GEEõô,´@bݺu85_­Üúgš#ýzœÜˆ@âÐ('NÄöüùó¸zõj???mŽãÇZ³páBIlݺÕZ¬555S¦Lé¶éôÍÁvm6'-úûûûúú:Xé'ÐãÄttt$''çææÂˆ½¼¼éíí0‰yóæ‰n°ëÈ‘#H,Z´È¦Ú uG¥¦¦Bm%%%EEEG…¹°+<<|Á‚`±…ørrròóóÕzÔ†º ±Q~É’%P9Z¬®®¶vÖµedd477ûøøH;v`[WW‡q–ž´µµmܸñðáÑ‘‘ŸþùåË—Ñgõ1ð¼ð ŠEßÙÙÙj'›ššä\àâ¼¼<ä •Ñ£G+–è[N `GCjsê®ØØØøøx$ÒÒÒ´‰) Ç‰;ñðð€[Ả¢¢ªª îƒ|wïÞÝÞÞŽ]ÐÖ¶mÛNŸ> ÁwIII°§uÀxåÊÝQ'OžD>LwêÔ)l± £££‡Žª 2©PŠ hZ‚I1X7„òh®”æP9ºdóÔ¬kÉ\¼x98vß¾}ÒmláS *ØÂÅâhhtÍš5ŠÅé8V<.c>feea0ÃÇôôtüĤ9ôJÎ%%%ÃÂjœ¬x|Ù²eµµµøQcA¯Ôæpò“Á.ä 8ãj–!ÛÓ2¤¿A7IÁ§°OYY™ä ô°³wï^ø½  @±L ÅÄk:tGIÍ8ò’]È«v55Œ†dZ¹µµ•t“J°Œ:QÿôéÓ¬ 9ð¦t–´>ä£>ƒþßÿý’# Ī~„¸ñ >~ü¸u øI:T‚tœþ™3ghhh —¡¡¡Ú³58Å‚¤1¨ùÁÁÁÒ.‚ëÖÖVEÚ##NP øøøà‡)s,Åã¹ïª6§ž…ÒÅŠÄÐãÄÈ„¯¤% D`¨»½¦-cÝQZ'jŸH$¤…㤘Z¸Û†Ô’ÖuZc]›.Gí°ývµmiÏK~bjŽ$$œw°ŽœéÿÐãÄdÈ„ƒ»²´vtt¨w U.\(7W ±¦ôÍØÆÌŠ3¤6zœô/Ô¯ÆtÅ„ <==u™þþþ†÷ÄfC6¯­§È)ñ놵k•ÒRåñÇ•‡VîºË€ §Mž ‰í¯o~£ìmç+¤ÇIÿ¢[ýÉÓ~®é‰ã ¹¦KÄ]¼ÿ~ç¿E‹”þÐY¡W½[1dˆïÖÿÿC$>ÛPÿAPðx'»GBˆ£8/ôˆ"ñïÚµö?¾]ðý{œ—¸BBH/°ºòÝþÚæWýéÀ=A†t†'®åÏéÊÕ»§Npw71UèãC¼”¯~¤(Ý?[RŸ~ö‰äŸ>1õ¥_¬|þÅUNö'®ep­rÛ‡?}dÒÝÃ|ÜÝBbï^åÝî¾ÙóàƒÊŒ‡þúÁ–ãÊ—Ý”¬z·â÷Å; 6o0 ó.ú¥K—œï!=N\‹ïlæ%ûM ¡Ç‰ièÊãÐ÷c)3g*¾¾Êg­ùùöön«òôô*}ó¯¾ìôýÉO¬ÏÉs¾{ô8!„ô ­¾{ʽ“ïÛ]^U]ùÒË–¿ˆÎ÷‡'„‡pFßZànCô­BBˆ=”ßþÖ}÷ô8!„ØãÑGÝ݃î Ç !ÄÜÐã„bnèqB1Œoþúõ{‡[ç×±·ˆ„BBˆaÜ<à›¾O?»Äÿ®!Öú£ýÈþ{ô8!„ÄM7]~ÿä‰cßyåiW6KBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8!„˜zœBÌ =N!æ†'„sCBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8¹IHH¨¯¯_¹rå®]»Š‹‹ñqùòå!!!ÎÔYWW·nÝ:mm555S¦L1ªÏ6lÀvéÒ¥FUH®èqr#‰744 1qâDlÏŸ?oHµRÏêÕ«ýüü õÇë<ÞÖÖvõêUì5¤9¡¤¤$>>ÞÀ ‰é Ç‰;éèèHNNÎÍÍõõõE¼éåå…Looo„ÉHÌ›7/**J±„¢GŽAbÑ¢E6#ÜÂÂBÝQ©©©ˆˆá¸¢¢¢£Gnݺ»ÂÃÃ,X€x¹±±[7'''??_­GmÈNxŽš/\¸€BoŢ洴4$ÆŒ#Pç’%K›c´1b„H±9zròäɬ¬,x\mG­Y³¦©©©´´TrBCCt[çHååååè¼|”ôÅ‹qúaaa‹/¶ÿ–jr½Bwâáá·ÂƒtEEEUUùîÞ½»½½»/oÛ¶íôéÓ&"ܤ¤¤êêjk[]¹rEwŒ‰üìììS§Na‹]ø=|øpTÃJ…RL@ÓÒÔœ˜˜íZ7ûË`‡FDD Ãè|ll,d ¿KÔ‰n`$¨¬¬D>J¢¨S!8‚–a• HlÙ²eÇŽ<ÐÎåu9R9N µ‰Ç18Iì¯X¢rÔ?xðàÍ›7ã§jü¯ŠôcèqâfàDˆ,%%¥¬¬Lrà> 111{÷î…ß £!Åpˆu=º£¤f’‘‘!»ƒX8//Ob|kÐì)ÓЭ­­¨Äz¾¢cµ Œ/C>fff¤ÖÕ½²²cƒb™+W;)“æÚ™Ì0ä`ð“é{Æà„'î&’\ÑÌ?DFFjŸ’Ö–±ƒî(™³¬M'"ˆ–bjánÂ!(ƒ`\½—(9Ú2jmÒŠ5¸nÐ킵×ëêÑåèžµßUrã@“QSSsøðaw5$2uã!ò,Â0Ü…|ö™âíÝùÏXò_ùÕuG 6ow¾*zœô/.\h¿À„ ´wÃ{b³¡>A½nÞß:GÀÕIvv¶“_=%=bûv%#Cyì1%:Z™9Ó¡üqs·³ÞŽC“þE·¡. ¸Æb.kH°žŽïêN%î~ÿûÎÀ¡/{æ)£:¦Ðã„Ò#œzþ+¿Â6!qžQ!9=N\Ë×·+_û|pl€ÒîîžâŸ|b;_'ô~Ç‘ÚÞ;tú~½èŠ=»ê!=N\Kãbå/>‹~âînbß }2$J¹©Ú~áõë~¡XæUÎk;ùá‰çÒÿ2{£“ Ç‰kû[åo·æ¥Ï»BˆClݪ¼úª½þþJl¬ñЕ/-WZþa¿¶1~cìœzœ¸3ØŒŸpm ïÕ“°¿í|Ñ÷œ9JppçÇÏZú.¾}ç¿ò«â¢­Îã =N!=B§ï^óDòO§ßnH—èqBé£ô­2`€gPðxCª¢Ç !ÄÓ¦)Ǧタ'„{·Æj_ABˆ¹¡Ç !ÄÜÐã„b×Ú¯ææüÒØ:/]¼d¿=N!qËÀŽï…ì¬|ÿ‰™“ ¬uÕª,;‹º)ô8!„ÉÐàQÇ®Xñ´+Û¤Ç !ÄÜÐã„bnèqB17ô8!„˜zœBÌ =N!æ†'„sCBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8¹A©««ó÷÷oiiñóókjjÂÖ‘£¬K:~¬®õŽŽ¤=<ýÌ™3/^„°+,,L¢Ô„„lÏŸ? ÙTÛ† tGáŸ}ûöUWWoܸQväççcWcc#¶ˆ¬×­[§U³ÚЂ âãã­’£}£·'OžT,CBW§¦«­­­-11qðàÁÈQ»íéÓ§ëëëQ!ú†%%%………(¶hÑ"Å"_tQ¦¬¬ÌÏÏ?1|”š ÚÛÛQ^:ƒ|õt¤9‰Ê›ššp%±zõꜜœÌÌÌ®~ŒÄ¤ÐãÄÍ@âb¤!;1²Ø‚€ žððp¨PrbbblÎüꎂãpÌ-ª»àJ(¦ƒ±­««S•*{CCCEpAAA'N´nHÊ£Nl¥N”´y^ÖµáDp‚8´›””¤–”¡ÛòòòeË–ÍŸ?¿¨¨È××»:::ªKCøÉ”––¢BŒ2“ƒnÈ<†+¹¼œ4iÒRâzŒ‘h:66§#CÊà§$éÍïŒô3èqâf`DÐ[¶l©­­•Då’€ÜM#Ò„èÕq™u=º£Áqê.h+==½+y´PQQ!QIW· qÑ0zôhIc\q°6|”¦¡à¡C‡Z2vìØàà`2bĹðððÈÍÍ…Á³ãD xdâX™ŽG™U«ViÏõã CN\>bHÀOM‹Á‹ÙÑDî6{NzÏßþ¦´Õ¬|²víUk½õÖ[Ÿzê©[n¹¥«ô8q?2éOEEEió!/1ÔÕ«W%ºDd=räHûµ©G ƒ º|ù²¤ÛÛÛeF¢+ ¾€€Iûûû÷øLìÖ&Q¼0là*¡­­mãÆ™1 Í;W+W®Äh¡Ž*(©àkõ#DzæK70`@zõêÕ|š¥OøæšÇ…ú¸yK:þòµfg¯={¶¥Ýèqâf222/^¼š8q"rvíÚ%³É0;"ñãÇçää œD‚q‰µ­ÑIþ}÷Ý—””„Ø6///66¶«žÀ›8•tttDGGt%;tM,\¸P± BqqqŽÔ†ô 'RWW§›U?þ<ŠÉˆ±ä®ç––˜Zbó#GŽŒ3F±ÌÈCÍó {ôD± 8qˆ¼yó´5ÃÚ8ý¬¬,¤€sZ¼¯à90mÉsÆÖY\´Õ~zœ¸ا¹¹YnÍ!ꄘ>#¢”;„ëׯ©ÁõÚ›U鎂¿<==ñ¾;{ölrr2Ò¡¡¡âÄàà`lQ@U),ÏÿÉ]Äôôt›—’!!!ˆµ¥NØYÒa]›äÈ X¹bðòòR«Emº‡aäÁì’óÂ_Ë®={ölݺõȨק¥¥É â|›ššÔë€ÄÄÄ””©Jw¹C®èqâN \U^ñpí?zôhu2WpäÙjÝQÚô ÚÂÒ¨zßO-ÜmCjIé­ýÂÖµérÔ^éÎ×f‹ÒhWåµc€z^ŠårAfÕÉu =NLFIIÉ®]»t™ˆC]ÓÍçÇå©>]¦+ç »šô—¯;ñ ANRS£\º¤ÌœiX…/ýbåÑ#ÿ¼«3köœÇŸt²Bzœô/ºÀu$6Ç’û“}ÝyÄP%$$¤«ÖipCØ¿_ÉÈPn¿]yôQåᇠ:$~î\Û!¶g{=N!ÝóÕWJaaç?z\œ2cF/«‚Ä#£ŽŽy4(x¼!}£Ç !¤¨B>\™5«Sè=zÓÁÇ7ŸüðåoaûzÑ£Fq²Kô8q-—•«£¶nöÙ?ÌÝ=!Ä1öîµö¬²qcç?ú´ðÿåHm°v檗žHþ)ÒEL]·:³`óv'{H×r:^ù‹Ï«yîî!Æ!BõUOe`”r“íï7h‰‹O0 óYÕ!C|¿úòKç;@×2¡óöòÓ¦„ðû„Ĭ]ÛyŸ³+ PfÏV¢£•q¡—ÆÎ*WZþa¿¶Ò7wæ¬_»dÙ ÿ±çε%$γ_ÞèqBé1ª¾gÎTäÅ'Ÿµvcp!fVÜ¡ƒ r¤ï J}úYç;CBˆ£Xë»ü2{#¶çϵ 6èÑCzœBºÁ}ë0Jâ =N!ö™3GIO7Lß}=N!öèú}±ýzœBÌ =N!æ†'„øøå¹™Q `älzkK‹ýô8!„Ä-¿ù~Œ2쎬g5²Ö[n±³¨›BBˆ‘xtÇè)=zu–ÓÐã„bnèqB17ô8!„˜zœBÌ =N!æ†'„sCBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8!„˜zœÀ† –.]êî^rƒBزe =Nˆ» Ç¯sšššüüüêêêBBB:::ZZZÚÛÛ‘V `¶Ú€’r>¶µµá@OOOT%u89Æ óõõÕV¥Ö#íºà !ôøuNJJŠ$²³³ÓÓÓ ”””T]] GDD„……a/vUUU©GmÚ´©¢¢Bò¯^½ZXXyäÈ‘ØØØøøøGy$88844eP **JŽBº  @ôvµBúzüúGT+qw~~>¶Pù¶mÛ¼¼¼ õ5kÖ ¦–\= ~—]AAAeee°3 Lš4 GææÍ›Qe233UcWNNš@m2<B\@—_¼8íć'Œmì;Šâé?ÍØ:I·ÜqÇ’PÝ:cÆ È!öèÑ£%gÁ‚oII‰bѱbq½Zp5ñ`èСbü)S¦´¶¶ª»P ¤ê)**êÓ“"„¨téñòòŠ+W <ÄÀÆòó~uùËEùë$ŽÓÜÜ,‰ÚÚZ]¼\^^>yòd„ÛHûûûoÚ´I»·¡¡A2®º»©© N×–œ>}º ÚysBHŸbo^eÂ&Þ9ÜÞ¢p=åß½f`}¤gÔ××C²wÞy'¶LLL Ä®eË–ÉŒ¹õQ111©©©ˆµa(³ÞqqqëÖ­“à]¦¤¤¬\¹Ò5gDQ8?~ÝÏ"¾–4bçË—/Ÿ9s&++KîFÂæ¥¥¥H”••i%®Ö׬YSXX¸sçΑ#Gª·.ÇWYY)·=ñqþüù’/ËêäNéSèñëÄÑv>Âæ6ŸûÖ­«;J±2µTÒÔÔ„ ~úôéÎt˜ÒSœõxJòœ ÷lÞnH‡:¹|YÙ³GùôSeÅ Ãê¼áñôô 0¤ªàà`›ùí9R\\lH+„qÊãøW_~¹dÙŠœõk‘vVå¢ïÝ»•ßÿ¾óãƒÒã‚Ð[:tž®LͯtâzïñóçÚ‰/øÙS1³âüýÇïeE:}Bé ½÷ø¹¶Ö“ž(}óÂßü7¾Ö#•ßÔ~UÙ±ƒú&„'qv~|ŒßØ7ÊÞNIž“ùÂsHt[þ»ÿó×_~öð…'OÍë¦èÑ£Êìd÷H¿bÂÐqîî!×!½÷øOOlGÞ= Ûñ!‹‹¶:rÔ°/[¦]þ|êå3Ýýê+åìu÷H?dă·)Š‘ß,#„(Îx|Ô¨1S§Ý_]ùÎôûÃ?¨;:dˆCßßûtØÈ9ߌRÆü[c¸{Ž(øC§¯mòàƒÊ;ïôº{¤²û¥"å­Cîî!×NÍ«d­þåÂO„O»7àû÷l*|­GÇ~5íß”Å?U :or¾õ–=¡Bé§<<~ÿ¡cçϵ v,·ÍÌ™ÿ(tBé|ŸÓ)‰kÑ ýÒ%cª%FÐÖÖ¶mÛ6ë'Ä›ššä]µjΆ æÎË÷dâ2úå÷òEè¤?‘–––››kßÞÞ~òäIm$ž••eÔwŽ!ÝÒ/=Nú555>>>jˆ]XXxåÊ•ÀÀ@u ‘øÂ… QìÂ… \×—A“¨Pß~/+Qœ={V^YæÍ›'ëK„‡‡—––òkú„¸zœtÏáÇãââ”oe–×%&&ŠÇ!÷ììlu‘edÂòô8!®¡KtÛ‚ÇÞÞCóðû/†WK\ƒv!!___Y[Dë;wîT=Nq%]züý÷kû¢½”—Š”Sü&ˆYA -˶)–IsYÔ Ûüü|„äÈQ_\>xð`·õ’ ΫîA ŽH<Äšš:zô芊ŠôôtµLVVVff¦,TYYî¾þrcA“îY¼xqZZšL‹Ë,¹úxAAbYBHŠå¾hQQ‘ûúKÈ=Nº¾öññikkqëæÁÕç %QSSƒøß"ÄeÐãÄ!òóóËËËuŒÛäÊ•+kÖ¬qA—!=Nʼn;^Œbô8!„˜zœBÌ =N!æ†'„sCBˆ¹¡Ç !ÄÜÐã„bnèq*–·³ªß`$„~ =nƒÀÀ@I¤¥¥wttÈò„Òq³Çåݧ¥¥¥111G8p`ccãˆ#âã㥀,!¦¾Uü­¨¨(((ðóóC ‡öòò’·8É^$P!öBÁÇŽûüóÏÏœ9ƒä£-),MWWW£þ¥K—–——£iíZehº¾¾^bó’’’ÈÈH.L釸ÙãP0Œ E"½jÕªéӧðëÖ­ƒváVíbYYY(“——wþüy”‘÷£B¾Ùjkkrssåeª<òHYYY{{{JJ Âj9Py\\ x{{£ yóêÙ³gƒ‚‚¤Ì²eË&Nœˆ^¡´ ч‡‡c<@‹:*ÀVT!Äítéñ={ö?~Üðöþúõ]$®.&‹¬‹Ž¡QåÛ%ÄvïÞ­X1€è±W Šapø]Þ‹½ˆÄQ›ÔW¯^ÍÉÉÁáp±TG/^¼Ç¢ $.M£°v­2TØÒÒ"•Ë»¶Õ—ù¡Fèc =Né?téñgžY9óa//o«øÏ·ùMR”±¹788Xê‚a*2=ÝÐÐPRRÅC¯‹-™zzzJhü¼yóä#g™ÑÒÕ¬ˆ¬kc‡ššõR@ó!„þ€½y•Ÿ<ù³;‡ße`cgÏ|vöZ—{ëëë%!3ת ËüIcc#Âöx bU€Ð¿øâ yvBBBhhèÁƒ%¯¬¬TW’t’ 6œ>}Z9!¤_Ñ¿žWWTTdeeùùù!¦V—SךQ,KψRW¯^’’Åó>>> .ŒŽŽÎÈÈP,÷3«««ÕI’Þ¡µñmÚ„þŒ›=ój?æææÂ¼êc!ùùùº%ÄtÀõEEE"k‰Ê«ªªä‘/ŽR€;vØlªeäùèüH@!}Mï=~íZûk›_Õæ¤>ýlO+Ñ=—íkA›£[BÌšnQoTªùÖƒëV&S XWNô`tœ9Sñ6ò> !¤GôÞã77m•ôÉOLv/<®eþüùÎNÜÜ9ÛÇS¢£)tBÜBï=<~ÿ¡cH”¾¹3gýÚõ/ÿÚÉ®¨OóñûßwþS(tBÜ€óãÛ_ßœ8oÔ¨1ÎWEL…NˆËqÖãÆÏkstFåïÿÁ‰£3.u>?~׿MʰÛlô_(tB\…³¯øÏ²{ƒ-ýõ×!õï%I:ï“Ms B>\Ù²ÅÝ]!äúÄYŸhlXð³§-}ë­á±[Û¾ƒä†ôÇ‚ýït²uâ~¦Nµ·úž5K‰‹Sä+T﹦S„ÜP8åñ†úN~xbÂ~èèßùNË÷†Uÿwç¼Êå •?gZ'ý¾ !}‰S _½ÿ=lê 17Ô7!îÀÙyJœPß„¸—þõ~b>jk•:<±Féèqâ”8!î†'„sCBˆ¹±çñE?K¾õÖ[ lìä‡Üm`…„BºôxyùžsçÎÞÞkÿÕ¬œ9bxµ„rÃÒ¥ÇÇZ0¼½m>3¼NB¹‘áü8!„˜zœBÌ =N!æ†'„sCBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8!„˜zœBÌ =N!æ†'„sC“‘„„„úúú•+WîÚµ«¸¸—/_âLuuuëÖ­ÓÖVSS3Ÿ5K7lØ€íÒ¥Kª\7ÐãäFohh@bâĉ؞?Þj¥žÕ«WûùùAë‡Öy¼­­íêÕ«ØkHsBIII||¼ÓAwÒÑÑ‘œœœ››ëëë‹xÓËË ™ÞÞÞ“‘˜7o^TT”b Eé|mý¢E‹lF¸………º£RSSÃqEEEGݺu+v…‡‡/X°ñrcc#¶nNNN~~¾ZÚð5_¸p „ÞŠEÍiiiHŒ3F  Î%K– 6Çh1bÄ‘,bsôääÉ“YYYð¸ÚŽZ³fMSSSii©ä„††"è¶Î‘ÊËËËÑyù(é‹/âôÃÂÂ/^Œ£Ó¿b>èqâN<<<àVx‚®¨¨¨ªª‚à ßÝ»w···câåmÛ¶>}ÒD„›””T]]mm«+W®èŽ‚1‘Ÿ}êÔ)l± £££‡Žª`X©PŠ hZ‚š¡]ë†` àЈˆt…¬áw)ƒ:Ñ Œ•••ÈGIt uÊ §CÐ2  t‰-[¶ìرƒšÀ¹ ¼.G*Ç©¡6ñ8'‰ýKTŽú¼yófüTÿU‘~ =NÜ œ‘¥¤¤”••IÜça!&&fïÞ½ð{AAòa4ä ±®Gw”ÔŒC222dr çååIŒo ‚=eºµµ•XÏW`@t¬–ñeHÀÇÌÌL˜ÔºÚƒJçÕ+ $&L˜€³®­­E%’,WÒ0„ 'ºœÑ£G#¡²ÆHàïïþüyuŠæþûïß·o†––cçmHÏèøêâ§ñ 2°J\kâÁNzœô;ÕôåË—±8p |4hBo.^¼8nÜ8I{zzÚŸ Gý’˜?þwÞi¿Ÿ(ƒ ÕbXëbkÖ¬Q,Á>BoغÇ%Æ-Œ7èØÐ¡C¥˜:-dÞF—#ÿ¡u(^†Dâ2¡„|íq½vó‰?(÷fþï?XëáC•¢I3zœ¸h’BÄ µUUU!’’е´´Ñwuuµƒ«±¹5º£Ô‰øª(¯°°ÐÎ-AHväÈ‘ÒPjj*>>ÚÚªCÄP¹6F–QÄËË ƒVss3ÒÓ§O¯¬¬ÄØ XæÊÕNʤ¹6G&s 9ü$@c NèqâN`"‰ÁÍüCdd¤ö)im;莒9kÁÚtòÐ!‚h)¦î¶!‚2ÆÕ{‰’£-£Ö&­XƒëÝ.Xq½®]ŽîÉñØØXû]%7ô81555‡vWC"S7>"ÏÒ( Ã]H[›bøóœ¥oîÄ6fVœ!µÑ㤱páBû&L˜ ½»(øûûÞ› õêuóþÖ9®N²³³üê)é›7+›6)³f)qqŠ!ßÏ6y‚$rÖ¯Ýè˜óÒã¤Ñm¨‹®±˜Ë¬§ã»º@‰»ž³g•;ÿ î¬Ðê?Àöeo{xz<1µêÝŠˆ"ì=N!Žâ¼Ð‡øv>iúÛ‚_¸ë.$Æÿó½¢Ç‰kùf€ríÎŽ PÚÝÝBã“OldZ ý.Çžü<Ä÷žÀ Šò·Fï!=N\KýsÊ_|ýÄÝÝ Ä T¡»ã6å*J7Ÿ’<铞ÀGç»GBHÐé»§Ü;ù¾ÝåUÕ•ï\ºt |t¾Kô8!„t“úÖw¢ozœBì1mšòÖ[è»ï Ç !ÄÆ­±ÚWÐã„bnèqB17ô8!„Ƶö«¹9¿4¶ÎK/Ù/@BˆAÜ< Ã'xgåûOÌœl`­+ì,ê¦Ðã„b7ݤÜ2jâØ+žve³ô8!„˜zœBÌ =N!æ†'„sCBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8!„˜zœBÌ =N!æ†'7(uuuþþþ---~~~MMMØ:r”uIÇÕµÒÑÑ´‡‡GOï–ÞõŠ˜zœÜˆDDD`›•••——W\\œ’’’ ±v{à /¼°|ùrmIÇÕ’””ÔÐаiÓ&¤—.]š‘‘±fÍšžD—`HOO¯ªª2ªBÒÏ¡ÇÉHkk+4ªt.ÙÕ³5» }»ƒK¢¹¹Y·«¦¦æðáÃjCÀhÙÓS&ýzœ¸„ƈ‹!—ÔÔÔéÓ§Ÿ9sæâÅ‹Pv………I”š€íùóç¡!›jÛ°aƒî(âãã³oß¾êêê7Ê®€€€üü|ìjllÄ‘õºuë´jVZ°`A||¼uCr¢oôöäÉ“ŠeHèêÔtµµµµ%&&<9j·±=}út}}=*Dß𱤤¤°°Å-Z¤Xä‹Îã#Ê”••ùùùá'†RsAAA{{;ÊKg¯žŽ4'QySS®$V¯^“““™™ÙÕ‘˜zœ¸H\Ì‚4d'Fû@XÔJNLLŒÍ™_ÝQpŽ‚¡Eu\ åÁtp"¶uuuªReohh¨.((hâÄ‰Ö IyÔ‰­Ô‰’6Ï˺6œN'‚v“’’Ô’2´`[^^¾lÙ²ùóçɺÕ¥!üdJKKQ!F™ÉA7džÕ\^È Nš4 i©q=ÆH4‹Ó‘¡eðS’ÎôæwFúô8q3° "è-[¶ÔÖÖJ¢rI@iBôjŽ¸ÌºÝQHˆÎà8u´•žžÞ•¼NZ¨¨¨¨¤«[…¸h=z´¤1®8X>JÓPðСC­;vlpp01b„\ xxxäææÂàˆÙq"P<2q¬Lǣ̪U«´çŽúq…!'.1$à'Œ¦ÅàŠÅìè "÷ÀÀ@›='NÑZÿñÑOÖ®½j`•ÞÞÞO=õ”ô8q?2éOEEEió!/1ÔÕ«W%ºDd=räHûµ©G ƒ º|ù²¤ÛÛÛeF¢+ ¾€€Iûûû÷øLìÖ&Q¼0là*¡­­mãÆ™1 Í;W+W®Äh¡Ž*(©àkõ#DzæK70`@zõêÕ|š¥OøúªÇ—uqs–tüåkÍÎÎ|øá‡ïºë®® ÐãÄÍddd _¼x1„5qâDäìÚµKf“avDâÇÏÉÉA8‰ãk[£; þ’üûî»/;;;)) ±m^^^lllW=7q,*éè舎Ž.((èJvè0šX¸p¡b„âââ© 9èN¤®®N7«~þüy“1bÉ]Î---0µÄæGŽ3fŒb™‘‡š1æA÷è‰bqâ8?„yóæik†µqúYYYH#ç´x_3Às`ڒ猭³¸h«ýô8q'°Oss³ÜšCÔ 1!|FD)wׯ_/Rƒëµ96«Òyzzâ#|wöìÙääd¤CCCʼnÁÁÁØ¢€ªRXžÿ“»ˆééé6%.%CBBkK°³4¤Ãº6É‘°rÅàåå¥V‹ÚtÃȃ%Ø%ç…¾–]{öìÙºu+ê‘Q®OKK“Äù655©×‰‰‰)))R•îr‡\7ÐãÄ@¸ª¼â-àÚôèÑêd®àȳպ£´é´…¥Qõ¾ŸZ¸Û†Ô’Ò[û…­kÓ娽үͥѮÊkÇõ¼Ëå‚̪“ëzœ˜Œ’’’]»vé2‡º¦!›ÏËS}ºLWÎAw5é/_wₜäý÷ooeìXÃ*|ïÐÁ7ß(¹í¶ÛžLyjðÛ×—=‚'ý‹n'p „ Áñ†äþd_÷GE1T éªuÜÞ}WÉÈP‚ƒ•„eölg…‰GGEÄÌšÝÜôQEù[ûs¾‡ô8!„tO}}ç?ç…ŽH<àû÷lÞþñÇÍa?Úþúoç<þ¤“}£Ç !¤%tOOýÊž4§Ç‰k¹â¯\»u³Ïþaîî !ޱw¯í|Ðï›~“#µÍšÿÚï ŸK_|éÒ…“ž0¤‡ô8q-Ís”¿ø¼šçînb"tEñVG+7wsOâÞÉ÷í.¯:z¤vòSO46Ø/ì ô8q-•¯}òÒg÷¿ÃÝ]!Ä!¶nU^}Õ^T{L™0éÒÄ'v+-ÝÔöÞ¡ƒ¿ûí¦_½ò›ú>@<>1t’ó=¤Ç‰k¹å+ü?áÚ”ž½¯›·±¿í|Ñ÷Ì™Š|5í³Ö¿;RÛè1~ʘŠt̬ÙÏï!=N!=@§ïž2xˆïÊÞ>X³ßßlPðxCºDBH÷8©o-PyÌ,ïäé5ô8!„Øcöl%9Ù}÷ô8!„ØÃÀoä÷ô8!„˜zœBÌ =N!†qñËs3£8àëlméæ¡tzœB â–ß|?FvGÖ³Yë-·ØYÔM¡Ç !ÄH<‡ ºc´,Àä2èqB17ô8!„˜zœBÌ =N!æ†'„sCBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8!„˜zœBÌ =NnDjjj>ÖØØ¸`Á‚¤¤$gêìèèØ±c‡Qµ9Ó·KÜ=NnDRRRæÏŸ?tèТ¢"|,))™4iRHHˆ3už:u õ@£uuuáááÈ)--]ºt©1=v€––4çOGKBBBqq±Qµálj›zÄ8'a¢aÆ!’iMd£ÇêŽjjjòóóS+×î’ôŒ3P`É’%Öý±ÓhkkC?===q¸ä ­v òubœ~ñâEé˜z`VVj–Y×T‚Ó÷µ,©ö_rlžšn—z:Ÿ~ú©···î¤~õG-?.µBmÓjßä‘>þ<Ê`X:}útbb¢‹_æGº…'nfݺuˆ^¡¶èèhh²¨¨¨ ƒOž}:vI+ð»Ú…‘INí¢¶™3gªCˆZ? à²mÛ6é->îÙ³Gv!݆٥oƒ Býëׯ?{ölkkkuu5𮩩ÉËËËÌÌÄïKN„ôèqâfrssáqh&‚¿dÚzÍš5ŠårÁ DÝDEEITb3Ô¥X$…£ ]uœ»víZ¤a(XIŠ©¼ð ªžà25\Õ²jÕª²²2D©i'Mš„¨ª¶¶Æ „Õ’qqq*ÐUôñ,ì¬ÆïèR@@€t iŒ2öÈYÀ’’Þ¼y3ª…^%„MËDÍ]Øbò¡x¤U‰£þ}ûö544Hý§NR\Ú]è¿ 8)u’½±±-â·#9S,àÂ"-- #'[ú ô8q3¸–‰‰A®JB—|Ö×׋Ä%¢·éqÝQHÜqÇØÂAóæÍ“]â»®zÛ®² Q‰ÍÙ„ÃjZF1&:¹uëVëò¨6n܈ ê\¼x1Î÷Ê•+ˆe¯Ü•Ø\±ˆÁ¯¤UcÀÐÕig—5íííC‡UëÇ?C„Û² ¦ ÒÖìíí-cz2þ|]mè-./0YOIåoØràä…<ø–µºmжm[o¹¥ËµâèqâfØB+ÁÁÁ–%Ø„YD7GŽY´h¢N54>}úôôéÓmÖc}”äCëØ%#Â^ÈNgdNCÑÌÚ[#Ñ«uD©pŸuyˆ[Boè/11Q7Q^^c—Z‰êܾøG}¤ÍÁÅ:³„3BŒdüÐmüF´%Åà/ÕòDÏ7×n¾ÖöJÁ—) ú8þ0ì,íF7“œœ,òê”1dküÏ?ÿZ„U±Atvv6,¼oß>›ÓÖÖG©ùöAc——— SwÕ´’™™‰†¾øâ‹eË–‰ËteP[jj*z[[[‹a45gçκÂK°WN‰·€q Q0„Ž.!SR¾}Èdݺuê……3 ¶õë×KØ.ƒÔŸnA-¦Ý…-‚t À[ZZN:…þ˰‡K"Œ7III|¨±[nþî-÷N¾ÏØ:o¹µ›U›éqâN 5¹É©Xbá£G*–©[DèŠå¥b™y>|¸L•ˆòl¢; Æô÷÷G-+++--U4á¶Ø $:–Â"5è:³)q€È¾C™‘#GJX*9èü;nÜ8µ68 Ô£z\𰀞H—Ð74´{÷îM›6¡õF¨:¡¡žˆÚÛ®v©æÎ»mÛ6¹Å*%ÑUר_î`HSû#»pF¸Ð‘ÃW®\‰’(ƒNÊ9ê5 é‡ÐãÄÀhjˆçgNFu]GY°_•î(mäˆjuÊGµuµ°# YǤºõ£èU×%ôÄfµ•¨½µ³K¶¬Ÿ[׿èn0è kg~dâ£)ýzœô/´¡¢MÔgø´È÷nŒÅfC|BãäP.]RfÎTl]¡õŒóçÚ}ä¡M…¯ÇÇÒ7wæ¬_‹Ä’e+bfÅõºZzœô/º•YfôÄe ‘~ÎÉ“JFFgâÁ•Çë½Ðß;tpýº_œüð„|„Ó!ñÉ?šŠ4÷M™6xH/ zœBâìü§|+ôÙ³ooGm¨ÿ :*"fÖl5çŠÎg—>—}O›<ç<þdï:FBHÏ¡?ùd§Í££;#ôn…>j̘Ãÿ§áZ{{é›oHΕ+W±UcpùØ;èqâZ¾œ¬üÅgëfŸýÃÜÝBcïÞ.wýþ÷ÿ„þ£i·(û×®Jà9j”'¢rãûGWsæ!xüÕ§Müý•ØXeÎÅ{ð¥‘ÿ^®´8ZmäÌ謕Ïoý·HŸüð>öº‡ô8!„ôUßÁÁÿÌù¬Õ¡¾$FóÌ’Ÿþæ×H?ñ“øØëÎÐã„â(ÖúîAÁã÷:¦~|þÅUøç|¯èqBé'õÝ×Ðã„b§žRV¬pw'ìBBˆ=ÿ²» Ç !ÄÜÐã„bnèqB1ˆïÜtñËs3£8 ›•zDkK‹EÝzœB 㻾¹çQe˜oÖ³X«···ÍUMTèqB1ÛÝ1ÚæRà}=N!æ†'„sCBˆ¹¡Ç !ÄÜÐã„bnèqB17ô8!„˜zœBÌ =N!æ†'„sCBˆ¹¡ÇÉȆ ***ÒÓÓ÷ìÙ“ŸŸŸššºdÉ??¿n´.éø±ZŠ‹‹ ‘^°`A[[›ý!Žt‰ŽŽ£ªuý‰…'7&[¶lÙ±c‡¿¿ÿÙ³gññäÉ“íííŽ2lØ0mŽãÇj©¯¯Ç600P>¦¥¥‰UµkX¡¡¡ØÖÕÕUVV.]ºÔ¨jmžékèqâN ªM›6‰Gjjj®\¹âåå5tèÐÒÒRäÌ;W‚;ì:|ø0 .´©6ÐURR ’B¸mÛ6슉‰AàŒ`iì‚ǭ둆ºU[SSšCoí”±®­¼¼¼±±qĈÚbˆÊ¡ul˜«æååùøøàZA à'&oÑé9sMKy|¼ÿþûq‚’#…åLíì’†vîÜÙÜÜŒæ´ïç“úÑÉøøxü‚Ž;†>><**J×´úƒ•ÔƒÁWÎWšÃX…ñãJIIÁo¤×.ÿ·½ûò,à8þ.ê¶ŒR„¦”?N3þŒ¹ÚŽ9Df %.E×–ÍZ„‰!Ó’2:¶8×¹¦´J©6Ã’6"nŒ –¸Á°Zš°”µ EVñgÙŸ¬ Ûjm™Ù23ÜŸ½^¯×ãzwíC¿Ÿ4—çž÷yŸ÷yïà÷>÷Þ{w9Ž¡¤Pîêê*))Y½zuiii]]rÁœñèéé™?~GG‡²oc@kkknnî¾}ûúö£¸ ZK!’ššªY§²fÉ’%Z¤fyyy»wï^°`æãº=uꔂɦ§Ý¦ØÙÙÙ!7¤ö³gÏÖ”\]mذAÓùæææ»Ö·7­«Q©ÆœN1ÃÖ­F¢ƒ†íýï„ònݺu&ï´®òTm´ eeeZªGÌ„òÊ•+ô*<óÌ3?ü° j¬f•BS[ ³H€Ò633S›¸õÖ[í°õ\ØþÙÚ®Ú+ú•ãv‘ÒßlZcÖƒ0iÒ¤òòr­»téRíˆVñ[‹ô¤¨lG}TK-Z¤šhÿÕ 9Ž!¶~ýzýŸ×ÜV‰`fß999šÁ©°ÿ~MñLÎ hooW„Œ€ µTP~i%£]¤@©¬¬4“\Ó›¿mÅô¬õ“âþ¾EZ›åË—›>ûËñ¾½™×v5€ôôtÛR5:ä(µÕ@ñªÇÁôìÎ{xèºÝ³gn•ª:J© Nô¸é(e÷tÊ”)*›u«ªªLa™±)¬ý/qôDØþ5‰îííU4ë9RÍC=TSS£BAA"[5laêÔ©šz«íˆ}`uÀó‘­ý2Ea€Ž?þ89Cä8†Þœ9sYYY殙ЉÒG3ÖsçÎ)lr'd'AkÙú¶¶¶eË–™²bËÎ…CÚ`ÊÇä×̉æHzó/ÊÈÈèÛ^y­®Ž4Š{{‚Îí(%¬JUegg§ÿ®ÉëÂ,ê2:èP§×L¦ ah0ª^m(»½>ƒž5óHLÛ^»fW ÷ß»úÅí>xñªM«cØkJJÊ™3gÆŒÓ_rCLI¡ØÕôVÓ=3SÖËvÜJ4Ý644˜¹¤ByÕªU!ûé»–©W¾8pÀ¬ÞÔÔ¤cF#QÜ(ñMKŽ™r†”œœüÊ+¯˜²†d!á{ÓÌý7Þ0ƒ4ïsö¥è4‚^F˜+aì"sn]ùhßÿôábÈô¯YvYY™=édè5Ù´†§©ºBÙœ}ÒÛß,))IAo©MÛõ,———knnöîŠõŸwÇ||ì‹o¼Û^¿0+½»»›ÇðeNûjæ»xñb3Y6oWvuu™éZºbÅ EØÙ³g/\¸Ðß9h-[_\\œ››k‚Fmú; "ëÖ­3R':´E˜Ÿegg›>5¤{³5Ú‘ ÆŠuõfN_v2®× tͦud2§5´;šÒêø‘ŸŸßß#·uëVû¾¥<öØc¦m.è°‹Ìø'Nœ¨‰];¨Ù¢m¦ÑnذÁ?lõ£g„ëXâ‡ÇS6™7ôôÿÜ\¢¹¹™áš8Vp+|Í „0ÓÏ µl·š?jbhÎؠܾ}»ii’Ñ6®««;uꔦ“þH 9ZÓ§Z>òÈ#!/¡Q³ ÞLvD ØÓÓãŽ1¦±ÝÁ¾45ƒ7×á(ßßš ÛËNìlovÃ,²€&ËþÍÞvÛmAýû½ôÒKv‘¶vPwõÜy'Q£Õb†­×ö¢#Ä9Ž!æ¿ Í–ƒò:-À”õª_á²+ÿZA·uhïšf¶ñ€ò÷ã/G6Ký½õoní oË0ƒÚ5{×81«„YôDÒ¿)ûïú‡Ý÷¹3oM÷·S#VDZ£é·„¯¹\ä8†—0ç¯#G޸߯Œp­(„ÜPÈ`Râ755UNŸ>=~ŸâA"ýêWÞßþæÝy§÷µ¯ ¶«Ãä.Ìn~á° î¾5Q Ç1¼ x‰È¼€ÄŒ$ )¯cø‘H 7ÝÝ^mí¥¿”ïôê'~±£î©ð5Ñ!Ç "o½} w;ªÈÎYxçÉ¿Ÿè¯&jä8\ž(==ã–Qvÿ²òçýÕDGb¹Ç{/å•“ÇŽê‘‘yùå~ùýŽœQ^÷Ížâëâ·‘ãH¬©O릲zͼ™|éÜð“ŸxÿÿyÕùÏ«¼zîbÝW{ï$vpä8\ž¨/\¹.)éö/}Y·aj¢@Ž@DyÝ¡ÜpÃÔ]»Ÿ_rÂ3Æ+*ŠÍç€â„€pî»o¨G0rÜFŽ€ÛÈqp91ò‘kºÿÙ•6öÚØöš’’æÇ€ø`mm­RXe“ìÊzÝööö?~<==Ýßá„ â*Ì›7ïܹs¶^Ù­×ê.\Тxï€r¼¾þ÷{öÅv“®ý^Ï»=±í‘;sæŒ)´¶¶*Ó:d)ÄóóógÏž­òôéÓ7oÞlÍŸ?ßÌÁ½ÀYoÝÚìÖl]™n[*»5=WWö€ ®>¯òù/Ìí&G%%{ïĶK\†cÇŽiÆ=yòdÝÖÔÔÌ;W³ò7*ÖUSTT4~üø UÌ{•ŠfE¼n5I75%%%åååfòn)Á«ªªš››¶SÀHÆùñA9«ùµ)kîÜÝÝýÚk¯•••M PeSS“níŒÛðO¨µ¨¢¢¢¾¾>++˦vff¦V\´h‘©Y¾|¹©W¸kšï¿ˆ@ü 6Ç;ŽMϸÅÞ½ðóºMÏàáE³ì0wô]+èìvßkæá¦A{{»æìA›?ƒÊñÃä.Ìn~á°‰ò÷Þsâø¥KÆOÛµûùÁíÕW½çžóÞ}×{àÁv…ÿIJJ²ïjRFFFÈzsâ%(âÄOô9^ýÄ/vÔ=eï>ýä¯â*7©üЫ´tåýߦ_ßO>éýõ¯—î2­‹©iÓ¦Åê‚î;v„¬ç‚q Á¢ÌñŽcGâ9 ï<ù÷¦æ®‚Å·~ösþs,—'(¾‘‰2Ç•×/<¢4ÿeåÏMÍu×%™ÿÁÚÕº-(\QGÄ7 NŒ¯WQˆÿö7µ5[¶…«óïÿ{ÒÎí^ÑŽï†ïŽ;b9D µ/w~ëBwÉõ\WÄR,s܆xþ]aš]å}ð™wÞýâQ/ð9ï¼þú¥?\n üÝ”zÓP¸¢Ä,ÇŸýC½B|ÆMŸ~úÉ-úË¿ëë÷|û;![~à]õ|òä“?þYê®ßy{÷z{öx»vyïôóÑ ¢"¯¦&VƒÄÐZñÓºÚçªð­¡ p%TŽ_—”tû—¾¬[•OtSùòÖ¿æïî»/ýÕÖè€P•ã7Ü0Õ^'þÃ=}G:Dk˜}.¿o _{íP :þü¶mÛú~°óôéÓ•••þ+ÇKJJü_e ~†YŽ[6Ð1œ¬Y³¦ªªªo}ooïÉ“'ý5™™™|:HŒášã~ZZZÆg¾ýÊLÌUÈÏÏú5¸ŠŠŠâââÂÂÂììlrHr‘jllÌÊÊò!¾dÉóyyy»wï¶m/^¼lÙ2óû©©©ü>'ä8"uèС‚‚KŸ ÐL<''Ç|EbOOOee¥™wkn~ºÓ´Ÿ5kVSS9ÄÛ9ž’’’66ï4f~3ö}"QÚÚÚ4é6å3fÔÖÖzŸÒl½¾¾žàl€óÍ7c»=ûI¸Kí˜ù¸fÜæç&&L˜P]]­)yKK‹ýâòäää¡(02p^‘R^·¶¶jº]\\œ››[RR¢ÊgŸ}¶¹¹¹³³Ó´)+++--5¿+ÔØØXÃgqø#ǩիW¯Y³¦¨¨hÔ¨QJjókËæ"ñ´´4Ùš‰û³;èRñ@Ž#R ëqãÆ?Þ\ztÜF¶)TTT˜ ZÄ9ŽËP]]ÝÐÐòÇ<ƒ\ýõ‘40xä8.O„éÌïs CŽ€ÛÈqp9n#ÇÀmä8¸·‘ãà6rÜFŽ€ÛÈqp9n#ÇÀmä8¸·‘ãà6rÜFŽ€ÛÈqp9n#ÇÀmä8¸·‘ãà6rÜFŽ€ÛÈqp9n#ÇÀmä8¸·‘ãà6rÜFŽ€ÛÈqp9n#ÇÀmä8¸·‘ãà6rÜFŽ€ÛÈqp9n#ÇÀmä8¸·‘ãà6rÜFŽ€ÛÈqp9n#ÇÀmä8¸·‘ãà6rÜFŽ€ÛÈqp9n#ÇÀmä8¸·‘ãà6rÜFŽ€ÛÈqp9n#ÇÀmä8¸·‘ãà6rÜFŽ€ÛÈqp9n#ÇÀmä8¸·‘ãà6rÜFŽ€ÛÈqp9n#ÇÀm‰ÎñŸo Ó?™–àMcÈñìã –6nô'R’_ë_R’¼éDçø½ys¯¹úcŸš8NûœàMcÈñìã ¦ÛøÙwÿrøÄ½yó¼éÿ¬ì{Å(ä†IEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/yang-data-tree.png0000664000175000017500000016455114770023131023735 0ustar vladimirvladimir‰PNG  IHDRŒöuJ» pHYsÄÄ•+éIDATxœìÝX[ð3Û,ÝÝ]Š"©‚(¡`‹bwb+vvcw^»» LD‘D¤;–íùfwAQ){ïýÞßÃsïìΜ3gÖÝýÏ™93KÁq@úPþvP;i@JAHR BRÒ€”‚¤„4 ¥ ¤)! H)i@JýTHWTTdeeq¹\†aدjðŸÁápètºŒŒŒ¶¶ö÷fåO…ô±cǦOŸN&“©T*„4ð-IWÖÊÊêÔ©SJJJßUö§BºPŒD" …Ÿ©ø¯’¤¤‚‚ŸÏÿÞ²?ÒùùùÄáw´€º a‡Ãß[ö§BZ]]AHõ"‚’N§“Éäï-ø+Gw»»»3Œ_X!ð/E¡PÂÃÃ+**~ª’_Õ¹sç$}kØÚÚÆÅÅýL ¿2¤‰ýi@âçGUÃÍL)! H)i@JAHR BRÒ€”‚¤„4 ¥ ¤)! H)i@JAHR BRÒ€”‚¤„4 ¥ ¤)! H)i@JAHR BRÒ€”‚¤„48BØÏV#ìgë¨'éåóøwEålš¬²º†¦…ƒ³"é÷­ ¤Á/”3ÑLkSŠhªÙÐÃöôcÖ»ôó®Î‹ŸŠ¦°¶áìÛî´Z–É|ÖÄe|xZÜ¡ôÍýë©ÃJ§©)®)¨zh9lóëãèuÄhøÿV£¯-º¯½wzŠlurÊ cœ]¸øôó¬Zf3uú ™0ÄÑTSžñ}Ÿ&VöCOmµÎ£Ê˜Y;º¸ùøw ðl¢¦ ÀüÎÊçäç I#˪©ÊýÆÝ›Î…¶ï6çú”´ö{~ÍNùW7é! ~!ÍÅÝrëW‚¢ŽÎØ1Äkr+º-ˆ<´æ¹hŠb´îñ™Zšðtç¢êÌE¶í{¶¶¿sKŠat"l« $ìY·ƈQæôZ%Se0qÿ]†A«#Ÿ/Ϭ›¿dõåWyu®•yyç¼Ë;çÛxž<'¸“¹žÖ}ÕVŒ,Ã@ˆ]Û<^yzüsâïìþMÉ;úvî=xô„>­êÛôPtÖD»O1¡=± sƒÊ¯­¼$ò~ç©”?³€T¿’¢u°‰þm_AìSzÎè’uȤÖå„eë§§²„DŸ±Ã¤åÁŽŠµWǹ¹hOþ燂;Söe>Ugð#5¸×†Q3d¾g> ß2¸óøÃ…xÕCMçÀ Ãzµuu0ÔR²Š?¦§EG\9°{÷£” "ëãî[[ªÛ¯Óš‰:†’w× Q¾cç²Y9éqQOßf²Ä³Ë"oyyûØêÍ>Xkñ ?²U¨!9yÆË?Cû¶}z0ø¤¯Žo`¯î=N$Þ4-c_w'2þUAӖʵïnð_! ~-Š×¢ãOÙlŽ+CÙ‡=vI9øíWkTX×e÷E $oê³h~oz‘´qþkñ„aÐ`òñýïz¼hþ‡Q»õÑ2ƒ,` „¯f†œ¶¥‹Ú÷nFú…q>ãWЧµºo=t¸«eͬ×Ð5²pöð>sCeÂ¥ Sœ½ý’Ïã,CVÔž¸|{G£ofó.†….Øz26á#W Ì}¼®][üáÕ5F²ÿâáMý§õŸòíó1ç»v^Bìïh8w=|z]{müß¿œÞ¢çgÓ9¼}<4xA€ÝŠ «š³+®˜xG`Úp‰’¶ÚR)ȆkwYñÏt£º;ã2–w]î0åÖî}g‹¿g5àB›‡õëçIê&¬ï4bòÅãÆ®¸HXÆÃ ý×u~4¯Í®Hб9<ÉÎçUr"tšƒ¿I·ÓÚMã}o.E+çÍîï}ÌNµútjÙÛ!“×K†`uØr|Ró:Ó¯àÖÅû٥ĄœO?Uù·Cƒeom®²ï9XÑvLƒ™k0ÍîùÑÝŠsŸ_9t;vagÛF7¿|˲ ¢SÅ8ÒìÿèÌt£†;®dkïQ+Ûá¿å ±ŒÁ Ðyñ^Óο&š>Ä™ IÝkïi ?Æ=}ôäÕûì¼J!]ËнM[{ýº;¥äêè©1§Ò¹©ObÓòËx²*ÚvΞí\-~ñiòÃξíæ‹¸Ô2!ÃÁoDW§ZF—U||}ýVxBz."gdíâסµfãöÊ3^ߤÊÙ¸øµolA~ ið[¸ô>òðù5·ÒQÒÙn³NÆíì'é'ÞØ2ãBd.1¡Ö2xÇ`ç:Ë㜠׮ˆ7ËöhÇ$#;—.v:aO3…Â/½Ën¯þM¿óKe£éCûî´•òÍÝ9ºóF­Æµœ›xñá¡hRfèÚI&>´Œý¾Ë¿0åmÓ6_èŸ.:žž<{Id÷5Ž5fó^ß°qï±s7^–STΤͬ…óGõöR­ñ‚e<ÙÕÔmd¡¸ÑÄ®HyÂrElyÍR–ÝöGŸ$ àâè«[vï?rìt|žà›ê;›7wF°³Þ÷ÿ~8;l´ÇøÏ‰áÙÇbçºoX4cöž{Ÿækĸu=ÖúóòœÌ+·/_ºâQ:﫚ºNZ2zÇfÚµ¯ˆóñÊí¡ËV†S°Y·ÉKæM ¨« ¿„4ø=¨z«†=ÐéüŒH•]ƒ8·<6ܳ|Ь ÑlµE›Wé3ëL5>+ãúé;D&PÕ´½}[AÉ0iÖÞÞúifœ0áÑÑ7ËÛ׿~¡™¼l«è’°7›ún™rg¬acž“þZ|Ò´íådÕÀÒŒv¿µ£z^ª &³Olü°æ`ó%»ÇM?üy€U^I± ˸¢åïîÍq=bõ…Í«?îD4׿C‘>ÏZ5nî‘wŸÒå”dɼ’ ɪ/†…Ü9}jç“Ç} ~r ‹.«@ôûT5™ô3>³CÃߕ՜‹×ܲ²'ƒ<û‰Jý´sA•U¤sKÊűûê܆.·ŽÞûrKÏoâ¶ôñÀ6}F¥}*H“S¤±KÊù¢é¨³ë»Üügô¾¨°ÀFîøðã ¤Áo£Ýéüµ†íwqÿü¬€›‡·yÎËÏq^ñ0¸Y}G¬ _l8ý^\‡ù€vf’c±*ã;/ºG|ƒ®ZsgyÛahîÚÓ3w8¬ v Lìqwð ¯Fœ™Î9'i¤†¡‘‘ÞwÊþ½Úû Kçˆ vyÄË\¤¯ñiF•EÎ#'ŽÝç™ü§#ׂÜS+§O=QÁ}°}ÑÚn‹}M%st]Fäã#PåE}fç ¢·m9'÷íÒºúÂ$†,C£EðÌ©Ãût±Öú¼TZø¡‰C†_HâVd=™ÔaL«˜mò)~AèôE•ˆ«ié»lÛ¦!^–ÄžëýƒKÑzU p2&ù÷9•FLbªN‹·nÖ«…ä@µ° zɈ Ï& ˳·õ±w°Éa[ã›ýaB‡>‡¢Þ‹ :/Þ¶eZÏê‚ù¯ì±èlŠ@\°™Mö0›Æ_sÀ€¿‘–ߦÕýM:ÏÎOàè–S.Ꙩy/¾5£þ*÷ŸaâN j3sªRõ³ª½'ø Ù„WîÏ6X³áPš.[¸ì©l0rMèÅ–Ë:5x5ûMŒdBCÁT­þ»±üYr¶íUѹ„x¼âÌL>ÒøôáU *—Wþf‚¬8{¿W“¦-§¾Ç —Ì>>ËwöIÌæV GÇùl„êi¼ùÒ;»Õ¾=êoÔrÀùÄ€…]—\LÊK=}úÖüÉ>ð0ΫD<«Þïÿ3áÓî ÓУWõá’W§—î~”FLèzŒ¿}u“eJ’j“g’ÜWöì>ûT¹ p˲u½MW¬NÛ¨ÓK÷FˆZÏsâí+,jTkºðL²ÛŠÀsNWð ÂB×õ<0MbüNÒà·bŒ_¹nÏŮѥIB“Ô­wnœ\×€î*©[çŠoD†dûÌï\3{š…N3¹¾äBË=¼µU#@ Y¾áÚ¤rteXô8ïÚ œ=-|_5H›!¯Å¨u ^ñ­ gs¾¾ Nfºûvkfü»®"ɨ‰ïÔ‚ ðò "›«çPä•ëü +ºMÙÔmA—3å(rçå’ÙßÝ:L^«ž ØTÆŒºåæ¬üÊâˆO&xw#ÿÉδ^ï³{ÆiÔ>¯ðÀ¼¢ëÎ5íý"¡?ñ·Èûðƒs1¹¯ïÜIÎà¨-ù×.80o§¸ æâ½_$ô'~ãÏÇæ½º}79|sÍÚß&üÒà÷ÂtÛßÙÖE­ß ÉCïàÝÝläê-!¼zP2Ê~ÎdÓ/çÙ]e¾$0 ¡Ìsž¯¹ïÔˆž®†Kçžvó.Ç Ìã<}2³MýËó*« ‘¨u\¿]™±fɘ믹ßÌPXpÌÙÁXñwå‰&9èŠãˆÏÖ²¿²¨¸´‚UÉáñ?ÝD—eZi!”ŒÐû«ñx ë·NÈ*-.-«¨äp…5®b$•ŒØ¼È÷ýÉvào!SǸ¾÷ûw‹O£“f1­}$kÕÓÞ€i”™[RÒiûw§ŠþOi6{Pí7â! Úô´Ó'Be¿ŽÌ)…¿„4øíTûNµëwB|™æ;Ô½¥‹^®»+þ~%Ù/îeùÕLŠzû~^--/{uéÊ»&•¹kÆn½<& ¡§³Æ߯Þá> â;W4¬\PYV[p.çÛ„&N&ÿÎÛŒð+$C¤0 £Ë|õɼ8¶åØÕÈÈèä÷Y•ߌ âÆQÖ‡Räú#}aö“U«¶Þx“’WÊ©u™ÂüRþ ~%¥Ñä]MLêPŸ{çœd?ÌŽ˜’ÆãÕò/I"cqâÉ‚ØÉ88”sçlEUÁðé!©uŒÏOæ*Ào! þ€Ï_u¯¯gùZÒãûqi%Ä„VC…¯æb$Y¿îƒ×^ÝTÆ-=wãüÌn“esFÐjô‰ÉËZ¯'¾‘ßÎè¿¥û­±õòÖ°TCè1Q^–Y£Z~wBÞöÊËr¾ ú|.§pNíæÛß^ýô‹ñòâ%÷'QIJš_œ185ÝkÀꇵÞ¼Š¸±\VC¯~m8O–›¸ÍÉD ÜNMÀÔ±Oó{èŠ JuÍüð2]2Áy{bíÛëâ±$C PÆË’ vü‰µñ ,þv‡€_ BHüá¥íY¢ï= »9 [gÒ7ÙÀ/xÏ]‚ƒ¢O]_1¶¹J£n§á¾äl»½N·KPÖãù{"ús¯óû]ÃÎMÒÙEÉ9¥HóÛž'FbÈ|>SŽS9æ§ "ÎKvhdcÝÏÏ8ÔsõCQ»Í:öîß§‹«ƒ•žêçs Y[ìuÆÅüØJ…o·n¿@”ÐdY§öFìàáda¤ýyß(ý¸nÓ~™Å<«0ŒŒÕyÜ‚]RµÇ"gæêbÀ¨÷W‰Y«Uí¶U~gAKÕ?u8ø! ¤Iå³;“ÅSxNê›+©oê[¸èú™z5¦b’lóÉÓº?š{†Ã*ܶ{¸[H]KjYw±@'*IIKL-hâ ú=ðûTî_#M‡hz]›îH¿8Jr¾_7ôò£™-kòž^ò£+Þ=}$¦„èSÜ,½´w’Ê7{#ììB¡àÇîYþ³êY«¼&±’CL(wÝ{kµuãëÝlG¥Û¾[«¤æ*yð BH‘G&?wÉ0¥ÞÓ»¸@|¸ùâômE½W6î'†ICÇXo½ö*“•üÏÆƒ ǵ¨£~5ÃM™‰‘,T»õæ‹@¿ïÝŠß÷d傸ªn]óq£>wïS®_+ÿä¦Û”iµ%41ëåÝ?¸V¡ >þ®èü†Q˜JDg²DÈæ~LÉD;Z@s9lûCi:¥Ôerâèã%8ÊÛ8{qЩù½0N¹ë¤^ Á'Jq”»aöÒ^'ç*üÉ+¿ø„4å{öÝ— >6?Ȫ¡oF«Þ Ìæ÷IF(ùÕîÇ™£Ûê4r-ÆóM8쳩ñRÕÕÁDˆÞäxäÎhã‘DWŒ•ñ¸{S“…ûOLðµ’aÈÐiŸ‰á—ÍæäÆ_{S*~GøœÅqœSYQQAýôX(ðyüÒœ¸£ëæÌÞó°êº&’ŒÏØ5³;šQز]+*ºËA(eå€ÃN 2ÿôÊñ9¬gÿÌœ´;#a¸°¶†ÉX9롨 Ä®¼súAáWyJõÞFïyÈf–žtt”Ã+Ú³gãïåÆÊÕ×ãBvöÝ^ÖÝj¹\\*ºÍܾùbbÏ%îÌ€©&ç–ôPfÊÔ¼Œ[Àã°*Yï#­¾U¾fÙlõªß¶"õ˜±-ìR¿8Š==?`šÉÙEݾ)ÈfUV¾?´ú.kí’™jð£Xàw‚R—ñÙ3ñ¤þäÎ .¯¤ëéÓJ.ùQ9ûý»^µíÝp ÝV³†µü',<¯þÅ0£·á½{?Ê Š´…=CµmÚ¸537ÒS‘£cçT”äd¤ÅF=~žX]•¦­™úô»„Eé ûm’©**äóXeEÙi¬OÉŠSµš™³|Ù8¿¯ûÊX³Í+¼ì&ß!rüÌ`kÏ«cüÝlÔåÈDã^?:äj´€é0²SþÎãµm¤¦÷÷Ë#„ì²¹ö×Ú¸é(3EÀq¡iëñsFºP©]× «%ÿ¼.Ç3¯opn—4¤«‡©®*Î*J}vüÀÉ÷lÔ¼µkÒËçeR×Öpè±sýØŽ£¶!<|ý½3a½;zXëªÈÒ8¬²Â¼Œ¤Øˆû·ßæp5üǯªY°yÏëÂ;ŽÞZŒðGkûéŸr êè).H•üûøÞ­·¹<ÍŽ“Ö@?üfÒ@*¤Dyš":1*Ûľµµ~ƒËcL5_O¿=NsQÑö“÷—övhä[ch…Œë¿ûézvCgRu[޼›Øfóø^S¾&r³ânœ‰»Qû² Ýæí ›ÞEKî‡zU|vrô³äºæÊYŽY:sH=•Úo¯e;áüŽ7-Gí&úx‡=g<ÿI(ã•gÞ½túÓóN(`ÖHb‚aÞùÆ©¦~+Êʺ¼:ê2ÑÇFB¤g®æ¿öÂr5gÏçeµÖþ—QÝG†Å:4íä;2²qÞ?=°å©è ÃqaÍC²d%ʯ,µå¨-¢‚~£^– ¶¤ †‘°¯ 2ÉŠô6¨àÿ„4øäl,¬©r ŒOÓ¬#ÅÞ=º¯Û¤™³÷îg®Ú˜o>j‡n]o§•seÉ/c*‘ƒè¼4IÇÎÖV…Ná² ê¾c¨aï)v?½VÄ–¼û-ŒÔëxFQ°˜|àÕ¨%7®ZsüþÛŠŠrV%‡/ŠFŸÓL&S^Û* çÀqûj6êjí/`Y Ër®ì7[‹É(ª˜˜7iæäéàf¡Ò@E$¹‘{ž9L¶ífqii%W@"SedeU­}C×­õ5§§g[›ÛPdQ¥¹êןw²Z³£o &žÚ~ą̀¤¬ª~âk£ÏC¹5|—¤¶>jÁãÄÜr[€H4S^ݨÛÄÕ¡Ñ» vMšk–r™¦Ú?s»5¦’®ƒ}%ªa¦_ïeç$5=óf…<ŠŠ¶RÃ÷ãÔvñ¢ Û?«æn:ú0¯¼LôÏ'Dd …ØYyË^}†ŽîÕÒèÛ‚:.##ó»]5oó?µ´rjKìéÞ¨?àg@HƒßGMO&ÄÕ¿Œh„è÷UKw|ÿñà/×$|9&¸á¢z+o…¯lôŠ˜n³ÂNÏB¨4?« ¸ŒÍå#EFNAU]S¾ŽÛ{7CµéîÇ ß «qè¾ãÃ|ÇWf¦},aqI4¦Š¦žº¼äÓô?™Ø¿ž²T—ÀñÄ_=KÐŒ^(ËËÈ*(å#Ь¢ªž¶jÕÐ>“Î×ïwþù °ñ÷è͸†—Ãhݦlë6å{ª&«õ™µ½Ï,T’÷1¿¨ŒÃÇ©t†œ’ª–jCcÂ(ê}goï;[T0¯¨Œ[UPMKUúÏà¿ÝŸûFûÍkRPÓ&þ~ï:~ŠŒŽ‘Ù7Cè~Ù‹"¯®'¯þ«*û Õu¨ý?\€Ÿ! H)i@JAHR BRÒ€”‚¤„4 ¥ ¤)! H)i@JAHR BRÒ€”‚¤„4 ¥ ¤)! H)i iwûöm6›aØßn €ÿ,Çõôô¥6n¤1¤£££»uë! à·"BÚÓÓóêÕ«222»-µ“Æ&^5F„41ñ·Ûà¿LʃFCHjCZÊwm€?@CZEEÅÆÆ†ÅbÁ9i¿Ñ!´¶¶&“É»!u’Æ666¾qãt¦ünT*•F£ýíVÔICšè@+((üíV™4†4„4 µ ¤)! H)i@JAHR BRÒ€”‚¤„4 ¥ ¤)! H)i@JAHR BRÒ€”‚¤„4 ¥ ¤)! H)i@JAHR BRÒ€”‚àß*åþÉÛq8 3réíç XÿÂåožˆ/gq‘¾ß˜.榅€Ÿ! þ} ®Ìõšqoƾ‹ýZ(ÿí¶üMšÖÎvO§µœq²c¨—$¤¹IW»M5w~ÇЯc˜®eáÔ”3µ“σƒÒü[@HƒŸÂg³Búƒëäœß~òMÌû[·ÂƒZtüÉw0·¬—U¤ÿêæW–—Sä䨿¸Ö¯Éišë«m'“0ñ˜Gw¿ŠÝ´­`è:Õ/¦*iš™³Td‰¥±kæU–UiJ²ôúã–•b² Ô?ùß(ŠRCAŽ\Ï"8§ Ÿ¥¤®\ß2u×_’[@QS—ý¹ Ç…‚J6ÉdüT-à¿Bü0öãã›—í ¾yWWkõ?¸^ú€Í»¸~)­{úýüÛ÷å’.!\çÍ›ég£ô šFfÚ°bÕYÎÁ{Û­~(¾V3qIͺoå)µ¨Ú˜ÅëvcUÏ“Ó÷i[ÿbç´_^æ;oÞ O™FÕû”¾Ý±bÉŽ§Æ—o/Õ®{)Vøt‰å·_ìÑjÜ ò…òøžÍºôŒJ¡ñãÍ$ð S¦ Eõ ž6®§n»Càÿ„4ø¹Ïöu «ÒeÏî­–jxíTCÑc=~¸xQÌé¾]æv¾?Æ9-:·ìŸ%ýœÕå|çœÝ·Ðº»õ>=ÒmôFå³ ±ø Mt¦¿ììbŠÆ}G­ka S–JkL­‰›]×;´¬Áå<—ŸçìžÓÃFÁ°×Š3Û§2SùïSvnéЋ.¹o?¾@–•6køŠ°«ÙòÖ×cãšÅ¶ Ø‘/@ý^³ ß¼íÈåôrdÙ#ôæ‘YúDè¼ ³õš—]Ú2ôð(îu»¿Ê¨`Úö¸p~o;S¹è«›§ŒŸq;…3pŽ3¼È¬ô™C:­<ͰòÞ<ÙçòþÝ' 1íIû®®ÔTôþ²_ß8yþÞ÷\İ*Êr ‹ƒö' M Ë(y]›14dï´Ñ¾öžƒg… ö·7Ñü®„å–fEÝ=¹M”QÂ6{¯Y1Y%nKãÖí:õ¡œaÕõü…ý¾æâ=VƉ s„]*%ËÒ¨TT,¬^;ëæˆ.Ã?ÿ ßtI櫹rHtP7æÊ¶I!k¢‹24*C^¹8!§ªå£ùvÚ•H¡‘Iüò ºåÂ=GÆxèçG4*ø:Žd'øß˜%ã5xîÅ]i8?îúÖq3Â’ *Ó´ã†í«ìÔ(²êþwæµiâ(7ë}£çLìçkk ÚøÄ+K}´dæÜ³O“9BŸ'4ë0~ã²ñÍ´™±†øÜÿ‘‹æÝczwý²Õ{õGÝ»µÖ³ÖšËsÓ_Ý¿hÅžó^WÞW¶Ò©ž_Y;yêÆ›•$2Ë#™õ||u'eÑàNËy”ô­–zdõ†?{²´hÛ 6Ë’(ˆ$d•òÔ‚7^ÐÓË{9nð€­Wâô:O:6ÎéÐÚe;žšž˜§5/twÂÆZL¤šÌiwdïž')ì©ÏŠÖ8)•>Þ7xÎÆ³w_£ ðcþkFzLÛõɘõébúðÚÃŒb–¢e‡UÛ· oc,:RŽ‘dõœŸˆžwnø°žzíBfÍèé묫ð»Ï“€iÐX¯N¯˜¾ü@…~Û‘Á;—ú8+×ø&±ì·ûàGAûûœ¹náÞyñÔèy.›¶zÞÁ\é² â‹•,§3qóÓf§XŠ€§î·ýÆ>ùÖ×de³GöŸã3bÁ«³KG,›=¼ëÀëýu‘Õ¸˜3µ9 tºÚÙi\yüážý×ÚØ#ySŸ&&Þ:›ˆ5ÙÃdˆß½LƒÇoÜÑØWðÔš‡¬?:·2>¤×ˆS¶o÷ÚO½»¿Ã§ûL§­/’Æ8’çÆªŽ~3Þ´qûª£¥=tõùÞ!o¯œÜ7­ÏºJ¿ekWz4&©K.®›½xÿ-· Ñ›¯ù9›ù‰*Ù1¼cð¡äe—Oô³AœÌ›!æ#ÙD«Êz<òHü\5¬8¦OÛ}ýfµÉÚ®m9æMœ™­²oNfvàÀc+ÞíÕoÍèõçßmˆPÞêþÓOg®¼öd|;S žºg¸Ùð}’•1´}ÝØnÀW`HÂ,éÖ*Žó›,º;ÕG]–R‘3Ä­ÉI¼ªq™áÏÚo‰ÛÙE‡ÄÍ™ÓÃ6¸ýd_Ö)S÷~ç.ä+›Lê¹êäÞÁ-q‰ˆ“Âè­m:Lï´÷Î¥î2œwóÜÍ:u/ŽøÇ^MüQŒ'l¹1xfô¥{Çv[F¶í±bõ"gÍF¼xܧý\ÚĸL¿ú⪥½øýÃ`÷6nqâ¯o°´oï{nÀ‚£÷ÏÜ4qï¾hrÌÁRÑð¯CZøa÷ìi/§4é00ôô3›/þY“vŽ˜vuͳä©NJ¬œ˜Qã7Upø4U‹eÿl½¤ì[é>üþõõ$œØ†¸G,^¿>Ôœ„¸‡'Ùè56 2ÜY½ù–Ë· dtÎ&GÝŠ¶n7hBdÉÓ¦#Ã6Q‹ü&ÜY™>DS€0r/mç¹2â3ý nCÎÜé2TYõ“F< Ùy%â¬Õ5•c×- •áÄ-Øw”o;Þ½WcÝ>µSÞ¦ëñÇÉOoÙ¿ÊgÁÇVƒç®žÖõ§Žé€ÿiÐ0!·p®éòtSžô°¯õÛƒÏáò‰.ûð)ƒí8šFâH˜(ür!Ž@ü?Ñ¡EœËGÄ\c..¢ç¬'ïÝpáùdD„4Ñ)䉒D­¹w»¶6¢ÙÍ­ƒ¯[ò$‹èx-›ÿeÅ¢JºŠ¾·Ÿ·¹¨Û"´Ó÷£ÅéïKžâ«[aùÈfúGqžÐº2™Ñsó•ôÑ6_mSÓ*pÜÊÀ1 îmïcHñ_tóÔ|ïz>!ìÄkmZú'ÚL¹}óe3ÍZz?•/N-;ûLÇwïT ñlºNûÑ®HR²ÿ¼ªå´M LQÔÓÒ&ö#ÄÛ®Ú¬]»¶MÄÛn3nÉšÏÞÑÎ{zyûµ7ê>¦ú™‰7“c~y2³’+Ä?=à[4¿H±ÿ¡ùþ’!t¦½ÆöÜ(LQ•ͬš K‘q,dÊDTЍn™Œ‘«Æ˜ÝY45O7h~Og&1‡i2qFËe}ï?ˆÿ`ßÚèSm úMúNÝÐwbÉåÕC]´(÷<ÚìZÿnNâòà‹yê«F±TÒV2ôܸ+ðŸ€=û£BµÑåðDï¨Ö#'&þ¡º7³|%ø*¡Kn¯2÷™¡ÙoÃÕ'Gôd¿]çÉ“TDR4·6`jÚnß»Áî§R1ÑÛØDDèv?’r³ªÍÜÑ ¡sÑ…ÈYÔ#¯‘¢]ëIF*Ry6fZLÚñ0=¢,‘Т%Ø\¼ÆZ…¼J>þigB@Âqº¬Fó6¦ âín|`÷ðÃ-×>pxˆ[0ó‹í¡š¹, þŠ^ÏÐQiͦDZW\Õ`L€@¢È…,J^{lûâéчú;)Ö@“¬Ý™î·ß³ …†ø\A]óeäˆÐàuÌþ SŽ„ñâ¬â"„Ìd«ßé$Š Cô$»Ö‚¬¬ØK§þ9x"Îgð‚ɽ[Ôÿñ ë;Î]8eÓÁG¡‹–ØßÏÕüË ƼŠ+.G|>=#Ûmgl%'A…)"¢ÒsK…ÂòWéD#Õ…µoY´íÑÆ¼}Ÿ[šu÷mÔÑxÁ›‹áˆè®W×eé""ßeñ…üG1™Dƒëh@ÖµÛ|¤‘sþèAy"ÝÈ Î›r„rӊʾ©ïÕ¹ãGŽ\Êì2jéˆÛ†Y~æR:RVÔ7üÜéVp覉N{òqQ}É3U-¢è4oñuyyÇ«$ì»rrþR|P¿^žv:_Χ·p4BûÃÇuY:s€{‹&uŸ3gçÀZF­«Ëôû¾é¾ƒ›ÿÄ–«¯MòŸî~e¾!/fvÿ9”–—¶úô].äVä?8:zÒ¹ŽóÃß_0ú΋a( :®]ƒÏt ~}tfwWõÅþ³¶¯æ¨#G%c ø€U²ˆ.â—…ØÛÆ.}QB>±}}WboAXpY¥Q£©T:ñZsʪŽú7Ô2QW­€Å­u晥ëo¿ÍYýö@_KÑɇ¤‹Ä¾EAÑ•—7éÔGþUÄ ¹å97vÎ3óÁ€Õ¯³&4æLô§­Qd`¨ç󈸪~ 8ÄnŒ"£ñoU#ï! ¼‡Ì¹¼b€¿¶Ló1kÂæ 1UcŠ®Þ&©tœq°`ƶû‡VM›²âàœŠÎ©›¼¿:² |vüÀ© #ÞŽr=¦¥iþηne‰Ñ™42Y”ÐB¯Ï~ƒ¥ž±< Ê>®×ïH4É{À¬ô#šðe jï ðc¨®½Bþé2*):¯«ëIÖÐFèIjÚ§®ão¼Ýîoî;üïHôŒŽz >.,$/÷y.¦ÖfÇÍöZò”:¾cYÉÛµž¦7ëâÉ Î ¯Œ¦ÝcòºÎ#ó#ïF H¯]'•e×îÌÜù¼ý(§šîd ”èœô´” äfJä!Ç#:²õÍÁbéꪭIM:3ñö²íÄ÷+‘¡‹Ž×QШëHÓ6¬ ¹=wk;QLS2¢£¢¢j)È"ôòUêLtÞ0š€S>UDaKås> û£tñŸ:èÊõÏžÏw«¹}ÿCw6YiÖ}o/œ}~Å q›CÎFLtùzOÎgÑ$ùÃSn>}6²•¯ä™ s×#ÅVc\ °/«l3bñÞÓ2Ÿ\‹æ!áíÕÓ²œži,žÅÐÓ”E:æâÝ"2•$D•‚ ñRÔ”h¨ôáKþèâ7he ªw¯‰D¦!T–]=ª&§­ŒÐÕùKDÇKHTÙ¯¨cŸ=|ýbDnש¿ ñ0F›‘«†7mªZïÉðBü8]ÞÒIô­Í)z—–•ÏG(')1S[K[MAü5e;o¢•ׯm†æÏÚØjóX¥i•ˆQ“Ø\I1íC6Þy)oKmh•)É)%lÄ)x—Uê¬É&'¤–_néIÙ9ÚšŒŒ´¤ÌRâ{;3!-S›‰å'æ"ÄÍý˜’bfn¢Y™—’K”--LNÎ4h¢Ã/ÏJέ@ÜÊ©ïJÜ›Ë(©©cgûÙ1ªH·í´+gW5Q@¦zÖ¿ol~’‹&ŽzNfZ4§ rdsp°ÇÚÁÇ´´;çi]ü195±ˆx££>:ÙȦˆ·]ž”•­¯¥)óñ}òGbÛyY ©™MŒ}Ž/èë1󸿅Y``{5 ;ùéu"i²S¢2Ê 5ù¹ïߦU²‘ äc\j¦NÇIû:ßí±Ã[öy—!-˜…ïc¯½#ªŽŽÉòëÚÇkÁ¹ˆe/:ºs?Æ'gÉTúæyr;39­v]´Ñ¡ñÇÄuÒ6i2ºŸeÏÝÓOµ [à®z¦c§‰Sšë,X2Ϻs`ý/Î.¾}çZFVéÆñ <[õõ‘ “Éw¶„·Œ/îÑLéù¡Å³Â5Ç®\å®Ï䦤eˆ6-1)SSKGõ;ºé y6=‰n®¢BéPçÖ‚Õ!nú¤ˆÃKÎ`wZŠ– 4éëª6íÞ¡¾½ñf:оÞM õWÍ `äÆ%äã¢# Ï_xXâåÉù\TYý6!ÙÔÔTMFô¾6±leF;2ÍÉ;-ÐF·ÇôYÍÚvqVØ;ÑÔø¨¯½:¹$?ûM1ªü›ænkD„3VYœvõü,5ò‡ˆ£Ó÷D·~xYOÑ!wªœªK˺n @ið Td&›tÙ¹«» çÁ‹wm;©U‚m³.ân닉¹ 5Ãm4Î-«L‹ÍµÖ¬Ôrß¶ËCPö4>KÍY37î{̆$ÿELf{^l2iÎÎ]ˆ—ó$.«³ªZܳHÛ©;w"^Ô‹¶viT‘ß®]þ\>ûyT²±R\dšßÚ]þBafl<«‰vQñõ:o—.à§ä³œ;Op¶yc7f¸ õœpaöóóv­<ÉõÙÞî ~LßòzèØ¬û¨šŽ•äëΓ”ì¾É©ê7so#ºÇJ¿ÝÏt:>z›-QwòôÖ(xyýÕ;NôãBM‹˜D|æÎ]¯àI\fu¸g/¬&‹¶ýUd’‘‘Žõ˜ý±Co?{[XÁ•×±™1mä•[ÑœÊü·YåŠxüý·Œ¥kwâˆ÷òÁKÝÞZ:†­OÞ ¿}ÿqZv ÆTo:r昱÷c’ ß}ݯï‚ð«Íï¼þÀ¥)Ú¹m²¦§_¾Y{?ÝÒÀFËdý½{Þ÷cË‘¼Kk*ÑÕ×^yòq·û÷cS2Y|$¯nØÜÍÝR£áó☌Ƭ G‹ºt{@®eÀ¡Eð©¤Vwo‡¿‰ŽÎSôœúx–—«¥h¸@ÑÇF‹À.A‚ì‘©í´Uå¿ÿ¬ ÍgÖ–í"“Óß½ŽFêmç¼Zåc©.éÍËM½|ÛðÌÃU×¢yËvÍ/œW¹þ4‰…1ÌW¶4å]:}=?íQœ‘rebFÏ»ú1/_24M$!mä9àŸ Ê/Ó I²º-mU‘‚ÌÚãwÛÝyYÆÇ4Œí\[:f^Ûý"›s?<ÁÖȉî•N.ÏIþXIRvÛivWÓïÞð Bü*¶FØÖ6ƒ¤Ü¦ÇÀ6Ÿjö®¾›§ÅÈš÷õTë5ô‹«–{û¢:¿ !5:yBÖµC?ו[Œ¬q¹ÎÒV_ ®ÑçÔôʱy‰/ÊQ÷oØM–5qka¯WîîÔÐ’uÁM}º™ú|S±µWk¯êGÆÞëÛ4´IÍå|{ þª¤†­GÛϯÞð៎Ãû ³øz=4uó5~óʦŸ›wõ:Ý; sÿ\ë Óϯšª…ç@ Ï/*¢«¸úvsEß‹¤mëe#ƒò×5à[¯‰× &^_=©fï?Âþ»Wö9m ï.޵͔ìzý|y‚¬C»Áí>= 6¦jª©±ï·…I²-ü‚j^¦dÔ¼ïÐæŸjw]=ì±WHSÐë1b”-Ò?Bü·½;³;­õ1“/žûôÅ3jÙ¡?©Áº±eɺkåçöuÿMíûoã–$mž6ñšÇ£¾½æíÿ‰T\èþ• ¤Á›žw7õ ³–úšmèØÜcçGÝ<>gÂÄ ™{µnDq¾‚¥ÿ®þnúŠðIù!d†û°u\¬þ_ïF­@#a™¦ ¾š –ÛšÐøêÿm´ñGc•WLåoÝ=ŸMÂ)ºMw#*ð×àbT*UUU•DjÜÙ ¤„4õ º}û6ñ¥O£ÁoH5@Àår nÞ¼ibbÒpþ ¤¨Ott4Ç#&8Îßn h†aùùùÙÙÙÒà?B€ú@ú_ÇqâßKF¦¶Ÿ{àß B€úÐéUwŠRU¥d)/Oſ吝ÁoG£‘K‰‰)$¦I$™\çï}ð¯! @}>E²ª*cÚ´&ZZúágðgQãâŠ%! À „4]hÀ! H)i@JAHR BRÒ€”‚¤„4 ¥ ¤)! H)i@JAHR BÐ !ªxÿ‘ˆÿ¿„4RÿWJ~Ðãäkq÷ ®¢îê¶ä‘aé]ÝÂÏ6e"a=¥¤¦ýü‹AH UÈÄ_efÖ±ýï(ANL)âgxâÎ+Y{¼êð#‰?¿’,ä#q^âÃMü ·ºBI HÜñåW?Y³luŸ˜˜ÄèâzÕs%Ed*c㈄Öí·J},–oi®i¦Aÿº‚êö YÉoןvln­J×i À‚@JˆzÏi‰‹æ<;^ÎãáÛ9{Þ¹ß"$¡é€¶Óì?Žš™X,§8û°ï4O9Qæœ<4âð½"ŽŒ|Ÿé.k'É‘p¬2·½ÃˆJÚÖ Ýû;‘wqÃÃYk’SÊÈ–­Íµj¥Jd0©èmÊø‘Ï.¼b‘Œ¶Cö­°P ²¬l²yfŽéÛ˜&ÛzßÙQaàm{òB«Šñ‡vŠ~ª9oß]ë§êK}å§mOs tuÝ­(ƒpNzæ˜÷þyɱlßôì,¾£ãKãï·Ì˜’°hΓ6–kV;v´—Eõõ¹u‚àoÃñœÔ‚»ç“V¯ˆy™‹«©øÚ™Õ¬£=JŽ”•óŸ{>㜧ڕóYÓÛ\±,íÓ¹0¥©éhLƯ› --ggÈõ7…~ËŒD}a:‰Î'‘I¢zOO¿¸6ϸ…A 1~ãtLkÍì«®IÏÌ<^–ª)øw6ÆÒsέ¼sáVá»çnúN!zéÙ}sä›k55â<¼ôzàP¹ý,{&FŸ¸YÆhf8¬‹–NYfI ·‚% zÇB6krЕ}OV-õÍKR]»–!¤ÏáɵòúðT?tYÜ—)šÄѵCB삺èÚëÒþök À¿ „4Ÿ½züõµW]Æ9Îì¨ÓÜAÍTSrØ‘0Q÷š¡©xäZÁæÁG'(ÞwEy‘+@öƒ<®í7G¨hÇô¨HŒWŒCyÓ©öe’¾ •½_´6i˜ßzîc‚ø»_‹ç Ù!¢ É2 vtXØ]ñ‹z;\8õ&ìŒýÊL’è 5Ën}Ÿ]UPäcz‹ÈøÄf§ëerˆ6êë:^%ayfu»Iééçžð‘ŽííG^:ˆwyÐáŽ%G„zÎf[Ï›•fEEåÝ8“¸yìÍ£)^ïÖšÿµ€'iþ6çâDÏ·".¡RnŒ¢‰GñÈ1Éð1¤©£¦'#ZLÝ@¡òŠBnRV©!Cw ñ)jÙQ«ZI*BÙÐBYœÍ.—ŒÕ ßeU­å™¢ž7I2P‹D§ˆ†œ• 1q1AAYB®@GVHŠ"˜Ï“D&^ÊUK¡ú*ÀHøçuÕD¥KqË…â¥1nIõÀ4/þyöƒ[é{¶Ç?ÿ(ÔµÕê7Òªß0ýŸ©ø! À_G2u3Yåf\–_ñ>)ÿضèU3î¯E¤×#¦T}@¿ìƒ’)®–*T”Ÿ|6¾lJynJ§KW­3“Z3禦q(í-,-L;P_^zt±¤EWEΖ7çGTNÜä=Ô›Q~ña~ƒÊɼø¶”ÈeÇvjŸÖÒÈ/Ž„²&º–(öíùè{›6äâ‹s%Íc½|éâöº ÑÚr¼6̸‰©œ¶ Ǝ𠤢d”W“µS“[êf²toʼnå‘94\(D¢¿êØ”0àX‹‰M·g¥¥Ë-áò)Ìñ[¬µ‰ä”-ŸþøimW K5ÕE{mž ‰ë¦´UQ\R*е5ê®gãçsÖðÚ‘ Î…Ê¡ìr k9#`œƒèÒ)üˢϫFÕá‹ÿ/n‰©­;¸¿âÜÃ9^”²tªŽ¡rbYNaÒz¿™“L˜¢……Õ•¾„4RE|á2E¦×cW{Ÿ¤gi\õÖ½ðGíÊiHH·¶[lÃ-ÉøyÒH!ÉýIгõ¼®;Qþ3tõѱ÷²´÷’CØ:Ñ"•ÃüÛû‹¦èÛ,.4‘ø@ i^]B/rÌ\/MU¤¡‚`„*D©¹2 -z‰¹hôºÛ£—(˜i¨ýYô0ÍhŠæ;¹wuCL¤¾ qÐýÎ!KÄõ0‘Öå¥Dm3›½d5ÓE4„…!QŸ~ÄʾW÷½–ÝyÉëùQD"R™êaÆ„xàW@Zñ„¨ß”9!êw )ЄúžBøÑ@-Û½è´h1ß+M˜ì@ïUßk Co в(ÄŸ$z¦ë´ÀåT"±‚Qr.Zúµ×Gw2ÑZ7d«…v¼B—Ò.…¾ByCDÁt1)ˆJB½OV­Ñj:ç[sdý-ÚN4,!—bžªl.SœŽR\tKå ¡øe ¤Vå<4¯¹øl. é€6E">ŽžvEx¹hnl´æ¹èäpú@„—ŠžyGLp«Ge“Ѓ,t¶“¸Ðzwtñ½(æ'6CˆÌ”PK-ô$5WC¶ªqШ&èPzWŠ–;‰ûÁ,t<@¼F!Šì^µÆ·Кg¢®uú ª5~4$,fHgÊ _ÃÇ”u0y‡ I(@ n À/! €´R¦‹þªpл/gsPHñ¿:˜y5®› ±¶¢e$Œå«‹óÄÿ"/ÑŸdZòü€¯î4Rë›~¹"!gCüolÍ¥¦9@Bð«@HR BRÒ€”‚¤„4 ¥ ¤)! H)i@JAHR B€ !²ä8þÞ*ˆ¹TÑ}Íðš·9übÒüËØ•‡VÇsðÒù]Ÿ_NNѦM©mg6w”¯ýŸùâ\¼ïg©Y¾ËôÖªºÕ(à׃à""£Šÿ'wFâ ’ø«~^X£Y< UýÎ4Ž#ŒÎM‹3ÿåüîDH“¿ùQH¬º6$®çÓ\’¸*ôyŸ["ÏÂ$ˋրÑR#⧇ÆíŸîâ(êL ¿nÂeÔÆÌhR®ª-_µ juË?WõÍ&|z’"^F²^!ü®%õ€àÂÐÙ~ÿLNT9²Åxç¤HÔ±ÙŽÙ–XÖ‡Ó¢®½aa F« ¦ë¦S«Ž3c)÷â®|õÇTWè>Öif êí #Nf±¶±Ëé‡Í-žpTú"ä¸6¼Øx4#‹Cmêmºª‰1™¨Šœ÷&eþü7’94Ùö× ÑAÿâÀããcåwJÞúlóµy;ÃuûÝ™¼}ŽM¼[BÄç×£z8l[j~92dUJb>_É\kþjg_s:§ üñÝJôñ“Leo{À±eFo[óèxd¥¾‡åæÍFQÃ"OE.\Ÿ’\‚Ì<,¶nuÐG‚狯:[6x|³’k¯Vj<×ʈ 9 @í ¤ø“HÜ÷…ï_VŽóOOAoÎKˆnbõ(BµuV§få‡M½ºç‚kæ½fJ{úc`_žÑ¤‰bṅY=Ó—úÏ6URä¼'*’Ó–30gRjþÞ”€¿mÔÅà=ùªz*&ŠìÓëݼ™÷ôE;¥O­[FÊЛ:ªT¼Ë zî̽¶oXqÓ Þ¿,ñºðNII‡]ñàä+¿aâkwUs%ÙkùD}Šzò¦ÌŒƒWM½c«÷p“½z4ÞïVyT^£rNtdQ¶Z™øŒ4V–Rš‘ú~ m\Š’ )¯äõöÇ%Ú¶ow_m1"UÖV¿WSìÊŽÇ×J?¾kƒå–Ä¿* ›ñ˜…8ZfJÏÔB€? #‹WVy•l±ÃPÙ ›Ãiˆ>aWǃµ'¯k³ ç<]uÙ*4 "$(ŽER;Ó½»½‹Ö6½içëЄ®a± ­¶FïjbE«y@»2ëÃÆ=ùHÃ*ꃯ>*ß?îÁñ\rÖûìÅ‘HaÓí®ãÝPþ»&&×ßœ~>Õ”":ÍæöõÍ[iRßš:ÞÉÈI{”áÞy±ïìØÔag¨³Ïtê'Ï}~²lÓvý–~¦Íänêïñ]ùaS$ëß$ÑFqKsƒÞ×Ü¿Gký:!»´TeòˆT¤e’ÓIá÷õ϶Y»à¤ãXªè%`Yê<¿ÛÖ˜ŠÃánê! ÀŸG¤…!*Í¿–#†k3EÑ4ÓåÚš2ÏÇsßG!Ûì[Dx¹h{j1ÆE&zí•Ñþ VbK‰G<ƒsX¼êÓ̤²„ô¢‡ík¨/úiÊ  mú"-/¡[1BæJ¦4Q=jê~:ä7‰üÔÔr3Q'œÞÛGYô¼†¬œ&ÀÙ•¢“Älñávv9É“\e6õ‰˜7=B @|žhF ǾÝ(ùQ]䉪È6ªÄ2\œœA<÷ÞFe; Cü .ñ(-<»8o妭KÀ/OP?iþ<’,E|݆‘Fä^5V ç Eç­1 ‘0QG—W_à„s¢åÉXÕão¯{’Ìð$uaÂJ^Óˆëùœ†Õõ$`RUáÚ+äæ¸Þº’‚æ¶žÒ]ýåŽËc¶UÔ²¨™*yžT5£’©DꪣGÈQDM'Vjâ¦D:-j JK·h„4AUbÊ«¡WQ•÷žõm¦‹Êò¯'–#³m©Š è]˜OŽEf\ÎÀÓ„‘)—KÙRÎB‹–!JaAN~?]Ê,”³4¶C 1—’Þ {T¶¹ï•Õ)”M‡]ƒ´ÐæwW9M5QÚ»ËY|L‰ie-Ï©§y¢öñ‹ˆúKÊ㪳ßä¦ÎJEGïTˆf)tZ ‹ q’‘ž†.•‘;Ípq—!¥Þ?ÍÒÔ“§~ùê! À_$3ç’ç1ÝÛ»F»¶I}(øPŠL‡ùMoE|0•–ŸmvË/jéîÅV yïŠKâb#KæÙÊ!T°ÆíxtåÞ.º$É9]¡Œ¶îÔñZÃ7§9ÉîÔ’¦çptlíµýnºŸ·˜åqx‡­rEja µ ñì I9W{“ˆì$©™1*›nz(~‚‘™ #5‡5±ÕÑ9s‹šši%$_ê{~î@C¹öòÕÕÙ$õÕGÌ®õMn)·ÏÁžòº¨Œ¤qgt3 ‡x ± ¤ø“pÖ†^rtÉGO€t,_å+® }”TI¶Séc7¡‹ºd,•‘¯KÔ3µ ÛSâ³Î- »lÚÓ‰ÈN¾í¿•‰O#2xÚJr¤G"Þä¯ß,úÀ¥Ü|>%°•IÈ4KÑFìš¾NR^·.áe:é¨ÓaH“!m‰Uh·2ôbPÍÔÄ=[2½u{ !MWVt¹vÀï‚WI%B]+£°{úóçÇFçcM'v]9Bqë4ìF"OQ_Á»“~!S•!¾ªÛ´nCEMšøRoš¬_GÃr+u&âk÷i¥¤µñ@FfjÞÎjÒ{:?Ù^§C'y'Y :Ó4B€?IèêG4ñiH3Ÿ¬ª>mmÛiÕ Ô˜%Ôs2]ãd&žÆ«ïC‚(²òÓ·úV?YóÎ'¢™í†´h7«ž+¹˜@ÉLoñVƒOÕJVᲤÃç–Е–ï“Ô)z(§§±ú@ûê…ѪÕÓxðjï`Éôx‹ê{› .a»T•Å‘¼Î¾‹zÕmÃí:Øíê`_ÝÑÂf#ܯŒÜðB€@Hð‡}{ÅQ=—! k{²þË–_DP÷_=¬uº®¾*ûm{jm! Ò€”‚¤„4UÇõÁ€ÿ±wpQl{ÀÏÌö.°tJ‹ŠŠÝÅ5°±»P¯Ý­×gaww#v^åÚb‚­ØEH׿Ä;»k`×Â.ðÿ~îã-³³gþî.ó›3qäi~ Ë"µšQ«áxªÑáñh.ëù„4?åÙ³t?¿½$ ½i£C(5Ueè*ÈÒ|JõníOQlB‚°ŀ¢(J­Vº ôB€ïÁ+}C—~«eè*r–B¡˜:ujdd$©•ïÿ½†‚ßX¡P¸fÍ –‘÷Búرc'NœÀ«N{{{øvþ<üÇüìÙ377·   C—“g„††r¹\÷ã¹ó†ap¿S àÇJ¥233S*•⬡ëú4M㋳³s¡B… ]KÎ’ËåaaaW¯^ÅÔø4t9ùAøoaÚ´iÒ¿fÁ‚8¤ ]EV±bźu뺊¼o®Ô2t!9¯ëÇçííÝ»woÝ”sçέ[·nñâÅff_¹%0xs$+Ks§Hè…ÿüñfŸî­6 ¼Òނ߻~Þ×îߘOœ={¶_¿~8’ñFÛ‡‰Õ«WÇ<%$$Ä×××€åïàp8&&&†®¢@àóù«ó^H[[[ëà(ÏùÊmuÁ†¥TïFjÔíÛÞJ;|øð¬Y³®_¿þå—¡dÉ’W¯^­Y³æÊ•+Ë—/o Á}ØÔ.U5`ÔúÃУÖ#ž-ÚëÂu†.ä¼ÒX9˜´TQ ÊÃÿ„\óüAâÞ× ]0<¹\>lØ0Ü ûÖæî:9r¤wïÞíµr¹BðKšRÈðOCבÐþψ.Èà 'rÝŠ[‹Mø†.$P*àe€²²²4h0vìØF}N{{û}ûöÆÆÆâPÏò_ÊÃ!­Á"8À Àϸzõê€æÌ™S£FŸ™Ÿ$Éýû÷wîÜyÍš5AAA9]à«òxH~¬Y³>|¸k×.—Ï­¥;inÆ “&MêÚµë¦M›r²@À×AHÏuìØÑÊÊ wˆ9ί¾–ËåΘ1Gu—.]Ö¯_ŸG/¡ ï‚?9ò­èèè#F4hР_¿~ÒN÷îÝÓÒÒz÷î=þ|sss}•ø!iò§ýû÷ãL]²d‰ÏŸ·6dÈS§N5nÜxÏž=vvvÞ àg@HßÐ4½páBÒçÎÓc³uëÖµ²²jݺõÁƒ¡? @î€ _IMM5j”­­­~ZÇ××wöìÙ-Z´˜:ujµjÕôÞ>à3ÒäÑÑÑÍš5[½zuõêÕsh•*UÚµkWçÎ'NœX¥J•Z @B€|wÇŒsêÔ)GGÇ]µµõöíÛëׯ 7k GAHç±,;lØ0™LvàÀƒÛç( ‹ðððAƒEEE ><–@Á! @ž×¤I“-ZôèÑ#7oÛebb²jÕªÁƒÏ›7r qy1š±¯¿3$‡‡HQªïÍó±I.â’š òD ¤Ááp¹ˆQÓô·¿7‡är–a(êç†-ÕÜ]œÄ+X–f(F:?+22rÈ!ýúõkÛ¶mî/Çã-_¾¼[·n#FŒÆ¿æ~ À(ˆ÷x|63È¢-¦¬Ûk#üú\ŒòÕÊJ.‘”ïøÈH71ß]ÕqMЕ–Ī{d“…·›T+^`W‹Ò¿ˆäĽšîÕ¯vó<õ×n>Cpˆä“Q;Â|‹6nëÌýáðâ¢ããnºû"VeV£t׿9Q8È6lØpìØ±uëÖ.\Ø€elܸqíÚµ]»vݾ}»Ë¿à!‘! )ÕH(ÒÜ A™…hB3÷wÕr¤T½›“ÃG|!âšÛ%PJ¤Tê^x"Ä×n›Ñ< þ)11!˜!äéHÓã ‘Ä?äY¸›Mâ§P²Éj–…ŸŠ‘f<©H¥ú´8q$ø9s/ WÓMWàò´½#Ž HÛ±A*¢pÓ|$áî^âqñÒY&_ÜÉBú×!‹|7MJ“Ä7ú RÆ$ݽüFiZˆ!ˆÞ„`Ô§7^;q<Ѫ\ñ ^ý ò£Áƒ?}útÿþý¿1اÞõêÕ ÿ¬Q£ÆÙ³g ] ø‰'¦O9,»¤c½foP›•éºìh±”eãGMMPXUÞµMiáˆÎ¼2iÇŠ%±©iâQ¹çж]þ°xòó#£Ë­¼Ì…\$ž•œN“<®uaû¢Å$Œ o’LVú£ ‰iÉåZyÚ+nªòÁ—üš7oÞŒ=ÚÇÇgÑ¢E†®å#œÓ+V¬øùÛxÃâò\ÊŒ^rÁ®^¿*žeZõ¸²wãÅàš¥“jz“·–×_²þ¢M…e</o¸°¨~lìÆaãº^ŸQáà©çÊžÚ°þ}«^­!®f]¦ý oFjþ—½[KH¹tR÷2ÛÍÆL£n®>2£#êä_ü‹ÃˆÉÇç­×¦ŽÅù³'¯îÞxÜ‘sJ­ÚûܽÁ¨Æ%çWO]Ó¾­ÅÅÿ¬¼Ä¨å¼‹ViâcAþÔaoãW0BZ@¾Ú~qç=уƒ¼èx¥"M‰¸|ŸöÞ‰‡ï¼I¡ñ›PvhÝžMðFžüèäÿŽ_J”+‘±JÄ1ó->n^Yifê¦IáWnf0ž˜sÍÄCÒDæ‹Ø=ó®dx”+WÏŠ§&S£¢v.I-báäçú>¤Yü]äfÝ\0,2N-træÄ=Ï”V®8anqâÝ‘g’K¼ »wÿ‰?~| RͯìcŸ¶¢Ï¹ j$ä#…Š'”h_7¨›=‘ôryЙ{oßMçŠEZ×îßÛ) ð9Ïùóçûôé³~ýúŠ+º–Ϻ¹¹µjÕêðáö¶¶†.ü Í©†<¾S³IË^u24C½9ƒ×l…‹[ ‘ZV¾ž¦(.%.ñÅ¥$üb'W3¢(dí]øÖD”"9ý ^UÞšãØ~ŽfÉã¢7W)ä€> i+O7GF‘“³¥èv‚L–‘¢ŒÞ»xÂÿâeˆÃå±ø9ÍP¸ïæ–˜—µ³@´Z?ïŒ1(!ý¿fkJNÛ;™ ˆ7B'OW+I\]Ñí'É Ü}ùä­ŒAþe\Ä*y&*ܰ„鱫¯½|Ù"E†¿)µJØ“ eY½•ÉÕ ™?µ@¼‘÷újBR&jÛµŸßÒ‡wtW¸ö,CZÚZ"”zãÚãô²Î¢SûO2_ž9Ãå{¹#Ñ-å›×î5ÚJÒon: ³ô„^åJ³Ôg³¦žÜÞ¤n1ŽSw’’H‰½¥%'“OòL-ÜJ–°"Oß×ôçÕâptëâ|±;›ÒßE6–\”œ§è\TÀcÓ^Æã>°¸„½­¹÷\Ro'(8V¤x~)]÷ ’C’8çSÓ²XŽ Wù""ãó6Y–´²¶Gè¥Ø²ÍäzîVdVbVJ:%0s¿~¾KX[»ñÑÝDYBŠÚÃ^„ÒÓŸ$ÒH(´òvvܾ•¢HHTz;IPVæÓ ñ9&ùl³|÷JGŒ±fÍšråʺ–_`jjzñâÅ&MšÈårÜ«6t9à†RãN¯.YFs"·vl‚Õ<Áª†¦)ë†óV?6ü@'ÑAw<Ì ù6ê;ÃTlÓd|»{Swìi/9`bãXÄiú#JJI”Ô÷Èð•gú;DX9»úz 5!ªÖvct;§)¼þ+ÕeL…³‘×Îâ7)Ò“âͺáÒ({·ƒRáß$ŽÏƒû•›@ÉRGR¸ÑôÊM«EoZzñÉÅ-…*•[ý¾&ÿ®¼ôw ó:eiüò|×oþ€(RÃÓsÛ㇗¯¬ž–ìÀ“ß>ùœÙtîm眩2F'E^[;-Å49.2Z‹,kfgf"EI O·MåZg%Ü‹h®7ü$|YJlß¼£Õâm¯V?ë[BøìÌã‡/ùÖ7¯æN|e –Us,[ ÷¸üt笳Ï*Zɾ|”NYU÷.VÊÞ}xá»Sï›wöMeÅ“×wTfÊ–+#BNË·&OžüòåËíÛ·*TÈеüŽýû÷5jذaóçÏ7t-à+ÄÞM:;°¢âB 1¬yõ6zR®–DQ‚R½™µÙy;áŒYñ A«<½w7SNñ$¶ŽÅ«;9hrØ2ºPç×q©S·b%m^ºÆZ¸˜HXméè5M_Ŧ¦nÞ¾®ëžS™:˜óAZÕž´¡„ÒÙŠAJQ™^ëÏÔ¸~%9%ƒåIí‹Uru±É~j7­@®½wtK•­X>+úRL¢\h]´hŲ$‹º¬+{-2Kŵ,R»˜;÷AݺiJÒÎÎÉ¥jC®´?ÅtÁ iöÃwØÏŸ$mìmirxî¥ð÷ïÒ¤cÅb×q·& ;×ÁÊ,޼õï#Ï&•‚ú?[4ëµBÉrìƒFù-›qýÞ‰è¢mkô x9mìc…š%²í§Ul‘®õûš^ ÝøäØ dêîÔmmµŠîÄWG+c5û–h›zÕÆ›[…,Œ:Ë55­ò·›¶Ž\Š’Ôª2ÑÂjûüÈ3;â9“ŠAuÛu(ÄÿÊŽ$OtïÞÝÉÉiíÚµ$ùÕc#y‡Ã™7oÞ„ ÷ìÙcèrÀçx6Å|ý‹ii ÝêtuÓNg®_€ÝûÙX‰lK”°-ñá…ïÏÌâ:ø4rðy7±L}¯÷ÏsìJ7´{â`©¿Ú½Ÿ.ò¨ÝÍCÓ¢võÈsð¬ÔÌ3ÛR²cid]±­n$zË ÍßME ©w©ººË²5}”"5Zé»”®ønR>R BšÎ¢Ë¬ÚÈe•2µšbW\Öº ÞV”eQ i9òj—¥³2´;y$–̓›µæiŽ3­TP¸¿‹¿+VÕ}§ÿUO¥Uj5]dCk’–©d*Ö¢Jéi§Êj¦+Õ*ÚmõښérÖ±Ç_þ&i5¥ÓˆÃõéX£BÚºQ?Ur5•ý;¤v‡?®ªï¤Ú䵚B*U…äé幩¯ #æÞkàI¦*4kû ÑòeZ¦£¥Ãî8˜ÕŽÇÑGÄÑÇñŬ’]Í õÆ‚rÿþý#F4lØpÀ€†®åO±,;mÚ´%K–ôéÓgÁ‚b±ØÐ—ˆÖŒ»)S~8!›QS25¥›Ž×!ŠtåÇ9YV-W«åŸ7ÀR´<ãÃ>&+íÝ«¿˜®i‡1£Tgek•VQ2Õç§ChÛEö’ÞCÂGT!¶á»–7â)¨€ñá7Ú•d*‹¯¸Ñ£ŒõÓd ¯^•s°½ûV!â¾.c_*ô.îAgÙITb£>Ñü†ÿýwþüù‹-*Q¢Äç6zº{~ 8pß¾}ÕªU;}ú´™™™¡‹ Ï(!m¬TÞ€Z›V„u'k¶ ®v.=ºôJšÇ™wµ—$IÎ’D™Ð»Ã+®Ið´Üº¥… Sõ¼’s‹!ÇœoÄÞlé}¾y’Ê_»u <Üéœ={öÞ½{/\¸Àåæ·¿Í-Zxxxøûûïß¿ßÁ¨à§ä·AÞÂÏR¯9ÜAœ$×]Çõ¶¨Õü+½p÷Ú$QÎ÷­¯w,u¹‡/A³‚ Ínpž\½oa†KpT Wùµ®9ȳÒÒÒ† æìì|ùòeC×’StC™öìÙsúôéeÊ”1t9äÒEàœV}¸ÒšÔ…±æJ¬wS8*š£¢usêàœÎí"Aλwï^£F6mÚT³fMC×’³*Uª´aÆúõëÏŸ?¿N:†.c! €¿Zµj†.£! €Á°,Û¯_?üàèÑ£ææè,}¡P¸k×®Þ½{_¾|yøðá†.ã! €a¨Tª€€€=z´oßÞе€µµõÞ½{ñ6Êœ9sFŽièr0RÒÀ7F5pàÀfÍšºCZ¾|yÏž=gΜ‰sZsËaÀ§ ¤È=š;ºÄŠ+Î;·zõjCWdxëÖ­[´hQ›6mBCCóß…gü!ø“ ÷à„;vìÓ§O·mÛôÁàÁƒ---»víºuëV‚€[Æð¬&È%/_¾9rd™2ep—ÑеÎ;+Š&Mš¬ZµÊÉÉÉÐå`, ¤È ýúõ[³fŸŸŸ¡k1RAAAåË—o×®ÝÞ½{mll ]FB:âÝ8Ã,ÃâG$¡9‚È~÷&Sš{f|{Ü~Ô¡Y&¡[¨n~Ýc_0 ³eË–yóæáœ†ÛK|Ÿ¯¯ï’%Küýý<èââbèrÀW°j¹RÍòÅâoÞš@ʤ—™*ž©Ÿ£·¸1Šôôô4–ýx4„àð&–"}e}É(RãåJ "3saFj ^½ LmÄ"¾¾êÉ5Ò)_½=sâ%mbW¯‹[êÁ§Ÿ²Åëz•ð’|k~V©zù(ͼ˜ô+_ѧWÝOÅß‘âÞ ë˜Ðê¯|H.‘zëùù ‰2¯bÏê7¢âH‰“]ÕÆ.¦u~ V«û÷ïoaaqóæM8Úú3pNïØ±£OŸ>ƒ jذ¡¡ËŸ ¹Lô†þ¡çˆžëÖ9_ G*bt¦…ëö—Òî{ŸUuµ õ³"c§ÿoÒìOo-v*ߢé¨5e}æËicdÅÚ.ÚQÞ‹8ÚÉÿÜc§¦³Â‹·¶M²¹_çB§Ž"® ^ª9—$HR"‘ ”¹á¿È±]V¸µ åéãã÷×kÓ£læÖa­o%Vè¸â‹"lõ᫦³ãæÌ3áˆM¤xþmÇ.·ï§ÜJï3§•zü¶—xí·µ«nÅRÙŽ “}÷Ð+àî…]‹5glW¼¾›!w³}¾Šaéwç”}ÈwVû]cè< -—ËOŸ>]¾|yí_пnݺ+VlöìÙ†.$ÏãñxsçÎí۷.]jèrò,FuçüÑT96ZZÛßVUWÈ?Ní¦|röVòšÓ³O[®•(¾âB÷wÉìјԜF‹ßÿb þíÂC’®«®L p7­}­r""–å}LÔlÛŒUÉRS¨ ÒÂÑ$6:ây"òúdzíi±T¡FcG—!—þ-NÚý8:1Ý|÷½·HØ`jÝ~,é×|ÓØu×.ÄÄeµÔ¾ˆ5œ»ªº¥fwúg«BµüÅ­/>¶þôNJRr7Í>Wýß¹$Ä-"ˆtSð VdcУð“Ñå:” U9ò6çé“HE|ņž^¯ò,ìiQ{z€»‹„¢¾yËH63óòíd¼†)à,°%šT"×EÄ܉UÕòüx,„à ´»¯Ï~–®$½ÊØ µaÍ2¬ÈÕÚÉœ|qÿùù–åk9J¹ß?»üwH¥ÒˆˆˆÚµk›™™}ÏÓÓÓÎÎÎú=>?ïi nß¾=vìØ&MšôéÓÇеä¸÷¼jÕªñãÇ÷êÕkÉ’%"‘ÈÐ^|||ffæ/¼€¡2Ó_àŽ¤Ô݉dÍò\kzòPò©2„„E= ÒtX%EìI”˜ô$E…4·cc)ñ2Çï·q%vx­¢Q¶Ž‡H<3£õæ=§E¶x¶ä8ܘø+Çèð~SĪi∠i¤N~HáìkÒÛW“ \‰“¥’½o¾¸›-Þ´øÊ?EZ¨Ç´óë,ðÚ—B1§gLû{ü¶qm·„¹Û u3(_ý7µE×^ÊÛNW^´{ëeŽWœYràá¿'mXnžwvÊ@Hÿ˜E)Ï~3PØÎ‡WB#/㯦D\º]Åî }cv"ýuâ‹Ç <ãÙàCWI¤JÂ_Ù¿’ëŽq¤•ï¾ÂqwvøñˆïéØÝ¤´§k°,áZ¸Cëç Ö¼Þ.(Û¹–5î£ëùŸ£T*ŒWv8H®jé¦ã`655511Á?q:400PÏËÎ×:„ãdþüùEŠ1t-ùÍôéÓ·oßÞ´iÓ°°0C×b0iiiÛ¶mÛ½{÷“'O^¿~ý ¯$IЯ†2cSi8èõ[Ÿ¦:xöÆãŠWo®&‰9Tl^ÛX8›}<2G|ÜLJP¶„& .Rߘ„Z\eðØ&›ÓV ªqïIöy²ùx©ªöÿY†kîNâÕ`õMÁã«*å,_bB²,ßÄ‚•§igã}ë„–Áwħ4m:ÕéêÁ•Ÿšæa+Ô½‚gW¶ÓŒq«ÆÍx}óXl|G{íV‰Rî¬}ôuà ¬¼ŠûQ¬0*S·@wÑ’'g—½è-}°³¿ÿ#Ë&A“‚m~t–QÊÓ|J†XŽõý£p—ÚÖÝFjö!ÓI¾Ô«åôn'-¿ò:î­§½+«¹ÜÅŸ ÉDV]¦N/éÀWç©óÇ ¤ìõéëóg>toY¾zi RÊ9xãÓN( R„¿PÊ+¡,jØ)n¡=~MpPÆ•‰™vÙѺî!3ˆ¥T‡&âõ÷ÛcÇ243ï/˜f§LM×#GbÕÏ䬓@ûý—=OÇß:Ϣ΅ ñi:¿Jøï÷•qï÷ªišþìÙ*UªT­Z5ç–žŸ¤¦¦âwÒÝÝýÂ… †®%ŸkÙ²¥]çÎ.\èééièrrÎæ1cÆ\»v-""â³§ðv6ÞT(?ÛK8ÕÖ&úÅ¡=Û‚ëiÖùfE›uŸ0Ö‚ïØxʆ¬y“t·> iתhÀÄv4ûÏXZ¥T!…L¦k€¡4'É*T ߺpÑb^ÏÏÙw×jÆ6û@÷$ô,úSºþtÿþýñŸúˆ#>{võêÕ9»øü"::ºAƒ6l¨Y³¦¡k)ð¶ã¼yóêׯàÀ%Jºœ$‘Hžâ"9|‘Tl*&>][êÆyü,¤µ#¯ü`Iã!ý‘´zé¥—Ë –Q¤*K.‹Ô µw|ù¢JAuª÷'IRó¹Ó­Vj¶mÊ”˜sÉO£Õ”R©ùF±%¬RwýÇ»\ñ1ŒWZÛª*b…ŒúÊyj5Câ ¦qOšÒ¶“›H>\.—Oœ8ñßž={Ž7®B… ¦¦¦ßymwèСþù¯+­­­ ]KR¤H‘7nà­¢E‹Õ¨QÃÐåä ¼­\®\¹øøøì¹\îÁƒ ô«­±_laÿÙ°´8óxb ©Ø"ÛÍO‚/‘Új¯þÐþÊZZØ[êžåŠ-ÍŖ﫱 ?¼ÖDjk¢{ıtyÔG`jãò¡‚gná`þn)W(µJ?-ˆàJl\$è+ë@Rhf.4ûÚ?ì‹ xm†Ûx~ûÆ•RŽ3 qÖÛ{‘çSrpòÔáh-éXŠ–gê†ÛA´\%ËöŽaú‹³¹Yº™ŸŸ€ˆsZ–ñí–?Å—òñvaÖË'»‰ëwó¢Nß>ª9„”špˆ\ j¼m9a‘H¤ëOãÿþû/EQ[·n]¾|y:upo;w*É[þþûo¼º<{ö¬‰‰‰¡k)pÌÌÌÂÂÂúöíûüùó|<ÔI¡B… } -Q¢E}eC Ã¨Cë¹Å޵¾µ¼Í­M&,j:?(¯Ï› ð0åå­áÆ„´aqÝ\{vw_ºüÙ•QÞ­=²vEFE#ï¢A®\&—èö{ãþtjjê´iÓ:uêdggÇápÆŽ‹W³fÍ*Z´(Þ¢‡Ý¹dddüõ×_xíÙ±cGC×RpY[[ïÞ½»~ýú111cÆŒ1t99â¿ÿþ;räHݺuO:¥›Ô¬Y³ôwÒÀ70ˆcßrð¹v%Jrx䜋‘fà IBOü †&<Û×^ѽI°Št±1(€ƒXšVdQ¹vÃÔ©SÅbq5pB#mxóx<ÜÉÆë…aÆáœîÚµ+~V(æveï%&&FGG3 ƒ»°†ªAw:OÓ¦M‹/þÕã…9 (jµš$IÜÍÊ奡ãÇãïçüùó³_§$%%áÔ;wNž<)‰ÜÝÝ_¾|ééé9{ölÝŸ'ø!’Ëÿ0º—Ÿ‡ß4icT”Lõ~ÏU–ò›#¤ä Ü{þòÌ Ü±Þ¶mÛãÇ÷ìÙ3cÆ ÜƒlÕª•AÊ[¶lîë#ÍeŸ¤¡FˆT*•xáÞ½{Ó§O7HºSñ8gÎX_c«V­ÂÛ—øûièZôãÒ¥KxãxèС¾ckÖ¬iÙ²å±cÇ Èµg ;ið‰o…_áÂ…G=xðànݺ-Z´¯½½½s9)cbbteØr”–¡–Ž7Pð†T||<Ã0ÆÒøs1àØÚÿüóÏêÕ«ñ¶ã–-[ 8Xžîø“·€·ƒwîÜyæÌ™ìyìïïÿþ}ggg}” òið „BáŽ;nß¾»q<¯Y³f¹¶t±Xœ'/¡Ð+Ý}LMMên:uJNNƹbªð·B DGG·iÓÆ€#Ò'&&:::nÞ¼ù÷^…ÿ¬J”(qóæÍÏžÂï*$t! ~Y©R¥Ö¯_ÿêÕ«5kÖLž|øŒ3BBBììì ]0R•/;¡VÊ ]Hî!2tî sûÖüê {õêÅår·lÙãá€ï€úA’$fÜ3ˆˆˆhÒ¤I:u:uêT²dIC×rA’|!AâÏš_"9¿¶"½víÞœ üÑ=AA! ô¬R¥JW®\9}úôš5kžÝ»w¯I“& ,Ðû}PH’4à}D Žwãó†-#‡tj‚l÷Ò«Ò?#åm–¡KøþêWªTéôéÓ7n,UªÔÀ›5kC €¼Ž¢(ü•^½zuDD„MN,ÂÔÔ´cÇŽ...\.W"‘|l~•••…?AÖ‘÷BZ&“éd¥+/þûȰÅäEº»=nݺµiÓf÷îÝC‡µµµ8q"œVò®-ZÔ¬YóâÅ‹97Ž=Ç¡•Cí£’÷BZ©Tº„¼Jw ÃÞŒù«Äbq­cÇŽU­ZµW¯^¹¹"H‚$ Ä"†ÖÏMµÍU1šâXº@ݦ h¬Y³ÆÞÞÞÐU€ü#ï…´··÷7H’ …†®%/ÁÉ‘••…·î-,, ]Ë75hÐàÁƒS¦Lñ÷÷oß¾}@@@í'9š›=ÒËÈ’C†»ðH-±³l·¨iY[æ÷]¸à~T¦W£mˆwAMpù$C1Ûù$4Я¼Ò“'Oîß¿?^ÕAHÿ*¹\.‰Œÿîñ“&MJJJ:qâDÇŽË•+7cÆ ýÝ>™àð9|B}úþÅ+é5ÕtN~®Iè€êÁCÝ„ó“à‹x\žæÊ0†¢U êÝÍ0 ‚'äòx¤f:M«ä¦sĦ\‚¦e2J—îI=:þ ñĵìÙíg™JkSžf£€}ý`ͦŒÊJ•ñ«EAT„¦ˆK YË ?½N#B"þ'—)±4R+Úèv*ð¹¼ÒVZ†®ä,ü·×Z¾|y±bÅæÌ™óç÷òc–Qf]ßvk_È£T9’xÙ»¬82mÏ+üTÖ‘sƒNDtØÐ©’0vÇœˆ3—Si„,Š{´Y¾¸‹'4“òv÷¼Ëÿ]LÆkui×¶#*”ò$ÉK¸ÿw¥»ç”%U,Eš;-0ñÑgî!³f¾ \ ½žð:Q]ÜLj dwNÞ¹~ò¯S³>¾ý,<ým{ä;êÄ› «úÞWÔ›r+ÌA…þd¨ŽÝ^tɱèìM=ê6´¤Vuol,ã–÷B(ýúõÃýé™3g>}úôw/e!HZvëøÓ›Ÿ]º”Œøâ"å תæZ®–³(&&“‘Ø—È÷)Ü¢ŽKú³¹½N¿T Ë·ò±çËoŽ^Þ#¹ËÖ&ˆ˜…}N>Íà—méã$VÜ<üpuï”›šÖp#Y®ÀÞZ"ssµ=Eo»™‰xõ+Ùûú¹„NzyêZFé–jŠFîeƒCœ.~ÿzÌž‰GvóM|x”­ãîSRÊ]´÷’ÂiJÈ’E„%X×ñ³%9<„T<‚ÄŸ˜¦cͨ‘Z¥í “ˆÇGºïC!µò]™'֬ΔJÄ"JŽRó€U!5«Ù‚_f]´vñÒÅHÜ‹ÎxyìøÎ!uxë®Öôqxu0‰ñ,R¦´T¤i G£! ŒT* fYvôèÑ¿óz‚à¦ÇlŸwrr œÒÐ×ÛÔD*ð–a—smÿ—8¤ÅÞ®õZ8^ZuúµÙÕ«Òe°+‡fÇ…1+Ôo•ËåH„æ¨sVÜþ dm]ÔE"6)Y„|ù`ËãÔV•M(š¥i¾MÍn6UZ+3’²¢Ï<8²îÖ£TÖÛ»Œ¨`ÿâæò•¾pÂñ“çSbcÍGTl¦½Ù%ÁÏ|ttÉäѯR‰¢­f¶i×G"&éÖ¾¥ã®]¹£Z{ý5®]ÿ–"¢•/·v.s]8~Ø`vÝȵ•—_/ölÔŠY;-»žÛGw³)²PÅní‡vâjûÖÅùk®%¿Š‹%JÛ<<2!tÿs gOï†#šöèâh‚” ˆj`, öêä4€(Að¸Dz|âÝËñÎΡˆæq¹¸=üïÆ<Æ(:99ÿfYÊŠ§ù5³·ÀA!úöS:…§—¶á³ŒšB¦ö–&¥=MÈdÝÄGbþ®Ãæí©§ÏU¬¥µ —’%§p}¹Ñ7„ݩкRӚ΢æ8·’ΈMºñ& ·É/èû»UJüá&½MÌ,¬K6˜¼£a£ŠÊÔ[Ú#Ñ77÷í&°-¤H¼yv^ ×öj»jÒ­ý.< Ýjw4sòÒªÀØ„Ý#'’!‹KNSo[?ø~"ëΰ-OJMJàe©ßqXUF|âë—ü!$üwâKX—+îá΂º£ÎZ{M?²yÛõu]/®êZ¨öðæ]{{õ’à-9ƒ¾9 iÿád•:÷žYãÞ•˜¨ÓQ Üz:”,çèS˵¨w¶Ýqó4£8)’Õ„&: šÒžW$K|¼ž—'«R‚§3”Z[•ˆùËâWñ9¸W®V1­Œ¸˜€X”üàÙü®Ï>4¹éI“9E¸jù³‹/"/¾¾{éU\*²)åÚ°“we'aAþ$QÜ™þÿbëÛ²áaEýþò.ãÎg4»µß L'VœŸÞª2‘t|Àп—…o;Ù²V«R­—1¯Tµ™/j^϶#“ìH’ÚMgÞpœõzJ 'u&Ê’N´"ˆïìN½oëÞ¾÷}¸´ëU½CkM ó­Êtš_®ã¬—×NÜ»qúÎÉK»Íó[‘5¨²Xg–C+ÈkPP°Ï©Œ«“¯Kíî2^Ä_}í|èõ«—^\ÖÌéÃL$§HQk™pêfL§:NBÅÃ÷å•nïVœPˆ8oRNF¾èþ—›Duwÿ,„¼Û¹ˆi–’'þ»ê™ÂÎÖ?À…›’þèn2‹„í·´*o®¹æšzñ,xìåô‡÷%zK¾·pâ- K·ªÐ­•§½‡Ë-èÝh¤éIgàŸééj«"uŠ—v'p<8I ç'§–_IBž‚%Ѳ×O£2yƒx©gCÜ:VÅ24E³ ~ýaMV¶F='u¢ñÇ`åíeå­YÄ»î0·TÛ%Ýúuä° #{1¿áÎ}g 즎n_N«Ãá¹–o N¾…—‡P–âÏO*@ ¤AA\>Dz¨k×åžâÞFœ}kÊeq??ƒ×õ¸SìÒ¨TÃ+IaÏþ翎ƒ{^\K£*=k …L±Fuã{=ý/ítßù¯Š½ëŠUâf% ½Ç’W­ïª|ðäa"}ÊV-ÌC2MóJ¹”µŽüïyúÝ‹ Eü¬š«W­© ^¨JEÃHŽ,².7¦a#æö­«[ûÙjV²v·1«V¶wvã½›b´II0jÍ8y|óô«ƒ—.ÙNëû÷Øþ¶É›&Ÿ§}úC‹¦|ÍQ í4Bs"ûuW¾H(1Õ¬ò̤fí¼èWåõÞ]oG¶¶T$'¼ºÿø¿u‡×mH¦¹æÎ¥Ë´lY¿´æóÀà ¤AA¡jʦ*•Y*dfQ±¥5î S‚ÒAãìxÖ¸ç«æ›Ôšß¸è·©Ï2hDšØš9–±¡¸„LÍ­7¶n‰‰ññŠL¡Ø^Z¤„OÓ,hS§Þ“jÒf¦¦|Äsvî6Áά„3GñnäUŠ5WÃýÚÂ…KظþÕ Ñ¥4®QY ÷wí«¶žu$ þÅ«WïÙzfq§ÿs|¦Þê_E{vþ‚ªGcþËÃKbqÊúUæÜc×>pLíZ®7¾b4cÏ<>"è÷-¾—ñêÜÝÄÅË–’ê¦p&©f•G0èá¶¥1x’¹­17Cº¯]v"]*tœ]®j-×b¾Ö–†;aó/÷dhv²VZYq}d²›ùæí-yrŠâsû){§iÑÿ†U²)bgV–? Î&~–úØ?5ŸÔpÕœnÆ7)YÓD»–çæèç¦yD¿ßaË2¬ØÓ¡¬ç»_ŒoܯU³qe^¸dƒže}] •i=…Äj$0BÒ ¿cÌBxp–¿Y\&þíàÌzwÞâ•ø­Å$‰š;ª^ 6ÍåÄ–´¥)Rá­–ÞâdùûT©0]ù¢R!¹…÷ÅŸVuæ¨üTD2…"ãäæBÇ–õ gªæ<03ÏÆSš›¶¡6“kk®^£‘öz9ŽW“‰E›!í Q²ì×sa?ü˜¦P­ ëˆÖe-‹ì+ íSy(K#•j3óïvŸ,…ÕžÀP—€q‚ùî4'yXpÔï×Ê-e‹ÿŸûþà°RÂ-¡™¢›÷ŒÓíMðšÇ´v„+ŠIôüä®$¸ÛWÂFÛx®ý; šúüÎÝw[e?\À‰›}ôPœÁóñ%ä0È› ¤#! )iÀHAHF B0RÒ€‘‚Œ„4`¤ ¤#! )iÀHAHF B0RÒ€‘‚Œ„4üÂЀ| BäIÉá‘iè:rC²y"XÿGºŠÜÃ˲†®ä[Ò bQÔ¹—ož¦²LZ9â$`hc¿gw.OëT™eC’«’bžºoAHƒ<ƒy¿êg6lÇÃcXFÛu“g¦=»aè*È? ¤Až‘ššjèŒEff¦Ñæ4@ ¤AžÁçó ]‚±àr¹aDǧmllrúâp8 Ã|¶uB’$«¥R©„B!~[hCÀËU*•NNN¹¿h¿AHƒÜ¸qc±X\©R%CÔX à÷¼{÷îQQQ_ݯ.Ôjuö)8ËgÍšÕ´iÓ£GâÏ­2ÈÒ|ÝôéÓÿùçŸÏÖòiiiÙ555=xð`»ví¶mÛ†ûp¹[`AzÇŽø­þê³xË鳯ªV­Ú§OŸàààÿýïFu<¿ B€¯˜9s&N…/ûÇ_•J¥#FŒèرãž={p—:· ,(NžŸÿeHcíÛ·ïß¿ÿüù󇞓³ ¤øÜÑ£GÏœ9ƒ~ù”¹¹¹\.ÿ¬{]·n]†aj×®}áÂã¹2*˜8q"ÞúùÎ Ÿ“ÎnÙ²eíÚµ[¹reß¾}s¦:r¬PøDbbâÂ… :ôÕg---SSS¿<ÒéïïƒeÆŒ°U_ºvíäèèøyÁ·BÛ±cGÓ¦M+W®üáZjòi>ƒw¼¾5x–.¤¾| 'ÊèÑ£gÏžæpÂÎ;¹\n=¾?Û—'Ž}oruïÞ·fgg§×È Ò|´xñb¥RÙ¢E‹oÍ`nnþá*¬/Íš5«I“&¦¦¦ýúõË™ ü&ãNð¦M›~8'ÞœR(ß™ÁÃÃcåÊ•*Txøðág£•`ü ¤xçÚµk!!!—.]úÎ<ßi¤†¬qãÆ%K–¬Q£†¾ ,@-Zð­3º³Ã=éÄÄÄïÏãíí½qãÆ    6ÀI oï+Hw‡†3fàÞÛ÷ç´´´LIIùþlüøñþþþ®®®ßŸ‡ôg—JÉÞÞ~÷îÝåË—¿sçîyë¯Ì¿½¼wïÞOÎÿ“! >¼uëÖóæÍƒ‹²@! Ú´iNÜ“þáœR©ô‡!9889r¤K—.¸kOÿÜÓÅ9Êáp~r~Òr¹ü'gÞµkÎiÝ8q¿[ ¹ Bt÷ïß ýÖ5WŸÁ=ãW¯^ýÌœ>>>:tèܹó÷/óÙEGGããŸþùù—‚Ÿi¤½kV›6m|}}á`È ¤AFÓôôéÓqïí'»nÖÖÖ?<&ýA»ví¢¢¢zõêµvíÚ?¨±iÞ¼9ÞZú¥s»x<Þ/…´¥¥%Îé5jàîÀŒ„4(Ð&Mšäíí]¼xñŸœ÷¤fw÷3gΜ0a¢E‹ü[ AAAcÆŒñôôü¥Wýü1éð"BBBð&ÔÎ;¿ç£! .¼Ž¾}ûöÁƒþ%?¼ëKx; sçÎ>>>µjÕúµú ’ˆˆÜîҥ˯¾ð—ŽIP±bÅÞ½{2vr#! ¨7oÞ¬^½úäÉ“¿ôª_íI#mŠlذÁÏÏÿ„;Z~˲sçÎ9sæo¼V Èd²ßxaóæÍñ§?cÆŒqãÆýÆËÈÒ €;vìo¬-,,’““õUb±øÜ¹s:uZ°`··÷¯¾<ß5jTùòå .ü¯ýÝÝ,]ºtðàÁp03iPK¥Ò:uêäÚ­­­—,YÒ¯_¿°°°\[hžpöìÙ«W¯ž>}ú÷^þ«'Ž}÷à;tèP«V-¸0NÒ ÀÁy€“2<<ü÷^þå-°~’——Wÿþýÿúë¯üv#ùŒL&›5kÖ¾}û~»@ðÛ=i¤ÍxÜ“ ܲeËïuåÈQÒ `Á©°`Á‚ÿùb±X¥R}ë6Yß×¼ys’$;uê ƒHc6lhÔ¨‘……Åo·€Sö÷ŽIàèè¸}ûö¶mÛîß¿ÿû·Å ÷Áj,ãÇÇ«c[[ÛßnA"‘à®Ûï…4Ö´iÓ‡â …‘#Gþv ùÃëׯW®\yëÖ­?lG©Tþa îîaÆmÚ´ wÍÿ°5ôB ‹/NNNîСß4" qOúOZÀñÜ£G~¾NÖ–-[îÛ· ˆ?lŠËå2 C’äŸ4R¶lÙªU«2dÅŠXz! Š«W¯†……ýäðŸß!‰þ0¤±õë×ãœÞºuk§Nþ°©|Xmš››ÿÆ0pß!‘H._¾Ü»wñŒ„4È·X–%büøñ3fÌÐc³ú:&ýA•*UnݺզM›?Ó#¯˜7oÞôéÓõÛ&îIë7¤1WW×öéÓg×®]úm€_! ò-œÐÿüó§§§÷¬¢i¬oß¾qqqýúõ[¾|¹~[6*Û·oǪ÷8¥RiLLŒ~ÛÄ7nÙ¡C\¶Þà'AHƒ|ëÀ×®];räˆ~›ÕË%XŸÁþÉ“'2$_”õðáÃàààÛ·oë½eSSÓ””½7‹´'{5 ÷þ‡žíðCÒ JNN^¿~}HHˆÞ[Æ!­÷ž´n@Ù³g7mÚÔÎÎÎßß_¿í~Çüï¿ÿæDã¿wk²Ÿ4}úôž={ž9s¦fÍš9´¾BäOãÇïÝ»·™™™Þ[–H$zoiﺈûýEŠÙµkWÙ²esb†røðáÚµk;;;çDãøSΡž4Ò^)·téÒ \¼x1‡Àw@Hƒ|hÊ”)xÝãžtRRRN´Œq8œ .àöõõýÃq.îæ.\¸P¿gtg§ßK°¾Ú>$40iß„‡‡GEEíÝ»7‡Úçr¹zßݽ½}ÿþýóMBc5kÖܸq£T*Í¡ös:¤0 i¯dee-^¼xݺu9·ÜG×û‰cŸÉO 7hÐ ??¿œ[„¥¥åÛ·os®} Bä+AŒ1ÂÊÊ*ç4===çÚÏgìí탂‚rtºVst „4ÈWÄbqµjÕrt%K–ÔËà@7nlèÈÒü±–¡«Ò€‘‚Œ„4`¤ ¤#! )iÀHAHF B0RÒ€‘‚Œ„4`¤ ¤#! )iÀHAHF B0RÒ€‘‚Œ„4`¤ ¤#! )iÀHAHF B0RÒ€‘‚Œ„4`¤ ¤#! )iÀHAHF B0RÒ€‘‚Œ„4`¤ ¤#! )iÀHAHF B0RÒ€‘‚Œ„t§z{íʃ4¥Š ߢxÕ2Ž„¡+úª”GW"_*A|eÝlņ®Èx1O/]{.gIr —)êlöÙGÏdÝ9vñ㙂¶è;µu{ÃjlYô­[o’2‚d.5«zñ~âEtêƒ ‘1j59N^劒äx _€þ]ƒ»M}÷¶4å#–Q(T Ëf{šà‹D•ÚLïíoc° £xóüòÑ-Ó6žäû-J½6H`èŠþ£L{ó dÜ཯ùc·\™Þ©äoms¤ížxJé3nTO‡ü›ò¬:=îMôÁYã6ßIi7õì¶ Õ?{¯‘G·]¦6o¾ážÌ¡ÖðVUìõº—u|ñÿö½²>~˜—¹>ÛÍq,š÷0êÌ´‘ ߈{Åg­±ý‰1ŠÔ¸W÷wNŸ°':µËÜÈÃ}óôÖ0È5ҿˤÂÜí»ží *Úe3ºuëÕÚÙìãö4‡HÙ3kEZ±Ñy ¤….Útñä¾^¹ñ$-ú™.QÚ—oÕÙ'yýÔ½¯i>—ü½F¨ˆÅí&-UR…«×\·°~+4KßÀN%™£«6ß‘ øœ/g H‘£W™¢ö¦èúÿì]\TË÷¿w{–î¤$i•RAÅÄ@)A°l”±P±H AB¤”n¶÷w—0ßÿ½÷{ûýðawçNœ™9sÎÌ™3sð?¬Sh…·Vï ®oÃs«rÔù7i,8^×ÔFCK"ÆûD-ú)GŠèÛ9h´%„Å}$ ‘¿È™lüÁVÒ¿ †Dãä„ß¹'m÷=¢Æ?ì¹Òóóú¨Iÿy 3ÿ~mçÃgÉÿïñBÅÌNÿvBYk¢èßMËÿ,`²z¶SuS[ùõ4ä‡è9zy^&ÇD NÔßGÚ‚þoÞlü«ñ¿'`ÿ_A£1Ç)F$ UÜÐG*ÓÅiDêßCÖO@ð'—2仾k­LÚ«¸þš~?[™‘Àë_{šÒNÆrÿ TÅïâÇÚ üí6ýX•3‰[{Ѽ\ƒ,´®¬åFvÛßdÛ)ó“ôð§Ç lüØJú¯‚ú¡ÏXßèTH˜šNá( Á &H€G¢Y¶Y:J&“©Táp‰„1žÓID•ÆÈ‰Â à •Å¶N‡òGa0 SH23[:†@jÆd& 1²`eʘmŒÂPˆh-@‰D!àý"÷T½<˜ð2æ&Èž^‹ûòd¤¡3æ0f"ø†= ™Àª‰BÁÁ¾ZC?($r¸iuTF®ÌVi{¥Ó(Ì(Œ ÑXr¾¦3bP¨Œ Œ™Çè”ÑéT"£¿ ªhP†pØ@Qt2‰Ô×b Ȩjt«%•L ’¡dÔ…(ƒJE!ƒ”C@¢Ð 0Á`PlVwP’¨tÅp ¿ú`ÝéT 4fÏ P(Øðæ QH$2³¦P’!×ßVLc¶M«ƒ†Q_™L¤RhÌöc‚`•J£0jÛßvPyh4Ä t2‘ÀŒ õ ƒAþé0¨‘iƒ¤C}°¥Óé4|{`Iz†N"{{ÍìH:£>Œ†bêž²¿¥qH£ÊQC „è‘=&‘),Ï’a…ÙÌÊÂ($9ЮýÀ¨,ÍÉ‘¨Ìá C`M2ú9œ5jF+zDg¶4‡ °="Qè¡©Æ¿tJ/Ìê+ Ã%V0¨Ù è̱Äj:££Æï“ÊÆ[IÿyÔäßoæ2PŸÐ·Â$¶åíut<šT}WÔ³2³ÙtÔÓZt7¾ñ>w)þ%VkÉùc¾ËŒ•híåç÷o¸ò–KBCnüÜȵÄ;à¨Û|NjkT°wøéó¯ê÷Ë FļKW¯ß~S.8ÇQ2/:§¾€‹Z.]âr$ÈB ÞúêªûÙ¤û±ID-³mÎ~ÞkõGécjküɽÇ.—"2Â\ˆ€÷´´Ðü`„–íöqeB"<`U^]eé™+gç)pC«nm9~-ó3@E]2×yDíBoòjµÔGOÛQ‹äâÚ?çÕѬ÷=°ZôÛy–á±ÓóTöW@gÞz+ùÞûwŸ¤—|žˆë­¸uv†LÿúœÜ{jo@pB¯°húRCÔÛèã¿ká”̺*ÒŽ ¼ý.2Q鄎¼V6t…OË>¼ë`XQ;V”USùUÆÄnï€9“8¿m›®úwG|\O¬„Â^ä¬3gåðö’‡ë‘è„–ÖTSى׺žž<›wx—¸w³ÛñŒ:Š’áò嘇w½*¬ÂÕm/Ež·R`EkÌæjB0ù!o:³wO¸ÿ}M‡ºGQî¡Ic7nÏ£3^»ŽÅv`ù±Äòòîé+6ôÝ¡&4šƒHO±ÏFè¨û5Ôö Ú«¤»RËš :aŒœ‚cÂ7ö¹°QšNmYz(±RXT€~ÊË‚©-;sõ¬ÕD<Ðü|úƒO’’¿PQ;ÂŽ÷>ŠŠ¾ý²‰aòãžå´ßg¹ä‹è›±‰÷ êM' nvõâu¾þ⻜òÜu,¾ 'ȇ!TTô8ºÞ»UYð_ïËòO[Iÿ ·§žñ¨³JPÒh^Í#wuÉ+Ÿ+#é-s;½Å”.d¸õˆJˆÿz1ù¦ C÷Ñ.9Mu‰Å¿ÉrVjZÐ ó6 ¢¥qöòŽ^¡Ú=ɪÊ.9N\d½vÿAÒ’¥X÷‹¡Í¼ËO döŸ9®Ít‘å3^¦¡kø´Ø3öö|éÑ]“Ÿx.+Mƒ“›×Eµ–„Ú ëXf²6þã¨|2:\ÁØaÏÊ`,”†Òçnæzvgj1½!y'VÞääérôŽ·€ÈŠ3G–³vñq <ΠÞ?|ó~pÙóÛ©’KÉû\G¾äÖ·Åh” ÂŽ{î%î+“ ˆ¼¿@‘@  'NÙm !Þ,N·™€‡ÓÕgVŸü@†úv„’†!-ÃÔÁJ ç§mÝ$ó‘ë}P´æ«›ÌüSoÙ»ƒ=—z…0­æU˜£SÐE—E3^ÎÔi¤âø9ó—té'dä‰c`trÓ›*¦{-ß•¥?¸¤ÄÇ’5ê:Å‹X{ç<Ø.ÆƒÖ›Ý ¯mÌ^2Í;èI³O\1@=åBÉÓÎápvz?FXl¯³!äåÇÒ§/Kµ­X kS–©YŠŒqx¨6té±B@~M¼÷l>K|`¸ñ8èÛà:œkÅžÓz:’,ñ" o6U(Ï|ðØ¡ =Ä`qX£0†v†~ô¥´>ï8…ˬž„¿_ê§êâ\ ?êÖ.’aEä5¦kª2³Ü£¯…rÍ/¿—×{v#Óú ÇŠ@C¯vL«FdâŽ]ž×ž®»»}yöÒT- s÷`tv—Ýý@+ÙþŒœC­‹¤Š]Awº`¸åÞÞ2L †™ºv‹âî}9ož¼,SŸ;ªó6u„OPÎåD2ȧ¦®ÇÏɬ,fÆ¥ˆÀ†±Og±ª& ¥2M[ ϤÅåÖ”YqrA{¿fçSu ôP‘àež—EN^®¿•\SúµPc’ßÝÄÓ‚9§µK8ûm§mLìéŸ^ev/ŸËÐIQÏæT“NDYÈ±Ž‚a$gíIXlz=òðc÷ËK'=oo½OÅËú{»Ë‹öÍË<ÜÃmØ#yš@Ò¾ëZÖùØÞ/)7Íe…Ó5aaÀ*ü¥+—ârœ „°£&bZ 1“gΚ¬ÈŠ ³m›SÊ– ob³%fŒ8£ï™éZb&…B ¦ú‚À§ôûe€›2“v&sq-[6%ld|Ö>]z0ŽShÆ2«¬R#,¤WªHÝDHÃÑHï_¾ˆµž= ܑӡô¦O9$À,#$êŒen3~¤ÊãBÈÜQ÷‚_F\xöI=è·¤ÖZ“‰g”Ÿ>õú¨ÿ4h Ð+Råc—Ç™c¾›×¸@qCZ$åúZÃ;Cá g˜ŒŸ†Æl´>'f£ô0­oºGòb!Eû‰Ùh4rGvj Àεš‰Þú…Àõ‹WodCJºåùÓ"2WJVCm4ËÉo 5åi9HR¼'Fd¦p%³0§‘8–’f‚N7t.~Æp'tôÀ¹æX Ž_$'†¡•GŒ_hhö7ŠòqAQ(Ý ÓyŸ°á•ˆª ô¹/´A“ŽX‡_Ê8µZ‰å§Ž‹]²×Ëa+3ÒÑH%!aظC ¹wß}‘û¥½—J¥QHðý­ËÞGgö½|ûÝǺ”† ­Š¾Ÿh(è}ÇZX‰ÞgAm‚”àæ–§ 8´€'¿~þ ]¹ùP¨ÑÄ1ÅYgksw;@!¾7Æ@j邿\§µ‡ðƒtÉí:?ïˆÖ§çæL¾*¤8Óãà‘Ís”¾Ÿl”ª ™në/û{Ÿ‹~ZRÓL¤ÐhdÒwZ˜ÎÌiØ) *¥õKôÙ²†»žõiÌšvz Y”æ3”ùw]ñ )¡€Œ®’T@î(!y+#ãGåÏŸºä=m–F}ô FÇã¢Äodép Å'aߊAnSç­Ýë¿×dÂϸÜ÷ÍzFsÄf†ÑHMÕЧˆâð PâS …ÜšW 齪Š/T2Àƒ‘ýÓ·„ÕV4€4àóÛ+â˜}ÚIÌkî¡üx>ÃÇ _s:~ïEÞ×v•Å\?™C?wõ…Ô”7Âè@eZ˜æÒ©Ä>Rÿ-ÇXþ•`+é?€þX?lb»£œ¯U˜én*ý4wpž»—zûém·f­y-Þg2¥÷ \°ÿÚIƒanÛ Jp|ÑÀeå±”wÕÍ—;nT§9I>ô|çy¾—ÜÚCcX‘¨qBi¢‡–ͬ‘[ä“«:øAbíFe…ȆqÉ ”lÔR -ÜtâÒ{QŽÜmú§rÆM3>¨ÝD†K3 ®¼ Òa —_æÄ¡áP…áÛs…J…–=åvéS X÷ÅÈÁ÷ÃWÍ 4¿~<èº=æUAEQ’‹EÒŽ)ëRœ›*ü«{qÍÉæLŸ ´‚/Ç-3QæãÁ%™¢&ÿÞÉ[H3»hh~¾G/yHf(N~,ÐÖÂh+4Çx=ÿ£€8dš ú8WÌÜuíÅûœÈ¼¯ëtðåw’¼vþä¾_ÝïvlN^5v¿¢…8 éFmïí¦ÓÇZ¹u]÷<Òè&Ý;aÂ\ÁÓ ñôƒ:ô²èóWŠsï 7+Öª‡úMî‚K¢¤ÃH¥tuBë‚ÏÍð7‚f9½_ åË=:‰4jZr/JW ûæJ¬œ×…;îíu¹oÏxl¹”îê®’rÍmä…×?†çûÜŸtaV:¼Í¦ÏÇû«÷£ „㹡ù\G™›_XøÛçaèik÷×îŸX¢…îŽ:H¥ðJË LR8f®u÷ ùX˜œW­K˜kJýÙ-eèxó…cKõ‡Ü× î+½¸\°5Þ¦ùgîòƒ!yðP}ºšº!n §´3ý›ùÄ :à1h8ImPÈ=¾ rã±ÐzžƒÊ’øc¾W×½ƒ{€©ï3dE*øÝñû]€¼x†¯ ò‹ ÿ9RÙø>ØJú¯ýa˜Í>b@ICrÌÉÙÚíú›ÂŒÓkZyÖ^z€GÅ\ ˆý&»œúãêw!;eŽÆdÿï¯ìô‘*–Xs_}½ÓÒîWg•W’¨bôþ¯+(‡°œ ºO7Òiß½\‰VU× MªÑbÊvgú/Üb6Z 4€U¾4І*éÆª2B‚ƒWn2cQ] I¥™K¼n#3¤Þ¾û¡íÏ)i~Y= ±þm0´6mï“!­-i¥Mð¤äÄ‘( ¥«°¼ûžn¦ùœú}g±!Ô˜ÂÁ†¦²Ï_hÄÿÔ]¡u…ÐlB^¦¿ý`ü~’š@XßPVÓ@“e_kúÿ¶’þ-ÀX[] ˆ@~k ëÌ^¥·z˜?BwÓn¿½y¹1€LdªÑÐGbK½¦8[¿‹:¼~ÞN©Ÿ£ƒOÉÉ@çA~êõ£Gí®¥½Í€ü†Í²{Ó+nFÜp]³^—%rÀ¾ @Ö>5Ÿ$$ÿæ¾m¥™Î4´Ž:¦¥š0θ££Z[0šÁ¸°PâÎÊ—­€9kîQÏØKg\ƒ1攡ïÁ·û…}øþ¶<‰ñ·ævÕí?öœ÷øaM-AÈØe®"ãçÔ5ŽèÇ—lwÏ3¿¢ÞWs+Ù=pqU+UÙ×cŽ]}ËKôÇXJìµØnô6õÏñ@3œ¼9°Â¸qõ8æ3FŠЈŸªJ@€u‚–PÝFgvÀØ,pØÇ°ï¬ÞCrÍœ{ìq̳=nï̯Lùæ­z..œ±A¯<=_Î7è›Òôwü8·g17·áˆA_fryÌ…ÈV@ÒáÅ0wÁ%'¬×©$…l݆Pº÷DmÌ Ç͘Ÿv6“‹ _=W5¢|lë¡ÐŒ>V}f4æÎóظ9s—J<ûðhù¦}ÿ0® òM…Óî%Œãø<Ƴõùö>øüñZTªI¿Û gª¾×œ4XjÞ?xש¯ÏºqE%“Xw£ŒU:jêB[‘½‘ç®ÝÓ÷²þÑY 8ðoÔZòI@ã·!çm#°ˆéFí¨meŽ_ø°@pT^#m°ÄVhÿ­Òw!7iï´`ŸŒþ[Iÿ:Z;ݵ÷b">ð!û§«ŒÓR½_ÓcJ9Ì1#Æ×Îó›öëãuð^:ÂpÆm~ÅÙxUè.Ë©· §é( I­e¹ †ÇŸí=Ç17aèQU0ÊvÛB®Ô!õ £E¡•7CNwŽtž£5Ólª"sEî‹äôàl¯é`ôæmÒ>½/ËÙpJœ‰6µ*+§Š‰ö–š6@•u[LŠK ÔD-œÓëìß~ÁÉLÀåܧ—ÇÌ,ŠÕ…`…Á­-­ Ñ0ši“i‡!1Ìw0c`XÇ·à}•ÕNß É1Údª½àäÙÝÓ%‘m?ß.·ìaœ7k18Ùjï¦éŽ¿º:cJÉ\siNRaÖ›¬— ˆÿÚØÎŒÂçzöõ+Ó»ñ»õTîÍœ3]Š ÖTS^\GÛi"3¢ƒX„r€Ì¾Dàú¦p ¹s†s÷þ5sµÐÎz{׈ݮۅÆ0zàv:ˆDÃA3O8'óìšå–ù@Ô¡;'œg•Ü–‚5eä6Êà ù±æKg_ã0ï­‘ØÝÃ2s Ã]@n4Óó‰aja¹iÿÆ[wN¿»:K-ßÌÂXQ„££ásQ~ñœC©Û8E¦zí]twWl‚…¶¦…™‰‚P–›þöY!İ-ÍͰѻ‰Nêéhm¯î݈ãÑçÃÁêrîø9[¡t5áˆìˆ¸0åµÛu’ü3ùíŽÍÏÔÍ*Ëb 8†³¿L¤ái ¸ÊcÑ’üyª ­b'Ã7Sj?9ØÚíw3TäoÈß¾þRu¥«öè/ÍädÞ¯5ÚÀ¶;qÔøÀ¦®Þœ|ßåÖêEÂMÞNfØŽ’³û<Â>³÷œ]¡Étî@kFDû©x_Þi›4Û\WŽÒXú"õm zêª ?Ç1Õa1spžVêd<¬ýSU}k{ ŠÒòš3ËÂ0oàhŽ~Ïß;_ átÓ{Y^ì4“™ÚüHb]Eɤ~b¸—ÀG͘#éŽþ¶ñÌÓ œµ$²Z?åÜwë§kÆk‘+3s«9pP];Z á=‰ÁÑ FgœbÁ8˜× †nÍ㘹.<‹Ë'…?8_8eÃ]Krn1HåCjË?”` ï…íäA±×ÖØJúWѯ.º°Í# -Ijú¸Ž´(0ФnäÉŒÞ>½XA‡™ßN\µÏ•+ÏôÚô8ãQQú}¼´æÂ5^a®Ó—}Ž&Ç5 òF¯ÑÞ©x%yÓŒáÛ«rë÷«»œ’4Q•þ¾ªÎ†ˆB9£]»ƒŸ'Gg§ñ/Þã´q͆òêS¢ju_Ÿ7õxpö«½otÓoO}°ñîVµ}·>ÛNô¾ôÌÇv"B âô›Ž½¹GÑ›çåª ˆèg„ÎsðÍzý¤UÁÀípüÉH®é§r£]á ù{†M.èÔ¤Ãó¾àÏ8ëÄwûNjñ-úyóíWÈ‚‚ä0~áÔ³_¯TG‹MÛXúYËß{·ïRÃ&€VYt8æ°û"®Í|Ž Á/‹ w¸x÷^DBHuS`¨Ÿ¡›îñ¢H÷©±oŸ4\7Eg%55_Ûã|ãék…–W×tñÎ o44#¿¢({õ‰,· oó†©|;'š]{ywªçy/ìåûa;"ºèp¬Òô…·KOÍýø7ð"tñ‚] ÆøëÅ•šòøC"oÜnÓõ'»øé7ExÓ?ÈvÑ?X™·Ù–ûäN³¶ÍÁÄÓO+ylž^Y¯qãâ±é×]¾È<áÈ}ñЙŒL®Ð¥«‚î\P·Ä8“œO'6HWŸêUEAC]µ(õK|¼ÃÀO:•Ó9ïÌNßs ɱWŸÀÑŠS­ÖÆ­6b4?ˆâÛ“5ùônß³ÑOb.&óÈ9x¹?'Lß7ùùéùøâÈÎÄ%ßÔ†c׫Bˆ«QqI;V7¨acÛÀ˜@Q#éZló/Ø8g\¿Ty!îÀjË" Á-i±álÜþM2ßžP§‘¢öÌÚpú=Ôh9§×p]>r:#‹ïÂrÇÃwèx¨ÑbÄ9ï®;dœ­1Õ£’Ìh´öeûžìœ „7߬˜ex»ß1³K^48F^wî•×/§n ðN÷*«žæ±yWô«'áïž)Ú~”õlµÂ¹ÂdɉAqÙÎF<†ëB¯TS|Â_|(ù k¾ìÄ©møg~‹ö^¯¸h*ÝqæÝª¢)óÂZ@´?GÄŽÉÑêÇß¼^)pª­ÍnœzØs×Õ»o"Ãß"y¤g-p Ùµit Ý•½Ìdîí2´  °_ sweð½óë#©¸Þ­ã$<_&·À#󦧥ÏÓKýãÞ§>ê0´ÛñìѺXµ€øOó'L•˜~jHˆé‹¶aŠäs<ïxph¦Çkœ›ºª-xÔœ|Ê>aw}F}6œZ‘Y_†…Чا‘ñ„¶„¦l ü½=¬x{{»­É6¡y_CGm‰o¹j{/Üßû#¤²ñ‡ÀVÒÿ# § ¬0Ït”ü~Ô¿ßÙúû¢b”’®èQcÿg^!ðÛ}uÑjÕ…ïùÒýgÚóÿì¶ü_[Iÿ‹QŸ^„³Ÿ¥Â ëvm‹˜zìÅ/øê°ÁÆo¡5ëpÌÃAúÚYrøç%?í¿›&6Øøß[Iÿ‹ÑsÁ5>ßc¹jFÔÙœKÒ7ü}Ëh6þ³ TßéßÖ³sBKúißó†— õ„Ù‹;6Øøc`+é1p¼RŸYõ¦½ÌÿÉ9OA¶ldãÿXaeTÉ¡(¡)þ?íš)þý$l°Áƃ­¤ÿ½ ËÛÅíþn*ØøoƒÎc”RMü»©`ƒÿY°•ô¿ì…3?Ø\È)ØJš 6Ø`ƒ 6þ¡`+i6Ø`ƒ 6Øø‡‚­¤Ù`ƒ 6Ø`ã ¶’þ'ùâuð'oßøÎ¼AŒÝD?Šÿr{ýRÝÿ?ì¿Ü9lü(ØJú„Ï™WƒÏ'7XJt³·»¶$þŽùbèïGÿɤc¤øù\~½•‰¾‡ïÔ“`hÎl¹ûbC©¿Iµ>¹r>áÅG†Smžçæ9¢ß±1:üÔÓŒ2 K° ?d‹ù»)úÿ¥íî•3wÓ ‰H otú¸ã¼ÝšRçô¨¡—‚F¢´æym°šðרyèô£F…äwضÓXñ7ßúk eݹxãNv-eâºÛ^éï ñÀVÒÿ HhÚ…ÏöU ¨Pš³~ó·Jº«ðž‰©Íû6šùjI:•ØÙÝ–VlÐ÷F-Ré½i3íÞ73Þ¶C"8¤gÝÏ|0ð’Ç~€9§ ô¶g 8,²ÚU¾¥£óž&ŸUljFÐ)DÐ0°ááöÁ÷„€´è…2Kâëqœ@éê"ê½x½cúôÔü{C.E=y™]ÛN‚~Ãq|”µô¦Ï²˜gmªÅ=6ÓÑ›žÎ™<Ÿ°:æy€å¨0Rþ§.[k9?j昲jÑ_«¤;®ºXo‰î>—òz™êwiPöÛÎ811<Ìñµ§Ã¾æE›ç Ï›z¤D)19Á@âgßþWÅoã´{ö‚³%Ì3Ä…BþSJÁ={…»ñ¼¼Å’ú±ôC?¦¤ÒÖþÇL®;Y¯¿’Þ,áô;Jº£4j¦þzÉýi ®£FP^t$Ôìð©Ýï…µíœÿB%M§¼ wµÙqoSÂ{?Ó!Í@§Ý9°pÁÁæ «»÷œžÆPÒôŠÓ§FÁ=»{ZŽ›ý挿l%ý †\æÛöF]™rªÌͪ'7d…èmþH¤„·Æ;Á‡,dQ s³jºËn,Stþü è‘¹ì¨oĪ ó{E¡#(íwŽÆµ^YøÍËŒðssÈtâ /ÜŒ#p@~´Ã"È$Å]³ï)fWet쟥éßãMy³$€V’|iý²u/ÚE-l¼B¼5$Ðô‚çOîÝ»}æÊ)?ë°/wÖ ÕÕ1îë{€ÀÓÅ–“F‹ÂÑ(8 ‹dµÑ_ z}ÞÙ;/;iQ×->b=ðÝÊ´§“Š<}·jj•X~u$¨¼sðáÇvrmRJÞtÍù„¡1hž“ù"ÿ›˜ÿo€(  p"a?s;ÁXÆKT× ]å•ÝÚ•íq¬ÆõºÄhúÇ;øÛE}tRgBôÕæ®Þðý=M·¼BŒÔødÏÞ»º¡yÛOtz_d¼†–úê|Л¸Zì%§?*á?Xêåþ°•ô?#_yù „´xß[rà~õý}gS,·Ì6ÏÖºD-ôK3]C”4ÿˆNå%6B±qÏ^Ÿ;Ѽp?ÿhÑМ8€†³u]þ(è|ª×ü:¹[™û ƒs`‘õ‹õ¿4|õô ך¥fß~|e¾úàU&k›Ù®:Ð[°RV­9ÞÀ_z%¨ò"EgÕ÷Þ‰ý€Ú{ùèaµSû^Šh ŒÌ¦¬t1úŠûi—šìIæ"C{’€ÉF{?ü"ºÚR-—FEUjøWÿKÀVÒÿ4€ˆï›Ka6;÷N½úòyuÝžƒÁ+ Žð ¾Ï•áèôg—·Í` CñÒ¾hn»[ç_&Ýy^^œ”Qæm!7ò¥× 0& 0SçýÏœÍøìç³ËöA¸6ø˜Î0— Ư¼e¶=ª&ä||¨†Vu©&÷yêxƒójì8rzüÊ3–Ó®³2ÎÛ+êʲ!oE9 –{,³±ô3cÁ…èSÇÆ¾¹.sÿdä|ìæñõ¢˜Ò6п l%ý “k?—–×6´(/¤¢3MGNñ¨»øò—ÐømWY"¿”¦g¿¯néEsŠèÏ6•þÊúÞú¢—ùÕM]4Æ-ÌUÕûf".½ÄsŽâV§;žžXcmìš>ÛpsÆ5—„–]/"”ÆÊ¡>åØ[¸[¡)¯Œ˜)OàºI¯s-äôÆ*‡Œ>áûDeMé«KÁöÏö™ŽñÎåè/]d9Õ¹f£ª0»’£ŽkãVò0«…CS1ŠËë Ý=¥·V¦½}WYßB¦8^î¢ÆÞoÓ×ä§½É+k#Ò°<"ª:FÒ};úŸß%<ÌnÇa“ŒL´ø>df”×ö’a¢“tŒµå I>µ»ù]vzìþ _þ‡7¯Öðb„¦Ì°Öä{SEFcH óiª @o®*LÏy÷´˜)éô[¯ =KGãüØ„F‡D`5f« õ$ µf¿zYPþ… ¥ätõ§‰s16J^E?/îÅaPÔ ÔÄ;« ^½Í4mæLÆ„¶ ±© ¤Ó=½œ2F‹Ì'A#¶¥èAüËK%RTgXéÈ t5–çæ®ûÚE¤ÂÐÜ2J“§ëNÂŒÁ¢_V!900ŒÂ¢ÅúX†{YNôíL ‡DqèΰRÜ€¯Ê}þ6¿¼ƒDÇñŠ©éM–Ý<3Zv½KN¼rìVFû´E{ÞÝݦÁ|ñFÉ›¨ç…D¨;¤ÔŒ&sge–×(1emc͉ ÿòñ]~QÙ×"šGÊÄÂHh¸ˆ"5”¤½Í­jè¤pu=y‘B¬¶èMf^ISšJqqÛúU ­±$6éE ‚üs—ÎB‚ñsRôãz‹yUm¬4yÆ­±øíóœ¢Ï=4hœNÐÐ7TX#R>¾¸ûæ jÖ3q.O•>OJ)nÄq hœ+¨%‘Q…ô¼Ï T:ˆâ.‡ÆýÈž¢–e=Ë,øÔEñ“õŒ•EÓèžO®=¨æàDqò«-°Ô¨zÿ6ïCe[/WJÅÄDÏØz&”¾{uÿØêL.ù4öB“§Ø¤–Zïb£òÚÆ%5cž1ã§¥*==ëIF”¨ðQô•:á^"fæÊ¥Ò¤ÊÄÛ)=(œg†…™ïàôÖUÿöõ›’ª& +*«4uª?4D©„ôDZùõ4 ­fl63 ÉÂ/ß´b²ÒÃF@ÛüÆ­Hã 8AéIØk+W†åÏ]µS­+åüƒüÖnò” 5#”4Ã?‰£H{ÙI*\Ô'äââ ü¬Jc&êHäÍšë5ùЋ›Œ$¯"¶Î?Pa·b¡®’PéíëÒ(‹|ÏGyØ FÓÓhY|Þ†•åøU¦L% Ç kkЏš/KîÄŸz8£_I÷\]m°1泎ղÙjøä }¹mp‡ãÏŽÛˌهý¨xxÒyëÞ´NùmþGÞ†éÉòŸ°t×™µ"?HÊ)b(mŸ>}%³¨â’Y¹a^ýý¸çkºH} jv¬ìñ–F)¾¶u®× ~òòå³ñ=Ǽ¶5ãD6Ýó_4±? õ¦«‘óù·Ø)óVÎÑÄÑz[J>Bs*æ ' ª-8cfD‹ú³ùæBH8ç’SQ)Ýãž&sc[•brÓ”toí Gë1¥¢ë¶Ø‹÷¾óvZ‡7]ó8î”'BJI%ç’…Cl‹ó?Ô³K—ÓÊ;‰´Ù L$RHPkÑÙ8ëÝ’.øÛú iÛŸ ?6Sv4ÂÆ`+éßÒÜ­ÿþ¡9““ç©êIZú-;³ë“/BÀhÁZóÐ-©õ€Ñ¾äGšÌAÚœn¡oô°àœßÍ×–2”ÏOÍØFà7xô>mzŸÿ‰ûîÅüÒ*ßbwŒu‚zRUùþ•þsSöПíØýRåù¥Ñý¢ î?z`ã•ÄÒJn‡T}, ž½Yïá :ú²‹:qþ©ýöO=¢*/,Ÿ½ðCšá7›Øbû—’¯Ð¬œþ/µFÅ#.£+¨'†ò‘ƒlÙ[ûf…ãÖÜZñð²§~QYòøå¯ýQèyÁ+–‡go¸ñ4t9”Ò{³©–‚e¸»Åã’ù20)ݵó5Ý£ ¸&Y¥Þ b½Gñõ^Ýéþ™¾gïîš§Š–[âä^¸çE¥¸£»Ï,±¾=i5}5 ¨ªŸá}æu¸¼¾%ôÇ“p§f¹Íw«t? jÓ•9€JÄ;<ù‚µJÈË  ²Ö¨0üi½ò!tÛï¿V}Ù™Ob²¹Ž–ÿôÉ=û3Ni?Þf}õ›™çaï·;ðâþ‚†¢n¸º‚+'$¬«jÇYžñ0dõ8÷E$xÍfnå;Û4ã$#ýÂön¶RÃ2ÀÑ"òÚZêüX Õ÷Á!>I .ÉÅ‘°>2èY¶«®äY숹d …yo4R‘wÖ}¥AŽ™äè³H ±»®(ÅcÍêØ2p¥_tû¶ÙßNþe§­§¾#òFÞ<áj°Š DõîvMëãù®V%ÞK‰Õ–‡ø³§$zÆÔ%O¶-Þ²išiͼ:{óÉ6Þ%™n*2³Ú½k¡–„å 6:*¯çMb̲öLµ?“©át6;|S‰ÔŸ®Å'±ÓüÊ“§ÊC½ÑÒïà™¤©[.'щÏ,N°ZÓ+©cGÄó eêŒÞ\%Ï#ãºÓs§å£KRœ €‚Óa±,‹1QÒEÖ@Õí«¶œˆ9ÕÂÔoȵè󞤞L¹–ýn°¤çÛŒ]c>9{xe»1£ ' q‰v®±Ò»§Î¯l¿h¶÷¹Û…sÏžê0…JùµÕòŽW"o]öv±Rà1]±³&< ±c¶Âm—UßZ€CMGSiÄÖÈ>òþ ºë·èêÔ Cœêàæm.Û_>ŸÂäéŒ&êDÂÛ£ãè|ã˜wZ÷[²,˜bÊe¶ƒ€Å9Ëö¥¦¢2ޱ_¯]{â»/¤$ZþŒ™nöжÑ¢Õ׌]¿ûÃáMŽf§®ñ?h½ùWl\ýÏ­¤H±}Ãû B0Œ” ?ð¾:£˜B„FclÛb%&ôO£ù5—›Ï|\ö°:«XªЈQ×Î5cÈéƒÖdêoP¡ùU}}6Ü]VþêLÈuÑ7n§-v¿ÐÇŽŸÜùþ~:wЉ~ÝÂa±cßêÈï€×g¦•ŠU77ÿ“1÷l³š^o÷¹ô:tÍW…ØÕÌø‚ß`ø^ý÷ >©µ€Äޏ ÕƒB©»âÈ­Peæ»-ïãfnCU®5) /’3ç9éAâ‚Jeìãyøšfš_ÈT7ï*ZéSmS™YÒ©TÊxÔ0AcF¥Q¡õô€üýÆÛïÃñÍOi‚æ–Vr}'^`:\ǯ^¹‘ꬃÆhÉ–æAO¥÷â"Ž<*Ç—×¹5 O%GUo3ïs"wDDßÑõ ÜÉpåcn©þ_S'‘åŸz©£*é±0¬ Eþ·2@$÷|gÛ>9ÊgºEçü¦:íu¾Ù’ÑÎ}I^±tSâÛ¶Mǯ׬³»dÑ‚’eY‘àVÁ7fŸœBá×gjh8«ó42®æ½k&A3ZÚÝ›§ª»ós¡ŠñšßÚ3IÞ«àP\ڼݖ@cÊús —¸ËÆ5ƒ®d øm|¹ãý;As¾¹z_È„µnR®~¯_¾­i‘RâdÞ<à~T‰±šo2$ÙH®¸TÙhÆX žo€˜yHŒæ´·>"ùÄçÛ÷…ˆÚmU\¹«èý«¢juCi:“÷(nÞþ¹ðD_Û Wâ>77vöÐL¥opÐFrò( Òû˜~üh”ô=û²i§µ³ûÅ‘çôõ‡O 0•ï«'|ñ®ý;—1öàw‡IÄæ,Æ¡äq5}Û‘ü0WU¡=³¿¾`MÍé \FSSx, û0ÐÓ¯¤jÙÒ1÷¹{¿ÖVjå)ƒ×0ÀÔe Qß\ÝX ¬>”:N~ÆŽDùkŠ”'¦’PZX’{°„•$ ¨4¿Ðë ¢ˆ½t HE³cd…6ð÷Ö8åŸ{ekÒñ8kƨ»!=:SüÊuù8â‡&Á0†5ýwÜ‘º«?×ÖµÂ@É3\C¼iûÔØ8z‘©îÎöÅùûCO‡¨MÑY¥/0~4sîÄR§t~HëX|Ù÷à`L°ÈÌñCï̘  9†Ãf¬mP^oâóH»-Û²ß}éîÅó)LQþçÁÓs[(dnœ´ô`°2𹾨©š}C¿Eõæ¯Óµƒ…Žwwr!Æ[¶D~¬mE:{0Ò ƒÛBÈ)2H ¸ùcc4—ë £3•ußb… ÕÜI¥Òþ¬ûÙ ÞG<„úIMVl`êljICóŒÏy…]€|ÿ„L ô¤¹x騹eܹñ7iÑ&»Yؿ˩ï_¶’þuP{ž\  ‰ÿÔÚ Í‡iTr{óOH|¡¶ò ô)ª&öݸã€ÏçQXˆàê:¸íΩñbv\ôyð¹9gò„‹£‡Ð)ýjTõ4‡ïÌDì""n§.ª?5ÃÚ¦å)bˆ9 †Àà… atÖ7öÙ†œ{/èܽ7i/ë!pj[¬:ùƒJúK~ôŸŠø˜b··»­³ú,µ„ÖeÌ•Žàà‘•‘åçGXÿU…šêv†>ÚOìþ‘¾Ù ÄrO‚´öÏÎ ¸xm<¶ä\¼«ßWë`H6Ýð [Øk ؈®ù<—UÙH A 3zOÓïÒßÝÕÚÓ Ía -áý­Šää‘‘æããí,8í¥¾yKw&Xï6oÂþIvA;L4ù9~DÈ|§;è”æêZè“wÂp¿C„´ºûZ]ÙM M€FJˆüaMonl…V› %‰LEŘä¤ÄÐÈvZ¦Ô•Ö@"cûNµ·¶’ˆ@WÏKV¾ŒfÇKJK ¹°ÛA¦O•3ÓžXÉ ÅbfÆ…[VBDñÃfŠžæÏY¯zy,ÀèùF”m³œøý4l0ÁVÒ¿Žó­ÞÚžJŽ^©/€Ç’› –¨LNjùÑät™ÄX$À×ÞïÍéë9Áq3*<T.y¹¤Ð’Ÿ80çFÀ:O:ØøÞ{ÿ*1¼e¹ß÷ÎâSI™†÷Ë^opô°$ 2Ë-­2(,ï.Ni F\í$:ß3n¾gôv›eÇåŒÖ¥ÜþÑûN¡Ù9ÃhCŒ·uÅZ2NŠ)Ï7DvÝ,@ 1×L¹h‡MYè™x~”dt†Áý4Mj†º²ŸWœ;ó4Àß´l@®}Ä=ŽþÎjHtѶ¹ °úØýˆÕ²ÂÜ(xJ æVð›`EÈÉ Yzðî¡¢s?… Ž»#¸”ß»ql½Ù&„ªÓ&÷ÝÌÇOóc1ð͵Ì©"ÓDBa.&Á¿à€Ë#,?ÿif¤4HòDa¸f}ã}¤.Êа!ðbbü¢œÔÒê—/Øü"sB©0Ç=¯Þùá™=Þžw}‹¯9ÿ¢Ä¹TÌ•€ÒO3»\%µtmÊ=ˆ5lÔÑ ®(‹ã€Õvä”P”•ʱö†1ÚÒ Óß÷ßC%2E‡DÔ5—잢>ºîìø”_Ô„PUŸÄ9Öz Ú4NàNÓõÛÕ뜷–†ÆæS˜ÀÅ…©îø”“Ûfbô‹>˜ý&‡?¦UèáÏÞµ?q½x]’Ö‡n¥S>­Ÿ!ýýølŒ¶’þUÀ%&‰«o_)ßÅôjj+yYÞÆðÆCþ# Dp˜/X”r%uÏ’óÔ™}“k‰Âaˆ7ÚÁY2„mG«Í}™Þnøfîh+XŒ¹Ÿ5§í®Ë‡î„.™°(IJ`“&k³ÉÌ„±*Î#ç4K3áݳh?ç•3 FU(£¬!h¦‡WuqÜì›h#¢²,&°a4P@ÿ¥©úÛ<.½»Âóê›ó«¿Ý$YÏïBaÓé9î+–d£ç½ß4ô´9Ÿ¶,T—Åç|^4…é×Ôž•VÍ 1’ ÃpòpÀ§û‘é$¾Cò„N†‹gu9¿Ê¦“žg^ñuµ7¹¥ý[>ûœs\Ž›9î̯¡õôm«‚c`–‹\λ¤ùxÔ.8Û·«KL_·ÿÀ1Ñs!é5y¶ƒÞ?³öHà‰…7ÜYÖa0ÔÀ¨DIO†å‡¯šº²^.ïéh¥ ¸ß1q¾—Î5¥ÒÛ+Ðä~À(>$uI¦k²jáë.$žY>à?ƬԠ¶D˜,]‡¿süÍ^ÛÄÙ™óûN›£™Žq Μ4ˆ»O^÷9h··urˆü¯XÜh¨6ŸZ:G{:¬f0i ìQ3¢úþ«E¢.§ŸµÝmúlÔ‹õFí"‰ØÓÙIääBƒ*¥‡q @ímëìáä€Ãf›§[^»)Kßœ©ÚN×O¾¿‡èjë!B„õtµvöôâ0XÒÛ][Våû™×Æ'ÁbÑ ™Jmi%P¡%LGG[Ww5—ð´'Ùf)ak°a;ôféñÁ õ•¿ª{TŸ"J[#gBÔ=0(“q¯4g´;袰]ÎÏ]uxÜÇ=¹5¢œ´ž,ÜûùCç¤ÅSðÀ³Æ´´㹓áT*¡½* w4“H(†F!PâJ Œ Ò>¿òÔGò £i-'o7C¬uèëq¡Ð6gã½²•¦GëðÆ+êMäC´­­lç8ï…•ÂX¾ccŽ›ÂœËÒèÔ6fwz{º:{ÐxŒJ&u·ö0tUOKG¯8…DÐz;»zŒÁBìh#Óù‘ LÔxÓÝ€”©^áÆúm^Ûí¸»KOúJo‡¯9g§ÎZà ú%ǽ•2}±{ÊÐHÙY•ó™åÜQü4·n±ºÔÌ®~¢ >O·¨‚î¼"Ü´†¦n¼€Ä -_×uË‹b¨$RG'´ÒfôN[€Ea0¼Ë£WÙÊ“¯<«ŠãhMuUŸ9 ^Ý—å†Ó+ ëZHZA ó$"F ;Úš™l×ÚH¦@rDLYêéõx߃Y¬²þ4)Rq~qÔ9ôö¬ì÷‹õÄx1XÇØ§Y“•ϤS@]T™n$ÍB#´Š&wçA‚–µ£³‡J¨Tbg ƒÓi ­­ª "º‹DÁ£ éfKàé­ÐÕ³2®ê¨üÁÎj‹#Ú:»¨·¹•Læd¼ÙƒN&µ~ê‚Ø¢<¯ AW˜ ‡Á T2Ä)MmPíiÄ–öŽž4 '}|-w†ãË@+Ä1±iÆê´îꒂιWªÎYÓZ['ãRhtuww#Ñ8KXpˆi©ý,›°1 ØJú×1sSD×Ù¤ìhM¬lêh«ÿä‚÷Ó÷)§ŽH9Íyqíz½Š«›&ý}àæs¶®­îñI!ɺlÙBêI8p…ÿàš© Lâàób›„kI¯ [»I(yïËÎ_ûù¥uu¾8çšptåxf¢ÆÜË^ÇÒ¨œ¹››%@ë}âž@‘^è°Hu NCêˆ7(n‹-”ËÇüDul|6ÙpôÏm¿?ö?IÀé¹¹M§CRéQè¶G²ûfGî ®BñºnÙзý-Iï°@án+’Ó—¨IƒËÃâ$÷Åç»Weß{ò<;/÷ÞÇL:Äp ÎZë·=Dwš®Ò˜›ÞäÖÄðÃI™ ªŒ6#mr–5[±s™)Žs8ùXgÞ͸äÌú¶Cl}à5£–xŸ;å`דÝ'_tÓQß´¶®Ýy‘û¥@G`e毵˜g£*ÂX‰”&Ÿ ÊàÙ²e …X¿Çç¤Wð.®¬xÿóä]ÜTé½G·í^âyÈB­êòð6ýhjY3ˆ—4±YÉ[›°íÀ£^Áé[¶“nnÝ–bàˆQýƒÇã¨*›Ý¦½N¸&!§m:°“¸#ø0m…›½éÙÉíY×ùïÓåB¦O>~NˆŠ~[PÙA  ¹Eí6,œ; I§§Ç8_4ÑmË$2¡xçŽ]&+ýV}ߎˆ’ßpίiⲑg”ÿ¯½{jº8¾1@š†øÀ|¡¦a¨ˆ€x‰‰ÂDVøLÅy¨ìŸšóQP|„¨ ¦h¾¹²0J´3«ÓÐLM¼ÌL¹2»ë½‡0y´ìÁ؆¯qׯ»ïç8nûý÷ÿïùÛwsî›úõgÝsòÏVeí|†LÔr8¹²ÕÞëeùk×–Mîd(Q“¥è*…ûÌmyílýôR©²¹§oàÐWF•™œwõ·»'2WuX“:¢}ÌkÅ©ò— vœ(.½]YçèÚU«¡‰êÙÚ®; ‡Ò²¾l+éõ÷*X07yLjFû3¹™yE^S%IV••¨ŠÕO ,KY¸³Ôu€^\u!mù;Ó—Äô7ݸæ¾{;êøŽ:’_#W NH_;!¨£Ù#uèÑÒ·ï:qéç»Õ²ÖÝÆgw}kb~Ií± ËšÍX5ª¯ºóù‡{î),®R8uö>RÓêFáêG •'SßòH¯HZUhhÞ_¯4ÞÜ;ëõãc–­õ0ÝSúä^ù}üþ]Ÿœ¹|«Â(wi1~NÔÈaíÝî?[öV°þÖþ«Í^»ÿ6Êë¹뾸V>X/…š.ö)ºçGK³GøÊå­:^°;ÿó ew*•-ºÌÉþ ÷ÅuÉÇnÉŠßNÞíßM&óÎ*.}pç‘¢oÊ F™Ê}ØèDÍȈÎ-å—,ÝsñÕÉzS’³æ%%¤§ô¾s.mEŽËX½$»÷þš¥e32¦º·µ¯hWŸ§¯W;{ø¿¨õw;·xξ_ªUSôR­ñô,]ñð…qÍKÒRVÿøÔ8I’W]ÉCÿ^È©óbƒ:¹qùö J­^RÔm[|þpßĤ٦IÛª÷øÓßúåååŸÿî†ÁXíÔªÓ„yëcÂ|eÕ†·.9xs¨$…U}hæÜ“ZÝbMŸG~Ï ‘¶ƒƒkØÄyaë4ñ+4ÿì °ü7×Úäl­­8Dë¢ÍžIßü€šñ쟰}_ÂC6mGØ´Æ–¶õ‰Ü´;Òz|Ù®;rµVJµuvþäÚÑ/6Þ/öa›hÀ±eLâÊÛËä=Cã’BãÌF¤œ—,ä;|‚éÇzå¡ÒÖІCA1ƒ¬ŽJ®ŽÒ§DÕï³!wŒåa<4Ù¹ËA™nËNíîÚ!:~N´å¨< fI@#gõšÅ-ZnsA§&¤¿`vÞÍÏ®ÿf‡!¶?­l«‘l~ –R›íýëåx¿ÈI~6n O®oTÒö¨†Cº-–`Úf«¯ƒ¸Ï­û™‹l/ú›c{í´æç«û¬ §¢ƒoøßp³§#V¾Yÿ*îúmM?õÐÑÓ‡Ú^è4(~IýEîÒ)!9³‘™éØ/rR?ó‹´ÛÒÍ–É)X« ¶š]êgÇnÊmø}/-,Ë`uòÀq Í®õ•9Öïy.yC®õ‰ë2{[H#§Û£×ؽ,¿nFéªIȰžhBDAiE¤‘@PDAiE¤‘@PDAiE¤‘@PDAiE¤‘@PDAiE¤‘@PDAiE¤‘@PDAiE¤‘@PDAiE¤‘@PDAiE¤‘@PDAiE¤‘@PDAiE¤‘@PDAiE¤TSFÚÝݽ ·ÀÿšB¡°s MiGGÇ&Ü €'PQQ¡R©”J^'þK¦B_»vÍÎØ5ëêêÌ÷–””Øwb@=»"]ZZjú-—Ë-j þe eeeeMMÍã®hW¤===eD€Æ)ŠÚÚZggg‡Ç]×®H{yyuíÚÕ´ÃÅÅÅþ?håå妻Þ ˆÀô$Ö`0¸¹¹y{{«TªÇ]Ý®HkµÚêêj¥Riz>mϦ4£Ñhš’šl™ygf3¿}fÞw†E%b  HAƒ¤ €Á RÀà)`ð‚0xA ¼  ^‚/HAƒ¤ €Áë¬SÐn·ïÙ³ÇjµŠ¢HQÔ/Ñ&ଂ Y–2dH||<†aý|ãY§`YYÙã?^TT$ýŒ¢èÙ¾ø%H)¨R©xà[o½µÿEÚY§ Çãinnnoo?Û7¿(¿ßßÖÖvV÷J:ëÄ;í»€_†ag{ò§¤à‰ã­ÒÌÌf³L&;Û‰ç„TùÕ××s×õ+I’g„?«(˲>øà¬Y³~ÎD€ŸÌjµÞ{ï½û÷ïÿioÿY)(åmllljjêÏ™ð“iµZ‚øéYã ^‚/HAƒ¤ €Á RÀà)`ð‚0xA ¼  ^‚/HAƒ¤ €Á RÀà)`ð‚0xA ¼  ^‚/HAƒ¤ €Á Rœ "×Ú`õ#б&‹–@OïvØ]_P;Ç4F£œDí--.ŽGPÚh1PØùnõ|ÍmA‘)´Z‹ž¾P€ ¤ 8ì GþñûÖ6%¾ê¹çïIÒÉN}–÷Ùöoø|Õ×ßnßwÔj÷ð‚HÈâï~êék§˜>øÇ£o~µ[“8íŸ/?žoü%ÈUîÿúþ?þlm“Tv!˜%cìåW\’¨!Oa{õžwßý¬ª-“9áÚ%sͲŸµ¡^g»Í%"˜J«“3}Ìõe¥¥¥M˜¥9ØëÞ»çË·þôØ?·WØ0†•ц"„Ïî祢o­­.++U£i~ñç4ð쉂³£Ýåç”2˜õdŸa!zªËJË:ˆÆVçùm\‰¢èµ5Þ¿sëÖ‡•5¶´»|†S¬Rm4™Â£bS3²s²³3S£hìøŠÁk«OªÈQÆhî«jÿõEÓfwû”Й '€s RðÂ"4ïùàWŠ]¡_è°]ˆ!éþK‡ž¾ƒð´/}÷ͽ5žaaó¯˜m–õ1­þâ]ß/{å/O¿×Ê$ÜùØã·,Áœ~èÅ(† azŠ´›n<²ô½w7—¶i¢r®^rõ¸á)j–Â1YòÐ m#(‚ H’&±ó\µpMŸ¾ðø«K7³±“ž~áñ ©–¾ö±I‘)5ð×XŠ¢àj:üîK/üߊµ…UV_Pèõ§”jÃÐq‹_øïã9aª®ý¶²—}tùöc–! þýüCÙ‘êóÞð~ãm«ß|êÙ·V‹¦áyî©9#bûþ²ÀÏ)xó7å¯^±bÎè¤ìÅ/ºð9ÛÊJJ¬ Òáôù™6æÚþœ×æÓDdYäÔ‰ÇE1ØTsdg~H¨'.ºíᇯ·Èˆ®¼ ¬ã‘™×ýÞ8n¥‰O2)É%8(´Õ×—”ȃ‰¾¾ËPÜ?ò¾§þiõc)ÃFÊ~uû_ÑÛRüÎ?ÿöÏ·¿jtóKü˜£r‡¤˜µ L ºl-•eÇ>ZUßV[Që?5E®¡¢¼¸¸ÄÎ6Îsý}ÖD{Kƒ´D»ÑÃÿÚÛ .\‚*‚%ƒž@Á–¯¾Ú<+ýÊqÛµ„bMÓ_7íô'„ Ãz¬Á@e渤L“œÂOB9|Æ¥ÃÏ[+Ï ŠÉ ‰‹nHèvœÈÛ¶úƒ•k¥T%M»ÿÑ{¯œ9*L§d(B xžó»œŽÖƪüÝÛU£Jò×Öï€_HÁ †E䎢*—6-_þÅ” 9££äg[®ˆ¢Ès.궉b8EQ8~ÎϽˆO»O0£Xæ'õEAà8.Èóbg;I’"ð¾ J/ J¯ ò‚(¢Ý/%~ÑC­¡9¤u*U0œÖ!Ö¿c»¡•”Ö>'µ'¤7žÝ1a1ؾÛ®ÒF!7Í»á¶[O3°Çÿ–QŒ d½L­3Å¥äÌòhVþS–픹 |h)¥ÕŠâ¤Œ¡ÎÅí±Y1'¤mÕÏu÷ ZÃÁÐvD)š!ˆ]Ÿ+éƒÕù m,é80 ¿HÁ Šâ‰y3rØ—ß_tÃÊ¥«¦eÝ1CÑï~è¯ÚÝVtäHá±cåUõ¯ÐšãÓ3³Ó"hâøÎH]muGÍ/® õyü…û¿ÿž§»gƒÊ´qÙ† AWÑÞü6N$e桹I2ƒ®#{ Ú}®CGšCû9Q¨/ÙÿýVWç6TkIHK ÇEåá#õv7Jj‡ ÏP½w‚Ò®©½¡ªèXqiiiMƒÕá ­16.!$1Á¨¢»ß Н£±®¦¶¦¦ªªª¦¾ÉááH¹:2&.)95##Õ¤<¥·¤l*-,­-®jµ‡–‰ëÈß¹i5vïÕP<"!#>LƒJÕV =o¡[@ŒQÉɱf¼× OQt¶Ö=täXIIUÕ'à:sdrJJzfVB„G{tÏ”¤¦¤°ÆjG(]ΰ4™è­*Ê?Xp¤¸¼ÖÅáÆÈ8iÍ–©gÉ~öê¼mUM^ad¡U§fú8s)M ')9Ioƒ¿¦à`i}Q‹×ßù›uÿŽï½ÇÏ"¸,)=Ó¢‘IO9Z*Ž«bt|jF¸–lª,:˜¸¤¬ÒjóªãÆ\ÕEz´uO~¹”²ˆ!¹q*íµjWíáüj»”ÑY£bÔ½»SIk£µ®¢¨¸¸´¤¬¦±ÅÇ£J9..^Ú¨ñ q%!b[MiQEqI]‹ÚbÎ#{vXµÇûò †ÈÔ”8i“‰ŽÒ½G›5'æ$‡õþ"(}Ì­¥Š›}8ͦÉÑ˺×°ì=ââE\=<; q5.(þ*ë¬AB;~ÎUÓGDâÝË»Zê;Z\RVQÓè J}XBbJƬä3M@ßHÁ ¡Š]8Å|xÏÞµEUk?_¶`Fî¤Ò}¨´ª<¸é£÷Þ[±fKycG0T6IíN±±™£.¹bÉ5—ÍN±¨B»c1pdíwÞût‘3à•Þ¨ùôå‡W¾vü[0¡·è±w^º-œ%œÍEÏÝ{ëgG›’ÇÝôþ‡§êdîÖcÏßÛªC•AÞçöñR³ô¹?®z±³y¸êâ[Ÿxñ雵¦OŸÿÓ>Û®Išó¿e/O0žÚL_GÍ–5+>üdåæ½…mÎÎ^¡&!(AéÓf\zË_Y£fBpÚª–½úìûßl+,­¶{¸®3HRJ¡©6ÇMœµð¦›®œGví²ç·ï>ûÈëŸ[]^A|µ{ž¸ûº“_ì™È;õo·O•¡Áš‚õ÷\}ÏqÕ}ÏþëOW:R"èwlYýÞ»ï¯ùno“ÝÓ=R*#Úô‘S¯¹þ†Ëf3+N9?êk\ñ꟟yw“,qî_ý£{ÿê7Þýt_i£_ªÃC®¶$Î\|ó]·_£ë_J/ ½Œ»SèÇY³€«ñÓg|nÍþv—GÚõ…ëï»iˉ9¡ª!O¼üÚ—%yçöU¯Ýñà[­²”ÿö×ù±wÿ÷á–ƒen'UØÆ\ûÔyëš{o}¬¨Ç›ï?)m¬M«—}øÉg[kw†ºKË* JʶyWÜñèýWEÈüÛ—½z÷Óï4º}¡ÞÐmGþóè­/h+e¸êžçþýèB%,ÞúÎ w½Õ"o}úƒ§—Œèõ]€s6}þÒx@‘5ù?o}°(­»+½nÿßïºmc™-yê=ÿ¾kÈæeï~òÕÖê§ "í5Ž›JA‘÷¶ïÛ´ú£?þfëúvWç'0ô—B2ªÄ챋¯½áªK¦Gëd0åB)xÁBñ˜¼y‹nÚ÷ìÒ²}›V­Û9N:ð%SRÒ3|õuÅe ÿ©£?¤R'¿æ'žz~Í&—Ç$¥E[ 2œ³µ4–••ïþæÃÇJJëûës´'sŠ¿WZ;–ŠO_}~׆oÛ(}|Z¶‚&8¯£¾²¸®êðû/üS`TÏ>|­E†ÿè~“écàqÚ­[×|³?;jd‚©WÚ Š¦ø”ôtûáü¢v?‡’êÄ´„Ý}Qe’VAwý,ð·´)ƒ¶ï¿|ÿëŠM‡±è褖B„ iÑHß%BÃ¥ãÂÜÞS»§}<¸ëÔç¥ï%¶¢7žþÇKï,«´ûi¥!69Ùb mVÃÞ.mÖúâ­ßí¹ã®Ë#d¨Êž‘™Ž–—W6¶KK“œ¦>qê›ÒGYÔÝŸ;Þ/}~\¼ÌÏõýE è—žv¡OðÔo "ï µßÕZ²õågV~ñíaCL\zV<ŠD¥îV$øë?zã_/¾¾åh)7Æ¥ 1u,!º:Z«+Kò·~^^YÑêp>tÛåFöW=Þü(HÁ N§Î_8fͶ/Ö|¾lå”I£ç1þà1ÑÝTøö«o.ßt˜'5ã\w÷×ÍŠUR˜ÇÖ¸kíÒ_}󻂲o½hŽŽÿóuSÔ49ëº7ÆÏýæíg~ÿà+-tÌUwÿûž«FŸÜuâ´‚éû#¤0¦=ðŸ·îá<û>{ðòû>µáW=øÊŸ®Ìîzuh`7Žô½ ®-Ÿ½÷ú{Ÿ·úÃÓ&\sóM‹gOŽÓÒ[‚Žæšü½»Ëš]÷¬QRiš¾äÞÑ–ôÜì̘0½Œ 枆²ü/>|ãÍWʶÔ!9^m–(¦šqÓ#®¸ú?÷Ýý÷¿•Gç=þÊËSÓNŽ” 6´€g¨®¤ ´nzé¥ÿIˆ«ã.¹æ–;oZ”o&1ÁÖPºîÓ7^zû“Ãu{—¾óRæ¤ë'§=sÉÙtô«¯ªÓ'_þØ-K&å&kå”ÏѼgíÇÏ>÷ê÷Ǿ^¶|ö¬)—ŠþÑÝ*JhGN—¾rÓîjÛ¶¥/ÝÝPxÑÔñó3SãL:CSM“=Ï]QŠð«ynî-GYrëû[ -³^ø¿§‡)2¬œBOYnoõŽMVcJÞO߸`ú˜“áÜÖ6.ÒÂÚ¤y} Ú7,}÷Í?«´óQC¦.¹ù¦…3ÇǙՆ𜿽±êàÞ=5NM(í0zì•wçοâÝ'øÓ +DCÖýÿüï%£âNŒ” (™ìÌÛ¨¿_õ±¶&üß=|ÝÕ—ŒH‹aIÄÑÞÂÓ\äJ·-í¥W¶m‰Ê™qÝ7]zÑØh“ŠDE¿«-ëê×^{}õ¶Ãï¼öftrÖí³³(82z!ƒ¼¡XdÖÄ…s&m;ôQíîÕË–M¼ÄÄœ¹‹È—îøzå7Û<ÜìRÅŽúÃCÝ~ÅT5}r–*>&9ÛçöòÎÊEåæøEwÞKÑTÏRH¡9#6>‚··<ýá÷»7l,½a®9A'½ž’É)LI]ç( ©’~îÇÄ€sçš÷• ˜|úÕxâ‰;ÒuÝ%”2eøõFËháÁ'Þm(ÚýÙªuJˆVô<+&Lc=ö÷GgeEt/ŽJ5çºßwT•þãc{Õ¾mGJf‹RÿèÈ8”H›0É•GZÞü´¢½ußúO÷÷¥Á Ó¨u–ˆ˜¬a£ÆŒ••‘efÈãK†b´´Šî“Y(!mtÉ™•Ã͹w<øð—MTt×a]çAk{¿VU¯©‰Ǿ[öÑE-nMÒ„{ùóŸì¼ªTk qi¹>—”ŠN!i‰«dTçÚCq¶³©½ÖŠø³GOˆ|`Øü›þüäýCMÝ'»54]ÎV¶òã×i”EÿݽOÜ}ÅHÅñú\ZiS/»ÍÂpö†¿m,Û¾ê˵óƧũH8.zႼ°a´aöâÅ›¾ÛöáöêÍ_¶eþäE#£»ÎNð·ìܱ³¼-H)cf]¶(/ÙÒ#60ÙˆyWÎûz]ñª5…Ûv5ŽŽÓžÿ︢à=´gÇwûê„6uÁ¥ó&¨N‚bŒBqÊïÍС’ó‡:lvöížfÈJU)vØê6ÛÇI)øóøíå›6ïnu ê¨ÌKÎNÕR§>K0Æ)s]öÍŠƒMûwnÙ[qMÔý©«Rš§]²plJX¥!µyc‡EYV¶[EeV_€W“?þ‡Ijâo¸ÿ±˜””—®úþ@Q«ÃÛÑ\ßÒXWV|d×÷ß~ð¦:.cļEW\yÙÜÔpõcíž2vÆÜé£åçb …È»÷oß¶ù@3‚ÈFÍX0æh–èk³ÊfwֳÆ¥Í]|éc^¯""4WäoÞz$ˆÊG͘åüaŠ^«£R§^FCõç[¶ô6”Þµkçö{ KkÚ]Þ}dí•Mž¦:j[Ý¡¾?«u¢«þXQ]}Å#cGH ï]w£¨!:}ô°¨Ïò[««ËJšø,ý©‡7E̘œ¶÷šGµá&…Z&6yíVWð´«Àœ*ÓÇÍ^rÏè ‹ ìß½s×Þ¥Õ ­mmm--N[ó¡í_;r°¸¢ê‘ûÿ¥:ûbE3tHV”Ž>UŽ(ølÇŠókýRñ›4fôøHõ¯¡x"Œ–äáá½ú¸"×T¶»¨Õ‰²FƒRÞ^Qè:½©‚› ” ñZ+jmNÑÿÜO8‚<—¾è’©_¬û߯cëW.ûrÊÄ'÷=ÖÛïtÙ›;¤Øðˆp­üô#§(FYãäÕâõÙš<";ïâ‚~_Gu=/"”ÁIþx5*=-–¾ýÊ›î,¬ñrBh9°“EÄ Oú¯ ð>oðçBsYÛœ64Ul‚^NŸþ œQ„ÅGÓè^«ÃÞfåÄ ¢G½­ÓËe§ë(NÑza0ÀŸÕ>#uáñc,±ÃÇN¿>à÷8;ê*Jîß½iÝšõ[÷·ÛëW½ÿ¶).ó©;çkÎv˜ŸLk6Zú±þû…óy;ê¥%£M–Ȩˆ_vg?a8£‰5²T¯‡Ežk¯¨ò‚¢§~Õ믷ïëçq§Ë(‚®oh!„à… Rð‚'íOô Ã/˜ùÝî’òâ-+—®˜œs__× •þº9.4:¡Yš¢úúÛFQ’UQ†„.?âùù¡ñˆ<ïw…:š’ )Sô3½_ôîûâ'žúÏÎòVmDÆô‰ãÇæ Kˆ ×(åKðÑuÿ}üÕÏÚBCþòˆœ7 Cý‚T Ñ×qFŦód`Ðx{—u~†ƒ“ÇSû§´1t…ZÆJÿTj­%2nظéófM{û•gžëë{ÕÚo¾]²pÚÈè³¼®NQd¿ ñþx>à z XJÚ¬¿ŽÈ¾õ)Nò'Ђ׿ùЦSÃ,½OGö 0öy5{p!ü v¼ò± ._ôÝæÿ¬Êßûí—kçÏ™k>}oŠâItö« x×ça7©prÁH’b¤xè*6l¨l úƒþ3\¶ôT¾ŽŠÕËWäW¶©¢Ç>ú¯§¯™‘«‘QcXh÷& ìè'?·3á)­#i '¥pE¤}:ß×Å-¥}¨ßï ­ (’ìWG¢s-Šæä‘W_yÅ–5[7T9­%å Í|”üì7èBðÄ£g¨[yžïõÖ¹Y¥¤ïa>Oàœ×NbŸM‘6ÿƒ‡—ûZ"}”Q(†Ú¨+îé±+sp/‰²Œž¿ A þ6 lİù—-^·«8¿:ÅgkÒ®0œþ"Z)W›4Òêill–¾íFô¾s¬T-ZË«<‡*•)üôáâ¹Ë“3!hFŽ¡‡ým­ µ A1ú‡ä¹› w«ô¡Tòȹ ¦´h˜SŸ_mUç"ì™&pVK„*Ì:…†E\ŽêªO JÓûxšTÉ6U×ù¤}2«UëŒ5” ÅcTrŒE†T9¯Ã8—·®’R¶ó+†pûOQà:Zݽâ‡`dš( ŠóY›jy±Ï›xœ}Kp¢ó»ŽÈùú0(p¾ŽVïÙ~f1ŒÔÆFP$t{:¬2^ Ó +PbÈ„9ó&­.üdçî5ËWèÇúzDIµ%!.E†–y›mÎ/Ÿ¢WôìþpÖnßqÌå 2zmRF s¢‹½ uAáû,Î)ŒV&$çF1*œU»÷ìh¸tXÌ9—Íé÷K».Ö¦ z}žEoóÑm‡Ž9üHïDÑ®‹VŠ|è:ªý^(TžœþýÑ¢öª]ûË›3Ãzž^E[CÉî}µÒº×GGÆ'‡ýBûÏÐeHyÃÏpAU¤³¯­£­Ã:ŽÊ”,}â²wÑu >À‹ýì†ÓÁjUð^gm•Ë4ö¼vŒßV¹§¸Á@Nùl¡¸Lœ”Am®³—îܽ£inf¤òLJƒtô<커C)…AŽB ÐÑØ(}à{œÅÅŽæâU¶³þÈb¤%eT¢ö“¦êŽý{·æ×Ϋ„bï7 Rð7•[R-œ·aóÁ «?ïp´úz½cŒyyyñ¬;ÚQÿõŠÏæŽH—lÿbÝðŒSö,! gY•öÈ –$EÞc¯:Öd÷™'o2%øZÖ}òÉ×Û‹¥(è}rFúÀûZÚÜ5n¿ŠÖ&M?|Õö⦺#Ÿ¾~›Ô'ËAÞß¾uíªíE"&Ï9~x¼ú—ZS\ûþûœ¸){HŠ^ÅöÞ R•æhܸîë}µ¡Qš’ÂLÚ®óÀÒêÕ+¥°9O³ÕîÿiG&MLªAµ§ÜÖ\¾måU±šÈSú"¹ö|µbÃÞcÒ¤O=9âòacÆŽÏþø“½-»Ö¾fò°ë挔§mV¯—±Ý§NQB-—Q8âñwÔ¶:xQìݯTúd’cT²ÒV[áá]GšçŽ;9Ϡ׺uÍç{Ë­g‚(Ї' Ÿ4:egõîòí«?ù`xÔ®ŽÓž~HB*…í¶ iRŸ×Ñàœƒüí@1*mê¢Åwø­oÊ«QAúúܳw J¤Œ›}ÉŒ5•+vYûþSzÙ=w^——ΨÏaÝ»îÓÿ¼üÞ¾F©KœµhQV¤æø»¨è¨ð˜0º¬ªmÇ·Ÿ‘6"ÉÌ„n<‹Òr­Ñ <ÇýýPL“<þò«¯Úyôå’²]/üó¶ö›.›=!Ö¤ÄQT* ì-u‡öí)i¢.^rI¤’fÍé#ã#ö”­Øýùÿ>ʸkÉÜ(­ E‡µrý²÷^{{yßמW&&E*Y¬ÉQýåÒ¥qôœ8“:tvÅU:ƒVqÆ"¥3gÑĵۗí¨XÿÁËSÂ]7]š©Ã;çøíÒ×_üï‡5n^—2üÒKfF*~±¿/Þ¹é“×ÞÙ\—=~âØÑ£r³Rãc"µÊе¼|®ŽÚò£׬øèÃeµN—‡Í¸hZJX÷ÑoœÒd¤‡ÉÖ!mÖc+—~fN4kØÐPŒÒ JYï¼}’ib¦LÎ\•_ÓR²íWþ§»ç†á & íÖÊ­«?yãUº{Ÿ”6«>cêâk®Ü]òFEÑÖçžy¦­õ¦E‰6„Ê,‘t4×ìÛSmWÍ¿j^Xו0&>.B§Ämî†o–š©å¥õº ŠÉÕz½Z.ý¤OÍû]Åþ£›W½ôŸxúÖ…©LàZj ¿þä­·Þ_iû CëQ”Ö%-¾æZ©ØßTZ·ô­‚¾ök.›?4)RÖyì„úÚk ó÷nݲMȘõÇëšÏ_jpî@ þ¦ªø‹.Yüå¦ß·õõ×*ón¾ýweMöe›‹Öð|ÙþMé ‘*êhi(*Ø_ÞìF•1ó®»óö«¦hO\®ÅÂ†Ž™<}ü¾ÿm(ܸ졊}á5%팤o÷ÝüçûšdçøS„ªÉ ¯¿¥¨þÕ÷WUøæÙGóW”&í£q¯ÓÞfm,/«$#æŒY4WJA™.qþ‹¿;Òp öØÿžyðà†eq‚÷5Ö”åï/ð[FM›¿ýû½ÎÞó ’ò&²îó%kßþGÑæeugP–ËoèÖËF1gˆAų§ýþŽ%möW6-úè…¿lþ,1ÊÌ`¶ÆªC‡ícÎ^¸äŽ…“ÒÁ1qRÁ‚êK”•øòS}tt„Å,­ZJÎëji¬-«¬qùZ6ý²ëoX8Esüè#F)‡Lž9ô‹}›‹ê—¾ðçÝ_ÆjäTh\‰<嶺|Zj:<rä‹§l,X¹·vãGÿ©)ؘ¡§0ÁÑZ_tø‘4iÆ8ýÆm½º6¡¤vÆâo*¬ý£Õe»¿|¦lÿÿ—¦W0˜Çaomn(/«Ö¤_>îÒÙÝ)ˆ±#&LÎˬ_W°ãÓ—ïÚ÷µYÛÙ¥“ÔξöÞ?Þ0U£Œ!^Úú[wn­®\þÚãEÛVFÕ¨èoo¬.:V™3i¢åØú=Õg½z1*}êwßÓ½úζÂCï=ÿ×Í_-OŽÔ©˜àw8lmÍMÕ•åu-¾?]Á€‚¼ÀH{aR$ûî–†â #¦.š=® úgè|IHEÛ)/”Þ=îš¿ü]—ðÞ»ËÖ|_udWÕQÇPž0‚ŠÍžpÉ•K–\± Ywê½QR—zå-÷s¤qÍæ]åµÕ‡j;¯.+˜ø¹ÇGS Ùå´(Þù0Ùç½ 1²ë}½NŸ¡Œ1íöÇþ—–üþG+¾Ï/Íß¹© ó$‘ ¸(® Kš5y”†&:§ sÙ-¸Å—þûþÞ²æ×ì%¤]¾ Š”Q n¾õæ4÷W{ |h¯Ëj¢áYSî½ï>eùÖýGꪊk»ÆªÓc¬Žã»5´«m=o&'å;êÒ?üEóÎ{k¶(Ü»¥p?†IõŒ4OF9dâ”kn¼éÊSM=®]€bxŸKzrª}¯‰3!tãç]q+¯ÞŸ¸¸¼ªº´°ªäÄéÍÐXIJ®Ë6jö‚E‹ÍÏŽ=y`ň¤ñ‹zÈöɪÝGJjJŽTtîÅQ%gµ{O,aW[ÎtÒE‰ða—Ü÷¨ƒ|áµov—–çï(/½guÙ“—Üþ»kt‡_ß±§P${œ§“ZÅZ†Üý·'“²Òþï£•Û ÊìØx0´Y‘ÎÍJê"RòÆåž¼j6‚jãFýþžå†O6쉲Æä7?4rҜЎ¾¤¬ÞÚÁ‰¸BcˆŠ‰MHJË’®îš‘T:F/¸å¡ô¶ï-(«¬súE¥ÞŸœ–;rtv¢Ù_¯å­IA„ˆËM:uŸ‡Qš¼7¦ŽžVZViípºR£³²;×+‘>éïo¼kçѨ¤yϽ%)ÓŽ›w]z»÷–”Ö6¶øy\c OLJ:|Tfb…õŒ\ÚtñͤLÿʆåÆéNßõ*"G?ôô·zy6<Çø£w -³2oÎ’ÜÉój**ªª*këjëÚlÎ0rµ9<"6.>%-+5!‚í}ý3—™f\sWΤye•Õ­6w÷ÍeöȄЌqùÈÙ7¼e™ÀØŒaIgj J(òæÝ“œ»`ÇÞ£¥•­NŽÕš“Ó‡Œ;.=JíH"ÞH»\ÀÉÔ!½Þ'·¤-¼õÑQ“çí;x¸¤¬¼¡Åq¥Ö›˜œ–™™&?YŽJ_q²¦_ñxÎøkJË›Úì~ŽïšwdR.{|±H¹eö’{“†ŒÝ±ç`qyÃh,Ñ)CÇ͋Փuº'¢f¹p¹vxÔÉKî©Â³øç+7xJ›jQžé pèê¨9K2FM^œ_PX\ZY]×áòa$£Ñ›"£c¤5œ”œmÑž« €)xAA1mTę̂Ôz Šé"3fGfüðkmÔØ‹"FMöû;ï!Žá$Í0g(0»Þ‚ÊT¦Ìá¦Ì¾žÅiMÞ¬¹}ÎH1b~Ĉ¾§‰Ë2ÆNù†b™:,"y¨ßççBCñÐýÜi’è•Ó(Á¨ÓG_”Ÿ¤SçGôõT×’ú¨´™‘ÉS¤Yr\çmØI†fðÞ_!º–”I6!uØ—”T„O™~ƧûnJËuIYºÄÌa¡[Á‡îx/•ƒÒ¦$¤B‡þÁ›×£8eŠI‘þõõ$nŒÉž“ýã ÀéðôÑ‹’‡Íõù‚¼´±†aºÖ¹>eìü>§Ý #dÑé#¢Rrý~_ óöî8NvÝãô&K+ZeŒfŒ:s;¤ÂL‘2|JBöØî­OR2†î:ö3lFÌi«™&Ι÷£ Ø5m¥1fô´è‘;ÿR¤©wVõMج`p!¼B{Z&ýè†ü°PÞ0,Áôã•Å(¨~¼ðÜAÑ®‹¶œÏyžÞT }é߀Ì]úZ"Wô«OMO¡Ã® «8g[ ý·>Ú9qâü~´Ày)`ð‚0xA ¼  ^‚/HAƒ¤ €Á RÀà)`ð‚0xA ¼  ^‚/HAƒ¤ €ÁkàSÐår8p ½½]Ån €_R©>|¸F£膜4ð)XYYùÄO:th à—÷â‹/æåå tCNø ƒR!ØÚÚ µ ü¶Iµ` èVô0ð) ”OAEI’¤( jAøm“võ† t+zøŒˆˆ¸îºëª««!à·Íl6ÇÄÄ t+zø4 7ÜpD  à tzøDQT&“ t+ FŸ‚À@0xA ¼  ^‚/HAƒ¤ €Á RÀà)`ð‚0xA ¼  ^‚/HAƒ¤ €Á RÀà)`ð‚0xA ¼  ^‚/HAƒ¤ €Á RÀà)`ð‚0xA ¼  ^‚/HAƒ¤ €Á RÀà)`ð‚0xA ¼ÎG A¾¸Ôö]Ï‹¢ "Ò?C¥ÇÃ,²h޳±Ì„–ÁÎCCº‰¢Èñ"‰£-žï«¹á™ªíèðn(r»edœ µñIérŽösj HhÐÇm;êÒÇ*³ôdÿÞ `€”rÂåÖwpžß_ê,òR—dʊɈº#-GtºÜx–ù©ÓxÑæáår‚ÆúZUÕ¶¯‘ù¹j¬iõ§p¢@›ó­Oíå.¥SÄNð ‚÷«n»çƒîaÙÚ R¨m ðf¡ßoE‚AÞÉ!„& Œó‘‚8‰çdÒ²1Ƚ¾šwÙ÷ÎÔYTªWµ¹àþcumÊcS”É*©I¢ÍæÛQî©s Œœ¯HÑJµVi†*0ޝiòl)uUÉËFjøZg;Cþ@YO°ä¸e¼—R²µÍ»½ÒÛäYiR rƒ?°|gëòFÄÍ#s¢‚d ¤¢ÒþåQ—e UÈ05S p‡*ÝùÍœT6fÄ*rM$ Tº ¬Á І›Ùq± Ëóßînyw爽)W¡U:ÃD±­Ý»½ÂÓèi–̉“§ë ”ãwup*Šsª¼\MOHd]MÎ ÜáQŠI ò85!ÍE!àü9?çQ‚Ä"`#(†ÊiLAc"UMâ±*ç†GƒÂѪöíâ_§ii‡û¿ß¶õã Z¼©Ü±¾Øýû‹ÌyRt„ 6'WXëÚVæ.éàejf| kÁø·5­ 0óÒYFvh?êÿ4Qãkv=·¾¥#Stx}™ã›b÷’…Ã!%/¦z×òíîÈdE”€ð‚(‰!Å%övrLšlç>ë;E>£–Âü¯Š\7O5ª­¶÷{ÌF)pŸ°×L²\ŸBºÝ¼—“%ºþ/··e)Ù(Ñ'µÿXHÑMeޝ ]¿ŸiÉ¥¹/65îeåó’dÇ}³¯£Ôm¹=S6ÔìßPÔ±³Ðž!Ÿ˜¬H7QZë_Y àçðÞ1"£“ÍcÎQ"›·Öþ»ÉSãQ¹Šìë»/1Ž·«ý/ß´}[å¡Ç  Ϭ±¿¸¹£ #2£Ø›†ÉÓŒ´F TE¥Ž™•gL¦„(ÎýZ“×êSÙw´O^am$šjÛÿøEÛ~—*/’>$£/ÎU¡ÅþPP46V9:ªµ2¨X”¥*hq vÄáð­ÊwG¤›¯¾/¹E¿TMÒ ÇÈÇ'ÊiŸÿ­¥Õ*=—gèGÄÉÃÊù鹚t2:¹)ŽÙ75 _nžFÚšl|Ѳ®Ò74—jD­A6?Ï {Õ×M¾›‡)ä™&gqEµîíåî·×ÛY£âÓŒ)òóxš±OA,VG¥ê9&†iq_ƒà焪vAKà‹Öõ8*ò|…]Ô:ø ˆ„ŠŠB}‹ï@?uˆ|l’"ÝH©©•­¥¢8) &5汉œ4)Ô(“µCbkÂÛë[ƒÙhè¸#‰£ü‰F`¨4q©H¥ðî#’/WìC˜=ƒãŒü–©rQjªíÅ®—Jì^¨hôc:AjÞ9{G»š! Bµó(d©zRš©ÑȆ3x}3çO•ª`ð馦ï5„Û@ìmò^/"H"-N¥°Úµ.ACö(6<]=­¡åë×)±gŽÖÌŒ£9‘r8^ÝÔz‘–ïã]”JfÑåëÚYî$ƒA7Å\›¢žéÙ˜oýk3Å’(¡§¤,\Q¬¾TEhî£-V$› „*P,;U3§ÑúáwM›•x[G€2©fÇÓ8ÂÞ>­Ž™?–Ž2ÒF#!ÿà¼;¯)ˆâø´á†H¥í -œÀ'Œ1%PŒ*Tè¡ ñº‡Õh² •²›gX²*<µndTCãä)¼Ï!ÒÂôŒEê8:c¢e„ŠeÑP5––l|ÈŒGÑ(Ë*þ8›Ø)!(RéQñl‹Ò A«€g†‘·kéj%)ÃÌa£'Ñœ!zÒƒ™,ëx Q,&RÓ¯Åð úÕÎHAƒ¤ €ÁkàRPmí¾}͈dVMœïî!¢(Ö׺êD2'š¡û3sQloñlG$±ý¼×Ä9pŽVQh<~…Ó«’e˜(¸n7œj kÁ’¢Žçvž þKŽy~ûQ»SÅN‰¦OÜwB„ŠZw>¤D2ô™g.½¬¸Â~ØO^”ÂmþÿLDÔe“Dšõž"{Á\”,cÒ@ˆs²ŠüÞÀÊo›ZMO£‚pŠó–‚"ç7øêÝB`‰a²8ºÏ_°kàºäøŠF_µƒçq,ÖÌ$j¿;PÐŒÐbÖ@ÅR"d´/PØÂ $žÉše(«÷5º”“ÃdÑJ ]äÌå`(ÜÏÕºœ&²"ouÇ{Û[íµ×åEÒÝ× AÑ(ã)*:£¾?BÒx¢EšÖ}¥Qlmt¸³uO t}k%/‚‚µÝ»±:ı¸6Q)%›ØÜê+²r>Õkè %?^mIK×Píø¿í-U2¹œ2Lˆ¡ÜvÿÑæ€‹GT 2#œÑhÀ,¨ó[ôxµUz01éòÔA!?°Šâ•Xq¥›6³‰*CEÎ̯õjÃåѸP$­´øxJ¸,RŠOyá|mj¸pœ§äýÜ—Û­++8—výš¹qªñļŽß°ÛúQ‘O© DŽw`䕌ñm¶§¿jÏRqáHµÇÅ2£ÃI)ˆ×z£3L c6ìjýª†ÓÉq'(*d·L5fËø5¾ôÑããi>À©ö¦çÆbò&õW۸܈î”м]íy’ ýb}óN›¦ÆÝž cRÞ1AŸ¦ìŽÁG ¼ÎÛH ­Ä ØÑáý|o;Á µVfT?9×€5Ú_Þaw„šZÜâ˜aƳåÝeŸ(¶Ø•M¾f YÕC}ïlmop ‹XíÁØ$Ý]cÕþzdzŸ6ÉU JÌÞá¯÷K.²ÌŒ$‰ÓFž¾Šä*ïlª 3Ë´£Ê¾®.¨Wà.WS±·M5¦Sçg#À…ç<Ýe·®Î¹4ß;n¢åÊ4™`s?óMó‡GØ…]aˆ­-î÷¹ãsM·““nï¾iü ßù³ĸ8Õmĭ- Ùî‹Ì3,Ž'Vmªûo™{7ë_~È7sšeaãosþ}mËÒ"EòPJJˆCNeÊa…/ÖU¿Sç7^=Ô`Ç†èæ¤„ÆÔŸ •Gép¾«æÒG[îÏe=6ï®fž8~ÙQ©`ŒŽVŒµPˆF}åpUå§ÄÓÓôW'P%‡­wm¶ïnV4íj·ËU^¤·`RJ5½‘oÇŽÔ„B ÅФxe®¡½)Q³ ùz}C)Oÿu)E÷Xÿ´«cH‚| ‚r‚¦¼#OŸÝý¼ñÓ£ž<ƒRÖy½8L*n‘3®¢%ÔŒ0æûrwõhV.ìªp»Ô2ªÝóñÿ%…]œ@»­Ž'Öµ®,Q$¤C(èÛyÙ?ŠB“ÝWÐÔU8^kt"‚PíÜÍA¯¡s'/"íNéYN¨s¾åpKÏV:„Æ`Ð¥å$žb¢3(I-L6R25*É ‡¯l Jo +µ7Ö:ž¯q d3ç㥪‹3ÐiœFÐhîªùÐý"PŒÀ¨¾®?£QRyflס–ç;dÉa²Üh6Vqâ Ša(……þË„.ƒšäðHZÍ`‘Åħo½¿Iåýd«CD»kn¬v^TwRÅqŒÄB·-$ø`Q§7k24‹!©Q,µÑ]Ó¥Bd‘Åèh'˜%±¯Æ·>ߟ_0,9Yßu‰·¾W‘>Š(ïØÓÌÅé¹µ|N6ð8¤õUl«®B¥ÕRë\Ö`èvúr~RD„bˆX3“¦•9Ccä2¥Œ®õŸxF‘F&Û:«&=ËÈ‹Í.Uc¡Û¯wÝá“‚,ôÚÎÛD„&Hˈ8 “¤ìœ`¬R©‘);—FJ;¬óUý¹0™\)»ûÒÈqUî]e®õ{]+³Ì57œ~H2D ÄÎþ5iŠJ5.Âã¸EOK1jw4;y1„÷ñ^Q ""jÒ®Xç +:o|qüvN¨4}TQ“†ÎFELjœj?¹O_E™áXÎaGA™;˨¥éù‰,_ì`dD¢E×Ù FZ-Z-ËâpJúv^R“Š9:šòè ²I 4äÔûHæíJ QÉ©8U«éñ)¡;ÅÕy9Ã?4Á0Erf#;%–B¹àá¿\‰wÝ1ðô ’BÆÏ Ad±Þå Ýæ-l ÆÇªrUÕÕ¶{Vuì¯ãFòÄ{1„ãEPO»Åj¼†&†%©ŒÒÜê­ðaLÏ휵$ˆx%QÙæ«ó*¢i´ÞêõɰpAˆˆ?,l8ÃIÂhpqÆ0*'^Õ=.÷UÿÐ*¢hr|<³õ°ãÃ61¬e"k©H’ 3˦FSR£5ø5J)|! oç#¥ä‰TÌŒw~µÝÚ\ˈ®@Q‡8¢©ëžR™g6Ëç$ÛWíoõ·É˜WÐÄOÌ3ŽÏ\Ê¡hrœbzç³íÖºjÆo÷•:Ð˦Рt¯•²Q¥@¿9Ú±Z]œ*ï5ðÎå ¬ÜÖæ‘Ó9f¢½ÝKèx#qâ%RѦÖ•¥Ž’1Þ1ÈÈèÙC•ÿÞkÿ×Z.A”Ôût±ÚÌè“×,•òG£Æ KmŸ™ˆôdåÖíŽ~Ãg©‘ÂjOF¢jB8Ö#œŸ/(±¿át{Ú|ùv|ažLEaÝ ‘ãßú^EC-T|ŒR½½vµxx1+U°ª$Õ´Rïòï[ª"iw»·Ü]=ŽÑÝö€Áã<õ› dôµÓÂbKÑË– UŒ¡´ò:œgPœ¢®˜]ê<ÖäúÊTù¸8™Ø.^9žHP`R DE*®'‹”…&D«G!‘zö†a‰¥®*/7ɯ.M1‚0j„1NÅv„G£¢µ7+ñ(-}éX“¢Ò/ÃOÖ‰(† ÉÔÊE*9Bö‡ÉÈŽŸ&Åý#£ÂOæ$N†™ì¬#ј8ÕuZÄÒ9i…†½y42TGdGÿ¬•í©÷»Edt¶~\’Âtr¢$CÎm"Ê|JÍHÒþEIíªõÙƒÈèãØ$Eƒ6H“’Ó£“Ym Øf`o*Ÿ"ÕʧÄtdŒò:ìL«ˆ•Ö¥cošdÊqàsb¥–¡´Š½yzXr™«ÆÉ³aŠ‰ÑŠÑÉóFÜz†ƒEApúE–ƈÎÓ»^—w¥»Ü.¤Ä«†q?*CÃ^~Ê”Þ# ªŸúöãÍ z\.£V÷Òà×à<¥ Tj´Ì¼‘=n³ ŒV]Ýý³\IOÏ¥§Ÿú´E±Ø¢èú1"Byíñ›?ÄG«â»ÞÅÈŒêq³©î>üøm),2J{cçí!L‰ÚôÄžíÁ°¬ ]VçÏ©ñjéß™šm Sþ.LÙõë¨ã+4òGwßò0'E““r†¥Æ°„XͱݿšÔ §Ì¨ó«4‹ÈpÅÅ‘}]ÕE#~xuæôÄ\ãÄSÞc0È.5ôZ-ĸ‘dE±¢¢ý™]¾K&˜fF‘˜ÀoØÝ²ÕM$³h›Õ³®Üõe-~ïlSè^Zg@Ç­ÞÖümýÈL}Œûñ7œ¡}-Ƕ¾ôö×êô¼YÓ'¦FBô¡P("gpØ?‹(ºþ‚V>-šÕàˆ\N&#JŸ›;PæFÒ, ‡ËqQ,­ Æû1eŸ7d>Ô«jÝV†Ê ]Ê3hé8‚”ýœþ¶¢ïЦõûKƒOÞ3%%B À¯ì’%Z}ße²ð0ò§¿U¡+ •·r˜ŒP#b«—GI"N/%bÀ¬±9ÁINvuJèjbïð"R+~Þ£~pš>ìøx’Ä5'V(zÝ\•51b³‹Ç t5‘ãéeÒ{ 5k(=ƒIsô¸¹:'Ä0‹oqp=£'—;Øè„•‘*‚úµžQ”ÉBGzݶlÙ"Õ‚‹%66vèСcÇŽ1bDddd?«Ã®¥”ª|?/JE[çªE¥Ž;ýz?B¯ˆœ¸y‡´5qQ(+íxá{×Ð M†–Ô2?tÔ’„@×ÐîKÿðœÀ÷lEwƒOy§dz­ZNò¶6[ç‹áÛ¿ ç/ Š˜œ£l¯v}tHqï0y¯ûHÉaÑ‘=n݇¢lè«7â–öõ¨^IEi)©ò«¯ìXÌ;yBÄC•]Ik ¢Ã•nŠÂÜÆƒ6Ѭ¼e’1]Ù¹ÓSQr¬ÞZ÷Ùa×E¡Q 1ª ¹øåÛÐp*«ç)†ˆÐPaô©-8y£üƒ­Ïìó I”¡þhsÀÁF“ˆÃÕ´§M ¿"Y¹­usSPÁ`.¯­þÃ8Aä>ú¶iS ¯dpŸŸ7D©~7\¹ogó7A… —¾(MŠ;'¨àê-KKý*æõòr³ê‘iz¥Ëýüzk‘‹Pr”;ÐÊ_93z6í{ysGma ÑÉa“‡¯Î1çúðZ×erÄSôúUâ÷û½^¯Ïçó×ëç}ûöõš¬Ç㩨¨¨­­•þÛÑÑ!—Ë CWXöFb‘ o÷‡wRˆÓÉp4L‡ãŽÐõ:Û…ö'†ŒÃùêVŸƒcÔDçÕʱ¶ÕsØÝ–¢LBýúSJ5žïœøÉuŒ*¤ï(ßâ EåøgÐ`$Xý‹…OÈSÇϺnËg n\0B®„;}ð«p{Ç hDŒzŠŒ{Û˜Hj¼ªÇ“þÿgï=Àä8Î3á·sOÎ3›ó.v±È ‘ `&EŠ¢D1X’%Ë<Ù¼ûÏö9þÇg,Y–e*X¢"£˜H`‘ÓXlÂæ09vºªÙ€]L"Ef¿Ï<‹îžê ݘzë­úêûÒÊ¡ö¤nílXŽk(‘í—ØCgt e;ÓüÝeëØô)IÜXAwFɽ=zÍ<[•mŒÆå“gJo´dÂ9Z$/ ×αþäåÁŸïµüÕòI•ˆ‡3»ÏÆCcZÐá²Lósã;Ë4Emk«w “ˆeiµ/¥v fyŸsQ‘²0ë¦ØmÉå2¹ÙAžÛs,¼èií½úÙAuKœHƒ40¯¢D"‘qYFxk\¨„GèpäïÈÁÈ1ò {– p¹\#„ØFþÂcóà8Ž€ñÓx§³b8nÉlï¾®îßïnœ_¡©®v~m­¿`l¦J’xyrHß)a?¬ÀŠ£aëahF†Œüy‰#ÊÒí‹åºrÑã¼¾ðÕ“‰=mñ'"ž短òysá«'»[ã?9±ûìº:P–ŽÿàÕÁ„Cž’h$&šnAÃIìÈ®2›…D†p|Z#Z–±* _)æø.ÔË„ÉöîÝûâ‹/úÇ0"ÔF ¸‘Ó‹A®üÃßqV«Õív¯X±bݺuóçÏ…BïJ~c`<^Ëí+-·žÊ5%ŽñïfÕ{fÕOJ vø¬·¯ž5vè•fÕyÆ“•­·ŸÈò5 äk&eÃݶ²àŠxý‹¦ί:_ø¬7ú& ã HKç„–Î=«Zo¹D³xÓÊ*‘´jú77aârÁGÍ‚¤¯â½¡#óÓ}ü®M»Îñ¬×.øíG͸„ÙÔYg‡ŸmŠ, ¦¹ Éxæ§[ûû<îÿ¶À¶jšåÿ?}úŒíÖj™p˜®é'NE¶uë+¯²ù%ê·s’M¾q®{ÿ‹Ãôëy´;e¾À%Iï{™áØb‡à—U³½ ¼œšSŽ÷)¡ŸMf;3ìºMsõSmÑÿï™È+…ò²"~Å\ÿúÙÞÖÎØ_=5´í¬}›z+&üÃ5¡e!®íôÐóû:øENÊÅÝÏ3mý¹pBç%¡Êα>Ëm‹½^‰Dötœ)´³̇¦!”SSS3®ÌÆõÙDÝö~Ûø~QWW÷÷ÿ÷Ó¦M«®®~/âïÆÒ¸lù¼ö×wïÜç\>¿Ð)]øòL˜0ñ‘ãcØ5Ï Âú9Þg;Ÿì:ςѡôÓ{‡ŽÊãјòBÛUòEQ"«ÇzÛBï7¶ ÿíSÙÙE²“ÑÛzSmYî–i·Ì¯˜ØßÓóÈ+=G›-6ËíJûª=÷̰ڹó¤JŠ©®pÝVÿÛ×ÃlÅÞ·ÎrójûÚ‡~µsè\©0Ø“:c>»Fd2éŸn:BÖy!¡ #ºxÉP~³mXðÛ á¡4ãà ܼ'Ç{ôÔÞ3±XzúÒA?ÔžÙä‘gbïä§ÛqôL²_'bCXÓ`ûæÁØ÷ßD£ Ím©„Û^Z _안9"¼>h£>êòøöËÿWÖÎÙðçîÊÞ/ —Xô6aÂÄGˆ9[2Õmu³#Aj}AÛ½+CžÓÙ9~žg*r^7”ÊF³ÍѱXVvÈZ~¶Òb•nšçjðŽ®®0,;gºï»åmg’-1µŸ¨ÃJÏuŽy¢À€wZÿôº’9§ãM=ÙÎaM’…MW¹–WÛJ-, =PèØhãœ4Là üºE!Vè·Xü";µÆ­ ²ü¶Š… Ùo„©(µßbeí4HS_î¸)(U–YÜÀ½v&u¢+#ÉÒuóí³ý‚E·z޾£3s°C‘eþUžÕåâI»½#s¤C%þî•îuU²Må¾¼ {r­º0cŠï¡üÖ0Be®/Ë\S_î'N/µÖ%DŽ»j~аGßêÊí1ÜAûÆgÑåjihòß%ÁpRaíŒÂ»&L˜ÇGÂŒh—o¾ê|@ –ãæM÷Ïi¨÷£þÒwR›Ÿí¡ ¶‰—ÈíµNò¹Dj†ñ¸åóå—øŽRç›þÁî±Ý¿~4çš……k/¾eB¾µõžÚ±JΙæ[²™þeùoµkjõdaÆ Kfø–̘tmþ4ïüi“®@¯]ºvB½6z_oòeß¼ØSïd÷íëe ”X^VÎö¯œý5a„ ï¦ÑË Ç'ÏFž</± +ªÎhôÎp™/Ë„ &>d˜ëe :iìøóõÜîÎLDÁ:·¼¨Òâû$Éý8ÈbàR6›<}ˆ<>௪a@A.¿6-sˆcþe ÄÄ4º1ß'ÂʞߤéRà!ŽšB#©"¬Ò”.NîîgHšŒ†A…¦±óððçÓèÂYp\—ºqäÞáR:X~ãVb:©FV#q“Yô_òqq(iΤ H ÷XA$‡°®ãkWb«s*‹¾É…$L´'Ù’–¦óixRg#>ŠS †Tèy ;?ÚÞÁ“s#Ï'`z²3a²àe †eAÛuAÛ»'5ñUðD;vêq§ñj3=¨Q‘´â/}ø?-(àÁB\d¨õþÐ2Œï Pva ¨XZˆ›]ôç×ÅÃ}°ðÔIlÅ…”<``0'{°-‡{Ëqµ•^Já§=ÈÑ4PÜWŒ2aPšÌáçç0L÷ÎÒ¿÷–¢Q¦÷fU¼Ú'‡±¤Ÿó^š„šðh˜ÒáÚr>¹5žÃó½x.ŽÊp“ŠŠg;°U£+’Æ–¦y0E¤ù',ø»8 ¼ž/h^îõåIQÇ®><¥¯ªPDÜ_ŒÒÉ5W4ìÂoûáðâ¡J·¤ çÚðoQðÌ(³füu-Ö¹0îà~0ŽowSº•ôfQîÅCtü¾;r”M &Üᇨáÿ4ãG˜ß€RàñnôÊøzâQüMž¶âN%³ƒ1´¤Ñ”Ågòò‘päSç°WÇ?Õ@Jã¯NáW,€<¡'|³­[ÓøÛZ¨øÇfüºÿ³‚Žãq´¤°7ê·Ù©Oáw=pñ`{»ñ.L±b¾ˆÃ1œIaO«Æ,¬=2îða¹GIYq,+—ÜLà‘T'´Úû’(ÑFŸs_ßîDi_.A$އNÓšÿù„šÂnMàdGRLXë·K¸©³ó,ÞÃYTY1ѲVÑQêÀæ„€GÎâß;°Ü…Yñä~Ô&Vð½3xSÂ'&où4ñ …É‚&® ¤rØÅ F§ææ:0D:Í4ì"f9ágq(‚N óÜ(Ч_‘1µõ2XÛ/ç£- ›„…N Dñƒv(ÜÀ,/͇aΫ“¼slÎ+B‘¥‚ôæ'£8šA±© uš¹Ï }w˜áB–¡šæX gs9LwæµÚX¦•nZϼ}2je<3ˆ!Ò–vÆpm|¼Tóx!‚k]ðŠ˜ç…x©'³XÛ¨+Aéå­˜gÅÓÜ s¶ãÕN«Ø:ˆB/*EPeÿEg¦¨²SõFn¹4ò­;ÂÿpÀÂb¶Ž~lMઠݓ`ñä˜#<žÃx)ÿ¸Æ]ß3ðXp-?‹<žŸPP$‹ã®uÀÎA¶QV{=Š»ð;ÃðÛÑ £Ø†Í"ÆÑ7vÇa~ dúdˆ.ìˆÒR%@'ª1 UÂ߃7UüMI>·w|&&>90YÐÄeýYœÈàà0cèKC$´Ç`µOœÅ¶8ý‹²Ø`‡¡Ñµ¥Ö~ÙI8Iƒ^éʉ"qðp€Îïe?€Ck"’ìˆNTç—y«ƒhêÆÿ=‹%jÙ1ÛŠî,嘀‚^ ¹ ÝM˜õ­A¼Äb•›.Mõ§():Z8-£&ßWö¤ÑO85CgY‘AZ£W¢ µ¬IéhO!* -CÍR:2¨·cS¿Âw LµP–9NÚÝ#Ï‚/õÒí•Ó+Ÿ.£û 6ñtO±ˆ¤ÀØq½—*<•0G M *žŽÄ膊j ®áÃx².°… ÿùᚬc kBhî¥{‚šYlPÃT]G[ gbTJvÄq@¢[ˆš=¥Ð‰u>Æï #àÂr;nj‰Óá©8‘A†uÔbv<®$]˜l£Y¦V¯„òIA)´ÄèP ƒžŒ*X¯ôCH£ŽES5~º]„¼;’òŸNazþ¢™,]ÉëQ0œ¢ÔXk§+ˆ„w ƒ³b¶õüºc2‹µ éÄ׫Èà•Ýéïf¨á¨,ÓU^Rç}a´j˜*¡?¿Õ䦨4é…E‚]ÇëCyiìJanà£úOlâ2†É‚&.SŒÄþ‰t(K¸ß›ÒîK9’¼UdÉ\¢ú]:¾T’,”00Àêúb¿ÿï$î@ÉdƉJT›{Qe$“þ>CŪâ!Ññ3ÎÈ®¡¬êpên éã9Í’ü‘ñrÁ¨ MÛÛ—‹cmaÂsB.uwg-QÍ)YqS1ЇúâTå„î‘àçÀÄ·˹ò»Ùb tUʪ°R÷¹¤¯wá~ŽÕaûòþhVÀ!ãL+¾ìBC~s~N§»EÆBtGÁ°J‰pv’œ_é†ôÙ¶ Eɭʃ/òØOø øT)ØéE¢Ÿâ |ª€N,äPt‘b9Ú¢¢0]½›êÅ|7*äTªYu÷Ò'<¤ Ô€=Oê ¢RBEjD˯hæW"ã*b n)€ÄSV¨#(b±›u1¨÷ba~7 %Ü]†µ % аÒ…£%&5ùIËî´Ra=«€ë‹éÛ!§ya¨õi‚A¹ ›]ÔÆÐéŒôÉ :Òt‘õÓå˜ç »en(A,¿ß¦5_OTv—˜ýŸ “M|ìÐ4-“G:ùKJ¥âñxiiiCCƒ$IL*xâ—79‚ÆF<ðÝMöðqæ [X8oùrÌ™ƒæf¼ü2Z[…ÚÚy<0Ïeà?~„'0cn¿ý†B7^xû÷“žµböllÚ„h´ìåßÌ>|S§â _ RóÛÕœ>Š ¬YƒÂ:„×_oèêj¨­Å½÷Ò]ëOþdúñã˜>ŸùLyÀ¶ü‰'@ê#I% `õjÂÁxüq>¼ª¾~Õ—¾DÉçû?@CV­õ„ù~ú¹"E~,š|Ñ*bé¥O÷ÐÏ;€eQ碟‰à9êgæ;¾R_›ˆÕ“ãq&[àÅ‚K•R颟 @ šî¦Ÿ pUW]” iãÍE£ÇULŒa< ŽÙ^Xµc±e+n¸xß;‡Å,ž|Ía¥.xL˜¸$L4ññ€Ü /¼ÐÜÜFcyL<Èf³‹/~衇¨ŒÅ(‡Íœ‰»ïÆž=øÞ÷茡ßÿþßI.xî9lÙB-(6l@0ˆ—^¿þ+í§ }þó”þsºæævS2#ýÓOãß ì…%KŸaçN|÷»”䊋ñ7ƒÁAšÃ3ÏÀnÇõ×Ãå³Ïâ[ߢ½/á¿{î¡Ì÷ã#—ƒÇÂv¤&$qS[¶Œf¸mþíߨ©¬ÄÒ¥÷c6aÂÄ»ÀdAˆ|ã7~øÃ*ŠrÁW„ù6oÞüàƒ.\¸#²Ïé¤XŸkuõÕäs¦åÃSy½¸õVôõÁçCa>lßm·áèÑó æÍ£t•NSN²å²ÞuΞ¥¤USCO×®½q$=!K„ ¡’!¶‰.ZD3$u&QÊO_Þy'ÚÛé]DGl܈²²óéM˜0qyÃdAœNçòåË}ôÑþþþ‰×Ç7Þøñ,;¶"T?!þäìBH10ÙÈá‚ÕÕ“N‰j¼ Á§ôó ¦L™têp¼K†&L˜¸Œa² ‰ Ã444Ô××OdAY–?ýéOX[[kF«7aÂÄG“M|¤0 #“Étuu555={¶¼¼Ün·'Ô[s(ºï¾û¾ð…/”””|¸%ªF.«kuâIþ°<#H,÷>9ÖH)‡“}ŠVçh¨%Å`m¬ðLMÊÕ 5k¨šaä=š1ʬCÉê*ÃZ¬­/gRœžUÁI¬À›# &Æ`² ‰étúôéÓ»ví:wî\EEÅæÍ›7mÚÔÒÒ²sçNŸÏGøï+_ùŠ×ëýK$ Ø•:½+v¦KM*”– ðZoñMórïƒ =µsèÍfÃS²á\÷ö؉=Y×ÝKKñínQ´á=Ñ£GÓq]É—kœ{©{¾Wojh¯ÃuÍgÝ%&s ì3ÄÒEÎ)5’ëÜ„‰OL4ñG‡ªªápøÈ‘#‡R¥¶¶öšk®)((èµk×’o¿úÕ¯ÞqÇN§óݳ{Ï Øß÷óþ}NûÜ¥¼d¹ÞÔÑ-ÉHB× ¾÷œ²jød¢Ý⛽Êd ŸÑú à”Ù }žè†¦Y6£ô>ÑóÜaÕ±!pÕ Ù#2H*ýÛ#‡ûUc¦Õ/ rN{›@ï¯TnyGJ|N3Ô³ñcû“‰Ïúëk%§©M˜‡É‚&þˆ ü×ÓÓÓÔÔtìØ1‹Å2+Ç3¾æ'Ëòm·Ý¶hÑ¢«¯¾ZßVW‘Þ>¡FÕ¬ˆœ'ÈiÝÙ°ÊðÞ่‰jªSô{Y ç†cºF’9ŸÃ[†vœBùÿôΩG<¥XŽ*wç Ì0´¨Sæb¬¼+À˪6Ü•ËÈ¢‡Óâ1M±ñžBÁ äº2ݽJÊ›趸*EÙ/W-”¬n–ò¨n(C¹p‚êLu0Ý‘âÓíå=ñ/%« 7.·²ùÖÚ¹²å6jØxFáÀF¶?ק誕w 6’†ÈÍ®\T5`0œOôÙ¡öæ†2ÜSÓ*Ää³S¶3T=Ý—‹)04=Û™rÉe¬zXŽh*ÃðävÇ…¼¨EÕT6'›hÂÄD˜,hâÃÑ(©TªµµuïÞ½½½½Á`pãÆÕÕÕv»¬ŸAÔ××O™2…{efèÑÌ™Gq‚W4Rq¦lƒÓºµï÷/çÜŸ-¼n™Un‰ï{!ž\\TÏ>éôKnMK:äéK-©c©p¹wE1/Nˆ ë˜bµs #×{ë¹X¯[ôqz¢[ÑxUí?îÝ’²ÌžËëgS­LÙ‹×Wsé~%ܯÄ3Ù®cI«Ûö m}#ãy¨|C•€£áí/$¢%²/m{5‘Ü\xËULæõD‡Êù¦Y}Üå0 ”Jü„ÿÔˆ­/ÓºCoiMµ÷…÷—\3M»SÇŸ‰ôyñ\úäÙwy‚û‡žùmʸÊY‡lÇátl®ïš»|5¼~upÛaE,¥¶øáczÁWKëy#w&öÖ3±èÍ(ý®òSÁ…ü¤_w>΄É&L\“M|˜ üF9ràÀX,V[[{ûí·—–– ‚ðv·°Í+^˜'”ÎÌÙýåþàU\¦9£ÊŽE®º7ú:YV”9‹…ªíSgKò¶è©Sª÷æÂe#rNrZÏ€ÆÎç­Â„ΟðîHxö”Ò÷ÂÀ„uñ=y’Ö÷h×ož» VöœP¼ÆW“Ny¿Óµ»%­µ‡¦Ûk+…ý¥®¹×ºÊDäú%çk©ᕌ2üæ0Éäêkýs³içþø›ŠžÓuô«I™õx¹I¿/B„Qr”‹8X¼ÊW£ÚŽþÛ¹íg²‘z±À%­òÕÖÈò›}Oþ"Ö’ôÖÖZ\z:]ë˜;Ï3ÍÑýØádkÄ[ͦN½9ÙP𹛜¾ÃlüÔ`”¨Ã´:øòСŒeñ­Á9ñWÿ¡·©ÖÙ°Éî™Ðn=¡e$!x)ßÚ&L|¢a² ‰ÔSUšššNž<Éó|ccãôéÓý~?9þÀ{87ï¶jGŸè}¡ÃQ;ÓV)0b™µ¡–?¶3qnžTÐ’I–º dV n5Òü«>fž£vºµXË2ù|—&˜Råˆþ+´± ˸êlÞçÂQ­Œ”Ä;]¼Õ&½Œ’5Þ)¡nhi-gå ÏÊ"4ÕÐFBYÔ¹ôÛÞ+qg5Ä—ÑÒ#EÆP¦¥#­œN‡3ºKÍß˱‚‹·9¹˜—+Y(r¹\kkë¾}ûZZZ¼^ïÒ¥Kg̘aµZ?¬ DBÛfÝ[Èm‹~©ÿùÝÖ÷^])Í´<;~RÎô3Þy’eøég;bÇ~×}ôwåMO1¯wæ¢iƒ1…‘WEœU‡’%ÔÅŒ„š§Û't]×&qmÁ;Çã•y{¥5ðZºõHÊ£d:EKi% ²L©èÜšˆ÷ªJÍ„iIÝÈ¥uH—Ê'§<Ó¿µ‹)^á*öñ!wÑ3@~·X¯\P.[%ÎÖ2ÞÃéAŸµ<$XSrB¼¯T ²R ÆŽ"ù¼$OÆÁÉ2c$´¤öî‘çM˜ødÁdAŒ0YÐÄ{ÑdŠ¢twwïܹ³½½Ýétò«¯¯÷x<ïjáòÁÀ‡¤€3Ùzp Ÿ,k}˧‰Æ0DÕ¶@.±U¢…n È1eŒ}@ékŠ ðlÑfw]™¦²lé§ 7LvœËœí‹æˆ4dë Wc /sº¶Úuµ'ÓÞ:¥ƒ›êZ6G eÒeÖ©~Ž( %£VKÕ^ªaͨ°ÕÙpWr@5–̱ëÉäPÖê ¾B5CwàçÒ½™3b®Š›gŠŽåå‘3ÍÙƒÚò»õ]LQoOhá2K¾ˆLDOç*­Dœ:Úì_º;|®7!—Ùê×ñ±´2(h¾E¬*(ÃýŒ!JÅuœ”J  ®BKIÖ »ÞLìh¤ùd&ûÕâekƒëíÃÇÚ­‡²Ž2Gu‘|áC4§AM˜¸$L4ñî™ülii9pàaAŸÏ·aƆ†Y¾¨¯ý#€a8¿Pyý¶½Ø·Æˆ¸s%QHxúYfÿÞ‘\´vª»Aw7óâ‹UgÎTÍ™ƒ[n¡·ýò—4àÑüùŽoœï™?×.ì¤ñi@¥%Õ4ÖÒOÈ{÷.™;Ÿþ4MÿøãØÿ TUݱ~= Kh܉-ëÏà 7L)çN{®ûž¦¸õÖ¢`9^o=eû¦­\~ÏzðÌÓ¿íÜûër{ôO¯¬ÜcÏVíß_5u*2 ÐO[·bè5Ì{ÇÆiM~ýkÒ¢Z’á§êÀ²¡Ýá†}'hÀŠ•+ѸCCxeÿ²£G1'O}ªX.Í·èùÎ)llÅ⻪®«=v ol=Ô½cë =~ûÒZýÚ¶±G´æ¢§ÈÚ8I@4cjA&&ÃdAï]×c±ØþýûGܾÔÔÔÜvÛm%%%’tI#?2üô§„Ÿðµ¯týßø†"2-%Eâõ³ÚÎ -ÛõûÖ~êhÚ4w©¹ßù=1ƒ¦?r„žºÝô³z5],;u ?ø¢ÑÑÇã›ß„(Ò@K÷Þ B{O?“–ÆFš!IÿÝïÒØL³ffø­oÑ`O€±ñKª-¸ïµòÖGôDBm¬8øùe¶Sÿãg4pC= @xæ ~õ+X,´¸.­á÷¾G##ΞM3$<÷oÐÀÅŸùfz0ÙŽH&iDCˆ'OâÛߦ¬9}ºñß¾bÛ÷äÌ_mñõiv{zAMW2Q×ɇ~ô0½qæÌñGDKã`ø [õ¬ô‘‰V³2ÄI¬¹wÞ„‰0+‡ÄbIò½€ge/ûÑU†aX™³É—Û;1aâŠÅåÈ‚º£Ãøa†5„shWPoƒÌ Ì‰Zò•‚¯Õ¢òíÆ>ô‰=’sKQ… ~vGrPtœL¡Ð‡€;Ë`‰cWŸ)EÁû¯ÿhÍ™ÙØodç‘};vìhnn¶X,õõõ›7o&*ðÜžý…qíÈÈF¿ 7Ò¬˜X½2&LüÀåÈ‚ä—]ïÁƒ6¨:^îÆÂ¸· µDÇ»ñrqíyçnNަ×u +HêcÙK‘œœŽž4N¦Qå¤fˆ2S PwVð ]þp–>oþ©Þ¦? A„ç3÷äK$Y%rH3à ÄuZ–_„’Æ/:ÑkÁŸ‡pg9²ºøZ –q‹ä¦ii.‘ ª†¤AK÷q´ˆŒ+ÞÍeŠÔM)SÎ×Ü~2†Sí(!âÃëýÚÚÚFÞ²,»pá |èÛÞ¯†Ò“iß9~4/µMÛì›ác¸2x0vto*\iŸ¶ÈQY |ˆÏþ«dº#Ýöfø„Ë>{«Òò‰|S&L|ˆ¸,YMD½CG³LI¢ÎŽº/gijøU'Ytd1͇¯•ÂÃ`g?~ÖÜá±á«e(ãÏ!¹˜Sq(Š­Ã8šA wÚOãû8BxŽ¡Ô5…¸ÎŽGZqJÆ?–ÃÍ ?ޝ·b!Ñm¤ÄꃈTɼ!OžÅk *mˆµšÁ² ¼1„a+Þ°àö¥:»+‹+¥.ªý ¼4Œç³(·àÙ0PcA_ý Ø¡©hM!+à¡*ªø·´©´É*ƒ[J°Ñ‰œ‚ýø]¦8°Ê‡YD³T ½ùæ›ä`ãÆUUU„ÿ>à»»¢Á0BH*,d<‘n;”êc…à§Ü…Á[eqíH Ë /|ìœÃ0²›·…SÍ]|Å*WåÇ]&®x\Ž,øÎèQà°âsEØ×ƒï aM ~Ö·÷Máÿvâ'ƒx([Þó"á¿W°#†Ž ­¸£Kp0x¦¯§p_f‰x¥¿êA]5Êe<F{!\"GpTÅ¿ì‚ßÏÉgþ³A|Õ‡tÇl,Å ?‹g#XXŒVdý¸ÖçÛt–iç²TáÅ3èàq_‚9|ù¶3ø‡J°I|™kÒ0(ø³ ”ŸÃO{Ð`Ãt*ìØÆkÃøJlXêÃ*+þƒáñxnºé&Y–‰¼’õŸ‘Tãq]s nJTMg Ö/8xÆP´tTËé0XÖâåɰÁÈj©˜–3ÀÈœÍÁ² 5‘2 'gÉèiµÛ9’ÌUïZ ¤¾8øæ4ˆ9’ÃÅÛFróVŽa²Z2Fã #’+¬žÖ2 k‘ %e¨,#99>­e²†Æ3V7/å#+¹|¡:u°9¹IjÒ0Ô˜š aÐoY!­Åcºjá,š–ɱrvG×Á CKjɤ®)ù¬È…ŒO*©‰‹³ ù†çÞÃÛÄ+øUš0ñQãÊcÁR+6¨RäÝøn*ÚRØŸÆ¢T>ê:ÞãO°±ÔspGßnƒÕ…¯V`¾Š3êå#‡íQdp;ÒˆhOàXÜ(ž$jXì £ÒVÁÁ ÖNÈ|{÷yh&µ¬p!Ìrâ‘–fÎñpñÔý]w'I7ÛFß@¹?»¤ 8î$NG ÙЙ@·…Á©8Ú³¨à’±®Ký80„ïä…l¹ `ñŽŸÍfûÃï¿,`äÔè¡á­¿‹ ÞPtël>µu`Û›iî¾ÒM5\æ¥íí:'"Gè¶Ð|·ÖõøÀ¾(fÎæÿFo}lÛ/ã±éöj=7´Ï¿ÑSNÕ–PºÞfíêzññ¡âŠ‚Eã[# =µ/¼ó`¹.%·Ø·ª‘ü]ÿógPUno èÑ£é~·uÁ æùXã\bÿS‘VŽµÉŒz6 è©Ñ= ¾%ÚWÜçŸá6ŸØ•¦ÞèV&H& š0ñqå± ……=o±?bfI¸† Œ:†²8¥Ñ+µ.,²Ñd#(°âZ?ÞJቴ»±Ä‰R‰Þ×Óq6 šÁ-AL“’0ÝŠ=,fФàZ7,Yºò7žy¡={^~1”eG—Ÿ,}¿ ­ÈÇýÏÒ^™ÉWƒ\Ñ D4ºãëL2‘Çm” ´Î ])ìŠà­8 ë=(2mÁˆ¼³\ræÔ–4§¬‘[â= Œ¡lûÖDxEÁõë-ÌÑdÔe¸éPN¸¿dmP=ñ®­oZ«¯–]±pÃçI^Ž<Á”Ù§ßàmýqx÷ –ÂõÖq“*Ö#W¬µ–rѶ?Þ”è]( qF“f›ã^°D ýøÜK-báþeÓÓG¾Ó{üP*\/úŽFöïNk÷–¬ªDçwÏ=÷|¬úoÅøn>Ž•ëì3§Û‹’é}_?w쌫qŽ5ÀÄòõOáÐ+FŸó5ZÙð};û¥9_ -rêÃZòø0kkt–ˆlÑu`ó)—à«¶W•ðóÊ¥ W®<<±ßºƒG¡€õ…Øä QÝz2Єüth~‰Ñ.áOªqm {#Ø=ˆ×ú1ʼnۂ(ñ¥Jº¬˜Sq–ˆ- Ë\Ø9ˆ§4J3KH‡Q$`C!®ÉgÞ!P͇±h7à}°áE7繇 ‚ –Óx@Yí*eÁsqüº'²p˸*„ENЦ™Ì• f\Z‘ÆL£Ž‘m`çð‹{j£¥ÈÁDO¤{ÓzAOºeX‹d¡td“º”c¯öÖ8Q5F·½ |á<{ɾ𩓹Ùõz_’±/œ¦S&Þ®d̃üä«XbÁs½Èø?‡í,,Baˆ±4"‡J;ÊlØÄ¦6°Üƒ=ýøEæXÐA‹/Z¨ýg Î<’Äæ2TðPXdÁ³}àèùÌ¡È~©šäí9ŰDžXÿоÈÊã/~ÅoûQÅãP)_”Ñ“'ⳘcÕ‘fw÷ÎðÊUŸ-z)râ±ÞÓÛ­So÷U&õlÎÐj*ÛÏŠ€%$¨í#‰fd½N¿]Ê7ú>÷Òï‡sVÆGF8ñlëcý{T¡°Zär†z‘ÔטpŒ¼ÝiJÏf =©¦ F\à^åó†[ÊÉèÞ§£5öRQÏ]ðÛ ¡ê©$x+'M|é #ÔØë<Ã[Å[vH+‚œéÆ„‰÷‡Ë›d\íÆø*‡Ï†ÅÜhÄT‘ÇÕ„8ØeÜ_‰Çú°¥Ÿzö¨sc¹ã"‘ÄÐyK§„A,"®qÛãx,FmaÖxQ’^}àRt”+[ð@%ïÇËýÔËÉ|™“òk±9qt6Õ*a¹“n¢X@ç&Ð`§,HTÝ2/ &<ã ©¿@'T«ÝHËyáÂ`†®¼,©ü"/5Ý€Ö‹·¢8lÀ'Ñú8x¼˜å‡¹àó6 ûe´É$bj}Á3Ûã{î?Ðd/r²\â™çõƒcœ‹½C–l©}Æu¾®ÿèÛ¥ ³4hÙÓoesŸ ,X,fzb‡;ÞKµ¢Î,λÈ=·ˆ¿ðç¦ë‰CÉãmÌŒûŽ$hoçöê ÒïÇÚ5ÂÉ“xìQTT0ƒƒ’ÓŽ)µ$+îÉ'¸¾>êô’¤?r?ü!î¿—3³ËŒS®]ãêÙ“nÞ®Gµ¤ôeÃE’”Êy¦¿…Wl_°ØVà±,Ö÷Ž7=–”‹äŠö°¢pÖhú\¯$p\wºíD6žÐ»¤|óí¥.–•…’M•¶dÂÊp…–úuŽ“‰£ªV´–kzto¼Meý)®/®³‚W4]™.Mv 6V‹ ªêT÷‚›uæ@âÐã)9$—.qMw‰£ÿUÖ>ß5§S=²#z´\´6Ú<ñLÛQ.éâ}Šnɰ]ša½15¦XÊn+¸ú¹ð‰­Ã;¼¼C’Š4=Ù•‹¹%g¥µz¶µ½ÐQêü¸½š0q%ãrgÁ? †kh€×KUÚ«¯Rλê*êøŸàÄ =®ª ëÖQ_ÆEExî9Äb4¬Aq1õô¿k¥:’˜\! Lèð¥—`³ÑSVWãå—)í-_NC:„7ߤŭYC‡Bxþy(`útR`æLlßN©Žä¼hMßÑAs ¼»z5u¾\QÝ»éöŽ ˜úmÉ«;»˜Ååìâ•:tæÕ×…ýMw46èk×AØ3­üÓ?%boÃò¥kjk Ee_y•?úª>wæìÕwÓ.{ö²»v•†n[³Z÷z¹!þ™_cp¼â’ysn³«[ÙýM7NŸª®]Ëpwâ˜úÆv^ìÕW.×+«øœÆ¼ÜT}ló«¹Õ%0Šê)Ì[¯ Õ캵këVt3¶laâÝÂ2™ùoFÁ–fK¾¸ã¨ÑwŠ[² îÔÁr[¶,k`f´Šë¦Tù×-:Éýæ%žã]+WÜ]u‹¦ä˜—·°G{´y%Üj;"Š{[¯Þr¸¸ÞÅ_³i4*… &Þ3>©,HFÍ„±F@a;ò¡«‘¨:# =Ëwž?%´G#µ.9…Pãç?þT/ S@xŽ|ÆA¨÷®»&eHø’|ÆQV†ûî;JÈïê«ß{ã>qPU¢ì9"â¯^Kc=ñ :ʹv³°w/¾ÿ#t)¦1’†;ÛÎuõÒ¨~äµ^wÛÖÆÿç/é±Ó‰e˘ӧÅgžGe% 4(X¸Jü§Ÿ•àÚëøƒùüS¦‰Ï×5С̱ftöààA2^á6ßD‡D¿ü¹—'oyÝz>LNÙúz©¿N/ª§€duü8¦N©9óü‹ 𱂳~ÿ­ À“|ššðýïóS§òdôSUMÛxê´p®K #*20Ú|×~6ûŸ?Öm®hK­ÜP.Î<ú( ÞDa¥¥÷+1aâŠÁ•Ä‚ºŽ¨ »€Á•A]cG5x„Ë.‘µä±Ý¿ø¾T¡±ÔçêH„’Ê‚t¹‘èþÓ§ééˆÐß·Î ŒÄ $dùâ‹4^à²etðë¯S¡OòúgÎк# åÖ×Ó¯HA#dÄCÊ% JøŒœž¦õôm½È¥uVC*_1ž›¤ɽé|£¯¶…¢âÙsØ¢âke¨ó™çëOʵsù0Šm—ÈÑl•|žä:K7Q9¤uª†–¤¼gIE£±/ô¼;{¾h’a<ÿXH­x’[>’¨7ƒ®O´÷ì+Œ] Õ¼{2&L¼.S$}w_?ë¦! H—]ãÄçŠ'‘M,M=hïOQ_ ÅVÜ]B£ú}¯îá#dÙ™E™ S8O 3‡!|1„|†Í,êœø|1J8lëÂkH8•ÀF£ Mgðû^ìcP,àžâÑM„­Q¼£~gÎFð‹t«tÓEµ÷•PW/ÌXµ“üøö%i%§ŒOãjÇ•hâánœÉQ¢*´á®pq<Ùƒm:‚<î á­>Z ·$`mŸòR©÷ï­ˆ  Ú.AÂå˜-àÉ.<¦nK§Iç)0ÞÃÎØ$ÜT„M. Dñ½Nd8ú 6CLáýÈ0¨³àTWº Q&L˜ø€¸,YРúÛpœÅ=¥°¨”ð¾Éà¾1á’Uñóv¼¡âs%ðx¼ ÿÚ¿ô¡9.ˆ¯¡/ŒÿÕL >[Šæü s­x¥"î)ŸÃOÈ-þWR<7Œû*ñgìê¯0¥ S-Ð-¸.€à„ )G4êï£]4|ÒWJ¨aÄ–Îd¨;ì1šÃ‰å°¾Õ,~׉ßôa–¾—§^éÃa_*‡Sö0šÓXoÇ4%¹‚DâÍ8ݹXÆ«=x¤ (bÐCŸ _+Oÿ´à·CÔ‹ÛÏ{±¤kíØ?@C2]œa_Ë 0SÀÓçð«nÌÍGt:ïÃgC(5ðÏ]ÔDã>/zãxµÕõ«½¬‘Qz·EÏ­SgZÜï}vZדg’-‡“Qات¦ÛŠ]ù †‘íNŸ;oµ[ëÙ‹':}1 =£ÅN'Ïse‹m>ÉÜênÂÄLJˑ — dðz3 áá¨+*"¶^£l?`$‹-Q„üT?±]Qûy]N:q:݃ÙvºªWÀbž Óí°æBsÛ“XæÎà•P*иÃ!ªç*lXëGÍ…ï'eiœ[§ˆRùÒ^:I)q… Ç©N|ÉEƒúާ"S=øB>&"á<¢#wd1lÀ7–@`éÌä€BݹÝe£S”vƒr­C@™…zo žRW©%†~Eù»fz©ãPAÃ+vgÑ”A?‹ëhQbà¥0ͼʉxZ4©F‰ˆ¦u„ò~Ýfø±Æ£ƒ8¥âë!,¶"#QL^¢}Ÿ\$ÕÁ=Ñ#Ulq£ÅýÞ©wNl‹ïy‹iœéœn=Oi‚̰­Ñ=qÝ1—°àØUÃ0†³oF??´Çê½c®Õk²  #.G$Ptþt¿ÍÒS•Ãrë¨kä—Ä‘Äãø­†|Ü5\å¢!IgÈj'<7zÌtýoPÁ‰(5˜§ùóXj],´Ž-+r,¨|çè¶¼š|r€®þ Ô=)Ñ£ÓØQר¤&û‡ñÓj“Aø»-¸õ¼mR“kŠå°½«Yq{1•}£0¨æûyÒ†è×FZ’Cöy¶å [NŠEÌ H$H!$"c±v±9‡É¡§ÓW5›)Š  gÐÓS]]Ý3[ÿ¾U·îÍ{Ëh¥ÁAÞ$½· "ëJ1ÕÀ8‚e5fèY]Qqvd’%œçÏ0T#«äÊPË‘„ah²Ž~M8¾ž@Ò[üdÀie½¹§TNËèjîë#9\2û”´¡‘Cš :IÐAç"_»Ý4Í€=ÈÚh‚i :E²Aħ7VÍ]a)*OžïúÌo”‰‰É4f¨ ²$îÊWúàk^leTH Ž…|D¶Ò‰Ynø“ þTÕ b€˜Õ¼iŒìdsÒ²6žpb=H© Ø”„±L£[¿¿[ ®Äà»-ð’•Áш6²/÷*Âÿ)ÀÏTØ£OÄWÆ/ Û áa'HúÏ6øi/Ì.ýT×áA8«Ã?•C : Až8é4/Nœ7JÅ>8Ô’¤†»`ˆÿWyì¼=“z_𽫴Š×{@Î&šýµ®÷VÀˆÊ-»Â—ÂjZ²TZ¼ÙL3ÒÑd ØZûâ5RPU{ö éR“Yd u:ƒ ‘cÇÓ‘´®“TÞžšd¦qÇÀåZ׺‡œE”7|ül&–Ábf©’ê×X]ÝÉ / M2ÅŤښ饸ªÏûV–2ÓþˆŒ˜|u÷ð¥^œŒWkJçM}%&À‡|4µŸ4#™˜Ü|f¢ ¢ÛÃcãïøÔñxæïÍ~¼Rkõ˜ØYX'ÁÞ0!DÂÁÐEØv£<ãøœwâÐTÑ éX0‚.xÂs£¢9ÉìNáYÀÖÑ\†ãÄ2°³ VK£–ML%sÓ›KI8Ÿ€æ ”ŠØ CæÃÝÐLༀ\.­ ÚIæN×—† 9‡$fÃY8›‚# È9Ö^‰zœCðÊÄE8>—³°r:ôËБ†“)èU¡9 މà $àÙJ¤ÓŒ :£pN†òžqb™SÑ÷Ïf_ñ¯ ÕþAƒIg»^ê;šl÷”4…ßÚ5|2Èß“Š8”Rž ¬sá6…‹e;Þˆ´»ÖoÙË©(GZ|¡¾7¨¢§½9þþKá¶{ü›–ðü…ðþö:Ùëy¿HÄ.éÜVGmwåé¾ÓÇÅòB{þäÆdÕWzy™(}Ê¿Úñ—3-Cæx§‰É f&ª ê¯{¬ü¨ ~ÖŽ‡õò,°Ñ¶4–Àþ“OC¶^èD89xÔv€Æ¥°Ä2–~„ 88øbøq7ü(— (Âb®Jä ”¾M’0Ë‚3Åœ°·v BͲ,ÈúÁÅA;‡°ö +qÁ6×Ä*~Ô°üðŸýð÷mP.šÄûàÐÌåp’BdÖXád/üc+™‹‡'óÀEÃl'8»àÕAX〹2üG[ Þ³ûc“7ˆZ5&ÆèbKrvð2¼6#0G€ ^ð1Pï…«=ðO­ø.-ñA¬ŽA¾ 磱FüVìjûã~ø^*xXà™¤n4 UW{3=W3õ–ÀlŠeΜËdë%«f¨,ÅõȃrÚfƒòÀÕTÏkh+*J\V‡¯%[ÚÙY•b‘“æ2 KÖ—º˜hí‡À\‹ÏEÓs¤ÂgÃïœKEêí:ôÖþ°u©ó®ûì¡ûÝõ{¢ÿ½óL8w»oyÞX…†¡Äô´AÚ8œ%`(tÿˆ„о·I†9Aä\ ¦£è±0ÐVÚB„þ™\¾‰‰ÉÇa†ª ä„P¤§.hC’ÀN|*Ð0-¶´(éœTÒ=¶-2ÓWÈ¡‡ר6²½ìèáîi>‚¹d^jtû<Q%ÎIŸ:§–D±]—ù2~]öIåÇ·m“vŽ_5j{ÜۘܰñmǤÛBOº‡·„…-ü\ ¸Jî<0ôÎá3’Ã'’ÖJ©n›={HQtß½y÷/ËöÞÿÚð‰rq[µcU¥mî娩C'ÞIV<*ŽŠAÐ"Éz"›“9UϨ`X¨º O‡$8Ô”&ß@!MLLf3WML~M =y6vÙ` ç 5¼ëtв3yU¬|"v¶–cƒ4­)QÔÅXãá[n Õþý™h,Û}(¡.q”ÕXËócW ì¬ë£ÿZj,’m—3a§`¹šèÐ)ÿl‹4p´"4Í0È\aÍÀy'Ñ¿v=ÕUCw1yµŒz.~®Q]F4¬¡ô‘Ì—£­Í•T We¨ >– ÌÅ&&7 SMn}´¡w-g¢”¡S«ìµµ–B›oé‹Ã;zûŒc–4{¹5ȱ Ñ+M Îг‹msg3Ôë‘3Ïf®0„As5Ëy¶-Ù” äö˲¿Bš¿%›>9Ò%³¬õÜ=ŸÒq%Ü!2JŠ  e¨Wî‹fšz4†ƒÓ‰öå¶ÂýËv ]z®w·—¶DI!© ´ÈÑ9¼}Dê #Óšj9›léT ­_yk83ÇZQÉ;SMLn3^ ¼€OËùúß½„ª›:ÓROÜÆ„°@\\¹ïcpˆX¿QÌ/a ²¼UüƒÃ¹¢V¯âi‹%–˜µ«)¤eô{·N·@ ü¡ý_ìÈ.^LÔÕÑ$/uvgöœÿS»‡ÜºPà|&³qtYtXß¼™H”@Ÿ:Å?ü¥ê*uÅ2+-I忺Ѻm¶_ªÿ}¡Fûå~î­7ÄÕk=³—ý¾enæÊUjÿ!6˜—Ùp—uLs­åó-¥n¾`•s…N Aó”`J ‰ÉÍbæª`RÁk=œè‡‹:<7‡ìãcèÐ?ô'Þóx)Å‹=àqÂFÇCϘ|0»ßquC(®¶Ã@  "‘BRC+„÷à|È ëN‚ƒsW!?ÓþõôHEåÐÒ áwqrÁpXpø…înØ{jkáÒ%¢wØáóAcôÅpRÀp˜ 9.µÃð›8ROGˆ8Ößñ“Lq±åÄ @Ç–—ãÄIýýÃHÑ(øòáZ»õõ·qÒ‰’’‘¦ÁžbšÔ¶‘ÆPcV¯‘3‚©ë.aäJµÜÚDT~$,ELTxÃsÝδµáT‚<€Óû< ヒó­^5)™ÄYúž'Iôæ›ðâ‹8‹òoÿ6NW{íÎÿ‡Š-] ›7ãào¼;v`áüÃ?Ä·òèQœÕÏéÄ‰Ž qFÀ½{a×.,l#)œFÊÏ ßøN‡Ô؈3* ÎÃŒ*QUؽ§ ñ§&&&3Œ™¨‚¨Ç?5/ôA+‹Cq†thÓñZ½}ÝÐM€H@[œ", 9×dIxᇄö8ì€ö,ÔØáA/YØÙÒxý€›‡ >ÈË ð|¯døbÄ’°' aGï\ˆ:R'4ôÃxXhNBŠ„å3p>…#“=„ZötÁ¯dhOcÚ˜wYÇFk é»1†WïIxm§•’¡[Á‰™žà@hãâ„”þÐŒBD+ wyÀ§À®l+Ài"Ч¿ì†³<ìóCp" ¼ˆ_ãl܈_ã”–â×8$ [·â×8HÌÐk$‡=6åì[¶LIaˆÌDô}IÓÒš˜˜Ì$f¢ "E ñX0h+ÔIÐÔ‡ðºaް8µB1Ý g\ðE¤jIø§^¼@p3 ?hƒ¶¸q¶‡Wº!LB)R…ø\¼}xʉ+¯X䫊33¨VXï€óÃð£V(°.þ¨¾P kÝðL;üß|%Ö;q„ϧ9ø¶® ÃÓ üal¶ÂøçN(,ŸH®¤éXÂË%œ®á¥n˜•¹¡$?îÅj½Ý>qôì üS^ê¾Çgÿçvø‚Ó5,Nañr?°N8ÙÏDá/ÓptþIÿ[©,ÎÇT€Mh†—ú`ƒ ʸ…]Ãð'þé×LLLLLnÈ UA—hªhßP"ÁjÈoÃñÀÖ¸ ÁÁ¿ö@g…‡ðˆjD¼¾þTöÃ4¶)æÂ¾ÔÚ ›[XÂ]‡Z <löƒh RŽ…qÎw)nm2py®¦`­Ê xwg¥PrƒœÅHt= À¡ÁÎVh–'’+MÆÊÀr7Üí€4‹Åéªãó†Gàp¼?‚/·Á5–qp. qŽzEƒ'%8ØV+ÜeÇA×” üåιX8ˆö /<ŠT‚“-8dZ‘ˆ•Þ.A ‹xÝâ†W“)]ÑJ$Åáåöj"eè4ÁXh«•üø>R†¦g£šl¥%©FFGp„R¼jíñ) EKô«òäÃDÊæ §¬ Íj©°–1€à)«b®œ×ÓZ"¦«È«öúëÕSZ(“*¿‘›IBÃzi k X˜U—ð«ú ‚ ³mðÍì‹3¬B€5 ¡>8€ Ç$,ÍÃÑG] ä9á`[‰}LÇ™[s)‘Fz)†Õø»0¼J€ÊÂ*»¹Hã“ÃÐãrÛ9Y­–ÊÜä´af‚¦œÕb¹—Ü3Àúk-y"Aˆ\hž5ô_ƒ¡¤rÒêˆ_ãJµT>ùHºjøJª·KîjT¤M¹ç/Y™´o•sÉbŽmý«®wmLè WMŠuŽsúI$³ïDûJϸ‹²aÈ"GeÔ'ƒK‰Ÿuí|3â+ôÖI£¿NdnF÷ ¼¤}ÿš‘/þs÷á=Œÿqg;Ö~ÃÈtgºÛ2½×ävIÐ>þ}51™9Ì\D]yoº²8&ç‡Cà)½Z‡a6²–Tì&Sâ* §Tì&3˳†¡UÅ~›È2‹«ØÅf@Á5Ý4$eØ…V ºeœööCiŒÃÞ(½Ü;V‚ÜGD*¸H‚7{àxî±ÂÕHãtƒU,áéœ;é/l8éÝ6Ø‘À£ ³i8>­$ö²!F/z”YpôÁ»aSTÝhlö¶ÆÐ µ#ÙÔ¤ÈEB~&ÛkÐÁj^êÏ´]•cÁ†„â"J¿œl0Ø# f‡d°”YB ö·Êƒ´Ïµ–y(,XY5Ú˜êÔ ¸RKq!#¤”ž7‡ìO«ÔôR[u>Mt¥Z›²q Ø¡¤„µ“3ŠÓ̤/‘lOCz0cèí©C6˜TëË´7f¢6¦°œI¿3tpZ»KÍ,³U0£â…j³Ò¶©ÖG†F“б´c©½ž¥’ <|Ðc¤û³á,ÄÚ­’ÕG±Èf»é Z+k9ëxkT=v*Úbå—³œÅð.´ˆÿšljuÖÖ²ÌÈCR8•ˆ–xнCò³Øô‘äµÍöBß„Œs^¾à.š¹’¾ÚÿiŸ&&Ÿ-3T jí°ž þ\’X‹ÓíŽàæñ0&†À™¬$¸Eœðö½ð·)œ€žca´{ ÇK÷d€‡b@“À> ;z`«–óð“v²P$à„GLJñJƒ“l T~‰¶¥ao¡±MYéÄã½ÈXj‡"Pá…ÃÞ—-mðLަÕaM¥$!ÈÃHDR(U€e"Îk¿Üw¢3>6§.ÿ¤÷D‰ã.¯6ÄsŠ‘M펶;¹ §vŠ^ÙìY2œzÿß#ᕎzQí:“èàøª…‚4,wœME½}ÙUBkÃo¸¨pe¼#&7¿kÙà]SN¨‘LODw©8Ιr5úþ+Ñ.7¢³‡£ÍÛòî)Ï}§ª–舕œ Þ¯¤Æ‚h}©††.(¢õ‹±KgRÕzæ‚|ù¹s¾À É&™ðÚ¤¨Ü=rŠiÑ· ‚±RÔä1U‚ ­Ìˆ‡•‘Õ1ÂRÌØÑ¯a\–Ü/AÎö\HÅKuŽIf¥¢G›”¬[´áß)AÛY{,Vd#§‚è©/¥ whL-mÍ­ÃáŒØŸŒ¥t(v켄@ Œn&D4¹ ™¡*HQ°Uoßp‚JØ—-$²_kÜmІ¸u3ðÜÿ¨îÜûì¡kîyuÿ§¢Âƈ-?¼·Šâë7e§4÷;o©ô_®^mèOkiòï;Í”•VÎ_ð}ÒÙ¿o÷•ˆlØLñ¬—ùл‘¶Ç’EñÂâú{ÜxlÿB›Ã[¾– ÉGòÔõ{v;ÞW ´©ÂgùÝ|€†KdÃ…<²ŠóÍm7¾U <‹3<ü‰;Ú X¾Q ,;i€”‘‡¯—ÁCYËa±þt¬ò‘a\dãÖ;ñá烾6_ðIŒXh™õ€À‡Õø‘ÁãIªæ«¾•Œ|毺Þ>-/\'x…$Qc_¼Šƒ¢žÞÈ(uλJ©Ä/:_9ŸìK; ©ËïDzï=ºÉêU²Wÿ¹óÕ=ñòo¸ó+8ëE½psžÇz:|!ÃÔ=ê]fdÞÿË®}'R ʬè‰ËèL·¼9˜‘+YíèU3¸]FêLô\“bûÃÀŠbš¼Þý7ƒ'KÅ¢"µç¢,?é«_Â’Ù¬Ÿ¥Ñ).Ðè!êC}{‰±µ¤Ù¦ä5ÞR¾X 1cF(ÚÙœ¸°‰ä7!HšÀëxƒ  FÓÕ nrg0CUΜáJ²I8Þé°}î\¸tÉÙÕŒm¥ ªÊõ*\¸¢H=RP=v;\½Jö÷yãq¯HáØèÓóç IrÛﮨ€h._Æ>0‡ápíAoÛ[­ºî>y¯¡>wN¼pÁåpÀûG]ñA¼ÀùêU)6(u4ãísæ8/^t^kÔ8Á׳/2»ÂÓ;`klÀM=z"ðfÍhË ð¥º(²ŒíËe¸½dòæ_·Ÿ¦ Ì2eN…15:p¤rÔSõ¤àíA8 ÖBÒwÚpè,´ä圙ø…ÜßNJ»÷Z8i°†®êø‘‚´¼@Qnš£Z xÔm$gèŠn(CJ—a+d% •£Ýn†=˜ŠÈÎàxåªiÎ ô©mo ʆ6œ1ÐÈzÃñ ЬµOä-sålÁžä‘÷ïÖžx‹6è ìQ‰0½ºÖØÍÌç}Aâü =/7Š¥ ¤YÁþ!µk‰Ÿ9›Qïv­šÍòãÓieàT*\h›ë%§ÌGÍ„ah#º¦£ tÄä¡\†2 ÝÀ¶¨=·4ôda.¶1¹C˜‘*¨(xÅR¦úzH$pœŽ¿ÿ{˜?¾õ-üéŽðýïÃìÙðä“àñ@CŽýãà ¿÷{Ø@åÑÛ?þc¬š{öÀK/áÅÎH¨jj`hûØ»WøG„+|æ\•ÿú×ñ¢„Ó§qìt–,‡Âyá…ñ 5î~vç]o?#•†àžuxÁuW47Ãð0¸\|YŸèrÏEá½l ÀcN˜îÿ~’ëÜuBÈç —Û«Y€»œ:O;c‰óE&mŒk‘ D0šEÓ!C'…P¼Ü^žIV9u í¢Œþ\i‚¶j¢z;óŽj£ˆè½K¨ýjH<k8{ïýDûW‚>Òõ†Ò?w(•)¶¯¼[Ê›ˆÒgèmÉ«ý„wµÅ5Í=Š&¥<ŠRõ”ŠžŸðZ‹KóÖ±DQسŒtx5­gtHCKi) #r¤9þirG0#Ua°ä09óÉÒ#ÀÃã#<úèh(67i„­¼|â-’=T•G#³›6ÁÚµÜnؾ¿Æ+üÜçð)P75rF$½HbÇË£#R¸SÀc’ßO©›' „BàõŽnæ F­ÏƒµyØ$1;­(R* ÙKzš¤ì~ 9AÅ>ø‚v1>?\èVS³8^Õ"U.¶Ø¹œÉ”Kœ¤“„THÓ­zššT­¢þÊ*IÔ Ö~Yîëš•€pvˆ ,y,ɤHÿf_ñ\áÐ_ôœnWe#§Ð‘F|ж¡§/FOKÙêZì²–RHV$Ñ/ÙP´¾³©þ¸ÂOþ°‘ÉØ–j‹®*Î>WtPû‡5ÝO$›Sñ"¾<Ÿa5-v%ÝÇÒy!Ú]ÍrÊ( ÔH[ƨ¶æ9(*£ô_ÊÄóø‚cÆÄ5¹m™‘*0*H×oCNç¦Iδ·ôuõÁå?R…Øí…øòŸ-y‡ë²Ù´!9"ëj¦?j±I”c‘£úâà¥ÿêQæ ã¬|½ÙxVKwËá8Åte“iêÎÆ}t´_Ieôðª åwÙ:.Dßgÿ°Ü'K$%±îD¢mÏð¹»m%‹³._ü¯ÞìÁm§•‚/’îèQµD¶·YK‚K×bMé~Ù»Ò’o®­æÒðå·†ÏÔ°úÅøÐli^=oJž~'‘ª¶Séh>_\Äð$>Eû›èöªRN$ƯJO¶gúÒÃY5Ý”j ^QïßÑø²ÎU(‡P)¢PZô˜k¶ÐZã ºíAÑ?–žÂÈ(}{úw5P‹ÿgpQ½¾aèêÞá“!2rŰÝå¨ñ“M]y¦ë-·óÁ¯yÊW;gíŽ7ÿ2L‘jw’­¼Ç^!ƵäÙïw_¸'ðèCŽ@<;ЕnTÑnoH EBÀCR(Ù&&·3TU öõÁ‘ ÜŸõ(°ÁÃ^¥1˜„†qÐ: OMYèpäšàu©ä?Àdnu#‹®±âÜã„Ö(ìI„ ëÝ£ øuj +8£=új/‡á}ÖûÀ{[ë$Ò ’ mÖÒð l+”Xa¶cå×T{c_«œ1 ¿—ÝÆV<,w†:ÓA8{½@X"ý)%RÉVú9:Ez7éµçSÃ\J` µ—TF“T̨r®úh/­äðÏÍ'T>ìw•[\4h=5¸…ö• aMj©´®Ÿ@d“›Ï UAC‡“Cðƒ0DBI!^c€:÷óÃp…-.NÂ3ýXÞæ- @jä`pP•<ÈÅØì…€¶ºð +1øÇh¨ÁGAGö ÀÚü÷< 5øa3¼ƒb+”ðøØ7ºàõaøãX"@8?é«n lñYâx¶‚¢ð|õB`ìÚèß0•’®ÁŽNàíð  /„GgA{^ë…çeø¦ì*$>J‚ˆŽüÇ ‰ê_z îæ¸Ú… ážeÐß/¿(œ?¿¢²ÏO##éßþ ®^ VT·lÁi•ŽÃé)ÂakEÅê§žÂóÍ;~—žGçbû·×»VÂÏíí°a,Z9—Žvγ9§]íF¯ÿF7 ŠÕÁ){Ðmæ:Æ‹“AKzM.ÁÒÞ»\Þ±âB¹µ¦Ü:¥ ‰+Ú̆) %èõ|=&&3œª‚ë»g[á ¼(Áo¹¦Ë€Àâd‰{rAa@4 Æ_ÍÇ—‡º¯ï·Â9€ÿ¯/ªCÕ¦x¹ ®)ס}ž‹Â¦Bøª$×p- ÿ» þ­ÊŠqµA ^ü÷BÌ)†‚©v’Æí!˜5içDÈ)úâðf§…êNÀV¸×ƒ”ž‰À»1Fba¹ / |¯þ«,I°hÀª'á½Ø8 ý Ž Q8xz5Åá@Tguµ *9¸…ýÒЃ?ÌwÂl^é‚_ ㈩¿ÀמÐð YCCp6…skøXëÔù½ÑQzpðp¯²·äÓ}w7Φ´iø|8Ñ?þ#VµY³àË_†K—àg?YÆ^Äè-ú•ìÜ ó7ØxùrøÂpÖ¤ù|§‚Aøö·ap×ðÚk£É($iÔ9‹¦aΜJéÜ9øñq…N'V;UÅž:<«V’·ýûá{ßÃOmÅŸþ®.\[:«Ú¾§|Bå¿û]<Î___ú>é+E— ë±sºì™˜˜|ªÌ\DT9¡( ¯vãp*µ×µt$à8L. êÄG♑¹áPÆ€á V» a‰FœÊY>_)$ž»>ç÷ؘa¥pÁß„¡#*ÌÀ½>ØÛ/†áÞ) 0r«ôÔ1GBÔÁN´‘€H ~p ʃ°ÑJ ~ÞÓ»þe'Øa!çÂp8V N l$øyÈcá@tÚà+œÚ)ÍC)MýpÅ<KÀß·€.à¼KïõÁ±Îñtô±°Âç#ðv×&’øØ|ÇÄ9…Q¸Ë :á§QXæ?‡zàLþ»^퀨sAÏuBŒ‚?ð+Ê °!ÀÛHi.^Ä#.N €Ã]Ž‘&‰¹‡&dɵ´`qªZ¿>^‰å#`Gb$¨ÞÜ÷”lr…K—b¹B¢…ÉøÄ؆C•£S@.ÑÒHš§‘òÕÕØpŒFqóF¼ˆë·pÂñ+WâýH5‘Uzý”¶‰‰É§ÌŒþ«£høœz¯ÁËP2u(™Úá˜&0ð@ÇN›6[Ÿ‹‹=Ü+ÀdŽÂ1VÐs|›6‡IitÄr©}•°¥á¹^¬£þIÎóÈÚû×É%wNœô”\ã3p^ä·Ì;!i£q¸˜¶!08øVäS°X€/7ÁÑ$<*a#¬Ü³8”SÓB ÏÊ,“ s,ÓÉa¸ Àw«pˆíf;œÌà´«¼à²âŒ³Hø«v@½®XâÄáIGˆgá!¨ðÁ‚`@!ÿ{®Ùp`ti[} hp1§eÈ·¦ "F$p„u§lÊ0 X,Ó L{›—‡_P ²rÊ[dä}p…ùùø5EM/PS&&&7‰­‚ŸòÁßõÃâ”àÚ4Afñ£oQ‡î¤n0š7b¢RIý±¹Ñ§{â(S3B ·9±öŽ¡áþ ¼ßO÷Â'Mœð4ÖË‚±[hg§KOá4K¢&T2Û*‚Æ;=ŠV ‘4O†>šž iÞ€çá‡ýðd1Nvß; ÿ¯ýÆÇ"µC†¯œ9癃¼òц&ëŠbÅÓ¡éYôÐ`!ÙN dº¬g4 9’ù8Ù‘LLLf83]"¿„+ͰWÎeÐÍzó2­&å›@š‹¹.©gã6yà;=ðSqa{¨3?ê‚”ˆÝIº!†µ•Õ\n=Ƽ‡Ex˜±y¼êܰ!ÏÀÀ„É–›züµ]0I æXàt ÞOÃ|Ç|A¶]¹uÔàÊB\û•bH8Ý„uރà ûºa_<«=ÇŠŽïOC›Š×HâX_¸¶ÌØwka`hr;—Ý×!âx¤·»ï(Æ0Œ¸2x<üîÁDtkðþEwjxïî$ýx`]Ãýju3bró³gx¾x™TUÊI¦š˜Ü®Ì\d)œ]h„|^±wº Fƒ"I ³ðl œÔüR ¾^€ Vzbpu_ë‚"àÅ>8Ô‡×*:ÙàK!Ñ@ØáÃÏzáﮂ%çŠI’ð@!<áÁËÈ\À‘ù5–†Gƒp*a-·žÄ‚ú]#Re‰žXÑ8Ò¤»üp.ÿÞŒq"*lòÃj ž–+àµðé ]5Oa?È… ‰²=Ï Û’ðRì%±çç?¬ap¢Áï·à\¾¯€üå x$ìkú“v‹FÛ/2°=þ¹þú*ö÷‰èð…”ÐxZqÜŽåÈÑÓÝf!1îJκwøJ?a6Æ›ÏQ"¶õh¦«M#*,!aú|²Ú¿x6ýº¯º”µšhbr3CU¦á‰bHÑ`™å"áÞ|(r‚ÇYlNø×ÙÓ¡ÈQωƒ?«I˜¸6 Â]>èSðx&˜Ž‘ÀÓÔ{ Ì]2öE}£“…|nôØRü¹aXÔ$øËèS¡ÜlþV…Ê_}ÿ ]ðÇ [šó÷ùŽ·Ù£E$ÑjP"À|–:tj&6j7ü…Y|ùvŠrY™¾QNKN)x²î£Áq ÷ö†¢Å{² ¿6?#R¨ZªO‰É†>˜ në6®h Ãx(FVûÞ:xRµ=ìeªyŸ8e„T «é XgJ ‰ÉíÍ UAÔ󸦿U`h¨·n³ ,´_Ð(H2k¦~Jä¦ CýŠsÙ9üº†:fJÉ|F½ý8˜û©ü8æØ&šT%n‹,Ô^pÍ#à×dòÇ.ßk…ñÈ­–¦+¡`léúÈ:ÇÛLÁ¼1"¯€_“)›TUÀ¸u1’ÙŽWŽ‚ÝM*rv©gíb.»wàÐe-äìÙÌp87›ÑbLJ÷½– þ°è>¯iI^n…¢«é>ã²SB‘ç"i“pk®Ÿ411ùõ™¡*hbò0Ô˱³‡©/Þ;Iü°ã绣 ~;Ú{_þöõVOŒ¸”Äa7yÚUÌ;h¿”“õ²Ö~¦rƒsŽkª«Œah ]¶0ÏÄÄäÃTA“[ÃÛäþnEnˆŸí!²ýšW¢Wõî!2TÁÙ?ØôzT=Ù=q6«.u–;(óÄÄä6Çü#7¹å1@W •§mAÎç§èB¾$ùÆH«N² ñ‘}~H‚±Ó‚@q5¥}xq“G×uâCrF™˜Ü4L4¹å!ÆIYCõpÁÙœ˜Ë—žˆÛiup@“C´EŸž/wŸã}4I°…âœYÑ+¯ÇÚ·9KDÚý¸ Ÿ={¶¬¬, Ñ4mʡɌÂTA“[¸Ybi~ìð®¡ã ©ÀFÒnÖSa)-Ú=t2#S™¡¨¡†•D–#†•”nÈCJ\¥8¶ e:O%ÚëÅ ›žÈB¤@ÒŠ–úÕ9tM>‚ =zô»ßýîÆׯ_佩)9ML&cª É­A¬u¿àN%{/'›íŒo.ã-±Ö=žÇ¾ö¤:ý”g™ƒf ¹;Kè”s¾Í¢hÉ$8êw Ç:²CaÞ碧äÅ2­•O‹År÷Ýw?÷ÜsþçþüóÏoÚ´éÞ{ï­®®æ8δ Mn:¦ šÜP¤0Ë6–mÊÎë÷ J„кñ7Öy_°Î»Q}¤Dq”‘’ ýFŸš|4ÔÍŸ?‚ÿðÿpøðáãÇ¿øâ‹[¶lAZ8gΫÕúáU˜˜|j˜*hbr=L‰µ|®Üp!Ñ*I%^Š!M“åãÌÁíÛ·ïÛ·ïÔ©SÙlöôéÓ¯¼òÊÚµkï¿ÿþ%K–H’ôᵘ˜| ˜*hbráµÔêR “SWW÷Øc]¾|9™L¢·étúâÅ‹ o¾ùæw¾óxÀœ,4¹)˜*hbr‚vó!÷ÍnÆíMÓ[¶lÙµkב#GÆw2 ³jÕªùóç›hr³0UÐÄÄä³€ ˆªª*dó:u ‚#;‘~ó›ß,--½¹m3¹“1UÐÄäSÁY¤h:AN|=ôÐÛo¿ýÖ[o!ÓpñâÅH“É$ºWæ}2¹Y˜*hbòi`(j×û©h±¥"aÍ~œ’’’G}ôìÙ³õõõßþö·£ÑèTU]ºt©)„&7SMî C32íéA·²‘Ÿ\EÃPõt‡<ìáCÒôxÜzwºãJʬäPº3I{ iö7îåQóûÒRÌ”J m0Óó±+WQå: ~&QrH’\¿~}8^¾|ù²eË4McæwÞI¥RkÖ¬¡(êë01ùD1UÐäöG3”«ñÓ;£=Ë|I-øVÞ=’ÚÔ’|U3´æØé·cƒä?²ˆIþ sÇÎHð›ÞÙÅœ·H7º—WÑ“,C{dÊWÅ;HÈ+æ\ùÎ¥›$<ÓðÚÐùû¾íöüTæü÷º½Êå=jå+¥ys¤`2uò;íµ.u4zÅ×>á-éHuS´ÃFøJyÏ"׊ Vèa?—à)Qü¨òbçÒ’?–¾ðÚÐ…B÷}9B‰ÔÙïõÙÅå=n´w™³|ŽEøeïË/D/¯³’GbWÀzÏžâödÃ8¬„U¾Ì½b½èèŒÛn^xl@¼ÐõôÎawUpE1ïˆEÚIÚ7Ÿs‘´åÓ˜»C¸lÙ2dîÚµ‹eÙ 6˜ 'L>3L4¹MëC¢þ– Jr’}“‘9®è™,èŽÁÆ—ZÊ |R~}0láª%’(²ÒjJÑå¦Lw¿f eÚOg3)Cë’c™ÜNCAL.c©Öt$ ̵d:wæÜ?ZO¦ýŠ*m¶øŠáùÂÖ8ïßf­Ò‰¦dsD+†,*ŒDê‡ã§^e”ù–Â|NL e­iºsûnX IÆ!·‡ IP»åö«ª´UN2œP˜ÏB•?h J´ƒS†ÎÄån%¥h¬JС‹ÚE+õ–TyLÉÕ_jk¦«UÑæf;OëzÌ0úähJ×hR(Ë–ðœbó鸰 ‹«««£(êí·ßÚ¾};ÏóŸÊ™LL¦bª É…a¤O„LÉÕb £kÓâ„%P–€%p8Ý9ÝïÔ:¢¤µVðr ¥õŒ dFO'jŽ}åB¶ØAR]7:C&ÛÕ© 8çÔ .]5ÒY‚å*§#KÐi5y1vâh|¨\,d5UË-® )÷&ïuøÌ‘½GèÀ½¾uu0)3A°”3”&v`(zZÆ•cß$[,Á¤49£ {hà-\¹ÿž¹ãõºldÒY=“ЙRiiôRtdÒÍùT½]‘Ι3‰ßŽ;^{íµ-[¶X,–Oñ|&&9L4¹³ÐÕþ÷—Òü=\UÑÄ‘ÄÕiHB˜-UH7žˆ¼/Qô|תն MÈ)Š$Uo¯ŸÅŽÚ(†=l¦¥.4 ­=Ý£QŽ<†Ÿâ¶‰õÕnÕ[H C7²)-c§†ä¥V¢îw\ó ùÒþpNU UgÊ–nH·>×÷ú;ñ–RÑ€IZ•Tc)ÜôĘ!2-”C2Ú㣕ËH³¬¨ªÝãת¼otúŽ*m§Ý0Tƒ)"P¶!Ó²£ï_Æ[K,c•¤HŠ6"[+Í[È‹cN§F>;ÐI+++üñ×_ý'?ùÉSO=eÆ5ù´1UÐ䎀¡8Y¼šîñ° tT2CI]‰«rJGzf¤µŒjˆ '/%ZzÔ¬O i u=Ñš”,žJK7vboøЦ›zti…³Ír¦¯1ÙF*ý ]N`nðÝHG‰µ,@[ì´-Mr<¥Ò v¤»»XÎK$Þêÿå5¢ü)ÿB‘D•ë#•ûù¢¹ÜµsÉ–|2©\ ŠäFo¦ý|2Ý&Ge‹f»Ï¥"åÖ²à¨Ê£âKl•§R GHj½ö¿¶/Ö²k ÃÁøØk­Tõ-F3½ …Ï ¸8² ¿øÅ/¾ôÒK?üáцÛm†t5ù´0UÐäöõªN¡úkùå:¨÷§ Pm[„Ç( R74ìúA’JÕ†š²ú϶EÈŒ‹g®þKûž%fùK]«:µÜè!‰JRáf-¿ÊÀUÑ#«ñEƳÜî$‰Ñ9A‚”V{¶®À~4C’ Ö~Î2kd2’Â-!,Öº¯‹µF®ax,#Ȁ׫åF) <ÕÇoò=°p ÑÛ¾ÍúÈ~Âv·wë]•Ïù¼¥f¼r»¼ˆ+¼ã,‰$^™:: aÌ™Z9·Å·mãHå$é¶/™e[8~èã…®Íõ®Ñ›óYæ×ðûý?üð/~ñ‹—_~ù¾ûîCo?»s›ÜI˜*hrG@¨ÇŸ˜HãÆT F7&–‚Ëz¬9Ýë·XE’PJX£]^J¤sBÅÔ´jYbªO?AÐ=éHjÒûœðL9`¢†± ’ §ýeÒCOÚ?öC+GUqcÒ5Z1ýÏ~råpÝ5Nùô3Ý:¤|_úÒ—ž{þô§O>ùd0¼ 1¹Í1UÐÄd å^`-:8¹C¶’ ÆÔDséÁa¦¼)Øívd¾øâ‹/½ôÒý÷ß_PP`†5ùd1UÐÄd 4e[åÝX­D3¹u iñ0Òo¦Óäcãr¹žzê©;vüâ¿xä‘GJKKÍoÃäÄTA“é$ŸÇ™K¶g<Ï?ôÐC¯½öÚ+¯¼²uëÖÊÊJSM>)L41ù$1 #£w©Dï¤Í삟’$=úè£/¾øâÎ;7oÞ\[[kÞ\“OSML>tCU ‚#)0Ò§#¿Ü§¿ÚTÅ 7³M8Ÿ”ªbï˜ÛD-hš¾ÿþû÷îÝûÆo躎„ÐL@aòñ1UÐÄäcch×¢ï^Õ+³%B˜m[òž(dnò˜ªêùèñaºh¹5Äß.BÈóü}÷ÝG’$ÒÂt:½dÉÓ"4ù˜˜*hr'`hzº%yí²‘ °Ð®9ÖÒ<ŠèH6¾›ì囡¥e`JÄÊz‹1äÖTSC&œ5Hšo-¤µÁ3± =„»’µtɱ2±œÑº.¤†0HÒ^/U°jë+CGŒ@¨¢»'ÙѪZò…<¢5Ú¼Ö¦¤4\[pž˜o…Ì¥øù3rº÷§³ýC:Uf­]`q2½¹¡ë™–Ե˙p/gdK,º|­Ù+yWf0ÏRQF+-Ýj†$-åbEg£@¾œ¸|%×Á`(Ï©HN_|yàÀ ;;£g—KE„Ò{:ÑÖš”fKU%Œp+êÇH›7lØ ¾}û ÃX´h=}]‰‰ÉGÀüõ˜Üþ†Þ=ñ‹hO…4o+ŸŒœ¼˜Ž|Á¿ØJ$ÎFRÒºu¼µ)Ùð³èµDð ýÊ3ƒ—Šmu%äð/ÞêÕ·Þoµ$äÆŸÇäMöʈ¢°Œ“TÂ)°Wqé×ÞíÕù‡­´®eEÎUÌ9}w-Û¹7œ®µWzÈÌ¡Á7ÊÌrÇ\Ië;4´¯YYù„«„Öûß:YïÚ\K©W⧤RÁ‚µ…cqA CmŒ}6ÚW)Í­¸–dcs6/ ¶ïl_î¨Ó”t9ðÍ™³g5ëj{U8yú¹dß“õed¢KŽR”×Oô¿0p0FlYËHý=œ§ˆuúà‹ý{;ÈÐJkºÏËÉ/ù—û>›xhŸ,Ë®]»‰ß{g,Â5kÖ ëðf7ÊäVÅTA“ÛÈŠ_‘ÉŠÎ*7a°Zç_\:cŸ³‚vÙhÎ*/·‡æ b_竇ã×bÚ•VÅúˆµ4Ÿp޾ÿ^²£T`‰u-r®É'2)è¼Ã x ļÄÉ.9B8J]cý•‚_$T/k£!ΛÈöŒv;›VÙÊÝÏœ}>Ö¸Ö^ìbm,e-«7ˆ¢OþÇpGLׯ[«jáw£—tjî&g›„ÃõNаl^cU%‡³? :çÏ·–ôí;ûš.eRe¢­Vªe)‹¡²6ýx6ÆY<6Šføü2ÞN~/‘X¨ž#:9åÂ;á¶Ve—nUÌ…+V¬`æÀÈ"\¹r%Çq7»Q&·$¦ šÜþèz´S‰Ó¼[Â9úHãTÕ–%¥c× ‚ÂÁH+ãv1ü™ô€ú›3ìî·Y0ztÁMFná`.éïeDCOœŸoÌ&5#{.= 2ºñ+Î+ë‘v(f ÎÈ{Yg,OjÊˆÏ MÒèe¡É¦j“ªPõH‹’¶ð^;N†N®Ðaè×28N OqnΪªÍ=Ê`2qúiõ ’L´±8mSòRül›"«z¼)™39ï’¡§´¡e¨!úî@‚Îjqå#_Õê[d .[¶ ‰ß‘#G2™ÌÆÍ¡Q“ßóGcrû“‹IŒh¡ƒ†wÁ´h0ºnè É0_(mõ®ôá¨j¼Djãy‘¢ô¤Î?n^áY¿ÚBirwÇX-׫ $—NŒ„íDÿi7ú!çš¡ Ã˜FrH‘®ÛâíÎ …êB éæè/_Ž =ØPAÆz­Óþ°Ñå ”·Þ±|•è ñ!”Dñ·®!8ÎHn^ôïÞ½{5M[¿~½ ÜT¿\“[SMnùWι+=ƒúœiôdx֙ϋ Ž1Ùþ!Õ¨qJå¾c™¾>M+f¬XqR\mRezF‹鄱 DJ³¨Ñ&ëYÅÐtbB ÊSÅ’ýÙþ4„8-ÑšöqÕ6š%”j-M:«9ñp¦¹1[Và ­Òu &K,OºJ¨l¯Ü'C±—Bz‰lZ-®…‡±ÓVƈ5§vJiYÕ‘òÈSò°aõ‰$:ĸm¦Ñ(Šª¯¯E áž={î½÷^–e?ü0“1L4¹ý!q¡£®u¨agÿ¡­´¥³« æ²<‘M—›ãçw«×ú2Ý‚PwTâŒyÿÎÞÝy›uÏ•ªCD²ENdTõJª'_ʳ³ù%DãÞþ7º÷°–’{º•"?ç9”<ÿò¿Ôâ¼’Hi±ÆdwÈÜà®}3~õ¥$¯Ç®-®9^"Û ‡]éÊô‡Yw›Íé®lx˳9HŠ´-w/në?±£û•"Îí Õ–ÐP&*«ÉkÉ®9t¾ÈúïqÏ~1rîÇ݃ùœ=À-°†ülq@;þrßžÖ’ÕÓ=™îaÃågmû#ï¾Ù5Rþ=΢ݱc?ʶæ1¶"Ky½´ÞÆ`d VTT [p÷îÝÏ?ÿü¶mÛ,Ë­èkrS0UÐäöuˆy–Ù_ ½-Ù¸ F¹8¯”÷‘Éeohkë qùù|ÀGsÀT%èl’ÃiðP?- ˢƾþ$ÒÅ A‰|ÕWC–v%-ÐεRI¯~ÖUâ} `0HÉÇXhû²|«†ŒEžf;îòð=]*:U Þá-â$ô4VBª´JÓOuîZ~~þ–-[víÚõôÓOoß¾Ýf³™¡É‡bª É‹‘Ñb]§”HB÷;)óoá–i^IIÉO<ñÜsϽöÚk÷Þ{¯Ãá¸Ù2™é˜ù&w(š.¨dP¨ ôdO6!ñÚ4n}ºÝîxàÕW_ÉÍër¹L‹Ðä0UÐä…"…Jû"ôºÙ 1ù„Aš üñgŸ}Y„›6mòûý7»Q&3SMLLn7Úl¶‡~x×®]#a 0-B“bª Éí€aèi-Ô–¬¤™mÇ ¡Çãyì±ÇžþùÝ»w¯[·®¸¸øf7Êd&bª Éí€fdz2­‡"ÃtÕ—¼sÔm³(Üäc!Šâƒ>ˆ,Ÿÿüç?þ8BÓ"4™†©‚&·4i)+#©+ÿ¾ÐëªtPf-“Qìvû#<òÊ+¯ìÙ³gõêÕ5557»Eÿ?{ïÙ%Çu§y>á#2Òûòå᪀‚+x ÑSIQ¶{z¦{zOïÌ9ûbOÏØ}±3sfæt÷¨»W-µ¤–DÑ{‚A„!ñÄtmÞª‚”Eyñì¼MÒc #O×N¢|ÝØíöoûÛ¿úÕ¯þíßþíé§Ÿnll¤Bø BU²h``>‡èº”»Ñ‚†X%J›a£*H¡Ü=¨ R(wΆ•ßA=—õÚä~V€»ªp» h\ü—/ÁUq÷òI¡PŠP¤ÜÿȆpþ0"QhÜ+±¢éËè:‰xŒˆÒ•hj4 ÖÔ9\êB¶–A8‚¦õ(Œbx ëž…’Æùƒ˜1°ù18­˜»ˆKç‘Lƒ•Q½•> ÅùÓð7¡FhÁ¬^6þO16|ºŽÊN4Ö!3†®cˆ&V´.½¡ 3>гûV±yì býè:Dz3(¹õæ4 Š0ƒšz$© ¤Pî6T)÷=F}{qv›žBHCvûƒdë¶bdü®¿=Š÷þÖ6tlF~ ø”wÀ–Fß'Xú¼.1tuaí.d&ñÁ¯Á7bíô¼‰ƒ/㉿€CÆäQpuXRÔ>~åuÈžÂÞ±êIÔ”aôDf‘ àè˘°áaLÀá_ÁñQ¸–[‹ÃÔÑ®>¬Ûì >ø¤Kй b }n ýf 1‚ÞQTµa”¨ò]-×;‡‘ÌM|šIßÚ‡c”ÙÊÅl nîÖVà;šC›LöÖåÖR½:›¸œ`ÔRž‹êò k…òGÍ0%ié© ñ»¥®B°< “SõéTÿ`A^n+Wþø¼†¡'²góürµÊ6_@¤ÐŒÌ¥X¯¤ÔÖ.¶½T)wÃ0ÆÇÇGFF‡ªª‚ pÇóüM¿oŽ”MÜR.Ð4 T/WA~ÝhX‡/âG0B¦Ã<³Áìx‹äÀ× ”­˜ÿF²,Ö…c#œEÇ.Øœð¸pêfâ(qB–à«5GµØYœú2!ôœ@ʇ¦å°É°mCœƒ6ƒž^x¶Áå‚È0ž_YîÆ æ/2ÿŒ^Fï6<‰êZd¦!ÜòlÐÓæ%„ ªª0öõþ×J:?×èÖµd~êx2Vo­.å%…wçò—? _~N.ws¶/Nå„<‹g3=Ÿf´ÕŽ£MgÆz O½Åu!²÷ïfGW:Wey}F·Ö[Ë•/x6“ŸØ®µ/+G¬;9èçƒeDïx¾ÿxt=r4zI’WÈNîÎÄ‘/\Šy3묰–ý *HN¿œ8ü/ åoäŠv¡~“פЬ¬·Š¨àÈâ]ƒª åîAäíØ±cû·›L&eY&Zè,âr¹®nƒÁÎÎNÇsM  •«Quûþ–´=ò8â3=‡£ÓÐ’(k‡U@j¼ªý‹ŸxÄJ&g0r…â¨XŽk+5´'bÆ(¤‘œƒZ7wÊ>È@ä2SÈ^ÂÑô ü+ Ì`ß;%ˆŠÕØüèµ ef ‹°yqÛº1ñ¸Ã‡0™EÛr³½4_€–G6U½ýñ÷/ŒÛÒø¢Tk@MÜíl‹ÅNîa*~hŸ¡Çó“G2}Iƒ+Qª$;ÇÀÐsc™‘l´1(—-‘]Ò•·[7ò£äqʹY-b:ÙÑôÈ@.ªAðJ% Їè")œîÔèl±pj-$Rä­É]N \ΧDέ ßÖWàÔj¥Ä"Ú8ä'Óc}™ÉÊ{›,¥Îû;ÒUAÊ]¥¬¬Œ|÷†††®î™W»ùåL›šš¾ûÝïvttÜp9ÀQ‹Gÿ qòMüví€äÆ’MØÞQœu@¾:zdhYò¸9äö-!¸‰/”ˆÃ{kš®ìaŸ¾å0¼ŒlšF¾ãWvŠ]¨^‡‡¶,\º5e¾ê¬€p]=˜lèÈçnÍ‚I>‰þcè#:júÝÑKˆáÐëØôÜö/*Èû –¬¼`š•†S9ÅÊ¥Gœ.ÌŸ±¡0›H°e^¶{™Èžž;ðZtXä‚‘ ‡ÏoðlyÄ^:o• ZüÐô›ôÀjk@ד½É±‹®‡¿ïmѲƒ¿Ÿú¸Oã}‚ÉEöÅj±&Ósye6Mé™K±ûµàãv{\Kçô\\KœK œÎ;š¬ÙØ¡—×YÎÂèÉL4ð¬ÇJ1¿oöƒ’É€`I"ïqÏûÚ&33Sù¸/ÏŒì™>PÇø=¼5³ÿ\>AŽåÃF+ž l­a£ïM¾ÖÍU¶[Ü9-Ú:Òæ}ä;®Z¹øI6Œ\wäðKá>µY˜ÜœvºÝµy§Êí}/ccþƒ·Ù ­'zäsÓß rc'Ïåu7/Ær±ýRÍóþ¥ÌÜ;“¯õrÕí'IüRêèJ÷¦ŠüÜD6â"s…ŒfÖ¶Š¥ÇN¿þ‘îY¥–°Fv8søœ}óóîÊ“¡=o'漂œEß3Þ¥çBŒHëÿSp›5Fã'~=}iW™oþÑOª§¢^‰ ˼S"µ¶ÂÉÕî­Ù¬Íîù0™ò –d!ò~´êÇ%ÛêyæBdÿ/C=²èór\<3œb›¯ÿ Ù&…æòÚ•BòwSGSœÓƳùôÇúgÝÍöþ­ÙQ¤ÜU***JKK/^¼xuÏ|…Ôf³=ùä“?øÁ6nܨ(Ê çË5~Ù ê:¡Obâ82FO!Ò »dÊŒ¬ÂQáCôœF ™yH˜ÆNÁ0=ò­…ÔH-…3ãhª€…'µZ³áôV8¥Õ¸x =kÑÜVGƒèGÀމ3˜]·jŠ®èÆŠëüŸqÝZ¬j9ì9 |ŠæRÓtæ´Òç,yÎXQijÈö€s"X‹k;«¬ËŸqT&SÇþÛÄ¡®\²†I¼îVÕÎúZ=üÎôDη[¥WìcdÁ;×z¶/á2û'ùZúrH«9’f\ñÈ*Å><ö_&>½ Ö¶«¥ƒY×NO{ §]0tÝ`+­ÍíÉ#Szó㮕}3ƒ'rF*?µ'tÉ¢®ÿ‰™‘yw®7’O¦9Vâ|ø6¬µxãé®ÿ9öQ~År[]Y*»Å³¦™ Hœ =± Ùï–=¾^uES§þûÄ¡‰–*ò‘3R<ïÙìÝV†èËã¿ø45üˆ£F.f?S˜Ý: yõŸÚìFâPè­7£çÛÔ-Íÿ+ñ¾×…ŸK ËJK>Ós0™ývéîÅIŸþû©cŸ¤šž%¦ËHò‚{‹wk)"/ÿâD&´ÁѼ$>Rë^ס®5«3 â6ÝÞ‡ùÂñÐÿ»¸J% åÞæiÛ`-ÍdzþnôÝî\C«Zy&Ú}9¿Ì)ê瓃Y±–8òbÉüäû¡‹kçŸûW¨F佩WÞœ[.µKœÿQ_Ë'š:÷?Ç\Ì´•+ÆðYYý—Á‹zs|¸[Óoz¯ub,üPjôbŽ{±âáu²2žš2¬_oŸðW†ª åkÄl*Òét<N¥R,ËêúÂWHÅ•+W~ï{ßûÎw¾ ÉK·$ØÃUÎÁe7’ÓX²Ñ·B ÓCxçát#؈ÖUðµbÃZœÿ‰>MeÍS=¨¨Å‰W1[oº:6ƒ‰!´4bóÃ8z ï„àð l)š13†4ƒÙ!„¼˜2•izõ[0Æñ—Ðï‡Íƒª5XR‰ubß¼ûpyá_‚¥k`³\½cÌ ajz #½h¨Ç¦øô,ÞÁn…Á!<‚P>çB»keæ'Gh)¤ÏÂêEË*(Ô·RemõJÐÆI’è9#nè©Â\ovÎÆö¼<=Ê@›Í¥¢F"¡åqmX*ú+I4àåt*—×3£¹PN\R'¹d–ÈUæÄD6Qg.ËŠ,ÑŸù†Ù¦ ˜;9YÐÔŒ6×_@§Réãe†[òý’ò*käÊ$×éô…ß'òy->™Ç c83b-ÃÏçÃ0²cùÙ$_V/{V”j?wb<ÍÙü€R&KxQ4 Ì_­àË]$ž ¯MÏ0йx$/Åt¶ÕZõnìÔ…lÌÎM^ÊmÞÒxz /ý4røbŒÕäT!çÌ%5ÕXÊ¥`ÐLÜâ.&†gAî‡çoîÃ+Ä@µ ‹ŒV©”롾 „2É}:ÕûrêBNOŒçc>ƒi°5”Æ÷œIÍÖ1zW:Ûè¬÷sì`ñü´67¨ñ[,Õ>^b Wƒ¥üÕéHޱ”JÎ3©ó¿KäsZ|ª`NN òUö2/'²¬§^qìMÝFÝF¨¶T5Ɔ÷Î|Ð/ûk”ªV5`½ ¨ R¾ˆøe2™ÉÉÉÑÑÑ‘‘‘©©©H$b±Xêêê¶lÙât:‰${È‘Döž{î¹^x¡½½]n;®Ø€¹d¼óNŽƒÝ‰Gý·è@:CPP$02ÖþM!Ó¦&Ñ}Ä<×R]DÊlÛ”DlŠAvBT°ìYTmF&Gž/°8AÜgÙ*|»œv'äM(] Ù»þ{Ä"(hfó¦ÍkšËªmøö2¤ÒÅÑ7¨7:W[ :„Š V|Õ[ÍÉ‚ëFžƒÛy%vL±r~“U±ú'ȉ°ò·‰,³˜!’#š­Þó?ó£ˆ °Ö€R¹\u˜UkÃNÖUÁ_%–áv¾hŠŒæÿºù3ÿ4%/™'’´¾DQÅs¯+TâWŒÙÔÙúÄ©.m³TÚd_¼ÿvÖ†Q¼Æ•s‹]hO`v>‡ -ò7œÄZürùr«·x|ý&Æ^G„Š«kN_Šyù˜P²\õ_Î0VÞU¯Öú‹òßf[éK8$ŠJ|Ýín鲸Z$æ­Í¥/í Ÿ•†µjl„.ÇzHþ­Ru»â<”è½À03¬o³µB¼Vfm”]¸¹*Ø£mO}4wÎg]ºÒRÁ&úãEßH¬¶YŒìBÆgf‰õ[–þY¹·'5Ô•~¦ë`¢ý/K6ø¯õ›ÞwP¤Üˆìåóyâù¢Ñ(Q¾þþþééiMÓˆÏóxeE«(2ÈÏŸ/sî ¶@ 'ª9e#6”—DWVŒÎ'\+D¶/6.–0ÃÚKX.”Šh¥’ žØ¸Íç•< F’}3ºÒl_µÒÞz<ôî¿FGC…¬Ÿµ|¹º÷ª‚”?"~ÉdrÞó‘ßñxœ¨]CCÃòåËý~¿ÃáPå¦ç[ رcy•XÀ¦¦&îŽÏ$Ó ˜B:‹ÙIÓŠ÷y¯Ä7«Üæ¨|=~ì—…1“ÊÄ[,þÏ;‡[ì­KS¿2½çœ(O¦Ç±~­ÅÇÎX¤ÿÙãßö´|Ö©*ïßâ¨~9väg¹Ajʶvç+ezöÍîïC‹s>v®Þæß:"ÚƒÓà M¶Ööä¾7§?¼$«ÓéÑ WµÎêç˜ØçäTá½[\¿ ŸùùÄŒ×TßP¹}C³¥„eÄjk³4÷‡cšç¯‚*+4Ù[W%÷½5ýa·ìˆç&"ð<#•ûn÷iåYÙÊéGÇ+Ø ¶2ùÚЉeFöÎ~(¹ÞääRÛ†¥Št’íù$´FR =Áñ\O¢ë¢µj¹R×À~+ÅÿÔWe»îëh[É{‘8ö mLE²7 ®e-B¸‡÷Îîï5G<Å%ž9;»F^¿Ö^õ›èÑŸp3¹©\8˖ݮꡓ:Ê[áËGäRg\N‡ƒ–&'Þ·ª‚”? "{Ùl6“É„ÃáááaâùB!sbñ|Á`h[UU•Íf»ý´¿+wøŸÿó&biµZ¿Àþ‰¹,€³bÝpùŠÃ;© Þ5§TûœÏ¹D”ç›éÜJÃn¯»Âœ]Ž+ÙáÞ¦Êv–±túvz•þîl$Ç[û µÂrå“À±–•ž-ÕB•y#”©k¾-Z<,c—¾TN'Gç´B‹s]«ZW-Z²Üº W”XF¨w¬u»••[í›$#`cùZÛÊÝŠäâ¬AïCN¹·;SºU­_ª¸CýQ >X{ƒºz­þ|&íWª¿ë— ° ¾¾Í^Åe—Ôï¥SÉáÙB¾ÑÞÑl­«-ÐÑéÛªK%BÑ/6Ú7~[÷د5›Ê+ÝÛ¬bÿEsª·Þ½t¥µÊZl:uJuß ì\£9Ö(Œ*Ö~7(ŸNO²nuùÃj]ƒdeuf£o+ä…Ä›ìŸÕ}>)ð¨ï‘’t\a¹͢ؠַ(ž™\r·¾ÝV_Ê1?(ÁÙôÃZ—XVm°7žMÇÈ›!pöRÑ^ª—.“]EoÉÕØÛµÈÖºÁ÷°[éë5g­8³¯[a)µ2ÙÅ®lTàjG‡uIW6#2ÊÏYìȧ-¼o££a&®ëïyç6ßfÙRÖ$VY_O&œ5Øvç¦Vkm OgJP9º®“7===22B ßÐÐP:&–Žx¾µk×z½^"i’$}ÉÔA¨¨ø:fr êÖ™?”» ð6±â1ïÕ7—HWÕ¹jþŽ<:I½¤ÏÙ[m­·K„cå&×ú«y-+v/l³>¥æa¥æúƒ-BÉCÞ+Qé„ÕÅ—Ø:–7ÊmKË^³/s¬ZvC^•f{{óµ )Þ¦ù&nKÉšùjåü¿n¹r‡\ycÕ6÷Õ UÖUU7Ǫöå·Ìvá8ëjׯÕWc8—\±M¾ñ»ÀYÛ¯%.ÖXWÏßp£½ýÖ ïZåXsý4õzÛŠúkÍõ¾:ÕÈé©Ë‰ ç2hw¶” BQ¯¹ ëÒ+&…Ó~CáÀÒâh¿f®eÏ•)j‡kÝÕiLKo¸i†íZÏÚù¿Zím­‹g²UAÊm ²—ËåˆÔÍÎÎ^¾|ypp0‰°,+ËrYYÙ“O>I~«ªúùžB¡|­ˆœÕÂ~þ3Ü0ŒÄ©È¡7"ƒªÒºÛQ#Óoë-P¤\CÓ´h4:33C<1|Dÿ …ñm•••.—Ëáp|æHί†¡"3SS3!K°¦Âóõ´”R("ç|¸äé çR?ï»Â°¬µÅÖæµ¬t k? UÁo6Döæ=ßÔÔÔÀÀQ¾d2IÈb±TUUmذ! *ŠB”Á¯W™Œ\tvøƒßþætbÉÿõ·?­uÓ:+…òù°¹ÌñŇ1v1°xš'ïT¿‰äóyâùfgg‰ìõ÷÷“ßDäêêêZ[[ËËËÝn·Ýn¿óC7?†³T·t<´úÈÿ÷ÞË3ß­q©.RŠ¡f‡4¦Jò·«Ë×CÙé¦ÙDO.óËsñ¿°®g.gB©ÔsmȈžÈNOR¥ä¼e‚ùŸ‚a®/̦K$¿Ê.²ÖõôH&¤Š>'2f$³ôHv¼¿JHUÑœ,›Ÿ*0årP5çHš{ò…©q]®óäôäL.šÔ5ŒÄYü¢ÓÂ|åZ&¹n!:œÏW)^…©ÌtDׂ=QÈ–+¾?ºRiÍfBn9hùSÞÅUÁó›¬iDùˆÏ'²7::šÍš¡UˆÚÕ××ïܹÓçóI’$Šæüæ{ø‘çy‰Ï%²úgÎý¢, =±}ÿ+)þ»’Ç›…ÛÔ¥æÒþÛØ‡i¾¼ÓVË„Vy·¶H¶?á3—Ì^øÅäÁ¥þ¿mõ^½ô©¹}ûòž¿(8pg>ÅÆ\vä7ã;ì[ÿÌÛlcX¢ ]‘ÿ¿¹‰‡»¶úxdφßûï“'¶­ÿgùÖRŽ×ä±Ðkÿ%4·ÕÿôŸy›ì¦ì‘cÞ5Wú—%ÛKXm*ÝóvèÓ®LŒe%ÆÈg ¦L]úŒ·c‰ð§¬î@¾ÜálWNh·V̤º÷';…õböôÿ;~L’kÖ(ÎÙlz‹K¨|qZfÍ"y.ÞÇʵ͢šÌ˜;×àÙÒ&;ìaÖTXÌ*g.‰DfffˆÛëíí%¨(Ê’%KÚÚÚJKK=QÁûªšWT`ý–ð„”Å€¡ÍåfCZžc…ŒžIjŒ^œC=—G 9á¼ËËKº‘¾”ì4ø ¶æ¶Šˆ8S!Z³…Єƹ9}6ŸûEŸ‹+®<¡çgós1­`0¼Gp»8³[+­E&óÉ8¡@®’ÎÝXeÒôlJËFr3q=ǰ’OtsZxªÀVÈ®¢15Ò¹©¨âõº«Grs¡BFgX…³õª$ŸGŸR·ÍÚõÛÈ‘ã–àV«;™íù Ò´®ß zÉaÙ|èÓØ¨] ijý§Ó«J¬fXÝ(HŸŽ=®·©f(âÓRº9Q=}eêƒ FÙ“Á­’•Cn$qæ¥ÐÙRI¹»þ:¥ÒC™I†·ç ñ˜®ñ¬¥DtÊ,Cîn&ŽëŠâæ…Laæã¹Oäl«6+U-ÈØ‘>šèc”G­ Ë,ž)1àåb®òá\8l.!zD·ƒãÍw'Žh9ƒá¬¼ÃÏK¡äÙ·CÇDkÁål.K–Ùóå’…QйxÆÐyVñ‹.•E<;cTÙÈ”|’Çv‡Iû¨ >8Ì{>¢|‰Db~2ßää$±€(NÑ[¹råSO=åv»‰ç›ïç»×ù½ÖUVVRÊõôloð«t¶ûâ{ÞàœNJæF3Ìòè?9öZdae*ãyÔ·q fû’ƒ“™øHzð´>z62´³Ü)&>þex²ÑZ) 7ž™â¥¥^²ÞÏdÍ|;6.syú3\ÙSÎ cê·SöjB@°IúÌxAk»9/úl¦÷Í™tAOÍæ’¥Öövaîõhø¹²g:dK¾0ûÊøËÃÒêŸV»ŠOmÃ(Œ%ÿræ|†‘#6¤¾‡²¸k:¨tx:û³o}9[%,=7{tØ(ù‰³Åa¶uêSéþ.MÚìÝ01÷Á©Äà&ÕY )ËTX[+ȇ¡3Òæ’k_5m8qéT:¿»|ã6óæ5¼ÎÍ©^ãÝ×:Kç§_û}7ão’í-2”I­óíú–Íq8´ObVåä‚–_öl`“=7~!~i@óŸO”e™éwcѼ·©?92“e‡ÓC'²Ý§âa‹ä_&ò磇^ ꌠYUnü®wU:yôwá޵ºA‚q?îYƒÔèùĨŠþ‹’—åoN~ÜQæîàS¯O|1¯9x!QH;•ÖçÎñÍQ²ÁªO¿>1pQ׹ѽ³çEuÝ÷¼ÍRaüדo¿:çþO%k68›ö&·º77ÎÕ2âkôô”–ßbmßmóô„ßù¯³çzòm¢ÞÿÎ\O¹kÛŽ*ä/ÿËäÞ7"¾uÌåci|¯|W‡,Ÿ˜}÷p|â–¼haHö·-ÖK‘½?w¾Ä³L*ôžLL´Iµ±Ü詜Öî¬p.D$wH Ҽݽ©‡^{éh´oêw]ŸEâ˶»V^œ<ñã£Ñ‚¶Á·±U²ÀÁÄÙø€ T®³Väƒ?õ ºZZ‹j&ñþ]¶ªßLŸ|7Zñ¼óJ=#?’ §™`Sq±\³ÚJþW¦”›=ãÅ@© —$EZ˜I‰Uë<[k˜èïÇþõp¢¯žQÞhö=ò˜½¢íùÙäÞ×£ÕÿÞUÓ¦øçд˳<ù ¦å=Ê’NGÍQ†Ûéé´$вĔ'rcoÍv‘÷âûÞ¦DæÂKs£ç““j>S¦®|ÔÝÌå†ÿyôõãÉÚïÚ£}e®5ëm¥™LwJKŒÂÅèÉýÉÌóeßZ«Ø‡cþÇÌÑO”€MNÞo96®“Ù#Ó¿ÿm²{£«¥åv à÷?Tó 5“G”o~xËÌÌ ÙÃqœßï߸qcEEQAY–s†ìb’Øà§ç.Œ­úÛÛj©."òÚ\w>Wb«­,¬QºTq’fbù©îl¼Tß7fŒüœ–Ï„“º!1<˰' ÚÕ·˜ó ®‹Wf¿h×™˜– Æ»sI)3´G'VrNK‡S“n:/Ô4É^Ï/U+Ñ[ò•‹þÕjÐųõj43’†cªìOöM;Ë&“C9¾´Íâ¾ÚxÇ2R•Ú2FN‡åŒìD>‘¹S$_¢rëÒGì?}®ÙûÌ£ö€Ùdj‘Lÿ©ädйJ04¿TáÐKNÕ;ŠÑhÖo]±#3öoá£-òÃWÄÁ(ºÁˆó-®mî@äL6©CŲ‡\måüõ!§•J¥²F´(†^*I‡òÉñ\d‚ñ}ßZídpµµÒñ’³YW‰Ä°…bEe¾8¤›“YiÞ_0â…é>M~ÂZ_*Ú¡ão,ËVŒe=5qdîXVOÌšž"Q¬ÀI"ÃeæÏ5ÒÙM¬_¦øH™×XëÝ3]Ùh#„r1ЪØe†dÏšNgSÆbíΧ*¸hÐu=•J囘˜¸|ùrooo<'>¯ªªjýúõ%%%D‰ò-.Ù»C ¦·,]b“è's1A*fyè<Ã{vÍõP´YF°pªSȇ²Ý¹i=_ê帹ۥÀ1Bq¡ƒùÁYæU3ò:#ªœÅjÅT7¸6˼k:1d¦žÂr,skÃ>+±¢dªñO$€Q–ÙNõ\LÕŒ¦gJÕ ×¯_Aôûý™?É2-jMW–OßfMd†eÕ¥je¥Ø_o­š·‰2]ñK§Ò?ûÉM1£HçÑX÷.ëBð†QV9WŸI¾ñA¸kíB°oÁÏ«‚>:–O× RqM(ÑÂf2?LG›í+Êùë½+›ñÒÊÄ0”ˆpe9N¯é7/øþYèÄ[—ª(fŒU8)eúƒݺ\­vq™ýŒZ§¡ç¡‘J˼r³Œ €ÑŠ£[E–d·¸ó~ú% Ïšû—«žovvvppp`` “=Äá•––îܹ“ü¶ÙlDùxsݱÅýAœ‡(½np¼9ðýA¸ogõ0\,Né增ÉÄ †lá=AVÊ5Û]" “™‰$ë”n£[ Üø–³VÎ`‡+­M«ùÑ̤ÆÙõŒÕHφµœ—á§2áhá&ÛFÄI ¢“ù\PâùPšì‚­\©¯/¼?w‚×øÞ2ùº+eµÙ㉙r×ãÏ{—h…ñáð¡iƒ<ã‹ *ݘ2 –3—-œÏ¿‘ÎÏ‹4¸¶>íj(Î[7¦Sg~5×{*ÓteIFË¿åYñ÷S§ö±©‚XIt¹ÆZS¹¸7|®VXàl««W™3s3'ã_ô‰7‡ylZÿx>Y+ðºÕb.Ù%-T8Hž?gh5#³.‘ÏÍeu«®…Fz &}<“_çßòmg0‘é;BÁ˜_óÓÐÌõŠ3Gq6==>«åœ¬˜ÈÍD¡—Š6>ÿùÙ]LP¼ïÐ4-‘HÏ7666X$›ÍŸG4¯µµ•ü&Û‹®©óKbNRbùEÙ·ðÍFä<«¬þ—Ç~3u±®Ìtš 8Ų VÛè'oêu#~69YëØX+;¿L‚ ¸€R³ÆröPè`>[ÆjsRÑ6ÏÖfK•?vôå™M‚0ž¾3šÑüu«ò`Þäƒ Ç¨½; \ÊÇó‚‹ow“®zw]`·7Ö3›K1â*׺{•‡TýˆÃSÆqªÜä^YÊK‚¥qoØŠ6KÊq±Õ‚ÀsÇ‚»ý±Þ±üœÁÊë<Íkl%6Ãùb@8š‰èÒRûêr5Q.\Æ-³®ø‘¤ÛYc(²È-ß³/]R V¡”8„Áe¶zïúe!ø¤ë¡ÄØT.Q®4üÀVs">dh±¬á¼I%!¸ÑÕqårË:wz:ÖZœW“S„ÀNïšóQÅålI±ÕÕlJíôl³>Vª²0`Yy•{›W©>ž*¤cë–k:ÜUÍJ‰ë:qfYe™k¦x9SÃørËʇuŸSª|:°ëH¼:7DZ¾§:¬nz££óa£ÏÐÒª\õ»ÌÍñšvÙY7ËòrÕ6·ÝË‘ÂT¿x´ø^ÄY¾ô9gS»âjâôc©⛫,ËÿL­:žœåXçf÷¦“éd\Ëð’kƒ»½RK…e?J'’ã㹘]iý¡µ±E²ôÛÚXÆ_tÕŒ]®Ä¡ùîÇaç_ ª‚÷†ùÖNâùÂáðèèèÀÀÀðð0ÙI|^0loo/++óù|_{ܲû-ÑßuêB̾ëǶ”Úiƒè"ƒa,béN_éÍû¹’-Þ’-7îó(-/ÌÏŒjSjÍ ±íê² v©þy_ýü¶KªØå»iùu¥sÝÊÏò“ŒÐèèh¼qùª¥´DOjÒ#׬±oêþb¡Ê¶’ü\ÝÓh]ŠÛÀXåÚÇäÚ+ µöÕµ7Q”®-Ën9Ñ%×½PRwíoV®V›ÉÏg܃‰È96ùw\9Anvl^8Z©yüÆU5H»•æï) ¯/¬éÀ¯za~\¾å©+KáªbÙvoÙöëÎlv¬m¾.Û•U ª×\Ùóh`atkm9ù¹þª-îW`–•/X>çnîw¨ ÞUˆÎ¥Ói"x}}}Dü’É$±€v»½ªªª££ƒÈžªªóžï^çô®Ã©•íß©^®Xm¿X+•”û#šíûíô‘Þ¼ÖáÙ^/H´~E¹ ª‚w"{~øaWW—Çã!žˆ_ee¥Ûíþ¦¾Ï†!5d‹U^Ì5JÊý‰Ì9[ìm«…@«% }ã¿h”[¡*xW!VoóæÍdC’¤û2€ …ò ÁÈBp“3x¯³A¹¡*xW!žÏårÝë\P( eª‚ åÎcö+L]ÈäÕ ëíÂ,†6›7ÔÅûysî@N´pvdDWZÿ×z!Ê"…ª …B¹F>’O¤ §ðVÙSÑï%´§9[»â"ЉˆÓ’XÅÅ+ùüäžÐ‡çŒŠû×׉V"šáB*o<+»xU0Ãljá|\ååX!™7 ò6 òá‚wÔÆÛ­,yvâ…dR'/²*oµ±QÛL!3x…ÑZ OŽä Ó{CûNj%?ôoX"Ùi`>ÊMP¤P(_]Ïœ ôftLcy]Ï‹BÉÕwnîøé¬ý~•[ùÕÔÑ1M- e“gk>t&ÖÕͤÉÕî94{¨+›&:–5øFûúg\Õùlÿ¿ŽíI éì¤.TÔ rºÏÎz­kà]6—<þÒ\w,ÑKQ¨xÚ·¡AäÏ„ö½—Š—ÉîL!>‘‹•[W¯•2§¢ç/0ñ£±€Ç³ÌÇчåè‚B¡|UrúÜð…ŒØþÿR&ÅDZ¬U­ìåÝyV— ÂPl*κvyÖÔqéw&ßú8ri©¿©^òAjÝê¬ìÛŸLílo—¥óá7w°ÖâkeŒÙܸ&·>ç]Kø&NËžG~àï޼ó³X÷JE=>s<)-{ÁÓ*F=õÑïçÜXš×B2©F×–gUÇéпŠw-·nm”}¡e»«ÙÍ~óæ Q¾ª‚ å«ÂB ˆ¶îìåC1¹Q ns¯÷ðÒÙ¸ °¢•—y0å–ÖÆH47rÎ(¤t-¥Ï¥Qa9‘•­Löbj,nÈ‰ÜØÉ“Ðõx~ª'k¶0õ£A5b'â—¶u¥bÅm–9q*;|2QµÚZf§¡*(7BUB¡|EE®µ×L ˆ ±ŒØà\·ÍQ#ç²m™³#©Ë³òêÍ®Žd¤ûPäB¹Rõï!g´k6i´®¨÷§§v¶¿ÀŸ&.ïÏ2ï\mo®àÅ‚.5Z뽌9µc­Ë¬5΢ÕãYw»ª{Ä`[p—-|þ|ì¬Îˆ%–ƶr…aœrÕrÞ2¿Š®È:TÞÇÛìö¶Ká¡þÌÄKª å&¨ R(”¯ ÃHÍŽŽûªâ2­`–cXðËþBiÕÍ5ü8Fjû©u…Q K:m¶å(n¬uéFq ô ׯ•N³³9Ú\KÅ+Ÿ)-cŽl»¥º”VsÅí€ÒòSÙàÍe˜jž VjÆ•+[A[\ f~at¿eÅOüº¸ñolŒ+‹­S(×CUB¡|u“Æ»¶çºº-|Æó)ò õáÊ6ÑQáJ¯ž»i‰z†¿©†a8‘¹}âÊUî{ÔrH%néÚf`±"‚ÕñM\ˆÎБŒA±áú˜ôRq(NpR‰äÓÈ`µ}Ë“B¡|ƒ¹ÏUPÇØ1|ü"èi„£°y!ò°– º ý}Øõ( ~³܆¹>ìû-;Ñ\åÞÉÎøèM¬øß°¤ì¤(£€soàìžü3¸lw>Ï×.¤cn¢VåÎ$…B¡| Üç*ÈÀ׊Mvä LÇ»‡Ñô‚àD$úqxÌtŠÉˆ) ¢ ©Ø#N¶ó)dsæC–WÌ·}Úêy¤¢˜‚¥þ iäs0È)"$lqšQ!‹LÆL— ŸÚä!žI  ™{xÖÜ_È «CUMAÒ‰sMAq€˜´<ÙŸ5],/C–̉Wes¿¦ƒ+¦¹p¡ 2Å#E¢Æ0ï+“†n˜w*[pS0b[+P0Ï5¯’1g-g“˜6³7t‚ßgÞ;9K×M™7Âp,æ§ CJcjáÕ\¹¬Y‚ I21ó“ƒÈ¯Å™Yҳșq!«¦û$N”dX+&H^%&•˜Ë< À,†…dÏ"Ú½¿„w+Ö¬‡EùfU_(Ê}É}¯‚Š UÅ¥ˆäYˆ](mF]¥éû‡¡§pâ¤#ˆÆXƒ]OC幈ƒïc&³O è¹îiK&ÈN£ï2"V? 9…O^Çä,IÕt™O¡® é |ò†GL)R‚hû+1ò~„ GÌ ”fl}_Ã¥(ÿ>Œ¡XÿS¸R8ø6Æ'ÍÓmåXûœy¼ý+pn(yÌMƒ `Ûs(÷!=†Co`hļº§Ÿ…#ƒ£o`à²)–²KŲÆÚ9cã8ñ //¿„Þ˰xaK›bL”gôºûáªCã*TÖÁj¹Q ú>Âñ#¦`s ª;±¡óÊ‹bý8ø&¦Ìm[%Ö=J?ºßÇáÃð”"5‡xuí0¢˜FšÁÚçÑRî½8ù©Yià-¨Ý„õëpö t ÂïBd‰ ª¶¡³>Á©ýðäM \½4š…B¹×Üç*ø¹dÃ0ý—,ƒÛŠOÿ]ãØù@ÏL¢Üƒ³ïââ8:Ÿ„GÂù·qèeXÇþס7bû$û±ç}¸ü¦¾a :ßsYHbrÙ„)cÃ9t~j'_Çá÷°e&NÀÖ‰5OƒáͿù:wàܸ8fªµšÆ±÷qú(œ“8q O¡Ä‹ïâÐoáÿßÍmãjïhÓC¦]ëÙ‹gÐù=”:pî5„àœXý,*zÌÛ<úÎ:Är4­‚Û¹P ‰„º°ï-”mƆ&„/àðf;ó|(-ŽO_Á(Ðù]X²8Nrþ<Ï#ŸÄå.Toújý…)ð;‚íëpðáÌ!X¦±ÿ]Tï@óÌœÁ±Wá 1…îÓ¨ùK¬þºþ€océ T5ÃWŽæMhZ :XB¡Ü,f”]hÙdvƒÅ9œÿ‰4âaôO f9¸ N«ÙwI!Pô‘ÅG¿ĂO¢¾jÑ$Y³ÅRK#:O=:¾m¶Ý±a vƒ/……7c^œ?ƒÞs˜È`ãÔ4!ÀÅ7ûŒyfPÚ ]áàtâÔ&—“£r9ªj 9Q&!Ff—{á^‹eX›NãðÇ0œP%ä p‘Ü‘i8â öW7mŸZŽ®§1Ñ®-pHÐFÐÝeî—l¨ZŠåˆŒãì;Øÿs$rØþæÇÕL]À¬†år°ø dÐ=€Í|•8`âAmk Ó,ÀáÀÙ~LÇLS.;ÍJCÀ…²œK£¦~eU8ÂåBÚ½f‚¶ø4z†àl~4¯„ÏŽšœ>m޾ñx аû`µÒæP …r?°˜UQL©(Î/2 ‰™)$ aòN›6ÔÖB¼¢UÄÿU4¢§=‡LÙ#bf·›­;Í'~ÿ\x–¬Ü: é9„uœÛ_lNÔP½rÞLD)öÿ1<ì B·>Èb?_ÒÌF¼§ãÅ}:ª› p`„…þE’Ç™¶L˘½˜‡ÙóGv;§ ÎâÂbw n '¹èº˜ý¾eXRf&«K@šýmfÆ,……ËåÒ]FßI ÃÛ€’ÊkÆË|5lú×Ã+¾ûö%`L˜Û$çÉÒ½8)Ì ¦ÑtÒq3þ†Ù/ˆâ½‹*øâ6Ëo!Š4©ÆHq§³%dÉYNaY³³“ư¢P(÷‹Z™›ýÑ9Õƒ†ØÜnþIžÎ9NËÂa‚[~ˆe£è;†óoáô¨íÀê­à¬x Ë3ˆŒšÝlö ø-Ór•.Å#OšHf< }ˆÏKšIé9Ì%Mg6Ÿ =¿ðˆOIÐ Ø záéÄ£ÛÌK›Ú¬ƒOݘùâoN†`A|¦8^†ÅìeÄ\ð–àá f+n4w)„ìn-žÈ#1°°!¨ÈÄPÐMéMÆÊš}~Ã'pú#LÏÀQ•Ï º6Ûµñ5æ@/¬eX_lG%$çÌ+ž=_,"›Y€¥±ss1çq¤· 3W ü†ÜÏorf‚/šž›ä$†Åcö;~ÖØ …B¹oXÌ*x+jê‚8ˆRrù ë:4€<”‰êxëà®ÄÒíéBÿÏ#y£9,_ )oÚ>b_$jðÉtU!`ÇÐq³uM‚’9 Ä«™>ol¾•æ€I‡8á>s¬Mïy$ò¨­ÁÅOqÑ'¾£`k°lÉm2,¸P]CÇpÒ{Ç?„g3ÊÐçO ÂÑ“˜¶=cö_Z$óâ “ÅÛ!÷RR‡³ûpö‚VtB¢€BCPëñð³”B–o_J´3Ð ×S)õef+ñ¥¬xl¡_8˺jôC·v½GÁ×£sÛç•9+¢¬Žó8ó1Z›¿ŒÞA¬~êöbG4˜øÕñÌTÂï¿9o …r×Y<*Ȫ(­2' ÎC4 XqÅ“°ðTC".ǃm/âà»8ò{³5ÏQ…uào’Æœ~` ¢9€ú 08D%L¿¿†ÁÂVŠ-߂Æå Y@×Û8c˜gË:à­Æ–'p`>ú-œ%‹“ÖYÔn@Ó0ŽýŽR³q©d6®}…·pòeh l%h¯3ÅÕ_ E^ȆZÞnÎ$X¶ ñ νЄ5›àÈ"[@h˜}ŸÛ`·ÜXÕæ¤‹†‡0F×;ð™£1—¯0gÍ×ï6ç*0¸½#;Ý-xè=€ϘN´|ÊJt£$ ÞŠõÏBÇÿ`²%¥±Šäœ1ï¨ôJi¿ð/lKnx øWb{ÇaÏ óÈÊ”ø"fúŠi$ÉÁ&®šX䆕8wÝ%pù Q¤P(÷˜Å£‚ÁUx®Õœf>9YTuâ;í°‡½X—à¡ÿh>9¢|MØY…|¾Ø÷&.Lw»-d¿ ˜Þext Ó¾p$Ù|I bó±.[œ´',L"¬ÞŽà:³õyA±+ j%výóts©QѺ©ØÌ~¸ífΨ3ç Š¦b‘\<û@´˜/‰.tþt’=¢£%Øú¬/Îäâ|AžÇªùKóæ|¾ë=É›§ßùëâ(Œ59ófɹ…Œ94æ bÇ0¦!«Ý„²ÕæÔD+®)…B¡Ü3îÌâô{Ȼѱñ+4?HOâ“ßÁµËW^K‡<µ{ß1C­{ ¶Ï) Ü¢wø T>†škÏw=c,õ–þ‰‹Ýs )3°§\ºÆ;Ò³€îqtàÆºgQ_·P,™IøÆìüs”¸¿úe( årß« ÙsÆ sh¥¡Çg ã;nš5o ¶ùalÑâèºéöÄPŽŸÚ¯Y(†å4ÀP7žn.[=oL/º%˜‹NŒœ†{×µipäø‰£øè¶ÿA2Œk§ßÃÚ0—\˜Ïo +—‰dÒߣ±ÎZcC9,YOV"a/Ç÷Ýr5±ÅØÒ²l핼ú÷Óåeó×(Íe¬ŒŽZ¨^h<‡ó,MïÓãaM9é!ó9ó!gγxÁœ9II&mÇñf‘ã§®•6‘7ËÊæ¢éÅYÖ?Yº’ê%le‹Øõ=“1LëYŠŸ"%ñ¶ sdóø?âk¦¿‰‹œ;I}-ÁT=neÏåÄ|Ä >®¥hË—±ó_9?ÀƱam‡h< ÝÛM$M/PS©­üúß9v˜ªyö,ÎÿŽ‚¥<³™ÓÿËoÿƒË=¬YÏ©w‰Œ²ì)FvÓÚÉ[ØôF9_KÏçÿ/DDäÞÍœ —±öòê^[¸kîC,Éžzt|ˆÖ#œjÁŸbQÑÙAæÃÖwŒ­ÛâO´*ŽÑX ŒØ½´xÒSh¿ÆH/ƒi~‡žØìõ±D2Òn&&>šLrðã/Ìiê uŸ+7›ðž˜IKŸUr¿nbÌ*TôwóþV[2ÛiÝÆçÙÄóÄ\* ,ûÊiifÖr’Fé¿HëA"'bFÉȱý=‰¤ÅVÄö8-Ô%Ô¦©ÉÉsSY¹p²¦Ä”8’rI1žb“ë/sµË:*# ¼ñoÖ:I^·9õê 3Øm;Ã9–¸…Ë™UÃÀqúH.#3Ën† ðÇÓ×[—ÇÉÁy¶èLJØnLŽŽM•?´¤Óî$fÚ!qd$Ñ£E´Ed:š9)è@ÅãT>Bw§öØ=¹ôB*o,nyå4öRöMÖ®d¬›žöý¸£±¤~CDFlðS G&Œ 1[è21ŸªgYXÛyÐÖmèµEc±Â×" |Ò•Ùâmã׬ìQ4–y× eOMɰª I¤óðß[[ÐyÒ¡IÙ–N¤}¸†ƒt °v¡^s¨|’• ìÀñAƃŒ]˜œÌp]R%³9Wk­áŒRfgÌOlVÉÍïÇ'Y\¥—±îoI§¿&Yi\Š=ê„–7ÞþJwòrÀB·ç4gÏXC6¢ç*ƒHµÒÎN_ÚcCl|“?þǾ ŽÁKvÈH¿-”ú‘µyDD¦ƒ™“‚ÝõÔ~ÀܵÖi™‹§ÙÚ7EÇp¾ $2álÕŠ ⟇ç=êâ-³B—lL0›¬dZp.×n_µœa¤”[úôA ÃD{©ÛCÞ ÒJÍÆ†”åØˆ•Ë‘;/Éi„M ÒÝI¸ˆÄ]Ö˜¸œ¾ië²À¾9:Ædv…Ýkln´Q-—ê9uže_·òC¹d½Éþ·H®°%­ ¤[£Öi#Æq|I‹(º}e2çlårü·œgýz‚wQ²=¹Èî#Ö_à|W£4Ô1+V+8Þw‚Žœ,gÖ|ü‡8úþsÖ±Ù|™M? r)ÍÛØÿ;ú+8³“áx*«H ~ÊÄMO<árÒýœÝÅ^‘f.\†Ü©Æ¢ˆÈ´1R01_².÷\aïïl†»ÓœZ°–’YS¿ŽSJ˜Wa¿²/e“’CÕCœn¥¯šÅ‹hø³ÝüsÒbv‰vˆ ³l=;ßbû¯¬_4y¶•GO.ä±o°ç=Þú• ñÏbQ>Ù…Ôœæø+´„I˜°B}·MªóUFÆ>¿ÉÚM¬]Ï‘:¶·<ž½Ž¥K¦:*fSñC¬ìåÄNZ÷Ø4ƒ¬EVóÏÈ¥¨’–”T‘Ä›Í#ß´kÛñk+KKˆÙø®‘–ëJ½~6çyç’áe(‚;ç¡û„óH½¾Ê¨×ªº§IÉçÑïÝʯX‡gV%sIð“=ŸÊÅ47Qw¯=ÃÆuûÙq K=ø æUA6‘q«Ù´ý0 <ú=V­¶q¹ aë;õÇbÞ—`]£IIö‚$eŽÚRÚ9U¬û:{ßáÀëvß´°‚~?w‘×""_°iŸ‚žVo±ŽÁ”$6¾Àà Ý-³Õ™Ó& _È`Ã?18`õ€ì¡(‹ûf⯦¦Ïª,Óˆ—nµ™ŠåÛÕ\µ%¤œ8‰ ´I]Gv Ã×,´‚É„b/þ>K,NRë'˜uËyÙ‹ùVc&ã-¤h5#cÖOJ#à¿¥Áä±»†+žgþ»Yè\|ÒÍ‹cñ·)}ФpìÌN~¬às‰ ÙQ$“,¿ýS˳ë'ŒÆ¾$P¹’ôÛ›‰Væi•ý´vÙÀ™Òbž|Ñf2ìÚFFϽh}¶Î¼¹ƒºZ f‘^ÉææÈ=ÉùK6Ÿoñ³ ðÚv¯Y͇@1Oü€ÑWimcÝzæ-">žöó4NPñKY°¥W“´Âú<牟12nó1ÞýùÕüÃ&›µ2ÇÖÿÁÉåìTDD¦™éŸ‚N #krÛŸluÏ?a¿;͸>ÀOøfq¾¥ <>’³¸¹ïä+I$q›@²}ÜqøMNž¥åÝøÂGJö;ÜzyNâ¦}t|džódÞRkÂùyCéöqë>é9“›#\¨§~‘LÖ®Àÿ‘æÕö·il%¶4=Ý$$ÐÖf7AëÛ-ž,àäI›Œè´D÷ì¡ €în­ê´ä ï2ÅÅœ=Ëè8íìÞMy9Grª•¬,šÎZÂy½vB_"-íìx›E‹hl¤ÿA{÷QTĹsœ>mayäýCäåÑÚʈ—«W©;ªUŸü‰ˆ| ¦} Ê”(£Who`0ž5›™“ugwè… ¤¦ò rõõcNö,_ΦM6Dèå—ùå/Y¼˜_´ß|“?ýɦ´¯[GI‰Eã[o±w/55üøÇ¶Ãÿ8¹ÿO~bãlkkÙ·Ï&H¬\ÉsÏY­['wøÙÏlÿW_å °gž!?ßÂoçÎÉþð‡¶ƒ“¸""ÓŒRpñÈdéó,Ýküè§á•mK¦9œf_E…mÄÅM~v¢ëÙg§F®>ù¤ ®¹¹ƒ‡[¶Ø†ïÆÍÏÍ›m›ûûÎÒ¥TWOíï÷ßy§ŸfãÆ©œ8üîwo;aÖ6½ˆÈ´¡œY<7æ'|ß-ÆÅ}âC»Ã_z½·×ßø´:yù—O("2ý|µ~OMŒ0pÕ–éo³ZkHóðÁÛt RùžKtôQóIwWë<:Ad€` £}Ô½Kú|ÊË4ÐQDä«ä«”‚Q:kÙÿ!üÍ„ ‰ãòì…ÂgHIgdøàÔ”ƒO=Ûp»_§ìòS­ÐEDdÚ™ö)áÃw€Ð:l᮹k(Ìgð G¬„'Þj/TV3r‘}/s¨™¸Tæ•2ÐË¥«x×q‘Ìnú/dàŠÕfî¡ñí¶‚ZÞ|*ª Ïp¹xÑÖ‘I-`þJF9¼ÛéñQ½”Ë$ [ë°ë$ GˆŸÈœ*EÙvˆ–.[¦ç£ –Q1÷#«š‰ˆÈt4ýSp‚¦w©ëä¡MÌ.æè›t’úïýš P¹z9ôúGXUmìœ6_Îâ®ñþ»Ì®!œE8ƒÜRûÜðï7S½’Ó[9ÚBñB+Z´÷wŒÅlæÐaŠ–‘x•ú׸2ÎãÛÀH¬|’ìÚAy.ù^¶ÿ_%Åô±¹íãß§ÿ¸·|øï((¥y;›Èü)¹éŸþ£‰ˆÈ—mÚ§àu9¥,Ù`k`Ž4p°•‹g8ÕÁ²ïY•ŸñÆÎrêK–‘3‹@?%ó;mGŇ,8ƒG(¬²ú‚×û3Gºh8AÞ[Oô*ùGlÉéP)+g‘ÿ¾zšhodü1f•J¤h!áØ0è(íÇqÏ?GAÃ\úN‘9nkÐ,Xc˜’{iý?‡¿Ä—JDDîÞŒHÁ8›Ež°QˆA?Œéf"«iàÃ"£€+çl›ñÜRƒþFÁ÷[+¿ôÚbÖiyv×T=+N»ŸÆ÷¨}Ãl?Fòƒ±šò781Nä‚ÍUHMµá‘Á4›ÎßÑck†Å§‘œh³ü|¶ºˆˆÌ3"¹ezxlðң±Qg#b‹‹zþâ‚›¬¦„ÏŠEØâÎvûpô2‡^#RÈ£HŠc×ò±Ó»=±š‚ã±’IÑXa÷ña‹=«§îÕÀ‘™h†¤àóIàL-„é ©‘YK­Ö×KtȪâù'>ñØ@69aÎ×ÑõžNÞ{Ô|+”6Ǧu_®g ‹á8+Èg­Àq®\&#¶ˆš'žôÒòI»}g§¥˜[:¹WûO2ù7ŠBLÕjðÄj æLŸLÞ-õ(f•MnÜ|Š”UâBdßö¤Np:“&ÚÈR™!¦} Šˆˆ|n”‚""â^JAq/¥ ˆˆ¸—RPDDÜK)(""î¥÷R Šˆˆ{)EDĽ”‚""â^JAq/¥ ˆˆ¸—RPDDÜK)(""î¥÷R Šˆˆ{)EDĽ”‚""â^JAq/¥ ˆˆ¸—RPDDÜK)(""î¥÷R Šˆˆ{)EDĽ”‚""â^JAq/¥ ˆˆ¸—RPDDÜK)(""î¥÷R Šˆˆ{)EDĽ”‚""â^JAq/¥ ˆˆ¸—RPDDÜK)(""î¥÷R Šˆˆ{)EDĽ”‚""â^JAq¯ûJÁh4ÚÞÞ~æÌ™¿ÖÕˆ¸ó&r>{<ž/ûBDfª®®®±±±Ï|ø}¥`$ùÅ/~ñÒK/ÝÏIDÜlbb BŸÏ÷e_ˆÈL弃ÚÚÚ>óá÷œ‚ãããÎûöæs;mÁÏüÜ"""EN*ŽŽ^ïb¹KŸ%÷z”ˆˆÈç-sO‡Üs &&&æääôöö:Û^¯W÷3DîÇõΕ¸¸8½•Dî‡ó>rÞMÉÉÉápøžÞM÷œ‚%%%?ÿùÏ;;;¼õûý÷z¸ˆÜjttÔy률ÈýpRpll, ÕÔÔ8VÞý÷œ‚©©©7n¼×£DDD¾Ÿo[P±ŠˆÈW†fÍ‹ˆˆ{)EDĽ”‚""â^JAq/¥ ˆˆ¸—RPDDÜK)(""î¥÷R Šˆˆ{)EDĽ”‚""â^ÿç*°’IEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/netconf-server.png0000664000175000017500000015077414770023131024075 0ustar vladimirvladimir‰PNG  IHDRnæEÁ•l pHYsÄÄ•+Ñ®IDATxœìÝ|ç7𺳽i‹V½÷F QESL16¸×8‰“K¿ä½{ÏIÞ$—ä’»œ'vìØ`cǦÚt0EDõÞ{—VÛË̼Ïj)d[Òb Äÿû±ù¬V;³ÏÎŽæ7ÿgfž!yž`¼È‰npoƒ(üQ ø¢ð D)àˆRÀ/¥€_ J¿@”~(üâW”z<ŽãxžÇ0ìN5øúù²Œ$IÇÇ:íø£Ôét=z´°°ÐívÓ4=Ž÷î(ÈPe¨V«/^;ÖÉÇ¥f³yÛôö£îi¨*Õh4_k”z<ž«Õ:î9wŠ¢ÆjãR’$ ‚÷äÀ]Çññåš_§¡4½þX,‡„„ÀùGî!‡£¹¹™eYßã;^yg.†Aï½páÂ_ýêW(PïÈ €¯Á™3g¾ÿýïwwwû~_AxÇ®+ …QQQ¥î!mmmþŸ9 C4~(üQ ø¢ð D)àˆRÀ/¥€_ J¿@”~(üQ ø¢ð D)àˆRÀ/¥€_ J¿@”~(üQ ø¢ŒŒçyŽãn}ÃpÌë¶£×ò·ÏÃÑËè—ü¿ü\Cï04áˆmåLp|„†zg9ô¿ïŸ[ßöê¿#L7|ú¡oþÚt#NË}’Û–ÁÈmü¼Oí]&ø˜æÐ»àŸÿFåêçý¼Ï,ø¢%Àý¢Œˆë+ÍÛ¾}ו6»Ãáô\ݲÁé {ò‘$-3|ÃÉsî–ò“[?ØßÐer:n–Ã0BÈ0bmäҵ煱‡·½{¤¨ÅépºÜž/OL4}ñº'6æªHA_ãù¿ýåÝŠö±eZ­Ua=ñÍÓƒnn'kéïjnj¨(-.¯ªmíè±ØœYâÚþ‹]´S¯|c±’Vè`„ZgLHˆP5—Ïß´@¾|}nlLltˆV.”á1ñ‰V¬âòÙ£{6šY Ǧ&H„·dJ·¥³¸¢Ë"OyøÑ\ôŒËÜxpû¶ <62HH×_î6÷”÷:xŒ%MKÓJèk¿âY·³»¡ºÎ³àñ§ß˜·ÛÒuþÓOv~¼ïè™Ân+lЪ¥"oæqwWkÃåSí­m=,IKÔ¡ë¿ñóW^^*%°á³õ5Û»sÛŽOò¯TZ yXX°V¥RÞ:ÑÔÛ^zþè;î—ÇÍË]±~ýÚÙÉ¡qýóaL@PxBJ7W]|òÈž£çÝÞg ñ‰^*â'ëµ 1|QÈUÚØ¸x7^]Z¿ÿ“SxôÂGœ¬WÑb&$26±Ÿ­*+ÚÿÉΊ^§@ OH0ª¥ø-QÆó{_EUKƒ5pÃS«Æ¹ OÝS»û½·¶lß[X7  ÔkÐBãÜSmÙñƦ>«G(2>ý¿ýé³Ë„)¸A”‚aŒ6bÑš°Þ}AgË/¨d»Õdï©Ø¾õͬi‘k³¢IÁÕM'†²1 nÅ#1ËYÛ©­Ââ õq+žû??\¤¾ÞÔœ5)óÙ®ó;Ú O4–YEò¨§üëUÓ#¨[£”¨=òƒïý>üY\‘³ö›?{ù!¼–7\×ùöýlp‰”±ÏÿëoWN »:+T »¬ÞÿÝß9?lœ¹µxÇ;¯ÿï”õ â2sž[ùàü¬éq‘!j™h(JýÝíµÕ…Î;¼ÿàÙºúæ^÷M cûê.lyýÿòÎÞæAᔜ¾ñðš³2"ƒtbš@oj3u×W8ðñ¶Ý{ßüýÅS§.½ü//¯Ë®ùÒ£ä†Ù+6e-÷”GQ—/4˜Î=hµ5ØöVÖŒ„'§ŠÈë c”á¹kCsΊã[JN•9ýëO×d”oaÆf¯ˆžµÌT{ÆTüiEo7! ZûüÏž~p sÓRó6ÚÑqöç?ûuçø×ÞcnÚö×_ýÇŸ>ê“Ä®~ü{W/L‰ SIÎeénk*ºpzßÎ÷þy ®ßêÿ›0)@”‚‘yâ.àHô¥™•³€n;¾ç\[ÛŃß2->æ;©Ú] W_Œ‘$éíHÄqïäêoqB0ô;_儌>$<22ŠÆo}S«´?F£6 ŠQ…F§ÅF„‰n¼˜#tjf(<1‚Vë AAAÃR™sM‰–l+ðý€*]WoÅ¿ýõÞÚÝîIJ×ûÇßûöâ)a 9¬¤éÀ0Y`XìŒù‹—.ˆs}ï·¨¶eotžòö®ŠÍ¯ýñÞÚÙb‘å¾ø£Ÿ}ïéì(íð*Ö…dèBR¦gO’ü»_þbÞ¶ß8ì¸ð?žÈMõuõý%p\€– FÊ2.4¸ >:Qo²¶;|ê¹5Yrä¶I0RöÈ3ÏU••ÿí`ioýÅwÞz3-ñç ´þ^¶2ü=fÖÃϧ¸ ¹h<æè³xûöKç…S—»§†hÅ7ï‡xw´ 7>ÍBám;<ï=l°¯«¥µÝdu‘ŒTg 2(E45t¡“÷иËéºÞŽMy<³—¹¯«©­WaˆÔ«„·Ì#H!uõã Wº=ïÂ(!íÛÑñ^»ÃzìVSGkKgŸ™Ãi…FdÔÉÅŒ·ïüj 9·ÓÅݘ³¯h:—÷LïÖn™1.Lg&ƒQ(£Geä>þtaqóŸ ZÏï~çÍÄ”ˆ¬Óù_’𜩻£Ç£ˆ0J(‰âzO"†“"ɘWQ }‰·kv°ëÒ•óufïæZŸ’¬ÆGª­nž©D×~d溋yƒÞÓjô±áQQº/­qZ• ÅLƒî¶’Så/$êÄ‚ÛßÃÕñó7=ûlAÍ/>«(;úÏ7ß›ó³'Â¥wðO#Åråx'&hIhR³¯Âféüäõ?ÉøÇZePR7-”^âÛ§E!ÕVyaß®]‡òη º¼ËUÀŒ21sÞCkÖΟ͸©£bÛ;[O—T÷ÚQŒ%.þ»ë³íÅÇí=r"¿fOH™­r4´õ å-&Ke2yÊÂÕ¬\b”༫?ÿà®{óû¬Ó •'D!‘iëžØ”Nã§¹íÔÁw}|¨¸¡Ë×YÏñ¤16mѲի˜4ÔQ`é©Ù¹ykÞ•Š³`±‹ŸúÞcK[òýŸ¿òØÜh™:hÆœù¾û7ÿ³§¡­ÁÂ5­‡ò9WR×gubšØÙe,Ô¼õêïö^l$Ùëž]»zUjJ²Aì]>¥H1Ÿ”jóvo~u÷ñ€Åß||é¢äØ ãýµïüúßþòÁÁv>tùúÕ³Ò¢÷@éÙ#íøÇ™üsµ­?þÉKkôbB¢œ–5ã{ÿ§wÊÛlÑnc`þ©#‡ó kz-L¾È»À¨@”‚±À0F—°éùgÊÊ*¶äÕuUœzç­ÍÓb¾Ÿ©KJñÛíq_ŸÄÝÛ\^q±šš>Ö¡¾g³ô6·Y‡Ë‚TÑ&‰Ûiªm³u"R*©Z>ªúk”ŒH(8\v{G³‰E» ŸóJZºú‰g .½¶¯h°éâo¿9+1pIj° ySýÉ?ÿù2¿ŸœnŒ÷\0ŒÏ\ùÜ'»þòQY§ÃÔQwøÃ7Nìߟœ1}ÆÌ9óæÏœ’f `n ~žwt•¿ûêÿññYÕ”5?úÍnÌŠ¤pïwž“¡7¿üïŸ~¶ãï¯GO‹ýqJ <%{Q ²{Ë»;ºûê/œÈã£"æ=¼èq¹úä»{KÔÁq¬žïh¾|®hK§Ë馔)™Y‰Úk—#c¸\1],,;ò:fý›Vä¤ ï6çïzûµ-»k¸ð~úÊO_X¡÷vlð¶Ù¤µç¿?:÷öëoǧOy&'Ф¤‰³˜¶¸£¢ÕÚ\œŠŸùÐO1ØÎlÝ}~¤¥ÀÈ JÁØ`aH}àÉç+‹«~y±ÝQ°wËë©I1?Ü0ú³y=Ξ“{¶¹«¯G›½­ø|Q37õζ”w:{-C½{¤X"–’c(Öcïµx¼Ãå$ÍHGWÕb#Þ.DÎÃZûm_0ò!Z˜Êˆì§^|¦¨ôgy ¶ê»ÞØšþ\ÄèÇ;à9§ÝfµXØa µ”×vw™üÝ/Á0R¬[ý'Taooþàda­ÝÍ»Ì=EgŽ”\ÈÛùÁ»ñÙ+ZÿðšÅ‘ÑõQsj/Øqä’Ö¬xpêôµkk#§å.Éx÷lU~YþÁÓÕ'F ]‘4ô[œ Š_ð£_~wJ”^,¢ëÓÎÍ:… Uss—fo?°« »*ÿȉ‚±‹b‡õ ð}…G>½±&wz4ª“1Œ·tìܾ«¢Ëœ“³~õBýÕ®L¢O^¾hÞÇûϖלþô³ÓeG0ÃWL•ýÝW~23Î  ­­™¬óu…œös‚ûD)3 eæ®Û˜Ÿ_ÿÖÁ^GËþ÷Þ˜š–ðì²téè:&––½ïÿíÓaÛ1žóô:“îh”òÞÑj]Nßõ¡A’㌇÷^34 Ç?÷ÔÝ[x/!õ½”çY–ýâHÃ0*iÞšg?_û§í̓݇Þ{#1!ᇛæ*F÷§É¹:÷¾û§†“Úa;2\OuAmû ¦=q@Ôºç~˜‘™}àà¡cGó®”ÕöZ.WgÙCÍEÎT÷ûßzrz¸Â÷å³NsùÉãõ½F®‹ËH ë uB”†â&SKaU·gv9l4CŒÑ´R±÷<#Eè”o}WêVá, qî²…Ùyå÷µ—ï?prÕ¬ˆÙµ„cí%§Ÿo.{|q| xè\&®­ðÂ¥âZ·€MM×Io¤Æ©Ðð`}]n,¯n°¸áðOŠ1!FLâS˜òüË/Ûåapb2%ˆR0v&ÒD?öÜS¥¥eoåÕ÷VŸÛüÖ?’£ÿ='N6š©%ê„ïüò—+gD] 7ÞZú¿ÿcÝm£÷¢kp(ÑF1bíí3ÁÉ«ƒ9 \ö¸F±ëöðÜÕéé/‹p £eA=ù쥂²?ï)°µo{çïé QM׿½xÎÑÝV_jnþ¤½¯Ëæ}Þ$câ=—‘Çe.‰NŸýèS­•Å—>Ý¿ïØ©ó%5&kí­Ûñ¿¿à9ò÷¯¼*õ.)·³¯°¸Íîäi‚²·W^¾d¿¾ËĹ­õf†c¬ËÓ×nf½Åþð²ðÆC´Ôq©¾Ç„H?gÙ²äÝ'òjMNì=Y¹ìÑ©FßWâê+ÿôÈ),|ÆâE3¤¤ïÄ]G]]Kk—K `psWiá厧ó½Mímñ\¦N³ÃénŠÒ›@І˜”;²Á}¢Œ †¤<ðÔ åWª~{¥ÝZxtû[ÿœýƒµ£›– ‹OJJо>Ú‘'H·kGË=Rê=çS¨Ð J¹S p[ÍV³%áûx JèìóØífoûåÅ-ï±›ínïi§MÈ$_><†K"f?ýüSEå¿8QÓWsþÐÛ[§%F>3š¶¢°'~ø‡o­Évñ([µç¿û‡ŽbêÑÃZ¢‰ÑELŸ÷ÀSõ¥Ç÷nßòþöS¥-¬«÷ø¾íÇÖ.{|fÊRÖÑßÜgG»-Žöê7þ½o^\û C ’J¥46ÚÛÛ`Þúrn³u§{*Î9’¿,m­Í–sW>þYaçôG0+RqõmXW÷@ŸÙ{¶ãÜÎ7^øôÝáoÏyœƒf½»ˆ"n¿1þ€(ã„‘’鹞¸p¥ù»­{ßy#1&<“Ϭ©qêÌÅsÀ8g~>\* –jûssoÍÍËÇxæ%TÄDÈÉs—ÇÝcî39¸æK“‘3wõ;lNôH(’‡*GsO §“¬úÉâúÿz§ÁÔûéÖ·þ»!n4›{\,•)”ÊaòɱÆíØÏ²úÒVb8A‰¤ÊÈä¬ð„ô”Xã+ÿñ›¥ýý­åŸ]lzxz(úú8·Ýáq£v«Œ‰/ýßÿ³8>p¤^q\”<Úcë&T‡/É]²cßùKÝí')Ú;;BÊ[ÛŽ;ÚDÆ=¿d†æúé`œÇå²³-›·ö…—a| ïÛËBâ%ã] Œ¢ŒŸP¹á©g‹ŠÊ·«0·^üçæ7ìSÕãéG%ÕK7~cǨÅÞÒwwLß-KÇ0j}TJ,×ì8j*zzæö[§ÝÊw|÷^ÉQŒ,*3I²³Äæq·vv¶õ8’”_rÍ>Ϲ[:,Þë(p™:.9x´v’Ãò O^¹TøÚî‹Îþ²[þ&~hЇÇŸ(&ŸùÄ“˜6B5æI‡ã½Ã%pÞá€oû"¼}ç’Œ¥?våô¹ºOú\®Ž†n÷P÷7FÞÛ£¢©12,}æœÌPÿðîgÌË]ÝM\Öžï¿×i˜¹fùLÉH$ɨ’Ò"ÄbaŸÙû£ï['h™Zäílv±ÖîÇêEÅ}jîÒ%Û–Ô˜šíÞÿèÜ öЬÆg焪†­$%ËQnæìݦAç¸úHˆRàŒNY²ñÙ’âú?ìètZê«+ ±ÚŸòœ£àÐ?ÿ{[ÇOþ­ ×øÕ6\˜<é²ùW|\lj/údßg9i!Æ/>‰ç\ çöüó(;ešÀ{LPâÌEsSÎ6žru?ufݼ(ƒ˜ü¼€GmoÍ•ü³Wl£‰_–;ǨÃÞ†Sqó7>÷|eÙÿ}³Éjo,-Âé;söÐ8p[Å÷óB¹9 gH>§g=,ÏóMjC5¾Ñ˜I¡:.HJ“»ÍR_ÙêZ;þ›Ž‡3És–ÎKþ¨þtCõ¹}Ÿì7¶º M_¶xvâðQ 1L¬ ÐH]fOKuË€Å(úò~üQ üä=”õÐãOŸ>[ºåxÇò,×ÏÅ;MÕe½z’ðåÄ$Æ)ë6>~¦ü'+º>ýè½í³Ò^X–þûsk]i“ͦïn 1Z½jÃ#Ÿ]¬ø´¬÷ôþÝÇrg¬ŸOxœç}ûwmû¬¤K€+²W=úÐÂ4Ñþâ"í¢5­9uù¯Ÿœw~Ù…4_5޵V7Ô·ô:Â$âÛ?†ÇÑ_ZPo³¹dúðiA¾eB2Ê”™Iн—-澂ÓyeÏœjadÁ±ÃT¡)Ë—ÏÞ©¹½½ôýÿþo«‹ßð¯‹Rô7Ò‹Sa‰±¡ÊòÒŽâ3gJ¢çÇûÓ³À(A”a¡‰]ðÒsTUü&¿Íî×¼P™zê+*L CÜr#˜ôe›¾ÓÔeýÃß/Õ{í÷¿ÁÜ?Ø´tŠêö¡xïøê-egO–¶8©Àkcž{+Å„¹k^øFS÷þVPtäÕW£Ä¢o/ŸNáØðM4šÚinßÿÑ[Û¼»ÃÁLY¾é;Ï­¿>4ÏèŒaаé/<ûXEyÍÁŠ>¿>üÀ·]Øóɱ°M‹‚•̰Ïâ ¾úô®í‡Î¸˜¹ó–ÏM ö•ê%ŽÍ^>=æHë喂þöFìϾ½6B%¼~ÄÔ»˜Y#¼ã)ŒiÙŒjÖ¢ÙÞ~¥«ºº&dæÆÜ¹)·í©àêø¬Åsgœ¬84Ðpqó;[Ãt/ÍO47ØòÇñ<†wô–Fà¾Q F†6x.—Ëív˜íÞk \ë Å‚Ó$-Žpž-Žëñ§‹Kë_ÝÞyëè>¼Çåt¹]f³åêÀ⬭©º¼Xf»ý #zÏŽŠü‹Å]TŒ”&o|žçv`÷ö½Çšlªùë×}gèãÃòƒs»œn·Óluð¼÷þef³YÄPMSÄm52FÇælxîÙ²Šÿ÷fãm{&ëv¹œó ëê=½9›-Z OAQqï-C2Œ³íòë¿ýimñéÜ…s§§ÆÈIÞ3ÐÕxéôá÷Þ~ïhµ;uñãß~é‰xíÕ õ†•4ÿ™'ªëÞ\Ü\õák?ïm¸873-T§Äy—Å4ÐÓÝÍ+×­Ï U‹XÖãt8}—¥ µÄî@‹ˆ"ÉÏù¡MÌÎ]œ}¤x—…TÏXº<-L}ûëhyØÊ*nøäLÕ©^ûq[Õò%sâÂ¥4î°™ûzºº­Ò9¬˜›L],ìt:})<ë²Û.·w-ºƒËÜ' JÁˆx[{åžÝ»N_)-:s²¥§íà0Õå%ħæ®[;3FûÑBZºêѧ.”oþ¬mø|X‡éìž÷÷.(-,8]gCOÙªþú‹Ù2R©·bqYÚÛ¢¦2ä̓»ò¬»éÜþÝy—jêê«+ÊÎ7y¯6±ö•þõ×?΋ŒŠˆŒž1gé’yI#Ã(©!çÑï$L™¹ï“Ý»ö?²õ/Çv¼ÍEŒˆR˜Ónw:»ÝbµâBYDúìì¬Ìéð‘x0¡2dÅ3?MÍÚµíŸûŽžyç¿K¶½!Ò¤w%΃ö8ÜN»ÍÉ%.||í†5ËÆ*†]¼«¯ùðîŽ](.¾p¶²£§u×_¾Óu1!>iáŠÕ Ò#¨ÛvM‘vÑÚ'½Rüç]…7/WsÑÁmŸœ**,:QÑ/ðÞ »qÛ¿®ÍO MšºtÙ‚@ɹÅ(R—¬\ÔÏ×´žÜùfþþ¤ô‘Ñþ÷îiVË ÏV=ýÝ'Ÿy"'=lØGÀF“ûÔOÝBí?6t¡¢ñÀ»¯Ù.“ˆh‚¤F$ ›½"íöpξ3‡–·¯¾Ó‚¾öžÊ£o½!ÌH‰OŸ½0%P+¬:µ»èÔ…T„¾*!Èñ3–Íe¼}üœÛtîðᓟ}RÙ<À ø¾š“oÿ]9%5!5{aF°ÒŒ D)ï¶[X§6Fe¯ ›µÂ{iŠ·îCO[­,' oï|Åð€„9/}ÿ§a™5¡Yá×Oa½IÅ †Ô¬EI3rFsð½U`ú,ÕÍwÙäQ(;¬€Ô#ºÐ)³—ð×^Œêãœ×F yž„00aö“+7~£¦²¼²¦®­³Ïæô Â—`BF¢Pkt†Àа°à@•B.ßZ,BiBÖÒÈ´ìǾÙXQZZÓÐÜÕkryߣÅr]`pt\B\L„^­`¨[‹cÝêv³r}،ܠi‹ÑÂÄP®Ãjuó‚‘¢“‡f<óòUq…ÒŒx1uýºI—Ùæp’ðÄ©›bÓ‡-T{¬v«ÍÍ FœßØBEöã?I^ùB{S]uM}s[{oÿ Ã;ŒÉ !Iii Ñá9s{W-3¬|ò_¦Î]VXp¥¢¶eÀêä˜XŸ––b1”­°³«—“'<ñbÔÐe4$C9ÛZš Ý}‰ñÈݯœ‘ûšÛäq9_pCxœŽžûè¿ÇÏ^[x¥°¬ª½wÐ{M‘PbŽHLMEmÖ*PR ìvsWw[½áÙï¼D2BwGk£¶«79P*„M# X_Àˆpetæ¦ïfŽiŒ”¤/z$}ÑMÏÑŠÀ޼Øÿ‘¢øœ ñ9㟠0Z$ՇƠÿ²ÇÕ Ì;R½2,ý—–;–é$Á‰k¾‘¸fLïE0Ñ3WþhæÊ›ž¤¤ÉÙ'geFã†>­P¬Ö¡ÿ‚’¦Íë´$# K˜Šþ[ù9/‘¨#Ö>ÿí±¶‰Q†­ûæ¾ôu8A©#g£ÿ–~îkDŠUOsÕ[Àˆ J¿@”~(üQ ø¢ð D)àˆRÀ/¥€_ J¿@”~(üQ ø¢ð D)àˆRÀ/¥€_ J¿@”~(üQ ø¢ð D)àˆRÀ/¥€_ J¿@”~(üQ ø¢ð D)àˆRÀ/¥€_î®(u8555‹y¢2‘T*udd$EQÝ£rwEiCcýkoþÑìî¤è»«a_§Ý¨ŠþÞË? 2Mt[ŒÊÝ•X‡ÝälWÇÙÄrz¢Û21{­mg¢`´î®(EpB 迉nÈÄ ܃cÝ cp×E)po(üQ ø¢ð D)àˆRÕj­©­³;œÝ #‰¢£"ÅbÑD7äQ #¨©©}íï»iq_=æq»pçàKO?2-#u¢Ûr€(€ØívV¨Š™³A$ULt[&€Í2P~â#‹Õ6Ñ ¹7L®(åyÝÕVÞÓP5hñàº(ut²J!#ÜGKIw}“S®ŽˆWªæ}-×SÚYVjѦãã%×ÇEpš¬Me}-5fN$ŽÍ b<ÛÛ8ÐXÖoᨠ„€p1…±ý̓íMV‹…åx^À 0Š*¢äB X`’ i¡X®º?£T€a$%œèFÜ3&W”¢ï^D„Êzjº/· tšd ž¤$4æpº8<À –K}¡ÉóNGCiOÅñî—08J,£1_Ò¡JN–4ö5¶v›xR¾Â(âRµˆÆ{œNF©cHÛt¦éÒùAEL@t’F&ÆQ~wV÷Ô\è3„K…Ô}:NÜ·&W” ¼i* ÅÌ26UU·—twg(‚µ¤Çdéìá Éú †¼š¤{—ÍIŠg*JÚ; ²Z0ôŒ$¤jF¦–yj/·I23•´”–)È~‚–Jq[sWA^:%ǨcÏ‹ 9î×Aøà¾6 ·ý(Û¤ÁêôYGöv—^Tk¨: {2YRœ„¼ÖËslo«…T+"™Æ²æ¦sxZHÜè›Å…tÄ 7X[þY«>PrmAñ\wUG/Ÿ¾R¥ã¾J½£P&ŽÎ`èûuè`¸ŸMÂ(E0œÎ4&7^9Ó¦¹ìílH¶J:ì,<ÍÙÛáR¦éULPHK}Ù€yš’–ذ”Z‘¾ °÷£¦Âü.̓†«Ï²ì@ŸÓÍQJ5yýż÷p)&C¼Éúu}PorF©·›V&›ÔTW}é;nQDQ8,áx{µÇ$g݃fTŸŠúÚ:52‘@0,1\oHe=y¼µPÏ„0¾Iy–ãЯHâzË9ìý½N§‹*Eˆ¦¥pÿ˜¬Qê­ áªèa{-™¨dÈéÆslOÍ€ÅŶuµc7‹s.GS¹9.J$¼91ŠŠÌ ìl¬:Ù슉dœ1Î;mï©»ØÐ©Àn»«ñTSQ¹#ymœRÃà$^¸ŸLÞ(EAHà"‰Ó$ƒ²mXº±fk·Oy ,2˜Æ1ŒµZ…–ÒÊšþNS@ˆŠ¸)1L O›صµ®ì’#)Gƒ¢T&•‰:lñ¡B •º!”ë´B¬Ü©2ŠÅ þuNj2Géˆxžl2ÛyJ­¦|]¾8# I^Ù7Ø\c š&%n)L1\˜1ßÚ½­“å¼u¨2F›˜a®¼Ò^(Œ“„·Ìu:¼¿pš¬QÊ{,Žöª¾ÖvátÔvãÉjÞ;Ø‚©±¿ôJ_CØÛn—…‹)B`ï± rëlºÜ¦‘õxGEO›•.ë§«T2#ɰ©Æ¤:6t‚.)'/§ò;»®´[ê(¥œ‡5[°°©zµÎà€ûÎdRŒ Õꩊt#iœ‘y`¢S¢•%-ŽLÄpZ"ôž:„ „JqDV„qz8†c´ˆ–H0CŠA§'hB,ŠF jäÙc]8EcC—¾¨dɹbû Ëéâ¼ÇK9§ ¡˜2¥pß™¬Qê=P*Q‰$·=MI…jéM£aBR®»i9Èn_,-ÑÃ~ÆIB¢¾}þpÇðœÇãrz<,%–]¿,þ¶—8ÝN+ Eb.Ä›(“6JàžÆó¼£¿¡*o[áùªÔ§þ#%.Œ¸-*9·µ§êÔ¹ýÛº±ÌÏ=®UŠ'¤©¢ôîÅq6d¢˜èoŸQ…µÛ>¶¸¹_ƒS’€¨4µtgiIËé¢ô.Ųluu5ŽãƒA&“A p¯áÝæ“ÉJ(ô¤cÀáp"¥T"t˜ûN.T)Ô$Žñ¬Ó:ÐípºQ,ÒRT&”眃ƒýý޳˜Ù¡ˆds'G)j ï2™ûú8B®Ô”˜¦oŒåƳ.k§ÝéÁZ¢dè‘û…ÁQz—òx<µµµEEE$IO:522’ ÈTî œËÒ]väЇ¯™µó#U|w]A?“1…´4¶×šÈèEϾ’)k=·õü…‘R-pöZÄi+Ÿ‹‹2°æº‹»ÿ^×%Pi5\OyŸÅƒæg문ôþ¿Õ0‹V<ý’°· ËŸ»˜¬Ußy9€¹ñ¦µp«ÇfìjlègŦ‰iSGF&æ— ôt6Wš™ŒÈèpßY»ØM³ÜôÌp‹Ck~g‹C-ð˜íÍç[Jë=)+"ƒwõÙöú.ý¡ˆÃðí5ï1[ËÏtvv»yc4²¤9‘Õ\z®½=Ë d!ÚŒY*!ùU…ŠI’$¥Rill,ÊN‹ÅÒØØX^^~ìØ±ƒ¢ÍªSQ «R©&q ¢Ý   YNNŒD"üòWƒ±°ÛÝ­­œ@—*Žç²t5ÔØì"®£²µÎHö—ÚH‘§·¦µ¦ë.t¼»fÀ“‘õè3O?¹µˆç8<|å‚ÒBfƆ¬Žâ“oï/ÔH=”FÚU[ÜbH[yesÑ'íMHÐ*ÜÕÚPzÅ#3w÷šÞÝPU!IO›³é%~ïÞÓ[ÿŸH®3&ÎKŸ·¢ôk0I±D#VȸKåŽÈ䃎®8ÔTzž›ºÄ«¤KN´•œë!ºN6—7᩹!)[²éÒá&ÑÃQ:ÂviOã`@@fŽ^Ì9®´ô›1)c”*Âr¦ßê0Q€’,mís²7½#*H›.´WÔ°©KƒÕnó¹­WÄ´ÞÜS×Ne- ºì=ý__G+ KT†¢z4..Îl6·´´”••9r%V«ÍÈÈ@qË0 î-Î'm¬pWñžÁ›²â¡¨…F3b‰À˜˜Ë´P,DDoL^‹‘ŒH&'\>×íA{-è ©¢?RJ‘ùð¿¥Øl4Ã{ì<ΈeRBõàêg¹Ýh6 Á?†*\Z¢¤1—:dÊ|£Ä!Ã`)k–G.r¹<Þd¡„3_ÞVà·É¥¨@#N+D†X•^¹+»Í½T`œZC{ªû¯´¸\{C™™ 38/¹PÐÕÒä‰MM­|âÎ ä-œ¯ƒÅ EC÷õöž*@ uóÞ‚w9[«ÍL)—¤›¦h¶­Á.g<ƒ]žþgh˜$LKP_ûQKŠ¢ÔCRRRQ‘ZRR²ÿþÇ£4MHH D¡‹2õën÷ŒʤBÙÕŸhš+®>¦´èÚcÅÈ4·Æ†ŒLÊ\›V ¿ö€ɵ¢k?\›Š–ÐÒaÓŽ4Cð›QzÌ{ ‘÷–/üÐc=æyÎétpBM å#%¡ 7ïp»vA‹%Þ§ì—ÎzÞí¶Z¼×T×\hG‰Iª‘!’0½Ðfn»òIe±\›œ’¡NÐF;¶ …bæÌ™S§Nhhh(..Þ¾};ªMCBBRSS###…Bè€;`FéÈH‚&1ÎÍr(h<çæ8B@28N8ËzÜÜHçÄ}ÑQ"T­’4¥SgäÅ׈rv†AÕhª9ÛZ˜×ª—Fh¿²ƒ¥£ƒêT­V0eÊ”¾¾>ß…4;wî”H$qCt:X,†:Æí~‰RBć25}æ[€†áz-¸B¬ aĬLMv5Wö‡…P´Ýít_O\ˆ¢…3u;l«Ùå¹m(iL(Ôéé¦ú–6M¤‘xOÛå;êy¹,4IG9¬ yV§ën9e©A LÍÉÉ™={vooommmaaa~~>z2&&&!!!((ˆüŠÎ9€ImrD)ï4Ù{º=œÙÑÝh¡•lWŸÛiÁ:›,„ØÝ×çâœø€I‘8pª·øp£TÈ tqq3ƒBõ”S¤ÌVwæ[,rnáxkµ»× “ºóšÜ-R¡ÝÉ <]f­éj°ºYwOóà Q–ihëk+Ú[Ûªg$jQH¢ÒÑ=ØXÐß@;ûœÁqš@5q·å’÷¾åBa`` Á`˜>}zOOOqqqUUUAA\.GŠêTµZ}øã39¢T@ËEÑ âCXŒ‘1b!Ÿ°".šÇ#¡¸ô•±ITÈÅâ,Ü<èxO®£ä!éíÔÆ,ˆ2¤:P=JIz¦ÞáÁ¥*JD*²N4³E0""~® ch©œ SƒŒ6PbJ,"épí‚M²A“¤„’µò €˜¡›!aZ‰š1wi¯©oTBß J*U;;;ËËË/^¼xøðá°°°ÔÔTTªÞo>€ûMss³Ûí‰Dèoý‹ö2ïÔ$Ϲ­½­‹Q+2Ø/ô&G”b(•7.úTÝx¬6Þ.D(c®ŸOwA“ÊÀç¿]?UŽQ‰ÕÕÇ2åÕ3t(!9|B¹H+ö³D¨¹/pD[߀YYY(SKKKÏ;wòäI•Jå»ÀF¡P@ß/˜d¬VëÖ­[wïÞ­T*uÃh¯4›½§Q°£:‘ç­ŸÖ·Ñi9ód ÖSòñ…³%<'ϘùÀ2Ñ(öHyÞmj<_r¥=nþJ‰?mn[g[ùÅ–ºŽà¹kBtJþr¿z“#JÁ€ PÙ¨¨(»ÝÞÐÐPVV†Õ©è™””·¾Lè–pP¥ÑhJJJP¦úžA‰D.—ûþpwÑcÆR±|ãœðîŽÂƒù—Õѳ²%|å±íÝâV®Ëqš]£=óg›/œùäJ@Æ ŠR?>A 1scÉÇo»¢æi•pÃÒ¯lÁ­ÐVC,'&&ÆÆÆ¢­Lkk+ÚÜ9r„ã8´«ž––†êT´ÅñõOtc3ß¶(8}C—\R–e‡ [­V§¦¥© áR•v4ë9†ÓJ•V(¤IÍP[Óâfy¡"4nöò ²ôŽƒ(_íÆ¢íKfffFFÆÀÀ@SSSiiéîÝ»}'£:5::úž²Àâv»{{{»ººÚÛÛÑ¿(DÑ ŒÒT"‘ A´Þ¢µ÷øñã¾Ó4=mÚ´‡~xÙ²eQQQçÏ_`µ£}'ŒP§­Êä#àB“µÚPYLVúœ*9VñÉŸÎÖ‰¦®x,H:Pvðíã;º–=ûm§òôû¯Z‚rgÌYÌv^Îÿä¯Vò¥ÙÓôS%Òk§zðÔ±Ê ¦pB¯6„`ý—JÏ\fRÿ%!%‰Áy6-/ÿÝ“µµk1þ\e…3kù‚ÈpA ¤åÜ?Μ?™5g†NyãQ¡Ù?¨úD+$ÊB”—aaa)))hEEÁ‰ÊÐÛ¯–FO†ÉÍÍE!𙙉ž¹³díÝf—N¦÷ŽMŠaŒ:P`±›;{íLà ¦’ŠeÞ㜔X¢ÖÙËz]N—@0üÞeiéqŠÂTFÒû:R¬Kqîî–¦V"’Þ¬B‰.ØÝÓoè5 ´¸¤)r‰XpýSz샦ÖÎ6sá×+HÌ1 2Âñ˜¯ÂÝ¥hUsÛmµF|w5ìkctIœ÷Rþ L5 :nÚ´i½½½eeehã…5!!mÈ„B!O~â8ŽeYǃªL“Éä+:;::Pe)¸v_ŸóçÏG»qJ¥¥)á½gð]Ä…~µxñ⤤$´öªÕê¯b-ņÂqÎãùŒç88í«¡Ý8œ …”ã8²2ä«ë>¡$¡áš–î ‹s¾’röÔÕPº]D Ü‘`ŸjoiNˆÕaƒ­]Ñ E’[nè‚1ò¨­­ôxÇŒ½‚ðn^@1ʘ`…½§¥ÊÅE“öÞ¶Ê:Eø¹Þ ÐE2æâ¦†z­2Úã°z'¥º`ÊVÖÑÙ¤Ãy–Ãà(éWâîŠR&àñO¡ÝωnÈDBûÑ÷ô=[hšö ø0sæÌ®®®òòò .œ{ú’¢ÒÂ'.sƒM&ÉÜY‹–)D7 5Šš&Ò&d>ôpÞ¼CkÖ%ê°¨©‹‚ ©3̹\|8o{+åêh³ÆÎX´B§«f<˜R]{i×ÿtʼnÙn³ÛÚV^957¹®þÒ¶?¶_Š–)õ!ió£¢#©»nHÓ{ÞÝ¥¾+'ºà@õtHDDDNNN}}}ii)ÊÔÏ>û (PÃÃÃÑqÄl6›Éd2 Ð-<¹¡øD1i200ÐÑÑ*Φ¦&«ÕŠÖFƒöº"##}c¡µåÞèÕÀH±>á_äi)/•¤H?åÑå’0SY¹Kh0>´Ê͸¢ÅœºLk°yx\ª6<wÙn 8 £dó^TDdw¶wyxL(3(•2œ¦c[SÒÛg`1QsbŒ!F ÇÈ Y žÖÆÔ×:<„X¥ÈXà¦ôšˆ¤EÏ…´Ô×ÙR Ó’ðWõ¸»¢L>¾ƒX‰‰‰qqq¨Âhkk«¨¨@EêÑ£GU*UZZZrr2Ún^/Ðæ5??ÿƒ>X±bÅ<À0pãI}¿‡¥&ª8›››ûûû}×z¢@©TFô¥ U}õB¡ðž] Ã…õ 2–Ã-}Žâ£ÕaOGfÁáà€C©iãƒxᘺ¶”²†Iäk^À86~çøÖùQ
̀¨›Ÿ¥eúØYú[^Šb}\¬>îÖ61±1ÚØ;þIÁp÷Øj î]¨NõÓJHH@›NT¤äåå¡XE*zU«2™ŒeY¥ï¾ûîéÓ§Q²nÝ:TžBgï½¥&ú6N'úº-Kww·¯Ï è·¾¤D¥'úÞ}¨ ?®qï~ãºâ³ÒöÆòuß@ш2móoÝby_tjû´²–ÚüƒÌÀV§LY¹êYV(ŠÙ·9ùƒ?q$i6F4Î_-¸g?5€(_+ß&Õ©Ó¦MËÈÈ0™L%%%;wîD[ÒˆˆTª~öÙg§ªªê׿þummí‹/¾ˆ*Zèì½'øâ³···{HWWWß›Í&‘HPj&&&êõz´G%2¾+8ïf}Ñ)½qéìÐIåë¿)k«GC¢Ñ¿æè†œ5¤ÃfWi=ï}1ªV=+o®¹þpï‚(Õ©jµegJJÊàà`EE…ï¤ßK—.ù^ÐÑÑñúë¯××׿üòËYYY(€'Ùf÷^ÇqœËår»Ý¨îDñé+:Q‚¢gÈ!2™ íÍ™3¾3lñ!“ø{ôˆoÜfŠ'ˆ[2Ò¦5ÞòzÑÉ¢L0߀(SQX¢L-((p:׋¶Ñ{öìAè—^zé‘GAuÌ$Þ ßý|£"X,“É400௹¹¹³³}‰!!!áááñññZ­…舣"0)A”‚»ªlJKKQ|m»QúÊ+¯TWW?÷Üs‘‘‘ÐÙû5³Ûí¾Ë:¯Šà»`I$ † F…BAÓ´¯½§ã•ÔhƒÁ/Á˜@”‚»Hee%ªu4ÃF¨ð]nˆ JèwÞAÅЋ/¾˜––vOo¬ï-hÉòÉ'hoF§ÓFEEéõzÕßM='ºwÚ;}útll,ª°'º-à^Q î"höË_þÒét²,ë{Æ3Ä÷e*JVç»ê"Îe»r²¢¤‹œ½0>RG&äyŽí¨n8žßeœ–¤ îâ!hÄbñÒ¥K.\ˆjPzÈ$‹ÏëÐÚU\\Œöؾõ­oA”‚1(w‘ˆ!ÝŠ±âmÍ ÿøýö·/Kùå·Ö†2_4úè#»Ýî»ò ݴΊu8žò¥ Jð`|êûkVØq½A:º(àBÑÜu9qóìR­\>t£´ÁꮨúÝ/N'?¹ìåµbú«m2¸ Zøuuuï¿ÿ~KK‹D"1™L‚¡rÜÒ×YuáSš¹Ãw^%l¨#Ÿ 1ÉvkSuqi‘pþìY÷ô¨à_ˆRüÀó¬ÛQv©¹Ãâà4KPáz†µ™Î]h³¹y‚’¤g\]ý Íf‡@¢Q&&è"\ÀyZ+[+[¬œ÷bÓÓ”çi©hüøÃsG/µãÉ5Gäcd`j¤‚€âôkʯ={öœ8qÂ7®!ŠRô ,,4gVz·iP œV˜÷,bVÀNÈ» D‚ yÓ»»;;::BCC¡0ýb¥øÅes´Öµ¼ûVþÁJò¿ÛøÃ¡›­êBÉ?Þ>_kU¬Ý”àií.)íll±zãó?Zö­ ±2ŒèîÏÛvöýÝu«VlùŸYÖZ~©úðÞêÎ^weAÍ>sOÚ**L!Ÿœç÷Ü]Pjž;wnË–-¾bÔãñ  u:ÝcÖð7nþyßaYvËæÍ%%%ÁÁÁpuЃ(À&Rª–¬ŸÖtºt_ရõnvÅZÆG¦+(.ä£s™)Â=EGξò‹oo Z07|f¨0yN²»³sßÎ »“óN.—ÏÌM/=Xx²Å½dÃü—V‹E´„šà:ÀårwuuÙív‘H4±-ùJõöönÞ¼¹¸¸øú3(SNçÐMÜî…{Ñ|•ÒÒÒòòòæÌ™#—Ë'º-w5ˆRü‡ÝÒý…ÓMb-ŒN’¢DÏèÅÎ=[ 4 ôõ:¹!që4˜H*y´¢B…B4ªKj¾J¨V3™¯\ÉëììIJJB›TFCÓô$ëès»Ý½«««²²m^Qü Ê5555!!Åí½U¤¢ÖŠÅ☘˜¨¨(T‰ÆÅÅ-^¼Øw§Uô¡&ºuw Š¢ÒÓÓѮƬY³Ð 0Ñ͹KA”p·ðä9·‡óÞvån$ïq_¡0$$$((Åj]]Ý¥K—Ž?~òäI”IÉÉɃុÇqÍÍÍ™™™F£}.ßg¸ÿÏÞ{À·qéÞg3è½…`{'EŠU-YÍ’,É-Î&qbg“µ7»ÎÝ/›ýÝÜý²%Éu’Ýdw“µ7‰[ì$îq—lõÆ"Rì½wA€èÀ”{@ز,Ér €äù[AÀÁ`æ`æÌyÞçTx)`ñÖ[o AMvrb$¥Ä­Ã2 jzc>èRÄ.þµ¸¼ç•]ØðÆð8EøÌ¢b¯„¿„á•N€û¦[j{ë3)_iVrcx‘lÇANNô£ÐÒõ÷÷_¾|¹®®N¯×‡MªZ­Æ°kû6Ç&0ý.— ¦<ÜjE¤9’@3Z\\ÜÔÔ”——‡êxo’Râ‹%Ô62òαžÓݸ|²á‘{C‰²÷dKÇ4C°ž÷^jÔa™rêô—» —¶¿ÿV‹–Hö ;6è"ßPßÓOs7ßžWfÜ^¸éÂÜÅ?ŸŸêì/ÛZòȃÅFå מᄡ´*èôôtKKKCCCMM txÐÄF±Xãåïðð0ŸÏ—ËåÑNHŒî|ôâ‹/NNN¢5sn’RâÀ0™Zžo6š¿,ù[ R5¤@ ×H0¡â¿µ¤ÇÏ/‘ãþ|¹æ_þnÈÖŒˆOPúÆ­‡«òß» H‡Ði…d%U™ô¿ûÆ”=ˆ‘\ ƒ%(‰˜­æ½(¨P p&%%¹Ýî¡¡!¨©/¿ü2t®PP¡yÕjµ1ÛãwppP"‘ˆDÑ™k7öw ÞVx+¡1MHHˆÍ›]”"_X¦NAç4Xð(¥P;ÓU ~H>ãþ Òàp1`ƒà…f³Ýx$ðAyhŸý¶T/¼Ap¨(ôÅk€Í66ÖÚÚZ__îܹÔÔT(´ÐåÄÈ\t.—Ëçó%''ÇŽºÇ&PJ/\¸022ßD;-1’Râ€z™ÿæAi¼§x@Æ¢ÅE©—€±ùЛDEènüJ%pø€ T‹u‰[3Aºö£8xHƒçÜ€ËÚ˜™%j*”O“Éät:aAÜÞÞþöÛoã8Õ ªl|||[RY–…6+@iVV r¹Ül6777ÃH…×€¤¸5®öŽP Uoo k䤂п›ìUv•®U õ–Å2™ Ässs ÝÝÝz½¾   <ADºD Ox$‹•Je„O½â€w§°°ðå—_¶X,ð®E;9±’RD¬ ;Š¢à+MÓ̇Ô²µ Ñh4»víÚ´iÓÄÄÔcÇŽÁ sÍÏχ¯‘ì3ÕÀÀ´¤«fVáåÞ”„„>Ÿà½{÷"cz5HJ±ô+¯½öÚèè(½Q(œÁ`J)|…ïI’,//ß·oŸJ¥Švb·„P(LKKKIIÙ²eKOOOSSÓ믿 bÖ"ðþòx¼å.¯a¦†ÙiYϲj€· Þš®®.—Ë%‘H¢œI)"¶€byòäÉ_|‘ý«?…Oò¡C‡Ìf3êC¸:€JÉápàÝ„áQqqñÌÌLgggKKË™3g Ä†M*ütù†n>ŸO«Õ.ÓñWðF”––¶¶¶Âø#77Ó+ )EÄ …¢¬¬ SXÀ]½=\¹ôÀ|å+_1¨:nõA„Á`ÐétÍÍÍ'1™LPeããã¡I]òû>22¢V«ÑÒ¤Ÿ•J•””ÔÖÖíiäÛ¶ct!±EQáù]®Þ^XXøàƒÞwß}È®n XBa3›Íóóóýýý°Ô~ê©§”J%TX|ÇÅÅ-¡  éõz>Ÿ¿T\õÀ‹ýèÛo¿ ïŽF£‰vrb$¥ˆèîXdµZ[ZZº»»á# ½)|b†ŸÂbnçÎ<òÈÆQW£µ†aP>ËÊÊÂ=~»ºº`ö¸xñ"4‘p#ÔZ@p‹süú|>˜ëŠŠŠHò'h„™3œ×2ða¼rá£ÑÈãñ._¾¼cÇTÇI)"šƒA›Í688ØÑÑa±XD"Qrrò¾}û +ýÞ÷¾g·ÛaazäÈ‘ï|ç;°èD•ºkXRCÉLHH€YbÆ ===°÷ÝwOœ8‘“““ MjXS¿ÀÁ̃á&_‡ûœ¹P73ﺅ±² yÕ•eñúš“Ë×vvvÂèV(F1m±’RD€‘¾Óé„b[[´Ðk¦§§ÃÇjáV+¯× #_FÍèáÇu: ~×8aM-,,ÌÍÍ…áWxÞ|leee¨TªÏ•I`&œŸŸ‡¯7ïs466öúÑS˜Ú̬Åzƒ¿m¤N­]-¥0¨…V^ÿññqøä¢g )EDš¦].×ää$ô Ð‰2 -Åí·ß'ŸÏ¿º².))顇‚RºwïÞðGÑM9"v f˜7ÊËËaQÕúTø ó TÙÄÄD©TúY*0`n„_—Éd7ÔÄ,Õ¤VíÉÖâà+Ÿ×Õ|l¢ék¶ëõúøøøæææäää›T¯”"–øÑÑQhC{{{¡MIIÙ¾};ôr¹ü†“ÆÁÒð+_ù ÇC]7$´A˜L&˜‹FFFÚÚÚþüç? …Bè“òòòn^ăA˜aVütÝÅ†ãØšl_À0ü†qlؘ¾öÚk[¶lA@ÌJéÜœµ½½¢¨h'$jÈdòœœ@ð黯*PA½^o¸ÃHww·Û토¬¬,;;*(,ænî5ÑŠWˆÏÌEáÉ333Gû‡(•ÊüüüŒŒŒð|„×g6ŸÏ788¸uëÖ¨${ã¸tttlÚ´ ÕŨ”^nn|üÉ_HÕ\‚X‹‘ ßàšGþžÙœí´|†±Z­0äomm…f4..®°°–t0zEuAˆåå\.f0h’ªªª, 4©G}óÍ7‹‹‹ ’’’®ñ³(Œö£úÂH$’¬¬,øŒ¯_¿u­Q)õøœ4ߪɕ“ܵ(¥ngÐÞk»¾}"– hq¹\PA[ZZl6ÇKKK»ýöÛ¡”Âèõ¿EDX¬'&& ¨©ÐwÂÜøÊ+¯ÀÜË}èSÕjuxŽß‘‘‘ðrÖàÃ6¯×ûÛY`±Ž†ÈíííðJÂ'}Ó•R˜·1à€Ã]‹·þplå<ÎЃ:ÎññqøPuuuAß™šš UøŠz !¢B¸%U.—CK ‹{hR¡yêéé©««ƒ* ·$'' ¥§§_±SçΫ¯¯ß¿NNNt¿‚€ñ ”›››M&Ó¯pŠY)EÄ4á©qa £þ¦¦&¨£4M'$$úèÆoØ¼Š¸‡STTôꫯnÙ²Úýh''š òñ¹ñx<ÓÓÓ½½½ÝÝÝЪTªòòò¼¼<¥R‰JDls¦d‘ŒŒ ‡ÃqìØ±ÆÆÆ . ‡mèùóç].×»ï¾;;; Õtß¾}hòÏBjj*ô£}}}HJˆÏŒßaPcyøØÀÂH¡P¦¥¥Ef1,b©J¥ñññ•••mmm…>µ¥¥,fòúúúüà>ø ê”ô©ˆD¢²²²æææÒÒÒµ\Ç‹¤q3Âu_ ƒƒƒííí333á¦P³ ‚ P ÄŠƒ¢¨ÉÉI(“ééé0ÌÊÊ:uêÔÕ+Á`ñg?û™Ýnÿú׿Ž&à½9'//¯©©ihh:þh''j )EÜ(¢ÐzÂÇ£«««§§ª&|`6nÜÃy4â±¢ª ¥4???\•2223ù5ûÌÏÏÿêW¿¯®®föF‡A„€×P¯×+ŠÆÆFdßpÆ•µ’RÄÇ iÚãñÀ¤¥¥ett *|N:Ž Ÿ“˜ªÈ…‰±X\çÏ +¢f)4a³BÊe¿Ÿ›7cèv/n·{ff&99,ÎyT__£F˜½Ù«‹½ê^ýõöövCV…©ÚíTÇ.0Î...>}ú4Œ?Öl‹)’RÄÀP}jjª³³ЍËå2›ÍÛ¶mƒ ^ï,Ú©»1‰‰)ññUÝÝžXÒ÷OÇ98Ž­”9¼ šèt ))iÑNÈÒu…áB¾)//ì±Ç`éõzïaÂï­VëäHÿü̸T¥…w0* fiŸkÞêYp‹ÓEä F–öÌÙm6?#Ñ&ê9²³³Ïœ9ÓßßÿyX5 )]Ó0 £rXX´¶¶vwwC5U*•ÕÕÕiiir¹<ö;É_ýêƒÑNÅjfU®‰=::j4Ã#JÃRZZZ6£á±1Wgß8«Ô&FKG!”Ïniþóé?½–þÐT•f“øµO%ËPnë`ãËÿÕ6šp÷÷ÿ>>NáW$A5…× ^ɵ9n-þf,  ‚ÎÏÏ ´µµMOOCá„ò™———˜˜xõ2¿1L*j¸E|.`æ‡R Ëý+­z7/ú¥R)—Ç'ÈhNŒGŠâ &3ŸqRŸÐ:€á¤4!G§»ÜéŽJ/)ø$æææ†[…RRR¢‘„(ƒ¤tÍžò*¨ÍfÛ¶mÓétP“VŠ‚"_»ÝƒÁ™f – ºFêzºú C¶Ø?m±’ªŒD£Î9Ö>;3G¨²Ì%¥>ÁøSÝõ“SVãÊ Lé)|’M²ß68ÔÞ´à fÚm.ZØ€}t°í‚›LL/®à¸[Z}x|vU…ðce9KÃvÕLLÍã\©.g½!NÁ¹ÎË.!áÎG2™¬®®Îd2Ål“Ðò¤ôÓzaÌ1:äòx@B‰V-‰‰åÌ™K MÓN§sbb¢££chhˆa˜ï÷ìÙc6›y<X|¢ÆUËèÈHcýÅ`ÀwýG\ž0'¿(5-m =Ñbll úQ…B±<Ïú¦ÛÞùѨtWE†fn¸q°kÒX¹[+ÖÁºþÆù—¿ØXªéy÷WMÖ¸ŒB^`¨¯ö¸eÓë«Ëع暗þgÜgLOÇB=¡B–Ф¥þ÷õó2s¹žõOÔü©e<^_X$”]uÆ ½ïø_jžÔ¤ç1§;ªïÿN²A±¬?–$yyyµµµ0XQ*•ËyªXIé§Â2.wOÍePª$ ãñt×NöÏ 7NTIb½Ûw¸"wppzP¨ ^¯7%%eçÎIII0~\³ÝÖ#LsSãóÿTÉ \¿ð£‡PÌïÿ £É„Öˆ ð‰æóù7_î{ ÁpRœX¬KPÍ ³ ÷ÝÃw¶ýÑ#B[rèK„½áÕýpªwØ™0}éØyᆿ©>¸‹ôM]þý·ÎœzÓ”AÕ¿ÑT;±ñ»WœŸì81|ö$<W¯×â `¡*Í`hŸ¼ö¤>[_Ó±ÓDñ·7Þïï~öÙÿú]wßaÓ2K) MŠ‹‹ûûûËÊÊÖZtޤôÓñYœ–~—±Ô”¬'Y`mÒx)O^×òÏ2T€åpñ/•A é ÷<ìîîîééq»ÝR©´¢¢";;û“ÖnD,TÀ¯âSùºPθæ£IšOQ¨¤jm#K‹Å¢V«Ã•1‘„# Db>®• 9¾P lœ$‚AŸ×15éà–êS¹°ìàÉ´é©®K 3×TßϬÓk?o•EÐ5>11ΓÕÖ¼:Å,ôi’vû#0 (©©©­­­‘¿ÂÑeõKi¨Wž?`ŸõéPÿ~®\ l è°úAÀL¤HÄ–¢Ó/… %DÀ X\¬J8ÌŽ¸Ü AǸsŽ/Õ¨qR¯àòBKÖ°,͸æ¼n/ßz­®ù 71S©VD¾/úP5;;Û××mèÄÄ„V« /‰·6»ÕŰ„„:z}cΠ˜&¢À°Òét^™œ!|ìÄá÷,¤Y˜"NøsœCšf¨ MûÉùü•ÿ,dH¥ÊT–Y–‡ƒ­Ù›i*5Mð7ÀxýÅ_œžžNJJZþÆ«½xeÙÀ¼³õÄ„æ*dÀæòÊäåòÉÆ‰áY Pr)§×äål׋驦‘óM^SaœÀãp Rã·Ý¡ãúGÐ餿§\ÓB‚œžS#Váî“ÕÌÒ2ÞÜâ‘é…ÀéêºìHÜjΕFº 5¼PèÂÂTPÎÍÍÁx0==}÷îÝPJù|>j‡C Àâ“uª©ÉdŠvZ®ø’$5ßç¶OQl*Ay&FxÚ±ZIõ\çàܼ]«ŒcŠýÐWbNÓÐ`áF†f ã½æ„@§äS4KKô©"ƒnàŠïãããÃ3%&&®©Ú¯U.¥0M5[º:|ë¾jJ7ò©Y[ÿcï·vÔ8LûÍ…ybzÖvòùþæ¡jG\\¢´ûéšÜDŽêX]ËÜÂö¸•ؘ&íot¦®3d› K¥TjnïD¨O:åö ]°¸”ú[‰ûL—“ö…‚ˈý:†a ‚ŽAÚÕÕ4''§ºº:¼ ñšÊÇÄgÁb±ÀGC¥REðœ,í™õzÔ¬ËåîYw€ñ»æ]Þ À9 Úã UÕyÅñ½=ï÷´IÅþ¾®^&£d³AȯJ¬ýuý»¤Ë‹1k¯Óç[˜™ 0i|m3süò[ÏûÒ㣣>¿Êƒ}27 8íóZCJVYúÅæWΫðd“žäËÔIiRQ$† …BX Õ××èE*•FàŒ1Âj—Ò`À2æö‰äqšP3©V¤J¨±‹Ö¹ ŽÏÁ0Ž\¤ÔpZ½h8ÆÁù®@Ä‘+xí¤nÒ¼ÀÂG„ñà lÀ98ÀIt€f—¿E*¨ßïjjjš˜˜€±vBBÂ]wÝ_%I¬Íí‡@ÄðÁO 4#ÙᎥ¡ÂùU©;9¤Üï˜~¢`»@.uÍ Ü qy¬‚ëñ‰Kîý{ys­µçÌ,Ë1l|0£tƒ€à2÷mûª°«¥m¢»I®”›·ï„ß르ù‡¶ÞNZl3 n…®x«³ûæ§ç™ G•‘YN\s>NnÉ]/¬=99Ý5°0¦J.V&FhÖ*XøäææÖÖÖÂKÕŠôH³Ê¥>=AŠDH^Àâ¨\£ƒ4šÃ µT@5%pÆO_;ôùS3>©LNX\Ã.¾ËáòâMŸ0«×R%3¼PhOOô ^¯W­VWVVfgg£…BˆO…¦éÑÑÑêêêH>,‡'Oݸ)uã‡2 Ù[¯¼OÈßûá{uîöÌk¿LˆŠ÷Ã×Õ\~äï®?—ÆTòÑœ¢}9E·ò/ 4ý)))f³yUΖuCV¹”b!ì¸Ëéc¤ÂE•Ãq¾ˆÇ§<Þ`h†0øxùÜA¾š$,øyÎå2å£õ®ÉN+‹§V'¥f‹—£¥SSSÍÍÍýýýn·*hiié•…B—þ|ÄjÄf³ÁÇG§Ó¡¸sY—·°°ðé§ŸžMLLŒvr"Ä*—R@ ™òžKÛY •'òqœ$I²s¼Ç.'ÅþÉù¹nò™€ /ÃÃÃb±xM5àE‹øøø¸¸8Xv%$$¬‘’j•K)†áêl]¥ïïr´žt"®>S“‘­,ÚÁöv-´œ´ãžP™˜U Á½^·‹• IÿŒÛÊ'~ PóÜó>;š™§q%o~È>%"p€Hf&Ü’ ±XFb}®©^;êÐNµÖØÊïJÏI,•7eÆápÀ|žÛO(®‘|‰@,-,Ë )•JøE;-«x‘¡1mllÜ´iÓ™%{•K)DìûOÉÎPL¦È vší‰}îy¿/_푼 “Ñ2«²¸Mɪ¤‡83L¸nÇIo®`PÏóªøòͦ=J«vÂ5í ç(I å¤;µ{žš˜{}–͸Ý\/Në˜S·X^mtLY½TŸ»D‚GDee%ŽãȆ"·‚ß™IKKCKEXXåääÔÖÖöööBM] e×*—Rœb’;l¡¢³UrwPÞmÃi–ÁÀp¶J;æÌi·M%†Ʋý…霯 Åêб¦É%v¿ihaÞC%öÌÃC”jÕ®‚‹S6ˆïºôBÜ ´Ãn=ËTÔ[à1;ùYC®-6ç ¶dóO”£'¸uœNg HJJZ Åz, V«[ZZ²³³×B—ŽU.¥2«×©àšP””SƒÁ\•=N8•,MiŸKk±Z’$ƒy*Ǽ""µmNl÷e+­ bøuS§ î0k ä«h’ã”óàŸ }ö‘LüzÐå‘ 9¤¯w^’ðz³s~ Ú˜¼]Ëz‚~ퟎ@ >€eY»Ý ããã—û\  (–àL0À°Áã}®™DY–¦|>@ð'øüâÉ`Ši#ùŸ/K YJJJžþy«ÕËuV¹”ÎëDð_ø½M/‚ÿ>ú ÃóÔðß• *AÓæ„«¿>œ­„ÿ®üé‘r[7®üIˆ…¾;2ÆK½Š…«ñ”:»‚ŒA Ö ÃLLLH¥R¹\¾¬'bé…Þ“Ï\nò”ܾÁ~ùõ‡b×IR‰?û|s§Ÿ~’—wwÕm•¼/Z”°Œa²£ó½­âêþ:QžVЕÊd²®®.ƒÁ°ê+V¹”.+0s"¾>}­ œB V(E ÆåžD À5e™ DÚt–ç›ìnñ?ßRÜ8Î:†Z¹¦#·2Ãy]ºRà?ßÑ룢²x@PVVÖÔÔTUUßG+‘I)XåááaX¬/û™0R*Sk“_( òHèR½ö©9v'øB¹šG†fY¢ .û0€B…† ‚nÇl€!DbqÀí`i>ACéÔ¯Ýb™€ËåX ™Ðtj¤X#AËÊR^‡ÛåfXI,S<4ÛgÞíò°ð<ê£ïÓ~cÎd0œÊ5\2•g0pÉÊʺpáBwwwQQT¦‹ˆHJÄ*ÇjµƒÁ¸¸¸e?†K“ s…ŸG8ë·õv¼ÿt¯×æ°û4‡·Üq»”ô]úCCÝ0OD:f¦eE_ÙX,ë=úÏõqÒò˃cÝ2ó.SÕÀ¨ºJêØ c ñgÆl ŸO¦±Ô·íÞÎuuÔ¼þ‡9/äb>§W‘{ jk96wùük„ER‘»«ÍéS„¾Oû&/=WWÛÅá ŽyaÚîê½;%¼Hþ*•J«Õ^¾|977—$Ws’5*¥,EÍöÛ¦&}ÆM+Wó‡1ÁÐ àÅœÕ^·@¬FFFt:]D8⢴Íy©Ðѳ!‹šdÞö sÞìßÞ{ïWšŒœÊ|_•wû®äd~ÃÓß?êTYåÃz£ÆY׎I“ÓK4Y’©p;‡C\)~XÆ7rþuû×ë_ 2<ï4OrÊ5púOmÎm?š¦ç¾ûß~ûWÊ9ÞðlÇØþíï™ecv޽Õà›m;ÿò+Tî7ö¹möÌc¯¼ý{}~YQz\J8‡SVVöæ›oÎÎÎ †OÿŠemJ)ëš°5ÕÚTY ž‹òÌ9ZÎLxýº*é°­ù”Å)Tm;l ÑÚdÄŠ‡eÙáááÄÄÄÈÌ‹aá³4üNŠ$JD-5oØVw¡n¤wb]a²BŸBOY-÷;@»æ)†Åqœjó7åǓŒÄÇÜëŸlo È6¤¤&r¹\2}gQ<&ÁG;;€ò@¢AÍåá yÅÄÛ§š.²=‚ø¯&'Å‘Zgc±K°ÛÚ9<ê7æÑ³CÝ® ¸¦g'ílD¤’žž.‹»»»õzý*î|´&¥”e]#Ž9K0û¥†Ã,(× ’ÄIB•,S\šž˜½Ù’0báõzçææ233 "jŇ/!pnÐíóÛû_úŸé &>YkŸ·SÌÇêœ1œ$¯FN¹<^¨Ãƒ0B(“ÚáôúܘJ¾Ø; ãðÅ‹ùf'‚~!R}¼%”¥ó^¿ËkžpZ¿n×ý)ÆÈ-3ÇãñrssÛÛÛ«ªªVñÓµ(¥”×oôQzaÆ+Ò Å\V—®Ä%˜)qvõ¹ E»ç}þ ‹q8"%ŸGb tØÚ"Qò¹äª ²ˆÕÍf£iZ«ÕFÑÁr„b‚2µÌc©¿|¹3çþ_VTêÚf›‡¬Ÿþ]Œ”ùbÆ:æ ÒÁ†ãJ…ë_\œ¥} Î 4Zb‚ërÏ–ûQ†så|¾B_°«r[^„V¿:ý–••USSÓ××55Ò§kNJYšvY=sÓÞùYj²w2~®ïҟljôÄ­⮪bY*8V7ÖÙ(¹þ9§*®’OsàR9¾à`%†ô¥›n@,9,ËBK KsµZýé{/-Xh!G*à¸g†kýdZzf‡î¥ý>×ìèÜ„Û:3MÑq4Eath! † Éâ äž«KÎÉo~étã™’²òB>‰Ñ,_,‹OÎάiß•®çµ63ÂŒ´¢ª€«ætûñöÖÂL£Ôa·Ó ˆÔYF­«·öÏi)R­B@Ó¬@öAwâH\†Å‹¯Óé.]ºݺeeuþª›Ý¤>.—Ë#>©:.I)M0Ìv¾þï=,pòîø›Ìdî*ÉÏ~¯÷üoÏÎò€Pµ 5¾OÎÎs¼KûE›¢J)“\£¦Î7m¼oƒÕÓuáéwšåE|bá‚ÔÍ÷XÏw¿ý«ø°‚}gfdÑÂ/O9žmzù±!} ßãáò}c}=†uÅïýê™wOœ~öŸÅr­>»:ýæˆI)„$É¢¢¢cÇŽÁ°F«ÕF켑dÍIég…e½÷ô,guõ] z§üWÐãÂÄBv¤v¢Žò›2eqjrõ6¢#«¿ß?44d6›—{r†ëÀ•¹ûö˜6SÁ tÆ)Ie¡qŸòÌÍßúy™Û‹û<R Æè-Ù{‚'J¯ÕÑE0Rž¾î¾ä:hè_q’'V xÆË]ß÷=¡!¤,ÆÃÇ7mÝõí¯×Ï—Ë øý„P)æó%åq s_ „f‚È„¢ˆÎì®ã=wî\ww·F£‰ø½ˆHJ?‘ЖàƒÞxBrý^Ž.MÁKDÍÌð…‘¡fQîNSn–˜X…¹X%x½Þ±±±;wFüÌNDòë¦øÁ0R¬‘8“ @ž—Tþi–ãð$Ržäš_&åË®9-O¢¹²ãG‡åðD ]W;PM[ZZÊÊÊVå:wHJ? æ=.Ÿƒ+SYe’ð S&tp”‡¹“ŽKo ôtÚSSEb>²¦DŒbµ†:ö@'턬uÂÆ´®®ntt4333ÚÉYzÖ¢”24ªtLÀKÑbœ„jMØÐ: ,w±íС÷R¥8^Ï7Mkä@%ãœÃa¼Ý—½Æ…DÌЏ>.g5VT «‡‘‘£Ñ¸ŠÇ`¬ âââ322V_ïš“R–¡ãŽi[JépóaSV‡›b ®‘.»ŒXð²$˜s Ò%¥{“Z.ζ½7DŠxúLuJ  -ï9p'UÊÂujáR­ñ@ –šðä iii«{ʺ•ŸÏÏÏÏ?~ü¸ÝnW*—lQçaÍI)†‡zð–IZÊ„:«“<œé²u,ü‹‹s«MS1,Æ!q‚ÀDiq’Ô4ꢒO+= bv±þ#ÑzjD ³°°Kmè„8œÈõVEܳÙ|æÌ™žžžuëÖ­2cºæ¤,÷â ®º‹$‡sU¿p.qÕS‡a$¿: %ùkñŠ!+‹Å‚a˜J¥ZÅóÕ­,Äb1TÓK—.¬²ÎGHÄ*„eÙ‰‰ .—+•FgákÄõÀ˜&??ÿܹsðÖ¤§§G;9K ’R± ƒSSSÐ’Ff{Äg$...55µµµ5--m5Õ )E «¯×;33SZZºšÊëUÇ+))9yò¤ÝnW(ÑNÎ’¤@¬B\.—Íf3™L·~(†¦|‡ŒÚˆ,4“itð{\T0¸„„‘Mzzú‰'ÚÛÛ7lذj$¥b-)4@·>‹=—Ëe}öî³/‘¼ë¦.Š¡îÇFSTTÎNAû„HP¹„Ç”J¥iiimmmО®šÎGHJÄ*ddd$))éÖ×!¾ö/¶;œK’ª/Àâ Œaè/|†aZ[[áqòóòÁç7BAe^nÖ>û )..~ê©§,KJJÊÒ9Z )E « –eGGGay}ë#JAyiÉçúŠËå‚ߊ©Á¬ljj*/-”ËåÑNKý"íííÉÉÉ«£ŽI)XmØl6§Ó ëÓEuww_ºtiÏž=&vDbݺu—/_Þ¼ys,Ì@’dIIÉéÓ§azVÇh¥Ø•R*ÀxÝA*È|ú®«Ÿ+HSÑêg€X»ßë W¦-8쟴Ë2.§svÚBrCÈd ¹± ÃÂ:°HŠ™Ãá8zôèSO=•’’ràÀØÑQÊZ²ÂÂÂææfø “öA9OKK;~üxKKËÆ£œ% F¥T¥ˆ“r’l]´E¥ßûΩ(uZ‹½æàã’h%æ³øøøhq+@-Ie3#Fþ‚Ç‘J,¸¡EÑ í„ÎtÆE¹„)R©<ʸ•N ˜™™‰‹‹ãr#±ÀµÛí>yòäï~÷»÷Þ{FQPJKKcª¡4 ,R***N:Sh0¢œ1]·nÝŸþô§ÉÉÉUÐb£R*‰òóó£ â‹¥ôŽ;öö ×¼–®$9Ÿ\³ÂçrTBO€Z ößõå¢"$¥·ŽËåòx<(a¨Ýßßÿä“O¾üòËð X”‡òòòØœ^âââööö³gÏ>|8*?’’’4Mgg§ÉdBRŠ@ ®E"‘êQõ:¿˜½0aqqqP½£^ Ó“––vúôihL×­[ÝÄÜ"HJˆe–S[·nëéhë<÷BaäÜÀqºý̈GpûÁ#………1[þ®, ° AXÖëIQŽãÁ`ðêîÐcUVVêõúå;ï­ó–-[^zé¥íE=×) è›ššŠŠŠ"Ó¶½L )E – ‰Dr×}_~Ü2<2V›'¸¦ØbXvxžÊ¬½HJˆHK(XÄßyß õY»ï¸*k´S´Ú`f||\¶ÈòâÂ… X¾³D ‡SRRR__æÌ™={öLMM544¤§§çääD>1< çûï¿ìر‹/úýþŸýìgHJˆAQT__¯Õj]5l CeåäNMMZ,SÑNËgÅ`ˆ7±ß‚ÕÀÀ@RRÒ2娣µµµÛ·oOIIYŽSDh¯ËËË?£øÚÔÔôýïÚÁH>M4MÃwîܹ£Gžw^™ŽqE€¤±²±Ûç_}õù@`Z©\©Ýèc™éé.çk_ûFŒÏ<×¶®®®^¦ãCÓ=Snn.ÔžØ,> ,ËÎÏÏÛíöS§NA1›››ƒ ::: ƒ†HþÀžžžÿøÇo¾ù¦ÃḲÞMhL#–†[I)beò çÞ±C—™©‹vZV!uuSmmÞ(®6øÙ±Z­P–irèÞ{ï=©TºeË–Õ¡£E]ºté©§ž‚ñÁØØ´†`Q\¡=u:‘ìÓ›––¶yóf˜Œk¤ÔëõF, ·’RÄŠÇ1±˜”HVvØD($WBÅyˆ‘‘‘„„„åQ UçèÑ£Pc|ðÁUÐDPø‹Îž= ­üÕÛ'&& ¤ERJ¹\î¡C‡ -~â‰'l6[x#´¤PM#–†[I)Xñ@;544”œœ¼“$477wuuíÙ³G§[=5Ð[ïÚµ ÊÕücøë®l‡úzµ;Œ P¹¿ýíoÛíößþö·Á`,¶LCW ½òJ©@RŠ@ VòÈ#ðVž9s&œèJ—{⪥bµe±Ö€šg±X }Q(KxX(''Nœ€ z÷Ýw …Â%è #?ŽãUUU>ú¨Ífkkkƒ®4ÜjE€¤@¬l(ŠšœœT*•K+x­­­ÝÝÝÛ¶mÓjµKxØzS¨¦ðõ§?ý)t„½½½áËÈ—;wÎÌÌüèG?BRŠ@ ‘Ãï÷åçç/aí.,ÄaQ^ZZº´‡]Z`"†Yª£AGã’ÑÑQ‡Ã­µ 7½ãŽ;FFFœN'¼³7u‡#p")E +¨y‹eß¾}KxLXFWVVÂb:f»½@©¯¯íëkð%SS(Ì)55uüãï †hzq¥’ër¹^}õè•?i #³² Š‹Ëbá!)E +X’VTT,y5lŒwxñz=gμ22ò¬^O-áae2¶¢Âãp ûýQv{b1=5u3œšâZ,_ËÎΉ¢¿X!’RsÏ>Ù A›6USù‘ËtcŸaŒ¬75u!;{9ÝËpÌ%¦½KÓ®h§â”"ˆØ" ž>}²µµ†ËÑFÊå&`22rwîÜ¿3N –$¥"¶€RÚ××Áå–”¬ÈU oŽŽ©în÷æÍ;”®”"ˆ˜ƒÃÁ32tUU©ÑNHtÀ0¬¾~É:!"’R@ n $¥@ÜHJ¸%”"qK )E¬!X†qŒ¾uzÚGañé‰ëò¥ÃmãÝÃvC¦%—ç¨yÄsı mŸœij™µx(|®J§**2`ÖÉ35SîÅ1ñ_´nk¦>hyïì䂟ÅyÂÒ ¹&1 Ýã m367…sI•NYPnJ’¡ùçX–ø{›‡[zl®+RÊr‹M™I"‚ ¶Õt5ô8œW¼)#[Gö·v{¤ þôˆ-°Ø…J×mJË0¼3Ó'Ï Í,Ð,ާ䥮/Ñò1Æ6>ÝÐ8>1çg9ÜÄŒø²"Œ»§Æß9>¶`¥jíöFvf¶±yŠN2mÍWr9±:±ARŠX[0TpàbÓo^è’ffìÞ¢iŸì˜›œpjK ÿþŸ÷^¯æáô_:~ù™g. ¹HC¢RBP–±¹ ñ­îßfdg‡GŸ}º®Û-;øm¥[¡êÒ}gß餫”—„48pùÝ‹ÿñx“S¦4jùîië …>òO÷|c½†¬wÎúúóçž{s„£R$ªð©ÙßHâ¿ñÈæÃë•LÀ×ôæ™gßwÜþÕÊ"-]w®·É‚Ý÷ðNKÓÏucúä‡Ù¼îÃ_A;ç_}²ÖiÊyÔœ˜`÷¹ÆÇs©c4&HiûÂÐSK÷®ûæ×Jt,ë|âÉKóòÔÁáŒéæ¡35£š{e(´"¤¤Ä’¤±†Àp\™fþÒ±wÞëõ«•û7~ó!å˜÷¹÷ô›öçÞ.Ø^¤Œbãõ—ÿã_ߪ%Ìÿú/;v(HÀ8†Gñ³ N£ËH}èa éuýó“ý¾ +˜va²M÷þíƒù>ðŒ¿ôÔ¹WÆoÛQjR.ÛÏ5„g£a™`Ã;5ÿþ‹š„»îø§¿)1ŠA×¹úï>òÆ/þ›Ÿ›½'oCþ—š^8i™^ð««2¾^xêÝNsqæ¶²õÂÈ90å'AKŠc˜0.n]‘ö%©ööû7Ü^© Œü÷¿ŸxcZõOÿ¼gO…³Oÿ×_ýÏÇOk’ J:|gÎÉwß±Ï1¢Í÷l0'·5ˆ"K ’RÄZ#I†ab…,;WŸ.ÁYFFOg>÷ûþá.»7À0˜çÄÛÍï¶öýŸâ]åq2nh&R‘9ñö¹L|HÉ•æðWÖ7_ž|áµ…9Íô 71õ¯îËVó¡D±^»×2â`¥8ŸO\\¨Ý{dK"ã` h·:ÑÑ”ܻÜ©Àãf¤–øÏÖ öŒ:͹BLÎ3g=/i۞»ïÌËÐrþ€ÓtÏ9/Ÿé´·†Wa‰ìÌ{‹¤f­€óI†›Ê³·W5üô­¾'·¦g6ô Ðòïl4ð0Œe(ûœŸ¦ééþñ£.ZhÀ¹U‡×§U$H¢¿öbEÁ— \x L¨Àý⸟þ0~;x÷5RÀ½{ÁÚl@@RŠ@| èU9è!™côOÄmwÎI•œÓvªåè…ôoîI"…ãp w®{Ìllº4|úd×û'{Ú0fq°ÿøâPõ-†I4ªCßܾ/óêe¡0¾ˆÇ¡ýŸt.žB³cOÖ 'ŽŸ9ÞÕµCQ_3WRP˜( õ†'„Dvuþwÿ¶TqÕ²`‚ð°…[¿"ˆ5Æ*-0HÀpÜ$ó_W *· ku$¥ÄÕ`€'äk„þ~Oߘ‡*W70vlÐ9ÿîË—Ý©ÿ§Ôô‹Ÿœ}òÉKùÙš )BºÄ íñÑš”„Fý†yû×þÃÿ>vüxç‘ûóËD|E×;è÷a‰àšÁ(Ì'¯”…qȬÊÜMùÏ7týá9Ò6Èîþ^²‚¿ø}œP„—šu9<žD|MíÚç*Wl¨vÛХ˜Ϭʬ.TópàžŸ¯??Ð;é…W¾ÀDöµMOºƒ8‡øpÍMžD³gš¯½ûýÖù ªÊ¤hàx¸æP*·nËLQ”Ï;Ô5ÑÞ;·àcÂP%Ä•–$êd:5590ÙÜ6=ç¦qê‡$#7!+YÆÃÇÄÄÙóc³>UjÒömÉj>Nù´Æ’®YÐÕ |ð‚ 3ð[@÷ ä # hE`¬ ÏC:3`j ˆ8¡k/yÀúzÀŒ0 à«@nÒ`°LØÎIf`T"ÊË¡.1HJˆÁ•‰³KŒ’wšëÞïî®RÄ ?(èØIÅ0Œ xοVwnŒ<òhe•Ì1×7ñØ/=ù\|òw×%ˆ±…áñw/Ì”ì/IW¥¼lszNfÝø<¤@-MËÑ/MÖŸ>\ªÐ 9WŒÝ|'¤ †ÛÓß8_óÌÓtõ*³dá°ÎÑç'%ªšû†; ´¥JÇ®:ì2^¨h½=—}ï×>~ÖjÚ`ùÉ¿îÚ˜!âðÚÞw|×C B!é~î‰Kò}Ûï[§!qà[°Õœê/¨68ú{\²ÒT™{xä¥gk½eUmOàyÕæÔxÂþú3O÷úS²ãSâÅ´ŒX^«ò T‡*¥¬Ï}ö•ÚNZTi†|³J€Ç{úw´%ç¶ò{÷¥. ,ܾ^/ãa>+7W&BH„ÒÆ0×[I¨‚ ;²«_n{mŒ,ß’/ù`†¨+IyæÃ;S{¡ó—¿Mï5§„L0èrS‰EÉ‚+?öG]‘`‡HHO,+0 Juã]ϾþÝrƒDZXš¨{ßWRlÐë¹%ù:•˜0Uå>Ïå`~§]Å’ó8³é`JjEªÔÞÁ?÷Vã|qöÁÃiRÚ¥9?$àúÏýáä¯^œ½çïvß»Ã(„B’€s^ý΀ ]ĉæ®_ý¶A±uëCUœ(‘ p¦¾ñë·~ñÛs£æÎR]iA|á(æyá7g ³÷o4ñ3‹M§ÇÕ¥ÉiÉJJ`LVãfãþ;‹Ä8KRT}”’Œ…pÇcÎ~ر#d©0…ÚMÇ:ACØó— 4 ¨üàÙVPRÊŠÀ[Àíà§€1ä&ÜÔâÅo±ÀÒ.Öƒò/ê@P@3('¨­ª`Û`ïµOƒŽQ‚¤X¡°45Ù3ðÊ{ÃnRÀ}õùËœ;ÌøÄØkoö¸ø<Ö:òò[½Göf$ge>ü¿IL—N]lÿi]»\"«%Æ4Ã6‘ÄÓÚöæk—ƹ©sÎÙZÍõ O¸q‰›˜ýãoNã®’ys ÿ7/ յйì¼Í›¸¹òËÂÒ3$~Y[Êþ៘g^j«yùLí»…B¯.Ý^Tév·×¶¾YoÉyý5mOˆØ;2󌂫êÀ0Uvúí» ƒ#üÛÖÇ}ÔµÃq†þ×^žêâ'ú~ù£^¹R¤PKS³÷g¤Ì ¿þfטÇaæ_þíYÛ–ÜÛ*tþª¨YÉÜÛ ¶ÁÏ_©ýC^üÃû?iGÊëu¸@ÅÎ ‘BÀVd²‚kGaqUU†kvögovÒÊnÛ”$ÿÐ(’ù¶Y,ÁÇhËÙÞ6+þÿÝ–žøAƒqÅòuÛÒÙ§Ž:3²³8Þ ¥9õÞ-Êûù¥g_LKû«"Ý'jËø)å©|! E°Ï†] \B%64° ˜›cVÐÓf:Óç[j[AS7ˆÏc6^HìÊ—B߲Π’t¡ídf€™~0apìmtÂÀ°þÕÖö ÷ˆÐÐèSâî¬ÜkJz^”'\ÃÁ´”ýß5ªTB!KجÓ8¾··ì¿Þãö‡ÚÌ8#’Thœ~iRAÆ·|ÜŽ]ó¶!%g]ÎóÌ i X]d²Ø”exàï9hõøNt³ÐAmËQèdJ ‰uO±y"Y½áÈæÜÍ%¶9oðL/†ãÂ]ùJ­DÈÅ9긿ÚRþíûd 7žÃåêÔ$~ªÐ L„zRâvpÄlÚ˜'MÔ/öí€6³fxX‰Q“–ò­Ð¾wÎsv€%qò¶™J¢ vb»B·éE ^]±D|¡'4Xac:¸Q#ðÊ‚#”í:RÑÔôêK¿=›—}g)ñ±Âzü‰æþ“‡m`j¯ºóþB @¤Âð‡Wï %äÛ]óͽTÚ:¥JĹ*PÁåÊP1Æ»Ð;lÇ8’dÿ£Úx «ÕFn`p|Æá ãù;+¾::ó¯Ï_x>S÷×[ù7L9íó4œmq¤îȸñ†aBy­?fCYL ©Y 5.ä ·â@­¤ ä&ƒËÍ •”U×~‘e¯¹¾‹õ"\ 7Ò²PKjåf V€UÐ}’RÄÚïw¤ ̆ ‘9 •9\žLwùS JúgÀÅ~°àãéŒ  y X€ä€wÚëSðM·Ì»Àì< Mq’fpŒÏ.´  G"HZ&€ Eƒ €úqP3*Sz¦¡4Êr˜O ´¥qT¤@–7Nʯ¤*[.ô‚¶ñPªf * xƒ0UÂ_JÐ Îp?HÕåÑPª¦`}*ƒ'´L‚ ¼00d|pv\K©‚çÒË€T.t†Ò©…޶Ê`ª´ä¾ZÖýà Oý¾)îàÕ¢Z§ì½=̤Í/Þ¬7ö"tÀ·)|’¸á¸Hšòøƒ.û'!ÉŽ(êÃ…º¡ÀïŠß'Ô›W&"9@¥ 1…×0tm ¯\x÷ƒÝ‡€èãf ã”Ë‚C“ Y xœO%ù@M†jƒÅJ &B[àÁWSí.@RŠX[ØÜ`Ü6¤AçzÿBxü,¨LßÝúôO—À¿rØš ªÓ/ž¾~w!ä ÿf[ÈÆëO]:´åþÅ q}<]:à_nb¸4 ^m“Pn»=´Ã¿¯ýy¸ÄËC"÷z h›ÉàÑÛB;üî<ø?o‚4 عT­nîÎu_ëœüÁï.þQœ¼J·8|^Á®Ò¿<`Xè¹ÜïýÔy8\Œ #¨Å°àºr#H©€ÇÒP-ÅUÐTÐéÇ¥|.ŒX|á=1L“‘úà׊ºX÷Ô󂌎»ju¢¦bK¶ó¶iQ¬3‰çÓmà= 0*Cq£›üÀdY‰àÌq@;€Fê=¯0¤L%S<[xüJ=Àî  È jŽÊ’ óBó6æƒ3Íà˜¤WâC9z5¤±–€† Ê | ¿ÿR9¸»ˆx|zOØ‘ýÁG> ¾\î* ½ W‡îÌe¦v€:fdRµtPå÷ûGGG­Vkqq±H$ºùþƒƒƒãããYYY2™Œ$ÉOéºüÀ¹âm‡+ê_~í÷uÊølæÚÏ1•Ù¸ÉDÓž…þ9ܘ,!otB¸MªR–äòŽ÷ÎNÙ)…†¼²Bex΃ñ‰Âl³’sf¨Ì»Åš?,vSsLÍŒÒü “Vvõ^ŒÌÝUñõ®é>[ߢIÈ¿ó£ñã”ùù ñ"™©õócEL8P½ÈšÁ¤L +/ðÚöÞZÚ}xD Eª%‹¯Õw€|Ä-ÎMB€}è2€’¶9 J;}+˜¶ƒAÐ$x#Ør¨[Bƒa†F€. $¬2OФ±æP^UÖK¯+Ë”W¨gW$í†;@=ã“7ÛKåÇŸ²kô N×|%©Z ,KssóéÓ§ Ãþç~ª”ž?þG?ú‘ÙlÞ°aCUUUjjªJ¥"ˆ[(ˆ0L•búÚ¥Í÷^ÿœïúŠÜÐà’émì93ªù²éÆR A¤Qï9˜ÛðÛ¡·ŽõÇLW/¶˜Rçé£~]J¼11wSÖº£ãµG»·å–§Ê¡Ö²~Çܹ£}|³iû–$áÇ&!’K¢öòÄŸjÔ’$TÈ—>®ùÂÀŸ¯ëoTa¡M;’¯Ûé…ýÉႤ¬Ð¿+ðu`ƒîã߂ЭK”Ú˜I)øL°‹†††úE†‡‡G0¬®®f?µA·ÛÝÓÓÓÛÛ{âĉ¸¸¸‚‚‚u‹äåå) Ç?ͧ²,E ¶ÖtÚ¦Ü}MÙ¢‚d)‰“æmå}eæ—GC>Ò5=}þÒøôBÐû^Ãof °tp¸s„›¿)$,cš<{z`t.èjê=]+Ü\¦— 9Á/?¼åQüÒû íOÌXµ"‚¥lSsÍ}¾Íweá‹ËÊxä÷‹GGŸþ•'Õ(åÊ2<=î|ã[e›2„ ã&†Fùš Û×d\L™šüÀ×*‡ç:C¾Íën©ëæzÆÞ;1°m½1I³fçZ )E ŸÔH§Ó911ÑÔÔtñâEèA;:: +½zš¦á(«7?”Çã jêÐ"o¼ñFJJJQQÔŠŠŠ´´4à¦5ŸF ¥ûʃBt÷×¶K‹ÝÉ*.îÄuÅ9ß1¦^e¹9ëóÓ‹µÄboRœÃQ¤™úÍã+„üCCIŠå;îÛ\P1ÕÖZ˜c9¼ô¤¼jUN¶ŠÕÚò v¬KÊKis3àf¬ËÝ›Ÿšcu„±"÷žL+ù`Œàæí¨øGe‚*[FâAQœæ¾ïö¤ZF¬²‰~I)øT(ŠêêêzòÉ'?>99 Uóú}¦¦¦^}õU™LvóCA#{͆aú9zôhnnî=÷ܳ{÷îO>@hŠSN*üwÍv‘V»s±Ÿàk7ïÒ~òp¥Q¿Å¨¿á‡8Aêÿ_{wÇyç üÚ™eÔjAÌ`I“eƘ)†P“^Rz‹×ëÛ»^{÷ö —ö®wIÓ¤i¸6ІÑ£Ì¶lÉ–,fÖj¥•–wà•L1$Ž×¶$ûÿýäãH³£ÙG«ÝùÍfžçIŠÿ»Îâ:køLëÕÇ&Ëüå–+Œ,wÆØô>H’Y’š “òܽ J_‚$I±jüÖ·¾•——wôèÑ'NtvvŠeå噪R©ÒÓÓÃïŸacÚÚÚ®XÂ0ŒÀÉÉÉbI*¦999z½þÖÿÜN¥€/a˜\.ÏÏÏÏÍÍÝ´iSGG‡X\~öÙg•••â×NgpîU1JÅ"#¯;êиªªªñ/p7 b‚Λ7/--Í`0H¥Rñ¹ÆO0…@”‚)ïFîv¹ÅÏÈsηÓÍñ‚€QŒÑÀ\»_ÿ]GÌ?­V«ÑhRSS×­[רØxüøññ:U¬,qü˯!V®býZ0fúôé&“I"‘ÜÀ GL^¥`JbYÖårŽŽ66Ö÷÷ÛÕ|vÎé8¶ãÔÇŸµœ<5`È+þõ?—$Üø,ÉSŸ˜yE‰š———““³qãÆñÚTLÙ/ýÙY³f½òÊ+b›Íf1A!>'À#—:£CÚ›è-% §9F‘"©ï¡·ÿµA”‚Én¼Çqb| µµµµ¶¶¶´´477755¹\ÎÄDAÄÝÉ&JmÉ}EFƒôô¶w«êF¾pŽð»ÜÅ:UüÝH÷ÐÄ17R¿‚ÛgÑ@+úèu¤ž…î_„dW%œ§ ØØÈ Wa¨§}ø!Š[ƒ–fǼ—A”‚IMÜA‹‘ÙÐÐP]]][[+†hOOO__ßðð0Ïó&“iÓ¦ :¿³çWÅZJ*gôzÙµûûß{Ää‡Y€< 2G ŒòšÄ²µö$âƒÃ^݇Ü‚¤ò_5ÄÔ=¢Ljbp>õÔS}ô‘gÌå·ŒÊåòÇ|óæ[¶¼8-àóžãúš:U°ë’9f9é¸üNÔX‡úGëF46ª$b½¨î,²¹‚ãZ¢P‚õV¡m[…œ…(' 9ºQ}+âDÉQrzpš† ÙÛQùhpns,JŒF„º[PSgp~‰¥¥çVØàÂæîàtlJÊHNÙÖ׊»!c JŒÖ¾“a¥`Rë΢¢¢>ø`ü6Ñ‹”Jåƒ>øÄOÐ4u½Ÿ‚À»G<#.?Ë! Ç£VÑ]¾†Çéqúý¬ s©D­fè±c~Þçêî÷‹; ’¦åÞå °¼X:*µLNpUñvã—OP)nÅu?Ö»ï#xzA‘Q¦œ\»YÎmG=(>¯^¸:áq ³µÁ}hßq´l-’ÇÔUŒu²³°±µ"“8ŒÚGЪÙÁå<\4‚£Átð0*¾ÍN ®ÙëE •ïG6ZRœñôÓ½HƒäêD±Vä¨CÛŽ …)œèTr®D91ˆ˜²oòÉõ7à $I.Z´hÛ¶mbš^,I)ŠZ±bÅw¿û]«Õj³õOl /Ç|uåUo¾_SÛçÃqŒõùI]øüU¹ëD«$Á!Ð.Ðr¼æÍwÏTµºpgý^¦³l·sÿòƒÜH9™~ãÉF`]®c»Nï?O¿ýÝ^q…uùºÉuexÒÁ4…LÁÄeDÏ+ÓçF‘Íÿ÷ãOË{ïK‹±iUŒeÎú²<™ ½ã©?ô„­X´dV´” ÷6ÖüíGõ¥…³Â”„Ò¨II6J]Û>i–N/[1?VKòBWë;¿«+¯É,å?ÝçXþÿÎ,2ÓœQ¦Á´¨±¢q×ßã¿M/ÊÓ ñtåÞ“[¶7Ü_:ýË;!p$Cˇzí(Å,ž/.9/jïB^ŠFntù­ébÊshduùѬ$d CW\Mï Æz}iLÈ5Šzx´: ™Èà<áþ±ìCˆ6¡ePV%zþ]Ôá Î%ÞÍ"©…OˆMíƒDˆR0‰‚0<<|ðàÁ3gÎ †Í›7ÇÄÄŒ§fVVÖ~ðN7þü¦·¼M~¸ßÙßãSXdåÅ›'0J¥Ôª»{ {t4ÜÝß饩ðpy)+eRF‰Ü#ýÎÀ5º§b8#5†kH¯­¿#¸t¿¹8!¾kpµEf`ÔŽúÆæˆÁ\*¥Ã¬ò€­±Ñ4Œ¸û&Éðh»»½¯Ûsñü¢tø\¿?N¯UâÁ¡‹ÐòCMÝ­£6¦¿WF›•Á¡%(&%-Jxö غ‘ÒªSC2Y¸IÑxÄåæ&g_cñÏ…R#С}ˆö %†—χ¤…Z*“²*5ÁùG¼—zÇò¿Ûãá)R¡e®ÙÿP`Y—ÓÍR¸ÊÀ@IúUŽÎ!>k®FJ}îr€L£Ï²àý]..JÁúZÎÙ4±š¨hi|^ÖÏr2…&'FÚÔ;0äK —° 6Ú¤ŠKR½¦8êܱ“íEI).pBðf]‹1–ê©ë-ˆ— ÃŽ¶6WÌ4­J1y»u Óx´4þªÂЪ„+—KQfé…¯¡â«~(µ¥^±H‡f.»ÆóZ¯xF åÎD¹7ØèI¢L°ÞÞÞ­[·öôô$$$,Z´ÈjµNÂë _ôѺ˜DMy£³µÇËÇïöëg—­»{H‘¤R°èd½o¯«¥ÓÃOS¯àµ;:;ì´Vœ¢¾F”ŠÅúÈh[£T«33Ôðq½\Àwö@õû=’š-XLúú™Â5ZÛ0âœ=Ó›fQk7¦¼q°þÅ—Ùplôä9bõŠÌ¢x™#AÛ³·æ¥W«%,Ý”õÒ–Ž?þñH‚Ê{ê¨{Á}Ù3z>þþ5M¯tðÉ–¶„H¥:ÜXZ•“¼vYÏ‘­Çðn£»«·“‰Û´< ëëkîqŽ œk6fè%Ð+ønŸM01A:qâÄéÓ§%ɪU«’’’‚˜d÷å~!áòÿcŒÉ:AúΪSû¶ÖÍIœnUÑá­×œì æ<4­0N*a$s¤z¨|ÿ¶ºû¦«ã5$ëvØQ}´ž-œŸU𢼸›óçõ|$Ïz<'öV¬ödÏÊ›‘ª™:/̈́ⱴô²L\AÆê‚Ø”HÆo÷Äüm¥DÁÉ|héŒõ³õ ÍUÍNS/z(¥(Ϭ¡Ñ¼cQ횦eÌ´%%?´6WÔ;¼|ÆÚE†àÑ‘vÍ7ÇLo®íp²©ÑJ• B¢[ÿø¼è£­]Ã::ê›+¢3ã•C-î’ù©>‰’Âà¼ü½¢ÜiãcÓ‹ zàÀ18‹‹‹óòò¤RéD·ë+Ø@k]ûÁý­ƒ>Þ?Ü_¾¯•Æ$›esÖcÀ÷áÞÃÿ:Б§´·õ<Õ•½²ø‰G²-2CxÁ²âïôù^ÿøø¿þ}OFšÖÕ;pêd‡efÁãß)NÐQâÇ·ÞYßòç?ì3Iq{Wÿ©­Š¢‚'¾W–NO¡ÃŒ DÐLöü¬M¥ú‹‡eR½næò¢™—¯ÃH§§OûüùʰØè ±Ñ¿MÌMI¼êü#£Ñ”,Î)ùüB™A?{éç¦+—&Ç®NŽ ñSD)¸£X–mmm=xð`WWWZZÚŒ3t:Ý”;£+V=ö¾áL¹áû yŒôÙ½vO¬I¦ŒˆøÆß­(ÛZYgõñ©±ùKKòs##رÛP0©>ló·—d—´ž®¶ {8ylă3sóó£ã̲K×Ö0\&“ªåâym¤eMAfaQlR„|òä: >O€Íèós /¸S JÁ"£ƒƒƒ;w­µX,>ø`DDÄ” Ñq8#Ï››;÷êG0©F“?ozþ¼ëü$†ÑJåô²Œée_´yCLĆ¿™'¿·§­úÊÏ€ýäiÛ¨«Ú]{(œ)®—@˜‚;¢Üvããÿ8qâøñãr¹|Íš5ÉÉÉ ÃLt»&1®¯ÝL¦_òø²YðAÈåÈQpÇ@”‚Û‹eY± =tèÐððpnnnII‰¢SéÞ¢;k¼³ãØdçÝ”) cR8FwD)¸]xžïììܶm[JJʲeËÂÃà NZ^›Àsµ ookìrq\SÛŸ_:|¸ººZ¬A7lØ?E/‹ÞAAQ1¥Óÿ±0kü ) ÀÔ0¢TÜ/Ÿ>]¾{÷'áÿòµÁW$˜L½bņÈÈÈ[²AŸÏwüøñC‡)ŠyóæeffÒ4}K¶|wÃp\ŸâÕƒÐpm‡ŒÈ{{'¬q7ØðЮVO–³\S#J[[«†‡w* °¹å\.¬¢¢·«kFèQê÷û›ššÄb´§§';;»¬¬L.—C1 Àí@Q’øøÂÊJogçÄ|Äp£(ÒïLÔu}1È““KÅ×abžþó¦@”¢±A#£¢˜¼< 1y¶œªFF°ŽÅ¥‘{nŠx¸Óßß¿k×.1Jcccyä‹Å! Àí#•JW­ºþüû&° âgœŸÐ »U*•DQ ¦>1DÇxGF³nݺ„„„IòæS— `55Ã4Ý3Q œã&,$ÎbYý—®&sÚ¾D)¸y>Ÿ¯²²rß¾}Ç͘1#??Ÿ¦ièèB$Š¥¦f:å?}z¢ÞKÁS—,ËNTŸ$Ž‹ÏȘ&•Ê'äÙÁM€(_™¸÷2»ví²Ûíz½~¢ÎèºÝ|UÕÐÈÈÔx3Ol¹óU;gŒwøII’,-Ì îðóN*âñEQÝ p£¦ÆÞL12¯žj{ppðàÁƒµµµb|nÞ¼ybÇÿ“Ë•qq¹mm­ÝÝSàÒ¬X±‹ûÇ@`Âʯ*KIɸóCSQcîð“pÓ JÁµ‰ûúúúúîîî™3g^WÁãñ”——:tÈ`0,X° --mÂÇÿ“Éd«W¯÷û§LG) æJŽŽÿÄpñ€/Q ®­©©éW¿úUGG‡Ñh#Óçó;wîèÑ£}}}EEEeeeR©t’\•Ž™èVî]¥àJbÍÔÛÛ+æèk¯½&~ûÆo<öØcC499yÍš5aaa“$D`2€(W²ÙløÃÞ~ûíñ³¦b” äææ®[·.**Š$IÈÑ/Ξ=Û×Û¡k¼V.##º1p×€(—ˆõ¨˜¯¼òÊ3Ï<3<<<¾°««K,R—/_n±X&¶ySˆøê½ù§—šNí“ÓW~ÄX©"§}ïÇŸ––6!mÜr¥àÇóæ›o¾ð v»ýâB1\Ož_ÖÕá„ø ¸Añ!¿ìIÅMáºcW%Y–Û³gϯ~õ«ššl I’ƒ!zL\\\ff&Ü% ×Qz9!àôž99t䤣×θ`ž’4i‰×Ì›–d¥Æ žåºGN·×4{œ^1ƒ!ªÔKSrŒ‹J•2‚;wx`Çn{Ý;ä0ZbµÒ9sÍss”2Äž9Ô¿cïp¿Í;ì™Äb’Í7ÍÈ’¶Ÿ´í9dï¶‹‰† £åtF¾¸PcQ߉ñûŃªªÊÊÛÛÛÓÒÒÄàL#†¨yŒF£S»p=¥—x‡œoüOÃûÞ”|}f¦V§ÀGܵUCyy”6ª,”X&Š¥hý¡®ç~ßÞ)Q”iÒ¬"À¶7Þ×s¬…ÈÏ–Ë5dB¶žV¼|\q³âZlNNRÈ)1©”\=-§Þÿ]õ;û½ÉËYjJN”1È|{÷'Õœ¹º+Í9}5'~ûOÝŸÍ‹úÑ¢´øí®M9Žóz½³fÍzì±ÇÄøƒS&“1 Cܬ _ ¢ôo<ÔûƇƒÖeñßÿa´EIc$q±µ(ÅÕ2¸ J×X¾³^˘À óŠö¶ª>î˜Vª[¬ºÝ“Û’$Y\\\XX4œŸð•@”^ÀsÍU#ÝbFœ2LNŒŸÎÄ0Ai”Í\`Œc'ZÅdôœ«q“JcR #?ù¼»„HÌÓ/dæü’`gÂK ƒ—íôq×-69ŸÿØÞ¾³ƒÔ‚¬ùñ4ºÖ ®\“H¥ƒgöþù(Ÿ4wnHQ*ì}6y˜‰¾°¸{@”^‚a£dÊ–Ee—™:[FO³ïßÛàÄðkÏXZò³GÃU’±Ê”$¢2ÂIÐ.[ï©=c?|ppÏîʽ=n?²F§ÎI¤/]ñ×LÓ¯Ù ×0—U¥.ÿ^ïh•ÍwÍ6W¤÷­S—Ç<ºÉ¨£ánZ0>$ÈXf ã™±±…cWÒÅ¥´Bk5ªqlHŽÂ-ÃÆïÄ.@cïðqç#áÏ ‚_ñøÞªíÜÚ¼è±'òŒ4wrpà J?Ãp’Pé¥iz&5ǰhaØ›/5þñ/ö v®0¤™Èó%"†Ñ2Ê+þ§œ1×4;·ýéç[U ªpÏH¤%—mM®Ì/Ôd—jÐÀ¨·s‰c׈Reë÷¾öF¯4Ëòø×¬É( €HpvŸÛýñå#æ¼ÌøÁsõ¦¬ùsS¸]Ûö6÷²HbM+Y¾8W\Í=ÐøéŸŸÝårr}îìû•¤ÈIäì>³ó³ƒ­}#~Sâæ.Y˜bV!Þ_bïîcç./!‘E§—,ž“è8óÉŸž{ùÓ¾>w,[>sZÔ¦Ü ˆÒ ¾¯ÅéÑÉ£ÕãC"ˆ¡Š…ÅkW­µ–ïqÔöù]^ñ^༶®€%Z&=Ò“(˜â•QÕŽÊ? õù’ÜÔþG`¹³ûº_}k@•aþöKŠ‘„݃ÉôVÞûñûûpÉC:·‡émùpß{;l‘=²†¬ùàÅWCŸŠËLZ—Yš!wîÿõg]M?ù ¢¼oþ÷¯ËýÉ_ûÚ:ƒ¯å½—^úÝ`àçß[O6öÇç^UÍzpÍÂÄîŠ]¯¾ø›^üŸ¿^mÐè¢ÒM³-žžqõ…Àõ@”^À³»º-¦'–¨éKwìb2ÅHª¦èà`Gh¨ÝñÖ;Ž%Åf™.íi0WÈIJ‚ËÕäM Àºý'>ë~oÛPX¾åÁuF«Æ;—à£×He‰%˘oðõ¶ÿ—ß×ÊçÎO6 X¼ðöÖã5=.SR³rŠ­òh¦oÏW~ª#›´m/ïNýú7J¦'KX‹£ê­~R~fùláÀÞƒ5Ô¿ý¬$5QŸ¨ç÷mkëîÓ÷,Ñ)RÂX"£Âd·¿/3wˆÒKFm¾êŽ¡ÞRE”†¿Lij\CÕP»¥e+ºàÎEàù¶*Ç©³îT£‚ÆÏ_‹òô;ÏÔ"‹2+CúÕKRõø÷~ÐþúVGt‰yõ" é ôyÏ?FHp†¢ Xë4Er\ÅxÚ{Ú݇w<ï?ƒF1Ur¬†F®±U‚§Tp•ɪ èÁîaçHW»OR¨ ßB!ÑY#<½-ö¾Aÿ`·6éåÒà¼2•F­émè÷úaÞ7nDé%œ¯ÙÝþ+ÁWV ‰µÐ$Ç¶Ö mûd@cy`µÑ( Þê!AÇ[¬·7‡e¥Êõ |¤ß}|Oï‘|寍)Œ¸.À9†üöN\=àöÚ2J"ë9Êù9‡Ý7äâùF|¶!¿”¢œµ¶wÿÜz¼—trü“G»/o’izØ£_‹JÖ@”‚KÄP¤–´yëÿî[3é±ûHZrê—Và|ñ2C’ -«Á»Ž>à÷b AÑ’à, œ/Àç6x–åü¹¦~à¦A”^€S…+¢žÐÚNUnyg$8ÇŽšJ\õyƬxf|´#CœæÑ&8:tîPoåADˆû8R_ÿIì‚2Z¬IY¶ú`ïGŸ4´ò±Ñ ßÑÿü³îÒ¥ÖÅEj…ÀVîïùhë`SŠŽd<5}Ï=뚳ܒÁó*$Ì+Øzܶϵ ã#n(Àç1ªˆéVâ\óÙnϬd­DLHìâÕ†`d²mgª=¤.sšI«#¦›ùΖ:—.ó ×T4†'eFÆXÈ®¤XlmÇ@V¤ÜÓÓÔÑãΚ%—R…³¬ÏãõùýK¸a¥`˜)Uwœf¥_[qº8<[£‹]ýغæÿÝóÔÏûRâ,æèÔYóK•qå¡ÏÞþÓYÌYw®=gÉ#ó3#ÔRæ¯-þ˾ý¿Þ¦ T÷[7­]™n”ãsV®o¶ûäõ‘jãp{½»ìá%Z¢OH¡ÞýðOÏÿÁ»bÕ¼‚Xæê‰Ë×Qz‰˜„†ÿûâÕÄÌ”}á.†¤I-}íV|Hw‡îò×E2Êió¾ñë\NoÒIH§¹Ë¿õï %õí6'uæ¸0¥œ™³áÿZóú†F¬Vt_FF²VJ‰ïè¢Uß0§žmêAXRÁâøô¤©Xmê“ïâ‡éÕuv7‹§ÝŸ’cR‹ïé„™>ɤõy©“–„ó½Ü0ˆR&9ŒR¦Í›vÙ"‚’ÆgÅg^¶ˆ6eä›2®úYB"Ë(Œ»òŒÑXrK-W,¥dÆüÙ‹oU»¸w@”!(BQ „¢ D)ˆR $¥L1<ëí©;}¸ò¬$}Ñ¢i77šÀs#-‡Ov¸ V-0ËC˜0€{D)S †á¸`Ç;O÷ö[g$[o$JyÎçõiÔÊKóycIÝǶ¼¸Ï?» ¢€P@”0Å`eˆŠ7j¥½7¶¾Àû÷¼þQ³áá ‘òóãj‰y,×EÄ[ÃH¼óö5€{D)“ÀºÎÜq¨fÈíÅ$ÚüYK ,Á¥ŽŽêßhíhëÄMYkÖ.&쇷½··•_ñÈ×Èþ½[¶WuRë¾~¿Ê^ý×W_øk{¸–,Y’¡óúl×éæ~ûž²{ âSF:îÞ[Ó>à ™ÖZ0knš?[¾{ë΃DBñŠEóÒ£µža› ÕIüöªÃ;¶ÌÊŽu·Ÿ>ëÒ¤ÅG 5œíñÐyó×.)Ÿù€{ D)“]ÀÙþÎkov„ÍùÚ}I5µöþQÞŒŽ÷úeX„e´ûÕž”‘?Ú˜# ôîüôè´•æ&•ž®ƒ+Þ°&R¯—a„91½¤¤ FO~çÙÿþ´cΆ‡ï›n¬­;Ùçà#ûßøÝîÛ¼i¶~dç_^úlûßÿÃ7­aòÞsûü‰×+¼ý5¿}êee|sÅ49rÙX:*>½§åƒ÷>î’>úíÌ(㙿¼õ|–‘ú½3Ñ/wD)“ïénkéDùHa»8’ ä׊‘¤16µlîBIVDÍ{[zûº¨@£VŒÿFÊõ: ‰ÏäJÚ0%)c ññqrýg;IÌ«×/+µ0ܰ^‰ÃçhÛ¹í“öõ¥³r´$/tyý×{Öl|´¨°´ óôá»ÓuœÜ½ë} µf^l¿/>«0>ÆŠåR£uZéü¥1‚¤õÌ“ŸÖ8`þppO‚(=õ±ý½^‡+8»)!T:‰187ÆØ|ÝÎÖçáx I¤¤1œQ2ÁG8·¯±ÕǧB£âÁåïð»¼ÂÅ9ÔHšŠ‰“J0õ¶t¼¸*f´Ê J ‚{Ø×k xNâj=mÔQÄØ4ζ.×À/îÍ1r5)Øm¾!'%WÑpÞì%QÅΜUôì›/}·ò“«ܴzlŽy„A”„‘J[à…/ÛŽˆínU¤Çê/{;œ= ƒn“΢ 1 #4f 6âîéfZŠŠó^ÛõÖ¡êÆÔÞîˆô¤êÇ+Ïæt iÙñò±Ù샷/‘$A"9MáB€»=¯“D)ãË;ìþô–­'=Rµ„FÜ@¯ÏXlýáßD˜¥Ècwíx»ýÓã¸B"Åù6¦ÐôÐfk’‘p ¸ÚñöGýÈlxà~mçéÁCGž0¥™sŸ¨ö™Sô÷¯·Êt”E…Ûº]‡¶u¿»Í®Ï±>ñ U•D·ë}ý¯=-#H¯%Gm>V.]º1fY©’ø›ÏØÞþsÛÞ:´òѨhÒwôÐ`#Ë|ççËS%¥÷&ŒRÎ{à»ñùsíÿìãŸû•—zêѬë¬üçš™:¾Ãqñ0gÁà½x3/!‘à8ϱcsæ <ëDÑbM‹›3J³Œïy÷Ï#É)6¬{ã¹þòþîŒÈ¨Äh Ž 78¢TÜsðõå]/¼1˜±:þÛ”ˆ«Úݵ¿ó²H}Üþì+=IËâ¿ýp¸ùw¿ÝüÛ7šŒä'š QšUK¼G>ëÞY=tª^šœ¨-ô &Ã|éHýÉNFçÍÒGhƒ¯°)IWÜêØÌ·r}Da*3TÛÿò3 ûýš¿ý~ÌŒTf°¶ïé_5<ó"¦·$ÏI¤óf›‡+{?=6TynT™&Ï. ãϺ¹©8À]*0Ú~¬²?1«ôkÓÒÈŠ×úºF¼WM§6† )$ømCn7åt^|Û`Iø}¾@€E2KªIµ¯ùtmvºyý¬z#s §{êmž9V†m¯k¦Â- ‰z!F7§4öÿ³]™\öÄ̼ú=ï=µíдŸ.0Ë $@”pD)ây¾¿ÆÑå—Ä+#Ãi CeK#-=¬Á<}ŽíÛûõêEk,éQBô¼៾Óp§­{•Á`&1'pÜ©\´.º,–r4«Î9ÉDSº»ÿÝ*{ù ×´¥j 8ÿÈ‘a,N?=ŽFÀ™#½ŸÕr¾ežS Ò2˜A^:­wÿ¶ãÇ-%ñz Ã(qÃ$•˜mܼ1LÅÒO(õ_4Ù8¸»qþ‘ŠÝ8zܪ⫆ÂË 3ñúÎÞÑA¦º¥;OÕ}®Õîmmîvø ±™éº÷Þ}æ×¶ât¼½up´·²².×”fJMv¼µÿùß×.[°`ÝòS¿ÿèÉŸ å¤EûjZÃuþ¹÷¯îúèÄ Ï½§r×T-Z¾¡,A¬Z)yꌅiÛ]ÙYÙ:MDÙÌÒO«Ós$ò;úê{]žºÚ–"UÛÒ=ênléÎ K¼¹Q#˜º J†ar#­ðîú¨-Z‘Ÿ&×ë˜ÔT CBg³³¹É£ŒÕ{;+V°ÎAÎ gkÐÍn ’¦c²Þ(®ÄÊfÏÔï:Ýsà€mÅ,e”³·;N6ó9› &%ÆŽ²íUŽQÓÉ…Ï( ^ .lV¼UgH¦x&Ü9ˆÇd.—KüWüzdd$X^oMŽs:‡cü[…BAÄl™VÇ-¾oAm»Ãðù‹ssÒ)Û¹õ_ÿ…‹1kÄ÷‡!æþŸü‚ÓD()\?óþé_N4Ø()rfAfY›,JO1êÂuû‹ð“C‚J£Ñ$.zø_ÓO7öp”ÚšõíäùC&“!-uÓ"R*ºý<¶pÓ¼¼Üt l†KÆÂ_ü2&,%šÄ©Ôyÿ^oŸ¯?ÉÄ?øO l„™!I*mÉ?.aôrHQp‚( Þ»‘Xj^~ÊýÎÎî_TÚâRT3f†-Xž-b]N¾¿vøý—?¿É ³ª Luþž‹koœ>Ó˜ù~_ÅáK:Yw¤ßiT•æÈ)óøÁ^Ö;¨ÜÓýûSÄØM‚o˜OÊÕE›Ä öNýÚàVëììxóõ×F{q÷zAÿíŸÐ9Ŷ£'†Nœ~ù¿l«4Ü;K§#ÆGïŽú%›“˜øwÌÎÉ)]|ÿÎ×ÿK.qIÈ/ºSÌÏ >ŢŠ1ø«0ÅA”;ôéßǰaêðpñ4µù•4%\¢‹RÌ™vâ÷¿Ùb’Dæ$2bü3/GÁH±¹k¢~ôÁ®¡ÿþ§!µ^¢T’“¬hQ„a† ݾs }XðažïwzŠ%™RæÂ [ ÃòÃJ3úºâõù© ]\Ž™2Ãÿ+}£ó³Wê÷¿Eju”R#‰É4¬ZIO ÎíÛ}Ö+ þÜÁÞ÷d¬]”úL âW§×¯ÚððÓu•Cž­¿fš:<ž¾dõFNwç ¸å Jƒwðf,‹ùEF¸m(à ‰kŒ²Äx±@ –šHÍß‘•.vuõ¼@Ðd¸UÅÈ(Ì/%#2ßM Û®QŸ?¥‡©"4ßù‡´¥2J‰_¾CÅ)*gAdLº¾±Í3âæ+µtT´,\G`¯2Èæ<˜<ëàŽ™VRr/pJ³3'7¯ì¾M‡Þû’¥®ºfêgùv73oÕjq58µ ÀÝ¢4¸ïc´Ò4­ôzKätR&tÕ#´F^2[þ[Æ "rzص·JúH¥øß•Dlº>öê> `ê izͺõ­ gÛO~«#ðËò’„Ž”Zºlý†Muý›ÀS D)·žÉl^µñ‘Ûë†Fët2üÂe°›Ç ©KWo »ö1`*‚(àÖ³377¯~ùƒÛ_}RÅx¨±‹Ý,/ô²ªù÷m„S»Üe J¸-hš^¼tùéãÛÎl×a¼€Ú†ø˜‚¹+W¯a €» D)·…XwšÍæµ›}ᩆ!O“¥¤)}õ¦GáÔ.wˆRn£â’Ò¦ÕòÊS†/]º1++ Níp÷(à6¢(jÁ’eR†^±zœÚà®Q ÀíeµZüÓŸgMÐë'º-€Û¢LU>ŸÏãñLt+nˆZ­ÿžè†|b=-—Q·iÀE¥`Jòûý»vm={ö8MѨñàæ°,o0Ĭ[·I¡PLt[˜¦F”⸬µ•Ü¿ŸÅñ)pË1Ö‰ã¦Æhôn·`³‰/ìu'›œSÓ¥²='fœ¾õººFªªFFG—A”p#¦@”b––Vêp Ç7Ñm¹!ã‰nÈ*(‹‹›èV|e…''‡M½–O~--öÖVûD·€)cjDiRRj\\âD7ä®%¾Â$9Þ 09M(Žã‰d¢[\ÃÔˆR`Ò‚(BQ „¢ D)ˆR $¥@H J€@”!(BQ „¢ D)ˆR $¥@H J€@”!(BQ „¢ D)ˆR¾ˆ ð<þkÇDÚÀdQ Àu‰1:ÚÛ}¬rÐáb­Ì/މÔK LW€(àúÁçñ÷wô¼õBy¹#üWÏo|x~|fW€Ý×…DX|ܽ¢þÃã{úX^˜è&%ˆR $¥@H J^~¤oðô©Îæn—H ¥1h2s£,²k®Î³þ®ºÎã•}ƒN–”ÉÒ£rÒ r Žaˆõ¸ª+ÚëÛ†‡NÀTaڜ¸D³”À1ßðÀ¶m =C‰\3wqÛÓWy¶o`”×F˜JgÄXÕ¤×6X¾¯¡yÀOȵ W¤F¨WÏÎÏÚúG˜R3knJ¢.pdÿ¹3MnŒVdghí¶Î~OÒQ)‘…9& ƒÃ=ÆÜy¥ çª÷W¼üÒ‘3CTdŒ>L†ºkêÜ+¾¿òG&\cý€§üÝòß½\åRëã#¥ƒÍ}|éØê¯Ïzl}ªžÁ½íÏþç¶>F—«Á†ŽéÖçÿôdzŠcebÎyþúܾc]ÌÚG3‰þÁ†[{û°C¢û›Ÿ,ÿé™Jg£ÿvÇOTxv´%]I8Á9?zqÿ¾aãÿX¢J)ŸÃ±ý÷Û·Ö£ÙKU(0<äêíñÊ´+*ûæÃÓãtp1wD)¸×‰¦½¡ö™ÿØöaOØüç}+‹L4!¸úúÿôô.‡+ „]¹>×qüô3ÿµ·>*ÿ·ÿ<3/V1ÚR÷˽ûôïY’¬› 4á–ÔäM› ãÕ˜ßñê?¾ùó¿ÛQœG«õ+ÖæTì8~jX2­,kevCpuûÿôï¶½õîÙ¥‹“ç$jg.œ¶ÿÕ=GÛ‚Ï…a¸,̼d%Úÿæ‰Žàœ–•ÌË®}çàN¾hYÉË"e8×}¦öÙßòüí–t?ÝÏ¥ÜQ¥à^'ø½§vŸÞzr(ãÑ…‹Š#´Ê`É¢Âç­Ìê@j!þóëó^Ï©òꃭì¢ÇÓ³µr &KŠ+J5¾õrËéÝ«ó5 SÄæGŒQqj)…#©:!\.ñuØFÅX¦Å,$IŠÄ’Žˆ OŒÓŠÏ¥7-'êð;]#ö!1º¯LB,ª¿Á ‚¡Ä%MQ:«YMã(ܘ÷PKÇ®Ÿر§þ¡å1q²€; ¢ÜënOÑV'Ggå…«¤øxa$Y–™Âãy¯\ßÛ~ºÛËaBïárA,>Ð= X·mhÔÇ Rš1©½gÛG\ŽçÎtºy!xyõŠç¿¨)þKH$ZÃxžçn²· FPñY£Šl©µÛì85yUn#ˆRp¯ã^/¨p5uy5GP”ôšë³ìÍðú:jZwŽ ŒŸLåTákµæ%BàÌîŠ_©èäÑQjµÖºØ/kD¨É‡I” Aú^O€»ú¤4à¶‚(÷< ‹PÞ¸ªr¼öêI`©|Æú²ïÏ33Ä¥‡H Åv·þù™ío7êž|fîêB#Mñ{ðÁ'jo[ëÏ8Žœ$¸é€;¢Üë(š¶Æ+…C}u-£ž€ ý²{vļ ‹”qG‡:Y‰”VH.[_àÚz†ªÎލb² 3%KßÜ…K!xVø†W\¶á!@¥Ð()Sî0ˆRp¯#eÒ¤¢$ó_»Oí¨9=×2+MMŒ_0»¼yu7MRÆÄäĨßî;½«¶n¾%/F6¾ÎøÕPñ1yÎ?^ãŠÅ¢ÛÏ_õœ_Ãp©‚àýî®>—®«Lž¸/´P`=5'ºìNrvn¤Ejî4øÔ{FJ2fg?¼ªåßþrô—¿ô¯\”`–ï6rùdÑ–²lS°» ë43–8ÅäΞ¾¶´þ…í‡þ]íZkVSÏÅSÙʼnÆpuBœôȹڗ_1ÌNUôÚOUô:¼Ûå÷ÏÀ¢±-¡±ªóB: c)<¶}ñÿ“iö6üõåý|k´–áíý¶³£Vêr.f*ϺZmmí #šŽž~ùÝ:MIîÆ•ÉZèV ÀQ &³DþÍOVÉ,Çvìmzá7 £Ñ*"ãÍsVh[N×ìÚY_ÑÏiežý$}é æÄG$%þŸŸ¯RüéĞÕÿy´Z£•LÚiIÓŠ0eTĆ'fv=wlûŸ÷VX iY±Éù±é¾ú5ùX¿"_zrWe WÑ=n`33ÂøýŸ¨Å4´kï¶Ê(]NQ²rÞ†²ÇZýŸ­|ölSBš5#M“`l?ç<¸åH’¹hf\ðcð8Ë?=Ö{¬’\òéy¿|¸la¦F;à΃( xN5,!æ;?5oxÜíò°b݇¤LÉè4RÜç”jK֕ޝ)‘KÃe8F …™?KÿÆÏÇòâÏÓRZ­‘)¥$† 3ÖÌN*Éuùyœ$ÕZ…Šò¯Þ3wìYqm¸ZË ™+Kòî+Û<®3+0œ0g¤ÿãÓ‘ßrøŽKåŒVÃxŠí^N\C®£øQñ Z©Y¸¶xUa…!Œ U:…^%Á!F˜¥ŒÃH†1Y™+“ʘ8åµVÇ¥je¤úê‡0BB›£èË–H¿±D‡]ñW/T¦Ñ\¶X¡×]øÚ?6ì0­Io¤ñëýF€;¢ D)S‰ ðn§Ûáåy ÛÝ#.¿NNpb€ Q À"x{º·¾}tOn"¼sÀÑ™¾~}Vv$ Y À‚(` Á“ù¾‡ÎÝ´àü÷$©ÕH G˜X¥L%N¨uJõD7p9ˆR $¥@H J€@”!(BQ „¢ D)ˆR $¥@H J€@”!(BQ „¢ D)ˆR $¥`ªây²¢¢×ãá'º!7Ç1!h¢ÛqcúûN§b¢[À”Q ¦$šfòóg;§œè¦Ü‚ÀÅ åù©‘¥8.Gétº‰nSD)˜’H’ÌË+ÈÎÎè†Üµp'b¢[ÀÔQ ¦*|ÌD· J€Ð@”!(BQ „¢ D)ˆR $¥@H J€@”!(Br+£”çyŽãná€ÛJL®Ð7rk¢TlʱcÇ~ðƒÀðâÜrü‚ 0 ›è¶p·p8!n$¤(½|"ãöööW^y%ÄÖ®Iü¬AŽp›\^˜Þ\‘zóQÊqœßï¿^k·ÖåG®€ÛAL±@ p?xóQJEQâ‘2/p»Á§ €;@Ì5’¼™X¼ù(¥iº¸¸Øãñˆ.~ Ÿsn“À†aà^n–e9Ž3 7ñã7¥ …â±ÇÛ°aƒØ‚›‹qÀKRñCŸ2n«ñO™N§»‰Ÿ½ù§X†ªÇÜô€»ç!(BQ „¢ D)ˆR $¥@H J€@”!(BQ „¢ D)ˆR $¥@Hþ?IH{€Œ©kÜIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/rpc-validate-phase.png0000664000175000017500000022354214770023131024600 0ustar vladimirvladimir‰PNG  IHDRŠô5Ö›÷ pHYsÄÄ•+'IDATxœì@ËÇ¿ÝÓ‡n$¤A”PTT,»»¯Ýí½vǵÛkëµ;Á» QRNŸ}»PZPÑã}ßïñ®{vggggwç?ñÍ7lŠ¢AAu‚ý«€ ‚ HAPžADí@yFAµåAAÔ”gAQ;PžADí@yFAµ£Ìò,•J ©¢<Ò„ ‚ ¿;´PA ¥‘‘›]fµ-ó gΜ1b}Iúb,«¬§#‚ Èÿ2™ŒÖf·cÇww÷²ž^fyþôéStt4-Ïèn AAŠƒJú¿tS655õN/³<ÇÆÆ~ÃeAäÿ º›­Ðéééßpz™åÙÔÔ4ûªßp1Aùÿ![+µ´´¾áÜ2Ësavppø¶ #‚ ȺÅ÷ñ|ïν{÷Ö®]û;#AAÿ Ó¦M›?þwFò½òœ™™ù1 ‚ È ©Túý‘ [AQ;PžADí@yFAµåAAÔ”gAQ;PžADí@yFAµåAAÔ”gAQ;PžADí@yFAµåAAÔ”gAQ;PžADí@yFAµåAAÔ”gAQ;PžADí@yFAµåAAÔ”gAQ;PžADí@yFAµåAAÔ”gAQ;Pžä?CØžmN½ÿ”š—&¦èßì&»eºgœÃj78™‘œ˜"Sý6ò?ÂÿûÓªnˆžnÙsÂãw1ŸÒÅÌo-çCWBÚ×ÔùÕéBþAyF~*²ÔˆéÝ®~ÒàpØlYd’#0±´sqóöïÐÁÙUd˜ûÓ}{ÍÔpèxˆBG Ž ‚UåÚ^:võ·à•2i¢G§/^»õäå›ñ© `beñ4ŒLÍ­lí+;:U¯áVÓÁø«±Hn¯¬7r?›Ãá²ÙDnÊ(…\&—ID¬Q§Bzš:'íL³ºS’u5¹l›EdÏLƒ«wO©oSÊÔ«°]zöÊ´4QFè_Ží¶Ñ¿åReá@ŽÎ™´ò¢mÀÌc—"olž»=.U2Å«÷¿ôo±¤ˆ8Õ‡¤  flG•—…^ ‚Ô6¶r®Q§y›õ ò8µ:xÖíSìÛ¿FvÙœÈÔÏK7‚åù©°…ƇqƒnÞ ¹÷FÕN!8<›ü,e”R©IÏîÛ¶fÊP°i1åðÎ95 Š´]—‰ƒ„WBïÞ¾|úr´ªYGl¾€KŸNG¡T01ìݲrl_¢þ°{– ,^¤)qZôž9C‡-=-aJo6—Ëa±X\6!“+é´(är™T–£H¤áŠ3÷GûY–pƒ<Ç–ãûI‚‚.?w=6Mš½Ó¬fã¦^õÝݽuŽ†Û¸iƒn†Þ¹pùa €®›Ÿ_Sÿ¦­MJ‘£y!„:†Bº½—a–»£`Yü‰.íf¾¤·n´Õ·z9·]áÊBH-C3-C¨bN?“ÏeLÔÏEÛ¥ñÐ>Á7o]¿pùÁ‡$Õ>’'à³rß ¹ìò‰ÛLbâÖûÀѵ>æ9g²x*ðLõÌO¢ˆÜCŸ Ê3òS!8B6é?H|ØÄµÆ¥ô>ƒ9Ç®ŒkdO·UèÒSœ™ùæÎ•3»wî¼ô86üÌwÇàC!'ÛTÊ×ͨSÕodU?zãfo-Ïô†žMÿGaŒÄ"IVZLä«[A§wíÜø8æÚÚAÎAg.ÿãeÂ/”œÌÀMsÆŒ[ô„Ž@Ó¾CŽíÚ¶mèQËTës³^ñüñÝàËΞ;~òjE¦§eP%—Û:O¢ÿîë©×mwö>Ÿ©Ç··{ ˨I÷áMºCê¿mt;Ÿ¨;kÿÍ™~eÉÔ2 ¹–½%—ÆDD*Á¼èŒŸÂWrò›`Úw8¾ã@È88B·ËS±ª~6>Ô‡+KDIqQOî\=vx÷Î#wâîîh`8óà™™ªÝEƒ ¿”gä× ÎÈç´I 6—Çü©~ðB]C³*µ|ºtiÓ¾iëÓÏ?ɯ·ï·9#x|au¥Éæô@Ò _:/ ÿ´õM*ר߽gûÎõZyöâ@ãnõÒ‡xÝÏ-êÞaòñLZ­œ8±§i5³B߃ÀÚ©ý×qà¤Y§zš·>%W*Jyƒº]ÿì:h÷>¦æ—¦l¿Íê+g¼:ì,hVÙ²v)/ñ «Œ˜ÔhËÜ 8¨Øj`/Ï_¢Í”8qFwŸÄ!741ûzèo%]$…œWƒäñH’ÃryB-] {—æú·ZܩݴÓrˆž=lXý§}í´Ê/%òm <#jUÄ ÏÜsQïz§'£·! vÄŽlúµhòÿd»oÝìH—]ô¶8hÜ’·Ã¦Ø9š<¥£J› é’ÇZT+ш Ì´3‡CR…¬TwÄ`?k‰×¾¡!ôVBôٓד‡×Ó+!tÊ™’eÖ^õ½«”ì;!¸&scǧdêèj|=tÞ\C75êÒÞ#Ï«öÿ¨¿JÁ÷‹-lÑvãΟ{#‡øëË.<òêýÓƒ ¥åQ_œ[×åN:¦¿M:q#}p»27qtº:À®7̦äÀΗSf;æßêæ¿0ƒ‘Ê¡×ö1õJa «U§.HR2$¥¿ºuç従ë¦Òmü„çOöîÁ)Nã”i›÷R ¨ÛsŠeù”eÕf’õ£ôùÞÚ áÕ~Plß˸buÇsožÒÛg>¤†zqwh†üRPž5ÆÐÒà£j3):…VÈ2ÇÀ«T… o²þñs ³KáG+Ÿ§ãc àJ‹7+]\æÿ$ÄRú¥W)ŽŽKÛînAëîR ¸²ãdÄŒ.Eq¢ÏÎÞ£ØM& øZø/‚ÅÕàü€¯ìÊäUÑßÍ÷BhXëçvf¼xP¸ËBÍàÿ<(ψ£Ö-£²7øX|ZUTòLɤ9¶HòÏ?É9î9Ô_»´qi–Ñ”šä·mÕ~Ö¶» b€Øg­Üí_¡È€o®­ Œç9+\‹:šùâEÔÇw/=~öê}\’DÁÒ5µ©U×Ë£nW»"mÁ‹Bžþøá£TÝ f‘PËÂÞµ¢!·ˆË}|óìÉýë×o>û!giéÛ8¸T5þDg¤¨¨x?¼xõôñãoÂ’Ò s[g¯úÞn5kX}1ˆ£¢øÑw×N UýLzuÿá"fòi]£N…<–Éï_<¼çêÕ›/£?|={·zžîÕ«W5Ñø&\—;ò.—aP@’.¨¬è°°·Ïï_¾zómLº¶EÕ&þþMê8kq H·2þõ£×ï?¼xüèùë°ØÄT)0µª\×»ž{íZ•Ì U+åoŸ?½qíúݧñ©R®aEÛÊÕ\\«Vu±6*lnÿáÕ“û·®]¹ù ê“X¨kZÕÝÓÓ­fµjÚhÒöŸåQcÞ¿MÈÙ4õÔÿ¦(’Þgæl +Úd—DzØÈ—‘9öË5[»”«y”™_;_³…ûß1Mõ=£V¬ò_\Ôm$ÏÕÃ*‡üØ`ß>çcè-Ûš j;Ùèp’¯o]¸u p ìûÎ?²aPéú‰¢g·Îžºp#(èJ,“'Üq;Öö´Èˆ \ÜgÔÚ“Ï¢’i«RÇÇJ›yïÊñů£²Tc…âŸÕ¨öæXzCÏÍ·^+#iü‹Mówý=Lj´Ù²ëŸVκÙá-hRgFˆ4§UJOi[#7ŠY¯©™Ù›â3 ‡LZwôéû4²Bµ¦nvdü͹»6Ó%•­kË9ÿëæPª{-Òø´ÜʆYÅ"Œ8ògçVí}(äfh¼øËî Kþj0iÇÅ…]òé«ZÖœ÷–ÞШ^ß§šmNzÄþ%{Ö.=‡z 6ÿ;Ø'ÝDVÌÂQ=—ï L‚“g“JF¼ÈGwm]O1ì±4úŸq¼|¢³ºçE¯Hý*^ÞŽ†‰÷ïܲ 8zu›öZ»{eMÝ“ˆ‚òŒ¨-Ôµƒ7sÔÀiüàoëô}sñ~î¦{û9HŽŠû˜1ßÓÁðûù5H‡%c\÷¸Âl‡/YpcÁÏ‚MÅ­™Ëßý ®^:E~”8SDZÔ ¼ÒÀ,§.!_÷¢[5§ƒ‘oÿ™ÖÎÏïMÛÒdϸëðy]‡)¶Otë·ôp„Ü‚]dç&4n¾ô½%ôšð0p±CžÉââûã4k-/Êl])J‡?¶>[Û“›à·ÿN­Ýsa܃ã,ÚÖøŸ±|Uª]§K¦ÃýÕj­yJ‡i}4òx@Å9¼ndçi;2”|ÿiGÍnËU(>æcßöÖÃãýk{[F×û!FÖY‰Ï"³7í[UãøbÉê 5ѪŸ½UVÖ‡£ëWü{3LÒ+‹Æl0Ô.ïL¥8Œ[O}þï\^N®ÆÝØêÝlÀÛ7×§Ïù«•ÛóÜ~„Ë›úLÙHÆkDsÍDö¼¹ó±‚6lÉK›yN8PqnÐi s&©?>0±q·%·N¯ê3ÀàÊþúXŠÿGÁ‹üz²ˆlâýýãöÜ`¶H“ùë|S™,;¼äPŽ)—vû©s:rÓÓb>åìZé?ùa1|™ß˜ZçUÝô§­y|dþ{Éø{Ü^` hÙœSLCž§!tñîiöå0[³’F‡çI’?ݺò< ·Ó÷“ÆßÙ3x=£ÍÇ÷ÎçÓf`ŒŸ‹3”"xÆF´o”Gáûö]F¬¹ŸñþÈÕ𠣪‹ê„-Ÿ,)tÖŒÍJЯÖsíܶŸ»Ý¹ó—vn>â€8ýöª•ëÎhR„š–‘„§—î„Æ©k>ª¹K!´]ö^|ØÒñK¾vìÙÇÍÏqì…h€Ø9Ÿ ]\+Oh‚¯¯Ó¥U;=Þ—ð&u}ëT²x{/:1øá˸4s›ìA”È­^ ÿÑé²ï‹6Ópœ&®hwæˆ(O®Po¯žAk>mÙgm¦qé<®ÛúC_ rdשçCz¹”z€ù­@yF~9ʬŒ´Œ´4f4¥ÈJýôîEè™}[wŸ¹™Eïë¿îظz¿I<Ü1aøŽ{ªMÝñ»TÉÝ/§eål „œï/ê¿JͳkžŸÊ4ã߆î¹ýnhcÛ/¥žùûIŠž¹_³ÆvÅH,;`Óå†B›|£ÄèjYÒ±HÒ´Ôx%8}ÿ(äÅ=ó£˜yfœ–݆;”a ßdÎ¥`MóüˆebÉVÝcæû U¥tU ˆƒã.©Ü|U6«€c6çjÍ…ÞeɃïœM‘41*­£Öâ®t¤G»‘¯U}Ûv͇v®SÔÛE)22Ré7'Ï.Q6{a3½sã @^yÖuêad—¯~Ej›ñ5™ Ibœ(LjžßLcþÕ4+è Ö¡íÆ«Rîç(”ÒS‡·¤ÈÔÐnæß*X“^V´<õfUÐ;”çÿ*(ÏÈ/çÓŒÖ5gÚK„®s«uvu.ÒIA(¥$U$áˆ3³RbŸÝ¾´uÙŸB™_O»å„ms[´T*d¹m’ü)æ¹U;ϯ5«Ù= È?>;zõnc[ÏÜ#²Ó‡ÏÅ¥+]‡Ï¯^¬†º+a¤³'» g|­ý€$Rï/¨& ‘N]ÿúe{v[ÛB;É\'­¥÷[-?»6$û\ï èt š$d)c߯¥‰äF¼2\ŠŒ”Œ žL,J|z|ûò9»˜ ÑO^ÓiЉÓŒJ}·¤±µ Û>NRÀHŽedS©pðÜ·KI}î+àhf÷D¯}uú%Ÿ<Ï•#4¬’'#•²wμ§7¸|ÇÊ?+çœ,z|匮SÚ@~+Pž‘_Ž A§îîVzLF°’ßœÙzô1½Í6­}äÂɆ¥õ+E$½Ûâ Ü’w—®…c]¯úéß$_ÑÉáiæ¶D%ÅÏpŽ¡cYÛ¯¹Ãýco(È\·éô¾žÙe´B”pòÄ~ ¸Ìœ^¤Év>bÞ…ˆMIË’2ž*•·ŸE”ÖYiø˜ÌüË"íjT*{•E–úúÅ›˜øOé"‰BIñ9ŸÂEÙþ[JÕ›S9ÖôÜ'{W¯ÑWÈó»"áiR¶}VªXuûe€$63É7ž@jšÔp÷nÞ¡ïÄ¡-Ë6hB”<Zôîù‹±‰i™b¹’â°2^$¦ ®յ!÷Ñe)d¶¬^oÔ¸!­ý|ëT.ÂáŽ2ýéÝOª qæ©MkŸs¿øÃ!H–èaŽm£ìcj™îù@yF~9šþC§ŒmÓp%tüðÔíì¥,æn¯I›¢v *]OiWh½ýÀD]™˜`q5u Œõ´ùB ==~¡¶‘¦¦‰ IL!Ÿñ1µ¨¹B?ŽÁ€¶~KO¾‘ÒŠzëï/gÏtd’•úrÇPÊdÄ’%vÿ†Ÿ˜ßsôÊI¢ÔÔŒ)ÉyE‡Æ¨6H–…eÙ†ãÇguŸ¸)0.%-5KúõàÅ!y¥ú— ÄÇfjc|˜•ˆ<=!§MÊÕ*ÕXB6ʘy ì¦_[ÓoÔê%c»U3S¶Bf¿Š·¿O*C ¤™ÙÚNQæÇ”Ñm~䘃¶»¯G9NNÞЩÆÐƒ‘@ò½úÏY>¥¿»MöD-åŸ \f_}V ´aõ.ïjöî6*è]§äôôäÄôäk³z{ÍWoÏ™]Ý*ätˆKÒ³;v´ýÄ+<~ð…òXZQPžuƒ¨õǬ)ûöÍ N'€ÚÖ¥E³§;–fÕcª´c°3+;;‹àXÆwUÈÉ'Ô¨ú?£pcÕ^=ιêô‡ôfÖ…ñ»£»÷bo{JfØ´­_Åb£ˆâîN›{€Þ0rl{üØzÇ/&QŠ ñ’f‚“µ\RêºòÞÚéŒ6»ÙÌýgg´Ìs(UZÆ6.p5r³™ï?|u¡Ë²À¬cR^òL…Ü0ùH$}·¾K.ožÇÔ0U,/ú•äUjµ?´Õ‡g!×BnžÚ}ørÂÄëÝ5'žÞîjÅ< ’¯•]:+”©±Y`[B—jó”gD 1qbÿ1ý–Œ­sÖ£Y£f4<2Çð¾ª|«A «íQ¹– \s&sXËÒ*??:b캧æw/8a¸t8_ë5ÝKeþwlÓ9£Äµq в³>¯ØèžìºÍØ·©ÇÇï4W.Ž-™´’Écãd`]{vEêÉPÆS ߺcƒZ__2E0md6[W_¯Õ6YZŠDSWóóC'Y¥Ï±{«j·} ½v8:ZÑÙ#t¬¦ ïÒèlN{ž¦èìÿ”gDM©ÓmA—õÁ;Æ$.ê9 aÌu?Í»fã#Æ9µXF7MÃ6÷^ÕåáHï¯: ͸˜©Ù¬ ·å2Q·ËûõýßJ!î%ãÒª÷æ%ÎY%99Á¢´Ì’Â}?‚šþuàþmPÊ2B_:­Á×}$›T 2…TZjøÜ>ïB+T [÷®4cúkzëõî¹áSw”f@C XlU¯¼R))ÍpƒìÁ€¶‹'>T³Àë¦Õ¦w%˜øšiŽgwˆ“|+÷†ÚǧQ¢ôKÏmÔlÿ¿òŒ¨+š–÷ßÝh>3šܩɢ¨›“J±îci©Ð|é¾…áí&IßêÑ¥FðÑz%6ÅwNG‚€û]…d…ê <ÜÌÞ†d¯Âe2c‘_Éá5mê !0 àSðîX¨ùeþ ¥T–up÷+høöï³èö?bJ~àŸÅcúÕq« øÊ\™ž% ”Q·^½êê^7Ï1EqNÆ8œ~óñYUê¿ÂezËÇÌjGæ­°~X½b×ßTÒÈØ‘ ¯äÒŒ'oHÛuÌS}Ë3Ýù3TFè• —n½­Ù¢@7Lô‹xÕ¿Õ›ÙäDÌ÷m3`ññå© »°l᡾Í:žýŽü§AyF~ |Ÿ•Ó@#¸ü¢•€ßpÞñ!‡Zm ÛDÚ©“¶·ØÐ·àò‚Üq`‚ä”ux®Ù¤=ÉÑ“–l~y±~¥ºãMïÜÄËÅÑ"ï ¯4-îÍ˧¡×Ïü=gù#€Êä÷)†Àv|€Ç®Ãô&¿Õœþ_[K×®•›þ¼«Iñh˨©Ž›9 @šyó̞퇮«L ·ÐÜ1nîwMC&Xœœ#Øœ|3‹¼Ûë´ñÈÎ{iÊ·g½|;/™>ؽ²…6Ÿ”d¦Å|çðyEN¹ÙCh5­[uÒ®[tíiÕ„ –¬Éu­ô’ô˜°'Gvÿs>!%;|a¾BÆÚÛ –< 7Bÿšr¤ÖÂÚÆž,—Y3k®%ÙØ¹!ËŒ‹|uöÀ?GCßdâ ¾¼›lH_±t¡“ÁDß:•rÞ{QÜ™eC¶3¦;eY»Ü€DNƒ{îÚ¿æòG=êâTçÆâiþîŽÆzš,J–•ž–œ”ñQÚ¸c[Ý2ØÚ#¿(ÏÈÏ…’G=¾z>ðzÈåK÷b³w%®Ó÷±‡›³³“‹{ãF®ù\4´\~ºÏ‡^RtSdûhÿèÀV 4ôoÝÂqøÀ™wo;•ÓK‰Úײmb ç*NÎ. }Ø—¦+œßrÂú:íÜ8{üâcËFvY«kjQÁHOÇÀ@O §}JLÍHONˆ‹Mek“°¦ƒÍw¶è\†p›pø.ÿ9¶ñW ŒkNßöêÔ£ MÿwÁ«„„L,’*ŒOÚ¸¤ÎàÑóâ¥Y»ÊŠêÚgàÞ¶|Qbø½{sþó½+vµ]jÕt±Õ åq޽{üšj‚±BtýèÆ#:¾µj¸Z22ATÝtê‚n;ßU73e/NŽî~FKW—Ç¢Ä)½®3ûYÁóH¦#cxëŽëïlà:xÆÔ;æß‘A\ð„îšzÍvd»±;ƒâõªq½7jèh ¸„R¦Ê…ŽûÐÛíÛ~õ-B~SPž‘Ÿ‹,uâ!ëÏ&±Ù,®q¶ßá̈GGßÝ?,ÍLqX$½7*ßK)°Ÿ°`þÍ!K“)z·èÎùý×O‰åØþUqqça§ôtù,®aN,@=9÷ôÚ‰L8jýá9ÝÝK§£„¡m¡‹Ž]$¾{|×þnÝüîíÓÆ#ÁákW°¬R»q×Zu|5©SéG,oÅ÷Y7ÖÖ#°V›ê¥XgŠà6™räe•E£æl~•JqtªÕo9tâ´ö5åϘ[›Bв"m˜Ån~aŽ¢•µÇ-6‹Çâ«2…PÞœ3úº$=ÅuKÔnó]^-ggiØlnöѧç—÷;½„4rØuñnKk&·x¦uþ¾‘1äܺï¼zï(žžMóþ½gLî ù²ë¶Õm‘r`黊-ý=UqÞít÷ùCfl¾—%çëXÖoÓcì¤Ñ5 !Þx»àJˆŸÌxum鼄ڞ [¹dϳ\ùè­þ€^/‡) ޹³G@·AÃú6Îé·åè¶œ|P6üåúEËÿ=}õet2­°,-sŸM» Öµ^)Æ£‚úŸ¡¤_0£Ü7#ñÙ­cOBŽ($úöÃnß]TÌäø¨¾îõĦ ¸Bcc¦±{jã„£kÇw^òxÓ Šq·þrõ\¢Ð×611VõÛ_ùÃÿ¤Éˆ=¯uÒhô‰èkFNXu<*UšÔmÑyô”ÉÞ¬ô“÷t‡óØ„ì}è¦%a&ŽMƶlü:áéÁÝÎ]~ðäuøëºý­alÛcüÄQG×,dˆ@êØÙñ¨ç¤ó+Wn:{5ôC²˜"@³‚S‹&mú Ú°ÒY½ QSPž‘Ÿ ×`þÞ7óKPÈÇ‚SÀ”—S …Û§Ú÷]ðÝÚ ¤ÿ~l¤ER{nà½þ¤ƒ~iWš®0é\À¤;ÙNï¿êœw-éÅG3õ£tji.W¥Ù;›ýQh·ã¾÷ù<¬å>(^›©ÛÛˆ˜¢Œ{Ëê]tü„ÐîϽ!–MÇ¡s6 SšÄ¢B‡W‘J Q´ *î|±³˜SL<æ&Rs‹“ð¾úÎðÕbÔò_›òim >>å¿D"‹%åù6›% ‚øÕ A5%++K.—ýêTü2hù¢ËMMM’$þÕËKžŸ< ;ÖO"‘—Ó%~è§{ãFø‘#‡ÊOžW®œÿäÉU n9Åÿ_…¢Œ,Xe` ÿÃc–ËäŸ<ÉHÏü¿•~º\ ùÕ]«s8œ_ä‰DS§ÎHK“’$ëW§å×@«r||ôäÉcêÖõøùW/¿ÎmÂÆÆ¨Ni¹]âw‹Å‰‰åXH}øp·Y³«vvÿ¯RðMÐú±e‹mV–ØÀàÇG1|ôS-¥òÿ´÷ˆ$‰˜ð´ «ÿ©^Ý¥ü®r74ô—´lÔ…BÞ±CKKËòˆ\.—§¥Ézô˜Åç Ë#~õ‡ÇãoÝ:;**ò?&Ï€½Ú?‚Ë.s¼l”_©®+ ,¹-ÿ°SÈÿO ›CžÝüF.+ßγëÿ6Nygn S®WQ[Ø,ò£·b‰têäIåt ºêCKýWNñ«9<°Ù¿¬ûMä\ ÛÍ2‰R!Wþê„üèÛW*èªIùöèèkòû9ÚÚ™•CÈo›d+eñl,Æÿ›àsEä7F®¤@ñZ¢+? %…cZÿUPžADí@yFAµåAAÔ”gA¤Ü €Ï+• r9H¥wÖ!‹ \.°Èœ  €QA—ìì¨(P*A"°Š—dì÷4N@yFAÊ8iצšvÿM˜Œd‰32äðõìëµ5¤wCNÎü[Þ^Y·y×–Ð×ï•A*ÔôéÕ¿ß(Göç©$$‘A+WϺpïq¦ŒâhW¬âÒêI³í3n ×ö~LöF’'ÔbSÒ,±H3ÁáÛñº}ø§P <#‚ å2íúó7_¿º¶êkd›ï¾÷ ²ž†¬9®gë +ÿnÞ`¡Á†¬·k†ÖÞüÜrĬ½ó[VÓdƒ8>rߪ^]š,í»ìÊðF•)S½«5âÌá¥M…ðþÁÉ #ǰx™X¢,\²*$2Šb‘<¶ªüS׺V·inݦf} :4kåêMÇ×´þd¾¡µµø?º µšÉ³Brißåý[Îm¿*2s¶©nÉOÿ”™ÈmóG“©ÃÝ*ðóV“D‹šþ9%(ÕsHßÀõž|ø|ˆ€´¨%ónÝwùÎ{¨ÕÌÅ”Pˆ3Eññ鼊ãvè\C3[´©Ì”]Ko¾’®¥Éf“TZRúÛhÎÚ[cÚŠl½~lç©=7äV5ì*ä¬%KM¹+t Ðø¹ZÙ±cGŸºuëÖ¨Q—ÎU"""¬­­u*ä7‚.¢Ö\~í%ž@"13$¬©mgʂ𗠖k¾àÈ“#â•2¾£‘&³ƒnLÑ-5ŠŽF`æ7`cãÖ}Fôô ^½5©ý üžƒË_AÍä™ÅkÚ£]cëèíõö›Õ}NûŠJ™ôù¶-Õ†lMWð7OªÊÉ} Ò°ð“\§Þ–7w\y‘äî¨ÿY3)жš¼ÈƆõ¬ËÖÊ3c¼ 9(eIáá½ü—tiéœ:¦*Hèp[ôþî¼ùÃÍPÀœ'NšÚ~ÇGl̓Û7¶x³§Õ»q댨“³Žìcx—9/~þ0Æ¥K—Ž9¢££cjjÚ¼yóáÇÛÙÙ•ò\ø ¦'A!ƒ¬Ü±‚¾8,æ“‹AšgÑ?„æãIÏÊ<>pØ@fÇ&W9 òÈçåxP*[M¸(ÐaA’ɇÅ}N-à‘ž/r’Z¼bO¡ä!V¹&`Z ‰…2$?xá$'''ºÎÔ¯_?ºòôC#F¾°wïÞN:±q9¦ÿÌg¨d¾îÏè˜Uwt6x}gÕSQ€S.ºÔ »š!VºøŽ2ãÒÅ—òòÑéæõfU6â2†cb 5«8W±yü_^P] _}…œ±§”JºšD‘^ÕÁÍj yxêîë ©³7; q?ä¹i ïù]>íèóôГäé>yW”£TýÓ̆R®ŽH–¾¹wm«Ó/^F)«Vd޼¼ÿ Ì›93Ú¬šUÇ×<¥išA¶Ì+åÌ>*Oÿ8Á14ÔƒÅýéúÌ¢5 UÅ«W¯V®\ioo?~üø€€mmm€©[iÁbÃÙ¹°ä|ÊR¼;Â’‘ÀR2ŸHf ̘g‚À †O„Nu¾¨×£}Ðo!«Ã¹]ÀÏ3_%‚µóàÌEˆJ¡Ôp…?ÆCeí/ž]‚•k 4œy§ªÔ„I‹ lì<‘iL·_5·AF‰%xøA¯¶`fd‹®0ª \R¦Kগó­’\ˆZ ˜úc‘©È©OðùLœtmšNíþi°ðO¸õ˜±.¡«lŠylt¶H%`>Αä‡=‘HtNýtz÷îM×™„Baé‡!R¹8Kž]´,R ÅUˆ¤R sÇÁÖÔåäDDQÒ,©DÊ„#9l.K)‘dÆ0?ìÜÚRœ&“©Žpù_5Cÿ_¡”ˆdRiî)lWÀáq‰ìŸ2‘4'6‚>ÄjªŒh”Ьt™‚N‹ä ¹\R‘™!Wª¾¦Ã=l ¾WŽß]ûéÞ½{ûöíÇŒãââB¿ê?Fª‹øZ¨|U¿bíŒrƒ•CÞ£EŒ_+âÐÏš”••E¿´?áB‘{s„ê/ïý ­FöveÒìÅKŽÿ=Ê_“C2 ¥"3îéú[•Ö£g r¦T}× ¯ƒ“-¢lõí¸‚  3ñÍÓo+wêjH7r.£¢T½ú„Ê\ì7Oõ5”çÂp •+óäµüòñm&57©Ü¶>±hÖ’Ó}ê”`!@?DF&]g3u1Q½&afm;Nu#˜9ܽ®>§•·sŽT„•|ýZ°µ·¿—V‰W) ú]‹Å¡¡¡eHær¹ryÁíß¾};dÈaÆ5PQ¿¾\^D²énóа)4èfuaá8`eKf0k"ܘûÏ…ĹW •ïö¨¬¯Â 8 [æ‰MÃæC'/h4¦m‚6L[ÅÂãÛ°s=ì„Ó`p òÜÁ†ä pMô“`å8Ô²r5U)…j3àHK!% z€l\YR¥Ã‚ 3u»`ÅDØ|öÝG9“6’Ñ[`˜¨¤Ž/:‡Kû0ŠB¡PlSaggçëëÛ¨Q#ú¿†††_†Hÿðèò˃{Þs-MšøWroi•þ4úÙ…{g/ÓE ¿Ù²€vî<©”¢òÈЈÇGî]‰­0ر²03ôJø¥‹1 eÙaLw!Sa$…øåÅ—G·=ÓòsmZßÒÙUÖgi\ÂÅ]žF‚…–€®ø(q©qÍúÝ«ÕqÕ¦ƽüðôƒS2LkÙz5²õhaÁ§?2YÖÙg¯$ê6imUÕÛÆF3åÞÉÈK_$er][Y L‚dIÁo2»wœÖE(“–WH?‰DrX…™™YãÆ6lHg¯¥¥å×O.‚L‰ ß|71‹1éPd0•!‚Åf[W0ò­fí¨C—]3¿ý<,0<]FRº •óŠÓ “ ã=+jЧoƒÂ3”,B"•ÑXlŽƒ¥i«Öf\%•³þÆÇDºÒC(I¿r›N6š_Þ?D<}µëmª”"éŠ_¨ÙÚͶŠ›N˜"ùÆ;qIb©˜Å÷ªV¹…UñE?ŸÖ­[Ó¹êíí]~Wa)ã.?x&ø5G·/ýèP«q½œœº]½6ôµÿ÷¬œ>ý˜¥‰!‡rqêÇȤëüGŒ4—ƒJ «Š†óv ©RÃÌP”g|x÷¨»vMϪâœR‚zÿøðÍGwo&H®ìÙµÌÑ©q#ïêìßÙpLýå™wïÜð³5×àf×»ØëØkýÕ´X<^@ƒkÛmOhØ×¨åŸé¨Qðæ#+$µß2²!/ÛŠ€h3´}»K¬ÜyËqm+ëQÓZOèZ™“Ožéï‡G—•tŒèá7¦’FXdä¸Ýg7Ú=¶Uƒ®µˆ~›N‹4¾¹¢¹]KÊ›E 0³±êÉ‹n³6ðq†Æ¶Ù]²-Y(%©eÔ²R–ßö7{Fù8jqNƆª ³TOO¯sçÎcÇŽ577ÿáWQ’µvqön?˜ž"6W(È?×I.“Úýg¹tÍÈÌf·4’ËÓÐÐ`úsd9YA¸wÚ·ß?K"•¨ªˆ¦–6‡übþkêи©y½FÍÇ05V’Åáj²~gm5–g*6<öÁ}HzórÜÁ65]§LòäÙ™MÜšw”Ýt`e]ªr›5÷œ»ýäÂ?Ÿ÷]瘿íK¿÷1-¬GjòðˆTŠÎ:3fsCºDÉ~5X&v‡Ã—_Øpzɶ/ÃßMë¶hÑ2ï뻹è}.¬Eüf/ä³è˜>½Kî>õ»úñèrÎÖÖvõêÕe=qëÖ­t"ïZTŒkÔ¨1`À€6mÚÐ{FjYœHÐs¬ëh¹uÈñA/I†à˜ä’<F·5#OǪ´…:³áÔ5Ñt~´-N–Ìïm×õ»ùä9ëœRÀþ†pïœ| Û.ÀÜ&ŒÖÒJlä í”ýÚçt4’àíǃBÖ›© À«Cíò ®@.—DÙ>âú?‚ÐÑÑáóùT¾éEA};Éî^VíNÓ~ÍuÏyqlÉ›Ù.šlU§2•Nµ­£ß¢žÁó=ñ·OÅTîbœS:‘dxÈË œuÆ?ÁaÇŸ ¹Îë¸ÅÅ”OÊs¯E’lûîž Y¿-£÷†&VZÌì…œ”º:Ãôº6Mæ(JçîáéÓ§»ví*S×4¸Àkª*izzú ݺu?~¼X\¶A Z´µ¸lxžµ‘¶®Bioiâk"!æ>Ø~ùy×ÞÕ5 ðùÆtRÉ :Õ,sGÍdu¬Tž èt)AêëhؘØXš>2SsîÏ¿ð²~'G=-¾n’,Cm¾¶¬àKÉåó¬l—5kz*bõÉWíû;kgÃpÉÀ »¾M}kë ÊtGßO–Šå*èªÏðáÃ}}}MLL455Èœ ØÚ†%‡aúº„Úº{Úó¾^$›¯¡Å/!_W—ÿÍÉTGÔVž•o&²ø“vüÑ qå ül%¡kS¯&lLyW;¨{×ËL )1>•ÉÑ£7MóÔÊ+Ït™kzøí¼úléí×ú6ú} €‘/¯éNM‡´yqj×¥ ó‚}G¸&ìþl@(˜{lòˆ:úôGýdæò=e,ÄË++«:4jÔ¨V­Zô÷óy B@ÉWTƒ-[áMo¨D7j9ðþ2°\ ‚F¾·Ÿ¯„-;ÀëP²¡_o¸½.Å@W+øá— X7Îz¸ýe'ý\n_ÛŽ`¦-?€ÿB|#Ð˽‹¦N(ä91d£©ÚræéîüÜÆAíbO”Éd½zõ411*ë-Œ5ªÀ{{ûöíÛ7lذfÍšFFFaï‚B–5Z`šþ”Q‹:ýß&¬ß÷èßóCÛH  +õp±Þ{áIpXZ#UyD²ŸBÎgxÌ7ÉËf³D¡ûâ@¿bev~¥𬽴nq?ÑÅË>GÎâ±^¸qá•`èæº–šŠ¢FKr£ËžÜŽ´­mSÝH©(Ý7A·ÉèÌ!Š÷Q·téÒ"»Ž¸\®¿¿‹-è¯`çêÅßèR™Êqa¥#k€x©DªdúŸ¿*wÅÜA>Îúœ#Ǫ¤2°²ñ„û÷’ÓÄJà}õ ²qk¯Ï¢fß½¿ÎÇjr% `³b®_üó£õ­>¦ÅŒ¯•DDDÄ‹/ʔôúÞùäÉ“Áƒ ú5öðððòòRÙ!¿µ•gV½ö^Ó:~n[}ye£w]»íÔöÕ1OvcBÆá)ë÷Z6âR̉ãÑ=Lò¼ž¹í ëL2÷Шéê0Î×8ç–ÓRÓøšZ\SìW¨l=pFǧ÷^­ |–µrKëÏÍyµ¿†Ï'rÛ8?úó`±X]ºt™8q"]·U­l1( QØ; æ†}=˜jɶÍÐhc ýY× 6ÄŸ‡kúð—#Ÿö­À|¬Ù]A®ÝÅ„]6¤g2"šcŒ'ƒë÷¡ëdÈ@ÓZÁ7ðäÔ·)säUr^kÒ¼¾Öo-•~×”IZEzôè1fÌ'''Èóhä²âì%BŸ/—N½}}Ÿ \~éœk'¿vú”R®iѹ΢í‘"«7vP2qÿh3çJÙÙëòŠÌ7±•8<6Qø}êi||óAYÏ8§mÄâ’ï¶Y¹-¾ú°¦æÊâ´™d“,%II¤Ï®ÇXÔ°R”zL@OOÏÝݽ´¡sÉ6ŠÌKõêÕ'Ož@+tvÃŽ](L™! *:6ˆÆÆZ좾o6ëý¹SãÍ›p-ôC—I’tyœÒ¹E ¯¡5sxóóãNOÙt·Ë2_ë䈦û“çÏö³`)¾¥€!ˆ°°°   2• |~±-M‘HNK8Êó/Dmå¹k|è¿¢³ ÓY’ý"s†/¬3¢æÙãçÎèê§QÄ×JÓ±ÀnÇ-~íÎG˜Ò-1J¹mÎîšÃÖ·æ¨̇.Sö_$#•%S)ÿîjÙøá„ ßl ö ìÚµ«eË–ßÙËT©&¸V„ëàYWÐünV„™N@åñ¡ÇV¾}`P‚XÆ26§àêd¨'yÉE]{/Ó($Ê È UšŸ3Äñp; *¿€ 0ÆTÛŽ®‡\€ÀûàcSæòjçM•iu  œf]ÐíãFuîܹmÛ¶å=EpxM‡z<|tÌûݵ LN“*ÌÚxØî8wlgtƒ¥•ùY ûvdy¯­,P2}üÌãP*dL•‹,b>}T%i9•Ûã |uèV¸……Gµ”›k¯³ðo륩(¸@.òøõýŽqé«Rž¡oÒ¬n»ÙF‘ÆÆÆ•+WöóóëÝ»·……ÅŠ›HÏL=ý0BS¡f&ÿ}&ܽjµ¿ZÚr¨|Ýo¯"b޳2Yl"ôn‚Ä¢åãÐ9ÌeƒT|ýÜýÀSÉ\PÊÏ”R²´,—õpðÝýºÉ?–£yol|{(Kê#*!2Jé«¢¬'Ι3§ÀZ+UªäííÝ«W¯ZµjÉd²¡C'|KšÊ3Ÿ«Åb̲4ø*³¬\ a >$…K¬Nùè|sRB€ÅÕÏNÜÿðɆ–u5US›>›Þ µ8BUëO¦íPcÞØ*í—žè3ÕöÜüZ@dÈ?¾2îÌ?·p·ÐH \shåþšÐZ$#½M¦#™‚ì³àË‚.~Ôôüä´¿¿ÿ÷GÂ2‚õàÆ8ú/Äî€>«A[yGí²"àB2ô« I)ªœ&¡¶ <¾;wƒ÷%v2Þ ‰¦¥ž A² å1SÕª˜›<¼Ü NÀQ0öe Âúà§„ UF $º&C§Ÿ.'Û®€{Ž’V£²Dñ5záZû{f†·ê (Ù|cD¥+”…[{HELˆ‰ùyLÙ nµ¾숦±Ã‚.̾ ·¨…O5ž2_Õ†–ñàMÍŒdr¥DtídØOðÍÓ¾}ûÖ­[»ººÚØØüðÙÏJ…<]$SdÅŽÙ÷TQÃ+a˜ WY°å*‘ÊÒÄ2º È9È._÷oàé“Djº(F.œÒ«Å$Vé_UJéåQ{hȇ•!—WTuºÜVï³Ô/ÁÝݽoß¾^^^¶¶¶9#¼b±¸ä³rEÍäY)=º|íô¥÷y…BÙ¹“šåXéP(Àg Xìc‹€ð„%öP`PïùÐò…N- g4‘ª2ì»á— ¼7T,Þ@Œ.¼BÖBhG˜äTÚÄмy ­ à]+çZ\¬?€¿ynñFû<ÜûÂ`¸C>¶¯“½v ZzŒ6¿ÝAU`PíÙ’.omÕ€ºeç½îíÛyðîI/‚S [ˆ ]}lÏœ»´üI‚ ½õ 7¶Dùy~œœÐ®íÁ½šž$Rj²ó>+“#’ÌjT$sGŽI=c[©$Í-FLwž8þÉ‘!vË}̵óÏKÏ®!PÁå7èX•±W(Ÿºå6óy×®]å5P:Ú]=t¶ìĘoÎzç8Ϧ€¶’.•*ö¬e,²™ s›fáj9«{ Ï©U BM€RYò4[¦2T ÷™§;·™ÙÊõáõ+Û˜ñÈ_2K×ÔÔ´ÿþ#GŽ466þùWGJFÍĆä¶߯íøÁª”ªü¥WgÜ€'ã²K©/ûYlWì¼"§«TÙï*×_^°>wLSµGÎ|\Ûsñ›²G÷˜ÓÝ=V¨·š Fä^KÊl𠦬Ÿ4e}qcZYŸm¿'(ÁÌÎ0èŒì l|nL,àsáîUè8”ñ"Ïç©é ý`Õkxð *ÕbäœÍcBÒÐMR ¡* "¸ú¬4Tñ˜¬$ÙŒÃR,.Uv2,6p¹Ì“PH!. În…}Ÿ û¨gÇ<ž¼ß oªBG Òrí±j·³}°s9 ÝœT•Ù6ÁÄÃD®²3 dòÜúOlÕCcq€­êog’Ê«§€ãñórú¢µ'6{8äô½e…F_:ÝškZ=º|ÿÞã:Õ*°ó@(%”Cß:úWBN]ú4±¿¹B”ãÿ„ÅãH^½¾¡pèR‘E·ó}þ´Ü <Ý&ÍX²<|ÝâçæW×£ùê4ŸKPÉc=Zr2½eKO»ßÕ!|¶aIG_Ï-wNÍ_u¾Ù¬Võ´”E´~JýêµÇSr*=ùL¿¥ÕgËjBCÀÓÕ䩼è}MYÙÔõ‹Œ¼jVáæ5 >§äWÔ°aÃ_sm¤¨™<3H‹ïK-r… .DEš3Lȼž*åEöxREE˜7žß¹\BÝ—ÐÐþËX2AÂÇë°|'\…JY1º;1s±()Xï™G±`<{ÁÈ1´NžeΚÓVæºW¥›lÉ©ð§^^•  î Š…ÑAØFØ­2^Þ0ö ²}¹ÐœëÂŽ=PÅ S0˜kCº!,X û€&‡±SK¹ s7@ ý0nAÛ.Ð{$´q"þž ·Bé&èøºÿõí"cf@H(s¹>ÞðÙÕý°’’aZ6ª¿’Íâ ÙD΋ÈI.›Ï' ’Å²É iöŒ'RK¯ÓðªÏ¦?.üÖÉI-ÿAV—'¼«åcÍ+P™¤”yü`V˜vƒ olyûñGO/i±ßêKï,¡anàý»M3U(¤©©bú]’Ȥ“³!WÀʧáºá\»¿ÊÕt µ&—-OLg:á>&¦ƒ¹Pz–(&U¤§ÉÿÉËí”ҬĔt‘ªÞ’;õŸ kšZZºÜìIú”,-õ“˜qTK0îò¼Ö¤ÀÈDWƒ ¤©)ŸDý+³­'Hºú¨£­¥Å&A!IIJÉPd¯2H²ÚÆÚRÖ !­eAQ ï"OJ+8ð3nÄ™.lWA"Mù74ùËÙæ£ít&†ED]L¬° ­™"ýÓ‘'âúÕíœ5³]“”(é裸º5jyÖ¤.=Vd÷6ÐpRkF› ÄçÎs:s%!ß½H7YØÖD iÇȽÝ+¹ þÌÍ •|íäÜeïÙ7mâQ¤É1ïÐA›—ã+)öþe²kàŠá,qÒ£;go}àFºGÀH{,þýý{/Ä>ݧèÑ‘ýéÞõÛoÞ+ãW4rt5Nõ¬4å¹_IR1Nœ<·mç…ÛÀµî3~}7?ÆK HâoÛ²pÃåÌA#†7¬×ÁÁ´dG)jÊ3‚ü”r…¤Àüb©\QLmFÁ´÷Š„’‰Jœ‹Cë«D^¸ö“­L®(òlfŽ/¢2;ÊPS(JC×dXsÓÏ¿s|±’%4éoüye /§b†°˜5X Z%›j•sá?ÿGµ¡$4M75)fLµ~ËçmRÐÊÛ¹äç÷Öf¢Z@/Þ­eïWóì=rT7¶Ê]ä>ÏöK¦˜9Õݤ¦’cìÙ¬—…xÓîëo/ñ·ÔÉîÙ’Fœüx(‘ÞPpLë·ìg.ÞtàFdë Z˜ éÏ"|¯g§eƒŒ+¹òðíÒ)kç¡b½úzôÓ“‚ú›³Š•[Ê­Æ,š¹÷m™Iæö/äfpŽÇ[;ÿI&ËN= êå;!ðs,S %Í'mXâ×{ãêyëî²J|º9ãbªÏZ÷ßF›åA)?H(ãŸd»Š‰q‘µ:@ìÙ©÷æ·®V¬{|Š`fðåg§(” 3é5ÿ¯k}ÿÚ:jaÀÉe Ó&ïn·1\Ÿ¿‘4”gAä£Z|™cOzz`°mº=:úÔ.¤ÎL6žÚüjü|ªá¤õ;îòß± 5ع¢B ¥±é¢äP¹éìvç.ï;ÝqYÓö'F¦÷ ìîÌ-ã*¿”gAäÇB…žûkÈ“âÿ±wàQmwÏ/îÁ‚^¼w—bÅŠ»»Cq(^Š+òQ(.ÁÝ]¢Äí|国MBB\r—d~OžËîÜÞìÜÞîüçyçøç/ÞGºµùëÈœÞÖtúqbdGnšÜì°DH˜ˆÇÑM³ðZW>¸t,‰ðgGw|V§Û‚NUKÑì7É3‡ÔuäˆÉ÷ü3¦”ÌwøÁþ¥³ól5I°yà\ªZúUIìÑ9VR‰±B³å„Ü’g–eBBbžÔÓMõiÚ·©*ÊBóx÷–MƒWûÖEÃò¹%Ï+úýûo¥­[cŒÛfAÒ(—Ë““F,ºÿ ‹îÝÛ±  “„`¦µ/ÉäúõÕZË. Ž^oê·aÚ¸†þ)'F2‰P¯½,ä¬U#¤“μ¶êfƒ,j±„?€”ÈÍäRHV§­gîJçD›Y€¹–“f‚on{¥±K Œ ¹Ud\À[R,5“ɸ F_ ŽnRä–<{xx®_¿)—2Ç`0ŒéÁ>=5gѪ]îÞONMìvï¯.³¶uYÕ¡s&‡-ýë·–•ÛvB½;~åV »‡åš~%W¤3àVÔ),¶uÁ¨ÿ]vs¯XóX¹&¿ ›YÌJo ˆ„G ÎÛÁʽDªCÍ+ïdÓ‚À å¾'‹ÉO3ÉC/Í9ýÑûwwÑö¹M/x×î3iU%K£-@ò`×0 ƒÁ¢Lã™û[ÌL‰…¦ã¯Ó÷T—j¼àxËÜÒyZd͘ Ìb 3²‰u*H¤]™¶¿?Ë-†«Õ¦ZÏ4XU˜¼ãèä,—1e(Pk>¹†YºÕ›½á²€LɇÑ­gL^ƒnk€ûÃ|Ø{ƒ1,H#•Y…¡)P$ÚÍfŽñ…8ü<\4~eÖoe>’M¾ŠÃÿ9Xž R©Ëß;ØØ˜Ü¯É² ‘:c@!䂸’ü˜es’-Ê!>ÞE,Ε‹†J§Vic#“hcº%FŒ)’J…: @7A–ÆTa€$³Z¬S@0¹ óLœ¸4<|í{:{î\¸±ÆuXV*“©U*#“XÕñíÑ­kîåÏwSå°œA•Ÿ» °ƒKT¾@(ÜÆ¥i]̦M›byƤn3=¹‘¹Nœ‹]bH(ŠB&oþêúXž1ùŒM›RÂÝœ={vúôé"‘ȸåÉ{ÐWFõšaóܼy³V«½víZ•*U ›3“%gΜ¹téÒâÅ‹ ›íÖ­[[´háîînØl–gLv CÍàJC¶lIq¹zûöí»wïJ•*eÜòä=R©T©ü¶¹ŸßÌ‚ ÐëîÝ»‡nØœ1˜,±°° iÚàÙ¢&»­­m§N žsÞƒåóLÊisÇŽiÛ‘‘‘OŸ>-„òlðŽ»{÷î…††¢›7oÆÇÇ[[[6 æsÐmlðºåòåËQQQW®\騱cè߯òŒÉŽÜx„rÂêÕ«Óï¹7oÞLëÙæÙºuëÊ•+ •¿QÀòŒÉÔÂ5µyϵZ,æ´Ý0ƒâ»Éd …aâ,Λ7ïóĈˆ¤Ð-[¶4È)0˜,‘J¥†º³fÍÊ”rúôiTwåë°EXž1Ùaj«x´Z-îzÍ9è—=xð`Ún›6möíÛ‡ª3†aL°M†)`°n‰‹‹;{öl¦DÔÊ|ôèQ¾Ž‚‡å“&+ÏÈ‚4v)Œ2; âS“©×ýÐ$I ôäk›“/0`ÏÜÆ?ODš}éÒ%,Ϙ‹éxng yF.-±GÓ4’gƒg‹Ád‰D"I?e ' >|À€¨N°°°ðóó;pຓÑn.-æ‘g`yÆä?4Ma¶žsi1~©ƒg‹Áä6fzøm$Ìîîîi»ù,Ϙì@õun„­Ï!HH óس\.7 OMzÁQØüì0ÆÂàQÃx”J¥T*5x¶FË3&;Pem‚ò\ÈÉ¥6îÜÆä%œ˜ž‚4ì…哦iK¡FwÁè¼ú1rÉ!À4 0˜ÜÒ‚ÔÊÄòŒÉ$Ï&Xk3 ƒšÞÆ.…щD¹Ñ¹mšM1LAŰóžÓP*•æN.¼uæ)HE˜lÀs1yI.y8$°BØÁØP3RÒc‚?4¦“K.©•‰åóL°s£Ñh ž'î&Áä1¹á¹-WcåƒÉ|t U”`ùz ¦ åóøà>¦3•y¸s[.—6ÂäïŠÉ/䆥k:5UÎÁòŒù "‘H«ÕšÔM_`<3ƒ7PL3¸:¦`“±i‘9að<–gÌW0µŠ›/L!WhƒËsBB‚………aóÄ`²§?Å_Ë3æ+ð« [YY» )Ð4;·ÍÌÌ(Š2àäo“ja .¤Ñh RÀ",Ϙ¯`ša·1L~ÇàílöYNÁù&˜\‚$I“š[…—mý›N§3`Moiii¨Ü0˜oÁàRZÀ*,Ϙ¯`mmþ)2úÀ0 îÜæåÙ€«jšš‡¦0`ff¦T* 8 A¡P¤ùXž1Yîãã£R©x×ʵk×òéH’’’D"‘QKWHJÕzÐ2sišFö.úuЮƒƒC3Ç«0yÁ…¨í^nc,Ϙ,°±±©Q£Æ©S§2¥wêÔÉ´=© ëÙºuë°aÃÒv‹)ÂoL™2eÞ¼y9¼ ¨ÕUÌL¾5÷ ëÌ…åSð‘J¥5:sæL¦8Ì#GŽ4V‘Ò(`Oà·Ó«W¯ôò̃ª¶&Mšä¼±‚;·1yº{Åb1èÛÜü¨Yïdܹ)´hÑbÆŒéÃú “ºjÕªF,R!ÇÒÒ²eË–ÇOŸhkk[­ZµœgnXG3 æK ¸}ûöi W¤Í¶¯U«V@@@åÙèž1†?˜¬)S¦L©R¥>|˜–Ò·o_#–'Bk=#fΜ™Ižëׯo1dvÐÑ ƒù5kÖttt ͔ޱcÇœ?×xb¦°0vìØ_~ù%m·C‡F,L¬ü]T­ZµH‘"AAAi)ƒ6HÎ…ö’bò;;»ºuëîÝ»7SzçÎsž¹R©ÄaI0…‚^½zõéÓ‡~ööööòò2v‰R(ÌZÒ³gÏùóçóÛæææµjÕ2H¶jµšÄ`r›#Fd’ç²e˺¹¹«<& –gLvôë×oË–- ~\]]]¾s»Ð*´¿¿ÿ²eËxŸ€*[”aAZKcÊÔ¨QU&aaai)cÆŒ1HÎZ­¶ ÝÆXž1Ù h^ž›4ib첤P˜;·åÊ•+Z´èóçÏÑvú¡‡R˜/)&ï5jÔ„ Òv{ôèalU*UAr¡ÀòŒÉŽz^¿~ݾ}{c—ÃaccS«V-$ÏÅ‹www7T¶J¥Ò¤Ö ÅlOš4‰8CMC ¬°ùXž1ÙáââR¡BKKK´a첤€W¬YTÂð$÷"<(^\²ªƒŒ4¤Š$¡yzæÈ³“ÇÂÚnêÒ´¸„Â"Á˜Xž1€ÑRýIq):ö‡ørYÄZÎtä— „pz È[BCC—5Ÿ Õ‰y—®… úììâçbȵ3Y†õø¹;uýX¸P€£ac )pnX-û8ÓË3&3”Ô:®óS@‚Dt4—"“Рѭ_uP ©ô³µêõæ:½ÉtÉÈÖê;½UƒÕkU Õwq$HeœµÍ2 ~Ë÷ÁÈœ 23½ ®Ï–ÒC$IA¿s)¼ã쟊…Ê}ݹ^€”¶×©_šHù 2¥dwË24ÚK÷Ó}Ö×r:($ˈc 2¨. =;}ÎÚí·ß·ÜÆ.öÍ\ ™&½’Ã|'<¹ ˧Á#-ô»€»®œ‚«Á¡LÁGaÅ(áT\~ƒç@çŠ~`·ÏÁ¶Yð¬Ìæ”U$†ëÛaͰó3 ÃA›v´fö¶ØáÑ=(Òý ¯`ÇH8±Ú@ÏIPZ§ÿ€]7Àà ”QAäÅPÎè‚«qoÞ=PÐuL²“õàÍý' ÒÉÓ¯ŒîÅ­Èx5aWÊÕ·¢ £L¼w:8™R&õªìêå*UDļ¸ŸÄzT+R¢˜9I«^\ VÑ„À¡„K¥ÚŽŠÎ°¤3TdÜýkañ*G-/O)ªË¢Ÿ…>¼£ÐVE*Õu³ÐLÁ½ÔS@™ΊÍ$‚ÜÈœ¡À­ñÜ•–îm ’ùÆåË3&4 šÀL:L»Úà) u®Ã0ú7°H(SêƒHXø¥Ø-W€4]}O  ^GÞïõÃÆ„ÎÂðåÐ|*Lm"¢OÃÙ©G“Ðr0Tk R€ÿÖÁä•0¸'+ƒ»Á¶è4üËKBäÿ`âŸ0~t. T Œí£WÁ…y@«òþ åv6n®»¨é[­¤“œ±.bõçƒkÏ‚bº±‘CXÀÓc›^Ñ[šÖð’»’ñK6¼qoÓ¸FsîŠË­­ƒ®î ,;¯“\$HÚ7àØ÷2Ýû&Æ\|îØárS–——ëÒ×O„™…îÚæ'Eg´(â$Fož¾·vÕÇ–skÕ²VráÒ)Ÿá *[Ì—ŠŠÁä‚ÔZü‹wßõŠåÖ╌Tuþº‰±ðþë»TˆYåÅD‹¥%¬åDà†óÂeƒçVòtbAh=dDä¨IwŽüëÕ½é§JÕ(Ž,~TtA».U%*-Ëê”—¶>{’èTÅ<æQhDÉ’ýÜX–V^>¯Y998  ýxŒyE7xq/DãÏ{Üb‘öâ’‹ŠVêËi3²bèä˜×AxéÁ–§"T}Aj©ââÕè¦0òwǘHeé¸g‡VõÚ l×¥%¡Œ}uáv´ËÀ1›û6óErùd÷ü9 …•Û•s’D¾ü÷zHñÉ ×Tw·Ö†Ì›<$¾t{_'ÉÇ{_yY×Kp|—†Ð<¼q8ám±f=ú¹Ó5»Q; 9ìö¿#foºÚ°y/™6ôòÐ;®T•<š;a”¦dã2Ľ³ÿ¨½Ì[0Ú6áåÿÖ÷ÚvNÓªc;‘6!üMÀ›>Ã6nW]ÎŒ ’?ÞØÞ|Þá75ÛÎÿuÀ{'öcèíc[‡5Þ»pxGAgJä,Ϙ,`hÚ–€c¡¼7Äw„z6œG˜" ~ß ãöAÛâ Ãù¥ðIÍïØEg´²Qš„†¹SA×~r.ÆÃ?—²ú<Áu‰ ÄàßÊŠRYÎaÊDž"£À¦zo1ZÚ¡u ßõGNŸ«_¥ä‹»a5šú ¸ÉRÚx5+ŠÄBTÃé? å|û45Íp×W¦ŸJÒ©ãî†w÷täêD‚U©’귯ؼ‚™~h¿JG „â\Ää_XÄ Ú5­¼û:ÝmÐïEÍűöè`›Ùk¦Ô®r¬´EÜö%=Ÿúþ{~js®ÃŒ½}ˆùˆß«][=ìñ•ÿ†Ô;»kŽ-ªº4øû?‰Ü³lÛÆ%—ˆôï0©v1s6S¼ö>MýÎÝôȯ͸¶å\¯Ügk.¸²¤éUû ÿ;Å\Bt+¯¨5dâÖš]§¶­Ó±…ß¶óQ~]ìc+ˆu'G8N]7®FÕ³U\%©ùľD§I{N®"s-ï$ÕP¾8ñN[}üÈ®ŽbªU° dN0n(LÝ]÷B—‰Ð½4¬¼ ãwÃ;˜¹âNO›Ÿ!ú\yñ!Ð7 †Î†ŠeàȘ4u±3ômÈ@;0*¯‡ [áÍ`Ñ Ê6†.?Ãíз-¬^eËÁˆº°z.œ- ã@Ãb°ï˜7Æöæ¹KÕ‚áSKUPá, ý¬c½9@¤ÊcJ"_í±lšjÊÝì+—·=zMë·Ê]ÈP¨š¢ulñÁõýÎ=¾íy‘‘%E1ÿZñŠ(V®uk‚¦ùOÒ:†aÅåzûÕ8zäÊöëÕ+5ô°2«ÕµØ“ ·¦ Wwíê&Õi¢"5ÅëzÛËq0ù F§‰¹XHJ,E@©uúðú™ú ¥@7©…½xuªÓèÄ;G§MZ¶nÆðPçƒÇÝÒe¢”Y{Z™^L¹qšŒ'"Df\Nn•5î,L?{ÿ³:I 3±øóÒÚ­[µ²ý‘usï”lY®{#ЙØdh,Ϙ/Âè ßf hÒîZ †–£¸Ç@£¶3 pqEÔ ìhÎEäA¢®Õq±µE"Šs(³ðm§¹•0X4:=‘ËL™Ì…í<|‹œ¦u\üí®¿q.` gØÃEœ§·ZÉE1÷‚U‡A¤Es!J °éÌ2oO=¹dëW^x`Ö±¬cH ÒÆÑÏ\pìCÃ6÷>'ªx9¼z{åYM_)C³ )©Û¹øÇ¶•ìFÃgÂR¤MŸ=®í}²{ÎEH×¶?õìXÄœ¦™Ä—ŽDoàzêmp¿r. *Û:E+$²—÷¾l>ȧB·Z£Ýn\ø;VbkéóS1KQp}ä19‡SH"¥JÚÛ -«–“°wß%I*»è´\T˜WçÁÖ¿¬„½Û{ÛšYÖê½ú¸ìiEÏ‚#ÁÕ \´€áb¹¬ w[¦Q4®kºLMØ÷豚k’›èA\å䇗lT¤èûÿÓEK8{dÑý#÷ìT·ñ‘{[¦lVµï:• LîÇòŒÉ"sŠNÃý¥î@Ú¦"9Ãa™FpГ£HJ—mºN¤äÄ ™:F éö8]WfŒaœZsN9€fµéY§´W]Ÿ´<”“5,°„H.´¶‚„ÿŒ]rµx‹?+y˜ Á§K†ãõ=QãjÛ2ðþÌ¢ù;ß¶™¾Ç‰`.¬ùWÕtéèVÀüïÊcÒ¼~1'Åúúõ‡7‹€ aéãl“ÁØEÍ}™LŠþKdæR1(µ@ÑöíGÿú÷”%g8NíßÉŽL ~óÈ¢B×2Ž)±îDr©•$¿99ná9·Æk«—°%¸µò¸[")çÖŠÌÔ¨Øq@É•†FW)UÂ[ûXž1S‰b†H]l†]&ËQw†Íb0½âf‘=1óŒËbd>;“%Èh¥Î lî¢T«(‘k“ßNïû³D‡ôQòóÈ“›'/ëg;%N-u¬ÜmÑÝ>þ¾,Í–¨X—]>¶î_Ý(RìQ¹Ï–=˽…@—_:ºÉµM‹Z_+ßvܬm.Ä'ÇOtÍËC³þ!5×n™×îEÛu£:•f´àí¿a;á¹üE»ÍÚÿ©åØ!µùF(úð•ám]Õ•NèØ°ÿ¿ h.g!ôÜÌ[ƒ¥uÀÜ6+{NêÙ¨&?Ê¦áø–v+È>Em‰,8,Ï ƒù!X$-·ÿ·ßËLï­µ&¥‹X§ûu]vø—e¤~`D«áFµvºUì²v_~úCscdúQ*Ë~+ÖŽe·âfpï°bw‘€³wuZÐè¢VP¾ùô½í¦óù£Sk4|8#6;ácû©HHy]ÎÞÒl¶@?¾¦Óqd¨E*$”/Ãíý'v’°&¨ÎXž1 óƒÀ¨““uIŒèóÎJËýeI²ò³!3øl0+ãg@¥Ì<^z….CJŠ—£INV'‰¤é‹ÄP Lõ¡ÑFÛsEŠyöQÚ Iò4ÄÚËÞ*+yfAR¢ÓÐúgãµ’±Kþª”ÔÁÁòŒÁ`0˜ïƒe@V¤n‹"uÓ§T©{­æîµÒR˜/‰°,Õ¬]J1S^”Ë3æÇ!H ¹aŒ¸ˆ„\$N†âf*£ ›²¨ÓA޼€õD…nÔÊÔâ`0L.åóƒHx›Aà¨8†—ƒø—°f?¼~EÂäZðè2þ‚X1J;üø‰èx8z.Ü„Rá× úú' :DB D$«ÒrËL‘¤X"¢]N¥a)– …B«Ôä°5#”ˆÄhZ¥0Ùþ? ¦À‚åóƒ°4X–‡afз3¼Š’©7 ›Á“(ÎhöiN‰Ðh$ ÉщÖÐ}0¬‡ÇÞ&Ö'ï!HBóâõ¶­/ÝŽvÕrf;;å‡Ð>¾w!BҹΜAÅ¢Ÿ¾;²âúý7LÓù[Õ”ü°·«Õ^ßqùŸí‚b%&l­cɘàÄ ¦ ƒå“ôËD²_ØEÂ@3xþá³0…{íÈôpqÀJ—¸Ôc{Û½!Á²¬ÀÝ­÷wûÛ.鯵C…ƒG~4ü-IäèÒI½aµB΄ÜùúšÞÌ÷C€XÌ[*ýJð"7X´~ò4zWÂ…¦µé‚ ÿ!w"t«•,c€ú(ïÀòŒÉ‘$â”™¡@¡Ÿr(”€T”²˜#¥ýà« ÇÌÂB¿~;‚[ÿ1‚©Œ{ü@oˆ«TŸ|4HH¥ÜsKè;´™¦$ ‘qkP¢2¨Ô…R¶iZ£¥>…n`­ŠI[ü‘¥Æ^:,Ki(ŠÍWõ&ÿ@E\_¿mõ­[wÍ[/\Õ»$¾Ý¹sÞùK·XßnKÆM“&ÝZµdÂõ·a•ú]žÖÊ™úa…&˜ÿL^ÿÏ©xªÊ’½[=òUCË3&7þ^»ÏC’–SG«*pp˜ áìï°ì?ÐR\ˆ€’M`þDpÊb˜Ï áÈ*˜‘*p( æ@CoÎÇ2ö L›¯ œaær¨ç¢ÄÀ³s°b ¼ ã[jÇØæŸòc’`AoØû ÷†ñÁ® ŒEsW^­S&ëh†[ôV ™[ˆ8·;-¥HÖê#eƒP&6³~ãR]ŒN—CQ4DB¹•XD¿–*Q­Ññ õ ̬%ŸüaU’V£¡¹õHBl&•H2d¨MV+UŒ@,”[ŠX´19Cè\sÌšۚÛ?Æ¢]ÒÊ»ßômnOˆÉÁaè”ÛW›0¬{Žƒœt9êbI¿. ^^9µäJ îëG›Xž1 “`V¸hÿ ¥lAýgssö‘$(¦í…&ž {~MaoeÙâr¤ ls5‘óðúk6Œï ¿ï…úE8ém<¶´©†·ƒ1càÒ!é ðü2&ü;ꂆ»[aó›t=äè p…U‹ iQHVå§¶p6$$½:°æA¢£S±.!ùÅ+ºÏüºžä«ÃwN¾`‹{ʨˆ7ž°-&7¬WAòõysñYüeerlòÛ·*ï†{Ž*mÁ0´*zרëUÝͺg×BlšÕÔÃѱ¤€º½þʹLÑRÖb–úxï]¼¯ÿ¬‰fŸò#‰ç».î¹#iÞ¥DÅúÖY‰Â`¾´êO½>|àO-“"J¨‰ªÕj r“Ñ:5•?W†ÇòŒÉÀ‹ËpátÞ ÞRHNÂÆ¤:š㺣5¹ƒ pï~ÓÃ#/O°µ° S`¸zêz‚½7t,řά \¬@ I,X `÷Rpn­+€*‘ãmá×ðÔù‹H˜3êÏ€ÚÎôy”¿| “”thÅ¥Çu—Î.Š%,M]Üu¡¸¥ž­Ëz÷hiël!C\P£c7Õ¬XJðõ,IïåZÔ”"›8ìøÍ%«¯ïótÐÁ’Êê ®ííg'ÚJܹõÉÇÅsäô¶£êþ+ý}KJ‘í̼ ý­e‰Ty&DÔû/åÖÖñ´%Y#G‡Â˜4¤~=Y2e¹5.J6GÔ‹SN‰¿Q0I¡X*ÕIë³JÓIR†Æ2¥§Œa§>! •yf3)‰ˆPã fÚ72–gL>„ Rà››,>>zuÔÁõ°í  á@…oΓå× f@ên‘±Ü“õÖn†P˜Éáõ[ ÅÜsE*àF<”p‚”¾S)øxsŸåBñ%œîp,Ú[çÊw7DRXäËWPc„»œe5,WÃÕìXI   àRÆ66,)(Œ•J4ºÎÞøyP ,‹û{Ù¯qí½²}E™ÄÜÓ›x›LEëXBD&þ3ÂÜÛÛ³¨L¿XM–¬RнXÙàhŸþ§÷oüÙJ‰wgú¯ŒfS§ cCƼ¿öŒ¶âIÿF%QñLY›Ë3&®ö ¸ùš”ç:´ÓVÕŠ€ZÉuXýˆ6“  …€:Î@DÃC (  “Ú1Å-ƒJ2¸ÎEÁÍä|„NZµ4o%¡÷|Ø×zÕTH±H´B­wŒOHnDZüîØ)eŸ½+;‰Å¢ÄKso'æ¬N§V(’È™¨-K^›µªÓ¸·˜¡™×—®éù %$$1tú‰plŠ j`êOlæ<áȹm7ªVhèf…½Â0YÃjÜq¯ÊÄ5Õ$*ý²T~¶”ÖÚ  §’ @7")4·“YqoU_•gô˜Û{Û[™étfuÛ ©zp÷íí+"»­²a<;þ6”dA£™7ª±â 5ÿX!.=©ãÏbýúTŸQkÆè(uŠÛ·”+tLîsvhmÝgKi™ Xž1ð©eáàt¨ÿ;”vâBt%%€¹XZhàÆ+hâï! Ý:".„'%âêsR¿M 'DÌ$!…Ü®Zï† _L*J¯p€º?È œž¾‚øJ@ÅCD'Kb÷ôvþN¬íµ¡]U0—ƒô›/nÔŠ€€2þÐñ(,ý üþ¥DÚù–•ÛØ:“Ìí€ø>N¤škÓœ£5A%%¡JÆ\* IB $ÉïÊV/£Ihc>êX²îrur,€›™D$ H’¤¶€(¡E•2äžðØèxÆÆžàu9Õ]–µqtñòv,³¼æÃ×ý«‹cˆa>‡~ºÿ  £‹ušU*³p—éœo/.Y½ÿ”ÐÜÑRFßýMZ~÷-D˜9»ÈlïD=ŽÑYèÙukç¾PØÙ[[$EÞ},A’Šz’$úºY§´qi¦|YôO¡/\‘ß;þuûuß¾ùfò3–gLHWX¼6®€Ñ½@n2k¨øŒ åêB‹ƒ0¿+l/þ]¡ÀÁ`Fƒú¼fáð)ü=áÏ=pCŠí°,:u3¨_þ˜¿kÖ€ÈVÿÕ@ǘ0jÜ?U›€ƒ40g5Ìí eºÁ–/€]r3ð,º@À!¸Bs䃮`‹ZÝOà×n0wÔ)Z:¹Y¹³ÃO?Ûü½íìê*Mš8YHˆ„ð$3o'sGGxqü@Y#‹¯ƒ(p %R!çû‚Ú+b´-ЩX±L "‘,ÊZ°Ü4,†fI™¥T}ðG*¯6Ýó¤’2¸ûõÓÇrItäõ«ì¤r!£ «­q¶÷ÕM3®uê_ÊËU jUdThb«ïK'HTѹ”è9$|Íú+Çkwí\T¨ JÇÆpÜmÃ~š¬Çê}JT—fŒè3-`F§:f¢„­¯W~î E³4Rs‘jÃÄ®‡”möÚZD ¯/;zg—þRЇJ?ü‚’S9!Ãs=`™rÍÀÄy0i7 ‹Û¥-+¶‚€L1Å”ÅùL"~Fp­X” KÂ2uÅ 4êþ?9y¢³µ¡ë0î,”~¸hÈF®/ •ÃÅ6LV ª?¥¹½÷Ão/ÛOr3ŸFe:•p’»xtêïµkϵßOX6SÍ¿tà¡ë6,Ž7ˆ{®"Õ»6y7mj}~çã'ɤ3ÖÆÆ·YÙ­lùn=…7Ö8]Í „Eꕟ3¿¼½ˆVë,º¬­ùqâí #"Šÿ\ö'ï/V8?x~mW÷âãw™Yyk÷ÄwAÚy»6è]éöæ«·­âÃé³Þm~²QÆiä2úü¨#ÄØ:M9Ø“¸ÊpóU(4( iÝZ¨quã 8´q÷`uÀ­žóC7“’üQìÝÊ…z÷42Ù²TM©Þg›N9#´¯ìiy,âùëpðvÍ䛢ëuû¥{ñŸbϵ¾yÛ¶òcûJLÞ†ÆòŒÉ$™Ÿ÷Óºt‰ÚÔÌ3§Ë4óŸÍ:+H\‘šu†cÐ[™G†.Ÿ¬KRP (AÙnµ}{ÿDpýÚ,MÑZ зo£ƒ¸š†&½[’¨Q‚Ì®¯›`Šbt:¶Ç\wnWÿ)n’§ØºÞà:†‘|o8²:´jš«Zâ]zÚ¿eõ‹ŒÑCÔé_™ÚgpÈÝ\û¬éØ_ïÎÒ¬NK±uÔêGr­"5·WeXóZc¸¶˜N­Óé°6c2CHʶéæ;yÃеî›Ûø•·5ÉÑ´ÔÕÙ¦‚D]¿£AQ‘2üj`B)K% !Hdn“B ÚFMm±Ĩ)OŠ$}럡´Ü´(RjBuõäæ{a6ƒæ÷µÆ‘ }ô®|çα”›s nöë€ ÇnZ:Ýqho'[’JŽŒR«PBÄÅ%QžlÑaëÆý×ó÷é[täk™¬þú—2"Xž1SÖRªÏÒfêjæãqj²8‰±:ùSÛ†«ÿÒÞb2Óé©ô J‚Ád ÑÐ -ƯZä¿MMåeü: :˱H“ñC[\×µí.Ïú]Æ”wß~¶aÌx²$qö-DFÍŸ@…õé×ýîÚénƒJ¸gÚtó_'̯âúóàNíÎnTg[ŒŽÒÉÝêMßx½e9iå2lÞ‚CF jSÌÛ¯s›Ÿª[ÂÉQ]Û-X¸VÙ1Ûvy,_4nH÷%‰ÔÆ¥Rã.‹›EþµýÊZ ›¶œÚ³™@l.€Ð½*†ŒX3ªoK‰ ·ô±2¿ZÃeÅÐ`Qqä±û#…÷AÔbýuàL’å&•P48øtZ¾¯“ˆïÒ÷ÃQPãÜ£ùü©ÕZ(Õåï'¿ü¶kÊ`yÆ`0ŒÁ u ü¬“‡[Y'ݤÀ´÷ÓwÇp’œ±{)(•¥‚2ÀOÜJ9,ýtC4*ÈÔË“þÔŒù$ú6–g ƒÁ`L,Ï ƒÁ˜Xž1 ƒ19°<ðÔ‚/ ƒ15°<ç{\]]Nžt•™¬Æp¡N¬%ññšï J™w¼¯4ÈÙPjÔÚˆx†úú¡¹‰@@ÐÆŽúODr¢2„hÂ`L ,Ïùž:שÓà «=™ B¡¢Œ¬U_ƒuvv4T^õj5¹~#@ûî[~Ì-X–µ±µ‰‹#ãÞD…â%íZ &ÿå9ß#‹=<ÜŒ]ŠìÐét"‘ÈØ¥ÈS ŒþŒ] &G°,zxµ†5j×j_·z¤z#O[ºXž1¹Ã0ׯ_¿zõê»wïÖ¯_olƒÁ|^^î§Oï|ôè?㊣¹¹Err’1KÀ­¶AFE…¸ºvÎË“byÆž€€€7Þºu«Y³fíÚµëÝ»w6¯]»ÖÛÛÛÕÕÕÍÍÍÞwb0¦B¿~}ºvÍSAÊ$¨­oìRp۰°ÈË3byÆ•Jóúõë'Nœ>}ÚÇÇgàÀ»víú–ÏÆÆÆ¾zõ*444,, ½J¥R¤Óvvvè•ßæw­­­Eé‹ÅiÛØ4Ç` ŽX±KQxÁòŒÉ§N:~üxbb"’O//¯¶mÛΟ?ÿ»é3fdJINNF&%%¡×„„´$<00¥ëRÑjµé7hš¶±±áU~ü®'$$äÞ½{ÈGÛÈG¯¨`nnnžžžn© www+++ƒ”ƒÁ` –gÌ7,×wïÞ½xñâÖ­[7nÜ@z<|øðƒæq1=Y¾eaaQ¼xñ/}0&&&""I5zEB޾Òo´ tÔ@"mù(C333Y*R©4m÷øa0˜ÜË3æ+=ztïÞ½III¾zúôé³páÂ|7QŠïë.[¶ìço!\¡P(•J•J¥Ôƒvùm$áè‹'§’¶6Eîììì”JÚ¶££c¾»8 ÆÁòŒÉ ’+Š¢®^½ºiÓ¦Ó§OwëÖmÚ´iåÊ•3v¹r Þ!ó{}2ÕjuHHHh*|w:Já;Õ]\\x_tþÁ§¸»»#‹œÏïHëÀÞm &=Xž1)ÄÇÇß¿ÿáÇAAAHcŠ)2bÄd7»\&ŠT*-®'ËwQû&ZOddd”žwïÞñ»4M£ÏJ2§Èd2‹Œ˜››óÝì¤ÉDÅ`0¹–g ìÛ·oÛ¶m …Âßß¿Aƒ:tprrÂë9A(:ëÉò]Þá<F¥RÅÆÆÆÄÄ -‹‹ã·h5ž¬­­‘ñLpÞG¯h½::,)ƒ1°<F 1¾qãÆž={®]»Ö¦M›•+W–.]ÚØå*,ðsµÓz¹¿¥RœÖ£þàÁ~%²,딾eÀo;88ðNõÈþd[äŒ)ƒå¹ ² .\½zÉ3Ú-Q¢Äøñã}}}].Ì7!—ËKéÉò]$Þè÷EFv\*ÏŸ?¿~ý:JÉ&^±¹¹¹M*¶¶¶È@ç·ó8:ƒù,Ï…‚]»v­Y³F­VwëÖmÈ!È¢B¦2ªŒ].ŒÁëqsËbq$ÏtF†á7bbb"##?~ütóæÍ©„……i4~²¸»ÞÁßF*ž÷_ƒ)là º`‚Œ'TÉÞ¿ÿÔ©S7nÜhÛ¶íîÝ»³™Œ)À!Ôóù[ÎÎÎYN6ãAwÚ…wGôè¿5ïµmooo›ŠÿŠ,rqF$ ‰Á|/Xž ñññ‡F’,“ÉéSºtéI“&áAeÌÁwt—)S&ËwÓb¯òÁyP£P©T"Ë[­VkµZôÊoó¯ÈjwÐÃÇQwÔÃÇ^•J¥yüí0ËsAÕ}ûöíÛ°aCDDÄ!CV¯^ê¾lla09'ûØ«Y¾"Áæíož{÷î…„„ðzpp0Òi~š¸{*i»–––yøÍ0“Ës~Uj¯^½zòäɃúé§;vàîkŒ)ð¥@+B¡°„ž/}Gð±WŸ>}Êï")4ß àƒ­¦m£Wy*|Ö´]¾ “¯ÁòœÏˆŽŽÞ»wï‘#Gýüü*W®ÜªU+OOOc— ƒ1|wwùòå?‹¢(U*jµZ•ޤ¤$d…óë›ñýí zІD"ᧉó3Íø)g(åKSÒ1Ó˳©Ã/Ç„*š£GnÞ¼9..®ÿþû÷ï·³³3vÑ0˜¼YÞ?{‰whhè‡øÉâwïÞ I=SH°ÝÝÝ‘Tóa^ø ¬¼œ‹Åb"#$Iâ#L^‚åÙt ¼uëÖãÇcbbMP³fÍ={öxyy»\L¾A&“e{U«ÕFEE¡ç+::š½zõ*¿ÞE ÍA-Þóœß–ËåiËš¥­u†Ú xqRŒaÁòlr ûYÉÛ¶m+Y²dëÖ­{ôèáàà€me Æà Ååçsgù®.¤âºt$''ó¢ŽÌñÈÈH^Î(5£ùÎó´eÅ]õ  üc¾,ÏÆ‡eÙ„„„øøøÿþûoÿþýááჾsçÎ÷}Ä`0„7¿÷SH¡ùŽt>ä*zƒƒÑs-ø%G‘„£67Ú@¯hmX[[ SA‡ Ó»Ó -XžÉ›7oN:uÿþ}ÔŠGbìëë»cÇwwwc— ƒÁü |_·O–ï*ŠøTøFùëׯ‘„#‹œåÆd¥ æ»••õaccÃ/^޶åry;L^‚åÙDEEmܸñÏ?ÿ,Z´h¿~ý–,Y‚3‰D‚›ÉLÁÆLO6±W™Ï@‰¨Æ@&xZìU´¡m S;}ÈU>+ Ïï`yÎ Ð3†¤>ܺuëâÅ‹111ݺu{ôèÑ—¢:`0˜Â{5Ë·­œMì¿èèèðTBCCïÝ»Ço#QG¦vÚ2'ÈÚF‚ͧð–wúµÆÓÀ±WM,ϹËóçϬ­>´kkÀÓ®ÜGQ„ÛˆµÛ!ãjWúœÙÌÌprVÿ‡ÞæÎνªžÂê—0“æÒùÜøcRrу Â`íCø¹)ð¥ä³àógؔϒDÖ§ÈF'®Â;¿”þèâ]è[Sõ¿ÿý¯Y³fxÎ[îR@¿y>^o =f H"ŸعY;záð\ç´“dwKd¦QØåÙˆ¼ùßÜÁóŽ \eô#uÝÇÓfÚó4R:¥©¿KË9k‡Uw¾¸jÔ¸Ã/‹8XiÃßIòYû÷žŽe¤77OY¸ã"hcZ´<á×¶“wü“ÅãògÒ}ùœZ4yÕ?j3GIż½^m×û?g87¥‚;áÌÅ n=aºÌÞ ÿÿÙÀ’~`¡‚!‹@à| Þ `JS%ê¿!Aû!Øj£û ¤\éª88~‰Ë¤e˜ß¨$X½^ëÀL‘APbÌ*ùå‹ÁÂá#pâè0ê14j-KEÎ?ãåË—<8yòä‘#G Åwþ ˜äÕ±9sývlž.o”åöðêØçíë&"(×óÛ3WÎsgÃo3û~ _¼:ôöþMïÿwUå/ï䯫ŠÉ)XžÅ«1]fºÎ»±cLu`ã7ýqIfm&L­pàÐCßeÿí­­?†¶.Ý|Ó¥U¥wª~Ë?ØòÏ^Õ-êyeëä·£Oçõ˜ Û¶x•4å‘Ö]™Z»ÛâΫnoâËýºÏ—¹ MÌ|òˆ§°ý#ü; D |R`æC|áìYè5š§Ð¯Ó zÔà*„ˆ+ðÓ!èR*ØÂ¯ áÄ3¨ßƸrǨ¯s=Ò™ tj {Ž€DÄ}äÍ=8¤‚scÁŒ€ä—°CíÕ  C;xpBÃÊZßu7oÞ¼ÿþ   ?¦¥K$_¿Ç| õë³£ÆLy&­òû´ÍTU™¹f\ÝÈ +ûMÛ©d½›Ù0¿×ÛMcúM[ó‚t{ºÝsÐÒåAG^ˆt_ub_uÍ•)£—þï–hóõ}5¨·óÆ :ò\8mû¦7+‡žSµjlóïî³mÌOÚ1ïøÓ¿®SÖNíjõÙ=tfåoóöÇk´¯&«×Ï)g#Ô½;Ýã—©!Bû2ED—®>3÷¨9oÓš%R†cBN/ýuÖß±*¶TÛ kgvM::«Ïœ}vδº2|åñ›7UL>ÖoàŠ(¡X£±¾zCϪn‰ï®NêßsopX@¹Ê? îëuÿß^{;»ª¤÷ôhŸ0zÅ«85m^fêÊmÊÛ3É!‹†õ:ôPÕqÖÔÐ?œy£ª×îÊQ­ä™Œíøk#ûL¸¦Ò‰ÝÆ,Ûêû~}ÿñ n+ìÊŸü«ß¾Û£Ë¼šÝë·ã¯ Ñ•n7aͬÎÖ >2¥û¤½gÕUîW®?»§ß¦Ë“Ê÷Ú¾b¬ðíÁ±£Wߌô=~{U6dýˆÁ[®EÒ,Y½ßÆÃ*æÁm€1Xž…N¥† ;CbË:XZ Ú†K£i®3˜Ñ¥#ðmÚ$9!1:Fe)aQu‘”Ms‡Q4ƒD™N=Œ¢Ù´lU›æíWÈJuîî›òÓú ¹°Q“ùäè³ñ¡pÿ#ÔsY1¦Oäs¡Ó²•A÷ª„lP0b µ £ô0\#žI=ŒaÓåK›Úºgˆ „ûQPÍÌKÁo_»([®oœùÚqF¥R]¸paÙ²eW¯^ÍòµZ-¤Ô÷$™EG%‘Ug,¡'‡¤%¯±šl²kª¥}»êÅo…$%^™äÚpËægÑý|Æ—²îJZü7wùàk5ÿóìè†è#q!v‹~{šˆna›:Óf*ÿñ”ˆî+»’ÓÆÔœ^aáìi«üT4ù;ö÷_lìrújôòiš^[øóocê5ªókm÷ôgûo`ÉvG^>mæI^\Ú¡|é_^…ï)Q¬É_ÃW˜õÑgᵫèÝS;´ôi#ñ|u9$ŸæéÿÏî7aݼc†³ï+µþg⬗6u™4LÖ­½‹«¥J Oü†ìœÔ½äÓýýʵœé¶Å¡Xí9=*¬¿i¹ïŽZĽ°þ£ü&¥þY Ÿ¬r.?vè¹èí ­£æ­Ú`ß«ëÍ‹¸O™ÞvVÉQv÷TóænivnfË1£ý›ÖêPÆ.]ÙÕ¨:íå‘%“nÏ[ÜcÌŒI‡—Ìr™eîýè{§ã{Þ¸ÕG 9^LÚjº·ßš^EÛLX´éÏceÝû½"×Þµ»6­áí .^Ç1mÎWö=~{û·;Iq·Äì©ßû93¬"!/À`y6eWïÞzÐ$ë·éÐ¥ß_›øè;¯>ÁjãwÍê³æÕÏÕKÚJ£ß1àüõ®­˜;¡J‰°D«´yÉòÌG9—‡ß|`Ôxð*õ*B×&Pä³þ´ð×ðûƒâÖ  ±ð;:ÖÐ÷ðö…¾w¡ÿH(U‘;E·&àš½ý­hµÚíÛ·O™2%&&&›Ã¦Nš¦_Ño×ï•áÏGm…, z”˜i ˲¨=ñùÀ9J—ËåŸçŒ>.gþùÐa(ç´Jút” ï—@QTƒ >ÿlzÔZ\JÎ[=·ºÓ-øåÊî%5÷C·*X ݰèâó sZ! ¡µjþx-ÚI- F£ýÔxÓP@ˆ._1ÄÚýüM£ª.®ûë–e—º¨P‘äöåÊ»X˜ÃàÙëöþã7dãû{£mMÜcÕ`U7ot¸ÝàiµËmº¬˜ÐT­Ò¸vŸùÇüfš¸X•Tdá>~œ„{6œKþd³-\ RdÁ¢f%£B QsÐjtlÊP¬Ÿ¾!®øôÉ 9ÓܾÖÈáuç ^wÿÃ’ŸÐP3²F×ÎÕÐu(¿ºÃ$·×ñÉ褟ʮ}u9JÑñhÓÂoÚÔ*.fJ£a€Ô¶nqä÷ÛHj׳„è4e´†eu*%º¶ÜÓ}jêju”þBjÇZØ$ÄØÚu?¶õ³^1LÁ˳Ñðé±òA‹ioïü·`òÈþ»ÃO/š¡æ~¾gè›ë^<ëUÊàí™i«²“£DRÀ²\-Ù´«ErþüÒ.ÀªãðïC855£øê`Ï¿pÅntåÒ`é½ïûz2k?$À¹s°úœ~ GÇ!Ö¼‰D½zõj×®ÝÉ“'/^üìÙ³,›>}zš8¥¹×¥'ËDVOn¬ÓéÅÿùñ(½õyIII™”í&&&¢·2¥£+ŠÏsæøË&gµZ]§NìåYÿZ$ÃŒ´q>K¤ÕÏgUð] 4¨ãÞ€y)]VWàKh9OxË]"Z›rÔ>ÉœMòókq`éY6å'´´w·“_>ý ò %1)ßÌÜÆÃÍâй—0Úûa2‘ølj…J¿ÊèW`_ƒÒ·(Y×tØp³Õ¡÷&Ž\𠘄À(š]ƒSó0"JT¬jjÛ–t*R<ø,ºÙ~î·J_xFË >/¼¸Þ†‰¾õÇV·X[ã·I f l9s¡â¿9öœ}'³do%Š[›3Röú.n;ÀÕqA³ž×,ôMË8cò-XžE³gÚ2e+4þåïšµÕŠo;8}¯¨)ºªŠ|š®EíyÇòŒ0û%'»Ÿ+Xo|õôöK¦‚Ï—õY“Éb°³ƒ¶mÁ`ÐqxÈ‚#Þ›‚¸x°.›r”˜V¥úƒ§îðIHιŒG™”ìí¡KpÑÀØëðœ_ÜoHZdz~Ñóþýû5kÖœ8q"<<©WÚaÖÖÖŸÛŽ˜ÿ³wp9}ÿ<«½·Ò0J(ÒNDe¤Ìd“ R„He‡Tfd„ŒÈ^Ù{ï™Y„ÊhïñÌÿ½ÏS)Êúùýñs߯^Oçž{ιçÜõ9ßsÎ=çŸÂ-•qÚ÷à c ¯:ôù'´ø k5êä§4úçTÀð™bdk0[¦Òsÿƒíkí®¹Á»é¤íÐdõ½óž­sïnhb÷•ÔJ€È Ÿe ¾ºI‡…wr†&†Ï_°Þ×ὸs«Ükì,Zѧ݄”aùãä‘;RA¹¢îT>õ%3^ xyƒcÂ#V®óÒŽ?ÿäm¼!Õºýß…’ç_EÒh»Å)gÚ)@›üò-œl5@{+A/{˜ø4§«\JQËB «–o=Û°§Î³ [qºâ’¤VÓhâŠxròÚW->M»‚tqQ[(³ïœémOq)1ÉÖP“ÉÉKI.leo^kbÒ·÷±®a£Þå \ú4TÈ œƒ”—ÈÖ] ÊJȼ«í¡^†“WÀdAZØ6+&>É'®$ÔuðöŽ›¢‰8Þ¿A6ƒF¼Äž^Ç) LmGFÉÈWz_¼Ùˆ—!aÓ§>AŽ)¸<¨É~ã;¾Q£FQQQ ,¸sçÎ¥K— V˜—–Vb`LÍHQæìýÝÙh¯R_šÏ÷Ÿ§©ŽhG§bs?¶»ùÎüDyYÊæ Õ×ݺ\„îÂg‡ÿ.%Y¿{ËoÊ|é³I½,]nw»µ»áÒã9|w)î ¡¦™RÓš-ì€âÖa¢6B«îé ÑÁ¥‘ác1hÂjû/-[{…Åy n)gxàQ¼¡ñ7åˆâO„’ç_E“îvE}t¤Š9\ˆk<µ¤ƒè ‹ü‡Z{…émµã·Å[Gø;˜ià¿z©GÓ³¶ôïd¾s„é «¶³Ñ>éÐÓY­âÕÉ$ä¼™ÔíÕõ±‹L ºøJ/ÈcÌ0{6*M,G-·ùäàr  ëƒäÊjM°=DØqæ€W°&§ 0ßc{âÖrxÍ„¾1‚º@íôÅÐÓÇØÐäbÇ(èÂM ÷ã`â…¤Õ ;f@ñ‹çƒÎDO'œˆ‡óc oK|é+..ncccmm=qâļ¼¼ˆˆˆÿö ­ÿ :Ü*ñUí;³óòÁ~Vëgô–*ûQ!ÝÆ@[AKìÊÙ›ïFÓéêêæF(9±fq‚\7͇ÇãßÄ"Ý#õ¥†ñI& Zå&ƒüTþSÃTuÞÖ ëº…IØK_âø†È»òcÒ†V+/M¨[¸¸#ü’äÈ7É–Ÿió\8¯r´X=ÙE¼äÝ[®‚iSMrP`uʺÖùaâwXÖ=¼nKW\$…òš (?ébª®¡Š”ƒÌƒØÁ6!ÔÀgn¢¸‹ú»óáÑ7MÇ·®Ì|Uª"Áý´†Ÿ~XD·´ýsQü¶Pòük@cÆÞ›3>õ¦·ö\_æùqnQ÷ˆSîUƒ3«œZ|^–×;ZZÓfÙ‘¤e_8¼rÄ…}æËÄÌY¨>ô°-êãÎËñÕY‡‡/n·ðе÷ª@ßuÎQ×9"÷ g‡êãLXwdB]`6í¾b{÷š>4 åþÓÖõŸV¹ÙgjDŸ©ŸE£©›µfXm?m‡µ‰ÕGìºæ`×J§ëÙêù\,\µ%>:r×èª~>"‡“[9¥øBÉ3Åÿ 1UL N]Ã`ôÊi_‡MTó(Û‘â? %Ïÿ/hLh©=Eýpr_:—Œ¬âÃ{Ï7Ú^…E \¦øÏBÉ3ÅCR¾ý”ÃI“@——füÁ}D_…’g Š?º¤Bï-xJAñŸ€’g ŠßJž)(((((~;(y¦     øí ä™â?‚€ƒs;±ý8¤bVþªþÉÂ{ˆÜ‚©0Iµgyütrvî$"þ2ið ƒm½`þ#î&D]m:ÌÇLõ_I‚âgŠÿ4ì‡àÀ,$H"ð/“g¹ÖCGczVÊsy&ôaž€åNµƒÒ`Ú×Ðr)ºÌøWäùd¨}Ð3çc[þå§ øk äù¯@Yk1c*¾¶và÷QpKÒ0kØ×Cþßø‰3m8Ž„rLèõÓ$`Þ:Lù9KkÖ€&œÄºFÙ¹e`É!»ž%H¿e¥ AÖ¬…Û¨|G^KØõÚo–úbŠò×SPP| Jžÿ`ê[Tòó@¯®`Af>ûà?#`^ޝ?3uæï›2ý‚Ÿ)Ãõá;RUžÿŒ\“dÜļX„LþR˜¯ëÛ²"£‹óiõW«)ĪÓË~‹0Ø÷ëǪ&ãüæ1Qç<¼¥÷‘(((ꆒç?‰’—í‚„§U…Ï,ìŽck0}*’$°ë$z·ª+ ç çLp Æ}pj;”h¸³þ3pý%h’è0«£ÐX¸"å³£6O³Àã aìÚƒVÒàäbê@¬»6*¸”-.Æ;!6´ Ц!<SôŒà¼+AƒVX²n-ðv/Ú@*)O°Ùs.âÃÛº§ïp°|üã¡ÜKú"l>žæ¢ýDì˜9XµYlŒ^èq§‘â±n‚’+O–p1t.–M€ôgóGm…áaÙ«â`&‹w·à1çR „;ŇLªNŠS1Â{rA÷@Ü(ÄdÀS¼|„ycE" Ê jŒùÑp·©T¾¹]±ä¹&S)AÔ]èÏÅʱ˜™€Â (4ª]Ð:‚3À^ˆæ.8½ªu­ÊEÄ]äŠiû1ôóÂҥЬÀd7¬LDË8³׿ÂkòJQVۑض >yŽK0Á «¡i4žT­œBTÑ|ø…ásûá7NGó^ýa.”÷œ H‡µJ¯Bº-ºuAßµSäá 'ЙÜb߆…ïA¢+J“ÐÒd±ß½îÌÈ4Âîã ébÌFĈÂd“gøA[$B¸±Ömñò"f ׳¨àNô€Ûë`Þ½ËÑR;GÂ÷4ÒK EøoÇÝtô Âì7€“ü¥«Lœ¢À½`ºbò9,^&\(D3#qKg·“âý>Ka®B¶!Z`ɨÚIHcùQªãn•ü_†y;ô^ž7¹™<‘•»ØltšŠÃÃI÷0#8vEÉ]´òÆi4šŒÜL–6^¸)…£è2Îà"kµ\ðóo|*fB5kÿ=ü¬V%Š:¡äùOCˆÃe„ÂTœ+ˆ–BZëúà Ÿž’rR®Ê¡…Œ•þhØýÛ7dƒÕpê>F·…™,Ef( ÍdpK(¨AܰIy†¨»„lå–‘N~)6¬†{"©Í} ¸v  QRJæaâ|è=] û…¢ñÁáBVƒ{]³&ŠX¿J½Ñ×…Ü©„»ñ¶´2ìäxˆë•2*¡X%µR"Ž2ǘˆ©+}6#[ž®Â(Fè`‹Í‘¨p‡x}™)&*ªwk'޽ÃÂE¤6ƒ\Y=—a±/¦Þ%¤3¶T.N­aD:2*Hy¾ñbˆòe6-9ÂsVF³„Ðz½ïzAØ>ŒZ…ÓBó÷Ü&ظTî^YvBù‰³šŸSG¥‡[L6uT³|JŒà_5J€Çÿ¸K½9ü«j6ú`ÞEÐ8¥äÛ7¿rÒ8$¢Emò è3㢑?£Öø;AÅ뤗Pø·ª3¿lçÌÁ‘ÙC¯¬îû=+•ÿ4r®ÅG,9Áp”àûI»Fú…0×¹Ù{-n(ñS²Æ½¾aþÌ•1˜pêØˆ–?#AŠº¡äùÏ£C0Tâ°â*6öÆŽ­è»ðKïú:àãhšª¡zÆb)C(Þ“îÔó˜6ɹ—ÁBh @¼¥Ãrôà7£û£ÉgÍÓü"–ù5w‘¨ì¹Ôч¬D•lÐÀV³¾:˜—&Ê`¥›Î[¹GÀ]Ø-JÂÆ¡,Ú  ,²>©Âså™'°kãGÿsÏPñ6Zäà8 L:ô[’ þõÊsmá!2ÒJ§j[ºòàÜÂ+ e1bgbÍi0¥À. jA•¥žè‹õžh&÷øFKío;RúΰÑÇù0${CŸ‹Ø½˜2E¸ƒ‡ë[ÈN„|dÅqhó ©y mKhÈÔ±+ç B‚pådeðüè¬:ÂÜ~AþöÑ‚$,qçhê’­ôµàæe÷’‚äwó¿Ÿ“žžrï9Q÷’ûÇW¶<¦Ç®uÑYŸ×ÕŠ3?zÆÎçþ³‰Åï3 ª%Ã`Z nxá–Üò”Üÿ7?·PVIéš©’ç?)]Œ¶Ç¢`,wÆñL̶ùÎø|RBjÝ*—nú8Àf+î!½*á)v°˜‹‡pø$v‡aÑl¸‰‘Fµ’ðHŸ° ‹ìÿaáê§Æ['q\`ß;ôÖ x5Ô°ñþ òGÁZF¡ÝAhm}žòZ¸óš´ k¥ùmÏ*_x€š¶èÆÈÊþð½‰Û¯a*…ôËÐkW@k(7Çž#8²¦ó°áC¿§¼’ˆ‹æS°ûúÞÆëΰU"½Ÿ…ý(,º‹‰&ä¦) Ü/§òVY¥ãSÓ‰Fž1ÖÈóÂÝýä=0ÛóïÕU|áÉ?™Ó/EXuús^}?¦Ü¤Õ7'á\üÖ;²Îpl¯ÎÎ#C·­%nߕ‰—Mí»ªHÕÐq§‹^£>¤¹1¥+çðødõùWðc-êükL}qpo$ãÏi“§äùσÆB¾X:·C¶´¿×\aÂF÷ IÅݤœt²=Ó@¥¯‘„YU½÷‰ å0ê#GL˜ /[„lÁÈEµ’¤Ë­¬§ïÿž» ¡ƒ¿¯ †“Ñ% [pè(f¾¨ô|• Žz›Tnòê‹\ô¥qª¹l(Öü´Žx÷¾ÅÉ"L±¬<ìz’k"œ_äâ ˜6ùÂQ‰P©…”E<>8{ð¤¸BN9_ÇõøÁ¨fŠbœW§Gsä%[ºiø®=š’í …·¸ M),¯j¹bÛNWS¥äã Ž_“Ç®ài:>´VóutŸAs®³|³…Šzp®oð÷˜ „Ë–4è½yû31öãÇnž/•M& mŸ°lÙÝ•€ÍGölV#;´×[†{i~ìá|ÅöKömU>>38j{*O#hÓ¡ î͉‹E+»9Úaðñ·t8uëCýû/w³Ÿ{»vÛþ€ë5÷é²ÇR*º®ßä*ÁïÍY/÷q'3øž€™÷Á|ÚÞŽ.µ¹ôí×ÇòÞžQVŽš¶ãÊt[â5Ä*ºí8wËérMçmçº4:÷ñôƒ7ÜÊ“”Ó™·w¼U­©ižmôrö_o¸òÚA€°GëÏﱕ+ˆ×/üÄ1q¥ÑËvÌìNž ö“]CÝ'Ÿ}ÓW»Ü)tãʦ›¼'®01)?TÿÍŠiS#¢Ïô]º'bl{"—åOö <áÂ[žŒ^‡{¶w'êà9çF÷ö8‘ZV!ñˆ<2J"ÁmdÈý1u™¸!‰Ñ¾±ÕìCÉó‰mo4Ø1ˆ>±oÐr˜n.>”B‚ÌbÈÖl rÜâgR€-ÀÈŽ{z G ”ç!‡ƒô±7Âê§0d@JÒJÐÆ"î&%™(ã‚CÇ€nœŽDg8è€ÆGq1R’#¬a³¾Eé¤^B!j^e2IÍ UÝ¡,V¥'HU†Ô•[Y°báõ#‚À¬j’e¿&B©tEÌx¹Ã5måàáàX6“ (ò"”K@½®ÆÞªã‘G/Ê"GGsèÐÍæ#n5ÆÎ&ýÓ¯àb\bɾ| ”¼Æ³|èpñ"…¬+°„8ÎCw £2Y(U]ȧƒI6çá}14é“ -Ú/³h1Ú@m,zW5˜ÊÊ“mû—’¡¡üt²‹œ8'ŒªÓE¯:]Ä !ì{Z•y3ÙÛ`÷Qxw† oóÈóLžL9/ÕGÏ‘_D|_D~¢sIfµ  Î„s_ÈÇ"6½¢Ñ@ì”0¡Q» —&®g¤‹«O³¾R¤ß AÞQ×aÛÖ&¿´SÃîÕÑœ .??µŸcZÈÃìM’¢ºë·U/¾ÙQúA?wËÄ¢eÒ.®¼˜U†¢S®}£=NsÖÁ‘+ÙÅÅÊf¾ACÎw‹-ÕÏž­q²›É½õ2ÕX çö1o6ðÎû½mZ ¸¸q+­ûC¶ÎÂmçÜOEðé0ZpXÿc†ºÃÖ¯O>ÑíèÃéa“õ>Ó}¨G›N‹w¯Ü|ÊûlÄÀÉVxu‹V&îòâä¬KÒ¯v‡àXºsD;‡…U'SZð6·áÐ[—6'·t¢×j±šG˜õÊ-V¯ÓlVZ±nøþë~'f9zžqï쬔9¸Sç—^_MjŽ´ t=µš6¯Y‘“Ò4]亾×áý©N4Âz.!n­ôoÛ-½î<ƾ—ïÈöÇ|¥øE³´?gµ-ë Ó‡Ä k å¯UWˆh62&àÒî‰Ëæ·ÞÝÉA\œÅ÷±Œæ¾OWö0N§U[¥Y¾‚}Í[ôo¶ìAÖr†Å†´Ä+†&3bZÐÂ-=+ˆŒ¡ã»xsz¼xN9Ydg\¶³Ô<6-«êÕimÚvy\°sekû>ÏÓ‚ô‘;iíû¦³ƒbO/ë“æýrÿ,&e=Sü»hav' » »o ÞÊ æÂÜ޽±l&Lü›_7ÈH£¢¹Ø»ÆJàË ¸?¦ô®–èК’8w!‡a®‚^VÐQ»´X/j¤ÕF¦­„Õ] ÅÄ5Hƒ^ÆÐk %5XwÅ´©(»Šˆu¤½4îèd\o<\Ø€³(ÎÆœí˜Ñ ‹fã!¼Ø!‰ö,Ì_A*_d(”–`”âÑ©1,-Ñ¥9¶y¶” w»žã[ÏabGh °3El †Ç¡Lø.€Éf¨+BÇ#çÀµiý§LÓÛ#,©§1l.üÛãÈL B»  »=Â%<3ÃqÚ æú°¶FûV ¬hÿAX¿6àáRM‹8X¿šì¸méíPX¶CO, ­/Ô„4 ãYžþÑÇ´ ÃÝ‘¦p╼eôäPq÷yxŽõ%d¹"£q<ÙÑ«ÀhO˜ÎÁª ÁZhÀAœû¶ Ž9½fàÆ&ò’)™ÍÁ¤(ÄøA³=Z¬ÑÁ á8K6˜CKM1h:Fš×Ê*®Ð¦‡!{ûÕ ùS†‡ñ _¤ݼŸn×¹¡›§Æx#ñÈëv/G­FcÉ ßu¢¸£Ë›'¨=~€ÎmµÛïDz¿.)½û ÕY§‘óH.)œ² NUÂÏ'·ò8&jSé0bl³0ç)ëßònPRÂ!ûNÖú wºrA*úÕJ(l/.a«ØtëÛÙ–p‡vÕ=škЯ¯ƒ. æ=núö¹\)ÏJ® ±Âždš¹¥ r²øÊ3M\œs?hÞ^ßeËšW¥ZZZƯ֣’ ¢êeÖͱ™šœÚÉkó«n);9)™9±¿ðyÐvvVà>8‘Ý·M….-c“ƒLòEa¯—£áàÒ¶ qæAå¾xBTð$rN®;%¿i9 Sݹ‡‘ü‚ÕÛÒM¬vADã³KKÊ´ûÎðµf•3—GlÊó;;ˆØ)iloÛ<Ùú¤V»_ʺ_˜ zYp+ª:®*ÊËø;±JªýŸßΙ1ÛDŽîX\W²‰¬±¹âV|Âo}S‰Êx‡1(óÑgfùWÍ*;¬ªÞ0ÇõÜ»æúW¹tñºF1gA0ëãf÷)äß·3ÿæ×ØÔíˆWê¦Ó ¯87çWű\‹ÑŸ¶Å›úçùI <(¬å#ÑÛT]‚ UŽÑ˜·©F6¢É¿šxG“ÕÌJ¨tô\A®Š˜j—îU|ô7…»Ÿß15¡1ºöôT]9}ý‹•_hÿ`茚ãÐE{ºNûðÕ«Æ9½OâòÎØ6n̤Ñè‚rŒ‚DE ĺošnæèo»uf ÿ°¥³½;³4G,îÓ£ñL-›+£ýzµ©ÕÏÿøAZ¨éU6Ž(¨iJ3S®¾€wRÑ€G_PŸNø•# ht\4 Ç^98å/—ûOÞy+SZ–§Tjtå‘h’´”q­MbØúâK-3¢äy<¨ûXLBFŽUñ*‹†, ûu!äT4¿¦ 4Aõ¸M"“ŠEÅÛÛÙHnÓDJœAp²+˜ ÙutvÒNTV¯ï¿Fñì&ËdX4ðrßA§¬äuÆcè{j}åèÉËLFÉ‘VM¯Ò‰LðK%•ùLã +Ý.³=°¯ß‚eóG|{j¿”<ÿIð+P(€‚Ù9á–Þ©ÞKÇðü}QxõMSüÿ)Á–m伟Ã`¡ÓhýÜIG¿7[ö@P×@e)8»@î_øÂÉýH/¨kmúÃ\ñß<ú¿CÃÎ#ÃFnõë8øÎ"í¯µÛÿФ&ízÕãòÞͱ«§»XfœNõHô½û2A©F(BÂne;»6zj—Ç9„ôðÞú¢ëø›ccæô±~s0iE½XLøÕêÅçr|¦äO¼¹3ƒ:Û.‘/¾;†¬2êl‘hÓé%×Ï5ºx×c¸…OdâÍŽ¿-5S«µ·¯íØÑÍgv+¹²!ÍfðzŸvu^À/Ût–­wݸ×N³fòõGcIð¡¶pï“1¶Õ_Ÿð×M ¹Õ•ͺ3@§Ó¡4&%ey­çµCÏeúæØ kWŒ4¹ñìñÙ_Ìøo %ÏÜ7ðߎùXãVÁ0©¹O>¿Ž(uzþJøäG·Ÿ?c´ÿ{wQÝðë9ißcàþ8Ê%ûä=ÖT óà¼{ÛMÉß]¢yo}g×ÝÖu®­‹|EÓè«ofõµQ)÷?ôV0\óãíHË»°þ–±GgûIö¶3»œ~ÂyË:˜Ö¾u¯YÖÝÕavýÍŠ»~!mÑSç_Üe£%©Éo_¿ÎáÛ»‘3ýœ;«äýí¬BÃ=„‚Äù˜&ŸG3w÷kg’¾,¤MÏ¡! çš}Û% ]®é?ßǶ‰8Û8z袆RŸ ÌgÔ_g%ö³¶k‰ÐSÏß´ÓÔù¸ã ϵf[ d^ytŒmÛêdé™ iç-ø ;O˜Õã3hä´òœª™Ä«‡³¨j›Iå®:T²¼wµÄ—=[^ÚÃÉdÄôåýÛs•žy]*PO§u´Ço%ÏLi|؃Åp‰Ø>5vÐÑÎíê÷Û a^¿:U0”1Êû]¾ï[—?†b¯=†—o¸õ!¸}ƒ_›¯À+¾5ãjó­òi÷ †yjJë5æ$9¬»`Û<+ ZVz–^‡Î:x³Òk­ñå˜æ2å7N?s° b–ß‹ <×rW ÿýÝûƒç6§¸´œ ^y±@ GSž½wz“ÞQ‡ïÚ;èзG,Ï· ‹îLv—”rÀã––óAYYÜÒAÍá„¥]ZÁãU”•Up%Å„[Àg—V@Àâ–Ñøœ’’r(50×n¾hü^óþÝ7JXŽe¥¯¤”-à–³ùMºõ´Zæ4ÈíM‚1“S\\Æ#"—ðdxeÄAÊKyÄ¥*/)áðøe%e¥¯mÛ¾ëñù´Û ¤ù|¸¬fϱ£ºÕv*¯Ý@œþzÏÖKzvz-šˆW°yÜòÑWd¥l>Ÿ[QÎ]Î.`\‹Q=úËo[ÚÍ@&ëuºJ[§æ5¦"âU'‰ODpØ`ɛ̜oã0ÞCO&º¿©z^zš´Y—N}˜.éíâ´kÙpù²·3!#̆Xí¦¥×¸ûÚÎï!xël&ͨœì6°êÙÓ2¼Q¿›CZ)q³Þ¶¶Þâ7Uåèæ.šŒ«Ï70è©'…,CÍôW.^»­§¬×ªÙŸ1:‚’ç? º&Ž>üÕ™  øÍzNþÆÇ²´ÜFtŠ[&FØÎ;<Ǝ죌J¸l·ùâ®5×ÅL:ôn#N¼&;Ïš¾{ñ,&] ë¼~ÃpG&§±Wϸµ‹‰ˆüÖAû=»6θ’®`>uíÀÎã®=ºª[…Ý;l±f÷ÊÛ¾¤ñ¤—Ñ®2„8e?=þ¶íäQ´ä[í54î¼âŒ<äu⩜A•«¬Ò¢ÇÇ3UFx wÿÅÔ³ïjºøn½¯ßºìJ×ß×êʱ›ƒ;ÌݼQqÕÎÓ»´à¿;³=Oæ?)jãßYêlR¾›1Ãvˆ--ÿÄ¡+ š—¿–ïãguß©ݲSTÅîŸ.hiuçìÕ¶“&•߸œëÑkèàû9MšËÒy ³óžNêÞUöþý!Æ?׳´o9?ÞyLÑ]­4UÎܯ/ÿΉ¤V&¬Ï[÷h%ýäé;=£#£.j˜­?z,nÙI)Kg÷ÚfxnÒi¦Ñ¸¬Ç;O©öîfFœZûég.6]»÷âîå—™:­; ·£AÑùÒÙ“1›÷o[¹²¡Y{u5z±(²œiܦ­+âÏ^>{³m÷sd,xú(¯‹¹r‹í‰—œââ/l‹>/©jÙ©¯•œaØ|§C+g]%lo•i' #Š¡ä½{uÁÊ£;vµ0©U3üPòLAAñ¯ð µ9//ïýû÷oß¾MKKëܹ³–VÝcƒhÒMÜ'ÏùÔW®Ñ@ß™kú0Ô{xNëQÓ‡ÙhȤYCjxhµØv@Í f}fšÕlãCÅpØÄ¹Õ›®ã§¹~–%ùVÝ'·ê^µ¥?>ôã¬C­?†kØvrXUƒ°ÍQ ãÕû{ŸQÝóìcÜécD÷Àª‰na?Ì×^8ÏkvÒÎ5)úÇ#•DµNÿªÎÃ0®ù5µœÓ¸YU«‡›O·ìWåoç?§Æ$bJÝFNí6ò³‚ Qµè;Í¢o-/šD»þÚõ¯å'Ù´ÃÄ9„Nþ¼ÈϪšïUÍ{Ï1¯*V»%ݪ#¨´0oxlݼmk·NÑ¥ô½æ,«;[¿+”QÖšðù|111V=HJJÊÊÊÊÈÈ4mÚT¶ b“ø%výê’ýyèZŽª0lØH y)^EIAnžÓ²Íš(|=æ¿KÙéõáÛŸ#3>þÎ@;S…¿N¤)y¦  øùêûNÈÛ·o««„+))5hÐ@]ˆÈÒ%6MLL‡ššš„9ŠV¿ºpÿ5Ä• öìgs¸¢o—è ¦˜ó78ËܧÝ2•È‘˜ø_§Í ä™â_ «–`QÄ|ñt~ò1eÏ^ofa ùÝË•f!r„ù«¤¤¤¬¬Lüª¨¨4mÚT¹ Âçge‚‚â߀’ç¿ ²„úxŒíˆ¤,Á%? }íðÊÑþø[+ì>nÍpg+œ‚µ V’X?·š`…Ù—Ò~~ ú£ß ë€ü—˜9\B\D¼¾‡‡- MÃÉ(ø¾GðZ÷¬7º84¤ ƇFkt0†Ì§wæíµÁÁk®«uótÄþ4I‹+{wÎÌÍQß°²­¶Ì¼Þ¸a…ùsý'#ï~Ÿ7ÆFh$làùrmÇÜÓ¤?‘¯W7ñZ‹ì"_ŸI8‘ ÛÖÜMsppðÉ“'ÓÓÓùÂ99 ã•PÇoŒNHì‡DF-á¨þ%ÈÌÌ” ¤T±6JB7nLÔX,–hó'¿L&õtSPüÁPð_@Ùyì/ÁÅÁ0mÓ¦O%=Gaož<€!±Ñ Ã#°ÝfâÄÐ\1´=fé^üòÂÃäh†HÑâL­1ÿÞeIÀƒ^Gø’n¯ñ¡‚ð ïíÏgóBg¡±¹p±Z ´nõÙnZ##—9{–zZ/m¿BnÖ…‹nCáiM„íÆSê˜LÈs1)À“@¶ðÁûlLž‚e3 fŠÄ30«ìUÑ+ò·æHh u—Ó%¬x”ÔµNÜ7Q]APšžx™..Y½ÍÅæÓpï=!.áÑX3œì'Дù_¶N¤C‘ >‡\¦ó»¦ $ŒãW¯^=zôè a†êââB®¢  ø1(yþ;0í—ý‘z“ÇÁÏvå‘&»ŸÅd &S+$MÉ¿Cðž¾=ððê<%#\’¨°Ž×?%ÿ5!‘ÿçŸ4Ñ%ed>ñjd‡‹ïQðSý°vš¶…º0ÈûbÒnþ<äqˆ¡üßf[ ‰MNNÞ¿ÿáÇ_¿~››[TTT&//Òf І’ç¿€Š»ØAÃp4jU8ÓŠaÔØ-O`X#(gÏÃÔòbè1œãðHG²õÈŒ„%Œ€¸SW×´Æ4ÚG“t×6(™BëË£…Es}ßܼ"ì;·^oŽ51Øeˆ›ÙØŠÝ;Ñ}T]qªs•'o á­]]I__ŠœœœçÏŸ?yòäòåË.\HIIIOOÿ)(((DPòüÀÏÄôt<]I=ˆ|ôP†¢¦¯AH¢áØiO c‡¦¸‡Ãˆ |ÀÒ+h2º_0å°$]§Âè%ÂÆBC9¯¡Ð 6ÍÈ…`K QHò±oæ¥`ÁZ4ø¢õ,×âHœ‡A~Ô‡‰Ê—W® §A·-,Tqai éLvpO[…žà¦bT7ÈôÎÞÐ {…Q&4qE˜9÷¥ñ$ŠØ¢Ÿ›ªm¿ºßÚ£«=ÚýÀDgÊÊÊ6BFŽÉçó333 ÁþîT((((ª äù/@¼ ÆYc¼+9Uˆ´>žß†"ák€Wo†c+p€†ÖŽì 0Ðo/ƒm iÅv^™î_I¼Ó<6ÅÊX –¬à/\ñmê)(nD¯ö(ä¢a+œO‡Ý׿è¶Áú9XróW"0ò LJg„ G2Ê‚Si4G)Ò^píÖ!vf_€”šÀe,MmD~‰½ï:Ø 8R4};¼Ù máCPú%Y¼~ç‘È‘¶²ÿ`¾aÆ`0Dã®8 Jžÿèj0»Ü '‡£++=7nÄÒe07Ç©ó•>>Þ¸|cÆbÝžJG-Ɔ°4¯7ñgÏÐÇŠ ¸|©Ògë„/F«Ö¤cšÐgr&öÀ¡˜4é‹Àe ù÷]…Çûc'A«!¶yÒæÎÅž=.Ý»º[$ò€[oLz9s¬ûô±î)lʶj„2Œ:M-BTµFÆ“ó8¦"Fÿ Ö   øë¡äù/€ÃÁ°a¸wt÷î;ñêRRpÿîÞÅòå=»w£ÿ¬ZÅ‹IÏæÍáé‰Ã‰,BgœNý,Q œ{‡iÃð8‰ôèÞ{÷"=âÁ<|ˆÈHx{cß>8uCD$–¯Æž!äèñ:V@º2á¾3ÊÍÅš¸•„ÌLLœˆ°0\»†† ÉòîÙƒƒÑ©-Ââ%hÔˆ,”µ5TUáèˆë÷Éè-Zàñcp¹å.¯YsgRTÔ_¹4Åo%ÏÇãêU²3˜ÐÅ30t(ÔÔ°RhF·iƒ;wàá¶mѱ#é3e &OFZ©¬äÒ¹8ô°î ´¥e0|®Þ¨LyÎ2e¬ZEî56ÆíÛ¤ð[YaÈÒg‚7üSÁå ¬®‘Þ4¾6yhÝøúbûvÒAÊÁ#G¢Y3ÌžMúôí‹ Ƚ}úÚL°f YØü|ÄÄ›DÎ m&öºµßœÎ¸ù¦½%ο”<ÿÄ çÄÙ¬¤õ\BAG®åQcƒ‰ú'Ù¼ícʦ¦Ÿ¦¯œÎïYG¦aï3¯ýíu ` hb¨‘žžÞ Aƒñ«3òŸ„ÖqlìñÌ—:óïnËȬT¼ íçh¯uq_æƒÞªPlzag}³¡V×;,‘sgc—.é;.F `Ñðöüò.ýLŒ»’±GUx{±ßÝ8öÖóI&šÊ¿ºh•¼½v®È¸c³O&àò•Œ­ôeØ\J~c¨KCñgÑŒ8LiúùžÝ>mVp½öÅúur¶2ÓW¾´ÊÛg7oÏžUúŠØÝ~àÀM›6?>%%EIéË‹}Qü8Eå\%ùqŽÅõ&û Z|x~Üž·½½4ɽ%e|Bw I!S6¹lvdû°Ýióû5.¾ç9Æïuç ·£Ü«+‰b ZN£}­.%šÉçëÔÓôü­Ñ‰I#zº÷IyÓìIýE[!©¹Í›}zm§} Sóú×’ÿvþ»íóØ ‹âGà”¡´4 ȉ¡´<&ĸ(ã‚!:htrîñ7CE)ÊÙÂi“„”X½‰ x(.ñ#‚Ѹ¨`tHÉ€Á'gãpAgó‹‰b—¡¬Bø81 ) 1Ñ]T”£œð§ÁÄÛ«ÜM+ POó ñBÉ?ƒW”â"HÉ¢ÖL_£ó ùÆ!÷‹ìÞûLVÚy]µ5ƒf_[á-QYmÜÞè:pq¦ÙªâëÞù‰“»G¢ÿø)êiQ±Ò9M\¸5ÍZ¾fŽZ;‡_7دìÌÁG™|“þ³wmšÞT×"ûuš¼ÛrëÝɯÂÜ–”Ý|{¨YΠqÞ[of®½oÜÚ@¡á^þá^ðX]OÊÅyùï_fH÷Ûòf¬äûƒ¡ŽCç– ]oõaLÈ¿m§Æ˜3Wø\(*ÔØÈ3>)TðóãþR‚Fßx=›Æ)ïûd›ëO¹ì¿!”<ÿ<<ˆ‰ãq¡1wÅÉ]¸Õ»ôá6]0\)i¸w†Ã±5:²døS«1k54L È"š,ë‚ó³êM¼ä¢ÆaÖ~´ ãRraŒ¤4tŸ; \»MNëýâÂNbz'¼¿ woȶ„2ápa­ÈÃèÐ5…†$’Žãt2ÖÞªW› Š30g©ñG"UQË V«òÌ«€N«ÎCT³ž=¾©lÞÕDMGIœF£ ^ŸDÜþ€Ùsqç4®]Æ5ÌôAÁ Ü{€«7!m‰Ä°Ö/óÆâ-Õ÷O ‘$œjl­/â’кØ»YFÈ|ðs¡ÜÜÜ‹/ž;wîØ±cOŸ>ýd¯˜XýÕ#ŠŸ&)¼÷QñnSܾrÍž¾ýÕê øþÎm4ê£#AOÉx ‡¦_¿þâì6-ëg»ÿÆ3Ž×¢EÇNɤìð2÷803³‡*ɺyi?Ý?-Á£íÒ±)9aLwÒžQJÜãÚ½§/5¼NÜðZ–^‹=Z,®ì¦Ioµèõ°çéçeJwjdägeÌ ¼Ñ¦ í(9ï,¥:ÊÊÓÂßdŽ–­UZ3·y^6ÛÒ˜D½OÙ%â@ø>«Cܶ3—›1Øqz¯ Ñ5×sí²Åe¯§ëìãÛw–¼>9̺‹ë¥+k<­ý׺'ÜäÏ}DÀgâ‚üоn9Å¿Ýë”®rÖ6îø>ùÚƒ—ãAÍå¯ ^¸51~¤ªE<á=笛¾Õvçù'öžA!šªŒÃã­Â߸ õ¨“ž±Ÿ¥¨P¦U…:Çï¿h‰}€§Ó¦¼ ê°ÿÕǃ’ç¿ÓºÞÁÍ#ûÁï$ÆÁ9×ÚaU0iàès 1]ɸ…á}äK‰Ëhb†/)ÏFvwƒhLB@6¼X¤e<ÇóÖ‘ò¼{ îZ k5+û8D6/aCÀ™ˆ!äVéYH; 3 ³z¬dbB_¬8‡¾‹0½Ég»o®Ç>‘÷šå¸j@ËÙËe&L=ÜǵKc é†ÛûÐדüÀ.@ì.ÃåÅä²WìP±Çá³°„çW0'Ï @¾{ 1K8‰xn¼b°ê ¼;›ªXtÇ^aáy¼ÄÄÄÕ«Wß¾}»   Ú\þ„uëÖÉüÜ™Pÿ£ 33³ïŽY^詯4Ng‰‰7´tûÆlÓÍ®„L²¤ÉWåÝÍÞ}WfŠ¡ÂÄ£òByeëüP¼2¤å» èÓ®ÀÖòÃ×@šUpá­ãäÑsà𤂌ƒa» Fï,2ZK*xÕqË8üªG_Nv‹¸>Cšãrwò¶Ò·³ÓX¼-)p¿èÚ‘¸ûÓ‡·V ˜Þ›ÈvIy…‚R"§µä™L•ÍãW¿þKJy2ºÍÌ›ë1hègÞ8üJr.PSžùå¥Ä½©¦G¶ HëvŽZ`Û(ôøÛüÑ¥åel­®#{tmØÍÞ™—½%æªbìZKa$)Ÿ9CÆYï:s7l„yÁS¹Ž«EO«†Žò‰3Gsa¯D<ñe\yÇQýº‚ߕƿbµ!½Ó Q¡ ÚÛ©G…J*1îþ4a¡zAÀ-å1 Ol8‘ѧ‹–á‚0ýoº&”<ÿø ÉB§è °ÕÜð`J‘M·,TZaœflÄö8v™LLïZQ®"´¿’8OØô*­9¡„´öK(W¾ZZ ô9¡Â`‰!{7NM†…Tºbœ(2Oó ¯[™””YÎÎþJ’¨±WÀ«s§óÜËÎd˜7©O³õ9èéäÔIt„ñP‘™Q˜ë#žyEá^Kïžâ’ʦw‰R¬:ŒàŽSÀ,Იïï‘QTÕ+¢§ n1Ò aô«lUA£Ñ”””X,ÖQíÛß!Ï?SBn͋ܡÊùõ·pŵUjÍ3Ï’Ânjj,ºRˇûu7$<åõè8’’ƒo^ÊE ¯!Æä¾+g îβ´‰pùÄmÜÚ¼ýÝíWUSQùj*=9)o€”…}-·H¥æ•Ëš8‰t7Îéèxs9Žöqjùg€HEøÐ}᱆ ‘id„wÏ‹+¸øX0¦ØwóhJ Uu }i^Yö¢v Ð¸ μÎúæ>Ê(’ke©P#YâmÄ ªëy©o HYàj¹YT(¹6D¡ìÇùÔ.Ô@ïI[NŒé®MS2í·$fé sÍï(æ%Ï UVݨOòaÒ˜hC£z ¾ïëT)-a»_ºƒÎMÑØ\:Z WÄêl…åÛ‘çB&/o$ô¦ø G)MÛ±CÙP±þ|’UZÍj ÚbÁxLíƒUZpvÄØP85AH/Ĺ+p#^Î¥ØR°þÚ ²—“n'¤´´ôêÕ«.\ ŒiÂ’þ$X@@€¬ì§æÅÏD (#DDY!toì¾V£ûEô<dU½“[Š^—3ÚÖŠÑÄк±Xäæ„—~S×ô/xŸœV(kd ú`0Åù\šyäË>z¦E±À«»ù„¼õ™hJv;ÃyÆXŸZ H+ŽÞó»}ùäΕ>ÝÚ&åMûwÆû Êr +!Îüô)eʪBÀæðªêÍì‚r0Ä%YDvG®X²qd¨GÖyÙâ×)FöF»Õñˆ å2ãÀúÚ…Zyôž?Q¨„¢B·ìzü~RÒ勇ÖÌlaËÉHuÿ 4%ÏBØ¥¤@X®,I eÀ·µÚ}+„ø)cãE,{u30}6n=Ç•m§aævœq€±&x4¨ þµþ G¤Ë[[ÿ`TYø.…Ç|\ŽÇÐI8} Ç’aÓ—Ðc4vCL fý2ßYs©)))GGG‡àààwïÞÅÄĬ^½:??_´—°)yþÿ h ??íñ«œ¢RzÊ£ùÍ%ÊŸ¿Î/zÿêiFqs vrFž€]šú®¬iÉš·IË|xõ¶¤lþ‹Cãö7‰Ÿª*†Â×Éïs‘“’ôî½´º†2]Â(dY¿1³# ‚ûË%…,HºÛ‘|‘_Þ›´÷ì2š@@gɘ8{Ì òÔáe‡.~ŸúðYzKÆ,h̉ŸÐ’(”\qÿ¶¢B $ µ>Õk€•°P–9*¤DnÙr3{UÃìK»Êô¶üoj3(yþ ¶-KŠÆþ¬Å¸JÞ®Dbõ{ló Ý.CÐjFuBÁBtnFžfÀÞîKwЏÔÇ_)aTu‡ùM‹Œ¾}$ÝàkOz^O…¬54‰ p3÷›Ã+,ác&¥‚^ÃÑVïKe’Q&;³oFzðÕ¡óm½ÂÓ@œÙªMqU{¥ÉÓ#.Iþ&_ÄâkH'­Žë·È(fFÈŽÇâãè ‹¤?Íæéu&RÍóù0}]„­êÏlsè($;;[^^þë¡)~vq‘ò ðt±Ü¶°UV¶Ý®c'N§”ˆ—òxtFûÍûm Q–]\¡óé˜u¿Í×ú?»uïyF~‡Æã@¹uÈú>-šÕ®!`—ìßÇ—–*â‰úzôú¹d|ñÖãì"6KFÕ ‘6ñ|´³ã¡Éůsè’JÍu•«{[v^zëTÏäÌ í&]­Ìº¼ù ›ŸÏ(ª;-Kº>ðæãô2du(Ðe'øHÞË+( «Xî8К¸kä÷9u÷M®„œLM‹µ¤ý#·¹òYìüžb‰AÿMûxRlŸWQÑpxÄA|•â|@²¶ÙMGyq~»@º}üÕ©¦É.MzBä^¶˜LN1GTïè{Ìêòƒ´ÜZÃù‡.Øi ½eºùú=»¥Ø»­€F§³Sð ’o;B§ùÀÍ{Òåe¢±ÂBÝxœ^^Y(Ú§…R¤çy©Ý~š“[@3r?w©£þï=?Û?’çÿ<4$†"äÙܵ?fDÃNX+gHâÙ\¨ÎBA14L±ó6ú ­B©f¸ŸŽIcÐŨbÜ\8ÖŸ|þ øùƒÁÀ_ì„õMLXNJÜОX¿WaÙN²«¶ÏŒ²ÁŒ¡ðÿ í†áúZƒc™Y §1X¿2Aá¸öVõw«FÐz„ùãÚFÄüFy~¸‹ö– —ÑX7k°ù92—bh6ÂF"Äo€M¡hhŒa-À ƒ$<:4ZcÿS´%t}æîFÈn\ªªÕðyØ~—vánlXâ²G&ÃSg™KN¾ •o.Dñc(é˜tÕ1ùÄS±Ug·V•î¶ô¾œBƒfæÄßÐ¥UÌìœ?M.¦nèèbXË‹&f`éh lï~v{¡•cÜh ÉÖ]ª›lš´©qèæ6=›×HAB·£³.jÁjl騨²¶èZ-l´Z|ÜîèdP唳íÚ ÎRð¹<5‹.Žöµ:Y”šZ85µø¤dMÌì›|RZî½éAN?è¤W©8ŒCs¿Kgit±ïVû$ Õë‹…RÔjÞU«9þ(yþÏ#€Ë8ÍBÂvr¾‘6Óh`ð Ý ;GÁº]eÀÇ‘–33XZ"ê ¢Èi«Èy?œ;~i –BÄÝ@·`W`h3Ðñxé¿a=®„5䟈èU˜8#GTn–ÿ¯½ó€Ëéûãø÷Ù«žö”¨ŒR²*”’†R(BIC”™¡$)´Yffd½’´´ç3ÿ÷> •ÌŸßï׿ßy{^¹÷Ü3¾ç<ãs¾çž{DÆãK~|º«®‚sÈχ  ì DáÊ©¯ßb"°ò"¬üµ†èáïŒïÂÝ;ø»ŠeèÅB/Ìlàì8×kGm¼m›¡ªúÐ @•«{ØDá»s.•_ñAƒH€ë°6¹øvîÐxáû‚õÎ'ÓÞé?W@Ï>!þ 4*Ö$¶žÇy‰T‰d:ýÇQ[¤<3ãsUUýïHÅÛŽW Þ>äÿu¡Ý $ÏmÚѸÁƒ "_ËÇÖ®v¤ƒªd}€ãÇÁÂ._†¬,è…¹Ñû¡sg‘åËaà@PV†‰˜³9ôV»¥/§áx¨É_7ü­^ý - ÔÕ…„ü cGÈÌÄÔÔð‚0“ÿ򪃮ƒaëvø|fÝŒ^xˆ·7Ì™ETx@—Ó l ô¯ÇŠ…ÐaœÜ ¿º VtYlØ ööðèΉ œ:ŸŠ {oØÁš & €™~0Â.]ÂÛnèPÇühõkVéØ/ Ö>ÇŽ†jBWRRÀýð"GzD¤Ú/Ú„@´@î³ãîçÇ Ž<ã1‚ñïk˜ðäJ÷³™.ÚþËæLÔÿåádÉћîXë-§‹¸5T©ŽÖ»î̶Pû÷kÖºAòÜÖÁe **êN1 ¦RAû¦iÁyi°µ—™0~<Œ‰‰xÌgÅÄõÅ ˆŒÄ• ãäQ“‚¬ (.i!ÿË× 1ôí‹ë¦ÍµO€”—St+ÓæÚìjíWçÎ} Áÿ¶£èDÂxx"}AB¼C '«¥…@aÁotâ±üµ´ðáÚ:N™––¸ToÝZÒµ+ddà§ÆÆx¦ÍXCq¹ž:a2âš^½š¿„ÅBA±ðR<Ì,|%öÏî=†@ þ<#~þwwŒþ“}[v~†ÒLˆ¼ §|-»œ—Ç}v¼q˜5£W7yæ'3Y(H yÑÃoáˆ?°®uñXúÖ»ŠNxøfÒ|#¿x?|+HÌYñßôTÕ™{{«ÓŸÚø@üc ynóü*ñ7•ÅþÕ;›òpÄâ,áX¤7GŒ¢ûu¡KSOŒÔrzÏî3U£ àȳ·3H°‚EçAÁ Bذt=d–ÁH¶€øHØ E50= bfýôÇ„[mÁ9 äç@”<†Ãó|0œ G#AN´¼Þ]ðð‚K/ Š zêp°ìY'è“ÀÅž•¤FËu ª!1–lÂ`©BHÏ+ÿ ™ Q×Á±'ä5(’„íÇa»"h/€Ë‹E%ñð¾Šà30ua×yøp ì¦Áwˆ2‚ à,J?Co/8ãÇÖÀüy°s2L–…’ Ð4ø‚æxÇ€‘œ{ÃÚÃõõ`¾oƒ}üR²%Œ)µx5,¶ƒXiÈ(6@¢=XšÁÓ»Ð]º¶Ælƒ¡ŒÐSiŸ¹%·”Ï–‘Wà”V`•Ÿ{CÄ¿’çÿÌ.°+ú.ûJxp>¦no7a¾²´¤.˜ ÀO»ÈCÀjègÃEûµ*¹À*W(.ÄŽ~®L‘*¡¦ĵahXÍ9 ¼rJpå>uÞQpEªeÄd0ZÞá` A±@ï>Öu—¾ÜY-¿]`´×f “ ÛNÅ„ïYQÍ¿÷Kl> +ÞNf–‡›yÝ'îÕ:Ñ ÎŒtãPD[Ø–áÓ¸t aØÜŸ÷°€ˆ+ cÆ"c‡@ÒÈPØä}À\´G°¼h·‡3±Æø3^M£Â„@–`tbƒ¡ÜÅZÞë#€kÐ.`Ôÿú9Œ‚Û!í#.Ï›æ€PÆ™Ôeà >špð0 s‡õÐÃÆ÷¨Ï»¾f%áÀpŒk3ÆO˜Øž¼ogî, /ÞWó•h,û}ËøLq´ñ%ñ’çÿ }üaÉ~X¶ξýj7Æ9$I4&[ ‘‚_úz—‹ò%…èî+~*€¬L€vСþ RÊpå<.Œo Òä¿Îè¼(ˆ­#¢\ˆ Ú:ÿÏD“ÀuŒ/gæÞM† Àb±ïªÛ`8IÔ} êBO¿ô>}­º/MziÂ÷§hã¦ësk N¨/÷ô ï Ob© Àû,¼—r¥,Õ¾º±O€ª"ÈÁz cà Mt矈÷0Ä=ËE—ÀÞì‚".^‘Ê`·ž=„ÄwAòü_‚) *Ìß÷[ø­ãÏÎþ’¡à«‰c"aðDâöÕG‘P›`¸$þQ›HšCÇkþnb¬B àî9oúîÇ©~&çf-D5ƒ–“[zR ë;U,Iy-çIhøƒ@ þ¯@òüŸáŒ7œ7&ÁÀià;B þÙ≠¨ p÷õÔD5ePš Ê(’là§C àQ7€‹•2(侄 ÀLj[½ÛCÔgÈ,…ìF¡Bw„MPZrµ*gÁ{ãv@ÃhyœýU!æããíɩߔçÊbL¾U¤)ß´¯¸¸˜@ á{†–••ñx¼êêꪪ*ì ´´´6‚©©©²²ò¨!øi<ÿ7Ⱦ ÑB8ç¸í„0 °K‡~@ â7h õ* ÷×ÈõëXÐ(ø_Jýx(Y´âÆ÷¹ RðqᆠA”!…‚'> Ú…BH2Ĉ&>¥¦À<Ø2ÿ:õ‡ Û`ÝQð6 ² j“ÈŸ1ÌM†Ä 0N((È©N Þâ’# eŠAÁ{(¬ªXâ@ú9å¤á K¦ÔélmÕɵU'…„ÿ­=óðƒðÙ°a,Ÿ 4(+Ž(SqëÊ?BY pj@†-*–X—kWRí½üo"¹î»GmTg0„8¹;Ðy ~z`9TI¹^´Çx˜ Ǭa˜&ð¶HL SAR LûÁ¦Ùp\†`ý•|; ‘ꪕ÷쨚vd@X}ÿÄÞ3é0fò”®ÒumrëÖ­iÓ¦=þü; #&&fhhˆäø‡AòÜæ@ü2ùñP$vP© ,ó°t´K‚ó/ ¼Võ€iÚ°|5>H9HÞ0ˆ«WTAP hï€[Á°ó.þ@”ûòu‰–K; R¡ð5õ‚yæ°b~ó8vtë6CàÌð^ º‹€Ï‚¬= E MÚ¥LÑÒÐQzЬš: öoCPæ Þf°H$ea€„¬h:¬ü&óaÁDè¦Ö.ê?–g~./Äg~í^òŠ0A ælÀ[nã0Ü ¤ƒ°v/懂ï ñ5W8\°HI€–!Ì[ÊŠ°Âfî€÷À1,sáØCÜçØ «ÆCø\8_UÁ°ëÝ„—ïwC„5¸u„…Áøòh ` ½là\ ,žQ<àW_öœËN¸…v‘ðÂìú´"t $sì|áÜ:Ú |7°Ñ9Qó)àÒЉú|å·ëøžRX׊—æçø¬øA¥æž€þµƒû÷ïGDD,X°à[Ó¥K--­¶!ø³ ynóaÚ2üUS Ür +Aði]©*âr([]±ªÖ킨ý_’n» Ûk}_µe`¿ ¸àóþíeCF@nÀ—ÓUñø«m+8kÕR2&¸mÄ_ Ä51`qþúyt€ãð ñH °p ,Œ*ÁGÔixÏcÿ Ñ…êÌ7‰ÇnµPZŽý7Æ=Ư6„ ÕUÀÇÄNlF`/VA%sŠ‹§Õ·REùü(ÁüxìTÔ’f0yq)^U {wbnBLmA\ì? #'¸è$JÅ)ÁïÈ3êºAtYÁÊÝå+± (uCê+j‹.'Jv‹>ÑxXp¸@ ¹¾èçQ›‹dW± â{Ç„[§ªT yDfãÁ:îçç7jÔ¨aÆ|Ý8¦¦¦¿Ô˜â€ä¹­S»¤´—ⳄÁDôàŽ«+TVâÒ;Ãǰx1p¹À`ÀÆxøµk°e >‹[B6lÀ“ìÞ §OCi:è:ûë’ † sü@êo®‘ ’v«¯ÖìÄ ÀTPû¾sý 6m‚›7ñƒN`ùrü`éRHOÇúõOOü`ž/äåáåZ[ã/ 7/(/ oáž=¡¤||ðGËh4X³¤¤àÑ#Çû4bb…'9z’’ð÷ENÖ‰ïÆš÷ömü{#–-Ö,7oðpwǰlóóñ¢ÇŽ+«&E{{ƒŽÁüùuE¯ q6ðÞº é=+fæHѸ´ &=í_Óröôž_×^GGçåË—ÞÞÞ;wîlvéÇááᆆ†úúú„¿cÑHžÛ:Øï©‚.3â"Ñ ;;<äñã:·ØÁΞ…þýáÈ<2}úÀû÷¸*lߎGÀT\Vª«ñÓ]»JáÔe¨â}U’xøžÛ!AEÈå-\â@Œò;y®^kðìÙøqf&>þñü9®—ׯc>&.„>€´èb¬ãÂfã/¬aé¢Ç˜<<`ÏèÞ.]»AX·fèPxòƃmÛð˜pª¨àúÉv­b‚Êbá1ÏœÁ“cddÔýò%¨‰fÐ]¹‚w˜j‹–u|bcñözýWb 77Ø¿zô€óçëŠÖÕ<¾0v|‘ó‘sÞ&µ“êŠÒï¸E MÞ£ùL¥¥¥ãããíííÇŒSSS÷w§N¢¢¢RSS÷ìÙ3mÚ4###ww÷=z´œøs ynë`^Znî—g—ýüðWmx-‰‰MâcJ–Ö$“‡&cžl0µø›Œý)4Ðþ‡óô÷ÿrܱ#¾TKc À{'ШќñWãÌ®c¨ Á::˜N7Ó]¬ÓÓII¨¨h‚9îÍŠ66†ªª&Íœ‰¿‡`Ny­_ÞPôãÇE·CUf$þ²%§”æßߨ$ÉÔÔôÅ‹VVV0¿`ÆŒ,kˆìôرc¾¾¾¹¹¹&L077WTT”’’"¶¼9øK ynëÔŽF~=&ù­QÊŸùägççü—ö‹–Ñ÷uû…ܛбcGÌ] Yºt©ƒC“»ø–"JJJ®^½ºwïÞÏŸ?—••õë×ÏÂÂKõ»"ˆ@òŒ@ šC£Ñ'Nœ¨¤ÔÂz®æ"jD=zÔÞÞ¾°°ÐËËËÙÙ™JE+“!$Ï¢BçÚ[ï߆&ÂQD~~þ–-[ŒŒŒºwï>hÐ ]]]ÌŸ–’ú»' "m$Ïâ ''ç/"##ãÉ“'ÉÉÉÏž=¶¶¶666$éÇY ˆF yF ’N",--ŸŠžååå5pà@Ì· …èé,âg@òŒ@ þ.0^+âÚµkû÷ï ÓÔÔìÖ­[OH§ˆï€ä@üí Q]]ýáÇW¯^íØ±cêÔ©XȬY³´µµÿmëˆÖ’gñA§Ó5DŒ=;MNNž={v^^ž““Ó¸qãäää zЍÉ3øw¨}:«¤¤äôéÓ¡¡¡ÓoÁƒ«Õ.š†@ü‡AòŒ@ þM$$$Ɖ(+++**ºxñâ¬Y³²³³g̘áììL¯]3õß@øgvñþ;©~æ5{ûÕw6‹6/vÐG“ãÛHžD«@\ı±±}ûöÕÒÒ²¶¶ÆdeeÙì¯7bù/Îx¸n>÷È’VÞ[÷®±á?9ééé±ÄÐ~áží rúxº‡§ ûMÝx!~6«‘ räÛMÊ'ˆõŠÚ¹äý6¯€yÑ«¡û ±n ®ø'+!¨ºujGÈäYWÛÏ|ú0Fá‡ñ…Üí>ŽçzE?­Ù¼ï­ð‡ñ¿MMIæîEã7¦zß®ëõ2úÇø°IZÅ}Îñwæ*­½ õ@òŒ@ Z˜/ñòåËÛ·oÇÅÅåå徨¹sÿþý!Ÿ®&qgÓ­æê˜‡W[OŽ{âÚ½:RdÜ÷]Û0?WuÛó*M3êvê©c¯ œ{ÊÕï” 5{ƒvSÙ ÓÇnÿþ Eh»E)?>¥{„¹éØÁÁ¿ÜOø ˆ 3×¹C|S³?sû]À-¹š|Ûx"Øí§Néþ?弪¼Sg˜˜¤Ktœîeïº)•ÑŠ𢗩Ëemz‹ö?Ó󞿨g±6¬Í€ä@´fºˆ|>?++«Å©ˆéÊäégT§_`úaÓƒEnÉý£oûro»¢¼Z}ò JØÜ KïÚiÔò>܉/î³°Ç©y5<|S&XÍ€PP]‰ï ó7!$üìÌ8"‘[–WC×P%P¨´'øŠìSs–Ôn9?áàûZ±<ŸÚ2÷P·ð:y–êò ½´ÿS<#ˆÖQ„šˆßHNb¨ø¯>fáßßNª›°còÞ¦;w ¸LeS‡±+|½£òìÂåEawÎ'éXŽWØ$øÅQã[ì=÷j.Šû>)áäÙ7|ƒ=—ÏMèÁà§Ÿr_±ŸCg *>×HZ4 æÏ Þ]Ý:Ã'ºˆ*-/#Vx½œÙµ6'aæ•Í3ý·T1$«róºM^³ÙwÄλ4onð…*Hu±L–Wö\³kŒfî¦ù‰iU A~ q´gÈb‡Þü÷» N¡,‹[ýrG@šÌšG§Ö.‰žq"rªóžGä›C NÛrqýù‰h'ßø”wyÚ·N„©KR@Pxd¥ë’Cïå¥I?‘<£gRnê³–ž Ÿ·xß ¶$£¼¨¸Ã Y˦ȓ˶º vÙ’¦ïèD}vëíÛrÃ|÷Å-Ð$e^‰›¹`k¯TwQ¥/Ïz:ÜüVoÇӛCˆ&{Žú}Ö×]"¦«Ù³å'$Z]¸ËgiäõOª3LökøE…Úë7uaœøGaN=Àyt0ÄkÝi¢¸·4Ÿ¨b±a¡®<ånô¤n ¢Ç}<Œ½é5}v]¹0IWì?;ÿ&HžDÛ§³¹gÔŒD»¸°1;îÈ}õ³G¤ˆ;mIRXÀ©Õ[L1ÉÌ;¸ûÖ˜¤ðÜ ¿\Áô•æ[öfÊõ[àïÀKŸ©¡»uóQ›H~î½év‹‡{¶Ô¸æ«&ØÊYÏ <:¶Z¦òÅl«ÙJ‘wNOÒÅ’_µŸ…ç#¨Éœo)5tLWÎýÉÞ–*ê‚zÕ+£üà°Ý²)lMöÖÃwð>å<<ðÚ€{O·«‘ ÿÊÆî¦fb}r|4{¯ö3 ¾x©j´z×ÁSG+:F{š/v'x^Ùî·Ç3üŠ‚ŽÍZ§ÊìËf–É/æÏ1hÿ*1Èviæ©¢T6< ÑÖå=äö.â_ꛞ°dôÜ›óžXÊT¼vìÕÍC«çþ)zÎs=ýw8p‹ 3 W|º=µ—[|ÿ³îj¾ö¾u•º·^²^©½†‡Ï¼Ù­DJÛr}BÏ;i N\{Òs¼®ê’F¾<ËgfèÄøÈnñ‡§èá¥,ڶ蕊;ûÅÏRÆ9lt½—1ï•m!7a¡ÜݘÙ}œV[oMx&ÓÛ/ÀÑ_ðv¶fÍQIv±S~gœá_É3ø/ÀÍþX Pãk©Øôö®ˆê6c´6œXTdGg\‘&~£‡ÏV;“  c¬ [+Jøù/_)7Œë%+ŠDµò3ÉüjF.Pó(ê}Ó¤nPï7÷cʱlÕÃ]äx\UE³“XÍÕÓo ×— ¼ù55X\.§v'òÇ¡{ߪ8Ä©‰F§åz è©ô9**Íg½Ç*CS»‹ŠR—sš¶_($p«ù FªmRRÀ¬¦«èu—á|®æbÍuþê-¾¦Ý6ð… jd,X~';¯ª‹8£¾áÊŸ¾ÊÕš1ZNtÊR7µÒœ´õLÑãs¹!‘%‰ßîgµÓŸ<³«å¾ec_ÏV=ÒUT)Õ.j¬š+§3 WgWbRÚÚ%5:‹Üm±(BŠ-CÖpj°†á …|nMƒí‚úiõ¯œ|Éí€k3†¸å¬‘žîWò+f²ˆ>XJ* vIÛ¸<ª(æ yF ˆVÄÍhÿ¤®a9Ëžk÷ñ³[7áêÂM¯ 60ÄFcèögŒ=áùjæñ?‘ëO<{%¬Û†»:ÿ5¦@b¤ºnYRÊVÔð«?=¶œµùO1çsF dŬ˜”MÁ²0rpîÞëÛw¼Ksr…|¦„lÝ)•%MeÜzûUø–բР¸%Õe}|®ç[!G@ gyØ+K42•Ï)*ÏIņ{Ö,¶dæs¾’¦”2d—Tæ`•Š^>ÿˆ¨RÆ]´Z¨”ðÅ¡Õs#®©öÖ•¥å=çцwñ׊ÏÀÖk³¦b ›÷ºï”|U¥ÿ7<#ˆ6/ûÀ²¤ò¸C£Ć®v>â²hd qñ #J³hCV.ím·ÔÚ¤|ôÞí&cBÅãðïÜFÿø1(š” T¾¨âש„ " •D—ƒò§ø¤³¦%  ìiÒ‰õcCÄd$…ÄŠŠ’ºS^U ·ZREægêð=ˆ$&…òæ›"ç·8‘DàÔðB¡@H 6ŠF 4XÍè«­KÚºqן)ˆXD>_@"7²‚ 6ܰ7ißžÛ\W}L“¹…÷Î?6¥_}B}¹%7<êГ­$mLðÁ*5DµI¥ê ¯Ì­,âŽ;¿‚ŠÌi'ël«®æaog­µ„z;Õ´†«”,º˜#°UÄÓßÙw¾Ã0_Yªñ7«¡æ¼ü¤º$¯„G—“f[ýSYHžD[Eðì춈¥>q\»ó’…P"‚0¯@ÌL—zûìaGGwOgwãvO·^xñ\vÓAqFºæ®F.1U  * .œJJ|5”Ûq±‡fϲ*ºµ7þÊ{È!E-ñ?'ÎL¬ æÎæýO4qß§žz–EÉ O œ˜y—S^ ᕻٓv0 ‹ïëîúƨ©èYÊõÞcVv È…„Ú9ÚL>¨‹zgâÇŠé—ŽßÏvꥻr»§ÝܱFŒº¶“ë 7eÞŒn̆šåŸØw§ ò·Ç©ä›™ÖŸ´ûÐuÇùž®óµÚQŸ§^ï;oÿ*=’ 8ë`r”~<¶÷dïY¦J´&*­n6:Û)|ªK¶éX÷î™7xB¸~üL±ÆÀ×É{î~†·GNdô›mè2Ï?õ‘WoíÃ&}å¤euY95f}ɆÐÓuièƒÉsí\‡÷V*ɸy¯Ý‚­}j/‘(üËQÁ!òœœWWoÈÏ‹Ÿ<˜”UX¥¼Çí¯«ÔTßše¯öž|ïiû÷mç<\–­ëa5`šßËÇæºZª|Aå‘=FøõÚ{y§ó³±cl<ÖðŽï=Ó×gDo«õA)A“Òª rÓμ6ÞæÁ&CÖÝ”Ç铜ðØÐ™]xõä xU}=­hê]ϧƒS?žê+ÓÚoC#yF mbgC›‡ÌW),–x­g(­¢í±'}&O€ùU4&›IRZxúµ?~,F%€‚žËñX>‹J—2f§ûÚ:Š@ “i¢LïÑ+®~Z†¹oºÁV|ïjßEâÒM~EÛN½‘9AH¢±ÙL¢´qtF.ŸHÇgHÏXa\qW€™á»RB‚%º;J¤öÞ‘éTRÅå) –ïÌÕ†„ÇpRØsËÀÊjÌ¡&Ré, fãšI žàuÇz6È4Ÿ©%m´1élQY•„_ IL¡@(.?jÑÕœ˜Ê 6÷ ;ÛÆç )âILq 2osî¸"•)N¥kœ|1g\Ÿ½(.—GV•;šGlZÒ·õeÂ<“IëX‹—“O:¯{wúÍ9Cé†í‚«®ožï¾þ2Mý\HsÝ8o„jvò*³‰ sÇ…FuÍœ»þ†ÿÂqGÖø_è8}—­b躣Vž^jAßàç¹;µˆAáqå\–‡»‘Ž÷2qŽI›´ã´Ê1U·¬‹³‚¦ü|iÓØ‰î÷z{ïN^–ä{Þ£oÉ·Y'2L~)S×qgŒoÆ: #ÿZã')¼¼ñ8#—¡;%qϺí±.‡àýÕmî 7çò[¬Ôžåä®3WøèEžN—}£¿_eîŽáQßÉ vÛ¯¿ÞóÌ ³o¦.é^½ûÀñGYL¤»‘6*xÍsï¸Í½UÈܹr¿täÖ¿&Ú|'ÂÖpN’ÆX‡·ÓÞäPºÛïÚ»a¨*=u“›™{´Áþ×Éví¯ìóX4xåó‹9FL\-˜³5Têú¬È×Vö]4}]¾Þ@cFñ×ÙUbzkgÈ?ÿrµ'3tBq%ÕÒ+xñdC ÖJ··Lš½±ˆ.Î+z+>bïÅðPþz§sÂÃqb¹PeôÖÍ+ºJS+3λº,|VE£T¼Ëé´æíáqÿà'îO‚ä@´Yt­VÄÝ8¯)uæþ e€š‚´iÖÞz OŽì€©Ïv+…ÑÓ„Ž­ê2qµKRLl^i×Ùó£$H7Ú="çÊvK×”ÈÌô‰àmÔÀNöô_Þh°hz¤Sf‰ÒÐY•´3±ÌßÕe[¹¸Þ„ÈÍ}*¥¨Çg ~ayïÖñvxw8@cŒ)ëÍ›éþn^[fTV³]ÖÄ37¼n¼‹±ÌàÙk¼Â “ˤô½6FÔ`ÏÔa`ÞýT1Î{ûžjÎ{ÆœôZo±õ„Ðfùñqj¼Ò»MôVz¸YüpºõÜ^‰Oލ­”¼ù4áC¼R«œ“bâòJ°JEJ<0tŸL:“4:ö¬÷P¼È®+(¾ô ¿¸E±ƒ®(ØÌóq[¾vˤAsœûÛloâ=êaÞ‹‹þ4(²è\Îm¶‹sß‘ñI9–‹ÎLî&(O_2HÓÁU&íhp¿A¶1±E¬Æã¹‡-_ˤci;Ù­ò<¾)(¯¤Ó$ïM2iÚö“"ïïœQi›ã)N(L˜1hÒH«®y7iËÇ8ưogÝêÉ€’´D=cszÏ u>8yæ¸f<ðTnÚæmøžG]‡…¾°~r/BÊýŒeœ¶Ž¾é«¿wmÀ­ÐœHc€wQáï¹Íwöþ?É3hËTó0]àW‰Ž ^;WÔo½‚èŒa7ÏÚiôÅ׫Ûs9»«¾ŽvìÕ(µðÁ … Ö–ðµ¡CÉî[_¦—”ecñYj:úzZ€½07ç*ªõï£Í¦hƒàŽÁ®÷Ýç;´ÝêV2\‹µ:*îõ´é¡Tztì Û,×pøŒöšúhS‰X×=Žê¬6Åï ¥´u•ƒŽ>ƒñ]ù|`ËHaadv×¹KG„ºÞøX Òx¥ô×ë7Tʦ¡R\·Q¥ÇùB>·º¡@>¾}5Aˆ- Ë(¨¶“%ÁHC’]Ð*\ûÜžƒ¨õöŠnæÃÌölpk¸<‚˜4nQL}Þ ó ‡™¹5ò²¾ ¡ûÁ­Uu<k7q>z:ÚØ xÎÒÅÅYÌ™‰óÇMÞ¾òü‰ÔW‰dŒw÷mZ!ÑÅ`@·êõ‘÷néJcÊ< ó¾n·c@»ž.3ðV_Ÿ”ÕÉů¹©­ß5ôäsðíOeIçnØ3󤽖ª›·ê_ùðü» yF ÿªó^‚˜´8¹Î9¦Hw„òûe•œo>d$–•@þ ש¹DÑ”öÎö]d䌿ÿ #![wJ—”¡R^¥çñ§g¦gåðóÞÅŽÏ$…¸ÎLÔxÂÆ7U^>?®âA•¨RìúJ‘E•*ÿN¥~–Þ©øç阞S¡üîó7RƳ•¾›7©ärE÷7 ”Q‘AÁ›ŒO¼jŠT»º@ ]ŠÉ.x‰µè€µÛ‚³f¯Ñ.Õ{ì²à°éúy@Ì=µjòY_Hv›jª‰5ÀDïÅ73üæHÍ”ëï»*ÂB¯ÿSû?5@ ~šT{¨|Vͯ“9Ae.Ðè ö3øÝ ØuH÷Þº}m‹;&¶Œ¸œ„8Ueu§ÜŠ—­$áw“5‚%ÍBÏ A»è6„ >?iEP– R,h’ÊX¥ªê3þ R¿ ÙfëSvŒ¶5tC†Æõ™׌ý~bÜ$q“NÁû5øêÖ¿Ø?¨È/RU–*¿¢¸.Ï-çTˆ«Hc‡Ô·üøámòâ1Îí{¦mdñ#vn0jRC%ÃMIg¿{{c«ý©~î~ÍV’gÑ–!4ú+¥nÞ‹°íÚ§JM6>+ëÑþVÏ!•˜@(ÿ†”45õÅò"âß»©‘1Ñá D‰P—e£4xúú<(3 h›ÒΕ@/¬˜²'3‹énS´€{¾DúÚÎFh}m: ^=ã­«@!î’ê*RŸþæÎ íúÏV”ºúh¬R׳«4»ànýCQ¥TE•‚¦[@ˆ.—‡k§°N;¿Ô@hÒR"2.ÄCÿ€Ká®tf3â¦×›´ãœtï *Š4©tAN~¹hùWÐ`FK’]¯ãéG/qÆ{R¸x7) FàÓrÞ=]>ne_à½Y÷>ÀmHûN:3—:Ï?›OîjÓƒ¹îêöô*£Î ¬wÀ‘ÕŸì?ÂJIµ›õœé*A'? yF ˆVEqÖ#·2ՔݻOÚÒEI?dõ|99£tÉŸï=¥¹6jòï¹óŒ“I;~IŸ>À k£ÁkB7SÇ€i§ýúè\²Ú^šÝQkàä)f¤/Ý+È¥žO>¯>|XOøvâÊ[΋»I'oÙZˆuVÒÁ+“Vú.àv“%¤ž;=(àÈ-ν-7+p'ùXºò`uù&S +ßÞ9uã]Ň›‡RîXŽîËÉ€ƒÏÛ®6ýʸ‡2S\i¤‹Û1 Q‰ÏÅÄ<¥¾¿µç‰qø.?| ½½Að:k?¯Oõ• «­ÔÝ3wŸsßÒ_îGëWŠØmh¿k–¸—ß75µ“,K~Êä_9yR»øæíeY×OÞ›8B³äܽL!‡|õ^–Foå·7ŽSÚ½ïij}Í…FŒÑb5¶3éõ±Í1bÅï&Üé¿¢=SJ¶‰Íèè™VWFéév~õžûöóÅï¦÷)¾zãQåû²”sפjQEÒŒ5~Äú^yÖ‘ƒÅs6 mÜî³ã<æ¸-é¯!þæöiq›ÍƒÅ_˜shùäÌë}•ï/è3i±¾4cXbÔ›…æ&VÃútc+ žä2J“Uv/ÉítªQW…ÂG)’fKgõùg>k$Ï¢ÍÂ’ÑtÝsgº@H¦1¥Ø4Ì4qÛtÎ6·´š›©>rò’ø$ Mã-3xDš¸˜DóßDfÇѧœ–}®¬áaZ$.)# Àï »d_FÊb±q-—UsŠxa/$3™â¢ùL@T2ßuÌ0·°\(Û‰3eˆB¾†Up¦Ér"•%-ÉlVMQÃ-ö³Ì`²kÎ×›}é®m~q_dº¸œŸ“_‚ß…ç(§ ׇÔaÍ}ŸK*pgÝv¢¬‚"ÞMè2ùäÍQy…e<h,9y)ìªCÀ&“Ï%øÄ7ÛñóåÛIÀÿ+HžD›…Âh¯Òô÷™H‘VT–n*.«,. ߀@STn²º™%ÝžÕ(†D»ÍU€*&ÓAL¦Q‰-§Ä–k¹CR©ƒd³@¦”‚ªÔ—S|ú³O‘i/£$!Ó,êÏUŠLg+w¬×Vq¦tƒ1Ò ùQ:¨Ô?wµu­JC­vÍÜòåyÏMAòŒ@ ˆ_ç/ŠióäH››ƒä@ ˆV’g@|ÊOO_xP%Þ}Œe¿æÏ„!þ6<#D„×CWÅHÎ\vû¼Ùß7a‰÷ùäÞ Q»Ä-E,Ûzö¢®z½Ãfö¥àÝѺò¢‡˜«¯;YùY‡/Ivô×±<~vŠÅÕ®«ùëÿívpžÏ=™ã°ÑIùDThÔ–ÃúÁ{èú·—Ûj@òŒ@ M Èðr-ß>¡÷wC–1›4·OÍ}Tã¯ÿúª’[’J!Ÿ[Éá³-­h^þöÒ³7¬;/yNš£Ýü¯F,Íø\ù e`9×ðYÌ_X+½µä@ šC£R¾?UéOÍ3&“þB~ ©Á±¼²/§Y7ß1C$èjšØéÞ[Ù"Ó„-®þSs/ÕöüðØÓþ3A¾w»ßB4i³û ëO8<©ŒŸ*Ld[Ö…UÞ×Õ.øCsZ-HžDÛEðy³ïôÕIÏ(d>£«]âÑà®é‡FZydJk»;%mˆL+•›³õÈr3ÚèïRVÛ{Çæ è4aõshöhqåþ€‰³£oÈwÑ™´ËÏDòìæàµ±åß8[ŽZ¹Ëß~qÒs2YlüªAcµøóˆyl¶%Aqàæ‹¯U†yص¬S­/—sÃËyÖ¥R²ì²ç„.Úõ%> p™‘xÿ3OÈåí=¬ìB¸ÍìUÕ£ãGQç/Þf²h—çhÍf’Tòt÷„É+Þ–Öp%zÅîßÛùÊ´î§»ÛŒ Ü;õ¿×ŸÄÝ~ëj½v¶}Ä¥Ot–Âìè½sŒD{7ç_ö˜<'åU§¢Ri¨Ë¾àól»A;p%ÉD•|0lÆÜãey‚Uù]§¥ìqz¢ËÆÂ®ÁÏOL­Lλ;×cMJ5WÅgOŠ=<¢ÉêaŸîDÙM +¦Ó«j¤vì™®¯\ø$e¦½cJVÉmEjG]Å×çÒj$”c{íJ*Ú0~geÍáÄg«Ï ýG¿ö_”ØÞ:þÔ3¾0¼=´d€ï™ûô¾ÖóvîðíH€gñ>Ž‹ã;úHš=ôÑÉP/¯ÈOrSSo¥ÇxØÏßøJ Ù>nÙÜÓ¯æõ€)A¶Þq¹BUÓ¹;6z«ÐàÓí‡ësªªyrƒöFõëÐêîª#yF mNὪ2H@¶½ŠÊÐÉÃrlÎFï$Œº[4{alòØsáNžæsÇ1}Î?6½£UrÜý =¼»¨ÝѦi^Ìq«ç]%¬Tãg‚¯Î1Üeêþ£Ö‘îr 8· ßÜ{Vïߣf&kª ”~–;wÙºð¤Þ7®ç&®H´ÏØ=Ì"(lèè ³ú Ò,ú˜p*;ÙËäÐ ÂZѺ ì£Ñ€·Ž3“úïÁX…^ƒ³^ õŽŸ»­Ó‚è–ÛÐ1FTî×[)?´íå8ätYÊ`±g)aO3sÕ¦î<É ¡ݾ¹•‘wqÜ 3mỨ‹e7S?ŽºüÃ[§¢«ë”õ¥>eMá'k+[¨¬˜ž¢Àô´Î ,[·3èszlýROؽqz—.Íîi¯¾#  ¢v÷Êëaf£¢åž½~­F„ü„)¦JšÉsiÉg§ ×§P|¸{rO«•Ö9ÑÒÚ£¢gé (¿sS ÑŠÕgçµ@ <¶œùjÐûf™L;F²š|~IÔ¹Â꺼Àëd},n7ùÓ…‰C̺ëå¥ßí>m­ÛѨ­ÅU˜­c6~â–±7Jkz¹nXqzëzøˬ±¤Å§½•Ͷ¿Î›¢Î[Õ‡bç#+L}ì`¿· ¦êP®$®+/ª$ÏñÏAS[é&ZsRÉ £Ü™ÌçÕ0\XÁiù᣻I‚œýô èe5 AËœ»GaHèd=|«…²²ŠwK6_¶xî°u‡×δ–'çïð¿0<|3ýî’çN‰'ñ±×ŽF&F¼Ð¸§sƒ¸œjа˜Ô«; º/›Ûiyj^–ú½¾uðTÖ€‡Á&µVñêh³Ÿ;ôL÷öÑþJd½èCüb_Z ªxTÃ1ëWxÐù5%弿£¹e¯žpH]23tºš§^ƒI=·²š ¨¥¥À¦{Ô¡6¬ ±);_\·»rá][,‘”Ѩ¾æ­ÛúB‰¸õ©Ä”3^:¢¼Hã}—¥[Q%h4rµ–PÀ)ÇϪ9üú†Ü]àsvzÜL›1ä¬ü×|ofú`ß.d|Ä@A­«èx.¤I€o."ä•U° š+äUW„€¯ëYÅ"}êòeŽÀlH.@ ·ÑÙtîÑ]†EuÓC›'ÒÌw¥Ü]>½/Ë¡!J5¿‰_ky–sM%_$Bñ¦Cý)êxË:/1_8ÿZQNõ‹jaÆ›w £nì0‡Ëýé­¸ÿA<#ˆ¶‹°êjL`ÐÖ+À¦g¦æPû5„ ù"GT(À71Æ›+2ÒJª%»|Æ“ŠžýèîË=‚ïY¯S_¹³*xÿ`<4÷ÁK>q7:H%¿àC•¸f%ð±q+†„ýßp¹àÝ-a‡>›ÞrÆl(y»ZR]žXwC•©Ñ².•$>ˉWÍ:•&!Aã½;è˜\CÃo+ô6_èjã¾xŒ“n´Ÿ…k"üûcòŒ×«^D™º}©œK9Kqç=úš÷XÁ"„œœlèR\ôºê5¨y*Õ›¡Ð¡'ö—[õãvÅM̺z$GÉ·¯ ë:¬[óhůNÌñZû²’,(z[z?΄Üü?U€¼f×õ•¢j÷eñOä}*h÷ã,¹E¯*ÜÔùêš«0œ_•G‘¶¯Q˜¸nòJ'àV¬ô¶éõ¶ýÓ yF m–;»æ™--z]psœÖ)®øj†t#§‰€ Zô™¿@”ê<ÝÂìÈÏãAéÚëk·_"aJÌòÞú`é˜FÛ1å]m9 Ì/ð¿vÖˆ˜—)à ¿¸‚5@"‰Ð,¦°êã³—o, –Q…bv5¬6<ÿè”»iCÔô/Š.î÷Ð$‹B SÈ@¦r¡cÔ™—¶=î©×Dº æ'Ôø+p«¨L f1ÿÛ³Û+Ï™hÙ?±ËV-çÚu“äF×þÚ”7N9æhÓèµ¶4G­qQD"·’>¤ß` jGðýñKV'= Ö¡»ÇK¡ûe¤‰-Ç£˜»ORqptŒЬ_oZ¶o¨xøä6Œ1ûñš!«jHú˜tŸµ›ZÔzÕ˜i2†È–†dðˆíDþ{á­Ë ©./ÍÄ“ÒÕëâm¯/çE×ö<Õ±h¾l«¹±ºÁØÛÐDž‹®ß0¥»tcl]ð¹þò•m†©hdíÎZ°e˞׈ßÅa&¤Ò›Ï‚M‡–#¤§¤QUê¨AíÄëzð.@&דÈdâ/Šuá… \¦tWmü>7(¬†Ú±"•ô%#¡PHÂÀȲ:в»no¦ª — .zÓׯÐfµ¡Ž\w¯¯<#Ä?G»Î«‚ÖÇ]ÒÖ*½yüU!IƒÇÜ pq¯OÈçaެ¦µ[µÚ2vÜz·Õ½—NÐ~{éb—ƒ] 4ÏTÙ>Âbæ¸×|£>šõAÝƒÖ ì;Ïs±šØ4cÕâ÷Â.Fz˜‹,¬ܦc9aeòE…ué7v”Vð¨ÞgvÎQä¾>óˆÝxØUùn£¦X8uþáG›¬ÉïÛÍ614¾Ò0ZÀçrùÂÆâÓïåš»toGk0*Ýz7lP·Ú^V®ËáÝpUoæI3|üÚ0ÀG{¬ãFÜØÒyoßÐ{š uuaf×Å:vûò¾Ê¬âüÏíÕ{Šs¹p¹ÜÚ‡§°’… "‘€U¤®VTP_±k¢–ÔiŒ¸yV½éܼŒ‚öÃÚ±­óMND¶í­”NlÝWÁgs°ŒID©Ž P‘rîôc¶V{) ‰ÔóWžLî!ÆPUáãÎû2û‹¿7õ·0¥ås8|NuUÖ%+ß«úžD•‚Ný{Ü_»6¾'MUøq_Z>©>™N©¯>N×g©º/q 4\j3iíæ%v¼Ïù ` öxçñžç×´'>~PhéÜùO|Üþ0HžD›ÅÐnñö!gãÖf u ]éu'ãè¡#¥O4¦[«ß¹j<®ÃÅ;l¦º¿ûX?gKy»„ô”˜uûöœ0:ÈÓ×&uCTÿ%^]ͳµÞ»ò˜b÷F Šõñ¾t]q]üÉ¿dbÝ‘nzðä\ÚÀ1Óø÷¶§õœÂ°ÿógÎÛy5 ºI'¯FlØè¯Ô{”•¿×Áé§_Ûv•>–Ú3r»Ë) 0¦}=bøôoY×ÉÔ+δn(Þtpƒ= “ƒb'ן¬ˆé_w48dÜ—¤Œ¡}†6ZS¤c?Ëýš–-‚*ÝÙ3$¦þÌjXý‘¸ò€1_Æù%{Ú¬ÚÔäI9§EaNß²»u€ä@ ÚD„‚VµN(âw@òŒ@ m‡¬Ó±‡oV>,ܼE™æh÷WVÆFüË yF ˆ¶ƒ¢Ñ¤£ÏÇ €H¥1©?ŽŽh½ yF ˆ¶™É–nuËS"~$Ï@´:<#Ñê@òŒ@ D«É3@ ­$Ï@´:<#Ñê@òŒ@ D«É3@ ­$Ï@´:<#Ñê@òŒ@ D«É3@ ­$Ï@´:<#Ñê@òŒ@ D«É3@ ­$Ï@´:<#Ñê@òŒ@ D«ã¯Ê³˜˜Ø±@ ˆ¶Fûë™üUy5jÔ±@üAuu5“Éü· A þë„ââ⿞Ï/˳P(l|šŸŸÿ×@ „ÂÂÂÛÑœfºù“ü²<çåå¨wð{å!ñÓJìoYYÙo¤ýeyVTTübøOQ«Íl6û7’ÿ²<·oß¾{÷î ºõM"‘~£HñÇáóùåååÿ¶!¢ŽššL%1­”““ûä¿,Ï&&&W®\Á~ 6#­¡Pˆ}+Édô¨$ÑZ˜M$%%%#ù/™±ï¿ŒŒÌo”„@ â'A}m@ ZHžhu yF ¢Õä@ ˆV’g@ ZHžhu yF ¢Õä@ ˆVÇÿÙT¶À„ÍÖ«IEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/netconf-server-io-loop.png0000664000175000017500000016271514770023131025447 0ustar vladimirvladimir‰PNG  IHDRráeVIJ pHYsÄÄ•+åIDATxœìÝ|eÞð¶½×”MÛôiZ¨¡WEŠ¢ Ø===¯yçÝyõ=Ï;Ïó<ŠœŠ‚ÒAz „Ré•ôº-ÛÛÌ;»¡$@”Èý¿—·ìÎ<;;;;¿yfžçœ¢(†~¯¸@¬Cb2«ÀX† Ä*0d V€!sç±JQTgggqq±ÅbAQð!\,à"IÒårq8œˆˆF3øŒ»«Úê™3g^{í5:\QŸ») >èª#«<oõêÕ/¾ø"ŸÏäŒw«Z­¶©©I¯×ßM!ÀðDDKK ]sü,w«0ð!Ð×]Å*‹ÅBäÊ?9œ ðƒF×m6[o½‘Î8&“Ù7étW±Ú÷ÁSO=w7÷Vww÷»ï¾ÛÐÐÐûÏÛÊTÆv°a³Ù³fÍš4iÒP|ÿššš6lØp%Voô[† Ä*0d V€!± ˆU`È@¬Cb2«ÀX† Ä*0d V€!± ˆU`È@¬Cb2«ÀX† Ä*0d V€!± ˆU`È@¬~Ÿ([gÝ‘½{*º$IRTï“O6eöÌ(9A¾SÛ õ{¶¸¨5{¼{§F%X~ Sç ÕÜ“WeqzKÌ{39þÙ‹çG+i/?{ð@n¹óö?Ë/nÎÌ)arvߥHÛå0v·ÖT×\lléÖm.ï“a,O$–È ÿÀÀ?‰Çık?¥·šÛi×w6WUV54·vëLNIϳy2…_ˆ&"*oïÖ3dŸJpe™3æ¥kdèµS“ŽîS‡ž+ou_žñ®PLš¶`ú(FsÁž#§»,žÁ®PD0jڌ̤Pâús=Š"Ý[wK]iYE}S[Åáýl(Έe e :(88Ø_!"Ð º¿PnkéÙ}‡N×»©¾«œþFP‚+Hš¶pœF†_³NèI:ìæÎæ‹UU5M­“ÕMψ lžHá ‰ˆ rY8ŠÞ°õ‹ì(ÉÝ•“o°{7Ÿ+ÏÒý?‚4eÆ´Ø ñÿØ×†Äê÷ŠŽ}gÓùsUŠ Ê/:};Bõ¬‰ó»§gЉk§&ÝÆ®–²¢ÒÚºÊsy¥vn`zjRxTtzˆƒžÏe56V—–UÕ–åªÖy#’ÉJļ~öå°hÝœÅèñJî±úò7¯mD•J.ÑwD9Ý&‡÷!Âõ âÈ•èètYº;±‘hFŒ ½«é4UÚ¿gÏ¡ùu-Ý—ÛCö&Û]¾ý%ÅÀ…R©R½pÅ_”ιv ÝKÍùÓ{wí<|ülM›ÎíKÈÞ…¢(†¡8Î ŠMž˜={ÖÔ ±Ár»:?ý&VC{EqqMeUQa±ÎX ”)_Ôâù¿Ÿ=.f^·"lÆîšÒ‚ÚúªüÓ…]^|rJlLd’0Ö;›ËÖÞT“_Z]QWܤ÷}¶B%ÁûÙ«R»¡Ó¨ø9?,5q X¥<ºÚ¼í[¾Ù¶7§ºYïôå‚P·‰‹åʸô¿xý'A’[tÿ¡\}ceqUMeaQa‹ŽÞðQhBzBLTlB¸‹¼vZÊaê(©È’h–>óʇ½©äÀË«^ª _ø§¿ÿ4)HÄæò8L”7þ_¦Î2uÕñëU¿Û\lus3f¬|ñ™ùÒë¿R:t9[ß]»»oí*ÖüñÕ‰qj†^~Ò•ÿù¿ÿdW§Š›ùÇ¿>%ã^*‚t›µÍÞý톞kJ¶vÕl^ûŸu_ïùüɺT‹Ap®,kùÓÚ:‹öýúÇ?úæ‚ɰ9œÝ»þ»6":ö×áö©ï Lù„§ÏXnl+þÓSÏm5D>ÿ‡ÿ{ Ãí]¡;îÙ_¤Øl=GßýÙ ÿØÔfÆ‚c³ýû§Â%ìWhí™/÷Ó~ßåé*ÙóÛß¾ñÍÁ|4hô²‡WL—¤dc¤IÛVq>ÿø‘ý;÷;u\®µy,í>ƒà¼¤‰Ë~›¾ÐÚYô×_¿ôÎ×ç)‚;ö‘ýí©B—Ç»òÕÑGg¦ö² ïÿë“{ÊLêÔ‰óVfge¦F¨•tÕ”ô8MòÏ¢;ùèà¾}Y3—¼ðòs¢¤ø5MÄ/iòK£­&í¾¿ÿèåövÛØñc¿õÎÏcå|&‹Åå°¡ª îÄê÷ ñž°°y\¹Dˆá¼„Ôd)U{$¿¹¥øðºµŸ%=—,¾R{DÌ;1—çPÉYe •J¹D|©²‡lˆÍbX¤_."˜B=nâ$qûºMŒÖS[÷žíûG3aâäq2ôêsNÛ19FïH„à)“RÓ“Uü+¯yl:wÁºÍG°ËOPŽîÊï¾ñú?64Ù±”i>ûÂK ³D,ìòÂ{«a±YÙËÌÝ'·ýû§øÂãrô©ƒPnKûÞÏÿõú_þß䎙°èÅ—¼<{$Ÿå­ú·STphtbjöœÙ™#þõ÷}||óG:ê·¯>ÏÁ¯¬ž˜Í¡r“Pª£Õ=ÇO—Û[Îm^÷áèøY)ê+'Wé2™l®÷Ï£àa‚³dr…T"î]Fð".%²zŸ` 3ÆOHRð®_Ÿ”;Œ×òáûE~Ý.cÃæO?Ú¸ûªÉúéëyb^ª¸¼¾5IicgÏϧ^úêÜ€%ÝŸ0‚#s¸”‚ÏéÝj]y—ËÄì>[%EZÚ.|ú?þõ£m­þø…«_ùù Ó’ÕlÜ;·âOog!šÄ´q3ff'ðÖ»Ÿ|½óÓ·ô6÷ë¯>;!FuËÓÁŠ3ùB&žrþ¥Ÿ †ó*…LÄúî>5¸ïA¬ÞC˜:mî’‘ú®_ý­¸Û|rëúOG$hž™+aÝÅ!2‚ô³ÁXr™ZÅÍë;K¡–лÆÕé©ëÎx!×”…LIXëø¥‰H‡îàÿó÷ÿ|Ý`r†ŒyøùŸÿnùø°ëN‡öÎí=8ªÒ'O_»ô¸®œW£kÀÇ7ÿçßä7š%Ñ“Ÿ{é…³Ry8zMÞ€¥gWÏyìeÛÖñú‡•¹Ûßy?0(äÕ á’>)Ê L]ýdK÷×o+ 5gö­ýïˆÈàgb•œ»¨rô»BQž8(€ãñËu(mEîîýÇôNd̸i ³S„Lìš²Œ§ŠÊLJØ_\ç x¿ó¸ŒG¶|öá†]­&†fÒ’W~õã9Éê¾ayé BpYȈÕϾhÑwþõãýǾ|ï_Ê€Ðß®ƒ»Î ÀX½—ŒŸ93{ÙÙ3Ÿ6ôToþä丨G§Ä°±!Ý ¸Ê?tÄ€åûºÂ_“8eŠZ.`2ý>ÊTEŸ6Ê àt¡­üøg·Vv› Qè¢.Ìà#“x‘ƒ^IU§®fçÆ9ezÊ•½`vÖH.†ö?3]±ä«¦Ì}hêî5«Îíýf㘠#žŸ#êç-±È ®ì¬/ÿ¿õuúÎC›Ö}–”ü‹Ç&‰ˆ›”|‡P®À/>>TÀgßêCSîÎÚ‹­­ƒ-–Éøý5IBPnBƘì.¥œ7`Hÿo¢ôU¹›7n®h³³Äš9K–Lˆ èï@ÇAx‰=´èØÑÓû*»oÙ°cêø'³ã†ø×À@ Vï1ž_ìŠÕ^(­Üpº©½ôè§Ÿ|«ùå¸pñPb#¨jäôÿœ@¬ÞcóO›·zMyaõ[åZë¹}_>"5ò…ü¸ØÀ3ˆ"é=;F‹vå=9)çV³õ·œ4B Ry uvV?s¶ÓÆ@˜,uú”?ဿiÅ1#ÞGÞzœ–ºó‡Ë:-ôcYTDl\è@§êè¦ !6!RŠœÑRmÎÕ·'jú;_Ž B2V¯^Q^V³§´«éü¾u%&†½˜äǺ=½.p¿ð˜¦Â¤J±PD0z\ÕÇwüi²põ<œmïc 7ÎM‘.mSÅñÇóŠ+š:tv7ÂJÕ¡áic'ÏHѵ[Êch-ݽóx·Íá!)”ÅÍZ89Fj×·œËÍ9q¶à¢]ªá#F§Ûã;I€ Š¢xHÚÄìôx.PnkÉÙùç꤇þý:‹«œ0wV\€„ŽNÒmk®,8|0§¸ª®½Û覿~‰"42nL֔щ®/ÞH§þô5­N‡¢‚°s§MãÖ†òœ£ÇÎ×´ŠÓWþdYºˆ}g3å2wžÏk¢¾ˆ_b|„¿è¦UÕ+ë“4"*RÌÎí°[+O­íz X$ûN3ÇÛòÎÞS[|:çD^yÍÅ.]ÃC±yb?uhBjfÖØô`ÿúžaôÓ¬-ÍËÍ9]Ps±Ig°¸(”'”†hFŒž0aT’RÈîÝL\ÖÎcÛv—v|-ìÑ€Ä ³çÜÙseUõ:ƒÉó ‘1~êäLµ”{cß5pO@¬Þ{(!=gùcy…o®ß×m¸¸ñã£ãbŸŸ›È¼Ëßåé¨Ìyç³²q,ÉNQ ÝiPÊPWYUQCW›!ä j+ê»CtÛzjÏ•˜éÃmèï§Vqþ¬àÀeh-öžú³µíKF‡±ú[GÊŒÎZ¼zuiõï߯6ŽmùüóÑi¿Z5MÌ‚u@Úµ-zÒ_%¸RÁ@q#FćŸmª°v–¯ûë¯Zê.,Y4wtR´ŸBÂaÞªÒGylÇ·¿ÿþ‡_ï+d©c’“bý%TSÅ™½>^”ºìñçž}ê!ˆÎHÒÔÕxòØ®]9åN¶rñ«J¬Ú°{óæ=GNÕ¶ððÉ+pÛr¶ïÜW§w ¨ iÒŒì £Bqæ¥Óí(Nל¦Î¼cû÷.â&d?¼xŠã½yyrË'ï}¸nÏÙÿ¸I Q†©&ÿ–õk¿J™¾úéç{0KF‡%‚¸í憒c»ö¨h6ûg­äáNCÁÞ­;öä•Õš‚1ÌìçºÓn*e×w^,¯÷VøœÐwà“Æ Ñøñx8Ãî6t””¶'EÈîpÇnhØýåÇk×}™×h ŽJŒUâ®–ê’“ß~µ÷›@¬G½üñ5Å¥Õ_åÖkñá{#þ’&¼›s;éÖ^<±uï…°I †pY”»£¥£­ÕJ?Ä1qt ø–Ñ?—ÃX]or{;•r\ÊLmáÉ%]±³xܶšÊ.‡‹dôŸ”tµK6iþ£Ë ‹ÞÜpÒÒSñÅGÆÆE­z—C.P.Ó¹oüí¬äg¯¼œæ7àaÂL}péÒ¼Ò7òš¬vcîõoŸÚ·%&)}ü¸ÌQ£3ÓGĪDœ~¾bŠì®8öî[ÿ|Ïùà©ÿægOOH eHO[ÅÖ÷ÿÛÿìûô0@óÓe£…~I+¬N wž-¬o0ÿ÷oú@… FNš©©:™ká¥f?¸04ƒíhsãYÃGNúù¥AbNï1}ü™–M'ÐRqäœvÑÓ/¼´l‚BÀB(gÕ‘ýû¿\0Lxü—¯>õ`R¨ £Ümå§ßãWïmßõ¶ U„„.§Ápј¹ËG†Øº+ªšËu¥9ï¾Õ*d"‚ˆÔ™¡šsy…w³¶ép·˜µÍm6ßcž¿HÂÔ©|Lè/cr˜ôÁ›ÝÚÓÔ`ô|‡»9Št~úÆŸþ¶®Ô*_öüož[1;B%ÀQÊnêÊ;¸ùÍ?ÿߎÿ¾×e´ ~ÿê„HioglWOÃæþöæ6uñ“žõ—Ï(á ú©áèŽÏþúæ{Ÿ¾ûOûó«OEËXG9cÕ ‘¡¬ÚšÒÜ* S2wÍŠô8BÌóXºËÎÿú³õ»rË6¼ówÅ{ëשùÐDëÞƒXL™¾fÕ¹òÚ¶Ù‹îütøˆ= ±ý!{´-eŠ;/¥¤ËRt2¯Û6Ô?2Ê­5µfo„®g+øœ;8õävYÚ Þ‘è ‡+à ê!]áq8<:HI©o5º=äͧEÄ!É+ô\qÝîómm…ûׯýobÐÓBD·³°¤Ç7jÃÕŶš›ÏŸ«mKö ®†Â1 Wÿ¬«ûíO·æWµÙ\ö®¦ê®¦šû¾Q…ÅgŽ›8cæÌ)2B•×?‘.ý±7ïÏ·ˆ’,_9s\2]I£_ðSæ=´dëöSG.ìÚ¹wáÌ‘Ir6›+ˆ|WxB¡É\óã%iñQ* «æÀ{/}PA0YÒÐÔY3¦mÞ_Pe0•œ:Uß5?T½ò^(‚:tMçòŠYQcfOL• Xô’8 u»¾Ùx´è¢ jöª•ËÇ&â¾å¦g=0æ¾C%‡wî;žªbÓ^6_ d3½_!ÂTŒ™³rÉÌÑÁ~,kí;¿ÿí©»êùI9ìÆn³ï[ƹ|\aOÈÆ¼íÜN·w~Š1ø¦y··|ô¡Fé‘uŸo/jgŒœ¿ìÙ§N¼t­A e?ð˜¾æ|ù›NíøòË´ñI/Ì•(Ãå9[×}¶«ÎȾôÑ5+應˜½³…¢Å+Ÿn--üú#»?[—42ýgËFs1ïXc"‘ ÷³+c2æÍ›.é½Z›8")Lhåw»Î7úæ‹=3²ËŠ`A®Þk«Ã¬üÑóV>^Zö»wvj-Í[>]ýâü”Áž ¦,'v¼¿êÔ†¾Õ7—¡U+œ:´ JQ¤Ýé°¹| M°Y~¿bŠtÙzûÚxÇÇlƒ c¨¯ý E¹¬Ž[2ˆ`¬°1‹ŸZY\þë÷ë,æ“»¿übdbØÓóeƒ½ÎGY õû·o­_Âiî:v¬Þé—8¸¼ç½YBõ‚çÿ¨IÉúæëoö圩ªi0»¼ UÚª ·T_8¸{WÖܥϼøü´Å¥õHQ¶öêãÇŽµÛ)¿QIéñÜË+˜ÞDäA I!‚#õúÚªüâFs¢¼ÏP+ vÒ´ é"oÂQ1z…ªð â!(;~|ö”ä¯êsj[Ëí?Q:&rBßQ²ªóö+·ŽZ5-9Dâ=B¢Hmù©#' ÌnFxJzj¤ ¿üõ 87"">ÚŸUQ¥?_Zܨs*®i„…‹#&O™–'§7BгxÕâ¬þÜ;n†íÛËáì=„Á dAô&Å$o Q$é²;o~ðu·H‡¹äÈÖÓU(_1rü´?^ŸNçÞÎßYS§¦nÜùmuë¡C‡ËOÍ âºM'í*l5ãÒèÑY“‚/gªo”) 6e†ÇÎw]Øw(ç‘Y©t‚^óãèíö†\ùF8‘ã=0ãЉ²o ­çw)žŸ¦âÁ^ýƒ/`aKÃ<¼òt^Ù—Çk̵¹ŸøqJlðĨA^b…%Œ™;#ƒ95(·ãbÞöÍÕCÛ·ä2ïN«w4ß;¹rFïA.]›¤|² ï¤ä¥Ö78Æ(ŒB<áÇÍ/úǦ“=Úê/?^ûø”š]âj®<ö×/\{Þ‘´vuÈç ®€Ëp¶päÄ1©YËË Näæž¦å7uê­WOgõž/Þéî±rþð³¬(¥ïy´m•t’È$B.îì1¯|~‡ÃÍy£Ô¬3tµõ ùÍú$¢ i3ý½øˆ00aÁ‚‰» õ‡öxpöèTÕ¥³ Ξƣ{Žd#¦NË’³|…QκŠÚú¦z‹RHù”ÃÜc¼ò&” E} `Õ¶L=v*à¦]Œ 1NM¢Ä÷n¢Ó»”é”w$ÌÁn&ôvÂè–ŽØï°{˦-È-1ÚÜl9?46ˆuÝEPy˜&8B‰T×·•–Õ7u¦…Z mçÏV;Ý”@"‰P]·pWdž+üEŒ.ÝÅÂ’I# ½Ê”L‹•ˆY:­µòT‘Ö2bõžƒ/`¡w"IÓžX±¨¢âŸù]ÎÒãÛÖþ79ô'öÓN´Ÿ™™1é3_úÕ³~—{ÍPnkþ7æ£iò…ä±¹Þn–ï ®§ëN²¡8GÂõžÖòP§Ãâ¢Ué´9\¾V¡(•òl4Dɬ\UPÖ°³ ¹³tÿúbâ#›Ê¿õ|½˜á#çüæ÷Ï\Á‘æ0¶îyçµoo»Y«÷(‚+R$ŒÎŽ5ù¡ÎÖš²ó‡woÞ²}w^­Öm7œÚ¾áó”Q‰?yPæíì6Ú›ô:_ër6¿òH!¯O°SWKUÃ;²ÇÙ{ÆàæoŠ—6: S§,ûÅÞÆ¼–¢ã‡÷å>”¼(ÉWë'Û‹Ê«‰ŸôÓ))—ŽsHgKgW—‰~èÌßöáªü­}Oò{lƪ oÌ»lN—ëÖ.úNDÜM{vÁ™*Yyu'å±v›Ï¬¼Ó ¯—’Ä/$M¥NLÏœ’=å¿ýé«#ÕNgûá£G‹—ÌšÆct^êmÞËÆ¨D””’&¹vͤeŒöþŸ ,%\<ÈÅ „$éÓ§Ù_ôµ¶½èЃ˧ĄŠÊ¡;~øH¥YùÜô‰jîåEº¬¶Þû-ª@͈Ôè¾_.EQã&ÓØiA’Á_ø¿3(—' Œfïi­©Ç{BxàuOYu»Ó{ÌÁd³¢¡è©Ö?ݨwÐÛ.‚!,>«Ÿ÷A‡émGÒËoõöƒrÛôF—‹ž…À8¼þ~˜(“ÏŽõ^·Û¤³¹sÀ‰sÅ<÷žKpMŽÿ¹Á¥‡!ˆÕa‡)Ž˜»ìñ“ù•_­ìi8·î£õÒ''ÜÁõ!:¢f¼þZ|`ìöÛCp?Mpp˜ŒÑÞérK*Ûl3’yøíí»¶02)˜y°Úîq¶é»: ®8Á€£°’úÖN}›·E°ä#¢”ƒèeI'/Y½²¤ìÍõ‡ úúoÖºÊug*LnHêäQ½ã»“Ù¯ðè(P¤NYü²¹µ¦æ',mu­­-=T¯÷ʱï¬8’6í§y=^x«u;ÈϲYÓ§§o;°·Ìp!çà©òEA£‚MŇsΉ’MJ ï[%¾\*+9{Éïù˜ü»NÏ›AºŠ¡`w3榮.‹“”Xÿ¥\]]N‹ÓÛt\¨Ž  åA%Eº\n#ðKéNQ—o1x³¡Ÿ.§b漏>Mn:VÔ¥—(Æ\aæJÃÄê0„ú'N\½ryYõ›çšMu'¿Y‡6 ·wTºfÄU¤e*zÿE‘Kw‡Iû½yÜí”ËILINÜzæÅi«:~ºmÕD9g€óNEÚº+ ë]±‰ñ†±a‚xÇKööÚ¦‹õÝuà­ë!ÞûÆÔ_¬ëð¶Ë%g„ù±[iD¸ªøÅ®.,®Ú’×ÜSsä“ÂV%äçíSΛöÔïF‘RÉ F¢Hcsua9yT²˜ÕOÃ.gGgNLKZ{²¡Ömw8ÎÞ{œl¯‚è6Ù­¾{³Eu A’'Íž˜v¢ú¾±`ç¾3Sĵ'wŸn`ÏøÅ´%ïj, 8›Ù;”¢Ó`µ¹<ßY;ÚA,4S¤ŒI!Ý^®ó¸Ë/vìjïÖÉA9uåµMF+E)cÆÇ)o2|Õq›šö<ÁÏå½›/ÊðqºÊ齬µ¿¦Q”ÛawYIßÉ] ‡å½¿GÄõ^ð¦Ü¤ÝîêgÒiµ{|ß:Ƴ5@•Çn²’nïQK"ì¯Ò ¾g«ÃBGÏ{äñ₆w¶wÙõgÅÝö»+Óalúú_ïZâæ¬\’Å¿»ŸÊ Êš29zç©‚[sñ¡Ü Ç*£oÝ ˜rÛJ÷ø‡Ö/~óz–£kkšÄñc’JÔÙ.œ+,\˜ ¾Õ>„rõ´Ë?Ûh÷VB’ÇŽ‘þàAñðŒ«>QV·¶\ë¬8™Ã$]œ·ñ‘¯ðö)ì螺ã[ßþ¸àéÿü;;JÑ_8!‹Ëx»Ç°øímã‰â|¡JÅÇ*Lö®.m§ÎN)îbð¿>~à¤Y3bvžÍkê>¹×±IªÒ‡)ͨiY©‚>uU:+©˜ÍÐÚím]&»Û{·“Ü1”)=f숰‡kÌÚʼ³UÍ Q·ì@B™Kò‹Ï›< ¦Ào|ö8µ”{ó‰oe¨=ñéGoE?“<Î×ÉQDªøÊpzÌtäßPµ¤Ü¡§ËÅ`p„j©˜@ß/LÆÊm$m.c·ÉuÃ1 å4vè­ôâ3XÒà!××bë–uVŠ4w¶šœ ÅyêX9î½sïA¬OôqzØüGŸ,..[{ Úí uÊ`Xº«vî9âÇ÷·f¦«ÁÑcg?0óhÝúƒ†–â-_ïž02,Nz«Ýé²6”´ëÂ=½§©T2rþüYϾWoi9º÷Û‚™c'FŠoÖÓ†"]uy‡+£ë삈q æÍPóoë'ãÒÉËŸ\]^ñÚ‡‡m¤ƒ.çúÛ¨~cevF¸øšâQfXX` ’YÛèl¿P\Þl—(îÙ¨xœ1}Áœ}…ÿÞiè¬Ø¹ãÀ´¡áÒ›œ•öŽ"h<µg÷Ѽ&’†¤ÏZ85]4„ (OkEm{³!‘}©-Á‘§Žàå”l¦úšçäÈkz`S”¡©©¥¾“þÅÉcbC”8Êà‰ü’S‚¾:ßbéÑ7^Ô’ÉÊk¶cÊÓYÛÐÝidxÇ)L” \ñi­-©7õØ0¦,nÜ)bõÞƒX®Ä/fܪ5VÿíÌEãÀÓßEê/–T-þP :pU±<ùtm}ûWG.ߺöíЀŸ¯ž*çõÛaŸ¢úÆÖ¢Š.7qõ.5˜`ÜÂ/ýׯã5'w}úEzàs‹£ýŒÜK‘îöŠÿ]·îLŽ'‹\ºråü±ôöÏL2å }âTaÕö¼f÷Ý\ ŽÕÔ]”WÖ5:Dɽ¶¢·Áª¡ðÄá3¥Z”ë?~ìø¸K^~`Dʨ”¯Ï|kêºðíö-SSG_wGnßE·Û§a+"¦Ï˜²í@a©þâ¡]­ü°ñ¯L%¹~ƒ@U‰é)É1§›Šuµ§7m;¸0LÊéûn—®ù}/å1…¡ó—?væ|õ¦œªÜoÖ­OŠy~I–’ÛÏXB·­2w×Ç_m¯é!eš1?±b\¤x›SKe}C›à]@c 'ÏJúâØá:Cɩܺ‡ÒãeWŽÖ(Ò¡;{êDa…ÁRŽÍ£æ#Þ¶ÁªQ“¦Do/,în+<}º#;*àêèß”ÛÜv,÷L]§FeM8p£0ÊÚRtäT~·•!ŽJ3!éš[Õ‚{buøB AêìG;•_ýïmºAä€Çã²Y­ÖþnKCz,z“‹Åæ ɽ\”J™óÓ߸ØÂw6í;óß·ÿä4w=ºxV²&@Äç\élèvÚMF}[]ÉÁmŸoÊ-gLw¥Þãíý¢Nyú…—t6ä¿ÛŽmüÇ<&íã,é/âqzã™t;ÍÆîªü£Ÿ}òÁÇ[òH~Ä¢Ï>¿fAàÐæíI™š½ú‘³åõ”uY‡`- iîÜöÞ_Ežîeó'Ç«„|]e¡ù཭gšýbÆ>öÔ“qGÛ:Eú\ú‡¯MIl·ËÖ]SR]¦§XüË5`!BRf, ôí-çö}ýÉ—‰/­˜ ôTe5ž?²û‹¯¿m´àñÙó—Λ¤ðuk¥¿Ý”i,_pº~í¡œ_mùØœ ßm)»±=wÛ—_î8®wóÇ/|xÉœ17ž‘!Ý.—Û×)×;n‡ÇÜU½ù“϶.õ°ÔÓ/Ÿž C«ß'oº¦ÚʺƦ¢£ù݇³òäÖ­þQaAJÑõ§Ú¼]DƒXóDiyéGª®)ˆ®ÿu7UT×7Ô–¯Ò{3—r”ŸÞõæï[n^×7ÖŒáBî‘nÎò]À»æE·£íbuCKGwWûÉsµvº,Š´hËwnÞÒ®V(üÂèý·°Ÿ>yFD™ÿK™:a䦯·»ã߯Ÿüvcú¨ÑqáÁ2GIz÷oèn¯­()*.¹ØÒeqq3Uþ‚>×éW%ÏúõU#GlübãŽ}ëÿZpüÛŒŒÔè°!—…n«Q[WYœw¶ ^댙¼tÉÃ<8kb¨¬ÏAWWCUU}S͹C5]6 Qº{ÛöŽè°àPMX°êÆ6M(K2é¡U+KŠÿðÉ¡ë^ò8zêj/^¬?ZPï] Ò¤-ß³}{kX€L¦P‡„øIx·9D3ÂWø)+«Î­{³öÐö¸Ô”‘15Ÿ»­†úòü'ÏÖt2&,~òÙçžœ§ì3Ú>jÞó/kí/§¸qÇ8³k½&,D&`“N[Agp g¯úùOWM ÎŽÆêó¥vzWKºtò‹BÃÕJ…”ÝïðÇÂU§Í˜>esN^;oþDa?7MC1Þ˜¹¿Ø¥{ë½/KšJ>{óç‡6…† Ù˜Ëf1èõffØÓ?{mÕì$”á±è»êJJ›;½Ÿ<æöâÂü`^´¿?½¶X·^]t;ªªë›«Ï×tQ¾SýõgïfF‡…ÇD«øÌK# a¬˜©«#‹ŒÿòóovüðWíNÉHKõ—ÓGzîén+/ÎÏ+¸Ðád-ynù²¥s§¤Ë¹†>e7tTU×45Ô*jv¸}·È½˜·îßo+¯Þ=‚òЇ«v›Ålìj¹xúx ã]iäý…ª|î;‚½³~÷go½Ör!'56ˆ‹yºÊ:t¦RŸœ½ìÅ—Ÿïéû@–"iÙs?7»±u;NÿëO¯VœÍŠó#H[sUÑ‘ƒ‡K;ˆIËå¹GÒü97®¿Žâœß!‚å|&¹m†êóg8ÖÍ _öÌó/­™çßßÓàû±ú}¢L ç·ôï ZR 3ÖŸ|ïoÕ EðŒ‡×¬Zx{ŸD"Q&,{bÍ¢) ý¦ƒ 8[?æ‰ð„)³ž>•Wtþ|iù‰ Gw¹(E(·‹dr2…22cz¶&<'ï€Îá²gñU!#g¬Xš:*33>LuÝ8‡]óÁ¯>üâðùŽÎ.CÌq×oøàŸûå£g/{j̓aü7o„­Œ[¼êÉ Í%„ªos[ѦO×:SÓÑÖ%R{W‚»aÃ{ÿÜ#‹¥Ù¯~xö8ñíݸÓ{/Õßþ+p^IYEYyUm}é‰='÷Û½7qÃXbE@ÌÄ¥+3ÇM›6)Z-¹öˆ Á¹òÉ=³{ïÁÜÓçjš:›jÊ[p–Ô/8aäÔôÌ1“³2¸»©ääº??s¡”+õ!CÅ·ÿøËyulÚâMŠUõ»ƒE0þȉ3'euMŸœÞÿX ‘G.}ö5MlÚÞƒGÎä_hìÐ×–ëèƒ2UpĈI32ÇMIo­¤½#gûç·¬ìƒCB¼c÷çÿ*:¬J›¹ôáy“ýnyÁrèÎüêƒÏµvvé Œ ï6¬+<øŸ¦¢Àˆ„ÅϾ´8#òJ~¡8;"mÚK1#¦Î9u:ï\aañù#Ûè;Íañ%!š½<25}̨4Ÿppƒ=Í…‡×~ôEQ}GW—^ê,¥Ÿ³Ônú¸ö¦s`2MH˜ìšã5D’¶æçŽJ³ÿHîÙü}ù-tUçˆC£ÆþdEVöÌÑ}Ïá{ëßÉ“ö§€äÌ]‡Žž(8´ùˆÙÛO˜-P„§Í[4aòÌYÓâÅýŽvâ0µ=´í€^owÒÛÅ©f<öÜÔsgf‹Ypûšabõû„ 5iKò—y×÷šD¸bùͺlлØQ>¿nô7G¤ºŠô^&.kák#fÝÞû#ˆD©¾öÚ Â Œ[ô̯§­¾ÉIf—*U·¼^Cg«(*ebxRæB³÷†ÛÞÑwÈ«CÇ“Éb±ÙlޝƒA E×F„Q©Ó3˜Í6›Ýát÷vò£«³ô¼.ÏãâýÌŒp¡³W½eGY~ª«…)ËŸýý’§û]O®ßn{Xa‹ýÒ&Í9.Ûj±Øìôçr]¾=B0—/òû¿ÿ·Ÿ 7vìÍȬÇz¼wÆö®ú9‚Ååó…^ï>7$>cÍ+š•7Ì,ñ—ß|YU|ö›kã]\I€ìæ­Ì„)PŽóðȬ¹&³Åît÷~L6Ç÷þ—âe+GM_7qñusBÅ­3Õ7¯büìUÑ7~y¾·çû_w²Á;À²@I¯Ïä1Sé Í·™¸{/ò¢8Aob¾Í¤ÿmì&°à´é/…fzú^L¿`ÿ¾FWYØ´¥O™µ¬Çluùîn‹`8›Ã…\f}«Là·pUøÔE+ÍV»ï^ªôÆNгˆDVÛz/Í„ÅÿøÅ*?åû‰ygáò"Ÿ‰Á%Õabõ{…â,•:ôvçBp~`èuî!li@˜t‰þ…ËCåw]†³búï.–ƒ`‹$ôßm͆ŠýCo÷=Œí|}%„à‡ jTÃÛ…¾5sû3Òûk6ODÿÝt\vû£LepØ Åy")ýwó%Äåê;Þx¸wòå¡›'¡ÿîôMû–ÅÈÂCs7V#¹`ð‹åÛàÅröm­ ãÈUAƒé6 îˆUøaðÞq`°#k{b2«ÀX€aŠtY;š/æU,Þ‘ÖŒM•§NµGˆÙ84ýž V`˜2¶]ذö“Çó-¸*8˜´®ýç_C"f<öÔ¢Q±¼þÆ~÷Ä* SbÿøWÿ|áêëžF¸~ÁjÕ<)‡``¸€X`p(ªá\ç—[»%S5Ï¿   ½•Ĭ±®¾¬ãòTn‹ãØÖÆýçÝË~£yrž¸7ðèZmfÓò¾®÷¼1‚b2nH‹ÀŒC‡p’K×< “ûsBY,‚óˆÀ`žJN ¾«ìŠxÙ’•¡©Þ)<ˆçþñëõ¿i•&J†\`¸€X`pHOc…¹Å̈ñãÊx蕯Ÿ9z¶ ›Õ›‹v‹³²Èlǹ‘ál6vi"zbI˜pÆ\TÅ¿³ˆÐ_¸pW#½T"‚¡¡±¹+¯±´¶;=A8ä*ÃÄ*ƒƒ tv¡×…"íéñØX6—ð†+Jà±ãcè×}SÑOâ0ZŽÓ&¨T¡J÷vC1êl>=ý\E‘?Œ_ßyGQBAË6†ˆUAC…QèÉÜ–¿¹œ•³ýƤ‰Bƒ8>Š^­»2X|fì(1/¿õë÷«uM³¦Ê“ãøAþ,AOvoŽ\74eÓÙM.PÍ–I ªª « ‚¨G¨V®0[>k-;Óþüî Á¼iÒ̱ò ™µ¼·VÊÀ¹¬ñóCV4860Ù\j_Kx¬(5C:!K‘žÀq®GÒåÑv::ødŸg©.½ËsËNË”Ç]SbÖY±Œ1Ò¨@8 À0± À !Ÿ=cUT`´dÏžöcg Í=Û+ ‡öµ›¸æñÑ‘lßé^Dª‘>ñóøè´Ž½û: Ë,•yE§»vÛ9yè3+ü”ìk 55ë7¼W¾•¸&lí]–ÚnŠx³%¡L ú9zQ’rÑ ¹‚ © À0± À`ÑMŒI$MðL‘/i6ŸÏ×åï>š«ß»±â²Ô/ zƒû f/åŸØPm<}J›s¤óÌíW{Dܧ²…œ>ÕKžR0uAHbÑ·¶Ú–×ÒxÑ¢ëo(вéÌ›×7^°q&tl ûx”O†#ˆUn‚ Â±4"fXœdj¶jççµoØr꘶t¿:ñòÐ t˜DÉ•(9IŠiã¥k߯ÙtÔtàˆqñ8A ðjâÁñ]hÓ[woj ~ b^ÒÕkžt—/"Ø”Ì;j¶Kgªö¢aÇ7-…zæ¼'‚'&pY© K« –Õà(¯Õ5g cü ôòð¿-U†*+8]ì}’"©Ž‹¾À45ŽÅÅ.5ûu[ç ]8sV¦@Ľíª*E‘Æšîßo,·°<ìŸì‡µŽ+¯rDLëúÆ€{b€ÁÂQªñtó[ÿtOÌG³Y ²­Îx`G[JöÄ~‘R_Ô" ¦Ó~xc-©7¥' ä¸Ûì(?×½sOìdõâÉ"¾w,BÒfqëõn’ôv°Ñë\f+Æç\ºƒ•~Éèv{§Gopš,(‹á>µµá«½zQ°`ë'Õ;ú(Jà™K5ËÆ ¹ðS`x€ß"ƒƒâ#f¨Ÿa² ŠMû¶ôÀQïÙ\ HôûådÕ¸<–/UyrÞ§ÃÅgôÎw×vÞÛS8—™µ"rÚTE¤NWh»;·µœ7²%\?ܾ㳚걪y³”áBÄÐÒ³k[ÛÙB#*áù#Žƒ_Ôt_TΘ òP¨JÅ´ZõµŽ¾ …±ðp½Ëwæ`Ø€X`pD!Y$œë ].ªw¸:ZYŒÇ¹:Ίãñ"F«Nª÷ž¬ô+r¸›¸4•áñ¸ÄXÞ÷6ô9EQn·LJôÀº¿cA0Là/I™ÍÞh!8’Ä‹BäR|ð1DG—¥±»¼Î:ÊOÁí S«™¡ÊLºšJSD—ÿ_ë¢|ôz}SS“V«ÍÌÌärû]²¶Þ‰Üp÷¶î·X¥<S[Os½Õæ )ŠÁS‹5æ•[R:LÖæ£ÎÊùs .J:œm•ú®n‰ "µ0HÃg˜,õç»{0–TŒ›Û-v HKQGÙ±¦â‹”ÅME&ËýÄuuVÊéìls¦úI0Gñ­~¬Œ§$¾‹ IÒår™L¦ÊÊÊs>_|ñÅ}«ôçÍÉÉ1)))*•Š ß=Ø bÃ×ý«ÕÓ¨=½¯’…ixŽ&m[Ñ_-¿ò:ÎDmmÆ’20Q*`’M§‹ªÜ‰2–N_²W똩BíÝyÕHÒhÏålÌooê &N1lnŒË)9\îçw){§©Ç‰„…)›°¼¼³±Ñ¨ †vçït:u:]mmíéÓ§sssËÊÊèzªÍf£_ …÷kÒбºsçί¾ú*::zìØ±S¦L‰‰‰‘Éd,ë^/ôハUÊãj.êjíÆ§> PË™¤š)Ð’}NJØL‘”…!Þ4rYl5çôh°:.MŽë‘¦j:³láj¡œ®u‰~!’mväךÜ,…DŒ1쀱„w}€Ñõã®f+ÅáŠø¢‘È-åS’`Hú<йÒÞÞ~þüyºbzöìÙ’’’îîn‹Åâí·q™Ûíîêꢧ¤Ó•9¿ôbÖïã[’xªŒ§Ëñx<ôñD»ýñׯ_OçëèÑ£GŽ)•J¡ò nî¯Xu:µZ»G ñ¼—N1_-`0<Î~÷»»£«Ãf#õEûˆËiqa|ÉðÅ‚"8â¡#˜$ûDX?ïé±9Úªu-=L‡ÞÈðx¬VWGµ¾µÍO2¹êp8Š‹‹×­[wìØ1­VK'èÓÐÕÖ 60™Þaj{/Fö>Oz—üêã+Óc>½iÔû˜áË0:wé”bø²™~²ßÇWÚFõ>¥+Ï÷-üJÆ{ï§Öçó¸waèêxOOOïbÓ[|:œ’’’éÁbÝNë3ø.ÝW±Ê¸4Lë-s°/‚) ‘ÄŒ–Ðk!n…rXLb°³ö¢ßɦ³Ø9âÔ )¥ÿ­×°njkªîѨetEù.“•ÃáЕ3FSPP“““ŸŸ_YYy%iz…‡‡?üðÀqm¬Þ,bél¦kŒË­ˆ{—þ/ýä•Çôó½Ó{|n|¾ïcºBÙ{:ºoá7¾Ñ•ǽeÞ¸7Nÿ—þà×­zÞzŸ#Gޤ§§/_¾<+küÝ­f2÷U¬"S"a1šŒÝ:%Ÿå½ó%½—¾Ù¸æ‹)å3L7Êe ؽ“zO;Þ¤hoZ“ŠŽ¤oƒ%Ò£o4#r‘ZÍóÞQ„¢¸¸$@ÞÜXaìÉÈã®k¬t^òù|:;çÎ[[[{öìÙÓ§OŸ9s¦±±ÑjµÒÁC×ê…Bá ¤nrÔÑïó7›ø{˜žf±XèÏXVVvåº:Kjøùù9’ÎÔÔÔÔÄÄD¨­†û+V1B(SÔ4|ÛГ(ð0–€#¸­6åtš{\|Šì18=.·±Û®Ð°5©’“g;Ïìe„i8L.Q ø˜»Çè!nsÛÍ£,o]ÊjõR¶£Ê\]¨EcE os$ÄW${Z uF˜ÈCW®¼qéçl ÓT¥­+³D|îmôä¹éçò åñxIII ‹/nnn¦“uÏž=………tèÞni·õü=äp8®œ¾¦/T*½¦M›–‘‘,‹{O2›LÆ{º˜pÕ}«t2ˆ5Š ‹‰†j“µÓâ`R‚iE<<µ(JŠº¬.3IâR~dq»$7l\.Õµ·;´-¾’/A¼÷ aʤ±Ât:­Fá' ã10’!éÔÙ»ÝCQDoÔãp÷t;Q—‘V‹‡ÃB$e5y„aŠdIYœ&£›ËÅû¤îNÑuS‘HD×M###,XP]]­ÓéØìÆÓûÁ¢èŠiJJJZZ]= £/˜Læ0<Æý«Þp©FNÿõ}R*½òXªõy…–vMxÄ8^Äå'ó£/?N™)¾öœÃ J Jéó$†IÃeôß]}†AðNËdJ¥ÒQ£F}×ïuq8œ'žxbÍš5tÝ”>˜€( ÷[¬‚û Žã ¦€ˆU0| ¯@õ mìq;ÝÞfVòŸ}©IœÇå1õ¸lo,‚… …Dï½H‡«CëÅ0¹’ ž“ÛáºÚP Å1™œ Pï5…n½»÷~s\!SäXšrÙ=F“ï„ÍÅE óÝÇÁiqh$] ›GH„˜Ãêî1y1KÌV« €ÿE« EÙÖý››öœ1“t°yH›I{ lÕ,)gxlŽœÍ»L$¡(Æ=#pñL™ˆ Zkôßlj9p¢G£\±DÒR¤ËÉÑvle¯lts•‚iÙª9sT1j¼½Ö°{wÛãzw |åÃê©™Bw‡iÏö–£yfN§.É’ò&Î šÉgyÜ¥g:¾ÞØš[æ;7hzó|n×ÙJǨÇbŸš"`}o÷yôb€Q¤Óuf{Ý;ŸÆ.ÓÌ+ ìöC››šmvƒãvßXûæ§]!Yêóä§}÷ukÿYigÆ?>M#[<Ã^t¼=ïT§LBJX .“EH& ,%ù­®Åä9ªØ ‚Nê€hél³½¤È>7pJº3X¾z¯b}®3{ièÌL¾«Õ¸þ£ú¾eçcg&q’ÇP]æÃ¹OëÀlŽsVíöz]¾ « ÌivUœÒv2¦(G„ã("ðãaUfŒƒ1Œ ›¾ië–É_^2>’@(«ÇrâxÍ®m]³F Â¥(‡1qT¨âMy(lb«ë|ûY1RÎÍ?ܹ³Êp®Â‘Ĥ“›AyÊŠ Ýáª.AÕ\èܲO/ÈÔ<´8 RŠ2âyÚ ýéw;å'$pÆãb(‚*4â×h¢eHIN{›lh[îÄ*C0ÎFíÚž=»Ûý*ÃT„$R’Î@˜ÕZe,ip)'²”†Õ⦒+`ªxä…jc·•¤cµ·ïµU“ÇÁ8iþJƒÍpe“ú ëÄñî¹cy\ÄÒe=oÓŒ ‰P¢”ËÕR¤m2P™!lâ±ZHºX™˜¢î¶³ÁE .(‡O(¤‡ÍH™èï@1&œà^ƒX``L>kÄxUB~Ã7ïV”oKÍMš(OMбªowÚlžžÂ®þÏÁñ¥šÛêjǘ*î!û9+‹â(—þÙQÌ´,EÜήâ“ÝÕ~~I¬æ2m-ÉZÑùÁÇfU„<#ŠÓ{Ó#Gýb¸,ÂP[aÑZ(1…î¨üP@¬00—É™³¹Ù=_6QÀÆ.‡÷öƒ;€+ü$ã“øÎêvlm \¦ ’x[‘E`Bέãaɳäßæ^¼PCŒ{D$¹4=‚c!##‚»OžîÜyPüб˜zï©çfœÃºZ*õV†ˆU…4Y¶­­j/•Fàu=Ç œ£¦¥G°E|ÖÒG‚Ú,‡7ÔÔœj V³8¸wÜÅäIó'I„ Gm­Õâ Ü¨»¾Êä‡ ‚•ø•PD0,~Œ"#º­P “!ä\nÇ‹ ˆ*Vñèrƒe}뺿–ÛÌSû8Ê@„ÜéóÕÓRxvƒµ®ÅA­Yg¯¬23Cy ÑàëÉ€ïÄ*ø!ñÞïÿÙ»ø¦Î{oàçí½å!ï½'ÞÆƒÍLI€†ì4oÚt7·ó6MÇm›6mosooÚì´$!„6ðÂx`<ñÞØ–m-kKç¼lBÆ&‰ðâÿýø“éèÑ#ñ{ž3ž3yKv6—C2E¬‚‡ƒyõÆQcxÈIs·}Ï+5Eì7f¡+ü~î+:^Ó>`sâ8WÄ ÆÅ‹D,l¼×Ü®Åc ý"(\ݬkf1äbùy° }ÄÛŸËçò㽯¹ë=ún æòí¡^±ŠÊjíวB'÷îÝ;>>Ž4--Fs X¿víZ©TŠRV(Î\IR££Žþ~ëœÔ× '§»Éù¤ÕR&“c¾kÀâ± æ™Ãáhmm=räƒÁظq£¿¿ÿT¦"è™ÌÌL:}–¥”NgxzF×׎Óî|}ÝwÝf·/š ²Û)Ï_"™iFÀˆU0ŸìvûÉ“'kkk}}}7lØ ®=€:?³/¢l6gÆ­ÙÙ…w²¦nFI.¦±´X,–\.ŸïZ°@¬‚ùAQ”Z­Þ·oßèèhvvvJJʗ楯`’{k_Ä*˜N§³¡¡áÔ©SV«õ ¼KÎòE=Ô±±1©TzuG7`‰Xs E) Ôªªªèèè+VˆD¢ù®Ñq8>=tº¤lãú5ÙYY¬,I«`î ¾Úððð‰':;;Q ¦¦¦Þ=ãSU]S{èLðÜ{¬L$ÆÅÅÁUC,=«`Ž \™:ã—Ëå>òÈ#þþþó]£¹ƒ¾{GGLJûѽâ23V7–~ºïèY…Béííu—ìýàî± î8*f³¹¼¼¼¬¬,,,lÕªUwÕ¥èëþëƒ=f~ptæZ“™VXwbçÛï}øÿH&“Íwî± î8FsàÀ-yyyéééwÏŽß)‹åÀ‘Ýœ¤¢Õ(SÑ3,.?nÅM§ÞßýÉÞíÛ¶òù¼ù®#Àm VÁD’ä¥K—vïÞ-•J·lÙ|·MD™ºóÃ]åÍÃÑùÛ9ü+cEá8Îæ ýWUVì‘?±~Ýš»­©À± î“ÉtæÌ™šš•JµqãF±X<ß5škv»ýTñés—†C27Š•ªFº«­ñ«>=»WÀç­Xƒ°D@¬÷£(jll uRõz}NNNzzúí –´Ä áÂ…ºÃ¥E!Y Ÿà›OMÂqBc™Ðì;qV¡PÄÇÇÍK=îu×mìÀæp8ZZZNžIcó•¾!1¡*&˜šÂi7^*+îÂ}s3bxÌ+…S¤Sw¹åBCÆde d~¡Ñ¾RáÆ% UÆ©h«»ÔCz$-VLþBÜA«à CÛP“ÉT^^^QQ¸víZ8u¾P˜s°áÈoüŸ ¬Ì¿¿ñâŠ0Ù䌠ìcwÉ'/½µ››ÿ؆8Ê»y¤¡¡Ršæ?Í(ÄN›ùrÃÙ—þ畉Œ¯ÿèaeˆ“ºöCœ¦‘“»Þ;PÑ%ñ ôñÓåÒûöŠŠØQ@Ã)MOÝî÷>h¡<ýýä"i™¸ÜÝÒ…ÿà_ðäNÖë,ñùŸŸÖýé¿ß“àu%ÛQ›õu^ÿßO/ä<ùŸOª‚$Eskò¡X5·¾ÿÊ_ûBÿó52îŒ=×éÙ&ƒËgÀò ¾$ˆUðÅ LÕëõû÷ïÉÏÏÏÈȸËÏNš_”ÝØ^ߪŠMÒÔU¯jÏ –qèŽÓ„Þ‘Ù©±|¼ß§pÛÓ›âØtÜn8ôQ‰`ú´À¹ò ¬ìŒOþõº&)7?=VÂú|ž¢àk>ñþŸÿy0zË·Ÿzj­ú®c]U¯þö¹¿ý“üêÙ$ÑØ¾·^z»„|æ—?ݘƦè=†¾ŠGºI;ùY)ŽÞ–Kܰäàêò%MùQž–«&8é’’üzqK\ÎÊŒH·Ÿ´…t–‡°—„Ý}][aZ¨Om(Ùóþ¨_öºô(’|«à p8mmmd2™6l ‚LW”EÓSßE>ô5¶ñŪcgû×%†ŠYÓN‰1$Y«ó9²/|Óx»Q]²ÿÀ0ð{ ¼®tâ™_Lц´wrüdéj™wûÁCe_,HaÑ'—œÆ÷N\½:H"¿r‘iêml×/¿o«'5øñÁ£][ÒÄÞ‚/Zen;[R9¨1‘;2¥ #ÖÛ©¬9w®¥oÌNÑ¥¾Y‰žBôq¤~¨½¢¼ªlÂá$9žayù˽¯C:FZÊžj'ågF;G.•”׎NØYBeÙ9¡ ZË©¯¾öÏñð>“nMQnº'ŸÑ ¾(ˆUp»P¦Nñëååµ~ýz¡P;~çE·\øoˆÏ&Òùèlù¥CÒ¼ož+]ïéºVÏȼí‹~ˆÕ8XÕ0,Íðä~^2© ˆQZÿÝÔÙÔ`èj ?"¸æp,Nçúúr?û9ÞR5äàmˆOóHüèÅ“% OÄ{…ß~dQŽñ“¿³¯ƒwϪ8MO[{ÇpRýðk/îo£å­^¥rôøð対mß}t G×üÞßÿ·ÎP˜›Ì1tŸi¨VD¤x{^-ÇØPyîÒ=/]Nhßzùï]¼ø‰ªæU¶þð»Û98é´Xdž>þ*¥‹7ø2 VÁì¦ÆøÝµk—V«ÍÈÈHKK»›‡zX8(»®®iÈ38W.Fe-÷xëOå%U“7?Û{é´YšŽ~ðÚh9¥h6s ÉýŸBZ ‹ƒÃäp®ÙŒc8ƒÉã1¬c½Ž=ftr”Ö­Î4BaV[×G—Ey‰²Ôì0ξsgÏmÍ õ`Ýî®Ê:v©©¡Ïžž”í—‘8ndMô×~²¿Ác˳÷ÝSȱÓÎ<äHfnš¢ùðÞc=[_øþºœP†m\ÞØÎ]ÙÊQ¤µ¥üh“U¹yëªäPI㡽GÎjÿë¦uIr•±ìï”VmØ´Ê/P&àŠÒScB`'0ør VÁ,œNgKKKqq1ÊÔ­[·O3j<˜”i¸­²²l€=ÔWµßi·˜ÊKK»,ŒódOÍ!4§h ‹ÅÆØ,CøåN"\ƒ6á°Ù\Ç&¯–@9V«“.asØ,.·, »v‚Ïëiï«:_^mîë)£lú Ëx{YÉÅž+Ä·Y!œí}ø_?÷³Ž´Ìœ¢U9\MW‹–ëåÏ&p‚Éó Ô¿wi gÈ<ÐÞCù„ûy0pœ`ËR’]çg9µ®£¦ÃuG_ké´Ä~mÃV9AšÕ#Ý­}ƒç¿×_JŸbáÊb£˜_âà:«`&V«jUUUhhèƒ>Cë-”ÓÚ×ZÏ)zjÓJOA9 ±Þô?½^QÚ2¡ šê Vxþæ‡6űpëà°Z ùb{€í†Á–NÔ#!Dø®V=¤µ…ð®6J)ýHÇ0æ‘îîEùHš-Y!,ægAI9FÔ,…BÈÀFºíª¼onZÀ!(§¥-VñÜ(­iÎ NåЦi£¹NíÒc*•‚s¥}@lÿ¦$(¾øä‰“o½PÛ­þz>m*Á¯¼Ýuú3zãä9ÇEÞtr’küeÍ+LUzf߱܌GòTEqe~É9k3CÄ8vÿ#8ÃÃSnkÿB¿Ó€XÓ»:ÔC[[ÛÊ•+SSS™LhÉ/ Óxã…iìÃ)‘!L”M”“—“½÷ÃCŸ«Ù¼ÌÏ“ÝþUœÆööòùb@‘ý ÅûÚžÙ³|}Á'ÿ[ƒz˜ñ›’®£³”i¼¯ìX?>#;=1Læ±*7b÷Ñ}Õùá9žtÔ'¦(ÇDñá3~9«“ü­x!EYQ!l×K¤‚žŸàùΉâʇ ‚$¬›û·¤EsîøÞïû·åúM5HëXÇ .(mCBFNük?üÍùK#y+C¶Ñá~ ζ›Û{„¾ •¿Rfò´ºÐÚ§Šæ1´„« œÉˆÉ{ò›ì?<·{מÄè§¥29Ö wÒä^>šk'h„͵{›²YíNçä…>°c|q«`hƒÒôÈ‘#(JŸxâ ¸aÙÂâÂÔR[v®IG8Ew¥ Í•*5Ç÷¯ -Jôìí0Ù)MOkg¿W˜¿‚6SD >¤¶»³Kg¡Œ=Íuu êþ:ÍOWÉ| /qýc]Òœ:òÞ.š6)Ü›°Ž×—-é>òøŽÜ01=ðÄ3ê¿ÿkçoŽæ…y 1«¾³îÌñJí¶Ì=çKk{„+p»ÍÉbÑ'ÇÁ%^ÒÞ’£ÅeÉâÜDžSÝ{Yƒ¹K uüq×UÑ£=eçÛ|6}¾Ïš²©Ïßo“E' zuœ¨Ð˜˜x[aÈ¡‹Ç>=Å÷rv»`_Q°2%HÎæ¦—îyåek~´ŸŒÁU„G„‰-jÑJâ:\¹i]ö¡_ì}ãßAO¯IÎI8öÑ[¯Rêü`_éÈæq…bú…³ÇN)ÙI‘R ‚|Q«à:h£f±XÊËËKJJÂÃà d²iFóŠ2i‡†.‹üæñQ½U%e;-ƒj{DöF‰Óôu·ò&†ª¬0˜º¶Î~¹DÊŸi\I“nlÄÊJYÿ°™c®-==y)ŠÝŽ{¦Åú°èøµþ"ñÂ…¦Ž¾seݨïÇñHù?ÿzt¨/Óužî›°ò'¿ ©«½ÐÑu±¼GoÆhŠÂM9a"gG«šåÂs‡Æ-Á^|ÒfÖ¤¬ÿZ}bx°gÀ[J¥÷Þ£àŒ6Ÿ:u‰"N§ƒã÷ù‘W‚ãz¾¥»²gä?•™¦ôøæ½K+Ú:jÕ-~ÓÙËÓÄtóLzâGÏŸ*éVw×ëG|"ù„c\‡…'¯òà)mV§glþ#“ÒÊ‹|òÇ¿ 9}n¨ïR£V'öaèsâî{ìÛ¢šÁá‘Qƒ”Þùy –ˆUp­V‹:©===9990Æï…àU[ƒW]óÁ•$¬Ø„þ®>“–’½å¦·štCƒ#ú=â4–‡wàšBתGs7^SO– "k ú»f’€€¨¬[~ðTù QrÁ¦ä‚ëžäÈóï Ì¿qZš2(á „ëž“å#æê„Ùßüiög=μþ픢)E3W€™@¬‚+¦nîöÑG¡îéÆCBBh´/|#XÈ(»¾¹òäÞ£ÕFòºçiâðµ7åÄ*`h¾:ˆUàb±XJKKÏ;§R©6oÞ gü.MtA\Φ°´õ7ž'‹ÓØCÀ- VïvSC=ìÙ³G£ÑdeeeffŽߥ Çq‹ƒþæ»",e 4VI’ìíííèh')rö©—('üýïÜÎX‡ÃÑÚÚzìØ1ô`Ó¦M0Æ/|E 4Vm6Û‘ãŸÙ/Rܽ-ký¸9#~ÕS_šÇã݉ò§Æø­®®öóó[¿~ýú¸«,ÐXÅ0Joc(µÊØYïå´dÙÛ& æñÉáÜljÇï§Ÿ~Úßß¿|ùòeË–Á¿à 6V]#‘Ñèy÷žHáæ|û©¡N:e2™¶nÝCÉ€»,ÜXw‚Íf+...-- ß¼y³\.‡L7‚X½[ NêèèèÉ“'óóó³³³áŒ_p;ˆÕ»I’­­­{÷îåóù;vì …N*Ü «Kê¤šÍæŠŠŠ²²²   ¢¢"©T ™ wÄê§×ë<888˜“““OšïJÀ’±º4¡NªÓéìììÜ·o‡ÃÙ°aCHH õwÄêÒäp8ÊÊʪªª<<<î¹ç¡PÔ¹d³Ù&&&¦~s½Nç$§, µ~Ðdfê1šMt:¬’,n°/Aããã»víBÿMMMÍÈÈ@½Õù®Ñ]§©©éý>ä ¤4mt\c£ÄíÆu Ç ŒÁ;~¦¢ñR§Ãa·™ O<ö(\F Àb±º¤Lñ{òäI­V{ÿý÷‡„„À6z^ðx<­ÁÜgq'd½ O‰'‡/¼a:“”R¨‰´Øm͇qŠ'”(ý¦Ým€fÜåÎF}×¹‡ïÍŽžûzÜbuQêííýïÿþï®®.Ÿ(GÛÚÚŽ9‚žß±cG`` ìø]h4ÚŠ¼Üáá‘Êúb¾DÁJožF«ì©:”˜•‘çi°4@¬.>o¼ñÆÁƒÍfó_ÿú×ÁÁA”¯aaa(_ dêÂÁápîÙ°Nýö{u'v&¯~˜Åá_} õSMzMÃÉÙ÷oÙI°d@¬.2v»ýðáÃ;wî4èŸGEÏ<óÌ3«W¯†Mó$“JÞ~ÿ?ÞÚÙTv8fùzóÊ øœ{gí© ¹qøÓ-K Äê"séÒ¥×_½³³sêŸ6›­¦¦æòå˨÷3¿ÓÂqÜÓÓcÓÚüwv몯IÌ!‚t:šÊ³µ­[ܬT(滎w‚X]L4ÍÛo¿}êÔ)òšáÔjõ+¯¼–““Çç ”¬±11ë5ºwœˆ½‚c{Ïc#Û¶†…Á=Xj V ‡Ã±{÷î;wZ­ÖkŸçñxv»½££#99Y ÌWõÀ h4ÚòåYcíž³‡Œz¥¿nÝò„ÄÄh°ô@¬.E]¸pá­·ÞÀ&7Ó\.×ÏÏEiZZZJJJxx8dêB†fÙª‚¼¡õ©ÒÖ¯ÌÎËËEÏÌw¥î±º8h4šÿûßååå|>ßÛÛ;!!¡  ej@@€D"‰‹j÷<òàÖŒ”¤ˆˆp. ¨Àu÷Ä*E‘IR®û¢øä?)Ê5Ð"ijÌ#ê†ÑÔÑç˜[ÏjµîÙ³§¾¾~ÇŽS}ÓÀÀ@´f2™_½p0gЀZE ñÐ ` »Kb•rZìêŽñæj-/Ø#!CLwØÚ+‡-¼Ä9ŸýUoQëॱöê1à é®M&ŽÓùl¿8E€?—pGlGFFþþ÷¿‰D‹w£l2™l6Û|×,Æ ‹Î]«8Í{²mê S—aNÒf²›l$éü"×¥P”Í`Ñ›0‰’M»&-q6Kê-`žêÁ$QE¾J61¤o<Ù]Üa.z$Ä[BÿŠ9Èf³Qu±ß2 eêÞ½Ÿôõµ²X‹û‹€¹a³‘Ñщ+W®†½2`qYÂ8ÊisÚ¬$9™›4ƒÎ¡3h¸yêEÃ/Iå3xWW•"I›ÙápºvÓ18®ü¢ìN‹É‰1htœ²ÛHŒ X<:f±v–öuYÉù^1ƒN»—è],>“Ã@SÑ…rŽX€‹$t}÷Ð¥³&Ó„ƒ’пzïr±g*b6›ÛÛ|}5¡¡Ó ãÀ êê[Z¨œœ«`qYôëi¹îî2al:{yx̉rÕi#y1ª”è+_–r’ºMÍÑÁqšdå*1×w\¬Ò˜­˜ÓIñýä‰Y2[ÿxõáîQ–Àσ¡éÕëÌØU~JÜÔV9ØåàÓYDxš‡¯ò–k»}¢µó½E\®2uÉàñX ª„ù®XìvÇÅ‹pX|–h¬:]gûšÚ°„{ü=húΑö Œülw/N#Ä*¡T8ÔÝm'ÑÚ«3Ö0(•Y”¦æ¾ââ~I€0Ò‹/’:Ì3RÄ«þ¸»«ÑàW ‹Y Er®—ˆ?ÍÕæA]Ãñîn&6¡ž¸|™š+—‰i‹öH(€/liƪÓdîj2°¼|üýØÎ SÒM‹a¹:Nà4ÚTÜQfíD_¿Ó#EY8›NZ­£Ã6JŤÓq¦-÷ð1¦¯¬·Ûds†t‚šѦ;‰ÁgIüD f‘3 \s¹f°AÀLˆãÓ¿úiKƒ¥«¤Íf° .“1™g“)c¢¬nR #­v³Ù®ÔuÒ͘“ôQª”ŒëbpòR™Ûs—.d«Â$^œ¢(_æÉ÷ÛëϪƒB¸Ò麶–ž¥«8ƒÎ¢ã6³ÍNRtÚŒ=EÃé4‹é鑜%¼:-i±Ìô®Y>Þ…-bsØ„Ecs0>Ü-–f¬Ò8l¿@vM×X[›0ÔŸMÃ(‡gÜâì–€ë)r ´ŒŽD°e|‚t4cúÞ%ê¶Ò1‹Éaµ8Y8A»Å¾]×Ðçh»F­v*bD\&œvw‹¥«“œ­3 ´é¼,gq…l™¿HD˜ f‡yÜ0ØÃ8-êQ›ÓB tƒƒxqž•åšs{­"1Sä%Œ“2 &ƒ¢ìVõÙ‰[ÆM”ÅaÑêœb?!Y7^sŠž¤T©XS§#‘ëPëØ s8 õ‡;Û™8ewÆ­ü0ïø\9—Vàn±4cÇqžJ–½]`ÐÙ¤ëÔ_Ž˜Mw°³RzÌb‘̤áqNŒ%dr8tnŠ_aˆ‡Ù‚&Å\†€K„{c¢)‚+b3qFÂö˜X‚àIÙLPe‚͉éçÇ_q&C M{T”zM+brØpTî"K3V'á KÊ»vä36ëó[¼Ð$×½Dã˹ükßÍbˆ=Ÿýƒ¨®>fJ½o¼\'®˜Ã»§Þ¯%«À\ƒXÀm(×]‘Ü=þ‡«Ðϯïš<Íܽp'ˆU¾:Ê2aÓXìNŠÎåz+9î<ù›´w5ö]hRk&•oaž˜ìX¸ VøJ(Š4ö¾þò¹ =FZëðŽþãïòÃ=™nëRâ†uö¼üZ"%?!Ù b€… b€¯ÄnÐ~ò÷#ïTàO=»*Âк³„r8Ü:Nó‰ð[¡}ïås½z;Œ-À± – ×8C=£,…TÂýª÷¸½ýÕõ÷|¸¿ƒ™”•éÎVÊ,ŒÙßwû\cgNŽ=2?‡T)ûÄDs¯=9û¿ÇÈÿüãÊOö:‹Ä*X(Ò9rY£3:1Œæá/aÛ­c㦠‹“`0^b!ûóŽéthÔÞF¢Å—Å’)øBmþÇú‡î­:R1 ÉQ·¶…H%ŠÊl0¡ÒLVôŒ ÑX\–‡’Ïœšt:õã†qÕ^$¾+—q§^ru] ÃØ<ž—‚©7j'lNHJÛuÃ?Ò9>¢mo×:ì”Õ`ìhqò‰§X!¼u¦£·¨õã¨ò”«&[®àa:mߨëë0¹|†¢zL74nE“ã\A°÷ê P8 7iõÝÒæ¤:](áËÄÌ«·$ö±ƒvµ£˜F§ó„¹”CPÎáþqƒõÊÎc&ëíÅgP¤zp\kžüAX\?# ã†Q­Õî¤Pï˜ÅfÉ<,›¹§¡õÃ/T©Ð·cê9 ¹„…c®ñǬ£ê‰ ³“Âp6£Pð8“ƒt:MºŽ~×xÚt&[¥âš4F4§ÐoËñ<ä\:æÔkŒZÕŸÁåzyñÙ3Ù À"± Ò¤­=UõÚ?Êk‡˜E›£$¤±¾¶¿¥CG“ÊVnÉyöÛI~B:޶æFCñ®’wvîs„dÅ<ñز`¦±ô`տߨíÀ‹÷•õWò“×¥<¸!LL³wŸo|{gÝÅ^ …³Ó>®6Ø>?úuQN„²ËöW¼¿¯]í`¸„Ig´0„¹÷¦>|o¨Œéìjêzõï%ûÎ ¥†g&Š:ª{.6 YhQÙ ?øéʼp¾M«=½¿êÀ¶^U_ÕòÚ_&xtîÊÒ6ús§9©ˆrZÍç÷•½¶§]ëd X˜nLyùÿ§—{ÞûÜ®1÷®|ñ¿²½h–öúöë?ÞÝ&ÌÉý¹¡ŠÉâ(J×y鯿ÔªuÃê ““žñµG3צy° Ìn2œzÿô¿ö;Xæjyˆ’ãôlNu¬â`ùßþ^Õ¡ÃbWÄn¾/iíÊ`ÉÄxÍ©º½WVÄ÷=¼ük‚MMMÿ|»¦KGˆùt“F?A=þ‹{RÉ¡]Ô|zº¿³¾ýÒÛ[±ññìüp¡eläã7Köœf Óª12Ò6¤>ý`´‚Mõ·÷½ÿjé'GzÁA9ʾڮªú­…Lß²1œ¯©¨è¬iÖQžá}»ðÑõ~Ø· –ˆU° ÐøÒ¼µËËjO\T÷ë‘ÉAÑÉ¡c—:w¾wþ­WNÇe=‘+ÇH{Õ¡òßüþ '3ëÙo§p¬Å¿ðÊ¡1Šùûï$&çE%í®*cÝŸ³}…§PÌ1¨Þó ¿~ïƒçßÊݪdQ¶®Ê†—ÿÕuYc'¶êƒeÏÿªKúþ÷2b<™Úîž×^:ù—_í³c›ž¹?8$)æ¡õýÇŽ_jïѦdùçÝë™W +ý´òƒOJø¡þ±?ˆW™k–‰(}ñ¹a¯ÔÈoý,ןK¤|ö-.¯ilùë_ŠGS~þãô1ÞUÝøÎ§Zmyfì–ÞÁƒŸ4ŽŒ¹:Ô“›˜—(’Ÿ¾S?4f»ö%:‹³<..YŒ5Åuo½sª©×Êyq}a4¯û\Ý /Uò²–ÿì;‰ {ÃéÚÝ Fãzz¬Ù–Ùs¾õWrý×{r œ«È[×r¦Ñ–˜ôèöh¡ºëw/;Üïù‹çWdóÆ»»Þ{§~Ô`—ŨVäÛuÑ® ~ê{+£<¹b¥ÀiÐîýç‘þÕ“±-ÿ©má£úí¿~åÅOéáw6úûÅD>¾e¬âdSMÇH|¦_ʪäô—Fà>Oå'õçÍpÁ*9ÜÐ[ÛmÏZ㟧äÓp._î%‘»Îoºa|Úapœëá濾p%õ\3=ñÑ‘?ì¯Ýu"&-$|ðBg“šþpbpr„œc*G¡QMÖ‡%çoŒ}ëÐÀ…Sm­ÇzqÑhº{Ëú‰ümr&6Ô>t¡vT·,s™Ê_H÷órY"»-b{{¡)Ncq¼}¥~®c«Ô`cÇ»Ö;B’~<5%€…“Ò­÷ö?rxïG-[ T)Ëc±˜„Dé¹å‘åy~Œt´Åð›*/7cÂõ-Ï0l$]ØÖ4p¦s¸sжLA‡î*XJ VÁBƒÓ´©ý‚›ë)ç£-·Íd')Lßv¹ñ¢š/Žñ©QõÚÄ›t…€fìÔ[IÅÕ5•ݘD””âÅÿìM ]{_ CÄÓo¸pqDæ—.¼†8îç$8ßСe"ÓW2õ4AàS šÜG.cã]«ÃyËK](’´Ûœäµ_‰ ˜Lœ-dó sù‰šI‚ÂO¹˜“”ŠŠF‰{Û¿ÍÕ¸ÀC¾<ßïö WÕíe‰8\‡¦øÓóû™™1r‰@”–."®T›œ±<¾ìÚŽ3µc™\:ik¨è1Jü²bÄ޳ø,—*¯mx÷¯ûVø{ÉÙ¡Ë‚1.7·á¦¾æ~«w"MYÔ#6Ty‚Δsœ­í#fGÈg›§1“?:N“yËP¹ÉA1˜´©#Á|¹XÄe;5«Åu;bˆU°”@¬‚… mm§ò•rmßI­Ú¤µ vu¼üËß¿r!Jmb‰hucP”Ac°r™h´˜m­gë~ûýÁ©¶ i³ ó¥ü[5pW»âƧàˆ*Xª VÁ¢A’¨HøF=þõ‰ŠÏ¯ %h Ï Ýª¹az4µ«ÏHÜ´Mÿ¬4”ÅÄõÛw”™®«c(ŠtÞvòFÔXGï»/9;d¿úTø=…éi¾a*ßoýôÞ¨¤¦â³íçªÛ_z¾yï¡øŸþbÍÆ´iòû¶àWêŒ("ÃúkNJVÓ™ÒÎsµ¿?Û°¯0ó—?/È ðÃéìôü°¤÷.V–·•6$¦šz[GyÛÒ½ù“­‚#ÞôõÕA~ÇOµUœïý÷Ÿ;öí»øÌO6|}½ÏÍŸétuÓéQ™1ßù~ªçç÷ÆéL–¯'ëæé¸Û@¬‚Åçñ™<î 2?è`Î ½‹õÆé9\–HDXuæã¦=„@Èâqñq›mâš³‚œ‹ÑjÅ9l¾èKHxÇGüøo=eûü)ާG€”Žb^ìïsÿSª5÷§u4ví~«ø¥÷ªv&fŧ™Ï!Æq­ £ä*‡A$îôp„ÿ½ÛµM5m¾~òµÝ¥- H Ž»ºÃ¸2*de¦ªäÝ®3G­X'ä̻Ҥ (ŽLV¸-'g]ROkß§ï–¼ôÆÅw?ôË]îuãžuœà‹˜t:æ`³üC½C…77X`(p·ƒX‹. xù Îvk›4…AœéOt¡¨+7{Á1—80Bn:¬«­Ú’ âÑ®ë–*Âþ‚Kc†žËÖ É^EéûÔƒ—u2?ïÐpþ—ÜK‰ãl‘(½@tÓ äP]W#!ÍŽ’dâøåñ\R÷鉾!µå:êO8å°Ù\ÇoÃbi­ì7á¬e) !¨ië“ù¤óÅž²ŒBÃ4òé™âþ1ƒµ(&÷23ø¢ÜÕá¾{»Žî©ì‘°V~3؃=µ?›ÒwÖ]v&¦úñ…‚ˆe‘ž{UuOåÈØˆñ³ÖŸý¨(V•ar‰èné4 x°/€@¬‚…drã}u~íó†sýüróÂOü÷…=ÿ.Vd& ˜æt8I:K)eá8M$fà–‰®Î‘Áa—ÁIE1‡ÊNíyãL¨’V”¢NŽ>à´;b¾Ð7°¨0²üõöâO[2|cU“ër]ÑÿÈÉ $]fùnƒµ—þÝÈ–<áÍAªÓYì+ÜK"bv¾D‚_lí=U~™-À¶Ž.­ÙAR$ú~הᰠ#¢œŽ¶Ê¦ÝÇ.+ãb7äú ™Ô…’ÆÝ6½Çc1*1“´Zµ:«“ÁPˆ¸ŒÏr`'G,®øç™᪌ÌeòÏx¤ô=»?ì3søáè'¥´“É‚û*¼4úËÆ,ºqTG0ÙʈàU©Þoœ¾ôÎ;U’c‚”,œ¢vãò”Âφ¦ °»­®ÝÓÌV–ˆU°  žZgC÷ù.‹“´µWwTG‹SƒuíuíZ‡ÓÙ{©§º]•"\ó@f÷ˆõãÕÏ>Õâ­‰L6“›¸ã¾‘á_\òîßö7WÆg%<ùPBêúŒïŒ;ÞÚYÿ—Ÿ¾ó¦\¨”rø<¦P&-Ü–¶6Ík݃Y}:çá#%¿èñåŽt ]hTgÞŸýäCñžÂ4:TQ;d´’Æ!uYi·"Ç—PŸ>Ó=f%mÚ‘òóÊ<s{ç‰òA“Õq¹½ÿоºË‘ž 1ž·:‰É£÷ŸûÕåËñr¦U×XÓ'ËHx`}¸œIØc‚W¯n|³ñ¹¯½â#òôÊŽ1£Ý>ÐìT›x](“ËŽ÷îóïÇ$<šiLÓÒ2<îûýo毊Ðp›×¾vú'­qaR7~±ª/r}ú–5!‚ÏbÇq¡§GþÊ ½š“ ¢#•ŸŸÆEç2':Úÿü_¦ÄhO1ÃÚr¡wTâÿð}±!B:®’%gûz«éß7GGz¯Þœ±­Hõä7s dɧo½x䜷·€Ï¡óÄâÜi›óT,ÓhÙ¹>µÁi"tgÚä¡2ó¸ë<)#éÀµeÅmrf°?×Tr¦sPgsLLÔVt¤E†Éó5ä1n·pc•N0´ÃöþÃ|UŸÜ½EÝöN9·Ó ZCBÜ:hûF9í£Í;<êÇ˩יÍVöèe³W`ÀOžñ£{³5£&[Oéë÷ƒï¬É[1Pßg4;PPqüe¢„•˜N`´e÷æþ—Ô7kY ITn ·œÁ¡+¶mÍYPÝ¥281‚&BUÒÄ@Ô%xÁAßù¹4ïô¥úFµ‘ÄãC—oÉMIöQa|Ì`Ôí·sRXûeC¨n4ÛÙâíß[MêmØev\fTOà^ßú‰®3“mra˜ƒºE¬¹É¿àÊÚ{ '%P¬Ú‘” @Ç”z>øíµÞ1áÃV®T­ V«–…vœ,Ê®·`áQ¡?ùëö{š‡û/tvR*ˆÊJHL Œ œØ‘½6ó9¥o÷à„ÉFâ<ïM߈OI ñ¾î4ÎäÄFy§ÅMåù>¯$.‹‹øæ/XÛÇugenÈ|21 1RÆB}{¹çƒß½×#®sÄBÈ}©ñrƒ^ö« ¿uçz:vŒà‰¡‘ªÄx/T¦nȤgËî}j-…áƒaLgf'ôÖÚÿWX„›yLc–Øj#kn.†ó¸­ÞFʰ3, 4V fjòr»•r8í³O}§ê€~ÜnŸ· „…ÑÓ–e²YwÅøæ‹—,õHæ©1õÉÄ–û¢'ej{ú° Ub+C±Ì`ÁÞ¯žmËË Éûþ*ÔÿÂ>mÀδb5mXvvO<["+2âEý㘿 âa »4ÄÜ1ªérTzö£å®Oz» Û[‰UK°¢(lYÃϱe\RcY!Ø“™ƒ†º„lÁ&¬ŠŒ ‡Ÿ.p½åÿб²fŒ´`[’ýFÖ3†}R‹­Ãì›yñ…öî9lP‹±è—ĸ·ç)åyëäyÓ¿HÈü}ï{Ô÷Úç‚BT×þSâƒþnU¸È׫È×kæµ]ºzô˜°e!BÚ5×À2‚¤ü¸¤üéÞCÐTQÁD_ÿitÏ`ß{‚}ož\øà“×?' Ž ¹þåƒAA3W€ÅkÆ*FKOÍHŒOšÇ:L]Ÿ8Íq¾9Äb±èô:ܬSU÷`ëã0«ìÆþrÌõdˆûí½Ø°+iÃNµ`;–€Ý—„Õôa/Řt×ÄOf»òµ¢ûÛ ÌæÄ|°­Ë°ÆAìÍR×4[=”ŽïÆþ|Ìu1¿ûϵ˜ÆìzKy§«Ìdìþd¬¶ûïã‹‰ØØŽ4׃s®g$«r•Ù:Œ}T¡Z"2‚±éXUö磮ðcÏbF««L4Ù–$LÌïô Ònµˆd¨wŽi{ûO]4F¥úaŸ+wÊÂÝdÓ'Íw-À\r°‚,@îz¼&ÆÕ_DÛ}”ˆ(=EŸ5]ƼEX’Ÿ+DW„»Nw±9\ñ&å¹Þ‚òµ´ÃÕ×ÌÀè–æ*°OƒEx`¡® £0:ÍuŠ*u+ULÀÂ.` >–è*3?Ü5Á„ua1O¡ë-J>VÜz%˜QÉ|LÀƺư`5Ù/DvEº&`Oî®q°‹ýw®îJwHýè‘OEÑ1I*çé]µ:‰÷CùÞpçTîÈ-°0Èù®¿)(öP²^ %Yð5—P¢¨[uݨw¸.öºg|]W¡x^}Ýþ2×ßµeæ…]7—…­½¾Ì•ëï*”µE×—‰º­èoA¡È®ªº=oÕÊy E€Ïö¯ÇGÊ` ^î ˆU–2B(ßøÐJßVÇI ‰С« À± ÀRF09Iù‰ÓŸŽ¸ V·XÜb̳ÙÖÐ0nµšç»" Ž»néê¼õ \ïN­­ÃN'o¾kÀ± æ‡Ã ïí­U«-ó]—…Åu›ta·;gŸônb³IcbbX¬tµ·bÌ.—»iÓýVëúù®ÈB„ãøü<²0q¹|b,2«`îp8\ô7ßµ€;bpˆUæEQN§Fë` €Õ€ù§Óéššš¸\ØIÀâ± À<#IòÈÑ£ïïÚû­ÿ÷ÄŠü<‚¸Õ­å‹Ä*óldd¤òbM]zþbʲd‘H4ß5|y«Ì'‡ÃQZ^¡'$©ëîi9õ~YyÅê¢Bî† À¢± À|R«Õ%çë•E"¹·8 ¾¤²6-5E*•Îw½_Ä*ó†$ɲŠJ ) ˆ "0&£þHSåùó…«VÁV)ˆUæÍðÈHEm“wT‹+@ÿdqùÒÀÄâ²’”eËd2Ù¬o,@«Ì§Óyölɘ›7u0ý×3(¶¶¹¼ª¦¶pea`1‚X`~ ®jIUƒwÌ::“uõI®@¬ K=[Qº,Y"‘Ìcõ_Ä*󀢨ªêZ% Œ¼¶WŠ„_ä² 5犊 á+‹Ä*ó`dd¤´ªAËdßxKQW  M=Uz:%e™\.Ÿ—ê¾4ˆUæšÓéšÅ³h³Ù,Ë­&@oGËÏÔ/‰¡©ÂàYX±Š2õÙgŸEÛñ›_BG´½úϼ¼¼_|ÅÌÌž>}ú7¿ù Jë™'C›ÚÕ«W£)QÒÌ0ʪãÇÿêW¿B©0m ¯¦ ÚŽoݺõ¹çž›9ø‘'Nüå/AYxí·»Z *gê›ÍÞ¶mÛÓO?-f( ¥Ëþýûßxãik8U±©dEí’;v<ôÐC(if®ááÇßzë­[åúéP™¨@¨¨´uëÖÍ\ ÙlþøãQ™·*> (‰¶lÙ’ŸŸ?s¢Lݽ{÷™3gnÕ’@og±Xè[£ŠmÞ¼9--m†Ò”ú{öìioo¿yŽ\5•ôè7,,,Œ¹5¦ÓéÐ÷E­± ¼ÔÚÖvY×ÕÛOÐèSm26OQÄdqg­!šË===3LƒJC_\.—¡ÙÌM´Æ•”” gh€Ný€^^^¨ØJC_sbb¢¼¼|hhhæ¢VaT Z¼g˜1 ccc3Ï4£Q!!!gæ¢ß°»»Ûd2Í\ úʨ±¨R©fm¡åUÏápÌ<ÙT­Å3/3SÎÐX¼ªäÌßw ªZûn§@TÉÛ)Ðn·ßÜšsÒíˆ\;GЯt;5YhV¬fff¾úꫳ.šZ.Ñâ>ëdÙÙÙÿ÷ÿ7íÆÍ<´¡=@ë··7êºÍ\ZµRSSö³Ÿ¡µqÚ Ð2ÊD‡–K´µ Å™¡.2 ´Õ˜¶†(*Ђ‹þ‹¶;QQQ³.š¨†è‹$&&Þª@T7TIT Z·%ÉíìWG›*”p· KT c*s†òµÓ£m.Ú¡íÚ­¦qNBówÚÖÆÍ¢<@ 2ô½n5ÍÔG_dÙ²e³Æ*š¹§NºUN_…~À   èèèYcõäÉ“¨À¾ ú&Ì6NkÓÕZòÅr¥Oˆ*$væÏE3÷àÁƒ¥¥¥³? T=ÔXœyF£üûðÃËÊÊfžƒh銉‰AÉ:s¬"###o¾ùfUUÕ̳ýnhYE•ôõõ¹@ÔÊùÓŸþÔÔÔ4ód¨†éééÿñÿ1ó”õõõ¿ÿýﻺºfž Õ­ò¿üå/}||fžµKPh^ÏZ`NNê<ÌÜ‚G¿Zf^~ùe´ Ì\ š³¨Àï}ï{3oÑÂŒZð¨Ài· ×B[-ÔÇxæ™gfÝÚ EúÝwß¶t-´íZ»víý÷ß¶93L†6t¨†@[†©ÅýÍǧžz ­h3ÄB³°bU(¢¦«»JCsÅkÒ­& 'M=&&ÍZ Zÿï»ï¾[Mpí±á«;g>CS¦Zâ³.èh‚ÜÜ\”·ÚœM•6õ5o'øÑ:†ÖÛ&˜*ÕÏçÏÚ÷EÓ<úè£<ðÀm8k ÑJûo|ã±Ç›y²)¨}0ë4 …â׿þõm6ÃQ³Ö5tž{î¹üà7Ì”ÓgJÎ6¥m 1˜è;;ì6œÀe^–‰é÷4\…R m—yä‘™C µuІ µÛfm<¡ÌxòÉ'ÑŒž9VÑÇ¡˜¹4lòZMPÂÍ\Cô*Z=Åbñ¬¢>èúõëfÍéàààÛip£¹ŒÖ´Õž¹@ôÓ¡ÔGËᬢ˜Dëݬ¡… Cëò¬¢,A-éY Ä&gßíÚ@k š}h1kãiÖC9SP9h5AÍЙ ¨½‹z/³¶¹Ñ(¡;;;QKbª@TT[ÔkŸµ& ÍŠÕ9v;Qzó[ngoÆí›ÚçéÆosËíãLrc¼Iî*mjwñíäåísoëÍ_•JuóóÝ’ÊÓ?œÎ¼n#;k¬¢ãããÝXC3·œ¾4GPRnÙ²Å]"(}üqw•6–?üáÝU ‚úñ¿ýíoÝUªaÚ$wˆ6\ÙÙÙIIIf³yÖ=@·ÓœEÐ2‰"sæX:1sW›Lý¢¢"ô3¢^--H·ÓHZhîêX€»ÁÔ±vÁ$w•‰’Òßßß]¥¡Ê&¹«Ày± ¸ Ä*à6«ÌŠ¢®ž±<Ãï“ãÚ¯ž¬q›§¿ˆUæI’ÇŽŸÀ×y›—ÚÚœ€i¦Ã1Þ°ÿà!¹\ŠQ†ç,ϼóo Ä*sg||üpq…2<'”PžË|}#hô/Iâ‹åÉE£†‘1nÐŽª›Kâcg¹’°p@¬0G¦®ª Òr¤Aéëq|úýº &Û?6sõnÍ凓““ÂÃÃæ¶¦€/b€¹Ãçóó²RßÞ_jÔeòD²™/ºŸÐŒò6çÏ:¶`á€X`N¥,K:SQ5Øq141»u¬R$Ùqᬟ„ ÷n`X`N øüœ´¤N\´D,ãð„ÓNCQ”A«6^nÉÝœçÆë÷sb€9å_>!ádéù®ú²È´¢i{¢$éìk®Šò“¤$'Í} _Ä*sM*•d&Ç~p¢ÖÎL3мQ7®ï½xÿ¦¼YGR,4«Ì5ÔCÍÊÊèÒ’”º¼f°ý"ê°^·$Iö6VÊ™‰‰ñ0`!‹Äê|¡œVmCÅÙ²êfµÎÄòJüÚÖ5>ÖÕबÚúò§+{Gy~Ë¿þÄ=~¹º„Èå²å©q»ÎTû„%²yWN÷5êFµ=6޳ܷ¤](Êi2è4žË„æ!XÚ Vçå´Ôxû×ÿ8ªˆÍ aªO,ÎY™¯º&Vq–(0*Ũùôµ¿·È©ÛV»b,!4-=-õle]_kmÈd‡•¢¨Á¶º)31>v‰í›pêZÞúÛ?úD…ÿñôZ›6ßÕà‚XE(‡ÍFÑ Úœís£,ãmû>ú¨›ùô3ßÎáõE•v{×nGqœ&QúFD† a-]R©4sYܮӕ>¡ñ¾ÈdÐj{.Þ³6M¸ŽªRv“¦¾¼Zœ”$áà[á©rˆdô9YÉ(ÊÑßP3ÄVÅy3iKª>ØfSvm×»¯ïeÄÝ·*Š5WÁjl8ßÒÉ Éô’òxâÈÕëC錇\¿¶ Kê’¦.K>UV5ÜÝìÖu±Lɶ&&,…£ª¤ÃÚZùé?_Ý—ô¸d}ZŒŒ&JÌ^Ëóå3›nh@m K›Ag´à,RÊ7ëÆõF+Îz«”lN‘­zHc´á]¤ð”ð˜øäždÃØÈ˜ÁBa“+öPˆ1³nhXMñd>J‰Í¨Qhœ ‘ÄÔ[ýá›o÷.üæ}+Ã|=X¬`Ýí±ŠÖÞîª#o½·?ÿ;sù±fÆ`´ÐÚ~â8Áb1§Ÿ½„%M.—åg&ï­¨{øiº/¬]²ºªiT÷Ö?°ïD…9ð„‚Iq†ßøóëã~[þø‹{&êÏþé//vŠÒsüy½ÍÚt‚å9éRRÝ\¡Ã(zøÙß=´Ü»·â£W>>Ï–*mcƒ&Aì7¾ûH¨˜6XwðÕ%„XÉtN ™“ïy(WexÿÿyÑgËýÇcxoÍ/üO¯¼à—¿üšöbý©'Gcħü|õý>å½úþ#¶ª7¿ó›få~=‰öÑ?Þlaüá[ßP᣽ø£7Þý0ðgÊñ³FŠÆ ŒLŠôà4Žê ¶(<&&DåX´åÑMËô¥u˜,x‹;Vq¶","¦jÿ¿þñî… }ÉR“~b|¸¯®fç‘’:ÚŸÿrš/N‘ê¦â~ÿdz}œü5…1^DCñþŸ~|ðþïýìÙ-É|¡·t‚çáž”¥ò÷æÓ1‹¦ûÿûó[{*¼“ òÒ¤ Kgcõ¡w߯GH5—μð‡?Ÿi§rŠ <´½M'?øã®=žüѯÊ ôö‰~ÿŸoì>|¦eCÝhšÐ޶_8÷î‡ZGÿð»ïlÒB™g€ÊCÎcØ=|#Sy,_% §&.7¾ú×Þ9\’¹:7=^ƦF»êh‹ ˜Žã …<5.ôÝ?É^óÔRèªNš<å ÿì!N]{0Ãõ .R…†‹L´Do~‹—OxXˆÀLýöÎ>ª*ýû·Oï“™d’™Iï½i@]Dª" –]×^wÕ]ÿê–×]×]Ww]umX@± *Ui¡BÒ{ïeR¦×[Þ;‰B(‚B0Î÷†;çžsî3wîœßyN ‘ËÚÌfã@Óá²AÉ guáq²×LR¶¦Æ¡a­1¯d8ꮈÀUJFô?÷œ(«TžqÕÓð/רà/Àäç*—UVé£ãƒ}Œ>õ†ß?´Â‡‡8ëÿóÌcÿÚRpødû’)ZÌÕ¿÷«õŸïkXöÜ;Oß#ơΟÊ5±þ‹ù3RRS§%èx!:㦛¦qPaœÇv|ôæ»›9üþ¹ã|…0D»§aî7ˆa\ý»7}¸qwíâßýû©ûæÉ8í¶$ú¡?ûƻD=™à«MNˆôsÌŠ¤Õÿ)K+<悯ÿýàï^?pødßš92•8*-[h©\·~³G–»è¦x êm륭yß|ðö†Ý7ýþ¹?ß©äÂÕ_-ß÷ч†‰¾Ï×CCCÕÕUk¢ 9 †@I1¡»åàÁ¼‰¶åR``½>H¯×ÿì^aAQ˜a~xÃJ ÃP.‹ÑaCƒm­-(-[¸âWaSu°»m˜„ ž¹G(›Å©÷\W·¬Ž.£€x»1X& ù8Ìã„%ÇÅȶÔõ= ãé©;vì¨Cè—:-VÊÅØ¨>Úè$àxuic—!Y-öþ’Y}FG@Ïp×þ½ßÕ9ÌŸ«ãÞÌQT´`å-”Líê(=°ÿ€Sœ>3M92ŒB¥É3f¦ÆlXl×Ñš;ã}u#ý¡0Nð¥2©€Ïe"&-3H²®´oÈáò@ôJð¨í#o¼óTÛïß× Éo[0;J-ð6\10ŽãÀWwjjj>ÿüíÀ@!ŽO¢ç_­‚ÚÚöM´—HW—%$$}Íš;x¼q˜†r%R¾($1{õÚ\Þƒ õ ‚qÙ,¡ã4 AB$å³?š¦Ï›sù¦\<Ñ80ÑVL"0 —Hä×ÀH½Q&Q±2^°jE`#jHSì/ËÒ?ÐÛjðX°C›ß<äbï± V¹Æ1ìtÓgýòÆ><ØQÛÎá„«°Z`”:¡îƒÝ]­ƒ"yš¿’÷ýI&¤¾*•?y¤§­±Ÿœ©;Ç69‡ƒŽ¬ óc?t†± ´Õ÷ð9‘‘Þî[À„¢<Äí·‡ …`{ðñaÿþŽŽçY§IKg{—AÍnŠ}üiÒCÒ ERì!åaÿ£HÏÈ1IRÞWšahšâ˳cx»ŽlÏËÔ§è% Ɇ(EʰÙiªCu'ûµ¨¡àhTRZ¬¯¡F:x¤©¤¼š”ZÛúLž‘ܽµdÝÑÞÙÛ?„)%<6©‚++Ë·lùP(¤A5äý=R8®Z±ân­V?ѶŒ× ¬ž ív¹ Ã8\‚ÀG&±àRßÙ¿þý|B:%Ò‡Ïj d<§ÅêAžèÌIë(†³'§ÍNãJ.oŒ‰à\.ÎGH·Ój%/±†L»Ý6“•ÂP”G€Y5W Cø|\ ø‘IM€Ÿ ‡snI‚øD¤¦ÎÛ¸Žé̉RšêûL²îp~Q8Ñ7há0=Í5µ52{c‡5÷·””•Ë­•=6§¥¿¹y(cåC÷¬Ûºá?Û.W„Ĥ-\¼ R­»ùÁ{mŸíÝøÆ?aÊmsßuÇêDªwúMq›>ëÊø!" mť꤀”i©åG·­ãÐ7/_”$Túe0ô#Hnn[pL´-ÏÐmÏž¶¡¡! «W 0†aŽ\Ÿœåk…ËÏ8EàŒ³ÿœøƒPf§ÅE›A¦›:­Ÿ åö.š^.ïRMA0…hÛIRÐè2ÁÀU ¬ˆ˜õ¿c­$ÌÊE¨#6e6Å ‰T„ºµSH˜‹Å­{æÝ4#„AëÿúîLæÈ”2¦}>l¦Ùæb`„ÃÉd"•‘³|"Ùds{§\¡\*ÄX7uõËÿ›cwÓ¾C¯²¸¡L!âKø{öjBð ñd›¥Æj¼(2Ò—Çû‘™u×Ö‚óD[1ž\ó²ŠäR…ŸÈÚ:ÔÕoÇcýÎú‘£ÿ®wyŸw®Hêäãè¬oìõ$k9cuFe~*_­¸Ä<Ü;ìb´£ÒÇx̆á^T"õô¹ÔÆ&˜'ª}ùަ¦.#¥íÀ€«!d¾²ïßñx¢Sgx¾|ÉÇÜ1øÈü²S‘Är•xl5xd`”@¢HÎ Dp…ZóÃÀ%é©“<‘<@tfzàáš—UXè—¿ñDÑ{&û‡H9£ýÌèèCÁP¢\V‡ñ92Mjz–ü»¶n=–8=ÌIÀxDqS§¥ný¸©¤¸~IŒ’‹z'Ŷœ,¨¨ì L¸cZ´Ï%Ö‹a„¯ÔD§ÄÑE…÷\’(á@ÞÞX5iÇ]à®nYe…Îí°šì¬ÏI;mf³Õ!’rI›ÉbsÐì)§Õìpó…ºKW-j<¼ù¿¿75f&…Jx˜ÛéàøÅÎÏÍPñ¸þá¼pÿ×¼N6¨ýÂrfN›¾dͯª›ÖíþäÙß¶å¤'ú+% y\”.uÆŒ¤Ð+ׯxéÀÆ·ÖqM)! sgÕæ ›{¤©wÞ½&^#‚(§Ùb!)†¢\f“Ù­ä ¤Ód4¹Yƒ(‡ÑdqS"Äeìv{(§ÍØÛÓ§ƒÅ"¡ãûÎ[rëтƛ^ÊÚžÈCÜý­•-Ãv’;ÔÕmˆññÀ¸`˜Ü\ݲJ;z}÷é¶¢>µÎ¯£hÏ[e7ÍŽoÚóåŽÂf¹^Ýurëú/d·/Ë Í^ñ—¿ 7³=ïø¡Ï«Ž ¥>¡qi9Qr™G,é†[o¯ÞUT³k/=m¶OŽÉ‚Ó{îa‰Û¾Ýsp×7õ8ÁSøêã§NWªx8õ¦þ.úòëo|µn«Ÿ0"ÕÍûó#KMåAžÖòï8>ÌÓrÌ _®û€X³Âg¸ò«/¾1 }•pûæ¯w`T.\ûíÆoòIi 6Põî Ïžš{óÍKâÕ‚ŒåÏü•ºé«ý'÷|Z!ò‰I‰ÐeNŸr¸©ýãO·âÄšùI>@W¯ahš¢F;ôaCÁ:ÀUÉÕ-«Ï7söm &ÓÝ Í˜%O‹xÚ©sS{ÜÌL´x1æ åcðÑ‚Èò®'æ,¿ï/Ñl‰e¶à»vókó±0à/›ñÌ]Øcû!Mwy®@ŒÃ¼ª{•‡·øñgÜq Þ&ÚÎÞ<þ¡­ˆäFH¡Ð&Í}`µãǨ¨hhfÁãó)ÿl#dµÎÊzäùÿ<È@P}=¼ÿ¨ì:öÏgÞË–—ûöÁÍ"ç¼è¶˜Ù«˜hûvÖãFæMû𽧤*¶²3$wécœN#(êöð÷ï'™YŽ'fÁ‘Ñ"±hê5 CzZ+öíîtÂRõò•q¡¾ÜÉ6Ö\”«[Va†áÊçÚíP|<48•AB!ÚÓÃÕé ‹jlô¹+/‡zz ¯¥çôxc64@¬@‰„PÉI¨¯—ÕB¾ÁÀ‹ƒÚÛ¡ƒyÞ%%ì1.é3@täãuvBbävC'N@!!p}=··—›µ¶B@ÉÉPm-4< )•pu£Ü× ‘$ÔßE„B¬ÞÉ—ÇÄ@EE^3âc¡†zÈdTjµP[«ÐÞÑXyœPG›Öò›šøä(5ªª‚† °HaÿÈpå„Nì \AT,«e½_¿yø¨;0:#<ÄL«½Z±X,<îîpi\åß:+œôÐCûøVT@‡± M›-Zä=ûõ×ÐûïCÑÑÐÍ7³‚±Þá®]ÞV±î»ÏáàAhï^V‰¡éÓ!VVY-ܺÕ•I6O‚`=N(/*(€¦N…î¸Ã›dÛ6èã¡à`háBH£ÌfèÛo¿Ï“MÂ’Ÿ9âµ'+ JIñŽ15ƒÍóÁ!.׫ÄìEYyž2ºûno’テ6oöæ6{69ÐŽÞ$lò‡öÖ Š‹½yÒ4”=Qwp¥D© ˜‘Ãìzmï±Á‰¶p nذÇñ´´´ÈÈH‘HÚó¯+®rY ƒBC¡Ñ%¯X]dò.5øýÙ%K Å‹O‡°Êºj•÷àÔY3fxÅïTV Ù$7ÝäU²Ñ8áá¬ozFV°YA=•D,†V¬ðþúÙ¤§{õòT6ü¬1#Î8¯âQóö÷w½¶¡Ñ'1,-RØq²ú“õ'r×ÌüíoR4BÄmþòƒƒ~QÍѤ&HqWceókaé \r°Çîaµ›Cˆ$|!¿ríÀ/%%eåÊ[9Îéûà]7ÿŒ½e(Š«”ìñ3ŠNGû–á9œ7Âr Gæ0 5À»ÆyR#œeáØöØãñœŠÏ°žúØK°YW:6šÉdª®®fÏj4êôt)Iž»<*àZÈ*0¾0–¶öm›J:DI<7^ãœæ}›ŠB bè¾Êê7Þ*h”ǾþÔ¼Œpeì—=¿éåÏŽ‡%‡E¬ 䜣« í©É;ñæ[Eîi¹ÿ÷\F´×ÖÙü×Ç>ÿ䃂ˆ„ »gJŠwý|xêôçÿ4#9€‡B´¥³ãßÿ<ÆàÜèŒÈì]'¿8jM½óî>†\¹}ðXÏçùøøŒËÆpWft±´3CÆ=B{{;ë˜677†°7‡­m%%%¥¥¥±ÞjGGKoïn=;×&@V€qÆev ÷ÙH©Ûb' åHd³nJ³p…ån(¨,h°§<&åq¼ ®§Eû ?m)+ì0®Ð«ñ³t•¡ì¶¢#5eȪÙáþº€´pŸ û[+J»Í Ô¾ïÊ+LÄ£‹â¦…ˆF×£ÆõšËŸ ¼­•lŠ£ÎtU¯"`ø—XdƒËå²þ+«¦b±ØÏÏ/999+++111,,L.—³X,Ã?_S½ þÎA£ÃI¢­øG» .– íñ Û-6J¢–Èø@ÆpO€qFè+ ‹’}¹¥ðO$—,ˆLŸ¢‹ “«¹(iµu”tÛ\Œ¹¾åëÏ)æ-(›+ $CšLV«RŸ³K˜Ûæìªêw“tiÍt/+ M–µZhÒ5l¶ ÷™›«8|ed¸=µ70ÆM›åíB<{ÓCÀ/{ïXMOOOIIÑjµÞ½zÎì²½$èþ–®/>ÞÿÅqâùÿÜ47^zi{؆Ž~[ôÚkµ³~wã#·† Ñqªg0Œ©»¯ dP754\I ×ñ€g «ÀøsÕš[î›g ï=^ò׃¥šPÿY V­JMPQÆ!EÒ¶!sS=«‘#[>¨ýoXœ$=ßΛIš$å¡Ì}C u.t¤¨âD„ý&<*>Kƒx¬&‰!)ïtÑÈ–Ýg$/ú<~T*Õ³Ï>;Ú$Îú¬ãç#špÿÄHÉ»[ îËî$Tû$'û£æb£ƒ'ü0´»ò`É__®XðÂmÍÕp1Èm®í¢B‚åüKõ¬¯R€¬ã ŒâQÓ“_Œ º£²ãP^ÍÎo«Ö½º«Çÿ¿Çb FP"anêoïŠÑQE‰³[€G²‚a‡ ?û–éå¨9crQ 1Õ7°©hé Á ÒIAAAAãŸïH 6z¹Î¥7_F‰èŒè'ŸQùÆ*fªvñ»G°GŸÊñ¹¾¶•² Œ/ ét›í”X¥HË‘%¤G,˜¡yú©íÇÔÖÝ£¡h—ÁA£—wñ*<Î%”|²ÄÒÖçBpœwÆ`a†/æ«x¶rkS»Š_¢òrIët:m6›X,žäš&45ÔÕw¢ ­©Ûæ"öq‰ÏŒÍŠö®gÉÐT_CÇ–ªŠ–N+Wã?o^d¨Š`ã¶w=ÚÖfpB7$.(kŠŸ˜€l†ÁÂã-µmf‡‡AqBŸ=ÚØmh™¡æömÛjmBù”œÈx½øA¶½3»Ì¦âü¦Ê&£ƒB¾YÓƒub¦îd}Þ‘ÎA35*+‚[y¸º¬NÌ ‹Õ¢%M&*Ôã†Û «Þz¿pk£P$&3s£s¦øñ/u3ê« «À8c¬oýú!kuz¤å øÉúÐiu'„„ZˆŸ¨¹öHÃÉšˆyÉ2 9]„·©òC’Å_–ì©©›å—È»[0ÏGšH¨:´«vQš2FEœÊކ¼ë^£ˆwN‰‹-•Göþ…>ÿ÷&0v»½¯¯¯¼¼ÝÙÔ¶ëíÂ6Ûòç~Mw¶½ñ¼N¡v~Žf°¨úÝ+ÛºéÖ$fÃvîãÏš•¤„jWïÜ'ÉJ–޽PoSW~­)17ÐWÁ»ó$C:ö¬ßûI{Ö ÑAŒiû‡»Öfüå‰d…ˆêëühÛÐRuèüdnSMÏ O#—ó¥bDdÜÿé±ÎðØÙáB¹Œc\!Ž×†êÅÜëiÔ3U`œ¡ÝÎÂí'jøÊÙ:—j8R^Þ䌟­æ«„ñ«æÕ½º¥üŸ/¡óBÕ\Úí±9™ðiÑ©!ï¶õÊÛ Ë0ÞŠ!p^úœ¤›÷6½¿÷ØŸyîųôj)F9=vˆ“œå'ž³|ê겞O>Ï{Ê40ºÞ_ÎÁ`Úa'ý’ÂÒÃ…*­ˆØNì+ùØßí#§NÕ(ˆ+êÓÒÞàikk;yòdÁf³9!!UÖ+xák †qí.ÙYn»ó/7Ý:[…ÑTˆ†[1,U=®X’4=þÆd©½=Ø]óþɦ>³#¬ã`åž‚ÁÛ^^´(Sé¢òó·mÛÕØ?üÅ®ŽÙ¿¿ýÎ嬰Eëù’j‚õG¿ÿ&hº§ª>¯Ô’»lêÜégŽ-fhcKûÖ¯j°y n¸!FA™ÊK^ú®nÍ-1éAú[ÖfÕVo)Üqb%û/¿9-!€MLÊ#}0ŒÈ´j5y”S3‚B}Î?'ûZÈ*0Î|•‰ñÒoöžh/ªàáÕFEÌŸ~ë¯Sub üú7‰´'vìkY÷Z³HÆW¨eQñ!Ó`Æå¨È¯Ú¶«¾ÎÊËŽ†Øù¹!þQÿq™ì“¢¼cÕo”Õ‰$<•¿2iZx*ê]¹Ó?!ú©?!þ_”æ­»¤^$äÈTÒ°Ý c‘3–/êß]Øôá:KbV|X¢6àJ}hÆår755Õ××>|¸¨¨¨¦¦fpðôŽEY,“Ét¥®õðSZ·µ¬¸—!$ ÑRïbZª‹ Uº` d„ ‡Ï'p¹ÈG„”¸ÙºŒ³®ÎPW;xð«#]û0Úië²á"·¹ädÁÊUðF`UAúEjFHÀÖ‘‹ôV7¾}¼§MñêƒçÌ×aèÁþÁòj¦ªùïK(Cõ6y$ãt“ «£Ãî¹#îñçò?fÂøÃâÄ€ëlHÒŲ Œ/0_ã·ö™›—XÝn’fÅq‘”/ŽI‚Õa!÷>¥Yy¯Óå¡Y¿'¡„/æ{Ûƒ#SÂå·ÿfîhF\!߇ÇÖûñ´Ø§"‚ï1{[s½ƒ˜¸„XÂrÑÑI©úÔØ'"Cî29nzd¼*ñ¤"E`ExÄŸÿí÷¨•M†Ä|¹„s%\UŠ¢»»·l9¸aÃç¬vžë˜ö÷÷oß¾uaÇÿòW'33óâ‹2R‹ƒBQœ÷ýS#î]PÄsÎØÝ‘ó CÙœ”Ô_=oiZN°wûÈß@ì£Bn{­ƒ1þs–a‹¼’ y=U[YEW´ÀZ]U½£ þ®Jb¬³êÝÿ×ãAx)Ó¢Ö.â{›pg@¡ÑŽLåB°Ð”˜èòµÃ“Ûûq&_¿þdg`J…Bé†9B¾Ÿî®H¬?GžDÈþýXŽÜÉM(RJEÊŸdö%3Ú1,÷‚³.©Óéšî;=F‚P”ÂRDºŽï®iÇekÏ´ìåþá_%~æ¯} Q}V½†1£Í–þ!‡Š‰ø/±ÀÕäÈ*Ø2“õÃââân½õÖúúú#³ú:!»³]Í ±¹)µZ6Ȧ¦6ÀW¤TIS³"“ü©²ª!’´•¶Åk‚ÌemÍýÓP^gÉÎH¸ïWƒŸì+ø¿¶–`!+ŠóãS¦Ä?ü ñƒ-%Ï=Õ –+ÅÑÉa9)¢Òâ#Dv6övCÂâtÊÂ}Ÿ õáÞzCR0* °Ð?`õ½Ó o¿ùí{ƒer©0*=27IÖp¸äÓoô7dû q”¯NAïþ<_ïÇ[Ã)+è48 OA}A8/-Œès°ñ­×öåÌK\2;PÆ¿NTÈ*?XqåŒÀJì´iÓ[ZZ À¤ÕŸ{ù>êÕ-ÎXdèr10Ì ´z… q欘°€ÊÅ2!—ª{è¥;\G£á }8·=¾xÚ †~“›A‘B¤ð9È¢_-ˆŸé*MÒ!àktJ¹˜ O‹ÿç¸H¨áÂà '_YköÀ"¥TÀ³ý-ÆI]˜ñ˜°ö^››‚8B~€^!— A«ï×*<åèõÿåÖ;_!Óø Ì‚ô³R`×߇ÇáðfÞ2GŸ`¥P¥ÖG|ev$œœY\w0 c0XKK»ÎÅcO((ŠPÔU°asóE)Î DÄÇLJÕׄ„—Ë%&Ķ«ïØ4?,Nfw4'.Urú]°Ÿ_°ßé·|AD¢ â¬|pN`¤60òŒÀÐ}è©7ĸ^pòd˜D°² ã Lˆ%AA kuÝ!·jíÒØËÌŽ}8I§ÝèÄRaÛ@ïžÞZ«{ö÷3#e(ÄP…¥o¾]›¶2%’7ì6;&×Üð_ «À$ô­ÆCeJ‘dœ–d<Î;‹êñÛnô' %mˆ)—Œl1nW}eCA?~WrHŠ ò çºk² L&XoÕl6Y½ÎahÚ2hêì±:I%pߥJŠC4e2;{mæˆZ­TH@¦žÖ>'*”ú‰Èþ~«‹Aåþ ­‚ãíÑd»ÉÒÙi²y÷Ð`¯í¬µº¼=®–¾–'Ä—H)6¹“‚e…·;šÜi¶vv™­. F1™J¢Qñ1ˆj)©ùtãÉ!=Eùâ.$1':\$Wóˆ"ûÚzK+ú¬.qME§0R¢¸þºV¬“ Ð `õÌØÜ¼îù¸D-B ÝÆ°Üœ{–¨; Ë×m¨vK%ÌÓR;¾0ãî%ú¿¼x¤›Ð-›­èmì.,ïÇ$ýí…Yñj|°©eÝÿŽÕØ8Az —r¶ôØéð3¯ãq·W×ÿío‡IÍÊ…>Í='J{xáñ{qN’†cnoûð­ã ŽF͵;MÄÂ5™ âø¥MyE<‡êèq “p¹}¿ðM¾&þ•§IÝm†¶v³±Ÿª,jpÙu…4Tuõ¬Y&`$0€•»òÃ5[÷Xwãò~KICÍu lzû@>ü¯ÿ›-°}ùÒ'/®/ŠMÒΛPÜià¦ÎK»%ùàûß½üiÉñÖ)‘t×Gy_”ÀýeáÍ j ×u¢hçŽ#Œܨ´(]qm#žœ›yKJþ†Ýÿø¨ôHÓ´±{ÿÆŸ—ÐüyþŠT±¥¥ååç6ÿ÷-"ò•S2uºjmNêƒ÷‡³N±G…%ùay7Í@W”˜[||§Uµú×¹S‚®§mkÆdp9S®Ã‘ö?›SËAL´!€ æpP—m(ï›"%“ªEPsKÝñ|ƒ%Lqh[áI˜lês ¶ööÙH­7>!)µb$5F­à6l”uØ”·§S•’™,ç FЛZC¸P¬ÔÉPOœŸŠ_Óo¥ìfÓoÛÄ‘SÒ¢%8ŠÈtš¬LÍ—ÿi®ë²hˆ_ô^\Yœ†r nÊ»ë"Á•(ä<Ôû›¤)Íb¶Ù$í‡âP$äsÎûƒehw[ÉÇ«º‡É¹7Í›"˜ [Y3ŒËf0¹äj»\'¦¡a››¢!—û(ù¸÷24i1[í.F ¾X. ?ù: Àþæârî¼Õ°ñÛcÏ­ŽJ ]¹*%³÷™.a(‚¢Dxæ´¿ÍeÆŠ1hplR¯xz{E!·ÝÚ>ÀøJBâg<ä##É=.Gk?%Lâðð‘ä(*’‹qk«}ØÁ~ È*àlÚÓßZõí–/?ùüÛA®ö†•«WÜ87F«€ÜÃ{¶ì:ZivÒl †ö÷ÇUå.\”È9S:Xa0·¾öê,~nFcµ™Ü 4öÛd(믾õmÓêçÿ±"Is9ºÊxlÍ•ùÛ6úÉÖãnAèò‡ž}òÖ© fýˆ{wnýjS3–°ôæ7äLQK¸?ÑS² `á©|ïøÝ²Ù+ ÅG«?yçð¿Г«Ô¬ÂÉbƒn¹=Ý; 釘”éü9 (ÆÁ§‡¼´íz—rá!’¢F*ÐìïÙãrQ!ÀQÐætQ€¬ÎFpUpÒ’e–ý[ve¡ –/‰õåÑöþ½^ùçgÅQóÖÞsÏœ_ B9;jŽoüð½ÿ÷|Å}O?½,#ø §•¡ m…†o_•±p¶¯ÝA 'ƒ« y?žÄGÄ(.{s„&å.w·nßSnrTöþÿ£|oL ä(ƒ².w´"æ.[˜!%°Ÿ^ 6ˆ!ëʺ‡iAjrN/C»þVgö ¡Q!ØÁÒκvkªžÀ «w8þ£?*žH’Êù®±·¶Ý& äyܤ‡üO$žš*ùØ`l7xt„Çlm¬îÅB5!jî`+Ôéòx<‚ Δ¡©Îꦂ*[DvdŒ¹>4È*à<À0JpV%aåpÖí­9ðî{ŸÁñ<|ÿ­‘ÒÑf ~ä”y÷#¶š‡~ÿÎû!‰‘E*OOQ³uU–UÝöž¶†ê§`8›‰Ûnìîêµ8=‚K¾~* Ž@¦ÞÖÎ+&ÒÈȾž~Ž\§Q OýúX×ÙÔ×Ù3hõÐΓhuþBÎHC«ÇÚÕÑe´¹!”£ð ð•ñ!šôƲx(6¦(@ âb Mšû»ºÍßða·OhêbY\„¿ þþîá¾îþ!«›b¾Ä/ÀOÂÅHûPcS—êý¥æÁ“͉òd:¿àÌcØJ=‡àʃ2oÉäoü÷Go˜ø`¼'8œuÿš !KÈûDv”Õl+v X㕤±ªO ˆ‹÷G—ĽUûÊËØœ ­œ qÕª”äñ°Åì )§µwÈ©ã£}}ì/…1öš ô€yËâŽþ¯êíªËÐò=¶¦§ChjéuéD¼*ÀŒÃh1ÙHÚeí° 9½½Å&7º9!snI9þzõöÏ é™þƒ5»+àyËõ<¨K,ÅŠó+>Ù ¥%jUÔP—…ö¸ííÆ8…ÌÓolëuÓ[CƒA/ñQ`ŽC;Ž<ý^σÿVûh×G2UÀO€vÔ?~²ºqíÔ@ñ˜gF|“c•¯É«î^¡ðƒ¿_½ÌÞÓZ[SÝ8ØÓ^_rp/•ÍUs¬=Ÿ¾µ®lÕhNcO»sÛ¯–¦k[J¿ûóß?§ÍŸ*«+¯œõðS÷äð¿W<²íÄöÿ¾»ÖËqWS<ÿ¶–eù‘¦ö­ï¿½¿Ñ©ö“[ºZ†aíÚÇÔóß|çkZ('Ü-½tî-¬œ®é)ùîõ·¿ò¨ô ާµ‡ÌYñ›ÜPóÞõÿ~çPÓ¯^ýì CÚNîþ|ýWǹ~Z áéhí$3ï»÷µ½u÷'/ýoëà¼e³L_Eiq£YuÏÓÏݽ0á|=ÉD₵Sßßÿ¿:ý /Q\j¥Çqj„KL¸@°¸Œð>gKCQC=Œhff¬È‰ÐŠù~wÍ“†-5 t¸Ò¸P®õôö“!)rZ`ì·ôâˆI¨˜¹8MQš¾jÖß5š#lü“_€lî]¹ƒ¸­ßì âðÙú¬·Êêî7A1!B;ÇÜkîp¾”¹¶qÀvÊÂ1~Xò¬çï}¯˜;{å}ÌÓ3.cÞ{ï®?Òµæ™Ü53ÐÙUö柞xã­ƒCžMKKÓÉ7ö¹ˆ¨Œy‰‘á6žìô(Óñ=»×ÿ~þÑ©¾ô‘ý…¸ƒªéèÖu_ì›þ»7_œ0\¼á‘§_ûè«È ç¡Uð¿þðhF”à,&`Êrbï®ýôKÉÖÁ h)_©Ó¥ÇéßÛ]ä X{Sã‘ß}»5ôWÿzôö`)Túõ+O¿òîÿ°gV'ggD½õÅ~EHÊ-3Ãoh:úâ“Ï{èØM³â5üóh&!Ö-¾ý®Â²º¯7¾“´fVà¥ÝiE1 s»Ý—–p-£~Ák"‚Ï &D’ôSÒœ(ž÷ø”¸So5kÕ‹NŸäL[0eÚ™ñÇ^Á9ÁÑaT[ -P{7”žêûô…­Ð:è£hZpø‚Øð´(蓨 R §ãè;uIµ»! %kØk¦90èx ÔÙµ‰¡ÔÈð„0è‹"oE+"³R"ÅÐ@?d@—ÝórUdðS Ý’aìܾÁ0» ™çBC#<¶ÞûN`êœôd=E MØÌÜäž/¨jêLLôf"R§MM×pÒì.xÌd7„ÀèaCÝέ;ˆÙÙIÓ³y¡Œå'J{¬¡µÇ>·VQCµÃ[eKjihøvë6ÎÜéñYÙ\œ@`+ŽA¦Á†]Û¶ñçÎˆÍœî „Æ´¯Ò®¦ÊòÂrëÜ_gÈx GgÍ ùhkáþãýË ‘ÞS}pHP ž¸âÕȇÅMýHó, K‚2¼ïÎö§^zïÝ5!—ÔŽ;ºå*Õ« š¦­V«Óét¹\ì+ûõ°¯ìñè[aÊÈÈðóó›hcÇàpCï†B| §æAåÐË»!¥r¡™Ÿ€šú¡7xãÄx#TuCŸBBdsCQ¾Ðü¨¦Ç›„‡C:9´*²º ú>¨¤°|Ÿ¤¬zuä#‚XG5\}h*dð“@…D€3”Ýì`Fž†rÛí&“ø\p ?é6µõ9øJáhÏ(„àB¥?ǾgÀj!é‘t£/(WÀ“ g,Z¾¬áõÝüóèÎ͉Y Vݺ<Íßi6»)‚UHš¢`qøªßüA®OIVºº^ß»þ•cß}1oÕêSCdÓ,»¹¾k׆W}÷MbÆÜ›W¯H•žÊž¡ÉaóЫ–~ßÛ„ äß:Ðe>«–ÀÖìQø"]ž>ó–{~ÓüÌËŸ¼¹N5Wê†|ÊÍ=3›Ø\u ¯[·.??ß9‚ëLع\¾téÒ´´´‰¶ôLØÇ9I e†B(©Å›„ V(M…¨¼g5è›RHƃ²C!ùK!1ªè‚¦BSƒØ ¤@ýÈCAÙatäw+x=Ý(?hn4„¡ŸÄû»î4B)zoàuUÀOFĆùà{{š›mT¢äÔtO†q[:š;ºôXµ†~TX”#æ¡Ý¤‡ü~¼>Mº¬Êãã8_ ñ‰Êyî1·7VÞ»í£õ/öSÜW›ÍÁqœ'ÎX¼jyŒê‡± E‘ø{Ô­µG÷mÿhýK}çէר³žú[Ä-M#ý£‡äÿá¶ÓÕ0Bà<JÚ½KÄŒdCºÜ”ç YW÷leû Ý¥0!¹ä¶55µÿÝüÁ Ÿ8uõÏöX·z5Âãñ,ËöíÛI’<÷lppð<°fÍ™LöËÛv!X—túëâ(´$錳¬FÞ•yú-«£éÁÞ¿S°Z{ÛÔ3’„©¼§`Õzatýdð€ÑÀ)³oÌþnoážåSçÆk¸¨wȃÇ>tòз'Z¹¹·Þ¥‘^@xp¾:59àDcws·5,LDÚMååŒ><ØßG:4m©(ªåh¢ÒfëÕ=U÷õöÛI~Xˆ?î..*ª›"b0MÑìª.©ÃÕÁ‘©3ƒõ¾ý5w°1íæÊŠFÄ'(*eFÞw îàÖ¾>›sLÁ‡AAaZ²º¶Ý6-˜àBCÍÕ]F›nF¢š‡¶_ÒâûƬþÕ=uÕ¿ßP`ˆuÿì9ƒ£² ¼Õ« VV§OŸþñÇ·¶¶Ž G„õP{ì±E‹ “aÚ6à—È*à<0s{k»ÕC¹¦¶ÖÎp‘^¨I|à±-o~¾ñ­ÿΉV#ns]É‘ïö _rÿ¿ž§ä1–Éenë4±Î_O{K{(@­œ¹tyÞ¿6»ñÞ‚T[sÁ–cÖœ%·¦GùØÛNZ]2ÐÙ;ä¬:£ó–vTÝYMiæLO¦{*zìš´ô™H(œ9~Þ‰]ï¾Ìô.H Q¢8/(:¸êÈÎj—jNNÒ_ÝiS§¥DÊTáñoK-й¹SQCM§E–)¤ëû¬s:ÔÕ1h÷õOžµxqþ¦ÃÛ¶ QRǾͻŒ’”{nHÑÖήA’rötu[Ü:WÁθLCÝC.½èô˜%·µ¯¥½¦MmÍí¡²1ƒÔ/~î=Ö5ô¾u w4_u0 cµZ¹\.땎•U Ãn¸á†G}tÆŒðg#àÈ*àlš4:ë›M13n F…ƒõU=þ²P•ØÊò?$”•”ÕµTî­? !_â·âÁ?%ÇG¨$Ü3J Æ3ÔÓf‚ô+ç($ŽÆÚ&¹L­Ë¸ùyöXQUñ¡ý Ê™û›ßϘ>EÆØêúÜi³üQYwKç°JªÛG‹JR2ÓMÕ'å!gú¿›ž“éëžýÛ?½˜|0¿m ¿±Ùî#©Ò²3lÇ+‹çÁ'ã¶Ç³gfI9hJF†1¿‚ „PbÊ­fe¥¹ûª‡xs,QØzš»âƒýnúõ“¾‡×4ž<àaÑsÿïά)qg# ]¾B!G­ƒC·Ù®Ÿ±‚Ï×: N–çŸ1¤³¯£¡¹ŠèkªoóWEé”ìmZNÈ]õ¤]JýÜåYYÞêU«¦§¯¯¯¸¸¸¡¡$ÉÀÀ@¡PÈJ,{V,¯\¹ò¡‡Še¿Ó‰6ð‹dp60‚É4Ñ‹o‹^|öTî‘Ãþ]< \‘¶šý;# IžÅþ7&{ ûwþ|nä´yìß9†p´1™kc2Ïô9eÎYÃÒf³gF˺=6klÇ'höò 3#A<¿ˆÅk"Nß?MLúü³2‡1®6*ëö¨,èl`Ž$à†Û}þu>FËhŠ¢ìv;{<<<<44DÓ4Š¢“®Cý^Ø/¨©©©¬¬¬§§G.—'''ÇÅÅõ÷÷³!¬Êj4š;ï¼ó`€“zd˜xXݶm[[[›Åb)--Åqü«¯¾b}ÖŒŒ ¶€–H$m À ë’¶´´¶··c<}út@ÀÊ'ÇËÍÍu»Ý÷Þ{ïŠ+T*ÐÔë «ÀÄÃz¨»víÚ´ië šÆÃܬD`#® Æãñ°îicc#ëŒ H¥Òôôô¤¤$¡P8¶Ó”×µk×Θ1#''‡ËåM½n² L‚ ÑÑѹ¹¹z½¨)àZÈê•…a¢m¹‚Œ~L»Ý^WWÇú¡¡¡mц¡-†Áâm]v‚yB~pŒ6%Ö‡Á C»,–Š¢Öª&““FDJibj`„V€!ðhBc¡¸°­¥×É&ˆ!qºä(½ÐãÁPdCýŽÃN䦛–(i¯ì¨n6:(,(.pZ¢ sX몺jš†Ì.D쟡UòQïÇУ©¤°µ¦Õâ¡!ާ ÷OMô•Þ@¬©VÃpYI{c§…Í™#à vKÁáwHÜ£÷Éd|b\ÙÑgÃh4²¾)«¦‡C¥R-\¸088˜}TÀ¦§€kðL?Þf.šÁP¶äb܃½_}ݬ™“+Å®Eae?¬Õjíîî.,,A¿y$gÙ4%уõõ¯½r ¿‹ V gS}//-íŧ§Š.øpÀ0ÑÝ%oo¬æÍÍVwTvÕ· ý6IpÐÂ9¡bÒ\UÙÝÔ>Ü×g‡ÔºÇŸ¿ñ‘UaB²vw}ðfÞöb³_JÉ¥»ZzÍ>ÁÏýõ†i*„ÕTCUÍ›o9ØL†G¨}„LKmçáÃí&ôåG¢£}ÇASÙgÃf³uvvVTT466²î)ë˜N:•}#{×0@Vǯ :í5'[: Ùô4?JÖ,yë–»ÂÂ2&Ú¶q‡$I¶ÄduôðáÃGŽijjb=6|Ù²eWôº£s,¬4M³¯ì1{pnà(£oDzezjjê%@“îò=…î4,}|éïÖ†ð!OÃñÊí•nšµ´çm>ö憺¬û—>sOŒ'Kvyü·{^ç Âç† Ü'·çœgºóÿ–Ý·\Ç£ÝûK÷¶"ÌÅ®#¨2"úÖÅÝ[wÖYd²)‹³zO[y›ö¿ðZÅwRÕŸH[u„» ·|ú¯Ç7í¬™?'8I…4+_÷Eó”;oúÓC1„j+¯ß|x¹i3íøèÐûßt/{réwÆøp™Þ’ŠÿëûrÏ ‡ÁäòD•½Û}}}eeeµµµ&“I«ÕΟ?t%‡k»Ù€€¬^†¦ZÛwímép¸H¶4‚•!Úœœ°H o´l öú²Öü}&7Á(†Øzú‹ëìi7ÏJ >y´rýÿŽÕSÊ û[ŠãfÄÌÏÔ ñ‹+”ËQSÜt¨ kÐìa„/&ÏŠÍŒ–qP˜Uocaãñ“}Ãv !8ºHmVfP ‡-=[Õ‰Æ#E=CV„ ©xÊܸ©abâ2Ëȱ7„aÜn7[PVWW³RzìØ±âââ¡¡!V±F#°…&ë¼3?Ì •4òNžuöÜÈccŽ^èÔÏ}e9Õö>öÔèÇ‹ˆˆ¸ä›À ¸±Ù0d¥1#8¸€ â¦'ÈÃírìèlß»§zX¦š9?\¯à›š ÌÛ~´¹©Ç¦×ÃC}&„q0áM˜2;ÙoÐ¥â]ü bÍÇ0”X‰Jž”¢‹”²î«œçÚüq]ÃOLÕÅúyš‘ÑÁëÊšzÌF3 ©0SÇ`¿‘DqŒ 0>—ˆH¼Ã×,”x=E‡yøð¡Îa®oRJ ¿÷á‚t±ÚÈHù–-UM6:YzÉ+/°_Ð 1 cïsrr²J¥Se×@V/å©;QúÁ¦ž”™q!üÎòÚ7Ÿ9š·bîËÊÖ ÙrÍ}ì³Ý/þ·”‰Y8]+qì,ù"opæš¹÷Ü"q[Ù¢ÝØçF®T!ÑÊ}”â"M_ C“¥[óþüj)'!jz¢4=±ÿÆŒ?<‘&c ¾Ü÷××+eS£³â”î¾Þ[ª:$ªø 1AŒÛý`%sÏž=ëׯgÕ´¿¿Ÿ•Øs>“——g³ÙFUÐãñŒ*.Š¢8Žc#Œqá@>ŸÿÀ©ÀÑ×Ñ¿X§“}{<öíhÈØ×±19Nggݥݞ º~Ó‡{…en–.H'ÓêåÄt6ÚŒ\¹ÚØÞSèd#;zÜ(ì1›z­ãûDkŸ~üîÂ5%7=@¯•êt|äçèÍÈ]h¤¦€ðø<bwú¾ö@ð¹|¥I¶BèµT¤ò%NlýxŸ·ß, öÉ:ïVŰ÷û¡_—½ÉÊP´ÛE]Ô¾ÐýA€€F£ÓéÁeä\•Y½ 0,Tª—¯ _±,BF@C)òÆ}5;w×7ü:E)0·µ¯ÿ è¤Ýïõûg,JV`•¦Gk*w´ö;LŒV#IÌ ÷Û—7,NŸ“°j†«ØG¦°¸‡†÷n)-²©Þ»7gV´¡Ü)!ò œÇ¦c²éXÉ[ï•Øã³Ÿzzf¼?×3‚?õùŸÄL »wº{Ki%­ÿèÞœÌ0ëG'…×Ë8ãç©za5’-(ãããYA­¬¬4 ìMÓcã$$$Üÿý¬n±26*™§ö¿<Ëw<×›ûöÇÜÐ wz` ;õþjó›ë_ù¿–õª)Ó#–Þœ6wŠÍê¶YI“ÃpôÛ’&þ÷u(IrÌmÙò`r¸I‹2ﯳ¯ßÒðâ“ëôêô™QKW¦ÌNU]xÈÒl¹`’‘ðÀ=†w>­zû¯_~þ¾2%3ì¦åi7Lp‚/Ž î96ÔÚ9ì Ä|„±ŒÝ&®\(¸œžOö ­¾\F6ÀÕ Õ ‚QÓâµ$!çc0̈¤"ñ4X,.Ö ‡û åõ6n¤_¤NÂÅYå@ý#|ÕJnmëP{—3Ý_„±^Öˆ°^/ë'2¤›´ ØÜ.g¿Õ%ry© RC)TŒC´ÃQy¬²´—YôhX˜†Oà0¡V§„©°¯Ë«Jºq¾lB›pÀF…‹ø|Á´…)‘0G0®ß°L&›;wnNNNOOOSSÓÉ“'9RUUÅz®‹e´ÉW,ãè#O2XW »÷Ù Z òë÷ì©ÛñÖÎür#ùâFT_°æÑùÎØDº¡r€¤I³Õ áIþŽ·«ÿö¨|^›0%Þ×Gˆý¬ÆŸûÑXÉÔëõ:.##cíÚµííí¬¾—––ÖÖÖŽŽß‹N*ØoÙmu""ñ”ÜøÄŒÈ%Ë»¾|ýë6V(M‰áŠåX­Óe¡p±˜wÖ½÷ν1;1‰4}^brvÔÒå^ýêßÛ«T¦g‡ Eët¿K=vEð²¢cÒÂnZ‘¼ý½x­z~ܲ,•‡HZ”:?¯å»nÓÑïŠO 0Îå¯yjÙœ9‘¡2°£"péY½Œµ«sû·ò õ Õ‰‡ÛMþ ¢0"U(byí½ÝÕ-Æp•QÕ=½Wh¦o°Ž{Ie$Œ Ä ïšÛgÇ7}Ûüþ¿?ûH>uVÜÍ·M»1CES”Ùäa(ˆ²;,£…°4.òáø˜ˆ©A2™tɯç\¿ÙÛòößë7ª™sâo¹}Ú¼TÅ87Ÿ²†¹#¨Tª”””Ûn»õ_ËËË}}}¯íé´Ç]±»Ô ›—âÃásµá3gü÷KƒÃC‹ƒTaQŠÝ 'ŽtÌ‹ˆü ”£•-Êá,ÙYd‰Ž›/ã xÑÚì Í»;Mv’f.§3óÇ-méÞ¤ä8úz0~~SôbîO¹a#m#ìËØÖ’ÂNGa~øÇ¾ë-oú´¶‹ÏMK RVÓác=nž"9ÂWLÀöSþ¶¢£Ç-.|oÛ~á}`aŒÀUÊ䌈Ì$]£À¥p-— ån«éï²ór‚}¸\¢qã†Á8/~J`rJ·Û3´ô`XÂüŒµÓ¦Ä(ÿ{wÅyÇüÙÛ½»½WîàŽã¸ãýE°˜Æ—hµ5ct&&&“´“fÚL­™f’63iú’¤íiZIŨ1Ñ11¾¢ñQ(DE@ŽŽ—{Ù> VEH.ßÏ0ÌrsÏî3z»ßý=»ûœò†#ÒÐ+Áçñ´8\¼IŸ”®‹Ÿ9óÁXÝÏ>xÿXEÉ¥Ž))rK”VØw¹ºÖ%°œØ¡þ Ýîfg§Æ¤OÉÐ'¦EÏÌ‹|ñ¹Â-‡ËKëgŒB¬^CóU.÷ó!DËÙ’‚Û K_]{>4XåîèhlU=ölæ¼,‹’c³æeÿÊ%ü»¨ìƒ?nݨ 0¨-¡AéãMzN>.ÄùÉ©W~]bRö´»œ†ÕÏN-H ’ßvDAðºkÏVî¨n' æ› …ïw?”¨n¨Ý^TÒ p¤õÒ†wwÌI0yš¶o9Õàa¹VÇöÍÇ—¤šcÍì'þôJ½Í¢º\;GúÀò‚Ëø ÆsR·Þ_R\^~ôêU Á'ôt¹Mã&¼ôÚ˦™nß+¸%Äêà"Þqäõ´µwº Äíêj¿þD‰ÐÙpá7÷j xé—ÓÓ‚9"£‰Âr+ø|é; 1rN¼}¶]|bA¸å}›t^nÚ´±4dVÖì-Ì‘ÖÄXƒò”‹“1œšJ5o¸T²ë̉ü°)½ÓÓ]­U:ê>úè¿1ó²ïRÒ†!±¡ñ´¾®ö±þ<;6drÅä%¼i këp{ÆÙè§D­S[9FUUÇèxU´iþš‡²çç4í9çõ L²Mg5›4*â#g§¥{N.m(>Âr¾Y+ÈB&ÚxÍÆ6Va3;§`âxbP11!«^¡f¥´¬‹%IVFÅ[#µ²üØè¼uÑ$ÕNXά1Äǽ±Æx%@íÔÒS•Fe¹|E{±ž ²32†U*ã"¢—=;}Љ5Ó§«É¹ûåÍÛ–ì:0!oRyàÕa¸3ÄêàdʘT«]_òÕ'û^ã[Ã4BãyG©³Çëq7^nïìÖ¶9ÚÊK.UTÕ½ú|‹Yß{fOk5ž·FYóæ¦Ḭ́p EX‚‘Ýwië†Ê»)88wz¸±o²ÖÁÞª='>­öÈ'Ø ìå²Ê/µDe¦N²kdœrBîÄå•o~vôåuž“y‘¡J_»‹(Òr’b_ùîã{. Þù±V=S{ªì@I{bö” ¡êÑûûžètË·Ÿµ_pŠËô¼)?™´t’O’Žn¢`ÉôxY²ÕRÑd¹ÜFÜ^úf2oií E'H³Kαa]ÉŠ! ¤¢žt{„ªºö ËÁ&¯Ë=è¨+W$éãKëØÎâh'aôsBJè²ÇKºÜdþDâê$[Ê’š]DΊï™MÎ^ _U„öxB5J27•5äóRòM“x‚W^ï6½uh&c^¤óòôÅ0#I ¯„…ë…ƒg§×¯o;AˆÕÁ1²ÄiO­nÞðieÑûÅ‘q¶©ö¬™qçw8¶m<¤œ:'3jùòñgß:ÝXëhª½ÚHðx>ÿäøÞãMš?Ì»?^›³8k~Õþƒ_Ÿ-lmÉš™‘s‡m*t™¹å{*Þy£F)g¼^Á”™¹úGSSCyšÙúðÈ'^\wtÇîê­¯ÑèÕ»yò´8žgyµ~jŽ}Ãþ³o¿^Ù×О›µàÇS“Ì~û Ë˜9TMœ䙲n+ 5é±$#ŠÔµ’‘wx y2—(81MÿöñúhMIσ–þI_L! ÒHx9~þÒÖÃïüZA#ú¾¤¸9‰dR8¡q^t’ücŸ¸‰§fˆ!úáQò—/Äñ‘ñ6²*‡ôxÈú½dÓq’B–¦«©!#µ-$ÆLžÎMþ¾O³ù¨ât—ïçS˜2HcùÏQòσ^›º8J³Þ´4¨U¢TÄêm0¼Î´2Ìþð|£gNªÜjÔTLYä㧉Q­`Úç*]ù†EÙá¶Ù‰$ÅJ_kåùÝk6œ:^~¼º-7^kÏœòÚOtû«˜¤PÍÜ$½úöÃ}Œ\«ËÏËÈê,é1j|³Ç3rN­Vê÷W²GN“éqô@šûÄÓRU±›FøÂIòFËkÎ\dŽŸ;1ù¾•áÝnŸÐía6ŸÒp¬ÞÌÜëì®p³ô1D¹Þ;fWç†+â‚Ý(þ¶%SÄò1@EøÞk̤‘)‘×ß äȪlZ¶Š f}I O°è¿òö÷3vÛ¢¥ê’tZæÒ¡¸Šþy±ùú&èvWf‰4]0iÅWhBÄ"Õ¢c¸·Û®8¹«fÛ_T+¿´LŠÒ)§EµÚ5_Ÿ8W¸¥6õ‘ì…yá¿üj€‘‡X=rí©;»Bè!²ÊAd2rÞA*í!zZ¬øö––ÔWÕo9·¢ -yœ™­k!:% ÖÙÚ<ÎU}͕ؓçØ$s¡ÙPÝd°„šÆ¶Â½ ëh÷ ¶A†•OÐMôõ˜L:ÒÔAÎÔ‰õÇ rê¢x|¤5=VÕÊŠ†`•œ¸}¤ª‘$räôEr¶Ž.ù’:^`È8í$q»‰’%û+Å5Äæ?›ÿÓõ»Œ¦T_˜]Ó—dý xƒF)þôj°ÝUzóøv›ÐòâO¶ßÀɧ§-{Áí-ªØøÖçÛuJŽñuw¹Ý,Ÿ±,áâ ãlJ„*Àð VW\-^£z>_\þ¬T%£§ÿ3âÉ’hâõ1Ù7m"AÅ r“g…ˆÁ¶ó¬PÛ|ÅìýШØ69eñ0þBv+H¡¿™¶®Ž‹Oí(«|ƒJ"‹ÆÏJ é‘âháGÈï>#‘ä¹ꄆë®2âh#™QâPõþaòúNq¬oÎxq8ÑÙ!n;-6ùEø†]gHýÄê`ÚÚzjjš[ZºÆº#wÄÜÍýäC8.e¥-¢¹µ«ïil†eÕZ>0PÍ{:kª;ïvmõõW\®sD|!V—sýP–Ÿ"ŽÁRêÞK•¬Œ,žÜäªÿÍ¡M…UîÄh«Z–l©Ó Uk xìÉÄ-Šeïõ&:Þº,ëÅ¥Ón¿MþÚûi´$,œD ñ•j¿ªÿ?Áòh:™7Q<äªz›jȧˆ­®=Ñÿ@²Xsí!ryÔÎíw×2šz‡ñ™‘œ¸ªÿüݽ?ÃÑÓãÑébtºIúðÝ…XÜ€gSÔ7Þû£TÄÌÌù­B_´­¼è½=]nq¥8uNÂÚÕ‰Yiµ8áàÖðò»»+—¦é€ûôöpÀ+üMÏŒ*ñ_|k1«V=Ó÷5s žç ÃX÷`Œùå1—io÷<ép:GdF¸þµmúC振¯wS2–U(å*SrìòPš3 ™Yë¤äru××w¥¤øÛµ6Žã,ËX÷üƪVkˆŠÊ¬®¾pñâ(L…@71ÌI…úfäÿöÏJ/¼Ù<),,z¬;ðà‡±ªÑh.\zó7l ÑX¾ýåjïSãû¨†Âc•f•º×Xw¾wü0VÆ b@2ˆUÉ V$ƒX b@2ˆUÉ V$ƒX b@2ˆUÉ V$ƒX b@2ˆUÉ V$ƒX b@2ˆUÉ V$#Y¬ ‚àt:¥Z!ÜŒîh ÃŒu/ü™Ãáðù|Ãn.Y¬ÒL]»v­Z­–j…0ÍTÇ#“ÉX–ë¾ø-š©eeeÃn~O±êõzû÷£´´çÑ#Š&+ý `Dõíh}ú'ÝPÜS¬ºÝnzî@***BBBBCD15;EEE234124>&H>-V(+Q%2_--ÿ%%ÿ55ÿ1St)Ns%S-Y„a:^@aN*OF+R]`cPRTWWW^`cHJLMMM`bfIGl^^ÿFFÿVVÿUUÿVy›Ns–ƒ,v2………z}lorprviiizzz‚‡vvÿ……ÿ}}ÿnnÿ…ž¶¢';£££ŒŒŒˆ‹”™£‹‹‹™™™¤¤¤”””¡§¤¤ÿ””ÿ§¿–¬Ä½+³%ÃÃó³³²¶½¦ª¯µµµ¨¨¨ººº¾Âɬ¬ÿ»»ÿÃÃÿáÊ"ÎÓÚÈÎÕÔÙáÒÒÒÏÏÏÛÛÛÊÊÊÙÙÙáááÊÕßÎØçÕßëÒÒÿÙÙÿááÿÒÛäßåìÛäïáçíÿÿðÿ--ÿ%%ÿVVÿeeÿFFÿ……ÿvvÿnnÿ””ÿ¤¤ÿœœÿÃÃÿÙÙÿááÿÒÒéïøôôôèèèøøøÿèèøøÿððÿÿððèèèÿøøèèÿðððèíòÿÿÿ[kÓ‹IDATxœí‰£ëJYÀƒÏ pG·‹"—놢"êK2PÔëÚ*‚ ؤ¢¸´CÐãVzŠ -‰¢¢¼ø.ŠïùÚ°):˜ß7“¥íizzÏiO¿6ßï½››6iî—þš™ÉòÍ8ƒ·ìŽwé}òûíŽ?8žHÇ~ù­»â·¾r¯ûýØwÅQ¤ÎcÃÙ®øë=ïw3#eCÔ#eCÔ#eCÔ#eCÔ#½l袋ôÊ—5½è^ZrLû}<‘^64v\ •¿juk>:vÆÇ¼ßÇé:CFÊŒ'}§s1›Ž§ðÚ`fgãñ„ ÝV¤ë uÆãñìÜéõœó–ãvû®çögW̹C˜õœº¥HëJ¹ñ¬íºms@¹Y×-ðgêvg½ÉÌóØÐ-EZ[ÊÍf®{/&<Ï›Íðê‘Ćn+ÒzC]ÇéÇÐt<« >•cÈÖŠWìäøò 64Á¸/®Š;tm)çy^{èt»Î9H½¶Ûq½C§Ý!r Ùü¤½vŸ;Ž;=|¤‹xV PstÝ+-m8êÑgE½îþôf³^þ^÷»ýa—Äùë´+CÝË¿ó}xëöû¶#]  ­¶3ACë#^Žô¸¯)¸®3DC-Z›®ãŽûÐÈ9Ÿ=|Ýu¡±­Q§E ÒzP·CÑÜCS!¶ÝiÏv ~tÛíÖÌm{ΰëô¦®Mœ H ¶·ša¨;tÚÎØu»]·ûkK<3Å·LG®”ƒ–B¯åô›bJt0äÁ9ö…14œ Í”°¡¶7Ëû¶¡V4—eÃtíµÓ±çh!¡©ëŒ;îyÇâ*×ë)çPðQ5ÔuÚÝÂh¨²ºkÓy¤ë ¹ÐÊ.[Hçë[„ž{ù’êíïw«7›õá·Ô'³)Ô8“–ך̦-σ&(´ÚmüŸ@¤‹À?„ áõ<är5¹ézCž14ÄVv ¯šö6´²a:쎱çIÄöûa8¦H×j9]0tî´±p¼I Ú­ðctÁ=™^Ûi›‹vǼßÇéZC](بifw µÙš±Ã!N§n¯ gèK÷‡Üïã‰t½!<„6l«åQö¡öûx"­1ÔwÌ14íb‹ï8tZ8šf<-C“n÷¡¿Ûtºá°‹ÿãå³eÖ\DË#­1 „ñÐÔ;cÇ·ÏéÌ:Žç¶fÔ õ8Þ;›…6R¼åµ¶¹^ºæÛ[s!:t¡1ê„LÍu{œLÆØ$œà,^4ŸLðÿÃîwîlÛU·êÇvÊׇ64Å6œ®Ù¯³0T=N€Ó©1/¦«·JŽýš Ëþ¨Å­zøkèLª×‡Ž´ŸçøAaocOìãæÅÐ\]4ÅÖò½’0ãvnÕ·¼ü_½>t¤Å¥qe†..à·c'è;ÃY»7vZ^Ž?Ÿ/–#=zCÓ‹Ô“ ·êûîØí/¼>t¤=4tõB Ž˜qUÁó8VìX9`jâAܧv a)¿ÍêVý JŠÙ­ûCGjê¡®ƒ¿–au áuû8AеC=8ò¡Úì-_NÛd¨ëâm°±;¶’þɺ‡bZ˜Øä¬nÕÃ;íÙâëCGŠBH-·[CN«íœÛÇ ¦®×6×p&n¯ëtð.×v†:Nç¼eî`WŒ¸5»} z¨ßi÷à¯òV=ü‚¦K¯é°Ó¢iwRœõ»ç3û8.è›ó¡œ‡vñýí ™ën ÂkÄ]¼dÜkõ௞³æ’Ü ]‡cŠ´ÎPñLÖ8?0ûm¼Ñî»­áºËÚG·ßÇév†<šêÃür±Rîzû}<‘n.弡5„tÙÐA"­5Ôvzpž‘j¹çÞ´4TsWð˜öûx"­omã¹zÑڞ©•9IŸµZ kí•”ãÚïã‰tÓëÆèk·v<û}<‘ý5…“” Q” Q” Q” Q” Qt;Cú³¿ßjk‡ßïýé?œX¤Ûõ/÷3w¿“@ÿrÛDú³w¿ëÄ"uÞ¿Mç‹ß}÷î6« öºß ÔÙꟼw÷î^wiwœ^¤lèP°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!êÀÐ|®“ú¥Iºaáî`CPJûz”®_åoõOÝ6´Â@Uó çUÍ‘"ª÷ƒ‰;¡á†„*"ÔZJk 9À‰3¸Lû©/Ì÷oÖJ`É@ )ý4€÷}|Cúz`>»'nÈO´ @T(=—#_eZgþ\Ç2I–röŠ%¼Ìà¸I|üP¨ðòqcY—…þ¾Ž£S6ôÉwÔ'‚½ðEÖ+ðóW‘)³Rs,$>=~"L‰‡†æJE¦ÀCk >”XCæ 4‹`¾ó=¶œ²¡Á36¤S~Þ£¸Jn(ˆÑPŠÇ‚P‰Hˆ²9ÊÀ­0dìøØhÈö±Ë†“6ôø†”äGJCʵ ʺÄ6ÍøÝG§¶”‹Ì»b>)b#¡ ÉH¾Þ_%¤ojH9‚–€”Eã,‘"À©Œ"Xa`[±”±Î¤”)~(°‡u“ÄjŸ4ÜΛÔ×EÎõˆ åÜÔаµCØ0ßy#îSO-¾jŒ¡©ç®7D'ï?ñ ’ÔC=×="C@)©)†ÎÏ»Çe¨”ÔCf$£E>ÿ'ž¤ÊÜ/I5ôï»üù÷4ÔC;gdž¸Ú975t1>*CkË]¢0tù’4Þݾ|Z\š¯~`÷×´›y>Tg(¸tQÚÜÝ—n¬w¼ËKaþwl¿ÛÑÐk ë .½FΆÊËqÁ~»Gl¸!™h{\…~ŒÙ2{w ÁÛ…¾ øùr0d–~¯²Æò.i¸!¼?dî‚Ão˜ÚûBhÈÜöŽì†àïÄÇ»EÚO•Ê—H]¬"v¿³ œ´¡­ï±š{¨q`ÞÈ )_?/ɰÌøÔ‰ðaÅ|‰ÔÅ*û}(ë” mÿœBƒ¡Ô‹†Â$Iò&]n(†wæ`È.Cù*lȲŸg}Ê»à ¦Pù…¡˜—¥œ½EÓ9‡*_"æùLÆ¥œe/† fï‚G c e íÝm˜ÄR”÷öBøÈœ]Ñ. d>‡õÿÌh¸¡LÞì+"÷÷œÒpC:Loj(¹©á+hº!úœ^¤lèP°!ê°!ê°!ê°!ê°!ê°!ê°!ê°¡m„I©YKí’4È“¥ËÅ×zø‚ mA"’Á îvˆRZ­MmO„ŽŒ“âƒå•¬‡ºäØlCiÇ@:HæcÆ**–Æ ÎÇ£HgJ¥óЬ¡…Uìü\©üÌe.2%RÓ~*Ê Tl>¨RbF<~ Sã2Ò«9MCZŒFB+1‚o1 ’ x6BÈ$”Ú¢ VðÓo`i³JX¬bçE4òÍ’8ï\¾û>9Jd†D‹$ðu$ð/Äá<ߊ͇ŒôJNÔPêû™¹S L¾s^þØÛ¿¾})à»6†üA’E–™ÇÄé(ï|À7 ÅPùÄ:ÕY¨Œ!Ü’ ¡”+·ùÛvŽÓpCZÛ?ŠR¶Ï»Tê¼ÿÌÅ…o¹0„Uä÷ðÍ<JŠî!L¿x_[RV†0kW†ÖÙ‡—Ÿ(¬ô TÅ削‚ Ò*4>æU.Úv€‹TXšc%c1óéÂ1¤ñvr¢(ÅP\~ ¡b Sœ¢!³…í‹8ŒÔÙðlÕëÞY®wš†R>÷S凄ÚHÉâλ€yüâõ\„¡Ÿ†b¡ÊªÊÎ]Q‰âÇœÂÏ:ó&Á£¡VóÍj~šøÑâ¶´Ñ†æØ ´ÇÓwJR>W.8oŽ »(ïr-]èÒËÎÃb³$)Ïupý >nÿƒÙ,5«Á4M—¶°e¤6dY¸cŸšŽSÒË/ëÕUöÜ+†&M44¨ª[|´Îe¢ gÿù*{Œí=o|ϧ«WK†:®ëŽ›gˆgwî¼ ’´hhâô¦®Ç† ºSIZ:†àøñØÐÁ±† I+õPßé7ÃÐË¿îë•*¯¹Sñ‚7þ ³,¨Ý–±ºó¦rê²!Ê‘žUzÎ>³TÊM]ÇóÚlèМUzôrKáÜóØÎ*=š¯)Pä¬Ò£Ù}ØuØuØuØuî}Q£ï‚M’„>lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lh„o²SÖb–¤õzËŽDφ¶ ˜Ro()rÁ׉Êó(æ×íá½Ù†ìÛ;‰b32i Ëž;ƒÇ B©GRF©ðƒ(BC°J¹3¸*-í0ç°™9¼-bm·h§" ANøî‡H.^éÕœ¦!(h%晟ÉXÇEÿÄ"0cøF©ÒwTäàÉß¶Øy?™‹Ò Dùùû ?šØ-Ú)¶¶ý$U6Ûårß൙Æóšùì«íŠtïΆ– åiÄ›rÁ·É4^647!aÙа7m ¡…AøòTüM¹øÛdëÛ-–Û}È« g¯ýí.Dºh¨åºî¤y†¨Ù)•¤¥ /ïüÂí²¡C“gxå’Vꡞ3dC‡¦Ê’DI+õPcJ¹—Óãàµ?°Ò–kJ /{þóßF•7/zÍÛ~d©šÌ<·†(GzVéùÇ•zÈu=‡[ ç¬Ò£W M»íÅ.IØÐa8«ôh¾¦@‘¿¬ôh6D6D6D6D{ΰž×³¡Ãs﫞óX-¯üÛr=6t(šþ¬}ØuØuØuØuØuØuØuØuØuØuØušd(Y¿›v¤‹4ÊÐB’"íH9!CA(ó¬n…™ÜøN$²æ—¦ðŽC#¥õH<҇℠ùQšç`ùá"‚d æZŽ?Ñ2Nf5Zy'mèÞRË _tHCEV·°£«&˜J§¤R2Àh€Ã{ËAf‹ºS64xÆ[ëù¼G÷èÕTYÝh("Í ©( ó¤û»®@NÚÐãž=yäÀ†Löué¹ÉêCØ‘Šqv“Ò­EžRʆ2Ù׺ÈêÆ4læ^'~žÒ­ƒ¼Å͆Ãå¬nKÕÎò¬új·ë }ö¿vì•\e(…ªæê9^¡Ï~âßî?Øy¼›™×õqP¤n—YÙ14uݵ†PpÛ†¶§1†Zﲡ\Ú 73Ôõf«†*=ÀGŸ¤Ê/¼º† ŒsÝÞ‚¡gýÆýcá÷õ•jy+†yôé¤úC¿ô½ûúJwÌ[Ûkê¡J×C7çÆ†&“K†t)‰ Ýœý]S@I4 å'Iöd)ïé ž~r·Á^ —b/8ðu¹}zmA>c Åö¢^à–»ñU:MÏך QÂÜÂΙ•2¶í ¢“6Dõk ¾•‚˜ûØV°}˜ž²¡O¾cÃs _Mo¿}Û}©{Ïn€¡]lí6Á±)j~=´‹­Ý&Ø—h†2‰t*užõÙÅÖn“L.½Tö68ÅH×sú†t¸Ô7vÎXw±µÃsz‘²¡CÁ†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨CįõàzŸ¼&lh•+¾ÿD'J¶ú7vCà ­öÅ—…Bé;áÃ7:€…ö½SÎH-ý8Á­H 7´Ü_н$› Á7üL:óÄ×åŒÇö0&nÉÑ)úï7ÔúõÜç½WYî‹oä‡YѵŽ}Æû} qµr&7¤•ˆv¾‹k9eCƒWm9ïÙ&;e¹/>­c!¹!“'‚bTaH•†Ámù9qCWgx]î‹o ð‰võGk …:¼ÅæZé‹O›Iè &!&.Z1²2”úÁºw_4Ü®ïé 1Ôòbž"Ì¿ B©GRF©ðƒ(2CMÉj 3?2PZ¦>&Âfæð¶ˆµÝ¢Šó¢Âw_7}­Ù†t T mέŒu,ò¥"Ð#é¸1Ÿ‹C¦,“¾m±ó~2¸Ž!%04T:óó-Ú©HâÀŒ©¯¯“"ÕpCÚ/sn±ù ø·Èp䢓ÂåÇ™™ÇÔÏ87dó©u–(?ßb™Ëëk”„yÔ׉ôÛ¿¦~H®w|²\ï¤ Ùœ[]Öf€‘b±c-´hHåU™GCInHg‡(ÔÈÏ·Xæò1”™×í‹àž³aT»g¹Þ‰²¥œÉ¹õ,Ÿ,P•d˜º©q\¶ * Á—=/*"3ɹ¡1d{ëˆ0EK9³E;…wR!tª²5al©³á¹×º!¬UÄÈæÜrn…”bdoÆP+«ØùX`KÁבŸ¿¯ð£‰Ý¢bkÛ¿AŽ{³ Y.gtŠºs˜yÍü v‹ÕvÅ5„ -аvQQ­¡j•[_64¬ø‡àsÿ´é’¡^«×@C 9·i‚¤éº,¯²qëv‹åvòªÂÙŸþ“JÒ’¡ŽÛ^J6iˆ!jœÝJIK†Üî¬7dC‡Æ*%-;¯Õ”Rîû_ú^ªüδlÈ/):aC?zçXø†%CÝY×m†¡{/=Šcè›÷½ËõP{ÖnŠ!‘žUzôJK¡ï¸NŸ š³J^=šô;éaC‡á¬Ò£ùšE>øÞÅWlˆ:lˆ:lˆ:lˆ: ¿ÇzÜ{цçÞò¿åzlèP4ýYú°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°¡mÈ’ù¼îÙÞú%ëV-I/ÍÔÀ†¶ óå¹à£«¾çbÕ*"ךyyuCj±¡-°ßb½!]ä‚GWŠ*3•/*–¬¦‚5ÛPêϵHUˆ™X‰¨†ë•&£[¡aQ€¹ày†—,óVì<&q)Là©N1KRKi¿D–ÀF‚PúÕŒR±oDBø‰Ù62c8â3cÆF½éÕœ¦!­‚ 29fҤ̒ŒL–db2„²$ ‰”8Y’A‘ilüÂ+›<IÜš?Ò¡*g`#FnÕObi·ÁÏæ¥Îg4L{)Ò+9QCpœ€¦óÁ31gn]¦11N^™y,¢e¶~æ[0™ß&Qó˜U9S‚1Ll—°uÜ”KòLë_H »÷-Ï©Dú (×;QC™ïgÆP°œ­Ÿ­ÏÖÇÜ®|f~Ñíñ ¥©·1b¦ú7›•ÚÎÌ«tZ©³a éןüs b4‚o_š+®º³€ß0vg Žs-ã²Ç‹¸*í<¦âC6k2Û¡é±’ Ö2ǘéG#©-Aá½HâÔ̬v/Óìg}FPš„#`ï.™Äžb,"Äyü½GR*hè(—R”«ØùDJ³dPþîᓉéL?…Ÿ‰¢rVUæ0…£M¥0fÛ‰ÄÎJ`‰Y´Ñ†,‘ ~ØÐ’!8š€`Scù*{Œíƒïùô«EChHåHÏî¼à•¤UCN› “?TJZ)åºî:4Ež•´bÈ]LÁ;iC_ûü·QåÍU+HzÙrŸ$î¬!†~ì6Ò¸wÂ7.òÚM1D9Ò³JÏ›Î>³\Ê-rlè0œUzôJ=4vÇlèðœUz4Ÿ±Rä¬Ò£ÙE>÷™ÅWlˆ:lˆ:lˆ:lˆ:÷¾ü×òL6txî}Ç‹õœ~Ÿõôiú³>ôaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔi’¡ùÂÀ[´#]¤I†’käž2”fé\Û¹|´{:0ïÌSœ¦C³Œ _H%Â@èT¨À$‚ !2†˜„~–L_ ãƒGúPœ’¡¹V F&8ü³I #3ämhòM¶¢ŸåÝ"œ²¡¼²>5ù¹Ï{ɾ½š|üh¥L] L¾ò™„yJw lnÖ)¼jCnò³ÝW W“Á.h(zb2wñ¨Ê²ÓHC0”ƒsŸ´¡Ç7<{òÈ ‰A,ôH$¦w4”‰Qÿûñ@¤XÖ‰¼=džT=*TØ+Î(ÏLæØMN8€ºÇó¡0ïK‚ †ËYÝ–òL()FènŒ¡¶ë×zêÁ'víUD5Ãu§Eê¶,º=hŠ¡ž{¾4øä‚¡§²“Œt- 0´–Ó‹”  6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DÝúµ»?|“`n‘_¿ûs‡aK¶t;Cÿ÷ö¿»I0·È¿¼ýŸ–léÿØd(‰-ý^äIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/software-components.png0000664000175000017500000014456314770023131025151 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+É%IDATxœìÝ\ïðï6º[DRAlT» »~vwÇ»»» » AT° ”^ü76`HídÂçýÚKwÇív»=Û}vOœ‚@ BaoÀ¿€h @€`€h @€`€h @€`€h @€`€h @€`€h @€`€h @€`€h @€`€h @€`€hþùÍb± {@Aao‚Lþù ð7!@0€ À4Å-@ÿ+mÏà ¢¸h€… À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4Ð @0€ À4@ÑîspÓÖƒgoù¼‹J̘¯Úþb䉦š…·a4”0)ßß=ü, ècø·qI|U M-½Ræe­mÊW°1ÓV,”J Ú3¤å{‚…òìP@¸1_<õ{ýöCè—¿âSØj:zúú†&åjV·5RaöæÀC€†’Aôùö÷œ|ö-ç¥TL*×qvmÒºm»V.vú)Ló¿_ãÖwOèßy6(p)_|mÙ´ëÀñK?rZFÁȾQ‡Þÿ×ÖÑHéonÈ4¼¨;+û÷˜|úSžK&†½¸~Tx[;íæõíjÝ¿°uI/WØ’žž+u:Êݹ‚‰Ž 'uKÑ ‚zê½èc ô;ß/f<üþÛõµÔþÂæüŸ‡çŒ·âFd^Kr¿ø_Ü% êà½øW @C1Æû|tŒÇ#~ú´Qûuçw sÔÎÔ}‹¥¤a`Yµ¡ðÖið4Oî÷—÷¬_µê0?ËÚ DÒççŸÓî«–¯f‚ŠüP‚¿gûæ™Òs©¦37­Ò¶¼†tQc)iš oå«Ôm3ÐcãÏ—'–OŸºðT07Ë  ˆC€†â‹âåy%ã|²Ãü+FTVÍý1 ºv­Æ¬o5jåÏä¿’e¹±?Óî«éªad†Žà׃Ù'ÞÉ(g*uç\>3£ž'÷DZ5í:Í9Ù~ôݽÚO¹PÀ ò… Å– æÉ)¿ô)峆Øç‘ž3°•5U d£²à%§Ÿ€dqŸÿ9I+û- ʘ®0éʹÿ9kËÚƒ£ï<ÙûeÍu»•Ñ~àß ÅVÊ×W¡¼ô)«zvZQ‰‘/Þ{ð6ôËx®‚šŽ‘i¹JÕëÔ¬XJE–§+v#?ó~…½ñ÷ù*8äË÷˜Ÿ‰E5m£2V¶Uª×°7ÓÈã¤lžÉ_ïÝõõþô5&A ªoY«k¯F95{áLJ¿|ôði è‰Ka«hšYWrª]«RiU¹þ—3Ó–fLÛÏ9ršÇ¸ö²é&=Œ””oÛëilÏß:¬ËðýorÙ˜oÍêxhC«e'÷¯­“Ÿw†ûùÜŽG“VƒÆÕczö9/TÒ¿üÖvo4Ú;ûÕFÜ\Õ³òõçW®-rM]7âü”VV>É~|šÐK‹:Øû,¹ã=©šŒ?Ix_¯NmÙr‰or¶M ò^ØÙûPï=·ô¶ÎõçHA} “ÞÚ´Ç®·Ùÿõç󃓻ìyËkŒCî¿€J£¼6 þwŸÅî­f\ËñÊ@ ŸXó(̱{³¾ÆhÅ4[ FUtÈOò•ŸtqÆÊMÔÎ;Þ—s£œ[m ÎõZ‚ô<–¤í‹ S~æ}½<¹Qó•/2 ¢ kbj¨­Âüøù{ÆfFžäìuóæ‚zœ¡Ñ>GžfLš»w¶•ïY79”´l%¿ßÝ£ûhïŒ$*ÚúªÜïßã¤öïùÒ6}*žìe{u|ƒ–k3šy³Õtt•’¢~H¿#ñ7§´WÍs2jüóe­{-ñMkú¯jlm]F‡ö.(ìWÆRïöö©“À~| §y'z jçp?í߫׌Óò¢+°«¤DGKïй4®ù˜Šþ[šåø‚ ¨4ækóþä›D$åíæNf\OÌ´€®¹y)u7.6&*ì]èY¾ÐŠh(¾4ªõi©»ïÀwÉäëE.õ#W®žÕßʼníÒì÷¦ûøè£ßŽza—.~H®Qñ÷F)¡×Î}L›°ïXG?ëa^Á¼A—.íÛ´jRÏѶŒ–t—8nÌ[Ÿ“;–ÏYtæ½øUGìí;¬Mce‰Ðv˜ššÕJ¹Žœ1¾_{×*f¢º¼_¡Ï¯ž °IßÒ„+Ú¶’Ê+F.ÃgNÞÍÍÎPY²-ü¸{GVÏšºòêñ´ßâ6_î\úÏÚf']{™‘Z4k7Íý|)Sò(iÙú¸»ß¢ô¬UcÐü9£º6²75:$E<=á9~ØÒ›’ßœ¿ÎŽ›yÍ©ùÆNâô\ºñ„ù3†t¬g“ÚÆV¸3}öÏ9rëóñÒáÛF®í7§r^¿!‚· ˜õMTà9¶=—¯›3À­œ¦¸0ñã?ÞÙ3oôØíÏ%Ñ÷Û±¾]j9ÝœX!›UØÎ ÙݧÏ{Qu+bZEóÌ[Êÿv×+ m¢lë†&™¾8XšuÝ~[½nÙ:“)h—kÐwAý.}vötp"J4ëçÉéë_¶—gÚµ½=@§ùêëGFUÕÌx¯8¦Ží;¦M&ú-í>å¾$Ë‘Qûõw­ª•y{ØêæÎýV\lÛ~^«Æs¤¶øq|ÄÌM·5ú“–ü˜àRín­ê”•y`IKü)|t¥1g¯,oaœñN²”»-¹PµL‡1wÄ{òÛîn|fmõ†K®Ÿ˜XCjuáά7xóM;­êõWHrì›ÍëOÞT7ë/ÀLxßâ„ÿª5\yß{le5©ýÎV³h0tۃƵ;×tFœáyf ß×åÒóßT¸sÞ‡ ÿµì{àêæîe¥Š'KÉÈ¡ýÿ¼ê:ql½]2tûÛ-«îOÛåšµÅSÁ•F¹lC1÷·OkS¹áæG[eó#DAËÜ©Õ`áÍcͧà_ºh¿Å4gJFÝu¿fßãÒŒøÏlÞR'ØzÖÕkÖªS¿a“æ-U+G—ý„§ž³®§·µÚí»°Ð5Û:\Ža³•—¶½ª8𲸢;ñŠÇªg]–;É3bå¥Sýw$Œ¼|î]RmûLy5)øìUÉ~1ìà1åuב>¢ýü؃èÁæ™Ú{b{œö+ĨQËr¿å^U+—zyoªm¿íûoÜj¾75BïÞ0c¹£LçlËM8{8Szþ/ìèÄEi Ÿ]}ñÕÃs²£_ÿ'ö<³ív*5|Ý3ïä\×¾e˜ÿ¶Iù,U¬ŒËéÉñë´€Kšz‹]ç3¥çt*­·Òi©¤¾á{¸0–rúø¤ÙÄ:–¶ótÏŽ[Úx‰óUä…SoëVËû=5ì{ìè˜Lé9²õ€}g^T®¿FÜÙ-ùú¼µ~=—e.'¼s”]6\ÙÒ½lv]]9F-¯k¸ÃIqc“¨ËÇ\kþ¶º.ùÝ<¦R"ýÞ¤5×Qn<®KvéY[ݬ|?¢Šh(Þ”,û|f4¯OŸyW³í¥Ï~xAxÛ¿z±J×í=jò´Qm*hdþ$ÎwÓ¾Ðô)ÇÙËÝMr>Ò)Xô^=c¥ÝÌ—âɽ›ŸÌÙ’MÃ1ª—Ñ‘ðí™ëaöVRxîçëgß‹ïª;wvmú²ùˆD§<<öìgç&Òœã^xù¤Õàª;w¨”k…o.Xºõ‡t0Ü»-õøtñÆçEŽåòÊ@ËÝsZ\›®§¼;°ìrZ 2³aT~sJ·Ÿ7¦Ü©ù©Ý°¸wv\ÿÚ»ó.O¼¸¨Œ8HÃPSŽß¦\Òl'/ëj–ÓæªÚuu7[º"­))¹-öpÑÍaÿ³ôêõo¨ê%åã€Aµ¼Žc×?·I6€ÒV©Uwúò–[»œ“¬sïÖ§s6Ö‘*s¼sÊM\ݯ\ŽŰ m¡yò¨ø'CØ}¿h^ÍÌy·€Kc~71~rBÚ¹tRPQù›µhE4{Š&Íç^þØÿʶåË×もpŸ=ÓÛïYꧺ»c>ê„•JW2&ÿ”yïó>q|ÞZ·Ýp×ìÖKÃýtnß‹´‰r}û9äð•­Û´4™¿VTN¼ççã{käù ßRS2¦”ä×ò³€KZÅ~ÝÊçÒxFŲnyΊO’ÖñŠ †6Ë­Y®º³–äÓˆ×_’©Tî sŽnošë‘‡]ªéèÖšç$)0ò’wpRŒÕ xçØöëU1ש†› }"žˆ|™B™j—ÆünsŠe …¿ÀSïÇ]Þx>Ì­K.?XJh(Xª–MF­k2jÕ Ÿ‹ç/]¿åsÿ¡ïóO?³[öÇÝåí«ø¯»ç5¢b¦SGüh¿;é§åȾ“³a^'-9Æ.ìè¾$\|¼ýâ¿ÚßÜIŶ‹îÊ}©Ý¨xŽ=ùÙ£EzGÂØÇÇKº8Uu TÑhí¦¿t§¨yEÄ¥óï’kUJ;Ë•òéÊùôÁc³ï@˜†pùøñ³W良,g%óOÕÆÍŽ½í¦¤%yìý‹Á‰äÒIôŸ,i²’ít¿0&K½é˜\ÄwN¡•ÆÇÑ©ÚkÉ™^ó£^Þ îdz|´ÏIzüi7hŸy¨kbë×íX™n>'Q{£·n«ßO‹Çêõ:ØåqIˆÄØÄ\7Šûáð’s9_Çæ)•ëØÝzÖÜ`ñT çâÃv6ÍéÒyòÅ.ÕfáÄ gç¾’LûÍè:×ÍgAmFg¡yßn®ß­øß„ºéW,ä’V°R®¯=ùÉ}°EÎ~äÅÕÞéÃRM[[Kÿ0+â;§K£¬þô›$w,½ší*ѱ{â©Ø¿>äÀ߆ Å– úÆÏ·Fô®c,ã ”¦mŒ­RÛZ™ýªWÚËtÇ*ÉàZ=&Ÿì|¤sNWiã†7?}e2ï=Ä1×ñVÔ5×M»Š_ÌÛ÷±|×®8žåæMZ˜M})>g°yÉ–âùJµÜ«üÞLQÁ¤a›rô\tݱø+ )¦5läÔèT-›&8𦢳Îâ”råv·ZÙ¾T¸¡‡ÆL½/§ƒufÊúO®·pðñûÛîÿ&tz¶µÑ_¹\šJå‰;&쯻â­d:`Q£6WNM­#ÛiN^”ÏÊÞí'_°=Üs¼Ô€p…[Ò ßgæ¬+v4ÏáŠ<‚Ø{‹&ŸOÿ¡eÞsPÕ̿܊øÎ)ÌÒ(›?þ&É/.*£öNMãÙAq‡ Å– ù£÷üÿ›?£ÞÀq£õnSÃD%—ó@‚_»‡·p)>}ŽBý1¿_%XµÚ¸9 7¸.Œ1^½:.4¿0£–v–CÿÛ•ÉÍû]H_™J“¹c«æ³>SÙ¢¦%m“Œšöx×¹ÐÞÍeû«”oíª»BÜ‘>Þ’ägªê^3ë™1år-Í+º¬LœŸoúìÊ9\PÝ®…=y?O<=ÕËý@—l.µÂ ÷Ó¨÷™l¯ü( =×Lõtœ/©¨ßÙÎUùÈYOw«\Ï5òbN­]²+fèÁeuÿ83Æ>ëþI ÞÑ»Íê´"JJ®ÿSå÷—SÄwNa–F™0û&ùy{êØK•‡Žê\Ã(·üï·Wy¦Pɩ԰\Q/…ù„ Å^ämS…7Žim›4¨W¯nMû²¥ uÔDW$$Ç„ù=¸î}pÛÆcÏ3å;‡ÿ­í•å°¢`Þkó²Ý•Gß»“ï{Ô®üh–缑m«H®&H½lÕ´±+nfôûWi¸rcÓüž‚â”rq·§GâKêro ªÛòÙ¨nnU¬ 5”Ø’ÂV7¯RÅ,k*‘îH˜®Bû¥²Ù&5»öõ56ÿ•if¹6Ùt Q0k;²áØ~×ÅCó~?ÚÕ1îõ6ϱ-˧]¢›÷ëýíýK&OÚü(uïXª~ûPÍ8ÔªÍ8¶â¦ã„Ûâ×(ÜÔ¹ì¹æã§àîf'})=Arôû¾>WN>pÈÛOøivý/ŸÍ²YjNŸýÚ¨ùr¿´ñHÂÏÏnc½Ø¦iï^Z¸Ö©fWÎÌ@#HùòúÑÕ³§N:û8<—óñ…YÒ G_m\5§ÇË×Íаœäô'?>äîÞù£Çn}–6î8±«Ï]ß;›lWÔwNa–F0û&á}»¿cþ’ó‡UhìÞµk§V ëT)«§$}±–ä/~g·ÎŸô¿cé¿{t;OnQ g ¡˜C€†’‚zïØáM¦…Ízòž–åÌ—ˆ’Íð#Ç_:·Þ”VgÿéÌ÷3sH³Œ™ëWä§o¿åÃò#Oœ[#MY)–ë3«ý‚Î'%cd}¾¼~êåõ™±šþéyž/õ¨Ø=}íWëÆ³î¦=_È…•C…7RÐ5µ,­£"Hüùãû·ˆ¨¸H(ƒÆKnÝ7íÛvì©ôëðPbÐ¥­³.m•eÛ«7°ÊR=Rˆ%­ •²£ûå^sq¹¯öm¼o¬š‰µ‰ëgø»×¡™k(ô:ì<:Þ.ûrWÔwN¡–Æ<ýÑ7ÉÏWWvÎÞ„wÙ:&–ƺ:ê Âß„¡ÁÁ‘™÷uéÞÛ=›a(ö ¡Øb©Û¶haã>(.ïe¥VÖ}ùžMcœõs:UÅ)ÕjÝý&Ý;y\‘ºhïÏÏA/?gYÖ°Ù‚ãû§ÔÏq]ÌpŒ;n?ãñ­õÜ;LÛB°œÛÛѽ—st]ÛÚf_ÇÊÖ«ÝÉ®?͘“{BûIÞ'B¶Ûô:c^Røëgá™3hµêÒÁ¡º»v0ÜtÙ±uêz\ pðè;`Ùíh©ùÜï¡ÁßCsz™…–\Þ¶võ1^¯ð9~õí¯y//aPwø’5óû:ewí™Â+iˆ¥VeÊÙsñÍ[-{’Zmä—ÍІÝw\ÚÞÇ2çÀ[ÔwNá–Æ<üù7I*þ°w?²R¹ÚÈ'Wt0.òå ß ¡ØbiÖžuîÍÌØ Û§Oz_ºzíÚ§Ÿs»Ê®¶m£Ž½ÚÉÑ ¯ÏÇÀåßwôZ1wѺ#O¢²[ÄЩÛÈiã:TÔ”gM&[¯Áœ›¡ÝÏïÞqÈûÚýç¯ßGþ’íÊ~â+¾L¿(в0$çÔÒRÁĵ­5= N›æÔpÏ®¡ÔJ·ÝàûÒeî¸i+/~Ⱥ=JåšôXäÑ«ª6›ûA¦­ýs*Ví—Þl2øÂÖ•«7ï¿ø*6§åT-j6iÞºSÞêYʯ·[ˡת[]¦=<ºyÓ®ǯ¼ÎñùK94qÿoèÐÿZØëæVÚ ­¤$Ža“¥wƒ\—Mœ¼äX@–Ÿ·JåZŽ[°lf;¼ÎaùS¸¥1W²“h·8øÄÛûôé3ç¯ÜÌréA)úU: ;ub¯š†ˆP2 ¤C1ÇѲqí5Ix]8æsPàËÀ×ï>ûû+‘ÇVV×Ñ/UÚ¼l…ÊUí-uÕï²5+¹Ï>ì>kWDÀÃûO‚B"cxUíRæ6•k׬d¬*K¦v« 2]ŒXú‰µ*´µ´Õ(fRq\"X.Û²J•f f3Z=K£b·¥ºÎ ÷»sëßÛ°èxž¢šn)‹òUjÕ«a­“öE£`9ê™ ¯-W°ó\0†ÑÓgÞuëc7´».!òõßgoC£~%ò9jZÚú¥­líììl-õsëQš?J¥jöôÞv¤üxï÷ÄïÕÛŸ¿ÄüJHá¨éèéë–±©ZÓÉÖHöç—CIc¶KÕj­¬Ï{91í6WxÌÛ!¨X´üßÑ–Ó~?¼åóôuÈ—˜DRÕ3µ©ZÇÕ¹’‘²ìïÍ_ß9Dn‡b‡d]:ߥ±à6O¶o–Jéj­ o³ˆ¸?C_ù¾ zû1"*ög|’@AMÛ ´¹u¥j5m ”ÐjJh(9XŠÚ¦vµ…7¹®TÕØÞ¥½½‹<×ùÏk«4îZ¥qao‡[µTEçV mu¬œÜ„7ù¬­8–4ëºm­ëæEEçzi”MSû:Â[“ÂÞ€"€h @€`€h @€€”ß+ã=Ð @0€ À4Ð @0€ À4Ð @0€ý‡X,Vao@¾‚ÂÞ„4Ð @0€ À4нVJ&h @€`€h @€`€h @€`€h @€`€h @€`€h @€`€h @€`€h€âO$H›d)é髲 s‹þaÐÅ߯'«;ö$ðÕçØÔí´ùÓƒÁ¦œÂÞ,€t  Û×Ú¾÷…ï™fô<°§Ñog#yŸv¶pèùGæ¹úíN¼:VëRû*=Ï~•õI[œýv®¥–è.÷ÃÚêV£Ÿ3ßðúG£n¹ëeš%HøtïÔáãg¯Þ}ä÷úCØ$É|%M£Ò¦VÖ¶•ªV¯×²­k9uV–ò½½yôÀѳ×|û…|‹ç§ÎUÑ3·±wrnܦsήå4~ß%…¼÷òE³ÎÜÓ÷çR‚ïX›«?g³/üô¨fÝ6¾4ì¸þò!å•óýŒÅt Â1éu>ºGRôGÏnó_‹g~ÛßgDûÀCîÆ™NGrÌú]úþ_òP¿ý=댼]qÑÝ3CœÌu•EY´‡÷—î)±¡÷W6m0ç•há²=¦õɹ¼øw§<÷þ>ß°õøQÎÆªŠ¶$Øò¢n-[xJ”)Yöƒç÷³UI_Ar|tйµ;îg^÷ëÝõã†MÛÿ"H¹¬«{·I£«–73ÔTLùõýKÈ«ç¾>×Î_?:¶ÓËîÀWtöïûs¯M÷º|çÙ»èÔÖ• šF¦em*¹[>¯C–“MRýæ7k>ïVx²x²²çÛ'cËþ¶|JäÝK–oóºöøc,_øº mk6é2lÊÄ®ZiïDÜÍ>f®{%í-•[ûrªö§s{·ï=ræÊ½àï|Ò´ré1mÕÒUÅH|½{ÆŒÍǽï¥þÂz¿°ŠÊÂÔGšŽó}³Ò)ï7J Tšå¤DVš2èOÙvÄÏ•FÝN&¾ïÔK›ß÷¨’ÏüòýtórÍ}}³ä°?Çÿvß[rVÇʵZ6çWr—æã#écæd.KÆU2u´`Ó{aZL||)(±]­ì÷Iì=ü|áu:ʹ««±l»ucNû¶óîü$U‡nãWίa©ÍúöúÑÕã{]öÿd7Ñ#×­â0ófØÌ¸ã+Ööü”ÝæÄéwX¹¡•[³¼5šBCŽPi–TšümÐ@JÖCv­>n7ìF‘߬îó[>š_=›~à2Kúìü“/¿íI yøAr·LåÒŒ¿ó“?û…‰ï±m e9ëÂR5±1 _„w#^|ŒÔRÍaÈ{ïÅ?]1b"ûX{ÙtÒ«5m›ÎóI!Ý{žêm™¾oºÙ­”Mc²=©‚ªj¶ÏÆÿvndkQzÖïvüñ¾Žâ®G­ÚµràÙ¶=ôiOŸÍ½»—NËV7«V·¢ 4é¹Ì»r­qYEÑž$¾;:´A×ÝŸ)îü‚oºyØ)‘b麭Ûsß}˜K$ ÐZµkN„ ƒ¢Sí“Tšå()äÒºyËvž¹™H¢Ú2c+›Š•]-_ÜÝ*ó—{^u_?Îw­ÐòH¤xaÕv"O6S‹ <·mý–½^—^•]½·Òª^[^'I­Q˺íâóG‡Y‹ž‡vô¿f#Žø•T¿iTvþÁ†zêòxêmnõ–áM‡4)–¸k½WÅ—ˆvŸÕúɲ:š²†@~BTDx¸TË·˜'w>r弅ܨÑâ{ê†zÊŒó)76,F|OE[]¶BÏVÕM;hÅ„ÅòÈ Ç‡ÉwïQÊû+÷"Sœe|x„׸éÂôL .Ë×ô°Ì|ðQµmëbtRÆ É^JðŽ©ûD‡q»IsÚdtÜg6ÓµÔ¡M‘ úÐy\¹ßwŽ–e qzb©”u_¼Äe_¯›< ºøËÃNþ*Ͳõ¯Tš%ø¯h^câ­D­ºÃìkn§'øþáÉ…«öx}´7O:@ËR÷¥ÓâpDòš‹#ª6ßA ¡O½ænš7÷ä[žx êõ7¾Š›}°u¹„ßÎlç-ïo 2—§¤óÞ÷DîhÙî”ýÆ÷·‡Jš…Ëá©ó¹Ká_€ ©,únßx¼âç…Q.xE÷émž­qÑ‘-&_îik’eneùn/1>E|OQU‘ùù]^r ŸáÃYJªigªSS¹.+÷½Gú²h^˜÷š ¢s8lçA­²?§QcòŠßLÿ¼#÷Óùý©'ÑŒ]\2¯FŪ®mŠþd¸øò׸r:y¬ˆ­mSÙˆn†“àWt<ôpqÈTšeãߨ4ã}Ú?lÊ­D*3úÂÅÕu$ã—´ëÖ½n²MßL Ê^÷¥¨m¬/þ…ñØs‹Ý ñ‡FשZ^ýþàj}SOºpJ·™Ñ§Ô…͑Ŀ»áø‡ÿÆYgÞ›üÈKëÎÿRo3Å=-ZËí©¡˜C€ ³^[·xUìyú'ÑÇu=&·}±)»¾ÜY±œfš]]êÒuq=ºÏ“ó)h¶’’p[D‡8^ ùÃ9Êʯ;Ñ“ã“sÃiIq’z=RRSÊëð"¿½G)!†¸$Ó6R\ÀY¿Ô;uí³écÄÒ¬ÒklkÊ^BðàÔ;ºfÚ,.WúMUÔчR(åëçXé䙈Ùñö dÛý¹A¥YVÿD¥Ù/?¯‡Â/bVù†¥Fÿcé4˜1è©ÞšT÷å0}ߎ1VâYeæŽZ3S%zU5ÇŽ¶Û<ã%ѳU›ü†.ÏÔI™ûñØÊËIÆC'71`Éÿ©¡XC€†tœ2]7m;~³«—ð‹;lk¯qíüw¶’¡êP¹Læ­¥‡M©ðºÒÒÝòÝ6]3]"Q•_‘ß“ó\<ËÃuD}'&þø™"ÓCxñÑñâ{l=Sí¼?(òÚ{Dßy›']‘mcÞ‡ˆ7R×B¯>Ëü_aáâõN·S›žýBÜd21üu¨4ûÝ?QiF¾èu üOÜ‹jÛB?ýW¿ªýÙöKå¿îKÕiñÃó’ûJ6ÿMk4§÷Õd Ù¾âÖÌýÍ2^jÒ«]«}©Â¼Q5ÒÆ-‘ïSCq† R8¥;mØÙõFÇÃÑD_öôÕîåF¼õ;E‹n«¶—56”g5½ŠEm+Ú) ЂP¿°$w=fÎTÌ«›ÑQ€„¿ŠL&½ù~ßÓ¨ÑÄZÆ~êrÙ{¤dçÝxrY±U4%?$c„‡jy÷ùf Ã’m2ªT£V–®‚… •fÒþ…J3R´¸vÜ6Ï×$:<¥õá)Z›wïÓoPÿöNFßÊr¯ûbi;On½{^0¥ÜXº7¸ÛÔÔÀ,ø~sÕþ­ÛÚ—Ièv™áˆ¿a¶Zµç¿+-wEÅž:^%_ÜOG'Ïë¿zŒ}þKR²é>¸Ê¬‰Ï…wl;÷¹Ï@³¼~þ'¿^Û}bÌÿŽÎ¬*|f•Š=ºZ,_ö‘(Þç„\sç¬×È$%äòyqˆÕj1 V6—­Íœ÷^8Ú–æjôXøu‘L–òþ8³ÕK—Vî2ax÷-… ¡¨A¥Yº¢Ò,µ¹óòϪΙ2wýŷ±6OÞLÛ.;¶{|-qWލûR¶4¥ÞÂAwx°vÝ“Ñkk«?â슓±e†Mj(5äªÝ@f(Û ÙŠ½/6Ý.ü6ù‘˜¯u bnÙ|Ò²“g¾·JѺÿ²~K›îüB|Ÿy+vó¬“ûW½ æ¹×¹Gå'JªT1£ÁªÁ·RèË!Ï+ ê´3ÈítmÜ“MëÅ×0°5Ý-¯kd"Ͻ—'µŠÍ*щGD‘7¯J®-Ï‹"¤R±r¶¢“€~çA·–¾/ ¨A¥™Ä?Qi–Š­]¥ÏÊ ½E>¿xìàþÝ;Ž<>{èéInÝ ½ÿ…Qu_ÓNÓ;Mly$†Âv/»:ûX­÷GV^K©¼dD5é_¨v™¡p@6Øz—ívÁmch~×”ðæ²_¼ª£º°t-?0âFãõï)dUÉÍ­k–[N|wëu"§2'-ü*XôÙ²dWåñ>)±ÇG̸RocSý-øõxÙ ñEù¬Çïœ\…é©sùí½oié¦tØkiÇrY†JM¿º°{û9/„wËtß{i‘³ÖtΓÓÞ“ǬǪi«œæPÔÁNnš;ö/îYE7ÓΤ$ñ•ÿtÿ+W2¯ÍÊîg~чemz[œÝ>Ô)óêù<›ó§ý9ƺ©­B®Ý Iv,/ïèPB Ò쟩4ãEù¿H´®RF%} Y*V–oésÌmO4}~þ9)õd{ÁÔ}©: ŸXcù¨GÄ¿»|íÞÚ{ÞévÞØ¦ôo_ލvY¡l”4üÄoï߯%E¾zõ©Šƒ©NݵYÚõÎyåÛ\V"’°ùßløñSâ¿<ÝçO䤫–ÃW??1úsð‹w±â©ä/þÁv•,Jiæ8n“¢¹û–§g¿üê®þ5íݽµ‹SE  /î{ÄÇ7/|ï^={Æ'$µ›¶²‚ôš8†MWø¼pš6pÔº{ëÜ­÷WëØÛ½I­J–¥´”x¿¾†¼~|ýä¾w…‡•J=—n[3¼öïüÏÞKøúÖ?èGÚköú²‚¥©áþÈPsô¸àÝ¢ó^À–¾U· 3¯R­¼‰®ª‹ŸòëÛÇW¯¸=®>—Œ|ÊKJœ¹‰IÂ;RO(H‰O×Ýrãã¹R_ “.;N>iÚbÙÓ”È#«Ÿ˜eW¯Fyc vòϨˆÏ߆hŒ¼ã;»²rúj$coe •ö¢“3þ’~Š‹¥[§W=…›·¸ôd¼C%/· ñ_Ëüïâ¾¶úC˜@¥Ù?RiöËgBËõžë’©²L¹T9¢hR~§Î( º/‹nÓÛMíp*ŽÞ,ö†Ì'Nhe𾂮vƒâºá…íomßëÂwÉäå¡5̇é·>pªKv]%XšuæìŸì]{雜W"òvï¼™¹>qvg yáÚUéyö«ô¼¤ë#ëØŒ$Qíå˜ë¾«jg߆¥^¡ë²+¦\›áä´ðeF¸äߟèZi¢èžŽÛ–gY0ÿH*švXÿ$´ç¾5öž¾þèeÈó»!Ï…{©”µ}u—nKV îê¤s}d“>{}Cc%Ùõùk¥IÖ.㎟ós²k—¾a¿$k œQYu¦–¹óÈã—TÅn¶A£¥÷?´Ú¾lõï[O‚_Þ¹ð’HÕ¸bÍFMë߯$='<ñhÜbÏñK ˜l«²¨RŸ×v´Ó ÙٽɘãÁ?Åë6ª¬ú²–+¯œa£D æ}÷ ì3zýOIÁ÷ïó]ÚôRKéC¥Ù¿QiF¼/—V~×~”uF…SJèµsAÂÿµ]ÛW\ä¤€ê¾ØM¦ô39µ.õ*ÕÆ ­œÍo‚­vƒbºá˜ô<Ý“É#Xµ–¼,ÉßJrؘÒ=¼¿ôøóÇ+èVj5l~«aô`–²Iín“…7‘ËÞ#•ª x ˜¬E6ŠÆuû-Þrø³RÃu?­Ëák}^“Çê•L\†y o¹,¢ê8÷näÜìþbÙïXP¿œ¦hÖ~Åõö+òx~(ÁPiVÌ*Í‚‡£+U¹:j°{}»Òj)þ—w.Yó@@Fîë–º¥ŸfP÷ÅO‘Ô¬ñ’’xy¥õê£Æ:¬›ìG*ͦt·ÊöéõÔPÜàý€¢•fTÜ*ÍÔ¦¬šlzñöÃG['ŸZ‘ÚlLÙ |®S—LœÔ«z¦ø/KÝW̵¡õ:mö—dû€)Ufê•­7òèÙ9ŽªÙ<»ˆ¢uïiÍ<º?î6¥E¶cÔàSC1ƒ E*ÍÒ›J3“&£–4%ëâyÕ}i»mzñ}³M`w»Ð­PžŠh @€`€h @€`€h @€`€h @€` Hc±X…½ ÅŸ@ (ìM(VPhÿ(ù Zp´(šŠÍ±Á·¸¼¢©:>¿…¶èCÉÈhù k]Ø›™˜˜xö&@ñ„ À4Ð @0€ [IÀªØ U’!@@!Øì[säê%÷—Cq‚ …NýL(¸=]>A*¡Ô׌>v¦ GH­°7 €‘ä ZàNgýˆ´h](ÕÖÌüg>®Msh}ÕÒÌ~ ð@€€¢„¥AÍFSt5|9Ü|\L]wÓáçd¡T°OôbŽ(=WŸA]êRyÕ‚}.(T8F@QÂÒ¡^« {# 8áSÀ9âþ•§Š ýÛy<¹éý•ç€Bƒ PbðÞRkzÛˆÎ^"}vÚÜdÚî@#hͪ£H—çÒîýôú³è/Ú¨ÅTÞ‡ÔR[ìñ#h@iŠEkœiµÝ#šiÙš&n£Ú¥Rˆ¢ƒchÏQŠJ&ƒÔ{%™/¡qhÕ7ª§M”BVÓÆuä÷QôÍcÝ‚,§Æåé·Ö€|4á€Ìò,xÄ#ÿ­´q ù ï’Vj5•†ö&u¶¤uP@êR”I±%]9Kê¹Åò]K[6г·ÂàMZ6Ôp8Iz ylIÍQ¹ L}¢)ú¢Õ ‹}ÝÚ7’ö§h.iV ®ËÈðïï>(Ð%Ç’z6$kt3”:šKf¦ÑÙפ7˜ªjÒ•N4ý9Œ¦%-H-žî,£CÿÑMZÜ‘„y›¥H¢¨Ó4å) ÜIæz’&¢IƒèôIÒåÑ‘¶äéCŽiF3JzF[;’†ðФÀ|8Іo&»A4»))„ѱÿÑ´ê”@­Ì u§@‘—GÁcÑó©¢lÙ“æ®"C>=ÝAÿ£´e )Ò¼{´®]Ó¢¥GÈ̈Ts)ŠeÈg8ÞFeûÐü d@ôb7­G?Òþ•¤ÎÊuKŽÒ‚§t{ y>¦Ñר¦™©ÐÑ–´Æ‡jϤžMHðŽO¤«á…½7@> JÕOj×éÈj;Qòé9L!D}†’Z½PÕÞ´p%qDªéHϬèæ&úÕ‘´ÒÖÁ A÷¨EiÑ}£ÁÔs=m¸MïãI-vûf7Z¾”´XDÉ©,uìD¤&:±Ç ¦›I£3­ÝLÚ©çœë–§-èÐ!j:‰8…°/à“SÁÓŒ eËI½mÙCz©õ*ŽIû-žC>ÉUŸÌHWYTËUµæ¾É±(6lMžÛH© ­ßA†©…Ò±!)½¢kèÊjgšë–¤cU2ÓÍ4­Jt)ÉWò‰X87õÑ€ªW¥®U+Å4@I¢åJMèä&ú0Ь…©"‰.î'ªHm+)ÑS4DjaŽ>™¨Ñë(Šç‘VzÈ­J5J¥ÝW"sC¢7ô‹GQè+Qn©Y!•^3r3 Óñ¢ûÑwè-Q­’È"Ú’æt5m+~A¾d(&r(xÑ7é•°àõ"dJNû{ÍnD÷éš?¹ºü¾š\Šâ—­ô^šAziW$çî´â Ýxž sڒߟè}nI—ŒO„RyjhA{¾æg/@ P¢hP—þtr1ò£ 5(ù]xGvëÉ,ut‚ø@Ú5›®Ü Ð/R¡6ó@­l-RaKM²$ Ä~ý[ÚHjQ²3§Ó¯Dw‰þ5F PøS9IîêÚˆþ ý$uFOQ!”¬JÆ¥àéÙŠJT¢Uv&vÞ‹çVulSÿšé¼õ÷Ô¡6JYˆVÎèªyš©½c#¿IÍJ¦wLVE4@IáãIµÚLþ¡Ôp·¤1hòOÑ¿ºFiÑA@¯Ö“‰ÆãËô«“6QÀIŠo!é%õý ]ù"é2¥S—ʽØB‘]È8õk'Þ‡zÖ'ÅIt|)¾‡àÏi;SEaÑZO¡ÝÉ<í:)o7ÐþPêó?²L½š‰¨b„/i•”KQ<<ˆÊ“hD¼¯]$ýh…‘÷ÆQCçÆUo˜~MÒ#zy˜bÛIšAÇ?¥›èDP<àÀPòè4¢Ætf6‘ u©-™iÒ„tÐ¥‰Tk1•áѳ}tü3µ±¤3¯éò5jR—JçºNÕÔ©<íØB3MȽ>Å?£žTÞ„|ˆþª`M†Ñð4¤õí@ ŸéÔ"ú¦A³G à :ŸãXÒÄ)Ô nOCQz{–6¯'v'&ÎÓ d¤CôŠm§šæT³YŽEQÍŒ&Œ¦!khäØt¹ôlmö§ óÈÕ˜ñ†©8R·*´áM,M]‰û–Ž{R)#úú“Ù™l(’ J Mêҗά ÓþT>í|˜Z}Z±œ.$F¢¾†5þ£•ëHõ=Dk[QÜCfžë:UiàEJLÇgÓ-"#gx‚G‘oLê_ÙTs5m´¤µ«iáqѤe3ÑxyMr_'@žXä°ö”£ ž¢ËyÂ’lI. høÉPtŸhmÐÅÞt|8]p¡ýMs+ŠN+i«mXK3v‹b®ž=õØECz“Êl˜2õ=OÜath5Mó$ [꾕*®£ñˆ‹­ä¨+¯^ØóŠ¿É´iÌØÄÿy£×ÜÉ×l=ØÈ˜òîñîEW÷l{«?kÖáÙ&|]v€Ü!@”HìÔÏ~×ÿ(ã Ä&‡ thBæåйSÛ²øÝn’oÚ}%Ks‰Æ¤ÿKâDÁZEœc©ÆdÚ39»)M»Ó×lJ{/@ K?‚',ºvƒhÝ ×`Ø‘wÌ4'§¢Hª6–¶ŽýÃ-ipUj«„++MƒOÒ`饛eZ2QÒwÚÑ<ùÅÿ´9?…Ïçòy©»]±¬SÿYñ7·½ýUxÛ ÄqJ+ãqs«ìçg@@¦ù‚Ÿ´w=Ö¢Ù«œô$Ãè>¨@ì:¢Zõ?Z?@º|Î"<rÅÑr;º"ÛŸÙ ‹"ˆ=»sìõF—[(Ëy; nÍðïB€(I¾=t2¬õxœ)ÝúFSý…I7ýp.<Àçï>— £èîn'½µ/z“J4]^ä4?ðñ‚Y¤ÏÎçú¡„Ëwá,¢÷á±²ËÏ)_½=GÔo$ÿç+¸5Ã?  äˆÒ³‰Î;JË äÙžË×|w}šÞZtϸæãíç4¦ÿ³w`Qe}ÀÿS0t#!H * (6vºv'®…½¶»ºv‹º«ð‰ÝXˆ±ê*6*¨ ‚Ò0L|3(5+1̼¿‡ïœ[çãwΜ{îìŸÝ>(³<‰³T^´åQŽWøe¼ÞqlÓÊ‹ÁL|"f÷¶ú>JaÂåS“k>þ÷q²£]½{ËIëÛ¸0_øß·íŸ÷ÌwÀyu‹×N4eÅ„L9zìVºš†(!Vd٭ݤ%õm53ÿ*é}Öþ¶?QÝT“b>%Uq›à×£U6e|½º`ׯý±L-fê§d•–}7,S;1!ï–Uˆx¯îl{,(œ¡Ëå¥8 ÛòKçªñ'§îÞ¼áI|¯¿G}Ú·üRH¬ûÖÐÎvhµVLÐÊ£€¦/Yïâ%V>¬d¶J«^´åTyp¬†v÷Ž}ì“wÛÒmæ¡a¦ê¼¨  9žG¼ž‰ößh?tK§ëû¶D ½k¤£…(éõ†f«öšöÙuÏÃZ‘:ÏyÛgÂ'J‡/~ gxž™7¨[øñÎdkßkÖ?â¶wÇôµìyÏ~kcÊ~¾7Xß Nž-‹‰âŸ­lê{¶Ϟ'Í«p¾u›;§5™‡÷ï¼Üóå±%»®\<äR×}\ÓÏkxy«ŠZ®ߟ}µñÈÇ£Çâ¾Ø×¼~ƺŠôï“ñâUïO^·®´Rûßm¯ú½>÷^ë·kMÆXå¶?Ügƽý#®e¸ok{¸#§À…^ì|0Ýÿó£×"†0ƒ¡_ðûèjÞµ4Š.€ÿN­²‘¡“A\‹Ž~›v«Ïâà}w[ά‘sQüÙ{ÂÕ[nn NÏâÇ §Q“MÎÎ>uøq½±µÅo­œÓz:ÚIÞc™ºfµ-èjħD¾àËÍé*öºê’U˜†µ§oKWeåß¿èëÙÀcï¸ÍþnPEr‡n#¯j*nî¿Ñ³nSé|ûÆcæÖÓcðl>©ø– ŠZ®0ÍÚØ-i¨ûùü9J3âŸ%‰âž&'²L:{×ÐuîX î“ãÔ§š¥Úç€kq²~øçct£:A'Œ­4”äã³oŒõºr=ºéQšJÊÛ¬™¥Úâ;OÂR„¹4?*8’Gzößn®Clc7S5ºz/EX[›)~Ø8ûjƒ%¹Ï¤P²µ–6ênLlÌ4¹IÇÕ,*©´Sþ‡>ðI§šEV„R16Ô¡‡‘áÉ‚¦¹êf\·ðÑó¡BC€®ÔÝj_¨’h¦c[ŽeUÔÛô4´’683Õ5ºÌª±ëàÝkk^Üéïä®Q~µª8Ù½!EøöN¹âª\qFÎHÊ3¸¶0õs:‘®ZŽþÇ 55ñ’)q<É’‚¤ÛE&r¸\fZÄs"É"z]oÛ¦½jñÕíÃB·Óoº|ôÂÉVšy÷)LûÂ#úºÿ—e!\Éa‹xÉ}]]œÜ• tEÀ`Ûëç.c3J¾ó„ìM²ëÿ^¯®æ÷o²˜šÕ èÚǤ×I"wtã($iP`e}Š$&¦CÛœ›»K#S݈KÄOMÿ^$J•—‘ãWgîú¬õÔªèç;¢÷×û÷&ìÜá³yK³ÅS«æÙ'“«'ŽâzƒÍìg›{·ioKáA>!@Ë»Œ×Q‹Eì;ŸPÍþŸs¶ÖYÐ77Þ>xîãÃ!q¹.¬gγid”uÚJ{õîYÏ=á³™Âd>Û¶K­]‹t2˳11±~ó£v“lVÅÂxòFçñ®œÌ³ƒŠŽJ®®"A’øLÄf먜žeïKô韈ßW¼¹!`3„ÉÄ­×ÃvÎ$óªÊ׌]ªo3®¥ðùª¢Å'9P6I:#*øÌ›8«å>ç±+·´PÝðòñÙfDϹù>ŒÝÔâ7ÖÀ·Bã†kqs¯%ˆ9–Ú¸Ž•K·Ž‹×ßqWì<à‰òh¶‰{%æÚ7ž§õµUWÞs­²C€–w«Ê Ö©}®{ýH®b§¶õšqŽú,Þ£Ϧ,zòËÝôãA®ZD©_ÿè}wƒóÅPssŽ(ñvxÏ?ÒÒŧ¯4åßÿó‹ÞÞNVëPeAGëø»ofοô×W­/V·/¨3?6ñI<‘“©c¾ï¶$dÕ!úz%¬cï7cÝÏ04d‹âï¾Öãnë;Áû¬l”õZ‹Rz›ñ UÆ9Úïfò¥ô’´(íÑÕu[âôöíhâd®®ÝˆI·4dh¶îÔ§ú{ÝxÓУ —øQ÷·­ŽQï<¬[ qîêXiPPø…¾Øº³co?~#¹ª¸üó©S;b,–Ðg“(éYd,7³We°rm™¡®¢Û¦M;Ãõ£öÖ?س£›¶Š0#!&‰ õ´Köø@®!@WP*µëÙWaq+qò}{±›_ÄÒ³¶û{¨ >ü#‹Ñz•$y”¡åd³|J†¶ø·Œòœ¬Ú[öê ±u5O¾Ÿû<‰ìõóï]yòí#Rí=ìrA¯ ™ûÊH˜û&JÛlÏqz–ÌÒq¶þ}ÜÛæ+Ÿ.½dîÛŠ£äÙ½;@ýè—²ò|àþX"Ö±‡ÓûºTºw=pÏGñœ#“÷%n;¨«>KϦ‡·SàéÃŽðÒRy‰©jŽ“ÆîœS[Oün¨i=hŠíÍ…{pØÎÅõŒ%}Uì§÷ì|%  ÍŒ“F^m9³C.œ[lVdzů‹ìÿ]x)ŠèËÅU>IݦyÔ5.‰¡²@N!@+vÍΆÚ~ïœKLí¡ª¦É5dÑõÕa[kÔÖHËR©Ý@Ò†,”Q^0&KO›)¹R‚/ÊËTþû÷sW'Xh0¿qÁ‘—%c_‚Ïq§Â‰êVÿÞnÍ´h¤§³2êÚéÄ´Vú^ïüŸ1*l7†Ìš#F@i+—ó¤Œr  úpþ‡Cý.lޤóD¤«Õ§^À8cé“©£ãa÷~aûssÔU¸>ßP²Ÿãp&ósÁåâFx¶pC¼xÝûkïÏO²ŸåÉZþtËMqBO𮽠ZkQÚ«xʈÿxøEÞ»·©áåÈ6o`PÉÿƒF=#VauÏÒmâxr¿ú’åš»‰Ô9Â$ªë0ç3“Ì­}h¼»( h¹Â4km·Xü³)GYíêk:VϽ˜õ¢FÖ²6Á24š`4'ÿ YåĬܹÆÖÎ5ru™å"þÉQPåìû*…ÔÛ¤«ËÝ®E¨ƒø±íÚÆ¶…l @ž!@4@1 @/6dÕ… €KçÃÌf¾ÙÂIÂÄ §WýãÞˆ¦)wýþ¸à¿ý¥Á¼yûæ›)ípƒÙ @ÅÀc†§%ïáù°ì‘H˜!ò…qlê›—riûˤò¬"€ü@€€üXÚ-¬ ÍW¬$c BYq-Î-îL(Fæ¨È  hì’;¸ȳk®%ôòìå¨è#BÁ|•ò£]•÷“CNx âB€€¼2^ï8¶iåÅàG&>³{[eÝm'áò©É5ÿû8YÈѮ޽å¤õm\™”sxÔ_ýßÄ»tšÞ1æ´ß½o3¸vuFmnczëÆ‰½÷o=HÈбì¹cœOw]‰ÒŸßÞüëé+ïT ´qoã㢠&LØæÀ¡ô'¦îÞ¼áI|¯¿G}Ú·üRH¬ûÖÐÎvªåûT@)ÁèøP¡!@@^«¡Ý½cûä*e[ºÍ<4ÌT4Çóˆ×3ÑþímT+yúzyÑÎÏ_ØîÓ{·O¿wyá€uý˜ã×5¾£Åø¯áÛ~Ù{p|Pçv}j #Ö·ÜvÈfÀ¡Ð&fQê­£}ê1t5Ä]Õ¸órÏ—Ç–ìºrñK]÷qM?¯á•ÓáŠF­²‘¡“A\‹Ž~›v«Ïâà}w[ÎtÏ–ôjÓvvâ(lßqè†yït«Y+ža8°WàÅ-QÑ©T5òþåH²šb_IÒ_ƒ¡æàè¬tòÄ›„¦ßn*oßxÌÜzz ¾“Í'5tëy„ ÅÆ6kf©¶øÎ“°¡» 3ÏL&[Mª…BaÖ÷³ ŽKüX ~ÌdФgÏ x|br9¬úA²ëš–ê1üWÐP| U®8$g$ñ‹Õ‰QÅÎ¥UÕÀþ7žzuvÔ}:sõßn/;m\H 4Ÿ 11•ÚæÜ¼ÍÏ…S³}ºÿãF§¶xª¯ÍÈ`ôÚ?sPOâm œ!@@±eD¿ã‘ygµâµ “ï® þ2Ò{עʸ£!TXÐP<¢´GW×m‰ÓØ·£ «Xkf<¿üç†ÝÎlJúEƒ¥¡eÕÄÉ£¡ž zq@… ±!+Ïî%b›q8½¯K¥{×÷|Ï92y_òà¶[Ùôðv <½sØ^Z*/1UÍqÒØsjë1ÅëÆ]üãÔÅH¢Ï—ÖþÆ1ÝöéÊSûï%Þß6]À,7æÓþÇ¿¥î›l´ Áĉ·}ÖÜÜ}+çî:­™·y 3dQà¥(¢/Wù$u›æQ×=;@>!@ÇÀcFoñÏ÷’N6-çôϹH·ucº¼®~ó£›/ø^`¹`p›ƒ¿?öîíäµeQRä®pV× •ÓÚjJÓ±(#úñêzëöÿõ f\«ÖK¼Z/)¡#(EÐeTömu·æ¾»r‰•»i‰/¢Ó[g|á貃¹ˆnP7Nþåä4HˆSï·üZJÓ[Nm]Ñu¢Ê¬°ÇYå´—þ>óoõ žíÓ\!s]€|x±!«.\:f6óíÌL&† X8-¸ê÷F4M¹ë÷Çÿí/ æÍÛ7ß W¬@IC€€¼iU<]`+òO–3‰7r凃ÛÚU²qŠÉ.o5ûúÌ®îêŒÂ¶ŠÇ OKÞÃóaÙ%"‘0C(ä "âØÔ6/åÒö—IåYEP`ÐP@Z••_®\E¯ÑÜ‘æJʳ‹zm;?ÂÒnq`eh¾â’ßE”pjÇÄ‹-7¯¨¢ZbÛTZ †ò¼#á,§ @A1Ø?Ó„q‘g×\KèåÙ«ú—“soG7iY""_¥ü´<ÚUy?9(h¨ð2^ï8¶iåÅàG&>³{[e˜pùÔäšÿ}œ,ähWïÞrÒú6®YÃ#¦?ôYûÛþDuSMŠù”TÅm‚_VUÄôç·7ÿzúÊ;mAÜÛø¸¨ƒ |#v Ü·íŸ÷ÌwÀyu‹×N4E×j%† ÇjhwïØGÁ>¹JÙ–n3 3UçEÍñ<âõL´ÿF{I á—p†ç™yƒj°…ïL¶ö]0±fý#ŽZÉë[n;d3àPh3Ž(õÖÑ>õ‚˜FºZƦC·tº¾oKÔðѻ֠  @€‚R«ld¨Åd×¢c§ß¦Ýê³8xßÝ–3ÝUĻƴžŽv’·@¦®Ym ºñ)‘Oª/î_Ž$«)ö•$ã*2ÔõƒNžx“0ÃÔ ¼ä tÉ03;YÞU™ØfÍ,Õßy–"tWaÛ¸±uÖƒÅ$ %ÓL¦xR.Ìê+ðøÄärXè· ¹ @—\Q ïª\¢Œ$¾ä„-Hz°-0àPd"‡Ëe¦E<'r,£bçÒªjàNÿO½:;j‹>¹úo·‰—64ä‚ J@˜˜J ms.“Q›6Œ˜2䯬1õ¹ŒŒ¿:s×g.¤f1útÿÇN-lñT_›‘Á6èµæ ž:Ìò­:ÈhŤ´ƒ%¹2¡øRòɈ ~Ç#ó&Îj ÊxøVhܰq-nÞÿ%Âä»k‚¿ŒôÞµ¨2ÙÙ 8AŠs$†YÈ$J{tuÝ–8ý};Ú°ˆ:V~áŸ/¶îìØÛÆU–,—ñüòŸ>|t;³A`¨&^, -«&N õTX\] º“ni&È`¨«°Êù  !@@…“²ò|àþX"Ö±‡ÓûºTºw=pÏGñœ#“÷%n;°•Mo§ÀÓ;‡ᥥòSÕ'Ý9§¶ž¤7†Šýôž¯´™ cÒÈ«m#gvÈ…s‹§Ìÿ½Ñĉ·}ÖÜÜ}+çÎ:­™·y‚õ )¶7îñÔ tØ{ŦºÆxP^øÿÇÀcFoñÏ÷’N6-çôϹH·ucºÉX›mU^Xýyß fÞS^”y&œÕ5hå´¶šÒ~Ï¢ŒèÇ«ë­Ûÿ׃˜q­L YP²‡4TT£²{ìlÍÝaé?—[²­ü›if_58º©ø÷ZŠ®ÆÌ³üV¥ì"™  BÊ™bKdZ³ímÇQO?ð«Y³¥åëöíðõÊã̧¶¬×… Føõõ¡iǃB?b:,ºÔ§ŽFyW**h¨xò¤Xñt­ËÅ*gV¼lí‡Ç¿ÚÐBÉC ›jó9޼<³«ûÖio21ß/Þ-µ{#ðbCV] ¸t>ÌlæÛ™=,¤_ C,œ\õ{#š¦Üõûã‚ÿö—óæí›oVÀ8*ÂÄKÃ7ø™xíÿWïdÇýñüÒ©'(h¨xò§XY¹¶8å*zæn ›ùà·¢mòø¦K:I«xÌð´ä=<–£P$f…|¡@D›ºÃæ¥\Úþ2IÆ„í>šlqÈL£ÖÉ×3ÍÏðß!@@É+“fi–v‹+Cóï» e3"_G Yö\¦x¾ºu¥Ò©( h(]¥Ö,]àÎØ/LMK'á x ”TÎF⊸nj×;ŽmZy1ø‘‰OÄìÞVYC¥$\>5¹æã' 9ÚÕ»·œ´¾«QzØš[¶ßI Œ†ÿÑK—¥ÛqàºeÖ\"ALxÀ”£Çn¥«iˆbE–ÝÚMZRßVSRËBfÒC€PR%Õ\¬X,ÞiIÅhŽÕÐîÞ±‚}r•²-ÝffªÎ‹ šãyÄë™hÿöŽ“-wvlêô×ÌUm¹YUIz½©Ùª½¦}vÝó°VcdD†ÎsÞ6ä™ðÀ‰†•ÒdÎ2Avh(}eÑy#“Ze#C-&ƒ¸;ý6íVŸÅÁûî¶œéžXQüÙ{ÂÕ[nn ŽÈâÇ §Q“MÎÎ>uø±[¿²fÕ[á ðPf??,T‰ïJÙåæ‚°ÍšYª-¾ó$,EX@€æGGòHÏÞòÛ,¶±›©Ý ½ßø¦¬Y)ÂÚÚ8[)=heöÓÃB•üÞ@”onþŽ¡ÊGàŒ$~AÕ¦~N'ÒUSͱ¼ššxù”¸´™³xòqdP¾  b %J8µcâÅ–›WTQ-`.ÀvÅY E^ró7‚ÄÄTbh›s j3fªq‰ø©éß‹D©’GêFjš2g© ù  H *ãÓɹ·£›´,óê€b•·°ÿ•ÜåælQÁïxdÞÄY­ S»rK Õ /¿á‘mæe…ü˜›ïÓÈØÝIÛBCÖ,u|èhÈ£¨ÃB¹0_øß·íŸ÷ÌwÀyu‹×N4U!⽺³yì± p†.——jà0lË/UÅï7¸Wg=xþ+K—Ãÿ’ðUhØiÿ¤_ëçoÊIè³ö·ý‰ê¦šó)©ŠÛ¿­ªàTÅ%J{tuÝ–8ý};Ú° Z€¡ÓºSŸêì]tãMC*\âGÝß¶:F½ó°n58:V²fát„ ycX¨¡[:]ß·%jøè]k²ºpˆâŸ­lê{¶Ϟ'Í«p¾u›;§5™‡÷¯Ëy÷wË?·$·Ü|k¢›Sü¾vwüò]±B¢üZø%œáyfÞ láÇ;“­}L¬Yÿˆ£V™<ȯŒØ•ç÷DZŽÍ8œ>¨UïÂ+ËNŸØóQ<óÈä}ɃÛleÓÃÛ)ðôÎaGxi©¼ÄT5ÇIcwΩ­Ç~ \·øAeÜš¶mn`N³ZÖ«Ä$ «±!“´&T犦#é³À|à°‹ë‹óv!³  ( ªFž¥D_Ï{Çmöwƒ*’ﵦ¾Ï"n%³:ž-~]ÖX[ËjÌ¥)z>GöŽú}o"Žº¹«c%­+û]ʹ÷¶ºöÓ{v¾ÐfF€ŽI#¯¶œÙ!Î-žf0ÿO'cœ¯@.à @™ýÔ°P’ÑèL Y«HÍÞÝç¼»O¾eYÆÕúûMïï—»´¹e‹Ü{'«úóÂêÏûþ¸a¯"@B€¹ ÃE€Ðu~RžfT|*€2ƒ åFÖ7ïø~ —ù²Éÿ"Á‹Ê4ÈoÁIò+<"ã5eÊ“¬®«™ˆÑ˜@ @@9ûaghd&È„WÈ h(…w]EÀ ä 4ȅ³š¢•þî o  Â@S´Bz9„  𢕠þÐ Ÿ  âAS´ÂÃßä4THçN¡áä4T`èÑ¡xð×ù‡ zt(ü ¢@€E€¦èŠ;¨@ @q )º‚Bz€Š ..¬pž ÂA€„>ç@… =:ä>Þ@Å… Š MÑò ¨Ð @ñ¡)Z® =@E‡ JMÑòc@1 @€ASt9§PР\0Î]¹@zE‚ Ê=:ÊžgP0Р¼Ð££´áé…„ J MÑ¥Ï*(*h4E—<¤gP`ÐhŠ.AxËKæçÀ¢ÌÅàg @|‡¦èŸ„g¯|e¾€CCCóÏúVèêêŠ?ÀOB€ÈãÜýghx% Pôè(.–Æ'}öjR/ƒ[Ð鎦H6»æ35Ö)ûg ºâÒôð{/ØüñÙ¥Õ½:,}(-RõXwmïð:•ÔóµoªÖœ}+uFò»+KZ5ÿã©õØÃ'æ¶­QÀrß ÞŸÚt1#s:õÌúÓº 4“Õñ€e6àt\¿ô¸7×V·o±8<³ðsÀ qÝžìíi’k',‹¡g¿ á}zпÁø+öüsbt]K=Õ<Ù™ÿùŸ“ÆÎÚõ Eü"µlÒ³×Toçê–ÆZ쌤¯Ÿ¢žÝ¿uíbPàž«{ÄËZL¹ýl… —*¤riŠFÃ3äÅàød{œ¦ß¥;h®%E%oòIÇ’ƒîÏ$`«þ´p  éîß´y=Œ¦­ÓIň]§ í)X›þÜOƤ&¤›Þ4Ö—FÒü6Ä~O£™®Ä{DÍéÚXš°lÑâMdHôÐ6L¢Ûo(`i0 ­ÉZr—®Œ Õ·iB0ÕÓ' t€†ò]±1Õk´ÿÍï·c.‹$ÉÓCV¼ê\IÆÒŒ´ûÛ·>%“Û–t«©+³=YŠÿöèæk"óf5?]zÄ#~Èúcoûޱ*ìõÂTÕ¯RÝZ›HÅ¡‰Éã+o‰¾6j¯ûáþ•ó®ÆPѵ¨^Õ€C¤cS=_z%Ý_׫ÅÄÓqâ¸í8zÇž?û;jÝ… Oޝ˜8bÑÙB (ã‹ ‘žA&a4¼NíM%ÓÆ£¨ÿFÚt…"RH+š–¯ î´ÕŸô¥'d—V¤IKеäa@–µIO•Hl%} ùÏh¥/iþBë}IGzŠoXº·§½{©y'Z½TZÓÆ¿ÉHÚÀâÒœTžÒÊut~ u­\hM2Èʼn,¤MΕ¨†^™?AY €ºó´>»Ý—¿O¿^5bù Ðu º¬"éæ’±±:ž7ÿAz&ʈ8ä{›êmZ·àö_ˆnmÜ÷jÄôjEé)¡î<3 ××&ó%ž5Ü¿ñ©aUŠþBã¿Û;¸ùÄÓ’î 5|.^YÚDWF»7SÛ¾Û¬e7þwvw‘·->®—kÕZßúÆ£%µU‹±@(›HÏP('rûÖ£B–FDÏ(I@q—è)Qƒ¤É#^öüz}ˆnPpy4Ë»™¸«$~Kªß/+=‹i·£ ÒÞÇm!ÍÃÉðÛדjÔ—VÞ¡ûß´¬šä‘G>g?`nÒ.åK¤ ‚f½9;Æïmº!R<ýxш}ÿ™Z#ï)$ýñºÑ«ß¨6ߺªk¥ŽÁ{¾w{«©_ךMêô1 ØMôh«ÿ“‰‹‹;êNÓw/9V{ö]¥œ;d[³³cl‹ÖIYstüèÃÒÎÔ¶3æËLÏYT-ÜlÕ÷iË¢/æ/¼•ªÓºÈk”µÒëÑNÏðcLmâæ8í2ůé æësÉïë=¨a¾UÞGKÉÓ*“¹¼‰Q»øòBò»²Y®Utm$?GIºD3 ­IoVQŸ%ߎxH^Ž2 ä @+†vã…8Øz»8éŠBg{íèq~´uÎ?.ÿÿ¸ÈyéÆAEh N²kG¸j«EíLXú£[m^öšèÕŽí÷f­«¯^¤ q§ìZv´Ö´[BJ™0pc‹‰Õ‹Ð*À ß>÷h¢dŠÕô·qu~¸/†®ÇŸ|96Ekpà=Ý2mW.8ùVMÑhx†ŸÂ”¶Ô[G£]òÎÒ¬–7=‹1¤ÙW(,`S™ ç}1Ф%?úr4?ÓþôgÊÚ‹ì,‹½€ÿZQ0ôZü±­ÿ‘αâ xiŠ÷¾NÇú›g?&üxÒgZÏvšïxû"4!§>Øá¡Ùq}+#ñ)ë0d”ý²YOˆÞïÞrsI}"^±¡ê0á+Ž8N¾Î'þõ)V·¾2½æ.ôã½8à—yy5ÕÒÚ´C½± \:´ûñbiá~³gû:yýäQÄïu¸¿KË+O }¶ªnvQÊË ßÕ›÷^½ñErý$[×Ê©qû>c&{µ·ÓÈb/îò²¤¿=»aÑò'®>Š‘ “ÂÖ2±®j_«ÙÈKûZã»Hå…ô rD¿º$ܦiS­FT”qìõªJ~GEæhœRì[â©‘nu鬨\íÖ_¤CmTª"Ùx±^ªöÔ¾8+” hÅÁ4l¿Â·ÇÉž‡â‰’OyO9ÙzwWé r¢„« Æøj2òÐLW"l(ùÎö=ïôºŽm¢'=·©Øõç:k|(Qì¡ WV5ë WÄh¨Z}œÿê#5½¯ðH:£ßŸínÌ-°oö7ÂÏ7ŸgNZ7¯kP‚7aiWm>`²Öı;ßt_µi$š2µªÙe Há7´å=o‰ãÐ{Æú¥õªheĄ߹bÓæ©'7¯íý÷…C«æ°˜ËË’¶²ÛÔËiÚ Ç,ÙÕÎA_ôåõ kü¿±š´JÒ3ü,F$Ž©7RT_²Ì>/½ÜDQ4è7²’žÚ%}„„YÁº ÉV¼üVŠéE&Ò¤‘rú7!ŽíIÕH2"Þ§^dœÙN£ݒŽÎ­ê”áü'Њ„eÒm톎AO%‹?Îïóš5¢é¶VzŒÔ«¼6½Óéqh‘ǯ”H¼¹õàÇJ¿Œq×Î*`[t÷ö˜88„O‰§ÖŸÿÔî—¼c;ˤb7zçÚCcBÒ‰Ì뻸íŮ…´Ì¦½¹õ:kÒ¼¶YIFGŽiÃNÝø¯^/$hmû–]»åêÍ-J¼>»$ ³ë/»<­VVß‘öÝëæíÚfÓë}ÃÚšT½·º±6ã?-/“ 2`ÌôËid>!èÌÚYCrwíÓ·!¯êà<~(PfŸ`7ˆûòˆeES§Ó°e4ªIæ\zyŠ|7³ÉÇ mºMg/öºSI~ï£06\TÞU€\Æ·ô/ï*@Tûwò·¥M«i©§äF*êVÔl ’5©Pç%tf KAÍ(  Õ[K›­hýZúýøÜHVmé÷UÔZÚG¹î*ÚfM›ÖÓl?I‡ }Gê·“F$Œç´¢aWìûçû ×øDoÖ Ì­²÷ßeÙ¦EIN²éT‚Í䡵rv¶`´ôîªsro< ®l8üf ÷baž ýµùýÓÉD/VöÕùÞºf2ZÂi)Ywná¨qÊ2?GÜvCÒæ¥ÙijVÞN. ƒ&« âÑ­í'ßzMzq—/ŒHz‘(ìÈõØ.í¿÷ZQs=W’@ÙaÐö|mÿ-.Qè·LÉ]Q6Œ”¹#OÚ癫ÄmùO+hQ9O¤mÿcMš^ÈQ+€ò­x8v£¶ÿîWsÚmñ èÙö¥ÏÈnºïøEõXøñü¦3)ÖZiÇ}È}“pë_ÚêîÝÿ•èΦ=ÏG϶/F ¶Å€m[Û÷?ž(ÎôúMëòpKký‚ZÙ**L’v däìó;A\Ø?w£yyϯ,êî®jÿ!x'?9›yé¢y‹ü ªnô’(ü\xòb/_õê­èÌúäß±AúÒÕ ¼ÚU×.ró>@ɪ›Ý¯évîŽ4¥ Ì @+"UûñÍÝá´@šò ošQ·hcÏ‘ :hS0+<¬VÈ\èév¿ÇS–:ç«6–yï-Û]ê}8žèý¶“º†íèXÀúlÝÊzD±â©äOñ¼üó³d|¸°}iÀ­'ááï²OÚVÎ {.ñs±P+~üÄ¿y—–¹SíþGˆ«¥+H{ÿ&^@:TÌå «ÇnÄúIÛ›­'Ñó}Ó;í›®m߮#‡u«kŒë ,‰Sé·z“žù0!âáÓH[s}5-¹Â”/î^ºû4Òûøòƒ©©f¯ ïkäËd+k6ÅH‚ëýµ+6œÔÜÑÎ$³™T˜öùÕ“—ñDé1OŸFÖ©]YW¥à>Ò &KvOl$½—_AT­ûí¼_»õœqSÖ]Þ9´ö®…-ôjßÌÍÞÂXWM”ûîEØ—ÏÝÿ$Ý\%÷Æ6êEh¦fè5И}é2ŸîL®]óp‹š)ÑŸÌ;³«‹C³þ¢ÓþïZÚ}{–›ûã9{Ô·Ò̈ ;÷÷ÒµçŪôó \Xï{Œâ./&H—ŽàAÄOKÏuå¡HtsBÍ:¼Gõlâ`ªžvnDzuÿŠÈ¸ç†?[éæë áÍRêíGûîSt]y‡­PRnNqóXõ85gÙëµ]k­•Lµ÷¿{b`žNqÇ:Tîv:{…ƒ#ÿSuÆ8ç¨9ñ\O‹6‡âs½õí×ÀWü¯Ý´›gjÏuô%kÆ9/7K/"ƒNûëUP?i†VƒÓNºÿùLÖ‘0´¬½Ô{îƒÓ{ö ºôï®Å»—}Í<ŽÁÑ4²°«nßz|÷FÍš·lÞ ºA›ÁÙ–ƒwx2hÂÆÈô7n]šu6º¾NfFUµ¸ë‘{ßM+7í \>bW¢¤“GÏک˸é^“F·³UÏe‹¼|üÅñ­ý/4*{Ìêû¿Ú©øÚ5›tèø¬Úu§¯™VùÌ•›·¶M;¶RÚ ®jXÍ­÷ŒeS}¸ê£( != ¤b}¯P~ Šz½•RVg ý®)EH«õÁ¯…-x:®qöËЬ¿,\´ìKq jw/þ)Ζ ß E·•»Éz†U;ùléäSÔ­myænFn5׬µ÷²ÖÞEÝ#@ÑÅ^ µóèòuJ©QµV4äOj]ƒ„¯¨¿-}L§vRV71>ís¥åohk$¹dÒÏÑÛ½´z&Ý~-þhO5{‘ÏZrÔ+tË/i½lI§ÎÒ÷Û ðè¯Ú´9šÖ½¦ê· ^Q$í4òHºxUât ó§HC@aÛhó: }B"íÔqy $ & £i¸)E§N´â7zøA|®¥æ³è·±tã7Ú¸Þ%“š- ØB#[ÆÓ‡R€  p2ž’w+zU‡¼¨ª%„Óáù4«©…Sâm!ýM £Ž³h  ½=Në·“×':|’ôžËÜr+êßœæÓ¥(ò´Ì®És:Nú£È1š¼d­hD‹®Ó†ö¬Mî' cRÑý4|Yõ§…kÈHHwÿ¦ÍCèa4mNޤ#\ì1ZIΑ ƒ¦¿§Sì â4¦eIó=­éBÛz“K¹ï®E  pþ¥gDí×Péã–Ô¸ýï©fu ¢HêN«It §´(ˆ‚^RÇB¶Ì¢Æ“Iý"íßO]¦fEŒ·ûè-Ñ /Ê(dE6YÖ&=U"u²u–ôæ¿ å+H£;mõ'}i²K+Ò‰¤¥ èÚj&Ý€0žÆýM õ%ÓçÒ®.ô Ž/"3ñŽ­iä0 YA×_‘›S <Ÿ¹!@(5+Ò!º4Ÿ‚×R“ÚÄa·&¬)™%xU´M8Rs›ìiÕõ$ºJÿ>¦ž²·,¦íAmŒéèzíMvâ@œNgˆì©KMRK(lÅ<â.ÑS¢H“G¼ìÂz}ˆnPp5s”>®I5õ²fq I“ˆÛ’ ³ƒVÉï/‰E}ÆŠ@á¨7¡‹iæšæ$NµT§5éEÝŒÖ +x¦Š-ù„P®û¨¶¥ÐìI–íÎŽ¨[ûoËß—’ŒÞCHå{ÑV4ò¤}ž¹JFÒ†‘,É0 í¹C4·>É]b2†BÇü ’ÿ4”(~­YKêÝ©uyW T @@ ù|“<¤“óéь夗 E€ J‡Á(¥Qd…_®=:ü.ôâÇdk·ù¾ö†Òkªo¯wûsƒfÓfXj ïîòïñ§aŸt»ûwjiŽxŠDD¡SiÎÒp IÈÓ¶¼ëPZ @)ä Í"‘ˆJ%F3õÖúÅÉ0áÆ¹»9Jñ_£SDI ©BÒfk9÷sÑxõ2ìbIï ü1¨ÝejWÞµ(}Р°ò‡ær¡âXÖöꩆú•pÆP8+²Rûž~V9†9…''¡9KÛÚ ß­ŠL&þ‡THÐ .TNäàŠ6%MCs6Á»—G·>½q56µŠË¼­ŽFßOºé/÷^ºy=*2VHªêVí»Œq¨¦'¹[ìÙk¾^½OfÙOjìøúåõKï£â„*æ–æ4j^#=TQÚËW?¾ó$Tˆ—ÄgêèÔÓ¼ocnʽ‡¶>¾Î#ŽN­nuzލœzüÖ‘C/ÂyFõ{ºôøÅX=_§ëŒ¨ˆãëîß~Åg1éŽq³c½MÕ%»%Þ{rlç³Çïø,†0Ôl[9vhm¢VvÏ€|B€€ IžCsN,sÛ34{Ÿ Í;GÈ®Zc`ŸzÌôw—îøÞ^÷4uòWu¦A›Æ8I3üð ѵiž‰÷íùûíáe†5}+±)íéýÕãÄ7i8q· —ˆŸ¶æLྈšNµ‡®27ô:u&ŠmÛÁ€Íµ°ÐLúeY‹f•YÔ/ýóqŸ+×mÏÞk£Ç¥=¾·v{J†øédˆRîü»Üç™jïÖ3Wšj±D)Oo›|eé“ôYËkã­”΂PaT”Ð\4\ gc3S&‘ºN¯FÃ>|Z}üɉk5'´Rûv†«Õo,½ …½F|È‘ƒ¯?|Lw¬D‰7Ö=xÇ2<ÞV’žÅØêU[›éFPÖŠ\ƒ63ïŒ ;¾üY–kî¤õmÕ¤ÀôL$øýä#üb¬#y7`pk8ô̓ԀŸøÏÆgqVãú‹Ó³d–z û^½Ÿ-ñ¿{"Ôf˜» ¾Fe† rM±B³,œÊ¦jÇ_E^çµRSÍ?Ÿ©¢­N$ð…$ü}+œÈÖÒF÷û3£Z§þ¨:ßW­^kÐ/oVÝ<ó%Ç¢î„æù;ndoX]]‹IÏÿw3ت~3g SÕ²ŽdÿÂ/1÷^U75ýÞaƒiàl¬îÿòùÕ¯îÆ*26   @î(GhÎ…¥­¡Aô96/Ž¿….)øŸ ~Š´5Ô nS–bp¬5ò¸rŸßi¢©†ìÁ¦z–=F˜¬ÙuÔ'꘾¡K‡­±®¬ÉÄ'&Šçj¨©æX—©©.Žñ±ïÓÄ•D€e† rA CsN"H(ɽÌßX…!¼ã‡O‘@(RGì„3^ÕÿÃNOVÚf°+÷n½¸áûÐ3¯nž‹¸½ëêíóŸ&øÖÇý— åé[nVÂМ“àKB‘޵úOÊl]m-¢¸Øøx>©ÉZZ˜öpÓÍOžm½Âƒ·ÝÚÉld ™½8$]Ÿ-Ì0o<ÔíñÆS›Ž=;sÇiœƒ–d/É©éBRË^S˜˜œL¤i!s·J§A(kòÐØ,g£¤óß_ýÈ#®[Îeè;[Ó›ˆ7_Ùw©Và9\ôåüõ“¼Úã:j7uwù'äÎÚ[›ÖÑË:daü§›§?rêUs±áˆ>¼<~Û¨s'mI 5‹[µ­¥î±'¼t!C·’d/‘ïß§TÕÕÌ\Oðùî§TR©ÛøÇ•PlРx„tÜ>¢ÑäOƒ-èÍ/´ŸÔ¥3yÏiIO:õ€H›6D‘Kt®‡îZå\wÅ%¡9gMÄu(ï ý4ˆ±×ïî>™¦Þ´I{‡"œ“ÙÚ Ç׸:åéÙ)çùcëÔ¯££©Jü䔨u[GI¿h~äÓ]Õº­¨¢Ã$Òµðô®öû›=¾ïm§™kJÚ’ùov\Üu"‚„æÛkñSžìy`áÒÀÅŒÅfD]}ÿ•¡ßÜ^•Áæ6[í²Ï³#»£-‡›h²D©áO÷ïOä8ÕïäŒ!8@Ù!@ƒBchRÛ çüý•þp$.»Î¦^ ©šZÞ‡P¢ä'4#®RiÖDøåú£óÁïž§E>;¸:Õ±KM‡èǧμ{”L”¾kqRížÎ-UL;9yÞÞœ~è|²€Ï¶žÃcûTÖ•´‹âÿ}pxo¬xêmÀµÃ©.]ZPèö;!ÏÅqg×Ü猭íèä6uΉ¿ŸÜXu.X(£Ã †Yýþ®¶Â´ðýwÏyþRÃÂäJBµŽ:¬ø˜›¡<¢¤s—Ö§Uo3Ú¹®)K¯Ž‰Î‰7ª.†ÚLbhéÛ[¼>2öÀ® G!Ô«Ô~q=Ê’ ­îRê ­c;¯/é+Ta Ó…jÖÝšLhm„÷ Pz8‚BcèÒ€µ¹J’?H~ÿ2™ZèK&Âr?„Ÿ&‡¡9Ó·†çÒÜ S¯A­_Ä?³s”UsØÔ9ÏrZNö=ìel„¡S¿ÎPñOŽ"÷1îcr-¤Y³zß•Õûæ[¹zŸâŸïuLÚLo×fz®et›7]ÒüÛónKͻɨ‰¶sÍÎ5eÔ@y!@ÃO¼¤Avô²%:Kß®SâÑ_µis4­{M uI”Lç’_…¿“ÌÔ©AígÐØA¤Î a4 7¥hoZ׈ÖÎ¥Ï$ Xu¢©ÛɽRÖÆ„±´çWò?@±<2t£«ÈrM ¢5Ÿ©± ?Ó®ñ´ëÅñI«õ^NFÙµF}ïÂÁ•N?‘–O7Èu’‡ì¬­AñÉmhþ¦”žó×Ò?sbã…AÊP l á§±¬¨sšL—¢ÈÓ2«0ã9 'ýQä¤+ŽØt¾?Í:Fµ'вö¤žBW—ÓÞ!ôQ‹–zƒC,¢Øã4ý.ØAs-)ê(ùx“ÏH:~”ôĉ<ƒöw¡Õ×Èe*ÍnKé÷h›'iŠ×á[:÷@WZwÜçPÿÖ$zM‡¦Ò…Ô“Y‰–Ü¥+#hõmšLõ´HȤ»£²ê“:@ƒü‡æLeÒðœ‹8e~Ë—Ê0  „ á籨ñdR¿Hû÷S—©Y¯©·ûè-Ñ /é¥{©ôJDNé÷Ud,¶ž ݳ¦K[(É“2S«0šF^§ö¦’iãQÔ#mºB)¤§)IÌ~×H«­ø“´Åa¨Õµ!Ï$Þ´øQúý¬¹¿/”ÎmJ®NÔ»e]3˜‡,ÈBÚÆ\Ù‰jèI&>ç~…ª(¡ù›2nx¦|ÉR<]`«­”Ëz”4”mjcLG·Ðko²S§Z:@dO]2{OjÒèc4:Çò,2S§ðXJdhr"·ì¤B–FDÏ(I yû/}"jÐGš¥ôÛR C:ž"™Žû—>Šy¯ïsUªQó*äÿ©tYiT¸Ðœ©ìž3åO–²²fE/PfÐP"4©×0:º”Ž= )nÄ{JA¯Èa#Ydßî7å íœOçC(ê# ¿­•#Ü0µ‰›ó–ÁŒïsÞH~›çØ—,éøSéÜ×’ß&F9ærÈÜ€ú§Tèû›”}Ã3(h(!6èêR:½™Æ¸Qd½çWwʼ{0ÿMv¡P6õ^J-ë‘®Q2­hB7‹¸iiÊ3d/3ûa9IDa¾Bø± ÚØœSy5<@Ùpe0Bñä4”¶ õmB PØïtu©u F&Y³Þ Ð4jx€¦ö¤Ì„&Œ¦´ô¢nYÓLòûã—Eéô42kRËBò;æs޹Çñ%¿õŒ)»S=ÝHH2:‡°qÇÀ•tˆ¥”öYW~9Oç?f]&hPô‰®YÝ SîÒ¥è‚."„,Šš3¡á”P¹¼àË÷ÿZ…îZ JŽnKjeH'æ™Q/÷ïåf­Io Jõ—’¹€îí¢C勉§sÁÔºú6«æF=ªÑß[iŽõlB)÷èïÕTÍŒB¿JærÿßÞÀ·|ÿ'½ïêEkC1çªsÛŒÍ5fnæØæÆögŒæfsÎæÜå˜Í¹aÌ5c˜ŸÃÜ÷U¥Õê‘&ù'mUéùmÓ&i^χé7ù&ß|óM¾¯~|“ÔÎÏȼ24P:šS1ä« _yÌuÜÈcõÌ 4ÌŽ€†É„ÖÐ< Ð,Ï€Y¤ÿ­•qh [4ócà0‹ÌžzÉ Í³È  ÀœxÌ%ëDæY d€`6 qæÂ³È €XÈÀóÀÆKÍ»€YPÏ@Ð š…ì¼ ~,äר8 yV€Ppl¶ S?ÓÀ\÷f‚Øð09@±Í†³œ#žlœm>|B@Èw¶™néÓÙŒk õ“}mó±õ ˜ ÙàžÛrF3CFÛ|ù€_l°Ò²Hg³¯‡ô_/GWÙe ?Ðò…­í¶-Ô9C6øKŽM±µ§!P`h&fkM–“t¶µ‘~:™­=d6‚zò À”lpŸY•Z2º0±Ág"Ph¦aËù•uC[Ô:É6÷ /kgËÏD ÀÐL€ê*4ãÐ6þ8Z;ž‰@Á  ä Ã]©2lh \3Y´¾.-rŽz  ÷Øa§•¼6¬wš‡Òªñd  7x~Lj¾¤mh‹]?éC?yŠÅ.0²Æc0º«iµƒa°dì­óØ ±ºqè´éÏ#kuxÔ€‚G@f¼¤ÂäxN/Ã|±üU”ZùéÓßò©x¼³  ä»êô¬}d˜þÖ~§l`.4€ì1ðœ^!X'™-< mù ÁæX5@6h©ô ý:¡¡- `v4€L1Ê•!ÉÚ2Ù΃’í;q-ÿãnPˆÐ2f;ûiEljµÐЖƦŽäÍïàÁƒž›<½fÍš¶³B`Qhcà936•/ÉhhËÁXÀ#ØIgÆf× m ló!Èzšág˜ ÏY°Í|IEC›—-¯ü¬0€‘-ÅÊÚLøµV2ih†Ÿa^4`ëØCgjLEC0Övf¨g˜ Ø4öÐYcý<††.0¬ç´8–†€lÏÙ¢``.l{é¥64Ãϰ4`‹Ø=g‹U”¡ó«°|4`[xÎ &k4tþaÅf –ƒ€léœCì¡s‚”ɬÒl±~`!èB.¹™`Q̲`ÇœC¬¨œ£¡M‹• Xºð;È+²%©Yà¿Ò0ðœsŒR4´Ið$¬ fÄMα®r‡†Î£B¿öøPËT¸·º@@…cZ9ǺÊ#:×ld½]½Zøï£u â·š¼" BÈFöÊ&Áº2 :Xc€õ" B…ÁTE(¢¡a]V€ vÉŠ°ºLކÎ!Ö`íh 0`àY) &ŸÐÐÙbý… X=öÇJ±Æò Ö P8аR:ÙP[Æ“¹×¥–‡¹ÆlxÎ ¦ÐÐéñl °VJ.°Ò «(dhÀú0”•;DL£¡“±€Â‡€FŽiÏH²r¦±lÚ&¾êS䫪2ÿºÌ9/udûxY²\N^1žãUAš=Ä5éÛu×¥W \$sêɧcäÏÿŒK½,C¿”ÚE“.p[¾W–®–Û â*ÝgJ‰eð™.õ½D.ß”o×JD¢xTNÓÄ?í²•×ÊÈÍž²i±¸$OJ”•5eÚYtIªE'Ýô@™^M¦–®N”ÑäÏÑ2÷ ¹#.e¤ÛéÓD ÷L{^ºKx_ù¤º|:Qþ1Üg©ÒE†Í’J^°¦³Þݲ3ÎÖ›YÐÐ6~÷Š€FŽÙ•’×ɘ_å·ËÒ¶DÊDÍ)ÙtR|úJ5ù¥Œ\/Uß‘›‹ë}Ù;MV¼.7=dj[c•ªÄNäö~Hz#cJÈåu2l ë#ÖI­¬j-³öI¡òaS‰?,_´wà bo˜Y#«_‘9û¤ö(yíEÑŸ—µCeǵœ.yÊM¯— —äíRZ%?ô”¯‡ËíŸÄ¡¾|ü¸_•Ù­å‹NR㜄z/oxfD¬”‘ÿÊ;+eR œ[-cFH¿›²f½ØåÓ NYؤÑå wº <çcF¶ÜÐ6{ÇB€FÎÙIý!âºSV­’ÖCS¶‹+å¢H~â+gõR­»Lž™’˜ÏÖÃÁòÛ‰n+ž®Cw]úü!ͧúÊkseÞ9w_\Ë’}âÑY¦"ž†Ll"!¥¥m;W1ü$åÜÉã“Îm(5«I§gŒçæœ.RÞþZêúOw#ß¶–¿ãeà 2Ü“`éó¦ìš.œ•Ðjbè}c©FË[ßI³'—&î‘÷7Éæ3Ò³œ‰Ög’9õtÚ]/{â\cÕ™m6´ ÞeÀvÐPÂóyy)@Ö-󃤬“!leër‘ŠÒº²ˆ£¼µ^ÞJsa;_ r•“·å¾V¥§Wëžt°ÄÍ;i&ÅˉK)'=’D¾žæÜ9{=ͪ¤M›ÔZ¹ã{•‘Äk¥·ƒÐÉ£à>Åócø™z69:Æbâ†.¬÷ @z4”²“†CÄåUY±PŽ^–FKÄ+)þ’Ž.ð`ÜV/'æÊßb Y]ö(¾5ÅKäØ:¹ß<åw~‘_n¦¼MÐ÷Yñùw¥D½’rôýCòÛõ‡o"T»i>sQâôâ’tÍyÙuFÄ;÷ôˆì½(‚“Nk%ìã¿u*çá 3–m=³WVŠ5fá eC¾{ 4”ón,Müä§±"AÒ±vÊÄ ¥È$Ù6TjM•âZ9ü­¬½"­JÉO'eû¯òb] Ìò:]B¥]9ùz‘Œ ’ö äþaùz–” ’ƒwç:×ÎÏȼ24P:˜%îå¥ËRñs²E“wZÒ~¥\é%ë^“µ"ž•¤óLy~Ž Ù%ÚÜîÕ\êÊ'¯Ê¼±2÷”èå™>2b†øÔû¡c] MCŽ{@¹¢NÚr:½.Ž'IÕ÷dÅ{^®—üÜëáO_¦ÛǼð›|pÚ±”¼»MÞM=/Q¾‹1†µsÒQÈvÒwôM;sÓ‡óg/#ÃvɰL.ðØM;×’­N)Ö_ödŠN/¥»Ë¼î/³éd;üÌ^9çˆkTÚÚ—@îÐP,äAö…5 Îxú£»“M×ß“eƒ$ÌSÆÎñIyß^Xo{QבâÎ&¸~EÓµWsrù<ʰžÙçc½¬º¡­wÉä %„ø× ü„ì—G ¥›Ú”†ÊÌÛéDñ¿-¿/ ñù,lëFqŽíSB&›ô‘øªMqý O÷¬/“Gië™p1ÖΚ£íG@#çôÆzã8ñàÕÒ¶Œ¡&3ÍÓôö¾2òeã©bφ}µ9¤WùФן“éÚ«!vA"ëÄM•þò&ihö»¦buá… YWC[Ñ¢È'4r.ƒv̬&M6ýÍ|¾þ §«•^Ì‚Ž)L¬¥¡­b!ä7€U¢c Ëoh _t [rC]DÓyì=—®û~Ïé,Śnj¨—xôk§_ósÁ74+CÇb;m™KÀ\hÖ„Ž)ô,°¡-my`­rôͳ¨9râЬc#,ª¡-gIð¨Dõïs<§/p:«WÅ«$(¡Ýð{ƒ;'º*5ÁnyWß×Ú%x$´o.ÿÛâx!JŠjZ ‰šø^‚oÊW ˆ>Ö~ÓXÏéK"ìD— /×6z´û•]ìV¿á3z¹}”KüÇËâO.vÙ¸Íáf‚>¸Í½ùKcªz$Ï©:»Âcä8—ã1zuœ*Ñ]ÛzQÄÄuªÌ¦'ßžZnìt»Ôuý‡ñú'[DÏý6ºfê!ÑõžYžÓæ;¼'n®z'ÕÕø‚^£P‚€`è›b ͇=[0zÏ ¿N õ¶ßZÝXg¯UšY¤]W¿¿ÂÃWJtpÔ¾öýíèãNª‚{Fž®s¸c¿a´÷Äá¾ÿņoúHcüz.­úçž¾}7$Žþãf¿êúØ#îíkx½zC·wm\‡e·Ïíÿ»ýþ3±-E½2D½oº×Çë<ß¿mD¢ƒHìÿ<:uu­´ìÖÁnZ*l”ï”+bØLâ2™žÐ:»°Óúý£ZVšç9v•Ç»³âvŽOtLº;»øuýRÕzñí•=] 3D9uóóáh F@°fo)<ó64›œEÓœr³ÀγSÄ;†z6ül§«þnäÛ‹üg|èñ[·;MR‡uÝk7Ô»‹<©í¿äîɲ>«§¹ïxç%_Ñœp¼Zí×ç^ÏêzC¯ºV½? ‘Gßõî[®Äõx2yfmó>±Í“®ªzI» ?{þ÷«}lR@_ÛétEߪ§5œµ¾ÚÈ÷î¯$³é¯°EÒ†TPm\å}p—}´>ÑGe\˜Ñ_Ú94¹;¾{R=à Ð,)c³ÌÕÐlr–.|óI‘gš$º§NrÔÖ««›±Ôqó¿ª&õ2xðTÞ mêëW¯wÜvBõR=ýí?Ïʸ¶Ö9ål}`9ì°;|Qõ  ²óÒzú7NtI?º?¡³ÇYƒÜ+Ì‹©WBoç§©ã—ÕôôÔ.:OCe'¨“¨oìr>#R¥m‚/Ç=[ €E#el\Á74›œåSÝ>e,MÏ¢º´Áéù¤áa³»pC%’ñãçl`»‹áÆ1Þ;ç³zß·Þ¤”sï©\ÜuŽ9öoõÁó¾7yt,éðll×wbúuÑxª3žíݹuÜp!}ÑR:»l/ KA@°\¤ ¤`šMΚ%?p™°>iÙÎîáüì¼ðuué.—MD«Ü4vÜhºÍeå2—5ß»Ìîæò÷·}–à™Ùôì\—h¼EoX€%âý[H«`šz¶z¿rƱä¨ëj­hSGm#/^5t¥‹fúFGµå×à]Âx ‘ÕÑ9äbÔú2ÍîlvÿýYNcžõY¼Ð}ϸˆ–¾™L÷Éæî$-Œý­ËÜX6€Å¡c^~74[5ñmWAÎn·é£Mß·Ûû‡Z¼b[TÊäQ¼o¿ã* ˆo^ÞxŸš OˆÃÉ•NWßM,©° /.õü­Ö½îI×cбvñ§êXêâRŒ¦g… üÄþØwŽ·Þˆ-FA[€e¡c™|jhþ»Ãú8”½?®Ÿk§…žs~½=â½NudŽ×ü3úzs£ëyg4ƒN6ÃëÛkú&‹£C’ÞxèT%fHS×![=_ë-“†ÆU,ª×ܱ»|MÊ5ÐÉæ¨e•æŠãçœ|[ÊEtQö[¶Ú©jÄÔð•̦gË54f`m×±;½úŒÔOxKh¯ºô‡Ó…”7̇€`A¨gdÍä Í&gÔºŸ…¯*ã9­h¢ÞA£ÒMxó»ðÁM¾ëüNˆ¿£N•pO¥ñ×ôYþ^mJøØk;­ ·íùÙ2Ï.K= ›V@Õ„–î½_W½c‚çÜ¿ —pø´·gô”{¯ªœ'pûÛ0!Ì}ÄXý¸Qñ^ÕÊ®ðhèéZD¯¿úÑK>»_ÚAžát±ûi¤ççi®°­£Ó´î }|ÄýÃÑúqcã‚{oº­â9ÿ³"->‡@Mó7âÊúÊ™ýž]»¨,½_Ö©À×0²A@°¤ r„ Í&gÍìuõ‡Þ­?4ËËøÅÎý+òY·ŒÏT¹'¶ŸÑ~ÖãÓ½kø“fBìØµ±cÓ^¢YÔòfQÜZÆÓµ­¦Üi5%픸‘+ ¹Ú'¡÷âðÞ‹ÓLšñrÃ"Ð,)ƒœ3IC³ÉY»À ”­¸vUŸáto•VÁå-|:,  ÀüH(•džf“³v†ÊLíËŒNkìÖw4õ#¡³»¼¥Ÿ†¥! ˜)ƒÜÉKC³ÉYµÇÊÒp:“Q[o—v.é¦gvy‹NF[ €9QÏÈ 6Û”>(ëðyïyG'«Ç?×7jj¿l¯Áº¦Ãìh °QYÛ×YYÝçN¡L=yì¬N¡ÜØ”pÑ \wk ¹—… BW¯¾lîEÀ#‚‚6š{ò ›¥)Ä`9h@P€€.üjr"€éÐ…œÍ¿•ÀÄh@P€€  h@P€€  h@P€€  h@P€€  @> R™{# @~Ñëõæ^Àôh@P€€  âNÅÊl .™ô:¡9}¶S‹ãç_ Ù2§X€îþO ήYr~ûUÑûô/¥6Ç"éN/þ{øÒðcç5zG•N£ò©à÷ê[åµòpã K…™nŸl à1ê —ÊNªëþËŸ?¥™ª‰¸÷_´>âDÌ=8¸¶TÁçßËÛ×›q‘t×~¿y½Þ3[~ (å¦ÒÝÙðáŸúíùãzÃÕ}Ý f¹`¸}°94€q ­ò뎒÷‚¼Ê8˜{QR9º¾ÔÞ¯TÒ€³ÚÕ­õÈ ß®9´oöé¿^«VÛÍ܈‚e‰Û'€Â‹€3*u@Eï€t“íUæ:`¾ÖägCÜíRV»»•ó•}7£ÏGëks‡­±¸í@aF@Èžæüå‰έÜU®âïÛË?äÓ˜¶fûÍnèÄÙ¹F‹à>*]Ï_"~?=râÙ G4âèñâOMY4jé±q‹.í/Ò±o…qý¼#¾;Ògò•ïªêNªÑòìåï~ºuì¦Î58àíŸnsý»ï¯lÞy-NP«ô¬EùgÜ@Ž^Žª¡×FÇ^Õ콜2¾|ÜÙ+SFžZ<Ñ^­‹I´/Ӻʷý½S.«¿õû¹ÉÓ/ì<§µWébÄùÙveF .þ#ÙÖ@áö™üëïý{eæÔ³ëÿŠW9Il”ÖÎÇý¥±¡Ÿ4w²Kwýl9C@ÈžC©'ÆÍq ùãÇÇÏÑ;T žýöÓ>v Ç~úï½ Ç;Šß°¥RÍzO-XPªéž9gÕµº{ÒA­kSô‰ïb'~úfiã;º¼ºU_[$¶bŸˆ“»î6m^|hÃ'ï„]?çæ´ŽíW¢~‹²SºêNýpbÒ†3ƒgþ1ÙÛ% ™xûÞñH‘jO»gtvìÝ)}_©ú΃ŋ;èï…l?%.^/bÌ ýÝ=G[vºà6 ö/«ýüìõ‘‡Î¾ÙîЋi~]Yª4‡X<ÅÛ§‡Dú¯Í˧n´xæÇO>åj(í¸í#ö͸¨Õ‰<Ðl9Ò! ä…c•úE*–44±sÑþÕ^ÜÙfɹ©Ûʬjç¤võ4§ÌO-ÏLr¡ùªbaÃOÄ ¬Õ£ô㟇Pºm™Þ¯8S¤±·ìùeÈ!çW–míc<ëÅšê]›Â~‹¼£õvI?*ø8Ý¥‰S§AOdô¦¹v{×yò­"EY£ò¨VzÚ{ÏäADMÌò1.{}ÿŽ¡ŒçzUžüöÅF3NLý­øÂ&`µ2Ù>[kWŽ—öÊ’&e_ß—þ–}:m?¿¢‰Gòq‡GV¯>åDúK©ªNøçÀ¨ÊN¢½µ÷›Ï¿ûåϰÃGOž½©I:Ó5 l•g_x¥G¿~m«Éþ÷xØ€É8q-"rá†Æ°»3ª²¯1¤Z¯Mû¾Ú§{Šo‘|û4^]Ô™oý¯EȺѾ^™ÜŠÚ¿Øø|_xsbÇ“¼[w ~»_ÐÓI‰‰·c ðtrM3¯½§‹·ÈÅ †ûB©Ûçýð蛆GÜÇÙ+û@¶œ<ÐEìÒéÕQ¿„‹]©FÝûN¨U¡¸—ƒ&òúéC¿m\ýÓ¶÷o[}¤|—Öe]Ô¹ŸË®xÏ÷»Ü¹øïî9íÛÌžøá³G6 ¹ïw Sº,Þ0¾EÕÒþ)·jéÈñ_Üïª/wzHHù wíó‡··hÝÂýN¬õÁ†ûÑHF@0½N§5ì½Tv§èõÆ7eEÏ}¥Ã·Oχ—]dÄÌna[C«o[´hׯ²«< ö_MÃXyyíš+ëgZÿÃÕÛž®ç™Ù 9÷†UIÝ>Õj•!™ô9|„ÙrrKsnQû&£vÆKéþ?íùôå ´…¿7qÎŵ}BÛ/3É\*Ç"%«†Vö1´c`µš•ŠgðøØ¹}ªFHiwc@{” ©VÖßéÑ‹¸·ûùÜšæÞ' öÞ÷]«u]±JËŽ¥Nlë[Šp‚Û“I¸s[¤hEçÿÿmÌÑó½ë,9ò¿ž+ŽÜèÿugÓŽßh®_óÚ±óíBìçí™ý·Ê½Œ‘þ=Þ¯´sÌÞ×_øtO¹º-üÜüE.GÅÇhÅóÁò%FÆö±¾¥øN–B#uût÷u7>â×ïÝÐóÌÑ[ýØrr!æÀÔ ,žçM~´ƒ“8•x©ûsîß^0É\¦b_©yÕÇÊÛáÉSGO]3øo‰ß9áÓÃÝgÕ´ùÿX€ ÀTOl¾+Nm[º;Ô_YûÏ'ñO}ßÍÛ¿e•Ö›nql[½Í3ùLº\ˆ;}qPÏsÃë,iíša¬h#î¬þ>Â¥QÉV•ìu/OýÍ{xwwãÙÞ©NÇbÅŸ‹5ŽBª}}[VCgoˆ)˜²óÔ]Ü{'Jì_i‘|_P<Ü>]}Œø‰ËkŽ—þ j¿Ó±å˜@ÂÅ»nO¯œñ‡ú¹V~¹ö“ËÕyŸË$ìüBÛ÷­ÝÐ?Ý5ÛÕ{.Hþ¾jøeiç×k“N  ä‘^—òÏ¥í'‡.‹÷nYý½Æ–„3ç/r½:°¨¡OüŠŽðËÀkÃ&ܪ5;ÀÇ»>]Äÿkõ÷6ÿ€V;þ¶#í9vûTê÷´áVµ‡?98di¼¬ÐUúõ©Rš¸]ŸŸªÒ jëRv*]â±-·®«¼úÖp0.‹ƒk×q%wº0~NxÕ~¾öú¨#çFοï\ïéáõø ë–ñöioÿÚ„RË:œÿ¬Ãþøqå:Öv÷u‘„¨¸ wk…:;°å˜„..2ù}—öÿwO_Î'ýú° ê±ýBSÌeNåúÌ››á9vn~)o¾£%`ÄVà1º«ÛÏÎûñæï1"§/Œ~?îÅ×Ë4ºtnúÊ›;¢D¢Îî¿Ù[ú†Ú—ëV¾Wâµµ]wÌÔÆ'èÅÛã…aÏ.; ˜*~ïÜ“Ÿ}uñ€gÑr?ÇÔ{ÍÝ>"bÍo‰Î"kÂ:Þ/5hL¹šGOM˜i¸±¿ç~¥ô°®v¦^}À;Qó‡ýs£Wùž~ק;´Û° §.ŒúHÕ»vâöÍié©.¾qg#Eyó‡ÓßÚ/UH huñ:¾E—^skPÄßNÔ^^Ï—½:¾ùöQ®ŽÎÚÄD?Ÿ!Kžî•ò±z*ïOo\å:iÚßBõ®ºh­SÍ7«o\¼åkqò¼}&í÷¼êUÞ´Á}êÇçVûs‘ñÈhûÕ:¼S©–ñL¶Spð{*@ä¢!:ìýöw,~-Ø)û™r9W>ÓÅÝKù÷œ…qÛç¦}óÓÞc7Œ3Ú{ ~ªb•çúLŸÚ%8Íÿ–inüþÍÇÓ¿üá×° Q†?'ÿòϾرÿð¡ª&vws§ -VÝH¾°Ë+[n¬kêuüç/ç.Zöö¥?]Vyv·E'ãÓܰgÙÖS7¯î_Öx#Ú««_oúöª£·’?ÃDÜ+÷ß¼^}7SÜô—çww/šoo ·4€Ç¨ƒ^,;Ñðg^šiUËÏnYþ±Ëù× žP/8£kpªÿvU߇||~Zwà§i.R¢Â‚¤™ÐkRÍ^“Ò^IÉ ‹JNHóós­]$)¹íjÉ,Ø+5½òà'?ÿQËýGe~á€úe>­_&Ë+„%Èûö™LåS³Ô'«K}’ÑYl9&`W¬q÷PÕÁÿéEn¬èVþȆ&~دuU߬»#wså³økÇ“SÒ¿fõ€l—$öèŒf¡CwÇyÖí?éÛf•|ôwÎÿµå›ÙK¸Pjð„Ô€ÖGîŸüò £öÞ— ¦CfOy®´sÄáµÓÆ.Ÿ°ç»Å[VÿñUûâöâÝ|åõ„9[ß®Öì‹ë{ùÐãL¿îŒ6ùJº5˜"fì÷/—éº%ÖðĨ·èÜ®>%.ž]P‡eÿtXz{CÛR¯¬zþ¹=ýJÚ›ê¦M¸r­ °>A“O\»ú²%O·aez/¿¬æèƒÆêÒ_5ªÝªQnO5îÔ­GÏíê—rËx3ws嫨SÛ' óvx³šk6Ö^ZÞøî8)þΖ­ŸÖI9òã•Î]ê&<Õóá¥tá?|Ù˜°¾×†}Û¶XÒ°vËWZVÕ–o½âÒÒ½›ÕÛØ%Ð8ÕÁ«˜oòáôa³Uê3dÅ;uª•sû³oõžƉv­>ìQtË¢û}ÞÚó¯.ûèÿ}ènlû|s´[«áí¤µÉnÚÖÐ+c¨ÕÔNµäӶεÚÈ­»zvxãÕ”)1§v|ý‘áÏëO4ê÷Á¸1½KÿþßÜÍ•†.ööõk×b2>3îVt¢²{sè›UW ÿÚ×=44ã76¦ý÷ í¯*רâïוwÃ'öû«hJÝjN=âÛpÉJÃÆµ*–zPˆÚ¿ñ»Š®Xp#vËÔç; .óH£Uùí×寮±øøAsFÙ«ŒGˆ»?ûïTZøá¿"‡g/ø»ßô´’xaÍÌíñÅú½ÿ¢ŸÊô7mÛh€5y¬P §3ý5ût2ZŒßVSw؆³]÷,™5ó³/×|0]yçü·w~¹èµ?ÏhôxŠän®T Û_+dª{xnÙ_\q¨;í«7sòùzñ«ú£?þq»usßÔs—§ßûôƒë¼´yùQã‰bÏ=÷Ä##ÆÎÁuƒeÁ ‘ã[ÿ\Æ[2á2õÀæ”ÓŽO½þAãqÝw$Èůfìµ¼©wjÜÆŸXüéA©0aPjø›ö¦m °&éÃ4³Tµ´é6JåT¼aßé ûN½{â×ÕË–.^üݾ«É_6£92§u3ï¿þW=ý¹›+yÖVŒ­™É™ §¿è=xÃ.{©…oÞ­‘Àn+Ö *Ÿ“÷3º–oRI¶þ-·–¶¬?uÖ¸~ÍÊ{>þ¾ÃØÓ»’ßû\äI/UbbÚqo/CÖjDsëJ”V¼sô¹ùv­Fvñݱä¶Ü]3uô&=ЧÌ6oÑi»_½^>uÄÞÄ7mËhïì½+¼ÔgÒK}&Ì?±nê ·&ÿb<@þ™ôÎò^;Ó¾ù-Ïs9¯Óì妙|SdÜ_»R¾‰0[Ú[[7¸+ίÍW»¿l˜³¢t(Ûû³Á_>7ë¤èO­þòÊáž›uéñFŸ7Û„¤d¬.úêµûI§Ž¬ä:2ãëILÐæø+-U^õÞPvɄӢÙõɲÓG$³þÎo³—_÷|õË6ÅSÝä7mÃhP`ÔÚNÚ\'´WW—^7dê¾åûîô.‘Ý7,ån®ÜÓ…ïѸå¼3Ov[þÛ—]|˜žÊ»áôý‡«>~îÖ3 "QÇ·,üÀðç‰ÖÓÖ,RË[-zMlÊ'Ë•¾~ñ«~œGpq…æT©Ïðú“ûìÕʱÏ>ÿëÏj»Šîú¦뢊÷Ö(͇içÃMÛ,Ö(Xö­§~ÒpyÝZCé¹­ÿœ|õyîæRNseý»ÛÌ?2|ûÏ“š(=šAíõL™[ºO¹qdëšï—/ùzÕÿÂE.oöBg¿ã_/¡v|ðÕ©Ž•Ck•1E‰Ù=Ñnd»¡-VEÊÕ%ÓvŒ]ÓÊóܪ™¿jª|üö#º¨òá¦më˜ZÂs^í}qÌ–éµ2>Yí]¡z1Ù}EÄÁåá×6æn.SÒß?þe÷F}P½:?lÙ[O»åúFTNE«µ~»Zëã¦þðîKí–û[§.=ÕuTE·À@ý»/rûl¸FLS±ª"ÏïSrÕô roÝÇ?\z¡Ö×s9¼°´[™GÞ)¨Î›¶Q¬;`jú¸Ç÷mÞuyJ­r-cbäÕ(ã¿nå*û;äm.“ÑÝÞ=®UÓñ+ ݲiJÓb¹h$íí£ÿÄ•}¦¸sjx«œƒÛM_ÔcÍ K#äÊ‘+ñRÑ9¸^°¬;&rmïþ뉵ròÙ9àRuÀÐÐéƒþ'ºß§¶¬öÒ³E:Ìoõø‘ÛùsÓ6‰UòƒöôÎï /ç“ÁYúÈßî¼g8áÑìPÌ•w × x¡ÃWQ­çþõ]ÿÜ=Gï{¯ÅÜ^‡îöøb§¢eüD"ÄÍÏøUà%šµ+;ìØi‘#³æêõIöŸ.#ö%;|eÄ«ëcä¿éýÿ“CßkèýøÈ§›¶E4È [ûöøØkÑàFA¬~Õ žK eÙpò”—|T¦˜+o4—½V³Çš[Í?h$»—Ìßá¥ÔžU^íÜ h–í¤½¹mÆÊ³m•}¸ðšË¿þ|Êð¯×óm*¿çÄ©â[ZÍìòS´œŸÖª{ÉM_õ )òÈP±N«WÛ)¾j¿‡¿´þó¤/ ©>¸_çô—ɧ›¶A409µ½11nmñBñ©UZvlóRݪ¥|î\:¶wÝ7_þrA'¾'®_=à)ǼÏ%Úû7/\IùòÁÄÈK—#â+ø8¥ûŒ ½&òêÅ[qÉ?ÄÞ¼p-ª|IÏäC©ãÏü¼í–áß››§ Îâ{B|ßxúÕlZôúïT~fÇ ¾íT tÕ\?ºý›çì×K@ûÏ?y!yLØ.¨ã×ëþz©ù´Cš?¬ùãG•ꇖ+æ®N¸wûú• g.ºÜ{pl•¤þÐitÉw1>^›]¶¹ÕôU?ÿoqn:¼Kp†‡¸ä×MÛV05§§Ç„nòóÆ-;öüù×?‡V}²iQ|Ò*·bå«·}wðz5/ç®Êë\Úk+ÚÕì¶þª6uJâo}*ûöç2½·þ¢aÊjÇÿ3éù†£þ¼ûpÆ« _^(âÓpÆïÛ‡T0ÕÝv >ûý'¶î9ð¿/Þ_?#é‹JœüÊ…vññÐaÝjú<üîl¿ÆŸüy¾åWÓ>]ºq÷_§ÿÝ»å_—bC6ôÁ›o´2&lä¯ýê·[x4e™ ¯è<ʧtý«7«á’ñ­‹CÙî4Ó%¬óðæ~F]>Þ´­! €éÙy”iÐé]ß|Ë.°óº+³½˜S•ÿ¸óaæç;6ú>Rÿ}o3 öA/úøÅA9º¬cÐsýgþdv¾× þ¹³@Ùí«‹uÞ›ýêÈ›¶14  (@@ Ѐ4  (@@ Ѐ4  (@@…PPÐFs/lD@…^¯7÷"ÀV°±°M4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ4  (@@ Ѐ…- U*•¹…Ya h _Ѐ4  (`õ­×ëͽ°!VÐ@A" h@P€€  h@P€€  h@P€€  h@P€€  h@P€€  h@ÿªòÑ.¿UIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/delete-an-xpo-container.png0000664000175000017500000001754014770023131025550 0ustar vladimirvladimir‰PNG  IHDR¡_á™OPLTE""# "   ! ##% 2(!?"+ ,9·"©×Û Û Ôÿ ÿöÿ+B&;%F2_+R3b3b(L0\9lBnEpCo5f 6i6i5f.‰+‘'ÇHG#QƒLz"N‡G“!LŒ IDEE'()=>@***BBBBCD15;EEE234124>&H>-V(+Q%2_--ÿ%%ÿ55ÿ1St)Ns%S-Y„a:^@aN*OF+R]`cPRTWWW^`cHJLMMM`bfIGl^^ÿFFÿVVÿUUÿVy›Ns–ƒ,v2………z}lorprviiizzz‚‡vvÿ……ÿ}}ÿnnÿ…ž¶¢';£££ŒŒŒˆ‹”™£‹‹‹™™™¤¤¤”””¡§¤¤ÿ””ÿ§¿–¬Ä½+³%ÃÃó³³²¶½¦ª¯µµµ¨¨¨ººº¾Âɬ¬ÿ»»ÿÃÃÿáÊ"ÎÓÚÈÎÕÔÙáÒÒÒÏÏÏÛÛÛÊÊÊÙÙÙáááÊÕßÎØçÕßëÒÒÿÙÙÿááÿÒÛäßåìÛäïáçíÿÿðÿ--ÿ%%ÿVVÿeeÿFFÿ……ÿvvÿnnÿ””ÿ¤¤ÿœœÿÃÃÿÙÙÿááÿÒÒéïøôôôèèèøøøÿèèøøÿððÿÿððèèèÿøøèèÿðððèíòÿÿÿ[kÓÌIDATxœí‰£ëXYÀã¸#ˆÛC‘Á幡¨ˆ:IΊ‚ú\{€Šà6©(.í!èÅ¥ô6AÛIõâÃAx(¾qÚ°)zþ0¿ïœ´Iïm{{ïmo¾&ßoÞë$mÚ÷¥¿ž%9ùNœÁ[÷Ç{ô!ùƒÇöÇO¤Îc¿ò¶}ñÛ_uÐý~ì†ûâOŽ(Rç±ál_üõ÷»™‘²!ꑲ!ꑲ!ꑲ!ê‘^4tÖEzËÕ o=ë^xå˜öûx"½hhì¸@+_ku7¼uìŒy¿'Òu†Œ” OúNçl6OaÝ`gãñ„ ÝV¤ë uÆãñìÔéõœÓ–ãvû®çög”+–Ü!,zN ÝR¤›j¹ñ¬íºmS Üά낖³3ø;u»³Þdæylè–"ÝXËÍf®{†+ ÏÄóf3ü †ºP’ØÐmEºÙP×q:‹24Ï Cc§O¥ ÙVñ’_Ü bCŒû첸óH×Öržçµ‡N·ëœBAêµÝŽë• Mœv‡H²Å|H{í>wwZ}¤e<h9ºî¥–¶õ¡$ÁQQ¯;„¿½Ù¬—ÿ…õ~·?ì’8rva¨{ñ7c¾oÝ~ßv¤%ºÐÙj;4´>âÕHûœ‚ë:C4Ôr¡·é:ÓÙØÃõ® è:-‘–èAÛUsC uLƒØv§=wÚöÑmŸµ[3·5î9îӛºtq&"-?ìo5ÃPwè´±ëv»nö×ÖxæŸ2U¹Zz ½–ÓoŠ!¨ÑÁÇØgÆÐp64„ µ½YÞµ µ: yY7Lמ;{šºÎ¸ãžvÜ)ž¡r½¾‘r UC]§ÝmA%Œ†:Ð)Ûtn:t!zÙËÒéú¡ç^<¥zûûÝêÍf}ø-µÁÉl -Τåµ&³iËó  =„vÿˆ´ ñC¸^ÏC.6“+‘®7äCCìe·ðÜ©éoC/‡Ý1vÂá8‰Ø~_…cŠt­¡–ÓC§N+ Ç›´ ß ?FÜCÉôÚNÛœ´;æý>žH×êBÅî@K3›¸chÍ&Ðñqêöºp„N±v¿â~O¤ë á¡ ôa[-ïˆú°WÚïã‰tƒ¡¾cÊд‹=Bqè´ðqhºñ´ MºÝ+!·é´á°‹ðôÙ*kN¢å‘n0„ñд;cÇ·ÏéÌ:Žç¶fÔ õ(ïíBªÏD9‹VÛœ/]óí­9GºÎÐu¦æ¼=>LÆØ%œà"ž4ŸLðOµû½w¶íΊ¡ú±ÝƒåzÕ†¦Ø§‚Ã5ûu. —àãÔ‚•éù¡’c?§@Dz?Åêb¨þ7t&ÅzÕ‘öóÒqŠ—, á0öÄ^N`V†æì¢©¶VÇJj` ãvJCõ-oŠõª#]œÚ÷PÖÂÐÙüvÌå}g8k÷ÆNËÁÓñ§ÓáÙj¤Gohz6ƒv²4TßwÇn¿´^u¤=4tíB J̸h‡à¯¹œvl‡05ñ !¬åá·Y ÕÏ ¦˜•†î«ŽÔ´C]-â áy{9Aªµ”¡”|h6{«§Ó¶êº8 6vÇVréŸÜt‘c%† ‡‰]Îb¨žiÏÊëUGŠBH-·[”!§ÕvNíåS×k›s8·×u:8ʵ›¡ŽÓ9m™Ìá9#î†Ý®¨êwÚ=øßr¨~AÓ•õÊ#vÚ}sT4íNÇCýîéÌ^N€/ôÍñPŽC»øün†Ìy·Î á9â.ž2îµz𿞳æ”\…†®Ã1EºÉÐ⚬q^0ûmÎh÷ÝÖpÝií£Ûïã‰t7Cž]õa~:X-w½ý>žH·×rÞРºl¨’H7j;=8ÎÈ µÜ³Soº4´aTð˜öûx"ÝÜÛÆcõEo{ ‡Væ }Öj®µgRŽk¿'ÒmG¬[Ð×~Úñì÷ñDzôçj)¢)¢)¢)¢)¢én†>üg¿Ó§U¿ßþÓ¨Y¤»Í/÷³w¿‹Àür»Dúsw¿»f‘:Oì2ùâ÷ܽ»ËfƒƒîwC#uvú'ïݽ{Ð]Úõ‹” U¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢N†æsl~5I·¼¸?ØÐ”Ò¾¥ë_ „òwú§n:Ç@Ë —Õ†’"Šçƒ ÷Bà !T(D¨µ”"Ö:r€ÌàkÚO}a¾³U¯ ´ÒOxÞÇ'¤¯æ½¢á†üD«D¥Òs9òU¦uæÏu,3`-gËP,a5ƒr“øø¦Paòñ!ƺ. ýC•£:úÔ;7'‚½èÅÖ”øù«ÈÔY©) ‰¥ÇO„©ñÐÐ\©ÈTxh ¤À›kÈ<†bÌ÷¾Ç–:’øhk¹È<+æs‘B ]2$#øúpn¼!h]T åzR.:g‰>Ê(‚ ¶KëLJ™â›Ò;pØ6Il¡Ià é¼K}]ä\ØPÎM [0„ó½wâ>ýTy­1†¦ž»Þ=ÞòA!©)†z®{D†€¥¤¦:=í—¡¥¤¦2w2*ó?ù*ÿq Hj¬¡/üÀýãàÏ¿·¡†¸ í=âvhïÜÔÐÙø¨ 5®/w…¡§¤¢ó [î,©¥™ÇC› ¿îÅêòœÃòÜÃOó4õœÂzCƒ`ãç\0´ßä<Ñ•i¸!i†¢G¶EŠCÝVY€cxv ™añØ—ùø¸ûß»Í4ÜíŒ 3Ëô8„ ~bF¾#¥ì°¸–ùø8:tKT¦Ö†vcÕ#gd‹…à8.aÍ&ʇ’$•²Ãâ`(_(_æsxêlh·ë . ¡L„P~ØÜä†Â$IR•‹£!»À†Ös˜k}4k‹‘öG: Gb®%ލš1ïM-W ‹Ûn‡ÖsCÁ@ËT§j8 mO$¥í§Å°Â£€nD”‹+‘Ú…Œ ­å †²Eú*øªƒ^9rž†Òa~êà*†}ĺJÓ Ñ§~‘²¡ª`CÔaCÔaCÔaCÔaCÔaCÔaCÔaC[1™4뮺Hƒ<zy*¤êlÁ†RJ+L<»@"tdœ,Nl-7Ú÷8d³ ¥“t*ÌÇŒm^ Á,Ï• ÑP(ðÅ+‘ši` ^´†F 3¢c¥R³ÑGÀ0ï}ÿ‘^N= i1 ­Ä¾è0H‚Å0°ZD#3:ãpng€ï‡TF‰ŒÀHb‘¾Ž‚$6ð¼ŽFD[þåkFz)55”ú~fÆ~ƒÉwΫ($&o=BCPùæú²ŸX§: •1„£Â>^˜˜D”$ Päïq¸¸á†´v~¥ìœöU¼rB ¡$7d'íЂ!eaSreh !T‡›/¼N¤_äl¡¨Qkj( ‚H«ÐÌç0/š}\Vi© i¼Ô"ÑÔ}(./C˜Kíãh¿ÝW£ù^«8ŒÔÙrmÕëߵܮž†R>÷S凄ÖHÉ05 Ú¤ PÒò_z ¿ÙÌW˜áކRØÌ7›ù)l”â\9û´Ñ†æ8 tÙ3wJRd7§fî.xÚÌ´–._Àí2ØÎþ‹Yj6KÍFÙà—+5Û¥”·žš‰S9)ו9ghÒDCƒ¢¹²ç–䪼ïMïûL±¶b¨ãºî¸y†¨qrçÎ IeC§7u=6T5`èN!i¥ AùñØPåXC IçÚ¡¾Óo†¡W|ý7üU^{§à…oú!gUP»!=…c1tçÍ?ìlTgC”#=)ôœ|v¥–›ºŽçµÙPÕœzôjOáÔóØN =šÏ)Pä¤Ð£Ù}ØuØuØuØuî}q£GÁ€¦_IB6D6D6D6D6D6D6D6D6D6D6D6´ss½91U~nV6´0”,Ò¼Ó5ùøyÒÞ|¿ÉÅ+4ÛP‚s'QlîLH¹Ìn„e̺“ÒRK?6s´Îáik»¹}IrÂ÷î7¹xM¤—SOC:P*ÐJÌ3?“±Žt22÷ðMæ"Ï’T"Ì09Rg~¾¹} Nó.L2°åŸ»y¤—RSCÚÇû/Ãï?Œí½¯í«¾RÊä‚ǹ!i¦£ÖY¢ü|sûhnÁŒ’ Àh®ã{¾d°‘¿)¶«µ!¬¨¢•\pÌóZÉÏ%Iü|óüM‰b™êH‡jŠî}É+ßȳ޳ܮ¦†l-eÀOý«0 |ñs™™¤o4d£T˜MìæöžI…Ðé!ï1ßè+I ±Ñb¤„!N‹ S~'ð 6KÒÜe>õó&Fá½`»¹}ÄÞ¶ؤÊf²¬»Cö–™®ìæÅ›|Ï6´bh—LãUC#nÛøæ¬ö¦ 4Tº'ß.Ùúvóå›öVáäu¿ó¡bmÅPËuÝIó Q³S I+^Þé™ÛeCU“gxå’εC=gȆª¦È’DIçÚ¡ÆÔr¯øæ;ÇÁë~ð\_®)ó)¼ü/x;UÞRôÚ·ÿèJ;4™yn3 QŽô¤ÐóçÚ!×õî)TÎI¡GŸ34í¶ËS’°¡j8)ôh>§@‘¿,ôh6D6D6D6D{Îp3o`CÕs﫟ûØF^õ·ËíØPU4ýZú°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê°!ê4ÉP"‹eÚ‘–i”¡R#íHËÔÈPÊ<«[IiÒ‚Hd#LAMá†FJ둪<Ò+Q#C~”æiÚA^\D Ä\ËQâ'ZƉÀÄG+¯Ö†þø­yÑ‹«4´Èêöîª ¦£*©” ° M2[ÕÕÙÐàoÛÌ3=T —Sdu£¡@ˆ47¤"Ìà}ˆã ² vµ6ôø–kO©ØIÐ"=7YÝ`ç:Pqâc£„UŸÈ³NÙP !“ ­Yݘ©)uâ !í1y› Uú¬n¤ègyB~ƒ µÛ› }î¿öì¥\f( UÍ1Ôs¼ †>÷É»ÿ`ïñng¾i„Ev÷2q»1†¦®»ÖênÛÐî4ÆP«ã]4”ëaC{áf†ºÞ켡Bð±‡TùÅ×4ÃÔq®Û+zöoÞ?~éP_éž¹™¡–wÎÐ#>ý‰ßá2ôËßw¨¯tÏܸ·½¦*$q;tsnlh2¹`H/%±¡›s¸s (‰†¡ü É,å“Á±!ÃÓ÷ìõr)ö„Ÿ—;T ×fO™Ší‰!6D<Á-%Îô«tšÉ±5¢„©´•2'¶íQ­ QcÝ€o¥àç>öìœÚu6ô©wn¹Nákèí·o§¢#FØ$ÕßÐ>>í6Ca ñ ¥ñR­ú·Cûø´Û§ûÍÝN"JÝ€k}öñi·I&WV•§ézêoH‡+Óg7àˆuŸV=õ‹” U¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢C¾Öƒë½óš°¡ó\òý':Qz´Ó¿±nèü\|Y(”Nq>|b x1ùw– RK?ŽDpk©á†VçâKq–Dx2!ø„Ÿ‰@‡bžøz¹àcÂÆÄ-9ª³¡ÿ~ãæ›~=ïù/ÅMVçâùa¶˜ZÇ^ãŽó¾…¸Ùr!7¤•ˆö¾‹k©³¡Á«·Ü9ï9&;eu.>­c!¹!“'‚bÔÂZ·å§æ†.Ïðº8ß@áí Ú',Gk …:¼Åî:7Ÿ6Iè BL\´bda(õƒuÿî¡h¸!½y¦7*4ÆPËó¼ÉqzúÁS¥µÆr7Ú8_e<¼ÿÉBRS þx}-G‡f¦¡…¤¦ê9æh$ÈÃÅ„]FRS Mz³¡Ó+úÒßz@••æ–{òÁ¯6ÄÐl MQ·´þe8øÌ}{᣿ÿmÍ0ÔvÇ}gxdµÜý|âéÆÔrSÏu;ÇÕ}äßÍ„]M1dª¹#2”ëÑM24;C…͆(ò?+klˆ:lˆ:lˆ:u6ô„³…/ÿöCºgêlhŸV=õ‹” U¢¢¢¢¢¢¢¢¢¢ÚŠRÛSle²ñ¥}Á†¶†’<ùL§k.tΤæÌ«i¶¡Dj$yÎm e¶x–1gSʦØj ÛÌáik»¹} &=…ï=`nZ³ é@©@Ûœ[ëXä¯ÊH„ö“¹P6=P Ìô •Îü|sû(’807Ìõå?5Üö—9·˜+(ò›xøxÇ/ÌëŒsC6YZg‰òóÍ—‰º¾FI˜$} H¿ãk7ß’ëŸZnWkC6çV/›cH¡¡$7¤³ïu(ÔÈÏ7_&ê±Lõ'¸çl¹«ÝO¼k¹]M ÙZÎäÜú Vaøâç3oCeSl‘óo±–3›ÛGx&B§*ÛôÝôþò¢¢¢¢NÃÇX€{/ÞrÂ[ÿw¹ªŠ¦_ëC6D6D6D6D6D6D6D6D6D6D6´•ù|ç«~Ë÷óY.o¿¾x'ØÐVL.x_¾¡ÙT•– É"/ï÷ËbC[1_už ]Z äÊÛtÉÐâ•k¤‚5ÛPêϵHUˆÉZ‰(n׋ˉIâÂ2„¹à)fIj)mâ—È’@Š ”~± Tìömð™x»`{ƒF|Å,˜Ÿ^'ÒË©§!­‚ 2iæ¤É2Kr 1ѳ(óLc#Ölòd$qS¤Cµ\PÊ–û¶çˆ‘Áo–¥Î4Þuö:‘^JM i·‰1|%ÓŠƒUÔ`™­¤ùË&óÛ$Šc’²Z., Ù·)ø_&"Á‚b¨óLë¿ZJؽo}îæ›H¿ñƒËíjj(óýÌ V³õM¢WÉñ ¥©w1b¦øIhÈ,Ì‹\Ù#u¶ÜCú µ¿NAŒFB+iê´Ø$dŒ5¦âC6Ñ8ÛûÎû‰ƒµ†l4oˆ¹©åp-’øh®1½L³¯õA…ŽT€ÀdRÊeoM XN¤Œ"øùÏåbº‘PÊÄL&=ÓA‹¢ålªL´o‹%tàÅDâd%ðŠ]¸F¤6d¡wwß2lhÅ”&)åµ~ëûãCïûLi­lhâ ÉÁ£éɾ©tÞÓfCUcò‡–’ÎÕr]÷Œ UÍ"ÏJ:gÈ-§àÕÚÐ×½àíTyK‘Ç ’^¾:'‰;kˆ¡¿4î½ðM+†¼vS QŽô¤Ðóæ“Ï®Ör«•ª†“B>×Ý1ªž“Bæ#VŠœz4¢Èç?[^cCÔaCÔaCÔaCÔ¹÷¯||#ÏbCÕsï;_2ØLý笧OÓ¯õ¡¢¢¢¢¢¢¢¢¢¢N“ ÍK÷æ¢i™&JJ鉴#-S#Ci–Îub’¬òû¤¥8‚yfžâ’0ó˜×ØPøB*B§B&WT‘‰0ÄÅ ô³D`†cW镨“¡¹V F&x‡h“$F: ð¶·X¿™„F?Ó~õ‘^‰«úà«6§&?ïù/=T —“ß?Z)“N“€¯|@â]‰1ëW‡ÑÀ¦oÕÙÐàÕ[r“Ÿó衽œüìr€†¢'“Ü‹¥*ËÌ4 ÁP¶¸w­ =¾åÚ“G*6$±Ð#‘˜ÙÐP&F üñãH±®yŽ U4=*T8qÎ(ÏLæ8“N8€.‚ ‡Â|º 6T ›²º—GBÉâ&Þ1Ôv½Ó†žzðÉ=G{ц;z§‹ìn¹˜¤)†zîéÊÍ'K†žzðäýûöðžhŠ¡®;;]gÈè¹Ï†öÁ k9×u{ç -ô°¡½p3C}·{®–û©BðÑSå¯þâ§õ•î™›ò¼ÙØ)gV>û>~ÿ8ø××ê+Ý373Ôr§=§|“]¨å>ýŸ…$®ånÎÍ M<×í\ì),%±¡›sÓ#ÖélvÑ^HbC7ç€ç@ C£|^Ds8;ç³>‡ ôú,g%7§„;üʆè€óÌ”š£¡4Ò©œœ ÑAdùp„R NdZÿ1Öc3äÛy²ƒX‹A#]oCO8[øÊï?T ×Æ·ÅF)%äH7ÁÐ>>í6ñÍ%ZB[”‰ùbÖsŠ‘®§þ†ÀÈÀÇo?4¡ÚǧÝ&1t²3¼¬/gLt®õÙǧÝ*ru̓“Œt- 0”–ïì4Ùÿ“Œt- 0´–úEʆª‚ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Qg¿†~ýîÜ$˜[ä7îþ|Õ!ìÈ®‘îfèÿÞñw7 æù—wüsÕ!ìÈ®‘þ?ã ì×~2IEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/create-an-xpo-container.png0000664000175000017500000001755414770023131025556 0ustar vladimirvladimir‰PNG  IHDR¡_á™OPLTE""# "   ! ##% 2(!?"+ ,9·"©×Û Û Ôÿ ÿöÿ+B&;%F2_+R3b3b(L0\9lBnEpCo5f 6i6i5f.‰+‘'ÇHG#QƒLz"N‡G“!LŒ IDEE'()=>@***BBBBCD15;EEE234124>&H>-V(+Q%2_--ÿ%%ÿ55ÿ1St)Ns%S-Y„a:^@aN*OF+R]`cPRTWWW^`cHJLMMM`bfIGl^^ÿFFÿVVÿUUÿVy›Ns–ƒ,v2………z}lorprviiizzz‚‡vvÿ……ÿ}}ÿnnÿ…ž¶¢';£££ŒŒŒˆ‹”™£‹‹‹™™™¤¤¤”””¡§¤¤ÿ””ÿ§¿–¬Ä½+³%ÃÃó³³²¶½¦ª¯µµµ¨¨¨ººº¾Âɬ¬ÿ»»ÿÃÃÿáÊ"ÎÓÚÈÎÕÔÙáÒÒÒÏÏÏÛÛÛÊÊÊÙÙÙáááÊÕßÎØçÕßëÒÒÿÙÙÿááÿÒÛäßåìÛäïáçíÿÿðÿ--ÿ%%ÿVVÿeeÿFFÿ……ÿvvÿnnÿ””ÿ¤¤ÿœœÿÃÃÿÙÙÿááÿÒÒéïøôôôèèèøøøÿèèøøÿððÿÿððèèèÿøøèèÿðððèíòÿÿÿ[kÓØIDATxœí‰£ëXYÀã¸#ˆÛC‘Á幡¨ˆ:IΊ‚ú\{€Šà6‰(.í!èÅ¥ô6AÛIõâÃAx(¾qÚ°)zþ0¿ïœ´iïm{{ïmo¾&ßoÞë$mÚ÷¥¿ž%9ùNœþ[÷Ç{ô!ùƒÇöÇO¤Îc¿ò¶}ñÛ_uÐý~ìûâOŽ(Rç±Át_üõ÷»™‘²!ꑲ!ꑲ!ꑲ!ê‘^4t!ÝÅꆷžE^9¦ý>žH/9.Ð*ÖZц·ŽœÑ1ï÷ñDºÎ‘2£qÏéœM'£ ¬Ìât4³¡ÛŠt¡Îh4šž:Ý®sÚrܨçznoêA¹ò`ÉÀ¢çtÙÐ-Eº©–MÛ®Û6ÊíL#´œÁ߉M»ã©ç±¡[Štc-7ºî®Œ,¤½vŸ;Ž;©>Òe<h9"÷RK[އzP’ਨ àow:ía½õ‰ã!×i—†¢‹¿ó}xëöû¶#]"‚ÎVÛ£¡õ¯FzÜç\× ¡– ½M×qG=èäœNG®G.tv 7ê´DºDÚv¨šb¨cĶ;麓´nû¬Ýšº­Q×DNwâzÐňtøÑ`«†¢ÓvF®Enö×ÖxæŸ2U¹Zz Ý–ÓkŠ!¨ÑÁÇØgÆÐ`:0„ µ½iÞµ µ: yQ7LÖž;yš¸Î¨ãžvÜ ž¡r½ž‘r UC‘ÓŽZP £¡tÊ6›."]gÈ…^ö¢‡tº¾Gè¹O©Þþ~·ºÓi~Kmp2@‹3ny­ñtÒò<è‚B¡ÝÆ?"]Žø!\¯ë!›É•H×òŒ¡ö²[xîÔô·¡— ƒh„p8N"¶ßWá˜"]k¨åD`èÔic%áxãô[áÇè‚{(™^Ûi›“vǼßÇéZCTì´4Ó±;‚Öl ÝØÁ'n7‚#tеû÷ûx"]o¡ÛjyGÔ‡½Ò~O¤ õS†&öqÄ¡ÓÂÇéÆÓ24Ž¢+!·éd)ÂA„ðôÙ*kN¢‘n0„ÑÀ´;#ǵÏéL;Žç¶¦Ô õ(ïíBªÏD9óVÛœ/]óí­9]DºÎÐuÂ&æ¼=>ŒGØ%ã"ž4ñOµû=w¶íNË¡ú‘݃ÅzÕ†&ا‚Ã5ûuÎ •—àãÄ‚•Éù¡’c?§@Dz7Áê|¨þ7pÆåzÕ‘öŠÒqŠ—Ì á0öØ^N`Væì¢©¶VÇJj` ãv–†ê[Þþ”ëUG:?µ3ꢬ¹¡³3øí˜Ë zÎ`Ú–ƒ§ãO'ƒ³ÕHÞÐäl íäÒP}Ϲ½¥õª#í¢¡3hZPbFe;Íåذc;䀩±q×­ a-¿Ír¨~ 5Åtiè¾êHM;9øk”eÏ ØË ºP­u  u¡äC³Ù]=¶ÍPäâ0ØÈYÉKÿ䦋+1=Lìr–CõðL{º¼^u¤!„Ôr£² 9­¶sj/'˜¸^ۜûÝÈéà(×n†:Nç´eF0猸v»¢v¨×iwá‹¡zøMVÖ+tÐi÷ÌQÑ$χzÑéÔ^N€/ôÌñPŽC#|~7Cæ¼[g †ðq„§Œ»­.ü¯ë¬9%W¡¡ëpL‘n24¿&kTÌ^‡3Ú=·5XwZûèöûx"ÝÍçAW}PœN VË]o¿'Òíµœ7°†ðB€ˆ UéFCm§ Ç…¡–{vêM†6Œ Ó~O¤›{Ûx¬>ïmOàÐʤO[-еöLÊqí÷ñDºíˆuëúÚO;žý>žHþœBí#eCÔ#eCÔ#eCÔ#eCÔ#eCÔ#ÝÍЇÿìïwú´ê÷ûÃú5‹t·ùå~öîw˜_n—Hîîw×,Rç‰]&_üž»wwÙ¬Ðýnh¤ÎNÿä½»wºKû£~‘²¡ª`CÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔaCÔ©ÀÐl¦Óͯ¦Ù–÷Ú‚RÚ×Ãlý‹PþNÿÔ aCçè«rYá²ÚPRDù|°Aâ^h¸!!„ …µ–R$Z'BöñA€|Mû™/Ì÷o¶JᕾRúYÏûø„ôuß¼÷@4ÜŸj€¨,Pz&‡¾ÊµÎý™Nd’¬ålJ$¬æPnRß*,C>>$X×塨rTgCŸzçæD°½Ø‚²?›:+3e!õ¡ôø©05š)› ­xSj ™'ÐP"‚ÙÞ÷ØRgCýglI§|棸Ia(HÐP†eA¨T¤ÈECù e`å67dìøØiȱˆZz|KJò# CêµêºÔvÍúøÝÇm-›gÅl&2(bC¡— ÉX¾>\#¤oZH9„ž€”óÎY*E€2Žaƒ¾í$R&:—Rfø¦,ƶM[¨CÒpCºèR_9ÓC6TpSCƒÖ aÇ|ï¸O?µ¼ÖCÏ]oˆï?ù ”ÔC]×="CÀBRS žFÇeh!©)†ÌŒ–ù‚Ÿ|H•ÿ¸¿$5ÖÐ~àþqðçßÛPC\†öΞ q;´wnjèltT†×—»ÀÜÐ5NIê,öœfm2ì~R:ŸŸ ̓ßs-zNa½¡þ¾ìtqz(8섈«4Ü4C q€#Û"án«ÌŒy !ì3™À³ÝÒOбò«x½1 7äC«â' ‡'2o>Àá ;æmžÃgÔ·„265w‹ƒìázjmh§1V=t†ö;÷Ç%{¾‡÷|!|©%!ãã±}ÛmQgC»]§Yh å"„/¿Ã=‹1o¦išA:³†X±¡õæZÃÚb¨ý¡á˜i‰=6;æí›¨åàõ! ðº xÞ^Qr[4ÜôÊd¦3 5œ„F(–ÒvØÌ˜·4 BA¹’*Èf°E(pÄ< ¯· ×¢á†òùö•ê-y¸+{.ÒpC:,ư¯b(½É¸ù•iº!úÔ/R6Tlˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lh+&“fÝ%YPdB/r4ªÎl¨!¥´Z{>*:6Næ'¶íûìb³ e1“¬Ÿ)ÌÇLl^ Á,Ï” ÑP(ûðÅ+‘™i` ^´†† 3¢¥2³QGÀ0ï}ÿ‘^N= i1 ­Ä¾è0Hƒù•°ZÄC3:“pfg€ï‡T†©ŒÁH‘¾Žƒ41 ñ¼ÞBÄ[þåkFz)55”ù~nÆ~ƒ¾Éw.ª(¿Ÿ¦&o=VöR ß\_–@ã“èLç¡2†0‘ÚÇTêÔŽÇi*°Åþg¾i¸!-„E);g€}gh(- ÙI;´Àqý@HY”\.®·1T‡W¸\p‡H¿ÈÙBY£ÖÔP±V¡™ÏaéJ \VÙRÒx©Eª¨ûP\Q†¤¹ZI̯YÂÕx¶×*#u¶\[õúw-¶«§¡ÌŸÍüLùa ¡5Rr>ŸmR(i ùó/=ƒßlî+ÌpGClæ›Íü 6Êp®œýGÚhC3œÎºl©™;%-³›33wlˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lh+ææzsbª<ümæÙÐVÀP:OóÎÖäãI{³NæÞlC)NüƉ¹3i ËyboÛ?lRe³ YÖÝ!{ËLWvóòMâ°3¹³¡C»d¯Šß aÕР;i ¡YY`vÉÖ·›/Þ´ÿ³ '¯û•k+†Z®ëŽ›gˆ˜RJZÉðòNÏ܈ UM‘áUH:×uªš2K%k‡S˽â›ï¯ûÁs}¹¦Ì§ðò¼àíTyË’ ×¾ýGWÚ¡ñÔs›aˆr¤'¥ž<×¹®çpO¡rNJ=úœ¡IÔ^ž’„ UÃI©Gó9Šüe©G³!ú°!ê°!ê°!êÜs›yªž{_ýÜÇ6òª¿]ldžª¢é×úЇ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q‡ Q§I†RY.ÓŽt™FZÊc¤é252„²ÈêVRšô… ùSP3xF¡¡Òz¨*ôJÔÈgEšvP¤}1Ór˜ú©–I*0ñÑÊ«µ¡?~ëF^ôâ* ͳº…½»jŠé¨J*%lúh“ýÜVuu6ÔÆÛ6óÌGèå”YÝh("+ ©s…ûxâ$ˆm‚]­ =¾åÚ“G*6d´ƒXÏLV7¹T’úØ(aÕ'ЬS6ThÈ$hëyV7fjBJúBH;GLÑãfCÕ°.«)ûÙA‘ß CíöfCŸû¯ý{)—ÊÅ\Us uoƒ¡Ï}òßî?Ø{¼Û™mšažÝ½HÜnŒ¡‰ë®5„z€Û6´;1Ôêx zØÐ^¸™¡È›ž7Tê>ö*¿øšf‚:Îu»K†žý›÷…_:ÔWºgnf¨å3ôÈ£Oâ£å·@¸ ýò÷ê+Ý37îm¯i‡JIÜÝœ/Ò IlèæîœJ¢a¨8H²KÅdplÈðôÃý{=‚BŠ=áÀçåèµéS`†{bˆ ÑOpK‰3ý*„frl͆(a††p*m¥Ì‰m;@TkCTÇX7à[)8ǹ};§v }ê[®SøzûíÛ©èÃX…16Iõ7´O»MÀPh¼Hi¼T«þíÐ>>í6ÁéþCs·“XgR7àZŸ}|Úm’Ë•Ue‡Á)FºžúÒáÊôÙ 8bÝǧUOý"eCUÁ†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Cįuÿzï¼&lè<—|ÿ©N•îôo쇆:?_ ¥3œ„Ÿèë^ÌqîÅ‚ÔÒObÜZAj¸¡Õ¹ø2œ%žLA>áç"С˜¥¾^,øX†p†1qKŽêlè¿ß¸ù¦_Ï{þKq“Õ¹ø†~˜Ï§Ö±×¸ã¼o!n¶X( i%â½ïâZêl¨ÿê-wÎ{ŽÉNY‹OëDÈ~aÈ䉠57¤†úÁmù©¹¡Ë3¼.ÎÅ×WxE»‚ö ËÑZC¡o±;džÎÍŧMA:‡‡­YÊü`Ý¿{(nHožé 1Ôòpð™ûöÂGÿÛša¨íŽzÎàÈj¹ûùÄÓ©å&žëvŽ«úÈ¿› »šbÈTsGd¨Ð£›dhz<†J=š QäVÖØuØuØuêlè g _þí‡ tÏÔÙÐ>>­zê)ª 6D6D6D6D6D6D6D6D6D6D6´¥¶§ØÊtãKû‚ m ¥Eò™ÎÖ\è\$H͘WÓlC©Ô:H‹œÛ@Ê|þ2,cΦ ”M±Õ¶™ÁÓ"Ñvsû(RLz ß{ÀÜ´fÒR¶9·2щ(^•± í§3¡lz ˜é*ûÅæöQ¤I`n˜ëÊj¸!í/rn1WP7ñðñŽ_˜×™†l²´ÎSå›/u}’0Iú@‘~Ç×n¾%×;?µØ®Ö†lέ^4+ÆBCiaHçÞëP¨¡_l¾HÔ ™éN4pÏÙrW»Ÿx×b»š²µœÉ¹õS¬Â,ðÅÏ$fކʦØ"1æßb-g6·ðL&„ÎT¾éºy¤Î–ëB^_wCÐØh1´9·ý¥œÛžÁf { K)¶JH)R»¹}ÄÞ¶Øöf²¬ËèÜ’Bh7/ß$W~6´b(ÆHm/«††"ÜwlŸÿ§åµUCÝV·†–rn³ÉÖ½ãÜæ‹7íÿ¬ÂÉŸyw)iÅPÇm¯$›4Ä5Nî I+†ÜhÚ°¡ª1†’– œŽ×jJ-÷/{?U~÷δjÈ­(ª±¡»s,|㊡h¹Í0tïeGQ†¾å÷Þ¿Úµ§í¦"éI©GŸë)ô×鱡ª9)õèóÇCãÞò$=l¨NJ=šÏ)PäCï_^cCÔaCÔaCÔaCÔiøëpïÅ[®Sxëÿ.¶cCUÑôk}èÆ¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¨Ã†¶2›í|Õï¡îçÆ¶brÁóäò ͦ[îŠuýËïÙÐVL†C‘ o¿à—¿tõ»16ÛPæÏ´ÈTˆÉZ©(o׋˩IâÂ2„¹àfIj)mâ—ÈÓ@Š ”~¹ TâðºÔ2ðsøØÀ¼GèÀì›íç\=ÒË©§!­‚ 6iæ¤é"K²¯1ѳ(‹Lc#Ölòd,qS¨CµXPÊ”¬«¡ˆu‚SÄýÐ$]J|³ö±ÆWËìo¸!-ð6± 惯dCéq0Ó¸¿ÈÖ²âe“ùmÅ1IY-憊[C„ORÕWÒGCð‘*ûxgô«Dú­ÏÝ|é7~p±]M 徟CÁj¶¾IôZ2dg¼@CY_êË ™›‡˜,–ˆ~nÊH`ufU_)RgË=¤ßPûëÄp(´’¦NKLâ±A&Xãa*¾1d³™½ï<”ˆ0XkÈA|¦ñ÷Å TÞ×ß<Ùðê‘6úZŸ!4áP8L.¥\ü¼¥€åTÊ8†_ÿLΧ ¥LÍd2Ðâ›þY/`SeÊ ¼-ÔXR ½‚§ž‰u u(äÕý4Ý…ÞÝ}—aC+† 4I¹(2ñ¡÷}fimÙÐØ’ƒG9Ò“;/|S)é¼!§Í†ªÆä-$«å"÷Œ UÍ<ÏJ:gÈ]NÁ«µ¡¯{ÁÛ©ò–2$½|uNwÚC?~iÜ{á›V yí¦¢éI©çÍ'Ÿ]­åV+96T '¥}®¹#6T='¥ÍG¬9)õh6D‘Ïvy Q‡ Q‡ Q‡ QçÞW¼òñ<‹ UϽï|I3õŸ³ž>M¿Ö‡>lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:lˆ:M24[Ê}£é2M2”.Ý1•v¤ËÔÈP–g3š$«â>iN†`ž™e¸$Ìœæ56T¾J„ЙP™¿@‘‹0”à&ý<˜á&•Gz%êdh¦ˆ‘)Þ!Ú¤‰¡Ž¼í-Öo&¡Ñϵ_}¤Wâê†>øªÍ©ÉÏ{þKèå÷Vʤ“Â$à+xWâT€¡0îÛô­:ê¿zKnòs=T —S܃]öÑPü´?&![ë,2Scèºe*ìC CˆÇCa1ª†MYÝ‹#¡t~ïÆj»ÞéFCO=øäž£½ŒxÃŒ9Ù<»[Î'"kŠ¡®{ºróÉ%CO=xòþýûxO4ÅPäNO×2z}pÃZÎuÝîyCs=lh/ÜÌPÏÎÕr?Uê>úqªüÕ_üô¡¾Ò=s3Cž79Ë™•Ïþ…ß?þõ5‡úJ÷ÌÍ µÜI×Y¾É.ÔrŸþÏR×r7çf†Æžëv.ö’ØÐ͹éëd:½hHÏ%±¡›sÀs ‰„¡a1­¨9œñYŸCz}æg|ì)¡bšQ6Dœg¶¯Ô e±ÎìÜälˆ"/†#”Jq"Óú±›!ßΓ$*˜ézzÂÙÂW~ÿ¡½6¾-6J);Woý íãÓnß\Q¢%´E¹˜Íg=§ézêoŒô}¬áðö3AÚ¡}|Úm’@';ÇËñrÆT7àZŸ}|Ú­²zW i‡ÁIFº–Ê–o…1+&ö'éZ`h-õ‹” U¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢Î~ ýúݹI0·ÈoÜýùªCØ‘]#ÝÍÐÿ½ãïnÌ-ò/ïøçªCØ‘]#ýÊÆï¸·IEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/netconf-concepts-top-down.png0000664000175000017500000013225514770023131026144 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+´_IDATxœìÝXïðwAw+" ˆŠŠ v`+býìî.l,ìÀîîî,ì@0PTD$¤cÿíÆÆØv°ðÿ~Ým·ÛݽÛî»÷Þ÷=6‡Ã! vi¯@Y‚ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 e>@3ŒÒ^‡SÚ« •2 J4 Ð4 @ÐPÞtYi{et4ˆò ä €h h@€ €h h@€ €h h@€ €h h@€ €h h@€ €h h@€ €h h@€ €h h@€ €h(²~êdÓÿj÷¦BÓíÁ>#Ìq€“Ÿôw‹kØz~äOÔÛýÈ1³t×è•}©oÕNÇâx·k¯zäQ]©´W dß/P~dÆ xùæÃç¯aQ¿ÿ$g0Uµuõô Œ-œêÚ*ýË“õáÅsî‚CÆ&&g0”Ô´ô+U¶¶­U·¦…>BbÈ«DòÉþuyê*=b=}]¤gø'0õÛ.õjxrì£,B^ν¿§ÏˆÊxo– (Ʋ­ª3‚Dï2ÿàÝgu‰OM}5£Zí•ßø*n×#ϺjH³|éÕßñp`fú‡e5«Í~_´eD|õFFÔ³S;¶í;rúƻ߅-mhß²kÿa#uv4T”öe3c_Þ²aÇþã·?%2‹~ÍÎ}†Œ™0ÌÕR•!Í"ÿÑâÓ|ïÛžFjtŸV¹•@aþø{;NÕòM÷U“T”ð×ÀߨV^ê²é÷Ýè;Ããr×c] ðþ) Ë°ÍSöN¼7ÞR¡´W¤Äe'_8nò𻑒æÌŒz{}û îŸz­K7¬ÕØHüÞJÿqsõØsÏåˆ_nôë &^Ø0ÇqÄú]+ÕÖ*·b¹*>ù•”ŽØSMôzÜãß®0Æÿóæúª¥»BËø´kâÆïüÛ6S¶E>‰ºÓtÏF[GÞÏ"ä÷É)«g´^QçŸü-ÐåAöãž7ûj¯+U%h9‘ðrëÐ.ãN}§÷´?¯öOhrpûäûþkRë›ùóú<·®Ë§ÐXê‹C/^Ú|uÿèštK¡¼ŸüJ@,NÜ틟ñì*¹zްE#Sø·°M{-è;»Õîíõ3NN¸>°«´W Š º|ˆ=2uÃŒ ”e¼\åZÝû9éJ[™£\Í–jÈÀÒ©7`܈¯é…Ï™rzŸOŒ`Ê´Ýàv¦bê•mìÔò†Ë옻s\[/‘)z§’e˾}»·oѰvuKSuNzbLdDø÷O}.?áÊ‹ŸÙ‚§>ýžÆi¨^@bÍüqfd£n{¾ŠÞ§fï6jD?·Ö.µ¬+¨s¿ô8é¿ÃÞ?÷½rrß¶·¾ J"ÎŽ­ÿ3Ö÷êœúZ4£piK¿™q1ëåW"d„˜s8–[¿÷ÌŽó¼ååÿu C«ÑÔQ––|æÞN»µ`Ãëž+U$> þiÐåÅûÓO ¿ÒOÆ?jÍÜmžWnU–a«Y[‰›#þüÇ}>ws&4œ§mØÞ‚FåcÊ[o·¶y²š‘ëÜmgt®ª.zäd(j˜rÿªÖtî4ÌskbÐÙÕ³g.=ÿ)3ß8‰O¶ï.šž•jÙshEoÛ<ÉŽ¡¨mêв÷oÚ⇛F÷ž|:4ç‘´GóÚô7yuzPešyô_*¾"[‰H”übýªç9·Ío¨™÷ayHEÉvðøÚK&½äÝþºué­';–õ³Žÿï Ë6c—Êá¾R7SoÌXú¨ËæFù;–•/œ?¸O»Ÿ&¼CÙyáÍ‹séJÈžL Ûn ϹMx°¦ŸÛŒk.9ÞoVw¯7¹­žµ;lypb”­˜îlçI'^ÔšÓªÕòWYü»â/ï»ÕåÎk)rkù(>ù•€Dœ¸;«÷ýÈ™°2È^Öçqdƒ]¹Û؆“‡=âbÏ®8û£ÝP4ã(Ë Ë4FÕÞí?uÝÅï±¾uÊîÉ&Y•ëÎXik¯ ή6ÝçÊ<©ÛL°ô\<.9mÚ¯¨ð÷SR^­¶Y¤ý®ý¼ÇĦçL½f^WO~«á~4šGæÃc޹_ ñ»±|ŸüJ@¢ì¨ë/$æLTíßC𮥂U±ípgÆ£¼}ã‰/¦”¹ï{]¶1µ{.k~`Ȫçé¹×ûé¨_n{ gG]œµâ]î´ýÂ㋤Ïj9XÍ&NË·äŸçf{ʶ[tdv=i›ä²*tÙ°¯ÏõŽGrÚa¦úÌYó¼»·“¤~Öå øäW"ùqR#ƒž½|ò"ðÓ·ˆØÄ”lEuMm³ªvµkW5T’v³“#‚ž>yùŽ·•ILeMS+»: êÛUT)ÎVf%|yzïÁË÷¡Qñ©DEÏÄÚ¡aS—êŠòýÕ’Âݘ§¯ß…„EÅ&¦rØÊªªêÚF&æ•­ª;Ø[è?SS¿OY7³•W­rz33ôÔÊK¹C2ë\7±†l65óÛÉ5×R…“º}׎§u*˜©ßfñ‚zG&<Í™Ûë}oþ‘6Ú’Ž e½øäW"¢²ƒÎ®]´lóñç¿ zذ^ïq³<'»Uóƒ'óËú:U&½áOØx½{3§š"'ùã™%Óf­¹œö÷ì5{{®]3±EEéŽnQþ‡×®Ú°çÌËWMëfn}‡ŽÖ½A¥B“BúÏûW._·ûRPBAë×ùoüœSÜlÄý¬+h3ÓÂnx{ÌXvôU¾Å*Ywœºtõìn6ù;Å¥¾œnã¸:4ï?·4PÛRÀ«ZÌ~ýn‰Cž‘/²â^Y¾håöóoã ]YÓzm»ö>eL;󢎚Á‰öÛ÷@ЄޠUGëÒ~CNoÑ"•]ñqþ¼;é5m¶÷•Ïùº^j;ö›³rÅ„–Æô~©kÿdýØÝÈd˜?Âzaà[OÛ_=-`ž­ƒWˆ`R±Íéð«îzK]*uºÀhÉ`øƒ¯;œsk;äù]ÁªØ¬k5ò”?*yБKߦڠºìB€.ûTjNYÛcK—“üUðêéÇG]X.›Veþ¸²çiî¤ÅðÉèÖu¶ä°Ë{ŸçNš ™ÒDböý »òÓÛLïy=çû5þòž'‰m\5Å?‰”ñâ“_‰eEû-ëÓmÞÍèÂg‰zzÔÓýè¦6ËÎñp‘v̘´££\ûìû\𣉯Noy妷ߙ‰âJvüóm£{?œ-n®„à»Ü=öDï×åv¼'8‰¯wŽî9æðǬÂýüØ|÷c[:¬:whJm)·2ë×­™íÛ¯xVðiÁ—–ö¸t¬ÿë;ú[ÉîwOvÜÃåÝ;̹]èEtr¤|zvÃÓpÇÞmV(âY—„ÇeäÜVªÛ¡Z© k §·h©”WÊÇ}Ã\ùVð£¿_šÞêÔé¹W/Í6’rÅÞ?,ÃÆnÕ‰?ÿDWð߈9¶æ|Cf†ß¹"2îêõ÷u+I <ûP0L©šKW[ñç eõ]Á£`Ú¼™yÏo¶pÒ7r’UYù¶‡| Ë¦~»%žuNNå'À4ŸY^ºnm¢Y&N‰ÑÁ‰}xâeî¤Y÷62ªoÊŽyxòu{¯êôILýfC\Ø×oçÔ‡%úž Jvm y´ü2\|ò+‘YQWÆ»tØú)ï½Êz&&Êi¿¾©Šº>«‘søµ{Þm $2œүߑŸÂ;xWΈMÁñ7&·XýíŽ6…&žÌˆó㛹mûø×ÝL-c‹Š:ÊÙÉq1á1©>5WÖ¯›-Û® È›ÀÙ:Æ&ZÊ™ ‘ß~ÄåæêÈËÓ]šÆøú.i$E†N~½ªc¿Ï5´*¬¬*i3ÂC‚ÃÿäÎrp@Ãæó#}ÍòT…±¤?¬³D߬Ÿ·wk9çNž­fé˜Y›i«±2“âcÂCÂ~‹ù¥@Cʇ+þI‚ ëVö´Ç`/>y½E‹UvE—ñuŸ†ƒÏÅæ¹SÓ¤ªE¬Ø°O!‘T¹¦ú{¹vR>ÜLŠÊdÿ(˜µjkBÞ…Q¯O?Žen¯¨³cy›çžxßsïSZÔËÿ«*5øâ-AœgÕíá(®žCVß*Ü÷){[$¿`_Ÿ{•8ÔD[ÂSà_…].(X ÷µºÉ¶j*bûäSý§Z—·3C)Á·ƒr¿³4¸Ê¬â%õ“è’Õ´«Z”š,†NŽÕÉ퀜ÉHÿgQ™ *Kñ+³Å'¿¡¤oíá.zèÕo>Ý{é„nõMø­9Éßôž9Ùû^\Î 6výÏöíµQUÄï¼Ðý|᪺ÏY0¥‡U4yGlNzTÀ•íó&x^t&Ø=võ¸·K lTÃùódA›<éYÝqÀÜ9´«]QExÍúó#ðá³'8pëGKI XÓ¹ƒHz6l:fîŒ1ÿµ°5´šÎN }tbýü™koEñ§ß,ï4Ì1èxŠRاCçGóÔ,›¾«7-ÚÂRƒ¿ZÙÉßîXs2Í,ñg€¬ö²M§fÚÞ‡¨s™O¾HèÙFëï×Jx~òé_¿ËÂo\ÿš^/ß@ža·¯+ØíÝê‰ ½²ù®ÁP·q©Lnñ÷Iê«ÛŸS:ÖÁxÐetùÀÐpž»¼õ¾7ùG®‹g_p¼Kñ/gzÆsl˜”g•m†,šLû"ÒËŽÿ Ò~΢aY}ídÇ¿9½X¹¡eÑ®²Ê6ªe¯IÍCŸ}O#ÒèR->–A‹éž½,ŠÒ÷J~%“zhä4?aý³†Ç­»Ëš‰n CÕÔyàÚ;íÛLnÔvcNŽM¹=y䑎Á¤|áÅ®ÊÜÚÞ»ŠHî`(:¸Í;ãì8Ò±ãûyÇ:ÿYûšå?üÇ߳גᤲóükçç5ÕÿûuYê•\sÿæyð{¡ú×Ñ5õÍÊÞ3ü Ý6_ß?ª–fÞcª™¹ ^s½³Ûâ­>¦Nèÿ>=vî]×]-ÅÚ²¢yµ³ªÍ×ú_šTCt8¦ªy“Q»·jУÞð‹ü†YçŒ9ÔóÆP³bâýw]Œ‰¡Ô|ûÓk#, HŠlM³:Fpÿ<7|ÿôG§¨oô”û¹§ìMMKº´ߢ¥Qv™_÷š÷<÷§\ÏCOö5ýf`iUm;íØ«Žmg>HKŠ–pA†ûGÍ¡[C¥CW©eý¹wî]r›¿Oî%ž}ÓCÛÞ4þíwÞPÎûýœUÝ,ïžÎŽ~p&P0Q¥cscqûM&ßy(UªeÎ"Ÿøû.ìñ»„ì:*e©ß8äB€./X•z®š¾´ÖâÔTü©©k_¹.+ö•ŽR_ÚõJÚ™ëÕŸ1IŽ:#ú“HZcW°Ô•Õ»÷¯%W´Ò+â’ «"Ðq_~r±šTÏ,Åâ3àô›]´-¿áJyé=ÿŽðØ«Ùåе¥Í ü-À2h³öÆ®÷Õ‡Ýä7jLõñ\÷ªçj µ:JM·øìè]¥ Íf¶[¾Éíx×süSå17O¿Kiæô×â2¿ìŸ¼î«pÒzêÍ+óÅ·ÿfjØ4kš÷®¬ð“Ó– å̺Ëoc_Øj³ôÏ;{à•Íç©|úëÀâs‹šI¾°ÁÀS''Ö(p0F%«¡‡.Ôh¼ßU0ýÎâoú®r,ÖI„ŒÈ7mK•ZMîYPzÅT3­*Ý'¤Y¿ƒ? »(²«ê—p´œß¢%]vÉ/¼½î ¯iTuöå=yÓ³CÝqúÙoªu9[À£"d¹Úõº×&Wù #o^ Ik`Ÿç÷RÚ§Ë·r¾ ºzÎøÐkÜC^‚~}êqì³<í=8ñOO=Œöoز½¥¤ß]Åý®È·âS¿ù¨vçNò«RÃýßÄf9åªiïöx?^r§Ò¸½óiÝÇ»õ‘U7Ãtâ–ñ…¦g>VE·Å-Ï{Q™2ïï¹ó«?ñ½ï˜Î^‹Z~‚š¡é<{uû=¯ðsË·ƒ;_.ÜÚ°h§_ø²ÓSÕé„­¬,ßÞQ¿D~Ài›ê–p~–ó[´¤Ë.éùö#Â6FZ=ÖN­SøÚ1 Ú{-v::ö §ÐYd½X†r;~¾x'ÜÓÞBäû&óÇË_ø7Õ\z4s ²#y ¢3žœz•Ø£µh#礀3mŒÔ\ºÚIÚgÅý®ÈGAÇL—œý+ø÷ƒ]6ýÓÇg ‡©çêµÀéÄÄ'ÔI¸ô;³Ýë¶£¹üª„K'#5#wŠ­Ä–Ù†ÉlÉ ¶’èA<#%]Üæ/e¯øäW"$õÃùÜNû¬¦“ÿ“ÐdT±ê€I æŽÈé*êÆ…©.µ ?*Ù î'¾›¨ºm krò"ò}dÉsPL¹p2·O~Í©“œŠÐ}-óû•CÂ& –;HN?JVÚ{m¤Úýf½¾ú.¹_qלVh>ÁÍDìw<ÓÈuBG+9‡ÿÈ—>¥5t(FKý*Ü|CÝNº¹õjx‹žbRS1e%DäŽî¦®¯V²ƒÈù-ZÒeÇÝáæèu×TüU¦Ùæî›Mì{'³Ð9d½ÌZµ3%ïø-ŽÎ<Ška$üyÁk•‘ÓU¯‡£žiÅöæä-¯s‚ß¹w)­ëçþ4M ¾xGPuΪÛÝQÒµ_‹û]‘KÝ ÷—AJt\*‡”BïWèrEÁrˆ÷˜•.›ø ‘»¦lŸöØÃ¦—¨º((`^¾>¥ƒ¡ ,ò œ™–I#œ–Ð’9™i"‰’(¨Ð»ÔAY+>ù•Hvì›û¹…´ïæ"±=8«BÓ®¶Ä?§ëÛ½€ßÙµ ­Õ®ëb"~¯°´L ¹ÛÆ/ͤè?y{r~¿º‘Ûw°Z¯ö¦E©üLxs%Hp[ñYei²¢q cBøçâ?|ŒËj®.æ`mçV_bÍÚ]k3Oúñ[¾~¹Ïq0,úÑœ©ïÜÕžøó³LÂÙ^u;¾\á9¶{“b]¦Ùir‡pPPU,ц¤r~‹–tÙeǽy õ[É©k q?Ìx˜ú »Ø’;o ] Ì÷²M§¦:kQ§è²žžz‘ا°#aÂóS‚ÆÛ5x•Õ;¶Ð[¹7†;ýóÆÕôúv‚Ï{ÆwŸ«Â •Ч¸ß`(ªŠ,1-1-[êó”ðoA€.gÔëÏZÙvwßküóz¯¼f^tÚݰ\tQ`©é‰TÑýù•(›‘°¨%ëŠ.9*±ðjñ²#E&Õtèv)[Å'¿IÿùV8ºÑ®á` ù‹J¡¢£­: Êâ+ümD:©PXÅ‘^‰Íe™ÊšÊ‚ƒbfzVÞŸiá¯C…÷hÔ¬_©(¿QÒÂ_†n%žlEàÂøˆ„Lq‡^-[{}‰;Ž¡aíhLüøçÚ9?¦ÃâTA[ YÖoU§C9u×–àþéToѦuó¦\\\êÛ«Êê Í)F ×áÉù-ZÌ²Ë ¿µïÄë1c“+·ÔËAXó™ñ6wŒ“ºU$Ÿ aÔ°Õ$o ¼è‘ËþQ«Ñ­ #aÒý3o“Ú Úx$½=ó0§5™e'ªS ÛÞÝYeïEÞé§‹w#æÚ™ó_?û×ý³¹;‰ï@ÈSÜ¾Y92«v€‡]Þ°Œ»­šéum>¿­Xâ¹i«_´]Y·8Íÿ úV„zÝgþ ‰É$Z2yÿµäˆ`î’‹r%g’õ1*wJ×¢íëÓ–©â“_‰dƇç˜u+KÕºU;7Æó'âÃãÅü ’¦µIžcÜß«÷û{œpÂÀ²h×2c¿Æåy¹2R2Ä|uÍ¥¹V¶w6Br«ò÷[qF³`êwØzÓ;¦õä«"ÛĽ»}Œû·šÁСuÇ®½ú èÙ´ŠZ±¢4CIûË%§Ï¢¤!kr~‹³ìÒ>œ1y¿ØN~57têî ì¶ws¤zq¶®™Nn—é¿ÉaÿäéHøëÖ•Ïi.ü&+©Á—nç¼ÛŒZ·ç•¡U§GÆÅû¼÷Ä›ÓbǘSuóœßOO G®7lÙNbÂbW€“‘"rqEµ’=u2„]þ(Ù÷î·¡í!þÑù˺©‡ÇÞn^öKš©eUÀ<túúèKÊÄ*’°I·dM+;}òXpÀÿúès2±”| Á|2£^¿9ž˜Õ-ʸZe¨øäW"ÜCLn[Ei~‡ä91š‘*Ï8ÅI]=5z u²Óó]X¶¤Úq ‘ý&ƒÊP¯5ébp³£Ëæym¼ô!å£Þ\ßÃý[8ªªûü ë§µ‘p†¼pü>»9ãp$E'Éìˆ4äü-é²Ë³9Ò½8SAULÉÉcÿäéHøåÒí ª°©a/åϡѸku~uÓÀÙÝžÜçu2È|zêEÂmyí=’¸ašNB¹ÈJŠ^ý‡(éêÒ®fÅ?y\†âaè´\´ÈùèØ‡Ô%ÃoÎBß»[Ò½4õ¿Gź…-s—oÎiÉÿëŸR[Šé…CoÉÕ™{îå,ùÿÕ)®ui!lj{vé]î¤Qý:†Eù|•¡â“[‰pƒAnUºtÁ€“žœ[¯£ ,Õ»ˆŠ¢«—D§«¨ÈBXJ¹oêË?¼žQUÆÃHH—¨òÔ‡I$ci×ê·âb?¯˜ ß+WnÜñõóõ{òWeeúÇ3sÚ^¿¹òþ¥iµ$_¹l}K}aýëï°¸ ñ³Ë–œß¢%]vy6‡ÿâ’–‘\ðUÆó/PVû'OG·gDO¨R)2¬3Û©GmAÍÛ¤EG ÀšãϽ³AÉmª’Ôï ÎIÓP>2ã¾ç^å^¯ˆ'°à_€].±-®°¢7¿³Ä¯½“·N6«z)¯T±1tö¨E|sº;“o§N}\\»8C1õœ»;{‚!“Ãο¬.Ý$ȉöÝ›;Œ*Ñhân[Äú2S|r+¶–±¦°n1ök¬4Í 2¸ó '´ŒeÓ–¤`lmSáêEŽÎ(J«¶v%-áðˆ€ð4"ëû-NŠ5ËàÎ&œÐª(Óý¦ gÛª?÷oáÓÿúòþ­ë—ÏŸ0 :³™ºøÇI ~!ìíŨd_A^û­U¹^‡¡Ü¿¹ë#ïyè<ãFNŠݱì¶çùÎâ‡M+Š…³93|YØ‹ïiĢĎhr~‹–tÙ)V´¯$ÜœïÏ>'{ ¹.3* ¨°ÐDnûGÍÁ½ÒA~G”gÞ&ut 8-Ö¹j—¦DúÔªÙ¹»¨î>Çë]rñöù¶J÷Î OZvl&±¡|¤G¼þ"Í®4pr“â´º(+Å'§aê:42%s¿¼sµc+‹¿dHV¤_îÁ‘˜7vЖãa‰¡U˵*ñ}ÏŸzüê÷i6–t«™ºu\­Éƒ`þÔÇ#gC<ªW“mz :ï;¶²øQ\^ž})¬¡rc»|‹ôâd‹ÖAz F§9ð´JçSü–òôzpjçúô¯»É®X¿¾>yËï¼ûšÒšH i2#ï·¨LÊNzLSâÏßœô'çþti"ö¤@vô£sAb—Óþah‰t$Œ¹}éãoõK‚a]Ûä½¢ªfîõ˜ç¨&fgýê£*ÚPòå$ù³ÿ7Ám–] «â^oJtù¥T}Œ÷ u­÷ñûx}Û0å@#§R^¥bcuZ:­ÚåE9Ñ…¼™ÓkQ‹‡KЪóÌŠöݼ_aÐTgÑbVÅ®Ë&XÜ\•s5+òÖ³ï²vÔU—fÉY‘& :œ;¦‚R ¯éÅ:£ŒŸœJDÙ¦Kký5{øÑ(ë®÷‰/='ˆ‹¨éÁ×=6³4tíl-×k{)Ztîi=gQNø}½zýÓaœé6BP´tïm5Ñ'þÔ;ïåwGïuÕ‘eÓíŒ;Ï}ï>BLÔìÈëë/ ‡^4ríhõwª`))sŸÎoš”š&“ÍküãÔÅŽœzÄŸJ; …*Õ:ÖSÚͯ$}Þ%«¬%á)2#ç·¨,ÊŽåª]Zé¯ÙËßœ˜³›|×4î æ¤@æ·3|Åšœö¯#¡p´èo—¯=Ôº’3¬³V·êyÃ(SÏÙ½ñ}Mxí=NúÝSóœ"TkÔµ¨ ìŠ+5äÎA?FbÛ±ŽL?ñP² Ë1†v³^¼G/Ê|0o~RÙ®]¹Æ´=S;¯\.pYËNê>çg6”®Æ1+æáÚþn×lŽ÷òW7UÇY;GnµCpN4`‘kãÇFTWÿ —ëëÙ®û‘Ü‹ ³ê/ÛÒǤػº¬Ÿ|JD­î¨~&{Öåô{îéq®Ç‰ Ù™¡G&{ Gw%fýG:Š»H² (U<¹¡×˜Gü<ùcã =Ÿ®jD³P©ÚFKGä4œÞ?hj·W;;ʰ”³ÎïÓmOÛB{r-ó¸*'ìïðZùR…‚o°2þ»;þó—„ìf겨ÛÏJŠÉ‰@U¯ˆãÙ1tœzÖe\}@å®ä§W?¦´«Wbzr~‹Ê ìhQ¯3ò?ã½›r.ÒsrÊfK겎ٿ®Îõ{o"·ý£`ÖºéÌ ~Õvàö;rªsëw¯ù÷oX¶qóN–ä5ï«)Ùgé2AÓ{V½nµK§!Éüùð–ðB.Uº´µIø·¡ðÊ5¶y¿5—9­þJM%¼?{™ÀÐh°èÌŠ‡õf<Êé°ì7ÇÙöþ‚§w°“u³ß[3{æ’sÁ¼¼bSÐ’uZ¬:9óËrÁ÷xÜÅQµ¿Ý{hÙÕ ©ˆÎŒ~´eLï‰'¿åÞ¥Ñnû‘±Å¹|`®2R|ò)•Ú“6ß:ô¿Î(þL?÷¥f׿Ô×Ê'²£}<Ú¾–,˜Vn½hR-¹§(våÞ“W7X“3 vð××ÎÍm¢_hüÍNüè÷BŹ©iî{ƒmÞwÃLoG¯œ×{»4S:qÙ»»…غɬøÀóWì‹ut•³Äß ¿öwëYëñʼnöù "íÓžþÖ ßºŠÍæM¬™¿)™;U&»r~>ßw%¬ÿ0³Â‰÷fNºQcÔøõ Å}²ãî­ó^ÄŽe×ܲˆÆ4jÑ¿yð„š¿~íkz½’»pª¼ß¢Å.;zTëL™ã¼5gôòqiÇ¡vö1ÏWiÌùórµ{Ï#’1—ÓþQ®Ú±™Î~GBòÍO°juwÊ_™«dÙ¾•¡×g^Ÿ½¤7Ï„w×|B9ÉŽyx:@0aÖÍÝ=Ë2èrNµÎôÕnÛ»ŸK”{rÿ¸7™ÚÆ•+èëh«±¹?ŸÂ>}ŠÌ[fûïönS„8X•:Mh­ôèõi ØáÛø)%— å÷•MÙÑ¥Rcúé]ë ½˜ÓÖ!éÉæ!Λ‡h™V³¨ žÇ-½Ÿ‚ÍQh°xO󃽖}»@¹ì†v½Žäê#ÑûLÚ¹šXo¡áؽëôÑœ/Õå‚ã»ë®à¸b7b`uÔ?—mÐ埢ÍHïakZ슒#ÒŠ‡dý þ&íB˜ÚΞ>ž‡®º'zñåLn`‰+l)LSsMI[ÊP­9ãò•ä¶V½ ÚÑ'‡¿ Ï?›yï=7v¨\hfaUpß}Ñ3ºã¢ûE:’ý;<äw/Ë¥T{Ü‘skºV(V‘1 ÛMï¡{ãµëÞì<öiÜÜ’kÅ!··¨ŒÊŽ6Å*ƒ=ÌÜfø ‘¯¾øïï_åù&Tª7ëÊÅ©·”¼@y즡‹›-y$2ˆN³Î6·ù`ê6èæ@î¼Ì½§ô:fÿ¼¶Å'çÄ»ÉÔ~V%ø>y@€þ?ÀÐj칬ù¡wÄ\7ªlbj:ô[ç×sÖ““Û·í;rÚçC¡ã’*9´î>hÔ¨Aíìu¤yÓ+™¶õ¼2æùÉͶí?é÷%߉sèÖèÐgÈØ‰ÃÛZéZjÒ(SÅ'ûaé7wý‹û™5‹–m:ñ¢À†—uþ7Ësr×ê¥Ð®‘©SÒéÝïî^¹bÃÞkþ<—vµ–îý†Ö²±Á•-ÜVú¶qmçÚõÛ__è^S1wjݶc·>ý»5ª,MÇ;–Aë•‚›­šæ±âT`Òß*Z¶Ÿ¼dÕÜž¶Æšaê6YèÖûêþ=Ç.ÝöýáKäŸBÎÜkµ;úâÒ¥ .^õñÍwéAz5»4sZ?'ƒâ‚¸±Ö‡Sc¢¼ß¾ãõTï’ëIÈ#§·¨lÊŽ6†jõaGº-ž6ÇûÚ—|mh8ôšµrõ”6&Jéï zzd¿øW$ †z%nH.¬;Û¸Yg+òRp–ˆ°êu/¥„™_O­»3´nï¹]+ý“Âè²FÁÚ#ãAóIlÓ!·Ó†Èoù´iu¹Ã)Òõ ¢häÔדû·'ã÷—7/Þ¼ÿüõGTüŸ” –ª¶®žžA%ëZNul i²![¿Nïùû{Ïß›òóÝóç>‡þŒKLÎ`*©iêW¶²­U¯–¥½ÊŸòQ|’ȺD˜vÝï>ßÏÀ'þ/ƒC#ãS²X*ZFfÖvŽ œì*¨H\ÛbâkÎDé7A½Å±Î1içV2i6fC³1ë’~¼}òäå»ÏaQ¼TV×Ô­P¹ªmZ5­ô%®"Cͪݤ-í&mJ‰üðâÙ«wŸC#bþ¤f³TÕ5µô*ZØØÚÚÚTÖ£ÿ>V6o?ïdûY¿?=ñ{øòChT|*QÑ5±®Õ°™‹¡’´‹cjVë0~e‡ñ¶A¹bíùó¹y!1ìýÛwƒ?û“˜œÆa«jéW4³²«]ÏÑZŠý!=%ÛÑ Û®îs÷37lïê;󎵧Õ(D_G2x‹D6eGwkz¯¼úߢŸ÷}ý_ú—B”µŒÌ«ÕviêTEpæC±ú¼œyÒ/Q–ûGÙqu(gµtó*Ú-æ, ±p9}W¤l_û<çvé³›ç:ðo@€†òCAÛ¢N —ËT©`רƒ]#Y/·ü“m‰0T*Ø7u³o*›¥ÉS­’Có®Í‹¹£ê.ª»ÈfØÚVέœe»Pñ¯¨abßû×Zþ/Ūè¶t¢åµå¼–¶ñ']î:¶ä®I(J>oÑ’/; C¹‚C«^­d¹Èý#,7ÙÑ7¼¶~åßÖí³b„lj‚Ò… e›JÍ©k{nërâ7!œÇ^+ ØÚ¸”úÈ/ýÃÎ9gø}X΋½Z£Û,ü;  Œcê·_¾¬ñÙѼëýÜáq`ºßØ*%7€Ù¿.Ï]Î ’T¹i°Þ™å4”yl‹Á[l³ŸóšXüç͹ÞëpGýÒ¹Z€¨¤g+¦œá÷¨5»ÓCþ—y‚‚ 倒ÝôÛŸ:‡%ñ.ÏÃÖAL‡e>âÂëBJ†ÖU5Ñz£Ü@€€rAA×Ò^·´W@CÙ¨j £Ò^ h h@€('è^þ(;€²€h h@€ €h h@€ €h h@€ €h h@€ €h ‹ˆÁ`”ö* ‡Ã)íU(“ h@€ €h h@€."ôZøÿ„ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ѐ @4 Ð4 @Ãÿ«Œà•µªÎ7‹ãÖÐ'£LY²}ݬ°õMG>çÞj|2Ư»®l—.ßg›é¸û¢ÞÃ'üDK ú ÈÝ„¿(èZ9¹þ7~ŽG{ fñW´¨Ò>[½ç~×)#êi1h?»­SгnЦ÷¸9Ó{تÓ_¨Å_h©Óå6‡¨v½ñóLë"gqdÿyaÓÚíǯ= øžÅ½CŨºS«næLîí¨+Õ'ç¯w£¤7gžÒqXûéÙdK჉wTn~0–º­?ôþ×].jô7‰æþ,ö§ Ê&h•ŒØOŽy=8vÒÿ®ÿš¦Ú¥”¡“_­›2wsDmÓ¡ÃêiÉìçOFLð½#‹î9õìÞÊÌËŸ”wÛú´}îgžû"ßùöò=¼qÿê»g§Ô’ïo7/‡Ÿ`!8Œ%½>t-Vž¯ „ ÿ¯,'<ø58ƒCM$?žT»Ó‘8BLÆÝzäYC‰º“¡ !]%Úÿ¯ú»>\ì¬CÝÌN‹ýì·kÚÐÕRɇuvy6ÕZAÂÓå"%ðȹ™,I¸uÙiq!÷vMºêQ Z5n÷ˆ§S¬þÚ6Nv6a2Ëh¬.ÊÊsîy´ÍIÏÛx,žÔÝÙJ';*àæþž;'ÆûLë4Ó)pccMyî“—®„[™K8zù÷îg6Kޝ Àƒ ÿ·˜ÊÚúÊ9·“tTøQ™¥ª«o` ,:_zÄÝí‹–ï8ëû62•û‘ѵvéyªµ–˜ á®IvÄ~gãA ±]pÞzߨ)[o}MfWh0tåïÞZ–»êüû¢]£×‚=ÛÇ×Õ¬{¡% Yçh99€?×Ë1æì1¬ÖcntÔ*ÚÞÙ:£Þ^;?œµ_ø‰·Wƒ{ßoo<˜»ævËŸlJ›Ñkþ(»UÁ/§Q±Z†p’>žYé¹úÀ•Ç_9D¥Rvƒf,ÕÝFøV»¶œ¤w‡g›¿÷vÈ¢fÑbØÒ ÷‰ü‚$íÍœê5—~áíÒ¯<ø¿`þn“±Ï¹À•gÑØKYa'fn åÝÒr?öìx/cþ±ÄÊÊι};»–ëÆ„§7ÖTüxl‰çÚ#7ž…þá~Àt«6í9eù²áõŠy‚¢ZǪï/}|¾ÿzø¨‘f¼oJÐñ Ü8¯Q·ò³¿rg”Pâ÷§4%ÿ— ÄI ÞÖÕiôÕßÜ›LCk;턃}÷Íð=ycÓ‹Ëc«rÁL% þ‘8ôPß ªºtì¤~éü‹Ð{[5‰Ö :ÚÕ¨GYNÂÃ9..˹7Mœ;ÕSÿâsãî¶‘õ†=y8¿Ë(çñÃrR}øÿvë ÃK]™?®î{Á{žŽÛئÜô,æé‹ê¡Y(} *ŠL Â]EUEjî¨kÓZͦh¡Å"É™?ý·p#OÕn3¯¬Gb~ŸÔ^ÍáÓ®æ¼ÚLq%rÁݬA»¿ÀhÞ£&µê™ÖÖgËzo°ÙlE5þšÿò™ÓÛçNïåøJ|Ã$¿ZÖÒiÎã î :–vñŸŸYÒó²ïš7§Ø*KÚcY§ºô?Ç}”QÑÁ^çóöÞ®—µ³i­?³À•§·—8±<¦^ÖrÂ7ã<%›1×~UÑQãÅÚôà í'ÞOçÆN»í+DÝ¿õöö¶Q.oRïN,Ö M§.¶—V=Ýw#bØ0Iýpòl!ê:Uyõì`.IÅ!Íþ,ýOüƒ  —z`È8êèk;Ã÷þÒ&:Ìì8¿™ ›®útkòÈ£]|qÜL?;q~êÌzõd^M’ùuG˪#ý2bOÌÝ·¨ã Ú9!ýÃæ‘Ô»ÒXßÀM4™¡{ÚÚ ½õÆkü¡¡¾G7c?¼“™tûÀÓ„®Z„d…_ß÷Œ÷<ãjJxú½‘Zâ_œmÐhÈØß?2*¹èÓø‚àdćÜÙ8Ñû3oB¥iïšj’6„»&lþ®‹¾óuÄã¯^NìÀ%ukÌåδ}{“-_F[&ßY½åΟä×Ù½/74oª&¾DNtñÙqÝR¯z­å_ ©9ûüƒÑf,’þ~™¸u0—b9¿?ßÞ8nõ'jÿ4èî ÆzÊ_ó(ÿÖÞÏ‚Æ;jfe2$¿aú¥ïA¥g¥¦[®¶VÊø¶¯³ÝàkI÷gM<Ýûzߊ™b÷Ø øÍ3©´§ØtKàÑVŠÙ17ÆÔl³]úbâa±ó¯|FÈê®4öRZø«/T; :M-”þ~¶ºNÎü©ïŽVÓÑÑ­:éÚ]ÏšÊióí}Êx¸aß»QKò=“ÆF»v©²*(äñ¾›?®”ùéÌéoÜÝÚ¨£ÚKÁ<‹£w¬äýY¼O”WÐ…ÊŠ¸¶ã>/&(´^8­1u¾–©ÓhÊÜF«ûßçdøíõ‹ØÇ0·‰FýICj¨ðn°Í»Žn8ÊÏC‚.>‹õ°1¢ÙŒ##ôÒá·¼†íûÔ¥Ú²M;¨Ï¼å›íÐ/vh›Ñ-•î\OK¸yðy¢k ¬ˆ{Ÿðf7í;¢Žš¤§Gï$þÕÙ•:yn’0À½zl›Q·­[Ý+0i¬IÉ£êðú›Ywêb17ð !ªífô¶Rä›z=«ï<ù‡Ä} KâeÉ%âNkgFï_¡Ð3mÛiáú^&¬ô§9ÓVS¼G×Ñaq÷gRÖ «Õ…¡fßB7S±k›ù5AŸð602ûôº*ÔÓ¿=ÉíôÆ`)ò¿Ô£¹?áx…öý…äŒG{/i5ÜRåòåò}ÛÌCã.¯’ûQH ÜÔ·Ë4ÿLbÚwûø¼ôLX-×ïYÔMA2¿|™+qÙRS±ëÓÕxý¦ð+ –¥q_Äep C&É Ð‹ÃÌ8\Âþ,ÑO”%Ð…bUh3² û©_fæ­EëýÛ,j¨ÅÌŒòY¶ØŸ÷ z‡ÑMtóÔ>¾^µòVŸMmô™ñþ›ÖPƒbšîN:…6€Îøó;.î¯G êZê •Z÷²#¯Iì9ïóßö1Uàü~0¿×¤©zU»®Ø6©¦*Ѩ?¢›áÁQaǼ–ÿâ&%f£±]Ír>Îl O7¿Õ™á—W¬¼‘É6nïáÑÞXÊï†vó¥Ûzv;—î7eÄþö׆™³‹»&ùIQ"Œp&¿:û×ÇÈ4b¦*i¸;Sf$®ž¡Iü€ÚäÕK’vsù¾ ×)v*¡§§œq.ž0l8IÒÕÈÕœ¼þF2n-ßó¶í´ª™¡§mûš» 6ÆLœM¾Ÿ9ò|¾£‹ffè™E[B$®9í½Ä4è°|ní+s^rR|FÔmõv‰GŸ&ÕôYqîì_ì±ÙŸ—š+´îÛPË“š;+>:9›è‘_7—®xMÝ“žø'£ø;\ž¯[…M[~†'p×ÈypK#&IÍ}TrqT “°?‹ýi€ò  pl³~»6®7êj|àgýժªþzû)†÷H¥¾{6u2ÈÓ>ƒ]Yãh[ãóÕ¬Ùß‚¾§ðî1º¢åÂ?cþƒ-ó_È›}o›1[§ìo²öCü¹¾•Í–:VLyÿ2ä!Š Ý7UåGµ:ÃzïÜéÏB©Õø…g—Å?=MüFgFùí\¿™7´Æà)Rh^¤ê¸fC»Ký¯&¥Þš8òP›KƒLÙÅ[“H.‘lC;î/‰L¶®Qµ›5]f9(qgÊŒÄÕcëØ¹ðHƒùÏ2ý§ÚWÜl«ÿ;è3ïây N‹· †º»Ç”ÆÎkµi˜OJöSãmv†ñÁJŽ•È‹9­BúÍÇuÖð=—H¾®md~ÙÑ$!à³Q×&š'ü¸óïÖ-¢$”T”í=. wuߘwoØ{ò<ªßaà mmÂÞÔ$ϸ‘ùÙ¨†N[ý~äÝdlËÄÍ·3}F¶íöjý¾…Õ‹µÃUú¹mÙÉÝò†ƒ[ý=b¤äâБ°?%ïúïa( ÄQ²y.ÈzË‚¥;ÎÞ{÷þ-7¸šÔiÕkìÜÙêþ}•BÕú«n/óñ˜½ó7=kVm;zù–®ºE»ÔC«ñªGOªyÎ[üN`೟DÕ¤N·3—Ìêf#hmªâ0¸¯ùÆUÔ kµvãZ‰¦yñO—Û!ŸU©×¦•Û«½Ÿ‘|}üèc­Ï÷«Ä’ùšH*¦‘Û¦ ½ºO?þ>)ý{H QbK±3eGÒê1ÔêÌ»ó¦Ú²¹+\{Ï]™ºíÏ\2Ó='¦JX[óÁ'ï¦LëuäYd|h¤YûÙç—·8פÕÞh’þ'•w*ÂÈ}¯ÏzµÑ^Ç^üúý%ܪNJ[+Ÿuæè´?ibŒ.Â^bwÞôü[½«×ï»p÷EH¯J™¥]¥~»¾ãfLíU“wá›IgOü2mÛÝÐþ[ ?~ii§ôÝß\'^ ûõæmDj±{rtgÃm;£ ·®¿A»¤â`KÚŸ÷ 4Àÿ)h.5—]¿8» ~LѸŤÜ?IËÈÎV¶þÏÛç?oñ³±LF<㌼JLzÃ7^¾±Ð”k¯üÊYY„§kw½[H6þrk­øÊY!nÕ ß…*cî¥)úš°«LzÙ$zjýÍœÍyŸ&¾Dªv£½},Ï’v¦© HÜ>”ô†a¨Wï¹ätÏ%….\lÑ3µÆí:n¿È]qöä™a¡ç‰ÌÐ0殺•—ø–+€R¥¦£¼¹…>nÑÃûN¼»ø}\îTÞ•‘ðæÌ_:j.;"9"û:ßFBqHÜŸÔLEþ4@y… @4 Ð4 @AüA€ €h h@€ €h h@€ €h h@€ €h h@€ €h h@€(ÿ Fi¯ü[8Ni¯@† @4 Ð4 @üŸAÛ×ÿ[h  #Ð4 @Ѐ @4 Ð4 @Ѐ ôeRŸç=dEÈ„x¢!vN=BÚ2‡Û¼÷¿'d-!×ùNMªâDÈ0BzÂ’´Ÿ±yô.!Í·‡²KºM;FÈ;B¦¢%¸§!¾„¨.بâ»@HêÆ BZËh™PR @¦¸)Ö‹“„ø¢]øl1„¡þîÒHpç6BFç-… ¯Ü¿ý„œ¥R¬— ™ 2yˆþÊ'SÑ9‚ ÜZ’g€ÿOÐP<Ñ¡ndò™i„<¢îÜMÈÔB挣êƒWQ·Çò”*I Ó³!Ý©™YAÈcB|™IÈF±W9@ÈÁ·Z2•§é ¤Ò3€XÐP<ú„è n²êö|Z8§UK}–ª®~KHÕ4b¦`¶c„ôܶ"¤=!-© KH:!J­C5ªíÇsª•…uO!?©Å*ò+ï̯™GÈ-ªz›;C_B–R7©¼ÎgNµ‰©‡æ÷xª†›»z©„8S[j/x”»Q[IMZSÍN& Ö–CÈaBæBˆõPÃ|›E-p›`4 q%d !U Ú^(UÐ *ÒÍÆÍ©L*?¦&- qË;ƒÕ$Z¥ 6ÐBšT[ê *µ£î9IýÛˆŠË¢âBÝ0!¤U±½ºó! ¨M=Z‹ü_LªRü±ÈrZRuêÚ„S µS÷s£suÏ je.SëŠþÔ£ *so/¨v|.!Ë©öÔ2ßPϺLUŠ[ˆÛPò @F8T ëDÁdïÂçüM5ÆøDÝn@UÊ~¡ª`¹êTÇ,¾é3¡²u*@ï#d0!™„œ¦îïCÈK‘ÙÒ IݨDSnì%ĆJ«G ÙAumä§Øó‚šl¡xj5â©×êDÈB¢yBH B†Ò³/!M¨¦,3©*·¨ÅöT®+R/jEUl×Ì»ðdBöR7&â-ØE¨E½B€øç @@ñètg7BÜ¥˜“û ´žŠ¤)‚{tóÍ#%î+.£š_GR1÷3Xå'”j1B¨f!šÔ Sjnð=HÈpI/±Qð¬ÙT€&Tî ä>u»5!©Lª'âjêÅ^jêþVTz&Ô~MU9 1©µ%TÇGê)U y@'@‰@€Y;L5bÓâ‚Ϫn« îŒ,ê‹V§Ze„Qqö+uOcªÕµ(áÂwR¢¸OI»|jù|Â_é„üÜ®%ÒÁQ“jçE­p‘¥9ä]¸2ÕBzÕ€„ß“’›§Û2]Ðàþ%ÐP<±5¼Ù@݈.$=ÌɦšCˆŽ§aB…ÈTBžQµÑµŸ!ä!M©šÚ¨P•Ðë©N~ߨ{ç›_¸VÕ¨VË¢ô%%~ÑG  „SÐŒBfÈÊ7çPªMö.ªåô*šŸ§þÎ FŒ€4pl…„œ Æ¾˜NHGBªä›S[ óÓ¢úä]¦®Ÿr(okŠj  ª¹Åsªf·0}¨}…ªKfQ­“ÿb$¸QŸj--Âjé—TDæ'æßTõ3¡v‚±`†@‘“8~¤Rí§ûQËYA5¹óËJ4Ȉ65¢…U{:‚:Cú/&Õœã 1GP-•ûPÑü!‹©ôL¨Fƶ·²'¤•à 5Ìœ•DEU"ÄŽ ²ç¨¤nJ%Ô^Ôl]©|œ™¯aa*Pý¨ÈëOO—IµÆæMHEjP¼oÔ ÜíªA5ÅÞ–w!ÁT t5^=ª2¾-5®ÈÃB*¶ T!@€ìt¤Zî^¥Ââ!BÑy®=•kÝ©æ u±B¨{ÄWĪRñŸMó·ß TÃâ­TÞ'¤25ðó{BþP÷o¢f°ÌÙˆ(ãŒHsaØT»‹zÔ2©Æ!¿¨q6UqÞ‰šaÕ¾9›ªcæ'xîK¿ æáçcî«$R­¥¨Úq5jˆ’Pê¡é¨~øç @€ì°¨$Ê ‘„Œ§êŒ+Ðyzgª¦v5!#W°¨@9Žª'–&Gö£4ƒzé5¦Æž›G £ñŒÊÜݨë•ð{ø¹Q/tœªD‘ú ÒšAoUüžº§!c  h9=˜jˆâEUlGRƒx´ å ‚:rj}Q£ïñ[w(S›à!˜ þ%Ð@‹JŸªB¥OQ…ÍY JÔ@ÈÞE] —¼mT º4w=ªyIT©>ˆÇòÞy7ßlµòµ¬0¦†‘ÞQÈb™Ôo€qyïük Ú„¬¥þàŸ‡ @4 Ð4 @Ѐ @4 Ðÿg¥½e4 Ð4 @Ѐ Pþq8É3€t d„sºµQ÷[Y*]®þ<×V“º/ãÇù -ݶ} Ĥ÷þ;{X)—òJB±!@ÈHÚ7ÿà,îÿ–M¬U¨;R>îÐt詟„å0ùÒ•m½Ê”"ƒÁïÿ¼q'ÿûýBœ„¿sÿ×®UÇH;õlU×3n'Í–+ÿÇÞ}À5uµa3Sö*¢ˆâFDGŽ7Uk[·8ªRW­«V­«j]µîݯ*Ö­ˆŠ""âTdO‘Mò%'‚lAxþ¿ÔÞäÞsîIr€''çÞëzêǶªÜªn |Ð ³¼àX›ctÞ‹ ]®/’â(ùeа³™ æê‚ž}×?Ê&“ÑÝþköqâFNì]çÅ?o:v#0Y$§o5|îæ­³Ì/wÓã.Ò™x;d·­’d£7{»7œp#Sãë_é¯è]­ @€lòÇZ«è‹€ DÂħÞâÿ뵨suŠÍøýÁÄkùãùËkz囸!Œ»2£]¯Á¤ÝiÜüŽ<Ï={ŽÍí«ã·Æ®>¹Ç<{"²UâPŠçÚ%72I®Óo¿õFz®v à?©©Qú¬ Åhdh€Z/=èÎ+Éÿ£N|3þ½d*´¡ãν L{~oÙ8qz&«ßï\s2PbçX£~g®îº»ma3 Nò6c´.ÿõÑ…Îo‰ÍÿãÛúkÕÞA¡ÈXËó¢øé#CÀG9q~>1lé}Ž^ègoßî^|rÁåïy¹›¤xm?"ƤÄ{¸uþlžäÏÒÅ÷“¢„ ¿jDÿú¾}ž!2óúmÙílÒþvë­«èÉ@i  ¼0y£(dhø(=èväÿÆãŽ]ß6 iY3ë !® W¸uîª*ýE‘éó8Q²êux›×Ç’-cmcC+-ò }úöUø¢=‘$o¿n¥½~ïVKÐPAˆ‰RÈÐ •ýè‰$uÔHI1ÇiUïíc.Eíž³óÇ{ó $›ÓÓ$ÿo±9èÁ,Ó‚)LùUcÚïëwuƒÛjºtÓ(ä´j o ” &o”ÄÒ^Ý‘ @su¬/9ãÏ`ÈêÙf—~{õhå‚¿;5LK$§e¦KôŠb^Df)?'üĸQûøÝ'-^0¬¼zsÛºäzl‘séMÜ6«.¸Rm!@@ٞ˄ PëeE<ðOÿ¿n‹:Òߊ-gý6`ËÈß¹Ì]ïÝ{C;eâêõš=°Îݳ;õNbqþÐõpRÕ·JÈêw²”_šñž{oXÞY ³7ª/h(¦>—24@í–úÊ#Xü?¹Æíëæsõ®šÛøß_Bþp:4ãÆÔú|žÁðC7wþ8sÕ‘ÛGvÝ&Õ]Þ²v¤;ʧ¤­L”A-—niÄ+yOPå @6H„¥@†¨ÅÔú¸¤ùi—o¾ü¹hyþG¸ª­§ì¾=ewá-…‰î¿N?OÔpžó A%6þ;h( &oÈ d”þÏS¶¸ß½åOÜÖËO,k§‚/þª9h(ÒsÅ C€,Òƒ/>+‚T-‡/Þ¹Ó©2âsµ‡ ðé!C@¹)Ûí~+*2¥ª3h(†Ÿÿ#dh€š ŠôüI CÔHЕ æA€€Â0üüi!CÔ0ÐPÒse@†¨I à#¤çʃ Pc @|&ÈÐ54|€áçÏ @€ ¤çÏ º)ô# EáwT!ÐP~QV6dh€/4`ô¥ C|¹ j;LÞ¨*ÈÐ_(høéí3C†¨vDÎUÝ‚jƒ3¥ª[P}!@Ôj˜¼Q塾8е&oTÈÐ_h€Z é¹ZA†ø‚ @Ôdå ajÕ¬Zº1Þ;€Ï †+6„aêsõTþ w  !@Ô|…B&oTgåÉÐHϵE°+58Iv“èV[’õç>;ެÑ#3ŠžG:ø¥ñ‰!@Ô %}Ýô\ •ž¡‘žj½–tJ´ÍdNÏPÉ jŒ=)JÊÐHÏÕÔ‹ËÔøõF ç)Ô–†Ó½ËôÃUò}O*z´ô{ú±qÅÛeÑ_‡iŽ'½S¤ozÓÓÓäSŸÂRÄÿ¨í5úÚ‰Ž5.0rœò˜†æŽ@¿,Ç.8™´ç0ýxÞ)C7J«ê—¥æB€¨ŽŠÍÐUÕ(ƒ Tç¯Òľ4½!½½C=Ï^GòüŠN ùÛ¨åJê©D7i¢'ÕiB'{ïe:,.Ã+o+Ï.Ü¥É÷H£ëA/Ò‰J{¾µ4@-…áçêCÎ_˜ú¶´µÉ iÏIÊ"šÖ›lôȬmØM»B¨gºó@²Ùôá4˜¨ÒÁ•ôšd›žQÚ.‚i ·dÇAÔϘìi÷S ©„§ еN3üE@†ø’‘@ü?!…§Jî.ZJ‹rW½Š¢ìÆÁ&U˜*Kþ•S!’èO¶‹ eõ×Ë­_‹ + 4@í… ð)}ø¸Ë#ažCßj|X¥P‡ø\ÒS,¿~O¤AYï(*· W2?š’3%ÿf§P\Åv¡B®l:DZòÇúáSC€¨Õp1ŽjÃÏ_ ÙÛ’\0½G]í(è­¡Óh¢Ù¶! ¦m§ÈÊž|.Ñ›ÜÆâHL®éŸž\þðxi¿˜KØEgVÿŽSÔÖž\¤°Ê~¦µ4`(ºšBzøRÕ³¥óIôÃ5êáArê4nÒ’<Þ¨ m ¢Ò° ú¦5{A~l{½V´©5{|9£Ît+G2ÅYÖ](å«ÿ»þÔö%y—UT4H CW7HÏ_S{Ùç»Ï¥ýéYÿ›qäiæTšÉ–sÈæl1KØå.ä«¶\»(TO÷ =(4@í…Ä\m!=Tgеóé šC€¨É˜¿Dx×jžy;Wu# ‚ d€ Ÿ ¾w†O¨ GaÑ“¡|'… @äâL©êÀ@Ð2@€†ÊƒPÕoî±H4¹ª›UƒÃÙUÕM€ê j5' ²B€4€  d€ µ.ŸT&L/@ÐP»…­'ºÀ¤š šKT'÷‘ãDψœˆÔ*­g‰±…+D=ª}µPSw?:ÖMH¤ºÌoärKnþu/7œ2ŸOÄír`ôõo•¸%ÖRAIgÏi z+"þ+cOõûÔÕTwÐP‹="²!Ê̽+þiÈfYyÑi""¢T#ˆ&Tf€W©÷LcE·Ð4J>v:y‘¥º oUVʹCñ’£}?yzh¨­„,KÓóßDCˆxDQ,(Ÿ'òeŽ#òg鹚“ÎLþZFë+˪¡'“éÅÑà­çg‡…z"Y¨ÓÏÒ^»ºv ‘HHnumÔf¸|R\N¨ÐP[eåNÛ0$êÇÒ³˜Ñ¢Du‰Úµ`IZªÛ&ŽC‹Ø¼ŽDÞl•&ÑH¢ßˆÔY.ïHt¨)›8áij³ØƒÎDÍØöââGˆ–)M$êP¤yåÙ…%Ñ6"¢h¢—Dfå¨jŽºþôá '÷¦Ó³—gCZÌk$k†_|ñP² ?dºÆ‡„*JzôrÅßc®qiâd­ÚuLËu«-¬Å«…©:þ^Òcmž^Ñqqº»ét\tϨc£_œ;Œo&'©@”ýìˆ×Œe׃²H¹N·‰íÖv|^+tHQfÄ[çvŽð‹Ê!âj6Ò8±õ/³ŒëÊK&œìïxxœ¤Ó¶óÚ&\ààí­±þå°¹ 1>_$h¨­äˆ ˆ‚‰ÞýÂ&=k³Çõ‰°qhφŸcÙÝVDº¹?1[‰f±K¶½+ÑN¢'D7XµÒ¯ÒÅ¡ö+¢Ð_³ !öD/XþþÑX¶ ‡Ejg6æ]HyvC4ŠíHª<ÕB"g=¥¡Ñ^¿pJ8|.eöUÉtNꥬËê›Mm÷aP:Ùã¾­í#ñ’±Þk¹àka7vºÛx¼÷òhÛV™«ô¡Ç†-üêþÅÕÆ†üè×ÙáÏ'Ú‹ê¿èb¯&ŠøŸ›íØàñ6¥Íä_9»öAlbF»5È]]pËŹ?aãY€&6¹Âœý+ÖÅ\bÝ‘m\]lb9[úx™ÕBÍ#¯þí$Õ‹èÑ‹‹aM§×çD^}ÁúŽÚ„ï4¥ý7+4ôˆŸdA·oöª’¾I½É6×›"ÏC±“êåÖ¥µp±‰¾¤Ç*to¢qðee…edšGœg]K¾{‹¯ÙŒ ®–álGuçʼny­È‰x³ë¶dB‡\«¹äÙFòvN­í6¸Ý o틈û®nN›šZIf–ppòør!@CífMt‘(%i"7¢«ìñì4vëJ(u‹h‘O‘ÇóÏ UÊBl„X*“Í»jœoãÚ…€È$w¹œÕBMÃ5ûº©Õ¢;(öÀåÔ©“¸×÷GIúHó&£Ì?ŒïfF%F±…èÝ.Ê» –‰ ËÈ ÐJušh(ÂSUReܲ3E1IÒÎ¥ÓX9÷%× … sr«É{à Z©ªäNÍપZêÐíh†%Dfæhz;Þ§{úU€H‘ÍÜß~bóÚ²IGÙÜ袂ÙÄl"#6zmÀÂ÷Ê"›åÿvºØÙžù£pNÁUåÜ… ¸šK©j"~ÝúÓí<ÆßÝß>@n¿‡¤´›ÖÀ4w€—ÃãJ+ߢ®C;…üé•§­­Æ#iö%.÷cŸ-¾Ç~ì[¢añ%+øè‡yÒù/ò&àËcò3À¢¤ûÏ·ÞӘꨯͣì`?«OìºÇÞj õ)~L>y…4ÔV>,’>c)yd¾ÇÍÙ|‰§,C ‹+x“E[±?ˆ†±…`Yö›7,íÏÒ†ô—Ú½ÿ¼‹òT 5O©ßL#ùÛaÞç/ÉyŠ{ß`ú@å¼_î=u}v†FU›–Îû ” —Ïô+µz9mUqç ×àŸ’*ÒQ”t­œ×÷Þåÿá7Ö®Ëvñöaò;‘¶ë~ÂÄD_6ãH®–€?É“¨I„igçßYa5|²$@óôên>¥”¤­Wa÷K€ µ•:;.PÈÎ\‘L4€M&Žb“•Ÿ² Z²‘é¼A¹¨Üƒ³s‘ž#†€(•RŽýê³3â½f'Ö'—æD¡lÆH~ØEyª…š‰£Óݲ¯JØé”·¿,ç¦)ô°ì£ÿñ/0ßÈØÁ’ûSü_—7z£M¸¢ÄÈew¯¤Ë›±Ù9»N)UKŠëë÷¬G_S–룽~&s›ó³CƒWì|—ž¾ñ”ÎÜû·„Ù®þð4^ÑAÀÍN»ö›§d¥\?GM¨DIž9ýððˆÇû âuh¸hk‡©V‚œZ5ö èÐæ'ÝW]’R•5¾ÙÒc÷èì%Omÿ¢¦ûMä_m:Bg•›ò}Š©Güç(ëåãVæ÷Þ î¼^ûéÂ=±‰Šê£·õÞ;^Užr^¹;nÁó;áBR×±¤ó®Ùº*UýrÔlÐP[5`'QžÆæ%O*²V™þrÙYä¤ìX¤>EÔ)÷‘©DÿÝe'ÖxÇnSØÕ K§@´„¤YÈæ([²1ã6¹Ó¥ß€W`å©j(ަÁôÁò§gD½Jë #íü3ˆêÓþlq ó“€¤1õ®n£”ö<6(E24=t›šRñ߳䣠9}‰Ñ¶‰ái˜ù-ï´TLòO’o£L>ï)oZ_å›=¶ÿX»_LJXÕñà> u¥˜øÀ8É£1]¶ Pà~¼à'@•üÝW·]²ôæîëÔEþËTßéÝ2 »÷çóÅa+çn@èÁ^>?Å,ìã~xò­.ÝûÌ<ÒÞ½³ç=C‹ÃÇšÙr%çTÍ­gBqõ ÖápøÃ4vűtâŒl#'ŽÁ’Aæ×aÿ\4ñ•^ƒ2ë)yß™©B"QÖ»Ô7Þ®Ý%9Æ7Ò'îu3|ëS‰ þ+žAƒCn³gø¬}a5ñëu±<°Ã¦·&'G Û4±ò¾7xQrºšÖ„=]ÇsH¨ã8Qóü‹Gy |Α/£ž’÷Ìo»ÐfÌ;G–]èÚÄlÍî:“.ÿ¹þú‹f•ÿ¤k/h€ÿŽ£Ö¶é>ϦûŠ]©¤9ó_/σ¹òö›‡§mλßì±(7ò–Pß4ß6$è{abî sãÃÏÎÛîÅäÒ…ñ­+ôD lÐ2@€4@e)8ïjh¨"ÙSν`wåˆZa×(Q¬â¦I'zFäD¤Æîv%ºI¤B’ S•q¡e¨i2##nò;pæíýéâûrò¦­ úiê4ɸ~Õ÷ÕœÀãOö>SâÔÄZÒW3OwÝ?TÒW\{ÛÝ}j h¨þ%JyWy’È"òb·ÍDרe½«P*‹ÎDr´>‘‘»6 Ô"·ÿzöê÷¤@_Íö Ù*¾mntèZçoTéeýRã6;Ýß¡m2ÁÂZMr0”’¾Š†FÏ@A}j §ø«ä‡ 5Ývin)G¢±D:DD¿³+xõ zH¤Z »–]Ê©‡¤ü‹\ûxñBÍ–þäQß~O$‹‚ŽŽmç5lªÃIˆ8ö»÷f·t ~9¶‡‚éö•ÒWEBâpËê«iþg ôUA¯ã£ã+¡9Õ4ÔhB¢ù¹Ë;‰¦ä.7$êE4œÈ…(ˆèÑL¶q6,]EØÅ,a‹ìDô'‘enÙGDKˆ\Åi‚ͯC´š ‹·ìHtm¹È(šè%Û—ˆU¸‘țՠI4’è76I£ ‘onÍâýòˆâˆ7…C\V&ŠbwM$šE$Ÿo×M‰®°ñìÓl”]ü 3‘tê][Þ™»;qëI´ŠÈ¼²^{0ÍeþÃÇ’%¾ýΡ秨~8'lCv½ê¶þ¿o\2)ÈwöË»Óù‡:/é¨-nW?±øñ1·¤D!¿n'ó¶ÿÖ’ŸƒEI^®Xâ{Ì5.BÒWU»Ži¹nµ…µ‡„©û;'é«í¼¶ 8x»Ek¬9lnC.‰²ü?\º1ðŠwJЏ?jªuÙbÍoÖ*ï6·9>çC_Vo÷4žñ¿qݳžÂ‘ñÖyÅ£]§#ü¢Ä}Ž«ÙHoàÄÖ¿Ì2®+}>ÂÔ/é«6O¯è¸8ÝÝt:.:‹gÔ±Ñ/ÎÆ7““4>'ÝÃÙ{ÅÎ[¾©â†sTU¬{šÏ[Õj˜9¿ÌO£Ÿ4Ôh D×ÙB]¢ï ®â³ë–÷±Ái> £b¡D݈숰 ܉:=%Ò#ò ²eÛY³é;Ùƒâ›2‘€­Š!ÅÒsž­,éËÖú,|ïdCãâ…ö,Dzµ­ˆtKø¡ñvDÒ‹³Š£s2{d‹ËçY³¥»ïô+"qð1$zÍZeϦ}«±ÏkØ6â<­Îöþ?VÖŸÈô?½ÆðIˆ"÷^g—é­ÛtÝw¹éYН2â·¿ºxˆ?‚í{æØ°Î‡Ž8¦[¦’]ýþäιĆº?ý¾sºêSû!z’œ™ìqßÖö‘øí%c½ÖrÁ×Ânìt·ñxïåѶ­2WùC_ ÿyT¸›¤¯J£©ðåÖ‹mfEfŠó¶¥a_ýôÛ®ñ×wºÛ>ÉñwmP·½eD„¿¤¯rŒ[éëjió w’ñòév·/J:*G·‘†zrÒ‹—ûDü}ÅÖç¼¥¹¸Ù\®Ò‡¾¶ð«ûSTò£_g‡{<Ÿh/ªÿ¢‹½Z¶÷â³¶kÄUpô›é¶UE>‰ñúŸÏˆóQÎþ}&›b¦T ÐP£…³XbéS¡ÈÚzl:G „Ng#¾Ò¿Î"¢ŸØ0³Ø.6nO´ŸhNî¶˪,j7faô0[%-ͦ…waÑT©¥G"ú±ûæÎ™6!²a¡ö;¡GžM,1K)°ÉÄ¢|»å³˜”ºD·ÙB6Ÿ„X>v"ÚÀ¶¯ààúâÜëñ,@ûxÐ)wˆú0Kü]Yš¿S¾½Ãg‘“𑯔µåŠÉˆ<š‘ö±¯êίÉÎÌÁ­7¤i‡©·Dôôߨøùêš¡¡GX_ÕíÛ°-ë«|“z“m8®7Ež‡"b'ÕË­AÕiSS+ qæÈ‰ï)h-»7VüùN”••‘@‚JJQÚ‹ha™Ç´æD¼Ùu[’BäzXÍí$/Ùš+oçÔÚnƒÛm‘ðÖ¾ˆ¸ïê~œ„¡µp±‰¾¤¯*to¢qðee…eätâIGÇÃ{-kl9¸«ž•¹á†;#*ð’Th¨ÑTrÞ·6›Í…n–?³h°IRJìn<Ë.a¹îf·üBˆ2r—,Xçw‹^ûi@9‡<ÂsZå;$Q• ŸG³Veæk­v¾ ¤2s‡½'²!jÇÜFö!š—;#ªOEA™-$¿ÍÈ*º:;3âC_UPæåö5 µ†ê:GIÉTƒnI:jr|©D%J§ÊGïvQ.ÜWcÂ2r´@½Iþ®/нõdæœÇ'|Ò öMQN9újFxܶ`ÐJU%·£rUU-uèv4 Ã"3I7onŠR&Ú9OUI•}´ÌΉ4§/«»wbhBløÇð ’F*Yõ1›4¯Õ[EüÅ€j¿Ž F3f™2•È“Í V)¸ö5;bØplþ/Í…ù¢­ˆ~G,¹æ% 6'$?í|kϼÌ&"g³‰KÙÉ鼈VVèébòï‹[ÂãÄΑgM´‡M›`©Ú…Ýΰc¡ªÉë6S¢W©”æì›bbW°¯f¾~{ûC_50S '¾Š„;ª(ëCG•¼ñWÚùuÚ)äÏÈpñ}àè~føAŠ˜ÂÕ4Ôhjlœõ6R»#ß9ˆ%ÚŸr—§NbãÍÒÏî;‚Ð8w6%ºœnæž‚ú¢al!XÆg‘·ß‡,šHÓFbî ¹Ó3ÊÔ‚h [Hg“§¿a•¬eÇJâЬ*§¦7±œË?Yà´£ÙÍùš¯š’rò§'/%KÜ.SL òújR‚oŒð+É›—ŸøœuTŽžª¶ ôÔõÙùZTmZ:ï7P.¼³L¿bZ нùB’ž‰cÿG—Ã8$ ¾_þg o¬]—íôíÃäw"mÖQ…‰‰¾¬£Ê5Ð2(gG%Žz Óy[L牋§§=u ˜ÿ×ÅÄ´#k߬`n€¾ Õ4Ôhvª³lþñvÐÞxv¦‹@vžô-‰¾.Rp;—ý+5”YKvøà’Œà™°êÀòèv|^±ò.Š!=ÕF ˬR)ìß¼@UÂA„úìÀÁ[,õz²síe³ç%åXŽSM¿d#ÐAìôvÖl¸½7;çG¹§‘@eãÈ÷üÍÚö¬Ç,Ñýg:½°^<Þ¨™.7!0òÄz¯ß¯K¦épZ¶ùãkeÞÇ);që×½½ÍX››é¹í‰tŠPË¡:â;XÒcŠ?ãëòFo´ W”¹Ìáî•tyó!6;gÍ@a¶´7ˆ’b³%ÃÆ1á«×Jǽ…ïR$s sÏö"*‡êþóÁÓ7žÒ™{ÿ–0ÛõÁžÆ+:¸Ùi×~óñ”¬”ëçh Y޳Ðe¾|6~‚¿{ïÛÓý—[Ëq›õn4¢©×EÖWÑ[ š@€†š®;YÛ@sÿb·üZ](r5o>;µ… [~ÕmÈ®À"`'„îÌÆ¤ë³S8?g!X/gÕ)wa* ¿ËjxÇnSØI0òÎ0mÇÒü©"5ðÙÔ k¶ßŽlILîä“1lü¸LÆlwálæ‰ ;ã^ ;…±iÐÒ«,OŸßoàãûéÙþº;¤`_´jvêB«–ùû*¿Nc ]ÔñSž¾aÔ -ÖŽ­#ùµ.PŸög‹Ÿ$…Œ©tu¥´ç±A’¾ª7t›šRácf¥8:L[RÌc"ï©.vÿ¨&ÞˆíliÿÎßõðÚ” ÃuXc©Â§älz¿ÙîäÕ–z?²UÊ__å›=¶ÿX»_LJXÕñà> u¥˜ø@ÖQÆtÙ6 \+ë6zç~(œV´;zÂFÓXYóPÒWæ롯@õ€ µ@6Ýùw6þʾ—LŒnÏæ0ˆorE¶WbƒÓóÙˆ/±ÁÚìê'ÄÒ°;û*Š›•±ŠÚ¢$âU'‰æ²ÀêÁNÖ±šåøY,G°¡q¢l^r\ ?”Øyñ–³§ðœ=bE4èÛ‚“OJ¢È¼‚%xé)AØË"~ŽÝËQ>ŽN›;¯Mýþä¯Óo½_¦gJŽTlÒÞhÐ7–³¾ÑÓ+ÔW•t×_owm¾×nWqz˜÷nºf‡UÏÜa^µN6w½Ô—.ñ=á–àïJJÊVÃÌ®j=¬1?ßa§7?}2müܧ7Bß{zp»Mê~nuÝÌ¿Þõœï!Ô]`»Åá꼉ï3S‚âôä‹ôUùFMξ<}U(Rhd¶éšÙ¦âWs4¬-¶^²ØZÌ*Á“‹›Á3ÑÁmD‡Íèý&__u<>Ò±@_-\ÀÐpö.ñ­¤F³k…V6!"›÷ÕµÇoì5}ª1h @È@Ðùܨê”­¤Ìð™ @È@ÐP9ÊqÉ1€êÃ)ñ|lPk!@È@Ð2@€†OF$™µ &@O€Ò!@È@Ð2@€4€  d€ Åãp>\§u«þ¤oÞ)€Ï Ë‹Îyw‘̪³¼÷ ïÀç …‰CX¡ ÕVþw éàó@€€bÊÐÚ¬žžª4” 2tuƒô PU  x˜ÈQ!=T!h(&rTOHÏU d€ ]垪4”9ª¤g€êÊ€‰ÕA¡1x ª4È ú3Cz¨V  l˜ÈQ…žªèJòþÎÄúvGí.GîY§ªÛð)`"G•@z¨† K—rnýO+÷œ»šB$§ÕØnФŸWÌ´7T¼Êœ°]6&Sˆ—:ýwk¸æ'kk¹%žîª1ô&‘ʈkoOÚW ß| …Èi6l×óë™?ÏѬ·¤Íä´µï5jÆÏóF4UÉ „)ÏÏnÛè|â’‡ï›äñŠzMÚu6ÑiΨ6š<šQBýÕ\Fàñ {Ÿiqšl­öå´*Ò3@õ„]Ь½ÃZN¸œw?.Àmï\·sÏøèþì{ã®Wz®²ñO¯DýÞ¯ü3§¹RÆ«ýûO;ü<Ǭÿ˜ºYUÙöª‘xçøÊ;Çÿö¼áù{—¢yVÜK÷£+ÜþÏÛÝc­4-¦=Û9º›ã™ÈüÛ¥E=»ydåÍ#[l¸qÚ©U¹ÃpqõW{©6;-ÞÑÚdÂDkµb?.T „þœp €j ºdÉîKç±ô¬5ÔùòvÓ”;k†ö_ï+Š>²øà².íåx}Õ«õM¿—†XÏ_¶òîÄã­Ýœ¦jûÇ£¿û.°=WÕOà3°Ùðï@ ¶(̈ukÏÜ î¦SÀæþšæ=[±ÈfÂŒ„ ÷=?NX7ž®Ÿñ×äûN åDÉîó{HϽæÿ:{xdžÂhß«Ö.Ýuï]Òµ¹¶óßÚIµÄ,\zý2>'‘PH\îçÍÝiþGÏDü·*ª¢Ù„ ]ižª3èež洲³Ë˜MØ0ÎJ_ÃzÍpl½~šÑ›‡á™íÅ›ÚOÙÜHÓ§ÁüûáÑéé!îR©ÅØ¡ÍôŒNì«uàx…w.LztpÅ’ÍÇ\G¤Õ1ë:fþºÕ­5¸”²½ƒé o"…¾§ÂÎ Ñ’þ•?jc:CÜ4åÁgCO PO.©x9¤?Z`ÑzÝk¢ú =û­¥BiÛ Ô´utrg¡èè^éüüt‹__=½òU´ÆÑ„ŸÒ3@5‡]"¹ã¸ŽË÷€(å¥Ëy¤eªÅ^8¾‚@Ž8<>Wògޤÿ|øs'ÌVtÏ¢dŸmm×ø‹;°V ¾våÆÎ)6a^+Ú*› šÚqæDQú̓ž‰ƒûiHþÒf‡_Üï#)ª1xzw‹J.^ÑFÉHNQPvZç įœ(Þãè=öZ™ý°|°a)ßxÚ¥È ŠÊ›Ô ©?óåÖ>mfÝÎçOËn}õ£o»ú]ß9ÕöIºÿYÄŸŠ¸eé„ö˜k?ºæ-^7¼ôR%i‘èKs»/ó˜ªñ(5;ÒÓùÛÁt_åsX½úZ”—è{bv_å{¾Rå”òžº;ÖmßÙ2â–¬dׯ­¬u[kK^…Ò»A±ÍNõ^lg»&@¼¬ß¬}[õœÈ'÷½þ·rÄyOgÿ “MeŠ/&rT*¤g€êº¼²Þœœ6f? 96?|o!Ytùº¶Í~¿ºÿ¸Oß~/ÿ¼PÁ=elŸÂb“Ñô›þ[;«r²C÷ön<ÁõÉÊ™‡'¸O©gØ×±+ßÃ-ûýõƒ÷“ûõT#Êy{y¿·¤¨îˆ©¶”Z¼Ì·œ¯c7~zbx–œ‘­¶ ýC”•ä¶uÖ¦W’;Š]FµT¦ìb6J|u}ëŒ lG퇷P¦ŒàGÁ’c©ŽUÓ"cÌ| Yúh‘úyÏþ:òRYCCÓ|ö¥K[*dø.kÖbE`–Ç–ýϦ®j!Þûø#}ͳÇ&ï§3Û¨ædg<[]Z©Ü"±n!“ï…¬lÇ÷_Õ¶ùbñkþÔÙ¹óÿ`G³ÔëSšØïޤ˜Óûnùª‹r)ïé± î».›i5iµ&„¨å"—;ŽuÙÇ…2ºZ1ÍN{ðÃ>qz&ãÙž›¬•Ä/Gâyý¦_òüÅä˜VÞÜjdèO‡ |) ËCôîÑöozÎ<#^Ví»óˆcC¹¬b4G»Ï†Cúþ0¯]£ÝC&ÚêÒÅŒlÙÿf…ž;â'YÐí;º-›öË78Ù†ëzSèyèV줱úz=íåÝ.g$_=ôà]Ïnur"®ìó’”03ÙJº½Œâe ó,Ý6 |u¡UÌ·ùzÃþüs¨x?a¥mÆo÷ËÆxÊ’5‹†7Vßá)kK畼‹J.±„lòן{kãÌ9ëOøÄÌ#œBùD`ÞÎ$/O—¯”R½êlTWüTØ#: ¥sÆy*’GRÄ5eŠ‹”õžŽ(:»¢¬"¹:³šO_ÖïÄs ±×68^Û YkhÕgä¤y?M°Õýä?ê˜Èñ !=|Y K—|hlÇoOJ†›µû¬?wÄɦ|âýžœô«v¾E?‡vZù¿vçi·f§8ãhvvì¯zödrìù£¾ïÌ^ìó”ü¹µœü…)¦²¼¬¥¸¼ÜFåÆ.?÷<Q™ïij‘æ”YäM‘fÉ™N8d}f÷žcg¯Ü¸—“ùöËæ.ÇÜÏ<>0H¯²O‡ ]1HÏ_èRã¯Í±gé™Û|Îù«ëzë}Ž—K ×TŸ(ŠHÕf¾óþÎÅõÇQ·2Hó䡸¨s'nµ}|W2:n5uD9ñªrÿ„Í;ý…ý/{&žì¾'2ÛcÞÄ¿ú_slPUNE]CC£„:ÔÚ³W<>Þì\xxÆùI >^¥&ÍÛ˜As=³ÉdìÙû蔢Kª_{ó€$Ïþ½+†iq(;8xqYOªb¥JSÖ{šþXæ"‰~ÅÞbؼ-Ãæ‰ŸFzÔS×½ó¿Yt11êÈÚKë|gð©?üa"LJô ð%B€.‘(þÊœQ;‚ÅKò]6í›ÓR‘{¦^Žœª¶¶ò~í²R ÅŽœŠaKzìOñg6¹¼é0ÚDN”xg™Ãì+éZæCÖîœÝR2ïêØL¦{hwtØñ•kbrÄßnúº’ñÊS¼4Ùoϯ]w1"›oØwþü¾†å|šõ¯Vïtøgð‰„Ì[N“ô½4±^9“W§ßšÅ­/üüP”vmrÛî~«æîl¡ÍKp;ðëüíž’$«ßcŒ­VEÒŸ0[:+$')6UHZsuõZiXÍ|—Râ•nÊ*%sKÊzS,8\i1/¢2¨®R9ŠÔ+º—Ì—»&NØæ¤ðíéëË­U¸ zÍz;¢éÏ=D•—Ë0‘ã¿@zøB!@—(éֆñl)ãæ¬¶ugå_×p™ŸßrËOK\NžãÌŠ^È»Óßq7§ýét óÆ€¤3cê×]ÝÆ íùà"A‡¡ÛÌó⯲ÕDÃÝ[ßFyJÎ8!ß}f?é7ô‚Æå)^Šìè[»ÿØ.9tqNåÐ’ Üÿ÷-}νø>Ýu֔ýÎ-ïØ¤B³ùçμí9t»N‚û–iî[ ¬Õî·åÊ–žšÉÏ\NÃ[’·8üzOí`÷OÃÄ»7c;O··Ýõ]öµ)½‡=úcÿ/Md/õË YRÆ›"§k)þô”Ma›í,®¶´ýéÔá1Æ¥É(f'Æ6ÞM=.ZÑ®î ›ÆÊÂøÀûCÅ™LÛa^o½Êž{2Bzør!@—H”“Uá39ÿ7µNëïzY,]òÇ 7ïHR2¶öÝÂU? kœo‚°b‹qcêm]ÿZ²¬ÜgF÷¼ù å+^xFÛÖ9[L¿•zy¦ãñnÛË]’o8pÛƒ×#ömøcÿÙ>A ’Áažz›>cf,øÑ¡¥ZEß ñìÓ'#ÇÏÝy#4ÜÓCÐmÒ‰s«dþõºç¬sa1Oü"Ò‹Í,e–(sCÊxSôoÛâ0|Þ‰çï3ßÅ‘<¿Ì"ÅhRlù³Û}£+vüsãá½›’Ú)èYö;~þ²Ýõ+o4¡+éà‹†]"a7+åÏx²·hrq5¬'m½4©Ôóx(´^"ZWâêCn”öÌZ­ ­-mÏ%?¹ÓÜ3§}¼_ö3ýHÞ¨ËÔMâ[¹ ”ë•”7±ÉmDÁJgüûfÆÇ{Že–}—¿Áì'¢ÙùQ²Ù!*ø¢´7…£déxü™cáKW–R¤ø7‘«n5~£ËøÅíã3B†.Ò3À—þ+MX~¸Ð @ € Ÿ&r” Ï54T dèüžjhø4Ê9‘£æë2ŸÒ3@ ƒ ŸLé9jð<éR24Ò3@̓ •(/YÖàô,Ul†Fz¨‘ àS*:‘£ÆGç<¥¸#=ÔÐð‰Õæ³Ú•4âŽô P“ @|JE?< =Ô0Еé æA€€O©ÌÉ5,P–þ|kØ“)hødjíÔçb!=ÔTÐð :Uó.RÐðŸ :— FB€€ÿ¤–\k°Â¡jhød¦‹… Pà @@¥@˜Î &A€€JWƒ³c ~jPh @ÈàË€iÄPfTh @È@Ð_‘hrU7ª‡³«ª›в@€4€  d€ h€š(çý.›#S]ÁÕl¨ÛókËŸç7hV‡SÒfrZjí{™Íø¹åˆ¦rù®(JyþzÛÆg'.Eù¾ÉÌ? ¨Ø¤Ñ°‰-æŒÒÖäÉÐŒê¯ær?ÙûLaˆSkµªn T)h€ZEy|eäñ¿£ox¶ïR§ø²â’Üú¸ öv´ÖNÀ2nö³×»9†Dæß.-íÙÍÀ•7·hãt‹V*åmDqõW{©q›îoÐ6™`a­öe4* 4@ͦ»' ÷@ ¶(̉±gîÍ ws(À÷‡¿šzÏæÙL”‘ì¾ÇsÂú¨4JX?ãùäû-ÊQ²û½ÞÒ³R¯ùmg×o¨!Šö ;°Ö{×½¬¤kžêúoÕW-1X–Q¿lD"!q¸Ÿ7Ħùž‰øoUTE³ 2 @Ôl\5mÍwtô®tN<ÝÂçÑÓ+‰©³µ‹ÙLGqÔÊΧÿþ%ÈïÍÓ÷-ÖI9¹ði¨d`èñ¡'”¤¿86ÔèØ×ÄÒþ³î)èÆÇ¿ÍÔW•/G3ŠÖ¯–åüáÒW¼SRˆxšj]F¶Xó›…µ: ›ÂÔý»GdÙÎk›pƒ·[´Æú—Ãæšå”XJ˜z ãáïÅEšÚøº¨íŸz÷O×w©|¥öln2S»ûhòô'.Ï3I]Óayç™:j\i#EI^®Xâ{Ì5."¨Žj×1-×­¶°®óns›ãs|¥ÛÄN«·{Ïøß¸¾ýÕJ.¢Á)±Ù¦™ÎÞ+v†ÜòM—ਪX÷4Ÿ·ªÕ0s>¢5À— –’SäqË܈ÇãsIuôžHr׬ÙòÁJ~kÈ«O»4v‚¢¼r±s ËU¿ðåÖ‹mfEfŠó§¥a_ýôÛ®ñ×wºÛ>Éñ¿Ñ¬‘œ8{s•lã˜ðŸG…»E‹—ÄQ³ôR\%i‘è7s»{{ ê¨ñ(5;ÕÓÙm0ŨzV¯ŽeÆ%ÆŸ˜}Q¹Å×{¾’L#Iö¸okûÈ_\ÊXo€µ\ðµ°;Ým<Þ{¹7­ÛÞÀ2"Â?V²kãV:ƺZÚìU(±ˆGÛ¶ÊÅ6;Û{ñYÛ5‰âeýfºmÕE‘Ob¼þç3â|”³ŸÉ¦e¿!P @Ô"aRÐÛ­³|_Iîð»ŒÒT&QÑ­²“¯o½³!mÔÞ´…2eÇKޤ:V¦EƘù*ò2ý)\?/á¯#IÊòšæÍ/ÝhÓR!ÇwÙß-V$gyøíÖdU q0çò¥Á2:ܳGï§ÍÚ¨Š²3V—V*·HìÛɃCVêòý¶m~_œtŸ:?í¼cd°£jêõ[MìŸGRúé}q[¾2PÎLÜ>…Ea#Ë›þ¶U);4 wã›®O|f³pßÕ×Lëd«5¹ r¬Ë‹K)rØÂ}Š ˜f§Åü°OœžÉxöà€M:Jâ÷$ñí¼~w® 9~ÒsL•*ö1>3h€š-r„Ö®¢ê ³ûs¨—Þ—¶_÷—?ÌŒy”š–™ÎPÒ”«èiÉõ+ó—Ý»L%³²R#HÐ@‰CÉ"J{-$Ê)U65µÒ‡WŽœ’V©¥òhÏ™ª£".Ñ¨Þ ÓûþÁâçPwÁ(U‘ÀÚ¬“Êó¿S(!ðý{ BCøI èömØV•µÎ¤ÞdŽëM‘硈ØI Š>¥¬2ŠÔ+¦Ù|žtX:ì°×²Æ–ƒ»êY™n¸3¢‚/*Th€ÚFcÆû_4ÕÅÑ4§¤mšn²dMëá%¿"xÊ ÊìÑwQ™%–MþúE±·žÌœóø„OzÁñpQN¡ñqz;“¼<]¾RJuH'RóøÚÒ“„è¨*°žœä‘ñ‹#.’•ÅŽÞí¢¼»à~CbÂ2=б¬"¹:³4§/«»wbhBløÇð ’µJV}Ì&Ík5ÁV¿Ž¾ø P³åþ"ûñÊ{lyG”äË+2W@wß«>ƒ$›q8|®Š ?ÿmòÆ: ô*R½ß¼H3¶R,P2#(ôìkõÞ]Të”6:]býYÁφØß»MdToñÒ†Vr^^_¯Œ/¦_^ÖR\N^£>åró΃‘ÿ =+}IøuÚ)äyxÚÚ’)ÔEšSf‘7Eš-Þ½é„^AÖ!»÷ž½q7 ='3õ‹ï—@÷3à RÄ€/4@Íöñôö¿tžxòüžH¡Ç¼[õïçØ à媨Ëkh”P‡šÞ8{þùóÙôæÙÂÖç'© òV¥%lse®§L}Ðu€NIg’(©~QìÍ’Lû?º¬¦À!apðý²žTÅJ•F §®OE¤jÓÒy¿ráõ9e.’éWü®8ê-Lçm1G$LO{ê0ÿ¯‹‰iGÖ¾Y?À܇|  j Žºáêfÿ ~•á4ùEßKõÊyâ4®B¿5­[_¸ÿP”}mòéî~ÖóGXhsÞøõÞvOÉœcýlµ*r6a¶tÊ…()6[RQLøêµqÒ5ïR„-%sKøFÆ–ôØŸâÏøº¼ÑmÂ%F.s¸{%]Þ|ˆÍÎÙê¹'pN{•Cuùå(RÌUj2_>›8Áß=ˆ÷íéþË­å¸ ŠÍz7ÑÔ뢇ø©wD'TKеG§û-}^½˜îê1å°ñ¹±å š ÍZž;“Ús¨¿N†û–Ûî[ ¬Õî×ñÊcÍŠŒžrt:™¶¤˜ÇDÞS]ìþQM¼ÛÙÒþ¿ë;áµ)†=ê¸ÿ—¢×e–j;HÖ†Ô§ýÙâ@ç'I!cê]ÝF)íylPŠdœyè65%âêZªð)9›Þo¶;yµ¥ÞO§º1.½H1é_`¬Ûèû¡pZÑîè MceQ|`ÌCɶæëaøà P›ð”¶µw¶¸};+ûòÌÛǻٕ»$×p íƒ× ömðÝ6Â'(#KR› MÝ13Zþè ¥VÑð'hÜüôÉ´ñsŸÞ}ïéÁí6©û¹Õu3ÿz×sVhXL¼_Dá# ËYj ì-Qëds×K}éßn þÞ©¤¤l5Ì|áªÖÃØ‘”zƒm·8\w"ñ}fJPœžôÔ}¥É,fŠZ?» 5Zñ`Ç?ïEˆQP´ìQoü|«Ýq;€/4@MÄSžì=yrqkä4uÏlúñ~ ›KÞÈ`ê&ñí4#ÿF¦#:¸èPà±½ßÌøxoÈÉEbt™¥&—_uö“ɳó?¢¤»=bòöå9Ö[/Yl-®•% Çã#~¸ä"‚âšM\uíñ{ßXÜ>à h @È@Ð2@€4À—‡ÃÙUÕM¨½ d€ h @|D¢¢—…€*€ h @È@Ð2@€4€   ºàp8yË8ï5T[Ð2@€4€  d€ h @È@Ð2@€4€  d€ h @ȺR½¿3±¾ÝQ»ËQ§{Ö©ê¶À§€]nÂDŸƒ¿.ÿãøµGoÓˆ8j¦ûOXø«S?SENjË Ûec2åx©Óßq·†k~êÖ–!ñtW¡7‰TF\{{Ò¾áþcû?’×lТ}×~£§Ni­#Wþº2oØûL{ˆÓdkµò¼–ÿ¹ñ•F”xÑ¡^ß¿“%ËŠýþ ýw¨vE:TkÐå#J¼5¯C—Ï?>|çÈâÿzð;=4hœI×Ë]f¶õt›ëÿ`®Ièþ‰ý§~žcÖLݬ*lõg–tÿ‚ø¶wÕºé'Îob\¾úh³Óâí­M&L´VãUr+•(Îmû¹äwÒ®ì¸5x´>·J›Ÿt¹dþõKÏŠ~9µgj{x·•C†nyNÉçÖ h+Ç£ׇ펺o°6æÅ_vš~8¨íþî¸Àö\U·½²Ùì øw †(+%&ÈçòµËöÜOÉz²}hOµû÷WµU.»|šÿÑ3•ßÌÏ@ueû•4ñ‚Aûºž¡Yn;οu˜`üE&€¢ ËG¡éô-¤‘bÃAßõ®+ Òí?í[‹-‹Ä™:5>5G²¯½ÓÚi¥û¸?J¥c‡6Ó3n8±¯ÖãÞ«0éÑÁK6s}!ÎeuÌºŽ™¿nõDë:¡Û;˜Îð·ªï©°sC´¤³²C¶Û˜Îð!R|6ôÔMNIÅ5Ê5&šþhEëu¯‰ê/|ôì·– %n(PÓÖÑ?o]CSK»ƒ¬´œrå==[çtbªÛxI|¥ø_µtãÑ+Þ¡)âWJÓ¼ËH§5¿M²V ÙÜÆlޝ´ž‡Óêñ§ñzüw¥¿j Û«l9‡“úïÒÅ‹wž“¥`b÷ÝòëÇ5¯Ã)ey5äÄz8/]±óô-ßHÉ„ÕzÖ=ÇÎ[õÓ0s%N)¯|©/]ÎÛóÛ¯K¾phâø‹ã¿½~¸/¼óç™Ðïf˜~ü!½qjÝÒ /Ü y'"E#«>ß/XýÓðÆÊœrmPj«ÊxFåØÊº\äLúLšÙ'÷^Njä“Sv³ù F˜+¤‹äë¨äÎX‰¤ÿ‰ s„ݧ(Ùãg[Û5þâE㎬U‚¯]¹±sŠG˜—DzAS;Μè!J¿yÐ3qp? IÊ¿¸ßGRNcðô.âô\Jñå® Ùw¿;­kþë+Êvßëýý·ÜÌ—[û´™u;Sù,»õÕ¾íêw}çTÛ'éþ®#ë¶ïlqË?V\’kÜÊÚX·µ6¿äíoÌj”VH†ïŠž½2˜é*Æ„§½¹í<¡StçÐÏ)«†TïÅv¶kÄ;ÕoÖ¾­zNä“û^ÿ[9â¼§³ÿ…ɦü ½tÙ¡§wxHÞu‹oFØ Ð²úáþzàüwДùæúè7ûv?ß“dl 3K$ÿN­yþæï>Wš*”µ|©­*ýÉ•õ”e˜±вIóšif³Í83j7zÆÊõ?¶Uʼùº¶Í~¿ºÿ¸Oß~/ÿ¼PÁ=elŸÂÒ’Ñô›þ[;«r²C÷ön<ÁõÉÊ™‡'ÜèØ•ïá–ýþúÁûÉýzªI†?/ï÷–”Ó1µƒjÅݧ¨•¹¾ŽÝøé‰áYrF¶Ú2õyÓvš¿¾Š' ºÿ&ã[γ£G^*khhšÏ¾tciK… ßeÍZ¬ÌòزÿÅÔU».›i5iµ&„¨å"—;Žuy”þø—’¶6uU ù|/ѳ°#¶÷Öå¦x¯èh½Ü—’N/ýëÅ 3KÜ£´†ÔÇíGI2ží°ÉZI2ÅýƼ~Ó¯ y~brŒ’K}éêÿjdtf`Ò@ÉXáû6ôÀ‡üö}1k¹¥¤ÑÙAMfáX¾ËßËŽä³^ïh9îÒûÛ?ÍúgÔå1ÂR7ø×j)­û¶´gdjÈ+ý)‹7å-¨å +,+24Ðß/(©›¡bÇ9Ú}6lÒ÷‡yíí2ÑV—.fd‹d¯=ôÜ?É‚nßÑmU%CÌ|““m¸®7…ž‡nÅOèåh/ïv9#ùê¡ïzv«“qeŸ—ds“1“­”Ë*;i@™ à Xº­ìÍŠÁSÖV!èôät!‘rËe÷"—‰[VJBBdD‚HÐÀ€C"Š~)þR¸¸BéÛç ÐÔjþÂè§Òfò–—èù… ?}SF \ye¤|ØáEËÏܵƒ•y× wü?¼òGK}éÆ{X`FÀ‘=¬óÑCÍÄ7î÷}ë™>éåþO®k­ þ„såÀ}ÉŠ=çj$yrõFíº¤ý 6›£Ô@ATƼץ¾¡c-KyF¥>e ´l­Ö?_ö.ÒÿòÖ¦üéuØ©K€ðéí÷ÆŠö~ÜH¡‰ã©`ÇÜ{[+´£Ì¨çQl!z·­òî‚ëB„e}ÓmZ/¥ËgS.y”Ò­cÒ•}÷$«Ì¾×L±ìâJÆå”•–ÈT´•%é6'öÖÆ™sÖŸð‰)ø9B˜SüçŠro¯Ö´yîà8WÕ´‘ÝM Š ŽËÎáy–ZƒBóéËúïx.!öÚÇkÄ ­úŒœ4ï§ ¶ºe½tcõ ~`bÒý÷ï”,˜9 ªÇÍÎÎ&ý^ß´ ‡Oèõá¿-ßÖ^)ãíã0¶©n£Ü åò&výMr«H-uƒ÷w¶”Ö*Î×¥<#~YO¿d‚?² …\U ñ­Ëøßwùœi¿=Bx×߯f.n"øÄ»âðä¤ßªó-ú9´ÓÊÿ ;O»µ£ÙÙ±¿êٓɱçú¾3{±ÏS’ -'c!_žâôæ·÷£Ô§.·Ù©ÜM:Ö“§¬à]CìçßÎ&2¸xéh+å ¯Ÿ¿^ù¤¤â2lÏárò‰„¢f—]ƒœé„3AÖgvï9vöÊ»q9™ÿoï>à«*ÆŸ›ÁÞ#@Å¢LJ+ªU_¤JÕ×EÄQU+ŠEûª8P±µh«Z‹X­þ„*ÈrŠ Bd°’Ü@AÉx”í÷ûáóñäœçžûÜ\9œ{²xÚ¨‡§úû„—>x¢v ‡n'ÖÏøÓó —>¿»E…»wØ–1|È”ûŽë”ßzYü¶ÿ~O±Jú -î={VíÄb_rá ´tidöÈ/:ýn\Ɔš='~þT‡-Ÿ#KHØRoY™›~ð‹–\ûÈ:Q´$Š*µ½ù©¡'ìä£kÉzUmÄ_W.yeøÛÇ|ðnÁZ_qÎ!I¥zøêwý” mš=ä†',•?µ× ÕbyKÆ?[вQbçAÏôïV=åÌŸ{ÑÏ[^úñ«gÏZ‘Û©ð,wÞš¹s O{Çê½3¨{H¬Ò¬ÛMt»)ÿ±›–Ìzó™›ÿ÷Ö×V/y~àë÷þµ¤#ÿ=™ï=5âë"·.ÿדï<ÔéÄ´£ëFÑÒü=¼`C¼ejÁž¬¹Ã=ÿÉÆXù£.üm·úÅ8ïŒgUä+zàŒ‹ÓJ3(]ÉõÚ4Žox+?`žîys§çn9©î†é¾ù™ÅÛÊ6ëÐ0¥¤góºÕ«V}çFb±¤ uOîÞ4ú`f´ò¥‡F-lw~ý¤øêÿöë~Ý›ªÞuà“×5/UlÛ³[­¿>½ô«a¸oYn~Ñw¼ªkƒ­¿¡eêÿð†%N+gñ«ï-#§LÝÓn¾ù´ºEÿAÙ:ÿxöš…ï¿>¤ßMƒ ï"‘Øö®ûO¯™åååÞå/Ê]³|C^T=Z6zÀÀ ×dg®Ë™KØÒnËæ,ÉŠ¤–4~{Ó>0¾û «ÅÖN~ìÂKÀ£æ]ÛTÎ-aÙŸ ¹ì7M˜—zÑÈ·îlS!!µöQ].:çÈÛ^{'Åã‰%ºü#¿£øê‰OŒ\Q°ÔàÆiŸ>Ðê›;þmþt`ËÆ}gFkþ=xüŠ“qêE-£÷gDY£ï:ë”>MËn^ðâM½nyiM;¯Ëÿ-+vÀ•ÅÍê‘Ó'ÿ¶èW/é%‡_¢?iºTÊ{ûãÿ;²Ûß2â³_pÌào7$wç€Sªý¨ûèNº´Ñ÷÷ñÿX1þÊ'ú<{ƒŸ®y邃 h•¶ñ“óÖåg»³;|KÕo}Y÷ºO?ºxɤ‚ËoSNºæô´oþ%>ùˆbžUâ´r–¾ýô Ç î]ñÒ>ÅôNæŸÒúÆQ£®+¼˜$¡æñ¿jMÍØ©W´ëøâ¡«ß¿ü„«:g>þffΘ^]º½ÿÐ}Mó«^NôÕÃnÞá†{»;~ÐÐ»ŽØzoÀĆåþÖ9í_M-óå¬ Ö¤õxá!iËŠÆACû¶=,óŠ¿.Š÷?¶Áð¶ÍÒËç­œ;eÆ‚üŒ¬Ñý¦.µSk—xä·_1vð–?xä¿9jûûe'5úõÕmûöž­íñ1Ë~ynϧïzá¸~Ss&ÝpTÚãGÖX=«àV%QÒ±w?qQá­=7 \ÙbfU%=*î%D‰éžd§Ÿ „€.Ä´³ÿòÁ„ãî¸ë±ŒûdENÁ§Ö:¶Ë…7ô¿¥ÛEÿŒ‘%VùøÞ}¯ñ¿4|ìÌ™S¿ŽÊ¥·îvqß{~×íˆmŸb+ÛìÒ >úÀ—ËåqõI5Jûð’ú‡HªÚ¨Mç³{\ßçÂöu¶]ž|Äu#G|ÝãÆ'Ç-X4éä/þÊ€3²ÿüå)×¾òÕ²?ÎÈ®uËctÿÕMÃ?YŸ½pÞŠ¨bÓüñËŠ¿)ž—µ.»p×éçþéå–#®»õé±ùõ\éð.½ï}üÎSª%DÕŠÆMñ²ín;¥^ÿþƒ_7còø‚»»¥Önzò…=nîwõIuò¿)ùÈ+oÉèÇÿ_ÁŒÚ^sþ¡;^ _¦þÿ\{ÒµçÉÎ~óñÿdüê’Ö¿ûaã{o¿ÿ¹×§Í›µ&·Çœviß{úž½5Ëcå‹P쬚ÿŠ¢²% JM@—Z™š¯zbôUOìš½%¦÷œïY „ªm.ôõË‹¾GjËû¿ˆßÿ^¥ë¸þá>µÅÀ/â‹Ü\ªùo‘rð9=ç¡Ö]ýï…WûUïa³{oÿóKɤø%Û–Û¿uÞÃáÏUiÝãÁQ=,jÎ%ùoGÖ9oLöyEoµýÆ MνçÅsï)ro±b7«„^QÉ€RÐ@@@ 4Ð@@@ 4Ð@@{Z,ûÁcâ%ü MØí4°§åGpiöMØ ~XC;ý À¾@@{GhC«göØkJßÐê€}‡€ö¦Ò4´z`Ÿ" }šz`_# ½ÌM9Ø¿h`ï+ª¡~`$ }Â÷Z=°oаoq1Ã7Š-|#°¯Ð@@@ 4ì»âñž{{ ì±Ø½=Š$  €€€h   €€€~"âë>ùò±g}ÉG ³sóW”-ÛäØzÝ.kvýy5ª%îºçÉËžú脞æÏXšEÉ¿|é¼—ÏŠ½ô³¡g¢ ‡ŒY|R犻åÎöá3³S»öiÒ¦ò–5Ù#÷•¹°ßÐðS3ûÉ·NìýÅ×Û¯Û¸qöø¹?÷Ñg7²Y‹ »æ™6Ïû¤ÇuŸE‰Gv{ZG5IŒEyåêT¨ZusbZjj®y–hʇûLy<£Fýß4nS9¶eݾ27ö|k'Lî²µžËzó1×ýªÎ¡UãK?úêÙS‡LÞ¼f̤3úÖšùhJ±]ð\9+3—.´¼þØß_V~Ë©íS‡¿rì»Xñx^K(ö%lœ9÷¥Œï¬KÞsàÀ" á@—»nDßY –’Ïvöðîå¶üµ?ôЪíO«ß´ó?¯œZkåÊÅÙu*¥¬ÏÎXüTÿ÷‡ŒÌøxIn%T;¬ö™—µ¼ëÚôù[ó6<Ûþo—LŽ¢#ÛÎz£æ¨>ï>4rÅÒ͉õÚv×Sízßz©F¡©—?_æò¨Ãß/šðë„ï^Âß<ó¯ï]sç§cççĪTérù1œñõ©'|¼(JèòÊE¯´î¶&ÿ0¿àYæ¼ßü°¤‚½­yù•ªg-ŽGeº¾qá¿NNÊŸÉÐö»4&M}ï±¼[ºO»´êŸu»±QîÌa3îxpîS×­‹¢Äj•;Ûì¾{·©ùp«a×´ej˯løô•‰éÿ^qÚ/+ïäŽq’vÅ÷ ìë4àâ+—¼09^°Ôè¨;ÿ§ÜçSª\ùú…¿)›R~Û5ÐYŸÍêzìÄ×Vç/ÆjVµÊÚ5s>ËzKÆ?Þè0ýÕ¦‡§$”K.·ô«¾?ŸòÚºJGÔ-³ôËœEï|rYçøAsÚ×j]¯ÍÒåSfgåIlP㘺)ÇÔùþIáxƈ±.þbMÁrRúñ©óóÑ• O'¤–2AÊo™É²E··hìÒ‚ ççíg¾ÖêÚ¯³£¨bÓº§ÕÙ4ñÍ•o=9¡Ã‡¹3ß<¤ÁqiM32fœ¥·¨™^«z½ÿý¸#ЩsåRM€ýš€†\Öâó >3UlvpÊw·–©òí»@κçzü·°«Þ2þÌ'¤$äe½ÝwT§V¯óÝ^?hÌ%Ie¶\(¼|Ñ”3O]ðTÃ: ›^½tÄ/ŸÛ-÷Ä{íÿù§ïþßFmgfDQ«~]ÞéQ®pxöŽZ5ø¶-õ\ýîégÞÖ2)wñü‹›~¡pc¬´§p¶Îdé¢I'·›:ë¨V•â9Y«<¿¦|Õ”j‡ýú¸VÍSs?ê÷fý×n~çã¡sšÜ3ä´FÕG´¸/3ÿyouVï;{¦}:ŸœTÊÀþK@Ã.ocö¦Â…rÕ’Šÿ˜\nÆÂ! ÎU'ÜúÆãS '¤tìÓ²ãÇNŒç½ý—Œ7Ø6¶zßÛë×)xÿHýYúUŸûlU´ù‹yY¹QÉù˜ûõׯ~^°P¦sË^- N8—©Ûàw½+½p÷Úôú*õyèÈÖUówK*W½ßä û\!²yÕª «¢äCÊÅ¢µñh㜂[‚”ü!Á]rváMØ7 h8À%–O-_¸¹¤ðîuEËZ´baáBZ‹J¶ŸM¨T©iÍhâÒ(ï«U_goËÇr›ÔØÚ£‰•ÊUŠ¢UQ”“—b>ÙËV/-\¨}TÅmÏ’Xÿ˜Š‰ÑÚâ§·sÉUŽ­ÿM²Æ—¿ýá5×0|ú¦gÏ-ÍÌöÔ`' á—’^³qjôù¦hÃÔ…s6¦·.»ÃÖ¬y ^þ²J—N•*n~vÇ ÌÛòŸØv—W$$|;<ôssÛv¾ýÕùá¹³ôü¶GórŠhÓä2)Ûö³yþì®'Ỏ¢z o¿ãÐÖiIYï½÷ë?ü {lìÖ#À~N@îríK;—yõÕœháì¾kúêå•’¿Ù´qÕc¼q㤼¨þa/OûÙié5DÑ’(Z½çþ¦Ã_m×5=¡äÇäÛ°âá>SϨQÿ7ÛTŽíæ9@q4°ÔúÓ§]άß¼nÓ¼é Ÿ8íOS6oþpæÙ§$O™Òæ˜ò%?~ã̹/eìþi@)h`H¨\#µfµ(ªY¶îÁU;žqP›3þÙëœhö}†7Û£Bbþøæ™ÃfÜñàÜ7¦®[E‰Õ*w:·Ù}÷6nS!óáVîÿhË~–_Ùðé+Óÿ½â´_V*b|ç§ؽ4°Ç%WºøÿšÝôôÏ£¼ Ï,^zÉái yŸ=úZ«k¿ÎŽ¢ŠMëžVgÓÄ7W¾õ䄿Î|óÇ¥5ÍȘ¹<ÿ‘±ô5ÓkU¯Q¦èñãŽ:,àÒj& ½ åàôŽÕ¦¾2Šæ-[˜uxZlÕ Ï¯)_5¥ÚáG¿>®UóÔÜúý£Yÿµ›ßùxèœ&÷ 9­Qõ-îËŒ¢ê·Ž:«wƒX´iÅ]EŸÝäžf‰{ûõp ÐÀÞX¦F…(ÊèM›7åEQùêý&_دà:ŽÍ«VmÈX%R.­Gç,Íßü½¦?^@° h`oØœõÕêÂ… ©å ¯€^þö‡×\ÿÁðé›â;Œ‹çÆwòèðñ°Ëh`/Ø0ëˉk ’›Ôn˜mž?»kçÉs¢¨^ÃÛï8´uZRÖ{ïýú+‹zxèxØ…4°ÇmZ=ä†Y‹ –ÊœÚ+­Z,¾düœ‚Žbuêß-5åÍŸ?¥èÇÇ—‡€]I@{@ÞºÕY«bQ<;{áû ‡ô›âè‘#6ö¸qÖ¸ë'½“pâå'½2 AöŸ3O¹vÁWËV~œ‘Wë–t}ÓðÕë³×Í[Q»bÓüñ›Šï“„ìFØmË÷œÚ³gé†|N»±ç´ÛaÝÕ]^ýíW½‡Û{Øv[K»‰€€h   €€€hØwÅbCöö€ïÐ@@@ 4ì[âñøÞžP 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ðà@ èX,¶·§Àì@ hØ­4Ð@@@€ý> ãñøÞž?!û}@Àž$  €€€h   €€€h   €€€h   €€€h   €€€h   €€€ÿ¡6ݼIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/default-linux-data-directories.png0000664000175000017500000012165514770023131027133 0ustar vladimirvladimir‰PNG  IHDRÀÐÐŽã pHYsÄÄ•+£_IDATxœìÝ \ íðÙ¶ûNwJ¢CE‘û,÷MHîÜ·#wþB¤P.·Ü7QTŽ\éP é®=þ[[©´ÇÔÖ¦÷÷ýø¼ïÎî´;;;Ï3¿™ç™gD™L&üöüK H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ áŸÐ EØ‹Àd2…½|ùç4@]B€ €hZ€þWúžÀ?:DC е €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h H@€ €h¨— ’ùï;èéÞ³èéyž—²¾šÒONx ƧÌk#ÔûŸÍ-~¬0þv‚Ÿ•¬—¨:Æ· [~ý‡ß„ ê¼÷¾³Mñc {Aª€Ýp¾ßÖÚpE ·Y¨RŠªêZMš™˜šYtînÙ½½¡²x]-`•?o,ê5Ù7A¨ ÑTüõ ]b^­3î/ûßòo–¾Ö-ÔÐÿiôÜ_ß>³þ½‰¸wÉ¿øQíÎ6v³Ùë¤!”ÝM~ŒÇüeéYÌtÌJ{›®-´%©ÅOPÄTZÈc¹jÃÀÊ£ï³kÌ û°·£´pêTý*} kk¬_ë¶žkX?=@A€† h Oýõo™å’Ý^ÎcLd)uúñy1Ç|Þ•N4]z/l{§:^(‡"©ÚDY)•AåUIa/OC'äÒ× ‘[·Øòë?üF lÐÿ²Ýf-°Rûó{3é…ùyÙiß’¾ÄżŠüô³bã´»;Ç™†\<|éÀTcé:ÛÓS݈/0ž=³-„PÉö8ð&퀰—¢ø'Jß?ªVÖ-¶üú¿ô…V¿ÅΜ{ÁÒ3??½r樷ç;‰žýä7½ã÷œ°³ L¤êd‰üÄȲ—2l£%V7 P»þ‰Ò÷º¡@€†"T¹¦m;Û:l|°{Þ„¥Ae§3¯Ùq4}îÑS©.ZÈh¿¿e–>–V’F£üÔ“Ò× aÝ@-A€†òÄÔº-9ó¢Sß^›#è%Ï}Ú3Åe|´{çºd“^@+}H¡bÇÿ)Â.} Ö-4T&Ò¨‡óÅSïLGü,y&ÞsÙÉÅwgéò³µ0r’cŸ¾ˆýú+»PDR^UGß´m§Ž¦šR¼;sVwägzVһׯcÞÄŧþÌÈÌcŠI+¨5Ö32o×¾¥Ž,µšo 5TóèîãÈ÷‰éÙt195Ýmºôè ¯ˆ:‡›ê–>¡¡/o5«ÙøÇ,øûøá³×q_¿gä2¥”›v3±7§þh5ª0«þôô¸O#¢ã¾$ÿÈÌeˆËÊ+ª6145kÓÆPM¢Ö:Ô3óRbž>~]ô5rh¢ÒŠjÚÍMÛuî`¬.YƒÏ$µ&ù!°µÍÈe½QxdìDŽԬ­]TRZZVQ][·©¾±YK=%t>üoÀÎ ª@ÕáîÑ÷‚Ýõ’{2í8úvòzS .Sðíá±m®»_Œù]ÕË*mÇÚ¯Ù°ÄÚè¯ëË¢TNÚán²‡+>UévS…éÑ×Ïø ¾xõö«Tzå¿g“Òï;Íaíš=4¹]õ3Ȳ‘Í=öc¥Ïß¹YHr™›È‹XfØvÇWöDÀwG)q›½ò_¿Xndá_ñÉo^d¼ª˜Youdìf3në½6ñq¯/Ú'¶ÍG±'Œ6ÅF­i!ÎÌy¼yÙªÞçWž]Î|œÓ΋zirÜÃò· §žŸj6üxJÉ¤ÊØ W'FjpIs´¯ÇG¶´»Pº7]p3£W]·ñ“(}5/5ÜWë¿ë¶š[>=ýÉa§U÷ÝN`”›S¤w󑽇)TúûjW˜œ¦†ùíÜîy$øÅ÷ªg7°²ž0}Î ›N‹R­`*"FfLÈN—-{O?¯òSÕÚ[°ÊÉÁº—¯QÍ5IêN„[ÛôŸ']]¶í?÷:ƒã+©¬­­*™ÿ½âŸzuU·.I¡÷Ýû«òüHþ×$?¸¶ ?ìÕ{Íí k’ªÔÄ ‰º¢ •–ý;#=écÂ/~vdÐP @'ÒÆC;JyŸ+­ÈÞÝzŸ;C»r#Yî«Ã—«žÔ,ç­]1ol/ÕÒîvŒìøÇg<Ö¯Üy3•=å:t†EÌéÑš%Ui£Q·i%õbÆ¥*C®²3ŒÊôŸuåqÛAÑ&=lm­‡îÛͨ±¼X¹Jž–ñáÑÙ#nÎ[.|b¿ù·ã“çí8ZØ»oB²ëG¦+ÑoÌÎîSQ03¹fÃ’Iƒ;5“/ZÝÌ‚ÔW—÷¯[èt¾¤¯‘|x¾Û‚×ÿk͵{†`HÛŸ>z¿åøöy£W­—ty¹o€J…]$ý[ÈÜ‘;Ë€º¿ïT=avdä«ô©a¨ùÖøï•A¾×-ÿ>ûL_šPü†êV Ö,™jme®#Wt^ +!òæåhƒ²Ü¨0+`f=ÝпBz–µ°[»f¡ÝÀ6šRe›8=+1úѵ3~¾¾7ËFó«ÑO_ðÞ{ôÈòéY¥çr÷ÿ-ÕQ›Ý™˜™óõq€ûJ÷û¥ýÍßî1ÖäuèœfÜ‹ßk’\ÛÌôPÇÒ³r¯å;7ÙÛtÔ©8Víw|äýÐ Á§ùÞý·Î}@u @GÒú]ôˆs1%Sq3=e+TyQÛÆ­+,™R³Þ{õØœÖòÞEdštºãê0ëƒû8?)>ø+hþÚ;ýõV¨AC‘ë²åþ‡v]šÉT}fFT¡yÉ›»ÛÚùLè:=$½è©Ì³«÷Æ ÛØJØÈ Wî§$Ö›N>ysÿ¸fåV3E\ÍÌz]p‹ÙC—ìÀ?ضꨕ\,–hã1‡ï?ïãÍîï™xÐvZ·×ÁvMÊj¿‚'Nô/kˆ6r Ù=DMÈZ¼KŸÐ‹€Ð ºx¯[²òоžâÛgì[Ëý©×¨²ÚÃgY”N ¼ÂÌ s³ùUÙ¤d—õ¡çÖYªTÞv©²ÍúMeý[çþö^„t[iñ'f/»WvŠY¤•ãÍ;[¬Ê7(Q¤uºLÞy{P‡nv—ÄûÜ[³O Ìõl?Ÿk’]Ûa‡®”ޱ*Ñsx謪°Eå›´<‹õÏÉók\FHlè #1}u"¦ôð=íCZ!Ѹܞž°lKtÉ„H;×›'çµät_ªr÷u!¾/Æž+®„¾ûn<ëb5¹q BŠ”že7ž3Q¤Œ¦ö»soÀñâÝwܱcÑkx\•5"aéuãÀ¸fU'¢ª tÝc}zÄÙ¬âÉôëA±¹VêäN"z»u këô¢øThæ…é¶;-n;o®Ììçÿ9÷féùH±.;Bœ;Ë ýô¯ÒGÔƒ" ô¨&>Öm54_zét…ÌW™À+LÚ§c»>—M,½~y}7®§%D䌬,ùø.Üå¾p_»,>Ë?ú?«*»cQUûï¼vèñŒëìâ•wÃi×K[·¶< =Ï5ÉÁ®í”¨w¥U„D[^ÍS"2:†<šOá߇ œ‰*¨HDVÉT ?žÜ~½´ÕYäeϱzb£jZo\ÔüܦâËËhŽÜþ>ib\bAQê>{„êñCÅ׈½z'q‹Es 2T[š/ó˜Úœc+«ˆJÏ9åΰÏä$…Eý w¨ÉAÒmVîÐjÖµœ¢)Ú“#Öv~æÖ]îÇõ%ÖÎQ¥s)X=½Ð¸^aq/}d½}*ܺ-#o㾊ûq—À+ÌüØ#îOÊz7^à³¾kMõø–ýl߉„²)‹ n6Zœ ±¨î$5;MÖ–œï?¾?Âù×®y¼×$?¼¶¹¥§² QIIa÷„ú8•,·›+Ì£•¿„ƒöõò‰²¶Ãæ“§šñî7'¡?tÖ¦ÝEÍü=òJlÎDº¸‡¸¦©A°YúôèSÞèÚb4u"÷ô)kÒË€ˆ`O¤¼I)$ê*@„X³iÇýï— k·säÜŽ—úù>Ptæ«]O*E®¥$¡¡/@E‚\·lJÃçY)s }¯0 >žø3ØùÒÅjxΖOyoÏýwƒjé0–G¯fqC»ÅÖÎ cO¥^;ÿ.¯kεï5ÉA¯m1•fªÁ^ÝÙ×½¯$õ²årÔÿõd_õ£ðÏQ7« ‘+úãwÔåÒN„„œ…_ÃS‰kµÒ"ˆâŠÈxûî'½§n±@û}=(èÒÍᑱE7àz6);1)›AÈ¡oZ­Pl×U›ûE>T5Öî–½Ye§eÕí5ëEÃÚ…8>í²­dX;ÿ1ýË^¤˜o8ëÖ§öá×ÒW‘Ћ€Ð€$ë–?"æCZñ8 è “ùëåµ?×¶3H§nŽI?¢|-›j9ª«*¯•GÕ°aB„•|û/÷_ýb´áØöÈǚ䇠׶ˆJ—-‰°×ì7ÓnÈ‹­Nóm:iWóv7Ð @g…iÙ¦¤¥ÊUzùI/>–íƒ2úorËHþMcU®5X>FÆËc«ç;z=â2ie¹¹ VuXƒOŽ”›©ðÚ‰‹HÊK–hZ½ÆgþH¢ÈwvÙù°tX»?¤z{­²¨ý¡©ùÇ­ô•zúT _ë– uS]ÛŽÀ+Ìü¤Èø²â#gÞ±qݦ¦àÛ뤲 ÅVfª¼C„˜¦…‰,SÒe&éur¡Áé4ï5ÉÁïžÄô§m™¸}艒í<9ÔÕŽõOɸWÿ¾=-»uíÚµ£©–4ö+ÿ1ÐÀQaZ\J¹€£Ò¼|:¢ýøœ^÷Ï-¬I|*ørrz 'âyÏY½°:cñ_D%Dyî¨(åæÊ/QiX»bªãýüfrî»- \K›Ð‹€Ð šøX·$ɪòjKx…IûõõgÙ„jÍ¿¿hInæ×¨i#~>WŒ5_YŸóŒ¤ çYy¯I~ÔÂîIDe°÷u÷ô¾WÊ+þŒ½uŠõϳx5³¾CFŒ™hgkÉi\h` £œ¸GŸÿL)6W(W+0ò3ÿºOsÊ~æ<°üž[L¯×ø1CzuicÒ¼‰–š²‚Œ¤¸XéØÿ´Ï{:8Dqx'øÏadç–BªMŸ¶Êõ¬G#×ÒWDèE@è Pm<×-i"¢¼6W˜Ìr—µâ2âuÕxÂ,ßýE\JŒŸÏ¥ˆKÿ9:-Ìãvæ„÷šäG­ìž(²­_xoå¿eݦÝßæV~™‘uõëŸóÑë==–õçÑ þ}ÐÀINÌù'å*‰½ Ê_ÆL¡JüÙxŒ]ßF®0¬»Ë‚h_N,Ú[:%ß×íúéÅ”8U¼L‚†ÛCA©œ—Ûlf_Í.ÿTîµÙc=:ÞZjR/†ß(ƽôÕƒ" ô¨>^ë¶V¼Â¤°ÂkÙDAvA]Ô§ˆ•ÿ\þš™9ºL‰Iòºk¤ÖvOTÅÖ·^˜¸)=æîåË×nß½w÷Þ“¿+ÎSð.xÍ€«×·=¸¸¬u}ê‡ dEø„¤ü™4±®x±ˆ¨bc‚(©9’_%åu i_Ïí{Tº?V´=qÒ¡·!ëi¿~q~µXùZŽ÷íb™…y\Ú ¡fjõ·`ü¼µbÄÚFåç /áÔ9|ká]ŒGé«…"@’Рúx­ÛÚ!ð STQG‰ 2ØÅCYur{Q-ù²ÏýñùÏ-dÍW6¡ ¥Pë¹£¶wObÊ&}&±þ-#Šú´|~ñàæÕKçü/D•måÙw‡¯é½«s] 4B‚ U¢'†¸Oý3m<~XÓ 5¸–y‚(¹ûWøƒ¯VÆuÕb•{­ìÔ—ÂÀÙV*\÷ŒŒ7Ï’¸ÍPÔlXt>;UåýÎû+^URVnw‚U›¿=)`–͞ϥ“ÚÓŽ®Ë\:;€Ý[òÝö‘öÝ¢Ž ö- >JŸà‹YB_€êâ½nk‡À+L‰Æ­uEˆÏìò;òIbA/£º¨Å5Zjýù"¯¢¾Ó,exäˆÂäˆÒ+Y´ZjÖúrÖåîIT¡iûÁÓYÿÖz¤Üw·¶âZIŠŽ?°å–Ó¹aêÇ19Ôh¨ýÛ¹¥‹¯ç•M‹öX6Ù°bý#Ò¨m?âá{öÔ»“![ÔM‚¦g&|+ëà¦n¢ÉýüógØé'2©²*2Á¾½Ç¯ø4Ö›s54ïÃíWé`Wîz:&ƒWRü¯¨½ß"ÿíÞq“Ë.¼jµ.Äî-M/&ÂÒƒ=¾kŠïx»î¯.Ìàu—±ZÆGé| ¹5ÖÆÔ>Öm-x…IQhÝϸû†=õæô•¯ËŒª9º6©Ÿ^¤‘Y7âII8}ü0m~Sî7Ä¢§Ü;[v´Eèv7S¬õ3þÂÙ=‰©w_~Ò7¼Ù°@ö¹ïÜð«ïó†u¬“›­‚0 @CeŒ_6{ºÜy½fnc´+Ÿ—o>rœþz—8öT¬»ë¹>ý”êü`›×IJÚçÓ[/ÿu½G%âê-ÔKC[aôí¹ÃÛq®ô²_øø“w JT IVùcw@Èû]¬–~ fæ瑋>Éž{‚Ö¶“em®=¶„lyØfÕ³âîÙWgÞÖþÞsÞ·]¨%|–¾òRj²5 fj_5Ö­ ¼Â×fk°Æ¥$$Fºy„ÏðìR$zI£á}UvaEA¿ã~æ“íBnɽàýñ]ËŸÔú 3¨ƒK „µ{¢4ê0Ü”|ÌžúÍuÀøç!@Cyôô'Þ ÆÛŸúXî9}‡£kÚUqëU‰Ó»ýoÖv ‘vlÊÒQ/­ƒpªœvÑ/vŠ¿q?‰Ö¦‡ ™–pjÑÊ0ža@\§‹¹,ÇneL<㱩]W{¢¼¯E^‚iSj¢Tzw¶ŒŸ~3¬d1øQíüŒïWÜRvge[_¿9%'¤¤Z- 8p¯Õô+ÅÉx¾vÄÊ.ÏwõäÖ£·–ð_ú_Hnµ°µ‹LÍVK^aJ´˜êÐyÓ¼Çìu›¸{ÊÛðíÝÈßÍ›lE$ÓnÎDí#»JnàùÜÉñìè3£59|ZüI‡MÑe“M&Ͷ¨“U.¬Ý=;ýÏÊÒÊÏ®AC€†"ŒœÄ׎îóðºú¹Â®Nq÷Åÿu¯ºNÕà¹ÒÝbSIë\²Ïp+‰3—Ümô¸ž` gDŸÛ½õhÆÿí]ªY“ʘ lI\|Ξx±ae°ÍI[­¿7eZòÅE½']Èäãå,Æ÷ ºÌnÚMöší:éÑÆN]OÆÌyë3µc¸€Â€„n‡¦Ä!ö~‹x~ôr¤MP ká·(üâ;eÌ‘²¤ÝlaàþQå÷÷bM§;uß|ÈÑäâÉO»mæt}’c$¸j”>Á’[£à vT«f«%¯0E›Ú¹;¸uÚQrTð~G¿Ár¡g×öPá¸å22ßÝ‹êb©S¾;éŠHªƒsOïé·ÙÇOÁGþ¯I蚎ÈH»á8`jhNé´d_—Å­ë¨Gƒ`×væý•‹¯µšc?º½·ž ŒŸ÷w¹—ÖH5íÙý72ì¯ÿ+’oìÙÄ(üÍ äçd¤¥$ÅÇÅD½x›Rð÷ßLö½ä=‰Kƒ›t›5;îZ,½Ï®I™±ûF7»<`ÉêÅÓmz™(—kÕcüøôêÙ£çNŸ‘¢:Ãô\<õ6{8ÒŸc,²ßr_<ȰôöÀô¬O÷ý¶:.ß^¼çVi*•ö™k2E¥ãDµË‡J..ŠÞܽsüv÷µSz*Ú¯÷Ïû¸mp½ø‰µØ¢µ)‰ …ÜÞTuK›–D8ûî°´{3» zi?¶—¹žª¬¸HÉ~]D¦‰¹¹Žt÷òñÁNóø½Kµd‹éwÚp‚þ-r_í=ãré5L"m7o±ªÜ SDugðаÎ[Ù=Jœ±×½õµ‚ê5)ðÒ'ø"@rküTW­ÔlµDà¦L‡ §Ö^ë°é{2÷áK½ “×­Yd7°µ†dY¦g%Å<¾|êÄ1ß[Iý.§V Ðä+"Ñ&÷o?ÖjaI—¨‚0§N­Â×»o\0̼ä†.ÌÜ„°À]«ï¸û§ÇŒdÏÞãëªÇ !صMO ;²ië‘Ms[ô±3fÔàžÍ›5*?ö6³ 5êÒÁMË×~)}Ji´ã@uœnÈ ÿ+2ïym¼Çÿìê½÷x92â5Œ¥¤Éâ󷲆ôYÿ°tçºsë!ª¤ÝTSQ’™—ùëgÚ·ôlA^CDÕëé¼Û|uépd)—†^v’Ð42ÖV¤æ¦%~øð­l_-ÓÇãDÿ#–GrKùnΞ#Æ— ÏD‹9îÐÿ¸!¡ "/’“þ3çÏÙ+Ý™þÞÒKy|­ñ×kn·Þzóè³%£-%^ß»òúÞŠ³è­ŽŒÝlVãá©ò^zÉ÷Ü:­\$¼-Ðß‚ùëÞª«ÂKÇ[“p py•œ)rœCܘ;°ïñw×~äÆŽ]ÚË d5¾ô ¾Ük¡ VO-ÕlµDÐ&E¶ýú«gS{X(éîKd=?¶bä±UQ»™–¢8='#=9!íϱK…Õ¨ˆÄ æ Šé:d߇’'¾^p¶¹àLÈ56ÐQ¤d¥|O«t¸d¸àìéYռʱº¿{Ê|sÃÇ™õõPDQ«©†Š’¢ŒhaVzB\\JÅ/¬9é°{ŒÀѰ!@CbMºŽžxOØ­q£œn”»kvfâû˜Ä¿æUí¿9ÈoEw!Üí³wOŒ_IU} †D›'Ïî¡!ü1¡V!@ÿ§‰H*ª©khé672niÞ®swËí ”ªqŽ@RÏzÛݾ³BîôØïwõÍoNóIévè;`Ȩñ“FukZÓ«+D5‡y=‹±tqXµóêç¿ïr&Þ|À§-N[+ˆÐ>ó÷Ž" n¿i·mÉÒ-Áoþjm–4²vܶcåÐfR”¼ïUýyµˆ4êá|7aÜ•cGN]¼ùöSJVý¹c› â·(ˆÛ?a⩲»ÑòÝC¸_D$ª=öPÀýgýö±Ïh'´ÖýuÐ$Ú©&k\ú^Èn‚/ƒ‚"¨š­–ºÂQê¸8è­ÍÃÛ¶zú„¾Íªz.ŽGNœ9wFoù*Þ¡:UÅrÝÕO#ƒw¸lÙs&"½ªYTÛŽ]°ÊÉa„±œðú2bm+ ô¸xñüù WnÜýëÖƒå(›˜¾x岉T‘­þð#7\bŽÑLÇ:û8ŠŒþÀÅ^ïÉMyñìeì‡øäô¬<UZV^AYSÏÈÄÄĨ©²$×&-…Á¡|ݶô#eÇn ã’õàÞ“¨I?rèbÒJ꺆滵×W,ݸE›Ú¿dÚó÷–ºƒÖ Z™þæñÝG/ß}ýžE•QÖ6lÝŪ«qY¯9I ·x¦×÷‘ë’Ãï7‘o1Ø~Û`>—Oùõùø¢z‹"™‹øKÙ^§~3Oñ5kM qýy·rçñ¿hEDõõŽgz“û£ªÔQé| ¹5ÖBä©Öm-lù• ¢Â¬@BÛjž§Õ¼]Ù‰¯Ÿ>}û!!5#—N•”•o¤ÑÔФUks}q®ïV­ŠHDÎÔfÃi›õG¿E? ‹ˆ~ŸRô©R êM L-:u0Õâù ª¹&ÉÔ±5^ÛIÍ6ƒg²þ­'Zf›ױïÞøò-ýwfN>STZAE³‰¾i›ö<Ö14(Ð `"RêÆ]w­³dUmæ}Ƙ÷à[Š)·è1²E¾#T~ Þj¡üc ðt…)"ÓØ¬ç³ž‚z?þP¤4ZZZ·´¬ÛO%Ok[TN»egÖ¿¾‚Z(øW!@€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @t5Q(a/@0™La/Â? €h H@€ €èjÂU«ÿMÐ$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @€ @4 Ð$ @wÌü)?ò™%SqUeiªP—ˆ3A¡ŠP¸ÎSñû°¾¢š²”H­/4ÐÀ]ö‹]Óçû=‰ú˜N+š4Ûõñù"½z!˜™Ñ§6­ÚräÒ«4kRÆhö•çûºËp˜;+b×,‡ÀˆØ7‰¿‹StÛý_ŸÌÒ®¯‡PÕ«­X²îL6ìé›\Å+­V?~º¹d¹§ò£7wk·öY^…ùO¹óÆÇR–Ãû3¾ùõm2ñV!ë¡Ôà ø #U8ž­£'ÒrRèÏ OªL8í;T­Òù:úWŸfÓ®ÿªø¬òð7!Ö•?€™—vîtÐÅ£Þ~Nú•_ò¼¨ŒŠfcm]}ã–­ÌÛvînÕ£½¾’§eã)ëîÔVG«xEmtè»3ýŠ%ýÜH#ë³é^—ì¸ýå½eFâU~}ƒõ¯^mh)Áóãiñ{6›õ€^î)©v›Â¬1­êmÙdÛo|tm+Þï^Çd;¹\zʸµëga/L²#6õîä®bãèÞšˆÚ?wIPܯâ¬OГÏÛ÷ë£:rïõ“³ Ù«V®³Ëù0"÷ÙbƒöUm"Ü!@TŸF@¡/Z!(Y«cItïÔwwÝm¹¾*~JÂÊóÑ©éæê7›K˜® Ï]™xsŸž[ÞèÍ ¾àÔ¿EóýAOºäu»ý8÷êî+ÉÖ“´8m6T­‰W~ŒÏÿñå‘ûÀ^›Þ²ŸLó³›o{ÊF£Â‡Pu¦^û9¥àWB”߄ΠîoyxavÛ&J•6bZÚýóVŸˆÊa킚t·±]f߯¨‰šœhaÖ¯ï ï"Ãݽìÿà²?k^¥Ïß¹YHÕ$ké“@ß›úþþž ƒ7>/²²>>agÚH¼l©(¬ˆŸFÏKyì: »óK…¡^—=ǶÕUbÏPå×ïå¶ô°¥OÏ}¹{sYzVŸp{Û Cµ’ß寷¥võ|è;Æ´‰š,vËÕPðvï4§ðBÑ^;÷:Œ`ÙY6ÿvúf0û5Z|À:ïW¹¼a׋I^¤…»¬Ð@ ¤”G®Pè €VÈLDZ­ÅÀuÇÖ³Ø˚̿³ÓïãÔ6êæ¦äE:ð†Ð˜qp³µ©"÷ã.ZüYïGÌÆ–¦ßïF´;»Ïś۔ÛÞ@D¢‘®‘ž5ê>¸›Î/€€ @78´ï÷:Ì]åWÔ`%ÑÌÊfìò…­ uTåÄ ³~¦Æ¿‰|öèÖ•«¯²Ö?={¿³­äßm”$%LV=ÍqÌI}weE/›£ßXϨN8u{Çê2ÿ\=ű°®ZÑ “í°ÖgÁ©{¾²Çlœ±wÜÃe-*=eÇzÎvÿ"ÑóÀÎá\OŠ+xêÐkjcÃM»›Õðóf•Úè¾±‹7ñ•”(Ò­WœÜ|ÎlÍ &‘smÞ”ƒ–׿r‰‘å1RÎ.˜\&›¯òÛÀ1=—Ðiß\ÚŸ¯w懸á¼c;M=,$ˆ/ž“ÖŽŠôè¡P¡?o-éòJ¢‡gКv2\?¤Ìæ8HÍXÁzøÂÝ;j¶[ÛªŽfX˜¿lÛñš0sYÞùðØ/@3ÞÜàž«ÐWØ Â'ÚÏxö:¦P«Þ¨ÄT۟ж. >è…™ýzï+ûK鬽E{ß“ÿ³m![Åâ[3ÇgÔÙF? }ñïE´qÝkÇÇÙàüØ>o%úl A•i4krSï­Ÿ â£Ï¡—«=;òwT&Ùr鉭g[9†3ˆü; 'ííug±QåH_…‚·‡œÎf=¢öX7ßœçgQ­¶ìkÆÇ[óGÜ`ÎÑ&öŠ"ôîIël¢Š"4=)p–­w¼âHÿ“sy7.‰é]1pó˜+E‡²‡¶ßYã?P©ªÄMûzf½ošìðSôà jùëNÁ›}Ž'~„—y(¼@¤2fa^¡°—þs zrÐtvz&Z­½{Ç¥“<‡ Žª`2bñ,3§r¸5ÖI Z!ŠR¯-'„ õcî‚»KíO97¡qé6ÀH½¸ÜñNAsÇý Œù8°Êòñý$;xwUÖAš¤É”YÆ[WÇDÒÉ}O7w´âuY\ “…ÇÝBZ.yL#h—Ntï{…)¯ÞZqÇbÙ;Lé«ÉÇ&LU¶4€¿⓸þ죻Læß+`åßݓ֎ŠÚÞøÄ„É?tf_;hÛ˜Ÿý!U}àŠqªWŽ|'ˆŒ ׳‰ý¦VqÍAnäîMèºËÖ P¥† ôÔ3çCè~woÿË^~úY7E›¶î6pìÜ%sê—žxÏ{{lÍšýAwÞþô?sÉÿ?¯íðìÝÎ §ÛŤD±WNø¼tóqLj>!­ÓiôÒ;tiTy¦<ôÙêv(øÖó/¿¬ÍGÕ¨C_Û¹+–1cŸ*øueL‹AgRØ3K M9Û_úwìåC{¾ö¦Ù¡Ï÷&qhYÉÙ¿l¥Oè'‹͈èåâËK^ë~ÏF:jSÿï%°Ÿkåþ!bq3þƒ¯%/‘mÏÆí>D§ †#*§¡g`ÜÊr¦›ë8=B}ƒÝ`0¾_\4ãtñHLFNÇ×pLÏlTåV­U)Ðåpo¬~ íç»·…z¦j<[¹Ñ ‰¨ tÛ?ê¢MPAd_²_z±ïÉáŃÈ1?p^ðKcfЪvü\ëšqÈ?Qiø¼îì§âúãæ·[½àA¤í¹¿ÓrP•çS« a4ß×=ÄÔþ~Áx¶rü¶aNUöÍ.ÃH »üžýP¯g[ea #Ö|¦G€ÉÜ;ù¬½gÌèx…ëwòZ® vëíësræqa}Ú½mGßNXkR)"1¾_qñþBívÀžµJ^ ü+TK~ܱ©½§øÇb&cVîví +W˜òúÆQ7/ïe½=ƹytªAÑ!UÞ çÄ%úr‹ç þ§lÃ:nQxú[çÕù­üFä&¼vÙ·Ñå쇒Lt¹|/Q%³ÓVöôùÄ<‡ 4‚К´×c¤Vq°UïÈ:3[{7imö“%ÆÜ¿’[gü,y‘Ü×;´_v/O¾ËÜÍ'˜4bþüê³Ë7øKS‡Ð tCQç³6 £è‘¨ÕÚ9­x^»'m±þlP¦Ñ¿u_­|# ýÇËÓÛÖ¬q»*¿þÅ“u<´BT k=ƒC']Ê&ˆŸ§ç¬žÑã`%JnÔÎ9^‰ £‚6Zñ¼v°HæÓ©ê£çv*QBTg„½ÕâÉwhDæ¥Ý7¾]ylgŽŠÎæz±£hÔúq›…oâÖ8ïKøç’‡Í´„*ÄšM?â`2ûVA¤\?ŸRÔõym»ªúºq"Ñbú².›f?bÄ›=žOïëV¡»[Á{Ÿ 糕íÖÛê°b¥ ¿:˜™× (JÏ¢·>¿åت¤÷ÌÀ“¦YÛ·ëçõùô´þ/Ý»ÉSÄ4» ±¦}üìB¬-oÜ{¸5‡®ñ n„öi¦(V¼æ Sn¬íÛoÛ+&íñ6Ï—³vw(9šb¤]^0¤(ƒ* z~b${ØÃÁÛцúêk7c@׋㊛#Ä4”Ùuäs÷&3—œZعµ¡Lج6“pùjbš‡X³Ž^G¬"Š´’Y¿áÖú­•’"vø]rúW¿¹+îå†^õè\²% ;®KÁd’ ÿè¢ îÌö©JÇ©½y_GDP¤u;u¯í¥âýgdð>ïç®?xùñGñþFTNM[·™©Õ\·#þ “ÅW$‘}×NÇêxÉU;ƒ/§žëôõòñÃÇÏ\¸ñ8î'ƒÓ³¿j×¶é­K›à¸7&îy3ŽW -íéÉ]žÇÎß I̤„ˆ¬†¾‰±ò·»ã„îôÐU\Ó3Z!ÔÆc÷îò¹9³(ù};<ÃeÚ«­¾ó6ÇJô<¸cežU‚~=ô>ûC{úL‹?'«©šƒìHݹ˜Kä]ß})yäÔÆ|÷k6ãèÞ`ã×s "öãÖ‰ØÞYŽS¥ýø\„dÔUºn‰éM;²;ÀxæâÁƒ”MÕHnߢ:£V _:<$‹Áo½º±Ë(Õ²ÕÏüýp›Û+ÂdÓÊ’K…öåÄ’Eø¨Oß1¿Uù¾ç"Ê}œwðµÉúä±Äwá£$ªù¦-tKÒ3‹˜zŸ5;l÷õ;ý›Hzü"ÞA§x3*Œ;²ò+×&ˇþ4\Dµ÷¢1ê§ö¥ä†ºžú<Ú¡y…5[}âHé=»Ø{®¥Ôýšä{ɳ¢‚Ÿ²*}ŠaOãrÇQÅk6͉P¯þ=€ þC€niaK†ø×³jÃw[¤ðÑÓï8[Ûø “2»dç¦öM(™IoÃoùžºþú«É2§ÒÍo$!cyôkü\_ûAóÎý"òï¯é¥óây²d#M59)9ÊÏ fæ§»ûgu‹—ˆ=o§ÃnéãÚ¨jfõ™õÂm˜¥ãíLB¹çb×3CÌ5ų>=:¹eï㢌ßÚíš—µ&÷r†VHà¨îäýÛ|Œ>¢Ä—]S—HêžzD³Øê5‰¿‘ä˜?îx]úÝlÉÔVå;[P”{ÛW¸x*ƒ ßßüe’=‰ÅZ ÃÞAÆS®dDÜŽq«‡¾ô´äp&œž—SÒÀ"&%&ìd)ªi9È„¸ñ¼èqÒþI+l^{÷Q"Qm²Ö™ã­=I‘sÑõô—á J¯ê¤'nðý.5hÏ ~®«¬´„‹Ê:ëÉvÙªr7Šr÷IÝÄCB ˆðCãç,$ñãW&ÙØDƒ ~Dvz6+QeNÚ×+~Å'v4,-µ+dII½.zľÖ‘×Õ˜,‡æŠœÞTª­ëÓ+Õ^¤ê#±äLFшýÌ×!Ó‡ üÓ7Iªåì -ëx©¡n!@7 yñO?—Ìé1æX"‘}e³Ï»±NÅùiäØ˜óÜeTQz&Zn¾{eµ)»±³s÷¾ýLò †ùE¼ô»øeá®ç!Ð üÓŸuèÇLŸ³Ñ»C®ïýû´àoPFê ¯«9z ûÈÿH®x“p½ÑýO±¶V"ÂËÿýì5Æ$jQ‰O8Ï*_öŒwöj_ß*àEÄÅYOE z!½Š×Kм~øâ[Aå‹3¨ FÚéH (xç¼tµ]úœQ ²‹º½%°[>êõ~dÎ<ÈX,XØrÏÊ¢ŒõlÇþW3\Ù7WÏܳñ­ñB§!|5 Ô‰ìØkì‹7›ëüÝF‘1h¯C„~ ˆ·×ßf/lÆmà D¨ì߇YöëåÆÝ‰+~ ¤£@¡ÑhåfST`Uq…Dá÷ÄßtB±¾]Íÿ’Kõ1!®Fß}wÎwuwž3ÀH¾¾}¨Ð -½¬yTUÈÍ£üc| vXÍJÏ„¨¥›çø¦wÚRFÃ,ÕΖLÔ  ²B;#E²™ëVËïÒ‰÷·c³œLÕð;d?Ûs¸xä ³cÊÅ«™}åÎd/|Cp½Ë[­A+d$a¼à°“OkçâL¤2Ùke[>G§ õºU@ÐݬšºqœéÍ¡c1K][“¹6‚Úx̾CAwdz²hÒÁ‰Ã_û ®âïEµ•¢è*çìïß­0ùæ!W¿ðØ·oKn®Iâ6]l6³Ð‘D0aü¼½bÄú—’½¼Ÿí—Xl>íjA$¶[f}h‰kÅõ'9öZgw‹U}>°ãþªã}(Œï¡½>S:î]ÌïRè_óŠÉkÊWQ±~öùß¼¤/tBA€á‘•”œSü(vµ‰ôêªg¢Ðëßµì$–\LÆn‡C–îo æûÓ+†œ^!o<`œÝÔ™Ó¬Û’íÿ膡^5ò‰žtÑ3´¨bé:sðßã²É¶wܱ&­¸ñLM" ­Ôˆ»É3ëGøk¼%’ß!=:ªøÈE\ÇP¥â‹k´P#ˆLV¦x•T@%@£²a’jnÕFÁ9¶èÜ©V}~¯~£%œ÷ºG×·?º£_1±0Þîü“¬-âó±/œ¼:“ÊTÍQ^>cîŒ<Í* ©¾vöÃc¼ÿD^R·cSâhQ€f$¾þV0²QÕáBÒt‘ïõE¬’sÓV«O@ë™vcÂfðß1›zrðÜÑ{>7² 81³…&ó w ñäËÙ‘â3y©Mô‘A*|GhªæÐ•¶J·ŠîäøóŒëùm½&©~8ºál–⸠ã„sÀ\5FAvÉñЍ„h‹kŸÁ~Äš‘Áîz! ÌÂÜ’6\qîèˆ*OÊSåôø>°n‘YrŠb·'/[;¯pÙ{õë¯~džî_Åú§=l{à±%yÜ.þaõoÃ…êà³y´^ÉŽ¾Uü@·KË*ꊜùÄ™%s ´ R¤d8¦N{04{!ª¸Ê¥ä)F¡°N° Êо„ìJ˜í\4~X•÷ ÌÓ¼¿å¤{A|;åõصsoù*æáŒª>|÷Ñ 7‡ù¥ÄÓSçôÚEû{–nƒõ‰çE[dÜ͈kM4„, >œhw:]wþ­}#‹‡~п_€ñ¤‹¬¤žzl²ƒM̱!ªü.E±ûÒ™Mý¶}f•¤[[}ßìäæE:­êÉïX€d0t&…Z›—ˆHÈ–´æÑªÈŒ‚Ü’³/r’‚ýM(âÒ%‡Iâj¦í;6ÿwòÉ%Q0·Û:iKJäÕ@¿cG΄³ BÂù彯ªÄ^œÒäßùÞ@ ~؆ATI§¤y4+å'çæÑú„žñ)žÝF¦¤Ûˆëv(Ä&H^D•ôu¥ˆÈ\"?é3ë£Ë‡ÎÂïï¿?Ðl¥%œû¢ÊÄ9ð’hµchr$MÆjìî‘H?CöÜýÑkh#rQMDuð.ß)7M!ˆßgç,‘üë¨QÂhÂL3—¬£fÆ£Ã×¾³«óÛå¼tµ™w+×|CÈÖž¥— ŠjÛw Àx|Qî´SÙÄœÎ÷H~’­f;´Ý¶¨èZÄh÷«š\N•è»sŽI-”÷¼—+Mû}>ðñtoþo[‚ªÐTGšxΪ ~'±*©¿ÎåÓ~~aY$­£+àÃfMMiÖzgí›>¦ÿP€®Ö’S$Ô[›ßzØ×}µ› y’k;©§Ôù˹D¤_h⌙e§˜¿ŸŸ¼Y<,wëÉC„t­P*ÿíÉñD+·aº;´Kµ7BÓcO2Ad_ö¼š:xÙ«àDTúï8>ãj¿CÉEãååý=ë0ÍuÒ¶AÇÓ ÚgÏ6®íê²£pI×g‰ž{Wµ)ßŒÚØÖûà™;cÏþf¥%ÿi GÇøPç³m:vå •£Y‘zþP*¡9wýp~î±H=íÙíxJ©j•Cãþ&ÄÙg‘øòkQù®/ÌÌ÷áì¡}Lúós+2$õºêg£ "ùÁ“o´ŽzÿNààÉéé¯_åé›7–,Û7Q$õF¹° ìåûƒHŒLÌ' ¨g{n¨ê݆ÏÞ±~¸ž¾¡¥Vm&žj7&–'")W’òó2rY±—ó ± ’'µ¡»¶÷¾³àf΃ùÖK¥÷/j®!ö;îžÏÊɬÝ)!Þió‘¹Ü‡à¨=h…„yÑÇŽ} Znç’ŸY ºÕxk=ÞßX¢7wŸO´©C6 Š4ê³Ýonh/ïN¯÷ßybæ¿·Npôd[º:4+íúlsúÄlýÊq†ªeãuhämÛ¢Ë œžf?º³ÿ( þ¾¼ˆZÿÔ.v Ç¥HŸ!æGvLh,Mªgõ4UkÐÌ."Ï1²œy‘Ùß²ÂíÚiw=,ª?©]g *;;]Ú%^P³›ÕŠ50JytADº{¿˜¾­½ z­áɳ-´wúË˶.$Ô›«°¶&BFEá,ìqƒq³Ì×/‹d=|rèr¢Ý ž;¿‚·»Ç-ËX°–Ô÷DË£*4mÂnXLŽþÆõ*;!6AòAÂ`Þ…’c;Ì8ÿÂsbÏÒ§EÔÛqY¿eù`=Ia]Ô‰VH`Ëyyød<ÑrÛð¦\å¤Í'W÷ÞŸÂ:D~¼7àÓ”%¥È EÑj‹ŸýeËÝ_ª~]DeÀî»~ù½'ø~z·Ã²CºO‡]+ù*b!#÷gÖ_ݨ‹Ðçšz *_wèÿO,m'ÏOñ*éúÜdî6Uv¡jŽÜ{xô-›€_ñ+`úüÑ]ÎŒæóL²\ÇÅó mxGò6&5«•£å¼·žgR ¥šäJÕ¿s©[§íï¿]ºgѽUfegþi×Ö:\(êèe¸tçø&¥ß˜*«¡T|QMü­»ñ†Õ/ûƳ7Ý9îBñyûÐIº—Ïi«Ta½2èÌÒÑïêKNO½¶ãôGkûrGf… ·Šo\¯`eÝ‚ëîá_†ÝPˆéOÛ>u[?ŸT‚ñhãΧcÝ;s·ÌŒÈàËá†Ëd?¨F‰åI÷7%B "åîí¯¸Üv@ˆM|`ü~éç}%·‹×ûK#©É©"ÒJê:Ú*5_C5…VȆ‡Y˜™›Rr‡èߟ^½ùªÐ¼q#N[#'5."Øõd2¡ÜU)õC²¦¾¦l•Û³à××ÙMYIJQpôp ìâг¥¾û#/ícì‡ ‚ÈOy󿫹™¶"‡~W…î›O.¹ÜµxØÉªHè?iÖwíü¥ž÷ŽN5;áÒ{¢í@ËöÆ:jŠRÌœôĸ×a÷®_,¾„€¢Þ©[3é?_Žöå´ó¨¢Á9¾\Xnç9ø%¯#;zvrìM÷ñónå6²ùß¶œÇ,‘ï²h©Eàº&AdOŸî¥¹g|{=å’1A¹}}‰S—uu™õ°éü5}•+¼=³ðwò»Øö…Döר÷ßškÈü‰aeo[$ïÓõSÇSË¥-½ ?'+ãÇ÷äŽ~&ˆ6Š%Ãø‘^ ÙŽ¯ø&ö¶;ù|uûN1kêØT¶0åõõ#®7XLºã]véð§ò¤(užØMôî=±ÄÌ4¸W Ùœo߯»zb˜2…QXÀÞiÐ +î=X¯°¯`§–·¦jÙ9Ñoàö…)! Ú…¬7éÖÞPCV¤ 3ý[â—ñ² <ÛÐJ¢äïKÞ9?ŸN*œ0ó¾)¹Þ#ëË›OéÚúÊÛ"™…9¹ìã1ZN­â{ÓóK>––—_¾“Ä’3™Oššß´ŸeÓÝDSºðÛëë>[=Ÿ0 5›=Ûzq¸±4Ð E©·ÛÉùwúìýDÄïï8 |On#2å}¼÷6ÚŠü±Ë£j šÕ~ŸNÄîp¾2Ë¥;Õh‚¬+ÌìH÷á½]ž+Ϲ?Q_QŽPT¯ãà­ JÎÓ¥í­vÆä–î³ÇðVETú¾¸0©ÒöÿãÜ më+¥8³[щ VFDmiS±Ù)óºN¿ ŒòOÅïßy?ëÿúŽO¯š9µ›ú³ä…ësÚ7™CÊCNGŸ³­ªŸ4E®³³ŸãÅNÛÞqú&¹–=îŽqŠºâïz÷ɉM'·þb÷×§ˆÉªêè÷]0¢«eÏÞ=;)W8§KU1ï N¼N)žøðèS·#»¬»ÓZXù$–®À‰ÆÅÌÖ<}²©B³íËÁþf³ný.¿B®,ìÞ|!!j¾þùÃY¯lZrýúTQ+G¹î8¯U¹wͺ5ɨ÷‰¤rï™z|l«ã¬ÃÚv›Ÿ=Xm*šä7¤ÜÛyµc¶ÇïÂ"¥XRë’Z %$šO:Ýiœ×/ÿËÛgœÈ,J¸bJz­‡Í_1Çaö€æÒö¢M&Ÿˆµ[¸÷Î×ü¸°0†…åÐi³;·ç´µÿõ/ö<ÑŽF’[Lí|n®òýìÔ^S}cJ^‰s6“Ùoé|5t™dQ«CïmaŸÞîá{ñ^D\̃ÐÖwÑ0nߣ¿ýªiS‡eÐŒ[sº•{çÆ’k5ë¶ à’³¯ó·Ùæµïë[ÚçþËž¡F{Bºq—Eg¯ÿ¯töÃ…V¶>Ï’²J^]ÓJj­|“® ‚®9f.ïkwüYBéxã‘‹ôÅ—«è[:_]´à|-9AÈ´]±ËQûêý§áÏí(Žé*†íǬܺlùÄv5(ê5èD¤Qo÷;ù}FzÿÙ{@×Ü#“«leæÅßôÜèŸLÈŠWuQW•k€ª3~ת]m7Eéþ£zÉñs`^±‘ŒY˜Ï“ ’o‚¬+Œos—Ýe%”\ÿESEúµÔ”ý3 7ETJ^¥IË.½ºse¤ö ²!‘î°#:g™¿h4ür_]Xåúþâ6ã•È|.E¶ãÖ·Ì­<æS6¶€õÌ;…Þû^Þ5?~ip"!¯Å}ÈYË# Ì#<ßSTwæÍŒ™œ_7ãõõ)†œù0¤Òg÷:žÈ<Îå´&\«å>ÌJ(÷g2C–ﲜ¯™Åt¬wܶ®¼¹í{õs_Us«[‹¶>ÆùÝĵ,纳þqz]¡§wæI¦›WL®ç—»z†'zrxqÏÓ¯{x¼=¯%gm>Z}í·öµç¹ Ðð @7,bMl¼ˆí³aÁ·›G§™ùoé7iÜ˶ƺ*²TzöÏo_Þ½zöðæ¥ â‹Ï÷(üÕ¢š‰lôìoqo²Ù9‰oÞ30Ñà㬥´…Shð£½^Ó£Ln}`nó6†ZJR¢FaVÚ—7ohãoFºµ•"ÝÉŠÞ…œÚ þ¼R.3poäÔ(¢Ú{ÞPùÇ~A^AU~KAÛ/øó질VH~ˆiôXä±Þ?xVt§!-êÏ ÿêV€Ð!@78™c¶ßµ:úê)ÿà+·qòuùU:4´¸œª†¶n³‡™[tèbÙ³{K‰¢[V¿1‘ÈÙÒ½ÝêðríÊiþãÍü B²Í†§×·â1žž˜öˆ½ Nxz?;<&>òa|$ë+¨ë·lg9vë®YcÚ–Æx¾› s#œú Üü(•(˵3*ÅûŒë»((.“=ßKûf2Ûí¼2_ë)çFÀ5ù+9µnn'EˆjO8Nu´¼7²Ówüvyù`‡6ïõVàCÑ À¿ü¸ ŸH¢…£s?åÿî‘V€!@7P¢J¦ƒçn<—Ÿy©5hL$$LV=ÍYUÍ?fÓè2õ¬~³ÙØxM¬hŸ B j•óˆ•$oYm i¤g€Ó›÷0}™,÷;—6tX õ4Iù$JŸÙ­ò,o½~>3ilc¢¨g¹l&“)WÅü´O{Ú¿^NYD?ùÊg¯€Q™Z¹Ñß¿+àÿìÝXGÃðÙ»ã8ŽÞAš¢ ŠŠ Ø°cˆÆ ,‰&Ä`PóE}­¯&¾%&±$v0±£1¶”E‚H³ ¨ˆ€ôÞ=„kßíJ‡C:ÿßãóÜîÜîÌìzÜýf÷Úhh#5Ód]ù²ý– {ïK âÝ>v#Ý~Fõíùiÿ|={eHQ°?øèª[/€Ž  É˜Fzî:7jÕõ3Ž–ñ ݼ7ØTG‰%äå$= »uå·ÓÉ„a6ïÐ9%HÏ4@ÓQÜþ+}bÇýqèà©Ë7¯=™U~;?¶ºQo+»/wÎýä“©Vmõ5*Ð2ðÑðn˜7z:olë~@+A€4€  d€ h¨Eákï Ùˆñ•RÐ) @È@Ð2@€€Æ‹]Ûº ÐñP”g[w ™!@ȺSÁÅòÐÒp=4€  d€ èNKêÑÖ]€N‚²YÒÖ]hG d€ h @È@Ð2@€4€  d€ h @È@ÐÐxA¦Ã£Òå /¤}à¨Ñ*½8ö¸ÓmB”L¯§L°WnÁ.ÕÔjgš4´>QFHzºä±W7Ë–ª­Öл耷  õ ¢®çKÔmt äZ z±XD(Õò 5¶õkÓN€ì  Õ½.¼ýP y´°WU<”älìó‡{!}‡F?°2“†Èü?ÿRŸž"&,Gß.¼'G„¯=B·Ž÷àB©(Ù:˜¯Ù>p¦9‹ñŽøõ“{„X Þ/Zçê—¡þÃó™«{1ª7TO%oúFQ~æÿû×ýLFÊk¦Á³-Ã]úÉ•o æGž¹ÿõîßТ"B˜ªcæ Øñ­…­šd·ººAå?x¾õ«ˆßnd§JZUV»Àê{w [uªúÙ/ï3íè¬+’eíñÓ—š0¤O‹âö^4ý2›öì«óÏŽ/ÚTÿ¹²çŸñëÇ÷è ".«ÿüî¡…<wاCOîé©z÷ë²G—Ÿ–5 çÿñøB[•ÑÐq@ ÐÐÚD¹Ùw^J•F g4jAè¦?íväIâ­^?5qÚ£Ìà?Âg{§{DNríÁPdK·ÊLÞ8/Ù/ƒÐ)¸–†ê¯¤¢)ÆëS³þ]l/îþlŒ½]ßó}×™V*‰Á–Ý>Ð{p#çæá;v„‘·ú™ÉÕÞ‚À;»‘’%CÝ©¶rqדn¾34ðUp bÕNZ©¾gbªy%:›dóæ¹.U¢?¤…<_¯lz;­ËG±γ ·¬‰«'„†²•U™„'àyøÍ ™J§¢’L”5Iiv^ÎÙ×Ìýyœ¤Îú«QÿC] 4´6Þ³¤'’ŽÆèÌÆíýË1Ið%†+f<Û£Í%Dœ—²fò¿×EÔ㯅=X¬²ž‘ôÞðШ~ƒUÄŠQ½!^zý•”·•ŸrQixX~¿AÌÜ}S/}é' /·w`“×¹§½òÕå5ÌûûÜlÅFl>7`k?ðññ'}¶`ÔÒ ~ÁNGiz6°¼i7Z…žMì}ûÆ£ð/~µ¸³„ŽÈ•;©"o¹Ð(zO" =–ìÚׄE„i)§BèZ ôµU"¤¤Á“UѬ”x×ñÛtX‘÷mú‡Húå5úàœ87ÞMÿ>öOÓÈë‹Ç²÷ŽÓWlà¸÷Е @CãEx™~âŸÎê{Á÷ÿU[ÄøúSr\0ÊV©±X”± hi¢¤À,:ÉšôQjÜ fÙànÒ¯Á›{[ΫkmÞm翳+ž.­XPYµ§¯5=5‚’«Ù ‘•¨®Ýg9˜~¯Ðøä¿Æ›üb ‰ >®THØLŽææ{m¦ç;ðssy©¹„mÊ¥H˜Ggˆ*u÷m7H\‚×cºHçƒ^6*ôËÈÄu(uã¶8èTjÖgfzŒªdiº~®¶gc }æl±Ô„ʺ}OL×ùɧ ²g­•ŸkÓï”f&Ó{„DÆÂ5^7OErض=G)==WDrc^½ÅŽ  :hh[¢Œ¨XúÚs# Kn#¢íëÄw_>ed4}d£´ŒM@K—FúHµ‡jë5òSˆ£±l³ñÑÅ ¹YÉ;Ý’wJJØ\ëI=?[3ðS;…·u°Õ†UJ{Õb5®õ!†åóJä4•Ô )$DX*KkÌòôÅʇgÃ_‹«’°òz¥n”¦ç¥K2Ž\V= ñb,Ž ¾”Õ²M@‹+.¸ñ˜¯í3^µÆªøM` ĕ£ǧïÇÚÆù9æOßÔ»Ï^ Kya—#Â.Çܹ4ëÄôŠdÉfÉWþ5¹zC«„ÉxûVAQ•ëãÇ=q´¿ ɳ&›¾îe­/W¿Þ4a«è³ê‹Yþ¥„˜¸²~©~ÅåígÜMw&™½sy¯Ý;¿M!ÄÔ)úôûfÒŽ|ÿ=ê«žŠ ÛñÀ†rˆ(ÿ„ËÚÓÛDìÒ9þí¹CÁÙ<¦Ê°é3Oþgˆê#×ïþ¾ÿš(8».òp6¡/o—Dú^ýÚ+Ø7*‡¾¶]EwÌ{v,e«Œáp袙wÓ$*cúW\GQì²£Â×…’¸H'Eabø«ªûQjz¬ÙÛcäçöuqÔgk? ¾–Wìõ]âS»7¶!Ù+©Dœu;šN™„²ÿiÌÖ™JòRÿ>l]5=B$¿Ä« µò8®¯X³“)5:ÉPpXÙCÍ'&/8ú¯›*þ’ÓÀÒ_>C‰%ù’ISŽ  ‹C€†¶$*H¼“*yÔeÆ•æYqÁ£Kv.>ô7:=§ZÊÇÝ‹ºuþסrƒcÜßÌ2ëy$=U24ïn¨a¬Å$¼hû…—îÑoýÜž¦Êù±éa7¯Î ˆÞõëŠUÒáš·M˜ë¼§?XÓ?(›¼<ë:[“~õ‹ò|¯%Ò-«Y/¤ðwƒ=f°¸eƒ@9‘«—^ •Ó’Ddž° è±$^éê$}MMò:»0ùì®}ŠfÛ~¶aÅœÝ;xg }m»©ÅZÁ)7Ï{Ù=çGzÚ›á纤WO’£%Šš#+då8½»Q乘$Æ<ØŽ-HˆÛz°àÍ.¥ÏŸ,þ4òN,sáÅ)ÿ³•cpúM4›Ý7øZ ämƒÔ5Ë ZCM«¤2‘ l+q~–€4ÎLvÿ.»ì™Â"QÕQÝr,CgKò0’ä\Џœ¨;߈!ÎKÛì|×÷µ¼¹ãÐÃ+4ù5φä id¿»ÅìOIÛ¾5'‹‡~“ô+*oè\5ACÇÕáÚïåSúÚs¶ÁènÒXÊO?°½,=½}nîhEJöïD§“7ž_ýâo»;¿ì©ºyàqÉÛºáw¯uÓcaÖ¾mÒôÌ6?xæK7c?5pÚœ>Å1v†ÏÛ?TŸQ¥ öø…ºA{ÒI蕈d§±&LIOOEÑ-Lm«@Є*¿[lÞ³x§ ñnÝY±Wmœ/Ó—·_¸3zýæ¸YÚ¼¯>nwÒHáÅ?öàž¾–¡¨ÂÕ0¶÷ñœbÅDxlp$ƒÿèæñ¸1Û‘ ¡+ÆÝÎæI{X¼¥8ã–›(ߎ/$…»Gžö¬TQ ë¨¯ò{* Å„m¨cVxçT2Ù:äôÙ¡†Šâœ˜Ìû ’ç8Îk uk¿^õ†šTIe”ö¨V$ó!!¡Ÿ_y^%ïnjÖhKûÂÈ…¢ëK®Î|`3½æNlµ¥‡œýèY~ü‚î§Ýs‹ŸfÅÑCÓNûU¹Dø æÙàj.ùLuÿ–üäÇ’ß¾ÙS¿0ÐzÎ:W²kð¸Fߢ‹oH¨ ŸßІI_ÒÊÆ}¸ô:?í‘× zAÇnˆ"ýqÁÒµríGÝy?Ïša]mafÔ iüUæ0Ϙ~1Ëéñܧ–'¢8ZœšM0 \gêï9˜J¢½3F/Õ§²Âƒ¤—·k2ÝP¤ÉÒy“•3éëLïv92Eòl¹î}úòvKëQÜ;çx$7)÷•œùæ?Ð×¶ Jr REb¶*E2Ĥ0:W€@èŠD%ý %úõ´ß¹RºNc¯ÿà¶íExfi\ söw“¿?‚…¢1QÐÜèçd°5ìàùÔû÷RŸIöà(X¾gâ²Özù.óí 4êm¨ •TÅîÝÿâïÅ.«£n%¼ dŒÿlÂ_îÆ¥¿:|™”™ó8U8­¶½TG ½¬öõWgýr#Cy„«h=Ó|ýöA3{³ˆˆWÛÙ`Z,²´ÞFèÛ?/YùöÏ +Ù5x\¸’ |~CÛ—D†Òß4 Ý¯»žô“£4'­ürõ‹ß+^¬ºqjBߺÚ%4%™‰IÒîjœò2–ÑÀFu6Áê5iü°C^Aâ—ÇþÍu)àý‚žýa:ê£î’O2Y:ÏÑ0U–X1嵤韨ëu“—.0äµá•]Þ.ʺý‹Ý¾gŸâÚvƒ»(ÐuQ-åì!ÿ7>ìÿÆW*Ò}û¼š–Ëî÷]v×Z)Ûñ–kõ©Ú’µÎÀ¡•û ùï1{¸ßìáU6Z>1qù›•ÚPê¶û|,öÕ|¦®³!Ý«lLÜð#éퟫìÕÀ¹ª~ ,•\WT.áêHu=ðv½Áã€* ¡í”¤ßxAÏìc«Sv!Å`–_®Þ½¿³¥b•ËÕÕŒèÙÆÕj¨˜¶Xg­ÑKwÐÊá¿;òC½Ÿ¤¾§vúŸmgÛ˜²Håá§J×¶‹j¯œÁ¨ø{oÅEúTBø)w渧¯¡×±Ú´xˆµ»$òòÜ_’êê/@—Fy–/ˆ][¤¼¡<%ïIÁôûŠÎú/4*•—/´Ð)ê§ U @C›ä¼¼K_¦¢5¦—BYâdkv+¿\½ßûÿ3«q¹:ÿaÕu¶–A7B2$»ÄäðÄFºAŒï ¯x>¥Ðí£¹ƒj4AJóª†äEý¢í_,ÉææËǪÓ? £üÚöW…å£Ñ‚ħïr#'qVø]é¨ö«m¯H_Ûžrù*è¼$ÑíMhk®åŽ(/‘¨_“üÆŸYL¯‰E¯z¼ùݼNQg=- ÚÌ«ø'ôµç F#+¾J¥ÝÇÙ”<Œ%9·®_N7¯Ë¾Ø¼á¬o©’ùX§Ãóõ¨ò[˜E爛¥Ówaoòà)¹çs<®ï*S9~Zø÷ —ŠÕÁ¢ùµ4AßÄj ý‡Ú!û3Ÿo?’L_Þ>lÜ$-éçK¹·EÄ$ýþé'S[qi÷·þ!ó}§+ Ê’¸(?¯TDIn”û‰²ágA!Oø.5t*Õâšd¹Ö¡PYË;"õkrÒô¬¢6mÕˆ´†zÍr*ºò)hÐÐVøqaIô”  ‹7ž”Ó]ºa‰Ϯ?+z°`êzw ÕâøÄXÉFr¦Nku¹„©cªÁ"Y’ûã§_ÿcÖkÃN×MSO|%TûŸ9k˜*åÅfÒ#ƬîßlfÂä?¨Ù„Çh‰£Î~ÏŒä’'9S-Ê/o§”Æ9[)‡?($Ù»?Ýàm¡V“©;ÎLåŸçM¿¶}µyI_Ûî¾cä ¼ˆè¬Ací_ݺÁ]wß;3ÚùøS\ÛPKD«+´ÉZÞáˆ]}k”ÔµeÛ”4´ïá}zv…þcí·w¢Tͺ{RïëC7φ¦DFŽºõøáë—MœiB_@¨;vÞ^5¾i¯ø¹±ùDžIûLö;£÷í!ß“/cc‹%ÛÛØlïdÌ&¢üÚš ô7‚Moíy†¾¼]ÍzÙÀ7_ðMéŽ[t}µ¼Û/!á¹¼¸LíÙÿ·êû÷GÐZPTÚ¤kÛMì/îÈwùÑÿVZ^Ð#ÖxÇÏþZfUz9Ça磤ܤÇY|\IÐá @Ca¨.:êQûåê}GíÛ7ª–ËÕéûŸvssßâæ^¥P©‡Íöïm¶ËÐy{yûcl¹•wá™ë6×¥R‘i|èÌJ«\GO*©—©µâŒG•ËÛ9=üíñöòö sü&Ì©Òøœe‰U  A€†®ÇfIÅåí¦ëç)T*/_õ¨¶}Ë–@‡‚ ]Iác2nŸŽ<É,¡×Ä¢ØWÝ*ævHÒí›\ÛšËÐÑ @CW2nŸKšžõ¦-pNû´¯^­£Â’å6)€Žº’Pꗷו_ÛªÚ=h @È@Ð2@€4€  d€ hh,Šòlë.´=h @È@ÐP±XÜÖ]h§(ŠjÌÉiäfÍÛ(´(h™µU•4Š Ðæ dƒ ÐÅ!@wZ”Í’¶î@'ÔæéƒÐm ƒA†h[ÐÕ~b+24@B€h”öX‘¡Ú t§‚·ÑáÚïð4@ÚmzÆ 4@›@€¨O;O¨ÈЭ N"›"C´2h€Úu TŠ Ðš j< uA€¨®#¦g B´h€N u @TÑ¡3(24@+@€x é„ P®s¤g B´4hZg ÈÐ-  S¥ç2ÈÐ-ººÎ4‘¡Z4tiˆ˜ +hèº:}zÆ 4@K@€€.ª‹$Kdh€f‡ ÐÉ!C4/h芺Z D†hFÐÐå JÀ»@€€®¥Ë¦g B4hèBºx‚D†hÐÐU ;dh€æ€ ]RãÈÐï:?äEhFÐÐÉ!=ׄAh€w bb]¡š  ‹B†hhè´„ ÐÐÐ9!@ A€€Né¹ñ0 +hèle… hèT› ñ  ó@|ÈЄ ´hè ž›¡: BTƒ b\[A†¨ :¸¶… ð44 24@hèÛ ý@€€öé¹ýÀ 4A€€vq­½A†@€€ö A­}B††.Ú)D´ö º2hhΠÝB€€vé¹CÀ 4tYÐо “u ÈÐÐ5!@@;‚4Öá CC„ ïºèÎLòvÖÖ]hpZÞ>#¡å „@‡€ íÒs‡†AhèR  í!{uÈÐÐu @@Cêê4¡¡‹@€î*Ä¡mÝèÀ(›%mÝè´·:dhè   Í i@G„ m鹳 4tzÐа:7dhèÜ  µ!ZuÈÐЉ!@@‹@††Î ZttÐÐzž» BC§„ ­AªkB††ÎZ"TW† 4´8„'èL  e!=Á 4t.ÐЂ™à dhè4  ¥ -A5ÈÐÐ9 @@ëA††NZBtVÐÐüž¡„†Žš²4:4hhNHEÐHÈÐÐq!@@³A™ CC… í Æ×÷hœ’ã‚Q¶JT+µ)*=ëåz,ì~Žˆî”]Ûþ£X­í|ÿ=ê«žŠ ÛñÀ†rZ©c²÷ ­ @ íÏëÄw_>ed4}d«h~ò¿.»B"ašÛ4­{¿ìö™J;J?¡ Bz†¦Á 4tDÐÐîÇ_ÊjíFYemšïøÕ 5fk·ßX¥ŸÐÕ Á»@††š•(ÿ„ËÚb굿ûå=çöø%fX†oÙ8Û¥§¼t¸Tœ}wë¡¿'¥–ÂÕ;éýï—´U¡ˆ0ëÇWƔՕ¸tŠÛRFŸ+7WLQ¿J¸ÿýáNÄÅóÄD^ÍÚnĺeg™È¿-ÍŠö8âãéý8‡OCø״·Ìµ4f7Ø+á%וNáåõ„n[ÇÚFìÜwÝyOî鵋Ë=o&—­ñ3f|ןHš«÷ ¾-úéÐjÓ\j‡èï:hhV WNºódýç—®ñt{kËe¤ò“ÝYì&ê~á#{%Rðè’‹O¤džS-åãîEÝ:ÿëÐG¹ÁG§ÙÈË÷7³Ìz™'yš24ïn¨a¬Å$¼hû…—î $…Üž¦Êù±éa7¯Î ˆÞõëŠU¦t{% þŽ‹¼®Ò{éwS+J‹Nˆ>¾7ú\мðÇš³ëïÕ>¶¹ !q<É&L=#mEM*íÆq»¯Ãsé*Uôä¾8ÿ³CWTïÑ·E?™HÏÐ9 CC‚ Í‹b•º¼'!£—%l GyoùßïB’z(rŽýà¼ÛËÒóØÛçæŽV¤iÿNt:yãùÕ/®ÙÝqÒtÚøeOÕÍgbøßÝkÝôD˜µo›4•²ÍžùÒ͘ÅO œ6ç„Oq̆áóöÕçœÜú›4•ê¯ó\ë>˜Ëñü÷ïs2ýUðïKþ¶º>U¾þ^ý±råm‡3=ù¥2Øuyà45FiÒ¦ÙÒô,g~ð÷/ÝŒX¢ü¨¥ó~ò¨çÐ…Y¿´~?[òÿ 1x  B€†b¸~q?=zŠ®ÒØé–êÞA¹¤$>¹øu·G^/è§uì†ØHïÁÒµríGÝy?Ïš¡©W# 3£NDÑ ÃæÓ¯X9ý!žû”ÂòDG‹C×HÏôаÜÐi«qé Ü‘ &[# zl–Nß…½Éƒg¤äžÏñ¸¾«LåøiákÜ/\*"Tÿ‹æ¦¦å’ÁŒp‘ Øû§Ë­ÂÂëǼƒèJä'Ï2Ó} —¥ÕËAŸÖ]‡››“O×e0iÑþÑÊ Â“¹Ÿlƒe‹ûìÿæI±(~íܵ‡é{ÒeÈ[¨‘§yÒ‘‘²„™1/FÓãÕ¹"ú—ƒ6è'@ëBz†VƒAhhÏ ¡•QªƒfÝ=©÷õ¡›gCS"£ GÝzüðõË&Î4)»2¥;vÞ^5¾i¯ø¹±ùDžIûLö;£÷í!ß“/cc‹%»ØØìâDûMÞxô¥s:=¯yúÅ<‰O‘ö±pž=ù¿SL4šx§7†ÉT×[%g—ýšSœ£ñËÒ6F/ Ì"¢ÒZ§rPmÑO€Öƒ4­ Ú-hh^\GOjous§øP§J”zßQûöÚWG§››û7÷*…J=l¶o³½î†ÙÚ+6Jþ5±W,˹)¡s«lÁà™óIÈœO*Ye†.ªÔQåG=Tí{ô U Ç@›@††ö Z€Í’ò…P”·lyh}_íÐ CC;„ Í­rªÃrë,´0Āʠ¡YUKu’åZGOQÞ¼å- éÚ¡¡½A€†fU3ÕÕ•óPÞ¼å-©Ú dhhW  vÈ+Ю CCû µ@R¨ 4T‡ô í¡¡@€€*P =C††öÞB4öÚ4”C(ŽÚ4€  €†ñ<èX0 mž¡CB††¶‚ ÐÕ!‚@Ç… m KCø€ŽZ4@×…ØÐÐ]Ò3t„†V† Ð!m@'ƒ ­  ËA΀N Z 4€ï¢ëJ§pB¸Ö×}\í¹ÕVÛ¨S¢âг^®ÇÂîçˆ$]™²kÛŸc©6ê t&HЉ!CCë@€¨‰ÁÕÒTW)fj)sÚ.±ò“ÿuÙAÓtئiÝûõ`#=´Ð5qÞwwÏiëN ²²¤ ƒæ;~5CÙÆÝNƒsÐéaZ4´0qÑÅÿlpò/%ÄäÀ•õKõÒRQÜwÓ‰’¨:{ï·gGp)qI¤ïÕ¯½‚}£rŠaªèŽyoÂŽå£l•)"Ê?á²öãÇ„˜:Eíï~yϹ=~‰–Á€á[6Îvé)OËJv¿zá »~)%”²ÞDÇé?ŒŠ~ÿ3¿dœøãîk#9e]y•pÿûÃÿœ ˆ‹ç‰‰¼šµÝˆuË&Î2‘¯1²Û˜) ÔVšíqÄÇÓ/úqŸ††q¯i3&n™kiÌ.;õ”ðRY¤B·­cm#vî»î8(aÞRtÈÐÐÒ ¡…QJöókúe“—Çr]gkÒ¯9QžïµDúY5ëå¹ö /ÝH¹=M•ócÓÃn^½ë׫Le>¬zk“+Iðw\äu­>~ãnjEiÑ ÑÇ÷FŸ šþãXsI†fÔPstúXØæ&„Äñ$›0õŒl´m4™HÏð.' KA††… -N¥ÿø…ºA{ÒI蕈d§±&L"Ìzz*Š~Ê`Òh[.!¥i§¯e(ªp5Œí}<§X±[Éà?ºy¥°<ÅÑâÈx@õ×&—éù@D¶z—¾ îÈ“Gž: ú_yž=eˆÎÛÁäÚJHdí@!š4´V¯Iã‡ò ¿<öo®ëLùït.6õQ÷²W (+üú»}Ï>-¬ú!/V^çh÷©‡e*ª¨’Kˆ€/*ÉÍÈêöÔª˜#Ì2ê«Å$™ÂŠ]K2¥›ètW«H¦,£ŒÊ—y2Oýµñ"nK§§ýÞÚoæ,3µ-ÕI@¥§¤ñ‰»ƒBØæ‚ô ]¡¡… @Ck`éZ9üwç@~¨÷“Ô÷ÔN? ó³ílSé ŸrÇÑí|€$íêXmZ<ÄZ‹]yyî/IÕka0ÞNc j™\¹L\íýRLÄåÍ¡‘µUé‚XTöH1ªt½¡ƒxˆÈÐР¡U0”æT É‹ ú+DÛ¿˜¦ùò±êÒן8+ì.ž Ã~õ¢­ã)"ŠK¹ÜøºåÔ´4 I&$ýEö+±‰B‰QY¢JÛ°µ º’!Ù&&‡'6’ÞÝYã{Ã+žO)tûh®…LGSmóÇKž"$åYF¡Ø¸ìNÒ¢ÂôéUr†úr2µÐD e¡¡Ù!@Cë ÔÚ¨²?óùö#ÉYôìáq“´ÊÇ^E²¬+ÊÏ+E’å~¢løYPÈ“$ëÆeYZ½ÞÓ%Ò ?ØçÈsËõæòü´û[ÏeUÙF§ïÂÞäÁ3RrÏçx\ßU¦rü´ð5î.ªÿ‚Eó꾘çûíþµK(徇~œ9¬þÚœû-Ì  ‚½аÜ:@!,¼~Ì;ˆ®H~ò,3 4CËC\¨ š4´ŽÑGýžÉ/x’•©ÎZå9’ÒdmE^>$$Ô}ÇÈ:yÑYƒÆÚ¿ºuƒ'ºî¾wfôÔéõ×Ì6üµÏÁož‹_þwþšCÆÊE Yò檤 ÿí6,m×MSO|%TûŸ9k˜*åÅfÒ#¬îßlf„×Y{afÌ‹‡Ñôpw®¨¡ÚØì7Í;¿ÐëZQÊv—ÕǺëpsSb¤}1˜´hÿheÜO CC€†Vò˜2ÞÚóL¡oÿ¼Œ¾ýs9¶‰ýÅù.?úßJË zÄïøÙ_ˬJ/ç8ì|””›ô8‹?­š&S]ýŠÏ,;–S’Z 5Ùeé7·¯Íª"­RŠ}&ûÑûöïÉÀ—±±Å„£nc7bý²‰NôW›ê­¿¦úk#òÆ£/Ó9èyÍÓ/æI|ФÀ°…óìÉÿb¢ø -#m5aš4´¦ò8køÁÛ*_ïÇê1aŽß„9U¶³,ñmÇ¢ªqÌâCÞ®3¸C纄Îu©X§^¹& Åò:Ü7‰•Rêa³ý{›íµtŒëèé!®sU²«ò‚£ ªKÝµÑØÚ+6JþÕñtÍ&jËrnJèܺö¨ "@]¡¡¹ @Ck±Y"yµÓ!Útý\#…Jåå ¡Õ¶ol¹˜wk¯ÇØSOß–—fü~á%½Ìéqð“/ÞµþöYP„€ú!CC³@€†–Wø˜ŒÛ§#O2Kè5±(öU·ŠQaI:|“ ›¼L)HÒ³®ô¶DNSl³ÄF‡ ˼9ʉ_?, õoƶÚp !ˆ­ZÞ¸},izVÔ›¶À9íÓ¾zµŽªJ–›^~ üÞͯŽ\~NI2zFY†à‡x(ÈVO;.¨Ò3@#aÞ4´¼Pß%umÙärƒ¡G‡:T{^¡Úz ´Ûªåu@ 24¼#h€Ž 9  ¡á] @t`HM† M† ÐQá³ M @@…Ahhh€ ŸúÍš ãÁç=@3B†Y!@t0ø¤‡öIòÊlë.¼“Ô¼´9h€Ž¤k¦ç”ljÕQúß_ZMƒ Б â´9h @@—ÖQþ¶Þ `ì¼K‹]Ûº Ey¶uà-hèHÌšb@ @È@Ðå07 ÙaztJÐ2@€†N%ßúª§bÂv<ðÃ…¡œ¶îN+‡žõr=v?GDwÊ®mŽQÄÝ%Z4@gÀOþ×eWH!LÓa›¦uï׃ô ÐB :AAV–taÐ|ǯf¨1Û¸;4ÈN\tñ?œüK 19peýR}†´TwÆÝtg"!œÙ{¿=;‚K‰K"}¯~íì•SDSEwÌ{v,e«LQþ —µ?&ÄÔ)j÷Ë{ÎíñK̰  ß²q¶KOùòÑSI W/|áq×/¥„RÖ›è8ý‡QÑïæ—L˜Ü}m$G²Á“k—{ÞL.! ZãgÌø®?‘ì[þm ¥Ig}ãžB·}ú}3鋽úJ=‰Ø¥süÛs‡‚³yL•aÓgžüÏÕG>®ßý}9þ5Q6pv]äál¢Jò}²Ö%’}‡[îºgùþšž'’3d·uƒÓBSù:Æ}ůîøŸ“qñ<1‘W³¶±nÙÄY&åÛ—fE{ññô‹~œÃ'„¡aÜkÚŒ‰[æZ³ËNmý§KxÉu¥SxyK¡ÛÖ±¶;÷]w”0 РAv”’ýüÁšþAÙäå±€\×ÙšôËH”ç{-‘~VÍzù@.EÏÏî¼3F’²•M->Ð*N¹yÞËî9?ÒÓÞŒÅâÊI«Êy²þóK×xº½µå2RùÉî,vu¿ð‘=ýD©ÿ³Û|?ŸÞNÞD[zÊc\V½ÊàH›L½qÜîëð\ºKªzr_œÿÙ!€+’éXoz¹zé•P9-Uá ‚.›Aâ•®ÞIÒ×Ô$¯³ “ÏîÚ§h¶íg–rY¨M Zðùkî@«)£äÿºpÿÖÇ‹‹TþXì¨QKjåEûØ/¼tO Yäö4UÎM»yuN@ô®_W¬2•+Iðw\äu­> ãnjEiÑ ÑÇ÷FŸ šþãXsvåNÖzºæèô±°ÍM‰ãI6aêÙh+Úh2‘žZ44…Jÿñ uƒö¤“Ð+ÉNcM˜D˜õôTý”Á¤Ñ¶\BJÓN_ËPTájÛûxN±b "<¶ 8’Átóxܘíf«,ßå= ½,aã=ªÈ{Ëÿ¦x’œÐC‘sì‡rHiêÁƒeéÙè¯5{Ë 3ï/šwø4]"ýöåÒ”û¤éYÎüàï_º±DùQKçýä!Û¡¼éɳx§ ñnÝY±Wmœ/GuáÎèõ›ãfióB¼ú¸ÝI#…ÿLØkcXžLÅêŸ|¼ØPˆâ/ì6wÎ/ÝteÚ”EºrÕZfý²MšžÙæÏ|éfÌâ§N›s§8fÃÎðy?™ýµõ7izÖ_ç¹Ö}0—!âùïß1ædú«àß—ümu}ª:“4pºþX¹ò¶Ã™ž‹üR ìºê^ö¢e…_ÿb·ïÙ§…â*;Š…•×9Ú}*FK™Š**„ä"à‹$›ðs32¤åº=µ*&ó²Œúj1I¦PºRš›Q¥µMÔ*ò#K¿—“¤ ›p@ SeiO˜òZ\i‰º^7yéC^K’”y’,,|ÛyÝ^ÊåÝ¢8j=”‰¤÷Y9Rmº$3Qú‹Ñéþ¶ŸFI—x·¥_ˆ~oí7s–ŠÚ–ê$ ‡ˆÒSÒøD‡Ýðé€Vƒ MÄÒ´røïÎüPï'©ï©~@çgÛÙ6¦Ò×?厣ÛùI’Õ±Ú´xˆµ»$òòÜ_’ª×Â`¼l@Õ>k·r±X¢Þ^‰Eµoð¦P$¨#n¾íIEƒT’ÊD¢·‰Å|QJÚWíCíªt\,ªèC•qº ¥!@CS1”æT É‹ ú+DÛ¿˜¦ùò±êÒ—”8+ì.ž Ã~õ¢­ã)"ŠK¹,SõrjZš„$’þ"û•ØDŽ‹‚Ĩ,ÑÛ ´µÊ7ÈáUlðòqæÛ‹)»ìþª °¼Tø4çÝ»BQJDžhœtÎ…° íi‘´AMm­?Rl-ƒn„dHú#é§GÚÏß^ñ|J¡ÛüñÆÆ’§Iy–Q(6–>KD…éÒnÊêWŸR m šŒRhÿ¡vÈþÌçÛ$gÑs|ÇMÒ*! Ë«(?¯TDIn”û‰²ágA!O’¬=eiõzO—»Rʽµ(’ &é÷O?™:ØŠ#ÔðGV-5EÒ'žÌ_g©EýþOÙM䬯õP§ï¢ÇóývÿÚÇ%”rßC?ΦÓwaoòà)¹çs<®ï*S9~Zø÷ —ŠÕÁ"ç~K3BÂE‚`ïŸ",·P` ¯ó¢ë“Ÿ<ˬ¶»z@[B€†wÀ1Z⨳ß3#ùO²2ÕÙB«böƒö k+òò!!¡î;FÞÐÉ‹ˆÎ4ÖþÕ­<Ñu÷½3£§No°r¶á®}~ó¤Xüò¿ó×2V.JÈ’7W%ù,[Üg¿dQüÚ¹kÓ·‡Ë·P#Oó¤s=èÛís¶RPH²wºÁÛB­ &Swœ™Ê?Ï Hƒ3*ÂÔTþ{o·ÛzfÌì¨t>]¢m÷Ýdéýˆ03æÅÃhz´?ÐðŽÊ²,1ü`Œ-·r9«Ç„9~æTÙvβķ‹ªVÄ1wŠuªRÄàë:×¥b]œzåšôxò:ܲÍ2ç“9ŸTÚÇ*3tQå†Ìu {[ƒ„i|èÌJ«\GO*Qš©µâŒÇŠ*=ëqàoå+¼Š¾ˆ9ƶ{Úî!5PÊ Žz,¨Z¤ÔÃfû÷6Ûkn,ÅÖ¶X±Qò¯Ž§kv²ÆébYÎM [×þÐŒ áØ,‘¼€‚émº~®‘B¥òò…PjÛËP.æÛ•e‹Çÿ:ô±ƒ”fü~á%ý ) Ÿ|ñ®õ7µÜ‘ÔÐ*íÊV*ã÷Ét9¥ÇwºMˆ’éõ” öÊÜKsæÑÑ'ÇU}lU[´{Ю!@C“>&ãöéÈ“ÌzM,Š}Õ­b¶Aåôö.˶+3ví¿ê^†¤þ)nC¬ÍE‰ÏÃ2è¹ÍcVmñ¯'ߌmɰ̻èºR^é*È6èC£— eäÿù—úô1a9ú~tá½ÆÜ+©«=âê)©«ó™úNãçËñ²\r UËèS [U\åÝu!@C“ŒÛçÀ’¦gE½i œÓ>í«Wëh¨dùÊuˆ ü€áWG4¤g‡E—oÛ½Ýg”–|sÔßäré6ÂÈè%­Ü®ÌåÐN uµ7b±ˆ°ß?3_Ö;›GÆ\Jm‘AÇ‚ Mêá[£¤®-ß¡œe0ÔáèP‡ÊÏojvë)¯e:r›ö§¡rhºšðu GèÖÃñþ¼bB(%[ó5ÛÎ4g½ùäUtÜ÷_?eªÜ_—³îD}<úµJÄpãaú–©©‘ô× P†µ u4µX w :%h€zˆSÿð³û(.W²Hqô“áqÃÁ›ýöKO‰èù¾kƒ¿L+%DÙ²Ûz¯näÜ<|Çî‘0òV?3«¶ÔUï.øþÑšxÙ¿“„Tb¸bƳ=Ú\ÉÿJ^ÊšÉÿ^Q¼öà2¿¸JÓ³¼þÁˆÜ̘ü—ÑÓ,où¼JÛðeܼ¿YeQ6#9è½á¡Qý«ˆ”à¯êÍP̲ÍÄ<õ 3ƒ¿ÒT ¢xOoó%©üœØM§lx~ÐSó÷; ÑüïåénÆ᥻Õß±ÖÛpj¦®×Ù[êÛ¡«³l9é×àͽ-gŒÕµ6ï¶óßÙož¦$¡¬æ™Ñ'PΤ§§',KDqU8oÿX ²jO_kú;c©zOÑYá¢!½µÃıïðÏSýÅ$êJVÎÚÊi¨cÐY!@¼ïĈ_?¾GHß¡—U~÷ÐB‹;ìÓ¡'÷ôT½ûÀuÙ£ËOK‰š†óÿÆx|¡­*â*MMñØúÀóbêãt¡äCXÃLwÚâA[¾44–¯¨¶þi%9ûüáG7ýÀªl̲êZû*Îð|ëW¿ÝÈN-&DYeì«ïÝ-lÕ¥ÎnÄÓ.H˜–êK/ÈO0·ýŸÇÐì¶ÂMÍcS^ùÍÍ÷>ÚLÏãàçæòRs Û”K‘1)ŽÎR[nÂ.]GcÙf㣋r³’wº%ï””°¹Ö“z~¶fà§v ’(S’’“$ÝP§"§|¦ÑHc£òåÒòG¶Ú£Fœ^uÕ^si(.·‡:ñÏ!$³ ‡/sÇ ³Â.À»a0¸eÓ+3WO e+«2 OÀ òð›A2•NE%™(k’Ò켜³+®)˜ûó8véó(Ç!×èF阩«äG?O=¾.õœ¯]¸·¥9¡œ6Ð!vv"%K†ºSmåâ®'Ý:|ghà«à@ELå¬]if~¦tA»÷›dÆÐ Ä$yÂòUq–ÿ£/V><þºêÆbaW7a—.ŽÑãÓ÷cmãüó§oêÝg¯…¥¼°Ëa—cî\šubº‹ËÎ\ÅcØ,ùÆü:(‹ÞT#óËþ§©Z÷l cøe¨³B€xGŒòé•Y)ñ®3â·é°"ïÛô‘äÔ(¨ÑçĹ©ðnú÷±šF^_<–½w”òi—¥éY}Ýíiî£å¢ÿõ—Çü÷êÆÝ%¿u¿þ±"³ÁiMPšw`‰4=XÞŽ´­B Ï&ö¾}ãQø¿ZÜùèUsLßù4ux•’™Xø6_ñãž8Úß HN¬É¦¯{YëË•ÏÝVßíÑš° H¬Ú€kööX#É·¯‹£n<[ûaðµ¼b¯ï˜j®­¯ÑMò;,!é‹xb-u…1g{=PŠýŸlMåçFdŠÆ)Ñ?ØÂœ¼§ùÒæuU´äHŠŒÓﺿ{vrÐÍEkåçÚJ’On3“é=B"ãᯛ§Â–Ä`Ûž£”žž+"¹1¯ Rò<èô%÷žõêQòÒ9ò#W ¹Ó/@,ò?–𽍗fƒÓdÇOHðzL/è|ÐËF…^`™¸¥nÜJÍúHS9k%§¥¢EH²$™EJ’™¶‚4™½¼WXñ×qÖíh: Êþ§1[gJ’›(..¤Þ*›°KWWúüÉâO#ïÄ2^œò?[9G¡ßD³Ù}ƒ¯Ò³šÄôMO"î“’2\eÉâ'Ä­YrïR>¡†\´BÖ³ø>eþ~C-FiÐþGáÒ"+'muФ2ÊÆ¡‹£Ó…ĘÕ`Ç ³B€h&\eÓ²y“L––’´D[µ[Ù_ý™rtI‘$z yIÙ‰Ò2ý*Jf¨¨Xj“€ "JÊM+%Ê OYiz^ºt!ãÈeÅ#UŸ‹ÏL¢zb*g­Xzz&äáK¿ñàèc£ÕýY‚„¸­‡ ßl ”ÏÈÏЩ:3Ùý»ì²g ‹Êc6U%u1³ TÆ6Ô1+¼s*™lrúìP CEqNLæýÉ3ç5†º úGÈõˆÍéa¡¡‚Œÿôûõ@_N^T=¤/§óÍ!s–(\¦öXÊÊ¿]ívYÍŒU•Hÿ®CºY|÷‘²äAÇR‰E äÕ#ÿÇJwƒ—•YaN}ƒNª+¿+4+õæã²<3 ªjIeU‡§*¢VÕ‰–uL¨u I,«gÄ‹b2ʦa°,Œ‡p*OÉ`ji©21•³e_ì_œ\,Ê\;à×Ö ù‘ùòƒIø+BŸ}J{T+’ùÐÏ/<¯’w75k´¥}aäBÑõ%Wg>q|‹NµÔõŸo»7´‹.îÜ]…‚æF?'ƒ­aϧ޿—úLRÂQ°|ÏÄe­õò åó‹­û=RûvÓƒ“>Y±Q¥„«hóAïõÛ:™³Þ^DØH\n¹¾6øÈ Izf›Oì»ã µƒý£©;Ãn¯ó?kÎæ½*-ŠÍÖ•Wn¸cÐ)!@´*yC-cBÒ I¹_PX>Y“ˆòò"è/w r¦šúì§ Ð9›]öþ]øºP(ÙÞ QéêÂÖUÓ“¶«2ÔÊ㸾b-›`*g­&Ÿ¼w«8pÙ¶˜ÐôÒ„t¥þûþŽññ£'<Ë"¢×bvïþ/vYu+áUP cügþr7.ý¥ÐáË„¤ÌœÇ©B1¡ª¥.eKÉ.¯ëݪc¨i¹ì~ßew}Û(õ1Ý~Þt{-ϰo¹Ö8«µJ‰Ä³ž{®÷ÜS㊫îvfŽÛ™Êe w :h€VÅÔ3\2šâ/Üû)Èpëp6CP|ýÛð úI¹Énúa44m€Èqzw£Ès1IŒ;6x°›Þà`A=í² -ÉÃH’s)âr¢î|#†8/m³ó]ß×òæŽC÷NÎø?Lå¬ ƒ=dùØåc+™dŠÇ¼Yé1{¸ßìáUvY>1qy¥Õš©«Á] C€h],¥¶;o{çZ~îö'Y¨q3sb¤3` ŒÙ?•Cõ60m€Š3n¹‰òíøBR¸{äiïÁJºŽú*¿§Ò!ºÖÀËV[zhÀ‰ÑžåÇ/è~Ú}0·øiVl=4í´_UÍ40Ç* @´6y³>—¢Tþï¾çÅÔ'Os$oņֆÎËýw¡¶Fù¬É¦ Ð÷Ôr{ý§·m/Â3KãR˜³¿›üý¨øt€•Ô>d¬:jèÝ`µ¯¿Š8ë—Ê#\Eë™æë·šÙ[òY€©œ… ðŽj̤d©¬xäZåÆY\©®*ïÓ­Û OÉ¿ºkmhÚ½Áÿû¿ñ•6Ð}³RÛüNJÝÖbŸÅ¾ZlÄS€N­îYÑU!@È@ÐеäûïQ_õTLØŽ~¸0TtÑu¥S8!\ëë>®ö\BDÅ¡g½\…ÝÏIJ§ìÚöçEªÖÂVîw{èH!@CWÆàjiª«3µ”ËîÅËOþ×eWH!LÓa›¦uï׃MÕQØÊÚC  4teœ÷ÝÝs*­ ²²¤ ƒæ;~5CYwa+k}€2ÐÐÄEÿ³ÁÉ¿”“WÖ/-ÿ3QÜwÓ‰’Ø:{ï·gGp)qI¤ïÕ¯½‚}£rŠaªèŽyoÂŽå£l•)"Ê?á²öãÇ„˜:Eíï~yϹ=~‰–Á€á[6Îvé)O¿Jv¿zá »~)%”²ÞDÇé?ŒŠ~ÿ3¿dœøãîk#9Òž”<¹vq¹GàÍ䢠5~ÆŒïúÓߨ\q‘5¯ÒŽ VH—¥B·­cm#vî߬úã«™Õ wÝqPª:,~•pÿûÃÿœ ˆ‹ç‰‰¼šµÝˆuË&Î2‘/Û¬4+Ú㈧_ôã>! ã^ÓfLÜ2×Ò˜]vbê?Xá%ך«Ùh%ÐÐ(%ûùƒ5ýƒ²ÉËc¹®³5é×™(Ï÷Z"ý¬šõò\ŠžŸÝ;xgŒ$e+›Z| Uœró¼—Ýs~¤§½‹Å•“V•ódýç—®ñt{kËe¤ò“ÝYì&ê~á#{%qê?Çì6ßϧ7’7чžò¤%NfpÊ_×¢ÔÇí¾Ï¥»¤: '÷ÅùŸ¸¢Zz,ÁÔéca››Ç£WôŒl´m4Ùºµ2«%W^´ýÂK÷’EnOSåüØô°›WçDïúuÅ*S¹’ÇE^×诣tŒ»©¥E'Dß}.h^øcÍ%šQÿÁΩ­cÕû­Z„Jÿñ uƒö¤“Ð+ÉNcM˜D˜õôTý”Á¤Ñ¶\BJÓN_ËPTájÛûxN±b "<¶ 8’Átóxܘíf«,!æ= ½,aã=ªÈ{Ëÿ¦x’œÐC‘sìeð›r`Ÿ4=Ë™üýK7#–(?j鼟ñÃfBÄ‚’Ü‚‚T‘˜m J‘ 1)ŒÎTªÈpýâ~zôœ_¥±Ó-Õ½ƒrII|rq©ñsï$úiÖIKÌé™,íþfiŸþ9óÍžÂìïdi_†L˜kD¿Ôªæ+fézLoÆfFþb 0Ìaž1ÝŠœþÏ}Jay"Š£%—éù€ò–:mõ .|Ü‘ &ýO{^~ÞµykCYåõ׎½ÎøÖ É««“óŸ¹ÿ¡· ¬Ù¹ê?S^×ì>#OJ}”ì\ðÌì¥Co:£ó®U §>òq{O«~ëœ;fLY¼½ÔcÈ}w]u~Ÿ!×~1yå­dûKÏüñÝ!?;½ÓÎU/ß<ýÑǶ$¥/Ÿ0öK“Îé°àåº]óŸúíkC§~¥k‡Ý›Ÿ}੨fÔw¿pœš 6š4u9eÒ}fÌZóáÒ­ ŸŒ;¨×Þ^,õ>ûÜ3“‹’¤vúßx®ÏÆ×Þ^wö…#>÷ÜÖºg§ß}ÕÛ£¿säGîÜïÆëß{ûÛêWÜzõÍ÷õï±å½u5?Ÿ|²é€ûôü£Á3îS·|ʸ)¿o|ƒ¹55ƒz&onlúu6ŸÕîµK–.z»ñ%î u½xým£ÿúƒ'jw/ûù˜›wú1—­m|}¹ã©·ÿâü;_sÛ÷¹öÁ§·¬œ6ñ¦NíÓmÃÊ%MÇØ÷’ 3.èáý4 :šTutÙðsg=´0i|ûçÉoÿ¼Wç#þq禉w=?oÕÆ_í8üŠ?9ù̯ù›W?ØðÁâu;/?Ê#w0úú¹Ûš<{ÁÂõÛ?ú¤×¨‰7Ü>hÞ9S6íJJ¸Ï¼í›ü‡ùµë·½·þ¸K'Þpç°W.¸á…uÉî-;Êõ«¥îƒGÍ}èÄ;î›óçV,[¶-érì°¯í–Éß¾²éÿ(¥¦ÿ=ÜçÞYOÏš»äå+nè7xÐØïºõ²ÇÉgˆ€&m{s¶ß¥ßl|ûçý:žvñ˜¹9è¾c&¿¿ÿ†™~ .¯\^{åþÏ;t;oÜÄÚq?û¼þ£'žnz¼š>Ýö%t·¯Ž¹nÁ˜ëx˜3×Öîà+fÍ<ð•è®CÇ­¬×ìšßXê1~öÌñŸã1§ ›öëaÓ’–uî=è§¿løßaþ9éÖì0’CN¶cKY¥Ò¬j¤H@“¦a“¾Ãæ7Fôé·Œ;¥ë·ïý vf³û·ööú­óîžyá_ÞÜûŽ5tEãÇ]N»÷ºÛûøqÝE¢Ì€ªФcóâä¢{úÔ$k·7~V_·ìÓ“?{U¸¡÷õ_›?.um¨çšÞþ"ét|ý°IÃú”®©oø¤þ‹¶×ά)ã×ÊàÇ@õhÒqÑ=#;6Õs÷/?vÕ‡œØâ«§ ·ýöß½üÒ?5û_¿²ºÔÐèköüÄÎm fv {œoªG@“ŽÚ™s¹åp÷lóí}Ï9û¼‘Íþ½k³ÏSøº™¸øÝhö¼½ÄE@uÊ È tQ”öý*-I©T*ûÄHã1‰ˆ€ -#+°õ˸ kÛ¦O‰,‹BJOiÃcV‘ h€œ³éÓ Ë¢ê¥)E& ªÌ掎‘‘¸Ú)6g~ª!û*³€mú* }æ «\<¤G@T#S‹‚KSR%  Á¦/ YÏiÐÕQù/ËÚɳǡüX“€¨—#“emæÒ” ЕVÅ]kÓ·™,‹‚gŒÊÐeÁÇÈÔ¢àÒ”ŠÐÅbÓ‡’e@3 rĤ¤ê?\®v E@THv–«MßzU¢ «5&W;¹'  ͦˆaAFh€TD:²lÏC¢“«|ÐågqÆ(ЩɲˆNß°rL@”YŒ+Ó¦/øéǘÈ PN"yV.OŠÜÐm”ïŸ×,Kò>8²,Ç?VE# (\æKþ²,g§s ü «˜4@[aæoÓçìtòʘÈ> Ì‚'krsµ“³8²Ü «È4@˜Bm¾©,[åe|XJ@@>e6˲yTÕ•ÙaÑ" Л첼é3{`ìcF䀀h΂”Á*ËW;4cXбÀZ#ƒ›>kÇC‹Œ‰|ÐgÙ¹ÚÉÈadYv†Å‘ h€ý¬®ÖËÔ¦ÏΑp8fDžh€½,øPiè,CÆU}RfÔzU­! ÙX‘2¸V’e1¬ìÐ"¬ílzŽÊwù# ˆ•2 R•«3j—¦' ¢³¥Ú©Z›Þà²ÏŒÈ+ š)ƒk¯kFݲL@Åe9•‹MÏ¡|Kc€2¨dC+³ö¨Ì¤Ì¨,\šf–€ ÊZŠ”ÁeŸ•‘†Î& ‘…”† lzƒ+ Mí$ "’\z -ÍËÎO hÊ&ÕM¯!²ÏŒ( @”YÙ¥qµcF)ñ"tÖhÊ)¥M/€ìД™Ø…×5#bX™" " ž³C@@qy]Ú@@@¡©g%  €€€h  bâェN@EçÒ” R¤Ì 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@€¼t©Tªö!gy hH•€€h}@×××Wû(è*I@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ 4Ð@@@ þQ~xÝAPB,IEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/yuma123-logo.svg0000664000175000017500000002153314770023131023275 0ustar vladimirvladimir image/svg+xml yuma123_2.14/netconf/doc/yuma_docs/files/notification-replay-buffer.png0000664000175000017500000016522114770023131026355 0ustar vladimirvladimir‰PNG  IHDRHÈ:$‚½ pHYsÄÄ•+êCIDATxœìXÇÚÇ϶Ó{‡z/R¤Š€»Ø5KšIÔܘ›vÓËMoßML5–ÄÞÅ‚ X@ETºt8ôÎég÷Û¢h,(†ÌïáQØúÎÌîüçyg%‚ÀpjL€°`X„ à lVaÀ°†@Ø0¬Â€a6 +îHØ´Z­^¯7™L0 Cô m ‚ pG„J¥Òéô;9寅TµíÛ·'''“†a6<4Ha3 (ŠFDDÌš5ëN´íŽ„-))iýúõƒa!÷‚^¯Ÿ4iÒà騑—#=ÁÁ0 €{áΕ诅 A îÿ A¹åÞMàÎ =«ëŸ !•†á;9뎂G®«‘¿¸»»5êÞLàÎ9yòdNNN·¶Ýy„ÇÝ…û“j÷Å_ܵuÜ%¯¼òJ~~¾Ñh¼«³îz©™( f¿àsoqø@¢0¬Â€a6 +€°`X„ à lVaÀ°†@Ø0¬Â€a6 +€°`X„ à lVaÀ°†@Ø0¬Â€a6 +€° GÂd2šp‚A(ŠÁÐฉ<€¤ëoAQ¢˜ŒF¹‚Qà¬!„ ŒNÚeôHÙö7À»Š·»¬­Òú¾ÃnaËO«w5aйÏ-›áN…‡Ú¤!×·^JKI>~â|^i›Æ@ê–…CØ¿^{ÆMÆ5ïÕ5_<’tüDV~y»–Ü‹(œ#V¼ö”“˜5Ô†î l÷׿gI9ÓJ>üXâà7&D@ë[gTkeæž„Ìvìâ7:"Èí¾ª7Œæv$cº/„¡èÂé½{ö1lµ-_r³%FÕ¥#Ÿ½ÿÑö”ìÆ¹O^UúN™G‚Üô”½{R.ÐÂ=uöÝ5dþ &ò¸Ë›èY 9|`ON½¿Ñá™ç‰‡iÛ_C˜ª¯œ9rò|‡±Ï£³Ä2…“›»£ŒŽ‘nÒ6øñÆŠ¼C{ö¨¨NÓàCgG_¼£®øè£UjS¿}æ ”È­Ýݬ$dBƒ”&]ó‘µ_úÆóE5:#y_ò²ˆmñ ócoÔ4øåó/~ý3«¸NoêÞ‹ÚVrž|iñ ÜðÂvŸKÓ|üòùæ?$ž“¾þùÇy¡ÖHŸ7V^:ôþkŸ•¶a¿þ#GÞ—°µÖ\ùáÓïΖ·º…Îxqùtù@î ŒÀ$Šô5·­jÇšŸVïKÓcÒˆ‰³ÃƒÜ, s‚Bœ1Ȉ =g=Ì×gþæëß+ôìI?·`jc ÌQAHÑ#€±ðÄž÷_þ¢LÓw‚Ñx‰ÒÉ=j|üœÙ3F:É@WV?ð†Â³ß½ýÒq•®ÿ>£óDR;¯ØÉ3æÎžîmÍŒ $ê/'®ýõ—“—›äî!bG»ÚÊhÂäÛÛ ™ä^UÖ‘Õ¿­JËm·ô Ÿæl#¡"KàdÃg ÂÍ lƒICÁ‰­ÛöŽö|ƆG}pU˜Iß|úpBBQ“ ò|né@GÀLÿØi/²œ0±›Òü®Þ ¥¶èøñójœ8yÉ'_¿ì%e ]­`Å`SkȤ9/Zø±,|\ú3´må‰öèx¶¡3tǨ‹ÉO,±©n·ñ çQAmë‚XNnV‚îfɨm®­®ªS]8UyéÂ…óÙeñf° wHý¶Gª@áî  ]kFƒ¶¹NU]W™Rž}.óBVÉÇŸ¾ê§`ß§ßF˜ÔÓÓÓ®4 l‡Ëþ³b^”‰u]Ñ<ŠF˜:.¤¥-lÁø®O¿ôæó3ÂøtôúÞûN"à!ŠjÐ@h¨Iךz`DZ)cG¹ eõ…pÂâ…ۧ£±¸°¡ƒBã8ºzYñ½­„ùcæ,3ç¡Yy0ùÊy/¾1o¨Í¸=Æðž¼ôëçb… „0ÇZë+Î$%lü}sfYý‰ýóéüÒ41ãQæ¡Æ*pÒÇï.³™…]¨ok¨ÌH>°ù÷Mé%5Çömö5ÚýéèûÌ?BßYV}µ^OÁ¬\ýýd“Î’X[‰б®¼¼I£§À,;Gë~0æ‹´ÔW—––••WÕ7 Œ'‘ÛÙÙY+2©ˆ!×ì$p]g[CC½JU]QVVÓТÇ®Hfkoogk§ò±^#„ISVXXPTÝxA4וçåå1{nÐ%öJ‘ùp\WQXÒIPªÀÎN†öÍW²ÞBZuõjQyU­Æˆð$–ŽŽö$r!§O!õšª¼¼Mk  |bTW•æå—×`†ÂÎÉÙÅÕÑFJë;LzO@Ê–ë´jg]aC}Í•Â:uˆ5·Ÿù„^ÓV]QRRRZRVÕ®51ùR[;Gk™ëg‰®£®¬² ‡P™Â†Ï€ê«Šóó ®–V´é!‘…Ò…ÄIÉ¡Ýñ«Màzu{CcCªºœ,¯ú& 椶ö¶v¶V2!õ&ðºò¢&5Q9¶–N !ææÚŠº5¢Z9Ú±ï+'!”)ö )lUu´77U7ép{v)꺂ò&ò¶ÌN! õ» ÑTYT×aBè,¥µ•9¦Îâ mgUUƒÚ¬*uIQA>M`>Â,mÄ e*MGyMS÷ÞŽ«DWa‘ q°e£7^ ƒ¦]E–W)Y^•­j#ƒ+VÚ;:8Ø[[ˆú<9„I]RXNê(ÂÚÛLê¦âB’¢ŠÚ& S=ÖS9(‡€›Â6hÐ$®³GPÏe\º ÊÞ±u{lˆ£§Œu'­1“®=÷Ôþ5ëÿ8œz^ÕÜn0á]Ë0…mHôäE‹FúÚukdG]á÷¯<³>=¿¦¶‘<èBâêù¶^¯[ÄÎ\õY˜£66ìüþ½Ï×f(£¾]ûM”‹Ò«6ýÆ·[SÔúöº&R5;¿{%yM—O ±bíë—ˆñšM_¾¾rç)ó„Ö~l'êe#¡m©>q`ÛÛ÷žÈÌmlS›gÉ™½„ÆâÛº,~~ÅÓÂÌ}D^wåÄê5ëO+(¯éк&™§Æ1ØB¿°Y?1oJ¸Ñ¥M„±2ëÈËϼ|ª²¡¾CK¡hwþüvâÚµ{ÂLÿ +~þ¿g­ØÔ–êK/]º/·Þwì³ß¯ü·-›zÃ2ÜPqùƵëö9yUդ뚉#(“/ó ™¿èÉø(n¯a9¼³è·þ½ú@–ÌoþÊç—$o]ýÇž¬¢j½Á"ŠÑX¶ž!/Y¾xz„€>8âFÈ…T³XôÚÎÃÍ{ ¤WåŸùsÆ]‡Rò+´z#) éS³ÌÙ5óñ'›4J̺Ѹ!Œ§vý°âÍUõ4ç¿ó¶/ýÒ굜̺ڮѓe‚  ‰Ò}ìÌÏ?9ÓÝ‚û×7ž]¿fuBêÙüÒêvÁx­¼Sàä\òõ¾<‹¨¹ß~ùy¸%Zy%ùÅůŸS5‘:NÑ^˜øÁó™Ÿt2•í¼èéè“7ç6´v¶Ö“[šs½õtz÷„?†Àû£_¿ŸæD¾0„ÉP_œ¹u㆒rËê4:é|A™]|Ç®ìz|êsvõØÒT–þÖ’—SŠÛý'½ôþS®‡6®ÙqødiM ùbP„~ |Ü•<Ð7=èa< Ì=jڬɉ…kŽKÜ»/e¢ë¬ ì¯kù„uŸ}üÙ¾sÕ&„i¥´– y®kn¬­(¼ðG~ö¥¼’WÿóÆÜ(ª¹'fòø>§¡¶ÑhV?:Ï¿^û xìë]`-M555 z“±§ÏÁáðù|ªÆP×dŽàÄ\¿KB`6‡Mï’¼l¶×Ôèx=gõ\MßTðûÿ}óÍ¿ç7i©l¡¥ÜJ$àÒQ¢³£½¥©1çÔ‘dï¨9Sú‚ÆðÊ̤u«7Öb"_d-ãp8tŠQÓÖÚR[­:±CΕ¼¦–÷ÿµxܵIÊ%ÍjW7Ö·’UÉæó{¦ !L—~-q„±¹–4­¾®¡ ïmaª<»ƒÌ½Õ³õLbie'ÒS{sCeeÁá? 2.–üçÍ—ææ\¯ Skc=y-cÑÅuÿ+Ý¿ã†-”Ê­HÇH¯n­®®¾²»¬¢™ ñ—M󥯋¡£¹Ý¨#­F1“uS+Aàiû¿ýêËÏt, + K‘€I…5í­µÕ'l¼œ›×ÐüŸÅK˜7^U“¾ƒÌŒ:ëøžõ{󯆕J[[SÛÙ¢*/-º˜R\xµY£÷•']´~æô¯É>±ñ·µE²¼„VR›Ã€ŒÚö¶–:•*ýðŸ9ÙWêÞ{ù¹©Rs³ÂøV~^nŒÕ{®^LÚŸt6Ê3^D¿ÙA!ŒÙ'=SY£užâîd+½Ï®6ƒº&=åtMc…&vññ³ì5†ëÛÉr¬ÑêZ˜*`¶EÝRGîG›{ú÷»6 é›Û:Œ‘v;XTŠÒx>ßáÚ¦v B0òåêvI|.vÍ[#Ê.ZùÕ—kvl1Ñ-–J›†ê:ÚjUéG¶ää\©©mÅ3s­¸Øu+ÍV¶•d%ýðéÆ?öžçÈä K˜|­è0åû„m0¡ñm§Ìœ}(é|JQÎö-;ã½ü-nç´‘Íõú+‡¿ûfåîsÕ4¡ÃÜ'—>1{œ»R›´Õç·­ýþç­I—ïü/vr|7XÉcKœ–µzZIÚŠ/$5ùÆ>õãÿ–[_¯+ašX"¸*Æ,æ½üYürCQêÊÿú¿òNî´}õéÓ¡ÝÅÑ8ò·+ó‹Ùrxã¯ß­ú#¿Éhí¹ðé'§D‡*¥\" ÚŽÊâÜ3içPkûž' âÚx=¾ü …OÐO¹€ƒa7u¶Ôd¦Xÿˇ.œ_³fOoü B­FÄ}µÙ+ûøÆ%Ïü·HÇ›þìGo=ÇèIŒ±IgåÖùM´WgÿøíOëe0Aä´ÅËŸy,Ð͆ŽâM•ù‡¶¬þ~Ͷ¢¼Äu?1\\lgÙô©ak‹3¶7²½Æ?õôâYÁžölÔRU°wãÿ[µ³ºôôÖ-;LJ»ºK™·º÷A —¡#+5µ¸†ô°Q©ÂËEÎêÕ‹·•ùõ«Ï×íÉÀ¬½ŸZøäìÉcÜ”2BÑ·7^<°êçŸö§eþ¸òg {×§ãÜû6’:KSÕ°­|_ùä…™qA|†IÛz9ýðOß}»çDÁ–ÿ'”Û~ôìXÞ_Ì,Ø.³ž}Mèìçíf!âRÉò"pukÝÅ“‡6üöÓ¾39ׯ0'XÙÕ²Â<"â¼ÿ,9]qòàÜÙQaJ~o»Lê¦ô„]—j:0‘ó˜˜hKæ]x½š†ŠÌ3iõükϳɠk®-ÏùÚã³&úºX3`cC} WfñÏž(ÿ Â6¨@˜KȸéãödüpðÊñ]Û÷ǹ<͹õ«ˆk›ïØ|4ã*æŒÿÂï.µg£ÝÝG2¹•¥Ø ëüakÚ¹„»b'ú>=†#\‰Ü¨u¿ú0Ê’Êärö,ÃÁˆC¡´ ¯U¬4¶H&“õ®+o1i—hÌKùcÓæœšv¡kô+ï}ðÌÔú¡©…•]@xœFc #×n¤ ™øJØÔ>Ói%R™½£V½üñ¹‹'“S/Žõ¶`ÀæŠR$•K%¼î×›ÉÊdræ¾ë„1çĽGÓ5&ªüS|ôz¸½°û¦±ÌÎщßùêÏ’ó);÷%ñ[$¹y nÔ:Æ<ÿáçÿ ë Á—IåÏËYuÅ…+÷^(ÌLɪ|ÎMʼ«zÔØÙ\ßPwy¤¨iÚê/¤&üüßå­FšÈvì”É®’J‰ëZRvþþç±óZ–íãKßþhÅt ¾–i©¥ÒΆKi}õ¿I¹);v'ŒqQrûŒ-L<×%¯¾þò¢qœk•¨X:ýi]Ó^ûß#‡wn›=>(ÜQx;‹!Ø2 îÅ€ñt*zS¿¥Djcçh- Õ¼ôîÉܳÇR.L ´á˜E â*|&;pþ²ìIé…ÁÊ´WFKEÖÞ£mzÄqDdL°ã]uæVžÝ÷Ö¿N][îËÉbìlmªojǸ1ó~Ùsý¬ï+š…lùÉå¦#—e®÷ „&’’›ðúãF—3Lí:n—s ¡t‰ùíº1F¶QÒ÷oÞräl'Ýráso~öÊcrFÏC.‘XØØÚ ©­Mï¼xfÇ£¼ù½ß|ÂdÔLyò½_!£wŸei¥¼÷´n ¶AaYMž=;!ñÌáü²=Û¶Žòí(ºÕÁ… GÒ›µ„À!`îãÓì{k  íýæÍq,éÂ…ºÊÔÓ§ªf‡Ûóï¹½zÆÎs©ÇS/ÖPPnDüìi±ô>æÀ9¬W„ÑXW½d4Œæ±¸]‡~¡Þ¼sÕu9—›µqŒ;U°Áµ5§RO–6é™"×isfÙ oÔzDe+ÆOŸ{pOÒîKu§NÏ,Ÿ5Ö‘ÝÛr†Ä.~ÎŒÞË ˆ)uê¾.ñb[[uAY îguçã„Awùà¯/άy1mCuYiyE]³†-¶ŸòÔŠçã÷íëh(>´/¹N ;„Ç-z,NJ¿éVBw‹ž>vtBZaâ•´seµ­6QŸZÝ>0&~ühN¯Õ@!˜ê3oÆ„”ôâ}•9i§r«Â„·íO…P½^^&~£«—°ñéã$$@F>[zEΞ›zqmÅùÃ[wñ]1u  ½ZGÖž 1øÌ¿$‚Ré4Ò}¨±ï"»“Ѩë0Wm4Á¼MÇ5pjÇO_¼÷ÅŸÃÝ7$44Ä×ÓÅB*dÑ©d ME«¿ýhû©Ê^_¹g£N¯7¯ËN¡±æ•~@0J§s0 EkÔèõÚ¾7Dþ4OW¥Þ=ÌswA(Õ)fáûoN1ÉZ7è:UÅ—¶m\³ò—Y‡ÖÎH¾úp´íõ`"B×®îÖN_,Þ"o%KR¹%üþ©ƒNÐç%[JŒRp-iƒñ¶©Àµu»~ýòíOWPgïÀаP?o7…LÄfÐÌåÕRºá‡O6'÷-.qЉؘŸ›ž°/ç±Èp¡QÝxòÀ¾ÜÚªØkLL”yüéÎòíV倠“+Œ‰Ÿ:nÏÉ¢Äò³G&Ÿ‰ñŠ¿£ŽƒA†ÐuhŒ:ó;Hå‹¥â[…›šË‹Á·ô)/²R ²þ:F0Ha{@Æ‹š6g¡äM§ÊíÜ2!6Ðe ã0:£0¡!´mZ¨2t©Ë "CPX0‚P»Y6h :­áö„I•{j뎣&ÔwâŸ|°"ØÙ‚I£"ÜÝx×ÖHö¯´~$”Ša]nn—èàÿ‘öèÈšÝ|( ÃúOà}€@0Lep”îÁÏ¿ÀÒו|¼.);qëÚÍÁî/Më ܇H£ 2gœðÇÞþáßcy´ÛxãK ê«Ãfa×â}Ü83d²;õ¸‰Ã•q =¢º¢sÛwRi!·èÇ?ùèÕ+s¤§¼tõyÉ´ bŠ'MŠÜy"¿*çÄÑSyÁv!Í%»3Û˜³_ô˜ÛAìsCè|'t¬ÎÔSVÖ¦1q8½’ev2xwº¾AüŒ¹¼`¨4ÿ©/¯~sêmÝ/ˆ)è3Sûá.âòÛ9†ÌšŸzåÇ’ìãÛv{.¸ÿ;³eb‹F´˜Z®¶éM¢¾¯ ¡ok¬WÕ† -x Fÿ7ãAÁc0ø D)R×TWªL^ÂÛµ•Múšâ3—T­0Ç"jÒìh_û›š¨®m­­­ê¸í ï4ãöd3"zn|TFñžÆ+d'q´ÇB‰Y@ ¾·BšSU^|)ùTî<óL»»ºt[Ö…‹ñ<³wºq}SVzvs«–Ƶvö²¡ßîš„®µAc2¡T”+÷é(#p]Qö©Ìò¶[$ æÛûÅF‡¼°ãò‰¤ää¹ÇŽ×j)2É‘#ØØ Æw&MkYA^›ùQGE6µçâT–`¸^߬ªVã”Þ x/G]ENö-Œ¿' ®µ§¯Òâ|iQUîñ“W9E8Þ׊*€ ¶q•³gM:v:7¿0mÛÞ²†Nù2ö>„%vå8»²¾èÌö퇃]ZÐ{j‚h¯Êݱs×åZ5…¦¬èYUÁX6¦ÎU£ÚhÏù롯{O 5*ÔãÏYÍÉ{· õœëƒõ]~ѤÓ©4šy'¢ÁPS{gEq‰÷è==·¶0}ë–Ýe­ý>»A4:—Gº£íšú¦z‰`ÝY3­ÂÂBm¶§Ô_Ý»ë@L€ã öõ½&]SòÁ§óT„:Êߎ=T•Æ–ÇM›ºÿô¾Kµ){·'MФ ëD®ÌeÜø°C—*òOl^»ÑÕf…Ÿeÿisæe0›ô¨\Ð?þˆ(:sìpò8ÇYŒ^ëªTœÞ¹çPJ³žbçênuûX*G@ƒaC§ºº¤¨Óä×{쪹ַWÝ·;»¢iÐì P˜"Çq"÷eW•el^·ÙÝþ•`V?'ŒšV2Ïä¢Gèôÿ@€°=8Èzžæ7vÖÔ˜#ßî¾Pš[F1™úô•! јóF'ß}®|ÿúoy4üé9c-xI_WriÛÚ•¿lIé¤ÐÝÇL™6>ðú`<)ôô”£ÊJrOÿ¾~»!ÚGÀ2¿ë•­°’ÓÑAõL Xê3wÞœ¬ÒµÅY‡?û®S=5%j¤¥ÈüJ›ôêš²‚Œ´ ŠÜ7>>’l>Ëí‚Àäý¤?*óŠž>%òdÞ–úÂô-»‡{,Pp0„!Ž›½húéìõG/'mù 1¶.œ7+ÔÛkžH1´M5å—³2RSNšÜ'¼öôœ~×cmî_Æ™Ñ~"6×wœ=òÝÿýrðr#̲?}„Rp[Ë`‰ÒßËRPv¹6iûªŸ$Åú ™T“A]~%í÷ŸVþ¾ÿÌ€)ê[ŒTA°Â'b|ˆGÖöŒ‹)Ç(:-ÓÒ/:&Bt¯1µ%§R’Êy×â.“±£¥þê•sGH½PC LŸ±Ó&Æø_x2ÇÐ`焜šüÔÝß~i‡=7ÓÝZ›tªâì}›~]óg‚Æ|ä ui­¶ÑÓÌÑS§Å%VìÌ0¯ƒ s´ÞÀ¤WG5}é Ûu›ŽœºX–±$ß<~FžÉ–:ŽŽš¼è‰E“½zGXAÝoòS꨷<ŸST[§êþ—–ÙÐ3×¢2X\.‡ÁéÛȇ‡ËåÂ\æ“g!:‹Å᩹)ˆ‚ ºÌgé;ÿuðpÛ¸ewڥ⫹YE¹P×\/ JãX{„„¸Ñ»çî2³—½®‡Y«¶¾ZSqîtÃáÊæ,_øD¼ÿÁ•ylFï/tCU2剗´ˆhÏÑ9W+j«[Ìã)SVÛÖ“ ˜a¶[Çfß4fA˜CÔâw–6kÖî9zº¢<·¶,·«•QÙ²ˆèùO<9{B˜à¦Ä ›¼—Mx ¡› TÇ¥ßñüb£™ó g±øØÏ5C-ÜÂgÇÇdUn®½täȉ¸6R&Bf˜GäÌ÷[þØ•œWZ]_N&¶ÛÑ@PšPî8qlÐÔ1AÜþ'L_ìù­Z½ùlVÚ%£ù£l“ÙùÄÎ\¸ôéyÞRzo× ézP;©l ¯[3dÓ—½ªÇ˜¿lÚŸWU}>­ 6wÂl‰í´g_|bFè©5¯¹ÒÊf ¸Déz¼=Bdžz¬ËO­ ²-¢ãÇÛ ï>¤BY¤eæÏ;ím7ŠÁ(ÍÂÑÓÁÕ+hTô¤Icý]½ÝwBÆ,zõ}‚ºrUJVIeÑ•Š®8ŽÔnâ³ËŸœ>ê̯+Nåµr˜Ì>3ê0‹,a{à¨×ö°Dyu‡àø7?’»xmÞ¹ÿØ•âšKç«»?2h./„Ê—ÚŽ œ7ŠýI Sg~Ú(lÖÐuüóÂvŸ`ÿâc$(l¥Çß„"+m±ëÒ~ûüµî~ÜŽ{³b,QXüSžÁ1—/]),*,W5šÈúÆÚÎÅÕÕÝÍU.`ô›è aËñ _ ;§¦¡¹óz>Ê÷t™E…Ó–¾0÷%RGÝœd½Ï´ ˜³jc8ùò¬<úè0D•Ïý÷'‘K´„çaÓgAˆ#sžñì[£&ÌÍÍË/,(¬®oÅšHfeïà`go§´²èù¸&$Pú/}ïëÈ©g_¾R\Ù`$µÎÖÑÙÕÝÇÝQÀDm…kf¬0 <™«ä†FÖ B[¿%oºNYXÕÐܦë‰ÝGYVò®É< Ï7V®YŠS0¦Lqóù¤Ì»†N~ß3hÜü¼âr•Æ dV.®.nîJ¹ Ï?Ìrxòoãÿ¥£P%nŠ~ßü„çÐùkÿŒ&-ÛxÞY_uÄ”gÖzO"O¡[¸ kÊ’Í~ù3׉/™(šÀB|í0ó÷|ìý¢_r ˜±¨¨¨èjaQq]s'Ù^H,lílm¬­---IvÀiŽ4žbÒœqá.d_.¸ZÖ¦…$Vv^Þ¾Þn&vS|9Äš¾d£÷DSººõzN!ޅד¯6a.Y^E嵈n¡4——·»“˜¹Ëšðœa‹<- Ú$µÃ0ÔœOR÷QãGyß:´èV Vþã>ß¼¿sÀÙðd»Éˆ¤2‰ƒøL-)%£g.uñºpñÒ•¼«­ZŠHáàæéåëí*f¡ÒŸb—QÏMvã™YŠ9˾š¡†h«ƒ‡Ã­/…rlo™ žÃ–ZH­ÿê(ã*ýJ½yµM¾½»¿ëˆH™g=y‹¢ð€ièŒÒmÜüN>ƒ¹9€Üê±!*üx ÍIfÜGF85ŒÌ!£ö¬a­pq›'—0ióÎ;}¹Ž‚rCÇDè½ðæBÞÎõð¿eáþõ`ªÜÞ;Né5ÞH>mz#,œüú¿dº¤6žÒ[”0¹Wnë%·ý ›1×ÎÍOéì=ÚhĻި«¼~åc\¯‘÷ž:À½„í‚|90ýÖêBÌ/0õvS‰o÷ÓBÞ¥Þùw£9 ®•6h÷2 ß¼Bùsw‡1*ín/¡i(Þ¿{q£š­.¼ËÅ! hrà€ï±¼P*à. S{MYec§º½>íàÆµûÏinìØéQÞV`ZภLºök?zwÓɵ¶¾VÕ®1ÚÆÏŸ?Æ‚# @Ø€¿0Jçñùz:ŠÉL)ÂÐÞÔÐÐЦ§°ÄvQ žZ23Hq_~@Ø€¿Ê›¶l‡Ï,„ZÙ9>|/ ¥ócžÿxÏs,.‚ÑK |f ðH„ ø›AçZz¸] îbÛxŒ¸›¸Qà!„ à lVaÀ°†@Ø0¬Â€a6 +€°`X„ Ê!6‚ 4ù£S. (J§Ó‡Ú 3C luuu›7o.//'îáß??¿Y³f1Œ¡6d(„M¥RmÚ´);; ¦M›6~üø¨°‘zf2™ŒF#66µúP›p <€aÅFS(---Àc€aƒ\.‡ïͰÙÙÙ}ðÁmmmÿÖx@Â&†Ú 3C l ÃßßÿáßÿÀ†@Ø0¬Â€a6 +€°`X„ à lVaÀ°†@Ø0¬Â€a6 +€°`X„ à lVaÀ°†@Ø0¬Â€a6 +€°YCGÝ…3i§3²k›Û &‚ÜDe |§Œ 21h¨í»cû…Ô#GO\¨PUwhq:“K§Â7öâD2ò¹gf;É™C˜ÂÔyùô±ÃIg+jU­jƒ'•J-FFÄE‡z0à¿>xÔÂx4!t-Å«?þïæôÊ1~Þ#tJ}Uñ¹‰GO×xùz my”û‚ ºþ‡ ¬'Êqö £Ò/Þ}qÃimü‚â­zî‰×eîK=×¶`ƃ5¢‹Û$BXŽ#BQ*õûO^Z´yÎòOŸŸ.—YÒÿm @Ø$^trßêM‡mæ¼óÖ›ÏÊY„i¼Ÿâ•™ø}_½µ(õÀɪ ‰S¥ìÁ0÷v°2g‰€Ñh#"¦,œï\³o̱®P{Ðt߬¥(åàÉêà‰SJ2ƒ#rru±3 ¤Cbãâæêˆ îíq“€©JÀÃà‘„Ð_Ê)ïÀ•ö|:Üíd@,s9{žPÂBÍ~Íé¦Û!nlƒº¶½¶\;×5Ûþû·ûˆ•cÆÙã8ÔE÷™76oêÞ|í6}®Ýñé½åî@bËìÆMŽ1ÑRÓÇø+ïÖÈîÝd’·ýþí~ÈiÌx»ëI¾gÈ«^ž`Ÿ½¤óÝV“vâd‡”|¶¢±ÃˆSP*;`âÌ([ìô¡?oØ}Q%ûáó÷m­'Ξä ÓuÔ¥?š’v©Ug‚1¦ƒ×ÈØ˜([®úÄѽ{N6jyQ¢ehKFzFqM‡ØÎ{ìøX;–:ûÜÙ3ç/Õ´š¬=ƒ¦Ïœîk+ºC½ µ¦©¡‰+vœ0Õ‘üSÝ\qlÿžC©g[Ô¸ØÊuÔ˜±c#}éêÊÔÄ„äÓ™UzAôø)bBù”¶s©‰‰©ç›Ôz£[»øEÇÆ¸J)çRîÜs¬²…m/4dedä•Ôs­Ü&L9f¤½®¦0ñÀŽŸ7’ÿðù{J§I³gÚËî±dSSÙåãÉ'.V¨uFÁ,ýÃ#£F8ˆQ¨{¿úrZRbʹÚv NPŒF}KõÕ*pÖŠÀÝñ t„ ðHÑœ‚BýþüxÝi§¸Ùqám…g6üøÑ©œÒÞZîåXpbϺu–´·EŽP;Mƶò­¿Ȭèõ–;ƒ'íIr@H¤‡…BÌ¿Çr!ˆ¦«éŸþ÷£ÓÌÉ3âƒlõW/îÚðÅþ£)o¼ûîw dÒfïþßk_n§“Y9.Z@4%ïX½mÿ•É+¾œn‹Á@Ù l€G¶ ÿÒògMk¶§§íû"åà{—€àðñ“&Oˆe-d’G°änÑãÜv&–•uàJꌚúzºmüØ Ž2zÚî¤Ô<ý Ï,˜3Í¡˜|”Òó-\ŽPæîë§àÒ²qEpĘ‘6|Òy*ÏHZ½ù€Ñó™çŸÆ]BÓŽr.¿¶~ó¶Ø˜Øö®J1Fc{EL{ó© bR—wôßO-ÛWeð÷äcQ.LŒRtü·ÅKÞL?—YÙ4[(§Ý²òƪÒܬ‹8B!Ú®ž=—Qä1õzz1¹[Øä q»Ž|‘›—ßIA»:a\Óxµ™?zÆd_{acÎáÕ¿ok”MûjÙs–L\ãß\xî³-ÛFEM|~‚“‹†ÒœFŽý÷«‰¨Zuöõ§Ÿ]{"=+·qD„eW’©— EHä˜ë{U5sgkòÎuæÎùà·Ÿa)1E[BµKÞݸÎ-Ø÷ÍyÌöŠÝ›¶ž¯»ø©–(…pà4%$g•_­k7rXVÀß l€G„&Œ~êmÛÀ±É‰‰É©'Ïfžß·9'ùر¤¹Ë>yûi{>¬÷£bF:|›z:ù|q­/Jh.§eД¾£<¥0DPp ¥£"#åðå@¹»_á-6AL jís'ÂP~>åJ•.b§„ éõ ]ên)2%WTüEóÂæñT³z ¬Ý=-D‡«(\›NEI'ÄÂÉ]ÆãU¶©µz#…B»UŠuõÙ£[ ň¼egÓ力„0·Qc£|6o=“˜œµÐ%Æ¥èrN§¶Š<Ç„8 \uùtæÕ6·H7K.j0((ßYa5f_-ÔâŽ]—€it:›}Z¦ÔÞÙÚ 9ߢéPßw¬Í蚪Μ<©çÈ‚Üé]£eÂ1*ÔZ²éôéô²†éö¦Ú‹…Þ[ézãY(xtziUCG§ž"Ü``€°]`Œéäá8bÔ¬…e—ÎJØ·kûŽý{7ÿìøêô1D¶‘±c6Y•šxrñxo™:ïLN…[ÔlÓx¨1jDjÂÆ/+sO ‹ ö÷`ö» ë늫Ô9Çwü·ú”¹'‘0ÕäÔòĈ0âDßã!R?Л; A)Ý·b)¢f,}i†;éçhª³Ön:…Þä¿@zkzFö€×\Ló¯+·¯ÒgÁÚ*UË´¥o.™Ë{Àê \àÑÄXœ™š ÇC½•TZOnhm®× t¥»’‰^«Ù!Lí´ñð¡¿Ê,f¿& u{xÅùS*‘G ƒÄÂÞcúaCIڹߋ*Û4~÷$1¿6÷ ³ðpá0æd^ªiÇÓûM‰{`DíåÔûÚ\‚ýÍݦŠFçñÇ‘„m«ó,—|8ZxMØQ©‹£ ^Ê­hPK¬ÞFÞI}×ÑxÿžÕ¿7žOKÈQÆ ç %öšCõÅå͸³ÈìfxcIamk‡]„ƒ\Ì¢˜ô—/42–¿¶(Ø^#T._ K¸L Äú@Ø(ÆÎ¦3G¶îòÅ8ó˜4×Wä¤íÚw”á6!“~½ùÁÖaÑ_î½ p÷³êé$3Jا//ž,çÐt µõÍjÒç³å³`DÍáÑt¥•9¹ù Ä’Ê+}""|voIÚõû6·ycƒ„ 7 8f©c=âÑK®m¸1¹çß[©a2˜ºÖºÔ´·655]k"p̼ͥÚÅž=29žá½þ»%‹67ÂDzgléì½æDÂïúA“ÃÉDá&£ÁHH-­X=½«½ è=Mš®-«ÈÉ)°„-¨,‰„Oï£4äÕº4h;ÚÛÚÐkâ†Îê̼ÜVÔ†!„¡ˆ7)!õIû"ÝwP5õy»w©ÂÜVŒ±á¡êÆ«»×ýšN9Ÿ£Ôñs Lc l\}|Ü… 0=ð0Âx4Am}Fz¦o^÷å;{e¤¾!]«ª¦焽úÖâ‰#äH¯Z™!²36jGzKhL´÷z@9Ä“òr[÷ÊÅ{… ³©¦¢†½èÙ§ƒ¤L <6v÷W;>}ãß{½GÎY¸dFdØ‹¯¼DûeíÎÿ{çСX"à $.¡çO…*RΖ™pý•ãXs¢¼d™‡wgT´ºÎä½;,“=$ »*ÚuzmÑþ½ â¹Ä×+Ì‘)ùçŽ'&&fw ÝÑÍ_Õ¤Ýè %Œš«9˜ÏrZ¯ ŸÆ·Š›<ñ`–~ÜäûF![áóüŠ0cÕÁ_?IÙÌï2RlçûØìÉÒ΋‡Nç)¦ÂŒä#§cý•ù){ϪÈÖÁ™äCÁb_¹Å ÓrÇÇþóùcæ (¹A',BÞø2Ü€ÐùtìæØ?Ò8¶@éöÒ‡a/ ˜RfYø èh¯ tïñO}-m7Ò›Ú;F¦9‡Ï|×%pfnQC«‡®Hîàä¢1›+x#§<÷ˤçȃD\&†a¤4ã…w§›mdrèT„!˜ôÌ»–~ñd’Å 7w»›¼È[²xV®#Ÿ}sä³Ù8‚D¡¼®Ì¥r,&-ú·oظüâÊN=S™ ;wG+b¶¥²¬ll|GM~<<ÖMÁéêñ5Ôen\»vû¶?&Ίï(¸³'¸w€°Q`”.µq"nu€yÀˆt! ×·]ÊÎçxGz+…½†› ºÀÂ'Чÿ™ÄX†ÆXö¹ŸÐÊy”•sŸc­]C¬]oÚ=®Ï1ÑãúžÕëVˆûH÷[íø*[!h“مÈìúl+}Ç+}{oñ‹ñ¸ù†@£¸•‘r{Ÿ‰ödÕ€GÃÍÚÅ—üé·hÈOýnÕnøò¥KföD‹¦ÿš¼3y§ê›Õæ¾NÐ xÐaü-!LºŠ’œNºƒ«‚]{6)Oç7'JÆK6 -xSQö™|Õ´Öìý„¦¾\UÛlá8ÊI ¾„xaü-! YÇ6í+’ň³’£vc&Ú ðnˆùöþÖè¹Äm»í!7…Ƶ5¥9‡÷îÎn·zâÅùî’þóã€Áàï Œ°¨Pþ©%—¥.~cž^8Ó’æ!’¸yûÝ·þÜŸºcõwP×lBEJ¿×?žìJCÀ¹Pþ–ÀT^ÄÜ·¶ŽÓÌäò¹L°ªÅ#Œ2Ý£{#hb‡Zkêš4‡ T:“ÅbÒ€? xhaü]A|9ãÞª< ²©Áá39Cmà 6 +€°`X„ à lVaÀ°†@Ø0¬Âÿu:¥¥å_ú7 ÚÚÚ~øá•JõÉ'ŸÃüãA@Ø`øc4‹‹‹ ÃPÛòÀÂFDGGŽã\.ëÑ@?`FQ””·¡6äa0„M«ÕþöÛo¤£ýŸÿü‡N§µ9ðÈA •J5™L¤'0Ô¶÷çöÄÊúvåùM˜7q¤•¶òìŸ;“«êÚ */`Òüñ~ ˜0dÛyøüUµÖ(”ÚOŸ?[É£ ŠE”®JòŸ3d3|„Íh4êt:|‘¸ D p²·flL­1âý*;B_tþBFmÞós¹,÷¶~ŒÑm\\˜úLƒžŠÆ²Ë¥-loo{lP}7‚0”_Énen ‰µ’PÞü—瘌z#£b·ö#!Dlíè«!D äV‡Ü-Ú¶êsg3NIÌ4†/xÚ¼ÅØY·{õº,tù cŠ~ݰy£­tî…5¿ç±Â—¿‘wà—-üéh·\ÞpzÛŽdÙË¢”ú5ß­üu§Õ "Ù÷%Ü7¸îüý´møé®éõú¡6xä!%BÛ^^V¡Å‰Êú Å<õSßÙ\Z¦2B0S¨°–²‰ÎÚÜ¢òöVþÕ’Ú ?ç–Ú’šfDà,™­œ¡¯(.6J nCeIU£ÉÙ˅Ѩ(騚Æò„ë.j±fz:H0èZ7œºµ®´²€žLɃš‹Ëji‰ÒNhš+++)<Ó\[VÝD#°°£íW‹«è!®nkÓR¤JGN{ÞæÕ«ë%!Ïp&:ɨtZnƆª²†v=AJNªrmyy«7éÛ K¬]½}œ-‘ë‰'ˆÖÚòš-ŽCR[;!‡9Ö>T1nVW5j16h5(Oꬴ4t6––×âÂ’X[“Gt%‡¼B“ŠÜÜÒ;S¥ÖΖ"Vwri,‘Gpl{NFî¹k{ÛŠ“ÎV¸=á«”Éé~îµ›·;å•Yã»ÄßFnù¹}³moF~cù™ó*Î\7GK‰ÑÇöCê™Úø0–ðþ»[¯õØ?H×{t>ÂFzl@Ø€¿€TµŽšÝ«~8]ƒº¹Ú·–©4:¾½rë?gµs•CA•iæ³Ë½™Mµ յꂼ…¿œ–PÚ.€«/æklŸ]žôåûµ^‹Þ\{å躯v–½þãÊQ²kïhª+8žX †JË«¬… %7j ~ÿá·JªB‚4µ°Ç„9üöëv÷9Ÿ¼÷ «>wÝÊõ²ñKg{S¶üö{K)$êJuòé±îû¿þ°BîλróŠ2βðÌ©6/yEuýÿ³wàQ]gÞ?·MïE£Ñ¨* ‚È€Œcc×8vlÇ%NÖ;e?gó%ÙÝì“ì³ù6Yg³Iœ8Õ‰[ÜíØÆ ¸ÐAtB½÷>ý–ïÌ$ËHIH˜yžÑ¹÷œûžsç¾ÿûž{î9IVÛhuø3½õ×÷OÅ:S¨žšÆaÍ]_yDUýΟÞ?›š—5t|û¶Ó²ýÕ2ú³ÚKýõû~ý‡·åñéòþn{Ùú …öc;ÿü³¿üàë½ß;ÙÆZÙÖ—_Ø–~Ç·¾_ñ¿úLÀâP»Îv0÷þãã¥)† 2ˆÞö†³{žñ Åé WZlfuÈŸR¬ÒbDãŸØù›Üþb“o”ëbUÃ#'*|kLjœ¢ÐÅʇ:êÛµ½µ#ÚDµœA e0Æ4t »|È(CsûŠ¢èõz笸+•6š¦!b€K"!©ãܾ>¬_÷ĺ6þä›§v}@uTïõã¶[ÿïãwdõ}÷kÿòáîÚ²û—,ÉNOˆ»é–ëãÕ‚R~Óu6çà§¿ýÇÿÚÕËÝ” §šýNeLMuˆ³ÂX@ц¸d§ÍÂç_³~u¾Šè‰$ùk+·¿}häÛO=TÀT~í+OÕ¬¼îÚÌßè0JÞ«K+]·<¥á£ß¾sÄ÷ý_<˜áßóè?þáHv~¢Y>¿èöo_¢ëÿñÇÜw”$Zbéå×gÊ…ÁóÕñ¶~ðÞ»ýÆ[¿ÿàÍLïÁþöOÞÙ±$µæ“^zžô¥þGŸøÐ/ú‘Èûü<1Sên?{´~àîÛÖU$È|2Z#Ou&PïW#V[´æækt¦Êçÿ]»èƵ%ÞÚ_ßÛóàž\kkøÆCÿ¶so]qr!K!ŠQæ–mÀŸé7»èÇ%0\ðA%ÍpœÄ{†z=H ¥P8E ø=.¯oXÄ)Á~SŠåd”ϘÓqTpÄŠäóùæ°Ì+“È6|'—Bé=Û%W9S­çãIt Ô5ô¶UíßòJµwÈ'¨Ü.~\g•(øúêk>ÙWuªÄÍÏt¸¸ì«nìo¯üð&Ô-Çýq%k7½ýñv>™7|Î^ìP¡zo ôØþúY¾C”xÞ=‚D®P(är“E+IC“Wn«ëhÑæ'ÈiD6%w®®o‘Ahìiéêîï`õ6­\ÖQ½ë¹×>îwñ¬)kmIr®vÛ_þã» KoØ|»%ctt-·Ç¨»Om{éýª¼MO¬È´6í8רÛ~ò“¿)‡|¢ Í‚…M<'÷íúø`•oÔ$J¦/*_W¶8aªÇaŒ\£@ ¶«ˆïÀiNe°ª¤Þ/ŸŸ‘ÉÕj¥WGûy1øÈS |’B%cæìÉdÂvÕÂÓ„bX‰øÏFŒà…Æ`KÍ.,MU/+Y­¶$)F;ï$‘¯Ýûêÿþí܆»îÉtž1¨[ϧOÿA …¥Þ`ËX\´È"+½f£Ù‘b‘'–å¾ñþs¿)̾ö¶t‹÷Q ¶ÌÅKÓM\Ùê›zÙë»G ˜º'ŽÂzÀÈ_P0°búE‰S›JÊ‹Þ?üá_÷GƒZ»ñ+ÿ°8Þào7$$¥š|£MLËýÚ¿ýg݉}/?ÿÜÿü^™úƒ/Ž&‰î®¿¿üÊ@lé·–Ë<ý.‘ÕâfÉ-,v(—•­ÕÛ’Cƒ&qÄïÌ^©¿ª“`¾È ¹>)E%î Lƒín­:%¯hàXÕpï0Nñ¶ûôZ{Š#–IÓuº¼útÇGYª%=}.> WY³2Ÿ¶6·s‹‹42V¥U Ñåõú%Þãr{ü®ö¶Á­VÉt"¿?à«¶«½¶ªºŽoïñùý^_@ô{qºà ¾§¥ÐÈú::zãÍ:CáhÅž˜ŸÌ¼ÑÚ9X˜Š5ŒÕ(e³ºbùKÿôÇž’õ™-Åð‰)ù Ô¶öžáü§Œee|Ÿ/ |Áø-I|@ä ©½³µ{`$FîóàÍø Š„‚E9»êOÖw¥±û»¼Ô’|{cõ‘«Mu&ë8άôözíq·Ýµ$X{IìjذMWR´rÕ©ƒ¯ÖûD>àvû‘w{ܧ?~ã=í_ÿªêÝ¿óÀH &™;ÑÚÚ³2+OÅ1*tjŒ16 ¦jbI ¸GF‚/ö ž‘‘¯Ê¨1§T”$¬>ÜÐnµ¶údRû#g–Æ•,5ýí­gÿ˜ž@;R­m½%×l°¾°ã/ükªÑk×5Ÿ«>1àðËeC-]YÎkÊw¾àշ̷ݺÑiVQ_°ñÛ[ßþà/Çw¨Ó Vnºq}zŒ:¹`Í] ‰å%v·\êò[îÛܳå?ÚªÍZ¶ª$MëWë™@_ý™]n5M ô»2Wž©Üùö6ãò,}—‹ÕÒîÆÖ‘ŠÛîì}eÛs¿ü‘f“×Ü»yUvÝK¯µ7Ÿ}ëo ’Ü.Ï’¿úƒÇo3pÁ>=I)ßpåöíûwÐkY¿y­ÆÛv¸¡[Cûêl;ÓX5Ìh[¼ÿ?==Ó­÷ÝùÈ=þ7?~ù?ö¾’[ºé¦Ùýty¤§a÷Çlè“|ž-ï¼wÍêÕiŽ›ü²÷õ_zæ·"«»ãž»%Ƨ<üÿæÎûŒÄéï¸ûn§ANëWÞ~{×G¶þi¿`È]¿yórõ4Æú‡Þße¦Ñi‰ý$þ7z¶"DØBSj]RØxžÇ7PøßÂÂB¥R¹0æÀ•Åh–ßöù}"¢X–âyI¦Tq(oÙF¯ "šaJƒŒÿóOî'“)dì²Và€‰“±| ÀÊ” ®äÿ•Ü&HÇ2ø¢ãJ9%f䔊«ËØEßÎÛäG4§TÉÇŽx­5«Ì6,0×­W(äÔáêݟ>b1P©š çno§·oßàQ®~øa13‹¡ÅG;–Ää‰ËÒ¹K„ôŒò”=²ƒŸ²ÎÂëî^CkuŠö–E±ùZ$¹–âR—}屬Àû ª3lª–¥zw ¢/–®»M9Bg9w6}òJks¿_2… (Úž³îŸ¾»J _”\©dxý}ß[ûE‰åä”´éo †&3€ˆ´J­Dy_]ºÑ+JÍrJÅtß•ÖXRWßà(ßpnO#WpXFãŠîy$7c°+S)§)J¿ôž‡sƒ]£)ø„äWÜšY擤àÛ Ù´°8p`×®]ÅÅÅ999ƒA¡PL5_ ÛUÆÅ»"±æ¹\®úúúÇïØ±ãСCXÕžzê)6 :¡Y™:8ÆçÒÔnÜŸ¬R­»<>û®8ÿÿjÍù™Æ."57æ÷vœˆ"õûß3:æ¶ÛPM zéd± •”ÊΜA¯½‚²²Pg'¥T²v»f`<ˆªªðª[oCÍÍè…Xìõ ´¶>¬}åe”Ÿ•ñ‰X4PCêî”>,ÃfÜr3êî^ü‹þ½÷¹ô²ú¼b铽ҡ}eËí„t!s(šS©Ç×”Q(Õ£«8r.çHË46Ô¥¡h\¦êÂDŠ’)T²Ï¥Ðò1ñKc.0ïÒœ;wîG?ú–´¬¬¬ŠŠŠ+VdffÆÄÄLŒá@Ø®2ð]V¯ ‡ ÅgMMMøŽfß¾}ø_¬mGîá2¢ Ÿ¥§£ââ ˜ÅÇ£÷ßÇòƒ–,AIIÁÄ>BGŽ ¥KQiipçS§ÐÇ£„´nR(Pl,Úº ·:hñb´sgpÿ’’`^Le%Ú³'(×^Ô9ùµ4—l¸ë€A–åÐò•…Y×ãȬ«éuám†yßÓ·µµµ··ïÞ½Ûb±,Y²¤”€c8£Ñ8ð]eŒ¶ññÙÎ;q|ÖÜÜ<Œ¯ K­Ysþ»Ñˆî¾û³MØÏâMc[199ÁÏV+º÷ÞÏþÄ!ÈÚµÁÏ……ÁÏv;uÿ&„fðŠÙeò9ÜO/›VµÐ _ láÂ9ÂÝ7Õãc¸{î¹ghhhΰ,· @ø‰aÂD l@DÂD l@D!ÂÆóBåñ“§Ï5 4ÇÃýe,UR˜çLIšQ.Ÿß¿{ßáÆön ÍñKþ•¬¼´ÈfµLggžç9^U×<ÍB—-q&ÇÏ(—ÏçÛ½ïPsGßœ¿E¯U+ÊK—ÆXŒ3Ê588øñÞƒ½ƒž¹6ÅX «Ë–©”òåjlnÛ}ðˆ?0Ç“¯K’˜‘W²´Þò¢„ù¡{}Þ»>9Úì6;œsX¬$I=§YJœ©° ½³uG2kÖ9´‡øúŽ[ ši ›ÛíÞ¾sשNÑhO™C3DQèª;É2ôL…m``à­ÈJÍ\ÎVðyûšöXL†KñŒ2666¾þ-›S¨æÐ¯kH8”ž’œ8³ö9qúÌ«ïjsæÓÌ\^˜ý͵çÎäd¥GÃ$€"FØ0"b’òÊœKVÎa™øV÷Ô' /ÌæšQ¨3_gŸK¡õy\Ç=ƒÂLì¡hΙ¿,!»dÍþ˜(Ì®Yd*]vñõ:KÜÚãu W¾ß?£f !ˆ¢ÆhK+ß4·B;Ô×uî£f‘Q%³#-oÕf–“Í¡=-ÕÇÄúsX \áDްa" 6 ¢a" 6 ¢a" 6 ¢a"Šh6 # ˆfhjŽç»š©!¢(âÿ…þ h†¢Âb‘´cÔ \„›Þ–9OèLIC‡ïTmøñs€QC…Ñ bùí|öË…ã`}®¾í¼¶ÑºôU÷–^[®_óÞîÚý[ëkjÝnV7sþ¦ÒeJ´ ÎMÂ&‰¾Áæs¶×ÝY¹g÷bÇù‹æzŠâ™€…­­±É´èæÌÌ_ç‘Oþö«sU ·=þìåBš…­½¾†Ž]UVšF¿ù‹-¿=Ì}çç9ÉÓšŽrÞÌ’o×ñ·~³ýÕ7í·¯\FCø¡ŽšZEl¶QCÎ £4 LøìÁÍrì¥=\‹òo¸¯0ÞØyðõš¶Z¿\É…Ï&¸Âˆ&a Λ¨4&/Íc|•‡ÛD+bÒËï4;—j•,²[Þ~{ûûÍ=_[`a£ä–¬Õ÷0†,½NŽD{FÖ¶Ê=ov å p $›lkmíPhÂþ¥”–EÅwÿSj¬)Ü–`¤š{v¶ÝðK–•Êd2=b hå®Àg„Ýk, Å©c–þÁ£s¾šÌ,¬¡¦Ä¬"ša°)þÑ-cº¢¹)!› ø}¼«­½±^¿ÔnÒ.¬ŸCù¡šΜn²/½¥§å”?Œ¦ŒZ$ü|Àl.ŠÆ§ …« [òuø¤G2-ÊaÄ@pÚg¹ÑË\!ÏDà !š„튃bØPÿ‘$ºÛ;:Ú¬9ëfMX,j=rxëímÃÃ(禯&Ç™ÃaA’¼]'*œQe¬[”ä:vM1Èßv𕟧ñÝ-7¦d–mLKK –®žÖ.Qòöžû`çÚÁ!F˜qÍmÙYIá$ W láGô×òZ?qÅw™µ3[šrNÀ.Q“–[ñ…”þæÚ}oÝñë㶘¥ùÎpøJIôvŸÙ³]Ôe.]Ìöî]x .€U;Kï~R`Uråj?uä­_þ½ª~ó?H‰Õ†CÚ„རBçH_”Ç5ùû/Þ{¦Nõ§Øt·@¶p#ºö¿q¶®/ûæ¯.I¥ÃrãMQ´L“¢GÉ9vÓðïß>öÉÇÙy©v¡‘oËþ—OVµgß°ÎÓUÛßZëó‹ú®¶®øÄذžàûcVç²ëÂnÅÈŒÉùÆä02ÍiâóV‡×Fn°g–ÚÃjÄç þt ñyø³PG”>'ãÈ,¢Á <ñmÃÉBÓ­â¿~?¢9޽Rf¢œh6fŒ$úz·½øì¾Tqë=å‹b%Ñ_µã•×>­µßüÅ KT,ê«;øÂ+[|Ö¢/}qcŒbG ü€°05’Ô×XõÒoþ÷…Óô~ñÒ¬XÖÝóÑ+þù‹ÓoT­Yžé4R§w½óËÿüÉPÒ-‹—_› Ê„6.¥³Ææ—,o1S‹³r Qrmêââ’:6=?Í –!Šrdæ–­Z5â(rØ 0g%p%ÂÀÔP”̘öè~ýE¯¤3h,l2íÚG¾Wt—W®5jÁu’KoùÙï× ¹Ú¨•Ã#6àJ„ €‹C)µFågÓbSŒ\c±~6©)ÍÊMÖ˜p“ÂD l@DÂD‘#l’$y†ûGºç´PÑçBh6ñ‰ïìQjærÎu¿Çðºg”E’D÷Ðt›…¢(ÜŒ—Ü WÍïFh6Uø6†f§5æ4íñ¹†yŸgÆ`~¯k [5-{Eæ>¾Á³\›ƒ³3œlÆLÂ{¨O.Â,É@!ÂÆ2L‚ÍØzìpë`í\–+!¹{Àn­˜i>™Œ‹3*ª>ô5ÌåŠb¢ ˜˜a‹Ù8ÍýY–µèÛªöµöž™ÎþË /^RK$IípØf<ó…\.ÑÉZN¼?,»ô²sXÕX–ãKj Z›Âc1ÌÔ½Ng–ó݇ߢÙK_t†Ÿ†ò¢‘S)3µÇfÖëøîöý/_rþŽàB 4-ŠÂtŠõŽ ¥:ãØiÔ"ƒù­c¹ùæW_³b> ‰™ñˆ/N÷À½_p¹\sn v®6›mš;+•ÊÛoÝ|Ýààô §}k?}3ÆÐëõ_¾ï·{ºAçôíafö$&&>öèƒ<ÏOsÿiFˆ´¼Å2ãÉÛ –äÅÅN÷Ç6}ciyµZ=S{à*%B„ _äZB¸ 9¶ÇL»zBxÍÛc±XÂmÅgà &...ÜV|¾?ÃZn+àª'B„ B€°Q€°Q€°Q€°EäÏóMMMV«U£Ñ\rF" R‰a;yòäüã;î¸ãÖ[o…iñ¢–È—Ëuâĉ’’’éO D‘#lTpºsÚïŸår!@d9ÂÆ0 9ÂF–Ë¢}>tED3%l8h ¦µ2©€°EäCàyº"¢™ˆ6–e!bˆr"GØB]‘8b ·!@8‰ac lp¢$RÁ÷ƒ¹H€È%r„-t•J„pÛW ÅÜ™sçÆôf•ôQBä"Ú†U-8¾(trþ€¤ÑC¤ [ ðù|jµ:ܶÀ•HmDš° ‚à÷ûÃmÈœ@ºTÁ']%HÁWåh8[p%iÂÆó<ŽØf’IòzZŽvVµ¹‡XF$áL‹b—$«´—ôRØ—¹\õG»«ýê%åV;7C¯†³ŒÔëç—92U }Á6žï?ÝÛ™lNײì¹Kläkî>Z5è.H.·É¸9êç,ðzw·5d9Êâ²éWZ’í½‡êxÛ²˜nZâ"ñÁšîcµ>n™£Ä"cfÓÀÁNooc÷‘SC®ü¤rï\CÀ”mŠ[¨³À”D”°Ñ4=‹ˆM=§ë_ùÕ‰Ê KÎTH'ÛÞzålÊ×W<^¨×_ÊEaÿØ·íÔ/+U÷ä–ßÃÍ`L¦ÀlÝýæ‰?ïÉûãMßvª”ãJ•F\ÕoŸûÄ%sÆ›Ó/´Vp{DFÍÍËû ’$ôtmÿí὚r6Xlóqˆ‹¿§ÿàK§>¾Y›§°N?›È¨ùÛû¾ücbœ|Z?iQä›»>øÏêÞïéó×›T³4WzûvþÏáOÕçV°½ûk?9ÕW²)µ@ÃÀ;ÎN"MØDQœ¡°Q*URžÅ©çZ+2o«0™[MÌc»~¿£ëÖ‚K E1zmJ²Öx„Ÿñ8L^p«”&»ÒÀ ù?Ÿ#;ϼpØíxli©‰ûÜ ’D÷³Ï×R‹oÍ*QÎy`€ëÃ(ã ‰v_˜Æ•Rq1ë¿·r™UožQåμ>ÿÉbQ‹Umz)NfN3§(¨¾Ù×7«I1%)˜ÝE›Ì×T¸:~}â/Zý{DmF"MØpÄ.§ ½:FÉ CIú¶íµ×’Rå\Ÿ¾Z<øÊ©->yŒLr(}©sS¹E3–SäöÕÿýXÿ0/òj]þ›~Ï™—«¥„›snŽ“zvTo¡lë6$äØ`Ç—LfsZ Mêúüñ%4ø°×½ˆë ðý•õož;Û=HÙoȹ³@§<¶¥nO@”É-ñËøî?hksZú†š]LÒ-y·/Ò¨ˆ+—x_ë‡56y\Qž“²¡Ì;Ü`KãÑqò˜ÎëžcÛ[Ïx^–5Wd¬énÝ~lh( QvsÑu Άöí¯66­Éþr¹QyªyGe_›‡çå2SQ fèÀÛuGmæ4¯»¥+ ½.û®åk(2vÕm?÷z5Êy {…ÂUÞ2Zå•kã«4MèèÞ³«­jÿ>$)Á¶>g4¬øõ;ª†zqºJ_žzm²\:Õ´m_‡_Íúìu©Ë:Û·ïîlôˆ’Jq}ú*¥ûÔÖzÜ ï9Òìö†~9 ¶²øÊç¶×ï_e»EKƒ´@؈4a Îb|Â&Šîªöcn”Rh4»†Žþrß‹êøÍ·ØÅç¾è‘'Ükaê[ö¨2¾ö…ÄøƒgŸýM¥+¦üþ±¼’èñ0ÚÇ2E×ßž>ùœÓòO Ô¹«u`ýâû5Tw“+P¨2˜óïüôE¼Ÿ„øÎ¡³§†µ7è¬ òhzó×ý…wˆkHÐ*LrÊ”jÉË5Ç=÷‡7º¹/-¹Éà>üì‰gºÐ7Þ¦ã½ôêE_fúv={êW^yÜf]ýKu•+r)\”—?™E¡ë8x„κs‘ìPÕÓ¯ ¨ÿ±ôaÖß+Jr½R/"ΤKϦ´=ô‡7TδìÜ·>»O̼oQáî“¿fPü²¹ç`ãG ‰ßÏåÄ?~Ž3f§d)iJ’øúŽ­¯65ÜžŸÙ]ƒÏçk{áàïÎ)W|)#ãƒczzÄŸ-ì=í|Ĺ´k°­¨òÅ3Û²ýÃ*ùP¯$ð´NljwÄ'Ý™Åú»£ïfݳÆÀm;ù§_ö{¾‘*?Û²ƒ)+±ª^;ôËWdñ¹¦›õ¤9Õr­ßUýaŸtgÖuƒûH•¿R"Ô¹)Ïh•‘Ûuê{~°®¿/kÅÐàÑ&¯[ u@J|MãËÏVU]¿øKY\ÿ‹GÿR9LÿߜطªßV:îÝhäÚü¨»ÿÓ^¶§ÞUaúÓ‘¿úYÝòÂa”ûÀâðMÄÖ–æÐ!XÖc•¿×w¼Ã¿I£˜fìÀÜQÂÆ0ÌÌ»"Ï3àªyãøïöðmzÚ ²¯°Æötl?ä |Áœ£õ*xÏÙîfÁž¬ãmJvìR£Póâ‡;«úúÄÑÎ,š³.‰)ðJòÞÇ×á¦õËSÖ¿Xû×ãÝ vE£ËZl´_z£Äx›{)– ŽžÀŸž¾ÓûZNT$ä;õvÖ/ŠWÚbe#/wòé¿VdMWKZÇÙ÷ö·Ô.Ò(Uœ2Íš›cÜÛ¸ç`kíF-52Xs¨iO’³4ÛìT‡ŽNÑF}ÖªÄìcMgÚ‡ÏUvžø°ÑS˜|­ÖÃÆKâe‡ä¬:Ù”½B—^Y÷—“¾!·f_W“p]¼:&V®ØÒ~ÖoÑ+9M²9÷eüÞs/¶û\cÓïãô÷ŸÙß\¹!©0˘( üIW]JνvµÝ±;Új†‘ºÞƒGM1Å1KLÒ9«íHó^gzy®5Mn™68dEòÖvï=î7<¿4C‰†ã’^;|¤+aµšUÙ¥VUS㟶øú¸é‰°Q´R¯PÝ1 ÖŽV9U=Úà݃Çöˆ¯Ë1Y½1ÙCy% U¶ï©–ò¿›/óå7¾ù‹¦ÊÞ´ëüž¾ê–ýê5‹-)®î—*ÝÒ£F§UÛËùú5nïë>›ÿµbKÒ€`­B!F«Öô¶õà[+ED]YpuQ—6)8ž¿ô®0¨â—'­qíþ¨¥Š“4ÍóƒÞ†ª–m¨Ÿ‘鳊,Ö1†Ý§Bn–ñ>/ï;¯k’ä9þâÑw)e>ÓîõKc^ºÜòâ¶Ú*“bYeä¦ÓÚ’ úDЦñ1hyvÒí_‘v´¾öï5¯^[ðƒ/;ÎsEϰ0ÌÉ•dæZ¹š‘{”ãÚÞ²åÇõo,ËýÎÖÁ>O ßµ÷-ñÌ0k¯H.º)~¾æý'ñ:“ôxÙ7Yú¥¿×ïyfÏ6kü=?,¹:ß–‚7Ð#pvÍ JRpzÊ_çxt~2âà: SM/c*{4¿}K㾟}üŽÓùè¿®Ó‘*û}†Qɸ^«ÓH ¡ƒ\J®di\]FÅixß0’9¿ÿ5;ž?ð£7,×ÝÓÕïi<ѼuPŽ –|›–þ{·O¥P_8·Íâ[+æ¾€°iÂ6ûgl”"VŸrÃQ×¶ÿåc¿-5}?C§JÍM¼åËɉ4$Äø]£ 6JBïp½¤¶˜UúöóI†®m[º{ÿeí7m=þm­A{dñ«—½µû¥½²;ËJ‘MkêtZÆšäb—»GQáù™^·¨óé]?ØÛ\ugl~ð•Q YCœÒÞ0ÜáE™ÐßpÇcÔyóø@wï¶ÇÄ Áã¸å_ÒnÞ~øßžnÜÓ¾è½FM¬ ÍÖÒ<ýóÏ=ýõÒ‡ob{xbkBÞÙj¹{`B«rÖ36¦ìþ %…$u4þqRÓ%ItóÞ¬Ôû×dÞú×O¿÷vãÑ;¬)ÉÚT[ìê—,—a‰¼íîž»Kÿå¦ÞOnýÙîÖ³7%]ûdÒ GÏüüG§÷5d_Ÿ:§W¤ªý}>?’KÝÃMœ&F#›ÖŠ ~Ám#UÞvè_ƒUÎ*ב*+å Fþ“šžFŸ9GN!qt7íÅ(ؾ®¡Ëx:Ý]:ýb9ò´…—WÔ5üùɇ«Õ‹âÔ)ùÉ·Ýæ°ã_ÂÈð¾Êú»Z}Rª }¶d®¾ßïQp&9ÌY a%¢„eƒÕ™iÄæ÷wœé­ ôh?™—¶ü®âGk?üÑ3•x(»¼Â¢øøøÏÜ}…mRQ|I,–É}¦íÓwQí™¶º•ik2¸Þ½Ã=}|Mã𠎳p¾½/ùcS×ëí­ïo - «ò4¨T©*ùøû{>Ðs²ãØ¡®úaµ»i¯˜Pš¦Rׂ£X³Ú™È|Úåw‰ר¹û6O¶Y;ÂÄXm Vc0îhy÷M‹qqâÚÆúÃ/5žª^:ᆔEë®!wÝΚ¿S‡‡Yç É‹Ü#§Þª>•`t´¹Ù¬˜T-÷™¶Ò¬cEâ²]~WVܧ®iq}C²-]õƒMý¾¾†Ö~Æ×<ÒßM5 3+¯K)}¶þoÿ%Ö¤ií™±…üPË ¿¯a°=Wènuõ÷4 ð+42ë}{ï¡×»3Læ.I_ã0iݘZørý³?óLÑÆeÛœÕÍÛšæxÙ É”'ëÝzú¯G Éî!zLº–ÛÎõù‡ëúš7&­Z×ö2ÞÚ¨ft¸Ödܤ{|ýõý ½jcÝP_/ÓÒ혃“Ó#·»¥º¯¥8PÝßA3gÞ:w:Ñ×êá²­©Ùù*Ç 7'lßVõÔ'ibæŒþÞúOwÍ@捻 × }ðÎé—â™áÊÍÆŒUFªã•êí”"…õ 8cr ãËùþª÷þ´«kIœ6¹06íÆä¥/ÕÿågüY»t¶ß3Pß×ì³Ä°¢¿k`(Q³ÌÄqð€ ÂH¤ ›(Š3ïŠdSnü†qUŒÖ IHg,lÅÿÝÛ7 S:(ûçíÛ}\6“A¤ààŒ ´jIÊEŽBµ¿±<ã¡,ɨg¹$ûæï,ÕŸsó‰†Åß2-·htøN^¡ˆ/Š_—´D9áéš„ØE©›¿ž ÅȸϖW¦«1gYŒòPǹëbV:cŠ‹ügýˆ*NûBq|¡Ž£×-ý6×|NÁp¹‰·:4‡O ˆòÂ{ÒŠ–˜¬Û(2 4e2•ÜŸUœg´‰~±8ÆÕÏ#gü›â–}n4<Åä9ïù®Ñ»D#—+¯ý‡²¤¤Ø$l”μò¾eN‡Ö@1ºœ'–sIV³*÷«zý§g†úhš•32£uõC˲“5ZZÆÜ¸ø ¿"ãü»o*(ròHhIÊE Å&™ìúÂo[-ŸœÂm(—Û‹â–²}Ý ¥Û\°v©Eªå`g@ÔYV¬Œ]–ªÕkò¾ºÔbÒÕº¬û—vTû$iuöCÅö,~XWñ7ôFÃ(ʲþ!“¶ëÆâ"J¹8ùîÇãä6cQ/*Žñô ’3áÆMöeq²óU–É“î-ù^V˾:×0E12†³›Ê¿QX¨‘[­k.Š­ìi H†Íë—ÚÒÒP±½»Áí¥Kt-6Ùò´ßÙÓ|¨; ± §”Y6|ÓbÞ]çr›4—Yõ|·§ñк8½Ä ºa%Ò„mÏØdrÇ §cŸ”¤¸µIq翯׽"-¹‡%D©ŕ߹ìü4éŠÌët™cËÒo/Ýyd¸¹ÍÝÇTò†%‹õæ tå, « &1‡â©7dßüJýGzÒWÇd]Ÿµhüf‹±äcIè»)n•sÔN\oœW§rV¤ß,õ¬rÇêtÇõ¦”Ê”ÒÐFÖTœ¸,”šl_<º,åºÏên*N½©x,«16mô¸×ë³?+‘¢Uª´u™iã#“YËÒn-û[»Â9îÍï²´MãwΈ[“1Z2Ò/Ú Wwó²[ÍçŒÉLŸK­N^ž<öçêôøÉª‹ëë(O¿­ü³¤ôü±ÌEÍEãö6$mvŒº¬r²Æ%Ä®L¿måØ_’$ßÖ¨ˆY}CÊâ™ÎAÀÜÂ6]pÁ]CÍÃ’à8S?’™‰cš‹îÞÛ·ÿ¹ú#ˆ+ͼÓ&ŸÁôT8žÈJØt‡(í=Ûª6$¨ÕÓð“Á‰ÁZFú]~t¬³ÆlÏв³š& ˜’äoí¯ê¢Sî,Ø©RAË@x‰@aáһΊbÖ’¾á£6(u—r^t¼ãúûõÅ,§·© Ì oáiFµ(å.‡Ï¥‘OGÕ‚Ö)”ñk—þk‰ i•Pµ†¢eCúF}¾Žƒ© üD”°ÅÄĤ¥¥étºù)žâd‡Ì0ͽV“hÐ\z¿©F1zÅ *‚uפq˜f}<ಠΤ·„ˆ(a+++‹MO¿pâ` zˆ(a‹!„Û œD”°Q€°À¼ Ȥ›0N¶¨@Å‘‘9!ܶD¸Áëëë;:: •Jå¥30w€°E /¿üò²eËÖ¬YÄÞ{ï½­[·>õÔS0L¶¨ ¹¹ MÓ«W¯a[$IÀAÛìVàra‹p áv»ÃmE´\%O.A‚µÙ`Áa‹ X–efžfÑ& ›H·!u€°EXÕhšžå¬ÀÌÁ›B¡U€°Â°„yš˜\%Gñn[ êa‹ BtE.¡ˆ „ Â[TÏØ˜±ˆ-܆@4Â@Wä]‘F@Ø¢‚PĶ``aS©TðÊ „¶¨@&“AĶ„"6DÞÔ·-u€°E¡®H}¾Ð4n Ja‹ 8ÛÂ36Ǹ €(„-*a XÒ¼^/,0 lQŒÏ{¬g.— 𶍠±Íî=6Q¤©Gˆc“Dwÿ`k—ßí£8Ž$ÓhÊéül$a¨g°£[2:MVù äB’ø¡ž¶n‰¢±ÕjG‚J«¸p—€{°}Xëˆ{«O½òÁp_Ÿ~Íb(ÖîÔhىǒxï@]#çTk&Ùz~ßð`Û€:1^ÆÎͬüX#Y–…‡š°ð€°E ¡…Fg±áPÃ××zîO}çœó¾Ût ¾ïôY1¶¤äñ{Œ&åŵJxoûÙ#Oýl(뫾ºV©˜öËsR kÛëgªt6ƒ»©ÚÍd?~¿É¤8JIR ¯±öÝ­Í¢ £®×–ŽN}ÞMKü¾º“ϼ.«xbé†\†¾À4Iè8|ü7ÔÜð ¯Íy¿„XæBõÂUè;º£ñH¼sý*†»üÀ-4*2@Ä [´¶ ¡(Z—“Ïɇ2î¼/>I×ýæOßûï¿Ö¯­0®L½D^†S%¥kMŠßLW7•dVgzvVÜ"»{ßK[¿û‡Æë* ei Q6B6üýåÖvmÁ£Å*¥¯ötGXgÉ+e¨7%ÒöŒ EÀØ rú—àD‚«éõ·ü¦¼ÔмÏKEë—åìýù‹‡ee÷—Ë.¯cŸA"6 lÑÂ,„í(ŠÑ$ų܈kÐ'IüpÕÁæ#ç<¥HÈM*]¨9pîÃJÚ ç]ÃH›X±ÖóY^É?ܾg{w‹Kâ%UF‰=Ö߸å}&%eõJO[ÓÎO™ìÕÉ…iGS”ÌTZŽdršY­™£¼~TqXU vë^Ó—¬×ÒÃç*[ŽWõúågß‘Y’•]GϪ—µY†¾ƒ;µèÓãÜ-Í~Ξ²¡B>RÕ´ç¸n¹Q œ>ñìï|ÎõœQ™Ÿì­©l:|KÜ‘•¼²P·8eñ–}ï¾Þ³.?.^wyí}~ðDl°ð€°E 8zÎ <õÔø—DC5 ´2Ñb×ò­•‡žze¯MJ©zñé€â»NõpË[¯kïøjrš®ñ­¿í¯\ýÍ?ËëéäâóPåKÇ~sZõä<5ŸœîìHØtíé¬jµ*¹Ðó/'Êå$àj®ñë²-6Ëù8L VŸînÖd¦ÄÒ¬Li‹ÕÄÆé¤KV–ÑÔqâWïHlAj^¬Ì×ÜðÚsq_{Ò¤¢kþôËÑP²žkÿä>í2['¹hu\’6Ö,v:øÓ§ý)+3– ³¯=ãežÌ¯H7f& /ìèlé·Çë.3dê†ÛÞ¯€…„-ZÀN–çùYùYÉß[sæ¥g›ùÎö“öM$eX†¼×Ü‚Š*utW?ûnçѦ´M¥V­K_œ¸&M%ÖoùEeoóª±P…Ò8¥×2Jýð€ÕóÞ!^—rã†s?ÙÕߨ%çÛ¹”GZìø‰ÁÇ{­Uµûëm·ÜåpšÐù~È€§¯ÅMåòà<ȜΦ֩X6Ù’™Äz2…Œ¼Ê@ÉÌv¹VkÈ*HO-ØûFGo7¥Ì•…²˜lJ•L™œf°F½ÛÜäϹ{ulŽ·þå­Gø5éœAƒ×@ǰtñi0§AhðDl°ð€°E 8€EqV%3Ú¬¹y¨ºu Ó“œ»X©¤]#Ã-'^û]·Jð²:ƒ yoЦ)†SÚ˜ác·g¬a°¶îõ÷<>ä­Ù?Ôë—p‘ùå6Ç[Õ;÷H¦~}Î*帑–ÁÈr¸¾öƒh[aÁm«²Ñ§a’(ú]"ËГ>Iû¼Í4ËP,Í*Ø)tEÜÝ#­'›ßþÓðGÒp@n6ÉÔ" Q‚w^ŠÀ Îq¼8 [´€,Y’lV.›Öšs lE1ƒ§¿wö÷¿³'<.ר´ 9I·<”žgF¢@+Ô¨ûÔù%Á×Ó*™ 2­j4ÁÛóé+U•Þ•ßÿ{Th:{ X¤Ñ™¼lÉGÏýI±iSÁõIôh¸†uÈÛVuîí|Ú4ç ×(8QàEš%RFÑŒ\G‚K§Í> ®¡†³Ó*«&>7áÆr–Ù°‰´LÍR’?`!N-›“Q‘8bƒ´`áa‹°°Íô[pYOßÀHg¯È 5wÄ–fç?òÅÞïÿúÈK©iq–íMï¾¢¬Pgjp€¼äª­îÐu7ïn°¯*5j݃nžoó{%† ô·¶íÙ£îéô»†Ü}ƒ(1ÁV^®~v¥M1ZcÝ’¿§öoÏyãtly™»®’’©¬wd,MdèàÚh*k¢Õx=^„4¼»Ï=äæ©–¡î!¿Ãçñû»Û|n` ›xÜ]½¼•qxüLÏHG»Ï맺Úd¹õVîî(r˜ãrâíoµ¼÷’†^£1õ©NN>ˆÑ®½ü'ËÃÍrÕr.¶hG³èŠ ¸ÜòôâE÷d2~—w$ -¸±ä›Bk›¥~ëñÖƒÇûNó&fª’8òÞ´«­¾W3¢É¿>}y)ën4/[¯`ì‚_4•|¡¨{ÛÈ`/›º|ñ—R±‚"’Û3ËW›Ë—ŒyOQœ6£0õ†8𠏧8EðK(æ¡X}ÖâXç¶Îê–¤4ƒÐk(XÎ FwG…([Éu‚Qíé¤4ɉ7ÞªÓr^³j³š‹÷ö öåëPŒ"Àš3¿òpãñ.Wg¥4·ø;ßnØ]9pú˜/.MåHDj¡§ªŽµ-³Å›.?b uE^Îhf[´ŠØf@P%·¤¦ß™š>.ѱñ^Çù¯©æüòÑdÉïF”Ì`/_ŸS‘Íœ*c®3tköα}Ý^ïo¬“ç.·§XÆ«.$éÆû’nD“AqñiW×þ´·ØŸ›÷HÞè¦,sÁ¨1 ëLK×…¾ê“…¾$®Úp~kÜí¶Õç¿*²Jó²JÏÛ$‰Þêkĸ›ï4Ǩ.Õ6—·^HØ +¶h!ôŒ 3…KBÀÝTïñxúÏœÌO2ZÔxÄá£;N쮢•VûºÍ*õ ~„%‹¿þvqÛÖŽ}GÔ×–ªµò9šÜQÛN7[+nv®Îc/=8åÒàY&“AÄ [´ —Ë/ó=¶‹@1œfQéŠÿI§5Fö’ ™2ú¥Y–lÎh7&Xg¦!ÅhmÉ7ÁÕ=¢š3U –Ëhb­ËoTÇ'ÌÈ5KhšÆÂá,< lÑ‚š€Ãˆù)žbv‹Ñ>ͽ]ŒmqÌ¥÷›ê`ŒLkšuöÉË䔺ع\[‡È ===Xáæ°X. [´ýðÃgee…Ûh Ûƒ>844¤Ó]îì\̶h!===))I&“…Û(ÂB·u€°E 4M+.ly€°Q€°9H’ÔÓÓóÞ{ï­X±"---Üæá„ €ˆ¢½½ý'?ùÉøC¶¨„ €ˆ‚eY·Û ÓOG3 lóKp¯_iÇ…Ûˆ|‚œÊåÔÜÍI\€°Í/>ŸïW¿ú•Óéܼys¸m€¨ ¤j0™Y4Â6¿ð<ÿöÛo—••°ÀB" t‹N@ظÆ`ñù|pÑE- ló¾´ð5†C7xÌ ¾è¼^¯(Š0utÂ6¿P¿ß/,øŠÃz¶pÛ„¶y_cXØLÕ ¾èB]‘á6 l󾯰ªÁÍ#, c,_µ€°Í; Ãàˆ ^€#tÑAĵ€°Í;øæ«Ü<ÀŠؠ+2ša›wB7 l°0`acY_tÐÿµ€°Í/¡k,€°À‚±[ÔÂ6ï`aóx< l°0ŒÝMBĵ€°Í;øƒgl°`Œ DlQ Û¼ƒ¯1¬jpó 6倰Í/øã8ž±Ÿí¤¿‘|&MŸÔ?Oš.~&MŸÔ˜© ŸþÎhê§™x‘ô©˜ªp/¢š)ÎÏAÄÍ€°Í;8b á6dÚ`oàChâ{w¸þÉœr(}býøÉ ‘.š>±‘¤OtPâd–„4cR·>UúT|Fžý""þ ó½Ðxœ=@Ò¥ é>’åBm>±²ÿƵùÄ“µL¨Í'6¯0Źà'+Dšù9šJ×ÑLšWB”H±,o⯦‹˜S@Øæ Q2ŸŒoæÅ§Ed&I“zji4M–>ñò¼HúŒ<ïTþb¦Â6©& ÄoND˜ÚßMZ£I›e¢”¦H3rú¡ šbç©fŠŸéD»ÔdEÑ“%RS¤ãDf²tf²kšš"&‰Ç;Ë'¤‡ á&”"‰ì„tœ]F²\;Eá2ò™Xˆ‚¤O у4û<&ýñQÛ<#!.À }‚øxþúd¦ðk]u=Sù¯I ™8Ó25êb¦¹‚GȼIý£r²ýéÉœcÈNLŸQBfL´<äL':M†ÔtâA¹Éœf¨Y&zjšxÞ‰ÍÈ';±Y8Ò,™)SIÓ/²óTûOš>©$SS§O_’ÑmBM‘>é¯Mñ+º(TÅ}Ây‡aäè„mž¡‘Ì*¢ø-e”I=25…G¦§ðÈì÷βɡò§iöL«šhãBm•"d½,K?W¬0“×y†¦éЃm¸›ŒZ@Øæ—PĆ¿Ìøs#t¡—R#d!dà7!dŸFÞ„>%ü„’¦}Dlà 9b?yõM‰Ðõ¥~Þ_ã#dC(gfµ™.ÕmAèf„R¦am7Bï 4‚ÐC¤•¦Ã>„ö"¤C¨}î„̓P%™«¥|†sdóChðDlÑ Û¼£T*gsç¨B(¡ã]ƒÐjD迉®<1 ï©GÈ@tñÆÔMÜôFrˆÿ! ÷ÑÃIdëŸIá‹'dôç^@Þ¿˜)&yDd I"‰À°V=0½ÂE¢÷X§ïžì‡‹:F{ÒÙU.‚Š(ý»d¦Ç s1 pyŒElá6 ló ŽØ°°¡YDl ,"$^d!t¡a„Lõ¢ˆKe‰£÷=ÈV¬jÚqEáM£ó$ÙH<‡ÒHvë'reU/ Q5'X¢¦Þq†U¡ ¡ï’ݼĉ„J&"¢Ï"ô5„–…è&Y°ñ1¤ü¢nRT,™›Ô9jžcTɤQb‰…½¤ä²)nœ½äXr*‰\u‘#êHvŠÐIöT‘Æ"Î!R#¼‹”ÖMÂY7)'žBèOÝP!9úÙŠ÷ì#µp‘ìV²³ƒœ…aRr ±·X:B¯!”;“˜°°Áƒí(„m~Á¦R©æ  ‘økQ‹£Ä³;ˆÓ_ŠÐ „ž!KB-d‡ÿóù¼'ú€hÖÅÛ‰ þŽD~kz ¡f„$ÑahF®4r¬!â÷ÓÇ…/Xж-tUûý¨Pá”ú¡Ä B»ˆ¨‰1eDoþ¡"²s=1ï¿•£:"!Žö ú‰@âº|‹èÊS$Zõ’Xð«DðBÕŠÐ{¤´˜ÌÜMJ;A”þGDi^ ]šzbÆWˆê„Ô®¡3ä ;‰ÒïAè§DŸ~‚ÐJRHĦ"›>D迈ŒýŒÄa¢[äpkHe-$K2‰§qÕòH«b3â!h 3 Ä„-܆a„mÞ‘ËåXÞf™aï\GòQ‘О%ž}Ó¸¾>‘¨¯Œ(%G$óÏDCCQÔ$€û;‘.¬‚/’.ĵ¤‹uò¶#Ra$• L½ÂN(j¤É¿~"í=¤e¶“p°mæ+XNKZ#fôqÚ.öõ“èV]ê¥{ŠEDpA[Ô¶@Ìò£‰|øÍÃ$"I$ߤK Ç «?o…V±t=O$'gt⚌ß{Ÿô­9'„k#$¶3‘>º?’>=û¸ñôÒ賫¯‘nÃÈ“ÿ¼ÁŸÕyÂ@|itxÈM$rÚI²k‰=a:7ßÔ¸/2"¢z2¨$~æ‹¥™:)̨†iIo$Žá2Çm½Œ{`nEÑB+€°-^opu¨DoÒèÚ˜"ñÔw‘8ì·}Ÿ8ës$б‘ÆIò$ê'¡5šw„ ‹ÀÊ‘MÜ}hñb'éyÛþùQ|[q¡[ÈŸçHt3ª ’Ý=úVÀi„V üo²°$ZòHÀd ÏÓIGhÈ67ÑæED’O¡M!»G%p¬Ê¸%C¥ k„†Ví!äÄ=ÍDrÒ9É|¾WpüJ¤¡g‡-äOIaˆ‘.R¦‚¤´ŽŽ4[Ó5t.I“¦Œ>”>Ãs‘ÞN¸¤®Bÿø¢ ·!@x€«pÞ u÷{<ž™m~Ò É’Î´ãD ÃÞ&1ÚÉÀ Úʈw>K,7é,$=It”$ Gd¯‹ô.®$N9åóñM´§†xùarÄôÑMJbCñà«r˜wÍèè +y¨véœü”ìÐO.š‰Ç߆Ð!¢a_$±Ô2Þ„&eÚ0DO$=œ]$±™ì³’Œ•x’h@h™cøG x–8÷\Rf(ZÒ’ƒâpðç£ãõs‰‹½]`$Ú£&OõJH™FòïDí(b†~ô:2ìpÙh}—“/ªÑAžæq±ã?‰ -Áê'•I«$…`a»Ž„_ܸáþ é3üïÑ÷(ðžß#m¨ [EbXÉe&%ÿi+%Qw‰u©¬†|¾?:ÈECöáI¤›F"6è¼HHH¨¨¨Ðétá6 lóE˜qÄFúÐñ)±£ßã4)ÔßÒ*Ûh¢áÿ³÷ðqT×úwÚÎÌöÞ´»ê½Z–d[î½aŒ ¦C¨!y¤¼Ô—¼’úxé! „ŽÁ8Æ€ cÜ«dY–Õ%«×ÕJ»Ò®´}gæ?{eÉ*db0ñ¾Ÿ~öîìÛÎósï™{áß(¦äsz,dÑ4-Œ|ì~pޱ RQìdW›P70¡rj² j¬©lò›vã¥+&ðÜø[úi)é+­,ާ'&W`ää}[T“y}JAši·+'|žXOÎ; ó]«ÝLüs(..NMM5ŸœTÀØ> (>«-7Ô§™p²QûIû’`Ð…rÃÙ¼˜«ÙÞp4b~3œ' Ïø®dµbøÙ«wãatg9œÀ‚Ì¿C\ïZ¸nˆí3ÇøY£Ÿ±ñnÇý3N,…oq}jPg.zÍþ'Jü⃆ËuøÂ@ ¶Ïæ‘G™5k† /î  @ÀgØ>sètºÇ…¸Þu @€€±}æ=¨þz×B€þ@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@l à†‚@lŸ38&2ÜâjuG"ÜèDBéãÕñr EdFYp¬Ï×ÙävDpk®Î€Ïì®+æãñ¶wEdi öi3™iQ\¸É~Îf§þ‚`ñºYZ¤¿vÈŸ¦K"‘öÁŒ ø{»},‹B-‹‹—«ù¾â«Â²#íÎfg8$é“ÕV¶žcCwK§Ïáb›*YO’ðýÜÝì±YŽ$ É*+…¢SªÍñ4;[Ü‘0MŤ¨ÌÄ¥&Dp7Û1K¦”žzËú»]û‚€)’4 œ½1v·v¨”þ™JåS øº/8ÛÇFïeà¤!_ït7t³º\µöSKþyÄöy#q·ÙýáÔSƒú[6›ã¸`_Í@«B·ú‰Â;ãij&9p\Äãk{½ä©Fbí3«ÐŸFˆ‘ðÀùÎCÛÎ?Ó®¸ý©…ÇW‘I$"„ÃfPWÖô`"yÐ]µã‹!y– ñW7½z<{{ê*-ìlHOûNnèŸUõoÙæ©5×N25Í;^éu%*ŒA_sýppMÎ76ÛRvè@õ‹‡>‹TÚájÓ7|9k‰c:vnm<¯”ÛÂþN(íÁ¼;U¾ÒgÏo&âhô¢³Ísë£ ø¤V‡C=ï\xþÜa“­C½ñ¶;¿”2[Š¡^WiÛ[+·1ñ?ücþ’)wvLiã«;;»bäZ—§S,|,ïfÎt;Ïï¯}kgߣKþt—YÍ:ã£À1¾°Ÿ"$W¤Þ+mïÜóJks¼Ò†„zö×mRß´ÅšLDúë2ËÙÊß}˜÷û%÷Z(ò³­¹ Ø>g $e™k›o9÷'±mãc³I€÷ÀÙ=Qþ\v첇cm3Ñ.(*2i²¤ÊjoxšÝ<#pl ÝÙÒçõ¸¡î`àj2á‘`YÃË-¢¬ Å2ìãëËûLͯW}‘zGJçÆmÈO\¯®ƒï”û3ï™õoÉdà|ÛÛvJ‘aØô=i([©F¯¥•ÏIık3Ö®4§°ÁºÿÞû­u‡Z’pLJ»êäüúË Éç+Ÿ|²öùl¯¦ƒxŸ²ÿÈ›ÝÎe·¦’30S (ʺ"}Þjk7\ÑÚþVGÌM¬”1{ÚΓ„iUÖW3ÃZ½Hô™Ö\€€‡@l×(&‰Uš1pd0À ÇF<:•»ì!€é”ÙËã ÁH;úÝ@¡ÁÑ¡`@­ÌY°È2Á昡3­V 0K‹SV'-TDzŽ6î:éöåÚÖ®0ÇÙJ´7$Å­[lLcQî@P*^Ÿ£Œéì{­ðÓL’çÁ]þôoçn’{*vœûM£üWÞìñL؈«ÏïÕ+ó”LÕïξÙNà‰Ï±·ñŒÄ°q)`[|‘<‰~”ÕF¢¸›äp1ÁŽs-;šeø‚¸Âqbã}»ÒÆW^ïÜ•³‘óµ ù}Y 18³:}–.Iù+G<":=OÒÿô™×œˆékqhmçGì¾ô´Ý¯Ì…D—eš®ŽÓQŸé^‹2Iju¸ô½WSQ¼&V*÷xŽ¿`×>‘µž@Fv•üO‰íö›bÓ°Hû³gJpqú# Öê¶©­^˜¾%9pv[ù¯‡±Ÿ¯P¦“扊k´£( õõ„BVc¦ œõu¹P½ã] DDªHítz¤] ¥É1€ -§äŽþa¹ÎH8[†-¤†AÐéó¸Y>óñžg½=>»ŸÈ–aüs„P”ã­÷ ŸçKF?RMóbò·ØBIP@p\*&¨ZgˆC$¼Xf0+(Ñ=û·ùó~µBŒŽ¼ræa<æÓðs¥ÏS¦›×Ymšv<}âXpñÏ–k/Íî²û;U¯5w'¤•w·õ 'êõ‚ªñ¹¦<ÚWöô¹7M±·®²hKw<ÕZúõ%?Î!u]ï´hîý·øŒnûÑçO=é¿|4!Y­á$‡Ap%­aqß«âÍ領b=…Öví¨–|Ť ø/>]¹gݬRirpðýß÷º“ÔÿAôïüsUYqú)áª×Ë3ˆþòÁ¸DaeNÀ5@l×,¨iÙù”ÿäÐ`-OTwÍþÆBµ†c=g;N7ùTÓm@,¯ßv¬·íK¾UL+°ø9¶eF<ü×ö“]©YãY¡„¹À¶Ã%þ‘a*°Ý12À¢É¹¶Õ¹Ûœ #l²Ûç‰ÕçÈå¯3qîýÅ¿ñãÓÄ.4às\t3&Ÿm[ ±”íT'OHV˜p”™m]Ît¨Ø‘^O sÕ´$V]xWþ’Ï=Ô·"«&5–">ª\¥ 2ƒ(Z&‚*›T¬'-NÞù۶ʒÖgÜÉy¬Ç›~Ñ7<a$%ígú# s©ÚP`gã®SŽž †,›H59OžE/ñ ÇúÊ›÷»0ËmÉó¤€ ³~U’¯Ž1€†"a†ñ‡xç…Óq<µ!ÃÐ “ï nÛvîw¥ ‹ŒmîóûÃL„cƒÃáà¨RÇA$ÄYTÄ;…QADsáÈæ‡y &ñÙÑiI”Æ‘ã‹ðŒ+É!¼‰…C|¶3œ~D0‘Æ,¦xÂüøuÊ`\]݇}#-eíÕGíìÏæ-ÉUk’ÇáÎ'÷´.Õj°Ký0Üënî¤3p*n}j’˜TˆpÎHQ"B•¬‰i«z±Ô)þÃâÅ9r¹ ´ï>ðôQ{WžÍÄÛneÆü¸•¤ÁÒÚ÷ؾö ·Ût±¬‹b*5%ýLÒ-µhòoʘÐë[:~n3-Úb͖λ0ìml?Ý4Í1¤ nØyÒÞq·-ÿ„ÉmfØ®PŒŠÓf/°ØŽ¹ö†“Ьyr å"Þ^o{­SôfÅ3ÂcqézôAGh@É,jËp× 7ˉ øÛÔ¼ÕËbãhò8Ò9Þa@”ò´¥±¶¿užlÉH«s{ÓlIÔ'i Þ}²i³¦]”Ù–Íé~öɃ߰êæÜû¥EŠË¿2aÇñúmõ–#œñf–»Ô:’Â¥µ4Ђ«Ï,8Š€’á(ßnþ3Ài ® ±Œ»{¤µÚáÝVþW‚ …ˆäL*XÚ¼c_ëÙ‘ ¤`Rß“}[šœ¯k¸¡cßî¤8뉕z#‘˜Œ`‘h/(Ý ŒLDḌâ[ÃFcýްA‚”˜tK¾»4·×ë47@ŠÈé=ÍEœjþ¾«­1¹<ýþÙ¸BÄ9Bl´Ç8.áÂ$EäUvF`´れ¯ÏßéÄÍÿ@Ó&)Nö¹xy”Ø0¶0nYMÕßz÷ÝYq·<<ëöü’t8Æßëëeª‰h\ªDb¢€®g4zExO¥(­Aª>àê2 ˜q¼‚¼øH\Bò¦Æ&(B‚†a<=Þ¶êÁí°OHÏ(rJ€€™@ ¶ë‰<%ß2φö–øÌ‹•»â 71L„)’u9=n%E¼ýΓˆ¿…l0ì'DJ|<"‘wGZ¶¾ÖîúñŠÿLC›k›ÎªFWÏ]¾³õõ½õF ªZ 4 Ÿ¨49.ÌDD¢&ÃToXù«2.tÜSûÚŸ+(Óœ›Ç*i·¿÷\cÕý ºFpwŸºr 3öCf„×’ª Ó¯Ì}H‡c|Ö(*bÂy±'‚¢´š–ñ>JMç#Ý-… V[R£N!JY$-[9µ 8¿ßdL•VI1qxÀd¢,ãôy¬‰‘È”"ŽáÝ¼Š¾ «ISi¥d(E—eb!-Rc@b–„;= O܈o€Ai‹R}…0 ”ÔɲŒ4 ¾0+'Ä ¥ÎªpÈ= &Xõ¢f‰÷â1Î6Úá,ŒpR$ºlY dQêƒIæ§[ß­öÙ§HÃ/ò–ŒßK¢4ÆøÃ£ ÍÀ’1±6|¿„˜˜¢1äÓ+Ój•cšaޗ碳–¦ÝE/üw»ÛaQ”òy|!¿7äŒp8†Büõé¯6ñƒr à ûÂr 'fSþ‘@1M†*æÝ–òóCWj ¼“Ä!¤˜TI¦9¤ÑèÍ‹onëh‰1.Œ£˜¦Z—Û–4s~¶òÔ¹þ¶b%]Õy.‚§ÏÓTz®Y^n¯p%å°Î õÃê%)©=GË€~yÜ,ÄW·§í¼Æ¸®HeÐHb.œ7^Hg(?¡uëygoŽØ\ÑU£”Ï)T©§7’wïæ$Þ1'ñríüªô|rª§n¥^ÛÝ[â[·bÉéýõæ<Õ}•˜4;S¡ø˜DP:E•‘„î­²gËâzû+Âô*ƒiü gBm';šS,‹¶ä™ºÇÏÜ!–Ã0,ñz4Q™lfÏÖ»ÉbQ‡£Ò‹érõF ŒGäpÞúfwd^z‚¿JÁTéJËÛ}åå®þ5†Ê‘o,ÖØ\ Äöy#ì9ÛUbÃ}'wk™3–f|i}÷ì­|A‡Ý_¿q]ÿ3oý͕լH^”´:CÌßÄv÷—¾qþ$d·S H_r×´yýþ– ®~‹fAòÚ %¿M‘IE8Ó=XÓåËKKq*y‰uöÁ€'Ü5evŒãB=ƒ5e]g+}áHÍžúÝ…1E©”£¬uO„[’8O<Átöz.¼ßxä¼LåfWÄ.ÉRº 駺v½D£ËtYôé·Ê~ݤ³†QÄå®:Ù²û9¯«ºv0c¶Ö¢R¥'KDûª^$C›×Çf‘òŽƒgÝa&Ò~ ù7/I&i9o¯ °C½åFÝì °~{mƒéj ±®á¶:gæ®fƒwS_(«8õ¶…ƒíô/Ϩ,eÚ’ÄUé²+,r{iû‡‡Zš´ö’ ºÔW}sî׋4‹nÍhÛÛµ÷©ÁÃŽafiîc³•jUÞš÷%Oí©WÊþÌ{“ïÚ—áéÚq´ùÃû–çojö×rïKOy7 1WÜ‘Úz¬yû{È^¯xÓ¬/eH%ëo¸PÞ]Ò4pGŽíjŒXæ¦ÈT"#Jšykö–­ÍÇž)­ñuåg<´Ü`…ú*ºËÊ;ª"AϹæ]†HÁlS&pžüÓÁzLßþËÒ{õÄ¥õ2ÏHg·aÃ=Mƒ]úÐÅ~0#=tʪ;2%ÍÛ½’OGjüƒëÌñ»¤½ïøi{¥š`{ñÜ›l…J‚4êòd-û^9÷ÚÍqs7'{N6lmë ûÜ”‡—ëÍH¤•÷ü:?x™môz;eÆÛH*òn%Ç8Ýõ'Zu…pwÿ©Ú¥–5ÎÚçÜ}L(xÖ^íúÜð`MSçï¯s²lKÍEI¸v %Ì 7¹ú–%ݲÜõ÷7Jÿ¯LeQ¤,NZ)S´&àš@ ¶Ï"B“aÝð+à %´F¡ˆrÎÿ¬ù[w‘Z³TùÃåI]Þ¡0Ë‘"¥I¦Âoñkå‰Eq+L8*¦ F±4’=¶èÉ/±„ZjЊžTE"RJÿpÖmƒ@#–BJZ5Ë9mB6E_ ˆH'‹Ÿ«ÎŒYæpš å´^J¾ºð×A\«Ÿ°€`òÒå GQ ˜½Ù ‘qì&ÏÊt‹+´©LI«ÓûŽ¢z)Åâ_õS*ƒX’îU…»ôv'¡µª4K¶4fðàQi]·$.%µ)†ù_ÁÝ@S´´ˆ¤Ë· =.'o —«¡ÄªŒ_.=^ª®ë’,˜õok^b©)kDCøo×ÿ™ë ”DÔ¢ý/fUO¢%c¡•I*¿¢ƒà1·ýlÙ¬Ëo 8+&T´0ý¡KïH„Áp¾=:ÊÓ8j3­}Bž7 pÑÎ1+p"·éqõÜPtÙ WIŒZR<½ œÐ­›õDž§×ϰ„Hm‘ކ¥ÐyR‘Șc]Å‘˜ ”Õ”x?!sâïø¾®{((m’™$8Æ¡ÊDm®Q™²,“!pš&¤J\„i‹VÄ}¯í²K,—&Ý[ü×Í€ÒIŒ$Kÿ`Ã+®0ËM)³¾5wÄd9€R™I6Á»ÂDq[²tØuçWÜ£0HéÏúÊy­Å:sÎrK^O}E¨º‰]cÊ_.Æp€KªfpÚ»¸xj±ISvñûÁ‚r‰uv­i‘ ¶©]推㣡ú:*áß’;Ä„R—¤!l?Xú\øx¹¹¬>¾=àtÈâ€ÃUT˜0{ºxú …´º 8# ³ ˜4+‹/ޝ %Þ²” ê88^"%{z§¤ ·ƒü|0m~ Ap£2Á8µ=£" Š„éé•R«Rzù AÈã5rðI@PqŒ2qÊE ¥åÿ>éF‘V¯t…ÒË­Óp\¤¸…¥’WÙæ«/o_‚`„¦ãÑKé±=JP¹Uuå:#°úl“º'ñ+«ÙQQ"6±œoa l•íÀƒ²:ÀP 6)?¥jw²’PÊ…N:'œ<Åw>àÓ{½DV–ehTö€J΃ ôz¢º:Žwnp¦ de%V÷‚–>@Ñð¢ìmb5 wD d2quu’ ݃@Rr0>‡Oìmfؾààüþîz·{0ÒZåìÖ¬ä â ¼Cçw6Œ“.öÓÂ<{Œ€Œ 0{6ï¾ý›5 |ãÑ__}ìÝ Á7¿É“xï=°};ÐhÀ’%Ñ[:;ÁþýÑô¹¹àë_¦ßº5úµ |ç;ѯ;w‚_6X³Äņpø0è닲ÔãG‹~ãhþ|ÑÿþïÑôü×§ŸiiàÎ;£°¢;ÆkXPT4Õn p˜È“°%Ec%þI¦´4Ú½O<ýüÊ+QYð²ãEÂwÞ X´äåñÖ/MîÝ=C&û®eª.MFnCMÎ{ïb|úo`س¼þ:0ÀÒ¥ 5´µ¢çeýµ¯E‰óµ×¢²—õŽàùç£RÞ°X­ ¶9œÎ¨pyY‡Ãàÿ|n» PBl¤€k€X#Üà"–wŸ’•Š?} ù“Õ-U&RŒ¶(va"%úôú' ^Í‘paI«2 _ñÝ’î¾lÙUs|GmÝ:°lYôènâõ×½÷F?cëRüí¼æ'¡Í›Áúõ—Óóú16öòW>ÙwLJϽå–hpÁh†|Åx’OƒAµÄª–\‹½¡,¸šqÏ=àöÛŠ^’æòå`þüè‡ÑÎä톻î,ïùŸ_ÒU9‚ØYE¬\ñ.ãh8.ÏO+W^NÏ3Ö}÷E?ŒŠ†/eЬùÏü-ãéy»'!áòW~üð•aÙËCE€€±}Á*™›ò2¯æ„¤mk3¼6åOÔ5Sô6¦æÆ1…c¦|åÕÜD׊WŸž×wqéoP ×$¼€IÂúDÙ‘$oA˜è…÷^9·k.k®b @€7bûœÁ1>WUí°cðÒ.%õV]N†˜ÆgzË{.ö5v²ÒdkAì§Ø9–cÂÃýíý, P±J“™­ÐJ?Ë4Ž ùúNñ OÛê'õ… %HWoƒÏX˜-"®áÁp\ÈÞÜ[Û Dø>–&¦ëLÏc TV ùqµÕB’шE.ð\¬vv:9‘\›+W‹£Ûgö´ô5¶…C¬HkÌJ&®¼#/Ó¡ªjQR¡RC!ðUæÁês®îAA1¹NŸ)‘ÓÓodCÃՕCý#ˆDkÈÉËÈKç± ÷÷×6KÒfËÿÄ4ò ú'ÜßÑUv!žúª0[ sB]U.Ê2+¿vïMslÄßReo±3¥äÊ´l•QŽ¢Q¡DÜ=}• C[Ó )–趦\x¤µ®¿¹—Ã%ªÌ<•>:!Ë1¡‘æÚþvGtäZÓôÉ1W<Ç0šáPOK¿2=›cцŽ*^è¾hè•)Þ‘(MŒTåø‡Â][>À€ht‘2úPèd£ ø!v¶tt©Ñ·1¯UwøÌ Ûç .ô5—•<ù»@î–¬¹Éœ§§ïõ—ñÔó¾~¿FKÏ, –ñ:_|ºŸ\£Ë~XJ_µ™¡îÆm¯à :35ܰ»ú½ä‚¯=f¶IgøÄ2o!É™áÆ3šÓ‰ª4Lw}ÓîÝlÜ,1ê³ï~®Í—½yƒ” ¸ªFDV£oש­Žù¿ø±5IóÉyÎLÿ‘ݵ†µ æHO}Íöí ÷}3ki&¹š¶½ÐÑRÆÈ\ïímËX]pÿZ1qÚVµ¿FŸÈ8ÛšOVæ=rŸØq¢ì¹7#ú4µíß»»mÖÆÂ»—ÓÔ¤<¾}ž†ŠÖ÷v”¿]šùÓ‹–E—ŽÂöÆú7^¨ÓåÒÈ`í¶úËxá*•“XŠc¼»_n(íSĽmûZÏÎ)xd‹œÕœmÞýzÅ‘ÞÂ'ÿžSx5Q‚ò åGï½<õoùÁÆ}åòÔlÌÛsñ—<–¹« ‰Ð€½·S+GÞû{MKêê_<¦ÖН¢ ÖçjßñR[À¢·*¼Í•vîÏ{üëñº°½¶â¹WÝŒV© 7ïÛçX}öŠŒáÒw*Þ:CX“Ñ‘®‹‡K³~8&VÜwp{ùÛgèäl9游{oÌ-d.Íœbnð„è,?ÙôÖKÕåKþï·‰ÉÒhc/ªÙyœNÈEœö7_—ιcÎýkÄôeiFZ¶¿>,NTiñ¡ºs•{3ç}û+³„Ý}%Ç.þãïÍîô5¿ú‰ÁøÿÅø¿:bûœàrSLñ|…òO¢ù7Ïzh‘õ·>ûã÷þú’qéjõ’¸™è$#$‰™*“Úá˜v õLk!ÒÌ]i)ZeЃ§·ïùòŸ›sçêï*ž1ʶþ®·žwÉò3ÖÌ'?!P÷ ê+^ÚgÜò%#!3¯ÜdXºJ̹jëwôee}é<ØwjïˆR­·Ü¾ØÒÆ«¯­×HYÒÒÒScs¸á†c_}¢áÃñóÓ‘Ê»Å<ñ»Y‹í;~s|ë+Ýó  Cõ[ß dÝWüè­ ùðÁïÿ¦ÆbSu¿ÕXM­{Xºó¥ŸëË‚ü„”IAüŒßͳ<¥q¸wGQʰhfÑj²ïýËûÿýfתò¢ØËú—ã ÿx¼í¿ò7ÏöžÚvøWÛš æddÑþFª©°Wwçk¤\µâË}eÇþýÇÕ{Ó´÷$]|g§]³è+Hðáª_ü{ý;{cŠÒ4²Ëz,z`àà ?Lj:ÌÇ{ëlkn³.YH¡CU¿ü^é+;’VÏÇ©/W ¥ô‹Ö'-ÓÈÁÀÑ—÷~ck÷ÚÕZcz¨ "D $غ–›Ã ø,!Ûu‚Ór‹å|Áá`Œ™2Ä3ØÖCØrMIjwÍ…ÁžAÅœµ™ëŠI Dœ›rŒp(¥Ì]›¥è|÷ݾ¶¾Ë–*RdÌO]]dZq"¢!”‰„}#,ï•×…°^Š`¸.׿Þ¢£<¿ë¼SýP:M¢¨>VŸÎUTyWH<<ÃJ’‚*ôÙ¦Ó/V÷wùxßøòž.BêbbÔFrhÿÙS=—›•3?C1E8Z¬QqÃf¢]ˆ:[ìr ‚¢(†‘b”<(J›ã­ZÖ³½¢ùãe"à Ø®+øgh¸¯»¼ž°Ì2¤¨Ží?öÖÙ­% ÷>`Mç_yÊ+ÒÏž£ uœiue-xøv`¿Pýò]#ØÒ‡ç\Î#ä …Puî"ÒáÄo_ãTɳ×ÄR’@ûáSÒyw‘ U€öúD±ÖÑØ&»‚b0,7o=ÍM¼*1 8\eMªÄµÜ8±Eý³=¯Öœùo®Ö`€¡õr!ReäÙò3dtÈéa% s4º¾’_¾$ãæ®¤‡*ö´5ùŠ O¢PPR«Õ\¼À˜žDË¢™Nò1žÖ•ÜpûÞ²ØÛnQ›5Œ³¼¦¢kþý›¤ªúé_õ/¼3}e®DÒXö·E–¼Ù«l}¶Ÿ{ûbÒ½÷(°‘PÄÛãžò¾¯F£gÎ “¯ñúHÍ1a_k5cMSêµß&äØHÐÕæE"ŠD¢¯eK šv\t0,7yáç#áo//{å}åÊûR³"}µ^D¢§$2YRšeÁµEÕ»ç/eû{’î¾'V;ܼí•ÒWñÝùÃ5vk6οcI ñtåÓ¿v‡¥óo+ ±é'«a"©‚ ¼çÖt¼?ó®•JÔSw ­S^¼2…"›«~ûäȃ_ŽI0ãígËž~C“‘mµD.þý =ú¬{nŽ”ï¨øËD?ý‰.žÀqñ4ÚÛqä '_›²d…±LëáÊ¿´/CË)*&Ñ\4ŸÌ± –ìi:z6M.¡]1„R é¥ÊÉõ”’–زÖ®EsÍýûNI3ŠRVg ÇDšŽî øÂC½-«°â/klÆàpŒ÷µ£}MC†<%:y¦!éq € ôu+SÌ_‘¼0å:Y& 0bÔfà=i„a¿—‰v1>*”7~BaNlMÝr—ûå=%ÿ÷ …ÙÌÙ‚Þf™ÀHغTq…òcuãXf¨â¤½_’rï&>aphÄßF‘R:jŠ|¡/ß|4<èîu«:aŸ¯EÄv}€ˆhMNaÒê¸û™.»\Ÿ“Ä›«œßçuv»ÚÝÍoo'PÑβ$¨Æž«¨N@E´4.&tÜö…²q\¨¯¡áå·‚´‹ô¹{tð%nÒ”¿4©ääI×­iŽ¿ni">M3^{ãË/³ ¹wo–‹£çeIãS¦¤Ap™eùÚ„úJ~òïTlNÖ&%ÿȱÃ]M[_vG¤$éhé‘]Ò̼B JK…ª>U@]”Z¢“U<Â3z!оvű\px¤¿ÓÙÄ5üc„¹@ƒô|¯åèù0s™$‰E·oÔ˜ä€ :ïin¥øv|ªaÜ8)Çx=!dCÊh”–ãï”Á^å P¥D’R<÷¿³|®ažóÂMï5¼wL¤y*÷žþó®‘á JÊãüVîâL¿‚æã]aÆÓÖøÎ>4~iþ­ËDŒ³áµ?Õ|XÇr(eËšýͯ`´ ô±£ërl„eC¸Z<ó×°Å1™ik kßÿëû¾³ê®‚‡Ö_.Úçô b©F¸a2JꌳÊçæ¢]ýCi™Ä¤œŒ„¦©~ŒLÐèÙx.ÂøöÂÏ(Áû{l´¥¾¡¾®pUIÍÖ^vëf-’b®Ö={»k;Øq™`´¦pUÖ-‹Å‚ ¸Z_É1lÊyè^­–~’ h$àçà‚Oùœ#ÔZQt)28zBò#¼˜p‘zÁ­KÓæû<ž݇[«JZ :Üõî³åÛOG8@¨mYO|79ýŠ;©Eƒ}-¥õÇŒ«ïÎ\‘¸«ËŸz¦«ÞÁ\5ÿ¦¢‡ïTH EáÚ¼‚U¡öÒ£?þaÙ¶8Ã6KÄ‚’ü׃ ³ëܧÏ+V ÷u}çO¯¼¥úÆ2Ç(…2%'籯+åДF0r]¾‡÷7FF0EtúîÒž¿Q}Ú½ü©ojÑFûî3—&)µuÑÒšý/Õï4ÐC|¬nòjo;|ÔƒÅg<|«Æcý£q!^£ð‰³aÑÓPÿ<ÃqîTý¶ÎmÝ¡üÖ¦KÊŠc†.¼á@ãìŸý*9Ñç=zÌÿÙŸÇ=¬’V©³æÎúÚ£Rê’ƒÅf›oœ”'ER1Ç»‡öuUtƬ¾=©0F­S “M•û}A¤¡H5$i)%§’ø}C.–K@#~ǰ2ÕÈkmDªUª € ÷œq±Œ^—¤£õŒeÑÒp˜';BcÕ}D$<çïªi=z* Ïξm­T* P¦ÍŽG¢û@bRD.º9qÚ;ì〚 zBêE†™Ö£mʣ߷®©ißûÚ…7ÿ,ËÍ4Žu3BÐ"’ò½<©ð® /S„Æ1ñÏŒD~\Åóĵ‰ãGpŠÔó×}sÅ%–G@d$3qò"_1RŒ3ÞÞŽ?씤Ü}—)^}Ñ‚T©Ì†öÊÎ`ˆ•PlÐîÀU*MV–O½+0ÔÏ· ù}ÝNY‚”‘®µ¨õÑø}{—SlŒUÇëĒ츕2.Jã2¹ZzÅJrLx¨òdËÉ D⢌u…"Þr¤4º¼y"kÔÿ¦ãâ1ÞEë2«E0‘Î*VámíƒL˜”ä¿"™]W ¸löƼ-g?xö…Ú¬Œ¢ ™úä8öØ©öòõâyI ”\4!:.ììè.µëóWKTc1ÇÑ7ƒ<áÃ|>wwÀ)Apy^±)ùµ oó½He“Îc‹¸ÛÎÿú÷vi[·.ÐVÝ݆–$µÒ_·ýͯ#7/¹¼ÆrwŸ<QçÆ¯¸ôœ±ŸðG"¼Jg}ýƒÁaoÄç#L(àsôøF‚Óy Á1Žõû\CÁ©HBþóC!¤VŸb«ß}ª½rUJn  "b¹H,Å%ÓÎccü»¶UžhSΚOsŽÞR*Óh’4Æ„“Ý•ÍÖXÊ~²”•äXÓL”Vj›£é,?ç]›Ã5”õ;t ·gxÏêŒè“Vp}•õÏKçlŒIÐóCÎ…SÃèTÕù»'¢Ž[î®)w£e\x³iÑåè‘Hb¦9‹è(©JÈÔxÎô†róâ°éG”òÖ†w ûT)[dNÖŽ‡òû{j[Ë} kæe*Ã]þ7#",ÂSò'lÒ›sSçÏ»œôrßÙ:IZº.IåË1ÐÝàhĬN§$×f¿\nµ¦K›ÊØ;fŒ4àrµhÚÛ j†õÖþéíœqÙjÆÑÔíh" ±:›Þ8¿¬8ÐÓÔ¯HúJšÕY lyiè’„úºsÏb²£ª»‰Ž»§PBûÞÝŠŸ›iöVh:?`[s—^/™Wªó¦—6©=ÕÊ·îG éñ0PQ†ðž{jfÒæ{ÇR0žº“‡{S¼UF!¾Ö*g{мÂFÌä_<bû¼vwu—”x#âPåñ–3ªØÂ¬¸Í÷¥•6´üãe™ä~ËÒ[Òú.¾ø‡þƒYlJìÚu†è&þücy¦òÙ?oÀ°4ÓâpkõP¿›ñ´Ú슌%FÉ+žþ£Ú$©ˆ‘ÖwŽZ/G‰±Å­CnÝìô)ž€¿»¹³ô¼Ó6UÀ ¨ùŽÇ笱 ”ŸÑÊS6.¹ü8£(ÓßÐôþI»Aà’–-ÓLJós;Ïì­Pa‰¹¹VcIã ¿Jµ©ØßYÕv$àucáÖGSš9ÝF%félï¶¼þîÙœ²4óõõœ8ÔÝÍpŽæ÷?KŠ51²`O³ýB<öòsâ j°7Œ 9úkêÂh÷øûÚúëꑞš0ûºš†‡2¬oOl}¶þ¯¿êµÅÈãÓã׮ћ`:‚ûÙ“Ý'Zœ5çZ ¡Šg¯žûÍoÄ$,HßÒZ_²ïLËÑ€S”ôð—L6%Š)Ò|ÄÿÆáÊ¿>Å8tîÍX”ê?YÙùþáó‡AÀ1/˜ûðÝJÕÔ)Æëè;WÖsª4ìôÙ]ÏÄd‡+[•EÇ…“Ñ.¤åñý¨pÕ,ÑeA0mzú=w]xïtÙŸª™!‡õîGâ2ŒO¯½ôlïÉ*nd¤wÿ[¡yv6Òt¶üçßñ|oÃ/îg‘ {íGvõׯÂn"~I|f¬Xž'ÛýzÍsσM›¬›np½Yû·?4‹"#}šÜ;o×k‰v.ì*;Rñ7ã¶‹òoËݼ€$Ð(ÿw4tþÀí%#­åM‡â‹óe täbu{FºË«"*Ï2û‘î:‡ µñøpÝEõ^¼hI.Èxì!×_wŸûÕÏä&ƒ*cvâê¥ Å6~ 9Û;O•ôôs®ÆóµðŠvÝ ¹S5g]fmgï;/ I€ÄgÝ·E©ÓÑ÷=:òòžÚgŸ>§lé–ìµ³1” 9ÛêOÕt©Äa×€dÎmy·-&&[ÑpÿöÞÒ²žãM`Èݱçè‚"S–¥ïÌéæNŠt5vòÉpC|Þ·ÿ3#ü,…¨Ûék:QöÛ’D#žqÁݹ·/ EÀ×ÓØs¦´§¬ x.¾½Ã?§ÐœB~ìÚª€ëØ>oàR½qñæÕ…ë8TD*”4…"1ù‹ó×?BHT¼ø'‰^§‡‰ÆûË%zŠÄ¤qi±k7FWä±RÌyåE?ù Ãà”VC‰“—ÿ¹0d …ÿÒ=AN*ÕÉ¡&'d )qk fëÔ°iʼå{iâ γ¢BTô£ß2„J2áMb—Ù2hZ÷s¶Xpǽb½ž`C©ºÙ1 F5ódVƒæ÷éþ{°ýüç~©A¬¤Ì´’¹POö`H,–¸hÙOŒ·ööFËATF}áEsa0‚ÏI¥µÑ(DQÏ`\@iþÑWñ¢JFIïùYÒ»ÉöFbíâ9ÚÊq8u¦ñ¯yö/¨Ú U˰|aF¾7)MLÂÅ ±NqeGŽÉ|â“ ]n‹ˆ’é Yn~@3× 1()“èT¤}YæÒ¹ßÊò{ƒ¼CJëô"/Þ[Ÿ™°å›Ñ5'’¢r¬hÃúWçNœ†# q¢Éïü!(¡[´iAzq0ÀŒ”èu„å06wŽ2-7ù>–8%¦Å"$}nÊúM#ÄÄÒŦ¼¹ çxOlºŸ0Xø!ZMkç‡æÎ¥2ciiÒÜ»Hïž÷9K<öàz±V¸Ú4]Áll$iÕ:‘„)µtÙäHXº”4Æ™WoÑ›3زrðˆ"­-ך“ƒ‹f%ÓR´ø§Ï3—•p‹ç-q>BKéÞ.ù€èæï<ˆ/Ì¥$bqÖ†å…}þ 6ÜŒ«ôÙ•cR¨˜¼ù¿~âÀÃ乌§&SÚcßµ:œü°Çej‰*ºåWTðD¢Ïí‹’ŽVOÒ|rÖ[R¹Fà%iޤ¦:ÑpµÑP0_“=;ý1€ñÝ(‘RbYü]ßЭx`â¤83Qâø‚ÂïYƒ¼ï]ÍÆ)Žä;Ah½ÅT,ÕÍ.ÎbPœáüsòÏž³ à3‡@lŸ7x%)6ÆLÞËãÇßEeZ…ìò[À£ú•*V«\2fJÕréåwK%1±c‘Ë š7ÂG††GB”$äìr2§¿FàEâÔ#ÄxˆÍ¶©—ÊÊð¦&…Ázz@uHNmm„7¬â5ÄÅ€„±šjx¨T ­—²â }ˆ´÷ TU€€ÄĈ;»Årp´ƒCAZ]WG‡Â@!-M ½½hS“D¥•ô÷ºj Ñ½@)íÁ)Z‘™ Aw; iÒÞ=Ñíƒ2ÐÐ@ÈdÊþ –ztµFwú¿Òyl³U2µ=c"0Y§l§ ©5’^¿F(©<öÊ 6îâYÐ0õfL®LœÉAnm°L¼7Z1ý¤ÀŽ ´ÕGä óçSã^B(„œ*•„Ùh«»»ï ž¨uuR½ 8@íùh㻺zèê%ÇAv6[U‚ö9Dª$EG™ž ŽŠt§Õ‚ÒRÔb‘ÑCdŒzÐTö³ ) mo—$Ae9HI‘×]ƒýQ)76SKô„š–‘B#vØÁ…³¼”ù+o8„‡AsH'ÁÅÜh£®8ð@4TT"³L‚ "¥N4iFÁ:¥B>0ÎÖ4å"*שä{#BˆM–雬 ¸Xb¼f{¯ø| Û\ÈÞj¿8àó×wU´$ÏM$f_ê8_óÝÞX›“šiûô+[‘H4Ž.++ú78=1kÛ¶Ëç«íÜ ž{.z<Ûw¾Õ°GŽ€?Œê¾… yÍÈ38tˆçÅhúû·Ié¿ÿýè×÷ßëe6ƒ›nŠjÆÖVpôh4}~>xì±èùª»vEÓó¤õÝïFÓó‰·oæ|ï½ÑSßjkÁÉ“Àç‹*v#ŸÇ†àêXóêxUœé²ä«ª¢çä}ûÛÑÏï¼^~9* ¯~H¥Ñƒ@Ïœ‰ž.4gNô` ¾öíã^xÞõ×Tm‚îLíÄQ,øÖ·àq¯§Á‰ѽöçϼárE¥üÆÑnŸ"e^ =3ïÀ VƒÅ‹£GÏðæÎ¨”ùô¥¼lYô¨6®n`pƒ!¤æ_SEhñ•gw®‘%M?«×VÇ,Z ”ÿó&ñèl£àùoôü¶Ñ—ÎcCoäÕÅ)­™Ò‚IÆ Ïýüß(6n7ß|Y ¼¼xKŒu‹X »}#Òr!9y® Uˆr @j @ÇÒÏ›¥ÀñôEz>wÄ&@€n(Äö¹c€Rº`XÿÌÌ@6ùêcP°LÛoˆP @ bfË›ÞWŸÏÌáàáçK/ÃEÀ™-P?P3î„™ €sxá0Ï€8,ÝÀ0P{€/4 ëÐëPâS¶Ã~`‚)¯X=>Ùe¤0ù„EwÁÌç ™v#Óœ Š~.”æèÆCTïúLÑ À1X 0Y(Z8®ZaMЏ†§[óã­€ZB“ÅQE“Û>ª™XX ~ØäM¨ŸØ @ ¼x¥=.¥é‡#<v/€m)ù6ÚàÝWz²PøPoáÄŒ%à«a‡ƒ!€+¼Å à Ø>wðŠ€_Çd|ÞÞàO@e:CðOÚN(½¹ðù¼ZxØoƒù=¾ õò IÅSΤ\^GôAEÉë…£$A~æp'ä ^ƒë í½ @ÉŸœåUàÔ¡)P[íàÖ`+Ôé±0Ï[š¼¥u¯à¾ ëüT‚| ·P ÀƒÓZÍÀëû`£ž#6vo)lN,âqÈ[Sn| þ”EÀø*H‡üx8À﯒ØF¹As5J$ø,H̯`à&Èsµ°Õ|‡´ Åw­àƒ=÷=v2?ð¾iŒdzp@ò]}€e°&~È»|ÅTüïX†aÇî†õ‡ ¶l×;ÔÃ4£ÄV [” ý–þ($×q8a¶røòåà»°BpœðòuÁܦ†[ ø"B ¶ÏRæ †¶ðýP ½ÀS¬óÀóyÔ„ŸøöÍèÅ8èç9&$žhq_ñ+2á+Ï.Ë¡Îâ«q€û ë:!ͤí·&gÈ{{ÿ€ eý˜=^=dBY`ŒÕxn¸òÁZB¥_ éün¨DÎÂc žïbDzâ¦}˜ø•›\ât 0ÏbHL”Ï"[`¹<#~@.¬ÓKî†Î®z,«éÂýBAaWdÁo€E„žô1Ȧ¿ÀŸ…w`­F]m5$³qŒ@/ß Yù£ºeúU|š‰Íð-Ú…ÎWïÿ 4o†ü:^Ä« )…Lö8Lo…þ¢Vxð#JðŃ@l×ø˜£æû÷]8é º¿jÉסŸ¡‚6#oBÞ Ÿöq„ ¦nƒ†°ªª40k!÷ÃïA^™36‘¢€Î"€Z YG õÈ6øÓm“Ç…Þó`â—`H ç Q2Ö.È»œDâñghzß4Vú¤óØà„Ï ´Žy~} VæMÈmFH*b˜ ~Žƒ.—ª­½p–@¯k=T‹§&kR+T[ùcó´üó9X}0¯d³á]õ0Y#wAf†¿–‹²±)â,Èa-ÓˆM ûì'uÁ¶§ÀÆB¦9;|œ®8(¯vèÒa€qyaÀ‚>ÑuC·£JŸó¡¦~6plÂ>X0´VCá¾[GÃä¹dl˜6•MCëÊ‘HÀª¾ €6§vN*$ì.H?›áõ¾ ƒ$Jmt”ÇÂëëà˜ÝÇ_ ÿ B¡WÀL´°2ÙÐsjƒÆY¿mB>*HC°W;\ =ï#®gŒUƒƒ½í›r„¼ktVœ…ýFÃîBàPÔÂ:|Ây¾@ˆíúƒjȧþã Æä¯†ÆþW`‚gáÃ?¦qB ÎsÌ3P<6!Ÿ|Aåõ ¨ ÖÁ_¨ * êÙ)ËÕ°èj˜óbø0_„ñÄ}_¨¡ÎBg~ÕóYP K¡ȇzä ¾XÍä>¨I˜ :+¹ð®)@`-ü÷0ô6b`ëÊxÖùwp j¬Û+ÖB¿dœ*tð2ÔGy𧉠õŽ«lÔæó¡^sÁâF}M ¼Òflb ‡dV9¦jçl¨â°2Ÿv£1̶L:Í“…tŽŽI„„‰›§©ÚA;´Bn†u«SåYÐï… wr’Îñ¾Àð-P4Mœ ž®Xáè.,ÿ¹6jäÑg tÒ`ÿ?¥oáTm·?‡Þyñ䜩±åL‡z/¼’ …;e1*/9ÖLºúª0>’ó¦MØÿå° Uøó®éН€Ï±]'¡vî€f ÿ8}ΊDàÚ€ġ#àÒËBHè˜gpz'[&dEA™ºÀ §•Pþ „¤µ^φóNS€C³TõÈEh} *Ù‰/Ìq0ÏVhÿfCÅäc#ݘËåƒÈgbÈ éÜ)¤l,:æŠ[€Œ›°˜DÂú˜ ѲÐQ0CeÍ«¿× ¯áDYvѨÉ_2ñ .Tr³’"뉎)¬Ñ4!xqü+?06üp®Ò/즤Jž<ñ;ê—xÆœrêîÈä‰_d,ˆc†ˆÀu©Vha,†&ƒft—¨ã°ç†ðÿþ ÚRØ<軇>:ø‡éG!ŽƒÓ×pi0zü؇Ç\@-†¹ÅÀñ6j ÓòÇèúÙBhmpPÖãÄ3ÚùpðCéŒBý±Z…VÚœ~WÀ‡eÔ˜uǰÒvô2nû„@l× $œiYmçJÈj(TmCPa}%“mð‰s\8œ:­ÎQpÐü|r7¶ê Þ_uG34·WNÎgtÖ.þñlô_ЮÏS‹A·¯ €_A2{*¬qð*þïPŸJ!IMø …¹ÿ'º™‡N !t>Þƒ RÇ‚/NŽ‘Ó(âà„ª^ü†|¦Œ@; <–xtñF žû::¥‚™ü*8'ýÞ‡ú®€?B–"aôÁ²iÏ™€+‚…$˜OÀRxê;P{ާý0=ròc`ÆÍ~è™-sñGá‡u6õ˜ŠÉ9!rØ™W d2®®ù!C4B" ‰V Ï&Ìcƒo›~ ÊâØÏ~X%ßXâð˜¯?C„àÔåëðv ìÛÌHÉB9ž„ãK`·üNÔ#bƒ…æÂ¿.(úípÌ|ŒM&à‹ Ø®4Ðþ}ÆC¾ ãÅPÃÆÃùŸñy’‰‹RT©Ê NU²¯¤ž†Ïç›c×Iø¬îƒj¸27~øè&Aݤ€5©…´:G9ÙÛˆà¿áäÌV¸3N~,¼xFÆCþø(½|íÎc‹êG1œ{l,@‘ƒªméäd(ü5ù¯εæ)eó˜  †“¥Jèñ¸àOH„é0•Ñ)böØèÂ'3F«ÓCG×xè± IÌv4F¥@C‰è GYs~Xv5onP0òb%\n|Iã<º ôðÁ›¨YØ|íµ{úG-›"p1Ú!¬Ò”™U V{dÌÏ»g,žž³B0A7ìŸØ—ŽC*¯É•…"+…‹ÙÐÝÄaŸ,t `"ÐއURC¹÷\¥ã(à Ø>wplyÎuÜ '‹2á:|tÈNÀ)šÈäX;ÚÝgǦǯ‡¡sBýè›U˜ 5ïh{J&³N¼þ|zÛ [¯¿yîÎ ®R§®Ø7À™R5Q/TÜÈ‘.¨Îܰhvlfi4”N4öPÌXÔ53ö;¦y¹ Ž 7!0í+ ‹Î€ü}ª¤Ì–¼Ò” ÃÀ¦¹ g£„^i\n¬€NÏ1x1m,HõtO«aú¹°«Ct³±« ­MÕ€bÁh±ˆ… ‚òGÿaÏù^ï8à ˠ]êNŸÜ˜ázî¹÷žß=¿ï9çž{~FOhÐüH«—H“m7¿£0®°›#Ï•f¯hÁÏ(ó¦Q¼ææ ´—7”¥ÖòÚ.s\=…æœIïcŒŸ1ôRQÚ¹eðÚö²qö-Û,·,¿²õýˆ2ÏèÛiÑ–ÀI=þFñý¸b~Ò`£Ýcx–—ýiã$¦_°YÐÎR*g©ú_†×ïóSáF„mÕ1>çäE:ÐWY™?£ƒ(åàá?èˆsé}Rx”JüW I6õï;þŒ±6î`áczê4VÈ~ŽD­¡³¥g_Ô±ˆcš¿Pféß¡Wí0‡4}Žõ ;dcôû¹Ô†Zö ùç'ôGÏÑÛÉnq`*î{ûyã|Í3Á—1wéë«ÙGÉæQ×x¢&Þà{¥­Ü3ÃNU+}tS>`qusô5ùW†AOqýßœïÝt»9Tî:^¡‡ƒx™Ìü(‹ýoÜy˜ ‹‹¼Ë4ÖFv£ƒßHMКõ•³Úb¯½Mõ=ÆÜŽ„zá”Ä×<ÿæìö)¦ßÂgãs›àÈç$K¸‹–*à$ußWt^^^+l¤vÒšçãñ3ÔÇ´éCs6é³c̶˜iŒÏÕ»(ÛSæ½Ìê&Ç–ç©NÚô.6Ãl µQ:ØPPÅxœ“}’Ù¢*_âíÝ0-2èg” ŽèRcªø¤ÙxÁv^C=õŽ îá5Ì"ºÈû:ɇó§òõL3Âjµ—ÔÂÛyàÃó[–ªŸ«?ó¦¦yÔëüÝˬ.³íøOÝÎÀ÷г‡Ûª“HyØG‘Àêäà,9•ãfï'–îϘS°‰Ó1Œ1¥5tCb':F H X0µlŸõIi?Ïò˜mϵÌYø{Ödÿ•&ô«¾ë¸q ›6c_ ÜL ÃQ…’ªÄúxäÌâ_5˜Âï 5ƒóã›ÑtCy°!Ç‚†ðYìZ‚’ìØãAÏ)½êü‹X÷œ^‡þöeŒ_ÃGx©@ß]tNŽn•㸠3“¨=…ñ?„4’P7PV„í;8T8K¨ÇØÎÑÝ_ÞK2‹ý ½áïÔenä˜ð$¸Ì‰¦yf =ÑoFŸ?:Ç<¶ ¢™a"“í¤ä{˜[Ò“‹hÖ¦1¦<$²¡S@0Æ„ãÍ©ùãgOÇó§0=¯×†Ît"a×Ï#¿{Jð“-úª*ÛQ]§à¹®( Œ¢§ ±¼Z©£%Ì£þKܹ£—áÏÊÕùgµ¢ñkX·Àù¬8‡‘Q…L Tzþ8ƒÏkÐð¥ñIº.öïšÐß„ßìÀþB$XuÛª±àÐù»ã°TОL>«þÄ›]ùw(Æ^éZÞl.‹î ÙõO`™$±Í¡Ì÷k–v\Ð7så”)Ì1|â9.R˜2%ð¨õ|þ§ÍJá4šIg›l¯Í˜ãú=.Å"üoa[u¢B-åà¿ÆD|¨÷Õvª‘o¿#p¨'%0ñÛ³ ll懪‡–P«ZX‚òQÔÕapynô6ã¶nÅÀ¢g`A{æ6è*S]ÈHÇàuL÷ê¸'ð͎o0Ú¯ìW{2-ºƒ¯¾ÂæÍhkƒ·Yɸs“.Ùëþ}lvcô:¾îCr2FG‘‹¾Nœ«ÖÑX::0:€5kp¯YëRíMô«l×áá ´õëÕëm63%&è&CÞŽ5ÔQ×ùO c™ CCîw‡Ú¿(MRОEGyÙ%rЛû†[§§Ñp ÑðǠ¯ –t\½ªUj{&n¢±O/C<6†m.t5áÌ€Ð~ƒpÍ£»1ëq¡·o#;[›C85…±måîœè×a”Ñݳ˜ÁÕZl •Ç»tĆžV8{µÉ††°Ñß ¶_磬¬=C½øöK}¸×­C …¼åÌ%J#&hå[¨š\hÁXC¥IXnBŠe‰5V¬ßëÚ+ª ÂöÌs—®m’#uÅáY¬‡£3ì…l{Š÷ss°Û‘Ÿ¯ã‹>|¨qUWc×.=ªÿ÷ôi|ú©’òá‡:ÊLc#txÉÒRíéîÝÓJvîvïÆ[oéôêX•^‰Hì€-5U÷9RS%îìDa!ÊÊt<¶š~çN¼ÿ¾Nö¬á–›‹#GàtꔵµÚ#‡PµHbgñeúÙ±½]÷tøggÎàÄ Ýàxï=-óJ¨TÁªÿ5â«y<øâ #ͺ i¿Ôº¥W”ÕòñÁºÜZZtH¶ØXrè…t«BYYYVYíÝwuþ‹¬¬L¬6UþÊÊYYZÿ”•ÏŸ_le•ÛÞ½?Lq ‚Ûÿ |3o.’ëù®nŽ#6áO›&:Z;,ƒµkqøpÀÿ–—ë͇¤B¿ùþªïõÆé+*T3()Ñ›ÌL¼ùæã?•U‚§6û÷ë͇êF,‹9¢0úp‹ºqª©¡6ƒ…µ3P ·uëã?ãâ°í5ÝÖ1ÞómzÏ¿òøARí µùPí’ŸûÏ ²rQÑ£øm))¢îùXdeAø!a{æIõ›»&ñœ—!NÒÉ^>¡ D"l‚ BD!Â&‚ D"l‚ BD±ba›ŸŸ÷x<˧A„§C)ε2aSç8yòdOOÏœI~´x‰ÕjµXd-&AXÍÍÍss+^²3,aSuÒ÷»««ëÖ­[+= ‚ ¬”……ÇKüù+ÑgyaS½´ÙYÿØìgA„U@uÝÂÔ¶å…Íjµ:ŽØØX•cTT”Œ¥ÂJQmAÕ@”¡HAxTõ1ÔÇn·«Ã9dyaSªVQQár¹”Z*y“š)+EÕééiU}¢£e² ¬ %lªú¨ºS\\¬ô(œC–¯f6›­¼¼¼¬¬ÌÐ̧¾HAø1"ÕGž£úÄÄÄ(= '}XíGyº A„Õ@FA„ˆB„MAˆ(DØA„ˆB„MAˆ(DØA„ˆB„MAˆ(þ©¾¾ró=ØÿIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/searching-for-yuma-roots.png0000664000175000017500000011162014770023131025764 0ustar vladimirvladimir‰PNG  IHDR‘ûÎS= pHYsÄÄ•+“BIDATxœìÝXÓZð¤Ê²7Š""2TÜ[ĉ(*‚{ãÖëVÔëÞ‚÷Vœ×yÅ-*Š¢,eï½énó¥-›¢è'”^ßßãmš¤§#ùŸ“œ“’0 CÐâ‘$]4 d6  ³é™ HÈl@:@fÒ2Ù€t€Ì¤d6 ~Cfóx¼’’>Ÿ ýÿ+þ«ðÐ$‰òòòd2ùg—ý ™ýòåËéÓ§ã™M H$h¸âáYÉb±ð¸\³f§§çÏ.þ"6///::oaÃÏß'ŠË¤¤¤_Xö7dvVVÖÿ¿àÏ‘““ó Ký†ÌÖÔÔÄÿ…F6ðC¢¸ÔÐÐø…eÿég<ÂûjéÅb±RRRªîþZC÷7g6Š¢»ví;vìï]- Õ¾}ûfaañ“þýíl:þÛ× HµÒÒÒÿ%04 Ù€t€Ì¤d6  ³é™ HÈl@:@fÒ2Ù€t€Ì¤d6  ³é™ HÈl@:@fÒ2Ù€t€Ì¤d6  ³é™ HÈl &Áâ`(J•‘“¡fQŒU^Îäò„@S'Ц*"´0Ù’„1 “Ó2‹J<”$K“Wm¥©¥*/éB5 Œ}jy?ßÝ/𛳽ß3½#á'.ZÕµý¶ð Q:ò髯µFSò·À*€ß2[2²^Í_¶74!›Îdq¸< A D…J•‘•5´î1Òs„Q.J’.d“¢È*àkAY–JüÉP#È*("žÙÊ”Ÿ]´ù¡ÜàÉ®sB2©ß­•`|ÄÂkÕõµ“¾?[ã<ºpôÖ|¿Xià»~ðîrN°ÞúœM$:^Ü,­Å¿ÿ¥¿Ï„cÏê?€¡$US;ç^c'Oq5Slþ’ÿ*ÈìæÆ+ý¶}b¿eÁ‰ Íœ÷ìú‘e­FÅæ\ÒkÎ’I´Îÿ-ZifJzRêg“Í-æcÍPœï tðÓ}Û‰£qErÈÉw@ê±!ß9i‘pÎsÖéüY½Ã¾y``ã0z~fBB‚ØÇ㿾~7pË"Íîs¯žñwÖ“kæÂ5€{ᯑc7ÝÀoùì ðsý©óR@â ³›ÆÉ^ãÞmÓ¿Y¢»úû pu0ÕQ%³$5!æSXèÓIøC4E’mÑd‡-]«9¦€*9ê*Hº0?U3í /ϗ̆Zhµ€Só&Gþdáñމdå×ëóO3±óq?0áŠð¦òì“i5g![$u3—¶Zø)¼ƒ1Kóâ"b‹÷Ðì§{];}¾ù2x€i‹8vF–‘Ãw.lD^–J’ü7ü$Èìff|E`Säç<Þäa#K!+wÕ|—Ãa3 Îm_´ý‡/É’¶dÛcl%]ˆ_ 31èÃŽ.L&Oì£(‘,Ñã• ÆÑÝs)Â:´f¶§ë-zÕGfê¼ùë¿ _‡Ýò [51[…>ËŸÇçTTÈ0 ãóè÷·ûNX\†Wq²CÆ,¹RrÕG²…ÿÙÍ)oÿÒG¢[V¾×÷L²¯ó0H¢â:ígì¼7c§°ÊÕàÿ”D4rdÈ’.ÈØ_1mÀííw“8q|6^?8¡N‰ßž]wúq2~ƒÖføñƒ%RÈ–%È B­9MÆ}Íå£y}<ö‡àwJ¯m¹Qâã§¶Áÿ2»ÑßÜÎÝ’÷^Ö뇳£ßlv^ćȴÜ"‰¦eØÖÞ²Qç¾K³â£¿&ç•”ñ‰²z&¬LþUéŸþK‚^ñ‰º;n<õdôòÈœá sÊ+ŽîšLzäX=?'çÄ_Sì½UĬ}\Ÿf8vþjÿ%^FJ⛘øÎ:îþÖ¡ã×D籪&*u¼uÕ£½Z½™‹×tï²þY ‚žüònB»êhGÙOl¨="ð[J^ÉE§ ){玙w4´æâ6c·];±È¤Áª ïMàœ1s%W¡õ;yíÔH[Óã•&œ-Á§¬|Ãߨ¹YŽðÙ¯N­œ0kO½òõt9ƒ½÷ç%zpÃSÓí‚0/Oï>¬{ü(Ñ#‰S»Ï‰–Åp΃´k-ÆÈœ9¼Ý¡{…¨†Õµ·Ã,ÅÔJ¬Õ²¾ŒéÎ9ž¶×»j:Êbär;µ‘éñ&ý^gZѱ¥¾{Cª—$ªýûê¿î1Ã+»·wÎX¿EÕsì¼÷Ý?>½î7FˆÏȺ~hËö}G_'•×yHÕ¬ÿîsǼ:éüðmù†vfxIx3-¡é¥-n&þ§«ÆûnùRĬ9UÅÊmïÑÀñ¿;š‘]xmûÜé+Ïä֞ܺ߼S› jv|cîàQy·t»·ÍvïL¿|êÝEWt/ñáα“×¾I+­ó„(QÁyŒßþ=ËlÔUÿ¿dv3R6·×lÁ­¼+Á >ÓL~z ܬ ëçLÙp¥2û'A <Ž0BóÎ,~ëêøKö5©U#(ЏÙÍÆ=¶zL&ò8¢óåe/‚·¹xúЃ;Ón©£=ÎD¯•×’kNfUŸsç~¾¾ÃËsÙ§u2…Äas·èÉçý}ÕLÍöMîZoÍ(™—wnIñÛžÔy¤4ééX+³·CwŽ6o°` !RXéÏÆô|)ªîçÓùÅÂ?„< ²Ó¨[oâ•%mñêó×õ¸ªµÐh&AOº?ª£ß¥GvÔfÝ^8鯗N±ëQfÕ DœwçÝmnxn¾¸lPCÝ‘ ¼Â‹«ûynxPk*KüÙôú4†ž:<îÍô³ñzâê¹›z]^©Jæ<Ø0ÿH ”j5ýÅÞn¿òª~ˆ@AòCg z8´¸Öt^þù…®á‡Ÿ˜ÖŠ»ÖcȺ›ßj/É}4ÃàKÔ»‡{-ëuö:ÔA{ö×OB¢1¶hË)øvo‚½î©¥çÎûUÿí] øÌªê‰*¦ÎÊËý´Ö×}ãÍê$UŸráç^·.®8wzÓeqë.‹¹íë1öâ§êý‰€q…eÜý=N&׿²eBçªo-]·Î^»¨ ®¨ƒ$'ûȼÁS…U=B•¥“ÁÂ×ñJ_œÛ<ÓÇF]ÿ‡¯üvÙ͈ääÛ™ýVpóá²A+Îú{vü™å vOt÷;*ܪ4=–¯öØI]ŽBÀ8ù oö®œwó+¿8ì̈¾Ù·Þ>pU­^ Ã0<9Mœ†yŽrïfÛZE^†D"b\vAZDÐæ¿Î¾ÉBè3ûؤÝwlh÷öóNKCh&³Vÿ5Ü¥ ÀÏÿöôMiE;/þîš~Ãý3…%“5ëµzå‚í´eÕ VQfÜ£kgž¸Ç{Hc\Û4k[R,"ká·yÝ0{C|©ÒôðËg]‹Áw¨…{gînîfú“ZVؔƗ¶î7}åÌ1–Úr—ýøø‚õA…l¤4æüøÍÑ»†ÖYèúÒMRš¸fß´V T—Uúùîá«Oíß§ýçŠQ,æþïзYŒ˜›ünŒ?fÿnÑæ›‚£Øòº;®iªˆü¯~ýÜ^&w÷^½p¢¥ á–¾¹¼mé®{xàÄœZ0¨5à»îß$ŶÖ­šëØZÀg{zrñŠ#<„þîЪƒ½/.Z§K4ÿ6ÑÌ<§NÚËÑHSBŒÕ`•å†Þ<¶aÛe¼ühë¤EjG÷úÍÝ Þ?Ь¸…:v¬w =±aÍ‘‡\Dv„ßÎù#¬«j)|TÆÜ oÓóßžþ{(°åŒg¯ß4¶«…¼ …€ð9lVINü“[—wºÃÇ$<0ñ™ÝœH^GþÞ`³ojóŠb6³Û<Åt”Ï„‘nCztjM#“Èd …Üନà½þM2éûðù-ך½y;Ú÷9yÛèvK.'•Ňü½áB—]UÇ­hÆÝB²‹õ5ê…²³ïåæ3v–åàƒÑHу1kÂR¶wÿܼÒè´RŠîÈ—ß.wªÊGçA¢ÌÐiCDM°vÛðøÚ µZ‡žCÆm:T˜Z$®…ÇgÆ$Å¢&“>ÅYW}í;÷àˆ2Ëo|ó¯Ñºƒck«Ñ›bâþ¯ýTúåE“R59Üz0®z»ñ[V©éOø_Í<¨dõÓá&Ug/::õtw_a×uó¼Ô¯v Þ?*w¶­˜ É͉D¨öߎ©NWç^î?Q`ŠAï«þÃô'ŸGÆÉу²ÚE}¶ÓìÇî›âÒd£»˜‰/‘ŽSN¿_õ†tvìi¨<|ôš`V¼aRo6iÝcÅÓG›t*?]{çvšò®>»ŠxÜkgngϤ+[ó+Ãs;îkß¡~¥©³K¿ñcN8÷™SÈ>u2`¹Ow Õ_Ú+Ší~ÂL_µêBE;[ÝkVûºó„ßÜxOpì “k{äéS_»ªom—oví¸â)þàÇý׌¡¯s©qdˆ_ºg•hŠàX:¦åö6áº}õk³ë:À«ßd#Ï |Í¥7Oðü¶—>þN4,:h ܬǭ„oQÛ¤½““SÝ# \ú¿!—稭6]|¶bPº™}÷kö”ð~ýôø@f7+EëE—ޤNœµ/‰-¼O¿¼o þ‡ßTÑoÛ©³cWg—ÞƒÝÍTë.‰•œÝ±)—Hªw­?ü¡-ÜüjÀëtæÝW“ F¶©ÜõPÕôî52è@ÈÔZéHꡱۯ[44ÅþbxÀ®áéÂqÿ ã˜l9ãÚõbÏ&¢2* íäUºýû®F`‹´gÌ™aòßE·b|6›^’q÷B`Ðý(áÅG–×;”\pzõ!Ñ¡÷5»‚íò''b'LÂ#:xÇÎù.Ë«ú/”gF\½õZPû£éî½tȾކ9öг‡ÖAï‹™©Ñgï¾ì5µÞi©ðyŒ¼dÁxTYU —¾=d9Õ–>úá? 2»™¡Ý|v½ï7ñø†Å ©ÑÏ )L~ˆÿ] Ú²fy×Y‡îlv¯ÕLȸzâ¥àܰŒÙT_×z‰.Dеamò:= ‰úø1§¸ªØô¬OsÐPµÀCùýÉÓtÄBWüLÓg¹‰íÇÍ}¿îœ¨7œÌâÓ›6î)k@Æuw²®]§v ŠÄ¢B^ê·,/Gú©32³öÎ×[]~¼o›5 áñ*qm]1¹8ä@„èÓpØ·ÜQÌrÒdzÙA÷obk ¼ÖðáD›[ó1vìñKÂ+§ÉÈ:O$6lÈçúí¹½8Ë }÷<«d²‰RÝ#ªºŽv0øåòV"yì»}ïƒåYÑ^ªýå[[Å_få·!Mß5GÌw× CG¥è—Q^^ÙßCmñ¹GÝÚÔ]&éÄñо¶óüÄŸàè?{]›ócØHLÔ“¸¬ÅöZ{ììˆ3¡‚ýÝœÄõk£êM=ø\øYŸ~ùŸ·‡§vmä”@–UÆßºRFIatdI/'è%Þ²@f7;”¨ªo·(ðñ¢À’—×/ßøçÑ›ÏÅe¥%ÅEeLÁÕK³ïo®½/"x¶zeã)ÿYP†ð†¢MVVr]Ìqf"ÕV[ú§¬265÷{œüŒ”×wN;üìmD³î²ÂSbÅ)xøŠÏlùÍÄŸRŽyõŽ%ìF¬2|ªØÞçßG³3k#6މêj²‚>;<^!óÓWõtÖV|y[Y"ˆ ³‹Ò«{sb"R8Âîrv};740M©Ï(Ä_üu*à V}I^ßî;sT wÊz,j~QgØ5Р1nßAS—’–ÌfÇ$§3L”êÿ2×ïô£AvBR2=~Âåå§ØbæíÙß°©¯ÁÙv°¥Ør+U ö¢ ï.ökJ5ÓÍÀN+äÔ˜]žöàøÑÓ·¿ŒÍ(©ó¨p@â‹ë/Ø(zQ‘˜îDs÷‡O¯ôÐóÏ{~]ôd‡ÙÝè‚­admb!ó™ÉIÉJÍ*¶×ªØ¨Ó^ݽBM‡ñâÏút¢®9ÏbòË^Däð½Æ^BI²¶z‘n]ç–gÌÒ%gÇ>ï¾í”ääq?÷ó{ i@fK¢ó°)ø~‹S’ñáÅÓ»Çwîÿ ¨>£™7üþ:ÒéÀÔ.¢Í$ñ…¨‡f]¦{á‡kfe•²«ïñË®þí·rË‘ØíŽXåx[SÜi*‚¼f­öüøÌŠŽªV–š¿°IÈrrÊâÓ•H¡ŠÎнÚç÷)êª4Ðk ¥Td¯Æ…æ Só¾à®ª¥fçéäŒÚQvƒÿŸUöÝÇ«Kœ–&º¡í`ÓàÜjZ x¶å#åé‚®nfËiµþMcq°êO€Çoò‹¢SÕTÄ_R“H£ˆ¦Ó TÄWdd+¯$W¿’Wöñä„É+ƒÃÓë-T ³¤^}¶qdì<7O°Ç„ý½ùlzNjÌãà³aïkpßÎ=]¸êë¤^g‘Ô÷…Ñv´i¨%ª ¢¥†/˜†0ó3KKñ&»hzúÇŠÑÚê5ß+ŒL5ˆÄ„äMa!z¬Q£äaS—O¸ñèø‡2¤ zƒwï %‹V–mÛ˜·¶tê;hˆ“9\êI‚ ³[²¢–Ë@—}&NìjMÐ׉sê|°ÿ¤ÎªÁn¨$³n³à»øÌôáìœ ËOˆš¦Î£|'Œtêhi ­&'#øE-”H ^ÐÚ'H0‚´Ác·‚!eâ¿*¬2¦h)9UÙ_&ƒ6tñ˜êÒüô>â×£©»N.“#z9Êw^U*¸NsÓ+ϯh±)´jxGK’‘'ŠŽw2è\qÇ]H2-áZ¨?­áÏ«šã;WÕ»Žü+]:NŠ=J5õ^8kˆ«ƒ…‰žš"Mpé`…~wšžÇ9á~±JBi;`þÜñÕáó8;vøØÏ=›ÈM}6×w¢íãvZµ6"zAÕ§Üð¡ "YŽ$ªz±œêƒôB®è†¼j稊 ¨è+PZÞØ!~ÂçÔê|äuòà-Ó'¯½,¸d:¿8öà üOPÅÞ²L^ÝaÏõ+^ZöoàþwAf·(¨¼V§%‹æ<œ±¯Co>&—sU)‚ý2EO | Çì½Ñ¨_¨8ÒŠÅn™! l‚ʼsovs.’Êçþr‰É²Ñþ‘UÂ’ÞÁyÑ«`2x oåyu/ÇÑDde„7B/n¸ÙÇc3*>8*•(6%ÐúñwkÄ<¿ëôŒU¢À6ö8ðùüÌú IüÿúÖ{”@¤PÕæº÷&Úáì‡"Fô?3öÞyëïVsžª++Ñ‹¾ó)s™§m3^á¯9u©Çï×óª>*­ný¨H(ÞLæ<G<ÄþT]~j\òWA¥ÕÕÒר®L8õB‘³xö漺˜tѳ(ö9ä#“-x¯Ém´k¼n´ò kü™TE§uü¯·y¥Ã]^ƒ½x6þäÊŸ(þOÙ͈_ü5žnn&ö‚ÃÕ^½yY()lÜÆX®")m¦Œo½æH¢سï¹Óâ®<îI¢V ñÈçmý.ŸQjãÉ8¯¨æ~ ¯z­˜²Âã⢵p‡åƒÔÜ.â¯"iÞÜóîÇÕk·–ÌXWØ\Å‘±ž:TyGpÂ,¿wìZʶáõ‡lñï]>”(Œl»NöZJ-cC&ËèªT~yæû¸ø‰Nõú-22.=xÈnæƒã"*ü¹[NASÐú¯{¼(üÎ…È_Ä÷#ä©çöo6š‘‚W¸ÃVîy2q™keôZøzh¬;™ƒ ¼'»Åžôsq„—·~'3okoª]]!Öì8ÑNöl‰ÿvó~øš ¶õŽ q²]¿/üÝWÊÐÁv5*ù¨ Ut¤œË`|¿S¤X6îí»‚Z>+§îuAsh›úûìÓe¹ß˜å«ÿêÓFü¨Ç´;«ænº&<»Epà¬!S¹!hû,Ÿ~ÞçpŸ{e•×J‹§þC Å®É|uàA†—çHEá®h×—†< ã™}iޭퟆÔî»qfþ¸ýÑÿçËvàí%·p|‡²dÌ\µÛ{'ˆ;PËÉ+¤««4êL¼D Ý»Ïúâ¼ú’siÊ3¹k‡Õlk__5ÁïN^ó•†d6q^ß›ëð8œ}KæŒìqáö¯ÄxÌ9,8‚‚Èî&+´S¨¼«µ9™ð’Å/Ý·9pñ„εOxbçüÜ‚^å7w©ÔõŒeUÃL$ñÞ¶Ó‚F×z”1ÏkFBAÖ#†¾+§îšø•Ï¥Ú;Íyue·w½)kFœu•‹ çWOÛçAƒZ›NêYŸñþoD·L[¨Q£²¡¨ÛÉm@û÷×¾`… s¦¯éùfO«Ÿ<Þäqà_ÁR+Ó‰ýkþ&´R—%"LóET,P§´|vÑÝ‹»z¿‡*ûç`E_ÇÉXì iAf7#”_Tòåü¶óÛˆúNÓ}<Ýöho¬#G!âÛItèÃ#[ÿ zQ1„ƒ¢a:wƤš=G&­šuæñÖ¯+y³›Ñ·åþ³:ê+„×êà³òÞ?¾ud÷Ö ¯Óu†ú;²b“3šâgµh#¾{gDŒ³ë~,øÌ@sUEùÌÌK[fÍÙ~ŸA j2¿ûãß§>ôØéÉÝ¼Ž—!ÜGû&ê¿}p`÷†þV(^,Œ_–óõæ‰ý·œt?òrÏ$§_–¦¦1üè±±Ý|Î3ÆMîj7úùÍð²5R*J|æÀß÷£éÙÆ½OFð?¢ßOjêî]èÐi«»òµ”³‹NÇ-—‚¦õ4#a|NYúÙ•^sŽ¿•ÀÀãì2×–SBm'Œ6[söK1‰9f`\xùò®>ª(Ÿ›yw¥¯÷•h¶––\NN9¿™:‘Í5»‰oYŒ£ÍÊ·ÿ³ª‹A°Í|{rdüðe‘\¢¶‰|fÂ/4:‰4lÒ²íç&+þ^:wgÕÌñí+êÎNc7  ¹VÀÉx6À´Ãê³§æ jG\%7ØÊäÝÿŠ>eÕ!'·­]Ý&©¬XçøhZj §äí^}õ/ǃ†Ûj>3/fÏL÷w+’u›0ļæ©´m—ÊJ¤Âî·‹ìé3‡»h)RQÁõÆi»h‹ÏÎ=x‚Zÿ)sgûŽuj£E& 8¼Pia—¼GÍÿ(üìhC6i9ß»? dv3"ÈkË#_„{^ê«kñ?ñ3ª´é±vÿù1–µÏ t¶Ü¿­2yêÆ3Oðu|»±¹ÇÍø'H£‘YtFÍS„·f7bÅÙAG‚»N‰d ¥)ÏFÛ ²ŠÊ$vQ©¨«ªÊ¨íÿŒI9rÇÆ­~Ÿí¸ýgÒ‘Y+ާó‘Ü7gG;žÅw ²4*‹^õ«œ$kÎýô¯°Ÿ”ŠÌ]{>Wð ÷WϺ_õœ¾Ëö[ÿ´Ú­,¼KlúíÕv¾yþÀH¯Ù÷£Jfø²¡–!¥Wþ0†í'îúçĈ–5XV¥ÿí€)]<Î']e­æƒ.>''k˜á_Ö¼ƒ ü/{Ýc˼ÝÉ»¼¨ßåE¨’Š"½°X4vJ»—ß­I¼Î6]4‡Lp1Yu/Abg,½äqg¦è„ª´=qê×kʵ÷y7fý˜ÎëDV†Ä`V åÀߦö#ýï^žP¿c;¡½÷»+9ƒ½W…¥süÇ“»™MF(²d6£zD˜RÿÅW7Ö½j)±û.ß¾#ÿþ‡‹ðÂ.nŸ|q{å#š/ÞûvÀ8‚Žêù÷ެÁÿð[2rŠ4 ZZùváT;N¼s¬Q=aÁo™Ýœ¬/|MŒ{xúÄé«×Ÿç‰‹0¢Nç%«×LÕÓ@U\73²ÙÒwFκ¿å¯ÅGÿNâÒéÕ#µTÍœ'LŸ7}\Õ*Ô´óy¦5ßkÜéÂAÞŒQ7q‚q¿ãAû=»·¾ì]qÔ—É­½'Å0½LPL^ «þ/[Ԅʺ-=ì8Â{ϲ9þWE]Ú0½j‹rß)K}ÔêdÃa ‡ŒòK˜l&¶ÝŠqK‹…­ÿFíŽÆSÔ_)áðêìúy§ØŠé eÆ­[RÿZrU'º{Î:¾uýs2…O®dÔiüŒÅó}†¶V“9˜.Z½Œr=ËÅb—WŒ­/aþ\ç+ùŽ^ןu >¸rÆêsÂËáðé•ãzHF=wîÛí=Àªî…T׊aˆæ*cйدá0é¢ñF¥ô‡­á˜=¯ô;Oåû$£fhn«ƒ/)÷t¥(°KuFºóÊK…¯ÿbˆŒU&z'KØ ô<ç1*ÞêrN¯«éÆ[¡í·Ì¿é†hUÅ…¢ ©{úݽÈMáÞXÑ|%ÌŸ÷ÈeT ÿ+¡ÿÝVÿkçôu÷–pG—gM¹;1h@EvÙ¶ÃÎÿÛùÞ±µSõq¬ lDÛqóÞ}3Ý:6tùPÍ>KCÞ <æïçwð‘ðW¶J‡Ñ÷ûs2·‘qó¿9ôöî¿w]}šS}ˆMpdŒ «½äÄNêá“W}mÀÌòfÕ G9“ië÷¬žÚOG^:{›J?ÈìæDRÕ6r2ÿ;Œo^Éaa3óŠË˜dšŠŽ‘iÇ.Ú?ìÿK¤™vq?òÈýRúéù󈯩¥,Œ¦¤¦oÖ¾s§¶ œ×DU-Ÿz_þéÞ“°Œ"¶¼ºžm×¾Ö:'Ï<ƒ˜žAâ–#(®¸ðiÅ/»&BÖhí²éJø&¬8üÙóÏßÒʸDEUM‹]ìÍëEA)“w>¼ó»ëS˜X.v׬¼5,m«¸0J¯„ô„ÅúÞ¬šEpmè:•EÛÌyåÑû+Ö_6ãC˜hIcËÆ_‘#ù>+ò­ºWÿ¿KFÍh쪳cWI|ñ-ŸÎWÔ0êÔµ‡¥Vƒ#ìÇÄÆü‰çø1ÒèõWF¯oüüDc§ !ér¢^<~•Ï@4Œ­z÷qT5lưÍõ—Áí~//1Dqý—Òï•Cœ/äbªîÕªåuÆm¼>nC~è½ÇI9Eɨ]§Þ]Ú …*CÎcØùƿªµ8ø;ظyÛ.æ`‹k¶ºx%¡~ø_ê§g¯>Æä•råÔõm]mô|íYy]«yÎ;ÀŠzù8,:©„MPÓ1ëÒ³§ñ÷/N¤™;>xc´Ø²ÛXpÿCÂäÏaŸ¢R³ èTIC¯½“­‰Tö1ý/Ì–Šª¡S_Ãÿ㯂M×6ýµY]÷q ^ ó·A•l»¶íÞäÏ#æ™r–ŸŠO~莓¢ŽãF°zí§ø©g¬¹œ±]Oüï×– KKñ?}QßߘŸ»²]Có£j]ŒêRw¢N/ˆ}J}›nclºýÒú¨–Î,ÿ¯"Õ§bhÕÇÐê7¯ü ³hnÄÈá‡EÍÀ®«§ÃO$2€ a—fo¸?vî´¦ºj*вT ã–—fćo›;-8Sx"Ü`ü‰ pÉF€d@fPå³"n¾y\E×ÔP»•‚œ,cåfÆÅ&–‰º4Q[¾°Õ~‘ !ÙT ËÈÊ) H)R˜ÿÕ}´Í¨›÷Î÷7„ÄH d6Ô­G~**|÷¡ïcâSòË9(‚¡TEC k×Ãv‚C⠃̠‚²}Ÿ‘øŸ¤Ëb@fÒ2Ù€t€Ì¤d6  ³é™ HÈl@:@fÒ2Ù€thÑ™Íápø|¾¤KàOA¡PP•t)Ôr3›Á`,[¶ìÝ»w’.€?E@@€•••¤KÑ –›ÙL&3<<üõë×’.€?EFFdö¯@Q”@ Hºþ -ùÀ8Ò’35µÜÌÆ0ŒÉdJºþ -¼ãsËÍl%%¥-[¶$%%Iº þNNN’.Â÷´ÜÌ&®®®’.ÐR´ÜÌ@MÙ€t€Ì¤d6  ³é™ HÈl@:@fÒ2Ù€t€Ì¤d6  ³é™ HÈl@:@fÒ2Ù€t€Ì¤d6  ³é™ HÈl@:@fÒ2Ù€t€Ì¤d6  ³é™ HÈl@:@fÒ2Ù€t€Ì¤d6  ³é™ HÈl@:@fÒ2Ù€t€Ì¤d6  ³é™ HÈl@:@fÒ2Ù€t€Ì¤d6  ³é™ HÈl@:@fÒ2Ù€t€Ì¤d6  ³é™ HÈl@:@fÒ2Ù€t€Ì¤d6  ³é™ HÈl@:@fÒ2Ù€t€Ì¤d6  ³é™ HÈl@:@fÒ2»eà³JʹrrrD‚àQZÎ%)(È¢ufÃx :#ÑhT¢ @¢ ³[„ò—S”†Ä¾ûÚIiþ…ÁšãÓÇ®W¬1¯0æÔá½;ÿ>d:;äêWmøÓ@f·q·žjY»é©‰ÚÕÉ·c‘öãGÕ ìׇ¦Ž™{4•‹!(Õ–FªÛþð€Ìn Xî¤vqRµ³"Ê®ní+ÄŠfyž.êp>.5|Π9·c$WN’™Ý|½öiû× Šð/))·¼ý$Ǫ¶´‚ÏÁ;Ó7a\®„J@ò ³%†¾W·Ï_eL>Šòé\ä·Öå)œæqè§7¦4`iì…ÕJdBå‡ÄÇ$X\’™-1½®«W¬.Ç‚gA[nÍ^¿ÔebïÕý7J{nšdC1v’%™ëæódw××ú°ž­ðàn5"ßp.äÐX ¾°tÓžWQlŒ@BùL¥Ëð…Ë ïÖ¶^ç~ ¥°œÄÏI\½Îfª¢»©^qô¬M4ð;ìÒ¼´Ìì‚Âb:‹ƒ!(™*§¦¡ch¬-#Ù"ƒ?d¶Ä ­lgúÙâ7^äÜÝò¾ÿ¦%s…θ[ÞïJr^ä7×FÂåûó$½ŠèÞw¥â^øÍbGgá]^Ìã›· zÜ{8ÃB[C–ÈÉK]6Î{¼ë•û®í™íJ–`¡ÁïÂc]Ú# Íõäõ]…÷Sç:¹´?µad[á<\çëx Õ¶w²5S ”G½zÅlÕ}€ÇÞëÍT Öšd¶Ä•<û’!Óq‘¨—8Ÿ™ÿ¡¤çT% êO”÷æY™ãÊ{±÷ÃPÙñõE÷ø<ÌvÆœ~ö–¢»šÚúöÿ#?äÄ¡C‡ç{º˜«Âv$õøœ’„pú˜é÷óž½æ5ÝÅòã>súÝ+¼Ü§rö’ÝÝtœØ8N»ÝmÐl`_#qå19©¶³íEwh~lšêbs-É–éOÄ {«»©mE*熼bËuéa.ºG´¹p©IÍÙå]59ß‹è檊uפ ‡žž¢¹Ö°¢º\z%W[ÇÈPNtWA§í¨áŽ5fWœ¿Ê›]ï‚þÍÞè¡Ehöâ‚?d¶Äð£Ï¸úìa0™q1å¼}öyŠñÊ¢˜… :®##´®¾w¶ÏT C%¾ •?\ÐÉ臭 ‰ÀÍNÂrVz:n—AQ‘‰–SGu°2ð=qg¶]k—>­ë,ÉG;j”]¤†!hÅçG-|Ûªc'ceÑݯÁ¯5 ¦ÙUÜ#Ø|¦¯¼|­e[Y¨"HAyƒ×µ›±ÌàÏ™-9JF®N]3R¿D†çëß]“ˆ`JÉçãtͺwïN#` V­É°/hbDMëŽ,¢%ëþÁU#—î. (È/¾÷‘Ú~Hg•V•;ñºÒ"³ð ”d›µÄà7BÑÄ=V&ó¿TÞEtjUÁ6Ð íw'|žg,£T÷kÀüZ€ 2N¶Ú°Í¾nCÐqY¿Ý%ùñú3ïÚÛ·[´ã3ëÄ[I‡öûÁÓ'%ûý«= ë8hN –ðá–œ³7µâ^ÉÍ»¹­½¬%Z¢?SÊÕ0¦šOÅ…lXqqiY sÚ7¼uä¿_{3 !wZù×D—f4ãñÿ‹_ÏDÚ7v„«àóLÈ/ã“ÌðfYs^,úâÆÁswñP:ƒØer`Ê6}Y8šd¶„ņDuînX±Ýó>ÞN$÷¶²iàSÁkø‚«ªðù<8ƒö›•D¾/ç9õ¨ØCd¤ð-]õš›uiDzXù a¡}[5W A“¾hl?ÓÆp©Pô543§Så/ÖeG~)³XѪ:ˆ‰¼âÇ'„mðt®5¬zþ)þßÄ%p`4Èlð§£&EÄ+O6­h>…žÎÖÒÐjUÝšf—æÅ|ÌÆ÷å˦=®¿¸³¼ d¶T£§$gæ´òéTÑA¡$íU†vZ|¦R©¼·þ;ƒ7 ´ÔÔP¥¢¼â¼Ô£ó–¼D¬¼÷ºÈ‹_/M2üéd5;œ}ýÐR½âÀ'­Ó†×·iæ5:PT[ï~ø¼€%æ(ÆÃTÛÖ¤KrlD"Ƕ»FåÝ3ÄvSäj^ƒPÑÄ{ÂðS3{o¦ó $"ÂçòPuË»®¾š8È*v¥4 Èlð§#ɪÛw©>NÕ²éRû'ZP2­Ks 4—¶£6 /|P¡ëº´ò:sÈúm¿è·½YK€XÙ€t€Ì¤d6  ³é™ HÈì&ÇápRRRðÉd2Šþ)?ÜÈãñ0 “••ÕÕÕ%à:Qð@f7¹> 6ŒÉdR©Ô?'½ðÌær¹&&&·oßÖÔÔ”tqÀŸÿB^»v-66VQQ¯=Kº8@¯ÖËËË»ººZYYIº8Ò2»É%&&æää`B’.KóQHII)((€ÌÇ`0ÆÏápþ¨Í°åÃwø'2|øð«W¯Jº,Ò2»ÉÑh4>Ÿ/éR47Ñž‘D"Q(-ô§…?~üXXXˆ—ðÏ9øÑlð/<žŽø7ßÎή…4jñ/$—Ëýñ| y០¾âßIDj@f79™êÒtpÐèÖM›Ëý/G8™L¸p!>%¥Lx›Œ‡¢¤K$ž³³3¾'‰N'ƒæ„¿·ÊÊÊOžw–k®×€„`¥yŒüR®àbn(AA]¶•±8»4·”OÓPÔQ¬¸D9«˜™‘Ï&Ézú2EÉ¥\‚¦.MQ¦¢–Ì)g¤f°QE9CU$#ÎÆ ª­E%V<ÌJHfa(B§kVí±¢Lz>ƒ‡Iúz4 ñ…,L)Éc´Œååø¼ŒtÁSÈ©Ék*‰ €å¦”—ò‘V:ò Âê7«Œ•WÀfq0 AÉ2DUuYy*’Ÿ\ZÄEêü-ÆÇdµuä+¦–0²‹8 ©-'O­˜XœZ’ÃD5 䕨u.ØŽå$•sˆº­åip)÷&™ÝRñ¹AËCæÎî<®=!*¿³+5pW„ãDùü`=9%|»!Wr(*'oš,Á$";#q˜ýÇ<='ãù]•°¼¢ëg§ôM¹°¸Ïý¿‰Ÿªˆ ‚HæîÍ™¹¥þLy …qqÂðV$QT8±ÿ´öÎ:äŠëyâ;0CªÞšsö×Êè°äŠCPÚ6Æÿô¢|Ù{EŠj/ÇÄG¶ŠáqæÝžw²ÀzjïOº+­/”Pö„Œ˜ëª \ˆ”pïe›‘ ÈÐ~¥Ûø®ãD¾ƒIø‹>­åðŠ©èÊ=SÏÁŒCûa7Œ+.šË.™ÖëÚ¥h¼®x)Ö}”ùw¶6<¹[;^ÜšO ˆŸèQœâÚñþ–žN¯ÎX+¡øÚóì// ý0ÁÇŠ˜ýñÛœ™a×_W—#ËØ»Y^:ÓvÑ…âÖÞö°WÔ4šà½{æ­¿.áe^{Í}»ŠðqþA§ +ÒЭo<—Ԫã¯lŽÅÅKl™›tï!²pL®©@f·TlöÇd|k‘_±ÛîÎÔ0ŸMmXBä)%Üïÿ‚ ÿ’÷ƒ‡| ¿~Ÿ·ñyùü%Óãû÷ ùwûÿ>«„Ç¿• ÊËS/}JÜÒßgÄ}ˆûÂwP/{S}Émb¿)VóíhMþ2[:BS ˜ðì€h VûLh;âV>ZóêåøÇE¬œ¿jsãÖX–(œ§Î EK!‰?¼|wý'­_HT8­|!5–TãÕÕ™«<#¶Ýט²I•òìéË£>”ROÝsÚâ—¿~9âíõÙ}\7ÄÑýTNð³°­×[o&—“¸ïd¢j~-ÀAÊdð¤.!Sªw¥$x»h4J@äeñ EʾŽg¶¶Æ“˜yì`¾h>šlõžå½Ï Oá jªrù¯Z¢ùý÷–Š?i>™L@P"*zЍ+‘7˜Lè„o³¨¬áÉ‚R°{ý{ù Áë¸û1*„ò’C®n¹òaHG½·+ËQ";1æÂúPtÊÍQk­ˆtŸ¦-#:…ÆÈ-~R„I5¿àøýìUî*¢âR52E̯¢áÏ+( ÙM 2»¥"“[kâ ÞÜUÓß+g1’S8Þ†NF0nI®p_Ëýò*S†ÀãbQ–W*˜†â{,cë>"£à§Cö"t:¸wWû|#ÿÉ™´Uz‚‰Åɹkße<þÈö逼8šHèÑ®]Lä›Ï/\ºæÆ÷ßÚ;7Æã> x3sqdœè䄺úú€‹-2µÛ¿(’S¹>lYm̬~´ë0ÙCQny±m»½*¦šg2ûh‰:гl5n|,'í §0+pò{Á4¿w“wtnz\κ‘'ÖÞÀ[u¤­Ç-é-j]¹©‘Ï ñ[.Ý™Ï-IßíI{â•¥wQÙ0®p°àI È·÷¨ù;DW34l¨ƒŠ¿’Øg [ׇ=..A°Ðæ¯UÖÕÉw“ÎÁ¿2Sw$N÷3~¹Š w¹r'Chj"†uS/î¯|åqý';;§óŸúm•rf‹'OSlg!C$»Ž·=TDÃŒUÙˆÚ‰m­ïùr|χùƒíM{‡PWÝs6mL¼zfmfñíâûbwk $7ët ƒ¦««•žUc®×O¿•#ý÷¹ø_ Ü•´DÛ¨ÑÝG0‚šhj%fO]þÕýaG…êGðí—·Áµ‘½•hT„¦±ùñHçGt“Žj²Ú”ÖÂ¥5„5sUE#QÍL´¥ÒßÅÜËAºŽ º¶èH‡m:A¾€Ìn©ˆä©«ºÄ|û÷ðÕ/ø½®y–´nwe |-›år­î"É*LÅoèÉkÊÔl‘mtðº3ÂÈ*ä"ú‚ lbŸYkG~}ý$Çǘyø2æ`+™õ53š{dþ“ÇÊDÑæËåG.ê4¹§r“½ÚˆXü)|ô¬HBÛ=öùÏd¿^Ž]íqßâÓ¨×Ém§¥ÎÞ’<蘂pÎù>}€ú/wñmKÉxIÄߣb¡ºŠƒ**Sð JD©ŠdQÏÁÀ-é;®ã;GNyÉË…‡aå(25š'¡›Ã ¢,>Ãû73-‡k¯ *I…„•ÉÕOJ âáÏW¢-#ìUÐ7Ÿ/yÚß·Cßö2R÷žŠ}?i~à€]>ª2x‰…™}|w–ßn-ÁYɄ„᪔)dA{•œŽ¡- t°Dy¢$Áx|9+¥ÿZMNCc|kÒšW™žîŒôDòÙÛÆ³fÛS—2Øñ´OÒ¨cß|Feþó±ž`·ÌŽÜ¨uâïœÆço[ŽdžöÖL{šœÇ·YeBßP•Ù‚d½<A´×zª}zß 9ðuîÇú’ûöâµ&]ËMÃx“w¾õ 2»î]¹í ~±N~ðXù‹ÇsÇ»ÞîÙEÕ¶“V÷žzƒ‡j ûŸbÕs þÅj?ç–4‚¨,¦ª ïRV\c<¡-yÙ-—¬¡ö¡§^'¤Žö2!¿ôͽ¯£Ÿ±ß§9öí˜ü¾g}í‰\Q;{Ù‚çϾpŸ,ߪŠËy,nÍÍ+¢ C$YJÅÁ8.O¾›µ§lÌ͹±'ããQÅ öê/j=9?òyZdõ]RÛ1VMþ‚[BÙ×T¼Y:ga;7vx¶¾ùÆn„•ïi:dn›)½þ:}rãñgëüZ¯d|]|«P^[cÙBSRcö°T=BÚÕ¯x­ˆXáûȇeÔέ¸oË«æ@~æÒÃt²µÞ*#ÆŠ›E·/f»Ïo…þb:¢Ü²’}«ñÀF&O81LtØÓڳãŽ~ »ßNõì‹âT”LËË2‚?}Û¥c†ò¿½ûÍVtnUò²ÖsR†øZvC9µ×ÿßÊlDvõÓá<·ÖÿSˆßñì'§v,ÌmrÁ(Œ¡Ûû ºqõÖõ²ªü²¥–4Â×V…ê衇̉¼U`Íx—Q„¨/E[½¡êQ"ëË—£ß…qTTÉQOõPîýMQô¾hkj ö\Ôc^§§ïÝsïµÇBuõ·ß§þlößÉ7roœ‹Å')šÞ éå¢ó?Ɍڊ»ëwS%ðÌÛ“SþQ0ÞEHdv Åçp"_g~c+ï­ªÓZÿPD·ÐÝÕ—^Š[ÚûY§o–;¹Ë<Þñ±Ò³dçÖÈl$ó|Ì+ ±k£?{wÕÍwOCògª«‹W«¼ìNp¼—‡Prf>_tЄ@ ''ÝMGƒ‡ÉUžGGmØt]–ð<²äc<]p„¤àÓƒ·âyÖã0–™=ò40…Òߪͧϵ3_1úß?]BR[wgâºä/­¢»õ'ݾ”æcûÈŽ>ØåR”UÇÏ4¸·>EÛÑa¬%¹êôUJ‚N$‚ÿj$§ƒ©.òtëÉ|—G™„!½z+òWTÏÏ»ý×g‚:ªÑ¯\Œçå± rh^dô£ë¡?QpYÃc´†íÏößðMµº;8•U˜¸uèÄ­ìôȬçÏÒ¯^ˆ ~–ÜuD$ãµ­Lƒ[4ÿù¦È±RæÞ Ž'–•’uHräÑO]–ØüD‘@S€Ìn¡0:cë‚ûg?(^ˆ,CÁ·~^qž¨¾2!N¸{àa|ÚæcAŒÿ§¸"Ðü؈¯[ÇÝåïèºhx+~vþ¾Ï®¾g šf«¦ªTÉæ³ø6:¿¸€7»Æ·1«ÛÇc”²k=þ´Ž%<%ëv§¶ŽXôuç>„(“õôú§.c;]o¦€ðõ»›¯üeƵ¸€$Dix×c^  M#Þ"Á~ŒžÊý¬ðËñÈP•­ÿð»Mk¥ù!¿zŸ1/çàÿ[6UhO˜ÑêÝþG™_²9®ú?8[œ›5}xÍ3¤‚Qöxl03 Š„ÜQ½F£MÐ ÑYyžÄÌ*ã Í&öœmˆÜzý,{š)ãÐ{Ä3Pú±Î *bI­ø®•—ɼíë"ÿãW,EJc36úL3ìxvµ,M]k× [ù´+ûþMšŒÙác²`ª SùyáÿRÍÕ$ñ2…3£¢»"úõNþŸ® Ä-­Y·£ºEkÒÓ-Ï¿å s6k’X9•Ÿ7Š',¿-¨|?ÙûöÉÞÊ¢ÊCîgž¢ñy„è¶Ëyø©«×>wà²ñ¯,B$p×íN.³5ßí­£ÛNË£ž‡—^ÕëOBÓã»v e63wý-A-òó¨ñ7¢*§ò&,9`\QîZý]jÕÞ¾óøÿAf·PD%ù©ãÌnG}ó°8‹ß=t1ÿW©¿ã"Kd¢°±TëØwõ®‘{·3‹èÓ÷füí}ëooá4J44 ïÑ–XãÊ*_ÍH·[[äB4Ò{&¾2«V"¼Ážg{z^­)Þ/öè«Ø$/¶eB ¤ {Ѻ\]soþ¢ŒâK^´n¯²i´*¾§ždïíQ*IýÜvÓºí¬Î=ÁýŠ–#µ7nü~{\Ú³ÀŒq±«±bDLÕÌÌ”¢ÇðŽb?Wj)=]qÿ†ìOÊ\½T¾_ZUmýs5¹\¼Ü”äX+·Ïˆð£$)ÈâáÍNgb‚"ˆJ†…“Ž'9B’£ì¸<Ù.VÞŠ‘×?ä~æÅ§•Ú«?©û üÔ¯EçÜ1¬´„WöŸ;ì"‡Ò¯ÜIKç—ŽêÕÇã&Çç¼+EÉêÖZQ«ºò pÍ…È£ýÍfúvvÜ}–Ë8;ê‡Óo'ÛÍjÛ<Y™ÕÆZ)=Î-T×󴦱‹ªÇ¢Å}ã!­ûY…Þë®&ø"‘èWnÊJºü8mƒ·†üOì¡ñ[ëðõv·{FâÕ-¢àû‚’8ç÷Å$ …E=V{hʳé7v¼~‚× Ì>l–ñ(-"‡­d ûŸ ¡O“ƒ®ä¿dv‹Eèæçj£wå~ú¹«yvƒu;vÔõo È¡ÕV·HA¯j“#S{XêwávÓn(aì¶A]F$?ÌIËç"T²YGíÑ#ôÕH¢ùQ»‰í|‹T´å D¹‰+ì5Þæ:âb¯iVTQK–`7©Ý¬L¥v ŸÉ¡ÿa´SŸ'ÝŠEFM4µuTõ1íºÒ,’âôåæÇafcˆ¢¢&>œL+jõ;E¥eñMª0?!iˆFIå”g1yYN¹bH“'7k¹ææ¹k³¹D…þC•ßî©Z!îCB¸ a‰‡Á±ª©·×}+örTúî©DUƦ½†–h¼™r:Yt&ƒÇ—omhGþôúݧIí½DÀˆì°øÛ…¢%ß^O6T°oÅXˆâ”ùJA{—?ÎW27èdC{R÷”Ÿ{ÛV_}«}ݽÿ‚¹ÉùMÙÓ6Ƹ»>ÁïÚ´þ¢`¨îwº·«LEe—˰H)½fÚqÕv¿áüu(iÛä»ÛSP;ý«†ê‘órøeø¢åøü¯©­¦çë:¨™ª‘xü2¡3ðée;.fàËØ»µS¼·‚1x´‘öCѤ›âžl³¬'~Í*ÅÿápøÆÃ*ŸB0¥U7ûå}ã×=`òJ9lieqçFÉìÕ1§üîŸò-J¶éÝfK ¹rÂÔÚð¯eåªùÜk7âr˜ˆÕèÎVgÄPÄÙÒW=< ,ó^x)§LðD†\ßP£<ž{¥Ä”ªÜûíÝ\M_ðûV¯½—’TFeE¶dË•¨YF(dFˆÄ¿ì=J²2BFFöÈÞ[(JI¥ýÞûŸ×KÒ’¼Æå÷ýÿ?©ûî=÷ÜûÞ»¿sî4 È_U¿çŽ.µJw¾”2»J3ìX{V;͇÷n¹-7o |¯Kjø¢¶Ã…/~ÿ’IHÙO0Ëù-¯ý/ÐkQÓµEÍ|%ý¸\¸ýôÖísGfvµkÜÕNT§ÿü–ýs†ëº‹F(ìŸÚÍÅþveü¢”s E¿Ú,ƒÿþ¡°gª-Ÿ·õש@Àÿ±VêúJúuØOÅŽ·½¶e¹¡Zj⢉×_Åóe t, 8gE= žÖ Æ¦£Ž¢dÚ˜YHSWì'áŸ_ûl÷†… ZÒˆ™Æ£ø™™KB6_»ëó°ù£’+,ÈλDûLj| e5‡áêWÖÇNêp eu™·—^L˜I"»i£æêœ+¢ñ2ùµû›êzž?JQ¦ÝêéÑ/$ñÓ)fváþ" ³Ñæçû›¦~ˆj70vçùfºl NÞåO=]Û¼thÁ–•þùÜf¯éíÛŽLûš’ͧ˜l–œ"WEA8•’qs/grÉøYÜN-^¿0å*H*søüÚº×^ Ζ•¡Ì…ûûÎÌ(iËŸ )Yiç;ëO© 5MN1›h†û­Á#2˜š5X2ü¼YälXnÁíã³,¦šyy†½nW?!1Kt^*›ËVR‘”ÏwiøˆÖVß(Ýï‡Ç,Û…]º¹óå´¾/¦ðÜwßû6SR(e-iö%ëþ™T¾ƒõÂ~¶‚º4Åbú?¼ˆÿÓ­Õø|†FM¤Œ8amVq$¤eÖµfý´/¼ˆÑ~=¤È—ŠüýŸÊæâðL§vìtxïÈ={…ÞC1”êVsî­õ}pÉûùHžŠêz¯æ£Þ ºf”s¹;‹¥¨^mí¹ÕHŸ—Ä;„<Ÿ¡ eÓ‰yç¿§G-a_˜’M¶ÞL*î…÷)¥Q{¡•bµÜM(¿—•Þök¯¶Î~9ï@-V‘û3ùáX?ÕDX7R¬p“5jyûOm¿ùyBýr^æJsjõ2 ÝTEeG%[s_¹FµVÆÔÛT7g]Ò äçì Îé„S9¿'¶g­ùyÆ:¯½ô¨g’ÓC^UF^BNG'M¯š¬ÚÏû6d”¤ô•¤Ššˆ¡¨&­¨–pͱ$ØÕõrÿdIÔ4È=“Åá舆SšÕå¨Bäµåå)ý|Ȉ§Én_ÂçñkÖÝç_«vî‡!o¹¤¥ôª¤´‚$ù¿¸VÐ’ÿizCYCNY£àhÒšrµsûyü|Ôk*¨7dv•Ç`((q‘£NÀQÒ¼6àØ±˜ž_WÐG«M§ ´ò®š•›¶´E”´œ®Bþ£‚ê}Ì_4Õ;z&î]|ŸÁT­©Ü±{u- áæµ©KóEýØæ9wbéïÕAÐ.{PGáéÇ]›J~¦ÚÕfe|a^Ü\½Y §2-mMVÉ«ó¹¬tŠ-SÔÇ º¹îâ\†¬’b^¯HU{¹¯¹@EVGtãhe¿óýFåArJ¦€-Í5lZ­SÅœk<š/L–'KÁ”–q^ڮі]c²Dœ Íõ(&KRÚÉÛ¬CF_ (Ž¢Êßy倒×:v¼§’—ŽGiÕ <^§¨—øÿØEð2›Ø•B ¥¦Øß^ÕX)>ûµÁX{MÑÙ7TîO©!Ó5U6·ºj¿aªù†än^ Íø>È ™{3Q9L3›z¢cT5=é‹“ÖSí¢Fñ³v{‡o9•$(°×šÉêíÒÊy¢I¾Û‘ (Õ “ T€eÜN׸]þ)EÄhboÒäû ónuÍ»‰^b[Œj`‘;˜ÛkœiQKJý½ÉÁ¨ §M!›¡ d6@ɲkukáÉcVò&œÉ±vï`í^d-²ÑªøG ³~ÅaKW‰ó^ÿîó¿à×Ùô€Ì d6= ³è™ @Èì Åå²rž·ô;Oë¡(Èì 5wîMOÏÈÊ®E¹ËÊ¢×廑o ŒÍæ ªü›Ÿ””ùæM2³²/¹ÿ—‘•óí×ãA!Èìr÷õëWƒ!ÈÙ’ñx¯ÊoÒÄ'===--­²kñ qqé£GŸÍÌü+oÃYÑ8æq•]‹¢1¾?½bóæ§äÿÊ­ äÇ` ýTZÈìr'---¨ú]q5SØl6—[ÕᙜœµaÃãÊ®Å_(;;›Ï¯*{/ȧ155µ²k‰6¤q_Ù¡ dv¹ÓÕÕmÞ¼9Ù~IHH0™q$›l›Þ¾}kddD¾ òòòä[Áãñ233edd*f3*Ú^W«V̽fUùø%''Wv-r‘l””D6T5¢þŒ”TQOKƒ¢ ³Ë±±ñåË—Id’À.ï]@ä Àb±ÝÜܬ­­É|#""¦O>wâÕ«WÎÎÎþþþ¤2å]QË€™£\gTf5kÖŒŠŠâp8U¶†ô%ê9©ªªêééUv]r‘ÀÞ¼ys\\yDZ'¶ê {ò$½šÊ®m ³+BEF×È‘#ëÕ«G›Êéè|þüY4\__åÊ•;wž={¶ƒƒCyW£êáëׯ+» PqØl¶­­me×àO!³ÿ¤Å:~üx ÒÉ.r„:uê¼|ùÒÅÅåìÙ³“'O65-î‹PùvìØaee¥¤¤ôñãGÒ;LKK#-0Ê®TŽ 6tìØÑÀÀ !!!55µzõêTNÓ³êìÌ€ €ÌþƒáêêJbxìØ±%ìïï÷îÝyóæÕ¨Qã¿ÿþ«˜Âo±±±166–——çñx7n1b„––Él‡=z7º²+-(((22rÈ!ä÷°°°÷ïß‹šæûöí#_瀀€Ê® Tdö߀¶è õ/[ÄÄÄ$$$Ä××·mÛ¶ÁÁÁššš8ÂWEæ×¸qãôõõ=<<(áÅ<Ò£ ¬¬îìì0>Ÿ_ø#Ád2ɇáÎ;äÃpãÆ¹sç*++W`e¡‚ ³iŒ|-I‹ûO;¦¦æÝ»wIS}èСöööýúõûó2¡ô¼½½×®][`8éd“ tqΙ˜˜<~ü˜´Ûlll¦L™Òºuëò¯)Tǯ[·îèÑ£ŠŠŠ^JII)áò²A8tèÐèÑ£EG¾Ê¹šPÑÙtµ~ýz²•_¾|¹Ëtrr2dȸqãöîÝXõ¯±þ;̘1ãÍ›7AAAe˜–tÍÝÝÝ?þLÞ¸fÍš-\¸PìÕƒŠ÷èÑ£5kÖ¯a‘g’–\ÉßÍÞ½{wêÔiÉ’%æææ¤›ŽÓÿ&ÈlZZ½zõ±cÇH\ì%KKKoÛ¶mË–-]ºtñðð Mu$w¹"o凊 ì’ûÙyTUUOœ81{öìþýû“Ÿ¸òžÖnß¾=jԨⲖô³eeeK.„|‘===---;vìèêêÚ·oߪç( d6ý¬X±âÝ»wåØy†N¾êk׮ݸq#éÐÿre³jÕª»wï’’XJ[°`Áƒüýý9)Y,eB»páy:¤¥¥UÜ8¢!—¦4Òæ>sæ ù"wëÖÍ××¹¿2›fÈ—ùêÕ«p ]]Ý%K–DDDž7ùΗ÷ÿ5$VI_*<<¼„qDç³X¬R–illLÚXd‹üøqqÔ*HLLÌÌ™3Éw¼äÓÇRSSKe&ipO›6ÍÞÞÞÊÊjäÈ‘NNNâ¨)Td6YPPЉ'*l5i§“>ý¨Q£ÂÂÂ\\\jÕªU1óýë‘V×Å‹Kì22dHÇŽÇG2{̘1å1¯û÷ï“Lݺuë/Ï÷ÎÊÊÊ}vi¨««_»vmòäÉä³1iÒ¤fÍšýAM¡2!³icçÎ7oÞ¼oß¾›7o–~’"o Qz¤~èÐ!Òà#-ÒÛ=dªŽÇ9’ôzkÖ¬YÊI Ü»´ \]]I{nþüùd¦S§NURRú“Ò "!³«:Ò» Û¿¿””Te×EˆôÛÚ´i³}ûöÆ9rD[[»²kDä}\¶lÙÑ£GKŸÁ€*ê>h¿køðá½{÷&s·°° ¨"Ÿ%ˆŽŽ&=ìÝ»wÿÖ#Vë´âö¶mÛÂÃÃÛ·o¿zõj܇.ÙUÚÕ«W=<·oßNƒsˆ«d(Èì*êÞ½{$°ƒƒƒ«rË—Íf»¹¹%$$o;éGâÆ™EºqãÆ¨Q£®^½ZENÜSTT$Qñöí[kkë1cÆ888Tvþ9$tÉjß¶m[Ù.žo3NÄÞÞ¾oß¾ÎÎÎ$¼Io§+VYÈìªèæÍ›dczäÈ‘ªØy”••CCCçÌ™ceeEºneè7üÅH§v„ ·oß.ÛÑ2_ëU2ƒQ³fÍ+W®ä8{ö¬‡‡G:uÄ; (Îׯ_‡ 2}úô2ßí@¼ýì<¤Ã½cÇŽýû÷[ZZΚ5‹üÄ}‹« dv•C¶¡>>>$555+».¿ÁÓÓóáÇþþþ›6m*ü|ªÓýû÷½¼¼HÛëOG(§«qH±¤SE’›´·êÖ­;þüò˜ äÛ¿ÿ¹sç’D,s!¥¿wiˆžÌ»jÕ*??¿uëÖ•òò3¨0ÈìªåÇdúË›VMFFFäKLZÇŽkܸqeר2½xñ¢sçÎ÷îÝSSS+s!åÔÏÎÏ<ÇÒ¥KIW;<<üwχ‚ÒËÈÈptt$YبQ£?)'55U\U*ùþ.\¸ðÑ£Gä¼bÅŠ.]º”ëìà· ³«[·n;vçÎt ì<ƒ¶°° bll±T\÷.-ƒ©S§FGGOŸ>}ãÆû÷ïÇ“$Ê 33³oß¾...b l>Ÿ_YÏhݺu“&M8ЦM²ÉÒÕÕ­”jüãÙ•éË—/¶¶¶~~~q`‹¨©©uèС²kQŽbbbÚ¶mK6dFFFâ-Y\÷.---­;vµhÑbÍš55®òÒãñxîîî c`S•úy rŽž 2„|ÈB9;;;::Vúƒ‹þ5ÈìJóâÅ ooo“Ê® ü‘ØØX'''ئ¦¦â-¹‚gÉÆÆ¦eË–äƒJzä§ŠŠJåÖ‡H`“渙™Ùˆ#Ä[rFFF¥?}ÀÀÀàÁƒþþþÖÖÖK–,144¬ÜúüSÙ•CtáG@@€|B¥àóù[¶l{`‹Tzfººº«V­zóæ Žb– ì¡C‡’v9]1QŽS:L˜0¡wïÞêêê•]— 2»$$$ØÙÙÍž=»»*lñÿb¤‡Ý«W/???Ò£*§YT‘wÉdêëëWv-èÁÓÓÓÈȨœ;==ýÏÄ).8¤]ñÙ-&&†|™½¼¼*àAX²²²ZZZå=—VffæðáÃ}||ÊïÙÃâzê"T˜Áƒ7nÜØÝݽœÊ¯ÜãÙPéÙ*##ƒlåW­ZUNûQ 0ËQ3ú7µhÑbáÂ…åØTν8*ýà%”i`¹¹¹‘¶ø¸qãÊo.¤¥ˆÄ¿ ™]¡^¾|iggW1 åçË—/666³fÍï)Á…ñs”ë,@\-ZD~–k`S9Ë˵|¨âÙª~ŽÊ®ü©aÆMž<¹žÀf³ñlSZ1b„ŽŽŽOyÏHRR²«A•…Ìø=:t°··¯˜‡ikk9²fâ¿ÿþÓÒÒš7o^ÌKUUÕÍÍ­fU2à7¼ÿžô¨ììì*»"P…ÔªU«¼w‰ˆ ³~CõêÕØP@=*» ð¯@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6@ÑÞŸYw]¢GßÖ:ŒÊ® €2*]Æ‹n<ŠÎfs4êwêÜHõ‹{{ëðÉ+o2X\J¢Ú€AÝ4e9e+çãQÏm²¦ýZëüa} ² ( í.øK ³¡4’¶L¾ìèc)Y) ¦ ;-5G†2L¶¤œNm#³ÖÙö×—ýý‚! íþ{mïÚûÍÖE‡ ‚³Ãzüa]å5ê˜5ÏvkÝÿ$ϼ¡¥E2;æzðR¿í¡'âc™CŸÖ32iÚªëÇžMnŽ´µ»óUZ^^š! Y)±Ÿ©Á›{YÔM˜÷pü°!ß3$Ó’¿f±júï[°iøˆÈÏ‘ï¼'|‘vá¥I:mÕlÄs®šŠ—!।¤dðXýܶxÚ7aý<"/öp·V.ï¥Õ•e%(AvrrJ%3jÑŽIV†Ì2­G€?̆Ò¾lßðÅñ^ƒôg…$I5›±y¦QVZfêרÇ7Îî;¼óðε3F3Ú¸l öq¨ö;‰øòäì9Áõ¶¿]>¬†À"¥æ"1l•´ •´åu¤)*óûÅ}Ýîhæ°íUsëÉ}_½,?´_í—k|½ÂâzuÚ ¥Ó4à“¨¯ífÅP”rÍï®—Î71[Íhýñ;q3ª·Û±04|ZC2°ý¹ÛIOOôjÑõ|"5ìPÜ^?íHPmcáŽý˳˛tÚüüz=aàm¼ö8íQ°Yg»²©Ëwr2U§ŠòéÌŒ…¡wÉ/Mgßyå©YôBñ÷Í]y+š¢®OÞ9ç®kƒß^+95uê¹GÔ^ÃzCž¥ÊMÚpØ×©•°ÑîÄä¯/û6\}.ÞÍ]f&?–=óù¾ö¦¯PÚýFŒ>ÜXŠ÷åvxðšU 7o¾ÛöÃ~ì”Û—ïÔ2×R[:§—,ˆ¹|pÙŽ°L9µ1s–Ëe“ü̇Áçî½ãµbv̼M&¼µþЧ§y¡ ¼å‘§ocŒ×赆’7Ùvø˜}»E.6K½×Éç½ÞYëØSÊíBÏîa¢Q¦ Èl(5¶Šš2—ü+W¯Í ³ïCÝÿ£2®¯œ6Åó‚¿c½Ë—Î_eªÆ-e‘Q+RL·ºÂ b´ðz¶§¨Í¯¸dĹÒv`OUVqcdžmë°íYŸEBf´¾é¾ï•L—)sÌ'޳6é˜7R¶kfœ¸øBbØô ûLj­üs!o_¢g·zlN`‹H’FDÎ*QÐ,zÏ¿’–¶W®ºŠdÞ)eu.‡£ Äÿú%jÚ²3NÖEM÷fžÓJRŽ‘žÌ‘ÓT*n±Î/<™a5cXÄâm÷æ-zæT§¸1…¡¦.ÍdR ަ¦êŽ¿Œ¦’´Dóqƒ’×m{tp±ãú>]åŸJ­¦.—#«£‘o?LÒ3§ïdl{ðÌþ{µÏ ‡ÙGZøåìÉæ¥s›Íœ7ÙôûlÞ(Fû† dªõ3¼ý÷nuF­Ó¦‹2TAY×íI60o¼ùüÔ†€éæÒ…Æ!Ô«©ÿáÈÖÐV+yÁµµsÖ.WQW[¹ä1Ê2JŸ»Ÿ’—ùópn‹ÁîgÌtMômGnêíÑéí†A¥,ñã‡%!Åm˜\Érü@¾¿<Üaïù¾ÅgvZÔ¶s7I}Z lMþÊÌxYÂF„t­FÊù÷M÷žìÝjW×óoãÏ÷´=¼R]êÇK×½'¯x`zn\þ|O³êü¢ç,àós~ä{™ÏÏJcvœ1þ¬›ÿ—]S·ýgíP(î?n¶9ºV?{íCÛÏ üâÊ~züØ ûcîŽc·‰Úí}xÓæ^…÷%—Î÷@þª’Yó²²äZNßÚì[‡½‡&v\Ü2~F“+ŒÏîÌþ>©PìóÈ’©Z3ý¼ƒ™¥dÔ¤©C <-©c¬õãH´ #“—3X™NZ ¹ã3k˜kK|-”Ù¼À›•om¹ÇÐéùkûžÅ3U/bŸKî:#ïsq+/¯ÄÜÈ¢þbL€r…ÌñàèYŸZ¿¦úè Q§íòdû½ß"ÈJû—ô-ƒÏ`JÊÈ«ªªŠ:35)!9éuL%áÝ«(Eùô,¶V-=Y¨‚ÌÄÏ_’SS3Hl’>¤Œ²º¦uXÿÿîXävÒaKçŸ^Î|9uÑIƒ‰G{ÉÌ )¡^úѰcí§ïÒR¨Ñ®£ÙÑ­×/úûí5]A ü©²‚¬¬zCV»^Xy꣯u¯[ÁFÅŸß™•–AZCQá÷Òf4Ï×âaÈÕÜux‡ð7ùfáaÍ~9WN÷Óa…†Æ…Î?Ϲ¶G]}nñ;î=¾yòÞ+SËZeX*€ª™ b£m¿¢ÃئgùQ>Û_ÛNÕ#Cø)/¼' _ºïU½UùÑ.F›9L\¿ljM9Æ»»§×nÛþê7²¹óõN‘óí›Ú¼ƒ›[’ }±GDÉ×ill ñáä¹H½–Žk÷ü×L–J|pzýš€ÿ‚óÕÇÅ}ZMzžß>Ý=¶7d©Çê÷L— m._¸bß^\[»aÝÆ£õÔÇ}ª,ÑÒuÉÌN†Ç“Qo¨ª½ŸJ šj׬ڶº¤iÀ*¦ÝúÍGO;îþ°ÁaDÏŽ—­rö˜žö›ðDÏí•£–˜Ö¨€%¥ÙË®ÿ:÷ýïÌ<ý©³e¾©oÎØmæÕêÕ쬊Èúö"ôGOÍ+ötBõšªԫჺñõžÕ߬ºø*!¸³ko”á´‰uÉï–ˇªwÚúiÙŽknÈlø+ ³A|¸†­›PgoP/OÝ¢r2;`Xã™!Jþ‘‘Î&*|ûí®ÞúC¦¬ÞèÆ¬ÎúÍû,3ïwdä±Þ›Ó­}vx6UÊæ1X¢ÏcÆûW &¡/Θ²yLFÆ>w[[?—™}¯úµ×hë0Ÿu0$ü£œ¤h§©‚^G—™µÏùl}Ï—å³Ó[¶^‡¹+kسñk£ÁûM%)>³¨QÊ38­ôåÕ)§öµ$¨l³aÏÓ©VREŒ*\ZÛÕ‡÷ŸkúñŠs¿¥íιɿ[k¿ôçùIâìÂ2Ø}ûª-9ô>þÙ‘«cZ|oCð­_l8-°LæÓ‡sóI¸ìý²¶…i59ò»fo›vRÿŠ~~òRdWýæb¬fYÓá§Vï2~z¾Ãâf}F¢”~K÷A­ûû]Œ:·Á¦í6YÆcfÍ÷pêTÖ]öùd%¿ÔwáQ'¿ý\O­c>íô=³~ˆE1o$ ³AŒ$”td©)üÏoÒ)Jò¥÷¨då~n#L4(EÂFoð’nö‡{nx9«³S˜>,†0y™$«lvÞ‡Q¡Ãê=]šOÚ"É*Ý¬Ž¶“Jzx)‰j_D'ºrfÂÎÉx2+6ƒEwD›c:ñã…AŽ .={ÿwn££ôÆÖ³¼f96Ò((ʦÏ>v]ÔU/¿ƒ­RgSèã7´M1çn—‘€ªÝ{bƒjÓ"Þm ™;¼¹rÎ)ÙYow,;¢¼ÎËœâS%FvfÐìÝ-_QE'ÓdéÝFKŸnØ{nÑÐæ…Ž ˆ‡þ˜°•‡Õ'ÿ<³õ`˨frEŽ%Ùï¿“!²ÖSv\~óþó—¨+‹Gu^ß¹'ƒb´®_K*/!Y†&5¨ã¯®ÞJ¤ ‹/Dƨ_¾½ªÊ²RÂÐ-îô-qãš8ºÑïÑ[{=úÎ;‘HQw/q8ðð±ë{Û©¸Ô›ÑjÄŒ1VÜJöaù%©ÍÉ—²µ*J$ëºÜnf³Å_O¾žãR—4»ÆLfwrkUW‘d”4iÔúE·k¬;Ü,ï|0Sw/Ã¥žÙ|(Öm°z9Ýi„í´:hW+«ë1G;ÿwfûÄb®þ“êãuÈrü£Ë—/‡صy÷Ù$Á§ ÏaÇ®ß>ÿŸžd¯ú{{Á/­U¯º2yï”´¥ýŸK›îùxÔÃ9,e·UÁ1Ù5|ùìÒó)!}ÀþÀNÅ^nõG˜Mg{6\<ã^´Ûô.!ý¨Çÿ9‡%M¼0Ux_–×MÄò€8ÞëþZ…³ùùtß»ƒ—˜–KuINêuÙäÞ·á¤=·wx¯µëãjT옲Zõ; ÿôÝüÎÏ©ËÄÀÇ_¯¯ê¿fô-Wã2Í9í ×þ‡öK¹xt:yŽeѽ~º@fƒøð>>~(üר‡ðòÜŒ,’'¬N“ü·NÀaüÈÅV,ájØ´7î]›,ÈpX¶k«}{-e©·\L]Ö—wÝ‹Äã1êõõ¾}"«^—I{Ö=ÛmUÄ•ÍÕÛ“è;AÉ5©Sl›‘óŸ°Àì¢ï£) ø<ŠÅd×ñ•²^ê¼ ëªÔƒÎ;£­x‹Ög6òšÞúW7¯I}è½ÿyŸ÷üZK¦e它ýääkçmoüÜn/>Ù¨ÜîéÙ`âÆåG#¦œŠ™9̵ÉÉ!¿ì6KéLxdX½qïÛ·×Ït5.ËÝÚ^¬ò}RcÃå³íU³óZzTì¬ÁÖw>ž žf9µÖÙ 6¼'['“õ]m„§k¨p…gŽ39j¿q·Ìè«a»¯$ÈÔŸµbj/Ñîs.§‚>¥io®{>_ê1$o~$Ñx¥ÔÀÎ\qʼnĄԢ§Ë¹†›ðxÅöyY®4é‚¢¢ÅQ­‹¸£YFÂó¯²ê ìb“M»Ùø.õׄ<ŠYl=žq'vvÄÄ_Þ(öãˆKßÌ/mPà†éÚÌ! Õ·]=½.(v½mÑ·WùÉ{\nÐlÿ»ƒz¿L’TÍß±ü⚊f(K{‡6ŽrÍ]ºyúøÂF¶…×TÓ"ïëõ“Ká2]ê.­chÚ@ïjøëëAklç–ßm½ M=ýæ_è?7öõ]Jñ§‹ËSc­Û7ªwËù™™þ5Ex&`Ó²œðíšç‘w– Ÿ|näd£:;üóÓ“Á?Ín]ÊëÊâŽí8Õ²K)«gGß{AÛ!Š€Ì†Òû¾ï–U¨§’µyæ°i7)iã!æ÷ SìãÖIêЩÓÛçí±ö³k\L‰ÂŸ æ-£¼¬‚$‡Jyyôe+º¢öcl<_ |‰hæ2òrli*+öüÍ8ª[î>öì Ñ?ÊÉ9Uœ‘?´Wx–˜<ªaqËÇ`rÞÙtæÍÌŽ5…KÉb0Ù‰®íZt!5Ògd1AÂʽ‹«ØÄ¥8*£ìÖîõ$Âk†¿±—s—ï¡x_^ì^1yè.Æ’cÖRù¿ŽÂ[„æ_3’ÖŽ=]Wÿ”­è>¾×S¾…ë…!ú÷çxùthû½1›šQ‰êÃÚ˜¬ }çÎÉQ3ÚÖø­hf?þÍ«B¡ùç¨ßÇyÆ€ É{žNÖU\ø¡ûã5ÈßfHYº †RñšÛ®PI¤x†èý,j>BïN‡E¦·½4 ¨wIc¨»éÈiwùžÝzhþBE ÀfúH;ô-§“tîpVî§Áæ3áì¹O\gnñï>€ø ³¡ÔR£câH“Jy|((øyfΣ½²¾Å?¾vrß‘ Q_Rm}ö®š`˜·ïQ¢åÆÃ [všåïÐáIJ&mÚšÈð’^=ˆ|k<óޚܛ›&|É> +&2Íí|ê4m^»šô»¨`KÓ·M ”^݉äHIg3¨Ô¤Oß(JŽ¢d›·©®öôþ=k“ºFY‰ÑïRøI”tÚÇDR=Ñ>cIa÷œOIÈäíBVnÖ]™Úvצ¡e_‹joRÛp*ÜWed~öèfº«‰åȉ#%Þ~{“¼sX;»Àˆ=æ\iU –{dš÷YøP*²&b£b(ãb÷£Öè1ãêävý¼×Nèu`©VÚõµ˜ñQOž½~÷é‹ôÄ +Sºé1ò•ËKŠÏÊâ}ùð‰jœ{ñ³ÁP¦“¦Åš °Ìw~–à[ü—2IvJ顿]#õä€G`|r2ù«šÇ ¹ùæg¿¿µvÿ “ V ¥›¯_Rù|JŸôc`f|bRúט$£ M ž?yì½G¸ï‡!|¾Ý—/ñ{Å(Ô•¬¨#8ðÃç J#Ñסß⣸ÒjꂸS]s÷L ,eºÝÆ.>~´™VÁ¥®¥Ç“ç-x.;véáɃO¥4ë÷°öØâ<€¼ôõj@‡ÁS>RÔ¡á-‚²Ù=‚Æv ”š>»ÇaÄô3/ß=–Tâuxf‡:NKŒÝ[³~öó{kt8z#+8N:“˜)ÕÝÁk攞;»˜/{–¼¨oõÐåOçfºœ{­d`ÀxcUG]§Åàm{ü¥©ÞáW>8žxùòÆSÅáÓÛl™ºÏ=¹¹/xßɳ~î#¿ÆÇgÈ„Uëi}t[W£Â½7Æç×F=6¦KÉÊ…ìÑfgZ¶þpÿ«¾¶E… ³vß%Ñiã¶/÷ :zþÙ«‡±¤ª¾Õ¸©î£kå¿'ÿþ :N&I()ë_ÕP&]uÙ™³ãiP”ô8ÿ‰×õ3Í­K²ß.sO¼‘–Ñ70Än¬#»´îôˆ‡”í{ÝIUÐÓ`Mëc<Ûp÷Ã3móš.¼ë–*moKªÕÐ×'ýÖËËG×Z<ºÏô µ®í~µ-ˆÞ¤Îš·R*š:daƒ=¬¶Oc»î:Ïß`³ü|”ŒŒ.ËoˆÊRf¿¹[Öºtù©(ɦ;"¼ŒÝç²~Ävõ–N÷ží?}þò¢É¡éY¶´R# ‡!ëxgâè¶üÜ[.WRYWŸå”ÖÕœ3VÞ]ïûŒÍû z¶Zy]^VV‡òo¨½¢é˜åØåÝÄöÖÏþ#W¤K+ß%ŠïÝ­Þ4–ùù ŽnMmH¨ê“õ@¥­ž3–—{;tY-½lnýz5d™¼ûš}#E>ùúÿãB×Q|Aþ1™L¶„Q#].öCE@fCi(ºn;ãZ¦)åjuXº£ÃÒBÃZØÝzkWä$ ýÛÏæ¿íeÓÛzÿ\>!W}ò ˜xâÞÄíWD±’ê¦kNÞ.¹¶òºM†»‘ÿ…¿GNR-pdA‹bƨ¶õ}—ì[r…*QÃÞÃÏÞ£¤QÌ{^|.òV“%Mòþ”›xyBQãxñµÀ„?úÙ,³Ó½e ¾$2qIáÁo”¸4¹Ë5žùöEÁ¡juÚ8ÏmãüëùªÍÚwsV‰c4˜š4»ØW÷›óºßœÂÃÏ'¤ýzæÂU7àfâ€RŒ PÙE3]òz£„3Ë«_U|¹ežc‰ò³.ìõ >ÿž*öJ3*;“ÑxÔ¬QMËöÜhZw@i]yø !³ŠÆ’”ûõÉÙþ»—‘W®¾f”ÙéYì>)U¶Ì±AfüÛ˜\Û™ûlgVv5 Ùô€Ì d6= ³è™ @Èlz@fÐ2@l|~‘· d6€¸d:µœ°ïi¶€ª×iâÑ} TqãK+d6Ð[ÜÝMµÚïºu²®\9˜³®7—lmwæ½K;õbÆàØm¾agíÊè¼â[j† à0+LÚæÆ#ÎØVÊì ü ³Þ/¯k=YO¦Ü?ÉñÁ³žêöêj^\`çJL)ÛS³ÄFÿ<øjÒ¼ãý+·PÙ@k/ýÖ¾íº¥›D¹DN\îyΠßÅÚå=£?õîQÄM‰^FÜÊ®ˆ2h,ã´W¨ «¥N9•ÿc÷öÓ½ëž+.šlöWÒ¹h·®1´õTËÜŠcòU2è+mÂÃ*]¶¶”£ÞnÛkq„¤’ [¹ÿá£î*•ô`]ï!~IR \iµ ÞÖÍäEÓ<>´Âsõž‡1)L)CsÛin½ôË'ôßv-MFBÞÁkÍ˜Žµîñ÷^¹[}Î)ßv¢§qòÂwOl4ÃQ;wƼ/OVΟ³;üqCBC¯^+‹^ãÆ Òäü¨™WN>ûó¡ ~G">|á×íä¸hŽc5NÞä·Vûl9s-òå§[¶^ó³E;ݯn4Ü÷¹’²tÃNSÖÎë{}¯×ÒŸ{¯ Z_4íµ€K6…¾LH—­VÀØY®}Œ~^'/·ìˆµÞÝ’¢Ò×o¿æž@–ËÎNOþʨåv §*õzóè>>\E%^ò³N)·vÝOæ²Õ½wo°ÔU Òî»ÚŒ9ú"UY†’ëºùäüÆÔó†Ý7É©ÈñÓëî¾³{1hå†]—Ÿ%(é6·È×Ê€•ööÚ–-;]¸Ÿ.ÝjÈ´ã;ç=À´„Å€²Afm}<ã™ápÍŠüªk¿öj‹c­M{\—3ÍÊyQÞxÌÙ»‡×2Úýv@bvΰŒP÷®VK#,\üWÎ6N|zÎËmîšvv½êMY{¶ýtæÞwô­ uZwô=‡‘•n9ž—;£ôÏûÎ\ì?µhw³ 麕~ó“Úƒ6{ûëIć­á½à^{ûAšŠ?ªöîòêjÞ”t5 ‰Œèèè+×.¼þÆ<ácÏÍéþf¾Ü>yÑ^'/ï©]gß²?çPpHèã[5©N{ÂeFh Ù’ÄSw¼0e[D ;;C¯IRÎŒc¼Ú×™Áî7ÍkE÷Z×v,rïkìg¿çé¶yûÁÓÎÌ;-e2ÏL‹ü>zKx|禧žµtð}°u¢è«®7býݾ‘­ëµïx6y¾°ຶ!cÜýZŸÓrV‘T߃—œÃšZÎfè©=ù~D«¡š?¼ÓÓøÄ«g_e帼ŒÌo×.‡íÙÛ¥{K§n ¸\ 6#-%åÚ•K©Ì3«Ç¶uòKXL(d6Дàþñ“QjŽS¾÷33²ø…öãfçßCœùî¦÷Îs”éÚp¿1¿۴­'/µI&÷µ¢< ¾ŒÈ7Lÿ½O÷7Ñ>hÛxu–@ôRRÌ­sk´Qýyk^ßã‰F‡®lée(CþlÛÉB¶}¾>¶rÃÁ—Nÿ'š ý¦·N³éç÷ŸŠY0¤fιrLiíáSý×Í$|¹qÃ{vÙ{zð†å˜f¤ÂÒÒ’dpô—/­\v¸Þ‡ÝS†&5É>½ÈeVDJÛ©§ö/µ$#tlß êqóõÛ‡,Ô~Aw5Ñ:Øì±O·CPC9Q-d¦¸uö8õäò¥3OâÇ«ä.é³k!UíçöÛ)f¡ß™¼Ÿ†±UëÖÔ rGìzíÖPJ8(ùž]‡®‘1ßô׿O·RÈYË7¼-ͦ‡8{r©S Ñ)ü%/&”2艗~üÔ‘S÷)ÿ$(4’ ÿ Œ””¬dò‘’@25gˆa—ùTnÚò…㲚v¶êÒÄ€ü9pý¹vLYÑKoÏø¦¶ëYO.wÌ÷wc(Šý5…„bÎ¥©;)i*?Ž„dÞµ^’M‡õQš¾éÓǸ ~Íœ)¸u]VÎÿ‘òzêäkÈKûöE[iá¿Z-zw2!©¯b·èX—Jý¸÷Ô)èVc,¿OWmPõõWcîž:‘ÑÝNØÕŽ9´äÃiCÖ÷1¸–3'húù=ß~ïã²5D/mÙhá¸#o½^kW"ŸÇ㓱$Õµ¤rÉÕØÂˆd¶Œ„¤Ü÷øm6r¨úôðÏщ™<>Åfþr1  Ù@KY)ÏOœLY _úId5´uêÈ]»õŸq;ÞŽÿ&6¬¡¡¢,'ûó8‚ï)Ï”Sÿ¾7y·÷™žS—H~OÂF¶Ô™ð±mÚGm\;º‹±Š²²¬¬BÁ™‘r~œÀÆæ’ïŸ/ÈËG–#3î}\JzV6SRâ]’hw¾àçø¾ðD6WNƒK¥Œÿð8‰¢´%ß>ššÍ§X\Î7>ô¿ã?e (.Cpg÷¡˜jNãä/Fc²Og?»“ËŸZÚa„°:igWŸàNõhUúõVhÝ nN* x¼ÃØ’œœåæç )ÅbÀoAf-Å_ö½§Û©™–â¯GýŽ¡l´|…Ÿ`ê¬ý«:5Z¥Yß¼SOëùó\ô¤J<5ú™ÿê75·wm’·;·Æðà-Ý—YìÐa1W³ug‹A£f¹ô¬W|‚Ÿs*ûì çé›Â³Ôê5ª[]FŠóáQâ/+Ÿ•”ôü›rd¥çMfvÎ^Š%ÙÂy|ý–…Ù¬„ °‹­fVýyBíž‹»(ž{þט *5öé™›²‹w¼ì#Wßʶɉ5‘>žWWéÎYÙÚ;´Z)êù‡Ê¶˜P2d6ÐЧ¯ër36tù­¯ 'é8’ÒZõÛyl:ã±äl·Æa3V¿v²ŒìóÓžä«3íÞNÚc[]ôÇfñøq›Æ$f¤ Ju V&:]ðã°¯û×q&!§ZÓH—zóöø¦‹îÞ­ ½žêlÑÿ¸ §ˆ¢jtîÙ\ÝûhìEÇ¡ô§ìmúó«Â£ÐÔ·oü2XÂ&ÁŸv.ãb@ÉÙ@7Ù/}öÝ7Ÿ±¿@oQFEUQ›M==èôz­žpPÒûi™CR4?=v˼M}v®•sÑ“ 3ùÙƒ;ÕÞÚ¸˜ 2Ÿ Õ¾°ÁσîXãaíÝVÔ H<žHIô«c,Á*¢ŒB8¦õ„×eݹ°íE²Em9*+áaЭRèåªhµöĪóëì|;ŸsíXóG5IÊÆí\~SÎkS뢫Àm°Ä¾ÖÑ…‘Ïbj„Œ1)ðbýÎ5¨È¨ ClwÈ9Fýáù‹ ±¥Yšb•u1 DÈl ™øáG_¦U`8K³Á„aC,ÚºÎV­„šV5úõ³“¾R©‰![|jjLµRR6‘«­aØ»g;]EÁ󧮿­=÷øöÆTæÃk'^ú@Q¼çBw¨&›´îa¢#™ý-*dßÓ)G˜‘bíú7W÷hx¥ckÝìØÞ´Åì2_K¿vM¹œ®„´ŠñïKl?;‹’“bR,®ŒªV£¥.±üŒ ©¶DãM§ÎOxüö[&Ÿ#£\³v]uÆ›vC>2™™Rò\©ìï%ð²xùü7kávŸ³«åˆ¯£?§fd LEµêºzj²¬î¯[ìajÒ—é9Ҍٯ¯¬hç¹èFâ9?¹êw=¹ÿ:‹bHË«V7ª­L nß¼‘‘žžÍUb³¥µ›µ÷<ñ„ô%;-]F^šô¡•š \wÔ61,%­,ÁdHë×ÖdM={q$CÀËàIÈ ³É¦«÷'\»õ-K \ÌõÕÙ‰íÆ`$|Ê”–ÿÓÀ¿ ™ 4#­RÃ\¥¨Nž¤ºtÑãpô23s‡,^\mÖ¬júúÔóç¢*Ž£UŽ¡¬¬¨ýûsÇ©]›zõJwáBÝ3r‡HHPYYÔ‰ÌÎ5/¸'™àȪéªya¸BuÓvÕ¤¤Ý¢­vјrÚ¦fù´icðý÷êmK<«[Q»V#íZj›4/8bÇÊÞžRV¦âã©oǶ?Õžº9v5efF]º$Äv«VÔõërcÆ4ó÷ÏJE…JHh²m›pÚÍsNš¿}›jÞBøKf¦qݜà _¾PŠ9×ÚEEÉêê¶§òÝÃ&ç½`r8¦ùÞ jÖ¬6ùÞ (d6ü¼½)>?73† £ (21þÌÈ ÆŽ¥45©”ÊÓS˜ÖRÓ¦Q²²ÔÇÔƒ—+8gÅbQ/_R¢˜!CRÊݽr«ìæÏOâR@¸?Âyõ9Ä*E±×¬ÁÔÁ•Â¥ž8Q˜å Â'kæüyjæLázˆŠ<±~=)AêOŸæ®U²*Hs‡ÈâèHéç4]bbr‡ØÚR††Â_8œ’Þ Ò,X¸°Â×Àß™ …~ý„=f‘uë¨wï(6[˜‰"’$Ф¥©ê9½`##ah¥¦ ÛsqTÿþ”©)•Mé|¿¾™t»iÝ#´± õQzò­›¡Ì§N ­û¨ì{uœÒËÙ}NBš,]R%//üh׎ÒÕ6V4¾ï_=šzöL˜»zz¹CH«èÉázßï³u+õø±ð—¼!©HÒ×­›;¤È÷âóçr_|€¿2þ yM ²ñ#3DH6«ü¼?½z]ØTn¨W&íÔ©Ãÿpº{Û¦†’×/¾zÇD/ßñnÕš??]«fÍÂ%"êCçWï绿1™Ç)Í{¥†Ìø;µ²^ºBóU&CÒÕ·w¦ÅÝ5è™ ðwÒk6`R³Ê®ˆ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³è™ @Èlz@fÐ2€ÄŸÙ222b/€Öäååÿ¼1g¶@ prrrqqo±Pä+)%%E~Vv]þuÙÙÙþMCf¨DJŽ?/ÄßG€*¨lù-†ÌŽ‹‹#? Úò%ÅåçÏŸË0­2[CCãÏ øw¨««—a*1d¶––VÓ¦MI«Åbq8œ?/ÄâÛ·o<OAAûÀªò•LMMe³ÙuëÖ-ÃäbÈl33³Ó§O“#ÇŸbÁçóEéÊ®ä"_IòÅd2™e»ÆJ ™MæMò^”÷T d6= ³è™ @Èlz@fÐ2€Ùô€Ì d6= ³èáÙØYÉíËIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/files/rpc-post-reply-phase.png0000664000175000017500000021536614770023131025132 0ustar vladimirvladimir‰PNG  IHDR‘÷l “F pHYsÄÄ•+¨IDATxœì\ÛÇÏÌ&,t—¢((*˜ ‚­`wdz»;ž>ãéÓgwwb¨Øv *ˆ¤4›3ÿ™]@JEuÿï|?ûÙÙ™;wfîÜß=çž{‡KÓ4 ‚ òÛÃýÕ@A¤D f#‚ ˆz€š ‚ êj6‚ ‚¨¨Ù‚ ¢ f#‚ ˆz€š ‚ êj6‚ ‚¨¨Ù‚ ¢ f#‚ ˆzðCš™™+•Jù|>A¥•'Aù¿A"‘ 33³ÔÊÒì½{÷N˜0Ãáðx<ÔlA)ŠÊ²uvv>xð žžÞ$õCš¬„$IŠ¢~$AùE¥’:::r¹ü“ú!ÍNLLdþâ›ÁAäs0‚M„D"Q(?˜Ôi¶‘‘ f#‚ Èa„R p8œL§4ãÆëÔ©# K1AAQS¸\îµk×233K3ÍRL+00Pey#‚ âââòøñãRL°45›iM f#‚ ˆŠRÐÆ9UAD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³ù?C‘š”,¡€‚ høÚåt…¿:W…‘¤%¦Ši‚dóÈd’êhó~u¦ßÔläA¿:HÉa”P—–´~Ü€/¢#_ĦÊÙµ–S7†—Iî ‡¯8h}Xäëè,šýªÛ&(å°_)f ù¿5ùEðdfƒªÿÜ×p8\Nñõ ¢r–ή5µêØ««~q›dG^mÛ¨IØG ‡C’Å$#2²q­Q7 SïîM]KXÜ?<8¿÷À±ka_¾M“(ØŒ<}c3[{§Š•]«×¬åíãm¥ýõú:ùâê:ݦ$H¹lÞŠ Aê™9¸×ònß½_Û:v%ËÚ¯ƒ n ¯TkË=öJæ~¡cl]©ªg“Ö]z´­­õs3˜ãÑëö•Óäó%zÕ¦I@Ùà(#ØÑL4àš)Æ$§Hù£iŽÈÐÉÅÝ·eÇø›ü¹Îè]7‡s3.÷1mº“ù*S”Y&‘ÿ#P³‘_†ý •ûœ®Eܸ¸kÛå÷Ùì*BÛÁÙŠGSÌ"M)dRqúû'§ß>µwíènºý—lŸ;Äߤ Shá6wÝök7o‡…žÏdwå™ÙÛêñ9MÉ$Ùéï> ܺ´®Û¢­û×Ð"‹Ï$5úò•§.¼ÏäDÃØÂÔP¯œ—ÏáÈ2yfú‡Ûœ Ü­Ú˜¨µFrcЗ]™z5Û­_¯vëÎõSGÝz©¬“ÖåíD$P”B*ÉNxºçîÕ=kæiØ5^½uE¯ò‚¼¦e‰Ëø{«‡2÷ëüŽ-¡‰lS†äêÚ;YpsïsFÉoî}vtÇŠ!<‹1«·OëÕPÿW¸{¹|—Ϙ®š¹*Y†‚&ŸXw)<âÞíS;÷?W–b‘®©¥™>Á\¹4+ýõ».Ø5cÇ↥“»Ûëæ\’'ñ€¯§‘“Euñb ¿Ôlä—!0«Üº{åÖ©àŠK^²«4}|ÈN!§h`$;+=%.úõ »ÆŒ]óR7Ž ¸znÖ¹33­ò%Bðµ«û¶c>òÖ5Ýêµ}ü‘YgºàøÕž d yVúÇØ·¯nï˜0a}lê½ mêž´æÐš¾ºE2“~ws§.O?Kd–ëö˜:¦—õ*mŒµó6¤Å¿‰|õèîÍ›Vì¿úšJN”2­ƒ/ž ©eâÕ²+óI¯.0m÷W–ŒYW~sx„¯ˆ’Ê¥™©ÉÑQϯ_3qîÁÔÈs}¼=Bì]7±éo+ÛÖÕÛ÷®Þ í°Ç¦df¦q»Ç›Lårš‘l¹,“½Ú/ÃÏn5uGŠ,柾W.,=¹{”ѯÎy™b\Ù§ó°{ºä`›smgnúC(—J³3㢟ÜY2uFÐëÔc û†ž½¾¡¤)–äד-Ëõ $S"I—µ„¹Œ±¤ÁºªÖmîNú¬b,»§Aó¦-o»mD•bK(:78\‡«khÆ|œ«Õk^ßÄ¡öŸ) =¿yüâ†Þs:8ä·j÷–8¸KP.·Yqÿа*EmŽqy7æS»Mûæ”_ Ùò’Ÿ D¹ycOŽCò9\¾±¦¾±e†žû×é¶)R·Í›Ð²iÍön†%NøQ,kWn_ÝÃ7Æø|ÓnŸîÉÞ/WYpy|¡¦¾‘E¥êÞ¾.`Õz³.üПËøÏé`ÿ°!R…j 8.Á^.ÏÀZÇÀÚɵIçFMo+èøˆ¾íÅvü\¤L@ÍFÔÝËfy¬š@Éoß4d¾á7–\ƒZÓÆ·Z1õ8£ÚÉNœÔv¢ˆ“óÚ¼Ùt•`ך~ùð°bÐ2®XÎ’~ÿ šýeÜZLlUu÷þˆlH°ôô­önMJ)ᯑxlÍáÃú?$ÅöÁZ¶Z6ÔeǪGÒç/™Únˆð3ýÿôYZÃzD8³˜rzܡ؎íÍ~u–õ5Q ¸n-Ü ì£QqoR²hCoTšS±v#Þñ2€gן¥H"ÍÑ>½eö…X¶’[¥ãöquJ–ާ¹€J~ëI|B׸ŠCåáŒþ]?rO>©ÉÏy,ÃæŽ~P·L,>íŠ^NðèsÝE½Ëi!ÿ¿nXZud4"œiÊ©¤›W>´ïhú«s„¨%¨Ùˆz ©gNÀ=FÕ¤YR©œà|}Ÿü„¡¾©€íSþ›(UX¨4[¹{É¥ÅÌkÕ¢NI’%›®¿K¯ÿÖ3øKƒÜöׯ“LŠlÁÆ3)lø3A$‡ËùŠÝÊnNQÊñYìöÅ„4K¯Xñ¦Tr_ ¡¥mLÀ &¿â41žðù(0…\N1Y&ŸägG‚¦äÌù)OuPÜxß'+ÖCQcÞÓP(м@ šònS4sv\æŠ|öìhš¢ªû _¿ÌESnœS†Øa_¸n´By‰•=M_/oÈO5Qòl•V¨-ð¾Q°•y5T>næí¥»UnqFUïFüËä÷Bä¯= Õ£TÂãÀãgCoEyàdÈ­[QñiÀ˜ZÚ;8Ú[;5ïÕ½|‘1yq÷‚Ÿºxóν—щ ŽÈ¡bÕjÕü:Ö²ÓùÁk€ü8¨Ùˆzðâ‰ò?ádê¤_´Nú:tJjRŽ7ÛÄÔ—S>ضOµÀV©VìðŸ€46%#gÑÆ.&^ŸÛtðÒ¨˜d9Qηƒݪ:£nÞ¼|ßfB{ú˜€G·ª“_Kä‘ç·è}õI¬LdÞºC€«.¼{|cÏÆ '¢†J³_Ì÷©8-JÉ)qñ܉[Öüyòõ„ºù¶¥X>pên€CíV-43×’‡í[¶9èøæ•‹þÚ¿þõ¡~ùG4Ü_ÙÆkìñT©¢œ{Ëõ+CúûРã'¥Gƒðë^¾Bþ\HOþÙµß?§âR²5íjuj^GKy`ÓêCÛaÃßZÜ´”ÚtÈ÷‚š¨Ò‡GvÇ)—õZ÷ÑÿG%õôšJ³mj;ê T%?;äb¼êwÇ©|Q—ôÏ!3ññûç*ÛË­Uå¼Ú6áî¶í§¿”‚…gß 6•Ï5ª·È^iìµ&$fçè&åÊ]ù§{Õ“Qž¾hÒ ‹Obuìš\¿w¦FÞ85ÙƒÖ&®÷TQõ4í45Œž k{š ÞAÓà½6,d`ͼ¼”ÊôtŠ”;Á§ØQw 0jÚ¸• HŠ áÿ¶î±ô€³ÿß7ŽŽÓS®\ºfÉdwûwc·ô©aé’2§&k9‹L\F,ØÒÎâ¶åˆÌW¾}Û»×v¯l,â@ú»£[ŒŸ²ñE¦øõ…yúÙoèÿ¹Á÷y”«Ø|ÚÚæ¦E®d1_…6}WüÕ1ÿFå}þçÓÎîöÖ@þ˜õf7±ÿáëÁ’|uwŒrËÓ®Z§˜´„£?ëÔ~òß#+8ØisÞÝ=ù÷ì•·³¥©6͘ÞëÜb“|Þ%…\&ýÕ¯’çånÊœ-þ¢¾Ç³¢÷x¢Óæ–9æ³üÖL÷á tÙöpwOç¼.NjÐpq(UÐäxpJ÷‡R€ÓpÄîsÿvQpùƧ}í+n‰|³¤Y%§çñJåz ß j6ò[ASåJ#çwnw(]´h5oz›ïH—~¸ug»D ÛøµÔÌ)øñwcr6àhZþ´!V…ˆ»{àÂu¥)°ÑÔ=wuÌœN½Ÿ±­ ç? 6 Ï~õÎuלÛÞÏÈX6rR÷6§«‹XU”¦Ü9z ’Y¨ØgÅ'Áf·¯Òsˆ÷¹ÒzËu§eËT rqfþÌ|£`ÅôR¤?œÑ¹ÓyV¡R§y4-Úz=)`#Ø Ýzo®`+1û3pÍ^›>Q ^Þwòàÿä)[¶”R-¸öžP?×C«m0bUý: |‘¬¸¹mÑÑ1mº¹”è6öZ9a”Û,æÌÓƒÆíyÛ±‹u¡ßãf8öMºW-‰é^^M{Zµ¤é9½máÃ)×·Ür[ÝOßëz·ô«Y·NÇ{áùËð—1RëO]7-‘UƒY} 6'šO%<þ— lÇ yËÎÊ2.Þòç~v Zù¶µ- ¶>Ó&T^ÔBBå[%¿;¹ï’fÁ²Ï¦\ÁVâüç¶¹'¼¦%@ÂʉK».ò_j6ò;¡ÈˆŽO&$™4;Ü53îÍókç®Ý²ÿU<;¥eÀü»Ç|oîíÑUºÞSŠ”n•áã{»å¶ ²âr}Ò 2ø%Ùâ{kýLŒVVnãÚyæèTöÉ +_° mµ+blÑb~Sƒ?@òÙ¡;Þ„²öâ½z­4›Hnáç:`æÙfeð°ÓÒ´·ñIbIkäK2c_?¾tb÷ê'c>²÷Ë©÷Æ›[úÁ4ëäÜÍJ7±å°±n"M›6²ƒ ‘{ðJÔ?m ï«2’T@2 jtë³rСP¼˜¾ó^·ù K’sžëر³æ„1ë‡Û.vœî“¿ñ¡]±â=ÔïPÞ¤4fqS$Îk×fÓkU›©î¡#C‹ C`$3«Ð ‘M 7WÓ{—˜»œ•–ž WBÉ#Ö=Æ)ôÚŽŽ-ÓTc.õ1šù«¼òi÷b”Þ%’„Bs£j5»•‘AæK"~ç¸éìB•qSl ¦l`ãëj4í|D¾8ù"itõ_Õ¼EP³‘ß ħë˜SVªÕmàÌ¿G4-™¬ŠŸ†]»úA[&'¼{qíÌþû¯*k…ö^ƒ^XdþiK…"o±ØÉÊKŸŒˆ+—¹BJ*eî‘m›Ï>bWóuª6ŸxlßÐÜðqêØÚKª%ÏŽîÅ&Ô w-8È,Ü\~P2hœ€9?‘@"ÀÃUCÏvÚßÄþ“\rxbSù!ˆÌø}5LöýÁ¤jƒþ£æÍíUìÀ9*xoNozÝz…gY'ùz&v|ˆ”ʲ¥1¯SÀV¯ÈîEÑìÒÙÕl€Èm§Sæ7,É>¬d °8lC-;wa߇±>Ÿ„4{ÇêsLcɯoßï œúcì³Ë—¯ðÒŒä÷o^ؾqëeGžcÃöíõ+YþX8|-¾ò¾Q”¢à”é$—_ô=e<7§—„Î+×fZÊJþé±Y»/-à«ó©Ê'šùÏOq|ÓMÕ’··m¡”5ô u9 ÈLHOJÊÃ2(MHÉ@ÍF~hÖÝ8ËDʆˆóe¯ÆuŸøP,eVNCÿÑ´„©Dü¼žþóò¯Ò4oèß±W¯njèp¾k15µÊ¼ÉH””ÎY|9o¯G7õο†oèØ´m—žÝ;µðrÉW Çž~¢ò]óªÙo•i:×B õìÄ3ç Ð÷lRWóùµ¬ô˜Óë{øt4°_S7óbw/ h¡aãõ»Æé±¶/!”ÜëßaÚ[öÝ— é:æ3‚ÍðñÊ U qã¯·Š ˜´üá¥=ª Ä%½!:îZ°õ˜ÄžP»d{U÷êXÑrëíhYbHðŨ¤î•r‹Tü“}7ÂÁfô@¯ï›F–¾}òï&'ÿη†ï\¯e>½Úµð«`RüÝülZªŸiNÆ=¹qýîÓ艙9dfܴ›h÷XsÚ¥ç¤opg£¶]úþѧ•y1 §ªòì¸v 8ùï -I»ùNé Ë¥’ÒšLùP³‘ß Ž™_¿¼þj×AæýO1 ‰G{ôÞÕpk·ÉM›- :ÑÞ^OO(225þü$\ÆÕ­àÈ3vI‘ù6±è˜ÙÒ†¦+ì|xº¾-'H¦¶‰Égæs“&¾—¨æ4Ìt>óêY3ÛfŠy—®Z@ g,\|¨ÅˆØTyÚû'G×Ì`><³ªcæ,Ò·NŒ°åjX7jÒ(×sßòü’NcC™¥¨]m§vú0¯yq.T*=.[¥ÙtthСb.8Éå’—û ïÌз)Ç4ºØ¥ø7)P»d†,϶v/÷J·£ï¼šºõn÷E9ïÁ|y÷\èKh~`ê÷†$u;Ì[¿¨«R\ž±™^éOG½ Ùоí ð Öþi£¢È¦¦]vºÚaË€Ì!Ìgö@pñ»úŸ)õì >9—‰ïs‚¨W—ŽGsPvŽZ9÷{†Y"¥j6òûbÖïÐ?ûÍÇœc‹&vàs¶®yI¼ã¿þ¯Ó!åÁÞ. ÜW$"ŒÃ’ªjGòî}¸}Ï(€Âd§å†o‰Œ¿%¦™¨5a†óÜéO™ÛriêÊwFZAÖ¹%;S=¦÷¬ðö$MÓ%¹ÉßËÛY}XÁÂcÃáU>ùº¤?¦f~î¸ÞýæÔë:æ}LÌã›gÖ,š}ô~:£Ò—ÖŽéM‘gVd[V|MaN¡ÎŠŠ–€áoûz¹ÿ:¨ÙÈo¶ƒßÔmÚÍ?˜O/ÖõݵùR¯’Çð|ÊÔÿsÕsFÖ²nœ?šÕ~ˆf Ô˜ˆÝíVmjÿÈáÎ_ßø{àÙk©*͌ȤÏÌkþái¢jAèh_Øp'õL,™KÍFÿ¼ÜÖ­É‘h1ÈŸM~ªÇþæe“ãŒ=ºLm³©ßöÌrÄ_þ³Û%Ít/TûëXi«²KÝ—Y4…ï!áeŽqÉwuü6óV{ÐÊ>3ü¶0°+¦½¹M²jÒC°ÛÜ´$ÓÎÿ""÷P !>ß#ÇÑгrd>.Mº½µq`³ë)¸±ïPÄ”~^6Zì\2Z$°ýᲈ7bpCÍþMAÍF~søþ³ætÙ¸ãM€t™«¦O®4)Åw"‘F=&ÿ±¥Ïú,Æì8¸'lvÿ&_¯õÓB/Ý‹*%Á)£6µ´W>a{«o?Nïb<•I·nª\™¼zTÃt)yz|’ÌÄÄ @«ÃÀëð­]®¦í0†mÄÙDh^ÆYiö]³äè™:Çâ«-s^ÛÍï«i]¯Š"¸Ê.Ý9t þ¨ôã‡üp5"Ç#Þ0Àñ÷Õ¯3£³Ã–=¯ ñÃÙà«¡§–<(W­“Wõßø /Uáó’øLqoÞkZ[il‰Öè¿túéБ‡ïCêëÉéJÍ6kR™·å=ûkؾ0ðoTêGJÔlä·‡_yã‰ÎVÍvϦ^›:þïÚÛÇ—bD•g÷©Vû÷ÎHºÚ¼ÛúÔàa_“KG„¾‚/(±»Ÿ£áØ.ä–9#lÏPƒ‡9]ñŽõUK=¦¶ÈYw¦eçÓÇÎn2/è+  ëzšÁƒX&ËŸA¤Y{ËñYž³R“íÍé© w\Ð-ÿì¥ zÔ… wÙŒ›¼5¡go£<^úÎ#Ï• üÁC¾Ù>æ-ºøœ{L–™¼rHçqò†£§:üÎF¦0ÇÏ$‰/ÁÖiK:y?7°]¡ðMo'UKŠ›÷þfƒ½àÜ1fáãþÑgÖ=lZtd=ò€š¨|çQ'ïôŸ›H³kÙê>'ù–F?¨ ®õ²«c\üF&gŸ^¹ÿÜê¾6ÚŸ}4¤ÉOÃß0ö-¿LÐeÆr¿Å³àΤEwúMr/àþN»¹aÞ=vÆ*ÒmäBï¼AÝòçWϽúØ«JÃRïßWúŽõëûçMf.ñA9.+êUB©çÝÀcÚü~{oz Z4{[·ý]?õhpêÏm¿j);ÿËûÑMGUZ\Å ÄQ‘hòô‡g6‡³ÍAå£ê}û<£¯e+³¿M—ÝyÀÜÖ*£FVüæD~&õañKf!ôŸ“I=[äº`hiVtTt‘~Z¡ŸžöWb»…ý+ÙaQÊqa¦N sBtZ/íbtlûºÐGc{.¬°m¼vѦq©Ln‹|?¨ÙȯG'o0 Áÿ\Q³‘«üvû¿É`*úÉÕ;]· djk‹r RK÷û¼5ꈸº|Òð‘kÎ?ß9°Îý}]zõëÝ9 ²y¾Cyʃë!Ç<|ñú£÷Œ¥Â)±fëjåÆ÷G»¤Òœ¾[Ož¨ÝôÌ»Œ9mšk­_=¬INçùÃS+†‘˜­^ù&—ËW…Š vlÏgÌÔ¦¦Jä3^]œÒ·_c°kÔ_9ïÓÔ˜–u\y40U|̺‰Cx½êXd'½K5iç§ûµ:Y[s¡ Rð™è|NŸ)‹Vhÿ 9‹-y¼ÛëúéW£YǶ×éõ RîüÛ¨Þ­VþÞîΆZœì´ä1ï^>ŽH6é·q^1ï—¤¥2»ç}}suÛˆãŸÄ3§gµpÅ'ý‚{häÚËœ¢SžBäѪ‹“áÂ;lx€Éàš~5ªÿ3§¬™ëwá±ñ\%…'ÌÝ[d'¾—S¦9a¦C}ˆÑí¼×Ç7à®lt‰‘Oïܸxþn2Í”IpxšŸŒdR(}¾¢ûPÃ9“†yX© _vȆóN±ó·×îÐÃÅ,o‚û¿vÿu¾Ù”x9<>2Õ/ú|‹æ-ê¸:è !3%ñ}ôÛŠjŒ7ÌÇ{ýBP³‘_GÆãmëöž¿têuîš=n®o<«Ws¯^³Eë®–Ÿ¦[âšxn™ÕÚªÏNöKÒÿæšwÖ£æíÛö>}þâÕgɪm£Ú;[U­Z­ZU·jµZôé\§D“6éT±:¸ßè“# ßtéÂ’±þG©ilj( ¤IqŒDR4MåÎFÅ­àÓ«†Á—SÅÛ¶åØ™à  ×³r¦÷¾[ÇÈ®†;“57¯€®-«~Á¿®aÑàôíKýùl¾wqx³J#IR$ÒgeªÞ ÖþÇ/li›/‚Ã<Ï"Nnwz "]ÈNÉ’³5ÇÔ}ßùÀ&ù3\y俀?»Miäê‘m×(£MœÜnï÷ÙAr)w7¬Ùwáê¥àÓª (Ȉ^WÙý¡s¿jÔnÓ¶µ£Q¾ ±í[šY¿üØóì—è}µônûú7kÒªû€Œê¸ô¸tE£¹w÷ÐiÜ“kŸ\ß”#ª4{j:® T)¶nº9¦ýÚYj_ÍRÒ´ˆKgo<§ú*¾¾hñÖ¤fM|]Ì´ÙW°¾¸tòPÐesæ«UKþ‘õîØÐÓQü>kîªí+î›wÿ8¡¼fFÞã®<î7Ç£âáXxtnbEï+'/ÕT‹KÒ@eŸ]=ñÜÚÉO¤AfdJ(å›±+t_byÏüql¶~“#Î ½|Ǽ¤¯ÃƒV„¯ÌY-ø¦^[‡–GÁþµ f#¿ŽØó›‡jiéz¶hEæLùDP YVü«“Q"Û†®–†K[ö^½þRêÑö}Ì´BžöêäöãºT ¹ráõG(_ÓÇ™]OS‰Q ¹<æÙÍÈçÜ6%Ôl%"§CZ¬MzyöÜù°;÷_¾ù)•Óì«y:úåÌ-­íœ«¸¹{V±)QzfÔ‘ó×âÄU½š¸+ÏÍ“5…4òþ•ØX½€–U¿â0òÜ‘5ûæÑ]Gƒ#žEg+hàjZ:¸x5oßÑ»B¡m5-;¾þXãüY&çžG½Ï”Èi’«kb[Ó«Y·.¾EÞ3Êíg°bƶK/¤a`U©n£VZz|Á  x{jÛéÛº:FuZçÞ/`Î&-æéÙ÷±6®>ŽFº¦Æì_v«{p–@9=ŠB–øäø¶Sí;x(ã»}×7>Ü>¹çèùëO£Øñè<‘M·Mü[×+|jy¸Ž\Ö™ÿüQä{vÍѳt¬Û¨MïÖµ êpô¢‘ÓnÉ(Ï¢MÖ™KËßÜ y~ùhx×ÖÅøÏE-§øÁ®«¾Ýü-¿yVÎÄg'/>ø m]µ¥}u•|²g«gŽ >”æÕ¡Õç4;ñʼn™3×Éô+µiS…½˜”4hÇ?GnÒ×Ö´Ù³Ùcg=‘ÐN5šT¨É$HKß\^6ûºlí‰kÐ-ßêê½ð+Ößx¯ ùfå«6k×ÍßÃÄI {v žÉØ ˆÎ^|7ªi­…7žü~áÂÕ°‡Ož¿‹Og®!Ðqp©Ñ¢soŸò:E}Ýf>£_Pý/ØuêRØË˜ v{]ÇÊÕ}[´kR­D#!‘25ùu8 ¿2ü[vÐ°åØ€"k§.=TjYRÂ5tlÑ…ùüpBzž[öÿñüXzLô (É–<=û¦˜O 4¾°Y‰ïÇuêÕËSKº5‹ÁÈݧF~i~õ½˜OÉS4­Ówr‡¯GY-:yºäi²<=tø½Û6×â~sg­c«Õ!­¾u'c—®Ç¯f¾^Å•ÇN}a_eq k^+4º`ÿÐÂk¹53ŸÏ¤Tìùj{uÄ|¾äWš ˆÚ fgO)õ€fÙ®‰ë¤åê”Á¯Rš f#òB9GAä‹?—< œS±óê¦èúE~{P³ùskkÿmâ+†äzÁëæOz•]ýÜ¿þhd#¿?¨Ù‚üîä½~Š TXiyfȺýWǵ¯n Ë2/,é0rûëÖ›4ÒþÑL"ÈO5Aß…$íå“§näÌùõöÒþóN5¬íœì,ô¿¯ò¢Ð–_mìQÇÕV?5êîËEí!¶ô¨\ŠyF²5Aß—ë›Úy Îû¹m¨ß6vaÂzáw鬦•³)çê‡W·@ӼƌÛ¦·¯„õ ¢.`YEä÷¥þ zHñ?}ß,šå*\‰ý†—l#Èoj6‚ j N{üAÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõ5AAÔÔlAQP³AD=@ÍFAõàgh¶B¡ÈÊÊú RD"I’¿:‚ ˆšñ34ûòå Ë—ÿcggJQôO8Üï IïÞ%6ÞÛÛ«,Ò—Éä<ÌÈDY$ÿÿ Í–JªjUÝ_ù¡(êÉ“ÇÉÉI¿:#¿mmÝÊ•+s¹¿Ø9ý3ÿæÍ+WWrÖ¬Úb±ì'îwF(äÍŸöÕ«¨2ÒìׯŸÕ¡B9E•EòÿŸ0í›§OSZ·ž6nÜØ²HÿîÝ»—.]æp8e‘¸@3­"ªc§Ž¦¦¦¿:+ÈwòñcÊ„ Ó ìˆÿª5Àœxtôã VØÛ;üÚœü¤&Ç!ž†ÆÏ9Úï ¹e—:cgÛÛ'Ÿ,•–ÝAþßàñààAÈΖ—Qúë7­z}ÕÒθŒÒÿÍápɈk¯y<þà!ƒÊî(AçÎ=|P[¤ ÿI_£¤©Y=ûö÷ôô,‹ô)Jajj7rä?ÿÙ–'IÂòåSäò²ª%JÆ ý¿¡Pb1#Þ¿:êƒBÁ^®²óx 5Õ}­Ë»YÀRO¸|ŽT"!ɲ­ì¯^²hìfÿßìãqÈÝOž_p(#ÍfZŒl‹ÅŠÿ¬»ˆ9oSSü f#H™£PP ù´»‚ éŸÈÂãrŒ´+›ë«ÂþspIÛr:ñÿUAýOš ‚ êj6‚ ‚¨¨Ù‚ ¢ f#‚ ˆz€š ‚”=…ÀÉ?$ r9H¥TpxÀçBÎD‘4;¬ƒÙ P#Á?7)( $b 9 !„ÏŽ§Aœ õPDÍFAʸI''Î\p5ìjª´Ë™óåb9G˱fÏ¡Cƹ™ëÒÊ1Ð߆?¸ìÂÍÛñib’¤¯oU¾~ëŽCëWuÖ r¥]–zðÏÍg£“S)’¯aè½pëv‹ËS<&®ÐÓÕb·£%IÉ4 Ëé*w“dˆõ{][ý§:DÍFAÊƤ6l±`]‹³­2Ýp)¬Š”zºvŠwßÞv ¬¬ q!þ1c† Þ’9+6T²Öä IM?³hôˆÚUÚlX<©½@ ´®Ó¤CÂå;®{9êñ H¾:¬ãàw2°Ñå_ \U"BþjhMÇ[Í7_ZЙR0¶{ÖªŽîÿÃ-Q³A²‡‰X,c•“’J@J“úÖuÚõutÂâM÷3×U}>}Éât£ÍÓÇ8rA’f®A6óæÞ µ¯ÏÖZ~ƒêéÑYÉ7".•ùTГd‚@h\køˆÑp­vµµgƒŒb4[ª|€œõ«+€¤4ëwÊáÿê+P f#‚ ¿øšŒ’¦¦Ibî_y¦å7¾’Èó½’¢8^m‘Ûw/îÉ®3Xƒ#Ð×µ¤/ ]v̲c-73žBÆuëÐmvnäO³‚]JÎ-†ÓòÂýâêj6‚ ò à !ùÅÉ,ÐósÑ}w+*gG®òOêMS °p3x“ô"[|Ãn½§]¸ôǦ‰µNÛWµqöíÐm”Ÿ«û‚|aÊfÅÿËtΨÙ‚ ÈÏ„’ŠAF'ß=4aì¿×*vÝÕÞœ ‹©èó–B «ðQž­ €¦ }×Çnøî\:lË©óáQwnœZT¾ÝŽ-S» ©ÿÄ„þ¿±fË2.ž~ô$*“à“â ©Œ¢¹šŽ®v>Þ¶ÚPèå*ôÃWƒùM[Ws6ÍßeAÂǨ]‡¢R%AÒ™iRЂ䔳2©áå\ÅLÀÜ-y²¸7§‚^¾xŸ­BSOÛÎѲAC­ìøcGžÇ$K€CdeH4û&:š)5$aR·vÏÚ:?ó­ …âõë×NNN?툂 ¥ ÁxµjjG]Z&дöOXë5 ŠÖÔ1uwVR&·:;1@›¯Å%U+@ηï5óTßI™·¯ìÛ¿mÑñC=†[{léS^öxŸáo¬Ùa%вÇÿ6™8;pX+cêáå°A µ¬lW­<±$@ü²o·áRò:¡¿`ù|IÐ 2¬ç‘9¸×ÊÓVGô¶ÎΊ‹|·iñÞ1ƒ$}¶ÍþÇ_K)ºdòÙÀj=Ïyì<®£•v8¸ÃˆKÁY3ë ´«Õvz2çïI[É/üQ‹#W°ï< Þí>6%¾BOvÜÀÏÓl‰DR©R¥ºuëöïß¿{÷î?í¸È8räHãÆE"ѯ΂¨ 4S‰:^²¿ [õ²¯t¡XÓ‰°4wÒáCü£R²À ª3)ûÍ@Ô1®¢Égû·³¤ ¾†$›ùQT©~ßYíßôhôâl`Z¿ ÿ…·=ÿÆšMrMLì,„Œl_Ųš½fµÚîm+/Õj¼ð„çêÎV¹Ûѯ¶_ûÐ½ßø›þþ÷AÊÀ zŸt”¾ÈƱœž6xšU*Û:€j:5«¯ïèºfÛ°Cü‡;‚ĆŒ?çÚ{ÿœÚÀÆ*Uª™Ü=¶I”$®ÀÊÎȼSR¸îÞê’*ûžË˯¼ü ^¡#—Ë/)éÑ£G·n݆ R±bE¯¾ –CÊGÈ–³MT’Z +ÊyRv:dd±.”^î”Ì£’© 00!¯P> 1 äJOA‚Pô5 _ R’ [5O¡<–’’rbC¨\ד hi[|`ˆ, ’S‚‘A¦QZ"¤KrE²‡£óùÄ´ŒA Ô 6Mš.ð’'’F†ÀÍ?¥Ã3sæÌ¶mÛúúúŽ=ÚÓÓ“¹|þ7§2­À¼‰ hBu^¹_ó¶cÝ<¹ëÙk›³œ³Ý§­T?±ƒTó]Ù¼õP4eÈwÄ|{åeLµqþ|€¦Ë4´gÅŠLkµZµjÚÚÚ<ïë; jBþ—[»5¬î`röÖ‚;‰­Ýµ>Õ B|bÿàÕjÖ‘*ûíâk†Y`À#”eøšúš$É3´~v:•ÿ+~cÍ. sƒÄ"ŸzŽpûÄýŸ4[ž¹þDÒ˜ÅîµÎ…ý=ÖŽ‡'0±ó%ÝÞ÷³Á;¬u€$àîA°XCyr +W¨ë’+E ¸ Wƒ£^iÜŒ\T }^‰s/|||š6mZ¡B…íÏ©$õÚÁWqi 6£\¡sãòŽÜ¸ó'㳌vs+6ªXÑŽ½ï‡H¹ýâJxªLF‘Ú"Gø×ññ©4ŸCu„öuË;Ûr™[@ògÁ¯^¼ÍÌȦtLŒê¶³Óa.-AðEdØ›‡‰)é æVñô´=¬«Õ0 Óô"%1o.žbŽÈ^'Ž–nÝöŽå„I‹o|ñ66KBòÌ«ØÕ«Å =ûA \B’!Ë_tjº4ÐU”ÙS§N1b„©©©êò6kÖÌÖÖöGeÛŒ(ÔaZ”2ù§²ÎeÚúœb&Öb„B,c«žrƒ)(@*g!¦$ಅRµ½L…F³‰“9`Ž+Íif2Æç°;2·S&ùÏðä…††ÖªUë'H¨)ä3g \M-ДB†øSk[Æwœ:gyʸ^SÆž7uvÝŠ:Ló:;1öÌ–q³/}l=úh—êBJÊ>2YÏ÷¯Ú_{`»ÖöÆŒ„‹ƒ·/ M´ž°´@ ª*›/>Íj@ð4«€6öÿ€j¥ÙÀVq&Ó"ÓÍS&"36öf–Ñ~¿~Ý*šþ:ïrΗâ™Jx)ÎÖ±«\I[¹ÂÀÈÏÚôò¹Sî5Þ/\Ó¹SÍrLC œ³iñ~o‚<;k‰|ÀðVß/Ø$I=z4**ê›ö’J‹é«y£dÏž=ŒýQ½zõ–-›ÑŽ?˜¯ ü¡’4IÖàí be —ÙÖ¹<ß^} o{VØTgEp æ<® žÀ®ý0ʈ¼+ÊØ[úЦ\<qQжXœYPABíæ@¤±š]¯4°‰:À£›¬f÷ö2PPl5•ƒ;èø÷x¨‹‚NN°/BïƒåO?é9C—a`%6‡Âý8hÛêZGáÉH€}50£YÍöhc:AÎü†rظˆÒ®‰|•~rr2s[?>iÒ$GGÇaÆõéÓç+f7s·xÚNÇç?SÑg_› L†ÍýZPó†]IH¡®>Vü¹¤ºˆÃÜZÛÅÖGù×Âä1œõh(_Óàd¿ƒÁñx™krU·¢¹vÞ8'‚NfÚ¶ln©ÁÞ¤©ÛFœ¼žnÜuLõúD$PI"wÏ=±³œÓ¸åµÌ8 ¾‰¹_sÅŸC¯ÈtÆnpÔç¬Ç’'r´–9’Þçïúå•KDœ»Ø58òMÅ# S3³y]ëµ³Òd2FÅSøÛÿz´j‡ù„c^ó¸B .Á!š\®Œ©­´kýátaæ‹ÀëÒq-ù™êâ‚ôÐC«OóÑÔà°–'ëÈÀÀë)“7ûÙê€Ê@¶®ï2ÍIsf÷•“xþª¦Íå5¹Œü$G É#er’ c‚ÂÖoJ麼±ƒ!¥êÑà ¹>s"¤¾¥Ž‰ˆP(5ÛPjT.‹[ÂúI(&&曄–Ãá0×3 ‰äöíÛ=zôèÙ³gçλwï^ÍÝ].—6š¿”ߪÚÕ96Mþ«€Ô¨ÞrZ¶Ó¸¹^“N¾‰Äi^¹Îcöôœ˜39Ó””IÐÎþÙdÌí±j;ŸŠ%1œ»·gïÚ†J`îXÆþeOü7öåšÀךÝÝiÓ•ð#îwù³2·€ãš)#â'ï2åqs‡Ÿ{ëâþðî@ݼàsè8®»:eÜÈ;a/l»¼fò–íënìço¢j¹±Š/î¿ãp(à)¢3)øüDô%©wZ´hñ­{Éd²Ñ£GZYµjÕ.]ºxyyUªT‰i ?}ú04töçR ùШ-œ[KOÃ?-ó‚iîá5`5jj°FYq.… H“-°v´]ý­YþrдßZ°'ÎÇC Ýœ Š+„[[ÁÜ < s9Ø{’zƒ1/§ƒÜ±Rì<˜•ZÎPf«¾ü·‡‘ËôP8 Cªäx4‹…ÏtéÒ­XgÆçÐÔÔdÌ>Æþ+ö×V­ZµmÛÖÓÓsÕú¥_ho}¹Ü´aýV—ãw\Ügåߥ©6-+˜­0ª\ÁÙòÅãUáQÍY(5à‘‡Þ×©ÔÆErÁ!>Gœy •;:›iCžG›’)–ö=Û†/ÜûêÑË µ Äîî«—ÜÕ°ÆÏZ—’5 ie7 >×Öº³>¡•è¹3gsÝJ~˜ÖêË—/‹ý‰ÉÂ%Œ~ˆŸëm/JÙR¥»šÊ’È e”–~¹I]«o8vÿ¥_ãò\¥kZébfÓV#·®»µ¡ªâ”æl WÈ))¥SÎláà:G†ŸÞvéÞ°ÊõÌ©„½àTZ–EÜrLâ¤è¯öîûþ½å|ã±~•Ù@F¦™™‡))¤‡0SÎ+3?àça4[µÀÔbººº}ûöe$ÜÒÒ2ÿ6òbê×ÔóãݾÞ4aMmñ{8ø fýÉ6E?AÀ»Ç å †àV{‚ãÁ£˜‘“? c7›™± bÀ_”qùÀK‚µ—`Ì4¶Ür ì;ŸÁd·>õùN FEùÛS4Ûô—òÏårKÚ  ‘ªŽŽŽãÇgLÀ<…1Ç¿sà(GØ|LͰð‹×6„Tui^Á´°yÊ3ЫSÓüÉ‘·A§R{·1¶Wž²ËGÏy~:4[å“ {r3…)1Ž\öò|B!ÓÚæ°÷Åý×u+}: ’GDï<³ù¼öè¥î|ú Šrž/;Ó²•·UIÛ$ÌåÚ°aÃ7\%‘JŠ®766góß·¦[ѽ•3eR Ò­ÀsbqÓüüª¦ *U;Ûø©ç¹ÈÉ×£7¶dª´{Î2©SU¤ß\ÉòM{-à ?~¼¦dîܹLmCQ¿ ÒCŠ¢šMêëié`–ì™x©Ò”i÷æÙä…ƒíj1¼û©W知ö¯^ì \¤ÝûNØ^ xYrЧZO˳¤æ5:;n\u2x–s=mRÈ£Ó“³Ä(ùÇŒÌL!u|¶w iµåÖìÅîË;rùz‚l½ÅûzÚ‰Äï£&õ=,öôß3”ÉBZª4=‹­íß<û`Ε+;\ 'eéñTûñ[ãe I’?(Ø,L]`ãëÃô@¸]˜§°oÅcÖ¸|¸¾ô`×d •}»|!lí k¯ÀÓ¨cþ¥Ê„ !êhWý+¹¢ŸˆÀ׆õÖ2Ä?„;<8}´ä9㿟ì„Ákáô6h?еû>Ÿ!öÓ†0nSh¶Ã>S¢RÖ¿ÿþ»4“+&ÿ.mpè¹U×fSŠàU!™M«ú»Èvm‰pnØpj#êàü3®mêt«¡ÏØr-7 úÇðú»¤kw#‡UÙ¼oü¥+¯nì½$¥žÐgê ­mœL;93aÿæˆíšÓ¦·V…~Êa7°¹¿…ZŽPH ö`0½#'@Ç5`DÇI†e!Ð't SZÕÚŽ†µÃaÝhЇ ã*ø$ìX í7I³ ¥Ç‚P_Õ€»‡áÔ+h5lhPu©ß¹ îíÀB#7Ì–€ÚíÁt#DÞWÉPþ[Æ¡¨<#ì_еy²Àð0Ø5T ƒHµt»¬övÐ ënKu N^ËHº]c¿[{<<%UˆÜ«ë‰È¼ÞWšæº44&NÄ%>—7³ãäV.Ÿ~~þ=:nÚŒFpr¶§Ù![U¾UíVRC®mÝ&ì×Ý’S¤¿š &£Œ}«™P´$2ê¡ÜÂÝ‘û…Pƒ¡}ûöe’® ’£­ÁãñÍ× ­~eÉí&‡¬öv $ùÛâÇÓò9ÔhP}0m ’­8?5eˆÞÖ7¶&y¬ÿœõe}A° ‚±7dB Q~í§ùmj–ƒ+q•ÍÍô¸?yÞ777___ooïŸ}läù5›ÔðíÒÀ·KþUŸæq¨Ò¶A•¶…ÖÓ\ Û#ì m?hzë")(€c>ú*€Öv¶ïÄ| ^ùÐj™õcþù,ªgÛ“=ðª ûàú¬¥›WQ'!±t´IZNÍÏÚ©žÐÜN­¨A`ˬhhäôCkˆ@ÄUNU#ƒ¤Ï€Þ<Ö4(õž§|H¤ìö\eY£U!¸$FÂáu°î,ø‡‰¾¬Ž2®ß€¶ç›ÂŒ‘˜ØFíçàV$å”j°­¶­ ¡€]¯ÚƒÃ¾Ò˜f$D!ÏqðäP"6Mí ÏÙy謄3_‚°È PP©Ieýð §tûï³ääëë`vÔpëåõlkðÓ¨nÖʱ#É€m‡q¨§O¶ž›y¹:Û ‹çQ\a“) “Ÿ¹¼ùú¹*-ü«jzÅ¡ªíEË„€½ïNX‹šå J-ßt¨œãM!7u¬2¹æ›q!AƒÊ›®óÐÈßcD+»˜† WÏЋ¹8Iï‡ß“­jhõ) åkXµþ*|ù®ÁÆ}Û´Ð,ðò*UH¿‚úŽ¥Àµk×pþ]uá7Öl–/àÏýô…ÁÂ_XùRc hé :Î` ƒ<Éæðàò¿°æ hóaüLè÷Øh±Ö°ø9ÌYoõÀQ£º@Ë^ÐÉΘl°3‡îµr[.Ûsl[ô X3.?84ÛÁ€0d:¼KGGî—³=W*zÂúcPÇž¦ÉèýeXºžeÃû¿ jttf'¢%°wœS¦¶ ž‡!cÁJIwaüZˆÌ]X>޹À„I #cc£/í€-‡ÀÁnùN›†cÂß%KŠNKKg'àKz›–nË4E$qï2¤I|Tª®¡@KÄ>¤Œ:xÕéÜ8vïõbNÀ¸Š¥½Uīе\MhEÁÁ4 ÇcvÛÔyg׌:ß´§K[ ‚ R^DŸØù\§AÍ¡3*‹(…<;;îm†DB˳ÅqoÒ œS\½NëÇv9wrôƒ5Þ•L¸²,EB“OÙ»ûœ+É!%Á×2E­xį‘›ï†~Tùç2ÍOF@9‚±m«m|¼ó`P¯ò­êè²/‚Ü—RŸÂY îÃÇÇiÊAÌZå$ApH²àDv–Õð3.ÛuGæ¬Ìþ°é•t~¡5’ä©@\fá§7}ÊB°Ù‹G«š]üÓœ‰ ‹–£r¼œ"SÕÑÊé˜he“‘̤ŽmâS…/[þÄÙVY®ë$/clf¾)øæ÷à7×l¤L`Êw•.P•Ê=¥‚1”ëßÑlif'")CÀ(”‡yË?•r¹d4¬ÚUüÃÆÎê(†Aó`˜jZFå”R¬,n{Jy”lqÎŽæõayCö@Ì3ÉE56^m‡CÇQÊÝiv˜«LÂîhX –oΉŸR={RÕ ç$xu¿ÅO°AIA\¢È ŸA(d^§Û7vµo ð6!¹œ…9™ù&–0ÄIü,1ÙÅD$ÊàÈTdž|ùž2¡¢°<*„ÚýÒLhqQßMQ‚&ÓZUÿúEJäÃ4vÎM‘^ç¿Z:8jQR9Åd!33ê·ÕÐ:ÌÆ‰O’4<,tŒ+hm³^Ëüž=ͼHHäèŠ?¤¶UºYlÊ»|¨ÔÏÓÆâÛÂ1IgÄÎ9xçÆ{…Þ Ýt©Y=·¶`âxºK¤Ýæ—m–ê^³b[;ɾ ×Ì59yõv‹7Ïò T|l|šg¦Ø=züdã¹(6Híú=­´øfõª62&s¢0Òcfx¾÷1Ó€’ þ÷¤¡2V±Å³Ó®e‰tùy¶c¡gm?vÿòS6[#©øV~î~?õõCe%8¿ìÔÿØ;¸(Ž/Ž¿Ý½~ô^PPì½"Ø{ï{Ôø·5MŒ‰ÆKbÔhìÆˆc4öˆ½Kì½ÒËõÛÝÿì-œ€ŒwÀ|Ãg3;·÷vnÝÝß¼)oþ‰ÕQB­*S­Õ¡;],÷ªÑºud[1Wá£T·¶n^÷ERfefúëþ\›ÍŸÖ!Ò¯nØðË£„BH*2ЄØÖ·FãíšÕ³ðâêÚíѧ³h’%X¹µWí{ sÝ„Š¿Ì8p7U*¤ÀÆ3|Ðî¶ ×Î÷èÌòÝGc³”éŸôìf]Â"ØcÍ.£ …~sø­}[Ä4o(P¿72’Õ|­„ï?ž‡¥óÔ!Œ ý~Sgßu0ðq‘JL5Ë eAM+äÍtªáôÖ£…vµ›¾e8ש]ÕÓáÃà ^:Ø•w©^Þ%w.­Í`+ttª‘÷ŒÙvXÖÎÏ£Žq™%¿÷ ,Žä&ƒe¹ËŒ~‘Ÿç,Ç…fCå×Ѿušik6e ®3ª&Öö¯ø=ÿ¼H.8à÷_…ï 9„Ñðñ}Üfvwѽћßåâʳ¹CKû¶ªÕ;²Öêœ,nÅŸt1ß ) ŸRÙqPÝ¡›:OˆšÞ©‘N‘tïÜê9sÚý´³÷æU›|¬´4°ûÐ…§×_£üjϙږ “ŽÎq+Žê¨‘…ö¹øïU•fn$—¾V~~åð·³'ÖÛñÝæy“<ª›d“X«ûŒªm–.÷™”ÈWk”7°¤Ü¶Ž]ýáÛ|ÖÚI]m˜ìhŒê¬s´ÁÌÌÃã:’%æEñ¬Ù ¦ B¨·6ËÂ\ùäÛÉ9”Èspá쿊$ͰJ`Ã5\sc^¡X&“Ùr›ú=n–Þl9uÛÎØ¯>oPAËèC…ÀÞÙÝUfºG€Cõ:åT2CSI øH©•ÄNäßlðÚrVq]fOÛØøø[Ö°ž!IPè0ò- LD¥!Ûþw¢Ü’£+·vü¿Æåù:jÖÃèå×Ëý¸¹£H_¸éxÖl ƒÁ-Ü*e*ûÔ¡àÏøLuþ^j~å\†H ¡÷™n‚7ƒÊps*°ªÔÉöŸº“2©šmN²Z%ï9ï™^6LXóðɆR …ðÇs ÞZ郦ãYX³1 SäpãL/ì¥! uE·|1qùùŸ””8=¯Úó^W»z¿må!ZEœÀÃQ^¨!,ráë9öÆ×+fOX±gÃXý±![Ÿ7þ9¼JIêÊÉ Öl ƒÁºÛçvlV^"IÈz~âÀ ¶ïôU|rI6딿·~ÿ@ ¤¯œ‰oÜ?Ÿ–‹¼/+$>»¾ë‡ot6ƒªÙkôP¸²Fçi½þ¶îÂç6ÒVŸ¸ñ¶‡0{ˆkIk6ƒÁ`ŠÒÞ=0¸rõ§çW­Øí?úÆÔ¾UÞXtKR.¨ve Kˆèø=ì³1‘4ß×ÒWÄ(2S•®5­ÜðUekn™¦BП~·éJxËõßMhúå­^~…šHo±`ÍÆ`0LA¹ùT«Q£qÝ`×KgŽüµªÝ[y“¹Z¶QJV)¬QM0p5)Ib˜Üþz0oçÍ:bRfK´!ªÒûj°4ÃPT.AGœÂ‡÷uùkÅOšú½¡O˧˜4ÛU „Mƒ+„Baé"ŠÁ`0o‡5šÓ[Uš1qÒÙ!ÓV,ü²î¢/m…ùŽ1„=Ñ‚w‹É>Š›ŽÐ=ÚW–çøÜ”DB‰… V|:Õ«ßJnß´YžQn\h.Z£ža?d•uK¤84› àèÑ[ÖÖÑ:9Ã.…$ð´Yã`¡2>üo·n­ÍX ƒ)fX=XÕœº|äÖkæ}¢÷w‘¹ã+d¯A@p‘@77Í=ѪwgÒ¼Œ}Ìøýòâä•ûŽí›¾Y“üóSšÝ¾}O•ŠdY¢Ö*ü‡Ë0 M›yìA‡á]ºt*ø8 ƒ)©0iñOßz {ùøêí½ª9>Ðé gÔ´®!v››ø:e¦$Ý– ¹t|ZœÝ“-$S¶ìÕWB2Чwn=|‘Šüì;×/Ù:¹øxy—vT&ß¿uóz›§$ܼxS{V«Ê¼ò×eůsE•þüY|ÂÝ—‰wbÏI=¼¼½}ÅoYŸ­dPšíà`?räðb8ƒÁ`,­"!Íqä¢E£i†MÏHuáÛX)‘‹Î6zðT“þR©–e¥Ç{5üaQB«V©49_y—9¿ûû€ŽÖ+2“+…¯YÔ˜Ìø4©•w._Y¯NIµ^¸¨Kk³’^ò+¢ÿ*6›×²r½\ àèseFJœWë͋ڲ¬6=]a_«ºx ƒÁ`L éìS§‰OÜY¼à:åÎ÷ªôz° ªôŽOmÜk7r¯]ˆ’VÎk:W,Ä‘%¬Ù¥ &=]ûà¼EÈìpUÙ¥$@\xxaCŠ")A‰mŒû8n}+s—ƒ)%`Í.Uxx”óóëwøp²ª£H$Ôét&/˜@  išý»¨2¡Tj"#ÃMXªÜ(•êÛ'$|ˆ.,ŸÞ¹sç´iÓLhƒy+‰D©TšÖæòåËÑvóæÍS¦L1­e³ƒ5SXÄb1r¿Ì]Šl-Z”/çÀeM³MÎ?ü`L_¹r%!!¡ô5-b, ¡P¨ÕjMhðСC|cû7^¾|éááaBãfk6¦°°,k!ƒ0išF^`¾Lô|ÆÇÇ»¹¹™¥HfïÏ6¡ÁM›6åÞE>oÞ<ÚÇ`ÞÄämã»ví2¦W®\ùõ×_¿çàÖlLa±±±)ŠÙÀÿ5kÖ¼™y÷îÝÇ—)ͦ(Ê„ãÆ×­[—/gíÚµX³1Ei_,ÉÉÉçÎ3î®_¿k6¦Œb9Ð*T¨°zõjÖÀ’%KFŒ¡Õju:H$2wÑŠÓ:(«VåTŸ””tùòe‹Š|‡)}˜6ꪸ߸qø›ššzéÒ¥Ò4 k6¦°ð+h™»­[·6¦ûí·±cÇŠÅb3–Ç\ ?ÛT‹ܼyóáÇù2†‰ŽŽÆš)RLÛ\´uëÖÜ»¨6èÐ!¬Ù˜²ˆ•••ɧQ~<¼·mîR˜F¦‹oÔ¨Qzz:ª™?>88%P~FFªði ¦(Ëå™™™¦²¶víÚܻ艉‰Ñh4¥¦ZELaA^—å4AO£%/ÄYRhn€O#§äàÁƒvvvæ-¦Œ`ÂÁ­ûöí{SþÏœ9ƒªž¥&@ÖlLa‘Éd&ŸFùñXfM¢x0ùültIÍ»€¦L!‘Hø ÄÏúõëíííU*•^¯§i𝠤¤¤\¹r%""Â$§0;X³1…E$™v¥I°¨èl¥ô•J¥æ.¦¬ šf‘Mô*ˆŠŠ2îöìÙsÇŽoÖÒÀš),–3?;7¦Ìjv­´†|ÜÝ€)6LUíÎgD©T"W›¢¨·lQ`ÍÆ[[[ ™Ÿ›2; L=àÖHY¾¤˜âÇÆÆ&55ÕäfKk¯ÖlLa±ÌÀ¢–-fŠH\ E©d‹±|p÷Ö5SXŠÈ«ûH,'zñC’¤©ægç¦(lb0ïB Å-§Óé,°/ïãÁš),¦F‰1 Eájã¶qLq"•J‹"ðCi½±fc> ¬·–µx¥Å€FÎÁ”bpÛø5ó˜j¥©Ðëõ‰ÄÜ¥0'è ˜ÜŸ(ËC0ÅX,.ŠÀ81Õ$`ÍÆ|j£´¶€’¢ðQp¤RL1SZõµ(À'¦£R©Êxô¡PˆÞw¦mlÉd&´†ÁHQtº•ÖÚ<ÖlÌ`eeeî"äA£Ñ”ñþl¤ÖJ¥Ò´š;1ÅŒÉçggffÚØØ˜Ö¦…€5S‚)¢@`%ˆ¢hÇAÐ0¥€ÒúfÀšù,- ªM[šë_ÌH¥R…Baooo*ƒz½¾Œw7`Š“¡(Åcѱfc>ô6·¨¾U³€~>‚)阼枞^ZkóX³1€¥Õ^q۸ɯ€ÉG´a0bòé…¥øÍ€5óˆÅbNg9Á¨Qmº´Ž4)$2™,##ÄÑ¿oÖ‡)~är¹iýR܇5S·oßNJJBa$Õ*•êСCèéBDHHˆ¿¿¿yËVZ«Ò…G `‰Å”t„B¡iW€ÍÌÌDõSY³(°fc ::zòäÉÆÝ:ð‰ßÿÝ4Û¢Úê‹ô>;v,†âüùó—/_¶µµE»è•·mÛ¶ìÆS«Õ¥õe‡);à¶qLÙeôèѹ5›ÇËË«jÕªf)OnÊfÛ8EQ÷ï߉‰áw>>Ož<1æôîÝÛŒå1’™™immmîR˜zõê9;;?}úÔ˜Ó¦M“T_†)›Õ L1ƒêýèáMKKË)“Éš7oþñÆÕjµåLo1-X³1#‰:tè°bÅ ~7$$ÄËË˼Eâ)ŵé>|øÌ™3»ýúõ3‰Yt=±fcЇ,_¾3w‰²)Å#M dâĉFͶ·· 3‰Y•J…ÇcЇ3fäÓìž={šÄr)“QFßw˜¥aƨ ¦{®>ž¬¬¬2+0‰Ä86°R¥JÞÞÞ&1[6§ÏaÌ‚‹‹K­Zµ.\¸`Ì9r¤I,ã1h˜²ì&Mšlß¾½yóæ–ÓQD’d™mÃ4<^³ëÕ«gªº Ã0x®¦Ø5j”Q³ýüüLò¡·À•Î_…) &L˜€4;""ÂrÖj,Å#M Chh¨——×óçÏ»víj*›¸mSœ ê&òRRRPzÈ!¦2[ŠGº`ÍÆ–ZµjUªTÉ$#DL˜²¼ …‡‡GõêÕ3334h`*›¸aSœ”/_¾J•*'OžDéöíÛ›Ê,Ž]ŠÁp,X°ÀŸ)ãmãEµhÑ" À„6ñ!˜âD,·jÕ i6RnT5•ÙR¹k6æèÔ©“¹‹¥RéììlîR˜“:˜öݤÑhJëàŒeÒ§OŸÏ?ÿ¼ZµjŽŽŽ&4[Z[Œ°f—*ž?ŸdîRB¡aHš&®\¹aŒ¦d\]¼½-bzøúúšÖ r²ñ4LqâããS§N–-[šÐfFF†››› ZX³K,ËÎ;53ó²\n)cÄŠ‚ =‚óçÚT«õüË/;L"]qqqûöýA%£™¹&ׯ_øðÁÆ_6ñ놙’"»télÂ¥ 0¥ƒ¹s熆†šÐ`)ž²ˆ5»T!%zÛÅÅÜå(ɤ¦Â–-&»‚{¢v/_;¿V“ ZoÊŠEÞr¯âÒ:õá«iÚÄÈ+§ZÉ­zöêaZ˘’NDD„i "Í.­ÍEX³K¨r‰¼£"pÊèê¡Ëh2sXË¥i/_¶h6pÃú¸ßÎ0¨´&vS„"J¥QP2š0¹yöìÙ;·KŠç*‹¯_¿¦ÕjŽ?jòª'ÂÉÉ9$$Ä\cܰfc0EðzKëJ†fç`âù$A04kꚦ8X¼xÙýû/ÝÜ<Í]B!bcï½z¥xñBž>ÓgYæåË{7®3×èW¬Ù ƒy¤OŸé¡¡!æ.H¡`Yøì³oЖ¦ÁäMÈæªUS Qæk6ƒÁ`Þ«ÕªÕjs—¢Ð3*†FEe½`ÍÆ`0 ¦d€5ƒÁ`0˜’Öl ƒÁ`JX³1 ƒ)`ÍÆ`0 ¦d€5ƒÁ`0˜’ÖlŒIa )’S•Ay“@  €ÖCþØzˆ‡ô ð‡Ú0$eˆ†Î°4]vW5Å`J X³1¦„e@¡€£³a »~ò2ˆ¿ «vA—©PÝòˆ™=¶—ƒc߃Õ{îDDÐꊼð AQR9¥ËÒš<–£P\úûÞ¿×3Ýjù·híA™:öƒ1-X³1¦„€_5¨æTRv¢gàÏÀ¶…Ú 9oû5"ªÜ9=~¿M)`[ tkl•m"-ööŒÏ®4þ±k[JgÒp¤Üª^¿+köܲqmÖJç¢ L)k6æ}Å@‘\äiZ:C @¡HÒ°«Jœ׼^ú|rÂr.rÛaàÕ*WMÎÊ%$B!×NI¸„>Ÿ×o†Ž3€Nwu|s†ôU*h §  4(=ÂNûF«{©C§¥mœ…™i A˜^Uq§¦ A@Êã3·Å«¢RýNÞ’’ײ„5óN=,™ €²ƒf g8h^ÂÏ+áÚcëc{ÃñÕ°ù$(uÀÈ¡ß8h’g] ‚õK˜¿$BÃ/aD§¸<9«ÖÂÍW ±âˆ«&àÁ øq+MoO(qw4ÖlÌ;a0r Ïœ``'`²@ì½jßzØ:( üóôþÚ”‡ çðÿ*Úæú:RO×:Ž_¥a F Õ×°¿  ØÖ¾5È-r»¯ßa°¢ (/@ݾ •À´Î> bb!JG¶€: ´z8µF¯…Õû Üæw‡½aóvð›é5I¤=ûiô… ·3ü¿ê=½©$ãò• Ÿ]M#]†Îö‹ºuñršÞÖ¹ÿ–‚ôû&î>"«Z«td£NÜ6åôñ‹)¾‘uFϨ,IO:ðí¹c§“•jFæé9ºv£¶ù†þ1Ьíã¢ÆBH÷j½> ´Ð_Ûþãíøt½À򨁿F­Ø–þf ŒaT§Ž¬a'.ªŽk¿ nN@ŠJè¸W¬Ù˜÷!õ…¶Õ`q D߆¶ÞÀªá×Ã0j2°Y baøð.Ï-ßèÛ¤ßÀCÙåú2çòqMèÆw8pqáÔýI ºôª'}}.‚dÕ ­‹×Ð_ë×÷¨urìô²ï_ XÕ­A°äÉÞSsfþ¡ù¾g—B<“ –r ÷ £'”ï´¢u Ö¼~ð"ºÃP=^§åjÞÆ|±kcC/ ­h ˜ø“‹ý¸æ÷ÖV2P*!_óATºP(qCaT  ²Ãæ‰-“]$½Ž[8$ÏË5=L̹è‹Z èK¸ãY=¨ÔÚlŽ5ó>ÐÃÕg$,? G÷BøPÇÃcŒö0< Ⱥ #f@²@ ªm‘@¤Â±8hzÃÃÃVÍãAO#•_χ+/A.†X€FoZ 8_ÿß,Hþ¾1òt… ±åÁríØFçɶFI£ÚTpCÏ Bæxæçt…ZËê)²Zó€Ãgc·íªþYRÖ[ß iÜ€QÑq—ŸÅ>'šÏ­ì@êYàQ7 Fèíc+nµiTËŽ/¤R¶uµÒ¸fÕ< ¥’I4'Wß³«U-¤¢U›*4ðv’Þù÷ÀËöµ|I¬Ù˜H!‘rdTäç;™€Î½öù'toÔeÎä‘=mî!‹4§6O^¾eçó4µo³¡ÿ[Ú¡ºo¡ˆ¾°uü‚õ[â`ç]cð„íÕâL\ôÓ }׿6>]–¯ûÅ;—@!}Í<÷Ý' –Ý¥ºDmšq~ÍäŸbìWm\î#PÇl›¶xýæJo­Sf}æD¼ˆÔïË}l`Ÿ^~¢ŽÉx·èõõÿu1¹K/Ìúß÷?%ƒ­w@žS7µw½½üóÏŸ»( ³hÁTgÈ_i°°fcÞKƒ¨Œ †Å' áH:jƒŒâÆ‹]_ý€)[`pMHÍÛlQ@&€ÍÛÆ}d>€½Àgì Ö´÷‡7ÇGsƒÎ4œ…¡ËajÐåXѨ^×ÜËÈG KI ãõônèãërëñŽKqm›¹hž_º&h?Ä–&ùU¦¤Þ®$Í/#(ÚÙXÁõ¸D iÏT¦muîT¢[]g_“"uYwâ!1íæœ.÷ ÃxB£µ“Áb½Æä‚ѱöá«—¦=è7÷¢tðŠe½¾8³húÂ^Ï’•«§–™;gÖŸwÄ}Õžø?¸úÛø^CCãW]]ÏçÎö¡C\]v<¥}9¸¸¾ã¦kñÝÆ-üIp"r_ÔÑmåäܬÑÜ’‰|_ëzS¾ý<¡õ°Ÿþ7òv`å@ŠÕ"—úÄÚîc¶ë÷%5wÉZÒÇkP÷[[vï lóËÜø†Ÿ.»hûÉÒúÙ>:þÕôeã²v/ÕEôºäàÙaÞRÁíÖ“NY¿¾ƒ¡`‚Æ|¹,î“OúͲPÁ¬Ù˜Ѫ¡Ãtø¡?ì»qQÐi=§¤öþ”7t¨Ê,Pª q#u±;€ø,®•,«FÀË{ð”E@“ÁùÜoÐÄ ‰’#Àç T€Úœ‹ØZ(´µs·ŽŽ_­}«¨xñ¬ªN}O®µ„Sw®í0§à'â1F÷ýþ•„ªÃB\\ݵåÑÄᾆ‘/Þ B&Í “¢ZáËŒžÖj-óU†1RS¥J V5Û4käP1øçÕé16¬¿7¢Ÿ"fë±Í¾?ÙÂÒR¡Bø÷c#·,™¿¦ï¯¯Ýz VA>Ž QB@§M£HZ JôT³:E–. „è>“ɹ–pþ&eô ÖJ­f hÚš¿kÙÀ­ëìôoˆŽøêIK/PÕ˜iý–õØ~ñÚÓŠõË©ÔphܶY]+ŒÜ"M‰½mÝÓÁ*ŠrTFvÕgÕ·¯úݪu ô°ÿÓ§îC¼mÀ¬kd¿¬Ù˜‚`@îÝÂ`ýX¨Øj»­1Lú¢€U€}JEbÊr÷¬!Â6EÜî ÑssÆŒÃ@HئRÎMåÊg ¹ûÉu;QrhPv¬WÀžå†C†N2ìûñ §Ç»gíJk£Ï:áa»î$­g€°³·ÁÓø4Uz¸Ú’N—‰êYŽ>N–¯mùÕóoЪryuâÂ¥çO¶ñnQNÀeÞÖp99#=]GŠñÆKëõ@ W‚{ýnðÓÒ{×´«YI™Æ\U]|êÃÁßoª¾i\·Æ·Qß÷irµãàŸôêàéž·D;{ðè;*†¾ d…FŸNÜ“ÿH§ÖªD"ïJ~LÊ®[*x5=°âL’a: ÀN©Vä) zxÖí ¿n¼“©²Í£z"kÿFÍžþ}ÒÑZ;$/Yu¶á’?Ðca©’5S!´ˆ€­W`Äh µÜÝL  ucˆ¾ £GB“ ÿÔÿ^‚0äG¿„»‰@gÀ[ v‡÷¸®î—·à¡;¸;B—pz´é݃LןƒÆ ®Üj¡–=|:ú6äæa'¨Ãýd±‡  °ï7˜¹‚*BצÐu,\­ÚBï6`î_1Dt™¹¯QÑA’”€«ÛÊ0銠\•†2ÌÚ")’›@O$EpÕ"–Õ‹\Úw\ôÓCû®­«ÚÒZ5gëš»ãÓ;Ÿ7Ÿà-²É·âný«ª2®²C¼qŠ$º\xÝFEíœ|¾ÒÖF^bë¦ÝN¯súUóºö"Ô™ÖZn#Æ5$L"k ´*=Kk3hJ¹ñ“†B (²4àÕnÑï² «·nüg]çmßÛ üúèèvUóš¡uŒH$d 5x‚dßÒÆè²J]õpFS7}Î}©V‚&_ü%H¡5pEÊßJÄ’²VáíVEMûuëÍÆ~¶™]¥ .B5S(BZÁê@¨i|.††¾°6^¦ƒØüCïXHÔ÷Ø(!x ,'@*â<`«*°x9€4†'®\-Xµ î? 6®Ñ:<åÂ’[{ÃÜŸáÞ3`((_ú¶{ ‘HB§QP¡)(õàä  Mà‡ ðð)di=þîÞàZz› Heìݨ­÷ŸÑ@þr::ÿn€~ï/ÐUþsÕUbt5Á?×\ }Ü}ñý{¸ °:Ú½iuç-çºõv¤5Ù¯1ÂÅí“ùMö®º¼pø50èVq`óΑVz…"f÷ÕXÐ1÷¯tò©í!°³•Âí{‹G2ƒfÔêÙ¬Ÿòìž…‡bl¥2+‰{g‹OªÚJwicÞº”ë4øúX ź vù2ƒ&Ý„†A§ŒBñ ¨œ7NÛ»ÑèF§½º³gnÝ¥ëævh²›ÌU!ÔªÎ[wØØäÆ9Ãù%eÞ.W¿Tdº½¯³ŒmJ,€¿¯\®" Îõú„»Í9ú÷Ô¥÷­&n²Õ‚ÚW¡¨Àš)¤5Ô Ü F4þUÁ?g×±ñ)?ðð{}˜‡g;èqð„Ú¹2]Ës[ô@:ûpÙ„GÎñB[¨Q7×y°÷„yÍ–VXš‘ô_±ŸáÕÅ2è?è·¸\n‡ehO<\9û£ì·+vuÿrg–f\åÉß§ß²òÙ¨2ÄpcØ¡´NßFuûqƒôš3ÐúÛnmø¾nõ†7o0*ûµÉy£Y<=ó Bp~ÛN²êð`{©¤^U/øó࿟× ´ ×&ÇÆœ¶é¸/X Ù¸ì‡ÐÞŸV¶“X»VŠlYqéÃË…€^§çî=†%H¤ëy óž‰BV«i0ü²zÍ£?y\/‚ot2Àݬ\s©¿ðÛQý/­„o*»–ò3²Éž/¢U:b­±ì­X³1Ë†×æ\0ú\» ’õ·¨(ó¶éX,ý–5 ͼg—“i=ViL üwﮕrV—zoÿ¾-æÌžé !HiðøÉ‹¾ü®ÿTfP°3ùìÊŽs²«ÇEè´„}~ά[­ë†JôqÿìMêÑã[¨û»¨ÆMŸ=¢QµÚm:÷µ­Pò•®Çœ½ð8æø^QõVAå¬l»LXë˱[_‰hTÓF"µq îÐm¨‡Ô04ëêž«ÅzMÒí=¦uš?oœL@$Ü>zêßûÀd]ûç`ð†åŒœÛ.k¶ PÝ©­ié]?X³1 ó‘0 «P³fSª-ÖjÓuœ·“å!gÙ§ÑÄ•{½JIÑÒLíz]G¸zÙJ…¨JÙdðªà¤W*޲AÄ7Wg!Bÿ‘›öE¤+µB™£«MybY ¬ÜtYW³+K­í$|gØ …"“RS4z†Êlí]ø@AÈë–W¬U«©P«ck·íåìíhÍ}ÅÊ©bãq{ëa(‘Ü^&â%Z/ªÔ¸I¶æ[¢BXX³1 óñH=ÊWv"rüÔœö†©§ÝëÞ,^kE2GÏrŽùmB'JNo5´]êìåãœ;Ç`Ùy”³óx˹W… ëœPŒüy¥¶^Þ9!–Y½ZÇrö!~Çá—gV±±t/k6ƒÁ`>RRnI]‘\"-XB0)‰¡H2Ùëæƒ@ÚÑYã†>_>å«Êã/xoæfi`ÍÆ`0Ì©ãóßÇN_v²N ìû²ÏÈk90f»ƒŠôhû'ó~> ªËýú=ü麈*6oì@±/~Pn›mа¯£‡U—X¾`Öl ƒÁüg-xu^q¤÷ .¸¡ž›Áeö@¨Hå{­;9h_$úíÞ3«gm"·Þ법e¸)ÝoõÅ-¬Ù ƒùï ÌÔš»y)\‘¸¨«ééÅPS‚5ƒÁ`0˜’ÖlŒ @ÕÕ‹‡aû"8AÚ(¨i ·OÀæeð×S˜¹z¸ÃÞ5°âgxÕ bWpÍPÿ ‚‚¤K°l5üy –œæ¢×K{•QôúøûI¯âÔ¬­¬RU É&?|õò¹Z/úÕô´hŸ\}•œ¦xyV ”¼uÆv¡ Õ‹¤‡²Ôja¥^Ö€£ª`0fk6ÆhÐ ¤§áÄe0Æ*‘0ì_øk3 . iÛp}*ý¨°—, NÕ¡Gøëwãbå’”»ÊSWÚ¥õš¹¼…›„•:ÊUëOlŠÚÔÕÖ†”‹´[WœJoÒ¦FˆTû‹^ dÂij·ûSõ¿>6ƒc—b0æk6¦ˆÜkl±ÆPÿ¹ò Y gY†ÜÑøÑ§üaÜz9á3_NäZÈ‹Í+ÆDž5¾XòÆéʶVFU„$í<íœí(2Ù“” ¬Ül]„`Xöƒ%(G/™²¨‚]{,+²·rt”h>Îó6h­R­Öëõ”ÔF.£S©Uh—É­$b†ÖªU ô2ËìÄ‚¸YF«Qh´:‚’Èå²’{'cÍÆ¼BgŽÀÕ ÙAå:P¯çï^>—sQ„öÙ<¥…²FêàØïpý·àf†PÓŸ“v’„ÇàŸk Ð-„ZM¡FaI¸Žž‡t57Ã3Ú7ÌU6 2oÂþP ¡v[uÈ¿–_É… I¡˜¢ Ê‹ª#´V¯ãˆB©€âC.3¬N­/ÌïE_GÖDR!EVýÊ6ÅA‰BŠàkEzÍë|„@$ùõ¶YnÍlM®Š2(ˆE$úÇ+d0˜wB€>%öð±ý›¿ûBó鱃C›*ӟĜڿkÎø;V9&+ùöáuýæï¾ÑûÛ¤I-Ùÿ:‹ŒÕ+ï_Z7ï³ÿ%ŒÜ¹cµ}‰]k6æ —ü‚pξË-V}êGXøöÎV;ÖC»y`»A÷£püg(Pµ‘Äf܃¸àW.í…á?ÀÄuÐ?Bøm>H&B x¾†Ž„5 –Äpìk˜qF΃.å!á2ÌX­çq»ímá0b”/M½¬¡~¿ã‡ —¯gè´v·o0¸qD)­ÎÜóÅÉs·•,ÁŠ=½ûÍ©ígKlŒ:ùùº‘§®ÜR›¯Ý¢®-r¾°âÄþã‰j=C‹¬ÃÇÔk^ÏžàÚ+Pa]Ltô‹L5MJ¥å#êŒáf4HŠÈ¸½1?þüÜ][ѤŽñÆò†L¡A·¢sõŽ=ün¯ÿâ²H€î?ÒÖ¿U·Ï6Ž*¡ÏmÝC;·n<÷½r²¹ ¯UÍýªºL=-“–\'°fcÞ%ëßÂŽDØø9KAŸ1pa+' #¿‚ Á@ÐP§¬Ÿ÷tPMX€AVÖ!0 '¨”к-ÈúÂ÷s¡Ó^°ÖA‡9P¡ˆôàÞ—­8¨í içaúv¨67•ʵ„f÷_[C> .¦Œ‡Þë ¥7hèÒÒ½Mšç>áòIËï––GB}eyÔ¾ËªÈÆrЫ·Ï¯ë@i~Ÿºëûqò%ÛªÜÀA€*‘®þYãæBÝͨ‹»>ÿ#e~ç^õ­HBûð’¦ó½j—¾ˆúkæŒß‘þΊÎÚ=d÷1]ùÉz¸PI§¯Î[ŸÎRî¯ Ò é,q øE kÐcÁƼ ÂZ¯{¯ÃCš'“áZÚXàW™{ýE6ï ~-9‚$)Š[‹„3żî#©×õx†ÎÓã†NDæœÝð—§bª³ùº %¬Ù˜·#f`Ó.p«®ö†š­Ì ´R S`Ù7\÷fü]î`¦0W‚kT×馹%=+7X±Jh(O[زâ ` Õ0¸Œ`áò%P²Ñô†y–:!Œ"Ã3É=ä øbxÍÞ>©+èÔ%HráÉ„J?ëêÁf©$Ø«vòUC3„Ä¡Ëd ºO?Wˆ‰K¢«—£ ÐLtÍ­Ë…„:hU¬ÛØæÉO;úýÍÖëZkÅ­†;xRŒŽu¬UA §^)èJ.‚gÑ×þ¾OD®häkÃ*³t6UÊ·m­2.ê‰ÞO^Ý+é>­²œÕã¿0ïB(†Äûwîûíab&KÊÜÃ{uéãfM¥>ŽÙ³gÓƒ$CˆüjöíÓ­¹LWÉdâþݵøŸ“qJÂ¥|Ó®ý†øKA‹´Vÿê¯ O?H`–y4é?d˜—Èб†ÞHê—îZv*ö‘Ž%¬œýêDLîR—Ì5nFsa÷W^|âÙ­{_WQÑ] ƒ5ó4 ™D”1?H¥@‰áÚB ‹VAƒrpa7¿ø_Ì‹ÅÜ6] É— ÃgÐx|ÞÈ 8¸+ûÎRq[«×j±$;ž¯ŸGÁ£tÈ\Ãö@ážù’APºÔ³7Ør‘6è²s? 9 Öu§š,}ycÌá“ɬR¦f8æW#‡†[Z›f‘%dÞÕ× ®¼|©$‚¥dòù«k'=Nב$«QñãýYý;ȲM@€¦¹ªž”5ˆå4©±wö|s³ñ/½ÜD´{ؘw@s{ïÔ!3W¶šwvNDem¹Ås'Ü«ßÉÝÚêÞ™¨çž=¿Û„~°¾ó ðK —Ö|šMÒ¯3""†„¥\úiåÈ-w®?òW˜tšÛÇÏ& [ð“§ qÅäjÇ‘gJ"kº{3»V¼SsÉsæÙ³)Ç—6X½¯K·FrQ¬Hº›VqØ}š±%ª­k6æˆÁY7ùˆÿT®l5,Ú •šAÃ` Ÿ›z·…÷Àr-äO)< Bø¬7HÀ sš¹š}_¦é”^Ú  óuÐ}2Lû –ÂÜáM†^¤RF‘¹Gæ£4ò¿Ú¥öûâ—îÞ6pæ‡#?ýZð{îM‚4¬„6ûîçÄ^ë»· ‘j^Þ›ÔñwYVÏ˧D‚Ü_Éþ?é‡~`íMDM:SyKcOá[WîÆ`@¯xôÓÊE²^'æwÍÊ©Wýný¢§›¢RË)U\¬… «Ù¹ªpܽ+Ñ™l˜¬`“Âê-F†7”³D³:ntĈi‹—ß0­)!­:þË•^®‚õ®ÒlÛºmôCÅpf~ãúð­ãÇ»I€!ÝÚŸyþ7!“}¿B!ÜÙ7å@Rƒ¯§‡@‰’l¬Ù˜w ¥ [ øôÜM€r¾†€½|G”t†eáI „"P›RÎÄ-J÷ÏQ‚„p•1Ìï"=H” r&‚AH7®-j ôœ%—C‘9íä-êC1Lø¾^ 'A_ÿ|ó€~¤ÔÊC3UzÚÐÂÁƒ䌄ã±l`O_;!­Ì¢µÿÍÉe˜,EˆœÜÄêcWSÁ- V°P©Õ(‘m¸ð$åh#Hx‘ó ëci´˜ÕªÊ‹«SÇ]ùíG‘cý„ÿy/¦C€*åÔÅ8è^Q«ä›‹ ¸þp®/š ¬ÅY»WΈ¹—$“j.©5öla+~zFGËÑgWµEy‘ íVtº¾©“ÄöåùyK¾»Ä/兀ê£bæþ–?â=ªõt´1¬Ê€ÆqàäÁ,­KFç˜Ë›‡Í[zeÁñ‹VDÉ›l‚5óvôj¨û94; ŸõA£ f 5Ÿí{@›Š°ø¬Ùåô°ow°N z¡A×Yôhqïz‚½!æ>­ãfT#_¢€É„ä,jàŸ ðómø~+P4„øƒd/L™½«@L4$æ|ŵ1|Öü ’¡wK°Àó'P¯ 7̡рFÝ¿èðÍ:¨ýH W`u`Û¬‹Õ¥íÏn? ªî#æZíP…m…B¤¥ê …Žf †¡ o¾~“ÛK rö™,¼n¥Ðfd=<ŸáÒ«¡+0¡222U¬˜aéœd,P•ë;‰£žŸ^·ö¤ò"þÛ Ùï ‘)w«öiÇ»K÷^¿Þʽ¦Ÿ¤¤½ñ0EBUzÐä”QI’«2ª§_öó‹­¿mçÊ^ŽÂ—Ãk•¿ÿ>Cï@h-'ÈDZ "8:ÅiBlÝÍöUu&£—vž¾!;@›š¢'¤¹Ðد$7ÝðÐÄÅnY«—¥iŸn^¬ê¨fTI»ƒ±fcÞ’ |»îóÿ±àà #9åî¿صpëU`â\ø{?D¯€Çá¹-ti çö‚¼+Hþ…#:èÚ ¦Œž a1°h6¨ô`í ¿ì†ªžÜx´°^0—…“·áÂ}èò%ÔØ gþ†þîýVBå?᯳p쬠jc°N„¨§Ð¹3܈‚*½Aüº~ù öyÉ=ÎhhŸA‘íâŽmúôãÕ\]œÄ2+¡K O£H—¶í×8¿&=ÞÅI¤¸ŸŽªU>Êr!o=¥EFìé—®ìý—ÿÞWA:y;æ™cw7¡Ð³¼]êÓ‡mI`Ôšg—Ÿ¦V®6¶·£†&j5÷>{ñÆ¢‡C¤hXÐÞ8ô°\ŸàòuBF|’¹}÷¹¯GÜó °’H…çÆ‘ò»·Ñ³î^Lò®åàÓ)Ä~ïÙ .R£*W©é ÀMä˜Ü°èi-gºG)zAî©IP½Ø²ÿ¹Ïâ~mD:ШŒKfi©•¿úÔ²É ú¬èDjuZÚ¸¨ØÕ]Ì^Õ$«õ`óFçнÞüµQ7gWøü×ñ§ÛÅ4õ´*! zeƒ5ónX.øIÛÐ.'ƒ›A-‡ á6XÎ¥®j8ùÖsCAÛèõ·[èâ s.S´áYÑÞ"øÑà „N„Ž9aÔô*nUÂsŠc8ݧ“³ËÆu¼V‚Ïfðì8k¥B:XB(ü¼U½$¥RÍp³¥ÅB+)«&BÇDÌé¦ÔêAd%‘‹«vHÕ°J"&}ݱ>ADÈ­ Xg»ZÓÛÖ@¯G¡ÐZL°ëŽÓ"Ã3´j5×÷Ò¬¢³L,DŽ:¸6 ì§P3”Xdã$nÕ£ªŠ&mĬNCTê]r„23SÇ5P”ÌN&éiÚa2 Å2¬£÷¸ ®èS¡•„,£!è0]šÞµrãÔ–ÓäJîñ' s´îÕ Õ2 €X"¥>¤+™ #+(H¼r♚nÙφ½n>Z¯"("GZÝÔ¦!Ëõlãò÷Þ˜Ÿ+Êù˹~$r†Œ³ö¶þVB"|Úþ£—*Ož÷ó©ã…ª’ôêÀšy/lv[t¾L:W&ýÖj*›?Ÿ›ùŽ -:…±_”Í•>XiÞƒó˜}ã,¥B °u³±Íeƒâèõ:Ob3ŒÞ1×a¶2I®¯q!Ëdb;™øÍS°eãfcc´æeg—ýnêžÔ^.µÏ}8åì%1~äRg¹Ô¸‡ÁäC ó8rúµo¦7<3´W7©&3£|®Á›ØÌš=ó³´Žµ^Ý>r]£¨ÓRÒ²tꥴ™I)éb‰$K™œ¥}”jçhO ­ÄRqÊË›Ï^x©ãÏ.]¼ÂªÁŠ9Ý\´P¯kë%ÍøÁ%ÑAsçÈ_‡Q->9I¥¶•VŸ|¢Ã¥*“ûÕ8ÓÿÓ0_7–©÷mÝÚW©£µšŒ,ØÉƒz qbÁÿÆoj¹¤sE¡ Ät­aÍÆ`0Œ)a n?S@«SÎÆ%=xœfãP·œ£ % ™úã_‡cΧ¥ékö\Ú²Z£ñÚkÊU¯*öÿ¦¼8îôÕµCÊݸpĶӂ~"ÙùË—ê4 w®‰ò8ÍÖ9 ¬vØók‡ÑY(ÑË— ^¾ŽVžõ>ý¬¼>ãð§aÜKÊèq¬Ù ƒ11:-ØWhÜÁ¯±1‡ïEq ˆì[1Ò°¬ßøŠo|Ö¨å Ü» Ø”mY¡jË×™zCk2(n×'Øx¤Oå¦Ù'b¸×o5²~î³{öªXóuIüë ¨ÿz·¤€5ƒÁ`0EÂ[åðu&ûö®•¾õîü|Ǽù•¼1QKdÇÖl ƒÁ`JX³1 ƒ)`Í.mÜæ?ƒ®QR†£`0˜2~»—*RS5þ Ù³v,~>Kž ••‰‰jÓÙc)ŠŠ(\ Š)Š"Kd"c1`Í.=1pà´³gÃI$Œ–ŠPÈ̓dYË]>ÓÊŠ6¬º©®¡X"¾})èë cÎÐÜÅ0¬yË@Rä£IâÈ’³ê!cy`Í.UDFF ?s—óšÝzy{øÖF1§§- š1¯f£ªÙ“¬ß žYË€ù…"‘¹«[\$5s7XÑ4e^§k6S„XYYµŒ/ø8 Æ‚Q(²¢£7]»ægB›|(R‚xýÿÜ™À0¦õú<½hR©\¥R˜° ÿ–e?¾gÆ`ÍÆ`0Ìû9rÈáÓ$?΃—ÔœÿåÝå·z½^§Ó½kíjµº\y(¥EÿCv†Î;µ¬[[QzºŠ0·¯Ý Á`GGÇ‚+°fc0 æ}„†VEÆ]¤²ééé)))iyIMMåE‰D"a"‰•• Ÿæ3år¹Ì€T*•½?𑘘ˆvÑÁfúéÖl ƒÁp¨ÕêW9 ±L0h€O$%%I$g®®®NNN(¶...åÊ•ãóÑ.Òì|mÝo6}’¸¸¸®]»îÞ½;44´h~t k6ƒÁ”xX–UÐh4ù<* …m•o}„ôØÑ€ƒ¤ÄAAA|šÏ{ð$ÕgÏžíÑ£GëÖ­ÇŽ+¿e‘º2Öl ƒ±hhšNNNF>n²”””FÛä²²²¬­­e2™•••üm |;;;ä‹ßé´H$B ‹#Šª ‡Z¿~}xxøŽ;<<<Ì]"s‚5ƒÁ`Š öðéõúÔÔT¾Í™o‚æü‰1Úfddð ÑÆ­‹*Uªs¤Ri%)ÑP5lذfÍš5lØpíڵ͛77ûH4s5ƒÁ`þ Z­V‘ äì·<è¤ÊÈKÖ¿ ”Ï0 òmmm‘Œ¶nnnÆ]´Åc¯rãïïãÆ1cÆüý÷ß3fÌ@—ÎÜ%2X³1 &?iii¼ãûêÕ+äõò[cNBB‚H$²···{”éèèèçç‡DW, rœE¡Pˆ¶ÆsÿÊ’ªÄ¬[·îÀM›6ݵkW… Ì]¢âß4 ¦ôÀ²,ïËæÛÕh4)))©©©ÉÉÉH˜ù4?I‰O£-’Uc+4?4íòmÑ|»tÙôð,’$Û¶m[¹råÎ;/X° eË–Û_`ÍÆ`0%†ÌÌÌté¹àw ·IÆ4Ÿà' !ß×ÊÒ]䥅††Z廿%__ß“'OΙ3yÛK—.Eÿpæ.Q1ïN c~ÜòÍι'ó9ñññ(ü`¾ÙÙ)”ægûøøð9¶¶¶H›‘0çÞæÛ5÷Ř TëZ¸páñãÇ«U«vðàA??S†VµX°fc0˜‚¦i­F£Í…qW©T"?¹ÈH˜Ñ6+++ßÁÏæe˜æÀÀ@>Áç˜ûWb,TkÞ¼9r¸;wî<|øðAƒQeîB-X³1Ì;1öþ¦¼Ÿ‰[j@"‘·b±Ø˜æVVVü”¤Ü‡·æþ•˜’‡‡Ç?ÿü³téÒ¶mÛnݺÕÿÌ]¢"k6Sjáç¿5‰qbÆ¡Ñ|‹4ŸF0 ãââbxŃvƒƒƒùL´ÅŠ‹±D"Ñ”)SZµjU³fÍ;w¢­¹KTT`ÍÆ`JHq!'ùP”¹·¾]Z“ƒ±¥šg‰Að“xú ŘƒÅSâ ½páÂðáÃëÔ©3nܸRycÍÆ`, >J%ïìòi>0Ê‘Ëå¶¶¶|,´µÉO»¹¹¡­q=¥Ük+¥¾ÏSfqttܶmÛ®]»"""~ûí7s—ÈÄ`ÍÆ`L˲L.ø(WFЧÆÑ|°h^ƒá£SSS­¬¬ø6gWWWcst•*UŒmÔ80ó~P­´oß¾Èçn򫎠 ÏmÙLÁ Î|üˆhF9½ÅÆ€Ò¹Óh+•JÞ°§§gPPP>ÙŒ?ƒ)e„„„œrs—È4`ÍÆ”uâæž üæV©Tò³ò‚rÊ—/Ï„ôظ#_c4sÿP ¦lžÍµk×FEEU­ZõèÑ£îîîæ.‘ Àš)ÁèõzNÇoyŒi”@¾¯1lÖ›1³xÔj5—ÃÙJ¸¸¸ðã¢ù0¥{ÞSºAÕå®]»"ÍŽŒŒœ3gNçÎKzík6ÆBAÞ-šßò¤æ€äù¯ü¸*‘”‹ÅÆÁV|¦Ì€½½½———\.—å€Òü4bsÿJ SäøûûŸ9s櫯¾Ú±cǺuëJt S¬Ù˜â¹¿ñññÆ–ç|‰„„ô)‚#7¼+`\¶Áš¯8«Ïùv1 UÓ¿ù曘˜˜°°°ýû÷W¬XÑÜ%ú`ÍÆ|,Ë"ÅU©Têw 2{¢0Ÿ@ÓÈ æÛœù.a¤Á|š(0÷Å`0¥ 9rdذa=zô0`@I|Õ`ÍÆdCÓtr^Œ‚yÜÊd2äË X[[£-¿kÌDb,‹E"‘D"áâøt™Z5ƒÁXåʕ۳gϺuë:uê´cÇŽ×NŽ5»4À¾?!ØHR.ż®D‰ÌÌLÞå5ƧD[~Í`cf© *„Á`ÊÈ÷;vlXXX5¢¢¢‚‚‚Ì]¢k¶¥£Óé E–>‘oË‘F G™ßÆ­m.ÜÝÝQÂÎÎŽßâ0 ¦¬Ñ°aãGŽ7‰÷Œ3JÊ¬Ùæ$==_ž÷wù)ÂFP>:ÆÁÁÁšO ¡E[''§€€”#‹EQo&Ìý+1 Æñôôܾ}ûÆ‘l?~½NÍ]¢‚Á/ôÿ˲oº³¹wÕj5?%É8=‰Oðñ)yH’äGDómÑ77·~Š0Úű0 ¦H …C‡­]»výúõW®\Ù´iS w¸±fÀùóçnÆ%Ú/»¶o\(ÊS§=>°vîÀé§&®_ùiçH/;áÝ¿ÿÕ^‡ÊU+¸ØÝ«Çwd:ùvY[¿w/x•~gϯ{ŽÿòN­nz"ùE‚ýüö¥À¾ß~Õ¿ÎÓýðéÈe—ÒšœPÍžQ&>8~éIí^ó~œÝsQYæÁÕ«FY«ŽÝËËkÖ¬YR©Ù ‹5Û`ŸÅlüdø´SR;·ˆq»~î‘Ø»mŸý´T§¥Úr›ïövTý6¥ÿŒí§2ÔZ­¤Â„¥?OíRI½3©Sä—tú멹._]rwæì×ïw8¤ø©^öópi×øÿÍüóòs=ˆ¼*ןµdk¿ú¶yÏ®‡õ›`ëUPP>f …KÛá‡s hxj4…Å]AÓÖ­ ô¿°¨ JÿÑýðõHÓC›1 s‡¸ü ÚO„±ÞðïøöÜWÀ¶ïÀB K~†?„Ê5`v/p÷áñ-˜²žÓðÅD˜î‡çÁŠ'˜°dÉ’_ýÕŽ÷íÛ÷_L`>˜¤e£¿h·x•_‘¯&“±jàˆz«¶…½»vštýЪ«ì"MtFQP“VA~ðJø±R§j­Û4Þ¶üª8!‘G³öÜ>‘P¹NÒÝ¿fDÛqîÚÛíýèHTð“ø_?oýsãÇC« eŽ•zé=o3|p;/Tå~òkDÄôž{NÓ1„äK£OÝ0$¢k¦^Ä4{×µu¨:h´ûé_Ò½¦~7¡&ŸwïŸ-‡¸ÚºžÃZ.Yvëþà¹ß(‡>ÑÅÌëÑð‹®¾‘êyu) ¨à½‡6ûjå Ũ¯¿ïÄuáiNiÕbf·J­²¦„Ytd OOÏ_~ùeçÎ+W®œ4i’¹Š5Ûü(_Æth6ºË±‡ØkÏÏ ™ûL>µý¾¨AD¥5_ì~5»-_¡K?{<íû«ñí q__—Žýë<:î[iùî¯7yõ«5ûÁßÓ=ÐAšêÀ}Ó´Ù–Ÿ_ÖlF-WÓû£gã·O+ÿy19¿fÿ¸ÖÄ,çî…?ÂÝ,è=ÈGðkDwË>F›îµ`y+t= >q†-¡y[Þ…I,œVègÜ€V+@AsÇWnSdÐg=ö`Öp)Ž.çÒk6@²æ}ší‹@û#ðÍbh*ú +™‘‘wäÈ‘üñúõë|fhhèÁ¼Fÿøn|ºÎ-¨2?^mW%ÐԤݿ÷(C ¹ƒ_€Xy}ë /ök|û_ƒÀ@Ûô‡ÏS´î•«ºŠÔqOŸ½JV¹”÷w·—¦<»ó4QåTUžòài†¬œ½âñó,Ûò~’´ñiJ¡•‹ÅrÒü¯nm܃{ñZH;÷€röpo÷ð‰{ï¯~Ã*(0ÀLzöðy’Tç€O1@Ö«§;WNØG iâæVÞOÿ0]'ªP9КÉxŒÊ•ET­dM±©/>IÈb•8øúØç;­&=îÞãWz–!¥Îž"CK0É(2ž%Þ¥X¹”“òZʨ_<|ð*S‡JèàQÁו[Þ1ãåýGñvþ!>6djÂóq©b{/?'rµ(#Ô)÷<ÍÒ›ž¬#Þˆ‚fµŠ‡7cŸ¨Ô"+G¿Šå­rd]‘øøÁ‹T†%l¹“Ú¼¡x¬–uœ¾{þ“à–_LšÞnÿB7TOàZ¤YÆðñž‘ƒŸTí=ªyÒøMýேÿuø‚yÇš,hö®ŠË~B¶`´Zïêíºk³ç^·Âêõ‚DÔË/¡®wÎÜLö¢Úƒ`áñû×ã!ÌÒ‡á$Ù«W/ó–k¶ùÉJºö¯Îyª·=p÷ï”?—( ¹ÈÏeÕŠ4^³füËŽ;œ[ös„îÓÖ P¡Û_§Nà4[©T1¯ §¯9z/_Å 6g»ã¬•Ò{ù‹UÁõ pðϾº€tCJÍ ×4rª m+À¾½N ÊÃù[- ߦ¹7J–¬D Òåy©u½—ÔàžÓœ8¨'0ÅTê¹Öuº…Õì£GÿÏÞY€U±ôaü=Á9‡îVJLPAPÄ[QÄB @EÁL0Pì»ìV,ìÆnQBºáÔ~»‡6®÷ªŸû{|pfvvvvÎμ3³³ó?èСëׯ“R]PPPö½-Ý?‡ „)×–5¸ÖbTƒð\؉–éŸ&†»wŒbv÷í¢~rQh†ý‚ÍÓ,/îÉ'êÅÞ¹ÃÓ2Ò¹¸ÚÒ}ùäçĬz‚Ïq·=Û °ÞrsÉ`óŒG»MS4µ¯ôÝùô®o3¼ãÀ”ž!“êæ?¾¹úPÚŒ½—fö6*sqñÝ5ãÝvÇ{Ù³Þì »1èÎ~ëK®0ŸÞ½¥¨iTW!q‚çŒz=:ofm± ½¸iˆ^nÒ‹g˜¾½wºî»ý{®*<™þº#7+öÜ–>Þۥŵx°Çè]=†÷P˾7soíGWüÊNwü·}q6Qîzº9Ü”&›½wÛûœ§w®Ü›´/:¨O]†8ïÀÜ!Á7këgÁH¼±bÍí¡kök¯“ùúyk{>ì몕›ùza{‡Û­ÃoE+·Š)ÿ‘_/×KÜN£z4å1rS ª~Z3Ÿž:vÙŸŽï;(²œ½}$YE/6u°¢y?ï¼×V]zä°³q¥))± G®ýþã³êÛϾnÐѱMË{¸æ¤ÈÄÅXI®ü%k·µãáñ½#ɶš5è–L±A„““§k-œo^͸<=>MH ~sêRJýJ*‘q­ê/FóZ³=Jº¶Æ¸êËn2ÍÛ¢®rU±˜oŽÎ¶7¾€#…´TÈ}]ˆø7ö=‡ý0ƒÒ÷ËRZœ*Ö i8ÔÆµËèü½ÐÛU^\˜‡e;p9\>£Á÷ÜŸ"ºiaõaô}îèTç{Îý /_¾ ß±cGvv¶X,®2·¶lÙ"¨¬(*VÞØHJJªJ»÷òòò䡲!äø€ÃááDù‘$tVnnÉÈÿFÇ,ž©µ…{m ÷àP+ÿvë.=?µeÁ>™‹Ó›±à¢y…ÛcÒµ‰o+ñ]G3â  ³¹›Ç¡ŠD¾iëv&*`³É’g5·ÑVaÿis:– 5i¨·í†Œ@ޱkÕ—;qìô¤Þž_äS”¼yû~n¿#è_LåquÚ5U—º©Þgݦdé T<æ„¶n¦Ï@Ö‹õ+Wl9Q8d”fãö–zRï-; Ö•LCÛÌ2ï©Ñ)¯–M-ìg1.n{aÜÛÏ}ÐÇ´Y\…5P…¯wí|%wiϨFÊ„}ë÷ŠrlðAEZm‡Œî§ÌhøŒ½ë义}êòß]š3'îuo]27nÍR Ú̘ÞËf£N3Ûµ‹¼k©ZõZ7¯ÅxÈ)÷8‘®„ÍZzR1F¸ÈšEv’S¢'xUz²(”õ,<'h²ˆ±µë:Œòóp_ßV¸ØÅ+Ñ`qpÀpYqaZŒ¦÷ú[Îó+ýr "õ6B¼Nxú´[Ú%uœVÉSšóî=)ÐÒJ•œ)¨ :ïs.¿&}`Éà©·™ì9²ÈËo6¿B¦¤@Sv Z§Ñw°UÅEfE/ïìq™½§öШ ¿÷Äøï­Ù¿Žjã]çcæÏ™òò2ËÚ[zE]5T¥Ü,º¶ ŸÍÄK«cMMMÛ´iûîÝ»*ãZž——Gj6©©Ÿ?®,íB¡ŒPùÄÜÜ\òPÙ2…"sæ•å™ì4Tr>ŸŸŸŸ_9eRË+tÈsY,V….BUš+(2´ZeÊ•óV”råì•ö'ÈCÝ»w¯œ`T®å;Œt:µdc¶´êë³ÇãElæG9Ù‰ùzÕŸXáÚ Ù`Kµu·b‰_,[[|¿\d–| 3“å>ì½ÚXZÛUêóI)›i&\8u’%-x—]È–,ŠªxÁÊ"t-³|}\Ã;ÛØ´iXaJGJ£…Þ‡é>Á“ܬ­š+’½Uþ—" ³¥¡ŒB!•ÕÔ—'žq¬-TŠ»zÝ:å…Ç~J-Э²û[¶þÞÂr’õ7¬™–”CÅÒ’Ô«¡­h÷c! ò£O'‚YÒq×ÞV{2SzÔŒ¹;·›Üex¯è^Å,ù‰Qå¢á Áb°™56¢<4\õà‰W}ÒfeŸC&Sö.ò2g÷²ÛÀ͸zõ¡Éð IëûU\ššž8©›]˜TÚå˜Ø~{?,îþ3Ö®þКý[ £ßræú¨é9Ÿ/„{t˜>b¥ÇÐéì2ϯ(ãè•Ðó`BU‹Â¬\1Ê5ÌŒ*v¹z¦Àã¤ô¯W„6Ô¿´DLm—`ãR”h1¹Oñ€ ¿¢/ˆJš-”‹VÔJV·=oggtî‚øð›ƒ=±²ùj¦¾¥kiiyIˆ‹‹{úôé®]»È!uq200 #|=­ß‘¨rŠ¢‚ =o\d(¶æÈdïç;2Jöãò3À©­¢¬Ä)¼.Çø54ã|Üý]iT¸Š†[f`è6u‹a‹<û m.?¹Î­lœø›Û]×[b§Ä粘ßÜ$êvô»zBkéâp§™ÃµÛÇœ *û6•¥êpäÖáUáKÜf‰M\O^Ød]~Æ„|ÊŠºGüìDHËpJ²Äà©  °PPEWÌ9LÕoÍ2 ›'Ëdeä’š™Ç$82ä/ (ˆ]&íqU©_Ó‰Zm£¶è´]8>œYœ3é†Íep#;9_Ùr:ø4µZè)Ö¼2šAf!³È9dÓ V…z/£|øâmœÐ´÷Š-—{ØVx¾”µ¿è¬Flñj4tý†!}­~_KZ¿´fÿz’žîÜt®Và[¶œ†c禘~’„PSK„  PLˆ™`Éòd‘ò4žl[EdsYH”Ê4ƒŠ' E¥ ^qóa2ÊC­÷’ Æõ°×à A1˜ìrÕ+3£1¹ È&GE ­Õp±8Yr¼C¦H98Ò [ÂôtªA%„T`išÌ¨¡˜:$Å£òüù ¡_Ú¬TxÅ`uWj.PÇÍ©é5C^”¼•B¡äyÕ’ ԖСC‡ˆˆˆÝ»w‡††>{öŒÔ$±„oø~ X¬*\Uþrä•tQ ®×¦V™À•¢•(è?YS@xš‡N'ÿ½^ÛÓØgìÞ™ne{|wN/{Öxø˜ÎdX޾§¼f3*9Êd…ÉmÒixD§áËn¯¯k520zÚ6û2E-j4qš»ÅiîÚw®M Gí›öÀ£êÕ²Z&Ⱦš!$Š$Ÿÿù%Ô¤‹#—–@åà(Ë©‰c_ó% 7¾©ˆ2?ˆ ÍõÁJÓd"OVÍʺÕ7>*ö î´`=dûHNÑ÷­oµûQ|º@MýËèväU1³G·¯M|AÍ„do^zªÓØ>¥¤ $ÕÜ9p³Ó~ó¶N˳/-¿ Á§"0úM^·é­½óúô3#þ^#ýûКýë‘b1£VOHè¦##¼wâ¨ý¥£Ì€ü>ÝtW¬™Ô'óVÛöÃg ØwÑi-›tv¶ÐSÎg ûÀÆ-æ3'ZÔnÓ¿‰úê%½äô6mj¡ñþ|pÿà–ËŠv-›u 93!i@ãúÖöÖzêÊêš­œ†ô¶«[F´Éºúih¢~:Î2áeC)²‰5"1Q³úpm=ìÛ¸‡Ð”ÃËäˆpöÚÕCƒV Ö#(Íàj Ç:8¸#îÂXùŸQP€#W0´1711 ”“Œ;š˜Ñè+%¢oÝóX¹Ï «3~_]î/áÍ›7G=þ¼ššÚ÷ÿ&4•àP#I©’ŸÂÔ¼V®Çøµ^›†ÛȱéÉirêšiFvNz^>‘/ðäYì‚kçæhëg$<ø˜ÏÖæq‹Òa2À╈„96‹S<¦ã²@Mä—½® ~ú´½£æù²P§ec6ÿ¦¼4¤¤™‚üŒ¬l²Û dsåò_ľËåkä<{™’Ϫ]ô.ÁæŠâÒ ˆ„"–‚Ž2r¯Ý»ÿÉ¢1çÕ³çYd²¬kB’Œ½z4S’7®£ÀRW(§}Ï—N9n»Ø¿xZzµ5RåØ`K²Ê-Î*‡EöQ©¬jÔïæ¨²pÅ…ø•]õ€ô sÕq 5Ôbð92ÊŒ çneØ*ˆr^¿Î$o+QrWŠvnRKÃÚíÄΗ®cÛ«ù1eÕëôñ š?©W½aA&!Cëé>¼Ã¯K˜§­‹WSÃ…V®c†ËA8ß‚M«CÜ b?ãÅ”a;fù»¶3y¼gî™;Hž;j¿Þr½·CnàÑ»ˆÝ±.ƒLKfŒÙrFü趆ËdåòóY>ÛÏtWcÐÔ3zÛêtŸ|`Çà©ö‹»ÔÕÚjãâç¤Ilºe5¹GÓVCGû» V=ºÙ¸óQß!›n?±Þ˧K½I,¿ V›ÐKý”H¡”Vó;~çÀžµ¶w÷£)&nî YãÓ¹kâÖ¹㣴÷½)cÃ"ãS<œ çq4ZtÄQÆìAî&WNõ*3M_w€_ýÙ‘ÍëÌÏ/àêÛº|БšgfôX5ltÿA–zL.O»q—Ðõ˺×/³gbü™Q>!»\8”:00b[}jÏV2Y¾È{_÷WESOLÙ&›oÅl[²hmÓt—Cä e ºOØy½ŸCMý܇ÝG/Ü›€<_kõ™Üâ/»9iª^[€»‘3¼–^C»u¼|F'=S÷+w¬õòmóúBÀÞý3“öM³ö1ÁǹÇýÙk¦´Ój>vå’=ÛüFØ<=5q÷¾i5¬Š ­Ù¿ W]W¿òÖJ2jµŒ¿TŽº®Ai9úš%n®’vêwaóäµk×øªRF–úW&šeª-›2ƒ]…ÒP+ó6Ž% 2£Ù7G^—f#!·êSž44 ¤üc[©T€#á'$ôwÃä*ZzmÐÒ²a3×yO]ç• !`08V0¸ØC ï’‹Ä’’ƒ~ËS §~9§ñà•Ÿ¯,öXNzà2©âµŠ#—¹¤| f¯y_fÁ/$”¾n ,M¹ï²\bYiÖfžOšYrh^Ь"Gÿ>Sª¹cpj»\|çR6Dß{Sš÷¦byྥ—‚Fóþ®Vþl—ÕÌma¶ÛÂb߬q%ûM:ÛíËmΊê1«ÔWqõ5Ií^ó¢{Í«ÎÖ¿)fü¦ÊG$è8®Ý︶R°f×pÉ—ÓÅïÑ8Êu=f­õ˜U)^ èu‹8Ò-¢šƒæ=gÝèY>9BÖó¬Ð³ÄgärÇ5¤|yß+„ï÷dáo†Ölš_AÊsD<§ª7qõп~Çw¸45S®ö{}ÿ[eæ·àJ$;f˜UŸQn9!ŠjõŽ˜×³úM‘¾ÿRô¯ó=КMó+е@pójÒu˜†æ—#Ýp¸@aÅÞAð´hÙø…Ð…Oó‹ø½b444åa+7hòí‹Çiþ#hͦ¡¡¡¡¡ù3 5›†††††æÏ€Ölš?Z³ihhhhhþ hÍþ+!™†¼Bpd¡¢H/Ó.‡0™9ÔÖ«rjû}ê!ÈHNf*ë(|ÇG6444ÿoü>mÍÇõ•˜†8ÿ§®@å«'üM¤¾DÄLÞ‡ñwÖìWç¦aú–™ϤL ›ÒˆÖlš¿Z³ÿ>²0h,Âônx z_þ h6Á'LÛÞ¶¯‹c`ÝÜjf<¶l³^yâ饃dèúJCówC·Ïð P• ®Ùÿâ¼üžTe™ú_$÷5FŽÃí»¨rkõ¸½üÜlºøSI8‹éëdž¦zù¢*"áê#H7Ĭé¨z+w#L²uñÊ 8ª„¶p ¼‡–açä Q·¡]bHD˜ŽÕ pöÄ\Øt‡ßào—¸Œ%›ðê3)ÔµÁô±Å–GžÄ²mxŸ ócC|ì űç0°F·:ع‰bØ…·-nÀ‘hðå0p<:›àêz¬8 ^#Lt–x•f]1¥?ÎíÀþSH'à8îŽ(Êø½ýX…”<¥ÐkµSŒc˱#jVð0Ãæmˆ+¤.4¦CQÁ€A`G(öÅ@È/_µß™t£—€ÝÝtpø xèèŠݾԨ»°j² VÞþ°*1Zùè í@r>ÔŒá=*wá‚Ñ» ôšbþ¨–ßóos°aûÕæ:Õ&§¡¡ù› 5ûOE»=¼žÃb |@-*dÈxD;Â3¬Áµ'¨¹6ó°³=FÎÅØ¶Å»„N0Á^Uœ;`r_è,ÇóO¨ʸv'=pÇcÿað_ £ VìDìÉêÓ—p9ö“1ã";!ó9ÜFâÓJ³O΂S0ÖÞÁªfˆY…V°î6F4‡Ûd¬×@ÔI¤x ž)žmÆèí·F{[˜šaû,t¹‹”›h5 ;·bu$ø”P郘:«CÑÞ&Mpt>†G£þU´Ö§ò°k6 7b­âvC¯#do£OS8yaõR< ©˜¶FòFŒí½tS¥ŠÁCÇp> Uðl¢®~¥ü5Ðó·Ù6Œ»ÛàÝÖaå*µmºî¡G]ž k=‰ƒ³.^®FÓ1Ø÷½ ppžÃLx£‰ØyˆRkFyiÎ8wäZyeªÉ Í_­Ù0 zÀr&¬A¯¹”7ë2Þ뢑öWÎbJTÔ†"Çó¥{‚¥OPW‘ò.Z]á6·æãøœÓÅóH3!Ý#Àëu'1±Sµ‰ç¿ÇøP4š‹ IÕú5 òR(ˆÇÜEh¹Ã%«ºl¼v#Gbðp™‹PvX·€:4Ò¦Vh¹«$û‘÷5ƒ~<ËE+Ej(Ì6Æ–¥’§v$âU FDO*Ú„ž`Ôų„bÍöÚÔ£vè»âñJ³)-C©)Â$Æœ\Ûàn}l»n¸=›±î8%Ø$Š•ìœU”ímLp£òÓ¦ Ô3à6 S‡B>Ó7£ëJ°IºMçqt â vE‚Q ¨ð3ÑðÝ—_„ü˨8–?{ðV F­Æ_ûQi~#ˆ¤×ï|VëØºî/±tS˜üúîãg‰Ì:ÝlTÈ@n⣋ùmÛšK3ÊÇ"¢Äg÷î?Ãkæl¯G÷*ÿ#hÍþƒ‘Õ„sw…àæ\´ÖM„í |oÕ‰< uKüÆh gP0ŸÔ·†R‰€qÛ‚ÏÇ>„¨Sµf·âß 6Á}¾„tsƒÄÛ¸—6%¡ ÉGᆶ’ÖC$D¨‰w23<ÒRŠ#Š$“êÂR»‹brð²w!@Q oÒJ¢É@ŽŒV2£­”_7@¦*& ÃB Üv§!ZÃgaÔq —ÅΫpmZSš!Ùµø¯×ÄÑü#¬¡¡w;…ÈþÁ&‘RPׯ¥Æ”ªB•›¹ŸŠï‘§ýí‚Mˆ ùB‡SÖ ƒ‘6·…mÓ±½T˜*µ ÕU÷sFíÿ¢7/xª¾ÂËéWg䡃?½~°‡ÛXqva?ò¥µŽ¥vé| h,'DŽ 0Á  Å(^Ù•‰t@]¡&‹\2ŠP ÇâIÔ¤qYx P"E+§ÄO ?—ú__¾*=þ'8v 2é'ñ~}5‰²Dbj!›Þ?h| Ò©¿†ò•§Ê’ºYÉë29Ôÿµ$%lç‹—¾x~=ŽŸ^Õ•²<ªËTXøã¹¢ùF„éOví:•’/ d z¹ô0Pá@˜uñàÎkïr9r\ÝL•%Õ@µõÀët¾ˆ­âÐõ¹¬0ýñÎ]§SÉå {÷ë­+|°çà•Ob“1žíŠ&½rÞ^ÝuäFŽ@ÄVmèâê¬É…8ýåŽGÒ9Ê–Íë?½r)/gÓõm]Õ²ùÉ~ttã±X¥vfħ‹·Ÿñ区¸õ•yqæä¥™L%ûý¬$Õ]”trçþؤBB(ÖkÑ¥g»Feçdˆ¼ä³ûwÝLUêÜ£»¹zbäþó/³µ=<»«ñ“¢vïy›#Õ¼mËWξÉf›9ör2×§ÂüOÇ÷|–.3¸œŒ§ÅͦQä?ž…qÇ—{O¿•0pÉÂLg7w3²L˜laâýcN=øÈÓ6sè¬]´…@aÊ©½»ïÆð”j;¹õ«'W®´?ÆìÛùµ±›oó¤sûo‹ÜFtS—)íþn¦*’ÒNݵ÷Ô‡ð¸\‚à´t`¥"ŒŽÚ}7NÜÞË» 'áÜá3÷_å9qk"(Ÿlßs*>—P0²ê××^…A}•±gûáOÙ1O»[¿>¼—§fÏœt–ÓI/çU;÷qj?û1ú÷¡5û‡…¥PÆ~ÂÛu?’@¿îð‹Ä•{°³¢¼ÂxšSõÎÏÖ‡‘œ-Iã‰T&Ì[T;1N¢m€¦ZX9óÏ£ìËY•Z°ÒÆž“˜ÞXâgàÉ9 ¬$ï˜.Ô[ájœBß²Þº¯V=™+hÚ†òr~hß’‡;V°”»! e°÷F“(ÄãÛˆ†Àâép› rVß‹üP;„"I¯EŒªÆERFš dMÿ”ƒær•Óü<âýÚ8°|öúöÕ¿¸}Þ•û­ ÚiïšÚiú‡>'öŠÝh×䨥¸Ý&Èšo×üt«õÆ['²uŸEóMß6í9¾û|œk_ØrõA+×–F KÎ{0´H³_,knî¹1²§‰ÜùcLín¼¼>[IÙØ^åœÞÀ«#B–¶7¬·qr»å>¾_«[¦ã&cl«?bwtˆg7%FZøô ÖÕ½ŽªÌýU>3Ž$¦œ V`3v2piÒŽc>ªo7šÛvL8yß·ÝígÉ«ÂwŽØÞPGl}c­Ìþ{º ëª&«ÙZûqO§ =flpm¬Wp9¸ëü Þìo"“2±O›ƒµç_œc—qzЉï®yçJ›„«¤mÌl¥¶}v4P‘Î’’zwæø“^t¹ï¼†Íùtjç4[–07Ü«í:öø“3»\šçamvöÖ«õuÊ·šQ½¾‚¢®»·Ô9q:×aX×sÓ;M~ß[RÚ©Òþ¸Û$åx«&ýxn׌iÉ*Hga•eÙÅJ[»ó©CÀ敼›è¨7µ4 vqäÚ9Qšýùœ­EOÍ §VºË.êвçÙUÖõ˜ÞÚê]ÿ]sÝLšqøœõ'+í¼d-kû~}Û*)üûÕ¿­Ù<õü`9 RèIà¯Â/@ÜGÊ‘ô « í!X²Ë}Ðb5t,ôÑ»'Pq¬Â0ô$|ƒ>‚7<VîÞº¦ô¹µ2 }g@»9ƃ–r2`;Õ1+Ý|1»\Mqg+æÞÅ¡;”\ñ?#G€‚l$dÀ‡øxˆ•ˆÂ0øø”@%›ð ÙBdäBOY—A"RˆÜÏÈÍ¢$öÓGðääˆÐª´˜pìŸ6¸‰Àç8¤@1“:¥0‰ù0ä")y’ „°‡s$f @f©PŸc‘Jšò“*¥´ xòªb¼:Œ‘ç°ê(äÉø ±Ônã±²6:èãìrìNÅ¥å’ò„>X1Ò9˜¶FAI *uÁŒÇ˜…pªÛvÐ,ó² A+S. ïn¾F·çÐiþ!©gö>æÏlnn '«ã;?—/#ι±pÁó9‡×R1ö0&´×Œýô¼³ùZ²í¤Ö†Úª†ƒ–|"OÜ»ï±`¶y3òľó󇧪¬VÑÜHNhß ù&¡žNM¥a~—ohë~( ²»‚6ù3+¨ôôÒY ŽuÙÇÌ}ïç­Õ-³ø‘%­ ©ÌQlÝ{’ïÒÛüÓE›űSÆë½Z0ö[Kà‹Ø,ȵۼߵ±&šÚ°'?}òJ\¬Ù ‘·cQ¸ÃÒˆ‘M%–€­¦®ÂeM“1Ô4Áä8ºs1@Oû:gÔÛÜËÔM¸{øxüø}uÔ 3`j·¡\½)jÛ¾L%`«+Ë€!¥m¨­Cj^>!âêuóÐ_›ìÿóo ºq)¶¬ÄKË#rÖ%¹éipNtõ\1*ìä²U¾¼^ç)ªË°„ Æmï¯æ{í‘tþùóŸÏŽu/)ížÁWz\Y“áðr¡‹1 Êgɱ‹æ¢XÊJŠl°%}q¶Š¦†‡)¹%Ñùõë®d»¤ø´$ïÎ⎼ÁÏ^Ên»Ÿå±ÄÊ –‚ÞÈðéàÉË(Ë0eTµô ôÿ½§é_…Öì?†õ»í7ÅÍúH\b0–˜;èæ ‚ð=ÎhuW.ân!ìýÞP*ªll¼‡“‘8~PÇìht5%Ø_Ûž¼Õ8Üi‹³×˜‰ÏB7‘¤?Ûb$î¶À±‹Øÿ<]Ü|‚&ºÔа§O0tµûÑÔn€'i˜¼ b!ÆÁ¨ï Ž¼Ç¸›†6#ÑRˆÇ·¨õk±OÐs„\Ä<…¥n=ÇÂ¥ÎÆ»\˜öG”,®¾ Ö†MˆÀ ƒ¸ž‰'™Ð}—ép!ðô)ôMð2£—‚âIšÖÆ‘8‰—‰HÊCßI¨ÕÂ8êëj½¾lcP¯bN@†Î߃•qñ‘޳pË 'cpàd›ãñL+QáCC¡'BÌ@¯õèæHÍg4ì‚ã;ðøòYà•ßW…mÑßÃÈïÕÝÉhªþM¿0Í¡ê8¸YÁ8ç6׺:ÄÁD±àɹGî]0í†2WŠÿ.£ ±ŸÐ§Åh'±½,RFvuܧ 9tth–çãd3¨ÿà!CÚ™(–{ÙSõF¤Üºu±X©ièH_Ú}Ýí)/A’p‚ÍHXÕ„£èÝ”Œ [,.¼P‚ˆàñX"aÑZi5á½ Ó×ÅKËæ<àsÚ—ÔN.7¿oÛÀ«unN¬]í]—d@$bób2b‘P‚Y,ÑTZÄWÍ0 ¿¼‘–a‹¨ÅÈýpá ò#fø“–b<%'¾øŒNúNæ‘ÿ´lÌ:ç„ûN»I•ö{²´ï<Ž~õšaf\ùŠUßKaì‡wqgOœÀ2…ñ/*•&ÕbTÙiÝ­Ÿ»»¹ âdQëçOëýçКýçSˆ ImñMqÕŒîÎ ðü9V­‚³DöQ KÀ’Ï %±ů¬ ‚)CtqCnQÈ·\E» 5©"\Ç #ÌÊ…0Xhê@ý+¥çr5þâ¶³ûâîܯ\´¾ƒËy­ºÂªÄÝÀÅçibt™5zÜÊgNÝÊ$ÒìÊ“* 9Œ*? 3´‚—UÅ@­pmP1ÉCÇèXõEt]¼½~ø¹ûýÍjþ0žæŸ zåC‡]KfLl¾<äèU\!ŒFÌ´3¤~Þà…`I‘ÊËõªÕñ5S'õ]æ:ëà–©\ë¸sqÐô@‡ð¡‡ÏLìT÷Kª‚B>CÌ*yÅÌ`q™,A¥p”ð«ÒH”_™Yâ+Øéa7ìR‹ûV4à=»=o™¨¨‚²ØùWì½Î`ÙÈù'¦-ïÙè«÷/954µhž;ãȨu]S‡¨Eô¶ÿö%æ¥ËBE…¹`™úMŸm¢Ì!S›#]ɲ´ GL¶7¢Þ‘¥Í!ÒÆuËeIów0Tÿ#zn¡ó½‰<>àÐML×òÌ{ûƒË§Mžé¼"|ü†aCÍ¿ùn~ShÍþSI=ƒÚð¨MC!7†¥¿dš™#ƒWÅ{\¶”h“•O«›7  ˆ  YY,^\¬Ä½zaôhJ­ýý‹Cš5Ã’%”câÄâ–˜8Ûqªã+«rú‚„Ü…Û¯Z/ûÓácœ Ž<ª¢5-ÈÆØË0åP ç¿m³ƒ]Ïïü¶—nƒ 7̵ª]i‘=ÍOŸü1OÅÑcù/bÚøÑ³«bS’€;–‘ aÆ»T™fÝüŽwó»9«IË£;n&ªt>—ü·ÑM5ààÕrš-g`"˾‘ðJ j±ò2â3s궦†Ž?gåeÞ»Ý1÷ëyG5 ò(ûå‘M§íó-Žj?düÇ“-¿q9„¬ñLÿáýwmuéÁTkxúõgG£Š¿$ç fëDxš¦2¢Ó‰·¹LMR] G«¨´eJ›¯«¨%|p3Nò’ë}ɇd&œ(ÚÕ€UÒ1Ùʺøô lÝÒm~©o3Z¹N½à:õØ8ƒ¾QÇæmFõ*í„ðAköŸ ‘ˆ‘ݰ¸l<°ª™š¸ù¶ê!"¤ž§›$0QQHOÇŽŇÄ®]”6ß»W¬Ð¤£èèÅ!×®cçö÷q5}oöÿÓ‡Ä,Þ…UÍ¥19x·ý6ñ<ž£a_iôüiPžz¹ÀÓSVcÅU=~üAHä0ää«Üãÿ·†Öì?5wîU’ªV8Ù0+c]²GŠÇ]]‹¥Ÿs¹¹• a±0hÈßóаØÕ.’7î‡;ýª9öóaÙ¸†Ù¸~="͡Լnú]92,¡j+Ï=‹ºÅ 9Ã:¨¥îR1OÅ®ÿ”ëÛ¨êLÕå<ƒmÔe÷î9rê·ë¤ºêjΑa ÔZ{ï›ÒãíÞÉ£Cö2Sø½†ÉÏ ±p¼Ã™àÖ¼v>Äа=}ïDñ·Æ_@aÎÜà­¶ œˆä ¡ó 7L©[Ry“O/˜¼!.³ |ÔCǤñ;^°Dbÿ9û×öŽñ^Ï9rÅîuc–,zÍ9@OcñÐ…›=›Θà ï´böáÛ{þû…9I)«*'œp¯°Ùá³ï¼uyüÄ݇>]É’b¬™ÞkµËö1^÷‰Â¦;fyô5ó ŸrN’B,ÍÞ}ÿþ¦ª_´M©Ïô‘¦GF7SÝá·w™ÉÑ‘4wðz툩[¦\e¼—Ù¹.ÆÍÇÆpɳžC†7QŸŽCƒ×//7?wí„SðyŽÛ¬LÿÀ‘Ž<–ʼ¨k|÷E¥mÛò¦uŒú­¼›%î`ÍɳèéÎV(‚7ôŒœx¨ç‡f[ð6ÓJ×g¸þƒöö/ò{{ QŸ×[Z«Éع+C™[²BŒÔÂd¤D²MìÚæ%t;g~÷2Ç–-ÚyÎÇáÏ[%ò÷4¿444Aä”    ©YÍ›i‹uW^TøX’§c¹òô³•eSƒÑì¨[³ËŲXõåú².!W]BÊ4u[붨lKÇrééOKK¼3ÝšY)Gê/¿ ,õ}õe2m×…þ»JnvãŸf/>àM¹œŸú”d+®Å­(MÄÉ¿4‘î;ŸL*qûm9ç·…rð?]›•Øò©àvñü~îÃæÇÞf4UýRn«ÑÚG9k‹}Îo†­)9x£×— ËÔqØzåíÖJ÷U„¹×†·^ʆðt,Vœz¶¢lf#V¿±ZâI¨²²d‡%vçÐ3¡%ÑF¹–Þlm§©7?L-›Fø™Øðò—6vœò>oJ5ùú 5›††æ&333QBRRRBBBbÈC¤N+KPRRêÓ§O—.]þɵ~»í¾jØÞ¨èøw¦÷4fç]U“//äÙrbžf]¥rÆüþ»B(s%Q~¾ˆ¨"¼šèÿÏКMCCó‹UƒX,ÎÊÊJ.ÃçÏŸ‹)))¤[(jiii— ££Ó¼yóR¯´ô7-€¢)E»n+î¥õ‘º´ÖàŠ 6ôKï:±³â¯Î—8;-%O€¬ô µþm®Z³iþ+ªß¥œæÿžôôôŒò!ä8˜tÚÌ”¬BfHF¤›Qé%u—"+**ª¨¨*–AF†¶%õ“Ñ0ë.RiGä¦GL¶HȬ3d×Õ.漯Ÿ÷o“wçôy ÿxñðƒ£©þÿÓB×ï‡Ölš“Œ»pkO1ã | ~un¾°m*–靖?Žß Iúã!¾ âããKÿAj3)ºeÐÔÔ$ÿššš’ÕÕÕy<³ ,«¬÷WßÙ_‡NÓNM«·¶ûksp÷uøz´¿Z³iþM”̱q:êõ¤¶ ý^òrÁ“Ae“A„¨@Ìⱈ˳zØÎVLlÕ‚ˆO°8ß3ˆ4Bt:ÿ“ ”üÑ>Ÿ_XXÈ—Pê ÉÉÉ!ÄEÃâÒs©W,)1IÑLµ™™Y©—M[%£¡ùyÐÕ‰æ_&7ÿG„Q‰©³0=•LG¾8Ñ×gㄥͩt¶ÖÂÇîî6[zȧáw]!Ÿÿý¹ú~nݺUPPЦM›ÿâb5B9NMMM)C‘—ü É,4¯ ¥^.—+###++Kб±±±ly¸Ü?ï Wš?Z³iþ¾w’óélxŒiUUëuo©±t°éFµÚ²Ú¯š©ôZ7à‡”ƒùMV¿~Œ7nøúú^¿~}„ ÿf“£áÄÄIJÔ¥á¤*«ªª ‚É¿ºººäß ”†T)½Œ¯-K¦¡¡ù¡5û/#í á=°_—/@›Ëóaë C; g¡§'œ£°®; ÀŽ`Ö8Çý6N®è‰u’íÕ’ÎÁãQée)c[sP¦@Ï®EwÈ™#¨*+"Et…¥I«dã–‘|ÍküŠó.iĵ=j·uŽæ"ï‘SsÌ.7ÑXîm¹È¼iàU ^H¶=2žQxpM$& ¯¯Ã©xIBe]Ûw1BdÀÏÆœöè9ï®BÿG·ô¾páBXXØ;wÈqmÙð²ûP‚P(L*áóçÏIe ½l6[QQQAA¡h]4éPRRR@zutt¸%ðx<‡Sä ÿJIýÛ»žÓÐÐüКýWA@H ý f¡¡4ôúa(£"óc`án2E¨eR´ŽGc¸=ò™x|©¡È[ÿSawR¤1­d?TÍ6XVbò™TGE Hq©úÀz!–‡!hKMéQÅàó©]¿ËîÛëáã^d¢÷—­Î¸w›©7wîÑØ¢CÙ|¯qå#ì4alʃ+Â6¬X°Iš‚anIZÒÐP¡Öóx¿ ; &ñû4[,“ƒÝóçÏ7îÝ»wUƹté’µµu|||ZZšŠŠ )½E_97n\ô1qµ[tÑÐÐД@kö_ }¾Æö˜<×ÓåIÓó_ONTHæJß¹q–¾¾þŠ+,XðòåËÈÈÈ={öÄÅÅUÌ(ƒA/®¦¡¡ù)КýWAàs2”ÕP×ó@Ëó."y:Œ€Ï…ò/sñ4 µ`ç‚ Ý`#ÃÏ«×l)ë'p-íjPf}Fò[XNø†Ü2¾ºrÞ¼‚¾1ŒÍ¼²èzþh+Ó7€Á_97åDL4ÿN3?L&³è“'uuu›… ¦¤¤â}àÀ§OŸfJ(,,ÌÎÎVRRú¾¤ihhh*Akö_…ëg£Ö iAùÞ¤@ÎJªXèŽÞ˜±þ½!-ÄË'¨ÓïÐjbc¨éîü7øxëVŸ2ý† t vÚ9hAö ð2 ­-!NˆŠÔW„ !xª‹{_“Py d=Ë8°¡¨ ™V™aN˜­ƒ9åŽ}‡†õ(cšžAØ=ý40y t›†|BȧòXTòžc„ÌF¡)©Ùüôý+C£³Ì‚ƒ\«šÜÿ jjj#$äåå=‘ðâÅ ‘èû·”¡¡¡¡©­Ù XÔ‡OW Oƒ ˾¸°ž!£ç:ìÕÃX_,õ‚¼6úŽA¸ ‹n\˜ÊQ3Ë,UŒß—7-QµÆÓçðö@h™ÀgÚµÛ!Ø »0û=µ΢/îÞ@“¯ÍA·[KØÔƒãpl]…o{ܽ;œ˜Bv Т/†P7g7—U0všS/Ù-;"tÈ;±„Áo`¨„O™à¨Àu –B–~ò»á N~»UÏðö?¾±©ŒŒŒ…±XLïÁICCóS 5û¯‚‰Žc°»5µ-¨y3H—(ᑱFbÁ—ˆ‘ f`ó…/!'ŽâøQôèZSò1q< ›lÁ*GE``ê*”Ú´={ o¯¡iϯäT¡!bs¾ãÎ@MüwŸŽî¶Q”Ë®dù:ÙM9x µ:î}êÒýØEá £v[NH¼â\½ BŒ¤£Õ,bÇ¢FmeäŽ-Z°ihh~´fÿeDFR»¨«cÄHlßN…øùÁÎŽÚÖÛÖ—.Q!íÛÃߟr´h›7)‡¥%æ„âý2XJ†®! j G[¨ò èÓ‡º ‰»;µ4T¬–-qíÒº5¦N—‡–V˜â„9ÇÁª¤g…@ÐZtküݷƆ‹ uEë–¸~ ´±Áô锣U+J˜I¬­©±C‡bóf*¤×p*«ÙŸ±f <=É€´woÕúYÑZKCCó{AkößDNFDåîÑŠŠÈʱcèÒ… qv.^ó•–eeÊѹsq!¡:˜Ã1 fÅ%ßäÑä œ:BM†C2CÍfC$¢TÓÊŠ qr*NG @‘ňNàØ;¶C(¬˜I1 kð#wGÞ——Õ? !ï¨èŠd6І¹:”»--*„â³ÁàC€Ÿ¬ÙüðàõÐø¹é—.]Z·nÝ7>~üXPP0xð×ì¡Ñüĉ[¦û¯=óV,.äê6Ÿº|M=fÖÝí^S×Ì.d¨[ÎYÚº¶¬0éѤÑc®%KõóùpÓ’‰,gÿ…cë&„…¯=ÿð“Aû€K†¨×¹[ö¤YÛÔèSR¹­L\:½B¥kæßßî1aùë BËfð²ÅcôÙ‚Ss†ù®Ì<òêq£K6®7WÅÍÍ®Š)ûÑógì©›ýbQ ÿΛÉJª i7iç¯×)ͺêàgæR¦r*ÂÒ0ÔCôõt¸> _vM(³ÀϽü&År£Fö’)/Úé—VÚÀî:i~‹‚ °¨ŽK]mj]Û8aRÄ518cõ³ NÈ|2?`žÛi J rr,=«¡áSûÝ\:zJÄŽÎNµ`žØ¸hÅú³†Ý¦,žâLå,ãé¬qã=Ë•Ój ›DeC,‚ YfH¿VíííÁ˜´ b¸KÑÁQ¡(c´S¾açe;›ß²Øoöªøì·f£Ãvûöm ¥(x {GÄ|Âä…܆}È‚oȆvîlŒX®£—!•H~<ƹb×mÅP±ÂÝ“ÐäA˜Œžˆˆ†Éñ˜ós› R›“’’6lذbÅŠÌò{ÕÑæFþEøÉS,Ö(ÌL¸éA*ÇÙÑõO¾$Rfê4_z+~Œ…ì³½>&&Ýo~:Ý\³ñ¢`+Fã%j­\½BÌvkýÈoÞP·€V¦kûLªiÑi›f÷0«ÕËÂÀÓG¦>ŒhëÐ×áãîËkû•½¦(vv3ïèÌvìÅÖœ.„×öøvôŸßjÃN刋+[Q6×ßouj=CxïÉUâvkÍÖ\ƒ7ÓÌE£Ú[6^ùñÎ@òy?Ö9EPÝ@›Á(~d Î{­eÖÔT¾ŠX"aVf*tê)#ï̽80ê·ªhÃ)'WÑŸ²­WpÏ僧²‡ Õ7VÉÍ'ÞFt²Ÿ+õ0öjýÂëÖÚ¶Ò†ï&šæ¹·µ:ÙpíÇ»Ȭjǘõ¬£h5në}ÙDŽŒbáÒ_yê†GÞ~ovÜÔ=qv`Q•/®~ê*îñâàij\"ãÕ­ é6˶n²+w\šRpâት;OŽŠêFŽß3øÙ è«|›2ò…ÒC¶/™Bæ ^ ézª½L}@ƺw¹9b=Gýíh/ gÂÜ[ðq jîKç‹!.¬òˆ’}à›þšjн–½»þØnÙü+='J«QDzÈNl: s[Ê7¹¶¿€­ kS^Ç–¸d@ ˆy ØR¹ÉFgRؼCìk4DÑ"j·õ¨gèn𕲯™¬¬¬½{÷nÛ¶­†8o߾ݱc‡˜ê8Ð|##£ïÒìŒ7çÓä,êñŠ•„]ËÙ¦0v^RŽL óâ©dŽ´ŽŽöž± 5TU yîHõa±‰¼#¦-vý¦lýÿBkößIQ“"E59D™ VC²mÂËÀp4@©}+N´ú¶ÄÅ%UTŠ%ÑTI0ƒMŠIù3Æt+̃+Û1~ú´‡Ž¤UlhDý%õ°s²%ć7@hþƒ»”’Ó¤Z¨êä» Ym²•QV—)s¼ÔŒelŒøbß‹š.94 Zσ\4|ÇcHoÔ×WꚈ½Ž¢f-+ºÿ ŸêÖ­»uëÖÍ6mÚôòåË””” òܲe˰°°z%šª`°Ø‘kÙIfêÓ@âKey†ñï'È'ŸÉ`–ÿÚ!̃EØý›åtH˜U6U‘Nšôe~ëÑŽÍ`–}«‡+£[[§ü Y· ô걽ˋze2Àæ-™1¼\SrhZ{Þ‰‹WŸÀÚ´l8‘–ÅQRä~íî;O>½×ß°Ô{wóz²Ìª]Õ1Ûñôýãúå¤iáè6/tåâµ\óhéW2ñ ­Ù4`P-U—ª_ŠúƒEÉSÌŠC$ÎÃü>˜Ø ÷/¢¾<,}±ä‚ºàI²‰ÀàØöªÌu/lSgwÓ¯G«›¹xÔ§Îcç\„NĶhŒ°åèÚm]Ñ@ñ/Ðo}Lj®&˜Læ0 oÞ¼¹yóæ™3g:”ššZtTTÚ­ ùÙ(Ú«ç;ÄaPÄճђ‹~y³öÔ´QaþǸÄf^ßoÙ]Bʵ+Ð×ÒÖà"¯$ˆ%m¤Q'ö¾‚kùNA0ØEVkåšÉàÍã —VE}kvòÖÇ…LMI_WŠùµÊÊr Ý¾ÑÆ»§Õ¥µuË|žGöÊi6³Ýx›ð[Ã6ùD +Sùø›BVÚMl&ÇBuÈ72ãáÓh K#)´”ú¼ýQ£­¤ëÏf–æ”êñ™ES \©’pÝj—?êë—Ì6Ó.<ÌokÞÂÙÁqýýe¶^IZÚ€ :O¬êóò ­Ù4åI} A>ê´jÀ´7È~º)é|v½aÛ >>°nˆíO1»Ya‘q#w Ô†ZñÅdSÃô_Mn>LÚÂÄ^c1À A;00JYHn;»À*¤))rõ“¯k$ÁÅÅeÍš5W¯^ 9}ú4=+þï¡V·Ë°N3F6¶Z×ËLKú.ƒÕ0pÍøùMgÿ¯½»‹bkãþnïÒ ÒJH (¢ J*(‚˜Ø&¶Ø×ìÆ®](Š^»Q¥{Ùþf–ëóryϽ<“gfÙÿÔ9gã­ŸZr,Ýûa€ôn¼Þ¤JÿyÒè4 …B-X _­¦ÐÈÙä¯J"¡Ò¤Ós/÷žôÐkfŒ¥2ù4ò¦8ìÞûÝ4ηÇÒ#«‰ËÝìw™|sKs&)§.>sìv®u šlÃÅ+ÝFÌœêj2µ›mqj|ŽŠ¹­U·Î¡½¦Ü84UOøú|¼„éPC~ Ä~•¯ªãáÈÑrîçnè¹g¢‡t.UºóŸ­¦Þþüã½­›ôë¨Î9¼¤Oé‹î 7¯=ã:§ú²4*Ù=½lÛF‹W¸˜Ž îl<©s“¢÷orÔ,mmºz›Ïèè5=&r²>5ùV2Ð¥ºhlS¥uA³†lD{ût2°ȃ&¯çÕÏMàïâ¯xjU]jòËת:[‚gr#÷z«Cê“'B{KMPÐS*L}þäY2EQÎÐPëz¡ÿ.â/˜¸Œ¦~võZ6EûC¡2„ö%ïûÎ÷ƒþQ°å4ôuy6Š @[í«åÓÈ™åDTé[ÙÖ¤ä™õc°\¯¶lù¹¡´¶Æ½E0÷!¬Á¤gÝSKøzp3e ë=ä] Y¨eR&yžO+? zYô–}ªtT:ìm›_AcpØ@eCCC BÙžÝôa%úfЀ8³)N»ó„¡kÕÜ\û—œnP¥\¥ŠŠŠž?þ+JE5a©/>qÓlÑÜ]ÓEévž!3šÐ¬§¥gF؈ÎtZvë1°ÛƒèÛo÷ÝR»ì³óND„ï,tv×àÏ=Î'xI_K¶¬Ûڨɯæ_6,ûã¨NâÍ÷èž.Wççùò1­|$dMú>ÈsݵlaÏtYް(¢b6Í„]åÎxþ݃wôůæ.=2¹‡ €IàÕX¹Yó¶Nõ^/Ö±í8c¾(›üûÚ¬™aÃ:µ×mék©Î(» fkÌZ´5gúšÙÁsÚ 9¤o» ‰gŽê>ãlùÍqµf~»Ïú•ø·ÿÔÀ‚ŠYðªƒÁ•Jp<›ø¯Êf˜fó"¯VÙL½™]ŸÐØ0vŒÙ±ô=«Òx#oæÁðÍ0ÍúÓ’\.ù“À¥ä@>J%_Á¡õŒÝˆ@¾k#–.i2†Í%§ dm®°ã&]L(„kqUŠâ•@‰˜¼O‘À?"òŒA, ‚`Ë&x 6_®Òa7ă¿ï0hì­0k3—<å§“Õl"ß”î—|›| çÕ˜ÒÅPÂ_qVÐËŠ…d%5?™é·Žô#&ñ‰]M·¡–*P7ª“7 ›¹´ðÈ·2Õį¦úîÝÍSD™g]ÓókmßèÏ¿šñ‡‰Þ>¼rö%$äýó<½Ä^ã§ûG©¯0³ë")‰à ·"Àiy)üôÙnÙç„`ÿÐ7á”)àë £F‘×}Æ'OȃC8vŒÜm&“<¿Q–>*#â¼KÐÖ†Û·‰“îSñ±=tÍn‚»+ìÚÇû@ èÌY6™%ÑÁùÃgoD§¯·ƒêU«¶»bžJ¨ 5l¾€ª¢mréy•®%¹óãðØÕ'Dv–6AF€÷úu=]X×r–@nn•å--¡°¨¨ÈÜ©+ùß—d¦|ZrÌò¿ÊëFD” ”Na0 6¶Êê¡ÇA} l8C‡‚P– ­º±Ï…‡¶¶®þ»v%ÿ«<…ÈòÒ8—NQ¶¢Ê=Òa6üs(‘ÀÑ‚þÙÖ·Ÿ)§%¿~_Q]%§ÕÈ\ëÛ‹Õ ]…_Þ"ýƒ™]_•¶íÀÒÃ÷j»pí‹­Í”ʤÂð¥äDí÷ÀÜÎ\©2ãwíBU‚™BÕ ˜Ù!„PÝ€™BÕ ˜Ù!„PÝ€™¾Sþ{¸ûÒrÀº 4Ñûâf½‡’ í Eã:úšWÎkˆ}\ðî jÌ_\xvÜ~ y%`Õ¬¿°Pá‹cW Úú´ÿÂ|„P‚™¾Cô5¡—7 9áÕ[.ü•èl0Ð…íÀz1ì™òVÇ/n¸Û¨ö›öù5ù™^?¿ˆ¥”÷0p\τ֪¿¸p¶0îAŸ0˜ñ¬¾ÔÝ”lÎëåýN8¦ñ :ICýI˜Ùè;q”ÀØ K{™þr g*@6ÀòÃÙ¯÷Ák7𮡒wêóËyŠ-k« r“Yt6¿ ëÙÛ4s «k¦¥ .DƒA}É¿.uh,mL½öÕåjOFÌ ÈcCÿJá¥ö#òÓZÙö^÷æÈèÚõºŒú—ÂÌFÿn?ÕÄaôô‡Éàýù,Ñõu“ƒÏCÇA+G‡ÃלŒ|¥ØåʱÅ?ÝHSêM¿úV™Hù=÷ j×rkÏàE‹¹ú,vº<Õ¶òtl¡º3»>óaõ XM¶AÖy6¬è cz‘§àaÞУ¤s h¤½ÛÖ“‡ºÎ?ƒ6pbèÖºqïÌàÛR)0þ/È>;OÃvDsù âQ0z2ÜOYM˜s{–¯)‚ÈÅ0ã/øÀ%-ÈèQ+é2v^¡ ˜ KF×»Œ§àçÅðÂBU!1‰ìüZ¯uÿ´y5gäÀ ±ÙÌìU« ê`È^}–ØN„BØ4ˆM»Þ9¢Öªmð6¼ƒaÝ$²ÑRÇ¢ý#Ïá°e<[=à˜ `Ó¢¶–-Vò &­‚ýWm @[}x¾ÚM„;¸:æN†è°êûWfùiʾP˜»>‚^KX±Ú5*›.)€Ð!°éˆ BI¥O•t†‚;) j wB/³òšc\_x}Ì­6r—pѯÝÐÆKo®÷Ó©í/!ô/€™]ŸP™0aÐ|a hï4eèØôÇÃ4rî?@ñ„}û` Ì·ƒ%óÀKzŒƒvCÀ5&Ï‚žîpstm·¥Ön®ª7>£ý`;Ì.^ ¸Kþ£{¹,†ÀîÇp¹ d\ƒ&®pé\]@ÎÚ0Fý Ÿ‚‡´áÇV²O0‚à´m…Ã!ã8ÀÐ5 !ìÿÅçÜêVp8t†Ã¢˜øù³jþZí±QôÁk#Ã[mÔjd¾´_«qªŸÞÙë<1þ)LÙÛÜ¡×$pºSWÞÑƇAÌX7Ü\À¿Ù-Ê8{Ø¡‰™ *^jà^ÖÂ&.t:ùÏʶ”%ýq:÷ƒ6ž0zŒ „›§Àb(Ì{+aû8:\€psƒ5½„ÒÖñàQ"˜2áÌBð4‚]oÀß ‡<0i]!!—|@‘¶tÊ[Lͼ MaÑ]8oW惻9&ÀІ¥3Ym= Eç܈Énã¥ò1>æq»û;îaf#T·`f×?c#!Ɔޖ°1N.›^XD\}B¿`å@Ž=€1{`É_`(ûö,¿PëÌ&“Í‘v ýœÈÑ÷10f+$˜ `h´ „^Ò÷¦Ô]`Ïh·®‚›¦ï†©gʤÿBK/$èLH] 1†UnÐû4¬ å/¿]TBþäæ—™ŸÍcú…h½©µ­NA/½Ø>v? yãQ†¡ƒØ\ ±ž6,› dç!=áùQx8Â¥½÷q‡íêð.Nº;CX6·(07ÌÇAÂJ(á‘ÝxòÉŽ¶? šÍ¥7)¨G¡säpA…#íòTfÙ§ˆÜß —ãÉeâOÀú0ö.Ø„ãÁo  þÑp:®ÐáÐ(}O\\é*{ÛpPé#ìÈa· Ѓˆ0tMÙ\u3-ñÞ¿‰{©;?yÁIÅÞ­¿E„пfv=ÄUGÁ¡x$C^,T¹Û-¿lÅ G K' €& á÷oKÂò™4r”̘pS ½t>ýë³÷Øo“ +rµ+Pæý+òg#Y J_èâ¥%Äß¿Ke´-Û’›á§7ëÔU™É–vëÚe[¤?‰à–“>OàÈ·¨|5ÈÙù!! ´a:•\IÄ#çæ j~BÌ#æJ5[Ä™Ÿú!Ö¬è`YVDÒ’ŸÄ‘æNw¶e¡Q\%OxŽÝ!»z±Ö¯zx¤›<úâŸÌQièb(âƒ~!¹‡¥o‹s”‰Í ²ˆ-ÐYJŽmÛþð¡Cý)˜Ùõ’º1´n{ïÁÙ$ènðß¼ôD€ÊønLio˜|!f¹Ç®©NŸXKr³¾ö û0õ§¬[ýc«§7D²žæBǪÓã~z§xÒóN¥ÃÀ¤’ù\D!ШPãëßÄy…˸ºî …ÒÈ"$"ÑOïBèÁÌ®—víÈ„±CÁá èþŸëí°Ü|AÙÓèÌ—Äu=ÙYµJ#ò´,n +)4ȆGbhñuÌ~G-+)%ò­ö‹¡£õ/.YK…<0ïòˆ³+é¸2K@¬ºtÐb_U~i¥Wí&t¸ór \ÓïSXHÞ`ËÈýp…B—Ë•H$ÄÄŠŸ***Mš|©&8Bè·ÃÌ®ž/…µJp·Œ5¶#,?+û“ÓK¿Ë+¾Ñ+µ±÷ù7ýg¡™Âî%€Òf>6nehnÊz`°æoèÓÄäÓðOµ‘LìÈ»n‡€DLÆ”„"½vü2Št.WúT[,ê÷í0å Ãã–ÖÐPvL„y—@NLî)±:MºéMiqÙ.|^Â×wÅÑtdàÀa$G‹áþS° e€A`õ8ù74í4 ”^6—–ØŽí àkB<â ÉO+ßRvbqb¤ÚÀ€8qs=K6²¶Td–íßÛ·o=<<¾~LÂÃÃ1³úƒ0³ëQ!Dm†^‹aw49ÊW…þưj0 ½#<¾B¾:s¼m@9®=!—9znðü<܉ ÎÿM5áÎuHøç2œÕ†v­j~s›—g®‘‘{ë¼w™wpý9ýìUÐu™—áqW= ù8Î¥À”ÈoeØ´z‹«ÐÆ 45à#@úy8ß|Ñ}N0„MË3àh ²ràÐ zº|­}4=W°P‚eÝ ›Xr*8«Õæ ¥?‚Û/>Àñ«ÐÓnŸ‡ï!é\v„æF} 2b/Â#g°±…Óaâr0o žÍAF ]!¨+˜ØƒÌRðìmÛÀH/8}–,6êèôJœ} Dgb #¸ù¼wpê ôi O.À?ĆnÂÕ§àj ‘ËaìJýÊÀÝóîp1”,§é} †{^ÐP dSÈs…èHø`îÁÈxèd>@W‘|ìÝq¸•¿ððzÈ(Û:Bîíõo9촌e-ÙÆÇÇOŸ>}åÊ•%%%_:8¬Í1Dý&˜Ùõ •¶ ¾?¨JÓ‹¥sÏÂt¨ë€š"(Á‹Q@£ƒmaåßš à0ÉÚ{ „òâQ^¬àékPRù/^0ÒÙà4^Ž&Ÿ[+3€¦³vA¨Ø äëÔt8xRR+ªLk e+:‡G¾™*(iÀè~Éeuòßê¨Åà7r É›ê9ÐÐüFƒ¦tK¸ò˜¬àLgŽr-’¼>¬:+Àæ›-±r ìz,«Pä•Cxñh4‰š ùÉÛO†þ™ ‘„©KQW"o Øt—o œF Êß@I»áT9æxr$e ‘ñó"aƒü(LªÄÈš²é‰D¬DQ”6ÚÖr$\è ²$BJ§ £Tþ² E2?Š2" >‡VC †„D䉃!Y}Ž25•|ìMQ9EÐd”ߦ½Úr+A±Õ*_Cr̦ëâ){†¿ùPñ†Z©… úùùµoß>++ëó#Ó¼ysMÍßÒÂ+B¨–0³ë ôŒÀÚ’’ $fÍ‚FJä ÍêêäÏ£G¡£ômªŒ 2b%ˆ‹ƒFÒ^@R³ iS²±Rb[ôdaÿ~²¾ o³ /·†m±åA§Q¥DW㪽X0å ‘iM{I%Ï!Ô+ê «Tª¨E-}Ъ´¬ ÞåÔ|Ó›8iÐmªzð­|sTÀdâ@8yZµ‚¨(rƒDðÙÚÀÛ·0a‚ÞܹäB<>(i’uÂRñõU!R63 T5Ƀöò%ÕÔ”8Ìðô)ÈØ…bH4eéCd$q¡*G\ÿ¦¥‘ÍÚÏŸ ›6™Äƪë‚:q¼ÜÝáþ}èÝ6nTÔEUèëC^¬^ C‡’…RÔÔôD"¸u ìíÉTø4¤í­¤¦2´´ Œ._ëöä&23C¶þòòè®+©–{ï/m †!¯Õ¸¡yѽ>ðMsfff—.]Nž<)‘Tn¯´´´=z¤«««¨¨H§ãWBþáÕ'D0ßûD$ßæÄ×ú°ad¥%6òóɹ›7“s‰¯i•²Ç¤³gCr29`l\6eâIJ…ÝÜ ¸²NCà©©†´™ úíÿ¾²¯ÃŒÈšÞ3—€º%„M‚èJ«_?˜1vî$"†'wî‹W¯–4b8Wz¦QvЈT.=DsæÀ»wä@£FÕš‹‹´F6@` ðùä9Ð!°d ˆd±Ä"×ÕÔàÑ#rCD±D4»A8tÈ)rrÒzcK—† 䀶vYå³)SÈ“*B³fdù¢Øñã“ÏŒ]}ŵsËúöâž^:/ÁaÒÞî•Oª8~üøŽ;&L˜Gœ.H!=pà@bzFFFqqqãÆ}||,--¿ÿø"„~fv}Âd’Á,½Â#¯çˆov"äË;i$ò©Gr@¹ü62qM™M)^jÅŠ²¬*]Fµ#쯡-oR¯­ýrš¾°×ç ó(ðc˜A¨ ½QOd!ñy…B2¡YÒf®®äý" +‘£Ýº‘JJeSˆÌþúA#ÍDN ­(½÷@ÄðªUdÊ¿Yé-pX¾œŒ|ÙòêóᦴFµbùíŠI“ªo(<œœBùŠ mØI»Cs6ž2’­ø}°Û‡û°Ø_?<ƒ òõõ%‚966–mÙ²%1ܽ{w>Ÿ_RR=eÊ”{÷î‘RTüRW ¡_ 3»>!¾Íe*Õì­ˆ™ ÊŸ=ô­ÈƒŠª,CùÞ—±µß° Ÿ†åäªÏý<œ¾û ™ÍÕ¦¿™ª•®+ïF-7ôù"–úïÛPm! “]«®WTUUoݺEDòÖ­[YÒ¦”‘ß^^^úúúîîîÍ›7700PWW¯Má¡€™úšµk×öîÝ[NNŽòYí>"¼GI¥¤¤ô¡Šÿ­»|sy‰8jñÈ´ÀãӋ׊ýDfs3^®Ÿâ?mïãùySÌ´”ÿ ÷ÙróvGvݺàlP_ðÁÌFýQ(Ö AƒYR÷ïßß±cÇâÅ‹íììJ«zW%,å&k®$¸µé¾;sèøÁZ èÚù»šF+N½·CÚ‰¸ï²Ë%Tƒá=¾¿ÿF\o㊦d%Ï÷ï}X#£ÝòÀµí͈¯Ì![Φ¿Õ›}{ñÕë~G;­T–ƒï„‡zÝI¾½4HD…g÷Ÿr]·HÁPܨ¢øNñ ⓲ŒŒ 9êfÁS»†l¿'®Í†ÿüÄ©JÆfÊd¿ b¡@ÍÀ€8z§þ0³Bu@3)@ÿòå˘˜˜èU¬Û–c£oôŸêó*‚~sãð˦§Žõª˜›—S¬Ûw¼îú¥Û/ÞömìV:QÂËÙyýã+Ùùi¢Â|²oXB!WHÌ)&F[Ûê :µ–¹I¡Óòß+)…*#'ÿí>“={ô “ kÉ.Ù#í:Äû9 Ô®­<á†é³n-—Þ ‘µ™z÷ÖïݱÌl„PÁ`0̤~p}j£™{"þnà:É»ÓÓm3W\Ю|Y̧èÌ´±×’Ãé£Ü4¤’óæDŠ\³> ΊS¿oS÷Wù´˜e·h»ï£áok{žŒ=×Q—œÅK‰4bdäƒl±HlÕ%t×Ú@]ig{¼Ô»S‡ÝóAEMQ”òQ®¼î[qrìÄAƒ?/b0”‡¯;<Ï·RÇói§{v>^Ú.PÔY°ïæ°Ö2ÑëÇŒ\ò7WÌ«9®Ü±ÑÏZ“÷æÒàž>û“öìYxzfÀ[ó5Ñ;ËzO¼5¨ëÈ• 4ŽòÎy㮥Íe›wv³ïæÐÓO2›[wam 9^ìî²3“/‘·è¶óàÆVZÕïÁ'žžß7hs*WPB7Z¹%¨³5-?a¼w˵7s]ü<ÿ}>W¬°t÷ª1íd¨ûøðÿ 7>ˆòF3÷ÓR5çòŠö}ƒß:M>ÚK9dú†ö‹/Îhy»uˉi² %yÓvnŸÒöèhϰ£ÿ]P?bÕîÔÞ‰WŸ(8òä¶Ÿ•*±¯­é7zéË\A‰DuМ óÚ°…k7·÷U×ûwOÛ“dØaÊ¥C‹ÖÙ[é˜Ù¡zDÓ®ïüà}ý—w‹yb™Eõ^Ú$BŠËð¹*;‚B//ÞâA^³žžf;á±ìŠ…ß»¡f_PÚ“Lé½øèˆ°Çì;̘´Ýcÿ`fIêXo¯÷CÎ}ˆrÈ^à¨Õ¬sγÓÓÕÄ™S;¹^uÚžwšì÷Á`M;¥û”;»‡×;¿Y—\ o„«´qT‹Ïר|3 |"c\È)4‹Nœ`CöÔ÷d©[»pJlÒ»¦²ðæðäÆŽ$Ýïbì±ou×ý®g®?ãX4ÿâ5vÅE¼õÐë¯DLVYõhõrü1G,Qh¾ñâËœ[]ÛÏþ;¤O_SåÔã!}îKÎì£ Çü» P{µP­RÏõiÇ'št=¸õMê@#¼9lÕÔvåEHóFkVOØÐ|žsPäÕ#òo.,wöôU³z½°i^¯ƒ®™ÝY'yGo}GÝôW]܃"ì4œÛiê¤y!ŠT_(¡3f4’¥!¥ 's‹bâÐæ•>¥+”½:Î;}øDžÁøñvÒ]Rv9ĠߦkDfC1W$ý­u5‰S¶A]ÔÇd|÷oó_3!Tä>?kôöÎ,ëóÆm¦——öóNü(yÿ ˜¥¡Ì,ûîei›ÑŠoääñK c YÚZ²ìj%ð?>̓ø±ígÈÐ( á«Y+~ùÁoARd%ƒF£ŠW4 EÏRˆóv•*„ßøðÿj˜Ù¡úãÍܱsýž3°Wß9õr×ÐamSf´`U[È}R€áÒsÛ‡t,Òð¼Mí¾$?œ‰Œ³ïé¢öÍ鲪À{!—e9ðóD@g2iT¶ðùŠéRDÐÐ8Š"0ßø÷Ýv5W KŽ-¦ øÜ²Q± XÄç¨È}c-2Ͼú…Æ”ˆÀ&ìÞ5/@c°il1¯¨¼@ _P Ê5l—Ç+uy&SèžÿZM6^L…®N²†¢sùþâ¯,L¡²˜²PR\1A((EÙÿ^Âý÷>BÕH°Ó×ý™ïù•-É·½»„¬lwÄuA{_¯çí«½&­Þ}}ïý/f…ï¬å»J£Â#…zº|c1"eùØÁº§¹%Í´ÈVb’ÏC½†ztŽÈÛD²îjrŽ“´ª1µü-jº®[K˜|êÉëvjñ–iÓÇ’±âéåh)O\|¦¾KLå·Øì›{EV­ûJjSä›5j'ׯH8Õ¸Mͳ‰Áš§ÞCwòµnüÃø6£›—^±!1÷UL¢Ûxku-–žpÝ8îÄÆ_n*'ûÍ+!t7#ï¶ ùÂ*'(ŸUA§²[XZÃîÓ¯ Pú†ž$!æžE·ÿ½„ûï}"„ª®0õùµÝaƒ_w~²C·´õ,¡œÉ´žÍ/,¸0zèŠu3ý-Ôó½yŸùøu’†¾n»ÅËLœêÓÂÄÂô´WOÓŠ  çÙÃWÖÎ&´œ7³€_üâVLtŠ@,‘Pâ «n0(TÞbqzBZ–8'ñevACåc|–$“š–/4Si:ßyÒ¤eÚÓ;ÉdÝZ–7rþ¾¦ŠDÊ¸Ï j6À¯§`þk Úí¸üâ¼”7iÅÚ šÏ\ÔÁ·_7ÑÌ`#âòÕ¶kOËŠ*]n“GiÅ ÿôÑ+-FC#í ‡7l¾|»m;S™ë;WˆÛ®ÚàÆ/.>Š Þ¼H*T×—£U ^={ógklß§jnîbô>(49á½ÐLóÃë—éEâ¤WñÅM»q9;¤[³Þ3&vÑ`»™Wk[Ö§è¤û,\ØéJŸ‰ó"Ç{¼^{Ž2ä\/“ò¹Â„{÷þù Lºþ×IÚÀ3ÝLÙ²:†Y9¸¤ÏÓT‹%²ZùuÑæÄ½Íf¦>K¶o¢Çlк³‘ʱéƒ,ÒÛ—$Ä|”<{ôÚ³ƒnCSÖ®°®c\ì,Í ÞÅåAþ›çÏÓ œF :à3"hgX‹¼ÛÖĵݶޅ8MÈJ~WÄ…oâøF¹)/RòA˜˜”Ud0ɬÁNƒ…ù7¦Õ­WÈ1³Bÿ}Ââ|¡I·#+ä‹ÓrŪr4b ·å6é¨Ýx‘ò󹹦óðµ"ZZfŽ.°ôž?ꢭL•ˆ…éé†ÁûŽR)ʈM„ùÙò~óv‹ï?|²Q¡ÐrÖNU.…"Úày‡û0å3‹„jüü¦ Žm¤ÉPyGAšÏïvôX•\¾H/–™zèM…^$]ß Ñ7¢cŸ¦ç—Kͨ¡£Úµ®¢ë‰Ø[Wo<ÈH{Ï0rõ²“ž|ÅÍ|*//+ƒVH1rårCe&3hõ9ç¾±oR󄦮™ Dy™Êk;³ÅÜbi#)Fë®^ê|óEMÉräR6^IÄ ”Lƒ™[¢Ý¾RX,JIªÈxó¡­2JP$€œÍö+·c®ßIOIkúýãdª)C\òÊÙí:z„ª˜_Àçòýw…<Š|Ï‘]¶Ï»‘Rñ¨¿ŽÀÌFý÷);t6®2…­¤ÓÊC§Ò}¯N¶•F™æÒ›¬T¦QSW£¦•æµô6úö í:Ú•YxêWº³Má˜;zÕÐ6(KÑÎͧb3ûò!šŒ…£§…cM›¡È[·ñ´®:ÓÀ²C§* ÎPå4Z¸ù¶øÂ®ÒeuÜ;—·-cìY±£FÍ]ŒšZŒ¡ÞØÃ÷«§`èêmXÓ ª¹»§wµS¶js׎Í+OÑ0qí`RyKÇÖ·Gù/E¯Wé-~y“æ]LÊÖk _õ7!£íäÕ¥Ê ÝÀÆÕÀ¦lLÕ¶•aYyV¯Vu+°3!„ÐïÃd‘WÛÌo½¨þÆ>üdÖÎyzG¾f6B¡ßB›8Ö&Ìë3ÒhC˜‡é·ß«ÿÿ`h¶™îU¥ºZ™Bè·`(5œwöý¼?½Ÿ£Ðeª×ð«#0³B¡º3!„ª0³B¡º3!TŸH¾ÑLçŸÜ‰_·oÿŠO‰~Ìl„P= .Ùä19aPòÉaµh¶û·x³uvà´ãÉvwsÎT©œ-L›Þ¡e¤Á†7¿«À¢´'k¦øÏ<ôzùã¢qU*6¥èª‡ž—Þâ‡;†×Pü×åÝ·nŤÙÙ­ç\:Z½‹Sôë`f#„ꉄ®®kðQÂøS™mì4t^×í– «÷ë)± Íõé%|fkV%äæ¾IÍol¤/Û ÉÔ)>Óö,ÞS(¯`e¯FáŠ>k›û7 )¶÷ŸN?8{ OôØZ}†™ªhœ¾¡û‚´SÆÏzÈ;¶ø¤Ì°I^ª?µ‰šJ®†A¯)@Yº³¶œ›Uëí¼¿0}â­Vgõ'GJÄúçQ¯ây,Ú³ÖEþt0²3Ìl„Ð_ÎÃuÞ>A±%£s2WýaK Ï8mïS&µ(515×4(홪èùÁ6n½ï°šÌîytíêÇ9Cv\Ý:l¶ôÃÕÕýÇ,‹Ër%*f¯æÌlí°ùÁ°ó¯¬ö÷žxÉ-3i™0fsÏÁ³žä]Å&üà‘Ödg”¼„e.=ñJ^E2’˜´êÅ›ÓÉoî+ƒ¥…1.þ­œ%Ê/¾µfÁÍ$ïÂcǦyWnùãÁÆÑ]ÇlHbìRÝ8-$:yÙ§6åéÉuí7Í:÷<ÇqÌ–ó+‡ÈA‡Mßo?þÜ…Už4÷ôÊ‘cÿº&–óô>¸Þ R[á™§Cíýæµ0ÓŒ»ró¡d¾Á´£§ãÖM ßù,ƒß+EõþšnVCÃæÏ‹ô¹u©·ÿäÁÅ ÞGwôœì{îý•Ö²/–¶¶hٶчØ6Š‚Í×K&$g_—HšÛ¦Ñ’êûÖØkæÄÇÆ¼¡1”íî@iq@Ƹӡ^›á1bjì¸N²Ÿ.ß›Ž\·òÒŽE&Ûî,ìIŽß“ˆiê­·^}•»Á¹ÓÌó“zûé+÷ž¶æì’ýïhTbͼW;ÖfÞO~« ’½ëQªÞHWóYpiý)£U…¦¡³¯ž¾sBϹ¾]Ö^>râîôW‡ÆxÏY1c¨{cñó®.=´þzšÖ«!@rˆ¥~»BÁÍõC˜ù/û¹û乜Ýš(êŠ/#P@–.,Lұ܂ûY}L>¡åâÐ 5©/¦ö¯€™ª„\ž üejѵýw OV#o)‹Ì›—¬=ãÍŠŠ„  lckÖ@\Û´U•ðøÈÉ\ƒqãšIŸƒ+µ1¤aŸ×ˆÌ†®ˆMzŒtw§¼gg¦>¥ûÝjMöºe> ;{òÌ›÷¸mìÏ/>Qx²§ŒtÃèB¾§ÙÑ[›²ùÔ™´¾C0´Ìl„P}C3µPÍz’šË–$õý;0²«Üפҕ¨D$þ˜õ4úË”OQÐ0?O!’”]ia–,«0ë-ä_mÓü1¬h%2if«)Kƒ”;É ¤¡¬ò=»')-Sòù›e_\¡ü'…¸Š®¾š¢Ù°°ûçwЛ¬Ütîêõ“z:p>ªNlSL˜Ò'òä t…¥P Òž€¢ŽJù=z¶¶|¸YÀFò]‰jC-fõâ¸é/øp§‡C3qZA‘¨ZX6”Ǫg¿f6B¨ÞqYyÜË«GàqMµ%OŸÑ6ï§ð¥E©TSJŠ+& $ûyït&{ߺ»¡JVñÄ’"¡°ÆËëÿ C1d罞“b.ŸÜ5±¯s¶ÎÇUm”¿§ CFxÅ‚ò“ ?8,¨,9àq…Ÿ\ÐØŠ­Ç^2áT/ ý$Ìl„P½Ãûp·@¥ó房´éB*AûÊE ZXÚÀŽÓq0JÚײ$þÆ]‹n¡ŸWk`ØR)oÞþô ƒ5*MmÔÞBoǽáªNö­ËkCš”Ú—Tðzÿ•ÜŽì56yxéȵ—yð=™- 5L=õ '=.„Òõ’Î^à4õÔT–‰—FᘠÈ(¡RÊ®ñeõ]LaIä‹ìÐfßuƒ}f6B迯(5îÕ»K}œøÞ¡¡Îë.\It±¹"CÞ¼eèÚùNŸ?ÃU^Äg@AÁ›·Ù-•Y‰ñIEûúyúÈaÁCt1aûüÞyw¬}ÕnëzˆÒß¾-äBÚË'96FÊò2 [tæ·)ÀªuÒœáU躲k§ZÌfá+½]¦*Nle ûò~Š˜«ô2!׬¡µ1>“×LË9d¢ã7ìÙcíu8BÓ±[7#¼æþ0³Bÿ}"Ϻǔã>Ù¹\}¶óB½^œêì¯@Ñ ?rÊnÝáeÌ$±íÜc»)ªŒ08"9+òrŠe¬¶^޽uãnúÇ4±N·¿cZ™hÈ€DÈ•·ßsüE®(¿D¤$YÃ%».w‹½›”QL9ƒÆMT¤Õ¦[;úÄ!úÉ»l*[£[Øî&#sTŠrÅ Tñ¸¤HÔ>tƒ»ÎûøAh<öø‰@ùB ÈÑÔ,7=Ì¡•ð$P9³[Ž;q©Il†PFQZ¤ëüDY•|¡XEÌP›wì(C…Z,åäÈ>A¡Ë”([ÌûÏÇü>UkÙÑS- ªôB).ÉÑj¿ñ¨["ùbeßÐí„ÊÅÙB!["gàHEhòkÎ8ݼÿá#0í¶ÅŒ²3’Öeg©LÙpÅíVlbf KÅhÜ®3nï…T>hòÃGÙw½õ:5WLcë›Xkc`ÿ"˜Ù¡ÿ>…†ÍÚ6¬Ë r¨¾:`ùÒqëøMG–¿(`*¶pó©XÈÖ¥mŧc;ß*%Rè¶.¶U¦¬VKŸ–Õ¶Me™9´3s(³°«¾o[¶o\Ã>[´r·¨é³0åõÜ;é•4ò´,ŸnÔÜÙ¨yÙ°Z3gÓfk(¶ò¬ºó•KS7qñúÔði+ÏOoãµp®ôfMÞºMëÏV§Èj´lÛ©â#›ÚW”«hÛ¦½ígË£Ÿ„™ªo>¾øj".€4³ ž/xdÝ7¬á·VCèÃÌFÕ7Û.¯Ô®é"6‹*Ò”]&;àŽµ‘пf6B¨ÞiàxòvàŸÞ „¾f6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„Buf6B!T7`f#„BuïÌl…_XB!T§Q©Ô_[à¯Ìl{{{ƒñ Dý€ââb&“I§ã]4„þ$"°ãããm™?õW-‘H*&$$üÜÎ „Bè‹~*³ÓÓÓ‰Ÿ ¥Zx#„B¨”%%%"‘è'Ëù©ÌÖÔÔÌl„Bè˨TªX,f³Ù4í'‹ú©ÌÖÕÕ522"8Î/ÒŽú1Ä·¾\‚пqM[TT$''gllÌd2²´ŸÊl???WWW¡PH§Ó‰«íŸÜ„Ð/Áçó‰?IëÓ§»»»‘ušu“Å­ßúÛ+© ,ÎÎÎNÃÙ³gÏ•+W¡B…J–,Y]ÇÉÉ)yË—™ö~@&@Ö–cÇŽýøãM›6M놤ªíÛ·þùç×®]3\ìîÝ»‹tÊ—/ïïïÿÞ{ï(={ölI‡¡¡¡Š]¹reìØ±Ó§OŸ0a‚´ÁÊÊ*©Ïě쵎¬À›7oÊÇT&J,þðÃ{öì)¯¦=AÒ•Ôéåb޽_:A7!¤!XsƌӤIÄ‹ˆˆˆ|ûí·±¦KÒòððÈ™3§ Ö¿tzúôé÷ßÞ¼y}ûö·ÚÖ­[ÿúë¯úe•æÈ‘#wîÜ2pïÞ½‡ªu>þ|È!»víZ¿~½³³sR?…¦6Yxxø:*T˜?~µjÕÒºE@†d¦½™‚5çüùóëÖ­kÛ¶­ ë´²²Ú¹s§ñå *¤K44\¸^½zÉ[Šìâ[´h±mÛ6uJöìÙ{õêõÑGU¯^ÝÚÚZþêÕ«Ý»wKÉ7>}úT™()6ÞjCCCëׯ¯ßE¡dÉ’rÀeyzzªïß¿¿yóæo¾ùæÂ… Ê Í2£|ÞlÙ²ÿ),̳É,’²>õ·— )kûÙ³gr$>yòäñãÇe@}÷Ô©Sï¼óÎäÉ“‡žò¥šb¦½É‚µhܸq­Zµ²±1ÙÖ·´´üàƒ’7¯ñ3&u)¾¾¾úÇ€Ž?ÞÕÕ5nÉ,Y²4ÖùJgúôér`H¨ÚÞ½{« X‚Ý„ dA¶¶¶±ŠåÊ•KJvëÖmÆŒcÆŒ‰ŽŽ–‰2cŸ>}V­Zeü§P˜|“Y¤l«™iq‡–Ó† 6¼~ýÚB×—QÖ­œuLš4)UÚdfÚû™ !X+äÔ¿bÅŠJÏËÀÀÀ+VtíÚ5­eFÛ·oŸ9s¦2,QU¢UÏž=+[¶lr¨hÓ¦MëÖ­ã-°víÚï¿ÿ^­véÒ¥:u2P¡„ãQ£FåË—ÏÇÇGÉÁ«W¯þøãe‰6Fk›L(?Œ:t¨¬õ·†~~~eË–mÙ²eÚ¶ È(Ì´÷2B°†H˜¨_¿¾2|øÖ­[cÕ&AdÚ´i±þåÊ•/¾øBV`¬Û`É7Äßß_ý?e˜I¶BÜróæÍaÆýðñ>H¾|ù&L˜Ð¥K•¨¢¢¢âÝ"îöm€9ö~úLõ“5¶téRù}òäÉ„ÊÈùàƒ|||ôCy²W`²—ˆLŒ¬-5kÖlذáÏ?ÿ,ÃAAAräöõõMëF™ØeeXNë5jd’j/\¸  çÌ™37î•Y$1[èŽë/^,]ºt¢sia“%¤[·n\ž½wïÞɸsmz¶ÿ~uXŽú7J‰ƒªÃg“ñ÷S‰J2ãwß}§VhL¶ÐÀ&Kˆ“““œ¨¿DÜ»woÜÕ¹sg‰/úsçέܮîÆJ†/_¾”È"G¸Ï>û,V%åË—/S¦Ì¹sç,t÷I=þüÛo¿m¸mÛ¶mSûÆÈÁ5{öìæh˜ñ$>Ö­[÷úõëêKKË (ç]ò®zUìðáÃrfõûï¿çϟߘšeµK¬Q.|JÞÞÞRçÇõë¼zõê{ï½÷çŸÊ»†k“¯}‹-”<íààP°`AGGG©Jÿž\ò…—ÍѪU«Í›7Ë:Q.²ª…oݺ%F-,·\¹r’Ìâ]œù¶‚œ’Ég%gÉ’EÚ&¯Rá£GÔ2’¥mr.gL…&a¦½ŸÂTß19¬W¯žòßMåâââåå%ÛWVéÓ§Oå 'ÖÅõ”Hý%"£ kŽïå ´iÓ& ]çTÿ‰'¦u£LIÿ0P¹reSU«‚“]­Ì¨‚üÁJ¦ßdHêUCð©S§â7nœq²fÍ:dÈ®]»ÊY™"GµŒ5JÙ|2*ë¼R¥JÅ‹UD¥Áƒ+Ã+V¬•l¸aRF^ó5ÌõÚ¶m«¦++« 4HŽñÊ (_ýõܹs•˜ríÚ5‰zûöíK´ßª”÷ññ‘,qJ>‚T«vš”0:kÖ¬Ù³g+ÑA2„deùßgàïàR[»ví$K%“'O–Щôí‘é»víêÞ½»Ô©”:t¨|í;uê$•çË—O ˵ðž={$YʇR 8PÎCâ |fÚ Ò†6mÚHx’ª¦L™Ò¼ysÉèJ Û·o—JÔ^¹#GŽ”U­ßWD½3z²ï€n€™ö~&ýŽ-X°@?ʦ”ªbvʺ=s挜ü¯_¿>ÖìÉX)\"21B°I„Ú¼y³rè’ØìäT>%Ê^o÷îÝF.¤“’ŦÞZËB÷»sT[±bÅäU¢?£~…‰2ù&³0z«™{{V¥Ju8î£_%ÁHQ†%*ÉѱD‰úäèøî»ïÊ‘XâÔš5k,t·ø°»cÇŽXUÉ{øðáÊõN‰ÝÓ¦M3píÁƒr°T†===?üðCó5ÌsæÌ9|ø°2,ÍÞ°aÃ'Ÿ|¢_@²š¤U9£ ª|‹$-ÍŸ??ÑßYJa ¦¶¶¶[¶lùøãõß’ô#¡çwÞùôÓO%!YèÎë.\Ø«W/õ•,YRRlîܹÕé’›•çÈ”)SF¹H,ÅêÔ©óüùóR¥JIaý?¬+7œV ‡……)…ûí·>ú(ÖâÌ·äƒHæ“%mË™3§~…Mš4)X° üOWz_oݺUÿI7qï–mÂ;v›iïgaÒïØºuëÔa9K‰·7‹œ6(7Lœ0a•+WôßJÆ Lᑉ‚µHBíÛ·_¹r¥…®?ÜÔ©S½îe˜ìòÔ“òD%´2õÔº'V˜£Zýãw’è?RN¿ÂD™|“Y½Õ̽½ Óß‚wïÞõ®œ(‡[›mÛ¶ÅŠ8*y÷»ï¾;~üx`` ŒJ~•¬«°‡‡GÆ úé' ]ßë;wÊhB­’”¬þHKÒsÜß™°a‰’*çEêè˜1cb¥UëÖ­Ïœ9#_etæÌ™}ûö5æ& b%`•,kÔ¨Qj^©³gÏž.ÛÙÙmܸ1ÞÿAÅŠóññQó{çÎ{{{)ïÿâ¢E‹vîÜY-üóÏ?Ç ÁfÝ Ò6ý¬’p,©zÉ’%ʨt“?î1!fÚû™ö;¦ŸÔ= “ïRRÿGÄ•úKDFAÖ(‰5k×®U®U|óÍ7ƒ6ÕïÓÓœþaÀ„w–5IµúOlJR¶ÈÔ›Ì777uøùóçúo]ºtIýÃh‡Ê•+g ‰,²Æz÷îm¡»¾eË–¸·Ž’ࢄ` Ýíð „`Ã}!LÞ0Ã$c©½$÷þݤÄ gʯ3¯_¿.ílРáúåk6dȤÁF•N½üñ‡µiÓ¦T©R ½Û¤I5× ÉŽrú—Pá¦M›ª…OŸ>ë]soi›^’Õ,¡ÐpU&d¦½Ÿi¿c/_¾T‡œœLÕHR‰È(ÁU°`Á®]»*‡W¯^ùùùI®JëF™Fhh¨2 'ôÆ<"5«ÕŸQ­ÐH™x“ ¿Æ”g)«Ô "Þ›3Ä¢Ù[¿‡·ªQ£FîîîÊ›$ ?yòD?‚«Îê(Ã+VŒûëF“7Ì0å6Òêâ”Ω Éš5«¤·¹sçªó&‚¥NÃwÌ’%‹Ô©Þ@ð÷ß7‚[µje ªX+ÓðcõôÚ?Xeî­`¸meÊ”Q‡ÿùçŸDk33íýLûË›7¯Ú·XNìãíRoZ©¿Dd„`í=zôòåË•Nurâ>lØ0‰YÉ«J½kfz-[6åV 111/_¾4Õ]uõ«}ñâEò0ú·…’ “:» 7™E:Ûj Ñ¿úkoo¯ÿÖÔacziëÿP]½”>åNgÊÁ;<<|ýúõñvo•M Ç{45yà ;räˆ:üÞ{ï%Zþý÷ßWŠÚËÓ€ºuëSF ÁG5PÒð ‘“ýQýZõ ?{ö,Ö»æÞ †Û¦ßM"nÛÌÇL{?Ó~Ç>üðà (Ã=zô ìÓ§YŸR‘úKDFAÖ.ÙÈŽ@¹³zDDÄ„ ôîWöìÙÕûáË€©úÕÊQ-y!XÿFýqoª•¨ÌºÉ Pï`eçï˜úI%©+3¡¾(jÕƒ÷Š+â†`9mP~Ae‘ðíÍÑ0ô/‚ê_€LHÙ²eÕaõoÜ$z·¸XËýûï¿ ”Œ·­*ÖÿVã ǽ‘°¹·‚á_¦ÊwCN2•ß ¦æ©¦™ö~¦ýŽ >üûï¿Wî¯,û±É“'O™2¥J•*¯kÖ¬YµjÕ”ÿê7Í—ˆŒ‚¬i#FŒX´h‘r±mõêÕ2š ~ ‡õ0üàÁSuœÕ¯öÞ½{É«V½q’E²B°E&ÝdÜ¿_ŽuåFÿ†¬I•ÐŹ *Hæ;þ¼…îêW@@@±bÅô üòË/êMjã½=°™f€þy‚1Çrýd©?oBŒ©Sÿ¢lÜG²©$ÿ³$Žûp5³n…$µ-5™iïgÚ·÷¶mÛZµj¥þW’ÍwTÇBב£T©RõêÕkÓ¦þÍaR"õ—ˆŒ"=þ7Fª‘]ÕçŸîççg¡ûýïØ±c7lØÖJ) …'NœP†OŸ>mø1Æ+^¼¸ZíÉ“'“w—4ýÇu&/¼fÊMf€þž .¬ÿ–°•(¤íܹ³ú;°+V|ùå—úï&ÚÂ| KˆÚcDŽåÆ\ùÓ/“hÇt©ÓÀóU)éìn&©¼Ò 3íýLþ{÷Ýw/]ºäïï¿téRýGŸXèVþ™3gV«VMÊÔ¬Y3eÍO›%"C kÝСCçÏŸ¯œ¬oÚ´éÌ™3¦Úo¦•Zµj©·Ç?~ü¸Iª•Ý¢úÔ©¶GɨDfÔ¯0y-É|›Ìý…±N<ä@«>±vçÎÆÜêËŸ}ö™¯¯¯ò'ìU«VMš4I­ùñãÇÛ·oW†ã½=°Y–'''åÊ¥‘Ý@õ)ŸhÇt©óÕ«W‰æàvv7‡TÞ é„™ö~æøŽ¹»»O™2EÎç<øÇìß¿ÿèÑ£±žA}äȑڵkóÍ7ÉÛߦù‘þ‚µÎÅÅeøðáÊý€d7zôhõ0ŸAÉa@Þ¶mÛܹsMòìPýÌúã?Ê^2©ON~ýúµÌo…I’ù6YBž?®ÿØ‚X¿ÈÉ‘#‡z«T©Ròº—Ä•+W.I·Ê*ýçŸöîݫއíÚµê*â½=°Y–777õÏ÷=J4 è? 8ÞÛ_Äœè–õûèß0 ¥òVH'Ì´÷3ßwLš÷®Ž…îÎå.\ؽ{÷Ö­[ÕGßEEEõéÓ§råÊåË—OÑgH»%"=#ÃbÀ€³fÍR:_Jæ8tèP5ÒºQÉW²dÉ%J(wG¿}ûöÏ?ÿܸqã”WûöÛo-ZT¹¾ìâ%ξGR\2‹rïL Ýs Ü-5Q™l“%dñâÅêµóåË«»ž···ÚýñêÕ«&ìÌ×¹sgõ¼bÅŠj6¦/„Y–Ðânܸ¡ Ÿ={6ÑÀªÞßÍB÷”¯Dë?wî\’êLÃç êKå­N˜iïgîï˜ÂÊʪŒÎàÁƒOœ8Ñ¢E åGu’J¿þúëU«V%§éél‰HoÁxówÃQ£F 8Pýâ‹/~ÿý÷´mR ÉMýcÖ¤I“6l˜òË!–––ýû÷—ô©ŒŽ?¾iÓ¦Æ_ ~ýúµþsפOÕJTæÛdqýóÏ?ê3oE¯^½b­±:uêìÝ»W–o”#¹A½aðæÍ›¿ùæ›lÙ²]ºtIímïíS¡añªV­Ú¾}û”aYn£F —ß³g:\½zõDë—Ðãâô˨ÃU«VM´ÎTÊ[!äû¬t>6mdsìýÌý‹«R¥J‹-R{©—iõ™v³Dd>„`¼Ñ³gOå¦î²³Û½{wZ·(E:vì8fÌåBéñãǧM›&‘1I5¼xñBfŒuÏŸqãÆ)q/^¼(mìØ±FV(…%E)Ãnnn)¿[{&Ûd±Èúÿä“OÔ?ªzzzª§*9\©ë_Ž^C‡5É_~-t÷·jÛ¶í¼yó,t½7mÚ$›ÞðSâR§añª]»¶ú”Úµk×Ê7-Ö •õ½zõJÊèÏ›hýR~òäÉž—!u®[·N5æ¾Â© •·B2È©¬Ò—:::ZN’ ?‘ÄxæØû™û;/ýõûW¨L¾]"2B0Þ=š0ºwﮌŽ=:mÛ“Bòq.\جY3eT>Z®\¹ºvíjäìŸ~ú©Ì+;99Í™3§C‡Êè„ ¼½½å“h…+W®œ8q¢:*é*åsÊd›LßÙ³gÛ´i£üIW¡\‹U¬råÊï¾ûîŸþi¡»7­ŸŸŸœ¢˜ª s•l¡ë![Yý½QB·N†ÅU¿~ý (wr š1c†/×_~©öÉ)X° ÚÓÀ»wïÊé– %‘HM E‹Mvè1­TÞ É 'Ãê ål6ÖÍO’Í{?sÇâ¥CãíÒmò˜è‘ù‚ñ/9êO›6íêÕ«‰=ó)Chڴ途§XEEEuëÖíòåËcÆŒqqq10—ìRgÍš5yòä—/_ªG}íÛ·ß±c‡rÝ+::ÚÇÇGv¾Ã‡O¨_DDD„Y®V¦´mÛÖ˜‡¸#“m2 ÝO³%ï®_¿^ÿ ÉãÇoÞ¼y¼ååÔ¢nݺÊCåœDN-†jx/^”C¸|1œ S:<\¸pA†%H-Z´H² òVB·N†ÅeeeõùçŸ4H•Å•-[6Þž ›6mR¯çYèþnnä=d”+W®aÆqßÚ²e‹üQG¥)éçcZ©¹’¡T©R·oßV†·oß®önJ9“ïýLû“ÿÎ2]ÿ7|ñÒï U©R¥¸Œ_¦Z"2B0þecc#»¶Ï>û,óÊa&IŽ/¤“Œ%‰Ì$ þüóÏÊèW_}µ|ùò^½zɱ¼jÕªú}õêÕï¿ÿ.»Ñ 6$zý… JµJ÷P‰¶£Gþþûïû÷ïÿÉ'ŸäÊ•K-öàÁÉóæÍS‚”¢råÊß~û­©>`J6Yê‹û%‰ŒŒ|¦tòäÉãÇß»wO¿€l#Iùê]{ãª]»¶ä3å ŸÔ?lذ͛7KÐùàƒôCŒœŠH¸Ù»w¯¡%gKI9Ø'ÚàN:I…JÍrU§ӕŬ ‹K¾~7n}ºD+¥Nù¯$ËÒ¯3Ož<«W¯No·ãMÍ­Tr–5yòdåÖcááá³tô ¤ä÷^&ßû™ü;ö×_M֑YÉæPúøJýê³9=zôhРAÜ’ºS¾Dd>„`üì°&Mš”Пž3"9,^¼øã?„`ü‡ìà*W®¬ÿ`³L@>T£F6mÚ´jÕª½{÷ªWõbÉ—/_Ó¦M}||Œy$²ì+‡ &…,X°zõꀀ€x‹-ZTbSß¾}säÈ‘¢Ï°Œ»É$jHäuss“ƒ™ä§·ÞzKÀrôMƯ%4·lÙRNKÖ¬Y£¼×—;wîúõëK1 ÙFžtîÜ9VNêm=ÌÔ°„ê9räˆ|É¿úê«sçÎÅ- YPÎÄä ™Ôn»Ÿ~úiéÒ¥}}}emĺºæîîÞ½{w9Ç3æéÊi%5·B’Ȳ®\¹²lÙ2Ù/]ºtééÓ§úÏZ3 ÓîýLò“O*_¤]»v=zT}G,ÎÎÎrn/U½ýöÛÚcä 4á‘É‚3¹düAM=Ë7GåÉ`’¥ØØØ´ÑyýúõÙ³gƒ‚‚dwioo/!ÌÓÓSvý õ0@¢íÛ·oŸ>}úæÍ›!!!Ò`ÙŸ(P |ùò‰ÞU>.³n²äÕŸ©¶¸ìÙ³ûéHÊ9uêÔÇ?~,ÛÝÅÅÅËËK¶ñ7íWI$JyûSÞ0ãÛ É££Ž|!åx/_r‰®®®ò ¯V­šÚ7¤Û¶m{ðàÁ䃄‡‡çÌ™³páÂï¾ûn¢—Æ“´ÍTØ$_$µMéH“(Ù4#uŒ¯9©L»÷Kùw¬¤ÎСCe}^½z5 àÖ­[²çŒˆˆprr’ªœqÉ1ò–gƬ@Ó.™ !Ú"»¹Ê:¦­6ŸŽiëDòÔIëVÄ#5f¦/¤‡‡Ç'Ÿ|bòjSSºýz¤ÓîýRþ“<]TÇ$íIŸKD:G€æ‚ 9„`h!šC€æ‚ 9„`h!ð©üDH„`h!šC€æ‚ 9„`h!šC€æ‚ 9„`h!8É,--Óº ÿÁ³“Š Í!@sÁÐB04‡ Í!'¿¾ÈèÁÐB04‡ Í!@sÁÐB04‡ Í!@sÁÐB04‡ Í!@sÁÐB04‡ Í!@sÁÐB04‡ Í!@sÁÐB04ÊÒÒ2­›€Ô“ÖM¤„`hWL̉´nR¥e¥´n !@sÁÐB04‡ Í!@sÁ@ưiÓžV­|ÕQkoï<:51¢óÖ­ûä­Ë—7•(ám|…‘‘Q;Ž•yeøå˃¦n/é!ÈHöï_R¤H~ ß½ûØÀþ>©U«¼‘³ÇÄĸºÖ9yrµTò矧֮Ý9gΰ¦Mk›³É¤G„` #É‘ÃÕÓÓ]îÖ­Ù­[A3f¬¬\ù-#gÿë¯!!/”á'OBåõ£jxyy3odd”uÒ› @zD2°’% †…½~ðà‰þÄû÷>{×®£<ΖͱnÝJ³f Q’î[oµ’×¢E›«…•ᘘáá¯GŽœ·~ý®‡ŸäËç! {äHŸ~ØÛª•ïÆÓ¤Bww×ãÇW*½2NŸ^Ó¿ÿô£G/äÏŸköì¡ÞÞyºw÷;yò²Ì8gΰFj¥îj ÉÁ@vãÆ];;Ûœ9Ýô'¶o?:0ðŸõë§.œOñ€3š6,±UÞZ¼x´¤ÕÇ—Ilݾ}¿:,oõê5eÇŽ+WN(Uªðþý§»t™hee¥t½øöÛ¾ÿÞ¯`Á¼ê"$OÚßÃÃMŠuë6©L™¢3gΞݥk׉]»N ÚÉS©é!Èbbb:7kÖÚNeÉb¯ÿÖŠä5OžœÊkïÞ-?ûlô½{Á¹sçpuÍfñÿ}*ô‡=z¶jÕÿÏ?ü°†Ll×îÃ?ÿ<5þ†™3‡ÈhëÖõªW/£¿YhÍšee sçÆ‚}|šT«ö¶:*ËR–@ºE2’ŠÛ+Y#""­¬,%t~ýõà;è—‘ãÇ/Ú»÷øƒ£¢¢£¢¢d⋯ T{öl€”T‚¬¢R¥·.Ü,áX·Ð’±Ê—)STpqq’×råŠé¾zž²O €Ù‚ŒdËÿB…ÞtK°±±Î;‡½½]¬¯_GÔ®ÝC"òôéJ—.lkkó믇û÷Ÿn¸Zå×rÎÎŽê”lÙ²Êkhè›éY³:Ä*/Õ‰‰IÒ‡ õ‚ŒÄËËSé§›C‡ÎÞÚ±cvÆ5•)ÖÖG­V¹‚ûìÙsuŠ2¬L ó!Ã,ž={6{öìq:ãÇOëæhÈóç/å5W®ìÊhTTô²e?Yü÷êlÜ+µe˳±±>xð¬Ú÷÷ðáó… åussNFêÁÞ… –,YòÍ7ßDDDHî0`€2ýÀ7nüî»ïºé”.]:5[åââ2vìXIÀñ¾;eÊ”ùóç_¾|9[¶l©Ù*-¨Té-[[›©S—O™Òïñã‰KÀ=~ü’ÜfL7ýçÌêèèàã3áÑ£gÞÞy¾újPß¾ŸnÚ´'­>fEÎ$¬¬¬Ö¬YS¶lÙÖ­[Ÿ:uÊÉÉØ®œ£FòööNå죓ԹŽ?þûï¿KÐ7G“Ò¿–-߉9aÌ[mÚÔ—úŽY®ïÜ9O4¨:loo÷õ׃åŸá…Æšbx€t‹œyxyy-^¼¸U«V½{÷^µjU¬w£££ýüü6oÞììì2bĉ˾¾¾+W®”÷íÛçææöÓO?U«VmذaÍš5“YÎ;W¦Ì›¢2×´iÓJ•*5cÆ w÷7Ïì>}úÚµk¥ªàààvíÚI’;v¬Ô?nܸ—/_.[¶ìáÇúK mÞ¼yXX˜”¬]»¶Ì>qâD¥»ð¬Y³ $ï,XðÈ‘#ýõ×¼yóÚ¶m+sIå}ûö½~ýzxxø‹/¤Î½{÷¦ÒÚ™!8SiÙ²e÷îÝ% ¿ÿþû;wÖkæÌ™K—.=}ú´„Ý-[¶HÉråÊI•\+%•߮կ_ÿõë×J¾}ûv… ®^½êííýÉ'ŸÌž={Μ9ÊæùóçûûûŸ?>W®\7nÜ([¶¬££ã¤I“$Ÿ8qB^Ÿ={«aR,[¶lÛ¶mË’%‹ŒN˜0AB°òÖçŸ.!XÒ¶ÒIҕÜ£GÇ=úææuëÖµ¶¶®X±¢YW ÐBpf3kÖ¬ôëׯZµj%J”P§KnÒ¤‰$`nРAttôo¿ý¦_@tèÐAñ;wòæÍ»zõꘘ˜U«V3æúõë2Eíb±páÂÆK–a‰È5úî»ï$ËZ¼ùUV%ÉÖ±z!ß¼ysܸqR•’€ “4¼uëVexçÎR­òlˆ5jHòNÉšP‚3›¬Y³®[·®jÕª­[·V®¡*®]»öâÅ‹sçÎ)£ ˆ{Ÿ¬O>ù¤wïÞëׯ¨Y³¦ƒƒC½zõ”4œÎmÚ´§U+ßË—7•(áÖmI3¬@úGδ–.]Z¶lÙçÏÿ}®¤[‰³ÇŽ«R¥Š~1GGÇ[·n©£ï¿ÿ~žÛÝÝÕ×·“~¥òÀÀ-EŠäW†OŸ^Ó¿ÿô£G/äÏŸköì¡R[÷î~'O^ΗÏcΜ—+az„E+Vl z$>ÿ¼mŸ>­ÔÚâmª~{bbNÜ¿ÿXÚ³k×ÑgËæX·n¥Y³†xyyê“6«M =rä¼õëw=|øDZÒ­[³‘#}ä+dxå`n„`¤SK—.ݸqãòåËÕ)Êã ÃÃÃÓ¬MÉ2dÈÌ©SûçÊ•½sçñ]»N Ú)P²o‡cl³mÛ¬§OC%­ÊèþýK¤üâÅ£eôðáe'N\–Pûí·?|ÿ½_Á‚y8cxA’MeAn]ºLìÖmR™2EgΜ=»K×®ÕåôÕ²eÛ.U«Vùß~;Ò§ÏTû.]šhªÚ%£·o?:0ðŸõë§.œO±DئMKþÖ/¦ßÔ^½¦ìØq`åÊ ¥JÞ¿ÿ´´ÍÊÊjĈΖhŽ­@,„`¤Svvv’w”_Ýɰ¯¯¯‡‡Ç§Ÿ~šÖMKŸ&ï¼SN:ujÔ³çä{÷‚óäÉyåÊèèèO?­W°à›d¹e‹ÿ“'!JyW×lòš#‡«Ò£ uëzÕ«—1fARÍšee sçÆ‚e¹Õª½­ŽÊr³d±_¸pó˜1Ý>ûì#‹7¿YlvìØÅéÓW¨!8Þ¦ÆjÏŠo~ž(Ó•×Þ½[~öÙh)«˜âÑ£g«Víð÷ÿüÃkÈh»vþùç©ùó7¨!8Þ%¦hu`B0Ò©:ܸq£U«V...666Ïž=«R¥ÊñãÇ•‡td åËW²gw‘×W¯Þ\É®R¥´‹‹SÛ¶_ôïßú£j”,Y0þø?WÅŠ%\P™2E•©ÙâMŸ“bú£²Ü¿þºùþûÿûed:/ÞòìÙsMÅÒÒrüøE{÷ðàqTT´ÒcûÅ‹Wñ6éìÙ)£dqE¥JoI—plü0B0Ò)kkëñ:iÝ”²µýÏÿ2å˹re?|xÙäÉË&N\ téÂRþ×_÷ï?=¡&)KtvvT§dË–U^CC_¹DÌ„ ¤’% ®Z5122ê÷ßO ôUãÆƒîÞýÕÎÎ6¡ò±:ËJMê•KÂë×O‘EëO÷òò|øpß¾}}ûöMë¶ q]»6 zôå—K{õšœ5«CÍšå~þy޵õ›®ºµkW¨_¿Úȑ󢢢õgñòòœ1cà”)Ë6lØýá‡Õ¿ü²o³fC’ºÜÙ³‡J0•ì{÷îCww—fÍêLÚßð,j{¦LY~öìÚ•+'Êp‰-¤=C‡vT}þüÕ=¾\ºt¬Zl̘nêìsæ uttðñ™ðèÑ3 Ü_}5¨oß v@¦DÎ$~øá‡ž={ÊÀüùó“:ïÝ»w”§#…Z¶|?&æDÜáX£–––£Gw•qk°··Û¹s^¼•Ò^þ©£Æ,(Ö¨­­Ÿ_ùg Ù±Fcµ§M›úòOÞ#G–+íÛ7T'ÔNýë¯Ë?ã—@* g>TòŸT£Fòöö&í gx.\øòË/cbb–/_¾oß¾zõê}ñÅÑÑÑ~~~›7ovvv 1bD›6mdbíÚµÃÃí­­ƒƒƒýýý›4iâëë»råJ///™×ÉÉiÇŽãÆ?~üž={ÆŒsøðaå§N2,Ê[/_¾\¶l™dîx‘Ö+À(„à ¯téÒk×®]·n]çÎÕ»êΜ9séÒ¥§OŸvssÛ²eKË–-Ë•+W´hQy;w®Å›>©]{õêÕ´iÓéӧϘ1CW½×ûï¿¿ÿ~ ÁÊè¤I“$òž8qB^Ÿ={–Ð"”§»¤s„àÌIâi“&M$žÊpƒ ¢££ûí7I¨Jùóç Jjµ•*U’¤;kÖ,‹0éç0 BpætíÚµ/^œ;wN-P @LLÌÍ›7G}ñâEggç7n$»ò,Y²$´ˆ7 5‚3'É£íÛ·÷óóÓŸX¥JyÝ¿¿££ãøñã'L˜`òEd„àÌ©P¡Bÿýw¬‰§Nòõõ•l¾Ed„àÌ©C‡“&M:vì˜rõWáééyõêUe8$$D.±øÖ­[j™ÀÀÀd/ C gxÏž=›2eŠ üôÓOVVVÝ»wÏ;·¯¯oXXXëÖ­sæÌ)¹¶`Á‚_|ñ…¿¿ÿþýkÔ¨Q²dIå.Ó§O>|øàÁƒå­ . 4ÈÏÏoÈ!uêÔ©\¹r@@€”ùú믥ÀÔ©Sex÷îÝ•*UjÔ¨‘ Ç»4]F!gx...Suô'Z[[OÔÑŸØFG]²d‰2«d×®ñ<Æl„N¢‹ÈÁÐB04‡ Í!@sÁÐB04‡ Í!@sÁÐB0´ËÒ²RZ7¤ B0´ë¡O­´nROÎeûÓº €t„ Í!@sÁÐB02?KK˘˜˜´nHGÁÈäHÀ .B04‡ŒÌŒËÀ ^„`h!™—@BÁÐB02'.ÁÈ„HÀÀ0B04‡ŒÌ†ËÀ Q„`h!™ —€1ÁÐB02.#‚‘I€€ñÁÐB02.€$!@sÁÈ𸠒ŠŒŒ ’ Í!#ã20HB04‡ŒŒŠËÀ ÙÁÐB02$.€” #ã!€"@sÁÈ`¸ RŽ Í!##á200 B04‡Œ ƒËÀÀTÁÈÒIÞv#¸Ëï—ÕQ+ËüNmŠx x;ÿ/·É[‡>©XÔ%«ñFFÇôÝåç›døŸŽ5Mßb“R>þ±• :gIó)e’ºÂP‚$ÛÞ°lAgŠþãîÓQG¯‡ETÏåbäì’å ¯>´§IyIx‡ï?Ûü÷Ã)Õ èånÎ&€ÿ #H'—UÙl<²Ø)Ãí‹yÞ~>ïüíò9²9{àÓ—¡QÊð³ðHy}?¯[>G{c挎±±²Lz“À‚”*æ’5<*:8,BâÃW¯'œ¸¾ïîÓàWN¶Öïävñ«ZXIº5·œ”×*?œP +Ã}j½ŽŠžtòÆÖë…Eäv´ïPÌs`™üÛu÷_Z·äø׳ÛÛìj\^é ð{Ó #Ž\=õ04£ý䪅ó;Ù>xöÑs™qJÕÂõógÕ΄š¤Ô¶§Iù‘G®õÌj?µÚ¿³K3†ººçÎc;+«†Ük$pµÛøöÄû- .HrÿŒ3·Ö_½ÿàÕëüN=KåíR"·I6@ËÁHïÒÛeà¸n=³³²tw°ÕŸØûÏ+‡¼ZR§„w¶,’>G½Öa÷E‰‰òÖÌšE ü¥Q9/'ûßþy¬Ë[C_ÝõÏãoÞ-^Ü5ë‘ûÏ”€XH×/vù_÷¾}·xlê"&¿>¶RÁ¶|~0°”›ã¤ª…Ýìm—ÚV‹uÅØ@“ÄØcKm9³ØöÛÿ¿Ùûî¿réñ‹µõJ{99ì¹ýxú™›Öƒ1í‰÷J6° 1Çþ^xÿ«Eªyºì»ódøá«ÖVíŠæJÙFh!H>ÉæÇ„,¼x§u‘\Y¬­ôßšW«¸¼zfµS^}JäîõÇ•û/_çÊjçb÷æÿ»ý›>úÃÃ#6^}0¡rÁ÷òºÉÄ…<…,ýëž_•B2Ú¬PÎÊÎú‹h]Ä£ŠnJÛ"¹$t¶­R¨RÎlê¨,KYz¢MRÞm[Ô³j®7µµ)’kÈ¡7³K<Ý{ûÉ´êEjz¾¹.Û¹DîSÁ¡kï'´6mµe¼P–˜Ð‚ž¾Ž\qåÞ²^- {Xè:Ÿœz:÷ü?„`@ ‚‘®¥ÏËÀïÿtÚÒâÍeÖÈèhiaÛ¢¹&U)´ëŸÇúeäíégnî¿÷,øÕ먋(ݧxe Ú‹_H±ŠzI·\'‰€Že¸¬»S¬ò¥²;*κ$]ú¿£aQѱÊnR÷gw³ÿwö›¡a1ÿ]nUg!8Ñö> ‹÷¾ÿ,¡ô<":æÝ<®ê[5s»¬  y™P30!éWúLÀbÅ{oyëº%ØXYæÊbg÷ßkÀâutL“_ÎIz_¹` ׬¶V–{î<yäšáj•_Ëe³µV§8醟ë¦g±‰½”X¿³ýïh¬U—h“lâÌ®,×Q¯=úÃq%Úž„>`îjt¼ Rfùä×óV–ÿÖ¦d÷‡ÿí @R‚$ËçdoøF¹Ç„üòjm½Räû÷×iÖwŸ&Z­³.ùé_ã yý&*WRS(Mʪ‹Ý¯"ÿwEùYÊ®¿&ô•ñ.H™eIÝ’Å\þ³Âó:9\zü"%h!éTº½ lŒºë—9ÿÿ6jQ11Ê÷õ?OÜÏV*»“•å±!jßßãC dsp5E6¦I±s}óŠsž—ËñoG…ÃA!)iCBPwA¥Ýl­,ï¿|ÝàÿovñðÕkkKK;îHB0`zæ$ºÍ>÷ϘŠÞOÂ#ýÏÞ*•Ýñtpè±û!žYí”>?ß|ô^>·Wz=wÝìmÚÍ5çüí’nŽÅ]³þy÷é×LÒý*άM²H Oæu´¯áé2óì­b®Yò::üzë‘dÖ”´!¡h`Arо˜çäS7<²Ø¾íît=äÕÈ#×ògsXóA©”´B0Ò£ }Xxd±›ÿnñI'nTß|2¯“}ßÒù$É]~òbð¡À¬¶ÖæÏ^7¯›ßÉI‡”óÒŸqJÕÂYm¬ûïx‘ßÉab•B]KæÙv#جMjX GBs}[»ÄC­v^°³¶úÈ+ûÄÊ…:ï½”’fÄû /è˪…]ím$û½zífoÛÐË}L%ï”´ B0Ò¡ôœ€{çxèS˘·šÌ)ÿô üÚ¨œ:¼¡~iu¸W©¼ê°DÀIU źúw¡±¦5¦IKꔈwöÜYíb]s·fãÛï4¼ [+ËQ¼åŸá…$„`h!éKz¾ 2 B04‡Œt„ËÀ u‚ 9„`¤\Vm»Üå÷ËÇZT2ü\ºLÜÌŒtœ™È†,¼úО&åÉЀt‹ ÀÄŸ¾ Õ=¥€t‹Œ´ÇeàGaC]Ýs籕UÃî5r¹¨oEFÇÌ8skýÕû^½ÎïäгTÞ.%r+Ý~ú¨Ì´Ó7?ÉbcÝÈ;Ç´j…í­­šÅâÿ;9ìiR~ä‘k§ƒC=³ÚO­V¸~þìÉh€áÚjn9)¯U~8a‘ÀÃ5Hs„` íõÝåÒãkë•örrØsûñô37Õ·Æû{Màý¯j©æé²ïΓᇯ:X[e³µ–·†¾ê[¾Àüw‹Ÿ}ô¼÷Wd¢ò$¶xgiW4—RáØc­T0gÛ~û¸Ô¶šeÒ`¸¶™5‹:øK£r^Nö©·H B0Ò—ï¿|½÷ö“iÕ‹Ôô|sýµs‰Ü§‚C×ޗ᧯#W\¹7¤¬WËÂ2Ú¾˜ç©‡¡sÏÿ£(e¨M8é®ÿ|óÑ{ùÜ 9gqÐݸ €t… ¤½ok—r(°ÕÎ vÖVyeŸX¹Pç½—”·¾¬ZØÕÞF¢gЫ×nö¶ ½ÜÇTòþóîSyk|å‚‹.ÝíôÔÎʪu‘\ÃÊ00‹i`¸¶šž.uóºù¼1ûÜ?ûšU‚u€Y‚‘¸ Kî¬v±®­ªÏ˜°µ²UÁ[¹'Z,ùœì×Ö‹çŠlB³4öΡÿè ýÑd4À@m’¤7Ô/Ïç Ý @sÁHm\iŽŒTE6‰X]@R‚ 9„`¤.€t‚ Í!#•p¤„`¤0HWÁÐB0ÌŽËÀ ½!@sÁ0/.€tˆ Í!ÃŒ¸ Ò'B0Ì… Ò-B04‡ ³à20HÏÁÐB0LËÀ #@sÁ01.€ô S"€  Í!Ãd¸ 2 B04‡ Óà20È@ÁÐB0L€ËÀ c!#¥HÀ Ã!@sÁH.€Œˆ Í!#ù¸ 2(B0’‰ 2.B04‡Œäà20ÈÐÁÐB0’ŒËÀ £#@sÁH.€L€Œ$ €Ì Í!ÃX\™!šC†Q¸ 2B04‡ŒÄqd2„`$Ž 2B04‡ íʹlZ7©ŠŽ=!Ú³°GZ7©Ê²ç¢´n ½ @sÁÐB04‡ Í!§S.\X²dÉ7ß|1{öì(Ó8°qãÆï¾û®[·n*T8sæÌÌ™3 (àíí­Î{âĉçÏŸË€“““”±²²’á?þøC-vÿþýÖ­[?^)¿}ûöÅ‹?|øÐÎÎ.,,ìéÓ§RòóÏ?ÿè£äÝèèèeË–}ÿý÷ò–ödÍšµsçΟ}öYJÚsäÈ‘ððpó¯K€ØÁéTéÒ¥gÍšµyóf//¯áÇתU«|ùò2ýüQÞ•ÑŽ;J¸”Hª&Z¡„WñÁlݺU¶´´T‹={vË–-Êô¾}ûîÙ³G2nÅŠ•)v%Î=zTê‘ܼys Í«W¯.R¤ˆR 00°]»v¿üò˪U«RÒNu‰©Œœ®YYY­Y³¦lÙ²­[·>uꔓ““1sÕ©SG¨oÂ… W©RE$¼~ûí·—.]*^¼¸ú®ƒƒƒ¼%ÃK—.ݵk×µk×rçέ(Z´¨dkÉÄ’’•ëÁÉkg×®])`r„àôÎËËkñâÅ­ZµêÝ»÷ªU«Œ™Å××WøôÓOã- !µaÆ2°hÑ¢Zµjé'`…‡‡G¯^½d`åÊ•uëÖÕOÀмyó¾÷Þ{Ë—/WBpòÚÙ§OcŠ˜!8hÙ²e÷îÝ%b¾ÿþû;w6Pòܹs›7oÖïr`ØÉ“'ºZ¬¸pá‚O¼o•(QBBpòÚyûöí%K–ßNÓ"g ³fÍ:pà@¿~ýªU«&é3Ö»F÷íÛ'ÁÁÁ’D¯öéÓ§ÎÎÎ „††:::ÆûVÖ¬YCBB’×ÎgÏž5mÚÔøv˜!8cĹnݺªU«¶nÝúèÑ£±ÞUp¦\ 6¾ZIÀs pqqQn4—LwuuM^;•+ÁÆ·À´ÁF™2ef̘ѿÿÁƒ(#Œ¯³råÊ'OžŒ÷­°°0‡òåË_¾|9Þ2=ÞÛ;ÓÎ|ùòѤ!BpFÒ¯_¿]»v-X°ÀÍÍÍ@±ˆˆˆY³f 6,Ñ {÷îݼyóS§NU¨P!V ³gÏöõõíÒ¥‹ÏíÛ·%¶ê¸uëÖ¾}ûúœ‘íH+„à féÒ¥eË–M¨‹‚âôéÓ/^¼0¦¶¦M›íææ¶eË––-[ÊŒ%J”Håõl„àôKòhƒ ”¬ÏÚÚZ‰ª"þüAAAñΞh±>}úL›6íçŸîرcBå?~%ï®X±ByWp“&M$˰´0::ú·ß~#вM›ö´jå«ŽÚØX{{çéԩш·nÝ'o]¾¼©D oã+ŒŒŒêØq¬Ì+Ã/_4u{‚Ó± .¼÷Þ{q§ß¼ysôèÑ/^tvv¾qãFB³'Z,oÞ¼òzûömåÇçëë[´hÑÏ>û¬GŽeâµk×^¼xqîÜ9¥L¸¿#ˆýû—)òf?¾{÷±ý>|R«Vycæ•©«k“'W+5üù穵kwΙ3¬iÓÚæm4 U„àô+22ÒÆ&ž ÔªU+‹7»ÚýŽŽŽj‰d{ýúµ¼ÚÛÛ(?tèP™¾aÆùóçÏž=ûøñãÅ‹—=uûöí¹ÛÄ’#‡«§§»2Ü­[³[·‚fÌXY¹ò[ÆÌû×_7BB^¨£Ož„ÊëGÕðòò4föÈÈ(ë¤7Ð.Bpúåíí}ýúõ¸ÓO:åëë+QÕðì‰ ”×2eÊ(ñâÅR¥J 6¬M›6^^^[·n•2… úûï¿“üy@cJ–,öúÁƒ'úïß<|øì]»Ž>xð8[6Ǻu+Íš5D’î[o½¹Q´hs ݳBZ¶®Ž†…9rÞúõ»>|’/Ÿ‡Äë‘#},--•>7N“ ÝÝ]}};ÉèéÓkú÷Ÿ~ôè…üùsÍž=ÔÛ;O÷î~'O^–çÌÖ¨Q­´X@zDN¿Ú·o?}úô+W®/^ÜBï‘’žžžW¯^U†CBBÔò’_Õ;H(¦š7o^É’%Õ»CÄ[¾k×®û÷ï·µµ }sM¢@òÚ¡C‡I“&;v¬J•*&û´éܸq×ÎÎ6gN7ý‰íÛ ügýú)… ç“@<`ÀŒ¦MKr]¼x´¤ÕÇ—Ilµxsw ÿöê5eÇŽ+WN(Uªðþý§»t™hee5bDg¥Âo¿ýáûïý Ì{àÀ•@|ðàÁòÖ…  ÔöÿØ»°¦®6àd3žʥ¢PRÎjë¨{Ï*NÜ[ôÓÖ]·8[­VE«­Öªmµî:PQœ¸pàdƒìò½áÚCÂáÿ{xòÜ{î¹ç¾ üïá& ¨°Ûäɓϟ?éÒ% µ¶¶¶'Ož”^q¡°¿………§§§‰‰ITT¢_¿~Ôîçç—™™Ù¿KKKŠÎNNNóæÍ³²²ª°G @ÈÅâ«Wï®_ÿë°a]ôô²›$›U¯nÉÜŽ×gðàù‘‘q&&†:2THWy<îÞ½Ç׬™Ú±csj4¨ã¥K¡[¶”†àþý¿lÖ¬žt|:¢·w}Z>¼+…`ŸnM›~&]¥1‡„`ÍEñtm¾‚›d?—wÇŽÌÂâ|²}vSFa ¾{r8¹ciÔh3‰““Ëf³(w®[7ýøñ˲}¨ÃÂ…?ýû‘(O$QcZZ†²1ïÜyBݘ Ëðòª»mÛáøø¤ÿZG¶½z5™cc!Ý6hPKv5##«Ä÷@K ”Ž#GÖ8;K>x‡ËåT«f!ðå:dgç´n=†"òªU“=<\x<îÉ“A“&­R1&ón9#£oØ04Ô§Û””ï¢Ó×וíOcªXŇùH!”æΔ¹zõnxø«ãÇ7tîìÍ´p8×UÉÌà&%¥J[˜e¦Š ! œ¤¦¦Ó­µµ³*åíÚõ—ŽÌmÁ™Úúõkq¹œ+WîH/ü ºçìlkjjTNEh)„`€râåU—Çã®X±{ùò‰ É‹o§Œ{ãÆʸÌÌîÑ£:thV³¦ƒt33£#º¯\àááâîîröìõ}ûNøûO¯¸; %‚ʉùž=‹çÎÝìæÖÛÁÁfæÌ¡£Fõ¸wïé˜1ËvîüšÒ¦åËwß¹ó«ì^7Î40ÐõñYŸäèX}íÚi&ô«¨» 5‚JªOŸvbñMu6 ð}Év¸vm7³0dHgi£½½µt/€¿nÝtúR=r‘V!ª„`¨r‚ ÊAÖ\yyy­[·ÎÊÊâp8qqqkÖ¬éÞ½;³é§Ÿ~úᇌŒŒ’’’¸\nHHˆ²FdéÒ¥‡¦öäää9sæ 0àÊ•+'Näñx"‘ÈÅÅåàÁƒÔSa#€VBÖ\b±¸Aƒ›6m¢å‘#Gúúú2!8 `Ê”)7oÞtww§Õ.]º(k$þþþ;wî¼uë–©©é‘#GúôéCc4¨mÛ¶»wï¦C >œé©°@+!k.‡Ã$`ÉÛ„í£¢¢˜åõë×wîÜ™ »döìÙÊ %ànݺQ¦å:äåå>}:!!!..N$Ñ!(=3=6h“C‡Îõíë÷ðá!77ÇŠ®¥bàBÖ\/_¾œ?~XX˜‘‘QDD„´ýáÇ:u’®¶jÕJY#yöìYZZÚÝ»w™Õ5jˆÅâ øùùÕ¬YsðàÁcÆŒ¡„M›6h“®][FFž²´4-ÞîôüibÒ&$äÕÿ¹<©Y’´[ m‚¬¹úöíK· .\´h‘t›Í.Ø_a#=ñ 2déÒ¥?xðà–-[6lØpãÆÚµkÏœ9³`c©Þ!€ &ðmlÌ‹½û£GÉÉir¹¹".—S²ºŠOaI*º•ðÐ&Áš+44ÔÏϰ\»››Û³gÏÔi$ÎÎÎÏŸ?—k swwŸ5kÖ€Ž=JRØXŠw ÂI/¸ÿ-„„ü2yòêàà0[[«M›fuéÒ’é¶ÿÉï¿ßùäÉ+]]~ëÖh“£cuj¯[W27Q³fO¦Û￯œ={ƒ¹¹É{ä.3`VÃÃܾý„nÝÚ?iÒªë×ïÛÛ[oØ0“F=ziHÈC;;«?—òô¢E?‹ŠŠ§>S§?¾/3”²ReKŠŠ:Mõœ9s=&&ÁÐРm[¯õëg88ج\ZgVVöܹ›8›HÅŒÕcî\‹¥ú Ú!XsÙØØ<}ú”YNNN–¶ûúúN›6íúõëMš4QÝH†ºdÉ’àààÆKGŽÈãñRRRtò¯‘PÖ ÅfÌð_±b’µµÙðá GŽ\uŠòeß¡C¿›2eÀ߯ÿ>…Ò*­î þ۷ϧՠ ]7o>¤P»uëûö-ur²-ô@”Mé@VV¦#F,5jI½z5ýý§›™¹Xz\ê6mÚÚ]»þÞ¶í-[6<}úÚøñ+tuFFÊJ•-‰Bó!óÃÃ_8°ÜÅÅ.::òk÷îÓ)¬\Z˜¯ïòãÇ/ïÙ³ÈÝÝ%0ð•Çf³çÌ®âñ)ýo@AÖ\kÖ¬™4iRóæÍëÔ©Ã<ï¬ZµjöìÙ”w333¿ùæ¡Phkkëää4oÞ<…VVV~~~ÔÞ¿KKKJÕL»………§§§‰‰ITT دŸäÐ+lÐb>>ÝZ´h@ Æu;öûÈȸêÕ-?ŽÈËËë×ïK''Éìï‘#k?LC˜˜êHž-M˜+ ú÷ÿ²Y³zêˆÆ÷ö®O Çw¥LÇmÚô3é*s\:ʶm‡¿ývÔàÁ’7xŒÕ#88lÕª€¥KÇ++U®¤€ÉUsL;ÝŽ×gðàùÔ³Z5 ¹ÊññI{÷_³fjÇŽÍu$ŸÔñÒ¥Ð-[JC°²ƒh„`Í5 ŸtuÇŽÒå©ùäú+läp8‹óÉ6;v¬àá6h±† ?¼óÁÌ̘n32²è¶qcccáÀó&Mêß©Só:uœìí­îÞ¨Q5T¯^MfF¦Û jÉ®2ǽuëqNNn»vÿjצM£íÛ$%¥*+U‹ÅZ¸ð§ÿ½“ å‰D"jLKËPVÕ;O¨Ç^^u)ˆS8fVÕ9(@å… U÷É‹ X,¦[kk³  ]ß¿kñâí3fø»»;oß>_ጯ¾¾nñ¤ð¸Ì×Ú·'}—3“b££ã•í"+;;§uë1£W­šìááBýOž ’½ò¡ æˆÌå CC}ºMIISQ'€Ö@øD:N{÷.ÎÍ?sÚ´µ]»N{÷î$ŸÏS±‹ÜŲI‹zPfVøÀåttÙöàà0uv¿zõnxø«ãÇ7tîìÍ´p8×Õ9"3ÓÌ`–™v­‡ ðQXØó/ÞvéÒ’Ëå|ùe?¿áß|ó]TT<ó1 :J&Duuù:2òÖ­ÇE=nƒµxè=::Ãa«ßÒ’RSÓuò§±™‘(o×®¿äª•«¼~ýZt¯\¹#ç ºçìlkjjTÔâ*#„`€‚‚îŽ¿â‡æ´oß855cÏžcööÖ¶¶V:ÿ]9pôè…””t¹½>ûÌ•ÅbmÙrÐÆÆ<4ôщWŠz\Šž£Fõ˜?ÿG‹† k?}úzÒ¤UŽŽÕ‡ïªb/iI”hiaÅŠÝË—OLHH^¼x;µÜ¸ñ€2nõê– +7331¢ûÊ•.îî.gÏ^ß·ï„¿ÿô¢VPI!|4rd÷¨¨øeËvúú~¯¯¯ëíÝàŸ6r8’ët[·öüꫦsçn‰òäörp°Y½zÊòå»<Û±c³eË&ôè1£¨‡Þ°a&SʾïÞÅš›÷èÑfÅŠIgÏ«ØEZ’‘‘pÕªÉ?üpÈÍ­73sæPŠÔ÷î=3f™^·n­V¾qãL]ŸEññI¸×®6a>ª „`¨*úôi'ßÔ‘üƒ!GfA®]'ÿêÞùóGÒWÁÝþ©S›• >cÆú’®2ººÚ+;PÁU»téxæ3ÑÔÜE®¤Y³¾‘Ý÷ÚµÝÒe…•ÓîëÖM§/¹vÕÐÁPå ƒdÚ|U B0HÞ/Œ U B0T9Á É`¨R‚ ÊA†0 UB0|„\ ‡BŸ÷ÝvÖÒP7rÕP›%mOLϲš±77//|ÉW+uÿ)3šê]˜>õs³1)ùheªÂ P! ¤fæ^ l[»º´å¯;/y6…à ¬ª²£Ó1“©»CæõB†€R‡ ŸÀdpñx»ZÿúB6¹Ag¾­Àª*»GQï“3³+º ÐNÁ 9¸:Ôµ÷?wwÓoVþiY¹§¼Yص‘4gåŠæ >póYlJ¦©Á¨ns;6¤Î´ê»/ðdØk>—ݳS«šÕ¤cææå-ú;4 èITrº£¹áÔvŸoSWuÊFS6s¹ÂÅ™]ürõy”>ŸÛ»¡óæÞº<޲½˜]BæõšüÛÕàˆ[º×]ê9¨. x£Õ]xnk~ûÝŠ·)ù· @ ! ´¯c»è˜$Gz»ØÐꉰW.–FΖ†Ò ß{µÇ§­{uÓÀð¨Ù,ÖœŽ †í>÷MÂñIÌh¯…ÇnJw™v0h×ÕÇÛ·jYÓ†"õøý—)›Žð®­¢ e£©jܾˋº5Ú3¢MÈ˸!;ÿ5Òã­ëÛLÙ^´•6ÍøýÚŠ^­ô†ï¾0rÏŨÕC™ô_Ô»£z´íC[Þ{)ȯ‡£…°4¾K!ƒ˜ .*Ês_æp(ä‚ÜŠèåé(ÝŸ–¹÷Zøš>M;ºÛÓê Æ®—Â#·\Ö¬Öɰ×[¶hSKr…o«ºÁ/b))ê俯nÛ¥‡ß~í9¸‰+­Žjá³êÔ!82)]áh…5´iÍ>žÎ´`o*ôi^{OP8…`e{-íáE«>ÞµZ¸Jî)Ý…±¿Ò¡«›è++@ÅÝQ1­šèóéÖB¨kc¤_ºß/„`€ÒÑÛÓiæ¡kþýšeçæ¿÷êâÌ®á1I̦;¯Dyâ¦NÖÒÎ^5,)R¦F5,¤íÞ®ÖLj¼õ*>G”×ÎÍVº‰’åöÀGIJ¯‘ {—¨p´[¯ãT%{ÑBC{‹ÍçÃbS2ï½MP±ucÍ t›‘“«¢€BïŽÂÑÊB0(†Éà¢êìá0l×…àˆ˜øÔ, ¡n};sifÞÝÅüéŸa¨+Y~÷>n…‚íÒef—öþÇÙì›(FÓmtr†²R2sŽ–œ‘£z(fÂUv—´ìÕð8l̉² ½; G(SÁ rp‘¸ÜíŽÜŠˆOËìÙÐQv“±ž$eÊNâ2ËÌýÓ³?N|¾OÏ–ÝåÀ˜vulLe‡r0Þ}¯¬…£:^)Y’](£+Û‹R¾ŠG ¨wGÅhe ! ÔôötÚøïýȤôƒcÚ˶׷3ç²ÙWžF7sþpEDÐóhg £æ.’ÕÐWq^5,™öKá‘ÌB{s‡MCu­Wƒi‰NÎà°Y|î'“¦²êT3Q8Z¡C]yÕÄÉŠY¦ÝmŒôÍ tK±€âÝ)±Nàô!ƒ*˜ .Jx£÷^23È^þ«“©ëïÚ+OÝö°5u¯nzöáÛ}ןú÷kfo*l]«Ú²nÕ±1u0þy'‚Â1³‹©¾`T ·ùÞ HÚÐÁüiLò¤ß®8š›ØQÙÑ•VèPAO(‘7ªaq#"v÷Õ'SÚy¨ØkxóZE- x£#]ÉüñÑÛêÚ×´6Òãáù J ^T ÈÁê3Öã·­]ÝÉÂÅ’ß´q@s×g÷Åø´L kû6ÐÆÚ÷übì/6üÃç²»×w\Û§Y¯­§™]6ôoNé™Ââ»÷éæBAŽ+z6Q]€²ÑTµºwÓ çîþùíõMÓZ »x©(àì£7Å( x£Q¤þª®ÝÜ#ÁËOܾó]o Ùªï>hË«¢K€r…(Á%ÒÇÓYöÿ8ü3©“ÂM.g]ßfÌçïʲ51›Ü•îÂã°—vÿœ¾TQÍÑÅp0Ÿ¤`‚YarG—[-êÝQ==h§¦tVX3h½XŸ–]”+Ë]]T9ÁP8L€–A€*!Ô‚É`­¤âÊ í† êB­ UB0&ƒ@; CÑ €@€*!Š “Á ™þŽˆqþ¡t•ËfÙ u¸ZMþÌž–™­W{5ªi¬¯æ€¹yâ ÿyO˯¿ñ.“¢Ksƒ{{9éUìŠñh”3„`Ð*Ç:×w2Ò¥…,QÞÅwïÿwýY\fÎ÷M\ÔÜÎï]~¹z®[CŠwAÑI‡ŸÇ.oêÒÑÁ¼,K€ € ÅÉ`ÐXfº\+=>³<¤–Í›´¬Í÷Þ,ðrRs÷ð÷é)9"f9)+—nÛÙšÚÔÙ77OÌe³Š^2T„`(&ä`¨jëg‰òb3sdc3²Ý|qáÝû¸Œ!Ó¢šñÒ&.LÒõ>B·ÿ¸)íÌ,Çú´Ìå- ‰8ú"6>3§š`h-›)õìåÿÝgÛ: o¾0pÏtmÈ\ p¾»çœkOCcSª¾oâb/L¿~'>•v\ÞÄå+{3¹:••ÄŒv®[ùמ݊K±Ñ¬húqwªdæÕ§çÞ&ðÙìÎ5Ì›[|Ô¯Gád©<åþÕ·_x“‘m/Ôën;­Z©|ãÊB0h³W©™|6ËB—'Û8îÒãçÉ;Ú¸9êQúœ{ýÙгai“¿wÍiWÂOtià œ~ ]¦M3‚žžyðC«ÚµMô¯E'M¾NÑ9ÿ¢ØÝ"·¶ª]ÃPWzˆE7^|çåDÇ|ùÉÔ+áî¦Kš¸˜ ¸S.?¡¯›ÊÍ«(‰|üœF³ÔãM üd÷ $¤ýú¥‡ƒP÷Ü›„U·_*{Ô©G᤬â@ß?ß½¶¹kSã og=Õå°Õ´.é·  ì!Cña24=7݈IÞö¶¿«5%3ÙM›[Ö¦[}>sëãVÍ÷âãèôlk}¾1_òÊh.\S!»œ•óûÓ˜EŸ;}akJ½­‚¢’w>Š\ÚØ™V{8[~ne${ˆþ®Vó[ºZSèØØÙËÒPºJÇbŽ^hIÌÖ5mšXKFàj=ãê‡Ýéöß7‰+›¹zÛHæe‡»U Kù5îþè}ºøÓC7±2R‚ ­'<)SÙTv {ñi9yâVÕM¤íÞÕŒ÷>‰JÎÎUX€FA­ðE]Çü˸l–µŸÿé0ÉÎw;q—ÒÛÂÏÜLôylÖ¹·‰s¯=S=,ón9CGÚ"Ì_NÍo×ãÊEîr¼OWåæ -‰«hwæÐ2%É.µew0-G¤ì@)9’°Ûëä=6ëÃhLv—»@3!CIa24ŠP úSroÄ$?OÎøõK÷övÞ^Æy÷¾ÐaòcŸìgr¶$23©%T¼’ôó“wFîÇIå¤LÁ*»ƒ†|®²1÷}GÛ:µŒ?yÀm…ºÒŠ] @ù@€ª%-^Óò¿Q‰ÅÌ%²§òOëÝÍ„\6+8&YzíïØä†º&¥‚Õ)© Z&’ÿCq7>µÅ‡ ‚¢’‹]ƒŠ;¨ì@f<6+:=»ÃŸV›‘Ía±øøœ8¨ ‚¡`2*JrÝ6Ü}ým#ÇĬÜ5w^¹›ÜŠK ŽN¶Ñç3×üó2þ ;Ó ™+wMÜA5­7Þ{SÇÔ ¶‰þ¥wïÿxSðjãR/IGyž´54·1ö¿óª–‰ž­îÉWñ[‹]ƒŠ;¨ì@”‡Ô²ù>4ÂJ÷™¹ðErÆÜkÏì u÷·w/vå!Jr0TVzü-­j/¹Ñìpˆ­P0ÁÃŽ’ÜÃÄ´éWÃõyœŽöfmmM—†DP$ÑÀAvÇåM\ô¹œIO³rì…º‹;¬Sý2-©s ;nmí6ãjxßS÷ùv'³ÅŸ;ÿ÷A±ËPxUhY—²oTF¶©€×ÙÁü[/ÇbPž‚@Ktu´ˆõi©ÎÖžN–ô%»õd—Òåƒ_yH—}Ým¥Ë”ÿ–4v–›ý-xP¹Õ«R*JÚÑÆMÙîÕôùrÓ®W¿…w°ÐýÏÓ‘¾T@!C©Ád0TÁPšƒ R@€*!J&ƒ@ó!@•ƒ ¥“Á ÅþŽˆqþapo/Õÿ—N‹ ÐÁP&ƒ4=/»ürõ\·†ÈÐPÅ!T!áïÓSòÿK3@‡ e“Á â3sf^}zîmŸÍî\ü¹µ±tSnžxõíWžFÇddÛ uǺێp«Æ\®ðW§z+o½¼“¬Çåtq´XÙÔEÀa+ÛEç¿‹Îuk8÷Ú³[q)6ú‚M]¾²7+FªGó>B·ÿ¸©£è?kTÁP†ƒ@ L|ü !í×/=„ºçÞ$¬ºýRºéÛàçûã×6wmjc|ámâì §º¶!C›f=õkXcK«ÚwâSÇ]|LÌbS¸Ë šÖÌ€ß?ÿÎËÉR71ðÉ”ËO lÊ*zªGó÷®9íJø‰. „‚ò{4B0€RÑéÙÿ¾I\ÙÌÕÛF2ÿ:Ü­Zh\ʯáÑ´ü>;7àqäŒú}\¬huH-›ÐØ”M÷^3ÿC¸¯‹UWG Z°5P*=ø4†B°²]¤±u`M›&ÖF´0ÀÕzÆÕp::ÅÖ¢ b4}¾1_òÒo.àZéñËõ¡Ð0ÁP¶0 •Ú£÷éôüUß\(mibeÄdÐ{ñ©9yâVÕM¤›¼«ï}•œKËÍm>^´ð™¹pÇÃwñ™9ÓTìBê™0 ¦É t¦(ïeJfñ P8Z)<"Ú!@©Ôü÷ä_áÀ.3o/ëuò›ÅbZDù'ü±™9tkÄÿø kÀ•\ œž+R½ á²Y²G‹ÅÅ.@áhÅx´B0”9L@奟Ÿ_3r?Ρ&ý7Ïj”Fw´­SËø“Ï KÑù/=3¤AVÙ.¶BÝ i¥U€ŠÑ@ !Êr0TRµLôéön|j‹$E%3 æB›žÝ!ÿSHlF6‡Åâ³%±58&¹‘¥!ÓN»[éñÍ<å»|2e[┎&…§c„`¥l ÍmŒýJe¢gk {òUüØÔ„ÏRËæûÐ+=ÞgæÂÉs¯=³7Ôè*y_ÚoáÑ5 uë› oÅ¥üú4zl][»ìoï^Ѝóçÿyÿ…©³‘žnþ·TAÁPN0 •ÔÖÖn3®†÷=uŸÏawr0[ü¹óð0›–5q1p)zFed› xÌ¿õr¼ôî=mZø¹ÓOÞùF½ç³Ùý]­g5¬¡b—Ò-@õhÞ6ÆmmM—†Dl¸ûúBO Ù%xl*1„`(?ÈÁPUÓçËÍ­JÿÇÍúŸ§#ó™hrì„‚_¿T0#«l—®Ž²ÿºBvµ¨’ôÁ¯<ÜO€*!ª„`(W˜ M€ Pšä.EÍ„ å “ÁPá‚¡ @ÅB€*!*&ƒ@Ë]]h9„`¨0ÈÁ ŒxÛ˜Š.ÊkìO]T9ÁPå CEÂd0T„`¨r‚¡‚a2ÊB0T¼ŠÊÁx@•… Uš8tWE—å‡åéSÑ%€¦@Pþ“ÁÌËíp Q‚ASàâ`(#‡BŸ÷ÝvÖÒP7rÕPûãÙobz–ÕŒ½¹yyáK¸Zu@{1.êçfcR¡Ê&ÔPþ‚ JHÍ̽Ù¶vuiË_w^ò8l ÁX•‹uL¦î™× *„`Ð ˜ €²ãíjýGè Ù|äV5ž}ø¶«Ò¢Þ'gfWtE† UB‡ºöþçînàͼ -+÷ôƒ7 »6’†à¬\ÑÜ#Án>‹MÉ´35ÕÂmndžLgjñÝx2ì5ŸËîÙÀ©UÍjÒasóòýô$*9ÝÑÜpj»ÏÆ·©«¢Œb Å\®pqf×…\}¥Ïçönè¼y ·.£z—y½&ÿv58"ÆÖÄ€îx—zªk(Þhu¤Ûšßþ¦ƒÿw • B0h–J1|èì;³·XšFžYÏa³¥í‰ÉiVí&çŠòÂÿ\éjoUÔUìÅtxxø{7Çjê´—üˆe­Â €*¨}ÛEÇ$!ÒÛņVO„½r±4r¶4”v \xüÞ«=>mÝ«›†G¸Èf±ætl@›†í>÷MÂñIÌhÇ…ÇnJ÷šv0h×ÕÇÛ·jYÓ†Rõøý—)›éñ”•QÔ¡Fx×f¶ŽÛwyQ·F{F´ y7dç¿tˆu}›©ÞeÆï×Vôjlm¤7|÷…‘{.F­Êdze5o´íC[Þ{)ȯ‡£…°Äß%€òƒ §Rä`’šžu)äqÛÏëH[þºx›ÇåæŠÊïÏ‚][5  Nq¼Üލ!èÇäՄý ‘¡A}”ç¾þÌáPÈ &¹ÑËÓQº5>-sïµð5}švt·§ÕA]/…Gn¹F!82)ýdØë-[´©%¹”·UÝà±uòßZ·íÒÃo¿öÜÄ•VGµp ŽˆYuêÎÒ^ k(ÆPÒ :´iÍ>žÎ´`o*ôi^{OP8uV½‹w­®’;;¬Y­±¿ÒÑ«›è+«¡ÐŽF«&ú|ºµêÚé—â÷  ¬!“w×?΅Ȇà#çC¨ñìõåVƒ€Ïµ17.·ÃiŽG‘Éi]T>½=fºæß¯YvnÞñ{¯.Îì“Älºó:A”'nêd-íìUÃ’B!…ã°w‰tVÞ¨†…t“·«5“\o½ŠÏåµs³•n¢d¹=ðQR†â“á’ %{áDC{‹ÍçÃN?x£zêÆ,˜è6#'WE ·^Çc4€Ê !4Q¥˜ îÐì3ÿ}§6ù f>o8-#ëtPØBßîÒœ•;wÓïNÇ&¦ØY›êÑj™ÎÔâ»,àäÕ{|·g[ÏVžµ¥ÃæŠòm;ð÷•¨ø$ÇêS}5¾ßÊj½‚YÙ¿pòª}Á÷ŸÛZ™nòÒ¥e}¦§²#*;3ÚÅsl=zõÎS}]~ïv^›ç Ñåó”íU¼Š7ZÝÞóè¶fw?üÇ(ŠÎÃv]Žˆ‰OͲêÖ·3—†`æ­]²—1êJ–S2sè‹„‚›¤ËÌ^íýK/Œ¢$M·ÑÉŠOÒJ23á*»WdRºê]xœ×kéäŒƒŠ’3rŠ1@å… Jóspû&uý$ ˆÞ jÒê‰+÷\ì-m?þuÞ÷û€ãwö,íîbúdÄ¢ŸÙlÖŸ¯iÓ°ïvÜ }|ã4'[ËWî.ÜvTº×´5ûwýuyÛ¼a-=kQª¿|®€gd §fU3Öý¶br_k3£á vŒ\´3êÌz&v+;¢ÂÃèÞ’Ù:îû=‹|{Ò]y1dþOFºëf T]dQ (ÞhÛ¿õ½dWPÀ| ÍÅýBUd àvp·;r+">-³gCGÙMÆz’ˆ);ƒË,S;íE éÙ'>ß§gËîu`L»:6¦²£QÎVV@Q‡r0Þ}¯ó_xe¤dIö²35P½K‘j(´-ƒ PLÔ¾nQÿÐÙ›L>r>¤×¯ŒOJÝ{ìêšéý;6ÿŒVujz)ôñ–ƒç(Gƽ?yõÞ–9CÛx¹Ñ&ß>mƒï?§¨“ÿÖºm\øvt·Á›Ñꨞ­‚Þ¯ øgéøÞjVåÓ­E‹üz†uõ»4 2.©º¥‰²#*;œ4ýºyŸö’;eomF#ï9v•B°ê"‹T@¡wYáh´jb(¹ôÐÂİj^ %ÑÛÓiã¿÷#“ÒŽi/Û^ßΜËf_yÝÌùÃAÏ£-ŒLõuªI~êB_ÅyÕ°d6] dØ›ó8l­k½LKtr‡Íâs?™4•*ÉPWžE5qúpšM#Øé·s³-ÒÑU×PÔû"G¬£¹s !ƒæÒüÉàÞí¼fúðŸ90;'÷xà‹;憿Šf6ÝyòZ”—×ô3ig¯ºN”ö(‡={GwªQÒM£™Dxëñ«œ\Q»Æ?_©M#·í‡/&¥¦«YRC·ÚIÞ¦‘?]¤ìˆ·¿Tr¸ý”½hFÞ|à\lbʽ§oTY¤ ½Ë G( Jx£÷^23È^þ«“ëïÚ+OÝö°5u¯nzöáÛ}ןú÷“œ›Ù› [ת¶ìŸ[ulL̄މ |ÌìEyT ·ùÞ HÚÐÁüiLò¤ß®8šo^KáÑ‹1Ô±‰™AO(”7ªaq#"v÷Õ'SÚyºK‘j(ÞhÄHW2…|ôvD‡ºö5­ôxˆP9à' ø:·¨7ì»Á÷ŸS´µ01¬_Ë^‚“ós¤ìe †ºt›’–™’.Ù$Ôוn’.3{µ÷]Íþï?»ŠD’ÿeŸ¬fI<.GvõÃ%€JŽ˜œš©äp®’d&\ÿÛEòV˜´Œ,ÕE©€Bï²ÂÑJÂXß¶vu' CK~ÓÆÍ \ŸÝãÓ2)ü­íÛtBwfÓ¾‘_Œý%°Ã†ø\v÷úŽkû4ëµõ4³iCÿæ )/¾{Ÿn.ôhยg“³Þ(+ ¨CIw\Ý»é†s÷ÿüŽvü¦i­…]¼ Ý¥¨5o4ŠÔ_Õµ›{$xù‰Ûw¾ëM!»Ð]4B0h4 Ÿ 6Ðthîqä|(…àž_xÊn2Jâ¯ì .³Líº’4™.ó–Þ§¤Ëîu`å¸:NÕeG£œ]¢:•QÙálÌî†K^ÂSÒ3¥ßuò£|1Š,ÿ» «§³ì?qøgR'…›\κ¾Íè«à¶&r¢Ò½xöÒîŸÓ—Š#–d()3áñIò“²Êv‘+@nUE Å·SS:+¬@“!ƒ¦Óðܻׯ_ÏDƽ?¸r¼l{ýZ\ûÊí§Íê¹2-AwŸ9ÛZšÔq–¤½ÐG^u™M—B3 j;ð¸­k«LKt|2‡Ãæ—ìϋʎXèá®ÜoâáÌ,‡>zicnln,,F‘et—5ö§4B0@‰Pt½x—™±ìå¿„ZFtoµr÷qW[wÛ³×ìû'Ȧä£ì­ÍZ7ª½lDZ:NÕlÌÿ¼Jù˜Ù‹"ò¨ž­æo9Lq³¡[§¯¢'­ÚçXÝbx×%)RÙ•îØ†©ÌŽ_¡àÞ¨®ã°»ÿºàxà=KF»»Ø†>±èg6›5Ççëaßí¸þúøÆiN¶–'®Ü]¸í¨t—iköïúëò¶yÃZzÖ¢H=~ù]oD÷–j–4cÝo+&÷µ63¾`ÇÈE;£Î¬gzQhd G›Æ}¿g‘oOª?äAÄù?è®›1°Ð"ÖP컬p´íßúŒ^²+(`>…æb}ëÊB0Tz˜ 9”Õ¾nQÿÐÙ›L>r>¤×^Ò­ñI©{]]3½ÇæŸÑê NM/…>Þrðܰ®Þ'¯ÞÛ2gh/7j÷íÓ6øþsJ:ùï«ÛöÇ…oGwܹ­ŽêÙ*8ìùª€ÔÁ>ÝZ´È/†Ž2vi@d\RuK“ȸ÷E=âÒñ½iuè×Íû´—Ü#{k3yϱ«‚ -²` ”Ë}—Þ#C}j±01¬‚—‚@¥ƒ Ú9äônç5Óÿ€ÿÌÙ9¹Çï\Ü17üU4³éΓע¼¼¦Ÿ¹H;{Õu¢Àw)ô =4ªSCÚNšI„·¿Êɵk\Wº©M#·í‡/&¥ª{ýkC·Ú é6#Kò½°gïŠ~ÄtZ–½hFÞ|à\lbʽ§oTY°†oãŠ}—Þ#€J!´Pçõ†}·#øþóø¤T Ãúµì¥!89?Æ1×0 téö]ì{ºêëJÛ¥ËÌ.í}W³Ù,¦E$Ê£Ûèø$5ëáq9²«Ì9{JzFјL·Ì„ë»tò¯{.´È‚5«¥£T.Á %0 ² ôš{9J!¸çž²›Œ…’øËL©2˜åê–&t›žùqFó}Jºì.VŽ«ãT]v(³»áoŠ_¤® ¨G¤X¯#IϙҖ”4É2åøbYŒJx—4B0h§Þí¼6þz&2îýÁ•ãeÛë×ràrØWn?mVÏ•i ºûÌÙÖ²y}Éj裯ºŽLû¥ÐÇÌBƒÚ<.‡†êÚªÓŸÌá°ù¼½ŒÖq®^¼#^¹ÞÄÙi}ôÒÆÜØÜXXŒ"‹]€j˜€J!´&ƒ@¥·Ñ‹w™È^þK¨eD÷V+w÷pµuw±={ýÁ¾‚üg´·6kݨö²Çê8Uw°1ÿóB(…cfS#ƒQ=[Íßr˜âfC·O_EOZµÏ±ºÅ± SKRa1Ž8¼k Úð÷Jíê:Þ{±û¯ËS}Y¼"Ký.å_Xrô|h‡æ5¬õü’<>e !´ r0H õÚ~^ÇÉÖ‚ù02Yg6Ðãû,ø9>)•‚ÝÚé&ôkGíû–», Ãøµ|§{ϵÓû÷š±™ÙeìÁfFÂI+÷½‹M47öhã¹brß’YÔ#2u¼zj¿ ¿žbÁv7Çj²äúãˆÄ¡šùñÓ ¶«Y¤ìjÉï²Ü{jËŒ‚…h „`Ð6˜ €B!ƒBÕ‚* eW_@Q!ƒvÂd0¨€ UB0h-L€2Á Í€*ÌY@ùC€*!ª(æ?H±<}*º¨ÁPu¹ˆ]*º(WÏXÏ*ºPNP ¬!€ÆÁ9jUƒsT(ÁPå @•ƒ PâlqòÉ)¿¤ä<ÊçŠy.<ƒÞ&³MØBvE—E€  .q¦8²CdöÃl³EfzíõtØ:g3æ'¤M³½bË6D¨4‚4¦š4Sâ’ÄÌ™v!vü:|¦…~=u›ê¾ùüMòOÉ&3L*¶<PB0€ÆÁT€f¢3Ò¤“ŒFI0ƒ_ŸïðÄë(yIg‰æ&¤HÅŠ¸v\ÃQ†¦sMuX:©‡R£ûFÛݲ‹›—u=‹kϵØ`A»ÄŽŽÍ É¢ž-ô»è«Ù­ð…ØÅMŽË ÎâØr,7Y2»€,„`ƒ©&Í”}/;/1O¿£‚@É$`ë›~<Ýjߟ˜3"†Åf™Ìùðk?;Þ|…9ÇŠCí1£bøõøæþæ3NÌÈúrŒrT·«°ÍÈÁš3üã. !@³`ª @c‰ÞŠè–ë ô¥S/JÙ›b±Æ‚ ÊÂAÂŒKI[’¤ÙÔh˜‘®·.-7ŒkäcDç·ÒÕÜÈ\5»±,Õ2ô1Ôm‘¿Ë0ÃØ±’]¸ÕñŠð üJhL5h®üŸpqžXÙöì;Ù:"AS´Eà%HÞ–Lá˜Y¥_4fm,¹®‰ßà“Uq†XÍn9rTHÐðÃ&¶Ù'#€B0€fÁT€ÆâÚK~ÂsŸå vÈKΣ[¶ÑÇ ÷™‹øÅ)2(‹÷É™¢ÜªŽXq{ÁnE=20@xÅÐ0˜jÐT¼º<Ž9'íhšAo¹Mi¤ñò™_Ÿ¼¤ÐbÁšSMšÌd®IæÕÌÈŽ‘¦ß™t5ÐèdœËH˜“@™˜ZX–áÃ÷+ßó=ø|w~ÆÙŒÔ}©æþæ¥^ÛŒ]>ÐbÁZâþýû;vìøá‡rrr6lØ0yòd¦ýòåË¿ÿþûÏ?ÿŸŸ™™ùþý{ê9uêÔN:ÑÖ¼¼¼]»víÛ·6QªG__øðáƒ.Ib±øÚµkYYYeÿXV0L5h2ŸesÜ&ykrÊî”ÄE‰’ÿeãÆ3oj4ш~_¨ƒÅF ¶;Æ'&/>ëÈ5_kn<Á¸,*)·h+„`-ááá±~ýúÇ;88Ìž=»eË– 6¤öùþüóOÚJ«ß|ó …KФÒDK˜ðJÚ·oôèQf™ÅbI»Ý¹sçÈ‘#Lû„ Î;G·Q£FL …]гם_§q(÷ìÙ“Bó/¿üâêêÊt4hЉ'öîÝ[’:¥GÔz˜jÐd’3Õ‰Æô¥x«€e¾Îœ¾äÚ…}„B±°ÐUž+OnÅ>H!k6›½ÿþúõë÷ïß?44T(T뉯M›6ÌEU…\\\7nL ^·nÝúàÁƒÚµkK·êêêúøøÐ&ZÞ¹sç™3gž={V­Z5i‡š5kR¶¦LL)™™.^#GŽT§›ÀT@YCÖ6Û·oïÛ·ï¸qãöîÝ«Î.~~~ÌB¿~ýv Ú¹sgZøé§ŸZ¶l)›€VVV¾¾¾´°gÏž¶mÛÊ&`†­­í_|±{÷n&¯ÎñãÇ«ÓM;`ª  L!k¡>}úŒ=š"f»v톮¢çÝ»w>,{Éj!!!Êf‹÷ïß÷ññQ¸ÉÍÍBpñê|óæÍŽ;Ô¯@5„`í´~ýúË—/Oœ8±iÓ¦”>å¶R½pá-ÄÅÅQUØ÷ïߩ蒒b` ÿ™ }}ýäääâÕ™””Ô½{wõëP !X;Qâüí·ßš4iÒ¿ÿëׯËm•¾áŒ™ VXJÀsUt066f>h¢ j711)^ÌL°úu¨†¬µêÕ«·zõêI“&MŸ>]E¢þ˜ŸþyHHˆÂM™™™ººº 6|øð¡ÂÔ®ðãÔ©ÓÎÎ×B@•’z(5ºo´ÃCžO›Ž 9‚µÙĉÏœ9óã?šššªè–““³~ýúY³f:à¸qãzöìêéé)7† üüüFŒáããóæÍŠ­²^½zuáÂeo€S³NM$ÖyaòÂ.ÄŽçZÑ Rs*¨ ‚µÜÎ;ëׯ¯ìÆ­[·ÒÒÒÔ­{÷îÓ§O§Ûýû÷·lÙ’iÌÊÊÚ±cGFF-8ðÔ©S=zôøí·ß¤ŸüäÉ“þýû2¤oß¾%©J]9Oÿ`¶ *#q®˜ùhBe²e3ÿc¼ÂiN%•B°–¸ÿþöíÛãââ&MšD‰³E‹L»¹¹ù/¿üÒ»woüÿ Ç|>%γgÏJ÷}ûöí°aä«piáÏ?ÿ¤€;yòdÙ<[³fÍ—_~¹zõê3fèëëçä䤧§SÞ2e ÓA#øúúR,æñxÌŒóóó0`€šu†……­X±‚~ýõר¨(* nݺe÷ÐUš3Á£9•” sVVýbõĉ™W3Yú,ao¡Åf –.‹Ùdý»uÂì¶9Ûî†8Kœ07!õ@ª(VĵãŽ24kª“Ÿ_×}M·¯j¾¢[çLgeÝÄ™â„ÿ%¤ìKÉKÌã×盯0×k§'iW>²ÒÊ÷§&~Ÿ˜ó$‡JÕm­k¹É’ëÈ•«Ä1Ê1~v|ú™tQŒˆmÈÖk«g±Þ‚ëÀUX‰Üøñ3âSRl¯Úòjá×´B°–ðððدà¦6mÚÄÇÇÓ‚W¾Í›7«jP¾üQY‡ù ¡Øuº»»ïͧºNm‚©&€Š7.Ît‘©Õ«¬¬˜!1l#¶ôƒ±“·&[í³â9I‚`¬olúñtêÆwçgfÆŒˆa±Y&s$oöµÜn;:Ö6È–çÈSÑ-vBlÆ¿6‡l8Õ9Ië’"¿Ž´¿kO)SÅ. Qö•üõ)&Få½Ï£CÓªm ­\%ÑC¢sÂslØp]¸¢hQÜ丨îQv·ìVbúÝÇKÑè^'ý˜Tý\u$`ÐzÁeSMr•ÈÂlhá†Â>’õµçú¦ìI‘†`a¡n3]ZÅ‹Rö¦X¬±Ðï¨/i$̸”‘´%‰‰ªl6Ýr,8:<eÝ$#¤Xýd¥ÛR2 ùzsQ’('"‡~÷UŒ¬Pöãl<ƒ~L:·9bC¿eÌ&i%ŽU€•äNUç2·ÆãŒ£G‹"E:|‚•äFä2#¤ŸN›g}Àš¹ãÚ ! Ìaª‰©Dö˜m ¡ÛêcÚ4$mN¢sÅ«ÌBöl‘Ž ©àcO/Aò¶dжsŽ´QE·ìÛ’MtBÈ´³x,ë_¬i~M”í¢´àÆºlcvôÀhãIÆúôùuø:ö º±X¬„… 4>£Ò!Ä"15æ¥åå>È-X “'oOÎ˦'óUæ=Ü;€–A(s˜jb*‘ŽÙ&ÐÌ4ƒ%”ü}Dœ&þ°IÿÃ&æ :}ý¸—¡dYœ"Ö‘¹žVE7fK_þï/ªFV‚cÍ¡³Ð÷ß¿O\œ?#žÎléÔTîWIœ-~Ûú­8GL‰–ïÁ§¤›~2=nRœôˆ+!”€óRór#s•@Ë ”9L51•¤’|úf›@£ä¥äÉ-³ å"ý:H¶&ÉôÌ_fÚÕéÆl*mÕYýJZíµçŠ3ÏgÆM‹‹êUã] ÿcÙ™W3sÂsª¯¦ßYŸiaqX²GT²é÷ëÈK{éµ’¿~ @û ”9L5ÉÂlh”Ì+™ºM>ülg‡fsl8²§ A}‹Ë’ôüï· 3(“çÌc›ÊDU±ªn’sBŽdUÐXÀtŽì)ì/4ènPøÈŸ¢ÓÈܹú]ôiG½/õLüLb¾‰E‰˜Ëñ™ÁÅ©’ßnúEþÐ"ÒIÞ•ÌlRX‰N~GÃ!†<7^úßé1CcìïÚ«âZ! ÌaªIf›@£¤¤Pè4dÝÈJÞl2EÁeBl3¶áÃ÷+ßÓùfœÍHÝ—jîÿáô”9ÃL;š¦ßA_8P¨°kᆉß'òêðx®¼äÍÉç3h“ê‘¢ü7>Îâ ýöút>™²'…kÏåÚre+aþó~Å{³åfy y ‹( Ó¤´mÐ× `%¦óMÓÿIgƧ3Þן½Žk½Ïº4h̓ Pæ0ÕÄTÂDÌ6F1_mž´!)cpã~chºPñÿ­´ØhÁ6`ÇøÄäÅçÑYœùZsã ÆÌ&½Özú_éÇÏO\žhlDZà(ìfùƒeüìxú±—|dŠßæoÉ…F*GVÈh¤ý2&.KŒõeë³u½u«ýS™Ê•V"y÷í*ó¤’^¹½â9ðŒg2ʾ—;&–eÀ*X‰ìçr¬8–;,£ºEt1 L_1€†B†Ò±|ùò-[¶<|øÐÐа¢kÑ8˜jb*¡l-=f›@CPF¬v¼š\£°P(þ$ÿ±,óuæÒ·´ÊmªvêãJ»é±,6YÐWÁÝîR°†ÿvÐ1oJ_…Vb2ë“§Ûk¶Òå‚•ÈË «‹ØEÁ¡´ B0ß»wïžœ¹mÛ¶‚U3uêT* ^½zÌ.”†gÍš% Á”iÝ»wÓáhØò{à êAÖÛ¶mëÚµ+…NZvttìÒ¥ËÏ?ÿL¹S¶Ï„ Ö¯_òäIÊ»´J ˜21%`ZîСC^^ÞéÓ§iÙËË‹R/õdöâp8L&öööQQQ¥R ¥á£GJWâââD".  „ÁT€fBÖOž<éÞ½»t•¢çü!ׇé6<<œY}öìYZZÚÝ»w™Õ5jˆÅ?ÛUOïÃg¸¾|ùrþüùaaaFFF¥U ‹õÉgå.X°ÀÏϯfÍšƒ3f ¥muP ÁZE6ÂÒ²\Ê$"‘ä?år8iŸ!C†,]ºT¶Ü|-éÛ·/Ý,\¸pÑ¢E¥RŒœ™3gÒ<¸eË– 6ܸq£víÚê ¨‚µEÆ·oßJW_¼xáææ&×çùóçtëááÁ¬:;;3-ª…††úùùQ.Ýbä„……¹»»Ïš5kÀ€G¥ƒªDõ!k1cÆ,X° **ÊÆÆ†Bç±cÇV®\)×g÷îÝ...íÛ·gV‡ºdÉ’àààÆ«™|úô)³œœœ,m§XüêÕ«b#gäÈ‘</%%E'ÿÚ ÕýŠ !X{Œ?žâc‡LLLæÏŸO-̦¿þú‹R,5fggŸ8q‚Ïç3í~~~™™™ýû÷·´´¤´êääÄ\)qöìY//¯.]º0ÝÖ¬Y3iÒ¤æÍ›×©S‡¹ªaÕªU³gÏž>}:mºÿ~÷îÝ322¨ýÔ©S^}||C{QŸ3gÎtîÜ™ÍfÓ­®X±bΜ9´`aaáééIý):Óàýúõ+ïGª „`íAñtN¾‚›(¤.X° `;EÞÅùd×­['×m@>éêŽ;˜¹}e¯-VXÌì|ÒÕ+W®Èn=v옒{PÊ‚«Ù÷¨B0T9ÁZnÚ´it{øða+++é%ÂUB°–óÏWÑUh„`¨r‚ ÊAóŒõ¬¢K-‡ U^e4>ÕÊB0TQx•¨Ê‚ ÊA€*!ª„`¨r‚ ÊA€*!ª„`¨r‚ ÊA€*!ª„`¨r‚ ÊA€*!ª„`¨r‚ ÊA Ó¹sçdddÐj‡Nž}jÕª¥¢çýû÷õôôäúÐ&úiÿûï¿ {öìÙ¯_¿5jPNJJzñâEppð±cÇþüóOê¹`Á‚… ú(AQáÄÁðÑÀ)ÓÂÁƒiYaŸ={ö$''Ós4½Üzøðaºíß¿¿lãê|*Ž;~üøçÏŸkøßL5§HÍ©¤_èO¨I+Oe)ø®[·îøñãÑÑÑ”tÏœ9C?E {RòÞ·oŸ‰‰‰¿¿¿¥¥e¡#SçÚµkÓ)%×Ý»w«ÁÄÞÞžy¸Ú¶mK0E¨Ñ£G_¸pÅb)ëéîî^pœ   ¯¿þ:11qèСÂNôÒà[·nU8Ó ¥'ÞÀ@†(×ÒSCvv6e”””CCC¹Ož<¹~ý:-ìß¿_aþã?x<^ïÞ½Õ?èãÇ·oßÞ®]»’T^Ö4§HÍ©¤*uñ•…VžÊÓÑ¿ùæZþöÛo©6ssó‚ÝV­ZEÉR,‰è!3f %àmÛ¶Qî lÙ²e¡;ÒÏp½zõÒÓÓ/]ºôÓO?;Vý;òêÕ«.]ºP1bÄÏ?ÿ¬¬eè©S§9rDý‘ËZå=÷Ɖ7¨€ ™ššvèÐáï¿ÿÎÌÌü믿,×aïÞ½ÌÂ?ÿüóþý{Ù­ôòÔ©S§"]Äæçç'{ ¢¬‚S,EE‘åLs*)†J]|e¡­§²C‡ݱcå΄„„yóæmݺU®CddäÚµk=<<”ÍË9sæLLL =4 …`jÙµk—:!ØÅÅeÉ’%3fÌ åÙ³gS¨µµµUó^L™2…gcc³qãÆB;+œH®•÷ôµòVå!>1pà@ Á:ùÓHr!X,ÿòË/Li!++‹^)GŽ)ÛáÈ‘#yyy²HÇŽ>|x||¼tÙþÓ¦M;}úôƒhùÔ©SÒÈ+íF¯Ä/^<~üøÕ«W©[jjª««ëÌ™3 ^ËH¨$z²£ªîß¿Ÿ””dllL/!ô 7zôh@ íÖ·o_ªS$é|ú¾u__߀€Jÿr5Z¤B¯_¿^¾|ù;wž?N¯yÔ™^öè‰xÁ‚ŽŽŽeT‰š@‹-®\¹Â,Ó¦M›6ýþûï¿ýöÀ¤§§×­[wÖ¬Y  o%îСC;"""èĆ ^½zµÜŽêÜSÅÓ›ŠŸõïÕ°gÏžGѲÏ7ß|C?{Ô’‘‘Q³fÍ9sæPŠRñ-ÓZ|*»eË–† ÒèçaìØ±r{|÷Ýwiii.¹\µ^×耞OêׯO?êÖÖÖÑÑÑô‹@¿…î;uêTz†¤s‰äää &=zT#ÒéÓsܸqêeúôéT›:#—µÊ{úZy+‡ò ŸèÖ­›¾¾>…!J*ôündd$ÝÄ„¡3f¬]»V'ÿ%D.SLÑÕÕíÑ£‡´¥K—.±±±íÛ·ÿ÷ß «w>fê¥I“&kÖ¬‘ëpóæMJNžžž;w¦b¨Å:(U%7ƒõîÝ»Ž;Þ»w¯U«V6l öìÙ3Z˜4i½^R¾¬V­Ó“^çè•R(ÊkëÖ­›7o¦R)v©H…èè?þø#3þ|77·ÄÄDz…Þ½{÷‰'(/Ò+n©W¢þ#páÂ…àà`oooZ>w“½üSâ¡b(n†††Ò‰Ðµk×(?½xñ‚~(hæääÄÄÄP¡ æýIEº§*ЧD®â'Dýû57“·(ÛQ£——ówgÊÇ7nÜ XLé™~¼ÕùöUv•÷TV5)S¦ÐóU8qâDé¹ Ûµk§µmÛV¡èÔˆî)åWü+‰éù„~¼©6:ë6lX¡»Ó.?ÿü3=5eggÿùçŸô»L‡.t/œYèÕ«—:EÒ¥N7FYœxëöý­Ê'Þ:8÷Ö Áð  ôôDσ† "Ý´wï^ʸß~û-¥¨º¥€"}ÿ5=§ÐK]×®]åþüJO= ߟ®“ÿÌ(]611‘]eÔ«WoÙ²eÒÕÖ­[ÛÙÙÑ e ÙLOýúõ£œDO뀘·¥ñÅT}šùK5=z”–™wgÓ+ÐÌ™3éÉbÿþýÒL/ôôJ'ÁeZó ž’’"ÛxàÀüˆLYM®»víE å‚éEÔ××W¶…žñ™…´´´R?\i=ôÚP«V- ÁôÝ—Û$r~™ÞÓÒº_L¤£ÔÖS‰TºSYõQ Ù¾};ßÈÈÈ%K–¬X±‚žŽ(YΚ5KÍ(BÉ~<sEÄÆÅb1ÒI—:ãPúù矛6m*‰vïÞM³ê7`QJ£[…ŸkQr8ñÖјo*yî]I!ƒ< 1½{÷Þ±c3Ä\Ò´gÏKKKÊÇ:ùÛÙ¶m[z¢ÑÇ×®]›âÅ™3gzõꥯ¯_þ߸qCGÉü å$jôèÑÍ›7Ë¿0F^>éªêwÔO)>ÊþÇ´]Eý¥~O5ü;«É*Ý©l‘P6ªW¯^vvöúõë)Hݾ}ûÈ‘# '2úý÷ßsrräæ¡)[3ŸØ@™Œ’™š“µ^ÿoï^C¤ªÿ?€EEv±„(£Õn¦™„!ô@— K±è¶‘IYÓèJJFeõ ñ&ù,+°¿—–¬-ZZZFDõ ŒÊ´ÔÍ ‹”² T¿s`Xvgfg¶½8û}½Íž9sæ{.û=ïÏ™™ï5*¶^6ØÖ´iÓ"„•é³/ݶþRiRxç¥Â;—dí]‹„`Š˜4iR„à\þ2R„à~ø!N¥·ß~{ágד'OŽœË§0*æµkׯ ©ÍÀ¢=æ»ï¾Ëå_ôÙFTÚ»woO6iß¾}‹/ŽÍòÕW_uǨ^Üݺ¦‡àž­5WÊV%Z;sæÌyóæE–;wî¸qãZ‰¹CÑkÅv8ýôÓ[ßf9jªlŒˆd›6mºøâ‹+\Zt€Q]Äñ¿cÇŽ‡~8û²uQÑF?Y~°‚Rñf{­B ïNSx§F¦ˆúúú8GF¼È.#Å9ãï¿ÿÎ. eâü™ø?þÈBðk¯½¥së³zÒÁƒsùOÖŠ>›]wé$Z'Ñ‹.º(Üi§¶páÂáÇgר.¼ðÂnzÇÞÚݽ¦‡Úž­-µUÊVköìÙ+V¬Ø³gO. ÜÊ_¸k×®-[¶DZ*sSåeË–U‚<òÈØÎÑgÆ2£%±Ù _5n#’wKKKa\‚¢š››ãß*‘:"~L9çœs.¸à‚C°Â»K(¼S#SD”à×]w]t4qR\³fÍòåˇÚºgïß¿ÿĉ_}õÕíÛ·oÞ¼yݺu Ýô1_‡Ž>úèDS‹>ûçŸæò¿§é±ö<øàƒÑ“öë×oýúõ±Ýzà{k t÷šj{¶¶ÔV)[­£Ž:jذaYŽ˜Xù ›šš"­>÷Üsƒ jóÔ÷ß?eÊ”\þžyO?ýtëQ5Ê3fÌm·ÝöÌ3ÏÄnll,u¥0þG"ïܹ3PêëÏ?ÿ|ö Rïo¼¶mÛÖaÞ o:G¦¸I“&e×WæÏŸµïܹsÛÌ0yòälØËéÓ§GÇÑ‹N9唈J?ÿüsÑg³O<+¿¥Ó·aÆ\~¥žIÀ¹ÞÛݽ¦‡Úž­-µUÊö˜Hüuuu·ÞzkÑgçÍ›÷å—_F‡¶zõê©S§V¾Ø ¬]»v÷îÝ[·n}üñÇ‹Î3vìØ7Æ!ýùçŸGzëLëKPxw…wj„`Š=zô!C¾þúëHÀÑã´¿ïÔ„ ²_ÞÄ9cÀ€—\rI¯´3—¿°mÛ¶;vüóÏ?m¾^öûï¿ïܹ3×WDÚËB[a¸ÊÐ[[ »×ôPÛ³5§†JÙžé3Bê¬Y³JÍpíµ×f[iÙ²eU…àcŽ9féÒ¥Ñ+ÆãÇ{¬è÷ªo¸á†G}4‘cΜ9Õ7¿$…wWQx§F¦¤ë¯¿>ø°¾¾¾ýœ‡~xœ0²﮾úê oUÚ^vóªì&FnçòåËýõ×÷ß¿Í0¥o¾ùfö½ºH­§G)óGêŽFÆÂ÷ïß_¸ýR‡3ÿ÷–tb t‰ªÖ4Wý¾î­õê3j¨”í«V­ÊåoQj††††,Ç!Û䬳Ϊ|áãÇ¿ñÆW¬Xñg^û9xåÊ•K–,™1cÆ 'œPý§ðî* ïÔÁ”ñ" Á¥nüçÔ,ÿ—EãLðÓO?mݺ5º˜6·Ç¬Ðe—]6f̘wß}÷‘GÙ°aÃa‡–Mÿí·ßfÏžËu>nܸÖ/>|ø|ðÉ'ŸÄ<¥Fït#GŒ±eË–O?ý4Ò[ჳ¢'Å®jI'¶@—¨jMK5¾ŒÞZ¯¾¤VJÙ-ljjŠ$ZV¶½‘#GžqÆÛ·oÇ/¾øbûkçå=ùä“QžeãµhÑ¢M›6µ´´466677—¡ZÝZxçŠí_…7}ƒLIÆ ;ï¼óâ|PêÆNP ô×_;¶ÔB¢žÎ:Ž\~˜ÌöcyŽ?~åÊ•?þøc”×ñŽQjÇI"{*âTt^…è ëàÁƒ…£›{ùå—# eMŸ>=»Ñeœ¾øâ‹óÏ?ÿ¥—^jó¦wÞygDÏ\sÍ5wß}w,*a®ÔÀæíùᇖZåXxDÃ즬÷Þ{ol>ú(ÚëÞ¾ïî’–T» {¤ýhM…)1Oë_‡^RxPíš–j|™#¤ªõÊ~Ñ’«ø°ID­”²= bÍ7ß|sÿý÷—Ÿ-æùóççò#ÊÍ™3§ªœÛá©§ž*s¥ù¤“NŠ,u饗®Y³æŠ+®xá…ân?[üGT5¾l·Þ¹bûWáMß SNœA?ûì³6÷P-ˆŒ3D°(ômÜ|óÍÍÍÍ…ßÒF‡;dÈU«Vµþ8é‰'žØ·oßÛo¿½k×®è°"EwÜqÙS›7oŽÇqBºãŽ;â<=cÆŒèø²§bú”)S–.]šý'’xaüùÊ+¯Üu×]qþ8þøã£¯”ÖØØXJ½ Nüû÷ï_´hÑÆ#‡}öÙ£GŽ®íÛo¿Í>09räĉ ßÛkÓÈò_ŒS`4`áÂ…1ÿ{ï½ %G:mÚ´õë×ÇÄQ£F=ôÐCW]uU¶¤ò-pË-·¬^½:{üÖ[oÅl7ÝtÓâÅ‹gÍšù—_~ÉžŠ/ÿøããlé|÷îÝÙô+¯¼rèСï¼ó΀ªZÓ¢ïð©p½¢…Ï>ûlá°9öØc#ùÝsÏ=1ñ¾ûîk}ØÄ›.Y²¤Ìîëcj¥”­J´6þA ÇjôQqØ”YT´?j§ì^qDíÙ³çÔSO-:g!˜rxàò3,X° Ì³ÿŸW~ \·n]‡-¹5¯Ì GqÄÝy.*s{^›‰&Lˆ³Ó,˜š×fb©%tUK*Ü/浟þx^ûéWæ•ZZUkÚ¾ñ—;ù䈭E_Ò¿ÿBûïÍ‹ÄÙþ–u±‰ mËÜ•3ÇAX߷÷‹èkÝþh'žxbv_º¦¦¦¨'£”Y0¢RÔxgžyfÔ¥Q“Ô××g·úëP·Þ¹µwâ…wNíÝ'Á}Y •²•ÈÆ®J›h[ʹ“sk‘¢J /ÐZ]]݃y]ò¦ÝWxçJïß” ïœÚ»O‚HŽ @r„`’#!€äÁ$G 9B0É‚HŽ @r„`’#!€äÁ$G 9B0É‚HŽ @r„`’#!€äÁ$G 9B0É‚HŽ @r„`’#!€äÁ$G 9B0É‚HŽ @r„`’#!€äÁ$G 9B0É‚HŽ @r„`’#!€äÁ$G 9B0É‚HŽ @r„`’#!€äÁ$G 9}*÷ëׯ·›@ èS!*!!€äÁ$§¶Cð¿ÿþÛÛM öÔv€N‚HŽ @r„`’#!€äÁ$G 9B0É‚HŽ @r„`’#!€äÁ$G 9B0É‚HŽ @r„`’#!€äÁ$çªÏÀ°¦éIEND®B`‚yuma123_2.14/netconf/doc/yuma_docs/server-call-chain.txt0000664000175000017500000001517314770023131023356 0ustar vladimirvladimir netconfd PDU Callback Path for a Database Node Andy Bierman 2010-11-17 ---------------------------------------------------- Note that generation can occur throughout this sequence, but that call path is not shown. Refer to documentation on the 'agt_record_error' and 'agt_record_attr_error' functions for details on PDU error handling. 1) session gets incoming PDU - Uses Session Control Block (scb) to stream input to agent 2) agt_top_dispatch_msg(scb) - Receives the top level element in the PDU - Determines the YANG module for the namespace - Dispatches element to one of its registered nodes by (module, /path/to/elname), or generates an error if no 'top' callback is registered for the node - use of ncx/top/top_register_node should not be needed unless custom top-level incoming elements are added - agt/agt_rpc/agt_rpc_dispatch is registered for - agt/agt_hello/agt_hello_dispatch is registered for 3) agt_rpc_dispatch(scb, top) typedef enum agt_rpc_phase_t_ { AGT_RPC_PH_VALIDATE, /* (2) cb after the input is parsed */ AGT_RPC_PH_INVOKE, /* (3) cb to invoke the requested method */ AGT_RPC_PH_POST_REPLY, /* (5) cb after the reply is generated */ AGT_RPC_PH_PARSE, /* (1) NO CB FOR THIS STATE */ AGT_RPC_PH_REPLY /* (4) NO CB FOR THIS STATE */ } agt_rpc_phase_t; RPC Callback phases: 1) Parse (AGT_RPC_PH_PARSE) [NO USER CALLBACK ALLOWED] 2) Validate (AGT_RPC_PH_VALIDATE) 3) Invoke (AGT_RPC_PH_INVOKE) 4) Reply (AGT_RPC_PH_REPLY) [NO USER CALLBACK ALLOWED] 5) Post-Reply (AGT_RPC_PH_POST_REPLY) Parsing: - receives 'top' (== ) element and saves all attributes - creates an rpc_msg_t (msg) and initializes it - looks for NETCONF message-id attribute - parses method name node (startnode) - finds corresponding object template function for that method node (obj_template_t:OBJ_TYP_RPC) - (ACL) checks if user is allowed to invoke this RPC method - parse the RPC input section, if any (agt/agt_rpc/parse_rpc_input) - agt/agt_val_parse/agt_val_parse_nc is used to fill in a val_value_t tree based on the XML input and the RPC object template - check for extra input after the element - validate all the YANG constraints on the input (agt/agt_rpc/post_psd_state) - ncx/val/val_add_defaults used to fill in the missing defaults - XPath (must/when statements) checked in agt/agt_val/agt_val_rpc_xpath_check - instance constraints checked in agt/agt_val/agt_val_instance_check - the user validate callback is invoked if it is non-NULL. - If the RPC function needs to return data, then the - msg can be updated 2 ways to do this: 1) callback A callback can be used to stream output directly from data structures to the session. An example is in agt/agt_ncx.c for (get_validate) msg->rpc_data_type = RPC_DATA_STD; msg->rpc_datacb = agt_output_filter; The data_type is set to the control the RPC reply encoding: RPC_DATA_STD == rpc/output objects will be encoded into a NETCONF element. RPC_DATA_YANG == rpc/output objects will be encoded directly into the element. The function prototype for rpc_datacb is defined in agt/agt_rpc.h (agt_rpc_data_cb_t) 2) static queued data A value is created somehow, such as val_make_simval in ncx/val.c then it is queued into the rpc_dataQ: dlq_enque(newval, &msg->rpc_dataQ); 8) [return to rpc_agt_dispatch] VALIDATE PHASE: - Check if a user validate callback is installed for this RPC method, and call it if configured. For example, the agt/agt_ncx/edit_config_validate function is the registered callback for the operation. 9) edit_config_validate - check the target config exists and ok to write - get edit-config options and save them - call agt/agt_val/agt_val_validate_write for the parameter (if present). 10) agt_val_validate_write - validate_write_val - check nested edit operation and convert it based on cur/new heuristic - if editop okay, then invoke agt_val_invoke_callback 11) agt_val_invoke_callback - invoke_btype_cb - invoke_complex_cb - invoke_simval_cb 12) [return to rpc_agt_dispatch] INVOKE PHASE - if no errors and an invoke callback exists for this RPC, then execute it. Since the 'rpc' definitions are loaded before the callbacks are loaded, it is theoretically possible to pass through a vendor RPC call with flying colors and it is really a NO-OP, because no callbacks are loaded yet. It is TBD to fully support this sub-mode as a pre-provisioning feature. 13) edit_config_invoke - retrieve parameters stored in rpc msg 14) agt_val_apply_write The invoke callback is called by agt/agt_rpc/agt_rpc_dispatch For , the agt/agt_val/agt_val_apply_write function is called. 15) [return to rpc_agt_dispatch] - TBD: Need to document rollback-on-error at this point - TBD: need to check for a partial operation, and if so generate the proper rpc_err_rec_t struct for this complex error report PRE-REPLY PHASE - check if an RPC_AGT_PH_PRERPY callback exists and invoke it if so. (This is for agent post-config-cleanup, etc.) 16) REPLY PHASE - check if an RPC_AGT_PH_RPY callback exists and invoke it if so. (This is for agent-specific reply generation, which is not expected to be used. - if no user callback, then invoke send_rpc_reply instead. This is the normal case. 17) send_rpc_reply - check msg->res, the rpc_errQ, and the reply data structure to see what needs to be generated. - build a namespace prefix map from the element, in order to use the same xmlns decls as the request. If (msg->useprefix is false then the default namespace will be set to NETCONF 1.0. - generate all elements and attributes on the fly as much as possible. No NETCONF layer buffers are used for output. - generate the elements in the order the rpc_err_rec_t structs appear in the rpc_errQ, if any. - check if there is a user response callback specified in the reply. If so, then invoke the user callback to generate the response data - If no user callback, then check if any static data is present in the rpc_msg_t instead. If so, generate the output. Otherwise an empty element might be generated (if data expected and no errors indicated). yuma123_2.14/netconf/etc/0000775000175000017500000000000014770023131015332 5ustar vladimirvladimiryuma123_2.14/netconf/etc/yangdump-sample.conf0000664000175000017500000002146714770023131021316 0ustar vladimirvladimir# # yangdump configuration file # Refer to yangdump container definition in yangdump.yang # for full CLI documentation. # # version 2.1 2011-10-08 # yangdump { ## leaf defnames # If 'true', then output to a file with the default name # for the format, usually to the current directory. # Not used if the format parameter is missing. # # defnames false # ## leaf dependencies [empty] # If present, validate the file, write the module name, version # and module source for each file that this [sub]module # imports and includes, then exit. # # default is not to print dependencies and exit # ## leaf-list deviation # Specifies a YANG module to use as a source of deviations # e.g. deviation project-X-deviations # # no default for deviation # ## leaf exports [empty] # If present, validate the file, write information for the symbols # that this [sub]module exports, then exit. # # default is not to print exports and exit # ## leaf feature-code-default # # The default feature code generation type. # Enums: # static # dynamic # # feature-code-default dynamic # ## leaf-list feature-disable # syntax: module-name:feature-name # Identifies a feature which should be considered disabled. # Zero or more entries are allowed. # # no default for feature-disable (e.g., feature-disable foo:feature1) # ## leaf-list feature-enable # syntax: module-name:feature-name # Identifies a feature which should be considered enabled. # Zero or more entries are allowed. # # no default for feature-enable (e.g., feature-enable foo:feature1) # ## leaf feature-enable-default # If true (the default), then features will be enabled by default. # If false, then features will be disabled by default. # # feature-enable-default true # ## leaf format [FormatType] # Type of conversion desired, if any. If this # parameter is missing, then no translation # will be done, but the module will be validated, # and any requested reports will be generated. # # The following values are supported: # xsd == XSD 1.0 translation # sql == TBD: not implemented # sqldb == netconfcentral.org SQL data # html == XHTML 1.0 translation # yang == Canonical YANG translation # copy == Validate and copy with a new name. # c == Combined SIL C file # h == Combined SIL H file # uc == User SIL C file # uh == User SIL H file # yc == Yuma SIL C file # yh == Yuma SIL H file # yin == YIN translation # # no default for format # ## leaf home # Directory specification for the home directory to use # instead of HOME. # # there is no default for home ($HOME env var used) # (e.g. home /home/someuser) # ## leaf html-div [boolean] # If 'true', and HTML translation is requested, then this # parameter will cause the output to be a single
element, # instead of an entire HTML file. # If 'false', then a complete element will be generated # instead. # # html-div false # ## leaf html-toc [TocType] # The HTML Table of Contents output mode. # Ignored unless the 'format' parameter is # set to 'html'. # Values: # - none: no ToC generated # - plain: plain list ToC generated # - menu: drop-down menu ToC generated. # # html-toc menu # ## leaf identifiers [empty] # Validate the file, write the list of object identifiers, # that this [sub]module contains, then exit. # # the default is not to print the identifiers and exit # ## leaf indent # Number of spaces to indent (0 to 9) when formatting # nested output. # # indent 2 # ## leaf log # Filespec for the log file to use instead of STDOUT. # Leave out to use STDOUT for log messages # # no default for log # ## leaf log-append # If present, the log will be appended not over-written. # If not, the log will be over-written. # Only meaningful if the 'log' parameter is also present. # # default is to overwrite (flag not present) # ## leaf log-level # Sets the debug logging level for the program. # off # error # warn # info # debug # debug2 # debug3 # # log-level info # ## leaf modpath # Internal file search path for YANG files. # e.g., modpath "~/modules:/some/path:../another/path" # Refer to user manual for complete module search path # # no default for modpath # ## leaf-list module # Specify a module that the server should load at boot-time # e.g., module test2 # module test3 # # no default for module # ## leaf modversion [empty] # Validate the file(s), write the [sub]module # name, version and source filespec, then exit. # # default is to not print each module version and exit # ## leaf objview [ObjViewType] # Determines how objects are generated in HTML and # YANG outputs. # XSD output is always 'cooked', since refined groupings # and locally-scoped definitions are not supported in XSD. # # raw -- output includes augment and uses clauses, not the # expanded results of those clauses. # cooked -- output does not include augment or uses clauses, # just the objects generated from those clauses. # # objview raw # ## leaf output [string] # Output directory or file name to use. # Default is STDOUT if none specified and the # 'defnames' parameter is also set to 'false'. # # no default for output # ## leaf runpath # Internal file search path for executable files. # e.g., runpath "~/tools:/usr/share/acmelibs" # Refer to user manual for complete module search path # # no default for runpath # ## leaf show-errors [empty] # If present, list each error or warning # number and its default message string. # The program will exit after this is done. # # # default is to not print error info and exit # ## leaf simurls [boolean] # If 'true', and HTML translation is requested, then this # parameter will cause the format of URLs within links # to be generated in simplified form, for WEB development # engines such as CherryPy, which support this format. # # Normal URL format (false): # example.html?parm1=foo&parm2=bar#frag # # Simplified URL format (true): # example/foo/bar#frag # # simurls false # ## leaf stats [enumeration] [d:none] # Generate a statistics report for each input # module. Developers: see ydump/yangstats.h # # enum values: # none # brief # basic # advanced # all # # stats none # ## leaf subdirs [boolean] # If false, the file search paths for modules, scripts, # and data files will not include sub-directories if they # exist in the specified path. # # If true, then these file search paths will include # sub-directories, if present. Any directory name beginning # with a dot '.' character, or named 'CVS', will be ignored. # # subdirs true # ## leaf-list subtree [NcPathSpec] # Path specification of the directory subtree to use. # All of the YANG source modules contained in the # specified directory sub-tree will be processed. # length: 1 .. 4095 # # no default for subtree # ## leaf totals [enumeration] [d:none] # Controls how stats totals are displayed. # Ignored if stats equals 'none' # # enum values: # none # summary # summary-only # # totals none # ## leaf unified [boolean] [d:false] # If set to 'true', then submodules will be processed # within the main module, in a unified report, # instead of separately, one report for each file. # # If 'false', a separate output file is generated for each # input file, so that XSD output and other reports # for a main module will not include information for # submodules. # # unified false # ## leaf urlstart [string] # If present, then this string will be used to prepend # to HREF links and URLs generated for SQL and HTML # translation. It is expected to be a URL ending # with a directory path. The trailing separator '/' # will be added if it is missing. # # If not present (the default), then relative URLs, # starting with the file name will be generated instead. # # no default for urlstart # ## leaf warn-idlen # Controls whether identifier length warnings will be generated. # range 0 | 8 .. 1023; 0==disable ID length checking # # warn-idlen 64 # ## leaf warn-linelen # Controls whether line length warnings will be generated. # range 0 | 40 .. 4095; 0==disable ID length checking # # warn-linelen 72 # ## leaf-list warn-off # Controls whether the specified warning number will be generated # range 400 .. 899; # e.g., warn-off 477 # # no default for warn-off # ## leaf xsd-schemaloc [string] # If present, then the schemaLocation attribute will # be generated during XSD translation. This will be # done for the module being processed, and any modules # that are imported into that module. # # If not present (the default), then the schemaLocation # attribute is not generated during XSD translation. # Relative URLs for include and import directives will # be generated, starting with the file name. # # no default for xsd-schemaloc # ## leaf yuma-home # Directory for the yuma project root # This will override the YUMA_HOME environment variable # if it is set. # # no default for yuma-home }yuma123_2.14/netconf/etc/yangdiff-sample.conf0000664000175000017500000000713014770023131021250 0ustar vladimirvladimir# # yangdiff configuration file # Refer to yangdiff container definition in yangdiff.yang # for full CLI documentation. # # version 2.1; 2011-10-08 # yangdiff { ## leaf difftype [DiffType] # The type of comparison output requested. # terse # normal # revision # # difftype normal # ## leaf header [boolean] # If false, the header clauses will be skipped, and any # differences between the module headers will not be reported. # Only object definitions will be compared. # # If true, then header clauses will be compared, # along will all the object definitions. # # header true # ## leaf home # Directory specification for the home directory to use # instead of HOME. # # there is no default for home ($HOME env var used) # (e.g. home /home/someuser) # ## leaf indent # Number of spaces to indent (0 to 9) when formatting # nested output. # # indent 2 # ## leaf log # Filespec for the log file to use instead of STDOUT. # Leave out to use STDOUT for log messages # # no default for log # ## leaf log-append # If present, the log will be appended not over-written. # If not, the log will be over-written. # Only meaningful if the 'log' parameter is also present. # # default is to overwrite (flag not present) # ## leaf log-level # Sets the debug logging level for the program. # off # error # warn # info # debug # debug2 # debug3 # debug4 # # log-level info # ## leaf modpath # Internal file search path for YANG files. # e.g., modpath "~/modules:/some/path:../another/path" # Refer to user manual for complete module search path # # no default for modpath # # leaf new [NcModuleSpec] # New YANG files to compare # If this parameter indicates a filename, then it # represents the YANG source module name # to compare as the newer of the two revisions. # # If this parameter indicates a directory # (and the 'old' parameter indicates a filename), # then it will be used to to search for a # file with the same name as the 'new' parameter. # Refer to the user manual for more details # # no default for new (required parm) # # leaf old [NcModuleSpec] # Older YANG files to compare. # Similar to 'new' parm. # Refer to the user manual for more details # # no default for old (required parm) # ## leaf output [string] # Output directory or file name to use. # Default is STDOUT if none specified and the # 'defnames' parameter is also set to 'false'. # # no default for output # ## leaf runpath # Internal file search path for executable files. # e.g., runpath "~/tools:/usr/share/acmelibs" # Refer to user manual for complete module search path # # no default for runpath # ## leaf subdirs [boolean] # If false, the file search paths for modules, scripts, # and data files will not include sub-directories if they # exist in the specified path. # # If true, then these file search paths will include # sub-directories, if present. Any directory name beginning # with a dot '.' character, or named 'CVS', will be ignored. # # subdirs true # ## leaf warn-idlen # Controls whether identifier length warnings will be generated. # range 0 | 8 .. 1023; 0==disable ID length checking # # warn-idlen 64 # ## leaf warn-linelen # Controls whether line length warnings will be generated. # range 0 | 40 .. 4095; 0==disable ID length checking # # warn-linelen 72 # ## leaf-list warn-off # Controls whether the specified warning number will be generated # range 400 .. 899; # e.g., warn-off 477 # # no default for warn-off # ## leaf yuma-home # Directory for the yuma project root # This will override the YUMA_HOME environment variable # if it is set. # # no default for yuma-home }yuma123_2.14/netconf/etc/yangcli-sample.conf0000664000175000017500000002173414770023131021115 0ustar vladimirvladimir# # yangcli configuration file # Refer to yangcli container definition in yangcli.yang # for full CLI documentation. # # version 2.1; 2011-10-08 # yangcli { ## leaf aliases-file # Specifies the yangcli command aliases file to use. # # aliases-file ~/.yuma/.yangcli_aliases # ## leaf alt-names # Match mode to use for alternamte names in UrlPath name searches. # # alt-names true # ## leaf autoalaises # Controls whether the yangcli command aliases will # be saved at exit and loaded at startup. # If true, the 'aliases-file' parameter will be used if it is set, # or else the default aliases file will be used # (~/.yuma/.yangcli_aliases), for loading # and saving the yangcli command aliases. # If false, the yangcli command aliases will only be stored # and loaded manually with the aliases command. # # autoaliases true # ## leaf autocomp # Controls whether partial keywords will be # checked for interactive or script commands. # # autocomp true # ## leaf autohistory # Controls whether the command line history buffer will # be saved at exit and loaded at startup. # If true, the default history file will be used # (~/.yuma/.yangcli_history) for loading # and saving the history buffer. # If false, the history buffer will only be stored # and loaded manually with the history command. # # autohistory true # ## leaf autoload # Controls whether any modules (except this one) # will be automatically loaded upon startup or # upon session startup with an server. If false, # the 'load-module' command must be used to # explicitly load all the desired definition modules. # # autoload true # ## leaf autouservars # Controls whether the yangcli user variables will # be saved at exit and loaded at startup. # If true, the 'uservars-file' parameter will be used if set, # or else the default user variables file will be used # (~/.yuma/yangcli_uservars.xml), for loading # and saving the yangcli user variables. # If false, the yangcli user variables will only be stored # and loaded manually with the uservars command. # # autouservars true # ## leaf bad-data # Specifies how invalid user input from the CLI # will be handled when filling PDUs for remote # operations. # ignore # warn # check # error # # bad-data check # ## leaf batch-mode [empty] # If present, the interactive CLI will not be used. # A script should be provided with the 'run-script' # parameter, or a command provided with the 'run-command' # parameter, or else the program will simply exit. # # default is interactive-mode, not batch-mode # ## leaf datapath # Internal file search path for data files. # e.g., datapath "~/work:/some/path:../another/path" # # no default for datapath # ## leaf default-module # Selects the default module to try after the core operations, # when searching for a command or parameter match # # no default for default-module # ## leaf-list deviation # Specifies a YANG module to use as a source of deviations # e.g. deviation project-X-deviations # # no default for deviation # ## leaf display-mode # Controls how values are displayed during output # to STDOUT or a log file. # # plain: Plain identifier without any prefix format. # prefix: Plain text with XML prefix added format. # module: Plain text with module name as prefix added format. # xml: XML format # xml-nons: XML format without namespaces # json: JSON format # # display-mode plain # ## leaf echo-replies # Allow RPC replies to be echoes to the log or STDOUT # # echo-replies true # ## leaf-list feature-disable module-name:feature-name # Identifies a feature which should be considered disabled. # Zero or more entries are allowed. # # no default for feature-disable (e.g., feature-disable foo:feature1) # ## leaf-list feature-enable module-name:feature-name # Identifies a feature which should be considered enabled. # Zero or more entries are allowed. # # no default for feature-enable (e.g., feature-enable foo:feature1) # ## leaf feature-enable-default # If true (the default), then features will be enabled by default. # If false, then features will be disabled by default. # # feature-enable-default true # ## leaf fixorder # Controls whether PDU parameters will be # automatically sent to the server in the # canonical order. # # fixorder true # ## leaf force-target # Controls whether the candidate or running # configuration datastore will be used as # the default edit target, when both are supported # by the server. # # force-target candidate # ## leaf home # Directory specification for the home directory to use # instead of HOME. # # there is no default for home ($HOME env var used) # (e.g. home /home/someuser) # ## leaf indent # Number of spaces to indent (0 to 9) when formatting # nested output. # # indent 2 # ## leaf log # Filespec for the log file to use instead of STDOUT. # Leave out to use STDOUT for log messages # # no default for log # ## leaf log-append # If present, the log will be appended not over-written. # If not, the log will be over-written. # Only meaningful if the 'log' parameter is also present. # # default is to overwrite (flag not present) # ## leaf log-level # Sets the debug logging level for the program. # off # error # warn # info # debug # debug2 # debug3 # debug4 # # log-level info # ## leaf match-names # Match mode to use for UrlPath name searches. # # match-name one-nocase # ## leaf modpath # Internal file search path for YANG files. # e.g., modpath "~/modules:/some/path:../another/path" # Refer to user manual for complete module search path # # no default for modpath # ## leaf-list module # Specify a module that the server should load at boot-time # e.g., module test2 # module test3 # # no default for module # ## leaf ncport # Specify the TCP port to use when starting a NETCONF session # in auto-connect mode. # # ncport 830 # ## leaf password # password string to use for the auto-connect mode # # no default for password # ## leaf private-key # Contains the file path specification # for the file containing the client-side private key. # # private-key $HOME/.ssh/id_rsa # ## leaf protocols # Specifies which protocol versions the program or session # will attempt to use. Empty set is not allowed. # # protocols "netconf1.0 netconf1.1" # ## leaf public-key # Contains the file path specification # for the file containing the client-side public key. # # public-key $HOME/.ssh/id_rsa.pub # ## leaf runpath # Internal file search path for executable files. # e.g., runpath "~/tools:/usr/share/acmelibs" # Refer to user manual for complete module search path # # no default for runpath # ## choice run-script or run-command or nothing # Selects the auto-startup mode # # case leaf run-command [string] # The specified command will be invoked upon startup. # If the auto-connect parameters are provided, then # a session will be established before running the # command. length: 1 .. 4095 # # case leaf run-script [string] # The specified script will be invoked upon startup. # If the auto-connect parameters are provided, then # a session will be established before running the # script. If a quoted string is used, then any parameters # after the script name will be passed to the script. # length: 1 .. 4095 # # no default for this optional choice # ## leaf time-rpcs # Measure the round-trip time of each request and # at the session level. # # time-rpcs false # ## leaf timeout [Timeout] # Number of seconds to wait for a response # from the server before declaring a timeout. # Zero means no timeout at all. # # timeout 30 # ## leaf transport # Identifies the default transport protocol that should be used. # enum values: # ssh: NETCONF over SSH. # tcp: NETCONF over TCP. # If this enum is selected, then the default --ncport # value is set to 2023, and the --protocols value # is set to netconf1.0. The --password value will # be ignored. # # transport ssh # ## leaf user [NcxUserName] # User name to use for NETCONF sessions. # length: 1..63 # pattern: [a-z,A-Z][a-z,A-Z,0-9,\-,_,\.]{0,62} # # there is no default for user # ## leaf uservars-file # Specifies the yangcli user variables file to use. # # uservars-file ~/.yuma/yangcli_uservars.xml # ## leaf use-xmlheader # Specifies which protocol versions the program or session # will attempt to use. Empty set is not allowed. # # use-xmlheader true # # no default for user # ## leaf warn-idlen # Controls whether identifier length warnings will be generated. # range 0 | 8 .. 1023; 0==disable ID length checking # # warn-idlen 64 # ## leaf warn-linelen # Controls whether line length warnings will be generated. # range 0 | 40 .. 4095; 0==disable ID length checking # # warn-linelen 72 # ## leaf-list warn-off # Controls whether the specified warning number will be generated # range 400 .. 899; # e.g., warn-off 477 # # no default for warn-off # ## leaf yuma-home # Directory for the yuma project root # This will override the YUMA_HOME environment variable # if it is set. # # no default for yuma-home }yuma123_2.14/netconf/etc/netconfd-sample.conf0000664000175000017500000002175214770023131021267 0ustar vladimirvladimir# # netconfd configuration file # Refer to netconfd container definition in netconfd.yang # for full CLI documentation. # # version 2.2; 2012-01-23 # netconfd { ## leaf access-control # Controls how access control is enforced by the server. # enforcing # permissive # disabled # off # # access-control enforcing # ## leaf audit-log # Filespec for the server audit log file to use in addition to the # normal log file or STDOUT. # # The default is to not have an additional audit-log # (no default filename) # ## leaf audit-log-append # If present, the audit log will be appended not over-written. # # The default is to not append (leaf not present) # ## leaf datapath # Internal file search path for data files. # e.g., datapath "~/work:/some/path:../another/path" # # no default for datapath # ## leaf default-style # Selects the type of filtering behavior the server will # advertise as the 'basic' behavior in the 'with-defaults' # capability. # report-all # trim # explicit # # default-style explicit # ## leaf delete-empty-npcontainers # Selects whether the server will keep or delete empty non-presence # containers in the running and startup configurations. Set to true # to delete these containers, and false to keep them. # !! This parameter is deprecated, and ignored by the server !! # !! empty NP containers are not deleted !! # delete-empty-npcontainers false # ## leaf-list deviation # Specifies a YANG module to use as a source of deviations # e.g. deviation project-X-deviations # # no default for deviation # ## leaf eventlog-size # Specifies the maximum number of notification events # that will be saved in the notification replay buffer. # # eventlog-size 1000 # ## leaf-list feature-disable # syntax: module-name:feature-name # Identifies a feature which should be considered disabled. # Zero or more entries are allowed. # # no default for feature-disable (e.g., feature-disable foo:feature1) # ## leaf-list feature-enable # syntax: module-name:feature-name # Identifies a feature which should be considered enabled. # Zero or more entries are allowed. # # no default for feature-enable (e.g., feature-enable foo:feature1) # ## leaf feature-enable-default # If true (the default), then features will be enabled by default. # If false, then features will be disabled by default. # # feature-enable-default true # ## leaf hello-timeout # Specifies the number of seconds that a session # may exist before the hello PDU is received. # range 0 | 10 .. 3600; 0 == no timeout used # # hello-timeout 600 # ## leaf home # Directory specification for the home directory to use # instead of HOME. # # there is no default for home ($HOME env var used) # (e.g. home /home/someuser) # ## leaf idle-timeout # Specifies the number of seconds that a session # may remain idle without issuing any RPC requests. # Sessions that have a notification subscription # active are never dropped. # range 0 | 10 .. 360000; 0 == no timeout used # # idle-timeout 3600 # ## leaf indent # Number of spaces to indent (0..9) in formatted output. # # indent 2 # ## leaf log # Filespec for the log file to use instead of STDOUT. # Leave out to use STDOUT for log messages # # no default for log # ## leaf log-append # If present, the log will be appended not over-written. # If not, the log will be over-written. # Only meaningful if the 'log' parameter is also present. # # default is to overwrite (flag not present) # ## leaf log-level # Sets the debug logging level for the program. # off # error # warn # info # debug # debug2 # debug3 # debug4 # # log-level info # ## leaf max-burst # Specifies the maximum number of notifications # that should be sent to one session, within a # one second time interval. The value 0 indicates # that the server should not limit notification # bursts at all # # max-burst 10 # ## leaf modpath # Internal file search path for YANG files. # e.g., modpath "~/modules:/some/path:../another/path" # Refer to user manual for complete module search path # # no default for modpath # ## leaf-list module # Specify a module that the server should load at boot-time # e.g., module test2 # module test3 # # no default for module # ## leaf-list port # Specify the TCP ports that the server will accept # connections from. Up to 4 port numbers can be configured. # If any ports are configured, then only those values # will be accepted by the server. # # port 830 # ## leaf protocols # Specifies which protocol versions the program or session # will attempt to use. Empty set is not allowed. # # protocols "netconf1.0 netconf1.1" # ## leaf running-error # Controls the server behavior if any errors are # encountered while validating the running database # during the initial load of the running configuration # at boot-time. # enum stop # Terminate the program if any errors are # encountered in the running configuration. # enum continue # Continue the program if any errors are # encountered in the running configuration. # Altering the running configuration will fail # until the commit validation tests succeed. # # running-error stop # ## leaf runpath # Internal file search path for executable files. # e.g., runpath "~/tools:/usr/share/acmelibs" # Refer to user manual for complete module search path # # no default for runpath # ## choice startup or no-startup or factory-startup # Selects the startup configuration file to use # - To skip loading any startup file, and save to the default # if needed, use 'no-startup' # - To skip loading any startup file, and force the startup file # to be the factory default, use 'factory-startup' # - To use a specific startup file, use an absolute filespec # - To use a specific startup file in the YUMA_DATAPATH, use a relative # filespec. # # startup startup-cfg.xml # ## leaf startup-error # Controls the server behavior if any errors are # encountered while loading the startup configuration # file into the running configuration at boot-time. # It is possible for the startup configuration # to contain errors within optional nodes. If this # parameter is set to 'continue', then the validation # tests on the running config (controlled by running-error) # should not fail due to missing optional nodes."; # # enum stop # Terminate the program if any errors are # encountered in the startup configuration. # # enum continue # Continue the program if any errors are # encountered in the startup configuration. # The entire module-specific data structure(s) # containing the error node(s) will not be added # to the running configuration at boot-time. # # startup-error continue # ## leaf subdirs [boolean] # If false, the file search paths for modules, scripts, # and data files will not include sub-directories if they # exist in the specified path. # # If true, then these file search paths will include # sub-directories, if present. Any directory name beginning # with a dot '.' character, or named 'CVS', will be ignored. # # subdirs true # ## leaf superuser # The user name to use as the superuser account. # To disable the superuser account completely, # set this parameter to the empty string. # # There is no default for the superuser name. # Default is to not enable any superuser. # ## leaf system-sorted # Indicates whether ordered-by system leaf-lists and lists will be # kept in sorted order. # # system-sorted true # ## leaf target # The database to use as the target of edit-config operations. # running # candidate # # target candidate # ## leaf usexmlorder # If set, then XML schema order will be checked as # required by the YANG spec. If not set then loose # XML ordering will be allowed. # # default is loose order; flag not set # ## leaf warn-idlen # Controls whether identifier length warnings will be generated. # range 0 | 8 .. 1023; 0==disable ID length checking # # warn-idlen 64 # ## leaf warn-linelen # Controls whether line length warnings will be generated. # range 0 | 40 .. 4095; 0==disable ID length checking # # warn-linelen 72 # ## leaf-list warn-off # Controls whether the specified warning number will be generated # range 400 .. 899; # e.g., warn-off 477 # # no default for warn-off # ## leaf with-startup # If 'true', then the :startup capability will be enabled. # If 'false', then the :startup capability will not be enabled. # # with-startup false # ## leaf with-url # If set to 'false', then the :url capability will be disabled. # Otherwise, the :url capability will be enabled. This capability # allows local files to be stored as backups on the server. # # with-url true # ## leaf with-validate # If 'true', then the :validate capability will be enabled. # If 'false', then no validate operation or test-option # These operations can use lots of memory, so disable # to reduce runtime meory spikes # # with-validate true ## ## leaf yuma-home # Directory for the yuma project root # This will override the YUMA_HOME environment variable # if it is set. # # no default for yuma-home }yuma123_2.14/netconf/test/0000775000175000017500000000000014770023131015536 5ustar vladimirvladimiryuma123_2.14/netconf/test/integ-tests/0000775000175000017500000000000014770023131020004 5ustar vladimirvladimiryuma123_2.14/netconf/test/integ-tests/state-edit-candidate.cpp0000664000175000017500000000254114770023131024467 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestStateEditCandidate #include "configure-yuma-integtest.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ), ( "--runpath=../modules/sil" ":../../../libtoaster/lib" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--target=candidate" ), ( "--module=toaster" ), ( "--no-startup" ), // ensure that no configuration from previous // tests is present }; // typedef that allows the use of parameterised test fixtures with // BOOST_GLOBAL_FIXTURE typedef IntegrationTestFixture MyFixtureType_T; // Set the global test fixture BOOST_GLOBAL_FIXTURE( MyFixtureType_T ); } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/alltests.py0000775000175000017500000000177514770023131022226 0ustar vladimirvladimir#!/usr/bin/env python import sys import os import subprocess # ----------------------------------------------------------------------------| def GetTestProgramList(): fileList=[] for dirname, dirnames, filenames in os.walk('.'): for filename in filenames: if filename.startswith( "test-" ) and os.access(filename, os.X_OK): fileList.append( "./" + filename ) return fileList # ----------------------------------------------------------------------------| def RunTests( tests, argv ): testArgs="" for arg in argv[1:]: testArgs += arg + " " for test in tests: testCommand = test + " " + testArgs print("running test %s" % testCommand) # commands.getoutput( test ) os.system( testCommand ) # ----------------------------------------------------------------------------| if __name__ == '__main__': # find the executables tests = GetTestProgramList() # run all executables RunTests( tests, sys.argv ) yuma123_2.14/netconf/test/integ-tests/simple-edit-startup-true.cpp0000664000175000017500000000155014770023131025402 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestSimpleEditStartupTrue #include "configure-yuma-integtest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ), ( "--runpath=../modules/sil" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--target=candidate" ), ( "--module=simple_list_test" ), ( "--with-startup=true" ), }; #include "define-yuma-integtest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/default-none-running.cpp0000664000175000017500000000170614770023131024553 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestDefaultNoneRunning #include "configure-yuma-integtest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ), ( "--runpath=../modules/sil" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--target=running" ), ( "--module=simple_list_test" ), ( "--no-startup" ), // ensure that no configuration from previous // tests is present }; #include "define-yuma-integtest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/state-edit-running.cpp0000664000175000017500000000253614770023131024237 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestDeviceEditRunning #include "configure-yuma-integtest.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ), ( "--runpath=../modules/sil" ":../../../libtoaster/lib" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--target=running" ), ( "--module=toaster" ), ( "--no-startup" ), // ensure that no configuration from previous // tests is present }; // typedef that allows the use of parameterised test fixtures with // BOOST_GLOBAL_FIXTURE typedef IntegrationTestFixture MyFixtureType_T; // Set the global test fixture BOOST_GLOBAL_FIXTURE( MyFixtureType_T ); } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/define-yuma-integtest-global-fixture.h0000664000175000017500000000144514770023131027312 0ustar vladimirvladimir/** * Configure the BOOST::TEST environment for Yuma integration Testing. * This file is contains common configuration of BOOST::TEST for * Yuma Integration Testing. * * NOTE: It purposely omits header include guards * * Yuma Integration Test Main Files Should have the following format: * * #define BOOST_TEST_MODULE [Name of test module] * #include "configure-yuma-integtest.h" * * namespace YumaTest { * * Custom Initialisation (e.g. definition of SpoofedArgs ) * * #include "define-yuma-integtest-global-fixture.h" * * } // namespace YumaTest. */ // typedef that allows the use of parameterised test fixtures with // BOOST_GLOBAL_FIXTURE typedef IntegrationTestFixture MyFixtureType_T; // Set the global test fixture BOOST_GLOBAL_FIXTURE( MyFixtureType_T ); yuma123_2.14/netconf/test/integ-tests/discard-changes.cpp0000664000175000017500000000170414770023131023531 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestDiscardChanges #include "configure-yuma-integtest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ), ( "--runpath=../modules/sil" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--target=candidate" ), ( "--module=simple_list_test" ), ( "--no-startup" ), // ensure that no configuration from previous // tests is present }; #include "define-yuma-integtest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/simple-edit-candidate.cpp0000664000175000017500000000171114770023131024636 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestSimpleEditCandidate #include "configure-yuma-integtest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ), ( "--runpath=../modules/sil" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--target=candidate" ), ( "--module=simple_list_test" ), ( "--no-startup" ), // ensure that no configuration from previous // tests is present }; #include "define-yuma-integtest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/simple-yang.cpp0000664000175000017500000000167614770023131022747 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestSimpleYang #include "configure-yuma-integtest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ), ( "--runpath=../modules/sil" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--target=running" ), ( "--module=simple_yang_test" ), ( "--no-startup" ), // ensure that no configuration from previous // tests is present }; #include "define-yuma-integtest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/Makefile.am0000664000175000017500000003410214770023131022040 0ustar vladimirvladimirTESTS=test_yangcli_wordex check_PROGRAMS=test_yangcli_wordex test_yangcli_wordex_SOURCES = \ $(top_srcdir)/netconf/test/test-suites/integ/test_yangcli_wordex.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_wordexp.c test_yangcli_wordex_CPPFLAGS = -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_yangcli_wordex_LDFLAGS = $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS=test-b64 check_PROGRAMS=test_b64 test_b64_SOURCES = \ $(top_srcdir)/netconf/src/ncx/b64.c \ $(top_srcdir)/netconf/test/test-suites/integ/test-b64.c test_b64_CPPFLAGS = -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_b64_LDFLAGS = $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-base-64 check_PROGRAMS+=test-base-64 test_base_64_SOURCES = \ $(top_srcdir)/netconf/test/integ-tests/simple-yang.cpp \ $(top_srcdir)/netconf/test/test-suites/integ/base-64-tests.cpp test_base_64_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_base_64_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-default-none-running check_PROGRAMS+=test-default-none-running test_default_none_running_SOURCES= \ $(top_srcdir)/netconf/test/test-suites/common/default-none-tests.cpp \ $(top_srcdir)/netconf/test/integ-tests/default-none-running.cpp test_default_none_running_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_default_none_running_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-device-edit-candidate check_PROGRAMS+=test-device-edit-candidate test_device_edit_candidate_SOURCES= \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-misc.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-create.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-merge.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-replace.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-get.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-delete.cpp \ $(top_srcdir)/netconf/test/test-suites/integ/base-64-tests.cpp \ $(top_srcdir)/netconf/test/integ-tests/device-edit-candidate.cpp test_device_edit_candidate_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_device_edit_candidate_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-device-edit-running check_PROGRAMS+=test-device-edit-running test_device_edit_running_SOURCES= \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-misc.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-create.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-merge.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-replace.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-get.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-delete.cpp \ $(top_srcdir)/netconf/test/test-suites/integ/base-64-tests.cpp \ $(top_srcdir)/netconf/test/integ-tests/device-edit-running.cpp test_device_edit_running_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_device_edit_running_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-discard-changes check_PROGRAMS+=test-discard-changes test_discard_changes_SOURCES= \ $(top_srcdir)/netconf/test/test-suites/common/discard-changes-tests.cpp \ $(top_srcdir)/netconf/test/integ-tests/discard-changes.cpp test_discard_changes_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_discard_changes_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-lock-load-candidate check_PROGRAMS+=test-lock-load-candidate test_lock_load_candidate_SOURCES= \ $(top_srcdir)/netconf/test/test-suites/common/module-load-test-suite.cpp \ $(top_srcdir)/netconf/test/test-suites/common/db-lock-test-suite-common.cpp \ $(top_srcdir)/netconf/test/test-suites/common/db-lock-test-suite-candidate.cpp \ $(top_srcdir)/netconf/test/integ-tests/lock-load-candidate.cpp test_lock_load_candidate_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_lock_load_candidate_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-lock-load-running check_PROGRAMS+=test-lock-load-running test_lock_load_running_SOURCES= \ $(top_srcdir)/netconf/test/test-suites/common/module-load-test-suite.cpp \ $(top_srcdir)/netconf/test/test-suites/common/db-lock-test-suite-common.cpp \ $(top_srcdir)/netconf/test/test-suites/common/db-lock-test-suite-running.cpp \ $(top_srcdir)/netconf/test/integ-tests/lock-load-running.cpp test_lock_load_running_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_lock_load_running_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-simple-edit-candidate check_PROGRAMS+=test-simple-edit-candidate test_simple_edit_candidate_SOURCES= \ $(top_srcdir)/netconf/test/test-suites/common/simple-edit-tests.cpp \ $(top_srcdir)/netconf/test/test-suites/common/simple-edit-tests-candidate.cpp \ $(top_srcdir)/netconf/test/test-suites/common/simple-edit-tests-validate.cpp \ $(top_srcdir)/netconf/test/test-suites/common/discard-changes-tests.cpp \ $(top_srcdir)/netconf/test/test-suites/common/default-none-tests.cpp \ $(top_srcdir)/netconf/test/test-suites/common/callback-failures-tests.cpp \ $(top_srcdir)/netconf/test/integ-tests/simple-edit-candidate.cpp test_simple_edit_candidate_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_simple_edit_candidate_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-simple-edit-running check_PROGRAMS+=test-simple-edit-running test_simple_edit_running_SOURCES = \ $(top_srcdir)/netconf/test/test-suites/common/simple-edit-tests.cpp \ $(top_srcdir)/netconf/test/test-suites/common/default-none-tests.cpp \ $(top_srcdir)/netconf/test/integ-tests/simple-edit-running.cpp test_simple_edit_running_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_simple_edit_running_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-simple-edit-startup-false check_PROGRAMS+=test-simple-edit-startup-false test_simple_edit_startup_false_SOURCES = \ $(top_srcdir)/netconf/test/test-suites/common/simple-edit-tests-startup.cpp \ $(top_srcdir)/netconf/test/integ-tests/simple-edit-startup-false.cpp test_simple_edit_startup_false_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_simple_edit_startup_false_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-simple-edit-startup-true check_PROGRAMS+=test-simple-edit-startup-true test_simple_edit_startup_true_SOURCES = \ $(top_srcdir)/netconf/test/test-suites/common/simple-edit-tests-startup.cpp \ $(top_srcdir)/netconf/test/test-suites/common/startup-delete-config-tests.cpp \ $(top_srcdir)/netconf/test/integ-tests/simple-edit-startup-true.cpp test_simple_edit_startup_true_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_simple_edit_startup_true_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-simple-yang check_PROGRAMS+=test-simple-yang test_simple_yang_SOURCES = \ $(top_srcdir)/netconf/test/test-suites/common/simple-choice-tests.cpp \ $(top_srcdir)/netconf/test/test-suites/common/simple-must-tests.cpp \ $(top_srcdir)/netconf/test/integ-tests/simple-yang.cpp test_simple_yang_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_simple_yang_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-state-edit-candidate check_PROGRAMS+=test-state-edit-candidate test_state_edit_candidate_SOURCES = \ $(top_srcdir)/netconf/test/test-suites/common/state-data-tests-get.cpp \ $(top_srcdir)/netconf/test/integ-tests/state-edit-candidate.cpp test_state_edit_candidate_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_state_edit_candidate_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-state-edit-running check_PROGRAMS+=test-state-edit-running test_state_edit_running_SOURCES = \ $(top_srcdir)/netconf/test/test-suites/common/state-data-tests-get.cpp \ $(top_srcdir)/netconf/test/integ-tests/state-edit-running.cpp test_state_edit_running_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_state_edit_running_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses yuma123_2.14/netconf/test/integ-tests/device-edit-running.cpp0000664000175000017500000000247014770023131024353 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestDeviceEditRunning #include "configure-yuma-integtest.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ), ( "--runpath=../modules/sil" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--target=running" ), ( "--module=device_test" ), ( "--no-startup" ), // ensure that no configuration from previous // tests is present }; // typedef that allows the use of parameterised test fixtures with // BOOST_GLOBAL_FIXTURE typedef IntegrationTestFixture MyFixtureType_T; // Set the global test fixture BOOST_GLOBAL_FIXTURE( MyFixtureType_T ); } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/lock-load-running.cpp0000664000175000017500000000143314770023131024034 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestLockLoadRunning #include "configure-yuma-integtest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ), ( "--runpath=../modules/sil" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--target=running" ), }; #include "define-yuma-integtest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/simple-edit-running.cpp0000664000175000017500000000170514770023131024405 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestSimpleEditRunning #include "configure-yuma-integtest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ), ( "--runpath=../modules/sil" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--target=running" ), ( "--module=simple_list_test" ), ( "--no-startup" ), // ensure that no configuration from previous // tests is present }; #include "define-yuma-integtest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/configure-yuma-integtest.h0000664000175000017500000000226614770023131025121 0ustar vladimirvladimir/** * Configure the BOOST::TEST environment for Yuma integration Testing. * This file is contains common configuration of BOOST::TEST for * Yuma Integration Testing. * * NOTE: It purposely omits header include guards * * Yuma Integration Test Main Files Should have the following format: * * #define BOOST_TEST_MODULE [Name of test module] * #include "configure-yuma-integtest.h" * * namespace YumaTest { * * Custom Initialisation (e.g. definition of SpoofedArgs ) * * #include "define-yuma-integtest-global-fixture.h" * * } // namespace YumaTest. */ /** Configure Dynamic linking with boost test libraries */ #define BOOST_TEST_DYN_LINK // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #define BOOST_TEST_DYN_LINK #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/integ-test-fixture.inl" #include "test/support/fixtures/spoofed-args.h" yuma123_2.14/netconf/test/integ-tests/device-edit-candidate.cpp0000664000175000017500000000253214770023131024606 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestDeviceEditCandidate #include "configure-yuma-integtest.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ), ( "--runpath=../modules/sil" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--log-level=debug3" ), ( "--target=candidate" ), ( "--module=device_test" ), ( "--no-startup" ), // ensure that no configuration from previous // tests is present }; // typedef that allows the use of parameterised test fixtures with // BOOST_GLOBAL_FIXTURE typedef IntegrationTestFixture MyFixtureType_T; // Set the global test fixture BOOST_GLOBAL_FIXTURE( MyFixtureType_T ); } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/lock-load-candidate.cpp0000664000175000017500000000151114770023131024265 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestLockLoadCandidate #include "configure-yuma-integtest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ":../../modules/test/fail" ), ( "--runpath=../modules/sil" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--target=candidate" ), }; #include "define-yuma-integtest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/simple-edit-startup-false.cpp0000664000175000017500000000161014770023131025512 0ustar vladimirvladimir#define BOOST_TEST_MODULE IntegTestSimpleEditStartupFalse #include "configure-yuma-integtest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--modpath=../../modules/netconfcentral" ":../../modules/ietf" ":../../modules/yang" ":../modules/yang" ":../../modules/test/pass" ), ( "--runpath=../modules/sil" ), ( "--access-control=off" ), ( "--log=./yuma-op/yuma-out.txt" ), ( "--log-level=debug4" ), ( "--target=candidate" ), ( "--module=simple_list_test" ), ( "--with-startup=false" ), }; #include "define-yuma-integtest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/integ-tests/test2.yang0000664000175000017500000000207414770023131021730 0ustar vladimirvladimirmodule test2 { namespace "http://netconfcentral.org/ns/test2"; prefix "t2"; import test1 { prefix t1; } revision 2008-10-15 { description "Initial revision."; } typedef XX { type string { pattern "a*"; } } typedef YY { type string { pattern "b*"; pattern "b7*"; } } typedef ZZ { type YY { pattern "b7b*"; pattern "b7b7*"; } } typedef ZZZ { type t1:AA { pattern "b7b*"; pattern "b7b7*"; } } leaf a { type int32 { range "2 .. 12"; } } leaf b { type string { length "1 .. 10"; pattern "1-9[0-9]*" { error-app-tag "error-test 2, leaf b"; } } } leaf c { description "Only 5151 should pass the AND pattern test"; type string { length "1 .. 10"; pattern "1-9[0-9]*" { error-message "error-test 2, leaf c"; } pattern "51*"; pattern "[1-9]{4}"; } } } yuma123_2.14/netconf/test/support/0000775000175000017500000000000014770023131017252 5ustar vladimirvladimiryuma123_2.14/netconf/test/support/callbacks/0000775000175000017500000000000014770023131021171 5ustar vladimirvladimiryuma123_2.14/netconf/test/support/callbacks/system-cb-checker.cpp0000664000175000017500000001453414770023131025214 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/system-cb-checker.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| void SystemCBChecker::addMainContainer(const std::string& modName, const std::string& containerName) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::addElement(const std::string& /* modName */, const std::string& /* containerName */, const std::vector& /* element */) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::addKey(const std::string& /* modName */, const std::string& /* containerName */, const std::vector& /* listElement */, const std::string& /* key */) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::addChoice(const std::string& /* modName */, const std::string& /* containerName */, const std::vector& /* choiceElement */, const std::vector& /* removeElement */) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::addKeyValuePair(const std::string& /* modName */, const std::string& /* containerName */, const std::vector& /* listElement */, const std::string& /* key */, const std::string& /* value */) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::deleteKey( const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key ) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::deleteKeyValuePair( const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value ) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::commitKeyValuePairs(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value, int count) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::addResourceNode(const std::string& /* modName */, const std::string& /* containerName */, const std::vector& /* elements */, bool /* statusConfig */, bool /* alarmConfig */) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::addResourceCon(const std::string& /* modName */, const std::string& /* containerName */, const std::vector& /* elements */) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::addStreamCon(const std::string& /* modName */, const std::string& /* containerName */, const std::vector& /* elements */) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::checkCallbacks(const std::string& modName) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::updateLeaf(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase ) { // Do nothing as callbacks are not logged during system tests. } // ---------------------------------------------------------------------------| void SystemCBChecker::updateContainer(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase ) { // Do nothing as callbacks are not logged during system tests. } } // namespace YumaTest yuma123_2.14/netconf/test/support/callbacks/running-cb-checker.cpp0000664000175000017500000004763414770023131025357 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/running-cb-checker.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------! RunningCBChecker::RunningCBChecker() : CallbackChecker() { } // ---------------------------------------------------------------------------! RunningCBChecker::~RunningCBChecker() { } // ---------------------------------------------------------------------------| void RunningCBChecker::addMainContainer(const std::string& modName, const std::string& containerName) { vector empty; addExpectedCallback(modName, containerName, empty, "edit", "validate", ""); addExpectedCallback(modName, containerName, empty, "edit", "apply", ""); addExpectedCallback(modName, containerName, empty, "edit", "commit", "create"); } // ---------------------------------------------------------------------------| void RunningCBChecker::addElement(const std::string& modName, const std::string& containerName, const std::vector& element) { addExpectedCallback(modName, containerName, element, "edit", "validate", ""); addExpectedCallback(modName, containerName, element, "edit", "apply", ""); addExpectedCallback(modName, containerName, element, "edit", "commit", "replace"); } // ---------------------------------------------------------------------------| void RunningCBChecker::addKey(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key) { vector elements(listElement); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.pop_back(); addExpectedCallback(modName, containerName, elements, "edit", "apply", ""); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "apply", ""); elements.pop_back(); addExpectedCallback(modName, containerName, elements, "edit", "commit", "create"); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "commit", "create"); } // ---------------------------------------------------------------------------| void RunningCBChecker::addChoice(const std::string& modName, const std::string& containerName, const std::vector& choiceElement, const std::vector& removeElement) { vector deleteElements(removeElement); addExpectedCallback(modName, containerName, deleteElements, "edit", "validate", ""); addExpectedCallback(modName, containerName, deleteElements, "edit", "apply", ""); addExpectedCallback(modName, containerName, deleteElements, "edit", "commit", "delete"); vector addElements(choiceElement); addExpectedCallback(modName, containerName, addElements, "edit", "validate", ""); addExpectedCallback(modName, containerName, addElements, "edit", "apply", ""); addExpectedCallback(modName, containerName, addElements, "edit", "commit", "create"); } // ---------------------------------------------------------------------------| void RunningCBChecker::addKeyValuePair(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value) { vector elements(listElement); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.pop_back(); addExpectedCallback(modName, containerName, elements, "edit", "apply", ""); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "apply", ""); elements.pop_back(); addExpectedCallback(modName, containerName, elements, "edit", "commit", "create"); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "commit", "create"); elements.pop_back(); elements.push_back(value); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); addExpectedCallback(modName, containerName, elements, "edit", "apply", ""); addExpectedCallback(modName, containerName, elements, "edit", "commit", "create"); } // ---------------------------------------------------------------------------| void RunningCBChecker::commitKeyValuePairs(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value, int count) { // Do nothing as commits are not performed separately when using writeable running. } // ---------------------------------------------------------------------------| void RunningCBChecker::deleteKey(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key) { vector elements(listElement); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.pop_back(); addExpectedCallback(modName, containerName, elements, "edit", "apply", ""); //TODO - Add commit callbacks } // ---------------------------------------------------------------------------| void RunningCBChecker::deleteKeyValuePair(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value) { vector elements(listElement); elements.push_back(value); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); addExpectedCallback(modName, containerName, elements, "edit", "apply", ""); addExpectedCallback(modName, containerName, elements, "edit", "commit", "delete"); elements.pop_back(); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.pop_back(); addExpectedCallback(modName, containerName, elements, "edit", "apply", ""); addExpectedCallback(modName, containerName, elements, "edit", "commit", "delete"); } // ---------------------------------------------------------------------------| void RunningCBChecker::addResourceNode(const std::string& modName, const std::string& containerName, const std::vector& elements, bool statusConfig, bool alarmConfig) { vector hierarchy(elements); hierarchy.push_back("resourceNode"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.push_back("id"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("resourceType"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("configuration"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); if (statusConfig) { hierarchy.pop_back(); hierarchy.push_back("statusConfig"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); } if (alarmConfig) { hierarchy.pop_back(); hierarchy.push_back("alarmConfig"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); } hierarchy.pop_back(); hierarchy.push_back("physicalPath"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.push_back("id"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("resourceType"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("configuration"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); if (statusConfig) { hierarchy.pop_back(); hierarchy.push_back("statusConfig"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); } if (alarmConfig) { hierarchy.pop_back(); hierarchy.push_back("alarmConfig"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); } hierarchy.pop_back(); hierarchy.push_back("physicalPath"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.push_back("id"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("resourceType"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("configuration"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); if (statusConfig) { hierarchy.pop_back(); hierarchy.push_back("statusConfig"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); } if (alarmConfig) { hierarchy.pop_back(); hierarchy.push_back("alarmConfig"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); } hierarchy.pop_back(); hierarchy.push_back("physicalPath"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); } // ---------------------------------------------------------------------------| void RunningCBChecker::addResourceCon(const std::string& modName, const std::string& containerName, const std::vector& elements) { vector hierarchy(elements); hierarchy.push_back("resourceConnection"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.push_back("id"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("sourceId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("sourcePinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("destinationId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("destinationPinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("bitrate"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.push_back("id"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("sourceId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("sourcePinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("destinationId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("destinationPinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("bitrate"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.push_back("id"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("sourceId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("sourcePinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("destinationId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("destinationPinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("bitrate"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); } // ---------------------------------------------------------------------------| void RunningCBChecker::addStreamCon(const std::string& modName, const std::string& containerName, const std::vector& elements) { vector hierarchy(elements); hierarchy.push_back("streamConnection"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.push_back("id"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("sourceStreamId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("destinationStreamId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("sourceId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("sourcePinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("destinationId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("destinationPinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("bitrate"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.push_back("id"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("sourceStreamId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("destinationStreamId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("sourceId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("sourcePinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("destinationId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("destinationPinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); hierarchy.push_back("bitrate"); addExpectedCallback(modName, containerName, hierarchy, "edit", "apply", ""); hierarchy.pop_back(); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.push_back("id"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("sourceStreamId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("destinationStreamId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("sourceId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("sourcePinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("destinationId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("destinationPinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); hierarchy.pop_back(); hierarchy.push_back("bitrate"); addExpectedCallback(modName, containerName, hierarchy, "edit", "commit", "create"); } // ---------------------------------------------------------------------------| void RunningCBChecker::updateLeaf(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase) { addExpectedCallback(modName, containerName, listElement, "edit", "validate", ""); addExpectedCallback(modName, containerName, listElement, "edit", "apply", ""); addExpectedCallback(modName, containerName, listElement, "edit", "commit", phase); } // ---------------------------------------------------------------------------| void RunningCBChecker::updateContainer(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase) { addExpectedCallback(modName, containerName, listElement, "edit", "validate", ""); addExpectedCallback(modName, containerName, listElement, "edit", "apply", ""); addExpectedCallback(modName, containerName, listElement, "edit", "commit", phase); } } // namespace YumaTest yuma123_2.14/netconf/test/support/callbacks/callback-checker.h0000664000175000017500000002623614770023131024511 0ustar vladimirvladimir#ifndef __CALLBACK_CHECKER_H #define __CALLBACK_CHECKER_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/sil-callback-log.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Base class for checking callback information. */ class CallbackChecker { public: /** * Constructor. */ CallbackChecker() { } /** * Destructor. */ virtual ~CallbackChecker() { } /** * Add an expected callback. * * \param modName the name of the module from which the callback is expected. * \param containerName the name of the top level container. * \param elementHierarchy a vector representing the hierarchy of elements * leading to the one being operated on. * \param operation the operation being performed (e.g. get, edit, mro). * \param type the callback type. * \param phase the callback phase. */ void addExpectedCallback(const std::string& modName, const std::string& containerName, const std::vector& elementHierarchy, const std::string& operation, const std::string& type, const std::string& phase); /** * Add expected callbacks for adding main container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. */ virtual void addMainContainer(const std::string& modName, const std::string& containerName) = 0; /** * Add expected callbacks for adding an element to a container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param element a vector representing the hierarchy of elements * leading to the element to be added. */ virtual void addElement(const std::string& modName, const std::string& containerName, const std::vector& element) = 0; /** * Add expected callbacks for adding a key to a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be added to. * \param key the key to be added. */ virtual void addKey(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key) = 0; /** * Add expected callbacks for adding a choice. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param choiceElement a vector representing the hierarchy of elements * leading to the choice. * \param removeElement a vector representing the hierarchy of elements * leading to the previous choice which should be removed. */ virtual void addChoice(const std::string& modName, const std::string& containerName, const std::vector& choiceElement, const std::vector& removeElement) = 0; /** * Add expected callbacks for adding a key value pair to a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be added to. * \param key the key to be added. * \param value the value to be added. */ virtual void addKeyValuePair(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value) = 0; /** * Add expected callbacks for commiting a number of key value pairs to a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be added to. * \param key the key to be added. * \param value the value to be added. * \param count the number of key value pairs to be added. */ virtual void commitKeyValuePairs(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value, int count) = 0; /** * Add expected callbacks for deleting a key from a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the key will be added to. * \param key the key to be added. */ virtual void deleteKey(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key) = 0; /** * Add expected callbacks for deleting a key value pair from a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be deleted from. * \param key the key to be deleted. * \param value the value to be deleted. */ virtual void deleteKeyValuePair(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value) = 0; /** * Add expected callbacks for adding a resource node. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param elements a vector representing the hierarchy of elements * leading to the resource node. * \param statusConfig true if the statusConfig leaf is added * \param alarmConfig true if the alarmConfig leaf is added */ virtual void addResourceNode(const std::string& modName, const std::string& containerName, const std::vector& elements, bool statusConfig, bool alarmConfig) = 0; /** * Add expected callbacks for adding a resource connection. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param elements a vector representing the hierarchy of elements * leading to the resource connection. */ virtual void addResourceCon(const std::string& modName, const std::string& containerName, const std::vector& elements) = 0; /** * Add expected callbacks for adding a stream connection. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param elements a vector representing the hierarchy of elements * leading to the stream connection. */ virtual void addStreamCon(const std::string& modName, const std::string& containerName, const std::vector& elements) = 0; /** * Add expected callbacks for adding a leaf to a container or updating a * leaf in a container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the leaf. * \param phase the specific edit operation which has beem invoked. */ virtual void updateLeaf(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase = "merge") = 0; /** * Add expected callbacks for creating a container or updating a container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the container; an empty vector means the top level container is * being checked. * \param phase the specific edit operation which has beem invoked. */ virtual void updateContainer(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase = "merge") = 0; /** * Check that the expected callbacks match those logged for a given module. * * \param modName the name of the module to check callbacks for. */ virtual void checkCallbacks(const std::string& modName); /** * Clear the expected callbacks. */ void resetExpectedCallbacks(); /** * Clear the logged callbacks for a given module. * * \param modName the name of the module to clear callbacks for. */ void resetModuleCallbacks(const std::string& modName); private: SILCallbackLog::ModuleCallbackData expectedCallbacks_; }; } // namespace YumaTest #endif // __CALLBACK_CHECKER_H //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/callbacks/system-cb-checker-factory.h0000664000175000017500000000240114770023131026314 0ustar vladimirvladimir#ifndef __YUMA_SYSTEM_CB_CHECKER_FACTORY_H #define __YUMA_SYSTEM_CB_CHECKER_FACTORY_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/abstract-cb-checker-factory.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class CallbackChecker; // ---------------------------------------------------------------------------| /** * A session factory for creating System CB Checkers. */ class SystemCBCheckerFactory : public AbstractCBCheckerFactory { public: /** * Constructor. */ explicit SystemCBCheckerFactory(); /** Destructor */ virtual ~SystemCBCheckerFactory(); /** * Create a new CallbackChecker. * * \return a new CallbackChecker. */ std::shared_ptr createChecker(); }; } // namespace YumaTest #endif // __YUMA_SYSTEM_CB_CHECKER_FACTORY_H yuma123_2.14/netconf/test/support/callbacks/candidate-cb-checker.cpp0000664000175000017500000003017514770023131025603 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/candidate-cb-checker.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| void CandidateCBChecker::addMainContainer(const std::string& modName, const std::string& containerName) { vector empty; addExpectedCallback(modName, containerName, empty, "edit", "validate", ""); } // ---------------------------------------------------------------------------| void CandidateCBChecker::addElement(const std::string& modName, const std::string& containerName, const std::vector& element) { addExpectedCallback(modName, containerName, element, "edit", "validate", ""); } // ---------------------------------------------------------------------------| void CandidateCBChecker::addKey(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key) { vector elements(listElement); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); } // ---------------------------------------------------------------------------| void CandidateCBChecker::addChoice(const std::string& modName, const std::string& containerName, const std::vector& choiceElement, const std::vector& removeElement) { vector deleteElements(removeElement); addExpectedCallback(modName, containerName, deleteElements, "edit", "validate", ""); vector addElements(choiceElement); addExpectedCallback(modName, containerName, addElements, "edit", "validate", ""); } // ---------------------------------------------------------------------------| void CandidateCBChecker::addKeyValuePair(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value) { vector elements(listElement); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.pop_back(); elements.push_back(value); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); } // ---------------------------------------------------------------------------| void CandidateCBChecker::commitKeyValuePairs(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value, int count) { vector elements; addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); for (int i = 0; i < count; i++) { elements = listElement; addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.pop_back(); elements.push_back(value); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); } elements.clear(); addExpectedCallback(modName, containerName, elements, "edit", "apply", ""); for (int i = 0; i < count; i++) { elements = listElement; addExpectedCallback(modName, containerName, elements, "edit", "apply", ""); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "apply", ""); elements.pop_back(); elements.push_back(value); addExpectedCallback(modName, containerName, elements, "edit", "apply", ""); } elements.clear(); addExpectedCallback(modName, containerName, elements, "edit", "commit", "create"); for (int i = 0; i < count; i++) { elements = listElement; addExpectedCallback(modName, containerName, elements, "edit", "commit", "create"); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "commit", "create"); elements.pop_back(); elements.push_back(value); addExpectedCallback(modName, containerName, elements, "edit", "commit", "create"); } } // ---------------------------------------------------------------------------| void CandidateCBChecker::deleteKey(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key) { vector elements(listElement); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); } // ---------------------------------------------------------------------------| void CandidateCBChecker::deleteKeyValuePair(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value) { vector elements(listElement); elements.push_back(value); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.pop_back(); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); elements.push_back(key); addExpectedCallback(modName, containerName, elements, "edit", "validate", ""); } // ---------------------------------------------------------------------------| void CandidateCBChecker::addResourceNode(const std::string& modName, const std::string& containerName, const std::vector& elements, bool statusConfig, bool alarmConfig) { vector hierarchy(elements); hierarchy.push_back("resourceNode"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.push_back("id"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("resourceType"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("configuration"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); if (statusConfig) { hierarchy.pop_back(); hierarchy.push_back("statusConfig"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); } if (alarmConfig) { hierarchy.pop_back(); hierarchy.push_back("alarmConfig"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); } hierarchy.pop_back(); hierarchy.push_back("physicalPath"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); } // ---------------------------------------------------------------------------| void CandidateCBChecker::addResourceCon(const std::string& modName, const std::string& containerName, const std::vector& elements) { vector hierarchy(elements); hierarchy.push_back("resourceConnection"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.push_back("id"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("sourceId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("sourcePinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("destinationId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("destinationPinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("bitrate"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); } // ---------------------------------------------------------------------------| void CandidateCBChecker::addStreamCon(const std::string& modName, const std::string& containerName, const std::vector& elements) { vector hierarchy(elements); hierarchy.push_back("streamConnection"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.push_back("id"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("sourceStreamId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("destinationStreamId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("sourceId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("sourcePinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("destinationId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("destinationPinId"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); hierarchy.pop_back(); hierarchy.push_back("bitrate"); addExpectedCallback(modName, containerName, hierarchy, "edit", "validate", ""); } // ---------------------------------------------------------------------------| void CandidateCBChecker::updateLeaf(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase) { addExpectedCallback(modName, containerName, listElement, "edit", "validate", ""); } // ---------------------------------------------------------------------------| void CandidateCBChecker::updateContainer(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase) { addExpectedCallback(modName, containerName, listElement, "edit", "validate", ""); } } // namespace YumaTest yuma123_2.14/netconf/test/support/callbacks/system-cb-checker-factory.cpp0000664000175000017500000000247614770023131026663 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/system-cb-checker-factory.h" #include "test/support/callbacks/system-cb-checker.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // File wide namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| SystemCBCheckerFactory::SystemCBCheckerFactory() { } // ---------------------------------------------------------------------------| SystemCBCheckerFactory::~SystemCBCheckerFactory() { } // ---------------------------------------------------------------------------| shared_ptr SystemCBCheckerFactory::createChecker() { return shared_ptr ( new SystemCBChecker() ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/callbacks/candidate-cb-checker.h0000664000175000017500000002320214770023131025241 0ustar vladimirvladimir#ifndef __CANDIDATE_CALLBACK_CHECKER_H #define __CANDIDATE_CALLBACK_CHECKER_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/callback-checker.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Support class for checking callback information when the candidate database * is in use. */ class CandidateCBChecker : public CallbackChecker { public: /** * Add expected callbacks for adding main container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. */ virtual void addMainContainer(const std::string& modName, const std::string& containerName); /** * Add expected callbacks for adding an element to a container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param element a vector representing the hierarchy of elements * leading to the element to be added. */ virtual void addElement(const std::string& modName, const std::string& containerName, const std::vector& element); /** * Add expected callbacks for adding a key to a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be added to. * \param key the key to be added. */ virtual void addKey(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key); /** * Add expected callbacks for adding a choice. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param choiceElement a vector representing the hierarchy of elements * leading to the choice. * \param removeElement a vector representing the hierarchy of elements * leading to the previous choice which should be removed. */ virtual void addChoice(const std::string& modName, const std::string& containerName, const std::vector& choiceElement, const std::vector& removeElement); /** * Add expected callbacks for adding a key value pair to a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be added to. * \param key the key to be added. * \param value the value to be added. */ virtual void addKeyValuePair(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value); /** * Add expected callbacks for commiting a number of key value pairs to a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be added to. * \param key the key to be added. * \param value the value to be added. * \param count the number of key value pairs to be added. */ virtual void commitKeyValuePairs(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value, int count); /** * Add expected callbacks for deleting a key from a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the key will be added to. * \param key the key to be added. */ virtual void deleteKey(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key); /** * Add expected callbacks for deleting a key value pair from a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be deleted from. * \param key the key to be deleted. * \param value the value to be deleted. */ virtual void deleteKeyValuePair(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value); /** * Add expected callbacks for adding a resource node. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param elements a vector representing the hierarchy of elements * leading to the resource node. * \param statusConfig true if the statusConfig leaf is added * \param alarmConfig true if the alarmConfig leaf is added */ virtual void addResourceNode(const std::string& modName, const std::string& containerName, const std::vector& elements, bool statusConfig, bool alarmConfig); /** * Add expected callbacks for adding a resource connection. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param elements a vector representing the hierarchy of elements * leading to the resource connection. */ virtual void addResourceCon(const std::string& modName, const std::string& containerName, const std::vector& elements); /** * Add expected callbacks for adding a stream connection. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param elements a vector representing the hierarchy of elements * leading to the stream connection. */ virtual void addStreamCon(const std::string& modName, const std::string& containerName, const std::vector& elements); /** * Add expected callbacks for adding a leaf to a container or updating a * leaf in a container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the leaf. * \param phase the specific edit operation which has beem invoked. */ virtual void updateLeaf(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase = "merge"); /** * Add expected callbacks for creating a container or updating a container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the container; an empty vector means the top level container is * being checked. * \param phase the specific edit operation which has beem invoked. */ virtual void updateContainer(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase = "merge"); }; } // namespace YumaTest #endif // __CANDIDATE_CALLBACK_CHECKER_H //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/callbacks/integ-cb-checker-factory.h0000664000175000017500000000262414770023131026105 0ustar vladimirvladimir#ifndef __YUMA_INTEG_CB_CHECKER_FACTORY_H #define __YUMA_INTEG_CB_CHECKER_FACTORY_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/abstract-cb-checker-factory.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class CallbackChecker; // ---------------------------------------------------------------------------| /** * A session factory for creating Integration CB Checkers. */ class IntegCBCheckerFactory : public AbstractCBCheckerFactory { public: /** * Constructor. * * \param usingCandidate if true indicates that the candidate database is in use. */ explicit IntegCBCheckerFactory(bool usingCandidate); /** Destructor */ virtual ~IntegCBCheckerFactory(); /** * Create a new CallbackChecker. * * \return a new CallbackChecker. */ std::shared_ptr createChecker(); private: bool usingCandidate_; }; } // namespace YumaTest #endif // __YUMA_INTEG_CB_CHECKER_FACTORY_H yuma123_2.14/netconf/test/support/callbacks/sil-callback-log.h0000664000175000017500000000534714770023131024453 0ustar vladimirvladimir#ifndef __SIL_CALLBACK_LOG_H #define __SIL_CALLBACK_LOG_H // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Support class for logging callback information for multiple modules. * Implements the Singleton pattern. */ class SILCallbackLog { public: /** Simple structure to hold callback information */ struct CallbackInfo { /** Constructor */ CallbackInfo(std::string name="", std::string type="", std::string phase="") : cbName(name), cbType(type), cbPhase(phase) { } /** Comparison of CallbackInfo */ bool operator==(const CallbackInfo& cbInfo) const; std::string cbName; std::string cbType; std::string cbPhase; }; /** Convenience typedef */ typedef std::vector ModuleCallbackData; /** * Returns the single instance of SILCallbackLog * * \return the SILCallbackLog instance. */ static SILCallbackLog& getInstance() { static SILCallbackLog instance; return instance; } /** * Add a callback to the log. * * \param modName the name of the module from which the callback originated. * \param cbInfo the callback information. */ void addCallback(const std::string& modName, CallbackInfo cbInfo); /** * Return a vector containing all callbacks logged for a given module. * * \param modName the name of the module to return callbacks for. * \return a vector of callback information. */ ModuleCallbackData getModuleCallbacks(const std::string& modName); /** * Clear the callbacks for a given module. * * \param modName the name of the module to clear callbacks for. */ void resetModuleCallbacks(const std::string& modName); private: // private as implementing Singleton pattern SILCallbackLog() {} ~SILCallbackLog() {} // not generated as implementing Singleton pattern SILCallbackLog(const SILCallbackLog &) = delete; SILCallbackLog& operator=(SILCallbackLog) = delete; typedef std::map CallbackMap; CallbackMap callbackMap_; }; } // namespace YumaTest #endif // __SIL_CALLBACK_LOG_H //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/callbacks/sil-callback-controller.cpp0000664000175000017500000000414714770023131026405 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/sil-callback-controller.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| void SILCallbackController::setValidateSuccess(bool success) { validateSuccess_ = success; } // ---------------------------------------------------------------------------| bool SILCallbackController::validateSuccess() { return validateSuccess_; } // ---------------------------------------------------------------------------| void SILCallbackController::setApplySuccess(bool success) { applySuccess_ = success; } // ---------------------------------------------------------------------------| bool SILCallbackController::applySuccess() { return applySuccess_; } // ---------------------------------------------------------------------------| void SILCallbackController::setCreateSuccess(bool success) { createSuccess_ = success; } // ---------------------------------------------------------------------------| bool SILCallbackController::createSuccess() { return createSuccess_; } // ---------------------------------------------------------------------------| void SILCallbackController::setDeleteSuccess(bool success) { deleteSuccess_ = success; } // ---------------------------------------------------------------------------| bool SILCallbackController::deleteSuccess() { return deleteSuccess_; } // ---------------------------------------------------------------------------| void SILCallbackController::reset() { validateSuccess_ = true; applySuccess_ = true; createSuccess_ = true; deleteSuccess_ = true; } } // namespace YumaTest yuma123_2.14/netconf/test/support/callbacks/sil-callback-log.cpp0000664000175000017500000000273714770023131025006 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/sil-callback-log.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| bool SILCallbackLog::CallbackInfo::operator==(const SILCallbackLog::CallbackInfo& cbInfo) const { return ((this->cbName == cbInfo.cbName) && (this->cbType == cbInfo.cbType) && (this->cbPhase == cbInfo.cbPhase)); } // ---------------------------------------------------------------------------| void SILCallbackLog::addCallback(const string& modName, CallbackInfo cbInfo) { callbackMap_[modName].push_back(cbInfo); } // ---------------------------------------------------------------------------| SILCallbackLog::ModuleCallbackData SILCallbackLog::getModuleCallbacks(const std::string& modName) { return callbackMap_[modName]; } // ---------------------------------------------------------------------------| void SILCallbackLog::resetModuleCallbacks(const std::string& modName) { callbackMap_[modName].clear(); } } // namespace YumaTest yuma123_2.14/netconf/test/support/callbacks/callback-checker.cpp0000664000175000017500000000754714770023131025050 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/callback-checker.h" // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; using namespace rel_ops; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| void CallbackChecker::addExpectedCallback(const string& modName, const string& containerName, const vector& elementHierarchy, const string& operation, const string& type, const string& phase) { string name = modName + "_" + containerName + "_"; BOOST_FOREACH (const string& val, elementHierarchy) { name += val + "_"; } name += operation; SILCallbackLog::CallbackInfo cbInfo(name, type, phase); expectedCallbacks_.push_back(cbInfo); } // ---------------------------------------------------------------------------| void CallbackChecker::checkCallbacks(const std::string& modName) { bool callbackError = false; SILCallbackLog& cbLog = SILCallbackLog::getInstance(); SILCallbackLog::ModuleCallbackData actualCallbacks = cbLog.getModuleCallbacks(modName); SILCallbackLog::ModuleCallbackData::iterator it_act, it_exp; for(it_act = actualCallbacks.begin(), it_exp = expectedCallbacks_.begin(); it_act != actualCallbacks.end() && it_exp != expectedCallbacks_.end(); ++it_act, ++it_exp) { BOOST_CHECK(*it_act == *it_exp); if (*it_act != *it_exp) { callbackError = true; } } BOOST_CHECK_MESSAGE(actualCallbacks.size() <= expectedCallbacks_.size(), "Unexpected callbacks were logged"); BOOST_CHECK_MESSAGE(actualCallbacks.size() >= expectedCallbacks_.size(), "Further callbacks were expected"); if (actualCallbacks.size() != expectedCallbacks_.size()) { callbackError = true; } // Debug output to help understand failures if (callbackError) { cout << "\nActual Callbacks:\n"; for(it_act = actualCallbacks.begin(); it_act != actualCallbacks.end(); ++it_act) { cout << it_act->cbName << ", " << it_act->cbType << ", " << it_act->cbPhase << "\n"; } cout << "\nExpected Callbacks:\n"; for(it_exp = expectedCallbacks_.begin(); it_exp != expectedCallbacks_.end(); ++it_exp) { cout << it_exp->cbName << ", " << it_exp->cbType << ", " << it_exp->cbPhase << "\n"; } } } // ---------------------------------------------------------------------------| void CallbackChecker::resetExpectedCallbacks() { expectedCallbacks_.clear(); } // ---------------------------------------------------------------------------| void CallbackChecker::resetModuleCallbacks(const std::string& modName) { SILCallbackLog& cbLog = SILCallbackLog::getInstance(); cbLog.resetModuleCallbacks(modName); } } // namespace YumaTest yuma123_2.14/netconf/test/support/callbacks/abstract-cb-checker-factory.h0000664000175000017500000000176514770023131026607 0ustar vladimirvladimir#ifndef __YUMA_ABSTRACT_CB_CHECKER_FACTORY_H #define __YUMA_ABSTRACT_CB_CHECKER_FACTORY_H // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class CallbackChecker; // ---------------------------------------------------------------------------| /** * An abstract CB Checker Factory. */ class AbstractCBCheckerFactory { public: /** * Constructor. */ explicit AbstractCBCheckerFactory() {} /** Destructor */ virtual ~AbstractCBCheckerFactory() {} /** * Create a new CallbackChecker. * * \return a new CallbackChecker. */ virtual std::shared_ptr createChecker() = 0; }; } // namespace YumaTest #endif // __YUMA_ABSTRACT_CB_CHECKER_FACTORY_H yuma123_2.14/netconf/test/support/callbacks/integ-cb-checker-factory.cpp0000664000175000017500000000310014770023131026426 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/integ-cb-checker-factory.h" #include "test/support/callbacks/running-cb-checker.h" #include "test/support/callbacks/candidate-cb-checker.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // File wide namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| IntegCBCheckerFactory::IntegCBCheckerFactory(bool usingCandidate) : usingCandidate_(usingCandidate) { } // ---------------------------------------------------------------------------| IntegCBCheckerFactory::~IntegCBCheckerFactory() { } // ---------------------------------------------------------------------------| shared_ptr IntegCBCheckerFactory::createChecker() { if (usingCandidate_) { return shared_ptr ( new CandidateCBChecker() ); } else { return shared_ptr ( new RunningCBChecker() ); } } } // namespace YumaTest yuma123_2.14/netconf/test/support/callbacks/system-cb-checker.h0000664000175000017500000002352714770023131024663 0ustar vladimirvladimir#ifndef __SYSTEM_CALLBACK_CHECKER_H #define __SYSTEM_CALLBACK_CHECKER_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/callback-checker.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Class to allow the skipping of callback checking during system tests. */ class SystemCBChecker : public CallbackChecker { public: /** * Add expected callbacks for adding main container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. */ virtual void addMainContainer(const std::string& modName, const std::string& containerName); /** * Add expected callbacks for adding an element to a container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param element a vector representing the hierarchy of elements * leading to the element to be added. */ virtual void addElement(const std::string& modName, const std::string& containerName, const std::vector& element); /** * Add expected callbacks for adding a key value pair to a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be added to. * \param key the key to be added. */ virtual void addKey(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key); /** * Add expected callbacks for adding a choice. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param choiceElement a vector representing the hierarchy of elements * leading to the choice. * \param removeElement a vector representing the hierarchy of elements * leading to the previous choice which should be removed. */ virtual void addChoice(const std::string& modName, const std::string& containerName, const std::vector& choiceElement, const std::vector& removeElement); /** * Add expected callbacks for adding a key value pair to a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be added to. * \param key the key to be added. * \param value the value to be added. */ virtual void addKeyValuePair(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value); /** * Add expected callbacks for commiting a number of key value pairs to a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be added to. * \param key the key to be added. * \param value the value to be added. * \param count the number of key value pairs to be added. */ virtual void commitKeyValuePairs(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value, int count); /** * Add expected callbacks for adding a leaf to a container or updating a * leaf in a container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the leaf. * \param phase the specific edit operation which has beem invoked. */ virtual void updateLeaf(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase = "merge"); /** * Add expected callbacks for creating a container or updating a container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the container; an empty vector means the top level container is * being checked. * \param phase the specific edit operation which has beem invoked. */ virtual void updateContainer(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase = "merge"); /** * Add expected callbacks for deleting a key from a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the key will be added to. * \param key the key to be added. */ virtual void deleteKey(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key); /** * Add expected callbacks for deleting a key value pair from a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be deleted from. * \param key the key to be deleted. * \param value the value to be deleted. */ virtual void deleteKeyValuePair(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value); /** * Add expected callbacks for adding a resource node. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param elements a vector representing the hierarchy of elements * leading to the resource node. * \param statusConfig true if the statusConfig leaf is added * \param alarmConfig true if the alarmConfig leaf is added */ virtual void addResourceNode(const std::string& modName, const std::string& containerName, const std::vector& elements, bool statusConfig, bool alarmConfig); /** * Add expected callbacks for adding a resource connection. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param elements a vector representing the hierarchy of elements * leading to the resource connection. */ virtual void addResourceCon(const std::string& modName, const std::string& containerName, const std::vector& elements); /** * Add expected callbacks for adding a stream connection. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param elements a vector representing the hierarchy of elements * leading to the stream connection. */ virtual void addStreamCon(const std::string& modName, const std::string& containerName, const std::vector& elements); /** * Check that the expected callbacks match those logged for a given module. * * \param modName the name of the module to check callbacks for. */ virtual void checkCallbacks(const std::string& modName); }; } // namespace YumaTest #endif // __SYSTEM_CALLBACK_CHECKER_H //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/callbacks/sil-callback-controller.h0000664000175000017500000000630414770023131026047 0ustar vladimirvladimir#ifndef __SIL_CALLBACK_CONTROLLER_H #define __SIL_CALLBACK_CONTROLLER_H // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Support class for controlling whether callbacks return success or failure. * Implements the Singleton pattern. */ class SILCallbackController { public: /** * Returns the single instance of SILCallbackController * * \return the SILCallbackController instance. */ static SILCallbackController& getInstance() { static SILCallbackController instance; return instance; } /** * Set success/failure of validate callbacks. * * \param success true if validate callbacks should succeed and false * otherwise */ void setValidateSuccess(bool success); /** * Should validate callbacks succeed? * * \return true if validate callbacks should succeed and false otherwise. */ bool validateSuccess(); /** * Set success/failure of apply callbacks. * * \param success true if apply callbacks should succeed and false * otherwise */ void setApplySuccess(bool success); /** * Should apply callbacks succeed? * * \return true if apply callbacks should succeed and false otherwise. */ bool applySuccess(); /** * Set success/failure of create callback. * * \param success true if create callbacks should succeed and false * otherwise */ void setCreateSuccess(bool success); /** * Should create callbacks succeed? * * \return true if create callbacks should succeed and false otherwise. */ bool createSuccess(); /** * Set success/failure of delete callbacks. * * \param success true if delete callbacks should succeed and false * otherwise */ void setDeleteSuccess(bool success); /** * Should delete callbacks succeed? * * \return true if delete callbacks should succeed and false otherwise. */ bool deleteSuccess(); /** * Set all callbacks to succeed. */ void reset(); private: // private as implementing Singleton pattern SILCallbackController() : validateSuccess_(true), applySuccess_(true), createSuccess_(true), deleteSuccess_(true) { } ~SILCallbackController() { } // not generated as implementing Singleton pattern SILCallbackController(const SILCallbackController &) = delete; SILCallbackController& operator=(SILCallbackController) = delete; bool validateSuccess_, applySuccess_; bool createSuccess_, deleteSuccess_; }; } // namespace YumaTest #endif // __SIL_CALLBACK_CONTROLLER_H //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/callbacks/running-cb-checker.h0000664000175000017500000002334714770023131025017 0ustar vladimirvladimir#ifndef __RUNNING_CALLBACK_CHECKER_H #define __RUNNING_CALLBACK_CHECKER_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/callbacks/callback-checker.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Support class for checking callback information when editing the running * database directly. */ class RunningCBChecker : public CallbackChecker { public: /** Constructor. */ RunningCBChecker(); /** Destructor */ virtual ~RunningCBChecker(); /** * Add expected callbacks for adding main container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. */ virtual void addMainContainer(const std::string& modName, const std::string& containerName); /** * Add expected callbacks for adding an element to a container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param element a vector representing the hierarchy of elements * leading to the element to be added. */ virtual void addElement(const std::string& modName, const std::string& containerName, const std::vector& element); /** * Add expected callbacks for adding a key to a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be added to. * \param key the key to be added. */ virtual void addKey(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key); /** * Add expected callbacks for adding a choice. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param choiceElement a vector representing the hierarchy of elements * leading to the choice. * \param removeElement a vector representing the hierarchy of elements * leading to the previous choice which should be removed. */ virtual void addChoice(const std::string& modName, const std::string& containerName, const std::vector& choiceElement, const std::vector& removeElement); /** * Add expected callbacks for adding a key value pair to a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be added to. * \param key the key to be added. * \param value the value to be added. */ virtual void addKeyValuePair(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value); /** * Add expected callbacks for commiting a number of key value pairs to a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be added to. * \param key the key to be added. * \param value the value to be added. * \param count the number of key value pairs to be added. */ virtual void commitKeyValuePairs(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value, int count); /** * Add expected callbacks for deleting a key from a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the key will be added to. * \param key the key to be added. */ virtual void deleteKey(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key); /** * Add expected callbacks for deleting a key value pair from a list. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the list that the pair will be deleted from. * \param key the key to be deleted. * \param value the value to be deleted. */ virtual void deleteKeyValuePair(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& key, const std::string& value); /** * Add expected callbacks for adding a resource node. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param elements a vector representing the hierarchy of elements * leading to the resource node. * \param statusConfig true if the statusConfig leaf is added * \param alarmConfig true if the alarmConfig leaf is added */ virtual void addResourceNode(const std::string& modName, const std::string& containerName, const std::vector& elements, bool statusConfig, bool alarmConfig); /** * Add expected callbacks for adding a resource connection. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param elements a vector representing the hierarchy of elements * leading to the resource connection. */ virtual void addResourceCon(const std::string& modName, const std::string& containerName, const std::vector& elements); /** * Add expected callbacks for adding a stream connection. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param elements a vector representing the hierarchy of elements * leading to the stream connection. */ virtual void addStreamCon(const std::string& modName, const std::string& containerName, const std::vector& elements); /** * Add expected callbacks for adding a leaf to a container or updating a * leaf in a container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the leaf. * \param phase the specific edit operation which has beem invoked. */ virtual void updateLeaf(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase = "merge"); /** * Add expected callbacks for creating a container or updating a container. * * \param modName the name of the module from which the callbacks are expected. * \param containerName the name of the top level container. * \param listElement a vector representing the hierarchy of elements * leading to the container; an empty vector means the top level container is * being checked. * \param phase the specific edit operation which has beem invoked. */ virtual void updateContainer(const std::string& modName, const std::string& containerName, const std::vector& listElement, const std::string& phase = "merge"); }; } // namespace YumaTest #endif // __RUNNING_CALLBACK_CHECKER_H //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/nc-session/0000775000175000017500000000000014770023131021333 5ustar vladimirvladimiryuma123_2.14/netconf/test/support/nc-session/abstract-nc-session-factory.h0000664000175000017500000000246514770023131027042 0ustar vladimirvladimir#ifndef __YUMA_ABSTRACT_NC_SESSION_FACTORY_H #define __YUMA_ABSTRACT_NC_SESSION_FACTORY_H // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractYumaOpLogPolicy; class AbstractNCSession; // ---------------------------------------------------------------------------| /** * An abstract NC Session Factory. */ class AbstractNCSessionFactory { public: /** * Constructor. */ explicit AbstractNCSessionFactory( std::shared_ptr< AbstractYumaOpLogPolicy > policy ) : policy_( policy ) , sessionId_( 0 ) {} /** Destructor */ virtual ~AbstractNCSessionFactory() {} /** * Create a new NCSession. * * \return a new NCSession. */ virtual std::shared_ptr createSession() = 0; protected: /** The policy for generating log filenames. */ std::shared_ptr< AbstractYumaOpLogPolicy > policy_; /** The current sessionId */ uint16_t sessionId_; }; } // namespace YumaTest #endif // __YUMA_ABSTRACT_NC_SESSION_FACTORY_H yuma123_2.14/netconf/test/support/nc-session/abstract-nc-session.h0000664000175000017500000000613014770023131025366 0ustar vladimirvladimir#ifndef __YUMA_ABSTRACT_NC_SESSION_H #define __YUMA_ABSTRACT_NC_SESSION_H // ---------------------------------------------------------------------------| #include #include #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractYumaOpLogPolicy; // ---------------------------------------------------------------------------| /** * Abstract base class for Netconf sessions. */ class AbstractNCSession { public: /** * Constructor. * * \param policy the log filename generation policy * \param sessionId the session id. */ AbstractNCSession( std::shared_ptr< AbstractYumaOpLogPolicy > policy, uint16_t sessionId ); /** Desstructor */ virtual ~AbstractNCSession(); /** * Inject the query into netconf. This function is used to inject * the supplied XML NC query. It returns the id of the message * that was used to inject the query. * * \param queryStr a string containing the XML message to inject. * \return the id of the message used to inject the query. */ virtual uint16_t injectMessage( const std::string& queryStr ) = 0; /** * Get the output of the query. * * \param messageId the id of the message containing the initial * query. * \return the query output returned by Yuma. */ virtual std::string getSessionResult( uint16_t messageId ) const; /** * Allocate a messageId. * * \return the next message id; */ uint16_t allocateMessageId() { return ++messageCount_; } /** * Return sessionId. * * \return the session id; */ uint16_t getId() { return sessionId_; } protected: /** * Genearte a new log filename and add it to the internal map * of messages to coressponding lig filename. * * \param queryStr the query to generate a new log file for. */ std::string newQueryLogFilename( const std::string& queryStr ); /** * Get the log filename associated with the message id. * * \param messageId the message id. * \return the associated log filename. */ std::string retrieveLogFilename( const uint16_t messageId ) const; /** * Concatenate all of thye logfiles genearted by this session into * a single large logfile. */ void concatenateLogFiles(); /** * Concatenate the associated logfile top op. * * \param op the output stream * \param key the key associated with the input filename; */ void appendLog( std::ofstream& op, uint16_t key ); protected: /** The session id */ uint16_t sessionId_; /** next message id */ uint16_t messageCount_; /** Output log filename generation policy */ std::shared_ptr< AbstractYumaOpLogPolicy > opLogFilenamePolicy_; /** A map of messageIds to output log filenames */ std::map< uint16_t, std::string > messageIdToLogMap_; }; } // namespace YumaTest #endif // __YUMA_ABSTRACT_NC_SESSION_H yuma123_2.14/netconf/test/support/nc-session/remote-nc-session.h0000664000175000017500000000517614770023131025067 0ustar vladimirvladimir#ifndef __YUMA_REMOTE_NC_SESSION_H #define __YUMA_REMOTE_NC_SESSION_H // ---------------------------------------------------------------------------| // test Harness Includes // ---------------------------------------------------------------------------| #include "test/support/nc-session/sys-test-nc-session-base.h" // ---------------------------------------------------------------------------| // STD Includes // ---------------------------------------------------------------------------| #include #include #include #include // ---------------------------------------------------------------------------! namespace YumaTest { /** * Utility class for creating a local connection to the netconf * server, bypassing SSH and netconf-subsytem.c. * * This class spoofs the work performed by netconf-subsystem.c * to allow a direct connection to the netconf server. */ // ---------------------------------------------------------------------------! class RemoteNCSession : public SysTestNCSessionBase { public: /** * Constructor. * * \param ipaddress the ipaddess of the remote host. * \param port the port to connect to * \param user the name of the user. * \param password the users password. * \param policy the log filename generation policy * \param sessionId the session id. */ RemoteNCSession( const std::string& ipAddress, const uint16_t port, const std::string& user, const std::string& password, std::shared_ptr< AbstractYumaOpLogPolicy > policy, uint16_t sessionId ); /** Destructor. */ virtual ~RemoteNCSession(); private: /** Connect to the netconf server */ virtual void connect(); /** * read the connection * * \param buffer the buffer to read into. * \param length the number of bytes to read. * \return the number of bytes read. */ virtual ssize_t receive( char* buffer, ssize_t length ); /** * write to the connection * * \param buffer the buffer to write. * \param length the number of bytes to sent. * \return the number of bytes read. */ virtual ssize_t send( const char* buffer, ssize_t length ); protected: std::string ipAddress_; ///< ip address of netconf server uint16_t port_; ///< netconf port std::string password_; ///< password for remote user LIBSSH2_SESSION* session_; ///< the ssh session LIBSSH2_CHANNEL* channel_; ///< the ssh channel }; } // YumaTest #endif // __YUMA_REMOTE_NC_SESSION_H yuma123_2.14/netconf/test/support/nc-session/spoof-nc-session.cpp0000664000175000017500000000741314770023131025251 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-session/spoof-nc-session.h" #include "test/support/nc-query-util/nc-query-utils.h" // ---------------------------------------------------------------------------| // STD Includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // libxml2 // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| #include "src/agt/agt_ses.h" #include "src/agt/agt_top.h" #include "src/ncx/xml_util.h" // ---------------------------------------------------------------------------| // Global namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------! namespace YumaTest { // ---------------------------------------------------------------------------! SpoofNCSession::SpoofNCSession( std::shared_ptr< AbstractYumaOpLogPolicy > policy, uint16_t sessionId ) : AbstractNCSession( policy, sessionId ) { } // ---------------------------------------------------------------------------! SpoofNCSession::~SpoofNCSession() { } // ---------------------------------------------------------------------------! uint16_t SpoofNCSession::injectMessage( const std::string& queryStr ) { BOOST_TEST_MESSAGE( "Injecting Test Message..." ); ses_cb_t* baldScbPtr = configureDummySCB( queryStr ); // dispatch the message - note this might free baldScbPtr if an // error occurred! agt_top_dispatch_msg( &baldScbPtr ); if ( baldScbPtr ) { agt_ses_free_dummy_session( baldScbPtr ); } return extractMessageId( queryStr ); } // ---------------------------------------------------------------------------! ses_cb_t* SpoofNCSession::configureDummySCB( const string& queryStr ) { ses_cb_t* baldScbPtr = agt_ses_new_dummy_session(); BOOST_REQUIRE_MESSAGE ( baldScbPtr, "Failed to generate dummy SCB" ); // set the session id baldScbPtr->sid = sessionId_; // get the logfilename baldScbPtr->fp = openLogFileForQuery( queryStr ); // Assign a memory xml reader, note this will be deallocated when // baldScbPtr is released. baldScbPtr->reader = xmlReaderForMemory( queryStr.c_str(), queryStr.size(), "", 0, XML_READER_OPTIONS ); BOOST_REQUIRE_MESSAGE( baldScbPtr->reader, "Error getting xmlTextReaderForMmemory!" ); return baldScbPtr; } // ---------------------------------------------------------------------------! FILE* SpoofNCSession::openLogFileForQuery( const string& queryStr ) { const string logFilename = newQueryLogFilename( queryStr ); // set the file descriptor for output from yuma FILE* fp = fopen( logFilename.c_str(), "w" ); BOOST_REQUIRE_MESSAGE( fp, "Failed to open file " << logFilename.c_str() ); // Write out the initial query. to the logfile fwrite( queryStr.c_str(), sizeof(char), queryStr.size(), fp ); fwrite( "\n", sizeof(char), sizeof(char), fp ); return fp; } } // namespace YumaTest yuma123_2.14/netconf/test/support/nc-session/sys-test-nc-session-base.h0000664000175000017500000000755114770023131026276 0ustar vladimirvladimir#ifndef __YUMA_SYSTEM_TEST_SESSION_BASE_H #define __YUMA_SYSTEM_TEST_SESSION_BASE_H // ---------------------------------------------------------------------------| // test Harness Includes // ---------------------------------------------------------------------------| #include "test/support/nc-session/abstract-nc-session.h" #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Base class for System Test Net Conf Sessions. * * This class simply provides some utility functions for sending& * receiving messages to/from the netconf server. */ class SysTestNCSessionBase : public AbstractNCSession { public: /** * Constructor * * \param user the name of the user. * \param policy the log filename generation policy * \param sessionId the session id. */ SysTestNCSessionBase( const std::string& user, std::shared_ptr< AbstractYumaOpLogPolicy > policy, uint16_t sessionId ); /** Destructor */ virtual ~SysTestNCSessionBase(); /** * Inject the query into netconf. This test function sends the * supplied query into the netconf server via a local or network * connection. It reads back any response from the netconf server * and writes it to the logfile. * * \param queryStr a string containing the XML message to inject. * \return the id of the message used to inject the query. */ virtual uint16_t injectMessage( const std::string& queryStr ); /** * Get the remote user. * * \return the user. */ const std::string& getUser() { return user_; } protected: /** * Send a hello response message to the server. * This message is called to establis a session after a successful * connection to the netconf server. The order of events is * *
    *
  1. a connection to the netconf server is established.
  2. *
  3. the netconf server sends it's capabilities.
  4. *
  5. send the hello message to complete session * establishment.
  6. *
*/ void sendHelloResponse(); private: /** * Send a query into netconf. * * \param queryStr a string containing the XML message to inject. */ void sendMessage( const std::string& queryStr ); /** * Read the reply from the netconf server. * * \return a string containing the reply. */ std::string receiveMessage(); /** * open output logfile for a query. * * \param queryStr a string containing the XML message to inject. * \return a file opened for output asa stream. */ std::shared_ptr openLogFileForQuery( const std::string& queryStr ); /** * open an output logfile. * * \param logFilename the name of the file to open. * \return a file opened for output asa stream. */ std::shared_ptr openLogFile( const std::string& logFilename ); /** * Connect to the netconf server */ virtual void connect() = 0; /** * read the connection * * \param buffer the buffer to read into. * \param length the number of bytes to read. * \return the number of bytes read. */ virtual ssize_t receive( char* buffer, ssize_t length ) = 0; /** * write to the connection * * \param buffer the buffer to write. * \param length the number of bytes to sent. * \return the number of bytes read. */ virtual ssize_t send( const char* buffer, ssize_t length ) = 0; protected: int ncxsock_; ///< The file descriptor for the socket bool connected_; ///< Flag indicating connection status std::string user_; ///< The remote user }; } // namespace YumaTest #endif // __YUMA_SYSTEM_TEST_SESSION_BASE_H yuma123_2.14/netconf/test/support/nc-session/abstract-nc-session.cpp0000664000175000017500000001131714770023131025724 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-session/abstract-nc-session.h" #include "test/support/nc-query-util/nc-query-utils.h" #include "test/support/nc-query-util/yuma-op-policies.h" // ---------------------------------------------------------------------------| // STD Includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Global namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| // Anonymous namespace // ---------------------------------------------------------------------------| namespace { string lineBreak( 75, '-' ); string testBreak( 75, '=' ); } // ---------------------------------------------------------------------------! namespace YumaTest { // ---------------------------------------------------------------------------! AbstractNCSession::AbstractNCSession( shared_ptr< AbstractYumaOpLogPolicy > policy, uint16_t sessionId ) : sessionId_( sessionId ) , messageCount_( 0 ) , opLogFilenamePolicy_( policy ) , messageIdToLogMap_() { } // ---------------------------------------------------------------------------! AbstractNCSession::~AbstractNCSession() { concatenateLogFiles(); } // ---------------------------------------------------------------------------! void AbstractNCSession::concatenateLogFiles() { namespace ph = boost::phoenix; using ph::arg_names::arg1; string logName = opLogFilenamePolicy_->generateSessionLog( sessionId_ ); ofstream op( logName ); BOOST_REQUIRE_MESSAGE( op, "Error opening output logfile: " << logName ); op << testBreak << "\n\n"; std::vector< uint16_t > keys; boost::transform( messageIdToLogMap_, back_inserter( keys ), ph::at_c<0>( arg1 ) ); auto sorted = boost::sort( keys ); boost::for_each( keys, ph::bind( &AbstractNCSession::appendLog, this, ph::ref( op ), arg1 ) ); } // ---------------------------------------------------------------------------! void AbstractNCSession::appendLog( ofstream& op, uint16_t key ) { ifstream in( messageIdToLogMap_[key] ); BOOST_REQUIRE_MESSAGE( in, "Error opening input logfile: " << messageIdToLogMap_[key] ); op << in.rdbuf() << "\n"; op << lineBreak << "\n\n"; } // ---------------------------------------------------------------------------! string AbstractNCSession::newQueryLogFilename( const string& queryStr ) { uint16_t messageId = extractMessageId( queryStr ); BOOST_REQUIRE_MESSAGE( ( messageIdToLogMap_.find( messageId ) == messageIdToLogMap_.end() ), "An entry already exists for message id: " << messageId ); string logFilename ( opLogFilenamePolicy_->generate( queryStr, sessionId_ ) ); messageIdToLogMap_[ messageId ] = logFilename; return logFilename; } // ---------------------------------------------------------------------------! string AbstractNCSession::retrieveLogFilename( const uint16_t messageId ) const { auto iter = messageIdToLogMap_.find( messageId ); BOOST_REQUIRE_MESSAGE( iter != messageIdToLogMap_.end(), "Entry Not found for messageId: " << messageId ); string logFilename = iter->second; if ( logFilename.empty() ) { BOOST_FAIL( "Empty log filename for Message Id: " << messageId ); } return logFilename; } // ---------------------------------------------------------------------------! string AbstractNCSession::getSessionResult( const uint16_t messageId ) const { // get the filename string logFilename( retrieveLogFilename( messageId ) ); // open the file ifstream inFile( logFilename.c_str() ); if ( !inFile ) { BOOST_FAIL( "Error opening file for Yuma output" ); } // read the result ostringstream oss; oss << inFile.rdbuf(); return oss.str(); } } // namespace YumaTest yuma123_2.14/netconf/test/support/nc-session/spoof-nc-session-factory.h0000664000175000017500000000263314770023131026362 0ustar vladimirvladimir#ifndef __YUMA_SPOOF_NC_SESSION_FACTORY_H #define __YUMA_SPOOF_NC_SESSION_FACTORY_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-session/abstract-nc-session-factory.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractYumaOpLogPolicy; class AbstractNCSession; // ---------------------------------------------------------------------------| /** * A session factory for creating Spoofed NC Sessions. */ class SpoofNCSessionFactory : public AbstractNCSessionFactory { public: /** * Constructor. * * \param policy the output log filename generation policy. */ explicit SpoofNCSessionFactory( std::shared_ptr< AbstractYumaOpLogPolicy > policy ); /** Destructor */ virtual ~SpoofNCSessionFactory(); /** * Create a new NCSession. * * \return a new NCSession. */ std::shared_ptr createSession(); }; } // namespace YumaTest #endif // __YUMA_SPOOF_NC_SESSION_FACTORY_H yuma123_2.14/netconf/test/support/nc-session/sys-test-nc-session-base.cpp0000664000175000017500000001755614770023131026637 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-session/sys-test-nc-session-base.h" #include "test/support/nc-query-util/nc-query-utils.h" #include "test/support/nc-query-util/nc-strings.h" #include "test/support/misc-util/array-deleter.h" #include "test/support/msg-util/NCMessageBuilder.h" #include "test/support/nc-query-util/yuma-op-policies.h" #include "test/support/fixtures/test-context.h" // ---------------------------------------------------------------------------| // STD Includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| // Boost Includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Global namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| // Anonymous namespace // ---------------------------------------------------------------------------| namespace { /// The maximum time to wait for blocking IO const uint16_t MaxReadWaitSeconds = 20; } // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------! SysTestNCSessionBase::SysTestNCSessionBase( const string& user, shared_ptr< AbstractYumaOpLogPolicy > policy, uint16_t sessionId ) : AbstractNCSession( policy, sessionId ) , ncxsock_( -1 ) , connected_( false ) , user_( user ) { } // ---------------------------------------------------------------------------! SysTestNCSessionBase::~SysTestNCSessionBase() { if ( connected_ ) { close( ncxsock_ ); } } // ---------------------------------------------------------------------------! uint16_t SysTestNCSessionBase::injectMessage( const string& queryStr ) { // open an output log file shared_ptr out = openLogFileForQuery( queryStr ); // write out the initial query to the logfile *out<< queryStr << "\n"; sendMessage( queryStr ); // read the reply and log it const string replyStr = receiveMessage(); *out << replyStr << "\n"; return extractMessageId( queryStr ); } // ---------------------------------------------------------------------------! void SysTestNCSessionBase::sendMessage( const string& queryStr ) { // TODO: Commonalise with receive ... const string fullQuery = queryStr + NC_SSH_END; BOOST_REQUIRE_MESSAGE( connected_, "ERROR Not connected to server!" ); ssize_t remainingLength = fullQuery.length(); const char* currentPosition = fullQuery.c_str(); ssize_t sentLength; uint16_t numRemainingRetries = MaxReadWaitSeconds * 1000; while( remainingLength && numRemainingRetries ) { sentLength = send(currentPosition, remainingLength); if ( sentLength ==0 || sentLength == LIBSSH2_ERROR_EAGAIN ) { --numRemainingRetries; usleep( 1000 ); } else if ( sentLength < 0 ) { BOOST_FAIL( "Yuma IO Opeartion Failed!" ); } else if ( sentLength > 0 ) { currentPosition += sentLength; remainingLength -= sentLength; } } if ( remainingLength ) { BOOST_FAIL( "Failed to send the full message, sent( " << fullQuery.length() - remainingLength << " ) bytes, attempted( " << fullQuery.length() << " )" ); } } // ---------------------------------------------------------------------------! void SysTestNCSessionBase::sendHelloResponse() { using namespace boost::xpressive; // write out the initial query to the logfile const string capabilities = receiveMessage(); // extract the session id as assigned by the remote agent sregex sesIdRegex_( sregex::compile( "(\\d+)<" ) ); smatch what; BOOST_REQUIRE_MESSAGE( regex_search( capabilities, what, sesIdRegex_ ), "Session ID not found in capabilities message!" ); sessionId_ = boost::lexical_cast( what[1] ); // open an output log file const string helloLogFile( opLogFilenamePolicy_->generateHelloLog( sessionId_ ) ); shared_ptr out = openLogFile( helloLogFile ); // the hello message is a special case and is always added as id 0. messageIdToLogMap_[ 0 ] = helloLogFile; *out<< capabilities << "\n"; // verify that the agent is running in the same mode as the test string dbName( TestContext::getTestContext()->writeableDbName_ ); if ( string::npos == capabilities.find( dbName ) ) { BOOST_FAIL( "Agent is not in '" << dbName << "' mode!" ); } // verify that the agent has the startup capability enabled if required bool usingStartup = TestContext::getTestContext()->usingStartup_; if ( usingStartup && (string::npos == capabilities.find( "startup" )) ) { BOOST_FAIL( "Agent has not enabled the startup capability!" ); } const string helloMsg = NCMessageBuilder().buildHelloMessage(); sendMessage( helloMsg ); *out<< helloMsg << "\n"; } // ---------------------------------------------------------------------------! string SysTestNCSessionBase::receiveMessage() { BOOST_REQUIRE_MESSAGE( connected_, "ERROR Not connected to server!" ); const ssize_t maxBufLen = 16*1024; shared_ptr buffer( new char[maxBufLen], ArrayDeleter() ); ssize_t remainingLength = maxBufLen; char* currentPosition = buffer.get(); ssize_t receivedLength; uint16_t numRemainingRetries = MaxReadWaitSeconds * 1000; // Note: the use case is send a message to the netconf server // then wait for a reply, ergo we will never received more // than one full message at a time. while( remainingLength && numRemainingRetries ) { receivedLength = receive(currentPosition, remainingLength); if ( receivedLength == 0 || receivedLength == LIBSSH2_ERROR_EAGAIN ) { --numRemainingRetries; usleep( 1000 ); } else if ( receivedLength < 0 ) { BOOST_FAIL( "Yuma IO Opeartion Failed!" ); } else if ( receivedLength > 0 ) { *(currentPosition+receivedLength)='\0'; if ( strstr( currentPosition, NC_SSH_END ) ) { string message( buffer.get() ); return message; } currentPosition += receivedLength; remainingLength -= receivedLength; } } return string(); } // ---------------------------------------------------------------------------! shared_ptr SysTestNCSessionBase::openLogFileForQuery( const string& queryStr ) { const string logFilename = newQueryLogFilename( queryStr ); return openLogFile( logFilename ); } // ---------------------------------------------------------------------------! shared_ptr SysTestNCSessionBase::openLogFile( const string& logFilename ) { shared_ptr outFile( new ofstream( logFilename.c_str() ) ); BOOST_REQUIRE_MESSAGE( *outFile, "Failed to open file " << logFilename.c_str() ); return outFile; } } // namespace YumaTest yuma123_2.14/netconf/test/support/nc-session/spoof-nc-session-factory.cpp0000664000175000017500000000266614770023131026723 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-session/spoof-nc-session-factory.h" #include "test/support/nc-session/spoof-nc-session.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // File wide namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| SpoofNCSessionFactory::SpoofNCSessionFactory( shared_ptr< AbstractYumaOpLogPolicy > policy ) : AbstractNCSessionFactory( policy ) { } // ---------------------------------------------------------------------------| SpoofNCSessionFactory::~SpoofNCSessionFactory() { } // ---------------------------------------------------------------------------| shared_ptr SpoofNCSessionFactory::createSession() { return shared_ptr ( new SpoofNCSession( policy_, ++sessionId_ ) ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/nc-session/remote-nc-session.cpp0000664000175000017500000001003414770023131025407 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-session/remote-nc-session.h" // ---------------------------------------------------------------------------| // STD Includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Platform Includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Global namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------! namespace YumaTest { // ---------------------------------------------------------------------------! RemoteNCSession::RemoteNCSession( const string& ipAddress, const uint16_t port, const string& user, const string& password, shared_ptr< AbstractYumaOpLogPolicy > policy, uint16_t sessionId ) : SysTestNCSessionBase( user, policy, sessionId ) , ipAddress_( ipAddress ) , port_( port ) , password_( password ) , channel_( 0 ) { ncxsock_ = socket( AF_INET, SOCK_STREAM, 0 ); assert( ( ncxsock_ >= 0 ) && "Error: socket creation failed!" ); connect(); sendHelloResponse(); } // ---------------------------------------------------------------------------! RemoteNCSession::~RemoteNCSession() { if( channel_ ) { libssh2_channel_free( channel_ ); } if( session_ ) { libssh2_session_disconnect( session_, "Normal Shutdown" ); libssh2_session_free( session_ ); } } // ---------------------------------------------------------------------------! void RemoteNCSession::connect() { sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons( port_ ); sin.sin_addr.s_addr = inet_addr( ipAddress_.c_str() ); if ( 0 != ::connect( ncxsock_, reinterpret_cast( &sin ), sizeof( sockaddr_in ) ) ) { assert( false && "NCX Socket Connect Failed!" ); } // Create a session instance and start it up. This will trade welcome // banners, exchange keys, and setup crypto, compression, and MAC layers session_ = libssh2_session_init(); assert( session_ && "Fatal: Failure initialising SSH session" ); if (libssh2_session_startup(session_, ncxsock_)) { assert( false && "Fatal: Failure establishing SSH session" ); } connected_ = true; // We could authenticate via password if ( libssh2_userauth_password( session_, user_.c_str(), password_.c_str())) { assert( false && "Fatal: Authentication by password failed" ); } // Request a shell channel_ = libssh2_channel_open_session( session_ ); if ( !channel_ ) { assert( false && "Fatal: Unable to open a session" ); } libssh2_channel_subsystem( channel_, "netconf" ); // set the channel to non-blocking libssh2_session_set_blocking( session_, 0 ); } // ---------------------------------------------------------------------------! ssize_t RemoteNCSession::receive( char* buffer, const ssize_t length ) { ssize_t count = libssh2_channel_read( channel_, buffer, length ); return count; } // ---------------------------------------------------------------------------! ssize_t RemoteNCSession::send( const char* buffer, const ssize_t length ) { ssize_t count = libssh2_channel_write( channel_, buffer, length ); return count; } } // namespace YumaTest yuma123_2.14/netconf/test/support/nc-session/spoof-nc-session.h0000664000175000017500000000506614770023131024720 0ustar vladimirvladimir#ifndef __YUMA_SPOOF_NC_SESSION_H #define __YUMA_SPOOF_NC_SESSION_H // ---------------------------------------------------------------------------| // test Harness Includes // ---------------------------------------------------------------------------| #include "test/support/nc-session/abstract-nc-session.h" // ---------------------------------------------------------------------------| // Standard Includes // ---------------------------------------------------------------------------| #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Includes // ---------------------------------------------------------------------------| #include "src/ncx/ses.h" // ---------------------------------------------------------------------------| namespace YumaTest { /** * A 'Spoofed' Netconf session. This class is used by integration test * harnesses to inject queries directly into Yuma by calling * agt_top_dispatch_msg directly. It is responsible for spoofing all * configuration required by Yuma for the operation to succeed. */ class SpoofNCSession : public AbstractNCSession { public: /** utility typedef of shared pointer to ses_cb_t */ typedef std::shared_ptr SharedSCB_T; /** Constructor. * * \param policy the log filename generation policy * \param sessionId the id of the session */ SpoofNCSession( std::shared_ptr< AbstractYumaOpLogPolicy > policy, uint16_t sessionId ); /** Destructor */ virtual ~SpoofNCSession(); /** * Inject the query into netconf. This test function spoofs up the * structures required for calling agt_top_dispatch_msg(), and * makes the call, redirecting all output to an appropriate * logfile. * * \param queryStr a string containing the XML message to inject. * \return the id of the message used to inject the query. */ virtual uint16_t injectMessage( const std::string& queryStr ); private: /** * Create and configure the dummy SCB structure. * * \param query a string containing the XML message to inject. * \return a 'bald' pointer to an ses_cb_t. */ ses_cb_t* configureDummySCB( const std::string& queryStr ); /** * open output logfile. * * \param queryStr a string containing the XML message to inject. * \return a FILE* opened with fopen. */ FILE* openLogFileForQuery( const std::string& queryStr ); }; } // namespace YumaTest #endif // __YUMA_NC_SESSION_H yuma123_2.14/netconf/test/support/nc-session/remote-nc-session-factory.h0000664000175000017500000000376614770023131026537 0ustar vladimirvladimir#ifndef __YUMA_SPOOF_NC_SESSION_FACTORY_H #define __YUMA_SPOOF_NC_SESSION_FACTORY_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-session/abstract-nc-session-factory.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractYumaOpLogPolicy; class AbstractNCSession; // ---------------------------------------------------------------------------| /** * A session factory for creating Remote NC Sessions. */ class RemoteNCSessionFactory : public AbstractNCSessionFactory { public: /** * Constructor. * * \param ipaddress the ipaddess of the remote host. * \param port the port to connect to * \param user the name of the user. * \param password the users password. * \param policy the output log filename generation policy. */ RemoteNCSessionFactory( const std::string& ipAddress, const uint16_t port, const std::string& user, const std::string& password, std::shared_ptr< AbstractYumaOpLogPolicy > policy ); /** Destructor */ virtual ~RemoteNCSessionFactory(); /** * Create a new NCSession. * * \return a new NCSession. */ std::shared_ptr createSession(); private: const std::string serverIpAddress_; ///< The Ip Address of the remote server uint16_t port_; ///< The port to connect to const std::string user_; ///< The remote user const std::string password_; ///< The user's password }; } // namespace YumaTest #endif // __YUMA_SPOOF_NC_SESSION_FACTORY_H yuma123_2.14/netconf/test/support/nc-session/remote-nc-session-factory.cpp0000664000175000017500000000337214770023131027063 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-session/remote-nc-session-factory.h" #include "test/support/nc-session/remote-nc-session.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // File wide namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| RemoteNCSessionFactory::RemoteNCSessionFactory( const std::string& ipAddress, const uint16_t port, const std::string& user, const std::string& password, shared_ptr< AbstractYumaOpLogPolicy > policy ) : AbstractNCSessionFactory( policy ) , serverIpAddress_( ipAddress ) , port_( port ) , user_( user ) , password_( password ) { } // ---------------------------------------------------------------------------| RemoteNCSessionFactory::~RemoteNCSessionFactory() { } // ---------------------------------------------------------------------------| shared_ptr RemoteNCSessionFactory::createSession() { return shared_ptr ( new RemoteNCSession( serverIpAddress_, port_, user_, password_, policy_, ++sessionId_ ) ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/checkers/0000775000175000017500000000000014770023131021041 5ustar vladimirvladimiryuma123_2.14/netconf/test/support/checkers/log-entry-presence-checkers.h0000664000175000017500000000602514770023131026524 0ustar vladimirvladimir#ifndef __YUMA_LOG_ENTRY_PRESENCE_CHECKERS_H #define __YUMA_LOG_ENTRY_PRESENCE_CHECKERS_H // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Check the netconf log includes all the supplied strings. */ class LogEntryPresenceChecker { public: /** * Constructor. * * \param expPresentText a list of strings that must be * present in the log for the test to pass */ explicit LogEntryPresenceChecker( const std::vector< std::string >& expPresentText ); /** * Destructor */ ~LogEntryPresenceChecker(); /** * Check the log results * * \param logFilePath the path/name of the log file to check */ void operator()( const std::string& logFilePath ) const; private: /** list of strings that must be present in the log */ const std::vector< std::string >& checkStrings_; }; /** * Check the netconf log excludes all the supplied strings. */ class LogEntryNonPresenceChecker { public: /** * Constructor. * * \param expNotPresentText a list of strings that must not be * present in the log for the test to pass */ explicit LogEntryNonPresenceChecker( const std::vector< std::string >& expNotPresentText ); /** * Destructor */ ~LogEntryNonPresenceChecker(); /** * Check the query results * * \param logFilePath the path/name of the log file to check */ void operator()( const std::string& logFilePath ) const; private: /** list of strings that must not be present in the output */ const std::vector< std::string >& checkStrings_; }; /** * Convenience Checker for checking for the presence and absence of * log entries. */ class LogEntriesPresentNotPresentChecker { public: /** * Constructor. * * \param expPresentReplyText a list of strings that must be * present in the netconf log for the test to pass * \param expAbsentReplyText a list of strings that must NOT be * present in the netconf log for the test to pass */ LogEntriesPresentNotPresentChecker( const std::vector< std::string >& expPresentLogText, const std::vector< std::string >& expAbsentLogText ); /** * Destructor */ ~LogEntriesPresentNotPresentChecker(); /** * Check the query results * * \param logFilePath the path/name of the log file to check */ void operator()( const std::string& logFilePath ) const; private: /** checker for list of entries that must be present in the log */ LogEntryPresenceChecker expPresentChecker_; /** checker for list of entries that must not be present in the log */ LogEntryNonPresenceChecker expNotPresentChecker_; }; } // namespace YumaTest #endif // __YUMA_LOG_ENTRY_PRESENCE_CHECKERS_H yuma123_2.14/netconf/test/support/checkers/string-presence-checkers.cpp0000664000175000017500000000625214770023131026447 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Global namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| StringPresenceChecker::StringPresenceChecker( const vector& expPresentReplyText ) : checkStrings_( expPresentReplyText ) { } // ---------------------------------------------------------------------------| StringPresenceChecker::~StringPresenceChecker() { } // ---------------------------------------------------------------------------| void StringPresenceChecker::operator()( const string& checkStr ) const { BOOST_FOREACH ( const string& val, checkStrings_ ) { BOOST_TEST_MESSAGE( "\tChecking " << val << " is present" ); BOOST_CHECK_NE( string::npos, checkStr.find( val ) ); } } // ---------------------------------------------------------------------------| StringNonPresenceChecker::StringNonPresenceChecker( const vector& expPresentReplyText ) : checkStrings_( expPresentReplyText ) { } // ---------------------------------------------------------------------------| StringNonPresenceChecker::~StringNonPresenceChecker() { } // ---------------------------------------------------------------------------| void StringNonPresenceChecker::operator()( const string& checkStr ) const { BOOST_FOREACH ( const string& val, checkStrings_ ) { BOOST_TEST_MESSAGE( "\tChecking " << val << " is NOT present" ); BOOST_CHECK_EQUAL( string::npos, checkStr.find( val ) ); } } // ---------------------------------------------------------------------------| StringsPresentNotPresentChecker::StringsPresentNotPresentChecker( const vector< string >& expPresentReplyText, const vector< string >& expAbsentReplyText ) : expPresentChecker_( expPresentReplyText ) , expNotPresentChecker_( expAbsentReplyText ) { } // ---------------------------------------------------------------------------| StringsPresentNotPresentChecker::~StringsPresentNotPresentChecker() { } // ---------------------------------------------------------------------------| void StringsPresentNotPresentChecker::operator()( const string& queryResult ) const { expPresentChecker_( queryResult ); expNotPresentChecker_( queryResult ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/checkers/checker-group.cpp0000664000175000017500000000372014770023131024305 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/checkers/checker-group.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // Boost // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Global namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| CheckerGroup::CheckerGroup() : checkers_() { } // ---------------------------------------------------------------------------| CheckerGroup::~CheckerGroup() { } // ---------------------------------------------------------------------------| void CheckerGroup::operator()( const string& str ) { BOOST_FOREACH( CheckSignature_T& chk, checkers_ ) { chk( str ); } } // ---------------------------------------------------------------------------| CheckerGroup& CheckerGroup::registerChecker( CheckSignature_T& checker ) { checkers_.push_back( checker ); return *this; } // ---------------------------------------------------------------------------| CheckerGroup& CheckerGroup::operator()( CheckSignature_T& checker ) { checkers_.push_back( checker ); return *this; } } yuma123_2.14/netconf/test/support/checkers/xml-content-checker.h0000664000175000017500000000653214770023131025072 0ustar vladimirvladimir#ifndef __YUMA_XML_DATABASE_CONTENT_CHECKER_H #define __YUMA_XML_DATABASE_CONTENT_CHECKER_H // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include "test/support/misc-util/ptree-utils.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| namespace YumaTest { template struct EmptyOp { void operator()( T& expData, const T& queryData ) const {} }; /** * Check the results of a netconf query and ensure that all the * supplied strings are present. * * \tparam the type of data being checked. This type must provide a * constructor that allows population from a boost::property tree. * * TODO: Add additional parameter to allow customisation of the check * function (currently this is hard-coded to 'checkEqual' */ template > class XMLContentChecker { public: typedef T type; typedef FuncTor CustomUpdateHook; /** * Constructor. * * \param expData the contents to check. This is the test * harness's model of the remote database. */ XMLContentChecker( std::shared_ptr expData, const CustomUpdateHook& hook = EmptyOp() ) : expData_( expData ) , customHook_( hook ) { } /** * Destructor */ virtual ~XMLContentChecker() { } /** * Check the query results. This function extracts the ... * section from the returned query and uses it to construct an * object of 'type', containing the actual results returned by the * Netconf server. These results are then checked using the * function checkEqual. * * \param queryResult the query to check, */ void operator()( const std::string& queryResult ) { using namespace boost::xpressive; using boost::property_tree::ptree; // extract the .... part of the result sregex re = sregex::compile( "(.*)" ); smatch what; BOOST_REQUIRE_MESSAGE( regex_search( queryResult, what, re ), "No section in result!" ); ptree pt = PTreeUtils::ParseXMlString( what[0] ); type queryData( pt ); customHook_( *expData_, queryData ); checkEqual( *expData_, queryData ); } protected: /** * Hook to allow custom inspection of results. * * \param queryData the data from the server. */ virtual void customCheckHook( const type& queryData ) { (void) queryData; // no operation; } protected: /** the expected results */ std::shared_ptr expData_; /** the custom hook functor */ const CustomUpdateHook& customHook_; }; } // namespace YumaTest #endif // __YUMA_XML_DATABASE_CONTENT_CHECKER_H yuma123_2.14/netconf/test/support/checkers/log-entry-presence-checkers.cpp0000664000175000017500000001003614770023131027054 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/checkers/log-entry-presence-checkers.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Global namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| LogEntryPresenceChecker::LogEntryPresenceChecker( const vector& expPresentText ) : checkStrings_( expPresentText ) { } // ---------------------------------------------------------------------------| LogEntryPresenceChecker::~LogEntryPresenceChecker() { } // ---------------------------------------------------------------------------| void LogEntryPresenceChecker::operator()( const string& logFilePath ) const { ifstream logFile; string line; bool entryFound; BOOST_FOREACH ( const string& val, checkStrings_ ) { entryFound = false; BOOST_TEST_MESSAGE( "\tChecking " << val << " is present" ); logFile.open( logFilePath ); if (logFile.is_open()) { while ( logFile.good() && entryFound == false ) { getline(logFile, line); if ( line.find( val ) != string::npos ) { entryFound = true; } } logFile.close(); } BOOST_CHECK_EQUAL( true, entryFound ); } } // ---------------------------------------------------------------------------| LogEntryNonPresenceChecker::LogEntryNonPresenceChecker( const vector& expNotPresentText ) : checkStrings_( expNotPresentText ) { } // ---------------------------------------------------------------------------| LogEntryNonPresenceChecker::~LogEntryNonPresenceChecker() { } // ---------------------------------------------------------------------------| void LogEntryNonPresenceChecker::operator()( const string& logFilePath ) const { ifstream logFile; string line; bool entryFound; BOOST_FOREACH ( const string& val, checkStrings_ ) { entryFound = false; BOOST_TEST_MESSAGE( "\tChecking " << val << " is NOT present" ); logFile.open( logFilePath ); if (logFile.is_open()) { while ( logFile.good() && entryFound == false ) { getline(logFile, line); if ( line.find( val ) != string::npos ) { entryFound = true; } } logFile.close(); } BOOST_CHECK_NE( true, entryFound ); } } // ---------------------------------------------------------------------------| LogEntriesPresentNotPresentChecker::LogEntriesPresentNotPresentChecker( const vector< string >& expPresentLogText, const vector< string >& expAbsentLogText ) : expPresentChecker_( expPresentLogText ) , expNotPresentChecker_( expAbsentLogText ) { } // ---------------------------------------------------------------------------| LogEntriesPresentNotPresentChecker::~LogEntriesPresentNotPresentChecker() { } // ---------------------------------------------------------------------------| void LogEntriesPresentNotPresentChecker::operator()( const string& logFilePath ) const { expPresentChecker_( logFilePath ); expNotPresentChecker_( logFilePath ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/checkers/string-presence-checkers.h0000664000175000017500000000577714770023131026127 0ustar vladimirvladimir#ifndef __YUMA_STRING_PRESENCE_CHECKERS_H #define __YUMA_STRING_PRESENCE_CHECKERS_H // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Check the results of a netconf query and ensure that all the * supplied strings are present. */ class StringPresenceChecker { public: /** * Constructor. * * \param expPresentText a list of strings that must be * present in the reply for the test to pass */ explicit StringPresenceChecker( const std::vector< std::string >& expPresentText ); /** * Destructor */ ~StringPresenceChecker(); /** * Check the query results * * \param queryResult the query to check, */ void operator()( const std::string& queryResult ) const; private: /** list of strings that must be present in the output */ const std::vector< std::string >& checkStrings_; }; /** * Check the results of a netconf query and ensure that all the * supplied strings are not present. */ class StringNonPresenceChecker { public: /** * Constructor. * * \param expPresentText a list of strings that must be * present in the reply for the test to pass */ explicit StringNonPresenceChecker( const std::vector< std::string >& expNotPresentText ); /** * Destructor */ ~StringNonPresenceChecker(); /** * Check the query results * * \param queryResult the query to check, */ void operator()( const std::string& queryResult ) const; private: /** list of strings that must be present in the output */ const std::vector< std::string >& checkStrings_; }; /** * Convenience Checker for checking for the presence and absence of * strings. */ class StringsPresentNotPresentChecker { public: /** * Constructor. * * \param expPresentReplyText a list of strings that must be * present in the reply for the test to pass * \param expAbsentReplyText a list of strings that must NOT be * present in the reply for the test to pass */ StringsPresentNotPresentChecker( const std::vector< std::string >& expPresentReplyText, const std::vector< std::string >& expAbsentReplyText ); /** * Destructor */ ~StringsPresentNotPresentChecker(); /** * Check the query results * * \param queryResult the query to check, */ void operator()( const std::string& queryResult ) const; private: /** checker for list of strings that must be present in the output */ StringPresenceChecker expPresentChecker_; /** checker for list of strings that must not be present in the output */ StringNonPresenceChecker expNotPresentChecker_; }; } // namespace YumaTest #endif // __YUMA_STRING_PRESENCE_CHECKER_H yuma123_2.14/netconf/test/support/checkers/checker-group.h0000664000175000017500000000257214770023131023756 0ustar vladimirvladimir#ifndef __YUMA_CHECKER_GROUP_H #define __YUMA_CHECKER_GROUP_H // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Utility class for grouping checkers together so that multiple * different checks can be performed on a test query output. */ class CheckerGroup { public: /** Convenience typedef. */ typedef std::function< void ( const std::string& ) > CheckSignature_T; public: /** Constructor */ CheckerGroup(); /** Destructor */ ~CheckerGroup() ; /** * Run the registered checkers. * * \param queryResult the query to check, */ void operator()( const std::string& str ); /** * Register a checker, that matches the CheckSignature_T. * * \param checker the checker to register. * \return a reference to this object. */ CheckerGroup& registerChecker( CheckSignature_T& checker ); /** * Convenience initialisation operator. * * \param checker the checker to register. * \return a reference to this object. */ CheckerGroup& operator()( CheckSignature_T& checker ); private: std::vector< CheckSignature_T > checkers_; }; } // namespace YumaTest #endif // __YUMA_CHECKER_GROUP_H yuma123_2.14/netconf/test/support/fixtures/0000775000175000017500000000000014770023131021123 5ustar vladimirvladimiryuma123_2.14/netconf/test/support/fixtures/simple-container-module-fixture.cpp0000664000175000017500000004751714770023131030065 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-container-module-fixture.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/misc-util/log-utils.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| // TODO: Much of the fuctionality in this file will probably be needed // TODO: by other tests - place it in a base class.... namespace { // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| /** * Simple utility functor that converts the supplied pair into xml NVP * strings and stores them in the supplied vector. This functor is * used to generate a list of values that should be present in * database content query replies. */ struct EntriesConvertFunctor { /** * constructor. * * \param target reference to the output vector of strings. */ explicit EntriesConvertFunctor( vector& target ) : target_( target ) { } /** * Convert the supplied pair into a 2 xmNVP strings. * * \param val the map entry to convert.tags */ void operator()( const std::pair& val ) { target_.push_back( "" + val.first + "" ); target_.push_back( "" + val.second + "" ); } /** reference to the ouput string vector. */ vector& target_; }; // ---------------------------------------------------------------------------| /** * Simple utility functor that converts any values not present in the * reference map into xml NVP strings and stores them in the supplied vector. * This functor is used to generate a list of values that should NOT be present * in database content query replies. */ struct AddDifferingEntriesFunctor { /** Convenience Typedef */ typedef YumaTest::SimpleContainerModuleFixture::SharedPtrEntryMap_T SharedPtrEntryMap_T; /** * Constructor * * \param target reference to the output vector of strings. * \param refMap the reference map of values that should be present. */ AddDifferingEntriesFunctor( vector& target, const SharedPtrEntryMap_T refMap ) : target_( target ) , refMap_( refMap ) { } /** * Check if the supplied value is in the reference map. If it is * not format an xml NVP string and store it. * * \param val the map entry to check. */ void operator()( const std::pair& val ) { auto pos = refMap_->find( val.first ); if ( pos == refMap_->end() ) { target_.push_back( "" + val.first + "" ); } else if ( pos->second != val.second ) { target_.push_back( "" + val.second + "" ); } } vector& target_; /// ref to the output string vector. const SharedPtrEntryMap_T refMap_; /// the reference map. }; } // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| SimpleContainerModuleFixture::SimpleContainerModuleFixture() : QuerySuiteFixture() , moduleNs_( "http://netconfcentral.org/ns/simple_list_test" ) , containerName_( "simple_list" ) , runningEntries_( new EntryMap_T() ) , candidateEntries_( useCandidate() ? SharedPtrEntryMap_T( new EntryMap_T() ) : runningEntries_ ) ,rollbackEntries_( new EntryMap_T() ) { // ensure the module is loaded queryEngine_->loadModule( primarySession_, "simple_list_test" ); } // ---------------------------------------------------------------------------| SimpleContainerModuleFixture::~SimpleContainerModuleFixture() { } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::createMainContainer( std::shared_ptr session ) { assert( session ); string query = messageBuilder_->genTopLevelContainerText( containerName_, moduleNs_, "create" ); runEditQuery( session, query ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::deleteMainContainer( std::shared_ptr session ) { string query = messageBuilder_->genTopLevelContainerText( containerName_, moduleNs_, "delete" ); runEditQuery( session, query ); candidateEntries_->clear(); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::mainContainerOp( std::shared_ptr session, const string& op ) { assert( session ); string query = messageBuilder_->genTopLevelContainerText( containerName_, moduleNs_, op ); runEditQuery( session, query ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::addEntry( std::shared_ptr session, const string& entryKeyStr, const string& operationStr ) { assert( session ); string query = messageBuilder_->genModuleOperationText( containerName_, moduleNs_, messageBuilder_->genKeyOperationText( "theList", "theKey", entryKeyStr, operationStr ) ); runEditQuery( session, query ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::addEntryValue( std::shared_ptr session, const string& entryKeyStr, const string& entryValStr, const string& operationStr ) { assert( session ); string query = messageBuilder_->genModuleOperationText( containerName_, moduleNs_, messageBuilder_->genKeyParentPathText( "theList", "theKey", entryKeyStr, messageBuilder_->genOperationText( "theVal", entryValStr, operationStr ) ) ); runEditQuery( session, query ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::addEntryValuePair( std::shared_ptr session, const string& entryKeyStr, const string& entryValStr ) { assert( session ); addEntry( session, entryKeyStr, "create" ); addEntryValue( session, entryKeyStr, entryValStr, "create" ); (*candidateEntries_)[ entryKeyStr ] = entryValStr; } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::mergeEntryValuePair( std::shared_ptr session, const string& entryKeyStr, const string& entryValStr ) { assert( session ); addEntry( session, entryKeyStr, "merge" ); addEntryValue( session, entryKeyStr, entryValStr, "merge" ); // if (candidateEntries_->find(entryKeyStr) == candidateEntries_->end()) // { (*candidateEntries_)[ entryKeyStr ] = entryValStr; // } //DEBUG EntryMap_T::iterator it; BOOST_TEST_MESSAGE("JOE DEBUG OP (MERGE) - " << entryKeyStr << ". " << entryValStr << ":"); for (it = candidateEntries_->begin(); it != candidateEntries_->end(); it++) { BOOST_TEST_MESSAGE((*it).first << " => " << (*it).second << "\n"); } //DEBUG } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::replaceEntryValuePair( std::shared_ptr session, const string& entryKeyStr, const string& entryValStr ) { assert( session ); addEntry( session, entryKeyStr, "replace" ); addEntryValue( session, entryKeyStr, entryValStr, "replace" ); (*candidateEntries_)[ entryKeyStr ] = entryValStr; //DEBUG EntryMap_T::iterator it; BOOST_TEST_MESSAGE("JOE DEBUG OP (REPLACE) - " << entryKeyStr << ". " << entryValStr << ":\n"); for (it = candidateEntries_->begin(); it != candidateEntries_->end(); it++) { BOOST_TEST_MESSAGE((*it).first << " => " << (*it).second << "\n"); } //DEBUG } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::noOpEntryValuePair( std::shared_ptr session, const string& entryKeyStr, const string& entryValStr ) { assert( session ); addEntry( session, entryKeyStr, "" ); addEntryValue( session, entryKeyStr, entryValStr, "" ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::deleteEntry( std::shared_ptr session, const string& entryKeyStr ) { assert( session ); string query = messageBuilder_->genModuleOperationText( containerName_, moduleNs_, messageBuilder_->genKeyOperationText( "theList", "theKey", entryKeyStr, "delete" ) ); runEditQuery( session, query ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::deleteEntryFailed( std::shared_ptr session, const string& entryKeyStr ) { assert( session ); string query = messageBuilder_->genModuleOperationText( containerName_, moduleNs_, messageBuilder_->genKeyOperationText( "theList", "theKey", entryKeyStr, "delete" ) ); runFailedEditQuery( session, query, "data missing" ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::deleteEntryValue( std::shared_ptr session, const string& entryKeyStr, const string& entryValStr ) { assert( session ); string query = messageBuilder_->genModuleOperationText( containerName_, moduleNs_, messageBuilder_->genKeyParentPathText( "theList", "theKey", entryKeyStr, messageBuilder_->genOperationText( "theVal", entryValStr, "delete" ) ) ); runEditQuery( session, query ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::deleteEntryValueFailed( std::shared_ptr session, const string& entryKeyStr, const string& entryValStr ) { assert( session ); string query = messageBuilder_->genModuleOperationText( containerName_, moduleNs_, messageBuilder_->genKeyParentPathText( "theList", "theKey", entryKeyStr, messageBuilder_->genOperationText( "theVal", entryValStr, "delete" ) ) ); runFailedEditQuery( session, query, "data missing" ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::deleteEntryValuePair( std::shared_ptr session, const string& entryKeyStr, const string& entryValStr ) { EntryMap_T::iterator it; assert( session ); deleteEntryValue( session, entryKeyStr, entryValStr ); deleteEntry( session, entryKeyStr ); it=candidateEntries_->find(entryKeyStr); candidateEntries_->erase(it); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::deleteEntryValuePairFailed( std::shared_ptr session, const string& entryKeyStr, const string& entryValStr ) { assert( session ); deleteEntryValueFailed( session, entryKeyStr, entryValStr ); deleteEntryFailed( session, entryKeyStr ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::populateDatabase( const uint16_t numEntries ) { string baseKeyText( "entryKey" ); string baseValText( "entryVal" ); for ( uint16_t cnt=0; cnt < numEntries; ++cnt ) { addEntryValuePair( primarySession_, baseKeyText+boost::lexical_cast( cnt ), baseValText+boost::lexical_cast( cnt ) ); } } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::editEntryValue( std::shared_ptr session, const string& entryKeyStr, const string& entryValStr ) { assert(session); string query = messageBuilder_->genModuleOperationText( containerName_, moduleNs_, messageBuilder_->genKeyParentPathText( "theList", "theKey", entryKeyStr, messageBuilder_->genOperationText( "theVal", entryValStr, "replace" ) ) ); runEditQuery( session, query ); (*candidateEntries_)[ entryKeyStr ] = entryValStr; } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::discardChangesOperation( std::shared_ptr session ) { assert(session); vector expPresent{ "rpc-reply", "ok" }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryDiscardChanges( session, checker ); discardChanges(); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::checkEntriesImpl( std::shared_ptr session, const string& targetDbName, const SharedPtrEntryMap_T refMap, const SharedPtrEntryMap_T otherMap ) { vector expPresent{ "data" }; vector expNotPresent{ "error", "rpc-error" }; // first check the candidate database // // Add all expected elements to expPresent EntriesConvertFunctor conv( expPresent ); for_each( refMap->begin(), refMap->end(), conv ); BOOST_TEST_MESSAGE("EXPECTED PRESENT:\n"); for (unsigned int i = 0; i < expPresent.size(); i++) { BOOST_TEST_MESSAGE(expPresent.at(i) + "\n"); } // now add all elements from the running that are not in the // candidate to the expectedNotPresent list if ( useCandidate() ) { AddDifferingEntriesFunctor diff( expNotPresent, refMap ); for_each( otherMap->begin(), otherMap->end(), diff ); } BOOST_TEST_MESSAGE("EXPECTED NOT PRESENT:\n"); for (unsigned int i = 0; i < expNotPresent.size(); i++) { BOOST_TEST_MESSAGE(expNotPresent.at(i) + "\n"); } StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetConfigXpath( session, containerName_, targetDbName, checker ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::checkEntries( std::shared_ptr session) { assert( session ); if ( useCandidate() ) { checkEntriesImpl( session, writeableDbName_, candidateEntries_, runningEntries_ ); } checkEntriesImpl( session, "running", runningEntries_, candidateEntries_ ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::commitChanges( std::shared_ptr session ) { QuerySuiteFixture::commitChanges( session ); // copy the editted changes to the running changes if ( useCandidate() ) { runningEntries_->clear(); runningEntries_->insert( candidateEntries_->begin(), candidateEntries_->end() ); } } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::commitChangesFailure( std::shared_ptr session ) { QuerySuiteFixture::commitChangesFailure( session ); } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::confirmedCommitChanges( std::shared_ptr session, const int timeout, bool extend ) { QuerySuiteFixture::confirmedCommitChanges( session, timeout ); // copy the editted changes to the running changes and store the original if ( useCandidate() && !extend ) { rollbackEntries_->clear(); rollbackEntries_->insert( runningEntries_->begin(), runningEntries_->end() ); runningEntries_->clear(); runningEntries_->insert( candidateEntries_->begin(), candidateEntries_->end() ); } } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::runDeleteStartupConfig( shared_ptr session ) { if( useStartup() ) { assert( session ); vector expPresent{ "ok" }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); // send a delete-config queryEngine_->tryDeleteConfig( session, "startup", checker ); rollbackEntries_->clear(); } } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::discardChanges() { // copy the editted changes to the running changes if ( useCandidate() ) { candidateEntries_->clear(); candidateEntries_->insert( runningEntries_->begin(), runningEntries_->end() ); } } // ---------------------------------------------------------------------------| void SimpleContainerModuleFixture::rollbackChanges( shared_ptr session ) { // rollback the changes if ( useCandidate() ) { vector expPresent{ "data" }; vector expNotPresent{ "error", "rpc-error" }; // Add all expected elements to expPresent EntriesConvertFunctor conv( expPresent ); for_each( rollbackEntries_->begin(), rollbackEntries_->end(), conv ); BOOST_TEST_MESSAGE("EXPECTED PRESENT:\n"); for (unsigned int i = 0; i < expPresent.size(); i++) { BOOST_TEST_MESSAGE(expPresent.at(i) + "\n"); } // now add all elements from the running that are not in the // rollback entries to the expectedNotPresent list AddDifferingEntriesFunctor diff( expNotPresent, rollbackEntries_ ); for_each( runningEntries_->begin(), runningEntries_->end(), diff ); BOOST_TEST_MESSAGE("EXPECTED NOT PRESENT:\n"); for (unsigned int i = 0; i < expNotPresent.size(); i++) { BOOST_TEST_MESSAGE(expNotPresent.at(i) + "\n"); } StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetConfigXpath( session, containerName_, "running", checker ); runningEntries_->clear(); runningEntries_->insert( rollbackEntries_->begin(), rollbackEntries_->end() ); candidateEntries_->clear(); candidateEntries_->insert( rollbackEntries_->begin(), rollbackEntries_->end() ); } } } // namespace YumaTest yuma123_2.14/netconf/test/support/fixtures/simple-container-module-fixture.h0000664000175000017500000002600614770023131027520 0ustar vladimirvladimir#ifndef __YUMA_SIMPLE_CONTAINER_MODULE_TEST_FIXTURE__H #define __YUMA_SIMPLE_CONTAINER_MODULE_TEST_FIXTURE__H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/query-suite-fixture.h" #include "test/support/msg-util/NCMessageBuilder.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractNCSession; // ---------------------------------------------------------------------------| /** * This class is used to perform simple test case initialisation. * It can be used on a per test case basis or on a per test suite * basis. */ struct SimpleContainerModuleFixture : public QuerySuiteFixture { public: /** Convenience typedef */ typedef std::map< std::string, std::string > EntryMap_T; /** Convenience typedef */ typedef std::shared_ptr< EntryMap_T > SharedPtrEntryMap_T; public: /** * Constructor. */ SimpleContainerModuleFixture(); /** * Destructor. Shutdown the test. */ ~SimpleContainerModuleFixture(); /** * Create the top level container. * * \param session the session running the query */ void createMainContainer( std::shared_ptr session ); /** * Delete the top level container. * * \param session the session running the query */ void deleteMainContainer( std::shared_ptr session ); /** * Perform specified operation on the top level container. * * \param session the session running the query * \param op the operation to perform */ void mainContainerOp( std::shared_ptr session, const std::string& op); /** * Add an entry. * * \param session the session running the query * \param entryKeyStr the name of the entry key to add. * \param operationStr the type of addition - add, merge, replace. */ void addEntry( std::shared_ptr session, const std::string& entryKeyStr, const std::string& operationStr ); /** * Add an entry value. * * \param session the session running the query * \param entryKeyStr the name of the entry key. * \param entryValStr the value of the entry. * \param operationStr the type of addition - add, merge, replace. */ void addEntryValue( std::shared_ptr session, const std::string& entryKeyStr, const std::string& entryValStr, const std::string& operationStr ); /** * Add an entry key and value. * * \param session the session running the query. * \param entryKeyStr the name of the entry key. * \param entryValStr the value of the entry. */ void addEntryValuePair( std::shared_ptr session, const std::string& entryKeyStr, const std::string& entryValStr ); /** * Merge an entry key and value. * * \param session the session running the query. * \param entryKeyStr the name of the entry key. * \param entryValStr the value of the entry. */ void mergeEntryValuePair( std::shared_ptr session, const std::string& entryKeyStr, const std::string& entryValStr ); /** * Replace an entry key and value. * * \param session the session running the query. * \param entryKeyStr the name of the entry key. * \param entryValStr the value of the entry. */ void replaceEntryValuePair( std::shared_ptr session, const std::string& entryKeyStr, const std::string& entryValStr ); /** * No operation on an entry key and value. * * \param session the session running the query. * \param entryKeyStr the name of the entry key. * \param entryValStr the value of the entry. */ void noOpEntryValuePair( std::shared_ptr session, const std::string& entryKeyStr, const std::string& entryValStr ); /** * Delete an entry. * * \param session the session running the query * \param entryKeyStr the name of the entry key to delete. */ void deleteEntry( std::shared_ptr session, const std::string& entryKeyStr ); /** * Fail to delete an entry. * * \param session the session running the query * \param entryKeyStr the name of the entry key to delete. */ void deleteEntryFailed( std::shared_ptr session, const std::string& entryKeyStr ); /** * Delete an entry value. * * \param session the session running the query * \param entryKeyStr the name of the entry key. * \param entryValStr the value of the entry. */ void deleteEntryValue( std::shared_ptr session, const std::string& entryKeyStr, const std::string& entryValStr ); /** * Fail to delete an entry value. * * \param session the session running the query * \param entryKeyStr the name of the entry key. * \param entryValStr the value of the entry. */ void deleteEntryValueFailed( std::shared_ptr session, const std::string& entryKeyStr, const std::string& entryValStr ); /** * Delete an entry key and value. * * \param session the session running the query. * \param entryKeyStr the name of the entry key. * \param entryValStr the value of the entry. */ void deleteEntryValuePair( std::shared_ptr session, const std::string& entryKeyStr, const std::string& entryValStr ); /** * Fail to delete an entry key and value. * * \param session the session running the query. * \param entryKeyStr the name of the entry key. * \param entryValStr the value of the entry. */ void deleteEntryValuePairFailed( std::shared_ptr session, const std::string& entryKeyStr, const std::string& entryValStr ); /** * Populate the database with the entries. This function creates * numEntries elements in the database, each having a key matching * the following format "entryKey#". This function uses the * primarySession_ for all operations. * * \param numEntries the number of entries to add. */ void populateDatabase( const uint16_t numEntries ); /** * Edit an entry key and value. * * \param session the session running the query. * \param entryKeyStr the name of the entry key. * \param entryValStr the value of the entry. */ void editEntryValue( std::shared_ptr session, const std::string& entryKeyStr, const std::string& entryValStr ); /** * Remove edits from the candidate configuration by performing a * discard-changes operation. * * \param session the session running the query. */ void discardChangesOperation( std::shared_ptr session ); /** * Verify the entries in the specified database. * This function checks that all expected entries are present in * the specified database. It also checks that both databases * differ as expected. * * e.g.: * If the candidate database is being checked it makes sure that * any values in the candidate database that should be different * are not present in the running database. * * TODO: This checking is still very crude - it currently does not * support databases with multiple values that are the same. * * \param session the session running the query * \param targetDbName the name of the database to check * \param refMap the map that corresponds to the expected values * in the database being checked. * \param otherMap the map that corresponds to the entries in the * database not being checked. */ void checkEntriesImpl( std::shared_ptr session, const std::string& targetDbName, const SharedPtrEntryMap_T refMap, const SharedPtrEntryMap_T otherMap ); /** * Check the status of both databases. * * \param session the session running the query */ void checkEntries( std::shared_ptr session ); /** * Commit the changes. * * \param session the session */ virtual void commitChanges( std::shared_ptr session ); /** * Fail to commit the changes. * * \param session the session */ virtual void commitChangesFailure( std::shared_ptr session ); /** * Confirmed commit of the changes. * * \param session the session requesting the locks * \param timeout the confirm-timeout of the message in seconds * \param extend true if the operation should extend an existing timeout */ virtual void confirmedCommitChanges( std::shared_ptr session, const int timeout, const bool extend = false ); /** * Run a delete-config with startup as the target . * * \param session the session running the query. */ void runDeleteStartupConfig( std::shared_ptr session ); /** * Let the test harness know that changes should be discarded * (e.g. due to unlocking the database without a commit). */ void discardChanges(); /** * Check the running database to confirm a rollback has taken place * and update the test harness internal lists to reflect this. */ void rollbackChanges( std::shared_ptr session ); const std::string moduleNs_; ///< the module namespace const std::string containerName_; ///< the container name SharedPtrEntryMap_T runningEntries_; /// Running Db Entries SharedPtrEntryMap_T candidateEntries_; /// CandidateTarget Db Entries SharedPtrEntryMap_T rollbackEntries_; /// Db Entries reloaded by rollback }; } // namespace YumaTest #endif // __YUMA_SIMPLE_CONTAINER_MODULE_TEST_FIXTURE__H yuma123_2.14/netconf/test/support/fixtures/integ-test-fixture.inl0000664000175000017500000000755014770023131025405 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/integ-test-fixture.h" #include "test/support/fixtures/test-context.h" #include "test/support/fixtures/integ-fixture-helper-factory.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-query-util/yuma-op-policies.h" #include "test/support/nc-session/spoof-nc-session-factory.h" #include "test/support/callbacks/integ-cb-checker-factory.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| #include "agt.h" #include "ncx.h" #include "ncxmod.h" // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| // IntegrationTestFixture // ---------------------------------------------------------------------------| template IntegrationTestFixture::IntegrationTestFixture() : AbstractGlobalTestFixture( ( sizeof( Args::argv )/sizeof( Args::argv[0] ) ), Args::argv ) { DisplayTestBreak( '=', true ); BOOST_TEST_MESSAGE( "Initialising..." ); // TODO: Ensure that the Traits target and Spoofed Args Targets match configureTestContext(); fixtureHelper_ = TestContext::getTestContext()-> fixtureHelperFactory_->createHelper(); fixtureHelper_->mimicStartup(); } // ---------------------------------------------------------------------------| template IntegrationTestFixture::~IntegrationTestFixture() { fixtureHelper_->mimicShutdown(); } // ---------------------------------------------------------------------------| template void IntegrationTestFixture::configureTestContext() { // TODO: Make these strings configurable - via environment // TODO: variables / test config file / traits? using std::shared_ptr; shared_ptr< AbstractYumaOpLogPolicy > queryLogPolicy( new OpPolicy( "./yuma-op" ) ); assert( queryLogPolicy ); shared_ptr< AbstractNCSessionFactory > sessionFactory( new SpoofNCSessionFactory( queryLogPolicy ) ) ; shared_ptr< AbstractCBCheckerFactory > cbCheckerFactory( new IntegCBCheckerFactory( getTargetDbConfig()==TestContext::CONFIG_USE_CANDIDATE) ) ; shared_ptr< AbstractFixtureHelperFactory > fixtureHelperFactory( new IntegFixtureHelperFactory() ); shared_ptr< TestContext > testContext( new TestContext( true, getTargetDbConfig(), usingStartupCapability(), numArgs_, argv_, sessionFactory, cbCheckerFactory, fixtureHelperFactory ) ); assert( testContext ); TestContext::setTestContext( testContext ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/fixtures/test-context.cpp0000664000175000017500000000230614770023131024271 0ustar vladimirvladimir#include "test/support/fixtures/test-context.h" // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| // initialise static data std::shared_ptr TestContext::testContext_; // ---------------------------------------------------------------------------| TestContext::TestContext( bool isIntegTest, TargetDbConfig targetDbConfig, bool usingStartup, int argc, const char** argv, std::shared_ptr sessionFactory, std::shared_ptr cbCheckerFactory, std::shared_ptr fixtureHelperFactory ) : isIntegTest_( isIntegTest ) , targetDbConfig_( targetDbConfig ) , usingStartup_( usingStartup ) , writeableDbName_( targetDbConfig == TestContext::CONFIG_WRITEABLE_RUNNNIG ? "running" : "candidate" ) , numArgs_( argc ) , argv_( argv ) , sessionFactory_( sessionFactory ) , cbCheckerFactory_( cbCheckerFactory ) , fixtureHelperFactory_( fixtureHelperFactory ) { } } // namespace YumaTest yuma123_2.14/netconf/test/support/fixtures/system-fixture-helper-factory.cpp0000664000175000017500000000254714770023131027571 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/system-fixture-helper-factory.h" #include "test/support/fixtures/system-fixture-helper.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // File wide namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| SystemFixtureHelperFactory::SystemFixtureHelperFactory() { } // ---------------------------------------------------------------------------| SystemFixtureHelperFactory::~SystemFixtureHelperFactory() { } // ---------------------------------------------------------------------------| shared_ptr SystemFixtureHelperFactory::createHelper() { return shared_ptr ( new SystemFixtureHelper() ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/fixtures/state-get-module-fixture.h0000664000175000017500000000473414770023131026150 0ustar vladimirvladimir#ifndef __STATE_GET_MODULE_FIXTURE_H #define __STATE_GET_MODULE_FIXTURE_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/state-data-module-common-fixture.h" #include "test/support/msg-util/state-data-query-builder.h" #include "test/support/db-models/state-data-test-db.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractNCSession; class ToasterContainer; // ---------------------------------------------------------------------------| // /// @brief This class is used to perform test case initialisation for testing /// with the State Get Test module. // struct StateGetModuleFixture : public StateDataModuleCommonFixture { public: /// @brief Constructor. StateGetModuleFixture(); /// @brief Destructor. Shutdown the test. ~StateGetModuleFixture(); /// @brief Check the content of the response. /// @desc Get the contents of the response and verify /// that all entries are as expected. virtual void checkXpathFilteredState(const string& xPath) const; /// @brief Check the content of the subtree filtered response. /// @desc Get the contents of the response and verify /// that all entries are as expected. virtual void checkSubtreeFilteredState(const string& subtree) const; //protected: /// @brief Configure toaster in expected response. /// @desc This function generates the complete configuration for a /// toaster, items are configured depending on whether or /// not they have been set in the supplied ToasterContainerConfig. /// /// @param config the configuration to generate void configureToasterInFilteredState( const YumaTest::ToasterContainerConfig& config ); /// @brief Clear container of expected response. void clearFilteredState(); protected: std::shared_ptr filteredStateData_; ///< response payload }; } // namespace YumaTest #endif // __STATE_GET_MODULE_FIXTURE_H //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/fixtures/abstract-fixture-helper.h0000664000175000017500000000203014770023131026033 0ustar vladimirvladimir#ifndef __ABSTRACT_FIXTURE_HELPER_H #define __ABSTRACT_FIXTURE_HELPER_H // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Base class for Fixture Helpers. */ class AbstractFixtureHelper { public: /** * Constructor. */ AbstractFixtureHelper() { } /** * Destructor. */ virtual ~AbstractFixtureHelper() { } /** * Mimic a yuma startup if required by test harness. */ virtual void mimicStartup() = 0; /** * Mimic a yuma shutdown if required by test harness. */ virtual void mimicShutdown() = 0; /** * Mimic a yuma restart if required by test harness. */ virtual void mimicRestart() = 0; }; } // namespace YumaTest #endif // __ABSTRACT_FIXTURE_HELPER_H yuma123_2.14/netconf/test/support/fixtures/state-data-module-common-fixture.cpp0000664000175000017500000001251614770023131030120 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/state-data-module-common-fixture.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/misc-util/log-utils.h" #include "test/support/checkers/xml-content-checker.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| StateDataModuleCommonFixture::StateDataModuleCommonFixture( const string& moduleNs ) : QuerySuiteFixture( shared_ptr( new StateDataQueryBuilder( moduleNs ) ) ) , toasterBuilder_( static_pointer_cast< StateDataQueryBuilder >( messageBuilder_ ) ) , runningEntries_( new ToasterContainer ) , candidateEntries_( useCandidate() ? shared_ptr( new ToasterContainer ) : runningEntries_ ) { } // ---------------------------------------------------------------------------| StateDataModuleCommonFixture::~StateDataModuleCommonFixture() { } // ---------------------------------------------------------------------------| void StateDataModuleCommonFixture::createToasterContainer( std::shared_ptr session, const std::string& op, const string& failReason ) { string query = toasterBuilder_->genToasterQuery( "create" ); if ( failReason.empty() ) { runEditQuery( session, query ); } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| void StateDataModuleCommonFixture::deleteToasterContainer( std::shared_ptr session, const bool commitChange, const string& failReason ) { string query = toasterBuilder_->genToasterQuery( "delete" ); if ( failReason.empty() ) { runEditQuery( session, query ); if ( commitChange ) { commitChanges( session ); } } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| void StateDataModuleCommonFixture::configureToaster( const ToasterContainerConfig& config ) { if ( config.toasterManufacturer_ ) { candidateEntries_->toasterManufacturer_ = *config.toasterManufacturer_; } if ( config.toasterModelNumber_ ) { candidateEntries_->toasterModelNumber_ = *config.toasterModelNumber_; } if ( config.toasterStatus_ ) { candidateEntries_->toasterStatus_ = *config.toasterStatus_; } } // ---------------------------------------------------------------------------| void StateDataModuleCommonFixture::commitChanges( std::shared_ptr session ) { QuerySuiteFixture::commitChanges( session ); if ( useCandidate() ) { *runningEntries_ = *candidateEntries_; } } // ---------------------------------------------------------------------------| void StateDataModuleCommonFixture::discardChanges() { if ( useCandidate() ) { *candidateEntries_ = *runningEntries_; } } // ---------------------------------------------------------------------------| void StateDataModuleCommonFixture::queryState( const string& dbName ) const { vector expPresent{ "data" }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetXpath( primarySession_, "toaster", dbName, checker ); } // ---------------------------------------------------------------------------| void StateDataModuleCommonFixture::checkEntriesImpl( const string& dbName, const shared_ptr& db ) const { XMLContentChecker checker( db ); queryEngine_->tryGetXpath( primarySession_, "toaster", dbName, checker ); } // ---------------------------------------------------------------------------| void StateDataModuleCommonFixture::checkState() const { checkEntriesImpl( writeableDbName_, candidateEntries_ ); checkEntriesImpl( "running", runningEntries_ ); } } // namespace StateDataNetconfIntegrationTest //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/fixtures/query-suite-fixture.h0000664000175000017500000001561314770023131025262 0ustar vladimirvladimir#ifndef __YUMA_QUERY_SUITE_FIXTURE__H #define __YUMA_QUERY_SUITE_FIXTURE__H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/test-context.h" #include "test/support/fixtures/base-suite-fixture.h" #include "test/support/fixtures/abstract-fixture-helper.h" #include "test/support/msg-util/NCMessageBuilder.h" #include "test/support/callbacks/callback-checker.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { class YumaQueryOpLogPolicy; class NCQueryTestEngine; class NCDbScopedLock; class AbstractNCSession; class AbstractNCSessionFactory; class CallbackChecker; class AbstractCBCheckerFactory; class AbstractFixtureHelper; class AbstractFixtureHelperFactory; // ---------------------------------------------------------------------------| /** * This class is used to perform simple test case initialisation. * It can be used on a per test case basis or on a per test suite * basis. This fixture provides basic functionality for querying Yuma. */ class QuerySuiteFixture : public BaseSuiteFixture { public: /** * Constructor. */ QuerySuiteFixture(); /** * Constructor. * \param builder an NCMessageBuilder */ QuerySuiteFixture( std::shared_ptr< NCMessageBuilder> builder); /** * Destructor. Shutdown the test. */ virtual ~QuerySuiteFixture(); /** * This function is used to obtain a full lock of the system under * test. * * If the configuration is writeable running the 'startup' and * 'running' databases are locked. * * If the configuration is candidate the 'startup', 'running' and * 'candidate' databases are locked. * * \param session the session requesting the locks * \param useStartup true to get startup lock and false otherwise * \return a vector of RAII locks. */ std::vector< std::unique_ptr< NCDbScopedLock > > getFullLock( std::shared_ptr session ); /** * Commit the changes. * If the configuration is CONFIG_USE_CANDIDATE a commit message * is sent to Yuma. * * \param session the session requesting the locks */ virtual void commitChanges( std::shared_ptr session ); /** * Commit the changes expecting the commit to fail. * If the configuration is CONFIG_USE_CANDIDATE a commit message * is sent to Yuma. * * \param session the session requesting the locks */ virtual void commitChangesFailure( std::shared_ptr session ); /** * Confirmed commit of the changes. * If the configuration is CONFIG_USE_CANDIDATE a confirmed-commit message * is sent to Yuma. * * \param session the session requesting the locks * \param timeout the confirm-timeout of the message in seconds */ virtual void confirmedCommitChanges( std::shared_ptr session, const int timeout ); /** * Run an edit query. * * \param session the session running the query * \param query the query to run */ virtual void runEditQuery( std::shared_ptr session, const std::string& query ); /** * Run an edit query. * * \param session the session running the query. * \param query the query to run. * \param failReson the expected fail reason. */ virtual void runFailedEditQuery( std::shared_ptr session, const std::string& query, const std::string& failReason ); /** * Run a validate command. * * \param session the session running the query */ void runValidateCommand( std::shared_ptr session ); /** * Run a get-my-session query. * * \param session the session running the query. * \param expIndent the expected indent. * \param expLinesize the expected linesize. * \param expWithDefaults the expected with-defaults setting. */ void runGetMySession( std::shared_ptr session, const std::string& expIndent, const std::string& expLinesize, const std::string& expWithDefaults ); /** * Run a set-my-session query. * * \param session the session running the query. * \param indent the indent to be set. * \param linesize the linesize to be set. * \param withDefaults the with-defaults setting to be set. */ void runSetMySession( std::shared_ptr session, const std::string& indent, const std::string& linesize, const std::string& withDefaults ); /** * Run a shutdown query. * * \param session the session running the query. */ void runShutdown( std::shared_ptr session ); /** * Run a restart query. * * \param session the session running the query. * \param query the query to run. */ void runRestart( std::shared_ptr session ); /** * Run a query when there is expected to be no response. * * \param session the session running the query. */ void runNoSession( std::shared_ptr session ); /** the session factory. */ std::shared_ptr sessionFactory_; /** the cb checker factory. */ std::shared_ptr cbCheckerFactory_; /** the fixture helper factory. */ std::shared_ptr fixtureHelperFactory_; /** Each test always has one session */ std::shared_ptr primarySession_; /** Each test always has one cb checker */ std::shared_ptr cbChecker_; /** Each test always has one fixture helper */ std::shared_ptr fixtureHelper_; /** Utility XML message builder */ std::shared_ptr messageBuilder_; /** The Query Engine */ std::shared_ptr queryEngine_; }; } // namespace YumaTest // ---------------------------------------------------------------------------| #endif // __YUMA_QUERY_SUITE_FIXTURE__H yuma123_2.14/netconf/test/support/fixtures/state-data-module-common-fixture.h0000664000175000017500000001062414770023131027563 0ustar vladimirvladimir#ifndef __STATE_MODULE_COMMON_FIXTURE_H #define __STATE_MODULE_COMMON_FIXTURE_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/query-suite-fixture.h" #include "test/support/msg-util/state-data-query-builder.h" #include "test/support/db-models/state-data-test-db.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractNCSession; class ToasterContainer; // ---------------------------------------------------------------------------| // /// @brief This class is used to perform test case initialisation for testing /// with the State Data Test module. /// @desc This class should be used as a base class for test suites /// fuixtures that need to use common StateDataModule access code. // struct StateDataModuleCommonFixture : public YumaTest::QuerySuiteFixture { public: /// @brief Constructor. explicit StateDataModuleCommonFixture( const std::string& moduleNs ); /// @brief Destructor. Shutdown the test. ~StateDataModuleCommonFixture(); /// @brief Create the toaster level container. /// /// @param session the session running the query /// @param op the operation type /// @param failReason optional expected failure reason. void createToasterContainer( std::shared_ptr session, const std::string& op = std::string( "create" ), const std::string& failReason = std::string() ); /// @brief Delete the toaster level container. /// /// @param session the session running the query /// @param commitChange flag indicating if the change should be committed. /// @param failReason optional expected failure reason. void deleteToasterContainer( std::shared_ptr session, bool commitChange = true, const std::string& failReason = std::string() ); /// @brief Configure toaster content in db. /// @desc This function generates the complete configuration for a /// toaster, items are configured depending on whether or /// not they have been set in the supplied ToasterContainerConfig. /// /// @param config the configuration to generate void configureToaster( const YumaTest::ToasterContainerConfig& config ); /// @brief Commit the changes. /// /// @param session the session requesting the locks virtual void commitChanges( std::shared_ptr session ); /// @brief Discard all changes locally. /// @desc Let the test harness know that changes should be discarded /// (e.g. due to unlocking the database without a commit. void discardChanges(); /// @brief Check the state data. /// @desc Get the contents of the candidate database and verify /// that all entries are as expected. virtual void checkState() const; protected: /// @brief Utility fuction for checking the db state content /// /// @param dbName the name of the netconf database to check /// @param db the expected contents. virtual void checkEntriesImpl( const std::string& dbName, const std::shared_ptr& db ) const; /// @brief Utility function to query state data. /// @desc this function can be used for debugging purposes. It /// queries the specified data and as a result the state data /// xml will be dumped into the test output file SesXXXX. /// /// @param dbName the name of the netconf database to check void queryState( const string& dbName ) const; protected: ///< The builder for the generating netconf messages in xml format. std::shared_ptr< YumaTest::StateDataQueryBuilder > toasterBuilder_; std::shared_ptr runningEntries_; ///< the running data std::shared_ptr candidateEntries_; ///< the candidate data }; } // namespace YumaTest #endif // __STATE_MODULE_COMMON_FIXTURE_H //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/fixtures/query-suite-fixture.cpp0000664000175000017500000002113714770023131025613 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/query-suite-fixture.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/callbacks/abstract-cb-checker-factory.h" #include "test/support/fixtures/abstract-fixture-helper-factory.h" #include "test/support/misc-util/log-utils.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| QuerySuiteFixture::QuerySuiteFixture() : BaseSuiteFixture() , sessionFactory_( testContext_->sessionFactory_ ) , cbCheckerFactory_( testContext_->cbCheckerFactory_ ) , fixtureHelperFactory_( testContext_->fixtureHelperFactory_ ) , primarySession_( sessionFactory_->createSession() ) , cbChecker_( cbCheckerFactory_->createChecker() ) , fixtureHelper_( fixtureHelperFactory_->createHelper() ) , messageBuilder_( new NCMessageBuilder() ) , queryEngine_( new NCQueryTestEngine( messageBuilder_ ) ) { assert( queryEngine_ ); } // ---------------------------------------------------------------------------| QuerySuiteFixture::QuerySuiteFixture( shared_ptr builder ) : BaseSuiteFixture() , sessionFactory_( testContext_->sessionFactory_ ) , cbCheckerFactory_( testContext_->cbCheckerFactory_ ) , primarySession_( sessionFactory_->createSession() ) , cbChecker_( cbCheckerFactory_->createChecker() ) , messageBuilder_( builder ) , queryEngine_( new NCQueryTestEngine( messageBuilder_ ) ) { assert( queryEngine_ ); } // ---------------------------------------------------------------------------| QuerySuiteFixture::~QuerySuiteFixture() { } // ---------------------------------------------------------------------------| std::vector< std::unique_ptr< NCDbScopedLock > > QuerySuiteFixture::getFullLock( std::shared_ptr session ) { vector< std::unique_ptr< NCDbScopedLock > >locks; locks.push_back( std::unique_ptr< NCDbScopedLock >( new NCDbScopedLock( queryEngine_, session, "running" ) ) ); if( useCandidate() ) { locks.push_back( std::unique_ptr< NCDbScopedLock >( new NCDbScopedLock( queryEngine_, session, "candidate" ) ) ); } if( useStartup() ) { locks.push_back( std::unique_ptr< NCDbScopedLock >( new NCDbScopedLock( queryEngine_, session, "startup" ) ) ); } return locks; } // ---------------------------------------------------------------------------| void QuerySuiteFixture::commitChanges( std::shared_ptr session ) { if( useCandidate() ) { // send a commit queryEngine_->commit( session ); } if( useStartup() ) { assert( session ); vector expPresent{ "ok" }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); // send a copy-config queryEngine_->tryCopyConfig( session, "startup", "running", checker ); } } // ---------------------------------------------------------------------------| void QuerySuiteFixture::commitChangesFailure( std::shared_ptr session ) { if( useCandidate() ) { // send a commit queryEngine_->commitFailure( session ); } } // ---------------------------------------------------------------------------| void QuerySuiteFixture::confirmedCommitChanges( std::shared_ptr session, const int timeout ) { if( useCandidate() ) { // send a confirmed-commit queryEngine_->confirmedCommit( session, timeout ); } } // ---------------------------------------------------------------------------| void QuerySuiteFixture::runEditQuery( shared_ptr session, const string& query ) { assert( session ); vector expPresent{ "ok" }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryEditConfig( session, query, writeableDbName_, checker ); } // ---------------------------------------------------------------------------| void QuerySuiteFixture::runFailedEditQuery( shared_ptr session, const string& query, const string& failReason ) { assert( session ); vector expNotPresent{ "ok" }; vector expPresent{ "error", "rpc-error", failReason }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryEditConfig( session, query, writeableDbName_, checker ); } // ---------------------------------------------------------------------------| void QuerySuiteFixture::runValidateCommand( shared_ptr session ) { assert( session ); vector expPresent{ "ok" }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryValidateDatabase( session, writeableDbName_, checker ); } // ---------------------------------------------------------------------------| void QuerySuiteFixture::runGetMySession( shared_ptr session, const string& expIndent, const string& expLinesize, const string& expWithDefaults ) { assert(session); vector expPresent{ "rpc-reply", expIndent, expLinesize, expWithDefaults }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetMySession( session, checker ); } // ---------------------------------------------------------------------------| void QuerySuiteFixture::runSetMySession( shared_ptr session, const string& indent, const string& linesize, const string& withDefaults ) { assert(session); vector expPresent{ "rpc-reply", "ok"}; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->trySetMySession( session, indent, linesize, withDefaults, checker ); } // ---------------------------------------------------------------------------| void QuerySuiteFixture::runShutdown( shared_ptr session ) { assert(session); vector expPresent{ "rpc-reply", "ok" }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryShutdown( session, checker ); } // ---------------------------------------------------------------------------| void QuerySuiteFixture::runRestart( shared_ptr session ) { assert(session); vector expPresent{ "rpc-reply", "ok" }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryRestart( session, checker ); fixtureHelper_->mimicRestart(); } // ---------------------------------------------------------------------------| void QuerySuiteFixture::runNoSession( shared_ptr session ) { assert(session); //If no response expect to check against sent message" vector expPresent{ "rpc", "get-my-session" }; vector expNotPresent{ "rpc-reply", "rpc-error", "ok" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetMySession( session, checker ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/fixtures/integ-fixture-helper-factory.cpp0000664000175000017500000000253714770023131027352 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/integ-fixture-helper-factory.h" #include "test/support/fixtures/integ-fixture-helper.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // File wide namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| IntegFixtureHelperFactory::IntegFixtureHelperFactory() { } // ---------------------------------------------------------------------------| IntegFixtureHelperFactory::~IntegFixtureHelperFactory() { } // ---------------------------------------------------------------------------| shared_ptr IntegFixtureHelperFactory::createHelper() { return shared_ptr ( new IntegFixtureHelper() ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/fixtures/integ-fixture-helper.cpp0000664000175000017500000001054314770023131025701 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/test-context.h" #include "test/support/fixtures/integ-fixture-helper.h" #include "test/support/callbacks/sil-callback-controller.h" // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| #include "agt.h" #include "ncx.h" #include "ncxmod.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| void IntegFixtureHelper::mimicStartup() { int numArgs = TestContext::getTestContext()->numArgs_; const char** argv = TestContext::getTestContext()->argv_; initialiseNCXEngine(numArgs, argv); loadBaseSchemas(); stage1AgtInitialisation(numArgs, argv); loadCoreSchema(); stage2AgtInitialisation(); // Make sure callback controller still exists following restart YumaTest::SILCallbackController& cbCon = YumaTest::SILCallbackController::getInstance(); cbCon.reset(); } // ---------------------------------------------------------------------------| void IntegFixtureHelper::mimicShutdown() { BOOST_TEST_MESSAGE( "QuerySuiteFixture() Cleaning Up..." ); agt_cleanup(); ncx_cleanup(); } // ---------------------------------------------------------------------------| void IntegFixtureHelper::mimicRestart() { mimicShutdown(); mimicStartup(); } // ---------------------------------------------------------------------------| void IntegFixtureHelper::initialiseNCXEngine(int numArgs, const char** argv) { BOOST_TEST_MESSAGE( "Initialising the NCX Engine..." ); assert( "NCX Engine Failed to initialise" && NO_ERR == ncx_init( FALSE, LOG_DEBUG_INFO, FALSE, 0, numArgs, const_cast( argv ) ) ); } // ---------------------------------------------------------------------------| void IntegFixtureHelper::loadBaseSchemas() { BOOST_TEST_MESSAGE( "Loading base schema..." ); assert( "ncxmod_load_module( NCXMOD_YUMA_NETCONF ) failed!" && NO_ERR == ncxmod_load_module( NCXMOD_YUMA_NETCONF, NULL, NULL, NULL ) ); assert( "ncx_modload_module( NCXMOD_NETCONFD ) failed!" && NO_ERR == ncxmod_load_module( NCXMOD_NETCONFD, NULL, NULL, NULL ) ); } // ---------------------------------------------------------------------------| void IntegFixtureHelper::loadCoreSchema() { BOOST_TEST_MESSAGE( "Loading core schemas..." ); agt_profile_t* profile = agt_get_profile(); assert ( "agt_get_profile() returned a null profile" && profile ); assert( "ncxmod_load_module( NCXMOD_WITH_DEFAULTS ) failed!" && NO_ERR == ncxmod_load_module( NCXMOD_YUMA_NETCONF, NULL, &profile->agt_savedevQ, NULL ) ); assert( "ncx_modload_module( NCXMOD_NETCONFD failed!" && NO_ERR == ncxmod_load_module( NCXMOD_NETCONFD, NULL, NULL, NULL ) ); } // ---------------------------------------------------------------------------| void IntegFixtureHelper::stage1AgtInitialisation(int numArgs, const char** argv) { BOOST_TEST_MESSAGE( "AGT Initialisation stage 1..." ); boolean showver = FALSE; help_mode_t showhelpmode = HELP_MODE_NONE; assert( "agt_init1() failed!" && NO_ERR == agt_init1( numArgs, const_cast( argv ), &showver, &showhelpmode ) ); } // ---------------------------------------------------------------------------| void IntegFixtureHelper::stage2AgtInitialisation() { BOOST_TEST_MESSAGE( "AGT Initialisation stage 2..." ); assert( "agt_init2() failed!" && NO_ERR == agt_init2() ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/fixtures/base-suite-fixture.cpp0000664000175000017500000000225414770023131025357 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/base-suite-fixture.h" // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/misc-util/log-utils.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| BaseSuiteFixture::BaseSuiteFixture() : testContext_( TestContext::getTestContext() ) , writeableDbName_( testContext_->writeableDbName_ ) { } // ---------------------------------------------------------------------------| BaseSuiteFixture::~BaseSuiteFixture() { DisplayCurrentTestResult(); } } // namespace YumaTest yuma123_2.14/netconf/test/support/fixtures/device-get-module-fixture.cpp0000664000175000017500000002162514770023131026620 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/device-get-module-fixture.h" #include "test/support/misc-util/base64.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/misc-util/log-utils.h" #include "test/support/checkers/xml-content-checker.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| DeviceGetModuleFixture::DeviceGetModuleFixture() : DeviceModuleCommonFixture( "http://netconfcentral.org/ns/device_test" ) , filteredConfig_( new XPO3Container ) { // ensure the module is loaded queryEngine_->loadModule( primarySession_, "device_test" ); } // ---------------------------------------------------------------------------| DeviceGetModuleFixture::~DeviceGetModuleFixture() { } // ---------------------------------------------------------------------------| void DeviceGetModuleFixture::ensureActiveProfileInFilteredConfig( const uint16_t profileId ) { filteredConfig_->activeProfile_ = profileId; } // ---------------------------------------------------------------------------| void DeviceGetModuleFixture::ensureProfileInFilteredConfig( const uint16_t profileId ) { if ( filteredConfig_->profiles_.end() == filteredConfig_->profiles_.find( profileId ) ) { Profile newProf; newProf.id_ = profileId; filteredConfig_->profiles_.insert( make_pair( profileId, newProf ) ); } } // ---------------------------------------------------------------------------| void DeviceGetModuleFixture::ensureStreamConnectionInFilteredConfig( const uint16_t profileId, const uint16_t connectionId ) { ensureProfileInFilteredConfig( profileId ); if ( filteredConfig_->profiles_[ profileId ].streamConnections_.end() == filteredConfig_->profiles_[ profileId ].streamConnections_.find( connectionId ) ) { StreamConnectionItem conn; conn.id_ = connectionId; filteredConfig_->profiles_[ profileId ].streamConnections_.insert( make_pair( connectionId, conn ) ); } } // ---------------------------------------------------------------------------| void DeviceGetModuleFixture::ensureProfileStreamInFilteredConfig( const uint16_t profileId, const uint16_t streamId ) { ensureProfileInFilteredConfig( profileId ); if ( filteredConfig_->profiles_[ profileId ].streams_.end() == filteredConfig_->profiles_[ profileId ].streams_.find( streamId ) ) { StreamItem newStream; newStream.id_ = streamId; filteredConfig_->profiles_[ profileId ].streams_.insert( make_pair( streamId, newStream ) ); } } // ---------------------------------------------------------------------------| void DeviceGetModuleFixture::ensureResourceDescriptionInFilteredConfig( const uint16_t profileId, const uint16_t streamId, const uint16_t resourceId ) { ensureProfileStreamInFilteredConfig( profileId, streamId ); if ( filteredConfig_->profiles_[ profileId ] .streams_[streamId].resourceDescription_.end() == filteredConfig_->profiles_[ profileId ] .streams_[streamId].resourceDescription_.find( resourceId ) ) { ResourceNode res; res.id_ = resourceId; filteredConfig_->profiles_[ profileId ].streams_[ streamId ] .resourceDescription_.insert( make_pair( resourceId, res ) ); } } // ---------------------------------------------------------------------------| void DeviceGetModuleFixture::ensureResourceConnectionInFilteredConfig( const uint16_t profileId, const uint16_t streamId, const uint16_t connectionId ) { ensureProfileStreamInFilteredConfig( profileId, streamId ); if ( filteredConfig_->profiles_[ profileId ] .streams_[streamId].resourceConnections_.end() == filteredConfig_->profiles_[ profileId ] .streams_[streamId].resourceConnections_.find( connectionId ) ) { ConnectionItem conn; conn.id_ = connectionId; filteredConfig_->profiles_[ profileId ].streams_[ streamId ] .resourceConnections_.insert( make_pair( connectionId, conn ) ); } } // ---------------------------------------------------------------------------| void DeviceGetModuleFixture::configureResourceDescrptionInFilteredConfig( const uint16_t profileId, const uint16_t streamId, const uint16_t resourceId, const ResourceNodeConfig& config ) { ensureResourceDescriptionInFilteredConfig( profileId, streamId, resourceId ); if ( config.resourceType_ ) { filteredConfig_->profiles_[ profileId ].streams_[ streamId ] .resourceDescription_[ resourceId ].resourceType_ = *config.resourceType_; } if ( config.configuration_ ) { filteredConfig_->profiles_[ profileId ].streams_[ streamId ] .resourceDescription_[ resourceId ].configuration_ = *config.configuration_; } if ( config.statusConfig_ ) { filteredConfig_->profiles_[ profileId ].streams_[ streamId ] .resourceDescription_[ resourceId ].statusConfig_ = *config.statusConfig_; } if ( config.alarmConfig_ ) { filteredConfig_->profiles_[ profileId ].streams_[ streamId ] .resourceDescription_[ resourceId ].alarmConfig_ = *config.alarmConfig_; } if ( config.physicalPath_ ) { filteredConfig_->profiles_[ profileId ].streams_[ streamId ] .resourceDescription_[ resourceId ].physicalPath_ = *config.physicalPath_; } } // ---------------------------------------------------------------------------| void DeviceGetModuleFixture::configureResourceConnectionInFilteredConfig( const uint16_t profileId, const uint16_t streamId, const uint16_t connectionId, const ConnectionItemConfig& config ) { ensureResourceConnectionInFilteredConfig( profileId, streamId, connectionId ); storeConnectionItemConfig( filteredConfig_->profiles_[ profileId ] .streams_[ streamId ].resourceConnections_[connectionId] , config ); } // ---------------------------------------------------------------------------| void DeviceGetModuleFixture::configureStreamConnectionInFilteredConfig( const uint16_t profileId, const uint16_t connectionId, const StreamConnectionItemConfig& config ) { ensureStreamConnectionInFilteredConfig( profileId, connectionId ); StreamConnectionItem& connItem = filteredConfig_->profiles_[ profileId ].streamConnections_[connectionId]; storeConnectionItemConfig( connItem, config ); if ( config.sourceStreamId_ ) { connItem.sourceStreamId_ = *config.sourceStreamId_; } if ( config.destinationStreamId_ ) { connItem.destinationStreamId_ = *config.destinationStreamId_; } } // ---------------------------------------------------------------------------| void DeviceGetModuleFixture::clearFilteredConfig() { filteredConfig_->clear(); } // ---------------------------------------------------------------------------| void DeviceGetModuleFixture::checkXpathFilteredConfig( const string& xPath) const { XMLContentChecker checker( filteredConfig_ ); queryEngine_->tryGetConfigXpath( primarySession_, xPath, writeableDbName_, checker ); } // ---------------------------------------------------------------------------| void DeviceGetModuleFixture::checkSubtreeFilteredConfig( const string& subtree) const { XMLContentChecker checker( filteredConfig_ ); queryEngine_->tryGetConfigSubtree( primarySession_, subtree, writeableDbName_, checker ); } } // namespace XPO3NetconfIntegrationTest //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/fixtures/state-get-module-fixture.cpp0000664000175000017500000000700114770023131026471 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/state-get-module-fixture.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/misc-util/log-utils.h" #include "test/support/checkers/xml-content-checker.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| StateGetModuleFixture::StateGetModuleFixture() : StateDataModuleCommonFixture( "http://netconfcentral.org/ns/toaster" ) , filteredStateData_( new ToasterContainer ) { // ensure the module is loaded queryEngine_->loadModule( primarySession_, "toaster" ); } // ---------------------------------------------------------------------------| StateGetModuleFixture::~StateGetModuleFixture() { } // ---------------------------------------------------------------------------| void StateGetModuleFixture::configureToasterInFilteredState( const ToasterContainerConfig& config ) { if ( config.toasterManufacturer_ ) { filteredStateData_->toasterManufacturer_ = *config.toasterManufacturer_; } if ( config.toasterModelNumber_ ) { filteredStateData_->toasterModelNumber_ = *config.toasterModelNumber_; } if ( config.toasterStatus_ ) { filteredStateData_->toasterStatus_ = *config.toasterStatus_; } } // ---------------------------------------------------------------------------| void StateGetModuleFixture::clearFilteredState() { filteredStateData_->toasterManufacturer_.clear(); filteredStateData_->toasterModelNumber_.clear(); filteredStateData_->toasterStatus_.clear(); } // ---------------------------------------------------------------------------| void StateGetModuleFixture::checkXpathFilteredState(const string& xPath) const { XMLContentChecker checker( filteredStateData_ ); queryEngine_->tryGetXpath( primarySession_, xPath, writeableDbName_, checker ); } // ---------------------------------------------------------------------------| void StateGetModuleFixture::checkSubtreeFilteredState( const string& subtree) const { XMLContentChecker checker( filteredStateData_ ); queryEngine_->tryGetSubtree( primarySession_, subtree, writeableDbName_, checker ); } } // namespace StateDataNetconfIntegrationTest //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/fixtures/integ-fixture-helper-factory.h0000664000175000017500000000244014770023131027010 0ustar vladimirvladimir#ifndef __YUMA_INTEG_FIXTURE_HELPER_FACTORY_H #define __YUMA_INTEG_FIXTURE_HELPER_FACTORY_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/abstract-fixture-helper-factory.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractFixtureHelper; // ---------------------------------------------------------------------------| /** * A factory for creating Integration test Fixture Helpers. */ class IntegFixtureHelperFactory : public AbstractFixtureHelperFactory { public: /** * Constructor. */ IntegFixtureHelperFactory(); /** Destructor */ virtual ~IntegFixtureHelperFactory(); /** * Create a new Fixture Helper. * * \return a new Fixture Helper. */ std::shared_ptr createHelper(); }; } // namespace YumaTest #endif // __YUMA_INTEG_FIXTURE_HELPER_FACTORY_H yuma123_2.14/netconf/test/support/fixtures/device-module-common-fixture.cpp0000664000175000017500000006357114770023131027337 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/device-module-common-fixture.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/misc-util/log-utils.h" #include "test/support/checkers/xml-content-checker.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| DeviceModuleCommonFixture::DeviceModuleCommonFixture( const string& moduleNs ) : QuerySuiteFixture( shared_ptr( new XPOQueryBuilder( moduleNs ) ) ) , xpoBuilder_( static_pointer_cast< XPOQueryBuilder >( messageBuilder_ ) ) , runningEntries_( new XPO3Container ) , candidateEntries_( useCandidate() ? shared_ptr( new XPO3Container ) : runningEntries_ ) { } // ---------------------------------------------------------------------------| DeviceModuleCommonFixture::~DeviceModuleCommonFixture() { } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::ensureProfileExists( const uint16_t profileId ) { if ( candidateEntries_->profiles_.end() == candidateEntries_->profiles_.find( profileId ) ) { Profile newProf; newProf.id_ = profileId; candidateEntries_->profiles_.insert( make_pair( profileId, newProf ) ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::ensureStreamConnectionExists( const uint16_t profileId, const uint16_t connectionId ) { ensureProfileExists( profileId ); if ( candidateEntries_->profiles_[ profileId ].streamConnections_.end() == candidateEntries_->profiles_[ profileId ].streamConnections_.find( connectionId ) ) { StreamConnectionItem conn; conn.id_ = connectionId; candidateEntries_->profiles_[ profileId ].streamConnections_.insert( make_pair( connectionId, conn ) ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::ensureProfileStreamExists( const uint16_t profileId, const uint16_t streamId ) { ensureProfileExists( profileId ); if ( candidateEntries_->profiles_[ profileId ].streams_.end() == candidateEntries_->profiles_[ profileId ].streams_.find( streamId ) ) { StreamItem newStream; newStream.id_ = streamId; candidateEntries_->profiles_[ profileId ].streams_.insert( make_pair( streamId, newStream ) ); } } //------------------------------------------------------------------------------ void DeviceModuleCommonFixture::ensureResourceExists( ResourceDescriptionMap& resourceMap, const uint32_t resourceId ) { if ( resourceMap.find( resourceId ) == resourceMap.end() ) { ResourceNode res; res.id_ = resourceId; resourceMap.insert( make_pair( resourceId, res ) ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::ensureResourceDescriptionExists( const uint16_t profileId, const uint16_t streamId, const uint16_t resourceId ) { ensureProfileStreamExists( profileId, streamId ); ensureResourceExists( candidateEntries_->profiles_[ profileId ]. streams_[ streamId ].resourceDescription_, resourceId ); } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::ensureResourceConnectionExists( const uint16_t profileId, const uint16_t streamId, const uint16_t connectionId ) { ensureProfileStreamExists( profileId, streamId ); if ( candidateEntries_->profiles_[ profileId ] .streams_[streamId].resourceConnections_.end() == candidateEntries_->profiles_[ profileId ] .streams_[streamId].resourceConnections_.find( connectionId ) ) { ConnectionItem conn; conn.id_ = connectionId; candidateEntries_->profiles_[ profileId ].streams_[ streamId ] .resourceConnections_.insert( make_pair( connectionId, conn ) ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::createXpoContainer( std::shared_ptr session, const string& failReason ) { string query = xpoBuilder_->genXPOQuery( "create" ); if ( failReason.empty() ) { runEditQuery( session, query ); } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::deleteXpoContainer( std::shared_ptr session, const bool commitChange, const string& failReason ) { string query = xpoBuilder_->genXPOQuery( "delete" ); if ( failReason.empty() ) { runEditQuery( session, query ); candidateEntries_->clear(); if ( commitChange ) { commitChanges( session ); } } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::xpoProfileQuery( std::shared_ptr session, const uint16_t profileId, const std::string& op, const string& failReason ) { string query = xpoBuilder_->genProfileQuery( profileId, op ); if ( failReason.empty() ) { runEditQuery( session, query ); if ( op == "delete" ) { candidateEntries_->profiles_.erase( profileId ); } else if ( op == "replace" ) { candidateEntries_->profiles_.erase( profileId ); ensureProfileExists( profileId ); } else { ensureProfileExists( profileId ); } } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::setActiveProfile( std::shared_ptr session, const uint16_t profileId, const string& failReason ) { string query = xpoBuilder_->genSetActiveProfileIdQuery( profileId, "replace" ); if ( failReason.empty() ) { runEditQuery( session, query ); // @TODO: add check for failure if the selected profile does not // @TODO: exist, when this is supported by the real database. candidateEntries_->activeProfile_ = profileId; } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::profileStreamQuery( std::shared_ptr session, const uint16_t profileId, const uint16_t streamId, const std::string& op, const string& failReason ) { string query = xpoBuilder_->genProfileStreamItemQuery( profileId, streamId, op ); if ( failReason.empty() ) { runEditQuery( session, query ); if ( op == "delete" ) { // remove the stream. candidateEntries_->profiles_[ profileId ].streams_.erase( streamId ); } else if ( op == "replace" ) { candidateEntries_->profiles_[ profileId ].streams_.erase( streamId ); ensureProfileStreamExists( profileId, streamId ); } else { ensureProfileStreamExists( profileId, streamId ); } } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::resourceDescriptionQuery( std::shared_ptr session, const uint16_t profileId, const uint16_t streamId, const uint16_t resourceNodeId, const std::string& op, const string& failReason ) { string query = xpoBuilder_->genProfileChildQuery( profileId, streamId, "resourceNode", resourceNodeId, op ); if ( failReason.empty() ) { runEditQuery( session, query ); if ( op == "delete" ) { candidateEntries_->profiles_[ profileId ].streams_[ streamId ] .resourceDescription_.erase( resourceNodeId ); } else { ensureResourceDescriptionExists( profileId, streamId, resourceNodeId ); } } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::resourceConnectionQuery( std::shared_ptr session, const uint16_t profileId, const uint16_t streamId, const uint16_t connectionId, const std::string& op, const string& failReason ) { string query = xpoBuilder_->genProfileChildQuery( profileId, streamId, "resourceConnection", connectionId, op ); if ( failReason.empty() ) { runEditQuery( session, query ); if ( op == "delete" ) { candidateEntries_->profiles_[ profileId ].streams_[ streamId ] .resourceConnections_.erase( connectionId ); } else { ensureResourceConnectionExists( profileId, streamId, connectionId ); } } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::updateResourceDescriptionEntry( ResourceNode& resourceNode, const ResourceNodeConfig& config ) { if ( config.resourceType_ ) { resourceNode.resourceType_ = *config.resourceType_; } if ( config.configuration_ ) { resourceNode.configuration_ = *config.configuration_; } if ( config.statusConfig_ ) { resourceNode.statusConfig_ = *config.statusConfig_; } if ( config.alarmConfig_ ) { resourceNode.alarmConfig_ = *config.alarmConfig_; } if ( config.physicalPath_ ) { resourceNode.physicalPath_ = *config.physicalPath_; } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::configureResourceDescrption( std::shared_ptr session, const uint16_t profileId, const uint16_t streamId, const uint16_t resourceId, const ResourceNodeConfig& config, const std::string& op, const string& failReason ) { string query = xpoBuilder_->configureVResourceNode( config, "" ); query = xpoBuilder_->addResourceNodePath( profileId, streamId, resourceId, query, op ); if ( failReason.empty() ) { runEditQuery( session, query ); if ( op == "delete" || op == "replace" ) { if ( candidateEntries_->profiles_.find( profileId ) != candidateEntries_->profiles_.end() && candidateEntries_->profiles_[ profileId ].streams_.find( streamId ) != candidateEntries_->profiles_[ profileId ].streams_.end() ) { candidateEntries_->profiles_[ profileId ].streams_[ streamId ] .resourceDescription_.erase( resourceId ); } if ( op == "delete" ) { return; } } ensureResourceDescriptionExists( profileId, streamId, resourceId ); updateResourceDescriptionEntry( candidateEntries_->profiles_[profileId].streams_[streamId].resourceDescription_[resourceId], config ); } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| template< class ConnectionItemType, class ConfigType > void DeviceModuleCommonFixture::storeConnectionItemConfig( ConnectionItemType& connectionItem, const ConfigType& config ) { if ( config.sourceId_ ) { connectionItem.sourceId_ = *config.sourceId_; } if ( config.sourcePinId_ ) { connectionItem.sourcePinId_ = *config.sourcePinId_; } if ( config.destinationId_ ) { connectionItem.destinationId_ = *config.destinationId_; } if ( config.destinationPinId_ ) { connectionItem.destinationPinId_ = *config.destinationPinId_; } if ( config.bitrate_ ) { connectionItem.bitrate_ = *config.bitrate_; } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::configureResourceConnection( std::shared_ptr session, const uint16_t profileId, const uint16_t streamId, const uint16_t connectionId, const ConnectionItemConfig& config, const std::string& op, const string& failReason ) { string query = xpoBuilder_->configureResourceConnection( config, "" ); query = xpoBuilder_->addVRConnectionNodePath( profileId, streamId, connectionId, query, op ); if ( failReason.empty() ) { runEditQuery( session, query ); if ( op == "delete" || op == "replace" ) { if ( candidateEntries_->profiles_.find( profileId ) != candidateEntries_->profiles_.end() && candidateEntries_->profiles_[ profileId ].streams_.find( streamId ) != candidateEntries_->profiles_[ profileId ].streams_.end() ) { candidateEntries_->profiles_[ profileId ].streams_[ streamId ] .resourceConnections_.erase( connectionId ); } if ( op == "delete" ) { return; } } ensureResourceConnectionExists( profileId, streamId, connectionId ); storeConnectionItemConfig( candidateEntries_->profiles_[ profileId ] .streams_[ streamId ].resourceConnections_[connectionId] , config ); } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::configureStreamConnection( std::shared_ptr session, const uint16_t profileId, const uint16_t connectionId, const StreamConnectionItemConfig& config, const std::string& op, const string& failReason ) { string query = xpoBuilder_->configureStreamConnection( config, "" ); query = xpoBuilder_->addStreamConnectionPath( profileId, connectionId, query, op ); if ( failReason.empty() ) { runEditQuery( session, query ); ensureStreamConnectionExists( profileId, connectionId ); StreamConnectionItem& connItem = candidateEntries_->profiles_[ profileId ].streamConnections_[connectionId]; storeConnectionItemConfig( connItem, config ); if ( config.sourceStreamId_ ) { connItem.sourceStreamId_ = *config.sourceStreamId_; } if ( config.destinationStreamId_ ) { connItem.destinationStreamId_ = *config.destinationStreamId_; } } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::streamConnectionQuery( std::shared_ptr session, const uint16_t profileId, const uint16_t streamConnId, const std::string& op, const string& failReason ) { string query = xpoBuilder_->genStreamConnectionQuery( profileId, streamConnId, op ); if ( failReason.empty() ) { runEditQuery( session, query ); if ( op == "delete" ) { candidateEntries_->profiles_[ profileId ] .streamConnections_.erase( streamConnId ); } else { ensureStreamConnectionExists( profileId, streamConnId ); } } else { runFailedEditQuery( session, query, failReason ); } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::commitChanges( std::shared_ptr session ) { QuerySuiteFixture::commitChanges( session ); if ( useCandidate() ) { *runningEntries_ = *candidateEntries_; } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::discardChanges() { if ( useCandidate() ) { *candidateEntries_ = *runningEntries_; } } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::queryConfig( const string& dbName ) const { vector expPresent{ "data" }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetConfigXpath( primarySession_, "xpo", dbName, checker ); } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::checkEntriesImpl( const string& dbName, const shared_ptr& db ) { XMLContentChecker checker( db ); queryEngine_->tryGetConfigXpath( primarySession_, "xpo", dbName, checker ); } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::checkConfig() { checkEntriesImpl( writeableDbName_, candidateEntries_ ); checkEntriesImpl( "running", runningEntries_ ); } // ---------------------------------------------------------------------------| void DeviceModuleCommonFixture::initialise2Profiles() { createXpoContainer( primarySession_ ); // create Profile 1 xpoProfileQuery( primarySession_, 1 ); profileStreamQuery( primarySession_, 1, 1 ); configureResourceDescrption( primarySession_, 1, 1, 1, ResourceNodeConfig{ 100, boost::optional(), boost::optional(), boost::optional(), string( "VR-01" ) } ); configureResourceDescrption( primarySession_, 1, 1, 2, ResourceNodeConfig{ 101, boost::optional(), boost::optional(), boost::optional(), string( "VR-02" ) } ); configureResourceDescrption( primarySession_, 1, 1, 3, ResourceNodeConfig{ 102, boost::optional(), boost::optional(), boost::optional(), string( "VR-03" ) } ); configureResourceConnection( primarySession_, 1, 1, 1, ConnectionItemConfig{ 1, 10, 2, 11, 100 } ); configureResourceConnection( primarySession_, 1, 1, 2, ConnectionItemConfig{ 2, 20, 3, 21, 200 } ); profileStreamQuery( primarySession_, 1, 2 ); configureResourceDescrption( primarySession_, 1, 2, 1, ResourceNodeConfig{ 103, boost::optional(), boost::optional(), boost::optional(), string( "VR-04" ) } ); configureResourceDescrption( primarySession_, 1, 2, 2, ResourceNodeConfig{ 104, boost::optional(), boost::optional(), boost::optional(), string( "VR-05" ) } ); configureResourceDescrption( primarySession_, 1, 2, 3, ResourceNodeConfig{ 105, boost::optional(), boost::optional(), boost::optional(), string( "VR-06" ) } ); configureResourceConnection( primarySession_, 1, 2, 1, ConnectionItemConfig{ 1, 1, 1, 1, 100 } ); configureResourceConnection( primarySession_, 1, 2, 2, ConnectionItemConfig{ 1, 1, 1, 1, 100 } ); configureStreamConnection( primarySession_, 1, 1, StreamConnectionItemConfig{ 1, 1, 3, 1, 100, 1, 2 } ); configureStreamConnection( primarySession_, 1, 2, StreamConnectionItemConfig{ 2, 1, 2, 1, 100, 2, 1 } ); // create Profile 2 xpoProfileQuery( primarySession_, 2 ); profileStreamQuery( primarySession_, 2, 1 ); configureResourceDescrption( primarySession_, 2, 1, 1, ResourceNodeConfig{ 100, boost::optional(), boost::optional(), boost::optional(), string( "VR-11" ) } ); configureResourceDescrption( primarySession_, 2, 1, 2, ResourceNodeConfig{ 101, boost::optional(), boost::optional(), boost::optional(), string( "VR-12" ) } ); configureResourceDescrption( primarySession_, 2, 1, 3, ResourceNodeConfig{ 102, boost::optional(), boost::optional(), boost::optional(), string( "VR-13" ) } ); configureResourceConnection( primarySession_, 2, 1, 1, ConnectionItemConfig{ 1, 10, 2, 11, 110 } ); configureResourceConnection( primarySession_, 2, 1, 2, ConnectionItemConfig{ 2, 20, 3, 21, 210 } ); profileStreamQuery( primarySession_, 2, 2 ); configureResourceDescrption( primarySession_, 2, 2, 1, ResourceNodeConfig{ 103, boost::optional(), boost::optional(), boost::optional(), string( "VR-14" ) } ); configureResourceDescrption( primarySession_, 2, 2, 2, ResourceNodeConfig{ 104, boost::optional(), boost::optional(), boost::optional(), string( "VR-15" ) } ); configureResourceDescrption( primarySession_, 2, 2, 3, ResourceNodeConfig{ 105, boost::optional(), boost::optional(), boost::optional(), string( "VR-16" ) } ); configureResourceConnection( primarySession_, 2, 2, 1, ConnectionItemConfig{ 1, 1, 1, 1, 250 } ); configureResourceConnection( primarySession_, 2, 2, 2, ConnectionItemConfig{ 1, 1, 1, 1, 300 } ); configureStreamConnection( primarySession_, 2, 1, StreamConnectionItemConfig{ 1, 1, 3, 1, 100, 2, 1 } ); configureStreamConnection( primarySession_, 2, 2, StreamConnectionItemConfig{ 2, 1, 2, 1, 100, 1, 2 } ); // set the active profile setActiveProfile( primarySession_, 1 ); // commit and check commitChanges( primarySession_ ); checkConfig(); } } // namespace XPO3NetconfIntegrationTest //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/fixtures/system-fixture-helper.h0000664000175000017500000000216214770023131025562 0ustar vladimirvladimir#ifndef __SYSTEM_FIXTURE_HELPER_H #define __SYSTEM_FIXTURE_HELPER_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/abstract-fixture-helper.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Helper class for system fixtures. */ class SystemFixtureHelper : public AbstractFixtureHelper { public: /** * Mimic a yuma startup if required by test harness. */ virtual void mimicStartup(); /** * Mimic a yuma shutdown if required by test harness. */ virtual void mimicShutdown(); /** * Mimic a yuma restart if required by test harness. */ virtual void mimicRestart(); }; } // namespace YumaTest #endif // __SYSTEM_FIXTURE_HELPER_H yuma123_2.14/netconf/test/support/fixtures/device-module-common-fixture.h0000664000175000017500000003463414770023131027002 0ustar vladimirvladimir#ifndef __DEVICE_MODULE_COMMON_FIXTURE_H #define __DEVICE_MODULE_COMMON_FIXTURE_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/query-suite-fixture.h" #include "test/support/msg-util/xpo-query-builder.h" #include "test/support/db-models/device-test-db.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractNCSession; class XPO3Container; // ---------------------------------------------------------------------------| // /// @brief This class is used to perform test case initialisation for testing /// with the Device Test module. /// @desc This class should be used as a base class for test suites /// fuixtures that need to use common DeviceModule access code. // struct DeviceModuleCommonFixture : public YumaTest::QuerySuiteFixture { public: /// @brief Constructor. explicit DeviceModuleCommonFixture( const std::string& moduleNs ); /// @brief Destructor. Shutdown the test. ~DeviceModuleCommonFixture(); /// @brief Create the xpo level container. /// /// @param session the session running the query /// @param op the operation type /// @param failReason optional expected failure reason. void createXpoContainer( std::shared_ptr session, const std::string& failReason = std::string() ); /// @brief Delete the xpo level container. /// /// @param session the session running the query /// @param commitChange flag indicating if the change should be committed. /// @param failReason optional expected failure reason. void deleteXpoContainer( std::shared_ptr session, bool commitChange = true, const std::string& failReason = std::string() ); /// @brief Set the active profile /// /// @param session the session running the query /// @param profileId the id of the active profile /// @param failReason optional expected failure reason. void setActiveProfile( std::shared_ptr session, uint16_t profileId, const std::string& failReason = std::string() ); /// @brief Perform an xpo profile query operation. /// /// @param session the session running the query. /// @param profileId the is of the profile. /// @param op the operation type /// @param failReason optional expected failure reason. void xpoProfileQuery( std::shared_ptr session, uint16_t profileId, const std::string& op = std::string( "create" ), const std::string& failReason = std::string() ); /// @brief Create a StreamItem for a profile. /// /// @param session the session running the query. /// @param profileId the of the owning profile. /// @param streamId the of the stream to create. /// @param op the operation type /// @param failReason optional expected failure reason. void profileStreamQuery( std::shared_ptr session, uint16_t profileId, uint16_t streamId, const std::string& op = std::string( "create" ), const std::string& failReason = std::string() ); /// @brief Create a resource node. /// /// @param session the session running the query. /// @param profileId the of the owning profile. /// @param streamId the of the stream to create. /// @param resourceNodeId the id of the resource node to create. /// @param op the operation type /// @param failReason optional expected failure reason. void resourceDescriptionQuery( std::shared_ptr session, uint16_t profileId, uint16_t streamId, uint16_t resourceNodeId, const std::string& op = std::string( "create" ), const std::string& failReason = std::string() ); /// @brief Create a VR connection node . /// /// @param session the session running the query. /// @param profileId the of the owning profile. /// @param streamId the of the stream to create. /// @param connectionId the id of the connection node to create. /// @param op the operation type /// @param failReason optional expected failure reason. void resourceConnectionQuery( std::shared_ptr session, uint16_t profileId, uint16_t streamId, uint16_t connectionId, const std::string& op = std::string( "create" ), const std::string& failReason = std::string() ); /// @brief Configure a Virtual resource item. /// @desc This function generates the complete configuration for a /// ResourceNode, items are configured depending on whether or /// not they have been set in the supplied ResourceNodeConfig. /// /// @param session the session running the query. /// @param profileId the of the owning profile. /// @param streamId the of the stream to create. /// @param resourceId the id of the resource node to create. /// @param config the configuration to generate /// @param op the operation type /// @param failReason optional expected failure reason. void configureResourceDescrption( std::shared_ptr session, uint16_t profileId, uint16_t streamId, uint16_t resourceId, const YumaTest::ResourceNodeConfig& config, const std::string& op = std::string( "create" ), const std::string& failReason = std::string() ); /// @brief Configure a virtual resource connection item. /// @desc This function generates the complete configuration for a /// ConnectionItem, items are configured depending on whether or /// not they have been set in the supplied ConnectionItemConfig. /// /// @param session the session running the query. /// @param profileId the of the owning profile. /// @param streamId the of the stream to create. /// @param connectionId the id of the connection node to create. /// @param config the configuration to generate /// @param op the operation type /// @param failReason optional expected failure reason. void configureResourceConnection( std::shared_ptr session, uint16_t profileId, uint16_t streamId, uint16_t connectionId, const YumaTest::ConnectionItemConfig& config, const std::string& op = std::string( "create" ), const std::string& failReason = std::string() ); /// @brief Configure a stream connection item. /// @desc This function generates the complete configuration for a /// ConnectionItem, items are configured depending on whether or not /// they have been set in the supplied ConnectionItemConfig. /// /// @param session the session running the query. /// @param profileId the of the owning profile. /// @param connectionId the id of the connection node to create. /// @param config the configuration to generate /// @param op the operation type /// @param failReason optional expected failure reason. void configureStreamConnection( std::shared_ptr session, uint16_t profileId, uint16_t connectionId, const YumaTest::StreamConnectionItemConfig& config, const std::string& op = std::string( "create" ), const std::string& failReason = std::string() ); /// @brief Create a new stream connection. /// /// @param session the session running the query. /// @param profileId the of the owning profile. /// @param id the id of the stream /// @param op the operation type /// @param failReason optional expected failure reason. void streamConnectionQuery( std::shared_ptr session, uint16_t profileId, uint16_t id, const std::string& op = std::string( "create" ), const std::string& failReason = std::string() ); /// @brief Commit the changes. /// /// @param session the session requesting the locks virtual void commitChanges( std::shared_ptr session ); /// @brief Discard all changes locally. /// @desc Let the test harness know that changes should be discarded /// (e.g. due to unlocking the database without a commit. void discardChanges(); /// @brief Check the configration. /// @desc Get the contents of the candidate database and verify /// that all entries are as expected. virtual void checkConfig(); /// @brief Initialise 2 profiles for use in most test scenarios. /// @desc This function creates 2 profiles with the following /// format: /// /// profile /// stream<1> /// virtual-resource<1> /// virtual-resource<2> /// virtual-resource<3> /// virtual-connection<1> /// virtual-connection<2> /// stream<2> /// virtual-resource<1> /// virtual-resource<2> /// virtual-resource<3> /// virtual-connection<1> /// virtual-connection<2> /// stream-connection<1> /// void initialise2Profiles(); protected: /// @brief Store connection configuration data in a connection item. /// /// @tparam ConnectionItemType the connection item type /// @tparam ConfigType the config item type /// @param connectionItem the connection item being updated /// @param config the new configuration. template< class ConnectionItemType, class ConfigType > void storeConnectionItemConfig( ConnectionItemType& connectionItem, const ConfigType& config ); /// @brief Update the stream connection entry in the expected data. /// @desc Utility function to ensure that the connection exists in the /// candidateEntries_ tree. If the item does not exist it will be created, /// initialised and added. /// /// @param profileId the of the profile. /// @param connectionId the of the connection. void ensureStreamConnectionExists( uint16_t profileId, uint16_t connectionId ); /// @brief Update the profile entry in the expected data. /// Utility function to ensure that the profile exists in the /// candidateEntries_ tree. If the item does not exist it will be created, /// initialised and added. /// /// @param profileId the of the connection. void ensureProfileExists( uint16_t profileId ); /// @brief Update the stream entry in the expected data. /// @desc Utility function to ensure that the stream exists for the /// profile in the candidateEntries_ tree. If the item does not exist /// it will be created, initialised and added. /// /// @param profileId the of the profile. /// @param streamId the of the connection. void ensureProfileStreamExists( uint16_t profileId, uint16_t streamId ); /// @brief Ensure a resource description node exists. /// @param resourceMap the map to create teh resouce for. /// @param resourceId the of the resource. void ensureResourceExists( ResourceDescriptionMap& resourceMap, uint32_t resourceId ); /// @brief Update the resource description entry in the expected data. /// @desc Utility function to ensure that the resource exists for the /// stream of a profile in the candidateEntries_ tree. If the item does /// not exist it will be created, initialised and added. /// /// @param profileId the of the profile. /// @param streamId the of the connection. /// @param resourceId the of the resource. void ensureResourceDescriptionExists( uint16_t profileId, uint16_t streamId, uint16_t resourceId ); /// @brief Update the resource connection entry in the expected data. /// @desc Utility function to ensure that the connection exists for the /// stream of a profile in the candidateEntries_ tree. If the item does /// not exist it will be created, initialised and added. /// /// @param profileId the of the connection. /// @param streamId the of the connection. /// @param connectionId the of the connection. void ensureResourceConnectionExists( uint16_t profileId, uint16_t streamId, uint16_t connectionId ); /// @brief Utility fuction for checking the contents of a configuartion /// /// @param dbName the name of the netconf database to check /// @param db the expected contents. virtual void checkEntriesImpl( const std::string& dbName, const std::shared_ptr& db ); /// @brief Utility function to query a configuration. /// @desc this function can be used for debugging purposes. It /// queries the specified config and as a result the configuration /// xml will be dumped into the test output file SesXXXX. /// /// @param dbName the name of the netconf database to check void queryConfig( const string& dbName ) const; /// @param resourceNode the resource node to update. /// @param config the configuration to generate /// @param op the operation type void updateResourceDescriptionEntry( YumaTest::ResourceNode& resourceNode, const YumaTest::ResourceNodeConfig& config ); protected: ///< The builder for the generating netconf messages in xml format. std::shared_ptr< YumaTest::XPOQueryBuilder > xpoBuilder_; std::shared_ptr runningEntries_; ///< the running data std::shared_ptr candidateEntries_; ///< the candidate data }; } // namespace YumaTest #endif // __DEVICE_MODULE_COMMON_FIXTURE_H //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/fixtures/integ-test-fixture.h0000664000175000017500000000351114770023131025043 0ustar vladimirvladimir#ifndef __INTEG_TEST_FIXTURE__H #define __INTEG_TEST_FIXTURE__H #include "test/support/fixtures/abstract-global-fixture.h" #include "test/support/fixtures/abstract-fixture-helper.h" #include "test/support/nc-query-util/yuma-op-policies.h" #include "test/support/fixtures/test-context.h" namespace YumaTest { // ---------------------------------------------------------------------------| /** * This class is used to perform all global initialisation and teardown * of Yuma. If any errors occur or are reported during initialisation * the test is terminated with an assertion. * * BOOST::TEST Fixtures do not have a way of configuring any internal * data at runtime. To allow configuration of the test fixture class * all configuration data is passed compile time as a template * parameter. * * \tparam ArgTraits a structure containing any command line arguments the * test harness should use to configure neconf. * \tparam YumaQueryOpLogPolicy the policy for generating Yuma OP log * filenames, which are written whenever the test harness * injects a message into yuma. */ template < class SpoofedArgs, class OpPolicy = MessageIdLogFilenamePolicy > class IntegrationTestFixture : public AbstractGlobalTestFixture { typedef SpoofedArgs Args; public: /** * Constructor. Initialise Yuma for system testing. This replaces * the functionality from cmn_init(). */ IntegrationTestFixture(); /** * Destructor. Shutdown the Yuma system test environment. */ ~IntegrationTestFixture(); private: /** * Configure the test context */ void configureTestContext(); /** * Fixture helper */ std::shared_ptr fixtureHelper_; }; } // namespace YumaTest #endif // __INTEG_TEST_FIXTURE__H yuma123_2.14/netconf/test/support/fixtures/system-test-fixture.inl0000664000175000017500000001231414770023131025615 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/system-test-fixture.h" #include "test/support/fixtures/test-context.h" #include "test/support/fixtures/system-fixture-helper-factory.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-query-util/yuma-op-policies.h" #include "test/support/nc-session/remote-nc-session-factory.h" #include "test/support/callbacks/system-cb-checker-factory.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include // ---------------------------------------------------------------------------| // Boost Includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace { // ---------------------------------------------------------------------------| /** * Get an environment variable. If the variable is mandatory and it is * not set this function calls assert. * * The system test harness uses the following environment variables to * determine the configuration for testing: * *
    *
  1. $YUMA_AGENT_IPADDR - The ip address of the remote Yuma Agent.
  2. *
  3. $YUMA_AGENT_USER - The user name to use.
  4. *
  5. $YUMA_AGENT_PASSWORD - The user's password.
  6. *
  7. $YUMA_AGENT_PORT - The port used by the Yuma Agent, (defaults: 830).
  8. *
* * \param key the name of the variable to retieve. * \param mandatory flag indicating if the variable is mandatory * \return the value for the environnment variable. */ char* GetEnvVar( const string& key, const bool mandatory = false ) { char* val = getenv( key.c_str() ); if ( mandatory && !val ) { assert( val && "Environment Not Configured!" ); } return val; } } // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| // SystemTestFixture // ---------------------------------------------------------------------------| template SystemTestFixture::SystemTestFixture() : AbstractGlobalTestFixture( ( sizeof( Args::argv )/sizeof( Args::argv[0] ) ), Args::argv ) { DisplayTestBreak( '=', true ); BOOST_TEST_MESSAGE( "Initialising..." ); configureTestContext(); } // ---------------------------------------------------------------------------| template SystemTestFixture::~SystemTestFixture() { BOOST_TEST_MESSAGE( "SystemTestFixture() Cleaning Up..." ); } // ---------------------------------------------------------------------------| template void SystemTestFixture::configureTestContext() { using std::shared_ptr; using boost::unit_test::framework::master_test_suite; cout << "command line arguments: " << master_test_suite().argc << "\n"; int argc = master_test_suite().argc; char** argv = master_test_suite().argv; for( int idx = 0; idx < argc; ++idx ) { cout << "\t" << argv[idx] << "\n"; } shared_ptr< AbstractYumaOpLogPolicy > queryLogPolicy( new OpPolicy( "./yuma-op" ) ); assert( queryLogPolicy ); string ipAddr( GetEnvVar( "YUMA_AGENT_IPADDR", true ) ); string user( GetEnvVar( "YUMA_AGENT_USER", true ) ); string password( GetEnvVar( "YUMA_AGENT_PASSWORD", true ) ); char* val = GetEnvVar( "YUMA_AGENT_PORT", false ); uint16_t ipPort = val ? boost::lexical_cast( val ) : 830; shared_ptr< AbstractNCSessionFactory > sessionFactory( new RemoteNCSessionFactory( ipAddr, ipPort, user, password, queryLogPolicy ) ) ; shared_ptr< AbstractCBCheckerFactory > cbCheckerFactory( new SystemCBCheckerFactory() ); shared_ptr< AbstractFixtureHelperFactory > fixtureHelperFactory( new SystemFixtureHelperFactory() ); shared_ptr< TestContext > testContext( new TestContext( false, getTargetDbConfig(), usingStartupCapability(), numArgs_, argv_, sessionFactory, cbCheckerFactory, fixtureHelperFactory ) ); assert( testContext ); TestContext::setTestContext( testContext ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/fixtures/abstract-global-fixture.cpp0000664000175000017500000000525414770023131026362 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/abstract-global-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/fixtures/test-context.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| // Boost Includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| namespace ph = boost::phoenix; namespace ph_args = boost::phoenix::arg_names; using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| AbstractGlobalTestFixture::AbstractGlobalTestFixture( int argc, const char** argv ) : numArgs_( argc ) , argv_( argv ) { } // ---------------------------------------------------------------------------| AbstractGlobalTestFixture::~AbstractGlobalTestFixture() { DisplayTestBreak( '=', true ); } // ---------------------------------------------------------------------------| TestContext::TargetDbConfig AbstractGlobalTestFixture::getTargetDbConfig() { const string running( "--target=running" ); const char** endPos = argv_ + numArgs_; const char** res = find_if( argv_, endPos, ph_args::arg1 == running ); if ( res == endPos ) { cout << "Using Candidate Config!\n"; return TestContext::CONFIG_USE_CANDIDATE; } cout << "Using Running Config!\n"; return TestContext::CONFIG_WRITEABLE_RUNNNIG; } // ---------------------------------------------------------------------------| bool AbstractGlobalTestFixture::usingStartupCapability() { const string startup( "--with-startup=true" ); const char** endPos = argv_ + numArgs_; const char** res = find_if( argv_, endPos, ph_args::arg1 == startup ); if ( res == endPos ) { cout << "Startup capability is NOT in use!\n"; return false; } cout << "Startup capability is in use!\n"; return true; } } // namespace YumaTest yuma123_2.14/netconf/test/support/fixtures/spoofed-args.h0000664000175000017500000000171114770023131023665 0ustar vladimirvladimir#ifndef __SPOOFED_ARGS_H #define __SPOOFED_ARGS_H // ---------------------------------------------------------------------------| namespace YumaTest { /** * This simple structure is used for spoofing command line arguments * which are passed to Text Fixtures as the ArgTraits template parameter. * * Usage: include this file and then simply initialise the internal * argv parameters to required 'spoofed' command line * arguments, e.g.: * * const char* SpoofedArgs::argv[] = { * ( "yuma-test" ), * ( "--modpath=../../../../modules/netconfcentral" * ":../../../../modules/ietf" * ":../../../../modules/yang" * ":../../../../modules/test/pass" * ), * ( "--access-control=off" ), * ( "--target=running" ), * }; */ struct SpoofedArgs { static const char* argv[]; ///< Spoofed command line arguments. }; }// namespace YumaTest #endif // __SPOOFED_ARGS_H yuma123_2.14/netconf/test/support/fixtures/base-suite-fixture.h0000664000175000017500000000407314770023131025025 0ustar vladimirvladimir#ifndef __YUMA_BASE_SUITE_FIXTURE__H #define __YUMA_BASE_SUITE_FIXTURE__H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/test-context.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| /** * This class is used to perform simple test case initialisation. * It can be used on a per test case basis or on a per test suite * basis. This fixture provides functionality common to all tests. */ class BaseSuiteFixture { public: /** * Constructor. */ BaseSuiteFixture(); /** * Destructor. Shutdown the test. */ virtual ~BaseSuiteFixture(); /** * Utility to check if the candidate configuration is in use. * * \return true if the candidate configuration is in use. */ bool useCandidate() const { return ( TestContext::CONFIG_USE_CANDIDATE == testContext_->targetDbConfig_ ); } /** * Utility to check if the startup capability is in use. * * \return true if the startup capability is in use. */ bool useStartup() const { return ( testContext_->usingStartup_ ); } /** the test context */ std::shared_ptr testContext_; /** The writable database name */ std::string writeableDbName_; }; } // namespace YumaTest // ---------------------------------------------------------------------------| #endif // __YUMA_BASE_SUITE_FIXTURE__H yuma123_2.14/netconf/test/support/fixtures/system-test-fixture.h0000664000175000017500000000317014770023131025262 0ustar vladimirvladimir#ifndef __YUMA_SYSTEM_TEST_FIXTURE__H #define __YUMA_SYSTEM_TEST_FIXTURE__H #include "test/support/fixtures/abstract-global-fixture.h" #include "test/support/nc-query-util/yuma-op-policies.h" #include "test/support/fixtures/test-context.h" namespace YumaTest { // ---------------------------------------------------------------------------| /** * This class is used to perform all global initialisation and teardown * of Yuma. If any errors occur or are reported during initialisation * the test is terminated with an assertion. * * BOOST::TEST Fixtures do not have a way of configuring any internal * data at runtime. To allow configuration of the test fixture class * all configuration data is passed compile time as a template * parameter. * * \tparam ArgTraits a structure containing any command line arguments the * test harness should use to configure neconf. * \tparam YumaQueryOpLogPolicy the policy for generating Yuma OP log * filenames, which are written whenever the test harness * injects a message into yuma. */ template < class SpoofedArgs, class OpPolicy = MessageIdLogFilenamePolicy > class SystemTestFixture : public AbstractGlobalTestFixture { typedef SpoofedArgs Args; public: /** * Constructor. Initialise Yuma for system testing. */ SystemTestFixture(); /** * Destructor. Shutdown the Yuma system test environment. */ ~SystemTestFixture(); private: /** * Configure the test context */ void configureTestContext(); }; } // namespace YumaTest #endif // __YUMA_SYSTEM_TEST_FIXTURE__H yuma123_2.14/netconf/test/support/fixtures/abstract-fixture-helper-factory.h0000664000175000017500000000202114770023131027500 0ustar vladimirvladimir#ifndef __YUMA_ABSTRACT_FIXTURE_HELPER_FACTORY_H #define __YUMA_ABSTRACT_FIXTURE_HELPER_FACTORY_H // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractFixtureHelper; // ---------------------------------------------------------------------------| /** * An abstract Fixture Helper Factory. */ class AbstractFixtureHelperFactory { public: /** * Constructor. */ AbstractFixtureHelperFactory() {} /** Destructor */ virtual ~AbstractFixtureHelperFactory() {} /** * Create a new Fixture Helper. * * \return a new Fixture Helper. */ virtual std::shared_ptr createHelper() = 0; }; } // namespace YumaTest #endif // __YUMA_ABSTRACT_FIXTURE_HELPER_FACTORY_H yuma123_2.14/netconf/test/support/fixtures/test-context.h0000664000175000017500000000575414770023131023750 0ustar vladimirvladimir#ifndef __YUMA_TEST_CONTEXT_H #define __YUMA_TEST_CONTEXT_H // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractNCSessionFactory; class AbstractCBCheckerFactory; class AbstractFixtureHelperFactory; /** * BOOST Test does not have any way of accessing global test suite * fixture configuration. This class provides a 'singleton' like way * of accessing any test configration data set up by the global test * fixture. */ struct TestContext { enum TargetDbConfig { CONFIG_WRITEABLE_RUNNNIG, CONFIG_USE_CANDIDATE, }; /** * Contructor * * \param targetDbConfig the name of the target database * (either 'running' or 'candidate' ) * \param usingStartup true if the startup capability is in use. * \param queryFactory the factory to use when creating NCQueries. * \param cbCheckerFactory the factory to use when creating callback checkers. * \param fixtureHelperFactory the factory to use when creating fixture helpers. */ TestContext( bool isIntegTest, TargetDbConfig targetDbConfig, bool usingStartup, int argc, const char** argv, std::shared_ptr queryFactory, std::shared_ptr cbCheckerFactory, std::shared_ptr fixtureHelperFactory ); /** * Set the new test context. * * \param newContext the new test context. */ static void setTestContext( std::shared_ptr newContext ) { testContext_ = newContext; } /** * Get the test context. * * \return the test context. */ static std::shared_ptr getTestContext() { return testContext_; } /** Flag indicating if this is an integration test */ bool isIntegTest_; /** The DB configuration */ TargetDbConfig targetDbConfig_; /** The startup configuration */ bool usingStartup_; /** the name of the writable database (either running or candidate) */ std::string writeableDbName_; /** the number of command line args. */ int numArgs_; /** the command line args. */ const char** argv_; /** the session factory. */ std::shared_ptr sessionFactory_; /** the cb checker factory. */ std::shared_ptr cbCheckerFactory_; /** the fixture helper factory. */ std::shared_ptr fixtureHelperFactory_; private: /** The current test context */ static std::shared_ptr testContext_; }; } // namespace YumaTest #endif // __YUMA_TEST_CONTEXT_H yuma123_2.14/netconf/test/support/fixtures/integ-fixture-helper.h0000664000175000017500000000460614770023131025351 0ustar vladimirvladimir#ifndef __INTEG_FIXTURE_HELPER_H #define __INTEG_FIXTURE_HELPER_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/abstract-fixture-helper.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Helper class for integration test fixtures. */ class IntegFixtureHelper : public AbstractFixtureHelper { public: /** * Mimic a yuma startup if required by test harness. */ virtual void mimicStartup(); /** * Mimic a yuma shutdown if required by test harness. */ virtual void mimicShutdown(); /** * Mimic a yuma restart if required by test harness. */ virtual void mimicRestart(); private: /** * Initialise the NCX engine. * This function this simply calls ncx_init() and checks that it * returned NO_ERR. */ void initialiseNCXEngine(int numArgs, const char** argv); /** * Load the base schemas. * This function loads the following base schemas: *
    *
  • NCXMOD_YUMA_NETCONF - NETCONF data types and RPC methods.
  • *
  • NCXMOD_NETCONFD - The netconf server boot parameter definition * file.
  • *
*/ void loadBaseSchemas(); /** * Load the core schemas. * This function loads the following base schemas: *
    *
  • NCXMOD_YUMA_NETCONF - NETCONF data types and RPC methods.
  • *
  • NCXMOD_NETCONFD - The netconf server boot parameter definition * file.
  • *
*/ void loadCoreSchema(); /** * Perform stage 1 Agt initialisation. * This function calls agt_init1 to perform stage 1 Agt initialisation. */ void stage1AgtInitialisation(int numArgs, const char** argv); /** * Perform stage 2 Agt initialisation. * This function calls agt_init2 to perform stage 2 Agt initialisation. */ void stage2AgtInitialisation(); }; } // namespace YumaTest #endif // __INTEG_FIXTURE_HELPER_H yuma123_2.14/netconf/test/support/fixtures/device-module-fixture.h0000664000175000017500000000247614770023131025513 0ustar vladimirvladimir#ifndef __XPO3_SUITE_FIXTURE_H #define __XPO3_SUITE_FIXTURE_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/device-module-common-fixture.h" #include "test/support/msg-util/xpo-query-builder.h" #include "test/support/db-models/device-test-db.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractNCSession; class XPO3Container; // ---------------------------------------------------------------------------| // /// @brief This class is used to perform test case initialisation for testing /// with the Device Test module. // struct DeviceModuleFixture : public DeviceModuleCommonFixture { public: /// @brief Constructor. DeviceModuleFixture(); /// @brief Destructor. Shutdown the test. ~DeviceModuleFixture(); }; } // namespace YumaTest #endif // __XPO3_SUITE_FIXTURE_H //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/fixtures/system-fixture-helper-factory.h0000664000175000017500000000243414770023131027231 0ustar vladimirvladimir#ifndef __YUMA_SYSTEM_FIXTURE_HELPER_FACTORY_H #define __YUMA_SYSTEM_FIXTURE_HELPER_FACTORY_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/abstract-fixture-helper-factory.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractFixtureHelper; // ---------------------------------------------------------------------------| /** * A factory for creating System Fixture Helpers. */ class SystemFixtureHelperFactory : public AbstractFixtureHelperFactory { public: /** * Constructor. */ SystemFixtureHelperFactory(); /** Destructor */ virtual ~SystemFixtureHelperFactory(); /** * Create a new Fixture Helper. * * \return a new Fixture Helper. */ std::shared_ptr createHelper(); }; } // namespace YumaTest #endif // __YUMA_SYSTEM_FIXTURE_HELPER_FACTORY_H yuma123_2.14/netconf/test/support/fixtures/device-module-fixture.cpp0000664000175000017500000000444414770023131026043 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/device-module-fixture.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/misc-util/log-utils.h" #include "test/support/checkers/xml-content-checker.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| // @TODO This suite currently only supports 'happy days' scenarios. // @TODO There is no support for many failures sceanrios such as: // @TODO An attempt to create a streamItem for a profile that does // @TODO not exist. // @TODO Support for these scenarios will be required! // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| DeviceModuleFixture::DeviceModuleFixture() : DeviceModuleCommonFixture( "http://netconfcentral.org/ns/device_test" ) { // ensure the module is loaded queryEngine_->loadModule( primarySession_, "device_test" ); } // ---------------------------------------------------------------------------| DeviceModuleFixture::~DeviceModuleFixture() { } } // namespace XPO3NetconfIntegrationTest //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/fixtures/simple-yang-fixture.h0000664000175000017500000000740314770023131025211 0ustar vladimirvladimir#ifndef __YUMA_SIMPLE_YANG_TEST_FIXTURE__H #define __YUMA_SIMPLE_YANG_TEST_FIXTURE__H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/query-suite-fixture.h" #include "test/support/msg-util/NCMessageBuilder.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractNCSession; // ---------------------------------------------------------------------------| /** * This class is used to perform simple test case initialisation. * It can be used on a per test case basis or on a per test suite * basis. */ struct SimpleYangFixture : public QuerySuiteFixture { public: /** * Constructor. */ SimpleYangFixture(); /** * Destructor. Shutdown the test. */ ~SimpleYangFixture(); /** * Create the top level container. * * \param session the session running the query * \param container the container to create */ void createContainer( std::shared_ptr session, const std::string& container ); /** * Delete the top level container. * * \param session the session running the query * \param container the container to delete */ void deleteContainer( std::shared_ptr session, const std::string& container ); /** * Create interface container. * * \param session the session running the query * \param type the type of the interface (ethernet or atm) * \param mtu the MTU of the interface * \param operation the operation to perform on the container */ void createInterfaceContainer( std::shared_ptr session, const std::string& type, int mtu, const std::string& operation ); /** * Add a choice leaf. * * \param session the session running the query * \param entryChoice the name of the choice leaf to add. * \param operationStr the type of addition - add, merge, replace. */ void addChoice( std::shared_ptr session, const std::string& choiceStr, const std::string& operationStr ); /** * Verify the choices in the specified database. * * \param session the session running the query * \param targetDbName the name of the database to check * \param choice the expected choice in the database being checked. */ void checkChoiceImpl( std::shared_ptr session, const std::string& targetDbName, const std::string& choice ); /** * Check the status of both databases. * * \param session the session running the query */ void checkChoice( std::shared_ptr session ); /** * Commit the changes. * * \param session the session requesting the locks */ virtual void commitChanges( std::shared_ptr session ); const std::string moduleNs_; ///< the module namespace const std::string containerName_; ///< the container name std::string runningChoice_; /// Running Db Choice std::string candidateChoice_; /// Candidate Db Choice }; } // namespace YumaTest #endif // __YUMA_SIMPLE_YANG_TEST_FIXTURE__H yuma123_2.14/netconf/test/support/fixtures/abstract-global-fixture.h0000664000175000017500000000264614770023131026031 0ustar vladimirvladimir#ifndef __ABSTRACT_GLOBAL_TEST_FIXTURE__H #define __ABSTRACT_GLOBAL_TEST_FIXTURE__H #include "test/support/fixtures/test-context.h" namespace YumaTest { // ---------------------------------------------------------------------------| /** * This class is a base class for the global etst fixtures which perform all * global initialisation and teardown for the Yuma test harness. * If any errors occur or are reported during initialisation * the test is terminated with an assertion. */ class AbstractGlobalTestFixture { public: /** * Constructor. * * \param argc the number of arguments * \param argv the arguments */ AbstractGlobalTestFixture( int argc, const char** argv ); /** * Destructor. Shutdown the Yuma system test environment. */ virtual ~AbstractGlobalTestFixture(); protected: /** * Get the target DB configuration type * * \return the target DB configuration type */ TestContext::TargetDbConfig getTargetDbConfig(); /** * Determine whether the startup capability is in use * * \return true if the startup capability is in use and false otherwise */ bool usingStartupCapability(); protected: int numArgs_; ///< The number of spoofed command line arguments const char** argv_; ///< the spoofed command line arguments }; } // namespace YumaTest #endif // __ABSTRACT_GLOBAL_TEST_FIXTURE__H yuma123_2.14/netconf/test/support/fixtures/device-get-module-fixture.h0000664000175000017500000001632314770023131026264 0ustar vladimirvladimir#ifndef __DEVICE_GET_MODULE_FIXTURE_H #define __DEVICE_GET_MODULE_FIXTURE_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/device-module-common-fixture.h" #include "test/support/msg-util/xpo-query-builder.h" #include "test/support/db-models/device-test-db.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractNCSession; class XPO3Container; //TODO Potential future refactoring - move common device module test code to //TODO parent class. // ---------------------------------------------------------------------------| // /// @brief This class is used to perform test case initialisation for testing /// with the Device Get Test module. // struct DeviceGetModuleFixture : public DeviceModuleCommonFixture { public: /// @brief Constructor. DeviceGetModuleFixture(); /// @brief Destructor. Shutdown the test. ~DeviceGetModuleFixture(); /// @brief Check the content of the xpath filtered response. /// @desc Get the contents of the response and verify /// that all entries are as expected. virtual void checkXpathFilteredConfig(const string& xPath) const; /// @brief Check the content of the subtree filtered response. /// @desc Get the contents of the response and verify /// that all entries are as expected. virtual void checkSubtreeFilteredConfig(const string& subtree) const; //protected: /// @brief Update activeProfile in expected response. /// Utility function to ensure that the activeProfile is set in the /// filteredConfig_ tree. /// /// @param profileId the id of the active profile. void ensureActiveProfileInFilteredConfig( uint16_t profileId ); /// @brief Update profile in expected response. /// Utility function to ensure that the profile exists in the /// filteredConfig_ tree. If the item does not exist it will be created, /// initialised and added. /// /// @param profileId the of of the connection. void ensureProfileInFilteredConfig( uint16_t profileId ); /// @brief Update stream connection in expected response. /// @desc Utility function to ensure that the connection exists in the /// filteredConfig_ tree. If the item does not exist it will be created, /// initialised and added. /// /// @param profileId the of of the profile. /// @param connectionId the of of the connection. void ensureStreamConnectionInFilteredConfig( uint16_t profileId, uint16_t connectionId ); /// @brief Update stream in expected response. /// @desc Utility function to ensure that the stream exists for the /// profile in the filteredConfig_ tree. If the item does not exist /// it will be created, initialised and added. /// /// @param profileId the of of the profile. /// @param streamId the of of the connection. void ensureProfileStreamInFilteredConfig( uint16_t profileId, uint16_t streamId ); /// @brief Update resource descrip in expected response. /// @desc Utility function to ensure that the resource exists for the /// stream of a profile in the filteredConfig_ tree. If the item does /// not exist it will be created, initialised and added. /// /// @param profileId the of of the profile. /// @param streamId the of of the connection. /// @param resourceId the of of the resource. void ensureResourceDescriptionInFilteredConfig( uint16_t profileId, uint16_t streamId, uint16_t resourceId ); /// @brief Update resource connection in expected response. /// @desc Utility function to ensure that the connection exists for the /// stream of a profile in the filteredConfig_ tree. If the item does /// not exist it will be created, initialised and added. /// /// @param profileId the of of the connection. /// @param streamId the of of the connection. /// @param connectionId the of of the connection. void ensureResourceConnectionInFilteredConfig( uint16_t profileId, uint16_t streamId, uint16_t connectionId ); /// @brief Configure resource in expected response. /// @desc This function generates the complete configuration for a /// ResourceNode, items are configured depending on whether or /// not they have been set in the supplied ResourceNodeConfig. /// /// @param profileId the of the owning profile. /// @param streamId the of the stream to create. /// @param connectionId the id of the connection node to create. /// @param config the configuration to generate void configureResourceDescrptionInFilteredConfig( uint16_t profileId, uint16_t streamId, uint16_t resourceId, const YumaTest::ResourceNodeConfig& config ); /// @brief Configure resource connection in expected response. /// @desc This function generates the complete configuration for a /// ConnectionItem, items are configured depending on whether or /// not they have been set in the supplied ConnectionItemConfig. /// /// @param profileId the of the owning profile. /// @param streamId the of the stream to create. /// @param connectionId the id of the connection node to create. /// @param config the configuration to generate void configureResourceConnectionInFilteredConfig( uint16_t profileId, uint16_t streamId, uint16_t connectionId, const YumaTest::ConnectionItemConfig& config ); /// @brief Configure stream connection in expected response. /// @desc This function generates the complete configuration for a /// ConnectionItem, items are configured depending on whether or not /// they have been set in the supplied ConnectionItemConfig. /// /// @param profileId the of the owning profile. /// @param connectionId the id of the connection node to create. /// @param config the configuration to generate void configureStreamConnectionInFilteredConfig( uint16_t profileId, uint16_t connectionId, const YumaTest::StreamConnectionItemConfig& config ); /// @brief Clear container of expected response. void clearFilteredConfig(); protected: std::shared_ptr filteredConfig_; ///< response payload }; } // namespace YumaTest #endif // __DEVICE_GET_MODULE_FIXTURE_H //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/fixtures/system-fixture-helper.cpp0000664000175000017500000000225714770023131026122 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/system-fixture-helper.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| void SystemFixtureHelper::mimicStartup() { // Do nothing as mimicking a startup is not necessary during system tests. } // ---------------------------------------------------------------------------| void SystemFixtureHelper::mimicShutdown() { // Do nothing as mimicking a shutdown is not necessary during system tests. } // ---------------------------------------------------------------------------| void SystemFixtureHelper::mimicRestart() { // Do nothing as mimicking a restart is not necessary during system tests. } } // namespace YumaTest yuma123_2.14/netconf/test/support/fixtures/simple-yang-fixture.cpp0000664000175000017500000001255514770023131025550 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-yang-fixture.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/misc-util/log-utils.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| SimpleYangFixture::SimpleYangFixture() : QuerySuiteFixture() , moduleNs_( "http://netconfcentral.org/ns/simple_yang_test" ) , containerName_( "protocol" ) , runningChoice_( "" ) , candidateChoice_( "" ) { // ensure the module is loaded queryEngine_->loadModule( primarySession_, "simple_yang_test" ); } // ---------------------------------------------------------------------------| SimpleYangFixture::~SimpleYangFixture() { } // ---------------------------------------------------------------------------| void SimpleYangFixture::createContainer( std::shared_ptr session, const string& container) { assert( session ); string query = messageBuilder_->genTopLevelContainerText( container, moduleNs_, "create" ); runEditQuery( session, query ); } // ---------------------------------------------------------------------------| void SimpleYangFixture::deleteContainer( std::shared_ptr session, const string& container) { string query = messageBuilder_->genTopLevelContainerText( container, moduleNs_, "delete" ); runEditQuery( session, query ); if (container == "protocol") { candidateChoice_ = ""; if ( !useCandidate() ) { runningChoice_= candidateChoice_; } } } // ---------------------------------------------------------------------------| void SimpleYangFixture::createInterfaceContainer( std::shared_ptr session, const string& type, int mtu, const string& operation) { assert( session ); string query = messageBuilder_->genInterfaceContainerText( moduleNs_, type, mtu, operation ); if ((type == "ethernet" && mtu == 1500) || (type == "atm" && mtu >= 64 && mtu <= 17966)) { runEditQuery( session, query ); } else { runFailedEditQuery( session, query, "MTU must be"); } } // ---------------------------------------------------------------------------| void SimpleYangFixture::addChoice( std::shared_ptr session, const string& choiceStr, const string& operationStr ) { assert( session ); string query = messageBuilder_->genModuleOperationText( containerName_, moduleNs_, "<" + choiceStr + " nc:operation=\"" + operationStr +"\"/>" ); runEditQuery( session, query ); candidateChoice_ = choiceStr; if ( !useCandidate() ) { runningChoice_= candidateChoice_; } } // ---------------------------------------------------------------------------| void SimpleYangFixture::checkChoiceImpl( std::shared_ptr session, const string& targetDbName, const string& choice ) { vector expPresent{ "data" }; vector expNotPresent{ "error", "rpc-error" }; if (choice == "") { expNotPresent.push_back("udp"); expNotPresent.push_back("tcp"); } else { string notChoice = (choice == "udp") ? "tcp" : "udp"; expPresent.push_back(choice); expNotPresent.push_back(notChoice); } StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetConfigXpath( session, containerName_, targetDbName, checker ); } // ---------------------------------------------------------------------------| void SimpleYangFixture::checkChoice( std::shared_ptr session) { assert( session ); if ( useCandidate() ) { checkChoiceImpl( session, "candidate", candidateChoice_ ); } checkChoiceImpl( session, "running", runningChoice_ ); } // ---------------------------------------------------------------------------| void SimpleYangFixture::commitChanges( std::shared_ptr session ) { QuerySuiteFixture::commitChanges( session ); // copy the editted changes to the running changes if ( useCandidate() ) { runningChoice_ = candidateChoice_; } } } // namespace YumaTest yuma123_2.14/netconf/test/support/misc-util/0000775000175000017500000000000014770023131021160 5ustar vladimirvladimiryuma123_2.14/netconf/test/support/misc-util/base64.h0000664000175000017500000000276014770023131022422 0ustar vladimirvladimir#ifndef _YUMA_TEST_BASE64_H_ #define _YUMA_TEST_BASE64_H_ #include // ---------------------------------------------------------------------------| namespace YumaTest { //------------------------------------------------------------------------------ // std::string base64_encode( const std::string& str_to_encode ); // /// @brief Encode a byte buffer as a Base64 std::string. /// /// @param bytes_to_encode Buffer of binary data to encode. /// @param len Length of binary data to encode. /// @return the base64 encoded std::string. //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // std::string base64_decode( const std::string& encoded_string ); // /// @brief Decode a base64 encoded std::string. /// /// @param encoded_std::string std::string to decode. /// @return the binary data encapsulated in a std::string type! (why not std::vector) //------------------------------------------------------------------------------ // Example //#include "base64.h" //#include // //int main() { // const std::string s = "ADP GmbH\nAnalyse Design & Programmierung\nGesellschaft mit beschränkter Haftung" ; // // std::string encoded = base64_encode(s); // std::string decoded = base64_decode(encoded); // // std::cout << "encoded: " << encoded << std::endl; // std::cout << "decoded: " << decoded << std::endl; // // return 0; //} } // YumaTest #endif /* _YUMA_TEST_BASE64_H_ */ yuma123_2.14/netconf/test/support/misc-util/array-deleter.h0000664000175000017500000000114214770023131024067 0ustar vladimirvladimir#ifndef __ARRAY_DELETER__H #define __ARRAY_DELETER__H // ---------------------------------------------------------------------------! namespace YumaTest { /** * Template class that provides a deleter that can be passed to * a shared_ptr to automatically free arrays allocated using new[]. * * \tparam T The type of array. */ template struct ArrayDeleter { /** * Call delete[] on the supplied pointer. * * \param p the pointer to call delete[] on. */ void operator()(T* p) { delete[] p; } }; } // namespace YumaTest #endif // __ARRAY_DELETER__H yuma123_2.14/netconf/test/support/misc-util/cpp-unit-op-formatter.cpp0000664000175000017500000000441714770023131026046 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/misc-util/cpp-unit-op-formatter.h" // ---------------------------------------------------------------------------| // Boost test include files. // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; using namespace boost::unit_test; // ---------------------------------------------------------------------------| // Anonymous namespace // ---------------------------------------------------------------------------| namespace { class CppUnitSuiteVisitor : public test_tree_visitor { public: explicit CppUnitSuiteVisitor( const string& name ) : name_( name ) {} virtual void visit( const test_case& tu ) { const test_results& tr = results_collector.results( tu.p_id ); cout << name_ << "::" << tu.p_name << " : " << ( tr.passed() ? "OK\n" : "FAIL\n" ); } private: string name_; }; } // anonymous namesapce // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| void CppUnitOpFormatter::do_confirmation_report( test_unit const& tu, std::ostream& ostr ) { using boost::unit_test::output::plain_report_formatter; CppUnitSuiteVisitor visitor( tu.p_name ); traverse_test_tree( tu, visitor ); const test_results& tr = results_collector.results( tu.p_id ); if( tr.passed() ) { ostr << "Test Passed\n"; } else { plain_report_formatter::do_confirmation_report( tu, ostr ); } } } // namespace YumaTest yuma123_2.14/netconf/test/support/misc-util/ptree-utils.cpp0000664000175000017500000000355714770023131024153 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/misc-util/ptree-utils.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| // Filewide namespace usage // ---------------------------------------------------------------------------| using namespace std; using boost::property_tree::ptree; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| namespace PTreeUtils { // ---------------------------------------------------------------------------| void Display( const ptree& pt) { BOOST_FOREACH( const ptree::value_type& v, pt ) { if ( v.first == "xpo" ) { cout << "Node is xpo\n"; } cout << v.first << " : " << v.second.get_value() << "\n"; Display( v.second ); } } // ---------------------------------------------------------------------------| ptree ParseXMlString( const string& xmlString ) { using namespace boost::property_tree::xml_parser; // Create empty property tree object ptree pt; istringstream ss( xmlString ); read_xml( ss, pt, trim_whitespace ); return pt; } }} // namespace YumaTest::PTreeUtils yuma123_2.14/netconf/test/support/misc-util/base64.cpp0000664000175000017500000001016214770023131022750 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "base64.h" // ---------------------------------------------------------------------------| // Standard Includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Anonymous Namespace // ---------------------------------------------------------------------------| namespace { static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; bool is_base64(unsigned char c) { return (isalnum(c) || (c == '+') || (c == '/')); } } // anonymous namespace // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| std::string base64_encode( const std::string& str ) { std::string ret; const unsigned char* bytes_to_encode = reinterpret_cast< const unsigned char* >( str.c_str() ); unsigned int in_len = str.length(); int i = 0; int j = 0; unsigned char char_array_3[3]; unsigned char char_array_4[4]; while (in_len--) { char_array_3[i++] = *(bytes_to_encode++); if (i == 3) { char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for(i = 0; (i <4) ; i++) { ret += base64_chars[char_array_4[i]]; } i = 0; } } if (i) { for(j = i; j < 3; j++) { char_array_3[j] = '\0'; } char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for (j = 0; (j < i + 1); j++) { ret += base64_chars[char_array_4[j]]; } while((i++ < 3)) { ret += '='; } } return ret; //+"\r\n"; } // ---------------------------------------------------------------------------| std::string base64_decode(std::string const& encoded_string) { int in_len = encoded_string.size(); int i = 0; int j = 0; int in_ = 0; unsigned char char_array_4[4], char_array_3[3]; std::string ret; while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { char_array_4[i++] = encoded_string[in_]; in_++; if (i ==4) { for (i = 0; i <4; i++) { char_array_4[i] = base64_chars.find(char_array_4[i]); } char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for (i = 0; (i < 3); i++) { ret += char_array_3[i]; } i = 0; } } if (i) { for (j = i; j <4; j++) { char_array_4[j] = 0; } for (j = 0; j <4; j++) { char_array_4[j] = base64_chars.find(char_array_4[j]); } char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for (j = 0; (j < i - 1); j++) { ret += char_array_3[j]; } } return ret; } } // namespace YumaTest yuma123_2.14/netconf/test/support/misc-util/log-utils.cpp0000664000175000017500000002130614770023131023605 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/misc-util/log-utils.h" // ---------------------------------------------------------------------------| // Linux includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace { /** text width for result display */ const uint8_t resultWidth(6); /** Mapping of test name to brief. */ unordered_map briefMap; // ---------------------------------------------------------------------------| /** * Get the result of the current test. * * \return true if the current test passed. */ inline bool CurrentTestPassed() { using boost::unit_test::results_collector; using boost::unit_test::framework::current_test_case; using boost::unit_test::test_case; return results_collector.results( current_test_case().p_id ).passed(); } // ---------------------------------------------------------------------------| /** * Get the name of the current test. * * \return the name of the current test. */ inline string CurrentTestName() { using boost::unit_test::results_collector; using boost::unit_test::framework::current_test_case; return current_test_case().p_name; } // ---------------------------------------------------------------------------| inline string CurrentTestSuiteName() { using boost::unit_test::results_collector; using boost::unit_test::framework::current_test_case; using boost::unit_test::framework::get; using boost::unit_test::test_suite; return get( current_test_case().p_parent_id ).p_name; } // ---------------------------------------------------------------------------| /** * Add supplied brief to the mapping between test name and test brief. * * \param brief a brief description of the currently running test. */ void AddTestBrief( const string& brief ) { using boost::unit_test::framework::current_test_case; using boost::unit_test::test_case; briefMap[ CurrentTestName() ] = brief; } // ---------------------------------------------------------------------------| /** * Get the width of the terminal * * \return the width of the terminal */ inline uint16_t GetTerminalWidth() { struct winsize w; ioctl( 0, TIOCGWINSZ, &w ); return w.ws_col; } /** * Get the width of the terminal to use for displaying text. * * \return the width of the terminal */ inline uint16_t GetParagraphWidth() { return GetTerminalWidth() - resultWidth; } // ---------------------------------------------------------------------------| /** * Split the supplied string across multiple lines. This function splits the * supplied string into words then it formats the text so that it wraps neatly * across multiple lines. The width of each line is determined by the * width of the terminal. * * \param text The text to prepare * \return a vector of strings, each one is one line of the text. */ vector SplitWorker( const string& text ) { vector lines; // allow space for result to be printed uint16_t wid = GetParagraphWidth(); if ( text.size() > wid ) { vector strs; boost::split(strs, text, boost::is_any_of( " " )); ostringstream oss; BOOST_FOREACH( string& str, strs ) { if ( oss.str().size() + str.size() > wid ) { lines.push_back( oss.str() ); oss.str(""); oss.clear(); } oss << str << " "; } lines.push_back( oss.str() ); } else { lines.push_back( text ); } return lines; } // ---------------------------------------------------------------------------| /** * Format text for display. * This function first splits the string into pre-formatted lines, * delimited by '\n'. It the calls SplitWorker to format each of those * individual lines. * * \param text The text to prepare * \return a vector of strings, each one is one line of the text. */ vector SplitText( const string& text ) { vector rawlines; boost::split( rawlines, text, boost::is_any_of( "\n" ) ); vector formattedlines; BOOST_FOREACH( string& rawline, rawlines ) { vector lines = SplitWorker( rawline ); formattedlines.insert( formattedlines.end(), lines.begin(), lines.end() ); } return formattedlines; } // ---------------------------------------------------------------------------| /** * Prepare the supplied string for display. This function first splits * the supplied string into N lines then creates a single string * containing the format text. * * \param text The text to prepare * \return a multi-line text string for display. */ string FormatText( const string& text ) { vector lines = SplitText( text ); ostringstream oss; copy( lines.begin(), lines.end(), ostream_iterator( oss, "\n" ) ); return oss.str(); } // ---------------------------------------------------------------------------| /** * Display the suplied string as formatted text. * * \param text The text to display. */ void DisplayFormattedText( const string& text ) { if ( !text.empty() ) { BOOST_TEST_MESSAGE( FormatText( text ) ); } } } // anonymous namespace // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| void DisplayTestBreak( char chr, bool force ) { const uint16_t terminalWidth = GetTerminalWidth(); string txt( terminalWidth, chr ); if ( force ) { cout << GREY_BOLD << txt << DEFAULT_TEXT; } else { BOOST_TEST_MESSAGE( GREY_BOLD << txt << DEFAULT_TEXT ); } } // ---------------------------------------------------------------------------| void DisplayTestTitle( const string& title ) { DisplayTestBreak(); BOOST_TEST_MESSAGE( HIGHLIGHT_ON << FormatText( title ) << DEFAULT_TEXT ); } // ---------------------------------------------------------------------------| void DisplayTestDescrption( const string& brief, const string& desc, const string& notes ) { AddTestBrief( brief ); DisplayTestTitle( brief ); DisplayFormattedText( desc ); DisplayFormattedText( notes ); DisplayCurrentTestSummary(); } // ---------------------------------------------------------------------------| void DisplayCurrentTestSummary( const char* highlight ) { uint16_t wid = GetParagraphWidth(); vector lines = SplitText( briefMap[ CurrentTestName() ] ); auto beg = lines.begin(); auto end = lines.end(); cout << highlight; // Display all lines except the last one copy( beg, --end, ostream_iterator( cout, "\n" ) ); cout.setf( ios::left ); cout << setw( wid ) << setfill( '.' ) << ( *end + " " ); cout << DEFAULT_TEXT; cout.flush(); } // ---------------------------------------------------------------------------| void DisplayCurrentTestId( const char* highlight ) { cout << highlight << CurrentTestSuiteName() << "/" << CurrentTestName() << " : " << DEFAULT_TEXT; } // ---------------------------------------------------------------------------| void DisplayCurrentTestResult() { cout << HIGHLIGHT_ON; if ( CurrentTestPassed() ) { cout << " PASS\n"; } else { DisplayCurrentTestSummary(); cout << RED_BOLD << " FAIL\n"; DisplayCurrentTestId( RED_BOLD ); cout << RED_BOLD << "FAILED!\n"; } cout << DEFAULT_TEXT; } } // namespace YumaTest yuma123_2.14/netconf/test/support/misc-util/ptree-utils.h0000664000175000017500000000240314770023131023605 0ustar vladimirvladimir#ifndef __PTREE_UTILS_H #define __PTREE_UTILS_H // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| namespace PTreeUtils { /** * This file contains a number of utils to aid with accessing and * manipulating boost::property trees. */ /** * Display a property tree. * this isa adebug utility thaht walks the entire property tree * printing out all entries. * * \param pt the property tree to walk. */ void Display( const boost::property_tree::ptree& pt ); /** * Populate from XML string. * Parse the xml formatted string into a property tree. * * \return A property tree; */ boost::property_tree::ptree ParseXMlString( const std::string& xmlString ); }} // namespace YumaTest::PTreeUtils #endif // __PTREE_UTILS_H yuma123_2.14/netconf/test/support/misc-util/cpp-unit-op-formatter.h0000664000175000017500000000142614770023131025510 0ustar vladimirvladimir#ifndef __CPP_UNIT_OP_FORMATTER_H #define __CPP_UNIT_OP_FORMATTER_H #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Boost test output formatter to output test results in a format that * mimics cpp unit. */ class CppUnitOpFormatter : public boost::unit_test::output::plain_report_formatter { public: /** * Overidden to provide output that is compatible with cpp unit. * * \param tu the top level test unit. * \param ostr the output stream */ virtual void do_confirmation_report( boost::unit_test::test_unit const& tu, std::ostream& ostr ); }; } // YumaTest #endif // __CPP_UNIT_OP_FORMATTER_H yuma123_2.14/netconf/test/support/misc-util/log-utils.h0000664000175000017500000000555714770023131023264 0ustar vladimirvladimir#ifndef __YUMA_BOOST_TEST_LOGGING_UTIL_H #define __YUMA_BOOST_TEST_LOGGING_UTIL_H #include #include // ---------------------------------------------------------------------------| // This file contains a number of simple wrapper functions around // BOOST_TEST_MESSAGE that provide formatted test output. // namespace YumaTest { // ---------------------------------------------------------------------------| #define USE_COLORED_PRINTING #ifdef USE_COLORED_PRINTING #define GREY_BOLD "\033[30;1m" #define RED_BOLD "\033[31;1m" #define GREEN_BOLD "\033[32;1m" #define YELLOW_BOLD "\033[33;1m" #define BLUE_BOLD "\033[34;1m" #define PURPLE_BOLD "\033[35;1m" #define CYAN_BOLD "\033[36;1m" #define WHITE_BOLD "\033[37;1m" #define BLACK_BOLD "\033[39;1m" #define BLACK_NORMAL "\033[39;0m" #else #define GREY_BOLD "" #define RED_BOLD "" #define GREEN_BOLD "" #define YELLOW_BOLD "" #define BLUE_BOLD "" #define PURPLE_BOLD "" #define CYAN_BOLD "" #define WHITE_BOLD "" #define BLACK_BOLD "" #define BLACK_NORMAL "" #endif #define HIGHLIGHT_ON BLACK_BOLD #define DEFAULT_TEXT BLACK_NORMAL // ---------------------------------------------------------------------------| /** * Utility function for simply printing a line between tests. * * \param chr the character to use in the line break * \param force true if the break should sent to std::cout, false if * BOOST_LOG_MESSAGE should be used */ void DisplayTestBreak( char chr='=', bool force=false ); // ---------------------------------------------------------------------------| /** * Utility function for displaying a test description. * * \param title the title of the test. */ void DisplayTestTitle( const std::string& title ); // ---------------------------------------------------------------------------| /** * Utility function for displaying a test description. * * \param brief the brief description for the test. * \param desc the detailed description. * \param notes some additional text */ void DisplayTestDescrption( const std::string& brief, const std::string& desc = std::string(), const std::string& notes = std::string() ); // ---------------------------------------------------------------------------| /** * Utility function to display the current test summary. */ void DisplayCurrentTestSummary( const char* highlight = HIGHLIGHT_ON ); // ---------------------------------------------------------------------------| /** * Utility function to display the current test summary. */ void DisplayCurrentTestId( const char* highlight = HIGHLIGHT_ON ); // ---------------------------------------------------------------------------| /** * Utility function to display the result of the current test. */ void DisplayCurrentTestResult(); } // namespace YumaTest #endif // __YUMA_BOOST_TEST_LOGGING_UTIL_H yuma123_2.14/netconf/test/support/db-models/0000775000175000017500000000000014770023131021120 5ustar vladimirvladimiryuma123_2.14/netconf/test/support/db-models/db-check-utils.h0000664000175000017500000000345314770023131024074 0ustar vladimirvladimir#ifndef __DB_CHECK_UTILS_H #define __DB_CHECK_UTILS_H // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| template void MapZipContainerHelper( const boost::tuple< const T&, const T& >& zIter ) { using namespace std; BOOST_CHECK_EQUAL( zIter.get<0>().first, zIter.get<1>().first ); checkEqual( zIter.get<0>().second, zIter.get<1>().second ); } // ---------------------------------------------------------------------------| template void CheckMaps( const T& lhs, const T& rhs ) { using namespace std; BOOST_REQUIRE_EQUAL( lhs.size(), rhs.size() ); auto begZip = boost::make_zip_iterator( boost::make_tuple( lhs.begin(), rhs.begin() ) ); auto endZip = boost::make_zip_iterator( boost::make_tuple( lhs.end(), rhs.end() ) ); typedef typename T::value_type value_type; #if 0 std::for_each( begZip, endZip, boost::phoenix::bind( &MapZipContainerHelper, boost::phoenix::arg_names::arg1 ) ); #endif } } // namespace YumaTest #endif // __DB_CHECK_UTILS_H yuma123_2.14/netconf/test/support/db-models/device-test-db.h0000664000175000017500000002411514770023131024073 0ustar vladimirvladimir#ifndef __DEVICE_TEST_DATABASE_H #define __DEVICE_TEST_DATABASE_H // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| struct ResourceNode; struct ConnectionItem; struct StreamItem; // ---------------------------------------------------------------------------| /* Convenience typedefs. */ typedef boost::optional opt_uint32_t; typedef boost::optional opt_string_t; // ---------------------------------------------------------------------------| struct ConnectionItem { uint32_t id_; ///< The id uint32_t sourceId_; ///< the id of the source uint32_t sourcePinId_; ///< additional source data uint32_t destinationId_; ///< the id of the destination uint32_t destinationPinId_; ///< additional destination data uint32_t bitrate_; ///< the bitrate of the connection /** Constructor */ ConnectionItem(); /** * Constructor. Populate from a parsed xml property tree. * * \param ptree the parsed xml representing the object to construct. */ explicit ConnectionItem( const boost::property_tree::ptree& pt ); /** * Unpack a property tree value. * * \param v the value to unpack * \return true if the item was unpacked. */ bool unpackItem( const boost::property_tree::ptree::value_type& v ); }; // ---------------------------------------------------------------------------| /** * Utility class to support test harness setting of Connection Items. * This class supplies optional parameters for each element in a * connectionItem. * * Example to only set the destination details and the bitrate, * use this class as follows: * * ConnectionItemConfig connCfg{ boost::optional(), * boost::optional(), * 10, * 100, * 1000 }; */ struct ConnectionItemConfig { opt_uint32_t sourceId_; ///< optional value for sourceId opt_uint32_t sourcePinId_; ///< optional value for sourcePinId opt_uint32_t destinationId_; ///< optional value for destinationId_ opt_uint32_t destinationPinId_; ///< optional value for destinationPinId_ opt_uint32_t bitrate_; ///< optional value for bitrate }; /** * Check if two ConnectionItems are equal. * * \param lhs the lhs containing expected results for comparison. * \param rhs the rhs containing expected results for comparison. */ void checkEqual( const ConnectionItem& lhs, const ConnectionItem& rhs ); // ---------------------------------------------------------------------------| struct StreamConnectionItem : public ConnectionItem { uint32_t sourceStreamId_; ///< the id of the source endpoint uint32_t destinationStreamId_; ///< the is of the destination endpoint /** Constructor */ StreamConnectionItem(); /** * Constructor. Populate from a parsed xml property tree. * * \param ptree the parsed xml representing the object to construct. */ explicit StreamConnectionItem( const boost::property_tree::ptree& pt ); }; // ---------------------------------------------------------------------------| /** * Utility class to support test harness setting of Stream Connection Items. * This class supplies optional parameters for each element in a * connectionItem. * * note: this class connot be derived from ConnectionItemConfig * because the use of inheritance prevents easy initialisation, which * defeats the purpose of the class! */ struct StreamConnectionItemConfig { opt_uint32_t sourceId_; ///< optional value for sourceId opt_uint32_t sourcePinId_; ///< optional value for sourcePinId opt_uint32_t destinationId_; ///< optional value for destinationId_ opt_uint32_t destinationPinId_; ///< optional value for destinationPinId_ opt_uint32_t bitrate_; ///< optional value for bitrate opt_uint32_t sourceStreamId_; ///< optional value for source stream Id opt_uint32_t destinationStreamId_; ///< optional value for destination stream id }; /** * Check if two ConnectionItems are equal. * * \param lhs the lhs containing expected results for comparison. * \param rhs the rhs containing expected results for comparison. */ void checkEqual( const StreamConnectionItem& lhs, const StreamConnectionItem& rhs ); // ---------------------------------------------------------------------------| struct ResourceNode { uint32_t id_; ///< The id of the resource node uint32_t channelId_; ///< The id of the channel uint32_t resourceType_; ///< the type of resource std::string configuration_; ///< binary configuration data std::string statusConfig_; ///< binary status configuration data std::string alarmConfig_; ///< binary alarm configuration data std::string physicalPath_; ///< that name of the path /** Constructor */ ResourceNode(); /** * Constructor. Populate from a parsed xml property tree. * * \param ptree the parsed xml representing the object to construct. */ explicit ResourceNode( const boost::property_tree::ptree& pt ); }; // ---------------------------------------------------------------------------| /** convenience typedef. */ typedef std::map ResourceDescriptionMap; // ---------------------------------------------------------------------------| /** * Utility class to support test harness setting of Resource Nodes. * This class supplies optional parameters for each element in a * connectionItem. * * Example to only set the destination details and the bitrate, * use this class as follows: * * ResourceNodeConfig cfg{ boost::optional(), * "physical path" }; */ struct ResourceNodeConfig { opt_uint32_t resourceType_; ///< optional value for resourceType_ opt_string_t configuration_; ///< optional value for configuration_ opt_string_t statusConfig_; ///< optional value for statusConfig_ opt_string_t alarmConfig_; ///< optional value for alarmConfig_ opt_string_t physicalPath_; ///< optional value for physicalPath_ }; /** * Check if two ResourceNodes are equal. * * \param lhs the lhs containing expected results for comparison. * \param rhs the rhs containing expected results for comparison. */ void checkEqual( const ResourceNode& lhs, const ResourceNode& rhs ); // ---------------------------------------------------------------------------| struct StreamItem { typedef std::map ResourceConnections_type; uint32_t id_; ///< the id of the profile. ResourceDescriptionMap resourceDescription_; ///< resource description details ResourceConnections_type resourceConnections_; /// resource connections /** Constructor */ StreamItem(); /** * Constructor. Populate from a parsed xml property tree. * * \param ptree the parsed xml representing the object to construct. */ explicit StreamItem( const boost::property_tree::ptree& pt ); }; /** * Check if two Profiles are equal. * * \param lhs the lhs containing expected results for comparison. * \param rhs the rhs containing expected results for comparison. */ void checkEqual( const StreamItem& lhs, const StreamItem& rhs ); // ---------------------------------------------------------------------------| struct Profile { typedef std::map< uint32_t, StreamItem > StreamsMap_type; typedef std::map< uint32_t, StreamConnectionItem > StreamConnectionMap_type; uint32_t id_; ///< the id of the profile. StreamsMap_type streams_; ///< the profiles streams StreamConnectionMap_type streamConnections_; ///< the list of stream connections. /** Constructor. */ Profile(); /** * Constructor. Populate from a parsed xml property tree. * * \param ptree the parsed xml representing the object to construct. */ explicit Profile( const boost::property_tree::ptree& pt ); }; /** * Check if two Profiles are equal. * * \param lhs the lhs containing expected results for comparison. * \param rhs the rhs containing expected results for comparison. */ void checkEqual( const Profile& lhs, const Profile& rhs ); // ---------------------------------------------------------------------------| struct XPO3Container { typedef std::map ProfilesMap_type; uint32_t activeProfile_; ///< the id of the active profile. ProfilesMap_type profiles_; ///< the list of profiles /** Constructor */ XPO3Container(); /** * Constructor. Populate from a parsed xml property tree. * * \param ptree the parsed xml representing the object to construct. * construct. */ explicit XPO3Container( const boost::property_tree::ptree& pt); /** * Clear the contents of the container. */ void clear(); }; /** * Check if two XPO3Containers are equal. * * \param lhs the lhs containing expected results for comparison. * \param rhs the rhs containing expected results for comparison. */ void checkEqual( const XPO3Container& lhs, const XPO3Container& rhs ); } // namespace YumaTest #endif // __DEVICE_TEST_DATABASE_H //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/db-models/device-test-db.cpp0000664000175000017500000003144214770023131024427 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/db-models/device-test-db.h" #include "test/support/misc-util/ptree-utils.h" #include "test/support/misc-util/base64.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include #include "test/support/db-models/db-check-utils.h" // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; using boost::property_tree::ptree; // ---------------------------------------------------------------------------| namespace YumaTest { // ===========================================================================| ConnectionItem::ConnectionItem() : id_(0) , sourceId_(0) , sourcePinId_(0) , destinationId_(0) , destinationPinId_(0) , bitrate_(0) {} // ---------------------------------------------------------------------------| bool ConnectionItem::unpackItem( const ptree::value_type& v ) { bool res = true; if ( v.first == "id" ) { id_ = v.second.get_value(); } else if ( v.first == "sourceId" ) { sourceId_ = v.second.get_value(); } else if ( v.first == "sourcePinId" ) { sourcePinId_ = v.second.get_value(); } else if ( v.first == "destinationId" ) { destinationId_ = v.second.get_value(); } else if ( v.first == "destinationPinId" ) { destinationPinId_ = v.second.get_value(); } else if ( v.first == "bitrate" ) { bitrate_ = v.second.get_value(); } else { res = false; } return res; } // ---------------------------------------------------------------------------| ConnectionItem::ConnectionItem( const boost::property_tree::ptree& pt ) : id_(0) , sourceId_(0) , sourcePinId_(0) , destinationId_(0) , destinationPinId_(0) , bitrate_(0) { BOOST_FOREACH( const ptree::value_type& v, pt ) { if ( !unpackItem( v ) ) { BOOST_FAIL( "Unsupported child for ConnectionItem: " << v.first ); } } } // ---------------------------------------------------------------------------| void checkEqual( const ConnectionItem& lhs, const ConnectionItem& rhs ) { BOOST_CHECK_EQUAL( lhs.id_, rhs.id_ ); BOOST_CHECK_EQUAL( lhs.sourceId_, rhs.sourceId_ ); BOOST_CHECK_EQUAL( lhs.sourcePinId_, rhs.sourcePinId_ ); BOOST_CHECK_EQUAL( lhs.destinationId_, rhs.destinationId_ ); BOOST_CHECK_EQUAL( lhs.destinationPinId_, rhs.destinationPinId_ ); BOOST_CHECK_EQUAL( lhs.bitrate_, rhs.bitrate_ ); } // ===========================================================================| StreamConnectionItem::StreamConnectionItem() : ConnectionItem() , sourceStreamId_(0) , destinationStreamId_(0) {} // ---------------------------------------------------------------------------| StreamConnectionItem::StreamConnectionItem( const boost::property_tree::ptree& pt ) : ConnectionItem() , sourceStreamId_(0) , destinationStreamId_(0) { BOOST_FOREACH( const ptree::value_type& v, pt ) { if ( !unpackItem( v ) ) { if ( v.first == "sourceStreamId" ) { sourceStreamId_ = v.second.get_value(); } else if ( v.first == "destinationStreamId" ) { destinationStreamId_ = v.second.get_value(); } else { BOOST_FAIL( "Unsupported child for StreamConnectionItem: " << v.first ); } } } } // ---------------------------------------------------------------------------| void checkEqual( const StreamConnectionItem& lhs, const StreamConnectionItem& rhs ) { checkEqual( static_cast( lhs ), static_cast( rhs ) ); BOOST_CHECK_EQUAL( lhs.sourceStreamId_, rhs.sourceStreamId_ ); BOOST_CHECK_EQUAL( lhs.destinationStreamId_, rhs.destinationStreamId_ ); } // ===========================================================================| ResourceNode::ResourceNode() : id_(0) , channelId_(0) , resourceType_(0) , configuration_() , statusConfig_() , alarmConfig_() , physicalPath_() {} // ---------------------------------------------------------------------------| ResourceNode::ResourceNode( const boost::property_tree::ptree& pt ) : id_(0) , channelId_(0) , resourceType_() , configuration_() , statusConfig_() , alarmConfig_() , physicalPath_() { BOOST_FOREACH( const ptree::value_type& v, pt ) { if ( v.first == "id" ) { id_ = v.second.get_value(); } else if ( v.first == "channelId" ) { channelId_ = v.second.get_value(); } else if ( v.first == "resourceType" ) { resourceType_ = v.second.get_value(); } else if ( v.first == "configuration" ) { configuration_ = base64_decode( v.second.get_value() ); } else if ( v.first == "statusConfig" ) { statusConfig_ = base64_decode( v.second.get_value() ); } else if ( v.first == "alarmConfig" ) { alarmConfig_ = base64_decode( v.second.get_value() ); } else if ( v.first == "physicalPath" ) { physicalPath_ = v.second.get_value(); } else { BOOST_FAIL( "Unsupported child for ResourceNode: " << v.first ); } } } // ---------------------------------------------------------------------------| void checkEqual( const ResourceNode& lhs, const ResourceNode& rhs ) { BOOST_CHECK_EQUAL( lhs.id_, rhs.id_ ); BOOST_CHECK_EQUAL( lhs.channelId_, rhs.channelId_ ); BOOST_CHECK_EQUAL( lhs.resourceType_, rhs.resourceType_ ); BOOST_CHECK_EQUAL( lhs.configuration_, rhs.configuration_ ); BOOST_CHECK_EQUAL( lhs.statusConfig_, rhs.statusConfig_ ); BOOST_CHECK_EQUAL( lhs.alarmConfig_, rhs.alarmConfig_ ); BOOST_CHECK_EQUAL( lhs.physicalPath_, rhs.physicalPath_ ); } // ===========================================================================| StreamItem::StreamItem() : id_(0) , resourceDescription_() , resourceConnections_() {} // ---------------------------------------------------------------------------| StreamItem::StreamItem( const boost::property_tree::ptree& pt ) : id_(0) , resourceDescription_() , resourceConnections_() { BOOST_FOREACH( const ptree::value_type& v, pt ) { if ( v.first == "id" ) { id_ = v.second.get_value(); } else if ( v.first == "resourceNode" ) { ResourceNode res( v.second ); BOOST_REQUIRE_MESSAGE( resourceDescription_.end() == resourceDescription_.find( res.id_ ), "ERROR Duplicate Resource found! stream ( " << id_ << ") resource ( " << res.id_ << " )" ); resourceDescription_.insert( make_pair( res.id_, res ) ); } else if ( v.first == "resourceConnection" ) { YumaTest::ConnectionItem conn( v.second ); BOOST_REQUIRE_MESSAGE( resourceConnections_.end() == resourceConnections_.find( conn.id_ ), "ERROR Duplicate connection item found! ID: " << conn.id_); resourceConnections_.insert( make_pair( conn.id_, conn ) ); } else { BOOST_FAIL( "Unsupported child for stream: " << v.first ); } } } // ---------------------------------------------------------------------------| void checkEqual( const StreamItem& lhs, const StreamItem& rhs ) { BOOST_TEST_MESSAGE( "Checking StreamItem match..." ); BOOST_TEST_MESSAGE( "Comparing Stream Id ... " << lhs.id_ ); BOOST_CHECK_EQUAL( lhs.id_, rhs.id_ ); BOOST_TEST_MESSAGE( "Comparing Stream Resource Descriptions ..." ); CheckMaps( lhs.resourceDescription_, rhs.resourceDescription_ ); BOOST_TEST_MESSAGE( "Comparing Stream Virtual Connection Resources ..." ); CheckMaps( lhs.resourceConnections_, rhs.resourceConnections_ ); } // ===========================================================================| Profile::Profile() : id_(0) , streams_() , streamConnections_() {} // ===========================================================================| Profile::Profile( const boost::property_tree::ptree& pt) : id_(0) , streams_() , streamConnections_() { BOOST_FOREACH( const ptree::value_type& v, pt ) { if ( v.first == "id" ) { id_ = v.second.get_value(); } else if ( v.first == "stream" ) { StreamItem str( v.second ); BOOST_REQUIRE_MESSAGE( streams_.end() == streams_.find( str.id_ ), "ERROR Duplicate Stream found! profile ( " << id_ << ") stream ( " << str.id_ << " )" ); streams_.insert( make_pair( str.id_, str ) ); } else if ( v.first == "streamConnection" ) { YumaTest::StreamConnectionItem conn( v.second ); BOOST_REQUIRE_MESSAGE( streamConnections_.end() == streamConnections_.find( conn.id_ ), "ERROR Duplicate stream connection item found! ID: " << conn.id_); streamConnections_.insert( make_pair( conn.id_, conn ) ); } else { BOOST_FAIL( "Unsupported child for profile: " << v.first ); } } } // ---------------------------------------------------------------------------| void checkEqual( const Profile& lhs, const Profile& rhs ) { BOOST_TEST_MESSAGE( "Checking Profile match..." ); BOOST_TEST_MESSAGE( "Comparing Profile Id ..." << lhs.id_ ); BOOST_CHECK_EQUAL( lhs.id_, rhs.id_ ); BOOST_TEST_MESSAGE( "Comparing Streams List ..." ); CheckMaps( lhs.streams_, rhs.streams_ ); BOOST_TEST_MESSAGE( "Comparing Stream Connections List ..." ); CheckMaps( lhs.streamConnections_, rhs.streamConnections_ ); } // ===========================================================================| XPO3Container::XPO3Container() : activeProfile_(0) , profiles_() {} // ---------------------------------------------------------------------------| XPO3Container::XPO3Container( const ptree& pt) : activeProfile_(0) , profiles_() { const ptree& xpo = pt.get_child( "data.xpo", ptree() ); BOOST_FOREACH( const ptree::value_type& v, xpo ) { if ( v.first == "profile" ) { Profile prof( v.second ); BOOST_REQUIRE_MESSAGE( profiles_.end() == profiles_.find( prof.id_ ), "ERROR Duplicate Profile with id( " << prof.id_ << " )\n" ); profiles_.insert( std::make_pair( prof.id_, prof ) ); } else if ( v.first == "activeProfile" ) { activeProfile_ = v.second.get_value(); } else if ( !boost::starts_with( v.first, " #include #include // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| struct Component; // ---------------------------------------------------------------------------| /* Convenience typedefs. */ typedef boost::optional opt_string_t; // ---------------------------------------------------------------------------| struct ToasterContainer { std::string toasterManufacturer_; std::string toasterModelNumber_; std::string toasterStatus_; /** Constructor */ ToasterContainer(); /** * Constructor. Populate from a parsed xml property tree. * * \param ptree the parsed xml representing the object to construct. */ explicit ToasterContainer( const boost::property_tree::ptree& pt ); /** * Unpack a property tree value. * * \param v the value to unpack * \return true if the item was unpacked. */ bool unpackItem( const boost::property_tree::ptree::value_type& v ); /** * Clear the contents of the container. */ // void clear(); }; // ---------------------------------------------------------------------------| /** * Utility class to support test harness setting of ToasterContainers. * This class supplies optional parameters for each element in a * Toaster container. * * Example to only set the toaster manufacturer details, * use this class as follows: * * ToasterContainerConfig connCfg{ string ( "Acme" ), * boost::optional(), boost::optional() }; */ struct ToasterContainerConfig { opt_string_t toasterManufacturer_; ///< optional value for toaster manufacturer opt_string_t toasterModelNumber_; ///< optional value for toaster model number opt_string_t toasterStatus_; ///< optional value for toaster status }; /** * Check if two ToasterContainers are equal. * * \param lhs the lhs containing expected results for comparison. * \param rhs the rhs containing expected results for comparison. */ void checkEqual( const ToasterContainer& lhs, const ToasterContainer& rhs ); } // namespace YumaTest #endif // __STATE_DATA_TEST_DATABASE_H //------------------------------------------------------------------------------ // End of file //------------------------------------------------------------------------------ yuma123_2.14/netconf/test/support/db-models/device-test-db-debug.cpp0000664000175000017500000001234314770023131025512 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/db-models/device-test-db-debug.h" #include "test/support/db-models/device-test-db.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace { static uint8_t indent; } // anonymous namespace // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| ostream& operator<<( ostream& o, const StreamConnectionItem& it ) { o << setw(indent) << setfill( ' ' ) << "StreamConnectionItem = { id_ = " << it.id_ << ", sourceId=" << it.sourceId_ << ", sourcePinId_=" << it.sourcePinId_ << ", destinationId_=" << it.destinationId_ << ", destinationPinId_=" << it.destinationPinId_ << ", bitrate_=" << it.bitrate_ << ", sourceStreamId_=" << it.sourceStreamId_ << ", destinationStreamId_=" << it.destinationStreamId_ << " }\n"; return o; } // ---------------------------------------------------------------------------| ostream& operator<<( ostream& o, const ConnectionItem& it ) { o << setw(indent) << setfill( ' ' ) << "ConnectionItem = { id_ = " << it.id_ << ", sourceId=" << it.sourceId_ << ", sourcePinId_=" << it.sourcePinId_ << ", destinationId_=" << it.destinationId_ << ", destinationPinId_=" << it.destinationPinId_ << ", bitrate_=" << it.bitrate_ << " }\n"; return o; } // ---------------------------------------------------------------------------| ostream& operator<<( ostream& o, const ResourceNode& it ) { o << setw(indent) << setfill( ' ' ) << "ResourceNode = { id_ = " << it.id_ << ", channelId_=" << it.channelId_ << ", resourceType=" << it.resourceType_ << ", configuration=" << it.configuration_ << ", alarmConfig=" << it.alarmConfig_ << ", statusConfig=" << it.statusConfig_ << ", physicalPath_=" << it.physicalPath_ << " }\n"; return o; } // ---------------------------------------------------------------------------| ostream& operator<<( ostream& o, const StreamItem& it ) { o << setw(indent) << setfill( ' ' ) << "StreamItem = { id_ = " << it.id_ << "\n"; indent +=2; BOOST_FOREACH( const ResourceDescriptionMap::value_type& val, it.resourceDescription_ ) { o << val.second; } BOOST_FOREACH( const StreamItem::ResourceConnections_type::value_type& val, it.resourceConnections_ ) { o << val.second; } indent -=2; o << setw(indent) << setfill( ' ' ) << " }\n"; return o; } // ---------------------------------------------------------------------------| ostream& operator<<( ostream& o, const Profile& it ) { o << setw(indent) << setfill( ' ' ) << "Profile = { id_ = " << it.id_ << "\n"; indent +=2; BOOST_FOREACH( const Profile::StreamsMap_type::value_type& val, it.streams_ ) { o << val.second; } BOOST_FOREACH( const Profile::StreamConnectionMap_type::value_type& val, it.streamConnections_ ) { o << val.second; } indent -=2; o << setw(indent) << setfill( ' ' ) << " }\n"; return o; } // ---------------------------------------------------------------------------| ostream& operator<<( ostream& o, const XPO3Container& it ) { o << setw(indent) << setfill( ' ' ) << "XPO3Container = { activeProfile=" << it.activeProfile_ << "\n"; indent +=2; BOOST_FOREACH( const XPO3Container::ProfilesMap_type::value_type& val, it.profiles_ ) { o << val.second; } indent -=2; o << setw(indent) << setfill( ' ' ) << " }\n"; return o; } } // namespace XPO3NetconfIntegrationTest yuma123_2.14/netconf/test/support/db-models/device-test-db-debug.h0000664000175000017500000000345114770023131025157 0ustar vladimirvladimir#ifndef __DEVICE_TEST_DATABASE_DEBUG_H #define __DEVICE_TEST_DATABASE_DEBUG_H // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| struct ResourceNode; struct ConnectionItem; struct StreamItem; struct StreamConnectionItem; struct Profile; struct XPO3Container; /** * Display the contents of a StreamConnectionItem. * * \param out the output stream. * \param it the item to display. */ std::ostream& operator<<( std::ostream& o, const StreamConnectionItem& it ); /** * Display the contents of a StreamConnectionItem. * * \param out the output stream. * \param it the item to display. */ std::ostream& operator<<( std::ostream& o, const ConnectionItem& it ); /** * Display the contents of a ConnectionItem. * * \param out the output stream. * \param it the item to display. */ std::ostream& operator<<( std::ostream& o, const ResourceNode& it ); /** * Display the contents of a ResourceNode. * * \param out the output stream. * \param it the item to display. */ std::ostream& operator<<( std::ostream& o, const StreamItem& it ); /** * Display the contents of a StreamItem. * * \param out the output stream. * \param it the item to display. */ std::ostream& operator<<( std::ostream& o, const Profile& it ); /** * Display the contents of a XPO3Container. * * \param out the output stream. * \param it the item to display. */ std::ostream& operator<<( std::ostream& o, const XPO3Container& it ); } // namespace YumaTest #endif // __DEVICE_TEST_DATABASE_DEBUG_H yuma123_2.14/netconf/test/support/db-models/state-data-test-db.cpp0000664000175000017500000000601114770023131025211 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/db-models/state-data-test-db.h" #include "test/support/misc-util/ptree-utils.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; using boost::property_tree::ptree; // ---------------------------------------------------------------------------| namespace YumaTest { // ===========================================================================| ToasterContainer::ToasterContainer() : toasterManufacturer_("") , toasterModelNumber_("") , toasterStatus_("") {} // ---------------------------------------------------------------------------| bool ToasterContainer::unpackItem( const ptree::value_type& v ) { bool res = true; if ( v.first == "toasterManufacturer" ) { toasterManufacturer_ = v.second.get_value(); } else if ( v.first == "toasterModelNumber" ) { toasterModelNumber_ = v.second.get_value(); } else if ( v.first == "toasterStatus" ) { toasterStatus_ = v.second.get_value(); } else { res = false; } return res; } // ---------------------------------------------------------------------------| ToasterContainer::ToasterContainer( const boost::property_tree::ptree& pt ) : toasterManufacturer_("") , toasterModelNumber_("") , toasterStatus_("") { const ptree& toaster = pt.get_child( "data.toaster", ptree() ); BOOST_FOREACH( const ptree::value_type& v, toaster ) { if ( !unpackItem( v ) && v.first != "") { BOOST_FAIL( "Unsupported child for Component: " << v.first ); } } } // ---------------------------------------------------------------------------| void checkEqual( const ToasterContainer& lhs, const ToasterContainer& rhs ) { BOOST_CHECK_EQUAL( lhs.toasterManufacturer_, rhs.toasterManufacturer_ ); BOOST_CHECK_EQUAL( lhs.toasterModelNumber_, rhs.toasterModelNumber_ ); BOOST_CHECK_EQUAL( lhs.toasterStatus_, rhs.toasterStatus_ ); } } // namespace ToasterNetconfIntegrationTest yuma123_2.14/netconf/test/support/msg-util/0000775000175000017500000000000014770023131021013 5ustar vladimirvladimiryuma123_2.14/netconf/test/support/msg-util/xpo-query-builder.cpp0000664000175000017500000002231514770023131025117 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/msg-util/xpo-query-builder.h" #include "test/support/misc-util/base64.h" // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| namespace YumaTest { // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; using namespace YumaTest; // ---------------------------------------------------------------------------| XPOQueryBuilder::XPOQueryBuilder( const string& moduleNs ) : NCMessageBuilder() , moduleNs_( moduleNs ) { } // ---------------------------------------------------------------------------| XPOQueryBuilder::~XPOQueryBuilder() { } // ---------------------------------------------------------------------------| string XPOQueryBuilder::addProfileNodePath( const uint16_t profileId, const string& queryText, const string& op ) const { // add the profile path string query = genKeyParentPathText( "profile", "id", boost::lexical_cast( profileId ) , queryText, op ); return genModuleOperationText( "xpo", moduleNs_, query ); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::addStreamConnectionPath( const uint16_t profileId, const uint16_t connectionId, const string& queryText, const string& op ) const { // add the profile path string query = genKeyParentPathText( "streamConnection", "id", boost::lexical_cast( connectionId ) , queryText, op ); return addProfileNodePath( profileId, query ); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::addProfileStreamNodePath( const uint16_t profileId, const uint16_t streamId, const string& queryText, const string& op ) const { // add the stream path string query = genKeyParentPathText( "stream", "id", boost::lexical_cast( streamId ) , queryText, op ); // add the profile path return addProfileNodePath( profileId, query ); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::addResourceNodePath( const uint16_t profileId, const uint16_t streamId, const uint16_t resourceId, const string& queryText, const string& op ) const { // add the resource path string query = genKeyParentPathText( "resourceNode", "id", boost::lexical_cast( resourceId ) , queryText, op ); // add the profile and stream path return addProfileStreamNodePath( profileId, streamId, query ); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::addVRConnectionNodePath( const uint16_t profileId, const uint16_t streamId, const uint16_t connectionId, const string& queryText, const string& op ) const { // add the resource path string query = genKeyParentPathText( "resourceConnection", "id", boost::lexical_cast( connectionId ) , queryText, op ); // add the profile and stream path return addProfileStreamNodePath( profileId, streamId, query ); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::genXPOQuery( const string& op ) const { return genTopLevelContainerText( "xpo", moduleNs_, op ); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::genSetActiveProfileIdQuery( const uint16_t profileId, const string& op ) const { string query = genOperationText( "activeProfile", profileId, op ); return genModuleOperationText( "xpo", moduleNs_, query ); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::genProfileQuery( const uint16_t profileId, const string& op ) const { // build the operation string query = genKeyOperationText( "profile", "id", boost::lexical_cast( profileId ), op ); // add the module info return genModuleOperationText( "xpo", moduleNs_, query ); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::genStreamConnectionQuery( const uint16_t profileId, const uint16_t streamId, const string& op ) const { // build the operation string query = genKeyOperationText( "streamConnection", "id", boost::lexical_cast( streamId ), op ); // add the module info return addProfileNodePath( profileId, query ); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::genProfileStreamItemQuery( const uint16_t profileId, const uint16_t streamId, const string& op ) const { // build the operation string query = genKeyOperationText( "stream", "id", boost::lexical_cast( streamId ), op ); // add the path return addProfileNodePath( profileId, query ); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::genProfileChildQuery( const uint16_t profileId, const uint16_t streamId, const string& nodeName, const uint16_t nodeId, const string& op ) const { // build the core operation string query = genKeyOperationText( nodeName, "id", boost::lexical_cast( nodeId ), op ); // add the stream path return addProfileStreamNodePath( profileId, streamId, query ); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::configureVResourceNode( const ResourceNodeConfig& config, const string& op ) const { ostringstream oss; if ( config.resourceType_ ) { oss << genOperationText( "resourceType", *config.resourceType_, op ); } if ( config.physicalPath_ ) { oss << genOperationText( "physicalPath", *config.physicalPath_, op ); } if ( config.configuration_ ) { oss << genOperationText( "configuration", base64_encode( *config.configuration_ ), op ); } if ( config.statusConfig_ ) { oss << genOperationText( "statusConfig", base64_encode( *config.statusConfig_ ), op ); } if ( config.alarmConfig_ ) { oss << genOperationText( "alarmConfig", base64_encode( *config.alarmConfig_ ), op ); } return oss.str(); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::configureResourceConnection( const ConnectionItemConfig& config, const string& op ) const { return configureConnectionItem( config, op ); } // ---------------------------------------------------------------------------| template string XPOQueryBuilder::configureConnectionItem( const T& config, const string& op ) const { ostringstream oss; if ( config.sourceId_ ) { oss << genOperationText( "sourceId", *config.sourceId_, op ); } if ( config.sourcePinId_ ) { oss << genOperationText( "sourcePinId", *config.sourcePinId_, op ); } if ( config.destinationId_ ) { oss << genOperationText( "destinationId", *config.destinationId_, op ); } if ( config.destinationPinId_ ) { oss << genOperationText( "destinationPinId", *config.destinationPinId_, op ); } if ( config.bitrate_ ) { oss << genOperationText( "bitrate", *config.bitrate_, op ); } return oss.str(); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::configureStreamConnection( const StreamConnectionItemConfig& config, const string& op ) const { ostringstream oss; oss << configureConnectionItem( config, op ); if ( config.sourceStreamId_ ) { oss << genOperationText( "sourceStreamId", *config.sourceStreamId_, op ); } if ( config.destinationStreamId_ ) { oss << genOperationText( "destinationStreamId", *config.destinationStreamId_, op ); } return oss.str(); } // ---------------------------------------------------------------------------| string XPOQueryBuilder::buildCustomRPC( const string& rpcName, const string& params ) const { ostringstream oss; oss << "<" << rpcName << " " << genXmlNsText( moduleNs_ ); if ( params.empty() ) { oss << " />" ; } else { oss << ">\n" << params; oss << ""; } return oss.str(); } } // namespace YumaTest yuma123_2.14/netconf/test/support/msg-util/NCMessageBuilder.h0000664000175000017500000004153114770023131024304 0ustar vladimirvladimir#ifndef __YUMA_NC_MESSAGE_BUILDER__H #define __YUMA_NC_MESSAGE_BUILDER__H #include #include #include namespace YumaTest { // ---------------------------------------------------------------------------! /** * Utility class for building Netconf XML Messages. */ class NCMessageBuilder { public: /** Constructor */ explicit NCMessageBuilder(); /** Destructor */ virtual ~NCMessageBuilder(); /** * Build aNetconf Message. This function simlpy adds the XML * start tag and NC_SSH_END indication to the supplied message. * * \param message the message to build. * \return a valid Netconf Message. */ std::string buildNCMessage( const std::string& message ) const; /** * Build a Netconf 'hello' message. * * \return a Netconf 'hello' Message. */ std::string buildHelloMessage() const; /** * Build a Netconf 'rpc' message. * * \param queryStr the RPC body * \param messageId the id of the message * \return a Netconf 'rpc' Message. */ std::string buildRPCMessage( const std::string& queryStr, uint16_t messageId ) const; /** * Build a commit message. * * \param messageId the id of the message * \return a Netconf 'rpc-commit' Message. */ std::string buildCommitMessage( const uint16_t messageId ); /** * Build a confirmed-commit message. * * \param timeout the confirm-timeout of the message in seconds * \param messageId the id of the message * \return a Netconf 'rpc-commit' Message. */ std::string buildConfirmedCommitMessage( const int timeout, const uint16_t messageId ); /** * Build a Netconf 'load' message. * * \param moduleName the name of the module to load. * \param messageId the id of the message * \return a Netconf 'load' Message. */ std::string buildLoadMessage( const std::string& moduleName, uint16_t messageId ) const; /** * Build a Netconf 'get-config' message * * \param filter the filter to apply in the format: * \. * \param target the target database name * \param messageId the id of the message * \return a Netconf 'get-config' Message. */ std::string buildGetConfigMessage( const std::string& filter, const std::string& target, uint16_t messageId ) const; /** * Build a Netconf 'get-config' message with an XPath filter. * * \param xPathStr the XPath filter to apply * \param target the target database name * \param messageId the id of the message * \return a Netconf 'get-config' Message. * */ std::string buildGetConfigMessageXPath( const std::string& xPathStr, const std::string& target, uint16_t messageId ) const; /** * Build a Netconf 'get-config' message with a Subtree filter. * * \param subtreeStr the Subtree filter to apply * \param target the target database name * \param messageId the id of the message * \return a Netconf 'get-config' Message. * */ std::string buildGetConfigMessageSubtree( const std::string& subtreeStr, const std::string& target, uint16_t messageId ) const; /** * Build a Netconf 'get' message * * \param filter the filter to apply in the format: * \. * \param target the target database name * \param messageId the id of the message * \return a Netconf 'get' Message. */ std::string buildGetMessage( const std::string& filter, const std::string& target, uint16_t messageId ) const; /** * Build a Netconf 'get' message with an XPath filter. * * \param xPathStr the XPath filter to apply * \param target the target database name * \param messageId the id of the message * \return a Netconf 'get' Message. * */ std::string buildGetMessageXPath( const std::string& xPathStr, const std::string& target, uint16_t messageId ) const; /** * Build a Netconf 'get' message with a Subtree filter. * * \param subtreeStr the Subtree filter to apply * \param target the target database name * \param messageId the id of the message * \return a Netconf 'get-config' Message. * */ std::string buildGetMessageSubtree( const std::string& subtreeStr, const std::string& target, uint16_t messageId ) const; /** * Build a Netconf 'lock' message. * * \param messageId the id of the message * \param target the target database name * \return a Netconf 'lock' Message. */ std::string buildLockMessage( uint16_t messageId, const std::string& target ) const; /** * Build a Netconf 'unlock' message. * * \param messageId the id of the message * \param target the target database name * \return a Netconf 'unlock' Message. */ std::string buildUnlockMessage( uint16_t messageId, const std::string& target ) const; /** * Build a Netconf 'validate' message. * * \param messageId the id of the message * \param source the source database name * \return a Netconf 'validate' Message. */ std::string buildValidateMessage( uint16_t messageId, const std::string& source ) const; /** * Build a Netconf 'set-log-level' message. * The following Log Levels are supported: *
    *
  • off
  • *
  • error
  • *
  • warn
  • *
  • info
  • *
  • debug
  • *
  • debug2
  • *
  • debug3
  • *
  • debug4
  • *
* * \param logLevelStr the log level to set to lock * \param messageId the id of the message * \return a Netconf 'set-log-level' Message. */ std::string buildSetLogLevelMessage( const std::string& logLevelStr, uint16_t messageId ) const; /** * Build a Netconf 'edit-config' message. * * \param configChange the configuration change * \param target the target database name * \param messageId the id of the message * \return a Netconf 'edit-config' message. */ std::string buildEditConfigMessage( const std::string& configChange, const std::string& target, uint16_t messageId ) const; /** * Build a Netconf 'copy-config' message. * * \param target the target database name * \param source the source database name * \param messageId the id of the message * \return a Netconf 'copy-config' message. */ std::string buildCopyConfigMessage( const std::string& target, const std::string& source, uint16_t messageId ) const; /** * Build a Netconf 'delete-config' message. * * \param target the target database name * \param messageId the id of the message * \return a Netconf 'delete-config' message. */ std::string buildDeleteConfigMessage( const std::string& target, uint16_t messageId ) const; /** * Build a Netconf 'discard-changes' message. * * \param messageId the id of the message * \return a Netconf 'edit-config' message. */ std::string buildDiscardChangesMessage( uint16_t messageId ) const; /** * Build a Netconf 'get-my-session' message. * * \param messageId the id of the message * \return a Netconf 'edit-config' message. */ std::string buildGetMySessionMessage( uint16_t messageId ) const; /** * Build a Netconf 'set-my-session' message. * * \param indent the indent to be set. * \param linesize the linesize to be set. * \param withDefaults the with-defaults setting to be set. * \param messageId the id of the message * \return a Netconf 'edit-config' message. */ std::string buildSetMySessionMessage( const std::string& indent, const std::string& linesize, const std::string& withDefaults, uint16_t messageId ) const; /** * Build a Netconf 'kill-session' message. * * \param sessionId the id of the session to be killed * \param messageId the id of the message * \return a Netconf 'kill-session' message. */ std::string buildKillSessionMessage( uint16_t sessionId, uint16_t messageId ) const; /** * Build a Netconf 'close-session' message. * * \param messageId the id of the message * \return a Netconf 'close-session' message. */ std::string buildCloseSessionMessage( uint16_t messageId ) const; /** * Build a Netconf 'shutdown' message. * * \param messageId the id of the message * \return a Netconf 'shutdown' message. */ std::string buildShutdownMessage( uint16_t messageId ) const; /** * Build a Netconf 'restart' message. * * \param messageId the id of the message * \return a Netconf 'restart' message. */ std::string buildRestartMessage( uint16_t messageId ) const; /** * Build a Netconf 'get-schema' message. * * \param messageId the id of the message * \return a Netconf 'get-schema' message. */ std::string buildGetSchemaMessage( const std::string& schemaId, uint16_t messageId ) const; /** * Set the default edit operation. * * \param defaultOperation the new default operation */ void setDefaultOperation( const std::string& defaultOperation ); /** * Set the default edit operation. * * \param defaultOperation the new default operation */ const std::string getDefaultOperation() const; /** * Utility function for generating text for 'edit-config' operations. * * \param moduleName the name of the module * \param moduleNs the namespace of the module * \param operation the operation to perform * \return a std::string containing the create text */ std::string genTopLevelContainerText( const std::string& moduleName, const std::string& moduleNs, const std::string& operation ) const; /** * Generating text for interface container 'edit-config' operations. * * \param moduleNs the namespace of the module * \param type the type of the interface (ethernet or atm) * \param MTU the MTU of the interface * \param operation the operation to perform * \return a std::string containing the create text */ std::string genInterfaceContainerText( const std::string& moduleNs, const std::string& type, int mtu, const std::string& operation ) const; /** * Generate XML text for an operation on a node. * This function generates XML in the following form: * * \ * nodeVal * \ * * \param nodeName the name of the node * \param nodeVal the new value of the node * \param operation the operation to perform * \return a string containing the xml formatted query */ std::string genOperationText( const std::string& nodeName, const std::string& nodeVal, const std::string& operation ) const; /** * Utility template function for generating operation text. * * \param nodeName the name of the node * \param nodeVal the new value of the node * \param operation the operation to perform * \return a string containing the xml formatted query */ std::string genOperationText( const std::string& nodeName, bool nodeVal, const std::string& operation ) const { return genOperationText( nodeName, ( nodeVal ? "true" : "false" ), operation ); } /** * Utility template function for generating operation text. * * \param nodeName the name of the node * \param nodeVal the new value of the node * \param operation the operation to perform * \return a string containing the xml formatted query */ template std::string genOperationText( const std::string& nodeName, const T& nodeVal, const std::string& operation ) const { return genOperationText( nodeName, boost::lexical_cast( nodeVal ), operation ); } /** * Generate XML text for an operation on a node. * This function generates XML in the following form: * * \ * \keyVal\ * \ * * \param nodeName the name of the node * \param keyName the name of the key for the node * \param keyVal the new value of the key * \param operation the operation to perform * \return a string containing the xml formatted query */ std::string genKeyOperationText( const std::string& nodeName, const std::string& keyName, const std::string& keyVal, const std::string& operation ) const; /** * Utility function for formatting module operation text. * This formats the supplied text as follows: * \ queryText \ * * \param moduleName the name of the module * \param moduleNs the namespace of the module * \param queryText the query to format * \return a string containing the xml formatted query */ std::string genModuleOperationText( const std::string& moduleName, const std::string& moduleNs, const std::string& queryText ) const; /** * Utility function for formatting a 'path' around the supplied * operation text. * This formats the supplied text as follows: * \ * \keyVal\ * queryText * \ * * \param nodeName the name of the parent node * \param keyName the name of the key for the node * \param keyVal the new value of the key * \param queryText the query to format * \param op the operation text. * \return a string containing the xml formatted query */ std::string genKeyParentPathText( const std::string& nodeName, const std::string& keyName, const std::string& keyVal, const std::string& queryText, const std::string& op="" ) const; protected: /** * Utility function for generating node text. * * \param nodeName the name of the parent node * \param op the operation text. * \return a string containing the xml formatted node * ( note this does not include the closing '>' ) */ std::string genNodeText( const std::string& nodeName, const std::string& op ) const; /** * Utility function for generating xmlns text. * * \param xmlnsArg the argument string (e.g. xmlns or xmlns:nc ) * \param ns the namespace string */ std::string genXmlNsText( const std::string& xmlnsArg, const std::string& ns ) const; /** * Utility function for generating xmlns text. * * \param ns the namespace string */ std::string genXmlNsText( const std::string& ns ) const; /** * Utility function for generating xmlns:nc text. * * \param ns the namespace string */ std::string genXmlNsNcText( const std::string& ns ) const; protected: std::string defaultOperation_; ///< The default edit operation }; } // namespace YumaTest #endif // __YUMA_NC_MESSAGE_BUILDER__H yuma123_2.14/netconf/test/support/msg-util/state-data-query-builder.h0000664000175000017500000000266314770023131026011 0ustar vladimirvladimir#ifndef __STATE_DATA_QUERY_BUILDER_H #define __STATE_DATA_QUERY_BUILDER_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/msg-util/NCMessageBuilder.h" #include "test/support/db-models/state-data-test-db.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Utility lass for build queries against the device test whole * container. */ class StateDataQueryBuilder : public NCMessageBuilder { public: /** * Constructor. * * \param moduleNs the namespace of the module defining the XPO * container. */ explicit StateDataQueryBuilder( const std::string& moduleNS ); /** Destructor */ virtual ~StateDataQueryBuilder(); /** * Generate a query on the basic toaster container. * * \param op the operation to perform. * \return XML formatted query */ std::string genToasterQuery( const std::string& op ) const; private: const std::string moduleNs_; ///< the module's namespace }; } // namespace YumaTest #endif // __STATE_DATA_QUERY_BUILDER_H yuma123_2.14/netconf/test/support/msg-util/xpo-query-builder.h0000664000175000017500000002227314770023131024567 0ustar vladimirvladimir #ifndef __XPO_QUERY_BUILDER_H #define __XPO_QUERY_BUILDER_H // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/msg-util/NCMessageBuilder.h" #include "test/support/db-models/device-test-db.h" // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Utility lass for build queries against the device test XPO * container. */ class XPOQueryBuilder : public NCMessageBuilder { public: /** * Constructor. * * \param moduleNs the namespace of the module defining the XPO * container. */ explicit XPOQueryBuilder( const std::string& moduleNS ); /** Destructor */ virtual ~XPOQueryBuilder(); /** * Add the profile node path and the xpo module to the XML query. * * \param profileId the id of the profile * \param queryText the query to add the path to. * \param op option operation type. * \return the formatted xml query, */ std::string addProfileNodePath( uint16_t profileId, const std::string& queryText, const std::string& op="" ) const; /** * Add the path to the resource node (profile.stream.resource) * * \param profileId the of the owning profile. * \param streamId the of the stream to create. * \param resourceId the id of the connection node to create. * \param queryText the resource query to add the path to. * \param op option operation type. * \return XML formatted query */ std::string addProfileStreamNodePath( uint16_t profileId, uint16_t streamId, const std::string& queryText, const std::string& op="" ) const; /** * Add the path to the stream connection (streamConnection) * * \param profileId the of the owning profile. * \param streamId the of the stream to create. * \param queryText the resource query to add the path to. * \param op option operation type. * \return XML formatted query */ std::string addStreamConnectionPath( uint16_t profileId, uint16_t connectionId, const std::string& queryText, const std::string& op="" ) const; /** * Add the path to the resource node (profile.stream.resource). * * \param profileId the id of the owning profile. * \param streamId the id of the owning stream. * \param resourceId the id of the resource node. * \param queryText the resource query to add the path to. * \param op option operation type. * \return XML formatted query */ std::string addResourceNodePath( uint16_t profileId, uint16_t streamId, uint16_t resourceId, const std::string& queryText, const std::string& op="" ) const; /** * Add the path to the VR connection node * (profile.stream.connection). * * \param profileId the id of the owning profile. * \param streamId the id of the owning stream. * \param connectionId the id of the connection node. * \param queryText the resource query to add the path to. * \param op option operation type. * \return XML formatted query */ std::string addVRConnectionNodePath( uint16_t profileId, uint16_t streamId, uint16_t connectionId, const std::string& queryText, const std::string& op="" ) const; /** * Generate a query on the basic XPO container. * * \param op the operation to perform. * \return XML formatted query */ std::string genXPOQuery( const std::string& op ) const; /** * Generate a query to set the active profile id. * * \param profileId the id of the new active profile * \param op the operation to perform. * \return XML formatted query */ std::string genSetActiveProfileIdQuery( uint16_t profileId, const std::string& op ) const; /** * Generate a query for a specific profile. * * \param profileId the id of the profile * \param op the operation to perform. * \return XML formatted query */ std::string genProfileQuery( uint16_t profileId, const std::string& op ) const; /** * Generate a query for a specific stream item in a profile. * * \param profileId the id of the profile * \param streamId the id of the stream * \param op the operation to perform. * \return XML formatted query */ std::string genProfileChildQuery( uint16_t profileId, uint16_t streamId, const std::string& op ) const; /** * Generate a query for a child node of profile. * * \param profileId the of the owning profile. * \param streamId the of the stream to create. * \param nodeName the name of the child node to create. * \param nodeId the id of the child to create. * \param resourceNodeId the id of the resource node to create. * \return XML formatted query */ std::string genProfileChildQuery( uint16_t profileId, uint16_t streamId, const std::string& nodeName, uint16_t nodeId, const std::string& op ) const; /** * Configure a resource connection item. * This function generates the complete configuration for a * ConnectionItem, items are configured depending on whether or * not they have been set in the supplied ConnectionItemConfig. * * \param config the configuration to generate * \param op the operation being performed * \return XML representation of the data being configured. */ std::string configureResourceConnection( const YumaTest::ConnectionItemConfig& config, const std::string& op ) const; /** * Configure a stream connection item. * This function generates the complete configuration for a * ConnectionItem, items are configured depending on whether or * not they have been set in the supplied StreamConnectionItemConfig. * * \param config the configuration to generate * \param op the operation being performed * \return XML representation of the data being configured. */ std::string configureStreamConnection( const YumaTest::StreamConnectionItemConfig& config, const std::string& op ) const; /** * Configure a Virtual resource item. * This function generates the complete configuration for a * ResourceNode, items are configured depending on whether or * not they have been set in the supplied ResourceNodeConfig. * * \param config the configuration to generate * \param op the operation being performed * \return XML representation of the data being configured. */ std::string configureVResourceNode( const YumaTest::ResourceNodeConfig& config, const std::string& op ) const; /** * Generate a query for a specific stream. * * \param profileId the id of the stream * \param streamId the id of the stream * \param op the operation to perform. * \return XML formatted query */ std::string genStreamConnectionQuery( uint16_t profileId, uint16_t streamId, const std::string& op ) const; /** * Generate a query for a specific stream in a profile. * * \param profileId the id of the stream * \param streamId the id of the stream * \param op the operation to perform. * \return XML formatted query */ std::string genProfileStreamItemQuery( uint16_t profileId, uint16_t streamId, const std::string& op ) const; /** * Generate a custom RPC query * * \param rpcName the name of the RPC * \param params a string contaiing the xml formatted parameters * for the RPC * \return XML formatted query */ std::string buildCustomRPC( const std::string& rpcName, const std::string& params ) const; private: /** * * Generate the ConnectionItemConfig part of the supplied config. * \tparam T a class that mus supply the member variables: * sourceId_ * sourcePinId_ * destinationId_ * destinationPinId_ * bitrate_ * \param config the config * \param op the operation being performed * \return XML representation of the data being configured. */ template std::string configureConnectionItem( const T& config, const std::string& op ) const; protected: const std::string moduleNs_; ///< the module's namespace }; } // namespace YumaTest #endif // __XPO_QUERY_BUILDER_H yuma123_2.14/netconf/test/support/msg-util/state-data-query-builder.cpp0000664000175000017500000000243114770023131026335 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/msg-util/state-data-query-builder.h" // ---------------------------------------------------------------------------| // Boost includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| namespace YumaTest { // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; using namespace YumaTest; // ---------------------------------------------------------------------------| StateDataQueryBuilder::StateDataQueryBuilder( const string& moduleNs ) : NCMessageBuilder() , moduleNs_( moduleNs ) { } // ---------------------------------------------------------------------------| StateDataQueryBuilder::~StateDataQueryBuilder() { } // ---------------------------------------------------------------------------| string StateDataQueryBuilder::genToasterQuery( const string& op ) const { return genTopLevelContainerText( "toaster", moduleNs_, op ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/msg-util/NCMessageBuilder.cpp0000664000175000017500000004416214770023131024642 0ustar vladimirvladimir#include "test/support/msg-util/NCMessageBuilder.h" //#include #include #include #include #include "src/ncx/xml_util.h" // ---------------------------------------------------------------------------! using namespace std; // ---------------------------------------------------------------------------! // Anonymous Namespace // ---------------------------------------------------------------------------! namespace { // ---------------------------------------------------------------------------! const char* IETF_BASE = "urn:ietf:params:netconf:base:1.0"; const char* IETF_NS = "urn:ietf:params:xml:ns:netconf:base:1.0"; const char* YUMA_NS = "http://netconfcentral.org/ns/yuma-system"; } // Anonymous namespace namespace YumaTest { // ---------------------------------------------------------------------------! NCMessageBuilder::NCMessageBuilder() : defaultOperation_( "merge" ) { } // ---------------------------------------------------------------------------! NCMessageBuilder::~NCMessageBuilder() { } // ---------------------------------------------------------------------------! void NCMessageBuilder::setDefaultOperation( const string& defaultOperation ) { defaultOperation_ = defaultOperation; } // ---------------------------------------------------------------------------! const std::string NCMessageBuilder::getDefaultOperation() const { return defaultOperation_; } // ---------------------------------------------------------------------------! string NCMessageBuilder::genXmlNsText( const string& xmlnsArg, const string& ns ) const { stringstream query; query << xmlnsArg << "=\"" << ns << "\""; return query.str(); } // ---------------------------------------------------------------------------! string NCMessageBuilder::genXmlNsText( const string& ns ) const { return genXmlNsText( "xmlns", ns ); } // ---------------------------------------------------------------------------! string NCMessageBuilder::genXmlNsNcText( const string& ns ) const { return genXmlNsText( "xmlns:nc", ns ); } // ---------------------------------------------------------------------------! string NCMessageBuilder::buildNCMessage( const string& message ) const { stringstream query; query << XML_START_MSG << "\n" << message << "\n"; // << NC_SSH_END; return query.str(); } // ---------------------------------------------------------------------------! string NCMessageBuilder::buildHelloMessage() const { stringstream query; query << "" << " " << " " << IETF_BASE << "" << " \n" << ""; return buildNCMessage( query.str() ); } // ---------------------------------------------------------------------------! string NCMessageBuilder::buildRPCMessage( const std::string& queryStr, const uint16_t messageId ) const { stringstream query; query << "\n" << queryStr << "\n" << ""; return buildNCMessage( query.str() ); } // ---------------------------------------------------------------------------! string NCMessageBuilder::buildLoadMessage( const std::string& moduleName, const uint16_t messageId ) const { stringstream query; query << " \n" << " " << moduleName << " \n" << " "; return buildRPCMessage( query.str(), messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildGetConfigMessage( const string& filter, const string& target, const uint16_t messageId ) const { stringstream query; query << " \n" << " " << "<" << target << "/> " << "\n" << " " << filter << "\n" << " "; return buildRPCMessage( query.str(), messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder:: buildGetConfigMessageXPath( const string& xPathStr, const string& target, const uint16_t messageId ) const { stringstream query; query << "" ; return buildGetConfigMessage( query.str(), target, messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder:: buildGetConfigMessageSubtree( const string& subtreeStr, const string& target, const uint16_t messageId ) const { stringstream query; query << "\n" << subtreeStr << "\n" << " "; return buildGetConfigMessage( query.str(), target, messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildGetMessage( const string& filter, const string& target, const uint16_t messageId ) const { stringstream query; query << " \n" << " " << filter << "\n" << " "; return buildRPCMessage( query.str(), messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder:: buildGetMessageXPath( const string& xPathStr, const string& target, const uint16_t messageId ) const { stringstream query; query << "" ; return buildGetMessage( query.str(), target, messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder:: buildGetMessageSubtree( const string& subtreeStr, const string& target, const uint16_t messageId ) const { stringstream query; query << "\n" << subtreeStr << "\n" << " "; return buildGetMessage( query.str(), target, messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildLockMessage( const uint16_t messageId, const string& target ) const { stringstream query; query << " <" << target << "/>"; return buildRPCMessage( query.str(), messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildUnlockMessage( const uint16_t messageId, const string& target ) const { stringstream query; query << " <" << target << "/>"; return buildRPCMessage( query.str(), messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildValidateMessage( const uint16_t messageId, const string& source ) const { stringstream query; query << " <" << source << "/>"; return buildRPCMessage( query.str(), messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildSetLogLevelMessage( const std::string& logLevelStr, const uint16_t messageId ) const { stringstream query; query << " \n" << " " << logLevelStr << "\n" << " \n"; return buildRPCMessage( query.str(), messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildEditConfigMessage( const string& configChange, const string& target, uint16_t messageId ) const { stringstream query; query << " \n" << " " << "<" << target << "/> " << "\n" << " " << defaultOperation_ << "\n" << " \n" << " " << configChange << "\n" << " \n" << " "; return buildRPCMessage( query.str(), messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildCopyConfigMessage( const string& target, const string& source, uint16_t messageId ) const { stringstream query; query << " \n" << " " << "<" << target << "/> " << "\n" << " " << "<" << source << "/> " << "\n" << " "; return buildRPCMessage( query.str(), messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildDeleteConfigMessage( const string& target, uint16_t messageId ) const { stringstream query; query << " \n" << " " << "<" << target << "/> " << "\n" << " "; return buildRPCMessage( query.str(), messageId ); } // ---------------------------------------------------------------------------! string NCMessageBuilder::buildDiscardChangesMessage( const uint16_t messageId ) const { stringstream query; query << XML_START_MSG << "\n" << "\n" << " " << "\n" << "\n"; return query.str(); } // ---------------------------------------------------------------------------! string NCMessageBuilder::buildGetMySessionMessage( const uint16_t messageId ) const { stringstream query; query << XML_START_MSG << "\n" << "\n" << " " << "\n" << "\n"; return query.str(); } // ---------------------------------------------------------------------------! string NCMessageBuilder::buildSetMySessionMessage( const string& indent, const string& linesize, const string& withDefaults, const uint16_t messageId ) const { stringstream query; query << XML_START_MSG << "\n" << "\n" << " " << "\n" << " " << "" << indent << "\n" << " " << "" << linesize << "\n" << " " << "" << withDefaults << "\n" << " " << "\n" << "\n"; return query.str(); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildKillSessionMessage( uint16_t sessionId, uint16_t messageId ) const { stringstream query; query << " \n" << " " << sessionId << "\n" << " "; return buildRPCMessage( query.str(), messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildCloseSessionMessage( uint16_t messageId ) const { stringstream query; query << " \n"; return buildRPCMessage( query.str(), messageId ); } // ---------------------------------------------------------------------------! string NCMessageBuilder::buildShutdownMessage( const uint16_t messageId ) const { stringstream query; query << XML_START_MSG << "\n" << "\n" << " " << "\n" << "\n"; return query.str(); } // ---------------------------------------------------------------------------! string NCMessageBuilder::buildRestartMessage( const uint16_t messageId ) const { stringstream query; query << XML_START_MSG << "\n" << "\n" << " " << "\n" << "\n"; return query.str(); } // ---------------------------------------------------------------------------! string NCMessageBuilder::buildGetSchemaMessage( const string& schemaId, const uint16_t messageId ) const { stringstream query; query << XML_START_MSG << "\n" << "\n" << " " << "\n" << " " << "" << schemaId << "\n" << " " << "\n" << "\n"; return query.str(); } // ------------------------------------------------------------------------| string NCMessageBuilder::genNodeText( const string& nodeName, const string& op ) const { stringstream query; query << "<" << nodeName; if ( !op.empty() ) { query << " " << genXmlNsNcText( IETF_NS ); query << " nc:operation=\"" << op << "\""; } return query.str(); } // ------------------------------------------------------------------------| string NCMessageBuilder::genTopLevelContainerText( const string& moduleName, const string& moduleNs, const string& operation ) const { stringstream query; query << genNodeText( moduleName, operation ); query << " " << genXmlNsText( moduleNs ) << "/>"; return query.str(); } // ------------------------------------------------------------------------| string NCMessageBuilder::genInterfaceContainerText( const string& moduleNs, const string& type, int mtu, const string& operation ) const { stringstream mtuStream, query; mtuStream << mtu; query << genNodeText( "interface", operation ); query << " " << genXmlNsText( moduleNs ) << ">\n" << " " << genOperationText( "ifType", type, operation) << " " << genOperationText( "ifMTU", mtuStream.str(), operation) << "\n"; return query.str(); } // ------------------------------------------------------------------------| string NCMessageBuilder::genOperationText( const string& nodeName, const string& nodeVal, const string& operation ) const { stringstream query; query << genNodeText( nodeName, operation ); query << ">" << nodeVal << "\n"; return query.str(); } // ------------------------------------------------------------------------| string NCMessageBuilder::genKeyOperationText( const string& nodeName, const string& keyName, const string& keyVal, const string& operation ) const { stringstream query; query << genNodeText( nodeName, operation ); query << ">" << "<" << keyName << ">" << keyVal << "" << ""; return query.str(); } // ------------------------------------------------------------------------| string NCMessageBuilder::genKeyParentPathText( const string& nodeName, const string& keyName, const string& keyVal, const string& queryText, const string& op ) const { stringstream query; query << genNodeText( nodeName, op ); query << ">" << "<" << keyName << ">" << keyVal << "\n" << queryText << "\n" << ""; return query.str(); } // ------------------------------------------------------------------------| string NCMessageBuilder::genModuleOperationText( const string& moduleName, const string& moduleNs, const string& queryText ) const { stringstream query; query << "<" << moduleName << " " << genXmlNsText( moduleNs ) << ">\n" << queryText << ""; return query.str(); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildCommitMessage( const uint16_t messageId ) { return buildRPCMessage( "", messageId ); } // ------------------------------------------------------------------------| string NCMessageBuilder::buildConfirmedCommitMessage( const int timeout, const uint16_t messageId ) { stringstream query; query << "\n" << " " << "\n" << " " << "" << timeout << "\n" << ""; return buildRPCMessage( query.str(), messageId ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/nc-query-util/0000775000175000017500000000000014770023131021770 5ustar vladimirvladimiryuma123_2.14/netconf/test/support/nc-query-util/nc-strings.h0000664000175000017500000000054214770023131024231 0ustar vladimirvladimir#ifndef __NC_STRINGS__H #define __NC_STRINGS__H // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| extern const char* NC_SSH_END; ///< SSH End of session indication } // namespace YumaTest #endif // __NC_STRINGS__H yuma123_2.14/netconf/test/support/nc-query-util/nc-default-operation-config.cpp0000664000175000017500000000227614770023131027766 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-default-operation-config.h" #include "test/support/msg-util/NCMessageBuilder.h" // ---------------------------------------------------------------------------| // File scope namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| DefaultOperationConfig::DefaultOperationConfig( std::shared_ptr< NCMessageBuilder > builder, const std::string& newDefaultOperation ) : builder_( builder ) , orignalOperation_( builder_->getDefaultOperation() ) { builder_->setDefaultOperation( newDefaultOperation ); } // ---------------------------------------------------------------------------| DefaultOperationConfig::~DefaultOperationConfig() { builder_->setDefaultOperation( orignalOperation_ ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/nc-query-util/yuma-op-policies.cpp0000664000175000017500000000600014770023131025664 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/yuma-op-policies.h" #include "test/support/nc-query-util/nc-query-utils.h" // ---------------------------------------------------------------------------| // STD Includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Global namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| AbstractYumaOpLogPolicy:: AbstractYumaOpLogPolicy( const string& baseDir ) : baseDir_( baseDir ) { } // ---------------------------------------------------------------------------| AbstractYumaOpLogPolicy::~AbstractYumaOpLogPolicy() { } // ---------------------------------------------------------------------------| std::string AbstractYumaOpLogPolicy::generateSessionLog( uint16_t sessionId ) { ostringstream oss; using boost::unit_test::framework::current_test_case; oss << baseDir_ << "/" << "Ses" << setw(4) << setfill('0') << sessionId << "-" << current_test_case().p_name << ".xml"; return oss.str(); } // ---------------------------------------------------------------------------| MessageIdLogFilenamePolicy::MessageIdLogFilenamePolicy( const string& baseDir ) : AbstractYumaOpLogPolicy( baseDir ) { } // ---------------------------------------------------------------------------| MessageIdLogFilenamePolicy::~MessageIdLogFilenamePolicy() { } // ---------------------------------------------------------------------------| string MessageIdLogFilenamePolicy::generate( const string& queryStr, uint16_t sessionId ) { ostringstream oss; using boost::unit_test::framework::current_test_case; oss << baseDir_ << "/" << current_test_case().p_name << "-Ses" << setw(4) << setfill('0') << sessionId << "-" << setw(4) << setfill('0') << extractMessageIdStr( queryStr) << ".xml"; return oss.str(); } // ---------------------------------------------------------------------------| string MessageIdLogFilenamePolicy::generateHelloLog( const uint16_t sessionId ) { ostringstream oss; using boost::unit_test::framework::current_test_case; oss << baseDir_ << "/" << current_test_case().p_name << "-Ses" << setw(4) << setfill('0') << sessionId << "-hello.xml"; return oss.str(); } } // namespace YumaTest yuma123_2.14/netconf/test/support/nc-query-util/nc-query-utils.h0000664000175000017500000000146114770023131025044 0ustar vladimirvladimir#ifndef __NC_QUERY_UTILS_H #define __NC_QUERY_UTILS_H // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------! /** * Extract the message id from the supplied XML query. * * \param queryStr an XML query. * \return the message id as a string * */ std::string extractMessageIdStr( const std::string& queryStr ); /** * Extract the message id from the supplied XML query. * * \param queryStr an XML query. * \return the message id as a unit16_t * */ uint16_t extractMessageId( const std::string& queryStr ); } // namespace YumaTest #endif // __NC_QUERY_UTILS_H yuma123_2.14/netconf/test/support/nc-query-util/nc-query-utils.cpp0000664000175000017500000000321414770023131025375 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-query-utils.h" // ---------------------------------------------------------------------------| // STD Includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Global namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| string extractMessageIdStr( const std::string& query ) { using namespace boost::xpressive; sregex re = sregex::compile( "message-id=\"(\\d+)\"" ); smatch what; // find a whole word BOOST_REQUIRE_MESSAGE( regex_search( query, what, re ), "Invalid XML Query - No message-id!" ); return what[1]; } // ---------------------------------------------------------------------------| uint16_t extractMessageId( const std::string& query ) { return boost::lexical_cast( extractMessageIdStr( query ) ); } } yuma123_2.14/netconf/test/support/nc-query-util/nc-default-operation-config.h0000664000175000017500000000273214770023131027430 0ustar vladimirvladimir#ifndef __YUMA_NC_DEFAULT_OPERATION_CONFIG_H #define __YUMA_NC_DEFAULT_OPERATION_CONFIG_H // ---------------------------------------------------------------------------| // Standard Includes // ---------------------------------------------------------------------------| #include #include // ---------------------------------------------------------------------------| namespace YumaTest { class NCMessageBuilder; // ---------------------------------------------------------------------------| /** * RAII Style change of default operation. * This class should be used to change the default-operation that an * NCBaseQueryTestEngine uses for generating queries. The constructor * stores the current default-operation and sets the new one, the * destructor restores the original default-operation. */ class DefaultOperationConfig { public: /** * Constructor * * \param builder the message builder to change the default operation for. * \param newDefaultOperation the new default operation. */ DefaultOperationConfig( std::shared_ptr< NCMessageBuilder > builder, const std::string& newDefaultOperation ); /** * Destructor. */ ~DefaultOperationConfig(); private: std::shared_ptr< NCMessageBuilder > builder_; ///< the message buidler const std::string orignalOperation_ ; ///< the original default operation }; } // namespace YumaTest #endif // __YUMA_NC_DEFAULT_OPERATION_CONFIG_H yuma123_2.14/netconf/test/support/nc-query-util/nc-base-query-test-engine.h0000664000175000017500000000646714770023131027051 0ustar vladimirvladimir#ifndef __YUMA_NC_BASE_QUERY_TEST_ENGINE_H #define __YUMA_NC_BASE_QUERY_TEST_ENGINE_H // ---------------------------------------------------------------------------| // Standard Includes // ---------------------------------------------------------------------------| #include #include #include #include // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/msg-util/NCMessageBuilder.h" #include "test/support/nc-session/abstract-nc-session.h" // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractNCSession; // ---------------------------------------------------------------------------| /** * Yuma-Test utility base class for sending Netconf Messages to Yuma. */ class NCBaseQueryTestEngine { public: /** * Constructor. * The target DB name must be either 'running' or 'candidate'. Running * is only valid if Yuma is configured to allow modifications to the * running config. * * \param builder the message builder to use. */ explicit NCBaseQueryTestEngine( std::shared_ptr builder ); /** Destructor */ virtual ~NCBaseQueryTestEngine(); /** * Utility function for setting the yuma log level. * The following Log Levels are supported: *
    *
  • off
  • *
  • error
  • *
  • warn
  • *
  • info
  • *
  • debug
  • *
  • debug2
  • *
  • debug3
  • *
  • debug4
  • *
* * \param session the id of the session associated with this query * \param logLevel the log level to set */ void setLogLevel( std::shared_ptr session, const std::string& logLevel ); protected: /** * Run the supplied query and check the results. * This method builds a NC-Query containing the supplied query * string, injects the message into Yuma and parses the result to * determine if the query was successful. * * \tparam the type of results checker * \param session the id of the session associated with this query * \param queryStr the query to try. * \param checker the results checker. */ template< class Checker > void runQuery( std::shared_ptr session, const std::string& queryStr, Checker& checker ) { // Inject the message uint16_t messageId = session->injectMessage( queryStr ); BOOST_TEST_MESSAGE( "Injecting Message:\n " << queryStr ); // Check the Result const std::string queryResult = session->getSessionResult( messageId ); BOOST_TEST_MESSAGE( "Response:\n " << queryResult ); checker( queryResult ); } protected: /** Utility XML message builder */ std::shared_ptr messageBuilder_; }; } // namespace YumaTest #endif // __YUMA_NC_BASE_QUERY_TEST_ENGINE_H yuma123_2.14/netconf/test/support/nc-query-util/nc-strings.cpp0000664000175000017500000000062414770023131024565 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-strings.h" // ---------------------------------------------------------------------------| namespace YumaTest { const char* NC_SSH_END = "]]>]]>"; } // namespace YumaTest yuma123_2.14/netconf/test/support/nc-query-util/nc-query-test-engine.cpp0000664000175000017500000001042514770023131026461 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // File scope namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| NCDbScopedLock::NCDbScopedLock( shared_ptr< NCQueryTestEngine > engine, shared_ptr< AbstractNCSession > session, const string& target ) : engine_( engine ) , session_( session ) , target_( target ) { engine_->lock( session_, target_ ); } // ---------------------------------------------------------------------------| NCDbScopedLock::~NCDbScopedLock() { engine_->unlock( session_, target_ ); } // ---------------------------------------------------------------------------| NCQueryTestEngine::NCQueryTestEngine( shared_ptr builder ) : NCBaseQueryTestEngine( builder ) { } // ---------------------------------------------------------------------------| NCQueryTestEngine::~NCQueryTestEngine() { } // ---------------------------------------------------------------------------| void NCQueryTestEngine::lock( shared_ptr session, const string& target ) { vector expPresent = { "ok" }; vector expNotPresent = { "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); tryLockDatabase( session, target, checker ); } // ---------------------------------------------------------------------------| void NCQueryTestEngine::unlock( shared_ptr session, const string& target ) { vector expPresent = { "ok" }; vector expNotPresent = { "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); tryUnlockDatabase( session, target, checker ); } // ---------------------------------------------------------------------------| void NCQueryTestEngine::commit( shared_ptr session ) { vector expPresent = { "ok" }; vector expNotPresent = { "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); string queryStr = messageBuilder_->buildCommitMessage( session->allocateMessageId() ); runQuery( session, queryStr, checker ); } // ---------------------------------------------------------------------------| void NCQueryTestEngine::commitFailure( shared_ptr session ) { vector expPresent = { "error", "rpc-error" }; vector expNotPresent = { "ok" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); string queryStr = messageBuilder_->buildCommitMessage( session->allocateMessageId() ); runQuery( session, queryStr, checker ); } // ---------------------------------------------------------------------------| void NCQueryTestEngine::confirmedCommit( shared_ptr session, const int timeout ) { vector expPresent = { "ok" }; vector expNotPresent = { "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); string queryStr = messageBuilder_->buildConfirmedCommitMessage( timeout, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } // ---------------------------------------------------------------------------| void NCQueryTestEngine::loadModule( shared_ptr session, const string& moduleName ) { vector expPresent = { "mod-revision" }; vector expNotPresent = { "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); tryLoadModule( session, moduleName, checker ); } } // namespace YumaTest yuma123_2.14/netconf/test/support/nc-query-util/nc-query-test-engine.h0000664000175000017500000004705714770023131026141 0ustar vladimirvladimir#ifndef __YUMA_TEST_TRANSFORM_ENGINE_H #define __YUMA_TEST_TRANSFORM_ENGINE_H // ---------------------------------------------------------------------------| // Standard Includes // ---------------------------------------------------------------------------| #include #include #include #include // ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-base-query-test-engine.h" // ---------------------------------------------------------------------------| namespace YumaTest { class AbstractYumaOpLogPolicy; class AbstractNCQueryFactory; // ---------------------------------------------------------------------------| /** * Yuma-Test utility class for sending Netconf Messages to Yuma that * modify the current configuration. */ class NCQueryTestEngine : public NCBaseQueryTestEngine { public: /** * Constructor. * The target DB name must be either 'running' or 'candidate'. Running * is only valid if Yuma is configured to allow modifications to the * running config. * * \param builder the message builder to use. */ explicit NCQueryTestEngine( std::shared_ptr builder ); /** Destructor */ virtual ~NCQueryTestEngine(); /** * Load the specified module. * This method builds a NC-Query containing a load request, * injects the message into Yuma and parses the result to * determine if the module was loaded successfully. * * \tparam the type of results checker * \param moduleName the name of the module to load * \param session the session to use for injecting the message. * \param checker the results checker. */ template< class Checker > void tryLoadModule( std::shared_ptr session, const std::string& moduleName, Checker& checker ) { // build a load module message for test.yang const std::string queryStr = messageBuilder_->buildLoadMessage( moduleName, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Try to lock the database associated with this engine. * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param target the target database name * \param checker the results checker. */ template< class Checker > void tryLockDatabase( std::shared_ptr session, const std::string& target, Checker& checker ) { // build a lock module message for test.yang const std::string queryStr = messageBuilder_->buildLockMessage( session->allocateMessageId(), target ); runQuery( session, queryStr, checker ); } /** * Try to unlock the database associated with this engine. * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param target the target database name * \param checker the results checker. */ template< class Checker > void tryUnlockDatabase( std::shared_ptr session, const std::string& target, Checker& checker ) { // build an unlock module message for test.yang const std::string queryStr = messageBuilder_->buildUnlockMessage( session->allocateMessageId(), target ); runQuery( session, queryStr, checker ); } /** * Try to validate the database associated with this engine. * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param target the target database name * \param checker the results checker. */ template< class Checker > void tryValidateDatabase( std::shared_ptr session, const std::string& target, Checker& checker ) { // build a validate message for test.yang const std::string queryStr = messageBuilder_->buildValidateMessage( session->allocateMessageId(), target ); runQuery( session, queryStr, checker ); } /** * Get the configuration filtered using the supplied xpath * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param xPathFilterStr the XPath filter to apply * \param target the target database name * \param checker the results checker. */ template< class Checker > void tryGetConfigXpath( std::shared_ptr session, const std::string& xPathFilterStr, const std::string& target, Checker& checker ) { // build a load module message for test.yang const std::string queryStr = messageBuilder_->buildGetConfigMessageXPath( xPathFilterStr, target, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Get the configuration filtered using the supplied subtree * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param subtreeFilterStr the subtree filter to apply * \param target the target database name * \param checker the results checker. */ template< class Checker > void tryGetConfigSubtree( std::shared_ptr session, const std::string& subtreeFilterStr, const std::string& target, Checker& checker ) { // build a load module message for test.yang const std::string queryStr = messageBuilder_->buildGetConfigMessageSubtree( subtreeFilterStr, target, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Get the state data filtered using the supplied xpath * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param xPathFilterStr the XPath filter to apply * \param target the target database name * \param checker the results checker. */ template< class Checker > void tryGetXpath( std::shared_ptr session, const std::string& xPathFilterStr, const std::string& target, Checker& checker ) { // build a load module message for test.yang const std::string queryStr = messageBuilder_->buildGetMessageXPath( xPathFilterStr, target, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Get the state data filtered using the supplied subtree * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param subtreeFilterStr the subtree filter to apply * \param target the target database name * \param checker the results checker. */ template< class Checker > void tryGetSubtree( std::shared_ptr session, const std::string& subtreeFilterStr, const std::string& target, Checker& checker ) { // build a load module message for test.yang const std::string queryStr = messageBuilder_->buildGetMessageSubtree( subtreeFilterStr, target, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Edit the configuration using the supplied query * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param query the edit config operation * \param target the target database name * \param checker the results checker. */ template< class Checker > void tryEditConfig( std::shared_ptr session, const std::string& query, const std::string& target, Checker& checker ) { const std::string queryStr = messageBuilder_->buildEditConfigMessage( query, target, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Try a custom RPC Query. * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param query the edit config operation * \param checker the results checker. */ template< class Checker > void tryCustomRPC( std::shared_ptr session, const std::string& query, Checker& checker ) { const std::string queryStr = messageBuilder_->buildRPCMessage( query, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Copy the configuration * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param target the target database name * \param source the source database name * \param checker the results checker. */ template< class Checker > void tryCopyConfig( std::shared_ptr session, const std::string& target, const std::string& source, Checker& checker ) { const std::string queryStr = messageBuilder_->buildCopyConfigMessage( target, source, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Delete the configuration * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param target the target database name * \param checker the results checker. */ template< class Checker > void tryDeleteConfig( std::shared_ptr session, const std::string& target, Checker& checker ) { const std::string queryStr = messageBuilder_->buildDeleteConfigMessage( target, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Set the log verbosity to the supplied level * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param logLevelStr the log level to apply * \param checker the results checker. */ template< class Checker > void trySetLogLevel( std::shared_ptr session, const std::string& logLevelStr, Checker& checker ) { // build a set log level message for test.yang const std::string queryStr = messageBuilder_->buildSetLogLevelMessage( logLevelStr, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Discard changes * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param checker the results checker. */ template< class Checker > void tryDiscardChanges( std::shared_ptr session, Checker& checker ) { // build a discard-changes message for test.yang const std::string queryStr = messageBuilder_->buildDiscardChangesMessage( session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Get my session * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param checker the results checker. */ template< class Checker > void tryGetMySession( std::shared_ptr session, Checker& checker ) { // build a get my session message for test.yang const std::string queryStr = messageBuilder_->buildGetMySessionMessage( session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Set my session * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param indent the indent to be set. * \param linesize the linesize to be set. * \param withDefaults the with-defaults setting to be set. * \param checker the results checker. */ template< class Checker > void trySetMySession( std::shared_ptr session, const std::string& indent, const std::string& linesize, const std::string& withDefaults, Checker& checker ) { // build a set my session message for test.yang const std::string queryStr = messageBuilder_->buildSetMySessionMessage( indent, linesize, withDefaults, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Kill session * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param sessionId the id of the session to be killed. * \param checker the results checker. */ template< class Checker > void tryKillSession( std::shared_ptr session, uint16_t sessionId, Checker& checker ) { // build a kill-session message for test.yang const std::string queryStr = messageBuilder_->buildKillSessionMessage( sessionId, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Close session * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param checker the results checker. */ template< class Checker > void tryCloseSession( std::shared_ptr session, Checker& checker ) { // build a close-session message for test.yang const std::string queryStr = messageBuilder_->buildCloseSessionMessage( session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Shutdown * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param checker the results checker. */ template< class Checker > void tryShutdown( std::shared_ptr session, Checker& checker ) { // build a shutdown message for test.yang const std::string queryStr = messageBuilder_->buildShutdownMessage( session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Restart * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param checker the results checker. */ template< class Checker > void tryRestart( std::shared_ptr session, Checker& checker ) { // build a restart message for test.yang const std::string queryStr = messageBuilder_->buildRestartMessage( session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Get the data model definition files from the server * * \tparam the type of results checker * \param session the session to use for injecting the message. * \param xPathFilterStr the XPath filter to apply * \param target the target database name * \param checker the results checker. */ template< class Checker > void tryGetSchema( std::shared_ptr session, const std::string& schemaIdStr, Checker& checker ) { // build a get-schema message for device_test.yang const std::string queryStr = messageBuilder_->buildGetSchemaMessage( schemaIdStr, session->allocateMessageId() ); runQuery( session, queryStr, checker ); } /** * Utility function for locking the database. * * \param session the session to use for injecting the message. * \param target the target database name */ void unlock( std::shared_ptr session, const std::string& target ); /** * Utility function for unlocking the database. * * \param session the session to use for injecting the message. * \param target the target database name */ void lock( std::shared_ptr session, const std::string& target ); /** * Utility function for loading a module. * * \param session the session to use for injecting the message. * \param moduleName the name of the module to load */ void loadModule( std::shared_ptr session, const std::string& moduleName ); /** * Send a commit message. * * \param session the session to use for injecting the message. */ void commit( std::shared_ptr session ); /** * Send a commit message that is expected to fail. * * \param session the session to use for injecting the message. */ void commitFailure( std::shared_ptr session ); /** * Send a confirmed-commit message. * * \param session the session to use for injecting the message. * \param timeout the confirm-timeout in seconds. */ void confirmedCommit( std::shared_ptr session, const int timeout ); }; // ---------------------------------------------------------------------------| /** * RAII Lock guard for locking and unlocking of a database during * testing. Use this lock guard to ensure that the database is left * unlocked when the test exits even on test failure. */ class NCDbScopedLock { public: /** Constructor. This issues a lock command to the database. * * \param engine shared_ptr to the query-test-engine. * \param session shared_ptr to the session. * \param target the target database name */ NCDbScopedLock( std::shared_ptr< NCQueryTestEngine > engine, std::shared_ptr< AbstractNCSession > session, const std::string& target ); /** * Destructor. */ ~NCDbScopedLock(); private: /** The engine that issues lock / unlock requests */ std::shared_ptr< NCQueryTestEngine > engine_; /** The session locking / unlocking the database */ std::shared_ptr< AbstractNCSession > session_; /** The name of the target database */ std::string target_; }; } // namespace YumaTest #endif // __YUMA_TEST_TRANSFORM_ENGINE_H yuma123_2.14/netconf/test/support/nc-query-util/yuma-op-policies.h0000664000175000017500000000567414770023131025351 0ustar vladimirvladimir#ifndef __YUMA_TEST_OP_POLICIES_H #define __YUMA_TEST_OP_POLICIES_H // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| namespace YumaTest { /** * Abstract base class for Yuma OP Policies. */ class AbstractYumaOpLogPolicy { public: /** * constructor. * * \param baseDir the base directory for logfiles. */ explicit AbstractYumaOpLogPolicy( const std::string& baseDir ); /** Destructor */ virtual ~AbstractYumaOpLogPolicy(); /** * Generate a logfile name. * * \param queryStr the query string. * \param sessionId the session id. * \return a filename for logging the query to. */ virtual std::string generate( const std::string& queryStr, uint16_t sessionId ) = 0; /** * Generate a logfile name for the 'hello' message exchange, * that is just based on the sessionId. * * \param sessionId the session id. * \return a filename for logging the hello exchange to. */ virtual std::string generateHelloLog( uint16_t sessionId ) = 0; /** * Generate a logfile name for writing all of the sessions messages * to. This function is used to generate the name of the file that * contains all of the messages from the session. This is used by * the Session's destructor, which writes the messages from * the session to the generated file. * * \param sessionId the session id. * \return a filename for logging the sessions messages. */ virtual std::string generateSessionLog( uint16_t sessionId ); protected: std::string baseDir_; ///< The base directory for logfiles }; /** * Log filename generation policy that generates the name of the * Yuma output logfile based in the injected query's mnessage id. * The Yuma test harness redirects output into a log * generates awhich is generated when a message is injected * into Yuma by the test harness.based on */ class MessageIdLogFilenamePolicy : public AbstractYumaOpLogPolicy { public: /** * constructor. */ explicit MessageIdLogFilenamePolicy( const std::string& baseDir ); /** Destructor */ virtual ~MessageIdLogFilenamePolicy(); /** * Generate a logfile name based on the message id in the supplied * query. The logfilename will have the following format: * \/\-ses[id]-[messageId].xml. * * \param queryStr the query string * \param sessionId the session id */ virtual std::string generate( const std::string& queryStr, uint16_t sessionId ); /** * Generate a logfile name for the 'hello' message exchange, * that is just based on the sessionId. * * \param sessionId the session id */ virtual std::string generateHelloLog( uint16_t sessionId ); }; } // namespace YumaTest #endif // __YUMA_TEST_OP_POLICIES_H yuma123_2.14/netconf/test/support/nc-query-util/nc-base-query-test-engine.cpp0000664000175000017500000000307314770023131027372 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/nc-query-util/nc-base-query-test-engine.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // File scope namespace usage // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| NCBaseQueryTestEngine::NCBaseQueryTestEngine( shared_ptr builder ) : messageBuilder_( builder ) {} // ---------------------------------------------------------------------------| NCBaseQueryTestEngine::~NCBaseQueryTestEngine() {} // ---------------------------------------------------------------------------| void NCBaseQueryTestEngine::setLogLevel( shared_ptr session, const string& logLevel ) { vector expPresent = { "ok" }; vector expNotPresent = { "error", "rpc-error" }; // build a load module message for test.yang string queryStr = messageBuilder_->buildSetLogLevelMessage( logLevel, session->allocateMessageId() ); StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); runQuery( session, queryStr, checker ); } } // namespace YumaTest yuma123_2.14/netconf/test/yangrpc/0000775000175000017500000000000014770023131017201 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangrpc/README0000777000175000017500000000000014770023131022747 2../netconfd/READMEustar vladimirvladimiryuma123_2.14/netconf/test/yangrpc/test-get0000775000175000017500000000003714770023131020663 0ustar vladimirvladimir#!/bin/bash -e cd get ./run.sh yuma123_2.14/netconf/test/yangrpc/get/0000775000175000017500000000000014770023131017760 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangrpc/get/yangrpc-example.c0000777000175000017500000000000014770023131033201 2../../../src/yangrpc/example/yangrpc-example.custar vladimirvladimiryuma123_2.14/netconf/test/yangrpc/get/Makefile.am0000664000175000017500000000047614770023131022023 0ustar vladimirvladimircheck_PROGRAMS = yangrpc-example yangrpc_example_SOURCES = yangrpc-example.c yangrpc_example_CPPFLAGS = -DINSTANCE_A -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform -I${includedir}/yuma/yangrpc yangrpc_example_LDFLAGS = -module -lyumaagt -lyumancx -lyangrpc yuma123_2.14/netconf/test/yangrpc/get/run.sh0000775000175000017500000000113114770023131021117 0ustar vladimirvladimir#!/bin/bash -e NCSERVER=localhost NCPORT=830 NCUSER=$USER NCPASSWORD="" killall -KILL netconfd || true rm -f /tmp/ncxserver.sock /usr/sbin/netconfd --no-startup --log-level=debug3 --target=running --superuser=$USER & SERVER_PID=$! sleep 4 valgrind -v --log-fd=1 --num-callers=100 --leak-check=full --show-leak-kinds=all -- ./yangrpc-example --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD 1>session.stdout 2>session.stderr for var in "$@" do if [ "$var" == "--check-memory-leak" ] ; then cat tmp/server.log | grep "ERROR SUMMARY: 0 errors" fi done yuma123_2.14/netconf/test/yangrpc/Makefile.am0000775000175000017500000000007514770023131021242 0ustar vladimirvladimirTESTS=\ test-get \ test-get-check-memory-leak SUBDIRS=\ get yuma123_2.14/netconf/test/yangrpc/test-get-check-memory-leak0000775000175000017500000000006314770023131024155 0ustar vladimirvladimir#!/bin/bash -e cd get ./run.sh --check-memory-leak yuma123_2.14/netconf/test/yangrpc/configure.ac0000664000175000017500000000072314770023131021471 0ustar vladimirvladimirAC_INIT([yangrpc-test-suite], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules" AC_SUBST(yangdir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1 -DRELEASE=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile get/Makefile ]) AC_OUTPUT yuma123_2.14/netconf/test/stubs/0000775000175000017500000000000014770023131016676 5ustar vladimirvladimiryuma123_2.14/netconf/test/stubs/agt/0000775000175000017500000000000014770023131017451 5ustar vladimirvladimiryuma123_2.14/netconf/test/stubs/agt/agt_ncxserver.cpp0000664000175000017500000000076114770023131023033 0ustar vladimirvladimir#include "agt_ncxserver.h" // ---------------------------------------------------------------------------| /******************************************************************** * FUNCTION agt_ncxserver_clear_fd * * Clear a dead session from the select loop * * INPUTS: * fd == file descriptor number for the socket to clear *********************************************************************/ void agt_ncxserver_clear_fd (int fd) { // Empty stub } /* agt_ncxserver_clear_fd */ yuma123_2.14/netconf/test/stubs/ncx/0000775000175000017500000000000014770023131017466 5ustar vladimirvladimiryuma123_2.14/netconf/test/stubs/ncx/send_buff.cpp0000664000175000017500000000053514770023131022130 0ustar vladimirvladimir#include "send_buff.h" // ---------------------------------------------------------------------------| status_t send_buff (int fd, const char *buffer, size_t cnt) { // TODO: Store input parameters for checking fd = fd; buffer = buffer; cnt = cnt; // TODO: Make return value configurable, via a std::queue return NO_ERR; } yuma123_2.14/netconf/test/sys-test/0000775000175000017500000000000014770023131017331 5ustar vladimirvladimiryuma123_2.14/netconf/test/sys-test/shutdown.cpp0000664000175000017500000000073414770023131021714 0ustar vladimirvladimir#define BOOST_TEST_MODULE SysTestShutdown #include "configure-yuma-systest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--target=candidate" ), }; #include "define-yuma-systest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/sys-test/configure-env.py0000775000175000017500000000165614770023131022465 0ustar vladimirvladimir#!/usr/bin/env python import sys import os # ----------------------------------------------------------------------------| if __name__ == '__main__': print("""Generating Configuration Script for remote Yuma Testing...\n""") ipAddr = input( "Enter Netconf Agent Ip address: ") port = input( "Enter Netconf Agent Source Port: ") user = input( "Enter Netconf User Name: ") passWd = input( "Enter Netconf User's Password: ") outFilename = "./config.sh" out=open( outFilename, 'w' ) out.write( "export YUMA_AGENT_IPADDR="+ipAddr+"\n" ) out.write( "export YUMA_AGENT_PORT="+port+"\n" ) out.write( "export YUMA_AGENT_USER="+user+"\n" ) out.write( "export YUMA_AGENT_PASSWORD="+passWd+"\n" ) print(( "Environment configuration written to %s" % outFilename )) print(( "Source %s with the comman '. %s' to configure the test environment" % ( outFilename, outFilename ) )) yuma123_2.14/netconf/test/sys-test/define-yuma-systest-global-fixture.h0000664000175000017500000000140514770023131026343 0ustar vladimirvladimir/** * Define the Yuma System test Global Test Fixture. * This file simply defines the global test fixture for Yuma System * Testing. * * NOTE: Yuma System Testing. It purposely omits header include guards * * Yuma System Test Files Should have the following format: * * #define BOOST_TEST_MODULE [Name of test module] * #include "configure-yuma-systest.h" * * namespace YumaTest { * * Custom Initialisation (e.g. definition of SpoofedArgs ) * * #include "define-yuma-systest-global-fixture.h" * * } // namespace YumaTest. */ // typedef that allows the use of parameterised test fixtures with // BOOST_GLOBAL_FIXTURE typedef SystemTestFixture MyFixtureType_T; // Set the global test fixture BOOST_GLOBAL_FIXTURE( MyFixtureType_T ); yuma123_2.14/netconf/test/sys-test/simple-edit-candidate.cpp0000664000175000017500000000074714770023131024173 0ustar vladimirvladimir#define BOOST_TEST_MODULE SysTestSimpleEditCandidate #include "configure-yuma-systest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--target=candidate" ), }; #include "define-yuma-systest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/sys-test/all-running.py0000775000175000017500000000202714770023131022135 0ustar vladimirvladimir#!/usr/bin/env python import sys import os import subprocess # ----------------------------------------------------------------------------| def GetTestProgramList(): fileList=[] for dirname, dirnames, filenames in os.walk('.'): for filename in filenames: if filename.startswith( "test-" ) and os.access(filename, os.X_OK) and "running" in filename: fileList.append( "./" + filename ) return fileList # ----------------------------------------------------------------------------| def RunTests( tests, argv ): testArgs="" for arg in argv[1:]: testArgs += arg + " " for test in tests: testCommand = test + " " + testArgs print("running test %s" % testCommand) # commands.getoutput( test ) os.system( testCommand ) # ----------------------------------------------------------------------------| if __name__ == '__main__': # find the executables tests = GetTestProgramList() # run all executables RunTests( tests, sys.argv ) yuma123_2.14/netconf/test/sys-test/Makefile.am0000664000175000017500000001002314770023131021361 0ustar vladimirvladimirTESTS=test-simple-edit-candidate check_PROGRAMS=test-simple-edit-candidate test_simple_edit_candidate_SOURCES = \ $(top_srcdir)/netconf/test/test-suites/common/simple-edit-tests.cpp \ $(top_srcdir)/netconf/test/test-suites/common/simple-edit-tests-candidate.cpp \ $(top_srcdir)/netconf/test/test-suites/common/discard-changes-tests.cpp \ $(top_srcdir)/netconf/test/test-suites/common/default-none-tests.cpp \ $(top_srcdir)/netconf/test/test-suites/system/simple-edit-tests-confirmed-commit.cpp \ $(top_srcdir)/netconf/test/sys-test/simple-edit-candidate.cpp test_simple_edit_candidate_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_simple_edit_candidate_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-device-edit-candidate check_PROGRAMS+=test-device-edit-candidate test_device_edit_candidate_SOURCES = \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-misc.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-create.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-merge.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-replace.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-get.cpp \ $(top_srcdir)/netconf/test/test-suites/common/device-tests-delete.cpp \ $(top_srcdir)/netconf/test/sys-test/device-edit-candidate.cpp test_device_edit_candidate_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_device_edit_candidate_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-lock-load-candidate check_PROGRAMS+=test-lock-load-candidate test_lock_load_candidate_SOURCES = \ $(top_srcdir)/netconf/test/test-suites/common/module-load-test-suite.cpp \ $(top_srcdir)/netconf/test/test-suites/common/db-lock-test-suite-common.cpp \ $(top_srcdir)/netconf/test/test-suites/common/db-lock-test-suite-candidate.cpp \ $(top_srcdir)/netconf/test/test-suites/system//my-session-tests.cpp \ $(top_srcdir)/netconf/test/sys-test/lock-load-candidate.cpp test_lock_load_candidate_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_lock_load_candidate_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses TESTS+=test-shutdown check_PROGRAMS+=test-shutdown test_shutdown_SOURCES = \ $(top_srcdir)/netconf/test/sys-test/shutdown.cpp \ $(top_srcdir)/netconf/test/test-suites/system/shutdown-tests.cpp test_shutdown_CPPFLAGS = -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump test_shutdown_LDFLAGS = -lboost_unit_test_framework $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses yuma123_2.14/netconf/test/sys-test/device-edit-running.cpp0000664000175000017500000000074314770023131023701 0ustar vladimirvladimir#define BOOST_TEST_MODULE SysTestSimpleEditRunning #include "configure-yuma-systest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--target=running" ), }; #include "define-yuma-systest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/sys-test/lock-load-running.cpp0000664000175000017500000000074114770023131023362 0ustar vladimirvladimir#define BOOST_TEST_MODULE SysTestLockLoadRunning #include "configure-yuma-systest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--target=running" ), }; #include "define-yuma-systest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/sys-test/simple-edit-running.cpp0000664000175000017500000000074514770023131023735 0ustar vladimirvladimir#define BOOST_TEST_MODULE SysTestSimpleEditCandidate #include "configure-yuma-systest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--target=running" ), }; #include "define-yuma-systest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/sys-test/device-edit-candidate.cpp0000664000175000017500000000125614770023131024135 0ustar vladimirvladimir#define BOOST_TEST_MODULE SysTestDeviceEditCandidate #include "configure-yuma-systest.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--target=candidate" ), }; #include "define-yuma-systest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/sys-test/configure-yuma-systest.h0000664000175000017500000000223714770023131024154 0ustar vladimirvladimir/** * Configure the BOOST::TEST environment for Yuma System Testing. * This file is contains common configuration of BOOST::TEST for * Yuma System Testing. * * NOTE: It purposely omits header include guards * * Yuma System Test Files Should have the following format: * * #define BOOST_TEST_MODULE [Name of test module] * #include "configure-yuma-systest.h" * * namespace YumaTest { * * Custom Initialisation (e.g. definition of SpoofedArgs ) * * #include "define-yuma-systest-global-fixture.h" * * } // namespace YumaTest. */ /** Configure Dynamic linking with boost test libraries */ #define BOOST_TEST_DYN_LINK // ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #define BOOST_TEST_DYN_LINK #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/system-test-fixture.inl" #include "test/support/fixtures/spoofed-args.h" yuma123_2.14/netconf/test/sys-test/lock-load-candidate.cpp0000664000175000017500000000074514770023131023622 0ustar vladimirvladimir#define BOOST_TEST_MODULE SysTestLockLoadCandidate #include "configure-yuma-systest.h" namespace YumaTest { // ---------------------------------------------------------------------------| // Initialise the spoofed command line arguments // ---------------------------------------------------------------------------| const char* SpoofedArgs::argv[] = { ( "yuma-test" ), ( "--target=candidate" ), }; #include "define-yuma-systest-global-fixture.h" } // namespace YumaTest yuma123_2.14/netconf/test/sys-test/all-candidate.py0000775000175000017500000000203314770023131022366 0ustar vladimirvladimir#!/usr/bin/env python import sys import os import subprocess # ----------------------------------------------------------------------------| def GetTestProgramList(): fileList=[] for dirname, dirnames, filenames in os.walk('.'): for filename in filenames: if filename.startswith( "test-" ) and os.access(filename, os.X_OK) and "candidate" in filename : fileList.append( "./" + filename ) return fileList # ----------------------------------------------------------------------------| def RunTests( tests, argv ): testArgs="" for arg in argv[1:]: testArgs += arg + " " for test in tests: testCommand = test + " " + testArgs print("running test %s" % testCommand) # commands.getoutput( test ) os.system( testCommand ) # ----------------------------------------------------------------------------| if __name__ == '__main__': # find the executables tests = GetTestProgramList() # run all executables RunTests( tests, sys.argv ) yuma123_2.14/netconf/test/yangcli/0000775000175000017500000000000014770023131017164 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/test-interactive-container-edits0000775000175000017500000000006714770023131025475 0ustar vladimirvladimir#!/bin/bash -e cd interactive-container-edits ./run.sh yuma123_2.14/netconf/test/yangcli/netconf-notifications/0000775000175000017500000000000014770023131023467 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/netconf-notifications/run.sh0000775000175000017500000000241714770023131024636 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana_if_type.fxs export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD= /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 expect session.exp #kill $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/netconf-notifications/session.exp0000664000175000017500000000544514770023131025700 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "Server Protocol Capabilities" {send " "} timeout {send_user "\nError: Timeout waiting for Server Protocol Capabilities\n"; exit 1} } expect { -re "(interleave:1.0)|(notification:1.0)" {send " "} timeout {send_user "\nError: Your server does not advertise the interleave:1.0 or notification:1.0 capability\n"; exit 1} } expect { -re "(interleave:1.0)|(notification:1.0)" {send " "} timeout {send_user "\nError: Your server does not advertise the interleave:1.0 or notification:1.0 capability\n"; exit 1} } expect { "notifications@2008-07-14" {send " "} timeout {expect "yangcli $env(NCUSER)@$env(NCSERVER)>" ; send "mgrload notifications\n"} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "help create-subscription\n"} timeout {send_user "\nError: Did not get prompt 1\n"; exit 1} } expect { "The command to create a notification subscription." {send " "} timeout {send_user "\nError: Did not get help for create-subscription\n"; exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create-subscription\n"} timeout {send_user "\nError: Did not get prompt 1\n"; exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "delete /interfaces/interface\[name=\'foo\'\]\n"} timeout {send_user "\nError: Did not get prompt 2\n"; exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /interfaces/interface -- name=foo type=ethernetCsmacd\n"} timeout {send_user "\nError: Did not get prompt 3\n"; exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {send_user "\nError: Did not get prompt 4\n"; exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {send_user "\nError: create commit failed\n"; exit 1} } expect { "Incoming notification:" {send " "} timeout {send_user "\nError: No Incoming notification\n"; exit 1} } expect { "netconf-config-change" {send "xget /interfaces/interface\[name=\'foo\'\]\n"} timeout {send_user "\nError: No netconf-config-change\n"; exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "delete /interfaces/interface\[name=\'foo\'\]\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "Incoming notification:" {send " "} timeout {send_user "\nError: No Incoming notification\n"; exit 1} } expect { "netconf-config-change" {send "quit\n"} timeout {send_user "\nError: No netconf-config-change\n"; exit 1} } expect { eof {exit 0} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/feature-depending-completions/0000775000175000017500000000000014770023131025104 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/feature-depending-completions/run.sh0000775000175000017500000000306314770023131026251 0ustar vladimirvladimir#!/bin/bash -e function expected_fail { export DID_IT_FAIL=0 ; $1 || export DID_IT_FAIL=1 true if [ "$DID_IT_FAIL" = "1" ] ; then #echo "GOOD ERROR" return 0 else echo "ERROR: Expected FAIL returned OK." return -1 fi } rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana_if_type.fxs export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --feature-disable=ietf-interfaces:if-mib --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 expected_fail "expect session.exp" kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/feature-depending-completions/session.exp0000664000175000017500000000045514770023131027311 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /interfaces/interface/link-up-down-trap-\t\t"} timeout {exit 1} } expect { "enable" {sleep 1} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/cancel-fill/0000775000175000017500000000000014770023131021335 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/cancel-fill/run.sh0000775000175000017500000000242514770023131022503 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana_if_type.fxs export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 expect session.exp kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/cancel-fill/session.exp0000664000175000017500000000072414770023131023541 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {sleep 1} timeout {exit 1} } send "create /interfaces/interface\n" expect { "Enter string value for leaf " {sleep 1} timeout {exit 1} } send "?c\n" expect { "create command canceled" {sleep 1} timeout {exit 1} } send "quit\n" expect { eof {exit 0} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/tab-completion-union/0000775000175000017500000000000014770023131023227 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/tab-completion-union/mod1.yang0000664000175000017500000000123714770023131024752 0ustar vladimirvladimirmodule mod1 { yang-version 1.1; namespace "http://yuma123.org/ns/test/yangcli/tab-completion-union/mod1"; prefix m1; organization "yuma123.org"; description 'For testing tab completion'; revision 2020-02-17 { description "Initial version"; } identity foo; identity bar; identity first-foo { base "foo"; } identity second-foo { base "foo"; } identity third-bar { base "bar"; } identity fourth-bar { base "bar"; } container u1 { leaf u2 { type union { type identityref { base "foo"; } type identityref { base "bar"; } } } } } yuma123_2.14/netconf/test/yangcli/tab-completion-union/run.sh0000775000175000017500000000170414770023131024374 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confdc -c mod1.yang --yangpath . -o mod1.fxs confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --no-startup --module=mod1.yang --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 expect session.exp kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/tab-completion-union/session.exp0000664000175000017500000000077514770023131025441 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /u1/u2\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER):create>" {send "thi\t"} timeout {exit 1} } expect { "rd-bar" {send "\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "quit\n"} timeout {exit 1} } expect { eof {exit 0} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/test-fill0000775000175000017500000000004014770023131021007 0ustar vladimirvladimir#!/bin/bash -e cd fill ./run.sh yuma123_2.14/netconf/test/yangcli/test-mutikey-list-tab-completion0000775000175000017500000000006714770023131025445 0ustar vladimirvladimir#!/bin/bash -e cd mutikey-list-tab-completion ./run.sh yuma123_2.14/netconf/test/yangcli/mgrload/0000775000175000017500000000000014770023131020611 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/mgrload/run.sh0000775000175000017500000000053314770023131021755 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp mkdir tmp killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm -f /tmp/ncxserver.sock /usr/sbin/netconfd --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! sleep 1 expect session.exp res=$? kill -KILL $SERVER_PID wait exit ${res} yuma123_2.14/netconf/test/yangcli/mgrload/session.exp0000664000175000017500000000224714770023131023017 0ustar vladimirvladimirspawn yangcli --modpath=. --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /toaster\n"} timeout {exit 1} } expect { "No object match for node" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "load toaster\n"} timeout {exit 1} } expect { "mod-revision" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "mgrload toaster\n"} timeout {exit 1} } expect { "OK" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /toaster\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "xget /toaster\n"} timeout {exit 1} } expect { "toasterManufacturer" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {sleep 1} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/non-interactive-container-edits/0000775000175000017500000000000014770023131025357 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/non-interactive-container-edits/session.delete.exp0000664000175000017500000000256214770023131031026 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "delete /interfaces/interface\[name='eth1'\]/a/foo\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "delete /interfaces/interface\[name='eth1'\]/b\[key1='one'\]\[key2='two'\]/bar\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "delete /interfaces/interface\[name='eth1'\]/b\[key1='one'\]\[key2='two'\]/bar\n"} timeout {exit 1} } expect { "error-message 'data missing'" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "delete /interfaces/interface\[name='eth1'\]\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "quit\n"} timeout {exit 1} } expect { eof {exit 0} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/non-interactive-container-edits/session.replace.exp0000664000175000017500000000171214770023131031173 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "replace /interfaces/interface\[name='eth1'\]/a/foo value='hello2'\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "xget /interfaces/interface\[name='eth1'\]/a/foo\n"} timeout {exit 1} } expect { "hello2" {send " "} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "quit\n"} timeout {exit 1} } expect { eof {exit 0} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "xget /interfaces/interface\[name='eth1'\]/b\[key1='one'\]\[key2='two'\]/bar\n"} timeout {exit 1} } expect { "world" {send " "} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/non-interactive-container-edits/session.create.exp0000664000175000017500000000403314770023131031022 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "merge /interfaces/interface -- name=eth1 type=ethernetCsmacd a/foo=hello b\[key1='one'\]\[key2='two'\]/bar=world b\[key1='A'\]\[key2='B'\]/bar=WORLD\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "merge /interfaces/interface\[name='eth1'\]/c -- c1/bar=world\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "xget /interfaces/interface\[name='eth1'\]/a/foo\n"} timeout {exit 1} } expect { "hello" {send " "} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "xget /interfaces/interface\[name='eth1'\]/b\[key1='one'\]\[key2='two'\]/bar\n"} timeout {exit 1} } expect { "world" {send " "} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "xget /interfaces/interface\[name='eth1'\]/b\[key1='A'\]\[key2='B'\]/bar\n"} timeout {exit 1} } expect { "WORLD" {send " "} timeout {exit 1} } # create empty presence container 'd' followed with leaf - starts expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "merge /interfaces/interface -- name=eth2 type=ethernetCsmacd d b\[key1='one'\]\[key2='two'\]/bar=world b\[key1='A'\]\[key2='B'\]/bar=WORLD\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "xget /interfaces/interface\[name='eth2'\]/b\[key1='A'\]\[key2='B'\]/bar\n"} timeout {exit 1} } expect { "WORLD" {send " "} timeout {exit 1} } # create empty container 'a' followed with leaf - ends expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "quit\n"} timeout {exit 1} } expect { eof {exit 0} timeout {exit 1} } ././@LongLink0000644000000000000000000000015400000000000011603 Lustar rootrootyuma123_2.14/netconf/test/yangcli/non-interactive-container-edits/test-non-interactive-container-edits.yangyuma123_2.14/netconf/test/yangcli/non-interactive-container-edits/test-non-interactive-container-edi0000664000175000017500000000226714770023131034112 0ustar vladimirvladimirmodule test-non-interactive-container-edits { namespace "http://yuma123.org/ns/test-non-interactive-container-edits"; prefix tnice; import ietf-interfaces { prefix if; } import mod2 { prefix m2; } organization "yuma123.org"; description "Model for testing yangcli non-interactive container edits. e.g. create /interfaces/interface -- name=eth1 type=ethernetCsmacd a/foo=hello b[key1='one'][key2='two']/bar=world create /interfaces/interface[name='eth1']/c -- c1/bar=world create /interfaces/interface -- name=eth2 type=ethernetCsmacd d b[key1='one'][key2='two']/bar=world"; revision 2018-10-06 { description "Added presence container 'd' to expose bug entering valueless containers as parameter."; } augment "/if:interfaces/if:interface" { container a { leaf foo { type string; } } list b { key "key1 key2"; leaf key1 { type string; } leaf key2 { type string; } leaf bar { type string; } } uses m2:gr0; container d { presence "case with presence container parameter"; } } } yuma123_2.14/netconf/test/yangcli/non-interactive-container-edits/mod2.yang0000664000175000017500000000063514770023131027104 0ustar vladimirvladimirmodule mod2 { namespace "urn:yuma123.org:non-interactive-container-edits:mod2"; prefix m2; import ietf-interfaces { prefix if; } organization "yuma123.org"; description "external grouping gr0"; revision 2018-09-25 { description "1.st version"; } grouping gr0 { container c { container c1 { leaf bar { type string; } } } } } yuma123_2.14/netconf/test/yangcli/non-interactive-container-edits/session.create-unknown-parm.exp0000775000175000017500000000076714770023131033471 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /interfaces/interface\[name='eth1'\] -- typee=ethernetCsmacd\n"} timeout {exit 1} } expect { "Error: Unknown parameter (typee=ethernetCsmacd)" {send " "} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "quit\n"} timeout {exit 1} } expect { eof {exit 0} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/non-interactive-container-edits/run.sh0000775000175000017500000000324414770023131026525 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cp *.yang tmp cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana_if_type.fxs confdc -c test-non-interactive-container-edits.yang --yangpath /usr/share/yuma/modules/ietf -o test-non-interactive-container-edits.fxs confdc -c mod2.yang --yangpath /usr/share/yuma/modules/ietf -o mod2.fxs export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --module=test-non-interactive-container-edits.yang --module=./mod2.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 expect session.create-unknown-parm.exp expect session.create.exp expect session.replace.exp expect session.delete.exp kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/test-external-file-vars0000775000175000017500000000005614770023131023600 0ustar vladimirvladimir#!/bin/bash -e cd external-file-vars ./run.sh yuma123_2.14/netconf/test/yangcli/test-netconf-notifications0000775000175000017500000000006114770023131024367 0ustar vladimirvladimir#!/bin/bash -e cd netconf-notifications ./run.sh yuma123_2.14/netconf/test/yangcli/memory-leak/0000775000175000017500000000000014770023131021406 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/memory-leak/session.connect-quit.exp0000664000175000017500000000043414770023131026220 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "quit\n"} timeout {exit 1} } expect { "Error: memory leak" {exit 1} timeout {exit 1} eof {exit 0} } yuma123_2.14/netconf/test/yangcli/memory-leak/session.start-quit.exp0000664000175000017500000000024214770023131025721 0ustar vladimirvladimirspawn yangcli expect { "yangcli>" {send "quit\n"} timeout {exit 1} } expect { "Error: memory leak" {exit 1} timeout {exit 1} eof {exit 0} } yuma123_2.14/netconf/test/yangcli/memory-leak/run.sh0000775000175000017500000000252114770023131022551 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cp *.yang tmp cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana_if_type.fxs export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 expect session.start-quit.exp expect session.connect-quit.exp kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/fill/0000775000175000017500000000000014770023131020112 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/fill/mod1.yang0000664000175000017500000000053714770023131021637 0ustar vladimirvladimirmodule mod1 { namespace "http://yuma123.org/ns/test/yangcli/fill/mod1"; prefix m1; grouping test-list { container list { list list-entry { leaf a { type string; } leaf b { type string; } } } } rpc test-module-list-input { input { uses test-list; } } } yuma123_2.14/netconf/test/yangcli/fill/run.sh0000775000175000017500000000170214770023131021255 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c mod1.yang --yangpath -o mod1.fxs export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=mod1.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 expect session.exp kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/fill/session.exp0000664000175000017500000000252614770023131022320 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --echo-requests=true --echo-replies=true --display-mode=xml --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {sleep 1} timeout {exit 1} } send "\@tmp/x.xml = fill test-module-list-input optional\n" expect { "Fill non-mandatory container list?" {sleep 1} timeout {exit 1} } expect { "Enter Y for yes" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER):fill>" {sleep 1} timeout {exit 1} } send "Y\n" expect { "Enter string value for leaf " {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER):fill>" {sleep 1} timeout {exit 1} } send "foo1\n" expect { "Enter string value for leaf " {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER):fill>" {sleep 1} timeout {exit 1} } send "bar1\n" expect { "Enter Y for yes" {sleep 1} timeout {exit 1} } send "N\n" expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {sleep 1} timeout {exit 1} } send "test-module-list-input \@tmp/x.xml\n" expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {sleep 1} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } send "quit\n" expect { eof {exit 0} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/test-memory-leak0000775000175000017500000000004714770023131022312 0ustar vladimirvladimir#!/bin/bash -e cd memory-leak ./run.sh yuma123_2.14/netconf/test/yangcli/external-file-vars/0000775000175000017500000000000014770023131022674 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/external-file-vars/filter.xml0000664000175000017500000000036214770023131024704 0ustar vladimirvladimir yuma123_2.14/netconf/test/yangcli/external-file-vars/run.sh0000775000175000017500000000257714770023131024052 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana_if_type.fxs export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD= /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 yangcli --server=${NCSERVER} --ncport=${NCPORT} --user=${NCUSER} --password=${NCPASSWORD} --batch-mode --run-script=script.yangcli #kill $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/external-file-vars/script.yangcli0000664000175000017500000000007614770023131025553 0ustar vladimirvladimir$filter=@./filter.xml create-subscription filter=$filter quit yuma123_2.14/netconf/test/yangcli/identical-node-names/0000775000175000017500000000000014770023131023144 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/identical-node-names/README0000664000175000017500000000052414770023131024025 0ustar vladimirvladimirWhen 2 modules define child nodes with identical names at the same parent yangcli should not accept unprefixed name but return error and report that the module name should be used as prefix to resolve the correct target. An exception to this rule should be made when the node is part of a case and nodes part of that case are already used. yuma123_2.14/netconf/test/yangcli/identical-node-names/mod1.yang0000664000175000017500000000111314770023131024660 0ustar vladimirvladimirmodule mod1 { yang-version 1.1; namespace "http://yuma123.org/ns/test/yangcli/identical-node-names/mod1"; prefix m1; organization "yuma123.org"; description 'Validation of handling of siblings with identical names but different module namespace.'; revision 2018-04-16 { description "Initial version"; } container bar { presence "/mod1:bar is present"; } container foo { choice choice1 { case case1 { leaf foo { type string; } leaf bar { type string; } } } } } yuma123_2.14/netconf/test/yangcli/identical-node-names/mod2.yang0000664000175000017500000000104314770023131024663 0ustar vladimirvladimirmodule mod2 { yang-version 1.1; namespace "http://yuma123.org/ns/test/yangcli/identical-node-names/mod2"; prefix m2; import mod1 { prefix m1; } organization "yuma123.org"; description 'Validation of handling of siblings with identical names but different module namespace.'; revision 2018-04-16 { description "Initial version"; } container bar { presence "/mod2:bar is present"; } augment /m1:foo/m1:choice1 { case case2 { leaf bar { type string; } } } } yuma123_2.14/netconf/test/yangcli/identical-node-names/run.sh0000775000175000017500000000216114770023131024307 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c ../mod1.yang --yangpath .. -o mod1.fxs confdc -c ../mod2.yang --yangpath .. -o mod2.fxs export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm /tmp/ncxserver.sock || true # /usr/sbin/netconfd --module=./mod1.yang --module=./mod2.yang --no-startup --superuser=$USER /usr/sbin/netconfd --module=./mod1.yang --module=./mod2.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 expect session.exp kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/identical-node-names/session.exp0000664000175000017500000000455214770023131025353 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /foo/bar value=\'hello\'\n"} timeout {exit 1} } expect { "Error: Multiple object matches for node 'bar' in expr '/foo/bar': mod1:bar, mod2:bar" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /foo -- m1:bar=hello\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "merge /foo -- mod2:bar=hello\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "discard-changes\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /foo/m2:bar value=\'hello\'\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /bar\n"} timeout {exit 1} } expect { "Error: Multiple object matches for node 'bar' in expr '/bar': mod1:bar, mod2:bar" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /m1:bar\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /m2:bar\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "delete /mod1:bar\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "delete /mod2:bar\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "quit\n"} timeout {exit 1} } expect { eof {exit 0} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/identical-identity-names/0000775000175000017500000000000014770023131024050 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/identical-identity-names/README0000664000175000017500000000031314770023131024725 0ustar vladimirvladimirWhen 2 modules define identities with identical names yangcli should not accept unprefixed name but return error and report that the module name should be used as prefix to resolve the correct identity. yuma123_2.14/netconf/test/yangcli/identical-identity-names/mod1.yang0000664000175000017500000000102414770023131025565 0ustar vladimirvladimirmodule mod1 { yang-version 1.1; namespace "http://yuma123.org/ns/test/yangcli/identical-identity-names/mod1"; prefix m1; organization "yuma123.org"; description 'Validation of handling of identities with identical names but different module namespace.'; revision 2018-06-27 { description "Initial version"; } identity mybase; identity myidentity { base "mybase"; } container mycontainer { leaf myleaf { type identityref { base "mybase"; } } } } yuma123_2.14/netconf/test/yangcli/identical-identity-names/mod2.yang0000664000175000017500000000066714770023131025602 0ustar vladimirvladimirmodule mod2 { yang-version 1.1; namespace "http://yuma123.org/ns/test/yangcli/identical-identity-names/mod2"; prefix m2; import mod1 { prefix m1; } organization "yuma123.org"; description 'Validation of handling of identities with identical names but different module namespace.'; revision 2018-06-27 { description "Initial version"; } identity myidentity { base "m1:mybase"; } } yuma123_2.14/netconf/test/yangcli/identical-identity-names/run.sh0000775000175000017500000000216114770023131025213 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c ../mod1.yang --yangpath .. -o mod1.fxs confdc -c ../mod2.yang --yangpath .. -o mod2.fxs export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm /tmp/ncxserver.sock || true # /usr/sbin/netconfd --module=./mod1.yang --module=./mod2.yang --no-startup --superuser=$USER /usr/sbin/netconfd --module=./mod1.yang --module=./mod2.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 expect session.exp kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/identical-identity-names/session.exp0000664000175000017500000000160414770023131026252 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /mycontainer/myleaf value=\'myidentity\'\n"} timeout {exit 1} } expect { "Error: Multiple identities match identityref value \'myidentity\': \'mod1:myidentity\', \'mod2:myidentity\'" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /mycontainer/myleaf value=\'mod1:myidentity\'\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "quit\n"} timeout {exit 1} } expect { eof {exit 0} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/list-key-with-spaces/0000775000175000017500000000000014770023131023152 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/list-key-with-spaces/mod1.yang0000664000175000017500000000060614770023131024674 0ustar vladimirvladimirmodule mod1 { yang-version 1.1; namespace "http://yuma123.org/ns/test/yangcli/tab-completion-union/mod1"; prefix m1; organization "yuma123.org"; description 'List key with spaces testing module'; revision 2024-08-06 { description "Initial version"; } container u1 { list u2 { key name; leaf name { type string; } } } } yuma123_2.14/netconf/test/yangcli/list-key-with-spaces/run.sh0000775000175000017500000000170414770023131024317 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confdc -c mod1.yang --yangpath . -o mod1.fxs confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --no-startup --module=mod1.yang --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 expect session.exp kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/list-key-with-spaces/session.exp0000664000175000017500000000072214770023131025354 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /u1/u2\[name=\"foo bar\"\]\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "commit\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "quit\n"} timeout {exit 1} } expect { eof {exit 0} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/test-feature-depending-completion0000775000175000017500000000007014770023131025621 0ustar vladimirvladimir#!/bin/bash -e cd feature-depending-completion ./run.sh yuma123_2.14/netconf/test/yangcli/Makefile.am0000775000175000017500000000064614770023131021231 0ustar vladimirvladimirTESTS= \ test-session-modules-list \ test-interactive-container-edits \ test-cancel-fill \ test-non-interactive-container-edits \ test-memory-leak \ test-netconf-notifications \ test-feature-depending-completion \ test-mutikey-list-tab-completion \ test-unversioned-module-imports \ test-identical-node-names \ test-identical-identity-names \ test-external-file-vars \ test-mgrload \ test-fill \ test-list-key-with-spaces yuma123_2.14/netconf/test/yangcli/test-identical-identity-names0000775000175000017500000000006414770023131024753 0ustar vladimirvladimir#!/bin/bash -e cd identical-identity-names ./run.sh yuma123_2.14/netconf/test/yangcli/interactive-container-edits/0000775000175000017500000000000014770023131024567 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/interactive-container-edits/run.sh0000775000175000017500000000242514770023131025735 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana_if_type.fxs export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 expect session.exp kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/interactive-container-edits/session.exp0000664000175000017500000000366714770023131027004 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "xget /\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "crea\t\t"} timeout {exit 1} } expect { "te" {sleep 1} timeout {exit 1} } send "\t" expect { "create" {sleep 1} timeout {exit 1} } expect { "create-subscription" {sleep 1} timeout {exit 1} } send " /interf\t" expect { "aces" {sleep 1} timeout {exit 1} } send "interf\t" expect { "ace" {sleep 1} timeout {exit 1} } send "\n" expect { "Enter string value for leaf " {sleep 1} timeout {exit 1} } send "foo\n" expect { "Enter identityref value for leaf " {sleep 1} timeout {exit 1} } send "\t\t" expect { "ethernetCsmacd" {sleep 1} timeout {exit 1} } send "ethernetC\t" expect { "smacd" {sleep 1} timeout {exit 1} } send "\n" expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } send "comm\t" expect { "it " {sleep 1} timeout {exit 1} } send "\n" expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "get-config\n"} timeout {exit 1} } expect { "1\: case candidate\:" {sleep 1} timeout {exit 1} } expect { "Enter choice number" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER):get-config>" {send "2\n"} timeout {exit 1} } expect { "foo" {sleep 1} timeout {exit 1} } send "delete /interfaces/interface\[n\t" expect { "ame" {sleep 1} timeout {exit 1} } send "='foo']\n" expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } send "commit\n" expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "quit\n"} timeout {exit 1} } expect { eof {exit 0} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/session-modules-list/0000775000175000017500000000000014770023131023266 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/session-modules-list/test-session-modules-list.py0000664000175000017500000001144314770023131030722 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse DEBUG = False rpc_discard_changes = """ """ rpc_get_modules_state = """ """ # TESTS = list-of-test-details = [ , ... ] # test-details = { 'RPC': , # 'edit-config-results': , # 'expected-results': , # 'unexpected-results': , # 'name': } tests = [ { 'RPC': rpc_get_modules_state, 'expected-results': [], 'unexpected-results': [], 'name': 'Check modules-state for deviation info', 'edit-config-results': [ '//rpc-reply' ] } ] # Examine the return value of _XSLTResultTree.xpath() and decide if # evaluating the xpath expression reflects a successful test run. def eval_xpath_results(reply, hdr, exp): rv = 0 errmsg = '%s: failed to evaluate XPATH expression "%s"' res = reply.xpath(exp) if type(res) == type([]) and len(res) == 0: # res[*] --> if DEBUG: print((errmsg % (hdr, exp))) rv = 2 elif type(res) == type(True) and res == False: if DEBUG: print((errmsg % (hdr, exp))) rv = 2 #elif type(res) == type(1.0): #elif type(res) == type(""): return rv # returns (status, reply-xml) def send_rpc(conn, rpcxml, expected=None): rv = 0 reply = conn.rpc(rpcxml) if not reply: return (1, None) if expected is not None: for exp in expected: rv = eval_xpath_results(reply, 'send_rpc', exp) if rv > 0: break else: rv = eval_xpath_results(reply, 'send_rpc', '//ok') return (rv, reply) def load_client_module_list(filename): with open(filename) as f: lines = f.readlines() f.close() return [x.strip() for x in lines] def parse_server_module_list(reply): modlist = [] modules = reply.xpath('//module') for m in modules: name = m.find('name') revision = m.find('revision') modlist.append('%s@%s' % (name.text, revision.text)) return modlist def compare_module_lists(server_module_list, client_module_list): # Check that every module in the server list appears in the # client list. ok = True for servermod in server_module_list: # One exception . . . if servermod.find('ietf-netconf@') == 0: pass elif not servermod in client_module_list: ok = False print(('Client is missing module %s' % (servermod))) return ok def main(): print("#Description: compare list of supported modules reported by server to modules loaded by client.") parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") parser.add_argument("--module-list", help="Client's module list - one module@revision per line") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password if args.module_list == None or args.module_list == "": module_list_fn = 'tmp/modlist' else: module_list_fn = args.module_list client_module_list = load_client_module_list(module_list_fn) if client_module_list == None: print("[FAILED] load client modules list") return(-1) conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print(("[FAILED] Connecting to server=%s:" % {'server':server})) return(-1) print(("[OK] Connecting to server=%(server)s:" % {'server':server})) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) # receive result=conn.receive() # get the modules list (rv, reply) = send_rpc(conn, tests[0]['RPC'], tests[0]['edit-config-results']) if rv > 0: print(('%s: sending RPC failed.' % tests[0]['name'])) server_module_list = parse_server_module_list(reply) ok = compare_module_lists(server_module_list, client_module_list) return 0 if ok else 1 sys.exit(main()) yuma123_2.14/netconf/test/yangcli/session-modules-list/run.sh0000775000175000017500000000172614770023131024437 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd \ --verbose --foreground \ --addloadpath ${RUN_WITH_CONFD}/src/confd \ --addloadpath ${RUN_WITH_CONFD}/src/confd \ --addloadpath ${RUN_WITH_CONFD}/src/confd/yang \ --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa \ --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm -f /tmp/ncxserver.sock /usr/sbin/netconfd --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 1 expect get-client-modules-list.exp python test-session-modules-list.py --module-list tmp/modlist res=$? kill -KILL $SERVER_PID wait exit ${res} yuma123_2.14/netconf/test/yangcli/session-modules-list/get-client-modules-list.exp0000664000175000017500000000101114770023131030447 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "show modules\n"} timeout {exit 1} } set fp [open "tmp/modlist" "w"] expect -re ".*yangcli $env(NCUSER)@$env(NCSERVER)>" regsub {.*show modules} $expect_out(buffer) {} modlist regsub -all {\r} $modlist {} modlist regsub -all -line {^.*:} $modlist {} modlist regsub -all -line {^yangcli.*$} $modlist {} modlist puts -nonewline $fp $modlist close $fp yuma123_2.14/netconf/test/yangcli/unversioned-module-imports/0000775000175000017500000000000014770023131024503 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/unversioned-module-imports/README0000664000175000017500000000056514770023131025371 0ustar vladimirvladimirThis testcase validates that when modules of differing versions are reported yangcli downloads and uses the ones implemented on the server side. The testcase has server-modules and client-modules directories with different incompatible definitions. The testcase validates that the client side modules are not loaded and the server side modules are in fact the ones in use. yuma123_2.14/netconf/test/yangcli/unversioned-module-imports/server-modules/0000775000175000017500000000000014770023131027457 5ustar vladimirvladimir././@LongLink0000644000000000000000000000020000000000000011573 Lustar rootrootyuma123_2.14/netconf/test/yangcli/unversioned-module-imports/server-modules/test-unversioned-module-imports-top@2018-03-27.yangyuma123_2.14/netconf/test/yangcli/unversioned-module-imports/server-modules/test-unversioned-module-0000664000175000017500000000060514770023131034261 0ustar vladimirvladimirmodule test-unversioned-module-imports-top { namespace "http://yuma123.org/ns/test-unversioned-module-imports"; prefix tumit; organization "yuma123.org"; description "Model for testing yangcli unversioned module imports resolution"; revision 2018-03-27 { description "1.st version"; } container top { leaf foo { type string; } } } ././@LongLink0000644000000000000000000000020400000000000011577 Lustar rootrootyuma123_2.14/netconf/test/yangcli/unversioned-module-imports/server-modules/test-unversioned-module-imports-augment@2018-03-27.yangyuma123_2.14/netconf/test/yangcli/unversioned-module-imports/server-modules/test-unversioned-module-0000664000175000017500000000075314770023131034265 0ustar vladimirvladimirmodule test-unversioned-module-imports-augment { namespace "http://yuma123.org/ns/test-unversioned-module-imports-augmentation"; prefix tumia; import test-unversioned-module-imports-top { prefix tumit; } organization "yuma123.org"; description "Model for testing yangcli unversioned module imports resolution. Augment part."; revision 2018-03-27 { description "1.st version"; } augment /tumit:top { leaf foo2 { type string; } } } yuma123_2.14/netconf/test/yangcli/unversioned-module-imports/client-modules/0000775000175000017500000000000014770023131027427 5ustar vladimirvladimir././@LongLink0000644000000000000000000000020000000000000011573 Lustar rootrootyuma123_2.14/netconf/test/yangcli/unversioned-module-imports/client-modules/test-unversioned-module-imports-top@2018-03-28.yangyuma123_2.14/netconf/test/yangcli/unversioned-module-imports/client-modules/test-unversioned-module-0000664000175000017500000000107614770023131034234 0ustar vladimirvladimirmodule test-unversioned-module-imports-top { namespace "http://yuma123.org/ns/test-unversioned-module-imports"; prefix tumit; organization "yuma123.org"; description "Model for testing yangcli unversioned module imports resolution"; revision 2018-03-28 { description "2.nd version. This newer version of the modules (with bar and bar2 leafs instead of foo and foo2) is available on the client side."; } revision 2018-03-27 { description "1.st version"; } container top { leaf bar { type string; } } } ././@LongLink0000644000000000000000000000020400000000000011577 Lustar rootrootyuma123_2.14/netconf/test/yangcli/unversioned-module-imports/client-modules/test-unversioned-module-imports-augment@2018-03-28.yangyuma123_2.14/netconf/test/yangcli/unversioned-module-imports/client-modules/test-unversioned-module-0000664000175000017500000000124414770023131034231 0ustar vladimirvladimirmodule test-unversioned-module-imports-augment { namespace "http://yuma123.org/ns/test-unversioned-module-imports-augmentation"; prefix tumia; import test-unversioned-module-imports-top { prefix tumit; } organization "yuma123.org"; description "Model for testing yangcli unversioned module imports resolution. Augment part."; revision 2018-03-28 { description "2.nd version. This newer version of the modules (with bar and bar2 leafs instead of foo and foo2) is available on the client side."; } revision 2018-03-27 { description "1.st version"; } augment /tumit:top { leaf bar2 { type string; } } } yuma123_2.14/netconf/test/yangcli/unversioned-module-imports/run.sh0000775000175000017500000000245214770023131025651 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cp *.yang tmp cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c ../server-modules/test-unversioned-module-imports@2018-03-27.yang -o test-unversioned-module-imports@2018-03-27.fxs confdc -c ../server-modules/test-unversioned-module-imports-augment@2018-03-27.yang -o test-unversioned-module-imports-augment@2018-03-27.fxs export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --modpath=./server-modules --module=./server-modules/test-unversioned-module-imports-augment@2018-03-27.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 cd client-modules expect ../session.exp cd .. kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/unversioned-module-imports/session.exp0000664000175000017500000000163414770023131026710 0ustar vladimirvladimirspawn yangcli --modpath=. --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /top/bar value=\"hello\"\n"} timeout {exit 1} } expect { "Error" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /top/bar2 value=\"hello\"\n"} timeout {exit 1} } expect { "Error" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /top/foo value=\"hello\"\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "create /top/foo2 value=\"hello\"\n"} timeout {exit 1} } expect { "RPC OK Reply" {sleep 1} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "quit\n"} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/test-identical-node-names0000775000175000017500000000006014770023131024043 0ustar vladimirvladimir#!/bin/bash -e cd identical-node-names ./run.sh yuma123_2.14/netconf/test/yangcli/configure.ac0000664000175000017500000000070614770023131021455 0ustar vladimirvladimirAC_INIT([yangcli-test-suite], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules" AC_SUBST(yangdir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1 -DRELEASE=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/netconf/test/yangcli/test-tab-completion-union0000775000175000017500000000006014770023131024126 0ustar vladimirvladimir#!/bin/bash -e cd tab-completion-union ./run.sh yuma123_2.14/netconf/test/yangcli/test-cancel-fill0000775000175000017500000000004714770023131022241 0ustar vladimirvladimir#!/bin/bash -e cd cancel-fill ./run.sh yuma123_2.14/netconf/test/yangcli/mutikey-list-tab-completion/0000775000175000017500000000000014770023131024537 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangcli/mutikey-list-tab-completion/run.sh0000775000175000017500000000160214770023131025701 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc export NCSERVER=localhost export NCPORT=2022 export NCUSER=admin export NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true export NCSERVER=localhost export NCPORT=830 export NCUSER=${USER} export NCPASSWORD="" rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 expect session.exp kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/yangcli/mutikey-list-tab-completion/session.exp0000664000175000017500000000107514770023131026743 0ustar vladimirvladimirspawn yangcli --user=$env(NCUSER) --server=$env(NCSERVER) --ncport=$env(NCPORT) --password=$env(NCPASSWORD) expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "xget /modules-sta\t"} timeout {exit 1} } expect { "te/" {send "module\[nam\t"} timeout {exit 1} } expect { "e" {send "=ietf-yang-library\]\[rev\t"} timeout {exit 1} } expect { "ision" {send "=2016-06-21\]\n"} timeout {exit 1} } expect { "yangcli $env(NCUSER)@$env(NCSERVER)>" {send "quit\n"} timeout {exit 1} } expect { eof {exit 0} timeout {exit 1} } yuma123_2.14/netconf/test/yangcli/test-session-modules-list0000775000175000017500000000006014770023131024165 0ustar vladimirvladimir#!/bin/bash -e cd session-modules-list ./run.sh yuma123_2.14/netconf/test/yangcli/test-unversioned-module-imports0000775000175000017500000000006614770023131025410 0ustar vladimirvladimir#!/bin/bash -e cd unversioned-module-imports ./run.sh yuma123_2.14/netconf/test/yangcli/test-list-key-with-spaces0000775000175000017500000000006014770023131024051 0ustar vladimirvladimir#!/bin/bash -e cd list-key-with-spaces ./run.sh yuma123_2.14/netconf/test/yangcli/test-mgrload0000775000175000017500000000004314770023131021511 0ustar vladimirvladimir#!/bin/bash -e cd mgrload ./run.sh yuma123_2.14/netconf/test/yangcli/test-non-interactive-container-edits0000775000175000017500000000007314770023131026262 0ustar vladimirvladimir#!/bin/bash -e cd non-interactive-container-edits ./run.sh yuma123_2.14/netconf/test/litenc/0000775000175000017500000000000014770023131017014 5ustar vladimirvladimiryuma123_2.14/netconf/test/litenc/litenc.py0000664000175000017500000000620014770023131020642 0ustar vladimirvladimirimport paramiko import socket import traceback import os class litenc: def __init__(self): self.t = None self.chan = None self.sock = None self.receive_total_data = "" def connect(self, user=os.getenv('USER'), server="localhost", port=830, password=None, private_key=os.environ['HOME']+"/.ssh/id_rsa", public_key=os.environ['HOME']+"/.ssh/id_rsa.pub", timeout=30): try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(timeout) self.sock.connect((server, port)) except Exception as e: print('*** Connect failed: ' + str(e)) traceback.print_exc() return -1 #self.sock.settimeout(None) #paramiko.util.log_to_file("filename.log") try: self.t = paramiko.Transport(self.sock) try: self.t.start_client() except paramiko.SSHException: print('*** SSH negotiation failed.') return -1 except Exception as e: print('*** Connect failed: ' + str(e)) traceback.print_exc() return -1 # TODO: check server's host key -- this is important. key = self.t.get_remote_server_key() if(password==None): self.t.auth_publickey(user, paramiko.RSAKey.from_private_key_file(private_key)) else: self.t.auth_password(user, password) if not self.t.is_authenticated(): print('*** Authentication failed. :(') self.t.close() return -1 self.chan = self.t.open_session() self.chan.settimeout(timeout) self.chan.set_name("netconf") self.chan.invoke_subsystem("netconf") return 0 def send(self, xml): try: data = xml + "]]>]]>" while data: n = self.chan.send(data) if n <= 0: return -1 data = data[n:] except Exception as e: print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e)) traceback.print_exc() return -1 return 0 def receive(self): while True: xml_len = self.receive_total_data.find("]]>]]>") if xml_len >= 0: reply_xml = self.receive_total_data[:xml_len] self.receive_total_data = self.receive_total_data[xml_len+len("]]>]]>"):] break try: data = self.chan.recv(4096) except socket.timeout: return (1,[]) if data: self.receive_total_data = self.receive_total_data + data.decode('ascii') else: return (-1,[]) return (0,reply_xml) def rpc(self, xml, message_id=1): ret=self.send(''''''+xml+"") if(ret!=0): return (ret,[]) (ret,reply_xml)=self.receive() return (ret,reply_xml) def close(self): self.chan.close() self.t.close() yuma123_2.14/netconf/test/litenc/litenc_lxml.py0000664000175000017500000000307514770023131021705 0ustar vladimirvladimirimport litenc import lxml from lxml import etree def strip_namespaces(tree): xslt=''' ''' xslt_doc = lxml.etree.fromstring(xslt) transform = lxml.etree.XSLT(xslt_doc) tree = transform(tree) return tree class litenc_lxml(): def __init__(self, litenc, strip_namespaces=False): self.litenc=litenc self.strip_namespaces=strip_namespaces def receive(self): (ret,reply_xml)=self.litenc.receive() if(ret!=0): return None reply_lxml = lxml.etree.fromstring(reply_xml.encode('ascii')) if(self.strip_namespaces): reply_lxml=strip_namespaces(reply_lxml) return reply_lxml def rpc(self, xml, message_id=1): ret=self.litenc.send(''''''+xml+"") if(ret!=0): return None reply_lxml=self.receive() if(self.strip_namespaces): reply_lxml=strip_namespaces(reply_lxml) return reply_lxml def send(self, xml): return self.litenc.send(xml) yuma123_2.14/netconf/test/Doxyfile0000664000175000017500000017756414770023131017270 0ustar vladimirvladimir# Doxyfile 1.6.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it parses. # With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = YES # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = YES # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = YES # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES yuma123_2.14/netconf/test/netconfd/0000775000175000017500000000000014770023131017336 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/test-notification-in-grouping0000775000175000017500000000010414770023131025156 0ustar vladimirvladimir#!/bin/sh set -e cd yang-1dot1 ./test-notification-in-grouping.sh yuma123_2.14/netconf/test/netconfd/test-perf0000775000175000017500000000004014770023131021167 0ustar vladimirvladimir#!/bin/bash -e cd perf ./run.sh yuma123_2.14/netconf/test/netconfd/test-multi-instance0000775000175000017500000000005214770023131023172 0ustar vladimirvladimir#!/bin/bash -e cd multi-instance ./run.sh yuma123_2.14/netconf/test/netconfd/instance-identifier/0000775000175000017500000000000014770023131023262 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/instance-identifier/test-instance-identifier.yang0000664000175000017500000000077614770023131031055 0ustar vladimirvladimirmodule test-instance-identifier { yang-version 1.1; namespace "http://yuma123.org/ns/test-instance-identifier"; prefix tii; organization "yuma123.org"; description "Part of the instance-identifier test."; revision 2017-10-21 { description "Initial version"; } container top { list list { key "idx"; leaf idx { type uint32; } } list id-list { key "id"; leaf id { type instance-identifier; } } } } yuma123_2.14/netconf/test/netconfd/instance-identifier/session.litenc.py0000664000175000017500000001234714770023131026603 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Test instance-identifier built-in type. #Procedure: #1 - Create /top/list[idx="4"] and /top/id-list[id="/top/list[idx=\"4\"]"]. #2 - Try to create /top/id-list[id="/top/list[idx=\"5\"]"]. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw,strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") edit_config_rpc = """ merge set """ print("edit-config ...") result = conn.rpc(edit_config_rpc) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) ok = result.xpath('//ok') assert(len(ok)==1) print('''#1 - Create /top/list[idx="4"] and /top/id-list[id="/top/list[idx=\"4\"]"].''') edit_config_rpc = """ merge set 4 /tii:top/tii:list[tii:idx="4"] """ # # /top/list[idx=4] # print("edit-config ...") result = conn.rpc(edit_config_rpc) print(result) ok = result.xpath('//ok') assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) ok = result.xpath('//ok') assert(len(ok)==1) print('''#2 - Try to create /top/id-list[id="/top/list[idx=\"5\"]"].''') edit_config_rpc = """ merge set /tii:top/tii:list[tii:idx="5"] """ print("edit-config ...") result = conn.rpc(edit_config_rpc) ok = result.xpath('//ok') assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) print(result) ok = result.xpath('//ok') assert(len(ok)==0) discard_changes_rpc = """ """ print("discard-changes ...") result = conn.rpc(discard_changes_rpc) ok = result.xpath('//ok') assert(len(ok)==1) edit_config_rpc = """ merge set """ print("edit-config ...") result = conn.rpc(edit_config_rpc) result = conn.rpc(commit_rpc) ok = result.xpath('//ok') commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) ok = result.xpath('//ok') assert(len(ok)==1) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/instance-identifier/run.sh0000775000175000017500000000172314770023131024430 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cp *.yang tmp cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c test-instance-identifier.yang --yangpath . -o test-instance-identifier.yang.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=test-instance-identifier.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/yang-validation/0000775000175000017500000000000014770023131022424 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-validation/update-module-repository.sh0000775000175000017500000000036214770023131027746 0ustar vladimirvladimir#!/bin/sh -e rm -rf tmp || true mkdir tmp cd tmp wget http://www.claise.be/YANG-RFC.tar tar -C ../../../../modules/ietf/ -xvf YANG-RFC.tar wget http://www.claise.be/YANG-drafts.tar tar -C ../../../../modules/ietf-draft/ -xvf YANG-drafts.tar yuma123_2.14/netconf/test/netconfd/yang-validation/run.sh0000775000175000017500000000276514770023131023601 0ustar vladimirvladimir#!/bin/bash #./update-module-repository.sh rm -rf tmp || true mkdir tmp OKS=0 FAILS=0 modpath=/usr/share/yuma/modules for filespec in `find /usr/share/yuma/modules/ -name '*.yang' | sort` ; do module=`basename $filespec .yang` echo $module >&2 is_submodule="`head -n 1 ${filespec} | grep submodule`" || true if [ "${is_submodule}" != "" ] ; then echo "Skip submodule: ${module}" continue fi if [ "$RUN_WITH_PYANG" != "" ] ; then pyang --path ${modpath}/ ${filespec} 1>&2 RES=$? elif [ "$RUN_WITH_YANGDUMP" != "" ] ; then yangdump --modpath=${modpath} ${filespec} 1>&2 RES=$? elif [ "$RUN_WITH_YANGLINT" != "" ] ; then yanglint --path=${modpath} ${filespec} 1>&2 RES=$? elif [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true source $RUN_WITH_CONFD/confdrc for subdir in `find ${modpath} -type d` do confd_modpath=${confd_modpath}${subdir}":" done confd_modpath=${confd_modpath}"." cmd="confdc -c ${filespec} --yangpath ${confd_modpath} -o tmp/${module}.fxs" echo $cmd >&2 $cmd >&2 RES=$? else cmd="/usr/sbin/netconfd --validate-config-only --startup-error=stop --module=${filespec} --no-startup --modpath=${modpath}" echo $cmd >&2 $cmd >&2 RES=$? fi if [ "${RES}" = "0" ] ; then OKS=$((${OKS}+1)) echo OK: $module else FAILS=$((${FAILS}+1)) echo FAIL: $module fi done echo OKS=$OKS echo FAILS=$FAILS exit $FAILS yuma123_2.14/netconf/test/netconfd/deviation-delete-ext/0000775000175000017500000000000014770023131023356 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/deviation-delete-ext/README0000664000175000017500000000027214770023131024237 0ustar vladimirvladimirThis regression test ensures that netconfd does not abort when an external deviation deletes a default value. See commit 7ca55483c98f ("fix abort when processing external deviations"). yuma123_2.14/netconf/test/netconfd/deviation-delete-ext/test-deviation-delete-ext.yang0000664000175000017500000000073314770023131031236 0ustar vladimirvladimirmodule test-deviation-delete-ext { yang-version 1.1; namespace "urn:labn:params:xml:ns:yang:test-deviation-delete-ext"; prefix devext; import ietf-interfaces { prefix if; } organization ""; contact ""; description "Create an external deviation that deletes a default value. Check later it see if netconfd crashed."; revision 2020-11-13 { reference ""; } deviation /if:interfaces/if:interface/if:enabled { deviate delete { default "true"; } } } yuma123_2.14/netconf/test/netconfd/deviation-delete-ext/test-deviation-delete-ext.sh0000775000175000017500000000104014770023131030705 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then exit 0 else modules="--module=test-deviation-delete-ext" killall -KILL netconfd > /dev/null 2>&1 || true rm -f /tmp/ncxserver.sock # Even with --validate-config-only, this will abort if the problem is # not fixed so don't bother starting a daemon we might have to kill at # the end of this script. /usr/sbin/netconfd --modpath=`pwd` ${modules} --no-startup --superuser=$USER \ --validate-config-only 1>tmp/server.log 2>&1 fi exit 0 yuma123_2.14/netconf/test/netconfd/xpath-deref/0000775000175000017500000000000014770023131021545 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/xpath-deref/session-2.litenc.py0000664000175000017500000001052114770023131025215 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Demonstrate that deref() works. #Procedure: #1 - Create interface foo of type ethernetCsmacd. #2 - Validate management-interface/mod2:ethernet-foo can be created. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") edit_config_rpc = """ merge set """ print("edit-config - delete /interfaces ...") result = conn.rpc(edit_config_rpc) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) edit_config_rpc = """ merge set foo ianaift:ethernetCsmacd true """ print("edit-config - create 'foo and bar' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) edit_config_rpc = """ merge set foo ianaift:ethernetCsmacd 123 """ print("edit-config - create mgmt-interface=foo ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/xpath-deref/mod2.yang0000664000175000017500000000116214770023131023266 0ustar vladimirvladimirmodule mod2 { yang-version 1.1; namespace "http://yuma123.org/ns/test-xpath-deref/m2"; prefix m2; import test-xpath-deref { prefix m1; } import ietf-interfaces { prefix if; } import iana-if-type { prefix ianaift; } organization "yuma123.org"; description 'Testcase with deref() argument differen then current() and XPath used in when statement.'; revision 2020-03-29 { description "Initial version"; } augment "/m1:mgmt-interface" { when "deref(./m1:name)/../if:type = 'ianaift:ethernetCsmacd'"; leaf ethernet-foo { type string; } } } yuma123_2.14/netconf/test/netconfd/xpath-deref/session.litenc.py0000664000175000017500000001352714770023131025067 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Demonstrate that deref() works. #Procedure: #1 - Create interfaces foo enabled=true and bar enabled=false. #2 - Validate management-interface/name=foo can be created. #3 - Validate management-interface/name=bar can not be created. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") edit_config_rpc = """ merge set """ print("edit-config - delete /interfaces ...") result = conn.rpc(edit_config_rpc) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) edit_config_rpc = """ merge set foo ianaift:ethernetCsmacd true bar ianaift:ethernetCsmacd false """ print("edit-config - create 'foo and bar' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) edit_config_rpc = """ merge set fooianaift:ethernetCsmacd """ print("edit-config - create mgmt-interface=foo ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) edit_config_rpc = """ merge set fooianaift:ethernetCsmacd """ print("edit-config - delete mgmt-interface ...") result = conn.rpc(edit_config_rpc) print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) edit_config_rpc = """ merge set barianaift:ethernetCsmacd """ print("edit-config - create mgmt-interface=bar ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)!=1) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/xpath-deref/test-xpath-deref.yang0000664000175000017500000000132214770023131025607 0ustar vladimirvladimirmodule test-xpath-deref { yang-version 1.1; namespace "http://yuma123.org/ns/test-xpath-deref"; prefix trm; import ietf-interfaces { prefix if; } organization "yuma123.org"; description "Part of the xpath-deref test."; revision 2017-07-14 { description "Initial version"; } container mgmt-interface { leaf name { type leafref { path "/if:interfaces/if:interface/if:name"; } } leaf type { type leafref { path "/if:interfaces/if:interface[if:name=current()/../name]/if:type"; } must 'deref(.)/../if:enabled = "true"' { error-message "The management interface cannot be disabled."; } } } } yuma123_2.14/netconf/test/netconfd/xpath-deref/run.sh0000775000175000017500000000252014770023131022707 0ustar vladimirvladimir#!/bin/bash -ex MODULES="\ /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang \ /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang \ ./test-xpath-deref.yang" SESSION_SCRIPT=session.litenc.py if [ "$#" -gt 0 ] ; then if [ "$1" == "2" ] ; then MODULES="${MODULES} ./mod2.yang" SESSION_SCRIPT=session-2.litenc.py fi fi if [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc for module in $MODULES ; do confdc -c $module --yangpath /usr/share/yuma/modules/ietf -o "`basename ${module}`".fxs done NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! else MODULE_ARGS="" for module in $MODULES ; do MODULE_ARGS="${MODULE_ARGS} --module=${module}" done killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd $MODULE_ARGS --no-startup --superuser=$USER 2>&1 1>server.log & SERVER_PID=$! fi sleep 3 python ${SESSION_SCRIPT} --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat server.log sleep 1 yuma123_2.14/netconf/test/netconfd/when/0000775000175000017500000000000014770023131020277 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/when/mod1.yang0000664000175000017500000000110714770023131022016 0ustar vladimirvladimirmodule mod1 { prefix m1; namespace "http://yuma123.org/ns/test/netconfd/when/mod1"; identity b; identity foo { base b; } identity bar { base b; } container top { container child { leaf type { type identityref { base b; } } choice top-type { case foos { when "type = 'm1:foo'"; leaf foo { type string; } } case bars { when "type = 'm1:bar'"; leaf bar { type string; } } } } } } yuma123_2.14/netconf/test/netconfd/when/session.yangcli.py0000664000175000017500000000332514770023131023764 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli from lxml import etree import yangrpc def main(): print(""" #Description: Validate leafs with failing when statements are removed from candidate #Procedure: #1 - Create /top/type = bar. #2 - Create '/top/foo'. #3 - Validate '/top/foo' is not present """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) time.sleep(1) print("hello") ok = yangcli(conn, '''create /top/child -- type=bar foo=1''').xpath('./ok') assert(len(ok)==1) result = yangcli(conn, "xget-config source=candidate /top") foo = result.xpath('./data/top/child/foo') assert(len(foo)==0) ok = yangcli(conn, '''create /top/child/bar value=1''').xpath('./ok') assert(len(ok)==1) print("done") return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/when/run.sh0000775000175000017500000000154014770023131021442 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c ../mod1.yang --yangpath .. -o mod1.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=mod1.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/test-val123-api0000775000175000017500000000004614770023131022020 0ustar vladimirvladimir#!/bin/bash -e cd val123-api ./run.sh yuma123_2.14/netconf/test/netconfd/deviation-in-modules-state/0000775000175000017500000000000014770023131024510 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/deviation-in-modules-state/test-deviation-in-modules-state.sh0000775000175000017500000000054714770023131033204 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp mkdir tmp killall -KILL netconfd || true rm -f /tmp/ncxserver.sock /usr/sbin/netconfd \ --deviation=./test-deviation-in-modules-state.yang \ --module=ietf-interfaces \ --no-startup \ --superuser=$USER \ 2>tmp/error.log \ 1>tmp/server.log & sleep 1 python test-deviation-in-modules-state.py res=$? kill %1 wait exit ${res} yuma123_2.14/netconf/test/netconfd/deviation-in-modules-state/test-deviation-in-modules-state.py0000664000175000017500000001061514770023131033214 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse DEBUG = False rpc_discard_changes = """ """ rpc_get_modules_state = """ """ # TESTS = list-of-test-details = [ , ... ] # test-details = { 'RPC': , # 'edit-config-results': , # 'expected-results': , # 'unexpected-results': , # 'name': } tests = [ { 'RPC': rpc_get_modules_state, 'expected-results': [ '//module[name=\'ietf-interfaces\']/deviation[name=\'test-deviation-in-modules-state\']' ], 'unexpected-results': [], 'name': 'Check modules-state for deviation info', 'edit-config-results': [ '//rpc-reply' ] } ] # Examine the return value of _XSLTResultTree.xpath() and decide if # evaluating the xpath expression reflects a successful test run. def eval_xpath_results(reply, hdr, exp): rv = 0 errmsg = '%s: failed to evaluate XPATH expression "%s"' res = reply.xpath(exp) if type(res) == type([]) and len(res) == 0: # res[*] --> if DEBUG: print((errmsg % (hdr, exp))) rv = 2 elif type(res) == type(True) and res == False: if DEBUG: print((errmsg % (hdr, exp))) rv = 2 #elif type(res) == type(1.0): #elif type(res) == type(""): return rv # returns (status, reply-xml) def send_rpc(conn, rpcxml, expected=None): rv = 0 reply = conn.rpc(rpcxml) if not reply: return (1, None) if expected is not None: for exp in expected: rv = eval_xpath_results(reply, 'send_rpc', exp) if rv > 0: break else: rv = eval_xpath_results(reply, 'send_rpc', '//ok') return (rv, reply) def run_one_test(conn, desc): (rv, reply) = send_rpc(conn, rpc_discard_changes) if rv > 0: print(('%s: sending discard-changes RPC failed.' % desc['name'])) return (desc['name'], rv) (rv, reply) = send_rpc(conn, desc['RPC'], desc['edit-config-results']) if rv > 0: print(('%s: sending RPC failed.' % desc['name'])) else: for exp in desc['expected-results']: rv = eval_xpath_results(reply, desc['name'], exp) if rv > 0: break if rv == 0: for exp in desc['unexpected-results']: rv = eval_xpath_results(reply, desc['name'], exp) rv = 0 if rv > 0 else 2 if rv > 0: break return (desc['name'], rv) def run_tests(conn, alltests): results = [] for t in alltests: results.append(run_one_test(conn, t)) return results def main(): print("#Description: Get /modules-state and check for deviation information") parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print(("[FAILED] Connecting to server=%s:" % {'server':server})) return(-1) print(("[OK] Connecting to server=%(server)s:" % {'server':server})) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) # receive result=conn.receive() results = run_tests(conn, tests) ok = True for x in results: print(("%-70.70s : %s" % (x[0], 'PASS' if x[1] == 0 else 'FAIL'))) if x[1] > 0: ok = False send_rpc(conn, rpc_discard_changes) return 0 if ok else 1 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/deviation-in-modules-state/test-deviation-in-modules-state.yang0000664000175000017500000000067014770023131033522 0ustar vladimirvladimirmodule test-deviation-in-modules-state { yang-version 1.1; namespace "urn:labn:params:xml:ns:yang:test-deviation-in-modules-state"; prefix dev2; import ietf-interfaces { prefix if; } organization ""; contact ""; description "Add an external deviation to ensure that it is applied."; revision 2020-07-28 { reference ""; } deviation /if:interfaces/if:interface/if:link-up-down-trap-enable { deviate not-supported; } } yuma123_2.14/netconf/test/netconfd/test-get-schema0000775000175000017500000000004614770023131022256 0ustar vladimirvladimir#!/bin/bash -e cd get-schema ./run.sh yuma123_2.14/netconf/test/netconfd/netconf-notifications/0000775000175000017500000000000014770023131023641 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/netconf-notifications/README0000664000175000017500000000207414770023131024524 0ustar vladimirvladimirFILES: * run.sh - shell script executing the testcase * session.litenc.py - python script connecting to the started netconfd server and generate sequence of actions to force the tested notifications to be sent. PURPOSE: Verify ietf-netconf-notifications (RFC6470) and NETCONF event notifications (RFC5277) implementation OPERATION: 1. Open session and 2. Open additional session and verify is sent with correct paramenters. 3. Close the additional session and verify is sent with correct parameters. 4. Add subtree filter to the create subscription filter so that only is sent. 5. (TODO) Make change to the configuration and verify is sent with correct parameters. 6. (TODO) Make change to the configuration with confirmed commit and verify notification is sent when the timeout elapses and the old configuration is restored. 7. (TODO) Load new module and verify is sent with correct parameters. yuma123_2.14/netconf/test/netconfd/netconf-notifications/session.litenc.py0000664000175000017500000001117514770023131027160 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import lxml import argparse sys.path.append("../../litenc") import litenc import litenc_lxml def main(): print(""" #Description: Verify netconf-* session-start, etc. notifications are implemented. #Procedure: #1 - Open session #1 and #2 - Open additional session #2 and verify is sent with correct paramenters on #1. #3 - TODO """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") parser.add_argument("--with-filter-subtree", action='store_true', help="when present create-subscription has a filter to receive only netconf-session-end") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn = litenc.litenc() ret = conn.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn_lxml=litenc_lxml.litenc_lxml(conn,strip_namespaces=True) ret = conn.send(""" urn:ietf:params:netconf:base:1.0 """) # urn:ietf:params:netconf:capability:notification:1.0 # urn:ietf:params:netconf:capability:interleave:1.0 # urn:ietf:params:xml:ns:yang:ietf-netconf-notifications?module=ietf-netconf-notifications&revision=2012-02-06 if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) filter="" if(args.with_filter_subtree): filter=""" """ ret = conn.send(""" %(filter)s """%{'filter':filter}) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn.receive() if ret != 0: print("[FAILED] Receiving reply") return(-1) print("[OK] Receiving reply =%(reply_xml)s:" % {'reply_xml':reply_xml}) conn2 = litenc.litenc() ret = conn2.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) ret = conn2.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn2.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) conn2_lxml=litenc_lxml.litenc_lxml(conn2,strip_namespaces=True) ret = conn2_lxml.rpc(""" """) if(not args.with_filter_subtree): notification_xml=conn_lxml.receive() if notification_xml == None: print("[FAILED] Receiving notification") return(-1) print(lxml.etree.tostring(notification_xml)) notification_xml=conn_lxml.receive() if notification_xml == None: print("[FAILED] Receiving notification") return(-1) print(lxml.etree.tostring(notification_xml)) notification_xml=litenc_lxml.strip_namespaces(notification_xml) match=notification_xml.xpath("/notification/netconf-session-end") assert(len(match)==1) print("[OK] Receiving ") return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/netconf-notifications/run.sh0000775000175000017500000000250614770023131025007 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana_if_type.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --with-filter-subtree #kill $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/yang-conformance/0000775000175000017500000000000014770023131022564 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-conformance/sec9_10/0000775000175000017500000000000014770023131023727 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-conformance/sec9_10/mod1.yang0000664000175000017500000000046614770023131025455 0ustar vladimirvladimirmodule mod1 { prefix m1; namespace "http://yuma123.org/ns/sec9_10/mod1"; identity foo; identity bar; identity foo-based { base foo; } identity bar-based { base bar; } container m1 { leaf i1 { type identityref { base "foo"; } default "bar"; } } } yuma123_2.14/netconf/test/netconfd/yang-conformance/sec9_10/mod2.yang0000664000175000017500000000047414770023131025455 0ustar vladimirvladimirmodule mod2 { prefix m2; namespace "http://yuma123.org/ns/sec9_10/mod2"; identity foo; identity bar; identity foo-based { base foo; } identity bar-based { base bar; } container m2 { leaf i2 { type identityref { base "foo"; } default "bar-based"; } } } yuma123_2.14/netconf/test/netconfd/yang-conformance/sec9_10/mod4.yang0000664000175000017500000000047414770023131025457 0ustar vladimirvladimirmodule mod4 { prefix m4; namespace "http://yuma123.org/ns/sec9_10/mod4"; identity foo; identity bar; identity foo-based { base foo; } identity bar-based { base bar; } container m4 { leaf i4 { type identityref { base "foo"; } default "foo-based"; } } } yuma123_2.14/netconf/test/netconfd/yang-conformance/sec9_10/mod3.yang0000664000175000017500000000046614770023131025457 0ustar vladimirvladimirmodule mod3 { prefix m3; namespace "http://yuma123.org/ns/sec9_10/mod3"; identity foo; identity bar; identity foo-based { base foo; } identity bar-based { base bar; } container m3 { leaf i3 { type identityref { base "foo"; } default "foo"; } } } yuma123_2.14/netconf/test/netconfd/yang-conformance/sec7_9/0000775000175000017500000000000014770023131023655 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-conformance/sec7_9/mod1.yang0000664000175000017500000000167014770023131025401 0ustar vladimirvladimirmodule mod1 { namespace "http://yuma123.org/ns/sec7_9/mod1"; prefix m1; container m1 { choice ch1 { case ca1 { leaf foo1 { type uint32; } leaf bar1 { type uint32; } choice ch01 { case ca011 { leaf foo11 { type uint32; } leaf bar11 { type uint32; } } case ca012 { leaf foo12 { type uint32; } leaf bar12 { type uint32; } } } choice ch02 { case ca021 { leaf foo021 { type uint32; } leaf bar021 { type uint32; } } } } case ca2 { leaf foo2 { type uint32; } leaf bar2 { type uint32; } } } } } yuma123_2.14/netconf/test/netconfd/yang-conformance/sec7_9/data1.xml0000664000175000017500000000016514770023131025373 0ustar vladimirvladimir 1 1 2 2 yuma123_2.14/netconf/test/netconfd/yang-conformance/sec7_9/data2.xml0000664000175000017500000000016114770023131025370 0ustar vladimirvladimir 1 2 1 2 yuma123_2.14/netconf/test/netconfd/yang-conformance/README0000664000175000017500000000126314770023131023446 0ustar vladimirvladimir=Conformance test suite for RFC6020 "YANG - A Data Modeling Language for the Network Configuration Protocol (NETCONF)"= ==FILES== testspec.txt - specification of the expected results 1-FAIL, 0-PASS check-yang-conformance.sh - script running the test suite ==USAGE== #Run with netconfd ./check-yang-conformance.sh .. testspec.txt #Run with pyang export RUN_WITH_PYANG=1 ./check-yang-conformance.sh .. testspec.txt export RUN_WITH_PYANG= #Run with yanglint export RUN_WITH_YANGLINT=1 ./check-yang-conformance.sh .. testspec.txt export RUN_WITH_YANGLINT= #Run with confd export RUN_WITH_CONFD=~/confd/root ./check-yang-conformance.sh .. testspec.txt export RUN_WITH_CONFD= yuma123_2.14/netconf/test/netconfd/yang-conformance/check-yang-conformance.sh0000775000175000017500000001304714770023131027431 0ustar vladimirvladimir#!/bin/bash test_data() { #TODO declare -a schema_load_fail=("${!2}") declare -a data_file_load_fail=("${!3}") MODULE_ARGS="" YANGLINT_MODULE_ARGS="" for index in ${!schema_load_fail[*]} do #printf "%4d: %s\n" $index ${schema_load_fail[$index]} if [ ${schema_load_fail[$index]} == "0" ] ; then if [ "$RUN_WITH_YANGLINT" != "" ] ; then YANGLINT_MODULE_ARGS=${YANGLINT_MODULE_ARGS}" "${1}/mod$(($index+1)).yang elif [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true source $RUN_WITH_CONFD/confdrc confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath ${RUN_WITH_CONFD}/etc/confd --addloadpath . 2>&1 1>server.log & else MODULE_ARGS=${MODULE_ARGS}" --module="${1}/mod$(($index+1)).yang fi fi done for index in ${!data_file_load_fail[*]} do if [ ${data_file_load_fail[$index]} == "0" ] ; then EXPECTED="OK" else EXPECTED="FAIL" fi DATA_FILE=${1}/data$(($index+1)).xml echo "Testing EXPECTED=$EXPECTED $DATA_FILE ..." >&2 echo '' >test-cfg.xml echo '' >>test-cfg.xml cat ${DATA_FILE} >> test-cfg.xml echo '' >> test-cfg.xml if [ "$RUN_WITH_PYANG" != "" ] ; then RES=1 elif [ "$RUN_WITH_YANGDUMP" != "" ] ; then RES=1 elif [ "$RUN_WITH_YANGLINT" != "" ] ; then yanglint --path=${1}/ ${YANGLINT_MODULE_ARGS} ${DATA_FILE} 1>&2 RES=$? elif [ "$RUN_WITH_CONFD" != "" ] ; then sleep 1 # wait for confd to start confd_load -l test-cfg.xml 1>&2 RES=$? else /usr/sbin/netconfd --validate-config-only --startup-error=stop --modpath=${1}/ ${MODULE_ARGS} --startup=test-cfg.xml 1>&2 RES=$? fi echo "RES="$RES >&2 if [ "$RES" != "0" ] ; then if [ "$EXPECTED" == "OK" ] ; then echo "FAIL: ${DATA_FILE}" >&2 FAIL=$(($FAIL+1)) echo -n ",1" else echo "OK: ${DATA_FILE}" >&2 OK=$(($OK+1)) echo -n ",0" fi else if [ "$EXPECTED" != "OK" ] ; then echo "FAIL: ${DATA_FILE}" >&2 FAIL=$(($FAIL+1)) echo -n ",1" else echo "OK: ${DATA_FILE}" >&2 OK=$(($OK+1)) echo -n ",0" fi fi done echo "OKs=$OK" >&2 echo "FAILs=$FAIL" >&2 return $FAIL } test_schema() { declare -a schema_load_fail=("${!2}") declare -a data_file_load_fail=("${!3}") FAIL=0 OK=0 if [ "$RUN_WITH_CONFD" != "" ] ; then rm *.fxs fi for index in ${!schema_load_fail[*]} do #printf "%4d: %s\n" $index ${schema_load_fail[$index]} if [ ${schema_load_fail[$index]} == "0" ] ; then EXPECTED="OK" else EXPECTED="FAIL" fi MODULE=${1}/mod$(($index+1)).yang echo "Testing EXPECTED=$EXPECTED $MODULE ..." >&2 if [ "$RUN_WITH_PYANG" != "" ] ; then pyang --path ${1}/ ${1}/mod$(($index+1)).yang 1>&2 RES=$? elif [ "$RUN_WITH_YANGDUMP" != "" ] ; then yangdump --modpath=${1}/ ${1}/mod$(($index+1)).yang 1>&2 RES=$? elif [ "$RUN_WITH_YANGLINT" != "" ] ; then yanglint --path=${1}/ ${1}/mod$(($index+1)).yang 1>&2 RES=$? elif [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true source $RUN_WITH_CONFD/confdrc confdc -c ${1}/mod$(($index+1)).yang --yangpath ${1}/ -o mod$(($index+1)).fxs 1>&2 RES=$? else /usr/sbin/netconfd --validate-config-only --startup-error=stop --no-startup --modpath=${1}/ --module=${1}/mod$(($index+1)).yang 1>&2 RES=$? fi echo "RES="$RES >&2 if [ "$RES" != "0" ] ; then if [ "$EXPECTED" == "OK" ] ; then echo "FAIL: ${MODULE}" >&2 FAIL=$(($FAIL+1)) echo -n ",1" else echo "OK: ${MODULE}" >&2 OK=$(($OK+1)) echo -n ",0" fi else if [ "$EXPECTED" != "OK" ] ; then echo "FAIL: ${MODULE}" >&2 FAIL=$(($FAIL+1)) echo -n ",1" else echo "OK: ${MODULE}" >&2 OK=$(($OK+1)) echo -n ",0" fi fi done echo "OKs=$OK" >&2 echo "FAILs=$FAIL" >&2 return $FAIL } TOTAL_FAILS=0 TOTAL_OKS=0 tests_base_dir=$1 tests_spec_file=$2 while read -r TEST_DIR_STR TEST_SCHEMA_LOAD_FAIL_STR TEST_DATA_FILE_FAIL_STR do echo -n "$TEST_DIR_STR" test_dir=${tests_base_dir}"/"${TEST_DIR_STR} test_schema_load_fail=(${TEST_SCHEMA_LOAD_FAIL_STR//,/ }) test_data_file_load_fail=(${TEST_DATA_FILE_FAIL_STR//,/ }) test_schema $test_dir test_schema_load_fail[@] test_data_file_load_fail[@] echo -n " " test_data $test_dir test_schema_load_fail[@] test_data_file_load_fail[@] FAILS=$? TOTAL_FAILS=$((${TOTAL_FAILS}+${FAILS})) echo done < "$tests_spec_file" exit $TOTAL_FAILS yuma123_2.14/netconf/test/netconfd/yang-conformance/sec9_12/0000775000175000017500000000000014770023131023731 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-conformance/sec9_12/mod1.yang0000664000175000017500000000031514770023131025450 0ustar vladimirvladimirmodule mod1 { prefix m1; namespace "http://yuma123.org/ns/sec9_12/mod1"; container m1 { leaf u1 { type union { type uint32; type string {length 0;} } } } } yuma123_2.14/netconf/test/netconfd/yang-conformance/sec9_12/data1.xml0000664000175000017500000000010114770023131025435 0ustar vladimirvladimir 5 yuma123_2.14/netconf/test/netconfd/yang-conformance/sec9_12/data2.xml0000664000175000017500000000010014770023131025435 0ustar vladimirvladimir yuma123_2.14/netconf/test/netconfd/yang-conformance/sec9_12/data4.xml0000664000175000017500000000010314770023131025442 0ustar vladimirvladimir foo yuma123_2.14/netconf/test/netconfd/yang-conformance/sec9_12/data3.xml0000664000175000017500000000007414770023131025450 0ustar vladimirvladimir yuma123_2.14/netconf/test/netconfd/yang-conformance/sec6_4/0000775000175000017500000000000014770023131023647 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-conformance/sec6_4/mod1.yang0000664000175000017500000000032514770023131025367 0ustar vladimirvladimirmodule mod1 { prefix m1; namespace "http://yuma123.org/ns/sec6_4/mod1"; container m1 { leaf foo { type uint32; } leaf bar { type uint32; must ". <= 0.75 * ../foo"; } } } yuma123_2.14/netconf/test/netconfd/yang-conformance/sec6_4/data1.xml0000664000175000017500000000012114770023131025355 0ustar vladimirvladimir 10 10 yuma123_2.14/netconf/test/netconfd/yang-conformance/sec6_4/mod2.yang0000664000175000017500000000031514770023131025367 0ustar vladimirvladimirmodule mod2 { prefix m2; namespace "http://yuma123.org/ns/sec6_4/mod2"; container m2 { leaf foo { type uint32; } leaf bar { type uint32; must ". < ../foo"; } } } yuma123_2.14/netconf/test/netconfd/yang-conformance/sec6_4/data2.xml0000664000175000017500000000012014770023131025355 0ustar vladimirvladimir 10 6 yuma123_2.14/netconf/test/netconfd/yang-conformance/sec6_4/data4.xml0000664000175000017500000000012014770023131025357 0ustar vladimirvladimir 2 10 yuma123_2.14/netconf/test/netconfd/yang-conformance/sec6_4/data3.xml0000664000175000017500000000012014770023131025356 0ustar vladimirvladimir 10 2 yuma123_2.14/netconf/test/netconfd/yang-conformance/sec7_8_2/0000775000175000017500000000000014770023131024075 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-conformance/sec7_8_2/mod1.yang0000664000175000017500000000027514770023131025621 0ustar vladimirvladimirmodule mod1 { prefix m1; namespace "http://yuma123.org/ns/sec7_8_2/mod1"; container m1 { config false; list l1 { leaf foo { type string; } } } } yuma123_2.14/netconf/test/netconfd/yang-conformance/sec7_8_2/mod2.yang0000664000175000017500000000027614770023131025623 0ustar vladimirvladimirmodule mod2 { prefix m2; namespace "http://yuma123.org/ns/sec7_8_2/mod2"; rpc m2 { input { list l1 { leaf foo { type string; } } } } } yuma123_2.14/netconf/test/netconfd/yang-conformance/testspec.txt0000664000175000017500000000011714770023131025156 0ustar vladimirvladimirsec6_4 0,0 1,0,0,1 sec7_8_2 0,0 sec7_9 0 0,1 sec9_10 1,1,1,0 sec9_12 0 0,0,0,1 yuma123_2.14/netconf/test/netconfd/yang-conformance/run.sh0000775000175000017500000000013114770023131023722 0ustar vladimirvladimir#!/bin/bash rm -rf tmp mkdir tmp cd tmp ../check-yang-conformance.sh .. ../testspec.txt yuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/0000775000175000017500000000000014770023131025332 5ustar vladimirvladimir././@LongLink0000644000000000000000000000016100000000000011601 Lustar rootrootyuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/ietf-interfaces-ethernet-like@2017-07-03.yangyuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/ietf-interfaces-ethernet-like@2010000777000175000017500000000000014770023131047503 2../../../modules/ietf-draft/ietf-interfaces-ethernet-like@2017-07-03.yangustar vladimirvladimir././@LongLink0000644000000000000000000000015700000000000011606 Lustar rootrootyuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/ietf-flexible-encapsulation@2017-07-04.yangyuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/ietf-flexible-encapsulation@2017-0000664000175000017500000003025614770023131033334 0ustar vladimirvladimirmodule ietf-flexible-encapsulation { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-flexible-encapsulation"; prefix flex; import ietf-interfaces { prefix if; } import iana-if-type { prefix ianaift; } import ietf-interfaces-common { prefix if-cmn; } import ieee802-dot1q-types { prefix dot1q-types; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Lou Berger WG Chair: Kent Watsen Editor: Robert Wilton "; description "This YANG module describes interface configuration for flexible VLAN matches and rewrites."; revision 2017-07-04 { description "Fix for c-vlan and s-vlan missing namespace prefixes."; reference "https://www.ietf.org/mail-archive/web/netmod/current/msg18367.html"; } revision 2017-07-03 { description "Latest draft revision"; reference "Internet-Draft draft-ietf-netmod-sub-intf-vlan-model-02"; } feature flexible-rewrites { description "This feature indicates whether the network element supports specifying flexible rewrite operations"; } feature asymmetric-rewrites { description "This feature indicates whether the network element supports specifying different rewrite operations for the ingress rewrite operation and egress rewrite operation."; } feature dot1q-tag-rewrites { description "This feature indicates whether the network element supports the flexible rewrite functionality specifying flexible 802.1Q tag rewrites"; } /* * flexible-match grouping. * * This grouping represents a flexible match. * * The rules for a flexible match are: * 1. default, untagged, priority tag, or a stack of tags. * - Each tag in the stack of tags matches: * 1. tag type (802.1Q or 802.1ad) + * 2. tag value: * i. single tag * ii. set of tag ranges/values. * iii. "any" keyword */ grouping flexible-match { description "Flexible match"; choice match-type { mandatory true; description "Provides a choice of how the frames may be matched"; case default { description "Default match"; leaf default { type empty; description "Default match. Matches all traffic not matched to any other peer sub-interface by a more specific encapsulation."; } // leaf default } // case default case untagged { description "Match untagged Ethernet frames only"; leaf untagged { type empty; description "Untagged match. Matches all untagged traffic."; } // leaf untagged } // case untagged case dot1q-priority-tagged { description "Match 802.1Q priority tagged Ethernet frames only"; container dot1q-priority-tagged { description "802.1Q priority tag match"; leaf tag-type { type dot1q-types:dot1q-tag-type; description "The 802.1Q tag type of matched priority tagged packets"; } } } case dot1q-vlan-tagged { container dot1q-vlan-tagged { description "Matches VLAN tagged frames"; container outer-tag { presence "The outermost VLAN tag exists"; description "Classifies traffic using the outermost VLAN tag on the frame."; uses 'dot1q-types:'+ 'dot1q-tag-ranges-or-any-classifier-grouping'; } container second-tag { must '../outer-tag/dot1q-tag/tag-type = "dot1q-types:s-vlan" and ' + 'dot1q-tag/tag-type = "dot1q-types:c-vlan"' { error-message "When matching two tags, the outermost tag must be specified and of S-VLAN type and the second outermost tag must be of C-VLAN tag type"; description "For IEEE 802.1Q interoperability, when matching two tags, it is required that the outermost tag exists and is an S-VLAN, and the second outermost tag is a C-VLAN"; } presence "The second outermost VLAN tag exists"; description "Classifies traffic using the second outermost VLAN tag on the frame."; uses 'dot1q-types:'+ 'dot1q-tag-ranges-or-any-classifier-grouping'; } leaf match-exact-tags { type empty; description "If set, indicates that all 802.1Q VLAN tags in the Ethernet frame header must be explicitly matched, i.e. the EtherType following the matched tags must not be a 802.1Q tag EtherType. If unset then extra 802.1Q VLAN tags are allowed."; } } } } // encaps-type } /* * Grouping for tag-rewrite that can be expressed either * symmetrically, or in the ingress and/or egress directions * independently. */ grouping dot1q-tag-rewrite { description "Flexible rewrite"; leaf pop-tags { type uint8 { range 1..2; } description "The number of tags to pop (or translate if used in conjunction with push-tags)"; } container push-tags { description "The 802.1Q tags to push (or translate if used in conjunction with pop-tags)"; container outer-tag { presence "Indicates existence of the outermost VLAN tag to push/rewrite"; description "The outermost VLAN tag to push onto the frame."; uses dot1q-types:dot1q-tag-classifier-grouping; } container second-tag { must '../outer-tag/dot1q-tag/tag-type = "dot1q-types:s-vlan" and ' + 'dot1q-tag/tag-type = "dot1q-types:c-vlan"' { error-message "When pushing/rewriting two tags, the outermost tag must be specified and of S-VLAN type and the second outermost tag must be of C-VLAN tag type"; description "For IEEE 802.1Q interoperability, when pushing two tags, it is required that the outermost tag exists and is an S-VLAN, and the second outermost tag is a C-VLAN"; } presence "Indicates existence of a second outermost VLAN tag to push/rewrite."; description "The second outermost VLAN tag to push onto the frame."; uses dot1q-types:dot1q-tag-classifier-grouping; } } } /* * Grouping for all flexible rewrites of fields in the L2 header. * * This currently only includes flexible tag rewrites, but is * designed to be extensible to cover rewrites of other fields in * the L2 header if required. */ grouping flexible-rewrite { description "Flexible rewrite"; /* * Tag rewrite. * * All tag rewrites are formed using a combination of pop-tags * and push-tags operations. */ container dot1q-tag-rewrite { if-feature dot1q-tag-rewrites; description "Tag rewrite. Translate operations are expressed as a combination of tag push and pop operations."; uses dot1q-tag-rewrite; } } augment "/if:interfaces/if:interface/if-cmn:encapsulation/" + "if-cmn:encaps-type" { when "derived-from-or-self(../if:type, 'ianaift:ethernetCsmacd') or derived-from-or-self(../if:type, 'ianaift:ieee8023adLag') or derived-from-or-self(../if:type, 'if-cmn:ethSubInterface')" { description "Applies only to Ethernet-like interfaces and sub-interfaces"; } description "Add flexible match and rewrite for VLAN sub-interfaces"; /* * A flexible encapsulation allows for the matching of ranges and * sets of VLAN Ids. The structure is also designed to be * extended to allow for matching/rewriting other fields within * the L2 frame header if required. */ case flexible { description "Flexible encapsulation and rewrite"; container flexible { must 'count(../../if-cmn:forwarding-mode) = 0 or ' + 'derived-from-or-self(../../if-cmn:forwarding-mode,' + '"if-cmn:layer-2-forwarding")' { error-message "If the interface forwarding-mode leaf is set then it must be set to an identity that derives from layer-2-forwarding"; description "The forwarding-mode leaf on an interface can optionally be used to enforce consistency of configuration"; } description "Flexible encapsulation and rewrite"; container match { description "The match used to classify frames to this interface"; uses flexible-match; } container rewrite { if-feature flexible-rewrites; description "L2 frame rewrite operations"; choice direction { description "Whether the rewrite policy is symmetrical or asymmetrical"; case symmetrical { container symmetrical { uses flexible-rewrite; description "Symmetrical rewrite. Expressed in the ingress direction, but the reverse operation is applied to egress traffic"; } } /* * Allow asymmetrical rewrites to be specified. */ case asymmetrical { if-feature asymmetric-rewrites; description "Asymmetrical rewrite"; container ingress { uses flexible-rewrite; description "Ingress rewrite"; } container egress { uses flexible-rewrite; description "Egress rewrite"; } } } } /* * For encapsulations that match a range of VLANs (or Any), * allow configuration to specify the default 802.1Q VLAN tag * values to use for any traffic that is locally sourced from * an interface on the device. */ container local-traffic-default-encaps { presence "A local traffic default encapsulation has been specified"; description "The 802.1Q VLAN tags to use by default for locally sourced traffic"; container outer-tag { presence "Indicates existence of the outermost VLAN tag"; description "The outermost VLAN tag for locally sourced traffic"; uses dot1q-types:dot1q-tag-classifier-grouping; } container second-tag { must '../outer-tag/dot1q-tag/tag-type = "dot1q-types:s-vlan" and ' + 'dot1q-tag/tag-type = "dot1q-types:c-vlan"' { error-message "When specifying two tags, the outermost tag must be specified and of S-VLAN type and the second outermost tag must be of C-VLAN tag type"; description "For IEEE 802.1Q interoperability, when specifying two tags, it is required that the outermost tag exists and is an S-VLAN, and the second outermost tag is a C-VLAN"; } presence "Indicates existence of a second outermost VLAN tag."; description "The second outermost VLAN tag for locally sourced traffic"; uses dot1q-types:dot1q-tag-classifier-grouping; } } } } } } yuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/composite-match.yang0000664000175000017500000000666014770023131031316 0ustar vladimirvladimir module composite-match { yang-version 1.1; namespace "http://example.com/ns/composite-match"; prefix "cm"; import ietf-interfaces { prefix if; } import ietf-yang-types {prefix yang;} import ietf-inet-types {prefix inet;} import ieee802-dot1q-types {prefix dot1q-types;} import ietf-interfaces-common { prefix if-cmn; } import ietf-flexible-encapsulation { prefix flex; } organization "example.com"; description "Model for defining sub-interface with composite ingress filter."; revision 2017-05-26 { description "Initial version."; } identity match-field { description "Base identity for matching fields"; } identity eth-dst { base match-field; } identity eth-src { base match-field; } identity eth-type { base match-field; } identity vlan-vid { base match-field; } identity vlan-pcp { base match-field; } /* and so on ... */ grouping match-entries-grouping { container match { list match-field { key match-type; uses match-entry-fields-grouping; uses match-entry-value-grouping; } } } grouping match-entry-fields-grouping { leaf match-type { type identityref { base match-field; } } } grouping match-entry-value-grouping { choice match-entry-value { case eth-src-case { container eth-src { leaf mac-address { type yang:mac-address; } leaf mask { type binary; } } } case eth-dst-case { container eth-dst { leaf mac-address { type yang:mac-address; } leaf mask { type binary; } } } case eth-type-case { container eth-type { leaf eth-type { type dot1q-types:ether-type; } } } case vlan-vid-case { container vlan-vid { leaf vlan-vid { type uint16; } leaf cfi-bit { type boolean; } leaf mask { type binary; } } } case vlan-pcp-case { container vlan-pcp { leaf vlan-pcp { type uint8; } } } } } augment "/if:interfaces/if:interface/if-cmn:encapsulation/" + "if-cmn:encaps-type/flex:flexible/flex:flexible/flex:match/flex:match-type" { case composite-match { description "List of match filters logically OR-ed to specify sub-interface ingress traffic from the parent interface ingress traffic."; list composite-match { key id; leaf id { type string; } uses match-entries-grouping; } } } } yuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/example-vlans.yang0000664000175000017500000000207114770023131030766 0ustar vladimirvladimirmodule example-vlans { namespace "http://example.com/ns/vlans"; prefix "vlans"; import ietf-interfaces { prefix if; } organization "example.com"; description "Model binding interfaces into VLANs."; revision 2017-05-15 { description "Fixed typo."; } container vlans { description "Container for all VLAN bridges."; list vlan { key name; leaf name { type string; mandatory true; } leaf-list interface { type if:interface-ref; description "Symmetrical interface participating as both ingress and egress member"; } leaf-list egress-interface { type if:interface-ref; description "Egress interface member."; } leaf-list ingress-interface { type if:interface-ref; description "Ingress interface member."; } } } } yuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/README0000664000175000017500000000035114770023131026211 0ustar vladimirvladimirPURPOSE: Validate the models part of draft-ietf-netmod-sub-intf-vlan-model load and the YANG 1.1 statements used there are correctly supported. OPERATION: Creates and delete sub interfaces. Create VLAN bridge with sub-interfaces. yuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/ieee802-types@2016-07-24.yang0000664000175000017500000000415514770023131031602 0ustar vladimirvladimirmodule ieee802-types { namespace "urn:ieee:std:802:yang:ieee802-types"; prefix "ieee"; organization "Institute of Electrical and Electronics Engineers"; contact "Web URL: http://www.ieee802.org/1/ Editor: Marc Holness mholness@ciena.com"; description "This module contains a collection of generally useful derived data types for IEEE YANG models."; revision "2016-07-24" { description "Make updates based upon comment resolution based upon draft D0.4 of P802.1Qcp."; reference "IEEE 802.1Q-2014, Media Access Control (MAC) Bridges and Virtual Bridged Local Area Networks."; } revision "2016-05-11" { description "Make updates based upon comment resolution from Jan2016 IEEE 802 Interim meeting."; reference "IEEE 802.1Q-2014, Media Access Control (MAC) Bridges and Virtual Bridged Local Area Networks."; } revision 2015-09-10 { description "Initial revision."; reference "IEEE 802"; } /* * Collection of IEEE address type definitions */ typedef mac-address { type string { pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; } description "The mac-address type represents an 802 MAC address represented in the canonical order defined by IEEE 802. The canonical representation uses lowercase characters. In the value set and its semantics, this type if equivalent to the MacAddress textual convention of the SMIv2."; reference "IEEE 802.3-2012, Clause 3.2.3 RFC 2579: Textual Conventions for SMIv2"; } /* * Collection of IEEE 802 related identifier types */ typedef vlanid { type uint16 { range "1..4094"; } description "The vlanid type uniquely identifies a VLAN. This is the 12-bit VLAN-ID used in the VLAN Tag header. The range is defined by the referenced specification. This type is in the value set and its semantics equivalent to the VlanId textual convention of the SMIv2."; reference "IEEE Std 802.1Q-2014: Virtual Bridged Local Area Networks."; } } ././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootyuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/ieee802-dot1q-types@2016-09-22.yangyuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/ieee802-dot1q-types@2016-09-22.ya0000664000175000017500000007057714770023131032316 0ustar vladimirvladimirmodule ieee802-dot1q-types { namespace "urn:ieee:std:802.1Q:yang:ieee802-dot1q-types"; prefix "dot1q-types"; import ietf-interfaces { prefix "if"; } import ietf-yang-types { prefix "yang"; } import ieee802-types { prefix "ieee"; } organization "Institute of Electrical and Electronics Engineers"; contact "Web URL: http://www.ieee802.org/1/ Working Group Chair: Glenn Parsons glenn.parsons@ericsson.com Working Group Vice-chair: John Messenger J.L.Messenger@ieee.org Chair, Time Sensitive Networking Task Group: Michael Johas Teener mikejt@broadcom.com Vice-Chair, Time Sensitive Networking Task Group: Janos Farkas janos.farkas@ericsson.com Editor: Marc Holness mholness@ciena.com"; description "Common types used within dot1Q-bridge modules."; revision "2016-09-22" { description "Include VLAN related types that were defined in draft draft-wilton-netmod-intf-vlan-yang-03."; reference "draft-wilton-netmod-intf-vlan-yang-03."; } revision "2016-07-24" { description "Make updates based upon comment resolution based upon draft D0.4 of P802.1Qcp."; reference "IEEE 802.1Q-2014, Media Access Control (MAC) Bridges and Virtual Bridged Local Area Networks."; } revision "2016-05-11" { description "Make updates based upon comment resolution from Jan2016 IEEE 802 Interim meeting."; reference "IEEE 802.1Q-2014, Media Access Control (MAC) Bridges and Virtual Bridged Local Area Networks."; } revision "2015-09-10" { description "Initial revision."; reference "IEEE 802.1Q-2014, Media Access Control (MAC) Bridges and Virtual Bridged Local Area Networks."; } /* * IEEE 802.1Q Identity Definitions. * Defines the supported IEEE 802.1Q types that can be used * for VLAN tag matching. */ identity dot1q-vlan-type { description "Base identity from which all 802.1Q VLAN tag types are derived from."; } identity c-vlan { base dot1q-vlan-type; description "An 802.1Q Customer VLAN, normally using the 0x8100 Ethertype"; reference "IEEE 802.1Q-2014, Clause 5.5"; } identity s-vlan { base dot1q-vlan-type; description "An 802.1Q Service VLAN, using the 0x88a8 Ethertype originally introduced in 802.1ad, and incorporated into 802.1Q (2011)"; reference "IEEE 802.1Q-2014, Clause 5.6"; } /* * IEEE 802.1Q Type Definitions. */ typedef name-type { type string { length "0..32"; } description "A text string of up to 32 characters, of locally determined significance."; } typedef port-number-type { type uint16 { range "1..4095"; } description "An Unsigned value assigned to a Port as part of a Port Identifier. Valid Port Numbers are in the range 1 through 4095."; } typedef priority-type { type uint8 { range "0..7"; } description "A range of priorities from 0 to 7 (inclusive)."; } typedef PCP { type priority-type; description "Priority Code Point (PCP) is a 3-bit field that refers to the class of service associated with an 802.1Q VLAN tagged frame. The field specifies a priority value between 0 and 7, these values can be used by quality of service (QoS) to prioritize different classes of traffic."; } typedef vid-range-type { /* * Defines the type used to represent ranges of VLAN Ids. * * Ideally we would model that as a list of VLAN Ids in YANG, * but the model is easier to use if this is just represented * as a string. * */ type string { pattern "([1-9][0-9]{0,3}(-[1-9][0-9]{0,3})?" + "(,[1-9][0-9]{0,3}(-[1-9][0-9]{0,3})?)*)"; } description "A list of VLAN Ids, or non overlapping VLAN ranges, in ascending order, between 1 and 4094. This type is used to match an ordered list of VLAN Ids, or contiguous ranges of VLAN Ids. Valid VLAN Ids must be in the range 1 to 4094, and included in the list in non overlapping ascending order. For example: 1,10-100,50,500-1000"; } typedef vlan-index-type { type uint32 { range "1..4094 | 4096..4294967295"; } description "A value used to index per-VLAN tables. Values of 0 and 4095 are not permitted. The range of valid VLAN indices. If the value is greater than 4095, then it represents a VLAN with scope local to the particular agent, i.e., one without a global VLAN-ID assigned to it. Such VLANs are outside the scope of IEEE 802.1Q, but it is convenient to be able to manage them in the same way using this YANG module."; reference "IEEE Std 802.1Q-2014: Virtual Bridged Local Area Networks."; } typedef pcp-selection-type { type enumeration { enum 8P0D { description "8 priorities, 0 discard"; } enum 7P1D { description "7 priorities, 1 discard"; } enum 6P2D { description "6 priorities, 2 discards"; } enum 5P3D { description "5 priorities, 3 discards"; } } description "Priority Code Point selection types."; reference "IEEE 802.1Q-2014 Clause 12.6.2.5.3, 6.9.3"; } typedef protocol-frame-format-type { type enumeration { enum Ethernet { description "Ethernet frame format"; } enum rfc1042 { description "RFC 1042 frame format"; } enum snap8021H { description "SNAP 802.1H frame format"; } enum snapOther { description "Other SNAP frame format"; } enum llcOther { description "Other LLC frame format"; } } description "A value representing the frame format to be matched."; reference "IEEE 802.1Q-2014 Clause 12.10.1.7.1"; } typedef ether-type { type string { pattern '[0-9a-fA-F]{4}'; } description "The Ethernet Type (or Length) value represented in the canonical order defined by IEEE 802. The canonical representation uses lowercase characters."; reference "IEEE 802-2014 Clause 9.2"; } typedef dot1q-tag-type { type identityref { base "dot1q-vlan-type"; } description "Identifies a specific 802.1Q tag type"; reference "IEEE 802.1Q (2014)"; } typedef traffic-class-type { type uint8 { range "0..7"; } description "This is the numerical value associated with a traffic class in a Bridge. Larger values are associated with higher priority traffic classes."; reference "IEEE Std 802.1Q-2014, Clause 3.239"; } /* * IEEE 802.1Q Bridge Group definitions. */ grouping dot1q-tag-classifier-grouping { description "A grouping which represents an 802.1Q VLAN, matching both the Ethertype and a single VLAN Id."; container dot1q-tag { description "Identifies an 802.1Q VLAN with an explicit EtherType and a single VLAN Id"; leaf tag-type { type dot1q-tag-type; mandatory true; description "VLAN type"; } leaf vlan-id { type ieee:vlanid; mandatory true; description "VLAN Id"; } } } grouping dot1q-tag-or-any-classifier-grouping { description "A grouping which represents an 802.1Q VLAN, matching both the Ethertype and a single VLAN Id or 'any' to match on any VLAN Id."; container dot1q-tag { description "Identifies an 802.1Q VLAN with an explicit EtherType and a single VLAN Id, or 'any' VLAN Id."; leaf tag-type { type dot1q-tag-type; mandatory true; description "VLAN type"; } leaf vlan-id { type union { type ieee:vlanid; type enumeration { enum "any" { value 4095; description "Matches 'any' VLAN in the range 1 to 4094 that is not matched by a more specific VLAN Id match"; } } } mandatory true; description "VLAN Id or any"; } } } grouping dot1q-tag-ranges-classifier-grouping { description "A grouping which represents an 802.1Q VLAN that matches a range of VLAN Ids."; container dot1q-tag { description "Identifies an 802.1Q VLAN with an explicit EtherType and and a range of VLAN Ids."; leaf tag-type { type dot1q-tag-type; mandatory true; description "VLAN type"; } leaf vlan-ids { type vid-range-type; mandatory true; description "VLAN Ids"; } } } grouping dot1q-tag-ranges-or-any-classifier-grouping { description "A grouping which represents an 802.1Q VLAN, matching both the Ethertype and a single VLAN Id, ordered list of ranges, or 'any' to match on any VLAN Id."; container dot1q-tag { description "Identifies an 802.1Q VLAN with an explicit EtherType, an ordered list of VLAN Id ranges, or 'any' VLAN Id."; leaf tag-type { type dot1q-tag-type; mandatory true; description "VLAN type"; } leaf vlan-id { type union { type vid-range-type; type enumeration { enum "any" { value 4095; description "Matches 'any' VLAN in the range 1 to 4094."; } } } mandatory true; description "VLAN Ids or any"; } } } grouping priority-regeneration-table-grouping { description "The priority regeneration table provides the ability to map incoming priority values on a per-Port basis, under management control."; reference "IEEE 802.1Q-2014 Clause 6.9.4"; leaf priority0 { type priority-type; default 0; description "Priority 0"; reference "IEEE 802.1Q-2014 Clause 6.9.4, 12.6.2.3"; } leaf priority1 { type priority-type; default 1; description "Priority 1"; reference "IEEE 802.1Q-2014 Clause 6.9.4, 12.6.2.3"; } leaf priority2 { type priority-type; default 2; description "Priority 2"; reference "IEEE 802.1Q-2014 Clause 6.9.4, 12.6.2.3"; } leaf priority3 { type priority-type; default 3; description "Priority 3"; reference "IEEE 802.1Q-2014 Clause 6.9.4, 12.6.2.3"; } leaf priority4 { type priority-type; default 4; description "Priority 4"; reference "IEEE 802.1Q-2014 Clause 6.9.4, 12.6.2.3"; } leaf priority5 { type priority-type; default 5; description "Priority 5"; reference "IEEE 802.1Q-2014 Clause 6.9.4, 12.6.2.3"; } leaf priority6 { type priority-type; default 6; description "Priority 6"; reference "IEEE 802.1Q-2014 Clause 6.9.4, 12.6.2.3"; } leaf priority7 { type priority-type; default 7; description "Priority 7"; reference "IEEE 802.1Q-2014 Clause 6.9.4, 12.6.2.3"; } } grouping pcp-decoding-table-grouping { description "The Priority Code Point decoding table enables the decoding of the priority and drop-eligible parameters from the PCP."; reference "IEEE 802.1Q-2014 Clause 6.9.3"; list pcp-decoding-map { key "pcp"; description "This map associates the priority code point field found in the VLAN to a priority and drop eligible value based upon the priority code point selection type."; leaf pcp { type pcp-selection-type; description "The priority code point selection type."; reference "IEEE 802.1Q-2014 Clause 12.6.2.7, 6.9.3"; } list priority-map { key "priority-code-point"; description "This map associated a priority code point value to priority and drop eligible parameters."; leaf priority-code-point { type priority-type; description "Priority associated with the pcp."; reference "IEEE 802.1Q-2014 Clause 12.6.2.7, 6.9.3"; } leaf priority { type priority-type; description "Priority associated with the pcp."; reference "IEEE 802.1Q-2014 Clause 12.6.2.7, 6.9.3"; } leaf drop-eligible { type boolean; description "Drop eligible value for pcp"; reference "IEEE 802.1Q-2014 Clause 12.6.2.7, 6.9.3"; } } } } grouping pcp-encoding-table-grouping { description "The Priority Code Point encoding table encodes the priority and drop-eligible parameters in the PCP field of the VLAN tag."; reference "IEEE 802.1Q-2014 Clause 12.6.2.9, 6.9.3"; list pcp-encoding-map { key "pcp"; description "This map associated the priority and drop-eligible parameters to the priority used to encode the PCP of the VLAN based upon the priority code point selection type."; leaf pcp { type pcp-selection-type; description "The priority code point selection type."; reference "IEEE 802.1Q-2014 Clause 12.6.2.7, 6.9.3"; } list priority-map { key "priority dei"; description "This map associated the priority and drop-eligible parameters to the priority code point field of the VLAN tag."; leaf priority { type priority-type; description "Priority associated with the pcp."; reference "IEEE 802.1Q-2014 Clause 12.6.2.7, 6.9.3"; } leaf dei { type boolean; description "The drop eligible value."; reference "IEEE 802.1Q-2014 Clause 12.6.2, 8.6.6"; } leaf priority-code-point { type priority-type; description "PCP value for priority when DEI value"; reference "IEEE 802.1Q-2014 Clause 12.6.2.9, 6.9.3"; } } } } grouping service-access-priority-table-grouping { description "The Service Access Priority Table associates a received priority with a serice access priority."; reference "IEEE 802.1Q-2014 Clause 6.13.1, 12.6.2.17"; leaf priority0 { type priority-type; default 0; description "Service access priority value for priority 0"; reference "IEEE 802.1Q-2014 Clause 6.13.1, 12.6.2.17"; } leaf priority1 { type priority-type; default 1; description "Service access priority value for priority 1"; reference "IEEE 802.1Q-2014 Clause 6.13.1, 12.6.2.17"; } leaf priority2 { type priority-type; default 2; description "Service access priority value for priority 2"; reference "IEEE 802.1Q-2014 Clause 6.13.1, 12.6.2.17"; } leaf priority3 { type priority-type; default 3; description "Service access priority value for priority 3"; reference "IEEE 802.1Q-2014 Clause 6.13.1, 12.6.2.17"; } leaf priority4 { type priority-type; default 4; description "Service access priority value for priority 4"; reference "IEEE 802.1Q-2014 Clause 6.13.1, 12.6.2.17"; } leaf priority5 { type priority-type; default 5; description "Service access priority value for priority 5"; reference "IEEE 802.1Q-2014 Clause 6.13.1, 12.6.2.17"; } leaf priority6 { type priority-type; default 6; description "Service access priority value for priority 6"; reference "IEEE 802.1Q-2014 Clause 6.13.1, 12.6.2.17"; } leaf priority7 { type priority-type; default 7; description "Service access priority value for priority 7"; reference "IEEE 802.1Q-2014 Clause 6.13.1, 12.6.2.17"; } } grouping traffic-class-table-grouping { description "The Traffic Class Table models the operations that can be performed on, or inquire about, the current contents of the Traffic Class Table (8.6.6) for a given Port."; reference "IEEE 802.1Q-2014 Clause 12.6.3, 8.6.6"; list traffic-class-map { key "priority"; description "The priority index into the traffic class table."; leaf priority { type priority-type; description "The priority of the traffic class entry."; reference "IEEE 802.1Q-2014 Clause 8.6.6"; } list available-traffic-class { key "num-traffic-class"; description "The traffic class index associated with a given priority within the traffic class table."; reference "IEEE 802.1Q-2014 Clause 8.6.6"; leaf num-traffic-class { type uint8 { range "1..8"; } description "The available number of traffic classes."; reference "IEEE 802.1Q-2014 Clause 8.6.6"; } leaf traffic-class { type traffic-class-type; description "The traffic class index associated with a given traffic class entry."; reference "IEEE 802.1Q-2014 Clause 8.6.6"; } } } } grouping port-map-grouping { description "A set of control indicators, one for each Port. A Port Map, containing a control element for each outbound Port"; reference "IEEE 802.1Q-2014 Clause 8.8.1, 8.8.2"; list port-map { key "port-ref"; description "The list of entries composing the port map."; leaf port-ref { type if:interface-ref; description "The interface port reference associated with this map."; reference "IEEE 802.1Q-2014 Clause 8.8.1"; } choice map-type { description "Type of port map"; container static-filtering-entries { description "Static filtering entries attributes."; leaf control-element { type enumeration { enum forward { description "Forwarded, independently of any dynamic filtering information held by the FDB."; } enum filter { description "Filtered, independently of any dynamic filtering information."; } enum forward-filter { description "Forwarded or filtered on the basis of dynamic filtering information, or on the basis of the default Group filtering behavior for the outbound Port (8.8.6) if no dynamic filtering information is present specifically for the MAC address."; } } description "containing a control element for each outbound Port, specifying that a frame with a destination MAC address, and in the case of VLAN Bridge components, VID that meets this specification."; reference "IEEE 802.1Q-2014 Clause 8.8.1"; } leaf connection-identifier { type port-number-type; description "A Port MAP may contain a connection identifier (8.8.12) for each outbound port. The connection identifier may be associated with the Bridge Port value maintained in a Dynamic Filtering Entry of the FDB for Bridge Ports."; reference "IEEE 802.1Q-2014, Clause 8.8.1, 8.8.12"; } } container static-vlan-registration-entries { description "Static VLAN registration entries."; leaf registrar-admin-control { type enumeration { enum fixed-new-ignored { description "Registration Fixed (New ignored)."; } enum fixed-new-propagated { description "Registration Fixed (New propagated."; } enum forbidden { description "Registration Forbidden."; } enum normal { description "Normal Registration."; } } description "The Registrar Administrative Control values for MVRP and MIRP for the VID."; reference "IEEE 802.1Q-2014 Clause 8.8.2"; } leaf vlan-transmitted { type enumeration { enum tagged { description "VLAN-tagged"; } enum untagged { description "VLAN-untagged"; } } description "Whether frames are to be VLAN-tagged or untagged when transmitted."; reference "IEEE 802.1Q-2014 Clause 8.8.2"; } } container mac-address-registration-entries { description "MAC address registration entries attributes."; leaf control-element { type enumeration { enum registered { description "Forwarded, independently of any dynamic filtering information held by the FDB."; } enum not-registered { description "Filtered, independently of any dynamic filtering information."; } } description "containing a control element for each outbound Port, specifying that a frame with a destination MAC address, and in the case of VLAN Bridge components, VID that meets this specification."; reference "IEEE 802.1Q-2014 Clause 8.8.4"; } } container dynamic-vlan-registration-entries { description "Dynamic VLAN registration entries attributes."; leaf control-element { type enumeration { enum registered { description "Forwarded, independently of any dynamic filtering information held by the FDB."; } } description "containing a control element for each outbound Port, specifying that a frame with a destination MAC address, and in the case of VLAN Bridge components, VID that meets this specification."; reference "IEEE 802.1Q-2014 Clause 8.8.5"; } } container dynamic-reservation-entries { description "Dynamic reservation entries attributes."; leaf control-element { type enumeration { enum forward { description "Forwarded, independently of any dynamic filtering information held by the FDB."; } enum filter { description "Filtered, independently of any dynamic filtering information."; } } description "Containing a control element for each outbound Port, specifying that a frame with a destination MAC address, and in the case of VLAN Bridge components, VID that meets this specification."; reference "IEEE 802.1Q-2014 Clause 8.8.7"; } } container dynamic-filtering-entries { description "Dynamic filtering entries attributes."; leaf control-element { type enumeration { enum forward { description "Forwarded, independently of any dynamic filtering information held by the FDB."; } } description "Containing a control element for each outbound Port, specifying that a frame with a destination MAC address, and in the case of VLAN Bridge components, VID that meets this specification."; reference "IEEE 802.1Q-2014 Clause 8.8.3"; } } } } } grouping bridge-port-statistics-grouping { description "Grouping of bridge port statistics."; reference "IEEE 802.1Q-2014 Clause 12.6.1.1.3"; leaf delay-exceeded-discards { type yang:counter64; description "The number of frames discarded by this port due to excessive transit delay through the Bridge. It is incremented by both transparent and source route Bridges."; reference "IEEE 802.1Q-2014 Clause 12.6.1.1.3, 8.6.6"; } leaf mtu-exceeded-discards { type yang:counter64; description "The number of frames discarded by this port due to an excessive size. It is incremented by both transparent and source route Bridges."; reference "IEEE 802.1Q-2014 Clause 12.6.1.1.3 g)"; } leaf frame-rx { type yang:counter64; description "The number of frames that have been received by this port from its segment. Note that a frame received on the interface corresponding to this port is only counted by this object if and only if it is for a protocol being processed by the local bridging function, including Bridge management frames."; reference "IEEE 802.1Q-2014 Clause 12.6.1.1.3"; } leaf octets-rx { type yang:counter64; description "The total number of octets in all valid frames received (including BPDUs, frames addressed to the Bridge as an end station, and frames that were submitted to the Forwarding Process)."; reference "IEEE 802.1Q-2014 Clause 12.6.1.1.3"; } leaf frame-tx { type yang:counter64; description "The number of frames that have been transmitted by this port to its segment. Note that a frame transmitted on the interface corresponding to this port is only counted by this object if and only if it is for a protocol being processed by the local bridging function, including Bridge management frames."; } leaf octets-tx { type yang:counter64; description "The total number of octets that have been transmitted by this port to its segment."; } leaf discard-inbound { type yang:counter64; description "Count of received valid frames that were discarded (i.e., filtered) by the Forwarding Process."; reference "IEEE 802.1Q-2014 Clause 12.6.1.1.3"; } leaf forward-outbound { type yang:counter64; description "The number of frames forwarded to the associated MAC Entity (8.5)."; reference "IEEE 802.1Q-2014 Clause 12.6.1.1.3"; } leaf discard-lack-of-buffers { type yang:counter64; description "The count of frames that were to be transmitted through the associated Port but were discarded due to lack of buffers."; reference "IEEE 802.1Q-2014 Clause 12.6.1.1.3"; } leaf discard-transit-delay-exceeded { type yang:counter64; description "The number of frames discarded by this port due to excessive transit delay through the Bridge. It is incremented by both transparent and source route Bridges."; reference "IEEE 802.1Q-2014 Clause 12.6.1.1.3"; } leaf discard-on-error { type yang:counter64; description "The number of frames that were to be forwarded on the associated MAC but could not be transmitted (e.g., frame would be too large, 6.5.8)."; reference "IEEE 802.1Q-2014 Clause 12.6.1.1.3"; } } } yuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/session.yangcli.py0000664000175000017500000000721414770023131031020 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli from lxml import etree import yangrpc def yangcli_ok_script(conn, yangcli_script): for line in yangcli_script.splitlines(): line=line.strip() if not line: continue print(("Executing: "+line)) ok = yangcli(conn, line).xpath('./ok') assert(len(ok)==1) def main(): print(""" #Description: Using sub-interfaces configure vlan bridge. #Procedure: #1 - Create interface "xe0", "ge0", "ge1" of type=ethernetCsmacd. #2 - Create sub-interface "xe0-green" - s-vlan-id=1000, "ge1-green" - c-vlan-id=10 of type=ethSubInterface. #3 - Create VLAN bridge with "ge0", "ge1-green" and "xe0-green". #4 - Commit. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa", "--dump-session=tmp/nc-session-") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) time.sleep(1) ok = yangcli(conn, '''delete /vlans''').xpath('./ok') ok = yangcli(conn, '''delete /interfaces''').xpath('./ok') ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) yangcli_script=''' create /interfaces/interface -- name='xe0' type='ethernetCsmacd' create /interfaces/interface -- name='ge0' type='ethernetCsmacd' create /interfaces/interface -- name='ge1' type='ethernetCsmacd' create /interfaces/interface -- name='ge1-green' type=ethSubInterface parent-interface=ge0 create /interfaces/interface[name='ge1-green']/forwarding-mode value=layer-2-forwarding create /interfaces/interface[name='ge1-green']/encapsulation/flexible/match/dot1q-vlan-tagged/outer-tag/dot1q-tag -- tag-type=c-vlan vlan-id=10 create /interfaces/interface -- name='xe0-green' type=ethSubInterface parent-interface=xe0 create /interfaces/interface[name='xe0-green']/forwarding-mode value=layer-2-forwarding create /interfaces/interface[name='xe0-green']/encapsulation/flexible/match/dot1q-vlan-tagged/outer-tag/dot1q-tag -- tag-type=s-vlan vlan-id=1000 create /interfaces/interface[name='xe0-green']/encapsulation/flexible/match/dot1q-vlan-tagged/second-tag/dot1q-tag -- tag-type=c-vlan vlan-id=10 create /vlans/vlan -- name=green create /vlans/vlan[name='green']/interface value=ge0 create /vlans/vlan[name='green']/interface value=ge1-green create /vlans/vlan[name='green']/interface value=xe0-green commit ''' yangcli_ok_script(conn, yangcli_script) result = yangcli(conn, "xget /interfaces") names = result.xpath('./data/interfaces/interface/name') for name in names: print(name.text) types = result.xpath('./data/interfaces/interface/type') for type in types: print(type.text) #cleanup # ok = yangcli(conn, '''delete /vlans''').xpath('./ok') # assert(len(ok)==1) # ok = yangcli(conn, '''delete /interfaces''').xpath('./ok') # assert(len(ok)==1) # ok = yangcli(conn, '''commit''').xpath('./ok') # assert(len(ok)==1) return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/session.litenc.py0000664000175000017500000001560414770023131030652 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Demonstrate that duplicated list entries in edit-config are detected. #Procedure: #Description: Using sub-interfaces configure vlan bridge. #Procedure: #1 - Create interface "xe0", "ge0", "ge1" of type=ethernetCsmacd. #2 - Create sub-interface "xe0-green" - s-vlan-id=1000, "ge1-green" - c-vlan-id=10 of type=ethSubInterface. #3 - Create VLAN bridge with "ge0", "ge1-green" and "xe0-green". #4 - Commit. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw,strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) edit_config_rpc = """ merge set ge0 ianaift:ethernetCsmacd ge1 ianaift:ethernetCsmacd ge1-green if-cmn:ethSubInterface dot1q-types:c-vlan 10 if-cmn:layer-2-forwarding ge0 xe0 ianaift:ethernetCsmacd xe0-green if-cmn:ethSubInterface dot1q-types:s-vlan 1000 dot1q-types:c-vlan 10 if-cmn:layer-2-forwarding xe0 green ge0 ge1-green xe0-green """ print("edit-config - create vlan ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) assert(len(ok)==1) edit_config_rpc = """ merge set """ print("edit-config - clean-up ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) assert(len(ok)==1) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/ietf-if-l3-vlan@2017-07-03.yang0000664000175000017500000000717714770023131032101 0ustar vladimirvladimirmodule ietf-if-l3-vlan { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-if-l3-vlan"; prefix if-l3-vlan; import ietf-interfaces { prefix if; } import iana-if-type { prefix ianaift; } import ieee802-dot1q-types { prefix dot1q-types; } import ietf-interfaces-common { prefix if-cmn; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Lou Berger WG Chair: Kent Watsen Editor: Robert Wilton "; description "This YANG module models L3 VLAN sub-interfaces"; revision 2017-07-03 { description "Latest draft revision"; reference "Internet-Draft draft-ietf-netmod-sub-intf-vlan-model-02"; } /* * Add support for the 802.1Q VLAN encapsulation syntax on layer 3 * terminated VLAN sub-interfaces. */ augment "/if:interfaces/if:interface/if-cmn:encapsulation/" + "if-cmn:encaps-type" { when "derived-from-or-self(../if:type, 'ianaift:ethernetCsmacd') or derived-from-or-self(../if:type, 'ianaift:ieee8023adLag') or derived-from-or-self(../if:type, 'if-cmn:ethSubInterface')" { description "Applies only to Ethernet-like interfaces and sub-interfaces"; } description "Augment the generic interface encapsulation with an basic 802.1Q VLAN encapsulation for sub-interfaces."; /* * Matches a single VLAN Id, or pair of VLAN Ids to classify * traffic into an L3 service. */ case dot1q-vlan { container dot1q-vlan { must 'count(../../if-cmn:forwarding-mode) = 0 or ' + 'derived-from-or-self(../../if-cmn:forwarding-mode,' + '"if-cmn:layer-3-forwarding")' { error-message "If the interface forwarding-mode leaf is set then it must be set to an identity that derives from layer-3-forwarding"; description "The forwarding-mode leaf on an interface can optionally be used to enforce consistency of configuration"; } description "Match VLAN tagged frames with specific VLAN Ids"; container outer-tag { presence "The outermost VLAN tag exists"; description "Classifies traffic using the outermost VLAN tag on the frame."; uses dot1q-types:dot1q-tag-classifier-grouping; } container second-tag { must '../outer-tag/dot1q-tag/tag-type = "s-vlan" and ' + 'dot1q-tag/tag-type = "c-vlan"' { error-message "When matching two tags, the outermost tag must be specified and of S-VLAN type and the second outermost tag must be of C-VLAN tag type"; description "For IEEE 802.1Q interoperability, when matching two tags, it is required that the outermost tag exists and is an S-VLAN, and the second outermost tag is a C-VLAN"; } presence "The second outermost VLAN tag exists"; description "Classifies traffic using the second outermost VLAN tag on the frame."; uses dot1q-types:dot1q-tag-classifier-grouping; } } } } } yuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/run.sh0000775000175000017500000000505314770023131026500 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc cp *.yang tmp cd tmp confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf confdc -c ietf-interfaces-common@2017-07-03.yang --yangpath /usr/share/yuma/modules/ietf --yangpath .. confdc -c ietf-interfaces-ethernet-like@2017-07-03.yang --yangpath /usr/share/yuma/modules/ietf --yangpath .. confdc -c ietf-flexible-encapsulation@2017-07-04.yang --yangpath /usr/share/yuma/modules/ietf --yangpath .. confdc -c ieee802-dot1q-types@2016-09-22.yang --yangpath /usr/share/yuma/modules/ietf --yangpath .. confdc -c ieee802-types@2016-07-24.yang --yangpath /usr/share/yuma/modules/ietf --yangpath .. confdc -c example-vlans.yang --yangpath /usr/share/yuma/modules/ietf --yangpath .. # confdc -c composite-match.yang --yangpath /usr/share/yuma/modules/ietf --yangpath .. NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath ${RUN_WITH_CONFD}/etc/confd --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=./ietf-interfaces-common@2017-07-03.yang --module=./ietf-interfaces-ethernet-like@2017-07-03.yang --module=./ietf-flexible-encapsulation@2017-07-04.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --module=./example-vlans.yang --module=./composite-match.yang --modpath=./:/usr/share/yuma/modules/ --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --module=./ietf-interfaces-common@2017-07-03.yang --module=./ietf-interfaces-ethernet-like@2017-07-03.yang --module=./ietf-flexible-encapsulation@2017-07-04.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --module=./example-vlans.yang --module=./composite-match.yang --modpath=./:/usr/share/yuma/modules/ --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 ././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootrootyuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/ietf-interfaces-common@2017-07-03.yangyuma123_2.14/netconf/test/netconfd/ietf-netmod-sub-intf-vlan-model/ietf-interfaces-common@2017-07-030000664000175000017500000003510114770023131032671 0ustar vladimirvladimirmodule ietf-interfaces-common { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces-common"; prefix if-cmn; import ietf-interfaces { prefix if; } import iana-if-type { prefix ianaift; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Lou Berger WG Chair: Kent Watsen Editor: Robert Wilton "; description "This module contains common definitions for extending the IETF interface YANG model (RFC 7223) with common configurable layer 2 properties. Copyright (c) 2016, 2017 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of XXX; see the RFC itself for full legal notices."; revision 2017-07-03 { description "Initial version"; reference "Internet draft: draft-ietf-netmod-intf-ext-yang-05"; } feature reservable-bandwidth { description "This feature indicates that the device supports configuring 'reservable-bandwidth' on interfaces."; reference "RFC XXX, Section 3.1 Reservable Bandwidth"; } feature carrier-delay { description "This feature indicates that configurable interface carrier delay is supported, which is a feature is used to limit the propagation of very short interface link state flaps."; reference "RFC XXX, Section 3.2 Carrier Delay"; } feature dampening { description "This feature indicates that the device supports interface dampening, which is a feature that is used to limit the propagation of interface link state flaps over longer periods"; reference "RFC XXX, Section 3.3 Dampening"; } feature loopback { description "This feature indicates that configurable interface loopback is supported."; reference "RFC XXX, Section 3.5 Loopback"; } feature configurable-l2-mtu { description "This feature indicates that the device supports configuring layer 2 MTUs on interfaces. Such MTU configurations include the layer 2 header overheads (but exclude any FCS overhead). The payload MTU available to higher layer protocols is either derived from the layer 2 MTU, taking into account the size of the layer 2 header, or is further restricted by explicit layer 3 or protocol specific MTU configuration."; reference "RFC XXX, Section 3.6 Layer 2 MTU"; } feature sub-interfaces { description "This feature indicates that the device supports the instantiation of sub-interfaces. Sub-interfaces are defined as logical child interfaces that allow features and forwarding decisions to be applied to a subset of the traffic processed on the specified parent interface."; reference "RFC XXX, Section 3.7 Sub-interface"; } feature forwarding-mode { description "This feature indicates that the device supports the configurable forwarding mode leaf"; reference "RFC XXX, Section 3.8 Forwarding Mode"; } /* * Define common identities to help allow interface types to be * assigned properties. */ identity sub-interface { description "Base type for generic sub-interfaces. New or custom interface types can derive from this type to inherit generic sub-interface configuration"; reference "RFC XXX, Section 3.7 Sub-interface"; } identity ethSubInterface{ base ianaift:l2vlan; base sub-interface; description "This identity represents the child sub-interface of any interface types that uses Ethernet framing (with or without 802.1Q tagging)"; } identity loopback { description "Base identity for interface loopback options"; reference "RFC XXX, section 3.5"; } identity loopback-internal { base loopback; description "All egress traffic on the interface is internally looped back within the interface to be received on the ingress path."; reference "RFC XXX, section 3.5"; } identity loopback-line { base loopback; description "All ingress traffic received on the interface is internally looped back within the interface to the egress path."; reference "RFC XXX, section 3.5"; } identity loopback-connector { base loopback; description "The interface has a physical loopback connector attached that loops all egress traffic back into the interface's ingress path, with equivalent semantics to loopback-internal"; reference "RFC XXX, section 3.5"; } identity forwarding-mode { description "Base identity for forwarding-mode options."; reference "RFC XXX, section 3.8"; } identity optical-layer { base forwarding-mode; description "Traffic is being forwarded at the optical layer. This includes DWDM or OTN based switching."; reference "RFC XXX, section 3.8"; } identity layer-2-forwarding { base forwarding-mode; description "Layer 2 based forwarding, such as Ethernet/VLAN based switching, or L2VPN services."; reference "RFC XXX, section 3.8"; } identity network-layer { base forwarding-mode; description "Network layer based forwarding, such as IP, MPLS, or L3VPNs."; reference "RFC XXX, section 3.8"; } /* * Augments the IETF interfaces model with a leaf to explicitly * specify the bandwidth available on an interface. */ augment "/if:interfaces/if:interface" { description "Augments the IETF interface model with optional common interface level commands that are not formally covered by any specific standard."; leaf reservable-bandwidth { if-feature "reservable-bandwidth"; type uint64; units kbps; description "The reservable-bandwidth configuration leaf allows the bandwidth of an interface reported to upper layer protocols to be changed (either higher or lower) from the inherent interface bandwidth. The reservable-bandwidth leaf can affect the routing metric cost associated with the interface, but it does not directly limit the amount of traffic that can be sent/received over the interface. If required, interface traffic can be limited to the required bandwidth by configuring an explicit QoS policy."; reference "RFC XXX, section 3.1"; } /* * Defines standard YANG for the Carrier Delay feature. */ container carrier-delay { if-feature "carrier-delay"; description "Holds carrier delay related feature configuration"; leaf down { type uint32; units milliseconds; description "Delays the propagation of a 'loss of carrier signal' event that would cause the interface state to go down, i.e. the command allows short link flaps to be suppressed. The configured value indicates the minimum time interval (in milliseconds) that the carrier signal must be continuously down before the interface state is brought down. If not configured, the behaviour on loss of carrier signal is vendor/interface specific, but with the general expectation that there should be little or no delay."; } leaf up { type uint32; units milliseconds; description "Defines the minimum time interval (in milliseconds) that the carrier signal must be continuously present and error free before the interface state is allowed to transition from down to up. If not configured, the behaviour is vendor/interface specific, but with the general expectation that sufficient default delay should be used to ensure that the interface is stable when enabled before being reported as being up. Configured values that are too low for the hardware capabilties may be rejected."; } reference "RFC XXX, Section 3.2 Carrier Delay"; } /* * Augments the IETF interfaces model with a container to hold * generic interface dampening */ container dampening { if-feature "dampening"; presence "Enable interface link flap dampening with default settings (that are vendor/device specific)"; description "Interface dampening limits the propagation of interface link state flaps over longer periods"; reference "RFC XXX, Section 3.3 Dampening"; leaf half-life { type uint32; units seconds; description "The Time (in seconds) after which a penalty reaches half its original value. Once the interface has been assigned a penalty, the penalty is decreased by half after the half-life period. For some devices, the allowed values may be restricted to particular multiples of seconds. The default value is vendor/device specific."; reference "RFC XXX, Section 3.3.2 Half-Life Period"; } leaf reuse { type uint32; description "Penalty value below which a stable interface is unsuppressed (i.e. brought up) (no units). The default value is vendor/device specific. The penalty value for a link up->down state change is nominally 1000 units."; reference "RFC XXX, Section 3.3.3 Reuse Threshold"; } leaf suppress { type uint32; description "Limit at which an interface is suppressed (i.e. held down) when its penalty exceeds that limit (no units). The value must be greater than the reuse threshold. The default value is vendor/device specific. The penalty value for a link up->down state change is nominally 1000 units."; reference "RFC XXX, Section 3.3.1 Suppress Threshold"; } leaf max-suppress-time { type uint32; units seconds; description "Maximum time (in seconds) that an interface can be suppressed. This value effectively acts as a ceiling that the penalty value cannot exceed. The default value is vendor/device specific."; reference "RFC XXX, Section 3.3.4 Maximum Suppress Time"; } } /* * Various types of interfaces support a configurable layer 2 * encapsulation, any that are supported by YANG should be * listed here. * * Different encapsulations can hook into the common encaps-type * choice statement. */ container encapsulation { when "derived-from-or-self(../if:type, 'ianaift:ethernetCsmacd') or derived-from-or-self(../if:type, 'ianaift:ieee8023adLag') or derived-from-or-self(../if:type, 'ianaift:pos') or derived-from-or-self(../if:type, 'ianaift:atmSubInterface') or derived-from-or-self(../if:type, 'ethSubInterface')" { description "All interface types that can have a configurable L2 encapsulation"; } description "Holds the OSI layer 2 encapsulation associated with an interface"; choice encaps-type { description "Extensible choice of layer 2 encapsulations"; reference "RFC XXX, Section 3.4 Encapsulation"; } } /* * Various types of interfaces support loopback configuration, * any that are supported by YANG should be listed here. */ leaf loopback { when "derived-from-or-self(../if:type, 'ianaift:ethernetCsmacd') or derived-from-or-self(../if:type, 'ianaift:sonet') or derived-from-or-self(../if:type, 'ianaift:atm') or derived-from-or-self(../if:type, 'ianaift:otnOtu')" { description "All interface types that support loopback configuration."; } if-feature "loopback"; type identityref { base loopback; } description "Enables traffic loopback."; reference "RFC XXX, Section 3.5 Loopback"; } /* * Many types of interfaces support a configurable layer 2 MTU. */ leaf l2-mtu { if-feature "configurable-l2-mtu"; type uint16 { range "64 .. 65535"; } description "The maximum size of layer 2 frames that may be transmitted or received on the interface (excluding any FCS overhead). In the case of Ethernet interfaces it also excludes the 4-8 byte overhead of any known (i.e. explicitly matched by a child sub-interface) 801.1Q VLAN tags."; reference "RFC XXX, Section 3.6 Layer 2 MTU"; } /* * Augments the IETF interfaces model with a leaf that indicates * which mode, or layer, is being used to forward the traffic. */ leaf forwarding-mode { if-feature "forwarding-mode"; type identityref { base forwarding-mode; } description "The forwarding mode that the interface is operating in."; reference "RFC XXX, Section 3.8 Forwarding Mode"; } } /* * Add generic support for sub-interfaces. * * This should be extended to cover all interface types that are * child interfaces of other interfaces. */ augment "/if:interfaces/if:interface" { when "derived-from(if:type, 'sub-interface') or derived-from-or-self(if:type, 'ianaift:atmSubInterface') or derived-from-or-self(if:type, 'ianaift:frameRelay')" { description "Any ianaift:types that explicitly represent sub-interfaces or any types that derive from the sub-interface identity"; } if-feature "sub-interfaces"; description "Add a parent interface field to interfaces that model sub-interfaces"; leaf parent-interface { type if:interface-ref; mandatory true; description "This is the reference to the parent interface of this sub-interface."; reference "RFC XXX, Section 3.7 Sub-interface"; } } } yuma123_2.14/netconf/test/netconfd/test-xpath-deref0000775000175000017500000000005014770023131022443 0ustar vladimirvladimir#!/bin/bash -e cd xpath-deref ./run.sh yuma123_2.14/netconf/test/netconfd/nacm/0000775000175000017500000000000014770023131020254 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/nacm/mod1.yang0000664000175000017500000000035114770023131021773 0ustar vladimirvladimirmodule mod1 { prefix m1; namespace "http://yuma123.org/ns/test/netconfd/nacm/mod1"; container top { leaf foo { type string; } leaf bar { type string; } leaf baz { type string; } } } yuma123_2.14/netconf/test/netconfd/nacm/session.yangcli.py0000664000175000017500000000561414770023131023744 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli from lxml import etree import yangrpc def main(): parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") parser.add_argument("--operation", help="One of {configure-nacm, create, replace, read}") parser.add_argument("--option", help="One of {foo, bar, baz}") args = parser.parse_args() print((""" #Description: Validate nacm --operation=%(operation)s --option=%(option)s #Procedure: #1 - Do %(operation)s. """ % {'operation': args.operation, 'option':args.option})) if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) if(args.operation == 'configure-nacm'): ok = yangcli(conn, '''create /nacm/groups/group[name='my-operators-group']/user-name value=%s''' % (user)).xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''create /nacm/rule-list[name='my-operators-rules'] -- group=my-operators-group''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''create /nacm/rule-list[name='my-operators-rules']/rule[name='my-foo-all-permit'] -- module-name=m1 path=/top/foo access-operations=* action=permit comment='With this rule the user can create /top/foo' ''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''create /nacm/rule-list[name='my-operators-rules']/rule[name='my-baz-read-deny'] -- module-name=m1 path=/top/baz access-operations=read action=deny comment='With this rule the user can not read /top/baz' ''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) elif(args.operation == 'get'): result = yangcli(conn, '''sget /top/%s''' % (args.option)) value = result.xpath('./data/top/%s' % (args.option)) print(etree.tostring(result, pretty_print=True, inclusive_ns_prefixes=True)) assert(len(value)==1) assert(value[0].text==args.option) else: ok = yangcli(conn, '''%s /top/%s value=%s''' % (args.operation, args.option, args.option)).xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) print("done") return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/nacm/run.sh0000775000175000017500000000513714770023131021425 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp #netconfd killall -KILL netconfd || true rm /tmp/ncxserver.sock || true echo '' > tmp/startup-cfg.xml /usr/sbin/netconfd --module=mod1.yang --startup=tmp/startup-cfg.xml --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! sleep 3 python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=configure-nacm python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=create --option=foo python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=create --option=bar python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=create --option=baz python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=replace --option=foo python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=replace --option=bar python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=replace --option=baz python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=get --option=foo python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=get --option=bar python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=get --option=baz kill -KILL $SERVER_PID cat tmp/server.log sleep 1 rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=mod1.yang --startup=tmp/startup-cfg.xml --superuser=root 2>&1 1>tmp/server.log & SERVER_PID=$! sleep 3 python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=replace --option=foo ! python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=replace --option=bar || exit 1 ! python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=replace --option=baz || exit 1 python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=get --option=foo python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=get --option=bar ! python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD --operation=get --option=baz || exit 1 #kill -KILL $SERVER_PID #cat tmp/server.log yuma123_2.14/netconf/test/netconfd/test-validate-config-only0000775000175000017500000000006014770023131024250 0ustar vladimirvladimir#!/bin/bash -e cd validate-config-only ./run.sh yuma123_2.14/netconf/test/netconfd/test-xpath-derived-from-or-self0000775000175000017500000000006614770023131025315 0ustar vladimirvladimir#!/bin/bash -e cd xpath-derived-from-or-self ./run.sh yuma123_2.14/netconf/test/netconfd/yang-library/0000775000175000017500000000000014770023131021736 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-library/test-deviation-add-must.yang0000664000175000017500000000071014770023131027267 0ustar vladimirvladimirmodule test-deviation-add-must { namespace "http://yuma123.org/ns/test-deviation-add-must"; prefix test-dam; import ietf-interfaces { prefix if; } organization "yuma123"; description "Test module for deviation-add-must"; revision 2017-03-19 { description "1.st version"; } deviation /if:interfaces/if:interface { deviate add { must "name='foo'" { error-message "Only foo."; } } } } yuma123_2.14/netconf/test/netconfd/yang-library/test-yang-library-import.yang0000664000175000017500000000073714770023131027512 0ustar vladimirvladimirmodule test-yang-library-import { yang-version 1.1; namespace "http://yuma123.org/ns/test-yang-library-import"; prefix tyli; organization "yuma123.org"; description "Part of the yang-library test."; revision 2017-08-03 { description "Initial version"; } typedef my-imported-typedef { type uint32; } container foobar { presence "Data object in modules reported as imported but not implemented should not be accessible."; } } yuma123_2.14/netconf/test/netconfd/yang-library/test-yang-library-deviation.yang0000664000175000017500000000074214770023131030156 0ustar vladimirvladimirmodule test-yang-library-deviation { yang-version 1.1; namespace "http://yuma123.org/ns/test-yang-library-deviation"; prefix tyld; import test-yang-library { prefix tyl; } organization "yuma123.org"; description "Part of the yang-library test."; revision 2017-08-03 { description "Initial version"; } deviation /tyl:foo/tyl:foo { deviate add { must ".=5" { error-message "Only foo=5 supported."; } } } } yuma123_2.14/netconf/test/netconfd/yang-library/session.yangcli.py0000664000175000017500000000434114770023131025422 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli from lxml import etree import yangrpc def main(): print(""" #Description: Demonstrate that leaf values can be replaced. #Procedure: #1 - Create test-yang-library container. Verify commit succeeds. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) time.sleep(1) result = yangcli(conn, '''xget /modules-state/module[name='test-yang-library'] ''') name = result.xpath('./data/modules-state/module/name') assert(len(name)==1) conformance_type = result.xpath('./data/modules-state/module/conformance-type') assert(len(conformance_type)==1) assert(conformance_type[0].text=="implement") result = yangcli(conn, '''xget /modules-state/module[name='test-yang-library-import'] ''') name = result.xpath('./data/modules-state/module/name') assert(len(name)==1) conformance_type = result.xpath('./data/modules-state/module/conformance-type') assert(len(conformance_type)==1) #assert(conformance_type[0].text=="import") ok = yangcli(conn, '''delete /foo''').xpath('./ok') ok = yangcli(conn, '''commit''').xpath('./ok') #assert(len(ok)==1) ok = yangcli(conn, '''create /foo''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/yang-library/session.litenc.py0000664000175000017500000001027014770023131025250 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Demonstrate that ietf-yang-library is implemented. #Procedure: #1 - Verify ietf-yang-library is listed as capability in the hello message. #2 - Verify /modules-state/module[name='test-yang-library'] container is present. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) # receive result=conn.receive() print("#1 - Verify ietf-yang-library is listed as capability in the hello message.") print(lxml.etree.tostring(result)) result=litenc_lxml.strip_namespaces(result) found=False for capability in result.xpath("/hello/capabilities/capability"): #print lxml.etree.tostring(capability) print(capability.text) if(capability.text.startswith('urn:ietf:params:netconf:capability:yang-library:1.0?')): print(("Found it:" + capability.text)) found=True assert(found==True) print("#2 - Verify /modules-state/module[name='test-yang-library'] container is present.") get_rpc = """ test-yang-library """ print("get ...") result = conn.rpc(get_rpc) result=litenc_lxml.strip_namespaces(result) print(lxml.etree.tostring(result)) name = result.xpath('data/modules-state/module/name') assert(len(name)==1) namespace = result.xpath('data/modules-state/module/namespace') assert(len(namespace)==1) assert(namespace[0].text=="http://yuma123.org/ns/test-yang-library") conformance_type = result.xpath('data/modules-state/module/conformance-type') assert(len(conformance_type)==1) assert(conformance_type[0].text=="implement") feature = result.xpath('data/modules-state/module/feature') assert(len(feature)==1) assert(feature[0].text=="foo") deviation = result.xpath('data/modules-state/module/deviation/name') assert(len(deviation)==1) assert(deviation[0].text=="test-yang-library-deviation") get_rpc = """ test-yang-library-import """ print("get ...") result = conn.rpc(get_rpc) result=litenc_lxml.strip_namespaces(result) print(lxml.etree.tostring(result)) name = result.xpath('data/modules-state/module/name') assert(len(name)==1) namespace = result.xpath('data/modules-state/module/namespace') assert(len(namespace)==1) assert(namespace[0].text=="http://yuma123.org/ns/test-yang-library-import") conformance_type = result.xpath('data/modules-state/module/conformance-type') assert(len(conformance_type)==1) #assert(conformance_type[0].text=="import") return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/yang-library/test-yang-library.yang0000664000175000017500000000154114770023131026174 0ustar vladimirvladimirmodule test-yang-library { yang-version 1.1; namespace "http://yuma123.org/ns/test-yang-library"; prefix tyl; import test-yang-library-import { prefix tyli; } organization "yuma123.org"; description "Part of the yang-library test."; revision 2017-08-03 { description "Upgraded the model to cover conformance-type, features and deviations."; } revision 2017-07-26 { description "Initial version"; } feature foo; feature bar; container foo { if-feature foo; presence "Dummy container for testing namespace resolution works and the client retrieved all modules reported in the yang-library capabilities list."; leaf foo { type uint32; } } container bar { if-feature bar; presence "bar."; leaf bar { type tyli:my-imported-typedef; } } } yuma123_2.14/netconf/test/netconfd/yang-library/run.sh0000775000175000017500000000262314770023131023104 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cp *.yang tmp cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c test-yang-library.yang -F test-yang-library:foo --deviation test-yang-library-deviation.yang --yangpath . -o test-yang-library.fxs # imported not implemented - how ? confdc -c test-yang-library-import.yang --yangpath . -o test-yang-library-import.fxs confdc -c test-yang-library-deviation.yang --yangpath . -o test-yang-library-deviation.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=./test-yang-library.yang --deviation=./test-yang-library-deviation.yang --feature-disable=test-yang-library:bar --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/test-ietf-ip-bis0000775000175000017500000000004714770023131022352 0ustar vladimirvladimir#!/bin/bash -e cd ietf-ip-bis ./run.sh yuma123_2.14/netconf/test/netconfd/test-xpath-derived-from0000775000175000017500000000005714770023131023750 0ustar vladimirvladimir#!/bin/bash -e cd xpath-derived-from ./run.sh yuma123_2.14/netconf/test/netconfd/test-identityref-typedef0000775000175000017500000000005714770023131024227 0ustar vladimirvladimir#!/bin/bash -e cd identityref-typedef ./run.sh yuma123_2.14/netconf/test/netconfd/test-leaf-list-defaults0000775000175000017500000000007514770023131023730 0ustar vladimirvladimir#!/bin/sh set -e cd yang-1dot1 ./test-leaf-list-defaults.sh yuma123_2.14/netconf/test/netconfd/ietf-routing/0000775000175000017500000000000014770023131021752 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/ietf-routing/session.yangcli.py0000664000175000017500000000503114770023131025433 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli import lxml from lxml import etree import yangrpc def yangcli_ok_script(conn, yangcli_script): for line in yangcli_script.splitlines(): line=line.strip() if not line: continue print(("Executing: "+line)) ok = yangcli(conn, line).xpath('./ok') assert(len(ok)==1) def main(): print(""" #Description: Usecase fo ietf-routing module. #Procedure: #1 - Create interface "eth0" of type=ethernetCsmacd with static ip=10.0.0.2/24 and static default route for destination-prefix=0.0.0.0/0 and next-hop=10.0.0.1. #2 - Validate returns identical result as the example in RFC8022 Appendix D. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa", "--dump-session=tmp/nc-session-") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) time.sleep(1) result = yangcli(conn, "delete /interfaces") result = yangcli(conn, "commit") ok = result.xpath('./ok') assert(len(ok)==1) yangcli_script=''' create /interfaces/interface -- name=eth0 type=ethernetCsmacd description="Uplink to ISP." create /interfaces/interface[name='eth0']/ipv4/address -- ip=192.0.2.1 prefix-length=24 commit ''' #create /interfaces/interface[name='eth0']/ipv4/forwarding value=true #create /routes/control-plane-protocols/control-plane-protocol -- name=st0 type=static #create /routes/control-plane-protocols/control-plane-protocol[name='st0']/static-routes/ipv4/route -- destination-prefix=0.0.0.0/0 next-hop/next-hop-address=192.0.2.2 #commit #''' yangcli_ok_script(conn, yangcli_script) result = yangcli(conn, "xget /interfaces") print(lxml.etree.tostring(result)) return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/ietf-routing/session.litenc.py0000664000175000017500000001544414770023131025274 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml from lxml import etree from io import StringIO import argparse # Hmm .. ?! def yang_data_equal(e1, e2): if e1.tag != e2.tag: assert(False) return False if e1.text==None and "" != e2.text.strip(): assert(False) return False if e2.text==None and "" != e1.text.strip(): assert(False) return False if e1.text!=None and e2.text!=None and e1.text.strip() != e2.text.strip(): assert(False) return False #if e1.tail != e2.tail: assert(False) return False if e1.attrib != e2.attrib: assert(False) print("diff attrib") return False if len(e1) != len(e2): print(e1) print(len(e1)) print(e2) print(len(e2)) assert(False) return False return all(yang_data_equal(c1, c2) for c1, c2 in zip(e1, e2)) def main(): print(""" #Description: Testcase for RFC8022 ietf-routing module. #Procedure: #1 - configuration as in RFC8022 Appendix D. #2 - the /interfaces /interfaces-state /routing /routing-state and verify data is same as in RFC8022 Appendix D. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw,strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") get_yang_library_rpc = """ """ print(" - /ietf-yang-library:modules-state ...") result = conn.rpc(get_yang_library_rpc, strip_ns=False) print(lxml.etree.tostring(result, pretty_print=True, inclusive_ns_prefixes=True)) namespaces = {"nc":"urn:ietf:params:xml:ns:netconf:base:1.0"} data = result.xpath('./nc:data', namespaces=namespaces) assert(len(data)==1) edit_config_rpc = """ merge set eth0 Uplink to ISP. ianaift:ethernetCsmacd
192.0.2.1 24
""" print(" - load example config to 'candidate' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==1) commit_rpc = '' print(" - commit example config ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==1) get_example_data_rpc = """ """ print(" - example data ...") result = conn.rpc(get_example_data_rpc, strip_ns=False) print(lxml.etree.tostring(result, pretty_print=True, inclusive_ns_prefixes=True)) namespaces = {"nc":"urn:ietf:params:xml:ns:netconf:base:1.0"} data = result.xpath('./nc:data', namespaces=namespaces) assert(len(data)==1) expected=""" eth0 Uplink to ISP. ianaift:ethernetCsmacd
192.0.2.1 24
eth0 Uplink to ISP. ianaift:ethernetCsmacd 00:0C:42:E5:B1:E9 up 2015-10-24T17:11:27+02:00
192.0.2.1 24
""" data_expected=etree.parse(StringIO(lxml.etree.tostring(lxml.etree.fromstring(expected), pretty_print=True))) data_received=etree.parse(StringIO(lxml.etree.tostring(data[0], pretty_print=True))) a = StringIO(); b = StringIO(); data_expected.write_c14n(a) data_received.write_c14n(b) if yang_data_equal(lxml.etree.fromstring(a.getvalue()), lxml.etree.fromstring(b.getvalue())): print("Bingo!") return 0 else: print("Expected (A) different from received (B):") print("A:") print(a.getvalue()) print("B:") print(b.getvalue()) return 1 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/ietf-routing/Makefile.am0000775000175000017500000000047014770023131024012 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libtest-ietf-routing.la libtest_ietf_routing_la_SOURCES = \ test-ietf-routing.c libtest_ietf_routing_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libtest_ietf_routing_la_LDFLAGS = -module -lyumaagt -lyumancx yuma123_2.14/netconf/test/netconfd/ietf-routing/test-ietf-routing.c0000664000175000017500000000630714770023131025515 0ustar vladimirvladimir/* module test-ietf-routing */ #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "val.h" #include "val123.h" #include "val_set_cplxval_obj.h" /* module static variables */ static ncx_module_t *ietf_interfaces_mod; static ncx_module_t *ietf_ip_mod; static ncx_module_t *ietf_routing_mod; static obj_template_t* interfaces_state_obj; /* Registered callback functions: get_interfaces_state */ static status_t get_interfaces_state(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res = NO_ERR; res = val_set_cplxval_obj(dst_val,dst_val->obj,"\ \ eth0\ Uplink to ISP.\ ianaift:ethernetCsmacd\ 00:0C:42:E5:B1:E9\ up\ \ 2015-10-24T17:11:27+02:00\ \ \
\ 192.0.2.1\ 24\
\
\
\
"); assert(res == NO_ERR); return res; } /* The 3 mandatory callback functions: y_test_ietf_routing_init, y_test_ietf_routing_init2, y_test_ietf_routing_cleanup */ status_t y_test_ietf_routing_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-interfaces", NULL, &agt_profile->agt_savedevQ, &ietf_interfaces_mod); if (res != NO_ERR) { return res; } interfaces_state_obj = ncx_find_object( ietf_interfaces_mod, "interfaces-state"); assert(interfaces_state_obj != NULL); return res; } status_t y_test_ietf_routing_init2(void) { status_t res; cfg_template_t* runningcfg; val_value_t* interfaces_state_val; res = NO_ERR; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (!runningcfg || !runningcfg->root) { return SET_ERROR(ERR_INTERNAL_VAL); } interfaces_state_val = val_find_child(runningcfg->root, "ietf-interfaces", "interfaces-state"); assert(interfaces_state_val==NULL); interfaces_state_val = val_new_value(); assert(interfaces_state_val); val_init_virtual(interfaces_state_val, get_interfaces_state, interfaces_state_obj); val_add_child(interfaces_state_val, runningcfg->root); return NO_ERR; } void y_test_ietf_routing_cleanup (void) { } yuma123_2.14/netconf/test/netconfd/ietf-routing/run.sh0000775000175000017500000000260514770023131023120 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc cd tmp for module in ietf-interfaces iana-if-type ietf-ip ietf-routing@2016-11-04 ietf-ipv4-unicast-routing@2016-11-04 ; do cp ../../../../modules/ietf/${module}.yang . confdc -c ${module}.yang --yangpath ../../../../ done NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath ${RUN_WITH_CONFD}/etc/confd --addloadpath . SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=iana-if-type --module=ietf-ip --module=ietf-ipv4-unicast-routing@2016-11-04 --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --module=iana-if-type --module=ietf-ip --module=ietf-ipv4-unicast-routing@2016-11-04 --module=test-ietf-routing --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD #python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD #kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/README0000664000175000017500000000034614770023131020221 0ustar vladimirvladimirIf you have installed netconfd, python-ncclient, python-paramiko and python-lxml and configured OpenSSH to accept netconf connections on port 830 then: autoreconf -i -f ./configure --prefix=/usr make sudo make install make check yuma123_2.14/netconf/test/netconfd/yang-1dot1/0000775000175000017500000000000014770023131021222 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-1dot1/test-enum-iffeature-pass.sh0000775000175000017500000000105214770023131026414 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then exit 0 else killall -KILL netconfd || true rm -f /tmp/ncxserver.sock /usr/sbin/netconfd --modpath=`pwd` --module=test-enum-iffeature --feature-enable=extra-enum --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --modpath=`pwd` --module=test-enum-iffeature --feature-enable=extra-enum --no-startup --superuser=$USER 1>tmp/server.log 2>&1 & SERVER_PID=$! fi sleep 3 python test-enum-iffeature.py res=$? killall -KILL netconfd || true exit ${res} yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-notification-in-grouping.yang0000664000175000017500000000120714770023131030001 0ustar vladimirvladimirmodule test-notification-in-grouping { yang-version 1.1; namespace "urn:labn:params:xml:ns:yang:test-notification-in-grouping"; prefix test; organization ""; contact ""; description "Expand a grouping that holds a notification."; revision 2020-06-06 { reference ""; } grouping grouping-with-notification { description "Take notice."; notification something-happened { description "A notification indicating that something happened."; leaf some-information { type int8; mandatory true; description "a number"; } } } container top-notifications { uses grouping-with-notification; } } yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-leaf-list-defaults.yang0000664000175000017500000001453214770023131026551 0ustar vladimirvladimirmodule test-leaf-list-defaults { yang-version 1.1; namespace "urn:labn:params:xml:ns:yang:test-leaf-list-defaults"; prefix test; organization ""; contact ""; description "A model to test logic that decides when leaf-list default values are used. See RFC 7950 section 7.7.2. These test cases are supported by this model: 1) no ancestor present that is not a non-presence container, i.e., all ancestors are non-presence containers. --> shoud use defaults 2) nearest ancestor that is not a non-presence container is a presence container. --> should use defaults 3) nearest ancestor that is not a non-presence container is a CASE that also happens to be the default CASE and it has no child nodes. --> should use defaults 4) nearest ancestor that is not a non-presence container is a CASE that also happens to be the default CASE and it has child nodes. --> should use defaults 5) nearest ancestor that is not a non-presence container is a CASE that is not the default case and that has child nodes. --> should use defaults 6) nearest ancestor that is not a non-presence container is a CASE that is not the default case and that has no child nodes. --> should _not_ use defaults 7) leaf-list has if-feature that evaluates to false. --> should _not_ use defaults 8) leaf-list has 'must' that evaluates to false. --> should _not_ use defaults 9) ancestor of leaf-list has if-feature that evaluates to false. --> should _not_ use defaults 10) leaf-list has multiple default values. Ensure that all values reported. --> should use defaults. 11) leaf-list has multiple default values and _one_ configured value. --> should _not_ use defaults."; revision 2020-06-13 { description "Initial revision."; reference ""; } feature always-disabled-feature { description "This should never be enabled and is used to disable ancestor nodes of a leaf-leaf."; } container test-one { leaf-list data { type int8; default 3; description "data"; } description "1) no ancestor present that is not a non-presence container, i.e., all ancestors are non-presence containers. --> shoud use defaults"; } container test-two { presence "true"; leaf-list data { type int8; default 3; description "data"; } description "2) nearest ancestor that is not a non-presence container is a presence container. --> should use defaults"; } container test-three { choice data-choice { case first { leaf-list data { type int8; default 3; description ""; } description ""; } case second { leaf other-data { type int8; description ""; } description ""; } default "first"; description ""; } description "3) nearest ancestor that is not a non-presence container is a CASE that also happens to be the default CASE and it has no child nodes. --> should use defaults"; } container test-four { choice data-choice { case first { leaf-list data { type int8; default 3; description ""; } leaf should-have-a-value { type int8; description "The test program should ensure that this leaf has a value in the data tree."; } description "The default"; } case second { leaf other-data { type int8; description ""; } description ""; } default "first"; description ""; } description "4) nearest ancestor that is not a non-presence container is a CASE that also happens to be the default CASE and it has child nodes. --> should use defaults"; } container test-five { choice data-choice { case first { leaf-list data { type int8; default 3; description ""; } leaf should-have-a-value { type int8; description "The test program should ensure that this leaf has a value in the data tree."; } description ""; } case second { leaf other-data { type int8; description ""; } description "The default"; } default "second"; description ""; } description "5) nearest ancestor that is not a non-presence container is a CASE that is not the default case and that has child nodes. --> should use defaults"; } container test-six { choice data-choice { case first { leaf-list data { type int8; default 3; description ""; } leaf should-not-have-a-value { type int8; description "The test program should ensure that this leaf does not have a value in the data tree."; } description ""; } case second { leaf other-data { type int8; description ""; } description "The default"; } default "second"; description ""; } description "6) nearest ancestor that is not a non-presence container is a CASE that is not the default case and that has no child nodes. --> should _not_ use defaults"; } container test-seven { leaf-list data { if-feature "always-disabled-feature"; type int8; default 3; description "data"; } description "7) leaf-list has if-feature that evaluates to false. --> should _not_ use defaults"; } container test-eight { leaf missing-data { type int8; description "The test program should never create this value so that the 'when' statement in the leaf-list always evaluates to 'false'."; } leaf-list data { type int8; when "../missing-data=4"; default 3; description "data"; } description "8) leaf-list has 'must' that evaluates to false. --> should _not_ use defaults"; } container test-nine { if-feature "always-disabled-feature"; leaf-list data { type int8; default 3; description "data"; } description "9) ancestor of leaf-list has if-feature that evaluates to false. --> should _not_ use defaults"; } container test-ten { leaf-list data { type int8; default 3; default 4; default 5; description "data"; } description "10) leaf-list has multiple default values. Ensure that all values reported. --> should use defaults."; } container test-eleven { leaf-list data { type int8; default 3; default 4; default 5; description "data"; } description "11) leaf-list has multiple default values and _one_ configured value. --> should _not_ use defaults."; } } yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-enum-iffeature-fail.sh0000775000175000017500000000107214770023131026363 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then exit 0 else killall -KILL netconfd || true rm -f /tmp/ncxserver.sock /usr/sbin/netconfd --modpath=`pwd` --module=test-enum-iffeature --feature-disable=extra-enum --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --modpath=`pwd` --module=test-enum-iffeature --feature-disable=extra-enum --no-startup --superuser=$USER 1>tmp/server.log 2>&1 & SERVER_PID=$! fi sleep 3 python test-enum-iffeature.py --expectfail res=$? killall -KILL netconfd || true exit ${res} yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-enum-iffeature.py0000664000175000017500000000735314770023131025475 0ustar vladimirvladimir#!/usr/bin/env python # Run netconfd with "--module=test-enum-iffeature.yang # --feature-enable=extra-enum" to test for an expected # success setting the leaf value and run netconfd with # "--module=test-enum-iffeature.yang --feature-disable=extra-enum" # to test for an expected failure. import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse DEBUG = False rpc_discard_changes = """ """ rpc_create = """ merge set test-enum """ # Examine the return value of _XSLTResultTree.xpath() and decide if # evaluating the xpath expression reflects a successful test run. def eval_xpath_results(reply, hdr, exp): rv = 0 errmsg = '%s: failed to evaluate XPATH expression "%s"' res = reply.xpath(exp) if type(res) == type([]) and len(res) == 0: # res[*] --> if DEBUG: print((errmsg % (hdr, exp))) rv = 2 elif type(res) == type(True) and res == False: if DEBUG: print((errmsg % (hdr, exp))) rv = 2 #elif type(res) == type(1.0): #elif type(res) == type(""): return rv # returns (status, reply-xml) def send_rpc(conn, rpcxml, expected=None): rv = 0 reply = conn.rpc(rpcxml) if not reply: return (1, None) if expected is not None: for exp in expected: rv = eval_xpath_results(reply, 'send_rpc', exp) if rv > 0: break else: rv = eval_xpath_results(reply, 'send_rpc', '//ok') return (rv, reply) def main(): print("#Description: Attempt to create a leaf with a conditional enum.") parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") parser.add_argument("--expectfail", help="Feature is disabled and RPC is expected to fail", action='store_true') args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print(("[FAILED] Connecting to server=%s:" % {'server':server})) return(-1) print(("[OK] Connecting to server=%(server)s:" % {'server':server})) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) # receive result=conn.receive() exp = None if args.expectfail: exp = ['//bad-value'] rv, reply = send_rpc(conn, rpc_discard_changes) if rv > 0: return rv rv, reply = send_rpc(conn, rpc_create, exp) if rv > 0: return rv return 0 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-identity-iffeature-pass.sh0000775000175000017500000000107614770023131027307 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then exit 0 else killall -KILL netconfd || true rm -f /tmp/ncxserver.sock /usr/sbin/netconfd --modpath=`pwd` --module=test-identity-iffeature --feature-enable=extra-identity --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --modpath=`pwd` --module=test-identity-iffeature --feature-enable=extra-identity --no-startup --superuser=$USER 1>tmp/server.log 2>&1 & SERVER_PID=$! fi sleep 3 python test-identity-iffeature.py res=$? killall -KILL netconfd || true exit ${res} yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-enum-iffeature.yang0000664000175000017500000000104414770023131025772 0ustar vladimirvladimirmodule test-enum-iffeature { yang-version 1.1; namespace "http://yuma123.org/ns/test-enum-iffeature"; prefix test; organization ""; contact ""; description "Create an enum leaf to check if an if-feature statement within an enum can be used to disable that enum."; revision 2020-09-22 { reference ""; } feature extra-enum { description "The enum is available when this feature is enabled."; } container top { leaf the-test { type enumeration { enum test-enum { if-feature extra-enum; } } } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-leaf-list-defaults.py0000664000175000017500000002110514770023131026235 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse DEBUG = False rpc_discard_changes = """ """ rpc_get_config_with_defaults = """ report-all """ rpc_create_test_simple = """ merge set """ rpc_create_test_case = """ merge set 1 """ rpc_create_test_data = """ merge set 11 """ # TESTS = list-of-test-details = [ , ... ] # test-details = { 'RPC': , # 'edit-config-results': , # 'expected-results': , # 'unexpected-results': , # 'name': } tests = [ { 'RPC': rpc_create_test_simple % ('one'), 'expected-results': [ '//test-one/data=3' ], 'unexpected-results': [], 'name': 'Test 1 - ancestors are non-presence containers', 'edit-config-results': None }, { 'RPC': rpc_create_test_simple % ('two'), 'expected-results': [ '//test-two/data=3' ], 'unexpected-results': [], 'name': 'Test 2 - one ancestor is presence container', 'edit-config-results': None }, { 'RPC': rpc_create_test_simple % ('three'), 'expected-results': [ '//test-three/data=3' ], 'unexpected-results': [], 'name': 'Test 3 - case is default without child nodes, has empty leaf-list', 'edit-config-results': None }, { 'RPC': rpc_create_test_case % tuple(['four']*2), 'expected-results': [ '//test-four/data=3' ], 'unexpected-results': [], 'name': 'Test 4 - case is default with child nodes, has empty leaf-list', 'edit-config-results': None }, { 'RPC': rpc_create_test_case % tuple(['five']*2), 'expected-results': [ '//test-five/data=3' ], 'unexpected-results': [], 'name': 'Test 5 - case has child node and is not default', 'edit-config-results': None }, { 'RPC': rpc_create_test_simple % ('six'), 'expected-results': ['//test-six'], 'unexpected-results': [ '//test-six/data=3' ], 'name': 'Test 6 - case has no child node and is not default', 'edit-config-results': None }, { 'RPC': rpc_create_test_simple % ('seven'), 'expected-results': ['//test-seven'], 'unexpected-results': [ '//test-seven/data=3' ], 'name': 'Test 7 - leaf-list if-feature evaluates to false', 'edit-config-results': None }, { 'RPC': rpc_create_test_simple % ('eight'), 'expected-results': ['//test-eight'], 'unexpected-results': [ '//test-eight/data=3' ], 'name': 'Test 8 - leaf-list "when" evaluates to false', 'edit-config-results': None }, { 'RPC': rpc_create_test_simple % ('nine'), 'expected-results': [], 'unexpected-results': [ '//test-nine', '//test-nine/data=3' ], 'name': 'Test 9 - ancestor has if-feature that evaluates to false', 'edit-config-results': ['//rpc-error/error-info/bad-element'], }, { 'RPC': rpc_create_test_simple % ('ten'), 'expected-results': [ '//test-ten/data=3', '//test-ten/data=4', '//test-ten/data=5' ], 'unexpected-results': [], 'name': 'Test 10 - leaf-list has multiple default values.', 'edit-config-results': None }, { 'RPC': rpc_create_test_data % tuple(['eleven']*2), 'expected-results': [ '//test-eleven/data=11' ], 'unexpected-results': [ '//test-eleven/data=3', '//test-eleven/data=4', '//test-evelen/data=5' ], 'name': 'Test 11 - leaf-list has multiple default values and _one_ configured value', 'edit-config-results': None } ] # Examine the return value of _XSLTResultTree.xpath() and decide if # evaluating the xpath expression reflects a successful test run. def eval_xpath_results(reply, hdr, exp): rv = 0 errmsg = '%s: failed to evaluate XPATH expression "%s"' res = reply.xpath(exp) if type(res) == type([]) and len(res) == 0: # res[*] --> if DEBUG: print((errmsg % (hdr, exp))) rv = 2 elif type(res) == type(True) and res == False: if DEBUG: print((errmsg % (hdr, exp))) rv = 2 #elif type(res) == type(1.0): #elif type(res) == type(""): return rv # returns (status, reply-xml) def send_rpc(conn, rpcxml, expected=None): rv = 0 reply = conn.rpc(rpcxml) if not reply: return (1, None) if expected is not None: for exp in expected: rv = eval_xpath_results(reply, 'send_rpc', exp) if rv > 0: break else: rv = eval_xpath_results(reply, 'send_rpc', '//ok') return (rv, reply) def run_one_test(conn, desc): (rv, reply) = send_rpc(conn, rpc_discard_changes) if rv > 0: print(('%s: sending discard-changes RPC failed.' % desc['name'])) return (desc['name'], rv) (rv, reply) = send_rpc(conn, desc['RPC'], desc['edit-config-results']) if rv > 0: print(('%s: sending test RPC failed.' % desc['name'])) return (desc['name'], rv) (rv, reply) = send_rpc(conn, rpc_get_config_with_defaults, ['//data']) if rv > 0: print(('%s: sending get-config RPC failed.' % desc['name'])) else: for exp in desc['expected-results']: rv = eval_xpath_results(reply, desc['name'], exp) if rv > 0: break if rv == 0: for exp in desc['unexpected-results']: rv = eval_xpath_results(reply, desc['name'], exp) rv = 0 if rv > 0 else 2 if rv > 0: break return (desc['name'], rv) def run_tests(conn, alltests): results = [] for t in alltests: results.append(run_one_test(conn, t)) return results def main(): parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") parser.add_argument("--debug", help="show debug messages (not yuma123 library debugging", action='store_true') args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password if args.debug: DEBUG = True conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print(("[FAILED] Connecting to server=%s:" % {'server':server})) return(-1) print(("[OK] Connecting to server=%(server)s:" % {'server':server})) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) # receive result=conn.receive() results = run_tests(conn, tests) ok = True for x in results: print(("%-70.70s : %s" % (x[0], 'PASS' if x[1] == 0 else 'FAIL'))) if x[1] > 0: ok = False return 0 if ok else 1 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-identity-iffeature-fail.sh0000775000175000017500000000111614770023131027247 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then exit 0 else killall -KILL netconfd || true rm -f /tmp/ncxserver.sock /usr/sbin/netconfd --modpath=`pwd` --module=test-identity-iffeature --feature-disable=extra-identity --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --modpath=`pwd` --module=test-identity-iffeature --feature-disable=extra-identity --no-startup --superuser=$USER 1>tmp/server.log 2>&1 & SERVER_PID=$! fi sleep 3 python test-identity-iffeature.py --expectfail res=$? killall -KILL netconfd || true exit ${res} yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-identity-iffeature.py0000664000175000017500000000754014770023131026360 0ustar vladimirvladimir#!/usr/bin/env python # Run netconfd with "--module=test-identity-iffeature.yang # --feature-enable=extra-identity" to test for an expected # success setting the leaf value and run netconfd with # "--module=test-identity-iffeature.yang --feature-disable=extra-identity" # to test for an expected failure. import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse DEBUG = False rpc_discard_changes = """ """ rpc_create = """ merge set test:derived-identity """ # Examine the return value of _XSLTResultTree.xpath() and decide if # evaluating the xpath expression reflects a successful test run. def eval_xpath_results(reply, hdr, exp): rv = 0 errmsg = '%s: failed to evaluate XPATH expression "%s"' res = reply.xpath(exp) if type(res) == type([]) and len(res) == 0: # res[*] --> if DEBUG: print((errmsg % (hdr, exp))) rv = 2 elif type(res) == type(True) and res == False: if DEBUG: print((errmsg % (hdr, exp))) rv = 2 #elif type(res) == type(1.0): #elif type(res) == type(""): return rv # returns (status, reply-xml) def send_rpc(conn, rpcxml, expected=None): rv = 0 reply = conn.rpc(rpcxml) if not reply: return (1, None) if expected is not None: for exp in expected: rv = eval_xpath_results(reply, 'send_rpc', exp) if rv > 0: break else: rv = eval_xpath_results(reply, 'send_rpc', '//ok') return (rv, reply) def main(): print("#Description: Attempt to create a leaf with a conditional identity.") parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") parser.add_argument("--expectfail", help="Feature is disabled and RPC is expected to fail", action='store_true') args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print(("[FAILED] Connecting to server=%s:" % {'server':server})) return(-1) print(("[OK] Connecting to server=%(server)s:" % {'server':server})) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) # receive result=conn.receive() exp = None if args.expectfail: exp = ['//bad-value'] rv, reply = send_rpc(conn, rpc_discard_changes) if rv > 0: return rv rv, reply = send_rpc(conn, rpc_create, exp) if rv > 0: return rv return 0 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-leaf-list-defaults.sh0000775000175000017500000000113214770023131026220 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then exit 0 else killall -KILL netconfd || true rm -f /tmp/ncxserver.sock /usr/sbin/netconfd --modpath=`pwd` --module=test-leaf-list-defaults --feature-disable=always-disabled-feature --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --modpath=`pwd` --module=test-leaf-list-defaults --feature-disable=always-disabled-feature --no-startup --superuser=$USER 1>tmp/server.log 2>&1 & SERVER_PID=$! fi sleep 1 python test-leaf-list-defaults.py --debug res=$? killall -KILL netconfd || true exit ${res} yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-identity-iffeature.yang0000664000175000017500000000147214770023131026664 0ustar vladimirvladimirmodule test-identity-iffeature { yang-version 1.1; namespace "urn:labn:params:xml:ns:yang:test-identity-iffeature"; prefix test; organization ""; contact ""; description "Create an identity-ref leaf to check if an if-feature statement within an identity can be used to disable that identity."; revision 2020-06-06 { reference ""; } feature extra-identity { description "The derived identity is available when th is feature is enabled."; } identity test-base-identity { description "Base identity"; } identity derived-identity { if-feature extra-identity; description "Derived identity. Conditionally enabled."; base test-base-identity; } container top { leaf the-test { type identityref { base test-base-identity; } description ""; } description ""; } } yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-action-in-grouping.sh0000775000175000017500000000071114770023131026246 0ustar vladimirvladimir#!/bin/sh # Check that the schema tree contains the expanded grouping and has # the action defined therein. yangdump is sufficient for this. set -e tmpfile=`mktemp` yangdump \ --format=tree \ --modpath=`pwd` \ --module=test-action-in-grouping > ${tmpfile} fgrep -q -- '---x make-something-happen' ${tmpfile} || { rm -f ${tmpfile} exit 1 } grep -q -- ' +--.. some-information' ${tmpfile} || { rm -f ${tmpfile} exit 1 } rm -f ${tmpfile} exit 0 yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-notification-in-grouping.sh0000775000175000017500000000072314770023131027462 0ustar vladimirvladimir#!/bin/sh # Check that the schema tree contains the expanded grouping and has # the notification defined therein. yangdump is sufficient for this. set -e tmpfile=`mktemp` yangdump \ --format=tree \ --modpath=`pwd` \ --module=test-notification-in-grouping > ${tmpfile} fgrep -q -- '---n something-happened' ${tmpfile} || { rm -f ${tmpfile} exit 1 } grep -q -- ' +--.. some-information' ${tmpfile} || { rm -f ${tmpfile} exit 1 } rm -f ${tmpfile} exit 0 yuma123_2.14/netconf/test/netconfd/yang-1dot1/test-action-in-grouping.yang0000664000175000017500000000113614770023131026571 0ustar vladimirvladimirmodule test-action-in-grouping { yang-version 1.1; namespace "urn:labn:params:xml:ns:yang:test-action-in-grouping"; prefix test; organization ""; contact ""; description "Expand a grouping that holds an action."; revision 2020-06-06 { reference ""; } grouping grouping-with-action { description "A group containing an action."; action make-something-happen { description "Take action."; input { leaf some-information { type int8; mandatory true; description "a number"; } } } } container top-actions { uses grouping-with-action; } } yuma123_2.14/netconf/test/netconfd/test-xpath-enum-value0000775000175000017500000000005414770023131023440 0ustar vladimirvladimir#!/bin/bash -e cd xpath-enum-value ./run.sh yuma123_2.14/netconf/test/netconfd/test-deviation-replace-type0000775000175000017500000000011314770023131024606 0ustar vladimirvladimir#!/bin/bash -e cd deviation-replace-type ./test-deviation-replace-type.sh yuma123_2.14/netconf/test/netconfd/xpath-ancestor/0000775000175000017500000000000014770023131022276 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/xpath-ancestor/mod1.yang0000664000175000017500000000103214770023131024012 0ustar vladimirvladimirmodule mod1 { yang-version 1.1; namespace "http://yuma123.org/ns/test/netconfd/xpath-ancestor/mod1"; prefix m1; organization "yuma123.org"; description "Part of the xpath-ancestor test."; revision 2022-01-01 { description "Initial version"; } container a { leaf value { type string; } container b { leaf value { type string; } container c { leaf value { type string; must '. = ancestor::a/value'; } } } } } yuma123_2.14/netconf/test/netconfd/xpath-ancestor/session.yangcli.py0000664000175000017500000000367614770023131025774 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli from lxml import etree import yangrpc def main(): print(""" #Description: Validate xpath ancestor axis #Procedure: #1 - Create /a/value = foo. #2 - Validate creating '/a/b/c/value = bar' fails. #3 - Validate creating '/a/b/c/value = foo' works. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) time.sleep(1) print("hello") ok = yangcli(conn, '''create /a/value value=foo''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''create /a/b/c/value value=bar''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==0) ok = yangcli(conn, '''discard-changes''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''create /a/b/c/value value=foo''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) print("done") return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/xpath-ancestor/run.sh0000775000175000017500000000222314770023131023440 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c ../test-xpath-current.yang --yangpath /usr/share/yuma/modules/ietf -o mod1.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & # confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=./mod1.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD #kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/yang-rfc6991-conformance/0000775000175000017500000000000014770023131023665 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-rfc6991-conformance/README0000777000175000017500000000000014770023131032007 2../yang-1dot1-conformance/READMEustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-rfc6991-conformance/mac-address/0000775000175000017500000000000014770023131026050 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-rfc6991-conformance/mac-address/mod1.yang0000664000175000017500000000042414770023131027570 0ustar vladimirvladimirmodule mod1 { prefix m1; namespace "http://yuma123.org/ns/mac-address/mod1"; import ietf-yang-types { revision-date "2013-07-15"; prefix yang; } container c { list l { key "mac"; leaf mac { type yang:mac-address; } } } } yuma123_2.14/netconf/test/netconfd/yang-rfc6991-conformance/mac-address/data1.xml0000664000175000017500000000022414770023131027562 0ustar vladimirvladimir AA:BB:CC:DD:EE:FF FF:EE:DD:CC:BB:AA yuma123_2.14/netconf/test/netconfd/yang-rfc6991-conformance/mac-address/data2.xml0000664000175000017500000000022414770023131027563 0ustar vladimirvladimir AA:BB:CC:DD:EE:FF AA:BB:CC:DD:EE:FF yuma123_2.14/netconf/test/netconfd/yang-rfc6991-conformance/mac-address/data3.xml0000664000175000017500000000022414770023131027564 0ustar vladimirvladimir AA:BB:CC:DD:EE:FF aa:bb:cc:dd:ee:ff yuma123_2.14/netconf/test/netconfd/yang-rfc6991-conformance/testspec.txt0000664000175000017500000000002514770023131026255 0ustar vladimirvladimirmac-address 0, 0,1,1 yuma123_2.14/netconf/test/netconfd/yang-rfc6991-conformance/run.sh0000777000175000017500000000000014770023131032547 2../yang-1dot1-conformance/run.shustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/test-xpath-re-match-concat0000775000175000017500000000006214770023131024326 0ustar vladimirvladimir#!/bin/bash -e cd xpath-re-match ./run.sh concat yuma123_2.14/netconf/test/netconfd/test-get0000775000175000017500000000003714770023131021020 0ustar vladimirvladimir#!/bin/bash -e cd get ./run.sh yuma123_2.14/netconf/test/netconfd/test-deviation-not-supported0000775000175000017500000000011714770023131025043 0ustar vladimirvladimir#!/bin/sh set -e cd deviation-not-supported ./test-deviation-not-supported.sh yuma123_2.14/netconf/test/netconfd/test-xpath-ancestor0000775000175000017500000000005214770023131023176 0ustar vladimirvladimir#!/bin/bash -e cd xpath-ancestor ./run.sh yuma123_2.14/netconf/test/netconfd/test-yang-validation0000775000175000017500000000005314770023131023325 0ustar vladimirvladimir#!/bin/bash -e cd yang-validation ./run.sh yuma123_2.14/netconf/test/netconfd/test-multiple-edit-callbacks0000775000175000017500000000006314770023131024733 0ustar vladimirvladimir#!/bin/bash -e cd multiple-edit-callbacks ./run.sh yuma123_2.14/netconf/test/netconfd/multi-instance/0000775000175000017500000000000014770023131022272 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/multi-instance/test-multi-instance@2017-12-01.yang0000664000175000017500000000063714770023131030141 0ustar vladimirvladimirmodule test-multi-instance { yang-version 1.1; namespace "http://yuma123.org/ns/test-multi-instance"; prefix tmi; import ietf-interfaces { prefix if; } organization "yuma123.org"; description "Part of the xpath-curent test."; revision 2017-12-01 { description "Initial version"; } augment "/if:interfaces/if:interface" { leaf foo { type string; } } } yuma123_2.14/netconf/test/netconfd/multi-instance/session.yangcli.py0000664000175000017500000001201014770023131025746 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli #from lxml import etree import lxml import yangrpc def yangcli_ok_script(conn, yangcli_script): for line in yangcli_script.splitlines(): line=line.strip() if not line: continue print(("Executing: "+line)) ok = yangcli(conn, line).xpath('./ok') assert(len(ok)==1) def connect(server, port, user, password): conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa", "--dump-session=tmp/nc-session- --keep-session-model-copies-after-compilation=true") if(conn==None): print("Error: yangrpc failed to connect!") return conn def commit(conn): result = yangcli(conn, "commit") print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==0 or len(ok)==1) if(len(ok)==1): return True else: return False def discard(conn): result = yangcli(conn, "discard") print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==0 or len(ok)==1) if(len(ok)==1): return True else: return False #Steps follow: def step_1(server, port0, port1, user, password): print("#1 - Create 2 sessions.") print("Connecting #1 ...") conn_1 = connect(server=server, port=port0, user=user, password=password) assert(conn_1!=None) print("Connecting #2 ...") conn_2 = connect(server=server, port=port1, user=user, password=password) assert(conn_2!=None) print("Connected ...") return (conn_1, conn_2) def step_2(conn_1,conn_2): print("#2 - Validate /interfaces/interface/foo can be created on session #1.") result = yangcli(conn_1, "create /interfaces/interface[name='foo'] -- type=ethernetCsmacd foo='hello'") print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==1) commit(conn_1) result = yangcli(conn_1, "delete /interfaces/interface[name='foo']") ok = result.xpath('./ok') assert(len(ok)==1) commit(conn_1) def step_3(conn_1,conn_2): print("#3 - Validate /interfaces/interface/bar can be created on session #2.") result = yangcli(conn_2, "create /interfaces/interface[name='bar'] -- type=ethernetCsmacd bar='hello'") print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==1) commit(conn_2) result = yangcli(conn_2, "delete /interfaces/interface[name='bar']") ok = result.xpath('./ok') assert(len(ok)==1) commit(conn_2) def step_4(conn_1,conn_2): print("#4 - Validate /interface/interface/bar can NOT be created on session #1.") (res, rpc_val) = yangrpc.parse_cli(conn_1, "create /interfaces/interface[name='bar'] -- type=ethernetCsmacd bar='hello'") assert(res!=0) def step_5(conn_1,conn_2): print("#5 - Validate /interface/interface/foo can NOT be created on session #2.") (res, rpc_val) = yangrpc.parse_cli(conn_2, "create /interfaces/interface[name='foo'] -- type=ethernetCsmacd foo='hello'") assert(res!=0) def step_6(conn_1,conn_2): print("#6 - Close session #1 and confirm 'bar' can still be created on session #2.") yangrpc.close(conn_1) result = yangcli(conn_2, "create /interfaces/interface[name='bar'] -- type=ethernetCsmacd bar='hello'") print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==1) commit(conn_2) result = yangcli(conn_2, "delete /interfaces/interface[name='bar']") ok = result.xpath('./ok') assert(len(ok)==1) commit(conn_2) def main(): print(""" #Description: Test multi-instance server and client implementation #Procedure: #1 - Create 2 sessions - one to each server instance. #2 - Validate /interface/interface/foo can be created on session #1. #3 - Validate /interface/interface/bar can be created on session #2. #4 - Validate /interface/interface/bar can NOT be created on session #1. #5 - Validate /interface/interface/foo can NOT be created on session #2. #6 - Close session #1 and confirm 'bar' can still be created on session #2. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port0", help="port0 e.g. 830 (830 if not specified)") parser.add_argument("--port1", help="port1 e.g. 1830 (1830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port0==None or args.port0==""): port0=830 else: port0=int(args.port0) if(args.port1==None or args.port1==""): port1=1830 else: port1=int(args.port1) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password (conn_1, conn_2)=step_1(server=server, port0=port0, port1=port1, user=user, password=password) step_2(conn_1,conn_2) step_3(conn_1,conn_2) step_4(conn_1,conn_2) step_5(conn_1,conn_2) step_6(conn_1,conn_2) return 0 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/multi-instance/test-multi-instance@2017-12-02.yang0000664000175000017500000000105414770023131030134 0ustar vladimirvladimirmodule test-multi-instance { yang-version 1.1; namespace "http://yuma123.org/ns/test-multi-instance"; prefix tmi; import ietf-interfaces { prefix if; } organization "yuma123.org"; description "Part of the xpath-curent test."; revision 2017-12-02 { description "Renamed foo to bar for the purpose of creating a backward incompatible revision."; } revision 2017-12-01 { description "Initial version"; } augment "/if:interfaces/if:interface" { leaf bar { type string; } } } yuma123_2.14/netconf/test/netconfd/multi-instance/run.sh0000775000175000017500000000262714770023131023444 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then #not implemented for confd - SKIP exit 77 killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc cd tmp for module in ietf-interfaces iana-if-type ; do cp ../../../../modules/ietf/${module}.yang . confdc -c ${module}.yang --yangpath ../../../../ done NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath ${RUN_WITH_CONFD}/etc/confd --addloadpath . & SERVER_PID=$! cd .. else NCPORT0=830 NCPORT1=1830 killall -KILL netconfd || true rm /tmp/ncxserver*.sock || true /usr/sbin/netconfd --modpath=.:/usr/share/yuma/modules/ --module=iana-if-type --module=test-multi-instance@2017-12-01 --no-startup --superuser=$USER --ncxserver-sockname=/tmp/ncxserver.sock --port=830 & SERVER0_PID=$! /usr/sbin/netconfd --modpath=.:/usr/share/yuma/modules/ --module=iana-if-type --module=test-multi-instance@2017-12-02 --no-startup --superuser=$USER --ncxserver-sockname=/tmp/ncxserver.${NCPORT1}.sock --port=1830 & SERVER1_PID=$! fi sleep 4 python session.yangcli.py --server=$NCSERVER --port0=$NCPORT0 --port1=$NCPORT1 --user=$NCUSER --password=$NCPASSWORD #kill -KILL $SERVER0_PID $SERVER1_PID sleep 1 yuma123_2.14/netconf/test/netconfd/test-ietf-routing0000775000175000017500000000005014770023131022650 0ustar vladimirvladimir#!/bin/bash -e cd ietf-routing ./run.sh yuma123_2.14/netconf/test/netconfd/test-xpath-bit-is-set0000775000175000017500000000005414770023131023342 0ustar vladimirvladimir#!/bin/bash -e cd xpath-bit-is-set ./run.sh yuma123_2.14/netconf/test/netconfd/get-schema/0000775000175000017500000000000014770023131021353 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/get-schema/sub-module.yang0000664000175000017500000000120314770023131024303 0ustar vladimirvladimirsubmodule sub-module { belongs-to main-module { prefix mm; } revision 2017-12-07 { description "Initial revision."; } typedef X { type int32 { range 1..10; } } typedef XX { type int32 { range min..10; } } list a.sub { key "a b c"; leaf a { type int32; } leaf b { type int16; } leaf c { type int64; } choice d { case e { leaf f { type empty; } leaf g { type int32; mandatory true; } leaf h { type int32; config false; } } leaf i { type leafref { path ../a; } } leaf j { type mm:X; } } } } yuma123_2.14/netconf/test/netconfd/get-schema/README0000664000175000017500000000030614770023131022232 0ustar vladimirvladimirPURPOSE: Verify get-schema works for a submodule. OPERATION: loaded modules and submodules should be represented to netconf-state/schema list and they should be also retrieved by get-schema rpc. yuma123_2.14/netconf/test/netconfd/get-schema/session.litenc.py0000664000175000017500000000547414770023131024677 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: ## Verify get-schema works for a submodule. #Procedure: ## loaded modules and submodules should be represented to netconf-state/schema list and they should be also retrieved by get-schema rpc. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) # receive result=conn.receive() get_rpc = """ %(identifier)s """ for identifier in {"main-module", "sub-module"}: print(("get %s."%(identifier))) result = conn.rpc(get_rpc%{'identifier':identifier}) ok = result.xpath('data/netconf-state/schemas/schema/identifier') print(result) # print ok # print lxml.etree.tostring(result) #assert(len(ok)==1) print("[OK] Retrieving a (sub)module") get_schema = """ %(identifier)s """ print("") print("get-schema") print((get_schema%{'identifier':identifier})) result = conn.rpc(get_schema%{'identifier':identifier}) print(lxml.etree.tostring(result)) rpc_error = result.xpath('rpc-error') assert(len(rpc_error)==0) print("[OK] Retrieving a (sub)module") sys.exit(main()) yuma123_2.14/netconf/test/netconfd/get-schema/run.sh0000775000175000017500000000173614770023131022525 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cp *.yang tmp cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c main-module.yang --yangpath /usr/share/yuma/modules/ietf:. -o main-module.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 1>server.stdout 2>server.stderr & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm -f /tmp/ncxserver.sock /usr/sbin/netconfd --module=main-module.yang --no-startup --superuser=$USER 1>tmp/server.stdout 2>tmp/server.stderr & SERVER_PID=$! fi sleep 10 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill $SERVER_PID cat tmp/server.stdout sleep 1 yuma123_2.14/netconf/test/netconfd/get-schema/main-module.yang0000664000175000017500000000052014770023131024437 0ustar vladimirvladimirmodule main-module { namespace "get-schema-test:test-module"; prefix "mm"; include sub-module; revision 2017-12-07 { description "Initial revision."; } typedef AA { type string { pattern ".*"; } } leaf t22 { type boolean; } leaf t23 { type string; config false; } } yuma123_2.14/netconf/test/netconfd/test-deviation-delete-ext0000775000175000017500000000011114770023131024252 0ustar vladimirvladimir#!/bin/sh set -e cd deviation-delete-ext ./test-deviation-delete-ext.sh yuma123_2.14/netconf/test/netconfd/test-netconf-notifications0000775000175000017500000000006114770023131024541 0ustar vladimirvladimir#!/bin/bash -e cd netconf-notifications ./run.sh yuma123_2.14/netconf/test/netconfd/test-when0000775000175000017500000000004114770023131021175 0ustar vladimirvladimir#!/bin/bash -e cd when ./run.sh yuma123_2.14/netconf/test/netconfd/test-yang-library-submodules0000775000175000017500000000006314770023131025020 0ustar vladimirvladimir#!/bin/bash -e cd yang-library-submodules ./run.sh yuma123_2.14/netconf/test/netconfd/test-ietf-routing-bis0000775000175000017500000000005414770023131023427 0ustar vladimirvladimir#!/bin/bash -e cd ietf-routing-bis ./run.sh yuma123_2.14/netconf/test/netconfd/openconfig/0000775000175000017500000000000014770023131021465 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/openconfig/run.sh0000775000175000017500000000036114770023131022630 0ustar vladimirvladimir#!/bin/sh if [ ! -d public ] ; then git clone https://github.com/openconfig/public.git fi /usr/sbin/netconfd --validate-config-only --startup-error=stop --no-startup --modpath=public/release/models --module=openconfig-network-instance yuma123_2.14/netconf/test/netconfd/test-enum-iffeature0000775000175000017500000000013514770023131023154 0ustar vladimirvladimir#!/bin/sh set -e cd yang-1dot1 ./test-enum-iffeature-pass.sh ./test-enum-iffeature-fail.sh yuma123_2.14/netconf/test/netconfd/memory-leak/0000775000175000017500000000000014770023131021560 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/memory-leak/session.yangcli.py0000664000175000017500000000532414770023131025246 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli from lxml import etree import yangrpc def main(): print("""Edit config, get and shutdown to test there are no memory leaks #Description: . #Procedure: #1 - Create interface "foo" of type=ethernetCsmacd. Verify commit succeeds. #2 - Replace type for interface "foo" with type=other. Read back the type after commit. #3 - the entire root. #4 - the netconf server. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) time.sleep(1) ok = yangcli(conn, '''delete /interfaces''').xpath('./ok') ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''create /interfaces/interface -- name='foo' type='ethernetCsmacd' ''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) result = yangcli(conn, "xget /interfaces") names = result.xpath('./data/interfaces/interface/name') for name in names: print(name.text) types = result.xpath('./data/interfaces/interface/type') for type in types: print(type.text) assert(len(types)==1) assert(types[0].text=='ianaift:ethernetCsmacd') ok = yangcli(conn, '''replace /interfaces/interface[name='foo']/type value='other' ''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) result = yangcli(conn, "xget /interfaces") names = result.xpath('./data/interfaces/interface/name') for name in names: print(name.text) types = result.xpath('./data/interfaces/interface/type') for type in types: print(type.text) assert(len(types)==1) assert(types[0].text=='ianaift:other') result = yangcli(conn, "xget /") ok = yangcli(conn, '''shutdown''').xpath('./ok') assert(len(ok)==1) return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/memory-leak/run.sh0000775000175000017500000000217014770023131022723 0ustar vladimirvladimir#!/bin/bash -e function expected_fail { export DID_IT_FAIL=0 ; sh -c "$1" || export DID_IT_FAIL=1 true if [ "$DID_IT_FAIL" = "1" ] ; then #echo "GOOD ERROR" return 0 else echo "ERROR: Expected FAIL returned OK." return -1 fi } rm -rf tmp || true mkdir tmp killall -KILL netconfd || true rm /tmp/ncxserver.sock || true #/usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/ietf-interfaces.yang --module=/usr/share/yuma/modules/ietf/iana-if-type.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & valgrind --log-fd=1 --num-callers=100 --leak-check=full -- /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & #--log-file=tmp/valgrind.log SERVER_PID=$! sleep 20 python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD sleep 2 expected_fail "kill -KILL $SERVER_PID" cat tmp/server.log #expected_fail "cat tmp/server.log | grep 'memory corruption'" cat tmp/server.log | grep "ERROR SUMMARY: 0 errors" sleep 1 yuma123_2.14/netconf/test/netconfd/test-deviation-in-modules-state0000775000175000017500000000012514770023131025411 0ustar vladimirvladimir#!/bin/sh set -e cd deviation-in-modules-state ./test-deviation-in-modules-state.sh yuma123_2.14/netconf/test/netconfd/test-anyxml0000775000175000017500000000004214770023131021545 0ustar vladimirvladimir#!/bin/bash -e cd anyxml ./run.sh yuma123_2.14/netconf/test/netconfd/draft-ietf-bmwg-network-tester-cfg/0000775000175000017500000000000014770023131026045 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/draft-ietf-bmwg-network-tester-cfg/session.yangcli.py0000664000175000017500000000603314770023131031531 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli import lxml from lxml import etree import yangrpc def yangcli_ok_script(conn, yangcli_script): for line in yangcli_script.splitlines(): line=line.strip() if not line: continue print("Executing: "+line) ok = yangcli(conn, line).xpath('./ok') assert(len(ok)==1) def main(): print(""" #Description: Usecase for ietf-traffic-generator and ietf-traffic-analyzer modules. #Procedure: #1 - Start traffic-analyzer on "veth-a1" #2 - Start traffic-generator on "veth-b1" 1000 64 octet frames with minimal interframe gap #3 - After 1 second validate 1000 test frames are registered by the traffic-analyzer """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa", "--dump-session=tmp/nc-session-") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) time.sleep(1) result = yangcli(conn, "delete /interfaces") result = yangcli(conn, "commit") ok = result.xpath('./ok') assert(len(ok)==1) yangcli_script=''' create /interfaces/interface -- name=veth2 type=ethernetCsmacd description="Analyzer interface in a link." create /interfaces/interface[name='veth2']/traffic-analyzer commit ''' yangcli_ok_script(conn, yangcli_script) yangcli_script=''' create /interfaces/interface -- name=veth1 type=ethernetCsmacd description="Generator interface in a link." create /interfaces/interface[name='veth1']/traffic-generator -- frame-size=64 interframe-gap=20 total-frames=10000 frame-data="6CA96F0000026CA96F00000108004500002ED4A500000A115816C0000201C0000202C0200007001A00000102030405060708090A0B0C0D0E0F101112" commit ''' yangcli_ok_script(conn, yangcli_script) time.sleep(3) result_state = yangcli(conn, "xget /interfaces-state/interface[name='veth2']/statistics" ) print(lxml.etree.tostring(result_state)) result = yangcli(conn, "xget /interfaces/interface[name='veth2']/traffic-analyzer" ) print(lxml.etree.tostring(result)) testframe_pkts = result.xpath("//rpc-reply/data/interfaces/interface[name='veth2']/traffic-analyzer/state/testframe-stats/pkts") print(testframe_pkts) assert((len(testframe_pkts)==1)) assert(testframe_pkts[0].text == "10000") return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/draft-ietf-bmwg-network-tester-cfg/run.sh0000775000175000017500000000344414770023131027215 0ustar vladimirvladimir#!/bin/bash -e ip link delete veth1 || true ip link add veth1 type veth peer name veth2 ifconfig veth1 hw ether 6C:A9:6F:00:00:01 ifconfig veth2 hw ether 6C:A9:6F:00:00:02 ifconfig veth1 192.0.2.1 up ifconfig veth2 192.0.2.1 up rm -rf tmp || true mkdir tmp cd tmp #wget https://www.ietf.org/archive/id/draft-ietf-bmwg-network-tester-cfg-05.txt #xml2rfc draft-ietf-bmwg-network-tester-cfg-05.xml #rfcstrip draft-ietf-bmwg-network-tester-cfg-05.txt #pyang -f tree --path . ietf-traffic-generator*.yang #pyang -f tree --path . ietf-traffic-analyzer*.yang if [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc for module in `find ./ -name '*.yang' -exec basename {} .yang \;` ; do echo confdc -c ${module}.yang --yangpath . confdc -c ${module}.yang --yangpath . done NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath ${RUN_WITH_CONFD}/etc/confd --addloadpath . #2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --modpath=.:/usr/share/yuma/modules --module=ietf-traffic-generator --module=ietf-traffic-analyzer --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --modpath=.:/usr/share/yuma/modules --module=ietf-interfaces --module=ietf-traffic-generator --module=ietf-traffic-analyzer --no-startup --superuser=$USER & SERVER_PID=$! cd .. fi sleep 3 python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 ip link delete veth1 || true yuma123_2.14/netconf/test/netconfd/test-copy-config0000775000175000017500000000004714770023131022457 0ustar vladimirvladimir#!/bin/bash -e cd copy-config ./run.sh yuma123_2.14/netconf/test/netconfd/cesnet-libyang-conformance-suite/0000775000175000017500000000000014770023131025661 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/cesnet-libyang-conformance-suite/README0000664000175000017500000000031014770023131026533 0ustar vladimirvladimir=Conformance test suite for RFC6020 "YANG - A Data Modeling Language for the Network Configuration Protocol (NETCONF)"= ==FILES== testspec.txt - specification of the expected results 1-FAIL, 0-PASS yuma123_2.14/netconf/test/netconfd/cesnet-libyang-conformance-suite/check-yang-conformance.sh0000777000175000017500000000000014770023131043007 2../yang-conformance/check-yang-conformance.shustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/cesnet-libyang-conformance-suite/genspec.sh0000775000175000017500000000070014770023131027641 0ustar vladimirvladimir#!/bin/bash if [ $# -eq 1 ] ; then cd $1 fi for file in `find -name '*.c'` ; do echo -n `basename $file .c` | sed -e "s/^test_//" echo -n " " echo -n `grep TEST_SCHEMA_LOAD_FAIL $file | cut -d ' ' -f 3` data_file_count="`grep TEST_DATA_FILE_COUNT $file | cut -d ' ' -f 3`" if [ "$data_file_count" != "0" ] ; then echo -n " " echo -n `grep TEST_DATA_FILE_LOAD_FAIL $file | cut -d ' ' -f 3` fi echo done yuma123_2.14/netconf/test/netconfd/cesnet-libyang-conformance-suite/testspec.txt0000664000175000017500000000341314770023131030255 0ustar vladimirvladimirsec5_1 1,1,1,1,1,0 sec5_5 1,1,1,1,1,1,0 sec6_1_1 0 sec6_1_3 sec6_2 0,1,1,1 sec6_2_1 1,1,1,1,1,1,1,1,1,1,1,0 sec7_10 1,1,1,1,1,1,1,0 1,1,0 sec7_1 1,1,1,1,1,0 sec7_11 1,1,1,1,1,1,0 1,0,0,0 sec7_12_1 1,1,1,1,0,0 1,0,1,0,0 sec7_12_2 1,1,1,1,1,1,1,1,1,1,1,0 1,1,1,0,1,0 sec7_13_1 1,1,1,1,1,0 sec7_13_2 0 0,0,0,0,1,0,0,0,1,1,1,1,1,1 sec7_13_3 0 0,0,0,0,1,0,0,0,1,1,1,1,1,1 sec7_14 1,1,1,0 1,1,0,1,0,1,0,0,0,0 sec7_15 1,1,1,1,1,1,1,0,0,0,0 0,0,0,0,1,0,1 sec7_16_1 1,1,1,1,0 sec7_16_2 0,1,1,1,0 0,1,0,1,0,1 sec7_18_1 1,1,1,1,1,0 sec7_18_2 0,1,0 1,1,0,1,0 sec7_18_3_1 1,1,1,0,0 1,0,1 sec7_18_3_2 1,1,1,1,1,1,1,1,1,1,1,1,0 1,1,1,0,0,1,1 sec7_19_1 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0 sec7_19_2 1,1,1,1,1,1,1,1,0 1,1,1,0 sec7_19_5 0,0,0 0,1,0,0,0,0,0,0,0,0,0 sec7_2 1,1,1,1,1,0 sec7_3_1 1,1,1,1,1,1,1,0 sec7_3 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0 sec7_3_4 1,1,1,1,0 1,1,1,1,0 sec7_5_2 1,1,1,1,1,1,0 sec7_5_4 1,1,1,1,0 1,1,0,1,1,1,1,0,0,1,0,0,1,0 sec7_5_5 0 0,0,0 sec7_6_2 1,1,1,1,1,1,1,1,1,1,0 sec7_6_3 0 1,1,1,1,0,1 sec7_6_4 1,1,0,0 0,0,0,0,1,0,0,0,0,0,0 sec7_6_5 1,0 1,1,0,1,0,1,1,0 sec7_7_2 1,1,1,1,1,1,1,1,1,1,1,0 sec7_7_3 1,1,0 1,1,0,1,0,1,0 sec7_7_4 1,1,1,1,1,0 1,1,1,1,1,1,0 sec7_7_5 1,0 1,0,0 sec7_8_1 1,1,1,1,1,1,1,1,1,0 sec7_8_2 1,1,1,1,0,1 1,1,0,0,1,1,0,0,1,0 sec7_8_3 1,1,1,0,0 1,1,0,1,1,0,0 sec7_9_1 1,1,1,1,1,1,1,0 sec7_9_2 1,1,1,1,1,0,1 1,1,0 sec7_9_3 1,1,0,1,1,0,1 0,0,0,0 sec7_9_4 0 1,0,1,1,1,0 sec9_10 1,1,0 1,1,0,1,0,1 sec9_11 1,1,1,0 1,0 sec9_12 1,1,1,1,0 1,0,0,1 sec9_13 1,1,0 1,0,1,0,1,0,1,0,0,0 sec9_2 1,1,1,1,1,1,1,1,1,1,1,1,0 1,1,1,1,0 sec9_3 1,1,1,1,1,1,1,1,1,1,0 1,1,1,0 sec9_4_4 1,1,1,1,1,1,1,1,1,0 0,1,1,1,0 sec9_4_6 1,1,1,1,0 1,1,1,1,1,1,0 sec9_5 1,0 1,1,0 sec9_6 1,1,1,1,1,1,1,1,1,1,1,0 1,0 sec9_7 1,1,1,1,1,1,1,1,1,1,1,0 1,0 sec9_8 1,1,0 0 sec9_9 1,1,1,1,1,1,1,0,0,1 0,0,0,1,0,0,1 yuma123_2.14/netconf/test/netconfd/cesnet-libyang-conformance-suite/run.sh0000775000175000017500000000035614770023131027030 0ustar vladimirvladimir#!/bin/bash LIB_YANG_PATH=../../../../../libyang rm -rf tmp mkdir tmp cd tmp ../genspec.sh ../${LIB_YANG_PATH}/tests/conformance | sort > ../testspec.txt ../check-yang-conformance.sh ../${LIB_YANG_PATH}/tests/conformance ../testspec.txt yuma123_2.14/netconf/test/netconfd/deviation-not-supported/0000775000175000017500000000000014770023131024141 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/deviation-not-supported/test-deviation-not-supported-2.yang0000664000175000017500000000066614770023131032750 0ustar vladimirvladimirmodule test-deviation-not-supported-2 { yang-version 1.1; namespace "urn:labn:params:xml:ns:yang:test-deviation-not-supported-2"; prefix dev2; import ietf-interfaces { prefix if; } organization ""; contact ""; description "Add an external deviation to ensure that it is applied."; revision 2020-07-28 { reference ""; } deviation /if:interfaces/if:interface/if:link-up-down-trap-enable { deviate not-supported; } } yuma123_2.14/netconf/test/netconfd/deviation-not-supported/test-deviation-not-supported-1.yang0000664000175000017500000000113114770023131032733 0ustar vladimirvladimirmodule test-deviation-not-supported-1 { yang-version 1.1; namespace "urn:labn:params:xml:ns:yang:test-deviation-not-supported-1"; prefix dev1; organization ""; contact ""; description "Add an internal deviation to ensure that it is applied."; revision 2020-07-28 { reference ""; } container top { leaf supported-value { type int8; description "An integer value"; } leaf unsupported-value { type int8; description "Another integer value"; } description "Two integer values"; } deviation /dev1:top/dev1:unsupported-value { deviate not-supported; } } yuma123_2.14/netconf/test/netconfd/deviation-not-supported/test-deviation-not-supported-3.yang0000664000175000017500000000106314770023131032741 0ustar vladimirvladimirmodule test-deviation-not-supported-3 { yang-version 1.1; namespace "urn:labn:params:xml:ns:yang:test-deviation-not-supported-3"; prefix dev3; import ietf-interfaces { prefix if; } import ietf-ip { prefix ip; } organization ""; contact ""; description "Add an external deviation to a path, with multiple module prefixes, created by an 'augment' to ensure that it is applied without crashing netconfd."; revision 2020-07-28 { reference ""; } deviation /if:interfaces/if:interface/ip:ipv4/ip:forwarding { deviate not-supported; } } yuma123_2.14/netconf/test/netconfd/deviation-not-supported/test-deviation-not-supported-4.yang0000664000175000017500000000065114770023131032744 0ustar vladimirvladimirmodule test-deviation-not-supported-4 { yang-version 1.1; namespace "urn:labn:params:xml:ns:yang:test-deviation-not-supported-4"; prefix dev4; import ietf-system { prefix sys; } organization ""; contact ""; description "Add an external deviation to a 'base' module to ensure that it is applied."; revision 2020-07-28 { reference ""; } deviation /sys:system/sys:location { deviate not-supported; } } yuma123_2.14/netconf/test/netconfd/deviation-not-supported/test-deviation-not-supported.py0000664000175000017500000002150014770023131032271 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse DEBUG = False rpc_discard_changes = """ """ rpc_get_config_with_defaults = """ report-all """ rpc_test1a = """ merge set 1 """ rpc_test1b = """ merge set 1 """ rpc_test2a = """ merge set 1 """ rpc_test2b = """ merge set 1 """ rpc_test3 = """ merge set eth0 ianaift:ethernetCsmacd true """ rpc_test4a = """ merge set me """ rpc_test4b = """ merge set here """ # TESTS = list-of-test-details = [ , ... ] # test-details = { 'RPC': , # 'edit-config-results': , # 'expected-results': , # 'unexpected-results': , # 'name': } tests = [ { 'RPC': rpc_test1a, 'expected-results': [ ], 'unexpected-results': [], 'name': 'Test 1a - internal deviation: set leaf', 'edit-config-results': None }, { 'RPC': rpc_test1b, 'expected-results': [], 'unexpected-results': [], 'name': 'Test 1b - internal deviation: set unsupported leaf', 'edit-config-results': [ '//bad-element' ] }, { 'RPC': rpc_test2a, 'expected-results': [ ], 'unexpected-results': [], 'name': 'Test 2a - external deviation: set leaf', 'edit-config-results': None }, { 'RPC': rpc_test2b, 'expected-results': [], 'unexpected-results': [], 'name': 'Test 2b - external deviation: set unsupported leaf', 'edit-config-results': [ '//bad-element' ] }, { 'RPC': rpc_test3, 'expected-results': [], 'unexpected-results': [], 'name': 'Test 3 - node path with multiple module prefixes', 'edit-config-results': [ '//bad-element' ] }, { 'RPC': rpc_test4a, 'expected-results': [ ], 'unexpected-results': [], 'name': 'Test 4a - add deviation to "base" module: set leaf', 'edit-config-results': None }, { 'RPC': rpc_test4b, 'expected-results': [], 'unexpected-results': [], 'name': 'Test 4b - add deviation to "base" module: set unsupported leaf', 'edit-config-results': [ '//bad-element' ] } ] # Examine the return value of _XSLTResultTree.xpath() and decide if # evaluating the xpath expression reflects a successful test run. def eval_xpath_results(reply, hdr, exp): rv = 0 errmsg = '%s: failed to evaluate XPATH expression "%s"' res = reply.xpath(exp) if type(res) == type([]) and len(res) == 0: # res[*] --> if DEBUG: print((errmsg % (hdr, exp))) rv = 2 elif type(res) == type(True) and res == False: if DEBUG: print((errmsg % (hdr, exp))) rv = 2 #elif type(res) == type(1.0): #elif type(res) == type(""): return rv # returns (status, reply-xml) def send_rpc(conn, rpcxml, expected=None): rv = 0 reply = conn.rpc(rpcxml) if not reply: return (1, None) if expected is not None: for exp in expected: rv = eval_xpath_results(reply, 'send_rpc', exp) if rv > 0: break else: rv = eval_xpath_results(reply, 'send_rpc', '//ok') return (rv, reply) def run_one_test(conn, desc): (rv, reply) = send_rpc(conn, rpc_discard_changes) if rv > 0: print(('%s: sending discard-changes RPC failed.' % desc['name'])) return (desc['name'], rv) (rv, reply) = send_rpc(conn, desc['RPC'], desc['edit-config-results']) if rv > 0: print(('%s: sending test RPC failed.' % desc['name'])) return (desc['name'], rv) return (desc['name'], rv) def run_tests(conn, alltests): results = [] for t in alltests: results.append(run_one_test(conn, t)) return results def main(): print("#Description: Attempt to create a leaf with a conditional identity.") parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print(("[FAILED] Connecting to server=%s:" % {'server':server})) return(-1) print(("[OK] Connecting to server=%(server)s:" % {'server':server})) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) # receive result=conn.receive() results = run_tests(conn, tests) ok = True for x in results: print(("%-70.70s : %s" % (x[0], 'PASS' if x[1] == 0 else 'FAIL'))) if x[1] > 0: ok = False send_rpc(conn, rpc_discard_changes) return 0 if ok else 1 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/deviation-not-supported/test-deviation-not-supported.sh0000775000175000017500000000125514770023131032263 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then exit 0 else modules="--module=test-deviation-not-supported-1" modules+=" --module=test-deviation-not-supported-2" modules+=" --module=test-deviation-not-supported-3" modules+=" --module=test-deviation-not-supported-4" killall -KILL netconfd || true rm -f /tmp/ncxserver.sock /usr/sbin/netconfd --modpath=`pwd` ${modules} --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --modpath=`pwd` ${modules} --no-startup --superuser=$USER 1>tmp/server.log 2>&1 & SERVER_PID=$! fi sleep 1 python test-deviation-not-supported.py res=$? killall -KILL netconfd || true exit ${res} yuma123_2.14/netconf/test/netconfd/test-cesnet-libyang-conformance-suite0000775000175000017500000000007414770023131026565 0ustar vladimirvladimir#!/bin/bash -e cd cesnet-libyang-conformance-suite ./run.sh yuma123_2.14/netconf/test/netconfd/test-memory-leak0000775000175000017500000000004714770023131022464 0ustar vladimirvladimir#!/bin/bash -e cd memory-leak ./run.sh yuma123_2.14/netconf/test/netconfd/perf/0000775000175000017500000000000014770023131020272 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/perf/session.flows.litenc.py0000664000175000017500000001602114770023131024735 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Create a number of list entries in edit-config and delete them. #Procedure: #1 - Create specified interface count (or none if --if-count=0) and commit. Verify commit succeeds. #2 - Delete the interfaces and commit. Verify commit succeeds. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") parser.add_argument("--connections-count", help="count of connections to be opened and closed in sequence for each completing negotiation and creating --interfaces-count interfaces in configuration before closing the session") parser.add_argument("--interfaces-count", help="interface count to create, commit and delete (0= just connect and terminate connection) starting from if0 e.g. if0, if1 ... if1000 ...") parser.add_argument("--skip-hello", help="true - only ssh session is established but no messages are sent or received") parser.add_argument("--bridge-flows-enable", help="true - in addition to interfaces create flows for simple mac learning bridge unicast forwarding table interfaces_count*(interfaces_count-1) flows") args = parser.parse_args() for x in range(int(args.connections_count)): ret=session(args) assert(ret==0) return(0) def session(args): if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) if(args.skip_hello=="true"): conn_raw.close() return(0) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) #print "[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml} print("Connected ...") if(int(args.interfaces_count)>0): edit_config(conn,args) conn_raw.close() return(0) def edit_config(conn,args): edit_config_rpc = """ merge test-then-set %s """%(edit_config_create(conn,args)) print("edit-config ...") print(edit_config_rpc) result = conn.rpc(edit_config_rpc) #print lxml.etree.tostring(result) ok = result.xpath('ok') #print result #print ok print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) ok = result.xpath('ok') assert(len(ok)==1) edit_config_rpc = """ merge set %s """%(edit_config_delete(conn,args)) print("edit-config - delete /interfaces ...") result = conn.rpc(edit_config_rpc) #print lxml.etree.tostring(result) ok = result.xpath('ok') assert(len(ok)==1) print("commit ...") result = conn.rpc(commit_rpc) #print lxml.etree.tostring(result) ok = result.xpath('//ok') assert(len(ok)==1) def edit_config_create(conn,args): config=""" """ if(args.bridge_flows_enable!="true"): for index in range(0,int(args.interfaces_count)): config=config+""" %s ianaift:ethernetCsmacd """%("if"+str(index)) config=config+""" """ return(config) for index in range(0,int(args.interfaces_count)): config=config+""" %s ianaift:ethernetCsmacd %s """%("if"+str(index), "if"+str(index)) config=config+""" """ config=config+""" """ for index in range(0,int(args.interfaces_count)): config=config+""" %s """%("if"+str(index)) config=config+""" """ config=config+""" """ for index_src in range(0,int(args.interfaces_count)): for index_dst in range(0,int(args.interfaces_count)): if(index_src==index_dst): continue config=config+""" %s %s
%s
%s
0 %s
"""%("flow-"+str(index_src)+"-to-"+str(index_dst),"if"+str(index_src),index2mac(index_src),index2mac(index_dst),"if"+str(index_dst)) config=config+"""
""" return(config) def edit_config_delete(conn,args): config=""" """ if(args.bridge_flows_enable=="true"): config=config+""" """ return config def index2mac(index): #dummy for now return("00:00:00:00:00:00") sys.exit(main()) yuma123_2.14/netconf/test/netconfd/perf/run.sh0000775000175000017500000000435514770023131021444 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana_if_type.fxs confdc -c /usr/share/yuma/modules/ietf/ietf-system@2014-08-06.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-system.fxs confdc -c /usr/share/yuma/modules/ietf-draft/ietf-network-bridge.yang --yangpath /usr/share/yuma/modules/ietf-draft:/usr/share/yuma/modules/ietf -o ietf-network-bridge.fxs confdc -c /usr/share/yuma/modules/ietf-draft/ietf-network-bridge-flows.yang --yangpath /usr/share/yuma/modules/ietf-draft:/usr/share/yuma/modules/ietf -o ietf-network-bridge-flows.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd \ --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang \ --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang \ --module=/usr/share/yuma/modules/ietf/ietf-system@2014-08-06.yang \ --module=/usr/share/yuma/modules/ietf-draft/ietf-network-bridge.yang \ --module=/usr/share/yuma/modules/ietf-draft/ietf-network-bridge-flows.yang \ --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 5 STARTTIME=$(date +%s) time python session.flows.litenc.py --skip-hello=false --connections-count=1 --interfaces-count=64 --bridge-flows-enable=true --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD ENDTIME=$(date +%s) kill -KILL $SERVER_PID wc tmp/server.log sleep 1 echo "It took $(($ENDTIME-$STARTTIME)) seconds to commit the configuration" if [ 20 -lt $(($ENDTIME-$STARTTIME)) ] ; then echo "Test failed since the PASS threshold is 20 seconds" return 1 fi yuma123_2.14/netconf/test/netconfd/test-yang-1dot1-conformance0000775000175000017500000000006314770023131024414 0ustar vladimirvladimir#!/bin/bash -e cd yang-1dot1-conformance ./run.sh yuma123_2.14/netconf/test/netconfd/deviation-add-must/0000775000175000017500000000000014770023131023034 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/deviation-add-must/test-deviation-add-must.yang0000664000175000017500000000071014770023131030365 0ustar vladimirvladimirmodule test-deviation-add-must { namespace "http://yuma123.org/ns/test-deviation-add-must"; prefix test-dam; import ietf-interfaces { prefix if; } organization "yuma123"; description "Test module for deviation-add-must"; revision 2017-03-19 { description "1.st version"; } deviation /if:interfaces/if:interface { deviate add { must "name='foo'" { error-message "Only foo."; } } } } yuma123_2.14/netconf/test/netconfd/deviation-add-must/README0000664000175000017500000000036414770023131023717 0ustar vladimirvladimirPURPOSE: Verify deviation adding must statement validation works. OPERATION: Uses deviation to add must statement to /interfaces/interface allowing only interface named 'foo' to be configured. Validates 'foo' is accepted and 'bar' rejected. yuma123_2.14/netconf/test/netconfd/deviation-add-must/session.litenc.py0000664000175000017500000000415614770023131026354 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml def main(): print(""" #Description: Demonstrate that edit-config works. #Procedure: #1 - Create interface "foo" and commit. Verify commit succeeds. #2 - Create interface "bar" and commit. Verify commit fails. """) port=830 server="127.0.0.1" conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=os.getenv('USER')) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0\ \ """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") edit_rpc = """ merge set %(interface)s ianaift:ethernetCsmacd """ print("edit-config - create 'foo' ...") result = conn.rpc(edit_rpc%{'interface':"foo"}) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) print("edit-config - create 'bar' ...") result = conn.rpc(edit_rpc%{'interface':"bar"}) print("commit ...") result = conn.rpc(commit_rpc) ok = result.xpath('//ok') assert(len(ok)!=1) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/deviation-add-must/run.sh0000775000175000017500000000051614770023131024201 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=./test-deviation-add-must.yang --module=iana-if-type --no-startup --superuser=$USER 1>tmp/netconfd.stdout 2>tmp/netconfd.stderr & NETCONFD_PID=$! sleep 3 python session.litenc.py kill $NETCONFD_PID cat tmp/netconfd.stdout sleep 1 yuma123_2.14/netconf/test/netconfd/deviation-add-must/session.ncclient.py0000664000175000017500000000302714770023131026671 0ustar vladimirvladimir#!/usr/bin/env python from ncclient import manager from ncclient.xml_ import * from ncclient.operations.rpc import RPCError import time import sys, os def main(): print(""" #Description: Demonstrate that edit-config works. #Procedure: #1 - Create interface "foo" and commit. Verify commit succeeds. #2 - Create interface "bar" and commit. Verify commit fails. """) conn = manager.connect(host="127.0.0.1", port=830, username=os.getenv('USER'), password='admin', look_for_keys=True, timeout=10, device_params = {'name':'junos'}, hostkey_verify=False) print("Connected ...") edit_rpc = """ merge set %(interface)s ianaift:ethernetCsmacd """ print("edit-config - create 'foo' ...") result = conn.rpc(edit_rpc%{'interface':"foo"}) ok = result.xpath('//ok') assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) print("edit-config - create 'bar' ...") result = conn.rpc(edit_rpc%{'interface':"bar"}) print("commit ...") result = conn.rpc(commit_rpc) ok = result.xpath('//ok') assert(len(ok)!=1) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/ietf-interfaces-bis/0000775000175000017500000000000014770023131023161 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/ietf-interfaces-bis/session.litenc.py0000664000175000017500000002567514770023131026512 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml from lxml import etree #from litenc import strip_namespaces from io import StringIO import argparse from operator import attrgetter def get_interface_name(interface): print("calling get_interface_name") print((interface.tag)) name=interface.xpath('./name') assert(len(name)==1) print((name[0].text)) return name[0].text def strip_namespaces(tree): xslt=''' ''' xslt_doc = lxml.etree.fromstring(xslt) transform = lxml.etree.XSLT(xslt_doc) tree = transform(tree) return tree # Hmm .. ?! def yang_data_equal(e1, e2): print(e1.tag) print(e2.tag) if e1.tag != e2.tag: assert(False) return False if e1.text==None and "" != e2.text.strip(): assert(False) return False if e2.text==None and "" != e1.text.strip(): assert(False) return False if e1.text!=None and e2.text!=None and e1.text.strip() != e2.text.strip(): assert(False) return False #if e1.tail != e2.tail: assert(False) return False if e1.attrib != e2.attrib: print((e1.attrib)) print((e2.attrib)) assert(False) print("diff attrib") return False if len(e1) != len(e2): print(e1) print(len(e1)) print(e2) print(len(e2)) assert(False) return False for node in e1.findall("*"): # searching top-level nodes only: node1, node2 ... node[:] = sorted(node, key=attrgetter("tag")) for node in e2.findall("*"): # searching top-level nodes only: node1, node2 ... node[:] = sorted(node, key=attrgetter("tag")) for c1,c2 in zip(e1,e2): print(c1) print(c2) print("---") yang_data_equal(c1, c2) return 1 #return all(yang_data_equal(c1, c2) for c1, c2 in sorted(zip(e1, e2))) def main(): print(""" #Description: Testcase for draft-ietf-netmod-rfc7223bis-00.txt ietf-interfaces module. #Procedure: #1 - configuration as in rfc7223bis Appendix D. #2 - the /interfaces container and verify data is same as in rfc7223bis Appendix E. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw,strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") get_yang_library_rpc = """ """ print(" - /ietf-yang-library:modules-state ...") result = conn.rpc(get_yang_library_rpc, strip_ns=False) print(lxml.etree.tostring(result, pretty_print=True, inclusive_ns_prefixes=True)) namespaces = {"nc":"urn:ietf:params:xml:ns:netconf:base:1.0"} data = result.xpath('./nc:data', namespaces=namespaces) assert(len(data)==1) #Copied from draft-ietf-netmod-rfc7223bis-00 Appendix D. edit_config_rpc = """ merge set eth0 ianaift:ethernetCsmacd false eth1 ianaift:ethernetCsmacd true true eth1.10 ianaift:l2vlan true eth1 10 lo1 ianaift:softwareLoopback true """ print(" - load Appendix D. example config to 'candidate' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==1) commit_rpc = '' print(" - commit example config ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==1) get_example_data_rpc = """ ds:operational """ print(" - Appendix E. data ...") result = conn.rpc(get_example_data_rpc, strip_ns=False) print(lxml.etree.tostring(result, pretty_print=True, inclusive_ns_prefixes=True)) namespaces = {"ncds":"urn:ietf:params:xml:ns:yang:ietf-netconf-datastores"} data = result.xpath('./ncds:data', namespaces=namespaces) assert(len(data)==1) #Copy from draft-netconf-nmda-netconf-01 expected=""" eth0 ianaift:ethernetCsmacd false down down 2 00:01:02:03:04:05 2013-04-01T03:00:00+00:00 eth1 ianaift:ethernetCsmacd true up up 7 00:01:02:03:04:06 eth1.10 2013-04-01T03:00:00+00:00 true eth1.10 ianaift:l2vlan true up up 9 eth1 2013-04-01T03:00:00+00:00 eth1 10 eth2 ianaift:ethernetCsmacd down down 8 00:01:02:03:04:07 2013-04-01T03:00:00+00:00 lo1 ianaift:softwareLoopback true up up 1 2013-04-01T03:00:00+00:00 """ expected = lxml.etree.fromstring(expected) #strip comments comments = expected.xpath('//comment()') for c in comments: p = c.getparent() p.remove(c) #strip namespaces data1 = expected.xpath('.',namespaces=namespaces) data_expected=strip_namespaces(data1[0]) data_received=strip_namespaces(data[0]) #sort schema lists by key in alphabetical key order - hardcoded /interfaces/interface[name] for node in data_expected.findall("./interfaces"): node[:] = sorted(node, key=lambda child: get_interface_name(child)) for node in data_received.findall("./interfaces"): node[:] = sorted(node, key=lambda child: get_interface_name(child)) #sort attributes a = StringIO(); b = StringIO(); data_expected.write_c14n(a) data_received.write_c14n(b) print("Expected:") print((a.getvalue())) print("Received:") print((b.getvalue())) if yang_data_equal(lxml.etree.fromstring(a.getvalue()), lxml.etree.fromstring(b.getvalue())): print("Bingo!") return 0 else: print("Error: YANG data not equal!") return 1 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/ietf-interfaces-bis/Makefile.am0000775000175000017500000000053314770023131025221 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libtest-ietf-interfaces-bis.la libtest_ietf_interfaces_bis_la_SOURCES = \ test-ietf-interfaces-bis.c libtest_ietf_interfaces_bis_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libtest_ietf_interfaces_bis_la_LDFLAGS = -module -lyumaagt -lyumancx yuma123_2.14/netconf/test/netconfd/ietf-interfaces-bis/test-ietf-interfaces-bis.c0000664000175000017500000001306714770023131030134 0ustar vladimirvladimir/* module test-ietf-interfaces-bis */ #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "val.h" #include "val123.h" #include "agt_nmda.h" #include "val_set_cplxval_obj.h" /* module static variables */ static ncx_module_t *ietf_interfaces_mod; static ncx_module_t *ietf_ip_mod; static ncx_module_t *ietf_interfaces_mod; static obj_template_t* interfaces_obj; /* Registered callback functions: get_system_cfg_interfaces */ static status_t get_system_cfg_interfaces(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res = NO_ERR; res = val_set_cplxval_obj(dst_val,dst_val->obj,"\ \ eth0\ ianaift:ethernetCsmacd\ false\ down\ down\ 2\ 00:01:02:03:04:05\ \ \ 2013-04-01T03:00:00+00:00\ \ \ \ \ \ eth1\ ianaift:ethernetCsmacd\ true\ up\ up\ 7\ 00:01:02:03:04:06\ eth1.10\ \ \ 2013-04-01T03:00:00+00:00\ \ \ \ true\ \ \ eth1.10\ ianaift:l2vlan\ true\ up\ up\ 9\ eth1\ \ \ 2013-04-01T03:00:00+00:00\ \ \ \ eth1\ 10\ \ \ \ eth2\ ianaift:ethernetCsmacd\ down\ down\ 8\ 00:01:02:03:04:07\ \ \ 2013-04-01T03:00:00+00:00\ \ \ \ \ \ lo1\ ianaift:softwareLoopback\ true\ up\ up\ 1\ \ \ 2013-04-01T03:00:00+00:00\ \ \ \ \ "); assert(res == NO_ERR); return res; } /* The 3 mandatory callback functions: y_test_ietf_interfaces_bis_init, y_test_ietf_interfaces_bis_init2, y_test_ietf_interfaces_bis_cleanup */ status_t y_test_ietf_interfaces_bis_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-interfaces", NULL, &agt_profile->agt_savedevQ, &ietf_interfaces_mod); if (res != NO_ERR) { return res; } interfaces_obj = ncx_find_object( ietf_interfaces_mod, "interfaces"); assert(interfaces_obj != NULL); return res; } status_t y_test_ietf_interfaces_bis_init2(void) { status_t res; val_value_t* root_system_val; val_value_t* interfaces_val; res = NO_ERR; root_system_val = agt_nmda_get_root_system(); assert(root_system_val); interfaces_val = val_find_child(root_system_val, "ietf-interfaces", "interfaces"); assert(interfaces_val==NULL); interfaces_val = val_new_value(); assert(interfaces_val); val_init_virtual(interfaces_val, get_system_cfg_interfaces, interfaces_obj); val_add_child(interfaces_val, root_system_val); return NO_ERR; } void y_test_ietf_interfaces_bis_cleanup (void) { } yuma123_2.14/netconf/test/netconfd/ietf-interfaces-bis/run.sh0000775000175000017500000000417514770023131024333 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp cd tmp wget https://tools.ietf.org/id/draft-ietf-netmod-rfc7223bis-00.txt rfcstrip draft-ietf-netmod-rfc7223bis-00.txt pyang --ietf -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ ietf-interfaces@2017-08-17.yang pyang -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ ex-ethernet-bonding.yang pyang -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ ex-ethernet.yang pyang -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ ex-vlan.yang if [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc cd tmp for module in ietf-interfaces@2017-08-17 iana-if-type ietf-ip ex-ethernet-bonding.yang ex-ethernet.yang ex-vlan.yang ; do cp ../../../../modules/ietf/${module}.yang . confdc -c ${module}.yang --yangpath ../../../../ done NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath ${RUN_WITH_CONFD}/etc/confd --addloadpath . SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --modpath=.:/usr/share/yuma/modules --module=iana-if-type --module=./ietf-interfaces@2017-08-17.yang --module=ex-ethernet-bonding.yang --module=ex-ethernet.yang --module=ex-vlan.yang --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --modpath=.:/usr/share/yuma/modules --module=iana-if-type --module=./ietf-interfaces@2017-08-17.yang --module=ex-ethernet-bonding.yang --module=ex-ethernet.yang --module=ex-vlan.yang --module=test-ietf-interfaces-bis --no-startup --superuser=$USER 2>&1 1>server.log & SERVER_PID=$! cd .. fi sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD #python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/test-deviation-add-must0000775000175000017500000000005614770023131023740 0ustar vladimirvladimir#!/bin/bash -e cd deviation-add-must ./run.sh yuma123_2.14/netconf/test/netconfd/get/0000775000175000017500000000000014770023131020115 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/get/session.litenc.py0000664000175000017500000000467514770023131023443 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: ## Verify get works. #Procedure: ## do """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) # receive result=conn.receive() get_rpc = """ """ result = conn.rpc(get_rpc) print(lxml.etree.tostring(result)) rpc_error = result.xpath('rpc-error') assert(len(rpc_error)==0) print("[OK] Getting") get_rpc_w_filter = """ """ result = conn.rpc(get_rpc_w_filter) print(lxml.etree.tostring(result)) rpc_error = result.xpath('rpc-error') assert(len(rpc_error)==0) print("[OK] Getting with subtree filter") sys.exit(main()) yuma123_2.14/netconf/test/netconfd/get/run.sh0000775000175000017500000000041514770023131021260 0ustar vladimirvladimir#!/bin/bash -e killall -KILL netconfd || true rm -f /tmp/ncxserver.sock /usr/sbin/netconfd --no-startup --superuser=$USER & SERVER_PID=$! sleep 4 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill $SERVER_PID sleep 1 yuma123_2.14/netconf/test/netconfd/identityref-submodule/0000775000175000017500000000000014770023131023661 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/identityref-submodule/mod1.yang0000664000175000017500000000047214770023131025404 0ustar vladimirvladimirmodule mod1 { namespace "http://example.com/ns/identityref-submodule-2-m1"; prefix "m1"; include sub1; organization "example.com"; description "Testing identityref leaf defined in included submodule."; revision 2020-04-10 { description "Initial."; } } yuma123_2.14/netconf/test/netconfd/identityref-submodule/mod2.yang0000664000175000017500000000047414770023131025407 0ustar vladimirvladimirmodule mod2 { namespace "http://example.com/ns/identityref-submodule-2-m2"; prefix "m2"; organization "example.com"; description "Testing identityref leaf defined in included submodule."; revision 2020-04-10 { description "Initial."; } identity mybase; } yuma123_2.14/netconf/test/netconfd/identityref-submodule/sub1.yang0000664000175000017500000000067314770023131025421 0ustar vladimirvladimirsubmodule sub1 { belongs-to mod1 { prefix "m1"; } import mod2 { prefix "m2"; } organization "example.com"; description "Testing identityref typedef type defined in imported submodule."; revision 2020-04-10 { description "Initial."; } container b { leaf foo { type identityref { base "m2:mybase"; } } } } yuma123_2.14/netconf/test/netconfd/identityref-submodule/run.sh0000775000175000017500000000032614770023131025025 0ustar vladimirvladimir#!/bin/bash killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=./mod1.yang --modpath=./:/usr/share/yuma/modules/ --startup-error=stop --validate-config-only --superuser=$USER yuma123_2.14/netconf/test/netconfd/different-module-revisions/0000775000175000017500000000000014770023131024606 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/different-module-revisions/test-different-module-revisions-a.yang0000664000175000017500000000071514770023131034134 0ustar vladimirvladimirmodule test-different-module-revisions-a { namespace "http://yuma123.org/ns/test-different-module-revisions-a"; prefix test-dmr-a; import ietf-yang-types { prefix yt; revision-date "2013-07-15";} organization "yuma123"; description "Test module for different-module-revisions"; revision 2017-05-16 { description "1.st version"; } container a { leaf my-yang-identifier { type yt:yang-identifier; } } } yuma123_2.14/netconf/test/netconfd/different-module-revisions/README0000664000175000017500000000046314770023131025471 0ustar vladimirvladimirPURPOSE: Verify modules of different revisions are handled correctly. OPERATION: Uses 3 test modules a, b and c referencing different revisions of ietf-yang-types. Module c should fail loading. When a and b are loaded simultaneously the server should have both ietf-yang-types in the capabilities list. yuma123_2.14/netconf/test/netconfd/different-module-revisions/test-different-module-revisions-c.yang0000664000175000017500000000112414770023131034131 0ustar vladimirvladimirmodule test-different-module-revisions-c { namespace "http://yuma123.org/ns/test-different-module-revisions-c"; prefix test-dmr-c; import ietf-yang-types { prefix yt; revision-date "2010-09-24";} organization "yuma123"; description "Test module for different-module-revisions. Should fail when loaded since it uses the yang-identifier type defined in the newer 2013-07-15 revision of ietf-yang-types."; revision 2017-05-16 { description "1.st version"; } container c { leaf my-yang-identifier { type yt:yang-identifier; } } } yuma123_2.14/netconf/test/netconfd/different-module-revisions/session.litenc.py0000664000175000017500000001005114770023131030115 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Demonstrate returns correct versions. #Procedure: #1 - ietf-yang-types, ietf-yang-types@2013-07-15 and ietf-yang-types@2010-09-29. Confirm ietf-yang-types@2010-09-29 is different. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw,strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") #check for /hello/capabilities/capability == urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2013-07-15 #check for /hello/capabilities/capability == urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24 get_schema_rpc = """ ietf-yang-types """ print("get-schema - get ietf-yang-types ...") result = conn.rpc(get_schema_rpc) print(lxml.etree.tostring(result)) error_app_tag = result.xpath('rpc-error/error-app-tag') assert(len(error_app_tag)==1) assert(error_app_tag[0].text=="data-not-unique") get_schema_rpc = """ ietf-yang-types 2013-07-15 """ print("get-schema - get ietf-yang-types@2013-07-15 ...") result1 = conn.rpc(get_schema_rpc) print(lxml.etree.tostring(result1)) data1 = result1.xpath('data') assert(len(data1)==1) get_schema_rpc = """ ietf-yang-types 2013-07-15 """ print("get-schema - get ietf-yang-types@2013-07-15 ...") result2 = conn.rpc(get_schema_rpc) print(lxml.etree.tostring(result2)) data2 = result2.xpath('data') assert(len(data2)==1) get_schema_rpc = """ ietf-yang-types 2010-09-24 """ print("get-schema - get ietf-yang-types@2010-09-24 ...") result3 = conn.rpc(get_schema_rpc) print(lxml.etree.tostring(result3)) data3 = result3.xpath('data') assert(len(data3)==1) if(lxml.etree.tostring(data1[0]) != lxml.etree.tostring(data2[0])): print("Error: Should be the same schema file") assert(0) if(lxml.etree.tostring(data2[0]) == lxml.etree.tostring(data3[0])): print("Error: Should be different schema file") assert(0) print("OK: All good.") return 0 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/different-module-revisions/test-different-module-revisions-b.yang0000664000175000017500000000070114770023131034130 0ustar vladimirvladimirmodule test-different-module-revisions-b { namespace "http://yuma123.org/ns/test-different-module-revisions-b"; prefix test-dmr-b; import ietf-yang-types { prefix yt; revision-date "2010-09-24";} organization "yuma123"; description "Test module for different-module-revisions"; revision 2017-05-16 { description "1.st version"; } container b { leaf my-counter32 { type yt:counter32; } } } yuma123_2.14/netconf/test/netconfd/different-module-revisions/run.sh0000775000175000017500000000134614770023131025755 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --validate-config-only --module=./test-different-module-revisions-a.yang --module=./test-different-module-revisions-b.yang --no-startup --superuser=$USER ! /usr/sbin/netconfd --validate-config-only --module=./test-different-module-revisions-a.yang --module=./test-different-module-revisions-b.yang --module=./test-different-module-revisions-c.yang --no-startup --superuser=$USER /usr/sbin/netconfd --module=./test-different-module-revisions-a.yang --module=./test-different-module-revisions-b.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! sleep 3 python session.litenc.py kill $SERVER_PID cat tmp/server.stdout sleep 1 yuma123_2.14/netconf/test/netconfd/leaflist-union/0000775000175000017500000000000014770023131022267 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/leaflist-union/session.litenc.py0000664000175000017500000000445414770023131025610 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Send invalid configuration. Check later to see if # netconfd crashed. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) # receive result=conn.receive() print("# send invalid value for union element of leaf-list") get_rpc = """ merge set
this should not work
""" print("edit-config ...") result = conn.rpc(get_rpc) result=litenc_lxml.strip_namespaces(result) print(lxml.etree.tostring(result)) return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/leaflist-union/leaflist-union-test.yang0000664000175000017500000000103014770023131027047 0ustar vladimirvladimirmodule leaflist-union-test { namespace "http://yuma123.org/ns/test-leaflist-union-test"; prefix "llut"; import ietf-inet-types { prefix inet; } organization "yuma123.org"; description "Model used for regression test"; revision 2018-06-11 { description "Initial revision"; reference "RFC ..."; } container test { leaf-list address { type inet:ip-address; description "An IP address"; } } } yuma123_2.14/netconf/test/netconfd/leaflist-union/run.sh0000775000175000017500000000114714770023131023435 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then exit 0 else killall -KILL netconfd || true rm -f /tmp/ncxserver.sock /usr/sbin/netconfd --module=ietf-inet-types --module=leaflist-union-test --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --module=ietf-inet-types --module=leaflist-union-test --no-startup --superuser=$USER 1>tmp/server.log 2>&1 & SERVER_PID=$! fi sleep 3 python session.litenc.py cat tmp/server.log sleep 1 # it's ok for the grep command below to fail set +e grep -q 'get_instance_string.*Assertion' tmp/server.log && exit 1 exit 0 yuma123_2.14/netconf/test/netconfd/validate-config-only/0000775000175000017500000000000014770023131023351 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/validate-config-only/startup-cfg-good.xml0000664000175000017500000000054014770023131027257 0ustar vladimirvladimir foo ianaift:ethernetCsmacd yuma123_2.14/netconf/test/netconfd/validate-config-only/startup-cfg-bad.xml0000664000175000017500000000051114770023131027053 0ustar vladimirvladimir ianaift:ethernetCsmacd yuma123_2.14/netconf/test/netconfd/validate-config-only/run.sh0000775000175000017500000000213614770023131024516 0ustar vladimirvladimir#!/bin/bash if [ "$RUN_WITH_CONFD" != "" ] ; then # skipped test return value exit 77 fi rm -rf tmp || true mkdir tmp /usr/sbin/netconfd --validate-config-only --startup-error=stop --startup=startup-cfg-good.xml --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang 1>tmp/netconfd.stdout 2>tmp/netconfd.stderr GOOD_RES=$? cat tmp/netconfd.stdout if [ "$GOOD_RES" != "0" ] ; then echo "Error: Failed with startup-cfg-good.xml" exit $GOOD_RES else echo "OK: Validated with startup-cfg-good.xml" fi /usr/sbin/netconfd --validate-config-only --startup-error=stop --startup=startup-cfg-bad.xml --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang 1>tmp/netconfd.stdout 2>tmp/netconfd.stderr BAD_RES=$? cat tmp/netconfd.stdout if [ "$BAD_RES" == "0" ] ; then echo "Error: Did not detect problem with startup-cfg-bad.xml" exit -1 else echo "OK: Detected problem with startup-cfg-bad.xml" fi cat tmp/netconfd.stdout exit 0 yuma123_2.14/netconf/test/netconfd/Makefile.am0000775000175000017500000000272214770023131021400 0ustar vladimirvladimirTESTS=\ test-ietf-alarms \ test-nacm \ test-get \ test-notification-in-grouping \ test-identity-iffeature \ test-enum-iffeature \ test-action-in-grouping \ test-leaf-list-defaults \ test-perf \ test-anyxml \ test-val123-api \ test-leaflist-union \ test-netconf-1dot1 \ test-yang-validation \ test-copy-config \ test-deviation-add-must \ test-deviation-not-supported \ test-deviation-delete-ext \ test-deviation-replace-type \ test-deviation-in-modules-state \ test-edit-config \ test-lock \ test-multiple-edit-callbacks \ test-netconf-notifications \ test-rollback-on-error \ test-validate-config-only \ test-identityref-typedef \ test-identityref-submodule \ test-instance-identifier \ test-when \ test-xpath-ancestor \ test-xpath-current \ test-xpath-re-match \ test-xpath-re-match-concat \ test-xpath-deref \ test-xpath-deref-2 \ test-xpath-derived-from \ test-xpath-derived-from-or-self \ test-xpath-enum-value \ test-xpath-bit-is-set \ test-yang-library \ test-yang-library-submodules \ test-ietf-netmod-sub-intf-vlan-model \ test-ietf-routing \ test-ietf-routing-bis \ test-ietf-interfaces-bis \ test-ietf-ip-bis \ test-draft-ietf-bmwg-network-tester-cfg \ test-multi-instance \ test-get-schema \ test-agt-commit-complete \ test-cesnet-libyang-conformance-suite \ test-yang-conformance \ test-yang-1dot1-conformance \ test-memory-leak SUBDIRS= \ multiple-edit-callbacks \ rollback-on-error \ ietf-routing \ ietf-routing-bis \ ietf-interfaces-bis \ ietf-ip-bis \ agt-commit-complete yuma123_2.14/netconf/test/netconfd/ietf-ip-bis/0000775000175000017500000000000014770023131021446 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/ietf-ip-bis/session.litenc.py0000664000175000017500000002446514770023131024773 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml from lxml import etree #from litenc import strip_namespaces from io import StringIO import argparse from operator import attrgetter def get_interface_name(interface): print("calling get_interface_name") print((interface.tag)) name=interface.xpath('./name') assert(len(name)==1) print((name[0].text)) return name[0].text def strip_namespaces(tree): xslt=''' ''' xslt_doc = lxml.etree.fromstring(xslt) transform = lxml.etree.XSLT(xslt_doc) tree = transform(tree) return tree # Hmm .. ?! def yang_data_equal(e1, e2): print(e1.tag) print(e2.tag) if e1.tag != e2.tag: assert(False) return False if e1.text==None and e2.text==None: print("empty container") else: if e1.text==None and "" != e2.text.strip(): assert(False) return False if e2.text==None and "" != e1.text.strip(): assert(False) return False if e1.text!=None and e2.text!=None and e1.text.strip() != e2.text.strip(): assert(False) return False #if e1.tail != e2.tail: assert(False) return False if e1.attrib != e2.attrib: print((e1.attrib)) print((e2.attrib)) assert(False) print("diff attrib") return False if len(e1) != len(e2): print(e1) print(len(e1)) print(e2) print(len(e2)) assert(False) return False for node in e1.findall("*"): # searching top-level nodes only: node1, node2 ... node[:] = sorted(node, key=attrgetter("tag")) for node in e2.findall("*"): # searching top-level nodes only: node1, node2 ... node[:] = sorted(node, key=attrgetter("tag")) for c1,c2 in zip(e1,e2): print(c1) print(c2) print("---") yang_data_equal(c1, c2) return 1 #return all(yang_data_equal(c1, c2) for c1, c2 in sorted(zip(e1, e2))) def main(): print(""" #Description: Testcase for draft-ietf-netmod-rfc7277bis-00.txt ietf-ip module. #Procedure: #1 - configuration as in rfc7277bis Appendix A. #2 - the /interfaces container and verify data is same as in rfc7277bis Appendix B. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw,strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") get_yang_library_rpc = """ """ print(" - /ietf-yang-library:modules-state ...") result = conn.rpc(get_yang_library_rpc, strip_ns=False) print(lxml.etree.tostring(result, pretty_print=True, inclusive_ns_prefixes=True)) namespaces = {"nc":"urn:ietf:params:xml:ns:netconf:base:1.0"} data = result.xpath('./nc:data', namespaces=namespaces) assert(len(data)==1) #Copied from draft-ietf-netmod-rfc7277bis-00 Appendix A. edit_config_rpc = """ merge set eth0 ianaift:ethernetCsmacd
192.0.2.1 24
1280
2001:db8::10 32
0
""" print(" - load Appendix B. example config to 'candidate' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==1) commit_rpc = '' print(" - commit example config ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==1) get_example_data_rpc = """ ds:operational """ print(" - Appendix B. data ...") result = conn.rpc(get_example_data_rpc, strip_ns=False) print(lxml.etree.tostring(result, pretty_print=True, inclusive_ns_prefixes=True)) namespaces = {"ncds":"urn:ietf:params:xml:ns:yang:ietf-netconf-datastores"} data = result.xpath('./ncds:data', namespaces=namespaces) assert(len(data)==1) #Copy from draft-netconf-nmda-netconf-01 #removed false if this is the default value then enabled should also be present #removed 1500 since it is not in the configuration #add or:origin="or:learned" to the ipv4 neighbor #removed false from ipv6 since it is not in the configuration expected=""" eth0 ianaift:ethernetCsmacd
192.0.2.1 24 static
192.0.2.2 00:01:02:03:04:05
1280
2001:db8::10 32 static preferred
2001:db8::1:100 32 dhcp preferred
0 2001:db8::1 00:01:02:03:04:05 dynamic reachable 2001:db8::4 dynamic incomplete
""" expected = lxml.etree.fromstring(expected) #strip comments comments = expected.xpath('//comment()') for c in comments: p = c.getparent() p.remove(c) #strip namespaces data1 = expected.xpath('.',namespaces=namespaces) data_expected=strip_namespaces(data1[0]) data_received=strip_namespaces(data[0]) #sort schema lists by key in alphabetical key order - hardcoded /interfaces/interface[name] for node in data_expected.findall("./interfaces"): node[:] = sorted(node, key=lambda child: get_interface_name(child)) for node in data_received.findall("./interfaces"): node[:] = sorted(node, key=lambda child: get_interface_name(child)) #sort attributes a = StringIO(); b = StringIO(); data_expected.write_c14n(a) data_received.write_c14n(b) print("Expected:") print((a.getvalue())) print("Received:") print((b.getvalue())) if yang_data_equal(lxml.etree.fromstring(a.getvalue()), lxml.etree.fromstring(b.getvalue())): print("Bingo!") return 0 else: print("Error: YANG data not equal!") return 1 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/ietf-ip-bis/Makefile.am0000775000175000017500000000050314770023131023503 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libtest-ietf-ip-bis.la libtest_ietf_ip_bis_la_SOURCES = \ test-ietf-ip-bis.c libtest_ietf_ip_bis_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform $(XML_CPPFLAGS) libtest_ietf_ip_bis_la_LDFLAGS = -module -lyumaagt -lyumancx yuma123_2.14/netconf/test/netconfd/ietf-ip-bis/test-ietf-ip-bis.c0000664000175000017500000001317114770023131024702 0ustar vladimirvladimir/* module test-ietf-ip-bis */ #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "val.h" #include "val123.h" #include "agt_nmda.h" #include "val_set_cplxval_obj.h" /* module static variables */ static ncx_module_t *ietf_interfaces_mod; static ncx_module_t *ietf_ip_mod; static obj_template_t* interfaces_obj; /* Registered callback functions: get_system_cfg_interfaces */ static status_t get_learned_cfg_interfaces(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res = NO_ERR; // false\ // 1500\ // 1280\ // false\ res = val_set_cplxval_obj(dst_val,dst_val->obj,"\ \ eth0\ ianaift:ethernetCsmacd\ \ \
\ 192.0.2.1\ 24\ static\
\ \ 192.0.2.2\ \ 00:01:02:03:04:05\ \ \
\ \
\ 2001:db8::10\ 32\ static\ preferred\
\
\ 2001:db8::1:100\ 32\ dhcp\ preferred\
\ 0\ \ 2001:db8::1\ \ 00:01:02:03:04:05\ \ dynamic\ \ reachable\ \ \ 2001:db8::4\ dynamic\ incomplete\ \
\
\ "); assert(res == NO_ERR); return res; } static status_t get_system_cfg_interfaces(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res = NO_ERR; res = val_set_cplxval_obj(dst_val,dst_val->obj,"\ \ eth0\ ianaift:ethernetCsmacd\ \ \ "); assert(res == NO_ERR); return res; } /* The 3 mandatory callback functions: y_test_ietf_ip_bis_init, y_test_ietf_ip_bis_init2, y_test_ietf_ip_bis_cleanup */ status_t y_test_ietf_ip_bis_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-interfaces", NULL, &agt_profile->agt_savedevQ, &ietf_interfaces_mod); assert(res == NO_ERR); res = ncxmod_load_module( "ietf-ip", NULL, &agt_profile->agt_savedevQ, &ietf_ip_mod); assert(res == NO_ERR); interfaces_obj = ncx_find_object( ietf_interfaces_mod, "interfaces"); assert(interfaces_obj != NULL); return res; } status_t y_test_ietf_ip_bis_init2(void) { status_t res; val_value_t* root_system_val; val_value_t* root_learned_val; val_value_t* interfaces_val; res = NO_ERR; root_system_val = agt_nmda_get_root_system(); assert(root_system_val); root_learned_val = agt_nmda_get_root_learned(); assert(root_learned_val); interfaces_val = val_find_child(root_system_val, "ietf-interfaces", "interfaces"); assert(interfaces_val==NULL); interfaces_val = val_new_value(); assert(interfaces_val); val_init_virtual(interfaces_val, get_system_cfg_interfaces, interfaces_obj); val_add_child(interfaces_val, root_system_val); interfaces_val = val_new_value(); assert(interfaces_val); val_init_virtual(interfaces_val, get_learned_cfg_interfaces, interfaces_obj); val_add_child(interfaces_val, root_learned_val); return NO_ERR; } void y_test_ietf_ip_bis_cleanup (void) { } yuma123_2.14/netconf/test/netconfd/ietf-ip-bis/run.sh0000775000175000017500000000463314770023131022617 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp cd tmp wget https://www.rfc-editor.org/rfc/rfc8343.txt wget https://www.rfc-editor.org/rfc/rfc8344.txt rfcstrip rfc8343.txt rfcstrip rfc8344.txt pyang --ietf -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ ietf-interfaces@2018-02-20.yang pyang -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ example-ethernet-bonding.yang pyang -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ example-ethernet.yang pyang -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ example-vlan.yang pyang --ietf -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ ietf-ip@2018-02-22.yang if [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc for module in ietf-interfaces@2018-02-20 ietf-ip@2018-02-22 example-ethernet-bonding example-ethernet example-vlan ../../../../modules/ietf/iana-if-type@2014-05-08; do confdc -c ${module}.yang --yangpath .:../../../../modules/ietf done NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath ${RUN_WITH_CONFD}/etc/confd --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --modpath=.:/usr/share/yuma/modules --module=iana-if-type --module=./ietf-interfaces@2018-02-20.yang --module=./ietf-ip@2018-02-22.yang --module=example-ethernet-bonding.yang --module=example-ethernet.yang --module=example-vlan.yang --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --with-nmda=true --modpath=.:/usr/share/yuma/modules --module=iana-if-type --module=./ietf-interfaces@2018-02-20.yang --module=./ietf-ip@2018-02-22.yang --module=example-ethernet-bonding.yang --module=example-ethernet.yang --module=example-vlan.yang --module=test-ietf-ip-bis --no-startup --superuser=$USER 2>&1 1>server.log & SERVER_PID=$! cd .. fi sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD #python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/test-edit-config0000775000175000017500000000005014770023131022424 0ustar vladimirvladimir#!/bin/bash -e cd edit-config ./run.sh yuma123_2.14/netconf/test/netconfd/edit-config/0000775000175000017500000000000014770023131021526 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/edit-config/session.duplicated-list-entry.litenc.py0000664000175000017500000001106214770023131031265 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Demonstrate that duplicated list entries in edit-config are detected. #Procedure: #1 - Create interface "foo" and commit. Verify commit succeeds. #2 - Create 2x duplicate interface "bar" and commit. Verify commit fails. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) edit_config_rpc = """ merge set foo ianaift:ethernetCsmacd """ print("edit-config - create single 'foo' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) edit_config_rpc = """ merge set bar ianaift:ethernetCsmacd bar ianaift:ethernetCsmacd """ print("edit-config - attempt to create duplicated 'bar's ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)!=1) edit_config_rpc = """ merge set """ print("edit-config - delete /interfaces ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/edit-config/README0000664000175000017500000000201514770023131022404 0ustar vladimirvladimirFILES: * run.sh - shell script executing the testcase. Starts netcond server and executes the session scripts. * session.ncclient.py - python script using ncclient external python module. Connects to the started netconf server performs edit-config RPCs and verify the configuration is correctly committed. * session.duplicated-list-entry.litenc.py - python script using litenc python module (added at ../litenc). Connects to the started netconf server performs edit-config RPCs and verify edit-config attempting to create duplicated interface list entry (equal 'name' keys) configuration is correctly rejected. PURPOSE: Verify edit-config works OPERATION: Uses edit-config and reads back the configuration with get-config for verification. ENVIRONMENT VARIABLES One can start confd server instead of netconfd by exporting variable named RUN_WITH_CONFD Example: export RUN_WITH_CONFD=~/confd/root ; ./run.sh The ~/confd/root in the example is the path to the confd installation where confdrc is located. yuma123_2.14/netconf/test/netconfd/edit-config/session.replace-leaf-list.yangcli.py0000664000175000017500000000503414770023131030502 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli from lxml import etree import yangrpc def main(): print(""" #Description: Demonstrate that leaf-list values can be replaced. Although the operation result is identical to either create or none. #Procedure: #1 - Create /system/dns-resolver/search entry with value 'foo'. Verify commit succeeds. #2 - Create entry 'foo'. #3 - Replace entry 'bar'. Read back the value after commit. Validate both 'foo' and 'bar' are present, """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) time.sleep(1) ok = yangcli(conn, '''delete /system''').xpath('./ok') ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''create /system/dns-resolver/search value='foo' ''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) result = yangcli(conn, "xget /system") searches = result.xpath('./data/system/dns-resolver/search') for search in searches: print(search.text) assert(len(searches)==1) assert(searches[0].text=='foo') ok = yangcli(conn, '''replace /system/dns-resolver/search value='bar' ''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) result = yangcli(conn, "xget /system") searches = result.xpath('./data/system/dns-resolver/search') for search in searches: print(search.text) assert(len(searches)==2) result = yangcli(conn, "delete /system").xpath('./ok') assert(len(ok)==1) result = yangcli(conn, "commit").xpath('./ok') assert(len(ok)==1) return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/edit-config/session.replace-leaf.yangcli.py0000664000175000017500000000525314770023131027534 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli from lxml import etree import yangrpc def main(): print(""" #Description: Demonstrate that leaf values can be replaced. #Procedure: #1 - Create interface "foo" of type=ethernetCsmacd. Verify commit succeeds. #2 - Replace type for interface "foo" with type=other. Read back the type after commit. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) time.sleep(1) ok = yangcli(conn, '''delete /interfaces''').xpath('./ok') ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''create /interfaces/interface -- name='foo' type='ethernetCsmacd' ''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) result = yangcli(conn, "xget /interfaces") names = result.xpath('./data/interfaces/interface/name') for name in names: print(name.text) types = result.xpath('./data/interfaces/interface/type') for type in types: print(type.text) assert(len(types)==1) assert(types[0].text=='ianaift:ethernetCsmacd') ok = yangcli(conn, '''replace /interfaces/interface[name='foo']/type value='other' ''').xpath('./ok') assert(len(ok)==1) ok = yangcli(conn, '''commit''').xpath('./ok') assert(len(ok)==1) result = yangcli(conn, "xget /interfaces") names = result.xpath('./data/interfaces/interface/name') for name in names: print(name.text) types = result.xpath('./data/interfaces/interface/type') for type in types: print(type.text) assert(len(types)==1) assert(types[0].text=='ianaift:other') result = yangcli(conn, "delete /interfaces").xpath('./ok') assert(len(ok)==1) result = yangcli(conn, "commit").xpath('./ok') assert(len(ok)==1) return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/edit-config/run.sh0000775000175000017500000000336114770023131022674 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana_if_type.fxs confdc -c /usr/share/yuma/modules/ietf/ietf-system@2014-08-06.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-system.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --module=/usr/share/yuma/modules/ietf/ietf-system@2014-08-06.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.ncclient.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD python session.duplicated-list-entry.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD python session.replace-leaf.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD python session.replace-leaf-list.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/edit-config/session.ncclient.py0000664000175000017500000001343414770023131025366 0ustar vladimirvladimir#!/usr/bin/env python from ncclient import manager from ncclient.xml_ import * import time import sys, os import argparse def main(): print(""" #Description: Demonstrate that edit-config works. #Procedure: #1 - Create interface "foo" and commit. #2 - Verify get-config returns the interface named "foo" in #3 - Create interface "bar" and commit. #4 - Verify get-config returns the interfaces named "bar" and "foo" in #5 - Delete interface "foo" and commit. #6 - Verify get-config returns the interface named "bar" only in #7 - Delete interface "bar" and commit. #8 - Verify get-config returns no interfaces in """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None look_for_keys=True else: password=args.password look_for_keys=False conn = manager.connect(host=server, port=port, username=user, password=password, look_for_keys=look_for_keys, timeout=10, device_params = {'name':'junos'}, hostkey_verify=False) print("Connected ...") rpc = """ merge set foo ianaift:ethernetCsmacd """ print("edit-config - create 'foo' ...") result = conn.rpc(rpc) rpc = """ """ print("commit ...") result = conn.rpc(rpc) rpc = """ """ print("get-config ...") result = conn.rpc(rpc) names = result.xpath('//data/interfaces/interface/name') print((len(names))) assert(len(names)==1) print((names[0].text)) assert(names[0].text=='foo') rpc = """ merge set bar ianaift:ethernetCsmacd """ print("edit-config - create 'bar' ...") result = conn.rpc(rpc) rpc = """ """ print("commit ...") result = conn.rpc(rpc) rpc = """ """ print("get-config ...") result = conn.rpc(rpc) names = result.xpath('//data/interfaces/interface/name') print((len(names))) assert(len(names)==2) name = result.xpath('//data/interfaces/interface[name=\'bar\']/name') print((name[0].text)) assert(name[0].text=='bar') name = result.xpath('//data/interfaces/interface[name=\'foo\']/name') print((name[0].text)) assert(name[0].text=='foo') rpc = """ merge set foo """ print("edit-config - delete 'foo' ...") result = conn.rpc(rpc) rpc = """ """ print("commit ...") result = conn.rpc(rpc) rpc = """ """ print("get-config ...") result = conn.rpc(rpc) names = result.xpath('//data/interfaces/interface/name') print((len(names))) assert(len(names)==1) name = result.xpath('//data/interfaces/interface[name=\'bar\']/name') print((name[0].text)) assert(name[0].text=='bar') rpc = """ merge set bar """ print("edit-config - delete 'bar' ...") result = conn.rpc(rpc) rpc = """ """ print("commit ...") result = conn.rpc(rpc) rpc = """ """ print("get-config ...") result = conn.rpc(rpc) names = result.xpath('//data/interfaces/interface/name') print((len(names))) assert(len(names)==0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/xpath-derived-from/0000775000175000017500000000000014770023131023043 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/xpath-derived-from/session.litenc.py0000664000175000017500000001405014770023131026355 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Demonstrate that derived-from() works. #Procedure: #1 - Create interfaces foo type=fast-ethernet and bar type=other. #2 - Validate ethernet-mac leaf can be commited on foo. #3 - Validate ethernet-mac leaf can not be commited on bar. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") edit_config_rpc = """ merge set """ print("edit-config - delete /interfaces ...") result = conn.rpc(edit_config_rpc) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) edit_config_rpc = """ merge set foo txfd:fast-ethernet bar txfd:other """ print("edit-config - create 'foo' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) edit_config_rpc = """ merge set foo 01:23:45:67:89:AB """ print("edit-config - create 'foo' ethernet-mac ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) edit_config_rpc = """ merge set bar 01:23:45:67:89:AB """ print("edit-config - create 'bar' ethernet-mac ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) #assert(len(ok)==0) commit_rpc = """ """ result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) get_config_rpc = """ """ print("get-config ...") result = conn.rpc(get_config_rpc) ethernet_mac_foo = result.xpath('data/interfaces/interface[name="foo"]/ethernet-mac') ethernet_mac_bar = result.xpath('data/interfaces/interface[name="bar"]/ethernet-mac') assert(len(ethernet_mac_foo)==1) assert(len(ethernet_mac_bar)==0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/xpath-derived-from/run.sh0000775000175000017500000000216014770023131024205 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cp *.yang tmp cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c test-xpath-derived-from.yang --yangpath /usr/share/yuma/modules/ietf -o test-xpath-derived-from.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=./test-xpath-derived-from.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/xpath-derived-from/test-xpath-derived-from.yang0000664000175000017500000000136614770023131030413 0ustar vladimirvladimirmodule test-xpath-derived-from { yang-version 1.1; namespace "http://yuma123.org/ns/test-xpath-derived-from"; prefix txdf; import ietf-interfaces { prefix if; } import ietf-yang-types {prefix yang;} organization "yuma123.org"; description "Part of the xpath-derived-from test."; revision 2017-07-15 { description "Initial version"; } identity ethernet { base if:interface-type; } identity other { base if:interface-type; } identity fast-ethernet { base ethernet; } identity gigabit-ethernet { base ethernet; } augment "/if:interfaces/if:interface" { when 'derived-from(if:type, "txdf:ethernet")'; leaf ethernet-mac { type yang:mac-address; } } } yuma123_2.14/netconf/test/netconfd/test-yang-conformance0000775000175000017500000000005514770023131023467 0ustar vladimirvladimir#!/bin/bash -e cd yang-conformance ./run.sh yuma123_2.14/netconf/test/netconfd/val123-api/0000775000175000017500000000000014770023131021115 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/val123-api/session.py0000664000175000017500000000105414770023131023152 0ustar vladimirvladimirfrom yangcli.yangcli import yangcli import lxml import yangrpc import sys import os conn = yangrpc.connect("127.0.0.1", 830, os.getenv('USER'), None, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa",) if(conn==None): print("Error: yangrpc failed to connect!") sys.exit(1) result=yangcli(conn, "xget /system-state/platform") print(lxml.etree.tostring(result)) assert("foo"==result.xpath('./data/system-state/platform/os-name')[0].text) assert("bar"==result.xpath('./data/system-state/platform/os-release')[0].text) print("Done.") yuma123_2.14/netconf/test/netconfd/val123-api/test-val123-api.c0000664000175000017500000000653514770023131024026 0ustar vladimirvladimir/* module test-val123-api */ #include #include #include #include #include #include #include #include #include #include "ncxmod.h" #include "val123.h" #include "agt.h" #include "ses.h" #include "cfg.h" #include "getcb.h" /* module static variables */ static ncx_module_t *ietf_system_mod; static status_t get_os_name(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { obj_template_t* obj; val_value_t* val; status_t res; res = NO_ERR; val = val_new_value(); assert(val); obj = obj_find_child(dst_val->obj, "ietf-system", "os-name"); assert(obj); val_init_from_template(val,obj); /* /system-state/platform/os-name */ res = val_set_simval_obj(val, val->obj, "foo"); val_add_child(val, dst_val); return res; } static status_t get_os_release(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { obj_template_t* obj; val_value_t* val; status_t res; res = NO_ERR; obj = obj_find_child(dst_val->obj, "ietf-system", "os-release"); assert(obj); val = val_new_value(); assert(val); val_init_from_template(val,obj); /* /system-state/platform/os-release */ res = val_set_simval_obj(val, val->obj, "bar"); val_add_child(val, dst_val); return res; } /* The 3 mandatory callback functions: y_test_val123_api_init, y_test_val123_api_init2, y_test_val123_api_cleanup */ status_t y_test_val123_api_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-system", NULL, &agt_profile->agt_savedevQ, &ietf_system_mod); if (res != NO_ERR) { return res; } return res; } status_t y_test_val123_api_init2(void) { status_t res; cfg_template_t* runningcfg; val_value_t* system_state_val; obj_template_t* system_state_obj; obj_template_t* platform_obj; val_value_t* platform_val; res = NO_ERR; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg && runningcfg->root); system_state_obj = ncx_find_object( ietf_system_mod, "system-state"); assert(system_state_obj); system_state_val = val_new_value(); assert(system_state_val); val_init_from_template(system_state_val, system_state_obj); val_add_child(system_state_val, runningcfg->root); platform_obj = obj_find_child(system_state_val->obj, "ietf-system", "platform"); assert(platform_obj != NULL); platform_val = val_new_value(); assert(platform_val != NULL); val_init_virtual(platform_val, get_os_name, platform_obj); val_add_child(platform_val, system_state_val); val123_add_virtual_cb(platform_val, get_os_release); return NO_ERR; } void y_test_val123_api_cleanup (void) { } yuma123_2.14/netconf/test/netconfd/val123-api/Makefile.am0000775000175000017500000000042314770023131023153 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libtest-val123-api.la libtest_val123_api_la_SOURCES = test-val123-api.c libtest_val123_api_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libtest_val123_api_la_LDFLAGS = -module -lyumaagt -lyumancx yuma123_2.14/netconf/test/netconfd/val123-api/run.sh0000775000175000017500000000040114770023131022253 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=test-val123-api --no-startup --superuser=$USER 1>tmp/netconfd.stdout 2>tmp/netconfd.stderr & NETCONFD_PID=$! sleep 2 python session.py kill $NETCONFD_PID yuma123_2.14/netconf/test/netconfd/netconf-1dot1/0000775000175000017500000000000014770023131021720 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/netconf-1dot1/session.litenc.py0000664000175000017500000000534714770023131025243 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import socket import paramiko import litenc import litenc_lxml import lxml import argparse #Steps follow: def step_1(server, port, user, password): print("#1") print("Connecting ...") conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) assert(0) print("Connected ...") return (conn_raw) def step_2(conn_raw): print("#2") time.sleep(5) try: rx_data = conn_raw.chan.recv(1000000) except socket.timeout: print("Timeout!") assert(0) #print the received from server print(rx_data) #intentionally send and without delay tx_data = """ urn:ietf:params:netconf:base:1.0 urn:ietf:params:netconf:base:1.1 ]]>]]> #181 ## """ print(tx_data) data=tx_data try: while data: n = conn_raw.chan.send(data) if n <= 0: return -1 data = data[n:] except Exception as e: print("Exception while sending.") assert(0) time.sleep(5) print("Receiving ...") try: rx_data = conn_raw.chan.recv(1000000) except socket.timeout: print("Timeout!") assert(0) print(rx_data) print("Done.") def main(): print(""" #Description: Test implementation of RFC6241 framing #Procedure: #1 - Connect. #2 - Validate NETCONF1.1 framed RPCs are accepted and processed correctly. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw=step_1(server=server, port=port, user=user, password=password) step_2(conn_raw) return 0 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/netconf-1dot1/run.sh0000775000175000017500000000207114770023131023063 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc cd tmp for module in ietf-interfaces@2014-05-08.yang iana-if-type@2014-05-08.yang ; do cp ../../../../modules/ietf/${module} . confdc -c ${module} --yangpath ../../../../ done NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath ${RUN_WITH_CONFD}/etc/confd --addloadpath . & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=../../../modules/ietf/iana-if-type@2014-05-08.yang --module=../../../modules/ietf/ietf-interfaces@2014-05-08.yang --no-startup --superuser=$USER & SERVER_PID=$! fi sleep 4 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID sleep 1 yuma123_2.14/netconf/test/netconfd/test-lock0000775000175000017500000000004114770023131021164 0ustar vladimirvladimir#!/bin/bash -e cd lock ./run.sh yuma123_2.14/netconf/test/netconfd/ietf-alarms/0000775000175000017500000000000014770023131021542 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/ietf-alarms/Makefile.am0000775000175000017500000000047014770023131023602 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libtest-ietf-routing.la libtest_ietf_routing_la_SOURCES = \ test-ietf-routing.c libtest_ietf_routing_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libtest_ietf_routing_la_LDFLAGS = -module -lyumaagt -lyumancx yuma123_2.14/netconf/test/netconfd/ietf-alarms/run.sh0000775000175000017500000000224114770023131022704 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc cd tmp for module in ietf-alarms iana-if-type ; do cp ../../../../modules/ietf/${module}.yang . confdc -c ${module}.yang --yangpath ../../../../ done NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath ${RUN_WITH_CONFD}/etc/confd --addloadpath . SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=ietf-alarms --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --module=ietf-alarms --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 #python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD #python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/test-netconf-1dot10000775000175000017500000000005214770023131022620 0ustar vladimirvladimir#!/bin/bash -e cd netconf-1dot1 ./run.sh yuma123_2.14/netconf/test/netconfd/test-draft-ietf-bmwg-network-tester-cfg0000775000175000017500000000007714770023131026754 0ustar vladimirvladimir#!/bin/bash -e cd draft-ietf-bmwg-network-tester-cfg ./run.sh yuma123_2.14/netconf/test/netconfd/test-ietf-alarms0000775000175000017500000000004714770023131022446 0ustar vladimirvladimir#!/bin/bash -e cd ietf-alarms ./run.sh yuma123_2.14/netconf/test/netconfd/xpath-current/0000775000175000017500000000000014770023131022142 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/xpath-current/test-xpath-current.yang0000664000175000017500000000107314770023131026604 0ustar vladimirvladimirmodule test-xpath-current { yang-version 1.1; namespace "http://yuma123.org/ns/test-xpath-current"; prefix txc; import ietf-interfaces { prefix if; } organization "yuma123.org"; description "Part of the xpath-curent test."; revision 2017-07-13 { description "Initial version"; } augment "/if:interfaces/if:interface" { leaf outgoing-interface { type leafref { path "/if:interfaces/if:interface/if:name"; } must '/if:interfaces/if:interface[if:name=current()]/if:enabled = "true"'; } } } yuma123_2.14/netconf/test/netconfd/xpath-current/session.litenc.py0000664000175000017500000001323114770023131025454 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Demonstrate that current() works. #Procedure: #1 - Create interfaces foo enabled=true and bar enabled=false. #2 - Validate foo-in with outgoing-interface=foo can be created. #3 - Validate bar-in with outgoing-interface=bar can not be created. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") edit_config_rpc = """ merge set """ print("edit-config - delete /interfaces ...") result = conn.rpc(edit_config_rpc) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) edit_config_rpc = """ merge set foo ianaift:ethernetCsmacd true bar ianaift:ethernetCsmacd false """ print("edit-config - create 'foo and bar' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) edit_config_rpc = """ merge set foo-in ianaift:ethernetCsmacd foo """ print("edit-config - create foo-in ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) edit_config_rpc = """ merge set bar-in ianaift:ethernetCsmacd bar """ print("edit-config - create bar-in ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)!=1) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/xpath-current/run.sh0000775000175000017500000000277214770023131023315 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana-if-type.fxs confdc -c ../test-xpath-current.yang --yangpath /usr/share/yuma/modules/ietf -o test-xpath-current.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & # confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --module=./test-xpath-current.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/test-leaflist-union0000775000175000017500000000005314770023131023170 0ustar vladimirvladimir#!/bin/bash -e cd leaflist-union ./run.sh yuma123_2.14/netconf/test/netconfd/configure.ac0000664000175000017500000000167214770023131021632 0ustar vladimirvladimirAC_INIT([netconfd-test-suite], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules" AC_SUBST(yangdir) PKG_CHECK_MODULES([LIBXML2], [libxml-2.0]) XML_LIBS="$LIBXML2_LIBS" XML_CFLAGS="$LIBXML2_CFLAGS -DLIBXML2_ENABLED" XML_CPPFLAGS="$LIBXML2_CFLAGS -DLIBXML2_ENABLED" AC_SUBST([XML_LIBS]) AC_SUBST([XML_CPPFLAGS]) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1 -DRELEASE=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile multiple-edit-callbacks/Makefile rollback-on-error/Makefile ietf-routing/Makefile ietf-routing-bis/Makefile ietf-interfaces-bis/Makefile ietf-ip-bis/Makefile agt-commit-complete/Makefile val123-api/Makefile anyxml/Makefile ]) AC_OUTPUT yuma123_2.14/netconf/test/netconfd/xpath-derived-from-or-self/0000775000175000017500000000000014770023131024410 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/xpath-derived-from-or-self/session.litenc.py0000664000175000017500000001432114770023131027723 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Demonstrate that derived-from-or-self() works. #Procedure: #1 - Create interfaces foo type=fast-ethernet and bar type=gigabit-ethernet. #2 - Validate fast-ethernet-specific leaf can be commited on foo. #3 - Validate fast-ethernet-specific leaf can not be commited on bar. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") edit_config_rpc = """ merge set """ print("edit-config - delete /interfaces ...") result = conn.rpc(edit_config_rpc) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) edit_config_rpc = """ merge set foo txfd:fast-ethernet bar txfd:other """ print("edit-config - create 'foo' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) edit_config_rpc = """ merge set foo 01:23:45:67:89:AB """ print("edit-config - create 'foo' ethernet-mac ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) edit_config_rpc = """ merge set bar 01:23:45:67:89:AB """ print("edit-config - create 'bar' ethernet-mac ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) #assert(len(ok)==0) commit_rpc = """ """ result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) get_config_rpc = """ """ print("get-config ...") result = conn.rpc(get_config_rpc) fast_ethernet_specific_foo = result.xpath('data/interfaces/interface[name="foo"]/fast-ethernet-specific') fast_ethernet_specific_bar = result.xpath('data/interfaces/interface[name="bar"]/fast-ethernet-specific') assert(len(fast_ethernet_specific_foo)==1) assert(len(fast_ethernet_specific_bar)==0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/xpath-derived-from-or-self/test-xpath-derived-from-or-self.yang0000664000175000017500000000142314770023131033317 0ustar vladimirvladimirmodule test-xpath-derived-from-or-self { yang-version 1.1; namespace "http://yuma123.org/ns/test-xpath-derived-from-or-self"; prefix txdf; import ietf-interfaces { prefix if; } import ietf-yang-types {prefix yang;} organization "yuma123.org"; description "Part of the xpath-derived-from test."; revision 2017-07-15 { description "Initial version"; } identity ethernet { base if:interface-type; } identity other { base if:interface-type; } identity fast-ethernet { base ethernet; } identity gigabit-ethernet { base ethernet; } augment "/if:interfaces/if:interface" { when 'derived-from-or-self(if:type, "txdf:fast-ethernet")'; leaf fast-ethernet-specific { type string; } } } yuma123_2.14/netconf/test/netconfd/xpath-derived-from-or-self/run.sh0000775000175000017500000000221014770023131025546 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cp *.yang tmp cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c test-xpath-derived-from-or-self.yang --yangpath /usr/share/yuma/modules/ietf -o test-xpath-derived-from-or-self.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=./test-xpath-derived-from-or-self.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/test-agt-commit-complete0000775000175000017500000000005714770023131024112 0ustar vladimirvladimir#!/bin/bash -e cd agt-commit-complete ./run.sh yuma123_2.14/netconf/test/netconfd/lock/0000775000175000017500000000000014770023131020266 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/lock/session.yangcli.py0000664000175000017500000001243414770023131023754 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli #from lxml import etree import lxml import yangrpc def yangcli_ok_script(conn, yangcli_script): for line in yangcli_script.splitlines(): line=line.strip() if not line: continue print(("Executing: "+line)) ok = yangcli(conn, line).xpath('./ok') assert(len(ok)==1) def connect(server, port, user, password): conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa", "--dump-session=tmp/nc-session-") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) return conn def lock(conn): result = yangcli(conn, "lock target=candidate") print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==0 or len(ok)==1) if(len(ok)==1): return True else: return False def unlock(conn): result = yangcli(conn, "unlock target=candidate") print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==0 or len(ok)==1) if(len(ok)==1): return True else: return False def commit(conn): result = yangcli(conn, "commit") print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==0 or len(ok)==1) if(len(ok)==1): return True else: return False #Steps follow: def step_1(server, port, user, password): print("#1 - Create 2 sessions.") print("Connecting #1 ...") conn_1 = connect(server=server, port=port, user=user, password=password) print("Connecting #2 ...") conn_2 = connect(server=server, port=port, user=user, password=password) print("Connected ...") return (conn_1, conn_2) def step_2(conn_1,conn_2): print("#2 - Lock candidate from session #1.") assert(lock(conn_1)) def step_3(conn_1,conn_2): print("#3 - Try to lock candidate from session #2. Validate this fails.") assert(not(lock(conn_2))) def step_4(conn_1,conn_2): print("#4 - Try to modify candidate from session #2. Validate this fails.") line="create /interfaces/interface -- name=foo type=ethernetCsmacd" result = yangcli(conn_2, line) print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==0) def step_5(conn_1,conn_2): print("#5 - Modify candidate from session #1 confirm this is OK.") line="create /interfaces/interface -- name=foo type=ethernetCsmacd" result = yangcli(conn_1, line) print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==1) def step_6(conn_1,conn_2): print("#6 - Unlock from #1") assert(unlock(conn_1)) def step_7(conn_1,conn_2): print("#7 - Try to lock candidate from session #2. Validate this fails.") assert(not (lock(conn_2))) def step_8(conn_1,conn_2): print("#8 - Commit") assert(commit(conn_1)) def step_9(conn_1,conn_2): print("#9 - Lock candidate from session #2.") assert(lock(conn_2)) def step_10(conn_1,conn_2): print("#10 - Try to unlock candidate from session #1. Validate this fails.") assert(not (lock(conn_1))) def step_11(conn_1,conn_2): print("#11 - Unlock candidate from session #2.") assert(unlock(conn_2)) def main(): print(""" #Description: Test implementation of RPC rfc6241#section-7.5 #Procedure: #1 - Create 2 sessions. #2 - Lock candidate from session #1. #3 - Try to lock candidate from session #2. Validate this fails. #4 - Try to modify candidate from session #2. Validate this fails. #5 - Modify candidate from session #1 confirm this is OK. #6 - Unlock from #1 #7 - Try to lock candidate from session #2. Validate this fails. #8 - Commit #9 - Lock candidate from session #2. #10 - Try to unlock candidate from session #1. Validate this fails. #11 - Unlock candidate from session #2. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password (conn_1, conn_2)=step_1(server=server, port=port, user=user, password=password) step_2(conn_1,conn_2) step_3(conn_1,conn_2) step_4(conn_1,conn_2) step_5(conn_1,conn_2) step_6(conn_1,conn_2) #step_7(conn_1,conn_2) step_8(conn_1,conn_2) step_9(conn_1,conn_2) step_10(conn_1,conn_2) return 0 time.sleep(1) yangcli_script=''' create /interfaces/interface -- name=eth0 type=ethernetCsmacd description="Uplink to ISP." create /interfaces/interface[name='eth0']/ipv4/address -- ip=192.0.2.1 prefix-length=24 create /interfaces/interface[name='eth0']/ipv4/forwarding value=enabled create /routes/control-plane-protocols/control-plane-protocol -- name=st0 type=static create /routes/control-plane-protocols/control-plane-protocol[name='st0']/static-routes/ipv4/route -- destination-prefix=0.0.0.0/0 next-hop/next-hop-address=192.0.2.2 commit ''' yangcli_ok_script(conn, yangcli_script) return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/lock/session.litenc.py0000664000175000017500000001476214770023131023612 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def connect(server, port, user, password): conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw,strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) return conn_raw def lock(conn): lock_rpc = """ """ result = conn.rpc(lock_rpc) ok = result.xpath('ok') print(lxml.etree.tostring(result)) assert(len(ok)==0 or len(ok)==1) if(len(ok)==1): return True else: return False def unlock(conn): unlock_rpc = """ """ result = conn.rpc(unlock_rpc) ok = result.xpath('ok') print(lxml.etree.tostring(result)) assert(len(ok)==0 or len(ok)==1) if(len(ok)==1): return True else: return False def commit(conn): commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) ok = result.xpath('ok') print(lxml.etree.tostring(result)) assert(len(ok)==0 or len(ok)==1) if(len(ok)==1): return True else: return False #Steps follow: def step_1(server, port, user, password): print("#1 - Create 2 sessions.") print("Connecting #1 ...") conn_raw_1 = connect(server=server, port=port, user=user, password=password) conn_1=litenc_lxml.litenc_lxml(conn_raw_1, strip_namespaces=True) print("Connecting #2 ...") conn_raw_2 = connect(server=server, port=port, user=user, password=password) conn_2=litenc_lxml.litenc_lxml(conn_raw_2, strip_namespaces=True) print("Connected ...") return (conn_raw_1, conn_1, conn_raw_2, conn_2) def step_2(conn_1,conn_2): print("#2 - Lock candidate from session #1.") assert(lock(conn_1)) def step_3(conn_1,conn_2): print("#3 - Try to lock candidate from session #2. Validate this fails.") assert(not(lock(conn_2))) def step_4(conn_1,conn_2): print("#4 - Try to modify candidate from session #2. Validate this fails.") edit_config_rpc = """ merge set foo ianaift:ethernetCsmacd """ print("edit-config - create single 'foo' ...") result = conn_2.rpc(edit_config_rpc) ok = result.xpath('ok') print(result) print(lxml.etree.tostring(result)) assert(len(ok)==0) def step_5(conn_1,conn_2): print("#5 - Modify candidate from session #1 confirm this is OK.") edit_config_rpc = """ merge set foo ianaift:ethernetCsmacd """ print("edit-config - create single 'foo' ...") result = conn_1.rpc(edit_config_rpc) ok = result.xpath('ok') print(result) print(lxml.etree.tostring(result)) assert(len(ok)==1) return def step_6(conn_1,conn_2): print("#6 - Unlock from #1") assert(unlock(conn_1)) def step_7(conn_1,conn_2): print("#7 - Try to lock candidate from session #2. Validate this fails.") assert(not (lock(conn_2))) def step_8(conn_1,conn_2): print("#8 - Commit") assert(commit(conn_1)) def step_9(conn_1,conn_2): print("#9 - Lock candidate from session #2.") assert(lock(conn_2)) def step_10(conn_1,conn_2): print("#10 - Try to unlock candidate from session #1. Validate this fails.") assert(not (lock(conn_1))) def step_11(conn_1,conn_2): print("#11 - Unlock candidate from session #2.") assert(unlock(conn_2)) def main(): print(""" #Description: Test implementation of RPC rfc6241#section-7.5 #Procedure: #1 - Create 2 sessions. #2 - Lock candidate from session #1. #3 - Try to lock candidate from session #2. Validate this fails. #4 - Try to modify candidate from session #2. Validate this fails. #5 - Modify candidate from session #1 confirm this is OK. #6 - Unlock from #1 #7 - Try to lock candidate from session #2. Validate this fails. #8 - Commit #9 - Lock candidate from session #2. #10 - Try to unlock candidate from session #1. Validate this fails. #11 - Unlock candidate from session #2. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password (conn_raw_1, conn_1, conn_raw_2, conn_2)=step_1(server=server, port=port, user=user, password=password) step_2(conn_1,conn_2) step_3(conn_1,conn_2) step_4(conn_1,conn_2) step_5(conn_1,conn_2) step_6(conn_1,conn_2) #step_7(conn_1,conn_2) step_8(conn_1,conn_2) step_9(conn_1,conn_2) step_10(conn_1,conn_2) return 0 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/lock/run.sh0000775000175000017500000000223314770023131021431 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc cd tmp for module in ietf-interfaces@2014-05-08.yang iana-if-type@2014-05-08.yang ; do cp ../../../../modules/ietf/${module} . confdc -c ${module} --yangpath ../../../../ done NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath ${RUN_WITH_CONFD}/etc/confd --addloadpath . & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=../../../modules/ietf/iana-if-type@2014-05-08.yang --module=../../../modules/ietf/ietf-interfaces@2014-05-08.yang --no-startup --superuser=$USER & SERVER_PID=$! fi sleep 4 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID sleep 1 yuma123_2.14/netconf/test/netconfd/xpath-re-match/0000775000175000017500000000000014770023131022160 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/xpath-re-match/session.litenc.py0000664000175000017500000001023514770023131025473 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Demonstrate that re-match works. #Procedure: #1 - Validate 1.22.333 is accepted as /xpath-re-match-test-string value. #2 - Validate a.bb.ccc is not accepted as /xpath-re-match-test-string value.. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw,strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) edit_config_rpc = """ merge set 1.22.333 """ print("edit-config - create 'test-xpath-re-match-string=1.22.333' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) edit_config_rpc = """ merge set """ print("edit-config - delete 'test-xpath-re-match-string' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)==1) edit_config_rpc = """ merge set a.bb.ccc """ print("edit-config - create 'test-xpath-re-match-string=a.bb.ccc' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') print(result) print(ok) print(lxml.etree.tostring(result)) assert(len(ok)==1) print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('//ok') assert(len(ok)!=1) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/xpath-re-match/test-xpath-re-match-concat.yang0000664000175000017500000000107014770023131030102 0ustar vladimirvladimirmodule test-xpath-re-match-concat { yang-version 1.1; namespace "http://yuma123.org/ns/test-xpath-re-match-concat"; prefix trmc; import ietf-interfaces { prefix if; } organization "yuma123"; description "Part of the xpath-re-match test - concat."; revision 2021-06-13 { description "Initial."; } leaf if-special-prefix { type string; } augment "/if:interfaces/if:interface" { leaf if-special-param { type uint32; must 're-match(../if:name, concat(/trmc:if-special-prefix, "\.\d+"))'; } } } yuma123_2.14/netconf/test/netconfd/xpath-re-match/test-xpath-re-match.yang0000664000175000017500000000136014770023131026637 0ustar vladimirvladimirmodule test-xpath-re-match { yang-version 1.1; namespace "http://yuma123.org/ns/test-xpath-re-match"; prefix trm; import ietf-interfaces { prefix if; } organization "example.com"; description "Part of the xpath-re-match test."; revision 2017-07-16 { description "Added non-trivial re-match expression"; } leaf test-xpath-re-match-string { type string; must 're-match("1.22.333","\d{1,3}\.\d{1,3}\.\d{1,3}")'; must 're-match(.,"\d{1,3}\.\d{1,3}\.\d{1,3}")'; must 're-match(current(),"\d{1,3}\.\d{1,3}\.\d{1,3}")'; } augment "/if:interfaces/if:interface" { leaf count-eth0 { type uint32; must '/if:interfaces/if:interface[re-match(if:name, "eth0\.\d+")]'; } } } yuma123_2.14/netconf/test/netconfd/xpath-re-match/run.sh0000775000175000017500000000353614770023131023332 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana-if-type.fxs if [[ $# -eq 0 ]] ; then confdc -c ../test-xpath-re-match.yang --yangpath /usr/share/yuma/modules/ietf -o test-xpath-re-match.fxs elif [[ "$1" -eq "concat" ]] ; then confdc -c ../test-xpath-re-match-concat.yang --yangpath /usr/share/yuma/modules/ietf -o test-xpath-re-match-concat.fxs fi NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & # confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true if [[ $# -eq 0 ]] ; then /usr/sbin/netconfd --module=./test-xpath-re-match.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & elif [[ "$1" -eq "concat" ]] ; then /usr/sbin/netconfd --module=./test-xpath-re-match-concat.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & fi SERVER_PID=$! fi sleep 3 if [[ $# -eq 0 ]] ; then python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD fi kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/test-ietf-netmod-sub-intf-vlan-model0000775000175000017500000000007314770023131026235 0ustar vladimirvladimir#!/bin/bash -e cd ietf-netmod-sub-intf-vlan-model ./run.sh yuma123_2.14/netconf/test/netconfd/yang-push/0000775000175000017500000000000014770023131021251 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-push/run.sh0000775000175000017500000000342414770023131022417 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp cd tmp for draft in draft-ietf-netconf-yang-push-15 draft-ietf-netconf-subscribed-notifications-10 draft-ietf-netconf-netconf-event-notifications-08 ; do wget https://tools.ietf.org/id/${draft}.txt rfcstrip ${draft}.txt done ln -sh ietf-subscribed-notifications@2018-02-23.yang ietf-subscribed-notifications.yang pyang --ietf -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ ietf-subscribed-notifications.yang pyang --ietf -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ ietf-yang-push.yang if [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc for module in ietf-yang-push.yang ietf-subscribed-notifications.yang ; do confdc -c ${module} --yangpath .:/usr/share/yuma/modules done NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath ${RUN_WITH_CONFD}/etc/confd --addloadpath . SERVER_PID=$! cd .. else killall -KILL netconfd || true rm ncxserver.sock || true /usr/sbin/netconfd --modpath=.:/usr/share/yuma/modules --module=./ietf-subscribed-notifications.yang --module=./ietf-yang-push.yang --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --modpath=.:/usr/share/yuma/modules --module=./ietf-subscribed-notifications.yang --module=./ietf-yang-push.yang --no-startup --superuser=$USER 2>&1 1>server.log & SERVER_PID=$! cd .. fi sleep 3 python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/mininet/0000775000175000017500000000000014770023131021001 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/mininet/README0000664000175000017500000000303714770023131021664 0ustar vladimirvladimirThis testcase demonstrates the use of netconfd for management of nodes in a network environment. Usage: sudo ./run.py mininet is used to create network with 3 type of nodes: * hosts (h0,h1) ietf-interfaces (statistics), ietf-traffic-generator, ietf-traffic-analyzer * bridges (b0,b1) ietf-interfaces (statistics), ietf-network-bridge (OpenFlow+openvswitch implementation) * routers (none so far) ietf-interfaces (statistics), ietf-routing (Unix cli tools route,ip implementation) according to topology.xml and start netconfd instances to manage each with relevant set of module implementations provided as yuma123 example-modules. h0-eth0 b0-eth0 b0-eth1 b1-eth0 b1-eth1 h1-eth0 +--------+ +--------+ +--------+ +--------+ | h0 |--------| b0 |--------| b1 |--------| h1 | +--------+ +--------+ +--------+ +--------+ The NETCONF server endpoint parameters for access to each of the nodes are formally specified in the topology.xml and are: * h0 localhost:8830 * h1 localhost:8831 * b0 localhost:8832 * b1 localhost:8833 All NETCONF server instances are started with root as superuser Your controller/client script can connect and manage the nodes using NETCONF once the network is created. Dependencies: * yuma123 installed (with ietf-interface and ietf-network-bridge example SILs) * tntapi for client side python scripting (optional) * since mininet requires root priviliges authorized local ssh key for root (root@host:~# ssh root@localhost #should work without password) yuma123_2.14/netconf/test/netconfd/mininet/topology.xml0000664000175000017500000001024614770023131023402 0ustar vladimirvladimir example b0 b0-eth0 b0-eth1 root localhost 8830 b1 b1-eth0 b1-eth1 root localhost 8831 h0 h0-eth0 root localhost 8832 h1 h1-eth0 root localhost 8833 b0 b0-eth1 b1 b1-eth0 b0-s1 b1 b1-eth0 b0 b0-eth1 b1-b0 h0 h0-eth0 b0 b0-eth0 h0-b0 b0 b0-eth0 h0 h0-eth0 b0-h0 h1 h1-eth0 b1 b1-eth1 h1-b1 b1 b1-eth1 h1 h1-eth0 b1-h1 yuma123_2.14/netconf/test/netconfd/mininet/run-netconfd0000775000175000017500000000111314770023131023325 0ustar vladimirvladimir#!/bin/sh -e port=830 ncxserver_sockname=/tmp/ncxserver.sock args=$@ while [ $# -gt 0 ]; do case "$1" in --port=*) port="${1#*=}" ;; --ncxserver-sockname=*) ncxserver_sockname="${1#*=}" ;; esac shift done sshd_cfg_filename=${ncxserver_sockname}.sshd_config kill -KILL `fuser $ncxserver_sockname 2>/dev/null` || true kill -KILL `fuser --ipv4 -v -n tcp $port 2>/dev/null` || true rm $ncxserver_sockname $sshd_cfg_filename || true #run dedicated sshd ./run-sshd-for-netconfd $port $ncxserver_sockname $sshd_cfg_filename /usr/sbin/netconfd $args yuma123_2.14/netconf/test/netconfd/mininet/run-sshd-for-netconfd0000775000175000017500000000137514770023131025062 0ustar vladimirvladimir#!/bin/sh -e # run_sshd_for_netconfd starts dedicated sshd instance for specified netconfd netconf subsystem unix socket #Usage: run_sshd_for_netconfd #Example: run_sshd_for_netconfd 8830 /tmp/ncxserver.8830.sock /tmp/ncxserver.8830.sshd.config ssh_port=$1 netconf_subsystem_unix_socket=$2 sshd_cfg_filename=$3 cat > $sshd_cfg_filename << EOF ChallengeResponseAuthentication no UsePAM yes AcceptEnv LANG LC_* Subsystem sftp /usr/lib/openssh/sftp-server PermitRootLogin yes Port ${ssh_port} Subsystem netconf "/usr/sbin/netconf-subsystem --ncxserver-sockname=${ssh_port}@${netconf_subsystem_unix_socket}" EOF #ForceCommand /usr/sbin/netconf-subsystem-${port} /usr/sbin/sshd -f $sshd_cfg_filename yuma123_2.14/netconf/test/netconfd/mininet/run.py0000775000175000017500000001512114770023131022162 0ustar vladimirvladimir#!/usr/bin/python """ Build a simple network from scratch according to topology.xml. eth0 eth0 eth1 eth0 eth1 eth0 +----+ +----+ +----+ +----+ | h0 |------| b0 |------| b1 |------| h1 | +----+ +----+ +----+ +----+ netconfd instance is started on each node with modules according to the node type. Node types: * h - host (--module=ietf-interfaces --module=ietf-traffic-generator --module=ietf-traffic-analyzer) * b - bridge (--module=ietf-network-bridge-openflow) * r - router (--module=ietf-interfaces --module=ietf-ip --module=ietf-routing) After the network is started configuration is commited and connectivity tested. """ from mininet.net import Mininet from mininet.node import Node from mininet.link import Link from mininet.log import setLogLevel, info from mininet.util import quietRun from time import sleep import tntapi import lxml from lxml import etree namespaces={"nc":"urn:ietf:params:xml:ns:netconf:base:1.0", "nd":"urn:ietf:params:xml:ns:yang:ietf-network", "nt":"urn:ietf:params:xml:ns:yang:ietf-network-topology"} def netconfNet(): "Create network from scratch using netconfd instance on each node." info( "*** Creating nodes\n" ) h0 = Node( 'h0', inNamespace=True) h1 = Node( 'h1', inNamespace=True) b0 = Node( 'b0', inNamespace=False) b1 = Node( 'b1', inNamespace=False) ncproxy = Node( 'ncproxy', inNamespace=False) info( "*** Creating links\n" ) b0.cmd("ip link del b0-eth0") b0.cmd("ip link del b0-eth1") b1.cmd("ip link del b1-eth0") b1.cmd("ip link del b1-eth1") Link( h0, b0, intfName2="b0-eth0") Link( b0, b1, intfName1="b0-eth1", intfName2="b1-eth0") Link( h1, b1, intfName2="b1-eth1") info( "*** Configuring hosts\n" ) h0.setIP( '192.168.123.1/24' ) h1.setIP( '192.168.123.2/24' ) info( str( h0 ) + '\n' ) info( str( h1 ) + '\n' ) info( str( b0 ) + '\n' ) info( str( b1 ) + '\n' ) info( "*** Starting network\n" ) h0.cmd( '''INTERFACE_NAME_PREFIX=h0- ./run-netconfd --module=ietf-interfaces --no-startup --port=8832 --ncxserver-sockname=/tmp/ncxserver.8832.sock --superuser=${USER} &''' ) ncproxy.cmd( '''./run-sshd-for-netconfd --port=8832 --ncxserver-sockname=/tmp/ncxserver.8832.sock &''' ) h1.cmd( '''INTERFACE_NAME_PREFIX=h1- ./run-netconfd --module=ietf-interfaces --no-startup --port=8833 --ncxserver-sockname=/tmp/ncxserver.8833.sock --superuser=${USER} &''' ) ncproxy.cmd( '''./run-sshd-for-netconfd --port=8833 --ncxserver-sockname=/tmp/ncxserver.8833.sock &''' ) #bridge b0 b0.cmd( '''VCONN_ARG=ptcp:16636 ./run-netconfd --module=ietf-network-bridge-openflow --no-startup --superuser=${USER} --port=8830 --ncxserver-sockname=/tmp/ncxserver.8830.sock &''' ) b0.cmd( 'ovs-vsctl del-br dp0' ) b0.cmd( 'ovs-vsctl add-br dp0' ) for intf in list(b0.intfs.values()): print("Setting: "+ str(intf)) print(b0.cmd( 'ovs-vsctl add-port dp0 %s' % intf )) print(b0.cmd( 'ifconfig -a' )) # Note: controller and switch are in root namespace, and we # can connect via loopback interface b0.cmd( 'ovs-vsctl set-controller dp0 tcp:127.0.0.1:16636' ) info( '*** Waiting for switch to connect to controller' ) while 'is_connected' not in quietRun( 'ovs-vsctl show' ): sleep( 1 ) info( '.' ) info( '\n' ) #bridge b1 b1.cmd( '''VCONN_ARG=ptcp:16635 ./run-netconfd --module=ietf-network-bridge-openflow --no-startup --superuser=${USER} --port=8831 --ncxserver-sockname=/tmp/ncxserver.8831.sock &''' ) b1.cmd( 'ovs-vsctl del-br dp1' ) b1.cmd( 'ovs-vsctl add-br dp1' ) for intf in list(b1.intfs.values()): print("Setting: "+ str(intf)) print(b1.cmd( 'ovs-vsctl add-port dp1 %s' % intf )) print(b1.cmd( 'ifconfig -a' )) # Note: controller and switch are in root namespace, and we # can connect via loopback interface b1.cmd( 'ovs-vsctl set-controller dp1 tcp:127.0.0.1:16635' ) sleep(10) input("Press Enter to continue...") tree=etree.parse("topology.xml") network = tree.xpath('/nc:config/nd:networks/nd:network', namespaces=namespaces)[0] conns = tntapi.network_connect(network) yconns = tntapi.network_connect_yangrpc(network) mylinks = tntapi.parse_network_links(network) print("Done") state_before = tntapi.network_get_state(network, conns) info( "*** Running test\n" ) #create flows configuration yangcli_script=""" merge /bridge/ports/port -- name=b0-eth0 merge /interfaces/interface -- name=b0-eth0 type=ethernetCsmacd port-name=b0-eth0 merge /bridge/ports/port -- name=b0-eth1 merge /interfaces/interface -- name=b0-eth1 type=ethernetCsmacd port-name=b0-eth1 create /flows/flow[id='h0-to-h1'] -- match/in-port=b0-eth0 actions/action[order='0']/output-action/out-port=b0-eth1 create /flows/flow[id='h1-to-h0'] -- match/in-port=b0-eth1 actions/action[order='0']/output-action/out-port=b0-eth0 """ result=tntapi.yangcli_ok_script(yconns["b0"],yangcli_script) yangcli_script=""" merge /bridge/ports/port -- name=b1-eth0 merge /interfaces/interface -- name=b1-eth0 type=ethernetCsmacd port-name=b1-eth0 merge /bridge/ports/port -- name=b1-eth1 merge /interfaces/interface -- name=b1-eth1 type=ethernetCsmacd port-name=b1-eth1 create /flows/flow[id='h0-to-h1'] -- match/in-port=b1-eth0 actions/action[order='0']/output-action/out-port=b1-eth1 create /flows/flow[id='h1-to-h0'] -- match/in-port=b1-eth1 actions/action[order='0']/output-action/out-port=b1-eth0 """ result=tntapi.yangcli_ok_script(yconns["b1"],yangcli_script) tntapi.network_commit(conns) # The commit should cause the netconfd server to execute the OpenFlow equivalent of: #switch.cmd( 'ovs-ofctl add-flow dp0 in_port=2,actions=output:1' ) #switch.cmd( 'ovs-ofctl add-flow dp0 in_port=1,actions=output:2' ) b0.cmdPrint( 'ovs-ofctl dump-flows dp0' ) info( "*** Running test\n" ) h0.cmdPrint( 'ping -I h0-eth0 -c10 ' + h1.IP() ) b0.cmdPrint( 'ovs-ofctl dump-flows dp0' ) state_after = tntapi.network_get_state(network, conns) #delta = tntapi.get_network_counters_delta(state_before,state_after) tntapi.print_state_ietf_interfaces_statistics_delta(network, state_before, state_after) input("Press Enter to continue...") info( "*** Stopping network\n" ) b0.cmd( 'killall -KILL netconfd' ) b0.cmd( 'ovs-vsctl del-br dp0' ) b0.deleteIntfs() b1.cmd( 'killall -KILL netconfd' ) b1.cmd( 'ovs-vsctl del-br dp1' ) b1.deleteIntfs() info( '\n' ) if __name__ == '__main__': setLogLevel( 'info' ) info( '*** NETCONF network demo\n' ) Mininet.init() netconfNet() yuma123_2.14/netconf/test/netconfd/yang-library-submodules/0000775000175000017500000000000014770023131024116 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-library-submodules/README0000664000175000017500000000005114770023131024772 0ustar vladimirvladimirTest yang-library support of submodules. yuma123_2.14/netconf/test/netconfd/yang-library-submodules/test-yang-library-submodules.yang0000664000175000017500000000060414770023131032533 0ustar vladimirvladimirmodule test-yang-library-submodules { namespace "http://yuma123.org/ns/test-yang-library-submodules"; prefix tyls; include test-yang-library-submodules-sub1; organization "yuma123.org"; description "Part of the yang-library-submodules test."; revision 2018-07-27 { description "Initial version."; } container top { uses tyls:grouping-bar; } } yuma123_2.14/netconf/test/netconfd/yang-library-submodules/session.litenc.py0000664000175000017500000000536214770023131027436 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Demonstrate that ietf-yang-library is implemented. #Procedure: #1 - Verify /modules-state/module[name='test-yang-library-submodules'] container is present. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) # receive result=conn.receive() print("#1 - Verify /modules-state/module[name='test-yang-library-submodules'] container is present.") get_rpc = """ test-yang-library-submodules """ print("get ...") result = conn.rpc(get_rpc) print(lxml.etree.tostring(result)) name = result.xpath('data/modules-state/module/name') assert(len(name)==1) namespace = result.xpath('data/modules-state/module/namespace') assert(len(namespace)==1) assert(namespace[0].text=="http://yuma123.org/ns/test-yang-library-submodules") conformance_type = result.xpath('data/modules-state/module/conformance-type') assert(len(conformance_type)==1) submodule_name = result.xpath('data/modules-state/module/submodule/name') assert(len(submodule_name)==1) assert(submodule_name[0].text=="test-yang-library-submodules-sub1") return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/yang-library-submodules/test-yang-library-submodules-sub1.yang0000664000175000017500000000037614770023131033411 0ustar vladimirvladimirsubmodule test-yang-library-submodules-sub1 { belongs-to test-yang-library-submodules { prefix tyls; } revision 2018-07-27 { description "Initial version."; } grouping grouping-bar { leaf bar { type string; } } } yuma123_2.14/netconf/test/netconfd/yang-library-submodules/run.sh0000775000175000017500000000206414770023131025263 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cp *.yang tmp cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c test-yang-library-submodules.yang --yangpath . -o test-yang-library.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=./test-yang-library-submodules.yang --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD #python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/test-identityref-submodule0000775000175000017500000000006114770023131024561 0ustar vladimirvladimir#!/bin/bash -e cd identityref-submodule ./run.sh yuma123_2.14/netconf/test/netconfd/deviation-replace-type/0000775000175000017500000000000014770023131023710 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/deviation-replace-type/test-deviation-replace-type.sh0000775000175000017500000000070114770023131031574 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp mkdir tmp killall -KILL netconfd || true rm -f /tmp/ncxserver.sock valgrind -- /usr/sbin/netconfd \ --deviation=./test-deviation-replace-type.yang \ --module=ietf-interfaces \ --module=ietf-ip@2014-06-16 \ --no-startup \ --superuser=$USER \ 2>tmp/error.log \ 1>tmp/server.log & SERVER=$! sleep 5 kill %1 wait cat tmp/server.log if grep -q "==${SERVER}==.*typ_clean_typdef" tmp/error.log ; then exit 1 fi exit 0 yuma123_2.14/netconf/test/netconfd/deviation-replace-type/test-deviation-replace-type.yang0000664000175000017500000000107214770023131032117 0ustar vladimirvladimirmodule test-deviation-replace-type { yang-version 1.1; namespace "urn:labn:params:xml:ns:yang:test-deviation-replace-type"; prefix devext; import ietf-interfaces { prefix if; } import ietf-ip { prefix ip; } organization ""; contact ""; description "Create an external deviation that replaces the type of an object that exists due to an augment. Run from valgrind and check for invalid reads/writes."; revision 2021-01-15 { reference ""; } deviation /if:interfaces/if:interface/ip:ipv4/ip:mtu { deviate replace { type uint32; } } } yuma123_2.14/netconf/test/netconfd/rollback-on-error/0000775000175000017500000000000014770023131022670 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/rollback-on-error/session.py0000664000175000017500000000172414770023131024731 0ustar vladimirvladimirfrom yangcli.yangcli import yangcli from lxml import etree import yangrpc import sys import os conn = yangrpc.connect("127.0.0.1", 830, os.getenv('USER'), None, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa",) if(conn==None): print("Error: yangrpc failed to connect!") sys.exit(1) result=yangcli(conn, "commit") assert(result.xpath('./ok')) yangcli(conn, "replace /system/hostname value='ok3'") yangcli(conn, "replace /system/location value='ok4'") result=yangcli(conn, "commit") assert(result.xpath('./ok')) yangcli(conn, "replace /system/hostname value='error1'") yangcli(conn, "replace /system/location value='ok5'") result=yangcli(conn, "commit") assert(result.xpath('./rpc-error')) yangcli(conn, "discard-changes") yangcli(conn, "replace /system/hostname value='ok6'") yangcli(conn, "replace /system/location value='error2'") result=yangcli(conn, "commit") assert(result.xpath('./rpc-error')) yangcli(conn, "discard-changes") print("Done.") yuma123_2.14/netconf/test/netconfd/rollback-on-error/startup-cfg.xml0000664000175000017500000000035314770023131025652 0ustar vladimirvladimir ok1 ok2 yuma123_2.14/netconf/test/netconfd/rollback-on-error/README0000664000175000017500000000066114770023131023553 0ustar vladimirvladimirFILES: * run.sh - shell script executing the testcase * session.py - python script connecting to the started netconfd server * startup-cfg.xml - initial configuration * rollback-on-error.c - SIL module test implementation based on ietf-system.yang PURPOSE: Verify edit callbacks in case of supported rollback-on-error are correct OPERATION: Initiates commit with some edit callbacks failing and logs the edit callback calls. yuma123_2.14/netconf/test/netconfd/rollback-on-error/rollback-on-error.c0000664000175000017500000001132414770023131026367 0ustar vladimirvladimir/* module test-rollback-on-error(based on ietf-system.yang) */ #define __USE_XOPEN 1 //#define _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" /* module static variables */ static ncx_module_t *ietf_system_mod; static status_t edit_cb ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; char* editop_str=NULL; char* cbtyp_str=NULL; char* newval_str=NULL; char* curval_str=NULL; res = NO_ERR; errorval = NULL; errorstr = NULL; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ cbtyp_str = "AGT_CB_VALIDATE"; break; case AGT_CB_APPLY: /* database manipulation done here */ cbtyp_str = "AGT_CB_APPLY"; break; case AGT_CB_COMMIT: /* device instrumentation done here */ cbtyp_str = "AGT_CB_COMMIT"; break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ cbtyp_str = "AGT_CB_ROLLBACK"; break; default: cbtyp_str = "AGT_CB_UNKNOWN!"; res = SET_ERROR(ERR_INTERNAL_VAL); } switch (editop) { case OP_EDITOP_DELETE: editop_str = "OP_EDITOP_DELETE"; break; case OP_EDITOP_LOAD: editop_str = "OP_EDITOP_LOAD"; break; case OP_EDITOP_MERGE: editop_str = "OP_EDITOP_MERGE"; break; case OP_EDITOP_REPLACE: editop_str = "OP_EDITOP_REPLACE"; break; case OP_EDITOP_CREATE: editop_str = "OP_EDITOP_CREATE"; break; } if(cbtyp==AGT_CB_COMMIT) { if(newval!=NULL && obj_has_text_content(newval->obj)) { int ret; ret = !memcmp("error",VAL_STRING(newval),(strlen("error")>strlen(VAL_STRING(newval)))?strlen(VAL_STRING(newval)):strlen("error")); if(ret != 0) { errorval=newval; errorstr="For the sake of testing all text values that start with \"error\" cause commit error!"; res = SET_ERROR(ERR_INTERNAL_VAL); } } } if(newval!=NULL) { val_make_serialized_string (newval, NCX_DISPLAY_MODE_XML, &newval_str); } if(curval!=NULL) { val_make_serialized_string (curval, NCX_DISPLAY_MODE_XML, &curval_str); } printf("%s%s%s%s\n", cbtyp_str, editop_str, newval?newval_str:"NULL", curval?curval_str:"NULL"); if(newval_str!=NULL) { free(newval_str); } if(curval_str!=NULL) { free(curval_str); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* The 3 mandatory callback functions: y_ietf_system_init, y_ietf_system_init2, y_ietf_system_cleanup */ status_t y_test_rollback_on_error_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-system", NULL, &agt_profile->agt_savedevQ, &ietf_system_mod); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( "ietf-system", (const xmlChar *)"/system", (const xmlChar *)NULL /*"YYYY-MM-DD"*/, edit_cb); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( "ietf-system", (const xmlChar *)"/system/hostname", (const xmlChar *)NULL /*"YYYY-MM-DD"*/, edit_cb); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( "ietf-system", (const xmlChar *)"/system/location", (const xmlChar *)NULL /*"YYYY-MM-DD"*/, edit_cb); if (res != NO_ERR) { return res; } return res; } status_t y_test_rollback_on_error_init2(void) { return NO_ERR; } void y_test_rollback_on_error_cleanup (void) { } yuma123_2.14/netconf/test/netconfd/rollback-on-error/Makefile.am0000775000175000017500000000051214770023131024725 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libtest-rollback-on-error.la libtest_rollback_on_error_la_SOURCES = rollback-on-error.c libtest_rollback_on_error_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libtest_rollback_on_error_la_LDFLAGS = -module -lyumaagt -lyumancx yuma123_2.14/netconf/test/netconfd/rollback-on-error/run.sh0000775000175000017500000000052014770023131024030 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp cp startup-cfg.xml tmp rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=test-rollback-on-error --startup=tmp/startup-cfg.xml --superuser=$USER 1>tmp/netconfd.stdout 2>tmp/netconfd.stderr & NETCONFD_PID=$! sleep 1 python session.py kill $NETCONFD_PID cat tmp/netconfd.stdout sleep 1 yuma123_2.14/netconf/test/netconfd/ietf-routing-bis/0000775000175000017500000000000014770023131022525 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/ietf-routing-bis/session.yangcli.py0000664000175000017500000000510614770023131026211 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli import lxml from lxml import etree import yangrpc def yangcli_ok_script(conn, yangcli_script): for line in yangcli_script.splitlines(): line=line.strip() if not line: continue print("Executing: "+line) ok = yangcli(conn, line).xpath('./ok') assert(len(ok)==1) def main(): print(""" #Description: Usecase fo ietf-routing module. #Procedure: #1 - Create interface "eth0" of type=ethernetCsmacd with static ip=10.0.0.2/24 and static default route for destination-prefix=0.0.0.0/0 and next-hop=10.0.0.1. #2 - Validate returns identical result as the example in RFC8022 Appendix D. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa", "--dump-session=tmp/nc-session-") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) time.sleep(1) result = yangcli(conn, "delete /interfaces") result = yangcli(conn, "commit") ok = result.xpath('./ok') assert(len(ok)==1) yangcli_script=''' create /interfaces/interface -- name=eth0 type=ethernetCsmacd description="Uplink to ISP." create /interfaces/interface[name='eth0']/ipv4/address -- ip=192.0.2.1 prefix-length=24 commit ''' #create /interfaces/interface[name='eth0']/ipv4/forwarding value=true #create /routes/control-plane-protocols/control-plane-protocol -- name=st0 type=static #create /routes/control-plane-protocols/control-plane-protocol[name='st0']/static-routes/ipv4/route -- destination-prefix=0.0.0.0/0 next-hop/next-hop-address=192.0.2.2 #commit #''' yangcli_ok_script(conn, yangcli_script) result = yangcli(conn, "get-data with-origin datastore=operational xpath-filter="/" ) print lxml.etree.tostring(result) return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/ietf-routing-bis/session.litenc.py0000664000175000017500000004521614770023131026047 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml from lxml import etree #from litenc import strip_namespaces from io import StringIO import argparse from operator import attrgetter def json2xml(json_config): #TODO return "" def strip_namespaces(tree): xslt=''' ''' xslt_doc = lxml.etree.fromstring(xslt) transform = lxml.etree.XSLT(xslt_doc) tree = transform(tree) return tree # Hmm .. ?! def yang_data_equal(e1, e2): print(e1.tag) print(e2.tag) if e1.tag != e2.tag: assert(False) return False if e1.text==None and e2.text==None: print("empty container") else: if e1.text==None and "" != e2.text.strip(): assert(False) return False if e2.text==None and "" != e1.text.strip(): assert(False) return False if e1.text!=None and e2.text!=None and e1.text.strip() != e2.text.strip(): assert(False) return False #if e1.tail != e2.tail: assert(False) return False if e1.attrib != e2.attrib: print((e1.attrib)) print((e2.attrib)) assert(False) print("diff attrib") return False if len(e1) != len(e2): print(e1) print(len(e1)) print(e2) print(len(e2)) assert(False) return False for node in e1.findall("*"): # searching top-level nodes only: node1, node2 ... node[:] = sorted(node, key=attrgetter("tag")) for node in e2.findall("*"): # searching top-level nodes only: node1, node2 ... node[:] = sorted(node, key=attrgetter("tag")) for c1,c2 in zip(e1,e2): print(c1) print(c2) print("---") yang_data_equal(c1, c2) return 1 #return all(yang_data_equal(c1, c2) for c1, c2 in sorted(zip(e1, e2))) def main(): print(""" #Description: Testcase for draft-ietf-netmod-rfc8022bis-04 ietf-routing module. #Procedure: #1 - configuration as in rfc8022bis Appendix D. #2 - the /interfaces and /routing containers and verify data is same as in rfc8022bis Appendix E. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw,strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") get_yang_library_rpc = """ """ print(" - /ietf-yang-library:modules-state ...") result = conn.rpc(get_yang_library_rpc, strip_ns=False) print(lxml.etree.tostring(result, pretty_print=True, inclusive_ns_prefixes=True)) namespaces = {"nc":"urn:ietf:params:xml:ns:netconf:base:1.0"} data = result.xpath('./nc:data', namespaces=namespaces) assert(len(data)==1) #Copied from draft-ietf-netmod-rfc8022bis-00 Appendix E. json_config = """ { "ietf-interfaces:interfaces": { "interface": [ { "name": "eth0", "type": "iana-if-type:ethernetCsmacd", "description": "Uplink to ISP.", "phys-address": "00:0C:42:E5:B1:E9", "oper-status": "up", "statistics": { "discontinuity-time": "2015-10-24T17:11:27+02:00" }, "ietf-ip:ipv4": { "forwarding": true, "mtu": 1500, "address": [ { "ip": "192.0.2.1", "prefix-length": 24 } ], }, "ietf-ip:ipv6": { "forwarding": true, "mtu": 1500, "address": [ { "ip": "2001:0db8:0:1::1", "prefix-length": 64 } ], "autoconf": { "create-global-addresses": false } "ietf-ipv6-unicast-routing: ipv6-router-advertisements": { "send-advertisements": false } } }, { "name": "eth1", "type": "iana-if-type:ethernetCsmacd", "description": "Interface to the internal network.", "phys-address": "00:0C:42:E5:B1:EA", "oper-status": "up", "statistics": { "discontinuity-time": "2015-10-24T17:11:29+02:00" }, "ietf-ip:ipv4": { "forwarding": true, "mtu": 1500, "address": [ { "ip": "198.51.100.1", "prefix-length": 24 } ], }, "ietf-ip:ipv6": { "forwarding": true, "mtu": 1500, "address": [ { "ip": "2001:0db8:0:2::1", "prefix-length": 64 } ], "autoconf": { "create-global-addresses": false }, "ietf-ipv6-unicast-routing: ipv6-router-advertisements": { "send-advertisements": true, "prefix-list": { "prefix": [ { "prefix-spec": "2001:db8:0:2::/64" } ] } } } } ] }, "ietf-routing:routing": { "router-id": "192.0.2.1", "control-plane-protocols": { "control-plane-protocol": [ { "type": "ietf-routing:static", "name": "st0", "description": "Static routing is used for the internal network.", "static-routes": { "ietf-ipv4-unicast-routing:ipv4": { "route": [ { "destination-prefix": "0.0.0.0/0", "next-hop": { "next-hop-address": "192.0.2.2" } } ] }, "ietf-ipv6-unicast-routing:ipv6": { "route": [ { "destination-prefix": "::/0", "next-hop": { "next-hop-address": "2001:db8:0:1::2" } } ] } } } ] } "ribs": { "rib": [ { "name": "ipv4-master", "address-family": "ietf-ipv4-unicast-routing:ipv4-unicast", "default-rib": true, "routes": { "route": [ { "ietf-ipv4-unicast-routing:destination-prefix": "192.0.2.1/24", "next-hop": { "outgoing-interface": "eth0" }, "route-preference": 0, "source-protocol": "ietf-routing:direct", "last-updated": "2015-10-24T17:11:27+02:00" }, { "ietf-ipv4-unicast-routing:destination-prefix": "198.51.100.0/24", "next-hop": { "outgoing-interface": "eth1" }, "source-protocol": "ietf-routing:direct", "route-preference": 0, "last-updated": "2015-10-24T17:11:27+02:00" }, { "ietf-ipv4-unicast-routing:destination-prefix": "0.0.0.0/0", "source-protocol": "ietf-routing:static", "route-preference": 5, "next-hop": { "ietf-ipv4-unicast-routing:next-hop-address": "192.0.2.2" }, "last-updated": "2015-10-24T18:02:45+02:00" } ] } }, { "name": "ipv6-master", "address-family": "ietf-ipv6-unicast-routing:ipv6-unicast", "default-rib": true, "routes": { "route": [ { "ietf-ipv6-unicast-routing:destination-prefix": "2001:db8:0:1::/64", "next-hop": { "outgoing-interface": "eth0" }, "source-protocol": "ietf-routing:direct", "route-preference": 0, "last-updated": "2015-10-24T17:11:27+02:00" }, { "ietf-ipv6-unicast-routing:destination-prefix": "2001:db8:0:2::/64", "next-hop": { "outgoing-interface": "eth1" }, "source-protocol": "ietf-routing:direct", "route-preference": 0, "last-updated": "2015-10-24T17:11:27+02:00" }, { "ietf-ipv6-unicast-routing:destination-prefix": "::/0", "next-hop": { "ietf-ipv6-unicast-routing:next-hop-address": "2001:db8:0:1::2" }, "source-protocol": "ietf-routing:static", "route-preference": 5, "last-updated": "2015-10-24T18:02:45+02:00" } ] } } ] } }, } """ edit_config_rpc = """ merge set %(xml_config) """ % {'xml_config':json2xml(json_config)} print(" - load Appendix D. example config to 'candidate' ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==1) commit_rpc = '' print(" - commit example config ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('./ok') assert(len(ok)==1) get_example_data_rpc = """ ds:operational """ print(" - Appendix E. data ...") result = conn.rpc(get_example_data_rpc, strip_ns=False) print(lxml.etree.tostring(result, pretty_print=True, inclusive_ns_prefixes=True)) namespaces = {"ncds":"urn:ietf:params:xml:ns:yang:ietf-netconf-datastores"} data = result.xpath('./ncds:data', namespaces=namespaces) assert(len(data)==1) expected=""" 192.0.2.1 ietf-routing:static 0.0.0.0/0 192.0.2.2 ::/0 2001:db8:0:1::2 ipv4-master ietf-ipv4-unicast-routing:ipv4-unicast true 192.0.2.1/24 eth0 0 ietf-routing:direct 2015-10-24T17:11:27+02:00 98.51.100.0/24 eth1 0 ietf-routing:direct 2015-10-24T17:11:27+02:00 0.0.0.0/0 192.0.2.2 5 ietf-routing:static 2015-10-24T18:02:45+02:00 ipv6-master ietf-ipv6-unicast-routing:ipv6-unicast true 2001:db8:0:1::/64 eth0 0 ietf-routing:direct 2015-10-24T17:11:27+02:00 2001:db8:0:2::/64 eth1 0 ietf-routing:direct 2015-10-24T17:11:27+02:00 ::/0 2001:db8:0:1::2 5 ietf-routing:static 2015-10-24T18:02:45+02:00 """ expected = lxml.etree.fromstring(expected) #strip comments comments = expected.xpath('//comment()') for c in comments: p = c.getparent() p.remove(c) #strip namespaces data1 = expected.xpath('.',namespaces=namespaces) data_expected=strip_namespaces(data1[0]) data_received=strip_namespaces(data[0]) #sort schema lists by key in alphabetical key order - hardcoded /interfaces/interface[name] for node in data_expected.findall("./interfaces"): node[:] = sorted(node, key=lambda child: get_interface_name(child)) for node in data_received.findall("./interfaces"): node[:] = sorted(node, key=lambda child: get_interface_name(child)) #sort attributes a = StringIO(); b = StringIO(); data_expected.write_c14n(a) data_received.write_c14n(b) print("Expected:") print((a.getvalue())) print("Received:") print((b.getvalue())) if yang_data_equal(lxml.etree.fromstring(a.getvalue()), lxml.etree.fromstring(b.getvalue())): print("Bingo!") return 0 else: print("Error: YANG data not equal!") return 1 sys.exit(main()) yuma123_2.14/netconf/test/netconfd/ietf-routing-bis/Makefile.am0000775000175000017500000000051414770023131024564 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libtest-ietf-routing-bis.la libtest_ietf_routing_bis_la_SOURCES = \ test-ietf-routing-bis.c libtest_ietf_routing_bis_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libtest_ietf_routing_bis_la_LDFLAGS = -module -lyumaagt -lyumancx yuma123_2.14/netconf/test/netconfd/ietf-routing-bis/run.sh0000775000175000017500000000467214770023131023701 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp cd tmp wget https://www.rfc-editor.org/rfc/rfc8343.txt wget https://www.rfc-editor.org/rfc/rfc8344.txt wget https://www.rfc-editor.org/rfc/rfc8349.txt rfcstrip rfc8343.txt rfcstrip rfc8344.txt rfcstrip rfc8349.txt pyang --ietf -f tree --path /usr/share/yuma/nmda-modules/ietf:/usr/share/yuma/modules/ietf ietf-interfaces@2018-02-20.yang pyang --ietf -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ ietf-ip@2018-02-22.yang pyang --ietf -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ ietf-routing@2018-03-13.yang pyang --ietf -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ ietf-ipv4-unicast-routing@2018-03-13.yang pyang --ietf -f tree --path ./:../../../../modules/ietf-draft/:../../../../modules/ietf/ ietf-ipv6-unicast-routing@2018-03-13.yang if [ "$RUN_WITH_CONFD" != "" ] ; then killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc for module in ietf-interfaces@2018-02-20 ietf-ip@2018-02-22 ietf-routing@2018-03-13 ietf-ipv4-unicast-routing@2018-03-13 ietf-ipv6-unicast-routing@2018-03-13 ../../../../modules/ietf/iana-if-type@2014-05-08 example-rip ; do confdc -c ${module}.yang --yangpath ../../../../ done NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath ${RUN_WITH_CONFD}/etc/confd --addloadpath . 2>&1 1>server.log & SERVER_PID=$! else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --modpath=.:/usr/share/yuma/nmda-modules:/usr/share/yuma/modules --module=iana-if-type --module=ietf-routing --module=ietf-ipv4-unicast-routing --module=ietf-ipv6-unicast-routing --no-startup --validate-config-only --superuser=$USER /usr/sbin/netconfd --with-nmda=true --modpath=.:/usr/share/yuma/nmda-modules:/usr/share/yuma/modules --module=iana-if-type --module=ietf-routing --module=ietf-ipv4-unicast-routing --module=ietf-ipv6-unicast-routing --no-startup --superuser=$USER 2>&1 1>server.log & SERVER_PID=$! fi cd .. sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD #python session.yangcli.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/ietf-routing-bis/test-ietf-routing-bis.c0000664000175000017500000000615014770023131027037 0ustar vladimirvladimir/* module test-ietf-routing-bis */ #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "val.h" #include "val123.h" #include "agt_nmda.h" #include "val_set_cplxval_obj.h" /* module static variables */ static ncx_module_t *ietf_interfaces_mod; static ncx_module_t *ietf_ip_mod; static ncx_module_t *ietf_routing_mod; static obj_template_t* interfaces_obj; /* Registered callback functions: get_system_cfg_interfaces */ static status_t get_system_cfg_interfaces(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res = NO_ERR; res = val_set_cplxval_obj(dst_val,dst_val->obj,"\ \ eth0\ Uplink to ISP.\ ianaift:ethernetCsmacd\ 00:0C:42:E5:B1:E9\ up\ \ 2015-10-24T17:11:27+02:00\ \ \
\ 192.0.2.1\ 24\
\
\
\
"); assert(res == NO_ERR); return res; } /* The 3 mandatory callback functions: y_test_ietf_routing_bis_init, y_test_ietf_routing_bis_init2, y_test_ietf_routing_bis_cleanup */ status_t y_test_ietf_routing_bis_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-interfaces", NULL, &agt_profile->agt_savedevQ, &ietf_interfaces_mod); if (res != NO_ERR) { return res; } interfaces_obj = ncx_find_object( ietf_interfaces_mod, "interfaces"); assert(interfaces_obj != NULL); return res; } status_t y_test_ietf_routing_bis_init2(void) { status_t res; val_value_t* root_system_val; val_value_t* interfaces_val; res = NO_ERR; root_system_val = agt_nmda_get_root_system(); assert(root_system_val); interfaces_val = val_find_child(root_system_val, "ietf-interfaces", "interfaces"); assert(interfaces_val==NULL); interfaces_val = val_new_value(); assert(interfaces_val); val_init_virtual(interfaces_val, get_system_cfg_interfaces, interfaces_obj); val_add_child(interfaces_val, root_system_val); return NO_ERR; } void y_test_ietf_routing_bis_cleanup (void) { } yuma123_2.14/netconf/test/netconfd/identityref-typedef/0000775000175000017500000000000014770023131023322 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/identityref-typedef/good.xml0000664000175000017500000000047414770023131025001 0ustar vladimirvladimir a:foo-based yuma123_2.14/netconf/test/netconfd/identityref-typedef/bad.xml0000664000175000017500000000047414770023131024577 0ustar vladimirvladimir a:bar-based yuma123_2.14/netconf/test/netconfd/identityref-typedef/a.yang0000664000175000017500000000122714770023131024424 0ustar vladimirvladimirmodule a { yang-version 1.1; namespace "http://example.com/ns/identityref-typedef-a"; prefix "a"; organization "example.com"; description "Testing identityref typedef type defined in imported module."; revision 2018-08-06 { description "Corrected example to actually define identities based on twa baseless (abstract) identities."; } identity foo; identity bar; identity foo-based { base "foo"; } identity bar-based { base "bar"; } typedef my-identityref-typedef-type { type identityref { base "foo"; } } } yuma123_2.14/netconf/test/netconfd/identityref-typedef/run.sh0000775000175000017500000000130414770023131024463 0ustar vladimirvladimir#!/bin/bash killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=./b.yang --modpath=./:/usr/share/yuma/modules/ --startup=./good.xml --startup-error=stop --validate-config-only --superuser=$USER GOOD_RES=$? if [ "$GOOD_RES" != "0" ] ; then echo "Error: Failed with good.xml" exit $GOOD_RES else echo "OK: Succeeded with good.xml" fi /usr/sbin/netconfd --module=./b.yang --modpath=./:/usr/share/yuma/modules/ --startup=./bad.xml --startup-error=stop --validate-config-only --superuser=$USER BAD_RES=$? if [ "$BAD_RES" == "0" ] ; then echo "Error: Did not detect problem with bad.xml" exit -1 else echo "OK: Detected problem with bad.xml" fi exit 0 yuma123_2.14/netconf/test/netconfd/identityref-typedef/b.yang0000664000175000017500000000110214770023131024415 0ustar vladimirvladimirmodule b { yang-version 1.1; namespace "http://example.com/ns/identityref-typedef-b"; prefix "b"; import a { prefix a; } organization "example.com"; description "Testing identityref typedef type defined in imported module."; revision 2018-08-06 { description "Corrected example to actually define identities based on twa baseless (abstract) identities."; } container b { leaf my-identityref-typedef-type-leaf { type a:my-identityref-typedef-type; } } } yuma123_2.14/netconf/test/netconfd/xpath-enum-value/0000775000175000017500000000000014770023131022536 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/xpath-enum-value/good.xml0000664000175000017500000000031414770023131024206 0ustar vladimirvladimir foo major yuma123_2.14/netconf/test/netconfd/xpath-enum-value/bad.xml0000664000175000017500000000031414770023131024004 0ustar vladimirvladimir foo minor yuma123_2.14/netconf/test/netconfd/xpath-enum-value/test-xpath-enum-value.yang0000664000175000017500000000152414770023131027575 0ustar vladimirvladimirmodule test-xpath-enum-value { yang-version 1.1; namespace "http://yuma123.org/ns/test-xpath-enum-value"; prefix txev; organization "yuma123.org"; description "Part of the xpath-enum-value test."; revision 2017-07-15 { description "Initial version"; } list alarm { key id; leaf id { type string; } leaf severity { type enumeration { enum cleared { value 1; } enum indeterminate { value 2; } enum minor { value 3; } enum warning { value 4; } enum major { value 5; } enum critical { value 6; } } } } container call-for-help { presence "Call for help mode."; must "/alarm[enum-value(severity) >= 5]"; } } yuma123_2.14/netconf/test/netconfd/xpath-enum-value/run.sh0000775000175000017500000000250314770023131023701 0ustar vladimirvladimir#!/bin/bash -e function expected_fail { export DID_IT_FAIL=0 ; $1 || export DID_IT_FAIL=1 true if [ "$DID_IT_FAIL" = "1" ] ; then #echo "GOOD ERROR" return 0 else #echo "BAD OK" return -1 fi } rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cp *.yang tmp cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c test-xpath-enum-value.yang --yangpath /usr/share/yuma/modules/ietf -o test-xpath-enum-value.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! sleep 3 confd_load -l ../good.xml expected_fail "confd_load -l ../bad.xml" cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=./test-xpath-enum-value.yang --validate-config-only --startup=good.xml expected_fail "/usr/sbin/netconfd --module=./test-xpath-enum-value.yang --validate-config-only --startup=bad.xml" fi yuma123_2.14/netconf/test/netconfd/test-action-in-grouping0000775000175000017500000000007614770023131023755 0ustar vladimirvladimir#!/bin/sh set -e cd yang-1dot1 ./test-action-in-grouping.sh yuma123_2.14/netconf/test/netconfd/test-rollback-on-error0000775000175000017500000000005514770023131023573 0ustar vladimirvladimir#!/bin/bash -e cd rollback-on-error ./run.sh yuma123_2.14/netconf/test/netconfd/test-instance-identifier0000775000175000017500000000005714770023131024167 0ustar vladimirvladimir#!/bin/bash -e cd instance-identifier ./run.sh yuma123_2.14/netconf/test/netconfd/test-identity-iffeature0000775000175000017500000000014514770023131024042 0ustar vladimirvladimir#!/bin/sh set -e cd yang-1dot1 ./test-identity-iffeature-pass.sh ./test-identity-iffeature-fail.sh yuma123_2.14/netconf/test/netconfd/opendaylight/0000775000175000017500000000000014770023131022025 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/opendaylight/README0000664000175000017500000000024414770023131022705 0ustar vladimirvladimirThis testcase downloads and installs OpenDaylight configures NETCONF northbound interface and creates topology of netconfd nodes connected as southbound NETCONF. yuma123_2.14/netconf/test/netconfd/opendaylight/session.yangcli.py0000664000175000017500000000550214770023131025511 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os import argparse from yangcli.yangcli import yangcli #from lxml import etree import lxml import yangrpc def yangcli_ok_script(conn, yangcli_script): for line in yangcli_script.splitlines(): line=line.strip() if not line: continue print(("Executing: "+line)) ok = yangcli(conn, line).xpath('./ok') assert(len(ok)==1) def main(): print(""" #Description: Testcase for OpenDaylight interoperability. #Procedure: #1 - Create topology. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password print("Connecting #1 ...") conn = yangrpc.connect(server, port, user, password, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa", "--dump-session=tmp/nc-session-") if(conn==None): print("Error: yangrpc failed to connect!") return(-1) result = yangcli(conn, "delete /modules/module[name='right'][type='sal-netconf-connector']") result = yangcli(conn, "commit") ok = result.xpath('./ok') print((lxml.etree.tostring(result))) assert(len(ok)==1) result = yangcli(conn, "merge /modules/module[name='right'][type='sal-netconf-connector'] -- address=127.0.0.1 sal-netconf:port=4830 tcp-only=false username=demo password=demo sal-netconf:event-executor/type=netty-event-executor sal-netconf:event-executor/name=global-event-executor sal-netconf:binding-registry/type=binding-broker-osgi-registry sal-netconf:binding-registry/name=binding-osgi-broker sal-netconf:dom-registry/type=dom-broker-osgi-registry sal-netconf:dom-registry/name=dom-broker sal-netconf:client-dispatcher/type=netconf-client-dispatcher sal-netconf:client-dispatcher/name=global-netconf-dispatcher sal-netconf:processing-executor/type=threadpool sal-netconf:processing-executor/name=global-netconf-processing-executor sal-netconf:keepalive-executor/type=scheduled-threadpool sal-netconf:keepalive-executor/name=global-netconf-ssh-scheduled-executor") ok = result.xpath('./ok') print((lxml.etree.tostring(result))) assert(len(ok)==1) result = yangcli(conn, "commit") ok = result.xpath('./ok') print((lxml.etree.tostring(result))) assert(len(ok)==1) return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/opendaylight/session.litenc.py0000664000175000017500000001401314770023131025336 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Connect to opendaylight and /. #Procedure: #1 - Print the #2 - Print the operational state. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw, strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) # receive result=conn.receive() print("#1 - Print the ") print(lxml.etree.tostring(result)) print("#2 - Print the operational state.") get_rpc = """ """ print("get / ...") result = conn.rpc(get_rpc) print(lxml.etree.tostring(result)) delete_config_rpc = """ merge sal-netconf:sal-netconf-connector left """ print("delete-config ...") result = conn.rpc(delete_config_rpc) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') assert(len(ok)==1) edit_config_rpc = """ merge sal-netconf:sal-netconf-connector left
127.0.0.1
3830 false demo demo prefix:netty-event-executor global-event-executor prefix:binding-broker-osgi-registry binding-osgi-broker prefix:dom-broker-osgi-registry dom-broker prefix:netconf-client-dispatcher global-netconf-dispatcher prefix:threadpool global-netconf-processing-executor prefix:scheduled-threadpool global-netconf-ssh-scheduled-executor
""" print("edit-config ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') assert(len(ok)==1) commit_rpc = """ """ print("commit ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result)) ok = result.xpath('ok') assert(len(ok)==1) return(0) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/opendaylight/run.sh0000775000175000017500000000331014770023131023165 0ustar vladimirvladimir#!/bin/bash -e mkdir tmp || true cd tmp wget https://nexus.opendaylight.org/content/groups/public/org/opendaylight/integration/distribution-karaf/0.6.3-Carbon/distribution-karaf-0.6.3-Carbon.tar.gz tar -xzvf distribution-karaf-0.6.3-Carbon.tar.gz cd distribution-karaf-0.6.3-Carbon/ ./bin/start #install NETCONF support ./bin/client -u karaf feature:install odl-netconf-all odl-netconf-ssh odl-netconf-connector-all #install GUI support ./bin/client -u karaf feature:install odl-dlux-core odl-dluxapps-applications odl-dluxapps-nodes odl-dluxapps-yangman odl-dluxapps-yangui odl-dluxapps-yangutils odl-dluxapps-yangvisualizer cd ../../ #start netconfd nodes - avoid 1830 used by the opendaylight NETCONF server. killall -KILL netconfd || true NCPORT0=3830 NCPORT1=4830 rm /tmp/ncxserver.${NCPORT0}.sock || true /usr/sbin/netconfd --modpath=/usr/share/yuma/modules/ietf-draft:/usr/share/yuma/modules/ietf --module=iana-if-type --module=ietf-interfaces --no-startup --superuser=demo --ncxserver-sockname=/tmp/ncxserver.${NCPORT0}.sock --port=${NCPORT0} & SERVER0_PID=$! rm /tmp/ncxserver.${NCPORT1}.sock || true /usr/sbin/netconfd --modpath=/usr/share/yuma/modules/ietf-draft:/usr/share/yuma/modules/ietf --module=iana-if-type --module=ietf-interfaces --no-startup --superuser=demo --ncxserver-sockname=/tmp/ncxserver.${NCPORT1}.sock --port=${NCPORT1} & SERVER1_PID=$! #configure topology - TODO python session.litenc.py --server=localhost --port=1830 --user=admin --password=admin python session.yangcli.py --server=localhost --port=1830 --user=admin --password=admin yangcli --server=localhost --user=admin --ncport=1830 --run-command="get" --batch-mode --password=admin #test - TODO kill -KILL $SERVER0_PID kill -KILL $SERVER1_PID yuma123_2.14/netconf/test/netconfd/multiple-edit-callbacks/0000775000175000017500000000000014770023131024031 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/multiple-edit-callbacks/session.py0000664000175000017500000000135414770023131026071 0ustar vladimirvladimirfrom yangcli.yangcli import yangcli from lxml import etree import yangrpc import sys import os conn = yangrpc.connect("127.0.0.1", 830, os.getenv('USER'), None, os.getenv('HOME')+"/.ssh/id_rsa.pub",os.getenv('HOME')+"/.ssh/id_rsa") if(conn==None): print("Error: yangrpc failed to connect!") sys.exit(1) yangcli(conn, "create /interfaces/interface[name='blah']/type value='ethernetCsmacd'") result=yangcli(conn, "commit") assert(result.xpath('./ok')) result=yangcli(conn, "xget /interfaces") a=result.xpath('./data/interfaces/interface[name=\'blah\']/a') b=result.xpath('./data/interfaces/interface[name=\'blah\']/b') print((a[0].text)) assert(a[0].text=="Hello from a!") print((b[0].text)) assert(b[0].text=="Hello from b!") print("Done.") yuma123_2.14/netconf/test/netconfd/multiple-edit-callbacks/startup-cfg.xml0000664000175000017500000000027514770023131027016 0ustar vladimirvladimir yuma123_2.14/netconf/test/netconfd/multiple-edit-callbacks/README0000664000175000017500000000113714770023131024713 0ustar vladimirvladimirFILES: * run.sh - shell script executing the testcase * session.py - python script connecting to the started netconfd server * startup-cfg.xml - initial configuration * multiple-edit-callbacks-a.c - SIL module test implementation based on ietf-interfaces.yang * multiple-edit-callbacks-b.c PURPOSE: Verify seperate modules can register edit callbacks for the same data object identifier OPERATION: Starts 2 modules registering callbacks for /interfaces/interface one registers /interfaces/interface/a the other /interfaces/interface/b virtual config=false leafs for each created interface instance. yuma123_2.14/netconf/test/netconfd/multiple-edit-callbacks/multiple-edit-callbacks.c0000664000175000017500000001071114770023131030670 0ustar vladimirvladimir/* module test-multiple-edit-callbacks(based on ietf-interfaces.yang) */ #define __USE_XOPEN 1 //#define _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #ifdef INSTANCE_A #define INSTANCE_NAME(func) y_test_multiple_edit_callbacks_a_ ## func #define INSTANCE_STR "a" #elif INSTANCE_B #define INSTANCE_NAME(func) y_test_multiple_edit_callbacks_b_ ## func #define INSTANCE_STR "b" #else #error "Unexpected instance" #endif static status_t myget(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res; res = val_set_simval_obj(dst_val, dst_val->obj, "Hello from " INSTANCE_STR "!"); return res; } static status_t edit_cb ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; char* editop_str=NULL; char* cbtyp_str=NULL; char* newval_str=NULL; char* curval_str=NULL; res = NO_ERR; errorval = NULL; errorstr = NULL; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ cbtyp_str = "AGT_CB_VALIDATE"; break; case AGT_CB_APPLY: /* database manipulation done here */ cbtyp_str = "AGT_CB_APPLY"; break; case AGT_CB_COMMIT: /* device instrumentation done here */ cbtyp_str = "AGT_CB_COMMIT"; break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ cbtyp_str = "AGT_CB_ROLLBACK"; break; default: cbtyp_str = "AGT_CB_UNKNOWN!"; res = SET_ERROR(ERR_INTERNAL_VAL); } switch (editop) { case OP_EDITOP_DELETE: editop_str = "OP_EDITOP_DELETE"; break; case OP_EDITOP_LOAD: editop_str = "OP_EDITOP_LOAD"; break; case OP_EDITOP_MERGE: editop_str = "OP_EDITOP_MERGE"; break; case OP_EDITOP_REPLACE: editop_str = "OP_EDITOP_REPLACE"; break; case OP_EDITOP_CREATE: editop_str = "OP_EDITOP_CREATE"; break; } if(cbtyp==AGT_CB_COMMIT) { if(newval!=NULL) { val_value_t* val; obj_template_t* obj; printf("register %s\n", "blah"); obj = obj_find_child(newval->obj, "test-multiple-edit-callbacks", INSTANCE_STR); val = val_new_value(); assert(val!=NULL); val_init_virtual(val, myget, obj); val_add_child(val, newval); } } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* The 3 mandatory callback functions: y_test_multiple_edit_callbacks_init, y_test_multiple_edit_callbacks_init2, y_test_multiple_edit_callbacks_cleanup */ status_t INSTANCE_NAME(init) ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; ncx_module_t *mod; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-interfaces", NULL, &agt_profile->agt_savedevQ, &mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "test-multiple-edit-callbacks", NULL, &agt_profile->agt_savedevQ, &mod); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( "ietf-interfaces", (const xmlChar *)"/interfaces/interface", (const xmlChar *)NULL /*"YYYY-MM-DD"*/, edit_cb); if (res != NO_ERR) { return res; } return res; } status_t INSTANCE_NAME(init2)(void) { return NO_ERR; } void INSTANCE_NAME(cleanup) (void) { } yuma123_2.14/netconf/test/netconfd/multiple-edit-callbacks/test-multiple-edit-callbacks.yang0000664000175000017500000000075014770023131032363 0ustar vladimirvladimirmodule test-multiple-edit-callbacks { namespace "http://yuma123.org/ns/test-multiple-edit-callbacks"; prefix interfaces-loopback; import ietf-interfaces { prefix if; } organization "yuma123.org"; description "Model for the /interfaces/interface/ ./a and ./b leafs"; revision 2016-11-21 { description "1.st version"; } augment "/if:interfaces/if:interface" { leaf a { type string; } leaf b { type string; } } } yuma123_2.14/netconf/test/netconfd/multiple-edit-callbacks/Makefile.am0000775000175000017500000000145414770023131026074 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libtest-multiple-edit-callbacks-a.la libtest_multiple_edit_callbacks_a_la_SOURCES = multiple-edit-callbacks.c libtest_multiple_edit_callbacks_a_la_CPPFLAGS = -DINSTANCE_A -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libtest_multiple_edit_callbacks_a_la_LDFLAGS = -module -lyumaagt -lyumancx netconfmodule_LTLIBRARIES += libtest-multiple-edit-callbacks-b.la libtest_multiple_edit_callbacks_b_la_SOURCES = multiple-edit-callbacks.c libtest_multiple_edit_callbacks_b_la_CPPFLAGS = -DINSTANCE_B -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libtest_multiple_edit_callbacks_b_la_LDFLAGS = -module -lyumaagt -lyumancx yang_DATA = test-multiple-edit-callbacks.yang yuma123_2.14/netconf/test/netconfd/multiple-edit-callbacks/run.sh0000775000175000017500000000061614770023131025177 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp cp startup-cfg.xml tmp rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=test-multiple-edit-callbacks-a --module=test-multiple-edit-callbacks-b --module=iana-if-type --startup=tmp/startup-cfg.xml --superuser=$USER 1>tmp/netconfd.stdout 2>tmp/netconfd.stderr & NETCONFD_PID=$! sleep 1 python session.py kill $NETCONFD_PID cat tmp/netconfd.stdout yuma123_2.14/netconf/test/netconfd/copy-config/0000775000175000017500000000000014770023131021553 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/copy-config/startup-cfg.xml0000664000175000017500000000044514770023131024537 0ustar vladimirvladimir foo ianaift:ethernetCsmacd yuma123_2.14/netconf/test/netconfd/copy-config/README0000664000175000017500000000057014770023131022435 0ustar vladimirvladimirFILES: * run.sh - shell script executing the testcase * session.ncclient.py - python script connecting to the started netconfd server copy config and rerify the configuration is correctly committed * startup-cfg.xml - initial configuration PURPOSE: Verify copy-config works OPERATION: Uses copy-config and reads back the configuration with get-config for verification. yuma123_2.14/netconf/test/netconfd/copy-config/run.sh0000775000175000017500000000055214770023131022720 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp cp startup-cfg.xml tmp rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=ietf-interfaces --module=iana-if-type --startup=tmp/startup-cfg.xml --superuser=$USER 1>tmp/netconfd.stdout 2>tmp/netconfd.stderr & NETCONFD_PID=$! sleep 3 python session.ncclient.py kill $NETCONFD_PID cat tmp/netconfd.stdout sleep 1 yuma123_2.14/netconf/test/netconfd/copy-config/session.ncclient.py0000664000175000017500000000351414770023131025411 0ustar vladimirvladimir#!/usr/bin/env python from ncclient import manager from ncclient.xml_ import * import time import sys, os def main(): print(""" #Description: Demonstrate that copy-config works. #Procedure: #1 - Copy configuration with single interface named "bar" to 'candidate' and commit. #2 - Verify get-config returns the interface named "bar" in """) conn = manager.connect(host="127.0.0.1", port=830, username=os.getenv('USER'), password='admin', look_for_keys=True, timeout=10, device_params = {'name':'junos'}, hostkey_verify=False) print("Connected ...") rpc = """ """ print("get-config ...") result = conn.rpc(rpc) names = result.xpath('//data/interfaces/interface/name') print((len(names))) assert(len(names)==1) print((names[0].text)) assert(names[0].text=='foo') rpc = """ bar ianaift:ethernetCsmacd """ print("copy-config ...") result = conn.rpc(rpc) rpc = """ """ print("commit ...") result = conn.rpc(rpc) rpc = """ """ print("get-config ...") result = conn.rpc(rpc) names = result.xpath('//data/interfaces/interface/name') print((len(names))) assert(len(names)==1) print((names[0].text)) assert(names[0].text=='bar') sys.exit(main()) yuma123_2.14/netconf/test/netconfd/test-xpath-deref-20000775000175000017500000000005214770023131022604 0ustar vladimirvladimir#!/bin/bash -e cd xpath-deref ./run.sh 2 yuma123_2.14/netconf/test/netconfd/test-yang-library0000775000175000017500000000005014770023131022634 0ustar vladimirvladimir#!/bin/bash -e cd yang-library ./run.sh yuma123_2.14/netconf/test/netconfd/test-xpath-re-match0000775000175000017500000000005314770023131023061 0ustar vladimirvladimir#!/bin/bash -e cd xpath-re-match ./run.sh yuma123_2.14/netconf/test/netconfd/agt-commit-complete/0000775000175000017500000000000014770023131023205 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/agt-commit-complete/session.py0000664000175000017500000000105214770023131025240 0ustar vladimirvladimirfrom yangcli.yangcli import yangcli from lxml import etree import yangrpc import sys import os conn = yangrpc.connect("127.0.0.1", 830, os.getenv('USER'), None, os.getenv('HOME')+"/.ssh/id_rsa.pub", os.getenv('HOME')+"/.ssh/id_rsa",) if(conn==None): print("Error: yangrpc failed to connect!") sys.exit(1) result=yangcli(conn, "commit") assert(result.xpath('./ok')) yangcli(conn, "replace /system/hostname value='ok3'") yangcli(conn, "replace /system/location value='ok4'") result=yangcli(conn, "commit") assert(result.xpath('./ok')) print("Done.") yuma123_2.14/netconf/test/netconfd/agt-commit-complete/test_agt_commit_complete.c0000664000175000017500000001202314770023131030421 0ustar vladimirvladimir/* module test-agt-commit-complete(based on test-agt-commit-complete.yang) */ #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "agt_commit_complete.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "xpath.h" /* module static variables */ static ncx_module_t *ietf_system_mod; static status_t y_commit_post_reply ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { printf("\n#y_commit_post_reply\n"); return NO_ERR; } static status_t y_system_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { if(cbtyp==AGT_CB_COMMIT) { printf("\n#y_system_edit cbtyp=AGT_CB_COMMIT\n"); } return NO_ERR; } void my_transaction_handler(unsigned int transaction_id, val_value_t* prev_root_config_val, val_value_t* root_val) { status_t res; val_value_t* location_before_val=NULL; val_value_t* location_after_val=NULL; val_value_t* hostname_before_val=NULL; val_value_t* hostname_after_val=NULL; if(prev_root_config_val) { res = xpath_find_val_target(prev_root_config_val, ietf_system_mod/*mod*/,"/sys:system/sys:location", &location_before_val); res = xpath_find_val_target(prev_root_config_val, ietf_system_mod/*mod*/,"/sys:system/sys:hostname", &hostname_before_val); } res = xpath_find_val_target(root_val, ietf_system_mod/*mod*/,"/sys:system/sys:location", &location_after_val); res = xpath_find_val_target(root_val, ietf_system_mod/*mod*/,"/sys:system/sys:hostname", &hostname_after_val); printf("\n#%u: location=%s -> location=%s, hostname=%s -> hostname=%s\n", transaction_id, (location_before_val)?VAL_STRING(location_before_val):"None", (location_after_val)?VAL_STRING(location_after_val):"None", (hostname_before_val)?VAL_STRING(hostname_before_val):"None", (hostname_after_val)?VAL_STRING(hostname_after_val):"None"); } static val_value_t* prev_root_config_val=NULL; static unsigned int transaction_id=0; status_t my_commit_complete_cb(void) { cfg_template_t* runningcfg; status_t res; val_value_t* cur_root_config_val; printf("in my_commit_complete_cb\n"); runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg!=NULL && runningcfg->root!=NULL); cur_root_config_val = val_clone_config_data(runningcfg->root, &res); assert(res==NO_ERR); val_dump_value(cur_root_config_val, NCX_DEF_INDENT); printf("\nTransaction id=%u", transaction_id); printf("\nBefore:"); if(prev_root_config_val==NULL) { printf("\nNone."); } else { val_dump_value(prev_root_config_val, NCX_DEF_INDENT); } printf("\nAfter:"); val_dump_value(cur_root_config_val, NCX_DEF_INDENT); my_transaction_handler(transaction_id, prev_root_config_val, runningcfg->root); if(prev_root_config_val!=NULL) { val_free_value(prev_root_config_val); } prev_root_config_val = cur_root_config_val; transaction_id++; return NO_ERR; } /* The 3 mandatory callback functions: y_ietf_system_init, y_ietf_system_init2, y_ietf_system_cleanup */ status_t y_test_agt_commit_complete_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; ncx_module_t *mod; agt_profile = agt_get_profile(); res=agt_commit_complete_register("test-agt-commit-complete", my_commit_complete_cb); assert(res == NO_ERR); res = ncxmod_load_module( "ietf-system", NULL, &agt_profile->agt_savedevQ, &mod); assert(res==NO_ERR); res = agt_rpc_register_method( "yuma123-netconf" /*hack since it overloads the "ietf-netconf" namespace*/, "commit", AGT_RPC_PH_POST_REPLY, y_commit_post_reply); assert(res==NO_ERR); res = agt_cb_register_callback( "my-test-agt-commit-complete", "/system/location", NULL, y_system_edit); assert(res==NO_ERR); res = agt_cb_register_callback( "ietf-system", "/system/hostname", NULL, y_system_edit); assert(res==NO_ERR); res = ncxmod_load_module( "ietf-system", NULL, &agt_profile->agt_savedevQ, &ietf_system_mod); assert(res==NO_ERR); return res; } status_t y_test_agt_commit_complete_init2(void) { return NO_ERR; } void y_test_agt_commit_complete_cleanup (void) { agt_commit_complete_unregister("test-agt-commit-complete"); if(prev_root_config_val!=NULL) { val_free_value(prev_root_config_val); } } yuma123_2.14/netconf/test/netconfd/agt-commit-complete/startup-cfg.xml0000664000175000017500000000035314770023131026167 0ustar vladimirvladimir ok1 ok2 yuma123_2.14/netconf/test/netconfd/agt-commit-complete/README0000664000175000017500000000054314770023131024067 0ustar vladimirvladimirFILES: * run.sh - shell script executing the testcase * session.py - python script connecting to the started netconfd server * test-agt-commit-complete.c - SIL module test implementation (loads ietf-system.yang) PURPOSE: Verify agt_commit_complete callbacks are correctly called. OPERATION: Initiates commit and logs the commit complete callbacks. yuma123_2.14/netconf/test/netconfd/agt-commit-complete/expected_output.txt0000664000175000017500000000031514770023131027166 0ustar vladimirvladimir#0: location=None -> location=ok2, hostname=None -> hostname=ok1 #y_commit_post_reply #y_system_edit cbtyp=AGT_CB_COMMIT #1: location=ok2 -> location=ok4, hostname=ok1 -> hostname=ok3 #y_commit_post_reply yuma123_2.14/netconf/test/netconfd/agt-commit-complete/Makefile.am0000775000175000017500000000050014770023131025237 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libtest-agt-commit-complete.la libtest_agt_commit_complete_la_SOURCES = test_agt_commit_complete.c libtest_agt_commit_complete_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libtest_agt_commit_complete_la_LDFLAGS = -module -lyumaagt -lyumancx yuma123_2.14/netconf/test/netconfd/agt-commit-complete/run.sh0000775000175000017500000000062514770023131024353 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp cp startup-cfg.xml tmp rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=test-agt-commit-complete --startup=tmp/startup-cfg.xml --superuser=$USER 1>tmp/netconfd.stdout 2>tmp/netconfd.stderr & NETCONFD_PID=$! sleep 1 python session.py kill $NETCONFD_PID cat tmp/netconfd.stdout | grep '#' > tmp/output.txt cmp expected_output.txt tmp/output.txt sleep 1 yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/0000775000175000017500000000000014770023131023512 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_20_1/0000775000175000017500000000000014770023131025074 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_20_1/mod1.yang0000664000175000017500000000034114770023131026612 0ustar vladimirvladimirmodule mod1 { yang-version 1.1; namespace "http://yuma123.org/ns/sec7_20_1/mod1"; prefix m1; feature "foo"; feature "bar"; container foobar { if-feature "foo or bar"; presence "foo or bar is on"; } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_20_1/mod2.yang0000664000175000017500000000031514770023131026614 0ustar vladimirvladimirmodule mod2 { namespace "http://yuma123.org/ns/sec7_20_1/mod2"; prefix m2; feature "foo"; feature "bar"; container foobar { if-feature "foo or bar"; presence "foo or bar is on"; } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/README0000664000175000017500000000121014770023131024364 0ustar vladimirvladimir=Conformance test suite for RFC 7950: "The YANG 1.1 Data Modeling Language"= ==FILES== testspec.txt - specification of the expected results 1-FAIL, 0-PASS check-yang-conformance.sh - script running the test suite ==USAGE== #Run with netconfd ./check-yang-conformance.sh .. testspec.txt #Run with pyang export RUN_WITH_PYANG=1 ./check-yang-conformance.sh .. testspec.txt export RUN_WITH_PYANG= #Run with yanglint export RUN_WITH_YANGLINT=1 ./check-yang-conformance.sh .. testspec.txt export RUN_WITH_YANGLINT= #Run with confd export RUN_WITH_CONFD=~/confd/root ./check-yang-conformance.sh .. testspec.txt export RUN_WITH_CONFD= yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_1_5/0000775000175000017500000000000014770023131025017 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_1_5/mod1.yang0000664000175000017500000000065214770023131026542 0ustar vladimirvladimirmodule mod1 { yang-version 1.1; namespace "http://yuma123.org/ns/sec7_1_5/mod1"; prefix m1; import ietf-interfaces { prefix if; revision-date "2014-05-08"; reference "RFC 7223"; description "All 4 sub-statements of the import statement."; } organization "yuma123.org"; description 'sec7_1_5 The "import" Statement'; revision 2017-07-15 { description "Initial version"; } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec9_10_3/0000775000175000017500000000000014770023131025077 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec9_10_3/mod1.yang0000664000175000017500000000042214770023131026615 0ustar vladimirvladimirmodule mod1 { prefix m1; namespace "http://yuma123.org/ns/sec9_10_3/mod1"; reference "https://mailarchive.ietf.org/arch/msg/netmod/D7ASSAQdpGmGXjIdRo_PCnQmVP8"; identity B; identity X { base B; } leaf foo { type identityref { base B; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec9_10_3/data1.xml0000664000175000017500000000017314770023131026614 0ustar vladimirvladimirX X yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec9_10_3/mod2.yang0000664000175000017500000000041514770023131026620 0ustar vladimirvladimirmodule mod2 { prefix m2; namespace "http://yuma123.org/ns/sec9_10_3/mod2"; import mod1 { prefix a; } reference "https://mailarchive.ietf.org/arch/msg/netmod/D7ASSAQdpGmGXjIdRo_PCnQmVP8"; leaf fooref { type leafref { path "/a:foo"; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec9_10_3/data2.xml0000664000175000017500000000026114770023131026613 0ustar vladimirvladimirX mod1:X yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec9_10_3/data3.xml0000664000175000017500000000045014770023131026614 0ustar vladimirvladimirX mod1:X a:X yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec9_10_3/mod3.yang0000664000175000017500000000042314770023131026620 0ustar vladimirvladimirmodule mod3 { prefix m3; namespace "http://yuma123.org/ns/sec9_10_3/mod3"; import mod2 { prefix b; } reference "https://mailarchive.ietf.org/arch/msg/netmod/D7ASSAQdpGmGXjIdRo_PCnQmVP8"; leaf foorefref { type leafref { path "/b:fooref"; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_7/0000775000175000017500000000000014770023131024601 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_7/mod1.yang0000664000175000017500000000054214770023131026322 0ustar vladimirvladimirmodule mod1 { yang-version 1.1; namespace "http://yuma123.org/ns/sec7_7/mod1"; prefix m1; organization "yuma123.org"; description 'sec7_7 The "leaf-list" Statement'; revision 2019-08-04 { description "Initial version"; } container top { leaf-list a { type uint32; default 3; default 4; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_7/mod2.yang0000664000175000017500000000057014770023131026324 0ustar vladimirvladimirmodule mod2 { yang-version 1.1; namespace "http://yuma123.org/ns/sec7_7/mod2"; prefix m2; organization "yuma123.org"; description 'sec7_7 The "leaf-list" Statement'; revision 2019-08-04 { description "Initial version"; } container top { leaf-list a { type uint32; default 3; default 4; min-elements 1; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_5_4/0000775000175000017500000000000014770023131025022 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_5_4/mod1.yang0000664000175000017500000000075314770023131026547 0ustar vladimirvladimirmodule mod1 { prefix m1; namespace "http://yuma123.org/ns/sec7_5_4/mod1"; container interface { leaf ifType { type enumeration { enum ethernet; enum atm; } } leaf ifMTU { type uint32; } must 'ifType != "ethernet" or ifMTU = 1500' { error-message "An Ethernet MTU must be 1500"; } must 'ifType != "atm" or' + ' (ifMTU <= 17966 and ifMTU >= 64)' { error-message "An ATM MTU must be 64 .. 17966"; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_5_4/data1.xml0000664000175000017500000000013314770023131026533 0ustar vladimirvladimir 1024 yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_5_4/mod2.yang0000664000175000017500000000077614770023131026555 0ustar vladimirvladimirmodule mod2 { prefix m2; namespace "http://yuma123.org/ns/sec7_5_4/mod2"; container interface { leaf ifType { type enumeration { enum ethernet; enum atm; } } leaf ifMTU { type uint32; } must 'string(ifType) != "ethernet" or ifMTU = 1500' { error-message "An Ethernet MTU must be 1500"; } must 'string(ifType) != "atm" or' + ' (ifMTU <= 17966 and ifMTU >= 64)' { error-message "An ATM MTU must be 64 .. 17966"; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_5_4/data2.xml0000664000175000017500000000013314770023131026534 0ustar vladimirvladimir 1024 yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_13_3/0000775000175000017500000000000014770023131025100 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_13_3/mod1.yang0000664000175000017500000000212714770023131026622 0ustar vladimirvladimirmodule mod1 { yang-version 1.1; namespace "http://yuma123.org/ns/sec7_13_3/mod1"; prefix m1; organization "yuma123.org"; description 'sec7_13_3 The "uses" Statement - XML Encoding Rules'; revision 2018-04-13 { description "Initial version"; } typedef foo-ref { type leafref { path "/top1/list/foo"; } } typedef bar-ref { type leafref { path "/top1/list[foo=current()/../foo]/bar"; } } grouping foo-bar-ref-typedef { leaf foo { type foo-ref; } leaf bar { type bar-ref; } } grouping foo-bar-ref-inline { leaf foo { type leafref { path "/m1:top1/m1:list/m1:foo"; } } leaf bar { type leafref { path "/m1:top1/m1:list[m1:foo=current()/../foo]/m1:bar"; } } } container top1 { list list { key "foo bar"; leaf foo { type string; } leaf bar { type string; } } container inline { uses foo-bar-ref-inline; } container typedef { uses foo-bar-ref-typedef; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_13_3/mod2.yang0000664000175000017500000000062414770023131026623 0ustar vladimirvladimirmodule mod2 { yang-version 1.1; namespace "http://yuma123.org/ns/sec7_13_3/mod2"; prefix m2; import mod1 { prefix m1; } organization "yuma123.org"; description 'sec7_13_3 The "uses" Statement - XML Encoding Rules.'; revision 2018-04-13 { description "Initial version"; } container top2 { container inline { uses m1:foo-bar-ref-inline; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec7_13_3/mod3.yang0000664000175000017500000000062614770023131026626 0ustar vladimirvladimirmodule mod3 { yang-version 1.1; namespace "http://yuma123.org/ns/sec7_13_3/mod3"; prefix m3; import mod1 { prefix m1; } organization "yuma123.org"; description 'sec7_13_3 The "uses" Statement - XML Encoding Rules.'; revision 2018-04-13 { description "Initial version"; } container top3 { container typedef { uses m1:foo-bar-ref-typedef; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/testspec.txt0000664000175000017500000000020714770023131026104 0ustar vladimirvladimirsec7_1_5 0 sec7_13_3 0,0,1 sec5_1 0,0 sec5_4 0,0,0 sec7_5_4 0,0 1,0 sec7_7 0,1 sec7_20_1 0,1 sec9_3 0 sec9_10_3 0,0,0 1,0,0 yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec9_3/0000775000175000017500000000000014770023131024577 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec9_3/mod1.yang0000664000175000017500000000040114770023131026312 0ustar vladimirvladimirmodule mod1 { prefix m1; namespace "http://yuma123.org/ns/sec9_3/mod1"; reference "https://github.com/vlvassilev/yuma123/issues/143"; leaf sample-configuration { type decimal64 { fraction-digits 4; range "0 .. 1.9999";} default 0.001; } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/run.sh0000775000175000017500000000016514770023131024657 0ustar vladimirvladimir#!/bin/bash rm -rf tmp mkdir tmp cd tmp bash -x ../../yang-conformance/check-yang-conformance.sh .. ../testspec.txt yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec5_1/0000775000175000017500000000000014770023131024571 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec5_1/sub3.yang0000664000175000017500000000042014770023131026321 0ustar vladimirvladimirsubmodule sub3 { yang-version 1.1; belongs-to mod2 { prefix m2; } container s3 { presence true; } grouping s3 { leaf s3 { type string; } } augment "/m2:m2" { container s3 { uses m2; uses s2; uses s3; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec5_1/mod1.yang0000664000175000017500000000025014770023131026306 0ustar vladimirvladimirmodule mod1 { yang-version 1.1; prefix m1; namespace "http://yuma123.org/ns/sec5_1/mod1"; include sub1; container m1 { presence true; uses s1; } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec5_1/mod2.yang0000664000175000017500000000030514770023131026310 0ustar vladimirvladimirmodule mod2 { yang-version 1.1; prefix m2; namespace "http://yuma123.org/ns/sec5_1/mod2"; include sub2; include sub3; container m2 { presence true; uses s2; uses s3; } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec5_1/sub1.yang0000664000175000017500000000021114770023131026315 0ustar vladimirvladimirsubmodule sub1 { yang-version 1.1; belongs-to mod1 { prefix m1; } grouping s1 { leaf s1 { type string; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec5_1/sub2.yang0000664000175000017500000000042014770023131026320 0ustar vladimirvladimirsubmodule sub2 { yang-version 1.1; belongs-to mod2 { prefix m2; } container s2 { presence true; } grouping s2 { leaf s2 { type string; } } augment "/m2:m2" { container s2 { uses m2; uses s2; uses s3; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec5_4/0000775000175000017500000000000014770023131024574 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec5_4/mod1.yang0000664000175000017500000000071714770023131026321 0ustar vladimirvladimirmodule mod1 { yang-version 1.1; namespace "http://yuma123.org/ns/sec5_4/mod1"; prefix m1; organization "yuma123.org"; description 'sec5_4 Resolving Grouping, Type, and Identity Names'; revision 2017-07-31 { description "Initial version"; } container top { list entry { key name; leaf name { type string; } } } typedef entry-ref { type leafref { path "/top/entry/name"; } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec5_4/mod2.yang0000664000175000017500000000067314770023131026323 0ustar vladimirvladimirmodule mod2 { yang-version 1.1; namespace "http://yuma123.org/ns/sec5_4/mod2"; prefix m2; import mod1 { prefix mod1-import; } organization "yuma123.org"; description 'sec5_4 Resolving Grouping, Type, and Identity Names'; revision 2017-07-31 { description "Initial version"; } container refs { list ref { key entry; leaf entry { type mod1-import:entry-ref; } } } } yuma123_2.14/netconf/test/netconfd/yang-1dot1-conformance/sec5_4/mod3.yang0000664000175000017500000000112114770023131026311 0ustar vladimirvladimirmodule mod3 { yang-version 1.1; namespace "http://yuma123.org/ns/sec5_4/mod3"; prefix m3; import mod1 { prefix mod1-import; } organization "yuma123.org"; description 'sec5_4 Resolving Grouping, Type, and Identity Names. Same as mod2 but the typedef is referenced in a local grouping.'; revision 2018-05-22 { description "Initial version"; } grouping entry-grouping { leaf entry { type mod1-import:entry-ref; } } container refs { list ref { key entry; uses entry-grouping; } } } yuma123_2.14/netconf/test/netconfd/test-ietf-interfaces-bis0000775000175000017500000000005714770023131024066 0ustar vladimirvladimir#!/bin/bash -e cd ietf-interfaces-bis ./run.sh yuma123_2.14/netconf/test/netconfd/xpath-bit-is-set/0000775000175000017500000000000014770023131022440 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/xpath-bit-is-set/good.xml0000664000175000017500000000123214770023131024110 0ustar vladimirvladimir foo ianaift:ethernetCsmacd UP DISABLED bar ianaift:ethernetCsmacd UP DISABLED yuma123_2.14/netconf/test/netconfd/xpath-bit-is-set/test-xpath-bit-is-set-bad-bit-name.yang0000664000175000017500000000152714770023131031622 0ustar vladimirvladimirmodule test-xpath-bit-is-set-bad-bit-name { yang-version 1.1; namespace "http://yuma123.org/ns/test-xpath-bit-is-set-bad-bit-name"; prefix txevbbn; import ietf-interfaces { prefix if; } organization "yuma123.org"; description "Part of the xpath-bit-is-set test."; revision 2017-07-25 { description "Initial version"; } augment "/if:interfaces/if:interface" { leaf flags { type bits { bit UP; bit PROMISCUOUS; bit DISABLED; } } } container redundancy { presence "Enables redundancy."; description "At least 2 interfaces must have UP bits to configure redundancy"; must '2<=count(/if:interfaces/if:interface[bit-is-set(txevbbn:flags, "BAD-BIT-NAME")])'; //must '/if:interfaces/if:interface[bit-is-set(txevbbn:flags, "BAD-BIT-NAME")]'; } } yuma123_2.14/netconf/test/netconfd/xpath-bit-is-set/bad.xml0000664000175000017500000000124314770023131023710 0ustar vladimirvladimir foo ianaift:ethernetCsmacd UP DISABLED bar ianaift:ethernetCsmacd PROMISCUOUS DISABLED yuma123_2.14/netconf/test/netconfd/xpath-bit-is-set/test-xpath-bit-is-set.yang0000664000175000017500000000132314770023131027376 0ustar vladimirvladimirmodule test-xpath-bit-is-set { yang-version 1.1; namespace "http://yuma123.org/ns/test-xpath-bit-is-set"; prefix txev; import ietf-interfaces { prefix if; } organization "yuma123.org"; description "Part of the xpath-bit-is-set test."; revision 2017-07-15 { description "Initial version"; } augment "/if:interfaces/if:interface" { leaf flags { type bits { bit UP; bit PROMISCUOUS; bit DISABLED; } } } container redundancy { presence "Enables redundancy."; description "At least 2 interfaces must have UP bits to configure redundancy"; must '2<=count(/if:interfaces/if:interface[bit-is-set(flags, "UP")])'; } } yuma123_2.14/netconf/test/netconfd/xpath-bit-is-set/run.sh0000775000175000017500000000377514770023131023617 0ustar vladimirvladimir#!/bin/bash -e function expected_fail { export DID_IT_FAIL=0 ; $1 || export DID_IT_FAIL=1 true if [ "$DID_IT_FAIL" = "1" ] ; then #echo "GOOD ERROR" return 0 else echo "ERROR: Expected FAIL returned OK." return -1 fi } rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cp *.yang tmp cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c /usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o ietf-interfaces.fxs confdc -c /usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --yangpath /usr/share/yuma/modules/ietf -o iana-if-type.fxs confdc -c test-xpath-bit-is-set.yang --yangpath /usr/share/yuma/modules/ietf -o test-xpath-bit-is-set.fxs confdc -c test-xpath-bit-is-set-bad-bit-name.yang --yangpath /usr/share/yuma/modules/ietf -o test-xpath-bit-is-set-bad-bit-name.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! sleep 3 echo "Loading good.xml ..." confd_load -l ../good.xml echo "Loading bad.xml" expected_fail "confd_load -l ../bad.xml" cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=./test-xpath-bit-is-set.yang --validate-config-only --no-startup expected_fail "/usr/sbin/netconfd --module=./test-xpath-bit-is-set-bad-bit-name.yang --validate-config-only --no-startup" /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --module=./test-xpath-bit-is-set.yang --validate-config-only --startup=good.xml expected_fail "/usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang --module=./test-xpath-bit-is-set.yang --validate-config-only --startup=bad.xml" fi yuma123_2.14/netconf/test/netconfd/test-xpath-current0000775000175000017500000000005214770023131023042 0ustar vladimirvladimir#!/bin/bash -e cd xpath-current ./run.sh yuma123_2.14/netconf/test/netconfd/test-nacm0000775000175000017500000000004014770023131021151 0ustar vladimirvladimir#!/bin/bash -e cd nacm ./run.sh yuma123_2.14/netconf/test/netconfd/anyxml/0000775000175000017500000000000014770023131020646 5ustar vladimirvladimiryuma123_2.14/netconf/test/netconfd/anyxml/session.config.litenc.py0000664000175000017500000000660614770023131025434 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Read and edit anyxml. #Procedure: #1 - Read /c/a and validate #2 - Replace /c/a read back and validate """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw,strip_namespaces=True) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) conn=litenc_lxml.litenc_lxml(conn_raw) print("Connected ...") namespaces={"nc":"urn:ietf:params:xml:ns:netconf:base:1.0", "test-anyxml":"http://yuma123.org/ns/test/netconfd/anyxml/test-anyxml", "one":"urn:1", "two":"urn:2"} edit_config_rpc = """ merge set 2 """ commit_rpc = """ """ get_rpc=""" """ print(" - /c/a ...") result = conn.rpc(edit_config_rpc) print(lxml.etree.tostring(result, pretty_print=True, inclusive_ns_prefixes=True)) print(" ...") result = conn.rpc(commit_rpc) print(lxml.etree.tostring(result, pretty_print=True, inclusive_ns_prefixes=True)) print(" - /c/a ...") result = conn.rpc(get_rpc) print(lxml.etree.tostring(result, pretty_print=True, inclusive_ns_prefixes=True)) one = result.xpath("./nc:data/test-anyxml:c/test-anyxml:a/test-anyxml:one", namespaces=namespaces) assert(len(one)==1) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/anyxml/test-anyxml.c0000664000175000017500000001006314770023131023277 0ustar vladimirvladimir#include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cli.h" #include "agt_nmda.h" #include "agt_rpc.h" #include "agt_util.h" #include "cfg.h" #include "getcb.h" #include "log.h" #include "ncxmod.h" #include "ncxtypes.h" #include "ncx_feature.h" #include "ncx_list.h" #include "rpc.h" #include "rpc_err.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "tstamp.h" #include "val.h" #include "val123.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xml_wr.h" #include "yangconst.h" /******************************************************************** * FUNCTION ping_pong * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t ping_pong(ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { val_value_t *ping_val; obj_template_t *output_obj; obj_template_t *pong_obj; val_value_t *pong_val; val_value_t *chval; status_t res; ping_val = val_find_child(msg->rpc_input, "test-anyxml", "ping"); assert(ping_val); printf("ping:\n"); val_dump_value_max_w_file(ping_val,0/*startident*/,NCX_DEF_INDENT/*indent_amount*/,NCX_DISPLAY_MODE_XML,TRUE/*with_meta*/,FALSE/*configonly*/,stdout); output_obj = obj_find_child( msg->rpc_input->obj->parent, "test-anyxml", "output"); assert(output_obj); pong_obj = obj_find_child( output_obj, "test-anyxml", "pong"); assert(pong_obj); pong_val = val_new_value(); assert(pong_val); val_init_from_template(pong_val, pong_obj); for(chval=val_get_first_child(ping_val); chval!=NULL; chval=val_get_next_child(chval)) { val_value_t *newval; newval = val_clone(chval); val_add_child(newval, pong_val); } #if 0 res = val_set_simval_obj( output_rpc_val, output_rpc_val->obj, ""); #endif dlq_enque(pong_val, &msg->rpc_dataQ); msg->rpc_data_type = RPC_DATA_YANG; return NO_ERR; } /* anyxml */ /******************************************************************** * FUNCTION y_test_anyxml_init * * INIT 1: * Initialize the module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t y_test_anyxml_init (void) { agt_profile_t *agt_profile; status_t res; ncx_module_t *mod; obj_template_t *root_obj; val_value_t* clivalset; val_value_t* val; /* load in the RPC methods */ res = ncxmod_load_module( "test-anyxml", NULL, NULL, NULL ); assert(res == NO_ERR); /* yangcli-to-rpc */ res = agt_rpc_register_method("test-anyxml", "ping-pong", AGT_RPC_PH_INVOKE, ping_pong); assert(res == NO_ERR); return NO_ERR; } /* y_test_anyxml_init */ /******************************************************************** * FUNCTION y_test_anyxml_init2 * * INIT 2: * Initialize the data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t y_test_anyxml_init2 (void) { return NO_ERR; } /* y_test_anyxml_init2 */ /******************************************************************** * FUNCTION y_test_anyxml_cleanup * * Cleanup the module data structures * * INPUTS: * * RETURNS: * none *********************************************************************/ void y_test_anyxml_cleanup (void) { agt_rpc_unregister_method("test-anyxml", "ping-pong"); } /* y_test_anyxml_cleanup */ yuma123_2.14/netconf/test/netconfd/anyxml/session.litenc.py0000664000175000017500000000561714770023131024171 0ustar vladimirvladimir#!/usr/bin/env python import time import sys, os sys.path.append("../../litenc") import litenc import litenc_lxml import lxml import argparse def main(): print(""" #Description: Demonstrate that duplicated list entries in edit-config are detected. #Procedure: #1 - Create interface "foo" and commit. Verify commit succeeds. #2 - Create 2x duplicate interface "bar" and commit. Verify commit fails. """) parser = argparse.ArgumentParser() parser.add_argument("--server", help="server name e.g. 127.0.0.1 or server.com (127.0.0.1 if not specified)") parser.add_argument("--user", help="username e.g. admin ($USER if not specified)") parser.add_argument("--port", help="port e.g. 830 (830 if not specified)") parser.add_argument("--password", help="password e.g. mypass123 (passwordless if not specified)") args = parser.parse_args() if(args.server==None or args.server==""): server="127.0.0.1" else: server=args.server if(args.port==None or args.port==""): port=830 else: port=int(args.port) if(args.user==None or args.user==""): user=os.getenv('USER') else: user=args.user if(args.password==None or args.password==""): password=None else: password=args.password conn_raw = litenc.litenc() ret = conn_raw.connect(server=server, port=port, user=user, password=password) if ret != 0: print("[FAILED] Connecting to server=%(server)s:" % {'server':server}) return(-1) print("[OK] Connecting to server=%(server)s:" % {'server':server}) conn=litenc_lxml.litenc_lxml(conn_raw) ret = conn_raw.send(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: print("[FAILED] Sending ") return(-1) (ret, reply_xml)=conn_raw.receive() if ret != 0: print("[FAILED] Receiving ") return(-1) print("[OK] Receiving =%(reply_xml)s:" % {'reply_xml':reply_xml}) print("Connected ...") namespaces={"nc":"urn:ietf:params:xml:ns:netconf:base:1.0", "test-anyxml":"http://yuma123.org/ns/test/netconfd/anyxml/test-anyxml", "one":"urn:1", "two":"urn:2"} ping_pong_rpc = """ 2 """ print("ping-pong ...") (ret, reply_xml)= conn_raw.rpc(ping_pong_rpc) print (reply_xml) request = lxml.etree.fromstring(ping_pong_rpc) reply = lxml.etree.fromstring(reply_xml.encode('ascii')) one_sent = request.xpath("./test-anyxml:ping/test-anyxml:one", namespaces=namespaces) one_received = reply.xpath("./test-anyxml:pong/test-anyxml:one", namespaces=namespaces) print(one_sent) print(one_received) assert(len(one_sent)==1) assert(len(one_received)==1) sys.exit(main()) yuma123_2.14/netconf/test/netconfd/anyxml/Makefile.am0000775000175000017500000000037714770023131022714 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libtest-anyxml.la libtest_anyxml_la_SOURCES = test-anyxml.c libtest_anyxml_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libtest_anyxml_la_LDFLAGS = -module -lyumaagt -lyumancx yuma123_2.14/netconf/test/netconfd/anyxml/run.sh0000775000175000017500000000177714770023131022025 0ustar vladimirvladimir#!/bin/bash -e rm -rf tmp || true mkdir tmp if [ "$RUN_WITH_CONFD" != "" ] ; then cd tmp killall -KILL confd || true echo "Starting confd: $RUN_WITH_CONFD" source $RUN_WITH_CONFD/confdrc confdc -c ../test-anyxml.yang --yangpath .. -o test-anyxml.fxs NCPORT=2022 NCUSER=admin NCPASSWORD=admin confd --verbose --foreground --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd --addloadpath ${RUN_WITH_CONFD}/src/confd/yang --addloadpath ${RUN_WITH_CONFD}/src/confd/aaa --addloadpath . 2>&1 1>server.log & SERVER_PID=$! cd .. else killall -KILL netconfd || true rm /tmp/ncxserver.sock || true /usr/sbin/netconfd --module=test-anyxml --no-startup --superuser=$USER 2>&1 1>tmp/server.log & SERVER_PID=$! fi sleep 3 python session.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD python session.config.litenc.py --server=$NCSERVER --port=$NCPORT --user=$NCUSER --password=$NCPASSWORD kill -KILL $SERVER_PID cat tmp/server.log sleep 1 yuma123_2.14/netconf/test/netconfd/anyxml/test-anyxml.yang0000664000175000017500000000037014770023131024013 0ustar vladimirvladimirmodule test-anyxml { prefix test-anyxml; namespace "http://yuma123.org/ns/test/netconfd/anyxml/test-anyxml"; container c { anyxml a; } rpc ping-pong { input { anyxml ping; } output { anyxml pong; } } } yuma123_2.14/netconf/test/README.TXT0000664000175000017500000000662214770023131017102 0ustar vladimirvladimirThe Legacy Yuma Test Harness =============================================================================== ATTENTION: This file is relevant for the libboost-test based test framework which is the legacy test system which manages the testcases in test/integ-tests and test/sys-tests. The majority of testcases that are actively used are under test/netconfd and test/yangcli and do not use libboost-test. They are following the Autotools test suite script based convention and launched with the `make test` command. If you are interested in the legacy Yuma Test Harness keep reading. 1: Pre-requisites: Operating System Ubuntu 10.04 or later Compiler GCC 4.4 or later Boost Boost 1.47 or later Lib Xml libxml2 / libxml2-dev Python 2.6 Python interpreter Libbz2-dev 1.05-4ubuntu0.1 Doxygen Source code documentation tool Graphviz Graphical Visualisation Tool used by Doxygen Texlive-font-utils Text formatting used by doxygen Libncursesw5 5.7 Shared libraries for terminal handling libssh2-1-dev 1.2.2-1 SSH2 ssh2 client-side libary 2: The Integration Test Harness The Integration test harness is built as a stand alone executable that includes most of the Yuma agt and ncx sources (which make up the system under test). This test harness runs on the local host machine and can be used to quickly verify the behaviour of netconfd. To build and run this test harness: 1: cd to netconf/test 2: make 3: sudo make install 4: cd integ-tests 5: `mkdir yuma-op` directory used to store output 6: make check Note: tests can be run individually, by specifying the name of the test suite and test on the command line as follows: ./test-simple-edit-running \ --run_test=simple_get_test_suite_running/edit_slt_entries_test_delete For more information on test harness command line options see: http://www.boost.org/doc/libs/1_47_0/libs/test/doc/html/utf/user-guide/runtime-config.html 3: The System Test Harness The System test harness is a stand alone program that is capable of running full NETCONF sessions against a full Yuma/Netconf Server (the system under test). The System test harness provides a fast way of verifying the behaviour of a full Yuma/Netconf system. It behaves in the same way as a real Netconf client. To use this test harness the Netconf Server must have the appropriate YANG modules installed. The SIL modules for the YANG libraries can be loaded, but this is not required. To make sure the proper modules get loaded, either configure the netconfd --modpath parameter, set the YUMA_MODPATH environment variable, or put the YANG files a directory called 'modules' in your HOME directory. To build and run this test harness: 1: cd netconf/test 2: make 3: sudo make install 5: start the server and make sure at least these flags are set: --module=device_test --module=simple_list_test --module=simple_yang_test --access-control=off OR --superuser= --target=candidate OR --target=running --no-startup 6: cd sys-test 7: `mkdir yuma-op` directory used to store output 8: Configure the session variables: export YUMA_AGENT_IPADDR=127.0.0.1 export YUMA_AGENT_PORT=830 export YUMA_AGENT_USER=root export YUMA_AGENT_PASSWORD=**** 9: run either all tests with `make check` or individual tests e.g. ./test-shutdown yuma123_2.14/netconf/test/Makefile.am0000775000175000017500000001416514770023131017604 0ustar vladimirvladimir#Tests lib_LTLIBRARIES = libyumatest.la libyumatest_la_SOURCES = \ $(top_srcdir)/netconf/test/support/nc-query-util/yuma-op-policies.cpp \ $(top_srcdir)/netconf/test/support/nc-query-util/nc-default-operation-config.cpp \ $(top_srcdir)/netconf/test/support/nc-query-util/nc-query-test-engine.cpp \ $(top_srcdir)/netconf/test/support/nc-query-util/nc-strings.cpp \ $(top_srcdir)/netconf/test/support/nc-query-util/nc-query-utils.cpp \ $(top_srcdir)/netconf/test/support/nc-query-util/nc-base-query-test-engine.cpp \ $(top_srcdir)/netconf/test/support/checkers/log-entry-presence-checkers.cpp \ $(top_srcdir)/netconf/test/support/checkers/checker-group.cpp \ $(top_srcdir)/netconf/test/support/checkers/string-presence-checkers.cpp \ $(top_srcdir)/netconf/test/support/callbacks/callback-checker.cpp \ $(top_srcdir)/netconf/test/support/callbacks/sil-callback-controller.cpp \ $(top_srcdir)/netconf/test/support/callbacks/system-cb-checker.cpp \ $(top_srcdir)/netconf/test/support/callbacks/sil-callback-log.cpp \ $(top_srcdir)/netconf/test/support/callbacks/candidate-cb-checker.cpp \ $(top_srcdir)/netconf/test/support/callbacks/running-cb-checker.cpp \ $(top_srcdir)/netconf/test/support/callbacks/system-cb-checker-factory.cpp \ $(top_srcdir)/netconf/test/support/callbacks/integ-cb-checker-factory.cpp \ $(top_srcdir)/netconf/test/support/msg-util/xpo-query-builder.cpp \ $(top_srcdir)/netconf/test/support/msg-util/NCMessageBuilder.cpp \ $(top_srcdir)/netconf/test/support/msg-util/state-data-query-builder.cpp \ $(top_srcdir)/netconf/test/support/db-models/device-test-db-debug.cpp \ $(top_srcdir)/netconf/test/support/db-models/state-data-test-db.cpp \ $(top_srcdir)/netconf/test/support/db-models/device-test-db.cpp \ $(top_srcdir)/netconf/test/support/fixtures/device-module-fixture.cpp \ $(top_srcdir)/netconf/test/support/fixtures/state-data-module-common-fixture.cpp \ $(top_srcdir)/netconf/test/support/fixtures/integ-fixture-helper-factory.cpp \ $(top_srcdir)/netconf/test/support/fixtures/abstract-global-fixture.cpp \ $(top_srcdir)/netconf/test/support/fixtures/test-context.cpp \ $(top_srcdir)/netconf/test/support/fixtures/system-fixture-helper-factory.cpp \ $(top_srcdir)/netconf/test/support/fixtures/system-fixture-helper.cpp \ $(top_srcdir)/netconf/test/support/fixtures/integ-fixture-helper.cpp \ $(top_srcdir)/netconf/test/support/fixtures/state-get-module-fixture.cpp \ $(top_srcdir)/netconf/test/support/fixtures/simple-container-module-fixture.cpp \ $(top_srcdir)/netconf/test/support/fixtures/base-suite-fixture.cpp \ $(top_srcdir)/netconf/test/support/fixtures/query-suite-fixture.cpp \ $(top_srcdir)/netconf/test/support/fixtures/device-module-common-fixture.cpp \ $(top_srcdir)/netconf/test/support/fixtures/simple-yang-fixture.cpp \ $(top_srcdir)/netconf/test/support/fixtures/device-get-module-fixture.cpp \ $(top_srcdir)/netconf/test/support/nc-session/sys-test-nc-session-base.cpp \ $(top_srcdir)/netconf/test/support/nc-session/abstract-nc-session.cpp \ $(top_srcdir)/netconf/test/support/nc-session/remote-nc-session-factory.cpp \ $(top_srcdir)/netconf/test/support/nc-session/remote-nc-session.cpp \ $(top_srcdir)/netconf/test/support/nc-session/spoof-nc-session.cpp \ $(top_srcdir)/netconf/test/support/nc-session/spoof-nc-session-factory.cpp \ $(top_srcdir)/netconf/test/support/misc-util/cpp-unit-op-formatter.cpp \ $(top_srcdir)/netconf/test/support/misc-util/log-utils.cpp \ $(top_srcdir)/netconf/test/support/misc-util/ptree-utils.cpp \ $(top_srcdir)/netconf/test/support/misc-util/base64.cpp libyumatest_la_CPPFLAGS = $(LIBXML2_CFLAGS) -DLIBXML2_ENABLED -DBOOST_TEST_DYN_LINK -std=c++0x -I $(top_srcdir)/netconf -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump libyumatest_la_LDFLAGS=-lboost_unit_test_framework $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) -lssh2 -lncurses #Test modules - YANGs yang_DATA = \ $(top_srcdir)/netconf/test/modules/yang/simple_yang_test.yang \ $(top_srcdir)/netconf/test/modules/yang/simple_list_test.yang \ $(top_srcdir)/netconf/test/modules/yang/device_test.yang #Test modules - SILs netconfmodule_LTLIBRARIES = libsimple_yang_test.la libsimple_yang_test_la_SOURCES = \ $(top_srcdir)/netconf/test/modules/build-sil/simple_yang_test/simple_yang_test.cpp libsimple_yang_test_la_CPPFLAGS = $(LIBXML2_CFLAGS) -DLIBXML2_ENABLED -I $(top_srcdir)/netconf -I $(top_srcdir)/libtoaster/src/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump libsimple_yang_test_la_LDFLAGS = -module $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) netconfmodule_LTLIBRARIES+=libsimple_list_test.la libsimple_list_test_la_SOURCES = \ $(top_srcdir)/netconf/test/modules/build-sil/simple_list_test/simple_list_test.cpp libsimple_list_test_la_CPPFLAGS = $(LIBXML2_CFLAGS) -DLIBXML2_ENABLED -I $(top_srcdir)/netconf -I $(top_srcdir)/libtoaster/src/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump libsimple_list_test_la_LDFLAGS = -module $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) netconfmodule_LTLIBRARIES+=libdevice_test.la libdevice_test_la_SOURCES = \ $(top_srcdir)/netconf/test/modules/build-sil/device_test/device_test.cpp libdevice_test_la_CPPFLAGS = $(LIBXML2_CFLAGS) -DLIBXML2_ENABLED -I $(top_srcdir)/netconf -I $(top_srcdir)/libtoaster/src/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump libdevice_test_la_LDFLAGS = -module $(top_builddir)/netconf/test/libyumatest.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lz $(LIBS) yuma123_2.14/netconf/test/platform-validation-scripts/0000775000175000017500000000000014770023131023177 5ustar vladimirvladimiryuma123_2.14/netconf/test/platform-validation-scripts/debian-wo-pkgs.sh0000775000175000017500000000412714770023131026351 0ustar vladimirvladimir#!/bin/bash -e apt-get update apt-get -y upgrade apt-get -y install git devscripts #getting/building/installing cd ~ if [ -d yuma123-git ] ; then cd yuma123-git else git clone https://git.code.sf.net/p/yuma123/git yuma123-git fi cd yuma123-git sudo mk-build-deps -i -t 'apt-get -y' git clean -f autoreconf -i -f ./configure --prefix=/usr make sudo make install cd netconf/python sudo mk-build-deps -i -t 'apt-get -y' autoreconf -i -f ./configure --prefix=/usr make sudo make install #testing ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys ssh-keyscan -t rsa -H localhost >> ~/.ssh/known_hosts echo "PermitRootLogin yes" >> /etc/ssh/sshd_config echo "Port 22" >> /etc/ssh/sshd_config echo "Port 830" >> /etc/ssh/sshd_config echo "Port 1830" >> /etc/ssh/sshd_config echo "Port 2830" >> /etc/ssh/sshd_config echo "Port 3830" >> /etc/ssh/sshd_config echo "Port 4830" >> /etc/ssh/sshd_config echo "Port 5830" >> /etc/ssh/sshd_config echo "Port 6830" >> /etc/ssh/sshd_config echo 'Subsystem netconf "/usr/sbin/netconf-subsystem --ncxserver-sockname=830@/tmp/ncxserver.sock --ncxserver-sockname=1830@/tmp/ncxserver.1830.sock --ncxserver-sockname=2830@/tmp/ncxserver.2830.sock --ncxserver-sockname=3830@/tmp/ncxserver.3830.sock --ncxserver-sockname=4830@/tmp/ncxserver.4830.sock --ncxserver-sockname=5830@/tmp/ncxserver.5830.sock --ncxserver-sockname=6830@/tmp/ncxserver.6830.sock"' >> /etc/ssh/sshd_config /etc/init.d/ssh restart cd ~/yuma123-git/netconf/test/netconfd apt-get -y install python-ncclient valgrind autoreconf -i -f ./configure --prefix=/usr make make install make check || true cd ~/yuma123-git/netconf/test/yangcli apt-get -y install expect autoreconf -i -f ./configure --prefix=/usr make make install make check || true cd ~/yuma123_${ver}/netconf/test/yangdump autoreconf -i -f ./configure --prefix=/usr make make install make check || true cd ~/yuma123-git/netconf/src/yangrpc/libapache2-mod-yangrpc-example/ apt-get -y install apache2-dev apache2 autoreconf -i -f ./configure --prefix=/usr make make install echo "Success!" yuma123_2.14/netconf/test/platform-validation-scripts/README0000664000175000017500000000050514770023131024057 0ustar vladimirvladimirThe are 3 options for installing on Debian.: 1. debian-wo-pkgs.sh - pulling from git and compiling using automake tools without debian packages 2. debian.sh - pulling from git and building using the debian package development tools 3. debian-apt-repo.sh - installing precompiled packages from the yuma123 online repository. yuma123_2.14/netconf/test/platform-validation-scripts/debian.sh0000775000175000017500000001054514770023131024765 0ustar vladimirvladimir#!/bin/bash -e apt-get update apt-get -y upgrade apt-get -y install git devscripts \ equivs #getting/building/installing cd ~ if [ -d yuma123-git ] ; then echo "Using preexisting yuma123-git" else git clone https://git.code.sf.net/p/yuma123/git yuma123-git fi cd yuma123-git dpkg_ver=`dpkg-parsechangelog --show-field Version` #deb package version e.g. 2.10-0 echo "dpkg_ver=${dpkg_ver}" #strip the deb package revision e.g. 2.10-0 -> 2.10 ver=`echo ${dpkg_ver} | sed 's/-[^-]*//'` mk-build-deps -i -t 'apt-get -y' git clean -f cd .. ln -s yuma123-git yuma123_${ver} tar -czvf yuma123_${ver}.orig.tar.gz --exclude .git yuma123-git cd yuma123_${ver} debuild -us -uc dpkg -i ../*.deb #build and install python3-yuma (used in the testsuite) cd ~ apt-get -y install rsync rsync -rav yuma123_${ver}/netconf/python/ yuma123-python_${ver} tar -czvf yuma123-python_${ver}.orig.tar.gz --exclude .git yuma123-python_${ver} cd yuma123-python_${ver} mk-build-deps -i -t 'apt-get -y' cd .. rm -rf yuma123-python_${ver} tar -xzvf yuma123-python_${ver}.orig.tar.gz cd yuma123-python_${ver} debuild -us -uc apt-get -y install python3-paramiko python3-lxml dpkg -i ../python3-yuma*.deb cd ~ rsync -rav yuma123_${ver}/example-modules/ietf-traffic-generator/ yuma123-netconfd-module-ietf-traffic-generator_${ver} tar -czvf yuma123-netconfd-module-ietf-traffic-generator_${ver}.orig.tar.gz --exclude .git yuma123-netconfd-module-ietf-traffic-generator_${ver} cd yuma123-netconfd-module-ietf-traffic-generator_${ver} mk-build-deps -i -t 'apt-get -y' cd .. rm -rf yuma123-netconfd-module-ietf-traffic-generator_${ver} tar -xzvf yuma123-netconfd-module-ietf-traffic-generator_${ver}.orig.tar.gz cd yuma123-netconfd-module-ietf-traffic-generator_${ver} debuild -us -uc dpkg -i ../*.deb #testing apt-get -y install openssh-client openssh-server ssh-keygen -t rsa -b 4096 -m PEM -f ~/.ssh/id_rsa -N "" cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys echo "PermitRootLogin yes" >> /etc/ssh/sshd_config echo "PubkeyAcceptedAlgorithms=+ssh-rsa" >> /etc/ssh/sshd_config echo "Port 22" >> /etc/ssh/sshd_config echo "Port 830" >> /etc/ssh/sshd_config echo "Port 1830" >> /etc/ssh/sshd_config echo "Port 2830" >> /etc/ssh/sshd_config echo "Port 3830" >> /etc/ssh/sshd_config echo "Port 4830" >> /etc/ssh/sshd_config echo "Port 5830" >> /etc/ssh/sshd_config echo "Port 6830" >> /etc/ssh/sshd_config echo 'Subsystem netconf "/usr/sbin/netconf-subsystem --ncxserver-sockname=830@/tmp/ncxserver.sock --ncxserver-sockname=1830@/tmp/ncxserver.1830.sock --ncxserver-sockname=2830@/tmp/ncxserver.2830.sock --ncxserver-sockname=3830@/tmp/ncxserver.3830.sock --ncxserver-sockname=4830@/tmp/ncxserver.4830.sock --ncxserver-sockname=5830@/tmp/ncxserver.5830.sock --ncxserver-sockname=6830@/tmp/ncxserver.6830.sock"' >> /etc/ssh/sshd_config /etc/init.d/ssh restart || true ssh-keyscan -t rsa -H localhost >> ~/.ssh/known_hosts apt-get -y install wget mkdir -p ~/bin wget https://raw.githubusercontent.com/mbj4668/rfcstrip/refs/heads/master/rfcstrip -O ~/bin/rfcstrip chmod ugo+x ~/bin/rfcstrip export PATH=$PATH:~/bin cd ~/yuma123_${ver}/netconf/test/netconfd apt-get -y install python3-ncclient valgrind apt-get -y install python-is-python3 apt-get -y install psmisc # contains killall multiarch=$(dpkg-architecture -q DEB_BUILD_MULTIARCH) prefix="/usr" CONFIGURE_FLAGS="--prefix=/${prefix} --libdir=${prefix}/lib/${multiarch} --libexecdir=${prefix}/lib/${multiarch}" autoreconf -i -f ./configure ${CONFIGURE_FLAGS} make make install make check || true cd ~/yuma123_${ver}/netconf/test/yangcli apt-get -y install expect autoreconf -i -f ./configure ${CONFIGURE_FLAGS} make make install make check || true cd ~/yuma123_${ver}/netconf/test/yangdump autoreconf -i -f ./configure ${CONFIGURE_FLAGS} make make install make check || true cd ~/yuma123-git/netconf/src/yangrpc/libapache2-mod-yangrpc-example/ dpkg_ver=`dpkg-parsechangelog --show-field Version` #deb package version e.g. 2.10-0 echo "dpkg_ver=${dpkg_ver}" #strip the deb package revision e.g. 2.10-0 -> 2.10 ver=`echo ${dpkg_ver} | sed 's/-[^-]*//'` mk-build-deps -i -t 'apt-get -y' git clean -f cd .. ln -s libapache2-mod-yangrpc-example libapache2-mod-yangrpc-example_${ver} tar -czvf libapache2-mod-yangrpc-example_${ver}.orig.tar.gz --exclude .git libapache2-mod-yangrpc-example cd libapache2-mod-yangrpc-example_${ver} debuild -us -uc apt-get -y install apache2 dpkg -i ../*.deb echo "Success!" yuma123_2.14/netconf/test/platform-validation-scripts/debian-apt-repo.sh0000775000175000017500000000434214770023131026510 0ustar vladimirvladimir#!/bin/bash -e apt-get update apt-get -y upgrade apt-get -y install software-properties-common add-apt-repository -s "deb http://yuma123.org/repos/apt/debian sid main" apt-get -y install wget gnupg wget -O - http://yuma123.org/repos/yuma123.gpg.key | apt-key add - apt-get update apt-get -y install netconfd yangcli netconfd-module-ietf-interfaces netconfd-module-ietf-system apt-get -y install python-yuma || true apt-get -y install libapache2-mod-yangrpc-example || true #testing ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys ssh-keyscan -t rsa -H localhost >> ~/.ssh/known_hosts echo "PermitRootLogin yes" >> /etc/ssh/sshd_config echo "Port 22" >> /etc/ssh/sshd_config echo "Port 830" >> /etc/ssh/sshd_config echo "Port 1830" >> /etc/ssh/sshd_config echo "Port 2830" >> /etc/ssh/sshd_config echo "Port 3830" >> /etc/ssh/sshd_config echo "Port 4830" >> /etc/ssh/sshd_config echo "Port 5830" >> /etc/ssh/sshd_config echo "Port 6830" >> /etc/ssh/sshd_config echo 'Subsystem netconf "/usr/sbin/netconf-subsystem --ncxserver-sockname=830@/tmp/ncxserver.sock --ncxserver-sockname=1830@/tmp/ncxserver.1830.sock --ncxserver-sockname=2830@/tmp/ncxserver.2830.sock --ncxserver-sockname=3830@/tmp/ncxserver.3830.sock --ncxserver-sockname=4830@/tmp/ncxserver.4830.sock --ncxserver-sockname=5830@/tmp/ncxserver.5830.sock --ncxserver-sockname=6830@/tmp/ncxserver.6830.sock"' >> /etc/ssh/sshd_config /etc/init.d/ssh restart #without dpkg-dev, apt-get source fails! apt-get -y install dpkg-dev apt-get -y install libyuma-dev apt-get -y install autoconf libtool libxml2-dev apt-get source yuma123 cd ~/yuma123-*/netconf/test/netconfd apt-get -y install python-ncclient valgrind multiarch=$(dpkg-architecture -q DEB_BUILD_MULTIARCH) prefix="/usr" CONFIGURE_FLAGS="--prefix=/${prefix} --libdir=${prefix}/lib/${multiarch} --libexecdir=${prefix}/lib/${multiarch}" autoreconf -i -f ./configure ${CONFIGURE_FLAGS} make make install make check || true cd ~/yuma123-*/netconf/test/yangcli apt-get -y install expect autoreconf -i -f ./configure ${CONFIGURE_FLAGS} make make install make check || true cd ~/yuma123-*/netconf/test/yangdump autoreconf -i -f ./configure ${CONFIGURE_FLAGS} make make install make check || true echo "Success!" yuma123_2.14/netconf/test/sys-test-python/0000775000175000017500000000000014770023131020650 5ustar vladimirvladimiryuma123_2.14/netconf/test/sys-test-python/ietf-interfaces/0000775000175000017500000000000014770023131023720 5ustar vladimirvladimiryuma123_2.14/netconf/test/sys-test-python/ietf-interfaces/test-if-names-present.py0000775000175000017500000000537414770023131030440 0ustar vladimirvladimir#!/usr/bin/python import sys, os import netconf import time from xml.dom.minidom import parseString import libxml2 def detect_rpc_error(reply_xml): #... # # dom = parseString(reply_xml) assert dom.documentElement.tagName == "rpc-reply" rpc_error = dom.getElementsByTagName("rpc-error") if len(rpc_error) == 0: return False return True def main(): print(""" #Demonstrate with xpath filter returns keys for each list container part of the result ##Procedure: ##1 - Call with /interfaces-state/interface/statistics xpath filter ##2 - Verify each /interfaces-state/interface container returned has 'name' key """) server=os.environ.get('YUMA_AGENT_IPADDR') #e.g. "192.168.209.31" port=os.environ.get('YUMA_AGENT_PORT') #e.g. "830" user=os.environ.get('YUMA_AGENT_USER') #e.g. "root" password=os.environ.get('YUMA_AGENT_PASSWORD') #e.g. "hadm1_123" my_netconf = netconf.netconf() sys.stderr.write("Connect to (server=%(server)s):\n" % {'server':server}) ret=my_netconf.connect("server=%(server)s port=%(port)s user=%(user)s password=%(password)s" % {'server':server,'port':port,'user':user,'password':password}) if ret != 0: sys.stderr.write("Connect: FAILED\n") return (-1) (ret, reply_xml) = my_netconf.rpc(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: sys.stderr.write("Hello: FAILED\n") return (-1) rpc = """ """ (ret, reply_xml) = my_netconf.rpc(rpc) if ret != 0: sys.stderr.write("Create: FAILED\n") return (-1) if detect_rpc_error(reply_xml): sys.stderr.write("rpc_reply contains rpc-error: FAILED\n") sys.stderr.write(reply_xml) return (-1) doc = libxml2.parseDoc(reply_xml) ctxt = doc.xpathNewContext() ctxt.xpathRegisterNs("nc","urn:ietf:params:xml:ns:netconf:base:1.0") ctxt.xpathRegisterNs("if","urn:ietf:params:xml:ns:yang:ietf-interfaces") res_interface = ctxt.xpathEval('/nc:rpc-reply/nc:data/if:interfaces-state/if:interface') res_name = ctxt.xpathEval('/nc:rpc-reply/nc:data/if:interfaces-state/if:interface/if:name') if len(res_interface) != len(res_name): sys.stderr.write("The count of interface containers does not match the count of the interface/name keys\n") sys.stderr.write(reply_xml) return(-1) return 0 sys.exit(main()) yuma123_2.14/netconf/test/sys-test-python/test-shutdown.py0000775000175000017500000000301414770023131024053 0ustar vladimirvladimir#!/usr/bin/python import sys, os import netconf import time def main(): print(""" Demonstrate shutdown operation (WARNING: netconf server must be manualy restarted following this test). #Procedure: #1 - Shutdown #2 - Try another query #3 - Check that no response is received """) server=os.environ.get('YUMA_AGENT_IPADDR') #e.g. "192.168.209.31" port=os.environ.get('YUMA_AGENT_PORT') #e.g. "830" user=os.environ.get('YUMA_AGENT_USER') #e.g. "root" password=os.environ.get('YUMA_AGENT_PASSWORD') #e.g. "hadm1_123" my_netconf = netconf.netconf() sys.stderr.write("Connect to (server=%(server)s):\n" % {'server':server}) ret=my_netconf.connect("server=%(server)s port=%(port)s user=%(user)s password=%(password)s" % {'server':server,'port':port,'user':user,'password':password}) if ret != 0: sys.stderr.write("Connect: FAILED\n") return (-1) (ret, reply_xml) = my_netconf.rpc(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: sys.stderr.write("Hello: FAILED\n") return (-1) shutdown_rpc = """ """ (ret, reply_xml) = my_netconf.rpc(shutdown_rpc) if ret != 0: sys.stderr.write("Shutdown: FAILED\n") return (-1) time.sleep(1) (ret, reply_xml) = my_netconf.rpc(shutdown_rpc) if ret == 0: sys.stderr.write("Shutdown did not take effect: FAILED\n") return (-1) return 0 sys.exit(main()) yuma123_2.14/netconf/test/sys-test-python/ietf-system/0000775000175000017500000000000014770023131023121 5ustar vladimirvladimiryuma123_2.14/netconf/test/sys-test-python/ietf-system/test-system-restart.py0000775000175000017500000000606314770023131027466 0ustar vladimirvladimir#!/usr/bin/python import sys, os import netconf import time from xml.dom.minidom import parseString import libxml2 def detect_rpc_error(reply_xml): #... # # dom = parseString(reply_xml) assert dom.documentElement.tagName == "rpc-reply" rpc_error = dom.getElementsByTagName("rpc-error") if len(rpc_error) == 0: return False return True def main(): print(""" Demonstrate ietf-system:system-restart RPC operation #Procedure: #1 - Send ietf-system:system-restart RPC. #2 - Confirm there is no error. #3 - Immediately attempt new connection. #4 - Confirm the attempt fails. #5 - Keep attempting to connect for up to 120 sec. #6 - Confirm the attempt succeeds. """) server=os.environ.get('YUMA_AGENT_IPADDR') #e.g. "192.168.209.31" port=os.environ.get('YUMA_AGENT_PORT') #e.g. "830" user=os.environ.get('YUMA_AGENT_USER') #e.g. "root" password=os.environ.get('YUMA_AGENT_PASSWORD') #e.g. "hadm1_123" my_netconf = netconf.netconf() sys.stderr.write("Connect to (server=%(server)s):\n" % {'server':server}) ret=my_netconf.connect("server=%(server)s port=%(port)s user=%(user)s password=%(password)s" % {'server':server,'port':port,'user':user,'password':password}) if ret != 0: sys.stderr.write("Connect: FAILED\n") return (-1) (ret, reply_xml) = my_netconf.rpc(""" urn:ietf:params:netconf:base:1.0 """) if ret != 0: sys.stderr.write("Hello: FAILED\n") return (-1) system_restart_rpc = """ """ (ret, reply_xml) = my_netconf.rpc(system_restart_rpc) if ret != 0: sys.stderr.write("Restart: FAILED\n") return (-1) if detect_rpc_error(reply_xml): sys.stderr.write("rpc_reply contains rpc-error: FAILED\n") sys.stderr.write(reply_xml) return (-1) time.sleep(1) (ret, reply_xml) = my_netconf.rpc(system_restart_rpc) if ret == 0: sys.stderr.write("Restart did not take effect: FAILED\n") return (-1) time.sleep(10) my_netconf = netconf.netconf() sys.stderr.write("Connect to (server=%(server)s):\n" % {'server':server}) ret=my_netconf.connect("server=%(server)s port=%(port)s user=%(user)s password=%(password)s" % {'server':server,'port':port,'user':user,'password':password}) if ret == 0: sys.stderr.write("Connect should have not succeeded: FAILED\n") return (-1) start = time.time() while((time.time()-start)<120): my_netconf = netconf.netconf() sys.stderr.write("Connect to (server=%(server)s):\n" % {'server':server}) ret=my_netconf.connect("server=%(server)s port=%(port)s user=%(user)s password=%(password)s" % {'server':server,'port':port,'user':user,'password':password}) if ret == 0: sys.stdout.write("Restart completed in %(seconds)d sec.\n" % {'seconds':time.time()-start}) return 0 sys.stderr.write("Timeout!\n") return(-1) sys.exit(main()) yuma123_2.14/netconf/test/sys-test-python/Makefile.am0000664000175000017500000000017714770023131022711 0ustar vladimirvladimirTESTS=ietf-interfaces/test-if-names-present.py \ ietf-system/test-system-restart.py #test-shutdown.py check_SCRIPTS=${TESTS} yuma123_2.14/netconf/test/modules/0000775000175000017500000000000014770023131017206 5ustar vladimirvladimiryuma123_2.14/netconf/test/modules/yang/0000775000175000017500000000000014770023131020144 5ustar vladimirvladimiryuma123_2.14/netconf/test/modules/yang/device_test.yang0000664000175000017500000001703414770023131023327 0ustar vladimirvladimirmodule device_test { namespace "http://netconfcentral.org/ns/device_test"; prefix "dt"; revision 2011-09-07 { description "Initial revision."; } grouping resourceDescription { description "Common description of resources."; list resourceNode { key id; leaf id { description "Unique ID for the resource."; type uint32; } leaf resourceType { description "Type identifier for resource."; type uint32; } leaf channelId { description "Internal comms channel identifier. Automatically set, can not be set or adjusted by users."; type uint32; } leaf configuration { description "Base-64 encoded binary configuration."; type binary; } leaf statusConfig { description "Base-64 encoded binary configuration of status reporting."; type binary; } leaf alarmConfig { description "Base-64 encoded binary configuration of alarm reporting."; type binary; } leaf physicalPath { description "Fixed physical path for the resource."; type string; } } } container xpo { presence "Indicates that the device test API is available."; description "Top-level container for all configuration and status objects."; //////////////////////////////////// // Start of main configuration block //////////////////////////////////// grouping connectionItem { description "Connection container."; leaf sourceId { description "The ID of the item providing the input to the connection."; type uint32; } leaf sourcePinId { description "The pin ID of the source item to use."; type uint32; } leaf destinationId { description "The ID of the item accepting the output of the connection."; type uint32; } leaf destinationPinId { description "The pin ID of the destination item out to use."; type uint32; } leaf bitrate { description "The maximum expected bitrate over this connection."; type uint32; units "bps"; } } grouping streamItem { description "Stream container."; uses resourceDescription; list resourceConnection { description "Connection between two virtual resources."; key id; leaf id { description "Connection identifier."; type uint32; } uses connectionItem; } } list profile { key id; description "Profile container."; leaf id { description "Unique ID for this profile."; type uint32; } list stream { description "Stream container."; key id; leaf id { description "Connection identifier."; type uint32; } uses streamItem; } list streamConnection { description "Connection between two streams."; key id; leaf id { description "Connection identifier."; type uint32; } leaf sourceStreamId { description "The ID of the stream providing the input to the connection."; type uint32; } leaf destinationStreamId { description "The ID of the stream accepting the output of the connection."; type uint32; } uses connectionItem; } } leaf activeProfile { description "The number of the active profile."; type uint32; } } typedef rpcResult { description "Result codes for XPO3 RPCs."; type enumeration { enum success { description "The operation was successful."; } enum invalidSourceProfileId { description "The operation failed due to the source profile ID being invalid."; } enum invalidDestinationProfileId { description "The operation failed due to the destination profile ID being invalid."; } enum invalidDestinationStreamId { description "The operation failed due to the destination stream ID being invalid."; } enum overwriteFailed { description "The operation failed because there was a pre-existing profile or stream in the requested destination, and the replace existing flag was set to false."; } enum routingFailed { description "As the result of a copy of modify operation, the profile can no longer be routed."; } enum validationFailed { description "As the result of a copy of modify operation, the profile can no longer be validated."; } enum invalidTime { description "The time specified was invalid or could not be parsed."; } enum unknownLanguage { description "The language specified is unknown."; } enum dictionaryReferenceNotFound { description "The dictionary reference requested was not found."; } } } rpc copy-profile { description "Allows a profile to be copied."; input { leaf sourceProfileId { description "The ID of the profile to copy."; type uint32; } leaf destinationProfileId { description "The ID to copy the profile to."; type uint32; } leaf replaceExisting { description "Indicates if the copy operation is able to replace an existing profile."; type boolean; } } output { leaf copyResult { description "The result of the copy operation."; type rpcResult; } } } } yuma123_2.14/netconf/test/modules/yang/simple_list_test.yang0000664000175000017500000000117614770023131024414 0ustar vladimirvladimirmodule simple_list_test { namespace "http://netconfcentral.org/ns/simple_list_test"; prefix "slt"; revision 2008-11-20 { description "Initial revision."; } container simple_list { list theList { key theKey; leaf theKey { type string; } leaf theVal { type string; } } } rpc inc-counter { description "Increments a counter."; } rpc get-counter { description "Gets the current count."; output { leaf count { description "The current count."; type uint32; } } } } yuma123_2.14/netconf/test/modules/yang/simple_yang_test.yang0000664000175000017500000000166014770023131024375 0ustar vladimirvladimirmodule simple_yang_test { namespace "http://netconfcentral.org/ns/simple_yang_test"; prefix "syt"; revision 2011-11-21 { description "Initial revision."; } container protocol { choice name { case udp { leaf udp { type empty; } } case tcp { leaf tcp { type empty; } } } } container interface { leaf ifType { type enumeration { enum ethernet; enum atm; } } leaf ifMTU { type uint32; } must "ifType != 'ethernet' or " + "(ifType = 'ethernet' and ifMTU = 1500)" { error-message "An ethernet MTU must be 1500"; } must "ifType != 'atm' or " + "(ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)" { error-message "An atm MTU must be between 64 and 17966"; } } } yuma123_2.14/netconf/test/modules/build-sil/0000775000175000017500000000000014770023131021072 5ustar vladimirvladimiryuma123_2.14/netconf/test/modules/build-sil/simple_yang_test/0000775000175000017500000000000014770023131024440 5ustar vladimirvladimiryuma123_2.14/netconf/test/modules/build-sil/simple_yang_test/simple_yang_test.h0000664000175000017500000000640514770023131030164 0ustar vladimirvladimir #ifndef _H_simple_yang_test #define _H_simple_yang_test /* * Copyright (c) 2009 - 2011, Andy Bierman * All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *** Generated by yangdump 2.1.1630 Combined SIL header module simple_yang_test revision 2011-11-21 namespace http://netconfcentral.org/ns/simple_yang_test */ #include "dlq.h" #include "ncxtypes.h" #include "op.h" #include "status.h" #include "val.h" #ifdef __cplusplus extern "C" { #endif #define y_simple_yang_test_M_simple_yang_test (const xmlChar *)"simple_yang_test" #define y_simple_yang_test_R_simple_yang_test (const xmlChar *)"2011-11-21" #define y_simple_yang_test_N_ifMTU (const xmlChar *)"ifMTU" #define y_simple_yang_test_N_ifType (const xmlChar *)"ifType" #define y_simple_yang_test_N_interface (const xmlChar *)"interface" #define y_simple_yang_test_N_name (const xmlChar *)"name" #define y_simple_yang_test_N_protocol (const xmlChar *)"protocol" #define y_simple_yang_test_N_tcp (const xmlChar *)"tcp" #define y_simple_yang_test_N_udp (const xmlChar *)"udp" /* case /protocol/name/udp */ typedef struct y_simple_yang_test_T_protocol_name_udp_ { boolean udp; } y_simple_yang_test_T_protocol_name_udp; /* case /protocol/name/tcp */ typedef struct y_simple_yang_test_T_protocol_name_tcp_ { boolean tcp; } y_simple_yang_test_T_protocol_name_tcp; /* choice /protocol/name */ typedef union y_simple_yang_test_T_protocol_name_ { y_simple_yang_test_T_protocol_name_udp udp; y_simple_yang_test_T_protocol_name_tcp tcp; } y_simple_yang_test_T_protocol_name; /* container /protocol */ typedef struct y_simple_yang_test_T_protocol_ { y_simple_yang_test_T_protocol_name name; } y_simple_yang_test_T_protocol; /* container /interface */ typedef struct y_simple_yang_test_T_interface_ { xmlChar *ifType; uint32 ifMTU; } y_simple_yang_test_T_interface; /******************************************************************** * FUNCTION y_simple_yang_test_init * * initialize the simple_yang_test server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ extern status_t y_simple_yang_test_init ( const xmlChar *modname, const xmlChar *revision); /******************************************************************** * FUNCTION y_simple_yang_test_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ extern status_t y_simple_yang_test_init2 (void); /******************************************************************** * FUNCTION y_simple_yang_test_cleanup * cleanup the server instrumentation library * ********************************************************************/ extern void y_simple_yang_test_cleanup (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif yuma123_2.14/netconf/test/modules/build-sil/simple_yang_test/simple_yang_test.c0000664000175000017500000004466114770023131030165 0ustar vladimirvladimir /* * Copyright (c) 2009 - 2011, Andy Bierman * All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *** Generated by yangdump 2.1.1630 Combined SIL module module simple_yang_test revision 2011-11-21 namespace http://netconfcentral.org/ns/simple_yang_test */ #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "simple_yang_test.h" /* module static variables */ static ncx_module_t *simple_yang_test_mod; static obj_template_t *protocol_obj; static obj_template_t *interface_obj; static val_value_t *protocol_val; static val_value_t *interface_val; /* put your static variables here */ /******************************************************************** * FUNCTION y_simple_yang_test_init_static_vars * * initialize module static variables * ********************************************************************/ static void y_simple_yang_test_init_static_vars (void) { simple_yang_test_mod = NULL; protocol_obj = NULL; interface_obj = NULL; protocol_val = NULL; interface_val = NULL; /* init your static variables here */ } /* y_simple_yang_test_init_static_vars */ /******************************************************************** * FUNCTION simple_yang_test_protocol_name_udp_udp_edit * * Edit database object callback * Path: /protocol/name/udp/udp * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_yang_test_protocol_name_udp_udp_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_yang_test_protocol_name_udp_udp_edit callback for %s phase", agt_cbtype_name(cbtyp)); } switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_yang_test_protocol_name_udp_udp_edit */ /******************************************************************** * FUNCTION simple_yang_test_protocol_name_tcp_tcp_edit * * Edit database object callback * Path: /protocol/name/tcp/tcp * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_yang_test_protocol_name_tcp_tcp_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_yang_test_protocol_name_tcp_tcp_edit callback for %s phase", agt_cbtype_name(cbtyp)); } switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_yang_test_protocol_name_tcp_tcp_edit */ /******************************************************************** * FUNCTION simple_yang_test_protocol_edit * * Edit database object callback * Path: /protocol * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_yang_test_protocol_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_yang_test_protocol_edit callback for %s phase", agt_cbtype_name(cbtyp)); } switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res == NO_ERR) { res = agt_check_cache(&protocol_val, newval, curval, editop); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_yang_test_protocol_edit */ /******************************************************************** * FUNCTION simple_yang_test_interface_ifType_edit * * Edit database object callback * Path: /interface/ifType * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_yang_test_interface_ifType_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_yang_test_interface_ifType_edit callback for %s phase", agt_cbtype_name(cbtyp)); } switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_yang_test_interface_ifType_edit */ /******************************************************************** * FUNCTION simple_yang_test_interface_ifMTU_edit * * Edit database object callback * Path: /interface/ifMTU * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_yang_test_interface_ifMTU_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_yang_test_interface_ifMTU_edit callback for %s phase", agt_cbtype_name(cbtyp)); } switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_yang_test_interface_ifMTU_edit */ /******************************************************************** * FUNCTION simple_yang_test_interface_edit * * Edit database object callback * Path: /interface * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_yang_test_interface_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_yang_test_interface_edit callback for %s phase", agt_cbtype_name(cbtyp)); } switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res == NO_ERR) { res = agt_check_cache(&interface_val, newval, curval, editop); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_yang_test_interface_edit */ /******************************************************************** * FUNCTION y_simple_yang_test_init * * initialize the simple_yang_test server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ status_t y_simple_yang_test_init ( const xmlChar *modname, const xmlChar *revision) { status_t res = NO_ERR; agt_profile_t *agt_profile; y_simple_yang_test_init_static_vars(); /* change if custom handling done */ if (xml_strcmp(modname, y_simple_yang_test_M_simple_yang_test)) { return ERR_NCX_UNKNOWN_MODULE; } if (revision && xml_strcmp(revision, y_simple_yang_test_R_simple_yang_test)) { return ERR_NCX_WRONG_VERSION; } agt_profile = agt_get_profile(); res = ncxmod_load_module( y_simple_yang_test_M_simple_yang_test, y_simple_yang_test_R_simple_yang_test, &agt_profile->agt_savedevQ, &simple_yang_test_mod); if (res != NO_ERR) { return res; } protocol_obj = ncx_find_object( simple_yang_test_mod, y_simple_yang_test_N_protocol); if (simple_yang_test_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } interface_obj = ncx_find_object( simple_yang_test_mod, y_simple_yang_test_N_interface); if (simple_yang_test_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } res = agt_cb_register_callback( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/interface", (const xmlChar *)"2011-11-21", simple_yang_test_interface_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/interface/ifMTU", (const xmlChar *)"2011-11-21", simple_yang_test_interface_ifMTU_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/interface/ifType", (const xmlChar *)"2011-11-21", simple_yang_test_interface_ifType_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/protocol", (const xmlChar *)"2011-11-21", simple_yang_test_protocol_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/protocol/name/tcp/tcp", (const xmlChar *)"2011-11-21", simple_yang_test_protocol_name_tcp_tcp_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/protocol/name/udp/udp", (const xmlChar *)"2011-11-21", simple_yang_test_protocol_name_udp_udp_edit); if (res != NO_ERR) { return res; } /* put your module initialization code here */ return res; } /* y_simple_yang_test_init */ /******************************************************************** * FUNCTION y_simple_yang_test_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ status_t y_simple_yang_test_init2 (void) { status_t res = NO_ERR; interface_val = agt_init_cache( y_simple_yang_test_M_simple_yang_test, y_simple_yang_test_N_interface, &res); if (res != NO_ERR) { return res; } protocol_val = agt_init_cache( y_simple_yang_test_M_simple_yang_test, y_simple_yang_test_N_protocol, &res); if (res != NO_ERR) { return res; } /* put your init2 code here */ return res; } /* y_simple_yang_test_init2 */ /******************************************************************** * FUNCTION y_simple_yang_test_cleanup * cleanup the server instrumentation library * ********************************************************************/ void y_simple_yang_test_cleanup (void) { agt_cb_unregister_callbacks( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/interface"); agt_cb_unregister_callbacks( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/interface/ifMTU"); agt_cb_unregister_callbacks( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/interface/ifType"); agt_cb_unregister_callbacks( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/protocol"); agt_cb_unregister_callbacks( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/protocol/name/tcp/tcp"); agt_cb_unregister_callbacks( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/protocol/name/udp/udp"); /* put your cleanup code here */ } /* y_simple_yang_test_cleanup */ /* END simple_yang_test.c */ yuma123_2.14/netconf/test/modules/build-sil/simple_yang_test/simple_yang_test.cpp0000664000175000017500000006321314770023131030517 0ustar vladimirvladimir /* * Copyright (c) 2009 - 2011, Andy Bierman * All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *** Generated by yangdump 2.1.1630 Combined SIL module module simple_yang_test revision 2011-11-21 namespace http://netconfcentral.org/ns/simple_yang_test */ //extern "C" { #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "simple_yang_test.h" //} #include "test/support/callbacks/sil-callback-log.h" /* module static variables */ static ncx_module_t *simple_yang_test_mod; static obj_template_t *protocol_obj; static obj_template_t *interface_obj; static val_value_t *protocol_val; static val_value_t *interface_val; /* put your static variables here */ /******************************************************************** * FUNCTION y_simple_yang_test_init_static_vars * * initialize module static variables * ********************************************************************/ static void y_simple_yang_test_init_static_vars (void) { simple_yang_test_mod = NULL; protocol_obj = NULL; interface_obj = NULL; protocol_val = NULL; interface_val = NULL; /* init your static variables here */ } /* y_simple_yang_test_init_static_vars */ /******************************************************************** * FUNCTION simple_yang_test_protocol_name_udp_udp_edit * * Edit database object callback * Path: /protocol/name/udp/udp * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_yang_test_protocol_name_udp_udp_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_yang_test_protocol_name_udp_udp_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "simple_yang_test_protocol_name_udp_udp_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_yang_test_protocol_name_udp_udp_edit */ /******************************************************************** * FUNCTION simple_yang_test_protocol_name_tcp_tcp_edit * * Edit database object callback * Path: /protocol/name/tcp/tcp * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_yang_test_protocol_name_tcp_tcp_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_yang_test_protocol_name_tcp_tcp_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "simple_yang_test_protocol_name_tcp_tcp_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_yang_test_protocol_name_tcp_tcp_edit */ /******************************************************************** * FUNCTION simple_yang_test_protocol_edit * * Edit database object callback * Path: /protocol * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_yang_test_protocol_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_yang_test_protocol_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "simple_yang_test_protocol_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res == NO_ERR) { res = agt_check_cache(&protocol_val, newval, curval, editop); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_yang_test_protocol_edit */ /******************************************************************** * FUNCTION simple_yang_test_interface_ifType_edit * * Edit database object callback * Path: /interface/ifType * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_yang_test_interface_ifType_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_yang_test_interface_ifType_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "simple_yang_test_interface_ifType_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_yang_test_interface_ifType_edit */ /******************************************************************** * FUNCTION simple_yang_test_interface_ifMTU_edit * * Edit database object callback * Path: /interface/ifMTU * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_yang_test_interface_ifMTU_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_yang_test_interface_ifMTU_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "simple_yang_test_interface_ifMTU_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_yang_test_interface_ifMTU_edit */ /******************************************************************** * FUNCTION simple_yang_test_interface_edit * * Edit database object callback * Path: /interface * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_yang_test_interface_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_yang_test_interface_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "simple_yang_test_interface_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res == NO_ERR) { res = agt_check_cache(&interface_val, newval, curval, editop); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_yang_test_interface_edit */ /******************************************************************** * FUNCTION y_simple_yang_test_init * * initialize the simple_yang_test server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ status_t y_simple_yang_test_init ( const xmlChar *modname, const xmlChar *revision) { status_t res = NO_ERR; agt_profile_t *agt_profile; y_simple_yang_test_init_static_vars(); /* change if custom handling done */ if (xml_strcmp(modname, y_simple_yang_test_M_simple_yang_test)) { return ERR_NCX_UNKNOWN_MODULE; } if (revision && xml_strcmp(revision, y_simple_yang_test_R_simple_yang_test)) { return ERR_NCX_WRONG_VERSION; } agt_profile = agt_get_profile(); res = ncxmod_load_module( y_simple_yang_test_M_simple_yang_test, y_simple_yang_test_R_simple_yang_test, &agt_profile->agt_savedevQ, &simple_yang_test_mod); if (res != NO_ERR) { return res; } protocol_obj = ncx_find_object( simple_yang_test_mod, y_simple_yang_test_N_protocol); if (simple_yang_test_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } interface_obj = ncx_find_object( simple_yang_test_mod, y_simple_yang_test_N_interface); if (simple_yang_test_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } res = agt_cb_register_callback( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/interface", (const xmlChar *)"2011-11-21", simple_yang_test_interface_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/interface/ifMTU", (const xmlChar *)"2011-11-21", simple_yang_test_interface_ifMTU_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/interface/ifType", (const xmlChar *)"2011-11-21", simple_yang_test_interface_ifType_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/protocol", (const xmlChar *)"2011-11-21", simple_yang_test_protocol_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/protocol/name/tcp/tcp", (const xmlChar *)"2011-11-21", simple_yang_test_protocol_name_tcp_tcp_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/protocol/name/udp/udp", (const xmlChar *)"2011-11-21", simple_yang_test_protocol_name_udp_udp_edit); if (res != NO_ERR) { return res; } /* put your module initialization code here */ return res; } /* y_simple_yang_test_init */ /******************************************************************** * FUNCTION y_simple_yang_test_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ status_t y_simple_yang_test_init2 (void) { status_t res = NO_ERR; interface_val = agt_init_cache( y_simple_yang_test_M_simple_yang_test, y_simple_yang_test_N_interface, &res); if (res != NO_ERR) { return res; } protocol_val = agt_init_cache( y_simple_yang_test_M_simple_yang_test, y_simple_yang_test_N_protocol, &res); if (res != NO_ERR) { return res; } /* put your init2 code here */ return res; } /* y_simple_yang_test_init2 */ /******************************************************************** * FUNCTION y_simple_yang_test_cleanup * cleanup the server instrumentation library * ********************************************************************/ void y_simple_yang_test_cleanup (void) { agt_cb_unregister_callbacks( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/interface"); agt_cb_unregister_callbacks( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/interface/ifMTU"); agt_cb_unregister_callbacks( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/interface/ifType"); agt_cb_unregister_callbacks( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/protocol"); agt_cb_unregister_callbacks( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/protocol/name/tcp/tcp"); agt_cb_unregister_callbacks( y_simple_yang_test_M_simple_yang_test, (const xmlChar *)"/protocol/name/udp/udp"); /* put your cleanup code here */ } /* y_simple_yang_test_cleanup */ /* END simple_yang_test.c */ yuma123_2.14/netconf/test/modules/build-sil/simple_yang_test/make-sil.sh0000775000175000017500000000075614770023131026511 0ustar vladimirvladimirexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../../../target/lib/ export PATH=$PATH:../../../../target/bin/ export YUMA_HOME=../../../../../ export YUMA_MODPATH=$YUMA_MODPATH:../../yang/:../../../../modules/ BASENAME=simple_yang_test yangdump format=h indent=4 module=../../yang/${BASENAME}.yang output=${BASENAME}.h yangdump format=c indent=4 module=../../yang/${BASENAME}.yang output=${BASENAME}.c yangdump format=cpp_test indent=4 module=../../yang/${BASENAME}.yang output=${BASENAME}.cpp yuma123_2.14/netconf/test/modules/build-sil/device_test/0000775000175000017500000000000014770023131023370 5ustar vladimirvladimiryuma123_2.14/netconf/test/modules/build-sil/device_test/device_test.cpp0000664000175000017500000035771614770023131026415 0ustar vladimirvladimir /* * Copyright (c) 2009 - 2011, Andy Bierman * All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *** Generated by yangdump 2.1.1539M Combined SIL module module device_test revision 2011-09-07 namespace http://netconfcentral.org/ns/device_test */ //extern "C" { #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_rpc.h" #include "agt_timer.h" #include "agt_util.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "rpc.h" #include "ses.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xml_util.h" #include "device_test.h" //} #include "test/support/callbacks/sil-callback-log.h" /* module static variables */ static ncx_module_t *device_test_mod; static obj_template_t *xpo_obj; static obj_template_t *copy_profile_obj; static val_value_t *xpo_val; /* put your static variables here */ /******************************************************************** * FUNCTION y_device_test_init_static_vars * * initialize module static variables * ********************************************************************/ static void y_device_test_init_static_vars (void) { device_test_mod = NULL; xpo_obj = NULL; copy_profile_obj = NULL; xpo_val = NULL; /* init your static variables here */ } /* y_device_test_init_static_vars */ /******************************************************************** * FUNCTION device_test_xpo_profile_id_edit * * Edit database object callback * Path: /xpo/profile/id * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_id_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_id_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_id_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_id_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_id_edit * * Edit database object callback * Path: /xpo/profile/stream/id * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_id_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_id_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_id_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_id_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceNode_id_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceNode/id * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceNode_id_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceNode_id_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceNode_id_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceNode_id_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceNode_resourceType_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceNode/resourceType * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceNode_resourceType_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceNode_resourceType_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceNode_resourceType_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceNode_resourceType_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceNode_channelId_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceNode/channelId * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceNode_channelId_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceNode_channelId_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceNode_channelId_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceNode_channelId_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceNode_configuration_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceNode/configuration * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceNode_configuration_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceNode_configuration_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceNode_configuration_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceNode_configuration_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceNode_statusConfig_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceNode/statusConfig * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceNode_statusConfig_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceNode_statusConfig_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceNode_statusConfig_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceNode_statusConfig_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceNode_alarmConfig_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceNode/alarmConfig * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceNode_alarmConfig_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceNode_alarmConfig_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceNode_alarmConfig_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceNode_alarmConfig_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceNode_physicalPath_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceNode/physicalPath * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceNode_physicalPath_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceNode_physicalPath_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceNode_physicalPath_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceNode_physicalPath_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceNode_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceNode * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceNode_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceNode_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceNode_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceNode_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceConnection_id_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceConnection/id * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceConnection_id_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceConnection_id_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceConnection_id_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceConnection_id_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceConnection_sourceId_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceConnection/sourceId * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceConnection_sourceId_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; val_value_t *lastkey = NULL; uint32 k_xpo_profile_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); uint32 k_xpo_profile_stream_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); uint32 k_xpo_profile_stream_resourceConnection_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); (void)k_xpo_profile_id; (void)k_xpo_profile_stream_id; (void)k_xpo_profile_stream_resourceConnection_id; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceConnection_sourceId_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceConnection_sourceId_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceConnection_sourceId_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceConnection_sourcePinId_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceConnection/sourcePinId * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceConnection_sourcePinId_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; val_value_t *lastkey = NULL; uint32 k_xpo_profile_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); uint32 k_xpo_profile_stream_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); uint32 k_xpo_profile_stream_resourceConnection_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); (void)k_xpo_profile_id; (void)k_xpo_profile_stream_id; (void)k_xpo_profile_stream_resourceConnection_id; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceConnection_sourcePinId_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceConnection_sourcePinId_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceConnection_sourcePinId_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceConnection_destinationId_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceConnection/destinationId * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceConnection_destinationId_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; val_value_t *lastkey = NULL; uint32 k_xpo_profile_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); uint32 k_xpo_profile_stream_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); uint32 k_xpo_profile_stream_resourceConnection_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); (void)k_xpo_profile_id; (void)k_xpo_profile_stream_id; (void)k_xpo_profile_stream_resourceConnection_id; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceConnection_destinationId_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceConnection_destinationId_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceConnection_destinationId_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceConnection_destinationPinId_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceConnection/destinationPinId * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceConnection_destinationPinId_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; val_value_t *lastkey = NULL; uint32 k_xpo_profile_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); uint32 k_xpo_profile_stream_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); uint32 k_xpo_profile_stream_resourceConnection_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); (void)k_xpo_profile_id; (void)k_xpo_profile_stream_id; (void)k_xpo_profile_stream_resourceConnection_id; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceConnection_destinationPinId_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceConnection_destinationPinId_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceConnection_destinationPinId_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceConnection_bitrate_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceConnection/bitrate * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceConnection_bitrate_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; val_value_t *lastkey = NULL; uint32 k_xpo_profile_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); uint32 k_xpo_profile_stream_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); uint32 k_xpo_profile_stream_resourceConnection_id = VAL_UINT(agt_get_key_value(errorval, &lastkey)); (void)k_xpo_profile_id; (void)k_xpo_profile_stream_id; (void)k_xpo_profile_stream_resourceConnection_id; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceConnection_bitrate_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceConnection_bitrate_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceConnection_bitrate_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_resourceConnection_edit * * Edit database object callback * Path: /xpo/profile/stream/resourceConnection * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_resourceConnection_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_resourceConnection_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_resourceConnection_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_resourceConnection_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_stream_edit * * Edit database object callback * Path: /xpo/profile/stream * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_stream_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_stream_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_stream_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_stream_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_streamConnection_id_edit * * Edit database object callback * Path: /xpo/profile/streamConnection/id * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_streamConnection_id_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_streamConnection_id_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_streamConnection_id_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_streamConnection_id_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_streamConnection_sourceStreamId_edit * * Edit database object callback * Path: /xpo/profile/streamConnection/sourceStreamId * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_streamConnection_sourceStreamId_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_streamConnection_sourceStreamId_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_streamConnection_sourceStreamId_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_streamConnection_sourceStreamId_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_streamConnection_destinationStreamId_edit * * Edit database object callback * Path: /xpo/profile/streamConnection/destinationStreamId * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_streamConnection_destinationStreamId_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_streamConnection_destinationStreamId_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_streamConnection_destinationStreamId_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_streamConnection_destinationStreamId_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_streamConnection_sourceId_edit * * Edit database object callback * Path: /xpo/profile/streamConnection/sourceId * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_streamConnection_sourceId_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_streamConnection_sourceId_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_streamConnection_sourceId_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_streamConnection_sourceId_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_streamConnection_sourcePinId_edit * * Edit database object callback * Path: /xpo/profile/streamConnection/sourcePinId * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_streamConnection_sourcePinId_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_streamConnection_sourcePinId_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_streamConnection_sourcePinId_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_streamConnection_sourcePinId_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_streamConnection_destinationId_edit * * Edit database object callback * Path: /xpo/profile/streamConnection/destinationId * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_streamConnection_destinationId_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_streamConnection_destinationId_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_streamConnection_destinationId_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_streamConnection_destinationId_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_streamConnection_destinationPinId_edit * * Edit database object callback * Path: /xpo/profile/streamConnection/destinationPinId * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_streamConnection_destinationPinId_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_streamConnection_destinationPinId_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_streamConnection_destinationPinId_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_streamConnection_destinationPinId_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_streamConnection_bitrate_edit * * Edit database object callback * Path: /xpo/profile/streamConnection/bitrate * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_streamConnection_bitrate_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_streamConnection_bitrate_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_streamConnection_bitrate_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_streamConnection_bitrate_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_streamConnection_edit * * Edit database object callback * Path: /xpo/profile/streamConnection * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_streamConnection_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_streamConnection_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_streamConnection_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_streamConnection_edit */ /******************************************************************** * FUNCTION device_test_xpo_profile_edit * * Edit database object callback * Path: /xpo/profile * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_profile_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_profile_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_profile_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_profile_edit */ /******************************************************************** * FUNCTION device_test_xpo_activeProfile_edit * * Edit database object callback * Path: /xpo/activeProfile * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_activeProfile_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_activeProfile_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_activeProfile_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_activeProfile_edit */ /******************************************************************** * FUNCTION device_test_xpo_edit * * Edit database object callback * Path: /xpo * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t device_test_xpo_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter device_test_xpo_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "device_test_xpo_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res == NO_ERR) { res = agt_check_cache(&xpo_val, newval, curval, editop); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* device_test_xpo_edit */ /******************************************************************** * FUNCTION y_device_test_copy_profile_validate * * RPC validation phase * All YANG constraints have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_device_test_copy_profile_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res = NO_ERR; val_value_t *errorval = NULL; val_value_t *sourceProfileId_val; val_value_t *destinationProfileId_val; val_value_t *replaceExisting_val; uint32 sourceProfileId; uint32 destinationProfileId; boolean replaceExisting; sourceProfileId_val = val_find_child( msg->rpc_input, y_device_test_M_device_test, y_device_test_N_sourceProfileId); if (sourceProfileId_val != NULL && sourceProfileId_val->res == NO_ERR) { sourceProfileId = VAL_UINT(sourceProfileId_val); } destinationProfileId_val = val_find_child( msg->rpc_input, y_device_test_M_device_test, y_device_test_N_destinationProfileId); if (destinationProfileId_val != NULL && destinationProfileId_val->res == NO_ERR) { destinationProfileId = VAL_UINT(destinationProfileId_val); } replaceExisting_val = val_find_child( msg->rpc_input, y_device_test_M_device_test, y_device_test_N_replaceExisting); if (replaceExisting_val != NULL && replaceExisting_val->res == NO_ERR) { replaceExisting = VAL_BOOL(replaceExisting_val); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "y_device_test_copy_profile_validate"; cbLog.addCallback("device_test", cbData); return res; } /* y_device_test_copy_profile_validate */ /******************************************************************** * FUNCTION y_device_test_copy_profile_invoke * * RPC invocation phase * All constraints have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_device_test_copy_profile_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res = NO_ERR; val_value_t *sourceProfileId_val; val_value_t *destinationProfileId_val; val_value_t *replaceExisting_val; uint32 sourceProfileId; uint32 destinationProfileId; boolean replaceExisting; sourceProfileId_val = val_find_child( msg->rpc_input, y_device_test_M_device_test, y_device_test_N_sourceProfileId); if (sourceProfileId_val != NULL && sourceProfileId_val->res == NO_ERR) { sourceProfileId = VAL_UINT(sourceProfileId_val); } destinationProfileId_val = val_find_child( msg->rpc_input, y_device_test_M_device_test, y_device_test_N_destinationProfileId); if (destinationProfileId_val != NULL && destinationProfileId_val->res == NO_ERR) { destinationProfileId = VAL_UINT(destinationProfileId_val); } replaceExisting_val = val_find_child( msg->rpc_input, y_device_test_M_device_test, y_device_test_N_replaceExisting); if (replaceExisting_val != NULL && replaceExisting_val->res == NO_ERR) { replaceExisting = VAL_BOOL(replaceExisting_val); } /* remove the next line if scb is used */ (void)scb; /* remove the next line if methnode is used */ (void)methnode; /* invoke your device instrumentation code here */ YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "y_device_test_copy_profile_invoke"; cbLog.addCallback("device_test", cbData); return res; } /* y_device_test_copy_profile_invoke */ /******************************************************************** * FUNCTION y_device_test_init * * initialize the device_test server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ status_t y_device_test_init ( const xmlChar *modname, const xmlChar *revision) { status_t res = NO_ERR; agt_profile_t *agt_profile; y_device_test_init_static_vars(); /* change if custom handling done */ if (xml_strcmp(modname, y_device_test_M_device_test)) { return ERR_NCX_UNKNOWN_MODULE; } if (revision && xml_strcmp(revision, y_device_test_R_device_test)) { return ERR_NCX_WRONG_VERSION; } agt_profile = agt_get_profile(); res = ncxmod_load_module( y_device_test_M_device_test, y_device_test_R_device_test, &agt_profile->agt_savedevQ, &device_test_mod); if (res != NO_ERR) { return res; } xpo_obj = ncx_find_object( device_test_mod, y_device_test_N_xpo); if (device_test_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } copy_profile_obj = ncx_find_object( device_test_mod, y_device_test_N_copy_profile); if (device_test_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } res = agt_rpc_register_method( y_device_test_M_device_test, y_device_test_N_copy_profile, AGT_RPC_PH_VALIDATE, y_device_test_copy_profile_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_device_test_M_device_test, y_device_test_N_copy_profile, AGT_RPC_PH_INVOKE, y_device_test_copy_profile_invoke); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo", (const xmlChar *)"2011-09-07", device_test_xpo_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/activeProfile", (const xmlChar *)"2011-09-07", device_test_xpo_activeProfile_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile", (const xmlChar *)"2011-09-07", device_test_xpo_profile_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/id", (const xmlChar *)"2011-09-07", device_test_xpo_profile_id_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection", (const xmlChar *)"2011-09-07", device_test_xpo_profile_streamConnection_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/bitrate", (const xmlChar *)"2011-09-07", device_test_xpo_profile_streamConnection_bitrate_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/destinationId", (const xmlChar *)"2011-09-07", device_test_xpo_profile_streamConnection_destinationId_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/destinationPinId", (const xmlChar *)"2011-09-07", device_test_xpo_profile_streamConnection_destinationPinId_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/destinationStreamId", (const xmlChar *)"2011-09-07", device_test_xpo_profile_streamConnection_destinationStreamId_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/id", (const xmlChar *)"2011-09-07", device_test_xpo_profile_streamConnection_id_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/sourceId", (const xmlChar *)"2011-09-07", device_test_xpo_profile_streamConnection_sourceId_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/sourcePinId", (const xmlChar *)"2011-09-07", device_test_xpo_profile_streamConnection_sourcePinId_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/sourceStreamId", (const xmlChar *)"2011-09-07", device_test_xpo_profile_streamConnection_sourceStreamId_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/id", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_id_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceConnection_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection/bitrate", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceConnection_bitrate_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection/destinationId", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceConnection_destinationId_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection/destinationPinId", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceConnection_destinationPinId_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection/id", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceConnection_id_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection/sourceId", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceConnection_sourceId_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection/sourcePinId", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceConnection_sourcePinId_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceNode_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/alarmConfig", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceNode_alarmConfig_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/channelId", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceNode_channelId_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/configuration", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceNode_configuration_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/id", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceNode_id_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/physicalPath", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceNode_physicalPath_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/resourceType", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceNode_resourceType_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/statusConfig", (const xmlChar *)"2011-09-07", device_test_xpo_profile_stream_resourceNode_statusConfig_edit); if (res != NO_ERR) { return res; } /* put your module initialization code here */ return res; } /* y_device_test_init */ /******************************************************************** * FUNCTION y_device_test_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ status_t y_device_test_init2 (void) { status_t res = NO_ERR; xpo_val = agt_init_cache( y_device_test_M_device_test, y_device_test_N_xpo, &res); if (res != NO_ERR) { return res; } /* put your init2 code here */ return res; } /* y_device_test_init2 */ /******************************************************************** * FUNCTION y_device_test_cleanup * cleanup the server instrumentation library * ********************************************************************/ void y_device_test_cleanup (void) { agt_rpc_unregister_method( y_device_test_M_device_test, y_device_test_N_copy_profile); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/activeProfile"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/id"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/bitrate"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/destinationId"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/destinationPinId"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/destinationStreamId"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/id"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/sourceId"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/sourcePinId"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/streamConnection/sourceStreamId"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/id"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection/bitrate"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection/destinationId"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection/destinationPinId"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection/id"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection/sourceId"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceConnection/sourcePinId"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/alarmConfig"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/channelId"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/configuration"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/id"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/physicalPath"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/resourceType"); agt_cb_unregister_callbacks( y_device_test_M_device_test, (const xmlChar *)"/xpo/profile/stream/resourceNode/statusConfig"); /* put your cleanup code here */ } /* y_device_test_cleanup */ /* END device_test.c */ yuma123_2.14/netconf/test/modules/build-sil/device_test/device_test.h0000664000175000017500000001351714770023131026046 0ustar vladimirvladimir #ifndef _H_device_test #define _H_device_test /* * Copyright (c) 2009 - 2011, Andy Bierman * All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *** Generated by yangdump 2.1.1539M Combined SIL header module device_test revision 2011-09-07 namespace http://netconfcentral.org/ns/device_test */ #include "dlq.h" #include "ncxtypes.h" #include "op.h" #include "status.h" #include "val.h" #ifdef __cplusplus extern "C" { #endif #define y_device_test_M_device_test (const xmlChar *)"device_test" #define y_device_test_R_device_test (const xmlChar *)"2011-09-07" #define y_device_test_N_activeProfile (const xmlChar *)"activeProfile" #define y_device_test_N_alarmConfig (const xmlChar *)"alarmConfig" #define y_device_test_N_bitrate (const xmlChar *)"bitrate" #define y_device_test_N_channelId (const xmlChar *)"channelId" #define y_device_test_N_configuration (const xmlChar *)"configuration" #define y_device_test_N_copyResult (const xmlChar *)"copyResult" #define y_device_test_N_copy_profile (const xmlChar *)"copy-profile" #define y_device_test_N_destinationId (const xmlChar *)"destinationId" #define y_device_test_N_destinationPinId (const xmlChar *)"destinationPinId" #define y_device_test_N_destinationProfileId (const xmlChar *)"destinationProfileId" #define y_device_test_N_destinationStreamId (const xmlChar *)"destinationStreamId" #define y_device_test_N_id (const xmlChar *)"id" #define y_device_test_N_physicalPath (const xmlChar *)"physicalPath" #define y_device_test_N_profile (const xmlChar *)"profile" #define y_device_test_N_replaceExisting (const xmlChar *)"replaceExisting" #define y_device_test_N_resourceConnection (const xmlChar *)"resourceConnection" #define y_device_test_N_resourceNode (const xmlChar *)"resourceNode" #define y_device_test_N_resourceType (const xmlChar *)"resourceType" #define y_device_test_N_sourceId (const xmlChar *)"sourceId" #define y_device_test_N_sourcePinId (const xmlChar *)"sourcePinId" #define y_device_test_N_sourceProfileId (const xmlChar *)"sourceProfileId" #define y_device_test_N_sourceStreamId (const xmlChar *)"sourceStreamId" #define y_device_test_N_statusConfig (const xmlChar *)"statusConfig" #define y_device_test_N_stream (const xmlChar *)"stream" #define y_device_test_N_streamConnection (const xmlChar *)"streamConnection" #define y_device_test_N_xpo (const xmlChar *)"xpo" /* list /xpo/profile/stream/resourceNode */ typedef struct y_device_test_T_xpo_profile_stream_resourceNode_ { dlq_hdr_t qhdr; uint32 id; uint32 resourceType; uint32 channelId; xmlChar *configuration; xmlChar *statusConfig; xmlChar *alarmConfig; xmlChar *physicalPath; } y_device_test_T_xpo_profile_stream_resourceNode; /* list /xpo/profile/stream/resourceConnection */ typedef struct y_device_test_T_xpo_profile_stream_resourceConnection_ { dlq_hdr_t qhdr; uint32 id; uint32 sourceId; uint32 sourcePinId; uint32 destinationId; uint32 destinationPinId; uint32 bitrate; } y_device_test_T_xpo_profile_stream_resourceConnection; /* list /xpo/profile/stream */ typedef struct y_device_test_T_xpo_profile_stream_ { dlq_hdr_t qhdr; uint32 id; dlq_hdr_t resourceNode; dlq_hdr_t resourceConnection; } y_device_test_T_xpo_profile_stream; /* list /xpo/profile/streamConnection */ typedef struct y_device_test_T_xpo_profile_streamConnection_ { dlq_hdr_t qhdr; uint32 id; uint32 sourceStreamId; uint32 destinationStreamId; uint32 sourceId; uint32 sourcePinId; uint32 destinationId; uint32 destinationPinId; uint32 bitrate; } y_device_test_T_xpo_profile_streamConnection; /* list /xpo/profile */ typedef struct y_device_test_T_xpo_profile_ { dlq_hdr_t qhdr; uint32 id; dlq_hdr_t stream; dlq_hdr_t streamConnection; } y_device_test_T_xpo_profile; /* container /xpo */ typedef struct y_device_test_T_xpo_ { dlq_hdr_t profile; uint32 activeProfile; } y_device_test_T_xpo; /* container /copy-profile/input */ typedef struct y_device_test_T_copy_profile_input_ { uint32 sourceProfileId; uint32 destinationProfileId; boolean replaceExisting; } y_device_test_T_copy_profile_input; /* container /copy-profile/output */ typedef struct y_device_test_T_copy_profile_output_ { xmlChar *copyResult; } y_device_test_T_copy_profile_output; /* rpc /copy-profile */ typedef struct y_device_test_T_copy_profile_ { y_device_test_T_copy_profile_input input; y_device_test_T_copy_profile_output output; } y_device_test_T_copy_profile; /******************************************************************** * FUNCTION y_device_test_init * * initialize the device_test server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ extern status_t y_device_test_init ( const xmlChar *modname, const xmlChar *revision); /******************************************************************** * FUNCTION y_device_test_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ extern status_t y_device_test_init2 (void); /******************************************************************** * FUNCTION y_device_test_cleanup * cleanup the server instrumentation library * ********************************************************************/ extern void y_device_test_cleanup (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif yuma123_2.14/netconf/test/modules/build-sil/device_test/make-sil.sh0000775000175000017500000000075714770023131025442 0ustar vladimirvladimirexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../../../target/lib/ export PATH=$PATH:../../../../target/bin/ export YUMA_HOME=../../../../../ export YUMA_MODPATH=$YUMA_MODPATH:../../yang/:$YUMA_HOME/netconf/modules BASENAME=device_test yangdump format=h indent=4 module=../../yang/${BASENAME}.yang output=${BASENAME}.h yangdump format=c indent=4 module=../../yang/${BASENAME}.yang output=${BASENAME}.c yangdump format=cpp_test indent=4 module=../../yang/${BASENAME}.yang output=${BASENAME}.cpp yuma123_2.14/netconf/test/modules/build-sil/simple_list_test/0000775000175000017500000000000014770023131024455 5ustar vladimirvladimiryuma123_2.14/netconf/test/modules/build-sil/simple_list_test/simple_list_test.c0000664000175000017500000004661214770023131030215 0ustar vladimirvladimir /* * Copyright (c) 2008-2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *** Generated by yangdump 2.2.1732 Combined SIL module module simple_list_test revision 2008-11-20 namespace http://netconfcentral.org/ns/simple_list_test */ #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_rpc.h" #include "agt_timer.h" #include "agt_util.h" #include "dlq.h" #include "ncx.h" #include "ncx_feature.h" #include "ncxmod.h" #include "ncxtypes.h" #include "rpc.h" #include "ses.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xml_util.h" #include "simple_list_test.h" /* module static variables */ static ncx_module_t *simple_list_test_mod; static obj_template_t *simple_list_obj; static obj_template_t *inc_counter_obj; static obj_template_t *get_counter_obj; static val_value_t *simple_list_val; /* put your static variables here */ static uint32 count_static; /******************************************************************** * FUNCTION y_simple_list_test_init_static_vars * * initialize module static variables * ********************************************************************/ static void y_simple_list_test_init_static_vars (void) { simple_list_test_mod = NULL; simple_list_obj = NULL; inc_counter_obj = NULL; get_counter_obj = NULL; simple_list_val = NULL; /* init your static variables here */ count_static = 0; } /* y_simple_list_test_init_static_vars */ /******************************************************************** * FUNCTION simple_list_test_simple_list_theList_theKey_edit * * Edit database object callback * Path: /simple_list/theList/theKey * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_list_test_simple_list_theList_theKey_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_list_test_simple_list_theList_theKey_edit callback for %s phase", agt_cbtype_name(cbtyp)); } switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_list_test_simple_list_theList_theKey_edit */ /******************************************************************** * FUNCTION simple_list_test_simple_list_theList_theVal_edit * * Edit database object callback * Path: /simple_list/theList/theVal * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_list_test_simple_list_theList_theVal_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_list_test_simple_list_theList_theVal_edit callback for %s phase", agt_cbtype_name(cbtyp)); } switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_list_test_simple_list_theList_theVal_edit */ /******************************************************************** * FUNCTION simple_list_test_simple_list_theList_edit * * Edit database object callback * Path: /simple_list/theList * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_list_test_simple_list_theList_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_list_test_simple_list_theList_edit callback for %s phase", agt_cbtype_name(cbtyp)); } switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_list_test_simple_list_theList_edit */ /******************************************************************** * FUNCTION simple_list_test_simple_list_edit * * Edit database object callback * Path: /simple_list * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_list_test_simple_list_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_list_test_simple_list_edit callback for %s phase", agt_cbtype_name(cbtyp)); } switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res == NO_ERR) { res = agt_check_cache(&simple_list_val, newval, curval, editop); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_list_test_simple_list_edit */ /******************************************************************** * FUNCTION y_simple_list_test_inc_counter_validate * * RPC validation phase * All YANG constraints have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_simple_list_test_inc_counter_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res = NO_ERR; val_value_t *errorval = NULL; if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* y_simple_list_test_inc_counter_validate */ /******************************************************************** * FUNCTION y_simple_list_test_inc_counter_invoke * * RPC invocation phase * All constraints have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_simple_list_test_inc_counter_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res = NO_ERR; /* remove the next line if scb is used */ (void)scb; /* remove the next line if msg is used */ (void)msg; /* remove the next line if methnode is used */ (void)methnode; /* invoke your device instrumentation code here */ count_static++; return res; } /* y_simple_list_test_inc_counter_invoke */ /******************************************************************** * FUNCTION y_simple_list_test_get_counter_validate * * RPC validation phase * All YANG constraints have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_simple_list_test_get_counter_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res = NO_ERR; val_value_t *errorval = NULL; if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* y_simple_list_test_get_counter_validate */ /******************************************************************** * FUNCTION y_simple_list_test_get_counter_invoke * * RPC invocation phase * All constraints have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_simple_list_test_get_counter_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res = NO_ERR; val_value_t *countVal; xmlChar countBuff[100]; /* remove the next line if scb is used */ (void)scb; /* remove the next line if msg is used */ (void)msg; /* remove the next line if methnode is used */ (void)methnode; /* invoke your device instrumentation code here */ snprintf((char *)countBuff, sizeof(countBuff), "%u", count_static); countVal = val_make_string(0, (xmlChar*)"count", countBuff); if (countVal == NULL) { val_free_value(countVal); return ERR_INTERNAL_MEM; } dlq_enque(countVal, &msg->rpc_dataQ); msg->rpc_data_type = RPC_DATA_STD; return res; } /* y_simple_list_test_get_counter_invoke */ /******************************************************************** * FUNCTION y_simple_list_test_init * * initialize the simple_list_test server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ status_t y_simple_list_test_init ( const xmlChar *modname, const xmlChar *revision) { status_t res = NO_ERR; agt_profile_t *agt_profile = agt_get_profile(); y_simple_list_test_init_static_vars(); /* change if custom handling done */ if (xml_strcmp(modname, y_simple_list_test_M_simple_list_test)) { return ERR_NCX_UNKNOWN_MODULE; } if (revision && xml_strcmp(revision, y_simple_list_test_R_simple_list_test)) { return ERR_NCX_WRONG_VERSION; } res = ncxmod_load_module( y_simple_list_test_M_simple_list_test, y_simple_list_test_R_simple_list_test, &agt_profile->agt_savedevQ, &simple_list_test_mod); if (res != NO_ERR) { return res; } simple_list_obj = ncx_find_object( simple_list_test_mod, y_simple_list_test_N_simple_list); if (simple_list_test_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } inc_counter_obj = ncx_find_object( simple_list_test_mod, y_simple_list_test_N_inc_counter); if (simple_list_test_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } get_counter_obj = ncx_find_object( simple_list_test_mod, y_simple_list_test_N_get_counter); if (simple_list_test_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } res = agt_rpc_register_method( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_inc_counter, AGT_RPC_PH_VALIDATE, y_simple_list_test_inc_counter_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_inc_counter, AGT_RPC_PH_INVOKE, y_simple_list_test_inc_counter_invoke); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_get_counter, AGT_RPC_PH_VALIDATE, y_simple_list_test_get_counter_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_get_counter, AGT_RPC_PH_INVOKE, y_simple_list_test_get_counter_invoke); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list", y_simple_list_test_R_simple_list_test, simple_list_test_simple_list_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list/theList", y_simple_list_test_R_simple_list_test, simple_list_test_simple_list_theList_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list/theList/theKey", y_simple_list_test_R_simple_list_test, simple_list_test_simple_list_theList_theKey_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list/theList/theVal", y_simple_list_test_R_simple_list_test, simple_list_test_simple_list_theList_theVal_edit); if (res != NO_ERR) { return res; } /* put your module initialization code here */ return res; } /* y_simple_list_test_init */ /******************************************************************** * FUNCTION y_simple_list_test_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ status_t y_simple_list_test_init2 (void) { status_t res = NO_ERR; simple_list_val = agt_init_cache( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_simple_list, &res); if (res != NO_ERR) { return res; } /* put your init2 code here */ return res; } /* y_simple_list_test_init2 */ /******************************************************************** * FUNCTION y_simple_list_test_cleanup * cleanup the server instrumentation library * ********************************************************************/ void y_simple_list_test_cleanup (void) { agt_rpc_unregister_method( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_inc_counter); agt_rpc_unregister_method( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_get_counter); agt_cb_unregister_callbacks( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list"); agt_cb_unregister_callbacks( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list/theList"); agt_cb_unregister_callbacks( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list/theList/theKey"); agt_cb_unregister_callbacks( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list/theList/theVal"); /* put your cleanup code here */ } /* y_simple_list_test_cleanup */ /* END simple_list_test.c */ yuma123_2.14/netconf/test/modules/build-sil/simple_list_test/simple_list_test.cpp0000664000175000017500000006273014770023131030554 0ustar vladimirvladimir /* * Copyright (c) 2008-2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *** Generated by yangdump 2.2.1732 Combined SIL module module simple_list_test revision 2008-11-20 namespace http://netconfcentral.org/ns/simple_list_test */ //extern "C" { #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_rpc.h" #include "agt_timer.h" #include "agt_util.h" #include "dlq.h" #include "ncx.h" #include "ncx_feature.h" #include "ncxmod.h" #include "ncxtypes.h" #include "rpc.h" #include "ses.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xml_util.h" #include "simple_list_test.h" //} #include "test/support/callbacks/sil-callback-log.h" #include "test/support/callbacks/sil-callback-controller.h" /* module static variables */ static ncx_module_t *simple_list_test_mod; static obj_template_t *simple_list_obj; static obj_template_t *inc_counter_obj; static obj_template_t *get_counter_obj; static val_value_t *simple_list_val; /* put your static variables here */ static uint32 count_static; /******************************************************************** * FUNCTION y_simple_list_test_init_static_vars * * initialize module static variables * ********************************************************************/ static void y_simple_list_test_init_static_vars (void) { simple_list_test_mod = NULL; simple_list_obj = NULL; inc_counter_obj = NULL; get_counter_obj = NULL; simple_list_val = NULL; /* init your static variables here */ count_static = 0; } /* y_simple_list_test_init_static_vars */ /******************************************************************** * FUNCTION simple_list_test_simple_list_theList_theKey_edit * * Edit database object callback * Path: /simple_list/theList/theKey * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_list_test_simple_list_theList_theKey_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_list_test_simple_list_theList_theKey_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "simple_list_test_simple_list_theList_theKey_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_list_test_simple_list_theList_theKey_edit */ /******************************************************************** * FUNCTION simple_list_test_simple_list_theList_theVal_edit * * Edit database object callback * Path: /simple_list/theList/theVal * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_list_test_simple_list_theList_theVal_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_list_test_simple_list_theList_theVal_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "simple_list_test_simple_list_theList_theVal_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_list_test_simple_list_theList_theVal_edit */ /******************************************************************** * FUNCTION simple_list_test_simple_list_theList_edit * * Edit database object callback * Path: /simple_list/theList * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_list_test_simple_list_theList_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_list_test_simple_list_theList_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "simple_list_test_simple_list_theList_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_list_test_simple_list_theList_edit */ /******************************************************************** * FUNCTION simple_list_test_simple_list_edit * * Edit database object callback * Path: /simple_list * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t simple_list_test_simple_list_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; if (LOGDEBUG) { log_debug("\nEnter simple_list_test_simple_list_edit callback for %s phase", agt_cbtype_name(cbtyp)); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackController& cbController = YumaTest::SILCallbackController::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "simple_list_test_simple_list_edit"; val_value_t* value = (newval) ? newval : curval; std::string module_name = (char*) val_get_mod_name(value); switch (cbtyp) { case AGT_CB_VALIDATE: cbData.cbType = "validate"; cbLog.addCallback(module_name, cbData); /* description-stmt validation here */ break; case AGT_CB_APPLY: cbData.cbType = "apply"; cbLog.addCallback(module_name, cbData); /* database manipulation done here */ break; case AGT_CB_COMMIT: cbData.cbType = "commit"; /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: cbData.cbPhase = "load"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_MERGE: cbData.cbPhase = "merge"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_REPLACE: cbData.cbPhase = "replace"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_CREATE: if(!cbController.createSuccess()) { res = SET_ERROR(ERR_INTERNAL_VAL); } cbData.cbPhase = "create"; cbLog.addCallback(module_name, cbData); break; case OP_EDITOP_DELETE: cbData.cbPhase = "delete"; cbLog.addCallback(module_name, cbData); break; default: cbData.cbPhase = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res == NO_ERR) { res = agt_check_cache(&simple_list_val, newval, curval, editop); } break; case AGT_CB_ROLLBACK: cbData.cbType = "rollback"; cbLog.addCallback(module_name, cbData); /* undo device instrumentation here */ break; default: cbData.cbType = "default"; cbLog.addCallback(module_name, cbData); res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } return res; } /* simple_list_test_simple_list_edit */ /******************************************************************** * FUNCTION y_simple_list_test_inc_counter_validate * * RPC validation phase * All YANG constraints have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_simple_list_test_inc_counter_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res = NO_ERR; val_value_t *errorval = NULL; if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "y_simple_list_test_inc_counter_validate"; cbLog.addCallback("simple_list_test", cbData); return res; } /* y_simple_list_test_inc_counter_validate */ /******************************************************************** * FUNCTION y_simple_list_test_inc_counter_invoke * * RPC invocation phase * All constraints have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_simple_list_test_inc_counter_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res = NO_ERR; /* remove the next line if scb is used */ (void)scb; /* remove the next line if msg is used */ (void)msg; /* remove the next line if methnode is used */ (void)methnode; /* invoke your device instrumentation code here */ count_static++; YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "y_simple_list_test_inc_counter_invoke"; cbLog.addCallback("simple_list_test", cbData); return res; } /* y_simple_list_test_inc_counter_invoke */ /******************************************************************** * FUNCTION y_simple_list_test_get_counter_validate * * RPC validation phase * All YANG constraints have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_simple_list_test_get_counter_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res = NO_ERR; val_value_t *errorval = NULL; if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval, (errorval) ? NCX_NT_VAL : NCX_NT_NONE, errorval); } YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "y_simple_list_test_get_counter_validate"; cbLog.addCallback("simple_list_test", cbData); return res; } /* y_simple_list_test_get_counter_validate */ /******************************************************************** * FUNCTION y_simple_list_test_get_counter_invoke * * RPC invocation phase * All constraints have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_simple_list_test_get_counter_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res = NO_ERR; val_value_t *countVal; xmlChar countBuff[100]; /* remove the next line if scb is used */ (void)scb; /* remove the next line if msg is used */ (void)msg; /* remove the next line if methnode is used */ (void)methnode; /* invoke your device instrumentation code here */ YumaTest::SILCallbackLog& cbLog = YumaTest::SILCallbackLog::getInstance(); YumaTest::SILCallbackLog::CallbackInfo cbData; cbData.cbName = "y_simple_list_test_get_counter_invoke"; cbLog.addCallback("simple_list_test", cbData); snprintf((char *)countBuff, sizeof(countBuff), "%u", count_static); countVal = val_make_string(0, (xmlChar*)"count", countBuff); if (countVal == NULL) { val_free_value(countVal); return ERR_INTERNAL_MEM; } dlq_enque(countVal, &msg->rpc_dataQ); msg->rpc_data_type = RPC_DATA_STD; return res; } /* y_simple_list_test_get_counter_invoke */ /******************************************************************** * FUNCTION y_simple_list_test_init * * initialize the simple_list_test server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ status_t y_simple_list_test_init ( const xmlChar *modname, const xmlChar *revision) { status_t res = NO_ERR; agt_profile_t *agt_profile = agt_get_profile(); y_simple_list_test_init_static_vars(); /* change if custom handling done */ if (xml_strcmp(modname, y_simple_list_test_M_simple_list_test)) { return ERR_NCX_UNKNOWN_MODULE; } if (revision && xml_strcmp(revision, y_simple_list_test_R_simple_list_test)) { return ERR_NCX_WRONG_VERSION; } res = ncxmod_load_module( y_simple_list_test_M_simple_list_test, y_simple_list_test_R_simple_list_test, &agt_profile->agt_savedevQ, &simple_list_test_mod); if (res != NO_ERR) { return res; } simple_list_obj = ncx_find_object( simple_list_test_mod, y_simple_list_test_N_simple_list); if (simple_list_test_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } inc_counter_obj = ncx_find_object( simple_list_test_mod, y_simple_list_test_N_inc_counter); if (simple_list_test_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } get_counter_obj = ncx_find_object( simple_list_test_mod, y_simple_list_test_N_get_counter); if (simple_list_test_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } res = agt_rpc_register_method( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_inc_counter, AGT_RPC_PH_VALIDATE, y_simple_list_test_inc_counter_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_inc_counter, AGT_RPC_PH_INVOKE, y_simple_list_test_inc_counter_invoke); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_get_counter, AGT_RPC_PH_VALIDATE, y_simple_list_test_get_counter_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_get_counter, AGT_RPC_PH_INVOKE, y_simple_list_test_get_counter_invoke); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list", y_simple_list_test_R_simple_list_test, simple_list_test_simple_list_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list/theList", y_simple_list_test_R_simple_list_test, simple_list_test_simple_list_theList_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list/theList/theKey", y_simple_list_test_R_simple_list_test, simple_list_test_simple_list_theList_theKey_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list/theList/theVal", y_simple_list_test_R_simple_list_test, simple_list_test_simple_list_theList_theVal_edit); if (res != NO_ERR) { return res; } /* put your module initialization code here */ return res; } /* y_simple_list_test_init */ /******************************************************************** * FUNCTION y_simple_list_test_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ status_t y_simple_list_test_init2 (void) { status_t res = NO_ERR; simple_list_val = agt_init_cache( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_simple_list, &res); if (res != NO_ERR) { return res; } /* put your init2 code here */ return res; } /* y_simple_list_test_init2 */ /******************************************************************** * FUNCTION y_simple_list_test_cleanup * cleanup the server instrumentation library * ********************************************************************/ void y_simple_list_test_cleanup (void) { agt_rpc_unregister_method( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_inc_counter); agt_rpc_unregister_method( y_simple_list_test_M_simple_list_test, y_simple_list_test_N_get_counter); agt_cb_unregister_callbacks( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list"); agt_cb_unregister_callbacks( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list/theList"); agt_cb_unregister_callbacks( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list/theList/theKey"); agt_cb_unregister_callbacks( y_simple_list_test_M_simple_list_test, (const xmlChar *)"/simple_list/theList/theVal"); /* put your cleanup code here */ } /* y_simple_list_test_cleanup */ /* END simple_list_test.c */ yuma123_2.14/netconf/test/modules/build-sil/simple_list_test/simple_list_test.h0000664000175000017500000000744714770023131030225 0ustar vladimirvladimir #ifndef _H_simple_list_test #define _H_simple_list_test /* * Copyright (c) 2008-2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *** Generated by yangdump 2.2.1732 Combined SIL header module simple_list_test revision 2008-11-20 namespace http://netconfcentral.org/ns/simple_list_test */ #include "dlq.h" #include "ncxtypes.h" #include "op.h" #include "status.h" #include "val.h" #ifdef __cplusplus extern "C" { #endif #define y_simple_list_test_M_simple_list_test (const xmlChar *)"simple_list_test" #define y_simple_list_test_R_simple_list_test (const xmlChar *)"2008-11-20" #define y_simple_list_test_N_count (const xmlChar *)"count" #define y_simple_list_test_N_get_counter (const xmlChar *)"get-counter" #define y_simple_list_test_N_inc_counter (const xmlChar *)"inc-counter" #define y_simple_list_test_N_simple_list (const xmlChar *)"simple_list" #define y_simple_list_test_N_theKey (const xmlChar *)"theKey" #define y_simple_list_test_N_theList (const xmlChar *)"theList" #define y_simple_list_test_N_theVal (const xmlChar *)"theVal" /* list /simple_list/theList */ typedef struct y_simple_list_test_T_simple_list_theList_ { dlq_hdr_t qhdr; xmlChar *theKey; xmlChar *theVal; } y_simple_list_test_T_simple_list_theList; /* container /simple_list */ typedef struct y_simple_list_test_T_simple_list_ { dlq_hdr_t theList; } y_simple_list_test_T_simple_list; /* container /inc-counter/input */ typedef struct y_simple_list_test_T_inc_counter_input_ { } y_simple_list_test_T_inc_counter_input; /* container /inc-counter/output */ typedef struct y_simple_list_test_T_inc_counter_output_ { } y_simple_list_test_T_inc_counter_output; /* rpc /inc-counter */ typedef struct y_simple_list_test_T_inc_counter_ { y_simple_list_test_T_inc_counter_input input; y_simple_list_test_T_inc_counter_output output; } y_simple_list_test_T_inc_counter; /* container /get-counter/output */ typedef struct y_simple_list_test_T_get_counter_output_ { uint32 count; } y_simple_list_test_T_get_counter_output; /* container /get-counter/input */ typedef struct y_simple_list_test_T_get_counter_input_ { } y_simple_list_test_T_get_counter_input; /* rpc /get-counter */ typedef struct y_simple_list_test_T_get_counter_ { y_simple_list_test_T_get_counter_output output; y_simple_list_test_T_get_counter_input input; } y_simple_list_test_T_get_counter; /******************************************************************** * FUNCTION y_simple_list_test_init * * initialize the simple_list_test server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ extern status_t y_simple_list_test_init ( const xmlChar *modname, const xmlChar *revision); /******************************************************************** * FUNCTION y_simple_list_test_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ extern status_t y_simple_list_test_init2 (void); /******************************************************************** * FUNCTION y_simple_list_test_cleanup * cleanup the server instrumentation library * ********************************************************************/ extern void y_simple_list_test_cleanup (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif yuma123_2.14/netconf/test/modules/build-sil/simple_list_test/make-sil.sh0000775000175000017500000000075614770023131026526 0ustar vladimirvladimirexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../../../target/lib/ export PATH=$PATH:../../../../target/bin/ export YUMA_HOME=../../../../../ export YUMA_MODPATH=$YUMA_MODPATH:../../yang/:../../../../modules/ BASENAME=simple_list_test yangdump format=h indent=4 module=../../yang/${BASENAME}.yang output=${BASENAME}.h yangdump format=c indent=4 module=../../yang/${BASENAME}.yang output=${BASENAME}.c yangdump format=cpp_test indent=4 module=../../yang/${BASENAME}.yang output=${BASENAME}.cpp yuma123_2.14/netconf/test/yangdump/0000775000175000017500000000000014770023131017362 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangdump/validate-ietf-rfc/0000775000175000017500000000000014770023131022650 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangdump/validate-ietf-rfc/run.sh0000775000175000017500000000032414770023131024012 0ustar vladimirvladimir#!/bin/bash -e rm -r tmp || true mkdir tmp cd tmp wget http://www.claise.be/YANG-RFC.tar tar -xvf YANG-RFC.tar rm YANG-RFC.tar for file in `ls *.yang` ; do echo $file yangdump --module=$file --modpath=. done yuma123_2.14/netconf/test/yangdump/test-yangtree0000775000175000017500000000004414770023131022101 0ustar vladimirvladimir#!/bin/bash -e cd yangtree ./run.sh yuma123_2.14/netconf/test/yangdump/c/0000775000175000017500000000000014770023131017604 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangdump/c/mod1.yang0000664000175000017500000000034514770023131021326 0ustar vladimirvladimirmodule mod1 { prefix m1; namespace "http://yuma123.org/ns/test/yangdump/c/mod1"; rpc myrpc { input { leaf myleaf { type union { type uint32; type string; } } } } } yuma123_2.14/netconf/test/yangdump/c/run.sh0000775000175000017500000000056614770023131020756 0ustar vladimirvladimir#!/bin/sh -e yangdump --format=c --modpath=/usr/share/yuma/modules/ --output=mod1.c mod1.yang yangdump --format=h --modpath=/usr/share/yuma/modules/ --output=mod1.h mod1.yang gcc -Werror -I/usr/include/yuma/platform -I/usr/include/yuma/agt -I/usr/include/yuma/ncx/ -I/usr/include -I/usr/include/libxml2 -I/usr/include/libxml2/libxml mod1.c -lyumancx -lyumaagt -c -o mod1.o yuma123_2.14/netconf/test/yangdump/yangtree/0000775000175000017500000000000014770023131021200 5ustar vladimirvladimiryuma123_2.14/netconf/test/yangdump/yangtree/run.sh0000775000175000017500000000225014770023131022342 0ustar vladimirvladimir#!/bin/bash #./update-module-repository.sh rm -rf tmp || true mkdir -p tmp/pyang mkdir -p tmp/yangdump TOTAL=0 OKS=0 FAILS=0 modpath=/usr/share/yuma/modules for filespec in `find /usr/share/yuma/modules/ -name '*.yang' | sort` ; do module=`basename $filespec .yang` echo $module >&2 echo $module is_submodule="`head -n 1 ${filespec} | grep submodule`" || true if [ "${is_submodule}" != "" ] ; then echo "Skip submodule: ${module}" continue fi TOTAL=$((${TOTAL}+1)) pyang_cmd="pyang -f tree --path ${modpath} ${filespec}" yangdump_cmd="yangdump --format=tree --modpath=/usr/share/yuma/modules/ ${filespec}" echo $pyang_cmd $pyang_cmd | grep -v '^$' | tee > tmp/pyang/${module}.tree pyang_ret=$? echo $yangdump_cmd $yangdump_cmd | grep -v '^$' | tee > tmp/yangdump/${module}.tree yangdump_ret=$? echo yangdump_ret=${yangdump_ret} >&2 echo pyang_ret=${pyang_ret} >&2 cmp tmp/pyang/${module}.tree tmp/yangdump/${module}.tree cmp_ret=$? if [ "${cmp_ret}" = "0" ] ; then echo OK: $module OKS=$((${OKS}+1)) else FAILS=$((${FAILS}+1)) echo FAIL: $module fi done echo OKS=$OKS echo FAILS=$((${TOTAL}-${OKS})) exit $FAILS yuma123_2.14/netconf/test/yangdump/test-c0000775000175000017500000000003514770023131020505 0ustar vladimirvladimir#!/bin/bash -e cd c ./run.sh yuma123_2.14/netconf/test/yangdump/Makefile.am0000775000175000017500000000004014770023131021413 0ustar vladimirvladimirTESTS=\ test-yangtree \ test-c yuma123_2.14/netconf/test/yangdump/configure.ac0000664000175000017500000000070714770023131021654 0ustar vladimirvladimirAC_INIT([yangdump-test-suite], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules" AC_SUBST(yangdir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1 -DRELEASE=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/netconf/test/make-doxygen.sh0000775000175000017500000000011114770023131020456 0ustar vladimirvladimir# Build Doxygen Documentation for the Yuma test Harness doxygen Doxyfile yuma123_2.14/netconf/test/include/0000775000175000017500000000000014770023131017161 5ustar vladimirvladimiryuma123_2.14/netconf/test/include/boost_includes.hpp0000664000175000017500000000072114770023131022706 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include yuma123_2.14/netconf/test/test-suites/0000775000175000017500000000000014770023131020027 5ustar vladimirvladimiryuma123_2.14/netconf/test/test-suites/common/0000775000175000017500000000000014770023131021317 5ustar vladimirvladimiryuma123_2.14/netconf/test/test-suites/common/simple-edit-tests-startup.cpp0000664000175000017500000001071414770023131027102 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include #include "test/include/boost_includes.hpp" // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-container-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| namespace ph = boost::phoenix; namespace ph_args = boost::phoenix::arg_names; using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( startup_slt_tests, SimpleContainerModuleFixture ) // ---------------------------------------------------------------------------| // Add some data and check that it was added correctly // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( slt_startup ) { DisplayTestDescrption( "Demonstrate startup config behaviour...", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Populate the list with 3 key-value pairs\n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Check all values are not in the running\n" "\t 5 - Commit the operation\n" "\t (performing a copy-config if startup capability is in use)\n" "\t 6 - Check all values are in the running\n" "\t 7 - Check all values are in the candidate\n" "\t 8 - Perform restart operation\n" "\t 9 - Check all values are loaded to the running\n" "\t10 - Check all values are loaded to the candidate\n" "\t11 - Delete all entries from the candidate\n" "\t12 - Check all values are not in the candidate\n" "\t13 - Check all values are in the running\n" "\t14 - Commit the operation\n" "\t (performing a copy-config if startup capability is in use)\n" "\t15 - Check all values are not in the candidate\n" "\t16 - Check all values are not in the running\n" ); { // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); //Reset logged callbacks cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // set some values populateDatabase( 3 ); // check the entries exist checkEntries( primarySession_ ); // commit the changes commitChanges( primarySession_ ); // check the entries exist checkEntries( primarySession_ ); } // restart runRestart( primarySession_ ); // get a secondary session std::shared_ptr secondarySession( sessionFactory_->createSession() ); { // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( secondarySession ); // check the entries still exist checkEntries( secondarySession ); // remove all entries deleteMainContainer( secondarySession ); checkEntries( secondarySession ); commitChanges( secondarySession ); checkEntries( secondarySession ); } } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/discard-changes-tests.cpp0000664000175000017500000001523014770023131026203 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-container-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( discard_changes_tests, SimpleContainerModuleFixture ) BOOST_AUTO_TEST_CASE( slt_discard_new_container ) { DisplayTestDescrption( "Demonstrate discard-changes operation on uncomitted " "container creation.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Populate the list with 3 key-value pairs\n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Check all values are not in the running\n" "\t 5 - Discard the changes\n" "\t 6 - Check all values are not in the candidate\n" "\t 7 - Check all values are not in the running\n" "\t 8 - Commit the operation\n" "\t 9 - Check all values are not in the candidate\n" "\t10 - Check all values are not in the running\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); // set some values populateDatabase( 3 ); // check the entries exist checkEntries( primarySession_ ); // discard entries discardChangesOperation( primarySession_ ); // check the entries no longer exist checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } BOOST_AUTO_TEST_CASE( slt_discard_list_additions ) { DisplayTestDescrption( "Demonstrate discard-changes operation on uncomitted " "list addition.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Populate the list with 3 key-value pairs\n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Check all values are not in the running\n" "\t 5 - Commit the operation\n" "\t 6 - Check all values are in the running\n" "\t 7 - Check all values are in the candidate\n" "\t 8 - Populate the list with 2 additional key-value pairs\n" "\t 9 - Check all values are in the candidate\n" "\t10 - Check additional values are not in the running\n" "\t11 - Discard the changes\n" "\t12 - Check additional values are not in the candidate\n" "\t13 - Check additional values are not in the running\n" "\t14 - Delete all entries from the candidate\n" "\t15 - Check all values are not in the candidate\n" "\t16 - Check all values are in the running\n" "\t17 - Commit the operation\n" "\t18 - Check all values are not in the candidate\n" "\t19 - Check all values are not in the running\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); // set some values populateDatabase( 3 ); // check the entries exist checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); // set some more values addEntryValuePair( primarySession_, "entryKey4", "entryVal4" ); addEntryValuePair( primarySession_, "entryKey5", "entryVal5" ); // check the entries exist checkEntries( primarySession_ ); // discard new entries discardChangesOperation( primarySession_ ); // check the new entries were discarded checkEntries( primarySession_ ); // remove all entries deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } BOOST_AUTO_TEST_CASE( slt_discard_list_edits ) { DisplayTestDescrption( "Demonstrate discard-changes operation on uncomitted " "list edits.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Populate the list with 3 key-value pairs\n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Check all values are not in the running\n" "\t 5 - Commit the operation\n" "\t 6 - Check all values are in the running\n" "\t 7 - Check all values are in the candidate\n" "\t 8 - Modify 2 of the key-value pairs\n" "\t 9 - Check modified values are in the candidate\n" "\t10 - Check modified values are not in the running\n" "\t11 - Discard the changes\n" "\t12 - Check edited values are not in the candidate\n" "\t13 - Check edited values are not in the running\n" "\t14 - Delete all entries from the candidate\n" "\t15 - Check all values are not in the candidate\n" "\t16 - Check all values are in the running\n" "\t17 - Commit the operation\n" "\t18 - Check all values are not in the candidate\n" "\t19 - Check all values are not in the running\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); // set some values populateDatabase( 3 ); // check the entries exist checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); // modify some values editEntryValue( primarySession_, "entryKey1", "newVal1" ); editEntryValue( primarySession_, "entryKey3", "newVal3" ); // discard modifications discardChangesOperation( primarySession_ ); // check the edits were discarded checkEntries( primarySession_ ); // remove all entries deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/simple-must-tests.cpp0000664000175000017500000000475514770023131025455 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-yang-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( simple_must, SimpleYangFixture ) BOOST_AUTO_TEST_CASE( add_must ) { DisplayTestDescrption( "Demonstrate that attempts to validate entries disallowed by " "must statements cause an error.", "Procedure: \n" "\t1 - Create the top level container with a valid entry\n" "\t2 - Check that errors are returned for invalid entries\n" "\t3 - Delete the top level container\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // create the top level container createInterfaceContainer( primarySession_, "ethernet", 1500, "create" ); createInterfaceContainer( primarySession_, "ethernet", 1501, "merge" ); createInterfaceContainer( primarySession_, "atm", 64, "merge" ); createInterfaceContainer( primarySession_, "atm", 17966, "merge" ); createInterfaceContainer( primarySession_, "atm", 63, "merge" ); createInterfaceContainer( primarySession_, "atm", 17967, "merge" ); // remove container deleteContainer( primarySession_, "interface" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/default-none-tests.cpp0000664000175000017500000000553414770023131025553 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-container-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-query-util/nc-default-operation-config.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( default_op_none, SimpleContainerModuleFixture ) BOOST_AUTO_TEST_CASE(default_none_test_create ) { DisplayTestDescrption( "Demonstrate no modification of a simple container with default operation none.", "Procedure: \n" "\t1 - Verify container not created with no nc operation\n" "\t2 - Create the top level container\n" "\t3 - Verify container not modified with no nc operation\n" "\t4 - Delete container\n" "\t5 - Verfiy that all entries were removed.\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); //Reset logged callbacks cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // Set default operation to none messageBuilder_->setDefaultOperation("none"); // no operation on the top level container mainContainerOp( primarySession_, "" ); checkEntries( primarySession_ ); // create the top level container createMainContainer( primarySession_ ); checkEntries( primarySession_ ); // No operation on an entry noOpEntryValuePair( primarySession_, "entryKey1", "entryVal1" ); checkEntries( primarySession_ ); // remove all entries deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/module-load-test-suite.cpp0000664000175000017500000001350114770023131026331 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/query-suite-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( mod_load_suite, QuerySuiteFixture ) // ---------------------------------------------------------------------------| // Test cases for loading yang modules // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE(load_yang_module_from_modpath) { DisplayTestDescrption( "Demonstrate loading of a 'yang' module from the configured --modpath.", "The test1 module should be found in one of the directories " "specified using the --modpath command line parameter.\n\n" "Note 1: There is no associated SIL for this module so it will be " "loaded as a '.yang' file\n" ); vector expPresent{ "mod-revision" }; vector expNotPresent{ "rpc-error", "error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryLoadModule( primarySession_, "test1", checker ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE(load_bad_yang_module_from_modpath) { DisplayTestDescrption( "Demonstrate loading of a corrupted 'yang' module (missing namespace)" "from the configured --modpath.", "The test1badns module should be found in one of the directories " "specified using the --modpath command line parameter.\n\n" "Note 1: There is no associated SIL for this module so it will be " "loaded as a '.yang' file\n" ); vector expPresent{ "rpc-error", "error" }; vector expNotPresent{ "mod-revision" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryLoadModule( primarySession_, "test1badns", checker ); } // ---------------------------------------------------------------------------| // Test cases for loading yang modules // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE(load_sil_module_from_modpath) { DisplayTestDescrption( "Demonstrate loading of a 'sil' module.", "The libsimple_list_test.so shared object containing 'sil' callbacks " "for the simple_list_test yang module should be found in one of the " "directories specified using the --modpath command line parameter." ); vector expPresent{ "mod-revision" }; vector expNotPresent{ "rpc-error", "error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryLoadModule( primarySession_, "simple_list_test", checker ); } #if 0 // THIS TEST IS DISABLED DUE TO YUMA BUGS! // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( load_yang_module_from_local_dir, 3 ) BOOST_AUTO_TEST_CASE(load_yang_module_from_local_dir) { DisplayTestDescrption( "Demonstrate loading of a 'yang' module from a local directory.", "The extension '.yang' is specified in the load command, ergo this " "module will be loaded from the local directory" ); vector expPresent{ "mod-revision" }; vector expNotPresent{ "rpc-error", "error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryLoadModule( primarySession_, "test2.yang", checker ); } #endif // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE(load_missing_yang_module) { DisplayTestDescrption( "Demonstrate that an error is reported if a module is not found." ); // build a load module message for test.yang vector expNotPresent{ "mod-revision" }; vector expPresent{ "rpc-error", "error-message", "module not found" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryLoadModule( primarySession_, "non-existent", checker ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE(load_module_twice) { DisplayTestDescrption( "Attempt to load a module twice.", "Demonstrate that an attempt is made to load a module that has " "already been loaded has no affect" ); // build a load module message for test.yang vector expPresent{ "mod-revision" }; vector expNotPresent{ "rpc-error", "error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryLoadModule( primarySession_, "test3", checker ); queryEngine_->tryLoadModule( primarySession_, "test3", checker ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/device-tests-delete.cpp0000664000175000017500000002756214770023131025676 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include #include "test/include/boost_includes.hpp" // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/device-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-query-util/nc-default-operation-config.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/callbacks/sil-callback-log.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| namespace ph = boost::phoenix; namespace ph_args = boost::phoenix::arg_names; using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( DTDelete, DeviceModuleFixture ) // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( Profile ) { DisplayTestDescrption( "Demonstrate operation for Deletion of a Profile ", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a operation, remove profile-2\n" "\t 3 - Commit the change and check the configuration content, " "profile-2 should not exist\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); initialise2Profiles(); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); xpoProfileQuery( primarySession_, 2, "delete" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( Stream ) { DisplayTestDescrption( "Demonstrate operation for Deletion of a Stream", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a operation, remove profile-2, stream-1\n" "\t 3 - Commit the change and check the configuration content, " "profile-2, stream-1 should not exist\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); initialise2Profiles(); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); profileStreamQuery( primarySession_, 2, 1, "delete" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( ResourceNode ) { DisplayTestDescrption( "Demonstrate operation for Deletion of a ResourceNode ", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a operation, remove profile-2, stream-1, res-2\n" "\t 3 - Commit the change and check the configuration content, " "profile-2, stream-1, res-2 should not exist\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); initialise2Profiles(); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); resourceDescriptionQuery( primarySession_, 2, 1, 2, "delete" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( ResourceConnection) { DisplayTestDescrption( "Demonstrate operation for Deletion of a ResourceConnection", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a operation, remove profile-2, stream-1, " "resCon-2 \n" "\t 3 - Commit the change and check the configuration content, " "profile-2, stream-1, res-2 should not exist\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); initialise2Profiles(); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); resourceConnectionQuery( primarySession_, 2, 1, 2, "delete" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( NonExistentProfile ) { DisplayTestDescrption( "Demonstrate operation for Deletion of a non-existent Profile", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a operation, try to remove profile-3\n" "\t 3 - Check that the operation failed with reason data-missing\n" "\t 4 - Commit the change and check the configuration content " "is unchanged.\n" "\t 5 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); initialise2Profiles(); xpoProfileQuery( primarySession_, 4, "delete", "data-missing" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( NonExistentResourceNode1 ) { DisplayTestDescrption( "Demonstrate operation for a non-existent ResourceNode and " "default operation of does not accidentally create the " "parent hierarchy.", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Set the default operation to None\n" "\t 3 - Using a operation, try to remove profile-3, " "stream-1 res-1\n" "\t 4 - Commit the change and check the configuration content " "is unchanged.\n" "\t 5 - Check that the operation failed with reason data-missing\n" "\t 6 - Check that profile-3, stream-1 were not accidentally created\n" "\t 7 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); initialise2Profiles(); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); { DefaultOperationConfig defConfig( xpoBuilder_, "none" ); resourceDescriptionQuery( primarySession_, 3, 1, 1, "delete", "data-missing" ); commitChanges( primarySession_ ); checkConfig(); } deleteXpoContainer( primarySession_ ); } // TODO: Re-enable me once expected behaviour is confirmed! #if 0 // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( NonExistentResourceNode2 ) { DisplayTestDescrption( "Demonstrate operation for a non-existent ResourceNode and " "default operation of does create the parent hierarchy.", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Set the default operation to None\n" "\t 3 - Using a operation, try to remove profile-3, " "stream-1 res-1\n" "\t 4 - Commit the change and check the configuration content " "is unchanged.\n" "\t 5 - Check that the operation failed with reason data-missing\n" "\t 6 - Check that profile-3, stream-1 were not accidentally created\n" "\t 7 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); initialise2Profiles(); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); { DefaultOperationConfig defConfig( xpoBuilder_, "merge" ); resourceDescriptionQuery( primarySession_, 3, 1, 1, "delete", "data-missing" ); commitChanges( primarySession_ ); checkConfig(); } deleteXpoContainer( primarySession_ ); } #endif // @TODO: Re-enable once Yuma's delete replace functionality works #if 0 // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( ProfileDeleteAndReCreate ) { DisplayTestDescrption( "Demonstrate operation for Deletion of a Profile, " "and re-create the same profile before calling ", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a operation, remove profile-2\n" "\t 3 - Create a new profile-2 using a operation\n" "\t 4 - Commit the change and check the configuration content, " "profile-2 should not exist\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); initialise2Profiles(); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); xpoProfileQuery( primarySession_, 2, "delete" ); configureResourceDescrption( primarySession_, 2, 1, 1, ResourceNodeConfig{ 106, boost::optional(), boost::optional(), boost::optional(), string( "VR-17" ) } ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } #endif BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/device-tests-misc.cpp0000664000175000017500000000435614770023131025363 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/device-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( DTMisc, DeviceModuleFixture ) // ---------------------------------------------------------------------------| // Simple get of database data // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( get_dt_entries_using_xpath_filter ) { DisplayTestDescrption( "Demonstrate retrieval of a module's entries.", "Procedure: \n" "\t1 - Query the entries for the 'device_test' module " "using an xpath filter\n" "\t1 - Check that the query returned without error." ); vector expPresent{ "data", "rpc-reply" }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetConfigXpath( primarySession_, "device_test", "running", checker ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/startup-delete-config-tests.cpp0000664000175000017500000001045114770023131027371 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include #include "test/include/boost_includes.hpp" // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-container-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| namespace ph = boost::phoenix; namespace ph_args = boost::phoenix::arg_names; using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( startup_slt_tests, SimpleContainerModuleFixture ) // ---------------------------------------------------------------------------| // Add some data and check that it was added correctly // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( slt_startup_delete_config ) { DisplayTestDescrption( "Demonstrate delete-config operation on startup config...", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Populate the list with 3 key-value pairs\n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Check all values are not in the running\n" "\t 5 - Commit the operation\n" "\t (performing a copy-config if startup capability is in use)\n" "\t 6 - Check all values are in the running\n" "\t 7 - Check all values are in the candidate\n" "\t 8 - Perform delete-config operation\n" "\t 9 - Check all values are still in the running\n" "\t10 - Check all values are still in the candidate\n" "\t11 - Perform restart operation\n" "\t12 - Check no values are loaded to the running\n" "\t13 - Check no values are loaded to the candidate\n" ); { // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); //Reset logged callbacks cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // set some values populateDatabase( 3 ); // check the entries exist checkEntries( primarySession_ ); // commit the changes commitChanges( primarySession_ ); // check the entries exist checkEntries( primarySession_ ); // delete startup config runDeleteStartupConfig( primarySession_ ); // check the entries exist checkEntries( primarySession_ ); } // restart runRestart( primarySession_ ); // get a secondary session std::shared_ptr secondarySession( sessionFactory_->createSession() ); { // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( secondarySession ); // simulate changes lost on restart rollbackChanges( secondarySession ); // check the entries have not been loaded on startup checkEntries( secondarySession ); } } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/db-lock-test-suite-common.cpp0000664000175000017500000001735714770023131026745 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/query-suite-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( db_lock_test_suite_common, QuerySuiteFixture ) // ---------------------------------------------------------------------------| // Simple lock and unlock of the target and running database // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( lock_unlock_running_db ) { DisplayTestDescrption( "Demonstrate locking and unlocking of the running databases.", "Procedure: \n" "\t1 - Lock the running database\n" "\t2 - Unlock the running database" ); vector expPresent{ "ok" }; vector expNotPresent{ "lock-denied", "error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryLockDatabase( primarySession_, "running", checker ); queryEngine_->tryUnlockDatabase( primarySession_, "running", checker ); } // ---------------------------------------------------------------------------| // Multiple attempts to lock the target database - same session // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( lock_running_twice ) { DisplayTestDescrption( "Demonstrate multiple attempts to lock the database by the " "same session are prevented.", "Procedure: \n" "\t1 - Lock the running database\n" "\t2 - Attempt to Lock the running database again ( should " "fail )\n" "\t3 - Unlock the running database" ); vector successPresentText{ "ok" }; vector successAbsentText{ "lock-denied", "error" }; StringsPresentNotPresentChecker successChecker( successPresentText, successAbsentText ); queryEngine_->tryLockDatabase( primarySession_, "running", successChecker ); vector failPresentText{ "lock-denied", "no-access", "error" }; vector failAbsentText{ "ok" }; StringsPresentNotPresentChecker failChecker( failPresentText, failAbsentText ); queryEngine_->tryLockDatabase( primarySession_, "running", failChecker ); queryEngine_->tryUnlockDatabase( primarySession_, "running", successChecker ); } // ---------------------------------------------------------------------------| // Multiple attempts to lock the target database - same session // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( unlock_running_twice ) { DisplayTestDescrption( "Demonstrate multiple attempts to unlock the database by the " "same session are prevented.", "Procedure: \n" "\t1 - Lock the running database\n" "\t2 - Unlock the running database\n" "\t3 - Attempt to unlock the running database again ( should " "fail )\n" ); vector successPresentText{ "ok" }; vector successAbsentText{ "lock-denied", "error" }; StringsPresentNotPresentChecker successChecker( successPresentText, successAbsentText ); queryEngine_->tryLockDatabase( primarySession_, "running", successChecker ); queryEngine_->tryUnlockDatabase( primarySession_, "running", successChecker ); vector failPresentText{ "error", "operation-failed", "no-access", "wrong config state" }; vector failAbsentText{ "ok" }; StringsPresentNotPresentChecker failChecker( failPresentText, failAbsentText ); queryEngine_->tryUnlockDatabase( primarySession_, "running", failChecker ); } // ---------------------------------------------------------------------------| // Multiple attempts to lock the target database - different session // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( lock_running_different_sessions ) { DisplayTestDescrption( "Demonstrate multiple attempts to lock the database by the " "different sessions are prevented.", "Procedure: \n" "\t1 - Session 1 Locks the running database\n" "\t2 - Session 2 attempts to lock the running database " "( should fail )\n" "\t3 - Session 1 Unlocks the running database" ); vector successPresentText{ "ok" }; vector successAbsentText{ "lock-denied", "error" }; StringsPresentNotPresentChecker successChecker( successPresentText, successAbsentText ); queryEngine_->tryLockDatabase( primarySession_, "running", successChecker ); std::shared_ptr secondarySession( sessionFactory_->createSession() ); vector failPresentText{ "lock-denied", "error" }; vector failAbsentText{ "ok" }; StringsPresentNotPresentChecker failChecker( failPresentText, failAbsentText ); queryEngine_->tryLockDatabase( secondarySession, "running", failChecker ); queryEngine_->tryUnlockDatabase( primarySession_, "running", successChecker ); } // ---------------------------------------------------------------------------| // Try to unlock the target database from a session that did not lock it // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( unlock_running_different_sessions ) { DisplayTestDescrption( "Demonstrate that only the session that locked the database " "can unlock it.", "Procedure: \n" "\t1 - Session 1 Locks the running database\n" "\t2 - Session 2 attempts to unlock the running database " "( should fail )\n" "\t3 - Session 1 Unlocks the running database" ); vector successPresentText{ "ok" }; vector successAbsentText{ "lock-denied", "error" }; StringsPresentNotPresentChecker successChecker( successPresentText, successAbsentText ); queryEngine_->tryLockDatabase( primarySession_, "running", successChecker ); vector failPresentText{ "error", "in-use", "no-access", "config locked" }; vector failAbsentText{ "ok" }; StringsPresentNotPresentChecker failChecker( failPresentText, failAbsentText ); std::shared_ptr secondarySession( sessionFactory_->createSession() ); queryEngine_->tryUnlockDatabase( secondarySession, "running", failChecker ); queryEngine_->tryUnlockDatabase( primarySession_, "running", successChecker ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/device-tests-merge.cpp0000664000175000017500000001714114770023131025523 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/device-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( DTMerge, DeviceModuleFixture ) // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( SimpleNewItems ) { DisplayTestDescrption( "Demonstrate for creation of Simple New Items.", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a merge operation, add profile-3\n" "\t 3 - Commit the change and check the configuration content\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); // TODO: Add callback checking.... initialise2Profiles(); xpoProfileQuery( primarySession_, 3, "merge" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( ComplexNewItems ) { DisplayTestDescrption( "Demonstrate for creation of Complex New Items.", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a merge operation, add profile-3, stream-1, resDesc-2\n" "\t 3 - Commit the change and check the configuration content\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); // TODO: Add callback checking.... initialise2Profiles(); configureResourceDescrption( primarySession_, 3, 1, 1, ResourceNodeConfig{ 100, boost::optional(), boost::optional(), boost::optional(), string( "/card[0]/sdiConnector[0]" ) }, "merge" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( SimpleExistingItem ) { DisplayTestDescrption( "Demonstrate for creation / update of Simple Existing Items.", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a merge operation, add profile-3\n" "\t 3 - Using a merge operation, add profile-3 again\n" "\t 4 - Commit the change and check the configuration content\n" "\t 5 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); // TODO: Add callback checking.... initialise2Profiles(); xpoProfileQuery( primarySession_, 3, "merge" ); xpoProfileQuery( primarySession_, 3, "merge" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( ComplexExistingItems ) { DisplayTestDescrption( "Demonstrate for creation / update of Complex Existing Items.", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a merge operation, add profile-3, stream-1, resDesc-2\n" "\t 3 - Commit the change and check the configuration content\n" "\t 4 - Using a merge operation, modify profile-3, stream-1, resDesc-2, " "with some values changed\n" "\t 5 - Commit the change and check the configuration content\n" "\t 6 - Using a merge operation, modify profile-3, stream-1, resDesc-2, " "with no values changed\n" "\t 7 - Commit the change and check the configuration content\n" "\t 8 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); // TODO: Add callback checking.... initialise2Profiles(); configureResourceDescrption( primarySession_, 3, 1, 1, ResourceNodeConfig{ 100, boost::optional(), boost::optional(), boost::optional(), string( "/card[0]/sdiConnector[0]" ) }, "merge" ); configureResourceConnection( primarySession_, 3, 1, 1, ConnectionItemConfig{ 100, 200, 300, boost::optional(), boost::optional() }, "merge" ); commitChanges( primarySession_ ); checkConfig(); configureResourceConnection( primarySession_, 3, 1, 1, ConnectionItemConfig{ boost::optional(), boost::optional(), 311, 400, 500 }, "merge" ); commitChanges( primarySession_ ); checkConfig(); configureResourceConnection( primarySession_, 3, 1, 1, ConnectionItemConfig{ 100, 200, 311, 400, 500 }, "merge" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/simple-edit-tests-validate.cpp0000664000175000017500000001374114770023131027174 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include #include "test/include/boost_includes.hpp" // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-container-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| namespace ph = boost::phoenix; namespace ph_args = boost::phoenix::arg_names; using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( validate_slt_tests, SimpleContainerModuleFixture ) // ---------------------------------------------------------------------------| // Add some data and check that it was added correctly // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( slt_validate ) { DisplayTestDescrption( "Demonstrate validation of a simple container.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Populate the database with 2 key value pairs\n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Check all values are not in the running\n" "\t 5 - Commit the operation\n" "\t 6 - Check all values are in the running\n" "\t 7 - Check all values are in the candidate\n" "\t 8 - Populate the database with another 2 key value pairs\n" "\t 9 - Check new values are in the candidate\n" "\t10 - Check new values are not in the running\n" "\t11 - Run a validate command\n" "\t12 - Check that validate callbacks are logged for each element\n" "\t13 - Delete all entries from the candidate\n" "\t14 - Commit the operation\n" "\t15 - Check all values are not in the candidate\n" "\t16 - Check all values are not in the running\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); //Reset logged callbacks cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); createMainContainer( primarySession_ ); // set some values populateDatabase( 2 ); // Check callbacks vector elements = {"theList"}; cbChecker_->addMainContainer("simple_list_test", "simple_list"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // check the entries exist checkEntries( primarySession_ ); // commit the changes commitChanges( primarySession_ ); // Check callbacks cbChecker_->commitKeyValuePairs("simple_list_test", "simple_list", elements, "theKey", "theVal", 2); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // check the entries exist checkEntries( primarySession_ ); // Set some more values addEntryValuePair( primarySession_, "entryKey2", "entryVal2" ); addEntryValuePair( primarySession_, "entryKey3", "entryVal3" ); // Check callbacks cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // check the entries exist checkEntries( primarySession_ ); // Validate runValidateCommand( primarySession_ ); // Check callbacks cbChecker_->addMainContainer("simple_list_test", "simple_list"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // remove all entries deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/simple-edit-tests.cpp0000664000175000017500000002273014770023131025403 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-container-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( simple_get_test_suite_running, SimpleContainerModuleFixture ) // ---------------------------------------------------------------------------| // Simple get of database data // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( get_slt_entries_using_xpath_filter ) { DisplayTestDescrption( "Demonstrate Simple retrieval of a modules entries.", "Procedure: \n" "\t1 - Query the entries for the 'simple_list_test' module " "using an xpath filter" ); vector expPresent{ "data", "rpc-reply" }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetConfigXpath( primarySession_, "simple_list_test", "running", checker ); } // ---------------------------------------------------------------------------| // Demonstrate poorly formed xml messages are ignored // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( edit_test5_entries_badxml ) { DisplayTestDescrption( "Demonstrate that poorly formed XML create requests are rejected.", "Procedure: \n" "\t1 - Create a query containing poorly formed xml (missing " "termination /\n" "\t2 - Inject the message and check that it was ignored.\n\n" "Note: originally this caused Yuma to exit with SIG SEGV" ); // build the query stringstream query; query << "\n"; vector expPresent{}; vector expNotPresent{ "error" "ok", "rpc-reply", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryEditConfig( primarySession_, query.str(), "running", checker ); } // ---------------------------------------------------------------------------| // Add some data and check that it was added correctly // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( edit_slt_entries_test_create ) { DisplayTestDescrption( "Demonstrate modification of a simple container.", "Procedure: \n" "\t1 - Create the top level container\n" "\t2 - Create a keyed entry with key = entry1Key\n" "\t3 - Set the value coressponding to entry1Key\n" "\t4 - Create a keyed entry with key = entry2Key\n" "\t5 - Set the value coressponding to entry2Key\n" "\t6 - Get the container and verify that all entries were " "created in the writeable database\n" "\t7 - Delete all entries\n" "\t8 - Verfiy that all entries were removed.\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); //Reset logged callbacks cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // create the top level container createMainContainer( primarySession_ ); // Check callbacks vector elements = {}; cbChecker_->updateContainer("simple_list_test", "simple_list", elements, "create"); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // Add an entry addEntryValuePair( primarySession_, "entryKey1", "entryVal1" ); // Check callbacks elements.push_back("theList"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); // Add an entry addEntryValuePair( primarySession_, "entryKey2", "entryVal2" ); // Check callbacks cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); // Add an entry addEntryValuePair( primarySession_, "entryKey3", "entryVal3" ); // Check callbacks cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); checkEntries( primarySession_ ); // remove all entries deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| // Delete existing list entries and container; attempt to delete non-existant // data. // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( edit_slt_entries_test_delete ) { DisplayTestDescrption( "Demonstrate the deletion of existing nodes and appropriate " "errors when attempting to delete non-existant nodes.", "Procedure: \n" "\t1 - Create the top level container\n" "\t2 - Create a keyed entry with key = entry1Key\n" "\t3 - Set the value corresponding to entry1Key\n" "\t4 - Create a keyed entry with key = entry2Key\n" "\t5 - Set the value corresponding to entry2Key\n" "\t6 - Delete the first key/value pair\n" "\t7 - Verify entry deleted\n" "\t8 - Attempt to delete non-existent key/value pair\n" "\t9 - Verify error message\n" "\t10 - Delete the second key/value pair\n" "\t11 - Delete the list\n" "\t12 - Verify list deleted\n" "\t13 - Delete the container\n" "\t14 - Verify container deleted\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // create the top level container createMainContainer( primarySession_ ); // Add two entries addEntryValuePair( primarySession_, "entryKey1", "entryVal1" ); addEntryValuePair( primarySession_, "entryKey2", "entryVal2" ); //Reset logged callbacks cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // Delete first entry deleteEntryValuePair( primarySession_, "entryKey1", "entryVal1" ); // Check callbacks vector elements = {"theList"}; cbChecker_->deleteKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // Attempt to delete non-existent list entry deleteEntryValuePairFailed( primarySession_, "entryKey3", "entryVal3" ); // Delete second entry deleteEntryValuePair( primarySession_, "entryKey2", "entryVal2" ); checkEntries( primarySession_ ); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // Delete the container deleteMainContainer( primarySession_ ); // Check callbacks elements.clear(); cbChecker_->updateContainer("simple_list_test", "simple_list", elements, "delete"); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/simple-edit-tests-candidate.cpp0000664000175000017500000003506014770023131027315 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include #include "test/include/boost_includes.hpp" // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-container-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| namespace ph = boost::phoenix; namespace ph_args = boost::phoenix::arg_names; using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( candidate_slt_tests, SimpleContainerModuleFixture ) // ---------------------------------------------------------------------------| // Add some data and check that it was added correctly // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( slt_value_create ) { DisplayTestDescrption( "Demonstrate population of simple container and candidate " "commit operation.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Create a keyed entry with key = entry1Key\n" "\t 3 - Set the value corresponding to entry1Key\n" "\t 4 - Create a keyed entry with key = entry2Key\n" "\t 5 - Set the value corresponding to entry2Key\n" "\t 6 - Check all values are in the candidate\n" "\t 7 - Check all values are not in the running\n" "\t 8 - Commit the operation\n" "\t 9 - Check all values are in the running\n" "\t10 - Check all values are in the candidate\n" "\t11 - Delete all entries from the candidate\n" "\t12 - Check all values are not in the candidate\n" "\t13 - Check all values are in the running\n" "\t14 - Commit the operation\n" "\t15 - Check all values are not in the candidate\n" "\t16 - Check all values are not in the running\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); //Reset logged callbacks cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // set some values populateDatabase( 3 ); // Check callbacks vector elements = {"theList"}; cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // check the entries exist checkEntries( primarySession_ ); // commit the changes commitChanges( primarySession_ ); // Check callbacks cbChecker_->commitKeyValuePairs("simple_list_test", "simple_list", elements, "theKey", "theVal", 3); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // check the entries exist checkEntries( primarySession_ ); // remove all entries deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| // Add some data then modify the data that it was added correctly // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( slt_edit_values ) { DisplayTestDescrption( "Demonstrate population and modification of database values.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Create some keyed entry values \n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Commit the operation\n" "\t 5 - Check all values are in the running\n" "\t 6 - Modify some of the values\n" "\t 7 - Check all values are in the candidate\n" "\t 8 - Commit the operation\n" "\t 9 - Check all values are in the running\n" "\t10 - Modify some of the values\n" "\t11 - Check all values are in the candidate\n" "\t12 - Commit the operation\n" "\t13 - Check all values are in the running\n" "\t14 - Clean out the database and check it was cleaned" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // create the top level container createMainContainer( primarySession_ ); // set some values populateDatabase( 20 ); // commit & check entries exist checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); // modify some values editEntryValue( primarySession_, "entryKey2", "newValue2" ); editEntryValue( primarySession_, "entryKey5", "newValue5" ); editEntryValue( primarySession_, "entryKey6", "newValue6" ); // commit & check entries exist checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); editEntryValue( primarySession_, "entryKey0", "newValue0" ); editEntryValue( primarySession_, "entryKey1", "newValue1" ); // // // commit & check entries exist checkEntries( primarySession_ ); // commitChanges( primarySession_ ); // checkEntries( primarySession_ ); // // remove all entries deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( slt_lock_released_no_comit ) { DisplayTestDescrption( "Demonstrate that any changes made to the database are reverted " "if the lock is released without a commit.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Create some keyed entry values \n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Commit the operation\n" "\t 5 - Check all values are in the running\n" "\t 6 - Modify some of the values\n" "\t 7 - Check all values are in the candidate\n" "\t 8 - Release all locks\n" "\t 9 - Obtain new locks\n" "\t10 - Check that all changes from step 6 were discarded\n" "\t11 - Clean out the database and check it was cleaned\n" ); std::shared_ptr secondarySession( sessionFactory_->createSession() ); // RAII Vector of database locks { vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // create the top level container createMainContainer( primarySession_ ); // set some values populateDatabase( 20 ); // commit & check entries exist checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } // session 1 modifications map ses1Mods = { { "entryKey2", "newValue2" }, { "entryKey5", "newValue5" }, { "entryKey6", "newValue6" } }; { vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // modify some values boost::for_each( ses1Mods, ph::bind( &SimpleContainerModuleFixture::editEntryValue, this, primarySession_, ph::at_c<0>( ph_args::arg1 ), ph::at_c<1>( ph_args::arg1 ) ) ); checkEntries( primarySession_ ); } discardChanges(); // let tst harness know that changes should be discarded { vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); checkEntries( primarySession_ ); deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } } // ---------------------------------------------------------------------------| // Add some data then merge some data with a mix of modified values for // existing keys and new key/value pairs; verify that the new pairs are added // and the existing values remain unchanged // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( slt_merge_values ) { DisplayTestDescrption( "Demonstrate population of database and merging of new and " "updated data.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Create some keyed entry values \n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Commit the operation\n" "\t 5 - Check all values are in the running\n" "\t 6 - Merge new pairs and modified values\n" "\t 7 - Check only new pairs added to candidate\n" "\t 8 - Commit the operation\n" "\t 9 - Check only new pairs added to running\n" "\t10 - Clean out the database and check it was cleaned" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // create the top level container createMainContainer( primarySession_ ); // set some values populateDatabase( 20 ); // commit & check entries exist checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); // merge some values BOOST_TEST_MESSAGE("MERGING VALUES"); mergeEntryValuePair( primarySession_, "entryKey18", "newValue22" ); mergeEntryValuePair( primarySession_, "entryKey19", "newValue23" ); mergeEntryValuePair( primarySession_, "entryKey20", "newValue20" ); mergeEntryValuePair( primarySession_, "entryKey21", "newValue21" ); // commit & check entries exist BOOST_TEST_MESSAGE("CHECKING ENTRIES - PRE-COMMIT"); checkEntries( primarySession_ ); commitChanges( primarySession_ ); BOOST_TEST_MESSAGE("CHECKING ENTRIES - POST-COMMIT"); checkEntries( primarySession_ ); // remove all entries BOOST_TEST_MESSAGE("DELETING MAIN CONTAINER"); deleteMainContainer( primarySession_ ); BOOST_TEST_MESSAGE("CHECKING ENTRIES - PRE-COMMIT"); checkEntries( primarySession_ ); commitChanges( primarySession_ ); BOOST_TEST_MESSAGE("CHECKING ENTRIES - POST-COMMIT"); checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| // Add some data then replace some data with a mix of modified values for // existing keys and new key/value pairs; verify that the new pairs are added // and the existing values remain unchanged // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( slt_replace_values ) { DisplayTestDescrption( "Demonstrate population of database and replacing of new and " "updated data.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Create some keyed entry values \n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Commit the operation\n" "\t 5 - Check all values are in the running\n" "\t 6 - Replace new pairs and modified values\n" "\t 7 - Check new pairs added to candidate\n" "\t 8 - Check existing pairs updated with modified values in " "candidate\n" "\t 9 - Commit the operation\n" "\t10 - Check new pairs added to running\n" "\t11 - Check existing pairs updated with modified values in " "running\n" "\t12 - Clean out the database and check it was cleaned" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // create the top level container createMainContainer( primarySession_ ); // set some values populateDatabase( 20 ); // commit & check entries exist checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); // replace some values replaceEntryValuePair( primarySession_, "entryKey18", "newValue22" ); replaceEntryValuePair( primarySession_, "entryKey19", "newValue23" ); replaceEntryValuePair( primarySession_, "entryKey20", "newValue20" ); replaceEntryValuePair( primarySession_, "entryKey21", "newValue21" ); // commit & check entries exist checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); // remove all entries deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/device-tests-replace.cpp0000664000175000017500000002264714770023131026046 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/device-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( DTReplace, DeviceModuleFixture ) // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( SimpleNewItems ) { DisplayTestDescrption( "Demonstrate for creation of Simple New Items.", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a replace operation, add profile-3\n" "\t 3 - Commit the change and check the configuration content\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); // TODO: Add callback checking.... initialise2Profiles(); xpoProfileQuery( primarySession_, 3, "replace" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( ComplexNewItems ) { DisplayTestDescrption( "Demonstrate for creation of Complex New Items.", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a replace operation, add profile-3, stream-1, resDesc-2\n" "\t 3 - Commit the change and check the configuration content\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); // TODO: Add callback checking.... // initialise2Profiles(); configureResourceDescrption( primarySession_, 3, 1, 1, ResourceNodeConfig{ 311, boost::optional(), boost::optional(), boost::optional(), string( "Profile3Stream1Resource1" ) }, "replace" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( SimpleExistingItem ) { DisplayTestDescrption( "Demonstrate for creation / update of Simple Existing Items.", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a replace operation, add profile-2\n" "\t 3 - Commit the change and check the configuration content, " "profile 2 should now contain no data\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); // TODO: Add callback checking.... initialise2Profiles(); xpoProfileQuery( primarySession_, 2, "replace" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } #if 0 // TODO Re-enable once replace functionality is fixed // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( ComplexExistingItems ) { DisplayTestDescrption( "Demonstrate for creation / update of Complex Existing Items.", "Procedure: \n" "\t 1 - Add 2 full profiles, commit changes and check content\n" "\t 2 - Using a replace operation, add profile-3, stream-1, resDesc-2\n" "\t 3 - Commit the change and check the configuration content\n" "\t 4 - Using a replace operation, modify profile-3, stream-1, resDesc-2, " "with some values changed\n" "\t 5 - Commit the change and check the configuration content\n" "\t 6 - Using a replace operation, modify profile-3, stream-1, resDesc-2, " "with no values changed\n" "\t 7 - Commit the change and check the configuration content\n" "\t 8 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); // TODO: Add callback checking.... initialise2Profiles(); configureResourceDescrption( primarySession_, 3, 1, 1, ResourceNodeConfig{ 100, boost::optional(), boost::optional(), boost::optional(), string( "/card[0]/sdiConnector[0]" ) }, "replace" ); configureResourceConnection( primarySession_, 3, 1, 1, ConnectionItemConfig{ 100, 200, 300, boost::optional(), boost::optional() }, "replace" ); commitChanges( primarySession_ ); checkConfig(); configureResourceConnection( primarySession_, 3, 1, 1, ConnectionItemConfig{ boost::optional(), boost::optional(), 311, 400, 500 }, "replace" ); commitChanges( primarySession_ ); checkConfig(); configureResourceConnection( primarySession_, 3, 1, 1, ConnectionItemConfig{ boost::optional(), boost::optional(), 311, 400, 500 }, "replace" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( ComplexExistingItems2 ) { DisplayTestDescrption( "Demonstrate for creation / update of Complex Existing Items.", "Procedure: \n" "\t 1 - Add Resource Connection ( profile-1, stream-1, resConn-1 )\n" "\t 2 - Commit the change and check the configuration content\n" "\t 3 - Using a replace operation, modify profile-3, stream-1, resConn-1, " "with some values changed, and some values unset\n" "\t 4 - Commit the change and check the configuration content\n" "\t 5 - Commit the change and check the configuration content\n" "\t 6 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); configureResourceConnection( primarySession_, 1, 1, 1, ConnectionItemConfig{ 1, 1, 1, 1, 1 }, "create" ); commitChanges( primarySession_ ); checkConfig(); configureResourceConnection( primarySession_, 1, 1, 1, ConnectionItemConfig{ boost::optional(), 123, boost::optional(), boost::optional(), boost::optional() }, "replace" ); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } #endif // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/device-tests-get.cpp0000664000175000017500000005520514770023131025206 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Boost Includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/device-get-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-query-util/nc-default-operation-config.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/callbacks/sil-callback-log.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| namespace ph = boost::phoenix; namespace ph_args = boost::phoenix::arg_names; using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( DTGet, DeviceGetModuleFixture ) // ---------------------------------------------------------------------------| // Create content and check filtered config data can be retrieved correctly // using xpath filter // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( GetConfigWithXpathFilterSuccess ) { DisplayTestDescrption( "Demonstrate successful with xpath filter operations " "on XPO3 Container.", "Procedure: \n" "\t 1 - Create the XPO3 container\n" "\t 2 - Add profile-1 to the XPO3 container\n" "\t 3 - Add Stream-1 to the profile-1\n" "\t 4 - Add resourceNode-1 to stream-1\n" "\t 5 - Add resourceConnection-1 to stream-1\n" "\t 7 - Configure resourceNode-1\n" "\t 8 - Configure resourceConnection-1\n" "\t 9 - Add StreamConnection-1\n" "\t10 - Configure StreamConnection-1\n" "\t11 - Set the active profile to profile-1\n" "\t12 - Commit the changes \n" "\t13 - Query netconf for filtered config data with valid xpath and " "verify response\n" "\t14 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); createXpoContainer( primarySession_ ); xpoProfileQuery( primarySession_, 1 ); profileStreamQuery( primarySession_, 1, 1 ); configureResourceDescrption( primarySession_, 1, 1, 1, ResourceNodeConfig{ 100, string( "a configuration"), string( "a status configuration"), string( "an alarm configuration"), string( "/card[0]/sdiConnector[0]") } ); configureResourceConnection( primarySession_, 1, 1, 1, ConnectionItemConfig{ 100, 200, 300, 400, 500 } ); configureStreamConnection( primarySession_, 1, 1, StreamConnectionItemConfig{ 100, 200, 300, 400, 500, 600, 700 } ); setActiveProfile( primarySession_, 1 ); // Check callbacks vector elements = {}; cbChecker_->updateContainer("device_test", "xpo", elements, "create"); elements.push_back("profile"); cbChecker_->addKey("device_test", "xpo", elements, "id"); elements.push_back("stream"); cbChecker_->addKey("device_test", "xpo", elements, "id"); cbChecker_->addResourceNode("device_test", "xpo", elements, true, true); cbChecker_->addResourceCon("device_test", "xpo", elements); elements.pop_back(); cbChecker_->addStreamCon("device_test", "xpo", elements); elements.pop_back(); elements.push_back("activeProfile"); cbChecker_->addElement("device_test", "xpo", elements); cbChecker_->checkCallbacks("device_test"); cbChecker_->resetModuleCallbacks("device_test"); cbChecker_->resetExpectedCallbacks(); commitChanges( primarySession_ ); checkConfig(); BOOST_TEST_MESSAGE("Test: Checking with xpath filter: request the physical path of a resource node"); configureResourceDescrptionInFilteredConfig(1, 1, 1, ResourceNodeConfig{ boost::optional(), boost::optional(), boost::optional(), boost::optional(), string( "/card[0]/sdiConnector[0]" ) } ); checkXpathFilteredConfig("xpo/profile[id='1']/stream[id='1']/resourceNode[id='1']/physicalPath"); clearFilteredConfig(); BOOST_TEST_MESSAGE("Test: Checking with xpath filter: request the resource type of a resource node"); configureResourceDescrptionInFilteredConfig(1, 1, 1, ResourceNodeConfig{ 100, boost::optional(), boost::optional(), boost::optional(), boost::optional() } ); checkXpathFilteredConfig("xpo/profile[id='1']/stream[id='1']/resourceNode[id='1']/resourceType"); clearFilteredConfig(); BOOST_TEST_MESSAGE("Test: Checking with xpath filter: request the destination id of a resource connection"); configureResourceConnectionInFilteredConfig( 1, 1, 1, ConnectionItemConfig{ boost::optional(), boost::optional(), 300, boost::optional(), boost::optional() } ); checkXpathFilteredConfig("xpo/profile[id='1']/stream[id='1']/resourceConnection[id='1']/destinationId"); clearFilteredConfig(); BOOST_TEST_MESSAGE("Test: Checking with xpath filter: request a complete resource connection"); configureResourceConnectionInFilteredConfig( 1, 1, 1, ConnectionItemConfig{ 100, 200, 300, 400, 500 } ); checkXpathFilteredConfig("xpo/profile[id='1']/stream[id='1']/resourceConnection[id='1']"); clearFilteredConfig(); BOOST_TEST_MESSAGE("Test: Checking with xpath filter: request the source stream id of a stream connection"); configureStreamConnectionInFilteredConfig( 1, 1, StreamConnectionItemConfig{ boost::optional(), boost::optional(), boost::optional(), boost::optional(), boost::optional(), 600, boost::optional() } ); checkXpathFilteredConfig("xpo/profile[id='1']/streamConnection[id='1']/sourceStreamId"); clearFilteredConfig(); BOOST_TEST_MESSAGE("Test: Checking with xpath filter: request a complete stream connection"); configureStreamConnectionInFilteredConfig( 1, 1, StreamConnectionItemConfig{ 100, 200, 300, 400, 500, 600, 700 } ); checkXpathFilteredConfig("xpo/profile[id='1']/streamConnection[id='1']"); clearFilteredConfig(); BOOST_TEST_MESSAGE("Test: Checking with xpath filter: request a complete stream"); configureResourceDescrptionInFilteredConfig(1, 1, 1, ResourceNodeConfig{ 100, string( "a configuration"), string( "a status configuration"), string( "an alarm configuration"), string( "/card[0]/sdiConnector[0]") } ); configureResourceConnectionInFilteredConfig( 1, 1, 1, ConnectionItemConfig{ 100, 200, 300, 400, 500 } ); checkXpathFilteredConfig("xpo/profile[id='1']/stream[id='1']"); clearFilteredConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| // Create content and check with malformed xpath filter rejected // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( GetConfigWithXpathFilterFailure ) { DisplayTestDescrption( "Demonstrate with malformed xpath filter rejected.", "Procedure: \n" "\t 1 - Create the XPO3 container\n" "\t 2 - Add profile-1 to the XPO3 container\n" "\t 3 - Add Stream-1 to the profile-1\n" "\t 4 - Add resourceNode-1 to stream-1\n" "\t 5 - Add resourceConnection-1 to stream-1\n" "\t 7 - Configure resourceNode-1\n" "\t 8 - Configure resourceConnection-1\n" "\t 9 - Add StreamConnection-1\n" "\t10 - Configure StreamConnection-1\n" "\t11 - Set the active profile to profile-1\n" "\t12 - Commit the changes \n" "\t13 - Query netconf for filtered config data with invalid xpath " "and verify response\n" "\t14 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createXpoContainer( primarySession_ ); xpoProfileQuery( primarySession_, 1 ); profileStreamQuery( primarySession_, 1, 1 ); configureResourceDescrption( primarySession_, 1, 1, 1, ResourceNodeConfig{ 100, string( "a configuration"), string( "a status configuration"), string( "an alarm configuration"), string( "/card[0]/sdiConnector[0]") } ); configureResourceConnection( primarySession_, 1, 1, 1, ConnectionItemConfig{ 100, 200, 300, 400, 500 } ); configureStreamConnection( primarySession_, 1, 1, StreamConnectionItemConfig{ 100, 200, 300, 400, 500, 600, 700 } ); setActiveProfile( primarySession_, 1 ); commitChanges( primarySession_ ); checkConfig(); BOOST_TEST_MESSAGE("Test: Checking with xpath filter: request includes invalid xpath expression"); vector expPresent{ "error", "rpc-error", "invalid XPath expression syntax" }; vector expNotPresent{ "ok" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetConfigXpath( primarySession_, "%%>", writeableDbName_, checker ); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| // Create content and check filtered config data can be retrieved correctly // using subtree filter // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( GetConfigWithSubtreeFilterSuccess ) { DisplayTestDescrption( "Demonstrate successful with subtree filter operations " "on XPO3 Container.", "Procedure: \n" "\t 1 - Create the XPO3 container\n" "\t 2 - Add profile-1 to the XPO3 container\n" "\t 3 - Add Stream-1 to the profile-1\n" "\t 4 - Add resourceNode-1 to stream-1\n" "\t 5 - Add resourceConnection-1 to stream-1\n" "\t 7 - Configure resourceNode-1\n" "\t 8 - Configure resourceConnection-1\n" "\t 9 - Add StreamConnection-1\n" "\t10 - Configure StreamConnection-1\n" "\t11 - Set the active profile to profile-1\n" "\t12 - Commit the changes \n" "\t13 - Query netconf for filtered config data with valid subtree and " "verify response\n" "\t14 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createXpoContainer( primarySession_ ); xpoProfileQuery( primarySession_, 1 ); profileStreamQuery( primarySession_, 1, 1 ); configureResourceDescrption( primarySession_, 1, 1, 1, ResourceNodeConfig{ 100, string( "/card[0]/sdiConnector[0]" ) } ); configureResourceConnection( primarySession_, 1, 1, 1, ConnectionItemConfig{ 100, 200, 300, 400, 500 } ); configureStreamConnection( primarySession_, 1, 1, StreamConnectionItemConfig{ 100, 200, 300, 400, 500, 600, 700 } ); setActiveProfile( primarySession_, 1 ); commitChanges( primarySession_ ); checkConfig(); BOOST_TEST_MESSAGE("Test: Checking with subtree filter: select the entire namespace"); ensureActiveProfileInFilteredConfig(1); configureResourceDescrptionInFilteredConfig( 1, 1, 1, ResourceNodeConfig{ 100, string( "/card[0]/sdiConnector[0]" ) } ); configureResourceConnectionInFilteredConfig( 1, 1, 1, ConnectionItemConfig{ 100, 200, 300, 400, 500 } ); configureStreamConnectionInFilteredConfig( 1, 1, StreamConnectionItemConfig{ 100, 200, 300, 400, 500, 600, 700 } ); stringstream filter; filter << " "; checkSubtreeFilteredConfig( filter.str() ); clearFilteredConfig(); BOOST_TEST_MESSAGE("Test: Checking with subtree filter: empty filter"); vector expPresent{ "data", "rpc-reply" }; vector expNotPresent{ "xpo" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetConfigSubtree( primarySession_, "", writeableDbName_, checker ); //TODO Test attribute matching expressions BOOST_TEST_MESSAGE("Test: Checking with subtree filter: complex content match"); configureResourceDescrptionInFilteredConfig( 1, 1, 1, ResourceNodeConfig{ 100, string( "/card[0]/sdiConnector[0]" ) } ); configureStreamConnectionInFilteredConfig( 1, 1, StreamConnectionItemConfig{ 100, 200, 300, 400, 500, 600, 700 } ); filter.str(""); filter << " \n" << " \n" << " 1\n" << " \n" << " 1\n" << " \n" << " 1\n" << " \n" << " \n" << " \n" << " 1\n" << " \n" << " \n" << " "; checkSubtreeFilteredConfig( filter.str() ); clearFilteredConfig(); BOOST_TEST_MESSAGE("Test: Checking with subtree filter: request the resource type of a resource node"); configureResourceDescrptionInFilteredConfig(1, 1, 1, ResourceNodeConfig{ 100, boost::optional() } ); filter.str(""); filter << " \n" << " \n" << " 1\n" << " \n" << " 1\n" << " \n" << " 1\n" << " \n" << " \n" << " \n" << " \n" << " "; checkSubtreeFilteredConfig( filter.str() ); clearFilteredConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| // Create content and check with malformed subtree filter // rejected // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( GetConfigWithSubtreeFilterFailure ) { DisplayTestDescrption( "Demonstrate with malformed subtree filter rejected.", "Procedure: \n" "\t 1 - Create the XPO3 container\n" "\t 2 - Add profile-1 to the XPO3 container\n" "\t 3 - Add Stream-1 to the profile-1\n" "\t 4 - Add resourceNode-1 to stream-1\n" "\t 5 - Add resourceConnection-1 to stream-1\n" "\t 7 - Configure resourceNode-1\n" "\t 8 - Configure resourceConnection-1\n" "\t 9 - Add StreamConnection-1\n" "\t10 - Configure StreamConnection-1\n" "\t11 - Set the active profile to profile-1\n" "\t12 - Commit the changes \n" "\t13 - Query netconf for filtered config data with invalid subtree " "and verify response\n" "\t14 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createXpoContainer( primarySession_ ); xpoProfileQuery( primarySession_, 1 ); profileStreamQuery( primarySession_, 1, 1 ); configureResourceDescrption( primarySession_, 1, 1, 1, ResourceNodeConfig{ 100, string( "/card[0]/sdiConnector[0]" ) } ); configureResourceConnection( primarySession_, 1, 1, 1, ConnectionItemConfig{ 100, 200, 300, 400, 500 } ); configureStreamConnection( primarySession_, 1, 1, StreamConnectionItemConfig{ 100, 200, 300, 400, 500, 600, 700 } ); setActiveProfile( primarySession_, 1 ); commitChanges( primarySession_ ); checkConfig(); // Response to malformed subtree expression is the same as empty subtree, // not sure if this is correct BOOST_TEST_MESSAGE("Test: Checking with subtree filter: malformed subtree expression"); vector expPresent{ "data", "rpc-reply" }; vector expNotPresent{ "xpo" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetConfigSubtree( primarySession_, "%~", writeableDbName_, checker ); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| // Create content and check // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( GetSchema ) { DisplayTestDescrption( "Demonstrate .", "Procedure: \n" "\t 1 - Create the XPO3 container\n" "\t 2 - Add profile-1 to the XPO3 container\n" "\t 3 - Add Stream-1 to the profile-1\n" "\t 4 - Add resourceNode-1 to stream-1\n" "\t 5 - Add resourceConnection-1 to stream-1\n" "\t 7 - Configure resourceNode-1\n" "\t 8 - Configure resourceConnection-1\n" "\t 9 - Add StreamConnection-1\n" "\t10 - Configure StreamConnection-1\n" "\t11 - Set the active profile to profile-1\n" "\t12 - Commit the changes \n" "\t13 - Query netconf for the device_test schema\n" "\t14 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createXpoContainer( primarySession_ ); xpoProfileQuery( primarySession_, 1 ); profileStreamQuery( primarySession_, 1, 1 ); configureResourceDescrption( primarySession_, 1, 1, 1, ResourceNodeConfig{ 100, string( "a configuration"), string( "a status configuration"), string( "an alarm configuration"), string( "/card[0]/sdiConnector[0]") } ); configureResourceConnection( primarySession_, 1, 1, 1, ConnectionItemConfig{ 100, 200, 300, 400, 500 } ); configureStreamConnection( primarySession_, 1, 1, StreamConnectionItemConfig{ 100, 200, 300, 400, 500, 600, 700 } ); setActiveProfile( primarySession_, 1 ); commitChanges( primarySession_ ); checkConfig(); BOOST_TEST_MESSAGE("Test: Checking "); vector expPresent{ "namespace \"http://netconfcentral.org/ns/device_test\"", "Start of main configuration block" }; vector expNotPresent{ "error", "rpc-error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetSchema( primarySession_, "device_test", checker ); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/simple-choice-tests.cpp0000664000175000017500000000744014770023131025711 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-yang-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( simple_choice, SimpleYangFixture ) BOOST_AUTO_TEST_CASE( add_choice ) { DisplayTestDescrption( "Demonstrate that when a new choice is created the old choice is " "automatically removed.", "Procedure: \n" "\t1 - Create the top level container\n" "\t2 - Add tcp choice\n" "\t3 - Check choice is now tcp\n" "\t4 - Add udp choice\n" "\t5 - Check choice is now udp (and tcp leaf has been removed)\n" "\t6 - Add tcp choice\n" "\t7 - Check choice is now tcp (and udp leaf has been removed)\n" "\t8 - Delete the top level container\n" "\t9 - Check choice has been removed\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // create the top level container createContainer( primarySession_, "protocol" ); checkChoice( primarySession_ ); // add tcp addChoice( primarySession_, "tcp", "create" ); checkChoice( primarySession_ ); //Reset logged callbacks cbChecker_->resetModuleCallbacks("simple_yang_test"); cbChecker_->resetExpectedCallbacks(); // add udp addChoice( primarySession_, "udp", "create" ); // Check callbacks vector udpElements = {"name", "udp", "udp"}; vector tcpElements = {"name", "tcp", "tcp"}; cbChecker_->addChoice("simple_yang_test", "protocol", udpElements, tcpElements); //TODO Check is currently disabled as yuma functionality is broken //cbChecker_->checkCallbacks("simple_yang_test"); cbChecker_->resetModuleCallbacks("simple_yang_test"); cbChecker_->resetExpectedCallbacks(); checkChoice( primarySession_ ); // add tcp addChoice( primarySession_, "tcp", "create" ); // Check callbacks cbChecker_->addChoice("simple_yang_test", "protocol", tcpElements, udpElements); //TODO Check is currently disabled as yuma functionality is broken //cbChecker_->checkCallbacks("simple_yang_test"); cbChecker_->resetModuleCallbacks("simple_yang_test"); cbChecker_->resetExpectedCallbacks(); checkChoice( primarySession_ ); // remove container deleteContainer( primarySession_, "protocol" ); checkChoice( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/db-lock-test-suite-running.cpp0000664000175000017500000000437314770023131027127 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/query-suite-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( db_lock_test_suite_running, QuerySuiteFixture ) // ---------------------------------------------------------------------------| // Simple lock and unlock of the target and running database // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( lock_candidate_running_db ) { DisplayTestDescrption( "Demonstrate that the candidate database cannot be locked with " "the writeable running configuration.", "Procedure: \n" "\t1 - Attempt to Lock the candidate database\n" ); vector failLockPresentText{ "error", "invalid-value", "config not found" }; vector failAbsentText{ "ok" }; StringsPresentNotPresentChecker failChecker( failLockPresentText, failAbsentText ); queryEngine_->tryLockDatabase( primarySession_, "candidate", failChecker ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/callback-failures-tests.cpp0000664000175000017500000002553714770023131026543 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include #include "test/include/boost_includes.hpp" // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-container-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/callbacks/sil-callback-controller.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| namespace ph = boost::phoenix; namespace ph_args = boost::phoenix::arg_names; using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( callback_failure_tests, SimpleContainerModuleFixture ) #if 0 //TODO Test needs updating when callback functionality is fixed BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( slt_validate_failure, 49 ) BOOST_AUTO_TEST_CASE( slt_validate_failure ) { DisplayTestDescrption( "Demonstrate correct behaviour following validate callback failure.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - TODO\n" ); SILCallbackController& cbCon = SILCallbackController::getInstance(); cbCon.reset(); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); //Reset logged callbacks cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); cbCon.setValidateSuccess(false); // set some values populateDatabase( 3 ); // Check callbacks vector elements = {"theList"}; cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // check the entries exist checkEntries( primarySession_ ); // commit the changes commitChanges( primarySession_ ); // TODO Check callbacks //cbChecker_->checkCallbacks("simple_list_test"); //cbChecker_->resetModuleCallbacks("simple_list_test"); //cbChecker_->resetExpectedCallbacks(); // check the entries exist checkEntries( primarySession_ ); cbCon.reset(); // remove all entries deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } #endif //TODO Test needs updating when callback functionality is fixed BOOST_AUTO_TEST_CASE( slt_apply_failure ) { DisplayTestDescrption( "Demonstrate correct behaviour following apply callback failure.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - TODO\n" ); SILCallbackController& cbCon = SILCallbackController::getInstance(); cbCon.reset(); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); //Reset logged callbacks cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // set some values populateDatabase( 3 ); // Check callbacks vector elements = {"theList"}; cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // check the entries exist checkEntries( primarySession_ ); cbCon.setApplySuccess(false); // commit the changes should fail commitChangesFailure( primarySession_ ); // TODO Check callbacks //cbChecker_->checkCallbacks("simple_list_test"); //cbChecker_->resetModuleCallbacks("simple_list_test"); //cbChecker_->resetExpectedCallbacks(); // TODO Candidate entries are removed when commit fails which is // TODO not the same behaviour as seen when a commit callback // TODO fails in the later tests. // TODO Behaviour should be made standard. // check the entries exist checkEntries( primarySession_ ); cbCon.reset(); // remove all entries //deleteMainContainer( primarySession_ ); //checkEntries( primarySession_ ); //commitChanges( primarySession_ ); //checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( slt_create_failure ) { DisplayTestDescrption( "Demonstrate correct behaviour following create callback failure.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Populate the list\n" "\t 3 - Force the commit callback to fail\n" "\t 4 - Check that data is rolled back\n" ); SILCallbackController& cbCon = SILCallbackController::getInstance(); cbCon.reset(); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); //Reset logged callbacks cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // set some values populateDatabase( 3 ); // Check callbacks vector elements = {"theList"}; cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // check the entries exist checkEntries( primarySession_ ); // cause first commit to fail cbCon.setCreateSuccess(false); // commit the changes should fail commitChangesFailure( primarySession_ ); // TODO Check callbacks //cbChecker_->checkCallbacks("simple_list_test"); //cbChecker_->resetModuleCallbacks("simple_list_test"); //cbChecker_->resetExpectedCallbacks(); // check the entries exist checkEntries( primarySession_ ); cbCon.reset(); // remove all entries deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( slt_delete_failure ) { DisplayTestDescrption( "Demonstrate correct behaviour following delete callback failure.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Populate the list\n" "\t 2 - Commit the list\n" "\t 3 - Delete the main container\n" "\t 4 - Force the commit callback to fail\n" "\t 5 - Check that data is rolled back\n" ); SILCallbackController& cbCon = SILCallbackController::getInstance(); cbCon.reset(); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); //Reset logged callbacks cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // set some values populateDatabase( 3 ); // Check callbacks vector elements = {"theList"}; cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->addKeyValuePair("simple_list_test", "simple_list", elements, "theKey", "theVal"); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // check the entries exist checkEntries( primarySession_ ); // commit the changes commitChanges( primarySession_ ); // Check callbacks cbChecker_->commitKeyValuePairs("simple_list_test", "simple_list", elements, "theKey", "theVal", 3); cbChecker_->checkCallbacks("simple_list_test"); cbChecker_->resetModuleCallbacks("simple_list_test"); cbChecker_->resetExpectedCallbacks(); // check the entries exist checkEntries( primarySession_ ); cbCon.setDeleteSuccess(false); // remove all entries deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); commitChangesFailure( primarySession_ ); // TODO Check callbacks //cbChecker_->checkCallbacks("simple_list_test"); //cbChecker_->resetModuleCallbacks("simple_list_test"); //cbChecker_->resetExpectedCallbacks(); checkEntries( primarySession_ ); cbCon.reset(); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/db-lock-test-suite-candidate.cpp0000664000175000017500000001472514770023131027365 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/query-suite-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( db_lock_test_suite_candidate, QuerySuiteFixture ) // ---------------------------------------------------------------------------| // Simple lock and unlock of the target and running database // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( lock_unlock_candidate_running_db ) { DisplayTestDescrption( "Demonstrate locking and unlocking of the running " "and candidate databases.", "Procedure: \n" "\t1 - Lock the running database\n" "\t2 - Unlock the running database" ); vector expPresent{ "ok" }; vector expNotPresent{ "lock-denied", "error" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryLockDatabase( primarySession_, "running", checker ); queryEngine_->tryLockDatabase( primarySession_, "candidate", checker ); queryEngine_->tryUnlockDatabase( primarySession_, "candidate", checker ); queryEngine_->tryUnlockDatabase( primarySession_, "running", checker ); } // ---------------------------------------------------------------------------| // Multiple attempts to lock the target database - different session // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( deadlock_running_candidate_dbs ) { DisplayTestDescrption( "Demonstrate deadlocking is handled whereby when different " "sessions lock the running and candidate databases.", "Procedure: \n" "\t1 - Session 1 Locks the running database\n" "\t2 - Session 2 Locks the candidate database\n" "\t3 - Session 1 attempts to Lock the candidate database" " (should fail)\n" "\t4 - Session 2 attempts to Lock the running database" " (should fail)\n" "\t5 - Session 1 attempts to Unlock the candidate database" " (should fail)\n" "\t6 - Session 2 attempts to Unlock the running database" " (should fail)\n" "\t7 - Session 2 Unlocks the candidate database\n" "\t8 - Session 1 Locks the candidate database\n" "\t9 - Session 1 Unlocks the candidate database\n" "\t10 - Session 1 Unlocks the running database\n" "\t11 - Session 2 Locks the running database\n" "\t12 - Session 2 Locks the candidate database\n" "\t13 - Session 1 Unlocks the candidate database\n" "\t14 - Session 1 Unlocks the running database\n" ); // setup the expected results vector successPresentText{ "ok" }; vector successAbsentText{ "lock-denied", "error" }; StringsPresentNotPresentChecker successChecker( successPresentText, successAbsentText ); vector failAbsentText{ "ok" }; vector failLockPresentText{ "error", "no-access", "lock-denied" }; vector failUnlockPresentText{ "error", "no-access", "in-use", "config locked" }; StringsPresentNotPresentChecker lockFailChecker( failLockPresentText, failAbsentText ); StringsPresentNotPresentChecker unlockFailChecker( failUnlockPresentText, failAbsentText ); // get a secondary session std::shared_ptr secondarySession( sessionFactory_->createSession() ); // 1 - Ses 1 Locks running queryEngine_->tryLockDatabase( primarySession_, "running", successChecker ); // 2 - Ses 2 Locks candidate queryEngine_->tryLockDatabase( secondarySession, "candidate", successChecker ); // 3 - Ses 1 attempts Lock candidate queryEngine_->tryLockDatabase( primarySession_, "candidate", lockFailChecker ); // 4 - Ses 2 attempts Lock running queryEngine_->tryLockDatabase( secondarySession, "running", lockFailChecker ); // 5 - Ses 1 attempts Unlock candidate queryEngine_->tryUnlockDatabase( primarySession_, "candidate", unlockFailChecker ); // 6 - Ses 2 attempts Unlock running queryEngine_->tryUnlockDatabase( secondarySession, "running", unlockFailChecker ); // 7 - Ses 2 Unlocks candidate queryEngine_->tryUnlockDatabase( secondarySession, "candidate", successChecker ); // 8 - Ses 1 Locks candidate queryEngine_->tryLockDatabase( primarySession_, "candidate", successChecker ); // 9 - Ses 1 Unlocks candidate queryEngine_->tryUnlockDatabase( primarySession_, "candidate", successChecker ); // 10 - Ses 1 Unlocks running queryEngine_->tryUnlockDatabase( primarySession_, "running", successChecker ); // 11 - Ses 2 Locks running queryEngine_->tryLockDatabase( secondarySession, "running", successChecker ); // 12 - Ses 2 Locks candidate queryEngine_->tryLockDatabase( secondarySession, "candidate", successChecker ); // 13 - Ses 2 Unlocks candidate queryEngine_->tryUnlockDatabase( secondarySession, "candidate", successChecker ); // 14 - Ses 2 Unlocks running queryEngine_->tryUnlockDatabase( secondarySession, "running", successChecker ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/device-tests-create.cpp0000664000175000017500000001557014770023131025673 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include #include "test/include/boost_includes.hpp" // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/device-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-query-util/nc-default-operation-config.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/callbacks/sil-callback-log.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| namespace ph = boost::phoenix; namespace ph_args = boost::phoenix::arg_names; using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( DTCreate, DeviceModuleFixture ) // ---------------------------------------------------------------------------| // Add some data and check that it was added correctly // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( FullProfile ) { DisplayTestDescrption( "Demonstrate operation for Creation of XPO3 Container " "Profile, Stream & Resource entries.", "Procedure: \n" "\t 1 - Create the XPO3 container\n" "\t 2 - Add profile-1 to the XPO3 container\n" "\t 3 - Add Stream-1 to the profile-1\n" "\t 4 - Add resourceNode-1 to stream-1\n" "\t 5 - Add resourceConnection-1 to stream-1\n" "\t 7 - Configure resourceNode-1\n" "\t 8 - Configure resourceConnection-1\n" "\t 9 - Add StreamConnection-1\n" "\t10 - Configure StreamConnection-1\n" "\t11 - Set the active profile to profile-1\n" "\t12 - Commit the changes \n" "\t13 - Query netconf and check that all elements exist in the " "netconf database\n" "\t14 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); createXpoContainer( primarySession_ ); xpoProfileQuery( primarySession_, 1 ); profileStreamQuery( primarySession_, 1, 1 ); configureResourceDescrption( primarySession_, 1, 1, 1, ResourceNodeConfig{ 100, string( "111100001111" ), boost::optional(), boost::optional(), string( "/card[0]/sdiConnector[0]" ) } ); configureResourceConnection( primarySession_, 1, 1, 1, ConnectionItemConfig{ 100, 200, 300, 400, 500 } ); configureStreamConnection( primarySession_, 1, 1, StreamConnectionItemConfig{ 100, 200, 300, 400, 500, 600, 700 } ); setActiveProfile( primarySession_, 1 ); // Check callbacks vector elements = {}; cbChecker_->updateContainer("device_test", "xpo", elements, "create"); elements.push_back("profile"); cbChecker_->addKey("device_test", "xpo", elements, "id"); elements.push_back("stream"); cbChecker_->addKey("device_test", "xpo", elements, "id"); cbChecker_->addResourceNode("device_test", "xpo", elements, false, false); cbChecker_->addResourceCon("device_test", "xpo", elements); elements.pop_back(); cbChecker_->addStreamCon("device_test", "xpo", elements); elements.pop_back(); elements.push_back("activeProfile"); cbChecker_->addElement("device_test", "xpo", elements); cbChecker_->checkCallbacks("device_test"); cbChecker_->resetModuleCallbacks("device_test"); cbChecker_->resetExpectedCallbacks(); commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } BOOST_AUTO_TEST_CASE( CreateExistingProfile ) { DisplayTestDescrption( "Demonstrate operations on an existing node are rejected", "Procedure: \n" "\t 1 - Create Profile1\n" "\t 2 - Create Profile2\n" "\t 3 - Create Profile3\n" "\t 4 - Commit the changes\n" "\t 5 - Check the configuration content\n" "\t 6 - Set the default operation to 'none'\n" "\t 7 - Attempt to Create Profile2 again\n" "\t 8 - Check the operation was rejected with 'data-exists'\n" "\t 9 - Set the default operation to 'merge'\n" "\t10 - Attempt to Create Profile2 again\n" "\t11 - Check the operation was rejected with 'data-exists'\n" "\t12 - Commit the changes\n" "\t13 - Check the configuration content\n" "\t14 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("device_test"); createXpoContainer( primarySession_ ); xpoProfileQuery( primarySession_, 1 ); xpoProfileQuery( primarySession_, 2 ); xpoProfileQuery( primarySession_, 3 ); setActiveProfile( primarySession_, 1 ); commitChanges( primarySession_ ); checkConfig(); { DefaultOperationConfig opConfig( xpoBuilder_, "none" ); xpoProfileQuery( primarySession_, 2, "create", "data-exists" ); } { DefaultOperationConfig opConfig( xpoBuilder_, "merge" ); xpoProfileQuery( primarySession_, 2, "create", "data-exists" ); } commitChanges( primarySession_ ); checkConfig(); deleteXpoContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/common/state-data-tests-get.cpp0000664000175000017500000002534014770023131025773 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Boost Includes // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/state-get-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-query-util/nc-default-operation-config.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/callbacks/sil-callback-log.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| // ---------------------------------------------------------------------------| // File wide namespace use and aliases // ---------------------------------------------------------------------------| using namespace std; // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( SDGet, StateGetModuleFixture ) // ---------------------------------------------------------------------------| // Create content and check filtered state data can be retrieved correctly // using xpath filter // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( GetStateDataWithXpathFilterSuccess ) { DisplayTestDescrption( "Demonstrate successful with xpath filter operations " "on toaster Container.", "Procedure: \n" "\t 1 - Create the Toaster container\n" "\t 2 - Commit the changes \n" "\t 3 - Query netconf for filtered state data with valid xpath and " "verify response\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("state_data_test"); // TODO: Add callback checking.... createToasterContainer( primarySession_ ); configureToaster( ToasterContainerConfig{ string( "Acme, Inc." ), string( "Super Toastamatic 2000" ), string( "up" ) } ); commitChanges( primarySession_ ); BOOST_TEST_MESSAGE("Test: Checking state data in database"); checkState(); BOOST_TEST_MESSAGE("Test: Checking with filter: request toasterManufacturer"); configureToasterInFilteredState( ToasterContainerConfig{ string( "Acme, Inc." ), boost::optional(), boost::optional() } ); checkXpathFilteredState("/toaster/toasterManufacturer"); clearFilteredState(); BOOST_TEST_MESSAGE("Test: Checking with filter: request toasterModelNumber"); configureToasterInFilteredState( ToasterContainerConfig{ boost::optional(), string( "Super Toastamatic 2000" ), boost::optional() } ); checkXpathFilteredState("/toaster/toasterModelNumber"); clearFilteredState(); BOOST_TEST_MESSAGE("Test: Checking with filter: request toasterStatus"); configureToasterInFilteredState( ToasterContainerConfig{ boost::optional(), boost::optional(), string( "up" ) } ); checkXpathFilteredState("/toaster/toasterStatus"); clearFilteredState(); deleteToasterContainer( primarySession_ ); } // ---------------------------------------------------------------------------| // Create content and check with malformed xpath filter rejected // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( GetStateDataWithXpathFilterFailure ) { DisplayTestDescrption( "Demonstrate with malformed xpath filter rejected.", "Procedure: \n" "\t 1 - Create the Toaster container\n" "\t 2 - Commit the changes \n" "\t 3 - Query netconf for filtered state data with invalid xpath " "and verify response\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("state_data_test"); // TODO: Add callback checking.... createToasterContainer( primarySession_ ); configureToaster( ToasterContainerConfig{ string( "Acme, Inc." ), string( "Super Toastamatic 2000" ), string( "up" ) } ); commitChanges( primarySession_ ); BOOST_TEST_MESSAGE("Test: Checking state data in database"); checkState(); BOOST_TEST_MESSAGE("Test: Checking with xpath filter: request includes invalid xpath expression"); vector expPresent{ "error", "rpc-error", "invalid XPath expression syntax" }; vector expNotPresent{ "ok" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetXpath( primarySession_, "%%>", writeableDbName_, checker ); deleteToasterContainer( primarySession_ ); } // ---------------------------------------------------------------------------| // Create content and check filtered state data can be retrieved correctly // using subtree filter // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( GetStateDataWithSubtreeFilterSuccess ) { DisplayTestDescrption( "Demonstrate successful with subtree filter operations " "on toaster Container.", "Procedure: \n" "\t 1 - Create the Toaster container\n" "\t 2 - Commit the changes \n" "\t 3 - Query netconf for filtered state data with valid subtree and " "verify response\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("state_data_test"); // TODO: Add callback checking.... createToasterContainer( primarySession_ ); configureToaster( ToasterContainerConfig{ string( "Acme, Inc." ), string( "Super Toastamatic 2000" ), string( "up" ) } ); commitChanges( primarySession_ ); BOOST_TEST_MESSAGE("Test: Checking state data in database"); checkState(); BOOST_TEST_MESSAGE("Test: Checking with subtree filter: select the entire namespace"); configureToasterInFilteredState( ToasterContainerConfig{ string( "Acme, Inc." ), string( "Super Toastamatic 2000" ), string( "up" ) } ); stringstream filter; filter << " "; checkSubtreeFilteredState( filter.str() ); clearFilteredState(); BOOST_TEST_MESSAGE("Test: Checking with subtree filter: empty filter"); vector expPresent{ "data", "rpc-reply" }; vector expNotPresent{ "toaster" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetSubtree( primarySession_, "", writeableDbName_, checker ); //TODO Test attribute matching expressions BOOST_TEST_MESSAGE("Test: Checking with subtree filter: complex content match"); configureToasterInFilteredState( ToasterContainerConfig{ string( "Acme, Inc." ), boost::optional(), string( "up" ) } ); filter.str(""); filter << " \n" << " \n" << " \n" << " "; checkSubtreeFilteredState( filter.str() ); clearFilteredState(); deleteToasterContainer( primarySession_ ); } // ---------------------------------------------------------------------------| // Create content and check with malformed subtree filter // rejected // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( GetStateDataWithSubtreeFilterFailure ) { DisplayTestDescrption( "Demonstrate with malformed subtree filter rejected.", "Procedure: \n" "\t 1 - Create the Toaster container\n" "\t 2 - Commit the changes \n" "\t 3 - Query netconf for filtered state data with invalid subtree " "and verify response\n" "\t 4 - Flush the contents so the container is empty for the next test\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); // Reset logged callbacks cbChecker_->resetExpectedCallbacks(); cbChecker_->resetModuleCallbacks("state_data_test"); // TODO: Add callback checking.... createToasterContainer( primarySession_ ); configureToaster( ToasterContainerConfig{ string( "Acme, Inc." ), string( "Super Toastamatic 2000" ), string( "up" ) } ); commitChanges( primarySession_ ); BOOST_TEST_MESSAGE("Test: Checking state data in database"); checkState(); // Response to malformed subtree expression is the same as empty subtree, // not sure if this is correct BOOST_TEST_MESSAGE("Test: Checking with subtree filter: malformed subtree expression"); vector expPresent{ "data", "rpc-reply" }; vector expNotPresent{ "toaster" }; StringsPresentNotPresentChecker checker( expPresent, expNotPresent ); queryEngine_->tryGetSubtree( primarySession_, "%~", writeableDbName_, checker ); deleteToasterContainer( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/integ/0000775000175000017500000000000014770023131021135 5ustar vladimirvladimiryuma123_2.14/netconf/test/test-suites/integ/test_boost_unit_test_framework.cpp0000664000175000017500000000103614770023131030201 0ustar vladimirvladimir#define BOOST_TEST_MODULE example #include BOOST_AUTO_TEST_SUITE( test_suite1 ) BOOST_AUTO_TEST_CASE( test_case1 ) { BOOST_WARN( sizeof(int) < 4 ); } BOOST_AUTO_TEST_CASE( test_case2 ) { BOOST_REQUIRE_EQUAL( 1, 2 ); BOOST_FAIL( "Should never reach this line" ); } BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE( test_suite2 ) BOOST_AUTO_TEST_CASE( test_case3 ) { BOOST_CHECK( true ); } BOOST_AUTO_TEST_CASE( test_case4 ) { BOOST_CHECK( false ); } BOOST_AUTO_TEST_SUITE_END() yuma123_2.14/netconf/test/test-suites/integ/test_yangcli_wordex.c0000664000175000017500000000230414770023131025355 0ustar vladimirvladimir#include #include #include "yangcli_wordexp.h" int main(char** argv, unsigned int argc) { int ret; yangcli_wordexp_t my_wordexp; /* #1 - basic */ ret = yangcli_wordexp ("connect user=root server=192.168.209.154", &my_wordexp, 0); assert(ret==0); assert(my_wordexp.we_wordc==3); assert(0==strcmp(my_wordexp.we_wordv[0],"connect")); assert(my_wordexp.we_word_line_offset[0]==0); assert(0==strcmp(my_wordexp.we_wordv[1],"user=root")); assert(my_wordexp.we_word_line_offset[1]==8); assert(0==strcmp(my_wordexp.we_wordv[2],"server=192.168.209.154")); assert(my_wordexp.we_word_line_offset[2]==18); yangcli_wordfree(&my_wordexp); /* #2 - striping spaces between tokens, ignoring spaces in quotes */ ret = yangcli_wordexp (" xget /interfaces/interface[name='blah blah'] ", &my_wordexp, 0); assert(ret==0); assert(my_wordexp.we_wordc==2); assert(0==strcmp(my_wordexp.we_wordv[0],"xget")); assert(my_wordexp.we_word_line_offset[0]==2); assert(0==strcmp(my_wordexp.we_wordv[1],"/interfaces/interface[name='blah blah']")); assert(my_wordexp.we_word_line_offset[1]==8); yangcli_wordfree(&my_wordexp); return 0; } yuma123_2.14/netconf/test/test-suites/integ/base-64-tests.cpp0000664000175000017500000003460614770023131024153 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Standard Includes // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/base-suite-fixture.h" #include "test/support/misc-util/base64.h" #include "test/support/misc-util/log-utils.h" #include "b64.h" #include "status.h" // ---------------------------------------------------------------------------| using namespace std; using namespace YumaTest; // ---------------------------------------------------------------------------| namespace { void test_yuma_b64_decode( const string& rawText ) { string rawEncoded = base64_encode( rawText ); char decodedChars[ 200 ]; unsigned int decodedLen = 0; BOOST_CHECK( rawText.length() <= b64_get_decoded_str_len( reinterpret_cast( rawEncoded.c_str() ), rawEncoded.length() ) ); BOOST_REQUIRE_EQUAL( NO_ERR, b64_decode( reinterpret_cast< const uint8_t* >( rawEncoded.c_str() ), rawEncoded.length(), reinterpret_cast< uint8_t* >( decodedChars ), 200, &decodedLen ) ); BOOST_CHECK_EQUAL( rawText.length(), decodedLen ); BOOST_CHECK_EQUAL( rawText, string( decodedChars, decodedLen ) ); } void test_yuma_b64_decode_Ins( const string& rawText, const string& insText, uint16_t insPos, bool valid=true ) { string rawEncoded = base64_encode( rawText ); string modified( rawEncoded ); modified.insert(insPos, insText ); modified += "\r\n"; char decodedChars[ 200 ]; unsigned int decodedLen = 0; BOOST_REQUIRE_EQUAL( NO_ERR, b64_decode( reinterpret_cast< const uint8_t* >( modified.c_str() ), modified.length(), reinterpret_cast< uint8_t* >( decodedChars ), 200, &decodedLen ) ); BOOST_CHECK_EQUAL( decodedLen, b64_get_decoded_str_len( reinterpret_cast( modified.c_str() ), modified.length() ) ); if ( valid ) { BOOST_CHECK_EQUAL( rawText.length(), decodedLen ); BOOST_CHECK_EQUAL( rawText, string( decodedChars, decodedLen ) ); } else { // check the decoded string was truncated to insPos length uint16_t expLen = 0; switch ( insPos%4 ) { case 0: expLen = (insPos/4)*3; break; case 1: expLen = (insPos/4)*3; break; case 2: expLen = (insPos%4)-1+(insPos/4)*3; break; case 3: expLen = (insPos%4)-1+(insPos/4)*3; break; } BOOST_CHECK_EQUAL( expLen, decodedLen ); } } void test_yuma_b64_encode( const string& rawText ) { // TODO: test embedding of CR-LF characters string th_encoded = base64_encode( rawText ); string th_encoded_appended = th_encoded += "\r\n\0"; // encode using yumas b64_encode char encodedChars[ 200 ]; unsigned int encodedLen = 0; BOOST_REQUIRE_EQUAL( NO_ERR, b64_encode( reinterpret_cast< const unsigned char*>( rawText.c_str() ), rawText.length(), reinterpret_cast< unsigned char* >( encodedChars ), 200, 200, &encodedLen ) ); string yumaEncoded( encodedChars, encodedLen ); // check that the lengths match BOOST_CHECK_EQUAL( th_encoded_appended.length(), encodedLen ); // check that the encoded string is as expected BOOST_CHECK_EQUAL( th_encoded_appended, yumaEncoded ); // check that the string decodes correctly BOOST_CHECK_EQUAL( rawText, base64_decode( yumaEncoded ) ); } } // anonymous namespace // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( Base64Tests, BaseSuiteFixture ) // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_large ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 1)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); string rawText = "ADP GmbH\nAnalyse Design & Programmierung\n" "Gesellschaft mit beschränkter Haftung"; test_yuma_b64_decode( rawText ); test_yuma_b64_encode( rawText ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_1 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 1)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "0" ); test_yuma_b64_decode( "1" ); test_yuma_b64_encode( "1" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_2 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 2)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "01" ); test_yuma_b64_decode( "11" ); test_yuma_b64_encode( "11" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_3 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 3)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "012" ); test_yuma_b64_decode( "111" ); test_yuma_b64_encode( "111" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_4 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 4)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "0123" ); test_yuma_b64_decode( "1111" ); test_yuma_b64_encode( "1111" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_5 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 5)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "01234" ); test_yuma_b64_decode( "11110" ); test_yuma_b64_encode( "11110" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_6 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 6)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "012345" ); test_yuma_b64_decode( "111100" ); test_yuma_b64_encode( "111100" ); } BOOST_AUTO_TEST_CASE( encode_decode_7 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 7)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "0123456" ); test_yuma_b64_decode( "1111000" ); test_yuma_b64_encode( "1111000" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_8 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 8)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "01234567" ); test_yuma_b64_decode( "11110000" ); test_yuma_b64_encode( "11110000" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_9 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 9)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "012345678" ); test_yuma_b64_decode( "111100001" ); test_yuma_b64_encode( "111100001" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_10 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 10)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "0123456789" ); test_yuma_b64_decode( "1111000011" ); test_yuma_b64_encode( "1111000011" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_11 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 11)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "0123456789A" ); test_yuma_b64_decode( "11110000111" ); test_yuma_b64_encode( "11110000111" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_12 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 12)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "0123456789AB" ); test_yuma_b64_decode( "111100001111" ); test_yuma_b64_encode( "111100001111" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_13 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 13)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "0123456789ABC" ); test_yuma_b64_decode( "1111000011110" ); test_yuma_b64_encode( "1111000011110" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_14 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 14)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "0123456789ABCD" ); test_yuma_b64_decode( "11110000111100" ); test_yuma_b64_encode( "11110000111100" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_15 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 15)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "0123456789ABCDE" ); test_yuma_b64_decode( "111100001111000" ); test_yuma_b64_encode( "111100001111000" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_16 ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string (length = 16)", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode( "0123456789ABCDEF" ); test_yuma_b64_decode( "1111000011110000" ); test_yuma_b64_encode( "1111000011110000" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_crlf ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string " "containing CR-LF characters", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "\r\n", 8 ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_cr ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string " "containing CR characters", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "\r", 1 ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "\r", 2 ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "\r", 3 ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_nl ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string " "containing LF characters", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "\n", 11 ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( encode_decode_invalid ) { DisplayTestDescrption( "Demonstrate Encode/Decode of Base64 format of string " "containing invalid characters", "Procedure: \n" "\t 1 - Encode and Decode the string\n" ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "!", 0, false ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "!", 1, false ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "£", 2, false ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "$", 3, false ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "%", 4, false ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "^", 5, false ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "*", 6, false ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", "(", 7, false ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", ")", 8, false ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", ")", 9, false ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", ")", 10, false ); test_yuma_b64_decode_Ins( "0123456789ABCDEF", ")", 11, false ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/integ/test-b64.c0000664000175000017500000000723414770023131022657 0ustar vladimirvladimir#include #include #include #include #include "b64.h" int check_b64_transcode(uint32_t data_len, uint32_t linesize) { status_t res; uint8_t* inbuff; uint8_t* outbuff; uint8_t* transcoded_buff; uint32_t retlen; uint32_t transcoded_retlen; uint32_t expected_retlen; uint32_t u; inbuff=(uint8_t*)malloc(data_len); transcoded_buff=(uint8_t*)malloc(data_len); expected_retlen=4*((data_len+2)/3); if(linesize) { expected_retlen+=2*(expected_retlen/linesize); } outbuff=(uint8_t*)malloc(expected_retlen+1); /* NULL termination */ for(u=0;u<((data_len+3)/4);u++) { inbuff[u*4+0]=(u&0x000000FF)>>0; if((u*4+1)>=data_len) break; inbuff[u*4+1]=(u&0x0000FF00)>>8; if((u*4+2)>=data_len) break; inbuff[u*4+2]=(u&0x00FF0000)>>16; if((u*4+3)>=data_len) break; inbuff[u*4+3]=(u&0xFF000000)>>24; } res=b64_encode(inbuff, data_len, outbuff, expected_retlen+1, linesize, &retlen); printf("%s\n",outbuff); assert(res==0); assert(retlen==expected_retlen); res=b64_decode(outbuff, retlen+1, transcoded_buff, data_len, &transcoded_retlen); assert(transcoded_retlen==data_len); assert(0==memcmp(inbuff,transcoded_buff,data_len)); free(inbuff); free(outbuff); free(transcoded_buff); return 0; } #define KNOWN_PAIRS_NUM 10 char* known_pairs[][2]={ {"1", "MQ=="}, {"12", "MTI="}, {"123", "MTIz"}, {"1234", "MTIzNA=="}, {"12345", "MTIzNDU="}, {"123456", "MTIzNDU2"}, {"1234567", "MTIzNDU2Nw=="}, {"12345678", "MTIzNDU2Nzg="}, {"123456789", "MTIzNDU2Nzg5"}, {"1234567890", "MTIzNDU2Nzg5MA=="} }; void check_b64_transcoder_w_known_pairs() { status_t res; int i; uint32_t result_len; uint32_t expected_result_len; uint8_t* result_buff; printf("%d known pairs\n",KNOWN_PAIRS_NUM); for(i=0;i // ---------------------------------------------------------------------------| // Standard includes // ---------------------------------------------------------------------------| #include #include #include #include #include #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-container-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( confirmed_commit_slt_tests, SimpleContainerModuleFixture ) BOOST_AUTO_TEST_CASE( slt_confirmed_commit_success ) { DisplayTestDescrption( "Demonstrate population of simple container and successful " "confirmed-commit operation.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Populate the database with 3 key/value pairs\n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Check all values are not in the running\n" "\t 5 - Confirmed-Commit the operation\n" "\t 6 - Check all values are in the running\n" "\t 7 - Check all values are in the candidate\n" "\t 8 - Commit the operation before the timeout\n" "\t 9 - Check all values are in the running\n" "\t10 - Check all values are in the candidate\n" "\t11 - Delete all entries from the candidate\n" "\t12 - Check all values are not in the candidate\n" "\t13 - Check all values are in the running\n" "\t14 - Commit the operation\n" "\t15 - Check all values are not in the candidate\n" "\t16 - Check all values are not in the running\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); // set some values populateDatabase( 3 ); // check the entries exist checkEntries( primarySession_ ); // confirmed-commit confirmedCommitChanges (primarySession_, 1); // check the entries exist checkEntries( primarySession_ ); // commit the changes commitChanges( primarySession_ ); // sleep for 2 seconds sleep( 2 ); // check the entries still exist checkEntries( primarySession_ ); // remove all entries deleteMainContainer( primarySession_ ); checkEntries( primarySession_ ); commitChanges( primarySession_ ); checkEntries( primarySession_ ); } BOOST_AUTO_TEST_CASE( slt_confirmed_commit_timeout ) { DisplayTestDescrption( "Demonstrate rollback of simple container following " "confirmed-commit operation timeout.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Populate the database with 3 key/value pairs\n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Check all values are not in the running\n" "\t 5 - Confirmed-Commit the operation\n" "\t 6 - Check all values are in the running\n" "\t 7 - Check all values are in the candidate\n" "\t 8 - Allow the timeout to occur\n" "\t 9 - Check all values are not in the candidate\n" "\t10 - Check all values are not in the running\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); // set some values populateDatabase( 3 ); // check the entries exist checkEntries( primarySession_ ); // confirmed-commit confirmedCommitChanges (primarySession_, 1); // check the entries exist checkEntries( primarySession_ ); // sleep for 2 seconds to cause rollback sleep( 2 ); rollbackChanges( primarySession_); // check the entries no longer exist checkEntries( primarySession_ ); } BOOST_AUTO_TEST_CASE( slt_confirmed_commit_extend_timeout ) { DisplayTestDescrption( "Demonstrate rollback of simple container following " "extended confirmed-commit operation timeout.", "Procedure: \n" "\t 1 - Create the top level container for the module\n" "\t 2 - Populate the database with 3 key/value pairs\n" "\t 3 - Check all values are in the candidate\n" "\t 4 - Check all values are not in the running\n" "\t 5 - Confirmed-Commit the operation\n" "\t 6 - Check all values are in the running\n" "\t 7 - Check all values are in the candidate\n" "\t 8 - Confirmed-Commit to extend the timeout\n" "\t 8 - Allow initial timeout period to pass\n" "\t 6 - Check all values are in the running\n" "\t 7 - Check all values are in the candidate\n" "\t 8 - Allow the timeout to occur\n" "\t 9 - Check all values are not in the candidate\n" "\t10 - Check all values are not in the running\n" ); // RAII Vector of database locks vector< unique_ptr< NCDbScopedLock > > locks = getFullLock( primarySession_ ); createMainContainer( primarySession_ ); // set some values populateDatabase( 3 ); // check the entries exist checkEntries( primarySession_ ); // confirmed-commit confirmedCommitChanges (primarySession_, 2); // check the entries exist checkEntries( primarySession_ ); sleep( 1 ); // confirmed-commit to extend timeout confirmedCommitChanges (primarySession_, 4, true); // go beyond initial timeout sleep( 3 ); // check the entries still exist checkEntries( primarySession_ ); // sleep for 2 seconds to cause rollback sleep( 2 ); rollbackChanges( primarySession_); // check the entries no longer exist checkEntries( primarySession_ ); // FIXME: if all entries are not removed the next tests will fail! // FIXME: this indicates that the rollback has not worked // FIXME: correctly // deleteMainContainer( primarySession_ ); // checkEntries( primarySession_ ); // commitChanges( primarySession_ ); // checkEntries( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/system/shutdown-tests.cpp0000664000175000017500000000246714770023131025103 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/query-suite-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( shutdown_tests, QuerySuiteFixture ) BOOST_AUTO_TEST_CASE( shutdown ) { DisplayTestDescrption( "Demonstrate shutdown operation (WARNING: netconf server must be " "manualy restarted following this test).", "Procedure: \n" "\t 1 - Shutdown\n" "\t 2 - Try another query\n" "\t 3 - Check that no response is received\n" ); runShutdown( primarySession_ ); sleep(1); runNoSession( primarySession_ ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/test-suites/system/my-session-tests.cpp0000664000175000017500000001141414770023131025326 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #include // ---------------------------------------------------------------------------| // Yuma Test Harness includes // ---------------------------------------------------------------------------| #include "test/support/fixtures/simple-container-module-fixture.h" #include "test/support/misc-util/log-utils.h" #include "test/support/nc-query-util/nc-query-test-engine.h" #include "test/support/nc-session/abstract-nc-session-factory.h" #include "test/support/checkers/string-presence-checkers.h" // ---------------------------------------------------------------------------| namespace YumaTest { BOOST_FIXTURE_TEST_SUITE( my_session_tests, QuerySuiteFixture ) BOOST_AUTO_TEST_CASE( set_and_get_my_session ) { DisplayTestDescrption( "Demonstrate set-my-session and get-my-session operations.", "Procedure: \n" "\t 1 - Set the session indent, linesize and with-defaults setting\n" "\t 2 - Check that the session has been updated correctly\n" "\t 3 - Set the session indent, linesize and with-defaults setting to new values\n" "\t 4 - Check that the session has been updated correctly\n" ); runSetMySession( primarySession_, "1", "80", "report-all" ); runGetMySession( primarySession_, "1", "80", "report-all" ); runSetMySession( primarySession_, "5", "54", "trim" ); runGetMySession( primarySession_, "5", "54", "trim" ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( kill_session ) { DisplayTestDescrption( "Demonstrate that following a kill-session a locked database " "can be accessed.", "Procedure: \n" "\t1 - Session 1 Locks the running database\n" "\t2 - Session 2 attempts to lock the running database " "( should fail )\n" "\t3 - Session 2 kills session 1" "\t4 - Session 2 Locks the running database" "\t5 - Session 2 Unlocks the running database" ); vector successPresentText{ "ok" }; vector successAbsentText{ "lock-denied", "error" }; StringsPresentNotPresentChecker successChecker( successPresentText, successAbsentText ); vector failPresentText{ "error", "no-access", "lock denied" }; vector failAbsentText{ "ok" }; StringsPresentNotPresentChecker failChecker( failPresentText, failAbsentText ); std::shared_ptr secondarySession( sessionFactory_->createSession() ); queryEngine_->tryLockDatabase( primarySession_, "running", successChecker ); queryEngine_->tryLockDatabase( secondarySession, "running", failChecker ); queryEngine_->tryKillSession( secondarySession, primarySession_->getId(), successChecker); queryEngine_->tryLockDatabase( secondarySession, "running", successChecker ); queryEngine_->tryUnlockDatabase( secondarySession, "running", successChecker ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE( close_session ) { DisplayTestDescrption( "Demonstrate that following a close-session a locked database " "can be accessed.", "Procedure: \n" "\t1 - Session 1 Locks the running database\n" "\t2 - Session 2 attempts to lock the running database " "( should fail )\n" "\t3 - Session 1 performs a close-session" "\t4 - Session 2 Locks the running database" "\t5 - Session 2 Unlocks the running database" ); vector successPresentText{ "ok" }; vector successAbsentText{ "lock-denied", "error" }; StringsPresentNotPresentChecker successChecker( successPresentText, successAbsentText ); vector failPresentText{ "error", "no-access", "lock denied" }; vector failAbsentText{ "ok" }; StringsPresentNotPresentChecker failChecker( failPresentText, failAbsentText ); std::shared_ptr secondarySession( sessionFactory_->createSession() ); queryEngine_->tryLockDatabase( primarySession_, "running", successChecker ); queryEngine_->tryLockDatabase( secondarySession, "running", failChecker ); queryEngine_->tryCloseSession( primarySession_, successChecker); queryEngine_->tryLockDatabase( secondarySession, "running", successChecker ); queryEngine_->tryUnlockDatabase( secondarySession, "running", successChecker ); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_SUITE_END() } // namespace YumaTest yuma123_2.14/netconf/test/ydump-test/0000775000175000017500000000000014770023131017651 5ustar vladimirvladimiryuma123_2.14/netconf/test/ydump-test/ydump-test.py0000775000175000017500000001423014770023131022341 0ustar vladimirvladimir#!/usr/bin/env python import sys import os import subprocess import re # ----------------------------------------------------------------------------| YumaRootDir = "../../../netconf" YumaModuleDir = YumaRootDir + "/modules" YumaModPath = "YUMA_MODPATH=" + YumaModuleDir YangdumpExe = YumaRootDir + "/target/bin/yangdump" LdLibraryPath = "LD_LIBRARY_PATH=" + YumaRootDir + "/target/lib" YangEnvSettings = LdLibraryPath + " " + YumaModPath + " " TestOutputDir = "./yangdump-op" # ----------------------------------------------------------------------------| def InitialiseOutputDir(): """Create / Clean the test output directory""" subprocess.getoutput( "mkdir -p " + TestOutputDir ) subprocess.getoutput( "rm -rf " + TestOutputDir+"/*" ) # ----------------------------------------------------------------------------| def RunYangDump( fmt ): """Run Yangdump over the yang files generating output in the requested format""" command = ( YangEnvSettings + YangdumpExe + " " + "subtree=" + YumaModuleDir+ "/ietf " + "subtree=" + YumaModuleDir+ "/netconfcentral " + "subtree=" + YumaModuleDir+ "/yang " + "subtree=" + YumaModuleDir+ "/test/pass " + "output=" + TestOutputDir + " " + "format="+fmt + " " + "defnames=true " + "log=" + TestOutputDir+"/test-"+fmt+".log " + "log-level=debug" ) print("Running command: %s" % command) subprocess.getoutput( command ) # ----------------------------------------------------------------------------| def CountOccurrences ( searchText, data, ignoreCase = True ): """Crude count of the number of occurrences of searchText in data""" if ignoreCase: res = [ m.start() for m in re.finditer( searchText, data, re.IGNORECASE) ] else: res = [ m.start() for m in re.finditer( searchText, data ) ] return len( res ) # ----------------------------------------------------------------------------| def SummariseErrorsAndWarnings( data ): """Search for the line '*** # Errors, # Warnings' in the output file and extract the count of errors and warnings reported""" regex = re.compile( "\*\*\* (\\d+) Errors, (\\d+) Warnings" ) errors = 0 warnings = 0 for m in re.findall( regex, data ): errors += int( m[0] ) warnings += int( m[1] ) return (errors, warnings) # ----------------------------------------------------------------------------| def AnalyseOutput( fmt ): """Analyse the output log file for the specified yangdump format""" filename = TestOutputDir + "/test-" + fmt + ".log" print("Analysing Results From %s" % filename) f = open( filename, "r" ) data = f.read(); # get the number of errors and warnings errors, warnings = SummariseErrorsAndWarnings( data ) # get the number of Segmentation Violations # Note this is a based on the original makefile script, which # simply greps for 'Segmentation' - it is assumed that any # occurrences indicates a Segmentation Fault segmentCount = CountOccurrences( "Segmentation", data ) # get the number of occurrences of 'internal' internalCount = CountOccurrences( "internal", data ) return ( errors, warnings, segmentCount, internalCount ) # ----------------------------------------------------------------------------| def TestFormat( fmt ): """Test the specified format and collate the results""" RunYangDump( fmt ) return AnalyseOutput( fmt ) # ----------------------------------------------------------------------------| def TestYang(): results = {} for fmt in [ "yin", "yang", "xsd", "sqldb", "html", "h", "c" ]: result = TestFormat( fmt ) results[ fmt ] = result return results # ----------------------------------------------------------------------------| def DisplayResults( results ): colWid = 15 tabWid = 80 print("\n") print("="*tabWid) print(" The Results of running yangdump are summarised in the table below.") print("-"*tabWid) print(( " %s %s %s %s %s" % ( "Format".ljust(colWid), "Errors".center(colWid), "Warnings".center(colWid), "Seg-Faults".center(colWid), "Internal".center(colWid) ) )) totalErrors = 0 totalInternal = 0 totalSegFaults = 0 warningHighWaterMark = 89 warningHighWaterMarkExceeded = False print("-"*tabWid) for k in sorted( results.keys() ): res = results[k] print(( " %s %s %s %s %s" % ( repr(k).ljust(colWid), repr(res[0]).center(colWid), repr(res[1]).center(colWid), repr(res[2]).center(colWid), repr(res[3]).center(colWid) ) )) totalErrors += res[0] totalSegFaults += res[2] totalInternal += res[3] if res[1] > warningHighWaterMark: warningHighWaterMarkExceeded = True print("-"*tabWid) print(" Note: Many yang files currently emit warnings.") print("-"*tabWid) errorOp = False if totalErrors>0: print("\033[31;1m Test Failed: There were %d errors \033[39;0m" % totalErrors) errorOp = True if totalInternal>0: print("\033[31;1m Test Failed: There were %d Segment Faults \033[39;0m" % totalErrors) errorOp = True if totalInternal>0: print("\033[31;1m Test Failed: There were %d internal errors \033[39;0m" % totalErrors) errorOp = True if warningHighWaterMarkExceeded: print("\033[31;1m Test Failed: Warning High Water Mark of %d Exceeded, \033[39;0m" % warningHighWaterMark) print("\033[31;1m New Warnings were introduced! \033[39;0m") errorOp = True if errorOp == False: print("\033[39;1m Test Passed! \033[39;0m") print("-"*tabWid) print("\n") # ----------------------------------------------------------------------------| if __name__ == '__main__': print("Testing Yangdump for various formats") InitialiseOutputDir() results = TestYang() DisplayResults( results ) yuma123_2.14/netconf/test/subsys-test/0000775000175000017500000000000014770023131020043 5ustar vladimirvladimiryuma123_2.14/netconf/test/subsys-test/ncx-init/0000775000175000017500000000000014770023131021574 5ustar vladimirvladimiryuma123_2.14/netconf/test/subsys-test/ncx-init/ncx-init-test.cpp0000664000175000017500000001024114770023131025004 0ustar vladimirvladimir// ---------------------------------------------------------------------------| // Boost Test Framework // ---------------------------------------------------------------------------| #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE MyTest #include #include // ---------------------------------------------------------------------------| // Yuma includes for files under test // ---------------------------------------------------------------------------| #include "ncx.h" // ---------------------------------------------------------------------------| // Test cases for ncx_init() // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE(ncx_init_test_1) { char* argv[] = { const_cast( "ncx_init_test" ), const_cast( "--modpath=../../../modules/netconfcentral:" "../../../modules/ietf:../../../modules/yang" ) }; BOOST_TEST_MESSAGE( "ncx_init() - Test with minimal Command line " "parameters!"); BOOST_CHECK_EQUAL( NO_ERR, ncx_init( FALSE, LOG_DEBUG_WARN, FALSE, 0, 2, argv ) ); ncx_cleanup(); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE(ncx_init_test_2) { char* argv[] = { const_cast( "ncx_init_test" ) }; BOOST_TEST_MESSAGE( "ncx_init() - Test failure to find modules" ); BOOST_CHECK_EQUAL( ERR_NCX_MOD_NOT_FOUND, ncx_init( FALSE, LOG_DEBUG_WARN, FALSE, 0, 1, argv ) ); ncx_cleanup(); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE(ncx_init_test_3) { char* argv[] = { const_cast( "ncx_init_test" ), const_cast( "--modpath=../../../modules/netconfcentral:" "../../../modules/ietf:../../../modules/yang" ) , const_cast( "--unknown-param=somethings_wrong" ) }; BOOST_TEST_MESSAGE( "ncx_init() - Check unexpected command line parameters " "are ignored" ); BOOST_CHECK_EQUAL( NO_ERR, ncx_init( FALSE, LOG_DEBUG_WARN, FALSE, 0, 3, argv ) ); ncx_cleanup(); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE(ncx_init_test_4) { char* argv[] = { const_cast( "ncx_init_test" ), const_cast( "--yuma-home=~/MyYumaHomeDir" ), const_cast( "--modpath=../../../modules/netconfcentral" ), }; BOOST_TEST_MESSAGE( "ncx_init() - Check setting of $YUMA_HOME via command line" ); BOOST_CHECK_EQUAL( NO_ERR, ncx_init( FALSE, LOG_DEBUG_WARN, FALSE, 0, 3, argv ) ); ncx_cleanup(); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE(ncx_init_test_5) { char* argv[] = { const_cast( "ncx_init_test" ), const_cast( "--modpath=../../../modules/netconfcentral" ), const_cast( "--modpath=../../../modules/ietf" ), }; BOOST_TEST_MESSAGE( "ncx_init() - Check multiple --modpath entries are disallowed" ); BOOST_CHECK_EQUAL( ERR_NCX_DUP_ENTRY, ncx_init( FALSE, LOG_DEBUG_WARN, FALSE, 0, 3, argv ) ); ncx_cleanup(); } // ---------------------------------------------------------------------------| BOOST_AUTO_TEST_CASE(ncx_init_test_6) { char* argv[] = { const_cast( "ncx_init_test" ), const_cast( "--modpath=../../../modules/netconfcentral" ), const_cast( "--yuma-home=~/MyYumaHomeDir" ), const_cast( "--yuma-home=~/AnotherMyYumaHomeDir" ), }; BOOST_TEST_MESSAGE( "ncx_init() - Check multiple --yuma-home entries are disallowed" ); BOOST_CHECK_EQUAL( ERR_NCX_DUP_ENTRY, ncx_init( FALSE, LOG_DEBUG_WARN, FALSE, 0, 4, argv ) ); ncx_cleanup(); } yuma123_2.14/netconf/modules/0000775000175000017500000000000014770023131016227 5ustar vladimirvladimiryuma123_2.14/netconf/modules/netconfcentral/0000775000175000017500000000000014770023131021234 5ustar vladimirvladimiryuma123_2.14/netconf/modules/netconfcentral/yuma-mysession.yang0000664000175000017500000000315214770023131025117 0ustar vladimirvladimirmodule yuma-mysession { namespace "http://netconfcentral.org/ns/yuma-mysession"; prefix "myses"; import yuma-app-common { prefix appcmn; } import ietf-netconf-with-defaults { prefix wd; } organization "Netconf Central"; contact "Andy Bierman "; description "This module contains RPC methods for customizing the default session settings for the current session."; revision 2010-05-10 { description "Added default to line-size parameter."; } revision 2009-08-11 { description "Initial published version"; } grouping session-params { uses appcmn:IndentParm; leaf linesize { description "The desired maximum number of characters printed per line. The server may exceed this number. It is only a suggestion, not a hard limit."; type uint32 { range "40 .. 1024"; } default 72; } leaf with-defaults { description "The desired maximum number of characters printed per line. The server may exceed this number. It is only a suggestion, not a hard limit."; type wd:with-defaults-mode; } } rpc get-my-session { description "Get the customization settings for this session"; output { uses session-params; } } rpc set-my-session { description "Set the customization settings for this session. This is like a merge operation. Only the values that are present will be used to overwrite the existing settings."; input { uses session-params; } } } yuma123_2.14/netconf/modules/netconfcentral/yuma-app-common.yang0000664000175000017500000003332314770023131025137 0ustar vladimirvladimirmodule yuma-app-common { yang-version 1; namespace "http://netconfcentral.org/ns/yuma-app-common"; prefix "appcmn"; import yuma-types { prefix "yt"; } organization "Netconf Central"; contact "Andy Bierman "; description "Common CLI parameters used in all yuma applications."; revision 2012-08-16 { description "Split yuma-home into its own grouping YumaHomeParm"; } revision 2011-10-06 { description "Add HomeParm grouping. Split CommonFeatureParms from NcxAppCommon grouping."; } revision 2011-04-24 { description "Remove empty featureParms grouping. Add ProtocolsParm grouping."; } revision 2011-01-28 { description "Add OutputParm grouping"; } revision 2010-12-18 { description "Change warn-linelen default value to 0 to turn it off"; } revision 2010-01-25 { description "Allow revision date in module parm for 0.10 release."; } revision 2010-01-14 { description "Initial version for 0.9.9 release."; } typedef CliWithDefaultsType { description "Add 'none' to standard enumerations"; type enumeration { enum none; enum report-all; enum report-all-tagged; enum trim; enum explicit; } } grouping CliWithDefaultsParm { leaf with-defaults { type CliWithDefaultsType; } } grouping HelpMode { choice help-mode { default normal; leaf brief { description "Show brief help text"; type empty; } leaf normal { description "Show normal help text"; type empty; } leaf full { description "Show full help text"; type empty; } } } grouping IndentParm { leaf indent { description "Number of spaces to indent (0..9) in formatted output."; type yt:IndentType; } } grouping DatapathParm { leaf datapath { description "Internal file search path for config files. Overrides the YUMA_DATAPATH environment variable."; type yt:NcPathList; } } grouping RunpathParm { leaf runpath { description "Internal file search path for executable modules. Overrides the YUMA_RUNPATH environment variable."; type yt:NcPathList; } } grouping NcxAppCommon { leaf help { description "Print program help file and exit."; type empty; } uses HelpMode; uses IndentParm; leaf log { description "Filespec for the log file to use instead of STDOUT."; type string; } leaf log-append { description "If present, the log will be appended not over-written. If not, the log will be over-written. Only meaningful if the 'log' parameter is also present."; type empty; } leaf log-level { description "Sets the debug logging level for the program."; type yt:NcDebugType; } leaf modpath { description "Directory search path for YANG or YIN modules. Overrides the YUMA_MODPATH environment variable."; type yt:NcPathList; } leaf version { description "Print program version string and exit."; type empty; } leaf warn-idlen { description "Control whether identifier length warnings will be generated. The value zero disables all identifier length checking. If non-zero, then a warning will be generated if an identifier is defined which has a length is greater than this amount."; type uint32 { range "0 | 8 .. 1023"; } default 64; } leaf warn-linelen { description "Control whether line length warnings will be generated. The value zero disables all line length checking. If non-zero, then a warning will be generated if the line length is greater than this amount. Tab characters are counted as 8 spaces."; type uint32 { range "0 | 40 .. 4095"; } // default 72; default 0; } leaf-list warn-off { description "Control whether the specified warning number will be generated and counted in the warning total for the module being parsed."; type uint32 { range "400 .. 899"; } } } grouping YumaHomeParm { leaf yuma-home { description "Directory for the yuma project root to use. If present, this directory location will override the 'YUMA_HOME' environment variable, if it is present. If a zero-length string is entered, then the YUMA_HOME environment variable will be ignored."; type string; } } grouping ConfigParm { leaf config { description "The name of the configuration file to use. Any parameter except this one can be set in the config file. The default config file will be not be checked if this parameter is present."; type string; } } grouping NewConfigParm { choice config-choice { leaf config { description "The name of the configuration file to use. Any parameter except this one can be set in the config file. The default config file will be not be checked if this parameter is present."; type string; } leaf no-config { description "Do not the default .conf file even if it exists."; type empty; } } } grouping CommonFeatureParms { leaf feature-enable-default { description "If true, then features will be enabled by default. If false, then features will be disabled by default."; type boolean; default true; } leaf-list feature-enable { description "Identifies a feature which should be considered enabled."; type yt:FeatureSpec; } leaf-list feature-disable { description "Identifies a feature which should be considered disabled."; type yt:FeatureSpec; } } grouping FeatureCodeParms { leaf feature-code-default { description "The default feature code generation type."; type enumeration { enum static { description "The default behavior for feature behavior is to use statically defined features at compile-time."; } enum dynamic { description "The default behavior for feature behavior is to use dynamically defined features at load-time."; } } default dynamic; } leaf-list feature-static { description "Identifies a feature which is configured to be a static feature, and therefore set at compile time."; type yt:FeatureSpec; } leaf-list feature-dynamic { description "Identifies a feature which is configured to be a static feature, and therefore set at compile time."; type yt:FeatureSpec; } } grouping ModuleParm { leaf-list module { description "YANG source module name to use."; type yt:NcModuleSpec; } } grouping SubtreeParm { leaf-list subtree { description "Path specification of the directory subtree to use. All of the YANG source modules contained in the specified directory sub-tree will be processed. Note that symbolic links are not followed during the directory traversal. Only real directories will be searched and regular files will be checked as modules. Processing will continue to the next file if a module contains errors. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path"; type yt:NcPathSpec; } } grouping DeviationParm { leaf-list deviation { description "YANG deviation file. This parameter identifies a YANG module that should only be checked for deviation statements for external modules. These will be collected and applied to the real module(s) being processed. Deviations are applied as patches to the target module. Since they are not identified in the target module at all (ala imports), they have to be specified explicitly, so they will be correctly processed. If this string represents a filespec, ending with the '.yang' or '.yin' extension, then only that file location will be checked. If this string represents a module name, then the module search path will be checked for a file with the module name and the '.yang' or '.yin' extension. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path "; type yt:NcModuleSpec; } } grouping SubdirsParm { leaf subdirs { description "If false, the file search paths for modules, scripts, and data files will not include sub-directories if they exist in the specified path. If true, then these file search paths will include sub-directories, if present. Any directory name beginning with a dot '.' character, or named 'CVS', will be ignored."; type boolean; default true; } } grouping OutputParm { leaf output { description "Output directory or file name to use. Default is STDOUT if none specified and the 'defnames' parameter is also set to 'false'. For yangdump, if this parameter represents an existing directory, then the 'defnames' parameter will be set to 'true' by default, and the translation output file(s) will be generated in the specified directory. For yangdump, if this parameter represents a file name, then the 'defnames' parameter will be ignored, and all translation output will be directed to the specified file. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path If this parameter is present, and identifies an existing directory, then any translation output files will be generated in that directory. If that parameter identifies a file, then that one file will be used for output. For yangdump, if the 'format' parameter is present, then one file with the default name will be generated for each YANG or YIN file found in the sub-tree."; type string; } } grouping ProtocolsParm { leaf protocols { description "Specifies which protocol versions the program or session will attempt to use. Empty set is not allowed."; must ". != ''"; type bits { bit netconf1.0 { description "RFC 4741 base:1.0"; position 0; } bit netconf1.1 { description "RFC xxxx base:1.1"; position 1; } } /* default "netconf1.0 netconf1.1"; */ } } grouping HomeParm { leaf home { description "Directory specification for the home directory to use instead of HOME."; type string { length "1..max"; } } } } yuma123_2.14/netconf/modules/netconfcentral/yangdiff.yang0000664000175000017500000002457014770023131023713 0ustar vladimirvladimirmodule yangdiff { yang-version 1; namespace "http://netconfcentral.org/ns/yangdiff"; prefix ydi; import yuma-types { prefix nt; } import yuma-ncx { prefix ncx; } import yuma-app-common { prefix ncxapp; } organization "Netconf Central"; contact "Andy Bierman "; description "yangdiff compares the semantics and syntax between two revisions of the same YANG module. The conceptual data model is compared, not the individual files. For example, unless statement order is significant, changing the order is not considered a change, and is not reported. Reformatted test (whitespace changes) are also not reported. If a data type definition is changed in form, but not content, then a 'modify type' message will be generated, but no additional sub-fields will be reported. INPUT FILES To compare one module, use the 'old' and 'new' parameters to specify YANG module files, each with a filespec string ending with the '.yang' file extension. The filespecs must represent different files. If the 'old' parameter represents a directory, then this directory will be searched for the 'new' filename. To compare all the modules in a subtree, use the 'old' and 'new' parameters to specify a directory to be searched for YANG modules to be processed. In this mode, each new module is compared to a corresponding file within the 'old' subtree. Also, dependency and include files will be kept separate, for each subtree. Unless the 'help' or 'version' parameters is entered, the 'old' and 'new' parameters must be present. SEARCH PATH When a module name is entered as input, or when a module or submodule name is specified in an import or include statement within the file, the following search algorithm is used to find the file: 1) if the parameter for the file that generated the search request represents a subtree, search that subtree first. 2) file is in the current directory 3) YANG_MODPATH environment var (or set by modpath parameter) 4) $HOME/modules directory 5) $YANG_HOME/modules directory 6) $YANG_INSTALL/modules directory OR default install module location, '/usr/share/yang/modules' By default, the entire directory tree for all locations (except step 1) will be searched, not just the specified directory. The 'subdirs' parameter can be used to prevent sub-directories from being searched. Any directory name beginning with a dot character '.' will be skipped. Also, any directory named 'CVS' will be skipped in directory searches. OUTPUT MODES By default, any comparison output will be sent to STDOUT. The 'output' parameter can be used to specify the full filespec of the output file, or a complete directory specification to be combined with the default filename (yangdiff.log). ERROR LOGGING By default, warnings and errors are sent to STDOUT. A log file can be specified instead with the 'log' parameter. Existing log files can be reused with the 'logappend' parameter, otherwise log files are overwritten. The logging level can be controlled with the 'log-level' parameter. The default log level is 'info'. The log-levels are additive: off: suppress all errors (not recommended!) A program return code of '1' indicates some error. error: print errors warn: print warnings info: print generally interesting trace info debug: print general debugging trace info debug2: print verbose debugging trace info "; revision 2012-10-05 { description "Add uses for YumaHomeParm"; } revision 2011-10-06 { description "Add --home parameter,"; } revision 2009-04-10 { description "Moved common parameters to ncx-app-common"; } revision 2008-08-17 { description "Initial version"; } typedef DiffType { description "Type of comparison output requested."; type enumeration { enum terse; enum normal; enum revision; } default "normal"; } container yangdiff { ncx:cli; description "CLI Parameter Set for the YANG Module Compare Application."; uses ncxapp:NcxAppCommon; uses ncxapp:ConfigParm; uses ncxapp:YumaHomeParm; leaf old { description "The older of the two revisions to compare. If this parameter indicates a filename, then it represents the YANG source module name to compare as the older of the two revisions. If this parameter indicates a directory, then it will be used to to search for a file with the same name as identified by the 'new' parameter. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path This parameter must be present unless the 'help' or 'version' parameters are used."; type nt:NcModuleSpec; } leaf new { description "If this parameter indicates a filename, then it represents the YANG source module name to compare as the newer of the two revisions. If this parameter indicates a directory (and the 'old' parameter indicates a filename), then it will be used to to search for a file with the same name as the 'new' parameter. If the 'old' parameter identifies a directory as well (and the 'subdirs' parameter is 'false'), then the modules within the 'new' directory will be compared to files with the same name in the 'old' directory. If the 'subdirs' parameter is 'true', then all sub-directories within the 'src' directory will also be checked. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path This parameter must be present unless the 'help' or 'version' parameters are used."; type nt:NcModuleSpec; } leaf difftype { description "The type of comparison output requested. Allowed values are 'terse', 'normal', and 'revision'. The basic format is: [add/delete/modify] field-name [field-value] The 'terse' option will include the names of the top-level fields that are different. A foo --> Added foo in new revision D foo --> Deleted foo in new revision M foo --> Modified foo in new revision (value too long) M foo from '0' to '1' --> Modified foo in new revision The 'normal' option will also include any changes for any nested fields or objects. This is the default option. The 'revision' option will generate the differences report in YANG revision-stmt format. For example: revision { description \" - Added import baxtypes - Changed contact to 'support@acme.com' - Modified container myobjects - Added list first-list\"; } "; type DiffType; } leaf output { description "Output directory or file name to use. The default is STDOUT if none is specified. If this parameter represents an existing directory, then the default comparison output file (yangdiff.log) will be generated in the specified directory. If this parameter represents a file name, then all comparison output will be directed to the specified file. If the file already exists, it will be overwritten. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path"; type string; } leaf header { description "If false, the header clauses will be skipped, and any differences between the module headers will not be reported. Only object definitions will be compared. If true, then header clauses will be compared, along will all the object definitions."; type boolean; default true; } uses ncxapp:HomeParm; uses ncxapp:SubdirsParm; } } yuma123_2.14/netconf/modules/netconfcentral/yuma-ncx.yang0000664000175000017500000004477114770023131023672 0ustar vladimirvladimirmodule yuma-ncx { namespace "http://netconfcentral.org/ns/yuma-ncx"; prefix "ncx"; organization "Netconf Central"; contact "Andy Bierman "; description "This module contains definitions for Yuma Netconf extensions. Some extensions are used to control yangdump translation format. Some are used for CLI parsing in yangcli. These elements may be present in appinfo elements, used in YANG to XSD translation."; revision 2012-01-13 { description "Added user-write extension."; } revision 2011-09-30 { description "Added default-parm-equals and sil-delete-children-first extensions."; } revision 2009-12-21 { description "Moved secure and very-secure extensions to yuma-nacm."; } revision 2009-06-12 { description "Add sequence-id element for notifications"; } revision 2009-04-10 { description "Removed rpc-type extension; use 'nacm:secure' instead"; } revision 2008-08-23 { description "Added abstract extension. Marked all objects as abstract so they will not be included in the NETCONF database."; } revision 2008-08-01 { description "Added password, hidden, xsdlist, and root extensions."; } revision 2008-04-16 { description "Added metadata and rpc-type extensions."; } revision 2008-03-21 { description "Added no-duplicates extension."; } revision 2008-02-21 { description "Initial revision."; } leaf belongs-to { description "Contains the module name value for a belongs-to-stmt. Will be present in appinfo for a YANG submodule."; type string; ncx:abstract; } leaf case-name { description "Contains the identifier-str value for a YANG case name. Will be present in appinfo for all choice case definitions."; type string; ncx:abstract; } leaf choice-name { description "Contains the identifier-str value for a YANG choice name. Will be present in appinfo for all choice object definitions."; type string; ncx:abstract; } leaf config { description "Contains the value from the config-stmt. Will be present in appinfo for top-level objects or if config-stmt is present in the object definition."; type boolean; ncx:abstract; } leaf contact { description "Contains the value from the contact-stmt. Will be present in appinfo for the module. if defined in the module."; type string; ncx:abstract; } leaf default { description "Contains the value from the default-stmt. Will be present in appinfo for a typedef that includes a default-stmt."; type string; ncx:abstract; } leaf mandatory { description "Contains the value from the mandatory-stmt. Will be present in appinfo if mandatory-stmt is present in the object definition."; type boolean; ncx:abstract; } leaf min-elements { description "Contains the value from the min-elements-stmt. Will be present in appinfo for a list or leaf-list object, if the min-elements-stmt is present in the object definition."; type uint32; ncx:abstract; } leaf max-elements { description "Contains the value from the max-elements-stmt. Will be present in appinfo for a list or leaf-list object, if the max-elements-stmt is present in the object definition."; type union { type uint32; type enumeration { enum unbounded; } } ncx:abstract; } container must { presence "Indicates a must statement is configured."; ncx:abstract; description "Contains the fields from one must-stmt. Will be present in appinfo for each must-stmt defined within an object."; leaf xpath { description "The xpath statement specifying the system conditions that must be true for a valid configuration."; type string; mandatory true; } leaf description { description "Contains the description-stmt value from the must-stmt, if present."; type string; } uses reference; leaf error-app-tag { description "Contains the error-app-tag-stmt value from the must-stmt, if present."; type string; } leaf error-message { description "Contains the error-message-stmt value from the must-stmt, if present."; type string; } } leaf ordered-by { description "Contains the value from the ordered-by. Will be present in appinfo for all list and leaf-list object definitions."; type enumeration { enum system; enum user; } ncx:abstract; } leaf organization { description "Contains the value from the organization-stmt. Will be present in appinfo if oraganization-stmt is present in the module."; type string; ncx:abstract; } leaf path { description "Contains the path-stmt Xpath expression value for a leafref. Will be present in appinfo for all leafref typedefs, and inline type-stmts for leafref leafs and leaf-lists."; type string; ncx:abstract; } leaf position { description "Contains the position-stmt value for a bit definition. Will be present in appinfo for all 'bits' typedefs, and inline type-stmts for leafs and leaf-lists."; type uint32; ncx:abstract; } grouping reference { container reference { ncx:abstract; presence "Indicates a reference statement is configured."; description "Contains information from the reference-stmt. Will be present in appinfo for objects which contain a reference-stmt."; leaf text { description "Value from the reference-stmt."; type string; mandatory true; } leaf url { description "URL for the specified reference. Will be present if the 'text' field begins with 'RFC xxxx' or 'draft-'"; type string; } } } uses reference; container revision { ncx:abstract; presence "Indicates a revision statement is configured."; description "Contains the fields from one revision-stmt. Will be present in appinfo for each revision-stmt defined within the module."; leaf version { description "Contains the date string value from the revision-stmt."; type string; mandatory true; ncx:abstract; } leaf description { description "Contains the description-stmt value from the revision-stmt."; type string; mandatory true; ncx:abstract; } } leaf rpc-output { description "Contains the XSD type name value for the RPC output data for one RPC method. Will be present in appinfo for all rpc definitions which contain an output-stmt."; type string; ncx:abstract; } leaf source { description "Contains the complete filespec of the source file used for the XSD translation. Will be present in the appinfo for the module header."; type string; ncx:abstract; } leaf units { description "Contains the units-stmt value for a type or leaf. Will be present in appinfo for typedefs, and inline type-stmts for leafs and leaf-lists, which contain a units-stmt."; type string; ncx:abstract; } leaf value { description "Contains the value-stmt value for an enum definition. Will be present in appinfo for all 'enumeration' typedefs, and inline type-stmts for leafs and leaf-lists."; type int32; ncx:abstract; } // Internal objects for anyxml parsing // every value node must be assigned an object template // even within the anyxml implementation anyxml any { ncx:hidden; ncx:abstract; } container struct { ncx:hidden; ncx:abstract; } leaf string { type string; ncx:hidden; ncx:abstract; } leaf empty { type empty; ncx:hidden; ncx:abstract; } container root { ncx:hidden; ncx:abstract; ncx:root; } leaf binary { type binary; ncx:hidden; ncx:abstract; } // NCX extensions leaf bad-value { description "Contains the value that was associated with the error. Usually added to 'invalid-value' errors, but may be present for other errors as well."; type string; ncx:abstract; } leaf error-number { description "Contains the internal error code associated with the error. Added for all error types."; type uint32 { range "1 .. max"; } ncx:abstract; } // Netconf Central Extensions extension abstract { description "Used with object definitions to indicate that they do not represent CLI or NETCONF configuration database data instances. Instead, the node is simply an object identifier, an 'error-info' extension, or some other abstract data structure."; } extension cli { description "Used within a container definition to indicate it is only used as a conceptual container for a set of CLI parameters. A top-level container containing this extension will not be included in any NETCONF configuration databases."; } extension default-parm { description "Used within a CLI container or rpc definition to specify a leaf parameter within the CLI container or rpc input section, that is used as the default if no parameter name is entered. These values must not begin with a dash (-) or double dash (--) sequence or they will be mistaken for CLI parameter names. This option is somewhat risky because any unrecognized parameter without any prefix (- or --) will be tried as the default parameter type, instead of catching the unknown parameter error. It can also be useful though, for assigning file name parameters through shell expansion, or if there is only one parameter."; argument parm { yin-element true; } } extension default-parm-equals-ok { description "Used within a CLI container or rpc definition to specify a leaf parameter within the CLI container or rpc input section, that is used as the default if no parameter name is entered. This can be used in addition to ncx:default-parm to allow an equals sign '=' in the default parm string value. This option is quite risky because any unrecognized parameter without any prefix (- or --) will be tried as the default parameter type, instead of catching the unknown parameter error. This includes strings containing an equals sign, so an unknown parameter error will never be generated. rpc foo { input { ncx:default-parm a; ncx:default-parm-equals-ok; leaf a { type string; } leaf b { type int32; } } } yangcli> foo bogus-parm=fred This will interpreted as if parameter 'a' were entered: yangcli> foo a='bogus-parm=fred' "; } extension hidden { description "Used to prevent publication of a YANG data object. Will be ignored for typedefs and other constructs. If present, that node and any sub-nodes will be ignored when generating HTML documentation or cYANG output. The yangdump -f=copy mode will not be affected by this extension. "; } extension metadata { description "Used to define an XML attribute to be associated with a data-def-stmt node. Only optional metadata can be defined. Errors for missing XML attributes (except as specified by the YANG language) will not be checked automatically. The syntax string has the following format: [prefix:]typename attribute-name Any YANG typedef of builtin type can be specified as the type name, except 'empty'. Example from get command in netconf.yang: ncx:metadata 'FilterType type'; "; argument syntax-string { yin-element true; } } extension no-duplicates { description "Used to indicate that no duplicate values are allowed in an ncx:xsdlist leaf or leaf-list object."; } extension password { description "Used to indicate the data type for the leaf is really a password. Only the encrypted version of the password is allowed to be generated in any output."; } extension root { description "Used within a container definition to indicate it is really a root container for a conceptual NETCONF database, instead of just an empty container. This is needed for yuma to correctly process any RPC method that contains a 'config' parameter."; } extension sil-delete-children-first { description "Used within a container or list definition to indicate that the SIL callbacks for descendant nodes should be invoked first, when a data node instance of the object containing this extension is deleted. Normally, the parent node is expected to delete all its own sub-structures when the SIL edit callback is invoked. If this extension is present, then any SIL callbacks for any of the child nodes will be invoked first instead. If a child node is a list or a container, and it also contains an 'ncx:sil-delete-children-first' extension, then its children will be checked first. The SIL edit callback will not be invoked for leaf, leaf-list, or anyxml descendant nodes in this mode. They will only will called if their parent node is not getting deleted. container foo { ncx:sil-delete-children-first; list foos { ncx:sil-delete-children-first; key a; leaf a { type string; } container b { list c { ... } } leaf d { type empty; } } } In this example, assume node /foo gets deleted. Then the SIL edit callbacks would be done as follows: 1) /foo/foos[a='n']/b (called for row 'n' of /foo/foos) 2) /foo/foos[a='n'] (called for row 'n' of /foo/foos) 3) repeat (1,2) until all rows in /foo/foos are deleted 4) /foo Note that the SIL edit callback is not done for list /foo/foos[a='n']/b/c because this extension is not present in container '/foo/foos/b'. Note that the SIL edit callback is not done for nodes /foo/foos[a='n']/a or /foo/foos[a='n']/d because they are leafs. "; } extension xsdlist { description "Used to indicate the leaf string type is really an XSD list, which is a series of whitespace separated strings. The type argument represents the data type to use for the list members, for validation purposes. Allowed to be present within the type sub-section for a string."; argument type { yin-element true; } } extension xpath { description "Used to indicate that the content of a data type is an XPath expression. This is needed to properly evaluate the namespace prefixes within the expression. The xpath extension may appear within the type-stmt, within a typedef, leaf, or leaf-list. The builtin data type must be 'string', or the 'xpath' extension will be ignored. All data using the 'instance-identifier' built-in type will automatically be processed as an XPath string, so the xpath extension is not needed in that case."; } extension qname { description "Used to indicate that the content of a data type is a Qualified Name. This is needed to properly evaluate the namespace prefix, if used. The qname extension may appear within the type-stmt, within a typedef, leaf, or leaf-list. The builtin data type must be 'string', or the 'qname' extension will be ignored."; } extension schema-instance { description "Used to indicate that the typedef or type statement for a string data type really identifies a special schema-instance node, not a generic string. A schema-instance value string is an unrestricted YANG instance-identifier expression. All the same rules as an instance-identifier apply except: * predicates for keys are optional; The dataRule will apply to all instances of any missing key leaf predicate. This extension will be ignored unless it is present in the type-stmt of a typedef-stmt, leaf-stmt, or leaf-list-stmt, or directly within a leaf-stmt or leaf-list-stmt."; } extension user-write { description "Used within database configuration data definition statements to control user write access to the database object containing this statement. The 'permitted' argument is a list of operations that users are permitted to invoke for the specified node. These permissions will over-ride all NACM access control rules, even if NACM is disabled. This extension does not apply to descendant nodes! This extension has no effect if config-stmt is false! The following values are supported: * create : allow users to create instances of the object * update : allow users to modify instances of the object * delete : allow users to delete instances of the object To dis-allow all user access, provide an empty string for the 'permitted' parameter (user-write '';) To allow only create and delete user access, provide the string 'create delete' for the 'permitted' parameter. Use this for parameters that cannot be changed once they are set. Providing all 3 parameters has the same affect as not using this extension at all, but can be used anyway. leaf user-write { description 'equivalent YANG definition'; type bits { bit create; bit update; bit delete; } default 'create update delete'; } "; argument exceptions { yin-element true; } } } yuma123_2.14/netconf/modules/netconfcentral/yuma-xsd.yang0000664000175000017500000002521314770023131023666 0ustar vladimirvladimirmodule yuma-xsd { namespace "http://www.w3.org/2001/XMLSchema"; prefix "xsd"; organization "Netconf Central"; contact "Andy Bierman "; description "XSD derived types for usage in YANG."; revision 2009-11-21 { description "Rename xsd to yuma-xsd."; } revision 2007-12-06 { description "Initial revision. There is more work to do filling in patterns for some of the string types"; } // The string data type is a YANG builtin type and not // available in this module. typedef normalizedString { description "XSD normalized string"; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028' + '/datatypes.html#normalizedString'; type string; } typedef token { description "XSD token string"; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#token'; type string; } typedef base64Binary { description "XSD base64 binary encoded string"; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#base64Binary'; type string; } typedef hexBinary { description "XSD hex binary encoded string"; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#hexBinary'; type binary; } typedef integer { description "XSD unbounded integer type. This cannot be given a range like a number. This pattern does not supoort string representations of numbers, such as one two three"; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#integer'; type string { pattern '[\-+]?[0-9]+'; } } typedef positiveInteger { description "XSD unbounded positive integer. This cannot be given a range like a number. This pattern does not supoort string representations of numbers, such as one two three"; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#positiveInteger'; type string { pattern '[\+]?[1-9]+[0-9]*'; } } typedef negativeInteger { description "XSD unbounded negative integer. This cannot be given a range like a number. This pattern does not supoort string representations of numbers, such as one two three"; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#negativeInteger'; type string { pattern '[\-]?[1-9]+[0-9]*'; } } typedef nonNegativeInteger { description "XSD unbounded non-negative integer. This cannot be given a range like a number. This pattern does not supoort string representations of numbers, such as one two three"; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#nonNegativeInteger'; type string { pattern '[\+]?[0-9]*'; } } typedef nonPositiveInteger { description "XSD unbounded non-positive integer. This cannot be given a range like a number. This pattern does not supoort string representations of numbers, such as one two three"; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#nonPositiveInteger'; type string { pattern '\-[1-9]+[0-9]*'; } } typedef long { description "XSD 64 bit signed integer."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#long'; type int64; } typedef unsignedLong { description "XSD 64 bit unsigned integer."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#unsignedLong'; type uint64; } typedef int { description "XSD 32 bit signed integer."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#int'; type int32; } typedef unsignedInt { description "XSD 32 bit unsigned integer."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#unsignedInt'; type uint32; } typedef short { description "XSD 16 bit signed integer."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#short'; type int16; } typedef unsignedShort { description "XSD 16 bit unsigned integer."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#unsignedShort'; type uint16; } typedef byte { description "XSD 8 bit signed integer."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#byte'; type int8; } typedef unsignedByte { description "XSD 8 bit unsigned integer."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#unsignedByte'; type uint8; } typedef decimal { description "XSD decimal data type. [To do: not sure if this is a bounded real number or an unbounded real number.]."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#decimal'; type string; } // The boolean type is a builtin type in YANG so it // not available in this XSD module. typedef duration { description "XSD duration string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#duration'; type string; } typedef dateTime { description "XSD date and time string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#dateTime'; type string { // adapted date-and-time pattern pattern '-?\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + '(Z|(\+|-)\d{2}:\d{2})'; } } typedef date { description "XSD date string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#date'; type string; } typedef time { description "XSD time string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#time'; type string; } typedef gYear { description "XSD year string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#gYear'; type string; } typedef gYearMonth { description "XSD year and month string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#gYearMonth'; type string; } typedef gMonth { description "XSD month string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#gMonth'; type string; } typedef gMonthDay { description "XSD month and day string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#gMonthDay'; type string; } typedef gDay { description "XSD day string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#gDay'; type string; } typedef Name { description "XSD name string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#Name'; type string; } typedef QName { description "XSD namespace-qualified name string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#QName'; type string; } typedef NCName { description "XSD not-namespace-qualified name string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#NCName'; type string { pattern '[\i-[:]][\c-[:]]*'; } } typedef anyURI { description "XSD universal resource identifier string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#anyURI'; type string; } typedef language { description "XSD language identifier string type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#language'; type string; } typedef ID { description "XSD ID attribute type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#ID'; type string; } typedef IDREF { description "XSD IDREF attribute type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#IDREF'; type string; } typedef IDREFS { description "XSD IDREFS attribute type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#IDREFS'; type string; } typedef ENTITY { description "XSD ENTITY attribute type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#ENTITY'; type string; } typedef ENTITIES { description "XSD ENTITIES attribute type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#ENTITIES'; type string; } typedef NOTATION { description "XSD NOTATION attribute type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#NOTATION'; type string; } typedef NMTOKEN { description "XSD NMTOKEN attribute type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#NMTOKEN'; type string; } typedef NMTOKENS { description "XSD NMTOKENS attribute type."; reference 'http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/' + 'datatypes.html#NMTOKENS'; type string; } } yuma123_2.14/netconf/modules/netconfcentral/yuma-proc.yang0000664000175000017500000002357114770023131024040 0ustar vladimirvladimirmodule yuma-proc { namespace "http://netconfcentral.org/ns/yuma-proc"; prefix "proc"; organization "Netconf Central"; contact "Andy Bierman ."; description "NETCONF /proc file system monitoring."; revision 2012-10-10 { description "Add more counters for ubuntu 12.04 support."; } revision 2010-06-01 { description "Add more counters for ubuntu 10.04 support."; } revision 2009-11-21 { description "Renamed proc to yuma-proc."; } revision 2009-07-17 { description "Initial version."; } typedef YesNo { type enumeration { enum no; enum yes; } } container proc { description "/proc file system monitoring data."; config false; container cpuinfo { description "/proc/cpuinfo file system monitoring data."; list cpu { key processor; leaf processor { description "Processor Identifier"; type uint32; } leaf vendor_id { description "Vendor Identifier"; type string; } leaf cpu_family { description "CPU family number"; type uint32; } leaf model { description "Model number"; type uint32; } leaf model_name { description "Model name"; type string; } leaf stepping { description "Stepping number"; type uint32; } leaf microcode { description "Microcode hex string"; type string; } leaf cpu_MHz { description "CPU mega-hurtz number"; type decimal64 { fraction-digits 3; } } leaf cache_size { description "Cache size string"; type string; } leaf physical_id { description "Physical Identifier number"; type uint32; } leaf siblings { description "Siblings number"; type uint32; } leaf core_id { description "Core identifier number"; type uint32; } leaf cpu_cores { description "Number of CPU cores"; type uint32; } leaf apicid { description "APICID number"; type uint32; } leaf initial_apicid { description "Initial APICID number"; type uint32; } leaf fdiv_bug { description "fdiv bug present"; type YesNo; } leaf hlt_bug { description "hlt bug present"; type YesNo; } leaf f00f_bug { description "f00f bug present"; type YesNo; } leaf coma_bug { description "coma bug present"; type YesNo; } leaf fpu { description "FPU present"; type YesNo; } leaf fpu_exception { description "FPU exception supported"; type YesNo; } leaf cpuid_level { description "CPU ID level number"; type uint32; } leaf wp { description "wp enabled"; type YesNo; } leaf flags { description "flags string"; type string; } leaf bogomips { description "bogo MIPs number"; type decimal64 { fraction-digits 2; } } leaf TLB_size { description "TLB size: number of pages"; type string; } leaf clflush_size { description "CL flush size number"; type uint32; } leaf cache_alignment { description "Cache alignment number"; type uint32; } leaf address_sizes { description "Address sizes string"; type string; } leaf power_management { description "power management string"; type string; } } } container meminfo { description "/proc/meminfo system monitoring data."; leaf MemTotal { description "Memory Total string"; type string; } leaf MemFree { description "Memory Free string"; type string; } leaf Buffers { description "Buffers string"; type string; } leaf Cached { description "Cached string"; type string; } leaf SwapCached { description "Swap Cached string"; type string; } leaf Active { description "Active string"; type string; } leaf Active_anon_ { description "Active(anon) string"; type string; } leaf Active_file_ { description "Active(file) string"; type string; } leaf Inactive { description "Inactive string"; type string; } leaf Inactive_anon_ { description "Inactive(anon) string"; type string; } leaf Inactive_file_ { description "Inactive(file) string"; type string; } leaf Unevictable { description "Unevictable string"; type string; } leaf Mlocked { description "Mlocked string"; type string; } leaf HighTotal { description "High Total string"; type string; } leaf HighFree { description "High Free string"; type string; } leaf LowTotal { description "Low Total string"; type string; } leaf LowFree { description "Low Free string"; type string; } leaf SwapTotal { description "Swap Total string"; type string; } leaf SwapFree { description "Swap Free string"; type string; } leaf Dirty { description "Dirty string"; type string; } leaf Writeback { description "Writeback string"; type string; } leaf AnonPages { description "Anonymous Pages string"; type string; } leaf Mapped { description "Mapped string"; type string; } leaf Shmem { description "Shmem string"; type string; } leaf Slab { description "Slab string"; type string; } leaf SReclaimable { description "SReclaimable string"; type string; } leaf SUnreclaim { description "SUnreclaim string"; type string; } leaf KernelStack { description "KernelStack string"; type string; } leaf PageTables { description "PageTables string"; type string; } leaf NFS_Unstable { description "NFS Unstable string"; type string; } leaf Bounce { description "Bounce string"; type string; } leaf WritebackTmp { description "Writeback Temp string"; type string; } leaf CommitLimit { description "Commit Limit string"; type string; } leaf Committed_AS { description "Committed AS string"; type string; } leaf VmallocTotal { description "Vmalloc Total string"; type string; } leaf VmallocUsed { description "Vmalloc Used string"; type string; } leaf VmallocChunk { description "Vmalloc Chunk string"; type string; } leaf HardwareCorrupted { description "HardwareCorrupted string"; type string; } leaf HugePages_Total { description "Huge Pages Total number"; type uint32; } leaf HugePages_Free { description "Huge Pages Free number"; type uint32; } leaf HugePages_Rsvd { description "Huge Pages Reserved number"; type uint32; } leaf HugePages_Surp { description "Huge Pages Surplus number"; type uint32; } leaf Hugepagesize { description "Huge Page size string"; type string; } leaf DirectMap4k { description "Direct Map 4k string"; type string; } leaf DirectMap2M { description "Direct Map 2M string"; type string; } leaf DirectMap4M { description "Direct Map 4M string"; type string; } } } } yuma123_2.14/netconf/modules/netconfcentral/yuma-time-filter.yang0000664000175000017500000000604714770023131025315 0ustar vladimirvladimirmodule yuma-time-filter { namespace "http://netconfcentral.org/ns/yuma-time-filter"; prefix "timefilter"; import ietf-yang-types { prefix yang; } import ietf-netconf-monitoring { prefix mon; } import ietf-netconf { prefix nc; } organization "Netconf Central"; contact "Andy Bierman ."; description "Yuma extension for optimizing datastore polling mechanisms by allowing the client to request datastore content only if it has been modified since a specified date and time. If the netconfd server advertises this module then the 'last-modified' XML attribute will be added to the element if there was no 'last-modified' attribute in the element. This is only done for the NETCONF and protocol operations. Example: ... Copyright (c) 2011 - 2012 Andy Bierman and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the BSD 3-Clause License http://opensource.org/licenses/BSD-3-Clause"; reference "RFC 2616: Last-Modified and If-Modified-Since headers"; revision 2012-11-15 { description "Use import of ietf-netconf instead of yuma-netconf so the ietf-netconf module can be advertised in the hello and clients will know which version is used for imported data types."; } revision 2011-08-13 { description "Initial version."; } augment /mon:netconf-state/mon:datastores/mon:datastore { leaf last-modified { description "The date and time when the server last detected that the datastore was changed, or last initialized if no changes have been made. Note that state data is not included in a datastore, and modifications to state data do not affect this timestamp."; type yang:date-and-time; } } grouping if-modified-since-parm { leaf if-modified-since { description "If this parameter is present, then the server will only process the retrieval request if the corresponding 'last-modified' timestamp is more recent than this timestamp. If so, then the retrieval request is processed as normal. If not, an empty element will be returned."; type yang:date-and-time; } } augment /nc:get-config/nc:input { uses if-modified-since-parm; } augment /nc:get/nc:input { uses if-modified-since-parm; } } yuma123_2.14/netconf/modules/netconfcentral/yuma-types.yang0000664000175000017500000002160514770023131024235 0ustar vladimirvladimirmodule yuma-types { yang-version 1; namespace "http://netconfcentral.org/ns/yuma-types"; prefix "yt"; organization "Netconf Central"; contact "Andy Bierman "; description "Yuma Common Data Types"; revision 2012-06-01 { description "Added new NcDebugType enum (sysLogLevel). Added yang-identifier data type."; } revision 2011-12-18 { description "Added TransactionId data type."; } revision 2010-11-28 { description "Change default indent from 3 to 2."; } revision 2010-01-25 { description "Adjust name lengths in ranges and patterns."; } revision 2008-11-21 { description "Renamed ncxtypes to yuma-types."; } revision 2008-07-20 { description "Converted from ncxtypes.ncx."; } // typedef any removed typedef int { description "Changed int base type to int32 for YANG"; type int32; } typedef uint { description "Changed uint base type to uint32 for YANG"; type uint32; } typedef long { description "Changed long base type to int64 for YANG"; type int64; } typedef ulong { description "Changed ulong base type to uint64 for YANG"; type uint64; } typedef ustring { description "Changed ustring base type to binary for YANG"; type binary; } typedef NcxName { description "General Purpose NCX Name string."; type string { length "1..64"; pattern '[a-z,A-Z,_][a-z,A-Z,0-9,\-,_,\.]{0,63}'; } } typedef NcxQName { description "Qualified Name: module-name:NcxName OR owner-name:NcxName."; type string { length "1..129"; pattern '(([a-z,A-Z,_][a-z,A-Z,0-9,\-,_,\.]{0,63}):)' + '?([a-z,A-Z][a-z,A-Z,0-9,\-,_,\.]{0,63})'; } } typedef NcxIdentifier { description "Union of all the Identifier types."; type union { type NcxName; type NcxQName; } } typedef yang-identifier { description "YANG identifier string."; type string { length "1..max"; pattern '[a-z,A-Z,_][a-z,A-Z,0-9,\-,_,\.]*'; } } typedef NcxSessionId { description "NCX Session ID number"; type uint32 { range "1..max"; } } typedef NcxLineLength { description "Requested Maximum Line Length"; type uint32 { range "32..65535"; } } typedef NcxUserName { description "NCX User Name string."; type NcxName; } typedef NcxGroupName { description "NCX Group Name string."; type NcxName; } // typedef NcxModuleList removed // typedef NcxLockList removed typedef NcAccessControlType { description "NCX System access control mode."; type enumeration { enum open { description "no access control checking enforced"; } enum loose { description "any RPC method in the netconf namespace can be invoked; read-only data allowed for all"; } enum strict { description "RPC entry must be present to invoke an RPC method; ncxacl Data entry must be present to access any data. (Except for user == 'root'.)"; } } default "strict"; } typedef NcxRpcType { description "NCX RPC Type Classifications"; type enumeration { enum other; enum config; enum exec; enum monitor; enum debug; } } // typedef NcxRpcTypeList removed typedef NcDebugType { description "NCX Session debug logging control enumeration. Each successive value includes all the previous messages from lower value enumeration values, plus the messages for the specified value. off == no logging is done write == log write messages (NOT SUPPORTED IN YUMA) dev0 == log developer level 0 messages (NOT SUPPORTED IN YUMA) error == log error messages warn == log warning messages info == log info messages dev1 == log developer level 1 messages (NOT SUPPORTED IN YUMA) debug == log debug level 1 messages debug2 == log debug level 2 messages debug3 == log debug level 3 messages debug4 == log debug level 4 messages"; type enumeration { enum off; enum write; // yumapro only enum dev0; // yumapro only enum error; enum warn; enum info; enum dev1; // yumapro only enum debug; enum debug2; enum debug3; enum debug4; } default "info"; } typedef NcPortNumber { description "Transport layer port number."; type uint32 { range "1 .. 65535"; } } typedef NcIndex { description "Non-negative index value"; type uint32 { range "1..max"; } } typedef NcPathList { description "PATHSPEC formatted string indicating the machine-dependent search path for the NCX programs to use. Parameters with this data type can be used to override the default search order, and insert special work directories in the search path. Each component in the string is an absolute or relative directory path specification. The colon char ':' is used to separate the path strings. Whitespace is not allowed in the string at all. For example, the following string contains 3 paths that would be used in the order given: /home/users/testbed1/yang:/home/users/yang:/usr/share/yang"; type string { length "1 .. max"; } } typedef NcModuleSpec { description "A string which specifies a module name, or a filespec which represents a module, with an optional revision date. If this string represents a filespec, containing any path separation characters, and/or ending with the '.yang' or '.yin' extension, then only that file location will be checked. If this string represents a module name, then the module search path will be checked for a file with the module name and the '.yang' or '.yin.' extension. If this string contains a module name followed by an 'at sign' character (@), followed by a revision string (e.g., foo@2010-01-01), then that specific version of the module will be used. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path "; type string { length "1 .. 4095"; } } typedef NcPathSpec { description "A string which specifies a directory name."; type string { length "1 .. 4095"; } } typedef IndentType { description "Requested indent amount. Only a limited range of line indent values are allowed."; type uint32 { range "0..9"; } default "2"; } typedef Timeout { description "Number of seconds to wait for a response from the NETCONF peer before declaring a timeout. Zero means no timeout at all."; type uint32; units seconds; default 30; } typedef Date { type string { pattern '\d{4}-\d{2}-\d{2}'; } description "Represents a specific date in YYYY-MM-DD format."; } typedef FeatureSpec { description "Represents a feature specifier, which consists of a module-name, colon character ':' and then a feature name (e.g., 'foo:bar')"; type NcxQName; } typedef TransactionId { description "Database edit transaction identifier. This is not a permanent identifier, and should only be used for 'equal or not-equal' comparison tests. The value will wrap after the maximum value is reached."; type uint64 { range "1 .. max"; } } } yuma123_2.14/netconf/modules/netconfcentral/toaster.yang0000664000175000017500000001661114770023131023602 0ustar vladimirvladimirmodule toaster { namespace "http://netconfcentral.org/ns/toaster"; prefix "toast"; organization "Netconf Central"; contact "Andy Bierman "; description "YANG version of the TOASTER-MIB."; revision 2009-11-20 { description "Toaster module in progress."; } identity toast-type { description "Base for all bread types supported by the toaster. New bread types not listed here nay be added in the future."; } identity white-bread { description "White bread."; base toast:toast-type; } identity wheat-bread { description "Wheat bread."; base toast-type; } identity wonder-bread { description "Wonder bread."; base toast-type; } identity frozen-waffle { description "Frozen waffle."; base toast-type; } identity frozen-bagel { description "Frozen bagel."; base toast-type; } identity hash-brown { description "Hash browned potatos."; base toast-type; } typedef DisplayString { description "YANG version of the SMIv2 DisplayString TEXTUAL-CONVENTION."; reference "RFC 2579, section 2."; type string { length "0 .. 255"; } } container toaster { presence "Indicates the toaster service is available"; description "Top-level container for all toaster database objects."; leaf toasterManufacturer { type DisplayString; config false; mandatory true; description "The name of the toaster's manufacturer. For instance, Microsoft Toaster."; } leaf toasterModelNumber { type DisplayString; config false; mandatory true; description "The name of the toaster's model. For instance, Radiant Automatic."; } leaf toasterStatus { type enumeration { enum up { value 1; description "The toaster knob position is up. No toast is being made now."; } enum down { value 2; description "The toaster knob position is down. Toast is being made now."; } } config false; mandatory true; description "This variable indicates the current state of the toaster."; } } rpc make-toast { description "Make some toast. The toastDone notification will be sent when the toast is finished. An 'in-use' error will be returned if toast is already being made. A 'resource-denied' error will be returned if the toaster service is disabled."; input { leaf toasterDoneness { type uint32 { range "1 .. 10"; } default 5; description "This variable controls how well-done is the ensuing toast. It should be on a scale of 1 to 10. Toast made at 10 generally is considered unfit for human consumption; toast made at 1 is warmed lightly."; } leaf toasterToastType { type identityref { base toast:toast-type; } default toast:wheat-bread; description "This variable informs the toaster of the type of material that is being toasted. The toaster uses this information, combined with toasterDoneness, to compute for how long the material must be toasted to achieve the required doneness."; } } } rpc cancel-toast { description "Stop making toast, if any is being made. A 'resource-denied' error will be returned if the toaster service is disabled."; } notification toastDone { description "Indicates that the toast in progress has completed."; leaf toastStatus { description "Indicates the final toast status"; type enumeration { enum done { description "The toast is done."; } enum cancelled { description "The toast was cancelled."; } enum error { description "The toaster service was disabled or the toaster is broken."; } } } } /************************************************************* Original TOASTER-MIB TOASTER-MIB DEFINITIONS ::= BEGIN IMPORTS enterprises FROM RFC1155-SMI OBJECT-TYPE FROM RFC-1212 DisplayString FROM RFC-1213; epilogue OBJECT IDENTIFIER ::= {enterprises 12} toaster OBJECT IDENTIFIER ::= {epilogue 2} toasterManufacturer OBJECT-TYPE SYNTAX DisplayString ACCESS read-only STATUS mandatory DESCRIPTION "The name of the toaster's manufacturer. For instance, Microsoft Toaster." ::= {toaster 1} toasterModelNumber OBJECT-TYPE SYNTAX DisplayString ACCESS read-only STATUS mandatory DESCRIPTION "The name of the toaster's model. For instance, Radiant Automatic." ::= {toaster 2} toasterControl OBJECT-TYPE SYNTAX INTEGER {up (1), down (2)} ACCESS read-write STATUS mandatory DESCRIPTION "This variable controls the current state of the toaster. To begin toasting, set it to down (2). To abort toasting (perhaps in the event of an emergency), set it to up (2)." ::= {toaster 3} toasterDoneness OBJECT-TYPE SYNTAX INTEGER (1..10) ACCESS read-write STATUS mandatory DESCRIPTION "This variable controls how well-done is the ensuing toast. It should be on a scale of 1 to 10. Toast made at 10 generally is considered unfit for human consumption; toast made at 1 is warmed lightly." ::= {toaster 4} toasterToastType OBJECT-TYPE SYNTAX INTEGER { white-bread (1), wheat-bread (2), wonder-bread (3), frozen-waffle (4), frozen-bagel (5), hash-brown (6), other (7) } ACCESS read-write STATUS mandatory DESCRIPTION "This variable informs the toaster of the type of material that is being toasted. The toaster uses this information, combined with toasterToastDoneness, to compute for how long the material must be toasted to achieve the required doneness." ::= {toaster 5} END *************************************************************/ } yuma123_2.14/netconf/modules/ietf/0000775000175000017500000000000014770023131017156 5ustar vladimirvladimiryuma123_2.14/netconf/modules/ietf/ietf-yang-metadata@2016-08-05.yang0000664000175000017500000000561314770023131024564 0ustar vladimirvladimirmodule ietf-yang-metadata { namespace "urn:ietf:params:xml:ns:yang:ietf-yang-metadata"; prefix "md"; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Lou Berger WG Chair: Kent Watsen Editor: Ladislav Lhotka "; description "This YANG module defines an 'extension' statement that allows for defining metadata annotations. Copyright (c) 2016 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7952 (http://www.rfc-editor.org/info/rfc7952); see the RFC itself for full legal notices."; revision 2016-08-05 { description "Initial revision."; reference "RFC 7952: Defining and Using Metadata with YANG"; } extension annotation { argument name; description "This extension allows for defining metadata annotations in YANG modules. The 'md:annotation' statement can appear only at the top level of a YANG module or submodule, i.e., it becomes a new alternative in the ABNF production rule for 'body-stmts' (Section 14 in RFC 7950). The argument of the 'md:annotation' statement defines the name of the annotation. Syntactically, it is a YANG identifier as defined in Section 6.2 of RFC 7950. An annotation defined with this 'extension' statement inherits the namespace and other context from the YANG module in which it is defined. The data type of the annotation value is specified in the same way as for a leaf data node using the 'type' statement. The semantics of the annotation and other documentation can be specified using the following standard YANG substatements (all are optional): 'description', 'if-feature', 'reference', 'status', and 'units'. A server announces support for a particular annotation by including the module in which the annotation is defined among the advertised YANG modules, e.g., in a NETCONF message or in the YANG library (RFC 7950). The annotation can then be attached to any instance of a data node defined in any YANG module that is advertised by the server. XML encoding and JSON encoding of annotations are defined in RFC 7952."; } } yuma123_2.14/netconf/modules/ietf/ietf-snmp-tls@2014-12-10.yang0000664000175000017500000001525314770023131023613 0ustar vladimirvladimirsubmodule ietf-snmp-tls { belongs-to ietf-snmp { prefix snmp; } import ietf-inet-types { prefix inet; } import ietf-x509-cert-to-name { prefix x509c2n; } include ietf-snmp-common; include ietf-snmp-engine; include ietf-snmp-target; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This submodule contains a collection of YANG definitions for configuring the Transport Layer Security Transport Model (TLSTM) of SNMP. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP)"; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } feature tlstm { description "A server implements this feature if it supports the Transport Layer Security Transport Model for SNMP."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP)"; } augment /snmp:snmp/snmp:engine/snmp:listen/snmp:transport { if-feature tlstm; case tls { container tls { description "A list of IPv4 and IPv6 addresses and ports to which the engine listens for SNMP messages over TLS."; leaf ip { type inet:ip-address; mandatory true; description "The IPv4 or IPv6 address on which the engine listens for SNMP messages over TLS."; } leaf port { type inet:port-number; description "The TCP port on which the engine listens for SNMP messages over TLS. If the port is not configured, an engine that acts as a Command Responder uses port 10161, and an engine that acts as a Notification Receiver uses port 10162."; } } } case dtls { container dtls { description "A list of IPv4 and IPv6 addresses and ports to which the engine listens for SNMP messages over DTLS."; leaf ip { type inet:ip-address; mandatory true; description "The IPv4 or IPv6 address on which the engine listens for SNMP messages over DTLS."; } leaf port { type inet:port-number; description "The UDP port on which the engine listens for SNMP messages over DTLS. If the port is not configured, an engine that acts as a Command Responder uses port 10161, and an engine that acts as a Notification Receiver uses port 10162."; } } } } augment /snmp:snmp { if-feature tlstm; container tlstm { uses x509c2n:cert-to-name { description "Defines how certificates are mapped to names. The resulting name is used as a security name."; refine cert-to-name/map-type { description "Mappings that use the snmpTlstmCertToTSNData column need to augment the cert-to-name list with additional configuration objects corresponding to the snmpTlstmCertToTSNData value. Such objects should use the 'when' statement to make them conditional based on the map-type."; } } } } grouping tls-transport { leaf ip { type inet:host; mandatory true; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetAddrTAddress RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.SnmpTLSAddress"; } leaf port { type inet:port-number; default 10161; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetAddrTAddress RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.SnmpTLSAddress"; } leaf client-fingerprint { type x509c2n:tls-fingerprint; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmParamsClientFingerprint"; } leaf server-fingerprint { type x509c2n:tls-fingerprint; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmAddrServerFingerprint"; } leaf server-identity { type snmp:admin-string; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmAddrServerIdentity"; } } augment /snmp:snmp/snmp:target/snmp:transport { if-feature tlstm; case tls { reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTLSTCPDomain"; container tls { uses tls-transport; } } } augment /snmp:snmp/snmp:target/snmp:transport { if-feature tlstm; case dtls { reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpDTLSUDPDomain"; container dtls { uses tls-transport; } } } } yuma123_2.14/netconf/modules/ietf/ietf-netconf-time@2016-01-26.yang0000664000175000017500000002251514770023131024434 0ustar vladimirvladimirmodule ietf-netconf-time { namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-time"; prefix nct; import ietf-netconf { prefix nc; } import ietf-yang-types { prefix yang; } import ietf-netconf-monitoring { prefix ncm; } organization "IETF"; contact "Editor: Tal Mizrahi Editor: Yoram Moses "; description "This module defines a capability-based extension to the Network Configuration Protocol (NETCONF) that allows time-triggered configuration and management operations. This extension allows NETCONF clients to invoke configuration updates according to scheduled times and allows NETCONF servers to attach timestamps to the data they send to NETCONF clients. Copyright (c) 2016 IETF Trust and the persons identified as the authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info)."; revision 2016-01-26 { description "Initial version."; reference "RFC 7758: Time Capability in NETCONF"; } typedef time-interval { type string { pattern '\d{2}:\d{2}:\d{2}(\.\d+)?'; } description "Defines a time interval, up to 24 hours. The format is specified as HH:mm:ss.f, consisting of two digits for hours, two digits for minutes, two digits for seconds, and zero or more digits representing second fractions."; } grouping scheduling-tolerance-parameters { leaf sched-max-future { type time-interval; default 00:00:15.0; description "When the scheduled time is in the future, i.e., greater than the present time, this leaf defines the maximal difference between the scheduled time and the present time that the server is willing to accept. If the difference exceeds this number, the server responds with an error."; } leaf sched-max-past { type time-interval; default 00:00:15.0; description "When the scheduled time is in the past, i.e., less than the present time, this leaf defines the maximal difference between the present time and the scheduled time that the server is willing to accept. If the difference exceeds this number, the server responds with an error."; } description "Contains the parameters of the scheduling tolerance."; } // extending the get-config operation augment /nc:get-config/nc:input { leaf scheduled-time { type yang:date-and-time; description "The time at which the RPC is scheduled to be performed."; } leaf get-time { type empty; description "Indicates that the rpc-reply should include the execution-time."; } description "Adds the time element to ."; } augment /nc:get-config/nc:output { leaf execution-time { type yang:date-and-time; description "The time at which the RPC was executed."; } description "Adds the time element to ."; } augment /nc:get/nc:input { leaf scheduled-time { type yang:date-and-time; description "The time at which the RPC is scheduled to be performed."; } leaf get-time { type empty; description "Indicates that the rpc-reply should include the execution-time."; } description "Adds the time element to ."; } augment /nc:get/nc:output { leaf execution-time { type yang:date-and-time; description "The time at which the RPC was executed."; } description "Adds the time element to ."; } augment /nc:copy-config/nc:input { leaf scheduled-time { type yang:date-and-time; description "The time at which the RPC is scheduled to be performed."; } leaf get-time { type empty; description "Indicates that the rpc-reply should include the execution-time."; } description "Adds the time element to ."; } augment /nc:copy-config/nc:output { leaf execution-time { type yang:date-and-time; description "The time at which the RPC was executed."; } description "Adds the time element to ."; } augment /nc:edit-config/nc:input { leaf scheduled-time { type yang:date-and-time; description "The time at which the RPC is scheduled to be performed."; } leaf get-time { type empty; description "Indicates that the rpc-reply should include the execution-time."; } description "Adds the time element to ."; } augment /nc:edit-config/nc:output { leaf execution-time { type yang:date-and-time; description "The time at which the RPC was executed."; } description "Adds the time element to ."; } augment /nc:delete-config/nc:input { leaf scheduled-time { type yang:date-and-time; description "The time at which the RPC is scheduled to be performed."; } leaf get-time { type empty; description "Indicates that the rpc-reply should include the execution-time."; } description "Adds the time element to ."; } augment /nc:delete-config/nc:output { leaf execution-time { type yang:date-and-time; description "The time at which the RPC was executed."; } description "Adds the time element to ."; } augment /nc:lock/nc:input { leaf scheduled-time { type yang:date-and-time; description "The time at which the RPC is scheduled to be performed."; } leaf get-time { type empty; description "Indicates that the rpc-reply should include the execution-time."; } description "Adds the time element to ."; } augment /nc:lock/nc:output { leaf execution-time { type yang:date-and-time; description "The time at which the RPC was executed."; } description "Adds the time element to ."; } augment /nc:unlock/nc:input { leaf scheduled-time { type yang:date-and-time; description "The time at which the RPC is scheduled to be performed."; } leaf get-time { type empty; description "Indicates that the rpc-reply should include the execution-time."; } description "Adds the time element to ."; } augment /nc:unlock/nc:output { leaf execution-time { type yang:date-and-time; description "The time at which the RPC was executed."; } description "Adds the time element to ."; } augment /nc:commit/nc:input { leaf scheduled-time { type yang:date-and-time; description "The time at which the RPC is scheduled to be performed."; } leaf get-time { type empty; description "Indicates that the rpc-reply should include the execution-time."; } description "Adds the time element to ."; } augment /nc:commit/nc:output { leaf execution-time { type yang:date-and-time; description "The time at which the RPC was executed."; } description "Adds the time element to ."; } augment /ncm:netconf-state { container scheduling-tolerance { uses scheduling-tolerance-parameters; description "The scheduling tolerance when the time capability is enabled."; } description "The scheduling tolerance of the server."; } rpc cancel-schedule { description "Cancels a scheduled message."; reference "RFC 7758: Time Capability in NETCONF"; input { leaf cancelled-message-id { type string; description "The ID of the message to be cancelled."; } leaf get-time { type empty; description "Indicates that the rpc-reply should include the execution-time."; } } output { leaf execution-time { type yang:date-and-time; description "The time at which the RPC was executed."; } } } notification netconf-scheduled-message { leaf schedule-id { type string; description "The ID of the scheduled message."; } leaf scheduled-time { type yang:date-and-time; description "The time at which the RPC is scheduled to be performed."; } description "Indicates that a scheduled message was received."; reference "RFC 7758: Time Capability in NETCONF"; } } yuma123_2.14/netconf/modules/ietf/ietf-network-topology-state@2018-02-26.yang0000664000175000017500000002265414770023131026534 0ustar vladimirvladimirmodule ietf-network-topology-state { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-network-topology-state"; prefix nt-s; import ietf-network-state { prefix nw-s; reference "RFC 8345: A YANG Data Model for Network Topologies"; } import ietf-network-topology { prefix nt; reference "RFC 8345: A YANG Data Model for Network Topologies"; } organization "IETF I2RS (Interface to the Routing System) Working Group"; contact "WG Web: WG List: Editor: Alexander Clemm Editor: Jan Medved Editor: Robert Varga Editor: Nitin Bahadur Editor: Hariharan Ananthakrishnan Editor: Xufeng Liu "; description "This module defines a common base data model for network topology state, representing topology that either (1) is learned or (2) results from applying topology that has been configured per the 'ietf-network-topology' data model, mirroring the corresponding data nodes in this data model. It augments the base network state data model with links to connect nodes, as well as termination points to terminate links on nodes. The data model mirrors 'ietf-network-topology' but contains only read-only state data. The data model is not needed when the underlying implementation infrastructure supports the Network Management Datastore Architecture (NMDA). Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8345; see the RFC itself for full legal notices."; revision 2018-02-26 { description "Initial revision."; reference "RFC 8345: A YANG Data Model for Network Topologies"; } grouping link-ref { description "References a link in a specific network. Although this grouping is not used in this module, it is defined here for the convenience of augmenting modules."; leaf link-ref { type leafref { path "/nw-s:networks/nw-s:network[nw-s:network-id=current()"+ "/../network-ref]/nt-s:link/nt-s:link-id"; require-instance false; } description "A type for an absolute reference to a link instance. (This type should not be used for relative references. In such a case, a relative path should be used instead.)"; } uses nw-s:network-ref; } grouping tp-ref { description "References a termination point in a specific node. Although this grouping is not used in this module, it is defined here for the convenience of augmenting modules."; leaf tp-ref { type leafref { path "/nw-s:networks/nw-s:network[nw-s:network-id=current()"+ "/../network-ref]/nw-s:node[nw-s:node-id=current()/../"+ "node-ref]/nt-s:termination-point/nt-s:tp-id"; require-instance false; } description "A type for an absolute reference to a termination point. (This type should not be used for relative references. In such a case, a relative path should be used instead.)"; } uses nw-s:node-ref; } augment "/nw-s:networks/nw-s:network" { description "Add links to the network data model."; list link { key "link-id"; description "A network link connects a local (source) node and a remote (destination) node via a set of the respective node's termination points. It is possible to have several links between the same source and destination nodes. Likewise, a link could potentially be re-homed between termination points. Therefore, in order to ensure that we would always know to distinguish between links, every link is identified by a dedicated link identifier. Note that a link models a point-to-point link, not a multipoint link."; container source { description "This container holds the logical source of a particular link."; leaf source-node { type leafref { path "../../../nw-s:node/nw-s:node-id"; require-instance false; } description "Source node identifier. Must be in the same topology."; } leaf source-tp { type leafref { path "../../../nw-s:node[nw-s:node-id=current()/../"+ "source-node]/termination-point/tp-id"; require-instance false; } description "This termination point is located within the source node and terminates the link."; } } container destination { description "This container holds the logical destination of a particular link."; leaf dest-node { type leafref { path "../../../nw-s:node/nw-s:node-id"; require-instance false; } description "Destination node identifier. Must be in the same network."; } leaf dest-tp { type leafref { path "../../../nw-s:node[nw-s:node-id=current()/../"+ "dest-node]/termination-point/tp-id"; require-instance false; } description "This termination point is located within the destination node and terminates the link."; } } leaf link-id { type nt:link-id; description "The identifier of a link in the topology. A link is specific to a topology to which it belongs."; } list supporting-link { key "network-ref link-ref"; description "Identifies the link or links on which this link depends."; leaf network-ref { type leafref { path "../../../nw-s:supporting-network/nw-s:network-ref"; require-instance false; } description "This leaf identifies in which underlay topology the supporting link is present."; } leaf link-ref { type leafref { path "/nw-s:networks/nw-s:network[nw-s:network-id="+ "current()/../network-ref]/link/link-id"; require-instance false; } description "This leaf identifies a link that is a part of this link's underlay. Reference loops in which a link identifies itself as its underlay, either directly or transitively, are not allowed."; } } } } augment "/nw-s:networks/nw-s:network/nw-s:node" { description "Augments termination points that terminate links. Termination points can ultimately be mapped to interfaces."; list termination-point { key "tp-id"; description "A termination point can terminate a link. Depending on the type of topology, a termination point could, for example, refer to a port or an interface."; leaf tp-id { type nt:tp-id; description "Termination point identifier."; } list supporting-termination-point { key "network-ref node-ref tp-ref"; description "This list identifies any termination points on which a given termination point depends or onto which it maps. Those termination points will themselves be contained in a supporting node. This dependency information can be inferred from the dependencies between links. Therefore, this item is not separately configurable. Hence, no corresponding constraint needs to be articulated. The corresponding information is simply provided by the implementing system."; leaf network-ref { type leafref { path "../../../nw-s:supporting-node/nw-s:network-ref"; require-instance false; } description "This leaf identifies in which topology the supporting termination point is present."; } leaf node-ref { type leafref { path "../../../nw-s:supporting-node/nw-s:node-ref"; require-instance false; } description "This leaf identifies in which node the supporting termination point is present."; } leaf tp-ref { type leafref { path "/nw-s:networks/nw-s:network[nw-s:network-id="+ "current()/../network-ref]/nw-s:node[nw-s:node-id="+ "current()/../node-ref]/termination-point/tp-id"; require-instance false; } description "Reference to the underlay node (the underlay node must be in a different topology)."; } } } } } yuma123_2.14/netconf/modules/ietf/ietf-ipv6-unicast-routing@2016-11-04.yang0000664000175000017500000001644314770023131026061 0ustar vladimirvladimirmodule ietf-ipv6-unicast-routing { yang-version "1.1"; namespace "urn:ietf:params:xml:ns:yang:ietf-ipv6-unicast-routing"; prefix "v6ur"; import ietf-routing { prefix "rt"; } import ietf-inet-types { prefix "inet"; } include ietf-ipv6-router-advertisements { revision-date 2016-11-04; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Lou Berger WG Chair: Kent Watsen Editor: Ladislav Lhotka Editor: Acee Lindem "; description "This YANG module augments the 'ietf-routing' module with basic configuration and state data for IPv6 unicast routing. Copyright (c) 2016 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'MAY', and 'OPTIONAL' in the module text are to be interpreted as described in RFC 2119. This version of this YANG module is part of RFC 8022; see the RFC itself for full legal notices."; revision 2016-11-04 { description "Initial revision."; reference "RFC 8022: A YANG Data Model for Routing Management"; } /* Identities */ identity ipv6-unicast { base rt:ipv6; description "This identity represents the IPv6 unicast address family."; } /* State data */ augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route" { when "derived-from-or-self(../../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } description "This leaf augments an IPv6 unicast route."; leaf destination-prefix { type inet:ipv6-prefix; description "IPv6 destination prefix."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/" + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" { when "derived-from-or-self(../../../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } description "Augment 'simple-next-hop' case in IPv6 unicast routes."; leaf next-hop-address { type inet:ipv6-address; description "IPv6 address of the next hop."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/" + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/" + "rt:next-hop-list/rt:next-hop" { when "derived-from-or-self(../../../../../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } description "This leaf augments the 'next-hop-list' case of IPv6 unicast routes."; leaf address { type inet:ipv6-address; description "IPv6 address of the next hop."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/rt:input" { when "derived-from-or-self(../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast RIBs."; } description "This augment adds the input parameter of the 'active-route' action."; leaf destination-address { type inet:ipv6-address; description "IPv6 destination address."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route" { when "derived-from-or-self(../../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } description "This augment adds the destination prefix to the reply of the 'active-route' action."; leaf destination-prefix { type inet:ipv6-prefix; description "IPv6 destination prefix."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" + "rt:simple-next-hop" { when "derived-from-or-self(../../../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } description "Augment 'simple-next-hop' case in the reply to the 'active-route' action."; leaf next-hop-address { type inet:ipv6-address; description "IPv6 address of the next hop."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" { when "derived-from-or-self(../../../../../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } description "Augment 'next-hop-list' case in the reply to the 'active-route' action."; leaf next-hop-address { type inet:ipv6-address; description "IPv6 address of the next hop."; } } /* Configuration data */ augment "/rt:routing/rt:control-plane-protocols/" + "rt:control-plane-protocol/rt:static-routes" { description "This augment defines the configuration of the 'static' pseudo-protocol with data specific to IPv6 unicast."; container ipv6 { description "Configuration of a 'static' pseudo-protocol instance consists of a list of routes."; list route { key "destination-prefix"; description "A list of static routes."; leaf destination-prefix { type inet:ipv6-prefix; mandatory "true"; description "IPv6 destination prefix."; } leaf description { type string; description "Textual description of the route."; } container next-hop { description "Configuration of next-hop."; uses rt:next-hop-content { augment "next-hop-options/simple-next-hop" { description "Augment 'simple-next-hop' case in IPv6 static routes."; leaf next-hop-address { type inet:ipv6-address; description "IPv6 address of the next hop."; } } augment "next-hop-options/next-hop-list/next-hop-list/" + "next-hop" { description "Augment 'next-hop-list' case in IPv6 static routes."; leaf next-hop-address { type inet:ipv6-address; description "IPv6 address of the next hop."; } } } } } } } } yuma123_2.14/netconf/modules/ietf/ietf-hardware-state@2018-03-13.yang0000664000175000017500000006112014770023131024752 0ustar vladimirvladimirmodule ietf-hardware-state { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-hardware-state"; prefix hw-state; import ietf-inet-types { prefix inet; } import ietf-yang-types { prefix yang; } import iana-hardware { prefix ianahw; } import ietf-hardware { prefix hw; } organization "IETF NETMOD (Network Modeling) Working Group"; contact "WG Web: WG List: Editor: Andy Bierman Editor: Martin Bjorklund Editor: Jie Dong Editor: Dan Romascanu "; description "This module contains a collection of YANG definitions for monitoring hardware. This data model is designed as a temporary solution for implementations that do not yet support the Network Management Datastore Architecture (NMDA) defined in RFC 8342. Such an implementation cannot implement the module 'ietf-hardware' properly, since without NMDA support, it is not possible to distinguish between instances of nodes in the running configuration and operational states. The data model in this module is the same as the data model in 'ietf-hardware', except all nodes are marked as 'config false'. If a server that implements this module but doesn't support NMDA also supports configuration of hardware components, it SHOULD also implement the module 'ietf-hardware' in the configuration datastores. The corresponding state data is found in the '/hw-state:hardware' subtree. Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8348; see the RFC itself for full legal notices."; revision 2018-03-13 { description "Initial revision."; reference "RFC 8348: A YANG Data Model for Hardware Management"; } /* * Features */ feature entity-mib { status deprecated; description "This feature indicates that the device implements the ENTITY-MIB."; reference "RFC 6933: Entity MIB (Version 4)"; } feature hardware-state { status deprecated; description "Indicates that ENTITY-STATE-MIB objects are supported"; reference "RFC 4268: Entity State MIB"; } feature hardware-sensor { status deprecated; description "Indicates that ENTITY-SENSOR-MIB objects are supported"; reference "RFC 3433: Entity Sensor Management Information Base"; } /* * Data nodes */ container hardware { config false; status deprecated; description "Data nodes representing components."; leaf last-change { type yang:date-and-time; status deprecated; description "The time the '/hardware/component' list changed in the operational state."; } list component { key name; status deprecated; description "List of components. When the server detects a new hardware component, it initializes a list entry in the operational state. If the server does not support configuration of hardware components, list entries in the operational state are initialized with values for all nodes as detected by the implementation. Otherwise, this procedure is followed: 1. If there is an entry in the '/hardware/component' list in the intended configuration with values for the nodes 'class', 'parent', and 'parent-rel-pos' that are equal to the detected values, then: 1a. If the configured entry has a value for 'mfg-name' that is equal to the detected value or if the 'mfg-name' value cannot be detected, then the list entry in the operational state is initialized with the configured values for all configured nodes, including the 'name'. Otherwise, the list entry in the operational state is initialized with values for all nodes as detected by the implementation. The implementation may raise an alarm that informs about the 'mfg-name' mismatch condition. How this is done is outside the scope of this document. 1b. Otherwise (i.e., there is no matching configuration entry), the list entry in the operational state is initialized with values for all nodes as detected by the implementation. If the '/hardware/component' list in the intended configuration is modified, then the system MUST behave as if it re-initializes itself and follow the procedure in (1)."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalEntry"; leaf name { type string; status deprecated; description "The name assigned to this component. This name is not required to be the same as entPhysicalName."; } leaf class { type identityref { base ianahw:hardware-class; } mandatory true; status deprecated; description "An indication of the general hardware type of the component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalClass"; } leaf physical-index { if-feature entity-mib; type int32 { range "1..2147483647"; } status deprecated; description "The entPhysicalIndex for the entPhysicalEntry represented by this list entry."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalIndex"; } leaf description { type string; status deprecated; description "A textual description of the component. This node should contain a string that identifies the manufacturer's name for the component and should be set to a distinct value for each version or model of the component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalDescr"; } leaf parent { type leafref { path "../../component/name"; require-instance false; } status deprecated; description "The name of the component that physically contains this component. If this leaf is not instantiated, it indicates that this component is not contained in any other component. In the event that a physical component is contained by more than one physical component (e.g., double-wide modules), this node contains the name of one of these components. An implementation MUST use the same name every time this node is instantiated."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalContainedIn"; } leaf parent-rel-pos { type int32 { range "0 .. 2147483647"; } status deprecated; description "An indication of the relative position of this child component among all its sibling components. Sibling components are defined as components that: o share the same value of the 'parent' node and o share a common base identity for the 'class' node. Note that the last rule gives implementations flexibility in how components are numbered. For example, some implementations might have a single number series for all components derived from 'ianahw:port', while some others might have different number series for different components with identities derived from 'ianahw:port' (for example, one for RJ45 and one for SFP)."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalParentRelPos"; } leaf-list contains-child { type leafref { path "../../component/name"; } status deprecated; description "The name of the contained component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalChildIndex"; } leaf hardware-rev { type string; status deprecated; description "The vendor-specific hardware revision string for the component. The preferred value is the hardware revision identifier actually printed on the component itself (if present)."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalHardwareRev"; } leaf firmware-rev { type string; status deprecated; description "The vendor-specific firmware revision string for the component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalFirmwareRev"; } leaf software-rev { type string; status deprecated; description "The vendor-specific software revision string for the component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalSoftwareRev"; } leaf serial-num { type string; status deprecated; description "The vendor-specific serial number string for the component. The preferred value is the serial number string actually printed on the component itself (if present)."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalSerialNum"; } leaf mfg-name { type string; status deprecated; description "The name of the manufacturer of this physical component. The preferred value is the manufacturer name string actually printed on the component itself (if present). Note that comparisons between instances of the 'model-name', 'firmware-rev', 'software-rev', and 'serial-num' nodes are only meaningful amongst components with the same value of 'mfg-name'. If the manufacturer name string associated with the physical component is unknown to the server, then this node is not instantiated."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalMfgName"; } leaf model-name { type string; status deprecated; description "The vendor-specific model name identifier string associated with this physical component. The preferred value is the customer-visible part number, which may be printed on the component itself. If the model name string associated with the physical component is unknown to the server, then this node is not instantiated."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalModelName"; } leaf alias { type string; status deprecated; description "An 'alias' name for the component, as specified by a network manager, that provides a non-volatile 'handle' for the component. If no configured value exists, the server MAY set the value of this node to a locally unique value in the operational state. A server implementation MAY map this leaf to the entPhysicalAlias MIB object. Such an implementation needs to use some mechanism to handle the differences in size and characters allowed between this leaf and entPhysicalAlias. The definition of such a mechanism is outside the scope of this document."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalAlias"; } leaf asset-id { type string; status deprecated; description "This node is a user-assigned asset tracking identifier for the component. A server implementation MAY map this leaf to the entPhysicalAssetID MIB object. Such an implementation needs to use some mechanism to handle the differences in size and characters allowed between this leaf and entPhysicalAssetID. The definition of such a mechanism is outside the scope of this document."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalAssetID"; } leaf is-fru { type boolean; status deprecated; description "This node indicates whether or not this component is considered a 'field-replaceable unit' by the vendor. If this node contains the value 'true', then this component identifies a field-replaceable unit. For all components that are permanently contained within a field-replaceable unit, the value 'false' should be returned for this node."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalIsFRU"; } leaf mfg-date { type yang:date-and-time; status deprecated; description "The date of manufacturing of the managed component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalMfgDate"; } leaf-list uri { type inet:uri; status deprecated; description "This node contains identification information about the component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalUris"; } leaf uuid { type yang:uuid; status deprecated; description "A Universally Unique Identifier of the component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalUUID"; } container state { if-feature hardware-state; status deprecated; description "State-related nodes"; reference "RFC 4268: Entity State MIB"; leaf state-last-changed { type yang:date-and-time; status deprecated; description "The date and time when the value of any of the admin-state, oper-state, usage-state, alarm-state, or standby-state changed for this component. If there has been no change since the last re-initialization of the local system, this node contains the date and time of local system initialization. If there has been no change since the component was added to the local system, this node contains the date and time of the insertion."; reference "RFC 4268: Entity State MIB - entStateLastChanged"; } leaf admin-state { type hw:admin-state; status deprecated; description "The administrative state for this component. This node refers to a component's administrative permission to service both other components within its containment hierarchy as well as other users of its services defined by means outside the scope of this module. Some components exhibit only a subset of the remaining administrative state values. Some components cannot be locked; hence, this node exhibits only the 'unlocked' state. Other components cannot be shut down gracefully; hence, this node does not exhibit the 'shutting-down' state."; reference "RFC 4268: Entity State MIB - entStateAdmin"; } leaf oper-state { type hw:oper-state; status deprecated; description "The operational state for this component. Note that this node does not follow the administrative state. An administrative state of 'down' does not predict an operational state of 'disabled'. Note that some implementations may not be able to accurately report oper-state while the admin-state node has a value other than 'unlocked'. In these cases, this node MUST have a value of 'unknown'."; reference "RFC 4268: Entity State MIB - entStateOper"; } leaf usage-state { type hw:usage-state; status deprecated; description "The usage state for this component. This node refers to a component's ability to service more components in a containment hierarchy. Some components will exhibit only a subset of the usage state values. Components that are unable to ever service any components within a containment hierarchy will always have a usage state of 'busy'. In some cases, a component will be able to support only one other component within its containment hierarchy and will therefore only exhibit values of 'idle' and 'busy'."; reference "RFC 4268: Entity State MIB - entStateUsage"; } leaf alarm-state { type hw:alarm-state; status deprecated; description "The alarm state for this component. It does not include the alarms raised on child components within its containment hierarchy."; reference "RFC 4268: Entity State MIB - entStateAlarm"; } leaf standby-state { type hw:standby-state; status deprecated; description "The standby state for this component. Some components will exhibit only a subset of the remaining standby state values. If this component cannot operate in a standby role, the value of this node will always be 'providing-service'."; reference "RFC 4268: Entity State MIB - entStateStandby"; } } container sensor-data { when 'derived-from-or-self(../class, "ianahw:sensor")' { description "Sensor data nodes present for any component of type 'sensor'"; } if-feature hardware-sensor; status deprecated; description "Sensor-related nodes."; reference "RFC 3433: Entity Sensor Management Information Base"; leaf value { type hw:sensor-value; status deprecated; description "The most recent measurement obtained by the server for this sensor. A client that periodically fetches this node should also fetch the nodes 'value-type', 'value-scale', and 'value-precision', since they may change when the value is changed."; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorValue"; } leaf value-type { type hw:sensor-value-type; status deprecated; description "The type of data units associated with the sensor value"; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorType"; } leaf value-scale { type hw:sensor-value-scale; status deprecated; description "The (power of 10) scaling factor associated with the sensor value"; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorScale"; } leaf value-precision { type hw:sensor-value-precision; status deprecated; description "The number of decimal places of precision associated with the sensor value"; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorPrecision"; } leaf oper-status { type hw:sensor-status; status deprecated; description "The operational status of the sensor."; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorOperStatus"; } leaf units-display { type string; status deprecated; description "A textual description of the data units that should be used in the display of the sensor value."; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorUnitsDisplay"; } leaf value-timestamp { type yang:date-and-time; status deprecated; description "The time the status and/or value of this sensor was last obtained by the server."; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorValueTimeStamp"; } leaf value-update-rate { type uint32; units "milliseconds"; status deprecated; description "An indication of the frequency that the server updates the associated 'value' node, represented in milliseconds. The value zero indicates: - the sensor value is updated on demand (e.g., when polled by the server for a get-request), - the sensor value is updated when the sensor value changes (event-driven), or - the server does not know the update rate."; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorValueUpdateRate"; } } } } /* * Notifications */ notification hardware-state-change { status deprecated; description "A hardware-state-change notification is generated when the value of /hardware/last-change changes in the operational state."; reference "RFC 6933: Entity MIB (Version 4) - entConfigChange"; } notification hardware-state-oper-enabled { if-feature hardware-state; status deprecated; description "A hardware-state-oper-enabled notification signifies that a component has transitioned into the 'enabled' state."; leaf name { type leafref { path "/hardware/component/name"; } status deprecated; description "The name of the component that has transitioned into the 'enabled' state."; } leaf admin-state { type leafref { path "/hardware/component/state/admin-state"; } status deprecated; description "The administrative state for the component."; } leaf alarm-state { type leafref { path "/hardware/component/state/alarm-state"; } status deprecated; description "The alarm state for the component."; } reference "RFC 4268: Entity State MIB - entStateOperEnabled"; } notification hardware-state-oper-disabled { if-feature hardware-state; status deprecated; description "A hardware-state-oper-disabled notification signifies that a component has transitioned into the 'disabled' state."; leaf name { type leafref { path "/hardware/component/name"; } status deprecated; description "The name of the component that has transitioned into the 'disabled' state."; } leaf admin-state { type leafref { path "/hardware/component/state/admin-state"; } status deprecated; description "The administrative state for the component."; } leaf alarm-state { type leafref { path "/hardware/component/state/alarm-state"; } status deprecated; description "The alarm state for the component."; } reference "RFC 4268: Entity State MIB - entStateOperDisabled"; } } yuma123_2.14/netconf/modules/ietf/ietf-snmp-common@2014-12-10.yang0000664000175000017500000001152014770023131024272 0ustar vladimirvladimirsubmodule ietf-snmp-common { belongs-to ietf-snmp { prefix snmp; } import ietf-yang-types { prefix yang; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This submodule contains a collection of common YANG definitions for configuring SNMP engines. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } /* Collection of SNMP-specific data types */ typedef admin-string { type string { length "0..255"; } description "Represents SnmpAdminString as defined in RFC 3411. Note that the size of an SnmpAdminString is measured in octets, not characters."; reference "RFC 3411: An Architecture for Describing Simple Network Management Protocol (SNMP) Management Frameworks. SNMP-FRAMEWORK-MIB.SnmpAdminString"; } typedef identifier { type admin-string { length "1..32"; } description "Identifiers are used to name items in the SNMP configuration datastore."; } typedef context-name { type admin-string { length "0..32"; } description "The context type represents an SNMP context name."; reference "RFC 3411: An Architecture for Describing Simple Network Management Protocol (SNMP) Management Frameworks"; } typedef security-name { type admin-string { length "1..32"; } description "The security-name type represents an SNMP security name."; reference "RFC 3411: An Architecture for Describing Simple Network Management Protocol (SNMP) Management Frameworks"; } typedef security-model { type union { type enumeration { enum v1 { value 1; } enum v2c { value 2; } enum usm { value 3; } enum tsm { value 4; } } type int32 { range "1..2147483647"; } } reference "RFC 3411: An Architecture for Describing Simple Network Management Protocol (SNMP) Management Frameworks"; } typedef security-model-or-any { type union { type enumeration { enum any { value 0; } } type security-model; } reference "RFC 3411: An Architecture for Describing Simple Network Management Protocol (SNMP) Management Frameworks"; } typedef security-level { type enumeration { enum no-auth-no-priv { value 1; } enum auth-no-priv { value 2; } enum auth-priv { value 3; } } reference "RFC 3411: An Architecture for Describing Simple Network Management Protocol (SNMP) Management Frameworks"; } typedef engine-id { type yang:hex-string { pattern '([0-9a-fA-F]){2}(:([0-9a-fA-F]){2}){4,31}'; } description "The engine ID specified as a list of colon-specified hexadecimal octets, e.g., '80:00:02:b8:04:61:62:63'."; reference "RFC 3411: An Architecture for Describing Simple Network Management Protocol (SNMP) Management Frameworks"; } typedef wildcard-object-identifier { type string; description "The wildcard-object-identifier type represents an SNMP object identifier where subidentifiers can be given either as a label, in numeric form, or a wildcard, represented by an asterisk ('*')."; } typedef tag-value { type string { length "0..255"; } description "Represents SnmpTagValue as defined in RFC 3413. Note that the size of an SnmpTagValue is measured in octets, not characters."; reference "RFC 3413: Simple Network Management Protocol (SNMP) Applications. SNMP-TARGET-MIB.SnmpTagValue"; } container snmp { description "Top-level container for SNMP-related configuration and status objects."; } } yuma123_2.14/netconf/modules/ietf/ietf-hardware@2018-03-13.yang0000664000175000017500000011071514770023131023641 0ustar vladimirvladimirmodule ietf-hardware { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-hardware"; prefix hw; import ietf-inet-types { prefix inet; } import ietf-yang-types { prefix yang; } import iana-hardware { prefix ianahw; } organization "IETF NETMOD (Network Modeling) Working Group"; contact "WG Web: WG List: Editor: Andy Bierman Editor: Martin Bjorklund Editor: Jie Dong Editor: Dan Romascanu "; description "This module contains a collection of YANG definitions for managing hardware. This data model is designed for the Network Management Datastore Architecture (NMDA) defined in RFC 8342. Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8348; see the RFC itself for full legal notices."; revision 2018-03-13 { description "Initial revision."; reference "RFC 8348: A YANG Data Model for Hardware Management"; } /* * Features */ feature entity-mib { description "This feature indicates that the device implements the ENTITY-MIB."; reference "RFC 6933: Entity MIB (Version 4)"; } feature hardware-state { description "Indicates that ENTITY-STATE-MIB objects are supported"; reference "RFC 4268: Entity State MIB"; } feature hardware-sensor { description "Indicates that ENTITY-SENSOR-MIB objects are supported"; reference "RFC 3433: Entity Sensor Management Information Base"; } /* * Typedefs */ typedef admin-state { type enumeration { enum unknown { value 1; description "The resource is unable to report administrative state."; } enum locked { value 2; description "The resource is administratively prohibited from use."; } enum shutting-down { value 3; description "The resource usage is administratively limited to current instances of use."; } enum unlocked { value 4; description "The resource is not administratively prohibited from use."; } } description "Represents the various possible administrative states."; reference "RFC 4268: Entity State MIB - EntityAdminState"; } typedef oper-state { type enumeration { enum unknown { value 1; description "The resource is unable to report its operational state."; } enum disabled { value 2; description "The resource is totally inoperable."; } enum enabled { value 3; description "The resource is partially or fully operable."; } enum testing { value 4; description "The resource is currently being tested and cannot therefore report whether or not it is operational."; } } description "Represents the possible values of operational states."; reference "RFC 4268: Entity State MIB - EntityOperState"; } typedef usage-state { type enumeration { enum unknown { value 1; description "The resource is unable to report usage state."; } enum idle { value 2; description "The resource is servicing no users."; } enum active { value 3; description "The resource is currently in use, and it has sufficient spare capacity to provide for additional users."; } enum busy { value 4; description "The resource is currently in use, but it currently has no spare capacity to provide for additional users."; } } description "Represents the possible values of usage states."; reference "RFC 4268: Entity State MIB - EntityUsageState"; } typedef alarm-state { type bits { bit unknown { position 0; description "The resource is unable to report alarm state."; } bit under-repair { position 1; description "The resource is currently being repaired, which, depending on the implementation, may make the other values in this bit string not meaningful."; } bit critical { position 2; description "One or more critical alarms are active against the resource."; } bit major { position 3; description "One or more major alarms are active against the resource."; } bit minor { position 4; description "One or more minor alarms are active against the resource."; } bit warning { position 5; description "One or more warning alarms are active against the resource."; } bit indeterminate { position 6; description "One or more alarms of whose perceived severity cannot be determined are active against this resource."; } } description "Represents the possible values of alarm states. An alarm is a persistent indication of an error or warning condition. When no bits of this attribute are set, then no active alarms are known against this component and it is not under repair."; reference "RFC 4268: Entity State MIB - EntityAlarmStatus"; } typedef standby-state { type enumeration { enum unknown { value 1; description "The resource is unable to report standby state."; } enum hot-standby { value 2; description "The resource is not providing service, but it will be immediately able to take over the role of the resource to be backed up, without the need for initialization activity, and will contain the same information as the resource to be backed up."; } enum cold-standby { value 3; description "The resource is to back up another resource, but it will not be immediately able to take over the role of a resource to be backed up and will require some initialization activity."; } enum providing-service { value 4; description "The resource is providing service."; } } description "Represents the possible values of standby states."; reference "RFC 4268: Entity State MIB - EntityStandbyStatus"; } typedef sensor-value-type { type enumeration { enum other { value 1; description "A measure other than those listed below."; } enum unknown { value 2; description "An unknown measurement or arbitrary, relative numbers"; } enum volts-AC { value 3; description "A measure of electric potential (alternating current)."; } enum volts-DC { value 4; description "A measure of electric potential (direct current)."; } enum amperes { value 5; description "A measure of electric current."; } enum watts { value 6; description "A measure of power."; } enum hertz { value 7; description "A measure of frequency."; } enum celsius { value 8; description "A measure of temperature."; } enum percent-RH { value 9; description "A measure of percent relative humidity."; } enum rpm { value 10; description "A measure of shaft revolutions per minute."; } enum cmm { value 11; description "A measure of cubic meters per minute (airflow)."; } enum truth-value { value 12; description "Value is one of 1 (true) or 2 (false)"; } } description "A node using this data type represents the sensor measurement data type associated with a physical sensor value. The actual data units are determined by examining a node of this type together with the associated sensor-value-scale node. A node of this type SHOULD be defined together with nodes of type sensor-value-scale and type sensor-value-precision. These three types are used to identify the semantics of a node of type sensor-value."; reference "RFC 3433: Entity Sensor Management Information Base - EntitySensorDataType"; } typedef sensor-value-scale { type enumeration { enum yocto { value 1; description "Data scaling factor of 10^-24."; } enum zepto { value 2; description "Data scaling factor of 10^-21."; } enum atto { value 3; description "Data scaling factor of 10^-18."; } enum femto { value 4; description "Data scaling factor of 10^-15."; } enum pico { value 5; description "Data scaling factor of 10^-12."; } enum nano { value 6; description "Data scaling factor of 10^-9."; } enum micro { value 7; description "Data scaling factor of 10^-6."; } enum milli { value 8; description "Data scaling factor of 10^-3."; } enum units { value 9; description "Data scaling factor of 10^0."; } enum kilo { value 10; description "Data scaling factor of 10^3."; } enum mega { value 11; description "Data scaling factor of 10^6."; } enum giga { value 12; description "Data scaling factor of 10^9."; } enum tera { value 13; description "Data scaling factor of 10^12."; } enum peta { value 14; description "Data scaling factor of 10^15."; } enum exa { value 15; description "Data scaling factor of 10^18."; } enum zetta { value 16; description "Data scaling factor of 10^21."; } enum yotta { value 17; description "Data scaling factor of 10^24."; } } description "A node using this data type represents a data scaling factor, represented with an International System of Units (SI) prefix. The actual data units are determined by examining a node of this type together with the associated sensor-value-type. A node of this type SHOULD be defined together with nodes of type sensor-value-type and type sensor-value-precision. Together, associated nodes of these three types are used to identify the semantics of a node of type sensor-value."; reference "RFC 3433: Entity Sensor Management Information Base - EntitySensorDataScale"; } typedef sensor-value-precision { type int8 { range "-8 .. 9"; } description "A node using this data type represents a sensor value precision range. A node of this type SHOULD be defined together with nodes of type sensor-value-type and type sensor-value-scale. Together, associated nodes of these three types are used to identify the semantics of a node of type sensor-value. If a node of this type contains a value in the range 1 to 9, it represents the number of decimal places in the fractional part of an associated sensor-value fixed-point number. If a node of this type contains a value in the range -8 to -1, it represents the number of accurate digits in the associated sensor-value fixed-point number. The value zero indicates the associated sensor-value node is not a fixed-point number. Server implementers must choose a value for the associated sensor-value-precision node so that the precision and accuracy of the associated sensor-value node is correctly indicated. For example, a component representing a temperature sensor that can measure 0 to 100 degrees C in 0.1 degree increments, +/- 0.05 degrees, would have a sensor-value-precision value of '1', a sensor-value-scale value of 'units', and a sensor-value ranging from '0' to '1000'. The sensor-value would be interpreted as 'degrees C * 10'."; reference "RFC 3433: Entity Sensor Management Information Base - EntitySensorPrecision"; } typedef sensor-value { type int32 { range "-1000000000 .. 1000000000"; } description "A node using this data type represents a sensor value. A node of this type SHOULD be defined together with nodes of type sensor-value-type, type sensor-value-scale, and type sensor-value-precision. Together, associated nodes of those three types are used to identify the semantics of a node of this data type. The semantics of a node using this data type are determined by the value of the associated sensor-value-type node. If the associated sensor-value-type node is equal to 'voltsAC', 'voltsDC', 'amperes', 'watts', 'hertz', 'celsius', or 'cmm', then a node of this type MUST contain a fixed-point number ranging from -999,999,999 to +999,999,999. The value -1000000000 indicates an underflow error. The value +1000000000 indicates an overflow error. The sensor-value-precision indicates how many fractional digits are represented in the associated sensor-value node. If the associated sensor-value-type node is equal to 'percentRH', then a node of this type MUST contain a number ranging from 0 to 100. If the associated sensor-value-type node is equal to 'rpm', then a node of this type MUST contain a number ranging from -999,999,999 to +999,999,999. If the associated sensor-value-type node is equal to 'truth-value', then a node of this type MUST contain either the value 1 (true) or the value 2 (false). If the associated sensor-value-type node is equal to 'other' or 'unknown', then a node of this type MUST contain a number ranging from -1000000000 to 1000000000."; reference "RFC 3433: Entity Sensor Management Information Base - EntitySensorValue"; } typedef sensor-status { type enumeration { enum ok { value 1; description "Indicates that the server can obtain the sensor value."; } enum unavailable { value 2; description "Indicates that the server presently cannot obtain the sensor value."; } enum nonoperational { value 3; description "Indicates that the server believes the sensor is broken. The sensor could have a hard failure (disconnected wire) or a soft failure such as out-of-range, jittery, or wildly fluctuating readings."; } } description "A node using this data type represents the operational status of a physical sensor."; reference "RFC 3433: Entity Sensor Management Information Base - EntitySensorStatus"; } /* * Data nodes */ container hardware { description "Data nodes representing components. If the server supports configuration of hardware components, then this data model is instantiated in the configuration datastores supported by the server. The leaf-list 'datastore' for the module 'ietf-hardware' in the YANG library provides this information."; leaf last-change { type yang:date-and-time; config false; description "The time the '/hardware/component' list changed in the operational state."; } list component { key name; description "List of components. When the server detects a new hardware component, it initializes a list entry in the operational state. If the server does not support configuration of hardware components, list entries in the operational state are initialized with values for all nodes as detected by the implementation. Otherwise, this procedure is followed: 1. If there is an entry in the '/hardware/component' list in the intended configuration with values for the nodes 'class', 'parent', and 'parent-rel-pos' that are equal to the detected values, then the list entry in the operational state is initialized with the configured values, including the 'name'. 2. Otherwise (i.e., there is no matching configuration entry), the list entry in the operational state is initialized with values for all nodes as detected by the implementation. If the '/hardware/component' list in the intended configuration is modified, then the system MUST behave as if it re-initializes itself and follow the procedure in (1)."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalEntry"; leaf name { type string; description "The name assigned to this component. This name is not required to be the same as entPhysicalName."; } leaf class { type identityref { base ianahw:hardware-class; } mandatory true; description "An indication of the general hardware type of the component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalClass"; } leaf physical-index { if-feature entity-mib; type int32 { range "1..2147483647"; } config false; description "The entPhysicalIndex for the entPhysicalEntry represented by this list entry."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalIndex"; } leaf description { type string; config false; description "A textual description of the component. This node should contain a string that identifies the manufacturer's name for the component and should be set to a distinct value for each version or model of the component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalDescr"; } leaf parent { type leafref { path "../../component/name"; require-instance false; } description "The name of the component that physically contains this component. If this leaf is not instantiated, it indicates that this component is not contained in any other component. In the event that a physical component is contained by more than one physical component (e.g., double-wide modules), this node contains the name of one of these components. An implementation MUST use the same name every time this node is instantiated."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalContainedIn"; } leaf parent-rel-pos { type int32 { range "0 .. 2147483647"; } description "An indication of the relative position of this child component among all its sibling components. Sibling components are defined as components that: o share the same value of the 'parent' node and o share a common base identity for the 'class' node. Note that the last rule gives implementations flexibility in how components are numbered. For example, some implementations might have a single number series for all components derived from 'ianahw:port', while some others might have different number series for different components with identities derived from 'ianahw:port' (for example, one for registered jack 45 (RJ45) and one for small form-factor pluggable (SFP))."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalParentRelPos"; } leaf-list contains-child { type leafref { path "../../component/name"; } config false; description "The name of the contained component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalChildIndex"; } leaf hardware-rev { type string; config false; description "The vendor-specific hardware revision string for the component. The preferred value is the hardware revision identifier actually printed on the component itself (if present)."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalHardwareRev"; } leaf firmware-rev { type string; config false; description "The vendor-specific firmware revision string for the component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalFirmwareRev"; } leaf software-rev { type string; config false; description "The vendor-specific software revision string for the component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalSoftwareRev"; } leaf serial-num { type string; config false; description "The vendor-specific serial number string for the component. The preferred value is the serial number string actually printed on the component itself (if present)."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalSerialNum"; } leaf mfg-name { type string; config false; description "The name of the manufacturer of this physical component. The preferred value is the manufacturer name string actually printed on the component itself (if present). Note that comparisons between instances of the 'model-name', 'firmware-rev', 'software-rev', and 'serial-num' nodes are only meaningful amongst components with the same value of 'mfg-name'. If the manufacturer name string associated with the physical component is unknown to the server, then this node is not instantiated."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalMfgName"; } leaf model-name { type string; config false; description "The vendor-specific model name identifier string associated with this physical component. The preferred value is the customer-visible part number, which may be printed on the component itself. If the model name string associated with the physical component is unknown to the server, then this node is not instantiated."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalModelName"; } leaf alias { type string; description "An 'alias' name for the component, as specified by a network manager, that provides a non-volatile 'handle' for the component. If no configured value exists, the server MAY set the value of this node to a locally unique value in the operational state. A server implementation MAY map this leaf to the entPhysicalAlias MIB object. Such an implementation needs to use some mechanism to handle the differences in size and characters allowed between this leaf and entPhysicalAlias. The definition of such a mechanism is outside the scope of this document."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalAlias"; } leaf asset-id { type string; description "This node is a user-assigned asset tracking identifier for the component. A server implementation MAY map this leaf to the entPhysicalAssetID MIB object. Such an implementation needs to use some mechanism to handle the differences in size and characters allowed between this leaf and entPhysicalAssetID. The definition of such a mechanism is outside the scope of this document."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalAssetID"; } leaf is-fru { type boolean; config false; description "This node indicates whether or not this component is considered a 'field-replaceable unit' by the vendor. If this node contains the value 'true', then this component identifies a field-replaceable unit. For all components that are permanently contained within a field-replaceable unit, the value 'false' should be returned for this node."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalIsFRU"; } leaf mfg-date { type yang:date-and-time; config false; description "The date of manufacturing of the managed component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalMfgDate"; } leaf-list uri { type inet:uri; description "This node contains identification information about the component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalUris"; } leaf uuid { type yang:uuid; config false; description "A Universally Unique Identifier of the component."; reference "RFC 6933: Entity MIB (Version 4) - entPhysicalUUID"; } container state { if-feature hardware-state; description "State-related nodes"; reference "RFC 4268: Entity State MIB"; leaf state-last-changed { type yang:date-and-time; config false; description "The date and time when the value of any of the admin-state, oper-state, usage-state, alarm-state, or standby-state changed for this component. If there has been no change since the last re-initialization of the local system, this node contains the date and time of local system initialization. If there has been no change since the component was added to the local system, this node contains the date and time of the insertion."; reference "RFC 4268: Entity State MIB - entStateLastChanged"; } leaf admin-state { type admin-state; description "The administrative state for this component. This node refers to a component's administrative permission to service both other components within its containment hierarchy as well other users of its services defined by means outside the scope of this module. Some components exhibit only a subset of the remaining administrative state values. Some components cannot be locked; hence, this node exhibits only the 'unlocked' state. Other components cannot be shut down gracefully; hence, this node does not exhibit the 'shutting-down' state."; reference "RFC 4268: Entity State MIB - entStateAdmin"; } leaf oper-state { type oper-state; config false; description "The operational state for this component. Note that this node does not follow the administrative state. An administrative state of 'down' does not predict an operational state of 'disabled'. Note that some implementations may not be able to accurately report oper-state while the admin-state node has a value other than 'unlocked'. In these cases, this node MUST have a value of 'unknown'."; reference "RFC 4268: Entity State MIB - entStateOper"; } leaf usage-state { type usage-state; config false; description "The usage state for this component. This node refers to a component's ability to service more components in a containment hierarchy. Some components will exhibit only a subset of the usage state values. Components that are unable to ever service any components within a containment hierarchy will always have a usage state of 'busy'. In some cases, a component will be able to support only one other component within its containment hierarchy and will therefore only exhibit values of 'idle' and 'busy'."; reference "RFC 4268: Entity State MIB - entStateUsage"; } leaf alarm-state { type alarm-state; config false; description "The alarm state for this component. It does not include the alarms raised on child components within its containment hierarchy."; reference "RFC 4268: Entity State MIB - entStateAlarm"; } leaf standby-state { type standby-state; config false; description "The standby state for this component. Some components will exhibit only a subset of the remaining standby state values. If this component cannot operate in a standby role, the value of this node will always be 'providing-service'."; reference "RFC 4268: Entity State MIB - entStateStandby"; } } container sensor-data { when 'derived-from-or-self(../class, "ianahw:sensor")' { description "Sensor data nodes present for any component of type 'sensor'"; } if-feature hardware-sensor; config false; description "Sensor-related nodes."; reference "RFC 3433: Entity Sensor Management Information Base"; leaf value { type sensor-value; description "The most recent measurement obtained by the server for this sensor. A client that periodically fetches this node should also fetch the nodes 'value-type', 'value-scale', and 'value-precision', since they may change when the value is changed."; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorValue"; } leaf value-type { type sensor-value-type; description "The type of data units associated with the sensor value"; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorType"; } leaf value-scale { type sensor-value-scale; description "The (power of 10) scaling factor associated with the sensor value"; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorScale"; } leaf value-precision { type sensor-value-precision; description "The number of decimal places of precision associated with the sensor value"; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorPrecision"; } leaf oper-status { type sensor-status; description "The operational status of the sensor."; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorOperStatus"; } leaf units-display { type string; description "A textual description of the data units that should be used in the display of the sensor value."; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorUnitsDisplay"; } leaf value-timestamp { type yang:date-and-time; description "The time the status and/or value of this sensor was last obtained by the server."; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorValueTimeStamp"; } leaf value-update-rate { type uint32; units "milliseconds"; description "An indication of the frequency that the server updates the associated 'value' node, represented in milliseconds. The value zero indicates: - the sensor value is updated on demand (e.g., when polled by the server for a get-request), - the sensor value is updated when the sensor value changes (event-driven), or - the server does not know the update rate."; reference "RFC 3433: Entity Sensor Management Information Base - entPhySensorValueUpdateRate"; } } } } /* * Notifications */ notification hardware-state-change { description "A hardware-state-change notification is generated when the value of /hardware/last-change changes in the operational state."; reference "RFC 6933: Entity MIB (Version 4) - entConfigChange"; } notification hardware-state-oper-enabled { if-feature hardware-state; description "A hardware-state-oper-enabled notification signifies that a component has transitioned into the 'enabled' state."; leaf name { type leafref { path "/hardware/component/name"; } description "The name of the component that has transitioned into the 'enabled' state."; } leaf admin-state { type leafref { path "/hardware/component/state/admin-state"; } description "The administrative state for the component."; } leaf alarm-state { type leafref { path "/hardware/component/state/alarm-state"; } description "The alarm state for the component."; } reference "RFC 4268: Entity State MIB - entStateOperEnabled"; } notification hardware-state-oper-disabled { if-feature hardware-state; description "A hardware-state-oper-disabled notification signifies that a component has transitioned into the 'disabled' state."; leaf name { type leafref { path "/hardware/component/name"; } description "The name of the component that has transitioned into the 'disabled' state."; } leaf admin-state { type leafref { path "/hardware/component/state/admin-state"; } description "The administrative state for the component."; } leaf alarm-state { type leafref { path "/hardware/component/state/alarm-state"; } description "The alarm state for the component."; } reference "RFC 4268: Entity State MIB - entStateOperDisabled"; } } yuma123_2.14/netconf/modules/ietf/ietf-network-state@2018-02-26.yang0000664000175000017500000001262214770023131024654 0ustar vladimirvladimirmodule ietf-network-state { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-network-state"; prefix nw-s; import ietf-network { prefix nw; reference "RFC 8345: A YANG Data Model for Network Topologies"; } organization "IETF I2RS (Interface to the Routing System) Working Group"; contact "WG Web: WG List: Editor: Alexander Clemm Editor: Jan Medved Editor: Robert Varga Editor: Nitin Bahadur Editor: Hariharan Ananthakrishnan Editor: Xufeng Liu "; description "This module defines a common base data model for a collection of nodes in a network. Node definitions are further used in network topologies and inventories. It represents information that either (1) is learned and automatically populated or (2) results from applying network information that has been configured per the 'ietf-network' data model, mirroring the corresponding data nodes in this data model. The data model mirrors 'ietf-network' but contains only read-only state data. The data model is not needed when the underlying implementation infrastructure supports the Network Management Datastore Architecture (NMDA). Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8345; see the RFC itself for full legal notices."; revision 2018-02-26 { description "Initial revision."; reference "RFC 8345: A YANG Data Model for Network Topologies"; } grouping network-ref { description "Contains the information necessary to reference a network -- for example, an underlay network."; leaf network-ref { type leafref { path "/nw-s:networks/nw-s:network/nw-s:network-id"; require-instance false; } description "Used to reference a network -- for example, an underlay network."; } } grouping node-ref { description "Contains the information necessary to reference a node."; leaf node-ref { type leafref { path "/nw-s:networks/nw-s:network[nw-s:network-id=current()"+ "/../network-ref]/nw-s:node/nw-s:node-id"; require-instance false; } description "Used to reference a node. Nodes are identified relative to the network that contains them."; } uses network-ref; } container networks { config false; description "Serves as a top-level container for a list of networks."; list network { key "network-id"; description "Describes a network. A network typically contains an inventory of nodes, topological information (augmented through the network-topology data model), and layering information."; container network-types { description "Serves as an augmentation target. The network type is indicated through corresponding presence containers augmented into this container."; } leaf network-id { type nw:network-id; description "Identifies a network."; } list supporting-network { key "network-ref"; description "An underlay network, used to represent layered network topologies."; leaf network-ref { type leafref { path "/nw-s:networks/nw-s:network/nw-s:network-id"; require-instance false; } description "References the underlay network."; } } list node { key "node-id"; description "The inventory of nodes of this network."; leaf node-id { type nw:node-id; description "Uniquely identifies a node within the containing network."; } list supporting-node { key "network-ref node-ref"; description "Represents another node that is in an underlay network and that supports this node. Used to represent layering structure."; leaf network-ref { type leafref { path "../../../nw-s:supporting-network/nw-s:network-ref"; require-instance false; } description "References the underlay network of which the underlay node is a part."; } leaf node-ref { type leafref { path "/nw-s:networks/nw-s:network/nw-s:node/nw-s:node-id"; require-instance false; } description "References the underlay node itself."; } } } } } } yuma123_2.14/netconf/modules/ietf/ietf-ipv4-unicast-routing@2018-03-13.yang0000664000175000017500000002653214770023131026062 0ustar vladimirvladimirmodule ietf-ipv4-unicast-routing { yang-version "1.1"; namespace "urn:ietf:params:xml:ns:yang:ietf-ipv4-unicast-routing"; prefix "v4ur"; import ietf-routing { prefix "rt"; description "An 'ietf-routing' module version that is compatible with the Network Management Datastore Architecture (NMDA) is required."; } import ietf-inet-types { prefix "inet"; } organization "IETF NETMOD (Network Modeling) Working Group"; contact "WG Web: WG List: Editor: Ladislav Lhotka Acee Lindem Yingzhen Qu "; description "This YANG module augments the 'ietf-routing' module with basic parameters for IPv4 unicast routing. The model fully conforms to the Network Management Datastore Architecture (NMDA). Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8349; see the RFC itself for full legal notices."; revision 2018-03-13 { description "Network Management Datastore Architecture (NMDA) revision."; reference "RFC 8349: A YANG Data Model for Routing Management (NMDA Version)"; } revision 2016-11-04 { description "Initial revision."; reference "RFC 8022: A YANG Data Model for Routing Management"; } /* Identities */ identity ipv4-unicast { base rt:ipv4; description "This identity represents the IPv4 unicast address family."; } augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route" { when "derived-from-or-self(../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } description "This leaf augments an IPv4 unicast route."; leaf destination-prefix { type inet:ipv4-prefix; description "IPv4 destination prefix."; } } augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/" + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" { when "derived-from-or-self(../../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } description "Augments the 'simple-next-hop' case in IPv4 unicast routes."; leaf next-hop-address { type inet:ipv4-address; description "IPv4 address of the next hop."; } } augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/" + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/" + "rt:next-hop-list/rt:next-hop" { when "derived-from-or-self(../../../../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } description "This leaf augments the 'next-hop-list' case of IPv4 unicast routes."; leaf address { type inet:ipv4-address; description "IPv4 address of the next hop."; } } augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/rt:input" { when "derived-from-or-self(../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast RIBs."; } description "This augment adds the input parameter of the 'active-route' action."; leaf destination-address { type inet:ipv4-address; description "IPv4 destination address."; } } augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route" { when "derived-from-or-self(../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } description "This augment adds the destination prefix to the reply of the 'active-route' action."; leaf destination-prefix { type inet:ipv4-prefix; description "IPv4 destination prefix."; } } augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" + "rt:simple-next-hop" { when "derived-from-or-self(../../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } description "Augments the 'simple-next-hop' case in the reply to the 'active-route' action."; leaf next-hop-address { type inet:ipv4-address; description "IPv4 address of the next hop."; } } augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" { when "derived-from-or-self(../../../../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } description "Augments the 'next-hop-list' case in the reply to the 'active-route' action."; leaf next-hop-address { type inet:ipv4-address; description "IPv4 address of the next hop."; } } augment "/rt:routing/rt:control-plane-protocols/" + "rt:control-plane-protocol/rt:static-routes" { description "This augment defines the 'static' pseudo-protocol with data specific to IPv4 unicast."; container ipv4 { description "Support for a 'static' pseudo-protocol instance consists of a list of routes."; list route { key "destination-prefix"; description "A list of static routes."; leaf destination-prefix { type inet:ipv4-prefix; mandatory true; description "IPv4 destination prefix."; } leaf description { type string; description "Textual description of the route."; } container next-hop { description "Support for next-hop."; uses rt:next-hop-content { augment "next-hop-options/simple-next-hop" { description "Augments the 'simple-next-hop' case in IPv4 static routes."; leaf next-hop-address { type inet:ipv4-address; description "IPv4 address of the next hop."; } } augment "next-hop-options/next-hop-list/next-hop-list/" + "next-hop" { description "Augments the 'next-hop-list' case in IPv4 static routes."; leaf next-hop-address { type inet:ipv4-address; description "IPv4 address of the next hop."; } } } } } } } /* * The subsequent data nodes are obviated and obsoleted * by the Network Management Datastore Architecture * as described in RFC 8342. */ augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route" { when "derived-from-or-self(../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } status obsolete; description "This leaf augments an IPv4 unicast route."; leaf destination-prefix { type inet:ipv4-prefix; status obsolete; description "IPv4 destination prefix."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/" + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" { when "derived-from-or-self( ../../../rt:address-family, 'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } status obsolete; description "Augments the 'simple-next-hop' case in IPv4 unicast routes."; leaf next-hop-address { type inet:ipv4-address; status obsolete; description "IPv4 address of the next hop."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/" + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/" + "rt:next-hop-list/rt:next-hop" { when "derived-from-or-self(../../../../../rt:address-family, 'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } status obsolete; description "This leaf augments the 'next-hop-list' case of IPv4 unicast routes."; leaf address { type inet:ipv4-address; status obsolete; description "IPv4 address of the next hop."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:input" { when "derived-from-or-self(../rt:address-family, 'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast RIBs."; } status obsolete; description "This augment adds the input parameter of the 'active-route' action."; leaf destination-address { type inet:ipv4-address; status obsolete; description "IPv4 destination address."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route" { when "derived-from-or-self(../../rt:address-family, 'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } status obsolete; description "This augment adds the destination prefix to the reply of the 'active-route' action."; leaf destination-prefix { type inet:ipv4-prefix; status obsolete; description "IPv4 destination prefix."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" + "rt:simple-next-hop" { when "derived-from-or-self(../../../rt:address-family, 'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } status obsolete; description "Augments the 'simple-next-hop' case in the reply to the 'active-route' action."; leaf next-hop-address { type inet:ipv4-address; status obsolete; description "IPv4 address of the next hop."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" { when "derived-from-or-self(../../../../../rt:address-family, 'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } status obsolete; description "Augments the 'next-hop-list' case in the reply to the 'active-route' action."; leaf next-hop-address { type inet:ipv4-address; status obsolete; description "IPv4 address of the next hop."; } } } yuma123_2.14/netconf/modules/ietf/iana-crypt-hash@2014-08-06.yang0000664000175000017500000001017514770023131024111 0ustar vladimirvladimirmodule iana-crypt-hash { namespace "urn:ietf:params:xml:ns:yang:iana-crypt-hash"; prefix ianach; organization "IANA"; contact " Internet Assigned Numbers Authority Postal: ICANN 12025 Waterfront Drive, Suite 300 Los Angeles, CA 90094-2536 United States Tel: +1 310 301 5800 E-Mail: iana@iana.org>"; description "This YANG module defines a type for storing passwords using a hash function and features to indicate which hash functions are supported by an implementation. The latest revision of this YANG module can be obtained from the IANA web site. Requests for new values should be made to IANA via email (iana@iana.org). Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). The initial version of this YANG module is part of RFC 7317; see the RFC itself for full legal notices."; revision 2014-08-06 { description "Initial revision."; reference "RFC 7317: A YANG Data Model for System Management"; } typedef crypt-hash { type string { pattern '$0$.*' + '|$1$[a-zA-Z0-9./]{1,8}$[a-zA-Z0-9./]{22}' + '|$5$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{43}' + '|$6$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{86}'; } description "The crypt-hash type is used to store passwords using a hash function. The algorithms for applying the hash function and encoding the result are implemented in various UNIX systems as the function crypt(3). A value of this type matches one of the forms: $0$ $$$ $$$$ The '$0$' prefix signals that the value is clear text. When such a value is received by the server, a hash value is calculated, and the string '$$$' or $$$$ is prepended to the result. This value is stored in the configuration data store. If a value starting with '$$', where is not '0', is received, the server knows that the value already represents a hashed value and stores it 'as is' in the data store. When a server needs to verify a password given by a user, it finds the stored password hash string for that user, extracts the salt, and calculates the hash with the salt and given password as input. If the calculated hash value is the same as the stored value, the password given by the client is accepted. This type defines the following hash functions: id | hash function | feature ---+---------------+------------------- 1 | MD5 | crypt-hash-md5 5 | SHA-256 | crypt-hash-sha-256 6 | SHA-512 | crypt-hash-sha-512 The server indicates support for the different hash functions by advertising the corresponding feature."; reference "IEEE Std 1003.1-2008 - crypt() function RFC 1321: The MD5 Message-Digest Algorithm FIPS.180-4.2012: Secure Hash Standard (SHS)"; } feature crypt-hash-md5 { description "Indicates that the device supports the MD5 hash function in 'crypt-hash' values."; reference "RFC 1321: The MD5 Message-Digest Algorithm"; } feature crypt-hash-sha-256 { description "Indicates that the device supports the SHA-256 hash function in 'crypt-hash' values."; reference "FIPS.180-4.2012: Secure Hash Standard (SHS)"; } feature crypt-hash-sha-512 { description "Indicates that the device supports the SHA-512 hash function in 'crypt-hash' values."; reference "FIPS.180-4.2012: Secure Hash Standard (SHS)"; } } yuma123_2.14/netconf/modules/ietf/ietf-geo-location@2022-02-11.yang0000664000175000017500000002334114770023131024412 0ustar vladimirvladimirmodule ietf-geo-location { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-geo-location"; prefix geo; import ietf-yang-types { prefix yang; reference "RFC 6991: Common YANG Data Types"; } organization "IETF NETMOD Working Group (NETMOD)"; contact "WG Web: WG List: Editor: Christian Hopps "; description "This module defines a grouping of a container object for specifying a location on or around an astronomical object (e.g., 'earth'). The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here. Copyright (c) 2022 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 9179 (https://www.rfc-editor.org/info/rfc9179); see the RFC itself for full legal notices."; revision 2022-02-11 { description "Initial Revision"; reference "RFC 9179: A YANG Grouping for Geographic Locations"; } feature alternate-systems { description "This feature means the device supports specifying locations using alternate systems for reference frames."; } grouping geo-location { description "Grouping to identify a location on an astronomical object."; container geo-location { description "A location on an astronomical body (e.g., 'earth') somewhere in a universe."; container reference-frame { description "The Frame of Reference for the location values."; leaf alternate-system { if-feature "alternate-systems"; type string; description "The system in which the astronomical body and geodetic-datum is defined. Normally, this value is not present and the system is the natural universe; however, when present, this value allows for specifying alternate systems (e.g., virtual realities). An alternate-system modifies the definition (but not the type) of the other values in the reference frame."; } leaf astronomical-body { type string { pattern '[ -@\[-\^_-~]*'; } default "earth"; description "An astronomical body as named by the International Astronomical Union (IAU) or according to the alternate system if specified. Examples include 'sun' (our star), 'earth' (our planet), 'moon' (our moon), 'enceladus' (a moon of Saturn), 'ceres' (an asteroid), and '67p/churyumov-gerasimenko (a comet). The ASCII value SHOULD have uppercase converted to lowercase and not include control characters (i.e., values 32..64, and 91..126). Any preceding 'the' in the name SHOULD NOT be included."; reference "https://www.iau.org/"; } container geodetic-system { description "The geodetic system of the location data."; leaf geodetic-datum { type string { pattern '[ -@\[-\^_-~]*'; } description "A geodetic-datum defining the meaning of latitude, longitude, and height. The default when the astronomical body is 'earth' is 'wgs-84', which is used by the Global Positioning System (GPS). The ASCII value SHOULD have uppercase converted to lowercase and not include control characters (i.e., values 32..64, and 91..126). The IANA registry further restricts the value by converting all spaces (' ') to dashes ('-'). The specification for the geodetic-datum indicates how accurately it models the astronomical body in question, both for the 'horizontal' latitude/longitude coordinates and for height coordinates."; reference "RFC 9179: A YANG Grouping for Geographic Locations, Section 6.1"; } leaf coord-accuracy { type decimal64 { fraction-digits 6; } description "The accuracy of the latitude/longitude pair for ellipsoidal coordinates, or the X, Y, and Z components for Cartesian coordinates. When coord-accuracy is specified, it indicates how precisely the coordinates in the associated list of locations have been determined with respect to the coordinate system defined by the geodetic-datum. For example, there might be uncertainty due to measurement error if an experimental measurement was made to determine each location."; } leaf height-accuracy { type decimal64 { fraction-digits 6; } units "meters"; description "The accuracy of the height value for ellipsoidal coordinates; this value is not used with Cartesian coordinates. When height-accuracy is specified, it indicates how precisely the heights in the associated list of locations have been determined with respect to the coordinate system defined by the geodetic-datum. For example, there might be uncertainty due to measurement error if an experimental measurement was made to determine each location."; } } } choice location { description "The location data either in latitude/longitude or Cartesian values"; case ellipsoid { leaf latitude { type decimal64 { fraction-digits 16; } units "decimal degrees"; description "The latitude value on the astronomical body. The definition and precision of this measurement is indicated by the reference-frame."; } leaf longitude { type decimal64 { fraction-digits 16; } units "decimal degrees"; description "The longitude value on the astronomical body. The definition and precision of this measurement is indicated by the reference-frame."; } leaf height { type decimal64 { fraction-digits 6; } units "meters"; description "Height from a reference 0 value. The precision and '0' value is defined by the reference-frame."; } } case cartesian { leaf x { type decimal64 { fraction-digits 6; } units "meters"; description "The X value as defined by the reference-frame."; } leaf y { type decimal64 { fraction-digits 6; } units "meters"; description "The Y value as defined by the reference-frame."; } leaf z { type decimal64 { fraction-digits 6; } units "meters"; description "The Z value as defined by the reference-frame."; } } } container velocity { description "If the object is in motion, the velocity vector describes this motion at the time given by the timestamp. For a formula to convert these values to speed and heading, see RFC 9179."; reference "RFC 9179: A YANG Grouping for Geographic Locations"; leaf v-north { type decimal64 { fraction-digits 12; } units "meters per second"; description "v-north is the rate of change (i.e., speed) towards true north as defined by the geodetic-system."; } leaf v-east { type decimal64 { fraction-digits 12; } units "meters per second"; description "v-east is the rate of change (i.e., speed) perpendicular to the right of true north as defined by the geodetic-system."; } leaf v-up { type decimal64 { fraction-digits 12; } units "meters per second"; description "v-up is the rate of change (i.e., speed) away from the center of mass."; } } leaf timestamp { type yang:date-and-time; description "Reference time when location was recorded."; } leaf valid-until { type yang:date-and-time; description "The timestamp for which this geo-location is valid until. If unspecified, the geo-location has no specific expiration time."; } } } } yuma123_2.14/netconf/modules/ietf/ietf-ipv6-router-advertisements@2016-11-04.yang0000664000175000017500000003740214770023131027277 0ustar vladimirvladimirsubmodule ietf-ipv6-router-advertisements { yang-version "1.1"; belongs-to ietf-ipv6-unicast-routing { prefix "v6ur"; } import ietf-inet-types { prefix "inet"; } import ietf-interfaces { prefix "if"; } import ietf-ip { prefix "ip"; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Lou Berger WG Chair: Kent Watsen Editor: Ladislav Lhotka Editor: Acee Lindem "; description "This YANG module augments the 'ietf-ip' module with configuration and state data of IPv6 router advertisements. Copyright (c) 2016 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'MAY', and 'OPTIONAL' in the module text are to be interpreted as described in RFC 2119. This version of this YANG module is part of RFC 8022; see the RFC itself for full legal notices."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)."; revision 2016-11-04 { description "Initial revision."; reference "RFC 8022: A YANG Data Model for Routing Management"; } /* State data */ augment "/if:interfaces-state/if:interface/ip:ipv6" { description "Augment interface state data with parameters of IPv6 router advertisements."; container ipv6-router-advertisements { description "Parameters of IPv6 Router Advertisements."; leaf send-advertisements { type boolean; description "A flag indicating whether or not the router sends periodic Router Advertisements and responds to Router Solicitations."; } leaf max-rtr-adv-interval { type uint16 { range "4..1800"; } units "seconds"; description "The maximum time allowed between sending unsolicited multicast Router Advertisements from the interface."; } leaf min-rtr-adv-interval { type uint16 { range "3..1350"; } units "seconds"; description "The minimum time allowed between sending unsolicited multicast Router Advertisements from the interface."; } leaf managed-flag { type boolean; description "The value that is placed in the 'Managed address configuration' flag field in the Router Advertisement."; } leaf other-config-flag { type boolean; description "The value that is placed in the 'Other configuration' flag field in the Router Advertisement."; } leaf link-mtu { type uint32; description "The value that is placed in MTU options sent by the router. A value of zero indicates that no MTU options are sent."; } leaf reachable-time { type uint32 { range "0..3600000"; } units "milliseconds"; description "The value that is placed in the Reachable Time field in the Router Advertisement messages sent by the router. A value of zero means unspecified (by this router)."; } leaf retrans-timer { type uint32; units "milliseconds"; description "The value that is placed in the Retrans Timer field in the Router Advertisement messages sent by the router. A value of zero means unspecified (by this router)."; } leaf cur-hop-limit { type uint8; description "The value that is placed in the Cur Hop Limit field in the Router Advertisement messages sent by the router. A value of zero means unspecified (by this router)."; } leaf default-lifetime { type uint16 { range "0..9000"; } units "seconds"; description "The value that is placed in the Router Lifetime field of Router Advertisements sent from the interface, in seconds. A value of zero indicates that the router is not to be used as a default router."; } container prefix-list { description "A list of prefixes that are placed in Prefix Information options in Router Advertisement messages sent from the interface. By default, these are all prefixes that the router advertises via routing protocols as being on-link for the interface from which the advertisement is sent."; list prefix { key "prefix-spec"; description "Advertised prefix entry and its parameters."; leaf prefix-spec { type inet:ipv6-prefix; description "IPv6 address prefix."; } leaf valid-lifetime { type uint32; units "seconds"; description "The value that is placed in the Valid Lifetime in the Prefix Information option. The designated value of all 1's (0xffffffff) represents infinity. An implementation SHOULD keep this value constant in consecutive advertisements except when it is explicitly changed in configuration."; } leaf on-link-flag { type boolean; description "The value that is placed in the on-link flag ('L-bit') field in the Prefix Information option."; } leaf preferred-lifetime { type uint32; units "seconds"; description "The value that is placed in the Preferred Lifetime in the Prefix Information option, in seconds. The designated value of all 1's (0xffffffff) represents infinity. An implementation SHOULD keep this value constant in consecutive advertisements except when it is explicitly changed in configuration."; } leaf autonomous-flag { type boolean; description "The value that is placed in the Autonomous Flag field in the Prefix Information option."; } } } } } /* Configuration data */ augment "/if:interfaces/if:interface/ip:ipv6" { description "Augment interface configuration with parameters of IPv6 router advertisements."; container ipv6-router-advertisements { description "Configuration of IPv6 Router Advertisements."; leaf send-advertisements { type boolean; default "false"; description "A flag indicating whether or not the router sends periodic Router Advertisements and responds to Router Solicitations."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvSendAdvertisements."; } leaf max-rtr-adv-interval { type uint16 { range "4..1800"; } units "seconds"; default "600"; description "The maximum time allowed between sending unsolicited multicast Router Advertisements from the interface."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - MaxRtrAdvInterval."; } leaf min-rtr-adv-interval { type uint16 { range "3..1350"; } units "seconds"; must ". <= 0.75 * ../max-rtr-adv-interval" { description "The value MUST NOT be greater than 75% of 'max-rtr-adv-interval'."; } description "The minimum time allowed between sending unsolicited multicast Router Advertisements from the interface. The default value to be used operationally if this leaf is not configured is determined as follows: - if max-rtr-adv-interval >= 9 seconds, the default value is 0.33 * max-rtr-adv-interval; - otherwise, it is 0.75 * max-rtr-adv-interval."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - MinRtrAdvInterval."; } leaf managed-flag { type boolean; default "false"; description "The value to be placed in the 'Managed address configuration' flag field in the Router Advertisement."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvManagedFlag."; } leaf other-config-flag { type boolean; default "false"; description "The value to be placed in the 'Other configuration' flag field in the Router Advertisement."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvOtherConfigFlag."; } leaf link-mtu { type uint32; default "0"; description "The value to be placed in MTU options sent by the router. A value of zero indicates that no MTU options are sent."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvLinkMTU."; } leaf reachable-time { type uint32 { range "0..3600000"; } units "milliseconds"; default "0"; description "The value to be placed in the Reachable Time field in the Router Advertisement messages sent by the router. A value of zero means unspecified (by this router)."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvReachableTime."; } leaf retrans-timer { type uint32; units "milliseconds"; default "0"; description "The value to be placed in the Retrans Timer field in the Router Advertisement messages sent by the router. A value of zero means unspecified (by this router)."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvRetransTimer."; } leaf cur-hop-limit { type uint8; description "The value to be placed in the Cur Hop Limit field in the Router Advertisement messages sent by the router. A value of zero means unspecified (by this router). If this parameter is not configured, the device SHOULD use the value specified in IANA Assigned Numbers that was in effect at the time of implementation."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvCurHopLimit. IANA: IP Parameters, http://www.iana.org/assignments/ip-parameters"; } leaf default-lifetime { type uint16 { range "0..9000"; } units "seconds"; description "The value to be placed in the Router Lifetime field of Router Advertisements sent from the interface, in seconds. It MUST be either zero or between max-rtr-adv-interval and 9000 seconds. A value of zero indicates that the router is not to be used as a default router. These limits may be overridden by specific documents that describe how IPv6 operates over different link layers. If this parameter is not configured, the device SHOULD use a value of 3 * max-rtr-adv-interval."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvDefaultLifeTime."; } container prefix-list { description "Configuration of prefixes to be placed in Prefix Information options in Router Advertisement messages sent from the interface. Prefixes that are advertised by default but do not have their entries in the child 'prefix' list are advertised with the default values of all parameters. The link-local prefix SHOULD NOT be included in the list of advertised prefixes."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvPrefixList."; list prefix { key "prefix-spec"; description "Configuration of an advertised prefix entry."; leaf prefix-spec { type inet:ipv6-prefix; description "IPv6 address prefix."; } choice control-adv-prefixes { default "advertise"; description "Either the prefix is explicitly removed from the set of advertised prefixes, or the parameters with which it is advertised are specified (default case)."; leaf no-advertise { type empty; description "The prefix will not be advertised. This can be used for removing the prefix from the default set of advertised prefixes."; } case advertise { leaf valid-lifetime { type uint32; units "seconds"; default "2592000"; description "The value to be placed in the Valid Lifetime in the Prefix Information option. The designated value of all 1's (0xffffffff) represents infinity."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvValidLifetime."; } leaf on-link-flag { type boolean; default "true"; description "The value to be placed in the on-link flag ('L-bit') field in the Prefix Information option."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvOnLinkFlag."; } leaf preferred-lifetime { type uint32; units "seconds"; must ". <= ../valid-lifetime" { description "This value MUST NOT be greater than valid-lifetime."; } default "604800"; description "The value to be placed in the Preferred Lifetime in the Prefix Information option. The designated value of all 1's (0xffffffff) represents infinity."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvPreferredLifetime."; } leaf autonomous-flag { type boolean; default "true"; description "The value to be placed in the Autonomous Flag field in the Prefix Information option."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvAutonomousFlag."; } } } } } } } } yuma123_2.14/netconf/modules/ietf/ietf-snmp@2014-12-10.yang0000664000175000017500000000412314770023131023005 0ustar vladimirvladimirmodule ietf-snmp { namespace "urn:ietf:params:xml:ns:yang:ietf-snmp"; prefix snmp; include ietf-snmp-common { revision-date 2014-12-10; } include ietf-snmp-engine { revision-date 2014-12-10; } include ietf-snmp-target { revision-date 2014-12-10; } include ietf-snmp-notification { revision-date 2014-12-10; } include ietf-snmp-proxy { revision-date 2014-12-10; } include ietf-snmp-community { revision-date 2014-12-10; } include ietf-snmp-usm { revision-date 2014-12-10; } include ietf-snmp-tsm { revision-date 2014-12-10; } include ietf-snmp-vacm { revision-date 2014-12-10; } include ietf-snmp-tls { revision-date 2014-12-10; } include ietf-snmp-ssh { revision-date 2014-12-10; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This module contains a collection of YANG definitions for configuring SNMP engines. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } } yuma123_2.14/netconf/modules/ietf/ietf-snmp-proxy@2014-12-10.yang0000664000175000017500000001107714770023131024172 0ustar vladimirvladimirsubmodule ietf-snmp-proxy { belongs-to ietf-snmp { prefix snmp; } include ietf-snmp-common; include ietf-snmp-target; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This submodule contains a collection of YANG definitions for configuring SNMP proxies. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; reference "RFC 3413: Simple Network Management Protocol (SNMP) Applications"; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } feature proxy { description "A server implements this feature if it can act as an SNMP proxy."; reference "RFC 3413: Simple Network Management Protocol (SNMP) Applications"; } augment /snmp:snmp { if-feature snmp:proxy; list proxy { key name; description "List of proxy parameters."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-PROXY-MIB.snmpProxyTable"; leaf name { type snmp:identifier; description "Identifies the proxy parameter entry."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-PROXY-MIB.snmpProxyName"; } leaf type { type enumeration { enum read { value 1; } enum write { value 2; } enum trap { value 3; } enum inform { value 4; } } mandatory true; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-PROXY-MIB.snmpProxyType"; } leaf context-engine-id { type snmp:engine-id; mandatory true; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-PROXY-MIB.snmpProxyContextEngineID"; } leaf context-name { type snmp:context-name; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-PROXY-MIB.snmpProxyContextName"; } leaf target-params-in { type snmp:identifier; description "The name of a target parameters list entry. Implementations MAY restrict the values of this leaf to be one of the available values of /snmp/target-params/name in a valid configuration."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-PROXY-MIB.snmpProxyTargetParamsIn"; } leaf single-target-out { when "../type = 'read' or ../type = 'write'"; type snmp:identifier; description "Implementations MAY restrict the values of this leaf to be one of the available values of /snmp/target/name in a valid configuration."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-PROXY-MIB.snmpProxySingleTargetOut"; } leaf multiple-target-out { when "../type = 'trap' or ../type = 'inform'"; type snmp:tag-value; description "Implementations MAY restrict the values of this leaf to be one of the available values of /snmp/target/tag in a valid configuration."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-PROXY-MIB.snmpProxyMultipleTargetOut"; } } } } yuma123_2.14/netconf/modules/ietf/ietf-ipv6-unicast-routing@2018-03-13.yang0000664000175000017500000002715614770023131026067 0ustar vladimirvladimirmodule ietf-ipv6-unicast-routing { yang-version "1.1"; namespace "urn:ietf:params:xml:ns:yang:ietf-ipv6-unicast-routing"; prefix "v6ur"; import ietf-routing { prefix "rt"; description "An 'ietf-routing' module version that is compatible with the Network Management Datastore Architecture (NMDA) is required."; } import ietf-inet-types { prefix "inet"; description "An 'ietf-interfaces' module version that is compatible with the Network Management Datastore Architecture (NMDA) is required."; } include ietf-ipv6-router-advertisements { revision-date 2018-03-13; } organization "IETF NETMOD (Network Modeling) Working Group"; contact "WG Web: WG List: Editor: Ladislav Lhotka Acee Lindem Yingzhen Qu "; description "This YANG module augments the 'ietf-routing' module with basic parameters for IPv6 unicast routing. The model fully conforms to the Network Management Datastore Architecture (NMDA). Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8349; see the RFC itself for full legal notices."; revision 2018-03-13 { description "Network Management Datastore Architecture (NMDA) revision."; reference "RFC 8349: A YANG Data Model for Routing Management (NMDA Version)"; } /* Identities */ revision 2016-11-04 { description "Initial revision."; reference "RFC 8022: A YANG Data Model for Routing Management"; } identity ipv6-unicast { base rt:ipv6; description "This identity represents the IPv6 unicast address family."; } augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route" { when "derived-from-or-self(../../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } description "This leaf augments an IPv6 unicast route."; leaf destination-prefix { type inet:ipv6-prefix; description "IPv6 destination prefix."; } } augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/" + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" { when "derived-from-or-self(../../../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } description "Augments the 'simple-next-hop' case in IPv6 unicast routes."; leaf next-hop-address { type inet:ipv6-address; description "IPv6 address of the next hop."; } } augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/" + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/" + "rt:next-hop-list/rt:next-hop" { when "derived-from-or-self(../../../../../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } description "This leaf augments the 'next-hop-list' case of IPv6 unicast routes."; leaf address { type inet:ipv6-address; description "IPv6 address of the next hop."; } } augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/rt:input" { when "derived-from-or-self(../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast RIBs."; } description "This augment adds the input parameter of the 'active-route' action."; leaf destination-address { type inet:ipv6-address; description "IPv6 destination address."; } } augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route" { when "derived-from-or-self(../../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } description "This augment adds the destination prefix to the reply of the 'active-route' action."; leaf destination-prefix { type inet:ipv6-prefix; description "IPv6 destination prefix."; } } augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" + "rt:simple-next-hop" { when "derived-from-or-self(../../../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } description "Augments the 'simple-next-hop' case in the reply to the 'active-route' action."; leaf next-hop-address { type inet:ipv6-address; description "IPv6 address of the next hop."; } } augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" { when "derived-from-or-self(../../../../../rt:address-family, " + "'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } description "Augments the 'next-hop-list' case in the reply to the 'active-route' action."; leaf next-hop-address { type inet:ipv6-address; description "IPv6 address of the next hop."; } } /* Data node augmentations */ augment "/rt:routing/rt:control-plane-protocols/" + "rt:control-plane-protocol/rt:static-routes" { description "This augment defines the 'static' pseudo-protocol with data specific to IPv6 unicast."; container ipv6 { description "Support for a 'static' pseudo-protocol instance consists of a list of routes."; list route { key "destination-prefix"; description "A list of static routes."; leaf destination-prefix { type inet:ipv6-prefix; mandatory true; description "IPv6 destination prefix."; } leaf description { type string; description "Textual description of the route."; } container next-hop { description "Next hop for the route."; uses rt:next-hop-content { augment "next-hop-options/simple-next-hop" { description "Augments the 'simple-next-hop' case in IPv6 static routes."; leaf next-hop-address { type inet:ipv6-address; description "IPv6 address of the next hop."; } } augment "next-hop-options/next-hop-list/next-hop-list/" + "next-hop" { description "Augments the 'next-hop-list' case in IPv6 static routes."; leaf next-hop-address { type inet:ipv6-address; description "IPv6 address of the next hop."; } } } } } } } /* * The subsequent data nodes are obviated and obsoleted * by the Network Management Datastore Architecture * as described in RFC 8342. */ augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route" { when "derived-from-or-self(../../rt:address-family, 'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } status obsolete; description "This leaf augments an IPv6 unicast route."; leaf destination-prefix { type inet:ipv6-prefix; status obsolete; description "IPv6 destination prefix."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/" + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" { when "derived-from-or-self(../../../rt:address-family, 'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } status obsolete; description "Augments the 'simple-next-hop' case in IPv6 unicast routes."; leaf next-hop-address { type inet:ipv6-address; status obsolete; description "IPv6 address of the next hop."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/" + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/" + "rt:next-hop-list/rt:next-hop" { when "derived-from-or-self(../../../../../rt:address-family, 'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } status obsolete; description "This leaf augments the 'next-hop-list' case of IPv6 unicast routes."; leaf address { type inet:ipv6-address; status obsolete; description "IPv6 address of the next hop."; } } augment "/rt:routing-state/rt:ribs/rt:rib/" + "rt:active-route/rt:input" { when "derived-from-or-self(../rt:address-family, 'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast RIBs."; } status obsolete; description "This augment adds the input parameter of the 'active-route' action."; leaf destination-address { type inet:ipv6-address; status obsolete; description "IPv6 destination address."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route" { when "derived-from-or-self(../../rt:address-family, 'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } status obsolete; description "This augment adds the destination prefix to the reply of the 'active-route' action."; leaf destination-prefix { type inet:ipv6-prefix; status obsolete; description "IPv6 destination prefix."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" + "rt:simple-next-hop" { when "derived-from-or-self(../../../rt:address-family, 'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } status obsolete; description "Augments the 'simple-next-hop' case in the reply to the 'active-route' action."; leaf next-hop-address { type inet:ipv6-address; status obsolete; description "IPv6 address of the next hop."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" { when "derived-from-or-self(../../../../../rt:address-family, 'v6ur:ipv6-unicast')" { description "This augment is valid only for IPv6 unicast."; } status obsolete; description "Augments the 'next-hop-list' case in the reply to the 'active-route' action."; leaf next-hop-address { type inet:ipv6-address; status obsolete; description "IPv6 address of the next hop."; } } } yuma123_2.14/netconf/modules/ietf/ietf-snmp-notification@2014-12-10.yang0000664000175000017500000001435414770023131025500 0ustar vladimirvladimirsubmodule ietf-snmp-notification { belongs-to ietf-snmp { prefix snmp; } include ietf-snmp-common; include ietf-snmp-target; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This submodule contains a collection of YANG definitions for configuring SNMP notifications. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; reference "RFC 3413: Simple Network Management Protocol (SNMP) Applications"; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } feature notification-filter { description "A server implements this feature if it supports SNMP notification filtering."; reference "RFC 3413: Simple Network Management Protocol (SNMP) Applications"; } augment /snmp:snmp { list notify { key name; description "Targets that will receive notifications. Entries in this list are mapped 1-1 to entries in snmpNotifyTable, except that if an entry in snmpNotifyTable has an snmpNotifyTag for which no snmpTargetAddrEntry exists, then the snmpNotifyTable entry is not mapped to an entry in this list."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-NOTIFICATION-MIB.snmpNotifyTable"; leaf name { type snmp:identifier; description "An arbitrary name for the list entry."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-NOTIFICATION-MIB.snmpNotifyName"; } leaf tag { type snmp:tag-value; mandatory true; description "Target tag, selects a set of notification targets. Implementations MAY restrict the values of this leaf to be one of the available values of /snmp/target/tag in a valid configuration."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-NOTIFICATION-MIB.snmpNotifyTag"; } leaf type { type enumeration { enum trap { value 1; } enum inform { value 2; } } default trap; description "Defines the notification type to be generated."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-NOTIFICATION-MIB.snmpNotifyType"; } } list notify-filter-profile { if-feature snmp:notification-filter; key name; description "Notification filter profiles. The leaf /snmp/target/notify-filter-profile is used to associate a filter profile with a target. If an entry in this list is referred to by one or more /snmp/target/notify-filter-profile items, each such notify-filter-profile is represented by one snmpNotifyFilterProfileEntry. If an entry in this list is not referred to by any /snmp/target/notify-filter-profile, the entry is not mapped to snmpNotifyFilterProfileTable."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-NOTIFICATION-MIB.snmpNotifyFilterProfileTable SNMP-NOTIFICATION-MIB.snmpNotifyFilterTable"; leaf name { type snmp:identifier; description "Name of the filter profile."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-NOTIFICATION-MIB.snmpNotifyFilterProfileName"; } leaf-list include { type snmp:wildcard-object-identifier; description "A family of subtrees included in this filter."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-NOTIFICATION-MIB.snmpNotifyFilterSubtree SNMP-NOTIFICATION-MIB.snmpNotifyFilterMask SNMP-NOTIFICATION-MIB.snmpNotifyFilterType"; } leaf-list exclude { type snmp:wildcard-object-identifier; description "A family of subtrees excluded from this filter."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-NOTIFICATION-MIB.snmpNotifyFilterSubtree SNMP-NOTIFICATION-MIB.snmpNotifyFilterMask SNMP-NOTIFICATION-MIB.snmpNotifyFilterType"; } } } augment /snmp:snmp/snmp:target-params { reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-NOTIFICATION-MIB.snmpNotifyFilterProfileTable"; leaf notify-filter-profile { if-feature snmp:notification-filter; type leafref { path "/snmp/notify-filter-profile/name"; } description "This leafref leaf is used to represent the sparse relationship between the /snmp/target-params list and the /snmp/notify-filter-profile list."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-NOTIFICATION-MIB.snmpNotifyFilterProfileName"; } } } yuma123_2.14/netconf/modules/ietf/ietf-snmp-ssh@2014-12-10.yang0000664000175000017500000001045614770023131023606 0ustar vladimirvladimirsubmodule ietf-snmp-ssh { belongs-to ietf-snmp { prefix snmp; } import ietf-inet-types { prefix inet; } include ietf-snmp-common; include ietf-snmp-engine; include ietf-snmp-target; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This submodule contains a collection of YANG definitions for configuring the Secure Shell Transport Model (SSHTM) of SNMP. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; reference "RFC 5592: Secure Shell Transport Model for the Simple Network Management Protocol (SNMP)"; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } feature sshtm { description "A server implements this feature if it supports the Secure Shell Transport Model for SNMP."; reference "RFC 5592: Secure Shell Transport Model for the Simple Network Management Protocol (SNMP)"; } augment /snmp:snmp/snmp:engine/snmp:listen/snmp:transport { if-feature sshtm; case ssh { container ssh { description "The IPv4 or IPv6 address and port to which the engine listens for SNMP messages over SSH."; leaf ip { type inet:ip-address; mandatory true; description "The IPv4 or IPv6 address on which the engine listens for SNMP messages over SSH."; } leaf port { type inet:port-number; description "The TCP port on which the engine listens for SNMP messages over SSH. If the port is not configured, an engine that acts as a Command Responder uses port 5161, and an engine that acts as a Notification Receiver uses port 5162."; } } } } augment /snmp:snmp/snmp:target/snmp:transport { if-feature sshtm; case ssh { reference "RFC 5592: Secure Shell Transport Model for the Simple Network Management Protocol (SNMP). SNMP-SSH-TM-MIB.snmpSSHDomain"; container ssh { leaf ip { type inet:host; mandatory true; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetAddrTAddress RFC 5592: Secure Shell Transport Model for the Simple Network Management Protocol (SNMP). SNMP-SSH-TM-MIB.SnmpSSHAddress"; } leaf port { type inet:port-number; default 5161; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetAddrTAddress RFC 5592: Secure Shell Transport Model for the Simple Network Management Protocol (SNMP). SNMP-SSH-TM-MIB.SnmpSSHAddress"; } leaf username { type string; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetAddrTAddress RFC 5592: Secure Shell Transport Model for the Simple Network Management Protocol (SNMP). SNMP-SSH-TM-MIB.SnmpSSHAddress"; } } } } } yuma123_2.14/netconf/modules/ietf/ietf-alarms-x733@2019-09-11.yang0000664000175000017500000004406214770023131024033 0ustar vladimirvladimirmodule ietf-alarms-x733 { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-alarms-x733"; prefix x733; import ietf-alarms { prefix al; } import ietf-yang-types { prefix yang; reference "RFC 6991: Common YANG Data Types"; } organization "IETF CCAMP Working Group"; contact "WG Web: WG List: Editor: Stefan Vallin Editor: Martin Bjorklund "; description "This module augments the ietf-alarms module with X.733 alarm parameters. The following structures are augmented with the X.733 event type and probable cause: 1) alarms/alarm-inventory: all possible alarm types 2) alarms/alarm-list: every alarm in the system 3) alarm-notification: notifications indicating alarm-state changes 4) alarms/shelved-alarms The module also optionally allows the alarm-management system to configure the mapping from the ietf-alarms' alarm keys to the ITU tuple (event-type, probable-cause). The mapping does not include a corresponding problem value specific to X.733. The recommendation is to use the 'alarm-type-qualifier' leaf, which serves the same purpose. The module uses an integer and a corresponding string for probable cause instead of a globally defined enumeration, in order to be able to manage conflicting enumeration definitions. A single globally defined enumeration is challenging to maintain. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here. Copyright (c) 2019 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8632; see the RFC itself for full legal notices."; reference "ITU-T Recommendation X.733: Information Technology - Open Systems Interconnection - System Management: Alarm Reporting Function"; revision 2019-09-11 { description "Initial revision."; reference "RFC 8632: A YANG Data Model for Alarm Management"; } /* * Features */ feature configure-x733-mapping { description "The system supports configurable X733 mapping from the ietf-alarms' alarm-type to X733 event-type and probable-cause."; } /* * Typedefs */ typedef event-type { type enumeration { enum other { value 1; description "None of the below."; } enum communications-alarm { value 2; description "An alarm of this type is principally associated with the procedures and/or processes required to convey information from one point to another."; } enum quality-of-service-alarm { value 3; description "An alarm of this type is principally associated with a degradation in the quality of a service."; } enum processing-error-alarm { value 4; description "An alarm of this type is principally associated with a software or processing fault."; } enum equipment-alarm { value 5; description "An alarm of this type is principally associated with an equipment fault."; } enum environmental-alarm { value 6; description "An alarm of this type is principally associated with a condition relating to an enclosure in which the equipment resides."; } enum integrity-violation { value 7; description "An indication that information may have been illegally modified, inserted, or deleted."; } enum operational-violation { value 8; description "An indication that the provision of the requested service was not possible due to the unavailability, malfunction, or incorrect invocation of the service."; } enum physical-violation { value 9; description "An indication that a physical resource has been violated in a way that suggests a security attack."; } enum security-service-or-mechanism-violation { value 10; description "An indication that a security attack has been detected by a security service or mechanism."; } enum time-domain-violation { value 11; description "An indication that an event has occurred at an unexpected or prohibited time."; } } description "The event types as defined by X.733 and X.736."; reference "ITU-T Recommendation X.733: Information Technology - Open Systems Interconnection - System Management: Alarm Reporting Function ITU-T Recommendation X.736: Information Technology - Open Systems Interconnection - System Management: Security Alarm Reporting Function"; } typedef trend { type enumeration { enum less-severe { description "There is at least one outstanding alarm of a severity higher (more severe) than that in the current alarm."; } enum no-change { description "The Perceived severity reported in the current alarm is the same as the highest (most severe) of any of the outstanding alarms"; } enum more-severe { description "The Perceived severity in the current alarm is higher (more severe) than that reported in any of the outstanding alarms."; } } description "This type is used to describe the severity trend of the alarming resource."; reference "ITU-T Recommendation X.721: Information Technology - Open Systems Interconnection - Structure of management information: Definition of management information Module Attribute-ASN1Module"; } typedef value-type { type union { type int64; type uint64; type decimal64 { fraction-digits 2; } } description "A generic union type to match the ITU choice of integer and real."; } /* * Groupings */ grouping x733-alarm-parameters { description "Common X.733 parameters for alarms."; leaf event-type { type event-type; description "The X.733/X.736 event type for this alarm."; } leaf probable-cause { type uint32; description "The X.733 probable cause for this alarm."; } leaf probable-cause-string { type string; description "The user-friendly string matching the probable cause integer value. The string SHOULD match the X.733 enumeration. For example, value 27 is 'localNodeTransmissionError'."; } container threshold-information { description "This parameter shall be present when the alarm is a result of crossing a threshold. "; leaf triggered-threshold { type string; description "The identifier of the threshold attribute that caused the notification."; } leaf observed-value { type value-type; description "The value of the gauge or counter that crossed the threshold. This may be different from the threshold value if, for example, the gauge may only take on discrete values."; } choice threshold-level { description "In the case of a gauge, the threshold level specifies a pair of threshold values: the first is the value of the crossed threshold, and the second is its corresponding hysteresis; in the case of a counter, the threshold level specifies only the threshold value."; case up { leaf up-high { type value-type; description "The going-up threshold for raising the alarm."; } leaf up-low { type value-type; description "The going-down threshold for clearing the alarm. This is used for hysteresis functions for gauges."; } } case down { leaf down-low { type value-type; description "The going-down threshold for raising the alarm."; } leaf down-high { type value-type; description "The going-up threshold for clearing the alarm. This is used for hysteresis functions for gauges."; } } } leaf arm-time { type yang:date-and-time; description "For a gauge threshold, it's the time at which the threshold was last re-armed; namely, it's the time after the previous threshold crossing at which the hysteresis value of the threshold was exceeded, thus again permitting the generation of notifications when the threshold is crossed. For a counter threshold, it's the later of the time at which the threshold offset was last applied or the counter was last initialized (for resettable counters)."; } } list monitored-attributes { uses attribute; key "id"; description "The Monitored attributes parameter, when present, defines one or more attributes of the resource and their corresponding values at the time of the alarm."; } leaf-list proposed-repair-actions { type string; description "This parameter, when present, is used if the cause is known and the system being managed can suggest one or more solutions (such as switch in standby equipment, retry, and replace media)."; } leaf trend-indication { type trend; description "This parameter specifies the current severity trend of the resource. If present, it indicates that there are one or more alarms ('outstanding alarms') that have not been cleared and that pertain to the same resource as this alarm ('current alarm') does. The possible values are: more-severe: The Perceived severity in the current alarm is higher (more severe) than that reported in any of the outstanding alarms. no-change: The Perceived severity reported in the current alarm is the same as the highest (most severe) of any of the outstanding alarms. less-severe: There is at least one outstanding alarm of a severity higher (more severe) than that in the current alarm."; } leaf backedup-status { type boolean; description "This parameter, when present, specifies whether or not the object emitting the alarm has been backed up; therefore, it is possible to know whether or not services provided to the user have been disrupted when this parameter is included. The use of this field in conjunction with the 'perceived-severity' field provides information in an independent form to qualify the seriousness of the alarm and the ability of the system as a whole to continue to provide services. If the value of this parameter is true, it indicates that the object emitting the alarm has been backed up; if false, the object has not been backed up."; } leaf backup-object { type al:resource; description "This parameter SHALL be present when the 'backedup-status' parameter is present and has the value 'true'. This parameter specifies the managed object instance that is providing back-up services for the managed object to which the notification pertains. This parameter is useful, for example, when the back-up object is from a pool of objects, any of which may be dynamically allocated to replace a faulty object."; } list additional-information { key "identifier"; description "This parameter allows the inclusion of an additional information set in the alarm. It is a series of data structures, each of which contains three items of information: an identifier, a significance indicator, and the problem information."; leaf identifier { type string; description "Identifies the data type of the information parameter."; } leaf significant { type boolean; description "Set to 'true' if the receiving system must be able to parse the contents of the information subparameter for the event report to be fully understood."; } leaf information { type string; description "Additional information about the alarm."; } } leaf security-alarm-detector { type al:resource; description "This parameter identifies the detector of the security alarm."; } leaf service-user { type al:resource; description "This parameter identifies the service-user whose request for service led to the generation of the security alarm."; } leaf service-provider { type al:resource; description "This parameter identifies the intended service-provider of the service that led to the generation of the security alarm."; } reference "ITU-T Recommendation X.733: Information Technology - Open Systems Interconnection - System Management: Alarm Reporting Function ITU-T Recommendation X.736: Information Technology - Open Systems Interconnection - System Management: Security Alarm Reporting Function"; } grouping x733-alarm-definition-parameters { description "Common X.733 parameters for alarm definitions. This grouping is used to define those alarm attributes that can be mapped from the alarm-type mechanism in the ietf-alarms module."; leaf event-type { type event-type; description "The alarm type has this X.733/X.736 event type."; } leaf probable-cause { type uint32; description "The alarm type has this X.733 probable cause value. This module defines probable cause as an integer and not as an enumeration. The reason being that the primary use of probable cause is in the management application if it is based on the X.733 standard. However, most management applications have their own defined enum definitions and merging enums from different systems might create conflicts. By using a configurable uint32, the system can be configured to match the enum values in the management application."; } leaf probable-cause-string { type string; description "This string can be used to give a user-friendly string to the probable cause value."; } } grouping attribute { description "A grouping to match the ITU generic reference to an attribute."; leaf id { type al:resource; description "The resource representing the attribute."; } leaf value { type string; description "The value represented as a string since it could be of any type."; } reference "ITU-T Recommendation X.721: Information Technology - Open Systems Interconnection - Structure of management information: Definition of management information Module Attribute-ASN1Module"; } /* * Add X.733 parameters to the alarm definitions, alarms, * and notification. */ augment "/al:alarms/al:alarm-inventory/al:alarm-type" { description "Augment X.733 mapping information to the alarm inventory."; uses x733-alarm-definition-parameters; } /* * Add X.733 configurable mapping. */ augment "/al:alarms/al:control" { description "Add X.733 mapping capabilities. "; list x733-mapping { if-feature "configure-x733-mapping"; key "alarm-type-id alarm-type-qualifier-match"; description "This list allows a management application to control the X.733 mapping for all alarm types in the system. Any entry in this list will allow the alarm manager to override the default X.733 mapping in the system, and the final mapping will be shown in the alarm inventory."; leaf alarm-type-id { type al:alarm-type-id; description "Map the alarm type with this alarm type identifier."; } leaf alarm-type-qualifier-match { type string; description "A W3C regular expression that is used when mapping an alarm type and alarm-type-qualifier to X.733 parameters."; } uses x733-alarm-definition-parameters; } } augment "/al:alarms/al:alarm-list/al:alarm" { description "Augment X.733 information to the alarm."; uses x733-alarm-parameters; } augment "/al:alarms/al:shelved-alarms/al:shelved-alarm" { description "Augment X.733 information to the alarm."; uses x733-alarm-parameters; } augment "/al:alarm-notification" { description "Augment X.733 information to the alarm notification."; uses x733-alarm-parameters; } } yuma123_2.14/netconf/modules/ietf/ietf-datastores@2018-02-14.yang0000664000175000017500000000516714770023131024221 0ustar vladimirvladimirmodule ietf-datastores { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-datastores"; prefix ds; organization "IETF Network Modeling (NETMOD) Working Group"; contact "WG Web: WG List: Author: Martin Bjorklund Author: Juergen Schoenwaelder Author: Phil Shafer Author: Kent Watsen Author: Rob Wilton "; description "This YANG module defines a set of identities for identifying datastores. Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8342 (https://www.rfc-editor.org/info/rfc8342); see the RFC itself for full legal notices."; revision 2018-02-14 { description "Initial revision."; reference "RFC 8342: Network Management Datastore Architecture (NMDA)"; } /* * Identities */ identity datastore { description "Abstract base identity for datastore identities."; } identity conventional { base datastore; description "Abstract base identity for conventional configuration datastores."; } identity running { base conventional; description "The running configuration datastore."; } identity candidate { base conventional; description "The candidate configuration datastore."; } identity startup { base conventional; description "The startup configuration datastore."; } identity intended { base conventional; description "The intended configuration datastore."; } identity dynamic { base datastore; description "Abstract base identity for dynamic configuration datastores."; } identity operational { base datastore; description "The operational state datastore."; } /* * Type definitions */ typedef datastore-ref { type identityref { base datastore; } description "A datastore identity reference."; } } yuma123_2.14/netconf/modules/ietf/ietf-yang-smiv2@2012-06-22.yang0000664000175000017500000001161314770023131024032 0ustar vladimirvladimirmodule ietf-yang-smiv2 { namespace "urn:ietf:params:xml:ns:yang:ietf-yang-smiv2"; prefix "smiv2"; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: David Kessens WG Chair: Juergen Schoenwaelder Editor: Juergen Schoenwaelder "; description "This module defines YANG extensions that are used to translate SMIv2 concepts into YANG. Copyright (c) 2012 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 6643; see the RFC itself for full legal notices."; revision 2012-06-22 { description "Initial revision."; reference "RFC 6643: Translation of Structure of Management Information Version 2 (SMIv2) MIB Modules to YANG Modules"; } identity object-identity { description "Base identity for all SMIv2 OBJECT-IDENTITYs."; } typedef opaque { type binary; description "The Opaque type supports the capability to pass arbitrary ASN.1 syntax. A value is encoded using the ASN.1 Basic Encoding Rules into a string of octets. This, in turn, is encoded as an OCTET STRING, in effect 'double-wrapping' the original ASN.1 value. In the value set and its semantics, this type is equivalent to the Opaque type of the SMIv2. This type exists in the SMIv2 solely for backward-compatibility reasons and this is also true for this YANG data type."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } extension display-hint { argument "format"; description "The display-hint statement takes as an argument the DISPLAY-HINT assigned to an SMIv2 textual convention."; reference "RFC 2579: Textual Conventions for SMIv2"; } extension max-access { argument "access"; description "The max-access statement takes as an argument the MAX-ACCESS assigned to an SMIv2 object definition. The MAX-ACCESS value is SMIv2 specific and has no impact on the access provided to YANG objects through protocols such as NETCONF."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } extension defval { argument "value"; description "The defval statement takes as an argument a default value defined by an SMIv2 DEFVAL clause. Note that the value is in the SMIv2 value space defined by the SMIv2 syntax of the corresponding object and not in the YANG value space defined by the corresponding YANG data type."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } extension implied { argument "index"; description "If an SMIv2 INDEX object is preceded by the IMPLIED keyword, then the implied statement is present in the YANG module and takes as an argument the name of the IMPLIED index object."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } extension alias { argument "descriptor"; description "The alias statement introduces an SMIv2 descriptor. The body of the alias statement is expected to contain an oid statement that provides the numeric OID associated with the descriptor."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } extension oid { argument "value"; description "The oid statement takes as an argument the object identifier assigned to an SMIv2 definition. The object identifier value is written in decimal dotted notation."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } extension subid { argument "value"; description "The subid statement takes as an argument the last sub-identifier of the object identifier assigned to an SMIv2 definition. The sub-identifier value is a single positive decimal natural number. The subid statement may not be used as a substatement to any top-level node in a YANG document. The subid substatement may be used only as a substatement to a node having a parent node defined with either an smiv2:oid or smiv2:subid substatement."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } } yuma123_2.14/netconf/modules/ietf/ietf-routing@2018-03-13.yang0000664000175000017500000005010614770023131023530 0ustar vladimirvladimirmodule ietf-routing { yang-version "1.1"; namespace "urn:ietf:params:xml:ns:yang:ietf-routing"; prefix "rt"; import ietf-yang-types { prefix "yang"; } import ietf-interfaces { prefix "if"; description "An 'ietf-interfaces' module version that is compatible with the Network Management Datastore Architecture (NMDA) is required."; } organization "IETF NETMOD (Network Modeling) Working Group"; contact "WG Web: WG List: Editor: Ladislav Lhotka Acee Lindem Yingzhen Qu "; description "This YANG module defines essential components for the management of a routing subsystem. The model fully conforms to the Network Management Datastore Architecture (NMDA). Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8349; see the RFC itself for full legal notices."; revision 2018-03-13 { description "Network Management Datastore Architecture (NMDA) revision."; reference "RFC 8349: A YANG Data Model for Routing Management (NMDA Version)"; } revision 2016-11-04 { description "Initial revision."; reference "RFC 8022: A YANG Data Model for Routing Management"; } /* Features */ feature multiple-ribs { description "This feature indicates that the server supports user-defined RIBs. Servers that do not advertise this feature SHOULD provide exactly one system-controlled RIB per supported address family and also make it the default RIB. This RIB then appears as an entry in the list '/routing/ribs/rib'."; } feature router-id { description "This feature indicates that the server supports an explicit 32-bit router ID that is used by some routing protocols. Servers that do not advertise this feature set a router ID algorithmically, usually to one of the configured IPv4 addresses. However, this algorithm is implementation specific."; } /* Identities */ identity address-family { description "Base identity from which identities describing address families are derived."; } identity ipv4 { base address-family; description "This identity represents an IPv4 address family."; } identity ipv6 { base address-family; description "This identity represents an IPv6 address family."; } identity control-plane-protocol { description "Base identity from which control-plane protocol identities are derived."; } identity routing-protocol { base control-plane-protocol; description "Identity from which Layer 3 routing protocol identities are derived."; } identity direct { base routing-protocol; description "Routing pseudo-protocol that provides routes to directly connected networks."; } identity static { base routing-protocol; description "'Static' routing pseudo-protocol."; } /* Type Definitions */ typedef route-preference { type uint32; description "This type is used for route preferences."; } /* Groupings */ grouping address-family { description "This grouping provides a leaf identifying an address family."; leaf address-family { type identityref { base address-family; } mandatory true; description "Address family."; } } grouping router-id { description "This grouping provides a router ID."; leaf router-id { type yang:dotted-quad; description "A 32-bit number in the form of a dotted quad that is used by some routing protocols identifying a router."; reference "RFC 2328: OSPF Version 2"; } } grouping special-next-hop { description "This grouping provides a leaf with an enumeration of special next hops."; leaf special-next-hop { type enumeration { enum blackhole { description "Silently discard the packet."; } enum unreachable { description "Discard the packet and notify the sender with an error message indicating that the destination host is unreachable."; } enum prohibit { description "Discard the packet and notify the sender with an error message indicating that the communication is administratively prohibited."; } enum receive { description "The packet will be received by the local system."; } } description "Options for special next hops."; } } grouping next-hop-content { description "Generic parameters of next hops in static routes."; choice next-hop-options { mandatory true; description "Options for next hops in static routes. It is expected that further cases will be added through augments from other modules."; case simple-next-hop { description "This case represents a simple next hop consisting of the next-hop address and/or outgoing interface. Modules for address families MUST augment this case with a leaf containing a next-hop address of that address family."; leaf outgoing-interface { type if:interface-ref; description "Name of the outgoing interface."; } } case special-next-hop { uses special-next-hop; } case next-hop-list { container next-hop-list { description "Container for multiple next hops."; list next-hop { key "index"; description "An entry in a next-hop list. Modules for address families MUST augment this list with a leaf containing a next-hop address of that address family."; leaf index { type string; description "A user-specified identifier utilized to uniquely reference the next-hop entry in the next-hop list. The value of this index has no semantic meaning other than for referencing the entry."; } leaf outgoing-interface { type if:interface-ref; description "Name of the outgoing interface."; } } } } } } grouping next-hop-state-content { description "Generic state parameters of next hops."; choice next-hop-options { mandatory true; description "Options for next hops. It is expected that further cases will be added through augments from other modules, e.g., for recursive next hops."; case simple-next-hop { description "This case represents a simple next hop consisting of the next-hop address and/or outgoing interface. Modules for address families MUST augment this case with a leaf containing a next-hop address of that address family."; leaf outgoing-interface { type if:interface-ref; description "Name of the outgoing interface."; } } case special-next-hop { uses special-next-hop; } case next-hop-list { container next-hop-list { description "Container for multiple next hops."; list next-hop { description "An entry in a next-hop list. Modules for address families MUST augment this list with a leaf containing a next-hop address of that address family."; leaf outgoing-interface { type if:interface-ref; description "Name of the outgoing interface."; } } } } } } grouping route-metadata { description "Common route metadata."; leaf source-protocol { type identityref { base routing-protocol; } mandatory true; description "Type of the routing protocol from which the route originated."; } leaf active { type empty; description "The presence of this leaf indicates that the route is preferred among all routes in the same RIB that have the same destination prefix."; } leaf last-updated { type yang:date-and-time; description "Timestamp of the last modification of the route. If the route was never modified, it is the time when the route was inserted into the RIB."; } } /* Data nodes */ container routing { description "Configuration parameters for the routing subsystem."; uses router-id { if-feature "router-id"; description "Support for the global router ID. Routing protocols that use a router ID can use this parameter or override it with another value."; } container interfaces { config false; description "Network-layer interfaces used for routing."; leaf-list interface { type if:interface-ref; description "Each entry is a reference to the name of a configured network-layer interface."; } } container control-plane-protocols { description "Support for control-plane protocol instances."; list control-plane-protocol { key "type name"; description "Each entry contains a control-plane protocol instance."; leaf type { type identityref { base control-plane-protocol; } description "Type of the control-plane protocol -- an identity derived from the 'control-plane-protocol' base identity."; } leaf name { type string; description "An arbitrary name of the control-plane protocol instance."; } leaf description { type string; description "Textual description of the control-plane protocol instance."; } container static-routes { when "derived-from-or-self(../type, 'rt:static')" { description "This container is only valid for the 'static' routing protocol."; } description "Support for the 'static' pseudo-protocol. Address-family-specific modules augment this node with their lists of routes."; } } } container ribs { description "Support for RIBs."; list rib { key "name"; description "Each entry contains a configuration for a RIB identified by the 'name' key. Entries having the same key as a system-controlled entry in the list '/routing/ribs/rib' are used for configuring parameters of that entry. Other entries define additional user-controlled RIBs."; leaf name { type string; description "The name of the RIB. For system-controlled entries, the value of this leaf must be the same as the name of the corresponding entry in the operational state. For user-controlled entries, an arbitrary name can be used."; } uses address-family { description "The address family of the system-controlled RIB."; } leaf default-rib { if-feature "multiple-ribs"; type boolean; default "true"; config false; description "This flag has the value of 'true' if and only if the RIB is the default RIB for the given address family. By default, control-plane protocols place their routes in the default RIBs."; } container routes { config false; description "Current contents of the RIB."; list route { description "A RIB route entry. This data node MUST be augmented with information specific to routes of each address family."; leaf route-preference { type route-preference; description "This route attribute, also known as 'administrative distance', allows for selecting the preferred route among routes with the same destination prefix. A smaller value indicates a route that is more preferred."; } container next-hop { description "Route's next-hop attribute."; uses next-hop-state-content; } uses route-metadata; } } action active-route { description "Return the active RIB route that is used for the destination address. Address-family-specific modules MUST augment input parameters with a leaf named 'destination-address'."; output { container route { description "The active RIB route for the specified destination. If no route exists in the RIB for the destination address, no output is returned. Address-family-specific modules MUST augment this container with appropriate route contents."; container next-hop { description "Route's next-hop attribute."; uses next-hop-state-content; } uses route-metadata; } } } leaf description { type string; description "Textual description of the RIB."; } } } } /* * The subsequent data nodes are obviated and obsoleted * by the Network Management Datastore Architecture * as described in RFC 8342. */ container routing-state { config false; status obsolete; description "State data of the routing subsystem."; uses router-id { status obsolete; description "Global router ID. It may be either configured or assigned algorithmically by the implementation."; } container interfaces { status obsolete; description "Network-layer interfaces used for routing."; leaf-list interface { type if:interface-state-ref; status obsolete; description "Each entry is a reference to the name of a configured network-layer interface."; } } container control-plane-protocols { status obsolete; description "Container for the list of routing protocol instances."; list control-plane-protocol { key "type name"; status obsolete; description "State data of a control-plane protocol instance. An implementation MUST provide exactly one system-controlled instance of the 'direct' pseudo-protocol. Instances of other control-plane protocols MAY be created by configuration."; leaf type { type identityref { base control-plane-protocol; } status obsolete; description "Type of the control-plane protocol."; } leaf name { type string; status obsolete; description "The name of the control-plane protocol instance. For system-controlled instances, this name is persistent, i.e., it SHOULD NOT change across reboots."; } } } container ribs { status obsolete; description "Container for RIBs."; list rib { key "name"; min-elements 1; status obsolete; description "Each entry represents a RIB identified by the 'name' key. All routes in a RIB MUST belong to the same address family. An implementation SHOULD provide one system-controlled default RIB for each supported address family."; leaf name { type string; status obsolete; description "The name of the RIB."; } uses address-family { status obsolete; description "The address family of the RIB."; } leaf default-rib { if-feature "multiple-ribs"; type boolean; default "true"; status obsolete; description "This flag has the value of 'true' if and only if the RIB is the default RIB for the given address family. By default, control-plane protocols place their routes in the default RIBs."; } container routes { status obsolete; description "Current contents of the RIB."; list route { status obsolete; description "A RIB route entry. This data node MUST be augmented with information specific to routes of each address family."; leaf route-preference { type route-preference; status obsolete; description "This route attribute, also known as 'administrative distance', allows for selecting the preferred route among routes with the same destination prefix. A smaller value indicates a route that is more preferred."; } container next-hop { status obsolete; description "Route's next-hop attribute."; uses next-hop-state-content { status obsolete; description "Route's next-hop attribute operational state."; } } uses route-metadata { status obsolete; description "Route metadata."; } } } action active-route { status obsolete; description "Return the active RIB route that is used for the destination address. Address-family-specific modules MUST augment input parameters with a leaf named 'destination-address'."; output { container route { status obsolete; description "The active RIB route for the specified destination. If no route exists in the RIB for the destination address, no output is returned. Address-family-specific modules MUST augment this container with appropriate route contents."; container next-hop { status obsolete; description "Route's next-hop attribute."; uses next-hop-state-content { status obsolete; description "Active route state data."; } } uses route-metadata { status obsolete; description "Active route metadata."; } } } } } } } } yuma123_2.14/netconf/modules/ietf/ietf-yang-patch@2017-02-22.yang0000664000175000017500000003151614770023131024076 0ustar vladimirvladimirmodule ietf-yang-patch { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-yang-patch"; prefix "ypatch"; import ietf-restconf { prefix rc; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: WG List: Author: Andy Bierman Author: Martin Bjorklund Author: Kent Watsen "; description "This module contains conceptual YANG specifications for the YANG Patch and YANG Patch Status data structures. Note that the YANG definitions within this module do not represent configuration data of any kind. The YANG grouping statements provide a normative syntax for XML and JSON message-encoding purposes. Copyright (c) 2017 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8072; see the RFC itself for full legal notices."; revision 2017-02-22 { description "Initial revision."; reference "RFC 8072: YANG Patch Media Type."; } typedef target-resource-offset { type string; description "Contains a data resource identifier string representing a sub-resource within the target resource. The document root for this expression is the target resource that is specified in the protocol operation (e.g., the URI for the PATCH request). This string is encoded according to the same rules as those for a data resource identifier in a RESTCONF request URI."; reference "RFC 8040, Section 3.5.3."; } rc:yang-data "yang-patch" { uses yang-patch; } rc:yang-data "yang-patch-status" { uses yang-patch-status; } grouping yang-patch { description "A grouping that contains a YANG container representing the syntax and semantics of a YANG Patch edit request message."; container yang-patch { description "Represents a conceptual sequence of datastore edits, called a patch. Each patch is given a client-assigned patch identifier. Each edit MUST be applied in ascending order, and all edits MUST be applied. If any errors occur, then the target datastore MUST NOT be changed by the YANG Patch operation. It is possible for a datastore constraint violation to occur due to any node in the datastore, including nodes not included in the 'edit' list. Any validation errors MUST be reported in the reply message."; reference "RFC 7950, Section 8.3."; leaf patch-id { type string; mandatory true; description "An arbitrary string provided by the client to identify the entire patch. Error messages returned by the server that pertain to this patch will be identified by this 'patch-id' value. A client SHOULD attempt to generate unique 'patch-id' values to distinguish between transactions from multiple clients in any audit logs maintained by the server."; } leaf comment { type string; description "An arbitrary string provided by the client to describe the entire patch. This value SHOULD be present in any audit logging records generated by the server for the patch."; } list edit { key edit-id; ordered-by user; description "Represents one edit within the YANG Patch request message. The 'edit' list is applied in the following manner: - The first edit is conceptually applied to a copy of the existing target datastore, e.g., the running configuration datastore. - Each ascending edit is conceptually applied to the result of the previous edit(s). - After all edits have been successfully processed, the result is validated according to YANG constraints. - If successful, the server will attempt to apply the result to the target datastore."; leaf edit-id { type string; description "Arbitrary string index for the edit. Error messages returned by the server that pertain to a specific edit will be identified by this value."; } leaf operation { type enumeration { enum create { description "The target data node is created using the supplied value, only if it does not already exist. The 'target' leaf identifies the data node to be created, not the parent data node."; } enum delete { description "Delete the target node, only if the data resource currently exists; otherwise, return an error."; } enum insert { description "Insert the supplied value into a user-ordered list or leaf-list entry. The target node must represent a new data resource. If the 'where' parameter is set to 'before' or 'after', then the 'point' parameter identifies the insertion point for the target node."; } enum merge { description "The supplied value is merged with the target data node."; } enum move { description "Move the target node. Reorder a user-ordered list or leaf-list. The target node must represent an existing data resource. If the 'where' parameter is set to 'before' or 'after', then the 'point' parameter identifies the insertion point to move the target node."; } enum replace { description "The supplied value is used to replace the target data node."; } enum remove { description "Delete the target node if it currently exists."; } } mandatory true; description "The datastore operation requested for the associated 'edit' entry."; } leaf target { type target-resource-offset; mandatory true; description "Identifies the target data node for the edit operation. If the target has the value '/', then the target data node is the target resource. The target node MUST identify a data resource, not the datastore resource."; } leaf point { when "(../operation = 'insert' or ../operation = 'move')" + "and (../where = 'before' or ../where = 'after')" { description "This leaf only applies for 'insert' or 'move' operations, before or after an existing entry."; } type target-resource-offset; description "The absolute URL path for the data node that is being used as the insertion point or move point for the target of this 'edit' entry."; } leaf where { when "../operation = 'insert' or ../operation = 'move'" { description "This leaf only applies for 'insert' or 'move' operations."; } type enumeration { enum before { description "Insert or move a data node before the data resource identified by the 'point' parameter."; } enum after { description "Insert or move a data node after the data resource identified by the 'point' parameter."; } enum first { description "Insert or move a data node so it becomes ordered as the first entry."; } enum last { description "Insert or move a data node so it becomes ordered as the last entry."; } } default last; description "Identifies where a data resource will be inserted or moved. YANG only allows these operations for list and leaf-list data nodes that are 'ordered-by user'."; } anydata value { when "../operation = 'create' " + "or ../operation = 'merge' " + "or ../operation = 'replace' " + "or ../operation = 'insert'" { description "The anydata 'value' is only used for 'create', 'merge', 'replace', and 'insert' operations."; } description "Value used for this edit operation. The anydata 'value' contains the target resource associated with the 'target' leaf. For example, suppose the target node is a YANG container named foo: container foo { leaf a { type string; } leaf b { type int32; } } The 'value' node contains one instance of foo: some value 42 "; } } } } // grouping yang-patch grouping yang-patch-status { description "A grouping that contains a YANG container representing the syntax and semantics of a YANG Patch Status response message."; container yang-patch-status { description "A container representing the response message sent by the server after a YANG Patch edit request message has been processed."; leaf patch-id { type string; mandatory true; description "The 'patch-id' value used in the request."; } choice global-status { description "Report global errors or complete success. If there is no case selected, then errors are reported in the 'edit-status' container."; case global-errors { uses rc:errors; description "This container will be present if global errors that are unrelated to a specific edit occurred."; } leaf ok { type empty; description "This leaf will be present if the request succeeded and there are no errors reported in the 'edit-status' container."; } } container edit-status { description "This container will be present if there are edit-specific status responses to report. If all edits succeeded and the 'global-status' returned is 'ok', then a server MAY omit this container."; list edit { key edit-id; description "Represents a list of status responses, corresponding to edits in the YANG Patch request message. If an 'edit' entry was skipped or not reached by the server, then this list will not contain a corresponding entry for that edit."; leaf edit-id { type string; description "Response status is for the 'edit' list entry with this 'edit-id' value."; } choice edit-status-choice { description "A choice between different types of status responses for each 'edit' entry."; leaf ok { type empty; description "This 'edit' entry was invoked without any errors detected by the server associated with this edit."; } case errors { uses rc:errors; description "The server detected errors associated with the edit identified by the same 'edit-id' value."; } } } } } } // grouping yang-patch-status } yuma123_2.14/netconf/modules/ietf/ietf-yang-types@2013-07-15.yang0000664000175000017500000004301514770023131024143 0ustar vladimirvladimirmodule ietf-yang-types { namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; prefix "yang"; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: David Kessens WG Chair: Juergen Schoenwaelder Editor: Juergen Schoenwaelder "; description "This module contains a collection of generally useful derived YANG data types. Copyright (c) 2013 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 6991; see the RFC itself for full legal notices."; revision 2013-07-15 { description "This revision adds the following new data types: - yang-identifier - hex-string - uuid - dotted-quad"; reference "RFC 6991: Common YANG Data Types"; } revision 2010-09-24 { description "Initial revision."; reference "RFC 6021: Common YANG Data Types"; } /*** collection of counter and gauge types ***/ typedef counter32 { type uint32; description "The counter32 type represents a non-negative integer that monotonically increases until it reaches a maximum value of 2^32-1 (4294967295 decimal), when it wraps around and starts increasing again from zero. Counters have no defined 'initial' value, and thus, a single value of a counter has (in general) no information content. Discontinuities in the monotonically increasing value normally occur at re-initialization of the management system, and at other times as specified in the description of a schema node using this type. If such other times can occur, for example, the creation of a schema node of type counter32 at times other than re-initialization, then a corresponding schema node should be defined, with an appropriate type, to indicate the last discontinuity. The counter32 type should not be used for configuration schema nodes. A default statement SHOULD NOT be used in combination with the type counter32. In the value set and its semantics, this type is equivalent to the Counter32 type of the SMIv2."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } typedef zero-based-counter32 { type yang:counter32; default "0"; description "The zero-based-counter32 type represents a counter32 that has the defined 'initial' value zero. A schema node of this type will be set to zero (0) on creation and will thereafter increase monotonically until it reaches a maximum value of 2^32-1 (4294967295 decimal), when it wraps around and starts increasing again from zero. Provided that an application discovers a new schema node of this type within the minimum time to wrap, it can use the 'initial' value as a delta. It is important for a management station to be aware of this minimum time and the actual time between polls, and to discard data if the actual time is too long or there is no defined minimum time. In the value set and its semantics, this type is equivalent to the ZeroBasedCounter32 textual convention of the SMIv2."; reference "RFC 4502: Remote Network Monitoring Management Information Base Version 2"; } typedef counter64 { type uint64; description "The counter64 type represents a non-negative integer that monotonically increases until it reaches a maximum value of 2^64-1 (18446744073709551615 decimal), when it wraps around and starts increasing again from zero. Counters have no defined 'initial' value, and thus, a single value of a counter has (in general) no information content. Discontinuities in the monotonically increasing value normally occur at re-initialization of the management system, and at other times as specified in the description of a schema node using this type. If such other times can occur, for example, the creation of a schema node of type counter64 at times other than re-initialization, then a corresponding schema node should be defined, with an appropriate type, to indicate the last discontinuity. The counter64 type should not be used for configuration schema nodes. A default statement SHOULD NOT be used in combination with the type counter64. In the value set and its semantics, this type is equivalent to the Counter64 type of the SMIv2."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } typedef zero-based-counter64 { type yang:counter64; default "0"; description "The zero-based-counter64 type represents a counter64 that has the defined 'initial' value zero. A schema node of this type will be set to zero (0) on creation and will thereafter increase monotonically until it reaches a maximum value of 2^64-1 (18446744073709551615 decimal), when it wraps around and starts increasing again from zero. Provided that an application discovers a new schema node of this type within the minimum time to wrap, it can use the 'initial' value as a delta. It is important for a management station to be aware of this minimum time and the actual time between polls, and to discard data if the actual time is too long or there is no defined minimum time. In the value set and its semantics, this type is equivalent to the ZeroBasedCounter64 textual convention of the SMIv2."; reference "RFC 2856: Textual Conventions for Additional High Capacity Data Types"; } typedef gauge32 { type uint32; description "The gauge32 type represents a non-negative integer, which may increase or decrease, but shall never exceed a maximum value, nor fall below a minimum value. The maximum value cannot be greater than 2^32-1 (4294967295 decimal), and the minimum value cannot be smaller than 0. The value of a gauge32 has its maximum value whenever the information being modeled is greater than or equal to its maximum value, and has its minimum value whenever the information being modeled is smaller than or equal to its minimum value. If the information being modeled subsequently decreases below (increases above) the maximum (minimum) value, the gauge32 also decreases (increases). In the value set and its semantics, this type is equivalent to the Gauge32 type of the SMIv2."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } typedef gauge64 { type uint64; description "The gauge64 type represents a non-negative integer, which may increase or decrease, but shall never exceed a maximum value, nor fall below a minimum value. The maximum value cannot be greater than 2^64-1 (18446744073709551615), and the minimum value cannot be smaller than 0. The value of a gauge64 has its maximum value whenever the information being modeled is greater than or equal to its maximum value, and has its minimum value whenever the information being modeled is smaller than or equal to its minimum value. If the information being modeled subsequently decreases below (increases above) the maximum (minimum) value, the gauge64 also decreases (increases). In the value set and its semantics, this type is equivalent to the CounterBasedGauge64 SMIv2 textual convention defined in RFC 2856"; reference "RFC 2856: Textual Conventions for Additional High Capacity Data Types"; } /*** collection of identifier-related types ***/ typedef object-identifier { type string { pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + '(\.(0|([1-9]\d*)))*'; } description "The object-identifier type represents administratively assigned names in a registration-hierarchical-name tree. Values of this type are denoted as a sequence of numerical non-negative sub-identifier values. Each sub-identifier value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers are separated by single dots and without any intermediate whitespace. The ASN.1 standard restricts the value space of the first sub-identifier to 0, 1, or 2. Furthermore, the value space of the second sub-identifier is restricted to the range 0 to 39 if the first sub-identifier is 0 or 1. Finally, the ASN.1 standard requires that an object identifier has always at least two sub-identifiers. The pattern captures these restrictions. Although the number of sub-identifiers is not limited, module designers should realize that there may be implementations that stick with the SMIv2 limit of 128 sub-identifiers. This type is a superset of the SMIv2 OBJECT IDENTIFIER type since it is not restricted to 128 sub-identifiers. Hence, this type SHOULD NOT be used to represent the SMIv2 OBJECT IDENTIFIER type; the object-identifier-128 type SHOULD be used instead."; reference "ISO9834-1: Information technology -- Open Systems Interconnection -- Procedures for the operation of OSI Registration Authorities: General procedures and top arcs of the ASN.1 Object Identifier tree"; } typedef object-identifier-128 { type object-identifier { pattern '\d*(\.\d*){1,127}'; } description "This type represents object-identifiers restricted to 128 sub-identifiers. In the value set and its semantics, this type is equivalent to the OBJECT IDENTIFIER type of the SMIv2."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } typedef yang-identifier { type string { length "1..max"; pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; } description "A YANG identifier string as defined by the 'identifier' rule in Section 12 of RFC 6020. An identifier must start with an alphabetic character or an underscore followed by an arbitrary sequence of alphabetic or numeric characters, underscores, hyphens, or dots. A YANG identifier MUST NOT start with any possible combination of the lowercase or uppercase character sequence 'xml'."; reference "RFC 6020: YANG - A Data Modeling Language for the Network Configuration Protocol (NETCONF)"; } /*** collection of types related to date and time***/ typedef date-and-time { type string { pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + '(Z|[\+\-]\d{2}:\d{2})'; } description "The date-and-time type is a profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. The profile is defined by the date-time production in Section 5.6 of RFC 3339. The date-and-time type is compatible with the dateTime XML schema type with the following notable exceptions: (a) The date-and-time type does not allow negative years. (b) The date-and-time time-offset -00:00 indicates an unknown time zone (see RFC 3339) while -00:00 and +00:00 and Z all represent the same time zone in dateTime. (c) The canonical format (see below) of data-and-time values differs from the canonical format used by the dateTime XML schema type, which requires all times to be in UTC using the time-offset 'Z'. This type is not equivalent to the DateAndTime textual convention of the SMIv2 since RFC 3339 uses a different separator between full-date and full-time and provides higher resolution of time-secfrac. The canonical format for date-and-time values with a known time zone uses a numeric time zone offset that is calculated using the device's configured known offset to UTC time. A change of the device's offset to UTC time will cause date-and-time values to change accordingly. Such changes might happen periodically in case a server follows automatically daylight saving time (DST) time zone offset changes. The canonical format for date-and-time values with an unknown time zone (usually referring to the notion of local time) uses the time-offset -00:00."; reference "RFC 3339: Date and Time on the Internet: Timestamps RFC 2579: Textual Conventions for SMIv2 XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; } typedef timeticks { type uint32; description "The timeticks type represents a non-negative integer that represents the time, modulo 2^32 (4294967296 decimal), in hundredths of a second between two epochs. When a schema node is defined that uses this type, the description of the schema node identifies both of the reference epochs. In the value set and its semantics, this type is equivalent to the TimeTicks type of the SMIv2."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } typedef timestamp { type yang:timeticks; description "The timestamp type represents the value of an associated timeticks schema node at which a specific occurrence happened. The specific occurrence must be defined in the description of any schema node defined using this type. When the specific occurrence occurred prior to the last time the associated timeticks attribute was zero, then the timestamp value is zero. Note that this requires all timestamp values to be reset to zero when the value of the associated timeticks attribute reaches 497+ days and wraps around to zero. The associated timeticks schema node must be specified in the description of any schema node using this type. In the value set and its semantics, this type is equivalent to the TimeStamp textual convention of the SMIv2."; reference "RFC 2579: Textual Conventions for SMIv2"; } /*** collection of generic address types ***/ typedef phys-address { type string { pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; } description "Represents media- or physical-level addresses represented as a sequence octets, each octet represented by two hexadecimal numbers. Octets are separated by colons. The canonical representation uses lowercase characters. In the value set and its semantics, this type is equivalent to the PhysAddress textual convention of the SMIv2."; reference "RFC 2579: Textual Conventions for SMIv2"; } typedef mac-address { type string { pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; } description "The mac-address type represents an IEEE 802 MAC address. The canonical representation uses lowercase characters. In the value set and its semantics, this type is equivalent to the MacAddress textual convention of the SMIv2."; reference "IEEE 802: IEEE Standard for Local and Metropolitan Area Networks: Overview and Architecture RFC 2579: Textual Conventions for SMIv2"; } /*** collection of XML-specific types ***/ typedef xpath1.0 { type string; description "This type represents an XPATH 1.0 expression. When a schema node is defined that uses this type, the description of the schema node MUST specify the XPath context in which the XPath expression is evaluated."; reference "XPATH: XML Path Language (XPath) Version 1.0"; } /*** collection of string types ***/ typedef hex-string { type string { pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; } description "A hexadecimal string with octets represented as hex digits separated by colons. The canonical representation uses lowercase characters."; } typedef uuid { type string { pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-' + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; } description "A Universally Unique IDentifier in the string representation defined in RFC 4122. The canonical representation uses lowercase characters. The following is an example of a UUID in string representation: f81d4fae-7dec-11d0-a765-00a0c91e6bf6 "; reference "RFC 4122: A Universally Unique IDentifier (UUID) URN Namespace"; } typedef dotted-quad { type string { pattern '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'; } description "An unsigned 32-bit number expressed in the dotted-quad notation, i.e., four octets written as decimal numbers and separated with the '.' (full stop) character."; } } yuma123_2.14/netconf/modules/ietf/ietf-ip@2018-02-22.yang0000664000175000017500000006556314770023131022465 0ustar vladimirvladimirmodule ietf-ip { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-ip"; prefix ip; import ietf-interfaces { prefix if; } import ietf-inet-types { prefix inet; } import ietf-yang-types { prefix yang; } organization "IETF NETMOD (Network Modeling) Working Group"; contact "WG Web: WG List: Editor: Martin Bjorklund "; description "This module contains a collection of YANG definitions for managing IP implementations. Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8344; see the RFC itself for full legal notices."; revision 2018-02-22 { description "Updated to support NMDA."; reference "RFC 8344: A YANG Data Model for IP Management"; } revision 2014-06-16 { description "Initial revision."; reference "RFC 7277: A YANG Data Model for IP Management"; } /* * Features */ feature ipv4-non-contiguous-netmasks { description "Indicates support for configuring non-contiguous subnet masks."; } feature ipv6-privacy-autoconf { description "Indicates support for privacy extensions for stateless address autoconfiguration in IPv6."; reference "RFC 4941: Privacy Extensions for Stateless Address Autoconfiguration in IPv6"; } /* * Typedefs */ typedef ip-address-origin { type enumeration { enum other { description "None of the following."; } enum static { description "Indicates that the address has been statically configured -- for example, using the Network Configuration Protocol (NETCONF) or a command line interface."; } enum dhcp { description "Indicates an address that has been assigned to this system by a DHCP server."; } enum link-layer { description "Indicates an address created by IPv6 stateless autoconfiguration that embeds a link-layer address in its interface identifier."; } enum random { description "Indicates an address chosen by the system at random, e.g., an IPv4 address within 169.254/16, a temporary address as described in RFC 4941, or a semantically opaque address as described in RFC 7217."; reference "RFC 4941: Privacy Extensions for Stateless Address Autoconfiguration in IPv6 RFC 7217: A Method for Generating Semantically Opaque Interface Identifiers with IPv6 Stateless Address Autoconfiguration (SLAAC)"; } } description "The origin of an address."; } typedef neighbor-origin { type enumeration { enum other { description "None of the following."; } enum static { description "Indicates that the mapping has been statically configured -- for example, using NETCONF or a command line interface."; } enum dynamic { description "Indicates that the mapping has been dynamically resolved using, for example, IPv4 ARP or the IPv6 Neighbor Discovery protocol."; } } description "The origin of a neighbor entry."; } /* * Data nodes */ augment "/if:interfaces/if:interface" { description "IP parameters on interfaces. If an interface is not capable of running IP, the server must not allow the client to configure these parameters."; container ipv4 { presence "Enables IPv4 unless the 'enabled' leaf (which defaults to 'true') is set to 'false'"; description "Parameters for the IPv4 address family."; leaf enabled { type boolean; default true; description "Controls whether IPv4 is enabled or disabled on this interface. When IPv4 is enabled, this interface is connected to an IPv4 stack, and the interface can send and receive IPv4 packets."; } leaf forwarding { type boolean; default false; description "Controls IPv4 packet forwarding of datagrams received by, but not addressed to, this interface. IPv4 routers forward datagrams. IPv4 hosts do not (except those source-routed via the host)."; } leaf mtu { type uint16 { range "68..max"; } units "octets"; description "The size, in octets, of the largest IPv4 packet that the interface will send and receive. The server may restrict the allowed values for this leaf, depending on the interface's type. If this leaf is not configured, the operationally used MTU depends on the interface's type."; reference "RFC 791: Internet Protocol"; } list address { key "ip"; description "The list of IPv4 addresses on the interface."; leaf ip { type inet:ipv4-address-no-zone; description "The IPv4 address on the interface."; } choice subnet { mandatory true; description "The subnet can be specified as a prefix length or, if the server supports non-contiguous netmasks, as a netmask."; leaf prefix-length { type uint8 { range "0..32"; } description "The length of the subnet prefix."; } leaf netmask { if-feature ipv4-non-contiguous-netmasks; type yang:dotted-quad; description "The subnet specified as a netmask."; } } leaf origin { type ip-address-origin; config false; description "The origin of this address."; } } list neighbor { key "ip"; description "A list of mappings from IPv4 addresses to link-layer addresses. Entries in this list in the intended configuration are used as static entries in the ARP Cache. In the operational state, this list represents the ARP Cache."; reference "RFC 826: An Ethernet Address Resolution Protocol"; leaf ip { type inet:ipv4-address-no-zone; description "The IPv4 address of the neighbor node."; } leaf link-layer-address { type yang:phys-address; mandatory true; description "The link-layer address of the neighbor node."; } leaf origin { type neighbor-origin; config false; description "The origin of this neighbor entry."; } } } container ipv6 { presence "Enables IPv6 unless the 'enabled' leaf (which defaults to 'true') is set to 'false'"; description "Parameters for the IPv6 address family."; leaf enabled { type boolean; default true; description "Controls whether IPv6 is enabled or disabled on this interface. When IPv6 is enabled, this interface is connected to an IPv6 stack, and the interface can send and receive IPv6 packets."; } leaf forwarding { type boolean; default false; description "Controls IPv6 packet forwarding of datagrams received by, but not addressed to, this interface. IPv6 routers forward datagrams. IPv6 hosts do not (except those source-routed via the host)."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) Section 6.2.1, IsRouter"; } leaf mtu { type uint32 { range "1280..max"; } units "octets"; description "The size, in octets, of the largest IPv6 packet that the interface will send and receive. The server may restrict the allowed values for this leaf, depending on the interface's type. If this leaf is not configured, the operationally used MTU depends on the interface's type."; reference "RFC 8200: Internet Protocol, Version 6 (IPv6) Specification Section 5"; } list address { key "ip"; description "The list of IPv6 addresses on the interface."; leaf ip { type inet:ipv6-address-no-zone; description "The IPv6 address on the interface."; } leaf prefix-length { type uint8 { range "0..128"; } mandatory true; description "The length of the subnet prefix."; } leaf origin { type ip-address-origin; config false; description "The origin of this address."; } leaf status { type enumeration { enum preferred { description "This is a valid address that can appear as the destination or source address of a packet."; } enum deprecated { description "This is a valid but deprecated address that should no longer be used as a source address in new communications, but packets addressed to such an address are processed as expected."; } enum invalid { description "This isn't a valid address, and it shouldn't appear as the destination or source address of a packet."; } enum inaccessible { description "The address is not accessible because the interface to which this address is assigned is not operational."; } enum unknown { description "The status cannot be determined for some reason."; } enum tentative { description "The uniqueness of the address on the link is being verified. Addresses in this state should not be used for general communication and should only be used to determine the uniqueness of the address."; } enum duplicate { description "The address has been determined to be non-unique on the link and so must not be used."; } enum optimistic { description "The address is available for use, subject to restrictions, while its uniqueness on a link is being verified."; } } config false; description "The status of an address. Most of the states correspond to states from the IPv6 Stateless Address Autoconfiguration protocol."; reference "RFC 4293: Management Information Base for the Internet Protocol (IP) - IpAddressStatusTC RFC 4862: IPv6 Stateless Address Autoconfiguration"; } } list neighbor { key "ip"; description "A list of mappings from IPv6 addresses to link-layer addresses. Entries in this list in the intended configuration are used as static entries in the Neighbor Cache. In the operational state, this list represents the Neighbor Cache."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)"; leaf ip { type inet:ipv6-address-no-zone; description "The IPv6 address of the neighbor node."; } leaf link-layer-address { type yang:phys-address; mandatory true; description "The link-layer address of the neighbor node. In the operational state, if the neighbor's 'state' leaf is 'incomplete', this leaf is not instantiated."; } leaf origin { type neighbor-origin; config false; description "The origin of this neighbor entry."; } leaf is-router { type empty; config false; description "Indicates that the neighbor node acts as a router."; } leaf state { type enumeration { enum incomplete { description "Address resolution is in progress, and the link-layer address of the neighbor has not yet been determined."; } enum reachable { description "Roughly speaking, the neighbor is known to have been reachable recently (within tens of seconds ago)."; } enum stale { description "The neighbor is no longer known to be reachable, but until traffic is sent to the neighbor no attempt should be made to verify its reachability."; } enum delay { description "The neighbor is no longer known to be reachable, and traffic has recently been sent to the neighbor. Rather than probe the neighbor immediately, however, delay sending probes for a short while in order to give upper-layer protocols a chance to provide reachability confirmation."; } enum probe { description "The neighbor is no longer known to be reachable, and unicast Neighbor Solicitation probes are being sent to verify reachability."; } } config false; description "The Neighbor Unreachability Detection state of this entry."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) Section 7.3.2"; } } leaf dup-addr-detect-transmits { type uint32; default 1; description "The number of consecutive Neighbor Solicitation messages sent while performing Duplicate Address Detection on a tentative address. A value of zero indicates that Duplicate Address Detection is not performed on tentative addresses. A value of one indicates a single transmission with no follow-up retransmissions."; reference "RFC 4862: IPv6 Stateless Address Autoconfiguration"; } container autoconf { description "Parameters to control the autoconfiguration of IPv6 addresses, as described in RFC 4862."; reference "RFC 4862: IPv6 Stateless Address Autoconfiguration"; leaf create-global-addresses { type boolean; default true; description "If enabled, the host creates global addresses as described in RFC 4862."; reference "RFC 4862: IPv6 Stateless Address Autoconfiguration Section 5.5"; } leaf create-temporary-addresses { if-feature ipv6-privacy-autoconf; type boolean; default false; description "If enabled, the host creates temporary addresses as described in RFC 4941."; reference "RFC 4941: Privacy Extensions for Stateless Address Autoconfiguration in IPv6"; } leaf temporary-valid-lifetime { if-feature ipv6-privacy-autoconf; type uint32; units "seconds"; default 604800; description "The time period during which the temporary address is valid."; reference "RFC 4941: Privacy Extensions for Stateless Address Autoconfiguration in IPv6 - TEMP_VALID_LIFETIME"; } leaf temporary-preferred-lifetime { if-feature ipv6-privacy-autoconf; type uint32; units "seconds"; default 86400; description "The time period during which the temporary address is preferred."; reference "RFC 4941: Privacy Extensions for Stateless Address Autoconfiguration in IPv6 - TEMP_PREFERRED_LIFETIME"; } } } } /* * Legacy operational state data nodes */ augment "/if:interfaces-state/if:interface" { status deprecated; description "Data nodes for the operational state of IP on interfaces."; container ipv4 { presence "Present if IPv4 is enabled on this interface"; config false; status deprecated; description "Interface-specific parameters for the IPv4 address family."; leaf forwarding { type boolean; status deprecated; description "Indicates whether IPv4 packet forwarding is enabled or disabled on this interface."; } leaf mtu { type uint16 { range "68..max"; } units "octets"; status deprecated; description "The size, in octets, of the largest IPv4 packet that the interface will send and receive."; reference "RFC 791: Internet Protocol"; } list address { key "ip"; status deprecated; description "The list of IPv4 addresses on the interface."; leaf ip { type inet:ipv4-address-no-zone; status deprecated; description "The IPv4 address on the interface."; } choice subnet { status deprecated; description "The subnet can be specified as a prefix length or, if the server supports non-contiguous netmasks, as a netmask."; leaf prefix-length { type uint8 { range "0..32"; } status deprecated; description "The length of the subnet prefix."; } leaf netmask { if-feature ipv4-non-contiguous-netmasks; type yang:dotted-quad; status deprecated; description "The subnet specified as a netmask."; } } leaf origin { type ip-address-origin; status deprecated; description "The origin of this address."; } } list neighbor { key "ip"; status deprecated; description "A list of mappings from IPv4 addresses to link-layer addresses. This list represents the ARP Cache."; reference "RFC 826: An Ethernet Address Resolution Protocol"; leaf ip { type inet:ipv4-address-no-zone; status deprecated; description "The IPv4 address of the neighbor node."; } leaf link-layer-address { type yang:phys-address; status deprecated; description "The link-layer address of the neighbor node."; } leaf origin { type neighbor-origin; status deprecated; description "The origin of this neighbor entry."; } } } container ipv6 { presence "Present if IPv6 is enabled on this interface"; config false; status deprecated; description "Parameters for the IPv6 address family."; leaf forwarding { type boolean; default false; status deprecated; description "Indicates whether IPv6 packet forwarding is enabled or disabled on this interface."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) Section 6.2.1, IsRouter"; } leaf mtu { type uint32 { range "1280..max"; } units "octets"; status deprecated; description "The size, in octets, of the largest IPv6 packet that the interface will send and receive."; reference "RFC 8200: Internet Protocol, Version 6 (IPv6) Specification Section 5"; } list address { key "ip"; status deprecated; description "The list of IPv6 addresses on the interface."; leaf ip { type inet:ipv6-address-no-zone; status deprecated; description "The IPv6 address on the interface."; } leaf prefix-length { type uint8 { range "0..128"; } mandatory true; status deprecated; description "The length of the subnet prefix."; } leaf origin { type ip-address-origin; status deprecated; description "The origin of this address."; } leaf status { type enumeration { enum preferred { description "This is a valid address that can appear as the destination or source address of a packet."; } enum deprecated { description "This is a valid but deprecated address that should no longer be used as a source address in new communications, but packets addressed to such an address are processed as expected."; } enum invalid { description "This isn't a valid address, and it shouldn't appear as the destination or source address of a packet."; } enum inaccessible { description "The address is not accessible because the interface to which this address is assigned is not operational."; } enum unknown { description "The status cannot be determined for some reason."; } enum tentative { description "The uniqueness of the address on the link is being verified. Addresses in this state should not be used for general communication and should only be used to determine the uniqueness of the address."; } enum duplicate { description "The address has been determined to be non-unique on the link and so must not be used."; } enum optimistic { description "The address is available for use, subject to restrictions, while its uniqueness on a link is being verified."; } } status deprecated; description "The status of an address. Most of the states correspond to states from the IPv6 Stateless Address Autoconfiguration protocol."; reference "RFC 4293: Management Information Base for the Internet Protocol (IP) - IpAddressStatusTC RFC 4862: IPv6 Stateless Address Autoconfiguration"; } } list neighbor { key "ip"; status deprecated; description "A list of mappings from IPv6 addresses to link-layer addresses. This list represents the Neighbor Cache."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)"; leaf ip { type inet:ipv6-address-no-zone; status deprecated; description "The IPv6 address of the neighbor node."; } leaf link-layer-address { type yang:phys-address; status deprecated; description "The link-layer address of the neighbor node."; } leaf origin { type neighbor-origin; status deprecated; description "The origin of this neighbor entry."; } leaf is-router { type empty; status deprecated; description "Indicates that the neighbor node acts as a router."; } leaf state { type enumeration { enum incomplete { description "Address resolution is in progress, and the link-layer address of the neighbor has not yet been determined."; } enum reachable { description "Roughly speaking, the neighbor is known to have been reachable recently (within tens of seconds ago)."; } enum stale { description "The neighbor is no longer known to be reachable, but until traffic is sent to the neighbor no attempt should be made to verify its reachability."; } enum delay { description "The neighbor is no longer known to be reachable, and traffic has recently been sent to the neighbor. Rather than probe the neighbor immediately, however, delay sending probes for a short while in order to give upper-layer protocols a chance to provide reachability confirmation."; } enum probe { description "The neighbor is no longer known to be reachable, and unicast Neighbor Solicitation probes are being sent to verify reachability."; } } status deprecated; description "The Neighbor Unreachability Detection state of this entry."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) Section 7.3.2"; } } } } } yuma123_2.14/netconf/modules/ietf/ietf-netconf-with-defaults@2011-06-01.yang0000664000175000017500000000752414770023131026252 0ustar vladimirvladimirmodule ietf-netconf-with-defaults { namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults"; prefix ncwd; import ietf-netconf { prefix nc; } organization "IETF NETCONF (Network Configuration Protocol) Working Group"; contact "WG Web: WG List: WG Chair: Bert Wijnen WG Chair: Mehmet Ersue Editor: Andy Bierman Editor: Balazs Lengyel "; description "This module defines an extension to the NETCONF protocol that allows the NETCONF client to control how default values are handled by the server in particular NETCONF operations. Copyright (c) 2011 IETF Trust and the persons identified as the document authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 6243; see the RFC itself for full legal notices."; revision 2011-06-01 { description "Initial version."; reference "RFC 6243: With-defaults Capability for NETCONF"; } typedef with-defaults-mode { description "Possible modes to report default data."; reference "RFC 6243; Section 3."; type enumeration { enum report-all { description "All default data is reported."; reference "RFC 6243; Section 3.1"; } enum report-all-tagged { description "All default data is reported. Any nodes considered to be default data will contain a 'default' XML attribute, set to 'true' or '1'."; reference "RFC 6243; Section 3.4"; } enum trim { description "Values are not reported if they contain the default."; reference "RFC 6243; Section 3.2"; } enum explicit { description "Report values that contain the definition of explicitly set data."; reference "RFC 6243; Section 3.3"; } } } grouping with-defaults-parameters { description "Contains the parameter for control of defaults in NETCONF retrieval operations."; leaf with-defaults { description "The explicit defaults processing mode requested."; reference "RFC 6243; Section 4.5.1"; type with-defaults-mode; } } // extending the get-config operation augment /nc:get-config/nc:input { description "Adds the parameter to the input of the NETCONF operation."; reference "RFC 6243; Section 4.5.1"; uses with-defaults-parameters; } // extending the get operation augment /nc:get/nc:input { description "Adds the parameter to the input of the NETCONF operation."; reference "RFC 6243; Section 4.5.1"; uses with-defaults-parameters; } // extending the copy-config operation augment /nc:copy-config/nc:input { description "Adds the parameter to the input of the NETCONF operation."; reference "RFC 6243; Section 4.5.1"; uses with-defaults-parameters; } } yuma123_2.14/netconf/modules/ietf/iana-hardware@2018-03-13.yang0000664000175000017500000001377414770023131023631 0ustar vladimirvladimirmodule iana-hardware { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:iana-hardware"; prefix ianahw; organization "IANA"; contact " Internet Assigned Numbers Authority Postal: ICANN 12025 Waterfront Drive, Suite 300 Los Angeles, CA 90094-2536 United States of America Tel: +1 310 301 5800 E-Mail: iana@iana.org>"; description "IANA-defined identities for hardware class. The latest revision of this YANG module can be obtained from the IANA website. Requests for new values should be made to IANA via email (iana@iana.org). Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). The initial version of this YANG module is part of RFC 8348; see the RFC itself for full legal notices."; reference "https://www.iana.org/assignments/yang-parameters"; revision 2018-03-13 { description "Initial revision."; reference "RFC 8348: A YANG Data Model for Hardware Management"; } /* * Identities */ identity hardware-class { description "This identity is the base for all hardware class identifiers."; } identity unknown { base ianahw:hardware-class; description "This identity is applicable if the hardware class is unknown to the server."; } identity chassis { base ianahw:hardware-class; description "This identity is applicable if the hardware class is an overall container for networking equipment. Any class of physical component, except a stack, may be contained within a chassis; a chassis may only be contained within a stack."; } identity backplane { base ianahw:hardware-class; description "This identity is applicable if the hardware class is some sort of device for aggregating and forwarding networking traffic, such as a shared backplane in a modular ethernet switch. Note that an implementation may model a backplane as a single physical component, which is actually implemented as multiple discrete physical components (within a chassis or stack)."; } identity container { base ianahw:hardware-class; description "This identity is applicable if the hardware class is capable of containing one or more removable physical entities, possibly of different types. For example, each (empty or full) slot in a chassis will be modeled as a container. Note that all removable physical components should be modeled within a container component, such as field-replaceable modules, fans, or power supplies. Note that all known containers should be modeled by the agent, including empty containers."; } identity power-supply { base ianahw:hardware-class; description "This identity is applicable if the hardware class is a power-supplying component."; } identity fan { base ianahw:hardware-class; description "This identity is applicable if the hardware class is a fan or other heat-reduction component."; } identity sensor { base ianahw:hardware-class; description "This identity is applicable if the hardware class is some sort of sensor, such as a temperature sensor within a router chassis."; } identity module { base ianahw:hardware-class; description "This identity is applicable if the hardware class is some sort of self-contained sub-system. If a module component is removable, then it should be modeled within a container component; otherwise, it should be modeled directly within another physical component (e.g., a chassis or another module)."; } identity port { base ianahw:hardware-class; description "This identity is applicable if the hardware class is some sort of networking port capable of receiving and/or transmitting networking traffic."; } identity stack { base ianahw:hardware-class; description "This identity is applicable if the hardware class is some sort of super-container (possibly virtual) intended to group together multiple chassis entities. A stack may be realized by a virtual cable, a real interconnect cable attached to multiple chassis, or multiple interconnect cables. A stack should not be modeled within any other physical components, but a stack may be contained within another stack. Only chassis components should be contained within a stack."; } identity cpu { base ianahw:hardware-class; description "This identity is applicable if the hardware class is some sort of central processing unit."; } identity energy-object { base ianahw:hardware-class; description "This identity is applicable if the hardware class is some sort of energy object, i.e., it is a piece of equipment that is part of or attached to a communications network that is monitored, it is controlled, or it aids in the management of another device for Energy Management."; } identity battery { base ianahw:hardware-class; description "This identity is applicable if the hardware class is some sort of battery."; } identity storage-drive { base ianahw:hardware-class; description "This identity is applicable if the hardware class is some sort of component with data storage capability as its main functionality, e.g., hard disk drive (HDD), solid-state device (SSD), solid-state hybrid drive (SSHD), object storage device (OSD), or other."; } } yuma123_2.14/netconf/modules/ietf/ietf-template@2010-05-18.yang0000664000175000017500000000451714770023131023660 0ustar vladimirvladimirmodule ietf-template { // replace this string with a unique namespace URN value namespace "urn:ietf:params:xml:ns:yang:ietf-template"; // replace this string, and try to pick a unique prefix prefix "temp"; // import statements here: e.g., // import ietf-yang-types { prefix yang; } // import ietf-inet-types { prefix inet; } // identify the IETF working group if applicable organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; // update this contact statement with your info contact "WG Web: WG List: WG Chair: your-WG-chair Editor: your-name "; // replace the first sentence in this description statement. // replace the copyright notice with the most recent // version, if it has been updated since the publication // of this document description "This module defines a template for other YANG modules. Copyright (c) IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX; see the RFC itself for full legal notices."; // RFC Ed.: replace XXXX with actual RFC number and remove this note reference "RFC XXXX"; // RFC Ed.: remove this note // Note: extracted from RFC 6087 // replace '2010-05-18' with the module publication date // The format is (year-month-day) revision "2010-05-18" { description "Initial version"; } // extension statements // feature statements // identity statements // typedef statements // grouping statements // data definition statements // augment statements // rpc statements // notification statements // DO NOT put deviation statements in a published module } yuma123_2.14/netconf/modules/ietf/ietf-alarms@2019-09-11.yang0000664000175000017500000014745714770023131023345 0ustar vladimirvladimirmodule ietf-alarms { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-alarms"; prefix al; import ietf-yang-types { prefix yang; reference "RFC 6991: Common YANG Data Types."; } organization "IETF CCAMP Working Group"; contact "WG Web: WG List: Editor: Stefan Vallin Editor: Martin Bjorklund "; description "This module defines an interface for managing alarms. Main inputs to the module design are the 3GPP Alarm Integration Reference Point (IRP), ITU-T X.733, and ANSI/ISA-18.2 alarm standards. Main features of this module include: * Alarm list: A list of all alarms. Cleared alarms stay in the list until explicitly purged. * Operator actions on alarms: Acknowledging and closing alarms. * Administrative actions on alarms: Purging alarms from the list according to specific criteria. * Alarm inventory: A management application can read all alarm types implemented by the system. * Alarm shelving: Shelving (blocking) alarms according to specific criteria. * Alarm profiles: A management system can attach further information to alarm types, for example, overriding system-default severity levels. This module uses a stateful view on alarms. An alarm is a state for a specific resource (note that an alarm is not a notification). An alarm type is a possible alarm state for a resource. For example, the tuple: ('link-alarm', 'GigabitEthernet0/25') is an alarm of type 'link-alarm' on the resource 'GigabitEthernet0/25'. Alarm types are identified using YANG identities and an optional string-based qualifier. The string-based qualifier allows for dynamic extension of the statically defined alarm types. Alarm types identify a possible alarm state and not the individual notifications. For example, the traditional 'link-down' and 'link-up' notifications are two notifications referring to the same alarm type 'link-alarm'. With this design, there is no ambiguity about how alarm and alarm clear correlation should be performed; notifications that report the same resource and alarm type are considered updates of the same alarm, e.g., clearing an active alarm or changing the severity of an alarm. The instrumentation can update the severity and alarm text on an existing alarm. The above alarm example can therefore look like the following: (('link-alarm', 'GigabitEthernet0/25'), warning, 'interface down while interface admin state is up') There is a clear separation between updates on the alarm from the underlying resource, like clear, and updates from an operator, like acknowledging or closing an alarm: (('link-alarm', 'GigabitEthernet0/25'), warning, 'interface down while interface admin state is up', cleared, closed) Administrative actions like removing closed alarms older than a given time is supported. This YANG module does not define how the underlying instrumentation detects and clears the specific alarms. That belongs to the Standards Development Organization (SDO) or enterprise that owns that specific technology. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here. Copyright (c) 2019 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8632; see the RFC itself for full legal notices."; revision 2019-09-11 { description "Initial revision."; reference "RFC 8632: A YANG Data Model for Alarm Management"; } /* * Features */ feature operator-actions { description "This feature indicates that the system supports operator states on alarms."; } feature alarm-shelving { description "This feature indicates that the system supports shelving (blocking) alarms. Alarm shelving may have an impact on server processing resources in order to match alarms against shelf criteria."; } feature alarm-history { description "This feature indicates that the server maintains a history of state changes for each alarm. For example, if an alarm toggles between cleared and active 10 times, these state changes are present in a separate list in the alarm. Keeping the alarm history may have an impact on server memory resources."; } feature alarm-summary { description "This feature indicates that the server summarizes the number of alarms per severity and operator state."; } feature alarm-profile { description "The system enables clients to configure further information to each alarm type."; } feature severity-assignment { description "The system supports configurable alarm severity levels."; reference "ITU-T Recommendation M.3100: Generic network information model ITU-T Recommendation M.3160: Generic, protocol-neutral management information model"; } feature root-cause-analysis { description "The system supports identifying candidate root-cause resources for an alarm, for example, a disk partition root cause for a logger failure alarm."; } feature service-impact-analysis { description "The system supports identifying candidate-impacted resources for an alarm, for example, an interface state change resulting in a link alarm, which can refer to a link as being impacted."; } feature alarm-correlation { description "The system supports correlating/grouping alarms that belong together."; } /* * Identities */ identity alarm-type-id { description "Base identity for alarm types. A unique identification of the alarm, not including the resource. Different resources can share alarm types. If the resource reports the same alarm type, it is considered to be the same alarm. The alarm type is a simplification of the different X.733 and 3GPP Alarm IRP correlation mechanisms, and it allows for hierarchical extensions. A string-based qualifier can be used in addition to the identity in order to have different alarm types based on information not known at design time, such as values in textual SNMP Notification varbinds. Standards and vendors can define sub-identities to clearly identify specific alarm types. This identity is abstract and MUST NOT be used for alarms."; } /* * Common types */ typedef resource { type union { type instance-identifier { require-instance false; } type yang:object-identifier; type string; type yang:uuid; } description "This is an identification of the alarming resource, such as an interface. It should be as fine-grained as possible to both guide the operator and guarantee uniqueness of the alarms. If the alarming resource is modeled in YANG, this type will be an instance-identifier. If the resource is an SNMP object, the type will be an 'object-identifier'. If the resource is anything else, for example, a distinguished name or a Common Information Model (CIM) path, this type will be a string. If the alarming object is identified by a Universally Unique Identifier (UUID), use the uuid type. Be cautious when using this type, since a UUID is hard to use for an operator. If the server supports several models, the precedence should be in the order as given in the union definition."; } typedef resource-match { type union { type yang:xpath1.0; type yang:object-identifier; type string; } description "This type is used to match resources of type 'resource'. Since the type 'resource' is a union of different types, the 'resource-match' type is also a union of corresponding types. If the type is given as an XPath 1.0 expression, a resource of type 'instance-identifier' matches if the instance is part of the node set that is the result of evaluating the XPath 1.0 expression. For example, the XPath 1.0 expression: /ietf-interfaces:interfaces/ietf-interfaces:interface [ietf-interfaces:type='ianaift:ethernetCsmacd'] would match the resource instance-identifier: /if:interfaces/if:interface[if:name='eth1'], assuming that the interface 'eth1' is of type 'ianaift:ethernetCsmacd'. If the type is given as an object identifier, a resource of type 'object-identifier' matches if the match object identifier is a prefix of the resource's object identifier. For example, the value: 1.3.6.1.2.1.2.2 would match the resource object identifier: 1.3.6.1.2.1.2.2.1.1.5 If the type is given as an UUID or a string, it is interpreted as an XML Schema regular expression, which matches a resource of type 'yang:uuid' or 'string' if the given regular expression matches the resource string. If the type is given as an XPath expression, it is evaluated in the following XPath context: o The set of namespace declarations is the set of prefix and namespace pairs for all YANG modules implemented by the server, where the prefix is the YANG module name and the namespace is as defined by the 'namespace' statement in the YANG module. If a leaf of this type is encoded in XML, all namespace declarations in scope on the leaf element are added to the set of namespace declarations. If a prefix found in the XML is already present in the set of namespace declarations, the namespace in the XML is used. o The set of variable bindings is empty. o The function library is the core function library, and the functions are defined in Section 10 of RFC 7950. o The context node is the root node in the data tree."; reference "XML Schema Part 2: Datatypes Second Edition, World Wide Web Consortium Recommendation REC-xmlschema-2-20041028"; } typedef alarm-text { type string; description "The string used to inform operators about the alarm. This MUST contain enough information for an operator to be able to understand the problem and how to resolve it. If this string contains structure, this format should be clearly documented for programs to be able to parse that information."; } typedef severity { type enumeration { enum indeterminate { value 2; description "Indicates that the severity level could not be determined. This level SHOULD be avoided."; } enum warning { value 3; description "The 'warning' severity level indicates the detection of a potential or impending service-affecting fault, before any significant effects have been felt. Action should be taken to further diagnose (if necessary) and correct the problem in order to prevent it from becoming a more serious service-affecting fault."; } enum minor { value 4; description "The 'minor' severity level indicates the existence of a non-service-affecting fault condition and that corrective action should be taken in order to prevent a more serious (for example, service-affecting) fault. Such a severity can be reported, for example, when the detected alarm condition is not currently degrading the capacity of the resource."; } enum major { value 5; description "The 'major' severity level indicates that a service- affecting condition has developed and an urgent corrective action is required. Such a severity can be reported, for example, when there is a severe degradation in the capability of the resource and its full capability must be restored."; } enum critical { value 6; description "The 'critical' severity level indicates that a service- affecting condition has occurred and an immediate corrective action is required. Such a severity can be reported, for example, when a resource becomes totally out of service and its capability must be restored."; } } description "The severity level of the alarm. Note well that the value 'clear' is not included. Whether or not an alarm is cleared is a separate boolean flag."; reference "ITU-T Recommendation X.733: Information Technology - Open Systems Interconnection - System Management: Alarm Reporting Function"; } typedef severity-with-clear { type union { type enumeration { enum cleared { value 1; description "The alarm is cleared by the instrumentation."; } } type severity; } description "The severity level of the alarm including clear. This is used only in notifications reporting state changes for an alarm."; } typedef writable-operator-state { type enumeration { enum none { value 1; description "The alarm is not being taken care of."; } enum ack { value 2; description "The alarm is being taken care of. Corrective action not taken yet or has failed"; } enum closed { value 3; description "Corrective action taken successfully."; } } description "Operator states on an alarm. The 'closed' state indicates that an operator considers the alarm being resolved. This is separate from the alarm's 'is-cleared' leaf."; } typedef operator-state { type union { type writable-operator-state; type enumeration { enum shelved { value 4; description "The alarm is shelved. Alarms in /alarms/shelved-alarms/ MUST be assigned this operator state by the server as the last entry in the 'operator-state-change' list. The text for that entry SHOULD include the shelf name."; } enum un-shelved { value 5; description "The alarm is moved back to 'alarm-list' from a shelf. Alarms that are moved from /alarms/shelved-alarms/ to /alarms/alarm-list MUST be assigned this state by the server as the last entry in the 'operator-state-change' list. The text for that entry SHOULD include the shelf name."; } } } description "Operator states on an alarm. The 'closed' state indicates that an operator considers the alarm being resolved. This is separate from the alarm's 'is-cleared' leaf."; } /* Alarm type */ typedef alarm-type-id { type identityref { base alarm-type-id; } description "Identifies an alarm type. The description of the alarm type id MUST indicate whether or not the alarm type is abstract. An abstract alarm type is used as a base for other alarm type ids and will not be used as a value for an alarm or be present in the alarm inventory."; } typedef alarm-type-qualifier { type string; description "If an alarm type cannot be fully specified at design time by 'alarm-type-id', this string qualifier is used in addition to fully define a unique alarm type. The definition of alarm qualifiers is considered to be part of the instrumentation and is out of scope for this module. An empty string is used when this is part of a key."; } /* * Groupings */ grouping common-alarm-parameters { description "Common parameters for an alarm. This grouping is used both in the alarm list and in the notification representing an alarm-state change."; leaf resource { type resource; mandatory true; description "The alarming resource. See also 'alt-resource'. This could be, for example, a reference to the alarming interface"; } leaf alarm-type-id { type alarm-type-id; mandatory true; description "This leaf and the leaf 'alarm-type-qualifier' together provide a unique identification of the alarm type."; } leaf alarm-type-qualifier { type alarm-type-qualifier; description "This leaf is used when the 'alarm-type-id' leaf cannot uniquely identify the alarm type. Normally, this is not the case, and this leaf is the empty string."; } leaf-list alt-resource { type resource; description "Used if the alarming resource is available over other interfaces. This field can contain SNMP OIDs, CIM paths, or 3GPP distinguished names, for example."; } list related-alarm { if-feature "alarm-correlation"; key "resource alarm-type-id alarm-type-qualifier"; description "References to related alarms. Note that the related alarm might have been purged from the alarm list."; leaf resource { type leafref { path "/alarms/alarm-list/alarm/resource"; require-instance false; } description "The alarming resource for the related alarm."; } leaf alarm-type-id { type leafref { path "/alarms/alarm-list/alarm" + "[resource=current()/../resource]" + "/alarm-type-id"; require-instance false; } description "The alarm type identifier for the related alarm."; } leaf alarm-type-qualifier { type leafref { path "/alarms/alarm-list/alarm" + "[resource=current()/../resource]" + "[alarm-type-id=current()/../alarm-type-id]" + "/alarm-type-qualifier"; require-instance false; } description "The alarm qualifier for the related alarm."; } } leaf-list impacted-resource { if-feature "service-impact-analysis"; type resource; description "Resources that might be affected by this alarm. If the system creates an alarm on a resource and also has a mapping to other resources that might be impacted, these resources can be listed in this leaf-list. In this way, the system can create one alarm instead of several. For example, if an interface has an alarm, the 'impacted-resource' can reference the aggregated port channels."; } leaf-list root-cause-resource { if-feature "root-cause-analysis"; type resource; description "Resources that are candidates for causing the alarm. If the system has a mechanism to understand the candidate root causes of an alarm, this leaf-list can be used to list the root-cause candidate resources. In this way, the system can create one alarm instead of several. An example might be a logging system (alarm resource) that fails; the alarm can reference the file system in the 'root-cause-resource' leaf-list. Note that the intended use is not to also send an alarm with the 'root-cause-resource' as an alarming resource. The 'root-cause-resource' leaf-list is a hint and should not also generate an alarm for the same problem."; } } grouping alarm-state-change-parameters { description "Parameters for an alarm-state change. This grouping is used both in the alarm list's status-change list and in the notification representing an alarm-state change."; leaf time { type yang:date-and-time; mandatory true; description "The time the status of the alarm changed. The value represents the time the real alarm-state change appeared in the resource and not when it was added to the alarm list. The /alarm-list/alarm/last-changed MUST be set to the same value."; } leaf perceived-severity { type severity-with-clear; mandatory true; description "The severity of the alarm as defined by X.733. Note that this may not be the original severity since the alarm may have changed severity."; reference "ITU-T Recommendation X.733: Information Technology - Open Systems Interconnection - System Management: Alarm Reporting Function"; } leaf alarm-text { type alarm-text; mandatory true; description "A user-friendly text describing the alarm-state change."; reference "ITU-T Recommendation X.733: Information Technology - Open Systems Interconnection - System Management: Alarm Reporting Function"; } } grouping operator-parameters { description "This grouping defines parameters that can be changed by an operator."; leaf time { type yang:date-and-time; mandatory true; description "Timestamp for operator action on the alarm."; } leaf operator { type string; mandatory true; description "The name of the operator that has acted on this alarm."; } leaf state { type operator-state; mandatory true; description "The operator's view of the alarm state."; } leaf text { type string; description "Additional optional textual information provided by the operator."; } } grouping resource-alarm-parameters { description "Alarm parameters that originate from the resource view."; leaf is-cleared { type boolean; mandatory true; description "Indicates the current clearance state of the alarm. An alarm might toggle from active alarm to cleared alarm and back to active again."; } leaf last-raised { type yang:date-and-time; mandatory true; description "An alarm may change severity level and toggle between active and cleared during its lifetime. This leaf indicates the last time it was raised ('is-cleared' = 'false')."; } leaf last-changed { type yang:date-and-time; mandatory true; description "A timestamp when the 'status-change' or 'operator-state-change' list was last changed."; } leaf perceived-severity { type severity; mandatory true; description "The last severity of the alarm. If an alarm was raised with severity 'warning' but later changed to 'major', this leaf will show 'major'."; } leaf alarm-text { type alarm-text; mandatory true; description "The last reported alarm text. This text should contain information for an operator to be able to understand the problem and how to resolve it."; } list status-change { if-feature "alarm-history"; key "time"; min-elements 1; description "A list of status-change events for this alarm. The entry with latest timestamp in this list MUST correspond to the leafs 'is-cleared', 'perceived-severity', and 'alarm-text' for the alarm. This list is ordered according to the timestamps of alarm state changes. The first item corresponds to the latest state change. The following state changes create an entry in this list: - changed severity (warning, minor, major, critical) - clearance status; this also updates the 'is-cleared' leaf - alarm-text update"; uses alarm-state-change-parameters; } } grouping filter-input { description "Grouping to specify a filter construct on alarm information."; leaf alarm-clearance-status { type enumeration { enum any { description "Ignore alarm-clearance status."; } enum cleared { description "Filter cleared alarms."; } enum not-cleared { description "Filter not-cleared alarms."; } } mandatory true; description "The clearance status of the alarm."; } container older-than { presence "Age specification"; description "Matches the 'last-status-change' leaf in the alarm."; choice age-spec { description "Filter using date and time age."; case seconds { leaf seconds { type uint16; description "Age expressed in seconds."; } } case minutes { leaf minutes { type uint16; description "Age expressed in minutes."; } } case hours { leaf hours { type uint16; description "Age expressed in hours."; } } case days { leaf days { type uint16; description "Age expressed in days."; } } case weeks { leaf weeks { type uint16; description "Age expressed in weeks."; } } } } container severity { presence "Severity filter"; choice sev-spec { description "Filter based on severity level."; leaf below { type severity; description "Severity less than this leaf."; } leaf is { type severity; description "Severity level equal to this leaf."; } leaf above { type severity; description "Severity level higher than this leaf."; } } description "Filter based on severity."; } container operator-state-filter { if-feature "operator-actions"; presence "Operator state filter"; leaf state { type operator-state; description "Filter on operator state."; } leaf user { type string; description "Filter based on which operator."; } description "Filter based on operator state."; } } /* * The /alarms data tree */ container alarms { description "The top container for this module."; container control { description "Configuration to control the alarm behavior."; leaf max-alarm-status-changes { type union { type uint16; type enumeration { enum infinite { description "The status-change entries are accumulated infinitely."; } } } default "32"; description "The 'status-change' entries are kept in a circular list per alarm. When this number is exceeded, the oldest status change entry is automatically removed. If the value is 'infinite', the status-change entries are accumulated infinitely."; } leaf notify-status-changes { type enumeration { enum all-state-changes { description "Send notifications for all status changes."; } enum raise-and-clear { description "Send notifications only for raise, clear, and re-raise. Notifications for severity-level changes or alarm-text changes are not sent."; } enum severity-level { description "Only send notifications for alarm-state changes crossing the level specified in 'notify-severity-level'. Always send clear notifications."; } } must '. != "severity-level" or ../notify-severity-level' { description "When notify-status-changes is 'severity-level', a value must be given for 'notify-severity-level'."; } default "all-state-changes"; description "This leaf controls the notifications sent for alarm status updates. There are three options: 1. Notifications are sent for all updates, severity-level changes, and alarm-text changes. 2. Notifications are only sent for alarm raise and clear. 3. Notifications are sent for status changes equal to or above the specified severity level. Clear notifications shall always be sent. Notifications shall also be sent for state changes that make an alarm less severe than the specified level. For example, in option 3, assume that the severity level is set to major and that the alarm has the following state changes: [(Time, severity, clear)]: [(T1, major, -), (T2, minor, -), (T3, warning, -), (T4, minor, -), (T5, major, -), (T6, critical, -), (T7, major. -), (T8, major, clear)] In that case, notifications will be sent at times T1, T2, T5, T6, T7, and T8."; } leaf notify-severity-level { when '../notify-status-changes = "severity-level"'; type severity; description "Only send notifications for alarm-state changes crossing the specified level. Always send clear notifications."; } container alarm-shelving { if-feature "alarm-shelving"; description "The 'alarm-shelving/shelf' list is used to shelve (block/filter) alarms. The conditions in the shelf criteria are logically ANDed. The first matching shelf is used, and an alarm is shelved only for this first match. Matching alarms MUST appear in the /alarms/shelved-alarms/shelved-alarm list, and non-matching /alarms MUST appear in the /alarms/alarm-list/alarm list. The server does not send any notifications for shelved alarms. The server MUST maintain states (e.g., severity changes) for the shelved alarms. Alarms that match the criteria shall have an operator state 'shelved'. When the shelf configuration removes an alarm from the shelf, the server shall add the operator state 'un-shelved'."; list shelf { key "name"; ordered-by user; leaf name { type string; description "An arbitrary name for the alarm shelf."; } description "Each entry defines the criteria for shelving alarms. Criteria are ANDed. If no criteria are specified, all alarms will be shelved."; leaf-list resource { type resource-match; description "Shelve alarms for matching resources."; } list alarm-type { key "alarm-type-id alarm-type-qualifier-match"; description "Any alarm matching the combined criteria of 'alarm-type-id' and 'alarm-type-qualifier-match' MUST be matched."; leaf alarm-type-id { type alarm-type-id; description "Shelve all alarms that have an 'alarm-type-id' that is equal to or derived from the given 'alarm-type-id'."; } leaf alarm-type-qualifier-match { type string; description "An XML Schema regular expression that is used to match an alarm type qualifier. Shelve all alarms that match this regular expression for the alarm type qualifier."; reference "XML Schema Part 2: Datatypes Second Edition, World Wide Web Consortium Recommendation REC-xmlschema-2-20041028"; } } leaf description { type string; description "An optional textual description of the shelf. This description should include the reason for shelving these alarms."; } } } } container alarm-inventory { config false; description "The 'alarm-inventory/alarm-type' list contains all possible alarm types for the system. If the system knows for which resources a specific alarm type can appear, it is also identified in the inventory. The list also tells if each alarm type has a corresponding clear state. The inventory shall only contain concrete alarm types. The alarm inventory MUST be updated by the system when new alarms can appear. This can be the case when installing new software modules or inserting new card types. A notification 'alarm-inventory-changed' is sent when the inventory is changed."; list alarm-type { key "alarm-type-id alarm-type-qualifier"; description "An entry in this list defines a possible alarm."; leaf alarm-type-id { type alarm-type-id; description "The statically defined alarm type identifier for this possible alarm."; } leaf alarm-type-qualifier { type alarm-type-qualifier; description "The optionally dynamically defined alarm type identifier for this possible alarm."; } leaf-list resource { type resource-match; description "Optionally, specifies for which resources the alarm type is valid."; } leaf will-clear { type boolean; mandatory true; description "This leaf tells the operator if the alarm will be cleared when the correct corrective action has been taken. Implementations SHOULD strive for detecting the cleared state for all alarm types. If this leaf is 'true', the operator can monitor the alarm until it becomes cleared after the corrective action has been taken. If this leaf is 'false', the operator needs to validate that the alarm is no longer active using other mechanisms. Alarms can lack a corresponding clear due to missing instrumentation or no logical corresponding clear state."; } leaf-list severity-level { type severity; description "This leaf-list indicates the possible severity levels of this alarm type. Note well that 'clear' is not part of the severity type. In general, the severity level should be defined by the instrumentation based on the dynamic state, rather than being defined statically by the alarm type, in order to provide a relevant severity level based on dynamic state and context. However, most alarm types have a defined set of possible severity levels, and this should be provided here."; } leaf description { type string; mandatory true; description "A description of the possible alarm. It SHOULD include information on possible underlying root causes and corrective actions."; } } } container summary { if-feature "alarm-summary"; config false; description "This container gives a summary of the number of alarms."; list alarm-summary { key "severity"; description "A global summary of all alarms in the system. The summary does not include shelved alarms."; leaf severity { type severity; description "Alarm summary for this severity level."; } leaf total { type yang:gauge32; description "Total number of alarms of this severity level."; } leaf not-cleared { type yang:gauge32; description "Total number of alarms of this severity level that are not cleared."; } leaf cleared { type yang:gauge32; description "For this severity level, the number of alarms that are cleared."; } leaf cleared-not-closed { if-feature "operator-actions"; type yang:gauge32; description "For this severity level, the number of alarms that are cleared but not closed."; } leaf cleared-closed { if-feature "operator-actions"; type yang:gauge32; description "For this severity level, the number of alarms that are cleared and closed."; } leaf not-cleared-closed { if-feature "operator-actions"; type yang:gauge32; description "For this severity level, the number of alarms that are not cleared but closed."; } leaf not-cleared-not-closed { if-feature "operator-actions"; type yang:gauge32; description "For this severity level, the number of alarms that are not cleared and not closed."; } } leaf shelves-active { if-feature "alarm-shelving"; type empty; description "This is a hint to the operator that there are active alarm shelves. This leaf MUST exist if the /alarms/shelved-alarms/number-of-shelved-alarms is > 0."; } } container alarm-list { config false; description "The alarms in the system."; leaf number-of-alarms { type yang:gauge32; description "This object shows the total number of alarms in the system, i.e., the total number of entries in the alarm list."; } leaf last-changed { type yang:date-and-time; description "A timestamp when the alarm list was last changed. The value can be used by a manager to initiate an alarm resynchronization procedure."; } list alarm { key "resource alarm-type-id alarm-type-qualifier"; description "The list of alarms. Each entry in the list holds one alarm for a given alarm type and resource. An alarm can be updated from the underlying resource or by the user. The following leafs are maintained by the resource: 'is-cleared', 'last-change', 'perceived-severity', and 'alarm-text'. An operator can change 'operator-state' and 'operator-text'. Entries appear in the alarm list the first time an alarm becomes active for a given alarm type and resource. Entries do not get deleted when the alarm is cleared. Clear status is represented as a boolean flag. Alarm entries are removed, i.e., purged, from the list by an explicit purge action. For example, purge all alarms that are cleared and in closed operator state that are older than 24 hours. Purged alarms are removed from the alarm list. If the alarm resource state changes after a purge, the alarm will reappear in the alarm list. Systems may also remove alarms based on locally configured policies; this is out of scope for this module."; uses common-alarm-parameters; leaf time-created { type yang:date-and-time; mandatory true; description "The timestamp when this alarm entry was created. This represents the first time the alarm appeared; it can also represent that the alarm reappeared after a purge. Further state changes of the same alarm do not change this leaf; these changes will update the 'last-changed' leaf."; } uses resource-alarm-parameters; list operator-state-change { if-feature "operator-actions"; key "time"; description "This list is used by operators to indicate the state of human intervention on an alarm. For example, if an operator has seen an alarm, the operator can add a new item to this list indicating that the alarm is acknowledged."; uses operator-parameters; } action set-operator-state { if-feature "operator-actions"; description "This is a means for the operator to indicate the level of human intervention on an alarm."; input { leaf state { type writable-operator-state; mandatory true; description "Set this operator state."; } leaf text { type string; description "Additional optional textual information."; } } } notification operator-action { if-feature "operator-actions"; description "This notification is used to report that an operator acted upon an alarm."; uses operator-parameters; } } action purge-alarms { description "This operation requests that the server delete entries from the alarm list according to the supplied criteria. Typically, this operation is used to delete alarms that are in closed operator state and older than a specified time. The number of purged alarms is returned as an output parameter."; input { uses filter-input; } output { leaf purged-alarms { type uint32; description "Number of purged alarms."; } } } action compress-alarms { if-feature "alarm-history"; description "This operation requests that the server compress entries in the alarm list by removing all but the latest 'status-change' entry for all matching alarms. Conditions in the input are logically ANDed. If no input condition is given, all alarms are compressed."; input { leaf resource { type resource-match; description "Compress the alarms matching this resource."; } leaf alarm-type-id { type leafref { path "/alarms/alarm-list/alarm/alarm-type-id"; require-instance false; } description "Compress alarms with this 'alarm-type-id'."; } leaf alarm-type-qualifier { type leafref { path "/alarms/alarm-list/alarm/alarm-type-qualifier"; require-instance false; } description "Compress the alarms with this 'alarm-type-qualifier'."; } } output { leaf compressed-alarms { type uint32; description "Number of compressed alarm entries."; } } } } container shelved-alarms { if-feature "alarm-shelving"; config false; description "The shelved alarms. Alarms appear here if they match the criteria in /alarms/control/alarm-shelving. This list does not generate any notifications. The list represents alarms that are considered not relevant by the operator. Alarms in this list have an 'operator-state' of 'shelved'. This cannot be changed."; leaf number-of-shelved-alarms { type yang:gauge32; description "This object shows the total number of current alarms, i.e., the total number of entries in the alarm list."; } leaf shelved-alarms-last-changed { type yang:date-and-time; description "A timestamp when the shelved-alarm list was last changed. The value can be used by a manager to initiate an alarm resynchronization procedure."; } list shelved-alarm { key "resource alarm-type-id alarm-type-qualifier"; description "The list of shelved alarms. Shelved alarms can only be updated from the underlying resource; no operator actions are supported."; uses common-alarm-parameters; leaf shelf-name { type leafref { path "/alarms/control/alarm-shelving/shelf/name"; require-instance false; } description "The name of the shelf."; } uses resource-alarm-parameters; list operator-state-change { if-feature "operator-actions"; key "time"; description "This list is used by operators to indicate the state of human intervention on an alarm. For shelved alarms, the system has set the list item in the list to 'shelved'."; uses operator-parameters; } } action purge-shelved-alarms { description "This operation requests that the server delete entries from the shelved-alarm list according to the supplied criteria. In the shelved-alarm list, it makes sense to delete alarms that are not relevant anymore. The number of purged alarms is returned as an output parameter."; input { uses filter-input; } output { leaf purged-alarms { type uint32; description "Number of purged alarms."; } } } action compress-shelved-alarms { if-feature "alarm-history"; description "This operation requests that the server compress entries in the shelved-alarm list by removing all but the latest 'status-change' entry for all matching shelved alarms. Conditions in the input are logically ANDed. If no input condition is given, all alarms are compressed."; input { leaf resource { type leafref { path "/alarms/shelved-alarms/shelved-alarm/resource"; require-instance false; } description "Compress the alarms with this resource."; } leaf alarm-type-id { type leafref { path "/alarms/shelved-alarms/shelved-alarm" + "/alarm-type-id"; require-instance false; } description "Compress alarms with this 'alarm-type-id'."; } leaf alarm-type-qualifier { type leafref { path "/alarms/shelved-alarms/shelved-alarm" + "/alarm-type-qualifier"; require-instance false; } description "Compress the alarms with this 'alarm-type-qualifier'."; } } output { leaf compressed-alarms { type uint32; description "Number of compressed alarm entries."; } } } } list alarm-profile { if-feature "alarm-profile"; key "alarm-type-id alarm-type-qualifier-match resource"; ordered-by user; description "This list is used to assign further information or configuration for each alarm type. This module supports a mechanism where the client can override the system-default alarm severity levels. The 'alarm-profile' is also a useful augmentation point for specific additions to alarm types."; leaf alarm-type-id { type alarm-type-id; description "The alarm type identifier to match."; } leaf alarm-type-qualifier-match { type string; description "An XML Schema regular expression that is used to match the alarm type qualifier."; reference "XML Schema Part 2: Datatypes Second Edition, World Wide Web Consortium Recommendation REC-xmlschema-2-20041028"; } leaf resource { type resource-match; description "Specifies which resources to match."; } leaf description { type string; mandatory true; description "A description of the alarm profile."; } container alarm-severity-assignment-profile { if-feature "severity-assignment"; description "The client can override the system-default severity level."; reference "ITU-T Recommendation M.3100: Generic network information model ITU-T Recommendation M.3160: Generic, protocol-neutral management information model"; leaf-list severity-level { type severity; ordered-by user; description "Specifies the configured severity level(s) for the matching alarm. If the alarm has several severity levels, the leaf-list shall be given in rising severity order. The original M3100/M3160 ASAP function only allows for a one-to-one mapping between alarm type and severity, but since YANG module supports stateful alarms, the mapping must allow for several severity levels. Assume a high-utilization alarm type with two thresholds with the system-default severity levels of threshold1 = warning and threshold2 = minor. Setting this leaf-list to (minor, major) will assign the severity levels as threshold1 = minor and threshold2 = major"; } } } } /* * Notifications */ notification alarm-notification { description "This notification is used to report a state change for an alarm. The same notification is used for reporting a newly raised alarm, a cleared alarm, or changing the text and/or severity of an existing alarm."; uses common-alarm-parameters; uses alarm-state-change-parameters; } notification alarm-inventory-changed { description "This notification is used to report that the list of possible alarms has changed. This can happen when, for example, a new software module is installed or a new physical card is inserted."; } } yuma123_2.14/netconf/modules/ietf/ietf-netconf-nmda@2019-01-07.yang0000664000175000017500000003034314770023131024415 0ustar vladimirvladimirmodule ietf-netconf-nmda { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-nmda"; prefix ncds; import ietf-yang-types { prefix yang; reference "RFC 6991: Common YANG Data Types"; } import ietf-inet-types { prefix inet; reference "RFC 6991: Common YANG Data Types"; } import ietf-datastores { prefix ds; reference "RFC 8342: Network Management Datastore Architecture (NMDA)"; } import ietf-origin { prefix or; reference "RFC 8342: Network Management Datastore Architecture (NMDA)"; } import ietf-netconf { prefix nc; reference "RFC 6241: Network Configuration Protocol (NETCONF)"; } import ietf-netconf-with-defaults { prefix ncwd; reference "RFC 6243: With-defaults Capability for NETCONF"; } organization "IETF NETCONF Working Group"; contact "WG Web: WG List: Author: Martin Bjorklund Author: Juergen Schoenwaelder Author: Phil Shafer Author: Kent Watsen Author: Robert Wilton "; description "This YANG module defines a set of NETCONF operations to support the Network Management Datastore Architecture (NMDA). The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here. Copyright (c) 2019 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8526; see the RFC itself for full legal notices."; revision 2019-01-07 { description "Initial revision."; reference "RFC 8526: NETCONF Extensions to Support the Network Management Datastore Architecture"; } feature origin { description "Indicates that the server supports the 'origin' annotation."; reference "RFC 8342: Network Management Datastore Architecture (NMDA)"; } feature with-defaults { description "NETCONF :with-defaults capability. If the server advertises the :with-defaults capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6243: With-defaults Capability for NETCONF, Section 4; and RFC 8526: NETCONF Extensions to Support the Network Management Datastore Architecture, Section 3.1.1.2"; } rpc get-data { description "Retrieve data from an NMDA datastore. The content returned by get-data must satisfy all filters, i.e., the filter criteria are logically ANDed. Any ancestor nodes (including list keys) of nodes selected by the filters are included in the response. The 'with-origin' parameter is only valid for an operational datastore. If 'with-origin' is used with an invalid datastore, then the server MUST return an element with an value of 'invalid-value'. The 'with-defaults' parameter only applies to the operational datastore if the NETCONF :with-defaults and :with-operational-defaults capabilities are both advertised. If the 'with-defaults' parameter is present in a request for which it is not supported, then the server MUST return an element with an value of 'invalid-value'."; input { leaf datastore { type ds:datastore-ref; mandatory true; description "Datastore from which to retrieve data. If the datastore is not supported by the server, then the server MUST return an element with an value of 'invalid-value'."; } choice filter-spec { description "The content filter specification for this request."; anydata subtree-filter { description "This parameter identifies the portions of the target datastore to retrieve."; reference "RFC 6241: Network Configuration Protocol (NETCONF), Section 6"; } leaf xpath-filter { if-feature "nc:xpath"; type yang:xpath1.0; description "This parameter contains an XPath expression identifying the portions of the target datastore to retrieve. If the expression returns a node-set, all nodes in the node-set are selected by the filter. Otherwise, if the expression does not return a node-set, then the operation fails. The expression is evaluated in the following XPath context: o The set of namespace declarations are those in scope on the 'xpath-filter' leaf element. o The set of variable bindings is empty. o The function library is the core function library, and the XPath functions are defined in Section 10 of RFC 7950. o The context node is the root node of the target datastore."; } } leaf config-filter { type boolean; description "Filter for nodes with the given value for their 'config' property. When this leaf is set to 'true', only 'config true' nodes are selected, and when set to 'false', only 'config false' nodes are selected. If this leaf is not present, no nodes are filtered."; } choice origin-filters { when 'derived-from-or-self(datastore, "ds:operational")'; if-feature "origin"; description "Filters configuration nodes based on the 'origin' annotation. Configuration nodes that do not have an 'origin' annotation are treated as if they have the 'origin' annotation 'or:unknown'. System state nodes are not affected by origin-filters and thus not filtered. Note that system state nodes can be filtered with the 'config-filter' leaf."; leaf-list origin-filter { type or:origin-ref; description "Filter based on the 'origin' annotation. A configuration node matches the filter if its 'origin' annotation is derived from or equal to any of the given filter values."; } leaf-list negated-origin-filter { type or:origin-ref; description "Filter based on the 'origin' annotation. A configuration node matches the filter if its 'origin' annotation is neither derived from nor equal to any of the given filter values."; } } leaf max-depth { type union { type uint16 { range "1..65535"; } type enumeration { enum unbounded { description "All descendant nodes are included."; } } } default "unbounded"; description "For each node selected by the filters, this parameter selects how many conceptual subtree levels should be returned in the reply. If the depth is 1, the reply includes just the selected nodes but no children. If the depth is 'unbounded', all descendant nodes are included."; } leaf with-origin { when 'derived-from-or-self(../datastore, "ds:operational")'; if-feature "origin"; type empty; description "If this parameter is present, the server will return the 'origin' annotation for the nodes that have one."; } uses ncwd:with-defaults-parameters { if-feature "with-defaults"; } } output { anydata data { description "Copy of the source datastore subset that matched the filter criteria (if any). An empty data container indicates that the request did not produce any results."; } } } rpc edit-data { description "Edit data in an NMDA datastore. If an error condition occurs such that an error severity element is generated, the server will stop processing the operation and restore the specified configuration to its complete state at the start of this operation."; input { leaf datastore { type ds:datastore-ref; mandatory true; description "Datastore that is the target of the operation. If the target datastore is not writable, or is not supported by the server, then the server MUST return an element with an value of 'invalid-value'."; } leaf default-operation { type enumeration { enum merge { description "The default operation is merge."; } enum replace { description "The default operation is replace."; } enum none { description "There is no default operation."; } } default "merge"; description "The default operation to use."; } choice edit-content { mandatory true; description "The content for the edit operation."; anydata config { description "Inline config content."; } leaf url { if-feature "nc:url"; type inet:uri; description "URL-based config content."; } } } } /* * Augment the and operations with a * "datastore" parameter. */ augment "/nc:lock/nc:input/nc:target/nc:config-target" { description "Add NMDA datastore as target."; leaf datastore { type ds:datastore-ref; description "Datastore to lock. The operation is only supported on writable datastores. If the operation is not supported by the server on the specified target datastore, then the server MUST return an element with an value of 'invalid-value'."; } } augment "/nc:unlock/nc:input/nc:target/nc:config-target" { description "Add NMDA datastore as target."; leaf datastore { type ds:datastore-ref; description "Datastore to unlock. The operation is only supported on writable datastores. If the operation is not supported by the server on the specified target datastore, then the server MUST return an element with an value of 'invalid-value'."; } } /* * Augment the operation with a * "datastore" parameter. */ augment "/nc:validate/nc:input/nc:source/nc:config-source" { description "Add NMDA datastore as source."; leaf datastore { type ds:datastore-ref; description "Datastore to validate. The operation is supported only on configuration datastores. If the operation is not supported by the server on the specified target datastore, then the server MUST return an element with an value of 'invalid-value'."; } } } yuma123_2.14/netconf/modules/ietf/ietf-snmp-engine@2014-12-10.yang0000664000175000017500000001033214770023131024247 0ustar vladimirvladimirsubmodule ietf-snmp-engine { belongs-to ietf-snmp { prefix snmp; } import ietf-inet-types { prefix inet; } include ietf-snmp-common; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This submodule contains a collection of YANG definitions for configuring SNMP engines. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } augment /snmp:snmp { container engine { description "Configuration of the SNMP engine."; leaf enabled { type boolean; default "false"; description "Enables the SNMP engine."; } list listen { key "name"; description "Configuration of the transport endpoints on which the engine listens."; leaf name { type snmp:identifier; description "An arbitrary name for the list entry."; } choice transport { mandatory true; description "The transport-protocol-specific parameters for this endpoint. Submodules providing configuration for additional transports are expected to augment this choice."; case udp { container udp { leaf ip { type inet:ip-address; mandatory true; description "The IPv4 or IPv6 address on which the engine listens."; } leaf port { type inet:port-number; description "The UDP port on which the engine listens. If the port is not configured, an engine that acts as a Command Responder uses port 161, and an engine that acts as a Notification Receiver uses port 162."; } } } } } container version { description "SNMP version used by the engine."; leaf v1 { type empty; } leaf v2c { type empty; } leaf v3 { type empty; } } leaf engine-id { type snmp:engine-id; description "The local SNMP engine's administratively assigned unique identifier. If this leaf is not set, the device automatically calculates an engine ID, as described in RFC 3411. A server MAY initialize this leaf with the automatically created value."; reference "RFC 3411: An Architecture for Describing Simple Network Management Protocol (SNMP) Management Frameworks. SNMP-FRAMEWORK-MIB.snmpEngineID"; } leaf enable-authen-traps { type boolean; description "Indicates whether the SNMP entity is permitted to generate authenticationFailure traps."; reference "RFC 3418: Management Information Base (MIB) for the Simple Network Management Protocol (SNMP) SNMPv2-MIB.snmpEnableAuthenTraps"; } } } } yuma123_2.14/netconf/modules/ietf/ietf-interfaces@2014-05-08.yang0000664000175000017500000005723314770023131024176 0ustar vladimirvladimirmodule ietf-interfaces { namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces"; prefix if; import ietf-yang-types { prefix yang; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund "; description "This module contains a collection of YANG definitions for managing network interfaces. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7223; see the RFC itself for full legal notices."; revision 2014-05-08 { description "Initial revision."; reference "RFC 7223: A YANG Data Model for Interface Management"; } /* * Typedefs */ typedef interface-ref { type leafref { path "/if:interfaces/if:interface/if:name"; } description "This type is used by data models that need to reference configured interfaces."; } typedef interface-state-ref { type leafref { path "/if:interfaces-state/if:interface/if:name"; } description "This type is used by data models that need to reference the operationally present interfaces."; } /* * Identities */ identity interface-type { description "Base identity from which specific interface types are derived."; } /* * Features */ feature arbitrary-names { description "This feature indicates that the device allows user-controlled interfaces to be named arbitrarily."; } feature pre-provisioning { description "This feature indicates that the device supports pre-provisioning of interface configuration, i.e., it is possible to configure an interface whose physical interface hardware is not present on the device."; } feature if-mib { description "This feature indicates that the device implements the IF-MIB."; reference "RFC 2863: The Interfaces Group MIB"; } /* * Configuration data nodes */ container interfaces { description "Interface configuration parameters."; list interface { key "name"; description "The list of configured interfaces on the device. The operational state of an interface is available in the /interfaces-state/interface list. If the configuration of a system-controlled interface cannot be used by the system (e.g., the interface hardware present does not match the interface type), then the configuration is not applied to the system-controlled interface shown in the /interfaces-state/interface list. If the configuration of a user-controlled interface cannot be used by the system, the configured interface is not instantiated in the /interfaces-state/interface list."; leaf name { type string; description "The name of the interface. A device MAY restrict the allowed values for this leaf, possibly depending on the type of the interface. For system-controlled interfaces, this leaf is the device-specific name of the interface. The 'config false' list /interfaces-state/interface contains the currently existing interfaces on the device. If a client tries to create configuration for a system-controlled interface that is not present in the /interfaces-state/interface list, the server MAY reject the request if the implementation does not support pre-provisioning of interfaces or if the name refers to an interface that can never exist in the system. A NETCONF server MUST reply with an rpc-error with the error-tag 'invalid-value' in this case. If the device supports pre-provisioning of interface configuration, the 'pre-provisioning' feature is advertised. If the device allows arbitrarily named user-controlled interfaces, the 'arbitrary-names' feature is advertised. When a configured user-controlled interface is created by the system, it is instantiated with the same name in the /interface-state/interface list."; } leaf description { type string; description "A textual description of the interface. A server implementation MAY map this leaf to the ifAlias MIB object. Such an implementation needs to use some mechanism to handle the differences in size and characters allowed between this leaf and ifAlias. The definition of such a mechanism is outside the scope of this document. Since ifAlias is defined to be stored in non-volatile storage, the MIB implementation MUST map ifAlias to the value of 'description' in the persistently stored datastore. Specifically, if the device supports ':startup', when ifAlias is read the device MUST return the value of 'description' in the 'startup' datastore, and when it is written, it MUST be written to the 'running' and 'startup' datastores. Note that it is up to the implementation to decide whether to modify this single leaf in 'startup' or perform an implicit copy-config from 'running' to 'startup'. If the device does not support ':startup', ifAlias MUST be mapped to the 'description' leaf in the 'running' datastore."; reference "RFC 2863: The Interfaces Group MIB - ifAlias"; } leaf type { type identityref { base interface-type; } mandatory true; description "The type of the interface. When an interface entry is created, a server MAY initialize the type leaf with a valid value, e.g., if it is possible to derive the type from the name of the interface. If a client tries to set the type of an interface to a value that can never be used by the system, e.g., if the type is not supported or if the type does not match the name of the interface, the server MUST reject the request. A NETCONF server MUST reply with an rpc-error with the error-tag 'invalid-value' in this case."; reference "RFC 2863: The Interfaces Group MIB - ifType"; } leaf enabled { type boolean; default "true"; description "This leaf contains the configured, desired state of the interface. Systems that implement the IF-MIB use the value of this leaf in the 'running' datastore to set IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry has been initialized, as described in RFC 2863. Changes in this leaf in the 'running' datastore are reflected in ifAdminStatus, but if ifAdminStatus is changed over SNMP, this leaf is not affected."; reference "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; } leaf link-up-down-trap-enable { if-feature if-mib; type enumeration { enum enabled { value 1; } enum disabled { value 2; } } description "Controls whether linkUp/linkDown SNMP notifications should be generated for this interface. If this node is not configured, the value 'enabled' is operationally used by the server for interfaces that do not operate on top of any other interface (i.e., there are no 'lower-layer-if' entries), and 'disabled' otherwise."; reference "RFC 2863: The Interfaces Group MIB - ifLinkUpDownTrapEnable"; } } } /* * Operational state data nodes */ container interfaces-state { config false; description "Data nodes for the operational state of interfaces."; list interface { key "name"; description "The list of interfaces on the device. System-controlled interfaces created by the system are always present in this list, whether they are configured or not."; leaf name { type string; description "The name of the interface. A server implementation MAY map this leaf to the ifName MIB object. Such an implementation needs to use some mechanism to handle the differences in size and characters allowed between this leaf and ifName. The definition of such a mechanism is outside the scope of this document."; reference "RFC 2863: The Interfaces Group MIB - ifName"; } leaf type { type identityref { base interface-type; } mandatory true; description "The type of the interface."; reference "RFC 2863: The Interfaces Group MIB - ifType"; } leaf admin-status { if-feature if-mib; type enumeration { enum up { value 1; description "Ready to pass packets."; } enum down { value 2; description "Not ready to pass packets and not in some test mode."; } enum testing { value 3; description "In some test mode."; } } mandatory true; description "The desired state of the interface. This leaf has the same read semantics as ifAdminStatus."; reference "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; } leaf oper-status { type enumeration { enum up { value 1; description "Ready to pass packets."; } enum down { value 2; description "The interface does not pass any packets."; } enum testing { value 3; description "In some test mode. No operational packets can be passed."; } enum unknown { value 4; description "Status cannot be determined for some reason."; } enum dormant { value 5; description "Waiting for some external event."; } enum not-present { value 6; description "Some component (typically hardware) is missing."; } enum lower-layer-down { value 7; description "Down due to state of lower-layer interface(s)."; } } mandatory true; description "The current operational state of the interface. This leaf has the same semantics as ifOperStatus."; reference "RFC 2863: The Interfaces Group MIB - ifOperStatus"; } leaf last-change { type yang:date-and-time; description "The time the interface entered its current operational state. If the current state was entered prior to the last re-initialization of the local network management subsystem, then this node is not present."; reference "RFC 2863: The Interfaces Group MIB - ifLastChange"; } leaf if-index { if-feature if-mib; type int32 { range "1..2147483647"; } mandatory true; description "The ifIndex value for the ifEntry represented by this interface."; reference "RFC 2863: The Interfaces Group MIB - ifIndex"; } leaf phys-address { type yang:phys-address; description "The interface's address at its protocol sub-layer. For example, for an 802.x interface, this object normally contains a Media Access Control (MAC) address. The interface's media-specific modules must define the bit and byte ordering and the format of the value of this object. For interfaces that do not have such an address (e.g., a serial line), this node is not present."; reference "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; } leaf-list higher-layer-if { type interface-state-ref; description "A list of references to interfaces layered on top of this interface."; reference "RFC 2863: The Interfaces Group MIB - ifStackTable"; } leaf-list lower-layer-if { type interface-state-ref; description "A list of references to interfaces layered underneath this interface."; reference "RFC 2863: The Interfaces Group MIB - ifStackTable"; } leaf speed { type yang:gauge64; units "bits/second"; description "An estimate of the interface's current bandwidth in bits per second. For interfaces that do not vary in bandwidth or for those where no accurate estimation can be made, this node should contain the nominal bandwidth. For interfaces that have no concept of bandwidth, this node is not present."; reference "RFC 2863: The Interfaces Group MIB - ifSpeed, ifHighSpeed"; } container statistics { description "A collection of interface-related statistics objects."; leaf discontinuity-time { type yang:date-and-time; mandatory true; description "The time on the most recent occasion at which any one or more of this interface's counters suffered a discontinuity. If no such discontinuities have occurred since the last re-initialization of the local management subsystem, then this node contains the time the local management subsystem re-initialized itself."; } leaf in-octets { type yang:counter64; description "The total number of octets received on the interface, including framing characters. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; } leaf in-unicast-pkts { type yang:counter64; description "The number of packets, delivered by this sub-layer to a higher (sub-)layer, that were not addressed to a multicast or broadcast address at this sub-layer. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; } leaf in-broadcast-pkts { type yang:counter64; description "The number of packets, delivered by this sub-layer to a higher (sub-)layer, that were addressed to a broadcast address at this sub-layer. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCInBroadcastPkts"; } leaf in-multicast-pkts { type yang:counter64; description "The number of packets, delivered by this sub-layer to a higher (sub-)layer, that were addressed to a multicast address at this sub-layer. For a MAC-layer protocol, this includes both Group and Functional addresses. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCInMulticastPkts"; } leaf in-discards { type yang:counter32; description "The number of inbound packets that were chosen to be discarded even though no errors had been detected to prevent their being deliverable to a higher-layer protocol. One possible reason for discarding such a packet could be to free up buffer space. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifInDiscards"; } leaf in-errors { type yang:counter32; description "For packet-oriented interfaces, the number of inbound packets that contained errors preventing them from being deliverable to a higher-layer protocol. For character- oriented or fixed-length interfaces, the number of inbound transmission units that contained errors preventing them from being deliverable to a higher-layer protocol. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifInErrors"; } leaf in-unknown-protos { type yang:counter32; description "For packet-oriented interfaces, the number of packets received via the interface that were discarded because of an unknown or unsupported protocol. For character-oriented or fixed-length interfaces that support protocol multiplexing, the number of transmission units received via the interface that were discarded because of an unknown or unsupported protocol. For any interface that does not support protocol multiplexing, this counter is not present. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; } leaf out-octets { type yang:counter64; description "The total number of octets transmitted out of the interface, including framing characters. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; } leaf out-unicast-pkts { type yang:counter64; description "The total number of packets that higher-level protocols requested be transmitted, and that were not addressed to a multicast or broadcast address at this sub-layer, including those that were discarded or not sent. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; } leaf out-broadcast-pkts { type yang:counter64; description "The total number of packets that higher-level protocols requested be transmitted, and that were addressed to a broadcast address at this sub-layer, including those that were discarded or not sent. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCOutBroadcastPkts"; } leaf out-multicast-pkts { type yang:counter64; description "The total number of packets that higher-level protocols requested be transmitted, and that were addressed to a multicast address at this sub-layer, including those that were discarded or not sent. For a MAC-layer protocol, this includes both Group and Functional addresses. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCOutMulticastPkts"; } leaf out-discards { type yang:counter32; description "The number of outbound packets that were chosen to be discarded even though no errors had been detected to prevent their being transmitted. One possible reason for discarding such a packet could be to free up buffer space. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; } leaf out-errors { type yang:counter32; description "For packet-oriented interfaces, the number of outbound packets that could not be transmitted because of errors. For character-oriented or fixed-length interfaces, the number of outbound transmission units that could not be transmitted because of errors. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifOutErrors"; } } } } } yuma123_2.14/netconf/modules/ietf/ietf-lmap-common@2017-08-08.yang0000664000175000017500000002163114770023131024271 0ustar vladimirvladimirmodule ietf-lmap-common { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-lmap-common"; prefix "lmap"; import ietf-inet-types { prefix inet; } organization "IETF Large-Scale Measurement of Broadband Performance Working Group"; contact "WG Web: WG List: Editor: Juergen Schoenwaelder Editor: Vaibhav Bajpai "; description "This module provides common definitions used by the data models written for Large-Scale Measurement Platforms (LMAPs). This module defines typedefs and groupings but no schema tree elements."; revision "2017-08-08" { description "Initial version"; reference "RFC 8194: A YANG Data Model for LMAP Measurement Agents"; } /* * Typedefs */ typedef identifier { type string { length "1..max"; } description "A string value used to name something."; } typedef tag { type string { length "1..max"; } description "A tag consists of at least one character."; } typedef glob-pattern { type string { length "1..max"; } description 'A glob style pattern (following POSIX.2 fnmatch() without special treatment of file paths): * matches a sequence of characters ? matches a single character [seq] matches any character in seq [!seq] matches any character not in seq A backslash followed by a character matches the following character. In particular: \* matches * \? matches ? \\ matches \ A sequence seq may be a sequence of characters (e.g., [abc] or a range of characters (e.g., [a-c]).'; } typedef wildcard { type string { pattern '\*'; } description "A wildcard for calendar scheduling entries."; } typedef cycle-number { type string { pattern '[0-9]{8}\.[0-9]{6}'; } description "A cycle number represented in the format YYYYMMDD.HHMMSS where YYYY represents the year, MM the month (1..12), DD the day of the months (01..31), HH the hour (00..23), MM the minute (00..59), and SS the second (00..59). The cycle number is using Coordinated Universal Time (UTC)."; } typedef month { type enumeration { enum january { value 1; description "January of the Gregorian calendar."; } enum february { value 2; description "February of the Gregorian calendar."; } enum march { value 3; description "March of the Gregorian calendar."; } enum april { value 4; description "April of the Gregorian calendar."; } enum may { value 5; description "May of the Gregorian calendar."; } enum june { value 6; description "June of the Gregorian calendar."; } enum july { value 7; description "July of the Gregorian calendar."; } enum august { value 8; description "August of the Gregorian calendar."; } enum september { value 9; description "September of the Gregorian calendar."; } enum october { value 10; description "October of the Gregorian calendar."; } enum november { value 11; description "November of the Gregorian calendar."; } enum december { value 12; description "December of the Gregorian calendar."; } } description "A type modeling the month in the Gregorian calendar."; } typedef month-or-all { type union { type month; type wildcard; } description "A month or a wildcard indicating all twelve months."; } typedef day-of-month { type uint8 { range "1..31"; } description "A day of a month of the Gregorian calendar."; } typedef day-of-months-or-all { type union { type day-of-month; type wildcard; } description "A day of a month or a wildcard indicating all days of a month."; } typedef weekday { type enumeration { enum monday { value 1; description "Monday of the Gregorian calendar."; } enum tuesday { value 2; description "Tuesday of the Gregorian calendar."; } enum wednesday { value 3; description "Wednesday of the Gregorian calendar."; } enum thursday { value 4; description "Thursday of the Gregorian calendar."; } enum friday { value 5; description "Friday of the Gregorian calendar."; } enum saturday { value 6; description "Saturday of the Gregorian calendar."; } enum sunday { value 7; description "Sunday of the Gregorian calendar."; } } description "A type modeling the weekdays in the Gregorian calendar. The numbering follows the ISO 8601 scheme."; reference "ISO 8601:2004: Data elements and interchange formats -- Information interchange -- Representation of dates and times"; } typedef weekday-or-all { type union { type weekday; type wildcard; } description "A weekday or a wildcard indicating all seven weekdays."; } typedef hour { type uint8 { range "0..23"; } description "An hour of a day."; } typedef hour-or-all { type union { type hour; type wildcard; } description "An hour of a day or a wildcard indicating all hours of a day."; } typedef minute { type uint8 { range "0..59"; } description "A minute of an hour."; } typedef minute-or-all { type union { type minute; type wildcard; } description "A minute of an hour or a wildcard indicating all minutes of an hour."; } typedef second { type uint8 { range "0..59"; } description "A second of a minute."; } typedef second-or-all { type union { type second; type wildcard; } description "A second of a minute or a wildcard indicating all seconds of a minute."; } typedef status-code { type int32; description "A status code returned by the execution of a Task. Note that the actual range is implementation dependent, but it should be portable to use values in the range 0..127 for regular exit codes. By convention, 0 indicates successful termination. Negative values may be used to indicate abnormal termination due to a signal; the absolute value may identify the signal number in this case."; } typedef timezone-offset { type string { pattern 'Z|[\+\-]\d{2}:\d{2}'; } description "A time zone offset as it is used by the date-and-time type defined in the ietf-yang-types module. The value Z is equivalent to +00:00. The value -00:00 indicates an unknown time-offset."; reference "RFC 6991: Common YANG Data Types"; } /* * Groupings */ grouping registry-grouping { description "This grouping models a list of entries in a registry that identify functions of a Task."; list function { key uri; description "A list of entries in a registry identifying functions."; leaf uri { type inet:uri; description "A URI identifying an entry in a registry."; } leaf-list role { type string; description "A set of roles for the identified registry entry."; } } } grouping options-grouping { description "A list of options of a Task. Each option is a name/value pair (where the value may be absent)."; list option { key "id"; ordered-by user; description "A list of options passed to the Task. It is a list of key/value pairs and may be used to model options. Options may be used to identify the role of a Task or to pass a Channel name to a Task."; leaf id { type lmap:identifier; description "An identifier uniquely identifying an option. This identifier is required by YANG to uniquely identify a name/value pair, but it otherwise has no semantic value"; } leaf name { type string; description "The name of the option."; } leaf value { type string; description "The value of the option."; } } } } yuma123_2.14/netconf/modules/ietf/ietf-system@2014-08-06.yang0000664000175000017500000005720014770023131023372 0ustar vladimirvladimirmodule ietf-system { namespace "urn:ietf:params:xml:ns:yang:ietf-system"; prefix "sys"; import ietf-yang-types { prefix yang; } import ietf-inet-types { prefix inet; } import ietf-netconf-acm { prefix nacm; } import iana-crypt-hash { prefix ianach; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Andy Bierman Editor: Martin Bjorklund "; description "This module contains a collection of YANG definitions for the configuration and identification of some common system properties within a device containing a NETCONF server. This includes data node definitions for system identification, time-of-day management, user management, DNS resolver configuration, and some protocol operations for system management. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7317; see the RFC itself for full legal notices."; revision 2014-08-06 { description "Initial revision."; reference "RFC 7317: A YANG Data Model for System Management"; } /* * Typedefs */ typedef timezone-name { type string; description "A time zone name as used by the Time Zone Database, sometimes referred to as the 'Olson Database'. The exact set of valid values is an implementation-specific matter. Client discovery of the exact set of time zone names for a particular server is out of scope."; reference "RFC 6557: Procedures for Maintaining the Time Zone Database"; } /* * Features */ feature radius { description "Indicates that the device can be configured as a RADIUS client."; reference "RFC 2865: Remote Authentication Dial In User Service (RADIUS)"; } feature authentication { description "Indicates that the device supports configuration of user authentication."; } feature local-users { if-feature authentication; description "Indicates that the device supports configuration of local user authentication."; } feature radius-authentication { if-feature radius; if-feature authentication; description "Indicates that the device supports configuration of user authentication over RADIUS."; reference "RFC 2865: Remote Authentication Dial In User Service (RADIUS) RFC 5607: Remote Authentication Dial-In User Service (RADIUS) Authorization for Network Access Server (NAS) Management"; } feature ntp { description "Indicates that the device can be configured to use one or more NTP servers to set the system date and time."; } feature ntp-udp-port { if-feature ntp; description "Indicates that the device supports the configuration of the UDP port for NTP servers. This is a 'feature', since many implementations do not support any port other than the default port."; } feature timezone-name { description "Indicates that the local time zone on the device can be configured to use the TZ database to set the time zone and manage daylight saving time."; reference "RFC 6557: Procedures for Maintaining the Time Zone Database"; } feature dns-udp-tcp-port { description "Indicates that the device supports the configuration of the UDP and TCP port for DNS servers. This is a 'feature', since many implementations do not support any port other than the default port."; } /* * Identities */ identity authentication-method { description "Base identity for user authentication methods."; } identity radius { base authentication-method; description "Indicates user authentication using RADIUS."; reference "RFC 2865: Remote Authentication Dial In User Service (RADIUS) RFC 5607: Remote Authentication Dial-In User Service (RADIUS) Authorization for Network Access Server (NAS) Management"; } identity local-users { base authentication-method; description "Indicates password-based authentication of locally configured users."; } identity radius-authentication-type { description "Base identity for RADIUS authentication types."; } identity radius-pap { base radius-authentication-type; description "The device requests Password Authentication Protocol (PAP) authentication from the RADIUS server."; reference "RFC 2865: Remote Authentication Dial In User Service (RADIUS)"; } identity radius-chap { base radius-authentication-type; description "The device requests Challenge Handshake Authentication Protocol (CHAP) authentication from the RADIUS server."; reference "RFC 2865: Remote Authentication Dial In User Service (RADIUS)"; } /* * Configuration data nodes */ container system { description "System group configuration."; leaf contact { type string; description "The administrator contact information for the system. A server implementation MAY map this leaf to the sysContact MIB object. Such an implementation needs to use some mechanism to handle the differences in size and characters allowed between this leaf and sysContact. The definition of such a mechanism is outside the scope of this document."; reference "RFC 3418: Management Information Base (MIB) for the Simple Network Management Protocol (SNMP) SNMPv2-MIB.sysContact"; } leaf hostname { type inet:domain-name; description "The name of the host. This name can be a single domain label or the fully qualified domain name of the host."; } leaf location { type string; description "The system location. A server implementation MAY map this leaf to the sysLocation MIB object. Such an implementation needs to use some mechanism to handle the differences in size and characters allowed between this leaf and sysLocation. The definition of such a mechanism is outside the scope of this document."; reference "RFC 3418: Management Information Base (MIB) for the Simple Network Management Protocol (SNMP) SNMPv2-MIB.sysLocation"; } container clock { description "Configuration of the system date and time properties."; choice timezone { description "The system time zone information."; case timezone-name { if-feature timezone-name; leaf timezone-name { type timezone-name; description "The TZ database name to use for the system, such as 'Europe/Stockholm'."; } } case timezone-utc-offset { leaf timezone-utc-offset { type int16 { range "-1500 .. 1500"; } units "minutes"; description "The number of minutes to add to UTC time to identify the time zone for this system. For example, 'UTC - 8:00 hours' would be represented as '-480'. Note that automatic daylight saving time adjustment is not provided if this object is used."; } } } } container ntp { if-feature ntp; presence "Enables the NTP client unless the 'enabled' leaf (which defaults to 'true') is set to 'false'"; description "Configuration of the NTP client."; leaf enabled { type boolean; default true; description "Indicates that the system should attempt to synchronize the system clock with an NTP server from the 'ntp/server' list."; } list server { key name; description "List of NTP servers to use for system clock synchronization. If '/system/ntp/enabled' is 'true', then the system will attempt to contact and utilize the specified NTP servers."; leaf name { type string; description "An arbitrary name for the NTP server."; } choice transport { mandatory true; description "The transport-protocol-specific parameters for this server."; case udp { container udp { description "Contains UDP-specific configuration parameters for NTP."; leaf address { type inet:host; mandatory true; description "The address of the NTP server."; } leaf port { if-feature ntp-udp-port; type inet:port-number; default 123; description "The port number of the NTP server."; } } } } leaf association-type { type enumeration { enum server { description "Use client association mode. This device will not provide synchronization to the configured NTP server."; } enum peer { description "Use symmetric active association mode. This device may provide synchronization to the configured NTP server."; } enum pool { description "Use client association mode with one or more of the NTP servers found by DNS resolution of the domain name given by the 'address' leaf. This device will not provide synchronization to the servers."; } } default server; description "The desired association type for this NTP server."; } leaf iburst { type boolean; default false; description "Indicates whether this server should enable burst synchronization or not."; } leaf prefer { type boolean; default false; description "Indicates whether this server should be preferred or not."; } } } container dns-resolver { description "Configuration of the DNS resolver."; leaf-list search { type inet:domain-name; ordered-by user; description "An ordered list of domains to search when resolving a host name."; } list server { key name; ordered-by user; description "List of the DNS servers that the resolver should query. When the resolver is invoked by a calling application, it sends the query to the first name server in this list. If no response has been received within 'timeout' seconds, the resolver continues with the next server in the list. If no response is received from any server, the resolver continues with the first server again. When the resolver has traversed the list 'attempts' times without receiving any response, it gives up and returns an error to the calling application. Implementations MAY limit the number of entries in this list."; leaf name { type string; description "An arbitrary name for the DNS server."; } choice transport { mandatory true; description "The transport-protocol-specific parameters for this server."; case udp-and-tcp { container udp-and-tcp { description "Contains UDP- and TCP-specific configuration parameters for DNS."; reference "RFC 1035: Domain Names - Implementation and Specification RFC 5966: DNS Transport over TCP - Implementation Requirements"; leaf address { type inet:ip-address; mandatory true; description "The address of the DNS server."; } leaf port { if-feature dns-udp-tcp-port; type inet:port-number; default 53; description "The UDP and TCP port number of the DNS server."; } } } } } container options { description "Resolver options. The set of available options has been limited to those that are generally available across different resolver implementations and generally useful."; leaf timeout { type uint8 { range "1..max"; } units "seconds"; default "5"; description "The amount of time the resolver will wait for a response from each remote name server before retrying the query via a different name server."; } leaf attempts { type uint8 { range "1..max"; } default "2"; description "The number of times the resolver will send a query to all of its name servers before giving up and returning an error to the calling application."; } } } container radius { if-feature radius; description "Configuration of the RADIUS client."; list server { key name; ordered-by user; description "List of RADIUS servers used by the device. When the RADIUS client is invoked by a calling application, it sends the query to the first server in this list. If no response has been received within 'timeout' seconds, the client continues with the next server in the list. If no response is received from any server, the client continues with the first server again. When the client has traversed the list 'attempts' times without receiving any response, it gives up and returns an error to the calling application."; leaf name { type string; description "An arbitrary name for the RADIUS server."; } choice transport { mandatory true; description "The transport-protocol-specific parameters for this server."; case udp { container udp { description "Contains UDP-specific configuration parameters for RADIUS."; leaf address { type inet:host; mandatory true; description "The address of the RADIUS server."; } leaf authentication-port { type inet:port-number; default "1812"; description "The port number of the RADIUS server."; } leaf shared-secret { type string; mandatory true; nacm:default-deny-all; description "The shared secret, which is known to both the RADIUS client and server."; reference "RFC 2865: Remote Authentication Dial In User Service (RADIUS)"; } } } } leaf authentication-type { type identityref { base radius-authentication-type; } default radius-pap; description "The authentication type requested from the RADIUS server."; } } container options { description "RADIUS client options."; leaf timeout { type uint8 { range "1..max"; } units "seconds"; default "5"; description "The number of seconds the device will wait for a response from each RADIUS server before trying with a different server."; } leaf attempts { type uint8 { range "1..max"; } default "2"; description "The number of times the device will send a query to all of its RADIUS servers before giving up."; } } } container authentication { nacm:default-deny-write; if-feature authentication; description "The authentication configuration subtree."; leaf-list user-authentication-order { type identityref { base authentication-method; } must '(. != "sys:radius" or ../../radius/server)' { error-message "When 'radius' is used, a RADIUS server" + " must be configured."; description "When 'radius' is used as an authentication method, a RADIUS server must be configured."; } ordered-by user; description "When the device authenticates a user with a password, it tries the authentication methods in this leaf-list in order. If authentication with one method fails, the next method is used. If no method succeeds, the user is denied access. An empty user-authentication-order leaf-list still allows authentication of users using mechanisms that do not involve a password. If the 'radius-authentication' feature is advertised by the NETCONF server, the 'radius' identity can be added to this list. If the 'local-users' feature is advertised by the NETCONF server, the 'local-users' identity can be added to this list."; } list user { if-feature local-users; key name; description "The list of local users configured on this device."; leaf name { type string; description "The user name string identifying this entry."; } leaf password { type ianach:crypt-hash; description "The password for this entry."; } list authorized-key { key name; description "A list of public SSH keys for this user. These keys are allowed for SSH authentication, as described in RFC 4253."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; leaf name { type string; description "An arbitrary name for the SSH key."; } leaf algorithm { type string; mandatory true; description "The public key algorithm name for this SSH key. Valid values are the values in the IANA 'Secure Shell (SSH) Protocol Parameters' registry, Public Key Algorithm Names."; reference "IANA 'Secure Shell (SSH) Protocol Parameters' registry, Public Key Algorithm Names"; } leaf key-data { type binary; mandatory true; description "The binary public key data for this SSH key, as specified by RFC 4253, Section 6.6, i.e.: string certificate or public key format identifier byte[n] key/certificate data."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } } } } } /* * Operational state data nodes */ container system-state { config false; description "System group operational state."; container platform { description "Contains vendor-specific information for identifying the system platform and operating system."; reference "IEEE Std 1003.1-2008 - sys/utsname.h"; leaf os-name { type string; description "The name of the operating system in use - for example, 'Linux'."; reference "IEEE Std 1003.1-2008 - utsname.sysname"; } leaf os-release { type string; description "The current release level of the operating system in use. This string MAY indicate the OS source code revision."; reference "IEEE Std 1003.1-2008 - utsname.release"; } leaf os-version { type string; description "The current version level of the operating system in use. This string MAY indicate the specific OS build date and target variant information."; reference "IEEE Std 1003.1-2008 - utsname.version"; } leaf machine { type string; description "A vendor-specific identifier string representing the hardware in use."; reference "IEEE Std 1003.1-2008 - utsname.machine"; } } container clock { description "Monitoring of the system date and time properties."; leaf current-datetime { type yang:date-and-time; description "The current system date and time."; } leaf boot-datetime { type yang:date-and-time; description "The system date and time when the system last restarted."; } } } rpc set-current-datetime { nacm:default-deny-all; description "Set the /system-state/clock/current-datetime leaf to the specified value. If the system is using NTP (i.e., /system/ntp/enabled is set to 'true'), then this operation will fail with error-tag 'operation-failed' and error-app-tag value of 'ntp-active'."; input { leaf current-datetime { type yang:date-and-time; mandatory true; description "The current system date and time."; } } } rpc system-restart { nacm:default-deny-all; description "Request that the entire system be restarted immediately. A server SHOULD send an rpc reply to the client before restarting the system."; } rpc system-shutdown { nacm:default-deny-all; description "Request that the entire system be shut down immediately. A server SHOULD send an rpc reply to the client before shutting down the system."; } } yuma123_2.14/netconf/modules/ietf/ietf-netconf@2011-06-01.yang0000664000175000017500000006416114770023131023474 0ustar vladimirvladimirmodule ietf-netconf { // the namespace for NETCONF XML definitions is unchanged // from RFC 4741, which this document replaces namespace "urn:ietf:params:xml:ns:netconf:base:1.0"; prefix nc; import ietf-inet-types { prefix inet; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: WG List: WG Chair: Bert Wijnen WG Chair: Mehmet Ersue Editor: Martin Bjorklund Editor: Juergen Schoenwaelder Editor: Andy Bierman "; description "NETCONF Protocol Data Types and Protocol Operations. Copyright (c) 2011 IETF Trust and the persons identified as the document authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 6241; see the RFC itself for full legal notices."; revision 2011-06-01 { description "Initial revision"; reference "RFC 6241: Network Configuration Protocol"; } extension get-filter-element-attributes { description "If this extension is present within an 'anyxml' statement named 'filter', which must be conceptually defined within the RPC input section for the and protocol operations, then the following unqualified XML attribute is supported within the element, within a or protocol operation: type : optional attribute with allowed value strings 'subtree' and 'xpath'. If missing, the default value is 'subtree'. If the 'xpath' feature is supported, then the following unqualified XML attribute is also supported: select: optional attribute containing a string representing an XPath expression. The 'type' attribute must be equal to 'xpath' if this attribute is present."; } // NETCONF capabilities defined as features feature writable-running { description "NETCONF :writable-running capability; If the server advertises the :writable-running capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.2"; } feature candidate { description "NETCONF :candidate capability; If the server advertises the :candidate capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.3"; } feature confirmed-commit { if-feature candidate; description "NETCONF :confirmed-commit:1.1 capability; If the server advertises the :confirmed-commit:1.1 capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.4"; } feature rollback-on-error { description "NETCONF :rollback-on-error capability; If the server advertises the :rollback-on-error capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.5"; } feature validate { description "NETCONF :validate:1.1 capability; If the server advertises the :validate:1.1 capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.6"; } feature startup { description "NETCONF :startup capability; If the server advertises the :startup capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.7"; } feature url { description "NETCONF :url capability; If the server advertises the :url capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.8"; } feature xpath { description "NETCONF :xpath capability; If the server advertises the :xpath capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.9"; } // NETCONF Simple Types typedef session-id-type { type uint32 { range "1..max"; } description "NETCONF Session Id"; } typedef session-id-or-zero-type { type uint32; description "NETCONF Session Id or Zero to indicate none"; } typedef error-tag-type { type enumeration { enum in-use { description "The request requires a resource that already is in use."; } enum invalid-value { description "The request specifies an unacceptable value for one or more parameters."; } enum too-big { description "The request or response (that would be generated) is too large for the implementation to handle."; } enum missing-attribute { description "An expected attribute is missing."; } enum bad-attribute { description "An attribute value is not correct; e.g., wrong type, out of range, pattern mismatch."; } enum unknown-attribute { description "An unexpected attribute is present."; } enum missing-element { description "An expected element is missing."; } enum bad-element { description "An element value is not correct; e.g., wrong type, out of range, pattern mismatch."; } enum unknown-element { description "An unexpected element is present."; } enum unknown-namespace { description "An unexpected namespace is present."; } enum access-denied { description "Access to the requested protocol operation or data model is denied because authorization failed."; } enum lock-denied { description "Access to the requested lock is denied because the lock is currently held by another entity."; } enum resource-denied { description "Request could not be completed because of insufficient resources."; } enum rollback-failed { description "Request to roll back some configuration change (via rollback-on-error or operations) was not completed for some reason."; } enum data-exists { description "Request could not be completed because the relevant data model content already exists. For example, a 'create' operation was attempted on data that already exists."; } enum data-missing { description "Request could not be completed because the relevant data model content does not exist. For example, a 'delete' operation was attempted on data that does not exist."; } enum operation-not-supported { description "Request could not be completed because the requested operation is not supported by this implementation."; } enum operation-failed { description "Request could not be completed because the requested operation failed for some reason not covered by any other error condition."; } enum partial-operation { description "This error-tag is obsolete, and SHOULD NOT be sent by servers conforming to this document."; } enum malformed-message { description "A message could not be handled because it failed to be parsed correctly. For example, the message is not well-formed XML or it uses an invalid character set."; } } description "NETCONF Error Tag"; reference "RFC 6241, Appendix A"; } typedef error-severity-type { type enumeration { enum error { description "Error severity"; } enum warning { description "Warning severity"; } } description "NETCONF Error Severity"; reference "RFC 6241, Section 4.3"; } typedef edit-operation-type { type enumeration { enum merge { description "The configuration data identified by the element containing this attribute is merged with the configuration at the corresponding level in the configuration datastore identified by the target parameter."; } enum replace { description "The configuration data identified by the element containing this attribute replaces any related configuration in the configuration datastore identified by the target parameter. If no such configuration data exists in the configuration datastore, it is created. Unlike a operation, which replaces the entire target configuration, only the configuration actually present in the config parameter is affected."; } enum create { description "The configuration data identified by the element containing this attribute is added to the configuration if and only if the configuration data does not already exist in the configuration datastore. If the configuration data exists, an element is returned with an value of 'data-exists'."; } enum delete { description "The configuration data identified by the element containing this attribute is deleted from the configuration if and only if the configuration data currently exists in the configuration datastore. If the configuration data does not exist, an element is returned with an value of 'data-missing'."; } enum remove { description "The configuration data identified by the element containing this attribute is deleted from the configuration if the configuration data currently exists in the configuration datastore. If the configuration data does not exist, the 'remove' operation is silently ignored by the server."; } } default "merge"; description "NETCONF 'operation' attribute values"; reference "RFC 6241, Section 7.2"; } // NETCONF Standard Protocol Operations rpc get-config { description "Retrieve all or part of a specified configuration."; reference "RFC 6241, Section 7.1"; input { container source { description "Particular configuration to retrieve."; choice config-source { mandatory true; description "The configuration to retrieve."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config source."; } leaf running { type empty; description "The running configuration is the config source."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config source. This is optional-to-implement on the server because not all servers will support filtering for this datastore."; } } } anyxml filter { description "Subtree or XPath filter to use."; nc:get-filter-element-attributes; } } output { anyxml data { description "Copy of the source datastore subset that matched the filter criteria (if any). An empty data container indicates that the request did not produce any results."; } } } rpc edit-config { description "The operation loads all or part of a specified configuration to the specified target configuration."; reference "RFC 6241, Section 7.2"; input { container target { description "Particular configuration to edit."; choice config-target { mandatory true; description "The configuration target."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config target."; } leaf running { if-feature writable-running; type empty; description "The running configuration is the config source."; } } } leaf default-operation { type enumeration { enum merge { description "The default operation is merge."; } enum replace { description "The default operation is replace."; } enum none { description "There is no default operation."; } } default "merge"; description "The default operation to use."; } leaf test-option { if-feature validate; type enumeration { enum test-then-set { description "The server will test and then set if no errors."; } enum set { description "The server will set without a test first."; } enum test-only { description "The server will only test and not set, even if there are no errors."; } } default "test-then-set"; description "The test option to use."; } leaf error-option { type enumeration { enum stop-on-error { description "The server will stop on errors."; } enum continue-on-error { description "The server may continue on errors."; } enum rollback-on-error { description "The server will roll back on errors. This value can only be used if the 'rollback-on-error' feature is supported."; } } default "stop-on-error"; description "The error option to use."; } choice edit-content { mandatory true; description "The content for the edit operation."; anyxml config { description "Inline Config content."; } leaf url { if-feature url; type inet:uri; description "URL-based config content."; } } } } rpc copy-config { description "Create or replace an entire configuration datastore with the contents of another complete configuration datastore."; reference "RFC 6241, Section 7.3"; input { container target { description "Particular configuration to copy to."; choice config-target { mandatory true; description "The configuration target of the copy operation."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config target."; } leaf running { if-feature writable-running; type empty; description "The running configuration is the config target. This is optional-to-implement on the server."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config target."; } leaf url { if-feature url; type inet:uri; description "The URL-based configuration is the config target."; } } } container source { description "Particular configuration to copy from."; choice config-source { mandatory true; description "The configuration source for the copy operation."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config source."; } leaf running { type empty; description "The running configuration is the config source."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config source."; } leaf url { if-feature url; type inet:uri; description "The URL-based configuration is the config source."; } anyxml config { description "Inline Config content: element. Represents an entire configuration datastore, not a subset of the running datastore."; } } } } } rpc delete-config { description "Delete a configuration datastore."; reference "RFC 6241, Section 7.4"; input { container target { description "Particular configuration to delete."; choice config-target { mandatory true; description "The configuration target to delete."; leaf startup { if-feature startup; type empty; description "The startup configuration is the config target."; } leaf url { if-feature url; type inet:uri; description "The URL-based configuration is the config target."; } } } } } rpc lock { description "The lock operation allows the client to lock the configuration system of a device."; reference "RFC 6241, Section 7.5"; input { container target { description "Particular configuration to lock."; choice config-target { mandatory true; description "The configuration target to lock."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config target."; } leaf running { type empty; description "The running configuration is the config target."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config target."; } } } } } rpc unlock { description "The unlock operation is used to release a configuration lock, previously obtained with the 'lock' operation."; reference "RFC 6241, Section 7.6"; input { container target { description "Particular configuration to unlock."; choice config-target { mandatory true; description "The configuration target to unlock."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config target."; } leaf running { type empty; description "The running configuration is the config target."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config target."; } } } } } rpc get { description "Retrieve running configuration and device state information."; reference "RFC 6241, Section 7.7"; input { anyxml filter { description "This parameter specifies the portion of the system configuration and state data to retrieve."; nc:get-filter-element-attributes; } } output { anyxml data { description "Copy of the running datastore subset and/or state data that matched the filter criteria (if any). An empty data container indicates that the request did not produce any results."; } } } rpc close-session { description "Request graceful termination of a NETCONF session."; reference "RFC 6241, Section 7.8"; } rpc kill-session { description "Force the termination of a NETCONF session."; reference "RFC 6241, Section 7.9"; input { leaf session-id { type session-id-type; mandatory true; description "Particular session to kill."; } } } rpc commit { if-feature candidate; description "Commit the candidate configuration as the device's new current configuration."; reference "RFC 6241, Section 8.3.4.1"; input { leaf confirmed { if-feature confirmed-commit; type empty; description "Requests a confirmed commit."; reference "RFC 6241, Section 8.3.4.1"; } leaf confirm-timeout { if-feature confirmed-commit; type uint32 { range "1..max"; } units "seconds"; default "600"; // 10 minutes description "The timeout interval for a confirmed commit."; reference "RFC 6241, Section 8.3.4.1"; } leaf persist { if-feature confirmed-commit; type string; description "This parameter is used to make a confirmed commit persistent. A persistent confirmed commit is not aborted if the NETCONF session terminates. The only way to abort a persistent confirmed commit is to let the timer expire, or to use the operation. The value of this parameter is a token that must be given in the 'persist-id' parameter of or operations in order to confirm or cancel the persistent confirmed commit. The token should be a random string."; reference "RFC 6241, Section 8.3.4.1"; } leaf persist-id { if-feature confirmed-commit; type string; description "This parameter is given in order to commit a persistent confirmed commit. The value must be equal to the value given in the 'persist' parameter to the operation. If it does not match, the operation fails with an 'invalid-value' error."; reference "RFC 6241, Section 8.3.4.1"; } } } rpc discard-changes { if-feature candidate; description "Revert the candidate configuration to the current running configuration."; reference "RFC 6241, Section 8.3.4.2"; } rpc cancel-commit { if-feature confirmed-commit; description "This operation is used to cancel an ongoing confirmed commit. If the confirmed commit is persistent, the parameter 'persist-id' must be given, and it must match the value of the 'persist' parameter."; reference "RFC 6241, Section 8.4.4.1"; input { leaf persist-id { type string; description "This parameter is given in order to cancel a persistent confirmed commit. The value must be equal to the value given in the 'persist' parameter to the operation. If it does not match, the operation fails with an 'invalid-value' error."; } } } rpc validate { if-feature validate; description "Validates the contents of the specified configuration."; reference "RFC 6241, Section 8.6.4.1"; input { container source { description "Particular configuration to validate."; choice config-source { mandatory true; description "The configuration source to validate."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config source."; } leaf running { type empty; description "The running configuration is the config source."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config source."; } leaf url { if-feature url; type inet:uri; description "The URL-based configuration is the config source."; } anyxml config { description "Inline Config content: element. Represents an entire configuration datastore, not a subset of the running datastore."; } } } } } } yuma123_2.14/netconf/modules/ietf/ietf-netconf-partial-lock@2009-10-19.yang0000664000175000017500000000373414770023131026066 0ustar vladimirvladimirmodule ietf-netconf-partial-lock { namespace urn:ietf:params:xml:ns:netconf:partial-lock:1.0; prefix pl; organization "IETF Network Configuration (netconf) Working Group"; contact "Netconf Working Group Mailing list: netconf@ietf.org Web: http://www.ietf.org/html.charters/netconf-charter.html Balazs Lengyel Ericsson balazs.lengyel@ericsson.com"; description "This YANG module defines the and operations."; revision 2009-10-19 { description "Initial version, published as RFC 5717."; } typedef lock-id-type { type uint32; description "A number identifying a specific partial-lock granted to a session. It is allocated by the system, and SHOULD be used in the partial-unlock operation."; } rpc partial-lock { description "A NETCONF operation that locks parts of the running datastore."; input { leaf-list select { type string; min-elements 1; description "XPath expression that specifies the scope of the lock. An Instance Identifier expression MUST be used unless the :xpath capability is supported, in which case any XPath 1.0 expression is allowed."; } } output { leaf lock-id { type lock-id-type; description "Identifies the lock, if granted. The lock-id SHOULD be used in the partial-unlock rpc."; } leaf-list locked-node { type instance-identifier; min-elements 1; description "List of locked nodes in the running datastore"; } } } rpc partial-unlock { description "A NETCONF operation that releases a previously acquired partial-lock."; input { leaf lock-id { type lock-id-type; description "Identifies the lock to be released. MUST be the value received in the response to a partial-lock operation."; } } } } yuma123_2.14/netconf/modules/ietf/iana-if-type@2014-05-08.yang0000664000175000017500000010365214770023131023406 0ustar vladimirvladimirmodule iana-if-type { namespace "urn:ietf:params:xml:ns:yang:iana-if-type"; prefix ianaift; import ietf-interfaces { prefix if; } organization "IANA"; contact " Internet Assigned Numbers Authority Postal: ICANN 4676 Admiralty Way, Suite 330 Marina del Rey, CA 90292 Tel: +1 310 823 9358 "; description "This YANG module defines YANG identities for IANA-registered interface types. This YANG module is maintained by IANA and reflects the 'ifType definitions' registry. The latest revision of this YANG module can be obtained from the IANA web site. Requests for new values should be made to IANA via email (iana@iana.org). Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). The initial version of this YANG module is part of RFC 7224; see the RFC itself for full legal notices."; reference "IANA 'ifType definitions' registry. "; revision 2014-05-08 { description "Initial revision."; reference "RFC 7224: IANA Interface Type YANG Module"; } identity iana-interface-type { base if:interface-type; description "This identity is used as a base for all interface types defined in the 'ifType definitions' registry."; } identity other { base iana-interface-type; } identity regular1822 { base iana-interface-type; } identity hdh1822 { base iana-interface-type; } identity ddnX25 { base iana-interface-type; } identity rfc877x25 { base iana-interface-type; reference "RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer"; } identity ethernetCsmacd { base iana-interface-type; description "For all Ethernet-like interfaces, regardless of speed, as per RFC 3635."; reference "RFC 3635 - Definitions of Managed Objects for the Ethernet-like Interface Types"; } identity iso88023Csmacd { base iana-interface-type; status deprecated; description "Deprecated via RFC 3635. Use ethernetCsmacd(6) instead."; reference "RFC 3635 - Definitions of Managed Objects for the Ethernet-like Interface Types"; } identity iso88024TokenBus { base iana-interface-type; } identity iso88025TokenRing { base iana-interface-type; } identity iso88026Man { base iana-interface-type; } identity starLan { base iana-interface-type; status deprecated; description "Deprecated via RFC 3635. Use ethernetCsmacd(6) instead."; reference "RFC 3635 - Definitions of Managed Objects for the Ethernet-like Interface Types"; } identity proteon10Mbit { base iana-interface-type; } identity proteon80Mbit { base iana-interface-type; } identity hyperchannel { base iana-interface-type; } identity fddi { base iana-interface-type; reference "RFC 1512 - FDDI Management Information Base"; } identity lapb { base iana-interface-type; reference "RFC 1381 - SNMP MIB Extension for X.25 LAPB"; } identity sdlc { base iana-interface-type; } identity ds1 { base iana-interface-type; description "DS1-MIB."; reference "RFC 4805 - Definitions of Managed Objects for the DS1, J1, E1, DS2, and E2 Interface Types"; } identity e1 { base iana-interface-type; status obsolete; description "Obsolete; see DS1-MIB."; reference "RFC 4805 - Definitions of Managed Objects for the DS1, J1, E1, DS2, and E2 Interface Types"; } identity basicISDN { base iana-interface-type; description "No longer used. See also RFC 2127."; } identity primaryISDN { base iana-interface-type; description "No longer used. See also RFC 2127."; } identity propPointToPointSerial { base iana-interface-type; description "Proprietary serial."; } identity ppp { base iana-interface-type; } identity softwareLoopback { base iana-interface-type; } identity eon { base iana-interface-type; description "CLNP over IP."; } identity ethernet3Mbit { base iana-interface-type; } identity nsip { base iana-interface-type; description "XNS over IP."; } identity slip { base iana-interface-type; description "Generic SLIP."; } identity ultra { base iana-interface-type; description "Ultra Technologies."; } identity ds3 { base iana-interface-type; description "DS3-MIB."; reference "RFC 3896 - Definitions of Managed Objects for the DS3/E3 Interface Type"; } identity sip { base iana-interface-type; description "SMDS, coffee."; reference "RFC 1694 - Definitions of Managed Objects for SMDS Interfaces using SMIv2"; } identity frameRelay { base iana-interface-type; description "DTE only."; reference "RFC 2115 - Management Information Base for Frame Relay DTEs Using SMIv2"; } identity rs232 { base iana-interface-type; reference "RFC 1659 - Definitions of Managed Objects for RS-232-like Hardware Devices using SMIv2"; } identity para { base iana-interface-type; description "Parallel-port."; reference "RFC 1660 - Definitions of Managed Objects for Parallel-printer-like Hardware Devices using SMIv2"; } identity arcnet { base iana-interface-type; description "ARCnet."; } identity arcnetPlus { base iana-interface-type; description "ARCnet Plus."; } identity atm { base iana-interface-type; description "ATM cells."; } identity miox25 { base iana-interface-type; reference "RFC 1461 - SNMP MIB extension for Multiprotocol Interconnect over X.25"; } identity sonet { base iana-interface-type; description "SONET or SDH."; } identity x25ple { base iana-interface-type; reference "RFC 2127 - ISDN Management Information Base using SMIv2"; } identity iso88022llc { base iana-interface-type; } identity localTalk { base iana-interface-type; } identity smdsDxi { base iana-interface-type; } identity frameRelayService { base iana-interface-type; description "FRNETSERV-MIB."; reference "RFC 2954 - Definitions of Managed Objects for Frame Relay Service"; } identity v35 { base iana-interface-type; } identity hssi { base iana-interface-type; } identity hippi { base iana-interface-type; } identity modem { base iana-interface-type; description "Generic modem."; } identity aal5 { base iana-interface-type; description "AAL5 over ATM."; } identity sonetPath { base iana-interface-type; } identity sonetVT { base iana-interface-type; } identity smdsIcip { base iana-interface-type; description "SMDS InterCarrier Interface."; } identity propVirtual { base iana-interface-type; description "Proprietary virtual/internal."; reference "RFC 2863 - The Interfaces Group MIB"; } identity propMultiplexor { base iana-interface-type; description "Proprietary multiplexing."; reference "RFC 2863 - The Interfaces Group MIB"; } identity ieee80212 { base iana-interface-type; description "100BaseVG."; } identity fibreChannel { base iana-interface-type; description "Fibre Channel."; } identity hippiInterface { base iana-interface-type; description "HIPPI interfaces."; } identity frameRelayInterconnect { base iana-interface-type; status obsolete; description "Obsolete; use either frameRelay(32) or frameRelayService(44)."; } identity aflane8023 { base iana-interface-type; description "ATM Emulated LAN for 802.3."; } identity aflane8025 { base iana-interface-type; description "ATM Emulated LAN for 802.5."; } identity cctEmul { base iana-interface-type; description "ATM Emulated circuit."; } identity fastEther { base iana-interface-type; status deprecated; description "Obsoleted via RFC 3635. ethernetCsmacd(6) should be used instead."; reference "RFC 3635 - Definitions of Managed Objects for the Ethernet-like Interface Types"; } identity isdn { base iana-interface-type; description "ISDN and X.25."; reference "RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN in the Packet Mode"; } identity v11 { base iana-interface-type; description "CCITT V.11/X.21."; } identity v36 { base iana-interface-type; description "CCITT V.36."; } identity g703at64k { base iana-interface-type; description "CCITT G703 at 64Kbps."; } identity g703at2mb { base iana-interface-type; status obsolete; description "Obsolete; see DS1-MIB."; } identity qllc { base iana-interface-type; description "SNA QLLC."; } identity fastEtherFX { base iana-interface-type; status deprecated; description "Obsoleted via RFC 3635. ethernetCsmacd(6) should be used instead."; reference "RFC 3635 - Definitions of Managed Objects for the Ethernet-like Interface Types"; } identity channel { base iana-interface-type; description "Channel."; } identity ieee80211 { base iana-interface-type; description "Radio spread spectrum."; } identity ibm370parChan { base iana-interface-type; description "IBM System 360/370 OEMI Channel."; } identity escon { base iana-interface-type; description "IBM Enterprise Systems Connection."; } identity dlsw { base iana-interface-type; description "Data Link Switching."; } identity isdns { base iana-interface-type; description "ISDN S/T interface."; } identity isdnu { base iana-interface-type; description "ISDN U interface."; } identity lapd { base iana-interface-type; description "Link Access Protocol D."; } identity ipSwitch { base iana-interface-type; description "IP Switching Objects."; } identity rsrb { base iana-interface-type; description "Remote Source Route Bridging."; } identity atmLogical { base iana-interface-type; description "ATM Logical Port."; reference "RFC 3606 - Definitions of Supplemental Managed Objects for ATM Interface"; } identity ds0 { base iana-interface-type; description "Digital Signal Level 0."; reference "RFC 2494 - Definitions of Managed Objects for the DS0 and DS0 Bundle Interface Type"; } identity ds0Bundle { base iana-interface-type; description "Group of ds0s on the same ds1."; reference "RFC 2494 - Definitions of Managed Objects for the DS0 and DS0 Bundle Interface Type"; } identity bsc { base iana-interface-type; description "Bisynchronous Protocol."; } identity async { base iana-interface-type; description "Asynchronous Protocol."; } identity cnr { base iana-interface-type; description "Combat Net Radio."; } identity iso88025Dtr { base iana-interface-type; description "ISO 802.5r DTR."; } identity eplrs { base iana-interface-type; description "Ext Pos Loc Report Sys."; } identity arap { base iana-interface-type; description "Appletalk Remote Access Protocol."; } identity propCnls { base iana-interface-type; description "Proprietary Connectionless Protocol."; } identity hostPad { base iana-interface-type; description "CCITT-ITU X.29 PAD Protocol."; } identity termPad { base iana-interface-type; description "CCITT-ITU X.3 PAD Facility."; } identity frameRelayMPI { base iana-interface-type; description "Multiproto Interconnect over FR."; } identity x213 { base iana-interface-type; description "CCITT-ITU X213."; } identity adsl { base iana-interface-type; description "Asymmetric Digital Subscriber Loop."; } identity radsl { base iana-interface-type; description "Rate-Adapt. Digital Subscriber Loop."; } identity sdsl { base iana-interface-type; description "Symmetric Digital Subscriber Loop."; } identity vdsl { base iana-interface-type; description "Very H-Speed Digital Subscrib. Loop."; } identity iso88025CRFPInt { base iana-interface-type; description "ISO 802.5 CRFP."; } identity myrinet { base iana-interface-type; description "Myricom Myrinet."; } identity voiceEM { base iana-interface-type; description "Voice recEive and transMit."; } identity voiceFXO { base iana-interface-type; description "Voice Foreign Exchange Office."; } identity voiceFXS { base iana-interface-type; description "Voice Foreign Exchange Station."; } identity voiceEncap { base iana-interface-type; description "Voice encapsulation."; } identity voiceOverIp { base iana-interface-type; description "Voice over IP encapsulation."; } identity atmDxi { base iana-interface-type; description "ATM DXI."; } identity atmFuni { base iana-interface-type; description "ATM FUNI."; } identity atmIma { base iana-interface-type; description "ATM IMA."; } identity pppMultilinkBundle { base iana-interface-type; description "PPP Multilink Bundle."; } identity ipOverCdlc { base iana-interface-type; description "IBM ipOverCdlc."; } identity ipOverClaw { base iana-interface-type; description "IBM Common Link Access to Workstn."; } identity stackToStack { base iana-interface-type; description "IBM stackToStack."; } identity virtualIpAddress { base iana-interface-type; description "IBM VIPA."; } identity mpc { base iana-interface-type; description "IBM multi-protocol channel support."; } identity ipOverAtm { base iana-interface-type; description "IBM ipOverAtm."; reference "RFC 2320 - Definitions of Managed Objects for Classical IP and ARP Over ATM Using SMIv2 (IPOA-MIB)"; } identity iso88025Fiber { base iana-interface-type; description "ISO 802.5j Fiber Token Ring."; } identity tdlc { base iana-interface-type; description "IBM twinaxial data link control."; } identity gigabitEthernet { base iana-interface-type; status deprecated; description "Obsoleted via RFC 3635. ethernetCsmacd(6) should be used instead."; reference "RFC 3635 - Definitions of Managed Objects for the Ethernet-like Interface Types"; } identity hdlc { base iana-interface-type; description "HDLC."; } identity lapf { base iana-interface-type; description "LAP F."; } identity v37 { base iana-interface-type; description "V.37."; } identity x25mlp { base iana-interface-type; description "Multi-Link Protocol."; } identity x25huntGroup { base iana-interface-type; description "X25 Hunt Group."; } identity transpHdlc { base iana-interface-type; description "Transp HDLC."; } identity interleave { base iana-interface-type; description "Interleave channel."; } identity fast { base iana-interface-type; description "Fast channel."; } identity ip { base iana-interface-type; description "IP (for APPN HPR in IP networks)."; } identity docsCableMaclayer { base iana-interface-type; description "CATV Mac Layer."; } identity docsCableDownstream { base iana-interface-type; description "CATV Downstream interface."; } identity docsCableUpstream { base iana-interface-type; description "CATV Upstream interface."; } identity a12MppSwitch { base iana-interface-type; description "Avalon Parallel Processor."; } identity tunnel { base iana-interface-type; description "Encapsulation interface."; } identity coffee { base iana-interface-type; description "Coffee pot."; reference "RFC 2325 - Coffee MIB"; } identity ces { base iana-interface-type; description "Circuit Emulation Service."; } identity atmSubInterface { base iana-interface-type; description "ATM Sub Interface."; } identity l2vlan { base iana-interface-type; description "Layer 2 Virtual LAN using 802.1Q."; } identity l3ipvlan { base iana-interface-type; description "Layer 3 Virtual LAN using IP."; } identity l3ipxvlan { base iana-interface-type; description "Layer 3 Virtual LAN using IPX."; } identity digitalPowerline { base iana-interface-type; description "IP over Power Lines."; } identity mediaMailOverIp { base iana-interface-type; description "Multimedia Mail over IP."; } identity dtm { base iana-interface-type; description "Dynamic synchronous Transfer Mode."; } identity dcn { base iana-interface-type; description "Data Communications Network."; } identity ipForward { base iana-interface-type; description "IP Forwarding Interface."; } identity msdsl { base iana-interface-type; description "Multi-rate Symmetric DSL."; } identity ieee1394 { base iana-interface-type; description "IEEE1394 High Performance Serial Bus."; } identity if-gsn { base iana-interface-type; description "HIPPI-6400."; } identity dvbRccMacLayer { base iana-interface-type; description "DVB-RCC MAC Layer."; } identity dvbRccDownstream { base iana-interface-type; description "DVB-RCC Downstream Channel."; } identity dvbRccUpstream { base iana-interface-type; description "DVB-RCC Upstream Channel."; } identity atmVirtual { base iana-interface-type; description "ATM Virtual Interface."; } identity mplsTunnel { base iana-interface-type; description "MPLS Tunnel Virtual Interface."; } identity srp { base iana-interface-type; description "Spatial Reuse Protocol."; } identity voiceOverAtm { base iana-interface-type; description "Voice over ATM."; } identity voiceOverFrameRelay { base iana-interface-type; description "Voice Over Frame Relay."; } identity idsl { base iana-interface-type; description "Digital Subscriber Loop over ISDN."; } identity compositeLink { base iana-interface-type; description "Avici Composite Link Interface."; } identity ss7SigLink { base iana-interface-type; description "SS7 Signaling Link."; } identity propWirelessP2P { base iana-interface-type; description "Prop. P2P wireless interface."; } identity frForward { base iana-interface-type; description "Frame Forward Interface."; } identity rfc1483 { base iana-interface-type; description "Multiprotocol over ATM AAL5."; reference "RFC 1483 - Multiprotocol Encapsulation over ATM Adaptation Layer 5"; } identity usb { base iana-interface-type; description "USB Interface."; } identity ieee8023adLag { base iana-interface-type; description "IEEE 802.3ad Link Aggregate."; } identity bgppolicyaccounting { base iana-interface-type; description "BGP Policy Accounting."; } identity frf16MfrBundle { base iana-interface-type; description "FRF.16 Multilink Frame Relay."; } identity h323Gatekeeper { base iana-interface-type; description "H323 Gatekeeper."; } identity h323Proxy { base iana-interface-type; description "H323 Voice and Video Proxy."; } identity mpls { base iana-interface-type; description "MPLS."; } identity mfSigLink { base iana-interface-type; description "Multi-frequency signaling link."; } identity hdsl2 { base iana-interface-type; description "High Bit-Rate DSL - 2nd generation."; } identity shdsl { base iana-interface-type; description "Multirate HDSL2."; } identity ds1FDL { base iana-interface-type; description "Facility Data Link (4Kbps) on a DS1."; } identity pos { base iana-interface-type; description "Packet over SONET/SDH Interface."; } identity dvbAsiIn { base iana-interface-type; description "DVB-ASI Input."; } identity dvbAsiOut { base iana-interface-type; description "DVB-ASI Output."; } identity plc { base iana-interface-type; description "Power Line Communications."; } identity nfas { base iana-interface-type; description "Non-Facility Associated Signaling."; } identity tr008 { base iana-interface-type; description "TR008."; } identity gr303RDT { base iana-interface-type; description "Remote Digital Terminal."; } identity gr303IDT { base iana-interface-type; description "Integrated Digital Terminal."; } identity isup { base iana-interface-type; description "ISUP."; } identity propDocsWirelessMaclayer { base iana-interface-type; description "Cisco proprietary Maclayer."; } identity propDocsWirelessDownstream { base iana-interface-type; description "Cisco proprietary Downstream."; } identity propDocsWirelessUpstream { base iana-interface-type; description "Cisco proprietary Upstream."; } identity hiperlan2 { base iana-interface-type; description "HIPERLAN Type 2 Radio Interface."; } identity propBWAp2Mp { base iana-interface-type; description "PropBroadbandWirelessAccesspt2Multipt (use of this value for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f is deprecated, and ieee80216WMAN(237) should be used instead)."; } identity sonetOverheadChannel { base iana-interface-type; description "SONET Overhead Channel."; } identity digitalWrapperOverheadChannel { base iana-interface-type; description "Digital Wrapper."; } identity aal2 { base iana-interface-type; description "ATM adaptation layer 2."; } identity radioMAC { base iana-interface-type; description "MAC layer over radio links."; } identity atmRadio { base iana-interface-type; description "ATM over radio links."; } identity imt { base iana-interface-type; description "Inter-Machine Trunks."; } identity mvl { base iana-interface-type; description "Multiple Virtual Lines DSL."; } identity reachDSL { base iana-interface-type; description "Long Reach DSL."; } identity frDlciEndPt { base iana-interface-type; description "Frame Relay DLCI End Point."; } identity atmVciEndPt { base iana-interface-type; description "ATM VCI End Point."; } identity opticalChannel { base iana-interface-type; description "Optical Channel."; } identity opticalTransport { base iana-interface-type; description "Optical Transport."; } identity propAtm { base iana-interface-type; description "Proprietary ATM."; } identity voiceOverCable { base iana-interface-type; description "Voice Over Cable Interface."; } identity infiniband { base iana-interface-type; description "Infiniband."; } identity teLink { base iana-interface-type; description "TE Link."; } identity q2931 { base iana-interface-type; description "Q.2931."; } identity virtualTg { base iana-interface-type; description "Virtual Trunk Group."; } identity sipTg { base iana-interface-type; description "SIP Trunk Group."; } identity sipSig { base iana-interface-type; description "SIP Signaling."; } identity docsCableUpstreamChannel { base iana-interface-type; description "CATV Upstream Channel."; } identity econet { base iana-interface-type; description "Acorn Econet."; } identity pon155 { base iana-interface-type; description "FSAN 155Mb Symetrical PON interface."; } identity pon622 { base iana-interface-type; description "FSAN 622Mb Symetrical PON interface."; } identity bridge { base iana-interface-type; description "Transparent bridge interface."; } identity linegroup { base iana-interface-type; description "Interface common to multiple lines."; } identity voiceEMFGD { base iana-interface-type; description "Voice E&M Feature Group D."; } identity voiceFGDEANA { base iana-interface-type; description "Voice FGD Exchange Access North American."; } identity voiceDID { base iana-interface-type; description "Voice Direct Inward Dialing."; } identity mpegTransport { base iana-interface-type; description "MPEG transport interface."; } identity sixToFour { base iana-interface-type; status deprecated; description "6to4 interface (DEPRECATED)."; reference "RFC 4087 - IP Tunnel MIB"; } identity gtp { base iana-interface-type; description "GTP (GPRS Tunneling Protocol)."; } identity pdnEtherLoop1 { base iana-interface-type; description "Paradyne EtherLoop 1."; } identity pdnEtherLoop2 { base iana-interface-type; description "Paradyne EtherLoop 2."; } identity opticalChannelGroup { base iana-interface-type; description "Optical Channel Group."; } identity homepna { base iana-interface-type; description "HomePNA ITU-T G.989."; } identity gfp { base iana-interface-type; description "Generic Framing Procedure (GFP)."; } identity ciscoISLvlan { base iana-interface-type; description "Layer 2 Virtual LAN using Cisco ISL."; } identity actelisMetaLOOP { base iana-interface-type; description "Acteleis proprietary MetaLOOP High Speed Link."; } identity fcipLink { base iana-interface-type; description "FCIP Link."; } identity rpr { base iana-interface-type; description "Resilient Packet Ring Interface Type."; } identity qam { base iana-interface-type; description "RF Qam Interface."; } identity lmp { base iana-interface-type; description "Link Management Protocol."; reference "RFC 4327 - Link Management Protocol (LMP) Management Information Base (MIB)"; } identity cblVectaStar { base iana-interface-type; description "Cambridge Broadband Networks Limited VectaStar."; } identity docsCableMCmtsDownstream { base iana-interface-type; description "CATV Modular CMTS Downstream Interface."; } identity adsl2 { base iana-interface-type; status deprecated; description "Asymmetric Digital Subscriber Loop Version 2 (DEPRECATED/OBSOLETED - please use adsl2plus(238) instead)."; reference "RFC 4706 - Definitions of Managed Objects for Asymmetric Digital Subscriber Line 2 (ADSL2)"; } identity macSecControlledIF { base iana-interface-type; description "MACSecControlled."; } identity macSecUncontrolledIF { base iana-interface-type; description "MACSecUncontrolled."; } identity aviciOpticalEther { base iana-interface-type; description "Avici Optical Ethernet Aggregate."; } identity atmbond { base iana-interface-type; description "atmbond."; } identity voiceFGDOS { base iana-interface-type; description "Voice FGD Operator Services."; } identity mocaVersion1 { base iana-interface-type; description "MultiMedia over Coax Alliance (MoCA) Interface as documented in information provided privately to IANA."; } identity ieee80216WMAN { base iana-interface-type; description "IEEE 802.16 WMAN interface."; } identity adsl2plus { base iana-interface-type; description "Asymmetric Digital Subscriber Loop Version 2 - Version 2 Plus and all variants."; } identity dvbRcsMacLayer { base iana-interface-type; description "DVB-RCS MAC Layer."; reference "RFC 5728 - The SatLabs Group DVB-RCS MIB"; } identity dvbTdm { base iana-interface-type; description "DVB Satellite TDM."; reference "RFC 5728 - The SatLabs Group DVB-RCS MIB"; } identity dvbRcsTdma { base iana-interface-type; description "DVB-RCS TDMA."; reference "RFC 5728 - The SatLabs Group DVB-RCS MIB"; } identity x86Laps { base iana-interface-type; description "LAPS based on ITU-T X.86/Y.1323."; } identity wwanPP { base iana-interface-type; description "3GPP WWAN."; } identity wwanPP2 { base iana-interface-type; description "3GPP2 WWAN."; } identity voiceEBS { base iana-interface-type; description "Voice P-phone EBS physical interface."; } identity ifPwType { base iana-interface-type; description "Pseudowire interface type."; reference "RFC 5601 - Pseudowire (PW) Management Information Base (MIB)"; } identity ilan { base iana-interface-type; description "Internal LAN on a bridge per IEEE 802.1ap."; } identity pip { base iana-interface-type; description "Provider Instance Port on a bridge per IEEE 802.1ah PBB."; } identity aluELP { base iana-interface-type; description "Alcatel-Lucent Ethernet Link Protection."; } identity gpon { base iana-interface-type; description "Gigabit-capable passive optical networks (G-PON) as per ITU-T G.948."; } identity vdsl2 { base iana-interface-type; description "Very high speed digital subscriber line Version 2 (as per ITU-T Recommendation G.993.2)."; reference "RFC 5650 - Definitions of Managed Objects for Very High Speed Digital Subscriber Line 2 (VDSL2)"; } identity capwapDot11Profile { base iana-interface-type; description "WLAN Profile Interface."; reference "RFC 5834 - Control and Provisioning of Wireless Access Points (CAPWAP) Protocol Binding MIB for IEEE 802.11"; } identity capwapDot11Bss { base iana-interface-type; description "WLAN BSS Interface."; reference "RFC 5834 - Control and Provisioning of Wireless Access Points (CAPWAP) Protocol Binding MIB for IEEE 802.11"; } identity capwapWtpVirtualRadio { base iana-interface-type; description "WTP Virtual Radio Interface."; reference "RFC 5833 - Control and Provisioning of Wireless Access Points (CAPWAP) Protocol Base MIB"; } identity bits { base iana-interface-type; description "bitsport."; } identity docsCableUpstreamRfPort { base iana-interface-type; description "DOCSIS CATV Upstream RF Port."; } identity cableDownstreamRfPort { base iana-interface-type; description "CATV downstream RF Port."; } identity vmwareVirtualNic { base iana-interface-type; description "VMware Virtual Network Interface."; } identity ieee802154 { base iana-interface-type; description "IEEE 802.15.4 WPAN interface."; reference "IEEE 802.15.4-2006"; } identity otnOdu { base iana-interface-type; description "OTN Optical Data Unit."; } identity otnOtu { base iana-interface-type; description "OTN Optical channel Transport Unit."; } identity ifVfiType { base iana-interface-type; description "VPLS Forwarding Instance Interface Type."; } identity g9981 { base iana-interface-type; description "G.998.1 bonded interface."; } identity g9982 { base iana-interface-type; description "G.998.2 bonded interface."; } identity g9983 { base iana-interface-type; description "G.998.3 bonded interface."; } identity aluEpon { base iana-interface-type; description "Ethernet Passive Optical Networks (E-PON)."; } identity aluEponOnu { base iana-interface-type; description "EPON Optical Network Unit."; } identity aluEponPhysicalUni { base iana-interface-type; description "EPON physical User to Network interface."; } identity aluEponLogicalLink { base iana-interface-type; description "The emulation of a point-to-point link over the EPON layer."; } identity aluGponOnu { base iana-interface-type; description "GPON Optical Network Unit."; reference "ITU-T G.984.2"; } identity aluGponPhysicalUni { base iana-interface-type; description "GPON physical User to Network interface."; reference "ITU-T G.984.2"; } identity vmwareNicTeam { base iana-interface-type; description "VMware NIC Team."; } } yuma123_2.14/netconf/modules/ietf/ietf-key-chain@2017-06-15.yang0000664000175000017500000002407614770023131023724 0ustar vladimirvladimirmodule ietf-key-chain { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-key-chain"; prefix key-chain; import ietf-yang-types { prefix yang; } import ietf-netconf-acm { prefix nacm; } organization "IETF RTGWG - Routing Area Working Group"; contact "WG Web: WG List: Editor: Acee Lindem Yingzhen Qu Derek Yeung Ing-Wher Chen Jeffrey Zhang "; description "This YANG module defines the generic configuration data for key chains. It is intended that the module will be extended by vendors to define vendor-specific key chain configuration parameters. Copyright (c) 2017 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8177; see the RFC itself for full legal notices."; reference "RFC 8177"; revision 2017-06-15 { description "Initial RFC Revision"; reference "RFC 8177: YANG Data Model for Key Chains"; } feature hex-key-string { description "Support hexadecimal key string."; } feature accept-tolerance { description "Support the tolerance or acceptance limit."; } feature independent-send-accept-lifetime { description "Support for independent send and accept key lifetimes."; } feature crypto-hmac-sha-1-12 { description "Support for TCP HMAC-SHA-1 12-byte digest hack."; } feature cleartext { description "Support for cleartext algorithm. Usage is NOT RECOMMENDED."; } feature aes-cmac-prf-128 { description "Support for AES Cipher-based Message Authentication Code Pseudorandom Function."; } feature aes-key-wrap { description "Support for Advanced Encryption Standard (AES) Key Wrap."; } feature replay-protection-only { description "Provide replay protection without any authentication as required by protocols such as Bidirectional Forwarding Detection (BFD)."; } identity crypto-algorithm { description "Base identity of cryptographic algorithm options."; } identity hmac-sha-1-12 { base crypto-algorithm; if-feature "crypto-hmac-sha-1-12"; description "The HMAC-SHA1-12 algorithm."; } identity aes-cmac-prf-128 { base crypto-algorithm; if-feature "aes-cmac-prf-128"; description "The AES-CMAC-PRF-128 algorithm - required by RFC 5926 for TCP-AO key derivation functions."; } identity md5 { base crypto-algorithm; description "The MD5 algorithm."; } identity sha-1 { base crypto-algorithm; description "The SHA-1 algorithm."; } identity hmac-sha-1 { base crypto-algorithm; description "HMAC-SHA-1 authentication algorithm."; } identity hmac-sha-256 { base crypto-algorithm; description "HMAC-SHA-256 authentication algorithm."; } identity hmac-sha-384 { base crypto-algorithm; description "HMAC-SHA-384 authentication algorithm."; } identity hmac-sha-512 { base crypto-algorithm; description "HMAC-SHA-512 authentication algorithm."; } identity cleartext { base crypto-algorithm; if-feature "cleartext"; description "cleartext."; } identity replay-protection-only { base crypto-algorithm; if-feature "replay-protection-only"; description "Provide replay protection without any authentication as required by protocols such as Bidirectional Forwarding Detection (BFD)."; } typedef key-chain-ref { type leafref { path "/key-chain:key-chains/key-chain:key-chain/key-chain:name"; } description "This type is used by data models that need to reference configured key chains."; } grouping lifetime { description "Key lifetime specification."; choice lifetime { default "always"; description "Options for specifying key accept or send lifetimes"; case always { leaf always { type empty; description "Indicates key lifetime is always valid."; } } case start-end-time { leaf start-date-time { type yang:date-and-time; description "Start time."; } choice end-time { default "infinite"; description "End-time setting."; case infinite { leaf no-end-time { type empty; description "Indicates key lifetime end-time is infinite."; } } case duration { leaf duration { type uint32 { range "1..2147483646"; } units "seconds"; description "Key lifetime duration, in seconds"; } } case end-date-time { leaf end-date-time { type yang:date-and-time; description "End time."; } } } } } } container key-chains { description "All configured key-chains on the device."; list key-chain { key "name"; description "List of key-chains."; leaf name { type string; description "Name of the key-chain."; } leaf description { type string; description "A description of the key-chain"; } container accept-tolerance { if-feature "accept-tolerance"; description "Tolerance for key lifetime acceptance (seconds)."; leaf duration { type uint32; units "seconds"; default "0"; description "Tolerance range, in seconds."; } } leaf last-modified-timestamp { type yang:date-and-time; config false; description "Timestamp of the most recent update to the key-chain"; } list key { key "key-id"; description "Single key in key chain."; leaf key-id { type uint64; description "Numeric value uniquely identifying the key"; } container lifetime { description "Specify a key's lifetime."; choice lifetime { description "Options for specification of send and accept lifetimes."; case send-and-accept-lifetime { description "Send and accept key have the same lifetime."; container send-accept-lifetime { description "Single lifetime specification for both send and accept lifetimes."; uses lifetime; } } case independent-send-accept-lifetime { if-feature "independent-send-accept-lifetime"; description "Independent send and accept key lifetimes."; container send-lifetime { description "Separate lifetime specification for send lifetime."; uses lifetime; } container accept-lifetime { description "Separate lifetime specification for accept lifetime."; uses lifetime; } } } } leaf crypto-algorithm { type identityref { base crypto-algorithm; } mandatory true; description "Cryptographic algorithm associated with key."; } container key-string { description "The key string."; nacm:default-deny-all; choice key-string-style { description "Key string styles"; case keystring { leaf keystring { type string; description "Key string in ASCII format."; } } case hexadecimal { if-feature "hex-key-string"; leaf hexadecimal-string { type yang:hex-string; description "Key in hexadecimal string format. When compared to ASCII, specification in hexadecimal affords greater key entropy with the same number of internal key-string octets. Additionally, it discourages usage of well-known words or numbers."; } } } } leaf send-lifetime-active { type boolean; config false; description "Indicates if the send lifetime of the key-chain key is currently active."; } leaf accept-lifetime-active { type boolean; config false; description "Indicates if the accept lifetime of the key-chain key is currently active."; } } } container aes-key-wrap { if-feature "aes-key-wrap"; description "AES Key Wrap encryption for key-chain key-strings. The encrypted key-strings are encoded as hexadecimal key strings using the hex-key-string leaf."; leaf enable { type boolean; default "false"; description "Enable AES Key Wrap encryption."; } } } } yuma123_2.14/netconf/modules/ietf/ietf-interfaces@2018-02-20.yang0000664000175000017500000011470514770023131024167 0ustar vladimirvladimirmodule ietf-interfaces { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces"; prefix if; import ietf-yang-types { prefix yang; } organization "IETF NETMOD (Network Modeling) Working Group"; contact "WG Web: WG List: Editor: Martin Bjorklund "; description "This module contains a collection of YANG definitions for managing network interfaces. Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8343; see the RFC itself for full legal notices."; revision 2018-02-20 { description "Updated to support NMDA."; reference "RFC 8343: A YANG Data Model for Interface Management"; } revision 2014-05-08 { description "Initial revision."; reference "RFC 7223: A YANG Data Model for Interface Management"; } /* * Typedefs */ typedef interface-ref { type leafref { path "/if:interfaces/if:interface/if:name"; } description "This type is used by data models that need to reference interfaces."; } /* * Identities */ identity interface-type { description "Base identity from which specific interface types are derived."; } /* * Features */ feature arbitrary-names { description "This feature indicates that the device allows user-controlled interfaces to be named arbitrarily."; } feature pre-provisioning { description "This feature indicates that the device supports pre-provisioning of interface configuration, i.e., it is possible to configure an interface whose physical interface hardware is not present on the device."; } feature if-mib { description "This feature indicates that the device implements the IF-MIB."; reference "RFC 2863: The Interfaces Group MIB"; } /* * Data nodes */ container interfaces { description "Interface parameters."; list interface { key "name"; description "The list of interfaces on the device. The status of an interface is available in this list in the operational state. If the configuration of a system-controlled interface cannot be used by the system (e.g., the interface hardware present does not match the interface type), then the configuration is not applied to the system-controlled interface shown in the operational state. If the configuration of a user-controlled interface cannot be used by the system, the configured interface is not instantiated in the operational state. System-controlled interfaces created by the system are always present in this list in the operational state, whether or not they are configured."; leaf name { type string; description "The name of the interface. A device MAY restrict the allowed values for this leaf, possibly depending on the type of the interface. For system-controlled interfaces, this leaf is the device-specific name of the interface. If a client tries to create configuration for a system-controlled interface that is not present in the operational state, the server MAY reject the request if the implementation does not support pre-provisioning of interfaces or if the name refers to an interface that can never exist in the system. A Network Configuration Protocol (NETCONF) server MUST reply with an rpc-error with the error-tag 'invalid-value' in this case. If the device supports pre-provisioning of interface configuration, the 'pre-provisioning' feature is advertised. If the device allows arbitrarily named user-controlled interfaces, the 'arbitrary-names' feature is advertised. When a configured user-controlled interface is created by the system, it is instantiated with the same name in the operational state. A server implementation MAY map this leaf to the ifName MIB object. Such an implementation needs to use some mechanism to handle the differences in size and characters allowed between this leaf and ifName. The definition of such a mechanism is outside the scope of this document."; reference "RFC 2863: The Interfaces Group MIB - ifName"; } leaf description { type string; description "A textual description of the interface. A server implementation MAY map this leaf to the ifAlias MIB object. Such an implementation needs to use some mechanism to handle the differences in size and characters allowed between this leaf and ifAlias. The definition of such a mechanism is outside the scope of this document. Since ifAlias is defined to be stored in non-volatile storage, the MIB implementation MUST map ifAlias to the value of 'description' in the persistently stored configuration."; reference "RFC 2863: The Interfaces Group MIB - ifAlias"; } leaf type { type identityref { base interface-type; } mandatory true; description "The type of the interface. When an interface entry is created, a server MAY initialize the type leaf with a valid value, e.g., if it is possible to derive the type from the name of the interface. If a client tries to set the type of an interface to a value that can never be used by the system, e.g., if the type is not supported or if the type does not match the name of the interface, the server MUST reject the request. A NETCONF server MUST reply with an rpc-error with the error-tag 'invalid-value' in this case."; reference "RFC 2863: The Interfaces Group MIB - ifType"; } leaf enabled { type boolean; default "true"; description "This leaf contains the configured, desired state of the interface. Systems that implement the IF-MIB use the value of this leaf in the intended configuration to set IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry has been initialized, as described in RFC 2863. Changes in this leaf in the intended configuration are reflected in ifAdminStatus."; reference "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; } leaf link-up-down-trap-enable { if-feature if-mib; type enumeration { enum enabled { value 1; description "The device will generate linkUp/linkDown SNMP notifications for this interface."; } enum disabled { value 2; description "The device will not generate linkUp/linkDown SNMP notifications for this interface."; } } description "Controls whether linkUp/linkDown SNMP notifications should be generated for this interface. If this node is not configured, the value 'enabled' is operationally used by the server for interfaces that do not operate on top of any other interface (i.e., there are no 'lower-layer-if' entries), and 'disabled' otherwise."; reference "RFC 2863: The Interfaces Group MIB - ifLinkUpDownTrapEnable"; } leaf admin-status { if-feature if-mib; type enumeration { enum up { value 1; description "Ready to pass packets."; } enum down { value 2; description "Not ready to pass packets and not in some test mode."; } enum testing { value 3; description "In some test mode."; } } config false; mandatory true; description "The desired state of the interface. This leaf has the same read semantics as ifAdminStatus."; reference "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; } leaf oper-status { type enumeration { enum up { value 1; description "Ready to pass packets."; } enum down { value 2; description "The interface does not pass any packets."; } enum testing { value 3; description "In some test mode. No operational packets can be passed."; } enum unknown { value 4; description "Status cannot be determined for some reason."; } enum dormant { value 5; description "Waiting for some external event."; } enum not-present { value 6; description "Some component (typically hardware) is missing."; } enum lower-layer-down { value 7; description "Down due to state of lower-layer interface(s)."; } } config false; mandatory true; description "The current operational state of the interface. This leaf has the same semantics as ifOperStatus."; reference "RFC 2863: The Interfaces Group MIB - ifOperStatus"; } leaf last-change { type yang:date-and-time; config false; description "The time the interface entered its current operational state. If the current state was entered prior to the last re-initialization of the local network management subsystem, then this node is not present."; reference "RFC 2863: The Interfaces Group MIB - ifLastChange"; } leaf if-index { if-feature if-mib; type int32 { range "1..2147483647"; } config false; mandatory true; description "The ifIndex value for the ifEntry represented by this interface."; reference "RFC 2863: The Interfaces Group MIB - ifIndex"; } leaf phys-address { type yang:phys-address; config false; description "The interface's address at its protocol sub-layer. For example, for an 802.x interface, this object normally contains a Media Access Control (MAC) address. The interface's media-specific modules must define the bit and byte ordering and the format of the value of this object. For interfaces that do not have such an address (e.g., a serial line), this node is not present."; reference "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; } leaf-list higher-layer-if { type interface-ref; config false; description "A list of references to interfaces layered on top of this interface."; reference "RFC 2863: The Interfaces Group MIB - ifStackTable"; } leaf-list lower-layer-if { type interface-ref; config false; description "A list of references to interfaces layered underneath this interface."; reference "RFC 2863: The Interfaces Group MIB - ifStackTable"; } leaf speed { type yang:gauge64; units "bits/second"; config false; description "An estimate of the interface's current bandwidth in bits per second. For interfaces that do not vary in bandwidth or for those where no accurate estimation can be made, this node should contain the nominal bandwidth. For interfaces that have no concept of bandwidth, this node is not present."; reference "RFC 2863: The Interfaces Group MIB - ifSpeed, ifHighSpeed"; } container statistics { config false; description "A collection of interface-related statistics objects."; leaf discontinuity-time { type yang:date-and-time; mandatory true; description "The time on the most recent occasion at which any one or more of this interface's counters suffered a discontinuity. If no such discontinuities have occurred since the last re-initialization of the local management subsystem, then this node contains the time the local management subsystem re-initialized itself."; } leaf in-octets { type yang:counter64; description "The total number of octets received on the interface, including framing characters. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; } leaf in-unicast-pkts { type yang:counter64; description "The number of packets, delivered by this sub-layer to a higher (sub-)layer, that were not addressed to a multicast or broadcast address at this sub-layer. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; } leaf in-broadcast-pkts { type yang:counter64; description "The number of packets, delivered by this sub-layer to a higher (sub-)layer, that were addressed to a broadcast address at this sub-layer. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCInBroadcastPkts"; } leaf in-multicast-pkts { type yang:counter64; description "The number of packets, delivered by this sub-layer to a higher (sub-)layer, that were addressed to a multicast address at this sub-layer. For a MAC-layer protocol, this includes both Group and Functional addresses. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCInMulticastPkts"; } leaf in-discards { type yang:counter32; description "The number of inbound packets that were chosen to be discarded even though no errors had been detected to prevent their being deliverable to a higher-layer protocol. One possible reason for discarding such a packet could be to free up buffer space. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifInDiscards"; } leaf in-errors { type yang:counter32; description "For packet-oriented interfaces, the number of inbound packets that contained errors preventing them from being deliverable to a higher-layer protocol. For character- oriented or fixed-length interfaces, the number of inbound transmission units that contained errors preventing them from being deliverable to a higher-layer protocol. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifInErrors"; } leaf in-unknown-protos { type yang:counter32; description "For packet-oriented interfaces, the number of packets received via the interface that were discarded because of an unknown or unsupported protocol. For character-oriented or fixed-length interfaces that support protocol multiplexing, the number of transmission units received via the interface that were discarded because of an unknown or unsupported protocol. For any interface that does not support protocol multiplexing, this counter is not present. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; } leaf out-octets { type yang:counter64; description "The total number of octets transmitted out of the interface, including framing characters. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; } leaf out-unicast-pkts { type yang:counter64; description "The total number of packets that higher-level protocols requested be transmitted and that were not addressed to a multicast or broadcast address at this sub-layer, including those that were discarded or not sent. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; } leaf out-broadcast-pkts { type yang:counter64; description "The total number of packets that higher-level protocols requested be transmitted and that were addressed to a broadcast address at this sub-layer, including those that were discarded or not sent. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCOutBroadcastPkts"; } leaf out-multicast-pkts { type yang:counter64; description "The total number of packets that higher-level protocols requested be transmitted and that were addressed to a multicast address at this sub-layer, including those that were discarded or not sent. For a MAC-layer protocol, this includes both Group and Functional addresses. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCOutMulticastPkts"; } leaf out-discards { type yang:counter32; description "The number of outbound packets that were chosen to be discarded even though no errors had been detected to prevent their being transmitted. One possible reason for discarding such a packet could be to free up buffer space. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; } leaf out-errors { type yang:counter32; description "For packet-oriented interfaces, the number of outbound packets that could not be transmitted because of errors. For character-oriented or fixed-length interfaces, the number of outbound transmission units that could not be transmitted because of errors. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifOutErrors"; } } } } /* * Legacy typedefs */ typedef interface-state-ref { type leafref { path "/if:interfaces-state/if:interface/if:name"; } status deprecated; description "This type is used by data models that need to reference the operationally present interfaces."; } /* * Legacy operational state data nodes */ container interfaces-state { config false; status deprecated; description "Data nodes for the operational state of interfaces."; list interface { key "name"; status deprecated; description "The list of interfaces on the device. System-controlled interfaces created by the system are always present in this list, whether or not they are configured."; leaf name { type string; status deprecated; description "The name of the interface. A server implementation MAY map this leaf to the ifName MIB object. Such an implementation needs to use some mechanism to handle the differences in size and characters allowed between this leaf and ifName. The definition of such a mechanism is outside the scope of this document."; reference "RFC 2863: The Interfaces Group MIB - ifName"; } leaf type { type identityref { base interface-type; } mandatory true; status deprecated; description "The type of the interface."; reference "RFC 2863: The Interfaces Group MIB - ifType"; } leaf admin-status { if-feature if-mib; type enumeration { enum up { value 1; description "Ready to pass packets."; } enum down { value 2; description "Not ready to pass packets and not in some test mode."; } enum testing { value 3; description "In some test mode."; } } mandatory true; status deprecated; description "The desired state of the interface. This leaf has the same read semantics as ifAdminStatus."; reference "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; } leaf oper-status { type enumeration { enum up { value 1; description "Ready to pass packets."; } enum down { value 2; description "The interface does not pass any packets."; } enum testing { value 3; description "In some test mode. No operational packets can be passed."; } enum unknown { value 4; description "Status cannot be determined for some reason."; } enum dormant { value 5; description "Waiting for some external event."; } enum not-present { value 6; description "Some component (typically hardware) is missing."; } enum lower-layer-down { value 7; description "Down due to state of lower-layer interface(s)."; } } mandatory true; status deprecated; description "The current operational state of the interface. This leaf has the same semantics as ifOperStatus."; reference "RFC 2863: The Interfaces Group MIB - ifOperStatus"; } leaf last-change { type yang:date-and-time; status deprecated; description "The time the interface entered its current operational state. If the current state was entered prior to the last re-initialization of the local network management subsystem, then this node is not present."; reference "RFC 2863: The Interfaces Group MIB - ifLastChange"; } leaf if-index { if-feature if-mib; type int32 { range "1..2147483647"; } mandatory true; status deprecated; description "The ifIndex value for the ifEntry represented by this interface."; reference "RFC 2863: The Interfaces Group MIB - ifIndex"; } leaf phys-address { type yang:phys-address; status deprecated; description "The interface's address at its protocol sub-layer. For example, for an 802.x interface, this object normally contains a Media Access Control (MAC) address. The interface's media-specific modules must define the bit and byte ordering and the format of the value of this object. For interfaces that do not have such an address (e.g., a serial line), this node is not present."; reference "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; } leaf-list higher-layer-if { type interface-state-ref; status deprecated; description "A list of references to interfaces layered on top of this interface."; reference "RFC 2863: The Interfaces Group MIB - ifStackTable"; } leaf-list lower-layer-if { type interface-state-ref; status deprecated; description "A list of references to interfaces layered underneath this interface."; reference "RFC 2863: The Interfaces Group MIB - ifStackTable"; } leaf speed { type yang:gauge64; units "bits/second"; status deprecated; description "An estimate of the interface's current bandwidth in bits per second. For interfaces that do not vary in bandwidth or for those where no accurate estimation can be made, this node should contain the nominal bandwidth. For interfaces that have no concept of bandwidth, this node is not present."; reference "RFC 2863: The Interfaces Group MIB - ifSpeed, ifHighSpeed"; } container statistics { status deprecated; description "A collection of interface-related statistics objects."; leaf discontinuity-time { type yang:date-and-time; mandatory true; status deprecated; description "The time on the most recent occasion at which any one or more of this interface's counters suffered a discontinuity. If no such discontinuities have occurred since the last re-initialization of the local management subsystem, then this node contains the time the local management subsystem re-initialized itself."; } leaf in-octets { type yang:counter64; status deprecated; description "The total number of octets received on the interface, including framing characters. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; } leaf in-unicast-pkts { type yang:counter64; status deprecated; description "The number of packets, delivered by this sub-layer to a higher (sub-)layer, that were not addressed to a multicast or broadcast address at this sub-layer. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; } leaf in-broadcast-pkts { type yang:counter64; status deprecated; description "The number of packets, delivered by this sub-layer to a higher (sub-)layer, that were addressed to a broadcast address at this sub-layer. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCInBroadcastPkts"; } leaf in-multicast-pkts { type yang:counter64; status deprecated; description "The number of packets, delivered by this sub-layer to a higher (sub-)layer, that were addressed to a multicast address at this sub-layer. For a MAC-layer protocol, this includes both Group and Functional addresses. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCInMulticastPkts"; } leaf in-discards { type yang:counter32; status deprecated; description "The number of inbound packets that were chosen to be discarded even though no errors had been detected to prevent their being deliverable to a higher-layer protocol. One possible reason for discarding such a packet could be to free up buffer space. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifInDiscards"; } leaf in-errors { type yang:counter32; status deprecated; description "For packet-oriented interfaces, the number of inbound packets that contained errors preventing them from being deliverable to a higher-layer protocol. For character- oriented or fixed-length interfaces, the number of inbound transmission units that contained errors preventing them from being deliverable to a higher-layer protocol. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifInErrors"; } leaf in-unknown-protos { type yang:counter32; status deprecated; description "For packet-oriented interfaces, the number of packets received via the interface that were discarded because of an unknown or unsupported protocol. For character-oriented or fixed-length interfaces that support protocol multiplexing, the number of transmission units received via the interface that were discarded because of an unknown or unsupported protocol. For any interface that does not support protocol multiplexing, this counter is not present. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; } leaf out-octets { type yang:counter64; status deprecated; description "The total number of octets transmitted out of the interface, including framing characters. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; } leaf out-unicast-pkts { type yang:counter64; status deprecated; description "The total number of packets that higher-level protocols requested be transmitted and that were not addressed to a multicast or broadcast address at this sub-layer, including those that were discarded or not sent. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; } leaf out-broadcast-pkts { type yang:counter64; status deprecated; description "The total number of packets that higher-level protocols requested be transmitted and that were addressed to a broadcast address at this sub-layer, including those that were discarded or not sent. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCOutBroadcastPkts"; } leaf out-multicast-pkts { type yang:counter64; status deprecated; description "The total number of packets that higher-level protocols requested be transmitted and that were addressed to a multicast address at this sub-layer, including those that were discarded or not sent. For a MAC-layer protocol, this includes both Group and Functional addresses. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifHCOutMulticastPkts"; } leaf out-discards { type yang:counter32; status deprecated; description "The number of outbound packets that were chosen to be discarded even though no errors had been detected to prevent their being transmitted. One possible reason for discarding such a packet could be to free up buffer space. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; } leaf out-errors { type yang:counter32; status deprecated; description "For packet-oriented interfaces, the number of outbound packets that could not be transmitted because of errors. For character-oriented or fixed-length interfaces, the number of outbound transmission units that could not be transmitted because of errors. Discontinuities in the value of this counter can occur at re-initialization of the management system and at other times as indicated by the value of 'discontinuity-time'."; reference "RFC 2863: The Interfaces Group MIB - ifOutErrors"; } } } } } yuma123_2.14/netconf/modules/ietf/ietf-lmap-control@2017-08-08.yang0000664000175000017500000007244414770023131024471 0ustar vladimirvladimirmodule ietf-lmap-control { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-lmap-control"; prefix "lmapc"; import ietf-yang-types { prefix yang; } import ietf-netconf-acm { prefix nacm; } import ietf-lmap-common { prefix lmap; } organization "IETF Large-Scale Measurement of Broadband Performance Working Group"; contact "WG Web: WG List: Editor: Juergen Schoenwaelder Editor: Vaibhav Bajpai "; description "This module defines a data model for controlling Measurement Agents that are part of a Large-Scale Measurement Platform (LMAP). This data model is expected to be implemented by Measurement Agents."; revision "2017-08-08" { description "Initial version"; reference "RFC 8194: A YANG Data Model for LMAP Measurement Agents"; } /* * Typedefs */ typedef event-ref { type leafref { path "/lmap/events/event/name"; } description "This type is used by data models that need to reference a configured event source."; } typedef task-ref { type leafref { path "/lmap/tasks/task/name"; } description "This type is used by data models that need to reference a configured Task."; } typedef schedule-ref { type leafref { path "/lmap/schedules/schedule/name"; } description "This type is used by data models that need to reference a configured Schedule."; } /* * Groupings */ grouping start-end-grouping { description "A grouping that provides start and end times for Event objects."; leaf start { type yang:date-and-time; description "The date and time when the Event object starts to create triggers."; } leaf end { type yang:date-and-time; description "The date and time when the Event object stops to create triggers. It is generally a good idea to always configure an end time and to refresh the end time as needed to ensure that agents that lose connectivity to their Controller do not continue executing Schedules forever."; } } /* * Capability, configuration, and state data nodes */ container lmap { description "Configuration and control of a Measurement Agent."; container capabilities { config false; description "Agent capabilities including a list of supported Tasks."; leaf version { type string; config false; mandatory true; description "A short description of the software implementing the Measurement Agent. This should include the version number of the Measurement Agent software."; } leaf-list tag { type lmap:tag; config false; description "An optional unordered set of tags that provide additional information about the capabilities of the Measurement Agent."; } container tasks { description "A list of Tasks that the Measurement Agent supports."; list task { key name; description "The list of Tasks supported by the Measurement Agent."; leaf name { type lmap:identifier; description "The unique name of a Task capability."; } uses lmap:registry-grouping; leaf version { type string; description "A short description of the software implementing the Task. This should include the version number of the Measurement Task software."; } leaf program { type string; description "The (local) program to invoke in order to execute the Task."; } } } } /* * Agent Configuration */ container agent { description "Configuration of parameters affecting the whole Measurement Agent."; leaf agent-id { type yang:uuid; description "The agent-id identifies a Measurement Agent with a very low probability of collision. In certain deployments, the agent-id may be considered sensitive, and hence this object is optional."; } leaf group-id { type string; description "The group-id identifies a group of Measurement Agents. In certain deployments, the group-id may be considered less sensitive than the agent-id."; } leaf measurement-point { type string; description "The measurement point indicating where the Measurement Agent is located on a path."; reference "RFC 7398: A Reference Path and Measurement Points for Large-Scale Measurement of Broadband Performance"; } leaf report-agent-id { type boolean; must '. != "true" or ../agent-id' { description "An agent-id must exist for this to be set to true."; } default false; description "The 'report-agent-id' controls whether the 'agent-id' is reported to Collectors."; } leaf report-group-id { type boolean; must '. != "true" or ../group-id' { description "A group-id must exist for this to be set to true."; } default false; description "The 'report-group-id' controls whether the 'group-id' is reported to Collectors."; } leaf report-measurement-point { type boolean; must '. != "true" or ../measurement-point' { description "A measurement-point must exist for this to be set to true."; } default false; description "The 'report-measurement-point' controls whether the 'measurement-point' is reported to Collectors."; } leaf controller-timeout { type uint32; units "seconds"; description "A timer is started after each successful contact with a Controller. When the timer reaches the controller-timeout, an event (controller-lost) is raised indicating that connectivity to the Controller has been lost."; } leaf last-started { type yang:date-and-time; config false; mandatory true; description "The date and time the Measurement Agent last started."; } } /* * Task Configuration */ container tasks { description "Configuration of LMAP Tasks."; list task { key name; description "The list of Tasks configured on the Measurement Agent. Note that a configured Task MUST resolve to a Task listed in the capabilities. Attempts to execute a configured Task that is not listed in the capabilities result in a runtime execution error."; leaf name { type lmap:identifier; description "The unique name of a Task."; } uses lmap:registry-grouping; leaf program { type string; nacm:default-deny-write; description "The (local) program to invoke in order to execute the Task. If this leaf is not set, then the system will try to identify a suitable program based on the registry information present."; } uses lmap:options-grouping { description "The list of Task-specific options."; } leaf-list tag { type lmap:identifier; description "A set of Task-specific tags that are reported together with the measurement results to a Collector. A tag can be used, for example, to carry the Measurement Cycle ID."; } } } /* * Schedule Instructions */ container schedules { description "Configuration of LMAP Schedules. Schedules control which Tasks are executed by the LMAP implementation."; list schedule { key name; description "Configuration of a particular Schedule."; leaf name { type lmap:identifier; description "The locally unique, administratively assigned name for this Schedule."; } leaf start { type event-ref; mandatory true; description "The event source controlling the start of the scheduled Actions."; } choice stop { description "This choice contains optional leafs that control the graceful forced termination of scheduled Actions. When the end has been reached, the scheduled Actions should be forced to terminate the measurements. This may involve being active some additional time in order to properly finish the Action's activity (e.g., waiting for any messages that are still outstanding)."; leaf end { type event-ref; description "The event source controlling the graceful forced termination of the scheduled Actions."; } leaf duration { type uint32; units "seconds"; description "The duration controlling the graceful forced termination of the scheduled Actions."; } } leaf execution-mode { type enumeration { enum sequential { value 1; description "The Actions of the Schedule are executed sequentially."; } enum parallel { value 2; description "The Actions of the Schedule are executed concurrently."; } enum pipelined { value 3; description "The Actions of the Schedule are executed in a pipelined mode. Output created by an Action is passed as input to the subsequent Action."; } } default pipelined; description "The execution mode of this Schedule determines in which order the Actions of the Schedule are executed."; } leaf-list tag { type lmap:tag; description "A set of Schedule-specific tags that are reported together with the measurement results to a Collector."; } leaf-list suppression-tag { type lmap:tag; description "A set of Suppression tags that are used to select Schedules to be suppressed."; } leaf state { type enumeration { enum enabled { value 1; description "The value 'enabled' indicates that the Schedule is currently enabled."; } enum disabled { value 2; description "The value 'disabled' indicates that the Schedule is currently disabled."; } enum running { value 3; description "The value 'running' indicates that the Schedule is currently running."; } enum suppressed { value 4; description "The value 'suppressed' indicates that the Schedule is currently suppressed."; } } config false; mandatory true; description "The current state of the Schedule."; } leaf storage { type yang:gauge64; units "bytes"; config false; mandatory true; description "The amount of secondary storage (e.g., allocated in a file system) holding temporary data allocated to the Schedule in bytes. This object reports the amount of allocated physical storage and not the storage used by logical data records."; } leaf invocations { type yang:counter32; config false; mandatory true; description "Number of invocations of this Schedule. This counter does not include suppressed invocations or invocations that were prevented due to an overlap with a previous invocation of this Schedule."; } leaf suppressions { type yang:counter32; config false; mandatory true; description "Number of suppressed executions of this Schedule."; } leaf overlaps { type yang:counter32; config false; mandatory true; description "Number of executions prevented due to overlaps with a previous invocation of this Schedule."; } leaf failures { type yang:counter32; config false; mandatory true; description "Number of failed executions of this Schedule. A failed execution is an execution where at least one Action failed."; } leaf last-invocation { type yang:date-and-time; config false; description "The date and time of the last invocation of this Schedule."; } list action { key name; description "An Action describes a Task that is invoked by the Schedule. Multiple Actions are invoked according to the execution-mode of the Schedule."; leaf name { type lmap:identifier; description "The unique identifier for this Action."; } leaf task { type task-ref; mandatory true; description "The Task invoked by this Action."; } container parameters { description "This container is a placeholder for runtime parameters defined in Task-specific data models augmenting the base LMAP control data model."; choice extension { description "This choice is provided to augment in different sets of parameters."; } } uses lmap:options-grouping { description "The list of Action-specific options that are appended to the list of Task-specific options."; } leaf-list destination { type schedule-ref; description "A set of Schedules receiving the output produced by this Action. The output is stored temporarily since the Destination Schedules will in general not be running when output is passed to them. The behavior of an Action passing data to its own Schedule is implementation specific. Data passed to a sequential or pipelined Schedule is received by the Schedule's first Action. Data passed to a parallel Schedule is received by all Actions of the Schedule."; } leaf-list tag { type lmap:tag; description "A set of Action-specific tags that are reported together with the measurement results to a Collector."; } leaf-list suppression-tag { type lmap:tag; description "A set of Suppression tags that are used to select Actions to be suppressed."; } leaf state { type enumeration { enum enabled { value 1; description "The value 'enabled' indicates that the Action is currently enabled."; } enum disabled { value 2; description "The value 'disabled' indicates that the Action is currently disabled."; } enum running { value 3; description "The value 'running' indicates that the Action is currently running."; } enum suppressed { value 4; description "The value 'suppressed' indicates that the Action is currently suppressed."; } } config false; mandatory true; description "The current state of the Action."; } leaf storage { type yang:gauge64; units "bytes"; config false; mandatory true; description "The amount of secondary storage (e.g., allocated in a file system) holding temporary data allocated to the Schedule in bytes. This object reports the amount of allocated physical storage and not the storage used by logical data records."; } leaf invocations { type yang:counter32; config false; mandatory true; description "Number of invocations of this Action. This counter does not include suppressed invocations or invocations that were prevented due to an overlap with a previous invocation of this Action."; } leaf suppressions { type yang:counter32; config false; mandatory true; description "Number of suppressed executions of this Action."; } leaf overlaps { type yang:counter32; config false; mandatory true; description "Number of executions prevented due to overlaps with a previous invocation of this Action."; } leaf failures { type yang:counter32; config false; mandatory true; description "Number of failed executions of this Action."; } leaf last-invocation { type yang:date-and-time; config false; mandatory true; description "The date and time of the last invocation of this Action."; } leaf last-completion { type yang:date-and-time; config false; mandatory true; description "The date and time of the last completion of this Action."; } leaf last-status { type lmap:status-code; config false; mandatory true; description "The status code returned by the last execution of this Action."; } leaf last-message { type string; config false; mandatory true; description "The status message produced by the last execution of this Action."; } leaf last-failed-completion { type yang:date-and-time; config false; mandatory true; description "The date and time of the last failed completion of this Action."; } leaf last-failed-status { type lmap:status-code; config false; mandatory true; description "The status code returned by the last failed execution of this Action."; } leaf last-failed-message { type string; config false; mandatory true; description "The status message produced by the last failed execution of this Action."; } } } } /* * Suppression Instructions */ container suppressions { description "Suppression information to prevent Schedules or certain Actions from starting."; list suppression { key name; description "Configuration of a particular Suppression."; leaf name { type lmap:identifier; description "The locally unique, administratively assigned name for this Suppression."; } leaf start { type event-ref; description "The event source controlling the start of the Suppression period."; } leaf end { type event-ref; description "The event source controlling the end of the Suppression period. If not present, Suppression continues indefinitely."; } leaf-list match { type lmap:glob-pattern; description "A set of Suppression match patterns. The Suppression will apply to all Schedules (and their Actions) that have a matching value in their suppression-tags and to all Actions that have a matching value in their suppression-tags."; } leaf stop-running { type boolean; default false; description "If 'stop-running' is true, running Schedules and Actions matching the Suppression will be terminated when Suppression is activated. If 'stop-running' is false, running Schedules and Actions will not be affected if Suppression is activated."; } leaf state { type enumeration { enum enabled { value 1; description "The value 'enabled' indicates that the Suppression is currently enabled."; } enum disabled { value 2; description "The value 'disabled' indicates that the Suppression is currently disabled."; } enum active { value 3; description "The value 'active' indicates that the Suppression is currently active."; } } config false; mandatory true; description "The current state of the Suppression."; } } } /* * Event Instructions */ container events { description "Configuration of LMAP events. Implementations may be forced to delay acting upon the occurrence of events in the face of local constraints. An Action triggered by an event therefore should not rely on the accuracy provided by the scheduler implementation."; list event { key name; description "The list of event sources configured on the Measurement Agent."; leaf name { type lmap:identifier; description "The unique name of an event source."; } leaf random-spread { type uint32; units seconds; description "This optional leaf adds a random spread to the computation of the event's trigger time. The random spread is a uniformly distributed random number taken from the interval [0:random-spread]."; } leaf cycle-interval { type uint32; units seconds; description "The optional cycle-interval defines the duration of the time interval in seconds that is used to calculate cycle numbers. No cycle number is calculated if the optional cycle-interval does not exist."; } choice event-type { description "Different types of events are handled by different branches of this choice. Note that this choice can be extended via augmentations."; case periodic { container periodic { description "A periodic timing object triggers periodically according to a regular interval."; leaf interval { type uint32 { range "1..max"; } units "seconds"; mandatory true; description "The number of seconds between two triggers generated by this periodic timing object."; } uses start-end-grouping; } } case calendar { container calendar { description "A calendar timing object triggers based on the current calendar date and time."; leaf-list month { type lmap:month-or-all; min-elements 1; description "A set of months at which this calendar timing will trigger. The wildcard means all months."; } leaf-list day-of-month { type lmap:day-of-months-or-all; min-elements 1; description "A set of days of the month at which this calendar timing will trigger. The wildcard means all days of a month."; } leaf-list day-of-week { type lmap:weekday-or-all; min-elements 1; description "A set of weekdays at which this calendar timing will trigger. The wildcard means all weekdays."; } leaf-list hour { type lmap:hour-or-all; min-elements 1; description "A set of hours at which this calendar timing will trigger. The wildcard means all hours of a day."; } leaf-list minute { type lmap:minute-or-all; min-elements 1; description "A set of minutes at which this calendar timing will trigger. The wildcard means all minutes of an hour."; } leaf-list second { type lmap:second-or-all; min-elements 1; description "A set of seconds at which this calendar timing will trigger. The wildcard means all seconds of a minute."; } leaf timezone-offset { type lmap:timezone-offset; description "The time zone in which this calendar timing object will be evaluated. If not present, the system's local time zone will be used."; } uses start-end-grouping; } } case one-off { container one-off { description "A one-off timing object triggers exactly once."; leaf time { type yang:date-and-time; mandatory true; description "This one-off timing object triggers once at the configured date and time."; } } } case immediate { leaf immediate { type empty; mandatory true; description "This immediate Event object triggers immediately when it is configured."; } } case startup { leaf startup { type empty; mandatory true; description "This startup Event object triggers whenever the Measurement Agent (re)starts."; } } case controller-lost { leaf controller-lost { type empty; mandatory true; description "The controller-lost Event object triggers when the connectivity to the Controller has been lost for at least 'controller-timeout' seconds."; } } case controller-connected { leaf controller-connected { type empty; mandatory true; description "The controller-connected Event object triggers when the connectivity to the Controller has been restored after it was lost for at least 'controller-timeout' seconds."; } } } } } } } yuma123_2.14/netconf/modules/ietf/ietf-snmp-community@2014-12-10.yang0000664000175000017500000001650014770023131025031 0ustar vladimirvladimirsubmodule ietf-snmp-community { belongs-to ietf-snmp { prefix snmp; } import ietf-netconf-acm { prefix nacm; } include ietf-snmp-common; include ietf-snmp-target; include ietf-snmp-proxy; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This submodule contains a collection of YANG definitions for configuring community-based SNMP. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; reference "RFC 3584: Coexistence between Version 1, Version 2, and Version 3 of the Internet-standard Network Management Framework"; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } augment /snmp:snmp { list community { key index; description "List of communities."; reference "RFC 3584: Coexistence between Version 1, Version 2, and Version 3 of the Internet-standard Network Management Framework. SNMP-COMMUNITY-MIB.snmpCommunityTable"; leaf index { type snmp:identifier; description "Index into the community list."; reference "RFC 3584: Coexistence between Version 1, Version 2, and Version 3 of the Internet-standard Network Management Framework. SNMP-COMMUNITY-MIB.snmpCommunityIndex"; } choice name { nacm:default-deny-all; description "The community name, specified as either a string or a binary value. The binary name is used when the community name contains characters that are not legal in a string. If not set, the value of 'security-name' is operationally used as the snmpCommunityName."; reference "RFC 3584: Coexistence between Version 1, Version 2, and Version 3 of the Internet-standard Network Management Framework. SNMP-COMMUNITY-MIB.snmpCommunityName"; leaf text-name { type string; description "A community name that can be represented as a YANG string."; } leaf binary-name { type binary; description "A community name represented as a binary value."; } } leaf security-name { type snmp:security-name; mandatory true; nacm:default-deny-all; description "The snmpCommunitySecurityName of this entry."; reference "RFC 3584: Coexistence between Version 1, Version 2, and Version 3 of the Internet-standard Network Management Framework. SNMP-COMMUNITY-MIB.snmpCommunitySecurityName"; } leaf engine-id { if-feature snmp:proxy; type snmp:engine-id; description "If not set, the value of the local SNMP engine is operationally used by the device."; reference "RFC 3584: Coexistence between Version 1, Version 2, and Version 3 of the Internet-standard Network Management Framework. SNMP-COMMUNITY-MIB.snmpCommunityContextEngineID"; } leaf context { type snmp:context-name; default ""; description "The context in which management information is accessed when using the community string specified by this entry."; reference "RFC 3584: Coexistence between Version 1, Version 2, and Version 3 of the Internet-standard Network Management Framework. SNMP-COMMUNITY-MIB.snmpCommunityContextName"; } leaf target-tag { type snmp:tag-value; description "Used to limit access for this community to the specified targets. Implementations MAY restrict the values of this leaf to be one of the available values of /snmp/target/tag in a valid configuration."; reference "RFC 3584: Coexistence between Version 1, Version 2, and Version 3 of the Internet-standard Network Management Framework. SNMP-COMMUNITY-MIB.snmpCommunityTransportTag"; } } } grouping v1-target-params { container v1 { description "SNMPv1 parameters type. Represents snmpTargetParamsMPModel '0', snmpTargetParamsSecurityModel '1', and snmpTargetParamsSecurityLevel 'noAuthNoPriv'."; leaf security-name { type snmp:security-name; mandatory true; description "Implementations MAY restrict the values of this leaf to be one of the available values of /snmp/community/security-name in a valid configuration."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetParamsSecurityName"; } } } grouping v2c-target-params { container v2c { description "SNMPv2 community parameters type. Represents snmpTargetParamsMPModel '1', snmpTargetParamsSecurityModel '2', and snmpTargetParamsSecurityLevel 'noAuthNoPriv'."; leaf security-name { type snmp:security-name; mandatory true; description "Implementations MAY restrict the values of this leaf to be one of the available values of /snmp/community/security-name in a valid configuration."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetParamsSecurityName"; } } } augment /snmp:snmp/snmp:target-params/snmp:params { case v1 { uses v1-target-params; } case v2c { uses v2c-target-params; } } augment /snmp:snmp/snmp:target { when "snmp:v1 or snmp:v2c"; leaf mms { type union { type enumeration { enum "unknown" { value 0; } } type int32 { range "484..max"; } } default "484"; description "The maximum message size."; reference "RFC 3584: Coexistence between Version 1, Version 2, and Version 3 of the Internet-standard Network Management Framework. SNMP-COMMUNITY-MIB.snmpTargetAddrMMS"; } } } yuma123_2.14/netconf/modules/ietf/ietf-netconf-monitoring@2010-10-04.yang0000664000175000017500000004200714770023131025647 0ustar vladimirvladimirmodule ietf-netconf-monitoring { namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"; prefix "ncm"; import ietf-yang-types { prefix yang; } import ietf-inet-types { prefix inet; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: WG List: WG Chair: Mehmet Ersue WG Chair: Bert Wijnen Editor: Mark Scott Editor: Martin Bjorklund "; description "NETCONF Monitoring Module. All elements in this module are read-only. Copyright (c) 2010 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 6022; see the RFC itself for full legal notices."; revision 2010-10-04 { description "Initial revision."; reference "RFC 6022: YANG Module for NETCONF Monitoring"; } typedef netconf-datastore-type { type enumeration { enum running; enum candidate; enum startup; } description "Enumeration of possible NETCONF datastore types."; reference "RFC 4741: NETCONF Configuration Protocol"; } identity transport { description "Base identity for NETCONF transport types."; } identity netconf-ssh { base transport; description "NETCONF over Secure Shell (SSH)."; reference "RFC 4742: Using the NETCONF Configuration Protocol over Secure SHell (SSH)"; } identity netconf-soap-over-beep { base transport; description "NETCONF over Simple Object Access Protocol (SOAP) over Blocks Extensible Exchange Protocol (BEEP)."; reference "RFC 4743: Using NETCONF over the Simple Object Access Protocol (SOAP)"; } identity netconf-soap-over-https { base transport; description "NETCONF over Simple Object Access Protocol (SOAP) over Hypertext Transfer Protocol Secure (HTTPS)."; reference "RFC 4743: Using NETCONF over the Simple Object Access Protocol (SOAP)"; } identity netconf-beep { base transport; description "NETCONF over Blocks Extensible Exchange Protocol (BEEP)."; reference "RFC 4744: Using the NETCONF Protocol over the Blocks Extensible Exchange Protocol (BEEP)"; } identity netconf-tls { base transport; description "NETCONF over Transport Layer Security (TLS)."; reference "RFC 5539: NETCONF over Transport Layer Security (TLS)"; } identity schema-format { description "Base identity for data model schema languages."; } identity xsd { base schema-format; description "W3C XML Schema Definition."; reference "W3C REC REC-xmlschema-1-20041028: XML Schema Part 1: Structures"; } identity yang { base schema-format; description "The YANG data modeling language for NETCONF."; reference "RFC 6020: YANG - A Data Modeling Language for the Network Configuration Protocol (NETCONF)"; } identity yin { base schema-format; description "The YIN syntax for YANG."; reference "RFC 6020: YANG - A Data Modeling Language for the Network Configuration Protocol (NETCONF)"; } identity rng { base schema-format; description "Regular Language for XML Next Generation (RELAX NG)."; reference "ISO/IEC 19757-2:2008: RELAX NG"; } identity rnc { base schema-format; description "Relax NG Compact Syntax"; reference "ISO/IEC 19757-2:2008: RELAX NG"; } grouping common-counters { description "Counters that exist both per session, and also globally, accumulated from all sessions."; leaf in-rpcs { type yang:zero-based-counter32; description "Number of correct messages received."; } leaf in-bad-rpcs { type yang:zero-based-counter32; description "Number of messages received when an message was expected, that were not correct messages. This includes XML parse errors and errors on the rpc layer."; } leaf out-rpc-errors { type yang:zero-based-counter32; description "Number of messages sent that contained an element."; } leaf out-notifications { type yang:zero-based-counter32; description "Number of messages sent."; } } container netconf-state { config false; description "The netconf-state container is the root of the monitoring data model."; container capabilities { description "Contains the list of NETCONF capabilities supported by the server."; leaf-list capability { type inet:uri; description "List of NETCONF capabilities supported by the server."; } } container datastores { description "Contains the list of NETCONF configuration datastores."; list datastore { key name; description "List of NETCONF configuration datastores supported by the NETCONF server and related information."; leaf name { type netconf-datastore-type; description "Name of the datastore associated with this list entry."; } container locks { presence "This container is present only if the datastore is locked."; description "The NETCONF and operations allow a client to lock specific resources in a datastore. The NETCONF server will prevent changes to the locked resources by all sessions except the one that acquired the lock(s). Monitoring information is provided for each datastore entry including details such as the session that acquired the lock, the type of lock (global or partial) and the list of locked resources. Multiple locks per datastore are supported."; grouping lock-info { description "Lock related parameters, common to both global and partial locks."; leaf locked-by-session { type uint32; mandatory true; description "The session ID of the session that has locked this resource. Both a global lock and a partial lock MUST contain the NETCONF session-id. If the lock is held by a session that is not managed by the NETCONF server (e.g., a CLI session), a session id of 0 (zero) is reported."; reference "RFC 4741: NETCONF Configuration Protocol"; } leaf locked-time { type yang:date-and-time; mandatory true; description "The date and time of when the resource was locked."; } } choice lock-type { description "Indicates if a global lock or a set of partial locks are set."; container global-lock { description "Present if the global lock is set."; uses lock-info; } list partial-lock { key lock-id; description "List of partial locks."; reference "RFC 5717: Partial Lock Remote Procedure Call (RPC) for NETCONF"; leaf lock-id { type uint32; description "This is the lock id returned in the response."; } uses lock-info; leaf-list select { type yang:xpath1.0; min-elements 1; description "The xpath expression that was used to request the lock. The select expression indicates the original intended scope of the lock."; } leaf-list locked-node { type instance-identifier; description "The list of instance-identifiers (i.e., the locked nodes). The scope of the partial lock is defined by the list of locked nodes."; } } } } } } container schemas { description "Contains the list of data model schemas supported by the server."; list schema { key "identifier version format"; description "List of data model schemas supported by the server."; leaf identifier { type string; description "Identifier to uniquely reference the schema. The identifier is used in the operation and may be used for other purposes such as file retrieval. For modeling languages that support or require a data model name (e.g., YANG module name) the identifier MUST match that name. For YANG data models, the identifier is the name of the module or submodule. In other cases, an identifier such as a filename MAY be used instead."; } leaf version { type string; description "Version of the schema supported. Multiple versions MAY be supported simultaneously by a NETCONF server. Each version MUST be reported individually in the schema list, i.e., with same identifier, possibly different location, but different version. For YANG data models, version is the value of the most recent YANG 'revision' statement in the module or submodule, or the empty string if no 'revision' statement is present."; } leaf format { type identityref { base schema-format; } description "The data modeling language the schema is written in (currently xsd, yang, yin, rng, or rnc). For YANG data models, 'yang' format MUST be supported and 'yin' format MAY also be provided."; } leaf namespace { type inet:uri; mandatory true; description "The XML namespace defined by the data model. For YANG data models, this is the module's namespace. If the list entry describes a submodule, this field contains the namespace of the module to which the submodule belongs."; } leaf-list location { type union { type enumeration { enum "NETCONF"; } type inet:uri; } description "One or more locations from which the schema can be retrieved. This list SHOULD contain at least one entry per schema. A schema entry may be located on a remote file system (e.g., reference to file system for ftp retrieval) or retrieved directly from a server supporting the operation (denoted by the value 'NETCONF')."; } } } container sessions { description "The sessions container includes session-specific data for NETCONF management sessions. The session list MUST include all currently active NETCONF sessions."; list session { key session-id; description "All NETCONF sessions managed by the NETCONF server MUST be reported in this list."; leaf session-id { type uint32 { range "1..max"; } description "Unique identifier for the session. This value is the NETCONF session identifier, as defined in RFC 4741."; reference "RFC 4741: NETCONF Configuration Protocol"; } leaf transport { type identityref { base transport; } mandatory true; description "Identifies the transport for each session, e.g., 'netconf-ssh', 'netconf-soap', etc."; } leaf username { type string; mandatory true; description "The username is the client identity that was authenticated by the NETCONF transport protocol. The algorithm used to derive the username is NETCONF transport protocol specific and in addition specific to the authentication mechanism used by the NETCONF transport protocol."; } leaf source-host { type inet:host; description "Host identifier of the NETCONF client. The value returned is implementation specific (e.g., hostname, IPv4 address, IPv6 address)"; } leaf login-time { type yang:date-and-time; mandatory true; description "Time at the server at which the session was established."; } uses common-counters { description "Per-session counters. Zero based with following reset behaviour: - at start of a session - when max value is reached"; } } } container statistics { description "Statistical data pertaining to the NETCONF server."; leaf netconf-start-time { type yang:date-and-time; description "Date and time at which the management subsystem was started."; } leaf in-bad-hellos { type yang:zero-based-counter32; description "Number of sessions silently dropped because an invalid message was received. This includes messages with a 'session-id' attribute, bad namespace, and bad capability declarations."; } leaf in-sessions { type yang:zero-based-counter32; description "Number of sessions started. This counter is incremented when a message with a is sent. 'in-sessions' - 'in-bad-hellos' = 'number of correctly started netconf sessions'"; } leaf dropped-sessions { type yang:zero-based-counter32; description "Number of sessions that were abnormally terminated, e.g., due to idle timeout or transport close. This counter is not incremented when a session is properly closed by a operation, or killed by a operation."; } uses common-counters { description "Global counters, accumulated from all sessions. Zero based with following reset behaviour: - re-initialization of NETCONF server - when max value is reached"; } } } rpc get-schema { description "This operation is used to retrieve a schema from the NETCONF server. Positive Response: The NETCONF server returns the requested schema. Negative Response: If requested schema does not exist, the is 'invalid-value'. If more than one schema matches the requested parameters, the is 'operation-failed', and is 'data-not-unique'."; input { leaf identifier { type string; mandatory true; description "Identifier for the schema list entry."; } leaf version { type string; description "Version of the schema requested. If this parameter is not present, and more than one version of the schema exists on the server, a 'data-not-unique' error is returned, as described above."; } leaf format { type identityref { base schema-format; } description "The data modeling language of the schema. If this parameter is not present, and more than one formats of the schema exists on the server, a 'data-not-unique' error is returned, as described above."; } } output { anyxml data { description "Contains the schema content."; } } } } yuma123_2.14/netconf/modules/ietf/ietf-routing@2016-11-04.yang0000664000175000017500000004070414770023131023530 0ustar vladimirvladimirmodule ietf-routing { yang-version "1.1"; namespace "urn:ietf:params:xml:ns:yang:ietf-routing"; prefix "rt"; import ietf-yang-types { prefix "yang"; } import ietf-interfaces { prefix "if"; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Lou Berger WG Chair: Kent Watsen Editor: Ladislav Lhotka Editor: Acee Lindem "; description "This YANG module defines essential components for the management of a routing subsystem. Copyright (c) 2016 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'MAY', and 'OPTIONAL' in the module text are to be interpreted as described in RFC 2119. This version of this YANG module is part of RFC 8022; see the RFC itself for full legal notices."; revision 2016-11-04 { description "Initial revision."; reference "RFC 8022: A YANG Data Model for Routing Management"; } /* Features */ feature multiple-ribs { description "This feature indicates that the server supports user-defined RIBs. Servers that do not advertise this feature SHOULD provide exactly one system-controlled RIB per supported address family and make it also the default RIB. This RIB then appears as an entry of the list /routing-state/ribs/rib."; } feature router-id { description "This feature indicates that the server supports configuration of an explicit 32-bit router ID that is used by some routing protocols. Servers that do not advertise this feature set a router ID algorithmically, usually to one of the configured IPv4 addresses. However, this algorithm is implementation specific."; } /* Identities */ identity address-family { description "Base identity from which identities describing address families are derived."; } identity ipv4 { base address-family; description "This identity represents IPv4 address family."; } identity ipv6 { base address-family; description "This identity represents IPv6 address family."; } identity control-plane-protocol { description "Base identity from which control-plane protocol identities are derived."; } identity routing-protocol { base control-plane-protocol; description "Identity from which Layer 3 routing protocol identities are derived."; } identity direct { base routing-protocol; description "Routing pseudo-protocol that provides routes to directly connected networks."; } identity static { base routing-protocol; description "Static routing pseudo-protocol."; } /* Type Definitions */ typedef route-preference { type uint32; description "This type is used for route preferences."; } /* Groupings */ grouping address-family { description "This grouping provides a leaf identifying an address family."; leaf address-family { type identityref { base address-family; } mandatory "true"; description "Address family."; } } grouping router-id { description "This grouping provides router ID."; leaf router-id { type yang:dotted-quad; description "A 32-bit number in the form of a dotted quad that is used by some routing protocols identifying a router."; reference "RFC 2328: OSPF Version 2."; } } grouping special-next-hop { description "This grouping provides a leaf with an enumeration of special next hops."; leaf special-next-hop { type enumeration { enum blackhole { description "Silently discard the packet."; } enum unreachable { description "Discard the packet and notify the sender with an error message indicating that the destination host is unreachable."; } enum prohibit { description "Discard the packet and notify the sender with an error message indicating that the communication is administratively prohibited."; } enum receive { description "The packet will be received by the local system."; } } description "Options for special next hops."; } } grouping next-hop-content { description "Generic parameters of next hops in static routes."; choice next-hop-options { mandatory "true"; description "Options for next hops in static routes. It is expected that further cases will be added through augments from other modules."; case simple-next-hop { description "This case represents a simple next hop consisting of the next-hop address and/or outgoing interface. Modules for address families MUST augment this case with a leaf containing a next-hop address of that address family."; leaf outgoing-interface { type if:interface-ref; description "Name of the outgoing interface."; } } case special-next-hop { uses special-next-hop; } case next-hop-list { container next-hop-list { description "Container for multiple next-hops."; list next-hop { key "index"; description "An entry of a next-hop list. Modules for address families MUST augment this list with a leaf containing a next-hop address of that address family."; leaf index { type string; description "A user-specified identifier utilized to uniquely reference the next-hop entry in the next-hop list. The value of this index has no semantic meaning other than for referencing the entry."; } leaf outgoing-interface { type if:interface-ref; description "Name of the outgoing interface."; } } } } } } grouping next-hop-state-content { description "Generic parameters of next hops in state data."; choice next-hop-options { mandatory "true"; description "Options for next hops in state data. It is expected that further cases will be added through augments from other modules, e.g., for recursive next hops."; case simple-next-hop { description "This case represents a simple next hop consisting of the next-hop address and/or outgoing interface. Modules for address families MUST augment this case with a leaf containing a next-hop address of that address family."; leaf outgoing-interface { type if:interface-state-ref; description "Name of the outgoing interface."; } } case special-next-hop { uses special-next-hop; } case next-hop-list { container next-hop-list { description "Container for multiple next hops."; list next-hop { description "An entry of a next-hop list. Modules for address families MUST augment this list with a leaf containing a next-hop address of that address family."; leaf outgoing-interface { type if:interface-state-ref; description "Name of the outgoing interface."; } } } } } } grouping route-metadata { description "Common route metadata."; leaf source-protocol { type identityref { base routing-protocol; } mandatory "true"; description "Type of the routing protocol from which the route originated."; } leaf active { type empty; description "Presence of this leaf indicates that the route is preferred among all routes in the same RIB that have the same destination prefix."; } leaf last-updated { type yang:date-and-time; description "Time stamp of the last modification of the route. If the route was never modified, it is the time when the route was inserted into the RIB."; } } /* State data */ container routing-state { config "false"; description "State data of the routing subsystem."; uses router-id { description "Global router ID. It may be either configured or assigned algorithmically by the implementation."; } container interfaces { description "Network-layer interfaces used for routing."; leaf-list interface { type if:interface-state-ref; description "Each entry is a reference to the name of a configured network-layer interface."; } } container control-plane-protocols { description "Container for the list of routing protocol instances."; list control-plane-protocol { key "type name"; description "State data of a control-plane protocol instance. An implementation MUST provide exactly one system-controlled instance of the 'direct' pseudo-protocol. Instances of other control-plane protocols MAY be created by configuration."; leaf type { type identityref { base control-plane-protocol; } description "Type of the control-plane protocol."; } leaf name { type string; description "The name of the control-plane protocol instance. For system-controlled instances this name is persistent, i.e., it SHOULD NOT change across reboots."; } } } container ribs { description "Container for RIBs."; list rib { key "name"; min-elements "1"; description "Each entry represents a RIB identified by the 'name' key. All routes in a RIB MUST belong to the same address family. An implementation SHOULD provide one system-controlled default RIB for each supported address family."; leaf name { type string; description "The name of the RIB."; } uses address-family; leaf default-rib { if-feature "multiple-ribs"; type boolean; default "true"; description "This flag has the value of 'true' if and only if the RIB is the default RIB for the given address family. By default, control-plane protocols place their routes in the default RIBs."; } container routes { description "Current content of the RIB."; list route { description "A RIB route entry. This data node MUST be augmented with information specific for routes of each address family."; leaf route-preference { type route-preference; description "This route attribute, also known as administrative distance, allows for selecting the preferred route among routes with the same destination prefix. A smaller value means a more preferred route."; } container next-hop { description "Route's next-hop attribute."; uses next-hop-state-content; } uses route-metadata; } } action active-route { description "Return the active RIB route that is used for the destination address. Address-family-specific modules MUST augment input parameters with a leaf named 'destination-address'."; output { container route { description "The active RIB route for the specified destination. If no route exists in the RIB for the destination address, no output is returned. Address-family-specific modules MUST augment this container with appropriate route contents."; container next-hop { description "Route's next-hop attribute."; uses next-hop-state-content; } uses route-metadata; } } } } } } /* Configuration Data */ container routing { description "Configuration parameters for the routing subsystem."; uses router-id { if-feature "router-id"; description "Configuration of the global router ID. Routing protocols that use router ID can use this parameter or override it with another value."; } container control-plane-protocols { description "Configuration of control-plane protocol instances."; list control-plane-protocol { key "type name"; description "Each entry contains configuration of a control-plane protocol instance."; leaf type { type identityref { base control-plane-protocol; } description "Type of the control-plane protocol - an identity derived from the 'control-plane-protocol' base identity."; } leaf name { type string; description "An arbitrary name of the control-plane protocol instance."; } leaf description { type string; description "Textual description of the control-plane protocol instance."; } container static-routes { when "derived-from-or-self(../type, 'rt:static')" { description "This container is only valid for the 'static' routing protocol."; } description "Configuration of the 'static' pseudo-protocol. Address-family-specific modules augment this node with their lists of routes."; } } } container ribs { description "Configuration of RIBs."; list rib { key "name"; description "Each entry contains configuration for a RIB identified by the 'name' key. Entries having the same key as a system-controlled entry of the list /routing-state/ribs/rib are used for configuring parameters of that entry. Other entries define additional user-controlled RIBs."; leaf name { type string; description "The name of the RIB. For system-controlled entries, the value of this leaf must be the same as the name of the corresponding entry in state data. For user-controlled entries, an arbitrary name can be used."; } uses address-family { description "Address family of the RIB. It is mandatory for user-controlled RIBs. For system-controlled RIBs it can be omitted; otherwise, it must match the address family of the corresponding state entry."; refine "address-family" { mandatory "false"; } } leaf description { type string; description "Textual description of the RIB."; } } } } } yuma123_2.14/netconf/modules/ietf/ietf-netconf-notifications@2012-02-06.yang0000664000175000017500000002434414770023131026344 0ustar vladimirvladimirmodule ietf-netconf-notifications { namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-notifications"; prefix ncn; import ietf-inet-types { prefix inet; } import ietf-netconf { prefix nc; } organization "IETF NETCONF (Network Configuration Protocol) Working Group"; contact "WG Web: WG List: WG Chair: Bert Wijnen WG Chair: Mehmet Ersue Editor: Andy Bierman "; description "This module defines a YANG data model for use with the NETCONF protocol that allows the NETCONF client to receive common NETCONF base event notifications. Copyright (c) 2012 IETF Trust and the persons identified as the document authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 6470; see the RFC itself for full legal notices."; revision "2012-02-06" { description "Initial version."; reference "RFC 6470: NETCONF Base Notifications"; } grouping common-session-parms { description "Common session parameters to identify a management session."; leaf username { type string; mandatory true; description "Name of the user for the session."; } leaf session-id { type nc:session-id-or-zero-type; mandatory true; description "Identifier of the session. A NETCONF session MUST be identified by a non-zero value. A non-NETCONF session MAY be identified by the value zero."; } leaf source-host { type inet:ip-address; description "Address of the remote host for the session."; } } grouping changed-by-parms { description "Common parameters to identify the source of a change event, such as a configuration or capability change."; container changed-by { description "Indicates the source of the change. If caused by internal action, then the empty leaf 'server' will be present. If caused by a management session, then the name, remote host address, and session ID of the session that made the change will be reported."; choice server-or-user { mandatory true; leaf server { type empty; description "If present, the change was caused by the server."; } case by-user { uses common-session-parms; } } // choice server-or-user } // container changed-by-parms } notification netconf-config-change { description "Generated when the NETCONF server detects that the or configuration datastore has been changed by a management session. The notification summarizes the edits that have been detected. The server MAY choose to also generate this notification while loading a datastore during the boot process for the device."; uses changed-by-parms; leaf datastore { type enumeration { enum running { description "The datastore has changed."; } enum startup { description "The datastore has changed"; } } default "running"; description "Indicates which configuration datastore has changed."; } list edit { description "An edit record SHOULD be present for each distinct edit operation that the server has detected on the target datastore. This list MAY be omitted if the detailed edit operations are not known. The server MAY report entries in this list for changes not made by a NETCONF session (e.g., CLI)."; leaf target { type instance-identifier; description "Topmost node associated with the configuration change. A server SHOULD set this object to the node within the datastore that is being altered. A server MAY set this object to one of the ancestors of the actual node that was changed, or omit this object, if the exact node is not known."; } leaf operation { type nc:edit-operation-type; description "Type of edit operation performed. A server MUST set this object to the NETCONF edit operation performed on the target datastore."; } } // list edit } // notification netconf-config-change notification netconf-capability-change { description "Generated when the NETCONF server detects that the server capabilities have changed. Indicates which capabilities have been added, deleted, and/or modified. The manner in which a server capability is changed is outside the scope of this document."; uses changed-by-parms; leaf-list added-capability { type inet:uri; description "List of capabilities that have just been added."; } leaf-list deleted-capability { type inet:uri; description "List of capabilities that have just been deleted."; } leaf-list modified-capability { type inet:uri; description "List of capabilities that have just been modified. A capability is considered to be modified if the base URI for the capability has not changed, but one or more of the parameters encoded at the end of the capability URI have changed. The new modified value of the complete URI is returned."; } } // notification netconf-capability-change notification netconf-session-start { description "Generated when a NETCONF server detects that a NETCONF session has started. A server MAY generate this event for non-NETCONF management sessions. Indicates the identity of the user that started the session."; uses common-session-parms; } // notification netconf-session-start notification netconf-session-end { description "Generated when a NETCONF server detects that a NETCONF session has terminated. A server MAY optionally generate this event for non-NETCONF management sessions. Indicates the identity of the user that owned the session, and why the session was terminated."; uses common-session-parms; leaf killed-by { when "../termination-reason = 'killed'"; type nc:session-id-type; description "The ID of the session that directly caused this session to be abnormally terminated. If this session was abnormally terminated by a non-NETCONF session unknown to the server, then this leaf will not be present."; } leaf termination-reason { type enumeration { enum "closed" { description "The session was terminated by the client in normal fashion, e.g., by the NETCONF protocol operation."; } enum "killed" { description "The session was terminated in abnormal fashion, e.g., by the NETCONF protocol operation."; } enum "dropped" { description "The session was terminated because the transport layer connection was unexpectedly closed."; } enum "timeout" { description "The session was terminated because of inactivity, e.g., waiting for the message or messages."; } enum "bad-hello" { description "The client's message was invalid."; } enum "other" { description "The session was terminated for some other reason."; } } mandatory true; description "Reason the session was terminated."; } } // notification netconf-session-end notification netconf-confirmed-commit { description "Generated when a NETCONF server detects that a confirmed-commit event has occurred. Indicates the event and the current state of the confirmed-commit procedure in progress."; reference "RFC 6241, Section 8.4"; uses common-session-parms { when "../confirm-event != 'timeout'"; } leaf confirm-event { type enumeration { enum "start" { description "The confirmed-commit procedure has started."; } enum "cancel" { description "The confirmed-commit procedure has been canceled, e.g., due to the session being terminated, or an explicit operation."; } enum "timeout" { description "The confirmed-commit procedure has been canceled due to the confirm-timeout interval expiring. The common session parameters will not be present in this sub-mode."; } enum "extend" { description "The confirmed-commit timeout has been extended, e.g., by a new operation."; } enum "complete" { description "The confirmed-commit procedure has been completed."; } } mandatory true; description "Indicates the event that caused the notification."; } leaf timeout { when "../confirm-event = 'start' or ../confirm-event = 'extend'"; type uint32; units "seconds"; description "The configured timeout value if the event type is 'start' or 'extend'. This value represents the approximate number of seconds from the event time when the 'timeout' event might occur."; } } // notification netconf-confirmed-commit } yuma123_2.14/netconf/modules/ietf/ietf-origin@2018-02-14.yang0000664000175000017500000000751014770023131023331 0ustar vladimirvladimirmodule ietf-origin { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-origin"; prefix or; import ietf-yang-metadata { prefix md; } organization "IETF Network Modeling (NETMOD) Working Group"; contact "WG Web: WG List: Author: Martin Bjorklund Author: Juergen Schoenwaelder Author: Phil Shafer Author: Kent Watsen Author: Rob Wilton "; description "This YANG module defines an 'origin' metadata annotation and a set of identities for the origin value. Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8342 (https://www.rfc-editor.org/info/rfc8342); see the RFC itself for full legal notices."; revision 2018-02-14 { description "Initial revision."; reference "RFC 8342: Network Management Datastore Architecture (NMDA)"; } /* * Identities */ identity origin { description "Abstract base identity for the origin annotation."; } identity intended { base origin; description "Denotes configuration from the intended configuration datastore."; } identity dynamic { base origin; description "Denotes configuration from a dynamic configuration datastore."; } identity system { base origin; description "Denotes configuration originated by the system itself. Examples of system configuration include applied configuration for an always-existing loopback interface, or interface configuration that is auto-created due to the hardware currently present in the device."; } identity learned { base origin; description "Denotes configuration learned from protocol interactions with other devices, instead of via either the intended configuration datastore or any dynamic configuration datastore. Examples of protocols that provide learned configuration include link-layer negotiations, routing protocols, and DHCP."; } identity default { base origin; description "Denotes configuration that does not have a configured or learned value but has a default value in use. Covers both values defined in a 'default' statement and values defined via an explanation in a 'description' statement."; } identity unknown { base origin; description "Denotes configuration for which the system cannot identify the origin."; } /* * Type definitions */ typedef origin-ref { type identityref { base origin; } description "An origin identity reference."; } /* * Metadata annotations */ md:annotation origin { type origin-ref; description "The 'origin' annotation can be present on any configuration data node in the operational state datastore. It specifies from where the node originated. If not specified for a given configuration data node, then the origin is the same as the origin of its parent node in the data tree. The origin for any top-level configuration data nodes must be specified."; } } yuma123_2.14/netconf/modules/ietf/ietf-ipv4-unicast-routing@2016-11-04.yang0000664000175000017500000001632314770023131026054 0ustar vladimirvladimirmodule ietf-ipv4-unicast-routing { yang-version "1.1"; namespace "urn:ietf:params:xml:ns:yang:ietf-ipv4-unicast-routing"; prefix "v4ur"; import ietf-routing { prefix "rt"; } import ietf-inet-types { prefix "inet"; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Lou Berger WG Chair: Kent Watsen Editor: Ladislav Lhotka Editor: Acee Lindem "; description "This YANG module augments the 'ietf-routing' module with basic configuration and state data for IPv4 unicast routing. Copyright (c) 2016 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'MAY', and 'OPTIONAL' in the module text are to be interpreted as described in RFC 2119. This version of this YANG module is part of RFC 8022; see the RFC itself for full legal notices."; revision 2016-11-04 { description "Initial revision."; reference "RFC 8022: A YANG Data Model for Routing Management"; } /* Identities */ identity ipv4-unicast { base rt:ipv4; description "This identity represents the IPv4 unicast address family."; } /* State data */ augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route" { when "derived-from-or-self(../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } description "This leaf augments an IPv4 unicast route."; leaf destination-prefix { type inet:ipv4-prefix; description "IPv4 destination prefix."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/" + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" { when "derived-from-or-self(../../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } description "Augment 'simple-next-hop' case in IPv4 unicast routes."; leaf next-hop-address { type inet:ipv4-address; description "IPv4 address of the next hop."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/" + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/" + "rt:next-hop-list/rt:next-hop" { when "derived-from-or-self(../../../../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } description "This leaf augments the 'next-hop-list' case of IPv4 unicast routes."; leaf address { type inet:ipv4-address; description "IPv4 address of the next-hop."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/rt:input" { when "derived-from-or-self(../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast RIBs."; } description "This augment adds the input parameter of the 'active-route' action."; leaf destination-address { type inet:ipv4-address; description "IPv4 destination address."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route" { when "derived-from-or-self(../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } description "This augment adds the destination prefix to the reply of the 'active-route' action."; leaf destination-prefix { type inet:ipv4-prefix; description "IPv4 destination prefix."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" + "rt:simple-next-hop" { when "derived-from-or-self(../../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } description "Augment 'simple-next-hop' case in the reply to the 'active-route' action."; leaf next-hop-address { type inet:ipv4-address; description "IPv4 address of the next hop."; } } augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" { when "derived-from-or-self(../../../../../rt:address-family, " + "'v4ur:ipv4-unicast')" { description "This augment is valid only for IPv4 unicast."; } description "Augment 'next-hop-list' case in the reply to the 'active-route' action."; leaf next-hop-address { type inet:ipv4-address; description "IPv4 address of the next hop."; } } /* Configuration data */ augment "/rt:routing/rt:control-plane-protocols/" + "rt:control-plane-protocol/rt:static-routes" { description "This augment defines the configuration of the 'static' pseudo-protocol with data specific to IPv4 unicast."; container ipv4 { description "Configuration of a 'static' pseudo-protocol instance consists of a list of routes."; list route { key "destination-prefix"; description "A list of static routes."; leaf destination-prefix { type inet:ipv4-prefix; mandatory "true"; description "IPv4 destination prefix."; } leaf description { type string; description "Textual description of the route."; } container next-hop { description "Configuration of next-hop."; uses rt:next-hop-content { augment "next-hop-options/simple-next-hop" { description "Augment 'simple-next-hop' case in IPv4 static routes."; leaf next-hop-address { type inet:ipv4-address; description "IPv4 address of the next hop."; } } augment "next-hop-options/next-hop-list/next-hop-list/" + "next-hop" { description "Augment 'next-hop-list' case in IPv4 static routes."; leaf next-hop-address { type inet:ipv4-address; description "IPv4 address of the next hop."; } } } } } } } } yuma123_2.14/netconf/modules/ietf/ietf-lmap-report@2017-08-08.yang0000664000175000017500000001474514770023131024324 0ustar vladimirvladimirmodule ietf-lmap-report { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-lmap-report"; prefix "lmapr"; import ietf-yang-types { prefix yang; } import ietf-lmap-common { prefix lmap; } organization "IETF Large-Scale Measurement of Broadband Performance Working Group"; contact "WG Web: WG List: Editor: Juergen Schoenwaelder Editor: Vaibhav Bajpai "; description "This module defines a data model for reporting results from Measurement Agents, which are part of a Large-Scale Measurement Platform (LMAP), to result data Collectors. This data model is expected to be implemented by a Collector."; revision "2017-08-08" { description "Initial version"; reference "RFC 8194: A YANG Data Model for LMAP Measurement Agents"; } rpc report { description "The report operation is used by a Measurement Agent to submit measurement results produced by Measurement Tasks to a Collector."; input { leaf date { type yang:date-and-time; mandatory true; description "The date and time when this result report was sent to a Collector."; } leaf agent-id { type yang:uuid; description "The agent-id of the agent from which this report originates."; } leaf group-id { type string; description "The group-id of the agent from which this report originates."; } leaf measurement-point { type string; description "The measurement-point of the agent from which this report originates."; } list result { description "The list of Tasks for which results are reported."; leaf schedule { type lmap:identifier; description "The name of the Schedule that produced the result."; } leaf action { type lmap:identifier; description "The name of the Action in the Schedule that produced the result."; } leaf task { type lmap:identifier; description "The name of the Task that produced the result."; } container parameters { description "This container is a placeholder for runtime parameters defined in Task-specific data models augmenting the base LMAP report data model."; choice extension { description "This choice is provided to augment in different sets of parameters."; } } uses lmap:options-grouping { description "The list of options there were in use when the measurement was performed. This list must include both the Task-specific options as well as the Action-specific options."; } leaf-list tag { type lmap:tag; description "A tag contains additional information that is passed with the result record to the Collector. This is the joined set of tags defined for the Task object, the Schedule object, and the Action object. A tag can be used to carry the Measurement Cycle ID."; } leaf event { type yang:date-and-time; description "The date and time of the event that triggered the Schedule of the Action that produced the reported result values. The date and time does not include any added randomization."; } leaf start { type yang:date-and-time; mandatory true; description "The date and time when the Task producing this result started."; } leaf end { type yang:date-and-time; description "The date and time when the Task producing this result finished."; } leaf cycle-number { type lmap:cycle-number; description "The optional cycle number is the time closest to the time reported in the event leaf that is a multiple of the cycle-interval of the event that triggered the execution of the Schedule. The value is only present if the event that triggered the execution of the Schedule has a defined cycle-interval."; } leaf status { type lmap:status-code; mandatory true; description "The status code returned by the execution of this Action."; } list conflict { description "The names of Tasks overlapping with the execution of the Task that has produced this result."; leaf schedule-name { type lmap:identifier; description "The name of a Schedule that might have impacted the execution of the Task that has produced this result."; } leaf action-name { type lmap:identifier; description "The name of an Action within the Schedule that might have impacted the execution of the Task that has produced this result."; } leaf task-name { type lmap:identifier; description "The name of the Task executed by an Action within the Schedule that might have impacted the execution of the Task that has produced this result."; } } list table { description "A list of result tables."; uses lmap:registry-grouping; leaf-list column { type string; description "An ordered list of column labels. The order is determined by the system and must match the order of the columns in the result rows."; } list row { description "The rows of a result table."; leaf-list value { type string; description "The value of a cell in the result row."; } } } } } } } yuma123_2.14/netconf/modules/ietf/ietf-ipfix-psamp@2012-09-05.yang0000664000175000017500000024220014770023131024275 0ustar vladimirvladimirmodule ietf-ipfix-psamp { namespace "urn:ietf:params:xml:ns:yang:ietf-ipfix-psamp"; prefix ipfix; import ietf-yang-types { prefix yang; } import ietf-inet-types { prefix inet; } organization "IETF IPFIX Working Group"; contact "WG Web: WG List: WG Chair: Nevil Brownlee WG Chair: Juergen Quittek Editor: Gerhard Muenz "; description "IPFIX/PSAMP Configuration Data Model Copyright (c) 2012 IETF Trust and the persons identified as the document authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info)."; revision 2012-09-05 { description "Initial version"; reference "RFC 6728: Configuration Data Model for the IP Flow Information Export (IPFIX) and Packet Sampling (PSAMP) Protocols"; } /***************************************************************** * Features *****************************************************************/ feature exporter { description "If supported, the Monitoring Device can be used as an Exporter. Exporting Processes can be configured."; } feature collector { description "If supported, the Monitoring Device can be used as a Collector. Collecting Processes can be configured."; } feature meter { description "If supported, Observation Points, Selection Processes, and Caches can be configured."; } feature psampSampCountBased { description "If supported, the Monitoring Device supports count-based Sampling. The Selector method sampCountBased can be configured."; } feature psampSampTimeBased { description "If supported, the Monitoring Device supports time-based Sampling. The Selector method sampTimeBased can be configured."; } feature psampSampRandOutOfN { description "If supported, the Monitoring Device supports random n-out-of-N Sampling. The Selector method sampRandOutOfN can be configured."; } feature psampSampUniProb { description "If supported, the Monitoring Device supports uniform probabilistic Sampling. The Selector method sampUniProb can be configured."; } feature psampFilterMatch { description "If supported, the Monitoring Device supports property match Filtering. The Selector method filterMatch can be configured."; } feature psampFilterHash { description "If supported, the Monitoring Device supports hash-based Filtering. The Selector method filterHash can be configured."; } feature immediateCache { description "If supported, the Monitoring Device supports Caches generating PSAMP Packet Reports by configuration with immediateCache."; } feature timeoutCache { description "If supported, the Monitoring Device supports Caches generating IPFIX Flow Records by configuration with timeoutCache."; } feature naturalCache { description "If supported, the Monitoring Device supports Caches generating IPFIX Flow Records by configuration with naturalCache."; } feature permanentCache { description "If supported, the Monitoring Device supports Caches generating IPFIX Flow Records by configuration with permanentCache."; } feature udpTransport { description "If supported, the Monitoring Device supports UDP as the transport protocol."; } feature tcpTransport { description "If supported, the Monitoring Device supports TCP as the transport protocol."; } feature fileReader { description "If supported, the Monitoring Device supports the configuration of Collecting Processes as File Readers."; } feature fileWriter { description "If supported, the Monitoring Device supports the configuration of Exporting Processes as File Writers."; } /***************************************************************** * Identities *****************************************************************/ /*** Hash function identities ***/ identity hashFunction { description "Base identity for all hash functions used for hash-based packet Filtering. Identities derived from this base are used by the leaf /ipfix/selectionProcess/selector/filterHash/hashFunction."; } identity BOB { base "hashFunction"; description "BOB hash function"; reference "RFC 5475, Section 6.2.4.1."; } identity IPSX { base "hashFunction"; description "IPSX hash function"; reference "RFC 5475, Section 6.2.4.1."; } identity CRC { base "hashFunction"; description "CRC hash function"; reference "RFC 5475, Section 6.2.4.1."; } /*** Export mode identities ***/ identity exportMode { description "Base identity for different usages of export destinations configured for an Exporting Process. Identities derived from this base are used by the leaf /ipfix/exportingProcess/exportMode."; } identity parallel { base "exportMode"; description "Parallel export of Data Records to all destinations configured for the Exporting Process."; } identity loadBalancing { base "exportMode"; description "Load-balancing between the different destinations configured for the Exporting Process."; } identity fallback { base "exportMode"; description "Export to the primary destination (i.e., the first SCTP, UDP, TCP, or file destination configured for the Exporting Process). If the export to the primary destination fails, the Exporting Process tries to export to the secondary destination. If the secondary destination fails as well, it continues with the tertiary, etc."; } /*** Options type identities ***/ identity optionsType { description "Base identity for report types exported with options. Identities derived from this base are used by the leaf /ipfix/exportingProcess/options/optionsType."; } identity meteringStatistics { base "optionsType"; description "Metering Process Statistics."; reference "RFC 5101, Section 4.1."; } identity meteringReliability { base "optionsType"; description "Metering Process Reliability Statistics."; reference "RFC 5101, Section 4.2."; } identity exportingReliability { base "optionsType"; description "Exporting Process Reliability Statistics."; reference "RFC 5101, Section 4.3."; } identity flowKeys { base "optionsType"; description "Flow Keys."; reference "RFC 5101, Section 4.4."; } identity selectionSequence { base "optionsType"; description "Selection Sequence and Selector Reports."; reference "RFC 5476, Sections 6.5.1 and 6.5.2."; } identity selectionStatistics { base "optionsType"; description "Selection Sequence Statistics Report."; reference "RFC 5476, Sections 6.5.3."; } identity accuracy { base "optionsType"; description "Accuracy Report."; reference "RFC 5476, Section 6.5.4."; } identity reducingRedundancy { base "optionsType"; description "Enables the utilization of Options Templates to reduce redundancy in the exported Data Records."; reference "RFC 5473."; } identity extendedTypeInformation { base "optionsType"; description "Export of extended type information for enterprise-specific Information Elements used in the exported Templates."; reference "RFC 5610."; } /***************************************************************** * Type definitions *****************************************************************/ typedef ieNameType { type string { length "1..max"; pattern "\S+"; } description "Type for Information Element names. Whitespaces are not allowed."; } typedef ieIdType { type uint16 { range "1..32767" { description "Valid range of Information Element identifiers."; reference "RFC 5102, Section 4."; } } description "Type for Information Element identifiers."; } typedef nameType { type string { length "1..max"; pattern "\S(.*\S)?"; } description "Type for 'name' leafs, which are used to identify specific instances within lists, etc. Leading and trailing whitespaces are not allowed."; } typedef ifNameType { type string { length "1..255"; } description "This corresponds to the DisplayString textual convention of SNMPv2-TC, which is used for ifName in the IF MIB module."; reference "RFC 2863 (ifName)."; } typedef direction { type enumeration { enum ingress { description "This value is used for monitoring incoming packets."; } enum egress { description "This value is used for monitoring outgoing packets."; } enum both { description "This value is used for monitoring incoming and outgoing packets."; } } description "Direction of packets going through an interface or linecard."; } typedef transportSessionStatus { type enumeration { enum inactive { description "This value MUST be used for Transport Sessions that are specified in the system but currently not active. The value can be used for Transport Sessions that are backup (secondary) sessions."; } enum active { description "This value MUST be used for Transport Sessions that are currently active and transmitting or receiving data."; } enum unknown { description "This value MUST be used if the status of the Transport Sessions cannot be detected by the device. This value should be avoided as far as possible."; } } description "Status of a Transport Session."; reference "RFC 6615, Section 8 (ipfixTransportSessionStatus)."; } /***************************************************************** * Groupings *****************************************************************/ grouping observationPointParameters { description "Interface as input to Observation Point."; leaf observationPointId { type uint32; config false; description "Observation Point ID (i.e., the value of the Information Element observationPointId) assigned by the Monitoring Device."; reference "IANA registry for IPFIX Entities, http://www.iana.org/assignments/ipfix."; } leaf observationDomainId { type uint32; mandatory true; description "The Observation Domain ID associates the Observation Point to an Observation Domain. Observation Points with identical Observation Domain IDs belong to the same Observation Domain. Note that this parameter corresponds to ipfixObservationPointObservationDomainId in the IPFIX MIB module."; reference "RFC 5101; RFC 6615, Section 8 (ipfixObservationPointObservationDomainId)."; } leaf-list ifName { type ifNameType; description "List of names identifying interfaces of the Monitoring Device. The Observation Point observes packets at the specified interfaces."; } leaf-list ifIndex { type uint32; description "List of ifIndex values pointing to entries in the ifTable of the IF-MIB module maintained by the Monitoring Device. The Observation Point observes packets at the specified interfaces. This parameter SHOULD only be used if an SNMP agent enables access to the ifTable. Note that this parameter corresponds to ipfixObservationPointPhysicalInterface in the IPFIX MIB module."; reference "RFC 2863; RFC 6615, Section 8 (ipfixObservationPointPhysicalInterface)."; } leaf-list entPhysicalName { type string; description "List of names identifying physical entities of the Monitoring Device. The Observation Point observes packets at the specified entities."; } leaf-list entPhysicalIndex { type uint32; description "List of entPhysicalIndex values pointing to entries in the entPhysicalTable of the ENTITY-MIB module maintained by the Monitoring Device. The Observation Point observes packets at the specified entities. This parameter SHOULD only be used if an SNMP agent enables access to the entPhysicalTable. Note that this parameter corresponds to ipfixObservationPointPhysicalEntity in the IPFIX MIB module."; reference "RFC 4133; RFC 6615, Section 8 (ipfixObservationPointPhysicalInterface)."; } leaf direction { type direction; default both; description "Direction of packets. If not applicable (e.g., in the case of a sniffing interface in promiscuous mode), this parameter is ignored."; } } grouping sampCountBasedParameters { description "Configuration parameters of a Selector applying systematic count-based packet Sampling to the packet stream."; reference "RFC 5475, Section 5.1; RFC 5476, Section 6.5.2.1."; leaf packetInterval { type uint32; units packets; mandatory true; description "The number of packets that are consecutively sampled between gaps of length packetSpace. This parameter corresponds to the Information Element samplingPacketInterval and to psampSampCountBasedInterval in the PSAMP MIB module."; reference "RFC 5477, Section 8.2.2; RFC 6727, Section 6 (psampSampCountBasedInterval)."; } leaf packetSpace { type uint32; units packets; mandatory true; description "The number of unsampled packets between two Sampling intervals. This parameter corresponds to the Information Element samplingPacketSpace and to psampSampCountBasedSpace in the PSAMP MIB module."; reference "RFC 5477, Section 8.2.3; RFC 6727, Section 6 (psampSampCountBasedSpace)."; } } grouping sampTimeBasedParameters { description "Configuration parameters of a Selector applying systematic time-based packet Sampling to the packet stream."; reference "RFC 5475, Section 5.1; RFC 5476, Section 6.5.2.2."; leaf timeInterval { type uint32; units microseconds; mandatory true; description "The time interval in microseconds during which all arriving packets are sampled between gaps of length timeSpace. This parameter corresponds to the Information Element samplingTimeInterval and to psampSampTimeBasedInterval in the PSAMP MIB module."; reference "RFC 5477, Section 8.2.4; RFC 6727, Section 6 (psampSampTimeBasedInterval)."; } leaf timeSpace { type uint32; units microseconds; mandatory true; description "The time interval in microseconds during which no packets are sampled between two Sampling intervals specified by timeInterval. This parameter corresponds to the Information Element samplingTimeInterval and to psampSampTimeBasedSpace in the PSAMP MIB module."; reference "RFC 5477, Section 8.2.5; RFC 6727, Section 6 (psampSampTimeBasedSpace)."; } } grouping sampRandOutOfNParameters { description "Configuration parameters of a Selector applying n-out-of-N packet Sampling to the packet stream."; reference "RFC 5475, Section 5.2.1; RFC 5476, Section 6.5.2.3."; leaf size { type uint32; units packets; mandatory true; description "The number of elements taken from the parent population. This parameter corresponds to the Information Element samplingSize and to psampSampRandOutOfNSize in the PSAMP MIB module."; reference "RFC 5477, Section 8.2.6; RFC 6727, Section 6 (psampSampRandOutOfNSize)."; } leaf population { type uint32; units packets; mandatory true; description "The number of elements in the parent population. This parameter corresponds to the Information Element samplingPopulation and to psampSampRandOutOfNPopulation in the PSAMP MIB module."; reference "RFC 5477, Section 8.2.7; RFC 6727, Section 6 (psampSampRandOutOfNPopulation)."; } } grouping sampUniProbParameters { description "Configuration parameters of a Selector applying uniform probabilistic packet Sampling (with equal probability per packet) to the packet stream."; reference "RFC 5475, Section 5.2.2.1; RFC 5476, Section 6.5.2.4."; leaf probability { type decimal64 { fraction-digits 18; range "0..1"; } mandatory true; description "Probability that a packet is sampled, expressed as a value between 0 and 1. The probability is equal for every packet. This parameter corresponds to the Information Element samplingProbability and to psampSampUniProbProbability in the PSAMP MIB module."; reference "RFC 5477, Section 8.2.8; RFC 6727, Section 6 (psampSampUniProbProbability)."; } } grouping filterMatchParameters { description "Configuration parameters of a Selector applying property match Filtering to the packet stream. The field to be matched is specified as an Information Element."; reference "RFC 5475, Section 6.1; RFC 5476, Section 6.5.2.5."; choice nameOrId { mandatory true; description "The field to be matched is specified by either the name or the identifier of the Information Element."; leaf ieName { type ieNameType; description "Name of the Information Element."; } leaf ieId { type ieIdType; description "Identifier of the Information Element."; } } leaf ieEnterpriseNumber { type uint32; default 0; description "If this parameter is zero, the Information Element is registered in the IANA registry of IPFIX Information Elements. If this parameter is configured with a non-zero private enterprise number, the Information Element is enterprise-specific."; reference "IANA registry for Private Enterprise Numbers, http://www.iana.org/assignments/enterprise-numbers; IANA registry for IPFIX Entities, http://www.iana.org/assignments/ipfix."; } leaf value { type string; mandatory true; description "Matching value of the Information Element."; } } grouping filterHashParameters { description "Configuration parameters of a Selector applying hash-based Filtering to the packet stream."; reference "RFC 5475, Section 6.2; RFC 5476, Section 6.5.2.6."; leaf hashFunction { type identityref { base "hashFunction"; } default BOB; description "Hash function to be applied. According to RFC 5475, Section 6.2.4.1, 'BOB' must be used in order to be compliant with PSAMP. This parameter functionally corresponds to psampFiltHashFunction in the PSAMP MIB module."; reference "RFC 6727, Section 6 (psampFiltHashFunction)"; } leaf initializerValue { type uint64; description "Initializer value to the hash function. If not configured by the user, the Monitoring Device arbitrarily chooses an initializer value. This parameter corresponds to the Information Element hashInitialiserValue and to psampFiltHashInitializerValue in the PSAMP MIB module."; reference "RFC 5477, Section 8.3.9; RFC 6727, Section 6 (psampFiltHashInitializerValue)."; } leaf ipPayloadOffset { type uint64; units octets; default 0; description "IP payload offset indicating the position of the first payload byte considered as input to the hash function. Default value 0 corresponds to the minimum offset that must be configurable according to RFC 5476, Section 6.5.2.6. This parameter corresponds to the Information Element hashIPPayloadOffset and to psampFiltHashIpPayloadOffset in the PSAMP MIB module."; reference "RFC 5477, Section 8.3.2; RFC 6727, Section 6 (psampFiltHashIpPayloadOffset)."; } leaf ipPayloadSize { type uint64; units octets; default 8; description "Number of IP payload bytes used as input to the hash function, counted from the payload offset. If the IP payload is shorter than the payload range, all available payload octets are used as input. Default value 8 corresponds to the minimum IP payload size that must be configurable according to RFC 5476, Section 6.5.2.6. This parameter corresponds to the Information Element hashIPPayloadSize and to psampFiltHashIpPayloadSize in the PSAMP MIB module."; reference "RFC 5477, Section 8.3.3; RFC 6727, Section 6 (psampFiltHashIpPayloadSize)."; } leaf digestOutput { type boolean; default false; description "If true, the output from this Selector is included in the Packet Report as a packet digest. Therefore, the configured Cache Layout needs to contain a digestHashValue field. This parameter corresponds to the Information Element hashDigestOutput."; reference "RFC 5477, Section 8.3.8."; } leaf outputRangeMin { type uint64; config false; description "Beginning of the hash function's potential range. This parameter corresponds to the Information Element hashOutputRangeMin and to psampFiltHashOutputRangeMin in the PSAMP MIB module."; reference "RFC 5477, Section 8.3.4; RFC 6727, Section 6 (psampFiltHashOutputRangeMin)."; } leaf outputRangeMax { type uint64; config false; description "End of the hash function's potential range. This parameter corresponds to the Information Element hashOutputRangeMax and to psampFiltHashOutputRangeMax in the PSAMP MIB module."; reference "RFC 5477, Section 8.3.5; RFC 6727, Section 6 (psampFiltHashOutputRangeMax)."; } list selectedRange { key name; min-elements 1; description "List of hash function return ranges for which packets are selected."; leaf name { type nameType; description "Key of this list."; } leaf min { type uint64; description "Beginning of the hash function's selected range. This parameter corresponds to the Information Element hashSelectedRangeMin and to psampFiltHashSelectedRangeMin in the PSAMP MIB module."; reference "RFC 5477, Section 8.3.6; RFC 6727, Section 6 (psampFiltHashSelectedRangeMin)."; } leaf max { type uint64; description "End of the hash function's selected range. This parameter corresponds to the Information Element hashSelectedRangeMax and to psampFiltHashSelectedRangeMax in the PSAMP MIB module."; reference "RFC 5477, Section 8.3.7; RFC 6727, Section 6 (psampFiltHashSelectedRangeMax)."; } } } grouping selectorParameters { description "Configuration and state parameters of a Selector."; choice Method { mandatory true; description "Packet selection method applied by the Selector."; leaf selectAll { type empty; description "Method that selects all packets."; } container sampCountBased { if-feature psampSampCountBased; description "Systematic count-based packet Sampling."; uses sampCountBasedParameters; } container sampTimeBased { if-feature psampSampTimeBased; description "Systematic time-based packet Sampling."; uses sampTimeBasedParameters; } container sampRandOutOfN { if-feature psampSampRandOutOfN; description "n-out-of-N packet Sampling."; uses sampRandOutOfNParameters; } container sampUniProb { if-feature psampSampUniProb; description "Uniform probabilistic packet Sampling."; uses sampUniProbParameters; } container filterMatch { if-feature psampFilterMatch; description "Property match Filtering."; uses filterMatchParameters; } container filterHash { if-feature psampFilterHash; description "Hash-based Filtering."; uses filterHashParameters; } } leaf packetsObserved { type yang:counter64; config false; description "The number of packets observed at the input of the Selector. If this is the first Selector in the Selection Process, this counter corresponds to the total number of packets in all Observed Packet Streams at the input of the Selection Process. Otherwise, the counter corresponds to the total number of packets at the output of the preceding Selector. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of selectorDiscontinuityTime. Note that this parameter corresponds to ipfixSelectorStatsPacketsObserved in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixSelectorStatsPacketsObserved)."; } leaf packetsDropped { type yang:counter64; config false; description "The total number of packets discarded by the Selector. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of selectorDiscontinuityTime. Note that this parameter corresponds to ipfixSelectorStatsPacketsDropped in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixSelectorStatsPacketsDropped)."; } leaf selectorDiscontinuityTime { type yang:date-and-time; config false; description "Timestamp of the most recent occasion at which one or more of the Selector counters suffered a discontinuity. Note that this parameter functionally corresponds to ipfixSelectionProcessStatsDiscontinuityTime in the IPFIX MIB module. In contrast to ipfixSelectionProcessStatsDiscontinuityTime, the time is absolute and not relative to sysUpTime."; reference "RFC 6615, Section 8 (ipfixSelectionProcessStatsDiscontinuityTime)."; } } grouping cacheLayoutParameters { description "Cache Layout parameters used by immediateCache, timeoutCache, naturalCache, and permanentCache."; container cacheLayout { description "Cache Layout parameters."; list cacheField { key name; min-elements 1; description "Superset of fields that are included in the Packet Reports or Flow Records generated by the Cache."; leaf name { type nameType; description "Key of this list."; } choice nameOrId { mandatory true; description "Name or identifier of the Information Element."; reference "RFC 5102, Section 2; IANA registry for IPFIX Entities, http://www.iana.org/assignments/ipfix."; leaf ieName { type ieNameType; description "Name of the Information Element."; } leaf ieId { type ieIdType; description "Identifier of the Information Element."; } } leaf ieLength { type uint16; units octets; description "Length of the field in which the Information Element is encoded. A value of 65535 specifies a variable-length Information Element. For Information Elements of integer and float type, the field length MAY be set to a smaller value than the standard length of the abstract data type if the rules of reduced size encoding are fulfilled. If not configured by the user, this parameter is set by the Monitoring Device."; reference "RFC 5101, Section 6.2."; } leaf ieEnterpriseNumber { type uint32; default 0; description "If this parameter is zero, the Information Element is registered in the IANA registry of IPFIX Information Elements. If this parameter is configured with a non-zero private enterprise number, the Information Element is enterprise-specific. If the enterprise number is set to 29305, this field contains a Reverse Information Element. In this case, the Cache MUST generate Data Records in accordance to RFC 5103."; reference "RFC 5101; RFC 5103; IANA registry for Private Enterprise Numbers, http://www.iana.org/assignments/enterprise-numbers; IANA registry for IPFIX Entities, http://www.iana.org/assignments/ipfix."; } leaf isFlowKey { when "(name(../../..) != 'immediateCache') and ((count(../ieEnterpriseNumber) = 0) or (../ieEnterpriseNumber != 29305))" { description "This parameter is not available for Reverse Information Elements (which have enterprise number 29305). It is also not available for immediateCache."; } type empty; description "If present, this is a flow key."; } } } } grouping flowCacheParameters { description "Configuration and state parameters of a Cache generating Flow Records."; leaf maxFlows { type uint32; units flows; description "This parameter configures the maximum number of Flows in the Cache, which is the maximum number of Flows that can be measured simultaneously. The Monitoring Device MUST ensure that sufficient resources are available to store the configured maximum number of Flows. If the maximum number of Flows is measured, an additional Flow can be measured only if an existing entry is removed. However, traffic that pertains to existing Flows can continue to be measured."; } leaf activeTimeout { when "(name(..) = 'timeoutCache') or (name(..) = 'naturalCache')" { description "This parameter is only available for timeoutCache and naturalCache."; } type uint32; units seconds; description "This parameter configures the time in seconds after which a Flow is expired even though packets matching this Flow are still received by the Cache. The parameter value zero indicates infinity, meaning that there is no active timeout. If not configured by the user, the Monitoring Device sets this parameter. Note that this parameter corresponds to ipfixMeteringProcessCacheActiveTimeout in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixMeteringProcessCacheActiveTimeout)."; } leaf idleTimeout { when "(name(..) = 'timeoutCache') or (name(..) = 'naturalCache')" { description "This parameter is only available for timeoutCache and naturalCache."; } type uint32; units seconds; description "This parameter configures the time in seconds after which a Flow is expired if no more packets matching this Flow are received by the Cache. The parameter value zero indicates infinity, meaning that there is no idle timeout. If not configured by the user, the Monitoring Device sets this parameter. Note that this parameter corresponds to ipfixMeteringProcessCacheIdleTimeout in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixMeteringProcessCacheIdleTimeout)."; } leaf exportInterval { when "name(..) = 'permanentCache'" { description "This parameter is only available for permanentCache."; } type uint32; units seconds; description "This parameter configures the interval (in seconds) for periodical export of Flow Records. If not configured by the user, the Monitoring Device sets this parameter."; } leaf activeFlows { type yang:gauge32; units flows; config false; description "The number of Flows currently active in this Cache. Note that this parameter corresponds to ipfixMeteringProcessCacheActiveFlows in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixMeteringProcessCacheActiveFlows)."; } leaf unusedCacheEntries { type yang:gauge32; units flows; config false; description "The number of unused Cache entries in this Cache. Note that this parameter corresponds to ipfixMeteringProcessCacheUnusedCacheEntries in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixMeteringProcessCacheUnusedCacheEntries)."; } } grouping exportingProcessParameters { description "Parameters of an Exporting Process."; leaf exportingProcessId { type uint32; config false; description "The identifier of the Exporting Process. This parameter corresponds to the Information Element exportingProcessId. Its occurrence helps to associate Exporting Process parameters with Exporing Process statistics exported by the Monitoring Device using the Exporting Process Reliability Statistics Template as defined by the IPFIX protocol specification."; reference "RFC 5101, Section 4.3; IANA registry for IPFIX Entities, http://www.iana.org/assignments/ipfix."; } leaf exportMode { type identityref { base "exportMode"; } default parallel; description "This parameter determines to which configured destination(s) the incoming Data Records are exported."; } list destination { key name; min-elements 1; description "List of export destinations."; leaf name { type nameType; description "Key of this list."; } choice DestinationParameters { mandatory true; description "Configuration parameters depend on whether SCTP, UDP, or TCP is used as transport protocol, and whether the destination is a file."; container sctpExporter { description "SCTP parameters."; uses sctpExporterParameters; } container udpExporter { if-feature udpTransport; description "UDP parameters."; uses udpExporterParameters; } container tcpExporter { if-feature tcpTransport; description "TCP parameters."; uses tcpExporterParameters; } container fileWriter { if-feature fileWriter; description "File Writer parameters."; uses fileWriterParameters; } } } list options { key name; description "List of options reported by the Exporting Process."; leaf name { type nameType; description "Key of this list."; } uses optionsParameters; } } grouping commonExporterParameters { description "Parameters of en export destination that are common to all transport protocols."; leaf ipfixVersion { type uint16; default 10; description "IPFIX version number."; reference "RFC 5101."; } leaf destinationPort { type inet:port-number; description "If not configured by the user, the Monitoring Device uses the default port number for IPFIX, which is 4739 without TLS or DTLS and 4740 if TLS or DTLS is activated."; } choice indexOrName { description "Index or name of the interface as stored in the ifTable of IF-MIB. If configured, the Exporting Process MUST use the given interface to export IPFIX Messages to the export destination. If omitted, the Exporting Process selects the outgoing interface based on local routing decision and accepts return traffic, such as transport-layer acknowledgments, on all available interfaces."; reference "RFC 2863."; leaf ifIndex { type uint32; description "Index of an interface as stored in the ifTable of IF-MIB."; reference "RFC 2863."; } leaf ifName { type string; description "Name of an interface as stored in the ifTable of IF-MIB."; reference "RFC 2863."; } } leaf sendBufferSize { type uint32; units bytes; description "Size of the socket send buffer. If not configured by the user, this parameter is set by the Monitoring Device."; } leaf rateLimit { type uint32; units "bytes per second"; description "Maximum number of bytes per second the Exporting Process may export to the given destination. The number of bytes is calculated from the lengths of the IPFIX Messages exported. If not configured, no rate limiting is performed."; reference "RFC 5476, Section 6.3."; } container transportLayerSecurity { presence "If transportLayerSecurity is present, DTLS is enabled if the transport protocol is SCTP or UDP, and TLS is enabled if the transport protocol is TCP."; description "TLS or DTLS configuration."; uses transportLayerSecurityParameters; } container transportSession { config false; description "State parameters of the Transport Session directed to the given destination."; uses transportSessionParameters; } } grouping sctpExporterParameters { description "SCTP-specific export destination parameters."; uses commonExporterParameters; leaf-list sourceIPAddress { type inet:ip-address; description "List of source IP addresses used by the Exporting Process. If configured, the specified addresses are eligible local IP addresses of the multihomed SCTP endpoint. If not configured, all locally assigned IP addresses are eligible local IP addresses."; reference "RFC 4960, Section 6.4."; } leaf-list destinationIPAddress { type inet:ip-address; min-elements 1; description "One or more IP addresses of the Collecting Process to which IPFIX Messages are sent. The user MUST ensure that all configured IP addresses belong to the same Collecting Process. The Exporting Process tries to establish an SCTP association to any of the configured destination IP addresses."; reference "RFC 4960, Section 6.4."; } leaf timedReliability { type uint32; units milliseconds; default 0; description "Lifetime in milliseconds until an IPFIX Message containing Data Sets only is 'abandoned' due to the timed reliability mechanism of PR-SCTP. If this parameter is set to zero, reliable SCTP transport is used for all Data Records. Regardless of the value of this parameter, the Exporting Process MAY use reliable SCTP transport for Data Sets associated with Options Templates."; reference "RFC 3758; RFC 4960."; } } grouping udpExporterParameters { description "Parameters of a UDP export destination."; uses commonExporterParameters; leaf sourceIPAddress { type inet:ip-address; description "Source IP address used by the Exporting Process. If not configured, the IP address assigned to the outgoing interface is used as source IP address."; } leaf destinationIPAddress { type inet:ip-address; mandatory true; description "IP address of the Collection Process to which IPFIX Messages are sent."; } leaf maxPacketSize { type uint16; units octets; description "This parameter specifies the maximum size of IP packets sent to the Collector. If set to zero, the Exporting Device MUST derive the maximum packet size from path MTU discovery mechanisms. If not configured by the user, this parameter is set by the Monitoring Device."; } leaf templateRefreshTimeout { type uint32; units seconds; default 600; description "Sets time after which Templates are resent in the UDP Transport Session. Note that the configured lifetime MUST be adapted to the templateLifeTime parameter value at the receiving Collecting Process. Note that this parameter corresponds to ipfixTransportSessionTemplateRefreshTimeout in the IPFIX MIB module."; reference "RFC 5101, Section 10.3.6; RFC 6615, Section 8 (ipfixTransportSessionTemplateRefreshTimeout)."; } leaf optionsTemplateRefreshTimeout { type uint32; units seconds; default 600; description "Sets time after which Options Templates are resent in the UDP Transport Session. Note that the configured lifetime MUST be adapted to the optionsTemplateLifeTime parameter value at the receiving Collecting Process. Note that this parameter corresponds to ipfixTransportSessionOptionsTemplateRefreshTimeout in the IPFIX MIB module."; reference "RFC 5101, Section 10.3.6; RFC 6615, Section 8 (ipfixTransportSessionOptionsTemplateRefreshTimeout)."; } leaf templateRefreshPacket { type uint32; units "IPFIX Messages"; description "Sets number of IPFIX Messages after which Templates are resent in the UDP Transport Session. Note that this parameter corresponds to ipfixTransportSessionTemplateRefreshPacket in the IPFIX MIB module. If omitted, Templates are only resent after timeout."; reference "RFC 5101, Section 10.3.6; RFC 6615, Section 8 (ipfixTransportSessionTemplateRefreshPacket)."; } leaf optionsTemplateRefreshPacket { type uint32; units "IPFIX Messages"; description "Sets number of IPFIX Messages after which Options Templates are resent in the UDP Transport Session protocol. Note that this parameter corresponds to ipfixTransportSessionOptionsTemplateRefreshPacket in the IPFIX MIB module. If omitted, Templates are only resent after timeout."; reference "RFC 5101, Section 10.3.6; RFC 6615, Section 8 (ipfixTransportSessionOptionsTemplateRefreshPacket)."; } } grouping tcpExporterParameters { description "Parameters of a TCP export destination."; uses commonExporterParameters; leaf sourceIPAddress { type inet:ip-address; description "Source IP address used by the Exporting Process. If not configured by the user, this parameter is set by the Monitoring Device to an IP address assigned to the outgoing interface."; } leaf destinationIPAddress { type inet:ip-address; mandatory true; description "IP address of the Collection Process to which IPFIX Messages are sent."; } } grouping fileWriterParameters { description "File Writer parameters."; leaf ipfixVersion { type uint16; default 10; description "IPFIX version number."; reference "RFC 5101."; } leaf file { type inet:uri; mandatory true; description "URI specifying the location of the file."; } leaf bytes { type yang:counter64; units octets; config false; description "The number of bytes written by the File Writer. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of fileWriterDiscontinuityTime."; } leaf messages { type yang:counter64; units "IPFIX Messages"; config false; description "The number of IPFIX Messages written by the File Writer. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of fileWriterDiscontinuityTime."; } leaf discardedMessages { type yang:counter64; units "IPFIX Messages"; config false; description "The number of IPFIX Messages that could not be written by the File Writer due to internal buffer overflows, limited storage capacity, etc. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of fileWriterDiscontinuityTime."; } leaf records { type yang:counter64; units "Data Records"; config false; description "The number of Data Records written by the File Writer. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of fileWriterDiscontinuityTime."; } leaf templates { type yang:counter32; units "Templates"; config false; description "The number of Template Records (excluding Options Template Records) written by the File Writer. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of fileWriterDiscontinuityTime."; } leaf optionsTemplates { type yang:counter32; units "Options Templates"; config false; description "The number of Options Template Records written by the File Writer. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of fileWriterDiscontinuityTime."; } leaf fileWriterDiscontinuityTime { type yang:date-and-time; config false; description "Timestamp of the most recent occasion at which one or more File Writer counters suffered a discontinuity. In contrast to discontinuity times in the IPFIX MIB module, the time is absolute and not relative to sysUpTime."; } list template { config false; description "This list contains the Templates and Options Templates that have been written by the File Reader. Withdrawn or invalidated (Options) Templates MUST be removed from this list."; uses templateParameters; } } grouping optionsParameters { description "Parameters specifying the data export using an Options Template."; leaf optionsType { type identityref { base "optionsType"; } mandatory true; description "Type of the exported options data."; } leaf optionsTimeout { type uint32; units milliseconds; description "Time interval for periodic export of the options data. If set to zero, the export is triggered when the options data has changed. If not configured by the user, this parameter is set by the Monitoring Device."; } } grouping collectingProcessParameters { description "Parameters of a Collecting Process."; list sctpCollector { key name; description "List of SCTP receivers (sockets) on which the Collecting Process receives IPFIX Messages."; leaf name { type nameType; description "Key of this list."; } uses sctpCollectorParameters; } list udpCollector { if-feature udpTransport; key name; description "List of UDP receivers (sockets) on which the Collecting Process receives IPFIX Messages."; leaf name { type nameType; description "Key of this list."; } uses udpCollectorParameters; } list tcpCollector { if-feature tcpTransport; key name; description "List of TCP receivers (sockets) on which the Collecting Process receives IPFIX Messages."; leaf name { type nameType; description "Key of this list."; } uses tcpCollectorParameters; } list fileReader { if-feature fileReader; key name; description "List of File Readers from which the Collecting Process reads IPFIX Messages."; leaf name { type nameType; description "Key of this list."; } uses fileReaderParameters; } } grouping commonCollectorParameters { description "Parameters of a Collecting Process that are common to all transport protocols."; leaf localPort { type inet:port-number; description "If not configured, the Monitoring Device uses the default port number for IPFIX, which is 4739 without TLS or DTLS and 4740 if TLS or DTLS is activated."; } container transportLayerSecurity { presence "If transportLayerSecurity is present, DTLS is enabled if the transport protocol is SCTP or UDP, and TLS is enabled if the transport protocol is TCP."; description "TLS or DTLS configuration."; uses transportLayerSecurityParameters; } list transportSession { config false; description "This list contains the currently established Transport Sessions terminating at the given socket."; uses transportSessionParameters; } } grouping sctpCollectorParameters { description "Parameters of a listening SCTP socket at a Collecting Process."; uses commonCollectorParameters; leaf-list localIPAddress { type inet:ip-address; description "List of local IP addresses on which the Collecting Process listens for IPFIX Messages. The IP addresses are used as eligible local IP addresses of the multihomed SCTP endpoint."; reference "RFC 4960, Section 6.4."; } } grouping udpCollectorParameters { description "Parameters of a listening UDP socket at a Collecting Process."; uses commonCollectorParameters; leaf-list localIPAddress { type inet:ip-address; description "List of local IP addresses on which the Collecting Process listens for IPFIX Messages."; } leaf templateLifeTime { type uint32; units seconds; default 1800; description "Sets the lifetime of Templates for all UDP Transport Sessions terminating at this UDP socket. Templates that are not received again within the configured lifetime become invalid at the Collecting Process. As specified in RFC 5101, the Template lifetime MUST be at least three times higher than the templateRefreshTimeout parameter value configured on the corresponding Exporting Processes. Note that this parameter corresponds to ipfixTransportSessionTemplateRefreshTimeout in the IPFIX MIB module."; reference "RFC 5101, Section 10.3.7; RFC 6615, Section 8 (ipfixTransportSessionTemplateRefreshTimeout)."; } leaf optionsTemplateLifeTime { type uint32; units seconds; default 1800; description "Sets the lifetime of Options Templates for all UDP Transport Sessions terminating at this UDP socket. Options Templates that are not received again within the configured lifetime become invalid at the Collecting Process. As specified in RFC 5101, the Options Template lifetime MUST be at least three times higher than the optionsTemplateRefreshTimeout parameter value configured on the corresponding Exporting Processes. Note that this parameter corresponds to ipfixTransportSessionOptionsTemplateRefreshTimeout in the IPFIX MIB module."; reference "RFC 5101, Section 10.3.7; RFC 6615, Section 8 (ipfixTransportSessionOptionsTemplateRefreshTimeout)."; } leaf templateLifePacket { type uint32; units "IPFIX Messages"; description "If this parameter is configured, Templates defined in a UDP Transport Session become invalid if they are neither included in a sequence of more than this number of IPFIX Messages nor received again within the period of time specified by templateLifeTime. Note that this parameter corresponds to ipfixTransportSessionTemplateRefreshPacket in the IPFIX MIB module."; reference "RFC 5101, Section 10.3.7; RFC 6615, Section 8 (ipfixTransportSessionTemplateRefreshPacket)."; } leaf optionsTemplateLifePacket { type uint32; units "IPFIX Messages"; description "If this parameter is configured, Options Templates defined in a UDP Transport Session become invalid if they are neither included in a sequence of more than this number of IPFIX Messages nor received again within the period of time specified by optionsTemplateLifeTime. Note that this parameter corresponds to ipfixTransportSessionOptionsTemplateRefreshPacket in the IPFIX MIB module."; reference "RFC 5101, Section 10.3.7; RFC 6615, Section 8 (ipfixTransportSessionOptionsTemplateRefreshPacket)."; } } grouping tcpCollectorParameters { description "Parameters of a listening TCP socket at a Collecting Process."; uses commonCollectorParameters; leaf-list localIPAddress { type inet:ip-address; description "List of local IP addresses on which the Collecting Process listens for IPFIX Messages."; } } grouping fileReaderParameters { description "File Reader parameters."; leaf file { type inet:uri; mandatory true; description "URI specifying the location of the file."; } leaf bytes { type yang:counter64; units octets; config false; description "The number of bytes read by the File Reader. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of fileReaderDiscontinuityTime."; } leaf messages { type yang:counter64; units "IPFIX Messages"; config false; description "The number of IPFIX Messages read by the File Reader. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of fileReaderDiscontinuityTime."; } leaf records { type yang:counter64; units "Data Records"; config false; description "The number of Data Records read by the File Reader. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of fileReaderDiscontinuityTime."; } leaf templates { type yang:counter32; units "Templates"; config false; description "The number of Template Records (excluding Options Template Records) read by the File Reader. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of fileReaderDiscontinuityTime."; } leaf optionsTemplates { type yang:counter32; units "Options Templates"; config false; description "The number of Options Template Records read by the File Reader. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of fileReaderDiscontinuityTime."; } leaf fileReaderDiscontinuityTime { type yang:date-and-time; config false; description "Timestamp of the most recent occasion at which one or more File Reader counters suffered a discontinuity. In contrast to discontinuity times in the IPFIX MIB module, the time is absolute and not relative to sysUpTime."; } list template { config false; description "This list contains the Templates and Options Templates that have been read by the File Reader. Withdrawn or invalidated (Options) Template MUST be removed from this list."; uses templateParameters; } } grouping transportLayerSecurityParameters { description "TLS or DTLS parameters."; leaf-list localCertificationAuthorityDN { type string; description "Distinguished names of certification authorities whose certificates may be used to identify the local endpoint."; reference "RFC 5280."; } leaf-list localSubjectDN { type string; description "Distinguished names that may be used in the certificates to identify the local endpoint."; reference "RFC 5280."; } leaf-list localSubjectFQDN { type inet:domain-name; description "Fully qualified domain names that may be used to in the certificates to identify the local endpoint."; reference "RFC 5280."; } leaf-list remoteCertificationAuthorityDN { type string; description "Distinguished names of certification authorities whose certificates are accepted to authorize remote endpoints."; reference "RFC 5280."; } leaf-list remoteSubjectDN { type string; description "Distinguished names which are accepted in certificates to authorize remote endpoints."; reference "RFC 5280."; } leaf-list remoteSubjectFQDN { type inet:domain-name; description "Fully qualified domain names that are accepted in certificates to authorize remote endpoints."; reference "RFC 5280."; } } grouping templateParameters { description "State parameters of a Template used by an Exporting Process or received by a Collecting Process in a specific Transport Session. Parameter names and semantics correspond to the managed objects in IPFIX-MIB"; reference "RFC 5101; RFC 6615, Section 8 (ipfixTemplateEntry, ipfixTemplateDefinitionEntry, ipfixTemplateStatsEntry)"; leaf observationDomainId { type uint32; description "The ID of the Observation Domain for which this Template is defined. Note that this parameter corresponds to ipfixTemplateObservationDomainId in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTemplateObservationDomainId)."; } leaf templateId { type uint16 { range "256..65535" { description "Valid range of Template IDs."; reference "RFC 5101"; } } description "This number indicates the Template ID in the IPFIX message. Note that this parameter corresponds to ipfixTemplateId in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTemplateId)."; } leaf setId { type uint16; description "This number indicates the Set ID of the Template. Currently, there are two values defined. The value 2 is used for Sets containing Template definitions. The value 3 is used for Sets containing Options Template definitions. Note that this parameter corresponds to ipfixTemplateSetId in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTemplateSetId)."; } leaf accessTime { type yang:date-and-time; description "Used for Exporting Processes, this parameter contains the time when this (Options) Template was last sent to the Collector(s) or written to the file. Used for Collecting Processes, this parameter contains the time when this (Options) Template was last received from the Exporter or read from the file. Note that this parameter corresponds to ipfixTemplateAccessTime in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTemplateAccessTime)."; } leaf templateDataRecords { type yang:counter64; description "The number of transmitted or received Data Records defined by this (Options) Template. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of templateDiscontinuityTime. Note that this parameter corresponds to ipfixTemplateDataRecords in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTemplateDataRecords)."; } leaf templateDiscontinuityTime { type yang:date-and-time; description "Timestamp of the most recent occasion at which the counter templateDataRecords suffered a discontinuity. Note that this parameter functionally corresponds to ipfixTemplateDiscontinuityTime in the IPFIX MIB module. In contrast to ipfixTemplateDiscontinuityTime, the time is absolute and not relative to sysUpTime."; reference "RFC 6615, Section 8 (ipfixTemplateDiscontinuityTime)."; } list field { description "This list contains the (Options) Template fields of which the (Options) Template is defined. The order of the list corresponds to the order of the fields in the (Option) Template Record."; leaf ieId { type ieIdType; description "This parameter indicates the Information Element identifier of the field. Note that this parameter corresponds to ipfixTemplateDefinitionIeId in the IPFIX MIB module."; reference "RFC 5101; RFC 6615, Section 8 (ipfixTemplateDefinitionIeId)."; } leaf ieLength { type uint16; units octets; description "This parameter indicates the length of the Information Element of the field. Note that this parameter corresponds to ipfixTemplateDefinitionIeLength in the IPFIX MIB module."; reference "RFC 5101; RFC 6615, Section 8 (ipfixTemplateDefinitionIeLength)."; } leaf ieEnterpriseNumber { type uint32; description "This parameter indicates the IANA enterprise number of the authority defining the Information Element identifier. If the Information Element is not enterprise-specific, this state parameter is zero. Note that this parameter corresponds to ipfixTemplateDefinitionIeEnterpriseNumber in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTemplateDefinitionIeEnterpriseNumber); IANA registry for Private Enterprise Numbers, http://www.iana.org/assignments/enterprise-numbers."; } leaf isFlowKey { when "../../setId = 2" { description "This parameter is available for non-Options Templates (Set ID is 2)."; } type empty; description "If present, this is a Flow Key field. Note that this corresponds to flowKey(1) being set in ipfixTemplateDefinitionFlags."; reference "RFC 6615, Section 8 (ipfixTemplateDefinitionFlags)."; } leaf isScope { when "../../setId = 3" { description "This parameter is available for Options Templates (Set ID is 3)."; } type empty; description "If present, this is a scope field. Note that this corresponds to scope(0) being set in ipfixTemplateDefinitionFlags."; reference "RFC 6615, Section 8 (ipfixTemplateDefinitionFlags)."; } } } grouping transportSessionParameters { description "State parameters of a Transport Session originating from an Exporting Process or terminating at a Collecting Process. Parameter names and semantics correspond to the managed objects in IPFIX-MIB."; reference "RFC 5101; RFC 6615, Section 8 (ipfixTransportSessionEntry, ipfixTransportSessionStatsEntry)."; leaf ipfixVersion { type uint16; description "Used for Exporting Processes, this parameter contains the version number of the IPFIX protocol that the Exporter uses to export its data in this Transport Session. Hence, it is identical to the value of the configuration parameter ipfixVersion of the outer SctpExporter, UdpExporter, or TcpExporter node. Used for Collecting Processes, this parameter contains the version number of the IPFIX protocol it receives for this Transport Session. If IPFIX Messages of different IPFIX protocol versions are received, this parameter contains the maximum version number. Note that this parameter corresponds to ipfixTransportSessionIpfixVersion in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionIpfixVersion)."; } leaf sourceAddress { type inet:ip-address; description "The source address of the Exporter of the IPFIX Transport Session. If the transport protocol is SCTP, this is one of the potentially many IP addresses of the Exporter. Preferably, the source IP address of the path that is usually selected by the Exporter to send IPFIX Messages to the Collector SHOULD be used. Note that this parameter functionally corresponds to ipfixTransportSessionSourceAddressType and ipfixTransportSessionSourceAddress in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionSourceAddressType, ipfixTransportSessionSourceAddress); RFC 4960, Section 6.4."; } leaf destinationAddress { type inet:ip-address; description "The destination address of the Collector of the IPFIX Transport Session. If the transport protocol is SCTP, this is one of the potentially many IP addresses of the Collector. Preferably, the destination IP address of the path that is usually selected by the Exporter to send IPFIX Messages to the Collector SHOULD be used. Note that this parameter functionally corresponds to ipfixTransportSessionDestinationAddressType and ipfixTransportSessionDestinationAddress in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionDestinationAddressType, ipfixTransportSessionDestinationAddress); RFC 4960, Section 6.4."; } leaf sourcePort { type inet:port-number; description "The transport-protocol port number of the Exporter of the IPFIX Transport Session. Note that this parameter corresponds to ipfixTransportSessionSourcePort in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionSourcePort)."; } leaf destinationPort { type inet:port-number; description "The transport-protocol port number of the Collector of the IPFIX Transport Session. Note that this parameter corresponds to ipfixTransportSessionDestinationPort in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionDestinationPort)."; } leaf sctpAssocId { type uint32; description "The association ID used for the SCTP session between the Exporter and the Collector of the IPFIX Transport Session. It is equal to the sctpAssocId entry in the sctpAssocTable defined in the SCTP-MIB. This parameter is only available if the transport protocol is SCTP and if an SNMP agent on the same Monitoring Device enables access to the corresponding MIB objects in the sctpAssocTable. Note that this parameter corresponds to ipfixTransportSessionSctpAssocId in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionSctpAssocId); RFC 3871"; } leaf status { type transportSessionStatus; description "Status of the Transport Session. Note that this parameter corresponds to ipfixTransportSessionStatus in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionStatus)."; } leaf rate { type yang:gauge32; units "bytes per second"; description "The number of bytes per second transmitted by the Exporting Process or received by the Collecting Process. This parameter is updated every second. Note that this parameter corresponds to ipfixTransportSessionRate in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionRate)."; } leaf bytes { type yang:counter64; units bytes; description "The number of bytes transmitted by the Exporting Process or received by the Collecting Process. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of transportSessionDiscontinuityTime. Note that this parameter corresponds to ipfixTransportSessionBytes in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionBytes)."; } leaf messages { type yang:counter64; units "IPFIX Messages"; description "The number of messages transmitted by the Exporting Process or received by the Collecting Process. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of transportSessionDiscontinuityTime. Note that this parameter corresponds to ipfixTransportSessionMessages in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionMessages)."; } leaf discardedMessages { type yang:counter64; units "IPFIX Messages"; description "Used for Exporting Processes, this parameter indicates the number of messages that could not be sent due to internal buffer overflows, network congestion, routing issues, etc. Used for Collecting Process, this parameter indicates the number of received IPFIX Message that are malformed, cannot be decoded, are received in the wrong order or are missing according to the sequence number. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of transportSessionDiscontinuityTime. Note that this parameter corresponds to ipfixTransportSessionDiscardedMessages in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionDiscardedMessages)."; } leaf records { type yang:counter64; units "Data Records"; description "The number of Data Records transmitted by the Exporting Process or received by the Collecting Process. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of transportSessionDiscontinuityTime. Note that this parameter corresponds to ipfixTransportSessionRecords in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionRecords)."; } leaf templates { type yang:counter32; units "Templates"; description "The number of Templates transmitted by the Exporting Process or received by the Collecting Process. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of transportSessionDiscontinuityTime. Note that this parameter corresponds to ipfixTransportSessionTemplates in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionTemplates)."; } leaf optionsTemplates { type yang:counter32; units "Options Templates"; description "The number of Option Templates transmitted by the Exporting Process or received by the Collecting Process. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of transportSessionDiscontinuityTime. Note that this parameter corresponds to ipfixTransportSessionOptionsTemplates in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixTransportSessionOptionsTemplates)."; } leaf transportSessionStartTime { type yang:date-and-time; description "Timestamp of the start of the given Transport Session. This state parameter does not correspond to any object in the IPFIX MIB module."; } leaf transportSessionDiscontinuityTime { type yang:date-and-time; description "Timestamp of the most recent occasion at which one or more of the Transport Session counters suffered a discontinuity. Note that this parameter functionally corresponds to ipfixTransportSessionDiscontinuityTime in the IPFIX MIB module. In contrast to ipfixTransportSessionDiscontinuityTime, the time is absolute and not relative to sysUpTime."; reference "RFC 6615, Section 8 (ipfixTransportSessionDiscontinuityTime)."; } list template { description "This list contains the Templates and Options Templates that are transmitted by the Exporting Process or received by the Collecting Process. Withdrawn or invalidated (Options) Templates MUST be removed from this list."; uses templateParameters; } } /***************************************************************** * Main container *****************************************************************/ container ipfix { description "Top-level node of the IPFIX/PSAMP configuration data model."; list collectingProcess { if-feature collector; key name; description "Collecting Process of the Monitoring Device."; leaf name { type nameType; description "Key of this list."; } uses collectingProcessParameters; leaf-list exportingProcess { if-feature exporter; type leafref { path "/ipfix/exportingProcess/name"; } description "Export of received records without any modifications. Records are processed by all Exporting Processes in the list."; } } list observationPoint { if-feature meter; key name; description "Observation Point of the Monitoring Device."; leaf name { type nameType; description "Key of this list."; } uses observationPointParameters; leaf-list selectionProcess { type leafref { path "/ipfix/selectionProcess/name"; } description "Selection Processes in this list process packets in parallel."; } } list selectionProcess { if-feature meter; key name; description "Selection Process of the Monitoring Device."; leaf name { type nameType; description "Key of this list."; } list selector { key name; min-elements 1; ordered-by user; description "List of Selectors that define the action of the Selection Process on a single packet. The Selectors are serially invoked in the same order as they appear in this list."; leaf name { type nameType; description "Key of this list."; } uses selectorParameters; } list selectionSequence { config false; description "This list contains the Selection Sequence IDs that are assigned by the Monitoring Device to distinguish different Selection Sequences passing through the Selection Process. As Selection Sequence IDs are unique per Observation Domain, the corresponding Observation Domain IDs are included as well. With this information, it is possible to associate Selection Sequence (Statistics) Report Interpretations exported according to the PSAMP protocol with a Selection Process in the configuration data."; reference "RFC 5476."; leaf observationDomainId { type uint32; description "Observation Domain ID for which the Selection Sequence ID is assigned."; } leaf selectionSequenceId { type uint64; description "Selection Sequence ID used in the Selection Sequence (Statistics) Report Interpretation."; } } leaf cache { type leafref { path "/ipfix/cache/name"; } description "Cache that receives the output of the Selection Process."; } } list cache { if-feature meter; key name; description "Cache of the Monitoring Device."; leaf name { type nameType; description "Key of this list."; } leaf meteringProcessId { type uint32; config false; description "The identifier of the Metering Process this Cache belongs to. This parameter corresponds to the Information Element meteringProcessId. Its occurrence helps to associate Cache parameters with Metering Process statistics exported by the Monitoring Device using the Metering Process (Reliability) Statistics Template as defined by the IPFIX protocol specification."; reference "RFC 5101, Sections 4.1 and 4.2; IANA registry for IPFIX Entities, http://www.iana.org/assignments/ipfix."; } leaf dataRecords { type yang:counter64; units "Data Records"; config false; description "The number of Data Records generated by this Cache. Discontinuities in the value of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of cacheDiscontinuityTime. Note that this parameter corresponds to ipfixMeteringProcessDataRecords in the IPFIX MIB module."; reference "RFC 6615, Section 8 (ipfixMeteringProcessDataRecords)."; } leaf cacheDiscontinuityTime { type yang:date-and-time; config false; description "Timestamp of the most recent occasion at which the counter dataRecords suffered a discontinuity. Note that this parameter functionally corresponds to ipfixMeteringProcessDiscontinuityTime in the IPFIX MIB module. In contrast to ipfixMeteringProcessDiscontinuityTime, the time is absolute and not relative to sysUpTime."; reference "RFC 6615, Section 8 (ipfixMeteringProcessDiscontinuityTime)."; } choice CacheType { mandatory true; description "Type of Cache and specific parameters."; container immediateCache { if-feature immediateCache; description "Flow expiration after the first packet; generation of Packet Records."; uses cacheLayoutParameters; } container timeoutCache { if-feature timeoutCache; description "Flow expiration after active and idle timeout; generation of Flow Records."; uses flowCacheParameters; uses cacheLayoutParameters; } container naturalCache { if-feature naturalCache; description "Flow expiration after active and idle timeout, or on natural termination (e.g., TCP FIN or TCP RST) of the Flow; generation of Flow Records."; uses flowCacheParameters; uses cacheLayoutParameters; } container permanentCache { if-feature permanentCache; description "No flow expiration, periodical export with time interval exportInterval; generation of Flow Records."; uses flowCacheParameters; uses cacheLayoutParameters; } } leaf-list exportingProcess { if-feature exporter; type leafref { path "/ipfix/exportingProcess/name"; } description "Records are exported by all Exporting Processes in the list."; } } list exportingProcess { if-feature exporter; key name; description "Exporting Process of the Monitoring Device."; leaf name { type nameType; description "Key of this list."; } uses exportingProcessParameters; } } } yuma123_2.14/netconf/modules/ietf/ietf-snmp-usm@2014-12-10.yang0000664000175000017500000001377014770023131023617 0ustar vladimirvladimirsubmodule ietf-snmp-usm { belongs-to ietf-snmp { prefix snmp; } import ietf-yang-types { prefix yang; } import ietf-netconf-acm { prefix nacm; } include ietf-snmp-common; include ietf-snmp-target; include ietf-snmp-proxy; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This submodule contains a collection of YANG definitions for configuring the User-based Security Model (USM) of SNMP. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; reference "RFC 3414: User-based Security Model (USM) for version 3 of the Simple Network Management Protocol (SNMPv3)"; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } grouping key { leaf key { type yang:hex-string; mandatory true; nacm:default-deny-all; description "Localized key specified as a list of colon-specified hexadecimal octets."; } } grouping user-list { list user { key "name"; reference "RFC 3414: User-based Security Model (USM) for version 3 of the Simple Network Management Protocol (SNMPv3). SNMP-USER-BASED-SM-MIB.usmUserTable"; leaf name { type snmp:identifier; reference "RFC 3414: User-based Security Model (USM) for version 3 of the Simple Network Management Protocol (SNMPv3). SNMP-USER-BASED-SM-MIB.usmUserName"; } container auth { presence "enables authentication"; description "Enables authentication of the user."; choice protocol { mandatory true; reference "RFC 3414: User-based Security Model (USM) for version 3 of the Simple Network Management Protocol (SNMPv3). SNMP-USER-BASED-SM-MIB.usmUserAuthProtocol"; container md5 { uses key; reference "RFC 3414: User-based Security Model (USM) for version 3 of the Simple Network Management Protocol (SNMPv3). SNMP-USER-BASED-SM-MIB.usmHMACMD5AuthProtocol"; } container sha { uses key; reference "RFC 3414: User-based Security Model (USM) for version 3 of the Simple Network Management Protocol (SNMPv3). SNMP-USER-BASED-SM-MIB.usmHMACSHAAuthProtocol"; } } } container priv { must "../auth" { error-message "when privacy (confidentiality) is used, " + "authentication must also be used"; } presence "enables encryption"; description "Enables encryption of SNMP messages."; choice protocol { mandatory true; reference "RFC 3414: User-based Security Model (USM) for version 3 of the Simple Network Management Protocol (SNMPv3). SNMP-USER-BASED-SM-MIB.usmUserPrivProtocol"; container des { uses key; reference "RFC 3414: User-based Security Model (USM) for version 3 of the Simple Network Management Protocol (SNMPv3). SNMP-USER-BASED-SM-MIB.usmDESPrivProtocol"; } container aes { uses key; reference "RFC 3826: The Advanced Encryption Standard (AES) Cipher Algorithm in the SNMP User-based Security Model. SNMP-USM-AES-MIB.usmAesCfb128Protocol"; } } } } } augment /snmp:snmp { container usm { description "Configuration of the User-based Security Model."; container local { uses user-list; } list remote { key "engine-id"; leaf engine-id { type snmp:engine-id; reference "RFC 3414: User-based Security Model (USM) for version 3 of the Simple Network Management Protocol (SNMPv3). SNMP-USER-BASED-SM-MIB.usmUserEngineID"; } uses user-list; } } } grouping usm-target-params { container usm { description "User-based SNMPv3 parameters type. Represents snmpTargetParamsMPModel '3' and snmpTargetParamsSecurityModel '3'."; leaf user-name { type snmp:security-name; mandatory true; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetParamsSecurityName"; } leaf security-level { type snmp:security-level; mandatory true; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetParamsSecurityLevel"; } } } augment /snmp:snmp/snmp:target-params/snmp:params { case usm { uses usm-target-params; } } } yuma123_2.14/netconf/modules/ietf/ietf-ip@2014-06-16.yang0000664000175000017500000005041114770023131022452 0ustar vladimirvladimirmodule ietf-ip { namespace "urn:ietf:params:xml:ns:yang:ietf-ip"; prefix ip; import ietf-interfaces { prefix if; } import ietf-inet-types { prefix inet; } import ietf-yang-types { prefix yang; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund "; description "This module contains a collection of YANG definitions for configuring IP implementations. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7277; see the RFC itself for full legal notices."; revision 2014-06-16 { description "Initial revision."; reference "RFC 7277: A YANG Data Model for IP Management"; } /* * Features */ feature ipv4-non-contiguous-netmasks { description "Indicates support for configuring non-contiguous subnet masks."; } feature ipv6-privacy-autoconf { description "Indicates support for Privacy Extensions for Stateless Address Autoconfiguration in IPv6."; reference "RFC 4941: Privacy Extensions for Stateless Address Autoconfiguration in IPv6"; } /* * Typedefs */ typedef ip-address-origin { type enumeration { enum other { description "None of the following."; } enum static { description "Indicates that the address has been statically configured - for example, using NETCONF or a Command Line Interface."; } enum dhcp { description "Indicates an address that has been assigned to this system by a DHCP server."; } enum link-layer { description "Indicates an address created by IPv6 stateless autoconfiguration that embeds a link-layer address in its interface identifier."; } enum random { description "Indicates an address chosen by the system at random, e.g., an IPv4 address within 169.254/16, an RFC 4941 temporary address, or an RFC 7217 semantically opaque address."; reference "RFC 4941: Privacy Extensions for Stateless Address Autoconfiguration in IPv6 RFC 7217: A Method for Generating Semantically Opaque Interface Identifiers with IPv6 Stateless Address Autoconfiguration (SLAAC)"; } } description "The origin of an address."; } typedef neighbor-origin { type enumeration { enum other { description "None of the following."; } enum static { description "Indicates that the mapping has been statically configured - for example, using NETCONF or a Command Line Interface."; } enum dynamic { description "Indicates that the mapping has been dynamically resolved using, e.g., IPv4 ARP or the IPv6 Neighbor Discovery protocol."; } } description "The origin of a neighbor entry."; } /* * Configuration data nodes */ augment "/if:interfaces/if:interface" { description "Parameters for configuring IP on interfaces. If an interface is not capable of running IP, the server must not allow the client to configure these parameters."; container ipv4 { presence "Enables IPv4 unless the 'enabled' leaf (which defaults to 'true') is set to 'false'"; description "Parameters for the IPv4 address family."; leaf enabled { type boolean; default true; description "Controls whether IPv4 is enabled or disabled on this interface. When IPv4 is enabled, this interface is connected to an IPv4 stack, and the interface can send and receive IPv4 packets."; } leaf forwarding { type boolean; default false; description "Controls IPv4 packet forwarding of datagrams received by, but not addressed to, this interface. IPv4 routers forward datagrams. IPv4 hosts do not (except those source-routed via the host)."; } leaf mtu { type uint16 { range "68..max"; } units octets; description "The size, in octets, of the largest IPv4 packet that the interface will send and receive. The server may restrict the allowed values for this leaf, depending on the interface's type. If this leaf is not configured, the operationally used MTU depends on the interface's type."; reference "RFC 791: Internet Protocol"; } list address { key "ip"; description "The list of configured IPv4 addresses on the interface."; leaf ip { type inet:ipv4-address-no-zone; description "The IPv4 address on the interface."; } choice subnet { mandatory true; description "The subnet can be specified as a prefix-length, or, if the server supports non-contiguous netmasks, as a netmask."; leaf prefix-length { type uint8 { range "0..32"; } description "The length of the subnet prefix."; } leaf netmask { if-feature ipv4-non-contiguous-netmasks; type yang:dotted-quad; description "The subnet specified as a netmask."; } } } list neighbor { key "ip"; description "A list of mappings from IPv4 addresses to link-layer addresses. Entries in this list are used as static entries in the ARP Cache."; reference "RFC 826: An Ethernet Address Resolution Protocol"; leaf ip { type inet:ipv4-address-no-zone; description "The IPv4 address of the neighbor node."; } leaf link-layer-address { type yang:phys-address; mandatory true; description "The link-layer address of the neighbor node."; } } } container ipv6 { presence "Enables IPv6 unless the 'enabled' leaf (which defaults to 'true') is set to 'false'"; description "Parameters for the IPv6 address family."; leaf enabled { type boolean; default true; description "Controls whether IPv6 is enabled or disabled on this interface. When IPv6 is enabled, this interface is connected to an IPv6 stack, and the interface can send and receive IPv6 packets."; } leaf forwarding { type boolean; default false; description "Controls IPv6 packet forwarding of datagrams received by, but not addressed to, this interface. IPv6 routers forward datagrams. IPv6 hosts do not (except those source-routed via the host)."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) Section 6.2.1, IsRouter"; } leaf mtu { type uint32 { range "1280..max"; } units octets; description "The size, in octets, of the largest IPv6 packet that the interface will send and receive. The server may restrict the allowed values for this leaf, depending on the interface's type. If this leaf is not configured, the operationally used MTU depends on the interface's type."; reference "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification Section 5"; } list address { key "ip"; description "The list of configured IPv6 addresses on the interface."; leaf ip { type inet:ipv6-address-no-zone; description "The IPv6 address on the interface."; } leaf prefix-length { type uint8 { range "0..128"; } mandatory true; description "The length of the subnet prefix."; } } list neighbor { key "ip"; description "A list of mappings from IPv6 addresses to link-layer addresses. Entries in this list are used as static entries in the Neighbor Cache."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)"; leaf ip { type inet:ipv6-address-no-zone; description "The IPv6 address of the neighbor node."; } leaf link-layer-address { type yang:phys-address; mandatory true; description "The link-layer address of the neighbor node."; } } leaf dup-addr-detect-transmits { type uint32; default 1; description "The number of consecutive Neighbor Solicitation messages sent while performing Duplicate Address Detection on a tentative address. A value of zero indicates that Duplicate Address Detection is not performed on tentative addresses. A value of one indicates a single transmission with no follow-up retransmissions."; reference "RFC 4862: IPv6 Stateless Address Autoconfiguration"; } container autoconf { description "Parameters to control the autoconfiguration of IPv6 addresses, as described in RFC 4862."; reference "RFC 4862: IPv6 Stateless Address Autoconfiguration"; leaf create-global-addresses { type boolean; default true; description "If enabled, the host creates global addresses as described in RFC 4862."; reference "RFC 4862: IPv6 Stateless Address Autoconfiguration Section 5.5"; } leaf create-temporary-addresses { if-feature ipv6-privacy-autoconf; type boolean; default false; description "If enabled, the host creates temporary addresses as described in RFC 4941."; reference "RFC 4941: Privacy Extensions for Stateless Address Autoconfiguration in IPv6"; } leaf temporary-valid-lifetime { if-feature ipv6-privacy-autoconf; type uint32; units "seconds"; default 604800; description "The time period during which the temporary address is valid."; reference "RFC 4941: Privacy Extensions for Stateless Address Autoconfiguration in IPv6 - TEMP_VALID_LIFETIME"; } leaf temporary-preferred-lifetime { if-feature ipv6-privacy-autoconf; type uint32; units "seconds"; default 86400; description "The time period during which the temporary address is preferred."; reference "RFC 4941: Privacy Extensions for Stateless Address Autoconfiguration in IPv6 - TEMP_PREFERRED_LIFETIME"; } } } } /* * Operational state data nodes */ augment "/if:interfaces-state/if:interface" { description "Data nodes for the operational state of IP on interfaces."; container ipv4 { presence "Present if IPv4 is enabled on this interface"; config false; description "Interface-specific parameters for the IPv4 address family."; leaf forwarding { type boolean; description "Indicates whether IPv4 packet forwarding is enabled or disabled on this interface."; } leaf mtu { type uint16 { range "68..max"; } units octets; description "The size, in octets, of the largest IPv4 packet that the interface will send and receive."; reference "RFC 791: Internet Protocol"; } list address { key "ip"; description "The list of IPv4 addresses on the interface."; leaf ip { type inet:ipv4-address-no-zone; description "The IPv4 address on the interface."; } choice subnet { description "The subnet can be specified as a prefix-length, or, if the server supports non-contiguous netmasks, as a netmask."; leaf prefix-length { type uint8 { range "0..32"; } description "The length of the subnet prefix."; } leaf netmask { if-feature ipv4-non-contiguous-netmasks; type yang:dotted-quad; description "The subnet specified as a netmask."; } } leaf origin { type ip-address-origin; description "The origin of this address."; } } list neighbor { key "ip"; description "A list of mappings from IPv4 addresses to link-layer addresses. This list represents the ARP Cache."; reference "RFC 826: An Ethernet Address Resolution Protocol"; leaf ip { type inet:ipv4-address-no-zone; description "The IPv4 address of the neighbor node."; } leaf link-layer-address { type yang:phys-address; description "The link-layer address of the neighbor node."; } leaf origin { type neighbor-origin; description "The origin of this neighbor entry."; } } } container ipv6 { presence "Present if IPv6 is enabled on this interface"; config false; description "Parameters for the IPv6 address family."; leaf forwarding { type boolean; default false; description "Indicates whether IPv6 packet forwarding is enabled or disabled on this interface."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) Section 6.2.1, IsRouter"; } leaf mtu { type uint32 { range "1280..max"; } units octets; description "The size, in octets, of the largest IPv6 packet that the interface will send and receive."; reference "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification Section 5"; } list address { key "ip"; description "The list of IPv6 addresses on the interface."; leaf ip { type inet:ipv6-address-no-zone; description "The IPv6 address on the interface."; } leaf prefix-length { type uint8 { range "0..128"; } mandatory true; description "The length of the subnet prefix."; } leaf origin { type ip-address-origin; description "The origin of this address."; } leaf status { type enumeration { enum preferred { description "This is a valid address that can appear as the destination or source address of a packet."; } enum deprecated { description "This is a valid but deprecated address that should no longer be used as a source address in new communications, but packets addressed to such an address are processed as expected."; } enum invalid { description "This isn't a valid address, and it shouldn't appear as the destination or source address of a packet."; } enum inaccessible { description "The address is not accessible because the interface to which this address is assigned is not operational."; } enum unknown { description "The status cannot be determined for some reason."; } enum tentative { description "The uniqueness of the address on the link is being verified. Addresses in this state should not be used for general communication and should only be used to determine the uniqueness of the address."; } enum duplicate { description "The address has been determined to be non-unique on the link and so must not be used."; } enum optimistic { description "The address is available for use, subject to restrictions, while its uniqueness on a link is being verified."; } } description "The status of an address. Most of the states correspond to states from the IPv6 Stateless Address Autoconfiguration protocol."; reference "RFC 4293: Management Information Base for the Internet Protocol (IP) - IpAddressStatusTC RFC 4862: IPv6 Stateless Address Autoconfiguration"; } } list neighbor { key "ip"; description "A list of mappings from IPv6 addresses to link-layer addresses. This list represents the Neighbor Cache."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)"; leaf ip { type inet:ipv6-address-no-zone; description "The IPv6 address of the neighbor node."; } leaf link-layer-address { type yang:phys-address; description "The link-layer address of the neighbor node."; } leaf origin { type neighbor-origin; description "The origin of this neighbor entry."; } leaf is-router { type empty; description "Indicates that the neighbor node acts as a router."; } leaf state { type enumeration { enum incomplete { description "Address resolution is in progress, and the link-layer address of the neighbor has not yet been determined."; } enum reachable { description "Roughly speaking, the neighbor is known to have been reachable recently (within tens of seconds ago)."; } enum stale { description "The neighbor is no longer known to be reachable, but until traffic is sent to the neighbor no attempt should be made to verify its reachability."; } enum delay { description "The neighbor is no longer known to be reachable, and traffic has recently been sent to the neighbor. Rather than probe the neighbor immediately, however, delay sending probes for a short while in order to give upper-layer protocols a chance to provide reachability confirmation."; } enum probe { description "The neighbor is no longer known to be reachable, and unicast Neighbor Solicitation probes are being sent to verify reachability."; } } description "The Neighbor Unreachability Detection state of this entry."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) Section 7.3.2"; } } } } } yuma123_2.14/netconf/modules/ietf/ietf-yang-library@2016-06-21.yang0000664000175000017500000001577614770023131024457 0ustar vladimirvladimirmodule ietf-yang-library { namespace "urn:ietf:params:xml:ns:yang:ietf-yang-library"; prefix "yanglib"; import ietf-yang-types { prefix yang; } import ietf-inet-types { prefix inet; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: WG List: WG Chair: Mehmet Ersue WG Chair: Mahesh Jethanandani Editor: Andy Bierman Editor: Martin Bjorklund Editor: Kent Watsen "; description "This module contains monitoring information about the YANG modules and submodules that are used within a YANG-based server. Copyright (c) 2016 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7895; see the RFC itself for full legal notices."; revision 2016-06-21 { description "Initial revision."; reference "RFC 7895: YANG Module Library."; } /* * Typedefs */ typedef revision-identifier { type string { pattern '\d{4}-\d{2}-\d{2}'; } description "Represents a specific date in YYYY-MM-DD format."; } /* * Groupings */ grouping module-list { description "The module data structure is represented as a grouping so it can be reused in configuration or another monitoring data structure."; grouping common-leafs { description "Common parameters for YANG modules and submodules."; leaf name { type yang:yang-identifier; description "The YANG module or submodule name."; } leaf revision { type union { type revision-identifier; type string { length 0; } } description "The YANG module or submodule revision date. A zero-length string is used if no revision statement is present in the YANG module or submodule."; } } grouping schema-leaf { description "Common schema leaf parameter for modules and submodules."; leaf schema { type inet:uri; description "Contains a URL that represents the YANG schema resource for this module or submodule. This leaf will only be present if there is a URL available for retrieval of the schema for this entry."; } } list module { key "name revision"; description "Each entry represents one revision of one module currently supported by the server."; uses common-leafs; uses schema-leaf; leaf namespace { type inet:uri; mandatory true; description "The XML namespace identifier for this module."; } leaf-list feature { type yang:yang-identifier; description "List of YANG feature names from this module that are supported by the server, regardless of whether they are defined in the module or any included submodule."; } list deviation { key "name revision"; description "List of YANG deviation module names and revisions used by this server to modify the conformance of the module associated with this entry. Note that the same module can be used for deviations for multiple modules, so the same entry MAY appear within multiple 'module' entries. The deviation module MUST be present in the 'module' list, with the same name and revision values. The 'conformance-type' value will be 'implement' for the deviation module."; uses common-leafs; } leaf conformance-type { type enumeration { enum implement { description "Indicates that the server implements one or more protocol-accessible objects defined in the YANG module identified in this entry. This includes deviation statements defined in the module. For YANG version 1.1 modules, there is at most one module entry with conformance type 'implement' for a particular module name, since YANG 1.1 requires that, at most, one revision of a module is implemented. For YANG version 1 modules, there SHOULD NOT be more than one module entry for a particular module name."; } enum import { description "Indicates that the server imports reusable definitions from the specified revision of the module but does not implement any protocol-accessible objects from this revision. Multiple module entries for the same module name MAY exist. This can occur if multiple modules import the same module but specify different revision dates in the import statements."; } } mandatory true; description "Indicates the type of conformance the server is claiming for the YANG module identified by this entry."; } list submodule { key "name revision"; description "Each entry represents one submodule within the parent module."; uses common-leafs; uses schema-leaf; } } } /* * Operational state data nodes */ container modules-state { config false; description "Contains YANG module monitoring information."; leaf module-set-id { type string; mandatory true; description "Contains a server-specific identifier representing the current set of modules and submodules. The server MUST change the value of this leaf if the information represented by the 'module' list instances has changed."; } uses module-list; } /* * Notifications */ notification yang-library-change { description "Generated when the set of modules and submodules supported by the server has changed."; leaf module-set-id { type leafref { path "/yanglib:modules-state/yanglib:module-set-id"; } mandatory true; description "Contains the module-set-id value representing the set of modules and submodules supported at the server at the time the notification is generated."; } } } yuma123_2.14/netconf/modules/ietf/ietf-network-topology@2018-02-26.yang0000664000175000017500000002410314770023131025405 0ustar vladimirvladimirmodule ietf-network-topology { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-network-topology"; prefix nt; import ietf-inet-types { prefix inet; reference "RFC 6991: Common YANG Data Types"; } import ietf-network { prefix nw; reference "RFC 8345: A YANG Data Model for Network Topologies"; } organization "IETF I2RS (Interface to the Routing System) Working Group"; contact "WG Web: WG List: Editor: Alexander Clemm Editor: Jan Medved Editor: Robert Varga Editor: Nitin Bahadur Editor: Hariharan Ananthakrishnan Editor: Xufeng Liu "; description "This module defines a common base model for a network topology, augmenting the base network data model with links to connect nodes, as well as termination points to terminate links on nodes. Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8345; see the RFC itself for full legal notices."; revision 2018-02-26 { description "Initial revision."; reference "RFC 8345: A YANG Data Model for Network Topologies"; } typedef link-id { type inet:uri; description "An identifier for a link in a topology. The precise structure of the link-id will be up to the implementation. The identifier SHOULD be chosen such that the same link in a real network topology will always be identified through the same identifier, even if the data model is instantiated in separate datastores. An implementation MAY choose to capture semantics in the identifier -- for example, to indicate the type of link and/or the type of topology of which the link is a part."; } typedef tp-id { type inet:uri; description "An identifier for termination points on a node. The precise structure of the tp-id will be up to the implementation. The identifier SHOULD be chosen such that the same termination point in a real network topology will always be identified through the same identifier, even if the data model is instantiated in separate datastores. An implementation MAY choose to capture semantics in the identifier -- for example, to indicate the type of termination point and/or the type of node that contains the termination point."; } grouping link-ref { description "This grouping can be used to reference a link in a specific network. Although it is not used in this module, it is defined here for the convenience of augmenting modules."; leaf link-ref { type leafref { path "/nw:networks/nw:network[nw:network-id=current()/../"+ "network-ref]/nt:link/nt:link-id"; require-instance false; } description "A type for an absolute reference to a link instance. (This type should not be used for relative references. In such a case, a relative path should be used instead.)"; } uses nw:network-ref; } grouping tp-ref { description "This grouping can be used to reference a termination point in a specific node. Although it is not used in this module, it is defined here for the convenience of augmenting modules."; leaf tp-ref { type leafref { path "/nw:networks/nw:network[nw:network-id=current()/../"+ "network-ref]/nw:node[nw:node-id=current()/../"+ "node-ref]/nt:termination-point/nt:tp-id"; require-instance false; } description "A type for an absolute reference to a termination point. (This type should not be used for relative references. In such a case, a relative path should be used instead.)"; } uses nw:node-ref; } augment "/nw:networks/nw:network" { description "Add links to the network data model."; list link { key "link-id"; description "A network link connects a local (source) node and a remote (destination) node via a set of the respective node's termination points. It is possible to have several links between the same source and destination nodes. Likewise, a link could potentially be re-homed between termination points. Therefore, in order to ensure that we would always know to distinguish between links, every link is identified by a dedicated link identifier. Note that a link models a point-to-point link, not a multipoint link."; leaf link-id { type link-id; description "The identifier of a link in the topology. A link is specific to a topology to which it belongs."; } container source { description "This container holds the logical source of a particular link."; leaf source-node { type leafref { path "../../../nw:node/nw:node-id"; require-instance false; } description "Source node identifier. Must be in the same topology."; } leaf source-tp { type leafref { path "../../../nw:node[nw:node-id=current()/../"+ "source-node]/termination-point/tp-id"; require-instance false; } description "This termination point is located within the source node and terminates the link."; } } container destination { description "This container holds the logical destination of a particular link."; leaf dest-node { type leafref { path "../../../nw:node/nw:node-id"; require-instance false; } description "Destination node identifier. Must be in the same network."; } leaf dest-tp { type leafref { path "../../../nw:node[nw:node-id=current()/../"+ "dest-node]/termination-point/tp-id"; require-instance false; } description "This termination point is located within the destination node and terminates the link."; } } list supporting-link { key "network-ref link-ref"; description "Identifies the link or links on which this link depends."; leaf network-ref { type leafref { path "../../../nw:supporting-network/nw:network-ref"; require-instance false; } description "This leaf identifies in which underlay topology the supporting link is present."; } leaf link-ref { type leafref { path "/nw:networks/nw:network[nw:network-id=current()/"+ "../network-ref]/link/link-id"; require-instance false; } description "This leaf identifies a link that is a part of this link's underlay. Reference loops in which a link identifies itself as its underlay, either directly or transitively, are not allowed."; } } } } augment "/nw:networks/nw:network/nw:node" { description "Augments termination points that terminate links. Termination points can ultimately be mapped to interfaces."; list termination-point { key "tp-id"; description "A termination point can terminate a link. Depending on the type of topology, a termination point could, for example, refer to a port or an interface."; leaf tp-id { type tp-id; description "Termination point identifier."; } list supporting-termination-point { key "network-ref node-ref tp-ref"; description "This list identifies any termination points on which a given termination point depends or onto which it maps. Those termination points will themselves be contained in a supporting node. This dependency information can be inferred from the dependencies between links. Therefore, this item is not separately configurable. Hence, no corresponding constraint needs to be articulated. The corresponding information is simply provided by the implementing system."; leaf network-ref { type leafref { path "../../../nw:supporting-node/nw:network-ref"; require-instance false; } description "This leaf identifies in which topology the supporting termination point is present."; } leaf node-ref { type leafref { path "../../../nw:supporting-node/nw:node-ref"; require-instance false; } description "This leaf identifies in which node the supporting termination point is present."; } leaf tp-ref { type leafref { path "/nw:networks/nw:network[nw:network-id=current()/"+ "../network-ref]/nw:node[nw:node-id=current()/../"+ "node-ref]/termination-point/tp-id"; require-instance false; } description "Reference to the underlay node (the underlay node must be in a different topology)."; } } } } } yuma123_2.14/netconf/modules/ietf/ietf-snmp-tsm@2014-12-10.yang0000664000175000017500000000622314770023131023611 0ustar vladimirvladimirsubmodule ietf-snmp-tsm { belongs-to ietf-snmp { prefix snmp; } include ietf-snmp-common; include ietf-snmp-target; include ietf-snmp-proxy; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This submodule contains a collection of YANG definitions for configuring the Transport Security Model (TSM) of SNMP. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; reference "RFC 5591: Transport Security Model for the Simple Network Management Protocol (SNMP)"; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } feature tsm { description "A server implements this feature if it supports the Transport Security Model for SNMP."; reference "RFC 5591: Transport Security Model for the Simple Network Management Protocol (SNMP)"; } augment /snmp:snmp { if-feature tsm; container tsm { description "Configuration of the Transport Security Model."; leaf use-prefix { type boolean; default false; reference "RFC 5591: Transport Security Model for the Simple Network Management Protocol (SNMP). SNMP-TSM-MIB.snmpTsmConfigurationUsePrefix"; } } } grouping tsm-target-params { container tsm { description "Transport-based security SNMPv3 parameters type. Represents snmpTargetParamsMPModel '3' and snmpTargetParamsSecurityModel '4'."; leaf security-name { type snmp:security-name; mandatory true; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetParamsSecurityName"; } leaf security-level { type snmp:security-level; mandatory true; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetParamsSecurityLevel"; } } } augment /snmp:snmp/snmp:target-params/snmp:params { if-feature tsm; case tsm { uses tsm-target-params; } } } yuma123_2.14/netconf/modules/ietf/ietf-snmp-target@2014-12-10.yang0000664000175000017500000001500314770023131024270 0ustar vladimirvladimirsubmodule ietf-snmp-target { belongs-to ietf-snmp { prefix snmp; } import ietf-inet-types { prefix inet; } include ietf-snmp-common; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This submodule contains a collection of YANG definitions for configuring SNMP targets. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; reference "RFC 3413: Simple Network Management Protocol (SNMP) Applications"; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } augment /snmp:snmp { list target { key name; description "List of targets."; reference "RFC 3413: Simple Network Management Protocol (SNMP) Applications. SNMP-TARGET-MIB.snmpTargetAddrTable"; leaf name { type snmp:identifier; description "Identifies the target."; reference "RFC 3413: Simple Network Management Protocol (SNMP) Applications. SNMP-TARGET-MIB.snmpTargetAddrName"; } choice transport { mandatory true; description "Transport address of the target. The snmpTargetAddrTDomain and snmpTargetAddrTAddress objects are mapped to transport-specific YANG nodes. Each transport is configured as a separate case in this choice. Submodules providing configuration for additional transports are expected to augment this choice."; reference "RFC 3413: Simple Network Management Protocol (SNMP) Applications. SNMP-TARGET-MIB.snmpTargetAddrTDomain SNMP-TARGET-MIB.snmpTargetAddrTAddress"; case udp { reference "RFC 3417: Transport Mappings for the Simple Network Management Protocol (SNMP). SNMPv2-TM.snmpUDPDomain RFC 3419: Textual Conventions for Transport Addresses. TRANSPORT-ADDRESS-MIB.transportDomainUdpIpv4 TRANSPORT-ADDRESS-MIB.transportDomainUdpIpv4z TRANSPORT-ADDRESS-MIB.transportDomainUdpIpv6 TRANSPORT-ADDRESS-MIB.transportDomainUdpIpv6z"; container udp { leaf ip { type inet:ip-address; mandatory true; reference "RFC 3413: Simple Network Management Protocol (SNMP). SNMP-TARGET-MIB.snmpTargetAddrTAddress"; } leaf port { type inet:port-number; default 162; description "UDP port number."; reference "RFC 3413: Simple Network Management Protocol (SNMP). SNMP-TARGET-MIB.snmpTargetAddrTAddress"; } leaf prefix-length { type uint8; description "The value of this leaf must match the value of ../snmp:ip. If ../snmp:ip contains an IPv4 address, this leaf must be less than or equal to 32. If it contains an IPv6 address, it must be less than or equal to 128. Note that the prefix-length is currently only used by the Community-based Security Model to filter incoming messages. Furthermore, the prefix-length filtering does not cover all possible filters supported by the corresponding MIB object."; reference "RFC 3584: Coexistence between Version 1, Version 2, and Version 3 of the Internet-standard Network Management Framework. SNMP-COMMUNITY-MIB.snmpTargetAddrTMask"; } } } } leaf-list tag { type snmp:tag-value; description "List of tag values used to select target addresses."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetAddrTagList"; } leaf timeout { type uint32; units "0.01 seconds"; default 1500; description "Needed only if this target can receive InformRequest-PDUs."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetAddrTimeout"; } leaf retries { type uint8; default 3; description "Needed only if this target can receive InformRequest-PDUs."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetAddrRetryCount"; } leaf target-params { type snmp:identifier; mandatory true; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetAddrParams"; } } list target-params { key name; description "List of target parameters."; reference "RFC 3413: Simple Network Management Protocol (SNMP). Applications. SNMP-TARGET-MIB.snmpTargetParamsTable"; leaf name { type snmp:identifier; } choice params { description "This choice is augmented with case nodes containing configuration parameters specific to the security model."; } } } } yuma123_2.14/netconf/modules/ietf/ietf-l3vpn-svc@2017-01-27.yang0000664000175000017500000014775014770023131023712 0ustar vladimirvladimirmodule ietf-l3vpn-svc { namespace "urn:ietf:params:xml:ns:yang:ietf-l3vpn-svc"; prefix l3vpn-svc; import ietf-inet-types { prefix inet; } import ietf-yang-types { prefix yang; } organization "IETF L3SM Working Group"; contact "WG List: Editor: L3SM WG Chairs: Adrian Farrel, Qin Wu "; description "This YANG module defines a generic service configuration model for Layer 3 VPNs. This model is common across all vendor implementations."; revision 2017-01-27 { description "Initial document."; reference "RFC 8049."; } /* Features */ feature cloud-access { description "Allows the VPN to connect to a CSP."; } feature multicast { description "Enables multicast capabilities in a VPN."; } feature ipv4 { description "Enables IPv4 support in a VPN."; } feature ipv6 { description "Enables IPv6 support in a VPN."; } feature carrierscarrier { description "Enables support of CsC."; } feature extranet-vpn { description "Enables support of extranet VPNs."; } feature site-diversity { description "Enables support of site diversity constraints."; } feature encryption { description "Enables support of encryption."; } feature qos { description "Enables support of classes of services."; } feature qos-custom { description "Enables support of the custom QoS profile."; } feature rtg-bgp { description "Enables support of the BGP routing protocol."; } feature rtg-rip { description "Enables support of the RIP routing protocol."; } feature rtg-ospf { description "Enables support of the OSPF routing protocol."; } feature rtg-ospf-sham-link { description "Enables support of OSPF sham links."; } feature rtg-vrrp { description "Enables support of the VRRP routing protocol."; } feature fast-reroute { description "Enables support of Fast Reroute."; } feature bfd { description "Enables support of BFD."; } feature always-on { description "Enables support of the 'always-on' access constraint."; } feature requested-type { description "Enables support of the 'requested-type' access constraint."; } feature bearer-reference { description "Enables support of the 'bearer-reference' access constraint."; } /* Typedefs */ typedef svc-id { type string; description "Defines a type of service component identifier."; } typedef template-id { type string; description "Defines a type of service template identifier."; } typedef address-family { type enumeration { enum ipv4 { description "IPv4 address family."; } enum ipv6 { description "IPv6 address family."; } } description "Defines a type for the address family."; } /* Identities */ identity site-network-access-type { description "Base identity for site-network-access type."; } identity point-to-point { base site-network-access-type; description "Identity for point-to-point connection."; } identity multipoint { base site-network-access-type; description "Identity for multipoint connection. Example: Ethernet broadcast segment."; } identity placement-diversity { description "Base identity for site placement constraints."; } identity bearer-diverse { base placement-diversity; description "Identity for bearer diversity. The bearers should not use common elements."; } identity pe-diverse { base placement-diversity; description "Identity for PE diversity."; } identity pop-diverse { base placement-diversity; description "Identity for POP diversity."; } identity linecard-diverse { base placement-diversity; description "Identity for linecard diversity."; } identity same-pe { base placement-diversity; description "Identity for having sites connected on the same PE."; } identity same-bearer { base placement-diversity; description "Identity for having sites connected using the same bearer."; } identity customer-application { description "Base identity for customer application."; } identity web { base customer-application; description "Identity for Web application (e.g., HTTP, HTTPS)."; } identity mail { base customer-application; description "Identity for mail application."; } identity file-transfer { base customer-application; description "Identity for file transfer application (e.g., FTP, SFTP)."; } identity database { base customer-application; description "Identity for database application."; } identity social { base customer-application; description "Identity for social-network application."; } identity games { base customer-application; description "Identity for gaming application."; } identity p2p { base customer-application; description "Identity for peer-to-peer application."; } identity network-management { base customer-application; description "Identity for management application (e.g., Telnet, syslog, SNMP)."; } identity voice { base customer-application; description "Identity for voice application."; } identity video { base customer-application; description "Identity for video conference application."; } identity site-vpn-flavor { description "Base identity for the site VPN service flavor."; } identity site-vpn-flavor-single { base site-vpn-flavor; description "Base identity for the site VPN service flavor. Used when the site belongs to only one VPN."; } identity site-vpn-flavor-multi { base site-vpn-flavor; description "Base identity for the site VPN service flavor. Used when a logical connection of a site belongs to multiple VPNs."; } identity site-vpn-flavor-sub { base site-vpn-flavor; description "Base identity for the site VPN service flavor. Used when a site has multiple logical connections. Each connection may belong to different multiple VPNs."; } identity site-vpn-flavor-nni { base site-vpn-flavor; description "Base identity for the site VPN service flavor. Used to describe an NNI option A connection."; } identity management { description "Base identity for site management scheme."; } identity co-managed { base management; description "Base identity for co-managed site."; } identity customer-managed { base management; description "Base identity for customer-managed site."; } identity provider-managed { base management; description "Base identity for provider-managed site."; } identity address-allocation-type { description "Base identity for address-allocation-type for PE-CE link."; } identity provider-dhcp { base address-allocation-type; description "Provider network provides DHCP service to customer."; } identity provider-dhcp-relay { base address-allocation-type; description "Provider network provides DHCP relay service to customer."; } identity provider-dhcp-slaac { base address-allocation-type; description "Provider network provides DHCP service to customer, as well as SLAAC."; } identity static-address { base address-allocation-type; description "Provider-to-customer addressing is static."; } identity slaac { base address-allocation-type; description "Use IPv6 SLAAC."; } identity site-role { description "Base identity for site type."; } identity any-to-any-role { base site-role; description "Site in an any-to-any IP VPN."; } identity spoke-role { base site-role; description "Spoke site in a Hub-and-Spoke IP VPN."; } identity hub-role { base site-role; description "Hub site in a Hub-and-Spoke IP VPN."; } identity vpn-topology { description "Base identity for VPN topology."; } identity any-to-any { base vpn-topology; description "Identity for any-to-any VPN topology."; } identity hub-spoke { base vpn-topology; description "Identity for Hub-and-Spoke VPN topology."; } identity hub-spoke-disjoint { base vpn-topology; description "Identity for Hub-and-Spoke VPN topology where Hubs cannot communicate with each other."; } identity multicast-tree-type { description "Base identity for multicast tree type."; } identity ssm-tree-type { base multicast-tree-type; description "Identity for SSM tree type."; } identity asm-tree-type { base multicast-tree-type; description "Identity for ASM tree type."; } identity bidir-tree-type { base multicast-tree-type; description "Identity for bidirectional tree type."; } identity multicast-rp-discovery-type { description "Base identity for RP discovery type."; } identity auto-rp { base multicast-rp-discovery-type; description "Base identity for Auto-RP discovery type."; } identity static-rp { base multicast-rp-discovery-type; description "Base identity for static type."; } identity bsr-rp { base multicast-rp-discovery-type; description "Base identity for BSR discovery type."; } identity routing-protocol-type { description "Base identity for routing protocol type."; } identity ospf { base routing-protocol-type; description "Identity for OSPF protocol type."; } identity bgp { base routing-protocol-type; description "Identity for BGP protocol type."; } identity static { base routing-protocol-type; description "Identity for static routing protocol type."; } identity rip { base routing-protocol-type; description "Identity for RIP protocol type."; } identity vrrp { base routing-protocol-type; description "Identity for VRRP protocol type. This is to be used when LANs are directly connected to PE routers."; } identity direct { base routing-protocol-type; description "Identity for direct protocol type."; } identity protocol-type { description "Base identity for protocol field type."; } identity tcp { base protocol-type; description "TCP protocol type."; } identity udp { base protocol-type; description "UDP protocol type."; } identity icmp { base protocol-type; description "ICMP protocol type."; } identity icmp6 { base protocol-type; description "ICMPv6 protocol type."; } identity gre { base protocol-type; description "GRE protocol type."; } identity ipip { base protocol-type; description "IP-in-IP protocol type."; } identity hop-by-hop { base protocol-type; description "Hop-by-Hop IPv6 header type."; } identity routing { base protocol-type; description "Routing IPv6 header type."; } identity esp { base protocol-type; description "ESP header type."; } identity ah { base protocol-type; description "AH header type."; } /* Groupings */ grouping vpn-service-cloud-access { container cloud-accesses { if-feature cloud-access; list cloud-access { key cloud-identifier; leaf cloud-identifier { type string; description "Identification of cloud service. Local administration meaning."; } choice list-flavor { case permit-any { leaf permit-any { type empty; description "Allows all sites."; } } case deny-any-except { leaf-list permit-site { type leafref { path "/l3vpn-svc/sites/site/site-id"; } description "Site ID to be authorized."; } } case permit-any-except { leaf-list deny-site { type leafref { path "/l3vpn-svc/sites/site/site-id"; } description "Site ID to be denied."; } } description "Choice for cloud access policy."; } container authorized-sites { list authorized-site { key site-id; leaf site-id { type leafref { path "/l3vpn-svc/sites/site/site-id"; } description "Site ID."; } description "List of authorized sites."; } description "Configuration of authorized sites."; } container denied-sites { list denied-site { key site-id; leaf site-id { type leafref { path "/l3vpn-svc/sites/site/site-id"; } description "Site ID."; } description "List of denied sites."; } description "Configuration of denied sites."; } container address-translation { container nat44 { leaf enabled { type boolean; default false; description "Controls whether or not address translation is required."; } leaf nat44-customer-address { type inet:ipv4-address; must "../enabled = 'true'" { description "Applicable only if address translation is enabled."; } description "Address to be used for translation. This is to be used if the customer is providing the address."; } description "IPv4-to-IPv4 translation."; } description "Container for NAT."; } description "Cloud access configuration."; } description "Container for cloud access configurations."; } description "Grouping for VPN cloud definition."; } grouping multicast-rp-group-cfg { choice group-format { case startend { leaf group-start { type inet:ip-address; description "First group address."; } leaf group-end { type inet:ip-address; description "Last group address."; } } case singleaddress { leaf group-address { type inet:ip-address; description "Group address."; } } description "Choice for group format."; } description "Definition of groups for RP-to-group mapping."; } grouping vpn-service-multicast { container multicast { if-feature multicast; leaf enabled { type boolean; default false; description "Enables multicast."; } container customer-tree-flavors { leaf-list tree-flavor { type identityref { base multicast-tree-type; } description "Type of tree to be used."; } description "Type of trees used by customer."; } container rp { container rp-group-mappings { list rp-group-mapping { key id; leaf id { type uint16; description "Unique identifier for the mapping."; } container provider-managed { leaf enabled { type boolean; default false; description "Set to true if the RP must be a provider-managed node. Set to false if it is a customer-managed node."; } leaf rp-redundancy { when "../enabled = 'true'" { description "Relevant when the RP is provider-managed."; } type boolean; default false; description "If true, a redundancy mechanism for the RP is required."; } leaf optimal-traffic-delivery { when "../enabled = 'true'" { description "Relevant when the RP is provider-managed."; } type boolean; default false; description "If true, the SP must ensure that traffic uses an optimal path."; } description "Parameters for a provider-managed RP."; } leaf rp-address { when "../provider-managed/enabled = 'false'" { description "Relevant when the RP is provider-managed."; } type inet:ip-address; description "Defines the address of the RP. Used if the RP is customer-managed."; } container groups { list group { key id; leaf id { type uint16; description "Identifier for the group."; } uses multicast-rp-group-cfg; description "List of groups."; } description "Multicast groups associated with the RP."; } description "List of RP-to-group mappings."; } description "RP-to-group mappings."; } container rp-discovery { leaf rp-discovery-type { type identityref { base multicast-rp-discovery-type; } default static-rp; description "Type of RP discovery used."; } container bsr-candidates { when "../rp-discovery-type = 'bsr-rp'" { description "Only applicable if discovery type is BSR-RP."; } leaf-list bsr-candidate-address { type inet:ip-address; description "Address of BSR candidate."; } description "Customer BSR candidate's address."; } description "RP discovery parameters."; } description "RP parameters."; } description "Multicast global parameters for the VPN service."; } description "Grouping for multicast VPN definition."; } grouping vpn-service-mpls { leaf carrierscarrier { if-feature carrierscarrier; type boolean; default false; description "The VPN is using CsC, and so MPLS is required."; } description "Grouping for MPLS CsC definition."; } grouping customer-location-info { container locations { list location { key location-id; leaf location-id { type svc-id; description "Identifier for a particular location."; } leaf address { type string; description "Address (number and street) of the site."; } leaf postal-code { type string; description "Postal code of the site."; } leaf state { type string; description "State of the site. This leaf can also be used to describe a region for a country that does not have states."; } leaf city { type string; description "City of the site."; } leaf country-code { type string { pattern '[A-Z]{2}'; } description "Country of the site. Expressed as ISO ALPHA-2 code."; } description "Location of the site."; } description "List of locations for the site."; } description "This grouping defines customer location parameters."; } grouping site-group { container groups { list group { key group-id; leaf group-id { type string; description "Group-id the site belongs to."; } description "List of group-ids."; } description "Groups the site or site-network-access belongs to."; } description "Grouping definition to assign group-ids to site or site-network-access."; } grouping site-diversity { container site-diversity { if-feature site-diversity; uses site-group; description "Diversity constraint type. All site-network-accesses will inherit the group values defined here."; } description "This grouping defines site diversity parameters."; } grouping access-diversity { container access-diversity { if-feature site-diversity; uses site-group; container constraints { list constraint { key constraint-type; leaf constraint-type { type identityref { base placement-diversity; } description "Diversity constraint type."; } container target { choice target-flavor { case id { list group { key group-id; leaf group-id { type string; description "The constraint will be applied against this particular group-id."; } description "List of groups."; } } case all-accesses { leaf all-other-accesses { type empty; description "The constraint will be applied against all other site network accesses of this site."; } } case all-groups { leaf all-other-groups { type empty; description "The constraint will be applied against all other groups managed by the customer."; } } description "Choice for the group definition."; } description "The constraint will be applied against this list of groups."; } description "List of constraints."; } description "Placement constraints for this site network access."; } description "Diversity parameters."; } description "This grouping defines access diversity parameters."; } grouping operational-requirements { leaf requested-site-start { type yang:date-and-time; description "Optional leaf indicating requested date and time when the service at a particular site is expected to start."; } leaf requested-site-stop { type yang:date-and-time; description "Optional leaf indicating requested date and time when the service at a particular site is expected to stop."; } description "This grouping defines some operational parameters."; } grouping operational-requirements-ops { leaf actual-site-start { type yang:date-and-time; config false; description "Optional leaf indicating actual date and time when the service at a particular site actually started."; } leaf actual-site-stop { type yang:date-and-time; config false; description "Optional leaf indicating actual date and time when the service at a particular site actually stopped."; } description "This grouping defines some operational parameters."; } grouping flow-definition { container match-flow { leaf dscp { type inet:dscp; description "DSCP value."; } leaf dot1p { type uint8 { range "0..7"; } description "802.1p matching."; } leaf ipv4-src-prefix { type inet:ipv4-prefix; description "Match on IPv4 src address."; } leaf ipv6-src-prefix { type inet:ipv6-prefix; description "Match on IPv6 src address."; } leaf ipv4-dst-prefix { type inet:ipv4-prefix; description "Match on IPv4 dst address."; } leaf ipv6-dst-prefix { type inet:ipv6-prefix; description "Match on IPv6 dst address."; } leaf l4-src-port { type inet:port-number; description "Match on Layer 4 src port."; } leaf-list target-sites { type svc-id; description "Identify a site as traffic destination."; } container l4-src-port-range { leaf lower-port { type inet:port-number; description "Lower boundary for port."; } leaf upper-port { type inet:port-number; must ". >= ../lower-port" { description "Upper boundary must be higher than lower boundary."; } description "Upper boundary for port."; } description "Match on Layer 4 src port range."; } leaf l4-dst-port { type inet:port-number; description "Match on Layer 4 dst port."; } container l4-dst-port-range { leaf lower-port { type inet:port-number; description "Lower boundary for port."; } leaf upper-port { type inet:port-number; must ". >= ../lower-port" { description "Upper boundary must be higher than lower boundary."; } description "Upper boundary for port."; } description "Match on Layer 4 dst port range."; } leaf protocol-field { type union { type uint8; type identityref { base protocol-type; } } description "Match on IPv4 protocol or IPv6 Next Header field."; } description "Describes flow-matching criteria."; } description "Flow definition based on criteria."; } grouping site-service-basic { leaf svc-input-bandwidth { type uint32; units bps; description "From the PE's perspective, the service input bandwidth of the connection."; } leaf svc-output-bandwidth { type uint32; units bps; description "From the PE's perspective, the service output bandwidth of the connection."; } leaf svc-mtu { type uint16; units bytes; description "MTU at service level. If the service is IP, it refers to the IP MTU."; } description "Defines basic service parameters for a site."; } grouping site-protection { container traffic-protection { if-feature fast-reroute; leaf enabled { type boolean; default false; description "Enables traffic protection of access link."; } description "Fast Reroute service parameters for the site."; } description "Defines protection service parameters for a site."; } grouping site-service-mpls { container carrierscarrier { if-feature carrierscarrier; leaf signalling-type { type enumeration { enum "ldp" { description "Use LDP as the signalling protocol between the PE and the CE."; } enum "bgp" { description "Use BGP (as per RFC 3107) as the signalling protocol between the PE and the CE. In this case, BGP must also be configured as the routing protocol."; } } description "MPLS signalling type."; } description "This container is used when the customer provides MPLS-based services. This is used in the case of CsC."; } description "Defines MPLS service parameters for a site."; } grouping site-service-qos-profile { container qos { if-feature qos; container qos-classification-policy { list rule { key id; ordered-by user; leaf id { type uint16; description "ID of the rule."; } choice match-type { case match-flow { uses flow-definition; } case match-application { leaf match-application { type identityref { base customer-application; } description "Defines the application to match."; } } description "Choice for classification."; } leaf target-class-id { type string; description "Identification of the class of service. This identifier is internal to the administration."; } description "List of marking rules."; } description "Configuration of the traffic classification policy."; } container qos-profile { choice qos-profile { description "Choice for QoS profile. Can be standard profile or custom."; case standard { leaf profile { type string; description "QoS profile to be used."; } } case custom { container classes { if-feature qos-custom; list class { key class-id; leaf class-id { type string; description "Identification of the class of service. This identifier is internal to the administration."; } leaf rate-limit { type uint8; units percent; description "To be used if the class must be rate-limited. Expressed as percentage of the service bandwidth."; } container latency { choice flavor { case lowest { leaf use-lowest-latency { type empty; description "The traffic class should use the path with the lowest latency."; } } case boundary { leaf latency-boundary { type uint16; units msec; description "The traffic class should use a path with a defined maximum latency."; } } description "Latency constraint on the traffic class."; } description "Latency constraint on the traffic class."; } container jitter { choice flavor { case lowest { leaf use-lowest-jitter { type empty; description "The traffic class should use the path with the lowest jitter."; } } case boundary { leaf latency-boundary { type uint32; units usec; description "The traffic class should use a path with a defined maximum jitter."; } } description "Jitter constraint on the traffic class."; } description "Jitter constraint on the traffic class."; } container bandwidth { leaf guaranteed-bw-percent { type uint8; units percent; description "To be used to define the guaranteed bandwidth as a percentage of the available service bandwidth."; } leaf end-to-end { type empty; description "Used if the bandwidth reservation must be done on the MPLS network too."; } description "Bandwidth constraint on the traffic class."; } description "List of classes of services."; } description "Container for list of classes of services."; } } } description "QoS profile configuration."; } description "QoS configuration."; } description "This grouping defines QoS parameters for a site."; } grouping site-security-authentication { container authentication { description "Authentication parameters."; } description "This grouping defines authentication parameters for a site."; } grouping site-security-encryption { container encryption { if-feature encryption; leaf enabled { type boolean; default false; description "If true, access encryption is required."; } leaf layer { type enumeration { enum layer2 { description "Encryption will occur at Layer 2."; } enum layer3 { description "Encryption will occur at Layer 3. For example, IPsec may be used."; } } mandatory true; description "Layer on which encryption is applied."; } container encryption-profile { choice profile { case provider-profile { leaf profile-name { type string; description "Name of the SP profile to be applied."; } } case customer-profile { leaf algorithm { type string; description "Encryption algorithm to be used."; } choice key-type { case psk { leaf preshared-key { type string; description "Key coming from customer."; } } case pki { } description "Type of keys to be used."; } } description "Choice of profile."; } description "Profile of encryption to be applied."; } description "Encryption parameters."; } description "This grouping defines encryption parameters for a site."; } grouping site-attachment-bearer { container bearer { container requested-type { if-feature requested-type; leaf requested-type { type string; description "Type of requested bearer: Ethernet, DSL, Wireless, etc. Operator specific."; } leaf strict { type boolean; default false; description "Defines whether requested-type is a preference or a strict requirement."; } description "Container for requested-type."; } leaf always-on { if-feature always-on; type boolean; default true; description "Request for an always-on access type. For example, this could mean no dial access type."; } leaf bearer-reference { if-feature bearer-reference; type string; description "This is an internal reference for the SP."; } description "Bearer-specific parameters. To be augmented."; } description "Defines physical properties of a site attachment."; } grouping site-routing { container routing-protocols { list routing-protocol { key type; leaf type { type identityref { base routing-protocol-type; } description "Type of routing protocol."; } container ospf { when "../type = 'ospf'" { description "Only applies when protocol is OSPF."; } if-feature rtg-ospf; leaf-list address-family { type address-family; description "Address family to be activated."; } leaf area-address { type yang:dotted-quad; description "Area address."; } leaf metric { type uint16; description "Metric of the PE-CE link."; } container sham-links { if-feature rtg-ospf-sham-link; list sham-link { key target-site; leaf target-site { type svc-id; description "Target site for the sham link connection. The site is referred to by its ID."; } leaf metric { type uint16; description "Metric of the sham link."; } description "Creates a sham link with another site."; } description "List of sham links."; } description "OSPF-specific configuration."; } container bgp { when "../type = 'bgp'" { description "Only applies when protocol is BGP."; } if-feature rtg-bgp; leaf autonomous-system { type uint32; description "AS number."; } leaf-list address-family { type address-family; description "Address family to be activated."; } description "BGP-specific configuration."; } container static { when "../type = 'static'" { description "Only applies when protocol is static."; } container cascaded-lan-prefixes { list ipv4-lan-prefixes { if-feature ipv4; key "lan next-hop"; leaf lan { type inet:ipv4-prefix; description "LAN prefixes."; } leaf lan-tag { type string; description "Internal tag to be used in VPN policies."; } leaf next-hop { type inet:ipv4-address; description "Next-hop address to use on the customer side."; } description "List of LAN prefixes for the site."; } list ipv6-lan-prefixes { if-feature ipv6; key "lan next-hop"; leaf lan { type inet:ipv6-prefix; description "LAN prefixes."; } leaf lan-tag { type string; description "Internal tag to be used in VPN policies."; } leaf next-hop { type inet:ipv6-address; description "Next-hop address to use on the customer side."; } description "List of LAN prefixes for the site."; } description "LAN prefixes from the customer."; } description "Configuration specific to static routing."; } container rip { when "../type = 'rip'" { description "Only applies when protocol is RIP."; } if-feature rtg-rip; leaf-list address-family { type address-family; description "Address family to be activated."; } description "Configuration specific to RIP routing."; } container vrrp { when "../type = 'vrrp'" { description "Only applies when protocol is VRRP."; } if-feature rtg-vrrp; leaf-list address-family { type address-family; description "Address family to be activated."; } description "Configuration specific to VRRP routing."; } description "List of routing protocols used on the site. This list can be augmented."; } description "Defines routing protocols."; } description "Grouping for routing protocols."; } grouping site-attachment-ip-connection { container ip-connection { container ipv4 { if-feature ipv4; leaf address-allocation-type { type identityref { base address-allocation-type; } default "static-address"; description "Defines how addresses are allocated."; } leaf number-of-dynamic-address { when "../address-allocation-type = 'provider-dhcp'" { description "Only applies when addresses are allocated by DHCP."; } type uint8; default 1; description "Describes the number of IP addresses the customer requires."; } container dhcp-relay { when "../address-allocation-type = 'provider-dhcp-relay'" { description "Only applies when provider is required to implement DHCP relay function."; } container customer-dhcp-servers { leaf-list server-ip-address { type inet:ipv4-address; description "IP address of customer DHCP server."; } description "Container for list of customer DHCP servers."; } description "DHCP relay provided by operator."; } container addresses { when "../address-allocation-type = 'static-address'" { description "Only applies when protocol allocation type is static."; } leaf provider-address { type inet:ipv4-address; description "Address of provider side."; } leaf customer-address { type inet:ipv4-address; description "Address of customer side."; } leaf mask { type uint8 { range "0..31"; } description "Subnet mask expressed in bits."; } description "Describes IP addresses used."; } description "IPv4-specific parameters."; } container ipv6 { if-feature ipv6; leaf address-allocation-type { type identityref { base address-allocation-type; } default "static-address"; description "Defines how addresses are allocated."; } leaf number-of-dynamic-address { when "../address-allocation-type = 'provider-dhcp' "+ "or ../address-allocation-type "+ "= 'provider-dhcp-slaac'" { description "Only applies when addresses are allocated by DHCP."; } type uint8; default 1; description "Describes the number of IP addresses the customer requires."; } container dhcp-relay { when "../address-allocation-type = 'provider-dhcp-relay'" { description "Only applies when provider is required to implement DHCP relay function."; } container customer-dhcp-servers { leaf-list server-ip-address { type inet:ipv6-address; description "IP address of customer DHCP server."; } description "Container for list of customer DHCP servers."; } description "DHCP relay provided by operator."; } container addresses { when "../address-allocation-type = 'static-address'" { description "Only applies when protocol allocation type is static."; } leaf provider-address { type inet:ipv6-address; description "Address of provider side."; } leaf customer-address { type inet:ipv6-address; description "Address of customer side."; } leaf mask { type uint8 { range "0..127"; } description "Subnet mask expressed in bits."; } description "Describes IP addresses used."; } description "IPv6-specific parameters."; } container oam { container bfd { if-feature bfd; leaf enabled { type boolean; default false; description "BFD activation."; } choice holdtime { case profile { leaf profile-name { type string; description "Well-known SP profile."; } description "Well-known SP profile."; } case fixed { leaf fixed-value { type uint32; units msec; description "Expected holdtime expressed in msec."; } } description "Choice for holdtime flavor."; } description "Container for BFD."; } description "Defines the OAM mechanisms used on the connection."; } description "Defines connection parameters."; } description "This grouping defines IP connection parameters."; } grouping site-service-multicast { container multicast { if-feature multicast; leaf multicast-site-type { type enumeration { enum receiver-only { description "The site only has receivers."; } enum source-only { description "The site only has sources."; } enum source-receiver { description "The site has both sources and receivers."; } } default "source-receiver"; description "Type of multicast site."; } container multicast-address-family { leaf ipv4 { if-feature ipv4; type boolean; default true; description "Enables IPv4 multicast."; } leaf ipv6 { if-feature ipv6; type boolean; default false; description "Enables IPv6 multicast."; } description "Defines protocol to carry multicast."; } leaf protocol-type { type enumeration { enum host { description "Hosts are directly connected to the provider network. Host protocols such as IGMP or MLD are required."; } enum router { description "Hosts are behind a customer router. PIM will be implemented."; } enum both { description "Some hosts are behind a customer router, and some others are directly connected to the provider network. Both host and routing protocols must be used. Typically, IGMP and PIM will be implemented."; } } default "both"; description "Multicast protocol type to be used with the customer site."; } description "Multicast parameters for the site."; } description "Multicast parameters for the site."; } grouping site-management { container management { leaf type { type identityref { base management; } description "Management type of the connection."; } description "Management configuration."; } description "Management parameters for the site."; } grouping site-devices { container devices { must "/l3vpn-svc/sites/site/management/type = "+ "'provider-managed' or "+ "/l3vpn-svc/sites/site/management/type = "+ "'co-managed'" { description "Applicable only for provider-managed or co-managed device."; } list device { key device-id; leaf device-id { type svc-id; description "Identifier for the device."; } leaf location { type leafref { path "/l3vpn-svc/sites/site/locations/"+ "location/location-id"; } description "Location of the device."; } container management { must "/l3vpn-svc/sites/site/management/type"+ "= 'co-managed'" { description "Applicable only for co-managed device."; } leaf address-family { type address-family; description "Address family used for management."; } leaf address { type inet:ip-address; description "Management address."; } description "Management configuration. Applicable only for co-managed device."; } description "Device configuration."; } description "List of devices requested by customer."; } description "Grouping for device allocation."; } grouping site-vpn-flavor { leaf site-vpn-flavor { type identityref { base site-vpn-flavor; } default site-vpn-flavor-single; description "Defines whether the site is, for example, a single VPN site or a multiVPN."; } description "Grouping for site VPN flavor."; } grouping site-vpn-policy { container vpn-policies { list vpn-policy { key vpn-policy-id; leaf vpn-policy-id { type svc-id; description "Unique identifier for the VPN policy."; } list entries { key id; leaf id { type svc-id; description "Unique identifier for the policy entry."; } container filter { choice lan { case prefixes { leaf-list ipv4-lan-prefix { if-feature ipv4; type inet:ipv4-prefix; description "List of IPv4 prefixes to be matched."; } leaf-list ipv6-lan-prefix { if-feature ipv6; type inet:ipv6-prefix; description "List of IPv6 prefixes to be matched."; } } case lan-tag { leaf-list lan-tag { type string; description "List of 'lan-tag' items to be matched."; } } description "Choice of ways to do LAN matching."; } description "If used, it permits the splitting of site LANs among multiple VPNs. If no filter is used, all the LANs will be part of the same VPNs with the same role."; } container vpn { leaf vpn-id { type leafref { path "/l3vpn-svc/vpn-services/"+ "vpn-service/vpn-id"; } mandatory true; description "Reference to an IP VPN."; } leaf site-role { type identityref { base site-role; } default any-to-any-role; description "Role of the site in the IP VPN."; } description "List of VPNs the LAN is associated with."; } description "List of entries for export policy."; } description "List of VPN policies."; } description "VPN policy."; } description "VPN policy parameters for the site."; } grouping site-maximum-routes { container maximum-routes { list address-family { key af; leaf af { type address-family; description "Address family."; } leaf maximum-routes { type uint32; description "Maximum prefixes the VRF can accept for this address family."; } description "List of address families."; } description "Defines 'maximum-routes' for the VRF."; } description "Defines 'maximum-routes' for the site."; } grouping site-security { container security { uses site-security-authentication; uses site-security-encryption; description "Site-specific security parameters."; } description "Grouping for security parameters."; } grouping site-service { container service { uses site-service-qos-profile; uses site-service-mpls; uses site-service-multicast; description "Service parameters on the attachment."; } description "Grouping for service parameters."; } grouping site-network-access-service { container service { uses site-service-basic; uses site-service-qos-profile; uses site-service-mpls; uses site-service-multicast; description "Service parameters on the attachment."; } description "Grouping for service parameters."; } grouping vpn-extranet { container extranet-vpns { if-feature extranet-vpn; list extranet-vpn { key vpn-id; leaf vpn-id { type svc-id; description "Identifies the target VPN."; } leaf local-sites-role { type identityref { base site-role; } default any-to-any-role; description "This describes the role of the local sites in the target VPN topology."; } description "List of extranet VPNs the local VPN is attached to."; } description "Container for extranet VPN configuration."; } description "Grouping for extranet VPN configuration. This provides an easy way to interconnect all sites from two VPNs."; } grouping site-attachment-availability { container availability { leaf access-priority { type uint32; default 1; description "Defines the priority for the access. The higher the access-priority value, the higher the preference of the access will be."; } description "Availability parameters (used for multihoming)."; } description "Defines availability parameters for a site."; } grouping access-vpn-policy { container vpn-attachment { choice attachment-flavor { case vpn-policy-id { leaf vpn-policy-id { type leafref { path "/l3vpn-svc/sites/site/"+ "vpn-policies/vpn-policy/"+ "vpn-policy-id"; } description "Reference to a VPN policy."; } } case vpn-id { leaf vpn-id { type leafref { path "/l3vpn-svc/vpn-services"+ "/vpn-service/vpn-id"; } description "Reference to a VPN."; } leaf site-role { type identityref { base site-role; } default any-to-any-role; description "Role of the site in the IP VPN."; } } mandatory true; description "Choice for VPN attachment flavor."; } description "Defines VPN attachment of a site."; } description "Defines the VPN attachment rules for a site's logical access."; } grouping vpn-svc-cfg { leaf vpn-id { type svc-id; description "VPN identifier. Local administration meaning."; } leaf customer-name { type string; description "Name of the customer."; } leaf vpn-service-topology { type identityref { base vpn-topology; } default "any-to-any"; description "VPN service topology."; } uses vpn-service-cloud-access; uses vpn-service-multicast; uses vpn-service-mpls; uses vpn-extranet; description "Grouping for VPN service configuration."; } grouping site-top-level-cfg { uses operational-requirements; uses customer-location-info; uses site-devices; uses site-diversity; uses site-management; uses site-vpn-policy; uses site-vpn-flavor; uses site-maximum-routes; uses site-security; uses site-service; uses site-protection; uses site-routing; description "Grouping for site top-level configuration."; } grouping site-network-access-top-level-cfg { leaf site-network-access-type { type identityref { base site-network-access-type; } default "point-to-point"; description "Describes the type of connection, e.g., point-to-point or multipoint."; } choice location-flavor { case location { when "/l3vpn-svc/sites/site/management/type = "+ "'customer-managed'" { description "Applicable only for customer-managed device."; } leaf location-reference { type leafref { path "/l3vpn-svc/sites/site/locations/"+ "location/location-id"; } description "Location of the site-network-access."; } } case device { when "/l3vpn-svc/sites/site/management/type = "+ "'provider-managed' or "+ "/l3vpn-svc/sites/site/management/type = "+ "'co-managed'" { description "Applicable only for provider-managed or co-managed device."; } leaf device-reference { type leafref { path "/l3vpn-svc/sites/site/devices/"+ "device/device-id"; } description "Identifier of CE to use."; } } mandatory true; description "Choice of how to describe the site's location."; } uses access-diversity; uses site-attachment-bearer; uses site-attachment-ip-connection; uses site-security; uses site-network-access-service; uses site-routing; uses site-attachment-availability; uses access-vpn-policy; description "Grouping for site network access top-level configuration."; } /* Main blocks */ container l3vpn-svc { container vpn-services { list vpn-service { key vpn-id; uses vpn-svc-cfg; description "List of VPN services."; } description "Top-level container for the VPN services."; } container sites { list site { key site-id; leaf site-id { type svc-id; description "Identifier of the site."; } uses site-top-level-cfg; uses operational-requirements-ops; container site-network-accesses { list site-network-access { key site-network-access-id; leaf site-network-access-id { type svc-id; description "Identifier for the access."; } uses site-network-access-top-level-cfg; description "List of accesses for a site."; } description "List of accesses for a site."; } description "List of sites."; } description "Container for sites."; } description "Main container for L3VPN service configuration."; } } yuma123_2.14/netconf/modules/ietf/ietf-complex-types@2011-03-15.yang0000664000175000017500000000556414770023131024655 0ustar vladimirvladimirmodule ietf-complex-types { namespace "urn:ietf:params:xml:ns:yang:ietf-complex-types"; prefix "ct"; organization "NETMOD WG"; contact "Editor: Bernd Linowski Editor: Mehmet Ersue Editor: Siarhei Kuryla "; description "YANG extensions for complex types and typed instance identifiers. Copyright (c) 2011 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 6095; see the RFC itself for full legal notices."; revision 2011-03-15 { description "Initial revision."; } extension complex-type { description "Defines a complex-type."; reference "Section 2.2, complex-type Extension Statement"; argument type-identifier { yin-element true; } } extension extends { description "Defines the base type of a complex-type."; reference "Section 2.5, extends Extension Statement"; argument base-type-identifier { yin-element true; } } extension abstract { description "Makes the complex-type abstract."; reference "Section 2.6, abstract Extension Statement"; argument status; } extension instance { description "Declares an instance of the given complex type."; reference "Section 2.3, instance Extension Statement"; argument ct-instance-identifier { yin-element true; } } extension instance-list { description "Declares a list of instances of the given complex type"; reference "Section 2.4, instance-list Extension Statement"; argument ct-instance-identifier { yin-element true; } } extension instance-type { description "Tells to which type instance the instance identifier refers."; reference "Section 3.2, instance-type Extension Statement"; argument target-type-identifier { yin-element true; } } feature complex-types { description "Indicates that the server supports complex types and instance identifiers."; } } yuma123_2.14/netconf/modules/ietf/ietf-yang-types@2010-09-24.yang0000664000175000017500000003631114770023131024143 0ustar vladimirvladimirmodule ietf-yang-types { namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; prefix "yang"; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: David Partain WG Chair: David Kessens Editor: Juergen Schoenwaelder "; description "This module contains a collection of generally useful derived YANG data types. Copyright (c) 2010 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 6021; see the RFC itself for full legal notices."; revision 2010-09-24 { description "Initial revision."; reference "RFC 6021: Common YANG Data Types"; } /*** collection of counter and gauge types ***/ typedef counter32 { type uint32; description "The counter32 type represents a non-negative integer that monotonically increases until it reaches a maximum value of 2^32-1 (4294967295 decimal), when it wraps around and starts increasing again from zero. Counters have no defined 'initial' value, and thus, a single value of a counter has (in general) no information content. Discontinuities in the monotonically increasing value normally occur at re-initialization of the management system, and at other times as specified in the description of a schema node using this type. If such other times can occur, for example, the creation of a schema node of type counter32 at times other than re-initialization, then a corresponding schema node should be defined, with an appropriate type, to indicate the last discontinuity. The counter32 type should not be used for configuration schema nodes. A default statement SHOULD NOT be used in combination with the type counter32. In the value set and its semantics, this type is equivalent to the Counter32 type of the SMIv2."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } typedef zero-based-counter32 { type yang:counter32; default "0"; description "The zero-based-counter32 type represents a counter32 that has the defined 'initial' value zero. A schema node of this type will be set to zero (0) on creation and will thereafter increase monotonically until it reaches a maximum value of 2^32-1 (4294967295 decimal), when it wraps around and starts increasing again from zero. Provided that an application discovers a new schema node of this type within the minimum time to wrap, it can use the 'initial' value as a delta. It is important for a management station to be aware of this minimum time and the actual time between polls, and to discard data if the actual time is too long or there is no defined minimum time. In the value set and its semantics, this type is equivalent to the ZeroBasedCounter32 textual convention of the SMIv2."; reference "RFC 4502: Remote Network Monitoring Management Information Base Version 2"; } typedef counter64 { type uint64; description "The counter64 type represents a non-negative integer that monotonically increases until it reaches a maximum value of 2^64-1 (18446744073709551615 decimal), when it wraps around and starts increasing again from zero. Counters have no defined 'initial' value, and thus, a single value of a counter has (in general) no information content. Discontinuities in the monotonically increasing value normally occur at re-initialization of the management system, and at other times as specified in the description of a schema node using this type. If such other times can occur, for example, the creation of a schema node of type counter64 at times other than re-initialization, then a corresponding schema node should be defined, with an appropriate type, to indicate the last discontinuity. The counter64 type should not be used for configuration schema nodes. A default statement SHOULD NOT be used in combination with the type counter64. In the value set and its semantics, this type is equivalent to the Counter64 type of the SMIv2."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } typedef zero-based-counter64 { type yang:counter64; default "0"; description "The zero-based-counter64 type represents a counter64 that has the defined 'initial' value zero. A schema node of this type will be set to zero (0) on creation and will thereafter increase monotonically until it reaches a maximum value of 2^64-1 (18446744073709551615 decimal), when it wraps around and starts increasing again from zero. Provided that an application discovers a new schema node of this type within the minimum time to wrap, it can use the 'initial' value as a delta. It is important for a management station to be aware of this minimum time and the actual time between polls, and to discard data if the actual time is too long or there is no defined minimum time. In the value set and its semantics, this type is equivalent to the ZeroBasedCounter64 textual convention of the SMIv2."; reference "RFC 2856: Textual Conventions for Additional High Capacity Data Types"; } typedef gauge32 { type uint32; description "The gauge32 type represents a non-negative integer, which may increase or decrease, but shall never exceed a maximum value, nor fall below a minimum value. The maximum value cannot be greater than 2^32-1 (4294967295 decimal), and the minimum value cannot be smaller than 0. The value of a gauge32 has its maximum value whenever the information being modeled is greater than or equal to its maximum value, and has its minimum value whenever the information being modeled is smaller than or equal to its minimum value. If the information being modeled subsequently decreases below (increases above) the maximum (minimum) value, the gauge32 also decreases (increases). In the value set and its semantics, this type is equivalent to the Gauge32 type of the SMIv2."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } typedef gauge64 { type uint64; description "The gauge64 type represents a non-negative integer, which may increase or decrease, but shall never exceed a maximum value, nor fall below a minimum value. The maximum value cannot be greater than 2^64-1 (18446744073709551615), and the minimum value cannot be smaller than 0. The value of a gauge64 has its maximum value whenever the information being modeled is greater than or equal to its maximum value, and has its minimum value whenever the information being modeled is smaller than or equal to its minimum value. If the information being modeled subsequently decreases below (increases above) the maximum (minimum) value, the gauge64 also decreases (increases). In the value set and its semantics, this type is equivalent to the CounterBasedGauge64 SMIv2 textual convention defined in RFC 2856"; reference "RFC 2856: Textual Conventions for Additional High Capacity Data Types"; } /*** collection of identifier related types ***/ typedef object-identifier { type string { pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + '(\.(0|([1-9]\d*)))*'; } description "The object-identifier type represents administratively assigned names in a registration-hierarchical-name tree. Values of this type are denoted as a sequence of numerical non-negative sub-identifier values. Each sub-identifier value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers are separated by single dots and without any intermediate whitespace. The ASN.1 standard restricts the value space of the first sub-identifier to 0, 1, or 2. Furthermore, the value space of the second sub-identifier is restricted to the range 0 to 39 if the first sub-identifier is 0 or 1. Finally, the ASN.1 standard requires that an object identifier has always at least two sub-identifier. The pattern captures these restrictions. Although the number of sub-identifiers is not limited, module designers should realize that there may be implementations that stick with the SMIv2 limit of 128 sub-identifiers. This type is a superset of the SMIv2 OBJECT IDENTIFIER type since it is not restricted to 128 sub-identifiers. Hence, this type SHOULD NOT be used to represent the SMIv2 OBJECT IDENTIFIER type, the object-identifier-128 type SHOULD be used instead."; reference "ISO9834-1: Information technology -- Open Systems Interconnection -- Procedures for the operation of OSI Registration Authorities: General procedures and top arcs of the ASN.1 Object Identifier tree"; } typedef object-identifier-128 { type object-identifier { pattern '\d*(\.\d*){1,127}'; } description "This type represents object-identifiers restricted to 128 sub-identifiers. In the value set and its semantics, this type is equivalent to the OBJECT IDENTIFIER type of the SMIv2."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } /*** collection of date and time related types ***/ typedef date-and-time { type string { pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + '(Z|[\+\-]\d{2}:\d{2})'; } description "The date-and-time type is a profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. The profile is defined by the date-time production in Section 5.6 of RFC 3339. The date-and-time type is compatible with the dateTime XML schema type with the following notable exceptions: (a) The date-and-time type does not allow negative years. (b) The date-and-time time-offset -00:00 indicates an unknown time zone (see RFC 3339) while -00:00 and +00:00 and Z all represent the same time zone in dateTime. (c) The canonical format (see below) of data-and-time values differs from the canonical format used by the dateTime XML schema type, which requires all times to be in UTC using the time-offset 'Z'. This type is not equivalent to the DateAndTime textual convention of the SMIv2 since RFC 3339 uses a different separator between full-date and full-time and provides higher resolution of time-secfrac. The canonical format for date-and-time values with a known time zone uses a numeric time zone offset that is calculated using the device's configured known offset to UTC time. A change of the device's offset to UTC time will cause date-and-time values to change accordingly. Such changes might happen periodically in case a server follows automatically daylight saving time (DST) time zone offset changes. The canonical format for date-and-time values with an unknown time zone (usually referring to the notion of local time) uses the time-offset -00:00."; reference "RFC 3339: Date and Time on the Internet: Timestamps RFC 2579: Textual Conventions for SMIv2 XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; } typedef timeticks { type uint32; description "The timeticks type represents a non-negative integer that represents the time, modulo 2^32 (4294967296 decimal), in hundredths of a second between two epochs. When a schema node is defined that uses this type, the description of the schema node identifies both of the reference epochs. In the value set and its semantics, this type is equivalent to the TimeTicks type of the SMIv2."; reference "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; } typedef timestamp { type yang:timeticks; description "The timestamp type represents the value of an associated timeticks schema node at which a specific occurrence happened. The specific occurrence must be defined in the description of any schema node defined using this type. When the specific occurrence occurred prior to the last time the associated timeticks attribute was zero, then the timestamp value is zero. Note that this requires all timestamp values to be reset to zero when the value of the associated timeticks attribute reaches 497+ days and wraps around to zero. The associated timeticks schema node must be specified in the description of any schema node using this type. In the value set and its semantics, this type is equivalent to the TimeStamp textual convention of the SMIv2."; reference "RFC 2579: Textual Conventions for SMIv2"; } /*** collection of generic address types ***/ typedef phys-address { type string { pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; } description "Represents media- or physical-level addresses represented as a sequence octets, each octet represented by two hexadecimal numbers. Octets are separated by colons. The canonical representation uses lowercase characters. In the value set and its semantics, this type is equivalent to the PhysAddress textual convention of the SMIv2."; reference "RFC 2579: Textual Conventions for SMIv2"; } typedef mac-address { type string { pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; } description "The mac-address type represents an IEEE 802 MAC address. The canonical representation uses lowercase characters. In the value set and its semantics, this type is equivalent to the MacAddress textual convention of the SMIv2."; reference "IEEE 802: IEEE Standard for Local and Metropolitan Area Networks: Overview and Architecture RFC 2579: Textual Conventions for SMIv2"; } /*** collection of XML specific types ***/ typedef xpath1.0 { type string; description "This type represents an XPATH 1.0 expression. When a schema node is defined that uses this type, the description of the schema node MUST specify the XPath context in which the XPath expression is evaluated."; reference "XPATH: XML Path Language (XPath) Version 1.0"; } } yuma123_2.14/netconf/modules/ietf/ietf-snmp-vacm@2014-12-10.yang0000664000175000017500000002406114770023131023734 0ustar vladimirvladimirsubmodule ietf-snmp-vacm { belongs-to ietf-snmp { prefix snmp; } include ietf-snmp-common; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This submodule contains a collection of YANG definitions for configuring the View-based Access Control Model (VACM) of SNMP. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP)"; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } typedef view-name { type snmp:identifier; description "The view-name type represents an SNMP VACM view name."; } typedef group-name { type snmp:identifier; description "The group-name type represents an SNMP VACM group name."; } augment /snmp:snmp { container vacm { description "Configuration of the View-based Access Control Model."; list group { key name; description "VACM groups. This data model has a different structure than the MIB. Groups are explicitly defined in this list, and group members are defined in the 'member' list (mapped to vacmSecurityToGroupTable), and access for the group is defined in the 'access' list (mapped to vacmAccessTable)."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmSecurityToGroupTable SNMP-VIEW-BASED-ACM-MIB.vacmAccessTable"; leaf name { type group-name; description "The name of this VACM group."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmGroupName"; } list member { key "security-name"; description "A member of this VACM group. A specific combination of security-name and security-model MUST NOT be present in more than one group."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmSecurityToGroupTable"; leaf security-name { type snmp:security-name; description "The securityName of a group member."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmSecurityName"; } leaf-list security-model { type snmp:security-model; min-elements 1; description "The security models under which this security-name is a member of this group."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmSecurityModel"; } } list access { key "context security-model security-level"; description "Definition of access right for groups."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmAccessTable"; leaf context { type snmp:context-name; description "The context (prefix) under which the access rights apply."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmAccessContextPrefix"; } leaf context-match { type enumeration { enum exact { value 1; } enum prefix { value 2; } } default exact; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmAccessContextMatch"; } leaf security-model { type snmp:security-model-or-any; description "The security model under which the access rights apply."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmAccessSecurityModel"; } leaf security-level { type snmp:security-level; description "The minimum security level under which the access rights apply."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmAccessSecurityLevel"; } leaf read-view { type view-name; description "The name of the MIB view of the SNMP context authorizing read access. If this leaf does not exist in a configuration, it maps to a zero-length vacmAccessReadViewName. Implementations MAY restrict the values of this leaf to be one of the available values of /snmp/vacm/view/name in a valid configuration."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmAccessReadViewName"; } leaf write-view { type view-name; description "The name of the MIB view of the SNMP context authorizing write access. If this leaf does not exist in a configuration, it maps to a zero-length vacmAccessWriteViewName. Implementations MAY restrict the values of this leaf to be one of the available values of /snmp/vacm/view/name in a valid configuration."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmAccessWriteViewName"; } leaf notify-view { type view-name; description "The name of the MIB view of the SNMP context authorizing notify access. If this leaf does not exist in a configuration, it maps to a zero-length vacmAccessNotifyViewName. Implementations MAY restrict the values of this leaf to be one of the available values of /snmp/vacm/view/name in a valid configuration."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmAccessNotifyViewName"; } } } list view { key name; description "Definition of MIB views."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmViewTreeFamilyTable"; leaf name { type view-name; description "The name of this VACM MIB view."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmViewTreeFamilyName"; } leaf-list include { type snmp:wildcard-object-identifier; description "A family of subtrees included in this MIB view."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmViewTreeFamilySubtree SNMP-VIEW-BASED-ACM-MIB.vacmViewTreeFamilyMask SNMP-VIEW-BASED-ACM-MIB.vacmViewTreeFamilyType"; } leaf-list exclude { type snmp:wildcard-object-identifier; description "A family of subtrees excluded from this MIB view."; reference "RFC 3415: View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP). SNMP-VIEW-BASED-ACM-MIB.vacmViewTreeFamilySubtree SNMP-VIEW-BASED-ACM-MIB.vacmViewTreeFamilyMask SNMP-VIEW-BASED-ACM-MIB.vacmViewTreeFamilyType"; } } } } } yuma123_2.14/netconf/modules/ietf/ietf-network@2018-02-26.yang0000664000175000017500000001367114770023131023543 0ustar vladimirvladimirmodule ietf-network { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-network"; prefix nw; import ietf-inet-types { prefix inet; reference "RFC 6991: Common YANG Data Types"; } organization "IETF I2RS (Interface to the Routing System) Working Group"; contact "WG Web: WG List: Editor: Alexander Clemm Editor: Jan Medved Editor: Robert Varga Editor: Nitin Bahadur Editor: Hariharan Ananthakrishnan Editor: Xufeng Liu "; description "This module defines a common base data model for a collection of nodes in a network. Node definitions are further used in network topologies and inventories. Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8345; see the RFC itself for full legal notices."; revision 2018-02-26 { description "Initial revision."; reference "RFC 8345: A YANG Data Model for Network Topologies"; } typedef node-id { type inet:uri; description "Identifier for a node. The precise structure of the node-id will be up to the implementation. For example, some implementations MAY pick a URI that includes the network-id as part of the path. The identifier SHOULD be chosen such that the same node in a real network topology will always be identified through the same identifier, even if the data model is instantiated in separate datastores. An implementation MAY choose to capture semantics in the identifier -- for example, to indicate the type of node."; } typedef network-id { type inet:uri; description "Identifier for a network. The precise structure of the network-id will be up to the implementation. The identifier SHOULD be chosen such that the same network will always be identified through the same identifier, even if the data model is instantiated in separate datastores. An implementation MAY choose to capture semantics in the identifier -- for example, to indicate the type of network."; } grouping network-ref { description "Contains the information necessary to reference a network -- for example, an underlay network."; leaf network-ref { type leafref { path "/nw:networks/nw:network/nw:network-id"; require-instance false; } description "Used to reference a network -- for example, an underlay network."; } } grouping node-ref { description "Contains the information necessary to reference a node."; leaf node-ref { type leafref { path "/nw:networks/nw:network[nw:network-id=current()/../"+ "network-ref]/nw:node/nw:node-id"; require-instance false; } description "Used to reference a node. Nodes are identified relative to the network that contains them."; } uses network-ref; } container networks { description "Serves as a top-level container for a list of networks."; list network { key "network-id"; description "Describes a network. A network typically contains an inventory of nodes, topological information (augmented through the network-topology data model), and layering information."; leaf network-id { type network-id; description "Identifies a network."; } container network-types { description "Serves as an augmentation target. The network type is indicated through corresponding presence containers augmented into this container."; } list supporting-network { key "network-ref"; description "An underlay network, used to represent layered network topologies."; leaf network-ref { type leafref { path "/nw:networks/nw:network/nw:network-id"; require-instance false; } description "References the underlay network."; } } list node { key "node-id"; description "The inventory of nodes of this network."; leaf node-id { type node-id; description "Uniquely identifies a node within the containing network."; } list supporting-node { key "network-ref node-ref"; description "Represents another node that is in an underlay network and that supports this node. Used to represent layering structure."; leaf network-ref { type leafref { path "../../../nw:supporting-network/nw:network-ref"; require-instance false; } description "References the underlay network of which the underlay node is a part."; } leaf node-ref { type leafref { path "/nw:networks/nw:network/nw:node/nw:node-id"; require-instance false; } description "References the underlay node itself."; } } } } } } yuma123_2.14/netconf/modules/ietf/ietf-ipv6-router-advertisements@2018-03-13.yang0000664000175000017500000004176314770023131027307 0ustar vladimirvladimirsubmodule ietf-ipv6-router-advertisements { yang-version "1.1"; belongs-to ietf-ipv6-unicast-routing { prefix "v6ur"; } import ietf-inet-types { prefix "inet"; } import ietf-interfaces { prefix "if"; description "An 'ietf-interfaces' module version that is compatible with the Network Management Datastore Architecture (NMDA) is required."; } import ietf-ip { prefix "ip"; description "An 'ietf-ip' module version that is compatible with the Network Management Datastore Architecture (NMDA) is required."; } organization "IETF NETMOD (Network Modeling) Working Group"; contact "WG Web: WG List: Editor: Ladislav Lhotka Acee Lindem Yingzhen Qu "; description "This YANG module augments the 'ietf-ip' module with parameters for IPv6 Router Advertisements. The model fully conforms to the Network Management Datastore Architecture (NMDA). Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8349; see the RFC itself for full legal notices."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)"; revision 2018-03-13 { description "Network Management Datastore Architecture (NMDA) revision."; reference "RFC 8349: A YANG Data Model for Routing Management (NMDA Version)"; } revision 2016-11-04 { description "Initial revision."; reference "RFC 8022: A YANG Data Model for Routing Management"; } augment "/if:interfaces/if:interface/ip:ipv6" { description "Augments interface configuration with parameters of IPv6 Router Advertisements."; container ipv6-router-advertisements { description "Support for IPv6 Router Advertisements."; leaf send-advertisements { type boolean; default "false"; description "A flag indicating whether or not the router sends periodic Router Advertisements and responds to Router Solicitations."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvSendAdvertisements"; } leaf max-rtr-adv-interval { type uint16 { range "4..65535"; } units "seconds"; default "600"; description "The maximum time allowed between sending unsolicited multicast Router Advertisements from the interface."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - MaxRtrAdvInterval"; } leaf min-rtr-adv-interval { type uint16 { range "3..1350"; } units "seconds"; must ". <= 0.75 * ../max-rtr-adv-interval" { description "The value MUST NOT be greater than 75% of 'max-rtr-adv-interval'."; } description "The minimum time allowed between sending unsolicited multicast Router Advertisements from the interface. The default value to be used operationally if this leaf is not configured is determined as follows: - if max-rtr-adv-interval >= 9 seconds, the default value is 0.33 * max-rtr-adv-interval; - otherwise, it is 0.75 * max-rtr-adv-interval."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - MinRtrAdvInterval"; } leaf managed-flag { type boolean; default "false"; description "The value to be placed in the 'Managed address configuration' flag field in the Router Advertisement."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvManagedFlag"; } leaf other-config-flag { type boolean; default "false"; description "The value to be placed in the 'Other configuration' flag field in the Router Advertisement."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvOtherConfigFlag"; } leaf link-mtu { type uint32; default "0"; description "The value to be placed in MTU options sent by the router. A value of zero indicates that no MTU options are sent."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvLinkMTU"; } leaf reachable-time { type uint32 { range "0..3600000"; } units "milliseconds"; default "0"; description "The value to be placed in the Reachable Time field in the Router Advertisement messages sent by the router. A value of zero means unspecified (by this router)."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvReachableTime"; } leaf retrans-timer { type uint32; units "milliseconds"; default "0"; description "The value to be placed in the Retrans Timer field in the Router Advertisement messages sent by the router. A value of zero means unspecified (by this router)."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvRetransTimer"; } leaf cur-hop-limit { type uint8; description "The value to be placed in the Cur Hop Limit field in the Router Advertisement messages sent by the router. A value of zero means unspecified (by this router). If this parameter is not configured, the device SHOULD use the IANA-specified value for the default IPv4 Time to Live (TTL) parameter that was in effect at the time of implementation."; reference "RFC 3232: Assigned Numbers: RFC 1700 is Replaced by an On-line Database RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvCurHopLimit IANA: IP Parameters (https://www.iana.org/assignments/ip-parameters)"; } leaf default-lifetime { type uint16 { range "0..65535"; } units "seconds"; description "The value to be placed in the Router Lifetime field of Router Advertisements sent from the interface, in seconds. It MUST be either zero or between max-rtr-adv-interval and 9000 seconds. A value of zero indicates that the router is not to be used as a default router. These limits may be overridden by specific documents that describe how IPv6 operates over different link layers. If this parameter is not configured, the device SHOULD use a value of 3 * max-rtr-adv-interval."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvDefaultLifetime"; } container prefix-list { description "Support for prefixes to be placed in Prefix Information options in Router Advertisement messages sent from the interface. Prefixes that are advertised by default but do not have their entries in the child 'prefix' list are advertised with the default values of all parameters. The link-local prefix SHOULD NOT be included in the list of advertised prefixes."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvPrefixList"; list prefix { key "prefix-spec"; description "Support for an advertised prefix entry."; leaf prefix-spec { type inet:ipv6-prefix; description "IPv6 address prefix."; } choice control-adv-prefixes { default "advertise"; description "Either (1) the prefix is explicitly removed from the set of advertised prefixes or (2) the parameters with which the prefix is advertised are specified (default case)."; leaf no-advertise { type empty; description "The prefix will not be advertised. This can be used for removing the prefix from the default set of advertised prefixes."; } case advertise { leaf valid-lifetime { type uint32; units "seconds"; default "2592000"; description "The value to be placed in the Valid Lifetime in the Prefix Information option. The designated value of all 1's (0xffffffff) represents infinity."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvValidLifetime"; } leaf on-link-flag { type boolean; default "true"; description "The value to be placed in the on-link flag ('L-bit') field in the Prefix Information option."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvOnLinkFlag"; } leaf preferred-lifetime { type uint32; units "seconds"; must ". <= ../valid-lifetime" { description "This value MUST NOT be greater than valid-lifetime."; } default "604800"; description "The value to be placed in the Preferred Lifetime in the Prefix Information option. The designated value of all 1's (0xffffffff) represents infinity."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvPreferredLifetime"; } leaf autonomous-flag { type boolean; default "true"; description "The value to be placed in the Autonomous Flag field in the Prefix Information option."; reference "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) - AdvAutonomousFlag"; } } } } } } } /* * The subsequent data nodes are obviated and obsoleted * by the Network Management Datastore Architecture * as described in RFC 8342. */ augment "/if:interfaces-state/if:interface/ip:ipv6" { status obsolete; description "Augments interface state data with parameters of IPv6 Router Advertisements."; container ipv6-router-advertisements { status obsolete; description "Parameters of IPv6 Router Advertisements."; leaf send-advertisements { type boolean; status obsolete; description "A flag indicating whether or not the router sends periodic Router Advertisements and responds to Router Solicitations."; } leaf max-rtr-adv-interval { type uint16 { range "4..1800"; } units "seconds"; status obsolete; description "The maximum time allowed between sending unsolicited multicast Router Advertisements from the interface."; } leaf min-rtr-adv-interval { type uint16 { range "3..1350"; } units "seconds"; status obsolete; description "The minimum time allowed between sending unsolicited multicast Router Advertisements from the interface."; } leaf managed-flag { type boolean; status obsolete; description "The value that is placed in the 'Managed address configuration' flag field in the Router Advertisement."; } leaf other-config-flag { type boolean; status obsolete; description "The value that is placed in the 'Other configuration' flag field in the Router Advertisement."; } leaf link-mtu { type uint32; status obsolete; description "The value that is placed in MTU options sent by the router. A value of zero indicates that no MTU options are sent."; } leaf reachable-time { type uint32 { range "0..3600000"; } units "milliseconds"; status obsolete; description "The value that is placed in the Reachable Time field in the Router Advertisement messages sent by the router. A value of zero means unspecified (by this router)."; } leaf retrans-timer { type uint32; units "milliseconds"; status obsolete; description "The value that is placed in the Retrans Timer field in the Router Advertisement messages sent by the router. A value of zero means unspecified (by this router)."; } leaf cur-hop-limit { type uint8; status obsolete; description "The value that is placed in the Cur Hop Limit field in the Router Advertisement messages sent by the router. A value of zero means unspecified (by this router)."; } leaf default-lifetime { type uint16 { range "0..9000"; } units "seconds"; status obsolete; description "The value that is placed in the Router Lifetime field of Router Advertisements sent from the interface, in seconds. A value of zero indicates that the router is not to be used as a default router."; } container prefix-list { status obsolete; description "A list of prefixes that are placed in Prefix Information options in Router Advertisement messages sent from the interface. By default, these are all prefixes that the router advertises via routing protocols as being on-link for the interface from which the advertisement is sent."; list prefix { key "prefix-spec"; status obsolete; description "Advertised prefix entry and its parameters."; leaf prefix-spec { type inet:ipv6-prefix; status obsolete; description "IPv6 address prefix."; } leaf valid-lifetime { type uint32; units "seconds"; status obsolete; description "The value that is placed in the Valid Lifetime in the Prefix Information option. The designated value of all 1's (0xffffffff) represents infinity. An implementation SHOULD keep this value constant in consecutive advertisements, except when it is explicitly changed in configuration."; } leaf on-link-flag { type boolean; status obsolete; description "The value that is placed in the on-link flag ('L-bit') field in the Prefix Information option."; } leaf preferred-lifetime { type uint32; units "seconds"; status obsolete; description "The value that is placed in the Preferred Lifetime in the Prefix Information option, in seconds. The designated value of all 1's (0xffffffff) represents infinity. An implementation SHOULD keep this value constant in consecutive advertisements, except when it is explicitly changed in configuration."; } leaf autonomous-flag { type boolean; status obsolete; description "The value that is placed in the Autonomous Flag field in the Prefix Information option."; } } } } } } yuma123_2.14/netconf/modules/ietf/ietf-netconf-acm@2018-02-14.yang0000664000175000017500000003174314770023131024241 0ustar vladimirvladimirmodule ietf-netconf-acm { namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm"; prefix nacm; import ietf-yang-types { prefix yang; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: WG List: Author: Andy Bierman Author: Martin Bjorklund "; description "Network Configuration Access Control Model. Copyright (c) 2012 - 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8341; see the RFC itself for full legal notices."; revision "2018-02-14" { description "Added support for YANG 1.1 actions and notifications tied to data nodes. Clarified how NACM extensions can be used by other data models."; reference "RFC 8341: Network Configuration Access Control Model"; } revision "2012-02-22" { description "Initial version."; reference "RFC 6536: Network Configuration Protocol (NETCONF) Access Control Model"; } /* * Extension statements */ extension default-deny-write { description "Used to indicate that the data model node represents a sensitive security system parameter. If present, the NETCONF server will only allow the designated 'recovery session' to have write access to the node. An explicit access control rule is required for all other users. If the NACM module is used, then it must be enabled (i.e., /nacm/enable-nacm object equals 'true'), or this extension is ignored. The 'default-deny-write' extension MAY appear within a data definition statement. It is ignored otherwise."; } extension default-deny-all { description "Used to indicate that the data model node controls a very sensitive security system parameter. If present, the NETCONF server will only allow the designated 'recovery session' to have read, write, or execute access to the node. An explicit access control rule is required for all other users. If the NACM module is used, then it must be enabled (i.e., /nacm/enable-nacm object equals 'true'), or this extension is ignored. The 'default-deny-all' extension MAY appear within a data definition statement, 'rpc' statement, or 'notification' statement. It is ignored otherwise."; } /* * Derived types */ typedef user-name-type { type string { length "1..max"; } description "General-purpose username string."; } typedef matchall-string-type { type string { pattern '\*'; } description "The string containing a single asterisk '*' is used to conceptually represent all possible values for the particular leaf using this data type."; } typedef access-operations-type { type bits { bit create { description "Any protocol operation that creates a new data node."; } bit read { description "Any protocol operation or notification that returns the value of a data node."; } bit update { description "Any protocol operation that alters an existing data node."; } bit delete { description "Any protocol operation that removes a data node."; } bit exec { description "Execution access to the specified protocol operation."; } } description "Access operation."; } typedef group-name-type { type string { length "1..max"; pattern '[^\*].*'; } description "Name of administrative group to which users can be assigned."; } typedef action-type { type enumeration { enum permit { description "Requested action is permitted."; } enum deny { description "Requested action is denied."; } } description "Action taken by the server when a particular rule matches."; } typedef node-instance-identifier { type yang:xpath1.0; description "Path expression used to represent a special data node, action, or notification instance-identifier string. A node-instance-identifier value is an unrestricted YANG instance-identifier expression. All the same rules as an instance-identifier apply, except that predicates for keys are optional. If a key predicate is missing, then the node-instance-identifier represents all possible server instances for that key. This XML Path Language (XPath) expression is evaluated in the following context: o The set of namespace declarations are those in scope on the leaf element where this type is used. o The set of variable bindings contains one variable, 'USER', which contains the name of the user of the current session. o The function library is the core function library, but note that due to the syntax restrictions of an instance-identifier, no functions are allowed. o The context node is the root node in the data tree. The accessible tree includes actions and notifications tied to data nodes."; } /* * Data definition statements */ container nacm { nacm:default-deny-all; description "Parameters for NETCONF access control model."; leaf enable-nacm { type boolean; default "true"; description "Enables or disables all NETCONF access control enforcement. If 'true', then enforcement is enabled. If 'false', then enforcement is disabled."; } leaf read-default { type action-type; default "permit"; description "Controls whether read access is granted if no appropriate rule is found for a particular read request."; } leaf write-default { type action-type; default "deny"; description "Controls whether create, update, or delete access is granted if no appropriate rule is found for a particular write request."; } leaf exec-default { type action-type; default "permit"; description "Controls whether exec access is granted if no appropriate rule is found for a particular protocol operation request."; } leaf enable-external-groups { type boolean; default "true"; description "Controls whether the server uses the groups reported by the NETCONF transport layer when it assigns the user to a set of NACM groups. If this leaf has the value 'false', any group names reported by the transport layer are ignored by the server."; } leaf denied-operations { type yang:zero-based-counter32; config false; mandatory true; description "Number of times since the server last restarted that a protocol operation request was denied."; } leaf denied-data-writes { type yang:zero-based-counter32; config false; mandatory true; description "Number of times since the server last restarted that a protocol operation request to alter a configuration datastore was denied."; } leaf denied-notifications { type yang:zero-based-counter32; config false; mandatory true; description "Number of times since the server last restarted that a notification was dropped for a subscription because access to the event type was denied."; } container groups { description "NETCONF access control groups."; list group { key name; description "One NACM group entry. This list will only contain configured entries, not any entries learned from any transport protocols."; leaf name { type group-name-type; description "Group name associated with this entry."; } leaf-list user-name { type user-name-type; description "Each entry identifies the username of a member of the group associated with this entry."; } } } list rule-list { key name; ordered-by user; description "An ordered collection of access control rules."; leaf name { type string { length "1..max"; } description "Arbitrary name assigned to the rule-list."; } leaf-list group { type union { type matchall-string-type; type group-name-type; } description "List of administrative groups that will be assigned the associated access rights defined by the 'rule' list. The string '*' indicates that all groups apply to the entry."; } list rule { key name; ordered-by user; description "One access control rule. Rules are processed in user-defined order until a match is found. A rule matches if 'module-name', 'rule-type', and 'access-operations' match the request. If a rule matches, the 'action' leaf determines whether or not access is granted."; leaf name { type string { length "1..max"; } description "Arbitrary name assigned to the rule."; } leaf module-name { type union { type matchall-string-type; type string; } default "*"; description "Name of the module associated with this rule. This leaf matches if it has the value '*' or if the object being accessed is defined in the module with the specified module name."; } choice rule-type { description "This choice matches if all leafs present in the rule match the request. If no leafs are present, the choice matches all requests."; case protocol-operation { leaf rpc-name { type union { type matchall-string-type; type string; } description "This leaf matches if it has the value '*' or if its value equals the requested protocol operation name."; } } case notification { leaf notification-name { type union { type matchall-string-type; type string; } description "This leaf matches if it has the value '*' or if its value equals the requested notification name."; } } case data-node { leaf path { type node-instance-identifier; mandatory true; description "Data node instance-identifier associated with the data node, action, or notification controlled by this rule. Configuration data or state data instance-identifiers start with a top-level data node. A complete instance-identifier is required for this type of path value. The special value '/' refers to all possible datastore contents."; } } } leaf access-operations { type union { type matchall-string-type; type access-operations-type; } default "*"; description "Access operations associated with this rule. This leaf matches if it has the value '*' or if the bit corresponding to the requested operation is set."; } leaf action { type action-type; mandatory true; description "The access control action associated with the rule. If a rule has been determined to match a particular request, then this object is used to determine whether to permit or deny the request."; } leaf comment { type string; description "A textual description of the access rule."; } } } } } yuma123_2.14/netconf/modules/ietf/ietf-restconf-monitoring@2017-01-26.yang0000664000175000017500000001045514770023131026053 0ustar vladimirvladimirmodule ietf-restconf-monitoring { namespace "urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"; prefix "rcmon"; import ietf-yang-types { prefix yang; } import ietf-inet-types { prefix inet; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: WG List: Author: Andy Bierman Author: Martin Bjorklund Author: Kent Watsen "; description "This module contains monitoring information for the RESTCONF protocol. Copyright (c) 2017 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8040; see the RFC itself for full legal notices."; revision 2017-01-26 { description "Initial revision."; reference "RFC 8040: RESTCONF Protocol."; } container restconf-state { config false; description "Contains RESTCONF protocol monitoring information."; container capabilities { description "Contains a list of protocol capability URIs."; leaf-list capability { type inet:uri; description "A RESTCONF protocol capability URI."; } } container streams { description "Container representing the notification event streams supported by the server."; reference "RFC 5277, Section 3.4, element."; list stream { key name; description "Each entry describes an event stream supported by the server."; leaf name { type string; description "The stream name."; reference "RFC 5277, Section 3.4, element."; } leaf description { type string; description "Description of stream content."; reference "RFC 5277, Section 3.4, element."; } leaf replay-support { type boolean; default false; description "Indicates if replay buffer is supported for this stream. If 'true', then the server MUST support the 'start-time' and 'stop-time' query parameters for this stream."; reference "RFC 5277, Section 3.4, element."; } leaf replay-log-creation-time { when "../replay-support" { description "Only present if notification replay is supported."; } type yang:date-and-time; description "Indicates the time the replay log for this stream was created."; reference "RFC 5277, Section 3.4, element."; } list access { key encoding; min-elements 1; description "The server will create an entry in this list for each encoding format that is supported for this stream. The media type 'text/event-stream' is expected for all event streams. This list identifies the subtypes supported for this stream."; leaf encoding { type string; description "This is the secondary encoding format within the 'text/event-stream' encoding used by all streams. The type 'xml' is supported for XML encoding. The type 'json' is supported for JSON encoding."; } leaf location { type inet:uri; mandatory true; description "Contains a URL that represents the entry point for establishing notification delivery via server-sent events."; } } } } } } yuma123_2.14/netconf/modules/ietf/ietf-inet-types@2010-09-24.yang0000664000175000017500000003707314770023131024152 0ustar vladimirvladimirmodule ietf-inet-types { namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; prefix "inet"; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: David Partain WG Chair: David Kessens Editor: Juergen Schoenwaelder "; description "This module contains a collection of generally useful derived YANG data types for Internet addresses and related things. Copyright (c) 2010 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 6021; see the RFC itself for full legal notices."; revision 2010-09-24 { description "Initial revision."; reference "RFC 6021: Common YANG Data Types"; } /*** collection of protocol field related types ***/ typedef ip-version { type enumeration { enum unknown { value "0"; description "An unknown or unspecified version of the Internet protocol."; } enum ipv4 { value "1"; description "The IPv4 protocol as defined in RFC 791."; } enum ipv6 { value "2"; description "The IPv6 protocol as defined in RFC 2460."; } } description "This value represents the version of the IP protocol. In the value set and its semantics, this type is equivalent to the InetVersion textual convention of the SMIv2."; reference "RFC 791: Internet Protocol RFC 2460: Internet Protocol, Version 6 (IPv6) Specification RFC 4001: Textual Conventions for Internet Network Addresses"; } typedef dscp { type uint8 { range "0..63"; } description "The dscp type represents a Differentiated Services Code-Point that may be used for marking packets in a traffic stream. In the value set and its semantics, this type is equivalent to the Dscp textual convention of the SMIv2."; reference "RFC 3289: Management Information Base for the Differentiated Services Architecture RFC 2474: Definition of the Differentiated Services Field (DS Field) in the IPv4 and IPv6 Headers RFC 2780: IANA Allocation Guidelines For Values In the Internet Protocol and Related Headers"; } typedef ipv6-flow-label { type uint32 { range "0..1048575"; } description "The flow-label type represents flow identifier or Flow Label in an IPv6 packet header that may be used to discriminate traffic flows. In the value set and its semantics, this type is equivalent to the IPv6FlowLabel textual convention of the SMIv2."; reference "RFC 3595: Textual Conventions for IPv6 Flow Label RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; } typedef port-number { type uint16 { range "0..65535"; } description "The port-number type represents a 16-bit port number of an Internet transport layer protocol such as UDP, TCP, DCCP, or SCTP. Port numbers are assigned by IANA. A current list of all assignments is available from . Note that the port number value zero is reserved by IANA. In situations where the value zero does not make sense, it can be excluded by subtyping the port-number type. In the value set and its semantics, this type is equivalent to the InetPortNumber textual convention of the SMIv2."; reference "RFC 768: User Datagram Protocol RFC 793: Transmission Control Protocol RFC 4960: Stream Control Transmission Protocol RFC 4340: Datagram Congestion Control Protocol (DCCP) RFC 4001: Textual Conventions for Internet Network Addresses"; } /*** collection of autonomous system related types ***/ typedef as-number { type uint32; description "The as-number type represents autonomous system numbers which identify an Autonomous System (AS). An AS is a set of routers under a single technical administration, using an interior gateway protocol and common metrics to route packets within the AS, and using an exterior gateway protocol to route packets to other ASs'. IANA maintains the AS number space and has delegated large parts to the regional registries. Autonomous system numbers were originally limited to 16 bits. BGP extensions have enlarged the autonomous system number space to 32 bits. This type therefore uses an uint32 base type without a range restriction in order to support a larger autonomous system number space. In the value set and its semantics, this type is equivalent to the InetAutonomousSystemNumber textual convention of the SMIv2."; reference "RFC 1930: Guidelines for creation, selection, and registration of an Autonomous System (AS) RFC 4271: A Border Gateway Protocol 4 (BGP-4) RFC 4893: BGP Support for Four-octet AS Number Space RFC 4001: Textual Conventions for Internet Network Addresses"; } /*** collection of IP address and hostname related types ***/ typedef ip-address { type union { type inet:ipv4-address; type inet:ipv6-address; } description "The ip-address type represents an IP address and is IP version neutral. The format of the textual representations implies the IP version."; } typedef ipv4-address { type string { pattern '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + '(%[\p{N}\p{L}]+)?'; } description "The ipv4-address type represents an IPv4 address in dotted-quad notation. The IPv4 address may include a zone index, separated by a % sign. The zone index is used to disambiguate identical address values. For link-local addresses, the zone index will typically be the interface index number or the name of an interface. If the zone index is not present, the default zone of the device will be used. The canonical format for the zone index is the numerical format"; } typedef ipv6-address { type string { pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + '(%[\p{N}\p{L}]+)?'; pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + '(%.+)?'; } description "The ipv6-address type represents an IPv6 address in full, mixed, shortened, and shortened-mixed notation. The IPv6 address may include a zone index, separated by a % sign. The zone index is used to disambiguate identical address values. For link-local addresses, the zone index will typically be the interface index number or the name of an interface. If the zone index is not present, the default zone of the device will be used. The canonical format of IPv6 addresses uses the compressed format described in RFC 4291, Section 2.2, item 2 with the following additional rules: the :: substitution must be applied to the longest sequence of all-zero 16-bit chunks in an IPv6 address. If there is a tie, the first sequence of all-zero 16-bit chunks is replaced by ::. Single all-zero 16-bit chunks are not compressed. The canonical format uses lowercase characters and leading zeros are not allowed. The canonical format for the zone index is the numerical format as described in RFC 4007, Section 11.2."; reference "RFC 4291: IP Version 6 Addressing Architecture RFC 4007: IPv6 Scoped Address Architecture RFC 5952: A Recommendation for IPv6 Address Text Representation"; } typedef ip-prefix { type union { type inet:ipv4-prefix; type inet:ipv6-prefix; } description "The ip-prefix type represents an IP prefix and is IP version neutral. The format of the textual representations implies the IP version."; } typedef ipv4-prefix { type string { pattern '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; } description "The ipv4-prefix type represents an IPv4 address prefix. The prefix length is given by the number following the slash character and must be less than or equal to 32. A prefix length value of n corresponds to an IP address mask that has n contiguous 1-bits from the most significant bit (MSB) and all other bits set to 0. The canonical format of an IPv4 prefix has all bits of the IPv4 address set to zero that are not part of the IPv4 prefix."; } typedef ipv6-prefix { type string { pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + '(/.+)'; } description "The ipv6-prefix type represents an IPv6 address prefix. The prefix length is given by the number following the slash character and must be less than or equal 128. A prefix length value of n corresponds to an IP address mask that has n contiguous 1-bits from the most significant bit (MSB) and all other bits set to 0. The IPv6 address should have all bits that do not belong to the prefix set to zero. The canonical format of an IPv6 prefix has all bits of the IPv6 address set to zero that are not part of the IPv6 prefix. Furthermore, IPv6 address is represented in the compressed format described in RFC 4291, Section 2.2, item 2 with the following additional rules: the :: substitution must be applied to the longest sequence of all-zero 16-bit chunks in an IPv6 address. If there is a tie, the first sequence of all-zero 16-bit chunks is replaced by ::. Single all-zero 16-bit chunks are not compressed. The canonical format uses lowercase characters and leading zeros are not allowed."; reference "RFC 4291: IP Version 6 Addressing Architecture"; } /*** collection of domain name and URI types ***/ typedef domain-name { type string { pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + '|\.'; length "1..253"; } description "The domain-name type represents a DNS domain name. The name SHOULD be fully qualified whenever possible. Internet domain names are only loosely specified. Section 3.5 of RFC 1034 recommends a syntax (modified in Section 2.1 of RFC 1123). The pattern above is intended to allow for current practice in domain name use, and some possible future expansion. It is designed to hold various types of domain names, including names used for A or AAAA records (host names) and other records, such as SRV records. Note that Internet host names have a stricter syntax (described in RFC 952) than the DNS recommendations in RFCs 1034 and 1123, and that systems that want to store host names in schema nodes using the domain-name type are recommended to adhere to this stricter standard to ensure interoperability. The encoding of DNS names in the DNS protocol is limited to 255 characters. Since the encoding consists of labels prefixed by a length bytes and there is a trailing NULL byte, only 253 characters can appear in the textual dotted notation. The description clause of schema nodes using the domain-name type MUST describe when and how these names are resolved to IP addresses. Note that the resolution of a domain-name value may require to query multiple DNS records (e.g., A for IPv4 and AAAA for IPv6). The order of the resolution process and which DNS record takes precedence can either be defined explicitely or it may depend on the configuration of the resolver. Domain-name values use the US-ASCII encoding. Their canonical format uses lowercase US-ASCII characters. Internationalized domain names MUST be encoded in punycode as described in RFC 3492"; reference "RFC 952: DoD Internet Host Table Specification RFC 1034: Domain Names - Concepts and Facilities RFC 1123: Requirements for Internet Hosts -- Application and Support RFC 2782: A DNS RR for specifying the location of services (DNS SRV) RFC 3492: Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA) RFC 5891: Internationalizing Domain Names in Applications (IDNA): Protocol"; } typedef host { type union { type inet:ip-address; type inet:domain-name; } description "The host type represents either an IP address or a DNS domain name."; } typedef uri { type string; description "The uri type represents a Uniform Resource Identifier (URI) as defined by STD 66. Objects using the uri type MUST be in US-ASCII encoding, and MUST be normalized as described by RFC 3986 Sections 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary percent-encoding is removed, and all case-insensitive characters are set to lowercase except for hexadecimal digits, which are normalized to uppercase as described in Section 6.2.2.1. The purpose of this normalization is to help provide unique URIs. Note that this normalization is not sufficient to provide uniqueness. Two URIs that are textually distinct after this normalization may still be equivalent. Objects using the uri type may restrict the schemes that they permit. For example, 'data:' and 'urn:' schemes might not be appropriate. A zero-length URI is not a valid URI. This can be used to express 'URI absent' where required. In the value set and its semantics, this type is equivalent to the Uri SMIv2 textual convention defined in RFC 5017."; reference "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax RFC 3305: Report from the Joint W3C/IETF URI Planning Interest Group: Uniform Resource Identifiers (URIs), URLs, and Uniform Resource Names (URNs): Clarifications and Recommendations RFC 5017: MIB Textual Conventions for Uniform Resource Identifiers (URIs)"; } } yuma123_2.14/netconf/modules/ietf/ietf-x509-cert-to-name@2014-12-10.yang0000664000175000017500000002543114770023131025033 0ustar vladimirvladimirmodule ietf-x509-cert-to-name { namespace "urn:ietf:params:xml:ns:yang:ietf-x509-cert-to-name"; prefix x509c2n; import ietf-yang-types { prefix yang; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: Thomas Nadeau WG Chair: Juergen Schoenwaelder Editor: Martin Bjorklund Editor: Juergen Schoenwaelder "; description "This module contains a collection of YANG definitions for extracting a name from an X.509 certificate. The algorithm used to extract a name from an X.509 certificate was first defined in RFC 6353. Copyright (c) 2014 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 7407; see the RFC itself for full legal notices."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP)"; revision 2014-12-10 { description "Initial revision."; reference "RFC 7407: A YANG Data Model for SNMP Configuration"; } typedef tls-fingerprint { type yang:hex-string { pattern '([0-9a-fA-F]){2}(:([0-9a-fA-F]){2}){0,254}'; } description "A fingerprint value that can be used to uniquely reference other data of potentially arbitrary length. A tls-fingerprint value is composed of a 1-octet hashing algorithm identifier followed by the fingerprint value. The first octet value identifying the hashing algorithm is taken from the IANA 'TLS HashAlgorithm Registry' (RFC 5246). The remaining octets are filled using the results of the hashing algorithm."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.SnmpTLSFingerprint"; } /* Identities */ identity cert-to-name { description "Base identity for algorithms to derive a name from a certificate."; } identity specified { base cert-to-name; description "Directly specifies the name to be used for the certificate. The value of the leaf 'name' in the cert-to-name list is used."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmCertSpecified"; } identity san-rfc822-name { base cert-to-name; description "Maps a subjectAltName's rfc822Name to a name. The local part of the rfc822Name is passed unaltered, but the host-part of the name must be passed in lowercase. For example, the rfc822Name field FooBar@Example.COM is mapped to name FooBar@example.com."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmCertSANRFC822Name"; } identity san-dns-name { base cert-to-name; description "Maps a subjectAltName's dNSName to a name after first converting it to all lowercase (RFC 5280 does not specify converting to lowercase, so this involves an extra step). This mapping results in a 1:1 correspondence between subjectAltName dNSName values and the name values."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmCertSANDNSName"; } identity san-ip-address { base cert-to-name; description "Maps a subjectAltName's iPAddress to a name by transforming the binary-encoded address as follows: 1) for IPv4, the value is converted into a decimal-dotted quad address (e.g., '192.0.2.1'). 2) for IPv6 addresses, the value is converted into a 32-character, all-lowercase hexadecimal string without any colon separators. This mapping results in a 1:1 correspondence between subjectAltName iPAddress values and the name values."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmCertSANIpAddress"; } identity san-any { base cert-to-name; description "Maps any of the following fields using the corresponding mapping algorithms: +------------+-----------------+ | Type | Algorithm | |------------+-----------------| | rfc822Name | san-rfc822-name | | dNSName | san-dns-name | | iPAddress | san-ip-address | +------------+-----------------+ The first matching subjectAltName value found in the certificate of the above types MUST be used when deriving the name. The mapping algorithm specified in the 'Algorithm' column MUST be used to derive the name. This mapping results in a 1:1 correspondence between subjectAltName values and name values. The three sub-mapping algorithms produced by this combined algorithm cannot produce conflicting results between themselves."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmCertSANAny"; } identity common-name { base cert-to-name; description "Maps a certificate's CommonName to a name after converting it to a UTF-8 encoding. The usage of CommonNames is deprecated, and users are encouraged to use subjectAltName mapping methods instead. This mapping results in a 1:1 correspondence between certificate CommonName values and name values."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmCertCommonName"; } /* * Groupings */ grouping cert-to-name { description "Defines nodes for mapping certificates to names. Modules that use this grouping should describe how the resulting name is used."; list cert-to-name { key id; description "This list defines how certificates are mapped to names. The name is derived by considering each cert-to-name list entry in order. The cert-to-name entry's fingerprint determines whether the list entry is a match: 1) If the cert-to-name list entry's fingerprint value matches that of the presented certificate, then consider the list entry a successful match. 2) If the cert-to-name list entry's fingerprint value matches that of a locally held copy of a trusted CA certificate, and that CA certificate was part of the CA certificate chain to the presented certificate, then consider the list entry a successful match. Once a matching cert-to-name list entry has been found, the map-type is used to determine how the name associated with the certificate should be determined. See the map-type leaf's description for details on determining the name value. If it is impossible to determine a name from the cert-to-name list entry's data combined with the data presented in the certificate, then additional cert-to-name list entries MUST be searched to look for another potential match. Security administrators are encouraged to make use of certificates with subjectAltName fields that can be mapped to names so that a single root CA certificate can allow all child certificates' subjectAltName fields to map directly to a name via a 1:1 transformation."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmCertToTSNEntry"; leaf id { type uint32; description "The id specifies the order in which the entries in the cert-to-name list are searched. Entries with lower numbers are searched first."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmCertToTSNID"; } leaf fingerprint { type x509c2n:tls-fingerprint; mandatory true; description "Specifies a value with which the fingerprint of the full certificate presented by the peer is compared. If the fingerprint of the full certificate presented by the peer does not match the fingerprint configured, then the entry is skipped, and the search for a match continues."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmCertToTSNFingerprint"; } leaf map-type { type identityref { base cert-to-name; } mandatory true; description "Specifies the algorithm used to map the certificate presented by the peer to a name. Mappings that need additional configuration objects should use the 'when' statement to make them conditional based on the map-type."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmCertToTSNMapType"; } leaf name { when "../map-type = 'x509c2n:specified'"; type string; mandatory true; description "Directly specifies the NETCONF username when the map-type is 'specified'."; reference "RFC 6353: Transport Layer Security (TLS) Transport Model for the Simple Network Management Protocol (SNMP). SNMP-TLS-TM-MIB.snmpTlstmCertToTSNData"; } } } } yuma123_2.14/netconf/modules/ietf/ietf-restconf@2017-01-26.yang0000664000175000017500000002104314770023131023663 0ustar vladimirvladimirmodule ietf-restconf { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; prefix "rc"; organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: WG List: Author: Andy Bierman Author: Martin Bjorklund Author: Kent Watsen "; description "This module contains conceptual YANG specifications for basic RESTCONF media type definitions used in RESTCONF protocol messages. Note that the YANG definitions within this module do not represent configuration data of any kind. The 'restconf-media-type' YANG extension statement provides a normative syntax for XML and JSON message-encoding purposes. Copyright (c) 2017 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8040; see the RFC itself for full legal notices."; revision 2017-01-26 { description "Initial revision."; reference "RFC 8040: RESTCONF Protocol."; } extension yang-data { argument name { yin-element true; } description "This extension is used to specify a YANG data template that represents conceptual data defined in YANG. It is intended to describe hierarchical data independent of protocol context or specific message-encoding format. Data definition statements within a yang-data extension specify the generic syntax for the specific YANG data template, whose name is the argument of the 'yang-data' extension statement. Note that this extension does not define a media type. A specification using this extension MUST specify the message-encoding rules, including the content media type. The mandatory 'name' parameter value identifies the YANG data template that is being defined. It contains the template name. This extension is ignored unless it appears as a top-level statement. It MUST contain data definition statements that result in exactly one container data node definition. An instance of a YANG data template can thus be translated into an XML instance document, whose top-level element corresponds to the top-level container. The module name and namespace values for the YANG module using the extension statement are assigned to instance document data conforming to the data definition statements within this extension. The substatements of this extension MUST follow the 'data-def-stmt' rule in the YANG ABNF. The XPath document root is the extension statement itself, such that the child nodes of the document root are represented by the data-def-stmt substatements within this extension. This conceptual document is the context for the following YANG statements: - must-stmt - when-stmt - path-stmt - min-elements-stmt - max-elements-stmt - mandatory-stmt - unique-stmt - ordered-by - instance-identifier data type The following data-def-stmt substatements are constrained when used within a 'yang-data' extension statement. - The list-stmt is not required to have a key-stmt defined. - The if-feature-stmt is ignored if present. - The config-stmt is ignored if present. - The available identity values for any 'identityref' leaf or leaf-list nodes are limited to the module containing this extension statement and the modules imported into that module. "; } rc:yang-data yang-errors { uses errors; } rc:yang-data yang-api { uses restconf; } grouping errors { description "A grouping that contains a YANG container representing the syntax and semantics of a YANG Patch error report within a response message."; container errors { description "Represents an error report returned by the server if a request results in an error."; list error { description "An entry containing information about one specific error that occurred while processing a RESTCONF request."; reference "RFC 6241, Section 4.3."; leaf error-type { type enumeration { enum transport { description "The transport layer."; } enum rpc { description "The rpc or notification layer."; } enum protocol { description "The protocol operation layer."; } enum application { description "The server application layer."; } } mandatory true; description "The protocol layer where the error occurred."; } leaf error-tag { type string; mandatory true; description "The enumerated error-tag."; } leaf error-app-tag { type string; description "The application-specific error-tag."; } leaf error-path { type instance-identifier; description "The YANG instance identifier associated with the error node."; } leaf error-message { type string; description "A message describing the error."; } anydata error-info { description "This anydata value MUST represent a container with zero or more data nodes representing additional error information."; } } } } grouping restconf { description "Conceptual grouping representing the RESTCONF root resource."; container restconf { description "Conceptual container representing the RESTCONF root resource."; container data { description "Container representing the datastore resource. Represents the conceptual root of all state data and configuration data supported by the server. The child nodes of this container can be any data resources that are defined as top-level data nodes from the YANG modules advertised by the server in the 'ietf-yang-library' module."; } container operations { description "Container for all operation resources. Each resource is represented as an empty leaf with the name of the RPC operation from the YANG 'rpc' statement. For example, the 'system-restart' RPC operation defined in the 'ietf-system' module would be represented as an empty leaf in the 'ietf-system' namespace. This is a conceptual leaf and will not actually be found in the module: module ietf-system { leaf system-reset { type empty; } } To invoke the 'system-restart' RPC operation: POST /restconf/operations/ietf-system:system-restart To discover the RPC operations supported by the server: GET /restconf/operations In XML, the YANG module namespace identifies the module: In JSON, the YANG module name identifies the module: { 'ietf-system:system-restart' : [null] } "; } leaf yang-library-version { type string { pattern '\d{4}-\d{2}-\d{2}'; } config false; mandatory true; description "Identifies the revision date of the 'ietf-yang-library' module that is implemented by this RESTCONF server. Indicates the year, month, and day in YYYY-MM-DD numeric format."; } } } } yuma123_2.14/netconf/modules/ietf/ietf-inet-types@2013-07-15.yang0000664000175000017500000004067614770023131024156 0ustar vladimirvladimirmodule ietf-inet-types { namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; prefix "inet"; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: WG Chair: David Kessens WG Chair: Juergen Schoenwaelder Editor: Juergen Schoenwaelder "; description "This module contains a collection of generally useful derived YANG data types for Internet addresses and related things. Copyright (c) 2013 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 6991; see the RFC itself for full legal notices."; revision 2013-07-15 { description "This revision adds the following new data types: - ip-address-no-zone - ipv4-address-no-zone - ipv6-address-no-zone"; reference "RFC 6991: Common YANG Data Types"; } revision 2010-09-24 { description "Initial revision."; reference "RFC 6021: Common YANG Data Types"; } /*** collection of types related to protocol fields ***/ typedef ip-version { type enumeration { enum unknown { value "0"; description "An unknown or unspecified version of the Internet protocol."; } enum ipv4 { value "1"; description "The IPv4 protocol as defined in RFC 791."; } enum ipv6 { value "2"; description "The IPv6 protocol as defined in RFC 2460."; } } description "This value represents the version of the IP protocol. In the value set and its semantics, this type is equivalent to the InetVersion textual convention of the SMIv2."; reference "RFC 791: Internet Protocol RFC 2460: Internet Protocol, Version 6 (IPv6) Specification RFC 4001: Textual Conventions for Internet Network Addresses"; } typedef dscp { type uint8 { range "0..63"; } description "The dscp type represents a Differentiated Services Code Point that may be used for marking packets in a traffic stream. In the value set and its semantics, this type is equivalent to the Dscp textual convention of the SMIv2."; reference "RFC 3289: Management Information Base for the Differentiated Services Architecture RFC 2474: Definition of the Differentiated Services Field (DS Field) in the IPv4 and IPv6 Headers RFC 2780: IANA Allocation Guidelines For Values In the Internet Protocol and Related Headers"; } typedef ipv6-flow-label { type uint32 { range "0..1048575"; } description "The ipv6-flow-label type represents the flow identifier or Flow Label in an IPv6 packet header that may be used to discriminate traffic flows. In the value set and its semantics, this type is equivalent to the IPv6FlowLabel textual convention of the SMIv2."; reference "RFC 3595: Textual Conventions for IPv6 Flow Label RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; } typedef port-number { type uint16 { range "0..65535"; } description "The port-number type represents a 16-bit port number of an Internet transport-layer protocol such as UDP, TCP, DCCP, or SCTP. Port numbers are assigned by IANA. A current list of all assignments is available from . Note that the port number value zero is reserved by IANA. In situations where the value zero does not make sense, it can be excluded by subtyping the port-number type. In the value set and its semantics, this type is equivalent to the InetPortNumber textual convention of the SMIv2."; reference "RFC 768: User Datagram Protocol RFC 793: Transmission Control Protocol RFC 4960: Stream Control Transmission Protocol RFC 4340: Datagram Congestion Control Protocol (DCCP) RFC 4001: Textual Conventions for Internet Network Addresses"; } /*** collection of types related to autonomous systems ***/ typedef as-number { type uint32; description "The as-number type represents autonomous system numbers which identify an Autonomous System (AS). An AS is a set of routers under a single technical administration, using an interior gateway protocol and common metrics to route packets within the AS, and using an exterior gateway protocol to route packets to other ASes. IANA maintains the AS number space and has delegated large parts to the regional registries. Autonomous system numbers were originally limited to 16 bits. BGP extensions have enlarged the autonomous system number space to 32 bits. This type therefore uses an uint32 base type without a range restriction in order to support a larger autonomous system number space. In the value set and its semantics, this type is equivalent to the InetAutonomousSystemNumber textual convention of the SMIv2."; reference "RFC 1930: Guidelines for creation, selection, and registration of an Autonomous System (AS) RFC 4271: A Border Gateway Protocol 4 (BGP-4) RFC 4001: Textual Conventions for Internet Network Addresses RFC 6793: BGP Support for Four-Octet Autonomous System (AS) Number Space"; } /*** collection of types related to IP addresses and hostnames ***/ typedef ip-address { type union { type inet:ipv4-address; type inet:ipv6-address; } description "The ip-address type represents an IP address and is IP version neutral. The format of the textual representation implies the IP version. This type supports scoped addresses by allowing zone identifiers in the address format."; reference "RFC 4007: IPv6 Scoped Address Architecture"; } typedef ipv4-address { type string { pattern '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + '(%[\p{N}\p{L}]+)?'; } description "The ipv4-address type represents an IPv4 address in dotted-quad notation. The IPv4 address may include a zone index, separated by a % sign. The zone index is used to disambiguate identical address values. For link-local addresses, the zone index will typically be the interface index number or the name of an interface. If the zone index is not present, the default zone of the device will be used. The canonical format for the zone index is the numerical format"; } typedef ipv6-address { type string { pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + '(%[\p{N}\p{L}]+)?'; pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + '(%.+)?'; } description "The ipv6-address type represents an IPv6 address in full, mixed, shortened, and shortened-mixed notation. The IPv6 address may include a zone index, separated by a % sign. The zone index is used to disambiguate identical address values. For link-local addresses, the zone index will typically be the interface index number or the name of an interface. If the zone index is not present, the default zone of the device will be used. The canonical format of IPv6 addresses uses the textual representation defined in Section 4 of RFC 5952. The canonical format for the zone index is the numerical format as described in Section 11.2 of RFC 4007."; reference "RFC 4291: IP Version 6 Addressing Architecture RFC 4007: IPv6 Scoped Address Architecture RFC 5952: A Recommendation for IPv6 Address Text Representation"; } typedef ip-address-no-zone { type union { type inet:ipv4-address-no-zone; type inet:ipv6-address-no-zone; } description "The ip-address-no-zone type represents an IP address and is IP version neutral. The format of the textual representation implies the IP version. This type does not support scoped addresses since it does not allow zone identifiers in the address format."; reference "RFC 4007: IPv6 Scoped Address Architecture"; } typedef ipv4-address-no-zone { type inet:ipv4-address { pattern '[0-9\.]*'; } description "An IPv4 address without a zone index. This type, derived from ipv4-address, may be used in situations where the zone is known from the context and hence no zone index is needed."; } typedef ipv6-address-no-zone { type inet:ipv6-address { pattern '[0-9a-fA-F:\.]*'; } description "An IPv6 address without a zone index. This type, derived from ipv6-address, may be used in situations where the zone is known from the context and hence no zone index is needed."; reference "RFC 4291: IP Version 6 Addressing Architecture RFC 4007: IPv6 Scoped Address Architecture RFC 5952: A Recommendation for IPv6 Address Text Representation"; } typedef ip-prefix { type union { type inet:ipv4-prefix; type inet:ipv6-prefix; } description "The ip-prefix type represents an IP prefix and is IP version neutral. The format of the textual representations implies the IP version."; } typedef ipv4-prefix { type string { pattern '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; } description "The ipv4-prefix type represents an IPv4 address prefix. The prefix length is given by the number following the slash character and must be less than or equal to 32. A prefix length value of n corresponds to an IP address mask that has n contiguous 1-bits from the most significant bit (MSB) and all other bits set to 0. The canonical format of an IPv4 prefix has all bits of the IPv4 address set to zero that are not part of the IPv4 prefix."; } typedef ipv6-prefix { type string { pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + '(/.+)'; } description "The ipv6-prefix type represents an IPv6 address prefix. The prefix length is given by the number following the slash character and must be less than or equal to 128. A prefix length value of n corresponds to an IP address mask that has n contiguous 1-bits from the most significant bit (MSB) and all other bits set to 0. The IPv6 address should have all bits that do not belong to the prefix set to zero. The canonical format of an IPv6 prefix has all bits of the IPv6 address set to zero that are not part of the IPv6 prefix. Furthermore, the IPv6 address is represented as defined in Section 4 of RFC 5952."; reference "RFC 5952: A Recommendation for IPv6 Address Text Representation"; } /*** collection of domain name and URI types ***/ typedef domain-name { type string { pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + '|\.'; length "1..253"; } description "The domain-name type represents a DNS domain name. The name SHOULD be fully qualified whenever possible. Internet domain names are only loosely specified. Section 3.5 of RFC 1034 recommends a syntax (modified in Section 2.1 of RFC 1123). The pattern above is intended to allow for current practice in domain name use, and some possible future expansion. It is designed to hold various types of domain names, including names used for A or AAAA records (host names) and other records, such as SRV records. Note that Internet host names have a stricter syntax (described in RFC 952) than the DNS recommendations in RFCs 1034 and 1123, and that systems that want to store host names in schema nodes using the domain-name type are recommended to adhere to this stricter standard to ensure interoperability. The encoding of DNS names in the DNS protocol is limited to 255 characters. Since the encoding consists of labels prefixed by a length bytes and there is a trailing NULL byte, only 253 characters can appear in the textual dotted notation. The description clause of schema nodes using the domain-name type MUST describe when and how these names are resolved to IP addresses. Note that the resolution of a domain-name value may require to query multiple DNS records (e.g., A for IPv4 and AAAA for IPv6). The order of the resolution process and which DNS record takes precedence can either be defined explicitly or may depend on the configuration of the resolver. Domain-name values use the US-ASCII encoding. Their canonical format uses lowercase US-ASCII characters. Internationalized domain names MUST be A-labels as per RFC 5890."; reference "RFC 952: DoD Internet Host Table Specification RFC 1034: Domain Names - Concepts and Facilities RFC 1123: Requirements for Internet Hosts -- Application and Support RFC 2782: A DNS RR for specifying the location of services (DNS SRV) RFC 5890: Internationalized Domain Names in Applications (IDNA): Definitions and Document Framework"; } typedef host { type union { type inet:ip-address; type inet:domain-name; } description "The host type represents either an IP address or a DNS domain name."; } typedef uri { type string; description "The uri type represents a Uniform Resource Identifier (URI) as defined by STD 66. Objects using the uri type MUST be in US-ASCII encoding, and MUST be normalized as described by RFC 3986 Sections 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary percent-encoding is removed, and all case-insensitive characters are set to lowercase except for hexadecimal digits, which are normalized to uppercase as described in Section 6.2.2.1. The purpose of this normalization is to help provide unique URIs. Note that this normalization is not sufficient to provide uniqueness. Two URIs that are textually distinct after this normalization may still be equivalent. Objects using the uri type may restrict the schemes that they permit. For example, 'data:' and 'urn:' schemes might not be appropriate. A zero-length URI is not a valid URI. This can be used to express 'URI absent' where required. In the value set and its semantics, this type is equivalent to the Uri SMIv2 textual convention defined in RFC 5017."; reference "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax RFC 3305: Report from the Joint W3C/IETF URI Planning Interest Group: Uniform Resource Identifiers (URIs), URLs, and Uniform Resource Names (URNs): Clarifications and Recommendations RFC 5017: MIB Textual Conventions for Uniform Resource Identifiers (URIs)"; } } yuma123_2.14/netconf/modules/ietf-expired/0000775000175000017500000000000014770023131020614 5ustar vladimirvladimiryuma123_2.14/netconf/modules/ietf-expired/ietf-direct-must-augment-extension@2015-06-12.yang0000664000175000017500000000314514770023131031407 0ustar vladimirvladimirmodule ietf-direct-must-augment-extension { namespace "urn:ietf:params:xml:ns:yang:ietf-direct-must-augment-extension"; prefix "direct-must-augment-ex"; organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: Editor: Vladimir Vassilev "; description "This module contains extension of the standard augment statement. Copyright (c) 2015 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX; see the RFC itself for full legal notices."; // RFC Ed.: replace XXXX with actual RFC number and remove this // note. // RFC Ed.: update the date below with the date of RFC publication // and remove this note. revision 2015-06-12 { description "Initial revision."; reference "RFC XXXX: Direct Must Augment Extension"; } /* * Extension */ extension augment { argument target; description "This extension introduces a direct must augment statement capable of augmenting existing data nodes with new must sub-statements."; } } yuma123_2.14/netconf/modules/ietf-draft/0000775000175000017500000000000014770023131020254 5ustar vladimirvladimiryuma123_2.14/netconf/modules/ietf-draft/ietf-traffic-generator.yang0000664000175000017500000002261514770023131025471 0ustar vladimirvladimirmodule ietf-traffic-generator { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-traffic-generator"; prefix nttg; import ietf-interfaces { prefix if; reference "RFC 8343: A YANG Data Model For Interface Management"; } import ietf-yang-types { prefix yang; reference "RFC 6991: Common YANG Data Types"; } organization "IETF Benchmarking Methodology Working Group"; contact "WG Web: WG List: Editor: Vladimir Vassilev "; description "This module contains a collection of YANG definitions for description and management of network interconnect testers. Copyright (c) 2024 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX; see the RFC itself for full legal notices."; revision 2024-10-21 { description "Initial revision."; reference "RFC XXXX: A YANG Data Model for Network Tester Management"; } feature multi-stream { description "The device can generate multi-stream traffic."; } feature realtime-epoch { description "The device can generate traffic precisely at configured realtime epoch."; } identity testframe-type { description "Base identity for all testframe types."; } identity static { base testframe-type; description "Identity for static testframe. The frame data and size are constant."; } identity dynamic { base testframe-type; description "Identity to be used as base for dynamic testframe type identities defined in external modules. When used itself it identifies dynamic testframe where the last 18 octets of the payload contain incrementing sequence number field (8 octets) followed by timestamp field in the IEEE 1588-2008 format (10 octets). If frame data is defined for the last 18 octets of the payload it will be ignored and overwritten with dynamic data according to this specification."; } identity modifier-action-type { description "Base identity for all modifier action types."; } identity increment { base modifier-action-type; description "Identity for increment modifier action."; } identity decrement { base modifier-action-type; description "Identity for decrement modifier action."; } identity random { base modifier-action-type; description "Identity for random modifier action."; } grouping common-data { description "Common configuration data."; leaf realtime-epoch { if-feature "realtime-epoch"; type yang:date-and-time; description "If this leaf is present the stream generation will start at the specified realtime epoch."; } leaf total-frames { type uint64; description "If this leaf is present the traffic generation will stop after the specified number of frames are generated."; } } grouping burst-data { description "Generated traffic burst parameters."; leaf testframe-type { type identityref { base nttg:testframe-type; } default "nttg:static"; description "In case of dynamic testframes this leaf specifies the dynamic testframe identity."; } leaf frame-size { type uint32; mandatory true; description "Size of the frames generated. For example for Ethernet interfaces the following definition applies: Ethernet frame-size in octets includes: * Destination Address (6 octets), * Source Address (6 octets), * Frame Type (2 octets), * Data (min 46 octets or 42 octets + 4 octets 802.1Q tag), * CRC Checksum (4 octets). Ethernet frame-size does not include: * Preamble (dependent on MAC configuration by default 7 octets), * Start of frame delimiter (1 octet) Minimum standard Ethernet frame-size is 64 bytes but generators might support smaller sizes for validation."; } leaf frame-data { type string { pattern '([0-9A-F]{2})*'; } description "The raw frame data specified as hexadecimal string. The string length of the specified data can be shorter then the 2*../frame-size value specifying only the header or the header and the payload with or without the 4 byte CRC Checksum in the case of a Ethernet frame. If the frame data string specified is longer then ../frame-size*2 the excess of the specified frame data is trunkated. In multi-stream mode if frame-data leaf for one of the streams is not specified, it is the value specified for the closest stream that precedes it that is used."; } leaf interframe-gap { type uint32; mandatory true; description "Length of the idle period between generated frames. For example for Ethernet interfaces the following definition applies: Ethernet interframe-gap between transmission of frames known as the interframe gap (IFG). A brief recovery time between frames allows devices to prepare for reception of the next frame. The minimum interframe gap is 96 bit times (12 octet times) (the time it takes to transmit 96 bits (12 octets) of raw data on the medium). However the preamble (7 octets) and start of frame delimiter (1 octet) are considered a constant gap that should be included in the interframe-gap. Thus the minimum value for standard Ethernet transmission should be considered 20 octets."; } leaf interburst-gap { type uint32; description "Similar to the interframe-gap but takes place between any two bursts of the stream."; } leaf frames-per-burst { type uint32; description "Number of frames contained in a burst"; } } grouping modifier-data { description "Modifier parameters."; container modifiers { description "Container holding the configured modifiers list."; list modifier { key "id"; description "Each modifier specifies action to be performed on data at certain offset."; leaf id { type uint32; description "Number specifying the identifier of the modifier."; } leaf action { type identityref { base nttg:modifier-action-type; } mandatory true; description "In case of dynamic testframes this leaf specifies the dynamic testframe identity."; } leaf offset { type uint32; mandatory true; description "Offset in octets of the modified data of the frame."; } leaf mask { type string { pattern '([0-9A-F]{2})*'; } mandatory true; description "Bit mask of the actual bits affected by the modifier."; } leaf repetitions { type uint32; mandatory true; description "Count of the packets that will repeat the data before the modifier makes the next update."; } } } } grouping multi-stream-data { description "Multi stream traffic generation parameters."; container streams { description "Non-presence container holding the configured stream list."; list stream { key "id"; description "Each stream repeats a burst until frames-per-stream count is reached followed by interstream-gap delay."; leaf id { type uint32; description "Number specifying the order of the stream."; } uses burst-data; leaf frames-per-stream { type uint32; mandatory true; description "The count of frames to be generated before generation of the next stream is started."; } leaf interstream-gap { type uint32; mandatory true; description "Idle period after the last frame of the last burst."; } uses modifier-data; } } } augment "/if:interfaces/if:interface" { description "Traffic generator augmentations of ietf-interfaces."; container traffic-generator { description "Traffic generator configuration data."; choice type { description "Choice of the type of the data model of the generator. Single or multi stream."; case single-stream { uses burst-data; uses modifier-data; } case multi-stream { uses multi-stream-data; } } uses common-data; } } } yuma123_2.14/netconf/modules/ietf-draft/ietf-traffic-analyzer.yang0000664000175000017500000002451714770023131025333 0ustar vladimirvladimirmodule ietf-traffic-analyzer { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-traffic-analyzer"; prefix ntta; import ietf-interfaces { prefix if; reference "RFC 8343: A YANG Data Model For Interface Management"; } import ietf-yang-types { prefix yang; reference "RFC 6991: Common YANG Data Types"; } organization "IETF Benchmarking Methodology Working Group"; contact "WG Web: WG List: Editor: Vladimir Vassilev "; description "This module contains a collection of YANG definitions for description and management of network interconnect testers. Copyright (c) 2024 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX; see the RFC itself for full legal notices."; revision 2024-10-21 { description "Initial revision."; reference "RFC XXXX: A YANG Data Model for Network Tester Management"; } feature testframe-filter { description "This feature indicates that the device implements filter that can specify a subset of packets to be analyzed as testframes."; } feature idle-octets-counter { description "This feature indicates that the device implements idle-octets counter that accumulates the time the link is not utilized. The minimum required idle gaps are not counted as idle octets."; } feature capture { description "This feature indicates that the device implements packet capture functionality."; } identity testframe-filter { description "Base testframe-filter identity."; } identity bit-field-match { base ntta:testframe-filter; description "Bit field matching filter according to template data and mask."; } grouping statistics-data { description "Analyzer statistics."; leaf pkts { type yang:counter64; description "Total number of packets analyzed."; } leaf octets { type yang:counter64; description "This counter is identical with the in-octets/out-octets counters defined in RFC8343 except that it counts the octets since the analyzer was created."; } leaf idle-octets { if-feature "idle-octets-counter"; type yang:counter64; description "Total accumulated period with no frame transmission taking place measured in octets at the current link speed. Octets not counted in ../octets but not idle are for example layer 1 framing octets - for Ethernet interfaces 7+1 preamble octets per packet."; } leaf errors { type yang:counter64; description "Count of packets with errors. Not counted in the pkts or captured. For example packets with CRC error."; } container testframe-stats { description "Statistics for received testframes containing either sequence number, payload checksum, timestamp or any combination of these features."; leaf pkts { type yang:counter64; description "Total count of detected testframes."; } leaf sequence-errors { type yang:counter64; description "Total count of testframes with unexpected sequence number. After each sequence error the expected next sequence number is updated."; } leaf payload-errors { type yang:counter64; description "Total count of testframes with payload errors."; } container latency { description "Latency statistics."; leaf samples { type uint64; description "Total count of packets used for estimating the latency statistics. Ideally samples=../testframe-stats."; } leaf min { type uint64; units "nanoseconds"; description "Minimum measured latency."; } leaf max { type uint64; units "nanoseconds"; description "Maximum measured latency."; } leaf average { type uint64; units "nanoseconds"; description "The sum of all sampled latencies divided by the number of samples."; } leaf latest { type uint64; units "nanoseconds"; description "Latency of the latest sample."; } } container last-sequence-error { description "Last sequence error state data."; leaf timestamp { type yang:date-and-time; description "Timestamp of the moment a testframe with unexpected sequence number was received."; } leaf expected { type yang:counter64; description "Expected sequence number."; } leaf received { type yang:counter64; description "Received sequence number."; } } } } grouping capture-config-data { description "Grouping with a capture configuration container."; container capture { if-feature "capture"; description "Contains capture parameters."; container start-trigger { description "Configures when the capture start is triggered."; choice start-trigger { description "If none of the cases in this choice are configured the capture process starts from the first frame received."; case frame-index { description "Start capturing frames at the specified frame index."; leaf frame-index { type uint64; description "First captured frame index."; } } case testframe-index { description "Start capturing frames at the specified testframe index."; leaf testframe-index { type uint64; description "Starts capture at specified testframe index."; } } } } container stop-trigger { description "Configures when the capture is stopped."; choice stop-trigger { description "If none of the cases in this choice are configured the captured frames are always the last frames received for as many frames the implementation can buffer."; case when-full { description "Stops capturing when the implementation can not store more frames."; leaf when-full { type empty; description "When present in configuration capture stops when the capture buffer is full."; } } } } } } grouping capture-data { description "Grouping with statistics and data of one or more captured frame."; container capture { if-feature "capture"; description "Statistics and data of one or more captured frames."; list frame { key "sequence-number"; description "Statistics and data of a captured frame."; leaf sequence-number { type uint64; description "Incremental counter of frames captured."; } leaf timestamp { type yang:date-and-time; description "Timestamp of the moment the frame was captured."; } leaf length { type uint32; description "Frame length. Ideally the data captured will be of the same length but can be shorter depending on implementation limitations."; } leaf data { type string { pattern '([0-9A-F]{2})*'; } description "Raw data of the captured frame."; } } } } grouping filter-data { description "Grouping with a filter container specifying the filtering rules for processing only a specific subset of the frames."; container testframe-filter { if-feature "testframe-filter"; presence "When present packets are filtered before analyzed according to the filter type"; description "Contains the filtering rules for processing only a specific subset of the frames."; leaf type { type identityref { base ntta:testframe-filter; } mandatory true; description "Type of the applied filter. External modules can define alternative filter type identities."; } } } augment "/if:interfaces/if:interface" { description "Traffic analyzer augmentations of ietf-interfaces."; container traffic-analyzer { presence "Enables the traffic analyzer."; description "Traffic analyzer for ingress direction."; uses filter-data; uses capture-config-data; container state { config false; description "State data."; uses statistics-data; uses capture-data; } } } augment "/if:interfaces/if:interface/ntta:traffic-analyzer/" + "ntta:testframe-filter" { when "derived-from-or-self(ntta:type, 'ntta:bit-field-match')"; description "Logical AND of masked bit fields."; leaf mask { type string { pattern '([0-9A-F]{2})*'; } description "Specifies bit field mask for comparison. Non-masked bit fields are ignored."; } leaf data { type string { pattern '([0-9A-F]{2})*'; } description "Specify data to be matched according to the specified mask."; } } } yuma123_2.14/netconf/modules/ietf-draft/ietf-tcp-client.yang0000664000175000017500000002563514770023131024136 0ustar vladimirvladimirmodule ietf-tcp-client { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-tcp-client"; prefix tcpc; import ietf-inet-types { prefix inet; reference "RFC 6991: Common YANG Data Types"; } import ietf-crypto-types { prefix ct; reference "RFC AAAA: YANG Data Types and Groupings for Cryptography"; } import ietf-tcp-common { prefix tcpcmn; reference "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; } organization "IETF NETCONF (Network Configuration) Working Group and the IETF TCP Maintenance and Minor Extensions (TCPM) Working Group"; contact "WG Web: https://datatracker.ietf.org/wg/netconf https://datatracker.ietf.org/wg/tcpm WG List: NETCONF WG list TCPM WG list Authors: Kent Watsen Michael Scharf "; description "This module defines reusable groupings for TCP clients that can be used as a basis for specific TCP client instances. Copyright (c) 2022 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC DDDD (https://www.rfc-editor.org/info/rfcDDDD); see the RFC itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here."; revision 2022-05-24 { description "Initial version"; reference "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; } // Features feature local-binding-supported { description "Indicates that the server supports configuring local bindings (i.e., the local address and local port) for TCP clients."; } feature tcp-client-keepalives { description "Per socket TCP keepalive parameters are configurable for TCP clients on the server implementing this feature."; } feature proxy-connect { description "Proxy connection configuration is configurable for TCP clients on the server implementing this feature."; } feature socks5-gss-api { description "Indicates that the server supports authenticating using GSSAPI when initiating TCP connections via and SOCKS Version 5 proxy server."; reference "RFC 1928: SOCKS Protocol Version 5"; } feature socks5-username-password { description "Indicates that the server supports authenticating using username/password when initiating TCP connections via and SOCKS Version 5 proxy server."; reference "RFC 1928: SOCKS Protocol Version 5"; } // Groupings grouping tcp-client-grouping { description "A reusable grouping for configuring a TCP client. Note that this grouping uses fairly typical descendant node names such that a stack of 'uses' statements will have name conflicts. It is intended that the consuming data model will resolve the issue (e.g., by wrapping the 'uses' statement in a container called 'tcp-client-parameters'). This model purposely does not do this itself so as to provide maximum flexibility to consuming models."; leaf remote-address { type inet:host; mandatory true; description "The IP address or hostname of the remote peer to establish a connection with. If a domain name is configured, then the DNS resolution should happen on each connection attempt. If the DNS resolution results in multiple IP addresses, the IP addresses are tried according to local preference order until a connection has been established or until all IP addresses have failed."; } leaf remote-port { type inet:port-number; default "0"; description "The IP port number for the remote peer to establish a connection with. An invalid default value (0) is used (instead of 'mandatory true') so that as application level data model may 'refine' it with an application specific default port number value."; } leaf local-address { if-feature "local-binding-supported"; type inet:ip-address; description "The local IP address/interface (VRF?) to bind to for when connecting to the remote peer. INADDR_ANY ('0.0.0.0') or INADDR6_ANY ('0:0:0:0:0:0:0:0' a.k.a. '::') MAY be used to explicitly indicate the implicit default, that the server can bind to any IPv4 or IPv6 addresses, respectively."; } leaf local-port { if-feature "local-binding-supported"; type inet:port-number; default "0"; description "The local IP port number to bind to for when connecting to the remote peer. The port number '0', which is the default value, indicates that any available local port number may be used."; } container proxy-server { if-feature "proxy-connect"; presence "Indicates that a proxy connection has been configured. Present so that the mandatory descendant nodes do not imply that this node must be configured."; choice proxy-type { mandatory true; description "Selects a proxy connection protocol."; case socks4 { container socks4-parameters { leaf remote-address { type inet:ip-address; mandatory true; description "The IP address of the proxy server."; } leaf remote-port { type inet:port-number; default "1080"; description "The IP port number for the proxy server."; } description "Parameters for connecting to a TCP-based proxy server using the SOCKS4 protocol."; reference "SOCKS, Proceedings: 1992 Usenix Security Symposium."; } } case socks4a { container socks4a-parameters { leaf remote-address { type inet:host; mandatory true; description "The IP address or hostname of the proxy server."; } leaf remote-port { type inet:port-number; default "1080"; description "The IP port number for the proxy server."; } description "Parameters for connecting to a TCP-based proxy server using the SOCKS4a protocol."; reference "SOCKS Proceedings: 1992 Usenix Security Symposium. OpenSSH message: SOCKS 4A: A Simple Extension to SOCKS 4 Protocol https://www.openssh.com/txt/socks4a.protocol"; } } case socks5 { container socks5-parameters { leaf remote-address { type inet:host; mandatory true; description "The IP address or hostname of the proxy server."; } leaf remote-port { type inet:port-number; default "1080"; description "The IP port number for the proxy server."; } container authentication-parameters { presence "Indicates that an authentication mechanism has been configured. Present so that the mandatory descendant nodes do not imply that this node must be configured."; description "A container for SOCKS Version 5 authentication mechanisms. A complete list of methods is defined at: https://www.iana.org/assignments/socks-methods /socks-methods.xhtml."; reference "RFC 1928: SOCKS Protocol Version 5"; choice auth-type { mandatory true; description "A choice amongst supported SOCKS Version 5 authentication mechanisms."; case gss-api { if-feature "socks5-gss-api"; container gss-api { description "Contains GSS-API configuration. Defines as an empty container to enable specific GSS-API configuration to be augmented in by future modules."; reference "RFC 1928: SOCKS Protocol Version 5 RFC 2743: Generic Security Service Application Program Interface Version 2, Update 1"; } } case username-password { if-feature "socks5-username-password"; container username-password { leaf username { type string; mandatory true; description "The 'username' value to use for client identification."; } uses ct:password-grouping { description "The password to be used for client authentication."; } description "Contains Username/Password configuration."; reference "RFC 1929: Username/Password Authentication for SOCKS V5"; } } } } description "Parameters for connecting to a TCP-based proxy server using the SOCKS5 protocol."; reference "RFC 1928: SOCKS Protocol Version 5"; } } } description "Proxy server settings."; } uses tcpcmn:tcp-common-grouping { augment "keepalives" { if-feature "tcp-client-keepalives"; description "Add an if-feature statement so that implementations can choose to support TCP client keepalives."; } } } } yuma123_2.14/netconf/modules/ietf-draft/ietf-ssh-client.yang0000664000175000017500000003154114770023131024136 0ustar vladimirvladimirmodule ietf-ssh-client { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-ssh-client"; prefix sshc; import ietf-netconf-acm { prefix nacm; reference "RFC 8341: Network Configuration Access Control Model"; } import ietf-crypto-types { prefix ct; reference "RFC AAAA: YANG Data Types and Groupings for Cryptography"; } import ietf-truststore { prefix ts; reference "RFC BBBB: A YANG Data Model for a Truststore"; } import ietf-keystore { prefix ks; reference "RFC CCCC: A YANG Data Model for a Keystore"; } import ietf-ssh-common { prefix sshcmn; revision-date 2022-05-24; // stable grouping definitions reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: https://datatracker.ietf.org/wg/netconf WG List: NETCONF WG list Author: Kent Watsen Author: Gary Wu "; description "This module defines reusable groupings for SSH clients that can be used as a basis for specific SSH client instances. Copyright (c) 2022 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC EEEE (https://www.rfc-editor.org/info/rfcEEEE); see the RFC itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here."; revision 2022-05-24 { description "Initial version"; reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } // Features feature ssh-client-keepalives { description "Per socket SSH keepalive parameters are configurable for SSH clients on the server implementing this feature."; } feature client-ident-publickey { description "Indicates that the 'publickey' authentication type, per RFC 4252, is supported for client identification. The 'publickey' authentication type is required by RFC 4252, but common implementations enable it to be disabled."; reference "RFC 4252: The Secure Shell (SSH) Authentication Protocol"; } feature client-ident-password { description "Indicates that the 'password' authentication type, per RFC 4252, is supported for client identification."; reference "RFC 4252: The Secure Shell (SSH) Authentication Protocol"; } feature client-ident-hostbased { description "Indicates that the 'hostbased' authentication type, per RFC 4252, is supported for client identification."; reference "RFC 4252: The Secure Shell (SSH) Authentication Protocol"; } feature client-ident-none { description "Indicates that the 'none' authentication type, per RFC 4252, is supported for client identification."; reference "RFC 4252: The Secure Shell (SSH) Authentication Protocol"; } // Groupings grouping ssh-client-grouping { description "A reusable grouping for configuring a SSH client without any consideration for how an underlying TCP session is established. Note that this grouping uses fairly typical descendant node names such that a stack of 'uses' statements will have name conflicts. It is intended that the consuming data model will resolve the issue (e.g., by wrapping the 'uses' statement in a container called 'ssh-client-parameters'). This model purposely does not do this itself so as to provide maximum flexibility to consuming models."; container client-identity { nacm:default-deny-write; description "The username and authentication methods for the client. The authentication methods are unordered. Clients may initially send any configured method or, per RFC 4252, Section 5.2, send the 'none' method to prompt the server to provide a list of productive methods. Whenever a choice amongst methods arises, implementations SHOULD use a default ordering that prioritizes automation over human-interaction."; leaf username { type string; description "The username of this user. This will be the username used, for instance, to log into an SSH server."; } container public-key { if-feature "client-ident-publickey"; presence "Indicates that publickey-based authentication has been configured. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "A locally-defined or referenced asymmetric key pair to be used for client identification."; reference "RFC CCCC: A YANG Data Model for a Keystore"; uses ks:local-or-keystore-asymmetric-key-grouping { refine "local-or-keystore/local/local-definition" { must 'public-key-format = "ct:ssh-public-key-format"'; } refine "local-or-keystore/keystore/keystore-reference" { must 'deref(.)/../ks:public-key-format' + ' = "ct:ssh-public-key-format"'; } } } container password { if-feature "client-ident-password"; presence "Indicates that password-based authentication has been configured. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "A password to be used to authenticate the client's identity."; uses ct:password-grouping; } container hostbased { if-feature "client-ident-hostbased"; presence "Indicates that hostbased authentication is configured. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "A locally-defined or referenced asymmetric key pair to be used for host identification."; reference "RFC CCCC: A YANG Data Model for a Keystore"; uses ks:local-or-keystore-asymmetric-key-grouping { refine "local-or-keystore/local/local-definition" { must 'public-key-format = "ct:ssh-public-key-format"'; } refine "local-or-keystore/keystore/keystore-reference" { must 'deref(.)/../ks:public-key-format' + ' = "ct:ssh-public-key-format"'; } } } leaf none { if-feature "client-ident-none"; type empty; description "Indicates that 'none' algorithm is used for client identification."; } container certificate { if-feature "sshcmn:ssh-x509-certs"; presence "Indicates that certificate-based authentication has been configured. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "A locally-defined or referenced certificate to be used for client identification."; reference "RFC CCCC: A YANG Data Model for a Keystore"; uses ks:local-or-keystore-end-entity-cert-with-key-grouping { refine "local-or-keystore/local/local-definition" { must 'public-key-format' + ' = "ct:subject-public-key-info-format"'; } refine "local-or-keystore/keystore/keystore-reference" + "/asymmetric-key" { must 'deref(.)/../ks:public-key-format' + ' = "ct:subject-public-key-info-format"'; } } } } // container client-identity container server-authentication { nacm:default-deny-write; must 'ssh-host-keys or ca-certs or ee-certs'; description "Specifies how the SSH client can authenticate SSH servers. Any combination of authentication methods is additive and unordered."; container ssh-host-keys { presence "Indicates that the SSH host key have been configured. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "A bag of SSH host keys used by the SSH client to authenticate SSH server host keys. A server host key is authenticated if it is an exact match to a configured SSH host key."; reference "RFC BBBB: A YANG Data Model for a Truststore"; uses ts:local-or-truststore-public-keys-grouping { refine "local-or-truststore/local/local-definition/public-key" { must 'public-key-format = "ct:ssh-public-key-format"'; } refine "local-or-truststore/truststore/truststore-reference" { must 'deref(.)/../*/ts:public-key-format' + ' = "ct:ssh-public-key-format"'; } } } container ca-certs { if-feature "sshcmn:ssh-x509-certs"; presence "Indicates that the CA certificates have been configured. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "A set of certificate authority (CA) certificates used by the SSH client to authenticate SSH servers. A server is authenticated if its certificate has a valid chain of trust to a configured CA certificate."; reference "RFC BBBB: A YANG Data Model for a Truststore"; uses ts:local-or-truststore-certs-grouping; } container ee-certs { if-feature "sshcmn:ssh-x509-certs"; presence "Indicates that the EE certificates have been configured. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "A set of end-entity certificates used by the SSH client to authenticate SSH servers. A server is authenticated if its certificate is an exact match to a configured end-entity certificate."; reference "RFC BBBB: A YANG Data Model for a Truststore"; uses ts:local-or-truststore-certs-grouping; } } // container server-authentication container transport-params { nacm:default-deny-write; if-feature "sshcmn:transport-params"; description "Configurable parameters of the SSH transport layer."; uses sshcmn:transport-params-grouping; } // container transport-parameters container keepalives { nacm:default-deny-write; if-feature "ssh-client-keepalives"; presence "Indicates that the SSH client proactively tests the aliveness of the remote SSH server."; description "Configures the keep-alive policy, to proactively test the aliveness of the SSH server. An unresponsive TLS server is dropped after approximately max-wait * max-attempts seconds. Per Section 4 of RFC 4254, the SSH client SHOULD send an SSH_MSG_GLOBAL_REQUEST message with a purposely nonexistent 'request name' value (e.g., keepalive@ietf.org) and the 'want reply' value set to '1'."; reference "RFC 4254: The Secure Shell (SSH) Connection Protocol"; leaf max-wait { type uint16 { range "1..max"; } units "seconds"; default "30"; description "Sets the amount of time in seconds after which if no data has been received from the SSH server, a TLS-level message will be sent to test the aliveness of the SSH server."; } leaf max-attempts { type uint8; default "3"; description "Sets the maximum number of sequential keep-alive messages that can fail to obtain a response from the SSH server before assuming the SSH server is no longer alive."; } } // container keepalives } // grouping ssh-client-grouping } yuma123_2.14/netconf/modules/ietf-draft/ietf-netconf-client.yang0000664000175000017500000004674314770023131025007 0ustar vladimirvladimirmodule ietf-netconf-client { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-client"; prefix ncc; import ietf-yang-types { prefix yang; reference "RFC 6991: Common YANG Data Types"; } import ietf-tcp-client { prefix tcpc; reference "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; } import ietf-tcp-server { prefix tcps; reference "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; } import ietf-ssh-client { prefix sshc; revision-date 2022-05-24; // stable grouping definitions reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } import ietf-tls-client { prefix tlsc; revision-date 2022-05-24; // stable grouping definitions reference "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: https://datatracker.ietf.org/wg/netconf WG List: NETCONF WG list Author: Kent Watsen Author: Gary Wu "; description "This module contains a collection of YANG definitions for configuring NETCONF clients. Copyright (c) 2022 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC HHHH (https://www.rfc-editor.org/info/rfcHHHH); see the RFC itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here."; revision 2022-05-24 { description "Initial version"; reference "RFC HHHH: NETCONF Client and Server Models"; } // Features feature ssh-initiate { description "The 'ssh-initiate' feature indicates that the NETCONF client supports initiating SSH connections to NETCONF servers."; reference "RFC 6242: Using the NETCONF Protocol over Secure Shell (SSH)"; } feature tls-initiate { description "The 'tls-initiate' feature indicates that the NETCONF client supports initiating TLS connections to NETCONF servers."; reference "RFC 7589: Using the NETCONF Protocol over Transport Layer Security (TLS) with Mutual X.509 Authentication"; } feature ssh-listen { description "The 'ssh-listen' feature indicates that the NETCONF client supports opening a port to listen for incoming NETCONF server call-home SSH connections."; reference "RFC 8071: NETCONF Call Home and RESTCONF Call Home"; } feature tls-listen { description "The 'tls-listen' feature indicates that the NETCONF client supports opening a port to listen for incoming NETCONF server call-home TLS connections."; reference "RFC 8071: NETCONF Call Home and RESTCONF Call Home"; } feature central-netconf-client-supported { description "The 'central-netconf-client-supported' feature indicates that the server supports the top-level 'netconf-client' node. This feature is needed as some servers may want to use features defined in this module, which requires this module to be implemented, without having to support the top-level 'netconf-client' node."; } // Groupings grouping netconf-client-grouping { description "A reusable grouping for configuring a NETCONF client without any consideration for how underlying transport sessions are established. This grouping currently does not define any nodes. It exists only so the model can be consistent with other 'client-server' models."; } grouping netconf-client-initiate-stack-grouping { description "A reusable grouping for configuring a NETCONF client 'initiate' protocol stack for a single connection."; choice transport { mandatory true; description "Selects between available transports."; case ssh { if-feature "ssh-initiate"; container ssh { description "Specifies IP and SSH specific configuration for the connection."; container tcp-client-parameters { description "A wrapper around the TCP client parameters to avoid name collisions."; uses tcpc:tcp-client-grouping { refine "remote-port" { default "830"; description "The NETCONF client will attempt to connect to the IANA-assigned well-known port value for 'netconf-ssh' (830) if no value is specified."; } } } container ssh-client-parameters { description "A wrapper around the SSH client parameters to avoid name collisions."; uses sshc:ssh-client-grouping; } container netconf-client-parameters { description "A wrapper around the NETCONF client parameters to avoid name collisions. This container does not define any nodes. It exists as a potential augmentation target by other modules."; uses ncc:netconf-client-grouping; } } } case tls { if-feature "tls-initiate"; container tls { description "Specifies IP and TLS specific configuration for the connection."; container tcp-client-parameters { description "A wrapper around the TCP client parameters to avoid name collisions."; uses tcpc:tcp-client-grouping { refine "remote-port" { default "6513"; description "The NETCONF client will attempt to connect to the IANA-assigned well-known port value for 'netconf-tls' (6513) if no value is specified."; } } } container tls-client-parameters { must client-identity { description "NETCONF/TLS clients MUST pass some authentication credentials."; } description "A wrapper around the TLS client parameters to avoid name collisions."; uses tlsc:tls-client-grouping; } container netconf-client-parameters { description "A wrapper around the NETCONF client parameters to avoid name collisions. This container does not define any nodes. It exists as a potential augmentation target by other modules."; uses ncc:netconf-client-grouping; } } } } } // netconf-client-initiate-stack-grouping grouping netconf-client-listen-stack-grouping { description "A reusable grouping for configuring a NETCONF client 'listen' protocol stack for a single connection. The 'listen' stack supports call home connections, as described in RFC 8071"; reference "RFC 8071: NETCONF Call Home and RESTCONF Call Home"; choice transport { mandatory true; description "Selects between available transports."; case ssh { if-feature "ssh-listen"; container ssh { description "SSH-specific listening configuration for inbound connections."; container tcp-server-parameters { description "A wrapper around the TCP server parameters to avoid name collisions."; uses tcps:tcp-server-grouping { refine "local-port" { default "4334"; description "The NETCONF client will listen on the IANA- assigned well-known port for 'netconf-ch-ssh' (4334) if no value is specified."; } } } container ssh-client-parameters { description "A wrapper around the SSH client parameters to avoid name collisions."; uses sshc:ssh-client-grouping; } container netconf-client-parameters { description "A wrapper around the NETCONF client parameters to avoid name collisions. This container does not define any nodes. It exists as a potential augmentation target by other modules."; uses ncc:netconf-client-grouping; } } } case tls { if-feature "tls-listen"; container tls { description "TLS-specific listening configuration for inbound connections."; container tcp-server-parameters { description "A wrapper around the TCP server parameters to avoid name collisions."; uses tcps:tcp-server-grouping { refine "local-port" { default "4334"; description "The NETCONF client will listen on the IANA- assigned well-known port for 'netconf-ch-ssh' (4334) if no value is specified."; } } } container tls-client-parameters { must client-identity { description "NETCONF/TLS clients MUST pass some authentication credentials."; } description "A wrapper around the TLS client parameters to avoid name collisions."; uses tlsc:tls-client-grouping; } container netconf-client-parameters { description "A wrapper around the NETCONF client parameters to avoid name collisions. This container does not define any nodes. It exists as a potential augmentation target by other modules."; uses ncc:netconf-client-grouping; } } } } } // netconf-client-listen-stack-grouping grouping netconf-client-app-grouping { description "A reusable grouping for configuring a NETCONF client application that supports both 'initiate' and 'listen' protocol stacks for a multiplicity of connections."; container initiate { if-feature "ssh-initiate or tls-initiate"; presence "Indicates that client-initiated connections have been configured. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "Configures client initiating underlying TCP connections."; list netconf-server { key "name"; min-elements 1; description "List of NETCONF servers the NETCONF client is to maintain simultaneous connections with."; leaf name { type string; description "An arbitrary name for the NETCONF server."; } container endpoints { description "Container for the list of endpoints."; list endpoint { key "name"; min-elements 1; ordered-by user; description "A user-ordered list of endpoints that the NETCONF client will attempt to connect to in the specified sequence. Defining more than one enables high-availability."; leaf name { type string; description "An arbitrary name for the endpoint."; } uses netconf-client-initiate-stack-grouping; } // list endpoint } // container endpoints container connection-type { description "Indicates the NETCONF client's preference for how the NETCONF connection is maintained."; choice connection-type { mandatory true; description "Selects between available connection types."; case persistent-connection { container persistent { presence "Indicates that a persistent connection is to be maintained."; description "Maintain a persistent connection to the NETCONF server. If the connection goes down, immediately start trying to reconnect to the NETCONF server, using the reconnection strategy. This connection type minimizes any NETCONF server to NETCONF client data-transfer delay, albeit at the expense of holding resources longer."; } } case periodic-connection { container periodic { presence "Indicates that a periodic connection is to be maintained."; description "Periodically connect to the NETCONF server. This connection type increases resource utilization, albeit with increased delay in NETCONF server to NETCONF client interactions. The NETCONF client should close the underlying TCP connection upon completing planned activities. In the case that the previous connection is still active, establishing a new connection is NOT RECOMMENDED."; leaf period { type uint16; units "minutes"; default "60"; description "Duration of time between periodic connections."; } leaf anchor-time { type yang:date-and-time { // constrained to minute-level granularity pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}' + '(Z|[\+\-]\d{2}:\d{2})'; } description "Designates a timestamp before or after which a series of periodic connections are determined. The periodic connections occur at a whole multiple interval from the anchor time. For example, for an anchor time is 15 minutes past midnight and a period interval of 24 hours, then a periodic connection will occur 15 minutes past midnight everyday."; } leaf idle-timeout { type uint16; units "seconds"; default 120; // two minutes description "Specifies the maximum number of seconds that a NETCONF session may remain idle. A NETCONF session will be dropped if it is idle for an interval longer then this number of seconds. If set to zero, then the NETCONF client will never drop a session because it is idle."; } } } } } container reconnect-strategy { description "The reconnection strategy directs how a NETCONF client reconnects to a NETCONF server, after discovering its connection to the server has dropped, even if due to a reboot. The NETCONF client starts with the specified endpoint and tries to connect to it max-attempts times before trying the next endpoint in the list (round robin)."; leaf start-with { type enumeration { enum first-listed { description "Indicates that reconnections should start with the first endpoint listed."; } enum last-connected { description "Indicates that reconnections should start with the endpoint last connected to. If no previous connection has ever been established, then the first endpoint configured is used. NETCONF clients SHOULD be able to remember the last endpoint connected to across reboots."; } enum random-selection { description "Indicates that reconnections should start with a random endpoint."; } } default "first-listed"; description "Specifies which of the NETCONF server's endpoints the NETCONF client should start with when trying to connect to the NETCONF server."; } leaf max-attempts { type uint8 { range "1..max"; } default "3"; description "Specifies the number times the NETCONF client tries to connect to a specific endpoint before moving on to the next endpoint in the list (round robin)."; } } } // netconf-server } // initiate container listen { if-feature "ssh-listen or tls-listen"; presence "Indicates that client-listening ports have been configured. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "Configures the client to accept call-home TCP connections."; leaf idle-timeout { type uint16; units "seconds"; default "3600"; // one hour description "Specifies the maximum number of seconds that a NETCONF session may remain idle. A NETCONF session will be dropped if it is idle for an interval longer than this number of seconds. If set to zero, then the server will never drop a session because it is idle. Sessions that have a notification subscription active are never dropped."; } list endpoint { key "name"; min-elements 1; description "List of endpoints to listen for NETCONF connections."; leaf name { type string; description "An arbitrary name for the NETCONF listen endpoint."; } uses netconf-client-listen-stack-grouping; } // endpoint } // listen } // netconf-client-app-grouping // Protocol accessible node for clients that implement this module. container netconf-client { if-feature central-netconf-client-supported; uses netconf-client-app-grouping; description "Top-level container for NETCONF client configuration."; } } yuma123_2.14/netconf/modules/ietf-draft/ietf-tls-client.yang0000664000175000017500000004702714770023131024151 0ustar vladimirvladimirmodule ietf-tls-client { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-tls-client"; prefix tlsc; import ietf-netconf-acm { prefix nacm; reference "RFC 8341: Network Configuration Access Control Model"; } import ietf-crypto-types { prefix ct; reference "RFC AAAA: YANG Data Types and Groupings for Cryptography"; } import ietf-truststore { prefix ts; reference "RFC BBBB: A YANG Data Model for a Truststore"; } import ietf-keystore { prefix ks; reference "RFC CCCC: A YANG Data Model for a Keystore"; } import ietf-tls-common { prefix tlscmn; revision-date 2022-05-24; // stable grouping definitions reference "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG List: NETCONF WG list WG Web: https://datatracker.ietf.org/wg/netconf Author: Kent Watsen Author: Jeff Hartley Author: Gary Wu "; description "This module defines reusable groupings for TLS clients that can be used as a basis for specific TLS client instances. Copyright (c) 2022 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC FFFF (https://www.rfc-editor.org/info/rfcFFFF); see the RFC itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here."; revision 2022-05-24 { description "Initial version"; reference "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers"; } // Features feature tls-client-keepalives { description "Per socket TLS keepalive parameters are configurable for TLS clients on the server implementing this feature."; } feature client-ident-x509-cert { description "Indicates that the client supports identifying itself using X.509 certificates."; reference "RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile"; } feature client-ident-raw-public-key { description "Indicates that the client supports identifying itself using raw public keys."; reference "RFC 7250: Using Raw Public Keys in Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)"; } feature client-ident-tls12-psk { description "Indicates that the client supports identifying itself using TLS-1.2 PSKs (pre-shared or pairwise-symmetric keys)."; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)"; } feature client-ident-tls13-epsk { description "Indicates that the client supports identifying itself using TLS-1.3 External PSKs (pre-shared keys)."; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } feature server-auth-x509-cert { description "Indicates that the client supports authenticating servers using X.509 certificates."; reference "RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile"; } feature server-auth-raw-public-key { description "Indicates that the client supports authenticating servers using raw public keys."; reference "RFC 7250: Using Raw Public Keys in Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)"; } feature server-auth-tls12-psk { description "Indicates that the client supports authenticating servers using PSKs (pre-shared or pairwise-symmetric keys)."; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)"; } feature server-auth-tls13-epsk { description "Indicates that the client supports authenticating servers using TLS-1.3 External PSKs (pre-shared keys)."; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } // Groupings grouping tls-client-grouping { description "A reusable grouping for configuring a TLS client without any consideration for how an underlying TCP session is established. Note that this grouping uses fairly typical descendant node names such that a stack of 'uses' statements will have name conflicts. It is intended that the consuming data model will resolve the issue (e.g., by wrapping the 'uses' statement in a container called 'tls-client-parameters'). This model purposely does not do this itself so as to provide maximum flexibility to consuming models."; container client-identity { nacm:default-deny-write; presence "Indicates that a TLS-level client identity has been configured. This statement is present so the mandatory descendant do not imply that this node must be configured."; description "Identity credentials the TLS client MAY present when establishing a connection to a TLS server. If not configured, then client authentication is presumed to occur a protocol layer above TLS. When configured, and requested by the TLS server when establishing a TLS session, these credentials are passed in the Certificate message defined in Section 7.4.2 of RFC 5246 and Section 4.4.2 in RFC 8446."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3 RFC CCCC: A YANG Data Model for a Keystore"; choice auth-type { mandatory true; description "A choice amongst authentication types, of which one must be enabled (via its associated 'feature') and selected."; case certificate { if-feature "client-ident-x509-cert"; container certificate { description "Specifies the client identity using a certificate."; uses ks:local-or-keystore-end-entity-cert-with-key-grouping{ refine "local-or-keystore/local/local-definition" { must 'public-key-format' + ' = "ct:subject-public-key-info-format"'; } refine "local-or-keystore/keystore/keystore-reference" + "/asymmetric-key" { must 'deref(.)/../ks:public-key-format' + ' = "ct:subject-public-key-info-format"'; } } } } case raw-public-key { if-feature "client-ident-raw-public-key"; container raw-private-key { description "Specifies the client identity using a raw private key."; uses ks:local-or-keystore-asymmetric-key-grouping { refine "local-or-keystore/local/local-definition" { must 'public-key-format' + ' = "ct:subject-public-key-info-format"'; } refine "local-or-keystore/keystore" + "/keystore-reference" { must 'deref(.)/../ks:public-key-format' + ' = "ct:subject-public-key-info-format"'; } } } } case tls12-psk { if-feature "client-ident-tls12-psk"; container tls12-psk { description "Specifies the client identity using a PSK (pre-shared or pairwise-symmetric key)."; uses ks:local-or-keystore-symmetric-key-grouping; leaf id { type string; description "The key 'psk_identity' value used in the TLS 'ClientKeyExchange' message."; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)"; } } } case tls13-epsk { if-feature "client-ident-tls13-epsk"; container tls13-epsk { description "An External Pre-Shared Key (EPSK) is established or provisioned out-of-band, i.e., not from a TLS connection. An EPSK is a tuple of (Base Key, External Identity, Hash). External PSKs MUST NOT be imported for (D)TLS 1.2 or prior versions. When PSKs are provisioned out of band, the PSK identity and the KDF hash algorithm to be used with the PSK MUST also be provisioned. The structure of this container is designed to satisfy the requirements of RFC 8446 Section 4.2.11, the recommendations from I-D ietf-tls-external-psk-guidance Section 6, and the EPSK input fields detailed in I-D draft-ietf-tls-external-psk-importer Section 3.1. The base-key is based upon ks:local-or-keystore-symmetric-key-grouping in order to provide users with flexible and secure storage options."; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3 I-D.ietf-tls-external-psk-importer: Importing External PSKs for TLS I-D.ietf-tls-external-psk-guidance: Guidance for External PSK Usage in TLS"; uses ks:local-or-keystore-symmetric-key-grouping; leaf external-identity { type string; mandatory true; description "As per Section 4.2.11 of RFC 8446, and Section 4.1 of I-D. ietf-tls-external-psk-guidance: A sequence of bytes used to identify an EPSK. A label for a pre-shared key established externally."; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3 I-D.ietf-tls-external-psk-guidance: Guidance for External PSK Usage in TLS"; } leaf hash { type tlscmn:epsk-supported-hash; mandatory true; description "As per Section 4.2.11 of RFC 8446, for externally established PSKs, the Hash algorithm MUST be set when the PSK is established or default to SHA-256 if no such algorithm is defined. The server MUST ensure that it selects a compatible PSK (if any) and cipher suite. Each PSK MUST only be used with a single hash function."; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } leaf context { type string; description "As per Section 4.1 of I-D. ietf-tls-external-psk-guidance: Context may include information about peer roles or identities to mitigate Selfie-style reflection attacks [Selfie]. If the EPSK is a key derived from some other protocol or sequence of protocols, context MUST include a channel binding for the deriving protocols [RFC5056]. The details of this binding are protocol specific."; reference "I-D.ietf-tls-external-psk-importer: Importing External PSKs for TLS I-D.ietf-tls-external-psk-guidance: Guidance for External PSK Usage in TLS"; } leaf target-protocol { type uint16; description "As per Section 3.1 of I-D. ietf-tls-external-psk-guidance: The protocol for which a PSK is imported for use."; reference "I-D.ietf-tls-external-psk-importer: Importing External PSKs for TLS"; } leaf target-kdf { type uint16; description "As per Section 3.1 of I-D. ietf-tls-external-psk-guidance: The specific Key Derivation Function (KDF) for which a PSK is imported for use."; reference "I-D.ietf-tls-external-psk-importer: Importing External PSKs for TLS"; } } } } } // container client-identity container server-authentication { nacm:default-deny-write; must 'ca-certs or ee-certs or raw-public-keys or tls12-psks or tls13-epsks'; description "Specifies how the TLS client can authenticate TLS servers. Any combination of credentials is additive and unordered. Note that no configuration is required for PSK (pre-shared or pairwise-symmetric key) based authentication as the key is necessarily the same as configured in the '../client- identity' node."; container ca-certs { if-feature "server-auth-x509-cert"; presence "Indicates that CA certificates have been configured. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "A set of certificate authority (CA) certificates used by the TLS client to authenticate TLS server certificates. A server certificate is authenticated if it has a valid chain of trust to a configured CA certificate."; reference "RFC BBBB: A YANG Data Model for a Truststore"; uses ts:local-or-truststore-certs-grouping; } container ee-certs { if-feature "server-auth-x509-cert"; presence "Indicates that EE certificates have been configured. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "A set of server certificates (i.e., end entity certificates) used by the TLS client to authenticate certificates presented by TLS servers. A server certificate is authenticated if it is an exact match to a configured server certificate."; reference "RFC BBBB: A YANG Data Model for a Truststore"; uses ts:local-or-truststore-certs-grouping; } container raw-public-keys { if-feature "server-auth-raw-public-key"; presence "Indicates that raw public keys have been configured. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "A set of raw public keys used by the TLS client to authenticate raw public keys presented by the TLS server. A raw public key is authenticated if it is an exact match to a configured raw public key."; reference "RFC BBBB: A YANG Data Model for a Truststore"; uses ts:local-or-truststore-public-keys-grouping { refine "local-or-truststore/local/local-definition" + "/public-key" { must 'public-key-format' + ' = "ct:subject-public-key-info-format"'; } refine "local-or-truststore/truststore" + "/truststore-reference" { must 'deref(.)/../*/ts:public-key-format' + ' = "ct:subject-public-key-info-format"'; } } } leaf tls12-psks { if-feature "server-auth-tls12-psk"; type empty; description "Indicates that the TLS client can authenticate TLS servers using configure PSKs (pre-shared or pairwise-symmetric keys). No configuration is required since the PSK value is the same as PSK value configured in the 'client-identity' node."; } leaf tls13-epsks { if-feature "server-auth-tls13-epsk"; type empty; description "Indicates that the TLS client can authenticate TLS servers using configured external PSKs (pre-shared keys). No configuration is required since the PSK value is the same as PSK value configured in the 'client-identity' node."; } } // container server-authentication container hello-params { nacm:default-deny-write; if-feature "tlscmn:hello-params"; uses tlscmn:hello-params-grouping; description "Configurable parameters for the TLS hello message."; } // container hello-params container keepalives { nacm:default-deny-write; if-feature "tls-client-keepalives"; description "Configures the keepalive policy for the TLS client."; leaf peer-allowed-to-send { type empty; description "Indicates that the remote TLS server is allowed to send HeartbeatRequest messages, as defined by RFC 6520 to this TLS client."; reference "RFC 6520: Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS) Heartbeat Extension"; } container test-peer-aliveness { presence "Indicates that the TLS client proactively tests the aliveness of the remote TLS server."; description "Configures the keep-alive policy to proactively test the aliveness of the TLS server. An unresponsive TLS server is dropped after approximately max-wait * max-attempts seconds. The TLS client MUST send HeartbeatRequest messages, as defined by RFC 6520."; reference "RFC 6520: Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS) Heartbeat Extension"; leaf max-wait { type uint16 { range "1..max"; } units "seconds"; default "30"; description "Sets the amount of time in seconds after which if no data has been received from the TLS server, a TLS-level message will be sent to test the aliveness of the TLS server."; } leaf max-attempts { type uint8; default "3"; description "Sets the maximum number of sequential keep-alive messages that can fail to obtain a response from the TLS server before assuming the TLS server is no longer alive."; } } } } // grouping tls-client-grouping } yuma123_2.14/netconf/modules/ietf-draft/ietf-network-bridge.yang0000664000175000017500000000431014770023131025002 0ustar vladimirvladimirmodule ietf-network-bridge { namespace "urn:ietf:params:xml:ns:yang:ietf-network-bridge"; prefix netbr; import ietf-interfaces { prefix if; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: Editor: Vladimir Vassilev "; description "This module contains a collection of YANG definitions for description and management of network bridges. Copyright (c) 2020 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX; see the RFC itself for full legal notices."; revision 2021-02-17 { description "Initial revision."; reference "RFC XXXX: Network Bridge"; } typedef port-ref { type leafref { path "/if:interfaces/if:interface/netbr:port-name"; } description "This type is used by data models that need to reference configured bridge ports."; } augment "/if:interfaces/if:interface" { description "Bridge port specific data."; leaf port-name { type leafref { path "/netbr:bridge/netbr:ports/netbr:port/netbr:name"; } description "Reference to the bridge port"; } } container bridge { description "Bridge parameters."; container ports { description "Member ports."; list port { key "name"; unique "index"; description "The list of bridge ports on the device."; leaf name { type string; description "Name of the port."; } leaf index { type uint64; description "Index of the port."; } } } } } yuma123_2.14/netconf/modules/ietf-draft/ietf-crypto-types.yang0000664000175000017500000007660414770023131024560 0ustar vladimirvladimirmodule ietf-crypto-types { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-crypto-types"; prefix ct; import ietf-yang-types { prefix yang; reference "RFC 6991: Common YANG Data Types"; } import ietf-netconf-acm { prefix nacm; reference "RFC 8341: Network Configuration Access Control Model"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: https://datatracker.ietf.org/wg/netconf WG List: NETCONF WG list Author: Kent Watsen "; description "This module defines common YANG types for cryptographic applications. Copyright (c) 2021 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC AAAA (https://www.rfc-editor.org/info/rfcAAAA); see the RFC itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here."; revision 2022-05-24 { description "Initial version"; reference "RFC AAAA: YANG Data Types and Groupings for Cryptography"; } /****************/ /* Features */ /****************/ feature one-symmetric-key-format { description "Indicates that the server supports the 'one-symmetric-key-format' identity."; } feature one-asymmetric-key-format { description "Indicates that the server supports the 'one-asymmetric-key-format' identity."; } feature symmetrically-encrypted-value-format { description "Indicates that the server supports the 'symmetrically-encrypted-value-format' identity."; } feature asymmetrically-encrypted-value-format { description "Indicates that the server supports the 'asymmetrically-encrypted-value-format' identity."; } feature cms-enveloped-data-format { description "Indicates that the server supports the 'cms-enveloped-data-format' identity."; } feature cms-encrypted-data-format { description "Indicates that the server supports the 'cms-encrypted-data-format' identity."; } feature certificate-signing-request-generation { description "Indicates that the server implements the 'generate-certificate-signing-request' action."; } feature certificate-expiration-notification { description "Indicates that the server implements the 'certificate-expiration' notification."; } feature hidden-keys { description "Indicates that the server supports hidden keys."; } feature password-encryption { description "Indicates that the server supports password encryption."; } feature symmetric-key-encryption { description "Indicates that the server supports encryption of symmetric keys."; } feature private-key-encryption { description "Indicates that the server supports encryption of private keys."; } /*************************************************/ /* Base Identities for Key Format Structures */ /*************************************************/ identity symmetric-key-format { description "Base key-format identity for symmetric keys."; } identity public-key-format { description "Base key-format identity for public keys."; } identity private-key-format { description "Base key-format identity for private keys."; } /****************************************************/ /* Identities for Private Key Format Structures */ /****************************************************/ identity rsa-private-key-format { base private-key-format; description "Indicates that the private key value is encoded as an RSAPrivateKey (from RFC 3447)."; reference "RFC 3447: PKCS #1: RSA Cryptography Specifications Version 2.2"; } identity ec-private-key-format { base private-key-format; description "Indicates that the private key value is encoded as an ECPrivateKey (from RFC 5915)"; reference "RFC 5915: Elliptic Curve Private Key Structure"; } identity one-asymmetric-key-format { if-feature "one-asymmetric-key-format"; base private-key-format; description "Indicates that the private key value is a CMS OneAsymmetricKey structure, as defined in RFC 5958, encoded using ASN.1 distinguished encoding rules (DER), as specified in ITU-T X.690."; reference "RFC 5958: Asymmetric Key Packages ITU-T X.690: Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)."; } /***************************************************/ /* Identities for Public Key Format Structures */ /***************************************************/ identity ssh-public-key-format { base public-key-format; description "Indicates that the public key value is an SSH public key, as specified by RFC 4253, Section 6.6, i.e.: string certificate or public key format identifier byte[n] key/certificate data."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity subject-public-key-info-format { base public-key-format; description "Indicates that the public key value is a SubjectPublicKeyInfo structure, as described in RFC 5280 encoded using ASN.1 distinguished encoding rules (DER), as specified in ITU-T X.690."; reference "RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile ITU-T X.690: Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)."; } /******************************************************/ /* Identities for Symmetric Key Format Structures */ /******************************************************/ identity octet-string-key-format { base symmetric-key-format; description "Indicates that the key is encoded as a raw octet string. The length of the octet string MUST be appropriate for the associated algorithm's block size. How the associated algorithm is known is outside the scope of this module. This statement also applies when the octet string has been encrypted."; } identity one-symmetric-key-format { if-feature "one-symmetric-key-format"; base symmetric-key-format; description "Indicates that the private key value is a CMS OneSymmetricKey structure, as defined in RFC 6031, encoded using ASN.1 distinguished encoding rules (DER), as specified in ITU-T X.690."; reference "RFC 6031: Cryptographic Message Syntax (CMS) Symmetric Key Package Content Type ITU-T X.690: Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)."; } /*************************************************/ /* Identities for Encrypted Value Structures */ /*************************************************/ identity encrypted-value-format { description "Base format identity for encrypted values."; } identity symmetrically-encrypted-value-format { if-feature "symmetrically-encrypted-value-format"; base encrypted-value-format; description "Base format identity for symmetrically encrypted values."; } identity asymmetrically-encrypted-value-format { if-feature "asymmetrically-encrypted-value-format"; base encrypted-value-format; description "Base format identity for asymmetrically encrypted values."; } identity cms-encrypted-data-format { if-feature "cms-encrypted-data-format"; base symmetrically-encrypted-value-format; description "Indicates that the encrypted value conforms to the 'encrypted-data-cms' type with the constraint that the 'unprotectedAttrs' value is not set."; reference "RFC 5652: Cryptographic Message Syntax (CMS) ITU-T X.690: Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)."; } identity cms-enveloped-data-format { if-feature "cms-enveloped-data-format"; base asymmetrically-encrypted-value-format; description "Indicates that the encrypted value conforms to the 'enveloped-data-cms' type with the following constraints: The EnvelopedData structure MUST have exactly one 'RecipientInfo'. If the asymmetric key supports public key cryptography (e.g., RSA), then the 'RecipientInfo' must be a 'KeyTransRecipientInfo' with the 'RecipientIdentifier' using a 'subjectKeyIdentifier' with the value set using 'method 1' in RFC 7093 over the recipient's public key. Otherwise, if the asymmetric key supports key agreement (e.g., ECC), then the 'RecipientInfo' must be a 'KeyAgreeRecipientInfo'. The 'OriginatorIdentifierOrKey' value must use the 'OriginatorPublicKey' alternative. The 'UserKeyingMaterial' value must not be present. There must be exactly one 'RecipientEncryptedKeys' value having the 'KeyAgreeRecipientIdentifier' set to 'rKeyId' with the value set using 'method 1' in RFC 7093 over the recipient's public key."; reference "RFC 5652: Cryptographic Message Syntax (CMS) RFC 7093: Additional Methods for Generating Key Identifiers Values ITU-T X.690: Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)."; } /***************************************************/ /* Typedefs for ASN.1 structures from RFC 2986 */ /***************************************************/ typedef csr-info { type binary; description "A CertificationRequestInfo structure, as defined in RFC 2986, encoded using ASN.1 distinguished encoding rules (DER), as specified in ITU-T X.690."; reference "RFC 2986: PKCS #10: Certification Request Syntax Specification Version 1.7 ITU-T X.690: Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)."; } typedef csr { type binary; description "A CertificationRequest structure, as specified in RFC 2986, encoded using ASN.1 distinguished encoding rules (DER), as specified in ITU-T X.690."; reference "RFC 2986: PKCS #10: Certification Request Syntax Specification Version 1.7 ITU-T X.690: Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)."; } /***************************************************/ /* Typedefs for ASN.1 structures from RFC 5280 */ /***************************************************/ typedef x509 { type binary; description "A Certificate structure, as specified in RFC 5280, encoded using ASN.1 distinguished encoding rules (DER), as specified in ITU-T X.690."; reference "RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile ITU-T X.690: Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)."; } typedef crl { type binary; description "A CertificateList structure, as specified in RFC 5280, encoded using ASN.1 distinguished encoding rules (DER), as specified in ITU-T X.690."; reference "RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile ITU-T X.690: Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)."; } /***************************************************/ /* Typedefs for ASN.1 structures from RFC 6960 */ /***************************************************/ typedef oscp-request { type binary; description "A OCSPRequest structure, as specified in RFC 6960, encoded using ASN.1 distinguished encoding rules (DER), as specified in ITU-T X.690."; reference "RFC 6960: X.509 Internet Public Key Infrastructure Online Certificate Status Protocol - OCSP ITU-T X.690: Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)."; } typedef oscp-response { type binary; description "A OCSPResponse structure, as specified in RFC 6960, encoded using ASN.1 distinguished encoding rules (DER), as specified in ITU-T X.690."; reference "RFC 6960: X.509 Internet Public Key Infrastructure Online Certificate Status Protocol - OCSP ITU-T X.690: Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)."; } /***********************************************/ /* Typedefs for ASN.1 structures from 5652 */ /***********************************************/ typedef cms { type binary; description "A ContentInfo structure, as specified in RFC 5652, encoded using ASN.1 distinguished encoding rules (DER), as specified in ITU-T X.690."; reference "RFC 5652: Cryptographic Message Syntax (CMS) ITU-T X.690: Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)."; } typedef data-content-cms { type cms; description "A CMS structure whose top-most content type MUST be the data content type, as described by Section 4 in RFC 5652."; reference "RFC 5652: Cryptographic Message Syntax (CMS)"; } typedef signed-data-cms { type cms; description "A CMS structure whose top-most content type MUST be the signed-data content type, as described by Section 5 in RFC 5652."; reference "RFC 5652: Cryptographic Message Syntax (CMS)"; } typedef enveloped-data-cms { type cms; description "A CMS structure whose top-most content type MUST be the enveloped-data content type, as described by Section 6 in RFC 5652."; reference "RFC 5652: Cryptographic Message Syntax (CMS)"; } typedef digested-data-cms { type cms; description "A CMS structure whose top-most content type MUST be the digested-data content type, as described by Section 7 in RFC 5652."; reference "RFC 5652: Cryptographic Message Syntax (CMS)"; } typedef encrypted-data-cms { type cms; description "A CMS structure whose top-most content type MUST be the encrypted-data content type, as described by Section 8 in RFC 5652."; reference "RFC 5652: Cryptographic Message Syntax (CMS)"; } typedef authenticated-data-cms { type cms; description "A CMS structure whose top-most content type MUST be the authenticated-data content type, as described by Section 9 in RFC 5652."; reference "RFC 5652: Cryptographic Message Syntax (CMS)"; } /*********************************************************/ /* Typedefs for ASN.1 structures related to RFC 5280 */ /*********************************************************/ typedef trust-anchor-cert-x509 { type x509; description "A Certificate structure that MUST encode a self-signed root certificate."; } typedef end-entity-cert-x509 { type x509; description "A Certificate structure that MUST encode a certificate that is neither self-signed nor having Basic constraint CA true."; } /*********************************************************/ /* Typedefs for ASN.1 structures related to RFC 5652 */ /*********************************************************/ typedef trust-anchor-cert-cms { type signed-data-cms; description "A CMS SignedData structure that MUST contain the chain of X.509 certificates needed to authenticate the certificate presented by a client or end-entity. The CMS MUST contain only a single chain of certificates. The client or end-entity certificate MUST only authenticate to last intermediate CA certificate listed in the chain. In all cases, the chain MUST include a self-signed root certificate. In the case where the root certificate is itself the issuer of the client or end-entity certificate, only one certificate is present. This CMS structure MAY (as applicable where this type is used) also contain suitably fresh (as defined by local policy) revocation objects with which the device can verify the revocation status of the certificates. This CMS encodes the degenerate form of the SignedData structure that is commonly used to disseminate X.509 certificates and revocation objects (RFC 5280)."; reference "RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile."; } typedef end-entity-cert-cms { type signed-data-cms; description "A CMS SignedData structure that MUST contain the end entity certificate itself, and MAY contain any number of intermediate certificates leading up to a trust anchor certificate. The trust anchor certificate MAY be included as well. The CMS MUST contain a single end entity certificate. The CMS MUST NOT contain any spurious certificates. This CMS structure MAY (as applicable where this type is used) also contain suitably fresh (as defined by local policy) revocation objects with which the device can verify the revocation status of the certificates. This CMS encodes the degenerate form of the SignedData structure that is commonly used to disseminate X.509 certificates and revocation objects (RFC 5280)."; reference "RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile."; } /*****************/ /* Groupings */ /*****************/ grouping encrypted-value-grouping { description "A reusable grouping for a value that has been encrypted by a referenced symmetric or asymmetric key."; container encrypted-by { nacm:default-deny-write; description "An empty container enabling a reference to the key that encrypted the value to be augmented in. The referenced key MUST be a symmetric key or an asymmetric key. A symmetric key MUST be referenced via a leaf node called 'symmetric-key-ref'. An asymmetric key MUST be referenced via a leaf node called 'asymmetric-key-ref'. The leaf nodes MUST be direct descendants in the data tree, and MAY be direct descendants in the schema tree."; } leaf encrypted-value-format { type identityref { base encrypted-value-format; } mandatory true; description "Identifies the format of the 'encrypted-value' leaf. If 'encrypted-by' points to a symmetric key, then a 'symmetrically-encrypted-value-format' based identity MUST by set (e.g., cms-encrypted-data-format). If 'encrypted-by' points to an asymmetric key, then an 'asymmetrically-encrypted-value-format' based identity MUST by set (e.g., cms-enveloped-data-format)."; } leaf encrypted-value { nacm:default-deny-write; type binary; must '../encrypted-by'; mandatory true; description "The value, encrypted using the referenced symmetric or asymmetric key. The value MUST be encoded using the format associated with the 'encrypted-value-format' leaf."; } } grouping password-grouping { description "A password that MAY be encrypted."; choice password-type { nacm:default-deny-write; mandatory true; description "Choice between password types."; case cleartext-password { leaf cleartext-password { nacm:default-deny-all; type string; description "The cleartext value of the password."; } } case encrypted-password { if-feature "password-encryption"; container encrypted-password { description "A container for the encrypted password value."; uses encrypted-value-grouping; } } } } grouping symmetric-key-grouping { description "A symmetric key."; leaf key-format { nacm:default-deny-write; type identityref { base symmetric-key-format; } description "Identifies the symmetric key's format. Implementations SHOULD ensure that the incoming symmetric key value is encoded in the specified format. For encrypted keys, the value is the same as it would have been if the key were not encrypted."; } choice key-type { nacm:default-deny-write; mandatory true; description "Choice between key types."; case cleartext-key { leaf cleartext-key { nacm:default-deny-all; type binary; must '../key-format'; description "The binary value of the key. The interpretation of the value is defined by the 'key-format' field."; } } case hidden-key { if-feature "hidden-keys"; leaf hidden-key { type empty; must 'not(../key-format)'; description "A hidden key. How such keys are created is outside the scope of this module."; } } case encrypted-key { if-feature "symmetric-key-encryption"; container encrypted-key { must '../key-format'; description "A container for the encrypted symmetric key value. The interpretation of the 'encrypted-value' node is via the 'key-format' node"; uses encrypted-value-grouping; } } } } grouping public-key-grouping { description "A public key."; leaf public-key-format { nacm:default-deny-write; type identityref { base public-key-format; } mandatory true; description "Identifies the public key's format. Implementations SHOULD ensure that the incoming public key value is encoded in the specified format."; } leaf public-key { nacm:default-deny-write; type binary; mandatory true; description "The binary value of the public key. The interpretation of the value is defined by 'public-key-format' field."; } } grouping asymmetric-key-pair-grouping { description "A private key and its associated public key. Implementations SHOULD ensure that the two keys are a matching pair."; uses public-key-grouping; leaf private-key-format { nacm:default-deny-write; type identityref { base private-key-format; } description "Identifies the private key's format. Implementations SHOULD ensure that the incoming private key value is encoded in the specified format. For encrypted keys, the value is the same as it would have been if the key were not encrypted."; } choice private-key-type { nacm:default-deny-write; mandatory true; description "Choice between key types."; case cleartext-private-key { leaf cleartext-private-key { nacm:default-deny-all; type binary; must '../private-key-format'; description "The value of the binary key The key's value is interpreted by the 'private-key-format' field."; } } case hidden-private-key { if-feature "hidden-keys"; leaf hidden-private-key { type empty; must 'not(../private-key-format)'; description "A hidden key. How such keys are created is outside the scope of this module."; } } case encrypted-private-key { if-feature "private-key-encryption"; container encrypted-private-key { must '../private-key-format'; description "A container for the encrypted asymmetric private key value. The interpretation of the 'encrypted-value' node is via the 'private-key-format' node"; uses encrypted-value-grouping; } } } } grouping certificate-expiration-grouping { description "A notification for when a certificate is about to, or already has, expired."; notification certificate-expiration { if-feature "certificate-expiration-notification"; description "A notification indicating that the configured certificate is either about to expire or has already expired. When to send notifications is an implementation specific decision, but it is RECOMMENDED that a notification be sent once a month for 3 months, then once a week for four weeks, and then once a day thereafter until the issue is resolved."; leaf expiration-date { type yang:date-and-time; mandatory true; description "Identifies the expiration date on the certificate."; } } } grouping trust-anchor-cert-grouping { description "A trust anchor certificate, and a notification for when it is about to (or already has) expire."; leaf cert-data { nacm:default-deny-write; type trust-anchor-cert-cms; description "The binary certificate data for this certificate."; } uses certificate-expiration-grouping; } grouping end-entity-cert-grouping { description "An end entity certificate, and a notification for when it is about to (or already has) expire. Implementations SHOULD assert that, where used, the end entity certificate contains the expected public key."; leaf cert-data { nacm:default-deny-write; type end-entity-cert-cms; description "The binary certificate data for this certificate."; } uses certificate-expiration-grouping; } grouping generate-csr-grouping { description "Defines the 'generate-certificate-signing-request' action."; action generate-certificate-signing-request { if-feature "certificate-signing-request-generation"; nacm:default-deny-all; description "Generates a certificate signing request structure for the associated asymmetric key using the passed subject and attribute values. This action statement is only available when the associated 'public-key-format' node's value is 'subject-public-key-info-format'."; reference "RFC 6125: Representation and Verification of Domain-Based Application Service Identity within Internet Public Key Infrastructure Using X.509 (PKIX) Certificates in the Context of Transport Layer Security (TLS)"; input { leaf csr-info { type ct:csr-info; mandatory true; description "A CertificationRequestInfo structure, as defined in RFC 2986. Enables the client to provide a fully-populated CertificationRequestInfo structure that the server only needs to sign in order to generate the complete 'CertificationRequest' structure to return in the 'output'. The 'AlgorithmIdentifier' field contained inside the 'SubjectPublicKeyInfo' field MUST be one known to be supported by the device."; reference "RFC 2986: PKCS #10: Certification Request Syntax Specification RFC AAAA: YANG Data Types and Groupings for Cryptography"; } } output { leaf certificate-signing-request { type ct:csr; mandatory true; description "A CertificationRequest structure, as defined in RFC 2986."; reference "RFC 2986: PKCS #10: Certification Request Syntax Specification RFC AAAA: YANG Data Types and Groupings for Cryptography"; } } } } // generate-csr-grouping grouping asymmetric-key-pair-with-cert-grouping { description "A private/public key pair and an associated certificate. Implementations SHOULD assert that certificates contain the matching public key."; uses asymmetric-key-pair-grouping; uses end-entity-cert-grouping; uses generate-csr-grouping; } // asymmetric-key-pair-with-cert-grouping grouping asymmetric-key-pair-with-certs-grouping { description "A private/public key pair and associated certificates. Implementations SHOULD assert that certificates contain the matching public key."; uses asymmetric-key-pair-grouping; container certificates { nacm:default-deny-write; description "Certificates associated with this asymmetric key."; list certificate { key "name"; description "A certificate for this asymmetric key."; leaf name { type string; description "An arbitrary name for the certificate."; } uses end-entity-cert-grouping { refine "cert-data" { mandatory true; } } } } uses generate-csr-grouping; } // asymmetric-key-pair-with-certs-grouping } yuma123_2.14/netconf/modules/ietf-draft/ietf-tcp-common.yang0000664000175000017500000000761614770023131024147 0ustar vladimirvladimirmodule ietf-tcp-common { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-tcp-common"; prefix tcpcmn; organization "IETF NETCONF (Network Configuration) Working Group and the IETF TCP Maintenance and Minor Extensions (TCPM) Working Group"; contact "WG Web: https://datatracker.ietf.org/wg/netconf https://datatracker.ietf.org/wg/tcpm WG List: NETCONF WG list TCPM WG list Authors: Kent Watsen Michael Scharf "; description "This module defines reusable groupings for TCP commons that can be used as a basis for specific TCP common instances. Copyright (c) 2022 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC DDDD (https://www.rfc-editor.org/info/rfcDDDD); see the RFC itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here."; revision 2022-05-24 { description "Initial version"; reference "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; } // Features feature keepalives-supported { description "Indicates that keepalives are supported."; } // Groupings grouping tcp-common-grouping { description "A reusable grouping for configuring TCP parameters common to TCP connections as well as the operating system as a whole."; container keepalives { if-feature "keepalives-supported"; presence "Indicates that keepalives are enabled. This statement is present so the mandatory descendant nodes do not imply that this node must be configured."; description "Configures the keep-alive policy, to proactively test the aliveness of the TCP peer. An unresponsive TCP peer is dropped after approximately (idle-time + max-probes * probe-interval) seconds."; leaf idle-time { type uint16 { range "1..max"; } units "seconds"; mandatory true; description "Sets the amount of time after which if no data has been received from the TCP peer, a TCP-level probe message will be sent to test the aliveness of the TCP peer. Two hours (7200 seconds) is safe value, per RFC 1122."; reference "RFC 1122: Requirements for Internet Hosts -- Communication Layers"; } leaf max-probes { type uint16 { range "1..max"; } mandatory true; description "Sets the maximum number of sequential keep-alive probes that can fail to obtain a response from the TCP peer before assuming the TCP peer is no longer alive."; } leaf probe-interval { type uint16 { range "1..max"; } units "seconds"; mandatory true; description "Sets the time interval between failed probes. The interval SHOULD be significantly longer than one second in order to avoid harm on a congested link."; } } // container keepalives } // grouping tcp-common-grouping } yuma123_2.14/netconf/modules/ietf-draft/ietf-truststore.yang0000664000175000017500000002516014770023131024323 0ustar vladimirvladimirmodule ietf-truststore { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-truststore"; prefix ts; import ietf-netconf-acm { prefix nacm; reference "RFC 8341: Network Configuration Access Control Model"; } import ietf-crypto-types { prefix ct; reference "RFC AAAA: YANG Data Types and Groupings for Cryptography"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web : https://datatracker.ietf.org/wg/netconf WG List : NETCONF WG list Author : Kent Watsen "; description "This module defines a 'truststore' to centralize management of trust anchors including certificates and public keys. Copyright (c) 2021 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC BBBB (https://www.rfc-editor.org/info/rfcBBBB); see the RFC itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here."; revision 2022-05-24 { description "Initial version"; reference "RFC BBBB: A YANG Data Model for a Truststore"; } /****************/ /* Features */ /****************/ feature central-truststore-supported { description "The 'central-truststore-supported' feature indicates that the server supports the truststore (i.e., implements the 'ietf-truststore' module)."; } feature local-definitions-supported { description "The 'local-definitions-supported' feature indicates that the server supports locally-defined trust anchors."; } feature certificates { description "The 'certificates' feature indicates that the server implements the /truststore/certificate-bags subtree."; } feature public-keys { description "The 'public-keys' feature indicates that the server implements the /truststore/public-key-bags subtree."; } /****************/ /* Typedefs */ /****************/ typedef certificate-bag-ref { type leafref { path "/ts:truststore/ts:certificate-bags/" + "ts:certificate-bag/ts:name"; } description "This typedef defines a reference to a certificate bag in the truststore, when this module is implemented."; } typedef certificate-ref { type leafref { path "/ts:truststore/ts:certificate-bags/ts:certificate-bag" + "[ts:name = current()/../ts:certificate-bag]/" + "ts:certificate/ts:name"; } description "This typedef defines a reference to a specific certificate in a certificate bag in the truststore, when this module is implemented. This typedef requires that there exist a sibling 'leaf' node called 'certificate-bag' that SHOULD have the typedef 'certificate-bag-ref'."; } typedef public-key-bag-ref { type leafref { path "/ts:truststore/ts:public-key-bags/" + "ts:public-key-bag/ts:name"; } description "This typedef defines a reference to a public key bag in the truststore, when this module is implemented."; } typedef public-key-ref { type leafref { path "/ts:truststore/ts:public-key-bags/ts:public-key-bag" + "[ts:name = current()/../ts:public-key-bag]/" + "ts:public-key/ts:name"; } description "This typedef defines a reference to a specific public key in a public key bag in the truststore, when this module is implemented. This typedef requires that there exist a sibling 'leaf' node called 'public-key-bag' that SHOULD have the typedef 'public-key-bag-ref'."; } /*****************/ /* Groupings */ /*****************/ grouping local-or-truststore-certs-grouping { description "A grouping that allows the certificates to be either configured locally, within the using data model, or be a reference to a certificate bag stored in the truststore. Servers that do not 'implement' this module, and hence 'central-truststore-supported' is not defined, SHOULD augment in custom 'case' statements enabling references to the alternate truststore locations."; choice local-or-truststore { nacm:default-deny-write; mandatory true; description "A choice between an inlined definition and a definition that exists in the truststore."; case local { if-feature "local-definitions-supported"; container local-definition { description "A container for locally configured trust anchor certificates."; list certificate { key "name"; min-elements 1; description "A trust anchor certificate."; leaf name { type string; description "An arbitrary name for this certificate."; } uses ct:trust-anchor-cert-grouping { refine "cert-data" { mandatory true; } } } } } case truststore { if-feature "central-truststore-supported"; if-feature "certificates"; leaf truststore-reference { type ts:certificate-bag-ref; description "A reference to a certificate bag that exists in the truststore, when this module is implemented."; } } } } grouping local-or-truststore-public-keys-grouping { description "A grouping that allows the public keys to be either configured locally, within the using data model, or be a reference to a public key bag stored in the truststore. Servers that do not 'implement' this module, and hence 'central-truststore-supported' is not defined, SHOULD augment in custom 'case' statements enabling references to the alternate truststore locations."; choice local-or-truststore { nacm:default-deny-write; mandatory true; description "A choice between an inlined definition and a definition that exists in the truststore."; case local { if-feature "local-definitions-supported"; container local-definition { description "A container to hold local public key definitions."; list public-key { key "name"; description "A public key definition."; leaf name { type string; description "An arbitrary name for this public key."; } uses ct:public-key-grouping; } } } case truststore { if-feature "central-truststore-supported"; if-feature "public-keys"; leaf truststore-reference { type ts:public-key-bag-ref; description "A reference to a bag of public keys that exists in the truststore, when this module is implemented."; } } } } grouping truststore-grouping { description "A grouping definition that enables use in other contexts. Where used, implementations MUST augment new 'case' statements into the various local-or-truststore 'choice' statements to supply leafrefs to the model-specific location(s)."; container certificate-bags { nacm:default-deny-write; if-feature "certificates"; description "A collection of certificate bags."; list certificate-bag { key "name"; description "A bag of certificates. Each bag of certificates SHOULD be for a specific purpose. For instance, one bag could be used to authenticate a specific set of servers, while another could be used to authenticate a specific set of clients."; leaf name { type string; description "An arbitrary name for this bag of certificates."; } leaf description { type string; description "A description for this bag of certificates. The intended purpose for the bag SHOULD be described."; } list certificate { key "name"; description "A trust anchor certificate."; leaf name { type string; description "An arbitrary name for this certificate."; } uses ct:trust-anchor-cert-grouping { refine "cert-data" { mandatory true; } } } } } container public-key-bags { nacm:default-deny-write; if-feature "public-keys"; description "A collection of public key bags."; list public-key-bag { key "name"; description "A bag of public keys. Each bag of keys SHOULD be for a specific purpose. For instance, one bag could be used authenticate a specific set of servers, while another could be used to authenticate a specific set of clients."; leaf name { type string; description "An arbitrary name for this bag of public keys."; } leaf description { type string; description "A description for this bag public keys. The intended purpose for the bag SHOULD be described."; } list public-key { key "name"; description "A public key."; leaf name { type string; description "An arbitrary name for this public key."; } uses ct:public-key-grouping; } } } } /*********************************/ /* Protocol accessible nodes */ /*********************************/ container truststore { if-feature central-truststore-supported; nacm:default-deny-write; description "The truststore contains bags of certificates and public keys."; uses truststore-grouping; } } yuma123_2.14/netconf/modules/ietf-draft/iana-ssh-public-key-algs.yang0000664000175000017500000002560714770023131025637 0ustar vladimirvladimirmodule iana-ssh-public-key-algs { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:iana-ssh-public-key-algs"; prefix sshpka; organization "Internet Assigned Numbers Authority (IANA)"; contact "Postal: ICANN 12025 Waterfront Drive, Suite 300 Los Angeles, CA 90094-2536 United States of America Tel: +1 310 301 5800 Email: iana@iana.org"; description "This module defines identities for the public key algorithms defined in the 'Public Key Algorithm Names' sub-registry of the 'Secure Shell (SSH) Protocol Parameters' registry maintained by IANA. Copyright (c) 2021 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). The initial version of this YANG module is part of RFC EEEE (https://www.rfc-editor.org/info/rfcEEEE); see the RFC itself for full legal notices."; revision 2021-06-01 { description "Initial version"; reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } // Typedefs typedef public-key-algorithm-ref { type identityref { base "public-key-alg-base"; } description "A reference to a SSH public key algorithm identifier."; } // Identities identity public-key-alg-base { description "Base identity used to identify public key algorithms."; } identity ssh-dss { base public-key-alg-base; description "SSH-DSS"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity ssh-rsa { base public-key-alg-base; description "SSH-RSA"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity rsa-sha2-256 { base public-key-alg-base; description "RSA-SHA2-256"; reference "RFC 8332: Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol"; } identity rsa-sha2-512 { base public-key-alg-base; description "RSA-SHA2-512"; reference "RFC 8332: Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol"; } identity spki-sign-rsa { base public-key-alg-base; description "SPKI-SIGN-RSA"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity spki-sign-dss { base public-key-alg-base; description "SPKI-SIGN-DSS"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity pgp-sign-rsa { base public-key-alg-base; description "PGP-SIGN-RSA"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity pgp-sign-dss { base public-key-alg-base; description "PGP-SIGN-DSS"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity null { base public-key-alg-base; description "NULL"; reference "RFC 4462: Generic Security Service Application Program Interface (GSS-API) Authentication and Key Exchange for the Secure Shell (SSH) Protocol"; } identity ecdsa-sha2-nistp256 { base public-key-alg-base; description "ECDSA-SHA2-NISTP256 (secp256r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-nistp384 { base public-key-alg-base; description "ECDSA-SHA2-NISTP384 (secp384r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-nistp521 { base public-key-alg-base; description "ECDSA-SHA2-NISTP521 (secp521r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-1.3.132.0.1 { base public-key-alg-base; description "ECDSA-SHA2-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-1.2.840.10045.3.1.1 { base public-key-alg-base; description "ECDSA-SHA2-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-1.3.132.0.33 { base public-key-alg-base; description "ECDSA-SHA2-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-1.3.132.0.26 { base public-key-alg-base; description "ECDSA-SHA2-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-1.3.132.0.27 { base public-key-alg-base; description "ECDSA-SHA2-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-1.3.132.0.16 { base public-key-alg-base; description "ECDSA-SHA2-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-1.3.132.0.36 { base public-key-alg-base; description "ECDSA-SHA2-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-1.3.132.0.37 { base public-key-alg-base; description "ECDSA-SHA2-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-1.3.132.0.38 { base public-key-alg-base; description "ECDSA-SHA2-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity x509v3-ssh-dss { base public-key-alg-base; description "X509V3-SSH-DSS"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ssh-rsa { base public-key-alg-base; description "X509V3-SSH-RSA"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-rsa2048-sha256 { base public-key-alg-base; description "X509V3-RSA2048-SHA256"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-nistp256 { base public-key-alg-base; description "X509V3-ECDSA-SHA2-NISTP256 (secp256r1)"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-nistp384 { base public-key-alg-base; description "X509V3-ECDSA-SHA2-NISTP384 (secp384r1)"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-nistp521 { base public-key-alg-base; description "X509V3-ECDSA-SHA2-NISTP521 (secp521r1)"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-1.3.132.0.1 { base public-key-alg-base; description "X509V3-ECDSA-SHA2-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-1.2.840.10045.3.1.1 { base public-key-alg-base; description "X509V3-ECDSA-SHA2-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-1.3.132.0.33 { base public-key-alg-base; description "X509V3-ECDSA-SHA2-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-1.3.132.0.26 { base public-key-alg-base; description "X509V3-ECDSA-SHA2-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-1.3.132.0.27 { base public-key-alg-base; description "X509V3-ECDSA-SHA2-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-1.3.132.0.16 { base public-key-alg-base; description "X509V3-ECDSA-SHA2-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-1.3.132.0.36 { base public-key-alg-base; description "X509V3-ECDSA-SHA2-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-1.3.132.0.37 { base public-key-alg-base; description "X509V3-ECDSA-SHA2-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-1.3.132.0.38 { base public-key-alg-base; description "X509V3-ECDSA-SHA2-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity ssh-ed25519 { base public-key-alg-base; description "SSH-ED25519"; reference "RFC 8709: Ed25519 and Ed448 Public Key Algorithms for the Secure Shell (SSH) Protocol"; } identity ssh-ed448 { base public-key-alg-base; description "SSH-ED448"; reference "RFC 8709: Ed25519 and Ed448 Public Key Algorithms for the Secure Shell (SSH) Protocol"; } // Protocol-accessible Nodes container supported-algorithms { config false; description "A container for a list of public key algorithms supported by the server."; leaf-list supported-algorithm { type public-key-algorithm-ref; description "A public key algorithm supported by the server."; } } } yuma123_2.14/netconf/modules/ietf-draft/ietf-if-extensions.yang0000664000175000017500000004021614770023131024657 0ustar vladimirvladimirmodule ietf-if-extensions { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-if-extensions"; prefix if-ext; import ietf-yang-types { prefix yang; reference "RFC 6991: Common YANG Data Types"; } import ietf-interfaces { prefix if; reference "RFC 8343: A YANG Data Model For Interface Management"; } import iana-if-type { prefix ianaift; reference "RFC 7224: IANA Interface Type YANG Module"; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: Editor: Robert Wilton "; description "This module contains common definitions for extending the IETF interface YANG model (RFC 8343) with common configurable layer 2 properties. Copyright (c) 2019 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); see the RFC itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here."; revision 2019-11-04 { description "Initial revision."; reference "RFC XXX, Common Interface Extension YANG Data Models"; } feature carrier-delay { description "This feature indicates that configurable interface carrier delay is supported, which is a feature is used to limit the propagation of very short interface link state flaps."; reference "RFC XXX, Section 2.1 Carrier Delay"; } feature dampening { description "This feature indicates that the device supports interface dampening, which is a feature that is used to limit the propagation of interface link state flaps over longer periods."; reference "RFC XXX, Section 2.2 Dampening"; } feature loopback { description "This feature indicates that configurable interface loopback is supported."; reference "RFC XXX, Section 2.4 Loopback"; } feature max-frame-size { description "This feature indicates that the device supports configuring or reporting the maximum frame size on interfaces."; reference "RFC XXX, Section 2.5 Maximum Frame Size"; } feature sub-interfaces { description "This feature indicates that the device supports the instantiation of sub-interfaces. Sub-interfaces are defined as logical child interfaces that allow features and forwarding decisions to be applied to a subset of the traffic processed on the specified parent interface."; reference "RFC XXX, Section 2.6 Sub-interface"; } /* * Define common identities to help allow interface types to be * assigned properties. */ identity sub-interface { description "Base type for generic sub-interfaces. New or custom interface types can derive from this type to inherit generic sub-interface configuration."; reference "RFC XXX, Section 2.6 Sub-interface"; } identity ethSubInterface{ base ianaift:l2vlan; base sub-interface; description "This identity represents the child sub-interface of any interface types that uses Ethernet framing (with or without 802.1Q tagging)."; } identity loopback { description "Base identity for interface loopback options"; reference "RFC XXX, Section 2.4"; } identity internal { base loopback; description "All egress traffic on the interface is internally looped back within the interface to be received on the ingress path."; reference "RFC XXX, Section 2.4"; } identity line { base loopback; description "All ingress traffic received on the interface is internally looped back within the interface to the egress path."; reference "RFC XXX, Section 2.4"; } identity connector { base loopback; description "The interface has a physical loopback connector attached that loops all egress traffic back into the interface's ingress path, with equivalent semantics to loopback internal."; reference "RFC XXX, Section 2.4"; } identity forwarding-mode { description "Base identity for forwarding-mode options."; reference "RFC XXX, Section 2.7"; } identity physical { base forwarding-mode; description "Physical layer forwarding. This includes DWDM or OTN based optical switching."; reference "RFC XXX, Section 2.7"; } identity data-link { base forwarding-mode; description "Layer 2 based forwarding, such as Ethernet/VLAN based switching, or L2VPN services."; reference "RFC XXX, Section 2.7"; } identity network { base forwarding-mode; description "Network layer based forwarding, such as IP, MPLS, or L3VPNs."; reference "RFC XXX, Section 2.7"; } /* * Augments the IETF interfaces model with leaves to configure * and monitor carrier-delay on an interface. */ augment "/if:interfaces/if:interface" { description "Augments the IETF interface model with optional common interface level commands that are not formally covered by any specific standard."; /* * Defines standard YANG for the Carrier Delay feature. */ container carrier-delay { if-feature "carrier-delay"; description "Holds carrier delay related feature configuration."; leaf down { type uint32; units milliseconds; description "Delays the propagation of a 'loss of carrier signal' event that would cause the interface state to go down, i.e. the command allows short link flaps to be suppressed. The configured value indicates the minimum time interval (in milliseconds) that the carrier signal must be continuously down before the interface state is brought down. If not configured, the behaviour on loss of carrier signal is vendor/interface specific, but with the general expectation that there should be little or no delay."; } leaf up { type uint32; units milliseconds; description "Defines the minimum time interval (in milliseconds) that the carrier signal must be continuously present and error free before the interface state is allowed to transition from down to up. If not configured, the behaviour is vendor/interface specific, but with the general expectation that sufficient default delay should be used to ensure that the interface is stable when enabled before being reported as being up. Configured values that are too low for the hardware capabilties may be rejected."; } leaf carrier-transitions { type yang:counter64; units transitions; config false; description "Defines the number of times the underlying carrier state has changed to, or from, state up. This counter should be incremented even if the high layer interface state changes are being suppressed by a running carrier-delay timer."; } leaf timer-running { type enumeration { enum none { description "No carrier delay timer is running."; } enum up { description "Carrier-delay up timer is running. The underlying carrier state is up, but interface state is not reported as up."; } enum down { description "Carrier-delay down timer is running. Interface state is reported as up, but the underlying carrier state is actually down."; } } config false; description "Reports whether a carrier delay timer is actively running, in which case the interface state does not match the underlying carrier state."; } reference "RFC XXX, Section 2.1 Carrier Delay"; } /* * Augments the IETF interfaces model with a container to hold * generic interface dampening */ container dampening { if-feature "dampening"; presence "Enable interface link flap dampening with default settings (that are vendor/device specific)."; description "Interface dampening limits the propagation of interface link state flaps over longer periods."; reference "RFC XXX, Section 2.2 Dampening"; leaf half-life { type uint32; units seconds; description "The time (in seconds) after which a penalty would be half its original value. Once the interface has been assigned a penalty, the penalty is decreased at a decay rate equivalent to the half-life. For some devices, the allowed values may be restricted to particular multiples of seconds. The default value is vendor/device specific."; reference "RFC XXX, Section 2.3.2 Half-Life Period"; } leaf reuse { type uint32; description "Penalty value below which a stable interface is unsuppressed (i.e. brought up) (no units). The default value is vendor/device specific. The penalty value for a link up->down state change is 1000 units."; reference "RFC XXX, Section 2.2.3 Reuse Threshold"; } leaf suppress { type uint32; description "Limit at which an interface is suppressed (i.e. held down) when its penalty exceeds that limit (no units). The value must be greater than the reuse threshold. The default value is vendor/device specific. The penalty value for a link up->down state change is 1000 units."; reference "RFC XXX, Section 2.2.1 Suppress Threshold"; } leaf max-suppress-time { type uint32; units seconds; description "Maximum time (in seconds) that an interface can be suppressed before being unsuppressed if no further link up->down state change penalties have been applied. This value effectively acts as a ceiling that the penalty value cannot exceed. The default value is vendor/device specific."; reference "RFC XXX, Section 2.2.4 Maximum Suppress Time"; } leaf penalty { type uint32; config false; description "The current penalty value for this interface. When the penalty value exceeds the 'suppress' leaf then the interface is suppressed (i.e. held down)."; reference "RFC XXX, Section 2.2 Dampening"; } leaf suppressed { type boolean; config false; description "Represents whether the interface is suppressed (i.e. held down) because the 'penalty' leaf value exceeds the 'suppress' leaf."; reference "RFC XXX, Section 2.2 Dampening"; } leaf time-remaining { when '../suppressed = "true"' { description "Only suppressed interfaces have a time remaining."; } type uint32; units seconds; config false; description "For a suppressed interface, this leaf represents how long (in seconds) that the interface will remain suppressed before it is allowed to go back up again."; reference "RFC XXX, Section 2.2 Dampening"; } } /* * Various types of interfaces support a configurable layer 2 * encapsulation, any that are supported by YANG should be * listed here. * * Different encapsulations can hook into the common encaps-type * choice statement. */ container encapsulation { when "derived-from-or-self(../if:type, 'ianaift:ethernetCsmacd') or derived-from-or-self(../if:type, 'ianaift:ieee8023adLag') or derived-from-or-self(../if:type, 'ianaift:pos') or derived-from-or-self(../if:type, 'ianaift:atmSubInterface') or derived-from-or-self(../if:type, 'ethSubInterface')" { description "All interface types that can have a configurable L2 encapsulation."; } description "Holds the OSI layer 2 encapsulation associated with an interface."; choice encaps-type { description "Extensible choice of layer 2 encapsulations"; reference "RFC XXX, Section 2.3 Encapsulation"; } } /* * Various types of interfaces support loopback configuration, * any that are supported by YANG should be listed here. */ leaf loopback { when "derived-from-or-self(../if:type, 'ianaift:ethernetCsmacd') or derived-from-or-self(../if:type, 'ianaift:sonet') or derived-from-or-self(../if:type, 'ianaift:atm') or derived-from-or-self(../if:type, 'ianaift:otnOtu')" { description "All interface types that support loopback configuration."; } if-feature "loopback"; type identityref { base loopback; } description "Enables traffic loopback."; reference "RFC XXX, Section 2.4 Loopback"; } /* * Allows the maximum frame size to be configured or reported. */ leaf max-frame-size { if-feature "max-frame-size"; type uint32 { range "64 .. max"; } description "The maximum size of layer 2 frames that may be transmitted or received on the interface (including any frame header, maximum frame payload size, and frame checksum sequence). If configured, the max-frame-size also limits the maximum frame size of any child sub-interfaces. The MTU available to higher layer protocols is restricted to the maximum frame payload size, and MAY be further restricted by explicit layer 3 or protocol specific MTU configuration."; reference "RFC XXX, Section 2.5 Maximum Frame Size"; } /* * Augments the IETF interfaces model with a leaf that indicates * which mode, or layer, is being used to forward the traffic. */ leaf forwarding-mode { type identityref { base forwarding-mode; } config false; description "The forwarding mode that the interface is operating in."; reference "RFC XXX, Section 2.7 Forwarding Mode"; } } /* * Add generic support for sub-interfaces. * * This should be extended to cover all interface types that are * child interfaces of other interfaces. */ augment "/if:interfaces/if:interface" { when "derived-from(if:type, 'sub-interface') or derived-from-or-self(if:type, 'ianaift:atmSubInterface') or derived-from-or-self(if:type, 'ianaift:frameRelay')" { description "Any ianaift:types that explicitly represent sub-interfaces or any types that derive from the sub-interface identity."; } if-feature "sub-interfaces"; description "Adds a parent interface field to interfaces that model sub-interfaces."; leaf parent-interface { type if:interface-ref; mandatory true; description "This is the reference to the parent interface of this sub-interface."; reference "RFC XXX, Section 2.6 Sub-interface"; } } } yuma123_2.14/netconf/modules/ietf-draft/ietf-network-bridge-flows.yang0000664000175000017500000003134114770023131026136 0ustar vladimirvladimirmodule ietf-network-bridge-flows { namespace "urn:ietf:params:xml:ns:yang:ietf-network-bridge-flows"; prefix flow; import ietf-network-bridge { prefix netbr; } import ietf-inet-types { prefix inet; } import ietf-yang-types { prefix yang; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: Editor: Vladimir Vassilev "; description "This module contains a collection of YANG definitions for description and management of network bridge based on flows. Copyright (c) 2020 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX; see the RFC itself for full legal notices."; revision 2021-02-17 { description "Unreleased revision."; reference "RFC XXXX: Network Bridge"; } identity packet-in-reason { description "Base identity for all the available packet in reasons."; } identity no-match { base packet-in-reason; description "No matching flow in the classifier"; } identity send-to-controller { base packet-in-reason; description "Explicit instruction to send packet to controller"; } identity invalid-ttl { base packet-in-reason; description "Packet with invalid TTL"; } typedef vlan-pcp { type uint8 { range "0..7"; } description "IEEE 802.1p priority. It indicates the frame priority level. Values are from 0 (best effort) to 7 (highest); 1 represents the lowest priority."; } typedef vlan-id { type uint16 { range "0..4095"; } description "IETF 802.1q VLAN tag id."; } typedef ether-type { type uint16; description "Length/Type field of the generated Ethernet packet."; reference "IEEE 802-2014 Clause 9.2"; } typedef vlan-cfi { type int32; description "Canonical Format Identifier (CFI) field (shall be 0 for Ethernet switches) of the transmitted 802.1q VLAN tag."; } typedef flow-id { type inet:uri; description "Flow identifier."; } grouping address { description "IP address."; choice address { description "Address choice."; case ipv4 { leaf ipv4-address { type inet:ipv4-prefix; description "IPv4 address."; } } case ipv6 { leaf ipv6-address { type inet:ipv6-prefix; description "IPv6 address."; } } } } grouping action-list { description "Action list grouping."; list action { key "order"; description "Contains action with corresponding order index."; leaf order { type int32; description "Order index."; } uses action; } } grouping action { description "Grouping of all action data definitions."; choice action { description "Choice with alternative action cases."; case output-action-case { container output-action { description "Contains output action specific data."; leaf out-port { type netbr:port-ref; description "Port on which the packet is sent."; } leaf max-length { type uint16; description "Packets above this length are discarded."; } } } case controller-action-case { container controller-action { description "Contains controller action specific data."; leaf max-length { type uint16; description "Packets above this length are discarted."; } } } case drop-action-case { container drop-action { presence "Drop action case"; description "Drop action presence container."; } } case pop-vlan-action-case { container pop-vlan-action { presence "Pop-vlan action case."; description "Pop-vlan presence container"; } } case push-vlan-action-case { container push-vlan-action { description "Contains push-vlan action specific data."; leaf ethernet-type { type ether-type; description "Tag protocol identifier (TPID) as defined in IEEE 802.1q"; } leaf pcp { type vlan-pcp; description "Specifies the IEEE 802.1p Priority Code Point (PCP) value of the pushed 802.1q VLAN tag."; } leaf cfi { type vlan-cfi; description "Configures the Canonical Format Identifier (CFI) field (shall be 0 for Ethernet switches) of the transmitted 802.1q VLAN tag."; } leaf vlan-id { type vlan-id; description "Specifies the VLAN ID as defined in IEEE 802.1q of the pushed VLAN tag."; } } } case set-vlan-cfi-action-case { container set-vlan-cfi-action { description "Contains set-vlan-cfi action specific data. The set-vlan-cfi action is used to replace CFI field on already tagged packets."; leaf vlan-cfi { type vlan-cfi; description "Configures the Canonical Format Identifier (CFI) field to set on the transmitted 802.1q VLAN tagged packet."; } } } case set-vlan-id-action-case { container set-vlan-id-action { description "Contains set-vlan-id action specific data. The set-vlan-id action is used to replace VLAN ID field on already tagged packets."; leaf vlan-id { type vlan-id; description "Specifies the VLAN ID to set on the 802.1q VLAN tagged packet."; } } } case set-vlan-pcp-action-case { container set-vlan-pcp-action { description "Contains set-vlan-pcp action specific data. The set-vlan-pcp action is used to replace VLAN PCP field on already tagged packets."; leaf vlan-pcp { type vlan-pcp; description "Specifies the IEEE 802.1p Priority Code Point (PCP) value to set on the 802.1q VLAN tagged packet."; } } } case strip-vlan-action-case { container strip-vlan-action { presence "Strip-vlan action case"; description "Strip-vlan presence container."; } } } } grouping mac-address-filter { description "Defines address and mask pair for definition of basic MAC address filter rules."; leaf address { type yang:mac-address; mandatory true; description "MAC address to compare with."; } leaf mask { type yang:mac-address; description "The mask specifies the bits to compare. All bits that are 1s are significant."; } } grouping ethernet-match-fields { description "Defines data for specification of filter rules for Ethernet frames based on source and destination MAC addresses and the ethernet type field."; container ethernet-source { presence "Match field is active and set"; description "Ethernet source address."; uses mac-address-filter; } container ethernet-destination { presence "Match field is active and set"; description "Ethernet destination address."; uses mac-address-filter; } container ethernet-type { presence "Match field is active and set"; description "Ethernet frame type."; leaf type { type ether-type; mandatory true; description "Type field of the Ethernet frame."; } } } grouping vlan-match-fields { description "Defines data for specification of filter rules for VLAN tagged or not Ethernet frames based on the presence of VLAN tag, the value of the VLAN ID and VLAN PCP fields."; container vlan-id { presence "Match field is active and set"; description "Match 802.1q VLAN ID."; leaf vlan-id-present { type boolean; description "If set to false match packets with different VLAN ID then the specified in the vlan-id leaf."; } leaf vlan-id { type vlan-id; description "802.1q VLAN ID to match. The match rule can be inverted by setting vlan-id-present to false."; } } leaf vlan-pcp { type vlan-pcp; description "Match 802.1p VLAN Priority code point (PCP) field."; } } grouping match { description "Defines data for specification of filter rules."; leaf in-port { type netbr:port-ref; description "Input port to match."; } container ethernet-match { description "Ethernet match rules."; uses ethernet-match-fields; } container vlan-match { description "VLAN match rules."; uses vlan-match-fields; } } grouping raw-packet { description "Basic packet structure."; leaf ingress { type netbr:port-ref; description "Port the packet was received on."; } leaf payload { type binary; description "Payload of the packet."; } } grouping packet-in { description "Input packet event data: ingress port, payload, event reason."; leaf packet-in-reason { type identityref { base packet-in-reason; } description "Reason identity: no-match, send-to-controller, invalid-ttl or another reason for the corresponding packet-in event."; } uses raw-packet; } grouping ethernet-packet { description "Ethernet packet headers structure."; leaf source { type yang:mac-address; description "MAC source address."; } leaf destination { type yang:mac-address; description "MAC destination address."; } } grouping flow { description "The definition of a flow has a name, match container specifying filter rules and ordered list of actions to be performed on each packet."; leaf id { type flow-id; description "Flow identifier."; } container match { description "Filter rules for the flow."; uses match; } container actions { description "Ordered list of actions."; uses action-list; } } rpc transmit-packet { description "Sending packet out."; input { leaf egress { type netbr:port-ref; description "Egress port the packet will be transmitted from."; } uses raw-packet; uses action-list; } } notification packet-received { description "Delivery of incoming packet."; uses packet-in; container match { description "Match data that triggered the source of the packet-received event."; uses match; } } container packet-in-message { description "Container with the last packet-in reported. Useful for basic monitoring."; uses packet-in; container match { description "Match data that triggered the source of the last packet-received event."; uses match; } } container flows { description "Contains the list of all configured flows."; list flow { key "id"; description ""; uses flow; container flow-statistics { config false; description "Contains flow counters."; leaf packet-count { type yang:counter64; description "Packets matched."; } leaf byte-count { type yang:counter64; description "Sum of the bytes of all matched packets."; } } } } } yuma123_2.14/netconf/modules/ietf-draft/iana-tls-cipher-suite-algs.yang0000664000175000017500000030074314770023131026176 0ustar vladimirvladimirmodule iana-tls-cipher-suite-algs { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:iana-tls-cipher-suite-algs"; prefix tlscsa; organization "Internet Assigned Numbers Authority (IANA)"; contact "Postal: ICANN 12025 Waterfront Drive, Suite 300 Los Angeles, CA 90094-2536 United States of America Tel: +1 310 301 5800 Email: iana@iana.org"; description "This module defines identities for the Cipher Suite algorithms defined in the 'TLS Cipher Suites' sub-registry of the 'Transport Layer Security (TLS) Parameters' registry maintained by IANA. Copyright (c) 2021 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). The initial version of this YANG module is part of RFC FFFF (https://www.rfc-editor.org/info/rfcFFFF); see the RFC itself for full legal notices."; revision 2021-06-02 { description "Initial version"; reference "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers"; } // Typedefs typedef cipher-suite-algorithm-ref { type identityref { base "cipher-suite-alg-base"; } description "A reference to a TLS cipher suite algorithm identifier."; } // Identities identity cipher-suite-alg-base { description "Base identity used to identify TLS cipher suites."; } identity tls-null-with-null-null { base cipher-suite-alg-base; status deprecated; description "TLS-NULL-WITH-NULL-NULL"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-rsa-with-null-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-NULL-MD5"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-rsa-with-null-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-NULL-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-rsa-export-with-rc4-40-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-EXPORT-WITH-RC4-40-MD5"; reference "RFC 4346: The TLS Protocol Version 1.1 RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-rsa-with-rc4-128-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-RC4-128-MD5"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-rsa-with-rc4-128-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-RC4-128-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-rsa-export-with-rc2-cbc-40-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-EXPORT-WITH-RC2-CBC-40-MD5"; reference "RFC 4346: The TLS Protocol Version 1.1"; } identity tls-rsa-with-idea-cbc-sha { base cipher-suite-alg-base; status obsolete; description "TLS-RSA-WITH-IDEA-CBC-SHA"; reference "RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS) RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS)"; } identity tls-rsa-export-with-des40-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-EXPORT-WITH-DES40-CBC-SHA"; reference "RFC 4346: The TLS Protocol Version 1.1"; } identity tls-rsa-with-des-cbc-sha { base cipher-suite-alg-base; status obsolete; description "TLS-RSA-WITH-DES-CBC-SHA"; reference "RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS) RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS)"; } identity tls-rsa-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-3DES-EDE-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-dss-export-with-des40-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-EXPORT-WITH-DES40-CBC-SHA"; reference "RFC 4346: The TLS Protocol Version 1.1"; } identity tls-dh-dss-with-des-cbc-sha { base cipher-suite-alg-base; status obsolete; description "TLS-DH-DSS-WITH-DES-CBC-SHA"; reference "RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS) RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS)"; } identity tls-dh-dss-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-3DES-EDE-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-rsa-export-with-des40-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-EXPORT-WITH-DES40-CBC-SHA"; reference "RFC 4346: The TLS Protocol Version 1.1"; } identity tls-dh-rsa-with-des-cbc-sha { base cipher-suite-alg-base; status obsolete; description "TLS-DH-RSA-WITH-DES-CBC-SHA"; reference "RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS) RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS)"; } identity tls-dh-rsa-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-3DES-EDE-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dhe-dss-export-with-des40-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-EXPORT-WITH-DES40-CBC-SHA"; reference "RFC 4346: The TLS Protocol Version 1.1"; } identity tls-dhe-dss-with-des-cbc-sha { base cipher-suite-alg-base; status obsolete; description "TLS-DHE-DSS-WITH-DES-CBC-SHA"; reference "RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS) RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS)"; } identity tls-dhe-dss-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dhe-rsa-export-with-des40-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-EXPORT-WITH-DES40-CBC-SHA"; reference "RFC 4346: The TLS Protocol Version 1.1"; } identity tls-dhe-rsa-with-des-cbc-sha { base cipher-suite-alg-base; status obsolete; description "TLS-DHE-RSA-WITH-DES-CBC-SHA"; reference "RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS) RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS)"; } identity tls-dhe-rsa-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-anon-export-with-rc4-40-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-EXPORT-WITH-RC4-40-MD5"; reference "RFC 4346: The TLS Protocol Version 1.1 RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-dh-anon-with-rc4-128-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-RC4-128-MD5"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-dh-anon-export-with-des40-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-EXPORT-WITH-DES40-CBC-SHA"; reference "RFC 4346: The TLS Protocol Version 1.1"; } identity tls-dh-anon-with-des-cbc-sha { base cipher-suite-alg-base; status obsolete; description "TLS-DH-ANON-WITH-DES-CBC-SHA"; reference "RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS) RFC 5469: DES and IDEA Cipher Suites for Transport Layer Security (TLS)"; } identity tls-dh-anon-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-3DES-EDE-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-krb5-with-des-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-WITH-DES-CBC-SHA"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS)"; } identity tls-krb5-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-WITH-3DES-EDE-CBC-SHA"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS)"; } identity tls-krb5-with-rc4-128-sha { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-WITH-RC4-128-SHA"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS) RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-krb5-with-idea-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-WITH-IDEA-CBC-SHA"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS)"; } identity tls-krb5-with-des-cbc-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-WITH-DES-CBC-MD5"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS)"; } identity tls-krb5-with-3des-ede-cbc-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-WITH-3DES-EDE-CBC-MD5"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS)"; } identity tls-krb5-with-rc4-128-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-WITH-RC4-128-MD5"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS) RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-krb5-with-idea-cbc-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-WITH-IDEA-CBC-MD5"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS)"; } identity tls-krb5-export-with-des-cbc-40-sha { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-EXPORT-WITH-DES-CBC-40-SHA"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS)"; } identity tls-krb5-export-with-rc2-cbc-40-sha { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-EXPORT-WITH-RC2-CBC-40-SHA"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS)"; } identity tls-krb5-export-with-rc4-40-sha { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-EXPORT-WITH-RC4-40-SHA"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS) RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-krb5-export-with-des-cbc-40-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-EXPORT-WITH-DES-CBC-40-MD5"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS)"; } identity tls-krb5-export-with-rc2-cbc-40-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-EXPORT-WITH-RC2-CBC-40-MD5"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS)"; } identity tls-krb5-export-with-rc4-40-md5 { base cipher-suite-alg-base; status deprecated; description "TLS-KRB5-EXPORT-WITH-RC4-40-MD5"; reference "RFC 2712: Addition of Kerberos Cipher Suites to Transport Layer Security (TLS) RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-psk-with-null-sha { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-NULL-SHA"; reference "RFC 4785: Pre-Shared Key Cipher Suites with NULL Encryption for Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-null-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-NULL-SHA"; reference "RFC 4785: Pre-Shared Key Cipher Suites with NULL Encryption for Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-null-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-NULL-SHA"; reference "RFC 4785: Pre-Shared Key Cipher Suites with NULL Encryption for Transport Layer Security (TLS)"; } identity tls-rsa-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-AES-128-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-dss-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-AES-128-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-rsa-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-AES-128-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dhe-dss-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-AES-128-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dhe-rsa-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-AES-128-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-anon-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-AES-128-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-rsa-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-AES-256-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-dss-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-AES-256-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-rsa-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-AES-256-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dhe-dss-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-AES-256-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dhe-rsa-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-AES-256-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-anon-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-AES-256-CBC-SHA"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-rsa-with-null-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-NULL-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-rsa-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-AES-128-CBC-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-rsa-with-aes-256-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-AES-256-CBC-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-dss-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-AES-128-CBC-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-rsa-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-AES-128-CBC-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dhe-dss-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-AES-128-CBC-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-rsa-with-camellia-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dh-dss-with-camellia-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-CAMELLIA-128-CBC-SHA"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dh-rsa-with-camellia-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-CAMELLIA-128-CBC-SHA"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dhe-dss-with-camellia-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dhe-rsa-with-camellia-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dh-anon-with-camellia-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-CAMELLIA-128-CBC-SHA"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dhe-rsa-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-dss-with-aes-256-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-AES-256-CBC-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-rsa-with-aes-256-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-AES-256-CBC-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dhe-dss-with-aes-256-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-AES-256-CBC-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dhe-rsa-with-aes-256-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-anon-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-AES-128-CBC-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-dh-anon-with-aes-256-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-AES-256-CBC-SHA256"; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls-rsa-with-camellia-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dh-dss-with-camellia-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-CAMELLIA-256-CBC-SHA"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dh-rsa-with-camellia-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-CAMELLIA-256-CBC-SHA"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dhe-dss-with-camellia-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dhe-rsa-with-camellia-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dh-anon-with-camellia-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-CAMELLIA-256-CBC-SHA"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-psk-with-rc4-128-sha { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-RC4-128-SHA"; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS) RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-psk-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-3DES-EDE-CBC-SHA"; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)"; } identity tls-psk-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-AES-128-CBC-SHA"; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)"; } identity tls-psk-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-AES-256-CBC-SHA"; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-rc4-128-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-RC4-128-SHA"; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS) RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-dhe-psk-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-3DES-EDE-CBC-SHA"; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-AES-128-CBC-SHA"; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-AES-256-CBC-SHA"; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-rc4-128-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-RC4-128-SHA"; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS) RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-rsa-psk-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-3DES-EDE-CBC-SHA"; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-AES-128-CBC-SHA"; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-AES-256-CBC-SHA"; reference "RFC 4279: Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)"; } identity tls-rsa-with-seed-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-SEED-CBC-SHA"; reference "RFC 4162: Addition of SEED Ciphersuites to Transport Layer Security (TLS)"; } identity tls-dh-dss-with-seed-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-SEED-CBC-SHA"; reference "RFC 4162: Addition of SEED Ciphersuites to Transport Layer Security (TLS)"; } identity tls-dh-rsa-with-seed-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-SEED-CBC-SHA"; reference "RFC 4162: Addition of SEED Ciphersuites to Transport Layer Security (TLS)"; } identity tls-dhe-dss-with-seed-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-SEED-CBC-SHA"; reference "RFC 4162: Addition of SEED Ciphersuites to Transport Layer Security (TLS)"; } identity tls-dhe-rsa-with-seed-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-SEED-CBC-SHA"; reference "RFC 4162: Addition of SEED Ciphersuites to Transport Layer Security (TLS)"; } identity tls-dh-anon-with-seed-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-SEED-CBC-SHA"; reference "RFC 4162: Addition of SEED Ciphersuites to Transport Layer Security (TLS)"; } identity tls-rsa-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-AES-128-GCM-SHA256"; reference "RFC 5288: AES-GCM Cipher Suites for TLS"; } identity tls-rsa-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-AES-256-GCM-SHA384"; reference "RFC 5288: AES-GCM Cipher Suites for TLS"; } identity tls-dhe-rsa-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; description "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256"; reference "RFC 5288: AES-GCM Cipher Suites for TLS"; } identity tls-dhe-rsa-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; description "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384"; reference "RFC 5288: AES-GCM Cipher Suites for TLS"; } identity tls-dh-rsa-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-AES-128-GCM-SHA256"; reference "RFC 5288: AES-GCM Cipher Suites for TLS"; } identity tls-dh-rsa-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-AES-256-GCM-SHA384"; reference "RFC 5288: AES-GCM Cipher Suites for TLS"; } identity tls-dhe-dss-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-AES-128-GCM-SHA256"; reference "RFC 5288: AES-GCM Cipher Suites for TLS"; } identity tls-dhe-dss-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-AES-256-GCM-SHA384"; reference "RFC 5288: AES-GCM Cipher Suites for TLS"; } identity tls-dh-dss-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-AES-128-GCM-SHA256"; reference "RFC 5288: AES-GCM Cipher Suites for TLS"; } identity tls-dh-dss-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-AES-256-GCM-SHA384"; reference "RFC 5288: AES-GCM Cipher Suites for TLS"; } identity tls-dh-anon-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-AES-128-GCM-SHA256"; reference "RFC 5288: AES-GCM Cipher Suites for TLS"; } identity tls-dh-anon-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-AES-256-GCM-SHA384"; reference "RFC 5288: AES-GCM Cipher Suites for TLS"; } identity tls-psk-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-AES-128-GCM-SHA256"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-psk-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-AES-256-GCM-SHA384"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-dhe-psk-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; description "TLS-DHE-PSK-WITH-AES-128-GCM-SHA256"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-dhe-psk-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; description "TLS-DHE-PSK-WITH-AES-256-GCM-SHA384"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-rsa-psk-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-AES-128-GCM-SHA256"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-rsa-psk-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-AES-256-GCM-SHA384"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-psk-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-AES-128-CBC-SHA256"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-psk-with-aes-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-AES-256-CBC-SHA384"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-psk-with-null-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-NULL-SHA256"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-psk-with-null-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-NULL-SHA384"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-dhe-psk-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-AES-128-CBC-SHA256"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-dhe-psk-with-aes-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-AES-256-CBC-SHA384"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-dhe-psk-with-null-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-NULL-SHA256"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-dhe-psk-with-null-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-NULL-SHA384"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-rsa-psk-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-AES-128-CBC-SHA256"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-rsa-psk-with-aes-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-AES-256-CBC-SHA384"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-rsa-psk-with-null-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-NULL-SHA256"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-rsa-psk-with-null-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-NULL-SHA384"; reference "RFC 5487: Pre-Shared Key Cipher Suites for Transport Layer Security (TLS) with SHA-256/384 and AES Galois Counter Mode"; } identity tls-rsa-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dh-dss-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dh-rsa-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dhe-dss-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dhe-rsa-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dh-anon-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-rsa-with-camellia-256-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dh-dss-with-camellia-256-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-CAMELLIA-256-CBC-SHA256"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dh-rsa-with-camellia-256-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-CAMELLIA-256-CBC-SHA256"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dhe-dss-with-camellia-256-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dhe-rsa-with-camellia-256-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-dh-anon-with-camellia-256-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-CAMELLIA-256-CBC-SHA256"; reference "RFC 5932: Camellia Cipher Suites for TLS"; } identity tls-sm4-gcm-sm3 { base cipher-suite-alg-base; status deprecated; description "TLS-SM4-GCM-SM3"; reference "RFC 8998: ShangMi (SM) Cipher Suites for Transport Layer Security (TLS) Protocol Version 1.3"; } identity tls-sm4-ccm-sm3 { base cipher-suite-alg-base; status deprecated; description "TLS-SM4-CCM-SM3"; reference "RFC 8998: ShangMi (SM) Cipher Suites for Transport Layer Security (TLS) Protocol Version 1.3"; } identity tls-empty-renegotiation-info-scsv { base cipher-suite-alg-base; status deprecated; description "TLS-EMPTY-RENEGOTIATION-INFO-SCSV"; reference "RFC 5746: Transport Layer Security (TLS) Renegotiation Indication Extension"; } identity tls-aes-128-gcm-sha256 { base cipher-suite-alg-base; description "TLS-AES-128-GCM-SHA256"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } identity tls-aes-256-gcm-sha384 { base cipher-suite-alg-base; description "TLS-AES-256-GCM-SHA384"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } identity tls-chacha20-poly1305-sha256 { base cipher-suite-alg-base; description "TLS-CHACHA20-POLY1305-SHA256"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } identity tls-aes-128-ccm-sha256 { base cipher-suite-alg-base; description "TLS-AES-128-CCM-SHA256"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } identity tls-aes-128-ccm-8-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-AES-128-CCM-8-SHA256"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } identity tls-fallback-scsv { base cipher-suite-alg-base; status deprecated; description "TLS-FALLBACK-SCSV"; reference "RFC 7507: TLS Fallback Signaling Cipher Suite Value (SCSV) for Preventing Protocol Downgrade Attacks"; } identity tls-ecdh-ecdsa-with-null-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-NULL-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdh-ecdsa-with-rc4-128-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-RC4-128-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-ecdh-ecdsa-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-3DES-EDE-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdh-ecdsa-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdh-ecdsa-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdhe-ecdsa-with-null-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-NULL-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdhe-ecdsa-with-rc4-128-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-RC4-128-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-ecdhe-ecdsa-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-3DES-EDE-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdhe-ecdsa-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdhe-ecdsa-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdh-rsa-with-null-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-NULL-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdh-rsa-with-rc4-128-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-RC4-128-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-ecdh-rsa-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-3DES-EDE-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdh-rsa-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdh-rsa-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdhe-rsa-with-null-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-NULL-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdhe-rsa-with-rc4-128-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-RC4-128-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-ecdhe-rsa-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdhe-rsa-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdhe-rsa-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdh-anon-with-null-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ANON-WITH-NULL-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdh-anon-with-rc4-128-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ANON-WITH-RC4-128-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-ecdh-anon-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ANON-WITH-3DES-EDE-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdh-anon-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ANON-WITH-AES-128-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-ecdh-anon-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ANON-WITH-AES-256-CBC-SHA"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } identity tls-srp-sha-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-SRP-SHA-WITH-3DES-EDE-CBC-SHA"; reference "RFC 5054: Using SRP for TLS Authentication"; } identity tls-srp-sha-rsa-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-SRP-SHA-RSA-WITH-3DES-EDE-CBC-SHA"; reference "RFC 5054: Using SRP for TLS Authentication"; } identity tls-srp-sha-dss-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-SRP-SHA-DSS-WITH-3DES-EDE-CBC-SHA"; reference "RFC 5054: Using SRP for TLS Authentication"; } identity tls-srp-sha-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-SRP-SHA-WITH-AES-128-CBC-SHA"; reference "RFC 5054: Using SRP for TLS Authentication"; } identity tls-srp-sha-rsa-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-SRP-SHA-RSA-WITH-AES-128-CBC-SHA"; reference "RFC 5054: Using SRP for TLS Authentication"; } identity tls-srp-sha-dss-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-SRP-SHA-DSS-WITH-AES-128-CBC-SHA"; reference "RFC 5054: Using SRP for TLS Authentication"; } identity tls-srp-sha-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-SRP-SHA-WITH-AES-256-CBC-SHA"; reference "RFC 5054: Using SRP for TLS Authentication"; } identity tls-srp-sha-rsa-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-SRP-SHA-RSA-WITH-AES-256-CBC-SHA"; reference "RFC 5054: Using SRP for TLS Authentication"; } identity tls-srp-sha-dss-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-SRP-SHA-DSS-WITH-AES-256-CBC-SHA"; reference "RFC 5054: Using SRP for TLS Authentication"; } identity tls-ecdhe-ecdsa-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdhe-ecdsa-with-aes-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdh-ecdsa-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA256"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdh-ecdsa-with-aes-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA384"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdhe-rsa-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdhe-rsa-with-aes-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdh-rsa-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA256"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdh-rsa-with-aes-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA384"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdhe-ecdsa-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; description "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdhe-ecdsa-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; description "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdh-ecdsa-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdh-ecdsa-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-AES-256-GCM-SHA384"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdhe-rsa-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; description "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdhe-rsa-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; description "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdh-rsa-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-AES-128-GCM-SHA256"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdh-rsa-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-AES-256-GCM-SHA384"; reference "RFC 5289: TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode"; } identity tls-ecdhe-psk-with-rc4-128-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-RC4-128-SHA"; reference "RFC 5489: ECDHE_PSK Ciphersuites for Transport Layer Security (TLS) RFC 6347: Datagram Transport Layer Security version 1.2"; } identity tls-ecdhe-psk-with-3des-ede-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-3DES-EDE-CBC-SHA"; reference "RFC 5489: ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-aes-128-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA"; reference "RFC 5489: ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-aes-256-cbc-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-AES-256-CBC-SHA"; reference "RFC 5489: ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-aes-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256"; reference "RFC 5489: ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-aes-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-AES-256-CBC-SHA384"; reference "RFC 5489: ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-null-sha { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-NULL-SHA"; reference "RFC 5489: ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-null-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-NULL-SHA256"; reference "RFC 5489: ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-null-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-NULL-SHA384"; reference "RFC 5489: ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; } identity tls-rsa-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-dss-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-dss-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-rsa-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-rsa-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-dss-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-dss-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-rsa-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-rsa-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-anon-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-anon-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-ecdsa-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-ecdsa-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-ecdsa-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-ecdsa-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-rsa-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-rsa-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-rsa-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-rsa-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-rsa-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-rsa-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-rsa-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-rsa-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-dss-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-dss-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-dss-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-dss-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-anon-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-anon-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-ecdsa-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-ecdsa-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-ecdsa-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-ecdsa-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-rsa-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-rsa-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-rsa-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-rsa-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-psk-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-psk-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-psk-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-psk-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-aria-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-ARIA-128-GCM-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-aria-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-ARIA-256-GCM-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-aria-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-ARIA-128-CBC-SHA256"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-aria-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-ARIA-256-CBC-SHA384"; reference "RFC 6209: Addition of the ARIA Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-ecdsa-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-ecdsa-with-camellia-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-CBC-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-ecdsa-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-ecdsa-with-camellia-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-CAMELLIA-256-CBC-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-rsa-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-rsa-with-camellia-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-CAMELLIA-256-CBC-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-rsa-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-rsa-with-camellia-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-CAMELLIA-256-CBC-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-rsa-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-rsa-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-rsa-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-rsa-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-RSA-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-dss-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-dss-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-DSS-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-dss-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-dss-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-DSS-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-anon-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dh-anon-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DH-ANON-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-ecdsa-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-ecdsa-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-ecdsa-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-ecdsa-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-ECDSA-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-rsa-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-rsa-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-RSA-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-rsa-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdh-rsa-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDH-RSA-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-psk-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-psk-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-camellia-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-CAMELLIA-128-GCM-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-camellia-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-CAMELLIA-256-GCM-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-psk-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-psk-with-camellia-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-CAMELLIA-256-CBC-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-camellia-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-PSK-WITH-CAMELLIA-256-CBC-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-camellia-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-CAMELLIA-256-CBC-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-camellia-128-cbc-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-CAMELLIA-128-CBC-SHA256"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-camellia-256-cbc-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-CAMELLIA-256-CBC-SHA384"; reference "RFC 6367: Addition of the Camellia Cipher Suites to Transport Layer Security (TLS)"; } identity tls-rsa-with-aes-128-ccm { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-AES-128-CCM"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-rsa-with-aes-256-ccm { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-AES-256-CCM"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-dhe-rsa-with-aes-128-ccm { base cipher-suite-alg-base; description "TLS-DHE-RSA-WITH-AES-128-CCM"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-dhe-rsa-with-aes-256-ccm { base cipher-suite-alg-base; description "TLS-DHE-RSA-WITH-AES-256-CCM"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-rsa-with-aes-128-ccm-8 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-AES-128-CCM-8"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-rsa-with-aes-256-ccm-8 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-WITH-AES-256-CCM-8"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-dhe-rsa-with-aes-128-ccm-8 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-AES-128-CCM-8"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-dhe-rsa-with-aes-256-ccm-8 { base cipher-suite-alg-base; status deprecated; description "TLS-DHE-RSA-WITH-AES-256-CCM-8"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-psk-with-aes-128-ccm { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-AES-128-CCM"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-psk-with-aes-256-ccm { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-AES-256-CCM"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-dhe-psk-with-aes-128-ccm { base cipher-suite-alg-base; description "TLS-DHE-PSK-WITH-AES-128-CCM"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-dhe-psk-with-aes-256-ccm { base cipher-suite-alg-base; description "TLS-DHE-PSK-WITH-AES-256-CCM"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-psk-with-aes-128-ccm-8 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-AES-128-CCM-8"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-psk-with-aes-256-ccm-8 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-AES-256-CCM-8"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-psk-dhe-with-aes-128-ccm-8 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-DHE-WITH-AES-128-CCM-8"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-psk-dhe-with-aes-256-ccm-8 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-DHE-WITH-AES-256-CCM-8"; reference "RFC 6655: AES-CCM Cipher Suites for TLS"; } identity tls-ecdhe-ecdsa-with-aes-128-ccm { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-AES-128-CCM"; reference "RFC 7251: AES-CCM ECC Cipher Suites for TLS"; } identity tls-ecdhe-ecdsa-with-aes-256-ccm { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-AES-256-CCM"; reference "RFC 7251: AES-CCM ECC Cipher Suites for TLS"; } identity tls-ecdhe-ecdsa-with-aes-128-ccm-8 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-AES-128-CCM-8"; reference "RFC 7251: AES-CCM ECC Cipher Suites for TLS"; } identity tls-ecdhe-ecdsa-with-aes-256-ccm-8 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-ECDSA-WITH-AES-256-CCM-8"; reference "RFC 7251: AES-CCM ECC Cipher Suites for TLS"; } identity tls-eccpwd-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECCPWD-WITH-AES-128-GCM-SHA256"; reference "RFC 8492: Secure Password Ciphersuites for Transport Layer Security (TLS)"; } identity tls-eccpwd-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECCPWD-WITH-AES-256-GCM-SHA384"; reference "RFC 8492: Secure Password Ciphersuites for Transport Layer Security (TLS)"; } identity tls-eccpwd-with-aes-128-ccm-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECCPWD-WITH-AES-128-CCM-SHA256"; reference "RFC 8492: Secure Password Ciphersuites for Transport Layer Security (TLS)"; } identity tls-eccpwd-with-aes-256-ccm-sha384 { base cipher-suite-alg-base; status deprecated; description "TLS-ECCPWD-WITH-AES-256-CCM-SHA384"; reference "RFC 8492: Secure Password Ciphersuites for Transport Layer Security (TLS)"; } identity tls-ecdhe-rsa-with-chacha20-poly1305-sha256 { base cipher-suite-alg-base; description "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256"; reference "RFC 7905: ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS)"; } identity tls-ecdhe-ecdsa-with-chacha20-poly1305-sha256 { base cipher-suite-alg-base; description "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256"; reference "RFC 7905: ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS)"; } identity tls-dhe-rsa-with-chacha20-poly1305-sha256 { base cipher-suite-alg-base; description "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256"; reference "RFC 7905: ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS)"; } identity tls-psk-with-chacha20-poly1305-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-PSK-WITH-CHACHA20-POLY1305-SHA256"; reference "RFC 7905: ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-chacha20-poly1305-sha256 { base cipher-suite-alg-base; description "TLS-ECDHE-PSK-WITH-CHACHA20-POLY1305-SHA256"; reference "RFC 7905: ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS)"; } identity tls-dhe-psk-with-chacha20-poly1305-sha256 { base cipher-suite-alg-base; description "TLS-DHE-PSK-WITH-CHACHA20-POLY1305-SHA256"; reference "RFC 7905: ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS)"; } identity tls-rsa-psk-with-chacha20-poly1305-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-RSA-PSK-WITH-CHACHA20-POLY1305-SHA256"; reference "RFC 7905: ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS)"; } identity tls-ecdhe-psk-with-aes-128-gcm-sha256 { base cipher-suite-alg-base; description "TLS-ECDHE-PSK-WITH-AES-128-GCM-SHA256"; reference "RFC 8442: ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites"; } identity tls-ecdhe-psk-with-aes-256-gcm-sha384 { base cipher-suite-alg-base; description "TLS-ECDHE-PSK-WITH-AES-256-GCM-SHA384"; reference "RFC 8442: ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites"; } identity tls-ecdhe-psk-with-aes-128-ccm-8-sha256 { base cipher-suite-alg-base; status deprecated; description "TLS-ECDHE-PSK-WITH-AES-128-CCM-8-SHA256"; reference "RFC 8442: ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites"; } identity tls-ecdhe-psk-with-aes-128-ccm-sha256 { base cipher-suite-alg-base; description "TLS-ECDHE-PSK-WITH-AES-128-CCM-SHA256"; reference "RFC 8442: ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites"; } // Protocol-accessible Nodes container supported-algorithms { config false; description "A container for a list of cipher suite algorithms supported by the server."; leaf-list supported-algorithm { type cipher-suite-algorithm-ref; description "A cipher suite algorithm supported by the server."; } } } yuma123_2.14/netconf/modules/ietf-draft/ietf-if-ethernet-like.yang0000664000175000017500000001170714770023131025223 0ustar vladimirvladimirmodule ietf-if-ethernet-like { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-if-ethernet-like"; prefix ethlike; import ietf-interfaces { prefix if; reference "RFC 8343: A YANG Data Model For Interface Management"; } import ietf-yang-types { prefix yang; reference "RFC 6991: Common YANG Data Types"; } import iana-if-type { prefix ianaift; reference "RFC 7224: IANA Interface Type YANG Module"; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: Editor: Robert Wilton "; description "This module contains YANG definitions for configuration for 'Ethernet-like' interfaces. It is applicable to all interface types that use Ethernet framing and expose an Ethernet MAC layer, and includes such interfaces as physical Ethernet interfaces, Ethernet LAG interfaces and VLAN sub-interfaces. Additional interface configuration and counters for physical Ethernet interfaces are defined in ieee802-ethernet-interface.yang, as part of IEEE Std 802.3.2-2019. Copyright (c) 2019 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); see the RFC itself for full legal notices."; revision 2019-11-04 { description "Initial revision."; reference "RFC XXX, Common Interface Extension YANG Data Models"; } feature configurable-mac-address { description "This feature indicates that MAC addresses on Ethernet-like interfaces can be configured."; reference "RFC XXX, Section 3 Interfaces Ethernet-Like Module"; } /* * Configuration parameters for Ethernet-like interfaces. */ augment "/if:interfaces/if:interface" { when "derived-from-or-self(if:type, 'ianaift:ethernetCsmacd') or derived-from-or-self(if:type, 'ianaift:ieee8023adLag') or derived-from-or-self(if:type, 'ianaift:ifPwType')" { description "Applies to all Ethernet-like interfaces"; } description "Augment the interface model with parameters for all Ethernet-like interfaces."; container ethernet-like { description "Contains parameters for interfaces that use Ethernet framing and expose an Ethernet MAC layer."; leaf mac-address { if-feature "configurable-mac-address"; type yang:mac-address; description "The MAC address of the interface. The operational value matches the /if:interfaces/if:interface/if:phys-address leaf defined in ietf-interface.yang."; } leaf bia-mac-address { type yang:mac-address; config false; description "The 'burnt-in' MAC address. I.e the default MAC address assigned to the interface if no MAC address has been explicitly configured on it."; } } } /* * Configuration parameters for Ethernet-like interfaces. */ augment "/if:interfaces/if:interface/if:statistics" { when "derived-from-or-self(../if:type, 'ianaift:ethernetCsmacd') or derived-from-or-self(../if:type, 'ianaift:ieee8023adLag') or derived-from-or-self(../if:type, 'ianaift:ifPwType')" { description "Applies to all Ethernet-like interfaces"; } description "Augment the interface model statistics with additional counters related to Ethernet-like interfaces."; leaf in-drop-unknown-dest-mac-pkts { type yang:counter64; units frames; description "A count of the number of frames that were well formed, but otherwise dropped because the destination MAC address did not pass any ingress destination MAC address filter. For consistency, frames counted against this drop counters are also counted against the IETF interfaces statistics. In particular, they are included in in-octets and in-discards, but are not included in in-unicast-pkts, in-multicast-pkts or in-broadcast-pkts, because they are not delivered to a higher layer. Discontinuities in the values of this counter can occur at re-initialization of the management system, and at other times as indicated by the value of the 'discontinuity-time' leaf defined in the ietf-interfaces YANG module (RFC 8343)."; } } } yuma123_2.14/netconf/modules/ietf-draft/iana-ssh-encryption-algs.yang0000664000175000017500000002111214770023131025750 0ustar vladimirvladimirmodule iana-ssh-encryption-algs { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:iana-ssh-encryption-algs"; prefix sshea; organization "Internet Assigned Numbers Authority (IANA)"; contact "Postal: ICANN 12025 Waterfront Drive, Suite 300 Los Angeles, CA 90094-2536 United States of America Tel: +1 310 301 5800 Email: iana@iana.org"; description "This module defines identities for the encryption algorithms defined in the 'Encryption Algorithm Names' sub-registry of the 'Secure Shell (SSH) Protocol Parameters' registry maintained by IANA. Copyright (c) 2021 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). The initial version of this YANG module is part of RFC EEEE (https://www.rfc-editor.org/info/rfcEEEE); see the RFC itself for full legal notices."; revision 2021-06-01 { description "Initial version"; reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } // Typedefs typedef encryption-algorithm-ref { type identityref { base "encryption-alg-base"; } description "A reference to a SSH encryption algorithm identifier."; } // Identities identity encryption-alg-base { description "Base identity used to identify encryption algorithms."; } identity triple-des-cbc { // YANG IDs cannot begin with a number base encryption-alg-base; description "3DES-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity blowfish-cbc { base encryption-alg-base; description "BLOWFISH-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity twofish256-cbc { base encryption-alg-base; description "TWOFISH256-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity twofish-cbc { base encryption-alg-base; description "TWOFISH-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity twofish192-cbc { base encryption-alg-base; description "TWOFISH192-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity twofish128-cbc { base encryption-alg-base; description "TWOFISH128-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity aes256-cbc { base encryption-alg-base; description "AES256-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity aes192-cbc { base encryption-alg-base; description "AES192-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity aes128-cbc { base encryption-alg-base; description "AES128-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity serpent256-cbc { base encryption-alg-base; description "SERPENT256-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity serpent192-cbc { base encryption-alg-base; description "SERPENT192-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity serpent128-cbc { base encryption-alg-base; description "SERPENT128-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity arcfour { base encryption-alg-base; status obsolete; description "ARCFOUR"; reference "RFC 8758: Deprecating RC4 in Secure Shell (SSH)"; } identity idea-cbc { base encryption-alg-base; description "IDEA-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity cast128-cbc { base encryption-alg-base; description "CAST128-CBC"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity none { base encryption-alg-base; description "NONE"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity des-cbc { base encryption-alg-base; status obsolete; description "DES-CBC"; reference "FIPS 46-3: Data Encryption Standard (DES)"; } identity arcfour128 { base encryption-alg-base; status obsolete; description "ARCFOUR128"; reference "RFC 8758: Deprecating RC4 in Secure Shell (SSH)"; } identity arcfour256 { base encryption-alg-base; status obsolete; description "ARCFOUR256"; reference "RFC 8758: Deprecating RC4 in Secure Shell (SSH)"; } identity aes128-ctr { base encryption-alg-base; description "AES128-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity aes192-ctr { base encryption-alg-base; description "AES192-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity aes256-ctr { base encryption-alg-base; description "AES256-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity triple-des-ctr { // YANG IDs cannot begin with a number base encryption-alg-base; description "3DES-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity blowfish-ctr { base encryption-alg-base; description "BLOWFISH-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity twofish128-ctr { base encryption-alg-base; description "TWOFISH128-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity twofish192-ctr { base encryption-alg-base; description "TWOFISH192-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity twofish256-ctr { base encryption-alg-base; description "TWOFISH256-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity serpent128-ctr { base encryption-alg-base; description "SERPENT128-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity serpent192-ctr { base encryption-alg-base; description "SERPENT192-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity serpent256-ctr { base encryption-alg-base; description "SERPENT256-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity idea-ctr { base encryption-alg-base; description "IDEA-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity cast128-ctr { base encryption-alg-base; description "CAST128-CTR"; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity aead-aes-128-gcm { base encryption-alg-base; description "AEAD_AES_128_GCM"; reference "RFC 5647: AES Galois Counter Mode for the Secure Shell Transport Layer Protocol"; } identity aead-aes-256-gcm { base encryption-alg-base; description "AEAD_AES_256_GCM"; reference "RFC 5647: AES Galois Counter Mode for the Secure Shell Transport Layer Protocol"; } // Protocol-accessible Nodes container supported-algorithms { config false; description "A container for a list of encryption algorithms supported by the server."; leaf-list supported-algorithm { type encryption-algorithm-ref; description "A encryption algorithm supported by the server."; } } } yuma123_2.14/netconf/modules/ietf-draft/iana-ssh-key-exchange-algs.yang0000664000175000017500000017475214770023131026151 0ustar vladimirvladimirmodule iana-ssh-key-exchange-algs { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:iana-ssh-key-exchange-algs"; prefix sshkea; organization "Internet Assigned Numbers Authority (IANA)"; contact "Postal: ICANN 12025 Waterfront Drive, Suite 300 Los Angeles, CA 90094-2536 United States of America Tel: +1 310 301 5800 Email: iana@iana.org"; description "This module defines identities for the key exchange algorithms defined in the 'Key Exchange Method Names' sub-registry of the 'Secure Shell (SSH) Protocol Parameters' registry maintained by IANA. Copyright (c) 2021 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). The initial version of this YANG module is part of RFC EEEE (https://www.rfc-editor.org/info/rfcEEEE); see the RFC itself for full legal notices."; revision 2021-06-01 { description "Initial version"; reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } // Typedefs typedef key-exchange-algorithm-ref { type identityref { base "key-exchange-alg-base"; } description "A reference to a SSH key exchange algorithm identifier."; } // Identities identity key-exchange-alg-base { description "Base identity used to identify key exchange algorithms."; } identity diffie-hellman-group-exchange-sha1 { base key-exchange-alg-base; description "DIFFIE-HELLMAN-GROUP-EXCHANGE-SHA1"; reference "RFC 4419: Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol"; } identity diffie-hellman-group-exchange-sha256 { base key-exchange-alg-base; description "DIFFIE-HELLMAN-GROUP-EXCHANGE-SHA256"; reference "RFC 4419: Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol"; } identity diffie-hellman-group1-sha1 { base key-exchange-alg-base; description "DIFFIE-HELLMAN-GROUP1-SHA1"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity diffie-hellman-group14-sha1 { base key-exchange-alg-base; description "DIFFIE-HELLMAN-GROUP14-SHA1"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity diffie-hellman-group14-sha256 { base key-exchange-alg-base; description "DIFFIE-HELLMAN-GROUP14-SHA256"; reference "RFC 8268: More Modular Exponentiation (MODP) Diffie-Hellman (DH) Key Exchange (KEX) Groups for Secure Shell (SSH)"; } identity diffie-hellman-group15-sha512 { base key-exchange-alg-base; description "DIFFIE-HELLMAN-GROUP15-SHA512"; reference "RFC 8268: More Modular Exponentiation (MODP) Diffie-Hellman (DH) Key Exchange (KEX) Groups for Secure Shell (SSH)"; } identity diffie-hellman-group16-sha512 { base key-exchange-alg-base; description "DIFFIE-HELLMAN-GROUP16-SHA512"; reference "RFC 8268: More Modular Exponentiation (MODP) Diffie-Hellman (DH) Key Exchange (KEX) Groups for Secure Shell (SSH)"; } identity diffie-hellman-group17-sha512 { base key-exchange-alg-base; description "DIFFIE-HELLMAN-GROUP17-SHA512"; reference "RFC 8268: More Modular Exponentiation (MODP) Diffie-Hellman (DH) Key Exchange (KEX) Groups for Secure Shell (SSH)"; } identity diffie-hellman-group18-sha512 { base key-exchange-alg-base; description "DIFFIE-HELLMAN-GROUP18-SHA512"; reference "RFC 8268: More Modular Exponentiation (MODP) Diffie-Hellman (DH) Key Exchange (KEX) Groups for Secure Shell (SSH)"; } identity ecdh-sha2-nistp256 { base key-exchange-alg-base; description "ECDH-SHA2-NISTP256 (secp256r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-nistp384 { base key-exchange-alg-base; description "ECDH-SHA2-NISTP384 (secp384r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-nistp521 { base key-exchange-alg-base; description "ECDH-SHA2-NISTP521 (secp521r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-1.3.132.0.1 { base key-exchange-alg-base; description "ECDH-SHA2-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-1.2.840.10045.3.1.1 { base key-exchange-alg-base; description "ECDH-SHA2-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-1.3.132.0.33 { base key-exchange-alg-base; description "ECDH-SHA2-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-1.3.132.0.26 { base key-exchange-alg-base; description "ECDH-SHA2-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-1.3.132.0.27 { base key-exchange-alg-base; description "ECDH-SHA2-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-1.3.132.0.16 { base key-exchange-alg-base; description "ECDH-SHA2-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-1.3.132.0.36 { base key-exchange-alg-base; description "ECDH-SHA2-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-1.3.132.0.37 { base key-exchange-alg-base; description "ECDH-SHA2-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-1.3.132.0.38 { base key-exchange-alg-base; description "ECDH-SHA2-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecmqv-sha2 { base key-exchange-alg-base; description "ECMQV-SHA2"; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity gss-group1-sha1-nistp256 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-nistp384 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-nistp521 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-1.3.132.0.1 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-1.2.840.10045.3.1.1 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-1.3.132.0.33 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-1.3.132.0.26 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-1.3.132.0.27 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-1.3.132.0.16 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-1.3.132.0.36 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-1.3.132.0.37 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-1.3.132.0.38 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-curve25519-sha256 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group1-sha1-curve448-sha512 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP1-SHA1-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-nistp256 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-nistp384 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-nistp521 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-1.3.132.0.1 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-1.2.840.10045.3.1.1 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-1.3.132.0.33 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-1.3.132.0.26 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-1.3.132.0.27 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-1.3.132.0.16 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-1.3.132.0.36 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-1.3.132.0.37 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-1.3.132.0.38 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-curve25519-sha256 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha1-curve448-sha512 { base key-exchange-alg-base; status deprecated; description "GSS-GROUP14-SHA1-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-nistp256 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-nistp384 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-nistp521 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-1.3.132.0.1 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-1.2.840.10045.3.1.1 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-1.3.132.0.33 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-1.3.132.0.26 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-1.3.132.0.27 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-1.3.132.0.16 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-1.3.132.0.36 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-1.3.132.0.37 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-1.3.132.0.38 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-curve25519-sha256 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-gex-sha1-curve448-sha512 { base key-exchange-alg-base; status deprecated; description "GSS-GEX-SHA1-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity rsa1024-sha1 { base key-exchange-alg-base; description "RSA1024-SHA1"; reference "RFC 4432: RSA Key Exchange for the Secure Shell (SSH) Transport Layer Protocol"; } identity rsa2048-sha256 { base key-exchange-alg-base; description "RSA2048-SHA256"; reference "RFC 4432: RSA Key Exchange for the Secure Shell (SSH) Transport Layer Protocol"; } identity ext-info-s { base key-exchange-alg-base; description "EXT-INFO-S"; reference "RFC 8308: Extension Negotiation in the Secure Shell (SSH) Protocol"; } identity ext-info-c { base key-exchange-alg-base; description "EXT-INFO-C"; reference "RFC 8308: Extension Negotiation in the Secure Shell (SSH) Protocol"; } identity gss-group14-sha256-nistp256 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-nistp384 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-nistp521 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-1.3.132.0.1 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-1.2.840.10045.3.1.1 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-1.3.132.0.33 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-1.3.132.0.26 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-1.3.132.0.27 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-1.3.132.0.16 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-1.3.132.0.36 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-1.3.132.0.37 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-1.3.132.0.38 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-curve25519-sha256 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group14-sha256-curve448-sha512 { base key-exchange-alg-base; description "GSS-GROUP14-SHA256-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-nistp256 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-nistp384 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-nistp521 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-1.3.132.0.1 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-1.2.840.10045.3.1.1 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-1.3.132.0.33 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-1.3.132.0.26 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-1.3.132.0.27 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-1.3.132.0.16 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-1.3.132.0.36 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-1.3.132.0.37 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-1.3.132.0.38 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-curve25519-sha256 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group15-sha512-curve448-sha512 { base key-exchange-alg-base; description "GSS-GROUP15-SHA512-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-nistp256 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-nistp384 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-nistp521 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-1.3.132.0.1 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-1.2.840.10045.3.1.1 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-1.3.132.0.33 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-1.3.132.0.26 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-1.3.132.0.27 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-1.3.132.0.16 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-1.3.132.0.36 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-1.3.132.0.37 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-1.3.132.0.38 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-curve25519-sha256 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group16-sha512-curve448-sha512 { base key-exchange-alg-base; description "GSS-GROUP16-SHA512-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-nistp256 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-nistp384 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-nistp521 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-1.3.132.0.1 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-1.2.840.10045.3.1.1 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-1.3.132.0.33 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-1.3.132.0.26 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-1.3.132.0.27 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-1.3.132.0.16 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-1.3.132.0.36 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-1.3.132.0.37 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-1.3.132.0.38 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-curve25519-sha256 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group17-sha512-curve448-sha512 { base key-exchange-alg-base; description "GSS-GROUP17-SHA512-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-nistp256 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-nistp384 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-nistp521 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-1.3.132.0.1 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-1.2.840.10045.3.1.1 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-1.3.132.0.33 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-1.3.132.0.26 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-1.3.132.0.27 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-1.3.132.0.16 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-1.3.132.0.36 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-1.3.132.0.37 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-1.3.132.0.38 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-curve25519-sha256 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-group18-sha512-curve448-sha512 { base key-exchange-alg-base; description "GSS-GROUP18-SHA512-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-nistp256 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-nistp384 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-nistp521 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-1.3.132.0.1 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-1.2.840.10045.3.1.1 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-1.3.132.0.33 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-1.3.132.0.26 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-1.3.132.0.27 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-1.3.132.0.16 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-1.3.132.0.36 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-1.3.132.0.37 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-1.3.132.0.38 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-curve25519-sha256 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp256-sha256-curve448-sha512 { base key-exchange-alg-base; description "GSS-NISTP256-SHA256-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-nistp256 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-nistp384 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-nistp521 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-1.3.132.0.1 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-1.2.840.10045.3.1.1 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-1.3.132.0.33 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-1.3.132.0.26 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-1.3.132.0.27 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-1.3.132.0.16 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-1.3.132.0.36 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-1.3.132.0.37 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-1.3.132.0.38 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-curve25519-sha256 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp384-sha384-curve448-sha512 { base key-exchange-alg-base; description "GSS-NISTP384-SHA384-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-nistp256 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-nistp384 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-nistp521 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-1.3.132.0.1 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-1.2.840.10045.3.1.1 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-1.3.132.0.33 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-1.3.132.0.26 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-1.3.132.0.27 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-1.3.132.0.16 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-1.3.132.0.36 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-1.3.132.0.37 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-1.3.132.0.38 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-curve25519-sha256 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-nistp521-sha512-curve448-sha512 { base key-exchange-alg-base; description "GSS-NISTP521-SHA512-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-nistp256 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-nistp384 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-nistp521 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-1.3.132.0.1 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-1.2.840.10045.3.1.1 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-1.3.132.0.33 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-1.3.132.0.26 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-1.3.132.0.27 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-1.3.132.0.16 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-1.3.132.0.36 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-1.3.132.0.37 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-1.3.132.0.38 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-curve25519-sha256 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve25519-sha256-curve448-sha512 { base key-exchange-alg-base; description "GSS-CURVE25519-SHA256-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-nistp256 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-NISTP256 (secp256r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-nistp384 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-NISTP384 (secp384r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-nistp521 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-NISTP521 (secp521r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-1.3.132.0.1 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-1.3.132.0.1 (nistk163, sect163k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-1.2.840.10045.3.1.1 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-1.3.132.0.33 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-1.3.132.0.33 (nistp224, secp224r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-1.3.132.0.26 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-1.3.132.0.26 (nistk233, sect233k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-1.3.132.0.27 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-1.3.132.0.27 (nistb233, sect233r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-1.3.132.0.16 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-1.3.132.0.16 (nistk283, sect283k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-1.3.132.0.36 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-1.3.132.0.36 (nistk409, sect409k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-1.3.132.0.37 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-1.3.132.0.37 (nistb409, sect409r1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-1.3.132.0.38 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-1.3.132.0.38 (nistt571, sect571k1)"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-curve25519-sha256 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-CURVE25519-SHA256"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity gss-curve448-sha512-curve448-sha512 { base key-exchange-alg-base; description "GSS-CURVE448-SHA512-CURVE448-SHA512"; reference "RFC 8732: Generic Security Service Application Program Interface (GSS-API) Key Exchange with SHA-2"; } identity curve25519-sha256 { base key-exchange-alg-base; description "CURVE25519-SHA256"; reference "RFC 8731: Secure Shell (SSH) Key Exchange Method Using Curve25519 and Curve448"; } identity curve448-sha512 { base key-exchange-alg-base; description "CURVE448-SHA512"; reference "RFC 8731: Secure Shell (SSH) Key Exchange Method Using Curve25519 and Curve448"; } // Protocol-accessible Nodes container supported-algorithms { config false; description "A container for a list of key exchange algorithms supported by the server."; leaf-list supported-algorithm { type key-exchange-algorithm-ref; description "A key exchange algorithm supported by the server."; } } } yuma123_2.14/netconf/modules/ietf-draft/iana-ssh-mac-algs.yang0000664000175000017500000000737414770023131024334 0ustar vladimirvladimirmodule iana-ssh-mac-algs { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:iana-ssh-mac-algs"; prefix sshma; organization "Internet Assigned Numbers Authority (IANA)"; contact "Postal: ICANN 12025 Waterfront Drive, Suite 300 Los Angeles, CA 90094-2536 United States of America Tel: +1 310 301 5800 Email: iana@iana.org"; description "This module defines identities for the MAC algorithms defined in the 'MAC Algorithm Names' sub-registry of the 'Secure Shell (SSH) Protocol Parameters' registry maintained by IANA. Copyright (c) 2021 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). The initial version of this YANG module is part of RFC EEEE (https://www.rfc-editor.org/info/rfcEEEE); see the RFC itself for full legal notices."; revision 2021-06-01 { description "Initial version"; reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } // Typedefs typedef mac-algorithm-ref { type identityref { base "mac-alg-base"; } description "A reference to a SSH mac algorithm identifier."; } // Identities identity mac-alg-base { description "Base identity used to identify message authentication code (MAC) algorithms."; } identity hmac-sha1 { base mac-alg-base; description "HMAC-SHA1"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity hmac-sha1-96 { base mac-alg-base; description "HMAC-SHA1-96"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity hmac-md5 { base mac-alg-base; description "HMAC-MD5"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity hmac-md5-96 { base mac-alg-base; description "HMAC-MD5-96"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity none { base mac-alg-base; description "NONE"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity aead-aes-128-gcm { base mac-alg-base; description "AEAD_AES_128_GCM"; reference "RFC 5647: AES Galois Counter Mode for the Secure Shell Transport Layer Protocol"; } identity aead-aes-256-gcm { base mac-alg-base; description "AEAD_AES_256_GCM"; reference "RFC 5647: AES Galois Counter Mode for the Secure Shell Transport Layer Protocol"; } identity hmac-sha2-256 { base mac-alg-base; description "HMAC-SHA2-256"; reference "RFC 6668: SHA-2 Data Integrity Verification for the Secure Shell (SSH) Transport Layer Protocol"; } identity hmac-sha2-512 { base mac-alg-base; description "HMAC-SHA2-512"; reference "RFC 6668: SHA-2 Data Integrity Verification for the Secure Shell (SSH) Transport Layer Protocol"; } // Protocol-accessible Nodes container supported-algorithms { config false; description "A container for a list of MAC algorithms supported by the server."; leaf-list supported-algorithm { type mac-algorithm-ref; description "A MAC algorithm supported by the server."; } } } yuma123_2.14/netconf/modules/ietf-draft/ietf-ssh-common.yang0000664000175000017500000001730014770023131024145 0ustar vladimirvladimirmodule ietf-ssh-common { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-ssh-common"; prefix sshcmn; import iana-ssh-encryption-algs { prefix sshea; reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } import iana-ssh-key-exchange-algs { prefix sshkea; reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } import iana-ssh-mac-algs { prefix sshma; reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } import iana-ssh-public-key-algs { prefix sshpka; reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } import ietf-crypto-types { prefix ct; reference "RFC AAAA: YANG Data Types and Groupings for Cryptography"; } import ietf-keystore { prefix ks; reference "RFC CCCC: A YANG Data Model for a Keystore"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: https://datatracker.ietf.org/wg/netconf WG List: NETCONF WG list Author: Kent Watsen Author: Gary Wu "; description "This module defines a common features and groupings for Secure Shell (SSH). Copyright (c) 2022 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC EEEE (https://www.rfc-editor.org/info/rfcEEEE); see the RFC itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here."; revision 2022-05-24 { description "Initial version"; reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } // Features feature ssh-x509-certs { description "X.509v3 certificates are supported for SSH."; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } feature transport-params { description "SSH transport layer parameters are configurable."; } feature public-key-generation { description "Indicates that the server implements the 'generate-public-key' RPC."; } // Groupings grouping transport-params-grouping { description "A reusable grouping for SSH transport parameters."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; container host-key { description "Parameters regarding host key."; leaf-list host-key-alg { type identityref { base sshpka:public-key-alg-base; } ordered-by user; description "Acceptable host key algorithms in order of descending preference. The configured host key algorithms should be compatible with the algorithm used by the configured private key. Please see Section 5 of RFC EEEE for valid combinations. If this leaf-list is not configured (has zero elements) the acceptable host key algorithms are implementation- defined."; reference "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; } } container key-exchange { description "Parameters regarding key exchange."; leaf-list key-exchange-alg { type identityref { base sshkea:key-exchange-alg-base; } ordered-by user; description "Acceptable key exchange algorithms in order of descending preference. If this leaf-list is not configured (has zero elements) the acceptable key exchange algorithms are implementation defined."; } } container encryption { description "Parameters regarding encryption."; leaf-list encryption-alg { type identityref { base sshea:encryption-alg-base; } ordered-by user; description "Acceptable encryption algorithms in order of descending preference. If this leaf-list is not configured (has zero elements) the acceptable encryption algorithms are implementation defined."; } } container mac { description "Parameters regarding message authentication code (MAC)."; leaf-list mac-alg { type identityref { base sshma:mac-alg-base; } ordered-by user; description "Acceptable MAC algorithms in order of descending preference. If this leaf-list is not configured (has zero elements) the acceptable MAC algorithms are implementation- defined."; } } } // Protocol-accessible Nodes rpc generate-public-key { if-feature "public-key-generation"; description "Requests the device to generate an public key using the specified key algorithm."; input { leaf algorithm { type sshpka:public-key-algorithm-ref; mandatory true; description "The algorithm to be used when generating the key."; } leaf bits { type uint16; description "Specifies the number of bits in the key to create. For RSA keys, the minimum size is 1024 bits and the default is 3072 bits. Generally, 3072 bits is considered sufficient. DSA keys must be exactly 1024 bits as specified by FIPS 186-2. For ECDSA keys, the 'bits' value determines the key length by selecting from one of three elliptic curve sizes: 256, 384 or 521 bits. Attempting to use bit lengths other than these three values for ECDSA keys will fail. ECDSA-SK, Ed25519 and Ed25519-SK keys have a fixed length and the 'bits' value, if specified, will be ignored."; } choice private-key-encoding { default cleartext; description "A choice amongst optional private key handling."; case cleartext { leaf cleartext { type empty; description "Indicates that the private key is to be returned as a cleartext value."; } } case encrypt { if-feature "ct:private-key-encryption"; container encrypt-with { description "Indicates that the key is to be encrypted using the specified symmetric or asymmetric key."; uses ks:encrypted-by-choice-grouping; } } case hide { if-feature "ct:hidden-keys"; leaf hide { type empty; description "Indicates that the private key is to be hidden. Unlike the 'cleartext' and 'encrypt' options, the key returned is a placeholder for an internally stored key. See the 'Support for Built-in Keys' section in RFC CCCC for information about hidden keys."; } } } } output { uses ct:asymmetric-key-pair-grouping; } } // end generate-public-key } yuma123_2.14/netconf/modules/ietf-draft/ietf-tls-common.yang0000664000175000017500000002221114770023131024147 0ustar vladimirvladimirmodule ietf-tls-common { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-tls-common"; prefix tlscmn; import iana-tls-cipher-suite-algs { prefix tlscsa; reference "RFC FFFF: YANG Groupings for TLS Clients and SSH Servers"; } import ietf-crypto-types { prefix ct; reference "RFC AAAA: YANG Data Types and Groupings for Cryptography"; } import ietf-keystore { prefix ks; reference "RFC CCCC: A YANG Data Model for a Keystore"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG List: NETCONF WG list WG Web: https://datatracker.ietf.org/wg/netconf Author: Kent Watsen Author: Jeff Hartley Author: Gary Wu "; description "This module defines a common features and groupings for Transport Layer Security (TLS). Copyright (c) 2022 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC FFFF (https://www.rfc-editor.org/info/rfcFFFF); see the RFC itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here."; revision 2022-05-24 { description "Initial version"; reference "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers"; } // Features feature tls10 { status "obsolete"; description "TLS Protocol Version 1.0 is supported. TLS 1.0 is obsolete and thus it is NOT RECOMMENDED to enable this feature."; reference "RFC 2246: The TLS Protocol Version 1.0"; } feature tls11 { status "obsolete"; description "TLS Protocol Version 1.1 is supported. TLS 1.1 is obsolete and thus it is NOT RECOMMENDED to enable this feature."; reference "RFC 4346: The Transport Layer Security (TLS) Protocol Version 1.1"; } feature tls12 { status "deprecated"; description "TLS Protocol Version 1.2 is supported TLS 1.2 is obsolete and thus it is NOT RECOMMENDED to enable this feature."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } feature tls13 { description "TLS Protocol Version 1.3 is supported."; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } feature hello-params { description "TLS hello message parameters are configurable."; } feature public-key-generation { description "Indicates that the server implements the 'generate-public-key' RPC."; } // Identities identity tls-version-base { description "Base identity used to identify TLS protocol versions."; } identity tls10 { if-feature "tls10"; base tls-version-base; status "obsolete"; description "TLS Protocol Version 1.0."; reference "RFC 2246: The TLS Protocol Version 1.0"; } identity tls11 { if-feature "tls11"; base tls-version-base; status "obsolete"; description "TLS Protocol Version 1.1."; reference "RFC 4346: The Transport Layer Security (TLS) Protocol Version 1.1"; } identity tls12 { if-feature "tls12"; base tls-version-base; status "deprecated"; description "TLS Protocol Version 1.2."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity tls13 { if-feature "tls13"; base tls-version-base; description "TLS Protocol Version 1.3."; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } typedef epsk-supported-hash { type enumeration { enum sha-256 { description "The SHA-256 Hash."; } enum sha-384 { description "The SHA-384 Hash."; } } description "As per Section 4.2.11 of RFC 8446, the hash algorithm supported by an instance of an External Pre-Shared Key (EPSK)."; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3 I-D.ietf-tls-external-psk-importer: Importing External PSKs for TLS I-D.ietf-tls-external-psk-guidance: Guidance for External PSK Usage in TLS"; } // Groupings grouping hello-params-grouping { description "A reusable grouping for TLS hello message parameters."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; container tls-versions { description "Parameters regarding TLS versions."; leaf-list tls-version { type identityref { base tls-version-base; } description "Acceptable TLS protocol versions. If this leaf-list is not configured (has zero elements) the acceptable TLS protocol versions are implementation- defined."; } } container cipher-suites { description "Parameters regarding cipher suites."; leaf-list cipher-suite { type identityref { base tlscsa:cipher-suite-alg-base; } ordered-by user; description "Acceptable cipher suites in order of descending preference. The configured host key algorithms should be compatible with the algorithm used by the configured private key. Please see Section 5 of RFC FFFF for valid combinations. If this leaf-list is not configured (has zero elements) the acceptable cipher suites are implementation- defined."; reference "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers"; } } } // hello-params-grouping rpc generate-public-key { if-feature "public-key-generation"; description "Requests the device to generate an public key using the specified key algorithm."; input { leaf algorithm { type tlscsa:cipher-suite-algorithm-ref; mandatory true; description "The cipher suite algorithm that the generated key is to work with. Implementations derive the public key algorithm from the cipher suite algorithm. Example: cipher suite 'tls-rsa-with-aes-256-cbc-sha256' maps to the RSA public key."; } leaf bits { type uint16; description "Specifies the number of bits in the key to create. For RSA keys, the minimum size is 1024 bits and the default is 3072 bits. Generally, 3072 bits is considered sufficient. DSA keys must be exactly 1024 bits as specified by FIPS 186-2. For elliptical keys, the 'bits' value determines the key length of the curve (e.g., 256, 384 or 521), where valid values supported by the server are conveyed via an unspecified mechanism. For some public algorithms, the keys have a fixed length and the 'bits' value, if specified, will be ignored."; } choice private-key-encoding { default cleartext; description "A choice amongst optional private key handling."; case cleartext { leaf cleartext { type empty; description "Indicates that the private key is to be returned as a cleartext value."; } } case encrypt { if-feature "ct:private-key-encryption"; container encrypt-with { description "Indicates that the key is to be encrypted using the specified symmetric or asymmetric key."; uses ks:encrypted-by-choice-grouping; } } case hide { if-feature "ct:hidden-keys"; leaf hide { type empty; description "Indicates that the private key is to be hidden. Unlike the 'cleartext' and 'encrypt' options, the key returned is a placeholder for an internally stored key. See the 'Support for Built-in Keys' section in RFC CCCC for information about hidden keys."; } } } } output { uses ct:asymmetric-key-pair-grouping; } } // end generate-public-key } yuma123_2.14/netconf/modules/ietf-draft/ietf-tcp-server.yang0000664000175000017500000000750514770023131024162 0ustar vladimirvladimirmodule ietf-tcp-server { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-tcp-server"; prefix tcps; import ietf-inet-types { prefix inet; reference "RFC 6991: Common YANG Data Types"; } import ietf-tcp-common { prefix tcpcmn; reference "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; } organization "IETF NETCONF (Network Configuration) Working Group and the IETF TCP Maintenance and Minor Extensions (TCPM) Working Group"; contact "WG Web: https://datatracker.ietf.org/wg/netconf https://datatracker.ietf.org/wg/tcpm WG List: NETCONF WG list TCPM WG list Authors: Kent Watsen Michael Scharf "; description "This module defines reusable groupings for TCP servers that can be used as a basis for specific TCP server instances. Copyright (c) 2022 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC DDDD (https://www.rfc-editor.org/info/rfcDDDD); see the RFC itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here."; revision 2022-05-24 { description "Initial version"; reference "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; } // Features feature tcp-server-keepalives { description "Per socket TCP keepalive parameters are configurable for TCP servers on the server implementing this feature."; } // Groupings grouping tcp-server-grouping { description "A reusable grouping for configuring a TCP server. Note that this grouping uses fairly typical descendant node names such that a stack of 'uses' statements will have name conflicts. It is intended that the consuming data model will resolve the issue (e.g., by wrapping the 'uses' statement in a container called 'tcp-server-parameters'). This model purposely does not do this itself so as to provide maximum flexibility to consuming models."; leaf local-address { type inet:ip-address; mandatory true; description "The local IP address to listen on for incoming TCP client connections. INADDR_ANY (0.0.0.0) or INADDR6_ANY (0:0:0:0:0:0:0:0 a.k.a. ::) MUST be used when the server is to listen on all IPv4 or IPv6 addresses, respectively."; } leaf local-port { type inet:port-number; default "0"; description "The local port number to listen on for incoming TCP client connections. An invalid default value (0) is used (instead of 'mandatory true') so that an application level data model may 'refine' it with an application specific default port number value."; } uses tcpcmn:tcp-common-grouping { augment "keepalives" { if-feature "tcp-server-keepalives"; description "Add an if-feature statement so that implementations can choose to support TCP server keepalives."; } } } } yuma123_2.14/netconf/modules/ietf-draft/ietf-network-bridge-scheduler.yang0000664000175000017500000003630114770023131026763 0ustar vladimirvladimirmodule ietf-network-bridge-scheduler { namespace "urn:ietf:params:xml:ns:yang:ietf-network-bridge-scheduler"; prefix sched; import ietf-network-bridge { prefix netbr; } import ietf-network-bridge-flows { prefix flow; } import ietf-interfaces { prefix if; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: Editor: Vladimir Vassilev "; description "This module contains a collection of YANG definitions for description and management of network bridge schedulers. Copyright (c) 2020 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX; see the RFC itself for full legal notices."; revision 2021-02-17 { description "Initial revision."; reference "RFC XXXX: Network Bridge"; } identity gate-controller { description "Represents the gate control block type e.g. round-robin, priority-based, time-aware-802dot1qbv etc."; } identity aggregator { base gate-controller; description "Abstract identity that all gate control blocks with multiple inputs and single output use as basetype e.g. round-robin, priority-based, time-aware-802dot1qbv etc."; } identity filter { base gate-controller; description "Abstract identity that all gate control blocks with corresponding input and output instances use as basetype e.g. rate-limiters, simple propagation delays, shapers etc."; } identity gate-controller-input { description "Identifies gate controller input type."; } identity private-queue-aggregator-input { base gate-controller-input; description "Abstract input identifier for gate controller inputs of the aggregator type where all instances of the input types derived from this identifier have their own private queue."; } identity shared-queue-aggregator-input { base gate-controller-input; description "Abstract input identifier for gate controller inputs of the aggregator type where all instances of the input types derived from this identifier have shared queue."; } identity filter-input { base gate-controller-input; description "Abstract input identifier for gate controller inputs of the filter type."; } identity traffic-class { description "Identifies traffic class."; } identity port-class { description "Identifies port class. Ports that belong to a class will have the same scheduler-class on their egress and have identical flow path through the rest of the scheduler classes."; } typedef port-class-ref { type leafref { path "/netbr:bridge/sched:port-classes/sched:port-class"; } description "This type is used by data models that need to reference configured port-class."; } typedef traffic-class-ref { type leafref { path "/netbr:bridge/sched:traffic-classes/sched:traffic-class"; } description "This type is used by data models that need to reference configured traffic-class."; } grouping gate-controller-input-config { description "Common gate controller input configuration data definitions."; leaf constant-propagation-delay { type uint64; units "picoseconds"; description "Constant delay attributed to delays in the gate-controller."; } leaf configurable-delay-line { type uint64; units "picoseconds"; description "Some gate controllers can delay the flow of packets with configurable delay which is added to the constant propagation-delay. Only inputs with zero queue lengths have deterministic delays equal to the sum of the constant-propagation-delay and the configurable-delay-line leafs. Inputs with queues have variable higher delay with dynamic component based on the controllers logic."; } leaf queue-len { type uint32; units "bytes"; description "Length of the queue."; } } grouping gate-controller-queue-state { description "Common gate controller queue state data definitions."; leaf queued-pkts { type uint64; config false; description "Number of packets queued."; } leaf queued-bytes { type uint64; config false; description "Number of bytes of the packets queued."; } leaf discards { type uint64; config false; description "The total number of discarded packets that were received on this input. This includes but is not limited to the overflow-discards. For example gate-controllers can start discarding certain packets before the input queue is filled. These discards are not registered as overflow-discards. The lower 32 bits of the sum of all discards counters part of a scheduler are equal to the /if:interfaces/if:interface/if:statistics/if:out-discards counter for the corresponding interface."; } leaf overflow-discards { type uint64; config false; description "Unintended discard caused by overflow of the input queue of the gate controller."; } leaf error-discards { type uint64; config false; description "Unintended discards caused by error in the scheduler."; } } augment "/flow:flows/flow:flow" { description "Adds traffic-class to the flow model."; leaf traffic-class { type leafref { path "/netbr:bridge/sched:traffic-classes/sched:traffic-class"; } description "Specifies the traffic class of a flow. When not present the default traffic class is used."; } } augment "/netbr:bridge/netbr:ports/netbr:port" { description "Adds port class and class-instance-index leafs."; leaf class { type port-class-ref; description "A port class allows discrimination of ports based on features and supported scheduler options."; } leaf class-instance-index { type uint32; description "Index enumerating the instances of the same port class."; } } augment "/netbr:bridge" { description "Adds scheduler specific data to the bridge model."; leaf default-traffic-class { type traffic-class-ref; description "Specifies the traffic-class for flows without /flow:flows/flow:flow/sched:traffic-class leaf."; } leaf default-port-class { type traffic-class-ref; description "Specifies the traffic-class for flows without /flow:flows/flow:flow/sched:traffic-class leaf."; } container traffic-classes { description "Contains the leaf-list of available traffic classes."; leaf-list traffic-class { type identityref { base traffic-class; } description "Leaf-list of available traffic classes."; } } container port-classes { description "Contains the leaf-list of available port classes."; leaf-list port-class { type identityref { base port-class; } description "Leaf-list of available port classes."; } } } augment "/if:interfaces/if:interface" { description "Augments the interface model with scheduler specific data."; container scheduler { description "Each egress capable interface has scheduler. The scheduler is a tree of interconnected gate controllers."; container gate-controllers { description "Contains the list of gate controllers."; list gate-controller { key "id"; description "The gate controller model can be augmented by external modules defining custom gate controller types."; leaf id { type string; description "Gate controller identifier."; } leaf type { type identityref { base gate-controller; } mandatory true; description "Gate controller type."; } container inputs { description "Contains the list of inputs."; list input { key "class index"; description "Double key list. There can be multiple instances of each input class."; leaf class { type identityref { base gate-controller-input; } description "Input class."; } leaf index { type uint32; description "Index of the input instance of the corresponding input class."; } uses gate-controller-queue-state; } } container input-classes { description "Contains the list of input-classes."; list input-class { key "class"; description "Contains configuration and state data that is common for all instances of certain input class."; leaf class { type identityref { base gate-controller-input; } description "Input class."; } uses gate-controller-queue-state; } } } } } } augment "/netbr:bridge" { description "Augments the bridge module with scheduler specific configuration data."; container scheduler-classes { description "Contains list of scheduler-classes."; list scheduler-class { key "egress-port-class"; description "All ports of same class inherit the scheduler configuration. The instance specific scheduler configuration defined under /interfaces/interface/scheduler can override this configuration."; leaf egress-port-class { type sched:port-class-ref; description "The port class the scheduler coonfiguration applies for."; } container inputs { description "Contains list of inputs."; list input { key "traffic-class ingress-port-class"; description "Double key list. There can be multiple instances of each input class."; leaf traffic-class { type traffic-class-ref; description "Reference to traffic class."; } leaf ingress-port-class { type port-class-ref; description "Reference to port class."; } leaf gate-controller { type leafref { path "../../../gate-controllers/gate-controller/id"; } description "Reference to gate controller id."; } leaf input-class { type leafref { path "../../../gate-controllers/gate-controller" + "[id=current()/../gate-controller]" + "/inputs/input/class"; } description "Reference to a input class defined for the specified gate controller."; } leaf base-index { type uint32; default "0"; description "Base index for the inputs of this class."; } } } container gate-controllers { description "Contains the list of gate controllers."; list gate-controller { key "id"; description "The gate controller model can be augmented by external modules defining custom gate controller types."; leaf id { type string; description "Gate controller identifier."; } leaf type { type identityref { base gate-controller; } mandatory true; description "Gate controller type."; } container inputs { description "Contains the list of inputs."; list input { key "class"; description "Double key list. There can be multiple instances of each input class."; leaf class { type identityref { base gate-controller-input; } mandatory true; description "Input class."; } leaf instance-count { type uint32; description "Number of input instances of the specified class."; } uses gate-controller-input-config; } } container output { description "Configuration of the gate-controller input the output is connected to."; leaf gate-controller { type leafref { path "../../../gate-controller/id"; } description "Specifies the gate-controller this output is connected to."; } leaf input-class { type leafref { path "../../../gate-controller" + "[id=current()/../gate-controller]/" + "inputs/input/class"; } description "Specifies the input-class of the gate-controller the input this output is connected to."; } leaf index { type uint32; description "In case the gate-controller is aggregator this is the index of the only input it is connected to from the specified class. If the gate-controller is filter with more then one input-output pairs this is the base index and the remaining indexes are connected to consecutive input indexes of the specified input class."; } } } } } } } } yuma123_2.14/netconf/modules/ietf-draft/ietf-network-bridge-scheduler-state.yang0000664000175000017500000000753714770023131030112 0ustar vladimirvladimirmodule ietf-network-bridge-scheduler-state { namespace "urn:ietf:params:xml:ns:yang:ietf-network-bridge-scheduler-state"; prefix sched-state; import ietf-interfaces { prefix if; } import ietf-network-bridge-scheduler { prefix sched; } organization "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; contact "WG Web: WG List: Editor: Vladimir Vassilev "; description "This module contains /if:interfaces-state/if:interface augmentation which mirrors the 'scheduler' container as the one part of the 'ietf-network-bridge-scheduler' but contains only read-only state data. The data model is not needed when the underlying implementation infrastructure supports the Network Management Datastore Architecture (NMDA). Copyright (c) 2020 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX; see the RFC itself for full legal notices."; revision 2021-02-17 { description "Initial revision."; reference "RFC XXXX: Network Bridge"; } augment "/if:interfaces-state/if:interface" { description "Augments the interface model with scheduler specific data."; container scheduler { description "Each egress capable interface has scheduler. The scheduler is a tree of interconnected gate controllers."; container gate-controllers { description "Contains the list of gate controllers."; list gate-controller { key "id type"; description "The gate controller model can be augmented by external modules defining custom gate controller types."; leaf id { type string; description "Gate controller identifier."; } leaf type { type identityref { base sched:gate-controller; } mandatory true; description "Gate controller type."; } container inputs { description "Contains the list of inputs."; list input { key "class index"; description "Double key list. There can be multiple instances of each input class."; leaf class { type identityref { base sched:gate-controller-input; } description "Input class."; } leaf index { type uint32; description "Index of the input instance of the corresponding input class."; } uses sched:gate-controller-queue-state; } } container input-classes { description "Contains the list of input-classes."; list input-class { key "class"; description "Contains the list of input-classes."; leaf class { type identityref { base sched:gate-controller-input; } description "Input class."; } uses sched:gate-controller-queue-state; } } } } } } } yuma123_2.14/netconf/modules/ietf-draft/ietf-keystore.yang0000664000175000017500000003260014770023131023727 0ustar vladimirvladimirmodule ietf-keystore { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-keystore"; prefix ks; import ietf-netconf-acm { prefix nacm; reference "RFC 8341: Network Configuration Access Control Model"; } import ietf-crypto-types { prefix ct; reference "RFC AAAA: YANG Data Types and Groupings for Cryptography"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: https://datatracker.ietf.org/wg/netconf WG List: NETCONF WG list Author: Kent Watsen "; description "This module defines a 'keystore' to centralize management of security credentials. Copyright (c) 2022 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC CCCC (https://www.rfc-editor.org/info/rfcCCCC); see the RFC itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document are to be interpreted as described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, they appear in all capitals, as shown here."; revision 2022-05-24 { description "Initial version"; reference "RFC CCCC: A YANG Data Model for a Keystore"; } /****************/ /* Features */ /****************/ feature central-keystore-supported { description "The 'central-keystore-supported' feature indicates that the server supports the keystore (i.e., implements the 'ietf-keystore' module)."; } feature local-definitions-supported { description "The 'local-definitions-supported' feature indicates that the server supports locally-defined keys."; } feature asymmetric-keys { description "The 'asymmetric-keys' feature indicates that the server supports asymmetric keys in keystores."; } feature symmetric-keys { description "The 'symmetric-keys' feature indicates that the server supports symmetric keys in keystores."; } /****************/ /* Typedefs */ /****************/ typedef symmetric-key-ref { type leafref { path "/ks:keystore/ks:symmetric-keys/ks:symmetric-key" + "/ks:name"; } description "This typedef enables modules to easily define a reference to a symmetric key stored in the keystore, when this module is implemented."; } typedef asymmetric-key-ref { type leafref { path "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key" + "/ks:name"; } description "This typedef enables modules to easily define a reference to an asymmetric key stored in the keystore, when this module is implemented."; } /*****************/ /* Groupings */ /*****************/ grouping encrypted-by-choice-grouping { description "A grouping that defines a 'choice' statement that can be augmented into the 'encrypted-by' node, present in the 'symmetric-key-grouping' and 'asymmetric-key-pair-grouping' groupings defined in RFC AAAA, enabling references to keys in the keystore, when this module is implemented."; choice encrypted-by-choice { nacm:default-deny-write; mandatory true; description "A choice amongst other symmetric or asymmetric keys."; case symmetric-key-ref { if-feature "central-keystore-supported"; if-feature "symmetric-keys"; leaf symmetric-key-ref { type ks:symmetric-key-ref; description "Identifies the symmetric key used to encrypt the associated key."; } } case asymmetric-key-ref { if-feature "central-keystore-supported"; if-feature "asymmetric-keys"; leaf asymmetric-key-ref { type ks:asymmetric-key-ref; description "Identifies the asymmetric key whose public key encrypted the associated key."; } } } } grouping asymmetric-key-certificate-ref-grouping { description "This grouping defines a reference to a specific certificate associated with an asymmetric key stored in the keystore, when this module is implemented."; leaf asymmetric-key { nacm:default-deny-write; if-feature "central-keystore-supported"; if-feature "asymmetric-keys"; type ks:asymmetric-key-ref; must '../certificate'; description "A reference to an asymmetric key in the keystore."; } leaf certificate { nacm:default-deny-write; type leafref { path "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key" + "[ks:name = current()/../asymmetric-key]/" + "ks:certificates/ks:certificate/ks:name"; } must '../asymmetric-key'; description "A reference to a specific certificate of the asymmetric key in the keystore."; } } // local-or-keystore-* groupings grouping local-or-keystore-symmetric-key-grouping { description "A grouping that expands to allow the symmetric key to be either stored locally, i.e., within the using data model, or a reference to a symmetric key stored in the keystore. Servers that do not 'implement' this module, and hence 'central-keystore-supported' is not defined, SHOULD augment in custom 'case' statements enabling references to the alternate keystore locations."; choice local-or-keystore { nacm:default-deny-write; mandatory true; description "A choice between an inlined definition and a definition that exists in the keystore."; case local { if-feature "local-definitions-supported"; if-feature "symmetric-keys"; container local-definition { description "Container to hold the local key definition."; uses ct:symmetric-key-grouping; } } case keystore { if-feature "central-keystore-supported"; if-feature "symmetric-keys"; leaf keystore-reference { type ks:symmetric-key-ref; description "A reference to an symmetric key that exists in the keystore, when this module is implemented."; } } } } grouping local-or-keystore-asymmetric-key-grouping { description "A grouping that expands to allow the asymmetric key to be either stored locally, i.e., within the using data model, or a reference to an asymmetric key stored in the keystore. Servers that do not 'implement' this module, and hence 'central-keystore-supported' is not defined, SHOULD augment in custom 'case' statements enabling references to the alternate keystore locations."; choice local-or-keystore { nacm:default-deny-write; mandatory true; description "A choice between an inlined definition and a definition that exists in the keystore."; case local { if-feature "local-definitions-supported"; if-feature "asymmetric-keys"; container local-definition { description "Container to hold the local key definition."; uses ct:asymmetric-key-pair-grouping; } } case keystore { if-feature "central-keystore-supported"; if-feature "asymmetric-keys"; leaf keystore-reference { type ks:asymmetric-key-ref; description "A reference to an asymmetric key that exists in the keystore, when this module is implemented. The intent is to reference just the asymmetric key without any regard for any certificates that may be associated with it."; } } } } grouping local-or-keystore-asymmetric-key-with-certs-grouping { description "A grouping that expands to allow an asymmetric key and its associated certificates to be either stored locally, i.e., within the using data model, or a reference to an asymmetric key (and its associated certificates) stored in the keystore. Servers that do not 'implement' this module, and hence 'central-keystore-supported' is not defined, SHOULD augment in custom 'case' statements enabling references to the alternate keystore locations."; choice local-or-keystore { nacm:default-deny-write; mandatory true; description "A choice between an inlined definition and a definition that exists in the keystore."; case local { if-feature "local-definitions-supported"; if-feature "asymmetric-keys"; container local-definition { description "Container to hold the local key definition."; uses ct:asymmetric-key-pair-with-certs-grouping; } } case keystore { if-feature "central-keystore-supported"; if-feature "asymmetric-keys"; leaf keystore-reference { type ks:asymmetric-key-ref; description "A reference to an asymmetric-key (and all of its associated certificates) in the keystore, when this module is implemented."; } } } } grouping local-or-keystore-end-entity-cert-with-key-grouping { description "A grouping that expands to allow an end-entity certificate (and its associated asymmetric key pair) to be either stored locally, i.e., within the using data model, or a reference to a specific certificate in the keystore. Servers that do not 'implement' this module, and hence 'central-keystore-supported' is not defined, SHOULD augment in custom 'case' statements enabling references to the alternate keystore locations."; choice local-or-keystore { nacm:default-deny-write; mandatory true; description "A choice between an inlined definition and a definition that exists in the keystore."; case local { if-feature "local-definitions-supported"; if-feature "asymmetric-keys"; container local-definition { description "Container to hold the local key definition."; uses ct:asymmetric-key-pair-with-cert-grouping; } } case keystore { if-feature "central-keystore-supported"; if-feature "asymmetric-keys"; container keystore-reference { uses asymmetric-key-certificate-ref-grouping; description "A reference to a specific certificate associated with an asymmetric key stored in the keystore, when this module is implemented."; } } } } grouping keystore-grouping { description "Grouping definition enables use in other contexts. If ever done, implementations MUST augment new 'case' statements into the various local-or-keystore 'choice' statements to supply leafrefs to the model-specific location(s)."; container asymmetric-keys { nacm:default-deny-write; if-feature "asymmetric-keys"; description "A list of asymmetric keys."; list asymmetric-key { key "name"; description "An asymmetric key."; leaf name { type string; description "An arbitrary name for the asymmetric key."; } uses ct:asymmetric-key-pair-with-certs-grouping; } } container symmetric-keys { nacm:default-deny-write; if-feature "symmetric-keys"; description "A list of symmetric keys."; list symmetric-key { key "name"; description "A symmetric key."; leaf name { type string; description "An arbitrary name for the symmetric key."; } uses ct:symmetric-key-grouping; } } } /*********************************/ /* Protocol accessible nodes */ /*********************************/ container keystore { if-feature central-keystore-supported; description "A central keystore containing a list of symmetric keys and a list of asymmetric keys."; nacm:default-deny-write; uses keystore-grouping { augment "symmetric-keys/symmetric-key/key-type/encrypted-key/" + "encrypted-key/encrypted-by" { description "Augments in a choice statement enabling the encrypting key to be any other symmetric or asymmetric key in the central keystore."; uses encrypted-by-choice-grouping; } augment "asymmetric-keys/asymmetric-key/private-key-type/" + "encrypted-private-key/encrypted-private-key/" + "encrypted-by" { description "Augments in a choice statement enabling the encrypting key to be any other symmetric or asymmetric key in the central keystore."; uses encrypted-by-choice-grouping; } } } } yuma123_2.14/netconf/modules/yuma123/0000775000175000017500000000000014770023131017430 5ustar vladimirvladimiryuma123_2.14/netconf/modules/yuma123/netconfd-ex.yang0000664000175000017500000000533514770023131022530 0ustar vladimirvladimirmodule netconfd-ex { namespace "http://yuma123.org/ns/netconfd-ex"; prefix "netconfd-ex"; import netconfd { prefix netconfd; } import yuma-types { prefix nt; } import ietf-inet-types { prefix inet; } organization "Yuma123"; contact "Vladimir Vassilev "; description "This module contains extra parameters for netconfd"; revision 2018-08-14 { description "Removed yet unimplemented yang-library-spec case augmentation. Shall be restored when implemented."; } augment "/netconfd:netconfd" { description "Adds extra parameters to netconfd."; leaf ncxserver-sockname { description "Overrides the default /tmp/ncxserver.sock UNIX socket name netconfd listens on for incoming connections. You have to add corresponding entry to /etc/ssh/sshd_config e.g.: ... Port 1830 Subsystem netconf \"/usr/sbin/netconf-subsystem \\ --ncxserver-sockname=/tmp/ncxserver1.sock\" ..."; type string; } leaf tcp-direct-address { description "The server will use this local TCP address to listen for incoming connections instead of the default /tmp/ncxserver.sock UNIX socket. By default if only tcp-direct-port is specified the addressed used is localhost."; type inet:host; } leaf tcp-direct-port { description "The server will use this local TCP port to listen for incoming connections instead of the default /tmp/ncxserver.sock UNIX socket. tcp-direct-address controls the server bind address."; type inet:port-number; } leaf non-advertised-module { description "Specifying a module as non advertised excludes it from the capabilities list in the message. For example taking out the RPCs defined in a given module from the list of available commands in a cli."; type nt:NcModuleSpec; } leaf max-sessions { description "Specifies the maximum number of sessions that can be accepted."; type uint32; default 1024; } leaf validate-config-only { description "When present netconfd returns immediately after initialization and startup configuration validation is complete without opening socket and waiting for incoming NETCONF sessions. Enables use of netconfd as command line configuration validator."; type empty; } leaf with-nmda { description "If set to 'true', then NMDA is enabled."; type boolean; default false; } } } yuma123_2.14/netconf/modules/yuma123/yuma123-netconf-node.yang0000664000175000017500000000123714770023131024071 0ustar vladimirvladimirmodule yuma123-netconf-node { namespace "http://yuma123.org/ns/yuma123-netconf-node"; prefix "ncnode"; import "ietf-network" { prefix nd; } import "ietf-netconf-client" { prefix ncc; } organization "yuma123"; contact "Vladimir Vassilev "; description "This module augments ietf-network with parameters for connecting to NETCONF enabled nodes."; revision 2018-08-27 { description "Initial."; } augment "/nd:networks/nd:network/nd:node" { leaf netconf-server { type leafref { path "/ncc:netconf-client/ncc:initiate/ncc:netconf-server/ncc:name"; } } } } yuma123_2.14/netconf/modules/yuma123/yuma123-mysession-cache.yang0000664000175000017500000000162314770023131024603 0ustar vladimirvladimirmodule yuma123-mysession-cache { namespace "http://yuma123.org/ns/yuma123-mysession-cache"; prefix "ymyses-cache"; import yuma-mysession { prefix myses; } import ietf-netconf-acm { prefix nacm; } organization "Yuma123"; contact "Vladimir Vassilev "; description "Augments yuma-mysession get-my-session and set-my-session RPCs with cache-timeout param."; revision 2018-11-12 { description "Initial version."; } grouping cache-params { leaf cache-timeout { description "The time it takes for cached virtual data to expire and be updated. When 0 caching is disabled."; type uint32; units seconds; nacm:default-deny-all; } } augment "/myses:set-my-session/myses:input" { uses cache-params; } augment "/myses:get-my-session/myses:output" { uses cache-params; } } yuma123_2.14/netconf/modules/yuma123/netconfd.yang0000664000175000017500000003540514770023131022117 0ustar vladimirvladimirmodule netconfd { namespace "http://yuma123.org/ns/netconfd"; prefix "nd"; import yuma-ncx { prefix ncx; } import yuma-types { prefix nt; } import yuma-app-common { prefix ncxapp; } import ietf-inet-types { prefix inet; } organization "yuma123"; contact "Vladimir Vassilev ."; description "Configuration Parameters for netconfd; This module is not advertised by the server. It contains only CLI parameters."; revision 2018-04-01 { description "Added model-spec choice allowing alternatives to be augmented supplementing the discrete cli parameters --module, --feature-enable etc."; } revision 2017-07-25 { description "Changed --startup-error default value to 'stop'. Changed responsible contact from Netconf central to yuma123"; } revision 2012-10-05 { description "Add uses for YumaHomeParm"; } revision 2011-12-15 { description "Add --running-error parameter."; } revision 2011-10-08 { description "Add --home parameter."; } revision 2011-08-27 { description "Add --runpath parameter. Add --factory-startup parameter."; } revision 2011-07-20 { description "Add --audit-log and --audit-log-append CLI parameters. Add --system-sorted CLI parameter. Make with-defaults enum local to prevent report-all-tagged from being accepted as a basic mode."; } revision 2011-05-29 { description "Removed superuser YANG default to disable by default, to make sure an admin has to explicitly enable this feature."; } revision 2011-04-24 { description "Added --protocols parameter via uses ProtocolsParm. Not available in yuma v1 branch."; } revision 2011-04-02 { description "Added --delete-np-containers parameter."; } revision 2010-05-13 { description "Added --with-url to enable :url capability."; } revision 2010-01-14 { description "Initial version for 0.9.9 release."; } grouping StartupChoice { choice start { description "select startup config for boot load"; // default is set in the agt_profile leaf no-startup { description "If present, do not load the startup config file. Use the factory default settings but do not overwrite the NV-storage startup unless it is altered. This option does not delete the startup config file if it exists."; type empty; } leaf factory-startup { description "Force the system to use the factory configuration and delete the startup config file if it exists. Force the NV-storage startup to contain the factory default configuration."; type empty; } leaf startup { description "The full or relative filespec of the startup config file to use. If present, overrides the default startup config file name 'startup-cfg.xml', This will also override the YUMA_DATAPATH environment variable and the datapath CLI parameter, if the first character is the forward slash '/', indicating an absolute file path."; type string; } } } container netconfd { ncx:cli; description "Server CLI for the NETCONF protocol. Usage: netconfd [parameters]"; uses ncxapp:NcxAppCommon; uses ncxapp:ConfigParm; uses ncxapp:YumaHomeParm; uses ncxapp:HomeParm; uses ncxapp:SubdirsParm; uses ncxapp:ProtocolsParm; uses ncxapp:RunpathParm; leaf access-control { description "Controls how access control is initially enforced by the server."; type enumeration { enum enforcing { description "All configured access control rules will be enforced."; } enum permissive { description "All configured access control rules will be enforced for write and execute requests. All read requests will be allowed, unless the requested object contains the 'nacm:very-secure' extension. In that case, all configured access control rules will be enforced."; } enum disabled { description "All read, write, and execute requests will be allowed, unless the object contains the 'nacm:secure' or 'nacm:very-secure' extension. If the 'nacm:secure' extension is in effect, then all configured access control rules will be enforced for write and execute requests. If the 'nacm:very-secure' extension is in effect, then all configured access control rules will be enforced for all requests. Use this mode with caution."; } enum off { description "All access control enforcement is disabled. Use this mode with extreme caution."; } } default enforcing; } leaf audit-log { description "Filespec for the server audit log file to use in addition to the normal log file or STDOUT."; type string; } leaf audit-log-append { description "If present, the audit log will be appended not over-written. If not, the audit log will be over-written. Only meaningful if the 'audit-log' parameter is also present."; type empty; } leaf default-style { description "Selects the type of filtering behavior the server will advertise as the 'basic' behavior in the 'with-defaults' capability. The server will use this default handling behavior if the 'with-defaults' parameter is not explicitly set. Also, when saving a configuration to NV-storage, this value will be used for filtering defaults from the saved configuration. See wd:with-defaults leaf for enumeration details."; type enumeration { enum report-all; enum trim; enum explicit; } default explicit; } leaf delete-empty-npcontainers { description "An empty non-presence container has no meaning in NETCONF/YANG so it may be deleted by the server. This takes non-trivial processing time for large databases, but uses less memory. Disabling this parameter will result in a larger database in both memory and NV-save."; type boolean; default false; } leaf eventlog-size { description "Specifies the maximum number of notification events that will be saved in the notification replay buffer. The oldest entries will be deleted first."; type uint32; default 1000; } leaf hello-timeout { description "Specifies the number of seconds that a session may exist before the hello PDU is received. A session will be dropped if no hello PDU is received before this number of seconds elapses. If this parameter is set to zero, then the server will wait forever for a hello message, and not drop any sessions stuck in 'hello-wait' state. Setting this parameter to zero may permit denial of service attacks, since only a limited number of concurrent sessions are supported by the server."; type uint32 { range "0 | 10 .. 3600"; } units seconds; default 600; // 10 minutes } leaf idle-timeout { description "Specifies the number of seconds that a session may remain idle without issuing any RPC requests. A session will be dropped if it is idle for an interval longer than this number of seconds. Sessions that have a notification subscription active are never dropped. If this parameter is set to zero, then the server will never drop a session because it is idle."; type uint32 { range "0 | 10 .. 360000"; } units seconds; default 3600; // 1 hour } leaf max-burst { description "Specifies the maximum number of notifications that should be sent to one session, within a one second time interval. The value 0 indicates that the server should not limit notification bursts at all."; type uint32; default 10; } leaf-list port { max-elements 4; description "Specifies the TCP ports that the server will accept connections from. These ports must also be configured in the /etc/ssh/sshd_config file for the SSH master server to accept the connection and invoke the netconf subsystem. Up to 4 port numbers can be configured. If any ports are configured, then only those values will be accepted by the server. If no ports are configured, then the server will accept connections on the netconf-ssh port (tcp/830)."; type inet:port-number; } choice model-spec { case cli { uses ncxapp:ModuleParm; uses ncxapp:DeviationParm; uses ncxapp:CommonFeatureParms; } } uses ncxapp:DatapathParm; leaf running-error { description "Controls the server behavior if any errors are encountered while validating the running database during the initial load of the running configuration at boot-time."; type enumeration { enum stop { description "Terminate the program if any errors are encountered in the running configuration."; } enum continue { description "Continue the program if any errors are encountered in the running configuration. Altering the running configuration will fail until the commit validation tests succeed."; } } default stop; } uses StartupChoice; leaf startup-error { description "Controls the server behavior if any errors are encountered while loading the startup configuration file into the running configuration at boot-time. It is possible for the startup configuration to contain errors within optional nodes. If this parameter is set to 'continue', then the validation tests on the running config (controlled by running-error) should not fail due to missing optional nodes."; type enumeration { enum stop { description "Terminate the program if any errors are encountered in the startup configuration."; } enum continue { description "Continue the program if any errors are encountered in the startup configuration. The entire module-specific data structure(s) containing the error node(s) will not be added to the running configuration at boot-time."; } } default stop; } leaf superuser { description "The user name to use as the superuser account. Any session associated with this user name will bypass all access control enforcement. See yuma-nacm.yang for more details. To disable the superuser account completely, set this parameter to the empty string or do not set it at all. The default mode is to disable superuser access."; type union { type nt:NcxName; type string { length 0; } } } leaf system-sorted { description "Indicates whether ordered-by system leaf-lists and lists will be kept in sorted order."; type boolean; default true; } leaf target { description "The database to use as the target of edit-config operations."; type enumeration { enum running { description "Write to the running config and support the :writable-running capability."; } enum candidate { description "Write to the candidate config and support the :candidate and :confirmed-commit capabilities."; } } // default is set in the agt_profile default candidate; } leaf usexmlorder { description "If present, then XML element order will be enforced. Otherwise, XML element order errors will not be generated if possible. Default is no enforcement of strict XML order."; type empty; } leaf with-startup { description "If set to 'true', then the :startup capability will be enabled. Otherwise, the :startup capability will not be enabled. This capability makes the NV-save operation an explicit operation instead of an automatic save."; type boolean; default false; } leaf with-url { description "If set to 'true', then the :url capability will be enabled. Otherwise, the :url capability will not be enabled. This capability requires a file system and may introduce security risks because internal files such as startup-cfg.xml and backup-cfg.xml will be exposed."; type boolean; default true; } leaf with-validate { description "If set to 'true', then the :validate capability will be enabled. Otherwise, the :validate capability will not be enabled. This capability requires extensive memory resources."; type boolean; default true; } } } yuma123_2.14/netconf/modules/yuma123/yuma123-netconf.yang0000664000175000017500000010714714770023131023155 0ustar vladimirvladimirmodule yuma123-netconf { // the namespace for NETCONF XML definitions is unchanged // from RFC 4741, which this document replaces namespace "urn:ietf:params:xml:ns:netconf:base:1.0"; prefix nc; import ietf-inet-types { prefix inet; } import ietf-netconf-acm { prefix nacm; } // for the xpath data type import ietf-yang-types { prefix "yang"; } // for NCX 'metadata' language extension import yuma-ncx { prefix "ncx"; } // for the uri data type import yuma123-netconf-types { prefix "nct"; } organization "yuma123"; contact "vladimir@transpacket.com"; description "This module imitates ietf-netconf.yang with additional YUMA extensions. The solution is a hack and the module is only for internal use."; // organization // "IETF NETCONF (Network Configuration) Working Group"; // // contact // "WG Web: // WG List: // // WG Chair: Bert Wijnen // // // WG Chair: Mehmet Ersue // // // Editor: Martin Bjorklund // // // Editor: Juergen Schoenwaelder // // // Editor: Andy Bierman // "; // description // "NETCONF Protocol Data Types and Protocol Operations. // // Copyright (c) 2011 IETF Trust and the persons identified as // the document authors. All rights reserved. // // Redistribution and use in source and binary forms, with or // without modification, is permitted pursuant to, and subject // to the license terms contained in, the Simplified BSD License // set forth in Section 4.c of the IETF Trust's Legal Provisions // Relating to IETF Documents // (http://trustee.ietf.org/license-info). // // This version of this YANG module is part of RFC 6241; see // the RFC itself for full legal notices."; // // revision 2011-06-01 { // description // "Initial revision; // 2013-09-29: Updated to include NACM attributes, // as specified in RFC 6536: sec 3.2.5 and 3.2.8"; // reference // "RFC 6241: Network Configuration Protocol"; // } revision 2017-06-22 { description "Synced with the latest RFC 6241 module."; } revision 2017-03-26 { description "Changed namespace in order to edit the original netconfcentral model. Splitted yuma-netconf into yuma123-netconf and yuma123-netconf-types"; } extension get-filter-element-attributes { description "If this extension is present within an 'anyxml' statement named 'filter', which must be conceptually defined within the RPC input section for the and protocol operations, then the following unqualified XML attribute is supported within the element, within a or protocol operation: type : optional attribute with allowed value strings 'subtree' and 'xpath'. If missing, the default value is 'subtree'. If the 'xpath' feature is supported, then the following unqualified XML attribute is also supported: select: optional attribute containing a string representing an XPath expression. The 'type' attribute must be equal to 'xpath' if this attribute is present."; } // NETCONF capabilities defined as features feature writable-running { description "NETCONF :writable-running capability; If the server advertises the :writable-running capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.2"; } feature candidate { description "NETCONF :candidate capability; If the server advertises the :candidate capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.3"; } feature confirmed-commit { if-feature candidate; description "NETCONF :confirmed-commit:1.1 capability; If the server advertises the :confirmed-commit:1.1 capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.4"; } feature rollback-on-error { description "NETCONF :rollback-on-error capability; If the server advertises the :rollback-on-error capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.5"; } feature validate { description "NETCONF :validate:1.1 capability; If the server advertises the :validate:1.1 capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.6"; } feature startup { description "NETCONF :startup capability; If the server advertises the :startup capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.7"; } feature url { description "NETCONF :url capability; If the server advertises the :url capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.8"; } feature xpath { description "NETCONF :xpath capability; If the server advertises the :xpath capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.9"; } container server-hello { description "Generic Server Hello Message Parameters."; uses nct:NcCapabilities; leaf session-id { type nc:session-id-type; config false; } ncx:hidden; ncx:abstract; } container client-hello { description "Generic Client Hello Message Parameters."; uses nct:NcCapabilities; ncx:hidden; ncx:abstract; } // NETCONF Operations PDU Data Types grouping ErrorInfoContent { description "NETCONF standard 'error-info' Element Content;"; leaf-list bad-attribute { description "Name of the missing or invalid XSD attribute. Used with missing-attribute, bad-attribute, and unknown-attribute error-tag values."; type string; // xs:QName config false; } leaf-list bad-element { description "Name of the element that contains (or should contain) an invalid XSD attribute when used with missing-attribute, bad-attribute, unknown-attribute, error-tag values. Name of an invalid or missing element when used with then missing-element, bad-element, unknown-element, and unknown-namespace error-tag values."; type string; // xs:QName config false; } leaf-list ok-element { description "Identifies an element in the data model for which the requested operation has been completed for that node and all its child nodes. This element can appear zero or more times in the 'error-info' container. Used with the partial-operation error-tag value."; type string; // xs:QName config false; } leaf-list err-element { description "Identifies an element in the data model for which the requested operation has failed for that node and all its child nodes. This element can appear zero or more times in the 'error-info' container. Used with the partial-operation error-tag value."; type string; // xs:QName config false; } leaf-list noop-element { description "Identifies an element in the data model for which the requested operation was not attempted for that node and all its child nodes. This element can appear zero or more times in the container. Used with the partial-operation error-tag value."; type string; // xs:QName config false; } leaf session-id { description "Session ID of session holding the requested lock, or zero to indicate a non-NETCONF entity holds the lock."; type session-id-or-zero-type; config false; } } // NETCONF Simple Types typedef session-id-type { type uint32 { range "1..max"; } description "NETCONF Session Id"; } typedef session-id-or-zero-type { type uint32; description "NETCONF Session Id or Zero to indicate none"; } typedef error-tag-type { type enumeration { enum in-use { description "The request requires a resource that already is in use."; } enum invalid-value { description "The request specifies an unacceptable value for one or more parameters."; } enum too-big { description "The request or response (that would be generated) is too large for the implementation to handle."; } enum missing-attribute { description "An expected attribute is missing."; } enum bad-attribute { description "An attribute value is not correct; e.g., wrong type, out of range, pattern mismatch."; } enum unknown-attribute { description "An unexpected attribute is present."; } enum missing-element { description "An expected element is missing."; } enum bad-element { description "An element value is not correct; e.g., wrong type, out of range, pattern mismatch."; } enum unknown-element { description "An unexpected element is present."; } enum unknown-namespace { description "An unexpected namespace is present."; } enum access-denied { description "Access to the requested protocol operation or data model is denied because authorization failed."; } enum lock-denied { description "Access to the requested lock is denied because the lock is currently held by another entity."; } enum resource-denied { description "Request could not be completed because of insufficient resources."; } enum rollback-failed { description "Request to roll back some configuration change (via rollback-on-error or operations) was not completed for some reason."; } enum data-exists { description "Request could not be completed because the relevant data model content already exists. For example, a 'create' operation was attempted on data that already exists."; } enum data-missing { description "Request could not be completed because the relevant data model content does not exist. For example, a 'delete' operation was attempted on data that does not exist."; } enum operation-not-supported { description "Request could not be completed because the requested operation is not supported by this implementation."; } enum operation-failed { description "Request could not be completed because the requested operation failed for some reason not covered by any other error condition."; } enum partial-operation { description "This error-tag is obsolete, and SHOULD NOT be sent by servers conforming to this document."; } enum malformed-message { description "A message could not be handled because it failed to be parsed correctly. For example, the message is not well-formed XML or it uses an invalid character set."; } } description "NETCONF Error Tag"; reference "RFC 6241, Appendix A"; } typedef error-severity-type { type enumeration { enum error { description "Error severity"; } enum warning { description "Warning severity"; } } description "NETCONF Error Severity"; reference "RFC 6241, Section 4.3"; } typedef edit-operation-type { type enumeration { enum merge { description "The configuration data identified by the element containing this attribute is merged with the configuration at the corresponding level in the configuration datastore identified by the target parameter."; } enum replace { description "The configuration data identified by the element containing this attribute replaces any related configuration in the configuration datastore identified by the target parameter. If no such configuration data exists in the configuration datastore, it is created. Unlike a operation, which replaces the entire target configuration, only the configuration actually present in the config parameter is affected."; } enum create { description "The configuration data identified by the element containing this attribute is added to the configuration if and only if the configuration data does not already exist in the configuration datastore. If the configuration data exists, an element is returned with an value of 'data-exists'."; } enum delete { description "The configuration data identified by the element containing this attribute is deleted from the configuration if and only if the configuration data currently exists in the configuration datastore. If the configuration data does not exist, an element is returned with an value of 'data-missing'."; } enum remove { description "The configuration data identified by the element containing this attribute is deleted from the configuration if the configuration data currently exists in the configuration datastore. If the configuration data does not exist, the 'remove' operation is silently ignored by the server."; } } default "merge"; description "NETCONF 'operation' attribute values"; reference "RFC 6241, Section 7.2"; } grouping RpcErrorType { description "NETCONF 'rpc-error' Element Content"; leaf error-type { description "Defines the conceptual layer that the error occurred."; type nct:ErrorType; mandatory true; } leaf error-tag { description "Contains a string identifying the error condition."; type nct:error-tag-type; mandatory true; } leaf error-severity { description "Contains a string identifying the error severity, as determined by the device."; type nct:error-severity-type; mandatory true; } leaf error-app-tag { description "Contains a string identifying the data-model-specific or implementation-specific error condition, if one exists. This element will not be present if no appropriate application error tag can be associated with a particular error condition."; type string; } leaf error-path { description "Contains the absolute XPath [2] expression identifying the element path to the node that is associated with the error being reported in a particular rpc-error element. This element will not be present if no appropriate payload element can be associated with a particular error condition, or if the 'bad-element' QString returned in the 'error-info' container is sufficient to identify the node associated with the error. When the XPath expression is interpreted, the set of namespace declarations are those in scope on the rpc-error element, including the default namespace."; type yang:xpath1.0; } leaf error-message { description "Contains a string suitable for human display that describes the error condition. This element will not be present if no appropriate message is provided for a particular error condition. This element SHOULD include an xml:lang attribute."; type nct:LangString; } anyxml error-info { description "Contains protocol- or data-model-specific error content. This element will not be present if no such error content is provided for a particular error condition. The list in RFC 4741, Appendix A, defines any mandatory error-info content for each error. After any protocol-mandated content, a data model definition may mandate that certain application-layer error information be included in the error-info container. An implementation may include additional elements to provide extended and/or implementation-specific debugging information."; } } grouping RpcDataReplyType { description "NETCONF 'rpc-reply' Error and/or Data Content"; list rpc-error { config false; uses RpcErrorType; } // anyxml data { // config false; // } // any XML is allowed here, not just 1 el. named data // The contents of the 'rpc output' section appears here } grouping RpcOkReplyType { description "NETCONF 'rpc-reply' OK Content."; choice ok-or-error { leaf ok { description "Sent in 'rpc-reply' messages if no errors or warnings occurred during the processing of an 'rpc' request."; type empty; config false; } list rpc-error { config false; uses RpcErrorType; } } } grouping RpcReplyType { description "Generic NETCONF 'rpc-reply' content. "; choice ok-or-data-error { mandatory true; config false; leaf ok { description "Sent in 'rpc-reply' messages if no errors or warnings occurred during the processing of an 'rpc' request."; type empty; } case data-error { uses RpcDataReplyType; } } } // NETCONF operation attribute leaf operation { description "Internal object for the nc:operation attribute."; type nc:edit-operation-type; ncx:abstract; ncx:hidden; } // NETCONF Generic RPC Messages container rpc { description "Remote Procedure Call request message"; reference "RFC 4741, section 4.1"; // do not treat missing message-id as an error ncx:metadata "nct:MessageId message-id"; ncx:abstract; } container rpc-reply { description "Remote Procedure Call response message"; reference "RFC 4741, section 4.2"; uses RpcReplyType; // do not treat missing message-id as an error ncx:metadata "nct:MessageId message-id"; ncx:abstract; } // NETCONF Standard Protocol Operations rpc get-config { description "Retrieve all or part of a specified configuration."; reference "RFC 6241, Section 7.1"; input { container source { description "Particular configuration to retrieve."; choice config-source { mandatory true; description "The configuration to retrieve."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config source."; } leaf running { type empty; description "The running configuration is the config source."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config source. This is optional-to-implement on the server because not all servers will support filtering for this datastore."; } } } anyxml filter { description "Subtree or XPath filter to use."; nc:get-filter-element-attributes; ncx:metadata "nct:FilterType type"; ncx:metadata "nct:SelectString select"; } } output { container data { presence "An empty data container indicates that the request did not produce any results."; description "Copy of the source datastore subset that matched the filter criteria (if any). An empty data container indicates that the request did not produce any results."; ncx:root; } } } rpc edit-config { description "The operation loads all or part of a specified configuration to the specified target configuration."; reference "RFC 6241, Section 7.2"; input { container target { description "Particular configuration to edit."; choice config-target { mandatory true; description "The configuration target."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config target."; } leaf running { if-feature writable-running; type empty; description "The running configuration is the config source."; } } } leaf default-operation { type enumeration { enum merge { description "The default operation is merge."; } enum replace { description "The default operation is replace."; } enum none { description "There is no default operation."; } } default "merge"; description "The default operation to use."; } leaf test-option { if-feature validate; type enumeration { enum test-then-set { description "The server will test and then set if no errors."; } enum set { description "The server will set without a test first."; } enum test-only { description "The server will only test and not set, even if there are no errors."; } } default "test-then-set"; description "The test option to use."; } leaf error-option { type enumeration { enum stop-on-error { description "The server will stop on errors."; } enum continue-on-error { description "The server may continue on errors."; } enum rollback-on-error { description "The server will roll back on errors. This value can only be used if the 'rollback-on-error' feature is supported."; } } default "stop-on-error"; description "The error option to use."; } choice edit-content { mandatory true; description "The content for the edit operation."; container config { description "Inline Config content."; ncx:root; } leaf url { if-feature url; type inet:uri; description "URL-based config content."; } } } } rpc copy-config { description "Create or replace an entire configuration datastore with the contents of another complete configuration datastore."; reference "RFC 6241, Section 7.3"; input { container target { description "Particular configuration to copy to."; choice config-target { mandatory true; description "The configuration target of the copy operation."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config target."; } leaf running { if-feature writable-running; type empty; description "The running configuration is the config target. This is optional-to-implement on the server."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config target."; } leaf url { if-feature url; type inet:uri; description "The URL-based configuration is the config target."; } } } container source { description "Particular configuration to copy from."; choice config-source { mandatory true; description "The configuration source for the copy operation."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config source."; } leaf running { type empty; description "The running configuration is the config source."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config source."; } leaf url { if-feature url; type inet:uri; description "The URL-based configuration is the config source."; } container config { description "Inline Config content: element. Represents an entire configuration datastore, not a subset of the running datastore."; ncx:root; } } } } } rpc delete-config { nacm:default-deny-all; description "Delete a configuration datastore."; reference "RFC 6241, Section 7.4"; input { container target { description "Particular configuration to delete."; choice config-target { mandatory true; description "The configuration target to delete."; leaf startup { if-feature startup; type empty; description "The startup configuration is the config target."; } leaf url { if-feature url; type inet:uri; description "The URL-based configuration is the config target."; } } } } } rpc lock { description "The lock operation allows the client to lock the configuration system of a device."; reference "RFC 6241, Section 7.5"; input { container target { description "Particular configuration to lock."; choice config-target { mandatory true; description "The configuration target to lock."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config target."; } leaf running { type empty; description "The running configuration is the config target."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config target."; } } } } } rpc unlock { description "The unlock operation is used to release a configuration lock, previously obtained with the 'lock' operation."; reference "RFC 6241, Section 7.6"; input { container target { description "Particular configuration to unlock."; choice config-target { mandatory true; description "The configuration target to unlock."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config target."; } leaf running { type empty; description "The running configuration is the config target."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config target."; } } } } } rpc get { description "Retrieve running configuration and device state information."; reference "RFC 6241, Section 7.7"; input { anyxml filter { description "This parameter specifies the portion of the system configuration and state data to retrieve."; nc:get-filter-element-attributes; ncx:metadata "nct:FilterType type"; ncx:metadata "string select"; } } output { container data { description "Copy of the running datastore subset and/or state data that matched the filter criteria (if any). An empty data container indicates that the request did not produce any results."; ncx:root; } } } rpc close-session { description "Request graceful termination of a NETCONF session."; reference "RFC 6241, Section 7.8"; } rpc kill-session { nacm:default-deny-all; description "Force the termination of a NETCONF session."; reference "RFC 6241, Section 7.9"; input { leaf session-id { type session-id-type; mandatory true; description "Particular session to kill."; } } } rpc commit { if-feature candidate; description "Commit the candidate configuration as the device's new current configuration."; reference "RFC 6241, Section 8.3.4.1"; input { leaf confirmed { if-feature confirmed-commit; type empty; description "Requests a confirmed commit."; reference "RFC 6241, Section 8.3.4.1"; } leaf confirm-timeout { if-feature confirmed-commit; type uint32 { range "1..max"; } units "seconds"; default "600"; // 10 minutes description "The timeout interval for a confirmed commit."; reference "RFC 6241, Section 8.3.4.1"; } leaf persist { if-feature confirmed-commit; type string; description "This parameter is used to make a confirmed commit persistent. A persistent confirmed commit is not aborted if the NETCONF session terminates. The only way to abort a persistent confirmed commit is to let the timer expire, or to use the operation. The value of this parameter is a token that must be given in the 'persist-id' parameter of or operations in order to confirm or cancel the persistent confirmed commit. The token should be a random string."; reference "RFC 6241, Section 8.3.4.1"; } leaf persist-id { if-feature confirmed-commit; type string; description "This parameter is given in order to commit a persistent confirmed commit. The value must be equal to the value given in the 'persist' parameter to the operation. If it does not match, the operation fails with an 'invalid-value' error."; reference "RFC 6241, Section 8.3.4.1"; } } } rpc discard-changes { if-feature candidate; description "Revert the candidate configuration to the current running configuration."; reference "RFC 6241, Section 8.3.4.2"; } rpc cancel-commit { if-feature confirmed-commit; description "This operation is used to cancel an ongoing confirmed commit. If the confirmed commit is persistent, the parameter 'persist-id' must be given, and it must match the value of the 'persist' parameter."; reference "RFC 6241, Section 8.4.4.1"; input { leaf persist-id { type string; description "This parameter is given in order to cancel a persistent confirmed commit. The value must be equal to the value given in the 'persist' parameter to the operation. If it does not match, the operation fails with an 'invalid-value' error."; } } } rpc validate { if-feature validate; description "Validates the contents of the specified configuration."; reference "RFC 6241, Section 8.6.4.1"; input { container source { description "Particular configuration to validate."; choice config-source { mandatory true; description "The configuration source to validate."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config source."; } leaf running { type empty; description "The running configuration is the config source."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config source."; } leaf url { if-feature url; type inet:uri; description "The URL-based configuration is the config source."; } container config { description "Inline Config content: element. Represents an entire configuration datastore, not a subset of the running datastore."; ncx:root; } } } } } rpc load-config { ncx:hidden; ncx:abstract; description "NCX internal load-config operation. The config database can be loaded dynamically only if the startup config phase was skipped via the 'no-startup' option. An operation-failed error will be returned and any startup config file was loaded (even partially) successfully."; input { container config { ncx:root; description "XML Config contents."; } } } container config { description "Used as the container for NETCONF object definitions."; ncx:root; ncx:abstract; } } yuma123_2.14/netconf/modules/yuma123/yangdump.yang0000664000175000017500000005435014770023131022143 0ustar vladimirvladimirmodule yangdump { yang-version 1; namespace "http://yuma123.org/ns/yangdump"; prefix "yd"; import yuma-ncx { prefix ncx; } import yuma-app-common { prefix ncxapp; } organization "Yuma123"; contact "Vladimir Vassilev "; description "yangdump provides validation and translation of YANG data models. Information about a module or submodule can be generated as well. INPUT FILES Operations can be performed on one or more files with the 'module' parameter, or an entire directory tree with the 'subtree' parameter. Unless the 'help' or 'version' parameters is entered, one of these input parameters must be present. SEARCH PATH When a module name is entered as input, or when a module or submodule name is specified in an import or include statement within the file, the following search algorithm is used to find the file: 1) file is in the current directory 2) YUMA_MODPATH environment var (or set by modpath parameter) 3) $HOME/modules directory 4) $YUMA_HOME/modules directory 5) $YUMA_INSTALL/modules directory OR default install module location, '/usr/share/yuma/modules' By default, the entire directory tree for all locations (except step 1) will be searched, not just the specified directory. The 'subdirs' parameter can be used to prevent sub-directories from being searched. Any directory name beginning with a dot character '.' will be skipped. Also, any directory named 'CVS' will be skipped in directory searches. TRANSLATION MODES The 'format' parameter is used to select a translation output mode. If it is missing, then no translation will be done. This parameter can be used with the module reports parameters, but the translation output should be directed to a file instead of STDOUT to keep them separated. For XSD 1.0 translation, use the 'format=xsd' parameter. For XHTML 1.0 translation, use the 'format=html' parameter. For YIN translation, use the 'format=yin' parameter. MODULE REPORTS For a 1 line output of the module name and version, use the 'modversion' parameter. For a listing of all the symbols that the file exports to other files, use the 'exports' parameter. For a listing of all the files that the file depends on, to compile, use the 'dependencies' parameter. For a listing of all the accessible object identifiers that the file contains, use the 'identifiers' parameter. For a tree listing of all the accessible object identifiers that the file contains, use the 'tree-identifiers' parameter. OUTPUT MODES By default, any translation output will be sent to STDOUT. The 'output' parameter can be used to specify the full filespec of the output file, or a partial directory specification to be combined with a default filename. The 'defnames' parameter can be used to generate a default filename in the current directory for the output, or in the 'output' directory, if one is specified. By default, an output filename will have the form: .. If the 'versionnames=false' parameter is used, then the default filename will have the form: . This parameter will also affect URL generation during HTML translation. When the 'subtree' input parameter is used for XSD or HTML translation, the 'defnames' parameter will be automatically set to 'true', to maintain well-formed XML documents when multiple translations are possible. If the 'unified' parameter is set to 'true', then all submodules will be processed when the input is a main module that includes any submodules. For XSD and HTML translation, the submodule content will be generated instead of an 'include' statement. Submodule files will be skipped in 'subtree' mode. ERROR LOGGING By default, warnings and errors are sent to STDOUT. A log file can be specified instead with the 'log' parameter. Existing log files can be reused with the 'logappend' parameter, otherwise log files are overwritten. The logging level can be controlled with the 'log-level' parameter. The default log level is 'info'. The log-levels are additive: off: suppress all errors (not recommended!) A program return code of '1' indicates some error. error: print errors warn: print warnings info: print generally interesting trace info debug: print general debugging trace info debug2: print verbose debugging trace info "; revision 2018-01-12 { description "Added 'tree' to FormatType enumeration. Changed namespace from Netconf central to Yuma123 and updated contact details."; } revision 2012-10-05 { description "Add uses for YumaHomeParm"; } revision 2011-10-08 { description "Add --home parameter."; } revision 2011-09-12 { description "Add --format=uc and --format=uh to support generation of separate SIL user functions"; } revision 2011-01-28 { description "Change output leaf to ncxapp:OutputParm grouping"; } revision 2010-05-31 { description "Added --stats and --totals parameters for YANG statistics reporting."; } revision 2010-03-11 { description "Added new format enum for TG2 code generation."; } revision 2010-01-30 { description "Initial version for 0.10 release."; } typedef FormatType { description "Conversion Output Formats."; type enumeration { enum xsd { description "Convert YANG to XSD"; } enum sql { description "Convert YANG to SQL history collection (TBD)"; } enum sqldb { description "Convert YANG to SQL input for netconfcentral.org database."; } enum html { description "Convert YANG to HTML documentation format."; } enum yang { description "Convert YANG to canonical YANG format."; } enum copy { description "Copy and rename the YANG file to canonical name format."; } enum h { description "Generate combined server instrumentation library H file. Compatible with version 1 'h' format"; } enum c { description "Generate combined server instrumentation library C file. Compatible with version 1 'c' format"; } enum cpp_test { description "Generate combined server instrumentation library CPP file for testing purposes."; } enum uh { description "Generate server instrumentation library user callback H file."; } enum uc { description "Generate server instrumentation library user callback C file."; } enum yh { description "Generate split server instrumentation library user callback H file."; } enum yc { description "Generate server instrumentation library user callback H file."; } enum yin { description "Convert YANG to YIN format."; } enum tg2 { description "Convert YANG to Turbogears 2 Source code files."; } enum tree { description "Convert YANG to YANG tree format."; } } } typedef TocType { description "Requested table of contents type."; type enumeration { enum none; enum plain; enum menu; } default "menu"; } typedef ObjViewType { description "Requested view format for objects."; type enumeration { enum raw { description "output includes augment and uses clauses, not the expanded results of those clauses."; } enum cooked { description "output does not include augment or uses clauses, just the objects generated from those clauses (if any)."; } } default "raw"; } container yangdump { ncx:cli; ncx:default-parm module; description "CLI Parameter Set for the YANG Converter Application."; uses ncxapp:NcxAppCommon; uses ncxapp:ConfigParm; uses ncxapp:YumaHomeParm; uses ncxapp:CommonFeatureParms; uses ncxapp:FeatureCodeParms; uses ncxapp:HomeParm; uses ncxapp:ModuleParm; uses ncxapp:SubtreeParm; uses ncxapp:DeviationParm; uses ncxapp:OutputParm; leaf defnames { description "If 'true', then output to a file with the default name for the format, usually to the current directory. Not used if the format parameter is missing. If the 'output' parameter is present and represents an existing directory, then the default filename will be created in that directory, instead of the current directory. If 'false', then default naming will not be used. Output will either be to the current directory or to STDOUT."; type boolean; default false; } leaf format { description "Type of conversion desired, if any. If this parameter is missing, then no translation will be done, but the module will be validated, and any requested reports will be generated. The following values are supported: xsd == XSD 1.0 translation sql == SQL schema (TBD) sqldb == netconfcentral.org database info html == XHTML 1.0 translation yang == Canonical YANG translation copy == Validate and copy with a new name. h == netconfd instrumentation H file c == netconfd instrumentation C file."; type FormatType; } leaf modversion { description "Validate the file, write the [sub]module name, version and source filespec, then exit."; type empty; } leaf exports { description "Validate the file, write information for the symbols that this [sub]module exports, then exit. Report includes the following info for the specific file, not the entire module, if submodules are used: - [sub]module name - version - source filespec - namespace (module only) - prefix (module only) - belongs-to (submodule only) - typedefs - groupings - objects, rpcs, notifications - extensions."; type empty; } leaf dependencies { description "Validate the file, write the module name, version and module source for each file that this [sub]module imports and includes, then exit. Each dependency type, name, version, and source is listed once. If the dependency version and source are missing, then that import or include file was not found."; type empty; } leaf identifiers { description "Validate the file, write the list of object identifiers, that this [sub]module contains, then exit. Each accessible object node is listed once, including all child nodes. Notifications and RPC methods are considered top-level objects, and have object identifiers as well as configuration and state data.."; type empty; } leaf html-div { description "If 'true', and HTML translation is requested, then this parameter will cause the output to be a single
element, instead of an entire HTML file. This allows the HTML translation to be easily integrated within more complex WEB pages, but the proper CSS definitions need to be present for the HTML to render properly. The default filename extension will be '.div' instead of '.html' if this parameter is present. The contents will be well-formed XHTML 1.0, but without any namespace declarations. If 'false', then a complete element will be generated instead."; type boolean; default false; } leaf html-toc { description "The HTML Table of Contents output mode. Ignored unless the 'format' parameter is set to 'html'. Default is 'menu'. Values: - none: no ToC generated - plain: plain list ToC generated - menu: drop-down menu ToC generated."; type TocType; } leaf objview { description "Determines how objects are generated in HTML and YANG outputs. The default mode is the 'raw' view. XSD output is always 'cooked', since refined groupings and locally-scoped definitions are not supported in XSD. raw -- output includes augment and uses clauses, not the expanded results of those clauses. cooked -- output does not include augment or uses clauses, just the objects generated from those clauses."; type ObjViewType; } leaf show-errors { description "If present, list each error or warning number and its default message string. The program will exit after this is done."; type empty; } leaf simurls { description "If 'true', and HTML translation is requested, then this parameter will cause the format of URLs within links to be generated in simplified form, for WEB development engines such as CherryPy, which support this format. Normal URL format (false): example.html?parm1=foo&parm2=bar#frag Simplified URL format (true): example/foo/bar#frag "; type boolean; default false; } uses ncxapp:SubdirsParm; choice stats-report { default statistics; case statistics { leaf stats { description "Generate a statistics report for each input module. The following metrics are reported: ... Developers: see ydump/yangstats.h"; type enumeration { enum none { description "No statistics reporting will be done."; } enum brief { description "Brief statistics reporting will be done: - Complexity score - Total nodes "; } enum basic { description "Basic statistics reporting will be done."; } enum advanced { description "Advanced statistics reporting will be done."; } enum all { description "All possible statistics reporting will be done."; } } default "none"; } leaf totals { description "Controls how stats totals are displayed."; type enumeration { enum none { description "No statistics totals will be reported."; } enum summary { description "Summary statistics totals will be reported, based on the stats mode that is requested."; } enum summary-only { description "Only the summary statistics totals will be reported, based on the stats mode that is requested. This mode will cause all individual module statistics reports to be generated, and a summary for all input modules will be generated instead."; } } default "none"; } } } leaf tree-identifiers { description "Validate the file, write the list of object identifiers, in tree format, that this [sub]module contains, then exit. Each accessible object node is listed once, including all child nodes. Notifications and RPC methods are considered top-level objects, and have object identifiers as well as configuration and state data.."; type empty; } leaf unified { description "If set to 'true', then submodules will be processed within the main module, in a unified report, instead of separately, one report for each file. For translation purposes, this parameter will cause any sub-modules to be treated as if they were defined in the main module. Actual definitions will be generated instead of an 'include' directive, for each submodule. If this mode is selected, then submodules entered with the 'module' parameter will be ignored. If 'false', a separate output file is generated for each input file, so that XSD output and other reports for a main module will not include information for submodules."; type boolean; default false; } leaf urlstart { description "If present, then this string will be used to prepend to HREF links and URLs generated for SQL and HTML translation. It is expected to be a URL ending with a directory path. The trailing separator '/' will be added if it is missing. If not present (the default), then relative URLs, starting with the file name will be generated instead. For example, if this parameter is set to 'http://example.com/public' then the URL generated for the 'bar' type on line 53, in the module FOO (version 2008-01-01) would be: if versionnames=false: 'http://example.com/public/FOO.html#bar.53' OR if versionnames=true (default): 'http://example.com/public/FOO_2008-01-01.html#bar.53' "; type string; } leaf versionnames { description "If false, the default filenames will not contain the module version string. If true, the [sub]module name and version string are both used to generate a default file name, when the 'defnames' parameter is set to 'true'."; type boolean; default true; } leaf xsd-schemaloc { description "If present, then the schemaLocation attribute will be generated during XSD translation. This will be done for the module being processed, and any modules that are imported into that module. If not present (the default), then the schemaLocation attribute is not generated during XSD translation. Relative URLs for include and import directives will be generated, starting with the file name. For example, if this parameter is set to 'http://example.com/public' then the schemaLocation XSD for the module test3 (version 10-19-2008) would be: if versionnames=false: xsi:schemaLocation='http://netconfcentral.com/ns/test3 http://example.com/public/test3.xsd' OR if versionnames=true (default): xsi:schemaLocation='http://netconfcentral.com/ns/test3 http://example.com/public/test3_2008-10-19.xsd' "; type string; } } } yuma123_2.14/netconf/modules/yuma123/yangcli-ex.yang0000664000175000017500000000550114770023131022351 0ustar vladimirvladimirmodule yangcli-ex { namespace "http://yuma123.org/ns/yangcli-ex"; prefix "yangcli-ex"; import yangcli { prefix yc; } import yuma-ncx { prefix ncx; } organization "Yuma123"; contact "Vladimir Vassilev "; description "This module contains extra parameters for yangcli"; revision 2018-08-08 { description "Reduced descripiton statement line length."; } grouping ConnectParmsEx { description "Common extended parms for connecting to a NETCONF server. Used by the connect operation and if present at the command line invocation, then the connect operation will be invoked automatically."; leaf dump-session { description "Filespec base for dumping the raw netconf traffic data and timestamp information. Example: --dump-session= The following files are created: * out - session data sent to server * in - session data received from server * out.ts - timestamp-tee formated output transactions report * in.ts - timestamp-tee formated input transactions report "; type string; } } augment "/yc:yangcli" { description "Adds extra parameters to yangcli."; leaf keep-session-model-copies-after-compilation { description "Controls if temporary session model copies are kept after compilation in ~/.yuma/tmp/<>/ until yangcli is terminated."; type boolean; default false; } uses ConnectParmsEx; } augment "/yc:connect/yc:input" { description "Adds extra parameters to connect."; uses ConnectParmsEx; } rpc clear-counters { description "Convenience function that allows the user to 'clear' all counters under the specified target path. The function performs with the specified target and updates a copy of all the yang:counter32 and yang:counter64 values in a tree that is later used to subtract the stored values from the data returned with xget. * Example: yangcli> clear-counters /interfaces-state/interface/statistics yangcli> xget /interfaces-state/interface/statistics "; input { ncx:default-parm select; leaf select { description "The XPath expression to use in the retrieval operation. The string may only contain single quotes, which are required for string literals. The entire string will be inserted into a double-quoted string, within the get-config PDU. Character entities will be inserted as needed to maintain well-formed XML."; type string { length "1..max"; } ncx:xpath; } } } } yuma123_2.14/netconf/modules/yuma123/yuma123-system.yang0000664000175000017500000001657514770023131023051 0ustar vladimirvladimirmodule yuma123-system { namespace "http://yuma123.org/ns/yuma123-system"; prefix "yuma123-sys"; import ietf-system { prefix sys; } import ietf-inet-types { prefix inet; } import ietf-yang-types { prefix yang; } import ietf-netconf { prefix nc; } import notifications { prefix notif; } import yuma-app-common { prefix yumaapp; } import yuma-types { prefix nt; } import yuma123-netconf-types { prefix nct; } import yuma-ncx { prefix ncx; } import ietf-netconf-acm { prefix nacm; } organization "yuma123"; contact "Vladimir Vassilev ."; description "NETCONF Basic System Group."; revision 2017-03-26 { description "Original netconfcentral yuma-system top level /system is moved. Now augment container /ietf-system:system-state/yuma-system:yuma. Notifications replaced with corresponding ietf-netconf-notifications removed."; } revision 2012-10-05 { description "Updated EditOperationType to edit-operation-type. Converged nacm extension usage."; } revision 2012-01-15 { description "Add sysNetconfServerCLI monitoring data."; } revision 2010-05-24 { description "Change SessionId to standard session-id-type."; } revision 2009-12-27 { description "Initial version."; } grouping SysCommonSessionParms { leaf userName { description "Name of the user for the session."; type string; } leaf sessionId { description "Identifier of the session."; type nc:session-id-type; mandatory true; } leaf remoteHost { description "Address of the remote host for the session."; type inet:ip-address; } } augment "/sys:system-state" { container yuma { description "Basic objects for NETCONF system status."; config false; leaf sysName { description "The system name."; type string; reference "RFC 3418, sysName object"; } leaf sysCurrentDateTime { description "The current system date and time."; type yang:date-and-time; } leaf sysBootDateTime { description "The system date and time when the system last restarted."; type yang:date-and-time; } leaf sysLogLevel { description "The current system logging verbosity level."; type nt:NcDebugType; } leaf sysNetconfServerId { description "The name and version ID for the NETCONF server running on this system."; type string; } anyxml sysNetconfServerCLI { nacm:default-deny-all; description "The boot-time CLI and configuration parameters that were used by the server running on this system. The format is the 'netconfd' container, defined in netconfd.yang. By default, only client-set parameters are shown. Use with-defaults=report-all to see all CLI parameter values in effect."; } container uname { description "Contains the broken out fields from the output of the 'uname' command on this machine."; leaf sysname { description "The name of the operating system in use."; type string; } leaf release { description "The current release level of the operating system in use."; type string; } leaf version { description "The current version level of the operating system in use."; type string; } leaf machine { description "A description of the hardware in use."; type string; } leaf nodename { description "The host name of this system, as reported by the uname command."; type string; } } } } rpc set-log-level { nacm:default-deny-all; description "Sets the server log verbosity level"; input { ncx:default-parm log-level; leaf log-level { description "The desired verbosity level for system logging messages generated by the server. The current value can be obtained by retrieving the /system-state/yuma-system/sysLogLevel object."; type nt:NcDebugType; mandatory true; } } } notification sysStartup { description "Generated when the system restarts. Used for logging purposes, since no sessions are actually active when the system restarts."; leaf startupSource { description "The filespec used to load the running configuration. This leaf will only be present if there was a startup configuration file used."; type string; } list bootError { description "There will be one entry for each encountered during the load config operation. The fields are used directly. There is no particular order, so no key is defined."; uses nct:RpcErrorType; } } augment /notif:notification { leaf sequence-id { description "Global auto-increment sequence ID added to each notification element sent by netconfd"; type uint32; } } rpc load { nacm:default-deny-all; description "Load a module into the server, if it is not already loaded. Returns the module revision date (or today's date if none), of the module that was loaded, or an error if not found or the module found had errors and was not loaded successfully. If the module is already loaded, then the revision date will simply be returned."; input { ncx:default-parm module; leaf module { description "Name of the module to load."; mandatory true; type nt:NcxName; } leaf revision { description "Module revision to load."; type nt:Date; } uses yumaapp:DeviationParm; } output { leaf mod-revision { description "Revision date of the module in use by the server. Will only be present if the module has a version"; type nt:Date; } } } rpc restart { description "Restart the server. Does not reload the software image.."; nacm:default-deny-all; } rpc shutdown { description "Shutdown the server."; nacm:default-deny-all; } rpc no-op { description "Just returns 'ok'. Used for debugging or relative performance measurements."; } } yuma123_2.14/netconf/modules/yuma123/yangcli.yang0000664000175000017500000016445314770023131021753 0ustar vladimirvladimirmodule yangcli { namespace "http://yuma123.org/ns/yangcli"; prefix yc; import ietf-yang-types { prefix yang; } import yuma-ncx { prefix ncx; } import yuma-app-common { prefix ncxapp; } import yuma-types { prefix nt; } import ietf-netconf { prefix nc; } import yuma123-netconf-types { prefix nct; } organization "yuma123"; contact "Vladimir Vassilev ."; description " Command Line Interface for the NETCONF protocol: Client side Usage: yangcli [parameters] yangcli --help yangcli --version Normal Mode: An interactive CLI shell with command line history. Autostart-mode: If the 'server' parameter is present, then yangcli will attempt to connect to that server upon startup. If the 'user' and 'password' parameters are also present, then the user will not be prompted before the connection is attempted. This parameter will be processed first, if script-mode or batch-mode is used. Script-mode: If the 'run-script' or 'run-command' parameter is present, then the specified script or command will be run automatically upon startup. Batch-mode: If the 'batch-mode' parameter is present, then either the 'run-script' or 'run-command' parameter will be invoked, if present, and then the program will exit. Any interactive input breaks in the script or command will be skipped. "; revision 2018-10-23 { description "Added echo-requests param to complement echo-replies."; } revision 2018-08-08 { description "Replaced double with single quotes for description stmt with fwd slash."; } revision 2018-04-21 { description "Added use-agent leaf to ssh configuration."; } revision 2017-03-26 { description "Changed namespace in order to edit the original netconfcentral model. Splitted yuma-netconf into yuma123-netconf and yuma123-netconf-types"; } typedef LogCount { description "Number of log entries. -1 means all entries"; type int32 { range "-1 | 1 .. max"; } } typedef LogIndex { description "Index into a log buffer."; type uint32; } typedef TimerId { description "Identifier for a local timer to use with the start-timer and stop-timer commands."; type uint8 { range "0 .. 15"; } } typedef NameMatchMode { description "Defines the search mode that should be used when resolving YANG node names in leafs and leaf-lists using the UrlPath data type."; type enumeration { enum exact { description "The name must exactly match the node name for all characters in both name strings."; } enum exact-nocase { description "The name must match the node name for all characters in both name strings. Strings are not case-sensitive."; } enum one { description "The name must exactly match the first N characters of just one node name, which must be the only partial name match found."; } enum one-nocase { description "The name must exactly match the first N characters of just one node name, which must be the only partial name match found. Strings are not case-sensitive."; } enum first { description "The name must exactly match the first N characters of any node name. The first one found will be used."; } enum first-nocase { description "The name must exactly match the first N characters of any node name. The first one found will be used. Strings are not case-sensitive."; } } } typedef AltNameMode { description "Defines the alternate name search mode that should be used when resolving YANG node names in leafs or leaflists using the UrlPath data type. If 'true' then nodes with an 'alt-name' defined will be considered a match if the YANG name or the alternative name matches the search string. If 'false' then only the YANG node name will be used in node name searches."; type boolean; } typedef UrlPath { description "Special URL encoded path expression. Normal Encoding Rules: - Normal content is encoded as an absolute path. - Keys are encoded as a path step within the URL, instead of a predicate expression like XPath. - The first character must be a forward slash '/'. - Each identifier or key encoded in the URL string is separated by a single forward slash '/' character. - Escaped character sequences are allowed, such as '%20' for the space ' ' character. - If any descendant nodes of a list are included, then all key leafs for that list must be encoded in the URL (or escaped with the dash '-' character to skip that key). - Only key leafs can be encoded within the URL string, similar to a YANG instance-identifier or schema-instance-identifier string. Other leafs are not allowed. Example URL and XPath strings: XPath: /foo/bar[id='fred'][id2='barney]/baz UrlPath: /foo/bar/fred/barney/baz Example showing the 'id2' key leaf escaped: XPath: /foo/bar[id='fred']/baz UrlPath: /foo/bar/fred/-/baz Special Encoding Rules Since these escaped characters are usually decoded by the time an HTTP gateway program will get them, the forward slash '/' character needs to be treated differently. To use this character within key leaf content, it must be escaped with another forward slash character. Example showing escaped forward slash in content: XPath: /interfaces/interface[name='1/0/22']/mtu URLPath: /interfaces/interface/1//0//22/mtu Name Matching A parameter using the 'NameMatchMode' data type can be used to control how name node searches are done for nodes using this data type. Alternate Naming A parameter using the 'AltNameMode' data type can be used to control whether alternative node names can be used when name searches are done for nodes using this data type. Exceptions: XML namespaces are not ignored, but if multiple sibling nodes have the same local-name, then the first node found will be used. "; type string { length "1..max"; } } typedef YangcliVariableType { description "yangcli user and system variable types"; type enumeration { enum session { description "Session variable (for future use)"; } enum local { description "Local user variable"; } enum config { description "User configuration variable"; } enum global { description "Global user variable"; } enum system { description "System variable"; } enum queue { description "System internal Queue variable"; } } } /************************************************************* * * Groupings for parameters used in multiple commands * *************************************************************/ grouping MatchParms { leaf match-names { description "Match mode to use for UrlPath name searches."; type NameMatchMode; } leaf alt-names { description "Match mode to use for UrlPath name searches."; type AltNameMode; } } grouping FillParms { choice target-parm { leaf target { description "XPath target object to fill. If prefixes are missing then the first match in any namespace will be used. Any prefix used must be the default prefix assigned to the desired YANG module. Prefix usage is optional."; // type schema-instance-identifier; TBD type string { length "1..max"; } mandatory true; ncx:schema-instance; } leaf urltarget { description "URL encoded target object to fill. Encoding Rules: TBD."; // type URL: TBD type UrlPath; mandatory true; } } leaf optional { description "If present, then prompt for leafs that are optional. If not, skip these objects."; type empty; } anyxml value { description "Contains a string representing the content to use for the filled variable. If a string is entered, then the target value being filled must be a leaf or leaf-list. If a variable is referenced, then it will be used as the content, if the target value being filled is a leaf or a leaf-list. If the target value is a complex object, then the referenced variable must also be a complex object of the same type. E.g., The global variable 'foo' would be specified as: value=$$foo The local variable 'bar' would be specified as: value=$bar An error will be reported if the global or local variable does not reference the same object type as the target parameter."; } uses MatchParms { description "These parameters are ignored unless the 'urltarget' parameter is also present."; } } grouping CommonPduParms { description "Common parms for some local commands that generate NETCONF PDUs"; choice from { mandatory true; leaf varref { description "Use the contents of the specified variable as the content"; type string; } case from-cli { uses FillParms; } } } grouping EditParms { description "Common parms for create, merge, replace commands"; uses CommonPduParms; leaf timeout { description "Timeout to use"; type nt:Timeout; } } grouping SGetParms { description "Common parms for sget and sget-config operations."; leaf nofill { description "If present, and the 'from-cli' option is used for input, then filling of mandatory nodes and key leafs will be skipped. Instead, the target object will be treated as a terminating select node in the filter element."; type empty; } uses ncxapp:CliWithDefaultsParm; } grouping XGetParms { description "Common parms for xget and xget-config operations."; choice from { mandatory true; leaf varref { description "Use the contents of the specified variable as the content"; type string; } leaf select { description "The XPath expression to use in the retrieval operation. The string may only contain single quotes, which are required for string literals. The entire string will be inserted into a double-quoted string, within the get-config PDU. Character entities will be inserted as needed to maintain well-formed XML."; type string { length "1..max"; } ncx:xpath; } leaf urltarget { description "The URL path expression to use in the retrieval operation."; type UrlPath; } } leaf timeout { description "Timeout to use"; type nt:Timeout; } uses ncxapp:CliWithDefaultsParm; uses MatchParms { description "These parameters are ignored unless the 'urltarget' parameter is also present."; } } grouping ConnectParms { description "Common parms for connecting to a NETCONF server. Used by the connect operation and if present at the command line invocation, then the connect operation will be invoked automatically."; leaf user { description "User name to use for NETCONF sessions."; type nt:NcxUserName; } leaf server { description "IP address or DNS name of the NETCONF server target."; type string; } leaf password { description "User password to use for NETCONF sessions. If none, then user will be prompted before connecting."; type string; ncx:password; } leaf private-key-pass { description "Passphrase to unlock the SSH private key."; type string; ncx:password; } leaf ncport { description "NETCONF port number to use. If not present, then port 830, followed by port 22, will be tried."; type uint16 { range "1..max"; } default 830; } leaf timeout { description "Number of seconds to wait for a response from the server before declaring a timeout. Zero means do not timeout at all."; type nt:Timeout; } leaf public-key { type string { length "1 .. max"; } description "Contains the file path specification for the file containing the client-side public key. If both 'public-key' and 'private-key' files are present, the client will attempt to connect to the server using these keys. If this fails, or not done, then password authentication will be attempted."; default "$HOME/.ssh/id_rsa.pub"; } leaf private-key { type string { length "1 .. max"; } description "Contains the file path specification for the file containing the client-side private key. If both 'public-key' and 'private-key' files are present, the client will attempt to connect to the server using these keys. If this fails, or not done, then password authentication will be attempted."; default "$HOME/.ssh/id_rsa"; } leaf use-agent { description "When true, attempt to hand off authentication tasks to an SSH agent. Requires the server to support public key authentication."; type boolean; default false; } uses ncxapp:ProtocolsParm; leaf transport { description "Identifies the transport protocol that should be used."; type enumeration { enum ssh { description "NETCONF over SSH."; reference "RFC 4742; RFC 6242"; } enum tcp { description "NETCONF over TCP. If this enum is selected, then the default --ncport value is set to 2023, and the --protocols value is set to netconf1.0. The --password value will be ignored."; reference "tail-f confd"; } } default ssh; } leaf tcp-direct-enable { description "When --transport=tcp enables use of ncx-connect message instead of the default tail-f custom message. Set this parameter to true to establish direct connection to netconfd."; type boolean; default false; } } grouping XPathParms { description "Common parameters used for XPath script constructs"; leaf expr { description "XPath expression string to evaluate. Use quotes if there are any whitespace or special characters in the expression."; type yang:xpath1.0 { length "1..max"; } mandatory true; } anyxml docroot { description "Use the contents of the specified variable or external XML file as the conceptual document to apply the expression specified in the 'expr' parameter. If this parameter is missing then a dummy document will be used. This is only allowed if the expression only contains variable references or numeric/string constants."; } } /************************************************************* * * CLI parameter set for yangcli program * *************************************************************/ container yangcli { ncx:cli; description "CLI Parameter Set for the NETCONF Client Application."; uses ncxapp:NcxAppCommon; uses ncxapp:ConfigParm; uses ncxapp:YumaHomeParm; uses ncxapp:CommonFeatureParms; uses ncxapp:SubdirsParm; uses ncxapp:HomeParm; uses ConnectParms; uses MatchParms { refine match-names { default one-nocase; } refine alt-names { default true; } } leaf aliases-file { description "Specifies the yangcli command aliases file to use."; type string; default "~/.yuma/.yangcli_aliases"; } leaf autocomp { description "Controls whether partial keywords will be checked for interactive or script commands. By default, the first match for a partial keyword will be used if no definition is found for a command name or parameter name."; type boolean; default true; } leaf autoaliases { description "Controls whether the yangcli command aliases will be saved at exit and loaded at startup. If true, the 'aliases-file' parameter will be used if it is set, or else the default aliases file will be used (~/.yuma/.yangcli_aliases), for loading and saving the yangcli command aliases. If false, the yangcli command aliases will only be stored and loaded manually with the aliases command."; type boolean; default true; } leaf autoload { description "Controls whether any modules (except this one) will be automatically loaded upon startup or upon session startup with a server. If false, the 'mgrload' command must be used to explicitly load all the desired YANG modules."; type boolean; default true; } leaf autohistory { description "Controls whether the command line history buffer will be saved at exit and loaded at startup. If true, the default history file will be used (~/.yuma/.yangcli_history) for loading and saving the history buffer. If false, the history buffer will only be stored and loaded manually with the history command."; type boolean; default true; } leaf autouservars { description "Controls whether the yangcli user variables will be saved at exit and loaded at startup. If true, the 'uservars-file' parameter will be used if set, or else the default user variables file will be used (~/.yuma/yangcli_uservars.xml), for loading and saving the yangcli user variables. If false, the yangcli user variables will only be stored and loaded manually with the uservars command."; type boolean; default true; } leaf bad-data { description "Specifies how invalid user input from the CLI will be handled when filling PDUs for remote operations."; type enumeration { enum ignore { description "Silently accept invalid PDU and data model parameters. Intended for advanced server testing mode only."; } enum warn { description "Warn, but accept invalid PDU and data model parameters."; } enum check { description "Prompt the user to keep the invalid value or re-enter the value."; } enum error { description "Prompt the user to re-enter the invalid value."; } } default "check"; } leaf batch-mode { description "If present, the interactive CLI will not be used. A script should be provided with the 'run-script' parameter, or a command provided with the 'run-command' parameter, or else the program will simply exit. If the auto-connect mode is enabled, then this will mode simply test if a NETCONF session can be established, then exit."; type empty; } leaf default-module { description "Default module name string to use before 'netconf' and 'yangcli' are tried. The module prefix may need to be used for other modules."; type nt:NcxName; } leaf display-mode { description "Controls how values are displayed during output to STDOUT or a log file."; type enumeration { enum plain { description "Plain identifier without any prefix format."; } enum prefix { description "Plain text with XML prefix added format."; } enum module { description "Plain text with module name as prefix added format."; } enum xml { description "XML format."; } enum xml-nons { description "XML format, but no namespace (xmlns) attributes will be generated."; } enum json { description "JSON format."; } } default plain; } leaf echo-replies { description "Allow RPC replies to be echoed to the log or STDOUT If 'true', messages containing data will be output to the log, if log-level is 'info' or higher. If 'false', messages containing data will not be output to the log, regardless of the value of log-level. The $$echo-replies system variable is derived from this parameter."; type boolean; default true; } leaf echo-requests { description "Allow RPC requests to be echoed to the log or STDOUT If 'true', messages containing data will be output to the log, if log-level is 'info' or higher. If 'false', messages will not be output to the log, regardless of the value of log-level. The $$echo-requests system variable is derived from this parameter."; type boolean; default false; } leaf fixorder { description "Controls whether PDU parameters will be automatically sent to the server in the correct order. If false, the specified order will be used. If true, then canonical order will be used"; type boolean; default true; } leaf force-target { description "Controls whether the candidate or running configuration datastore will be used as the default edit target, when both are supported by the server."; type enumeration { enum candidate { description "Force default edit target to be candidate."; } enum running { description "Force default edit target to be running."; } } default candidate; } uses ncxapp:ModuleParm; uses ncxapp:DeviationParm; uses ncxapp:DatapathParm; uses ncxapp:RunpathParm; choice run-startup-mode { leaf run-script { description "The specified script will be invoked upon startup. If the auto-connect parameters are provided, then a session will be established before running the script. If a quoted string is used, then any parameters after the script name will be passed to the script."; type string { length "1 .. 4095"; } } leaf run-command { description "The specified command will be invoked upon startup. If the auto-connect parameters are provided, then a session will be established before running the command."; type string { length "1 .. 4095"; } } } leaf time-rpcs { description "Measure the round-trip time of each request and at the session level. Echo the elapsed time value to screen if in interactive mode, as well as the log if the log is a file instead of stdout. The $$time-rpcs system variable is derived from this parameter."; type boolean; default false; } leaf uservars-file { description "Specifies the yangcli user variables file to use."; type string; default "~/.yuma/yangcli_uservars.xml"; } leaf use-xmlheader { description "Specifies how file result variables will be written for XML files. Controls whether the XML preamble header will be written or not."; type boolean; default true; } } /************************************************************* * * RPC methods for local commands within yangcli * *************************************************************/ rpc alias { description "Show or set a specific yangcli command alias. * Show all aliases in memory (same as aliases): yangcli> alias * Show one alias 'foo': yangcli> alias foo * Set one alias; make sure there is no whitespace between the '=' char and either string. If the value has whitespace, it must be quoted. If the equals sign is present, then a valid value string must be present. Quotes will be preserved if used: * Single quotes can appear within doubled-quoted strings but not any double quotes. * Double quotes can appear within single-quoted strings but not any single quotes. The first token in a plain command or the first token in the right-hand-side expression in an assignment statement can be an alias. The alias name must exact match the token. A new command line will be constructed replacing the alias name with its value. The new command line will then be parsed as usual. yangcli> alias s=show yangcli> alias getfoo='sget-config source=running /top/foo' * Aliases can override real commands, so be careful not to unintentionally alter real commands. yangcli> alias get-config='get-config source=running' "; input { ncx:default-parm var; ncx:default-parm-equals-ok; leaf var { description "The alias command string."; type string { length "1 .. max"; } } } } rpc aliases { description "Manage the yangcli command aliases"; input { choice alias-action { default show; leaf show { description "Show all the yangcli command aliases."; type empty; } leaf clear { description "Delete all the yangcli aliases from memory."; type empty; } leaf load { description "Load the yangcli command aliases from the specified file spec. The default aliases file will be loaded automatically at startup if the '--autoaliases' parameter is present."; type string; default "~/.yuma/.yangcli_aliases"; } leaf save { description "Save the yangcli command aliases to the specified filespec. The default aliases file will be saved automatically at shutdown if the '--autoaliases' parameter is present."; type string; default "~/.yuma/.yangcli_aliases"; } } } } rpc cd { description "Change the current working directory."; input { ncx:default-parm dir; leaf dir { description "Directory path to use."; type string; mandatory true; } } } rpc connect { description "Connect to a NETCONF server."; input { ncx:default-parm server; uses ConnectParms { refine user { mandatory true; } refine server { mandatory true; } refine password { mandatory true; } } } } rpc create { description "Create some NETCONF config data with the edit-config operation"; input { ncx:default-parm target; uses EditParms; } } rpc delete { description "Delete some NETCONF config data with the edit-config operation"; input { ncx:default-parm target; choice delete-target { mandatory true; leaf target { description "Xpath expression indicating the node which is going to be deleted."; type string; } leaf urltarget { description "The URL path expression to use in the delete operation."; type UrlPath; } } } } rpc elif { description "Evaluate an XPath expression locally on the manager. and execute the block of commands that follow if the expression is true. The block ends when a matching 'elif', 'else', or 'end' command is reached. This may only be used after an 'if' or 'elif' command or an error (no matching if command) will occur. "; input { ncx:default-parm expr; uses XPathParms; } } rpc else { description "End an 'if' or 'elif' command block, and start a new command block that must end with an 'end' command. If no 'if' or 'elif' block is in progress then an error will occur. "; } rpc end { description "End an 'if' command block or a 'while' command block. If no block is in progress then an error will occur. "; } rpc eval { description "Evaluate an XPath expression locally on the manager. All local variables will be available to the session context, in the following precedence: 1) current script run level 2) global variable When the result of an eval command is saved to a user variable, it may be altered as follows: Convert to string: - is a simple type - contains 1 node that is a simple type Remove the container: - contains a single complex element Retain the container: - contains multiple elements Usage Examples: > $x = 5 x = '5' > $x = eval '$x + 10' x = '15' > $y = xget /system y = data { system { ... } } > $z = eval '//sysname' docroot=$y z = 'Linux' > $w = eval '/system/uname' docroot=$y w = uname { sysname 'Linux' ... } "; input { ncx:default-parm expr; uses XPathParms; } output { anyxml data { description "The XPath result, returned in a data element. The content will be a simple string for boolean, string, and number result types. The content will be zero or more child elements, in their proper namespaces, if the result is a node-set. Only the requested node(s) will be returned, not their entire path to the document root, like the get and get-config operations. An empty 'data' element will be returned if the result is an empty node-set."; mandatory true; } } } rpc eventlog { description "Access the notification event log"; input { ncx:default-parm show; choice eventlog-action { default show-case; case show-case { leaf show { description "Show the specified number of event log buffer entries. -1 means show all entries. If the --full or --brief help-mode parameters are entered, then the output will be altered."; type LogCount; default -1; } leaf start { description "Start at the specified event index number instead of the beginning of the event log."; type LogIndex; } uses ncxapp:HelpMode; } leaf clear { description "Clear the specified number of notification event log entries. The value zero means clear all the entries."; type LogCount; default 25; } } } } rpc fill { description 'Fill a value for reuse in a NETCONF PDU or other operation. Used in an assignment statement to create a variable for later use: $foo = fill --target=/t:foo-con/bar-list \ --optional \ --current-value=$bar'; input { ncx:default-parm target; uses FillParms; } output { anyxml data { description "The result of the fill operation. The name of the value node may not really be 'data'. If the 'target' or 'current-value' input parameters are used, then the name of the result node will be copied instead. The data type will be inherited from the 'target' or 'current_value' parameters, and not really be an 'anyxml' structure. YANG does have a data type that supports this feature."; } } } rpc get-locks { description "Get a lock for the running configuration and the candidate and startup configurations, if needed. If all the locks cannot be obtained, then release all of them (all-or-nothing). The entire operation must be completed within the lock timeout interval, if it is set."; input { leaf lock-timeout { description "Total time to wait to retrieve all the locks needed to complete the get-locks command. A value of zero means to wait forever and not use a timeout value."; type nt:Timeout; default 120; } leaf retry-interval { description "Retry interval to use if a lock is temporarily unavailable. The client will wait this number of seconds before attempting to get the current lock."; units seconds; type uint32 { range "1 .. 300"; } default 2; } leaf cleanup { description "Indicates whether the locks obtained should be automatically released if an RPC operation fails (after the get-locks succeeds)."; type boolean; default true; } } } rpc help { description "Print the yangcli help text"; input { ncx:default-parm command; choice helptype { leaf command { description "Show help for the specified command, also called an RPC method"; type nt:NcxIdentifier; } leaf commands { description "Show info for all local commands"; type empty; } leaf notification { description "Show help for the specified notification"; type nt:NcxIdentifier; } leaf object { description "Show help for the specified object"; type union { type nt:NcxIdentifier; type UrlPath; } } leaf type { description "Show help for the specified type"; type nt:NcxIdentifier; } } uses ncxapp:HelpMode; } } rpc history { description "Access the command line history buffer"; input { ncx:default-parm show; choice history-action { default show-case; case show-case { leaf show { description "Show the specified number of history buffer entries. -1 means show all entries."; type LogCount; default 25; } uses ncxapp:HelpMode; } leaf clear { description "Clear all the history buffer entries."; type empty; } leaf load { description "Load the command history buffer from the specified file spec."; type string; default "~/.yuma/.yangcli_history"; } leaf save { description "Save the command history buffer in the specified filespec."; type string; default "~/.yuma/.yangcli_history"; } } } } rpc if { description "Evaluate an XPath expression locally on the manager. and execute the block of commands that follow if the expression is true. The block ends when a matching 'elif', 'else', or 'end' command is reached. "; input { ncx:default-parm expr; uses XPathParms; } } rpc insert { description "Insert some NETCONF config data with the edit-config operation"; input { ncx:default-parm target; uses EditParms; leaf order { description "The insert order that should be used. If the values 'before' or 'after' are selected, then the edit-target parameter must also be present."; type enumeration { enum first; enum last; enum before; enum after; } default last; } leaf operation { description "The edit-config operation that should be used."; type enumeration { enum create; enum merge; enum replace; } default merge; } leaf edit-target { description "The value or key clause that should be used. This parameter should only be present if the order selected is 'before' or 'after'. For a leaf-list, the edit-target contains the value of the target leaf-list node within the configuration being edited. The new config will be inserted before or after this leaf-list node, depending on the order selected. E.g., edit-target='some leaf content'. For a list, the edit-target contains the key values of the target list node within the configuration being edited. The new config will be inserted before or after this list node, depending on the order selected. E.g., edit-target=[name='fred'][zipcode=90210]."; type string; } } } rpc list { description "List some NETCONF info."; input { choice listtype { mandatory true; leaf commands { description "List all local and remote commands"; type empty; } leaf files { description "List all available data files."; type empty; } leaf objects { description "List all available top-level object names."; type empty; } leaf oids { description "List all available object identifiers."; type empty; } leaf modules { description "List all available local YANG files."; type empty; } leaf scripts { description "List all available script files."; type empty; } } leaf module { description "List only from this module, if specified."; type nt:NcxIdentifier; } uses ncxapp:HelpMode; } } rpc log-error { description "Write a message to the output log if the log-level is greater or equal to 'error'."; input { ncx:default-parm msg; leaf msg { description "The formatted text string to write to the log."; type string; mandatory true; } } } rpc log-warn { description "Write a message to the output log if the log-level is greater or equal to 'warn'."; input { ncx:default-parm msg; leaf msg { description "The formatted text string to write to the log."; type string; mandatory true; } } } rpc log-info { description "Write a message to the output log if the log-level is greater or equal to 'info'."; input { ncx:default-parm msg; leaf msg { description "The formatted text string to write to the log."; type string; mandatory true; } } } rpc log-debug { description "Write a message to the output log if the log-level is greater or equal to 'debug'."; input { ncx:default-parm msg; leaf msg { description "The formatted text string to write to the log."; type string; mandatory true; } } } rpc merge { description "Merge some NETCONF config data with the edit-config operation"; input { ncx:default-parm target; uses EditParms; } } rpc mgrload { description "Load a module or other file into the client. Use the 'load' command to load a module into the server."; input { ncx:default-parm module; leaf module { description "Module name to load"; type nt:NcxName; mandatory true; } leaf revision { description "Module revision to load."; type nt:Date; } uses ncxapp:DeviationParm; } } rpc pwd { description "Print the current working directory."; } rpc replace { description "Create some NETCONF config data with the edit-config operation"; input { ncx:default-parm target; uses EditParms; } } rpc quit { description "Quit the yangcli application"; } rpc recall { description "Recall the specified command line history buffer entry into the current command line."; input { ncx:default-parm index; leaf index { description "Index [0..N] of the command line history entry to recall."; type LogIndex; mandatory true; } } } rpc release-locks { description "Unlock all the server databases that were previously locked with the get-locks command."; } rpc remove { description "Remove some NETCONF config data with the edit-config operation"; input { ncx:default-parm target; leaf target { description "Xpath expression indicating the node which is going to be deleted."; type string; mandatory true; } } } rpc run { description "Internal command to run a script."; input { ncx:default-parm script; leaf script { description "script filespec to run"; type string; mandatory true; } // hardwired parameter names required at this time // just allow 9 parameter/value pairs // P0 is reserved for the name of the called script leaf P1 { description "script parameter $1"; type string; } leaf P2 { description "script parameter $2"; type string; } leaf P3 { description "script parameter $3"; type string; } leaf P4 { description "script parameter $4"; type string; } leaf P5 { description "script parameter $5"; type string; } leaf P6 { description "script parameter $6"; type string; } leaf P7 { description "script parameter $7"; type string; } leaf P8 { description "script parameter $8"; type string; } leaf P9 { description "script parameter $9"; type string; } } } rpc save { description "Meta command to save configuration edits."; } rpc sget { description "Get some NETCONF running config or state data with the get operation, using an optional subtree filter."; input { ncx:default-parm target; uses CommonPduParms; uses SGetParms; } output { anyxml data { description "The data resulting from the retrieval operation."; } } } rpc sget-config { description "Get some NETCONF config data with the get-config operation, using an optional subtree filter."; input { ncx:default-parm target; uses CommonPduParms; uses SGetParms; container source { description "Particular configuration to retrieve."; choice config-source { mandatory true; leaf candidate { if-feature nc:candidate; description "Only available if 'candidate' capability supported."; type empty; } leaf running { type empty; } leaf startup { if-feature nc:startup; description "Only available if 'startup' capability supported."; type empty; } leaf url { if-feature nc:url; description "URL pointing to config data. Only available if 'url' capability supported."; type nct:ConfigURIType; } } } } output { anyxml data { description "The data resulting from the retrieval operation."; } } } rpc show { description "Local show command for yangcli session info."; input { choice showtype { mandatory true; leaf cli { description "Show the yangcli CLI parameters."; type empty; } leaf module { description "Show full info for one module loaded within the current session."; type nt:NcxIdentifier; } leaf modules { description "Show info for all modules loaded within the current session."; type empty; } leaf local { description "Show info for one local user variable."; type nt:NcxName; } leaf locals { description "Show info for all local user variables."; type empty; } leaf global { description "Show full info for one global user variable."; type nt:NcxName; } leaf globals { description "Show info for all global user variables."; type empty; } leaf objects { description "Show config database objects loaded within the current session."; type empty; } leaf session { description "Show the current session info, including the initial server capabilities for the session."; type empty; } leaf system { description "Show the read-only system environment variables and the read-write yangcli program variables."; type empty; } leaf var { description "Show info for one local or global variable."; type nt:NcxName; } leaf vars { description "Show truncated info for all program variables"; type empty; } leaf version { description "Show yangcli version info."; type empty; } } uses ncxapp:HelpMode; } } rpc start-timer { description "Start a timer to do simple performance measurements."; input { ncx:default-parm id; leaf id { description "The timer ID to use."; type TimerId; default 0; } leaf restart-ok { description "Indicates whether the timer will be used if it is already running. If 'true', the timer will be restarted if it is already running. If 'false', an error will occur if the timer is already running."; type boolean; default true; } } } rpc stop-timer { description "Stop a timer and output the delta value."; input { ncx:default-parm id; leaf id { description "The timer ID to use."; type TimerId; default 0; } leaf echo { description "Echo the elapsed time value to screen if in interactive mode, as well as the log if the log is a file instead of stdout."; type boolean; default true; } } output { // yangcli local RPC commands can return 1 value of any type // as an output parameter. No extra parameters will be // processed in assignment statements leaf delta { description "The elapsed time since the timer started."; type decimal64 { fraction-digits 6; // microseconds } units seconds; } } } rpc while { description "Evaluate an XPath expression locally on the manager. and execute the block of commands that follow while the expression is true. The block ends when a matching 'end' command is reached. "; input { ncx:default-parm expr; uses XPathParms; leaf maxloops { description "Set a maximum number of loops that can be iterated for this while command. The value zero means that no maximum will be enforced. Use this mode with caution."; type uint32; default 65535; } } } rpc xget { description "Get some NETCONF running config or state data with the get operation, using an optional XPath filter."; input { ncx:default-parm select; uses XGetParms; } output { anyxml data { description "The data resulting from the retrieval operation."; } } } rpc xget-config { description "Get some NETCONF config data with the get-config operation, using an optional XPath filter."; input { ncx:default-parm select; uses XGetParms; container source { description "Particular configuration to retrieve."; choice config-source { mandatory true; leaf candidate { if-feature nc:candidate; description "Only available if 'candidate' capability supported."; type empty; } leaf running { type empty; } leaf startup { if-feature nc:startup; description "Only available if 'startup' capability supported."; type empty; } leaf url { if-feature nc:url; description "URL pointing to config data. Only available if 'url' capability supported."; type nct:ConfigURIType; } } } } output { anyxml data { description "The data resulting from the retrieval operation."; } } } rpc unset { description "Delete a specific yangcli command alias. * Delete one alias 'foo': yangcli> unset foo "; input { leaf name { description "Name of the yangcli command alias to delete."; type nt:NcxName; mandatory true; } } } rpc uservars { description "Manage the yangcli user variables"; input { choice uservars-action { mandatory true; leaf clear { description "Delete all the yangcli user variables from memory."; type empty; } leaf load { description "Load the yangcli user variables from the specified XML file spec. The default user variables file will be loaded automatically at startup if the '--autouservars' parameter is present. The container 'vars' data structure represents the format of the XML file expected."; type string; default "~/.yuma/yangcli_uservars.xml"; } leaf save { description "Save the yangcli user variables to the specified filespec. The default user variables file will be saved automatically at shutdown if the '--autouservars' parameter is present. The container 'vars' data structure represents the format of the XML file that will be written."; type string; default "~/.yuma/yangcli_uservars.xml"; } } } } container vars { description "Represents all saved yangcli user variables"; list var { description "Data structure to save one yangcli user variable"; key name; leaf name { type nt:NcxName; description "Name of the user variable"; } leaf target { //need to treat as string in case referenced modules //are not loaded when yangcli starts //ncx:schema-instance; type string; description "Target variable if variable represents a node from a datastore."; } leaf vartype { type YangcliVariableType; description "User variable type; only 'global' variables can be saved in the yangcli uservars file."; default global; } anyxml value { description "Node value renamed to 'value' since YANG requires each node to have a consistent name."; } } } } yuma123_2.14/netconf/modules/yuma123/yuma123-netconf-types.yang0000664000175000017500000003327314770023131024315 0ustar vladimirvladimirmodule yuma123-netconf-types { namespace "http://yuma123.org/ns/yuma123-netconf-types"; prefix "nct"; // for the uri data type import ietf-inet-types { prefix "inet"; } // for the xpath data type import ietf-yang-types { prefix "yang"; } // for NCX 'metadata' language extension import yuma-ncx { prefix "ncx"; } description "NETCONF Protocol * Data Types * Abstract object for PDU components * RPCs Translated from RFC 4741."; contact "Originally Translated by Andy Bierman. Send comments to ."; revision 2017-06-23 { description "Removed dependency to ietf-netconf."; } revision 2017-03-26 { description "Changed namespace in order to edit the original netconfcentral model. Splitted yuma-netconf into yuma123-netconf and yuma123-netconf-types"; } typedef session-id-or-zero-type { type uint32; description "NETCONF Session Id or Zero to indicate none. Duplicates the typedef from ietf-netconf module to avoid dependency"; } typedef Language { description "XML language type for LangString"; type string { pattern '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*'; } } typedef ConfigURIType { type inet:uri; } typedef LangString { description "XML string with a language attribute."; type string; ncx:metadata "Language lang"; } typedef MessageId { description "NETCONF message-id attribute"; type string { length "1..4095"; } } typedef ErrorType { description "NETCONF Error Type"; type enumeration { enum transport; enum rpc; enum protocol; enum application; } } // NETCONF Simple Types typedef error-tag-type { type enumeration { enum in-use { description "The request requires a resource that already is in use."; } enum invalid-value { description "The request specifies an unacceptable value for one or more parameters."; } enum too-big { description "The request or response (that would be generated) is too large for the implementation to handle."; } enum missing-attribute { description "An expected attribute is missing."; } enum bad-attribute { description "An attribute value is not correct; e.g., wrong type, out of range, pattern mismatch."; } enum unknown-attribute { description "An unexpected attribute is present."; } enum missing-element { description "An expected element is missing."; } enum bad-element { description "An element value is not correct; e.g., wrong type, out of range, pattern mismatch."; } enum unknown-element { description "An unexpected element is present."; } enum unknown-namespace { description "An unexpected namespace is present."; } enum access-denied { description "Access to the requested protocol operation, or data model is denied because authorization failed."; } enum lock-denied { description "Access to the requested lock is denied because the lock is currently held by another entity."; } enum resource-denied { description "Request could not be completed because of insufficient resources."; } enum rollback-failed { description "Request to rollback some configuration change (via rollback-on-error or discard-changes operations) was not completed for some reason."; } enum data-exists { description "Request could not be completed because the relevant data model content already exists. For example, a 'create' operation was attempted on data that already exists."; } enum data-missing { description "Request could not be completed because the relevant data model content does not exist. For example, a 'delete' operation was attempted on data that does not exist."; } enum operation-not-supported { description "Request could not be completed because the requested operation is not supported by this implementation."; } enum operation-failed { description "Request could not be completed because the requested operation failed for some reason not covered by any other error condition."; } enum partial-operation { description "This error-tag is obsolete, and SHOULD NOT be sent by servers conforming to this document."; } enum malformed-message { description "A message could not be handled because it failed to be parsed correctly. For example, the message is not well-formed XML or it uses an invalid character set."; } } description "NETCONF Error Tag"; reference "RFC 6241, Appendix A."; } typedef error-severity-type { type enumeration { enum error { description "Error severity"; } enum warning { description "Warning severity"; } } description "NETCONF Error Severity"; reference "RFC XXXX, section 4.3."; } typedef TestOptionType { description "NETCONF 'test-option' Element Content."; type enumeration { enum test-then-set; enum set; enum test-only; } } typedef ErrorOptionType { description "NETCONF 'error-option' Element Content"; type enumeration { enum stop-on-error; enum continue-on-error; enum rollback-on-error; } default "stop-on-error"; } typedef FilterType { description "NETCONF 'filter' Attribute Content"; type enumeration { enum subtree; enum xpath; } default "subtree"; } typedef SelectString { description "XPath string used in the select attribute."; type string { ncx:xpath; } } typedef DefaultOperationType { description "NETCONF 'default-operation' Element Content"; type enumeration { enum merge; enum replace; enum none; } default "merge"; } typedef ConfirmTimeoutType { description "NETCONF 'confirm-timeout' Element Content"; type uint32 { range "1..max"; } units "seconds"; default "600"; // 10 minutes } // NETCONF Hello PDU Data Types grouping NcCapabilities { description "Generic Capabilities List."; container capabilities { config false; leaf-list capability { description "One Generic Capability URI."; type inet:uri; } } } // NETCONF Operations PDU Data Types grouping ErrorInfoContent { description "NETCONF standard 'error-info' Element Content;"; leaf-list bad-attribute { description "Name of the missing or invalid XSD attribute. Used with missing-attribute, bad-attribute, and unknown-attribute error-tag values."; type string; // xs:QName config false; } leaf-list bad-element { description "Name of the element that contains (or should contain) an invalid XSD attribute when used with missing-attribute, bad-attribute, unknown-attribute, error-tag values. Name of an invalid or missing element when used with then missing-element, bad-element, unknown-element, and unknown-namespace error-tag values."; type string; // xs:QName config false; } leaf-list ok-element { description "Identifies an element in the data model for which the requested operation has been completed for that node and all its child nodes. This element can appear zero or more times in the 'error-info' container. Used with the partial-operation error-tag value."; type string; // xs:QName config false; } leaf-list err-element { description "Identifies an element in the data model for which the requested operation has failed for that node and all its child nodes. This element can appear zero or more times in the 'error-info' container. Used with the partial-operation error-tag value."; type string; // xs:QName config false; } leaf-list noop-element { description "Identifies an element in the data model for which the requested operation was not attempted for that node and all its child nodes. This element can appear zero or more times in the container. Used with the partial-operation error-tag value."; type string; // xs:QName config false; } leaf session-id { description "Session ID of session holding the requested lock, or zero to indicate a non-NETCONF entity holds the lock."; type session-id-or-zero-type; config false; } } grouping RpcErrorType { description "NETCONF 'rpc-error' Element Content"; leaf error-type { description "Defines the conceptual layer that the error occurred."; type ErrorType; mandatory true; } leaf error-tag { description "Contains a string identifying the error condition."; type error-tag-type; mandatory true; } leaf error-severity { description "Contains a string identifying the error severity, as determined by the device."; type error-severity-type; mandatory true; } leaf error-app-tag { description "Contains a string identifying the data-model-specific or implementation-specific error condition, if one exists. This element will not be present if no appropriate application error tag can be associated with a particular error condition."; type string; } leaf error-path { description "Contains the absolute XPath [2] expression identifying the element path to the node that is associated with the error being reported in a particular rpc-error element. This element will not be present if no appropriate payload element can be associated with a particular error condition, or if the 'bad-element' QString returned in the 'error-info' container is sufficient to identify the node associated with the error. When the XPath expression is interpreted, the set of namespace declarations are those in scope on the rpc-error element, including the default namespace."; type yang:xpath1.0; } leaf error-message { description "Contains a string suitable for human display that describes the error condition. This element will not be present if no appropriate message is provided for a particular error condition. This element SHOULD include an xml:lang attribute."; type LangString; } anyxml error-info { description "Contains protocol- or data-model-specific error content. This element will not be present if no such error content is provided for a particular error condition. The list in RFC 4741, Appendix A, defines any mandatory error-info content for each error. After any protocol-mandated content, a data model definition may mandate that certain application-layer error information be included in the error-info container. An implementation may include additional elements to provide extended and/or implementation-specific debugging information."; } } grouping RpcDataReplyType { description "NETCONF 'rpc-reply' Error and/or Data Content"; list rpc-error { config false; uses RpcErrorType; } // anyxml data { // config false; // } // any XML is allowed here, not just 1 el. named data // The contents of the 'rpc output' section appears here } grouping RpcOkReplyType { description "NETCONF 'rpc-reply' OK Content."; choice ok-or-error { leaf ok { description "Sent in 'rpc-reply' messages if no errors or warnings occurred during the processing of an 'rpc' request."; type empty; config false; } list rpc-error { config false; uses RpcErrorType; } } } grouping RpcReplyType { description "Generic NETCONF 'rpc-reply' content. "; choice ok-or-data-error { mandatory true; config false; leaf ok { description "Sent in 'rpc-reply' messages if no errors or warnings occurred during the processing of an 'rpc' request."; type empty; } case data-error { uses RpcDataReplyType; } } } } yuma123_2.14/netconf/modules/Makefile.am0000775000175000017500000001335414770023131020274 0ustar vladimirvladimirdist_ietf_yang_DATA = \ $(top_srcdir)/netconf/modules/ietf/ietf-alarms@2019-09-11.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-alarms-x733@2019-09-11.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-system@2014-08-06.yang \ $(top_srcdir)/netconf/modules/ietf/iana-crypt-hash@2014-08-06.yang \ $(top_srcdir)/netconf/modules/ietf/iana-if-type@2014-05-08.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-interfaces@2014-05-08.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-ip@2014-06-16.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-netconf-monitoring@2010-10-04.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-netconf-nmda@2019-01-07.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-netconf-with-defaults@2011-06-01.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-netconf-acm@2018-02-14.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-netconf-notifications@2012-02-06.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-netconf@2011-06-01.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-netconf-partial-lock@2009-10-19.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-yang-library@2016-06-21.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-yang-metadata@2016-08-05.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-yang-types@2013-07-15.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-inet-types@2013-07-15.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-yang-smiv2@2012-06-22.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-ipv4-unicast-routing@2016-11-04.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-ipv6-router-advertisements@2016-11-04.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-ipv6-unicast-routing@2016-11-04.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-routing@2016-11-04.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-hardware@2018-03-13.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-hardware-state@2018-03-13.yang \ $(top_srcdir)/netconf/modules/ietf/iana-hardware@2018-03-13.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-origin@2018-02-14.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-datastores@2018-02-14.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-network@2018-02-26.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-network-state@2018-02-26.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-network-topology@2018-02-26.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-network-topology-state@2018-02-26.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-geo-location@2022-02-11.yang dist_ietf_draft_yang_DATA = \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-network-bridge.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-network-bridge-flows.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-network-bridge-scheduler.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-network-bridge-scheduler-state.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-traffic-generator.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-traffic-analyzer.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-if-extensions.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-if-ethernet-like.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-netconf-client.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-ssh-client.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-tcp-client.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-tcp-server.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-tls-client.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-crypto-types.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-tcp-common.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-truststore.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-keystore.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-ssh-common.yang \ $(top_srcdir)/netconf/modules/ietf-draft/iana-ssh-encryption-algs.yang \ $(top_srcdir)/netconf/modules/ietf-draft/iana-ssh-key-exchange-algs.yang \ $(top_srcdir)/netconf/modules/ietf-draft/iana-ssh-mac-algs.yang \ $(top_srcdir)/netconf/modules/ietf-draft/iana-ssh-public-key-algs.yang \ $(top_srcdir)/netconf/modules/ietf-draft/ietf-tls-common.yang \ $(top_srcdir)/netconf/modules/ietf-draft/iana-tls-cipher-suite-algs.yang dist_ietf_derived_yang_DATA = \ $(top_srcdir)/netconf/modules/ietf-derived/nc-notifications.yang \ $(top_srcdir)/netconf/modules/ietf-derived/notifications.yang dist_ietf_expired_yang_DATA = \ $(top_srcdir)/netconf/modules/ietf-expired/ietf-direct-must-augment-extension@2015-06-12.yang dist_netconfcentral_yang_DATA = \ $(top_srcdir)/netconf/modules/netconfcentral/yuma-types.yang \ $(top_srcdir)/netconf/modules/netconfcentral/yuma-xsd.yang \ $(top_srcdir)/netconf/modules/netconfcentral/yuma-ncx.yang \ $(top_srcdir)/netconf/modules/netconfcentral/yuma-time-filter.yang \ $(top_srcdir)/netconf/modules/netconfcentral/yuma-proc.yang \ $(top_srcdir)/netconf/modules/netconfcentral/yuma-app-common.yang \ $(top_srcdir)/netconf/modules/netconfcentral/yuma-mysession.yang \ $(top_srcdir)/netconf/modules/netconfcentral/toaster.yang dist_yuma123_yang_DATA = \ $(top_srcdir)/netconf/modules/yuma123/netconfd.yang \ $(top_srcdir)/netconf/modules/yuma123/yangcli.yang \ $(top_srcdir)/netconf/modules/yuma123/yangcli-ex.yang \ $(top_srcdir)/netconf/modules/yuma123/netconfd-ex.yang \ $(top_srcdir)/netconf/modules/yuma123/yuma123-netconf.yang \ $(top_srcdir)/netconf/modules/yuma123/yuma123-netconf-types.yang \ $(top_srcdir)/netconf/modules/yuma123/yuma123-system.yang \ $(top_srcdir)/netconf/modules/yuma123/yuma123-mysession-cache.yang dist_nmda_modules_ietf_yang_DATA= \ $(top_srcdir)/netconf/modules/ietf/ietf-interfaces@2018-02-20.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-ip@2018-02-22.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-routing@2018-03-13.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-ipv4-unicast-routing@2018-03-13.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-ipv6-unicast-routing@2018-03-13.yang \ $(top_srcdir)/netconf/modules/ietf/ietf-ipv6-router-advertisements@2018-03-13.yang yuma123_2.14/netconf/modules/test/0000775000175000017500000000000014770023131017206 5ustar vladimirvladimiryuma123_2.14/netconf/modules/test/pass/0000775000175000017500000000000014770023131020154 5ustar vladimirvladimiryuma123_2.14/netconf/modules/test/pass/test8a.yang0000664000175000017500000000034714770023131022250 0ustar vladimirvladimirsubmodule test8a { belongs-to test8 { prefix t8; } revision 2011-02-04 { description "Initial revision."; } container top { leaf top1 { type string; must "/top"; } } } yuma123_2.14/netconf/modules/test/pass/testgrouping.yang0000664000175000017500000000160314770023131023566 0ustar vladimirvladimirmodule testgrouping { namespace "http://netconfcentral.org/ns/testgrouping"; prefix "foo"; revision 2009-07-28 { description "Initial revision."; } typedef MyType { type int32; } identity MyBase; identity my-identity { base MyBase; } grouping ggg { leaf XXX { type int32; } container test-top { typedef MyType2 { type uint16; } must "foo:bar < 42"; must "bar = 142"; leaf bar { type uint8; when "../foo:XXX = 11"; } leaf baz { must "../goo = 'my-identity'"; type MyType; } leaf goo { type identityref { base MyBase; } default my-identity; } leaf AA { type MyType2; } leaf BB { type foo:MyType2; } } } } yuma123_2.14/netconf/modules/test/pass/entertainment-facilities.yang0000664000175000017500000000225314770023131026025 0ustar vladimirvladimirmodule entertainment-facilities { namespace "http://example.com/ns/entertainment-facilities"; prefix entertainment-facilities; organization "Example, Inc."; contact "support@example.com"; description "Module used in DIRECT-MUST-AUGMENT-EX example."; revision 2015-06-12 { description "Initial version"; } container people { description "Contains all people."; list person { key name; leaf name { type string; } leaf age { type uint32; mandatory true; } } } identity entertainment-facility-generic { description "Base identity from which specific" + "entertainment facility types are derived."; } container entertainment-facilities { description "Container for all entertainment-facilities information."; list entertainment-facility { key name; leaf name { type string; } leaf type { type identityref { base entertainment-facility-generic; } mandatory true; } leaf-list visitor { type leafref { path "/people/person/name"; } } } } } yuma123_2.14/netconf/modules/test/pass/testgrp2.yang0000664000175000017500000000037114770023131022607 0ustar vladimirvladimirmodule testgrp2 { namespace "http://netconfcentral.org/ns/testgrp2"; prefix "tgrp2"; import testgrp { prefix "tgrp"; } revision "2010-05-27" { description "Initial revision."; } uses tgrp:testgrp; rpc foo; } yuma123_2.14/netconf/modules/test/pass/testrev.yang0000664000175000017500000000122014770023131022523 0ustar vladimirvladimirmodule testrev { namespace "http://netconfcentral.org/ns/testrev"; prefix "trev"; import testr1 { prefix r1; revision-date 2009-01-01; } import testr2 { prefix r2; revision-date 2009-01-01; } organization "Netconf Central"; contact "Andy Bierman"; description "YANG import-by-revision test module."; revision 2009-02-20 { description "Initial version."; } container foo { uses r1:TestGroup1; } container bar { uses r2:TestGroup; } leaf baz { type r2:TestType { range 2..max; } } } yuma123_2.14/netconf/modules/test/pass/t29.yang0000664000175000017500000000470214770023131021455 0ustar vladimirvladimirmodule t29 { namespace "http://netconfcentral.org/ns/t29"; prefix "t29"; revision 2012-01-22; augment /mlist/mchoice/mcase1 { when "mkey = 4"; leaf xx { type string; } } grouping G { leaf xxx { type string; } } leaf t29-a { type int32; } leaf t29-b { type int32; } leaf t29-c { type int32; } leaf t29-d { type int32; } list mlist { when "../t29-a = 1"; must "../t29-b = 2"; key mkey; leaf mkey { must "../../t29-b > 1"; type int32; } choice mchoice { when "../t29-c != 3"; case mcase1 { when "/t29-c != 4"; leaf x { must "../mkey > 0"; type int32; } uses G { when "mkey < 10"; } } case mcase2 { when "../t29-d != 4"; leaf y { must "../mkey > 1"; type int32; } } } } choice top-choice { case a { leaf a1 { type string; } leaf a2 { type uint32; mandatory true; } } case b { leaf-list b1 { type uint8; min-elements 2; } choice b2 { case b2a { leaf b2a1 { type string; mandatory true; } leaf b2a2 { type string; } } container b3-npcon { leaf b3-npcon-leaf { type int32; mandatory true; } } } } case c { leaf c1 { type int32; } } } list list1 { key "a b"; leaf a { type string; } leaf b { type string; } choice list1-choice { case c1 { leaf c1-leaf1 { type string; } leaf c1-leaf2 { type uint32; mandatory true; } } container c2 { leaf c2mand { type int32; mandatory true; } leaf c2def { type int32; default 42; } leaf c2opt { type string; } } case c3 { list c3list { key x; leaf x { type int8; } leaf y { type string; } leaf z { type string; } } } } } list list2 { key "x"; unique zz; unique "z/a z/b z/c"; leaf x { type int32; } leaf-list y { type int32; } container z { leaf a { type leafref { path "../../y"; } } leaf b { type boolean; } leaf c { type int8; } } leaf zz { type uint8; } } } yuma123_2.14/netconf/modules/test/pass/test1c.yang0000664000175000017500000000073014770023131022237 0ustar vladimirvladimirsubmodule test1c { belongs-to test1 { prefix t1c; } revision 2008-10-12 { description "Initial revision."; } list test1-c.sub { key "a b"; leaf a { type int32; } leaf b { type int16; } leaf g { type int32; mandatory true; } } list test1-c.sub2 { key "c"; leaf c { type int32; } leaf d { type leafref { path "/test1-c.sub/b"; } } leaf g { type int32; mandatory true; } } } yuma123_2.14/netconf/modules/test/pass/test6.yang0000664000175000017500000000040314770023131022076 0ustar vladimirvladimirmodule test6 { namespace "http://netconfcentral.org/ns/test6"; prefix "t6"; import test5 { prefix t5; } revision 2008-11-20 { description "Initial revision."; } augment /t5:test5/t5:a { leaf g3 { type int32; } } } yuma123_2.14/netconf/modules/test/pass/test1b.yang0000664000175000017500000000105114770023131022233 0ustar vladimirvladimirsubmodule test1b { belongs-to test1 { prefix t1; } revision 2008-10-12 { description "Initial revision."; } list test1-b.sub { key "a b c"; leaf a { type int32; } leaf b { type int16; } leaf c { type int64; } choice d { case e { leaf f { type empty; } leaf g { type int32; mandatory true; } leaf h { type int32; config false; } } leaf i { type leafref { path /test1-b.sub/a; } } } } } yuma123_2.14/netconf/modules/test/pass/t12.yang0000664000175000017500000000076414770023131021451 0ustar vladimirvladimirmodule t12 { namespace "http://netconfcentral.org/ns/t12"; prefix t12; import t11 { prefix t11; } revision "2011-07-11" { description "Augment test"; } leaf t12-leaf { type uint32; } augment /t11:t11-list { container e42 { when "/t12-leaf = 42"; leaf ext42 { type string; } } leaf X { type string; } } leaf t12-leaf2 { type string { pattern foo:bar; } } leaf t12-leaf3 { type string { pattern 42; } } } yuma123_2.14/netconf/modules/test/pass/siltest3.yang0000664000175000017500000000416414770023131022613 0ustar vladimirvladimirmodule siltest3 { namespace "http://netconfcentral.org/ns/siltest3"; prefix "siltest3"; description "Test SIL code generation for nested lists and union types."; revision 2011-09-16 { description "Initial version."; } list u { key ux; leaf ux { type union { type uint32; type string; } } leaf uy { type uint32; } leaf uz { config false; type uint32; } leaf uu { config false; type union { type int32; type instance-identifier; type uint8; type boolean; } } } list c { key x; leaf x { type uint16; } leaf y { type instance-identifier; } leaf z { type uint64; config false; } leaf cuu { type union { type int32; type instance-identifier; type uint8; type boolean; type string; } } list cc { key "xx1 xx2 xx3 xx4 xx5 xx6 xx7 xx8 xx9"; leaf xx1 { type union { type uint32; type string; } } leaf xx2 { type int8; } leaf xx3 { type int16; } leaf xx4 { type int32; } leaf xx5 { type int64; } leaf xx6 { type uint8; } leaf xx7 { type uint16; } leaf xx8 { type uint32; } leaf xx9 { type uint64; } leaf yy { type int32; } leaf zz { type uint64; config false; } list ccc { key xxx; leaf xxx { type string; } leaf yyy { type boolean; } leaf zzz { type uint64; config false; } } } } identity x; rpc r { input { leaf a { type string; } leaf b { type int8; } } output { leaf c { type int64; } leaf d { type identityref { base x; } } } } notification n { leaf a { type binary; } leaf b { type instance-identifier; } leaf c { type int64; } leaf d { type identityref { base x; } } } } yuma123_2.14/netconf/modules/test/pass/testgrouping2.yang0000664000175000017500000000057314770023131023655 0ustar vladimirvladimirmodule testgrouping2 { namespace "http://netconfcentral.org/ns/testgrouping2"; prefix "foo2"; import testgrouping { prefix foo1; } revision 2009-07-28 { description "Initial revision."; } typedef MyType { type boolean; } identity my-identity { base foo1:MyBase; } container one { uses foo1:ggg; } } yuma123_2.14/netconf/modules/test/pass/t9.yang0000664000175000017500000000035614770023131021374 0ustar vladimirvladimirmodule t9 { namespace "http://netconfcentral.org/ns/t9"; prefix t9; revision "2011-07-01" { description "Bug report"; } list t9-one { key "id1"; leaf id1 { type string; } leaf id2 { type int8; } } } yuma123_2.14/netconf/modules/test/pass/test4.yang0000664000175000017500000000103614770023131022077 0ustar vladimirvladimirmodule test4 { namespace "http://netconfcentral.org/ns/test4"; prefix "t4"; revision 2009-09-09 { description "Initial revision."; } typedef aa-type { description "test type"; type int32 { range "1..10 | 20..30"; } } container a { leaf-list aa { type aa-type; max-elements 5; } } leaf b { type leafref { path ../a/aa; } } list c { key x; leaf x { type uint16; } leaf y { type instance-identifier; } } } yuma123_2.14/netconf/modules/test/pass/testgrouping3.yang0000664000175000017500000000050414770023131023650 0ustar vladimirvladimirmodule testgrouping3 { namespace "http://netconfcentral.org/ns/testgrouping3"; prefix "foo3"; import testgrouping2 { prefix foo2; } revision 2009-07-28 { description "Initial revision."; } augment /foo2:one/foo2:test-top { leaf my-leaf { type int32; } } } yuma123_2.14/netconf/modules/test/pass/siltest2.yang0000664000175000017500000000145114770023131022606 0ustar vladimirvladimirmodule siltest2 { namespace "http://netconfcentral.org/ns/siltest2"; prefix "sil2"; description "Test proper SIL generation of MRO functions and auto-generated containers."; revision 2011-09-10 { description "Initial revision."; } container c1 { leaf counter1 { config false; type int32; } } container c2 { config false; leaf counter2 { config false; type int32; } } container top { container c3 { leaf counter3 { config false; type int32; } } container c4 { config false; leaf counter4 { config false; type int32; } } } } yuma123_2.14/netconf/modules/test/pass/testaug2.yang0000664000175000017500000000060314770023131022571 0ustar vladimirvladimirmodule testaug2 { namespace "http://netconfcentral.org/ns/testaug2"; prefix testaug2; import testobsolete { prefix t3; } description "Bug report (???)"; revision "2011-07-01" { description "Initial version"; } augment /t3:one { leaf myid1 { type string; } list mylist { key x; leaf x { type int32; } leaf y { type string; } } } } yuma123_2.14/netconf/modules/test/pass/testmandatory.yang0000664000175000017500000000224014770023131023730 0ustar vladimirvladimirmodule testmandatory { namespace "http://netconfcentral.org/ns/testmandatory"; prefix "tm"; revision 2009-08-29 { description "Initial revision."; } grouping top-group { leaf XXX { type string; mandatory true; } choice YYY { mandatory true; leaf a { type string; } leaf b { type string; } } } uses top-group; leaf top-mandatory-leaf { type string; mandatory true; } choice top-mandatory-choice { mandatory true; leaf a { type string; } leaf b { type string; } } container top-mandatory-container { leaf a { type string; mandatory true; } } container top-mandatory-container2 { container next-np-container { leaf a { type string; mandatory true; } } } leaf not-top-mandatory-leaf { type string; mandatory true; config false; } choice not-top-mandatory-choice { mandatory true; config false; leaf c { type string; } leaf d { type string; } } } yuma123_2.14/netconf/modules/test/pass/testsi.yang0000664000175000017500000000046714770023131022356 0ustar vladimirvladimirmodule testsi { namespace "http://netconfcentral.org/ns/testsi"; prefix "tsi"; import yuma-ncx { prefix ncx; } revision 2010-02-12 { description "Initial revision."; } typedef t1 { type string { ncx:schema-instance; } } leaf si1 { type t1; } } yuma123_2.14/netconf/modules/test/pass/testcrud.yang0000664000175000017500000000116314770023131022672 0ustar vladimirvladimirmodule testcrud { namespace "http://netconfcentral.org/ns/testcrud"; prefix "tcrud"; revision 2010-05-08 { description "Initial revision."; } leaf defleaf { type int32; default 42; } container pcon { presence "test"; leaf defleaf { type int32; default 42; } } container npcon { leaf defleaf { type int32; default 42; } } list deflist { key name; unique defleaf; leaf name { type string; } leaf defleaf { type int32; default 42; } } } yuma123_2.14/netconf/modules/test/pass/test1.yang0000664000175000017500000000056514770023131022102 0ustar vladimirvladimirmodule test1 { namespace "http://netconfcentral.org/ns/test1"; prefix "t1"; include test1a; include test1b; include test1c; revision 2008-10-12 { description "Initial revision."; } typedef AA { type string { pattern ".*"; } } leaf t22 { type boolean; } leaf t23 { type string; config false; } } yuma123_2.14/netconf/modules/test/pass/siltest6.yang0000664000175000017500000000100014770023131022600 0ustar vladimirvladimirmodule siltest6 { namespace "http://netconfcentral.org/ns/siltest6"; prefix "siltest6"; revision 2011-10-28 { description "Initial revision."; } container top6 { container profiles { list profile { key id; leaf id { type uint32; } container resources { list resource { key id; leaf id { type uint32; } leaf id2 { type uint32; } } } } } } } yuma123_2.14/netconf/modules/test/pass/test10.yang0000664000175000017500000000303714770023131022157 0ustar vladimirvladimirmodule test10 { namespace "http://netconfcentral.org/ns/test10"; prefix "t10"; revision 2011-02-06 { description "Initial revision."; } extension e1 { argument parm { yin-element true; } } t10:e2 "before ext e2"; extension e2 { argument parm2 { yin-element false; } t10:e2 "in ext e2"; } leaf a { t10:e1 "in leaf a"; type int32 { t10:e1 "in type for leaf a"; range "2 .. 12" { t10:e1 "in range for type for leaf a"; } } } t10:e2 "after leaf a"; typedef T_NcAccessControlType { t10:e2 "in typedef"; description "NCX System access control mode."; type enumeration { t10:e1 "in typedef type"; enum open { description "no access control checking enforced"; t10:e1 "in typedef type enum open"; } enum loose { description "any RPC method in the netconf namespace can be invoked; read-only data allowed for all"; t10:e1 "in typedef type enum loose"; } enum strict { description "RPC entry must be present to invoke an RPC method; ncxacl Data entry must be present to access any data. (Except for user == 'root'.)"; } } default "strict"; } t10:e2 myleaf { type string; mandatory true; t10:e1 "inside an e2 type"; } } yuma123_2.14/netconf/modules/test/pass/trev.yang0000664000175000017500000000027414770023131022017 0ustar vladimirvladimirmodule trev { namespace "http://netconfcentral.org/ns/trev"; prefix trev; revision "2011-07-04"; revision "2011-06-04"; revision "2010-07-04"; revision "2010-01-04"; } yuma123_2.14/netconf/modules/test/pass/siltest1.yang0000664000175000017500000000156214770023131022610 0ustar vladimirvladimirmodule siltest1 { namespace "http://www.netconfcentral.org/ns/siltest1"; prefix "siltest1"; description "Bug Report -- nested SIL callbacks not called correctly."; revision 2011-06-21 { description "Initial version."; } leaf sil-leaf { type string; } leaf sil-rleaf { config false; type string; } container sil-rcon { config false; leaf rcon1 { type string; } leaf rcon2 { type int32; } } list sil-list { key sil-list-key; leaf sil-list-key { type int8; } leaf sil-list-leaf { type string; } leaf sil-list-rleaf { config false; type string; } leaf sil-list-rleaf2 { config false; type int8; } } container sil-A { container sil-B { container sil-C { leaf val1 { type string; } leaf val2 { type string; } } } } } yuma123_2.14/netconf/modules/test/pass/tunion.yang0000664000175000017500000000234514770023131022354 0ustar vladimirvladimir module tunion { namespace "http://netconfcentral.org/ns/tunion"; prefix tunion; import ietf-inet-types { prefix inet; } revision "2011-12-09" { description "Bug report"; } typedef nested-union { type union { type boolean; type inet:ip-address; type int32; } default 42; } leaf nested-inline { type union { type boolean; type union { type string { length 4; } type union { type uint8; type instance-identifier; type int8; } type int16; } } default -42; description "default test needs to fail all members until int8 test"; } typedef matchall-string-type { type string { pattern "\*"; } } container tunion { leaf tunion-leaf { type union { type matchall-string-type; type int32; } default "*"; } leaf nested { type nested-union; default -1; description "default test needs to fail all members until int32 test"; } leaf nested2 { type nested-union; default true; description "default test needs to pass boolean test"; } } } yuma123_2.14/netconf/modules/test/pass/negnum.yang0000664000175000017500000000046514770023131022332 0ustar vladimirvladimirmodule negnum { namespace "http://www.netconfcentral.org/ns/negnum"; prefix "negnum"; revision 2011-06-06 { description "Bug Report."; } container test1 { leaf longitude { type int32; description "Longitude of the location"; } must "longitude > -180"; } } yuma123_2.14/netconf/modules/test/pass/siltest4.yang0000664000175000017500000000320114770023131022603 0ustar vladimirvladimirmodule siltest4 { namespace "http://netconfcentral.org/ns/siltest4"; prefix "siltest4"; import yuma-ncx { prefix ncx; } description "Test SIL callback behavior for ncx:sil-delete-children-first."; revision 2011-10-08 { description "Initial version."; } list c4 { ncx:sil-delete-children-first; // applies here key x; leaf x { type uint16; } leaf y { ncx:sil-delete-children-first; // ignored here type uint32; } leaf z { type uint64; config false; } leaf cuu { type union { type int32; type instance-identifier; type uint8; type boolean; type string; } } list cc { ncx:sil-delete-children-first; // applies here key "xx1 xx2"; leaf xx1 { type uint32; } leaf xx2 { type int32; } leaf yy { type int32; } leaf zz { type uint64; config false; } list ccc { // this SIL will get called in ancestor deleted key xxx; leaf xxx { type string; } leaf yyy { type boolean; } leaf zzz { type uint64; config false; } container con-xxx { // this SIL will not get called in ancestor deleted leaf x { type string; } } } container dddd { ncx:sil-delete-children-first; // applies here leaf xxx2 { type string; } leaf yyy2 { type boolean; } leaf zzz2 { type uint64; config false; } } } } } yuma123_2.14/netconf/modules/test/pass/testdev.yang0000664000175000017500000000060114770023131022507 0ustar vladimirvladimirmodule testdev { namespace "http://netconfcentral.org/ns/testdev"; prefix "td"; revision 2009-08-02 { description "Initial revision."; } container test-top { presence "not mandatory top"; leaf a { type uint32; units seconds; } leaf b { type string; mandatory true; } } } yuma123_2.14/netconf/modules/test/pass/test8d.yang0000664000175000017500000000047514770023131022255 0ustar vladimirvladimirsubmodule test8d { belongs-to test8 { prefix t8; } include test8a; revision 2011-02-04 { description "Initial revision."; } augment /top { leaf c { type string; must "/top/a"; } } leaf ttt { must "/foo/bar"; type string; } } yuma123_2.14/netconf/modules/test/pass/test8c.yang0000664000175000017500000000036714770023131022254 0ustar vladimirvladimirsubmodule test8c { belongs-to test8 { prefix t8; } include test8a; include test8d; revision 2011-02-04 { description "Initial revision."; } augment /top { leaf b { type string; } } } yuma123_2.14/netconf/modules/test/pass/testconfig.yang0000664000175000017500000000074514770023131023207 0ustar vladimirvladimirmodule testconfig { namespace "http://netconfcentral.org/ns/testconfig"; prefix "tcfg"; organization "Netconf Central"; contact "Andy Bierman"; description "YANG config-stmt test module."; revision 2011-01-20 { description "Initial version."; } grouping A { leaf a { type string; } } container foo { config false; uses A; } augment /foo { leaf b { type string; } } } yuma123_2.14/netconf/modules/test/pass/testext.yang0000664000175000017500000000115614770023131022537 0ustar vladimirvladimirmodule testext { namespace "http://netconfcentral.org/ns/testext"; prefix testext; description "Test extension handling and string indent preservation"; revision "2011-06-11" { description "Test module in-progress. Testing Testing Testing"; } extension ext-1 { argument ext-1-1 { yin-element true; } } extension ext-2 { argument ext-2-1 { yin-element false; } } testext:ext-1 foo-message; testext:ext-2 bar-message; rpc test-rpc { input { leaf a { type string; default '"'; } } } } yuma123_2.14/netconf/modules/test/pass/t8.yang0000664000175000017500000000045114770023131021367 0ustar vladimirvladimirmodule t8 { namespace "http://netconfcentral.org/ns/t8"; prefix t8; import t9 { prefix t9; } revision "2011-07-01" { description "Bug report"; } augment /t9:t9-one { list mylist { key x; leaf x { type int32; } leaf y { type string; } } } } yuma123_2.14/netconf/modules/test/pass/entertainment-facilities-casino.yang0000664000175000017500000000162514770023131027301 0ustar vladimirvladimirmodule entertainment-facilities-casino { namespace "http://example.com/ns/entertainment-facilities-casino"; prefix ef-casino; import ietf-direct-must-augment-extension { prefix direct-must-augment-ex; } import entertainment-facilities { prefix ef; } organization "Example, Inc."; contact "support@example.com"; description "Module used in DIRECT-MUST-AUGMENT-EX example. " + "Defines entertainment facility of type casino where" + "only adults are allowed."; revision 2015-06-12 { description "Initial version"; } identity casino { base ef:entertainment-facility-generic; } direct-must-augment-ex:augment "/ef:entertainment-facilities/ef:entertainment-facility/" + "ef:visitor" { when "../ef:type='ef-casino:casino'"; must "(/people/person[name=current()]/age>=18)" { error-message "Only adults are allowed in a casino."; } } } yuma123_2.14/netconf/modules/test/pass/test3a.yang0000664000175000017500000000167114770023131022244 0ustar vladimirvladimirmodule test3 { namespace "http://netconfcentral.org/ns/test3"; prefix "t3"; revision 2009-09-09 { description "Initial revision."; } identity testbase; identity testbase1 { base t3:testbase; } container test33 { choice f { mandatory true; leaf f1 { if-feature X; type string; } leaf f2 { type string; } } choice g { mandatory false; leaf g1 { type string; mandatory true; } leaf g2 { type string; } case g3 { leaf g3a { type string; mandatory true; } leaf g3b { type string; } } } } feature X; typedef A { type uint32; default 42; } typedef B { type A { range "14 .. max"; } } typedef C { type B { range "min .. 41"; } default 15; } leaf test-C { type C; } } yuma123_2.14/netconf/modules/test/pass/entertainment-facilities-zoo.yang0000664000175000017500000000320114770023131026624 0ustar vladimirvladimirmodule entertainment-facilities-zoo { namespace "http://example.com/ns/entertainment-facilities-zoo"; prefix ef-zoo; import ietf-direct-must-augment-extension { prefix direct-must-augment-ex; } import entertainment-facilities { prefix ef; } organization "Example, Inc."; contact "support@example.com"; description "Module used in DIRECT-MUST-AUGMENT-EX example. " + "Defines entertainment facility of type zoo where" + "no children without adult supervision are allowed."; revision 2015-06-12 { description "Initial version"; } identity zoo { base ef:entertainment-facility-generic; } augment "/ef:entertainment-facilities/ef:entertainment-facility" { when "ef:type='ef-zoo:zoo'"; list child-supervision { key child; leaf child { type leafref { path "../../ef:visitor"; } must "/people/person[name=current()]/age<=14" { error-message "People older then 14 are not children."; } } leaf adult { mandatory true; type leafref { path "../../ef:visitor"; } must "/people/person[name=current()]/age>=18" { error-message "People younger then 18 are not adults."; } } } } direct-must-augment-ex:augment "/ef:entertainment-facilities/ef:entertainment-facility/" + "ef:visitor" { when "../ef:type='ef-zoo:zoo'"; must "((../child-supervision[child=current()]) or (/people/person[name=current()]/age>=14))" { error-message "Children without adult supervision are not allowed in a zoo."; } } } yuma123_2.14/netconf/modules/test/pass/testobsolete.yang0000664000175000017500000000114514770023131023551 0ustar vladimirvladimirmodule testobsolete { namespace "http://netconfcentral.org/ns/testobsolete"; prefix testobsolete; description "Bug report -- obsolete status getting ignored in mandatory-check."; revision "2011-06-23" { description "Initial version"; } list one { key "id1 id2"; leaf id1 { type string; } leaf id2 { type string; } leaf leaf1 { config false; type int32; } leaf leaf2 { type int32; mandatory true; status obsolete; } } list two { config false; leaf id { type string; } leaf leaf1a { type int32; } } } yuma123_2.14/netconf/modules/test/pass/test1a.yang0000664000175000017500000000145414770023131022241 0ustar vladimirvladimirsubmodule test1a { belongs-to test1 { prefix t1a; } revision 2011-07-14 { description "Fix bug in relative path. Choice and case nodes are not supposed to be included in path-stmts."; } revision 2008-10-12 { description "Initial revision."; } typedef X { type int32 { range 1..10; } } typedef XX { type int32 { range min..10; } } list test1-a.sub { key "a b c"; leaf a { type int32; } leaf b { type int16; } leaf c { type int64; } choice d { case e { leaf f { type empty; } leaf g { type int32; mandatory true; } leaf h { type int32; config false; } } leaf i { type leafref { path ../a; } } leaf j { type t1a:X; } } } } yuma123_2.14/netconf/modules/test/pass/testr2@2008-01-01.yang0000664000175000017500000000107314770023131023326 0ustar vladimirvladimirmodule testr2 { namespace "http://netconfcentral.org/ns/testr2"; prefix "r2"; organization "Netconf Central"; contact "Andy Bierman"; description "YANG import-by-revision test module."; revision 2008-01-01 { description "Initial version."; } typedef TestType { type int32 { range 1..9; } default 5; } grouping TestGroup { leaf A { type int64; } leaf B { type instance-identifier; } } container bar-r2 { uses r2:TestGroup; } } yuma123_2.14/netconf/modules/test/pass/test8b.yang0000664000175000017500000000041014770023131022240 0ustar vladimirvladimirsubmodule test8b { belongs-to test8 { prefix t8; } include test8a; include test8c; revision 2011-02-04 { description "Initial revision."; } augment /top { leaf a { type leafref { path /top/b; } } } } yuma123_2.14/netconf/modules/test/pass/test3.yang0000664000175000017500000000214714770023131022102 0ustar vladimirvladimirmodule test3 { namespace "http://netconfcentral.org/ns/test3"; prefix "t3"; revision 2008-10-19 { description "Initial revision."; } container test-D1 { leaf test-D { type local-D; } typedef local-D { type int32 { range "1 .. 4 | 7"; } } } leaf test-D { type int32 { range "1 .. 4 | 7"; } } container test33 { presence "not a top-level mandatory container"; choice f { mandatory true; leaf f1 { type string; } leaf f2 { type string; } } choice g { mandatory false; leaf g1 { type string; mandatory true; } leaf g2 { type string; } case g3 { leaf g3a { type string; mandatory true; } leaf g3b { type string; } } } } typedef A { type uint32; default 42; } typedef B { type A { range "14 .. max"; } } typedef C { type B { range "min .. 41 | 45"; } default 15; } leaf test-C { type C; } } yuma123_2.14/netconf/modules/test/pass/testmust.yang0000664000175000017500000003157114770023131022733 0ustar vladimirvladimirmodule testmust { namespace "http://netconfcentral.org/ns/testmust"; prefix "tm"; organization "Netconf Central"; contact "Andy Bierman"; description "YANG must-stmt test module."; revision 2011-03-19 { description "Add more test cases."; } revision 2008-12-18 { description "Initial version."; } container test1 { description "Test XPath 1.0 location path expression parsing. These must-stmts are not expected to evaluate to true. Taken from XPath 1.0, section 2"; must "/child::doc/child::chapter[position()=5]/child::section[position()=2]" { description "selects the second section of the fifth chapter of the doc document element."; } must "child::doc/child::chapter[position()=1]/child::section[position()=2]" { description "selects the second section of the fifth chapter of the doc document element."; } must "boolean(42) + boolean(/test1) + boolean ( 'test' )"; must "/descendant::para" { description "selects all the para elements in the same document as the context node."; } must "//ancestor::*"; must "descendant::para" { description "selects the para element descendants of the context node."; } must "child::*" { description "selects all element children of the context node."; } must "child::para" { description "selects the para element children of the context node."; } must "child::text()" { description "selects all text node children of the context node."; } must "child::node()" { description "selects all the children of the context node, whatever their node type."; } must "attribute::name" { description "selects the name attribute of the context node."; } must "attribute::*" { description "selects all the attributes of the context node."; } must "ancestor::div" { description "selects all div ancestors of the context node."; } must "ancestor-or-self::div" { description " selects the div ancestors of the context node and, if the context node is a div element, the context node as well."; } must "descendant-or-self::para" { description "selects the para element descendants of the context node and, if the context node is a para element, the context node as well."; } must "self::para" { description "selects the context node if it is a para element, and otherwise selects nothing."; } must "child::chapter/descendant::para" { description "selects the para element descendants of the chapter element children of the context node."; } must "child::*/child::para" { description "selects all para grandchildren of the context node."; } must "/" { description "selects the document root (which is always the parent of the document element)."; } must "/descendant::olist/child::item" { description "selects all the item elements that have an olist parent and that are in the same document as the context node."; } must "child::para[position()=1]" { description "selects the first para child of the context node."; } must "child::para[position()=last()]" { description "selects the last para child of the context node."; } must "child::para[position()=last()-1]" { description "selects the last but one para child of the context node."; } must "child::para[position()>1]" { description "selects all the para children of the context node other than the first para child of the context node."; } must "following-sibling::chapter[position()=1]" { description "selects the next chapter sibling of the context node."; } must "preceding-sibling::chapter[position()=1]" { description "selects the previous chapter sibling of the context node."; } must "/descendant::figure[position()=42]" { description "selects the forty-second figure element in the document."; } must "child::para[attribute::type='warning']" { description "selects all para children of the context node that have a type attribute with value warning."; } must "child::para[attribute::type='warning'][position()=5]" { description "selects the fifth para child of the context node that has a type attribute with value warning."; } must "child::para[position()=5][attribute::type='warning']" { description "selects the fifth para child of the context node if that child has a type attribute with value warning."; } must "child::chapter[child::title='Introduction']" { description "selects the chapter children of the context node that have one or more title children with string-value equal to Introduction."; } must "child::chapter[child::title]" { description "selects the chapter children of the context node that have one or more title children."; } must "child::*[self::chapter or self::appendix]" { description "selects the chapter and appendix children of the context node."; } must "child::*[self::chapter or self::appendix][position()=last()]" { description "selects the last chapter or appendix child of the context node."; } /* must "last()-position() or last-position() or last()-position or last-position or last - position" { description "email example with invalid function name"; } */ must "self::node()/descendant-or-self::node()/child::para" { description "longhand for .//para"; } container doc { list chapter { key id; leaf id { type uint32; } leaf title { type string; } list section { key id; leaf id { type uint32; } leaf para { type string; } } } } list chapter { key id; leaf id { type uint32; } leaf title { type string; } list section { key id; leaf id { type uint32; } leaf para { type string; } } } leaf-list para { type string; } } container test2 { description "Test XPath 1.0 abbreviated syntax location path expression parsing. These must-stmts are not expected to evaluate to true. Taken from XPath 1.0, section 2.5"; must "para" { description "selects the para element children of the context node."; } must "*" { description "selects all element children of the context node."; } must "text()" { description "selects all text node children of the context node."; } must "@name" { description "selects the name attribute of the context node."; } must "@*" { description "selects all the attributes of the context node."; } must "para[1]" { description "selects the first para child of the context node."; } must "para[last()]" { description "selects the last para child of the context node."; } must "*/para" { description "selects all para grandchildren of the context node."; } must "/doc/chapter[5]/section[2]" { description "selects the second section of the fifth chapter of the doc."; } must "chapter//para" { description "selects the para element descendants of the chapter element children of the context node."; } must "//para" { description "selects all the para descendants of the document root and thus selects all para elements in the same document as the context node."; } must "//olist/item" { description "selects all the item elements in the same document as the context node that have an olist parent."; } must "." { description "selects the context node"; } must ".//para" { description "selects the para element descendants of the context node."; } must ".." { description "selects the parent of the context node."; } must "../@lang" { description "selects the lang attribute of the parent of the context node."; } must "para[@type='warning']" { description "selects all para children of the context node that have a type attribute with value warning."; } must "para[@type='warning'][5]" { description "selects the fifth para child of the context node that has a type attribute with value warning."; } must "para[5][@type='warning']" { description "selects the fifth para child of the context node if that child has a type attribute with value warning."; } must "chapter[title='Introduction']" { description "selects the chapter children of the context node that have one or more title children with string-value equal to Introduction."; } must "chapter[title]" { description "selects the chapter children of the context node that have one or more title children."; } must "employee[@secretary and @assistant]" { description "selects all the employee children of the context node that have both a secretary attribute and an assistant attribute."; } must ".//para" { description "shorthand for self::node()/descendant-or-self::node()/child::para"; } container doc { list chapter { key id; leaf id { type uint32; } leaf title { type string; } list section { key id; leaf id { type uint32; } leaf para { type string; } } } } list chapter { key id; leaf id { type uint32; } leaf title { type string; } list section { key id; leaf id { type uint32; } leaf para { type string; } } } leaf-list para { type string; } } /* leaf test-bad-path-syntax { description "Test XPath 1.0 location path expression parsing. These must-stmts are not expected to evaluate to true. Taken from XPath 1.0, section 2"; type string; must "child::foo:para"; must "child::**"; must "3 or 4 or 7 7 or or and 4"; must "grandma::foo and (17 div current())"; must (18)(+42); must ((18)+42); must 18+42-foo; must foo-18+9+1+; // yangdump bug; unquoted strings must /..; must "//"; must "///"; must "...."; } */ leaf A { type int64; } leaf B { type int32; } container test4 { must "a/b/c + ../../A | /B"; must "//B = 4"; must ".//AA"; must "//foo[@x = 1]"; // causing missing prefix error // must "//foo:*[@x = 1]"; must ".//foo/bar[x='fred'] or .=10"; must "*[.=1 or . = 'fred']"; must "*[.=1 or . = 'fred'][1]"; must "*[.=1 or . = 'fred'][last()]"; must "/a/b | /c/d"; container C { container D { leaf AA { type string; } } } } container doc { list chapter { key id; leaf id { type uint32; } list section { key id; leaf id { type uint32; } leaf para { type string; } } } } rpc rpc-must1 { input { leaf a { type int32; } leaf b { type string; must "../a > 10"; } } output { leaf x { type int32; } leaf y { type string; must "../x != 10"; } } } notification notif-must1 { leaf a { type int32; } leaf b { type string; must "../a > 10 and starts-with(../c/d, 'foo')"; } container c { leaf d { type string; } } } } yuma123_2.14/netconf/modules/test/pass/testrev2.yang0000664000175000017500000000122514770023131022612 0ustar vladimirvladimirmodule testrev2 { namespace "http://netconfcentral.org/ns/testrev2"; prefix "trev2"; import testr1 { prefix r1; revision-date 2009-02-02; } import testr2 { prefix r2; revision-date 2008-01-01; } organization "Netconf Central"; contact "Andy Bierman"; description "YANG import-by-revision test module 2."; revision 2010-06-10 { description "Initial version."; } container foo { uses r1:TestGroup1; } container bar { uses r2:TestGroup; } leaf baz { type r2:TestType { range 2..max; } } } yuma123_2.14/netconf/modules/test/pass/siltest7.yang0000664000175000017500000000064114770023131022613 0ustar vladimirvladimirmodule siltest7 { namespace "http://netconfcentral.org/ns/siltest7"; prefix "siltest7"; revision 2011-12-12 { description "Initial revision. Check SIL callback order for various operations."; } container top7con { leaf leaf1 { type uint32; } } list top7list { key key; leaf key { type uint32; } leaf v1 { type int32; } } } yuma123_2.14/netconf/modules/test/pass/testgrp.yang0000664000175000017500000000056014770023131022525 0ustar vladimirvladimirmodule testgrp { namespace "http://netconfcentral.org/ns/testgrp"; prefix "tgrp"; revision 2010-05-27 { description "Initial revision."; } grouping testgrp { list a { key g1; leaf g1 { type string; } leaf g2 { type string; } } } rpc foo { input { leaf bar { type int32; } } } } yuma123_2.14/netconf/modules/test/pass/testr1@2009-02-02.yang0000664000175000017500000000143314770023131023330 0ustar vladimirvladimirmodule testr1 { namespace "http://netconfcentral.org/ns/testr1"; prefix "r1"; import testr2 { prefix r2; revision-date 2009-01-01; } organization "Netconf Central"; contact "Andy Bierman"; description "YANG import-by-revision test module."; revision 2009-02-02 { description "Initial version."; } grouping TestGroup1 { leaf X { type string; } leaf Y { type leafref { path "../X"; } } uses r2:TestGroup { augment E { when "../Y = 'fred'"; container Z { presence "exists"; } } } } container foo-r2 { uses r2:TestGroup; } container goo-local { uses r1:TestGroup1; } } yuma123_2.14/netconf/modules/test/pass/when_bug.yang0000664000175000017500000000153214770023131022633 0ustar vladimirvladimirmodule when_bug { yang-version 1; namespace "http://www.when_bug.com/"; prefix wbug; import iana-if-type { prefix ianaift; } organization "Augment When Bug"; contact "augment@bug.com"; description "Example file to find the augment bug."; revision "2012-06-14" { description "Initial revision."; } container when_bug { list interface { key "name"; leaf name { type string; } leaf type { type ianaift:iana-if-type; mandatory true; } } // list interface } // container when_bug augment "/when_bug/interface" { when "type = 'ethernetCsmacd'"; leaf when_b { type boolean; default 'false'; } leaf when_i { type int32; } container cont { leaf eth { type int32; } } // container cont } } yuma123_2.14/netconf/modules/test/pass/t31.yang0000664000175000017500000000306414770023131021446 0ustar vladimirvladimirmodule t31 { namespace "http://netconfcentral.org/ns/t31"; prefix "t31"; description "Test SIL code generation for YANG features"; revision 2012-02-05; feature t31-1; feature t31-2; feature t31-3 { if-feature t31-2; } feature t31-4; feature t31-5; list t31-list { if-feature t31-1; key a; leaf a { type int32; } leaf b { if-feature t31-2; type string; mandatory true; } leaf c { if-feature t31-3; type string; mandatory true; } container d { if-feature t31-1; if-feature t31-2; leaf dd { type string; } leaf ee { type string; } } } rpc t31-rpc { if-feature t31-4; input { leaf a { type string; } leaf b { if-feature t31-5; type string; } } output { leaf c { type int32; } leaf d { if-feature t31-5; type string; } } } notification t31-notif { if-feature t31-4; leaf w { type string; } leaf x { if-feature t31-5; type string; } leaf y { type int32; } leaf z { if-feature t31-5; type string; } } rpc t31-plain-rpc { input { leaf a { type string; } leaf b { type string; } } output { leaf c { type int32; } leaf d { type string; } } } notification t31-plain-notif { leaf w { type string; } leaf x { type string; } leaf y { type int32; } leaf z { type string; } } } yuma123_2.14/netconf/modules/test/pass/test5.yang0000664000175000017500000000044614770023131022104 0ustar vladimirvladimirmodule test5 { namespace "http://netconfcentral.org/ns/test5"; prefix "t5"; revision 2008-11-20 { description "Initial revision."; } container test5 { list a { key g1; leaf g1 { type string; } leaf g2 { type string; } } } } yuma123_2.14/netconf/modules/test/pass/t11.yang0000664000175000017500000000133414770023131021442 0ustar vladimirvladimirmodule t11 { namespace "http://netconfcentral.org/ns/t11"; prefix t11; revision "2011-07-04" { description "Bug report"; } typedef test-pattern { type string { pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; } } list t11-list { key "id1"; leaf id1 { type string; } leaf id2 { type int8; } } container t11-con { leaf con1 { type leafref { path "../../t11-list/id1"; } default "fred"; } } typedef t11-typ1 { type leafref { path "/t11-list/id1"; } default "freddy"; } leaf t11-leaf { type leafref { path "../t11-list/id1"; } } leaf t11-leaf2 { type leafref { path "../t11-list" + "/id1"; } } } yuma123_2.14/netconf/modules/test/pass/testfeature.yang0000664000175000017500000000207314770023131023371 0ustar vladimirvladimirmodule testfeature { namespace "http://netconfcentral.org/ns/testfeature"; prefix "tf"; revision 2010-01-29 { description "Initial revision."; } feature A { if-feature B; } feature B; rpc f1 { input { leaf a { if-feature A; type string; mandatory true; } leaf b { if-feature B; type string; mandatory true; } leaf c { type string; mandatory true; } } output { leaf o1 { if-feature A; type string; mandatory true; } leaf o2 { if-feature B; type string; mandatory true; } leaf o3 { type string; mandatory true; } } } list f2 { key c2; leaf a2 { if-feature A; type string; mandatory true; } leaf b2 { if-feature B; type string; mandatory true; } leaf c2 { type string; } } } yuma123_2.14/netconf/modules/test/pass/testcommit.yang0000664000175000017500000000507314770023131023231 0ustar vladimirvladimirmodule testcommit { namespace "http://netconfcentral.org/ns/testcommit"; prefix "testcommit"; revision 2011-12-04; container vehicle { list car { key "regno"; unique "model"; leaf regno { type string; } leaf model { type string; mandatory true; } leaf-list drivers { type string; min-elements 1; max-elements 10; } leaf owner { type leafref { path "../../../owner/last"; } } list carlist { key carid; unique "test1 con/test2"; leaf carid { type uint32; } leaf test1 { type int8; } container con { when "../../model = 'ford'"; leaf test2 { type boolean; } leaf test3 { type boolean; mandatory true; } } } } } list owner { key "last first"; leaf first { type string; } leaf last { type string; } leaf-list models { type leafref { path "../../vehicle/car/model"; } max-elements 21; min-elements 1; } choice owner-choice { leaf own1 { type string; } case own2case { choice own2 { case own3 { leaf own4 { type string; mandatory true; } leaf own5 { type string; } } list own-list { key x; leaf x { type boolean; } leaf y { type int32; mandatory true; } } } } } } leaf top-mandatory-leaf { type int32; mandatory true; } choice top-choice { case a { leaf a1 { type string; } leaf a2 { type uint32; mandatory true; } } case b { leaf-list b1 { type uint8; min-elements 2; } choice b2 { case b2a { leaf b2a1 { type string; mandatory true; } leaf b2a2 { type string; } } container b3-npcon { leaf b3-npcon-leaf { type int32; mandatory true; } } } } case c { leaf c1 { type int32; } } } } yuma123_2.14/netconf/modules/test/pass/ytest.yang0000664000175000017500000000335614770023131022213 0ustar vladimirvladimirmodule ytest { namespace "http://www.netconfcentral.org/ns/ytest"; prefix "ytest"; organization "Netconf Central"; contact "Andy Bierman"; revision 2011-01-30 { description "Code generation testing."; } rpc test19 { input { container t-2 { container inside { } } leaf-list q { type int32; } } output { container t-1 { leaf id2-id2 { description "Unique ID"; type union { type uint32; type string; } } } leaf-list z { type boolean; } } } list one { key i; leaf i { type int32; } leaf j { type string; } container two { leaf-list a { type uint32; } list objects { key id-id; leaf id-id { description "Unique ID"; type union { type uint32; type string; } } container t-1 { leaf id2-id2 { description "Unique ID"; type union { type uint32; type string; } } } } } container three { description "second sub container"; list objects { key id; leaf id { description "Unique ID"; type uint32; } } } } notification n1 { } notification n2 { container t-2 { leaf id2-id2 { type string; } } } notification n3 { container t-3 { leaf id2-id2 { description "Unique ID"; type union { type uint32; type string; } } } } rpc test17; rpc test18 { input { leaf id3 { type union { type uint32; type string; } } } output { anyxml out; } } } yuma123_2.14/netconf/modules/test/pass/testr1@2009-01-01.yang0000664000175000017500000000117714770023131023333 0ustar vladimirvladimirmodule testr1 { namespace "http://netconfcentral.org/ns/testr1"; prefix "r1"; import testr2 { prefix r2; revision-date 2008-01-01; } organization "Netconf Central"; contact "Andy Bierman"; description "YANG import-by-revision test module."; revision 2009-01-01 { description "Initial version."; } grouping TestGroup1 { leaf X { type string; } leaf Y { type leafref { path "../X"; } } uses r2:TestGroup; } container bar-r1 { uses r2:TestGroup; } container foo-r2 { uses r2:TestGroup; } } yuma123_2.14/netconf/modules/test/pass/testdev_dev.yang0000664000175000017500000000136514770023131023355 0ustar vladimirvladimirmodule testdev_dev { namespace "http://netconfcentral.org/ns/testdev_dev"; prefix "td_dev"; import testdev { prefix tdev; } import test { prefix test; } revision 2009-08-02 { description "Initial revision."; } deviation /tdev:test-top/tdev:b { deviate replace { type uint32; } deviate replace { mandatory false; } } deviation /tdev:test-top/tdev:a { deviate replace { units minutes; } deviate add { default 30; } } deviation /test:test1/test:d { deviate add { default y; } } deviation /test:test1/test:e/test:ee { deviate replace { max-elements 10; } } } yuma123_2.14/netconf/modules/test/pass/t30.yang0000664000175000017500000000112714770023131021443 0ustar vladimirvladimirmodule t30 { namespace "http://netconfcentral.org/ns/t30"; prefix "t30"; revision 2012-02-04; leaf t30-a { type int32; } leaf t30-b { type int32; } leaf t30-c { type int32; } list t30-list { key "x"; leaf x { type int32; } leaf-list y { type int32; } container z { when "../x > 4 and /t30-a > 3"; must "../y = ../../t30-a"; leaf a { type uint8; mandatory true; } leaf b { when "/t30-b = /t30-c"; mandatory true; type boolean; } } } } yuma123_2.14/netconf/modules/test/pass/siltest5.yang0000664000175000017500000000226514770023131022615 0ustar vladimirvladimirmodule siltest5 { namespace "http://netconfcentral.org/ns/siltest5"; prefix "siltest5"; import yuma-ncx { prefix ncx; } description "Test SIL callback behavior for YANG features."; revision 2011-10-08 { description "Initial version."; } feature f1; feature f2; feature f3 { if-feature f1; } leaf c5-leaf { if-feature f1; type string; } leaf-list c5-leaf-list { if-feature f2; type string; } list c5-list { key a; leaf a { type string; } leaf b { type leafref { path "../../c5-leaf"; } } } list c5a-list { if-feature f2; ncx:sil-delete-children-first; // applies here key a; leaf a { type string; } list cc5 { ncx:sil-delete-children-first; // applies here key "key1 key2"; leaf key1 { type uint32; } leaf key2 { type int32; } leaf yy { if-feature f3; type int32; } leaf zz { if-feature f3; type uint64; config false; } } } } yuma123_2.14/netconf/modules/test/pass/testmalloc.yang0000664000175000017500000000043514770023131023205 0ustar vladimirvladimirmodule testmalloc { namespace "http://netconfcentral.org/ns/testmalloc"; prefix "tm"; revision 2008-10-15 { description "Initial revision."; } leaf leafref1 { type leafref { path "../foo"; } } leaf foo { type int32; } } yuma123_2.14/netconf/modules/test/pass/testdec.yang0000664000175000017500000000061714770023131022473 0ustar vladimirvladimirmodule testdec { namespace "http://netconfcentral.org/ns/testdec"; prefix "tdec"; revision 2011-03-03 { description "Initial revision."; } leaf dec1 { type decimal64 { fraction-digits 1; range "0.5 .. 30"; } default 1.5; } leaf dec2 { type decimal64 { fraction-digits 8; } default 1.00033; } } yuma123_2.14/netconf/modules/test/pass/test8.yang0000664000175000017500000000031414770023131022101 0ustar vladimirvladimirmodule test8 { namespace "http://netconfcentral.org/ns/test8"; prefix "t8"; include test8a; include test8b; revision 2011-02-04 { description "Initial revision."; } } yuma123_2.14/netconf/modules/test/pass/test9.yang0000664000175000017500000000136214770023131022106 0ustar vladimirvladimirmodule test9 { namespace "http://netconfcentral.org/ns/test9"; prefix "t9"; revision 2010-12-11 { description "Initial revision."; } container XX { uses g3; } grouping g3 { uses g2; } grouping g2 { uses g1; } grouping g1 { leaf a { type string; } } list L { config false; uses g3; } container C { grouping gg { leaf bb { type string; } } grouping gg3 { uses gg2; uses gg; } grouping gg2 { leaf a { type uint32; } uses gg1; } grouping gg1 { leaf cc { type string; } } list L { config false; uses gg3; } } } yuma123_2.14/netconf/modules/test/pass/testr2@2009-01-01.yang0000664000175000017500000000135314770023131023330 0ustar vladimirvladimirmodule testr2 { namespace "http://netconfcentral.org/ns/testr2"; prefix "r2"; organization "Netconf Central"; contact "Andy Bierman"; description "YANG import-by-revision test module."; revision 2009-01-01 { description "Add C and D to the test grouping"; } revision 2008-01-01 { description "Initial version."; } typedef TestType { type int32 { range 1..max; } default 5; units seconds; } grouping TestGroup { leaf A { type int64; } leaf B { type instance-identifier; } leaf C { type string; } anyxml D; container E; } container bar-r2 { uses r2:TestGroup; } } yuma123_2.14/netconf/modules/test/pass/test3b.yang0000664000175000017500000000063714770023131022246 0ustar vladimirvladimirmodule test3b { namespace "http://netconfcentral.org/ns/test3b"; prefix "t3"; revision 2009-09-03 { description "Initial revision."; } container test-D1 { leaf test-D { type local-D; } typedef local-D { type int32 { range "1 .. 4 | 7"; } } } leaf test-D2 { type int32 { range "1 .. 4 | 7"; } } } yuma123_2.14/netconf/modules/test/pass/test.yang0000664000175000017500000001636214770023131022023 0ustar vladimirvladimirmodule test { namespace "http://netconfcentral.org/ns/test"; prefix "t"; organization "Netconf Central"; contact "Andy Bierman"; description "YANG test module 1."; reference // testing "RFC 1234; RFC 3456. more text draft-ietf-netmod-yang-09.txt "; revision 2009-12-26 { description "Test module in-progress."; } feature feature1; feature feature2; feature feature3; feature feature4; container test1 { leaf a { type string { length 1..max; } default "1"; } leaf b { type uint32 { range min..32; } } leaf c { type binary; } leaf d { type enumeration { enum x; enum y; enum z; } // mandatory true; } leaf noconfig { type string; config false; } container e { leaf-list ee { ordered-by user; // min-elements 2; max-elements 5; type string { pattern [a-e][1-9]+; } } leaf eee { type string; default "test"; } } choice f { // mandatory true; leaf f1 { type string; } leaf f2 { type string; } } choice g { mandatory false; leaf g1 { type string; mandatory true; } leaf g2 { type string; default "agent-set"; } case g3 { leaf g3a { type string; mandatory true; } leaf g3b { type string; } } } } list test2 { key a2; ordered-by user; unique b2; leaf a1 { type int8; default 4; } leaf a2 { type string { length 1..5 { error-app-tag leaf-a2-error; error-message 'leaf a2 is invalid'; } } } leaf b2 { type uint32; default 5; } } augment /test2 { container xxx; leaf foo { type string; mandatory true; } } list test3 { key x; list test3deep { key "t:yy t:y"; leaf b { type leafref { path "../../x"; } } leaf bb { type leafref { path "../yy"; } } leaf bbb { type leafref { path "../deepy/yy1"; } } leaf c { type leafref { path "../../../test3[x = current()/../b]/test3deep [yy=current()/../bb][y=current()/../bbb]/deepy/yy3"; } } leaf y { type string; } list deepy { config true; key yy3; leaf yy1 { type instance-identifier; } leaf yy2 { type int64; } leaf yy3 { type string; } } list deepz { key zz3; leaf zz1 { type instance-identifier; } leaf zz2 { type int64; } leaf zz3 { type int32; } } leaf yy { type int32; } leaf yy64 { type int64; } } leaf b2 { type uint32; default 5; } leaf-list c2 { type int32; min-elements 4; max-elements 4; } leaf x { type string; } } identity id-A; identity id-B; identity id-C { base id-A; } identity id-D { base id-C; } identity id-E { base id-D; } leaf idtest { type identityref { base id-A; } } container musttest { presence "testing one"; leaf A { type string; } choice B { mandatory true; case B1 { leaf B1leaf { type string; } leaf B1num { type int32; mandatory true; } } case B2 { leaf B2leaf { type string; } } } } grouping Leafs.1 { anyxml anyxml.1; leaf binary.1 { type binary; } leaf bits.1 { type bits { bit zero; bit one; bit two; bit ten { position 10; } bit last; } } leaf boolean.1 { type boolean; } /* this will be added in yang-05 * leaf decimal64.1 { * type decimal64 { fraction-digits 3; } * } */ leaf empty.1 { type empty; } leaf enumeration.1 { type enumeration { enum red { value 5; } enum green { value 9; } enum blue { value 11; } enum 'deep purple' { value 17; } } } leaf identityref.1 { type identityref { base id-A; } } leaf instance-identifier.1 { type instance-identifier { require-instance false; } } leaf instance-identifier.2 { type instance-identifier { require-instance true; } } leaf int8.1 { type int8; } leaf int16.1 { type int16; } leaf int32.1 { type int32; } leaf int64.1 { type int64; } leaf leafref.1 { type leafref { path "../instance-identifier.1"; } } leaf leafref.2 { type leafref { path "../string.1"; } } leaf string.1 { type string; } leaf uint8.1 { type uint8; } leaf uint16.1 { type uint16; } leaf uint32.1 { type uint32; } leaf uint64.1 { type uint64; } leaf dec64.1 { type decimal64 { fraction-digits 4; } } leaf dec64.2 { type decimal64 { fraction-digits 2; range "0 .. 3 | 9 .. max"; } } leaf dec64.3 { type decimal64 { fraction-digits 18; range "3.1234567 .. max"; } } leaf union.1 { type union { type int8; type binary; type string; type union { type boolean; type uint64; } } } } leaf instance.2 { type instance-identifier; } leaf instance.3 { type instance-identifier { require-instance false; } } leaf leafref.3 { type leafref { path "../test2/a2"; } } uses Leafs.1; container container.1 { uses Leafs.1; } container container.2 { presence "this is a presence container"; uses Leafs.1; } list list.1 { key "string.1 uint32.1"; uses Leafs.1; } choice choice.1 { case case.1.1 { leaf a { type string; } leaf b { type string; mandatory true; } } leaf c { type string; mandatory true; } leaf d { type int8; } } container xpath.1 { // must "name != 'fred' or data2"; // must "name != name2 or data2"; leaf name { when "../data1 >= 4"; type string; } leaf name2 { type string; } leaf data1 { type uint32; } leaf data2 { when "../data1 < 4"; mandatory true; type uint32; } leaf data3 { if-feature t:feature1; type string; default foo:bar; } leaf data4 { if-feature t:feature2; when "../data1 > 4"; mandatory true; type string; } } rpc test-rpc { input { leaf a { type string; mandatory true; } leaf b { type string; default '"'; } } } } yuma123_2.14/netconf/modules/test/pass/augment_interface.yang0000664000175000017500000000114214770023131024512 0ustar vladimirvladimirmodule augment_interface { yang-version 1; namespace "http://www.augment_interface.com/"; prefix aintf; import iana-if-type { prefix ianaift; } organization "Augment"; contact "augment@bug.com"; description "Example file to find the augment bug."; revision "2012-06-14" { description "Initial revision."; } container augment_interface { list interface { key "name"; leaf name { type string; } leaf type { type ianaift:iana-if-type; mandatory true; } } // list interface } // container augment_interface } yuma123_2.14/netconf/modules/test/pass/test2.yang0000664000175000017500000000207414770023131022100 0ustar vladimirvladimirmodule test2 { namespace "http://netconfcentral.org/ns/test2"; prefix "t2"; import test1 { prefix t1; } revision 2008-10-15 { description "Initial revision."; } typedef XX { type string { pattern "a*"; } } typedef YY { type string { pattern "b*"; pattern "b7*"; } } typedef ZZ { type YY { pattern "b7b*"; pattern "b7b7*"; } } typedef ZZZ { type t1:AA { pattern "b7b*"; pattern "b7b7*"; } } leaf a { type int32 { range "2 .. 12"; } } leaf b { type string { length "1 .. 10"; pattern "1-9[0-9]*" { error-app-tag "error-test 2, leaf b"; } } } leaf c { description "Only 5151 should pass the AND pattern test"; type string { length "1 .. 10"; pattern "1-9[0-9]*" { error-message "error-test 2, leaf c"; } pattern "51*"; pattern "[1-9]{4}"; } } } yuma123_2.14/netconf/modules/test/pass/testuses.yang0000664000175000017500000000222714770023131022716 0ustar vladimirvladimirmodule testuses { namespace "http://netconfcentral.org/ns/testuses"; prefix "tu"; organization "Netconf Central"; contact "Andy Bierman"; description "YANG uses-stmt test module."; revision 2008-12-17 { description "Initial version."; } container foo { uses top-choice; } container bar { uses top2-choice; } grouping top2-choice { uses top-choice { augment config-source { leaf url { description "URL pointing to config data. Only available if 'url' capability supported."; type string; // } } } } grouping top-choice { choice config-source { mandatory true; leaf candidate { description "Only available if 'candidate' capability supported."; type empty; } leaf running { type empty; } leaf startup { description "Only available if 'startup' capability supported."; type empty; } } } } yuma123_2.14/netconf/modules/test/pass/examples.yang0000664000175000017500000002144514770023131022660 0ustar vladimirvladimirmodule examples { namespace "http://netconfcentral.org/ns/examples"; prefix "ex"; import ietf-inet-types { prefix inet; } organization "Netconf Central"; contact "Andy Bierman"; description "Some YANG draft examples."; revision 2008-11-09 { description "Initial version."; } container system { leaf host-name { type string; description "Hostname for this system"; } leaf-list domain-search { type string; description "List of domain names to search"; } container login { leaf message { type string; description "Message given at start of login session"; } list user { key "name"; leaf name { type string; } leaf full-name { type string; } leaf class { type string; } } } container services { description "Configure externally available services"; container "ssh" { presence "Enables SSH"; description "SSH service specific configuration"; // more leafs, containers and stuff here... } } list server { key "name"; unique "ip port"; leaf name { type string; } leaf ip { type inet:ip-address; } leaf port { type inet:port-number; } } } leaf mgmt-interface { type leafref { path "../interfaces/interface/name"; } } container default-address { leaf ifname { type leafref { path "../../interfaces/interface/name"; } } leaf address { type leafref { path "../../interfaces/interface[name = current()/../ifname]" + "/address/ip"; } } } container interfaces { list interface { must "ifType != 'ethernet' or " + "(ifType = 'ethernet' and ifMTU = 1500)" { error-message "An ethernet MTU must be 1500"; } must "ifType != 'atm' or " + "(ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)" { error-message "An atm MTU must be 64 .. 17966"; } key "name"; config true; leaf name { type string; } leaf type { type string; } leaf speed { type enumeration { enum 10m; enum 100m; enum auto; } } leaf observed-speed { type uint32; config false; } list address { key "ip"; leaf ip { type inet:ip-address; } } leaf ifType { type enumeration { enum ethernet; enum atm; } } leaf ifMTU { type uint32; } } } typedef percent { type uint16 { range "0 .. 100"; } description "Percentage"; } leaf completed { type percent; } grouping target { leaf address { type inet:ip-address; description "Target IP address"; } leaf port { type inet:port-number; description "Target port number"; } } container peer { container destination { uses target; } } /* container connection { container source { uses target { refine "address" { description "Source IP address"; } refine "port" { description "Source port number"; } } } container destination { uses target { refine "address" { description "Destination IP address"; } refine "port" { description "Destination port number"; } } } } */ container food { choice snack { mandatory true; case sports-arena { leaf pretzel { type empty; } leaf beer { type empty; } } case late-night { leaf chocolate { type enumeration { enum dark; enum milk; enum first-available; } } } } } augment /system/login/user { when "class != 'wheel'"; leaf uid { type uint16 { range "1000 .. 30000"; } } } rpc activate-software-image { input { leaf image-name { type string; } } output { leaf status { type string; } } } notification link-failure { description "A link failure has been detected"; leaf if-name { type leafref { path "/interfaces/interface/name"; } } leaf if-admin-status { type string; // ifAdminStatus; } } container transfer { choice how { default interval; case interval { leaf interval { type uint16; default 30; units minutes; } } case daily { leaf daily { type empty; } leaf time-of-day { type string; units 24-hour-clock; default 1am; } } case manual { leaf manual { type empty; } } } } container protocol { choice name { case a { leaf udp { type empty; } } case b { leaf tcp { type empty; } } } } grouping address { description "A reusable address group."; leaf ip { type inet:ip-address; } leaf port { type inet:port-number; } } container http-server { leaf name { type string; } uses ex:address { refine port { default 80; } } } list server { if-feature ex:local-storage; key "ip port"; leaf name { type string; } uses ex:address; } rpc rock-the-house { input { leaf zip-code { type string; } } } notification event { leaf event-class { type string; } anyxml reporting-entity; leaf severity { if-feature local-storage; type string; } } // not in draft typedef ChannelNumber { type int32 { range 0..max; } } augment "/ex:interfaces/ex:interface" { when "ex:type='ds0'"; leaf ds0ChannelNumber { type ChannelNumber; } } identity crypto-alg { description "Base identity from which all crypto algorithms are derived."; } identity des { base "ex:crypto-alg"; description "DES crypto algorithm"; } identity des3 { base "ex:crypto-alg"; description "Triple DES crypto algorithm"; } leaf crypto { type identityref { base "ex:crypto-alg"; } } feature local-storage { description "This feature means the device supports local storage (memory, flash or disk) that can be used to store syslog messages."; reference "yang-02"; status current; } container syslog { leaf local-storage-limit { if-feature local-storage; config false; description "The amount of local storage that can be used to hold syslog messages."; // !! not in the draft !! type uint32; units bytes; } } } yuma123_2.14/netconf/modules/test/pass/augment_bug.yang0000664000175000017500000000117714770023131023337 0ustar vladimirvladimirmodule augment_bug { yang-version 1; namespace "http://www.augment_bug.com/"; prefix abug; import augment_interface { prefix aintf; } organization "Augment"; contact "augment@bug.com"; description "Example file to find the augment bug."; revision "2012-06-14" { description "Initial revision."; } augment "/aintf:augment_interface/aintf:interface" { when "aintf:type = 'ethernetCsmacd'"; leaf when_b { type boolean; default 'false'; } leaf when_i { type int32; } container cont { leaf eth { type int32; } } // container cont } } yuma123_2.14/netconf/modules/test/pass/testsim.yang0000664000175000017500000000135514770023131022530 0ustar vladimirvladimirmodule testsim { namespace "http://netconfcentral.org/ns/testsim"; prefix "sim"; description "simple data structures for compiler testing"; revision 2011-06-27 { description "Initial revision."; } container sim1 { leaf-list aa { type int16; max-elements 5; } } list sim2 { key x; leaf x { type uint16; } leaf y { type uint32; } leaf z { type string; } } container sim3 { list sim4 { key "xx yy"; leaf xx { type uint8; } leaf yy { type uint64; } leaf zz { type leafref { path /sim:sim2/sim:y; } } } } leaf sim5 { when "/sim:sim1/sim:aa = 10"; type string; } } yuma123_2.14/netconf/modules/test/pass/test7.yang0000664000175000017500000000141014770023131022076 0ustar vladimirvladimirmodule test7 { namespace "http://netconfcentral.org/ns/test7"; prefix "t7"; revision 2009-10-23 { description "Initial revision."; } extension X { description "test extension"; argument "test-parm"; } container test-ext { presence "not a top-level mandatory container"; t7:X test-string { list test2 { key a2; ordered-by user; unique b2; leaf a1 { type int8; default 4; } leaf a2 { type string { length 1..5 { error-app-tag leaf-a2-error; error-message 'leaf a2 is invalid'; } } } leaf b2 { type uint32; default 5; } } } } leaf next { type string; } } yuma123_2.14/netconf/modules/test/pass/testfalse.yang0000664000175000017500000000076614770023131023037 0ustar vladimirvladimirmodule testfalse { namespace "http://www.netconfcentral.org/ns/testfalse"; prefix "testfalse"; revision 2011-03-30 { description "Bug Report. (???) "; } container emptynp1; container emptynp2 { leaf del { type string; default "fred"; } } container monitor { config false; leaf node { type string; } leaf comment { type string; } leaf starts-with { type string; } leaf preceding { type string; } } } yuma123_2.14/netconf/modules/test/fail/0000775000175000017500000000000014770023131020121 5ustar vladimirvladimiryuma123_2.14/netconf/modules/test/fail/testbadext.yang0000664000175000017500000000035414770023131023152 0ustar vladimirvladimirmodule testbadext { namespace "http://netconfcentral.org/ns/testbadext"; prefix "tbe"; import foobar { prefix xxxx; } include garbage; revision 2011-02-28 { description "Initial revision."; } } yuma123_2.14/netconf/modules/test/fail/testnest.yang0000664000175000017500000000045314770023131022654 0ustar vladimirvladimirsubmodule testnest { belongs-to testnesttop { prefix tntop; } revision 2010-08-21 { description "Initial revision."; } grouping one { leaf two { type string; } } feature A; identity B; typedef C { type string; } leaf D { type uint32; } extension E; } yuma123_2.14/netconf/modules/test/fail/testnesttop.yang0000664000175000017500000000033614770023131023377 0ustar vladimirvladimirmodule testnesttop { namespace "http://netconfcentral.org/ns/testnesttop"; prefix "tntop"; include testnest; include testnest2; revision 2010-08-20 { description "Initial revision."; } } yuma123_2.14/netconf/modules/test/fail/testloops.yang0000664000175000017500000000203614770023131023036 0ustar vladimirvladimirmodule testloops { namespace "http://netconfcentral.org/ns/testloops"; prefix "tl"; import testloops { prefix testloops; } include testloops; revision 2009-03-03 { description "Initial revision."; } leaf LR1 { type leafref { path "../LR2"; } } leaf LR2 { type leafref { path "../LR3"; } } leaf LR3 { type leafref { path "../LR1"; } } typedef typeloop { type typeloop; } typedef typeloop2 { type typeloop3; } typedef typeloop3 { type typeloop4; } typedef typeloop4 { type typeloop2; } grouping grouploop { uses grouploop; } grouping grouploop2 { uses grouploop3; } grouping grouploop3 { uses grouploop4; } grouping grouploop4 { uses grouploop2; } feature A { if-feature B; } feature B { if-feature C; } feature C { if-feature A; } feature D { if-feature B; } feature E; } yuma123_2.14/netconf/modules/test/fail/testunion.yang0000664000175000017500000000043014770023131023026 0ustar vladimirvladimirmodule testunion { namespace "http://www.netconfcentral.org/ns/testunion"; prefix "testunion"; revision 2011-03-08 { description "Initial version."; } leaf t1 { type union { type foo; type uint32; } } typedef foo { type empty; } } yuma123_2.14/netconf/modules/test/fail/testnest3.yang0000664000175000017500000000032314770023131022733 0ustar vladimirvladimirsubmodule testnest3 { // added extra line belongs-to testnesttop { prefix tntop; } revision 2010-09-25 { description "Initial revision."; } leaf x { type string; } feature A; } yuma123_2.14/netconf/modules/test/fail/testmust2.yang0000664000175000017500000000065714770023131022763 0ustar vladimirvladimirmodule testmust2 { namespace "http://netconfcentral.org/ns/testmust2"; prefix "tm2"; organization "Netconf Central"; contact "Andy Bierman"; description "YANG uses-stmt test module."; revision 2008-12-18 { description "Initial version."; } leaf test-abbreviated-path-parsing { type string; must "grandma::foo and (17 div current())"; } } yuma123_2.14/netconf/modules/test/fail/testnest2.yang0000664000175000017500000000054014770023131022733 0ustar vladimirvladimirsubmodule testnest2 { // added extra line belongs-to testnesttop { prefix tntop; } include testnest3; revision 2010-07-12 { description "Initial revision."; } grouping one { leaf two { type string; } } feature A; identity B; typedef C { type string; } leaf D { type uint32; } extension E; } yuma123_2.14/netconf/modules/test/fail/t13.yang0000664000175000017500000000123214770023131021406 0ustar vladimirvladimirmodule t13 { namespace "http://netconfcentral.org/ns/t13"; prefix t13; revision "2011-09-11" { description "mandatory / default test"; } leaf t13-leaf { type uint32; mandatory true; default 11; } grouping g1 { leaf t13-leaf2 { type uint32; default 11; } } grouping g2 { leaf t13-leaf3 { type uint32; mandatory true; } } uses g1 { refine t13-leaf2 { mandatory true; } } uses g2 { refine t13-leaf3 { default 11; } } container c { uses g2 { refine t13-leaf3 { default 11; mandatory false; } } } } yuma123_2.14/netconf/modules/test/fail/testimp.yang0000664000175000017500000000034714770023131022472 0ustar vladimirvladimirmodule testimp { namespace "http://netconfcentral.org/ns/testimp"; prefix "timp"; // include testnest; import testnest { prefix bogus; } revision 2010-09-27 { description "Initial revision."; } } yuma123_2.14/netconf/modules/test/fail/test1badns.yang0000664000175000017500000000050614770023131023052 0ustar vladimirvladimirmodule test1badns { prefix "t1"; include test1a; include test1b; include test1c; revision 2008-10-12 { description "Initial revision."; } typedef AA { type string { pattern ".*"; } } leaf t22 { type boolean; } leaf t23 { type string; config false; } } yuma123_2.14/netconf/modules/ietf-derived/0000775000175000017500000000000014770023131020576 5ustar vladimirvladimiryuma123_2.14/netconf/modules/ietf-derived/notifications.yang0000664000175000017500000000547514770023131024342 0ustar vladimirvladimirmodule notifications { namespace "urn:ietf:params:xml:ns:netconf:notification:1.0"; prefix "ncEvent"; import ietf-yang-types { prefix yang; } import yuma-ncx { prefix ncx; } organization "IETF NETCONF WG"; contact "netconf@ops.ietf.org"; description "Conversion of the 'ncEvent' XSD in the NETCONF Notifications RFC."; reference "RFC 5277."; revision 2008-07-14 { description "RFC 5277 version."; } typedef streamNameType { description "The name of an event stream."; type string; } rpc create-subscription { description "The command to create a notification subscription. It takes as argument the name of the notification stream and filter. Both of those options limit the content of the subscription. In addition, there are two time-related parameters, startTime and stopTime, which can be used to select the time interval of interest to the notification replay feature."; input { leaf stream { description "An optional parameter that indicates which stream of events is of interest. If not present, then events in the default NETCONF stream will be sent."; type streamNameType; default "NETCONF"; } anyxml filter { description "An optional parameter that indicates which subset of all possible events is of interest. The format of this parameter is the same as that of the filter parameter in the NETCONF protocol operations. If not present, all events not precluded by other parameters will be sent."; } leaf startTime { description "A parameter used to trigger the replay feature and indicates that the replay should start at the time specified. If start time is not present, this is not a replay subscription."; type yang:date-and-time; } leaf stopTime { // must ". >= ../startTime"; description "An optional parameter used with the optional replay feature to indicate the newest notifications of interest. If stop time is not present, the notifications will continue until the subscription is terminated. Must be used with startTime."; type yang:date-and-time; } } } container notification { description "internal struct to start a notification"; ncx:abstract; ncx:hidden; config false; leaf eventTime { mandatory true; type yang:date-and-time; } // eventType and any data content goes here } } yuma123_2.14/netconf/modules/ietf-derived/nc-notifications.yang0000664000175000017500000000532014770023131024725 0ustar vladimirvladimirmodule nc-notifications { namespace "urn:ietf:params:xml:ns:netmod:notification"; prefix "manageEvent"; import ietf-yang-types{ prefix yang; } import notifications { prefix ncEvent; } organization "IETF NETCONF WG"; contact "netconf@ietf.org"; description "Conversion of the 'manageEvent' XSD in the NETCONF Notifications RFC."; reference "RFC 5277"; revision 2008-07-14 { description "RFC 5277 version."; } container netconf { description "Top-level element in the notification namespace"; config false; container streams { description "The list of event streams supported by the system. When a query is issued, the returned set of streams is determined based on user privileges."; list stream { description "Stream name, description and other information."; key name; min-elements 1; leaf name { description "The name of the event stream. If this is the default NETCONF stream, this must have the value 'NETCONF'."; type ncEvent:streamNameType; } leaf description { description "A description of the event stream, including such information as the type of events that are sent over this stream."; type string; mandatory true; } leaf replaySupport { description "A description of the event stream, including such information as the type of events that are sent over this stream."; type boolean; mandatory true; } leaf replayLogCreationTime { description "The timestamp of the creation of the log used to support the replay function on this stream. Note that this might be earlier then the earliest available notification in the log. This object is updated if the log resets for some reason. This object MUST be present if replay is supported."; type yang:date-and-time; // xsd:dateTime is wrong! } } } } notification replayComplete { description "This notification is sent to signal the end of a replay portion of a subscription."; } notification notificationComplete { description "This notification is sent to signal the end of a notification subscription. It is sent in the case that stopTime was specified during the creation of the subscription.."; } } yuma123_2.14/netconf/systemd/0000775000175000017500000000000014770023131016247 5ustar vladimirvladimiryuma123_2.14/netconf/systemd/startup-cfg.xml0000664000175000017500000000014114770023131021224 0ustar vladimirvladimir yuma123_2.14/netconf/systemd/debian/0000775000175000017500000000000014770023131017471 5ustar vladimirvladimiryuma123_2.14/netconf/systemd/debian/source/0000775000175000017500000000000014770023131020771 5ustar vladimirvladimiryuma123_2.14/netconf/systemd/debian/source/format0000664000175000017500000000001414770023131022177 0ustar vladimirvladimir3.0 (quilt) yuma123_2.14/netconf/systemd/debian/control0000664000175000017500000000177214770023131021103 0ustar vladimirvladimirSource: yuma123-systemd-services Section: net Priority: optional Maintainer: Vladimir Vassilev Build-Depends: debhelper (>= 10) Standards-Version: 4.1.4 Homepage: http://yuma123.org Package: netconfd-service Section: net Architecture: all Depends: ${misc:Depends} Multi-Arch: foreign Description: Init scripts and systemd service definitions for starting netconfd listening on /tmp/ncxserver.sock . SSH and TLS services with compatible NETCONF subsystems can be installed to enable incomming sessions to be forwarded to /tmp/ncxserver.sock Package: netconfd-openssh-server-service Section: net Architecture: all Depends: ${misc:Depends}, openssh-server, netconfd-service Multi-Arch: foreign Provides: netconfd-ssh-service Description: Init scripts and systemd service definitions for starting openssh-server sshd instance listening on the NETCONF port 830 with only the netconf subsystem enabled. . Edit the /etc/netconfd/ssh/sshd_config to modify the configuration. yuma123_2.14/netconf/systemd/debian/rules0000775000175000017500000000033714770023131020554 0ustar vladimirvladimir#!/usr/bin/make -f %: dh $@ override_dh_systemd_enable: dh_systemd_enable -pnetconfd-service --name netconfd netconfd.service dh_systemd_enable -pnetconfd-openssh-server-service --name netconfd-ssh netconfd-ssh.service yuma123_2.14/netconf/systemd/debian/netconfd-service.install0000664000175000017500000000007514770023131024321 0ustar vladimirvladimirdebian/tmp/var/lib/netconfd/startup-cfg.xml var/lib/netconfd yuma123_2.14/netconf/systemd/debian/copyright0000664000175000017500000000333614770023131021431 0ustar vladimirvladimirFormat: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: netconfd-autostart Upstream-Contact: Vladimir Vassilev Files: * Copyright: 2019-2025 Vladimir Vassilev License: BSD-3-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Vassilev and Transpacket AS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. yuma123_2.14/netconf/systemd/debian/compat0000664000175000017500000000000314770023131020670 0ustar vladimirvladimir10 yuma123_2.14/netconf/systemd/debian/systemd/0000775000175000017500000000000014770023131021161 5ustar vladimirvladimiryuma123_2.14/netconf/systemd/debian/systemd/netconfd-ssh.service0000664000175000017500000000123314770023131025135 0ustar vladimirvladimir[Unit] Description=OpenBSD Secure Shell server instance for netconfd Documentation=man::netconfd(1) man:sshd(8) man:sshd_config(5) After=network.target auditd.service netconfd.service [Service] EnvironmentFile=-/etc/default/netconfd-ssh ExecStartPre=/usr/sbin/sshd -t -f /etc/netconf/ssh/sshd_config ExecStart=/usr/sbin/sshd -D -f /etc/netconf/ssh/sshd_config $SSHD_OPTS ExecReload=/usr/sbin/sshd -t -f /etc/netconf/ssh/sshd_config ExecReload=/bin/kill -HUP $MAINPID KillMode=process Restart=on-failure RestartPreventExitStatus=255 Type=notify RuntimeDirectory=netconfd-ssh RuntimeDirectoryMode=0755 [Install] WantedBy=multi-user.target Alias=netconfd-sshd.service yuma123_2.14/netconf/systemd/debian/systemd/netconfd.service0000664000175000017500000000102514770023131024341 0ustar vladimirvladimir[Unit] Description=yuma123 netconfd server Documentation=man:netconfd(1) man:sshd(8) man:sshd_config(5) After=network.target auditd.service [Service] Type=simple EnvironmentFile=-/etc/default/netconfd ExecStartPre=/usr/sbin/netconfd --validate-config-only ExecStart=/usr/sbin/netconfd $NETCONFD_OPTS --startup=/var/lib/netconfd/startup-cfg.xml --superuser=root ExecReload=/bin/kill -HUP $MAINPID KillMode=control-group RuntimeDirectory=netconfd RuntimeDirectoryMode=0755 [Install] WantedBy=multi-user.target Alias=netconfd.service yuma123_2.14/netconf/systemd/debian/changelog0000664000175000017500000000025614770023131021346 0ustar vladimirvladimiryuma123-systemd-services (2.14-0) stable; urgency=medium * Unofficial package -- Vladimir Vassilev Sun, 23 Mar 2025 15:25:36 +0100 yuma123_2.14/netconf/systemd/debian/netconfd-openssh-server-service.netconfd-ssh.service0000777000175000017500000000000014770023131037324 2systemd/netconfd-ssh.serviceustar vladimirvladimiryuma123_2.14/netconf/systemd/debian/netconfd-openssh-server-service.install0000664000175000017500000000007014770023131027275 0ustar vladimirvladimirdebian/tmp/etc/netconf/ssh/sshd_config etc/netconf/ssh/ yuma123_2.14/netconf/systemd/debian/netconfd-service.netconfd.service0000777000175000017500000000000014770023131032755 2systemd/netconfd.serviceustar vladimirvladimiryuma123_2.14/netconf/systemd/sshd_config0000664000175000017500000000044414770023131020462 0ustar vladimirvladimirPidFile /run/ssh-netconf.pid ChallengeResponseAuthentication no UsePAM yes AcceptEnv LANG LC_* PermitRootLogin yes Port 830 Subsystem netconf "/usr/sbin/netconf-subsystem --ncxserver-sockname=830@/tmp/ncxserver.sock" ForceCommand /usr/sbin/netconf-subsystem #LogLevel DEBUG3 #SyslogFacility yuma123_2.14/netconf/systemd/Makefile.am0000775000175000017500000000015114770023131020303 0ustar vladimirvladimirSUBDIRS= netconfd_ssh_config_DATA = sshd_config netconfd_state_DATA = startup-cfg.xml sbin_SCRIPTS = yuma123_2.14/netconf/systemd/configure.ac0000664000175000017500000000071114770023131020534 0ustar vladimirvladimirAC_INIT([yuma123-systemd-services], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([foreign]) netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules" AC_SUBST(yangdir) netconfd_ssh_configdir="$sysconfdir/netconf/ssh" AC_SUBST(netconfd_ssh_configdir) netconfd_statedir="$localstatedir/lib/netconfd" AC_SUBST(netconfd_statedir) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/netconf/scripts/0000775000175000017500000000000014770023131016246 5ustar vladimirvladimiryuma123_2.14/netconf/scripts/test-cancel0000664000175000017500000000011214770023131020365 0ustar vladimirvladimirget xget /system copy-config source=candidate target=running xget /system yuma123_2.14/netconf/scripts/start-replay0000664000175000017500000000006314770023131020617 0ustar vladimirvladimircreate-subscription startTime=2009-01-01T00:00:00Z yuma123_2.14/netconf/src/0000775000175000017500000000000014770023131015346 5ustar vladimirvladimiryuma123_2.14/netconf/src/agt/0000775000175000017500000000000014770023131016121 5ustar vladimirvladimiryuma123_2.14/netconf/src/agt/agt_nmda.c0000664000175000017500000003362514770023131020050 0ustar vladimirvladimir/* * Copyright (c) 2017 Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cli.h" #include "agt_nmda.h" #include "agt_rpc.h" #include "agt_util.h" #include "cfg.h" #include "getcb.h" #include "log.h" #include "ncxmod.h" #include "ncxtypes.h" #include "ncx_feature.h" #include "ncx_list.h" #include "rpc.h" #include "rpc_err.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "tstamp.h" #include "val.h" #include "val123.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xml_wr.h" #include "yangconst.h" /* merge running(intended), system and learned origins */ static val_value_t* root_operational_val=NULL; static val_value_t* root_system_val=NULL; static val_value_t* root_learned_val=NULL; ncx_module_t* ietf_origin_mod; #if 0 /* Work in progress: more efficient solution then the proof-of-concept hack below */ static status_t operational_get_callback(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val); static void operational_merge_cplx(val_value_t* dst, val_value_t* src, char* origin_identity) { val_value_t* chval; val_value_t* match_val; for (chval = val_get_first_child(src); chval != NULL; chval = val_get_next_child(chval)) { #if 0 if(obj_is_key(chval->obj)) { continue; } #endif match_val = val123_find_match(dst, chval); if(match_val==NULL) { val_value_t* new_val; assert(!obj_is_key(chval->obj)); new_val=val_new_value(); assert(new_val); val_init_virtual(new_val, operational_get_callback, chval->obj); /*add keys*/ TODO -add keys val_add_child(new_val,dst); } else { continue; } } return NO_ERR; } static status_t operational_get_callback(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res = NO_ERR; ncx_module_t* ietf_origin_mod; val_value_t* interfaces_val; val_value_t* interface_val; val_value_t* origin_val; /* Add all children from running,system and learned with keys as real values non-keys as virtual */ running_val = val123_find_match(root_val, vir_val); system_val = val123_find_match(root_system_val, vir_val); learned_val = val123_find_match(root_learned_val, vir_val); cur_origin=agt_nmda_operational_check_origin(dst_val); if(running_val) { val_value_t* running_cloned_real_val; running_cloned_real_val = val123_clone_real(running_val); assert(running_cloned_real_val); operational_merge_cplx(dst_val, running_cloned_real_val, "or:intended"); } if(system_val) { operational_merge_cplx(dst_val, system_val, "or:system"); } if(learned_val) { operational_merge_cplx(dst_val, learned_val, "or:learned"); } assert(res == NO_ERR); return res; } #endif static void add_origin(val_value_t* val, char* origin_id) { val_value_t* origin_val; origin_val = val_make_string(ietf_origin_mod->nsid, "origin", origin_id /*e.g. "or:intended"*/); val_add_meta(origin_val, val); } /* the origin meta for configuration store value b is added to the nodes existing only in b to operational */ static void operational_resolve_origin_meta(val_value_t* operational_val, val_value_t* a_val, val_value_t* b_val, char* origin_b_str) { val_value_t* chval; val_value_t* child_a_val; val_value_t* child_b_val; assert(obj_is_config(operational_val->obj)); for (chval = val_get_first_child(operational_val); chval != NULL; chval = val_get_next_child(chval)) { if(!obj_get_config_flag(chval->obj)) { /* config false data has no config origin */ continue; } #if 0 if(obj_is_key(chval->obj)) { continue; } #endif child_b_val = val123_find_match(b_val, chval); if(child_b_val==NULL) { continue; } child_a_val = val123_find_match(a_val, chval); if(child_a_val==NULL && !obj_is_np_container(chval->obj)) { add_origin(chval,origin_b_str); continue; } if(!typ_is_simple(chval->btyp)) { operational_resolve_origin_meta(chval, child_a_val, child_b_val, origin_b_str); } } return; } /* Initial proof-of-concept implementation with complete merge of the intended and system trees instead of recursive tree resolution */ static status_t operational_get_callback(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res = NO_ERR; val_value_t* running_real_clone_val; val_value_t* system_real_clone_val; val_value_t* learned_real_clone_val; val_value_t* oper_w_running_clone_val; val_value_t* oper_w_system_clone_val; cfg_template_t *runningcfg; /* get the running config */ runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert (runningcfg && runningcfg->root); running_real_clone_val = val123_clone_real(runningcfg->root); assert(running_real_clone_val); val123_merge_cplx(dst_val, running_real_clone_val); oper_w_running_clone_val = val_clone(dst_val); assert(oper_w_running_clone_val); system_real_clone_val = val123_clone_real(root_system_val); assert(system_real_clone_val); val123_merge_cplx(dst_val, system_real_clone_val); oper_w_system_clone_val = val_clone(dst_val); assert(oper_w_system_clone_val); learned_real_clone_val = val123_clone_real(root_learned_val); assert(learned_real_clone_val); val123_merge_cplx(dst_val, learned_real_clone_val); operational_resolve_origin_meta(dst_val, NULL, running_real_clone_val, "or:intended"); operational_resolve_origin_meta(dst_val, oper_w_running_clone_val, system_real_clone_val, "or:system"); operational_resolve_origin_meta(dst_val, oper_w_system_clone_val, learned_real_clone_val, "or:learned"); val_free_value(oper_w_running_clone_val); val_free_value(oper_w_system_clone_val); assert(res == NO_ERR); return res; } /************* E X T E R N A L F U N C T I O N S ***************/ val_value_t* agt_nmda_get_root_operational(void) { return root_operational_val; } val_value_t* agt_nmda_get_root_system(void) { return root_system_val; } val_value_t* agt_nmda_get_root_learned(void) { return root_learned_val; } /******************************************************************** * FUNCTION get_data_validate * * get_data : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t get_data_validate(ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { cfg_template_t *source; val_value_t *testval; status_t res, res2; boolean empty_callback = FALSE; static cfg_template_t *operational_cfg=NULL; if(operational_cfg==NULL) { operational_cfg = cfg_new_template("operational", 1000 /*dummy cfg_id*/); assert(operational_cfg); operational_cfg->root = agt_nmda_get_root_operational(); } /* TODO set to the configuration datastore selected with datastore if not operational*/ source = operational_cfg; #if 0 /* check if the config is ready to read */ source = cfg_get_config_id(NCX_CFGID_RUNNING); if (!source) { res = ERR_NCX_OPERATION_FAILED; } else { res = cfg_ok_to_read(source); } if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } #endif /* check if the optional filter parameter is ok */ res = agt_validate_filter(scb, msg); /* check the with-defaults parameter */ res2 = agt_set_with_defaults(scb, msg, methnode); if (res != NO_ERR) { return res; /* error already recorded */ } if (res2 != NO_ERR) { return res2; /* error already recorded */ } #if 0 testval = val_find_child(msg->rpc_input, y_yuma_time_filter_M_yuma_time_filter, IF_MODIFIED_SINCE); if (testval != NULL && testval->res == NO_ERR) { boolean isneg = FALSE; xmlChar *utcstr; int ret; utcstr = tstamp_convert_to_utctime(VAL_STR(testval), &isneg, &res); if (res != NO_ERR || isneg) { if (utcstr) { m__free(utcstr); } if (isneg) { res = ERR_NCX_INVALID_VALUE; } agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_VAL, testval, NCX_NT_VAL, testval); return res; } ret = xml_strcmp(source->last_ch_time, utcstr); if (ret <= 0) { empty_callback = TRUE; } m__free(utcstr); } #endif /* cache the 2 parameters and the data output callback function * There is no invoke function -- it is handled automatically * by the agt_rpc module */ msg->rpc_user1 = source; msg->rpc_data_type = RPC_DATA_STD; if (empty_callback) { msg->rpc_datacb = agt_output_empty; } else { msg->rpc_datacb = agt_output_filter; } return NO_ERR; } /* get_data_validate */ /******************************************************************** * FUNCTION agt_nmda_init * * INIT 1: * Initialize the NMDA module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t agt_nmda_init (void) { agt_profile_t *agt_profile; status_t res; ncx_module_t *mod; obj_template_t *root_obj; val_value_t* clivalset; val_value_t* val; clivalset = agt_cli_get_valset(); val = val_find_child(clivalset, NCXMOD_NETCONFD_EX, NCX_EL_WITH_NMDA); if((val==NULL) || (!VAL_BOOL(val))) { return NO_ERR; } agt_profile = agt_get_profile(); /* load in the NMDA NETCONF data types and RPC methods */ res = ncxmod_load_module( "ietf-netconf-nmda", NULL, NULL, NULL ); assert(res == NO_ERR); /* get-data */ res = agt_rpc_register_method("ietf-netconf-nmda"/*NC_MODULE*/, "get-data"/*op_method_name(OP_GET)*/, AGT_RPC_PH_VALIDATE, get_data_validate); assert(res == NO_ERR); /* load the module */ res = ncxmod_load_module("ietf-origin", NULL, &agt_profile->agt_savedevQ, &ietf_origin_mod); assert(res == NO_ERR); mod = ncx_find_module(NCXMOD_NETCONF, NULL); assert(mod); root_obj = ncx_find_object(mod, NCX_EL_CONFIG); assert(root_obj); /*init root_operational_val */ root_operational_val = val_new_value(); assert(root_operational_val); val_init_virtual(root_operational_val, operational_get_callback, root_obj); /*init root_system_val */ root_system_val = val_new_value(); assert(root_system_val); val_init_from_template(root_system_val,root_obj); /*init root_learned_val */ root_learned_val = val_new_value(); assert(root_learned_val); val_init_from_template(root_learned_val,root_obj); return NO_ERR; } /* agt_nmda_init */ /******************************************************************** * FUNCTION agt_nmda_init2 * * INIT 2: * Initialize the yang library data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t agt_nmda_init2 (void) { val_value_t* clivalset; val_value_t* val; clivalset = agt_cli_get_valset(); val = val_find_child(clivalset, NCXMOD_NETCONFD_EX, NCX_EL_WITH_NMDA); if((val==NULL) || (!VAL_BOOL(val))) { return NO_ERR; } return NO_ERR; } /* agt_nmda_init2 */ /******************************************************************** * FUNCTION agt_nmda_cleanup * * Cleanup the module data structures * * INPUTS: * * RETURNS: * none *********************************************************************/ void agt_nmda_cleanup (void) { val_value_t* clivalset; val_value_t* val; clivalset = agt_cli_get_valset(); val = val_find_child(clivalset, NCXMOD_NETCONFD_EX, NCX_EL_WITH_NMDA); if((val==NULL) || (!VAL_BOOL(val))) { return; } val_free_value(root_operational_val); val_free_value(root_system_val); val_free_value(root_learned_val); agt_rpc_unregister_method("ietf-netconf-nmda", "get-data"); } /* agt_nmda_cleanup */ yuma123_2.14/netconf/src/agt/agt_cli.c0000664000175000017500000005064514770023131017701 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_cli.c Set agent CLI parameters This module is called before any other agent modules have been initialized. Only core ncx library functions can be called while processing agent CLI parameters ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 03feb06 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include "procdefs.h" #include "agt_cli.h" #include "agt_ncxserver.h" #include "cli.h" #include "conf.h" #include "help.h" #include "ncx.h" #include "ncxconst.h" #include "obj.h" #include "status.h" #include "val.h" #include "val_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* #define AGT_CLI_DEBUG 1 */ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static val_value_t *cli_val = NULL; /******************************************************************** * FUNCTION set_server_profile * * Get the initial agent profile variables for now * Set the agt_profile data structure * * INPUTS: * valset == value set for CLI parsing or NULL if none * agt_profile == pointer to profile struct to fill in * * OUTPUTS: * *agt_profile is filled in with params of defaults * *********************************************************************/ static void set_server_profile (val_value_t *valset, agt_profile_t *agt_profile) { val_value_t *val; uint32 i; boolean done; /* check if there is any CLI data to read */ if (valset == NULL) { /* assumes agt_profile already has default values */ return; } /* check all the netconfd CLI parameters; * follow the order in netconfd.yang since * no action will be taken until all params are collected * * conf=filespec param checked externally */ /* get access-control param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_ACCESS_CONTROL); if (val && val->res == NO_ERR) { agt_profile->agt_accesscontrol = VAL_ENUM_NAME(val); } if (agt_profile->agt_accesscontrol) { if (!xml_strcmp(agt_profile->agt_accesscontrol, NCX_EL_ENFORCING)) { agt_profile->agt_accesscontrol_enum = AGT_ACMOD_ENFORCING; } else if (!xml_strcmp(agt_profile->agt_accesscontrol, NCX_EL_PERMISSIVE)) { agt_profile->agt_accesscontrol_enum = AGT_ACMOD_PERMISSIVE; } else if (!xml_strcmp(agt_profile->agt_accesscontrol, NCX_EL_DISABLED)) { agt_profile->agt_accesscontrol_enum = AGT_ACMOD_DISABLED; } else if (!xml_strcmp(agt_profile->agt_accesscontrol, NCX_EL_OFF)) { agt_profile->agt_accesscontrol_enum = AGT_ACMOD_OFF; } else { SET_ERROR(ERR_INTERNAL_VAL); agt_profile->agt_accesscontrol_enum = AGT_ACMOD_ENFORCING; } } else { agt_profile->agt_accesscontrol_enum = AGT_ACMOD_ENFORCING; } /* get default-style param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_DEFAULT_STYLE); if (val && val->res == NO_ERR) { agt_profile->agt_defaultStyle = VAL_ENUM_NAME(val); agt_profile->agt_defaultStyleEnum = ncx_get_withdefaults_enum(VAL_ENUM_NAME(val)); } /* help parameter checked externally */ /* the logging parameters might have already been * set in the bootstrap CLI (cli_parse_raw) * they may get reset here, if the conf file has * a different value selected */ /* get delete-empty-npcontainters param */ val = val_find_child(valset, AGT_CLI_MODULE, AGT_CLI_DELETE_EMPTY_NPCONTAINERS); if (val && val->res == NO_ERR) { agt_profile->agt_delete_empty_npcontainers = VAL_BOOL(val); } else { agt_profile->agt_delete_empty_npcontainers = AGT_DEF_DELETE_EMPTY_NP; } /* get indent param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_INDENT); if (val && val->res == NO_ERR) { agt_profile->agt_indent = (int32)VAL_UINT(val); } /* get log param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_LOG); if (val && val->res == NO_ERR) { agt_profile->agt_logfile = VAL_STR(val); } /* get log-append param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_LOGAPPEND); if (val && val->res == NO_ERR) { agt_profile->agt_logappend = TRUE; } /* get log-level param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_LOGLEVEL); if (val && val->res == NO_ERR) { agt_profile->agt_loglevel = log_get_debug_level_enum((const char *)VAL_STR(val)); } /* get leaf-list port parameter */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_PORT); if (val && val->res == NO_ERR) { agt_profile->agt_ports[0] = VAL_UINT16(val); val = val_find_next_child(valset, AGT_CLI_MODULE, NCX_EL_PORT, val); while (val) { done = FALSE; for (i = 0; i < AGT_MAX_PORTS && !done; i++) { if (agt_profile->agt_ports[i] == VAL_UINT16(val)) { done = TRUE; } else if (agt_profile->agt_ports[i] == 0) { agt_profile->agt_ports[i] = VAL_UINT16(val); done = TRUE; } } val = val_find_next_child(valset, AGT_CLI_MODULE, NCX_EL_PORT, val); } } /* eventlog-size param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_EVENTLOG_SIZE); if (val && val->res == NO_ERR) { agt_profile->agt_eventlog_size = VAL_UINT(val); } /* get hello-timeout param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_HELLO_TIMEOUT); if (val && val->res == NO_ERR) { agt_profile->agt_hello_timeout = VAL_UINT(val); } /* get idle-timeout param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_IDLE_TIMEOUT); if (val && val->res == NO_ERR) { agt_profile->agt_idle_timeout = VAL_UINT(val); } /* max-burst param */ val = val_find_child(valset, AGT_CLI_MODULE, AGT_CLI_MAX_BURST); if (val && val->res == NO_ERR) { agt_profile->agt_maxburst = VAL_UINT(val); } /* running-error param */ val = val_find_child(valset, AGT_CLI_MODULE, AGT_CLI_RUNNING_ERROR); if (val && val->res == NO_ERR) { if (!xml_strcmp(VAL_ENUM_NAME(val), AGT_CLI_STARTUP_STOP)) { agt_profile->agt_running_error = TRUE; } else if (!xml_strcmp(VAL_ENUM_NAME(val), AGT_CLI_STARTUP_CONTINUE)) { agt_profile->agt_running_error = FALSE; } } /* start choice: * cli module will check that only 1 of the following 3 * parms are actually entered * start choice 1: get no-startup param */ val = val_find_child(valset, AGT_CLI_MODULE, AGT_CLI_NOSTARTUP); if (val && val->res == NO_ERR) { agt_profile->agt_usestartup = FALSE; } /* start choice 2: OR get startup param */ val = val_find_child(valset, AGT_CLI_MODULE, AGT_CLI_STARTUP); if (val && val->res == NO_ERR) { agt_profile->agt_startup = VAL_STR(val); } /* start choice 3: OR get factory-startup param */ val = val_find_child(valset, AGT_CLI_MODULE, AGT_CLI_FACTORY_STARTUP); if (val && val->res == NO_ERR) { agt_profile->agt_factorystartup = TRUE; } /* startup-error param */ val = val_find_child(valset, AGT_CLI_MODULE, AGT_CLI_STARTUP_ERROR); if (val && val->res == NO_ERR) { if (!xml_strcmp(VAL_ENUM_NAME(val), AGT_CLI_STARTUP_CONTINUE)) { agt_profile->agt_startup_error = FALSE; } } /* superuser param */ val = val_find_child(valset, AGT_CLI_MODULE, AGT_CLI_SUPERUSER); if (val && val->res == NO_ERR) { agt_profile->agt_superuser = VAL_STR(val); } /* get target param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_TARGET); if (val && val->res == NO_ERR) { if (!xml_strcmp(VAL_ENUM_NAME(val), NCX_EL_RUNNING)) { agt_profile->agt_targ = NCX_AGT_TARG_RUNNING; } else if (!xml_strcmp(VAL_ENUM_NAME(val), NCX_EL_CANDIDATE)) { agt_profile->agt_targ = NCX_AGT_TARG_CANDIDATE; } } /* get the :startup capability setting */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_WITH_STARTUP); if (val && val->res == NO_ERR) { if (VAL_BOOL(val)) { agt_profile->agt_start = NCX_AGT_START_DISTINCT; agt_profile->agt_has_startup = TRUE; } else { agt_profile->agt_start = NCX_AGT_START_MIRROR; agt_profile->agt_has_startup = FALSE; } } /* check the subdirs parameter */ val_set_subdirs_parm(valset); /* version param handled externally */ /* get usexmlorder param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_USEXMLORDER); if (val && val->res == NO_ERR) { agt_profile->agt_xmlorder = TRUE; } /* get the :url capability setting */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_WITH_URL); if (val && val->res == NO_ERR) { agt_profile->agt_useurl = VAL_BOOL(val); } /* get with-validate param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_WITH_VALIDATE); if (val && val->res == NO_ERR) { agt_profile->agt_usevalidate = VAL_BOOL(val); } /* get max-sessions param */ val = val_find_child(valset, AGT_CLI_MODULE_EX, NCX_EL_MAX_SESSIONS); if (val && val->res == NO_ERR) { agt_profile->agt_max_sessions = VAL_UINT(val); } val = val_find_child(valset, AGT_CLI_MODULE_EX, NCX_EL_TCP_DIRECT_PORT); agt_profile->agt_tcp_direct_port = -1; if(val != NULL) { agt_profile->agt_tcp_direct_port = VAL_INT(val); } val = val_find_child(valset, AGT_CLI_MODULE_EX, NCX_EL_TCP_DIRECT_ADDRESS); agt_profile->agt_tcp_direct_address = NULL; if(val != NULL) { agt_profile->agt_tcp_direct_address = VAL_STR(val); if(agt_profile->agt_tcp_direct_port==-1) agt_profile->agt_tcp_direct_port = 2023; } val = val_find_child(valset, AGT_CLI_MODULE_EX, NCX_EL_NCXSERVER_SOCKNAME); if(val != NULL) { agt_profile->agt_ncxserver_sockname = VAL_STR(val); } else { agt_profile->agt_ncxserver_sockname = NCXSERVER_SOCKNAME; } } /* set_server_profile */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION agt_cli_process_input * * Process the param line parameters against the hardwired * parmset for the netconfd program * * INPUTS: * argc == argument count * argv == array of command line argument strings * agt_profile == agent profile struct to fill in * showver == address of version return quick-exit status * showhelpmode == address of help return quick-exit status * * OUTPUTS: * *agt_profile is filled in, with parms gathered or defaults * *showver == TRUE if user requsted version quick-exit mode * *showhelpmode == requested help mode * (none, breief, normal, full) * * RETURNS: * NO_ERR if all goes well *********************************************************************/ status_t agt_cli_process_input (int argc, char *argv[], agt_profile_t *agt_profile, boolean *showver, help_mode_t *showhelpmode) { ncx_module_t *mod; obj_template_t *obj; val_value_t *valset, *val; FILE *fp; status_t res; boolean test; #ifdef DEBUG if (!argv || !agt_profile || !showver || !showhelpmode) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif *showver = FALSE; *showhelpmode = HELP_MODE_NONE; /* find the parmset definition in the registry */ obj = NULL; mod = ncx_find_module(AGT_CLI_MODULE, NULL); if (mod) { obj = ncx_find_object(mod, AGT_CLI_CONTAINER); } if (!obj) { log_error("\nError: netconfd module with CLI definitions not loaded"); return ERR_NCX_NOT_FOUND; } /* parse the command line against the object template */ res = NO_ERR; valset = NULL; if (argc > 0) { valset = cli_parse(NULL, argc, argv, obj, FULLTEST, PLAINMODE, TRUE, CLI_MODE_PROGRAM, &res); if (res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } } if (valset != NULL) { /* transfer the parmset values */ set_server_profile(valset, agt_profile); /* next get any params from the conf file */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_CONFIG); if (val) { if (val->res == NO_ERR) { /* try the specified config location */ agt_profile->agt_conffile = VAL_STR(val); res = conf_parse_val_from_filespec(VAL_STR(val), valset, TRUE, TRUE); if (res != NO_ERR) { val_free_value(valset); return res; } else { /* transfer the parmset values again */ set_server_profile(valset, agt_profile); } } } else { fp = fopen((const char *)AGT_DEF_CONF_FILE, "r"); if (fp != NULL) { fclose(fp); /* use default config location */ res = conf_parse_val_from_filespec(AGT_DEF_CONF_FILE, valset, TRUE, TRUE); if (res != NO_ERR) { val_free_value(valset); return res; } else { /* transfer the parmset values again */ set_server_profile(valset, agt_profile); } } } /* set the logging control parameters */ val_set_logging_parms(valset); /* audit-log-append param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_AUDIT_LOG_APPEND); if (val && val->res == NO_ERR) { test = TRUE; } else { test = FALSE; } /* audit-log param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_AUDIT_LOG); if (val && val->res == NO_ERR) { xmlChar *filespec = ncx_get_source(VAL_STR(val), &res); if (filespec == NULL) { log_error("\nError: get source for audit log failed"); return res; } res = log_audit_open((const char *)filespec, test, TRUE); if (res == NO_ERR) { if (LOGDEBUG) { log_debug("\nAudit log '%s' opened for %s", filespec, (test) ? "append" : "write"); } } else { log_error("\nError: open audit log '%s' failed", filespec); } m__free(filespec); if (res != NO_ERR) { return res; } } /* set the file search path parms */ res = val_set_path_parms(valset); if (res != NO_ERR) { return res; } /* set the warning control parameters */ res = val_set_warning_parms(valset); if (res != NO_ERR) { return res; } /* set the feature code generation parameters */ res = val_set_feature_parms(valset); if (res != NO_ERR) { return res; } /* check the subdirs parameter */ res = val_set_subdirs_parm(valset); if (res != NO_ERR) { return res; } /* check the protocols parameter */ res = val_set_protocols_parm(valset); if (res != NO_ERR) { return res; } /* check the system-sorted param */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_SYSTEM_SORTED); if (val && val->res == NO_ERR) { agt_profile->agt_system_sorted = VAL_BOOL(val); } /* version param handled externally */ /* check if version mode requested */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_VERSION); *showver = (val) ? TRUE : FALSE; /* check if help mode requested */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_HELP); if (val) { *showhelpmode = HELP_MODE_NORMAL; /* help submode parameter (brief/normal/full) */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_BRIEF); if (val) { *showhelpmode = HELP_MODE_BRIEF; } else { /* full parameter */ val = val_find_child(valset, AGT_CLI_MODULE, NCX_EL_FULL); if (val) { *showhelpmode = HELP_MODE_FULL; } } } } /* cleanup and exit * handoff the malloced 'valset' memory here */ cli_val = valset; return res; } /* agt_cli_process_input */ /******************************************************************** * FUNCTION agt_cli_get_valset * * Retrieve the command line parameter set from boot time * * RETURNS: * pointer to parmset or NULL if none *********************************************************************/ val_value_t * agt_cli_get_valset (void) { return cli_val; } /* agt_cli_get_valset */ /******************************************************************** * FUNCTION agt_cli_cleanup * * Cleanup the module static data * *********************************************************************/ void agt_cli_cleanup (void) { if (cli_val) { val_free_value(cli_val); cli_val = NULL; } } /* agt_cli_cleanup */ /* END file agt_cli.c */ yuma123_2.14/netconf/src/agt/agt_ncxserver.h0000664000175000017500000000473014770023131021150 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_ncxserver #define _H_agt_ncxserver /* FILE: agt_ncxserver.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Server Library ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11-jan-07 abb Begun */ #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define NCXSERVER_SOCKNAME "/tmp/ncxserver.sock" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_ncxserver_run * * IO server loop for the ncxserver socket * * RETURNS: * status *********************************************************************/ extern status_t agt_ncxserver_run (void); /******************************************************************** * FUNCTION agt_ncxserver_clear_fd * * Clear a dead session from the select loop * * INPUTS: * fd == file descriptor number for the socket to clear *********************************************************************/ extern void agt_ncxserver_clear_fd (int fd); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_ncxserver */ yuma123_2.14/netconf/src/agt/agt_commit_complete.c0000664000175000017500000001274014770023131022304 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_commit_complete.c ********************************************************************* * P U R P O S E ********************************************************************* NETCONF Server Commit Complete callback handler This file contains functions to support registering, unregistering and execution of commit complete callbacks. ********************************************************************* * C H A N G E H I S T O R Y ********************************************************************* date init comment ---------------------------------------------------------------------- 24-oct-11 mp First draft. */ /******************************************************************** * Y U M A H E A D E R S *********************************************************************/ #include "agt_commit_complete.h" #include "dlq.h" #include "xml_util.h" /******************************************************************** * S T A N D A R D H E A D E R S *********************************************************************/ #include #include /******************************************************************** * T Y P E S *********************************************************************/ typedef struct agt_cb_commit_complete_set_t_ { dlq_hdr_t qhdr; xmlChar *modname; agt_commit_complete_cb_t callback; } agt_cb_commit_complete_set_t; /************** S T A T I C D A T A ****************************/ static bool initialised = false; static dlq_hdr_t callbackQ; /************** S T A T I C F U N C T I O N S ******************/ /********************************************************************/ static void free_callback_set( agt_cb_commit_complete_set_t* cbSet ) { if ( cbSet->modname ) { m__free( cbSet->modname ); } m__free( cbSet ); } /********************************************************************/ static agt_cb_commit_complete_set_t* new_callback_set( const xmlChar *modname ) { agt_cb_commit_complete_set_t* cbSet = m__getObj( agt_cb_commit_complete_set_t ); if ( !cbSet ) { return NULL; } memset( cbSet, 0, sizeof( agt_cb_commit_complete_set_t ) ); cbSet->modname = xml_strdup( modname ); if ( !cbSet->modname ) { m__free( cbSet ); return NULL; } return cbSet; } /********************************************************************/ static agt_cb_commit_complete_set_t* find_callback_set( const xmlChar *modname ) { agt_cb_commit_complete_set_t* cbSet; for ( cbSet = ( agt_cb_commit_complete_set_t* )dlq_firstEntry( &callbackQ ); cbSet != NULL; cbSet = ( agt_cb_commit_complete_set_t* )dlq_nextEntry( cbSet ) ) { if ( 0 == xml_strcmp( modname, cbSet->modname ) ) { return cbSet; } } return NULL; } /************** E X T E R N A L F U N C T I O N S **************/ /********************************************************************/ void agt_commit_complete_init( void ) { if ( !initialised ) { dlq_createSQue( &callbackQ ); initialised = true; } } /* agt_commit_complete_init */ /********************************************************************/ void agt_commit_complete_cleanup( void ) { if ( initialised ) { agt_cb_commit_complete_set_t* cbSet; while ( !dlq_empty( &callbackQ ) ) { cbSet = ( agt_cb_commit_complete_set_t* )dlq_deque( &callbackQ ); free_callback_set( cbSet ); } initialised = false; } } /* agt_commit_complete_cleanup */ /********************************************************************/ status_t agt_commit_complete_register( const xmlChar *modname, agt_commit_complete_cb_t cb ) { assert( modname ); agt_cb_commit_complete_set_t* cbSet = find_callback_set( modname ); if ( !cbSet ) { cbSet = new_callback_set( modname ); if ( !cbSet ) { return ERR_INTERNAL_MEM; } dlq_enque( cbSet, &callbackQ ); } cbSet->callback = cb; return NO_ERR; } /********************************************************************/ void agt_commit_complete_unregister( const xmlChar *modname ) { assert( modname ); agt_cb_commit_complete_set_t* cbSet = find_callback_set( modname ); if ( cbSet ) { dlq_remove( cbSet ); free_callback_set( cbSet ); } } /********************************************************************/ status_t agt_commit_complete( void ) { agt_cb_commit_complete_set_t* cbSet; for ( cbSet = ( agt_cb_commit_complete_set_t* )dlq_firstEntry( &callbackQ ); cbSet != NULL; cbSet = ( agt_cb_commit_complete_set_t* )dlq_nextEntry( cbSet ) ) { if ( cbSet->callback ) { status_t res = cbSet->callback(); if ( NO_ERR != res ) { return res; } } } return NO_ERR; } /* END file agt_commit_complete.c */ yuma123_2.14/netconf/src/agt/agt_signal.h0000664000175000017500000000443614770023131020411 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_signal #define _H_agt_signal /* FILE: agt_signal.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Handle interrupt signals for the server ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 22-jan-07 abb Begun */ #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_signal_init * * Initialize the agt_signal module * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ extern void agt_signal_init (void); /******************************************************************** * FUNCTION agt_signal_cleanup * * Cleanup the agt_signal module. * *********************************************************************/ extern void agt_signal_cleanup (void); /******************************************************************** * FUNCTION agt_signal_handler * * Handle an incoming interrupt signal * * INPUTS: * intr == interrupt numer * *********************************************************************/ extern void agt_signal_handler (int intr); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_signal */ yuma123_2.14/netconf/src/agt/agt_tree.c0000664000175000017500000010520214770023131020057 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_tree.c NETCONF Subtree Filtering Implementation for NCX Step 1) val_parse_nc will parse the entire subtree filter as type NCX_BT_ANYDATA or NCX_BT_ANYXML A filter is parsed as type 'any' as follows: NCX_BT_CONTAINER -- container node NCX_BT_STRING -- content match node NCX_BT_EMPTY -- select node val->metaQ -- attribute match expressions Step 2) agt_tree_prune_filter will traverse the filter val and compare it to the target config, figuring out if a node is TRUE or FALSE. An ncx_filptr_t tree is built as this is done, to optimize node removal and rendering later on. The filter val_value_t is no longer used after this is done. - If a filter node has any child nodes, they must all be TRUE, for the node itself to be TRUE. If not, the filter node and all its children are removed - If access control is requested, then the scb->username will be checked against the NACM configuration. - For read operations, an access-denied results in a skipped node. - For notifications, an access-denied results in the entire notification being skipped. - Each ncx_filptr_t node indicates 1 matching instance at that level. It should be possible to optimize away all the ncx_filptr_t records for all 'container of container' nodes. Unless the child nodes contain 1 or more content select or attribute select expressions, all or none of the child nodes will match in this special case. (Left for a future project.) Step 3) agt_tree_output_filter will traverse the altered filter value node, and output the cached node instances from the target, if the node is not marked as deleted ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 16jun06 abb begun; split out from agt_util.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_acm.h" #include "agt_rpc.h" #include "agt_rpcerr.h" #include "agt_tree.h" #include "agt_util.h" #include "agt_val.h" #include "b64.h" #include "cfg.h" #include "def_reg.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "ncx_num.h" #include "ncxconst.h" #include "obj.h" #include "op.h" #include "rpc.h" #include "rpc_err.h" #include "ses.h" #include "status.h" #include "tk.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xml_wr.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION save_filptr * * create ncx_filptr_t struct and save it in the parent->childQ * * INPUTS: * parent == filptr struct to save the nodeptr in * valnode == value node to save * * RETURNS: * pointer to new ncx_filptr_t struct that was saved * NULL if malloc error *********************************************************************/ static ncx_filptr_t * save_filptr (ncx_filptr_t *parent, val_value_t *valnode) { ncx_filptr_t *filptr; filptr = ncx_new_filptr(); if (filptr) { filptr->node = valnode; dlq_enque(filptr, &parent->childQ); } return filptr; } /* save_filptr */ /******************************************************************** * FUNCTION find_filptr * * find an ncx_filptr_t struct * * INPUTS: * parent == parent with the Q of ncx_filptr_t to search * valnode == value node to find * * RETURNS: * pointer to found ncx_filptr_t struct * NULL if not found *********************************************************************/ static ncx_filptr_t * find_filptr (ncx_filptr_t *parent, val_value_t *valnode) { ncx_filptr_t *filptr; for (filptr = (ncx_filptr_t *)dlq_firstEntry(&parent->childQ); filptr != NULL; filptr = (ncx_filptr_t *)dlq_nextEntry(filptr)) { if (filptr->node == valnode) { return filptr; } } return NULL; } /* find_filptr */ /******************************************************************** * FUNCTION attr_test * * Check any attribute match expressions * * INPUTS: * filval == filter node value * targval == corresponding node from the target * * RETURNS: * TRUE if no tests failed * FALSE if any tests fail (exits at first failure) *********************************************************************/ static boolean attr_test (val_value_t *filval, val_value_t *targval) { val_value_t *m1; dlq_hdr_t *metaQ; xmlns_id_t invid; boolean done, ret; invid = xmlns_inv_id(); metaQ = val_get_metaQ(filval); if (!metaQ) { return TRUE; } ret = TRUE; done = FALSE; for (m1 = val_get_first_meta(metaQ); m1 != NULL && !done; m1 = val_get_next_meta(m1)) { if (m1->nsid == invid) { ret = FALSE; done = TRUE; } else if (!val_meta_match(targval, m1)) { ret = FALSE; done = TRUE; } } return ret; } /* attr_test */ /******************************************************************** * FUNCTION content_match_test * * Check a content match node against the corresponding * node in the target. * * INPUTS: * scb == session control block * testval == string to compare with * curval == target node to compare against * * RETURNS: * TRUE if content match test OK * FALSE if content different or target is a complex data * type and not a simple type *********************************************************************/ static boolean content_match_test (ses_cb_t *scb, const xmlChar *testval, val_value_t *curval) { const val_value_t *cmpval; val_value_t *v_val; xmlChar *binbuff; boolean testres; status_t res; uint32 binlen, retlen, testlen; ncx_num_t num; /* virtual values are cached in the real values so they do * not have to be freed after use */ v_val = NULL; /* skip matches of any password object */ if (obj_is_password(curval->obj)) { return FALSE; } /* handle virtual compare differently */ if (val_is_virtual(curval)) { /* get temp value to store virtual value */ v_val = val_get_virtual_value(scb, curval, &res); if (!v_val) { if (res != ERR_NCX_SKIPPED) { log_error("\agt_tree: get virtual failed %s", get_error_string(res)); } return FALSE; } } testres = FALSE; cmpval = (v_val) ? v_val : curval; switch (cmpval->btyp) { case NCX_BT_BOOLEAN: if (!xml_strcmp(testval, NCX_EL_TRUE) || !xml_strcmp(testval, (const xmlChar *)"1")) { testres = cmpval->v.boo; } else if (!xml_strcmp(testval, NCX_EL_FALSE) || !xml_strcmp(testval, (const xmlChar *)"0")) { testres = !cmpval->v.boo; } break; case NCX_BT_BINARY: binlen = xml_strlen(testval); binbuff = m__getMem(binlen); if (!binbuff) { SET_ERROR(ERR_INTERNAL_MEM); return 0; } res = b64_decode(testval, binlen, binbuff, binlen, &retlen); if (res == NO_ERR) { testres = !memcmp(binbuff, cmpval->v.binary.ustr, retlen); } else { SET_ERROR(res); testres = 0; } m__free(binbuff); break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: ncx_init_num(&num); res = ncx_decode_num(testval, cmpval->btyp, &num); if (res == NO_ERR) { testres = !ncx_compare_nums(&num, &cmpval->v.num, curval->btyp) ? TRUE : FALSE; } ncx_clean_num(cmpval->btyp, &num); break; case NCX_BT_ENUM: case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: case NCX_BT_IDREF: case NCX_BT_LEAFREF: /****/ testlen = xml_strlen(testval); retlen = 0; res = val_sprintf_simval_nc(NULL, cmpval, &retlen); if (res == NO_ERR && retlen == testlen) { binbuff = m__getMem(retlen+1); if (binbuff) { res = val_sprintf_simval_nc(binbuff, cmpval, &retlen); if (res == NO_ERR) { testres = (xml_strcmp(binbuff, testval)) ? FALSE : TRUE; } m__free(binbuff); } } break; case NCX_BT_BITS: case NCX_BT_SLIST: case NCX_BT_EMPTY: case NCX_BT_CONTAINER: case NCX_BT_LIST: default: ; /* test is automatically FALSE for these data types */ } return testres; } /* content_match_test */ /******************************************************************** * FUNCTION process_val * * Evaluate the subtree and remove nodes * which are not in the result set * * The filval is a NCX_BT_CONTAINER, and already matched * to the 'curnode'. This function evaluates the child nodes * recursively as more container nodes are matched to the target * * INPUTS: * msg == incoming or outgoing message header in progress * scb == session control block * == NULL if no read access control is desired * getop == TRUE if this is a and not a * The target is expected to be the * config, and all state data will be available for the * filter output. * FALSE if this is a and only the * specified target in available for filter output * isnotif == TRUE if this is for a notification * FALSE if for or * filval == filter node * curval == current database node * result == filptr tree result to fill in * keepempty == address of return keepempty flag * * OUTPUTS: * *result is filled in as needed * only 'true' result filter nodes should be remaining * *keepempty is set to TRUE if a select node is tested * and needs to be kept because all descendants are selected * == FALSE if select node needs to be deleted (no match) * * RETURNS: * status, NO_ERR or malloc error *********************************************************************/ static status_t process_val (xml_msg_hdr_t *msg, ses_cb_t *scb, boolean getop, boolean isnotif, val_value_t *filval, val_value_t *curval, ncx_filptr_t *result, boolean *keepempty) { val_value_t *filchild, *curchild, *useval, *virtualval; val_index_t *valindex; ncx_filptr_t *filptr; boolean test, anycon, anysel, mykeepempty; xmlns_id_t ncid, wildid; status_t res; res = NO_ERR; *keepempty = FALSE; /* little hack: treat a filter node with the * NETCONF namespace as if it were set to 0 * this will happen if the manager is lazy * and just set the NETCONF namespace at * the top level, and uses it for everything */ ncid = xmlns_nc_id(); /* base:1.1 subtree filtering allows * wildcard namespace ID xmlns="" * * !!! Note that libxml2 does not actually return * !!! anything for the namespace "". This still works * !!! with yuma filtering so it is ignored. * !!! This wildid nsid code is in case a different * !!! xml parser is used. */ wildid = xmlns_wildcard_id(); /* The filval is the same level as the curval * * Go through and check all the child nodes of the * filval (sibling set) against all the children * of the curval. Determine which nodes to keep * and save ncx_filptr_t structs for those nodes */ anycon = FALSE; anysel = FALSE; /* check if this is a real or a virtual value */ res = NO_ERR; virtualval = NULL; if (val_is_virtual(curval)) { virtualval = val_get_virtual_value(scb, curval, &res); if (virtualval == NULL) { return res; } else { useval = virtualval; result->virtualnode = virtualval; } } else { useval = curval; } /* check any content match nodes first * they must all be true or this entire sibling * set is rejected */ for (filchild = val_get_first_child(filval); filchild != NULL; filchild = val_get_next_child(filchild)) { if (!isnotif && filchild->nsid == ncid) { /* no content in NETCONF namespace, so assume * that no namespace was used and the filter NSID * has been passed down to this point * only use this for , not */ filchild->nsid = 0; } if (!isnotif && filchild->nsid == wildid) { /* For base:1.1 filtering, wildcard namespace * gets reset to 0 to match all namespaces * only use this for , not */ if (ses_get_protocol(scb) == NCX_PROTO_NETCONF11) { filchild->nsid = 0; } else { return ERR_NCX_PROTO11_NOT_ENABLED; } } /* skip all but content match nodes */ switch (filchild->btyp) { case NCX_BT_STRING: break; case NCX_BT_EMPTY: anysel = TRUE; continue; case NCX_BT_CONTAINER: anycon = TRUE; continue; default: return SET_ERROR(ERR_INTERNAL_VAL); } /* check corner case not caught by XML parser */ if (val_all_whitespace(VAL_STR(filchild))) { /* should not happen! */ return SET_ERROR(ERR_INTERNAL_VAL); } /* This is a valid content select node * Need to compare it to the current node * based on the target data type. * The filter data type is always going to * be string and this needs to be * compared to a sprintf of the target value * Compare the string value to all instances * of the 'filchild' node; need just 1 match */ test = FALSE; for (curchild = val_first_child_qname(useval, filchild->nsid, filchild->name); curchild != NULL && !test; curchild = val_next_child_qname(useval, filchild->nsid, filchild->name, curchild)) { /* check access control for notifications only */ if (isnotif && scb && !agt_acm_val_read_allowed(msg, scb->username, curchild)) { /* treat an access-failed on a content match * test as a termination trigger */ return NO_ERR; } test = content_match_test(scb, VAL_STR(filchild), curchild); } if (!test) { log_debug2("\nagt_tree_process_val: %s " "sibling set pruned; CM not found for '%s'", filval->name, filchild->name); return NO_ERR; } } /* at this point any content match node tests have passed * except any attribute match tests in the content nodes * have not been tested yet. They are AND tests. * RFC 4741 is not that clear, but this means the content * match is not ever skipped because of failed match tests * * Check if there are no more tests; If not, all the * child nodes of curval are selected */ if (!anycon && !anysel) { *keepempty = TRUE; return NO_ERR; } /* Go through the filval child nodes again and this * time select nodes for real */ for (filchild = val_get_first_child(filval); filchild != NULL; filchild = val_get_next_child(filchild)) { /* first check if this is a get-config operation * and if so, if the test node fails the config test */ if (!getop && !agt_check_config(ses_withdef(scb), TRUE, filchild)) { continue; } /* go through all the actual instances of 'filchild' * within the child nodes of 'curval' */ for (curchild = val_first_child_qname(useval, filchild->nsid, filchild->name); curchild != NULL; curchild = val_next_child_qname(useval, filchild->nsid, filchild->name, curchild)) { filptr = NULL; #if 0 /* check access control if scb is non-NULL */ if (isnotif && scb && !agt_acm_val_read_allowed(msg, scb->username, curchild)) { continue; } #endif /* check any attr-match tests */ if (!attr_test(filchild, curchild)) { /* failed an attr-match test so skip it */ continue; } /* process the filter child node based on its type */ switch (filchild->btyp) { case NCX_BT_STRING: /* This is a content select node */ test = content_match_test(scb, VAL_STR(filchild), curchild); if (!test) { break; } /* else fall through and add node */ case NCX_BT_EMPTY: /* this is a select node matched to the * current node, so save it */ filptr = save_filptr(result, curchild); if (!filptr) { return ERR_INTERNAL_MEM; } break; case NCX_BT_CONTAINER: /* this is a container node, so the current node * in the target must be a complex type; not a leaf node */ if (!typ_has_children(curchild->btyp)) { break; } /* save this node for now */ filptr = save_filptr(result, curchild); if (!filptr) { return ERR_INTERNAL_MEM; } /* go through the child nodes of the filter * and compare to the complex target */ res = process_val(msg, scb, getop, isnotif, filchild, curchild, filptr, &mykeepempty); if (res != NO_ERR) { return res; } if (!mykeepempty && dlq_empty(&filptr->childQ)) { dlq_remove(filptr); ncx_free_filptr(filptr); filptr = NULL; } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } if (filptr && filptr->node->btyp == NCX_BT_LIST && !dlq_empty(&filptr->childQ)) { /* make sure all the list keys (if any) * are present, since specific nodes * are requested, and the keys could * get filtered out */ for (valindex = val_get_first_index(filptr->node); valindex != NULL; valindex = val_get_next_index(valindex)) { if (!find_filptr(filptr, valindex->val)) { if (!save_filptr(filptr, valindex->val)) { return ERR_INTERNAL_MEM; } } } } } } return NO_ERR; } /* process_val */ /******************************************************************** * FUNCTION output_node * * Output the current node to the specified session * * INPUTS: * scb == session control block * msg == rpc_msg_t in progress * parent == parent filter chain node * indent == start indent amount * getop == TRUE if , FALSE if * * RETURNS: * none *********************************************************************/ static void output_node (ses_cb_t *scb, rpc_msg_t *msg, ncx_filptr_t *parent, int32 indent, boolean getop) { ncx_filptr_t *filptr; val_value_t *val; xmlns_id_t parentnsid, valnsid; int32 indentamount; boolean retval; indentamount = ses_indent_count(scb); for (filptr = (ncx_filptr_t *)dlq_firstEntry(&parent->childQ); filptr != NULL; filptr = (ncx_filptr_t *)dlq_nextEntry(filptr)) { val = filptr->node; valnsid = obj_get_nsid(val->obj); if (val->parent) { parentnsid = obj_get_nsid(val->parent->obj); } else { parentnsid = 0; } if (dlq_empty(&filptr->childQ)) { if (getop) { xml_wr_full_check_val(scb, &msg->mhdr, val, indent, agt_check_default); } else { xml_wr_full_check_val(scb, &msg->mhdr, val, indent, agt_check_config); } } else { /* check if access control is allowing this user * to retrieve this value node */ retval = agt_acm_val_read_allowed(&msg->mhdr, scb->username, val); /* check the child nodes in the filter * If there are container nodes or select nodes * then only those specific nodes will be output * * If there are only content match nodes then * the entire filval node is supposed to be output * * Do not need to check for the wd:default attribute * in a complex node, only leafs */ if (retval) { xml_wr_begin_elem_ex(scb, &msg->mhdr, parentnsid, valnsid, val->name, &val->metaQ, FALSE, indent, FALSE); if (indent >= 0) { indent += indentamount; } output_node(scb, msg, filptr, indent, getop); if (indent >= 0) { indent -= indentamount; } xml_wr_end_elem(scb, &msg->mhdr, valnsid, val->name, indent); } } } } /* output_node */ /******************************************************************** * FUNCTION dump_filptr_node * * Output the ncx_filptr_t node to STDOUT * * INPUTS: * filptr == filptr node to dump * * RETURNS: * none *********************************************************************/ static void dump_filptr_node (ncx_filptr_t *filptr, int32 indent) { ncx_filptr_t *fp; int32 i; if (LOGDEBUG2) { log_debug2("\n"); for (i=0; inode->nsid, filptr->node->name, tk_get_btype_sym(filptr->node->btyp)); for (fp = (ncx_filptr_t *)dlq_firstEntry(&filptr->childQ); fp != NULL; fp = (ncx_filptr_t *)dlq_nextEntry(fp)) { dump_filptr_node(fp, indent+2); } } } /* dump_filptr_node */ /************ E X T E R N A L F U N C T I O N S **************/ /******************************************************************** * FUNCTION agt_tree_prune_filter * * get and get-config step 1 * Need to evaluate the entire subtree filter and remove nodes * which are not in the result set * * The filter subtree usually starts out as type NCX_BT_CONTAINER * but a single select node could be present instead. * * The subtree starts out as type NCX_BT_CONTAINER (root) * and is converted by val_parse as follows: * * Subtree Filter: * NCX_BT_ANY -- start type * changed to real types during parsing * NCX_BT_CONTAINER --> container node * NCX_BT_EMPTY --> select node * NCX_BT_STRING --> content select node * * INPUTS: * scb == session control block * == NULL if access control should not be applied * msg == rpc_msg_t in progress * cfg == config target to check against * getop == TRUE if this is a and not a * The target is expected to be the * config, and all state data will be available for the * filter output. * FALSE if this is a and only the * specified target in available for filter output * * OUTPUTS: * msg->rpc_filter.op_filter is pruned as needed by setting * the VAL_FL_FILTERED bit.in the val->flags field * for the start of subtrees which failed the filter test. * Only nodes which result in non-NULL output should * remain unchanged at the end of this procedure. * * RETURNS: * pointer to generated tree of matching nodes * NULL if no match *********************************************************************/ ncx_filptr_t * agt_tree_prune_filter (ses_cb_t *scb, rpc_msg_t *msg, const cfg_template_t *cfg, boolean getop) { val_value_t *filter; ncx_filptr_t *top; status_t res; boolean keepempty; #ifdef DEBUG if (!msg || !cfg || !msg->rpc_filter.op_filter) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif /* make sure the config has some data in it */ if (!cfg->root) { return NULL; } /* the msg filter should be non-NULL */ filter = msg->rpc_filter.op_filter; /* start at the top with itself */ switch (filter->btyp) { case NCX_BT_EMPTY: /* This is an empty filter element; * This is allowed, but the result is the empty set */ break; case NCX_BT_STRING: /* This is a mixed mode request, which is supposed to * be invalid; In this case it is simply interpreted * as 'not a match', because all NCX data is in XML. * This is not really allowed, and the result is the empty set */ break; case NCX_BT_CONTAINER: /* This is the normal case - a container node * Go through the child nodes. */ top = ncx_new_filptr(); if (!top) { return NULL; } top->node = cfg->root; keepempty = FALSE; res = process_val((msg) ? &msg->mhdr : NULL, scb, getop, FALSE, filter, cfg->root, top, &keepempty); if (res != NO_ERR || dlq_empty(&top->childQ)) { /* ignore keepempty because the result will * be the same w/NULL return, just faster */ ncx_free_filptr(top); } else { dump_filptr_node(top, 0); return top; } break; default: SET_ERROR(ERR_INTERNAL_VAL); } return NULL; } /* agt_tree_prune_filter */ /******************************************************************** * FUNCTION agt_tree_output_filter * * get and get-config step 2 * Output the pruned subtree filter to the specified session * * INPUTS: * scb == session control block * == NULL if access control should not be applied * msg == rpc_msg_t in progress * top == ncx_filptr tree to output * indent == start indent amount * getop == TRUE if , FALSE if * * RETURNS: * none *********************************************************************/ void agt_tree_output_filter (ses_cb_t *scb, rpc_msg_t *msg, ncx_filptr_t *top, int32 indent, boolean getop) { #ifdef DEBUG if (!scb || !msg || !top) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif output_node(scb, msg, top, indent, getop); } /* agt_tree_output_filter */ /******************************************************************** * FUNCTION agt_tree_test_filter * * notification filter evaluation * Need to evaluate the entire subtree filter and return 'FALSE' * if any nodes in the filter are not in the result set * (this is probably a notification payload) * * INPUTS: * msghdr == message in progress; needed for access control * scb == session control block; needed for access control * filter == subtree filter to use * topval == value tree to check against * * RETURNS: * TRUE if the filter matched; notification can be sent * FALSE if the filter did not match; notification not sent *********************************************************************/ boolean agt_tree_test_filter (xml_msg_hdr_t *msghdr, ses_cb_t *scb, val_value_t *filter, val_value_t *topval) { ncx_filptr_t *top; status_t res; boolean keepempty, retval; #ifdef DEBUG if (!msghdr || !scb || !filter || !topval) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif retval = FALSE; /* start at the top with itself */ switch (filter->btyp) { case NCX_BT_EMPTY: /* This is an empty filter element; * This is allowed, but the result is the empty set */ break; case NCX_BT_STRING: /* This is a mixed mode request, which is supposed to * be invalid; In this case it is simply interpreted * as 'not a match', because all NCX data is in XML. * This is not really allowed, and the result is the empty set */ break; case NCX_BT_CONTAINER: /* This is the normal case - a container node * Go through the child nodes. */ top = ncx_new_filptr(); if (!top) { /* dropping ERR_INTERNAL_MEM error */ return FALSE; } top->node = topval; keepempty = FALSE; res = process_val(msghdr, scb, TRUE, TRUE, filter, topval, top, &keepempty); if (res != NO_ERR || dlq_empty(&top->childQ)) { /* ignore keepempty because the result will * be the same w/NULL return, just faster */ ; } else { retval = TRUE; } ncx_free_filptr(top); break; default: SET_ERROR(ERR_INTERNAL_VAL); } return retval; } /* agt_tree_test_filter */ /* END file agt_tree.c */ yuma123_2.14/netconf/src/agt/agt_time_filter.h0000664000175000017500000000260314770023131021431 0ustar vladimirvladimir #ifndef _H_agt_time_filter #define _H_agt_time_filter /* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *** Originally generated by yangdump 2.0.1297 module yuma-time-filter revision 2012-11-15 namespace http://netconfcentral.org/ns/yuma-time-filter organization Netconf Central */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif #define y_yuma_time_filter_M_yuma_time_filter \ (const xmlChar *)"yuma-time-filter" #define y_yuma_time_filter_R_yuma_time_filter (const xmlChar *)"2012-11-15" /* yuma-time-filter module init 1 */ extern status_t y_yuma_time_filter_init ( const xmlChar *modname, const xmlChar *revision); /* yuma-time-filter module init 2 */ extern status_t y_yuma_time_filter_init2 (void); /* yuma-time-filter module cleanup */ extern void y_yuma_time_filter_cleanup (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif yuma123_2.14/netconf/src/agt/agt_ses.h0000664000175000017500000003515014770023131017723 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_ses #define _H_agt_ses /* FILE: agt_ses.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Session Server definitions module ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 06-jun-06 abb Begun. */ #include #ifndef _H_getcb #include "getcb.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_ses_init * * INIT 1: * Initialize the session manager module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_ses_init (void); /******************************************************************** * FUNCTION agt_ses_cleanup * * Cleanup the session manager module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void agt_ses_cleanup (void); /******************************************************************** * FUNCTION agt_ses_new_dummy_session * * Create a dummy session control block * * INPUTS: * none * RETURNS: * pointer to initialized dummy SCB, or NULL if malloc error *********************************************************************/ extern ses_cb_t * agt_ses_new_dummy_session (void); /******************************************************************** * FUNCTION agt_ses_set_dummy_session_acm * * Set the session ID and username of the user that * will be responsible for the rollback if needed * * INPUTS: * dummy_session == session control block to change * use_sid == Session ID to use for the rollback * * RETURNS: * status *********************************************************************/ extern status_t agt_ses_set_dummy_session_acm (ses_cb_t *dummy_session, ses_id_t use_sid); /******************************************************************** * FUNCTION agt_ses_free_dummy_session * * Free a dummy session control block * * INPUTS: * scb == session control block to free * *********************************************************************/ extern void agt_ses_free_dummy_session (ses_cb_t *scb); /******************************************************************** * FUNCTION agt_ses_new_session * * Create a real server session control block * * INPUTS: * transport == the transport type * fd == file descriptor number to use for IO * RETURNS: * pointer to initialized SCB, or NULL if some error * This pointer is stored in the session table, so it does * not have to be saved by the caller *********************************************************************/ extern ses_cb_t * agt_ses_new_session (ses_transport_t transport, int fd); /******************************************************************** * FUNCTION agt_ses_free_session * * Free a real session control block * * INPUTS: * scb == session control block to free * *********************************************************************/ extern void agt_ses_free_session (ses_cb_t *scb); /******************************************************************** * FUNCTION agt_ses_session_id_valid * * Check if a session-id is for an active session * * INPUTS: * sid == session ID to check * RETURNS: * TRUE if active session; FALSE otherwise *********************************************************************/ extern boolean agt_ses_session_id_valid (ses_id_t sid); /******************************************************************** * FUNCTION agt_ses_request_close * * Start the close of the specified session * * INPUTS: * scb == the session to close * killedby == session ID executing the kill-session or close-session * termreason == termination reason code * * RETURNS: * none *********************************************************************/ extern void agt_ses_request_close (ses_cb_t *scb, ses_id_t killedby, ses_term_reason_t termreason); /******************************************************************** * FUNCTION agt_ses_kill_session * * Kill the specified session * * INPUTS: * scb == the session to close * killedby == session ID executing the kill-session or close-session * termreason == termination reason code * * RETURNS: * none *********************************************************************/ extern void agt_ses_kill_session (ses_cb_t *scb, ses_id_t killedby, ses_term_reason_t termreason); /******************************************************************** * FUNCTION agt_ses_process_first_ready * * Check the readyQ and process the first message, if any * * RETURNS: * TRUE if a message was processed * FALSE if the readyQ was empty *********************************************************************/ extern boolean agt_ses_process_first_ready (void); /******************************************************************** * FUNCTION agt_ses_check_timeouts * * Check if any sessions need to be dropped because they * have been idle too long. * *********************************************************************/ extern void agt_ses_check_timeouts (void); /******************************************************************** * FUNCTION agt_ses_ssh_port_allowed * * Check if the port number used for SSH connect is okay * * RETURNS: * TRUE if port allowed * FALSE if port not allowed *********************************************************************/ extern boolean agt_ses_ssh_port_allowed (uint16 port); /******************************************************************** * FUNCTION agt_ses_fill_writeset * * Drain the ses_msg outreadyQ and set the specified fdset * Used by agt_ncxserver write_fd_set * * INPUTS: * fdset == pointer to fd_set to fill * maxfdnum == pointer to max fd int to fill in * * OUTPUTS: * *fdset is updated in * *maxfdnum may be updated *********************************************************************/ extern void agt_ses_fill_writeset (fd_set *fdset, int *maxfdnum); /******************************************************************** * FUNCTION agt_ses_get_inSessions * * operation handler for the inSessions counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ extern status_t agt_ses_get_inSessions (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); /******************************************************************** * FUNCTION agt_ses_get_inBadHellos * * operation handler for the inBadHellos counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ extern status_t agt_ses_get_inBadHellos (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); /******************************************************************** * FUNCTION agt_ses_get_inRpcs * * operation handler for the inRpcs counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ extern status_t agt_ses_get_inRpcs (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); /******************************************************************** * FUNCTION agt_ses_get_inBadRpcs * * operation handler for the inBadRpcs counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ extern status_t agt_ses_get_inBadRpcs (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); /******************************************************************** * FUNCTION agt_ses_get_outRpcErrors * * operation handler for the outRpcErrors counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ extern status_t agt_ses_get_outRpcErrors (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); /******************************************************************** * FUNCTION agt_ses_get_outNotifications * * operation handler for the outNotifications counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ extern status_t agt_ses_get_outNotifications (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); /******************************************************************** * FUNCTION agt_ses_get_droppedSessions * * operation handler for the droppedSessions counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ extern status_t agt_ses_get_droppedSessions (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); /******************************************************************** * FUNCTION agt_ses_get_session_inRpcs * * operation handler for the session/inRpcs counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ extern status_t agt_ses_get_session_inRpcs (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); /******************************************************************** * FUNCTION agt_ses_get_session_inBadRpcs * * operation handler for the inBadRpcs counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ extern status_t agt_ses_get_session_inBadRpcs (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); /******************************************************************** * FUNCTION agt_ses_get_session_outRpcErrors * * operation handler for the outRpcErrors counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ extern status_t agt_ses_get_session_outRpcErrors (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); /******************************************************************** * FUNCTION agt_ses_get_session_outNotifications * * operation handler for the outNotifications counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ extern status_t agt_ses_get_session_outNotifications (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); /******************************************************************** * FUNCTION agt_ses_invalidate_session_acm_caches * * Invalidate all session ACM caches so they will be rebuilt * TBD:: optimize and figure out exactly what needs to change * *********************************************************************/ extern void agt_ses_invalidate_session_acm_caches (void); /******************************************************************** * FUNCTION agt_ses_get_session_for_id * * get the session for the supplied sid * * INPUTS: * ses_id_t the id of the session to get * * RETURNS: * ses_cb_t* or NULL * *********************************************************************/ extern ses_cb_t* agt_ses_get_session_for_id(ses_id_t sid); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_ses */ yuma123_2.14/netconf/src/agt/agt_nmda.h0000664000175000017500000000047314770023131020050 0ustar vladimirvladimir#include "ncxtypes.h" #include "val.h" val_value_t* agt_nmda_get_root_operational(void); val_value_t* agt_nmda_get_root_system(void); val_value_t* agt_nmda_get_root_learned(void); /* module initialization and cleanup */ status_t agt_nmda_init (void); status_t agt_nmda_init2 (void); void agt_nmda_cleanup (void); yuma123_2.14/netconf/src/agt/agt_commit_complete.h0000664000175000017500000000623314770023131022311 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_commit_complete_cb #define _H_agt_commit_complete_cb /* FILE: agt_cb.h ********************************************************************* * P U R P O S E ********************************************************************* NETCONF Server Commit Complete callback handler This file contains functions to support registering, unregistering and execution of commit complete callbacks. ********************************************************************* * C H A N G E H I S T O R Y ********************************************************************* date init comment ---------------------------------------------------------------------- 24-oct-11 mp First draft. */ #include "procdefs.h" #include "status.h" #include "ncxtypes.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * T Y P E D E F S *********************************************************************/ /** Typedef of the commit_complete callback */ typedef status_t (*agt_commit_complete_cb_t)(void); /******************************************************************** * F U N C T I O N S *********************************************************************/ /** * Initialise the callback commit module. */ extern void agt_commit_complete_init( void ); /** * Cleanup the callback commit module. */ extern void agt_commit_complete_cleanup( void ); /** * Register a commit complete callback. * This function registers a commit-complete callback that will be * called after all changes to the candidate database have been * committed. The function will be called after that final SIL commit * operation. If a commit complete operation is already registered for * the module it will be replaced. * * \param modname the name of the module registering the callback * \param cb the commit complete function. * \return the status of the operation. */ extern status_t agt_commit_complete_register( const xmlChar *modname, agt_commit_complete_cb_t cb ); /** * Unregister a commit complete callback. * This function unregisters a commit-complete callback. * * \param modname the name of the module unregistering the callback */ extern void agt_commit_complete_unregister( const xmlChar *modname ); /** * Complete a commit operation. * This function simply calls each registered commit complete * callback. If a commit complete operation fails the status of the * failing operation is returned immediately and no further commit * complete callbacks are made. * * \return the ERR_OK or the status of the first failing callback. */ extern status_t agt_commit_complete( void ); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif // _H_agt_commit_complete_cb yuma123_2.14/netconf/src/agt/agt_top.c0000664000175000017500000001432514770023131017727 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_top.c NCX Agent Top Element Handler This module uses a simple queue of top-level entries because there are not likely to be very many of them. Each top-level node is keyed by the owner name and the element name. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30dec05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_ses.h" #include "agt_top.h" #include "agt_xml.h" #include "dlq.h" #include "log.h" #include "ncxconst.h" #include "ncx.h" #include "status.h" #include "top.h" #include "xmlns.h" #include "xml_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION agt_top_dispatch_msg * * Find the appropriate top node handler and call it * called by the transport manager (through the session manager) * when a new message is detected * * INPUTS: * scb == session control block containing the xmlreader * set at the start of an incoming message. * * RETURNS: * none *********************************************************************/ void agt_top_dispatch_msg (ses_cb_t **ppscb) { ses_total_stats_t *myagttotals; agt_profile_t *profile; xml_node_t top; status_t res; top_handler_t handler; ses_cb_t *scb = *ppscb; #ifdef DEBUG if (!scb) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif myagttotals = ses_get_total_stats(); profile = agt_get_profile(); xml_init_node(&top); /* get the first node */ res = agt_xml_consume_node(scb, &top, NCX_LAYER_TRANSPORT, NULL); if (res != NO_ERR) { scb->stats.inBadRpcs++; myagttotals->stats.inBadRpcs++; myagttotals->droppedSessions++; if (LOGINFO) { log_info("\nagt_top: bad msg for session %d (%s)", scb->sid, get_error_string(res)); } xml_clean_node(&top); agt_ses_free_session(scb); /* set the supplied ptr to ptr to scb to NULL so that the * caller of this function knows that it was deallotcated */ *ppscb=NULL; return; } log_debug3("\nagt_top: got node"); if (LOGDEBUG4 && scb->state != SES_ST_INIT) { xml_dump_node(&top); } /* check node type and if handler exists, then call it */ if (top.nodetyp==XML_NT_START || top.nodetyp==XML_NT_EMPTY) { /* find the owner, elname tuple in the topQ */ handler = top_find_handler(top.module, top.elname); if (handler) { /* call the handler */ (*handler)(scb, &top); } else { res = ERR_NCX_DEF_NOT_FOUND; } } else { res = ERR_NCX_WRONG_NODETYP; } /* check any error trying to invoke the top handler */ if (res != NO_ERR) { scb->stats.inBadRpcs++; myagttotals->stats.inBadRpcs++; myagttotals->droppedSessions++; if (LOGINFO) { log_info("\nagt_top: bad msg for session %d (%s)", scb->sid, get_error_string(res)); } agt_ses_free_session(scb); /* set the supplied ptr to ptr to scb to NULL so that the * caller of this function knows that it was deallotcated */ *ppscb=NULL; } else if (profile->agt_stream_output && scb->state == SES_ST_SHUTDOWN_REQ) { /* session was closed */ agt_ses_kill_session(scb, scb->killedbysid, scb->termreason); /* set the supplied ptr to ptr to scb to NULL so that the * caller of this function knows that it was deallotcated */ *ppscb=NULL; } xml_clean_node(&top); } /* agt_top_dispatch_msg */ /* END file agt_top.c */ yuma123_2.14/netconf/src/agt/agt_rpc.c0000664000175000017500000017177614770023131017727 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_rpc.c NETCONF Protocol Operations: RPC Agent Side Support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17may05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "agt_acm.h" #include "agt_cfg.h" #include "agt_cli.h" #include "agt_rpc.h" #include "agt_rpcerr.h" #include "agt_ses.h" #include "agt_sys.h" #include "agt_util.h" #include "agt_val.h" #include "agt_val_parse.h" #include "agt_xml.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "ncx_num.h" #include "ncxconst.h" #include "obj.h" #include "rpc.h" #include "rpc_err.h" #include "ses.h" #include "status.h" #include "top.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_msg.h" #include "xml_util.h" #include "xml_wr.h" #include "yangconst.h" #include "uptime.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* hack for for a couple corner case errors */ #define RPC_ROOT ((const xmlChar *)"/nc:rpc") /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_rpc_init_done = FALSE; /******************************************************************** * FUNCTION free_msg * * Free an rpc_msg_t * * INPUTS: * msg == rpc_msg_t to delete *********************************************************************/ static void free_msg (rpc_msg_t *msg) { agt_cfg_free_transaction(msg->rpc_txcb); rpc_free_msg(msg); } /* free_msg */ /******************************************************************** * FUNCTION send_rpc_error_info * * Send one element on the specified session * print all the rpc_err_info_t records as child nodes * * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * err == error record to use for an error_info Q * indent == indent amount * * RETURNS: * none *********************************************************************/ static void send_rpc_error_info (ses_cb_t *scb, xml_msg_hdr_t *msg, const rpc_err_rec_t *err, int32 indent) { rpc_err_info_t *errinfo; status_t res; xmlns_id_t ncid; uint32 len; xmlChar numbuff[NCX_MAX_NUMLEN]; /* check if there is any error_info data */ if (dlq_empty(&err->error_info)) { return; } /* init local vars */ ncid = xmlns_nc_id(); /* generate the start tag */ xml_wr_begin_elem_ex(scb, msg, ncid, ncid, NCX_EL_ERROR_INFO, NULL, FALSE, indent, START); if (indent >= 0) { indent += ses_indent_count(scb); } /* generate each child variable */ for (errinfo = (rpc_err_info_t *) dlq_firstEntry(&err->error_info); errinfo != NULL; errinfo = (rpc_err_info_t *)dlq_nextEntry(errinfo)) { switch (errinfo->val_btype) { case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: if (errinfo->v.strval) { if (errinfo->isqname) { xml_wr_qname_elem(scb, msg, errinfo->val_nsid, errinfo->v.strval, ncid, errinfo->name_nsid, errinfo->name, NULL, FALSE, indent, FALSE); } else { xml_wr_string_elem(scb, msg, errinfo->v.strval, ncid, errinfo->name_nsid, errinfo->name, NULL, FALSE, indent); } } else { SET_ERROR(ERR_INTERNAL_PTR); } break; case NCX_BT_DECIMAL64: case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_FLOAT64: res = ncx_sprintf_num(numbuff, &errinfo->v.numval, errinfo->val_btype, &len); if (res == NO_ERR) { xml_wr_string_elem(scb, msg, numbuff, ncid, errinfo->name_nsid, errinfo->name, NULL, FALSE, indent); } else { SET_ERROR(res); } break; case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_BITS: case NCX_BT_ENUM: case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: case NCX_BT_BINARY: case NCX_BT_UNION: case NCX_BT_INTERN: case NCX_BT_EXTERN: case NCX_BT_IDREF: case NCX_BT_SLIST: SET_ERROR(ERR_INTERNAL_VAL); break; default: if (typ_is_simple(errinfo->val_btype)) { SET_ERROR(ERR_INTERNAL_VAL); } else if (errinfo->v.cpxval) { xml_wr_full_val(scb, msg, errinfo->v.cpxval, indent); } else { SET_ERROR(ERR_INTERNAL_PTR); } } } if (indent >= 0) { indent -= ses_indent_count(scb); } /* generate the end tag */ xml_wr_end_elem(scb, msg, ncid, NCX_EL_ERROR_INFO, indent); } /* send_rpc_error_info */ /******************************************************************** * FUNCTION send_rpc_error * * Send one element on the specified session * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * err == error record to send * indent == starting indent count * * RETURNS: * status *********************************************************************/ static status_t send_rpc_error (ses_cb_t *scb, xml_msg_hdr_t *msg, const rpc_err_rec_t *err, int32 indent) { status_t res, retres; xmlns_id_t ncid; rpc_err_info_t *errinfo; xml_attrs_t attrs; xmlChar buff[12]; /* init local vars */ retres = NO_ERR; ncid = xmlns_nc_id(); /* check if any XMLNS decls need to be added to * the element */ xml_init_attrs(&attrs); for (errinfo = (rpc_err_info_t *) dlq_firstEntry(&err->error_info); errinfo != NULL; errinfo = (rpc_err_info_t *)dlq_nextEntry(errinfo)) { res = xml_msg_check_xmlns_attr(msg, errinfo->name_nsid, errinfo->badns, &attrs); if (res == NO_ERR) { res = xml_msg_check_xmlns_attr(msg, errinfo->val_nsid, errinfo->badns, &attrs); } if (res != NO_ERR) { SET_ERROR(res); /***/ retres = res; } } /* generate the start tag */ xml_wr_begin_elem_ex(scb, msg, ncid, ncid, NCX_EL_RPC_ERROR, &attrs, ATTRQ, indent, START); xml_clean_attrs(&attrs); if (indent >= 0) { indent += ses_indent_count(scb); } /* generate the field */ xml_wr_string_elem(scb, msg, ncx_get_layer(err->error_type), ncid, ncid, NCX_EL_ERROR_TYPE, NULL, FALSE, indent); /* generate the field */ xml_wr_string_elem(scb, msg, err->error_tag, ncid, ncid, NCX_EL_ERROR_TAG, NULL, FALSE, indent); /* generate the field */ xml_wr_string_elem(scb, msg, rpc_err_get_severity(err->error_severity), ncid, ncid, NCX_EL_ERROR_SEVERITY, NULL, FALSE, indent); /* generate the field */ if (err->error_app_tag) { xml_wr_string_elem(scb, msg, err->error_app_tag, ncid, ncid, NCX_EL_ERROR_APP_TAG, NULL, FALSE, indent); } else if (err->error_res != NO_ERR) { /* use the internal error code instead */ *buff = 0; snprintf((char *)buff, sizeof(buff), "%u", err->error_res); if (*buff) { xml_wr_string_elem(scb, msg, buff, ncid, ncid, NCX_EL_ERROR_APP_TAG, NULL, FALSE, indent); } } /* generate the field */ if (err->error_path) { xml_wr_string_elem(scb, msg, err->error_path, ncid, ncid, NCX_EL_ERROR_PATH, NULL, FALSE, indent); } /* generate the field */ if (err->error_message) { /* see if there is a xml:lang attribute */ if (err->error_message_lang) { res = xml_add_attr(&attrs, 0, NCX_EL_LANG, err->error_message_lang); if (res != NO_ERR) { SET_ERROR(res); retres = res; } } xml_wr_string_elem(scb, msg, err->error_message, ncid, ncid, NCX_EL_ERROR_MESSAGE, &attrs, TRUE, indent); xml_clean_attrs(&attrs); } /* print all the elements */ send_rpc_error_info(scb, msg, err, indent); if (indent >= 0) { indent -= ses_indent_count(scb); } /* generate the end tag */ xml_wr_end_elem(scb, msg, ncid, NCX_EL_RPC_ERROR, indent); return retres; } /* send_rpc_error */ /******************************************************************** * FUNCTION check_add_changed_since_attr * * Check if the changed-since attribute should be added to the * message attributes * * INPUTS: * msg == rpc_msg_t in progress * * RETURNS: * status *********************************************************************/ static status_t check_add_changed_since_attr (rpc_msg_t *msg) { const xmlChar *rpcname; xmlns_id_t ncid = xmlns_nc_id(); if (msg->rpc_method == NULL) { return NO_ERR; } if (obj_get_nsid(msg->rpc_method) != ncid) { /* not the NETCONF module */ return NO_ERR; } rpcname = obj_get_name(msg->rpc_method); /* check if or */ if (!xml_strcmp(rpcname, NCX_EL_GET) || !xml_strcmp(rpcname, NCX_EL_GET_CONFIG)) { cfg_template_t *cfg; xml_attr_t *attr; status_t res; /* !!! this code is coupled to agt_ncx.c get_validate * !!! and get_config_validate. Those functions set * !!! the rpc_user1 field to the config target if * !!! everything went well. Only add last-modified * !!! if rpc_user1 is set */ if (msg->rpc_user1 == NULL) { return NO_ERR; } cfg = (cfg_template_t *)msg->rpc_user1; /* check if this attr already present in rpc_in_attrs */ attr = xml_find_attr_q(msg->rpc_in_attrs, 0, NCX_EL_LAST_MODIFIED); if (attr != NULL) { return NO_ERR; } /* make a new attr and add it to rpc_in_attrs */ res = xml_add_attr(msg->rpc_in_attrs, 0, NCX_EL_LAST_MODIFIED, cfg->last_ch_time); return res; } return NO_ERR; } /* check_add_changed_since_attr */ /******************************************************************** * FUNCTION send_rpc_reply * * Operation succeeded or failed * Return an * * INPUTS: * scb == session control block * msg == rpc_msg_t in progress * *********************************************************************/ static void send_rpc_reply (ses_cb_t *scb, rpc_msg_t *msg) { agt_rpc_data_cb_t agtcb; const rpc_err_rec_t *err; val_value_t *val; ses_total_stats_t *agttotals; status_t res; xmlns_id_t ncid, rpcid; uint64 outbytes; boolean datasend, datawritten, errsend; int32 indent, indentamount; res = ses_start_msg(scb); if (res != NO_ERR) { return; } agttotals = ses_get_total_stats(); errsend = !dlq_empty(&msg->mhdr.errQ); res = xml_msg_clean_defns_attr(msg->rpc_in_attrs); if (res == NO_ERR) { res = check_add_changed_since_attr(msg); } if (res == NO_ERR) { res = xml_msg_gen_xmlns_attrs(&msg->mhdr, msg->rpc_in_attrs, errsend); } if (res != NO_ERR) { ses_finish_msg(scb); return; } ncid = xmlns_nc_id(); if (msg->rpc_method) { rpcid = obj_get_nsid(msg->rpc_method); } else { rpcid = ncid; } indentamount = ses_indent_count(scb); indent = indentamount; /* generate the start tag */ xml_wr_begin_elem_ex(scb, &msg->mhdr, 0, ncid, NCX_EL_RPC_REPLY, msg->rpc_in_attrs, ATTRQ, 0, START); /* check if there is data to send */ datasend = (!dlq_empty(&msg->rpc_dataQ) || msg->rpc_datacb) ? TRUE : FALSE; /* check which reply variant needs to be sent */ if (dlq_empty(&msg->mhdr.errQ) && !datasend) { /* no errors and no data, so send variant */ xml_wr_empty_elem(scb, &msg->mhdr, ncid, ncid, NCX_EL_OK, indent); } else { /* send rpcResponse variant * 0 or elements followed by * 0 to N data elements, specified in the foo-rpc/output * * first send all the buffered errors */ for (err = (const rpc_err_rec_t *) dlq_firstEntry(&msg->mhdr.errQ); err != NULL; err = (const rpc_err_rec_t *)dlq_nextEntry(err)) { res = send_rpc_error(scb, &msg->mhdr, err, indent); if (res != NO_ERR) { SET_ERROR(res); } } /* check generation of data contents * if the element * If the rpc_dataQ is non-empty, then there is * staticly filled data to send * If the rpc_datacb function pointer is non-NULL, * then this is dynamic reply content, even if the rpc_data * node is not NULL */ if (datasend) { if (msg->rpc_data_type == RPC_DATA_STD) { xml_wr_begin_elem(scb, &msg->mhdr, ncid, rpcid, NCX_EL_DATA, indent); if (indent > 0) { indent += ses_indent_count(scb); } } outbytes = SES_OUT_BYTES(scb); if (msg->rpc_datacb) { /* use user callback to generate the contents */ agtcb = (agt_rpc_data_cb_t)msg->rpc_datacb; res = (*agtcb)(scb, msg, indent); if (res != NO_ERR) { log_error("\nError: SIL data callback failed (%s)", get_error_string(res)); } } else { /* just write the contents of the rpc varQ */ for (val = (val_value_t *) dlq_firstEntry(&msg->rpc_dataQ); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { xml_wr_full_val(scb, &msg->mhdr, val, indent); } } /* only indent the end tag if any data written */ datawritten = (outbytes != SES_OUT_BYTES(scb)); if (msg->rpc_data_type == RPC_DATA_STD) { if (indent > 0) { indent -= ses_indent_count(scb); } xml_wr_end_elem(scb, &msg->mhdr, rpcid, NCX_EL_DATA, (datawritten) ? indent : -1); } } } /* generate the end tag */ xml_wr_end_elem(scb, &msg->mhdr, ncid, NCX_EL_RPC_REPLY, 0); /* finish the message */ ses_finish_msg(scb); if (errsend) { scb->stats.inBadRpcs++; agttotals->stats.inBadRpcs++; scb->stats.outRpcErrors++; agttotals->stats.outRpcErrors++; } else { scb->stats.inRpcs++; agttotals->stats.inRpcs++; } } /* send_rpc_reply */ /******************************************************************** * FUNCTION find_rpc * * Find the specified rpc_template_t * * INPUTS: * modname == module name containing the RPC to find * rpcname == RPC method name to find * * RETURNS: * pointer to retrieved obj_template_t or NULL if not found *********************************************************************/ static obj_template_t * find_rpc (const xmlChar *modname, const xmlChar *rpcname) { ncx_module_t *mod; obj_template_t *rpc; /* look for the RPC method in the definition registry */ rpc = NULL; mod = ncx_find_module(modname, NULL); if (mod) { rpc = ncx_find_rpc(mod, rpcname); } return rpc; } /* find_rpc */ /******************************************************************** * FUNCTION parse_rpc_input * * RPC received, parse parameters against rpcio for 'input' * * INPUTS: * scb == session control block * msg == rpc_msg_t in progress rpcobj == RPC object template to use * method == method node * * RETURNS: * status *********************************************************************/ static status_t parse_rpc_input (ses_cb_t *scb, rpc_msg_t *msg, obj_template_t *rpcobj, xml_node_t *method) { obj_template_t *obj; status_t res; res = NO_ERR; /* check if there is an input parmset specified */ obj = obj_find_template(obj_get_datadefQ(rpcobj), NULL, YANG_K_INPUT); if (obj && obj_get_child_count(obj)) { msg->rpc_agt_state = AGT_RPC_PH_PARSE; res = agt_val_parse_nc(scb, &msg->mhdr, obj, method, NCX_DC_CONFIG, msg->rpc_input); if (LOGDEBUG3) { log_debug3("\nagt_rpc: parse RPC input state"); rpc_err_dump_errors(msg); val_dump_value(msg->rpc_input, 0); } } return res; } /* parse_rpc_input */ /******************************************************************** * FUNCTION post_psd_state * * Fixup parmset after parse phase * Only call if the RPC has an input PS * * INPUTS: * scb == session control block * msg == rpc_msg_t in progress * psdres == result from PSD state * * RETURNS: * status *********************************************************************/ static status_t post_psd_state (ses_cb_t *scb, rpc_msg_t *msg, status_t psdres) { status_t res; res = NO_ERR; /* at this point the input is completely parsed and * the syntax is valid. Now add any missing defaults * to the RPC Parmset. */ if (psdres == NO_ERR) { /*** need to keep errors for a bit so this function *** can be called. Right now, child errors are not *** added to the value tree, so val_add_defaults *** will add a default where there was an error before ***/ res = val_add_defaults(msg->rpc_input, NULL, NULL, FALSE); } if (res == NO_ERR) { /* check for any false when-stmts on nodes * in the rpc/input section and flag them * as unexpected-element errors */ res = agt_val_rpc_xpath_check(scb, msg, &msg->mhdr, msg->rpc_input, msg->rpc_method); } if (res == NO_ERR) { /* check that the number of instances of the parameters * is reasonable and only one member from any choice is * present (if applicable) * * Make sure to check choice before instance because * any missing choices would be incorrectly interpreted * as multiple missing parameter errors */ res = agt_val_instance_check(scb, &msg->mhdr, msg->rpc_input, msg->rpc_input, NCX_LAYER_OPERATION); } return res; } /* post_psd_state */ /******************************************************************** * FUNCTION load_config_file * * Dispatch an internal request * used for OP_EDITOP_LOAD to load the running from startup * and OP_EDITOP_REPLACE to restore running from backup * * - Create a dummy session and RPC message * - Create a config transaction control block * - Call a special agt_val_parse function to parse the config file * - Call the special agt_ncx function to invoke the proper * parmset and application 'validate' callback functions, and * record all the error/warning messages * - Call the special agt_ncx function to invoke all the 'apply' * callbacks as needed * - transfer any error messages to the cfg->load_errQ * - Dispose the transaction CB (do not send to audit) * INPUTS: * filespec == XML config filespec to load * cfg == cfg_template_t to fill in * isload == TRUE for normal load-config * FALSE for restore backup load-config * use_sid == session ID to use for the access control * justval == TRUE if just the element is needed * configval == address of return config val * errorQ == address of Q to hold errors if justval is TRUE * * OUTPUTS: * *configval set to the processed config node * if NO_ERR, it has been removed from the rpc_input * data structure and needs to be freed by the caller * errorQ contains any rpc_err_rec_t structs that were * generated during this RPC operation * * RETURNS: * status *********************************************************************/ static status_t load_config_file (const xmlChar *filespec, cfg_template_t *cfg, boolean isload, ses_id_t use_sid, boolean justval, val_value_t **configval, dlq_hdr_t *errorQ) { /* first make sure the load-config RPC is registered */ obj_template_t *rpcobj = find_rpc(NC_MODULE, NCX_EL_LOAD_CONFIG); if (!rpcobj) { return SET_ERROR(ERR_INTERNAL_VAL); } /* make sure the agent callback set for this RPC is ok */ agt_rpc_cbset_t *cbset = (agt_rpc_cbset_t *)rpcobj->rpc_cbset; if (!cbset) { return SET_ERROR(ERR_INTERNAL_VAL); } /* make sure no transaction already in progress unless it is a * rollback by the system (SID==0) */ if (agt_cfg_txid_in_progress(NCX_CFGID_RUNNING) && use_sid) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } /* create a dummy session control block */ ses_cb_t *scb = agt_ses_new_dummy_session(); if (!scb) { return ERR_INTERNAL_MEM; } status_t retres = NO_ERR; /* set the ACM profile to use for rollback */ status_t res = agt_ses_set_dummy_session_acm(scb, use_sid); if (res != NO_ERR) { agt_ses_free_dummy_session(scb); return res; } /* create a dummy RPC msg */ rpc_msg_t *msg = rpc_new_msg(); if (!msg) { agt_ses_free_dummy_session(scb); return ERR_INTERNAL_MEM; } /* setup the config file as the xmlTextReader input */ res = xml_get_reader_from_filespec((const char *)filespec, &scb->reader); if (res != NO_ERR) { free_msg(msg); agt_ses_free_dummy_session(scb); return res; } msg->rpc_in_attrs = NULL; /* create a dummy method XML node */ xml_node_t method; xml_init_node(&method); method.nodetyp = XML_NT_START; method.nsid = xmlns_nc_id(); method.module = NC_MODULE; method.qname = xml_strdup(NCX_EL_LOAD_CONFIG); if (method.qname == NULL) { free_msg(msg); agt_ses_free_dummy_session(scb); return ERR_INTERNAL_MEM; } method.elname = NCX_EL_LOAD_CONFIG; method.depth = 1; msg->rpc_method = rpcobj; msg->rpc_user1 = cfg; msg->rpc_err_option = OP_ERROP_CONTINUE; if (isload) { msg->rpc_top_editop = OP_EDITOP_LOAD; } else { msg->rpc_top_editop = OP_EDITOP_REPLACE; } /* parse the config file as a root object */ res = parse_rpc_input(scb, msg, rpcobj, &method); if (res != NO_ERR) { retres = res; } if (!(NEED_EXIT(res) || res==ERR_XML_READER_EOF)) { /* keep going if there were errors in the input * in case more errors can be found or * continue-on-error is in use * Each value node has a status field (val->res) * which will retain the error status */ res = post_psd_state(scb, msg, res); if (res != NO_ERR) { retres = res; } } if (retres != NO_ERR) { agt_profile_t *profile = agt_get_profile(); if (NEED_EXIT(retres)) { res = retres; // force exit log_error("\nError: Parse failed with fatal errors"); } else if (profile->agt_startup_error) { res = retres; // force exit log_error("\nError: Parse failed and " "--startup-error=stop"); } else { log_warn("\nWarning: Parse failed and " "--startup-error=continue"); msg->rpc_parse_errors = TRUE; retres = NO_ERR; } } /* call all the object validate callbacks * but only if there were no parse errors so far */ boolean valdone = FALSE; if (res == NO_ERR && cbset->acb[AGT_RPC_PH_VALIDATE]) { msg->rpc_agt_state = AGT_RPC_PH_VALIDATE; res = (*cbset->acb[AGT_RPC_PH_VALIDATE])(scb, msg, &method); if (res != NO_ERR) { retres = res; } else { valdone = TRUE; } } /* get rid of the error nodes after the validation and instance * checks are done; must checks and unique checks should also * be done by now, * Also set the canonical order for the root node */ if (valdone) { val_value_t *testval = val_find_child(msg->rpc_input, NULL, NCX_EL_CONFIG); if (testval) { //val_purge_errors_from_root(testval); !! already done val_set_canonical_order(testval); if (justval) { /* remove this node and return it */ val_remove_child(testval); *configval = testval; } } } /* call all the object invoke callbacks, only callbacks for valid * subtrees will be called; skip if valonly mode */ if (!justval && valdone && cbset->acb[AGT_RPC_PH_INVOKE]) { msg->rpc_agt_state = AGT_RPC_PH_INVOKE; res = (*cbset->acb[AGT_RPC_PH_INVOKE])(scb, msg, &method); if (res != NO_ERR) { /*** DO NOT ROLLBACK, JUST CONTINUE !!!! ***/ retres = res; } } if (LOGDEBUG) { if (!dlq_empty(&msg->mhdr.errQ)) { rpc_err_dump_errors(msg); } } /* move any error messages to the config error Q */ dlq_block_enque(&msg->mhdr.errQ, errorQ); /* cleanup and exit */ xml_clean_node(&method); free_msg(msg); agt_ses_free_dummy_session(scb); return retres; } /* load_config_file */ /************** E X T E R N A L F U N C T I O N S ***************/ /******************************************************************** * FUNCTION agt_rpc_init * * Initialize the agt_rpc module * Adds the agt_rpc_dispatch function as the handler * for the NETCONF top-level element. * should call once to init RPC agent module * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ status_t agt_rpc_init (void) { status_t res; if (!agt_rpc_init_done) { res = top_register_node(NC_MODULE, NCX_EL_RPC, agt_rpc_dispatch); if (res != NO_ERR) { return res; } agt_rpc_init_done = TRUE; } return NO_ERR; } /* agt_rpc_init */ /******************************************************************** * FUNCTION agt_rpc_cleanup * * Cleanup the agt_rpc module. * Unregister the top-level NETCONF element * should call once to cleanup RPC agent module * *********************************************************************/ void agt_rpc_cleanup (void) { if (agt_rpc_init_done) { top_unregister_node(NC_MODULE, NCX_EL_RPC); agt_rpc_init_done = FALSE; } } /* agt_rpc_cleanup */ /******************************************************************** * FUNCTION agt_rpc_register_method * * add callback for 1 phase of RPC processing * * INPUTS: * module == module name or RPC method * method_name == RPC method name * phase == RPC agent callback phase for this callback * method == pointer to callback function * * RETURNS: * status of the operation *********************************************************************/ status_t agt_rpc_register_method (const xmlChar *module, const xmlChar *method_name, agt_rpc_phase_t phase, agt_rpc_method_t method) { obj_template_t *rpcobj; agt_rpc_cbset_t *cbset; #ifdef DEBUG if (!module || !method_name || !method) { return SET_ERROR(ERR_INTERNAL_PTR); } if (phase >= AGT_RPC_NUM_PHASES) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif /* find the RPC template */ rpcobj = find_rpc(module, method_name); if (!rpcobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* get the agent CB set, or create a new one */ if (rpcobj->rpc_cbset) { cbset = (agt_rpc_cbset_t *)rpcobj->rpc_cbset; } else { cbset = m__getObj(agt_rpc_cbset_t); if (!cbset) { return SET_ERROR(ERR_INTERNAL_MEM); } memset(cbset, 0x0, sizeof(agt_rpc_cbset_t)); rpcobj->rpc_cbset = (void *)cbset; } rpcobj->def.rpc->supported = TRUE; /* add the specified method */ cbset->acb[phase] = method; return NO_ERR; } /* agt_rpc_register_method */ /******************************************************************** * FUNCTION agt_rpc_support_method * * mark an RPC method as supported within the agent * this is needed for operations dependent on capabilities * * INPUTS: * module == module name of RPC method (really module name) * method_name == RPC method name *********************************************************************/ void agt_rpc_support_method (const xmlChar *module, const xmlChar *method_name) { obj_template_t *rpcobj; #ifdef DEBUG if (!module || !method_name) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* find the RPC template */ rpcobj = find_rpc(module, method_name); if (!rpcobj) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return; } rpcobj->def.rpc->supported = TRUE; } /* agt_rpc_support_method */ /******************************************************************** * FUNCTION agt_rpc_unsupport_method * * mark an RPC method as unsupported within the agent * this is needed for operations dependent on capabilities * * INPUTS: * module == module name of RPC method (really module name) * method_name == RPC method name *********************************************************************/ void agt_rpc_unsupport_method (const xmlChar *module, const xmlChar *method_name) { obj_template_t *rpcobj; #ifdef DEBUG if (!module || !method_name) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* find the RPC template */ rpcobj = find_rpc(module, method_name); if (!rpcobj) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return; } rpcobj->def.rpc->supported = FALSE; } /* agt_rpc_unsupport_method */ /******************************************************************** * FUNCTION agt_rpc_unregister_method * * remove the callback functions for all phases of RPC processing * for the specified RPC method * * INPUTS: * module == module name of RPC method (really module name) * method_name == RPC method name *********************************************************************/ void agt_rpc_unregister_method (const xmlChar *module, const xmlChar *method_name) { obj_template_t *rpcobj; #ifdef DEBUG if (!module || !method_name) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* find the RPC template */ rpcobj = find_rpc(module, method_name); if (!rpcobj) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return; } /* get the agent CB set and delete it */ if (rpcobj->rpc_cbset) { m__free(rpcobj->rpc_cbset); rpcobj->rpc_cbset = NULL; } } /* agt_rpc_unregister_method */ /******************************************************************** * FUNCTION agt_rpc_dispatch * * Dispatch an incoming request * called by top.c: * This function is registered with top_register_node * for the module 'yuma-netconf', top-node 'rpc' * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ void agt_rpc_dispatch (ses_cb_t *scb, xml_node_t *top) { rpc_msg_t *msg; obj_template_t *rpcobj; obj_rpc_t *rpc; obj_template_t *testobj; agt_rpc_cbset_t *cbset; ses_total_stats_t *agttotals; xmlChar *buff; char *errstr; xml_node_t method, testnode; status_t res, res2; boolean errdone; xmlChar tstampbuff[TSTAMP_MIN_SIZE]; xml_attr_t *attr; #ifdef DEBUG if (!scb || !top) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* init local vars */ res = NO_ERR; res2 = NO_ERR; cbset = NULL; rpcobj = NULL; buff = NULL; agttotals = ses_get_total_stats(); /* make sure any real session has been properly established */ if (scb->type != SES_TYP_DUMMY && scb->state != SES_ST_IDLE) { res = ERR_NCX_ACCESS_DENIED; if (LOGINFO) { log_info("\nagt_rpc dropping session %d (%d) %s", scb->sid, res, get_error_string(res)); } agttotals->stats.inBadRpcs++; agttotals->droppedSessions++; agt_ses_request_close(scb, scb->sid, SES_TR_DROPPED); return; } /* the current node is 'rpc' in the netconf namespace * First get a new RPC message struct */ msg = rpc_new_msg(); if (!msg) { res = ERR_INTERNAL_MEM; agttotals->droppedSessions++; if (LOGINFO) { log_info("\nError: malloc failed, dropping session %d (%d) %s", scb->sid, res, get_error_string(res)); } agt_ses_request_close(scb, scb->sid, SES_TR_DROPPED); return; } /* make sure 'rpc' is the right kind of node */ if (top->nodetyp != XML_NT_START) { res = ERR_NCX_WRONG_NODETYP; scb->stats.inBadRpcs++; agttotals->stats.inBadRpcs++; agttotals->droppedSessions++; if (LOGINFO) { log_info("\nagt_rpc dropping session %d (%d) %s", scb->sid, res, get_error_string(res)); } agt_ses_request_close(scb, scb->sid, SES_TR_OTHER); free_msg(msg); return; } /* setup the struct as an incoming RPC message * borrow the top->attrs queue without copying it * The rpc-reply phase may add attributes to this list * that the caller will free ater this function returns */ msg->rpc_in_attrs = &top->attrs; /* set the default for the with-defaults parameter */ msg->mhdr.withdef = ses_withdef(scb); /* get the NC RPC message-id attribute; must be present */ attr = xml_find_attr(top, 0, NCX_EL_MESSAGE_ID); #ifndef IGNORE_STRICT_RFC4741 if (!attr || !attr->attr_val) { xml_attr_t errattr; res2 = ERR_NCX_MISSING_ATTRIBUTE; memset(&errattr, 0x0, sizeof(xml_attr_t)); errattr.attr_ns = xmlns_nc_id(); errattr.attr_name = NCX_EL_MESSAGE_ID; errattr.attr_val = (xmlChar *)NULL; agt_record_attr_error(scb, &msg->mhdr, NCX_LAYER_RPC, res2, &errattr, top, NULL, NCX_NT_STRING, RPC_ROOT); } #endif /* analyze the element and populate the * initial namespace prefix map for the message */ res = xml_msg_build_prefix_map(&msg->mhdr, msg->rpc_in_attrs, TRUE, !dlq_empty(&msg->mhdr.errQ)); /* init agt_acm cache struct even though it may not * get used if the RPC method is invalid */ if (res == NO_ERR) { res = agt_acm_init_msg_cache(scb, &msg->mhdr); } if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_RPC, res, top, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } /* check any errors in the node */ if (res != NO_ERR || res2 != NO_ERR) { send_rpc_reply(scb, msg); agt_acm_clear_msg_cache(&msg->mhdr); free_msg(msg); return; } /* get the next XML node, which is the RPC method name */ xml_init_node(&method); res = agt_xml_consume_node(scb, &method, NCX_LAYER_RPC, &msg->mhdr); if (res != NO_ERR) { errdone = TRUE; } else { errdone = FALSE; /* reset idle timeout */ (void)uptime(&scb->last_rpc_time); if (LOGDEBUG) { const xmlChar *msgid; tstamp_datetime(tstampbuff); if (attr != NULL && attr->attr_val != NULL) { msgid = attr->attr_val; } else { msgid = NCX_EL_NONE; } log_debug("\nagt_rpc: <%s> for %u=%s@%s (m:%s) [%s]", method.elname, scb->sid, scb->username ? scb->username : NCX_EL_NONE, scb->peeraddr ? scb->peeraddr : NCX_EL_NONE, msgid, tstampbuff); if (LOGDEBUG2) { xml_dump_node(&method); } } /* check the node type which should be type start or simple */ if (!(method.nodetyp==XML_NT_START || method.nodetyp==XML_NT_EMPTY)) { res = ERR_NCX_WRONG_NODETYP; } else { /* look for the RPC method in the definition registry */ rpcobj = find_rpc(method.module, method.elname); msg->rpc_method = rpcobj; rpc = (rpcobj) ? rpcobj->def.rpc : NULL; if (!rpc) { res = ERR_NCX_DEF_NOT_FOUND; } else if (!rpc->supported) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } else if (scb->username && !agt_acm_rpc_allowed(&msg->mhdr, scb->username, rpcobj)) { res = ERR_NCX_ACCESS_DENIED; } else { /* get the agent callback set for this RPC * if NULL, no agent instrumentation has been * registered yet for this RPC method */ cbset = (agt_rpc_cbset_t *)rpcobj->rpc_cbset; } } } /* check any errors in the RPC method node */ if (res != NO_ERR && !errdone) { if (res != ERR_NCX_ACCESS_DENIED) { /* construct an error-path string */ buff = m__getMem(xml_strlen(method.qname) + xml_strlen(RPC_ROOT) + 2); if (buff) { xml_strcpy(buff, RPC_ROOT); xml_strcat(buff, (const xmlChar *)"/"); xml_strcat(buff, method.qname); } } /* passing a NULL buff is okay; it will get checked * The NCX_NT_STRING node type enum is ignored if * the buff pointer is NULL */ agt_record_error(scb, &msg->mhdr, NCX_LAYER_RPC, res, &method, NCX_NT_NONE, NULL, (buff) ? NCX_NT_STRING : NCX_NT_NONE, buff); if (buff) { m__free(buff); } send_rpc_reply(scb, msg); agt_acm_clear_msg_cache(&msg->mhdr); free_msg(msg); xml_clean_node(&method); return; } /* change the session state */ scb->state = SES_ST_IN_MSG; /* parameter set parse state */ if (res == NO_ERR) { res = parse_rpc_input(scb, msg, rpcobj, &method); } /* read in a node which should be the endnode to match 'top' */ xml_init_node(&testnode); if (res == NO_ERR) { res = agt_xml_consume_node(scb, &testnode, NCX_LAYER_RPC, &msg->mhdr); } if (res == NO_ERR) { if (LOGDEBUG2) { log_debug2("\nagt_rpc: expecting %s end node", top->qname); } if (LOGDEBUG3) { xml_dump_node(&testnode); } res = xml_endnode_match(top, &testnode); } xml_clean_node(&testnode); /* check that there is nothing after the element */ if (res == NO_ERR && !xml_docdone(scb->reader)) { /* get all the extra nodes and report the errors */ res = NO_ERR; while (res==NO_ERR) { /* do not add errors such as unknown namespace */ res = agt_xml_consume_node(scb, &testnode, NCX_LAYER_NONE, NULL); if (res==NO_ERR) { res = ERR_NCX_UNKNOWN_ELEMENT; errstr = (char *)xml_strdup((const xmlChar *)RPC_ROOT); agt_record_error(scb, &msg->mhdr, NCX_LAYER_RPC, res, &method, NCX_NT_NONE, NULL, (errstr) ? NCX_NT_STRING : NCX_NT_NONE, errstr); if (errstr) { m__free(errstr); } } } /* reset the error status */ res = ERR_NCX_UNKNOWN_ELEMENT; } xml_clean_node(&testnode); /* check the defaults and any choices if there is clean input */ if (res == NO_ERR) { testobj = obj_find_child(rpcobj, NULL, YANG_K_INPUT); if (testobj && obj_get_child_count(testobj)) { res = post_psd_state(scb, msg, res); } } /* validate state */ if ((res==NO_ERR) && (cbset && cbset->acb[AGT_RPC_PH_VALIDATE])) { /* input passes the basic YANG schema tests at this point; * check if there is a validate callback for * referential integrity, locks, resource reservation, etc. * Only the top level data node has been checked for instance * qualifer usage. The caller must do data node instance * validataion in this VALIDATE callback, as needed */ msg->rpc_agt_state = AGT_RPC_PH_VALIDATE; res = (*cbset->acb[AGT_RPC_PH_VALIDATE])(scb, msg, &method); if (res != NO_ERR) { /* make sure there is an error recorded in case * the validate phase callback did not add one */ if (!rpc_err_any_errors(msg)) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_RPC, res, &method, NCX_NT_NONE, NULL, NCX_NT_OBJ, msg->rpc_method); } } } /* there does not always have to be an invoke callback, * especially since the return of data can be automated * in the send_rpc_reply phase. */ if ((res==NO_ERR) && cbset && cbset->acb[AGT_RPC_PH_INVOKE]) { msg->rpc_agt_state = AGT_RPC_PH_INVOKE; res = (*cbset->acb[AGT_RPC_PH_INVOKE])(scb, msg, &method); if (res != NO_ERR) { /* make sure there is an error recorded in case * the invoke phase callback did not add one */ if (!rpc_err_any_errors(msg)) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_RPC, res, &method, NCX_NT_NONE, NULL, NCX_NT_OBJ, msg->rpc_method); } } } /* make sure the prefix map is correct for report-all-tagged mode * OK to skip this if there was an error exit */ if (res == NO_ERR) { res = xml_msg_finish_prefix_map(&msg->mhdr, msg->rpc_in_attrs); } /* always send an element in response to an */ msg->rpc_agt_state = AGT_RPC_PH_REPLY; send_rpc_reply(scb, msg); /* check if there is a post-reply callback; * call even if the RPC failed */ if (cbset && cbset->acb[AGT_RPC_PH_POST_REPLY]) { msg->rpc_agt_state = AGT_RPC_PH_POST_REPLY; (void)(*cbset->acb[AGT_RPC_PH_POST_REPLY])(scb, msg, &method); } /* check if there is any auditQ because changes to * the running config were made */ if (msg->rpc_txcb && !dlq_empty(&msg->rpc_txcb->auditQ)) { agt_sys_send_netconf_config_change(scb, &msg->rpc_txcb->auditQ); } /* only reset the session state to idle if was not changed * to SES_ST_SHUTDOWN_REQ during this RPC call */ if (scb->state == SES_ST_IN_MSG) { scb->state = SES_ST_IDLE; } /* cleanup and exit */ xml_clean_node(&method); agt_acm_clear_msg_cache(&msg->mhdr); free_msg(msg); print_errors(); clear_errors(); } /* agt_rpc_dispatch */ /******************************************************************** * FUNCTION agt_rpc_load_config_file * * Dispatch an internal request * used for OP_EDITOP_LOAD to load the running from startup * and OP_EDITOP_REPLACE to restore running from backup * * - Create a dummy session and RPC message * - Call a special agt_ps_parse function to parse the config file * - Call the special agt_ncx function to invoke the proper * parmset and application 'validate' callback functions, and * record all the error/warning messages * - Call the special ncx_agt function to invoke all the 'apply' * callbacks as needed * - transfer any error messages to the cfg->load_errQ * * INPUTS: * filespec == XML config filespec to load * cfg == cfg_template_t to fill in * isload == TRUE for normal load-config * FALSE for restore backup load-config * use_sid == session ID to use for the access control * * RETURNS: * status *********************************************************************/ status_t agt_rpc_load_config_file (const xmlChar *filespec, cfg_template_t *cfg, boolean isload, ses_id_t use_sid) { status_t res; #ifdef DEBUG if (!filespec || !cfg) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = load_config_file(filespec, cfg, isload, use_sid, FALSE, NULL, &cfg->load_errQ); return res; } /* agt_rpc_load_config_file */ /******************************************************************** * FUNCTION agt_rpc_get_config_file * * Dispatch an internal request * except skip the INVOKE phase and just remove * the 'config' node from the input and return it * * - Create a dummy session and RPC message * - Call a special agt_ps_parse function to parse the config file * - Call the special agt_ncx function to invoke the proper * parmset and application 'validate' callback functions, and * record all the error/warning messages * - return the element if no errors * - otherwise return all the error messages in a Q * * INPUTS: * filespec == XML config filespec to get * targetcfg == target database to validate against * use_sid == session ID to use for the access control * errorQ == address of return queue of rpc_err_rec_t structs * res == address of return status * * OUTPUTS: * if any errors, the error structs are transferred to * the errorQ (if it is non-NULL). In this case, the caller * must free these data structures with ncx/rpc_err_clean_errQ * * *res == return status * * RETURNS: * malloced and filled in struct representing a element * NULL if some error, check errorQ and *res *********************************************************************/ val_value_t * agt_rpc_get_config_file (const xmlChar *filespec, cfg_template_t *targetcfg, ses_id_t use_sid, dlq_hdr_t *errorQ, status_t *res) { val_value_t *retval; #ifdef DEBUG if (!filespec || !errorQ) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif retval = NULL; *res = load_config_file(filespec, targetcfg, FALSE, use_sid, TRUE, &retval, errorQ); return retval; } /* agt_rpc_get_config_file */ /******************************************************************** * FUNCTION agt_rpc_fill_rpc_error * * Fill one like element using the specified * namespace and name, which may be different than NETCONF * * INPUTS: * err == error record to use to fill 'rpcerror' * rpcerror == NCX_BT_CONTAINER value struct already * initialized. The val_add_child function * will use this parm as the parent. * This namespace will be used for all * child nodes * * RETURNS: * status *********************************************************************/ status_t agt_rpc_fill_rpc_error (const rpc_err_rec_t *err, val_value_t *rpcerror) { val_value_t *leafval; status_t res, retres; #ifdef DEBUG if (!rpcerror || !err) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* init local vars */ res = NO_ERR; retres = NO_ERR; /* add the field */ leafval = agt_make_leaf(rpcerror->obj, NCX_EL_ERROR_TYPE, ncx_get_layer(err->error_type), &res); if (leafval) { val_add_child(leafval, rpcerror); } else { retres = res; } /* add the field */ leafval = agt_make_leaf(rpcerror->obj, NCX_EL_ERROR_TAG, err->error_tag, &res); if (leafval) { val_add_child(leafval, rpcerror); } else { retres = res; } /* add the field */ leafval = agt_make_leaf(rpcerror->obj, NCX_EL_ERROR_SEVERITY, rpc_err_get_severity(err->error_severity), &res); if (leafval) { val_add_child(leafval, rpcerror); } else { retres = res; } /* generate the field */ leafval = NULL; if (err->error_app_tag) { leafval = agt_make_leaf(rpcerror->obj, NCX_EL_ERROR_APP_TAG, err->error_app_tag, &res); if (leafval) { val_add_child(leafval, rpcerror); } else { retres = res; } } /* generate the field */ if (err->error_path) { /* since the error nodes may already be deleted, * suppress the warning in xpath1.c about no child node * found */ boolean savewarn = ncx_warning_enabled(ERR_NCX_NO_XPATH_CHILD); if (savewarn) { (void)ncx_turn_off_warning(ERR_NCX_NO_XPATH_CHILD); } leafval = agt_make_leaf(rpcerror->obj, NCX_EL_ERROR_PATH, err->error_path, &res); if (leafval) { val_add_child(leafval, rpcerror); } else { retres = res; } if (savewarn) { (void)ncx_turn_on_warning(ERR_NCX_NO_XPATH_CHILD); } } /* generate the field */ if (err->error_message) { leafval = agt_make_leaf(rpcerror->obj, NCX_EL_ERROR_MESSAGE, err->error_message, &res); if (leafval) { val_add_child(leafval, rpcerror); } else { retres = res; } } /* TBD: add the error info */ return retres; } /* agt_rpc_fill_rpc_error */ /******************************************************************** * FUNCTION agt_rpc_send_error_reply * * Operation failed or was never attempted * Return an with an * * INPUTS: * scb == session control block * retres == error number for termination reason * *********************************************************************/ void agt_rpc_send_error_reply (ses_cb_t *scb, status_t retres) { rpc_err_rec_t *err; ses_total_stats_t *agttotals; xmlChar *error_path; xml_attrs_t attrs; xml_msg_hdr_t mhdr; status_t res; xmlns_id_t ncid; int32 indent; ncx_layer_t layer; res = ses_start_msg(scb); if (res != NO_ERR) { return; } if (retres == ERR_NCX_INVALID_FRAMING) { layer = NCX_LAYER_TRANSPORT; error_path = NULL; } else { layer = NCX_LAYER_RPC; error_path = xml_strdup(RPC_ROOT); /* ignore malloc error; send as much as possible */ } err = agt_rpcerr_gen_error(layer, retres, NULL, NCX_NT_NONE, NULL, error_path); if (err == NULL) { m__free(error_path); error_path = NULL; } agttotals = ses_get_total_stats(); xml_init_attrs(&attrs); xml_msg_init_hdr(&mhdr); res = xml_msg_gen_xmlns_attrs(&mhdr, &attrs, TRUE); if (res != NO_ERR) { if (err != NULL) { rpc_err_free_record(err); } ses_finish_msg(scb); xml_clean_attrs(&attrs); xml_msg_clean_hdr(&mhdr); return; } ncid = xmlns_nc_id(); indent = ses_indent_count(scb); /* generate the start tag */ xml_wr_begin_elem_ex(scb, &mhdr, 0, ncid, NCX_EL_RPC_REPLY, &attrs, ATTRQ, 0, START); if (err != NULL) { res = send_rpc_error(scb, &mhdr, err, indent); } else { log_error("\nError: could not send error reply for session %u", SES_MY_SID(scb)); } /* generate the end tag */ xml_wr_end_elem(scb, &mhdr, ncid, NCX_EL_RPC_REPLY, 0); /* finish the message */ ses_finish_msg(scb); scb->stats.inBadRpcs++; agttotals->stats.inBadRpcs++; scb->stats.outRpcErrors++; agttotals->stats.outRpcErrors++; if (err != NULL) { rpc_err_free_record(err); } xml_clean_attrs(&attrs); xml_msg_clean_hdr(&mhdr); } /* agt_rpc_send_error_reply */ /* END file agt_rpc.c */ yuma123_2.14/netconf/src/agt/agt_signal.c0000664000175000017500000001120714770023131020376 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_signal.c Handle interrupt signals for the agent ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 22jan07 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_signal.h" #include "ncx.h" #include "status.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* don't rely on GNU extension being defined */ typedef void (*sighandler_t)(int signum); /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_signal_init_done = FALSE; static sighandler_t sh_int, sh_hup, sh_term, sh_pipe, sh_alarm; /******************************************************************** * FUNCTION agt_signal_init * * Initialize the agt_signal module * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ void agt_signal_init (void) { if (!agt_signal_init_done) { /* setup agt_signal_handler */ sh_int = signal(SIGINT, agt_signal_handler); sh_hup = signal(SIGHUP, agt_signal_handler); sh_term = signal(SIGTERM, agt_signal_handler); sh_pipe = signal(SIGPIPE, agt_signal_handler); sh_alarm = signal(SIGALRM, agt_signal_handler); agt_signal_init_done = TRUE; } } /* agt_signal_init */ /******************************************************************** * FUNCTION agt_signal_cleanup * * Cleanup the agt_signal module. * *********************************************************************/ void agt_signal_cleanup (void) { if (agt_signal_init_done) { /* restore previous handlers */ signal(SIGINT, sh_int); signal(SIGHUP, sh_hup); signal(SIGTERM, sh_term); signal(SIGPIPE, sh_pipe); signal(SIGALRM, sh_alarm); agt_signal_init_done = FALSE; } } /* agt_signal_cleanup */ /******************************************************************** * FUNCTION agt_signal_handler * * Handle an incoming interrupt signal * * INPUTS: * intr == interrupt numer * *********************************************************************/ void agt_signal_handler (int intr) { switch (intr) { case SIGINT: /* control-C */ case SIGKILL: /* kill -9 */ case SIGQUIT: /* core dump */ case SIGABRT: /* core dump */ case SIGILL: /* core dump */ case SIGTRAP: /* core dump */ case SIGTERM: /* kill */ agt_request_shutdown(NCX_SHUT_EXIT); break; case SIGHUP: /* hang up treated as reset; kill -1 */ agt_request_shutdown(NCX_SHUT_RESET); break; case SIGPIPE: /* broken pipe ignored */ break; case SIGALRM: break; default: /* ignore */; } } /* agt_signal_handler */ /* END file agt_signal.c */ yuma123_2.14/netconf/src/agt/agt_time_filter.c0000664000175000017500000000674514770023131021437 0ustar vladimirvladimir /* * agt_time_filter.c * * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *** Originally generated by yangdump 2.0.1297 module yuma-time-filter revision 2012-11-15 namespace http://netconfcentral.org/ns/yuma-time-filter organization Netconf Central */ #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "agt_time_filter.h" /* module static variables */ static ncx_module_t *yuma_time_filter_mod; /* put your static variables here */ /******************************************************************** * FUNCTION y_yuma_time_filter_init_static_vars * * initialize module static variables * ********************************************************************/ static void y_yuma_time_filter_init_static_vars (void) { yuma_time_filter_mod = NULL; /* init your static variables here */ } /* y_yuma_time_filter_init_static_vars */ /******************************************************************** * FUNCTION y_yuma_time_filter_init * * initialize the yuma-time-filter server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ status_t y_yuma_time_filter_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; y_yuma_time_filter_init_static_vars(); /* change if custom handling done */ if (xml_strcmp(modname, y_yuma_time_filter_M_yuma_time_filter)) { return ERR_NCX_UNKNOWN_MODULE; } if (revision && xml_strcmp(revision, y_yuma_time_filter_R_yuma_time_filter)) { return ERR_NCX_WRONG_VERSION; } agt_profile = agt_get_profile(); res = ncxmod_load_module( y_yuma_time_filter_M_yuma_time_filter, y_yuma_time_filter_R_yuma_time_filter, &agt_profile->agt_savedevQ, &yuma_time_filter_mod); if (res != NO_ERR) { return res; } /* put your module initialization code here */ return res; } /* y_yuma_time_filter_init */ /******************************************************************** * FUNCTION y_yuma_time_filter_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ status_t y_yuma_time_filter_init2 (void) { status_t res; res = NO_ERR; /* put your init2 code here */ return res; } /* y_yuma_time_filter_init2 */ /******************************************************************** * FUNCTION y_yuma_time_filter_cleanup * cleanup the server instrumentation library * ********************************************************************/ void y_yuma_time_filter_cleanup (void) { /* put your cleanup code here */ } /* y_yuma_time_filter_cleanup */ /* END yuma_time_filter.c */ yuma123_2.14/netconf/src/agt/agt_xml.h0000664000175000017500000001327114770023131017731 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_xml #define _H_agt_xml /* FILE: agt_xml.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Server XML Reader interface ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 14-oct-05 abb begun 2-jan-06 abb rewrite xml_consume_* API to use simpler xml_node_t 11-feb-07 abb moved consume_node fns to agt_xml.h 22-aug-08 abb changed reader parameter to the session control block to record error stats; also errQ to msghdr to record xmlns directives */ #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_msg #include "xml_msg.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_xml_consume_node * * Parse the next node and return its namespace, type and name * The xml_init_node or xml_clean_node API must be called before * this function for the node parameter * * There are 2 types of XML element start nodes * - empty node (XML_NT_EMPTY) * - start of a simple or complex type (XML_NT_START) * * There is one string content node for simpleType content * - string node (XML_NT_STRING) * * There is one end node to end both simple and complex types * - end node (XML_NT_END) * * If nodetyp==XML_NT_EMPTY, then no further nodes will occur * for this element. This node may contain attributes. The * naming parameters will all be set. * * If nodetyp==XML_NT_START, then the caller should examine * the schema for that start node. * For complex types, the next node is probably another XML_NT_START. * For simple types, the next node will be XML_NT_STRING, * followed by an XML_NT_END node. This node may contain attributes. * The naming parameters will all be set. * * If the nodetype==XML_NT_STRING, then the simval and simlen * fields will be set. There are no attributes or naming parameters * for this node type. * * IF the nodetype==XML_NT_END, then no further nodes for this element * will occur. This node should not contain attributes. * All of the naming parameters will be set. The xml_endnode_match * function should be used to confirm that the XML_NT_START and * XML_NT_END nodes are paired correctly. * * The node pointer for the scb->reader will be advanced before the * node is read. * * INPUTS: * scb == session control block containing XmlTextReader * * node == pointer to an initialized xml_node_t struct * to be filled in * layer == protocol layer of caller (only used if errQ != NULL) * msghdr == msg hdr w/ Q to get any rpc-errors as found, * NULL if not used * * OUTPUTS: * *node == xml_node_t struct filled in * *errQ may have errors appended to it * * RETURNS: * status of the operation * Try to fail on fatal errors only *********************************************************************/ extern status_t agt_xml_consume_node (ses_cb_t *scb, xml_node_t *node, ncx_layer_t layer, xml_msg_hdr_t *msghdr); /* consume node but do not generate an EOF error if seen */ extern status_t agt_xml_consume_node_noeof (ses_cb_t *scb, xml_node_t *node, ncx_layer_t layer, xml_msg_hdr_t *msghdr); /* consume node but do not generate namespace errors if seen * needed to process subtree filters properly */ extern status_t agt_xml_consume_node_nons (ses_cb_t *scb, xml_node_t *node, ncx_layer_t layer, xml_msg_hdr_t *msghdr); /* consume node but do not advance the node pointer */ extern status_t agt_xml_consume_node_noadv (ses_cb_t *scb, xml_node_t *node, ncx_layer_t layer, xml_msg_hdr_t *msghdr); /******************************************************************** * FUNCTION agt_xml_skip_subtree * * Already encountered an error, so advance nodes until the * matching start-node is reached or a terminating error occurs * - end of input * - start depth level reached * * INPUTS: * reader == XmlReader already initialized from File, Memory, * or whatever * startnode == xml_node_t of the start node of the sub-tree to skip * RETURNS: * status of the operation * SIDE EFFECTS: * the xmlreader state is advanced until the current node is the * end node of the specified start node or a fatal error occurs *********************************************************************/ extern status_t agt_xml_skip_subtree (ses_cb_t *scb, const xml_node_t *startnode); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_xml */ yuma123_2.14/netconf/src/agt/agt_xml.c0000664000175000017500000006312114770023131017723 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_xml.c Agent XML Reader interface ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11feb07 abb begun; split from xml_util.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include "procdefs.h" #include "agt_util.h" #include "agt_xml.h" #include "log.h" #include "ncx.h" #include "status.h" #include "xmlns.h" #include "xml_util.h" #include "xpath.h" #include "xpath_yang.h" #include "xpath1.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION get_all_attrs * * Copy all the attributes from the current node to * the xml_attrs_t queue * * INPUTS: * scb == session control block * errnode == element node to use for reporting errors * msghdr == msg hdr w/ Q to get any rpc-errors as found, * NULL if not used * nserr == TRUE if unknown namespace should cause the * function to fail and the attr not to be saved * This is the normal mode. * == FALSE and the namespace will be marked INVALID * but an error will not be returned * * OUTPUTS: * attrs Q contains 0 or more entries * *msghdr.errQ may have rpc-errors added to it * * RETURNS: * status of the operation * returns NO_ERR if all copied okay or even zero copied *********************************************************************/ static status_t get_all_attrs (ses_cb_t *scb, xml_node_t *errnode, xml_attrs_t *attrs, ncx_layer_t layer, xml_msg_hdr_t *msghdr, boolean nserr) { xmlChar *value; const xmlChar *badns, *name; xpath_result_t *result; xml_attr_t *attr; int i, cnt, ret; xmlns_id_t nsid; status_t res; boolean done, xpatherror; xml_attr_t errattr; uint32 plen; /* check the attribute count first */ cnt = xmlTextReaderAttributeCount(scb->reader); if (cnt==0) { return NO_ERR; } name = NULL; res = NO_ERR; xpatherror = FALSE; /* move through the list of attributes */ for (i=0, done=FALSE; ireader); } else { ret = xmlTextReaderMoveToNextAttribute(scb->reader); } if (ret != 1) { res = ERR_XML_READER_INTERNAL; done = TRUE; } else { /* get the attribute name */ name = xmlTextReaderConstName(scb->reader); if (!name) { res = ERR_XML_READER_NULLNAME; } else { res = xml_check_ns(scb->reader, name, &nsid, &plen, &badns); if (!nserr && res != NO_ERR) { /* mask invalid namespace as requested */ nsid = xmlns_inv_id(); plen = 0; res = NO_ERR; } /* get the attribute value even if a NS error */ value = xmlTextReaderValue(scb->reader); if (!value) { res = ERR_XML_READER_NULLVAL; } else { /* save the values as received, may be QName * only error that can occur is a malloc fail */ attr = xml_add_qattr(attrs, nsid, name, plen, value, &res); if (!attr) { res = ERR_INTERNAL_MEM; } else { /* special hack: do not know which * attributes within an XML node * are tagged as XPath strings at this point * To save resources, only the 'select' and * 'key' attributes are supported at this time. */ if (!xml_strcmp(&name[plen], NCX_EL_SELECT)) { res = NO_ERR; attr->attr_xpcb = agt_new_xpath_pcb(scb, value, &res); if (!attr->attr_xpcb) { ; /* res already set */ } else { /* do a first pass parsing to resolve all * the prefixes and check well-formed XPath */ result = xpath1_eval_xmlexpr (scb->reader, attr->attr_xpcb, NULL, NULL, FALSE, FALSE, &res); if (res != NO_ERR) { xpatherror = TRUE; } xpath_free_result(result); } } else if (!xml_strcmp(&name[plen], NCX_EL_KEY)) { res = NO_ERR; attr->attr_xpcb = agt_new_xpath_pcb(scb, value, &res); if (!attr->attr_xpcb) { ; /* res already set */ } else { res = xpath_yang_validate_xmlkey (scb->reader, attr->attr_xpcb, NULL, FALSE); if (res != NO_ERR) { xpatherror = TRUE; } } } } } } } /* check the result */ if (res != NO_ERR && msghdr) { /* need a dummy attribute struct to report this error */ memset(&errattr, 0x0, sizeof(xml_attr_t)); errattr.attr_ns = xmlns_nc_id(); errattr.attr_qname = name; errattr.attr_name = name; errattr.attr_val = value; /* save the error info */ if (xpatherror) { res = ERR_NCX_INVALID_XPATH_EXPR; /* generate an invalid-value error */ agt_record_attr_error(scb, msghdr, layer, res, &errattr, errnode, NULL, NCX_NT_NONE, NULL); } else if (res==ERR_XML_READER_NULLVAL || res==ERR_NCX_UNKNOWN_NAMESPACE) { /* generate a bad namespace error */ agt_record_attr_error(scb, msghdr, layer, res, &errattr, errnode, badns, NCX_NT_NONE, NULL); } else { /* generate a generic error */ agt_record_attr_error(scb, msghdr, layer, res, &errattr, errnode, NULL, NCX_NT_NONE, NULL); } } if (value) { xmlFree(value); } } /* reset the current node to where we started */ ret = xmlTextReaderMoveToElement(scb->reader); if (ret != 1) { if (msghdr) { res = ERR_XML_READER_INTERNAL; agt_record_error(scb, msghdr, layer, res, errnode, NCX_NT_STRING, name, NCX_NT_NONE, NULL); } } return res; } /* get_all_attrs */ /******************************************************************** * FUNCTION consume_node * * Internal consume XML node function * see agt_xml_consume_node for details. * * EXTRA INPUTS: * eoferr == TRUE if an End of File error should be generated * == FALSE if not * nserr == TRUE if bad namespace should be checked * == FALSE if not * clean == TRUE is a string should be cleaned before returned * == FALSE if a string node should be returned as-is * * RETURNS: * status of the operation * Try to fail on fatal errors only *********************************************************************/ static status_t consume_node (ses_cb_t *scb, boolean advance, xml_node_t *node, ncx_layer_t layer, xml_msg_hdr_t *msghdr, boolean eoferr, boolean nserr, boolean clean) { int ret, nodetyp; const xmlChar *badns; xmlChar *valstr, *namestr; uint32 len; status_t res, res2; boolean done; /* init local vars */ done = FALSE; res = NO_ERR; res2 = NO_ERR; badns = NULL; /* loop past any unused xmlTextReader node types */ while (!done) { /* check if a new node should be read */ if (advance) { /* advance the node pointer */ ret = xmlTextReaderRead(scb->reader); if (ret != 1) { /* do not treat this as an internal error */ res = ERR_XML_READER_EOF; if (msghdr && eoferr) { /* generate an operation-failed error */ agt_record_error(scb, msghdr, layer, res, NULL, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } return res; } } /* get the node depth to match the end node correctly */ node->depth = xmlTextReaderDepth(scb->reader); if (node->depth == -1) { /* this never actaully happens */ SET_ERROR(ERR_XML_READER_INTERNAL); node->depth = 0; } /* get the internal nodetype, check it and convert it */ nodetyp = xmlTextReaderNodeType(scb->reader); switch (nodetyp) { case XML_ELEMENT_NODE: /* classify element as empty or start */ if (xmlTextReaderIsEmptyElement(scb->reader)) { node->nodetyp = XML_NT_EMPTY; } else { node->nodetyp = XML_NT_START; } done = TRUE; break; case XML_ELEMENT_DECL: node->nodetyp = XML_NT_END; done = TRUE; break; case XML_TEXT_NODE: /* case XML_DTD_NODE: */ node->nodetyp = XML_NT_STRING; done = TRUE; break; default: /* unused node type -- keep trying */ if (LOGDEBUG3) { log_debug3("\nxml_consume_node: skip unused node (%s)", xml_get_node_name(nodetyp)); } advance = TRUE; } } /* finish the node, depending on its type */ switch (node->nodetyp) { case XML_NT_START: case XML_NT_END: case XML_NT_EMPTY: /* get the element QName */ namestr = xml_strdup(xmlTextReaderConstName(scb->reader)); if (!namestr) { res = ERR_INTERNAL_MEM; } else { node->qname = namestr; /* check for namespace prefix in the name * only error returned is unknown-namespace */ len = 0; res = xml_check_ns(scb->reader, namestr, &node->nsid, &len, &badns); if (!nserr && res != NO_ERR) { node->nsid = xmlns_inv_id(); len = 0; res = NO_ERR; } /* set the element name to the char after the prefix, if any */ node->elname = (const xmlChar *)(namestr+len); /* get all the attributes, except for XML_NT_END */ if (res == NO_ERR && node->nodetyp != XML_NT_END) { res2 = get_all_attrs(scb, node, &node->attrs, layer, msghdr, nserr); } /* Set the node module */ if (res == NO_ERR) { if (node->nsid) { node->module = xmlns_get_module(node->nsid); } else { /* no entry, use the default module (ncx) */ node->module = NCX_DEF_MODULE; } } } break; case XML_NT_STRING: /* get the text value -- this is a malloced string */ node->simval = NULL; valstr = xmlTextReaderValue(scb->reader); if (valstr) { if (clean) { node->simfree = xml_copy_clean_string(valstr); } else { node->simfree = xml_strdup(valstr); } if (node->simfree) { node->simlen = xml_strlen(node->simfree); node->simval = (const xmlChar *)node->simfree; } /* see if this is a QName string; if so save the NSID */ xml_check_qname_content(scb->reader, node); xmlFree(valstr); } if (!node->simval) { /* prevent a NULL ptr reference */ node->simval = EMPTY_STRING; node->simlen = 0; node->simfree = NULL; } break; default: break; } if ((res != NO_ERR) && msghdr) { if (badns) { /* generate an operation-failed error */ agt_record_error(scb, msghdr, layer, res, node, NCX_NT_STRING, badns, NCX_NT_NONE, NULL); } else { agt_record_error(scb, msghdr, layer, res, node, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } } if (LOGDEBUG4) { log_debug4("\nxml_consume_node: return (%d)", (res==NO_ERR) ? res2 : res); if (scb->state != SES_ST_INIT) { xml_dump_node(node); } } /* return general error first, then attribute error * It doesn't really matter since the caller will * assume all error reports have been queued upon return */ return (res==NO_ERR) ? res2 : res; } /* consume_node */ /************** E X T E R N A L F U N C T I O N S **************/ /******************************************************************** * FUNCTION agt_xml_consume_node * * Parse the next node and return its namespace, type and name * The xml_init_node or xml_clean_node API must be called before * this function for the node parameter * * There are 2 types of XML element start nodes * - empty node (XML_NT_EMPTY) * - start of a simple or complex type (XML_NT_START) * * There is one string content node for simpleType content * - string node (XML_NT_STRING) * * There is one end node to end both simple and complex types * - end node (XML_NT_END) * * If nodetyp==XML_NT_EMPTY, then no further nodes will occur * for this element. This node may contain attributes. The * naming parameters will all be set. * * If nodetyp==XML_NT_START, then the caller should examine * the schema for that start node. * For complex types, the next node is probably another XML_NT_START. * For simple types, the next node will be XML_NT_STRING, * followed by an XML_NT_END node. This node may contain attributes. * The naming parameters will all be set. * * If the nodetype==XML_NT_STRING, then the simval and simlen * fields will be set. There are no attributes or naming parameters * for this node type. * * IF the nodetype==XML_NT_END, then no further nodes for this element * will occur. This node should not contain attributes. * All of the naming parameters will be set. The xml_endnode_match * function should be used to confirm that the XML_NT_START and * XML_NT_END nodes are paired correctly. * * The node pointer for the scb->reader will be advanced before the * node is read. * * INPUTS: * scb == session control block containing XmlTextReader * * node == pointer to an initialized xml_node_t struct * to be filled in * layer == protocol layer of caller (only used if errQ != NULL) * msghdr == msg hdr w/ Q to get any rpc-errors as found, * NULL if not used * * OUTPUTS: * *node == xml_node_t struct filled in * *errQ may have errors appended to it * * RETURNS: * status of the operation * Try to fail on fatal errors only *********************************************************************/ status_t agt_xml_consume_node (ses_cb_t *scb, xml_node_t *node, ncx_layer_t layer, xml_msg_hdr_t *msghdr) { return consume_node(scb, TRUE, node, layer, msghdr, TRUE, TRUE, TRUE); } /* agt_xml_consume_node */ status_t agt_xml_consume_node_noeof (ses_cb_t *scb, xml_node_t *node, ncx_layer_t layer, xml_msg_hdr_t *msghdr) { return consume_node(scb, TRUE, node, layer, msghdr, FALSE, TRUE, TRUE); } /* agt_xml_consume_node_noeof */ status_t agt_xml_consume_node_nons (ses_cb_t *scb, xml_node_t *node, ncx_layer_t layer, xml_msg_hdr_t *msghdr) { return consume_node(scb, TRUE, node, layer, msghdr, FALSE, FALSE, TRUE); } /* agt_xml_consume_node_nons */ status_t agt_xml_consume_node_noadv (ses_cb_t *scb, xml_node_t *node, ncx_layer_t layer, xml_msg_hdr_t *msghdr) { return consume_node(scb, FALSE, node, layer, msghdr, TRUE, TRUE, TRUE); } /* agt_xml_consume_node_noadv */ /******************************************************************** * FUNCTION agt_xml_skip_subtree * * Already encountered an error, so advance nodes until the * matching start-node is reached or a terminating error occurs * - end of input * - start depth level reached * * INPUTS: * reader == XmlReader already initialized from File, Memory, * or whatever * startnode == xml_node_t of the start node of the sub-tree to skip * RETURNS: * status of the operation * SIDE EFFECTS: * the xmlreader state is advanced until the current node is the * end node of the specified start node or a fatal error occurs *********************************************************************/ status_t agt_xml_skip_subtree (ses_cb_t *scb, const xml_node_t *startnode) { xml_node_t node; const xmlChar *qname, *badns; uint32 len; int ret, depth, nodetyp; xmlns_id_t nsid; boolean done, justone; status_t res; #ifdef DEBUG if (!scb || !startnode) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif justone = FALSE; switch (startnode->nodetyp) { case XML_NT_START: break; case XML_NT_EMPTY: return NO_ERR; case XML_NT_STRING: justone = TRUE; break; case XML_NT_END: return NO_ERR; default: return SET_ERROR(ERR_INTERNAL_VAL); } xml_init_node(&node); res = agt_xml_consume_node_noadv(scb, &node, NCX_LAYER_NONE, NULL); if (res == ERR_NCX_UNKNOWN_NS) { res = NO_ERR; } if (res == NO_ERR) { res = xml_endnode_match(startnode, &node); if (res == NO_ERR) { xml_clean_node(&node); return NO_ERR; } } xml_clean_node(&node); if (justone) { return NO_ERR; } done = FALSE; while (!done) { /* advance the node pointer */ ret = xmlTextReaderRead(scb->reader); if (ret != 1) { /* fatal error */ return SET_ERROR(ERR_XML_READER_EOF); } /* get the node depth to match the end node correctly */ depth = xmlTextReaderDepth(scb->reader); if (depth == -1) { /* not sure if this can happen, treat as fatal error */ return SET_ERROR(ERR_XML_READER_INTERNAL); } else if (depth <= startnode->depth) { /* this depth override will cause errors to be ignored * - wrong namespace in matching end node * - unknown namespace in matching end node * - wrong name in 'matching' end node */ done = TRUE; } /* get the internal nodetype, check it and convert it */ nodetyp = xmlTextReaderNodeType(scb->reader); /* get the element QName */ qname = xmlTextReaderConstName(scb->reader); if (qname) { /* check for namespace prefix in the name * only error is 'unregistered namespace ID' * which doesn't matter in this case */ nsid = 0; (void)xml_check_ns(scb->reader, qname, &nsid, &len, &badns); } else { qname = EMPTY_STRING; } /* check the normal case to see if the search is done */ if (depth == startnode->depth && !xml_strcmp(qname, startnode->qname) && nodetyp == XML_ELEMENT_DECL) { done = TRUE; } if (LOGDEBUG3) { log_debug3("\nxml_skip: %s L:%d T:%s", qname, depth, xml_get_node_name(nodetyp)); } } return NO_ERR; } /* agt_xml_skip_subtree */ /* END agt_xml.c */ yuma123_2.14/netconf/src/agt/agt_not.c0000664000175000017500000017531414770023131017733 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_not.c NETCONF Notification Data Model implementation: Agent Side Support RFC 5277 version identifiers: container /netconf container /netconf/streams list /netconf/streams/stream leaf /netconf/streams/stream/name leaf /netconf/streams/stream/description leaf /netconf/streams/stream/replaySupport leaf /netconf/streams/stream/replayLogCreationTime notification /replayComplete notification /notificationComplete ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24feb09 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_acm.h" #include "agt_cap.h" #include "agt_cb.h" #include "agt_not.h" #include "agt_rpc.h" #include "agt_ses.h" #include "agt_tree.h" #include "agt_util.h" #include "agt_xpath.h" #include "agt_not_queue_notification_cb.h" #include "cfg.h" #include "getcb.h" #include "log.h" #include "ncxmod.h" #include "ncxtypes.h" #include "rpc.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "tstamp.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xml_wr.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define notifications_N_create_subscription \ (const xmlChar *)"create-subscription" #define notifications_N_eventTime (const xmlChar *)"eventTime" #define notifications_N_create_subscription_stream \ (const xmlChar *)"stream" #define notifications_N_create_subscription_filter \ (const xmlChar *)"filter" #define notifications_N_create_subscription_startTime \ (const xmlChar *)"startTime" #define notifications_N_create_subscription_stopTime \ (const xmlChar *)"stopTime" #define nc_notifications_N_netconf (const xmlChar *)"netconf" #define nc_notifications_N_streams (const xmlChar *)"streams" #define nc_notifications_N_stream (const xmlChar *)"stream" #define nc_notifications_N_name (const xmlChar *)"name" #define nc_notifications_N_description \ (const xmlChar *)"description" #define nc_notifications_N_replaySupport \ (const xmlChar *)"replaySupport" #define nc_notifications_N_replayLogCreationTime \ (const xmlChar *)"replayLogCreationTime" #define nc_notifications_N_replayComplete \ (const xmlChar *)"replayComplete" #define nc_notifications_N_notificationComplete \ (const xmlChar *)"notificationComplete" #define AGT_NOT_SEQID_MOD (const xmlChar *)"yuma123-system" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_not_init_done = FALSE; /* notifications.yang */ static ncx_module_t *notifmod; /* nc-notifications.yang */ static ncx_module_t *ncnotifmod; /* Q of agt_not_subscription_t * one entry is created for each create-subscription call * if stopTime is given then the subscription will be * automatically deleted when all the replay is complete. * Otherwise, a subscription is deleted when the session * is terminated */ static dlq_hdr_t subscriptionQ; /* Q of agt_not_msg_t * these are the messages that represent the replay buffer * only system-wide notifications are stored in this Q * the replayComplete and notificationComplete events are * generated special-case, and not stored for replay */ static dlq_hdr_t notificationQ; /* cached pointer to the element template */ static obj_template_t *notificationobj; /* cached pointer to the /notification/eventTime element template */ static obj_template_t *eventTimeobj; /* cached pointer to the /ncn:replayComplete element template */ static obj_template_t *replayCompleteobj; /* cached pointer to the /ncn:notificationComplete element template */ static obj_template_t *notificationCompleteobj; /* cached pointer to the /ncx:sequence-id element template */ static obj_template_t *sequenceidobj; /* flag to signal quick exit */ static boolean anySubscriptions; /* auto-increment message index */ static uint32 msgid; /* keep track of eventlog size */ static uint32 notification_count; /******************************************************************** * FUNCTION free_subscription * * Clean and free a subscription control block * * INPUTS: * sub == subscription to delete * *********************************************************************/ static void free_subscription (agt_not_subscription_t *sub) { if (sub->stream) { m__free(sub->stream); } if (sub->startTime) { m__free(sub->startTime); } if (sub->stopTime) { m__free(sub->stopTime); } if (sub->filterval) { val_free_value(sub->filterval); } if (sub->scb) { sub->scb->notif_active = FALSE; } m__free(sub); } /* free_subscription */ /******************************************************************** * FUNCTION new_subscription * * Malloc and fill in a new subscription control block * * INPUTS: * scb == session this subscription is for * stream == requested stream ID * curTime == current time for creation time * startTime == replay start time (may be NULL) * !!! THIS MALLOCED NODE IS FREED LATER !!!! * stopTime == replayStopTime (may be NULL) * !!! THIS MALLOCED NODE IS FREED LATER !!!! * futurestop == TRUE if stopTime in the future * FALSE if not set or not in the future * filtertype == internal filter type * filterval == filter value node passed from PDU * !!! THIS MALLOCED NODE IS FREED LATER !!!! * selectval == back-ptr into filterval->select value node * only used if filtertype == OP_FILTER_XPATH * * RETURNS: * pointer to malloced struct or NULL if no memoryerror *********************************************************************/ static agt_not_subscription_t * new_subscription (ses_cb_t *scb, const xmlChar *stream, const xmlChar *curTime, xmlChar *startTime, xmlChar *stopTime, boolean futurestop, op_filtertyp_t filtertype, val_value_t *filterval, val_value_t *selectval) { agt_not_subscription_t *sub; agt_not_stream_t streamid; if (!xml_strcmp(stream, NCX_DEF_STREAM_NAME)) { streamid = AGT_NOT_STREAM_NETCONF; } else { /*** !!! ***/ SET_ERROR(ERR_INTERNAL_VAL); streamid = AGT_NOT_STREAM_NETCONF; } sub = m__getObj(agt_not_subscription_t); if (!sub) { return NULL; } memset(sub, 0x0, sizeof(agt_not_subscription_t)); sub->stream = xml_strdup(stream); if (!sub->stream) { free_subscription(sub); return NULL; } sub->startTime = startTime; sub->stopTime = stopTime; sub->scb = scb; sub->sid = scb->sid; sub->streamid = streamid; xml_strcpy(sub->createTime, curTime); sub->filtertyp = filtertype; sub->filterval = filterval; sub->selectval = selectval; if (futurestop) { sub->flags = AGT_NOT_FL_FUTURESTOP; } sub->state = AGT_NOT_STATE_INIT; /* prevent any idle timeout */ scb->notif_active = TRUE; return sub; } /* new_subscription */ /******************************************************************** * FUNCTION create_subscription_validate * * create-subscription : validate params callback * * INPUTS: * see rpc/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t create_subscription_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { const xmlChar *stream, *startTime, *stopTime; xmlChar *starttime_utc, *stoptime_utc; val_value_t *valstream, *valfilter, *valselect; val_value_t *valstartTime, *valstopTime; agt_not_subscription_t *sub, *testsub; xmlChar tstampbuff[TSTAMP_MIN_SIZE]; int ret; status_t res, res2, filterres; boolean futurestop, isnegative; op_filtertyp_t filtertyp; res = NO_ERR; filterres = NO_ERR; filtertyp = OP_FILTER_NONE; valstream = NULL; valfilter = NULL; valselect = NULL; valstartTime = NULL; valstopTime = NULL; startTime = NULL; stopTime = NULL; starttime_utc = NULL; stoptime_utc = NULL; stream = NCX_DEF_STREAM_NAME; tstamp_datetime(tstampbuff); futurestop = FALSE; isnegative = FALSE; /* get the stream parameter */ valstream = val_find_child(msg->rpc_input, AGT_NOT_MODULE1, notifications_N_create_subscription_stream); if (valstream) { if (valstream->res == NO_ERR) { stream = VAL_STR(valstream); if (xml_strcmp(NCX_DEF_STREAM_NAME, stream)) { /* not the hard-wired NETCONF stream and * no other strams supported at this time * report the error */ res = ERR_NCX_NOT_FOUND; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, stream, NCX_NT_VAL, valstream); } /* else name==NETCONF: OK */ } /* else error already reported */ } /* else default NETCONF stream is going to be used */ /* get the filter parameter */ valfilter = val_find_child(msg->rpc_input, AGT_NOT_MODULE1, notifications_N_create_subscription_filter); if (valfilter) { if (valfilter->res == NO_ERR) { /* check if the optional filter parameter is ok */ filterres = agt_validate_filter_ex(scb, msg, valfilter); if (filterres == NO_ERR) { filtertyp = msg->rpc_filter.op_filtyp; if (filtertyp == OP_FILTER_XPATH) { valselect = msg->rpc_filter.op_filter; } } /* else error already recorded if not NO_ERR */ } } /* get the startTime parameter */ valstartTime = val_find_child(msg->rpc_input, AGT_NOT_MODULE1, notifications_N_create_subscription_startTime); if (valstartTime) { if (valstartTime->res == NO_ERR) { startTime = VAL_STR(valstartTime); } } /* get the stopTime parameter */ valstopTime = val_find_child(msg->rpc_input, AGT_NOT_MODULE1, notifications_N_create_subscription_stopTime); if (valstopTime) { if (valstopTime->res == NO_ERR) { stopTime = VAL_STR(valstopTime); } } if (startTime || stopTime) { res = NO_ERR; /* Normalize the xsd:dateTime strings first */ if (startTime) { isnegative = FALSE; starttime_utc = tstamp_convert_to_utctime(startTime, &isnegative, &res); if (!starttime_utc || isnegative) { if (isnegative) { res = ERR_NCX_INVALID_VALUE; } agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, startTime, NCX_NT_VAL, valstartTime); } } if (res == NO_ERR && stopTime) { isnegative = FALSE; res = NO_ERR; stoptime_utc = tstamp_convert_to_utctime(stopTime, &isnegative, &res); if (!stoptime_utc || isnegative) { if (isnegative) { res = ERR_NCX_INVALID_VALUE; } agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, stopTime, NCX_NT_VAL, valstopTime); } } /* check the start time against 'now' */ if (res == NO_ERR && starttime_utc) { ret = xml_strcmp(starttime_utc, tstampbuff); if (ret > 0) { res = ERR_NCX_BAD_ELEMENT; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, startTime, NCX_NT_VAL, valstartTime); } /* else startTime before now (OK) */ /* check the start time after the stop time */ if (res == NO_ERR && stoptime_utc) { ret = xml_strcmp(starttime_utc, stoptime_utc); if (ret > 0) { res = ERR_NCX_BAD_ELEMENT; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, stopTime, NCX_NT_VAL, valstopTime); } /* else startTime before stopTime (OK) */ } } /* check stopTime but no startTime */ if (res == NO_ERR && stoptime_utc) { if (!starttime_utc) { res = ERR_NCX_MISSING_ELEMENT; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, valstartTime); } /* treat stopTime in the future as an error */ ret = xml_strcmp(stoptime_utc, tstampbuff); if (ret > 0) { futurestop = TRUE; } } } /* check if there is already a subscription * present for the specified session; * this is an error since there is no way to * identify multiple subscriptions per session, */ res2 = NO_ERR; for (testsub = (agt_not_subscription_t *) dlq_firstEntry(&subscriptionQ); testsub != NULL && res2 == NO_ERR; testsub = (agt_not_subscription_t *) dlq_nextEntry(testsub)) { if (testsub->sid == scb->sid) { res2 = ERR_NCX_IN_USE; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res2, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } } /* create a new subscription control block if no errors */ if (res == NO_ERR && res2 == NO_ERR && filterres == NO_ERR) { /* passing off malloced memory here * - starttime_utc * - stoptime_utc * - valfilter */ sub = new_subscription(scb, stream, tstampbuff, starttime_utc, stoptime_utc, futurestop, filtertyp, valfilter, valselect); if (!sub) { res = ERR_INTERNAL_MEM; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } else { if (valfilter) { /* passing off this memory now !! * the new_subscription function * saved a pointer to valfilter * need to remove it from the * incoming PDU to prevent it * from be deleted after the is done */ val_remove_child(valfilter); } msg->rpc_user1 = sub; } /* these malloced strings have been * stored in the new subscription, if used */ starttime_utc = NULL; stoptime_utc = NULL; } if (starttime_utc) { m__free(starttime_utc); } if (stoptime_utc) { m__free(stoptime_utc); } if (res == NO_ERR) { return res2; } else { return res; } } /* create_subscription_validate */ /******************************************************************** * FUNCTION create_subscription_invoke * * create-subscription : invoke callback * * INPUTS: * see rpc/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t create_subscription_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { agt_not_subscription_t *sub; agt_not_msg_t *not, *nextnot; int ret; boolean done; (void)scb; (void)methnode; sub = (agt_not_subscription_t *)msg->rpc_user1; if (sub->startTime) { /* this subscription has requested replay * go through the notificationQ and set * the start replay pointer */ sub->state = AGT_NOT_STATE_REPLAY; done = FALSE; for (not = (agt_not_msg_t *) dlq_firstEntry(¬ificationQ); not != NULL && !done; not = (agt_not_msg_t *)dlq_nextEntry(not)) { ret = xml_strcmp(sub->startTime, not->eventTime); if (ret <= 0) { sub->firstreplaymsg = not; sub->firstreplaymsgid = not->msgid; done = TRUE; } } if (!done) { /* the startTime is after the last available * notification eventTime, so replay is over */ sub->flags |= AGT_NOT_FL_RC_READY; } else if (sub->stopTime) { /* the sub->firstreplaymsg was set; * the subscription has requested to be * terminated after a specific time */ if (sub->flags & AGT_NOT_FL_FUTURESTOP) { /* just use the last replay buffer entry * as the end-of-replay marker */ sub->lastreplaymsg = (agt_not_msg_t *) dlq_lastEntry(¬ificationQ); sub->lastreplaymsgid = sub->lastreplaymsg->msgid; } else { /* first check that the start notification * is not already past the requested stopTime */ ret = xml_strcmp(sub->stopTime, sub->firstreplaymsg->eventTime); if (ret <= 0) { sub->firstreplaymsg = NULL; sub->firstreplaymsgid = 0; sub->flags |= AGT_NOT_FL_RC_READY; } else { /* check the notifications after the * start replay node, to find out * which one should be the last replay */ done = FALSE; for (not = sub->firstreplaymsg; not != NULL && !done; not = nextnot) { nextnot = (agt_not_msg_t *)dlq_nextEntry(not); if (nextnot) { ret = xml_strcmp(sub->stopTime, nextnot->eventTime); } else { ret = -1; } if (ret < 0) { /* the previous one checked was the winner */ sub->lastreplaymsg = not; sub->lastreplaymsgid = not->msgid; done = TRUE; } } if (!done) { sub->lastreplaymsg = (agt_not_msg_t *) dlq_lastEntry(¬ificationQ); sub->lastreplaymsgid = sub->lastreplaymsg->msgid; } } } } } else { /* setup live subscription by setting the * lastmsg to the end of the replayQ * so none of the buffered notifications * are send to this subscription */ sub->state = AGT_NOT_STATE_LIVE; sub->lastmsg = (agt_not_msg_t *) dlq_lastEntry(¬ificationQ); if (sub->lastmsg) { sub->lastmsgid = sub->lastmsg->msgid; } } dlq_enque(sub, &subscriptionQ); anySubscriptions = TRUE; if (LOGDEBUG) { log_debug("\nagt_not: Started %s subscription on stream " "'%s' for session '%u'", (sub->startTime) ? "replay" : "live", sub->stream, sub->scb->sid); } return NO_ERR; } /* create_subscription_invoke */ /******************************************************************** * FUNCTION expire_subscription * * Expire a removed subscription because it has a stopTime * and no more replay notifications are left to deliver * * MUST REMOVE FROM subscriptionQ FIRST !!! * * INPUTS: * sub == subscription to expire * *********************************************************************/ static void expire_subscription (agt_not_subscription_t *sub) { if (LOGDEBUG) { log_debug("\nagt_not: Removed %s subscription " "for session '%u'", (sub->startTime) ? "replay" : "live", sub->scb->sid); } free_subscription(sub); anySubscriptions = (dlq_empty(&subscriptionQ)) ? FALSE : TRUE; } /* expire_subscription */ /******************************************************************** * FUNCTION get_entry_after * * Get the entry after the specified msgid * * INPUTS: * thismsgid == get the first msg with an ID higher than this value * * RETURNS: * pointer to an notification to use * NULL if none found *********************************************************************/ static agt_not_msg_t * get_entry_after (uint32 thismsgid) { agt_not_msg_t *not; for (not = (agt_not_msg_t *)dlq_firstEntry(¬ificationQ); not != NULL; not = (agt_not_msg_t *)dlq_nextEntry(not)) { if (not->msgid > thismsgid) { return not; } } return NULL; } /* get_entry_after */ /******************************************************************** * FUNCTION send_notification * * Send the specified notification to the specified subscription * If checkfilter==TRUE, then check the filter if any, * and only send if it passes * * INPUTS: * sub == subscription to use * notif == notification to use * checkfilter == TRUE to check the filter if any * FALSE to bypass the filtering * (e.g., replayComplete, notificationComplete) * * OUTPUTS: * message sent to sub->scb if filter passes * * RETURNS: * status *********************************************************************/ static status_t send_notification (agt_not_subscription_t *sub, agt_not_msg_t *notif, boolean checkfilter) { val_value_t *topval, *eventTime; val_value_t *eventType, *payloadval, *sequenceid; ses_total_stats_t *totalstats; xml_msg_hdr_t msghdr; status_t res; boolean filterpassed; xmlChar numbuff[NCX_MAX_NUMLEN]; filterpassed = TRUE; totalstats = ses_get_total_stats(); if (!notif->msg) { /* need to construct the notification msg */ topval = val_new_value(); if (!topval) { log_error("\nError: malloc failed: cannot send notification"); return ERR_INTERNAL_MEM; } val_init_from_template(topval, notificationobj); eventTime = val_make_simval_obj(eventTimeobj, notif->eventTime, &res); if (!eventTime) { log_error("\nError: make simval failed (%s): cannot " "send notification", get_error_string(res)); val_free_value(topval); return res; } val_add_child(eventTime, topval); eventType = val_new_value(); if (!eventType) { log_error("\nError: malloc failed: cannot send notification"); val_free_value(topval); return ERR_INTERNAL_MEM; } val_init_from_template(eventType, notif->notobj); val_add_child(eventType, topval); notif->event = eventType; /* move the payloadQ: transfer the memory here */ while (!dlq_empty(¬if->payloadQ)) { payloadval = (val_value_t *)dlq_deque(¬if->payloadQ); val_add_child(payloadval, eventType); } /* only use a msgid on a real event, not replay * also only use if enabled in the agt_profile */ agt_profile_t *profile = agt_get_profile(); if (checkfilter && profile->agt_notif_sequence_id) { snprintf((char *)numbuff, sizeof(numbuff), "%u", notif->msgid); sequenceid = val_make_simval_obj(sequenceidobj, numbuff, &res); if (!sequenceid) { log_error("\nError: malloc failed: cannot " "add sequence-id"); } else { val_add_child(sequenceid, topval); } } notif->msg = topval; } /* create an RPC message header struct */ xml_msg_init_hdr(&msghdr); /* check if any filtering is needed */ if (checkfilter && sub->filterval) { switch (sub->filtertyp) { case OP_FILTER_SUBTREE: filterpassed = agt_tree_test_filter(&msghdr, sub->scb, sub->filterval, notif->event->parent); break; case OP_FILTER_XPATH: filterpassed = agt_xpath_test_filter(&msghdr, sub->scb, sub->selectval, notif->event->parent); break; case OP_FILTER_NONE: default: filterpassed = FALSE; SET_ERROR(ERR_INTERNAL_VAL); } if (filterpassed) { if (LOGDEBUG2) { log_debug2("\nagt_not: filter passed"); } } else { if (LOGDEBUG) { log_debug("\nagt_not: filter failed"); } } } if (filterpassed) { /* send the notification */ res = ses_start_msg(sub->scb); if (res != NO_ERR) { log_error("\nError: cannot start notification"); xml_msg_clean_hdr(&msghdr); return res; } xml_wr_full_val(sub->scb, &msghdr, notif->msg, 0); ses_finish_msg(sub->scb); sub->scb->stats.outNotifications++; totalstats->stats.outNotifications++; if (LOGDEBUG) { log_debug("\nagt_not: Sent <%s> (%u) on '%s' stream " "for session '%u'", obj_get_name(notif->notobj), notif->msgid, sub->stream, sub->scb->sid); } if (LOGDEBUG2) { log_debug2("\nNotification contents:"); if (notif->msg) { val_dump_value(notif->msg, ses_indent_count(sub->scb)); } log_debug2("\n"); } } xml_msg_clean_hdr(&msghdr); return NO_ERR; } /* send_notification */ /******************************************************************** * FUNCTION delete_oldest_notification * * Clean and free a subscription control block * * INPUTS: * sub == subscription to delete * *********************************************************************/ static void delete_oldest_notification (void) { agt_not_msg_t *msg; agt_not_subscription_t *sub; /* get the oldest message in the replay buffer */ msg = (agt_not_msg_t *)dlq_deque(¬ificationQ); if (msg == NULL) { return; } /* make sure none of the subscriptions are pointing * to this message in tracking the replay progress */ for (sub = (agt_not_subscription_t *) dlq_firstEntry(&subscriptionQ); sub != NULL; sub = (agt_not_subscription_t *)dlq_nextEntry(sub)) { if (sub->firstreplaymsg == msg) { sub->firstreplaymsg = NULL; } if (sub->lastreplaymsg == msg) { sub->lastreplaymsg = NULL; } if (sub->lastmsg == msg) { sub->lastmsg = NULL; } } if (LOGDEBUG2) { log_debug2("\nDeleting oldest notification (id: %u)", msg->msgid); } agt_not_free_notification(msg); if (notification_count > 0) { notification_count--; } else { SET_ERROR(ERR_INTERNAL_VAL); } } /* delete_oldest_notification */ /******************************************************************** * FUNCTION new_notification * * Malloc and initialize the fields in an agt_not_msg_t * * INPUTS: * eventType == object template of the event type * usemsgid == TRUE if this notification will have * a sequence-id, FALSE if not * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static agt_not_msg_t * new_notification (obj_template_t *eventType, boolean usemsgid) { agt_not_msg_t *not; not = m__getObj(agt_not_msg_t); if (!not) { return NULL; } (void)memset(not, 0x0, sizeof(agt_not_msg_t)); dlq_createSQue(¬->payloadQ); if (usemsgid) { not->msgid = ++msgid; if (msgid == 0) { /* msgid is wrapping!!! */ SET_ERROR(ERR_INTERNAL_VAL); } } tstamp_datetime(not->eventTime); not->notobj = eventType; return not; } /* new_notification */ /******************************************************************** * FUNCTION send_replayComplete * * Send the notification * * INPUTS: * sub == subscription to use *********************************************************************/ static void send_replayComplete (agt_not_subscription_t *sub) { agt_not_msg_t *not; status_t res; not = new_notification(replayCompleteobj, FALSE); if (!not) { log_error("\nError: malloc failed; cannot " "send "); return; } res = send_notification(sub, not, FALSE); if (res != NO_ERR && NEED_EXIT(res)) { sub->state = AGT_NOT_STATE_SHUTDOWN; } agt_not_free_notification(not); } /* send_replayComplete */ /******************************************************************** * FUNCTION send_notificationComplete * * Send the notification * * INPUTS: * sub == subscription to use *********************************************************************/ static void send_notificationComplete (agt_not_subscription_t *sub) { agt_not_msg_t *not; status_t res; not = new_notification(notificationCompleteobj, FALSE); if (!not) { log_error("\nError: malloc failed; cannot " "send "); return; } res = send_notification(sub, not, FALSE); if (res != NO_ERR && NEED_EXIT(res)) { sub->state = AGT_NOT_STATE_SHUTDOWN; } agt_not_free_notification(not); } /* send_notificationComplete */ /******************************************************************** * FUNCTION init_static_vars * * Init the static vars * *********************************************************************/ static void init_static_vars (void) { notifmod = NULL; ncnotifmod = NULL; notificationobj = NULL; eventTimeobj = NULL; replayCompleteobj = NULL; notificationCompleteobj = NULL; sequenceidobj = NULL; anySubscriptions = FALSE; msgid = 0; notification_count = 0; } /* init_static_vars */ /************* E X T E R N A L F U N C T I O N S ***************/ /******************************************************************** * FUNCTION agt_not_init * * INIT 1: * Initialize the agent notification module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t agt_not_init (void) { agt_profile_t *agt_profile; status_t res; if (agt_not_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } log_debug2("\nagt_not: Loading notifications module"); agt_profile = agt_get_profile(); dlq_createSQue(&subscriptionQ); dlq_createSQue(¬ificationQ); init_static_vars(); agt_not_init_done = TRUE; /* load the notifications module */ res = ncxmod_load_module(AGT_NOT_MODULE1, NULL, &agt_profile->agt_savedevQ, ¬ifmod); if (res != NO_ERR) { return res; } /* load the nc-notifications module */ res = ncxmod_load_module(AGT_NOT_MODULE2, NULL, &agt_profile->agt_savedevQ, &ncnotifmod); if (res != NO_ERR) { return res; } /* find the object definition for the notification element */ notificationobj = ncx_find_object(notifmod, NCX_EL_NOTIFICATION); if (!notificationobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } eventTimeobj = obj_find_child(notificationobj, AGT_NOT_MODULE1, notifications_N_eventTime); if (!eventTimeobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } replayCompleteobj = ncx_find_object(ncnotifmod, nc_notifications_N_replayComplete); if (!replayCompleteobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } notificationCompleteobj = ncx_find_object(ncnotifmod, nc_notifications_N_notificationComplete); if (!notificationCompleteobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } sequenceidobj = obj_find_child(notificationobj, AGT_NOT_SEQID_MOD, NCX_EL_SEQUENCE_ID); if (!sequenceidobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } return NO_ERR; } /* agt_not_init */ /******************************************************************** * FUNCTION agt_not_init2 * * INIT 2: * Initialize the monitoring data structures * This must be done after the config is loaded * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t agt_not_init2 (void) { obj_template_t *topobj, *streamsobj, *streamobj; obj_template_t *nameobj, *descriptionobj; obj_template_t *replaySupportobj, *replayLogCreationTimeobj; val_value_t *topval, *streamsval, *streamval, *childval; cfg_template_t *runningcfg; status_t res; xmlChar tstampbuff[TSTAMP_MIN_SIZE]; if (!agt_not_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } /* set up create-subscription RPC operation */ res = agt_rpc_register_method(AGT_NOT_MODULE1, notifications_N_create_subscription, AGT_RPC_PH_VALIDATE, create_subscription_validate); if (res != NO_ERR) { return SET_ERROR(res); } res = agt_rpc_register_method(AGT_NOT_MODULE1, notifications_N_create_subscription, AGT_RPC_PH_INVOKE, create_subscription_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* get the running config to add some static data into */ runningcfg = cfg_get_config(NCX_EL_RUNNING); if (!runningcfg || !runningcfg->root) { return SET_ERROR(ERR_INTERNAL_VAL); } /* get all the object nodes first */ topobj = obj_find_template_top(ncnotifmod, AGT_NOT_MODULE2, nc_notifications_N_netconf); if (!topobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } streamsobj = obj_find_child(topobj, AGT_NOT_MODULE2, nc_notifications_N_streams); if (!streamsobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } streamobj = obj_find_child(streamsobj, AGT_NOT_MODULE2, nc_notifications_N_stream); if (!streamobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } nameobj = obj_find_child(streamobj, AGT_NOT_MODULE2, nc_notifications_N_name); if (!nameobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } descriptionobj = obj_find_child(streamobj, AGT_NOT_MODULE2, nc_notifications_N_description); if (!descriptionobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } replaySupportobj = obj_find_child(streamobj, AGT_NOT_MODULE2, nc_notifications_N_replaySupport); if (!replaySupportobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } replayLogCreationTimeobj = obj_find_child(streamobj, AGT_NOT_MODULE2, nc_notifications_N_replayLogCreationTime); if (!replayLogCreationTimeobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* add /netconf */ topval = val_new_value(); if (!topval) { return ERR_INTERNAL_MEM; } val_init_from_template(topval, topobj); /* handing off the malloced memory here */ val_add_child_sorted(topval, runningcfg->root); /* add /netconf/streams */ streamsval = val_new_value(); if (!streamsval) { return ERR_INTERNAL_MEM; } val_init_from_template(streamsval, streamsobj); val_add_child(streamsval, topval); /* add /netconf/streams/stream * creating only one stram for the default NETCONF entry */ streamval = val_new_value(); if (!streamval) { return ERR_INTERNAL_MEM; } val_init_from_template(streamval, streamobj); val_add_child(streamval, streamsval); /* add /netconf/streams/stream/name */ childval = val_make_simval_obj(nameobj, NCX_DEF_STREAM_NAME, &res); if (!childval) { return res; } val_add_child(childval, streamval); /* add /netconf/streams/stream/description */ childval = val_make_simval_obj(descriptionobj, NCX_DEF_STREAM_DESCR, &res); if (!childval) { return res; } val_add_child(childval, streamval); /* add /netconf/streams/stream/replaySupport */ childval = val_make_simval_obj(replaySupportobj, NCX_EL_TRUE, &res); if (!childval) { return res; } val_add_child(childval, streamval); /* set replay start time to now */ tstamp_datetime(tstampbuff); /* add /netconf/streams/stream/replayLogCreationTime */ childval = val_make_simval_obj(replayLogCreationTimeobj, tstampbuff, &res); if (!childval) { return res; } val_add_child(childval, streamval); return NO_ERR; } /* agt_not_init2 */ /******************************************************************** * FUNCTION agt_not_cleanup * * Cleanup the module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ void agt_not_cleanup (void) { agt_not_subscription_t *sub; agt_not_msg_t *msg; if (agt_not_init_done) { init_static_vars(); agt_rpc_unregister_method(AGT_NOT_MODULE1, notifications_N_create_subscription); /* clear the subscriptionQ */ while (!dlq_empty(&subscriptionQ)) { sub = (agt_not_subscription_t *)dlq_deque(&subscriptionQ); free_subscription(sub); } /* clear the notificationQ */ while (!dlq_empty(¬ificationQ)) { msg = (agt_not_msg_t *)dlq_deque(¬ificationQ); agt_not_free_notification(msg); } agt_not_init_done = FALSE; } } /* agt_not_cleanup */ /******************************************************************** * FUNCTION agt_not_send_notifications * * Send out some notifications to the configured subscriptions * if needed. * * Simple design: * go through all the subscriptions and send at most one * notification to each one if needed. This will build * in some throttling based on the ncxserver select loop * timeout (or however this function is called). * * OUTPUTS: * notifications may be written to some active sessions * * RETURNS: * number of notifications sent; * used for simple burst throttling *********************************************************************/ uint32 agt_not_send_notifications (void) { agt_not_subscription_t *sub, *nextsub; agt_not_msg_t *not; xmlChar nowbuff[TSTAMP_MIN_SIZE]; status_t res; int ret; uint32 notcount; if (!anySubscriptions) { return 0; } notcount = 0; tstamp_datetime(nowbuff); for (sub = (agt_not_subscription_t *) dlq_firstEntry(&subscriptionQ); sub != NULL; sub = nextsub) { nextsub = (agt_not_subscription_t *)dlq_nextEntry(sub); switch (sub->state) { case AGT_NOT_STATE_NONE: case AGT_NOT_STATE_INIT: SET_ERROR(ERR_INTERNAL_VAL); break; case AGT_NOT_STATE_REPLAY: /* check if replayComplete is ready */ if (sub->flags & AGT_NOT_FL_RC_READY) { /* yes, check if it has already been done */ if (sub->flags & AGT_NOT_FL_RC_DONE) { /* yes, check if is * needed or if the state should be * changed to AGT_NOT_STATE_LIVE */ if (sub->stopTime) { /* need to terminate the subscription * at some point */ if (sub->flags & AGT_NOT_FL_FUTURESTOP) { /* switch to timed mode */ sub->state = AGT_NOT_STATE_TIMED; } else { /* send */ sub->flags |= AGT_NOT_FL_NC_READY; send_notificationComplete(sub); sub->flags |= AGT_NOT_FL_NC_DONE; notcount++; /* cleanup subscription next time * through this function */ sub->state = AGT_NOT_STATE_SHUTDOWN; } } else { /* no stopTime, so go to live mode */ sub->state = AGT_NOT_STATE_LIVE; } } else { /* send */ send_replayComplete(sub); sub->flags |= AGT_NOT_FL_RC_DONE; notcount++; /* figure out the rest next time through fn */ } } else { /* still sending replay notifications * figure out which one to send next */ if (sub->lastmsg) { not = (agt_not_msg_t *) dlq_nextEntry(sub->lastmsg); } else if (sub->lastmsgid) { /* use ID, back-ptr was cleared */ not = get_entry_after(sub->lastmsgid); } else { /* this is the first replay being sent */ if (sub->firstreplaymsg) { not = sub->firstreplaymsg; } else { /* use ID, back-ptr was cleared */ not = get_entry_after(sub->firstreplaymsgid); } } if (not) { /* found a replay entry to send */ if (!agt_acm_notif_allowed(sub->scb->username, not->notobj)) { log_debug("\nAccess denied to user '%s' " "for notification '%s'", sub->scb->username, obj_get_name(not->notobj)); res = NO_ERR; } else { notcount++; res = send_notification(sub, not, TRUE); } if (res != NO_ERR && NEED_EXIT(res)) { /* treat as a fatal error */ sub->state = AGT_NOT_STATE_SHUTDOWN; } else { /* msg sent OK; set up next loop through fn */ sub->lastmsg = not; sub->lastmsgid = not->msgid; if (sub->lastreplaymsg == not) { /* this was the last replay to send */ sub->flags |= AGT_NOT_FL_RC_READY; } else if (sub->lastreplaymsgid && sub->lastreplaymsgid <= not->msgid) { /* this was the last replay to send */ sub->flags |= AGT_NOT_FL_RC_READY; } } } else { /* nothing left in the replay buffer */ sub->flags |= AGT_NOT_FL_RC_READY; send_replayComplete(sub); sub->flags |= AGT_NOT_FL_RC_DONE; notcount++; if (sub->stopTime) { /* need to terminate the subscription * at some point */ if (sub->flags & AGT_NOT_FL_FUTURESTOP) { /* switch to timed mode */ sub->state = AGT_NOT_STATE_TIMED; } else { sub->flags |= AGT_NOT_FL_NC_READY; } } else { /* no stopTime, so go to live mode */ sub->state = AGT_NOT_STATE_LIVE; } } } break; case AGT_NOT_STATE_TIMED: if (sub->lastmsg) { not = (agt_not_msg_t *)dlq_nextEntry(sub->lastmsg); } else if (sub->lastmsgid) { /* use ID, back-ptr was cleared */ not = get_entry_after(sub->lastmsgid); } else { /* this is the first notification sent */ not = (agt_not_msg_t *)dlq_firstEntry(¬ificationQ); } res = NO_ERR; if (not) { sub->lastmsg = not; sub->lastmsgid = not->msgid; ret = xml_strcmp(sub->stopTime, not->eventTime); if (!agt_acm_notif_allowed(sub->scb->username, not->notobj)) { log_debug("\nAccess denied to user '%s' " "for notification '%s'", sub->scb->username, obj_get_name(not->notobj)); res = NO_ERR; } else { notcount++; res = send_notification(sub, not, TRUE); } if (res != NO_ERR && NEED_EXIT(res)) { /* treat as a fatal error */ sub->state = AGT_NOT_STATE_SHUTDOWN; } } else { /* there is no notification to send */ ret = xml_strcmp(sub->stopTime, nowbuff); } if (ret <= 0) { /* the future stopTime has passed; * start killing the subscription next loop * through this function */ sub->flags |= AGT_NOT_FL_NC_READY; sub->state = AGT_NOT_STATE_SHUTDOWN; } /* else stopTime still in the future */ break; case AGT_NOT_STATE_LIVE: if (sub->lastmsg) { not = (agt_not_msg_t *)dlq_nextEntry(sub->lastmsg); } else if (sub->lastmsgid) { /* use ID, back-ptr was cleared */ not = get_entry_after(sub->lastmsgid); } else { /* this is the first notification sent */ not = (agt_not_msg_t *)dlq_firstEntry(¬ificationQ); } if (not) { sub->lastmsg = not; sub->lastmsgid = not->msgid; if (!agt_acm_notif_allowed(sub->scb->username, not->notobj)) { log_debug("\nAccess denied to user '%s' " "for notification '%s'", sub->scb->username, obj_get_name(not->notobj)); res = NO_ERR; } else { notcount++; res = send_notification(sub, not, TRUE); } if (res != NO_ERR && NEED_EXIT(res)) { /* treat as a fatal error */ sub->state = AGT_NOT_STATE_SHUTDOWN; } } /* else don't do anything */ break; case AGT_NOT_STATE_SHUTDOWN: /* terminating the subscription after * the is sent, * only if the stopTime was set */ if (sub->stopTime) { if (!(sub->flags & AGT_NOT_FL_NC_DONE)) { send_notificationComplete(sub); sub->flags |= AGT_NOT_FL_NC_DONE; notcount++; break; } } dlq_remove(sub); expire_subscription(sub); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } return notcount; } /* agt_not_send_notifications */ /******************************************************************** * FUNCTION agt_not_clean_eventlog * * Remove any delivered notifications when the replay buffer * size is set to zero * *********************************************************************/ void agt_not_clean_eventlog (void) { const agt_profile_t *agt_profile; agt_not_subscription_t *sub; agt_not_msg_t *msg, *nextmsg; uint32 lowestmsgid; agt_profile = agt_get_profile(); if (agt_profile->agt_eventlog_size > 0) { return; } if (!anySubscriptions) { /* zap everything in the Q, since there * are no subscriptions right now */ while (!dlq_empty(¬ificationQ)) { msg = (agt_not_msg_t *)dlq_deque(¬ificationQ); agt_not_free_notification(msg); } return; } /* find the lowest msgid that has been delivered * to all the sessions, and any messages in the Q * that are lower than that can be deleted */ lowestmsgid = NCX_MAX_UINT; for (sub = (agt_not_subscription_t *) dlq_firstEntry(&subscriptionQ); sub != NULL; sub = (agt_not_subscription_t *)dlq_nextEntry(sub)) { if (sub->lastmsgid && sub->lastmsgid < lowestmsgid) { lowestmsgid = sub->lastmsgid; } } /* keep deleting the oldest entries until the * lowest msg ID is passed yb in the buffer */ for (msg = (agt_not_msg_t *)dlq_firstEntry(¬ificationQ); msg != NULL; msg = nextmsg) { nextmsg = (agt_not_msg_t *)dlq_nextEntry(msg); if (msg->msgid < lowestmsgid) { dlq_remove(msg); agt_not_free_notification(msg); } else { return; } } } /* agt_not_clean_eventlog */ /******************************************************************** * FUNCTION agt_not_remove_subscription * * Remove and expire a subscription with the specified session ID. * The session is being removed. * * INPUTS: * sid == session ID to use *********************************************************************/ void agt_not_remove_subscription (ses_id_t sid) { agt_not_subscription_t *sub; if (!anySubscriptions) { return; } for (sub = (agt_not_subscription_t *) dlq_firstEntry(&subscriptionQ); sub != NULL; sub = (agt_not_subscription_t *)dlq_nextEntry(sub)) { if (sub->sid == sid) { dlq_remove(sub); expire_subscription(sub); return; } } } /* agt_not_remove_subscription */ /******************************************************************** * FUNCTION agt_not_new_notification * * Malloc and initialize the fields in an agt_not_msg_t * * INPUTS: * eventType == object template of the event type * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ agt_not_msg_t * agt_not_new_notification (obj_template_t *eventType) { #ifdef DEBUG if (!eventType) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return new_notification(eventType, TRUE); } /* agt_not_new_notification */ /******************************************************************** * FUNCTION agt_not_free_notification * * Scrub the memory in an agt_not_template_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * notif == agt_not_template_t to delete *********************************************************************/ void agt_not_free_notification (agt_not_msg_t *notif) { val_value_t *val; #ifdef DEBUG if (!notif) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (!dlq_empty(¬if->payloadQ)) { val = (val_value_t *)dlq_deque(¬if->payloadQ); val_free_value(val); } if (notif->msg) { val_free_value(notif->msg); } m__free(notif); } /* agt_not_free_notification */ /******************************************************************** * FUNCTION agt_not_add_to_payload * * Queue the specified value node into the payloadQ * for the specified notification * * INPUTS: * notif == notification to send * val == value to add to payloadQ * !!! THIS IS LIVE MALLOCED MEMORY PASSED OFF * !!! TO THIS FUNCTION. IT WILL BE FREED LATER * !!! DO NOT CALL val_free_value * !!! AFTER THIS CALL * * OUTPUTS: * val added to the notif->payloadQ * *********************************************************************/ void agt_not_add_to_payload (agt_not_msg_t *notif, val_value_t *val) { #ifdef DEBUG if (!notif || !val) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif dlq_enque(val, ¬if->payloadQ); } /* agt_not_add_to_payload */ /******************************************************************** * FUNCTION agt_not_queue_notification * * Queue the specified notification in the replay log. * It will be sent to all the active subscriptions * as needed. * * INPUTS: * notif == notification to send * !!! THIS IS LIVE MALLOCED MEMORY PASSED OFF * !!! TO THIS FUNCTION. IT WILL BE FREED LATER * !!! DO NOT CALL agt_not_free_notification * !!! AFTER THIS CALL * * OUTPUTS: * message added to the notificationQ * *********************************************************************/ void agt_not_queue_notification (agt_not_msg_t *notif) { const agt_profile_t *agt_profile; #ifdef DEBUG if (!notif) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (!agt_not_init_done) { SET_ERROR(ERR_INTERNAL_INIT_SEQ); return; } if (LOGDEBUG2) { log_debug2("\nQueueing <%s> notification to send (id: %u)", (notif->notobj) ? obj_get_name(notif->notobj) : (const xmlChar *)"??", notif->msgid); if (LOGDEBUG3) { log_debug3("\nEvent Payload:"); val_value_t *payload = (val_value_t *) dlq_firstEntry(¬if->payloadQ); if (payload == NULL) { log_debug3(" none"); } else { for (; payload != NULL; payload = (val_value_t *)dlq_nextEntry(payload)) { val_dump_value(payload, NCX_DEF_INDENT); } } } } agt_profile = agt_get_profile(); if (agt_profile->agt_eventlog_size) { assert(notification_count<=agt_profile->agt_eventlog_size); if (notification_count == agt_profile->agt_eventlog_size) { delete_oldest_notification(); } notification_count++; dlq_enque(notif, ¬ificationQ); } else { /* not tracking the event log size * since the entries will get deleted once * they are sent to all active subscriptions */ dlq_enque(notif, ¬ificationQ); } agt_not_queue_notification_cb(notif); } /* agt_not_queue_notification */ /******************************************************************** * FUNCTION agt_not_is_replay_event * * Check if the specified notfication is the replayComplete * or notificationComplete notification events * * INPUTS: * notifobj == notification object to check * * RETURNS: * TRUE if the notification object is one of the special * replay buffer events * FALSE otherwise *********************************************************************/ boolean agt_not_is_replay_event (const obj_template_t *notifobj) { #ifdef DEBUG if (notifobj == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (!agt_not_init_done) { SET_ERROR(ERR_INTERNAL_INIT_SEQ); return FALSE; } if (notifobj == replayCompleteobj || notifobj == notificationCompleteobj) { return TRUE; } return FALSE; } /* agt_not_is_replay_event */ /* END file agt_not.c */ yuma123_2.14/netconf/src/agt/agt_timer.h0000664000175000017500000001234414770023131020251 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_timer #define _H_agt_timer /* FILE: agt_timer.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Handle timer services for the server ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 23-jan-07 abb Begun */ #include #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* timer callback function * * Process the timer expired event * * INPUTS: * timer_id == timer identifier * cookie == context pointer, such as a session control block, * passed to agt_timer_set function (may be NULL) * * RETURNS: * 0 == normal exit * -1 == error exit, delete timer upon return */ typedef int (*agt_timer_fn_t) (uint32 timer_id, void *cookie); typedef struct agt_timer_cb_t_ { dlq_hdr_t qhdr; boolean timer_periodic; uint32 timer_id; agt_timer_fn_t timer_cbfn; time_t timer_start_time; uint32 timer_duration; /* seconds */ void *timer_cookie; } agt_timer_cb_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_timer_init * * Initialize the agt_timer module * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ extern void agt_timer_init (void); /******************************************************************** * FUNCTION agt_timer_cleanup * * Cleanup the agt_timer module. * *********************************************************************/ extern void agt_timer_cleanup (void); /******************************************************************** * FUNCTION agt_timer_handler * * Handle an incoming server timer polling interval * main routine called by agt_signal_handler * *********************************************************************/ extern void agt_timer_handler (void); /******************************************************************** * FUNCTION agt_timer_create * * Malloc and start a new timer control block * * INPUTS: * seconds == number of seconds to wait between polls * is_periodic == TRUE if periodic timer * FALSE if a 1-event timer * timer_fn == address of callback function to invoke when * the timer poll event occurs * cookie == address of user cookie to pass to the timer_fn * ret_timer_id == address of return timer ID * * OUTPUTS: * *ret_timer_id == timer ID for the allocated timer, * if the return value is NO_ERR * * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ extern status_t agt_timer_create (uint32 seconds, boolean is_periodic, agt_timer_fn_t timer_fn, void *cookie, uint32 *ret_timer_id); /******************************************************************** * FUNCTION agt_timer_restart * * Restart a timer with a new timeout value. * If this is a periodic timer, then the interval * will be changed to the new value. Otherwise * a 1-shot timer will just be reset to the new value * * INPUTS: * timer_id == timer ID to reset * seconds == new timeout value * * RETURNS: * status, NO_ERR if all okay, *********************************************************************/ extern status_t agt_timer_restart (uint32 timer_id, uint32 seconds); /******************************************************************** * FUNCTION agt_timer_delete * * Remove and delete a timer control block * periodic timers need to be deleted to be stopped * 1-shot timers will be deleted automatically after * they expire and the callback is invoked * * INPUTS: * timer_id == timer ID to destroy * *********************************************************************/ extern void agt_timer_delete (uint32 timer_id); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_timer */ yuma123_2.14/netconf/src/agt/agt_cap.c0000664000175000017500000003477314770023131017701 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_cap.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 03feb06 abb begun; split out from base/cap.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cap.h" #include "cap.h" #include "cfg.h" #include "ncx.h" #include "ncxconst.h" #include "ncxmod.h" #include "ses.h" #include "status.h" #include "typ.h" #include "xml_val.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static val_value_t *agt_caps = NULL; static cap_list_t *my_agt_caps = NULL; /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION agt_cap_cleanup * * Clean the NETCONF agent capabilities * * INPUTS: * none * RETURNS: * none *********************************************************************/ void agt_cap_cleanup (void) { if (agt_caps) { val_free_value(agt_caps); agt_caps = NULL; } if (my_agt_caps) { cap_clean_caplist(my_agt_caps); m__free(my_agt_caps); my_agt_caps = NULL; } } /* agt_cap_cleanup */ /******************************************************************** * FUNCTION agt_cap_set_caps * * Initialize the NETCONF agent capabilities * * INPUTS: * agttarg == the target of edit-config for this agent * agtstart == the type of startup configuration for this agent * defstyle == default with-defaults style for the entire agent * * RETURNS: * NO_ERR if all goes well *********************************************************************/ status_t agt_cap_set_caps (ncx_agttarg_t agttarg, ncx_agtstart_t agtstart, const xmlChar *defstyle) { const agt_profile_t *agt_profile; val_value_t *oldcaps, *newcaps; cap_list_t *oldmycaps,*newmycaps; xmlns_id_t nc_id; status_t res; res = NO_ERR; newcaps = NULL; nc_id = xmlns_nc_id(); oldcaps = agt_caps; oldmycaps = my_agt_caps; agt_profile = agt_get_profile(); /* get a new cap_list */ newmycaps = cap_new_caplist(); if (!newmycaps) { res = ERR_INTERNAL_MEM; } /* get a new val_value_t cap list for agent messages */ if (res == NO_ERR) { newcaps = xml_val_new_struct(NCX_EL_CAPABILITIES, nc_id); if (!newcaps) { res = ERR_INTERNAL_MEM; } } /* add capability for NETCONF version 1.0 and/or 1.1 support */ if (res == NO_ERR) { if (ncx_protocol_enabled(NCX_PROTO_NETCONF10)) { res = cap_add_std(newmycaps, CAP_STDID_V1); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_V1); } } if (ncx_protocol_enabled(NCX_PROTO_NETCONF11)) { res = cap_add_std(newmycaps, CAP_STDID_V11); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_V11); } } } if (res == NO_ERR) { /* set the capabilities based on the native target */ switch (agttarg) { case NCX_AGT_TARG_RUNNING: res = cap_add_std(newmycaps, CAP_STDID_WRITE_RUNNING); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_WRITE_RUNNING); } break; case NCX_AGT_TARG_CANDIDATE: res = cap_add_std(newmycaps, CAP_STDID_CANDIDATE); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_CANDIDATE); } if (res == NO_ERR) { if (ncx_protocol_enabled(NCX_PROTO_NETCONF10)) { res = cap_add_std(newmycaps, CAP_STDID_CONF_COMMIT); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_CONF_COMMIT); } } if (ncx_protocol_enabled(NCX_PROTO_NETCONF11)) { res = cap_add_std(newmycaps, CAP_STDID_CONF_COMMIT11); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_CONF_COMMIT11); } } } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); break; } } if (res == NO_ERR) { /* set the rollback-on-error capability */ res = cap_add_std(newmycaps, CAP_STDID_ROLLBACK_ERR); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_ROLLBACK_ERR); } } if (res == NO_ERR) { if (agt_profile->agt_usevalidate) { /* set the validate capability */ if (ncx_protocol_enabled(NCX_PROTO_NETCONF10)) { res = cap_add_std(newmycaps, CAP_STDID_VALIDATE); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_VALIDATE); } } if (ncx_protocol_enabled(NCX_PROTO_NETCONF11)) { res = cap_add_std(newmycaps, CAP_STDID_VALIDATE11); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_VALIDATE11); } } } } /* check the startup type for distinct-startup capability */ if (res == NO_ERR) { if (agtstart==NCX_AGT_START_DISTINCT) { res = cap_add_std(newmycaps, CAP_STDID_STARTUP); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_STARTUP); } } } /* set the url capability */ if (res == NO_ERR) { if (agt_profile->agt_useurl) { res = cap_add_url(newmycaps, AGT_URL_SCHEME_LIST); if (res == NO_ERR) { res = cap_add_urlval(newcaps, AGT_URL_SCHEME_LIST); } } } /* set the xpath capability */ if (res == NO_ERR) { res = cap_add_std(newmycaps, CAP_STDID_XPATH); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_XPATH); } } /* set the notification capability */ if (res == NO_ERR) { res = cap_add_std(newmycaps, CAP_STDID_NOTIFICATION); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_NOTIFICATION); } } /* set the interleave capability */ if (res == NO_ERR) { res = cap_add_std(newmycaps, CAP_STDID_INTERLEAVE); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_INTERLEAVE); } } /* set the partial-lock capability */ if (res == NO_ERR) { res = cap_add_std(newmycaps, CAP_STDID_PARTIAL_LOCK); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_PARTIAL_LOCK); } } /* set the yang-library capability */ if (res == NO_ERR) { res = cap_add_std(newmycaps, CAP_STDID_YANG_LIBRARY); if (res == NO_ERR) { #if 0 res = cap_add_stdval(newcaps, CAP_STDID_YANG_LIBRARY); #else res = cap_add_yang_library_val(newcaps, "2016-06-21", "123"); #endif } } /* set the with-defaults capability */ if (res == NO_ERR) { res = cap_add_withdef(newmycaps, defstyle); if (res == NO_ERR) { res = cap_add_withdefval(newcaps, defstyle); } } /* check the return value */ if (res != NO_ERR) { /* toss the new, put back the old */ cap_free_caplist(newmycaps); val_free_value(newcaps); my_agt_caps = oldmycaps; agt_caps = oldcaps; } else { /* toss the old, install the new */ if (oldmycaps) { cap_free_caplist(oldmycaps); } if (oldcaps) { val_free_value(oldcaps); } my_agt_caps = newmycaps; agt_caps = newcaps; } return res; } /* agt_cap_set_caps */ /******************************************************************** * FUNCTION agt_cap_set_modules * * Initialize the NETCONF agent capabilities modules list * MUST call after agt_cap_set_caps * * INPUTS: * profile == agent profile control block to use * * RETURNS: * status *********************************************************************/ status_t agt_cap_set_modules (agt_profile_t *profile) { ncx_module_t *mod; ncx_save_deviations_t *savedev; status_t res; if (!agt_caps || !my_agt_caps) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } res = NO_ERR; mod = ncx_get_first_module(); /* add capability for each module loaded in ncxmod */ while (mod && res == NO_ERR) { /* keep internal modules out of the capabilities */ if (agt_advertise_module_needed(mod->name)) { res = cap_add_modval(agt_caps, mod); } mod = (ncx_module_t *)dlq_nextEntry(mod); } /* add capability for each deviation module, not already * listed in the module capabilities so far */ for (savedev = (ncx_save_deviations_t *) dlq_firstEntry(&profile->agt_savedevQ); savedev != NULL && res == NO_ERR; savedev = (ncx_save_deviations_t *) dlq_nextEntry(savedev)) { if (agt_advertise_module_needed(savedev->devmodule)) { /* make sure this is not a hard-wired internal module * or a duplicate already loaded as a regular module */ mod = ncx_find_module(savedev->devmodule, savedev->devrevision); if (mod == NULL) { /* not already announced in the capabilities */ res = cap_add_devmodval(agt_caps, savedev); } } } return res; } /* agt_cap_set_modules */ /******************************************************************** * FUNCTION agt_cap_add_module * * Add a module at runtime, after the initial set has been set * MUST call after agt_cap_set_caps * * RETURNS: * status *********************************************************************/ status_t agt_cap_add_module (ncx_module_t *mod) { #ifdef DEBUG if (!mod) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (!agt_caps || !my_agt_caps) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } if (agt_advertise_module_needed(mod->name)) { return cap_add_modval(agt_caps, mod); } else { return NO_ERR; } } /* agt_cap_add_module */ /******************************************************************** * FUNCTION agt_cap_get_caps * * Get the NETCONF agent capabilities * * INPUTS: * none * RETURNS: * pointer to the agent caps list *********************************************************************/ cap_list_t * agt_cap_get_caps (void) { return my_agt_caps; } /* agt_cap_get_caps */ /******************************************************************** * FUNCTION agt_cap_get_capsval * * Get the NETCONF agent capabilities in val_value_t format * * INPUTS: * none * RETURNS: * pointer to the agent caps list *********************************************************************/ val_value_t * agt_cap_get_capsval (void) { /* update the module-set-id in ietf-yang-library */ cfg_template_t *runningcfg; val_value_t *modules_state_val; val_value_t *module_set_id_val; status_t res; /* get the running config */ runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); if(runningcfg) { modules_state_val = val_find_child(runningcfg->root, "ietf-yang-library", "modules-state"); if(modules_state_val) { if (val_is_virtual(modules_state_val)) { modules_state_val = val_get_virtual_value(NULL, modules_state_val, &res); assert(modules_state_val && res==NO_ERR); } module_set_id_val = val_find_child(modules_state_val, "ietf-yang-library", "module-set-id"); assert(module_set_id_val); res = cap_update_yang_library_val(agt_caps, "2016-06-21", VAL_STRING(module_set_id_val)); assert(res==NO_ERR); } } return agt_caps; } /* agt_cap_get_capsval */ /******************************************************************** * FUNCTION agt_cap_std_set * * Check if the STD capability is set for the agent * * INPUTS: * cap == ID of capability to check * * RETURNS: * TRUE is STD cap set, FALSE otherwise *********************************************************************/ boolean agt_cap_std_set (cap_stdid_t cap) { return cap_std_set(my_agt_caps, cap); } /* agt_cap_std_set */ /* END file agt_cap.c */ yuma123_2.14/netconf/src/agt/agt_proc.c0000664000175000017500000004276714770023131020103 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_proc.c /proc file system monitoring module ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 18jul09 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_proc.h" #include "agt_rpc.h" #include "agt_util.h" #include "cfg.h" #include "getcb.h" #include "log.h" #include "ncxmod.h" #include "ncxtypes.h" #include "rpc.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define proc_MOD (const xmlChar *)"yuma-proc" #define proc_MOD_REV NULL #define proc_N_proc (const xmlChar *)"proc" #define proc_N_cpuinfo (const xmlChar *)"cpuinfo" #define proc_N_cpu (const xmlChar *)"cpu" #define proc_N_meminfo (const xmlChar *)"meminfo" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_proc_init_done = FALSE; static ncx_module_t *procmod; static val_value_t *myprocval; static obj_template_t *myprocobj; /******************************************************************** * FUNCTION is_proc_supported * * Check if at least /proc/meminfo and /proc/cpuinfo exist * * RETURNS: * TRUE if minimum /proc support found * FALSE if minimum /proc support not found *********************************************************************/ static boolean is_proc_supported (void) { struct stat statbuf; int ret; memset(&statbuf, 0x0, sizeof(statbuf)); ret = stat("/proc/meminfo", &statbuf); if (ret == 0 && S_ISREG(statbuf.st_mode)) { memset(&statbuf, 0x0, sizeof(statbuf)); ret = stat("/proc/cpuinfo", &statbuf); if (ret == 0 && S_ISREG(statbuf.st_mode)) { return TRUE; } } return FALSE; } /* is_proc_supported */ /******************************************************************** * FUNCTION make_proc_varname * * Make a proc var name string * * RETURNS: * malloced and filled in string *********************************************************************/ static xmlChar * make_proc_varname (const char *varname, int varnamelen) { const char *str; xmlChar *buff, *retbuff; int count; retbuff = (xmlChar *)m__getMem(varnamelen+1); if (retbuff == NULL) { return NULL; } count = 0; str = varname; buff = retbuff; while (*str && count < varnamelen) { if (isalnum((xmlChar)*str)) { *buff++ = (xmlChar)*str++; } else { *buff++ = (xmlChar)'_'; str++; } count++; } *buff = (xmlChar)'\0'; return retbuff; } /* make_proc_varname */ /******************************************************************** * FUNCTION make_proc_value * * Make a proc value string * * INPUTS: * line == pointer to input line to process * * RETURNS: * malloced and filled in string *********************************************************************/ static xmlChar * make_proc_value (const char *line) { const char *str, *start; uint32 valuelen; str = line; while (isspace((xmlChar)*str) && *str != '\n' && *str != '\0') { str++; } if (*str == '\n' || *str == '\0') { start = line; valuelen = 0; } else { start = str++; while (*str != '\n' && *str != '\0') { str++; } valuelen = (uint32)(str - start); } return xml_strndup((const xmlChar *)start, valuelen); } /* make_proc_value */ /******************************************************************** * FUNCTION make_proc_leaf * * make a val_value_t struct for the leaf found on the * current buffer line * * INPUTS: * buffer == input line buffer to check * parentobj == parent object for this leaf * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced and filled in leaf *********************************************************************/ static val_value_t * make_proc_leaf (char *buffer, obj_template_t *parentobj, status_t *res) { obj_template_t *parmobj; val_value_t *parmval; xmlChar *parmname, *parmvalstr; char *colonchar, *str; int parmnamelen; *res = NO_ERR; colonchar = strchr(buffer, ':'); if (colonchar == NULL) { /* skip this line, no colon char found */ if (LOGDEBUG) { log_debug("\nagt_proc: skipping line '%s'", buffer); } return NULL; } /* look backward until the first non-whitespace */ str = colonchar - 1; while (str >= buffer && isspace((xmlChar)*str)) { str--; } if (str == buffer) { /* no keyword found */ if (LOGDEBUG) { log_debug("\nagt_proc: skipping line '%s'", buffer); } return NULL; } /* get the converted parm name */ parmnamelen = str-buffer+1; parmname = make_proc_varname(buffer, parmnamelen); if (parmname == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } /* find the leaf for this parm name */ parmobj = obj_find_child(parentobj, proc_MOD, parmname); m__free(parmname); if (parmobj == NULL) { /* no parameter to match this line */ if (LOGDEBUG) { log_debug("\nagt_proc: skipping <%s> line '%s'", obj_get_name(parentobj), buffer); } return NULL; } /* found the leaf, get the trimmed value string */ parmvalstr = make_proc_value(colonchar+1); if (parmvalstr == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } /* make the leaf value */ parmval = val_make_simval_obj(parmobj, parmvalstr, res); m__free(parmvalstr); if (parmval == NULL) { log_error("\nError: make /proc leaf <%s> failed", obj_get_name(parmobj)); } return parmval; } /* make_proc_leaf */ /******************************************************************** * FUNCTION add_cpuinfo * * make a val_value_t struct for the /proc/cpuinfo file * and add it as a child to the specified container value * INPUTS: * procval == parent value struct to add the cpuinfo as a child * * RETURNS: * status *********************************************************************/ static status_t add_cpuinfo (val_value_t *procval) { FILE *cpuinfofile; obj_template_t *cpuinfoobj, *cpuobj; val_value_t *cpuinfoval, *cpuval, *parmval; char *buffer, *readtest; boolean done; status_t res; /* find the cpuinfo object */ cpuinfoobj = obj_find_child(myprocobj, proc_MOD, proc_N_cpuinfo); if (cpuinfoobj == NULL) { return ERR_NCX_DEF_NOT_FOUND; } /* find the cpu object */ cpuobj = obj_find_child(cpuinfoobj, proc_MOD, proc_N_cpu); if (cpuobj == NULL) { return ERR_NCX_DEF_NOT_FOUND; } /* open the /proc/cpuinfo file for reading */ cpuinfofile = fopen("/proc/cpuinfo", "r"); if (cpuinfofile == NULL) { return errno_to_status(); } /* get a file read line buffer */ buffer = m__getMem(NCX_MAX_LINELEN); if (buffer == NULL) { fclose(cpuinfofile); return ERR_INTERNAL_MEM; } /* create cpuinfo container */ cpuinfoval = val_new_value(); if (cpuinfoval == NULL) { m__free(buffer); fclose(cpuinfofile); return ERR_INTERNAL_MEM; } val_init_from_template(cpuinfoval, cpuinfoobj); /* hand off cpuinfoval memory here */ val_add_child(cpuinfoval, procval); /* loop through the file until done */ res = NO_ERR; cpuval = NULL; done = FALSE; while (!done) { readtest = fgets(buffer, NCX_MAX_LINELEN, cpuinfofile); if (readtest == NULL) { done = TRUE; continue; } if (cpuval == NULL) { cpuval = val_new_value(); if (cpuval == NULL) { res = ERR_INTERNAL_MEM; done = TRUE; continue; } else { val_init_from_template(cpuval, cpuobj); /* hand off cpuval memory here */ val_add_child(cpuval, cpuinfoval); } } /* else already have an active 'cpu' entry */ if (strlen(buffer) == 1 && *buffer == '\n') { /* force a new CPU entry */ if (cpuval) { res = val_gen_index_chain(cpuobj, cpuval); if (res == NO_ERR) { cpuval = NULL; } else { log_error("\nError:val_gen_index failed (%s)", get_error_string(res)); } /* else empty line in cpu entry, e.g. arm */ } } else { res = NO_ERR; parmval = make_proc_leaf(buffer, cpuobj, &res); if (parmval) { val_add_child(parmval, cpuval); } } } if (res == NO_ERR && cpuval) { if (val_get_first_index(cpuval) == NULL) { val_gen_index_chain(cpuobj, cpuval); } } fclose(cpuinfofile); m__free(buffer); return res; } /* add_cpuinfo */ /******************************************************************** * FUNCTION get_meminfo * * operation handler for the meminfo NP container * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ static status_t get_meminfo (ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *virval, val_value_t *dstval) { FILE *meminfofile; obj_template_t *meminfoobj; val_value_t *parmval; char *buffer, *readtest; boolean done; status_t res; (void)scb; res = NO_ERR; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } meminfoobj = virval->obj; /* open the /proc/meminfo file for reading */ meminfofile = fopen("/proc/meminfo", "r"); if (meminfofile == NULL) { return errno_to_status(); } /* get a file read line buffer */ buffer = m__getMem(NCX_MAX_LINELEN); if (buffer == NULL) { fclose(meminfofile); return ERR_INTERNAL_MEM; } /* loop through the file until done */ res = NO_ERR; done = FALSE; while (!done) { readtest = fgets(buffer, NCX_MAX_LINELEN, meminfofile); if (readtest == NULL) { done = TRUE; continue; } if (strlen(buffer) == 1 && *buffer == '\n') { ; } else { res = NO_ERR; parmval = make_proc_leaf(buffer, meminfoobj, &res); if (parmval) { val_add_child(parmval, dstval); } } } fclose(meminfofile); m__free(buffer); return res; } /* get_meminfo */ /******************************************************************** * FUNCTION add_meminfo * * make a val_value_t struct for the /proc/meminfo file * and add it as a child to the specified container value * INPUTS: * procval == parent value struct to add the cpuinfo as a child * * RETURNS: * status *********************************************************************/ static status_t add_meminfo (val_value_t *procval) { obj_template_t *meminfoobj; val_value_t *meminfoval; /* find the meminfo object */ meminfoobj = obj_find_child(myprocobj, proc_MOD, proc_N_meminfo); if (meminfoobj == NULL) { return ERR_NCX_DEF_NOT_FOUND; } /* create meminfo virtual NP container */ meminfoval = val_new_value(); if (meminfoval == NULL) { return ERR_INTERNAL_MEM; } val_init_virtual(meminfoval, get_meminfo, meminfoobj); /* hand off meminfoval memory here */ val_add_child(meminfoval, procval); return NO_ERR; } /* add_meminfo */ /************* E X T E R N A L F U N C T I O N S ***************/ /******************************************************************** * FUNCTION agt_proc_init * * INIT 1: * Initialize the proc monitor module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t agt_proc_init (void) { agt_profile_t *agt_profile; status_t res; if (agt_proc_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } log_debug2("\nagt: Loading proc module"); agt_profile = agt_get_profile(); procmod = NULL; myprocval = NULL; myprocobj = NULL; agt_proc_init_done = TRUE; /* load the netconf-state module */ res = ncxmod_load_module(proc_MOD, proc_MOD_REV, &agt_profile->agt_savedevQ, &procmod); return res; } /* agt_proc_init */ /******************************************************************** * FUNCTION agt_proc_init2 * * INIT 2: * Initialize the monitoring data structures * This must be done after the config is loaded * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t agt_proc_init2 (void) { cfg_template_t *runningcfg; status_t res; if (!agt_proc_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } res = NO_ERR; /* check if /proc file system supported */ if (!is_proc_supported()) { if (LOGDEBUG) { log_debug("\nagt_proc: no /proc support found"); } return NO_ERR; } runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (!runningcfg || !runningcfg->root) { return SET_ERROR(ERR_INTERNAL_VAL); } /* get all the object nodes first */ myprocobj = obj_find_template_top(procmod, proc_MOD, proc_N_proc); if (!myprocobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* add /proc */ myprocval = val_new_value(); if (!myprocval) { return ERR_INTERNAL_MEM; } val_init_from_template(myprocval, myprocobj); /* handing off the malloced memory here */ val_add_child_sorted(myprocval, runningcfg->root); res = add_cpuinfo(myprocval); if (res == NO_ERR) { res = add_meminfo(myprocval); } return res; } /* agt_proc_init2 */ /******************************************************************** * FUNCTION agt_proc_cleanup * * Cleanup the module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ void agt_proc_cleanup (void) { if (agt_proc_init_done) { procmod = NULL; myprocval = NULL; myprocval = NULL; agt_proc_init_done = FALSE; } } /* agt_proc_cleanup */ /* END file agt_proc.c */ yuma123_2.14/netconf/src/agt/agt_proc.h0000664000175000017500000000675114770023131020101 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_proc #define _H_agt_proc /* FILE: agt_proc.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF /proc file system monitoring ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 18-jul-09 abb Begun. */ #ifndef _H_status #include "status.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_proc_init * * INIT 1: * Initialize the proc monitor module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_proc_init (void); /******************************************************************** * FUNCTION agt_proc_init2 * * INIT 2: * Initialize the monitoring data structures * This must be done after the config is loaded * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_proc_init2 (void); /******************************************************************** * FUNCTION agt_proc_cleanup * * Cleanup the module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void agt_proc_cleanup (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_proc */ yuma123_2.14/netconf/src/agt/agt_cb.c0000664000175000017500000005215214770023131017511 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2013 - 2016, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_cb.c Manage Agent callbacks for data model manipulation ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 16apr07 abb begun; split out from agt_ps.c 01aug08 abb rewrite for YANG OBJ only data tree ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include "agt_cb.h" #include "agt_util.h" #include "obj.h" #include "xpath.h" #include "yang.h" /******************************************************************** * * * C O N S T A N T S * * * ********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ typedef enum agt_cb_status_t_ { AGTCB_STAT_NONE, AGTCB_STAT_INIT_DONE, AGTCB_STAT_LOADED, AGTCB_STAT_LOAD_FAILED } agt_cb_status_t; typedef struct agt_cb_modhdr_t_ { dlq_hdr_t qhdr; const xmlChar *modname; const xmlChar *modversion; dlq_hdr_t callbackQ; /* Q of agt_cb_set_t */ agt_cb_status_t loadstatus; status_t status; } agt_cb_modhdr_t; typedef struct agt_cb_set_t_ { dlq_hdr_t qhdr; agt_cb_modhdr_t *parent; const xmlChar *defpath; const xmlChar *version; agt_cb_fnset_t cbset; agt_cb_status_t loadstatus; status_t status; } agt_cb_set_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ boolean agt_cb_init_done = FALSE; dlq_hdr_t modhdrQ; /******************************************************************** * FUNCTION free_callback * * Clean and free a callback struct * * INPUTS: * callback == agt_cb_set_t struct to clean and free * *********************************************************************/ static void free_callback (agt_cb_set_t *callback) { m__free(callback); } /* free_callback */ /******************************************************************** * FUNCTION new_callback * * Malloc and init a new callback header * * INPUTS: * defpath == Xpath OID for definition object target * version == expected revision data for this object * cbset == set of callback functions to copy * * RETURNS: * malloced and initialized callback header struct *********************************************************************/ static agt_cb_set_t * new_callback (agt_cb_modhdr_t *parent, const xmlChar *defpath, const xmlChar *version, const agt_cb_fnset_t *cbset) { agt_cb_set_t *callback; agt_cbtyp_t cbtyp; callback = m__getObj(agt_cb_set_t); if (!callback) { return NULL; } memset(callback, 0x0, sizeof(agt_cb_set_t)); callback->defpath = defpath; callback->version = version; callback->parent = parent; callback->loadstatus = AGTCB_STAT_INIT_DONE; callback->status = NO_ERR; for (cbtyp = AGT_CB_VALIDATE; cbtyp <= AGT_CB_ROLLBACK; cbtyp++) { callback->cbset.cbfn[cbtyp] = cbset->cbfn[cbtyp]; } return callback; } /* new_callback */ /******************************************************************** * FUNCTION free_modhdr * * Clean and free a modhdr struct * * INPUTS: * modhdr == agt_cb_modhdr_t struct to clean and free * *********************************************************************/ static void free_modhdr (agt_cb_modhdr_t *modhdr) { agt_cb_set_t *callback; while (!dlq_empty(&modhdr->callbackQ)) { callback = (agt_cb_set_t *) dlq_deque(&modhdr->callbackQ); free_callback(callback); } m__free(modhdr); } /* free_modhdr */ /******************************************************************** * FUNCTION find_callback * * Find a callback record in a modhdr record * * INPUTS: * modhdr == agt_cb_modhdr_t struct to check * defpath == definition path string to find * cbfn == callback function pointer * * RETURNS: * pointer to found callback record or NULL if not found *********************************************************************/ static agt_cb_set_t * find_callback (agt_cb_modhdr_t *modhdr, const xmlChar *defpath, const agt_cb_fn_t cbfn) { agt_cb_set_t *callback; int ret; for (callback = (agt_cb_set_t *) dlq_firstEntry(&modhdr->callbackQ); callback != NULL; callback = (agt_cb_set_t *)dlq_nextEntry(callback)) { ret = xml_strcmp(defpath, callback->defpath); if (ret == 0) { if(cbfn==NULL || callback->cbset.cbfn[AGT_CB_VALIDATE]==cbfn) { return callback; } } else if (ret < 0) { return NULL; } } return NULL; } /* find_callback */ /******************************************************************** * FUNCTION add_callback * * Add a callback record in a modhdr record * * INPUTS: * modhdr == agt_cb_modhdr_t struct to add to * callback == callback definition struct to add * * RETURNS: * status *********************************************************************/ static status_t add_callback (agt_cb_modhdr_t *modhdr, agt_cb_set_t *callback) { agt_cb_set_t *cb; int ret; for (cb = (agt_cb_set_t *) dlq_firstEntry(&modhdr->callbackQ); cb != NULL; cb = (agt_cb_set_t *)dlq_nextEntry(cb)) { ret = xml_strcmp(callback->defpath, cb->defpath); if (ret < 0) { dlq_insertAhead(callback, cb); return NO_ERR; } } dlq_enque(callback, &modhdr->callbackQ); return NO_ERR; } /* add_callback */ /******************************************************************** * FUNCTION new_modhdr * * Malloc and init a new module callback header * * INPUTS: * modname == module name * * RETURNS: * malloced and initialized module header struct *********************************************************************/ static agt_cb_modhdr_t * new_modhdr (const xmlChar *modname) { agt_cb_modhdr_t *modhdr; modhdr = m__getObj(agt_cb_modhdr_t); if (!modhdr) { return NULL; } memset(modhdr, 0x0, sizeof(agt_cb_modhdr_t)); dlq_createSQue(&modhdr->callbackQ); modhdr->modname = modname; modhdr->loadstatus = AGTCB_STAT_INIT_DONE; modhdr->status = NO_ERR; return modhdr; } /* new_modhdr */ /******************************************************************** * FUNCTION find_modhdr * * Find a module header record in the global Q * * INPUTS: * modname == module name to find * * RETURNS: * pointer to found modhdr record or NULL if not found *********************************************************************/ static agt_cb_modhdr_t * find_modhdr (const xmlChar *modname) { agt_cb_modhdr_t *modhdr; int ret; for (modhdr = (agt_cb_modhdr_t *)dlq_firstEntry(&modhdrQ); modhdr != NULL; modhdr = (agt_cb_modhdr_t *)dlq_nextEntry(modhdr)) { ret = xml_strcmp(modname, modhdr->modname); if (ret == 0) { return modhdr; } else if (ret < 0) { return NULL; } } return NULL; } /* find_modhdr */ /******************************************************************** * FUNCTION add_modhdr * * Add a module header record to the global Q * * INPUTS: * modhdr == agt_cb_modhdr_t struct to add * * RETURNS: * status *********************************************************************/ static status_t add_modhdr (agt_cb_modhdr_t *modhdr) { agt_cb_modhdr_t *mh; int ret; for (mh = (agt_cb_modhdr_t *)dlq_firstEntry(&modhdrQ); mh != NULL; mh = (agt_cb_modhdr_t *)dlq_nextEntry(mh)) { ret = xml_strcmp(modhdr->modname, mh->modname); if (ret == 0) { return ERR_NCX_ENTRY_EXISTS; } else if (ret < 0) { dlq_insertAhead(modhdr, mh); return NO_ERR; } } dlq_enque(modhdr, &modhdrQ); return NO_ERR; } /* add_modhdr */ /******************************************************************** * FUNCTION load_callbacks * * Register an object specific callback function * * INPUTS: * mod == module that defines the target object for * these callback functions * modhdr == agt_cb module header for this module * callback == agt_cb callback structure to load * * RETURNS: * status *********************************************************************/ static status_t load_callbacks (ncx_module_t *mod, agt_cb_modhdr_t *modhdr, agt_cb_set_t *callback) { obj_template_t *obj; status_t res; int ret; /* else module found */ modhdr->loadstatus = AGTCB_STAT_LOADED; modhdr->modversion = mod->version; /* check if the version loaded is acceptable for this callback */ if (callback->version) { ret = yang_compare_revision_dates(modhdr->modversion, callback->version); if (ret != 0) { res = ERR_NCX_WRONG_VERSION; callback->loadstatus = AGTCB_STAT_LOAD_FAILED; callback->status = res; log_error("\nError: load callbacks failed for module '%s'" "\n wrong version: got '%s', need '%s'", mod->name, mod->version, callback->version); return res; } } /* find the object template for this callback */ res = xpath_find_schema_target_int(callback->defpath, &obj); if (res == NO_ERR) { agt_cb_fnset_node_t* cbset_node; /* set the callbacks in the object */ cbset_node = m__getObj(agt_cb_fnset_node_t); if(!cbset_node) { return ERR_INTERNAL_MEM; } cbset_node->fnset_ptr = &callback->cbset; dlq_enque(cbset_node, &obj->cbsetQ); callback->loadstatus = AGTCB_STAT_LOADED; callback->status = NO_ERR; log_debug2("\nagt_cb: load OK for mod '%s', def '%s'", mod->name, callback->defpath); } else { callback->loadstatus = AGTCB_STAT_LOAD_FAILED; callback->status = res; log_error("\nError: load callbacks failed for module '%s'" "\n '%s' for defpath '%s'", mod->name, get_error_string(res), callback->defpath); } return res; } /* load_callbacks */ /******************************************************************** * FUNCTION check_module_pending * * Check if module callbacks have been registered for * a module that was just added. Set any callbacks as * needed in the object tree * * INPUTS: * mod == newly added module * *********************************************************************/ static void check_module_pending (ncx_module_t *mod) { agt_cb_modhdr_t *modhdr; agt_cb_set_t *callback; log_debug2("\nagt_cb: got new module '%s', rev '%s'", mod->name, mod->version); modhdr = find_modhdr(mod->name); if (!modhdr) { return; } for (callback = (agt_cb_set_t *) dlq_firstEntry(&modhdr->callbackQ); callback != NULL; callback = (agt_cb_set_t *) dlq_nextEntry(callback)) { (void)load_callbacks(mod, modhdr, callback); } } /* check_module_pending */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION agt_cb_init * * Init the agent callback module * *********************************************************************/ void agt_cb_init (void) { if (!agt_cb_init_done) { dlq_createSQue(&modhdrQ); agt_cb_init_done = TRUE; } ncx_set_load_callback(check_module_pending); } /* agt_cb_init */ /******************************************************************** * FUNCTION agt_cb_cleanup * * CLeanup the agent callback module * *********************************************************************/ void agt_cb_cleanup (void) { agt_cb_modhdr_t *modhdr; if (agt_cb_init_done) { while (!dlq_empty(&modhdrQ)) { modhdr = (agt_cb_modhdr_t *)dlq_deque(&modhdrQ); free_modhdr(modhdr); } agt_cb_init_done = FALSE; } } /* agt_cb_cleanup */ /******************************************************************** * FUNCTION agt_cb_register_callback * * Register an object specific callback function * use the same fn for all callback phases * all phases will be invoked * * INPUTS: * modname == module that defines the target object for * these callback functions * defpath == Xpath with default (or no) prefixes * defining the object that will get the callbacks * version == exact module revision date expected * if condition not met then an error will * be logged (TBD: force unload of module!) * == NULL means use any version of the module * cbfn == address of callback function to use for * all callback phases * * RETURNS: * status *********************************************************************/ status_t agt_cb_register_callback (const xmlChar *modname, const xmlChar *defpath, const xmlChar *version, const agt_cb_fn_t cbfn) { agt_cb_modhdr_t *modhdr; agt_cb_set_t *callback; ncx_module_t *mod; status_t res; agt_cbtyp_t cbtyp; agt_cb_fnset_t cbset; #ifdef DEBUG if (!modname || !defpath || !cbfn) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif modhdr = find_modhdr(modname); if (!modhdr) { modhdr = new_modhdr(modname); if (!modhdr) { return ERR_INTERNAL_MEM; } res = add_modhdr(modhdr); if (res != NO_ERR) { free_modhdr(modhdr); return res; } } memset(&cbset, 0x0, sizeof(agt_cb_fnset_t)); for (cbtyp = AGT_CB_VALIDATE; cbtyp <= AGT_CB_ROLLBACK; cbtyp++) { cbset.cbfn[cbtyp] = cbfn; } callback = new_callback(modhdr, defpath, version, &cbset); if (!callback) { return ERR_INTERNAL_MEM; } res = add_callback(modhdr, callback); if (res != NO_ERR) { return res; } /* data structures in place, now check if the module * is loaded yet */ mod = ncx_find_module(modname, version); if (!mod) { /* module not present yet */ return NO_ERR; } res = load_callbacks(mod, modhdr, callback); return res; } /* agt_cb_register_callback */ /******************************************************************** * FUNCTION agt_cb_register_callbacks * * Register an object specific callback function * setup array of callbacks, could be different or NULL * to skip that phase * * INPUTS: * modname == module that defines the target object for * these callback functions * defpath == Xpath with default (or no) prefixes * defining the object that will get the callbacks * version == exact module revision date expected * if condition not met then an error will * be logged (TBD: force unload of module!) * == NULL means use any version of the module * cbfnset == address of callback function set to copy * * RETURNS: * status *********************************************************************/ status_t agt_cb_register_callbacks (const xmlChar *modname, const xmlChar *defpath, const xmlChar *version, const agt_cb_fnset_t *cbfnset) { agt_cb_modhdr_t *modhdr; agt_cb_set_t *callback; ncx_module_t *mod; status_t res; #ifdef DEBUG if (!modname || !defpath || !cbfnset) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif modhdr = find_modhdr(modname); if (!modhdr) { modhdr = new_modhdr(modname); if (!modhdr) { return ERR_INTERNAL_MEM; } res = add_modhdr(modhdr); if (res != NO_ERR) { free_modhdr(modhdr); return res; } } callback = new_callback(modhdr, defpath, version, cbfnset); if (!callback) { return ERR_INTERNAL_MEM; } res = add_callback(modhdr, callback); if (res != NO_ERR) { return res; } /* data structures in place, now check if the module * is loaded yet */ mod = ncx_find_module(modname, version); if (!mod) { /* module not present yet */ return NO_ERR; } res = load_callbacks(mod, modhdr, callback); return res; } /* agt_cb_register_callbacks */ /******************************************************************** * FUNCTION agt_cb_unregister_callback * * Unregister all callback functions for a specific object with mathching * callback function pointers. * * INPUTS: * modname == module containing the object for this callback * defpath == definition XPath location * * RETURNS: * none *********************************************************************/ void agt_cb_unregister_callback(const xmlChar *modname, const xmlChar *defpath, const agt_cb_fn_t cbfn) { agt_cb_modhdr_t *modhdr; agt_cb_set_t *callback; obj_template_t *obj; status_t res; #ifdef DEBUG if (!modname || !defpath) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif modhdr = find_modhdr(modname); if (!modhdr) { /* entry not found; assume it is an early exit cleanup */ return; } callback = find_callback(modhdr, defpath, cbfn); if (!callback) { SET_ERROR(ERR_INTERNAL_VAL); return; } res = xpath_find_schema_target_int(defpath, &obj); if (res == NO_ERR) { agt_cb_fnset_node_t* cbset_node; for (cbset_node = (agt_cb_fnset_node_t*)dlq_firstEntry(&obj->cbsetQ); cbset_node!=NULL; cbset_node = (agt_cb_fnset_node_t*)dlq_nextEntry(cbset_node)) { if(cbset_node->fnset_ptr == &callback->cbset) { dlq_remove(cbset_node); free(cbset_node); break; } } } dlq_remove(callback); free_callback(callback); if (dlq_empty(&modhdr->callbackQ)) { dlq_remove(modhdr); free_modhdr(modhdr); } } /* agt_cb_unregister_callback */ /******************************************************************** * FUNCTION agt_cb_unregister_callbacks * * Unregister all callback functions for a specific object * * INPUTS: * modname == module containing the object for this callback * defpath == definition XPath location * * RETURNS: * none *********************************************************************/ void agt_cb_unregister_callbacks (const xmlChar *modname, const xmlChar *defpath) { agt_cb_unregister_callback(modname,defpath,NULL); } /* agt_cb_unregister_callbacks */ /* END file agt_cb.c */ yuma123_2.14/netconf/src/agt/agt_val_parse.c0000664000175000017500000026210414770023131021101 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2013 - 2018, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_val_parse.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11feb06 abb begun 06aug08 abb YANG redesign ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_ses.h" #include "agt_top.h" #include "agt_util.h" #include "agt_val_parse.h" #include "agt_xml.h" #include "b64.h" #include "cfg.h" #include "def_reg.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "ncx_num.h" #include "ncx_str.h" #include "ncx_list.h" #include "ncxconst.h" #include "ncxtypes.h" #include "obj.h" #include "status.h" #include "tk.h" #include "typ.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xpath.h" #include "xpath_yang.h" #include "xpath1.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* forward declaration for recursive calls */ static status_t parse_btype_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval); /******************************************************************** * FUNCTION parse_error_subtree * * Generate an error during parmset processing for an element * Add rpc_err_rec_t structs to the msg->errQ * * INPUTS: * scb == session control block (NULL means call is a no-op) * msg = xml_msg_hdr_t from msg in progress (NULL means call is a no-op) * startnode == parent start node to match on exit * If this is NULL then the reader will not be advanced * errnode == error node being processed * If this is NULL then the current node will be * used if it can be retrieved with no errors, * and the node has naming properties. * errcode == error status_t of initial internal error * This will be used to pick the error-tag and * default error message * errnodetyp == internal VAL_PCH node type used for error_parm * error_parm == pointer to attribute name or namespace name, etc. * used in the specific error in agt_rpcerr.c * intnodetyp == internal VAL_PCH node type used for intnode * intnode == internal VAL_PCH node used for error_path * RETURNS: * status of the operation; only fatal errors will be returned *********************************************************************/ static status_t parse_error_subtree (ses_cb_t *scb, xml_msg_hdr_t *msg, const xml_node_t *startnode, const xml_node_t *errnode, status_t errcode, ncx_node_t errnodetyp, const void *error_parm, ncx_node_t intnodetyp, void *intnode) { status_t res; res = NO_ERR; agt_record_error(scb, msg, NCX_LAYER_OPERATION, errcode, errnode, errnodetyp, error_parm, intnodetyp, intnode); if (scb && startnode) { res = agt_xml_skip_subtree(scb, startnode); } return res; } /* parse_error_subtree */ /******************************************************************** * FUNCTION parse_error_subtree_errinfo * * Generate an error during parmset processing for an element * Add rpc_err_rec_t structs to the msg->errQ * * INPUTS: * scb == session control block (NULL means call is a no-op) * msg = xml_msg_hdr_t from msg in progress (NULL means call is a no-op) * startnode == parent start node to match on exit * If this is NULL then the reader will not be advanced * errnode == error node being processed * If this is NULL then the current node will be * used if it can be retrieved with no errors, * and the node has naming properties. * errcode == error status_t of initial internal error * This will be used to pick the error-tag and * default error message * errnodetyp == internal VAL_PCH node type used for error_parm * error_parm == pointer to attribute name or namespace name, etc. * used in the specific error in agt_rpcerr.c * intnodetyp == internal VAL_PCH node type used for intnode * intnode == internal VAL_PCH node used for error_path * errinfo == errinfo struct to use for details * * RETURNS: * status of the operation; only fatal errors will be returned *********************************************************************/ static status_t parse_error_subtree_errinfo (ses_cb_t *scb, xml_msg_hdr_t *msg, const xml_node_t *startnode, const xml_node_t *errnode, status_t errcode, ncx_node_t errnodetyp, const void *error_parm, ncx_node_t intnodetyp, void *intnode, ncx_errinfo_t *errinfo) { status_t res; res = NO_ERR; agt_record_error_errinfo(scb, msg, NCX_LAYER_OPERATION, errcode, errnode, errnodetyp, error_parm, intnodetyp, intnode, errinfo); if (scb && startnode) { res = agt_xml_skip_subtree(scb, startnode); } return res; } /* parse_error_subtree_errinfo */ /******************************************************************** * FUNCTION get_xml_node * * Get the next (or maybe current) XML node in the reader stream * This hack needed because xmlTextReader cannot look ahead or * back up during processing. * * The YANG leaf-list is really a collection of sibling nodes * and there is no way to tell where it ends without reading * past the end of it. * * This hack relies on the fact that a top-levelleaf-list could * never show up in a real NETCONF PDU * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress (NULL means don't record errors) * xmlnode == xml_node_t to fill in * usens == TRUE if regular NS checking mode * == FALSE if _nons version should be used * * OUTPUTS: * *xmlnode filled in * * RETURNS: * status *********************************************************************/ static status_t get_xml_node (ses_cb_t *scb, xml_msg_hdr_t *msg, xml_node_t *xmlnode, boolean usens) { status_t res; /* get a new node */ if (usens) { res = agt_xml_consume_node(scb, xmlnode, NCX_LAYER_OPERATION, msg); } else { res = agt_xml_consume_node_nons(scb, xmlnode, NCX_LAYER_OPERATION, msg); } return res; } /* get_xml_node */ /******************************************************************** * FUNCTION get_editop * * Check the node for operation="foo" attribute * and convert its value to an op_editop_t enum * * INPUTS: * node == xml_node_t to check * RETURNS: * editop == will be OP_EDITOP_NONE if explicitly set, * not-present, or error *********************************************************************/ static op_editop_t get_editop (const xml_node_t *node) { const xml_attr_t *attr; attr = xml_find_ro_attr(node, xmlns_nc_id(), NC_OPERATION_ATTR_NAME); if (!attr) { return OP_EDITOP_NONE; } return op_editop_id(attr->attr_val); } /* get_editop */ /******************************************************************** * FUNCTION pick_dataclass * * Pick the correct data class for this value node * * INPUTS: * parentdc == parent data class * obj == object template definition struct for the value node * * RETURNS: * data class for this value node *********************************************************************/ static ncx_data_class_t pick_dataclass (ncx_data_class_t parentdc, obj_template_t *obj) { boolean ret, setflag; setflag = FALSE; ret = obj_get_config_flag2(obj, &setflag); if (setflag) { return (ret) ? NCX_DC_CONFIG : NCX_DC_STATE; } else { return parentdc; } /*NOTREACHED*/ } /* pick_dataclass */ /******************************************************************** * FUNCTION parse_any_nc * * Parse the XML input as an 'any' type * * INPUTS: * see parse_btype_nc parameter list * RETURNS: * status *********************************************************************/ static status_t parse_any_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval) { const xml_node_t *errnode; status_t res; boolean done, getstrend, errdone; xml_node_t nextnode; /* init local vars */ errnode = startnode; res = NO_ERR; done = FALSE; getstrend = FALSE; errdone = FALSE; xml_init_node(&nextnode); retval->dataclass = parentdc; /* make sure the startnode is correct */ switch (startnode->nodetyp) { case XML_NT_START: /* do not set the object template yet, in case * there is a form of empty element * or another start (child) node */ break; case XML_NT_EMPTY: /* treat this 'any' is an 'empty' data type */ val_init_from_template(retval, ncx_get_gen_empty()); retval->v.boo = TRUE; retval->nsid = startnode->nsid; return NO_ERR; default: res = ERR_NCX_WRONG_NODETYP; /* add rpc-error to msg->errQ */ (void)parse_error_subtree(scb, msg, startnode, errnode, res, NCX_NT_NONE, NULL, NCX_NT_VAL, retval); xml_clean_node(&nextnode); return res; } /* at this point have either a simple type or a complex type * get the next node which could be any type */ res = get_xml_node(scb, msg, &nextnode, FALSE); if (res != NO_ERR) { xml_clean_node(&nextnode); return res; } log_debug3("\nparse_any: expecting any node type"); if (LOGDEBUG4) { xml_dump_node(&nextnode); } /* decide the base type from the child node type */ switch (nextnode.nodetyp) { case XML_NT_START: case XML_NT_EMPTY: /* A nested start or empty element means the parent is * treated as a 'container' data type */ val_init_from_template(retval, ncx_get_gen_container()); retval->nsid = startnode->nsid; break; case XML_NT_STRING: /* treat this string child node as the string * content for the parent node */ val_init_from_template(retval, ncx_get_gen_string()); retval->nsid = startnode->nsid; retval->v.str = xml_strdup(nextnode.simval); res = (retval->v.str) ? NO_ERR : ERR_INTERNAL_MEM; getstrend = TRUE; break; case XML_NT_END: res = xml_endnode_match(startnode, &nextnode); if (res == NO_ERR) { /* treat this start + end pair as an 'empty' data type */ val_init_from_template(retval, ncx_get_gen_empty()); retval->v.boo = TRUE; retval->nsid = startnode->nsid; xml_clean_node(&nextnode); return NO_ERR; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { errnode = &nextnode; /* add rpc-error to msg->errQ */ (void)parse_error_subtree(scb, msg, startnode, errnode, res, NCX_NT_NONE, NULL, NCX_NT_VAL, retval); xml_clean_node(&nextnode); return res; } /* check if processing a simple type as a string */ if (getstrend) { /* need to get the endnode for startnode then exit */ xml_clean_node(&nextnode); res = get_xml_node(scb, msg, &nextnode, FALSE); if (res == NO_ERR) { log_debug3("\nparse_any: expecting end node for %s", startnode->qname); if (LOGDEBUG4) { xml_dump_node(&nextnode); } res = xml_endnode_match(startnode, &nextnode); } else { errdone = TRUE; } if (res == NO_ERR) { xml_clean_node(&nextnode); return NO_ERR; } errnode = &nextnode; if (!errdone) { /* add rpc-error to msg->errQ */ (void)parse_error_subtree(scb, msg, startnode, errnode, res, NCX_NT_NONE, NULL, NCX_NT_VAL, retval); } xml_clean_node(&nextnode); return res; } /* if we get here, then the startnode is a container */ while (!done) { /* At this point have a nested start node * Allocate a new val_value_t for the child value node */ val_value_t *chval; chval = val_new_child_val(nextnode.nsid, nextnode.elname, TRUE, retval, get_editop(&nextnode), ncx_get_gen_anyxml()); if (!chval) { res = ERR_INTERNAL_MEM; /* add rpc-error to msg->errQ */ (void)parse_error_subtree(scb, msg, startnode, errnode, res, NCX_NT_NONE, NULL, NCX_NT_VAL, retval); xml_clean_node(&nextnode); return res; } val_add_child(chval, retval); /* recurse through and get whatever nodes are present * in the child node; call it an 'any' type * make sure this function gets called again * so the namespace errors can be ignored properly ;-) * * Cannot call this function directly or the * XML attributes will not get processed */ res = parse_btype_nc(scb, msg, ncx_get_gen_anyxml(), &nextnode, retval->dataclass, chval); chval->res = res; xml_clean_node(&nextnode); if (res != NO_ERR) { return res; } /* get the next node */ res = get_xml_node(scb, msg, &nextnode, FALSE); if (res == NO_ERR) { log_debug3("\nparse_any: expecting start, empty, or end node"); if (LOGDEBUG4) { xml_dump_node(&nextnode); } res = xml_endnode_match(startnode, &nextnode); if (res == NO_ERR) { done = TRUE; } } else { /* error already recorded */ done = TRUE; } } xml_clean_node(&nextnode); return res; } /* parse_any_nc */ /******************************************************************** * FUNCTION parse_enum_nc * * Parse the XML input as a 'enumeration' type * e.g.. * * fred * * These NCX variants are no longer supported: * 11 * fred(11) * * INPUTS: * see parse_btype_nc parameter list * RETURNS: * status *********************************************************************/ static status_t parse_enum_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval) { const xml_node_t *errnode; const xmlChar *badval; xml_node_t valnode, endnode; status_t res, res2, res3; boolean errdone, empty, enddone; /* init local vars */ xml_init_node(&valnode); xml_init_node(&endnode); errnode = startnode; errdone = FALSE; empty = FALSE; enddone = FALSE; badval = NULL; res = NO_ERR; res2 = NO_ERR; res3 = NO_ERR; val_init_from_template(retval, obj); retval->dataclass = pick_dataclass(parentdc, obj); if (retval->editop == OP_EDITOP_DELETE || retval->editop == OP_EDITOP_REMOVE) { switch (startnode->nodetyp) { case XML_NT_EMPTY: empty = TRUE; break; case XML_NT_START: break; default: res = ERR_NCX_WRONG_NODETYP; } } else { /* make sure the startnode is correct */ res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_START); } if (res == NO_ERR && !empty) { /* get the next node which should be a string node */ res = get_xml_node(scb, msg, &valnode, TRUE); if (res != NO_ERR) { errdone = TRUE; } } if (res == NO_ERR && !empty) { log_debug3("\nparse_enum: expecting string node"); if (LOGDEBUG4) { xml_dump_node(&valnode); } /* validate the node type and enum string or number content */ switch (valnode.nodetyp) { case XML_NT_START: res = ERR_NCX_WRONG_NODETYP_CPX; errnode = &valnode; break; case XML_NT_EMPTY: res = ERR_NCX_WRONG_NODETYP_CPX; errnode = &valnode; enddone = TRUE; break; case XML_NT_STRING: if (val_all_whitespace(valnode.simval) && (retval->editop == OP_EDITOP_DELETE || retval->editop == OP_EDITOP_REMOVE)) { res = NO_ERR; } else { /* get the non-whitespace string here */ res = val_enum_ok(obj_get_typdef(obj), valnode.simval, &retval->v.enu.val, &retval->v.enu.name); } if (res == NO_ERR) { /* record the value even if there are errors after this */ retval->btyp = NCX_BT_ENUM; } else { badval = valnode.simval; } break; case XML_NT_END: enddone = TRUE; if (retval->editop == OP_EDITOP_DELETE || retval->editop == OP_EDITOP_REMOVE) { retval->btyp = NCX_BT_ENUM; /* leave value empty, OK for leaf */ } else { res = ERR_NCX_INVALID_VALUE; errnode = &valnode; } break; default: res = SET_ERROR(ERR_NCX_WRONG_NODETYP); errnode = &valnode; enddone = TRUE; } /* get the matching end node for startnode */ if (!enddone) { res2 = get_xml_node(scb, msg, &endnode, TRUE); if (res2 == NO_ERR) { log_debug3("\nparse_enum: expecting end for %s", startnode->qname); if (LOGDEBUG4) { xml_dump_node(&endnode); } res2 = xml_endnode_match(startnode, &endnode); if (res2 != NO_ERR) { errnode = &endnode; } } else { errdone = TRUE; } } } /* check if this is a list index, add it now so * error-path info will be as complete as possible */ if (obj_is_key(retval->obj)) { res3 = val_gen_key_entry(retval); } if (res == NO_ERR) { res = res2; } if (res == NO_ERR) { res = res3; } /* check if any errors; record the first error */ if ((res != NO_ERR) && !errdone) { /* add rpc-error to msg->errQ */ (void)parse_error_subtree(scb, msg, startnode, errnode, res, NCX_NT_STRING, badval, NCX_NT_VAL, retval); } xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_enum_nc */ /******************************************************************** * FUNCTION parse_empty_nc * For NCX_BT_EMPTY * * Parse the XML input as an 'empty' or 'boolean' type * e.g.: * * * * * * * INPUTS: * see parse_btype_nc parameter list * RETURNS: * status *********************************************************************/ static status_t parse_empty_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval) { const xml_node_t *errnode; xml_node_t endnode; status_t res; boolean errdone; /* init local vars */ xml_init_node(&endnode); errnode = startnode; errdone = FALSE; val_init_from_template(retval, obj); retval->dataclass = pick_dataclass(parentdc, obj); /* validate the node type and enum string or number content */ switch (startnode->nodetyp) { case XML_NT_EMPTY: res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_NONE); break; case XML_NT_START: res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_NONE); if (res == NO_ERR) { res = get_xml_node(scb, msg, &endnode, TRUE); if (res != NO_ERR) { errdone = TRUE; } else { log_debug3("\nparse_empty: expecting end for %s", startnode->qname); if (LOGDEBUG4) { xml_dump_node(&endnode); } res = xml_endnode_match(startnode, &endnode); if (res != NO_ERR) { if (endnode.nodetyp != XML_NT_STRING || !xml_isspace_str(endnode.simval)) { errnode = &endnode; res = ERR_NCX_WRONG_NODETYP; } else { /* that was an empty string -- try again */ xml_clean_node(&endnode); res = get_xml_node(scb, msg, &endnode, TRUE); if (res == NO_ERR) { log_debug3("\nparse_empty: expecting " "end for %s", startnode->qname); if (LOGDEBUG4) { xml_dump_node(&endnode); } res = xml_endnode_match(startnode, &endnode); if (res != NO_ERR) { errnode = &endnode; } } else { errdone = TRUE; } } } } } break; default: res = ERR_NCX_WRONG_NODETYP; } /* record the value if no errors */ if (res == NO_ERR) { retval->v.boo = TRUE; } else if (!errdone) { /* add rpc-error to msg->errQ */ (void)parse_error_subtree(scb, msg, startnode, errnode, res, NCX_NT_NONE, NULL, NCX_NT_VAL, retval); } xml_clean_node(&endnode); return res; } /* parse_empty_nc */ /******************************************************************** * FUNCTION parse_boolean_nc * * Parse the XML input as a 'boolean' type * e.g.. * * true * false * 1 * 0 * * INPUTS: * see parse_btype_nc parameter list * RETURNS: * status *********************************************************************/ static status_t parse_boolean_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval) { const xml_node_t *errnode; const xmlChar *badval; xml_node_t valnode, endnode; status_t res, res2, res3; boolean errdone, empty; /* init local vars */ xml_init_node(&valnode); xml_init_node(&endnode); errnode = startnode; errdone = FALSE; badval = NULL; empty = FALSE; res = NO_ERR; res2 = NO_ERR; res3 = NO_ERR; val_init_from_template(retval, obj); retval->dataclass = pick_dataclass(parentdc, obj); /* make sure the startnode is correct */ if (retval->editop == OP_EDITOP_DELETE || retval->editop == OP_EDITOP_REMOVE) { switch (startnode->nodetyp) { case XML_NT_EMPTY: empty = TRUE; break; case XML_NT_START: break; default: res = ERR_NCX_WRONG_NODETYP; } } else { res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_START); } if (res == NO_ERR && !empty) { /* get the next node which should be a string node */ res = get_xml_node(scb, msg, &valnode, TRUE); if (res != NO_ERR) { errdone = TRUE; } } if (res == NO_ERR && !empty) { log_debug3("\nparse_boolean: expecting string node."); if (LOGDEBUG4) { xml_dump_node(&valnode); } /* validate the node type and enum string or number content */ switch (valnode.nodetyp) { case XML_NT_START: res = ERR_NCX_WRONG_NODETYP_CPX; errnode = &valnode; break; case XML_NT_STRING: /* get the non-whitespace string here */ if (ncx_is_true(valnode.simval)) { retval->v.boo = TRUE; } else if (ncx_is_false(valnode.simval)) { retval->v.boo = FALSE; } else { res = ERR_NCX_INVALID_VALUE; badval = valnode.simval; } break; default: res = ERR_NCX_WRONG_NODETYP; errnode = &valnode; } /* get the matching end node for startnode */ res2 = get_xml_node(scb, msg, &endnode, TRUE); if (res2 == NO_ERR) { log_debug3("\nparse_boolean: expecting end for %s", startnode->qname); if (LOGDEBUG4) { xml_dump_node(&endnode); } res2 = xml_endnode_match(startnode, &endnode); if (res2 != NO_ERR) { errnode = &endnode; } } else { errdone = TRUE; } } /* check if this is a list index, add it now so * error-path info will be as complete as possible */ if (obj_is_key(retval->obj)) { res3 = val_gen_key_entry(retval); } if (res == NO_ERR) { res = res2; } if (res == NO_ERR) { res = res3; } /* check if any errors; record the first error */ if ((res != NO_ERR) && !errdone) { /* add rpc-error to msg->errQ */ (void)parse_error_subtree(scb, msg, startnode, errnode, res, NCX_NT_STRING, badval, NCX_NT_VAL, retval); } xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_boolean_nc */ /******************************************************************** * FUNCTION parse_num_nc * * Parse the XML input as a numeric data type * * INPUTS: * scb == session control block * Input is read from scb->reader. * msg == incoming RPC message * Errors are appended to msg->errQ * obj == object template for this number type * btyp == base type of the expected ordinal number type * startnode == top node of the parameter to be parsed * Parser function will attempt to consume all the * nodes until the matching endnode is reached * parentdc == data class of the parent node * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * msg->errQ may be appended with new errors or warnings * * RETURNS: * status *********************************************************************/ static status_t parse_num_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, ncx_btype_t btyp, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval) { const xml_node_t *errnode; const xmlChar *badval; ncx_errinfo_t *errinfo; xml_node_t valnode, endnode; status_t res, res2, res3; boolean errdone, empty, endtagdone; ncx_numfmt_t numfmt; /* init local vars */ xml_init_node(&valnode); xml_init_node(&endnode); badval = NULL; errnode = startnode; errdone = FALSE; empty = FALSE; endtagdone = FALSE; errinfo = NULL; res = NO_ERR; res2 = NO_ERR; res3 = NO_ERR; val_init_from_template(retval, obj); retval->dataclass = pick_dataclass(parentdc, obj); /* make sure the startnode is correct */ if (retval->editop == OP_EDITOP_DELETE || retval->editop == OP_EDITOP_REMOVE) { switch (startnode->nodetyp) { case XML_NT_EMPTY: empty = TRUE; break; case XML_NT_START: break; default: res = ERR_NCX_WRONG_NODETYP; } } else { res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_START); } if (res == NO_ERR && !empty) { /* get the next node which should be a string node */ res = get_xml_node(scb, msg, &valnode, TRUE); if (res != NO_ERR) { errdone = TRUE; } } log_debug3("\nparse_num: expecting string node."); if (LOGDEBUG4) { xml_dump_node(&valnode); } if (res == NO_ERR && !empty) { /* validate the number content */ switch (valnode.nodetyp) { case XML_NT_START: res = ERR_NCX_WRONG_NODETYP_CPX; errnode = &valnode; break; case XML_NT_STRING: /* convert the non-whitespace string to a number * XML numbers are only allowed to be in decimal * or real format, not octal or hex */ numfmt = ncx_get_numfmt(valnode.simval); if (numfmt == NCX_NF_OCTAL) { numfmt = NCX_NF_DEC; } if (numfmt == NCX_NF_DEC || numfmt == NCX_NF_REAL) { if (btyp == NCX_BT_DECIMAL64) { res = ncx_convert_dec64(valnode.simval, numfmt, obj_get_fraction_digits(obj), &retval->v.num); } else { res = ncx_convert_num(valnode.simval, numfmt, btyp, &retval->v.num); } } else { res = ERR_NCX_WRONG_NUMTYP; } if (res == NO_ERR) { res = val_range_ok_errinfo(obj_get_typdef(obj), btyp, &retval->v.num, &errinfo); } if (res != NO_ERR) { badval = valnode.simval; } break; case XML_NT_END: /* got a start-end NULL string * treat as an invalid value */ res = ERR_NCX_INVALID_VALUE; badval = EMPTY_STRING; errnode = &valnode; endtagdone = TRUE; /* not trying to match the end node; * skip that error; the XML parser should have * already complained if the wrong end tag was present */ break; default: res = ERR_NCX_WRONG_NODETYP; errnode = &valnode; } /* get the matching end node for startnode */ if (!endtagdone) { res2 = get_xml_node(scb, msg, &endnode, TRUE); if (res2 == NO_ERR) { log_debug3("\nparse_num: expecting end for %s", startnode->qname); if (LOGDEBUG4) { xml_dump_node(&endnode); } res2 = xml_endnode_match(startnode, &endnode); if (res2 != NO_ERR) { errnode = &endnode; } } else { errdone = TRUE; } } } /* check if this is a list index, add it now so * error-path info will be as complete as possible */ if (obj_is_key(retval->obj)) { res3 = val_gen_key_entry(retval); } if (res == NO_ERR) { res = res2; } if (res == NO_ERR) { res = res3; } /* check if any errors; record the first error */ if ((res != NO_ERR) && !errdone) { /* add rpc-error to msg->errQ */ (void)parse_error_subtree_errinfo(scb, msg, startnode, errnode, res, NCX_NT_STRING, badval, NCX_NT_VAL, retval, errinfo); } xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_num_nc */ /******************************************************************** * FUNCTION parse_string_nc * * Parse the XML input as a string data type * * INPUTS: * scb == session control block * Input is read from scb->reader. * msg == incoming RPC message * Errors are appended to msg->errQ * obj == object template for this string type * btyp == base type of the expected ordinal number type * startnode == top node of the parameter to be parsed * Parser function will attempt to consume all the * nodes until the matching endnode is reached * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * msg->errQ may be appended with new errors or warnings * * RETURNS: * status *********************************************************************/ static status_t parse_string_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, ncx_btype_t btyp, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval) { const xml_node_t *errnode; const xmlChar *badval; typ_template_t *listtyp; ncx_errinfo_t *errinfo; obj_template_t *targobj; xpath_result_t *result; xml_node_t valnode, endnode; status_t res, res2, res3; boolean errdone, empty; ncx_btype_t listbtyp; /* init local vars */ xml_init_node(&valnode); xml_init_node(&endnode); errnode = startnode; badval = NULL; errdone = FALSE; empty = FALSE; errinfo = NULL; res2 = NO_ERR; res3 = NO_ERR; listbtyp = NCX_BT_NONE; listtyp = NULL; val_init_from_template(retval, obj); retval->dataclass = pick_dataclass(parentdc, obj); /* make sure the startnode is correct */ res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_START); if (res != NO_ERR) { res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_EMPTY); if (res == NO_ERR) { empty = TRUE; } } /* get the value string node */ if (res == NO_ERR && !empty) { /* get the next node which should be a string node */ res = get_xml_node(scb, msg, &valnode, TRUE); if (res != NO_ERR) { errdone = TRUE; } else if (valnode.nodetyp == XML_NT_END) { empty = TRUE; } } /* check empty string corner case */ if (empty) { if (retval->editop == OP_EDITOP_DELETE || retval->editop == OP_EDITOP_REMOVE) { if (obj->objtype == OBJ_TYP_LEAF) { res = NO_ERR; } else { res = ERR_NCX_MISSING_VAL_INST; } } else if (btyp==NCX_BT_INSTANCE_ID) { res = ERR_NCX_INVALID_VALUE; } else if (btyp==NCX_BT_SLIST || btyp==NCX_BT_BITS) { /* no need to check the empty list */ ; } else { /* check the empty string */ res = val_string_ok_errinfo(obj_get_typdef(obj), btyp, EMPTY_STRING, &errinfo); retval->v.str = xml_strdup(EMPTY_STRING); if (!retval->v.str) { res = ERR_INTERNAL_MEM; } } } if (res != NO_ERR) { if (!errdone) { /* add rpc-error to msg->errQ */ (void)parse_error_subtree_errinfo(scb, msg, startnode, errnode, res, NCX_NT_NONE, NULL, NCX_NT_VAL, retval, errinfo); } xml_clean_node(&valnode); return res; } if (empty) { res2 = NO_ERR; xml_clean_node(&valnode); if (retval->v.str == NULL) { retval->v.str = xml_strdup(EMPTY_STRING); if (retval->v.str == NULL) { res2 = ERR_INTERNAL_MEM; } } if (res2 == NO_ERR && obj_is_key(retval->obj)) { res2 = val_gen_key_entry(retval); } return res2; } log_debug3("\nparse_string: expecting string node."); if (LOGDEBUG4) { xml_dump_node(&valnode); } /* validate the string data type content */ switch (valnode.nodetyp) { case XML_NT_START: res = ERR_NCX_WRONG_NODETYP_CPX; errnode = &valnode; break; case XML_NT_STRING: switch (btyp) { case NCX_BT_SLIST: case NCX_BT_BITS: if (btyp==NCX_BT_SLIST) { /* get the list of strings, then check them */ listtyp = typ_get_listtyp(obj_get_typdef(obj)); listbtyp = typ_get_basetype(&listtyp->typdef); } else if (btyp==NCX_BT_BITS) { listbtyp = NCX_BT_BITS; } res = ncx_set_list(listbtyp, valnode.simval, &retval->v.list); if (res == NO_ERR) { if (btyp == NCX_BT_SLIST) { res = ncx_finish_list(&listtyp->typdef, &retval->v.list); } else { res = ncx_finish_list(obj_get_typdef(obj), &retval->v.list); } } if (res == NO_ERR) { res = val_list_ok_errinfo(obj_get_typdef(obj), btyp, &retval->v.list, &errinfo); } break; default: if (!obj_is_xpath_string(obj)) { /* check the non-whitespace string */ res = val_string_ok_errinfo(obj_get_typdef(obj), btyp, valnode.simval, &errinfo); break; } /* else fall through and parse XPath string */ case NCX_BT_INSTANCE_ID: res = NO_ERR; retval->xpathpcb = agt_new_xpath_pcb(scb, valnode.simval, &res); if (!retval->xpathpcb) { ; /* res already set */ } else if (btyp == NCX_BT_INSTANCE_ID || obj_is_schema_instance_string(obj)) { /* do a first pass parsing to resolve all * the prefixes and check well-formed XPath */ res = xpath_yang_validate_xmlpath(scb->reader, retval->xpathpcb, obj, FALSE, &targobj); } else { result = xpath1_eval_xmlexpr(scb->reader, retval->xpathpcb, NULL, NULL, FALSE, FALSE, &res); // not interested in the actual result, just the status in res! xpath_free_result(result); } } if (res != NO_ERR) { badval = valnode.simval; } /* record the value even if there are errors */ switch (btyp) { case NCX_BT_BINARY: if (valnode.simval) { /* result is going to be less than the encoded length */ retval->v.binary.ustr = m__getMem(valnode.simlen); retval->v.binary.ubufflen = valnode.simlen; if (!retval->v.binary.ustr) { res = ERR_INTERNAL_MEM; } else { res = b64_decode(valnode.simval, valnode.simlen, retval->v.binary.ustr, retval->v.binary.ubufflen, &retval->v.binary.ustrlen); } } break; case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: /* make copy for now, optimize later */ case NCX_BT_LEAFREF: if (valnode.simval) { retval->v.str = xml_strdup(valnode.simval); if (!retval->v.str) { res = ERR_INTERNAL_MEM; } } break; case NCX_BT_SLIST: case NCX_BT_BITS: break; /* value already set */ default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; default: res = ERR_NCX_WRONG_NODETYP; errnode = &valnode; } /* get the matching end node for startnode */ res2 = get_xml_node(scb, msg, &endnode, TRUE); if (res2 == NO_ERR) { log_debug3("\nparse_string: expecting end for %s", startnode->qname); if (LOGDEBUG4) { xml_dump_node(&endnode); } res2 = xml_endnode_match(startnode, &endnode); } else { errdone = TRUE; } /* check if this is a list index, add it now so * error-path info will be as complete as possible */ if (obj_is_key(retval->obj)) { res3 = val_gen_key_entry(retval); } if (res == NO_ERR) { res = res2; } if (res == NO_ERR) { res = res3; } /* check if any errors; record the first error */ if ((res != NO_ERR) && !errdone) { /* add rpc-error to msg->errQ */ (void)parse_error_subtree_errinfo(scb, msg, startnode, errnode, res, NCX_NT_STRING, badval, NCX_NT_VAL, retval, errinfo); } xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_string_nc */ /******************************************************************** * FUNCTION parse_idref_nc * * Parse the XML input as an 'identityref' type * e.g.. * * acme:card-type3 * * INPUTS: * see parse_btype_nc parameter list * RETURNS: * status *********************************************************************/ static status_t parse_idref_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval) { const xml_node_t *errnode; const xmlChar *badval, *str; xml_node_t valnode, endnode; status_t res, res2, res3; boolean errdone, empty, enddone; /* init local vars */ xml_init_node(&valnode); xml_init_node(&endnode); errnode = startnode; errdone = FALSE; empty = FALSE; enddone = FALSE; badval = NULL; res = NO_ERR; res2 = NO_ERR; res3 = NO_ERR; val_init_from_template(retval, obj); retval->dataclass = pick_dataclass(parentdc, obj); if (retval->editop == OP_EDITOP_DELETE || retval->editop == OP_EDITOP_REMOVE) { switch (startnode->nodetyp) { case XML_NT_EMPTY: empty = TRUE; break; case XML_NT_START: break; default: res = ERR_NCX_WRONG_NODETYP; errnode = startnode; } } else { /* make sure the startnode is correct */ res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_START); } if (res == NO_ERR && !empty) { /* get the next node which should be a string node */ res = get_xml_node(scb, msg, &valnode, TRUE); if (res != NO_ERR) { errdone = TRUE; } } if (res == NO_ERR && !empty) { log_debug3("\nparse_idref: expecting string node"); if (LOGDEBUG4) { xml_dump_node(&valnode); } /* validate the node type and enum string or number content */ switch (valnode.nodetyp) { case XML_NT_START: res = ERR_NCX_WRONG_NODETYP_CPX; errnode = &valnode; break; case XML_NT_EMPTY: res = ERR_NCX_WRONG_NODETYP_CPX; errnode = &valnode; enddone = TRUE; break; case XML_NT_STRING: if (val_all_whitespace(valnode.simval) && (retval->editop == OP_EDITOP_DELETE || retval->editop == OP_EDITOP_REMOVE)) { res = NO_ERR; } else { retval->v.idref.nsid = valnode.contentnsid; /* get the name field and verify identity is valid * for the identity base in the typdef */ str = NULL; res = val_idref_ok(obj_get_typdef(obj), valnode.simval, retval->v.idref.nsid, &str, &retval->v.idref.identity); if (str) { retval->v.idref.name = xml_strdup(str); if (!retval->v.idref.name) { res = ERR_INTERNAL_MEM; } } else { /* save the name for error purposes or else * a NULL string will get printed */ retval->v.idref.name = xml_strdup(valnode.simval); if (!retval->v.idref.name) { res = ERR_INTERNAL_MEM; } } } if (res == NO_ERR) { /* record the value even if there are errors after this */ retval->btyp = NCX_BT_IDREF; } else { badval = valnode.simval; } break; case XML_NT_END: enddone = TRUE; if (retval->editop == OP_EDITOP_DELETE || retval->editop == OP_EDITOP_REMOVE) { retval->btyp = NCX_BT_IDREF; } else { res = ERR_NCX_INVALID_VALUE; errnode = &valnode; } break; default: res = SET_ERROR(ERR_NCX_WRONG_NODETYP); errnode = &valnode; enddone = TRUE; } /* get the matching end node for startnode */ if (!enddone) { res2 = get_xml_node(scb, msg, &endnode, TRUE); if (res2 == NO_ERR) { log_debug3("\nparse_idref: expecting end for %s", startnode->qname); if (LOGDEBUG4) { xml_dump_node(&endnode); } res2 = xml_endnode_match(startnode, &endnode); if (res2 != NO_ERR) { errnode = &endnode; } } else { errdone = TRUE; } } } /* check if this is a list index, add it now so * error-path info will be as complete as possible */ if (obj_is_key(retval->obj)) { res3 = val_gen_key_entry(retval); } if (res == NO_ERR) { res = res2; } if (res == NO_ERR) { res = res3; } /* check if any errors; record the first error */ if ((res != NO_ERR) && !errdone) { /* add rpc-error to msg->errQ */ (void)parse_error_subtree(scb, msg, startnode, errnode, res, NCX_NT_STRING, badval, NCX_NT_VAL, retval); } xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_idref_nc */ /******************************************************************** * FUNCTION parse_union_nc * * Parse the XML input as a 'union' type * e.g.. * * fred * 11 * * * * INPUTS: * see parse_btype_nc parameter list * OUTPUTS: * *retval will be filled in * RETURNS: * status *********************************************************************/ static status_t parse_union_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval) { const xml_node_t *errnode; const xmlChar *badval; ncx_errinfo_t *errinfo; xml_node_t valnode, endnode; status_t res, res2, res3; boolean errdone, stopnow, empty; /* init local vars */ xml_init_node(&valnode); xml_init_node(&endnode); errnode = startnode; errdone = FALSE; empty = FALSE; badval = NULL; res = NO_ERR; res2 = NO_ERR; res3 = NO_ERR; errinfo = NULL; stopnow = FALSE; val_init_from_template(retval, obj); retval->dataclass = pick_dataclass(parentdc, obj); /* make sure the startnode is correct */ if (retval->editop == OP_EDITOP_DELETE || retval->editop == OP_EDITOP_REMOVE) { switch (startnode->nodetyp) { case XML_NT_EMPTY: empty = TRUE; break; case XML_NT_START: break; default: res = ERR_NCX_WRONG_NODETYP; } } else { res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_START); if (res != NO_ERR) { res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_EMPTY); if (res == NO_ERR) { empty = TRUE; } } } /* get the value string node */ if (res == NO_ERR && !empty) { /* get the next node which should be a string node */ res = get_xml_node(scb, msg, &valnode, TRUE); if (res == NO_ERR && valnode.nodetyp == XML_NT_END) { res = xml_endnode_match(startnode, &valnode); if(res==NO_ERR) { empty = TRUE; xml_clean_node(&valnode); } } else if (res != NO_ERR) { errdone = TRUE; } } if (empty && res==NO_ERR) { /* check the empty string */ res = val_union_ok_errinfo(obj_get_typdef(obj), EMPTY_STRING, retval, &errinfo); if(res==NO_ERR) { retval->v.str = xml_strdup(EMPTY_STRING); if (!retval->v.str) { res = ERR_INTERNAL_MEM; } } else { badval = EMPTY_STRING; } } if (res == NO_ERR && !empty) { log_debug3("\nparse_union: expecting string or number node."); if (LOGDEBUG4) { xml_dump_node(&valnode); } /* validate the node type and union node content */ switch (valnode.nodetyp) { case XML_NT_STRING: /* get the non-whitespace string here */ res = val_union_ok_errinfo(obj_get_typdef(obj), valnode.simval, retval, &errinfo); if (res != NO_ERR) { badval = valnode.simval; } break; default: stopnow = TRUE; SET_ERROR(ERR_INTERNAL_VAL); res = ERR_NCX_WRONG_NODETYP; errnode = &valnode; } if (stopnow) { res2 = val_set_simval(retval, retval->typdef, retval->nsid, retval->name, EMPTY_STRING); } else { res = val_set_simval(retval, retval->typdef, retval->nsid, retval->name, valnode.simval); if (res != NO_ERR) { errnode = &valnode; } /* get the matching end node for startnode */ res2 = get_xml_node(scb, msg, &endnode, TRUE); if (res2 == NO_ERR) { log_debug3("\nparse_union: expecting end for %s", startnode->qname); if (LOGDEBUG4) { xml_dump_node(&endnode); } res2 = xml_endnode_match(startnode, &endnode); if (res2 != NO_ERR) { errnode = &endnode; } } else { errdone = TRUE; } } } /* check if this is a list index, add it now so * error-path info will be as complete as possible */ if (obj_is_key(retval->obj)) { res3 = val_gen_key_entry(retval); } if (res == NO_ERR) { res = res2; } if (res == NO_ERR) { res = res3; } /* check if any errors; record the first error */ if ((res != NO_ERR) && !errdone) { /* add rpc-error to msg->errQ */ (void)parse_error_subtree_errinfo(scb, msg, startnode, errnode, res, NCX_NT_STRING, badval, NCX_NT_VAL, retval, errinfo); } xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_union_nc */ /******************************************************************** * FUNCTION parse_complex_nc * * Parse the XML input as a complex type * * Handles the following base types: * NCX_BT_CONTAINER * NCX_BT_LIST * May also work for * NCX_BT_CHOICE * NCX_BT_CASE * * E.g., container: * * * blah * 7 * * * * In an instance document, containers and lists look * the same. The validation is different of course, but the * parsing is basically the same. * * INPUTS: * see parse_btype_nc parameter list * errfromchild == address of return flag for origin of error * * OUTPUTS: * *errfromchild == TRUE if the return code is from a child node * * RETURNS: * status *********************************************************************/ static status_t parse_complex_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval, boolean *errfromchild) { const xml_node_t *errnode; obj_template_t *chobj, *topchild, *curchild; const agt_profile_t *profile; xml_node_t chnode; status_t res, res2, retres; boolean done, errdone, empty, xmlorder; ncx_btype_t chbtyp; /* setup local vars */ *errfromchild = FALSE; errnode = startnode; chobj = NULL; topchild = NULL; curchild = NULL; res = NO_ERR; res2 = NO_ERR; retres = NO_ERR; done = FALSE; errdone = FALSE; empty = FALSE; chbtyp = NCX_BT_NONE; profile = agt_get_profile(); if (!profile) { return SET_ERROR(ERR_INTERNAL_PTR); } xmlorder = profile->agt_xmlorder; val_init_from_template(retval, obj); retval->dataclass = pick_dataclass(parentdc, obj); /* do not really need to validate the start node type * since it is probably a virtual container at the * very start, and after that, a node match must have * occurred to call this function recursively */ switch (startnode->nodetyp) { case XML_NT_START: break; case XML_NT_EMPTY: empty = TRUE; break; case XML_NT_STRING: case XML_NT_END: res = ERR_NCX_WRONG_NODETYP_SIM; break; default: res = ERR_NCX_WRONG_NODETYP; } if (res != NO_ERR) { /* add rpc-error to msg->errQ */ (void)parse_error_subtree(scb, msg, startnode, errnode, res, NCX_NT_NONE, NULL, NCX_NT_VAL, retval); return res; } /* start setting up the return value */ retval->editop = get_editop(startnode); if (empty) { return NO_ERR; } /* setup the first child in the complex object * Allowed be NULL in some cases so do not check * the result yet */ chobj = obj_first_child(obj); xml_init_node(&chnode); /* go through each child node until the parent end node */ while (!done) { /* init per-loop vars */ val_value_t *chval; res2 = NO_ERR; errdone = FALSE; empty = FALSE; /* get the next node which should be a child or end node */ res = get_xml_node(scb, msg, &chnode, TRUE); if (res != NO_ERR) { errdone = TRUE; if (res != ERR_NCX_UNKNOWN_NS) { done = TRUE; } } else { log_debug3("\nparse_complex: expecting start-child " "or end node."); if (LOGDEBUG4) { xml_dump_node(&chnode); } /* validate the child member node type */ switch (chnode.nodetyp) { case XML_NT_START: case XML_NT_EMPTY: /* any namespace OK for now, check it in get_child_node */ break; case XML_NT_STRING: res = ERR_NCX_WRONG_NODETYP_SIM; errnode = startnode; break; case XML_NT_END: res = xml_endnode_match(startnode, &chnode); if (res != NO_ERR) { errnode = &chnode; } else { /* no error exit */ done = TRUE; continue; } break; default: res = ERR_NCX_WRONG_NODETYP; errnode = &chnode; } } /* if we get here, there is a START or EMPTY node * that could be a valid child node * * if xmlorder enforced then check if the * node is the correct child node * * if no xmlorder, then check for any valid child */ if (res==NO_ERR) { res = obj_get_child_node(obj, chobj, &chnode, xmlorder, NULL, &topchild, &curchild); if (res != NO_ERR) { /* have no expected child element to match to * this XML node * if xmlorder == TRUE, then this could indicate * an extra instance of the last child node */ res = ERR_NCX_UNKNOWN_OBJECT; errnode = &chnode; } } /* try to setup a new child node */ if (res == NO_ERR) { /* save the child base type */ chbtyp = obj_get_basetype(curchild); /* at this point, the 'curchild' template matches the * 'chnode' namespace and name; * Allocate a new val_value_t for the child value node */ chval = val_new_child_val(obj_get_nsid(curchild), obj_get_name(curchild), FALSE, retval, get_editop(&chnode), curchild); if (!chval) { res = ERR_INTERNAL_MEM; } } /* check any errors in setting up the child node */ if (res != NO_ERR) { /* try to skip just the child node sub-tree */ if (res != ERR_XML_READER_EOF) { if (errdone) { if (scb) { res2 = agt_xml_skip_subtree(scb, &chnode); } } else { /* add rpc-error to msg->errQ */ res2 = parse_error_subtree(scb, msg, &chnode, errnode, res, NCX_NT_NONE, NULL, NCX_NT_VAL, retval); } } xml_clean_node(&chnode); if (res2 != NO_ERR) { /* skip child didn't work, now skip the entire value subtree */ (void)agt_xml_skip_subtree(scb, startnode); return res; } else { /* skip child worked, go on to next child, parse for errors */ retres = res; if (NEED_EXIT(res) || res==ERR_XML_READER_EOF) { done = TRUE; } continue; } } /* recurse through and get whatever nodes are present * in the child node */ val_add_child(chval, retval); res = parse_btype_nc(scb, msg, curchild, &chnode, retval->dataclass, chval); // chval->res = res; already done if (res != NO_ERR) { retres = res; *errfromchild = TRUE; if (NEED_EXIT(res) || res==ERR_XML_READER_EOF) { done = TRUE; continue; } } /* setup the next child unless the current child * is a list */ if (chbtyp != NCX_BT_LIST) { /* setup next child if the cur child is 0 - 1 instance */ switch (obj_get_iqualval(curchild)) { case NCX_IQUAL_ONE: case NCX_IQUAL_OPT: if (chobj) { chobj = obj_next_child(chobj); } break; default: break; } } xml_clean_node(&chnode); /* check if this is the special internal load-config RPC method, * in which case there will not be an end tag for the * start tag passed to this function. Need to quick exit * to prevent reading past the end of the XML config file, * which just contains the element * * know there is just one parameter named so just * go through the loop once because the tag * will not be present */ if ((startnode->nsid == 0 || startnode->nsid == xmlns_nc_id() || startnode->nsid == xmlns_ncx_id() || startnode->nsid == xmlns_find_ns_by_name(NC_MODULE)) && !xml_strcmp(startnode->elname, NCX_EL_LOAD_CONFIG)) { done = TRUE; } } xml_clean_node(&chnode); return retres; } /* parse_complex_nc */ /******************************************************************** * FUNCTION parse_metadata_nc * * Parse all the XML attributes in the specified xml_node_t struct * * Only XML_NT_START or XML_NT_EMPTY nodes will have attributes * * INPUTS: * scb == session control block * msg == incoming RPC message * Errors are appended to msg->errQ * obj == object template containing meta-data definition * nserr == TRUE if namespace errors should be checked * == FALSE if not, and any attribute is accepted * node == node of the parameter maybe with attributes to be parsed * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * msg->errQ may be appended with new errors or warnings * retval->editop contains the last value of the operation attribute * seen, if any; will be OP_EDITOP_NONE if not set * * RETURNS: * status *********************************************************************/ static status_t parse_metadata_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, const xml_node_t *node, boolean nserr, val_value_t *retval) { obj_metadata_t *meta; typ_def_t *metadef; xml_attr_t *attr; val_value_t *metaval; xmlns_id_t ncid, yangid, wdaid; status_t res, retres; retres = NO_ERR; ncid = xmlns_nc_id(); yangid = xmlns_yang_id(); wdaid = xmlns_wda_id(); /* go through all the attributes in the node and convert * to val_value_t structs * find the correct typedef to match each attribute */ for (attr = xml_get_first_attr(node); attr != NULL; attr = xml_next_attr(attr)) { if (attr->attr_dname && !xml_strncmp(attr->attr_dname, XMLNS, xml_strlen(XMLNS))) { /* skip this 'xmlns' attribute */ continue; } /* init per-loop locals */ res = NO_ERR; metadef = NULL; if (retval->editvars == NULL) { res = val_new_editvars(retval); if (res != NO_ERR) { agt_record_error(scb, msg, NCX_LAYER_CONTENT, res, NULL, NCX_NT_NONE, NULL, NCX_NT_VAL, retval); return res; } } /* check qualified and unqualified operation attribute, * then the 'xmlns' attribute, then a defined attribute */ if (val_match_metaval(attr, ncid, NC_OPERATION_ATTR_NAME)) { retval->editop = op_editop_id(attr->attr_val); if (retval->editop == OP_EDITOP_NONE) { res = ERR_NCX_INVALID_VALUE; } else { retval->editvars->operset = TRUE; continue; } } else if (val_match_metaval(attr, yangid, YANG_K_INSERT)) { retval->editvars->insertop = op_insertop_id(attr->attr_val); if (retval->editvars->insertop == OP_INSOP_NONE) { res = ERR_NCX_INVALID_VALUE; } else { retval->editvars->operset = TRUE; continue; } } else if (val_match_metaval(attr, yangid, YANG_K_KEY)) { if (!retval->editvars->insertstr) { retval->editvars->iskey = TRUE; retval->editvars->insertstr = xml_strdup(attr->attr_val); if (!retval->editvars->insertstr) { res = ERR_INTERNAL_MEM; } else { /* hand off the XPath parser control block * which has already resolved the XML * namespace prefixes to NSID numbers */ retval->editvars->insertxpcb = attr->attr_xpcb; attr->attr_xpcb = NULL; retval->editvars->operset = TRUE; continue; } } else { /* insert and key both entered */ res = ERR_NCX_EXTRA_ATTR; } } else if (val_match_metaval(attr, yangid, YANG_K_VALUE)) { if (!retval->editvars->insertstr) { retval->editvars->iskey = FALSE; retval->editvars->insertstr = xml_strdup(attr->attr_val); if (!retval->editvars->insertstr) { res = ERR_INTERNAL_MEM; } else { retval->editvars->operset = TRUE; continue; } } else { /* insert and key both entered */ res = ERR_NCX_EXTRA_ATTR; } } else if (val_match_metaval(attr, wdaid, YANG_K_DEFAULT)) { if (ncx_is_true(attr->attr_val)) { val_set_withdef_default(retval); continue; } else if (ncx_is_false(attr->attr_val)) { continue; } else { res = ERR_NCX_INVALID_VALUE; } } else { /* find the attribute definition in this typdef */ meta = obj_find_metadata(obj, attr->attr_name); if (meta) { metadef = meta->typdef; } else if (!nserr) { metadef = typ_get_basetype_typdef(NCX_BT_STRING); } } if (metadef) { /* alloc a new value struct for the attribute * do not save the operation, insert, key, or * value attributes from NETCONF and YANG * these must not persist in the database contents */ metaval = val_new_value(); if (!metaval) { res = ERR_INTERNAL_MEM; } else { /* parse the attribute string against the typdef */ res = val_parse_meta(metadef, attr, metaval); if (res == NO_ERR) { dlq_enque(metaval, &retval->metaQ); } else { val_free_value(metaval); } } } else if (res == NO_ERR) { res = ERR_NCX_UNKNOWN_ATTRIBUTE; } if (res != NO_ERR) { agt_record_attr_error(scb, msg, NCX_LAYER_OPERATION, res, attr, node, NULL, NCX_NT_VAL, retval); CHK_EXIT(res, retres); } } return retres; } /* parse_metadata_nc */ /******************************************************************** * FUNCTION metadata_inst_check * * Validate that all the XML attributes in the specified * xml_node_t struct are pesent in appropriate numbers * * Since attributes are unordered, they all have to be parsed * before they can be checked for instance count * * INPUTS: * scb == session control block * msg == incoming RPC message * Errors are appended to msg->errQ * val == value to check for metadata errors * * OUTPUTS: * msg->errQ may be appended with new errors or warnings * * RETURNS: * status *********************************************************************/ static status_t metadata_inst_check (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val) { obj_metadata_t *meta; obj_type_t objtype; uint32 insertcnt, cnt, checkcnt; status_t res = NO_ERR; xmlns_qname_t qname; xmlns_id_t yangid = xmlns_yang_id(); op_insertop_t insop = OP_INSOP_NONE; if (!val->obj) { return SET_ERROR(ERR_INTERNAL_VAL); } objtype = val->obj->objtype; /* figure out how many key/value attributes are allowed */ if (val->editvars) { insop = val->editvars->insertop; } switch (insop) { case OP_INSOP_NONE: insertcnt = 0; checkcnt = 1; break; case OP_INSOP_FIRST: case OP_INSOP_LAST: insertcnt = 1; checkcnt = 0; break; case OP_INSOP_BEFORE: case OP_INSOP_AFTER: insertcnt = 1; checkcnt = 1; break; default: return SET_ERROR(ERR_INTERNAL_VAL); } /* check if delete, these attrs not allowed at all then */ if (val->editop == OP_EDITOP_DELETE || val->editop == OP_EDITOP_REMOVE) { checkcnt = 0; } /* check if 'insert' entered and not allowed */ if (insertcnt && (!(objtype==OBJ_TYP_LIST || objtype==OBJ_TYP_LEAF_LIST))) { res = ERR_NCX_EXTRA_ATTR; qname.nsid = yangid; qname.name = YANG_K_INSERT; agt_record_error(scb, msg, NCX_LAYER_CONTENT, res, NULL, NCX_NT_QNAME, &qname, NCX_NT_VAL, val); } /* check the inst count of the YANG attributes * key attribute allowed for list only */ if (res == NO_ERR && val->editvars && val->editvars->insertstr) { if (val->editvars->iskey) { if (!checkcnt || val->obj->objtype != OBJ_TYP_LIST) { res = ERR_NCX_EXTRA_ATTR; qname.nsid = yangid; qname.name = YANG_K_KEY; agt_record_error(scb, msg, NCX_LAYER_CONTENT, res, NULL, NCX_NT_QNAME, &qname, NCX_NT_VAL, val); } } else { if (!checkcnt || val->obj->objtype != OBJ_TYP_LEAF_LIST) { res = ERR_NCX_EXTRA_ATTR; qname.nsid = yangid; qname.name = YANG_K_VALUE; agt_record_error(scb, msg, NCX_LAYER_CONTENT, res, NULL, NCX_NT_QNAME, &qname, NCX_NT_VAL, val); } } } /* check missing value or key attributes */ if (res == NO_ERR && val->editvars && !val->editvars->insertstr && (val->editvars->insertop==OP_INSOP_BEFORE || val->editvars->insertop==OP_INSOP_AFTER)) { if (val->obj->objtype == OBJ_TYP_LEAF_LIST) { /* the value attribute is missing */ res = ERR_NCX_MISSING_ATTR; qname.nsid = yangid; qname.name = YANG_K_VALUE; agt_record_error(scb, msg, NCX_LAYER_CONTENT, res, NULL, NCX_NT_QNAME, &qname, NCX_NT_VAL, val); } else if (val->obj->objtype == OBJ_TYP_LIST) { /* the key attribute is missing */ res = ERR_NCX_MISSING_ATTR; qname.nsid = yangid; qname.name = YANG_K_KEY; agt_record_error(scb, msg, NCX_LAYER_CONTENT, res, NULL, NCX_NT_QNAME, &qname, NCX_NT_VAL, val); } } /* check more than 1 of each specified metaval * probably do not need to do this because the * xmlTextReader parser might barf on the invalid XML * way before this test gets to run */ for (meta = obj_first_metadata(val->obj); meta != NULL; meta = obj_next_metadata(meta)) { /* check the instance qualifier from the metadata * continue the loop if there is no error */ cnt = val_metadata_inst_count(val, meta->nsid, meta->name); if (cnt > 1) { res = ERR_NCX_EXTRA_ATTR; qname.nsid = meta->nsid; qname.name = meta->name; agt_record_error(scb, msg, NCX_LAYER_CONTENT, res, NULL, NCX_NT_QNAME, &qname, NCX_NT_VAL, val); } } return res; } /* metadata_inst_check */ /******************************************************************** * FUNCTION parse_btype_nc * * Switch to dispatch to specific base type handler * * INPUTS: * scb == session control block * Input is read from scb->reader. * msg == incoming RPC message * Errors are appended to msg->errQ * obj == object template to use for parsing * startnode == top node of the parameter to be parsed * Parser function will attempt to consume all the * nodes until the matching endnode is reached * parentdc == data class of the parent node, which will get * used if it is not explicitly set for the typdef * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * msg->errQ may be appended with new errors or warnings * * RETURNS: * status *********************************************************************/ static status_t parse_btype_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval) { ncx_btype_t btyp; status_t res, res2, res3; boolean nserr, errfromchild = FALSE; btyp = obj_get_basetype(obj); /* get the attribute values from the start node */ retval->editop = OP_EDITOP_NONE; retval->nsid = startnode->nsid; /* check namespace errors except if the type is ANY* */ nserr = (btyp != NCX_BT_ANYDATA && btyp != NCX_BT_ANYXML); if (retval->obj == NULL) { /* in case the parse_metadata_nc function * wants to record an error */ retval->obj = ncx_get_gen_anyxml(); } /* parse the attributes, if any */ res2 = parse_metadata_nc(scb, msg, obj, startnode, nserr, retval); /* continue to parse the startnode depending on the base type * to record as many errors as possible */ switch (btyp) { case NCX_BT_ANYDATA: case NCX_BT_ANYXML: res = parse_any_nc(scb, msg, startnode, parentdc, retval); break; case NCX_BT_ENUM: res = parse_enum_nc(scb, msg, obj, startnode, parentdc, retval); break; case NCX_BT_EMPTY: res = parse_empty_nc(scb, msg, obj, startnode, parentdc, retval); break; case NCX_BT_BOOLEAN: res = parse_boolean_nc(scb, msg, obj, startnode, parentdc, retval); break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: res = parse_num_nc(scb, msg, obj, btyp, startnode, parentdc, retval); break; case NCX_BT_LEAFREF: case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_SLIST: case NCX_BT_BITS: case NCX_BT_INSTANCE_ID: res = parse_string_nc(scb, msg, obj, btyp, startnode, parentdc, retval); break; case NCX_BT_IDREF: res = parse_idref_nc(scb, msg, obj, startnode, parentdc, retval); break; case NCX_BT_UNION: res = parse_union_nc(scb, msg, obj, startnode, parentdc, retval); break; case NCX_BT_CONTAINER: case NCX_BT_LIST: case NCX_BT_CHOICE: case NCX_BT_CASE: res = parse_complex_nc(scb, msg, obj, startnode, parentdc, retval, &errfromchild); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } /* set the config flag for this value */ res3 = NO_ERR; if (res == NO_ERR && res2 == NO_ERR) { /* this has to be after the retval typdef is set */ res3 = metadata_inst_check(scb, msg, retval); } if (res != NO_ERR) { if (!errfromchild) { retval->res = res; } return res; } else if (res2 != NO_ERR) { if (!errfromchild) { retval->res = res; } retval->res = res2; return res2; } if (!errfromchild) { retval->res = res3; } return res3; } /* parse_btype_nc */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION agt_val_parse_nc * * Parse NETCONF PDU sub-contents into value fields * This module does not enforce complex type completeness. * Different subsets of configuration data are permitted * in several standard (and any proprietary) RPC methods * * Makes sure that only allowed value strings * or child nodes (and their values) are entered. * * Defaults are not added to any objects * Missing objects are not checked * * A seperate parsing phase is used to fully validate the input * contained in the returned val_value_t struct. * * This parsing phase checks that simple types are complete * and child members of complex types are valid (but maybe * missing or incomplete child nodes. * * Note that the NETCONF inlineFilterType will be parsed * correctly because it is type 'anyxml'. This type is * parsed as a struct, and no validation other well-formed * XML is done. * * INPUTS: * scb == session control block * msg == incoming RPC message * obj == obj_template_t for the object to parse * startnode == top node of the parameter to be parsed * parentdc == parent data class * For the first call to this function, this will * be the data class of a parameter. * retval == address of initialized return value * * OUTPUTS: * *status will be filled in * msg->errQ may be appended with new errors * * RETURNS: * status *********************************************************************/ status_t agt_val_parse_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval) { status_t res; #ifdef DEBUG if (!scb || !msg || !obj || !startnode || !retval) { /* non-recoverable error */ return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (LOGDEBUG3) { log_debug3("\nagt_val_parse: %s:%s btyp:%s", obj_get_mod_prefix(obj), obj_get_name(obj), tk_get_btype_sym(obj_get_basetype(obj))); } /* get the element values */ res = parse_btype_nc(scb, msg, obj, startnode, parentdc, retval); return res; } /* agt_val_parse_nc */ #ifdef DEBUG /******************************************************************** * FUNCTION agt_val_parse_test * * scaffold code to get agt_val_parse tested * * INPUTS: * testfile == NETCONF PDU in a test file * *********************************************************************/ void agt_val_parse_test (const char *testfile) { ses_cb_t *scb; status_t res; /* create a dummy session control block */ scb = agt_ses_new_dummy_session(); if (!scb) { SET_ERROR(ERR_INTERNAL_MEM); return; } /* open the XML reader */ res = xml_get_reader_from_filespec(testfile, &scb->reader); if (res != NO_ERR) { SET_ERROR(res); agt_ses_free_dummy_session(scb); return; } /* dispatch the test PDU, note scb might be deleted! */ agt_top_dispatch_msg(&scb); /* clean up and exit */ if ( scb ) { agt_ses_free_dummy_session(scb); } } /* agt_val_parse_test */ #endif /* END file agt_val_parse.c */ yuma123_2.14/netconf/src/agt/agt_if.h0000664000175000017500000000673114770023131017532 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_if #define _H_agt_if /* FILE: agt_if.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF interface entry monitoring ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 18-jul-09 abb Begun. */ #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_if_init * * INIT 1: * Initialize the interfaces monitor module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_if_init (void); /******************************************************************** * FUNCTION agt_if_init2 * * INIT 2: * Initialize the monitoring data structures * This must be done after the config is loaded * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_if_init2 (void); /******************************************************************** * FUNCTION agt_if_cleanup * * Cleanup the module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void agt_if_cleanup (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_if */ yuma123_2.14/netconf/src/agt/agt_yang_library.h0000664000175000017500000000402114770023131021604 0ustar vladimirvladimir/* * Copyright (c) 2017, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_yang_library.h NETCONF ietf-yang-library.yang Data Model defs: Server Side Support */ #include "dlq.h" #include "ncxtypes.h" #include "obj.h" #include "ses.h" #include "status.h" #include "tstamp.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_yang_library_init * * INIT 1: * Initialize the yang library data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_yang_library_init (void); /******************************************************************** * FUNCTION agt_yang_library_init2 * * INIT 2: * Initialize the yang library data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_yang_library_init2 (void); /******************************************************************** * FUNCTION agt_yang_library_cleanup * * Cleanup the module data structures * * INPUTS: * * RETURNS: * none *********************************************************************/ extern void agt_yang_library_cleanup (void); #ifdef __cplusplus } /* end extern 'C' */ #endif /* END file agt_yang_library.h */ yuma123_2.14/netconf/src/agt/agt.h0000664000175000017500000003553014770023131017053 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt #define _H_agt /* FILE: agt.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Server message handler - NETCONF PDUs - PDU instance document parsing - Object instance syntax validation - Basic object-level referential integrity - Value instance document parsing - Value instance document syntax validation - Value instance count validation - Value instance default value insertion The netconfd server handler registers callbacks with the agt_val module that can be called IN ADDITION to the automated callback, for referential integrity checking, resource reservation, etc. NCX Write Model --------------- netconfd uses a 3 pass callback model to process PDUs. Pass 1: Validation : AGT_CB_VALIDATE The target database is not touched during this phase. - XML parsing - data type validation - pattern and regular expression matching - dynamic instance validation - instance count validation - choice and choice group validation - insertion by default validation - nested 'operation' attribute validation If any errors occur within a value node (within the /config/ container) then that entire value node is considered unusable and it is not applied. If continue-on-error is requested, then all sibling nodes will be processed if the validation tests all pass. Pass 2: Apply : AGT_CB_APPLY On a per-node granularity, an operation is processed only if no errors occurred during the validation phase. The target database is modified. User callbacks should not emit external PDUs (e.g., BGP changes) until the COMMIT phase. The automated PDU processing will make non-destructive edits during this phase, even if the 'rollback-on-error' error-option is not requested (in order to simplify the code). If an error-option other than continue-on-error is requested, then any error returned during this phase will cause further 'apply' phase processing to be terminated. If error-option is 'stop-on-error' then phase 3 is not executed if this phase terminates with an error. Pass 3-OK: Commit (Positive Outcome) : AGT_CB_COMMIT The database edits are automatically completed during this phase. The user callbacks must not edit the database -- only modify their own data structures, send PDUs, etc. Pass 3-ERR: Rollback (Negative Outcome) : AGT_CB_ROLLBACK If rollback-on-error is requested, then this phase will be executed for only the application nodes (and sibling parmsets within the errored application) that have already executed the 'apply' phase. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 04-nov-05 abb Begun 02-aug-08 abb Convert from NCX typdef to YANG object design 03-oct-08 abb Convert to YANG only, remove app, parmset, and parm layers in the NETCONF database */ #ifndef _H_help #include "help.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define AGT_NUM_CB (AGT_CB_ROLLBACK+1) /* ncxserver magic cookie hack */ #define NCX_SERVER_MAGIC \ "x56o8937ab17eg922z34rwhobskdbyswfehkpsqq3i55a0an960ccw24a4ek864aOpal1t2p" #define AGT_MAX_PORTS 4 #define AGT_DEF_CONF_FILE (const xmlChar *)"/etc/yuma/netconfd.conf" /* this behavior used to set to TRUE, before 1.15-1 */ #define AGT_DEF_DELETE_EMPTY_NP FALSE /* this is over-ridden by the --system-sorted CLI parameter */ #define AGT_DEF_SYSTEM_SORTED FALSE #define AGT_USER_VAR (const xmlChar *)"user" #define AGT_URL_SCHEME_LIST (const xmlChar *)"file" #define AGT_FILE_SCHEME (const xmlChar *)"file:///" /* tests for agt_commit_test_t flags field */ #define AGT_TEST_FL_MIN_ELEMS bit0 #define AGT_TEST_FL_MAX_ELEMS bit1 #define AGT_TEST_FL_MANDATORY bit2 #define AGT_TEST_FL_MUST bit3 #define AGT_TEST_FL_UNIQUE bit4 #define AGT_TEST_FL_XPATH_TYPE bit5 #define AGT_TEST_FL_CHOICE bit6 /* not really a commit test; done during delete_dead_nodes() */ #define AGT_TEST_FL_WHEN bit7 /* mask for all the commit tests */ #define AGT_TEST_ALL_COMMIT_MASK (bit0|bit1|bit2|bit3|bit4|bit5|bit6) /* mask for the subset of instance tests */ #define AGT_TEST_INSTANCE_MASK (bit0|bit1|bit2|bit6) /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* matches access-control enumeration in netconfd.yang */ typedef enum agt_acmode_t_ { AGT_ACMOD_NONE, AGT_ACMOD_ENFORCING, AGT_ACMOD_PERMISSIVE, AGT_ACMOD_DISABLED, AGT_ACMOD_OFF } agt_acmode_t; /* server config state used in agt_val_root_check */ typedef enum agt_config_state_t_ { AGT_CFG_STATE_NONE, AGT_CFG_STATE_INIT, AGT_CFG_STATE_OK, AGT_CFG_STATE_BAD } agt_config_state_t; /* enumeration of the different server callback types * These are used are array indices so there is no dummy zero enum * AGT_CB_TEST_APPLY has been replaced by AGT_CB_APPLY * during the edit-config procedure * AGT_CB_COMMIT_CHECK has been replaced by AGT_CB_VALIDATE * during the commit procedure */ typedef enum agt_cbtyp_t_ { AGT_CB_VALIDATE, /* P1: write operation validate */ AGT_CB_APPLY, /* P2: write operation apply */ AGT_CB_COMMIT, /* P3-pos: write operation commit */ AGT_CB_ROLLBACK /* P3-neg: write operation rollback */ } agt_cbtyp_t; /* hardwire some of the server profile parameters * because they are needed before the NCX engine is running * They cannot be changed after boot-time. */ typedef struct agt_profile_t_ { ncx_agttarg_t agt_targ; ncx_agtstart_t agt_start; log_debug_t agt_loglevel; boolean agt_log_acm_reads; boolean agt_log_acm_writes; boolean agt_validate_all; boolean agt_has_startup; boolean agt_usestartup; /* --no-startup flag */ boolean agt_factorystartup; /* --factory-startup flag */ boolean agt_startup_error; /* T: stop, F: continue */ boolean agt_running_error; /* T: stop, F: continue */ boolean agt_logappend; boolean agt_xmlorder; boolean agt_deleteall_ok; /* TBD: not implemented */ boolean agt_stream_output; /* d:true; no CLI support yet */ boolean agt_delete_empty_npcontainers; /* d: false */ boolean agt_notif_sequence_id; /* d: false */ const xmlChar *agt_accesscontrol; const xmlChar *agt_conffile; const xmlChar *agt_logfile; const xmlChar *agt_startup; const xmlChar *agt_defaultStyle; const xmlChar *agt_superuser; uint32 agt_eventlog_size; uint32 agt_maxburst; uint32 agt_hello_timeout; uint32 agt_idle_timeout; uint32 agt_linesize; int32 agt_indent; boolean agt_usevalidate; /* --with-validate */ boolean agt_useurl; /* --with-url */ boolean agt_system_sorted; ncx_withdefaults_t agt_defaultStyleEnum; agt_acmode_t agt_accesscontrol_enum; uint16 agt_ports[AGT_MAX_PORTS]; /* yuma123 extended parameters */ uint32 agt_max_sessions; const xmlChar *agt_tcp_direct_address; int32 agt_tcp_direct_port; const xmlChar *agt_ncxserver_sockname; /****** state variables; TBD: move out of profile ******/ /* root commit test flags */ uint32 agt_rootflags; /* server config state */ agt_config_state_t agt_config_state; /* server load-config errors */ boolean agt_load_validate_errors; boolean agt_load_rootcheck_errors; boolean agt_load_top_rootcheck_errors; boolean agt_load_apply_errors; /* Q of malloced ncx_save_deviations_t */ dlq_hdr_t agt_savedevQ; /* Q of malloced agt_commit_test_t */ dlq_hdr_t agt_commit_testQ; /* cached location of startup transaction ID file */ xmlChar *agt_startup_txid_file; } agt_profile_t; /* SIL init function */ typedef status_t (*agt_sil_init_fn_t)(const xmlChar *modname, const xmlChar *revision); /* SIL init2 function */ typedef status_t (*agt_sil_init2_fn_t)(void); /* SIL cleanup function */ typedef void (*agt_sil_cleanup_fn_t)(void); /* struct to keep track of the dynamic libraries * opened by the 'load' command */ typedef struct agt_dynlib_cb_t_ { dlq_hdr_t qhdr; void *handle; xmlChar *modname; xmlChar *revision; ncx_module_t *mod; agt_sil_init_fn_t initfn; agt_sil_init2_fn_t init2fn; agt_sil_cleanup_fn_t cleanupfn; status_t init_status; status_t init2_status; boolean init2_done; boolean cleanup_done; } agt_dynlib_cb_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_init1 * * Initialize the Server Library: stage 1: CLI and profile * * TBD -- put platform-specific server init here * * INPUTS: * argc == command line argument count * argv == array of command line strings * showver == address of version return quick-exit status * showhelp == address of help return quick-exit status * * OUTPUTS: * *showver == TRUE if user requsted version quick-exit mode * *showhelp == TRUE if user requsted help quick-exit mode * * RETURNS: * status of the initialization procedure *********************************************************************/ extern status_t agt_init1 (int argc, char *argv[], boolean *showver, help_mode_t *showhelpmode); /******************************************************************** * FUNCTION agt_init2 * * Initialize the Server Library * The agt_profile is set and the object database is * ready to have YANG modules loaded * * RPC and data node callbacks should be installed * after the module is loaded, and before the running config * is loaded. * * RETURNS: * status *********************************************************************/ extern status_t agt_init2 (void); /******************************************************************** * FUNCTION agt_cleanup * * Cleanup the Server Library * *********************************************************************/ extern void agt_cleanup (void); /******************************************************************** * FUNCTION agt_get_profile * * Get the server profile struct * * INPUTS: * none * RETURNS: * pointer to the server profile *********************************************************************/ extern agt_profile_t * agt_get_profile (void); /******************************************************************** * FUNCTION agt_request_shutdown * * Request some sort of server shutdown * * INPUTS: * mode == requested shutdown mode * *********************************************************************/ extern void agt_request_shutdown (ncx_shutdowntyp_t mode); /******************************************************************** * FUNCTION agt_shutdown_requested * * Check if some sort of server shutdown is in progress * * RETURNS: * TRUE if shutdown mode has been started * *********************************************************************/ extern boolean agt_shutdown_requested (void); /******************************************************************** * FUNCTION agt_shutdown_mode_requested * * Check what shutdown mode was requested * * RETURNS: * shutdown mode * *********************************************************************/ extern ncx_shutdowntyp_t agt_shutdown_mode_requested (void); /******************************************************************** * FUNCTION agt_cbtype_name * * Get the string for the server callback phase * * INPUTS: * cbtyp == callback type enum * * RETURNS: * const string for this enum *********************************************************************/ extern const xmlChar * agt_cbtype_name (agt_cbtyp_t cbtyp); #ifndef STATIC_SERVER /******************************************************************** * FUNCTION agt_load_sil_code * * Load the Server Instrumentation Library for the specified module * * INPUTS: * modname == name of the module to load * revision == revision date of the module to load (may be NULL) * cfgloaded == TRUE if running config has already been done * FALSE if running config not loaded yet * * RETURNS: * const string for this enum *********************************************************************/ extern status_t agt_load_sil_code (const xmlChar *modname, const xmlChar *revision, boolean cfgloaded); #endif /******************************************************************** * FUNCTION agt_advertise_module_needed * * Check if the module should be advertised or not * Hard-wired hack at this time * * INPUTS: * modname == module name to check * RETURNS: * none *********************************************************************/ extern boolean agt_advertise_module_needed (const xmlChar *modname); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt */ yuma123_2.14/netconf/src/agt/agt_cfg.c0000664000175000017500000006336314770023131017672 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_cfg.c Manage Server configuration edit transactions Special Config Operation: agt_rpc_load_config_file .... Used for loading running from startup at boot-time and when restoring the running config from backup after the confirm-commit is canceled or timed out. Two sources supported: inline config and URL for a local XML file .... file://foo.xml Configuration States: +-----------+ +---------+ +---------+ | INIT | | BAD | | OK | +-----------+ +---------+ +---------+ V ^ ^ | | fail | pass +-------------+------------+ root-check If agt_profile.agt_running_error == FALSE: - only INIT and OK states are used. BAD state will cause the server to terminate If agt_profile.agt_running_error == TRUE: - server config state will stay BAD until the root-check succeeds, then it will transition to GOOD and stay there Init State: - the startup config is loaded into the running config if enabled. - the agt_profile.agt_startup_error flag is checked - if FALSE, then any errors at this point will cause the server to terminate. - if TRUE the server will continue after pruning error nodes from the config - the static and dynamic SIL code is run to give it a chance to make sure all factory-preset nodes, especially top-level nodes, are present and properly configured - the root check is run on the complete running config - the agt_profile.agt_running_error flag is checked - if FALSE, then any errors at this point will cause the server to terminate. The config state is set to 'OK' - if TRUE the server will continue!!! Use this mode carefully!!! No edit-config to running or commit operation will succeed until the root checks all pass. The config state is set to Bad OK State: - The running config was loaded correctly - Normal root test node pruning optimizations are enabled Bad State: - The running config was not loaded correctly - Normal root test node pruning optimizations are not enabled - Full root checks will be done until they all pass, and if so the config state is set to OK Configuration Edit Actions Pre-Apply: The newnode is removed from its source value tree and a deleted node put in its place +-----------+ +----------------+ | newnode | <- val_swap_child | newnode_marker | +-----------+ +----------------+ If curnode exists and it is a leaf: +-----------+ +---------------+ | curnode | val_clone_ex -> | curnode_clone | +-----------+ +---------------+ AGT_CFG_EDIT_ACTION_ADD: The newnode is added to the proper parent node in the target tree +-----------+ +---------+ | newnode | add_child_clean -> | newnode | +-----------+ +---------+ AGT_CFG_EDIT_ACTION_SET: Merge leaf or duplicate leaf-list if curnode exists. The newnode will be placed back in its source tree in the recovery phase. +-----------+ +---------+ | newnode | val_merge -> | curnode | +-----------+ +---------+ Merge complex type will always pick the current node if it exists. The applyhere flag will not be set for merge if both newnode and curnode exist. AGT_CFG_EDIT_ACTION_MOVE: AGT_CFG_EDIT_ACTION_DELETE: AGT_CFG_EDIT_ACTION_DELETE_DEFAULT: Recovery: The newnode_marker is removed from its source value tree and discarded, and the newnode parm is put in its place +-----------+ +----------------+ | newnode | val_swap_child -> | newnode_marker | +-----------+ +----------------+ ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 22dec11 abb begun; split out from ncx/rpc ... ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cfg.h" #include "agt_util.h" #include "cfg.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "ncx_num.h" #include "ncxconst.h" #include "obj.h" #include "op.h" #include "rpc.h" #include "rpc_err.h" #include "status.h" #include "val.h" #include "val_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static cfg_transaction_id_t agt_cfg_txid; static const xmlChar *agt_cfg_txid_filespec; /******************************************************************** * FUNCTION allocate_txid * * Allocate a new transaction ID * * SIDE EFFECTS: * agt_cfg_txid is updated * RETURNS: * new value of agt_cfg_txid *********************************************************************/ static cfg_transaction_id_t allocate_txid (void) { ++agt_cfg_txid; if (agt_cfg_txid == 0) { /* skip value zero */ ++agt_cfg_txid; } log_debug2("\nAllocated transaction ID '%llu'", (unsigned long long)agt_cfg_txid); /* for now, update txid file every time new transaction allocated * may need to change this to timer-based task if lots of * transactions requested per second */ status_t res = agt_cfg_update_txid(); if (res != NO_ERR) { log_error("\nError: could not update TXID file (%s)", get_error_string(res)); } return agt_cfg_txid; } /* allocate_txid */ /******************************************************************** * FUNCTION read_txid_file * * Read the transaction ID file and return the current ID value found * * INPUTS: * txidfile == full filespec of the transaction ID file * curid == address of return ID * * OUTPUTS: * *curid == return current ID (if return NO_ERR * * RETURNS: * status *********************************************************************/ static status_t read_txid_file (const xmlChar *txidfile, cfg_transaction_id_t *curid) { assert( txidfile && "txidfile is NULL" ); assert( curid && "curid is NULL" ); *curid = 0; status_t res = NO_ERR; FILE *fil = fopen((const char *)txidfile, "r"); if (!fil) { res = errno_to_status(); log_error("\nError: Open txid file for read failed (%s)", get_error_string(res)); return res; } char buffer [128]; if (fgets(buffer, sizeof buffer, fil)) { /* expecting just 1 line containing the ASCII encoding of * the transaction ID */ log_debug4("\nRead transaction ID line '%s'", buffer); uint32 len = xml_strlen((const xmlChar *)buffer); if (len > 1) { /* strip ending newline */ buffer[len-1] = 0; ncx_num_t num; ncx_init_num(&num); res = ncx_convert_num((xmlChar *)buffer, NCX_NF_DEC, NCX_BT_UINT64, &num); if (res == NO_ERR) { agt_cfg_txid = num.ul; *curid = num.ul; log_debug3("\nGot transaction ID line '%llu'", (unsigned long long)num.ul); } else { log_error("\nError: txid is not valid (%s)", get_error_string(res)); } ncx_clean_num(NCX_BT_UINT64, &num); } else { res = ERR_NCX_INVALID_NUM; log_error("\nError: txid is not valid (%s)", get_error_string(res)); } } else { res = errno_to_status(); log_error("\nError: Read txid file failed (%s)", get_error_string(res)); } fclose(fil); return res; } /* read_txid_file */ /******************************************************************** * FUNCTION write_txid_file * * Write the transaction ID file with the supplied value * * INPUTS: * txidfile == full filespec of the transaction ID file * curid == pointer to new txid to write * * RETURNS: * status *********************************************************************/ static status_t write_txid_file (const xmlChar *txidfile, cfg_transaction_id_t *curid) { assert( txidfile && "txidfile is NULL" ); assert( curid && "curid is NULL" ); status_t res = NO_ERR; FILE *fil = fopen((const char *)txidfile, "w"); if (!fil) { res = errno_to_status(); log_error("\nError: Open txid file for write failed (%s)", get_error_string(res)); return res; } uint32 len = 0; char buffer [128]; ncx_num_t num; ncx_init_num(&num); num.ul = *curid; res = ncx_sprintf_num((xmlChar *)buffer, &num, NCX_BT_UINT64, &len); if (res == NO_ERR) { buffer[len] = '\n'; buffer[len+1] = 0; int ret = fputs(buffer, fil); if (ret <= 0) { res = errno_to_status(); log_error("\nError: write txid ID file failed (%s)", get_error_string(res)); } } else { log_error("\nError: sprintf txid ID failed (%s)", get_error_string(res)); } ncx_clean_num(NCX_BT_UINT64, &num); fclose(fil); return res; } /* write_txid_file */ /******************************************************************** * FUNCTION clean_undorec * * Clean all the memory used by the specified agt_cfg_undo_rec_t * but do not free the struct itself * * !!! The caller must free internal pointers that were malloced * !!! instead of copied. This function does not check them!!! * * INPUTS: * undo == agt_cfg_undo_rec_t to clean * reuse == TRUE if possible reuse * FALSE if this node is being freed right now * * RETURNS: * none *********************************************************************/ static void clean_undorec (agt_cfg_undo_rec_t *undo, boolean reuse) { if (undo->free_curnode && undo->curnode) { val_free_value(undo->curnode); } if (undo->curnode_clone) { val_free_value(undo->curnode_clone); } if (undo->newnode_marker) { val_free_value(undo->newnode_marker); } if (undo->curnode_marker) { val_free_value(undo->curnode_marker); } /* really expecting the extra_deleteQ to be empty at this point */ while (!dlq_empty(&undo->extra_deleteQ)) { agt_cfg_nodeptr_t *nodeptr = (agt_cfg_nodeptr_t *) dlq_deque(&undo->extra_deleteQ); if (nodeptr && nodeptr->node) { val_remove_child(nodeptr->node); val_free_value(nodeptr->node); } else { SET_ERROR(ERR_INTERNAL_VAL); } agt_cfg_free_nodeptr(nodeptr); } if (reuse) { agt_cfg_init_undorec(undo); } } /* clean_undorec */ /***************** E X P O R T E D F U N C T I O N S ***********/ /******************************************************************** * FUNCTION agt_cfg_new_transaction * * Malloc and initialize agt_cfg_transaction_t struct * * INPUTS: * cfgid == config ID to use * edit_type == database edit type * rootcheck == TRUE if root_check needs to be done before commit * during agt_val_apply_write; FALSE if it is done * manually via agt_val_root_check * is_validate == TRUE if this is a operation * the target data nodes will not be altered * at all during the transaction * FALSE if this is some sort of real edit * res == address of return status * OUTPUTS: * *res == return status * RETURNS: * malloced transaction struct; need to call agt_cfg_free_transaction *********************************************************************/ agt_cfg_transaction_t * agt_cfg_new_transaction (ncx_cfg_t cfgid, agt_cfg_edit_type_t edit_type, boolean rootcheck, boolean is_validate, status_t *res) { assert( edit_type && "edit_type in NONE" ); assert( res && "res is NULL" ); cfg_template_t *cfg = cfg_get_config_id(cfgid); if (cfg == NULL) { *res = ERR_NCX_CFG_NOT_FOUND; return NULL; } if (cfg->cur_txid != 0) { /* a current transaction is already in progress */ *res = ERR_NCX_NO_ACCESS_STATE; return NULL; } agt_cfg_transaction_t *txcb = m__getObj(agt_cfg_transaction_t); if (txcb == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } memset(txcb, 0x0, sizeof txcb); dlq_createSQue(&txcb->undoQ); dlq_createSQue(&txcb->auditQ); dlq_createSQue(&txcb->deadnodeQ); txcb->txid = allocate_txid(); txcb->cfg_id = cfgid; txcb->rootcheck = rootcheck; txcb->edit_type = edit_type; txcb->is_validate = is_validate; txcb->apply_res = ERR_NCX_SKIPPED; txcb->commit_res = ERR_NCX_SKIPPED; txcb->rollback_res = ERR_NCX_SKIPPED; agt_profile_t *profile = agt_get_profile(); if (profile->agt_config_state == AGT_CFG_STATE_BAD) { txcb->start_bad = TRUE; } cfg->cur_txid = txcb->txid; *res = NO_ERR; return txcb; } /* agt_cfg_new_transaction */ /******************************************************************** * FUNCTION agt_cfg_free_transaction * * Clean and free a agt_cfg_transaction_t struct * * INPUTS: * txcb == transaction struct to free *********************************************************************/ void agt_cfg_free_transaction (agt_cfg_transaction_t *txcb) { if (txcb == NULL) { return; } /* clear current config txid if any */ if (txcb->txid) { cfg_template_t *cfg = cfg_get_config_id(txcb->cfg_id); if (cfg) { if (cfg->cur_txid == txcb->txid) { log_debug3("\nClearing current txid for %s config", cfg->name); cfg->cur_txid = 0; } } } /* clean undo queue */ while (!dlq_empty(&txcb->undoQ)) { agt_cfg_undo_rec_t *undorec = (agt_cfg_undo_rec_t *) dlq_deque(&txcb->undoQ); agt_cfg_free_undorec(undorec); } /* clean audit queue */ while (!dlq_empty(&txcb->auditQ)) { agt_cfg_audit_rec_t *auditrec = (agt_cfg_audit_rec_t *) dlq_deque(&txcb->auditQ); agt_cfg_free_auditrec(auditrec); } /* clean dead node queue */ while (!dlq_empty(&txcb->deadnodeQ)) { agt_cfg_nodeptr_t *nodeptr = (agt_cfg_nodeptr_t *) dlq_deque(&txcb->deadnodeQ); agt_cfg_free_nodeptr(nodeptr); } m__free(txcb); } /* agt_cfg_free_transaction */ /******************************************************************** * FUNCTION agt_cfg_init_transactions * * Initialize the transaction ID functionality * * INPUTS: * txidfile == TXID filespec to use * foundfile == TRUE if file found; FALSE if this is first write * RETURNS: * status *********************************************************************/ status_t agt_cfg_init_transactions (const xmlChar *txidfile, boolean foundfile) { assert( txidfile && "txidfile is NULL" ); status_t res = NO_ERR; /* if we get here profile->agt_startup_txid_file is set * now either read the file if it was found or start a new * file with an ID of zero if this is the first time set */ cfg_transaction_id_t txid = CFG_INITIAL_TXID; if (foundfile) { res = read_txid_file(txidfile, &txid); } else { log_debug("\nNo initial transaction ID file found;" " Setting running config initial transaction id to '0'"); res = write_txid_file(txidfile, &txid); } if (res != NO_ERR) { return res; } /* set all the last transaction ID values for the standard datastores */ cfg_template_t *cfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (cfg != NULL) { cfg->last_txid = txid; } cfg = cfg_get_config_id(NCX_CFGID_CANDIDATE); if (cfg != NULL) { cfg->last_txid = txid; } cfg = cfg_get_config_id(NCX_CFGID_STARTUP); if (cfg != NULL) { cfg->last_txid = txid; } /* set the global cached values of the TXID file and ID */ agt_cfg_txid = txid; agt_cfg_txid_filespec = txidfile; return res; } /* agt_cfg_init_transactions */ /******************************************************************** * FUNCTION agt_cfg_txid_in_progress * * Return the ID of the current transaction ID in progress * * INPUTS: * cfgid == config ID to check * RETURNS: * txid of transaction in progress or 0 if none *********************************************************************/ cfg_transaction_id_t agt_cfg_txid_in_progress (ncx_cfg_t cfgid) { cfg_template_t *cfg = cfg_get_config_id(cfgid); if (cfg) { return cfg->cur_txid; } return 0; } /* agt_cfg_txid_in_progress */ /******************************************************************** * FUNCTION agt_cfg_new_undorec * * Malloc and initialize a new agt_cfg_undo_rec_t struct * * INPUTS: * none * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ agt_cfg_undo_rec_t * agt_cfg_new_undorec (void) { agt_cfg_undo_rec_t *undo; undo = m__getObj(agt_cfg_undo_rec_t); if (!undo) { return NULL; } agt_cfg_init_undorec(undo); return undo; } /* agt_cfg_new_undorec */ /******************************************************************** * FUNCTION agt_cfg_init_undorec * * Initialize a new agt_cfg_undo_rec_t struct * * INPUTS: * undo == agt_cfg_undo_rec_t memory to initialize * RETURNS: * none *********************************************************************/ void agt_cfg_init_undorec (agt_cfg_undo_rec_t *undo) { #ifdef DEBUG if (!undo) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif memset(undo, 0x0, sizeof(agt_cfg_undo_rec_t)); dlq_createSQue(&undo->extra_deleteQ); undo->apply_res = ERR_NCX_SKIPPED; undo->commit_res = ERR_NCX_SKIPPED; undo->rollback_res = ERR_NCX_SKIPPED; } /* agt_cfg_init_undorec */ /******************************************************************** * FUNCTION agt_cfg_free_undorec * * Free all the memory used by the specified agt_cfg_undo_rec_t * * INPUTS: * undo == agt_cfg_undo_rec_t to clean and delete * RETURNS: * none *********************************************************************/ void agt_cfg_free_undorec (agt_cfg_undo_rec_t *undo) { if (!undo) { return; } clean_undorec(undo, FALSE); m__free(undo); } /* agt_cfg_free_undorec */ /******************************************************************** * FUNCTION agt_cfg_clean_undorec * * Clean all the memory used by the specified agt_cfg_undo_rec_t * but do not free the struct itself * * !!! The caller must free internal pointers that were malloced * !!! instead of copied. This function does not check them!!! * * INPUTS: * undo == agt_cfg_undo_rec_t to clean * RETURNS: * none *********************************************************************/ void agt_cfg_clean_undorec (agt_cfg_undo_rec_t *undo) { if (!undo) { return; } clean_undorec(undo, TRUE); } /* agt_cfg_clean_undorec */ /******************************************************************** * FUNCTION agt_cfg_new_auditrec * * Malloc and initialize a new agt_cfg_audit_rec_t struct * * INPUTS: * target == i-i string of edit target * editop == edit operation enum * * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ agt_cfg_audit_rec_t * agt_cfg_new_auditrec (const xmlChar *target, op_editop_t editop) { agt_cfg_audit_rec_t *auditrec; #ifdef DEBUG if (target == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif auditrec = m__getObj(agt_cfg_audit_rec_t); if (!auditrec) { return NULL; } memset(auditrec, 0x0, sizeof(agt_cfg_audit_rec_t)); auditrec->target = xml_strdup(target); if (auditrec->target == NULL) { m__free(auditrec); return NULL; } auditrec->editop = editop; return auditrec; } /* agt_cfg_new_auditrec */ /******************************************************************** * FUNCTION agt_cfg_free_auditrec * * Free all the memory used by the specified agt_cfg_audit_rec_t * * INPUTS: * auditrec == agt_cfg_audit_rec_t to clean and delete * * RETURNS: * none *********************************************************************/ void agt_cfg_free_auditrec (agt_cfg_audit_rec_t *auditrec) { if (auditrec == NULL) { return; } if (auditrec->target) { m__free(auditrec->target); } m__free(auditrec); } /* agt_cfg_free_auditrec */ /******************************************************************** * FUNCTION agt_cfg_new_commit_test * * Malloc a agt_cfg_commit_test_t struct * * RETURNS: * malloced commit test struct or NULL if ERR_INTERNAL_MEM *********************************************************************/ agt_cfg_commit_test_t * agt_cfg_new_commit_test (void) { agt_cfg_commit_test_t *commit_test = m__getObj(agt_cfg_commit_test_t); if (commit_test) { memset(commit_test, 0x0, sizeof(agt_cfg_commit_test_t)); } return commit_test; } /* agt_cfg_new_commit_test */ /******************************************************************** * FUNCTION agt_cfg_free_commit_test * * Free a previously malloced agt_cfg_commit_test_t struct * * INPUTS: * commit_test == commit test record to free * *********************************************************************/ void agt_cfg_free_commit_test (agt_cfg_commit_test_t *commit_test) { if (commit_test == NULL) { return; } if (commit_test->objpcb) { xpath_free_pcb(commit_test->objpcb); } if (commit_test->result) { xpath_free_result(commit_test->result); } m__free(commit_test); } /* agt_cfg_free_commit_test */ /******************************************************************** * FUNCTION agt_cfg_new_nodeptr * * Malloc and initialize a new agt_cfg_nodeptr_t struct * * INPUTS: * node == node to point at * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ agt_cfg_nodeptr_t * agt_cfg_new_nodeptr (val_value_t *node) { agt_cfg_nodeptr_t *nodeptr; nodeptr = m__getObj(agt_cfg_nodeptr_t); if (!nodeptr) { return NULL; } memset(nodeptr, 0x0, sizeof(agt_cfg_nodeptr_t)); nodeptr->node = node; return nodeptr; } /* agt_cfg_new_nodeptr */ /******************************************************************** * FUNCTION agt_cfg_free_nodeptr * * Free all the memory used by the specified agt_cfg_nodeptr_t * * INPUTS: * nodeptr == agt_cfg_nodeptr_t to clean and delete *********************************************************************/ void agt_cfg_free_nodeptr (agt_cfg_nodeptr_t *nodeptr) { if (!nodeptr) { return; } m__free(nodeptr); } /* agt_cfg_free_nodeptr */ /******************************************************************** * FUNCTION agt_cfg_update_txid * * Update the TXID file with the latest value transaction ID * * RETURNS: * status *********************************************************************/ status_t agt_cfg_update_txid (void) { assert( agt_cfg_txid_filespec && "txidfile is NULL" ); status_t res = NO_ERR; log_debug2("\nUpdating agt_txid file to '%llu'", (unsigned long long)agt_cfg_txid); res = write_txid_file(agt_cfg_txid_filespec, &agt_cfg_txid); return res; } /* agt_cfg_update_txid */ /* END file agt_cfg.c */ yuma123_2.14/netconf/src/agt/agt_not.h0000664000175000017500000002560114770023131017731 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_not #define _H_agt_not /* FILE: agt_not.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Notifications DM module support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30-may-09 abb Begun. */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tstamp #include "tstamp.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define AGT_NOT_MODULE1 (const xmlChar *)"notifications" #define AGT_NOT_MODULE2 (const xmlChar *)"nc-notifications" /* agt_not_subscription_t flags */ /* if set, stopTime is in the future */ #define AGT_NOT_FL_FUTURESTOP bit0 /* if set, replayComplete is ready to be sent */ #define AGT_NOT_FL_RC_READY bit1 /* if set replayComplete has been sent */ #define AGT_NOT_FL_RC_DONE bit2 /* if set, notificationComplete is ready to be sent */ #define AGT_NOT_FL_NC_READY bit3 /* if set, notificationComplete has been sent */ #define AGT_NOT_FL_NC_DONE bit4 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* per-subscription state */ typedef enum agt_not_state_t_ { /* none == cleared memory; not set */ AGT_NOT_STATE_NONE, /* init == during initialization */ AGT_NOT_STATE_INIT, /* replay == during sending of selected replay buffer */ AGT_NOT_STATE_REPLAY, /* timed == stopTime in future, after replayComplete * but before notificationComplete */ AGT_NOT_STATE_TIMED, /* live == live delivery mode (no stopTime given) */ AGT_NOT_STATE_LIVE, /* shutdown == forced shutdown mode; * notificationComplete is being sent */ AGT_NOT_STATE_SHUTDOWN } agt_not_state_t; /* server supported stream types */ typedef enum agt_not_stream_t_ { AGT_NOT_STREAM_NONE, AGT_NOT_STREAM_NETCONF } agt_not_stream_t; /* one notification message that will be sent to all * subscriptions and kept in the replay buffer (notificationQ) */ typedef struct agt_not_msg_t_ { dlq_hdr_t qhdr; obj_template_t *notobj; dlq_hdr_t payloadQ; /* Q of val_value_t */ uint32 msgid; xmlChar eventTime[TSTAMP_MIN_SIZE]; val_value_t *msg; /* /notification element */ val_value_t *event; /* ptr inside msg for filter */ } agt_not_msg_t; /* one subscription that will be kept in the subscriptionQ */ typedef struct agt_not_subscription_t_ { dlq_hdr_t qhdr; ses_cb_t *scb; /* back-ptr to session */ ses_id_t sid; /* used when scb deleted */ xmlChar *stream; agt_not_stream_t streamid; op_filtertyp_t filtertyp; val_value_t *filterval; val_value_t *selectval; xmlChar createTime[TSTAMP_MIN_SIZE]; xmlChar *startTime; /* converted to UTC */ xmlChar *stopTime; /* converted to UTC */ uint32 flags; agt_not_msg_t *firstreplaymsg; /* back-ptr; could be zapped */ agt_not_msg_t *lastreplaymsg; /* back-ptr; could be zapped */ agt_not_msg_t *lastmsg; /* back-ptr; could be zapped */ uint32 firstreplaymsgid; /* w/firstreplaymsg is deleted */ uint32 lastreplaymsgid; /* w/lastreplaymsg is deleted */ uint32 lastmsgid; /* w/ lastmsg is deleted */ agt_not_state_t state; } agt_not_subscription_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_not_init * * INIT 1: * Initialize the server notification module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_not_init (void); /******************************************************************** * FUNCTION agt_not_init2 * * INIT 2: * Initialize the monitoring data structures * This must be done after the config is loaded * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_not_init2 (void); /******************************************************************** * FUNCTION agt_not_cleanup * * Cleanup the module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void agt_not_cleanup (void); /******************************************************************** * FUNCTION agt_not_send_notifications * * Send out some notifications to the configured subscriptions * if needed. * * Simple design: * go through all the subscriptions and send at most one * notification to each one if needed. This will build * in some throttling based on the ncxserver select loop * timeout (or however this function is called). * * OUTPUTS: * notifications may be written to some active sessions * * RETURNS: * number of notifications sent; * used for simple burst throttling *********************************************************************/ extern uint32 agt_not_send_notifications (void); /******************************************************************** * FUNCTION agt_not_clean_eventlog * * Remove any delivered notifications when the replay buffer * size is set to zero * *********************************************************************/ extern void agt_not_clean_eventlog (void); /******************************************************************** * FUNCTION agt_not_remove_subscription * * Remove and expire a subscription with the specified session ID. * The session is being removed. * * INPUTS: * sid == session ID to use *********************************************************************/ extern void agt_not_remove_subscription (ses_id_t sid); /******************************************************************** * FUNCTION agt_not_new_notification * * Malloc and initialize the fields in an agt_not_msg_t * * INPUTS: * eventType == object template of the event type * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern agt_not_msg_t * agt_not_new_notification (obj_template_t *eventType); /******************************************************************** * FUNCTION agt_not_free_notification * * Scrub the memory in an agt_not_template_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * notif == agt_not_template_t to delete *********************************************************************/ extern void agt_not_free_notification (agt_not_msg_t *notif); /******************************************************************** * FUNCTION agt_not_add_to_payload * * Queue the specified value node into the payloadQ * for the specified notification * * INPUTS: * notif == notification to send * val == value to add to payloadQ * !!! THIS IS LIVE MALLOCED MEMORY PASSED OFF * !!! TO THIS FUNCTION. IT WILL BE FREED LATER * !!! DO NOT CALL val_free_value * !!! AFTER THIS CALL * * OUTPUTS: * val added to the notif->payloadQ * *********************************************************************/ extern void agt_not_add_to_payload (agt_not_msg_t *notif, val_value_t *val); /******************************************************************** * FUNCTION agt_not_queue_notification * * Queue the specified notification in the replay log. * It will be sent to all the active subscriptions * as needed. * * INPUTS: * notif == notification to send * !!! THIS IS LIVE MALLOCED MEMORY PASSED OFF * !!! TO THIS FUNCTION. IT WILL BE FREED LATER * !!! DO NOT CALL agt_not_free_notification * !!! AFTER THIS CALL * * OUTPUTS: * message added to the notificationQ * *********************************************************************/ extern void agt_not_queue_notification (agt_not_msg_t *notif); /******************************************************************** * FUNCTION agt_not_is_replay_event * * Check if the specified notfication is the replayComplete * or notificationComplete notification events * * INPUTS: * notifobj == notification object to check * * RETURNS: * TRUE if the notification object is one of the special * replay buffer events * FALSE otherwise *********************************************************************/ extern boolean agt_not_is_replay_event (const obj_template_t *notifobj); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_not */ yuma123_2.14/netconf/src/agt/agt_hello.c0000664000175000017500000003057714770023131020237 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_hello.c Handle the NETCONF (top-level) element. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 15jan07 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cap.h" #include "agt_hello.h" #include "agt_ses.h" #include "agt_util.h" #include "agt_val_parse.h" #include "cap.h" #include "log.h" #include "ncx.h" #include "obj.h" #include "op.h" #include "ses.h" #include "status.h" #include "top.h" #include "val.h" #include "xml_wr.h" #include "uptime.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define CLIENT_HELLO_CON ((const xmlChar *)"client-hello") /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_hello_init_done = FALSE; static ses_total_stats_t *mytotals; /******************************************************************** * FUNCTION check_manager_hello * * Verify that the same NETCONF protocol verion is supported * by the manager and this server * * INPUTS: * scb == session control block * val == value struct for the hello message to check * * RETURNS: * status *********************************************************************/ static status_t check_manager_hello (ses_cb_t *scb, val_value_t *val) { val_value_t *caps, *cap; /* look for the NETCONF base capability string */ caps = val_find_child(val, NC_MODULE, NCX_EL_CAPABILITIES); if (caps && caps->res == NO_ERR) { if (ncx_protocol_enabled(NCX_PROTO_NETCONF11)) { for (cap = val_find_child(caps, NC_MODULE, NCX_EL_CAPABILITY); cap != NULL; cap = val_find_next_child(caps, NC_MODULE, NCX_EL_CAPABILITY, cap)) { if (cap->res == NO_ERR) { if (!xml_strcmp(VAL_STR(cap), CAP_BASE_URN11)) { if (LOGDEBUG3) { log_debug3("\nagt_hello: set " "protocol to base:1.1"); } ses_set_protocol(scb, NCX_PROTO_NETCONF11); return NO_ERR; } } } } if (ncx_protocol_enabled(NCX_PROTO_NETCONF10)) { for (cap = val_find_child(caps, NC_MODULE, NCX_EL_CAPABILITY); cap != NULL; cap = val_find_next_child(caps, NC_MODULE, NCX_EL_CAPABILITY, cap)) { if (cap->res == NO_ERR) { if (!xml_strcmp(VAL_STR(cap), CAP_BASE_URN)) { if (LOGDEBUG3) { log_debug3("\nagt_hello: set " "protocol to base:1.0"); } ses_set_protocol(scb, NCX_PROTO_NETCONF10); return NO_ERR; } } } } } log_info("\nagt_hello: no NETCONF base:1.0 or base:1.1 URI found"); return ERR_NCX_MISSING_VAL_INST; } /* check_manager_hello */ /******************************************************************** * FUNCTION agt_hello_init * * Initialize the agt_hello module * Adds the agt_hello_dispatch function as the handler * for the NETCONF top-level element. * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ status_t agt_hello_init (void) { status_t res; if (!agt_hello_init_done) { res = top_register_node(NC_MODULE, NCX_EL_HELLO, agt_hello_dispatch); if (res != NO_ERR) { return res; } mytotals = ses_get_total_stats(); agt_hello_init_done = TRUE; } return NO_ERR; } /* agt_hello_init */ /******************************************************************** * FUNCTION agt_hello_cleanup * * Cleanup the agt_hello module. * Unregister the top-level NETCONF element * *********************************************************************/ void agt_hello_cleanup (void) { if (agt_hello_init_done) { top_unregister_node(NC_MODULE, NCX_EL_HELLO); agt_hello_init_done = FALSE; } } /* agt_hello_cleanup */ /******************************************************************** * FUNCTION agt_hello_dispatch * * Handle an incoming message from the client * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ void agt_hello_dispatch (ses_cb_t *scb, xml_node_t *top) { val_value_t *val; ncx_module_t *mod; obj_template_t *obj; xml_msg_hdr_t msg; status_t res; #ifdef DEBUG if (!scb || !top) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (LOGDEBUG2) { log_debug2("\nagt_hello got node"); if (LOGDEBUG3) { xml_dump_node(top); } } /* only process this message in hello wait state */ if (scb->state != SES_ST_HELLO_WAIT) { if (LOGINFO) { log_info("\nagt_hello dropped, wrong state " "(%d) for session %d", scb->state, scb->sid); } mytotals->inBadHellos++; mytotals->droppedSessions++; agt_ses_request_close(scb, scb->sid, SES_TR_BAD_HELLO); return; } /* init local vars */ res = NO_ERR; obj = NULL; xml_msg_init_hdr(&msg); /* get a value struct to hold the client hello msg */ val = val_new_value(); if (!val) { res = ERR_INTERNAL_MEM; } /* get the type definition from the registry */ if (res == NO_ERR) { obj = NULL; mod = ncx_find_module(NC_MODULE, NULL); if (mod) { obj = ncx_find_object(mod, CLIENT_HELLO_CON); } if (!obj) { /* netconf module should have loaded this definition */ res = SET_ERROR(ERR_INTERNAL_PTR); } } /* parse a manager hello message */ if (res == NO_ERR) { res = agt_val_parse_nc(scb, &msg, obj, top, NCX_DC_STATE, val); } /* check that the NETCONF base capability is included * and it matches the server protocol version */ if (res == NO_ERR) { res = check_manager_hello(scb, val); } /* report first error and close session */ if (res != NO_ERR) { if (LOGINFO) { log_info("\nagt_connect error " "(%s), dropping session %d", get_error_string(res), scb->sid); } mytotals->inBadHellos++; mytotals->droppedSessions++; agt_ses_request_close(scb, scb->sid, SES_TR_BAD_HELLO); } else { scb->state = SES_ST_IDLE; scb->active = TRUE; /* start the timer for the first rpc request */ (void)uptime(&scb->last_rpc_time); if (LOGINFO) { log_info("\nSession %d for %s@%s now active", scb->sid, scb->username, scb->peeraddr); if (ses_get_protocol(scb) == NCX_PROTO_NETCONF11) { log_info(" (base:1.1)"); } else { log_info(" (base:1.0)"); } } } if (val) { val_free_value(val); } } /* agt_hello_dispatch */ /******************************************************************** * FUNCTION agt_hello_send * * Send the server message to the manager on the * specified session * * INPUTS: * scb == session control block * * RETURNS: * status *********************************************************************/ status_t agt_hello_send (ses_cb_t *scb) { val_value_t *mycaps; xml_msg_hdr_t msg; status_t res; xml_attrs_t attrs; boolean anyout; xmlns_id_t nc_id; xmlChar numbuff[NCX_MAX_NUMLEN]; #ifdef DEBUG if (!scb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; anyout = FALSE; xml_msg_init_hdr(&msg); xml_init_attrs(&attrs); nc_id = xmlns_nc_id(); /* start the hello timeout */ (void)uptime(&scb->hello_time); /* get the server caps */ mycaps = agt_cap_get_capsval(); if (!mycaps) { res = SET_ERROR(ERR_INTERNAL_PTR); } /* setup the prefix map with the NETCONF and NCX namepsaces */ if (res == NO_ERR) { res = xml_msg_build_prefix_map(&msg, &attrs, TRUE, FALSE); } /* send the directive */ if (res == NO_ERR) { res = ses_start_msg(scb); } /* start the hello element */ if (res == NO_ERR) { anyout = TRUE; xml_wr_begin_elem_ex(scb, &msg, 0, nc_id, NCX_EL_HELLO, &attrs, TRUE, 0, FALSE); } /* send the capabilities list */ if (res == NO_ERR) { xml_wr_full_val(scb, &msg, mycaps, ses_indent_count(scb)); } /* send the session ID */ if (res == NO_ERR) { xml_wr_begin_elem(scb, &msg, nc_id, nc_id, NCX_EL_SESSION_ID, ses_indent_count(scb)); } if (res == NO_ERR) { snprintf((char *)numbuff, sizeof(numbuff), "%d", scb->sid); ses_putstr(scb, numbuff); } if (res == NO_ERR) { xml_wr_end_elem(scb, &msg, nc_id, NCX_EL_SESSION_ID, -1); } /* finish the hello element */ if (res == NO_ERR) { xml_wr_end_elem(scb, &msg, nc_id, NCX_EL_HELLO, 0); } /* finish the message */ if (anyout) { ses_finish_msg(scb); } xml_clean_attrs(&attrs); xml_msg_clean_hdr(&msg); return res; } /* agt_hello_send */ /* END file agt_hello.c */ yuma123_2.14/netconf/src/agt/agt_val.c0000664000175000017500000063540014770023131017712 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_val.c Manage Server callbacks for YANG-based config manipulation ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 20may06 abb begun 03dec11 abb convert tree traversal validation to optimized validation task list ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_acm.h" #include "agt_cap.h" #include "agt_cb.h" #include "agt_cfg.h" #include "agt_commit_complete.h" #include "agt_ncx.h" #include "agt_util.h" #include "agt_val.h" #include "agt_val_parse.h" #include "cap.h" #include "cfg.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "ncxconst.h" #include "obj.h" #include "op.h" #include "plock.h" #include "rpc.h" #include "rpc_err.h" #include "status.h" #include "typ.h" #include "tstamp.h" #include "val.h" #include "val123.h" #include "val_util.h" #include "xmlns.h" #include "xpath.h" #include "xpath_yang.h" #include "xpath1.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* recursive callback function forward decls */ static status_t invoke_btype_cb (agt_cbtyp_t cbtyp, op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *newval, val_value_t *curval, val_value_t *curparent ); static status_t handle_user_callback (agt_cbtyp_t cbtyp, op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, val_value_t *newnode, val_value_t *curnode, boolean lookparent, boolean indelete); static status_t apply_commit_deletes (ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *candval, val_value_t *runval); /******************************************************************** * * * T Y P E S * * * *********************************************************************/ typedef struct unique_set_t_ { dlq_hdr_t qhdr; dlq_hdr_t uniqueQ; /* Q of val_unique_t */ val_value_t *valnode; /* value tree back-ptr */ } unique_set_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION cvt_editop * Determine the effective edit (if OP_EDITOP_COMMIT) * * \param editop the edit operation to check * \param newnode the newval * \param curnode the curval * * \return the converted editoperation *********************************************************************/ static op_editop_t cvt_editop( op_editop_t editop, const val_value_t *newval, const val_value_t *curval) { op_editop_t retop = editop; if (editop == OP_EDITOP_COMMIT) { if (newval && curval) { /*********************************** * keep this simple so SIL code does not need to * worry about with-defaults basic-mode in use if (val_set_by_default(newval)) { retop = OP_EDITOP_DELETE; } else if (val_set_by_default(curval)) { retop = OP_EDITOP_CREATE; } else { retop = OP_EDITOP_REPLACE; } ***************************************/ retop = OP_EDITOP_REPLACE; } else if (newval && !curval) { retop = OP_EDITOP_CREATE; } else if (!newval && curval) { retop = OP_EDITOP_DELETE; } else { retop = OP_EDITOP_REPLACE; // error: both NULL!! } } return retop; } /* cvt_editop */ /******************************************************************** * FUNCTION skip_obj_commit_test * * Check if the commit tests should be skipped for this object * These tests on the object tree are more strict than the data tree * because the data tree will never contain instances of obsolete, * non-database, or disabled objects * * INPUTS: * obj ==obj object to check * * RETURNS: * TRUE if object should be skipped * FALSE if object should not be skipped *********************************************************************/ static boolean skip_obj_commit_test(obj_template_t *obj) { if (obj_is_cli(obj) || !obj_has_name(obj) || !obj_is_config(obj) || (obj_is_abstract(obj) && !obj_is_root(obj)) || /* removed because ncx_delete_all_obsolete_objects() has * already been called * obj_get_status(obj) == NCX_STATUS_OBSOLETE || */ !obj_is_enabled(obj)) { return TRUE; } return FALSE; } /* skip_obj_commit_test */ /******************************************************************** * FUNCTION clean_node * * Clean the value node and its ancestors from editvars and flags * * INPUTS: * val == node to check * *********************************************************************/ static void clean_node (val_value_t *val) { if (val == NULL) { return; } if (obj_is_root(val->obj)) { return; } if (val->parent) { clean_node(val->parent); } val_free_editvars(val); } /* clean_node */ /******************************************************************** * Create and store a change-audit record, if needed * this function generates a log message if log level * set to LOG_INFO or higher; An audit record for a sysConfigChange * event is also generated * * \note !!! ONLY CALLED FOR RUNNING CONFIG !!!! * * \param editop edit operation requested * \param scb session control block * \param msg RPC message in progress * \param newnode top new value node involved in edit * \param curnode top cur value node involved in edit *********************************************************************/ static void handle_audit_record (op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, val_value_t *newnode, val_value_t *curnode) { if (editop == OP_EDITOP_LOAD) { return; } xmlChar *ibuff = NULL; const xmlChar *name = NULL; xmlChar tbuff[TSTAMP_MIN_SIZE+1]; ncx_cfg_t cfgid; cfg_transaction_id_t txid; ses_id_t sid = (scb) ? SES_MY_SID(scb) : 0; const xmlChar *username = (scb && scb->username) ? scb->username : (const xmlChar *)"nobody"; const xmlChar *peeraddr = (scb && scb->peeraddr) ? scb->peeraddr : (const xmlChar *)"localhost"; editop = cvt_editop(editop, newnode, curnode); if (msg && msg->rpc_txcb) { cfgid = msg->rpc_txcb->cfg_id; name = cfg_get_config_name(cfgid); txid = msg->rpc_txcb->txid; } else { cfgid = NCX_CFGID_RUNNING; name = NCX_EL_RUNNING; txid = 0; } /* generate a log record */ tstamp_datetime(tbuff); val_value_t *usenode = newnode ? newnode : curnode; if (usenode) { status_t res = val_gen_instance_id(NULL, usenode, NCX_IFMT_XPATH1, &ibuff); if (res != NO_ERR) { log_error("\nError: Generate instance id failed (%s)", get_error_string(res)); } } log_info( "\nedit-transaction %llu: operation %s on session %d by %s@%s" "\n at %s on target '%s'\n data: %s\n", (unsigned long long)txid, op_editop_name(editop), sid, username, peeraddr, tbuff, name, (ibuff) ? (const char *)ibuff : "??" ); if (cfgid == NCX_CFGID_RUNNING && log_audit_is_open()) { log_audit_write( "\nedit-transaction %llu: operation %s on " "session %d by %s@%s\n at %s on target 'running'" "\n data: %s\n", (unsigned long long)txid, op_editop_name(editop), sid, username, peeraddr, tbuff, (ibuff) ? (const char *)ibuff : "??"); } /* generate a sysConfigChange notification */ if ( cfgid == NCX_CFGID_RUNNING && msg && msg->rpc_txcb && ibuff ) { agt_cfg_audit_rec_t *auditrec = agt_cfg_new_auditrec(ibuff, editop); if ( !auditrec ) { log_error("\nError: malloc failed for audit record"); } else { dlq_enque(auditrec, &msg->rpc_txcb->auditQ); } } if (ibuff) { m__free(ibuff); } } /* handle_audit_record */ /******************************************************************** * FUNCTION handle_subtree_node_callback * * Compare current node if config * find the nodes that changed and call the * relevant user callback functions * * INPUTS: * scb == session control block invoking the callback * msg == RPC message in progress * cbtyp == agent callback type * editop == edit operation applied to newnode oand/or curnode * newnode == new node in operation * curnode == current node in operation * * RETURNS: * status of the operation (usually returned from the callback) * NO USER CALLBACK FOUND == NO_ERR *********************************************************************/ static status_t handle_subtree_node_callback (agt_cbtyp_t cbtyp, op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, val_value_t *newnode, val_value_t *curnode) { const xmlChar *name; status_t res; if (newnode != NULL) { name = newnode->name; } else if (curnode != NULL) { name = curnode->name; } else { name = NULL; } if (newnode && curnode) { int retval; if (newnode->parent && newnode->parent->editop == OP_EDITOP_REPLACE && newnode->parent->editvars && newnode->parent->editvars->operset) { /* replace parent was explicitly requested and this * function would not have been called unless something * in the replaced node changed. * Invoke the SIL callbacks for the child nodes * even if oldval == newval; * * Operation must be explicitly set w/ operation="replace" * attribute because will also * appear to be a replace operation and the unchanged * subtrees must be skipped in this mode */ retval = -1; } else { retval = val_compare_ex(newnode, curnode, TRUE); if (retval == 0) { /* apply here but nothing to do, * so skip this entire subtree */ if (LOGDEBUG) { log_debug("\nagt_val: Skipping subtree node " "'%s', no changes", (name) ? name : (const xmlChar *)"--"); } } } if (retval == 0) { return NO_ERR; } } res = handle_user_callback(cbtyp, editop, scb, msg, newnode, curnode, FALSE, (editop == OP_EDITOP_DELETE) ? TRUE : FALSE); return res; } /* handle_subtree_node_callback */ /******************************************************************** * FUNCTION handle_subtree_callback * * Search the subtrees and find the correct user callback * functions and invoke them * * INPUTS: * cbtyp == agent callback type * editop == edit operation applied to newnode oand/or curnode * scb == session control block invoking the callback * msg == RPC message in progress * newnode == new node in operation * curnode == current node in operation * * RETURNS: * status of the operation (usually returned from the callback) * NO USER CALLBACK FOUND == NO_ERR *********************************************************************/ static status_t handle_subtree_callback (agt_cbtyp_t cbtyp, op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, val_value_t *newnode, val_value_t *curnode) { val_value_t *newchild, *curchild; status_t res = NO_ERR; if (newnode == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } for (newchild = val_get_first_child(newnode); newchild != NULL; newchild = val_get_next_child(newchild)) { if (!obj_is_config(newchild->obj)) { continue; } if (curnode != NULL) { curchild = val_first_child_match(curnode, newchild); } else { curchild = NULL; } res = handle_subtree_node_callback(cbtyp, editop, scb, msg, newchild, curchild); if (res != NO_ERR) { return res; } } if (curnode == NULL) { return NO_ERR; } for (curchild = val_get_first_child(curnode); curchild != NULL; curchild = val_get_next_child(curchild)) { if (!obj_is_config(curchild->obj)) { continue; } newchild = val_first_child_match(newnode, curchild); if (newchild == NULL) { res = handle_subtree_node_callback(cbtyp, OP_EDITOP_DELETE, scb, msg, NULL, curchild); if (res != NO_ERR) { return res; } } } return res; } /* handle_subtree_callback */ /******************************************************************** * FUNCTION delete_children_first * * Doing a delete operation and checking for the * sil-delete-children-first flag. Just calling the * SIL callback functions, not actually removing the * child nodes in case a rollback is needed * * Calls recursively for all child nodes where sil-delete-children-first * is set, then invokes the SIL callback for the 'curnode' last * * INPUTS: * cbtyp == agent callback type * scb == session control block invoking the callback * msg == RPC message in progress * curnode == current node in operation * * RETURNS: * status of the operation (usually returned from the callback) * NO USER CALLBACK FOUND == NO_ERR *********************************************************************/ static status_t delete_children_first (agt_cbtyp_t cbtyp, ses_cb_t *scb, rpc_msg_t *msg, val_value_t *curnode) { val_value_t *curchild; status_t res = NO_ERR; if (curnode == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } if (LOGDEBUG2) { log_debug2("\nEnter delete_children_first for '%s'", curnode->name); } for (curchild = val_get_first_child(curnode); curchild != NULL; curchild = val_get_next_child(curchild)) { if (!obj_is_config(curchild->obj)) { continue; } if (obj_is_leafy(curchild->obj)) { continue; } if (obj_is_sil_delete_children_first(curchild->obj)) { res = delete_children_first(cbtyp, scb, msg, curchild); } else { res = handle_subtree_node_callback(cbtyp, OP_EDITOP_DELETE, scb, msg, NULL, curchild); } if (res != NO_ERR) { return res; } } if (!obj_is_leafy(curnode->obj)) { res = handle_subtree_node_callback(cbtyp, OP_EDITOP_DELETE, scb, msg, NULL, curnode); } return res; } /* delete_children_first */ /******************************************************************** * FUNCTION handle_user_callback * * Find the correct user callback function and invoke it * * INPUTS: * cbtyp == agent callback type * editop == edit operation applied to newnode oand/or curnode * scb == session control block invoking the callback * !!! This parm should not be NULL because only the * !!! agt_val_root_check in agt.c calls with scb==NULL and * !!! that never invokes any SIL callbacks * msg == RPC message in progress * newnode == new node in operation * curnode == current node in operation * lookparent == TRUE if the parent should be checked for * a callback function; FALSE otherwise * indelete == TRUE if in middle of a sil-delete-children-first * callback; FALSE if not * RETURNS: * status of the operation (usually returned from the callback) * NO USER CALLBACK FOUND == NO_ERR *********************************************************************/ static status_t handle_user_callback (agt_cbtyp_t cbtyp, op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, val_value_t *newnode, val_value_t *curnode, boolean lookparent, boolean indelete) { agt_cb_fnset_t *cbset; val_value_t *val; status_t res; boolean done; if (newnode) { val = newnode; } else if (curnode) { val = curnode; } else { return SET_ERROR(ERR_INTERNAL_VAL); } if (obj_is_root(val->obj)) { return NO_ERR; } if (LOGDEBUG4) { log_debug4("\nChecking for %s user callback for %s edit on %s:%s", agt_cbtype_name(cbtyp), op_editop_name(editop), val_get_mod_name(val), val->name); } /* no difference during apply stage, and validate * stage checks differences before callback invoked * so treat remove the same as a delete for user callback purposes */ if (editop == OP_EDITOP_REMOVE) { editop = OP_EDITOP_DELETE; } /* check if this is a child-first delete * the child nodes may have SIL callbacks and not the * parent, so the recursion is done before the * SIL callback is found for the current node * Only call during COMMIT phase */ if (obj_is_sil_delete_children_first(val->obj) && !obj_is_leafy(val->obj) && !indelete && editop == OP_EDITOP_DELETE && cbtyp == AGT_CB_COMMIT) { res = delete_children_first(cbtyp, scb, msg, curnode); return res; } /* find the right callback function to call */ done = FALSE; while (!done) { agt_cb_fnset_node_t *cbset_node; unsigned int cbfn_calls=0; for(cbset_node=val->obj?(agt_cb_fnset_node_t *)dlq_firstEntry(&val->obj->cbsetQ):NULL; cbset_node!=NULL; cbset_node=(agt_cb_fnset_node_t *)dlq_nextEntry(cbset_node)) { cbset = cbset_node->fnset_ptr; if(cbset->cbfn[cbtyp]==NULL) { continue; } else { cbfn_calls++; } if (LOGDEBUG2) { log_debug2("\nFound %s user callback for %s:%s", agt_cbtype_name(cbtyp), op_editop_name(editop), val_get_mod_name(val), val->name); } editop = cvt_editop(editop, newnode, curnode); res = (*cbset->cbfn[cbtyp])(scb, msg, cbtyp, editop, newnode, curnode); if(res != NO_ERR) { if (LOGDEBUG) { log_debug("\n%s user callback failed (%s) for %s on %s:%s", agt_cbtype_name(cbtyp), get_error_string(res), op_editop_name(editop), val_get_mod_name(val), val->name); } return res; } } if(cbfn_calls>0) { if (res == NO_ERR) { val->res = NO_ERR; switch (editop) { case OP_EDITOP_CREATE: case OP_EDITOP_MERGE: case OP_EDITOP_REPLACE: case OP_EDITOP_LOAD: if (cbtyp != AGT_CB_VALIDATE) { res = handle_subtree_callback(cbtyp, editop, scb, msg, newnode, curnode); } break; default: ; } } return res; } else if (!lookparent) { done = TRUE; } else if (val->parent != NULL && !obj_is_root(val->parent->obj) && val_get_nsid(val) == val_get_nsid(val->parent)) { val = val->parent; } else { done = TRUE; } } return NO_ERR; } /* handle_user_callback */ /******************************************************************** * FUNCTION add_undo_node * * Add an undo node to the msg->undoQ * * INPUTS: * msg == RPC message in progress * editop == edit-config operation attribute value * newnode == node from PDU * newnode_marker == newnode placeholder * curnode == node from database (if any) * curnode_marker == curnode placeholder * parentnode == parent of curnode (or would be) * OUTPUTS: * agt_cfg_undo_rec_t struct added to msg->undoQ * * RETURNS: * pointer to new undo record, in case any extra_deleteQ * items need to be added; NULL on ERR_INTERNAL_MEM error *********************************************************************/ static agt_cfg_undo_rec_t * add_undo_node (rpc_msg_t *msg, op_editop_t editop, val_value_t *newnode, val_value_t *newnode_marker, val_value_t *curnode, val_value_t *curnode_marker, val_value_t *parentnode) { agt_cfg_undo_rec_t *undo; /* create an undo record for this merge */ undo = agt_cfg_new_undorec(); if (!undo) { return NULL; } /* save a copy of the current value in case it gets modified * in a merge operation;; done for leafs only!!! */ if (curnode && obj_is_leafy(curnode->obj)) { undo->curnode_clone = val_clone(curnode); if (!undo->curnode_clone) { agt_cfg_free_undorec(undo); return NULL; } } undo->editop = cvt_editop(editop, newnode, curnode); undo->newnode = newnode; undo->newnode_marker = newnode_marker; undo->curnode = curnode; undo->curnode_marker = curnode_marker; undo->parentnode = parentnode; dlq_enque(undo, &msg->rpc_txcb->undoQ); return undo; } /* add_undo_node */ /******************************************************************** * FUNCTION add_child_clean * * Add a child value node to a parent value node * This is only called by the agent when adding nodes * to a target database. * * Pass in the editvar to use * * If the child node being added is part of a choice/case, * then all sibling nodes in other cases within the same * choice will be deleted * * The insert operation will also be check to see * if the child is a list oo a leaf-list, which is ordered-by user * * The default insert mode is always 'last' * * INPUTS: * editvars == val_editvars_t struct to use (may be NULL) * child == node to store in the parent * parent == complex value node with a childQ * cleanQ == address of Q to receive any agt_cfg_nodeptr_t * structs for nodes marked as deleted * * OUTPUTS: * cleanQ may have nodes added if the child being added * is part of a case. All other cases will be marked as deleted * from the parent Q and nodeptrs added to the cleanQ * RETURNS: * status *********************************************************************/ static status_t add_child_clean (val_editvars_t *editvars, val_value_t *child, val_value_t *parent, dlq_hdr_t *cleanQ) { val_value_t *testval, *nextval; agt_cfg_nodeptr_t *nodeptr; if (child->casobj) { for (testval = val_get_first_child(parent); testval != NULL; testval = nextval) { nextval = val_get_next_child(testval); if (testval->casobj && (testval->casobj->parent == child->casobj->parent)) { if (testval->casobj != child->casobj) { log_debug3("\nagt_val: clean old case member '%s'" " from parent '%s'", testval->name, parent->name); nodeptr = agt_cfg_new_nodeptr(testval); if (nodeptr == NULL) { return ERR_INTERNAL_MEM; } dlq_enque(nodeptr, cleanQ); VAL_MARK_DELETED(testval); } } } } child->parent = parent; boolean doins = FALSE; if (child->obj->objtype == OBJ_TYP_LIST) { doins = TRUE; } else if (child->obj->objtype == OBJ_TYP_LEAF_LIST) { doins = TRUE; } op_insertop_t insertop = OP_INSOP_NONE; if (editvars) { insertop = editvars->insertop; } if (doins) { switch (insertop) { case OP_INSOP_FIRST: testval = val_find_child(parent, val_get_mod_name(child), child->name); if (testval) { dlq_insertAhead(child, testval); } else { val_add_child_sorted(child, parent); } break; case OP_INSOP_LAST: case OP_INSOP_NONE: val_add_child_sorted(child, parent); break; case OP_INSOP_BEFORE: case OP_INSOP_AFTER: /* find the entry specified by the val->insertstr value * this is value='foo' for leaf-list and * key="[x:foo='bar'][x:foo2=7]" for list */ if (child->obj->objtype == OBJ_TYP_LEAF_LIST || child->obj->objtype == OBJ_TYP_LIST) { if (editvars && editvars->insertval) { testval = editvars->insertval; if (editvars->insertop == OP_INSOP_BEFORE) { dlq_insertAhead(child, testval); } else { dlq_insertAfter(child, testval); } } else { SET_ERROR(ERR_NCX_INSERT_MISSING_INSTANCE); val_add_child_sorted(child, parent); } } else { /* wrong object type */ SET_ERROR(ERR_INTERNAL_VAL); val_add_child_sorted(child, parent); } break; default: SET_ERROR(ERR_INTERNAL_VAL); val_add_child_sorted(child, parent); } } else { val_add_child_sorted(child, parent); } return NO_ERR; } /* add_child_clean */ /******************************************************************** * FUNCTION add_child_node * * Add a child node * * INPUTS: * child == child value to add * marker == newval marker for this child node (if needed_ * !! consuming this parm here so caller can assume * !! memory is transferred even if error returned * parent == parent value to add child to * msg == RPC messing to use * editop == edit operation to save in undo rec * * OUTPUTS: * child added to parent->v.childQ * any other cases removed and either added to undo node or deleted * * RETURNS: * status *********************************************************************/ static status_t add_child_node (val_value_t *child, val_value_t *marker, val_value_t *parent, rpc_msg_t *msg, op_editop_t editop) { if (LOGDEBUG3) { log_debug3("\nAdd child '%s' to parent '%s'", child->name, parent->name); } agt_cfg_undo_rec_t *undo = add_undo_node(msg, editop, child, marker, NULL, NULL, parent); if (undo == NULL) { val_free_value(marker); return ERR_INTERNAL_MEM; } undo->apply_res = add_child_clean(child->editvars, child, parent, &undo->extra_deleteQ); undo->edit_action = AGT_CFG_EDIT_ACTION_ADD; return undo->apply_res; } /* add_child_node */ /******************************************************************** * FUNCTION move_child_node * * Move a child list or leaf-list node * * INPUTS: * newchild == new child value to add * newchild_marker == new child marker for undo rec if needed * !! consuming this parm here so caller can assume * !! memory is transferred even if error returned * curchild == existing child value to move * parent == parent value to move child within * msg == message in progress * editop == edit operation to save in undo rec * * OUTPUTS: * child added to parent->v.childQ * any other cases removed and either added to undo node or deleted * * RETURNS: * status *********************************************************************/ static status_t move_child_node (val_value_t *newchild, val_value_t *newchild_marker, val_value_t *curchild, val_value_t *parent, rpc_msg_t *msg, op_editop_t editop) { if (LOGDEBUG3) { log_debug3("\nMove child '%s' in parent '%s'", newchild->name, parent->name); } agt_cfg_undo_rec_t *undo = add_undo_node(msg, editop, newchild, newchild_marker, curchild, NULL, parent); if (undo == NULL) { /* no detailed rpc-error is recorded here!!! * relying on catch-all operation-failed in agt_rpc_dispatch */ val_free_value(newchild_marker); return ERR_INTERNAL_MEM; } VAL_MARK_DELETED(curchild); undo->apply_res = add_child_clean(newchild->editvars, newchild, parent, &undo->extra_deleteQ); undo->edit_action = AGT_CFG_EDIT_ACTION_MOVE; return undo->apply_res; } /* move_child_node */ /******************************************************************** * FUNCTION move_mergedlist_node * * Move a list node that was just merged with * the contents of the newval; now the curval needs * to be moved * * INPUTS: * newchild == new child value with the editvars * to use to move curchild * curchild == existing child value to move * parent == parent value to move child within * undo == undo record in progress * * OUTPUTS: * child added to parent->v.childQ * any other cases removed and either added to undo node or deleted * * RETURNS: * status *********************************************************************/ static status_t move_mergedlist_node (val_value_t *newchild, val_value_t *curchild, val_value_t *parent, agt_cfg_undo_rec_t *undo) { if (LOGDEBUG3) { log_debug3("\nMove list '%s' in parent '%s'", newchild->name, parent->name); } val_value_t *marker = val_new_deleted_value(); if (marker == NULL) { return ERR_INTERNAL_MEM; } undo->curnode_marker = marker; val_swap_child(marker, curchild); undo->apply_res = add_child_clean(newchild->editvars, curchild, parent, &undo->extra_deleteQ); undo->edit_action = AGT_CFG_EDIT_ACTION_MOVE; return undo->apply_res; } /* move_mergedlist_node */ /******************************************************************** * FUNCTION restore_newnode * * Swap the newnode and newnode_pointers * * INPUTS: * undo == undo record to use *********************************************************************/ static void restore_newnode (agt_cfg_undo_rec_t *undo) { if (undo->newnode_marker) { if (undo->newnode) { val_swap_child(undo->newnode, undo->newnode_marker); } else { SET_ERROR(ERR_INTERNAL_VAL); } val_free_value(undo->newnode_marker); undo->newnode_marker = NULL; } } /* restore_newnode */ /******************************************************************** * FUNCTION restore_newnode2 * * Swap the newnode and newnode_pointers * There was no undo record created * * INPUTS: * newval == new value currently not in any tree * newval_marker == marker for newval in source tree *********************************************************************/ static void restore_newnode2 (val_value_t *newval, val_value_t *newval_marker) { if (newval && newval_marker) { val_swap_child(newval, newval_marker); } val_free_value(newval_marker); } /* restore_newnode2 */ /******************************************************************** * FUNCTION restore_curnode * * Restore the curnode * * INPUTS: * undo == undo record to use * target == target database being edited *********************************************************************/ static void restore_curnode (agt_cfg_undo_rec_t *undo, cfg_template_t *target) { if (undo->curnode == NULL) { return; } if (VAL_IS_DELETED(undo->curnode)) { /* curnode is a complex type that needs to be undeleted */ VAL_UNMARK_DELETED(undo->curnode); undo->free_curnode = FALSE; if (undo->commit_res != ERR_NCX_SKIPPED && target->cfg_id == NCX_CFGID_RUNNING) { val_check_swap_resnode(undo->newnode, undo->curnode); } } else if (undo->curnode_clone) { /* node is a leaf or leaf-list that was merged */ if (undo->commit_res != ERR_NCX_SKIPPED && target->cfg_id == NCX_CFGID_RUNNING) { val_check_swap_resnode(undo->newnode, undo->curnode_clone); } if (undo->curnode_marker) { /* the curnode was moved so move it back */ val_remove_child(undo->curnode); val_swap_child(undo->curnode_clone, undo->curnode_marker); val_free_value(undo->curnode_marker); undo->curnode_marker = NULL; } else { val_swap_child(undo->curnode_clone, undo->curnode); } undo->curnode_clone = NULL; undo->free_curnode = TRUE; } else { // should not happen SET_ERROR(ERR_INTERNAL_VAL); } } /* restore_curnode */ /******************************************************************** * FUNCTION restore_extra_deletes * * Restore any extra deleted nodes from other YANG cases * * INPUTS: * nodeptrQ == Q of agt_cfg_nodeptr_t records to use *********************************************************************/ static void restore_extra_deletes (dlq_hdr_t *nodeptrQ) { while (!dlq_empty(nodeptrQ)) { agt_cfg_nodeptr_t *nodeptr = (agt_cfg_nodeptr_t *)dlq_deque(nodeptrQ); if (nodeptr && nodeptr->node) { VAL_UNMARK_DELETED(nodeptr->node); } else { SET_ERROR(ERR_INTERNAL_VAL); } agt_cfg_free_nodeptr(nodeptr); } } /* restore_extra_deletes */ /******************************************************************** * FUNCTION finish_extra_deletes * * Finish deleting any extra deleted nodes from other YANG cases * * INPUTS: * cfgid == internal config ID of the target database * undo == undo record to use *********************************************************************/ static void finish_extra_deletes (ncx_cfg_t cfgid, agt_cfg_undo_rec_t *undo) { while (!dlq_empty(&undo->extra_deleteQ)) { agt_cfg_nodeptr_t *nodeptr = (agt_cfg_nodeptr_t *) dlq_deque(&undo->extra_deleteQ); if (nodeptr && nodeptr->node) { /* mark ancestor nodes dirty before deleting this node */ if (cfgid == NCX_CFGID_RUNNING) { val_clear_dirty_flag(nodeptr->node); } else { val_set_dirty_flag(nodeptr->node); } val_remove_child(nodeptr->node); val_free_value(nodeptr->node); } else { SET_ERROR(ERR_INTERNAL_VAL); } agt_cfg_free_nodeptr(nodeptr); } } /* finish_extra_deletes */ /******************************************************************** * FUNCTION check_insert_attr * * Check the YANG insert attribute * * INPUTS: * scb == session control block * msg == incoming rpc_msg_t in progress * newval == val_value_t from the PDU * curparent == current parent node for inserting children * * RETURNS: * status *********************************************************************/ static status_t check_insert_attr (ses_cb_t *scb, rpc_msg_t *msg, val_value_t *newval, val_value_t *curparent) { val_value_t *testval, *simval, *insertval; status_t res = NO_ERR; op_editop_t editop = OP_EDITOP_NONE; if (newval == NULL) { return NO_ERR; // no insert attr possible } editop = newval->editop; if (editop == OP_EDITOP_DELETE || editop == OP_EDITOP_REMOVE) { /* this error already checked in agt_val_parse */ return NO_ERR; } if (newval->obj == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } switch (newval->obj->objtype) { case OBJ_TYP_LEAF_LIST: /* make sure the insert attr is on a node with a parent * this should always be true since the docroot would * be the parent of every accessible object instance * * OK to check insertstr, otherwise errors * should already be recorded by agt_val_parse */ if (newval->editvars == NULL || newval->editvars->insertop == OP_INSOP_NONE) { return NO_ERR; } if (!newval->editvars->insertstr) { /* insert op already checked in agt_val_parse */ return NO_ERR; } if (obj_is_system_ordered(newval->obj)) { res = ERR_NCX_UNEXPECTED_INSERT_ATTRS; agt_record_error(scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, newval->editvars->insertstr, NCX_NT_VAL, newval); return res; } /* validate the insert string against siblings * make a value node to compare in the * value space instead of the lexicographical space */ simval = val_make_simval_obj(newval->obj, newval->editvars->insertstr, &res); if (res == NO_ERR && simval) { testval = val_first_child_match(curparent, simval); if (!testval) { /* sibling leaf-list with the specified * value was not found (13.7) */ res = ERR_NCX_INSERT_MISSING_INSTANCE; } else { newval->editvars->insertval = testval; } } if (simval) { val_free_value(simval); } break; case OBJ_TYP_LIST: /* there should be a 'key' attribute * OK to check insertxpcb, otherwise errors * should already be recorded by agt_val_parse */ if (newval->editvars == NULL || newval->editvars->insertop == OP_INSOP_NONE) { return NO_ERR; } if (!newval->editvars->insertstr) { /* insert op already checked in agt_val_parse */ return NO_ERR; } if (!newval->editvars->insertxpcb) { /* insert op already checked in agt_val_parse */ return NO_ERR; } if (obj_is_system_ordered(newval->obj)) { res = ERR_NCX_UNEXPECTED_INSERT_ATTRS; agt_record_error(scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, newval->editvars->insertstr, NCX_NT_VAL, newval); return res; } if (newval->editvars->insertxpcb->validateres != NO_ERR) { res = newval->editvars->insertxpcb->validateres; } else { res = xpath_yang_validate_xmlkey(scb->reader, newval->editvars->insertxpcb, newval->obj, FALSE); } if (res == NO_ERR) { /* get the list entry that the 'key' attribute * referenced. It should be valid objects * and well-formed from passing the previous test */ testval = val_make_from_insertxpcb(newval, &res); if (res == NO_ERR && testval) { val_set_canonical_order(testval); insertval = val_first_child_match(curparent, testval); if (!insertval) { /* sibling list with the specified * key value was not found (13.7) */ res = ERR_NCX_INSERT_MISSING_INSTANCE; } else { newval->editvars->insertval = insertval; } } if (testval) { val_free_value(testval); testval = NULL; } } break; default: if (newval->editvars && newval->editvars->insertop != OP_INSOP_NONE) { res = ERR_NCX_UNEXPECTED_INSERT_ATTRS; agt_record_error(scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, newval->editvars->insertstr ? NCX_NT_STRING : NCX_NT_NONE, newval->editvars->insertstr, NCX_NT_VAL, newval); return res; } } if (res != NO_ERR) { agt_record_insert_error(scb, &msg->mhdr, res, newval); } return res; } /* check_insert_attr */ /******************************************************************** * FUNCTION commit_delete_allowed * * Check if the current session is allowed to delete * the node found in the requested commit delete operation * * INPUTS: * scb == session control block * msg == incoming commit rpc_msg_t in progress * deleteval == value struct to check * isfirst == TRUE if top-level caller; FALSE if recursive call * OUTPUTS: * rpc_err_rec_t structs may be malloced and added * to the msg->mhdr.errQ * * RETURNS: * status *********************************************************************/ static status_t commit_delete_allowed (ses_cb_t *scb, rpc_msg_t *msg, val_value_t *deleteval, boolean isfirst) { status_t res = NO_ERR; if (obj_is_root(deleteval->obj)) { return NO_ERR; } /* recurse until the config root is found and check all * the nodes top to bottom to see if this random XPath resnode * can be deleted by the current session right now */ if (deleteval->parent) { res = commit_delete_allowed(scb, msg, deleteval->parent, FALSE); if (res != NO_ERR) { return res; } } op_editop_t op = (isfirst) ? OP_EDITOP_DELETE : OP_EDITOP_MERGE; /* check if access control allows the delete */ if (!agt_acm_val_write_allowed(&msg->mhdr, scb->username, NULL, deleteval, op)) { res = ERR_NCX_ACCESS_DENIED; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, NULL, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } /* check if this curval is partially locked */ uint32 lockid = 0; res = val_write_ok(deleteval, op, SES_MY_SID(scb), TRUE, &lockid); if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_UINT32_PTR, &lockid, NCX_NT_VAL, deleteval); return res; } return res; } /* commit_delete_allowed */ /******************************************************************** * FUNCTION check_commit_deletes * * Check the requested commit delete operations * * INPUTS: * scb == session control block * msg == incoming commit rpc_msg_t in progress * candval == value struct from the candidate config * runval == value struct from the running config * * OUTPUTS: * rpc_err_rec_t structs may be malloced and added * to the msg->mhdr.errQ * * RETURNS: * none *********************************************************************/ static status_t check_commit_deletes (ses_cb_t *scb, rpc_msg_t *msg, val_value_t *candval, val_value_t *runval) { if (candval == NULL || runval == NULL) { return NO_ERR; } log_debug3("\ncheck_commit_deletes: start %s ", runval->name); /* go through running config * if the matching node is not in the candidate, * then delete that node in the running config as well */ status_t res = NO_ERR; val_value_t *curval = val_get_first_child(runval); for (; curval != NULL; curval = val_get_next_child(curval)) { /* check only database config nodes */ if (obj_is_data_db(curval->obj) && obj_is_config(curval->obj)) { /* check if node deleted in source */ val_value_t *matchval = val_first_child_match(candval, curval); if (!matchval) { res = commit_delete_allowed(scb, msg, curval, TRUE); if (res != NO_ERR) { return res; /* errors recorded */ } } } /* else skip non-config database node */ } return res; } /* check_commit_deletes */ /******************************************************************** * FUNCTION apply_write_val * * Invoke all the AGT_CB_APPLY callbacks for a * source and target and write operation * * INPUTS: * editop == edit operation in effect on the current node * scb == session control block * msg == incoming rpc_msg_t in progress * target == config database target * parent == parent value of curval and newval * newval == new value to apply * curval == current instance of value (may be NULL if none) * *done == TRUE if the node manipulation is done * == FALSE if none of the parent nodes have already * edited the config nodes; just do user callbacks * * OUTPUTS: * newval and curval may be moved around to * different queues, or get modified * *done may be changed from FALSE to TRUE if node is applied here * RETURNS: * status *********************************************************************/ static status_t apply_write_val (op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *parent, val_value_t *newval, val_value_t *curval, boolean *done) { const xmlChar *name = NULL; agt_cfg_undo_rec_t *undo = NULL; val_value_t *newval_marker = NULL; status_t res = NO_ERR; op_editop_t cur_editop = OP_EDITOP_NONE; boolean applyhere = FALSE, topreplace = FALSE; boolean add_defs_done = FALSE; if (newval) { cur_editop = (newval->editop == OP_EDITOP_NONE) ? editop : newval->editop; name = newval->name; } else if (curval) { cur_editop = editop; name = curval->name; } else { *done = TRUE; return SET_ERROR(ERR_INTERNAL_VAL); } /* check if this is a top-level replace via or * a restore-from-backup replace operation */ if (newval && obj_is_root(newval->obj) && msg->rpc_txcb->edit_type == AGT_CFG_EDIT_TYPE_FULL) { topreplace = TRUE; } log_debug4("\napply_write_val: %s start", name); /* check if this node needs the edit operation applied */ if (*done || (newval && obj_is_root(newval->obj))) { applyhere = FALSE; } else if (cur_editop == OP_EDITOP_COMMIT) { /* make sure the VAL_FL_DIRTY flag is set, not just * the VAL_FL_SUBTREE_DIRTY flag */ applyhere = (newval) ? val_get_dirty_flag(newval) : FALSE; *done = applyhere; } else if (editop == OP_EDITOP_DELETE || editop == OP_EDITOP_REMOVE) { applyhere = TRUE; *done = TRUE; } else { applyhere = agt_apply_this_node(cur_editop, newval, curval); *done = applyhere; } if (applyhere && newval) { /* check corner case applying to the config root */ if (obj_is_root(newval->obj)) { ; } else if (!typ_is_simple(newval->btyp) && !add_defs_done && editop != OP_EDITOP_DELETE && editop != OP_EDITOP_REMOVE) { res = val_add_defaults(newval, (target) ? target->root : NULL, curval, FALSE); if (res != NO_ERR) { log_error("\nError: add defaults failed"); applyhere = FALSE; } } } /* apply the requested edit operation */ if (applyhere) { if (LOGDEBUG) { val_value_t *logval; if (curval) { logval = curval; } else if (newval) { logval = newval; } else { logval = NULL; } if (logval) { xmlChar *buff = NULL; res = val_gen_instance_id(&msg->mhdr, logval, NCX_IFMT_XPATH1, &buff); if (res == NO_ERR) { log_debug("\napply_write_val: apply %s on %s", op_editop_name(editop), buff); } else { log_debug("\napply_write_val: apply %s on %s", op_editop_name(editop), name); } if (buff) { m__free(buff); } } } if (target->cfg_id == NCX_CFGID_RUNNING) { /* only call SIL-apply once, when the data tree edits * are applied to the running config. * The candidate target is skipped so the SIL * apply callback is not called twice. * Also there is no corresponding callback * to cleanup the candidate if * is invoked by the client or the server */ res = handle_user_callback(AGT_CB_APPLY, editop, scb, msg, newval, curval, TRUE, FALSE); if (res != NO_ERR) { /* assuming SIL added an rpc-error already * relying on catch-all operation-failed in agt_rpc_dispatch */ return res; } } /* exit here if the edited node is a virtual value * it is assumed that the user callback handled its own * internal representation of this node any subtrees */ if (curval && val_is_virtual(curval)) { /* update the last change time; assume the virtual * node contents changed */ cfg_update_last_ch_time(target); return NO_ERR; } /* handle recoverable edit; * make a deleted node markers for newval or curval if they need * to be removed from their parent nodes; this is needed * for non-descructive commit from candidate to running * or copy from running to startup; the source and target are * no longer deleted, just marked as deleted until commit * The newnode and curnode pointers MUST be rooted in a data * tree until after all SIL callbacks have been invoked */ switch (cur_editop) { case OP_EDITOP_LOAD: case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: /* leave source tree alone since it will not be re-parented * in the target tree */ break; default: /* remove newval node; swap with newval_marker for undo */ if (newval) { newval_marker = val_new_deleted_value(); if (newval_marker) { val_swap_child(newval_marker, newval); } else { return ERR_INTERNAL_MEM; } } // else keep source leaf or leaf-list node in place } switch (cur_editop) { case OP_EDITOP_MERGE: if (curval) { /* This is a leaf: * The applyhere code will not pick a complex node for * merging if the current node already exists. * The current node is always used instead of the new node * for merge, in case there are any read-only descendant * nodes already */ if (newval && newval->editvars && newval->editvars->insertstr) { res = move_child_node(newval, newval_marker, curval, parent, msg, cur_editop); } else if (newval) { /* merging a simple leaf or leaf-list * make a copy of the current node first */ undo = add_undo_node(msg, cur_editop, newval, newval_marker, curval, NULL, parent); if (undo == NULL) { /* no detailed rpc-error is recorded here!!! * relying on catch-all operation-failed in * agt_rpc_dispatch */ restore_newnode2(newval, newval_marker); return ERR_INTERNAL_MEM; } undo->apply_res = res = val_merge(newval, curval); undo->edit_action = AGT_CFG_EDIT_ACTION_SET; restore_newnode(undo); } else { res = SET_ERROR(ERR_INTERNAL_VAL); } } else if (newval) { res = add_child_node(newval, newval_marker, parent, msg, cur_editop); } else { res = SET_ERROR(ERR_INTERNAL_VAL); } break; case OP_EDITOP_REPLACE: case OP_EDITOP_COMMIT: if (curval) { if (newval && newval->editvars && newval->editvars->insertstr) { res = move_child_node(newval, newval_marker, curval, parent, msg, cur_editop); } else if (newval) { undo = add_undo_node(msg, cur_editop, newval, newval_marker, curval, NULL, parent); if (undo == NULL) { /* no detailed rpc-error is recorded here!!! * relying on catch-all operation-failed in * agt_rpc_dispatch */ restore_newnode2(newval, newval_marker); return ERR_INTERNAL_MEM; } if (obj_is_leafy(curval->obj)) { undo->apply_res = res = val_merge(newval, curval); undo->edit_action = AGT_CFG_EDIT_ACTION_SET; restore_newnode(undo); } else { val_insert_child(newval, curval, parent); VAL_MARK_DELETED(curval); undo->edit_action = AGT_CFG_EDIT_ACTION_REPLACE; undo->free_curnode = TRUE; } } else { res = SET_ERROR(ERR_INTERNAL_VAL); } } else if (newval) { res = add_child_node(newval, newval_marker, parent, msg, cur_editop); } else { res = SET_ERROR(ERR_INTERNAL_VAL); } break; case OP_EDITOP_CREATE: if (newval && curval) { /* there must be a default leaf */ undo = add_undo_node(msg, cur_editop, newval, newval_marker, curval, NULL, parent); if (undo == NULL) { /* no detailed rpc-error is recorded here!!! * relying on catch-all operation-failed in * agt_rpc_dispatch */ restore_newnode2(newval, newval_marker); return ERR_INTERNAL_MEM; } undo->apply_res = res = val_merge(newval, curval); undo->edit_action = AGT_CFG_EDIT_ACTION_SET; restore_newnode(undo); } else if (newval) { res = add_child_node(newval, newval_marker, parent, msg, cur_editop); } else { res = SET_ERROR(ERR_INTERNAL_VAL); } break; case OP_EDITOP_LOAD: assert( newval && "newval NULL in LOAD" ); undo = add_undo_node(msg, cur_editop, newval, newval_marker, curval, NULL, parent); if (undo == NULL) { /* no detailed rpc-error is recorded here!!! * relying on catch-all operation-failed in * agt_rpc_dispatch */ restore_newnode2(newval, newval_marker); return ERR_INTERNAL_MEM; } break; case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: if (!curval) { /* this was a REMOVE operation NO-OP (?verify?) */ break; } undo = add_undo_node(msg, cur_editop, newval, newval_marker, curval, NULL, parent); if (undo == NULL) { /* no detailed rpc-error is recorded here!!! * relying on catch-all operation-failed in agt_rpc_dispatch */ restore_newnode2(newval, newval_marker); return ERR_INTERNAL_MEM; } if (curval->parent) { val_set_dirty_flag(curval->parent); } /* check if this is a leaf with a default */ if (obj_get_default(curval->obj)) { /* convert this leaf to its default value */ res = val_delete_default_leaf(curval); undo->edit_action = AGT_CFG_EDIT_ACTION_DELETE_DEFAULT; } else { /* mark this node as deleted */ VAL_MARK_DELETED(curval); undo->edit_action = AGT_CFG_EDIT_ACTION_DELETE; undo->free_curnode = TRUE; } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } } if (res == NO_ERR && applyhere == TRUE && newval && curval && newval->btyp == NCX_BT_LIST && cur_editop == OP_EDITOP_MERGE) { if (undo == NULL) { undo = add_undo_node(msg, editop, newval, newval_marker, curval, NULL, parent); if (undo == NULL) { restore_newnode2(newval, newval_marker); res = ERR_INTERNAL_MEM; } } if (res == NO_ERR) { /* move the list entry after the merge is done */ res = move_mergedlist_node(newval, curval, parent, undo); } } if (res == NO_ERR && newval && curval) { if (topreplace || (editop == OP_EDITOP_COMMIT && !typ_is_simple(newval->btyp) && val_get_subtree_dirty_flag(newval))) { res = apply_commit_deletes(scb, msg, target, newval, curval); } } /* TBD: should this be moved to the commit phase somehow, although no * recovery is possible during a OP_EDITOP_LOAD at boot-time */ if (res == NO_ERR && newval && obj_is_root(newval->obj) && editop == OP_EDITOP_LOAD) { /* there is no newval_marker for this root node newval * so this operation cannot be undone */ val_remove_child(newval); cfg_apply_load_root(target, newval); } return res; } /* apply_write_val */ /******************************************************************** * FUNCTION check_withdef_default * * Check the wd:default node for correctness * * INPUTS: * scb == session control block * msg == incoming rpc_msg_t in progress * newval == val_value_t from the PDU * * RETURNS: * status *********************************************************************/ static status_t check_withdef_default (ses_cb_t *scb, rpc_msg_t *msg, val_value_t *newval) { status_t res = NO_ERR; if (newval == NULL) { return NO_ERR; } /* check object is a leaf */ if (newval->obj->objtype != OBJ_TYP_LEAF) { res = ERR_NCX_WRONG_NODETYP; } else { /* check leaf has a schema default */ const xmlChar *defstr = obj_get_default(newval->obj); if (defstr == NULL) { res = ERR_NCX_NO_DEFAULT; } else { /* check value provided is the same as the default */ int32 ret = val_compare_to_string(newval, defstr, &res); if (res == NO_ERR && ret != 0) { res = ERR_NCX_INVALID_VALUE; } } } if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_VAL, newval, NCX_NT_VAL, newval); } return res; } /* check_withdef_default */ /******************************************************************** * Invoke the specified agent simple type validate callback. * * \param editop parent node edit operation * \param scb session control block * \param msg incoming rpc_msg_t in progress * \param newval val_value_t from the PDU, this cannot be null! * \param curval current value (if any) from the target config * \param curparent current parent value for inserting children * * \return status *********************************************************************/ static status_t invoke_simval_cb_validate( op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, val_value_t *newval, val_value_t *curval, val_value_t *curparent ) { assert ( newval && "newval is NULL!" ); log_debug4( "\ninvoke_simval:validate: %s:%s start", obj_get_mod_name(newval->obj), newval->name); /* check and adjust the operation attribute */ boolean is_validate = msg->rpc_txcb->is_validate; ncx_iqual_t iqual = val_get_iqualval(newval); op_editop_t cur_editop = OP_EDITOP_NONE; op_editop_t cvtop = cvt_editop(editop, newval, curval); status_t res = agt_check_editop( cvtop, &cur_editop, newval, curval, iqual, ses_get_protocol(scb) ); if (cur_editop == OP_EDITOP_NONE) { cur_editop = editop; } if (editop != OP_EDITOP_COMMIT && !is_validate) { newval->editop = cur_editop; } if (res == ERR_NCX_BAD_ATTRIBUTE) { xml_attr_t errattr; memset(&errattr, 0x0, sizeof(xml_attr_t)); errattr.attr_ns = xmlns_nc_id(); errattr.attr_name = NC_OPERATION_ATTR_NAME; errattr.attr_val = (xmlChar *)NULL; agt_record_attr_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, &errattr, NULL, NULL, NCX_NT_VAL, newval ); return res; } else if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_NONE, NULL, NCX_NT_VAL, newval ); return res; } /* check the operation against the object definition and whether or not * the entry currently exists */ res = agt_check_max_access(newval, (curval != NULL)); if ( res != NO_ERR ) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_VAL, newval, NCX_NT_VAL, newval ); return res; } /* make sure the node is not partial locked by another session; there is a * corner case where all the PDU nodes are OP_EDITOP_NONE, and so nodes * that touch a partial lock will never actually request an operation; treat * this as an error anyway, since it is too hard to defer the test until * later, and this is a useless corner-case so clients should not do it */ if (curval) { uint32 lockid = 0; res = val_write_ok( curval, cur_editop, SES_MY_SID(scb), FALSE, &lockid ); if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, NULL, NCX_NT_UINT32_PTR, &lockid, NCX_NT_VAL, curval ); return res; } } res = check_insert_attr(scb, msg, newval, curparent); if (res != NO_ERR) { /* error already recorded */ return res; } /* check if the wd:default attribute is present and if so, that it is used * properly; this needs to be done after the effective operation is set for * newval */ if ( val_has_withdef_default(newval) ) { res = check_withdef_default( scb, msg, newval ); if (res != NO_ERR) { return res; } } /* check the user callback only if there is some operation in * affect already */ if ( agt_apply_this_node(cur_editop, newval, curval)) { res = handle_user_callback( AGT_CB_VALIDATE, cur_editop, scb, msg, newval, curval, TRUE, FALSE ); } return res; } /******************************************************************** * Invoke the specified agent simple type apply / commit_check callback. * * \param scb session control block * \param msg incoming rpc_msg_t in progress * \param target cfg_template_t for the config database to write * \param newval val_value_t from the PDU * \param curval current value (if any) from the target config * * \return status *********************************************************************/ static status_t invoke_simval_cb_apply_commit_check( op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *newval, val_value_t *curval, val_value_t *curparent ) { op_editop_t cureditop = editop; boolean done = false; if (newval && newval->editop != OP_EDITOP_NONE) { cureditop = newval->editop; } else if (curval) { if (cureditop == OP_EDITOP_NONE) { cureditop = curval->editop; } } return apply_write_val( cureditop, scb, msg, target, curparent, newval, curval, &done ); } /******************************************************************** * Invoke all the specified agent simple type callbacks for a * source and target and write operation * * \param cbtyp callback type being invoked * \param editop parent node edit operation * \param scb session control block * \param msg incoming rpc_msg_t in progress * \param target cfg_template_t for the config database to write * \param newval val_value_t from the PDU * \param curval current value (if any) from the target config * \param curparent current parent node for inserting children * * \return status *********************************************************************/ static status_t invoke_simval_cb (agt_cbtyp_t cbtyp, op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *newval, val_value_t *curval, val_value_t *curparent) { status_t res = NO_ERR; /* check the 'operation' attribute in VALIDATE phase */ switch (cbtyp) { case AGT_CB_VALIDATE: res = invoke_simval_cb_validate( editop, scb, msg, newval, curval, curparent ); break; case AGT_CB_APPLY: res = invoke_simval_cb_apply_commit_check( editop, scb, msg, target, newval, curval, curparent ); break; case AGT_CB_COMMIT: case AGT_CB_ROLLBACK: default: /* nothing to do for commit or rollback at this time */ break; } return res; } /* invoke_simval_cb */ /******************************************************************** * FUNCTION check_keys_present * * Check that the specified list entry has all key leafs present * Generate and record s for each missing key * * INPUTS: * scb == session control block * msg == incoming rpc_msg_t in progress * newval == val_value_t from the PDU * * RETURNS: * status *********************************************************************/ static status_t check_keys_present (ses_cb_t *scb, rpc_msg_t *msg, val_value_t *newval) { obj_key_t *objkey = NULL; status_t res = NO_ERR; if (newval == NULL) { return NO_ERR; } if (newval->obj->objtype != OBJ_TYP_LIST) { return SET_ERROR(ERR_INTERNAL_VAL); } for (objkey = obj_first_key(newval->obj); objkey != NULL; objkey = obj_next_key(objkey)) { if (val_find_child(newval, obj_get_mod_name(objkey->keyobj), obj_get_name(objkey->keyobj))) { continue; } res = ERR_NCX_MISSING_KEY; agt_record_error(scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_OBJ, objkey->keyobj, /* error-info */ NCX_NT_VAL, newval); /* error-path */ } return res; } /* check_keys_present */ /******************************************************************** * FUNCTION invoke_cpxval_cb * * Invoke all the specified agent complex type callbacks for a * source and target and write operation * * INPUTS: * cbtyp == callback type being invoked * editop == parent node edit operation * scb == session control block * msg == incoming rpc_msg_t in progress * target == cfg_template_t for the config database to write * newval == val_value_t from the PDU * curval == current value (if any) from the target config * curparent == current parent node for adding children * done == TRUE if the node manipulation is done * == FALSE if none of the parent nodes have already * edited the config nodes; just do user callbacks * * RETURNS: * status *********************************************************************/ static status_t invoke_cpxval_cb (agt_cbtyp_t cbtyp, op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *newval, val_value_t *curval, val_value_t *curparent, boolean done) { status_t res = NO_ERR, retres = NO_ERR; op_editop_t cur_editop = OP_EDITOP_NONE, cvtop = OP_EDITOP_NONE; boolean isroot = FALSE, isdirty = TRUE; /* check the 'operation' attribute in VALIDATE phase */ switch (cbtyp) { case AGT_CB_VALIDATE: assert ( newval && "newval is NULL!" ); isroot = obj_is_root(newval->obj); if (LOGDEBUG4 && !isroot) { log_debug4("\ninvoke_cpxval:validate: %s:%s start", obj_get_mod_name(newval->obj), newval->name); } if (editop == OP_EDITOP_COMMIT) { isdirty = val_get_dirty_flag(newval); } if (!isroot && isdirty) { /* check and adjust the operation attribute */ ncx_iqual_t iqual = val_get_iqualval(newval); cvtop = cvt_editop(editop, newval, curval); res = agt_check_editop(cvtop, &cur_editop, newval, curval, iqual, ses_get_protocol(scb)); if (editop != OP_EDITOP_COMMIT ) { /* need to check the max-access but not over-write * the newval->editop if this is a validate operation * do not care about parent operation in agt_check_max_access * because the operation is expected to be OP_EDITOP_LOAD */ boolean is_validate = msg->rpc_txcb->is_validate; op_editop_t saveop = editop; newval->editop = cur_editop; if (res == NO_ERR) { res = agt_check_max_access(newval, (curval != NULL)); } if (is_validate) { newval->editop = saveop; } } } if (res == NO_ERR) { if (!isroot && editop != OP_EDITOP_COMMIT && cur_editop != OP_EDITOP_LOAD) { res = check_insert_attr(scb, msg, newval, curparent); CHK_EXIT(res, retres); res = NO_ERR; /* any error already recorded */ } } if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_VAL, newval, NCX_NT_VAL, newval); CHK_EXIT(res, retres); } if (res == NO_ERR && !isroot && newval->btyp == NCX_BT_LIST) { /* make sure all the keys are present since this * was not done in agt_val_parse.c */ if (editop == OP_EDITOP_NONE && !agt_any_operations_set(newval)) { if (LOGDEBUG3) { log_debug3("\nSkipping key_check for '%s'; no edit ops", newval->name); } } else { res = check_keys_present(scb, msg, newval); /* errors recorded if any */ } } /* check if the wd:default attribute is present and if so, * it will be an error */ if (res == NO_ERR && !isroot && editop != OP_EDITOP_COMMIT) { if (val_has_withdef_default(newval)) { res = check_withdef_default(scb, msg, newval); } } /* any of the errors so far indicate this node is bad * setting the val->res to the error will force it to be removed * if --startup-error=continue; only apply to the new tree, if any */ if (res != NO_ERR && newval != NULL) { VAL_MARK_DELETED(newval); newval->res = res; } /* make sure the node is not partial locked * by another session; there is a corner case where * all the PDU nodes are OP_EDITOP_NONE, and * so nodes that touch a partial lock will never * actually request an operation; treat this * as an error anyway, since it is too hard * to defer the test until later, and this is * a useless corner-case so clients should not do it */ if (res == NO_ERR && !isroot) { val_value_t *useval = newval ? newval : curval; uint32 lockid = 0; if (obj_is_root(useval->obj) && cur_editop == OP_EDITOP_NONE) { /* do not check OP=none on the config root */ ; } else if (editop == OP_EDITOP_COMMIT && !isdirty) { ; // no write requested on this node } else { res = val_write_ok(useval, cur_editop, SES_MY_SID(scb), FALSE, &lockid); } if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, NULL, NCX_NT_UINT32_PTR, &lockid, NCX_NT_VAL, useval); } } /* check the user callback only if there is some * operation in affect already */ if (res == NO_ERR && !isroot && isdirty && agt_apply_this_node(cur_editop, newval, curval)) { res = handle_user_callback(AGT_CB_VALIDATE, cur_editop, scb, msg, newval, curval, TRUE, FALSE); } if (res != NO_ERR) { retres = res; } break; case AGT_CB_APPLY: if (newval) { cur_editop = newval->editop; if (cur_editop == OP_EDITOP_NONE) { cur_editop = editop; } isroot = obj_is_root(newval->obj); } else if (curval) { isroot = obj_is_root(curval->obj); cur_editop = editop; } else { cur_editop = editop; } res = apply_write_val(cur_editop, scb, msg, target, curparent, newval, curval, &done); if (res != NO_ERR) { retres = res; } break; case AGT_CB_COMMIT: case AGT_CB_ROLLBACK: break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); } /* change the curparent parameter to the current nodes * before invoking callbacks for the child nodes */ if (retres == NO_ERR) { if (newval) { curparent = newval; } else if (curval) { curparent = curval; } else { retres = SET_ERROR(ERR_INTERNAL_VAL); } } /* check all the child nodes next */ if (retres == NO_ERR && !done && curparent) { val_value_t *chval, *curch, *nextch; for (chval = val_get_first_child(curparent); chval != NULL && retres == NO_ERR; chval = nextch) { nextch = val_get_next_child(chval); if (curval) { curch = val_first_child_match(curval, chval); } else { curch = NULL; } cur_editop = chval->editop; if (cur_editop == OP_EDITOP_NONE) { cur_editop = editop; } res = invoke_btype_cb(cbtyp, cur_editop, scb, msg, target, chval, curch, curval); //if (chval->res == NO_ERR) { // chval->res = res; //} CHK_EXIT(res, retres); } } return retres; } /* invoke_cpxval_cb */ /******************************************************************** * FUNCTION invoke_btype_cb * * Invoke all the specified agent typedef callbacks for a * source and target and write operation * * INPUTS: * cbtyp == callback type being invoked * editop == parent node edit operation * scb == session control block * msg == incoming rpc_msg_t in progress * target == cfg_template_t for the config database to write * newval == val_value_t from the PDU * curval == current value (if any) from the target config * curparent == current parent for inserting child nodes * RETURNS: * status *********************************************************************/ static status_t invoke_btype_cb( agt_cbtyp_t cbtyp, op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *newval, val_value_t *curval, val_value_t *curparent ) { val_value_t *useval; if (newval != NULL) { useval = newval; } else if (curval != NULL) { useval = curval; } else { return SET_ERROR(ERR_INTERNAL_VAL); } boolean is_root = obj_is_root(useval->obj); if (editop == OP_EDITOP_COMMIT && !is_root && !val_dirty_subtree(useval)) { log_debug2("\nSkipping unaffected %s in %s commit for %s:%s", obj_get_typestr(useval->obj), agt_cbtype_name(cbtyp), val_get_mod_name(useval), useval->name); return NO_ERR; } val_value_t *v_val = NULL; status_t res = NO_ERR; boolean has_children = !typ_is_simple(useval->btyp); if (cbtyp == AGT_CB_VALIDATE) { boolean check_acm = TRUE; op_editop_t cvtop = cvt_editop(editop, newval, curval); /* check if ACM test needs to be skipped */ if (is_root) { check_acm = FALSE; } else if (editop == OP_EDITOP_COMMIT) { switch (cvtop) { case OP_EDITOP_CREATE: case OP_EDITOP_DELETE: break; case OP_EDITOP_MERGE: case OP_EDITOP_REPLACE: if (!val_get_dirty_flag(useval)) { check_acm = FALSE; } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } } /* check if allowed access to this node */ if (check_acm && scb && !agt_acm_val_write_allowed(&msg->mhdr, scb->username, newval, curval, cvtop)) { res = ERR_NCX_ACCESS_DENIED; agt_record_error(scb,&msg->mhdr, NCX_LAYER_OPERATION, res, NULL, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } if (curval && val_is_virtual(curval)) { v_val = val_get_virtual_value(scb, curval, &res); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } else if (res != NO_ERR) { return res; } } /* check if there are commit deletes to validate */ if (has_children && msg->rpc_txcb->edit_type == AGT_CFG_EDIT_TYPE_FULL) { res = check_commit_deletes(scb, msg, newval, v_val ? v_val : curval); if (res != NO_ERR) { return res; } } } else if (cbtyp == AGT_CB_APPLY && editop == OP_EDITOP_COMMIT && has_children) { if (curval && val_is_virtual(curval)) { v_val = val_get_virtual_value(scb, curval, &res); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } else if (res != NO_ERR) { return res; } } } /* first traverse all the nodes until leaf nodes are reached */ if (has_children) { res = invoke_cpxval_cb(cbtyp, editop, scb, msg, target, newval, (v_val) ? v_val : curval, curparent, FALSE); } else { res = invoke_simval_cb(cbtyp, editop, scb, msg, target, newval, (v_val) ? v_val : curval, curparent ); if (newval != NULL && res != NO_ERR) { VAL_MARK_DELETED(newval); newval->res = res; } } return res; } /* invoke_btype_cb */ /******************************************************************** * FUNCTION commit_edit AGT_CB_COMMIT * * Finish the database editing for an undo-rec * The SIL callbacks have already completed; this step cannot fail * * INPUTS: * scb == session control block to use * msg == RPC message in progress * undo == agt_cfg_undo_rec_t struct to process * *********************************************************************/ static void commit_edit (ses_cb_t *scb, rpc_msg_t *msg, agt_cfg_undo_rec_t *undo) { if (undo->editop == OP_EDITOP_LOAD) { /* the top-level nodes are kept in the original tree the very * first time the running config is loaded from startup */ undo->newnode = NULL; return; } handle_audit_record(undo->editop, scb, msg, undo->newnode, undo->curnode); /* check if the newval marker was placed in the source tree */ if (undo->newnode_marker) { dlq_remove(undo->newnode_marker); val_free_value(undo->newnode_marker); undo->newnode_marker = NULL; } /* check if the curval marker was placed in the target tree */ if (undo->curnode_marker) { dlq_remove(undo->curnode_marker); val_free_value(undo->curnode_marker); undo->curnode_marker = NULL; } if (msg->rpc_txcb->cfg_id == NCX_CFGID_RUNNING) { switch (undo->editop) { case OP_EDITOP_REPLACE: case OP_EDITOP_COMMIT: val_check_swap_resnode(undo->curnode, undo->newnode); break; case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: if (undo->curnode) { val_check_delete_resnode(undo->curnode); } break; default: ; } } if (undo->newnode) { if (msg->rpc_txcb->cfg_id == NCX_CFGID_RUNNING) { val_clear_dirty_flag(undo->newnode); } else { val_set_dirty_flag(undo->newnode); } } if (undo->curnode) { if (msg->rpc_txcb->cfg_id == NCX_CFGID_RUNNING) { val_clear_dirty_flag(undo->curnode); } else { val_set_dirty_flag(undo->curnode); } if (undo->free_curnode) { if (VAL_IS_DELETED(undo->curnode)) { val_remove_child(undo->curnode); } if (undo->curnode_clone) { /* do not need curnode as a backup for redo-restore */ val_free_value(undo->curnode); undo->curnode = NULL; } } } clean_node(undo->newnode); clean_node(undo->curnode); finish_extra_deletes(msg->rpc_txcb->cfg_id, undo); /* rest of cleanup will be done in agt_cfg_free_undorec; * Saving curnode_clone or curnode and the entire extra_deleteQ * in case this commit needs to be undone by simulating a * new transaction wrt/ SIL callbacks */ } /* commit_edit */ /******************************************************************** * FUNCTION reverse_edit * * Attempt to invoke SIL callbacks to reverse an edit that * was already commited; database tree was never undone * The apply and commit phases completed OK * * INPUTS: * undo == agt_cfg_undo_rec_t struct to process * scb == session control block * msg == incoming rpc_msg_t in progress * * RETURNS: * status *********************************************************************/ static status_t reverse_edit (agt_cfg_undo_rec_t *undo, ses_cb_t *scb, rpc_msg_t *msg) { agt_cfg_transaction_t *txcb = msg->rpc_txcb; if (LOGDEBUG3) { val_value_t *logval = undo->newnode ? undo->newnode : undo->curnode; log_debug3("\nReverse transaction %llu, %s edit on %s:%s", (unsigned long long)txcb->txid, op_editop_name(undo->editop), val_get_mod_name(logval), logval->name); } val_value_t *newval = NULL; val_value_t *curval = NULL; op_editop_t editop = OP_EDITOP_NONE; status_t res = NO_ERR; boolean lookparent = TRUE; boolean indelete = FALSE; switch (undo->edit_action) { case AGT_CFG_EDIT_ACTION_NONE: log_warn("\nError: undo record has no action set"); return NO_ERR; case AGT_CFG_EDIT_ACTION_ADD: /* need to generate a delete for curnode */ assert( undo->newnode && "undo newnode is NULL!" ); curval = undo->newnode; editop = OP_EDITOP_DELETE; indelete = TRUE; break; case AGT_CFG_EDIT_ACTION_SET: /* need to generate a set for the old value */ assert( undo->newnode && "undo newnode is NULL!" ); curval = undo->newnode; newval = (undo->curnode_clone) ? undo->curnode_clone : undo->curnode; assert( newval && "undo curnode is NULL!" ); editop = undo->editop; break; case AGT_CFG_EDIT_ACTION_MOVE: /* need to generate a similar operation to move the old entry * back to its old location * !!! FIXME: are the editvars correct, and do SIL callbacks * !!! look at the editvars? */ assert( undo->newnode && "undo newnode is NULL!" ); assert( undo->curnode && "undo curnode is NULL!" ); newval = undo->curnode; curval = undo->newnode; editop = undo->editop; break; case AGT_CFG_EDIT_ACTION_REPLACE: /* need to geerate a replace with the values revered */ assert( undo->newnode && "undo newnode is NULL!" ); assert( undo->curnode && "undo curnode is NULL!" ); newval = undo->curnode; curval = undo->newnode; editop = undo->editop; break; case AGT_CFG_EDIT_ACTION_DELETE: /* need to generate a create for the deleted value */ assert( undo->curnode && "undo curnode is NULL!" ); newval = undo->curnode; editop = OP_EDITOP_CREATE; break; case AGT_CFG_EDIT_ACTION_DELETE_DEFAULT: if (undo->curnode) { newval = undo->curnode; editop = OP_EDITOP_CREATE; } else { return NO_ERR; } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } res = handle_user_callback(AGT_CB_APPLY, editop, scb, msg, newval, curval, lookparent, indelete); if (res == NO_ERR) { res = handle_user_callback(AGT_CB_COMMIT, editop, scb, msg, newval, curval, lookparent, indelete); } if (res == NO_ERR) { /* reverse all the extra deletes as create operations */ editop = OP_EDITOP_CREATE; curval = NULL; lookparent = FALSE; indelete = FALSE; agt_cfg_nodeptr_t *nodeptr = (agt_cfg_nodeptr_t *) dlq_firstEntry(&undo->extra_deleteQ); for (; nodeptr && res == NO_ERR; nodeptr = (agt_cfg_nodeptr_t *)dlq_nextEntry(nodeptr)) { if (nodeptr && nodeptr->node) { newval = nodeptr->node; res = handle_user_callback(AGT_CB_APPLY, editop, scb, msg, newval, curval, lookparent, indelete); if (res == NO_ERR) { res = handle_user_callback(AGT_CB_COMMIT, editop, scb, msg, newval, curval, lookparent, indelete); } } else { SET_ERROR(ERR_INTERNAL_VAL); } } } if (res != NO_ERR) { log_error("\nError: SIL rejected reverse edit (%s)", get_error_string(res)); } return res; } /* reverse_edit */ /******************************************************************** * FUNCTION rollback_edit * * Attempt to rollback an edit * * INPUTS: * undo == agt_cfg_undo_rec_t struct to process * scb == session control block * msg == incoming rpc_msg_t in progress * target == cfg_template_t for the config database to write * * RETURNS: * status *********************************************************************/ static status_t rollback_edit (agt_cfg_undo_rec_t *undo, ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target) { val_value_t *curval = NULL; status_t res = NO_ERR; ncx_cfg_t cfgid = msg->rpc_txcb->cfg_id; if (LOGDEBUG2) { val_value_t *logval = undo->newnode ? undo->newnode : undo->curnode; log_debug2("\nRollback transaction %llu, %s edit on %s:%s", (unsigned long long)msg->rpc_txcb->txid, op_editop_name(undo->editop), val_get_mod_name(logval), logval->name); } if (undo->editop == OP_EDITOP_LOAD) { log_debug3("\nSkipping rollback edit of LOAD operation"); undo->newnode = NULL; return NO_ERR; } if (undo->curnode_clone != NULL) { curval = undo->curnode_clone; } else { curval = undo->curnode; } switch (undo->commit_res) { case NO_ERR: /* need a redo edit since the SIL already completed the commit */ res = reverse_edit(undo, scb, msg); break; case ERR_NCX_SKIPPED: /* need a simple undo since the commit fn was never called * first call the SIL with the ROLLBACK request */ if (cfgid == NCX_CFGID_RUNNING) { undo->rollback_res = handle_user_callback(AGT_CB_ROLLBACK, undo->editop, scb, msg, undo->newnode, curval, TRUE, FALSE); if (undo->rollback_res != NO_ERR) { val_value_t *logval = undo->newnode ? undo->newnode : curval; log_warn("\nWarning: SIL rollback for %s:%s in transaction " "%llu failed (%s)", (unsigned long long)msg->rpc_txcb->txid, val_get_mod_name(logval), logval->name, get_error_string(undo->rollback_res)); res = undo->rollback_res; /* !!! not sure what the SIL callback thinks the resulting data * is going to look like; return to original state anyway */ } } break; default: /* this is the node that failed during the COMMIT callback * assuming this SIL does not need any rollback since the * failure indicates the commit was not applied; * TBD: need to flag SIL partial commit errors and what * to do about it */ ; } /* if the source is from a PDU, then it will get cleanup up when * this is done. If it is from config then * preserve the edit in candidate since it is still a pending edit * in case commit is attempted again */ //clean_node(undo->newnode); //val_clear_dirty_flag(undo->newnode); //clean_node(undo->curnode); //val_clear_dirty_flag(undo->curnode); /* undo the specific operation * even if the SIL commit callback returned an error * the commit_edit function was never called so this * edit needs to be cleaned up */ switch (undo->editop) { case OP_EDITOP_CREATE: /* delete the node from the tree; if there was a default * leaf before then restore curval as well */ val_remove_child(undo->newnode); restore_newnode(undo); restore_curnode(undo, target); break; case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: /* no need to restore newnode */ restore_curnode(undo, target); break; case OP_EDITOP_MERGE: case OP_EDITOP_REPLACE: /* check if the old node needs to be swapped back * or if the new node is just removed */ restore_curnode(undo, target); /* undo newval that was a new or merged child node */ if (undo->newnode_marker) { val_remove_child(undo->newnode); } restore_newnode(undo); break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } restore_extra_deletes(&undo->extra_deleteQ); return res; } /* rollback_edit */ /******************************************************************** * FUNCTION attempt_commit * * Attempt complete commit by asking all SIL COMMIT callbacks * to complete the requested edit * * INPUTS: * scb == session control block * msg == incoming rpc_msg_t in progress * target == cfg_template_t for the config database to write * * OUTPUTS: * if errors are encountered (such as undo-failed) then they * will be added to the msg->mhdr.errQ * * RETURNS: * status *********************************************************************/ static status_t attempt_commit (ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target) { agt_cfg_undo_rec_t *undo = NULL; agt_cfg_transaction_t *txcb = msg->rpc_txcb; if (LOGDEBUG) { uint32 editcnt = dlq_count(&txcb->undoQ); const xmlChar *cntstr = NULL; switch (editcnt) { case 0: break; case 1: cntstr = (const xmlChar *)"edit"; break; default: cntstr = (const xmlChar *)"edits"; } if (editcnt) { log_debug("\nStart full commit of transaction %llu: %d" " %s on %s config", txcb->txid, editcnt, cntstr, target->name); } else { log_debug("\nStart full commit of transaction %llu:" " LOAD operation on %s config", txcb->txid, target->name); } } /* first make sure all SIL callbacks accept the commit */ if (target->cfg_id == NCX_CFGID_RUNNING) { undo = (agt_cfg_undo_rec_t *)dlq_firstEntry(&txcb->undoQ); for (; undo != NULL; undo = (agt_cfg_undo_rec_t *)dlq_nextEntry(undo)) { if (undo->curnode && undo->curnode->parent == NULL) { /* node was actually removed in delete operation */ undo->curnode->parent = undo->parentnode; } undo->commit_res = handle_user_callback(AGT_CB_COMMIT, undo->editop, scb, msg, undo->newnode, undo->curnode, TRUE, FALSE); if (undo->commit_res != NO_ERR) { val_value_t *logval = undo->newnode ? undo->newnode : undo->curnode; if (LOGDEBUG) { log_debug("\nHalting commit %llu: %s:%s SIL returned error" " (%s)", (unsigned long long)txcb->txid, val_get_mod_name(logval), logval->name, get_error_string(undo->commit_res)); } return undo->commit_res; } } } /* all SIL commit callbacks accepted and finalized the commit * now go through and finalize the edit; this step should not fail * first, finish deleting any false when-stmt nodes then commit edits */ while (!dlq_empty(&txcb->deadnodeQ)) { agt_cfg_nodeptr_t *nodeptr = (agt_cfg_nodeptr_t *) dlq_deque(&txcb->deadnodeQ); if (nodeptr && nodeptr->node) { /* mark ancestor nodes dirty before deleting this node */ val_remove_child(nodeptr->node); val_free_value(nodeptr->node); } else { SET_ERROR(ERR_INTERNAL_VAL); } agt_cfg_free_nodeptr(nodeptr); } undo = (agt_cfg_undo_rec_t *)dlq_firstEntry(&txcb->undoQ); for (; undo != NULL; undo = (agt_cfg_undo_rec_t *)dlq_nextEntry(undo)) { commit_edit(scb, msg, undo); } /* update the last change time * this will only apply here to candidate or running */ cfg_update_last_ch_time(target); cfg_update_last_txid(target, txcb->txid); cfg_set_dirty_flag(target); agt_profile_t *profile = agt_get_profile(); profile->agt_config_state = AGT_CFG_STATE_OK; if (LOGDEBUG) { log_debug("\nComplete commit OK of transaction %llu" " on %s database", (unsigned long long)txcb->txid, target->name); } return NO_ERR; } /* attempt_commit */ /******************************************************************** * FUNCTION attempt_rollback * * Attempt to rollback a transaction attempt * if commit not tried: * All edits have a status of NCX_ERR_SKIPPED * if commit tried: * There are N edits that succeeded and commit_res == NO_ERR * There is 1 edit that the SIL callback rejected with an error * There are M edits with a commit_res of NCX_ERR_SKIPPED * * INPUTS: * scb == session control block * msg == incoming rpc_msg_t in progress * target == cfg_template_t for the config database to write * * RETURNS: * status *********************************************************************/ static status_t attempt_rollback (ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target) { status_t res = NO_ERR; agt_cfg_transaction_t *txcb = msg->rpc_txcb; if (LOGDEBUG) { uint32 editcnt = dlq_count(&txcb->undoQ); const xmlChar *cntstr = NULL; switch (editcnt) { case 0: break; case 1: cntstr = (const xmlChar *)"edit"; break; default: cntstr = (const xmlChar *)"edits"; } if (editcnt) { log_debug("\nStart full rollback of transaction %llu: %d" " %s on %s config", txcb->txid, editcnt, cntstr, target->name); } else { log_debug("\nStart full rollback of transaction %llu:" " LOAD operation on %s config", txcb->txid, target->name); } } restore_extra_deletes(&txcb->deadnodeQ); agt_cfg_undo_rec_t *undo = (agt_cfg_undo_rec_t *) dlq_firstEntry(&txcb->undoQ); for (; undo != NULL; undo = (agt_cfg_undo_rec_t *)dlq_nextEntry(undo)) { /* rollback the edit operation */ undo->rollback_res = rollback_edit(undo, scb, msg, target); if (undo->rollback_res != NO_ERR) { res = undo->rollback_res; } } return res; } /* attempt_rollback */ /******************************************************************** * FUNCTION handle_callback * * Invoke all the specified agent typedef callbacks for a * source and target and write operation * * INPUTS: * cbtyp == callback type being invoked * editop == parent node edit operation * scb == session control block * msg == incoming rpc_msg_t in progress * target == cfg_template_t for the config database to write * newval == val_value_t from the PDU * curval == current value (if any) from the target config * curparent == current parent node for inserting children * RETURNS: * status *********************************************************************/ static status_t handle_callback (agt_cbtyp_t cbtyp, op_editop_t editop, ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *newval, val_value_t *curval, val_value_t *curparent) { assert( msg && "msg is NULL!" ); assert( msg->rpc_txcb && "txcb is NULL!" ); agt_cfg_transaction_t *txcb = msg->rpc_txcb; status_t res = NO_ERR; if (LOGDEBUG2) { if (editop == OP_EDITOP_DELETE) { log_debug2("\n\n***** start commit_deletes for " "session %d, transaction %llu *****\n", scb ? SES_MY_SID(scb) : 0, (unsigned long long)txcb->txid); } else { log_debug2("\n\n***** start %s callback phase for " "session %d, transaction %llu *****\n", agt_cbtype_name(cbtyp), scb ? SES_MY_SID(scb) : 0, (unsigned long long)txcb->txid); } } /* get the node to check */ if (newval == NULL && curval == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } /* check if trying to write to a config=false node */ if (newval != NULL && !val_is_config_data(newval)) { res = ERR_NCX_ACCESS_READ_ONLY; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, NULL, NCX_NT_NONE, NULL, NCX_NT_VAL, newval); return res; } /* check if trying to delete a config=false node */ if (newval == NULL && curval != NULL && !val_is_config_data(curval)) { res = ERR_NCX_ACCESS_READ_ONLY; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, NULL, NCX_NT_NONE, NULL, NCX_NT_VAL, curval); return res; } /* this is a config node so check the operation further */ switch (cbtyp) { case AGT_CB_VALIDATE: case AGT_CB_APPLY: /* keep checking until all the child nodes have been processed */ res = invoke_btype_cb(cbtyp, editop, scb, msg, target, newval, curval, curparent); if (cbtyp == AGT_CB_APPLY) { txcb->apply_res = res; } break; case AGT_CB_COMMIT: txcb->commit_res = res = attempt_commit(scb, msg, target); if (res != NO_ERR) { txcb->rollback_res = attempt_rollback(scb, msg, target); if (txcb->rollback_res != NO_ERR) { log_warn("\nWarning: Recover rollback of transaction " "%llu failed (%s)", (unsigned long long)txcb->txid, get_error_string(txcb->rollback_res)); } } break; case AGT_CB_ROLLBACK: txcb->rollback_res = res = attempt_rollback(scb, msg, target); if (res != NO_ERR) { log_warn("\nWarning: Apply rollback of transaction " "%llu failed (%s)", (unsigned long long)txcb->txid, get_error_string(res)); } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* handle_callback */ /******************************************************************** * FUNCTION apply_commit_deletes * * Apply the requested commit delete operations * * Invoke all the AGT_CB_COMMIT callbacks for a * source and target and write operation * * INPUTS: * scb == session control block * msg == incoming commit rpc_msg_t in progress * target == target database (NCX_CFGID_RUNNING) * candval == value struct from the candidate config * runval == value struct from the running config * * OUTPUTS: * rpc_err_rec_t structs may be malloced and added * to the msg->mhsr.errQ * * RETURNS: * status *********************************************************************/ static status_t apply_commit_deletes (ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *candval, val_value_t *runval) { val_value_t *curval, *nextval; status_t res = NO_ERR; if (candval == NULL || runval == NULL) { return NO_ERR; } /* go through running config * if the matching node is not in the candidate, * then delete that node in the running config as well */ for (curval = val_get_first_child(runval); curval != NULL && res == NO_ERR; curval = nextval) { nextval = val_get_next_child(curval); /* check only database config nodes */ if (obj_is_data_db(curval->obj) && obj_is_config(curval->obj)) { /* check if node deleted in source */ val_value_t *matchval = val_first_child_match(candval, curval); if (!matchval) { /* prevent the agt_val code from ignoring this node */ val_set_dirty_flag(curval); /* deleted in the source, so delete in the target */ res = handle_callback(AGT_CB_APPLY, OP_EDITOP_DELETE, scb, msg, target, NULL, curval, runval); assert(res==NO_ERR); } else { res = apply_commit_deletes(scb, msg, target, matchval, curval); assert(res==NO_ERR); } } /* else skip non-config database node */ } return res; } /* apply_commit_deletes */ /******************************************************************** * FUNCTION compare_unique_testsets * * Compare 2 Qs of val_unique_t structs * * INPUTS: * uni1Q == Q of val_unique_t structs for value1 * uni2Q == Q of val_unique_t structs for value2 * * RETURNS: * TRUE if compare test is equal * FALSE if compare test not equal *********************************************************************/ static boolean compare_unique_testsets (dlq_hdr_t *uni1Q, dlq_hdr_t *uni2Q) { /* compare the 2 Qs of values */ val_unique_t *uni1 = (val_unique_t *)dlq_firstEntry(uni1Q); val_unique_t *uni2 = (val_unique_t *)dlq_firstEntry(uni2Q); while (uni1 && uni2) { status_t res = NO_ERR; boolean cmpval = xpath1_compare_nodeset_results(uni1->pcb, uni1->pcb->result, uni2->pcb->result, &res); if (res != NO_ERR) { // log_error()!! return FALSE; } if (!cmpval) { return FALSE; } uni1 = (val_unique_t *)dlq_nextEntry(uni1); uni2 = (val_unique_t *)dlq_nextEntry(uni2); } return TRUE; } /* compare_unique_testsets */ /******************************************************************** * FUNCTION new_unique_set * Malloc and init a new unique test set * * \return the malloced data structure *********************************************************************/ static unique_set_t * new_unique_set (void) { unique_set_t *uset = m__getObj(unique_set_t); if (uset == NULL) { return NULL; } memset(uset, 0x0, sizeof(unique_set_t)); dlq_createSQue(&uset->uniqueQ); return uset; } /* new_unique_set */ /******************************************************************** * FUNCTION free_unique_set * Clean and free a unique test set * * \param uset the data structure to clean and free *********************************************************************/ static void free_unique_set (unique_set_t *uset) { if (uset == NULL) { return; } val_unique_t *unival; while (!dlq_empty(&uset->uniqueQ)) { unival = (val_unique_t *)dlq_deque(&uset->uniqueQ); val_free_unique(unival); } m__free(uset); } /* free_unique_set */ /******************************************************************** * FUNCTION make_unique_testset * * Construct a Q of val_unique_t records, each with the * current value of the corresponding obj_unique_comp_t * If a node is not present the test will be stopped, * ERR_NCX_CANCELED will be returned in the status parm, * Any nodes in the resultQ will be left there * and need to be deleted * * INPUTS: * curval == value to run test for * unidef == obj_unique_t to process * root == XPath docroot to use * resultQ == Queue header to store the val_unique_t records * freeQ == Queue of free val_unique_t records to check first * * OUTPUTS: * resultQ has zero or more val_unique_t structs added * which need to be freed with the val_free_unique fn * * RETURNS: * status of the operation, NO_ERR if all nodes found and stored OK *********************************************************************/ static status_t make_unique_testset (val_value_t *curval, obj_unique_t *unidef, val_value_t *root, dlq_hdr_t *resultQ, dlq_hdr_t *freeQ) { /* need to get a non-const pointer to the module */ if (curval->obj == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } ncx_module_t *mod = curval->obj->tkerr.mod; status_t retres = NO_ERR; obj_unique_comp_t *unicomp = obj_first_unique_comp(unidef); if (!unicomp) { /* really should not happen */ return ERR_NCX_CANCELED; } /* for each unique component, get the descendant * node that is specifies and save it in a val_unique_t */ while (unicomp && retres == NO_ERR) { if (unicomp->isduplicate) { unicomp = obj_next_unique_comp(unicomp); continue; } xpath_pcb_t *targpcb = NULL; status_t res = xpath_find_val_unique(curval, mod, unicomp->xpath, root, FALSE, &targpcb); if (res != NO_ERR) { retres = ERR_NCX_CANCELED; xpath_free_pcb(targpcb); continue; } /* skip the entire unique test for this list entry * if this is an empty node-set */ if (xpath_get_first_resnode(targpcb->result) == NULL) { retres = ERR_NCX_CANCELED; xpath_free_pcb(targpcb); continue; } val_unique_t *unival = (val_unique_t *)dlq_deque(freeQ); if (!unival) { unival = val_new_unique(); if (!unival) { retres = ERR_INTERNAL_MEM; xpath_free_pcb(targpcb); continue; } } unival->pcb = targpcb; dlq_enque(unival, resultQ); unicomp = obj_next_unique_comp(unicomp); } return retres; } /* make_unique_testset */ /******************************************************************** * FUNCTION one_unique_stmt_check * * Run the unique-stmt test for the specified list object type * * INPUTS: * scb == session control block (may be NULL; no session stats) * msg == xml_msg_hdr t from msg in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * ct == commit test record to use * root == XPath docroot to use * unidef == obj_unique_t to process * uninum == ordinal ID for this unique-stmt * * OUTPUTS: * if msg not NULL: * msg->msg_errQ may have rpc_err_rec_t structs added to it * which must be freed by the called with the * rpc_err_free_record function * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ static status_t one_unique_stmt_check (ses_cb_t *scb, xml_msg_hdr_t *msg, agt_cfg_commit_test_t *ct, val_value_t *root, obj_unique_t *unidef, uint32 uninum) { dlq_hdr_t uniQ, freeQ, usetQ; val_unique_t *unival; unique_set_t *uset; assert( ct && "ct is NULL!" ); assert( ct->result && "result is NULL!" ); assert( unidef && "unidef is NULL!" ); assert( unidef->xpath && "xpath is NULL!" ); status_t retres = NO_ERR; dlq_createSQue(&uniQ); // Q of val_unique_t dlq_createSQue(&freeQ); // Q of val_unique_t dlq_createSQue(&usetQ); // Q of unique_set_t if (LOGDEBUG3) { log_debug3("\nunique_chk: %s:%s start unique # %u (%s)", obj_get_mod_name(ct->obj), obj_get_name(ct->obj), uninum, unidef->xpath); } /* create a unique test set for each list instance that has * complete tuple to test; skip all instances with partial tuples */ xpath_resnode_t *resnode = xpath_get_first_resnode(ct->result); for (; resnode && retres == NO_ERR; resnode = xpath_get_next_resnode(resnode)) { val_value_t *valnode = xpath_get_resnode_valptr(resnode); status_t res = make_unique_testset(valnode, unidef, root, &uniQ, &freeQ); if (res == NO_ERR) { /* this valnode provided a complete tuple so it will be * used in the compare test */ uset = new_unique_set(); if (uset == NULL) { retres = ERR_INTERNAL_MEM; continue; } dlq_block_enque(&uniQ, &uset->uniqueQ); uset->valnode = valnode; dlq_enque(uset, &usetQ); } else if (res == ERR_NCX_CANCELED) { dlq_block_enque(&uniQ, &freeQ); } else { retres = res; } } /* done with these 2 queues */ while (!dlq_empty(&uniQ)) { unival = (val_unique_t *)dlq_deque(&uniQ); val_free_unique(unival); } while (!dlq_empty(&freeQ)) { unival = (val_unique_t *)dlq_deque(&freeQ); val_free_unique(unival); } if (retres == NO_ERR) { /* go through all the test sets and compare them to each other * this is a brute force compare N to N+1 .. last moving N * through the list until all entries have been compared to * each other and all unique violations recorded; * As compare is done, delete old set1 since no longer needed */ while (!dlq_empty(&usetQ)) { unique_set_t *set1 = (unique_set_t *)dlq_deque(&usetQ); if (set1 && set1->valnode->res != ERR_NCX_UNIQUE_TEST_FAILED) { unique_set_t *set2 = (unique_set_t *)dlq_firstEntry(&usetQ); for (; set2; set2 = (unique_set_t *)dlq_nextEntry(set2)) { if (set2->valnode->res == ERR_NCX_UNIQUE_TEST_FAILED) { // already compared this to rest of list instances // if it is flagged with a unique-test failed error continue; } if (compare_unique_testsets(&set1->uniqueQ, &set2->uniqueQ)) { /* 2 lists have the same values so generate an error */ agt_record_unique_error(scb, msg, set1->valnode, &set1->uniqueQ); set1->valnode->res = ERR_NCX_UNIQUE_TEST_FAILED; retres = ERR_NCX_UNIQUE_TEST_FAILED; } } } free_unique_set(set1); } } while (!dlq_empty(&usetQ)) { uset = (unique_set_t *)dlq_deque(&usetQ); free_unique_set(uset); } return retres; } /* one_unique_stmt_check */ /******************************************************************** * FUNCTION unique_stmt_check * * Check for the proper uniqueness of the tuples within * the set of list instances for the specified node * * INPUTS: * scb == session control block (may be NULL; no session stats) * msg == xml_msg_hdr t from msg in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * ct == commit test record to use * root == XPath docroot to use * * OUTPUTS: * if msg not NULL: * msg->msg_errQ may have rpc_err_rec_t structs added to it * which must be freed by the called with the * rpc_err_free_record function * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ static status_t unique_stmt_check (ses_cb_t *scb, xml_msg_hdr_t *msg, agt_cfg_commit_test_t *ct, val_value_t *root) { obj_unique_t *unidef = obj_first_unique(ct->obj); uint32 uninum = 0; status_t retres = NO_ERR; /* try to catch as many errors as possible, so keep going * even if an error recorded already */ while (unidef) { ++uninum; if (unidef->isconfig) { status_t res = one_unique_stmt_check(scb, msg, ct, root, unidef, uninum); CHK_EXIT(res, retres); } unidef = obj_next_unique(unidef); } return retres; } /* unique_stmt_check */ /******************************************************************** * FUNCTION instance_xpath_check * * Check the leafref or instance-identifier leaf or leaf-list node * Test 'require-instance' for the NCX_BT_LEAFREF * and NCX_BT_INSTANCE_ID data types * * INPUTS: * scb == session control block (may be NULL; no session stats) * msg == xml_msg_hdr t from msg in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * val == value node to check * root == config root for 'val' * layer == NCX layer calling this function (for error purposes only) * * OUTPUTS: * if msg not NULL: * msg->msg_errQ may have rpc_err_rec_t structs added to it * which must be freed by the called with the * rpc_err_free_record function * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ static status_t instance_xpath_check (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, val_value_t *root, ncx_layer_t layer) { xpath_result_t *result; xpath_pcb_t *xpcb; ncx_errinfo_t *errinfo; typ_def_t *typdef; boolean constrained, fnresult; status_t res, validateres; res = NO_ERR; errinfo = NULL; typdef = obj_get_typdef(val->obj); switch (val->btyp) { case NCX_BT_LEAFREF: /* do a complete parsing to retrieve the * instance that matched; this is always constrained * to 1 of the instances that exists at commit-time */ xpcb = typ_get_leafref_pcb(typdef); if (!val->xpathpcb) { val->xpathpcb = xpath_clone_pcb(xpcb); if (!val->xpathpcb) { res = ERR_INTERNAL_MEM; } } if (res == NO_ERR) { assert( scb && "scb is NULL!" ); result = xpath1_eval_expr(val->xpathpcb, val, root, FALSE, TRUE, &res); if (result && res == NO_ERR) { /* check result: the string value in 'val' * must match one of the values in the * result set */ fnresult = xpath1_compare_result_to_string(val->xpathpcb, result, VAL_STR(val), &res); if (res == NO_ERR && !fnresult) { /* did not match any of the * current instances (13.5) */ res = ERR_NCX_MISSING_VAL_INST; } } xpath_free_result(result); } if (res != NO_ERR) { val->res = res; agt_record_error_errinfo(scb, msg, layer, res, NULL, NCX_NT_OBJ, val->obj, NCX_NT_VAL, val, errinfo); } break; case NCX_BT_INSTANCE_ID: /* do a complete parsing to retrieve the * instance that matched, just checking the * require-instance flag */ result = NULL; constrained = typ_get_constrained(typdef); validateres = val->xpathpcb->validateres; if (validateres == NO_ERR) { assert( scb && "scb is NULL!" ); result = xpath1_eval_xmlexpr(scb->reader, val->xpathpcb, val, root, FALSE, FALSE, &res); if (result) { xpath_free_result(result); } if (!constrained) { if (!NEED_EXIT(res)) { res = NO_ERR; } } if (res != NO_ERR) { val->res = res; agt_record_error(scb, msg, layer, res, NULL, NCX_NT_OBJ, val->obj, NCX_NT_VAL, val); } } break; default: ; } return res; } /* instance_xpath_check */ /******************************************************************** * FUNCTION instance_check * * Check for the proper number of object instances for * the specified value struct. Checks the direct accessible * children of 'val' only!!! * * The top-level value set passed cannot represent a choice * or a case within a choice. * * INPUTS: * scb == session control block (may be NULL; no session stats) * msg == xml_msg_hdr t from msg in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * obj == object template for child node in valset to check * val == val_value_t list, leaf-list, or container to check * valroot == root node of the database * layer == NCX layer calling this function (for error purposes only) * * OUTPUTS: * if msg not NULL: * msg->msg_errQ may have rpc_err_rec_t structs added to it * which must be freed by the called with the * rpc_err_free_record function * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ static status_t instance_check (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, val_value_t *val, val_value_t *valroot, ncx_layer_t layer) { /* skip this node if it is non-config */ if (!obj_is_config(val->obj)) { if (LOGDEBUG3) { log_debug3("\ninstance_chk: skipping r/o node '%s:%s'", obj_get_mod_name(val->obj), val->name); } return NO_ERR; } /* check if the child node is config * this function only tests server requirements * and ignores mandatory read-only nodes; * otherwise the test on candidate would always fail */ if (!obj_is_config(obj)) { if (LOGDEBUG3) { log_debug3("\ninstance_chk: skipping r/o node '%s:%s'", obj_get_mod_name(obj), obj_get_name(obj)); } return NO_ERR; } val_value_t *errval = NULL; xmlChar *instbuff = NULL; status_t res = NO_ERR, res2 = NO_ERR; // do not need to check again to see if conditional object is present //iqual = val_get_cond_iqualval(val, valroot, obj); ncx_iqual_t iqual = obj_get_iqualval(obj); boolean cond = FALSE; boolean whendone = FALSE; uint32 whencnt = 0; boolean minerr = FALSE; boolean maxerr = FALSE; uint32 minelems = 0; uint32 maxelems = 0; boolean minset = obj_get_min_elements(obj, &minelems); boolean maxset = obj_get_max_elements(obj, &maxelems); uint32 cnt = val_instance_count(val, obj_get_mod_name(obj), obj_get_name(obj)); if (LOGDEBUG3) { if (!minset) { switch (iqual) { case NCX_IQUAL_ONE: case NCX_IQUAL_1MORE: minelems = 1; break; case NCX_IQUAL_ZMORE: case NCX_IQUAL_OPT: minelems = 0; break; default: SET_ERROR(ERR_INTERNAL_VAL); } } if (!maxset) { switch (iqual) { case NCX_IQUAL_ONE: case NCX_IQUAL_OPT: maxelems = 1; break; case NCX_IQUAL_1MORE: case NCX_IQUAL_ZMORE: maxelems = 0; break; default: SET_ERROR(ERR_INTERNAL_VAL); } } char buff[NCX_MAX_NUMLEN]; if (maxelems) { snprintf(buff, sizeof(buff), "%u", maxelems); } log_debug3("\ninstance_check '%s:%s' against '%s:%s'\n" " (cnt=%u, min=%u, max=%s)", obj_get_mod_name(obj), obj_get_name(obj), obj_get_mod_name(val->obj), val->name, cnt, minelems, maxelems ? buff : "unbounded"); } if (minset) { if (cnt < minelems) { if (cnt == 0) { /* need to check if this node is conditional because * of when stmt, and if so, whether when-cond is false */ res = val_check_obj_when(val, valroot, NULL, obj, &cond, &whencnt); if (res == NO_ERR) { whendone = TRUE; if (whencnt && !cond) { if (LOGDEBUG2) { log_debug2("\nwhen_chk: skip false when-stmt for " "node '%s:%s'", obj_get_mod_name(obj), obj_get_name(obj)); } return NO_ERR; } } else { agt_record_error(scb, msg, layer, res, NULL, NCX_NT_NONE, NULL, NCX_NT_VAL, errval); return res; } } /* not enough instances error */ minerr = TRUE; res = ERR_NCX_MIN_ELEMS_VIOLATION; val->res = res; if (cnt) { /* use the first child instance as the * value node for the error-path */ errval = val_find_child(val, obj_get_mod_name(obj), obj_get_name(obj)); agt_record_error(scb, msg, layer, res, NULL, NCX_NT_NONE, NULL, NCX_NT_VAL, errval); } else { /* need to construct a string error-path */ instbuff = NULL; res2 = val_gen_split_instance_id(msg, val, NCX_IFMT_XPATH1, obj_get_nsid(obj), obj_get_name(obj), FALSE, &instbuff); if (res2 == NO_ERR && instbuff) { agt_record_error(scb, msg, layer, res, NULL, NCX_NT_NONE, NULL, NCX_NT_STRING, instbuff); } else { agt_record_error(scb, msg, layer, res, NULL, NCX_NT_NONE, NULL, NCX_NT_VAL, val); } if (instbuff) { m__free(instbuff); } } } } if (maxset) { if (cnt > maxelems) { maxerr = TRUE; res = ERR_NCX_MAX_ELEMS_VIOLATION; val->res = res; /* too many instances error * need to find all the extra instances * and mark the extras as errors or they will * not get removed later */ val_set_extra_instance_errors(val, obj_get_mod_name(obj), obj_get_name(obj), maxelems); /* use the first extra child instance as the * value node for the error-path * this should always find a node, since 'cnt' > 0 */ errval = val_find_child(val, obj_get_mod_name(obj), obj_get_name(obj)); uint32 i = 1; while (errval && i <= maxelems) { errval = val_get_next_child(errval); i++; } agt_record_error(scb, msg, layer, res, NULL, NCX_NT_OBJ, obj, NCX_NT_VAL, (errval) ? errval : val); } } switch (iqual) { case NCX_IQUAL_ONE: case NCX_IQUAL_1MORE: if (cnt == 0 && !minerr) { /* need to check if this node is conditional because * of when stmt, and if so, whether when-cond is false */ if (!whendone) { cond = FALSE; whencnt = 0; res = val_check_obj_when(val, valroot, NULL, obj, &cond, &whencnt); if (res == NO_ERR) { if (whencnt && !cond) { if (LOGDEBUG2) { log_debug2("\nwhen_chk: skip false when-stmt for " "node '%s:%s'", obj_get_mod_name(obj), obj_get_name(obj)); } return NO_ERR; } } else { agt_record_error(scb, msg, layer, res, NULL, NCX_NT_NONE, NULL, NCX_NT_VAL, errval); return res; } } /* make sure this object is not an NP container with * mandatory descendant nodes which also have a when-stmt * !!! For now, not clear if when-stmt and mandatory-stmt * !!! together can be enforced if the context node does * !!! not exist; creating a dummy context node changes * !!! the data model. */ if (obj_is_np_container(obj) && !obj_is_mandatory_when_ex(obj, TRUE)) { /* this NP container is mandatory but there are when-stmts * that go with the mandatory nodes, so do not generate * an error for this NP container. If this container * is non-empty then the when-tests will be run for * those runs after test for this node exits */ ; } else { /* missing single parameter (13.5) */ res = ERR_NCX_MISSING_VAL_INST; val->res = res; /* need to construct a string error-path */ instbuff = NULL; res2 = val_gen_split_instance_id(msg, val, NCX_IFMT_XPATH1, obj_get_nsid(obj), obj_get_name(obj), FALSE, &instbuff); if (res2 == NO_ERR && instbuff) { agt_record_error(scb, msg, layer, res, NULL, NCX_NT_OBJ, obj, NCX_NT_STRING, instbuff); } else { agt_record_error(scb, msg, layer, res, NULL, NCX_NT_OBJ, obj, NCX_NT_VAL, val); } if (instbuff) { m__free(instbuff); } } } if (iqual == NCX_IQUAL_1MORE) { break; } /* else fall through */ case NCX_IQUAL_OPT: if (cnt > 1 && !maxerr) { /* too many parameters */ val_set_extra_instance_errors(val, obj_get_mod_name(obj), obj_get_name(obj), 1); res = ERR_NCX_EXTRA_VAL_INST; val->res = res; /* use the first extra child instance as the * value node for the error-path */ errval = val_find_child(val, obj_get_mod_name(obj), obj_get_name(obj)); uint32 i = 1; while (errval && i < maxelems) { errval = val_get_next_child(errval); i++; } agt_record_error(scb, msg, layer, res, NULL, NCX_NT_OBJ, obj, NCX_NT_VAL, (errval) ? errval : val); } break; case NCX_IQUAL_ZMORE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); val->res = res; agt_record_error(scb, msg, layer, res, NULL, NCX_NT_OBJ, obj, NCX_NT_VAL, val); } return res; } /* instance_check */ /******************************************************************** * FUNCTION choice_check_agt * * Agent version of ncx/val_util.c/choice_check * * Check a val_value_t struct against its expected OBJ * for instance validation: * * - choice validation: * only one case allowed if the data type is choice * Only issue errors based on the instance test qualifiers * * The input is checked against the specified obj_template_t. * * INPUTS: * scb == session control block (may be NULL; no session stats) * msg == xml_msg_hdr t from msg in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * choicobj == object template for the choice to check * val == parent val_value_t list or container to check * valroot == root node of the database * layer == NCX layer calling this function (for error purposes only) * * OUTPUTS: * if msg not NULL: * msg->msg_errQ may have rpc_err_rec_t structs added to it * which must be freed by the called with the * rpc_err_free_record function * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ static status_t choice_check_agt (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *choicobj, val_value_t *val, val_value_t *valroot, ncx_layer_t layer) { status_t res = NO_ERR, retres = NO_ERR; if (LOGDEBUG3) { log_debug3("\nchoice_check_agt: check '%s:%s' against '%s:%s'", obj_get_mod_name(choicobj), obj_get_name(choicobj), obj_get_mod_name(val->obj), val->name); } /* Go through all the child nodes for this object * and look for choices against the value set to see if each * a choice case is present in the correct number of instances. * * The current value could never be a OBJ_TYP_CHOICE since * those nodes are not stored in the val_value_t tree * Instead, it is the parent of the choice object, * and the accessible case nodes will be child nodes * of that complex parent type */ val_value_t *chval = val_get_choice_first_set(val, choicobj); if (!chval) { if (obj_is_mandatory(choicobj)) { /* error missing choice (13.6) */ res = ERR_NCX_MISSING_CHOICE; agt_record_error(scb, msg, layer, res, NULL, NCX_NT_VAL, val, NCX_NT_VAL, val); } return res; } /* else a choice was selected * first make sure all the mandatory case * objects are present */ obj_template_t *testobj = obj_first_child(chval->casobj); for (; testobj != NULL; testobj = obj_next_child(testobj)) { res = instance_check(scb, msg, testobj, val, valroot, layer); CHK_EXIT(res, retres); /* errors already recorded if other than NO_ERR */ } /* check if any objects from other cases are present */ val_value_t *testval = val_get_choice_next_set(choicobj, chval); while (testval) { if (val123_get_case_for_choice(choicobj, testval) != val123_get_case_for_choice(choicobj, chval)) { /* error: extra case object in this choice */ retres = res = ERR_NCX_EXTRA_CHOICE; agt_record_error(scb, msg, layer, res, NULL, NCX_NT_OBJ, choicobj, NCX_NT_VAL, testval); } testval = val_get_choice_next_set(choicobj, testval); } if (val->res == NO_ERR) { val->res = retres; } return retres; } /* choice_check_agt */ /******************************************************************** * Perform a must statement check of the object tree. * Check for any must-stmts in the object tree and validate the Xpath * expression against the complete database 'root' and current * context node 'curval'. * * \param scb session control block (may be NULL; no session stats) * \param msg xml_msg_hdr t from msg in progress, NULL means no RPC-ERROR recording. * \param root val_value_t or the target database root to validate * \param curval val_value_t for the current context node in the tree * \return NO_ERR if there are no validation errors. *********************************************************************/ static status_t must_stmt_check ( ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *root, val_value_t *curval ) { status_t res = NO_ERR; obj_template_t *obj = curval->obj; if ( !obj_is_config(obj) ) { return NO_ERR; } dlq_hdr_t *mustQ = obj_get_mustQ(obj); if ( !mustQ || dlq_empty( mustQ ) ) { /* There are no must statements, return NO ERR */ return NO_ERR; } /* execute all the must tests top down, so* foo/bar errors are reported * before /foo/bar/child */ xpath_pcb_t *must = (xpath_pcb_t *)dlq_firstEntry(mustQ); for ( ; must; must = (xpath_pcb_t *)dlq_nextEntry(must)) { res = NO_ERR; xpath_result_t *result = xpath1_eval_expr( must, curval, root, FALSE, TRUE, &res ); if ( !result || res != NO_ERR ) { log_error("\nmust_chk: failed for %s:%s (%s) expr '%s'", obj_get_mod_name(obj), curval->name, get_error_string(res), must->exprstr ); if (res == NO_ERR) { res = ERR_INTERNAL_VAL; SET_ERROR( res ); } } else if (!xpath_cvt_boolean(result)) { log_debug2("\nmust_chk: false for %s:%s expr '%s'", obj_get_mod_name(obj), curval->name, must->exprstr); res = ERR_NCX_MUST_TEST_FAILED; } else { log_debug3("\nmust_chk: OK for %s:%s expr '%s'", obj_get_mod_name(obj), curval->name, must->exprstr); } if ( NO_ERR != res ) { agt_record_error_errinfo(scb, msg, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, must->exprstr, NCX_NT_VAL, curval, ( ( ncx_errinfo_set( &must->errinfo) ) ? &must->errinfo : NULL ) ); if ( terminate_parse( res ) ) { return res; } curval->res = res; } xpath_free_result(result); } if(res == NO_ERR && curval->res != NO_ERR) { return curval->res; } return res; } /* must_stmt_check */ /******************************************************************** * FUNCTION run_when_stmt_check * * Run one when-stmt test on a node * INPUTS: * scb == session control block (may be NULL; no session stats) * msg == xml_msg_hdr t from msg in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * root == val_value_t or the target database root to validate * curval == val_value_t for the current context node in the tree * configmode == TRUE to test config-TRUE * FALSE to test config=FALSE * deleteQ == address of Q for deleted descendants * deleteme == address of return delete flag * * OUTPUTS: * if msg not NULL: * msg->msg_errQ may have rpc_err_rec_t structs added to it * which must be freed by the called with the * rpc_err_free_record function * txcb->deadnodeQ will have any deleted instance entries * due to false when-stmt expressions * * RETURNS: * status of the operation, NO_ERR or ERR_INTERNAL_MEM most likely *********************************************************************/ static status_t run_when_stmt_check (ses_cb_t *scb, xml_msg_hdr_t *msg, agt_cfg_transaction_t *txcb, val_value_t *root, val_value_t *curval) { obj_template_t *obj = curval->obj; status_t res = NO_ERR; boolean condresult = FALSE; uint32 whencount = 0; res = val_check_obj_when(curval->parent, root, curval, obj, &condresult, &whencount); if (res != NO_ERR) { log_error("\nError: when_check: failed for %s:%s (%s)", obj_get_mod_name(obj), obj_get_name(obj), get_error_string(res)); agt_record_error(scb, msg, NCX_LAYER_OPERATION, res, NULL, NCX_NT_NONE, NULL, NCX_NT_VAL, curval); } else if (whencount == 0) { if (LOGDEBUG) { log_debug("\nwhen_chk: no when-tests found for node '%s:%s'", obj_get_mod_name(obj), curval->name); } } else if (!condresult) { if (LOGDEBUG2) { log_debug2("\nwhen_chk: test false for node '%s:%s'", obj_get_mod_name(obj), curval->name); } /* check if this session is allowed to delete this node * and make sure this node is not partially locked */ if (!agt_acm_val_write_allowed(msg, scb->username, NULL, curval, OP_EDITOP_DELETE)) { res = ERR_NCX_ACCESS_DENIED; agt_record_error(scb, msg, NCX_LAYER_OPERATION, res, NULL, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } uint32 lockid = 0; res = val_write_ok(curval, OP_EDITOP_DELETE, SES_MY_SID(scb), TRUE, &lockid); if (res != NO_ERR) { agt_record_error(scb, msg, NCX_LAYER_OPERATION, res, NULL, NCX_NT_UINT32_PTR, &lockid, NCX_NT_VAL, curval); return res; } /* need to delete the current value */ agt_cfg_nodeptr_t *nodeptr = agt_cfg_new_nodeptr(curval); if (nodeptr == NULL) { res = ERR_INTERNAL_MEM; agt_record_error(scb, msg, NCX_LAYER_OPERATION, res, NULL, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } log_debug("\nMarking false when-stmt as deleted %s:%s", obj_get_mod_name(obj), obj_get_name(obj)); dlq_enque(nodeptr, &txcb->deadnodeQ); VAL_MARK_DELETED(curval); } else { if (LOGDEBUG3) { log_debug3("\nwhen_chk: test passed for node '%s:%s'", obj_get_mod_name(obj), curval->name); } } return res; } /* run_when_stmt_check */ /******************************************************************** * FUNCTION rpc_when_stmt_check * * Check for any when-stmts in the object tree and validate the Xpath * expression against the complete database 'root' and current * context node 'curval'. ONLY USED FOR RPC INPUT CHECKING!! * * INPUTS: * scb == session control block (may be NULL; no session stats) * rpcmsg == rpc msg header for audit purposes * msg == xml_msg_hdr t from msg in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * root == val_value_t or the target database root to validate * curval == val_value_t for the current context node in the tree * * OUTPUTS: * if msg not NULL: * msg->msg_errQ may have rpc_err_rec_t structs added to it * which must be freed by the called with the * rpc_err_free_record function * * RETURNS: * status of the operation *********************************************************************/ static status_t rpc_when_stmt_check (ses_cb_t *scb, rpc_msg_t *rpcmsg, xml_msg_hdr_t *msg, val_value_t *root, val_value_t *curval) { obj_template_t *obj = curval->obj; status_t retres = NO_ERR; boolean condresult = FALSE; uint32 whencount = 0; retres = val_check_obj_when(curval->parent, root, curval, obj, &condresult, &whencount); if (retres != NO_ERR) { log_error("\nError: when_check: failed for %s:%s (%s)", obj_get_mod_name(obj), obj_get_name(obj), get_error_string(retres)); agt_record_error(scb, msg, NCX_LAYER_OPERATION, retres, NULL, NCX_NT_NONE, NULL, NCX_NT_VAL, curval); return retres; } else if (whencount && !condresult) { retres = ERR_NCX_RPC_WHEN_FAILED; agt_record_error(scb, msg, NCX_LAYER_OPERATION, retres, NULL, NCX_NT_VAL, curval, NCX_NT_VAL, curval); return retres; } else { if (LOGDEBUG3 && whencount) { log_debug3("\nwhen_chk: test passed for node '%s:%s'", obj_get_mod_name(obj), curval->name); } } /* recurse for every child node until leafs are hit */ val_value_t *chval = val_get_first_child(curval); for (; chval != NULL && retres == NO_ERR; chval = val_get_next_child(chval)) { if (!obj_is_config(chval->obj)) { continue; } if (obj_is_root(chval->obj)) { /* do not dive into a node parameter * while processing an RPC input node */ continue; } retres = rpc_when_stmt_check(scb, rpcmsg, msg, root, chval); } return retres; } /* rpc_when_stmt_check */ /******************************************************************** * Perform a must statement check of the object tree. * USED FOR RPC INPUT TESTING ONLY * Check for any must-stmts in the object tree and validate the Xpath * expression against the complete database 'root' and current * context node 'curval'. * * \param scb session control block (may be NULL; no session stats) * \param msg xml_msg_hdr t from msg in progress, NULL means no RPC-ERROR recording. * \param root val_value_t or the target database root to validate * \param curval val_value_t for the current context node in the tree * \return NO_ERR if there are no validation errors. *********************************************************************/ static status_t rpc_must_stmt_check ( ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *root, val_value_t *curval ) { status_t res; status_t firstError = NO_ERR; obj_template_t *obj = curval->obj; if ( !obj_is_config(obj) ) { return NO_ERR; } dlq_hdr_t *mustQ = obj_get_mustQ(obj); if ( !mustQ || dlq_empty( mustQ ) ) { /* There are no must statements, return NO ERR */ return NO_ERR; } /* execute all the must tests top down, so* foo/bar errors are reported * before /foo/bar/child */ xpath_pcb_t *must = (xpath_pcb_t *)dlq_firstEntry(mustQ); for ( ; must; must = (xpath_pcb_t *)dlq_nextEntry(must)) { res = NO_ERR; xpath_result_t *result = xpath1_eval_expr( must, curval, root, FALSE, TRUE, &res ); if ( !result || res != NO_ERR ) { log_error("\nrpc_must_chk: failed for %s:%s (%s) expr '%s'", obj_get_mod_name(obj), curval->name, get_error_string(res), must->exprstr ); if (res == NO_ERR) { res = ERR_INTERNAL_VAL; SET_ERROR( res ); } } else if (!xpath_cvt_boolean(result)) { log_debug2("\nrpc_must_chk: false for %s:%s expr '%s'", obj_get_mod_name(obj), curval->name, must->exprstr); res = ERR_NCX_MUST_TEST_FAILED; } else { log_debug3("\nrpc_must_chk: OK for %s:%s expr '%s'", obj_get_mod_name(obj), curval->name, must->exprstr); } if ( NO_ERR != res ) { agt_record_error_errinfo(scb, msg, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, must->exprstr, NCX_NT_VAL, curval, ( ( ncx_errinfo_set( &must->errinfo) ) ? &must->errinfo : NULL ) ); if ( terminate_parse( res ) ) { return res; } curval->res = res; firstError = ( firstError != NO_ERR) ? firstError : res; } xpath_free_result(result); } /* recurse for every child node until leafs are hit but only if the parent * must test did not fail will check sibling nodes even if some must-test * already failed; this provides complete errors during validate and * load_running_config */ if ( firstError == NO_ERR ) { val_value_t *chval = val_get_first_child(curval); for ( ; chval; chval = val_get_next_child(chval)) { /* do not dive into parameters and hit database * must-stmts by mistake */ if ( !obj_is_root(chval->obj) ) { res = rpc_must_stmt_check(scb, msg, root, chval); if ( NO_ERR != res ) { if ( terminate_parse( res ) ) { return res; } firstError = ( firstError != NO_ERR) ? firstError : res; } } } } return firstError; } /* rpc_must_stmt_check */ /******************************************************************** * FUNCTION prep_commit_test_node * * Update the commit test instances * * \param scb session control block * \param msghdr XML message header in progress * \param txcb transaction control block to use * \param ct commit test to use * \param root node to check * * \return status *********************************************************************/ static status_t prep_commit_test_node ( ses_cb_t *scb, xml_msg_hdr_t *msghdr, agt_cfg_transaction_t *txcb, agt_cfg_commit_test_t *ct, val_value_t *root ) { status_t res = NO_ERR; /* first get all the instances of this object if needed */ if (ct->result) { if (ct->result_txid == txcb->txid) { log_debug3("\nReusing XPath result for %s", ct->objpcb->exprstr); } else { /* TBD: figure out if older TXIDs are still valid * for this XPath expression */ log_debug3("\nGet all instances of %s", ct->objpcb->exprstr); xpath_free_result(ct->result); ct->result = NULL; } } if (ct->result == NULL) { ct->result_txid = 0; ct->result = xpath1_eval_expr(ct->objpcb, root, root, FALSE, TRUE, &res); if (res != NO_ERR || ct->result->restype != XP_RT_NODESET) { if (res == NO_ERR) { res = ERR_NCX_WRONG_NODETYP; } agt_record_error(scb, msghdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_NONE, NULL, NCX_NT_OBJ, ct->obj); } else { ct->result_txid = txcb->txid; } } return res; } /* prep_commit_test_node */ /******************************************************************** * * Delete all the nodes that have false when-stmt exprs * Also delete empty NP-containers * * \param scb session control block (may be NULL) * \param msghdr XML message header in progress * \param txcb transaction control block to use * \param root root from the target database to use * \param retcount address of return deletecount * \return status *********************************************************************/ static status_t delete_dead_nodes ( ses_cb_t *scb, xml_msg_hdr_t *msghdr, agt_cfg_transaction_t *txcb, val_value_t *root, uint32 *retcount ) { *retcount = 0; agt_profile_t *profile = agt_get_profile(); agt_cfg_commit_test_t *ct = (agt_cfg_commit_test_t *) dlq_firstEntry(&profile->agt_commit_testQ); for (; ct != NULL; ct = (agt_cfg_commit_test_t *)dlq_nextEntry(ct)) { uint32 tests = ct->testflags & AGT_TEST_FL_WHEN; if (tests == 0) { /* no when-stmt tests needed for this node */ continue; } status_t res = prep_commit_test_node(scb, msghdr, txcb, ct, root); if (res != NO_ERR) { return res; } /* run all relevant tests on each node in the result set */ xpath_resnode_t *resnode = xpath_get_first_resnode(ct->result); xpath_resnode_t *nextnode = NULL; for (; resnode != NULL; resnode = nextnode) { nextnode = xpath_get_next_resnode(resnode); val_value_t *valnode = xpath_get_resnode_valptr(resnode); res = run_when_stmt_check(scb, msghdr, txcb, root, valnode); if (res != NO_ERR) { /* treat any when delete error as terminate transaction */ return res; } else if (VAL_IS_DELETED(valnode)) { /* this node has just been flagged when=FALSE so * remove this resnode from the result so it will * not be reused by a commit test */ xpath_delete_resnode(resnode); (*retcount)++; } } } return NO_ERR; } /* delete_dead_nodes */ /******************************************************************** * FUNCTION check_parent_tests * * Check if the specified object has any commit tests * to record in the parent node * * INPUTS: * obj == object to check * flags == address of return testflags * * OUTPUTS: * *flags is set if any tests are needed * *********************************************************************/ static void check_parent_tests (obj_template_t *obj, uint32 *flags) { uint32 numelems = 0; switch (obj->objtype) { case OBJ_TYP_LEAF_LIST: case OBJ_TYP_LIST: if (obj_get_min_elements(obj, &numelems)) { if (numelems > 1) { *flags |= AGT_TEST_FL_MIN_ELEMS; } // else AGT_TEST_FL_MANDATORY will check for 1 instance } numelems = 0; if (obj_get_max_elements(obj, &numelems)) { *flags |= AGT_TEST_FL_MAX_ELEMS; } break; case OBJ_TYP_LEAF: case OBJ_TYP_CONTAINER: *flags |= AGT_TEST_FL_MAX_ELEMS; break; case OBJ_TYP_CHOICE: *flags |= AGT_TEST_FL_CHOICE; break; default: ; } if (obj_is_mandatory(obj) && !obj_is_key(obj)) { *flags |= AGT_TEST_FL_MANDATORY; } } /* check_parent_tests */ /******************************************************************** * FUNCTION add_obj_commit_tests * * Check if the specified object and all its descendants * have any commit tests to record in the commit_testQ * Tests are added in top-down order * TBD: cascade XPath lookup results to nested commmit tests * * Tests done in the parent node: * AGT_TEST_FL_MIN_ELEMS * AGT_TEST_FL_MAX_ELEMS * AGT_TEST_FL_MANDATORY * * Tests done in the object node: * AGT_TEST_FL_MUST * AGT_TEST_FL_UNIQUE * AGT_TEST_FL_XPATH_TYPE * AGT_TEST_FL_WHEN (part of delete_dead_nodes, not this fn) * * INPUTS: * obj == object to check * commit_testQ == address of queue to use to fill with * agt_cfg_commit_test_t structs * rootflags == address of return root node testflags * * OUTPUTS: * commit_testQ will contain an agt_cfg_commit_test_t struct * for each descendant-or-self object found with * commit-time validation tests * *rootflags will be altered if node is top-level and * needs instance testing * RETURNS: * status of the operation, NO_ERR unless internal errors found * or malloc error *********************************************************************/ static status_t add_obj_commit_tests (obj_template_t *obj, dlq_hdr_t *commit_testQ, uint32 *rootflags) { if (skip_obj_commit_test(obj)) { return NO_ERR; } status_t res = NO_ERR; /* check for tests in active config nodes, bottom-up traversal */ uint32 testflags = 0; dlq_hdr_t *mustQ = obj_get_mustQ(obj); ncx_btype_t btyp = obj_get_basetype(obj); obj_template_t *chobj; if (!(obj->objtype == OBJ_TYP_CHOICE || obj->objtype == OBJ_TYP_CASE)) { if (mustQ && !dlq_empty(mustQ)) { testflags |= AGT_TEST_FL_MUST; } if (obj_has_when_stmts(obj)) { testflags |= AGT_TEST_FL_WHEN; } } obj_unique_t *unidef = obj_first_unique(obj); boolean done = FALSE; for (; unidef && !done; unidef = obj_next_unique(unidef)) { if (unidef->isconfig) { testflags |= AGT_TEST_FL_UNIQUE; done = TRUE; } } if (obj_is_top(obj)) { /* need to check if this is a top-level node that * has instance requirements (mandatory/min/max) */ check_parent_tests(obj, rootflags); } if (obj->objtype == OBJ_TYP_CHOICE || obj->objtype == OBJ_TYP_CASE) { /* do not create a commit test record for a choice or case * since they will never be in the data tree, so XPath * will never find them */ testflags = 0; } else { /* set the instance or MANDATORY test bits in the parent, * not in the child nodes for each commit test * check all child nodes to determine if parent needs * to run instance_check */ for (chobj = obj_first_child(obj); chobj != NULL; chobj = obj_next_child(chobj)) { if (skip_obj_commit_test(chobj)) { continue; } check_parent_tests(chobj, &testflags); } typ_def_t *typdef = obj_get_typdef(obj); if (btyp == NCX_BT_LEAFREF || (btyp == NCX_BT_INSTANCE_ID && typ_get_constrained(typdef))) { /* need to check XPath-based leaf to see that the referenced * instance exists in the target data tree */ testflags |= AGT_TEST_FL_XPATH_TYPE; } } if (testflags) { /* need a commit test record for this object */ agt_cfg_commit_test_t *ct = agt_cfg_new_commit_test(); if (ct == NULL) { return ERR_INTERNAL_MEM; } ct->objpcb = xpath_new_pcb(NULL, NULL); if (ct->objpcb == NULL) { agt_cfg_free_commit_test(ct); return ERR_INTERNAL_MEM; } res = obj_gen_object_id_xpath(obj, &ct->objpcb->exprstr); if (res != NO_ERR) { agt_cfg_free_commit_test(ct); return res; } ct->obj = obj; ct->btyp = btyp; ct->testflags = testflags; dlq_enque(ct, commit_testQ); if (LOGDEBUG4) { log_debug4("\nAdded commit_test record for %s testflags=0x%08X", ct->objpcb->exprstr, testflags); } } for (chobj = obj_first_child(obj); chobj != NULL; chobj = obj_next_child(chobj)) { res = add_obj_commit_tests(chobj, commit_testQ, rootflags); if (res != NO_ERR) { return res; } } return res; } /* add_obj_commit_tests */ /******************************************************************** * FUNCTION check_prune_obj * * Check if the specified object test can be reduced or eliminated * from the current commit test * * INPUTS: * obj == commit test object to check; must not be root! * rootval == config root to check * curflags == current test flags * RETURNS: * new testflags or unchanged test flags *********************************************************************/ static uint32 check_prune_obj (obj_template_t *obj, val_value_t *rootval, uint32 curflags) { /* for now just check the top level object to see if * this subtree could have possibly changed; * TBD: come up with fast way to check more, that does * take as much time as just using XPath to get these objects */ boolean done = FALSE; obj_template_t *testobj = obj; while (!done) { if (testobj->parent && !obj_is_root(testobj->parent)) { testobj = testobj->parent; } else { done = TRUE; } } /* find this object in the rootval */ val_value_t *testval = val_find_child(rootval, obj_get_mod_name(testobj), obj_get_name(testobj)); if (testval) { switch (testobj->objtype) { case OBJ_TYP_LEAF: case OBJ_TYP_ANYXML: case OBJ_TYP_CONTAINER: if (val_dirty_subtree(testval)) { /* do not skip this node */ return curflags; } break; default: done = FALSE; while (!done) { if (val_dirty_subtree(testval)) { /* do not skip this node */ return curflags; } else { testval = val_find_next_child(rootval, obj_get_mod_name(testobj), obj_get_name(testobj), testval); if (testval == NULL) { done = TRUE; } } } } } /* skip this node for all tests, since no instances of * the top-level object exist which might have been changed * Relying on the fact that the root-check is always done for * the root node (on the top-level YANG data nodes, so * any instance or mandatory tests on the top-node will * always be done */ //return 0; /* xpath tests can depend on any top level container */ return (curflags & AGT_TEST_FL_XPATH_TYPE); } /* check_prune_obj */ /******************************************************************** * FUNCTION check_no_prune_curedit * * Check if the specified object test is needed from the current * commit test, based on the current edit in the undo record * * INPUTS: * undo == edit operation to check * obj == commit test object to check; must not be root! * RETURNS: * TRUE if found and test needed; FALSE if not found *********************************************************************/ static boolean check_no_prune_curedit (agt_cfg_undo_rec_t *undo, obj_template_t *obj) { obj_template_t *editobj = (undo->newnode) ? undo->newnode->obj : ((undo->curnode) ? undo->curnode->obj : NULL); if (editobj == NULL) { return FALSE; } /* see if the commit test object is a descendant of * the edit object, but only if not deleting the edit * object; do not trigger descendant test on a delete */ boolean done = (undo->editop == OP_EDITOP_DELETE); obj_template_t *testobj = obj; while (!done) { if (editobj == testobj) { return TRUE; // do not prune this object test } if (testobj->parent && !obj_is_root(testobj->parent)) { testobj = testobj->parent; } else { done = TRUE; } } /* see if the edit object is a descendant of the commit test object */ done = FALSE; while (!done) { if (editobj == obj) { return TRUE; // do not prune this object test } if (editobj->parent && !obj_is_root(editobj->parent)) { editobj = editobj->parent; } else { done = TRUE; } } return FALSE; } /* check_no_prune_curedit */ /******************************************************************** * FUNCTION prune_obj_commit_tests * * Check if the specified object test can be reduced or elimated * from the current commit test * * INPUTS: * * OUTPUTS: * *testflags will be altered * RETURNS: * new (or unchanged) test flags *********************************************************************/ static uint32 prune_obj_commit_tests (agt_cfg_transaction_t *txcb, agt_cfg_commit_test_t *ct, val_value_t *rootval, uint32 curflags) { if (curflags & AGT_TEST_FL_MUST) { /* do not prune a must-stmt test since the XPath expression * may reference any arbitrary data node */ return curflags; } agt_cfg_undo_rec_t *undo = NULL; if (txcb->cfg_id == NCX_CFGID_RUNNING) { if (txcb->commitcheck) { /* called from * look for dirty or subtree dirty flags in the rootval * this rootval is reallt candidate->root, so it is * OK to check the dirty flags */ curflags = check_prune_obj(ct->obj, rootval, curflags); } else { if (txcb->edit_type == AGT_CFG_EDIT_TYPE_FULL) { /* called from on external element; * is not supported on the running config * so there is nothing to do for now; do not prune; * force a full test for */ } else if (txcb->edit_type == AGT_CFG_EDIT_TYPE_PARTIAL) { /* called from on running config; * check the edit list; only nodes that have been * edited need to run a commit test */ undo = (agt_cfg_undo_rec_t *)dlq_firstEntry(&txcb->undoQ); while (undo) { if (check_no_prune_curedit(undo, ct->obj)) { return curflags; } undo = (agt_cfg_undo_rec_t *)dlq_nextEntry(undo); } curflags = 0; } // else unknown edit-type! do not prune } } else if (txcb->cfg_id == NCX_CFGID_CANDIDATE) { if (txcb->edit_type == AGT_CFG_EDIT_TYPE_FULL) { /* called from operation on the config */ curflags = check_prune_obj(ct->obj, rootval, curflags); } else if (txcb->edit_type == AGT_CFG_EDIT_TYPE_PARTIAL) { /* called from with test-option=set-then-test * first check all the current edits; because the dirty flags * for any edits from this have not been set yet */ undo = (agt_cfg_undo_rec_t *)dlq_firstEntry(&txcb->undoQ); while (undo) { if (check_no_prune_curedit(undo, ct->obj)) { return curflags; } undo = (agt_cfg_undo_rec_t *)dlq_nextEntry(undo); } /* did not find the object in the current edits so check * pending edits (dirty flags) on the candidate->root target */ curflags = check_prune_obj(ct->obj, rootval, curflags); } // else unknown edit-type! do not prune! } // else unknown config target! do not prune! return curflags; } /* prune_obj_commit_tests */ /******************************************************************** * FUNCTION run_instance_check * * Check for the proper number of object instances for * the specified value struct. * * The top-level value set passed cannot represent a choice * or a case within a choice. * * This function is intended for validating PDUs (RPC requests) * during the PDU processing. It does not check the instance * count or must-stmt expressions for any (ncx:root) * container. This must be done with the agt_val_root_check function. * * INPUTS: * scb == session control block (may be NULL; no session stats) * msg == xml_msg_hdr t from msg in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * valset == val_value_t list, leaf-list, or container to check * valroot == root node of the database * layer == NCX layer calling this function (for error purposes only) * all_levels == TRUE to recurse for all levels * == FALSE to just check the curent level * OUTPUTS: * if msg not NULL: * msg->msg_errQ may have rpc_err_rec_t structs added to it * which must be freed by the called with the * rpc_err_free_record function * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ static status_t run_instance_check (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *valset, val_value_t *valroot, ncx_layer_t layer, boolean all_levels) { assert( valset && "valset is NULL!" ); if (LOGDEBUG4) { log_debug4("\nrun_instance_check: %s:%s start", obj_get_mod_name(valset->obj), valset->name); } status_t res = NO_ERR, retres = NO_ERR; obj_template_t *obj = valset->obj; if (skip_obj_commit_test(obj)) { return NO_ERR; } if (all_levels && val_child_cnt(valset)) { val_value_t *chval = val_get_first_child(valset); for (; chval != NULL; chval = val_get_next_child(chval)) { if (!(obj_is_root(chval->obj) || obj_is_leafy(chval->obj))) { /* recurse for all object types except root, leaf, leaf-list */ res = run_instance_check(scb, msg, chval, valroot, layer, all_levels); CHK_EXIT(res, retres); } } } /* check all the child nodes for correct number of instances */ obj_template_t *chobj = obj_first_child(obj); for (; chobj != NULL; chobj = obj_next_child(chobj)) { if (obj_is_root(chobj)) { continue; } if (chobj->objtype == OBJ_TYP_CHOICE) { res = choice_check_agt(scb, msg, chobj, valset, valroot, layer); } else { res = instance_check(scb, msg, chobj, valset, valroot, layer); } if (res != NO_ERR && valset->res == NO_ERR) { valset->res = res; } CHK_EXIT(res, retres); } return retres; } /* run_instance_check */ /******************************************************************** * FUNCTION run_obj_commit_tests * * Run selected validation tests from section 8 of RFC 6020 * * INPUTS: * profile == agt_profile pointer * scb == session control block (may be NULL; no session stats) * msghdr == XML message header in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * ct == commit test record to use (NULL == root test) * val == context node in data tree to use * root == root of the data tree to use * testmask == bitmask of the tests that are requested * * OUTPUTS: * if msghdr not NULL: * msghdr->msg_errQ may have rpc_err_rec_t * structs added to it which must be freed by the * caller with the rpc_err_free_record function * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ static status_t run_obj_commit_tests (agt_profile_t *profile, ses_cb_t *scb, xml_msg_hdr_t *msghdr, agt_cfg_commit_test_t *ct, val_value_t *val, val_value_t *root, uint32 testmask) { status_t res, retres = NO_ERR; uint32 testflags = (ct) ? ct->testflags : profile->agt_rootflags; testflags &= testmask; if (testflags) { if (LOGDEBUG2) { log_debug2("\nrun obj_commit_tests for %s", (ct) ? ct->objpcb->exprstr : (const xmlChar *)"/"); } } else { if (LOGDEBUG4) { log_debug4("\nskip obj_commit_tests for %s", (ct) ? ct->objpcb->exprstr : (const xmlChar *)"/"); } return NO_ERR; } /* go through all the commit test objects that might need * to be checked for this object */ if (testflags & AGT_TEST_INSTANCE_MASK) { /* need to run the instance tests on the 'val' node */ if (ct) { res = run_instance_check(scb, msghdr, val, root, NCX_LAYER_CONTENT, FALSE); CHK_EXIT(res, retres); } else { /* TBD: figure out how to use the gen_root object * by moving all the data_db nodes into gen_root datadefQ * This requires moving them from mod->datadefQ so that * these 2 functions below will no longer work * Too much code uses this for-loop search approach * so this change is still TBD */ ncx_module_t *mod = ncx_get_first_module(); for (; mod != NULL; mod = ncx_get_next_module(mod)) { obj_template_t *obj = ncx_get_first_data_object(mod); for (; obj != NULL; obj = ncx_get_next_data_object(mod, obj)) { if (skip_obj_commit_test(obj)) { continue; } /* just run instance check on the top level object */ if (obj->objtype == OBJ_TYP_CHOICE) { res = choice_check_agt(scb, msghdr, obj, val, root, NCX_LAYER_CONTENT); } else { res = instance_check(scb, msghdr, obj, val, root, NCX_LAYER_CONTENT); } CHK_EXIT(res, retres); } } } } if (testflags & AGT_TEST_FL_XPATH_TYPE) { res = instance_xpath_check(scb, msghdr, val, root, NCX_LAYER_CONTENT); if (res != NO_ERR) { CHK_EXIT(res, retres); } } if (testflags & AGT_TEST_FL_MUST) { res = must_stmt_check(scb, msghdr, root, val); if (res != NO_ERR) { CHK_EXIT(res, retres); } } // defer AGT_TEST_FL_UNIQUE and process entire resnode list at once return retres; } /* run_obj_commit_tests */ /******************************************************************** * FUNCTION run_obj_unique_tests * * Run unique-stmt tests from section 7.8.3 of RFC 6020 * * INPUTS: * scb == session control block (may be NULL; no session stats) * msghdr == XML message header in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * ct == commit test record to use (NULL == root test) * root == docroot for XPath * OUTPUTS: * if msghdr not NULL: * msghdr->msg_errQ may have rpc_err_rec_t * structs added to it which must be freed by the * caller with the rpc_err_free_record function * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ static status_t run_obj_unique_tests (ses_cb_t *scb, xml_msg_hdr_t *msghdr, agt_cfg_commit_test_t *ct, val_value_t *root) { status_t res = NO_ERR; if (ct->testflags & AGT_TEST_FL_UNIQUE) { log_debug2("\nrun unique tests for %s", ct->objpcb->exprstr); res = unique_stmt_check(scb, msghdr, ct, root); } return res; } /* run_obj_unique_tests */ /******************* E X T E R N F U N C T I O N S ***************/ /******************************************************************** * FUNCTION agt_val_rpc_xpath_check * * Check for any nodes which are present * but have false when-stmts associated * with the node. These are errors and * need to be flagged as unknown-element * * Any false nodes will be removed from the input PDU * and discarded, after the error is recorded. * This prevents false positives or negatives in * the agt_val_instance_check, called after this function * * Also checks any false must-stmts for nodes * which are present (after false when removal) * These are flagged as 'must-violation' errors * as per YANG, 13.4 * * INPUTS: * scb == session control block (may be NULL; no session stats) * rpcmsg == RPC msg header for audit purposes * msg == xml_msg_hdr t from msg in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * rpcinput == RPC input node conceptually under rpcroot * except this rpcinput has no parent node * so a fake one will be termporarily added * to prevent false XPath validation errors * rpcroot == RPC method node. * The conceptual parent of this node * is used as the document root (/rpc == /) * * OUTPUTS: * if false nodes found under : * they are deleted * if msg not NULL: * msg->msg_errQ may have rpc_err_rec_t structs added to it * which must be freed by the called with the * rpc_err_free_record function * * RETURNS: * status of the operation * NO_ERR if no false when or must statements found *********************************************************************/ status_t agt_val_rpc_xpath_check (ses_cb_t *scb, rpc_msg_t *rpcmsg, xml_msg_hdr_t *msg, val_value_t *rpcinput, obj_template_t *rpcroot) { val_value_t *method = NULL; status_t res = NO_ERR; #ifdef DEBUG if (!rpcinput || !rpcroot) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (LOGDEBUG3) { log_debug3("\nagt_val_rpc_xpathchk: %s:%s start", obj_get_mod_name(rpcroot), obj_get_name(rpcroot)); } /* make a dummy tree to align with the XPath code */ method = val_new_value(); if (!method) { return ERR_INTERNAL_MEM; } val_init_from_template(method, rpcroot); /* add the rpc/method-name/input node */ val_add_child(rpcinput, method); /* just check for when-stmt deletes once, since it is an error */ res = rpc_when_stmt_check(scb, rpcmsg, msg, method, rpcinput); /* check if any must expressions apply to * descendent-or-self nodes in the rpcinput node */ if (res == NO_ERR) { res = rpc_must_stmt_check(scb, msg, method, rpcinput); } /* cleanup fake RPC method parent node */ val_remove_child(rpcinput); val_free_value(method); return res; } /* agt_val_rpc_xpath_check */ /******************************************************************** * FUNCTION agt_val_instance_check * * Check for the proper number of object instances for * the specified value struct. * * The top-level value set passed cannot represent a choice * or a case within a choice. * * This function is intended for validating PDUs (RPC requests) * during the PDU processing. It does not check the instance * count or must-stmt expressions for any (ncx:root) * container. This must be dome with the agt_val_root_check function. * * INPUTS: * scb == session control block (may be NULL; no session stats) * msg == xml_msg_hdr t from msg in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * valset == val_value_t list, leaf-list, or container to check * valroot == root node of the database * layer == NCX layer calling this function (for error purposes only) * * OUTPUTS: * if msg not NULL: * msg->msg_errQ may have rpc_err_rec_t structs added to it * which must be freed by the called with the * rpc_err_free_record function * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ status_t agt_val_instance_check (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *valset, val_value_t *valroot, ncx_layer_t layer) { return run_instance_check(scb, msg, valset, valroot, layer, TRUE); } /* agt_val_instance_check */ /******************************************************************** * FUNCTION agt_val_root_check * * !!! Full database validation !!! * Check for the proper number of object instances for * the specified configuration database * Check must and when statements * Check empty NP containers * Check choices (selected case, and it is complete) * * Tests are divided into 3 groups: * A) top-level nodes (child of conceptual root * B) parent data node for child instance tests (mand/min/max) * C) data node referential tests (must/unique/leaf) * * Test pruning * The global variable agt_profile.agt_rootflags is used * to determine if any type (A) commit tests are needed * * The global variable agt_profile.agt_config_state is * used to force complete testing; There are 3 states: * AGT_CFG_STATE_INIT : running config has not been * validated, or validation-in-progress * AGT_CFG_STATE_OK : running config has been validated * and it passed all validation checks * AGT_CFG_STATE_BAD: running config validation has been * attempted and it failed; running config is not valid! * The server will shutdown if * The target nodes in the undoQ records * INPUTS: * scb == session control block (may be NULL; no session stats) * msghdr == XML message header in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * txcb == transaction control block * root == val_value_t for the target config being checked * * OUTPUTS: * if mshdr not NULL: * msghdr->msg_errQ may have rpc_err_rec_t * structs added to it which must be freed by the * caller with the rpc_err_free_record function * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ status_t agt_val_root_check (ses_cb_t *scb, xml_msg_hdr_t *msghdr, agt_cfg_transaction_t *txcb, val_value_t *root) { log_debug3("\nagt_val_root_check: start"); assert ( txcb && "txcb is NULL!" ); assert ( root && "root is NULL!" ); assert ( root->obj && "root->obj is NULL!" ); assert ( obj_is_root(root->obj) && "root obj not config root!" ); agt_profile_t *profile = agt_get_profile(); status_t res = NO_ERR, retres = NO_ERR; /* the commit check is always run on the root because there * are operations such as and that * make it impossible to flag the 'root-dirty' condition * during editing */ res = run_obj_commit_tests(profile, scb, msghdr, NULL, root, root, AGT_TEST_ALL_COMMIT_MASK); if (res != NO_ERR) { profile->agt_load_top_rootcheck_errors = TRUE; CHK_EXIT(res, retres); } /* go through all the commit test objects that might need * to be checked for this commit */ agt_cfg_commit_test_t *ct = (agt_cfg_commit_test_t *) dlq_firstEntry(&profile->agt_commit_testQ); for (; ct != NULL; ct = (agt_cfg_commit_test_t *)dlq_nextEntry(ct)) { uint32 tests = ct->testflags & AGT_TEST_ALL_COMMIT_MASK; /* prune entry that has not changed in a valid config * this will only work for and * on the running config, because otherwise there will * not be any edits recorded in the txcb->undoQ or any * nodes marked dirty in val->flags */ if (profile->agt_config_state == AGT_CFG_STATE_OK) { tests = prune_obj_commit_tests(txcb, ct, root, tests); } if (tests == 0) { /* no commit tests needed for this node */ if (LOGDEBUG3) { log_debug3("\nrun_root_check: skip commit test %s:%s", obj_get_mod_name(ct->obj), obj_get_name(ct->obj)); } continue; } res = prep_commit_test_node(scb, msghdr, txcb, ct, root); if (res != NO_ERR) { CHK_EXIT(res, retres); continue; } /* run all relevant tests on each node in the result set */ xpath_resnode_t *resnode = xpath_get_first_resnode(ct->result); for (; resnode != NULL; resnode = xpath_get_next_resnode(resnode)) { val_value_t *valnode = xpath_get_resnode_valptr(resnode); valnode->res = NO_ERR; res = run_obj_commit_tests(profile, scb, msghdr, ct, valnode, root, tests); if (res != NO_ERR) { valnode->res = res; profile->agt_load_rootcheck_errors = TRUE; CHK_EXIT(res, retres); } } /* check if any unique tests, which are handled all at once * instead of one instance at a time */ res = run_obj_unique_tests(scb, msghdr, ct, root); if (res != NO_ERR) { profile->agt_load_rootcheck_errors = TRUE; CHK_EXIT(res, retres); } } log_debug3("\nagt_val_root_check: end"); return retres; } /* agt_val_root_check */ /******************************************************************** * FUNCTION agt_val_validate_write * * Validate the requested write operation * * Check all the embedded operation attributes against * the default-operation and maintained current operation. * * Invoke all the user AGT_CB_VALIDATE callbacks for a * 'new value' and 'existing value' pairs, for a given write operation, * * These callbacks are invoked bottom-up, so the first step is to * step through all the child nodes and traverse the * 'new' data model (from the PDU) all the way to the leaf nodes * * The operation attribute is checked against the real data model * on the way down the tree, and the user callbacks are invoked * bottom-up on the way back. This way, the user callbacks can * share sub-tree validation routines, and perhaps add additional * information, based on the context and specific errors * reported from 'below'. * * INPUTS: * scb == session control block * msg == incoming rpc_msg_t in progress * target == cfg_template_t for the config database to write * == NULL for no actual write acess (validate only) * valroot == the val_value_t struct containing the root * (NCX_BT_CONTAINER, ncx:root) * datatype representing the config root with * proposed changes to the target * editop == requested start-state write operation * (usually from the default-operation parameter) * OUTPUTS: * rpc_err_rec_t structs may be malloced and added to the msg->rpc_errQ * * RETURNS: * status of the operation *********************************************************************/ status_t agt_val_validate_write (ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *valroot, op_editop_t editop) { assert( scb && "scb is NULL!" ); assert( msg && "msg is NULL!" ); assert( valroot && "valroot is NULL!" ); assert( valroot->obj && "valroot obj is NULL!" ); assert( obj_is_root(valroot->obj) && "valroot obj not root!" ); status_t res = NO_ERR; if (target) { /* check the lock first */ res = cfg_ok_to_write(target, scb->sid); if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_NONE, NULL, NCX_NT_VAL, valroot); return res; } } /* the root is just a value node of type 'root' * traverse all nodes and check the request */ res = handle_callback(AGT_CB_VALIDATE, editop, scb, msg, target, valroot, (target) ? target->root : NULL, (target) ? target->root : valroot); return res; } /* agt_val_validate_write */ /******************************************************************** * FUNCTION agt_val_apply_write * * Apply the requested write operation * * Invoke all the AGT_CB_APPLY callbacks for a * source and target and write operation * * TBD: support for handling nested parmsets independently * of the parent parmset is not supported. This means * that independent parmset instances nested within the * parent parmset all have to be applied, or none applied * * INPUTS: * scb == session control block * msg == incoming rpc_msg_t in progress * target == cfg_template_t for the config database to write * pducfg == the 'root' value struct that represents the * tree of changes to apply to the target * editop == requested start-state write operation * (usually from the default-operation parameter) * * OUTPUTS: * rpc_err_rec_t structs may be malloced and added to the msg->mhsr.errQ * * RETURNS: * status *********************************************************************/ status_t agt_val_apply_write (ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *pducfg, op_editop_t editop) { assert( scb && "scb is NULL!" ); assert( msg && "msg is NULL!" ); assert( msg->rpc_txcb && "txcb is NULL!" ); assert( target && "target is NULL!" ); assert( pducfg && "pducfg is NULL!" ); assert( obj_is_root(pducfg->obj) && "pducfg root is NULL!" ); /* start with the config root, which is a val_value_t node */ status_t res = handle_callback(AGT_CB_APPLY, editop, scb, msg, target, pducfg, target->root, target->root); if (res == NO_ERR) { boolean done = FALSE; while (!done) { /* need to delete all the false when-stmt config objects * and then see if the config is valid. This is done in * candidate or running in apply phase; not evaluated at commit * time like must-stmt or unique-stmt */ uint32 delcount = 0; res = delete_dead_nodes(scb, &msg->mhdr, msg->rpc_txcb, target->root, &delcount); if (res != NO_ERR || delcount == 0) { done = TRUE; } } } if (res == NO_ERR && msg->rpc_txcb->rootcheck) { res = agt_val_root_check(scb, &msg->mhdr, msg->rpc_txcb, target->root); } if (res == NO_ERR) { /* complete the operation */ res = handle_callback(AGT_CB_COMMIT, editop, scb, msg, target, pducfg, target->root, target->root); /*commit_complete?*/ } else { /* rollback the operation */ status_t res2 = handle_callback(AGT_CB_ROLLBACK, editop, scb, msg, target, pducfg, target->root, target->root); if (res2 != NO_ERR) { log_error("\nagt_val: Rollback failed (%s)\n", get_error_string(res2)); } } return res; } /* agt_val_apply_write */ /******************************************************************** * FUNCTION agt_val_apply_commit * * Apply the requested commit operation * * Invoke all the AGT_CB_COMMIT callbacks for a * source and target and write operation * * INPUTS: * scb == session control block * msg == incoming commit rpc_msg_t in progress * source == cfg_template_t for the source (candidate) * target == cfg_template_t for the config database to * write (running) * save_nvstore == TRUE if the mirrored NV-store * should be updated after the commit is done * FALSE if this is the start of a confirmed-commit * so the NV-store update is deferred * Never save to NV-store if :startup is supported * * OUTPUTS: * rpc_err_rec_t structs may be malloced and added * to the msg->mhdr.errQ * * RETURNS: * status *********************************************************************/ status_t agt_val_apply_commit (ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *source, cfg_template_t *target, boolean save_nvstore) { assert( scb && "scb is NULL!" ); assert( msg && "msg is NULL!" ); assert( msg->rpc_txcb && "txcb is NULL!" ); assert( source && "source is NULL!" ); assert( target && "target is NULL!" ); agt_profile_t *profile = agt_get_profile(); status_t res = NO_ERR; #ifdef ALLOW_SKIP_EMPTY_COMMIT /* usually only save if the source config was touched */ if (!cfg_get_dirty_flag(source)) { boolean no_startup_errors = dlq_empty(&target->load_errQ); /* no need to merge the candidate into the running * config because there are no changes in the candidate */ if (profile->agt_has_startup) { /* separate copy-config required to rewrite * the startup config file */ if (LOGDEBUG) { log_debug("\nSkipping commit, candidate not dirty"); } } else { /* there is no distinct startup; need to mirror now; * check if the running config had load errors * so overwriting it even if the candidate is * clean makes sense */ if (no_startup_errors) { if (LOGDEBUG) { log_debug("\nSkipping commit, candidate not dirty"); } } else { if (LOGINFO) { log_debug("\nSkipping commit, but saving running " "to NV-storage due to load errors"); } res = agt_ncx_cfg_save(target, FALSE); if (res != NO_ERR) { /* write to NV-store failed */ agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, NULL, NCX_NT_CFG, target, NCX_NT_NONE, NULL); } } } return res; } #endif /* check any top-level deletes */ //res = apply_commit_deletes(scb, msg, target, source->root, target->root); if (res == NO_ERR) { /* apply all the new and modified nodes */ res = handle_callback(AGT_CB_APPLY, OP_EDITOP_COMMIT, scb, msg, target, source->root, target->root, target->root); } if (res==NO_ERR) { /* complete the transaction */ res = handle_callback(AGT_CB_COMMIT, OP_EDITOP_COMMIT, scb, msg, target, source->root, target->root, target->root); if ( NO_ERR == res ) { res = agt_commit_complete(); } } else { /* rollback the transaction */ status_t res2 = handle_callback(AGT_CB_ROLLBACK, OP_EDITOP_COMMIT, scb, msg, target, source->root, target->root, target->root); if (res2 != NO_ERR) { log_error("\nError: rollback failed (%s)", get_error_string(res2)); } } if (res == NO_ERR && !profile->agt_has_startup) { if (save_nvstore) { res = agt_ncx_cfg_save(target, FALSE); if (res != NO_ERR) { /* write to NV-store failed */ agt_record_error(scb,&msg->mhdr, NCX_LAYER_OPERATION, res, NULL, NCX_NT_CFG, target, NCX_NT_NONE, NULL); } else { /* don't clear the dirty flags in running * unless the save to file worked */ val_clean_tree(target->root); } } else if (LOGDEBUG2) { log_debug2("\nagt_val: defer NV-save after commit " "until confirmed"); } } return res; } /* agt_val_apply_commit */ /******************************************************************** * FUNCTION agt_val_check_commit_edits * * Check if the requested commit operation * would cause any ACM or partial lock violations * in the running config * Invoke all the AGT_CB_VALIDATE callbacks for a * source and target and write operation * * !!! Only works for the operation for applying * !!! the candidate to the running config; relies on value * !!! flags VAL_FL_SUBTREE_DIRTY and VAL_FL_DIRTY * * INPUTS: * scb == session control block * msg == incoming commit rpc_msg_t in progress * source == cfg_template_t for the source (candidate) * target == cfg_template_t for the config database to * write (running) * * OUTPUTS: * rpc_err_rec_t structs may be malloced and added * to the msg->mhdr.errQ * * RETURNS: * status *********************************************************************/ status_t agt_val_check_commit_edits (ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *source, cfg_template_t *target) { assert( scb && "scb is NULL!" ); assert( msg && "msg is NULL!" ); assert( msg->rpc_txcb && "txcb is NULL!" ); assert( source && "source is NULL!" ); assert( target && "target is NULL!" ); /* usually only save if the source config was touched */ if (!cfg_get_dirty_flag(source)) { /* no need to check for partial-lock violations */ return NO_ERR; } status_t res = handle_callback(AGT_CB_VALIDATE, OP_EDITOP_COMMIT, scb, msg, target, source->root, target->root, target->root); return res; } /* agt_val_check_commit_edits */ /******************************************************************** * FUNCTION agt_val_delete_dead_nodes * * Mark nodes deleted for each false when-stmt from * INPUTS: * scb == session control block * msg == incoming validate rpc_msg_t in progress * root == value tree to check for deletions * * OUTPUTS: * rpc_err_rec_t structs may be malloced and added * to the msg->mhdr.errQ * * RETURNS: * status *********************************************************************/ status_t agt_val_delete_dead_nodes (ses_cb_t *scb, rpc_msg_t *msg, val_value_t *root) { assert( scb && "scb is NULL!" ); assert( msg && "msg is NULL!" ); assert( msg->rpc_txcb && "txcb is NULL!" ); assert( root && "root is NULL!" ); status_t res = NO_ERR; boolean done = FALSE; while (!done) { /* need to delete all the false when-stmt config objects * and then see if the config is valid. This is done in * candidate or running in apply phase; not evaluated at commit * time like must-stmt or unique-stmt */ uint32 delcount = 0; res = delete_dead_nodes(scb, &msg->mhdr, msg->rpc_txcb, root, &delcount); if (res != NO_ERR || delcount == 0) { done = TRUE; } } return res; } /* agt_val_delete_dead_nodes */ /******************************************************************** * FUNCTION agt_val_add_module_commit_tests * * !!! Initialize module data node validation !!! * !!! Must call after all modules are initially loaded !!! * * Find all the data node objects in the specified module * that need some sort database referential integrity test * * !!! dynamic features are not supported. Any disabled objects * due to false if-feature stmts will be skipped. No support yet * to add or remove tests from the commit_testQ when obj_is_enabled() * return value changes. * * INPUTS: * mod == module to check and add tests * * SIDE EFFECTS: * agt_profile->commit_testQ will contain an agt_cfg_commit_test_t struct * for each object found with commit-time validation tests * * RETURNS: * status of the operation, NO_ERR unless internal errors found * or malloc error *********************************************************************/ status_t agt_val_add_module_commit_tests (ncx_module_t *mod) { assert( mod && "mod is NULL!" ); agt_profile_t *profile = agt_get_profile(); status_t res = NO_ERR; obj_template_t *obj = ncx_get_first_data_object(mod); for (; obj != NULL; obj = ncx_get_next_data_object(mod, obj)) { res = add_obj_commit_tests(obj, &profile->agt_commit_testQ, &profile->agt_rootflags); if (res != NO_ERR) { return res; } } return res; } /* agt_val_add_module_commit_tests */ /******************************************************************** * FUNCTION agt_val_init_commit_tests * * !!! Initialize full database validation !!! * !!! Must call after all modules are initially loaded !!! * !!! Must be called before load_running_config is called !!! * * Find all the data node objects that need some sort * database referential integrity test * For :candidate this is done during the * validate of the RPC * For :writable-running, this is done during * or * * SIDE EFFECTS: * all commit_test_t records created are stored in the * agt_profile->commit_testQ * * RETURNS: * status of the operation, NO_ERR unless internal errors found * or malloc error *********************************************************************/ status_t agt_val_init_commit_tests (void) { status_t res = NO_ERR; /* check all the modules in the system for top-level database objects */ ncx_module_t *mod = ncx_get_first_module(); for (; mod != NULL; mod = ncx_get_next_module(mod)) { res = agt_val_add_module_commit_tests(mod); if (res != NO_ERR) { return res; } } return res; } /* agt_val_init_commit_tests */ /* END file agt_val.c */ yuma123_2.14/netconf/src/agt/agt_fd_event_cb.h0000664000175000017500000000501514770023131021364 0ustar vladimirvladimir/* * Copyright (c) 2024, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt123_fd_event_cb #define _H_agt123_fd_event_cb /* FILE: agt123_fd_event_cb.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Registration of file descriptor event callbacks to the server select */ #include "status.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* file descriptor event callback function * * Process the file descriptor event * * INPUTS: * fd == file descriptor * * RETURNS: * 0 == normal exit * -1 == error exit, unregister callback upon return */ typedef int (*agt_fd_event_cb_fn_t) (int fd); /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_fd_event_cb_register * * Register event file descriptor in the select loop with * corresponding callback * * INPUTS: * fd == file descriptor number * cb_fn == callback function pointer *********************************************************************/ extern void agt_fd_event_cb_register(int fd, agt_fd_event_cb_fn_t cb_fn); /******************************************************************** * FUNCTION agt_fd_event_cb_unregister * * Unregister event file descriptor from the select loop * * INPUTS: * fd == file descriptor number *********************************************************************/ extern void agt_fd_event_cb_unregister(int fd); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_fd_event_cb */ yuma123_2.14/netconf/src/agt/agt_val_parse.h0000664000175000017500000000740714770023131021111 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_val_parse #define _H_agt_val_parse /* FILE: agt_val_parse.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Parameter Value Parser Module ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11-feb-06 abb Begun 06-aug-08 abb Rewrite for OBJ/VAL only model in YANG Remove app, parmset, and parm from database model */ #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_val_parse_nc * * Parse NETCONF PDU sub-contents into value fields * This module does not enforce complex type completeness. * Different subsets of configuration data are permitted * in several standard (and any proprietary) RPC methods * * Makes sure that only allowed value strings * or child nodes (and their values) are entered. * * Defaults are not added to any objects * Missing objects are not checked * * A seperate parsing phase is used to fully validate the input * contained in the returned val_value_t struct. * * This parsing phase checks that simple types are complete * and child members of complex types are valid (but maybe * missing or incomplete child nodes. * * Note that the NETCONF inlineFilterType will be parsed * correctly because it is type 'anyxml'. This type is * parsed as a struct, and no validation other well-formed * XML is done. * * INPUTS: * scb == session control block * msg == incoming RPC message * obj == obj_template_t for the object to parse * startnode == top node of the parameter to be parsed * parentdc == parent data class * For the first call to this function, this will * be the data class of a parameter. * retval == address of initialized return value * * OUTPUTS: * *status will be filled in * msg->errQ may be appended with new errors * * RETURNS: * status *********************************************************************/ extern status_t agt_val_parse_nc (ses_cb_t *scb, xml_msg_hdr_t *msg, obj_template_t *obj, const xml_node_t *startnode, ncx_data_class_t parentdc, val_value_t *retval); #ifdef DEBUG /******************************************************************** * FUNCTION agt_val_parse_test * * scaffold code to get agt_val_parse tested * * INPUTS: * testfile == NETCONF PDU in a test file * *********************************************************************/ extern void agt_val_parse_test (const char *testfile); #endif #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_val_parse */ yuma123_2.14/netconf/src/agt/agt_val.h0000664000175000017500000003642114770023131017715 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_val #define _H_agt_val /* FILE: agt_val.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Server database callback handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 20-may-06 abb Begun 30-sep-08 abb Implement AGT_CB_TEST_APPLY and agt_val_split_root_test for YANG support of dummy running config commit-time validation */ #ifndef _H_agt #include "agt.h" #endif #ifndef _H_agt_cfg #include "agt_cfg.h" #endif #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h.h" #endif #ifndef _H_op #include "op.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xml_msg #include "xml_msg.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /* General val_value_t processing * * step 1: call agt_val_parse_nc * step 2: call val_add_defaults * step 3: call agt_val_rpc_xpath_check * step 4: call agt_val_instance_check * * Additional steps to write to a config database * * step 4: call agt_val_validate_write * step 5: call agt_val_apply_write * * Steps to test for commit-ready * * step 6a: agt_val_root_check (candidate) * step 6b: agt_val_split_root_check (running) * step 7: agt_val_apply_commit */ /******************************************************************** * FUNCTION agt_val_rpc_xpath_check * * Check for any nodes which are present * but have false when-stmts associated * with the node. These are errors and * need to be flagged as unknown-element * * Any false nodes will be removed from the input PDU * and discarded, after the error is recorded. * This prevents false positives or negatives in * the agt_val_instance_check, called after this function * * Also checks any false must-stmts for nodes * which are present (after false when removal) * These are flagged as 'must-violation' errors * as per YANG, 13.4 * * INPUTS: * scb == session control block (may be NULL; no session stats) * rpcmsg == RPC msg header for audit purposes * msg == xml_msg_hdr t from msg in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * rpcinput == RPC input node conceptually under rpcroot * except this rpcinput has no parent node * so a fake one will be termporarily added * to prevent false XPath validation errors * rpcroot == RPC method node. * The conceptual parent of this node * is used as the document root (/rpc == /) * * OUTPUTS: * if false nodes found under : * they are deleted * if msg not NULL: * msg->msg_errQ may have rpc_err_rec_t structs added to it * which must be freed by the called with the * rpc_err_free_record function * * RETURNS: * status of the operation * NO_ERR if no false when or must statements found *********************************************************************/ extern status_t agt_val_rpc_xpath_check (ses_cb_t *scb, rpc_msg_t *rpcmsg, xml_msg_hdr_t *msg, val_value_t *rpcinput, obj_template_t *rpcroot); /******************************************************************** * FUNCTION agt_val_instance_check * * Check for the proper number of object instances for * the specified value struct. * * The top-level value set passed cannot represent a choice * or a case within a choice. * * This function is intended for validating PDUs (RPC requests) * during the PDU processing. It does not check the instance * count or must-stmt expressions for any (ncx:root) * container. This must be done with the agt_val_root_check function. * * INPUTS: * scb == session control block (may be NULL; no session stats) * msg == xml_msg_hdr t from msg in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * valset == val_value_t list, leaf-list, or container to check * valroot == root node of the database * layer == NCX layer calling this function (for error purposes only) * * OUTPUTS: * if msg not NULL: * msg->msg_errQ may have rpc_err_rec_t structs added to it * which must be freed by the called with the * rpc_err_free_record function * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ extern status_t agt_val_instance_check (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *valset, val_value_t *valroot, ncx_layer_t layer); /******************************************************************** * FUNCTION agt_val_root_check * * !!! Full database validation !!! * Check for the proper number of object instances for * the specified configuration database * Check must and when statements * Check empty NP containers * Check choices (selected case, and it is complete) * * Tests are divided into 3 groups: * A) top-level nodes (child of conceptual root * B) parent data node for child instance tests (mand/min/max) * C) data node referential tests (must/unique/leaf) * * Test pruning * The global variable agt_profile.agt_rootflags is used * to determine if any type (A) commit tests are needed * * The global variable agt_profile.agt_config_state is * used to force complete testing; There are 3 states: * AGT_CFG_STATE_INIT : running config has not been * validated, or validation-in-progress * AGT_CFG_STATE_OK : running config has been validated * and it passed all validation checks * AGT_CFG_STATE_BAD: running config validation has been * attempted and it failed; running config is not valid! * The server will shutdown if * The target nodes in the undoQ records * INPUTS: * scb == session control block (may be NULL; no session stats) * msghdr == XML message header in progress * == NULL MEANS NO RPC-ERRORS ARE RECORDED * txcb == transaction control block * target == target database to use * root == val_value_t for the target config being checked * * OUTPUTS: * if mshdr not NULL: * msghdr->msg_errQ may have rpc_err_rec_t * structs added to it which must be freed by the * caller with the rpc_err_free_record function * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ extern status_t agt_val_root_check (ses_cb_t *scb, xml_msg_hdr_t *msghdr, agt_cfg_transaction_t *txcb, val_value_t *root); /******************************************************************** * FUNCTION agt_val_validate_write * * Validate the requested write operation * * Check all the embedded operation attributes against * the default-operation and maintained current operation. * * Invoke all the user AGT_CB_VALIDATE callbacks for a * 'new value' and 'existing value' pairs, for a given write operation, * * These callbacks are invoked bottom-up, so the first step is to * step through all the child nodes and traverse the * 'new' data model (from the PDU) all the way to the leaf nodes * * The operation attribute is checked against the real data model * on the way down the tree, and the user callbacks are invoked * bottom-up on the way back. This way, the user callbacks can * share sub-tree validation routines, and perhaps add additional * information, based on the context and specific errors * reported from 'below'. * * INPUTS: * scb == session control block * msg == incoming rpc_msg_t in progress * target == cfg_template_t for the config database to write * == NULL for no actual write acess (validate only) * valroot == the val_value_t struct containing the root * (NCX_BT_CONTAINER, ncx:root) * datatype representing the config root with * proposed changes to the target * editop == requested start-state write operation * (usually from the default-operation parameter) * OUTPUTS: * rpc_err_rec_t structs may be malloced and added to the msg->rpc_errQ * * RETURNS: * status of the operation *********************************************************************/ extern status_t agt_val_validate_write (ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *valroot, op_editop_t editop); /******************************************************************** * FUNCTION agt_val_apply_write * * Apply the requested write operation * * Invoke all the AGT_CB_APPLY callbacks for a * source and target and write operation * * INPUTS: * scb == session control block * msg == incoming rpc_msg_t in progress * target == cfg_template_t for the config database to write * pducfg == the 'root' value struct that represents the * tree of changes to apply to the target * editop == requested start-state write operation * (usually from the default-operation parameter) * * OUTPUTS: * rpc_err_rec_t structs may be malloced and added to the msg->mhsr.errQ * * RETURNS: * status *********************************************************************/ extern status_t agt_val_apply_write (ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *target, val_value_t *pducfg, op_editop_t editop); /******************************************************************** * FUNCTION agt_val_apply_commit * * Apply the requested commit operation * * Invoke all the AGT_CB_COMMIT callbacks for a * source and target and write operation * * INPUTS: * scb == session control block * msg == incoming commit rpc_msg_t in progress * source == cfg_template_t for the source (candidate) * target == cfg_template_t for the config database to * write (running) * save_nvstore == TRUE if the mirrored NV-store * should be updated after the commit is done * FALSE if this is the start of a confirmed-commit * so the NV-store update is deferred * Never save to NV-store if :startup is supported * * OUTPUTS: * rpc_err_rec_t structs may be malloced and added * to the msg->mhsr.errQ * * RETURNS: * status *********************************************************************/ extern status_t agt_val_apply_commit (ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *source, cfg_template_t *target, boolean save_nvstore); /******************************************************************** * FUNCTION agt_val_check_commit_edits * * Check if the requested commit operation * would cause any ACM or partial lock violations * in the running config * Invoke all the AGT_CB_VALIDATE callbacks for a * source and target and write operation * * INPUTS: * scb == session control block * msg == incoming commit rpc_msg_t in progress * source == cfg_template_t for the source (e.g., candidate) * target == cfg_template_t for the config database to * write (e.g., running) * * OUTPUTS: * rpc_err_rec_t structs may be malloced and added * to the msg->mhdr.errQ * * RETURNS: * status *********************************************************************/ extern status_t agt_val_check_commit_edits (ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *source, cfg_template_t *target); /******************************************************************** * FUNCTION agt_val_delete_dead_nodes * * Mark nodes deleted for each false when-stmt from * INPUTS: * scb == session control block * msg == incoming validate rpc_msg_t in progress * root == value tree to check for deletions * * OUTPUTS: * rpc_err_rec_t structs may be malloced and added * to the msg->mhdr.errQ * * RETURNS: * status *********************************************************************/ extern status_t agt_val_delete_dead_nodes (ses_cb_t *scb, rpc_msg_t *msg, val_value_t *root); /******************************************************************** * FUNCTION agt_val_add_module_commit_tests * * !!! Initialize module data node validation !!! * !!! Must call after all modules are initially loaded !!! * * Find all the data node objects in the specified module * that need some sort database referential integrity test * * !!! dynamic features are not supported. Any disabled objects * due to false if-feature stmts will be skipped. No support yet * to add or remove tests from the commit_testQ when obj_is_enabled() * return value changes. * * INPUTS: * mod == module to check and add tests * * SIDE EFFECTS: * agt_profile->commit_testQ will contain an agt_commit_test_t struct * for each object found with commit-time validation tests * * RETURNS: * status of the operation, NO_ERR unless internal errors found * or malloc error *********************************************************************/ extern status_t agt_val_add_module_commit_tests (ncx_module_t *mod); /******************************************************************** * FUNCTION agt_val_init_commit_tests * * !!! Initialize full database validation !!! * !!! Must call after all modules are initially loaded !!! * !!! Must be called before load_running_config is called !!! * * Find all the data node objects that need some sort * database referential integrity test * For :candidate this is done during the * validate of the RPC * For :writable-running, this is done during * or * * SIDE EFFECTS: * all commit_test_t records created are stored in the * agt_profile->commit_testQ * * RETURNS: * status of the operation, NO_ERR unless internal errors found * or malloc error *********************************************************************/ extern status_t agt_val_init_commit_tests (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_val */ yuma123_2.14/netconf/src/agt/agt_rpcerr.h0000664000175000017500000002554714770023131020437 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_rpcerr #define _H_agt_rpcerr /* FILE: agt_rpcerr.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF protocol server-side handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 07-mar-06 abb Begun. 13-jan-07 abb moved from rpc dir to agt; rename rpc_agterr to agt_rpcerr */ #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_rpc_err #include "rpc_err.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xml_msg #include "xml_msg.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_rpc_gen_error * * Generate an internal record for an element * (or non-attribute) related error for any layer. * * INPUTS: * layer == protocol layer where the error occurred * interr == internal error code * if NO_ERR than use the rpcerr only * errnode == XML node where error occurred * == NULL then there is no valid XML node (maybe the error!) * parmtyp == type of node contained in error_parm * error_parm == pointer to the extra parameter expected for * this type of error. * * == (void *)pointer to session_id for lock-denied errors * == (void *) pointer to the bad-value string to use * for some other errors * * error_path == malloced string of the value (or type, etc.) instance * ID string in NCX_IFMT_XPATH format; this will be added * to the rpc_err_rec_t and freed later * == NULL if not available * * RETURNS: * pointer to allocated and filled in rpc_err_rec_t struct * ready to add to the msg->rpc_errQ * NULL if a record could not be allocated or not enough * val;id info in the parameters *********************************************************************/ extern rpc_err_rec_t * agt_rpcerr_gen_error (ncx_layer_t layer, status_t interr, const xml_node_t *errnode, ncx_node_t parmtyp, const void *error_parm, xmlChar *error_path); /******************************************************************** * FUNCTION agt_rpc_gen_error_errinfo * * Generate an internal record for an element * * INPUTS: * layer == protocol layer where the error occurred * interr == internal error code * if NO_ERR than use the rpcerr only * errnode == XML node where error occurred * == NULL then there is no valid XML node (maybe the error!) * parmtyp == type of node contained in error_parm * error_parm == pointer to the extra parameter expected for * this type of error. * * == (void *)pointer to session_id for lock-denied errors * == (void *) pointer to the bad-value string to use * for some other errors * * error_path == malloced string of the value (or type, etc.) instance * ID string in NCX_IFMT_XPATH format; this will be added * to the rpc_err_rec_t and freed later * == NULL if not available * errinfo == error info struct to use for whatever fields are set * * RETURNS: * pointer to allocated and filled in rpc_err_rec_t struct * ready to add to the msg->rpc_errQ * NULL if a record could not be allocated or not enough * val;id info in the parameters *********************************************************************/ extern rpc_err_rec_t * agt_rpcerr_gen_error_errinfo (ncx_layer_t layer, status_t interr, const xml_node_t *errnode, ncx_node_t parmtyp, const void *error_parm, xmlChar *error_path, const ncx_errinfo_t *errinfo); /******************************************************************** * FUNCTION agt_rpc_gen_error_ex * * Generate an internal record for an element * * INPUTS: * layer == protocol layer where the error occurred * interr == internal error code * if NO_ERR than use the rpcerr only * errnode == XML node where error occurred * == NULL then there is no valid XML node (maybe the error!) * parmtyp == type of node contained in error_parm * error_parm == pointer to the extra parameter expected for * this type of error. * * == (void *)pointer to session_id for lock-denied errors * == (void *) pointer to the bad-value string to use * for some other errors * * error_path == malloced string of the value (or type, etc.) instance * ID string in NCX_IFMT_XPATH format; this will be added * to the rpc_err_rec_t and freed later * == NULL if not available * errinfo == error info struct to use for whatever fields are set * nodetyp == type of node contained in error_path_raw * error_path_raw == pointer to the extra parameter expected for * this type of error. * * RETURNS: * pointer to allocated and filled in rpc_err_rec_t struct * ready to add to the msg->rpc_errQ * NULL if a record could not be allocated or not enough * val;id info in the parameters *********************************************************************/ extern rpc_err_rec_t * agt_rpcerr_gen_error_ex (ncx_layer_t layer, status_t interr, const xml_node_t *errnode, ncx_node_t parmtyp, const void *error_parm, xmlChar *error_path, const ncx_errinfo_t *errinfo, ncx_node_t nodetyp, const void *error_path_raw); /******************************************************************** * FUNCTION agt_rpc_gen_insert_error * * Generate an internal record for an element * for an insert operation failed error * * INPUTS: * layer == protocol layer where the error occurred * interr == internal error code * if NO_ERR than use the rpcerr only * errval == pointer to the node with the insert error * error_path == malloced string of the value (or type, etc.) instance * ID string in NCX_IFMT_XPATH format; this will be added * to the rpc_err_rec_t and freed later * == NULL if not available * * RETURNS: * pointer to allocated and filled in rpc_err_rec_t struct * ready to add to the msg->rpc_errQ * NULL if a record could not be allocated or not enough * val;id info in the parameters *********************************************************************/ extern rpc_err_rec_t * agt_rpcerr_gen_insert_error (ncx_layer_t layer, status_t interr, const val_value_t *errval, xmlChar *error_path); /******************************************************************** * FUNCTION agt_rpc_gen_unique_error * * Generate an internal record for an element * for a unique-stmt failed error (data-not-unique) * * INPUTS: * msghdr == message header to use for prefix storage * layer == protocol layer where the error occurred * interr == internal error code * if NO_ERR than use the rpcerr only * errval == pointer to the node with the insert error * valuniqueQ == Q of val_unique_t structs to * use for elements * error_path == malloced string of the value (or type, etc.) instance * ID string in NCX_IFMT_XPATH format; this will be added * to the rpc_err_rec_t and freed later * == NULL if not available * * RETURNS: * pointer to allocated and filled in rpc_err_rec_t struct * ready to add to the msg->rpc_errQ * NULL if a record could not be allocated or not enough * val;id info in the parameters *********************************************************************/ extern rpc_err_rec_t * agt_rpcerr_gen_unique_error (xml_msg_hdr_t *msghdr, ncx_layer_t layer, status_t interr, const dlq_hdr_t *valuniqueQ, xmlChar *error_path); /******************************************************************** * FUNCTION agt_rpc_gen_attr_error * * Generate an internal record for an attribute * * INPUTS: * layer == protocol layer where the error occurred * interr == internal error code * if NO_ERR than use the rpcerr only * attr == attribute that had the error * errnode == XML node where error occurred * == NULL then there is no valid XML node (maybe the error!) * errnodeval == valuse struct for the error node id errnode NULL * == NULL if not used * badns == URI string of the namespace that is bad (or NULL) * * RETURNS: * pointer to allocated and filled in rpc_err_rec_t struct * ready to add to the msg->rpc_errQ (or add more error-info) * NULL if a record could not be allocated or not enough * valid info in the parameters to generate an error report *********************************************************************/ extern rpc_err_rec_t * agt_rpcerr_gen_attr_error (ncx_layer_t layer, status_t interr, const xml_attr_t *attr, const xml_node_t *errnode, const val_value_t *errnodeval, const xmlChar *badns, xmlChar *error_path); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_rpcerr */ yuma123_2.14/netconf/src/agt/agt_ses.c0000664000175000017500000012427014770023131017720 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_ses.c NETCONF Session Manager: Agent Side Support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 06jun06 abb begun; cloned from ses_mgr.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_acm.h" #include "agt_cb.h" #include "agt_connect.h" #include "agt_ncx.h" #include "agt_ncxserver.h" #include "agt_rpc.h" #include "agt_ses.h" #include "agt_state.h" #include "agt_sys.h" #include "agt_top.h" #include "agt_util.h" #include "cfg.h" #include "def_reg.h" #include "getcb.h" #include "log.h" #include "ncxmod.h" #include "ncx.h" #include "rpc.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "tstamp.h" #include "val.h" #include "val123.h" #include "xmlns.h" #include "xml_util.h" #include "uptime.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define AGT_SES_MODULE (const xmlChar *)"yuma-mysession" #define AGT_SES_GET_MY_SESSION (const xmlChar *)"get-my-session" #define AGT_SES_SET_MY_SESSION (const xmlChar *)"set-my-session" #define AGT_SES_CACHE_MODULE (const xmlChar *)"yuma123-mysession-cache" /* number of seconds to wait between session timeout checks */ #define AGT_SES_TIMEOUT_INTERVAL 1 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_ses_init_done = FALSE; static uint32 next_sesid; static ses_cb_t **agtses; static ses_total_stats_t *agttotals; static ncx_module_t *mysesmod; static ncx_module_t *mysescachemod; static time_t last_timeout_check; /******************************************************************** * FUNCTION get_session_idval * * Get the session ID number for the virtual * counter entry that was called from a operation * * INPUTS: * virval == virtual value * retsid == address of return session ID number * * OUTPUTS: * *retsid == session ID for this entry * RETURNS: * status *********************************************************************/ static status_t get_session_key (const val_value_t *virval, ses_id_t *retsid) { const val_value_t *parentval, *sidval; parentval = virval->parent; if (parentval == NULL) { return ERR_NCX_DEF_NOT_FOUND; } sidval = val_find_child(parentval, obj_get_mod_name(parentval->obj), (const xmlChar *)"session-id"); if (sidval == NULL) { return ERR_NCX_DEF_NOT_FOUND; } *retsid = VAL_UINT(sidval); return NO_ERR; } /* get_session_key */ /******************************************************************** * FUNCTION get_my_session_invoke * * get-my-session : invoke params callback * * INPUTS: * see rpc/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t get_my_session_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { val_value_t *indentval, *linesizeval, *withdefval; val_value_t *cache_timeout_val; obj_template_t *output_obj; obj_template_t *cache_timeout_obj; xmlChar numbuff[NCX_MAX_NUMLEN]; (void)methnode; snprintf((char *)numbuff, sizeof(numbuff), "%u", scb->indent); indentval = val_make_string(mysesmod->nsid, NCX_EL_INDENT, numbuff); if (indentval == NULL) { return ERR_INTERNAL_MEM; } snprintf((char *)numbuff, sizeof(numbuff), "%u", scb->linesize); linesizeval = val_make_string(mysesmod->nsid, NCX_EL_LINESIZE, numbuff); if (linesizeval == NULL) { val_free_value(indentval); return ERR_INTERNAL_MEM; } withdefval = val_make_string(mysesmod->nsid, NCX_EL_WITH_DEFAULTS, ncx_get_withdefaults_string(scb->withdef)); if (withdefval == NULL) { val_free_value(indentval); val_free_value(linesizeval); return ERR_INTERNAL_MEM; } cache_timeout_val = val_new_value(); output_obj = obj_find_child( msg->rpc_method, AGT_SES_MODULE, NCX_EL_OUTPUT); assert(output_obj); cache_timeout_obj = obj_find_child( output_obj, AGT_SES_CACHE_MODULE, "cache-timeout"); assert(cache_timeout_obj); cache_timeout_val = val_new_value(); if (cache_timeout_val == NULL) { val_free_value(indentval); val_free_value(linesizeval); val_free_value(withdefval); return ERR_INTERNAL_MEM; } val_init_from_template(cache_timeout_val, cache_timeout_obj); VAL_UINT(cache_timeout_val) = scb->cache_timeout; dlq_enque(indentval, &msg->rpc_dataQ); dlq_enque(linesizeval, &msg->rpc_dataQ); dlq_enque(withdefval, &msg->rpc_dataQ); dlq_enque(cache_timeout_val, &msg->rpc_dataQ); msg->rpc_data_type = RPC_DATA_YANG; return NO_ERR; } /* get_my_session_invoke */ /******************************************************************** * FUNCTION set_my_session_invoke * * set-my-session : invoke params callback * * INPUTS: * see rpc/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t set_my_session_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { val_value_t *indentval, *linesizeval, *withdefval; val_value_t *cache_timeout_val; (void)methnode; /* get the indent amount parameter */ indentval = val_find_child(msg->rpc_input, AGT_SES_MODULE, NCX_EL_INDENT); if (indentval && indentval->res == NO_ERR) { scb->indent = VAL_UINT(indentval); } /* get the line sizer parameter */ linesizeval = val_find_child(msg->rpc_input, AGT_SES_MODULE, NCX_EL_LINESIZE); if (linesizeval && linesizeval->res == NO_ERR) { scb->linesize = VAL_UINT(linesizeval); } /* get the with-defaults parameter */ withdefval = val_find_child(msg->rpc_input, AGT_SES_MODULE, NCX_EL_WITH_DEFAULTS); if (withdefval && withdefval->res == NO_ERR) { scb->withdef = ncx_get_withdefaults_enum(VAL_ENUM_NAME(withdefval)); } /* get the cache-timeout parameter */ cache_timeout_val = val_find_child(msg->rpc_input, AGT_SES_CACHE_MODULE, "cache-timeout"); if (cache_timeout_val && cache_timeout_val->res == NO_ERR) { scb->cache_timeout = VAL_UINT(cache_timeout_val); } return NO_ERR; } /* set_my_session_invoke */ /************* E X T E R N A L F U N C T I O N S ***************/ /******************************************************************** * FUNCTION agt_ses_init * * INIT 1: * Initialize the session manager module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t agt_ses_init (void) { agt_profile_t *agt_profile; status_t res; uint32 i; if (agt_ses_init_done) { return ERR_INTERNAL_INIT_SEQ; } #ifdef AGT_STATE_DEBUG log_debug2("\nagt: Loading netconf-state module"); #endif agt_profile = agt_get_profile(); /* maximum number of concurrent inbound sessions * Must be >= 2 since session 0 is used for the dummy scb */ assert(agt_profile->agt_max_sessions>=2); agtses = (ses_cb_t **) malloc((agt_profile->agt_max_sessions)*sizeof(ses_cb_t *)); assert(agtses!=NULL); for (i=0; iagt_max_sessions; i++) { agtses[i] = NULL; } next_sesid = 1; mysesmod = NULL; mysescachemod = NULL; agttotals = ses_get_total_stats(); memset(agttotals, 0x0, sizeof(ses_total_stats_t)); tstamp_datetime(agttotals->startTime); (void)uptime(&last_timeout_check); agt_ses_init_done = TRUE; /* load the netconf-state module */ res = ncxmod_load_module(AGT_SES_MODULE, NULL, &agt_profile->agt_savedevQ, &mysesmod); if (res != NO_ERR) { return SET_ERROR(res); } /* load the netconf-state module */ res = ncxmod_load_module(AGT_SES_CACHE_MODULE, NULL, &agt_profile->agt_savedevQ, &mysescachemod); if (res != NO_ERR) { return SET_ERROR(res); } /* set up get-my-session RPC operation */ res = agt_rpc_register_method(AGT_SES_MODULE, AGT_SES_GET_MY_SESSION, AGT_RPC_PH_INVOKE, get_my_session_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* set up set-my-session RPC operation */ res = agt_rpc_register_method(AGT_SES_MODULE, AGT_SES_SET_MY_SESSION, AGT_RPC_PH_INVOKE, set_my_session_invoke); if (res != NO_ERR) { return SET_ERROR(res); } return res; } /* agt_ses_init */ /******************************************************************** * FUNCTION agt_ses_cleanup * * Cleanup the session manager module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ void agt_ses_cleanup (void) { uint32 i; agt_profile_t *agt_profile; agt_profile = agt_get_profile(); if (agt_ses_init_done) { for (i=0; iagt_max_sessions; i++) { if (agtses[i]) { agt_ses_free_session(agtses[i]); } } free(agtses); next_sesid = 0; agt_rpc_unregister_method(AGT_SES_MODULE, AGT_SES_GET_MY_SESSION); agt_rpc_unregister_method(AGT_SES_MODULE, AGT_SES_SET_MY_SESSION); agt_ses_init_done = FALSE; } } /* agt_ses_cleanup */ /******************************************************************** * FUNCTION agt_ses_new_dummy_session * * Create a dummy session control block * * INPUTS: * none * RETURNS: * pointer to initialized dummy SCB, or NULL if malloc error *********************************************************************/ ses_cb_t * agt_ses_new_dummy_session (void) { ses_cb_t *scb; if (!agt_ses_init_done) { agt_ses_init(); } /* check if dummy session already cached */ if (agtses[0]) { SET_ERROR(ERR_INTERNAL_INIT_SEQ); return NULL; } /* no, so create it */ scb = ses_new_dummy_scb(); if (!scb) { return NULL; } agtses[0] = scb; return scb; } /* agt_ses_new_dummy_session */ /******************************************************************** * FUNCTION agt_ses_set_dummy_session_acm * * Set the session ID and username of the user that * will be responsible for the rollback if needed * * INPUTS: * dummy_session == session control block to change * use_sid == Session ID to use for the rollback * * RETURNS: * status *********************************************************************/ status_t agt_ses_set_dummy_session_acm (ses_cb_t *dummy_session, ses_id_t use_sid) { ses_cb_t *scb; assert( dummy_session && "dummy_session is NULL!" ); if (!agt_ses_init_done) { agt_ses_init(); } scb = agtses[use_sid]; if (scb == NULL) { return ERR_NCX_INVALID_VALUE; } dummy_session->rollback_sid = use_sid; /*TODO Quick fix for confirmed-commit/rollback bug. */ /* Needs further investigation. */ dummy_session->sid = use_sid; if (scb == dummy_session) { return NO_ERR; /* skip -- nothing to do */ } if (dummy_session->username && scb->username) { m__free(dummy_session->username); dummy_session->username = NULL; } if (scb->username) { dummy_session->username = xml_strdup(scb->username); if (dummy_session->username == NULL) { return ERR_INTERNAL_MEM; } } if (dummy_session->peeraddr && scb->peeraddr) { m__free(dummy_session->peeraddr); dummy_session->peeraddr = NULL; } if (scb->peeraddr) { dummy_session->peeraddr = xml_strdup(scb->peeraddr); if (dummy_session->peeraddr == NULL) { return ERR_INTERNAL_MEM; } } return NO_ERR; } /* agt_ses_set_dummy_session_acm */ /******************************************************************** * FUNCTION agt_ses_free_dummy_session * * Free a dummy session control block * * INPUTS: * scb == session control block to free * *********************************************************************/ void agt_ses_free_dummy_session (ses_cb_t *scb) { assert( scb && "scb is NULL!" ); assert( agt_ses_init_done && "agt_ses_init_done is false!" ); assert( agtses[0] && "agtses[0] is null" ); agt_acm_clear_session_cache(scb); ses_free_scb(scb); agtses[0] = NULL; } /* agt_ses_free_dummy_session */ /******************************************************************** * FUNCTION agt_ses_new_session * * Create a real agent session control block * * INPUTS: * transport == the transport type * fd == file descriptor number to use for IO * RETURNS: * pointer to initialized SCB, or NULL if some error * This pointer is stored in the session table, so it does * not have to be saved by the caller *********************************************************************/ ses_cb_t * agt_ses_new_session (ses_transport_t transport, int fd) { ses_cb_t *scb; uint32 i, slot; status_t res; agt_profile_t *agt_profile; agt_profile = agt_get_profile(); if (!agt_ses_init_done) { agt_ses_init(); } res = NO_ERR; slot = 0; scb = NULL; /* check if any sessions are available */ if (next_sesid == 0) { /* end has already been reached, so now in brute force * session reclaim mode */ slot = 0; for (i=1; iagt_max_sessions && !slot; i++) { if (!agtses[i]) { slot = i; } } } else { slot = next_sesid; } if (slot) { /* make sure there is memory for a session control block */ scb = ses_new_scb(); if (scb) { /* initialize the profile vars */ scb->linesize = agt_profile->agt_linesize; scb->withdef = agt_profile->agt_defaultStyleEnum; scb->indent = agt_profile->agt_indent; if (ncx_protocol_enabled(NCX_PROTO_NETCONF10)) { scb->protocols_requested |= NCX_FL_PROTO_NETCONF10; } if (ncx_protocol_enabled(NCX_PROTO_NETCONF11)) { scb->protocols_requested |= NCX_FL_PROTO_NETCONF11; } /* initialize the static vars */ scb->type = SES_TYP_NETCONF; scb->transport = transport; scb->state = SES_ST_INIT; scb->mode = SES_MODE_XML; scb->sid = slot; scb->inready.sid = slot; scb->outready.sid = slot; scb->state = SES_ST_INIT; scb->fd = fd; scb->instate = SES_INST_IDLE; scb->stream_output = TRUE; res = ses_msg_new_buff(scb, TRUE, &scb->outbuff); } else { res = ERR_INTERNAL_MEM; } } else { res = ERR_NCX_RESOURCE_DENIED; } /* add the FD to SCB mapping in the definition registry */ if (res == NO_ERR) { res = def_reg_add_scb(scb->fd, scb); } /* check result and add to session array if NO_ERR */ if (res == NO_ERR) { agtses[slot] = scb; /* update the next slot now */ if (next_sesid) { if (++next_sesid==agt_profile->agt_max_sessions) { /* reached the end */ next_sesid = 0; } } if (LOGINFO) { log_info("\nNew session %d created OK", slot); } agttotals->inSessions++; agttotals->active_sessions++; } else { if (scb) { agt_ses_free_session(scb); scb = NULL; } if (LOGINFO) { log_info("\nNew session request failed (%s)", get_error_string(res)); } } return scb; } /* agt_ses_new_session */ /******************************************************************** * FUNCTION agt_ses_free_session * * Free a real session control block * * INPUTS: * scb == session control block to free * *********************************************************************/ void agt_ses_free_session (ses_cb_t *scb) { ses_id_t slot; assert( scb && "scb is NULL!" ); assert( agt_ses_init_done && "agt_ses_init_done is false!" ); if (scb->type==SES_TYP_DUMMY) { agt_ses_free_dummy_session( scb ); return; } slot = scb->sid; if (scb->fd) { def_reg_del_scb(scb->fd); } cfg_release_locks(slot); agt_state_remove_session(slot); agt_not_remove_subscription(slot); /* add this session to ses stats */ agttotals->active_sessions--; if (scb->active) { agttotals->closed_sessions++; } else { agttotals->failed_sessions++; } /* make sure the ncxserver loop does not try * to read from this file descriptor again */ agt_ncxserver_clear_fd(scb->fd); /* clear any NACM cache that this session was using */ agt_acm_clear_session_cache(scb); ses_msg_unmake_inready(scb); ses_msg_unmake_outready(scb); /* this will close the socket if it is still open */ ses_free_scb(scb); agtses[slot] = NULL; if (LOGINFO) { log_info("\nSession %d closed", slot); } } /* agt_ses_free_session */ /******************************************************************** * FUNCTION agt_ses_session_id_valid * * Check if a session-id is for an active session * * INPUTS: * sid == session ID to check * RETURNS: * TRUE if active session; FALSE otherwise *********************************************************************/ boolean agt_ses_session_id_valid (ses_id_t sid) { agt_profile_t *agt_profile; agt_profile = agt_get_profile(); if (sid < agt_profile->agt_max_sessions && agtses[sid]) { return TRUE; } else { return FALSE; } } /* agt_ses_session_id_valid */ /******************************************************************** * FUNCTION agt_ses_request_close * * Start the close of the specified session * * INPUTS: * sid == session ID to close * killedby == session ID executing the kill-session or close-session * termreason == termination reason code * * RETURNS: * none *********************************************************************/ void agt_ses_request_close (ses_cb_t *scb, ses_id_t killedby, ses_term_reason_t termreason) { if (!scb) { return; } /* N/A for dummy sessions */ if (scb->type==SES_TYP_DUMMY) { return; } scb->killedbysid = killedby; scb->termreason = termreason; /* change the session control block state */ switch (scb->state) { case SES_ST_IDLE: case SES_ST_SHUTDOWN: case SES_ST_SHUTDOWN_REQ: agt_ses_kill_session(scb, killedby, termreason); break; case SES_ST_IN_MSG: case SES_ST_HELLO_WAIT: scb->state = SES_ST_SHUTDOWN_REQ; break; default: if (dlq_empty(&scb->outQ)) { agt_ses_kill_session(scb, killedby, termreason); } else { scb->state = SES_ST_SHUTDOWN_REQ; } } } /* agt_ses_request_close */ /******************************************************************** * FUNCTION agt_ses_kill_session * * Kill the specified session * * INPUTS: * sid == session ID to kill * RETURNS: * none *********************************************************************/ void agt_ses_kill_session (ses_cb_t *scb, ses_id_t killedby, ses_term_reason_t termreason) { if (!scb) { return; /* no session found for this ID */ } /* N/A for dummy sessions */ if (scb->type==SES_TYP_DUMMY) { return; } /* handle confirmed commit started by this session */ if (agt_ncx_cc_active() && agt_ncx_cc_ses_id() == scb->sid) { if (agt_ncx_cc_persist_id() == NULL) { agt_ncx_cancel_confirmed_commit(scb, NCX_CC_EVENT_CANCEL); } else { agt_ncx_clear_cc_ses_id(); } } agt_sys_send_netconf_session_end(scb, termreason, killedby); /* clear all resources and delete the session control block */ agt_ses_free_session(scb); } /* agt_ses_kill_session */ /******************************************************************** * FUNCTION agt_ses_process_first_ready * * Check the readyQ and process the first message, if any * * RETURNS: * TRUE if a message was processed * FALSE if the readyQ was empty *********************************************************************/ boolean agt_ses_process_first_ready (void) { ses_cb_t *scb; ses_ready_t *rdy; ses_msg_t *msg; status_t res; uint32 cnt; xmlChar buff[32]; rdy = ses_msg_get_first_inready(); if (!rdy) { return FALSE; } /* get the session control block that rdy is embedded into */ scb = agtses[rdy->sid]; if (scb == NULL) { log_debug("\nagt_ses: session %d gone", rdy->sid); return FALSE; } log_debug2("\nagt_ses msg ready for session %d", scb->sid); /* check the session control block state */ if (scb->state >= SES_ST_SHUTDOWN_REQ) { /* don't process the message or even it mark it * It will be cleaned up when the session is freed */ log_debug("\nagt_ses drop input, session %d shutting down", scb->sid); return TRUE; } /* make sure a message is really there */ msg = (ses_msg_t *)dlq_firstEntry(&scb->msgQ); if (!msg || !msg->ready) { SET_ERROR(ERR_INTERNAL_PTR); log_error("\nagt_ses ready Q message not correct"); if (msg && scb->state != SES_ST_INIT) { /* do not echo the ncx-connect message */ cnt = xml_strcpy(buff, (const xmlChar *)"Incoming msg for session "); snprintf((char *)(&buff[cnt]), sizeof(buff) - cnt, "%u", scb->sid); ses_msg_dump(msg, buff); } return FALSE; } else if (LOGDEBUG2 && scb->state != SES_ST_INIT) { cnt = xml_strcpy(buff, (const xmlChar *)"Incoming msg for session "); snprintf((char *)(&buff[cnt]), sizeof(buff) - cnt, "%u", scb->sid); ses_msg_dump(msg, buff); } /* setup the XML parser */ if (scb->reader) { /* reset the xmlreader */ res = xml_reset_reader_for_session(ses_read_cb, NULL, scb, scb->reader); } else { res = xml_get_reader_for_session(ses_read_cb, NULL, scb, &scb->reader); } /* process the message */ if (res == NO_ERR) { uint32 vtimeout_copy = ncx_get_vtimeout_value(); ncx123_set_vtimeout_value(scb->cache_timeout); /* process the message * the scb pointer may get deleted !!! */ agt_top_dispatch_msg(&scb); ncx123_set_vtimeout_value(vtimeout_copy); } else { if (LOGINFO) { log_info("\nReset xmlreader failed for session %d (%s)", scb->sid, get_error_string(res)); } agt_ses_kill_session(scb, 0, SES_TR_OTHER); scb = NULL; } if (scb) { /* free the message that was just processed */ dlq_remove(msg); ses_msg_free_msg(scb, msg); /* check if any messages left for this session */ msg = (ses_msg_t *)dlq_firstEntry(&scb->msgQ); if (msg && msg->ready) { ses_msg_make_inready(scb); } } return TRUE; } /* agt_ses_process_first_ready */ /******************************************************************** * FUNCTION agt_ses_check_timeouts * * Check if any sessions need to be dropped because they * have been idle too long. * *********************************************************************/ void agt_ses_check_timeouts (void) { ses_cb_t *scb; agt_profile_t *agt_profile; uint32 i, last; time_t timenow; double timediff; agt_profile = agt_get_profile(); /* check if both timeouts are disabled and the * confirmed-commit is not active -- quick exit */ if (agt_profile->agt_idle_timeout == 0 && agt_profile->agt_hello_timeout == 0 && !agt_ncx_cc_active()) { return; } /* at least one timeout enabled, so check if enough * time has elapsed since the last time through * the process loop; the AGT_SES_TIMEOUT_INTERVAL * is used for this purpose */ (void)uptime(&timenow); timediff = difftime(timenow, last_timeout_check); if (timediff < (double)AGT_SES_TIMEOUT_INTERVAL) { return; } /* reset the timeout interval for next time */ last_timeout_check = timenow; /* check all the sessions only if idle or hello * timeout checking is enabled */ if (agt_profile->agt_idle_timeout == 0 && agt_profile->agt_hello_timeout == 0) { last = 0; } else if (next_sesid == 0) { last = agt_profile->agt_max_sessions; } else { last = next_sesid; } for (i=1; i < last; i++) { scb = agtses[i]; if (scb == NULL) { continue; } /* check if the the hello timer needs to be tested */ if (agt_profile->agt_hello_timeout > 0 && scb->state == SES_ST_HELLO_WAIT) { timediff = difftime(timenow, scb->hello_time); if (timediff >= (double)agt_profile->agt_hello_timeout) { if (LOGDEBUG) { log_debug("\nHello timeout for session %u", i); } agt_ses_kill_session(scb, 0, SES_TR_TIMEOUT); continue; } } /* check if the the idle timer needs to be tested * check only active sessions * skip if notifications are active */ if (agt_profile->agt_idle_timeout > 0 && scb->active && !scb->notif_active && (strcmp(scb->peeraddr,"127.0.0.1")!=0)) { timediff = difftime(timenow, scb->last_rpc_time); if (timediff >= (double)agt_profile->agt_idle_timeout) { if (LOGDEBUG) { log_debug("\nIdle timeout for session %u", i); } agt_ses_kill_session(scb, 0, SES_TR_TIMEOUT); continue; } } } /* check the confirmed-commit timeout */ agt_ncx_check_cc_timeout(); } /* agt_ses_check_timeouts */ /******************************************************************** * FUNCTION agt_ses_ssh_port_allowed * * Check if the port number used for SSH connect is okay * * RETURNS: * TRUE if port allowed * FALSE if port not allowed *********************************************************************/ boolean agt_ses_ssh_port_allowed (uint16 port) { const agt_profile_t *profile; uint32 i; if (port == 0) { return FALSE; } profile = agt_get_profile(); if (!profile) { SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } if (profile->agt_ports[0]) { /* something configured, so use only that list */ for (i = 0; i < AGT_MAX_PORTS; i++) { if (port == profile->agt_ports[i]) { return TRUE; } } return FALSE; } else { /* no ports configured so allow 830 */ if (port==NCX_NCSSH_PORT) { return TRUE; } else { return FALSE; } } /*NOTREACHED*/ } /* agt_ses_ssh_port_allowed */ /******************************************************************** * FUNCTION agt_ses_fill_writeset * * Drain the ses_msg outreadyQ and set the specified fdset * Used by agt_ncxserver write_fd_set * * INPUTS: * fdset == pointer to fd_set to fill * maxfdnum == pointer to max fd int to fill in * * OUTPUTS: * *fdset is updated in * *maxfdnum may be updated *********************************************************************/ void agt_ses_fill_writeset (fd_set *fdset, int *maxfdnum) { ses_ready_t *rdy; ses_cb_t *scb; boolean done; FD_ZERO(fdset); done = FALSE; while (!done) { rdy = ses_msg_get_first_outready(); if (!rdy) { done = TRUE; } else { scb = agtses[rdy->sid]; if (scb && scb->state <= SES_ST_SHUTDOWN_REQ) { FD_SET(scb->fd, fdset); if (scb->fd > *maxfdnum) { *maxfdnum = scb->fd; } } } } } /* agt_ses_fill_writeset */ /******************************************************************** * FUNCTION agt_ses_get_inSessions * * operation handler for the inSessions counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ status_t agt_ses_get_inSessions (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { (void)scb; (void)virval; if (cbmode == GETCB_GET_VALUE) { VAL_UINT(dstval) = agttotals->inSessions; return NO_ERR; } else { return ERR_NCX_OPERATION_NOT_SUPPORTED; } } /* agt_ses_get_inSessions */ /******************************************************************** * FUNCTION agt_ses_get_inBadHellos * * operation handler for the inBadHellos counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ status_t agt_ses_get_inBadHellos (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { (void)scb; (void)virval; if (cbmode == GETCB_GET_VALUE) { VAL_UINT(dstval) = agttotals->inBadHellos; return NO_ERR; } else { return ERR_NCX_OPERATION_NOT_SUPPORTED; } } /* agt_ses_get_inBadHellos */ /******************************************************************** * FUNCTION agt_ses_get_inRpcs * * operation handler for the inRpcs counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ status_t agt_ses_get_inRpcs (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { (void)scb; (void)virval; if (cbmode == GETCB_GET_VALUE) { VAL_UINT(dstval) = agttotals->stats.inRpcs; return NO_ERR; } else { return ERR_NCX_OPERATION_NOT_SUPPORTED; } } /* agt_ses_get_inRpcs */ /******************************************************************** * FUNCTION agt_ses_get_inBadRpcs * * operation handler for the inBadRpcs counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ status_t agt_ses_get_inBadRpcs (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { (void)scb; (void)virval; if (cbmode == GETCB_GET_VALUE) { VAL_UINT(dstval) = agttotals->stats.inBadRpcs; return NO_ERR; } else { return ERR_NCX_OPERATION_NOT_SUPPORTED; } } /* agt_ses_get_inBadRpcs */ /******************************************************************** * FUNCTION agt_ses_get_outRpcErrors * * operation handler for the outRpcErrors counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ status_t agt_ses_get_outRpcErrors (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { (void)scb; (void)virval; if (cbmode == GETCB_GET_VALUE) { VAL_UINT(dstval) = agttotals->stats.outRpcErrors; return NO_ERR; } else { return ERR_NCX_OPERATION_NOT_SUPPORTED; } } /* agt_ses_get_outRpcErrors */ /******************************************************************** * FUNCTION agt_ses_get_outNotifications * * operation handler for the outNotifications counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ status_t agt_ses_get_outNotifications (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { (void)scb; (void)virval; if (cbmode == GETCB_GET_VALUE) { VAL_UINT(dstval) = agttotals->stats.outNotifications; return NO_ERR; } else { return ERR_NCX_OPERATION_NOT_SUPPORTED; } } /* agt_ses_get_outNotifications */ /******************************************************************** * FUNCTION agt_ses_get_droppedSessions * * operation handler for the droppedSessions counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ status_t agt_ses_get_droppedSessions (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { (void)scb; (void)virval; if (cbmode == GETCB_GET_VALUE) { VAL_UINT(dstval) = agttotals->droppedSessions; return NO_ERR; } else { return ERR_NCX_OPERATION_NOT_SUPPORTED; } } /* agt_ses_get_droppedSessions */ /******************************************************************** * FUNCTION agt_ses_get_session_inRpcs * * operation handler for the session/inRpcs counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ status_t agt_ses_get_session_inRpcs (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { ses_cb_t *testscb; ses_id_t sid; status_t res; (void)scb; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } sid = 0; res = get_session_key(virval, &sid); if (res != NO_ERR) { return res; } testscb = agtses[sid]; VAL_UINT(dstval) = testscb->stats.inRpcs; return NO_ERR; } /* agt_ses_get_session_inRpcs */ /******************************************************************** * FUNCTION agt_ses_get_session_inBadRpcs * * operation handler for the inBadRpcs counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ status_t agt_ses_get_session_inBadRpcs (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { ses_cb_t *testscb; ses_id_t sid; status_t res; (void)scb; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } sid = 0; res = get_session_key(virval, &sid); if (res != NO_ERR) { return res; } testscb = agtses[sid]; VAL_UINT(dstval) = testscb->stats.inBadRpcs; return NO_ERR; } /* agt_ses_get_session_inBadRpcs */ /******************************************************************** * FUNCTION agt_ses_get_session_outRpcErrors * * operation handler for the outRpcErrors counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ status_t agt_ses_get_session_outRpcErrors (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { ses_cb_t *testscb; ses_id_t sid; status_t res; (void)scb; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } sid = 0; res = get_session_key(virval, &sid); if (res != NO_ERR) { return res; } testscb = agtses[sid]; VAL_UINT(dstval) = testscb->stats.outRpcErrors; return NO_ERR; } /* agt_ses_get_session_outRpcErrors */ /******************************************************************** * FUNCTION agt_ses_get_session_outNotifications * * operation handler for the outNotifications counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ status_t agt_ses_get_session_outNotifications (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { ses_cb_t *testscb; ses_id_t sid; status_t res; (void)scb; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } sid = 0; res = get_session_key(virval, &sid); if (res != NO_ERR) { return res; } testscb = agtses[sid]; VAL_UINT(dstval) = testscb->stats.outNotifications; return NO_ERR; } /* agt_ses_get_session_outNotifications */ /******************************************************************** * FUNCTION agt_ses_invalidate_session_acm_caches * * Invalidate all session ACM caches so they will be rebuilt * TBD:: optimize and figure out exactly what needs to change * *********************************************************************/ void agt_ses_invalidate_session_acm_caches (void) { uint32 i; agt_profile_t *agt_profile; agt_profile = agt_get_profile(); for (i=0; iagt_max_sessions; i++) { if (agtses[i] != NULL) { agt_acm_invalidate_session_cache(agtses[i]); } } } /* acm_ses_invalidate_session_acm_caches */ /******************************************************************** * FUNCTION agt_ses_get_session_for_id * * get the session for the supplied sid * * INPUTS: * ses_id_t the id of the session to get * * RETURNS: * ses_cb_t* * *********************************************************************/ ses_cb_t* agt_ses_get_session_for_id(ses_id_t sid) { ses_cb_t *scb = NULL; agt_profile_t *agt_profile; agt_profile = agt_get_profile(); if ( sid >0 && sid < agt_profile->agt_max_sessions ) { scb = agtses[ sid ]; } return scb; } /* END file agt_ses.c */ yuma123_2.14/netconf/src/agt/agt_timer.c0000664000175000017500000002537214770023131020251 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_timer.c Handle timer services ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 23jan07 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_timer.h" #include "log.h" #include "ncx.h" #include "status.h" #include "uptime.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define AGT_TIMER_SKIP_COUNT 1 /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_timer_init_done = FALSE; static dlq_hdr_t timer_cbQ; static uint32 next_id; static uint32 skip_count; /******************************************************************** * FUNCTION get_timer_id * * Allocate a timer ID * * INPUTS: * none * RETURNS: * timer_id or zero if none available *********************************************************************/ static agt_timer_cb_t * find_timer_cb (uint32 timer_id) { agt_timer_cb_t *timer_cb; for (timer_cb = (agt_timer_cb_t *) dlq_firstEntry(&timer_cbQ); timer_cb != NULL; timer_cb = (agt_timer_cb_t *) dlq_nextEntry(timer_cb)) { if (timer_cb->timer_id == timer_id) { return timer_cb; } } return NULL; } /* find_timer_cb */ /******************************************************************** * FUNCTION get_timer_id * * Allocate a timer ID * * INPUTS: * none * RETURNS: * timer_id or zero if none available *********************************************************************/ static uint32 get_timer_id (void) { agt_timer_cb_t *timer_cb; uint32 id; if (next_id < NCX_MAX_UINT) { return next_id++; } if (dlq_empty(&timer_cbQ)) { next_id = 1; return next_id++; } id = 1; while (id < NCX_MAX_UINT) { timer_cb = find_timer_cb(id); if (timer_cb == NULL) { return id; } id++; } return 0; } /* get_timer_id */ /******************************************************************** * FUNCTION new_timer_cb * * Malloc and init a new timer control block * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ static agt_timer_cb_t * new_timer_cb (void) { agt_timer_cb_t *timer_cb; timer_cb = m__getObj(agt_timer_cb_t); if (timer_cb == NULL) { return NULL; } memset(timer_cb, 0x0, sizeof(agt_timer_cb_t)); return timer_cb; } /* new_timer_cb */ /******************************************************************** * FUNCTION free_timer_cb * * Clean and free a timer control block * * INPUTS: * timer_cb == control block to free * *********************************************************************/ static void free_timer_cb (agt_timer_cb_t *timer_cb) { m__free(timer_cb); } /* free_timer_cb */ /******************************************************************** * FUNCTION agt_timer_init * * Initialize the agt_timer module * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ void agt_timer_init (void) { if (!agt_timer_init_done) { dlq_createSQue(&timer_cbQ); next_id = 1; skip_count = 0; agt_timer_init_done = TRUE; } } /* agt_timer_init */ /******************************************************************** * FUNCTION agt_timer_cleanup * * Cleanup the agt_timer module. * *********************************************************************/ void agt_timer_cleanup (void) { agt_timer_cb_t *timer_cb; if (agt_timer_init_done) { while (!dlq_empty(&timer_cbQ)) { timer_cb = (agt_timer_cb_t *)dlq_deque(&timer_cbQ); free_timer_cb(timer_cb); } agt_timer_init_done = FALSE; } } /* agt_timer_cleanup */ /******************************************************************** * FUNCTION agt_timer_handler * * Handle an incoming agent timer polling interval * main routine called by agt_signal_handler * *********************************************************************/ void agt_timer_handler (void) { agt_timer_cb_t *timer_cb, *next_timer_cb; time_t timenow; double timediff; int retval; if (skip_count < AGT_TIMER_SKIP_COUNT) { skip_count++; return; } else { skip_count = 0; } (void)uptime(&timenow); for (timer_cb = (agt_timer_cb_t *)dlq_firstEntry(&timer_cbQ); timer_cb != NULL; timer_cb = next_timer_cb) { next_timer_cb = (agt_timer_cb_t *)dlq_nextEntry(timer_cb); timediff = difftime(timenow, timer_cb->timer_start_time); if (timediff >= (double)timer_cb->timer_duration) { if (LOGDEBUG3) { log_debug3("\nagt_timer: timer %u popped", timer_cb->timer_id); } retval = (*timer_cb->timer_cbfn)(timer_cb->timer_id, timer_cb->timer_cookie); if (retval != 0 || !timer_cb->timer_periodic) { /* destroy this timer */ dlq_remove(timer_cb); free_timer_cb(timer_cb); } else { /* reset this periodic timer */ (void)uptime(&timer_cb->timer_start_time); } } } } /* agt_timer_handler */ /******************************************************************** * FUNCTION agt_timer_create * * Malloc and start a new timer control block * * INPUTS: * seconds == number of seconds to wait between polls * is_periodic == TRUE if periodic timer * FALSE if a 1-event timer * timer_fn == address of callback function to invoke when * the timer poll event occurs * cookie == address of user cookie to pass to the timer_fn * ret_timer_id == address of return timer ID * * OUTPUTS: * *ret_timer_id == timer ID for the allocated timer, * if the return value is NO_ERR * * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ status_t agt_timer_create (uint32 seconds, boolean is_periodic, agt_timer_fn_t timer_fn, void *cookie, uint32 *ret_timer_id) { agt_timer_cb_t *timer_cb; uint32 timer_id; #ifdef DEBUG if (timer_fn == NULL || ret_timer_id == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } if (seconds == 0) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif *ret_timer_id = 0; timer_id = get_timer_id(); if (timer_id == 0) { return ERR_NCX_RESOURCE_DENIED; } timer_cb = new_timer_cb(); if (timer_cb == NULL) { return ERR_INTERNAL_MEM; } *ret_timer_id = timer_id; timer_cb->timer_id = timer_id; timer_cb->timer_periodic = is_periodic; timer_cb->timer_cbfn = timer_fn; (void)uptime(&timer_cb->timer_start_time); timer_cb->timer_duration = seconds; timer_cb->timer_cookie = cookie; dlq_enque(timer_cb, &timer_cbQ); return NO_ERR; } /* agt_timer_create */ /******************************************************************** * FUNCTION agt_timer_restart * * Restart a timer with a new timeout value. * If this is a periodic timer, then the interval * will be changed to the new value. Otherwise * a 1-shot timer will just be reset to the new value * * INPUTS: * timer_id == timer ID to reset * seconds == new timeout value * * RETURNS: * status, NO_ERR if all okay, *********************************************************************/ status_t agt_timer_restart (uint32 timer_id, uint32 seconds) { agt_timer_cb_t *timer_cb; #ifdef DEBUG if (seconds == 0) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif timer_cb = find_timer_cb(timer_id); if (timer_cb == NULL) { return ERR_NCX_NOT_FOUND; } (void)uptime(&timer_cb->timer_start_time); timer_cb->timer_duration = seconds; return NO_ERR; } /* agt_timer_restart */ /******************************************************************** * FUNCTION agt_timer_delete * * Remove and delete a timer control block * periodic timers need to be deleted to be stopped * 1-shot timers will be deleted automatically after * they expire and the callback is invoked * * INPUTS: * timer_id == timer ID to destroy * *********************************************************************/ void agt_timer_delete (uint32 timer_id) { agt_timer_cb_t *timer_cb; timer_cb = find_timer_cb(timer_id); if (timer_cb == NULL) { log_warn("\nagt_timer: delete unknown timer '%u'", timer_id); return; } dlq_remove(timer_cb); free_timer_cb(timer_cb); } /* agt_timer_delete */ /* END file agt_timer.c */ yuma123_2.14/netconf/src/agt/agt_rpc.h0000664000175000017500000002744014770023131017720 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_rpc #define _H_agt_rpc /* FILE: agt_rpc.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF protocol remote procedure call server-side definitions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30-apr-05 abb Begun. */ #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_rpc_err #include "rpc_err.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* this constant is for the number of callback slots * allocated in a 'cbset', and only includes the * RPC phases that allow callback functions */ #define AGT_RPC_NUM_PHASES 3 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* There are 3 different callbacks possible in the * server processing chain. * * Only AGT_RPC_PH_INVOKE is needed to do any work * Validate is needed if parameter checking beyond the * YANG constraints, such as checking if a needed * lock is available * * The engine will check for optional callbacks during * RPC processing. * */ typedef enum agt_rpc_phase_t_ { AGT_RPC_PH_VALIDATE, /* (2) cb after the input is parsed */ AGT_RPC_PH_INVOKE, /* (3) cb to invoke the requested method */ AGT_RPC_PH_POST_REPLY, /* (5) cb after the reply is generated */ AGT_RPC_PH_PARSE, /* (1) NO CB FOR THIS STATE */ AGT_RPC_PH_REPLY /* (4) NO CB FOR THIS STATE */ } agt_rpc_phase_t; /* Template for RPC server callbacks * The same template is used for all RPC callback phases */ typedef status_t (*agt_rpc_method_t) (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode); typedef struct agt_rpc_cbset_t_ { agt_rpc_method_t acb[AGT_RPC_NUM_PHASES]; } agt_rpc_cbset_t; /* Callback template for RPCs that use an inline callback * function instead of generating a malloced val_value_t tree * * INPUTS: * scb == session control block * msg == RPC request in progress * indent == start indent amount; ignored if the server * is configured not to use PDU indentation * RETURNS: * status of the output operation */ typedef status_t (*agt_rpc_data_cb_t) (ses_cb_t *scb, rpc_msg_t *msg, uint32 indent); /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_rpc_init * * Initialize the agt_rpc module * Adds the agt_rpc_dispatch function as the handler * for the NETCONF top-level element. * should call once to init RPC server module * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ extern status_t agt_rpc_init (void); /******************************************************************** * FUNCTION agt_rpc_cleanup * * Cleanup the agt_rpc module. * Unregister the top-level NETCONF element * should call once to cleanup RPC server module * *********************************************************************/ extern void agt_rpc_cleanup (void); /******************************************************************** * FUNCTION agt_rpc_register_method * * add callback for 1 phase of RPC processing * * INPUTS: * module == module name or RPC method * method_name == RPC method name * phase == RPC server callback phase for this callback * method == pointer to callback function * * RETURNS: * status of the operation *********************************************************************/ extern status_t agt_rpc_register_method (const xmlChar *module, const xmlChar *method_name, agt_rpc_phase_t phase, agt_rpc_method_t method); /******************************************************************** * FUNCTION agt_rpc_support_method * * mark an RPC method as supported within the server * this is needed for operations dependent on capabilities * * INPUTS: * module == module name of RPC method (really module name) * method_name == RPC method name *********************************************************************/ extern void agt_rpc_support_method (const xmlChar *module, const xmlChar *method_name); /******************************************************************** * FUNCTION agt_rpc_unsupport_method * * mark an RPC method as unsupported within the server * this is needed for operations dependent on capabilities * * INPUTS: * module == module name of RPC method (really module name) * method_name == RPC method name *********************************************************************/ extern void agt_rpc_unsupport_method (const xmlChar *module, const xmlChar *method_name); /******************************************************************** * FUNCTION agt_rpc_unregister_method * * remove the callback functions for all phases of RPC processing * for the specified RPC method * * INPUTS: * module == module name of RPC method (really module name) * method_name == RPC method name *********************************************************************/ extern void agt_rpc_unregister_method (const xmlChar *module, const xmlChar *method_name); /******************************************************************** * FUNCTION agt_rpc_dispatch * * Dispatch an incoming request * called by top.c: * This function is registered with top_register_node * for the module 'yuma-netconf', top-node 'rpc' * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ extern void agt_rpc_dispatch (ses_cb_t *scb, xml_node_t *top); /******************************************************************** * FUNCTION agt_rpc_load_config_file * * Dispatch an internal request * used for OP_EDITOP_LOAD to load the running from startup * and OP_EDITOP_REPLACE to restore running from backup * * - Create a dummy session and RPC message * - Call a special agt_ps_parse function to parse the config file * - Call the special agt_ncx function to invoke the proper * parmset and application 'validate' callback functions, and * record all the error/warning messages * - Call the special ncx_agt function to invoke all the 'apply' * callbacks as needed * - transfer any error messages to the cfg->load_errQ * * INPUTS: * filespec == XML config filespec to load * cfg == cfg_template_t to fill in * isload == TRUE for normal load-config * FALSE for restore backup load-config * use_sid == session ID to use for the access control * * RETURNS: * status *********************************************************************/ extern status_t agt_rpc_load_config_file (const xmlChar *filespec, cfg_template_t *cfg, boolean isload, ses_id_t use_sid); /******************************************************************** * FUNCTION agt_rpc_get_config_file * * Dispatch an internal request * except skip the INVOKE phase and just remove * the 'config' node from the input and return it * * - Create a dummy session and RPC message * - Call a special agt_ps_parse function to parse the config file * - Call the special agt_ncx function to invoke the proper * parmset and application 'validate' callback functions, and * record all the error/warning messages * - return the element if no errors * - otherwise return all the error messages in a Q * * INPUTS: * filespec == XML config filespec to get * targetcfg == target database to validate against * use_sid == session ID to use for the access control * errorQ == address of return queue of rpc_err_rec_t structs * res == address of return status * * OUTPUTS: * if any errors, the error structs are transferred to * the errorQ (if it is non-NULL). In this case, the caller * must free these data structures with ncx/rpc_err_clean_errQ * * *res == return status * * RETURNS: * malloced and filled in struct representing a element * NULL if some error, check errorQ and *res *********************************************************************/ extern val_value_t * agt_rpc_get_config_file (const xmlChar *filespec, cfg_template_t *targetcfg, ses_id_t use_sid, dlq_hdr_t *errorQ, status_t *res); /******************************************************************** * FUNCTION agt_rpc_fill_rpc_error * * Fill one like element using the specified * namespace and name, which may be different than NETCONF * * INPUTS: * err == error record to use to fill 'rpcerror' * rpcerror == NCX_BT_CONTAINER value struct already * initialized. The val_add_child function * will use this parm as the parent. * This namespace will be used for all * child nodes * * RETURNS: * status *********************************************************************/ extern status_t agt_rpc_fill_rpc_error (const rpc_err_rec_t *err, val_value_t *rpcerror); /******************************************************************** * FUNCTION agt_rpc_send_error_reply * * Operation failed or was never attempted * Return an with an * * INPUTS: * scb == session control block * retres == error number for termination reason * *********************************************************************/ extern void agt_rpc_send_error_reply (ses_cb_t *scb, status_t retres); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_rpc */ yuma123_2.14/netconf/src/agt/agt_ncx.c0000664000175000017500000036473114770023131017726 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_ncx.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 04feb06 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cap.h" #include "agt_cb.h" #include "agt_cfg.h" #include "agt_cli.h" #include "agt_ncx.h" #include "agt_nmda.h" #include "agt_rpc.h" #include "agt_rpcerr.h" #include "agt_ses.h" #include "agt_sys.h" #include "agt_state.h" #include "agt_time_filter.h" #include "agt_util.h" #include "agt_val.h" #include "agt_commit_validate.h" #include "cap.h" #include "cfg.h" #include "ncxmod.h" #include "obj.h" #include "op.h" #include "rpc.h" #include "rpc_err.h" #include "ses.h" #include "status.h" #include "tstamp.h" #include "val.h" #include "xml_wr.h" #include "yangconst.h" #include "uptime.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define IF_MODIFIED_SINCE (const xmlChar *)"if-modified-since" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* candidate commit control block struct */ typedef struct commit_cb_t_ { xmlChar *cc_backup_source; /* malloced */ xmlChar *cc_persist_id; /* malloced */ time_t cc_start_time; uint32 cc_cancel_timeout; ses_id_t cc_ses_id; boolean cc_active; } commit_cb_t; /* copy-config parm-block passed from validate to invoke callback */ typedef struct copy_parms_t_ { cfg_template_t *srccfg; cfg_template_t *destcfg; val_value_t *srcval; val_value_t *srcurlval; /* malloced */ xmlChar *srcfile; /* malloced */ xmlChar *destfile; /* malloced */ xmlChar *desturlspec; /* malloced */ } copy_parms_t; /* edit-config parm-block passed from validate to invoke callback */ typedef struct edit_parms_t_ { cfg_template_t *target; val_value_t *srcval; val_value_t *urlval; /* malloced */ op_editop_t defop; op_testop_t testop; } edit_parms_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_ncx_init_done = FALSE; static commit_cb_t commit_cb; /******************************************************************** * FUNCTION new_copyparms * * malloc a copy parms struct * * RETURNS: * malloced struct or NULL if no memory error *********************************************************************/ static copy_parms_t * new_copyparms (void) { copy_parms_t *copyparms; copyparms = m__getObj(copy_parms_t); if (copyparms == NULL) { return NULL; } memset(copyparms, 0x0, sizeof(copy_parms_t)); return copyparms; } /* new_copyparms */ /******************************************************************** * FUNCTION free_copyparms * * clean and free a copy parms struct * * INPUTS: * copyparms == struct to free *********************************************************************/ static void free_copyparms ( copy_parms_t *copyparms) { if (copyparms->srcurlval != NULL) { val_free_value(copyparms->srcurlval); } if (copyparms->srcfile != NULL) { m__free(copyparms->srcfile); } if (copyparms->destfile != NULL) { m__free(copyparms->destfile); } if (copyparms->desturlspec != NULL) { m__free(copyparms->desturlspec); } m__free(copyparms); } /* free_copyparms */ /******************************************************************** * FUNCTION cfg_save_inline * * Save the specified cfg to the its startup source, which should * be stored in the cfg struct * * INPUTS: * target_url == filespec where to save newroot * newroot == value root to save * forstartup == TRUE if this is the startup config being saved * FALSE if this is a URL file being saved * * RETURNS: * status *********************************************************************/ static status_t cfg_save_inline (const xmlChar *target_url, val_value_t *newroot, boolean forstartup) { agt_profile_t *profile; cfg_template_t *startup; val_value_t *copystartup; status_t res; xml_attrs_t attrs; startup = NULL; copystartup = NULL; res = NO_ERR; profile = agt_get_profile(); if (forstartup) { startup = cfg_get_config_id(NCX_CFGID_STARTUP); if (startup != NULL) { copystartup = val_clone_config_data(newroot, &res); if (copystartup == NULL) { return res; } } } if (res == NO_ERR) { /* write the new startup config */ xml_init_attrs(&attrs); /* output to the specified file or STDOUT */ res = xml_wr_check_file(target_url, newroot, &attrs, XMLMODE, WITHHDR, TRUE, 0, profile->agt_indent, agt_check_save); xml_clean_attrs(&attrs); if (res == NO_ERR && forstartup && copystartup != NULL) { /* toss the old startup and save the new one */ if (startup->root) { val_free_value(startup->root); } startup->root = copystartup; copystartup = NULL; } } if (copystartup) { val_free_value(copystartup); } return res; } /* cfg_save_inline */ /******************************************************************** * FUNCTION get_validate * * get : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t get_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { cfg_template_t *source; val_value_t *testval; status_t res, res2; boolean empty_callback = FALSE; /* check if the config is ready to read */ source = cfg_get_config_id(NCX_CFGID_RUNNING); if (!source) { res = ERR_NCX_OPERATION_FAILED; } else { res = cfg_ok_to_read(source); } if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } /* check if the optional filter parameter is ok */ res = agt_validate_filter(scb, msg); /* check the with-defaults parameter */ res2 = agt_set_with_defaults(scb, msg, methnode); if (res != NO_ERR) { return res; /* error already recorded */ } if (res2 != NO_ERR) { return res2; /* error already recorded */ } testval = val_find_child(msg->rpc_input, y_yuma_time_filter_M_yuma_time_filter, IF_MODIFIED_SINCE); if (testval != NULL && testval->res == NO_ERR) { boolean isneg = FALSE; xmlChar *utcstr; int ret; utcstr = tstamp_convert_to_utctime(VAL_STR(testval), &isneg, &res); if (res != NO_ERR || isneg) { if (utcstr) { m__free(utcstr); } if (isneg) { res = ERR_NCX_INVALID_VALUE; } agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_VAL, testval, NCX_NT_VAL, testval); return res; } ret = xml_strcmp(source->last_ch_time, utcstr); if (ret <= 0) { empty_callback = TRUE; } m__free(utcstr); } /* cache the 2 parameters and the data output callback function * There is no invoke function -- it is handled automatically * by the agt_rpc module */ msg->rpc_user1 = source; msg->rpc_data_type = RPC_DATA_STD; if (empty_callback) { msg->rpc_datacb = agt_output_empty; } else { msg->rpc_datacb = agt_output_filter; } return NO_ERR; } /* get_validate */ /******************************************************************** * FUNCTION get_config_validate * * get-config : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t get_config_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { cfg_template_t *source; val_value_t *testval; boolean empty_callback = FALSE; status_t res, res2; /* check if the source config database exists */ res = agt_get_cfg_from_parm(NCX_EL_SOURCE, msg, methnode, &source); if (res != NO_ERR) { return res; /* error already recorded */ } /* check if this config can be read right now */ res = cfg_ok_to_read(source); if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } /* check if the optional filter parameter is ok */ res = agt_validate_filter(scb, msg); /* check the with-defaults parameter */ res2 = agt_set_with_defaults(scb, msg, methnode); if (res != NO_ERR) { return res; /* error already recorded */ } if (res2 != NO_ERR) { return res2; /* error already recorded */ } testval = val_find_child(msg->rpc_input, y_yuma_time_filter_M_yuma_time_filter, IF_MODIFIED_SINCE); if (testval != NULL && testval->res == NO_ERR) { boolean isneg = FALSE; xmlChar *utcstr; int ret; utcstr = tstamp_convert_to_utctime(VAL_STR(testval), &isneg, &res); if (res != NO_ERR || isneg) { if (utcstr) { m__free(utcstr); } if (isneg) { res = ERR_NCX_INVALID_VALUE; } agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_VAL, testval, NCX_NT_VAL, testval); return res; } ret = xml_strcmp(source->last_ch_time, utcstr); if (ret <= 0) { empty_callback = TRUE; } m__free(utcstr); } /* cache the 2 parameters and the data output callback function * There is no invoke function -- it is handled automatically * by the agt_rpc module */ msg->rpc_user1 = source; msg->rpc_data_type = RPC_DATA_STD; if (empty_callback) { msg->rpc_datacb = agt_output_empty; } else { msg->rpc_datacb = agt_output_filter; } return NO_ERR; } /* get_config_validate */ /******************************************************************** * FUNCTION edit_config_validate * * edit-config : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t edit_config_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { cfg_template_t *target = NULL; val_value_t *val = NULL, *urlval = NULL; const agt_profile_t *agt_profile = agt_get_profile(); const xmlChar *urlstr = NULL; xmlChar *urlspec = NULL; edit_parms_t *editparms = NULL; op_editop_t defop = OP_EDITOP_MERGE; op_errop_t errop = OP_ERROP_STOP; op_testop_t testop = OP_TESTOP_NONE; boolean rootcheck = FALSE; /* check if the target config database exists */ status_t res = agt_get_cfg_from_parm(NCX_EL_TARGET, msg, methnode, &target); if (res != NO_ERR) { return res; /* error already recorded */ } /* check if the server supports this config as a target */ if (target->cfg_id == NCX_CFGID_STARTUP || (target->cfg_id == NCX_CFGID_RUNNING && !(agt_profile->agt_targ == NCX_AGT_TARG_RUNNING || agt_profile->agt_targ == NCX_AGT_TARG_CAND_RUNNING))) { /* trying to edit running but this is not allowed */ res = ERR_NCX_CONFIG_NOT_TARGET; val = val_find_child(msg->rpc_input, NC_MODULE, NCX_EL_TARGET); agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, target->name, (val != NULL) ? NCX_NT_VAL : NCX_NT_NONE, val); return res; } /* get the default-operation parameter */ val = val_find_child(msg->rpc_input, NC_MODULE, NCX_EL_DEFAULT_OPERATION); if (!val || val->res != NO_ERR) { /* set to the default if any error */ defop = OP_EDITOP_MERGE; } else { defop = op_defop_id(VAL_ENUM_NAME(val)); } /* get the error-option parameter */ val = val_find_child(msg->rpc_input, NC_MODULE, NCX_EL_ERROR_OPTION); if (!val || val->res != NO_ERR) { /* set to the default if any error */ errop = OP_ERROP_STOP; } else { errop = op_errop_id(VAL_ENUM_NAME(val)); } /* the internal processing needs to know if rollback is * requested to optimize the undo-prep and cleanup code path * !!! This field is obsolete; rollback always done now !!! */ msg->rpc_err_option = errop; /* Get the test-option parameter: * * This implementation always runs the validation tests in * the same order, even if the value 'set' is used. * The validation stage is never bypassed, even if 'set' * is used instead of 'test-then-set'. * * Get the value to check for the test-only extension */ val = val_find_child(msg->rpc_input, NC_MODULE, NCX_EL_TEST_OPTION); if (val == NULL) { /* set to the default if not present */ if (agt_profile->agt_usevalidate) { testop = OP_TESTOP_TESTTHENSET; } else { testop = OP_TESTOP_SET; } } else if (val->res != NO_ERR) { res = val->res; } else if (!agt_profile->agt_usevalidate) { res = ERR_NCX_UNKNOWN_ELEMENT; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_VAL, val, NCX_NT_VAL, val); return res; } else { testop = op_testop_enum(VAL_ENUM_NAME(val)); } /* try to get the config parameter */ val = val_find_child(msg->rpc_input, NC_MODULE, NCX_EL_CONFIG); if (val == NULL) { val_value_t *errval = NULL; /* try to get the config parameter */ res = agt_get_url_from_parm(NCX_EL_URL, msg, methnode, &urlstr, &errval); if (res == NO_ERR) { /* get the filespec out of the URL */ urlspec = agt_get_filespec_from_url(urlstr, &res); if (urlspec != NULL && res == NO_ERR) { /* get the external file loaded into a value struct * for the object node */ val = agt_rpc_get_config_file(urlspec, target, SES_MY_SID(scb), RPC_ERR_QUEUE(msg), &res); urlval = val; } else { /* convert file name to full filespec failed */ agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_VAL, errval, NCX_NT_VAL, errval); } } /* else error done */ } /* if this is an edit-config on running or a test-then-set edit * on candidate then force an agt_val_root_check after the * apply phase; rollback will be done if any test fails */ if (res==NO_ERR && ((target->cfg_id == NCX_CFGID_RUNNING) || (target->cfg_id == NCX_CFGID_CANDIDATE && testop == OP_TESTOP_TESTTHENSET))) { /* !!! defer this test; just apply for real and * !!! rollback the edits if root check fails * !!! force a rootcheck in the apply phase */ rootcheck = TRUE; } if (res == NO_ERR) { /* allocate a new transaction control block */ msg->rpc_txcb = agt_cfg_new_transaction(target->cfg_id, AGT_CFG_EDIT_TYPE_PARTIAL, rootcheck, FALSE, &res); if (msg->rpc_txcb == NULL || res != NO_ERR) { if (res == NO_ERR) { res = ERR_NCX_OPERATION_FAILED; } agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } } if (res == NO_ERR && val != NULL && val->res == NO_ERR) { /* first set the config node to canonical order */ val_set_canonical_order(val); /* validate the element (wrt/ embedded operation * attributes) against the existing data model. * records will be added as needed */ res = agt_val_validate_write(scb, msg, target, val, defop); } else if (res == NO_ERR) { if (!val) { /* this is reported in agt_val_parse phase */ res = ERR_NCX_DATA_MISSING; } else { res = val->res; } } /* save the edit options in 'user1' */ if (res == NO_ERR) { editparms = m__getObj(edit_parms_t); if (editparms == NULL) { res = ERR_INTERNAL_MEM; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } else { editparms->target = target; editparms->srcval = val; editparms->urlval = urlval; /* transfer memory here */ editparms->defop = defop; editparms->testop = testop; msg->rpc_user1 = (void *)editparms; } } if (res != NO_ERR && urlval) { val_free_value(urlval); } if (urlspec != NULL) { m__free(urlspec); } return res; } /* edit_config_validate */ /******************************************************************** * FUNCTION edit_config_invoke * * edit-config : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t edit_config_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { cfg_template_t *target; val_value_t *srcval, *urlval; edit_parms_t *editparms; agt_profile_t *profile; op_editop_t defop; op_testop_t testop; status_t res; (void)methnode; /* get the cached options */ editparms = (edit_parms_t *)msg->rpc_user1; defop = editparms->defop; testop = editparms->testop; target = editparms->target; urlval = editparms->urlval; srcval = editparms->srcval; profile = agt_get_profile(); /* quick exit if this is a test-only request */ if (testop == OP_TESTOP_TESTONLY) { return NO_ERR; } /* apply the into the target config */ res = agt_val_apply_write(scb, msg, target, srcval, defop); /* check if the NV-storage needs to be updated after each * successful edit-config */ if (res == NO_ERR && profile->agt_targ == NCX_AGT_TARG_RUNNING && profile->agt_has_startup == FALSE) { res = agt_ncx_cfg_save(target, FALSE); if (res != NO_ERR) { log_error("\nError: Save to NV-storage failed (%s)", get_error_string(res)); } } /* cleanup the urlval and editparms * allocated in validate callback */ if (urlval != NULL) { val_free_value(urlval); } m__free(editparms); return res; } /* edit_config_invoke */ /******************************************************************** * FUNCTION validate_copy_source * * Muster and validate the parameters for the source of the copy operation. * * INPUTS: * scb - the session control block * msg - the message that triggered the copy_config_validate operation * methnode - the xml method node * * OUTPUTS: * srccfg - the source configuration * srcurl - the url of the source * srcfile - the source file * * RETURNS: * status *********************************************************************/ static status_t validate_copy_source( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode, cfg_template_t **srccfg, xmlChar **srcfile, xmlChar **srcurlspec, val_value_t **srcval ) { status_t res = NO_ERR; const void* errPtr = 0; ncx_node_t errNodeType = NCX_NT_NONE; const cap_list_t *mycaps = agt_cap_get_caps(); val_value_t *errval2 = NULL; if ( !mycaps ) { return SET_ERROR(ERR_INTERNAL_PTR); } res = agt_get_cfg_from_parm( NCX_EL_SOURCE, msg, methnode, srccfg ); if ( NO_ERR == res ) { if ( *srccfg ) { switch ( (*srccfg)->cfg_id ) { case NCX_CFGID_CANDIDATE: if ( !cap_std_set( mycaps, CAP_STDID_CANDIDATE ) ) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } break; case NCX_CFGID_RUNNING: break; case NCX_CFGID_STARTUP: if ( !cap_std_set( mycaps, CAP_STDID_STARTUP ) ) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } errPtr = srccfg; errNodeType = NCX_NT_CFG; } else if ( ERR_NCX_FOUND_INLINE == res ) { res = agt_get_inline_cfg_from_parm( NCX_EL_SOURCE, msg, methnode, srcval ); } else if ( ERR_NCX_FOUND_URL == res ) { const xmlChar *srcurl = NULL; res = agt_get_url_from_parm( NCX_EL_SOURCE, msg, methnode, &srcurl, &errval2 ); if ( NO_ERR == res ) { if ( srcurl ) { /* get the pointer to the filespec part */ *srcurlspec = agt_get_filespec_from_url( srcurl, &res ); /* check the URL parameter to see if it is valid */ if ( *srcurlspec ) { if ( NO_ERR == res) { status_t ignoreRes; /* allowed to be a not-found error, which is ignored */ *srcfile = ncxmod_find_data_file( *srcurlspec, FALSE, &ignoreRes ); } } else { res = SET_ERROR(ERR_INTERNAL_PTR); } errPtr = srcurl; errNodeType = NCX_NT_STRING; } } } if (res != NO_ERR) { /* set the errval to the destval for now, just in case * look for more errors, even if src already invalid */ val_value_t* errval; if (errval2) { errval = errval2; } else { errval = val_find_child( msg->rpc_input, NC_MODULE, NCX_EL_SOURCE ); } /* the sourcs is not found */ agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, errNodeType, errPtr, NCX_NT_VAL, errval); } return res; } /******************************************************************** * FUNCTION validate_copy_dest * * Muster and validate the parameters for the dest of the copy operation. * * INPUTS: * scb - the session control block * msg - the message that triggered the copy_config_validate operation * methnode - the xml method node * * OUTPUTS: * destcfg - the dest configuration * desturl - the url of the dest * destfile - the dest file * * RETURNS: * status *********************************************************************/ static status_t validate_copy_dest( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode, cfg_template_t **destcfg, xmlChar **destfile, xmlChar **desturlspec ) { status_t res = NO_ERR; const void* errPtr = 0; ncx_node_t errNodeType = NCX_NT_NONE; const cap_list_t *mycaps = agt_cap_get_caps(); val_value_t *errval2 = NULL; if ( !mycaps ) { return SET_ERROR(ERR_INTERNAL_PTR); } /* get the config to copy to */ res = agt_get_cfg_from_parm(NCX_EL_TARGET, msg, methnode, destcfg); if (res == NO_ERR ) { if ( *destcfg ) { switch ( (*destcfg)->cfg_id ) { case NCX_CFGID_CANDIDATE: if ( !cap_std_set(mycaps, CAP_STDID_CANDIDATE) ) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } break; case NCX_CFGID_RUNNING: /* a server may choose not to support this so it is not * supported use edit-config instead */ res = ERR_NCX_OPERATION_NOT_SUPPORTED; break; case NCX_CFGID_STARTUP: if (!cap_std_set(mycaps, CAP_STDID_STARTUP)) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } errPtr = destcfg; errNodeType = NCX_NT_CFG; } } else if ( ERR_NCX_FOUND_INLINE == res ) { /* got some inline as the parameter * error not recorded yet; only OK for */ res = ERR_NCX_OPERATION_NOT_SUPPORTED; } else if ( ERR_NCX_FOUND_URL == res ) { const xmlChar *desturl = NULL; res = agt_get_url_from_parm( NCX_EL_TARGET, msg, methnode, &desturl, &errval2 ); if ( NO_ERR == res ) { if ( desturl ) { /* check the destination URL */ *desturlspec = agt_get_filespec_from_url( desturl, &res ); /* check the URL parameter to see if it is valid */ if ( *desturlspec ) { if ( NO_ERR == res ) { status_t ignoreRes; /* allowed to be a not-found error, which is ignored */ *destfile = ncxmod_find_data_file( *desturlspec, FALSE, &ignoreRes); } } else { res = ERR_INTERNAL_PTR; } errPtr = desturl; errNodeType = NCX_NT_STRING; } } } if (res != NO_ERR) { /* set the errval to the destval for now, just in case * look for more errors, even if src already invalid */ val_value_t* errval; if (errval2) { errval = errval2; } else { errval = val_find_child( msg->rpc_input, NC_MODULE, NCX_EL_TARGET ); } /* cannot use this configuration datastore */ agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, errNodeType, errPtr, NCX_NT_VAL, errval); } return res; } /******************************************************************** * FUNCTION copy_config_validate * * copy-config : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t copy_config_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; /// Function return value /// Source configuration parameters cfg_template_t *srccfg = NULL; xmlChar *srcfile = NULL; xmlChar *srcurlspec = NULL; val_value_t *srcval = NULL; val_value_t *srcurlval = NULL; /// Destination configuration parameters cfg_template_t *destcfg = NULL; xmlChar *destfile = NULL; xmlChar *desturlspec = NULL; val_value_t *errval; copy_parms_t *copyparms; /* check the source config, URL, or inline (copy source) */ res = validate_copy_source( scb, msg, methnode, &srccfg, &srcfile, &srcurlspec, &srcval ); /* NOTE: The original version of the function simply ignored the * value of res set by the switch statement that validated * srccfg->cfg_id and continued on regardless. It is assumed that * this was not the intended behaviour, ergo the refactored * version now checks the result. */ if ( NO_ERR == res ) { res = validate_copy_dest( scb, msg, methnode, &destcfg, &destfile, &desturlspec ); } errval = val_find_child( msg->rpc_input, NC_MODULE, NCX_EL_TARGET ); if ( NO_ERR == res ) { /* check a corner-case: URL to database */ if ( srcfile && destcfg ) { /* get the URL contents as a value struct */ srcurlval = agt_rpc_get_config_file( srcfile, destcfg, SES_MY_SID(scb), RPC_ERR_QUEUE(msg), &res ); } /* check a corner-case: URL to URL * this is optional-to-support and is not allowed by this server */ /* NOTE: Invariant: it is not pssible to have srcfile and srcurl */ else if ( srcurlspec && desturlspec ) { const xmlChar *desturl = NULL; res = agt_get_url_from_parm( NCX_EL_TARGET, msg, methnode, &desturl, &errval ); res = ERR_NCX_OPERATION_NOT_SUPPORTED; agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, (const void *)desturl, NCX_NT_VAL, errval); } } /* check the with-defaults parameter, but only if the * target is an ; otherwise basic mode will be used */ if ( res == NO_ERR && desturlspec ) { res = agt_set_with_defaults(scb, msg, methnode); } /* check source config == dest config */ if ( res == NO_ERR && srccfg && srccfg == destcfg) { /* invalid operation */ res = ERR_NCX_OPERATION_NOT_SUPPORTED; agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_CFG, (const void *)destcfg, NCX_NT_VAL, errval ); } /* get the config state; check if database already locked */ if ( NO_ERR == res && destcfg ) { res = cfg_ok_to_write(destcfg, SES_MY_SID(scb)); /* make sure no transaction in progress (should not happen) */ if (res == NO_ERR && agt_cfg_txid_in_progress(destcfg->cfg_id)) { res = ERR_NCX_NO_ACCESS_STATE; } if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_CFG, (const void *)destcfg, NCX_NT_NONE, NULL ); } } if ( NO_ERR == res && srcval && destcfg && destcfg->cfg_id != NCX_CFGID_STARTUP) { /* allocate a transaction control block */ msg->rpc_txcb = agt_cfg_new_transaction(destcfg->cfg_id, AGT_CFG_EDIT_TYPE_FULL, FALSE, FALSE, &res); if (msg->rpc_txcb == NULL || res != NO_ERR) { if (res == NO_ERR) { res = ERR_NCX_OPERATION_FAILED; } agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL ); } if (res == NO_ERR) { /* validate the element (wrt/ embedded operation * attributes) against the existing data model. * records will be added as needed */ msg->rpc_top_editop = OP_EDITOP_REPLACE; res = agt_val_validate_write( scb, msg, destcfg, srcval, OP_EDITOP_REPLACE ); /* since the running config is not supported as a target, * there is no need to run an agt_val_root_check on * the result of the full-root replace operation */ } } /* setup the edit parms to save for the invoke phase */ if ( NO_ERR == res ) { copyparms = new_copyparms(); if ( copyparms ) { copyparms->srccfg = srccfg; copyparms->destcfg = destcfg; copyparms->srcval = srcval; copyparms->srcurlval = srcurlval; copyparms->srcfile = srcfile; copyparms->destfile = destfile; copyparms->desturlspec = desturlspec; msg->rpc_user1 = copyparms; } else { res = ERR_INTERNAL_MEM; agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL ); } } if ( NO_ERR != res ) { if (srcurlval ) { val_free_value(srcurlval); } if (desturlspec ) { m__free(desturlspec); } if (srcfile ) { m__free(srcfile); } if (destfile ) { m__free(destfile); } } if ( srcurlspec ) { m__free( srcurlspec ); } return res; } /* copy_config_validate */ /******************************************************************** * FUNCTION copy_config_invoke * * copy-config : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t copy_config_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { copy_parms_t *copyparms = (copy_parms_t *)msg->rpc_user1; val_value_t *sourceval = NULL; status_t res = NO_ERR; if (copyparms->srcval != NULL || copyparms->srcurlval != NULL) { /* set the sourceval; only 1 of these 2 parms * should be set by the validate function */ if (copyparms->srcval != NULL) { sourceval = copyparms->srcval; } else { sourceval = copyparms->srcurlval; } /* copy from inline data or URL data to a database or URL */ if (copyparms->destcfg != NULL) { switch (copyparms->destcfg->cfg_id) { case NCX_CFGID_STARTUP: /* figure out which URL to use for startup */ res = NO_ERR; copyparms->destfile = agt_get_startup_filespec(&res); if (copyparms->destfile != NULL && res == NO_ERR) { res = cfg_save_inline(copyparms->destfile, sourceval, TRUE); } break; case NCX_CFGID_CANDIDATE: /* do not just blindly apply the replace write * instead, use the same API as for edit-config * except the edit_type is set differently */ /* res = cfg_fill_candidate_from_inline(sourceval); */ msg->rpc_top_editop = OP_EDITOP_REPLACE; res = agt_val_apply_write(scb, msg, copyparms->destcfg, sourceval, OP_EDITOP_REPLACE); break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } else if (copyparms->desturlspec != NULL) { /* copy from inline data to an URL */ if (copyparms->destfile == NULL) { /* creating a new file; get the destfile to use */ copyparms->destfile = agt_get_target_filespec(copyparms->desturlspec, &res); } if (res == NO_ERR) { res = cfg_save_inline(copyparms->destfile, sourceval, FALSE); } } else { res = SET_ERROR(ERR_INTERNAL_VAL); } } else if (copyparms->srccfg != NULL) { if (copyparms->destcfg != NULL) { /* copy from one config to config or URL */ switch (copyparms->destcfg->cfg_id) { case NCX_CFGID_STARTUP: res = agt_ncx_cfg_save(copyparms->srccfg, FALSE); break; case NCX_CFGID_CANDIDATE: switch (copyparms->srccfg->cfg_id) { case NCX_CFGID_RUNNING: /* same as discard-changes */ res = cfg_fill_candidate_from_running(); break; case NCX_CFGID_STARTUP: res = cfg_fill_candidate_from_startup(); break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case NCX_CFGID_RUNNING: res = ERR_NCX_OPERATION_NOT_SUPPORTED; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } else if (copyparms->desturlspec != NULL) { /* copy from source config to an URL */ if (copyparms->destfile == NULL) { /* creating a new file; get the destfile to use */ copyparms->destfile = agt_get_target_filespec(copyparms->desturlspec, &res); } if (res == NO_ERR) { res = cfg_save_inline(copyparms->destfile, copyparms->srccfg->root, FALSE); } } else { res = SET_ERROR(ERR_INTERNAL_VAL); } } else if (copyparms->srcfile != NULL) { /* this is an URL to URL copy; * not supported at this time */ res = SET_ERROR(ERR_INTERNAL_VAL); } else { /* no source parameter is set */ res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { /* config operation failed */ agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, (copyparms->destcfg) ? NCX_NT_CFG : NCX_NT_STRING, (copyparms->destcfg) ? (void *)copyparms->destcfg : (void *)copyparms->desturlspec, NCX_NT_NONE, NULL); } free_copyparms(copyparms); return res; } /* copy_config_invoke */ /******************************************************************** * FUNCTION delete_config_validate * * delete-config : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t delete_config_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { const agt_profile_t *prof; cfg_template_t *target = NULL; status_t res; void *errval = NULL; const xmlChar *desturl = NULL; xmlChar *desturlspec = NULL, *destfile = NULL; char *errstr = NULL; val_value_t *urlval = NULL; ncx_node_t errtyp; /* get the config to delete */ res = agt_get_cfg_from_parm(NCX_EL_TARGET, msg, methnode, &target); if (res == ERR_NCX_FOUND_URL) { res = agt_get_url_from_parm(NCX_EL_TARGET, msg, methnode, &desturl, &urlval); if (res != NO_ERR) { /* errors already recorded */ return res; } } else if (res == ERR_NCX_FOUND_INLINE) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } else if (res != NO_ERR) { return res; /* error already recorded */ } /* get the agent profile */ prof = agt_get_profile(); if (!prof) { res = SET_ERROR(ERR_INTERNAL_PTR); } /* check the cfg value provided -- only and * databases are supported, plus files */ if (res == NO_ERR) { /* check if the startup config is allowed to be deleted * and that is the config to be deleted */ if (desturl != NULL) { if (!prof->agt_useurl) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } else { desturlspec = agt_get_filespec_from_url(desturl, &res); /* check the URL parameter to see if it is valid */ if (desturlspec != NULL && res == NO_ERR) { destfile = ncxmod_find_data_file(desturlspec, FALSE, &res); } } } else if (target->cfg_id == NCX_CFGID_STARTUP) { if (!prof->agt_has_startup) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } } else if (target->cfg_id == NCX_CFGID_CANDIDATE) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } else { res = ERR_NCX_INVALID_VALUE; } } /* check if okay to delete this config now */ if (res == NO_ERR && desturl == NULL) { res = cfg_ok_to_write(target, SES_MY_SID(scb)); } if (res != NO_ERR) { errval = urlval ? urlval : val_find_child(msg->rpc_input, val_get_mod_name(msg->rpc_input), NCX_EL_TARGET); if (errval) { errtyp = NCX_NT_VAL; } else { errtyp = NCX_NT_NONE; } errstr = strdup("/nc:rpc/nc:delete-config/nc:target"); agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, (errstr) ? NCX_NT_STRING : NCX_NT_NONE, errstr, errtyp, errval); if (errstr != NULL) { m__free(errstr); } if (destfile != NULL) { m__free(destfile); } } else { msg->rpc_user1 = target; msg->rpc_user2 = destfile; } if (desturlspec != NULL) { m__free(desturlspec); } return res; } /* delete_config_validate */ /******************************************************************** * FUNCTION delete_config_invoke * * delete-config : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t delete_config_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { cfg_template_t *target = (cfg_template_t *)msg->rpc_user1; xmlChar *destfile = (xmlChar *)msg->rpc_user2; status_t res = NO_ERR; if ( !destfile ) { const agt_profile_t *profile; const xmlChar *startspec; /* else this must be a request to delete the startup */ profile = agt_get_profile(); /* use the user-set startup or default filename */ startspec = ( profile->agt_startup ? profile->agt_startup : NCX_DEF_STARTUP_FILE ); destfile = ncxmod_find_data_file( startspec, FALSE, &res ); if ( !destfile ) { log_error("\nError: cannot find config file '%s' to delete", startspec); return res; } } if ( 0 != remove( (const char *)destfile) ) { char *errstr; res = errno_to_status(); errstr = (char *) xml_strdup( (const xmlChar *) "/nc:rpc/nc:delete-config/nc:target" ); if (errstr) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, destfile, NCX_NT_STRING, errstr ); m__free(errstr); } else { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, destfile, NCX_NT_NONE, NULL ); } } else if ( !msg->rpc_user2 ) { if ( target != NULL && target->root != NULL) { val_free_value( target->root ); target->root = NULL; } } if (destfile != NULL) { m__free(destfile); } return res; } /* delete_config_invoke */ /******************************************************************** * FUNCTION lock_validate * * lock : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t lock_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; cfg_template_t *cfg; /* get the config to lock */ res = agt_get_cfg_from_parm(NCX_EL_TARGET, msg, methnode, &cfg); if (res != NO_ERR) { return res; } /* get the config state; check if lock can be granted * based on the current config state */ res = cfg_ok_to_lock(cfg); /* cannot start a lock when confirmed commit pending */ if (res == NO_ERR) { if ((cfg->cfg_id == NCX_CFGID_RUNNING || cfg->cfg_id == NCX_CFGID_CANDIDATE) && commit_cb.cc_active) { res = ERR_NCX_IN_USE_COMMIT; } } if (res != NO_ERR) { /* lock probably already held */ agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_CFG, cfg, NCX_NT_NONE, NULL); } else { /* lock can be granted * setup the user1 scratchpad with the cfg to lock */ msg->rpc_user1 = (void *)cfg; } return res; } /* lock_validate */ /******************************************************************** * FUNCTION lock_invoke * * lock : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t lock_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { cfg_template_t *cfg; status_t res; cfg = (cfg_template_t *)msg->rpc_user1; res = cfg_lock(cfg, SES_MY_SID(scb), CFG_SRC_NETCONF); if (res != NO_ERR) { /* config is in a state where locks cannot be granted */ agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } return res; } /* lock_invoke */ /******************************************************************** * FUNCTION unlock_validate * * unlock : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t unlock_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; cfg_template_t *cfg; /* get the config to lock */ res = agt_get_cfg_from_parm(NCX_EL_TARGET, msg, methnode, &cfg); if (res != NO_ERR) { return res; } /* get the config state; check if lock is already granted * based on the current config state */ res = cfg_ok_to_unlock(cfg, SES_MY_SID(scb)); if (res == NO_ERR) { msg->rpc_user1 = (void *)cfg; } else { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } return res; } /* unlock_validate */ /******************************************************************** * FUNCTION unlock_invoke * * unlock : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t unlock_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { cfg_template_t *cfg; status_t res; cfg = (cfg_template_t *)msg->rpc_user1; res = cfg_unlock(cfg, SES_MY_SID(scb)); if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } return res; } /* unlock_invoke */ /******************************************************************** * FUNCTION close_session_invoke * * close-session : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t close_session_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { (void)msg; (void)methnode; agt_ses_request_close(scb, SES_MY_SID(scb), SES_TR_CLOSED); return NO_ERR; } /* close_session_invoke */ /******************************************************************** * FUNCTION kill_session_validate * * kill-session : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t kill_session_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; val_value_t *val; res = NO_ERR; /* get the session-id parameter */ val = val_find_child(msg->rpc_input, NC_MODULE, NCX_EL_SESSION_ID); if (!val || val->res != NO_ERR) { /* error already recorded in parse phase */ if (val) { return val->res; } else { return ERR_NCX_OPERATION_FAILED; } } /* make sure the session-id is valid * The RFC forces a kill-session of the current * session to be an error, even though agt_ses.c * supports this corner-case */ if (VAL_UINT(val) == scb->sid || !agt_ses_session_id_valid(VAL_UINT(val))) { res = ERR_NCX_INVALID_VALUE; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } return res; } /* kill_session_validate */ /******************************************************************** * FUNCTION kill_session_invoke * * kill-session : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t kill_session_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { val_value_t *val; status_t res; /* get the session-id parameter */ val = val_find_child(msg->rpc_input, NC_MODULE, NCX_EL_SESSION_ID); if (!val || val->res != NO_ERR) { /* error already recorded in parse phase */ if (val) { res = val->res; } else { res = ERR_NCX_OPERATION_FAILED; } agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } else { ses_id_t sid = (ses_id_t)VAL_UINT(val); agt_ses_kill_session( agt_ses_get_session_for_id( sid ), scb->sid, SES_TR_KILLED ); } return NO_ERR; } /* kill_session_invoke */ /******************************************************************** * FUNCTION validate_validate * * validate : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t validate_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { cfg_template_t *target = NULL; val_value_t *val = NULL, *child = NULL, *rootval = NULL, *urlval = NULL; const xmlChar *errstr = NULL, *urlstr = NULL; const agt_profile_t *profile = agt_get_profile(); xmlChar *urlspec = NULL, *urlfilename = NULL; status_t res = NO_ERR; boolean errdone = FALSE, delneeded = FALSE, needfullcheck = FALSE; if (!profile->agt_usevalidate) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } if (res == NO_ERR) { /* attempt to get the source parameter */ val = val_find_child(msg->rpc_input, NC_MODULE, NCX_EL_SOURCE); if (!val || val->res != NO_ERR) { res = val ? val->res : ERR_NCX_OPERATION_FAILED; errstr = NCX_EL_SOURCE; } } if (res == NO_ERR) { /* attempt to get the child parameter */ child = val_get_first_child(val); if (!child || child->res != NO_ERR) { res = child ? child->res : ERR_NCX_MISSING_PARM; errstr = child ? child->name : NCX_EL_SOURCE; } } /* determine which variant of the input parameter is present */ if (res == NO_ERR) { if (!xml_strcmp(child->name, NCX_EL_RUNNING)) { target = cfg_get_config_id(NCX_CFGID_RUNNING); } else if (!xml_strcmp(child->name, NCX_EL_CANDIDATE)) { if (profile->agt_targ != NCX_AGT_TARG_CANDIDATE) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; errstr = child->name; } else { target = cfg_get_config_id(NCX_CFGID_CANDIDATE); needfullcheck = TRUE; } } else if (!xml_strcmp(child->name, NCX_EL_STARTUP)) { if (!profile->agt_has_startup) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; errstr = child->name; } else { target = cfg_get_config_id(NCX_CFGID_STARTUP); } } else if (!xml_strcmp(child->name, NCX_EL_URL)) { urlstr = VAL_STR(child); errstr = child->name; /* get the filespec out of the URL */ urlspec = agt_get_filespec_from_url(urlstr, &res); if (urlspec || res == NO_ERR) { urlfilename = ncxmod_find_data_file(urlspec, FALSE, &res); if (urlfilename && res == NO_ERR) { /* get the external file loaded into a value struct * for the object node */ target = cfg_get_config_id(NCX_CFGID_RUNNING); urlval = agt_rpc_get_config_file(urlfilename, target, SES_MY_SID(scb), RPC_ERR_QUEUE(msg), &res); if (res == NO_ERR) { rootval = urlval; delneeded = TRUE; } } } } else if (!xml_strcmp(child->name, NCX_EL_CONFIG)) { rootval = child; delneeded = TRUE; } if (res == NO_ERR && !rootval) { if (!target || !target->root) { res = ERR_NCX_OPERATION_FAILED; errstr = child->name; } else { rootval = target->root; } } } if (res == NO_ERR) { /* set the error parameter to gather the most errors */ msg->rpc_err_option = OP_ERROP_CONTINUE; /* allocate a transaction control block; the target cfg_id will * be ignored if this is an inline validate */ msg->rpc_txcb = agt_cfg_new_transaction(target ? target->cfg_id : NCX_CFGID_RUNNING, AGT_CFG_EDIT_TYPE_FULL, FALSE, TRUE, &res); if (msg->rpc_txcb == NULL || res != NO_ERR) { if (res == NO_ERR) { res = ERR_NCX_OPERATION_FAILED; } } else { /* the validate command is just a root check * there are no individual edits to check, so the * SIL validate callbacks do not need to be called */ if (delneeded) { res = agt_val_delete_dead_nodes(scb, msg, rootval); // errors recorded if res != NO_ERR } if (res == NO_ERR && needfullcheck) { /* set target to NULL to suppress checks on any locks */ op_editop_t useop = (profile->agt_validate_all) ? OP_EDITOP_LOAD : OP_EDITOP_COMMIT; res = agt_val_validate_write(scb, msg, NULL, rootval, useop); } if (res == NO_ERR) { res = agt_val_root_check(scb, &msg->mhdr, msg->rpc_txcb, rootval); } errdone = TRUE; // rpc-error already recorded if res != NO_ERR } } if (res != NO_ERR && !errdone) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, (errstr) ? NCX_NT_STRING : NCX_NT_NONE, (errstr) ? errstr : NULL, NCX_NT_NONE, NULL); } if (urlval != NULL) { val_free_value(urlval); } if (urlspec != NULL) { m__free(urlspec); } if (urlfilename != NULL) { m__free(urlfilename); } return res; } /* validate_validate */ /******************************************************************** * FUNCTION commit_validate * * commit : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t commit_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { const agt_profile_t *profile = agt_get_profile(); val_value_t *errval = NULL; status_t res = NO_ERR; boolean errdone = FALSE; if (profile->agt_targ != NCX_AGT_TARG_CANDIDATE) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } else { /* get the candidate config */ cfg_template_t *candidate = cfg_get_config_id(NCX_CFGID_CANDIDATE); cfg_template_t *running = cfg_get_config_id(NCX_CFGID_RUNNING); assert ( candidate && "candidate is NULL!" ); assert ( running && "candidate is NULL!" ); val_value_t *persistval, *persistidval; /* make sure base:1.1 params allowed if present */ persistval = val_find_child(msg->rpc_input, val_get_mod_name(msg->rpc_input), NCX_EL_PERSIST); persistidval = val_find_child(msg->rpc_input, val_get_mod_name(msg->rpc_input), NCX_EL_PERSIST_ID); if ((persistval != NULL || persistidval != NULL) && ses_get_protocol(scb) == NCX_PROTO_NETCONF10) { res = ERR_NCX_PROTO11_NOT_ENABLED; if (persistval != NULL) { errval = persistval; } else { errval = persistidval; } } if (res == NO_ERR && persistidval != NULL) { if (!commit_cb.cc_active) { res = ERR_NCX_CC_NOT_ACTIVE; errval = persistidval; } else if (commit_cb.cc_persist_id == NULL || xml_strcmp(VAL_STR(persistidval), commit_cb.cc_persist_id)) { res = ERR_NCX_INVALID_VALUE; errval = persistidval; } } if (res == NO_ERR) { /* check if the candidate config can be written */ res = cfg_ok_to_write(candidate, SES_MY_SID(scb)); } if (res == NO_ERR) { /* check if the running config can be written */ res = cfg_ok_to_write(running, SES_MY_SID(scb)); } if (res == NO_ERR) { /* allocate a transaction control block */ msg->rpc_txcb = agt_cfg_new_transaction(NCX_CFGID_RUNNING, AGT_CFG_EDIT_TYPE_FULL, FALSE, FALSE, &res); if (msg->rpc_txcb == NULL || res != NO_ERR) { if (res == NO_ERR) { res = ERR_NCX_OPERATION_FAILED; } agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL ); errdone = TRUE; } else { /* check if this session allowed to perform all the * edits in the commit request */ msg->rpc_txcb->commitcheck = TRUE; res = agt_val_check_commit_edits(scb, msg, candidate, running); if (res != NO_ERR) { errdone = TRUE; } /* do not need to delete dead nodes in candidate because * it is already done at the end of every edit */ if (res == NO_ERR ) { res = agt_val_root_check(scb, &msg->mhdr, msg->rpc_txcb, candidate->root); if (res != NO_ERR) { errdone = TRUE; } } msg->rpc_txcb->commitcheck = FALSE; } } if(res==NO_ERR) { res = agt_commit_validate(scb, &msg->mhdr, candidate->root ); if (res != NO_ERR) { errdone = TRUE; } } } if (res != NO_ERR && !errdone) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, (errval != NULL) ? NCX_NT_VAL : NCX_NT_NONE, errval); } return res; } /* commit_validate */ /******************************************************************** * FUNCTION write_config * * Write the specified cfg->root to the the default backup source * * INPUTS: * filespec == complete path for the output file * cfg == config template to write to XML file * * RETURNS: * status *********************************************************************/ static status_t write_config (const xmlChar *filespec, cfg_template_t *cfg) { agt_profile_t *profile; status_t res; xml_attrs_t attrs; if (cfg->root == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } profile = agt_get_profile(); /* write the new startup config */ xml_init_attrs(&attrs); /* output to the specified file or STDOUT */ res = xml_wr_check_file(filespec, cfg->root, &attrs, XMLMODE, WITHHDR, TRUE, 0, profile->agt_indent, agt_check_save); xml_clean_attrs(&attrs); return res; } /* write_config */ /******************************************************************** * FUNCTION clear_commit_cb * * Clear the commit_cb data structure * *********************************************************************/ static void clear_commit_cb (void) { if (commit_cb.cc_persist_id != NULL) { m__free(commit_cb.cc_persist_id); } if (commit_cb.cc_backup_source != NULL) { m__free(commit_cb.cc_backup_source); } memset(&commit_cb, 0x0, sizeof(commit_cb_t)); } /* clear_commit_cb */ /******************************************************************** * FUNCTION commit_invoke * * commit : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t commit_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { val_value_t *confirmedval, *timeoutval; val_value_t *persistval, *persistidval, *errval; cfg_template_t *candidate, *running; xmlChar *fname; status_t res; boolean save_nvstore, errdone, timeout_extended; res = NO_ERR; errdone = FALSE; timeout_extended = FALSE; errval = NULL; candidate = cfg_get_config_id(NCX_CFGID_CANDIDATE); running = cfg_get_config_id(NCX_CFGID_RUNNING); if (candidate == NULL || running == NULL) { res = SET_ERROR(ERR_INTERNAL_VAL); agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } save_nvstore = TRUE; /* get the confirmed parameter */ confirmedval = val_find_child(msg->rpc_input, val_get_mod_name(msg->rpc_input), NCX_EL_CONFIRMED); /* get the confirm-timeout parameter */ timeoutval = val_find_child(msg->rpc_input, val_get_mod_name(msg->rpc_input), NCX_EL_CONFIRM_TIMEOUT); /* get the persist parameters only if base:1.1 enabled */ if (ses_get_protocol(scb) == NCX_PROTO_NETCONF11) { persistval = val_find_child(msg->rpc_input, val_get_mod_name(msg->rpc_input), NCX_EL_PERSIST); persistidval = val_find_child(msg->rpc_input, val_get_mod_name(msg->rpc_input), NCX_EL_PERSIST_ID); } else { persistval = NULL; persistidval = NULL; } /* figure out what to do wrt/ confirmed-commit */ if (commit_cb.cc_active) { /* confirmed-commit already active * see if this commit is finishing the * confirmed commit or extending the timer * and perhaps adding more data to running */ if (confirmedval != NULL) { if (persistidval != NULL && commit_cb.cc_persist_id != NULL && !xml_strcmp(VAL_STR(persistidval), commit_cb.cc_persist_id)) { /* this session is allowed to be different than * one that started the conf-commit */ ; } else if (commit_cb.cc_persist_id == NULL) { /* check same session that started cc */ if (commit_cb.cc_ses_id != SES_MY_SID(scb)) { res = ERR_NCX_IN_USE_COMMIT; errval = confirmedval; } } else { res = ERR_NCX_OPERATION_FAILED; errval = confirmedval; } /* set the persist-id if needed */ if (res == NO_ERR && persistval != NULL) { if (commit_cb.cc_persist_id != NULL) { if (LOGDEBUG) { log_debug("\nagt_ncx: confirmed-commit by '%u' " "changing persist from '%s' to '%s'", SES_MY_SID(scb), commit_cb.cc_persist_id, VAL_STR(persistval)); } m__free(commit_cb.cc_persist_id); } else { if (LOGDEBUG) { log_debug("\nagt_ncx: confirmed-commit by '%u' " "setting persist to '%s'", SES_MY_SID(scb), VAL_STR(persistval)); } } commit_cb.cc_persist_id = xml_strdup(VAL_STR(persistval)); if (commit_cb.cc_persist_id == NULL) { res = ERR_INTERNAL_MEM; errval = persistval; } } /* extend the conf-commit timer and send a notification */ if (res == NO_ERR) { /* perhaps set a new owner session */ commit_cb.cc_ses_id = SES_MY_SID(scb); /* extend the timer */ (void)uptime(&commit_cb.cc_start_time); if (timeoutval != NULL) { commit_cb.cc_cancel_timeout = VAL_UINT(timeoutval); } else { commit_cb.cc_cancel_timeout = NCX_DEF_CONFIRM_TIMEOUT; } if (LOGDEBUG2) { log_debug2("\nConfirmed commit timer extended " "by %u seconds", commit_cb.cc_cancel_timeout); } save_nvstore = FALSE; timeout_extended = TRUE; agt_sys_send_netconf_confirmed_commit(scb, NCX_CC_EVENT_EXTEND); } } else { /* confirmedval == NULL; finishing conf-commit */ if (persistidval == NULL && commit_cb.cc_ses_id != SES_MY_SID(scb)) { /* persist-id not present and session ID did not match */ res = ERR_NCX_IN_USE_COMMIT; errval = persistidval; } else if (LOGDEBUG2) { log_debug2("\nConfirmed commit completed by session %u", SES_MY_SID(scb)); } /* finish the confirmed-commit unless invalid persist-id */ if (res == NO_ERR) { res = agt_ncx_cfg_save(running, FALSE); } if (res == NO_ERR) { agt_sys_send_netconf_confirmed_commit(scb, NCX_CC_EVENT_COMPLETE); clear_commit_cb(); } } } else { /* check if a new confirmed commit is starting */ if (confirmedval != NULL) { /* save the session ID that started this conf-commit * if persist active and orig session terminated, * this will be reset to 0; otherwise keep * the starting session the same */ if (commit_cb.cc_ses_id == 0) { commit_cb.cc_ses_id = SES_MY_SID(scb); } if (persistval != NULL) { if (commit_cb.cc_persist_id != NULL) { SET_ERROR(ERR_INTERNAL_VAL); if (LOGDEBUG) { log_debug("\nagt_ncx: confirmed-commit by '%u' " "changing persist from '%s' to '%s'", SES_MY_SID(scb), commit_cb.cc_persist_id, VAL_STR(persistval)); } m__free(commit_cb.cc_persist_id); } else { if (LOGDEBUG) { log_debug("\nagt_ncx: confirmed-commit by '%u' " "setting persist to '%s'", SES_MY_SID(scb), VAL_STR(persistval)); } } commit_cb.cc_persist_id = xml_strdup(VAL_STR(persistval)); if (commit_cb.cc_persist_id == NULL) { res = ERR_INTERNAL_MEM; } } if (res == NO_ERR) { /* set the timer */ (void)uptime(&commit_cb.cc_start_time); if (timeoutval) { commit_cb.cc_cancel_timeout = VAL_UINT(timeoutval); } else { commit_cb.cc_cancel_timeout = NCX_DEF_CONFIRM_TIMEOUT; } commit_cb.cc_active = TRUE; save_nvstore = FALSE; if (LOGDEBUG2) { log_debug2("\nConfirmed commit started, timeout in " "%u seconds", commit_cb.cc_cancel_timeout); } agt_sys_send_netconf_confirmed_commit(scb, NCX_CC_EVENT_START); } } else { /* no confirmed commit is starting */ save_nvstore = TRUE; } } /* make a backup of running to make sure * that if this step fails, running config MUST not change */ if (res == NO_ERR && commit_cb.cc_backup_source == NULL) { /* search for the default startup-cfg.xml filename */ fname = ncxmod_find_data_file(NCX_DEF_BACKUP_FILE, FALSE, &res); if (fname) { /* rewrite the existing backup file * hand off fname malloced memory here */ commit_cb.cc_backup_source = fname; } else if (running->src_url) { /* use the same path as the startup config file * if it has already been set; it may be different * each boot via the --startup CLI parameter */ res = NO_ERR; commit_cb.cc_backup_source = ncxmod_make_data_filespec_from_src(running->src_url, NCX_DEF_BACKUP_FILE, &res); } else { /* create a new backup file name * should really check the res code * to make sure the error is some sort * of not-found error, but if it was a fatal * malloc error, etc. then the following * attempt to create a backup will probably * fail as well */ res = NO_ERR; commit_cb.cc_backup_source = ncxmod_make_data_filespec(NCX_DEF_BACKUP_FILE, &res); } } /* attempt to save the backup, if a file name is available */ if (res == NO_ERR && timeout_extended == FALSE) { res = write_config(commit_cb.cc_backup_source, running); } if (res == NO_ERR) { res = agt_val_apply_commit(scb, msg, candidate, running, save_nvstore); if (res != NO_ERR) { errdone = TRUE; if (msg->rpc_txcb->rollback_res != NO_ERR) { /* close out the current transaction now so another * one can start */ agt_cfg_free_transaction(msg->rpc_txcb); msg->rpc_txcb = NULL; /* restore the config because rollback failed */ status_t res2 = agt_ncx_load_backup(commit_cb.cc_backup_source, running, commit_cb.cc_ses_id); if (res2 != NO_ERR) { res = res2; errdone = FALSE; } } } else { res = cfg_fill_candidate_from_running(); } } if (res != NO_ERR && !errdone) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, (errval != NULL) ? NCX_NT_VAL : NCX_NT_NONE, errval); } return res; } /* commit_invoke */ /******************************************************************** * FUNCTION cancel_commit_validate * * cancel-commit : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t cancel_commit_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { cfg_template_t *running; const agt_profile_t *profile; val_value_t *persistidval; const xmlChar *cc_persistid; status_t res; running = NULL; profile = agt_get_profile(); persistidval = NULL; cc_persistid = NULL; res = NO_ERR; if (ses_get_protocol(scb) != NCX_PROTO_NETCONF11) { res = ERR_NCX_UNKNOWN_ELEMENT; } else if (profile->agt_targ != NCX_AGT_TARG_CANDIDATE) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } else if (!agt_ncx_cc_active()) { res = ERR_NCX_OPERATION_FAILED; } else { /* get the running config */ running = cfg_get_config_id(NCX_CFGID_RUNNING); if (running == NULL) { res = SET_ERROR(ERR_INTERNAL_VAL); } else { /* check if this session is allowed to revert running now */ res = cfg_ok_to_write(running, SES_MY_SID(scb)); } } if (res == NO_ERR) { cc_persistid = agt_ncx_cc_persist_id(); persistidval = val_find_child(msg->rpc_input, val_get_mod_name(msg->rpc_input), NCX_EL_PERSIST_ID); if (persistidval == NULL && cc_persistid == NULL) { ; /* no persist in progress or requested - this is OK */ } else if (persistidval == NULL && cc_persistid != NULL) { /* the persist-id is mandatory now */ res = ERR_NCX_MISSING_PARM; } else if (persistidval != NULL && cc_persistid == NULL) { /* no persist cc in progress so cannot match ID */ res = ERR_NCX_CC_NOT_ACTIVE; } else { /* try to match the persist ID */ if (xml_strcmp(VAL_STR(persistidval), cc_persistid)) { res = ERR_NCX_INVALID_VALUE; } } } if (res == NO_ERR && cc_persistid == NULL) { /* the cc session is the only one that can cancel */ if (SES_MY_SID(scb) != agt_ncx_cc_ses_id()) { res = ERR_NCX_OPERATION_FAILED; } } if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } return res; } /* cancel_commit_validate */ /******************************************************************** * FUNCTION cancel_commit_invoke * * cancel-commit : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t cancel_commit_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { (void)msg; (void)methnode; agt_ncx_cancel_confirmed_commit(scb, NCX_CC_EVENT_CANCEL); return NO_ERR; } /* cancel_commit_invoke */ /******************************************************************** * FUNCTION discard_changes_validate * * discard-changes : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t discard_changes_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { cfg_template_t *candidate; const agt_profile_t *profile; status_t res; res = NO_ERR; profile = agt_get_profile(); if (profile->agt_targ != NCX_AGT_TARG_CANDIDATE) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } else { /* get the candidate config */ candidate = cfg_get_config_id(NCX_CFGID_CANDIDATE); if (!candidate) { res = SET_ERROR(ERR_INTERNAL_VAL); } else { /* check if this session is allowed to invoke now */ res = cfg_ok_to_write(candidate, SES_MY_SID(scb)); } } if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } return res; } /* discard_changes_validate */ /******************************************************************** * FUNCTION discard_changes_invoke * * discard-changes : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t discard_changes_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { cfg_template_t *candidate; status_t res; res = NO_ERR; /* get the candidate config */ candidate = cfg_get_config_id(NCX_CFGID_CANDIDATE); if (!candidate) { res = SET_ERROR(ERR_INTERNAL_VAL); } else if (cfg_get_dirty_flag(candidate)) { res = cfg_fill_candidate_from_running(); } if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } return res; } /* discard_changes_invoke */ /******************************************************************** * FUNCTION load_config_validate * * load-config : validate params callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t load_config_validate (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res = NO_ERR; /* This special callback is used by internal NCX functions * to load the initial configuration. The msg->rpc_user1 parameter * has already been set to the address of the cfg_template_t * to fill in. * * NOTE: HACK DEPENDS ON THE agt_rpc_load_config_file to setup * the rpc->rpc_user1 parameter * * make sure this is a DUMMY session, not a real session */ if (scb->type != SES_TYP_DUMMY) { res = ERR_NCX_ACCESS_DENIED; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } cfg_template_t *target = (cfg_template_t *)msg->rpc_user1; if (!target) { return SET_ERROR(ERR_INTERNAL_PTR); } /* get the parameter */ val_value_t *val = val_find_child(msg->rpc_input, NC_MODULE, NCX_EL_CONFIG); if (!val) { /* we shouldn't get here if the config param is missing */ return SET_ERROR(ERR_NCX_OPERATION_FAILED); } /* check if any error values need to be purged from the load config */ if (msg->rpc_parse_errors) { val_purge_errors_from_root(val); } /* create a transaction CB */ msg->rpc_txcb = agt_cfg_new_transaction(NCX_CFGID_RUNNING, AGT_CFG_EDIT_TYPE_FULL, FALSE, FALSE, &res); if (msg->rpc_txcb == NULL || res != NO_ERR) { if (res == NO_ERR) { res = ERR_NCX_OPERATION_FAILED; } } else { /* errors will be added as needed */ res = agt_val_validate_write(scb, msg, target, val, msg->rpc_top_editop); /* check what to do if errors occurred */ agt_profile_t *profile = agt_get_profile(); if (res != NO_ERR) { profile->agt_load_validate_errors = TRUE; if (profile->agt_startup_error) { /* any startup errors mean server shutdown */ return res; } // delay this until end of load_running_config // error nodes are marked as deleted //val_purge_errors_from_root(val); } res = agt_val_delete_dead_nodes(scb, msg, val); if (res == NO_ERR) { res = agt_val_root_check(scb, &msg->mhdr, msg->rpc_txcb, val); } if (res != NO_ERR) { if (profile->agt_load_rootcheck_errors && profile->agt_startup_error) { ; } else if (profile->agt_load_top_rootcheck_errors && profile->agt_running_error) { ; } else { // delay this until end of load_running_config // error nodes are marked as deleted //val_purge_errors_from_root(val); res = NO_ERR; } } msg->rpc_user2 = val; } return res; } /* load_config_validate */ /******************************************************************** * FUNCTION load_config_invoke * * load-config : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t load_config_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { (void)methnode; status_t res = NO_ERR; /* This special callback is used by internal NCX functions * to load the initial configuration. */ cfg_template_t *target = (cfg_template_t *)msg->rpc_user1; val_value_t *val = (val_value_t *)msg->rpc_user2; if (target && val) { /* load the into the target config */ res = agt_val_apply_write(scb, msg, target, val, msg->rpc_top_editop); if (res != NO_ERR) { agt_profile_t *profile = agt_get_profile(); profile->agt_load_apply_errors = TRUE; } } else { res = SET_ERROR(ERR_INTERNAL_PTR); } if (target) { val_clean_tree(target->root); } return res; } /* load_config_invoke */ /******************************************************************** * FUNCTION load_invoke * * load module : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t load_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { val_value_t *modval, *revval, *devval, *newval; ncx_module_t *mod, *testmod; xmlChar *moduri; agt_profile_t *agt_profile; status_t res; boolean module_added, errdone, sil_loaded; res = NO_ERR; newval = NULL; mod = NULL; errdone = FALSE; sil_loaded = FALSE; module_added = FALSE; agt_profile = agt_get_profile(); /* mandatory module name */ modval = val_find_child(msg->rpc_input, AGT_SYS_MODULE, NCX_EL_MODULE); if (!modval || modval->res != NO_ERR) { /* error already recorded */ return ERR_NCX_OPERATION_FAILED; } /* optional revision data string */ revval = val_find_child(msg->rpc_input, AGT_SYS_MODULE, NCX_EL_REVISION); if (revval && revval->res != NO_ERR) { /* error already recorded */ return ERR_NCX_OPERATION_FAILED; } /* check for any version of this module already loaded */ mod = ncx_find_module(VAL_STR(modval), NULL); if (mod == NULL) { /* module not loaded already * load all the deviations first */ for (devval = val_find_child(msg->rpc_input, AGT_SYS_MODULE, NCX_EL_DEVIATION); devval != NULL && res == NO_ERR; devval = val_find_next_child(msg->rpc_input, AGT_SYS_MODULE, NCX_EL_DEVIATION, devval)) { res = ncxmod_load_deviation(VAL_STR(devval), &agt_profile->agt_savedevQ); if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, devval); errdone = TRUE; } } if (res == NO_ERR) { #ifdef STATIC_SERVER res = ncxmod_load_module(VAL_STR(modval), (revval) ? VAL_STR(revval) : NULL, &agt_profile->agt_savedevQ, &mod); #else res = agt_load_sil_code(VAL_STR(modval), (revval) ? VAL_STR(revval) : NULL, TRUE); if (res == ERR_NCX_SKIPPED) { log_warn("\nWarning: SIL code for module '%s' not found", VAL_STR(modval)); res = ncxmod_load_module(VAL_STR(modval), (revval) ? VAL_STR(revval) : NULL, &agt_profile->agt_savedevQ, &mod); } else if (res == NO_ERR) { sil_loaded = TRUE; } #endif /* reget the module; it should be found if status == NO_ERR */ if (res == NO_ERR) { mod = ncx_find_module(VAL_STR(modval), (revval) ? VAL_STR(revval) : NULL); if (mod == NULL) { res = SET_ERROR(ERR_INTERNAL_VAL); } } if (res == NO_ERR) { module_added = TRUE; } else { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, modval); errdone = TRUE; } } } else if (revval != NULL) { /* some version of the module is already loaded * try again to get the exact version requested */ testmod = ncx_find_module(VAL_STR(modval), VAL_STR(revval)); if (testmod != NULL) { mod = testmod; } else { res = ERR_NCX_WRONG_VERSION; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, revval); errdone = TRUE; } } /* generate the return value */ if (res == NO_ERR && mod != NULL) { newval = val_make_string(val_get_nsid(modval), NCX_EL_MOD_REVISION, (mod->version) ? mod->version : EMPTY_STRING); if (newval == NULL) { res = ERR_INTERNAL_MEM; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); errdone = TRUE; } } if (res == NO_ERR && mod && module_added && !sil_loaded) { /* make sure any top-level defaults are set */ res = agt_set_mod_defaults(mod); } if (res == NO_ERR && mod && module_added) { /* prune all the obsolete objects */ ncx_delete_mod_obsolete_objects(mod); /* add the module commit tests for this module */ res = agt_val_add_module_commit_tests(mod); } if (res == NO_ERR && mod && module_added) { /* add the node in netconf-state module */ res = agt_state_add_module_schema(mod); } if (res == NO_ERR && mod && module_added) { /* add the node in template */ res = agt_cap_add_module(mod); } if (res == NO_ERR && mod && module_added) { /* send the capability change notification */ moduri = cap_make_moduri(mod); if (!moduri) { res = ERR_INTERNAL_MEM; } else { agt_sys_send_netconf_capability_change(scb, TRUE, moduri); m__free(moduri); } } if (res != NO_ERR) { if (!errdone) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, modval); } if (newval != NULL) { val_free_value(newval); } } else { /* pass off newval memory here */ if (newval != NULL) { msg->rpc_data_type = RPC_DATA_YANG; dlq_enque(newval, &msg->rpc_dataQ); } } return res; } /* load_invoke */ /******************************************************************** * FUNCTION restart_invoke * * restart : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t restart_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { xmlChar timebuff[TSTAMP_MIN_SIZE]; (void)msg; (void)methnode; tstamp_datetime(timebuff); log_write("\n\n**************" "\nNotice: restart requested\n by %s " "on session %u at %s\n\n", scb->username, scb->sid, timebuff); agt_request_shutdown(NCX_SHUT_RESTART); return NO_ERR; } /* restart_invoke */ /******************************************************************** * FUNCTION shutdown_invoke * * shutdown : invoke callback * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t shutdown_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { xmlChar timebuff[TSTAMP_MIN_SIZE]; (void)msg; (void)methnode; tstamp_datetime(timebuff); log_write("\n\n*****************************" "\nNotice: shutdown requested\n by %s " "on session %u at %s\n\n", scb->username, scb->sid, timebuff); agt_request_shutdown(NCX_SHUT_EXIT); return NO_ERR; } /* shutdown_invoke */ /******************************************************************** * FUNCTION register_nc_callbacks * * Register the agent callback functions for the NETCONF RPC methods * * RETURNS: * status, NO_ERR if all registered okay *********************************************************************/ static status_t register_nc_callbacks (void) { status_t res; /* get */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_GET), AGT_RPC_PH_VALIDATE, get_validate); if (res != NO_ERR) { return SET_ERROR(res); } /* get-config */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_GET_CONFIG), AGT_RPC_PH_VALIDATE, get_config_validate); if (res != NO_ERR) { return SET_ERROR(res); } /* edit-config */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_EDIT_CONFIG), AGT_RPC_PH_VALIDATE, edit_config_validate); if (res != NO_ERR) { return SET_ERROR(res); } res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_EDIT_CONFIG), AGT_RPC_PH_INVOKE, edit_config_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* copy-config */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_COPY_CONFIG), AGT_RPC_PH_VALIDATE, copy_config_validate); if (res != NO_ERR) { return SET_ERROR(res); } res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_COPY_CONFIG), AGT_RPC_PH_INVOKE, copy_config_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* delete-config */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_DELETE_CONFIG), AGT_RPC_PH_VALIDATE, delete_config_validate); if (res != NO_ERR) { return SET_ERROR(res); } res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_DELETE_CONFIG), AGT_RPC_PH_INVOKE, delete_config_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* lock */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_LOCK), AGT_RPC_PH_VALIDATE, lock_validate); if (res != NO_ERR) { return SET_ERROR(res); } res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_LOCK), AGT_RPC_PH_INVOKE, lock_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* unlock */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_UNLOCK), AGT_RPC_PH_VALIDATE, unlock_validate); if (res != NO_ERR) { return SET_ERROR(res); } res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_UNLOCK), AGT_RPC_PH_INVOKE, unlock_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* close-session * no validate for close-session */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_CLOSE_SESSION), AGT_RPC_PH_INVOKE, close_session_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* kill-session */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_KILL_SESSION), AGT_RPC_PH_VALIDATE, kill_session_validate); if (res != NO_ERR) { return SET_ERROR(res); } res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_KILL_SESSION), AGT_RPC_PH_INVOKE, kill_session_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* validate :validate capability */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_VALIDATE), AGT_RPC_PH_VALIDATE, validate_validate); if (res != NO_ERR) { return SET_ERROR(res); } /* commit :candidate capability */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_COMMIT), AGT_RPC_PH_VALIDATE, commit_validate); if (res != NO_ERR) { return SET_ERROR(res); } res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_COMMIT), AGT_RPC_PH_INVOKE, commit_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* discard-changes :candidate capability */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_DISCARD_CHANGES), AGT_RPC_PH_VALIDATE, discard_changes_validate); if (res != NO_ERR) { return SET_ERROR(res); } res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_DISCARD_CHANGES), AGT_RPC_PH_INVOKE, discard_changes_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* cancel-commit :confirmed-commit + :base:1.1 capability */ res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_CANCEL_COMMIT), AGT_RPC_PH_VALIDATE, cancel_commit_validate); if (res != NO_ERR) { return SET_ERROR(res); } res = agt_rpc_register_method(NC_MODULE, op_method_name(OP_CANCEL_COMMIT), AGT_RPC_PH_INVOKE, cancel_commit_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* load-config extension */ res = agt_rpc_register_method(NC_MODULE, NCX_EL_LOAD_CONFIG, AGT_RPC_PH_VALIDATE, load_config_validate); if (res != NO_ERR) { return SET_ERROR(res); } res = agt_rpc_register_method(NC_MODULE, NCX_EL_LOAD_CONFIG, AGT_RPC_PH_INVOKE, load_config_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* load module extension */ res = agt_rpc_register_method(AGT_SYS_MODULE, NCX_EL_LOAD, AGT_RPC_PH_INVOKE, load_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* restart extension */ res = agt_rpc_register_method(AGT_SYS_MODULE, NCX_EL_RESTART, AGT_RPC_PH_INVOKE, restart_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* shutdown extension */ res = agt_rpc_register_method(AGT_SYS_MODULE, NCX_EL_SHUTDOWN, AGT_RPC_PH_INVOKE, shutdown_invoke); if (res != NO_ERR) { return SET_ERROR(res); } /* no-op extension */ agt_rpc_support_method(AGT_SYS_MODULE, NCX_EL_NO_OP); return NO_ERR; } /* register_nc_callbacks */ /******************************************************************** * FUNCTION unregister_nc_callbacks * * Unregister the agent callback functions for the NETCONF RPC methods * *********************************************************************/ static void unregister_nc_callbacks (void) { /* get */ agt_rpc_unregister_method(NC_MODULE, op_method_name(OP_GET)); /* get-config */ agt_rpc_unregister_method(NC_MODULE, op_method_name(OP_GET_CONFIG)); /* edit-config */ agt_rpc_unregister_method(NC_MODULE, op_method_name(OP_EDIT_CONFIG)); /* copy-config */ agt_rpc_unregister_method(NC_MODULE, op_method_name(OP_COPY_CONFIG)); /* delete-config */ agt_rpc_unregister_method(NC_MODULE, op_method_name(OP_DELETE_CONFIG)); /* lock */ agt_rpc_unregister_method(NC_MODULE, op_method_name(OP_LOCK)); /* unlock */ agt_rpc_unregister_method(NC_MODULE, op_method_name(OP_UNLOCK)); /* close-session */ agt_rpc_unregister_method(NC_MODULE, op_method_name(OP_CLOSE_SESSION)); /* kill-session */ agt_rpc_unregister_method(NC_MODULE, op_method_name(OP_KILL_SESSION)); /* validate */ agt_rpc_unregister_method(NC_MODULE, op_method_name(OP_VALIDATE)); /* commit */ agt_rpc_unregister_method(NC_MODULE, op_method_name(OP_COMMIT)); /* discard-changes */ agt_rpc_unregister_method(NC_MODULE, op_method_name(OP_DISCARD_CHANGES)); /* cancel-commit (base:1.1 only) */ agt_rpc_unregister_method(NC_MODULE, NCX_EL_CANCEL_COMMIT); /* load-config extension */ agt_rpc_unregister_method(NC_MODULE, NCX_EL_LOAD_CONFIG); /* load module extension */ agt_rpc_unregister_method(AGT_SYS_MODULE, NCX_EL_LOAD); /* restart extension */ agt_rpc_unregister_method(AGT_SYS_MODULE, NCX_EL_RESTART); /* shutdown extension */ agt_rpc_unregister_method(AGT_SYS_MODULE, NCX_EL_SHUTDOWN); /* no-op extension */ agt_rpc_unregister_method(AGT_SYS_MODULE, NCX_EL_NO_OP); } /* unregister_nc_callbacks */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION agt_ncx_init * * Initialize the NCX Agent standard method routines * * RETURNS: * status of the initialization procedure *********************************************************************/ status_t agt_ncx_init (void) { status_t res; if (!agt_ncx_init_done) { res = register_nc_callbacks(); if (res != NO_ERR) { unregister_nc_callbacks(); return res; } memset(&commit_cb, 0x0, sizeof(commit_cb_t)); agt_ncx_init_done = TRUE; } return NO_ERR; } /* agt_ncx_init */ /******************************************************************** * FUNCTION agt_ncx_cleanup * * Cleanup the NCX Agent standard method routines * * TBD -- put platform-specific agent cleanup here * *********************************************************************/ void agt_ncx_cleanup (void) { if (agt_ncx_init_done) { unregister_nc_callbacks(); clear_commit_cb(); agt_ncx_init_done = FALSE; } } /* agt_ncx_cleanup */ /******************************************************************** * FUNCTION agt_ncx_cfg_load * * Load the specifed config from the indicated source * Called just once from agt.c at boot or reload time! * * This function should only be used to load an empty config * in CFG_ST_INIT state * * INPUTS: * cfg = Config template to load data into * cfgloc == enum for the config source location * cfgparm == string parameter used in different ways * depending on the cfgloc value * For cfgloc==CFG_LOC_FILE, this is a system-dependent filespec * * OUTPUTS: * errQ contains any rpc_err_rec_t structs (if non-NULL) * * RETURNS: * overall status; may be the last of multiple error conditions *********************************************************************/ status_t agt_ncx_cfg_load (cfg_template_t *cfg, cfg_location_t cfgloc, const xmlChar *cfgparm) { cfg_template_t *startup; val_value_t *copystartup; status_t res; #ifdef DEBUG if (!cfg) { return SET_ERROR(ERR_INTERNAL_PTR); } if (cfg->cfg_state != CFG_ST_INIT) { return SET_ERROR(ERR_NCX_CFG_STATE); } #endif startup = NULL; copystartup = NULL; cfg->cfg_loc = cfgloc; if (cfgparm) { cfg->src_url = xml_strdup(cfgparm); if (!cfg->src_url) { return ERR_INTERNAL_MEM; } } res = ERR_NCX_OPERATION_NOT_SUPPORTED; switch (cfgloc) { case CFG_LOC_INTERNAL: break; case CFG_LOC_FILE: if (!cfg->src_url) { res = ERR_INTERNAL_MEM; } else { /* the cfgparm should be a filespec of an XML config file */ res = agt_rpc_load_config_file(cfgparm, cfg, TRUE, 0); if (res == NO_ERR && cfg->root != NULL && cfg->cfg_id != NCX_CFGID_STARTUP) { startup = cfg_get_config_id(NCX_CFGID_STARTUP); if (startup != NULL) { copystartup = val_clone(cfg->root); if (copystartup == NULL) { log_error("\nError: create config failed"); } else { if (startup->root != NULL) { val_free_value(startup->root); } startup->root = copystartup; copystartup = NULL; } } } } break; case CFG_LOC_NAMED: break; case CFG_LOC_LOCAL_URL: break; case CFG_LOC_REMOTE_URL: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* agt_ncx_cfg_load */ /******************************************************************** * FUNCTION agt_ncx_cfg_save * * Save the specified cfg to the its startup source, which should * be stored in the cfg struct * * INPUTS: * cfg = Config template to save from * bkup = TRUE if the current startup config should * be saved before it is overwritten * = FALSE to just overwrite the old startup cfg * RETURNS: * status *********************************************************************/ status_t agt_ncx_cfg_save (cfg_template_t *cfg, boolean bkup) { cfg_template_t *startup; val_value_t *copystartup; xmlChar *filebuffer; agt_profile_t *profile; status_t res; xml_attrs_t attrs; #ifdef DEBUG if (!cfg) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!cfg->root) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif filebuffer = NULL; startup = NULL; copystartup = NULL; res = ERR_NCX_OPERATION_NOT_SUPPORTED; profile = agt_get_profile(); switch (cfg->cfg_loc) { case CFG_LOC_INTERNAL: break; case CFG_LOC_NONE: /* candidate config */ case CFG_LOC_FILE: if (bkup) { /* FIXME: remove any existing backup */ /****/ /* FIXME: rename the current startup to the backup */ /****/ } /* save the new startup database, if there is one */ res = NO_ERR; startup = cfg_get_config_id(NCX_CFGID_STARTUP); if (startup != NULL) { copystartup = val_clone_config_data(cfg->root, &res); if (copystartup == NULL) { return res; } } if (res == NO_ERR) { filebuffer = agt_get_startup_filespec(&res); if (filebuffer != NULL && res == NO_ERR) { if (LOGDEBUG) { log_debug("\nWriting <%s> config to file '%s'", cfg->name, filebuffer); } /* write the new startup config */ xml_init_attrs(&attrs); /* output to the specified file or STDOUT */ res = xml_wr_check_file(filebuffer, cfg->root, &attrs, XMLMODE, WITHHDR, TRUE, 0, profile->agt_indent, agt_check_save); xml_clean_attrs(&attrs); if (res == NO_ERR && startup != NULL) { /* toss the old startup and save the new one */ if (startup->root) { val_free_value(startup->root); } startup->root = copystartup; copystartup = NULL; cfg_update_last_ch_time(startup); } } } break; case CFG_LOC_NAMED: break; case CFG_LOC_LOCAL_URL: break; case CFG_LOC_REMOTE_URL: break; default: return SET_ERROR(ERR_INTERNAL_VAL); } if (copystartup) { val_free_value(copystartup); } if (filebuffer) { m__free(filebuffer); } return res; } /* agt_ncx_cfg_save */ /******************************************************************** * FUNCTION agt_ncx_load_backup * * Load a backup config into the specified config template * * INPUTS: * filespec == complete path for the input file * cfg == config template to load * use_sid == session ID of user to use * * RETURNS: * status *********************************************************************/ status_t agt_ncx_load_backup (const xmlChar *filespec, cfg_template_t *cfg, ses_id_t use_sid) { status_t res; res = agt_rpc_load_config_file(filespec, cfg, FALSE, /* OP_EDITOP_REPLACE */ use_sid); return res; } /* agt_ncx_load_backup */ /******************************************************************** * FUNCTION agt_ncx_cc_active * * Check if a confirmed-commit is active, and the timeout * may need to be processed * * RETURNS: * TRUE if confirmed-commit is active * FALSE otherwise *********************************************************************/ boolean agt_ncx_cc_active (void) { return commit_cb.cc_active; } /* agt_ncx_cc_active */ /******************************************************************** * FUNCTION agt_ncx_cc_ses_id * * Get the confirmed commit session ID * * RETURNS: * session ID for the confirmed commit *********************************************************************/ ses_id_t agt_ncx_cc_ses_id (void) { return commit_cb.cc_ses_id; } /* agt_ncx_cc_ses_id */ /******************************************************************** * FUNCTION agt_ncx_clear_cc_ses_id * * Clear the confirmed commit session ID * This will be called by agt_ses when the current * session exits during a persistent confirmed-commit * *********************************************************************/ void agt_ncx_clear_cc_ses_id (void) { commit_cb.cc_ses_id = 0; } /* agt_ncx_clear_cc_ses_id */ /******************************************************************** * FUNCTION agt_ncx_cc_persist_id * * Get the confirmed commit persist ID * * RETURNS: * session ID for the confirmed commit *********************************************************************/ const xmlChar * agt_ncx_cc_persist_id (void) { return commit_cb.cc_persist_id; } /* agt_ncx_cc_persist_id */ /******************************************************************** * FUNCTION agt_ncx_check_cc_timeout * * Check if a confirmed-commit has timed out, and needs to be canceled * *********************************************************************/ void agt_ncx_check_cc_timeout (void) { time_t timenow; double timediff; if (!commit_cb.cc_active) { return; } (void)uptime(&timenow); timediff = difftime(timenow, commit_cb.cc_start_time); if (timediff >= (double)commit_cb.cc_cancel_timeout) { if (LOGDEBUG) { log_debug("\nConfirmed-commit timeout"); } agt_ncx_cancel_confirmed_commit(NULL, NCX_CC_EVENT_TIMEOUT); } } /* agt_ncx_check_cc_timeout */ /******************************************************************** * FUNCTION agt_ncx_cancel_confirmed_commit * * Cancel the confirmed-commit in progress and rollback * to the backup-cfg.xml file * * INPUTS: * scb == session control block making this change, may be NULL * event == confirmEvent enumeration value to use * *********************************************************************/ void agt_ncx_cancel_confirmed_commit (ses_cb_t *scb, ncx_confirm_event_t event) { cfg_template_t *running; status_t res; if (!commit_cb.cc_active) { return; } running = cfg_get_config_id(NCX_CFGID_RUNNING); if (LOGDEBUG) { log_debug("\nConfirmed-commit canceled"); } /* restore the config if needed * restore as the system user, not any specific user * to make sure that all the rollback edits will succeed */ res = agt_ncx_load_backup(commit_cb.cc_backup_source, running, 0); if (res != NO_ERR) { log_error("\nError: restore running config failed (%s)", get_error_string(res)); } if (res == NO_ERR) { res = cfg_fill_candidate_from_running(); if (res != NO_ERR) { log_error("\nError: resynch candidate after restore " "running config failed (%s)", get_error_string(res)); } } agt_sys_send_netconf_confirmed_commit(scb, event); clear_commit_cb(); } /* agt_ncx_cancel_confirmed_commit */ /* END file agt_ncx.c */ yuma123_2.14/netconf/src/agt/agt_sys.c0000664000175000017500000011640214770023131017742 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2013 - 2017, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_sys.c NETCONF System Data Model implementation: Server Side Support identifiers: container /system leaf /system/sysName leaf /system/sysCurrentDateTime leaf /system/sysBootDateTime leaf /system/sysLogLevel leaf /system/sysNetconfServerId notification /sysStartup leaf /sysStartup/startupSource list /sysStartup/bootError leaf /sysStartup/bootError/error-type leaf /sysStartup/bootError/error-tag leaf /sysStartup/bootError/error-severity leaf /sysStartup/bootError/error-app-tag leaf /sysStartup/bootError/error-path leaf /sysStartup/bootError/error-message leaf /sysStartup/bootError/error-info notification /sysConfigChange leaf /sysConfigChange/userName leaf /sysConfigChange/sessionId list /sysConfigChange/edit leaf /sysConfigChange/edit/target leaf /sysConfigChange/edit/operation notification /sysCapabilityChange container /sysCapabilityChange/changed-by choice /sysCapabilityChange/changed-by/server-or-user case /sysCapabilityChange/changed-by/server-or-user/server leaf /sysCapabilityChange/changed-by/server-or-user/server/server case /sysCapabilityChange/changed-by/server-or-user/by-user leaf /sysCapabilityChange/changed-by/server-or-user/by-user/userName leaf /sysCapabilityChange/changed-by/server-or-user/by-user/sessionId leaf /sysCapabilityChange/changed-by/server-or-user/by-user/remoteHost leaf-list /sysCapabilityChange/added-capability leaf-list /sysCapabilityChange/deleted-capability notification /sysSessionStart leaf /sysSessionStart/userName leaf /sysSessionStart/sessionId leaf /sysSessionStart/remoteHost notification /sysSessionEnd leaf /sysSessionEnd/userName leaf /sysSessionEnd/sessionId leaf /sysSessionEnd/remoteHost leaf /sysSessionEnd/terminationReason notification /sysConfirmedCommit leaf /sysConfirmedCommit/userName leaf /sysConfirmedCommit/sessionId leaf /sysConfirmedCommit/remoteHost leaf /sysConfirmedCommit/confirmEvent rpc /set-log-level leaf rpc/input/log-level rpc /disable-cache ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24feb09 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cap.h" #include "agt_cb.h" #include "agt_cfg.h" #include "agt_cli.h" #include "agt_not.h" #include "agt_rpc.h" #include "agt_ses.h" #include "agt_sys.h" #include "agt_util.h" #include "cfg.h" #include "getcb.h" #include "log.h" #include "ncxmod.h" #include "ncxtypes.h" #include "rpc.h" #include "rpc_err.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "tstamp.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xml_wr.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define ietf_system_N_system_state (const xmlChar *)"system-state" #define system_N_system (const xmlChar *)"yuma" #define system_N_sysName (const xmlChar *)"sysName" #define system_N_sysCurrentDateTime (const xmlChar *)"sysCurrentDateTime" #define system_N_sysBootDateTime (const xmlChar *)"sysBootDateTime" #define system_N_sysLogLevel (const xmlChar *)"sysLogLevel" #define system_N_sysNetconfServerId (const xmlChar *)"sysNetconfServerId" #define system_N_sysNetconfServerCLI (const xmlChar *)"sysNetconfServerCLI" #define system_N_sysStartup (const xmlChar *)"sysStartup" #define system_N_sysConfigChange (const xmlChar *)"sysConfigChange" #define system_N_edit (const xmlChar *)"edit" #define system_N_sysCapabilityChange (const xmlChar *)"sysCapabilityChange" #define system_N_sysSessionStart (const xmlChar *)"sysSessionStart" #define system_N_sysSessionEnd (const xmlChar *)"sysSessionEnd" #define system_N_sysConfirmedCommit (const xmlChar *)"sysConfirmedCommit" #define system_N_userName (const xmlChar *)"userName" #define system_N_sessionId (const xmlChar *)"sessionId" #define system_N_remoteHost (const xmlChar *)"remoteHost" #define system_N_killedBy (const xmlChar *)"killedBy" #define system_N_terminationReason (const xmlChar *)"terminationReason" #define system_N_confirmEvent (const xmlChar *)"confirmEvent" #define system_N_target (const xmlChar *)"target" #define system_N_operation (const xmlChar *)"operation" #define system_N_changed_by (const xmlChar *)"changed-by" #define system_N_added_capability (const xmlChar *)"added-capability" #define system_N_deleted_capability (const xmlChar *)"deleted-capability" #define system_N_uname (const xmlChar *)"uname" #define system_N_sysname (const xmlChar *)"sysname" #define system_N_release (const xmlChar *)"release" #define system_N_version (const xmlChar *)"version" #define system_N_machine (const xmlChar *)"machine" #define system_N_nodename (const xmlChar *)"nodename" #define system_N_set_log_level (const xmlChar *)"set-log-level" #define system_N_log_level (const xmlChar *)"log-level" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_sys_init_done = FALSE; /* ietf-system.yang */ static ncx_module_t *ietf_sysmod; /* system.yang */ static ncx_module_t *sysmod; /* ietf-netconf-notifications.yang */ static ncx_module_t *ietf_netconf_notifications_mod; /* cached pointer to the element template */ static obj_template_t *ietf_system_state_obj; static obj_template_t *yuma_system_obj; /* cached pointers to the eventType nodes for this module */ static obj_template_t *sysStartupobj; static obj_template_t *sysCapabilityChangeobj; static obj_template_t *sysSessionEndobj; static obj_template_t *sysConfirmedCommitobj; /******************************************************************** * FUNCTION payload_error * * Generate an error for a payload leaf that failed * * INPUTS: * name == leaf name that failed * res == error status *********************************************************************/ static void payload_error (const xmlChar *name, status_t res) { log_error( "\nError: cannot make payload leaf '%s' (%s)", name, get_error_string(res) ); } /* payload_error */ /******************************************************************** * FUNCTION get_currentDateTime * * operation handler for the sysCurrentDateTime leaf * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ static status_t get_currentDateTime (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { xmlChar *buff; (void)scb; (void)virval; if (cbmode == GETCB_GET_VALUE) { buff = (xmlChar *)m__getMem(TSTAMP_MIN_SIZE); if (!buff) { return ERR_INTERNAL_MEM; } tstamp_datetime(buff); VAL_STR(dstval) = buff; return NO_ERR; } else { return ERR_NCX_OPERATION_NOT_SUPPORTED; } } /* get_currentDateTime */ /******************************************************************** * FUNCTION get_currentLogLevel * * operation handler for the sysCurrentDateTime leaf * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ static status_t get_currentLogLevel (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { const xmlChar *loglevelstr; log_debug_t loglevel; status_t res; (void)scb; (void)virval; res = NO_ERR; if (cbmode == GETCB_GET_VALUE) { loglevel = log_get_debug_level(); loglevelstr = log_get_debug_level_string(loglevel); if (loglevelstr == NULL) { res = ERR_NCX_OPERATION_FAILED; } else { res = ncx_set_enum(loglevelstr, VAL_ENU(dstval)); } } else { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } return res; } /* get_currentLogLevel */ /******************************************************************** * FUNCTION set_log_level_invoke * * set-log-level : invoke params callback * * INPUTS: * see rpc/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t set_log_level_invoke (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { val_value_t *levelval; log_debug_t levelenum; status_t res; (void)scb; (void)methnode; res = NO_ERR; /* get the level parameter */ levelval = val_find_child(msg->rpc_input, AGT_SYS_MODULE, NCX_EL_LOGLEVEL); if (levelval) { if (levelval->res == NO_ERR) { levelenum = log_get_debug_level_enum((const char *) VAL_ENUM_NAME(levelval)); if (levelenum != LOG_DEBUG_NONE) { log_set_debug_level(levelenum); } else { res = ERR_NCX_OPERATION_FAILED; } } else { res = levelval->res; } } return res; } /* set_log_level_invoke */ /******************************************************************** * FUNCTION send_sysStartup * * Queue the notification * *********************************************************************/ static void send_sysStartup (void) { agt_not_msg_t *not; cfg_template_t *cfg; val_value_t *leafval, *bootErrorval; rpc_err_rec_t *rpcerror; obj_template_t *bootErrorobj; status_t res; log_debug("\nagt_sys: generating notification"); not = agt_not_new_notification(sysStartupobj); if (!not) { log_error("\nError: malloc failed; cannot send "); return; } /* add sysStartup/startupSource */ cfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (cfg) { if (cfg->src_url) { leafval = agt_make_leaf(sysStartupobj, (const xmlChar *)"startupSource", cfg->src_url, &res); if (leafval) { agt_not_add_to_payload(not, leafval); } else { payload_error((const xmlChar *)"startupSource", res); } } /* add sysStartup/bootError for each error recorded */ if (!dlq_empty(&cfg->load_errQ)) { /* get the bootError object */ bootErrorobj = obj_find_child( sysStartupobj, AGT_SYS_MODULE, (const xmlChar *)"bootError"); if (bootErrorobj) { rpcerror = (rpc_err_rec_t *)dlq_firstEntry(&cfg->load_errQ); for ( ; rpcerror; rpcerror = (rpc_err_rec_t *)dlq_nextEntry(rpcerror)) { /* make the bootError value struct */ bootErrorval = val_new_value(); if (!bootErrorval) { payload_error((const xmlChar *)"bootError", ERR_INTERNAL_MEM); } else { val_init_from_template(bootErrorval, bootErrorobj); res = agt_rpc_fill_rpc_error(rpcerror, bootErrorval); if (res != NO_ERR) { log_error("\nError: problems making " "(%s)", get_error_string(res)); } /* add even if there are some missing leafs */ agt_not_add_to_payload(not, bootErrorval); } } } else { SET_ERROR(ERR_INTERNAL_VAL); } } } else { SET_ERROR(ERR_INTERNAL_VAL); } agt_not_queue_notification(not); } /* send_sysStartup */ /******************************************************************** * FUNCTION netconf_notifications_add_common_session_parms * * Add the leafs from the SysCommonSessionParms grouping * * INPUTS: * scb == session control block to use for payload values * not == notification msg to use to add parms into * * OUTPUTS: * 'not' payloadQ has malloced entries added to it *********************************************************************/ static void netconf_notifications_add_common_session_parms (const ses_cb_t *scb, agt_not_msg_t *not, val_value_t* parent_val) { obj_template_t *parent_obj; val_value_t *leafval; status_t res; ses_id_t use_sid; if(not!=NULL) { assert(parent_val==NULL); parent_obj = not->notobj; } else if(parent_val!=NULL) { assert(not==NULL); parent_obj=parent_val->obj; } else { assert(0); } /* add userName */ if (scb->username) { leafval = agt_make_leaf(parent_obj, "username", scb->username, &res); assert(leafval); if(not) { agt_not_add_to_payload(not, leafval); } else { val_add_child(leafval, parent_val); } } /* add sessionId */ if (scb->sid) { use_sid = scb->sid; } else if (scb->rollback_sid) { use_sid = scb->rollback_sid; } else { res = ERR_NCX_NOT_IN_RANGE; use_sid = 0; } if (use_sid) { leafval = agt_make_uint_leaf(parent_obj, "session-id", use_sid, &res); assert(leafval); if(not) { agt_not_add_to_payload(not, leafval); } else { val_add_child(leafval, parent_val); } } /* add remoteHost */ if (scb->peeraddr) { leafval = agt_make_leaf(parent_obj, "source-host", scb->peeraddr, &res); assert(leafval); if(not) { agt_not_add_to_payload(not, leafval); } else { val_add_child(leafval, parent_val); } } } /* netconf_notifications_add_common_session_parms */ /******************************************************************** * FUNCTION init_static_vars * * Init the static vars * *********************************************************************/ static void init_static_sys_vars (void) { ietf_sysmod = NULL; sysmod = NULL; ietf_system_state_obj = NULL; yuma_system_obj = NULL; sysStartupobj = NULL; sysCapabilityChangeobj = NULL; sysSessionEndobj = NULL; sysConfirmedCommitobj = NULL; } /* init_static_sys_vars */ /************* E X T E R N A L F U N C T I O N S ***************/ /******************************************************************** * FUNCTION agt_sys_init * * INIT 1: * Initialize the server notification module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t agt_sys_init (void) { agt_profile_t *agt_profile; status_t res; if (agt_sys_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } if (LOGDEBUG2) { log_debug2("\nagt_sys: Loading notifications module"); } agt_profile = agt_get_profile(); init_static_sys_vars(); agt_sys_init_done = TRUE; /* load the yuma module */ res = ncxmod_load_module(AGT_SYS_MODULE, NULL, &agt_profile->agt_savedevQ, &sysmod); if (res != NO_ERR) { return res; } /* load the ietf-system module */ res = ncxmod_load_module(AGT_IETF_SYS_MODULE, NULL, &agt_profile->agt_savedevQ, &ietf_sysmod); if (res != NO_ERR) { return res; } /* load the ietf-netconf-notifications module */ res = ncxmod_load_module("ietf-netconf-notifications", NULL, &agt_profile->agt_savedevQ, &ietf_netconf_notifications_mod); if (res != NO_ERR) { return res; } /* find the object definition for the system element */ ietf_system_state_obj = ncx_find_object(ietf_sysmod, ietf_system_N_system_state); if (!ietf_system_state_obj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } yuma_system_obj = obj_find_child(ietf_system_state_obj, AGT_SYS_MODULE, system_N_system); if (!yuma_system_obj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } sysStartupobj = ncx_find_object(sysmod, system_N_sysStartup); if (!sysStartupobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* set up set-log-level RPC operation */ res = agt_rpc_register_method(AGT_SYS_MODULE, system_N_set_log_level, AGT_RPC_PH_INVOKE, set_log_level_invoke); if (res != NO_ERR) { return SET_ERROR(res); } return NO_ERR; } /* agt_sys_init */ /******************************************************************** * FUNCTION agt_sys_init2 * * INIT 2: * Initialize the monitoring data structures * This must be done after the config is loaded * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t agt_sys_init2 (void) { val_value_t *ietf_system_state_val, *yuma_system_val, *unameval, *childval, *tempval; cfg_template_t *runningcfg; const xmlChar *myhostname; obj_template_t *unameobj; status_t res; xmlChar *buffer, *p, tstampbuff[TSTAMP_MIN_SIZE]; struct utsname utsbuff; int retval; if (!agt_sys_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } /* get the running config to add some static data into */ runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (!runningcfg || !runningcfg->root) { return SET_ERROR(ERR_INTERNAL_VAL); } /* add /system-state */ ietf_system_state_val = val_new_value(); if (!ietf_system_state_val) { return ERR_INTERNAL_MEM; } val_init_from_template(ietf_system_state_val, ietf_system_state_obj); /* handing off the malloced memory here */ val_add_child_sorted(ietf_system_state_val, runningcfg->root); /* add /system-state/yuma */ yuma_system_val = val_new_value(); if (!yuma_system_val) { return ERR_INTERNAL_MEM; } val_init_from_template(yuma_system_val, yuma_system_obj); /* handing off the malloced memory here */ val_add_child_sorted(yuma_system_val, ietf_system_state_val); /* add /system-state/yuma/sysName */ myhostname = (const xmlChar *)getenv("HOSTNAME"); if (!myhostname) { myhostname = (const xmlChar *)"localhost"; } childval = agt_make_leaf(yuma_system_obj, system_N_sysName, myhostname, &res); if (childval) { val_add_child(childval, yuma_system_val); } else { return res; } /* add /system-state/yuma/sysCurrentDateTime */ childval = agt_make_virtual_leaf(yuma_system_obj, system_N_sysCurrentDateTime, get_currentDateTime, &res); if (childval) { val_add_child(childval, yuma_system_val); } else { return res; } /* add /system-state/yuma/sysBootDateTime */ tstamp_datetime(tstampbuff); childval = agt_make_leaf(yuma_system_obj, system_N_sysBootDateTime, tstampbuff, &res); if (childval) { val_add_child(childval, yuma_system_val); } else { return res; } /* add /system-state/yuma/sysLogLevel */ childval = agt_make_virtual_leaf(yuma_system_obj, system_N_sysLogLevel, get_currentLogLevel, &res); if (childval) { val_add_child(childval, yuma_system_val); } else { return res; } /* add /system-state/yuma/sysNetconfServerId */ buffer = m__getMem(256); if (buffer == NULL) { return ERR_INTERNAL_MEM; } p = buffer; p += xml_strcpy(p, (const xmlChar *)"netconfd "); res = ncx_get_version(p, 247); if (res == NO_ERR) { childval = agt_make_leaf(yuma_system_obj, system_N_sysNetconfServerId, buffer, &res); m__free(buffer); if (childval) { val_add_child(childval, yuma_system_val); } else { return res; } } else { m__free(buffer); log_error("\nError: could not get netconfd version"); } buffer = NULL; /* add /system-state/yuma/sysNetconfServerCLI */ tempval = val_clone(agt_cli_get_valset()); if (tempval == NULL) { return ERR_INTERNAL_MEM; } val_change_nsid(tempval, yuma_system_val->nsid); childval = agt_make_object(yuma_system_obj, system_N_sysNetconfServerCLI, &res); if (childval == NULL) { val_free_value(tempval); return ERR_INTERNAL_MEM; } val_move_children(tempval, childval); val_free_value(tempval); val_add_child(childval, yuma_system_val); /* get the system information */ memset(&utsbuff, 0x0, sizeof(utsbuff)); retval = uname(&utsbuff); if (retval) { log_warn("\nWarning: data not available"); } else { unameobj = obj_find_child(yuma_system_obj, AGT_SYS_MODULE, system_N_uname); if (!unameobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* add /system-state/yuma/uname */ unameval = val_new_value(); if (!unameval) { return ERR_INTERNAL_MEM; } val_init_from_template(unameval, unameobj); val_add_child(unameval, yuma_system_val); /* add /system-state/yuma/uname/sysname */ childval = agt_make_leaf(unameobj, system_N_sysname, (const xmlChar *)utsbuff.sysname, &res); if (childval) { val_add_child(childval, unameval); } else { return res; } /* add /system-state/yuma/uname/release */ childval = agt_make_leaf(unameobj, system_N_release, (const xmlChar *)utsbuff.release, &res); if (childval) { val_add_child(childval, unameval); } else { return res; } /* add /system-state/yuma/uname/version */ childval = agt_make_leaf(unameobj, system_N_version, (const xmlChar *)utsbuff.version, &res); if (childval) { val_add_child(childval, unameval); } else { return res; } /* add /system-state/yuma/uname/machine */ childval = agt_make_leaf(unameobj, system_N_machine, (const xmlChar *)utsbuff.machine, &res); if (childval) { val_add_child(childval, unameval); } else { return res; } /* add /system-state/yuma/uname/nodename */ childval = agt_make_leaf(unameobj, system_N_nodename, (const xmlChar *)utsbuff.nodename, &res); if (childval) { val_add_child(childval, unameval); } else { return res; } } /* add sysStartup to notificationQ */ send_sysStartup(); return NO_ERR; } /* agt_sys_init2 */ /******************************************************************** * FUNCTION agt_sys_cleanup * * Cleanup the module data structures * * INPUTS: * * RETURNS: * none *********************************************************************/ void agt_sys_cleanup (void) { if (agt_sys_init_done) { init_static_sys_vars(); agt_sys_init_done = FALSE; agt_rpc_unregister_method(AGT_SYS_MODULE, system_N_set_log_level); } } /* agt_sys_cleanup */ /******************************************************************** * FUNCTION agt_sys_send_netconf_session_start * * Queue the notification * * INPUTS: * scb == session control block to use for payload values * * OUTPUTS: * notification generated and added to notificationQ * *********************************************************************/ void agt_sys_send_netconf_session_start (const ses_cb_t *scb) { agt_not_msg_t *not; obj_template_t *netconf_session_start_obj; if (LOGDEBUG) { log_debug("\nagt_sys: generating " "notification"); } netconf_session_start_obj = ncx_find_object(ietf_netconf_notifications_mod, "netconf-session-start"); assert(netconf_session_start_obj); not = agt_not_new_notification(netconf_session_start_obj); if (!not) { log_error("\nError: malloc failed; cannot " "send "); return; } netconf_notifications_add_common_session_parms(scb, not, NULL /*parent_val*/); agt_not_queue_notification(not); } /* agt_sys_send_netconf_session_start */ /******************************************************************** * FUNCTION get_termination_reason_str * * Convert the termination reason enum to a string * * INPUTS: * termreason == enum for the terminationReason leaf * * OUTPUTS: * the termination reason string * *********************************************************************/ static const xmlChar* get_termination_reason_str ( ses_term_reason_t termreason) { const xmlChar *termreasonstr; switch (termreason) { case SES_TR_NONE: SET_ERROR(ERR_INTERNAL_VAL); termreasonstr = (const xmlChar *)"other"; break; case SES_TR_CLOSED: termreasonstr = (const xmlChar *)"closed"; break; case SES_TR_KILLED: termreasonstr = (const xmlChar *)"killed"; break; case SES_TR_DROPPED: termreasonstr = (const xmlChar *)"dropped"; break; case SES_TR_TIMEOUT: termreasonstr = (const xmlChar *)"timeout"; break; case SES_TR_OTHER: termreasonstr = (const xmlChar *)"other"; break; case SES_TR_BAD_HELLO: termreasonstr = (const xmlChar *)"bad-hello"; break; case SES_TR_BAD_START: default: SET_ERROR(ERR_INTERNAL_VAL); termreasonstr = (const xmlChar *)"other"; } return termreasonstr; } /******************************************************************** * FUNCTION agt_sys_send_netconf_session_end * * Queue the notification * * INPUTS: * scb == session control block to use for payload values * termreason == enum for the termination-reason leaf * killedby == session-id for killed-by leaf if termination_reason == "killed" * ignored otherwise * * OUTPUTS: * notification generated and added to notificationQ * *********************************************************************/ void agt_sys_send_netconf_session_end(const ses_cb_t *scb, ses_term_reason_t termination_reason, ses_id_t killed_by) { agt_not_msg_t *not; val_value_t *leafval; const xmlChar *termination_reason_str; status_t res; obj_template_t *netconf_session_end_obj; assert(scb && "agt_sys_send_netconf_session_end() - param scb is NULL"); log_debug("\nagt_sys: generating notification"); netconf_session_end_obj = ncx_find_object(ietf_netconf_notifications_mod, "netconf-session-end"); assert(netconf_session_end_obj); not = agt_not_new_notification(netconf_session_end_obj); assert(not); /* session started; not just being killed * in the message handler */ if (termination_reason != SES_TR_BAD_START) { netconf_notifications_add_common_session_parms(scb, not, NULL /*parent_val*/); } /* add sysSessionEnd/killedBy */ if (termination_reason == SES_TR_KILLED) { leafval = agt_make_uint_leaf( netconf_session_end_obj, "killed-by", killed_by, &res ); assert(leafval); agt_not_add_to_payload(not, leafval); } /* add sysSessionEnd/terminationReason */ termination_reason_str = get_termination_reason_str(termination_reason); leafval = agt_make_leaf( netconf_session_end_obj, "termination-reason", termination_reason_str, &res ); assert(leafval); agt_not_add_to_payload(not, leafval); agt_not_queue_notification(not); } /* agt_sys_send_netconf_session_end */ /******************************************************************** * FUNCTION agt_sys_send_netconf_config_change * * Queue the notification * * INPUTS: * scb == session control block to use for payload values * auditrecQ == Q of agt_cfg_audit_rec_t structs to use * for the notification payload contents * * OUTPUTS: * notification generated and added to notificationQ * *********************************************************************/ void agt_sys_send_netconf_config_change (const ses_cb_t *scb, dlq_hdr_t *auditrecQ) { agt_not_msg_t *not; agt_cfg_audit_rec_t *auditrec; val_value_t *leafval, *listval; obj_template_t *netconf_config_change_obj; obj_template_t *listobj; status_t res; #ifdef DEBUG if (!scb || !auditrecQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (LOGDEBUG) { log_debug("\nagt_sys: generating " "notification"); } netconf_config_change_obj = ncx_find_object(ietf_netconf_notifications_mod, "netconf-config-change"); assert(netconf_config_change_obj); not = agt_not_new_notification(netconf_config_change_obj); assert(not); { obj_template_t *changed_by_obj; val_value_t *changed_by_val; changed_by_obj = obj_find_child(not->notobj, "ietf-netconf-notifications", "changed-by"); assert(changed_by_obj); changed_by_val = val_new_value(); val_init_from_template(changed_by_val, changed_by_obj); netconf_notifications_add_common_session_parms(scb, NULL /*not*/, changed_by_val); agt_not_add_to_payload(not, changed_by_val); } listobj = obj_find_child(netconf_config_change_obj, "ietf-netconf-notifications", "edit"); if (listobj == NULL) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } else { for (auditrec = (agt_cfg_audit_rec_t *)dlq_firstEntry(auditrecQ); auditrec != NULL; auditrec = (agt_cfg_audit_rec_t *)dlq_nextEntry(auditrec)) { /* add netconf-config-change/edit */ listval = val_new_value(); assert(listval != NULL); { val_init_from_template(listval, listobj); /* pass off listval malloc here */ agt_not_add_to_payload(not, listval); /* add netconf-config-change/edit/target */ leafval = agt_make_leaf(listobj, "target", auditrec->target, &res); assert(leafval); val_add_child(leafval, listval); /* add netconf-config-change/edit/operation */ leafval = agt_make_leaf(listobj, "operation", op_editop_name(auditrec->editop), &res); assert(leafval); val_add_child(leafval, listval); } } } agt_not_queue_notification(not); } /* agt_sys_send_netconf_config_change */ /******************************************************************** * FUNCTION agt_sys_send_netconf_capablity_change * * Send a event for a module * being added * * Queue the notification * * INPUTS: * changed_by == session control block that made the * change to add this module * == NULL if the server made the change * is_add == TRUE if the capability is being added * FALSE if the capability is being deleted * capstr == capability string that was added or deleted * * OUTPUTS: * notification generated and added to notificationQ * *********************************************************************/ void agt_sys_send_netconf_capability_change (ses_cb_t *changed_by, boolean is_add, const xmlChar *capstr) { agt_not_msg_t *not; obj_template_t *netconf_capability_change_obj; val_value_t *leafval; status_t res; #ifdef DEBUG if (!capstr) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (LOGDEBUG) { log_debug("\nagt_sys: generating " "notification"); } netconf_capability_change_obj = ncx_find_object(ietf_netconf_notifications_mod, "netconf-capability-change"); assert(netconf_capability_change_obj); not = agt_not_new_notification(netconf_capability_change_obj); assert(not); { obj_template_t *changed_by_obj; val_value_t *changed_by_val; changed_by_obj = obj_find_child(not->notobj, "ietf-netconf-notifications", "changed-by"); assert(changed_by_obj); changed_by_val = val_new_value(); val_init_from_template(changed_by_val, changed_by_obj); if(changed_by) { netconf_notifications_add_common_session_parms(changed_by, NULL /*not*/, changed_by_val); } else { leafval = agt_make_leaf(changed_by_obj, "server", NULL, &res); assert(leafval); val_add_child(leafval, changed_by_val); } agt_not_add_to_payload(not, changed_by_val); } if (is_add) { /* add netconf-capability-change/added-capability */ leafval = agt_make_leaf(netconf_capability_change_obj, "added-capability", capstr, &res); } else { /* add netconf-capability-change/deleted-capability */ leafval = agt_make_leaf(netconf_capability_change_obj, "deleted-capability", capstr, &res); } assert(leafval); agt_not_add_to_payload(not, leafval); agt_not_queue_notification(not); } /* agt_sys_send_netconf_capability_change */ /******************************************************************** * FUNCTION agt_sys_send_netconf_confirmed_commit * * Queue the notification * * INPUTS: * scb == session control block to use for payload values * event == enum for the confirmEvent leaf * * OUTPUTS: * notification generated and added to notificationQ * *********************************************************************/ void agt_sys_send_netconf_confirmed_commit (const ses_cb_t *scb, ncx_confirm_event_t event) { agt_not_msg_t *not; obj_template_t *netconf_confirmed_commit_obj; val_value_t *leafval; const xmlChar *eventstr; status_t res; res = NO_ERR; eventstr = ncx_get_confirm_event_str(event); if (!eventstr) { SET_ERROR(ERR_INTERNAL_VAL); return; } if (LOGDEBUG) { log_debug("\nagt_sys: generating " "notification (%s)", eventstr); } netconf_confirmed_commit_obj = ncx_find_object(ietf_netconf_notifications_mod, "netconf-confirmed-commit"); assert(netconf_confirmed_commit_obj); not = agt_not_new_notification(netconf_confirmed_commit_obj); assert(not); if (event!=NCX_CC_EVENT_TIMEOUT) { assert(scb); netconf_notifications_add_common_session_parms(scb, not, NULL /*changed_by_val*/); } /* add sysConfirmedCommit/confirmEvent */ leafval = agt_make_leaf(netconf_confirmed_commit_obj, "confirm-event", eventstr, &res); assert(leafval); agt_not_add_to_payload(not, leafval); agt_not_queue_notification(not); } /* agt_sys_send_netconf_confirmed_commit */ /* END file agt_sys.c */ yuma123_2.14/netconf/src/agt/agt_rpcerr.c0000664000175000017500000016056314770023131020430 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_rpcerr.c NETCONF Protocol Operations: Agent Side Support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 06mar06 abb begun; split from agt_rpc.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "agt_rpc.h" #include "agt_rpcerr.h" #include "cfg.h" #include "def_reg.h" #include "ncx.h" #include "ncxconst.h" #include "rpc.h" #include "rpc_err.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xpath.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION get_rpcerr * * Translate the status_t to a rpc_err_t * * INPUTS: * intres == internal status_t error code * isel == TRUE if this error is for an element * == FALSE if this error is for an attribute * errsev == address of return error severity * apptag == address of return error-app-tag * * OUTPUTS: * *errsev == error severity (error or warning) * *apptag == pointer to error-app-tag string * * RETURNS: * rpc error id for the specified internal error id *********************************************************************/ static rpc_err_t get_rpcerr (status_t intres, boolean isel, rpc_err_sev_t *errsev, const xmlChar **apptag) { if (intres >= ERR_WARN_BASE) { *errsev = RPC_ERR_SEV_WARNING; *apptag = RPC_ERR_APPTAG_GEN_WARNING; return RPC_ERR_NONE; } /* setup defaults */ *errsev = RPC_ERR_SEV_ERROR; *apptag = RPC_ERR_APPTAG_GEN_ERROR; /* translate the internal NCX error code to a netconf code */ switch (intres) { case NO_ERR: *errsev = RPC_ERR_SEV_NONE; *apptag = NCX_EL_NONE; return RPC_ERR_NONE; case ERR_END_OF_FILE: *apptag = RPC_ERR_APPTAG_IO_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_INTERNAL_PTR: *apptag = RPC_ERR_APPTAG_INT_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_INTERNAL_MEM: *apptag = RPC_ERR_APPTAG_MALLOC_ERROR; return RPC_ERR_RESOURCE_DENIED; case ERR_INTERNAL_VAL: case ERR_INTERNAL_BUFF: case ERR_INTERNAL_QDEL: case ERR_INTERNAL_INIT_SEQ: case ERR_QNODE_NOT_HDR: case ERR_QNODE_NOT_DATA: case ERR_BAD_QLINK: case ERR_Q_ALREADY: *apptag = RPC_ERR_APPTAG_INT_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_TOO_MANY_ENTRIES: case ERR_BUFF_OVFL: *apptag = RPC_ERR_APPTAG_LIMIT_REACHED; return RPC_ERR_RESOURCE_DENIED; case ERR_XML2_FAILED: case ERR_XML_READER_INTERNAL: case ERR_XML_READER_START_FAILED: case ERR_XML_READER_READ: *apptag = RPC_ERR_APPTAG_LIBXML2_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_FIL_OPEN: case ERR_FIL_READ: case ERR_FIL_CLOSE: case ERR_FIL_WRITE: case ERR_FIL_CHDIR: case ERR_FIL_STAT: case ERR_FIL_DELETE: case ERR_FIL_SETPOS: case ERR_OPEN_DIR_FAILED: case ERR_READ_DIR_FAILED: case ERR_PARSPOST_RD_INPUT: *apptag = RPC_ERR_APPTAG_IO_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_DB_CONNECT_FAILED: case ERR_DB_ENTRY_EXISTS: case ERR_DB_NOT_FOUND: case ERR_DB_QUERY_FAILED: case ERR_DB_DELETE_FAILED: case ERR_DB_WRONG_CKSUM: case ERR_DB_WRONG_TAGTYPE: case ERR_DB_READ_FAILED: case ERR_DB_WRITE_FAILED: case ERR_DB_INIT_FAILED: *apptag = RPC_ERR_APPTAG_SQL_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_TR_BEEP_INIT: case ERR_TR_BEEP_NC_INIT: *apptag = RPC_ERR_APPTAG_BEEP_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_NO_CFGFILE: case ERR_NO_SRCFILE: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_OPERATION_FAILED; case ERR_FIL_BAD_DRIVE: case ERR_FIL_BAD_PATH: case ERR_FIL_BAD_FILENAME: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_OPERATION_FAILED; case ERR_DUP_VALPAIR: case ERR_PAGE_NOT_HANDLED: case ERR_PAGE_ACCESS_DENIED: case ERR_MISSING_FORM_PARAMS: case ERR_FORM_STATE: case ERR_DUP_NS: *apptag = RPC_ERR_APPTAG_HTML_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_XML_READER_NODETYP: *apptag = RPC_ERR_APPTAG_LIBXML2_ERROR; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_XML_READER_NULLNAME: case ERR_XML_READER_NULLVAL: *apptag = RPC_ERR_APPTAG_LIBXML2_ERROR; return (isel) ? RPC_ERR_BAD_ELEMENT : RPC_ERR_BAD_ATTRIBUTE; case ERR_XML_READER_WRONGNAME: *apptag = RPC_ERR_APPTAG_LIBXML2_ERROR; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_XML_READER_WRONGVAL: *apptag = RPC_ERR_APPTAG_LIBXML2_ERROR; return (isel) ? RPC_ERR_BAD_ELEMENT : RPC_ERR_BAD_ATTRIBUTE; case ERR_XML_READER_WRONGEL: case ERR_XML_READER_EXTRANODES: *apptag = RPC_ERR_APPTAG_LIBXML2_ERROR; return RPC_ERR_UNKNOWN_ELEMENT; case ERR_XML_READER_EOF: *apptag = RPC_ERR_APPTAG_LIBXML2_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_WRONG_LEN: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_BAD_ELEMENT : RPC_ERR_BAD_ATTRIBUTE; case ERR_NCX_ENTRY_EXISTS: case ERR_NCX_DUP_ENTRY: *apptag = RPC_ERR_APPTAG_DUPLICATE_ERROR; return RPC_ERR_DATA_EXISTS; case ERR_NCX_NOT_FOUND: case ERR_NCX_MISSING_FILE: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_UNKNOWN_PARM: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_INVALID_NAME: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_BAD_ELEMENT : RPC_ERR_BAD_ATTRIBUTE; case ERR_NCX_UNKNOWN_NS: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_UNKNOWN_NAMESPACE; case ERR_NCX_WRONG_NS: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_UNKNOWN_NAMESPACE; case ERR_NCX_WRONG_TYPE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_BAD_ELEMENT : RPC_ERR_BAD_ATTRIBUTE; case ERR_NCX_WRONG_VAL: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_BAD_ELEMENT : RPC_ERR_BAD_ATTRIBUTE; case ERR_NCX_MISSING_PARM: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_DATA_MISSING; case ERR_NCX_EXTRA_PARM: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_EMPTY_VAL: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_BAD_ELEMENT : RPC_ERR_BAD_ATTRIBUTE; case ERR_NCX_MOD_NOT_FOUND: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_LEN_EXCEEDED: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_INVALID_TOKEN: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_UNENDED_QSTRING: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_READ_FAILED: *apptag = RPC_ERR_APPTAG_IO_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_INVALID_NUM: case ERR_NCX_INVALID_HEXNUM: case ERR_NCX_INVALID_REALNUM: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_BAD_ELEMENT : RPC_ERR_BAD_ATTRIBUTE; case ERR_NCX_EOF: *apptag = RPC_ERR_APPTAG_IO_ERROR; return RPC_ERR_DATA_MISSING; case ERR_NCX_WRONG_TKTYPE: case ERR_NCX_WRONG_TKVAL: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; return RPC_ERR_INVALID_VALUE; case ERR_NCX_BUFF_SHORT: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_INVALID_RANGE: case ERR_NCX_OVERLAP_RANGE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_DEF_NOT_FOUND: case ERR_NCX_DEFSEG_NOT_FOUND: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_TYPE_NOT_INDEX: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_BAD_ELEMENT : RPC_ERR_BAD_ATTRIBUTE; case ERR_NCX_INDEX_TYPE_NOT_FOUND: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_INVALID_VALUE; case ERR_NCX_TYPE_NOT_MDATA: case ERR_NCX_MDATA_NOT_ALLOWED: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_TOP_NOT_FOUND: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; /*** match netconf errors here ***/ case ERR_NCX_IN_USE: *apptag = RPC_ERR_APPTAG_RESOURCE_IN_USE; return RPC_ERR_IN_USE; case ERR_NCX_INVALID_VALUE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_TOO_BIG: *apptag = RPC_ERR_APPTAG_LIMIT_REACHED; return RPC_ERR_TOO_BIG; case ERR_NCX_MISSING_ATTRIBUTE: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_MISSING_ATTRIBUTE; case ERR_NCX_BAD_ATTRIBUTE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_BAD_ATTRIBUTE; case ERR_NCX_UNKNOWN_ATTRIBUTE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_MISSING_ELEMENT: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_MISSING_ELEMENT; case ERR_NCX_BAD_ELEMENT: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_BAD_ELEMENT; case ERR_NCX_UNKNOWN_ELEMENT: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_UNKNOWN_ELEMENT; case ERR_NCX_UNKNOWN_NAMESPACE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_UNKNOWN_NAMESPACE; case ERR_NCX_ACCESS_DENIED: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_ACCESS_DENIED; case ERR_NCX_LOCK_DENIED: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_LOCK_DENIED; case ERR_NCX_RESOURCE_DENIED: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_RESOURCE_DENIED; case ERR_NCX_ROLLBACK_FAILED: *apptag = RPC_ERR_APPTAG_RECOVER_FAILED; return RPC_ERR_ROLLBACK_FAILED; case ERR_NCX_DATA_EXISTS: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_DATA_EXISTS; case ERR_NCX_DATA_MISSING: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_DATA_MISSING; case ERR_NCX_OPERATION_NOT_SUPPORTED: *apptag = RPC_ERR_APPTAG_NO_SUPPORT; return RPC_ERR_OPERATION_NOT_SUPPORTED; case ERR_NCX_OPERATION_FAILED: *apptag = RPC_ERR_APPTAG_GEN_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_PARTIAL_OPERATION: *apptag = RPC_ERR_APPTAG_GEN_ERROR; return RPC_ERR_PARTIAL_OPERATION; /* netconf error extensions */ case ERR_NCX_WRONG_NAMESPACE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_UNKNOWN_NAMESPACE; case ERR_NCX_WRONG_NODEDEPTH: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_BAD_ELEMENT; case ERR_NCX_WRONG_OWNER: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_WRONG_ELEMENT: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_WRONG_ORDER: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_EXTRA_NODE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_WRONG_NODETYP: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_WRONG_NODETYP_SIM: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_WRONG_NODETYP_CPX: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_WRONG_DATATYP: case ERR_NCX_WRONG_DATAVAL: case ERR_NCX_NUMLEN_TOOBIG: case ERR_NCX_WRONG_NUMTYP: case ERR_NCX_EXTRA_ENUMCH: case ERR_NCX_EXTRA_LISTSTR: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_BAD_ELEMENT : RPC_ERR_BAD_ATTRIBUTE; case ERR_NCX_NOT_IN_RANGE: *apptag = RPC_ERR_APPTAG_RANGE; return RPC_ERR_INVALID_VALUE; case ERR_NCX_VAL_NOTINSET: *apptag = RPC_ERR_APPTAG_VALUE_SET; return RPC_ERR_INVALID_VALUE; case ERR_NCX_UNKNOWN_OBJECT: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_UNKNOWN_ELEMENT; case ERR_NCX_EXTRA_PARMINST: case ERR_NCX_EXTRA_CHOICE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_MISSING_CHOICE: *apptag = RPC_ERR_APPTAG_CHOICE; return RPC_ERR_DATA_MISSING; /* 13.6 */ case ERR_NCX_CFG_STATE: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_UNKNOWN_APP: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_UNKNOWN_ELEMENT; case ERR_NCX_UNKNOWN_TYPE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_NO_ACCESS_ACL: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_ACCESS_DENIED; case ERR_NCX_NO_ACCESS_LOCK: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_IN_USE; case ERR_NCX_NO_ACCESS_STATE: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_NO_ACCESS_MAX: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_ACCESS_DENIED; case ERR_NCX_WRONG_INDEX_TYPE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_BAD_ELEMENT; case ERR_NCX_WRONG_INSTANCE_TYPE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_MISSING_INDEX: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_DATA_MISSING; case ERR_NCX_CFG_NOT_FOUND: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_INVALID_VALUE; case ERR_NCX_EXTRA_ATTR: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_MISSING_ATTR: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_MISSING_ATTRIBUTE; case ERR_NCX_MISSING_VAL_INST: *apptag = RPC_ERR_APPTAG_INSTANCE_REQ; return RPC_ERR_DATA_MISSING; /* 13.5 */ case ERR_NCX_EXTRA_VAL_INST: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_NOT_WRITABLE: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_ACCESS_DENIED; case ERR_NCX_INVALID_PATTERN: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_BAD_ELEMENT : RPC_ERR_BAD_ATTRIBUTE; case ERR_NCX_WRONG_VERSION: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_BAD_ELEMENT : RPC_ERR_BAD_ATTRIBUTE; case ERR_NCX_CONNECT_FAILED: case ERR_NCX_SESSION_FAILED: *apptag = RPC_ERR_APPTAG_IO_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_UNKNOWN_HOST: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_AUTH_FAILED: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_ACCESS_DENIED; case ERR_NCX_UNENDED_COMMENT: case ERR_NCX_INVALID_CONCAT: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_IMP_NOT_FOUND: case ERR_NCX_MISSING_TYPE: *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_RESTRICT_NOT_ALLOWED: case ERR_NCX_REFINE_NOT_ALLOWED: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_DEF_LOOP: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_DEFCHOICE_NOT_OPTIONAL: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_IMPORT_LOOP: case ERR_NCX_INCLUDE_LOOP: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_EXP_MODULE: case ERR_NCX_EXP_SUBMODULE: case ERR_NCX_PREFIX_NOT_FOUND: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_IMPORT_ERRORS: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_PATTERN_FAILED: *apptag = RPC_ERR_APPTAG_PATTERN; return RPC_ERR_INVALID_VALUE; case ERR_NCX_INVALID_TYPE_CHANGE: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_MANDATORY_NOT_ALLOWED: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_UNIQUE_TEST_FAILED: *apptag = RPC_ERR_APPTAG_UNIQUE_FAILED; return RPC_ERR_OPERATION_FAILED; /* 13.1 */ case ERR_NCX_MAX_ELEMS_VIOLATION: *apptag = RPC_ERR_APPTAG_MAX_ELEMS; return RPC_ERR_OPERATION_FAILED; /* 13.2 */ case ERR_NCX_MIN_ELEMS_VIOLATION: *apptag = RPC_ERR_APPTAG_MIN_ELEMS; return RPC_ERR_OPERATION_FAILED; /* 13.3 */ case ERR_NCX_MUST_TEST_FAILED: *apptag = RPC_ERR_APPTAG_MUST; return RPC_ERR_OPERATION_FAILED; /* 13.4 */ case ERR_NCX_DATA_REST_VIOLATION: *apptag = RPC_ERR_APPTAG_DATA_REST; return RPC_ERR_INVALID_VALUE; /* obsolete */ case ERR_NCX_INSERT_MISSING_INSTANCE: *apptag = RPC_ERR_APPTAG_INSERT; return RPC_ERR_BAD_ATTRIBUTE; /* 13.7 */ case ERR_NCX_NOT_CONFIG: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_INVALID_CONDITIONAL: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_USING_OBSOLETE: *apptag = RPC_ERR_APPTAG_GEN_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_INVALID_AUGTARGET: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_DUP_REFINE_STMT: *apptag = RPC_ERR_APPTAG_DUPLICATE_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_INVALID_DEV_STMT: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_INVALID_XPATH_EXPR: case ERR_NCX_INVALID_INSTANCEID: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_MISSING_INSTANCE: /* not used */ *apptag = RPC_ERR_APPTAG_DATA_INCOMPLETE; return RPC_ERR_DATA_MISSING; case ERR_NCX_UNEXPECTED_INSERT_ATTRS: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_INVALID_UNIQUE_NODE: case ERR_NCX_INVALID_DUP_IMPORT: case ERR_NCX_INVALID_DUP_INCLUDE: case ERR_NCX_AMBIGUOUS_CMD: case ERR_NCX_UNKNOWN_MODULE: case ERR_NCX_UNKNOWN_VERSION: case ERR_NCX_VALUE_NOT_SUPPORTED: case ERR_NCX_LEAFREF_LOOP: case ERR_NCX_VAR_NOT_FOUND: case ERR_NCX_VAR_READ_ONLY: case ERR_NCX_DEC64_BASEOVFL: case ERR_NCX_DEC64_FRACOVFL: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_RPC_WHEN_FAILED: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_NO_MATCHES: *apptag = RPC_ERR_APPTAG_NO_MATCHES; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_MISSING_REFTARGET: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_CANDIDATE_DIRTY: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_RESOURCE_DENIED; case ERR_NCX_TIMEOUT: *apptag = RPC_ERR_APPTAG_LIMIT_REACHED; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_GET_SCHEMA_DUPLICATES: *apptag = RPC_ERR_APPTAG_DATA_NOT_UNIQUE; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_XPATH_NOT_NODESET: *apptag = RPC_ERR_APPTAG_NOT_NODESET; return RPC_ERR_INVALID_VALUE; case ERR_NCX_XPATH_NODESET_EMPTY: *apptag = RPC_ERR_APPTAG_NO_MATCHES; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_IN_USE_LOCKED: *apptag = RPC_ERR_APPTAG_LOCKED; return RPC_ERR_IN_USE; case ERR_NCX_IN_USE_COMMIT: *apptag = RPC_ERR_APPTAG_COMMIT; return RPC_ERR_IN_USE; case ERR_NCX_SUBMOD_NOT_LOADED: *apptag = RPC_ERR_APPTAG_GEN_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_ACCESS_READ_ONLY: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_ACCESS_DENIED; case ERR_NCX_CONFIG_NOT_TARGET: *apptag = RPC_ERR_APPTAG_NO_ACCESS; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_MISSING_RBRACE: *apptag = RPC_ERR_APPTAG_GEN_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_INVALID_FRAMING: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_MALFORMED_MESSAGE; case ERR_NCX_PROTO11_NOT_ENABLED: *apptag = RPC_ERR_APPTAG_NO_SUPPORT; return (isel) ? RPC_ERR_UNKNOWN_ELEMENT : RPC_ERR_UNKNOWN_ATTRIBUTE; case ERR_NCX_CC_NOT_ACTIVE: *apptag = RPC_ERR_APPTAG_GEN_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_MULTIPLE_MATCHES: *apptag = RPC_ERR_APPTAG_GEN_ERROR; return RPC_ERR_OPERATION_FAILED; case ERR_NCX_NO_DEFAULT: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_INVALID_VALUE; case ERR_NCX_MISSING_KEY: *apptag = RPC_ERR_APPTAG_DATA_INVALID; return RPC_ERR_MISSING_ELEMENT; case ERR_NCX_TOP_LEVEL_MANDATORY_FAILED: *apptag = RPC_ERR_APPTAG_GEN_ERROR; return RPC_ERR_OPERATION_FAILED; /* user warnings start at 400 and do not need to be listed here */ default: return RPC_ERR_OPERATION_FAILED; } /*NOTREACHED*/ } /* get_rpcerr */ /** * Create a new error information structure and add it to an error * * /param err pointer to the error to which the error info should be added * /param name_nsid the namespace id * /param name the error name * /param isqname val_nsid + strval == QName * /param val_btype the type of error information content * /param val_nsid the namespace id value * /param badns an invalid namespace string (NULL if not required) * /param strval the string error content (NULL if not required) * /param interr pointer to the number error content (NULL if not required) * * /return the status of the operation ********************************************************************/ static status_t enque_error_info (rpc_err_rec_t* err, xmlns_id_t name_nsid, const xmlChar* name, boolean isqname, ncx_btype_t val_btype, xmlns_id_t val_nsid, const xmlChar* badns, const xmlChar* strval, status_t* interr) { /* no value for union */ if (!strval && !interr) { return ERR_INTERNAL_MEM; } rpc_err_info_t* errinfo = rpc_err_new_info(); if (!errinfo) { return ERR_INTERNAL_MEM; } /* generate bad-attribute value */ errinfo->name_nsid = name_nsid; errinfo->name = name; errinfo->isqname = isqname; errinfo->val_btype = val_btype; errinfo->val_nsid = val_nsid; if (badns) { errinfo->badns = xml_strdup(badns); } if (interr) { errinfo->v.numval.u = (uint32)(*interr); } if (strval) { errinfo->dval = xml_strdup(strval); errinfo->v.strval = errinfo->dval; } dlq_enque(errinfo, &err->error_info); return NO_ERR; } /* enque_error_info */ /******************************************************************** * FUNCTION add_base_vars * * Malloc val_value_t structs, fill them in and add them to * the err->error_info Q, for the basic vars that are * generated for specific error-tag values. * * INPUTS: * err == rpc_err_rec_t in progress * rpcerr == error-tag internal error code * errnode == XML node that caused the error (or NULL) * badval == bad_value string (or NULL) * badns == bad-namespace string (or NULL) * badnsid1 == bad NSID #1 * badnsid2 == bad NSID #2 * errparm1 == void * to the 1st extra parameter to use (per errcode) * == NULL if not use * errparm2 == void * to the 2nd extra parameter to use (per errcode) * == NULL if not used * !!! errparm3 removed !!! * errparm4 == void * to the 4th extra parameter to use (per errcode) * == NULL if not used * RETURNS: * status *********************************************************************/ static status_t add_base_vars (rpc_err_rec_t *err, rpc_err_t rpcerr, const xml_node_t *errnode, const xmlChar *badval, const xmlChar *badns, xmlns_id_t badnsid1, xmlns_id_t badnsid2, const void *errparm1, const void *errparm2, const void *errparm4) { status_t res = NO_ERR; const xmlChar *badel; const uint32 *numptr; ses_id_t sesid; boolean attrerr = FALSE; xmlns_id_t badid; /* figure out the required error-info */ switch (rpcerr) { case RPC_ERR_UNKNOWN_ATTRIBUTE: case RPC_ERR_MISSING_ATTRIBUTE: case RPC_ERR_BAD_ATTRIBUTE: /* badnsid1 == error attribute namespace ID * errparm2 == error attribute name */ if (errparm2) { res = enque_error_info(err, xmlns_nc_id(), NCX_EL_BAD_ATTRIBUTE, TRUE, NCX_BT_STRING, badnsid1, (badnsid1 == xmlns_inv_id()) ? badns : NULL, (const xmlChar *)errparm2, NULL); if (res != NO_ERR) { return res; } } /* else internal error already recorded */ attrerr = TRUE; /* fall through */ case RPC_ERR_UNKNOWN_ELEMENT: case RPC_ERR_MISSING_ELEMENT: case RPC_ERR_BAD_ELEMENT: case RPC_ERR_UNKNOWN_NAMESPACE: /* check mandatory param for this rpcerr */ if (errnode) { badid = errnode->nsid; badel = errnode->elname; } else if (attrerr) { badid = badnsid2; badel = (const xmlChar *)errparm4; } else { badid = badnsid1; badel = (const xmlChar *)errparm2; } /* generate bad-element value */ if (badel) { res = enque_error_info(err, xmlns_nc_id(), NCX_EL_BAD_ELEMENT, TRUE, NCX_BT_STRING, badid, (badid == xmlns_inv_id()) ? badns : NULL, badel, NULL); if (res != NO_ERR) { return res; } } else { SET_ERROR(ERR_INTERNAL_VAL); } if (rpcerr == RPC_ERR_UNKNOWN_NAMESPACE) { if (badns) { res = enque_error_info(err, xmlns_nc_id(), NCX_EL_BAD_NAMESPACE, FALSE, NCX_BT_STRING, 0, NULL, badns, NULL); if (res != NO_ERR) { return res; } } else { SET_ERROR(ERR_INTERNAL_VAL); } } break; case RPC_ERR_LOCK_DENIED: /* generate session-id value */ numptr = (const uint32 *)errparm1; if (numptr != NULL) { sesid = *numptr; } else { SET_ERROR(ERR_INTERNAL_VAL); sesid = 0; } res = enque_error_info(err, xmlns_nc_id(), NCX_EL_SESSION_ID, FALSE, NCX_BT_UINT32, 0, NULL, NULL, &sesid); if (res != NO_ERR) { return res; } break; case RPC_ERR_DATA_MISSING: if (errparm2 && !badval) { /* expecting the obj_template_t of the missing choice */ res = enque_error_info(err, xmlns_yang_id(), (const xmlChar *)"missing-choice", FALSE, NCX_BT_STRING, 0, NULL, (const xmlChar *)errparm2, NULL); if (res != NO_ERR) { return res; } } break; default: ; /* all other rpc-err_t enums handled elsewhere */ } /* generate NCX extension bad-value */ if (badval) { res = enque_error_info(err, xmlns_ncx_id(), NCX_EL_BAD_VALUE, FALSE, NCX_BT_STRING, 0, NULL, badval, NULL); if (res != NO_ERR) { return res; } } return NO_ERR; } /* add_base_vars */ /******************************************************************** * FUNCTION add_error_number * * Malloc a val_value_t struct, fill it in and add it to * the err->error_info Q, for the n * error info element * * INPUTS: * err == rpc_err_rec_t in progress * interr == internal error number to use * * OUTPUTS: * err->errinfoQ has a data node added if NO_ERR * * RETURNS: * status *********************************************************************/ static status_t add_error_number (rpc_err_rec_t *err, status_t interr) { status_t res = enque_error_info(err, xmlns_ncx_id(), NCX_EL_ERROR_NUMBER, FALSE, NCX_BT_UINT32, 0, NULL, NULL, &interr); return res; } /* add_error_number */ /******************************************************************** * FUNCTION agt_rpc_gen_error * * Generate an internal record for an element * (or non-attribute) related error for any layer. * * INPUTS: * layer == protocol layer where the error occurred * interr == internal error code * if NO_ERR than use the rpcerr only * errnode == XML node where error occurred * == NULL then there is no valid XML node (maybe the error!) * parmtyp == type of node contained in error_parm * error_parm == pointer to the extra parameter expected for * this type of error. * * == (void *)pointer to session_id for lock-denied errors * == (void *) pointer to the bad-value string to use * for some other errors * * error_path == malloced string of the value (or type, etc.) instance * ID string in NCX_IFMT_XPATH format; this will be added * to the rpc_err_rec_t and freed later * == NULL if not available * * RETURNS: * pointer to allocated and filled in rpc_err_rec_t struct * ready to add to the msg->rpc_errQ * NULL if a record could not be allocated or not enough * val;id info in the parameters *********************************************************************/ rpc_err_rec_t * agt_rpcerr_gen_error (ncx_layer_t layer, status_t interr, const xml_node_t *errnode, ncx_node_t parmtyp, const void *error_parm, xmlChar *error_path) { return agt_rpcerr_gen_error_ex(layer, interr, errnode, parmtyp, error_parm, error_path, NULL, NCX_NT_NONE, NULL); } /* agt_rpcerr_gen_error */ /******************************************************************** * FUNCTION agt_rpc_gen_error_errinfo * * Generate an internal record for an element * * INPUTS: * layer == protocol layer where the error occurred * interr == internal error code * if NO_ERR than use the rpcerr only * errnode == XML node where error occurred * == NULL then there is no valid XML node (maybe the error!) * parmtyp == type of node contained in error_parm * error_parm == pointer to the extra parameter expected for * this type of error. * * == (void *)pointer to session_id for lock-denied errors * == (void *) pointer to the bad-value string to use * for some other errors * * error_path == malloced string of the value (or type, etc.) instance * ID string in NCX_IFMT_XPATH format; this will be added * to the rpc_err_rec_t and freed later * == NULL if not available * errinfo == error info struct to use for whatever fields are set * * RETURNS: * pointer to allocated and filled in rpc_err_rec_t struct * ready to add to the msg->rpc_errQ * NULL if a record could not be allocated or not enough * val;id info in the parameters *********************************************************************/ rpc_err_rec_t * agt_rpcerr_gen_error_errinfo (ncx_layer_t layer, status_t interr, const xml_node_t *errnode, ncx_node_t parmtyp, const void *error_parm, xmlChar *error_path, const ncx_errinfo_t *errinfo) { return agt_rpcerr_gen_error_ex(layer, interr, errnode, parmtyp, error_parm, error_path, errinfo, NCX_NT_NONE, NULL); } /* agt_rpcerr_gen_error_errinfo */ /** * Set the values of an error record structure * * /param err pointer to the error to set * /param error_res the error result * /param error_id the error id * /param error_type the error type * /param error_severity the error severity * /param error_tag the error tag * /param error_app_tag the error application tag * /param error_path the error path * /param error_message the error message (may be NULL) ********************************************************************/ static void set_error_record (rpc_err_rec_t* err, status_t error_res, rpc_err_t error_id, ncx_layer_t error_type, rpc_err_sev_t error_severity, const xmlChar* error_tag, const xmlChar* error_app_tag, xmlChar* error_path, const xmlChar* error_message) { err->error_res = error_res; err->error_id = error_id; err->error_type = error_type; err->error_severity = error_severity; err->error_tag = error_tag; err->error_app_tag = error_app_tag; err->error_path = error_path; err->error_message = (error_message) ? xml_strdup(error_message) : NULL; err->error_message_lang = NCX_DEF_LANG; } /* set_error_record */ /******************************************************************** * FUNCTION agt_rpc_gen_error_ex * * Generate an internal record for an element * * INPUTS: * layer == protocol layer where the error occurred * interr == internal error code * if NO_ERR than use the rpcerr only * errnode == XML node where error occurred * == NULL then there is no valid XML node (maybe the error!) * parmtyp == type of node contained in error_parm * error_parm == pointer to the extra parameter expected for * this type of error. * * == (void *)pointer to session_id for lock-denied errors * == (void *) pointer to the bad-value string to use * for some other errors * * error_path == malloced string of the value (or type, etc.) instance * ID string in NCX_IFMT_XPATH format; this will be added * to the rpc_err_rec_t and freed later * == NULL if not available * errinfo == error info struct to use for whatever fields are set * nodetyp == type of node contained in error_path_raw * error_path_raw == pointer to the extra parameter expected for * this type of error. * * RETURNS: * pointer to allocated and filled in rpc_err_rec_t struct * ready to add to the msg->rpc_errQ * NULL if a record could not be allocated or not enough * val;id info in the parameters *********************************************************************/ rpc_err_rec_t * agt_rpcerr_gen_error_ex (ncx_layer_t layer, status_t interr, const xml_node_t *errnode, ncx_node_t parmtyp, const void *error_parm, xmlChar *error_path, const ncx_errinfo_t *errinfo, ncx_node_t nodetyp, const void *error_path_raw) { rpc_err_rec_t *err; const obj_template_t *parm, *in, *obj; const xmlns_qname_t *qname; const val_value_t *valparm; const xmlChar *badval, *badns, *msg, *apptag; const void *err1, *err2, *err4; const cfg_template_t *badcfg; rpc_err_t rpcerr; rpc_err_sev_t errsev; xmlns_id_t badnsid1, badnsid2; boolean just_errnum; /* get a new error record */ err = rpc_err_new_record(); if (!err) { return NULL; } parm = NULL; in = NULL; obj = NULL; qname = NULL; valparm = NULL; badval = NULL; badns = NULL; msg = NULL; apptag = NULL; err1 = NULL; err2 = NULL; err4 = NULL; errsev = RPC_ERR_SEV_NONE; badnsid1 = 0; badnsid2 = 0; just_errnum = FALSE; switch (interr) { case ERR_NCX_MISSING_PARM: case ERR_NCX_EXTRA_CHOICE: case ERR_NCX_MISSING_CHOICE: case ERR_NCX_MISSING_KEY: case ERR_NCX_RPC_WHEN_FAILED: if (!error_parm) { SET_ERROR(ERR_INTERNAL_PTR); } else { switch (parmtyp) { case NCX_NT_OBJ: parm = (const obj_template_t *)error_parm; if (parm) { badnsid1 = obj_get_nsid(parm); err2 = (const void *)obj_get_name(parm); } break; case NCX_NT_VAL: valparm = (const val_value_t *)error_parm; if (valparm) { badnsid1 = val_get_nsid(valparm); err2 = (const void *)valparm->name; } break; case NCX_NT_STRING: /* FIXME: setting this to 0 may cause a * SET_ERROR when the XML element is printed * because the error-info 'bad-element' * is supposed to be a QName and nsid must != 0 * TBD: redo this whole error API!!! * but first just pass in the NSID of the bad-element */ badnsid1 = 0; err2 = (const void *)error_parm; break; default: SET_ERROR(ERR_INTERNAL_VAL); } } break; case ERR_NCX_LOCK_DENIED: if (!error_parm) { SET_ERROR(ERR_INTERNAL_VAL); } else { if (parmtyp == NCX_NT_CFG) { badcfg = (const cfg_template_t *)error_parm; if (badcfg != NULL) { err1 = &badcfg->locked_by; } else { SET_ERROR(ERR_INTERNAL_VAL); } } else if (parmtyp == NCX_NT_UINT32_PTR) { err1 = error_parm; } else { SET_ERROR(ERR_INTERNAL_VAL); } } break; case ERR_NCX_MISSING_INDEX: if (!error_parm || parmtyp != NCX_NT_OBJ) { SET_ERROR(ERR_INTERNAL_VAL); } else { in = (const obj_template_t *)error_parm; if (in) { badnsid1 = obj_get_nsid(in); err2 = (const void *)obj_get_name(in); } } break; case ERR_NCX_DEF_NOT_FOUND: if (parmtyp == NCX_NT_STRING && error_parm) { err1 = error_parm; } else if (parmtyp == NCX_NT_VAL && error_parm) { valparm = (const val_value_t *)error_parm; if (valparm) { badnsid1 = val_get_nsid(valparm); err2 = (const void *)valparm->name; } } break; case ERR_NCX_MISSING_ATTR: case ERR_NCX_EXTRA_ATTR: case ERR_NCX_MISSING_VAL_INST: case ERR_NCX_EXTRA_VAL_INST: /* this hack is needed because this error is generated * after the xml_attr_t record is gone * * First set the bad-attribute NS and name */ if (error_parm) { if (parmtyp == NCX_NT_QNAME) { qname = (const xmlns_qname_t *)error_parm; badnsid1 = qname->nsid; err2 = (const void *)qname->name; } else if (parmtyp == NCX_NT_OBJ) { obj = (const obj_template_t *)error_parm; badnsid1 = obj_get_nsid(obj); err2 = (const void *)obj_get_name(obj); } else if (parmtyp == NCX_NT_STRING) { badnsid1 = 0; err2 = (const void *)error_parm; } else { SET_ERROR(ERR_INTERNAL_VAL); } } else { SET_ERROR(ERR_INTERNAL_VAL); } /* hack: borrow the errnode pointer to use as a string * for the bad-element name */ if (errnode) { if (qname) { badnsid2 = qname->nsid; } err4 = (const void *)errnode; /* make sure add_base_vars doesn't use as an xml_node_t */ errnode = NULL; } else if (error_path_raw) { if (nodetyp == NCX_NT_VAL) { /* element nsid:name for the attribute error */ valparm = (const val_value_t *)error_path_raw; badnsid2 = val_get_nsid(valparm); err4 = valparm->name; } } break; default: break; } rpcerr = get_rpcerr(interr, TRUE, &errsev, &apptag); /* generate a default error message, and continue anyway * if the xml_strdup fails with a malloc error */ if (errinfo && errinfo->error_message) { msg = errinfo->error_message; } else { msg = (const xmlChar *)get_error_string(interr); } if (errinfo && errinfo->error_app_tag) { apptag = errinfo->error_app_tag; } /* setup the return record */ set_error_record(err, interr, rpcerr, layer, errsev, rpc_err_get_errtag(rpcerr), apptag, error_path, msg); /* figure out the required error-info * It is possible for an attribute error to be recorded * through this function due to errors detected after * the xml_node_t and xml_attr_t structs have been discarded */ switch (rpcerr) { case RPC_ERR_MISSING_ELEMENT: case RPC_ERR_UNKNOWN_ELEMENT: case RPC_ERR_LOCK_DENIED: case RPC_ERR_MISSING_ATTRIBUTE: case RPC_ERR_BAD_ATTRIBUTE: case RPC_ERR_UNKNOWN_ATTRIBUTE: break; case RPC_ERR_BAD_ELEMENT: if (parmtyp==NCX_NT_STRING) { badval = (const xmlChar *)error_parm; } break; case RPC_ERR_UNKNOWN_NAMESPACE: if (parmtyp==NCX_NT_STRING) { badns = (const xmlChar *)error_parm; } break; case RPC_ERR_DATA_MISSING: if (interr == ERR_NCX_MISSING_CHOICE) { badval = NULL; } else if (interr == ERR_NCX_MISSING_VAL_INST) { if (obj) { badval = obj_get_name(obj); } } else { just_errnum = TRUE; } break; default: if (error_parm && parmtyp==NCX_NT_STRING) { badval = (const xmlChar *)error_parm; } else { just_errnum = TRUE; } } if (!just_errnum) { /* add the required error-info, call even if err2 is NULL */ /* ignore result and use err anyway */ add_base_vars(err, rpcerr, errnode, badval, badns, badnsid1, badnsid2, err1, err2, err4); } /* ignore result and use err anyway */ add_error_number(err, interr); return err; } /* agt_rpcerr_gen_error_ex */ /******************************************************************** * FUNCTION agt_rpc_gen_insert_error * * Generate an internal record for an element * for an insert operation failed error * * INPUTS: * layer == protocol layer where the error occurred * interr == internal error code * if NO_ERR than use the rpcerr only * errval == pointer to the node with the insert error * error_path == malloced string of the value (or type, etc.) instance * ID string in NCX_IFMT_XPATH format; this will be added * to the rpc_err_rec_t and freed later * == NULL if not available * * RETURNS: * pointer to allocated and filled in rpc_err_rec_t struct * ready to add to the msg->rpc_errQ * NULL if a record could not be allocated or not enough * val;id info in the parameters *********************************************************************/ rpc_err_rec_t * agt_rpcerr_gen_insert_error (ncx_layer_t layer, status_t interr, const val_value_t *errval, xmlChar *error_path) { rpc_err_rec_t *err; const xmlChar *badval = NULL, *msg; const void *err2 = NULL; assert(errval && "param errval is NULL"); /* get a new error record */ err = rpc_err_new_record(); if (!err) { return NULL; } msg = (const xmlChar *)get_error_string(interr); set_error_record(err, interr, RPC_ERR_BAD_ATTRIBUTE, layer, RPC_ERR_SEV_ERROR, rpc_err_get_errtag(RPC_ERR_BAD_ATTRIBUTE), RPC_ERR_APPTAG_INSERT, error_path, msg); if (errval->editvars) { badval = errval->editvars->insertstr; } if (errval->obj->objtype == OBJ_TYP_LIST) { err2 = (const void *)NCX_EL_KEY; } else { err2 = (const void *)NCX_EL_VALUE; } /* add the required error-info, call even if err2 is NULL */ /* ignore result and use err anyway */ add_base_vars(err, RPC_ERR_BAD_ATTRIBUTE, NULL, badval, NULL, xmlns_yang_id(), val_get_nsid(errval), NULL, err2, (const void *)errval->name); /* ignore result and use err anyway */ add_error_number(err, interr); return err; } /* agt_rpcerr_gen_insert_error */ /******************************************************************** * FUNCTION agt_rpc_gen_unique_error * * Generate an internal record for an element * for a unique-stmt failed error (data-not-unique) * * INPUTS: * msghdr == message header to use for prefix storage * layer == protocol layer where the error occurred * interr == internal error code * if NO_ERR than use the rpcerr only * errval == pointer to the node with the insert error * valuniqueQ == Q of val_unique_t structs to * use for elements * error_path == malloced string of the value (or type, etc.) instance * ID string in NCX_IFMT_XPATH format; this will be added * to the rpc_err_rec_t and freed later * == NULL if not available * * RETURNS: * pointer to allocated and filled in rpc_err_rec_t struct * ready to add to the msg->rpc_errQ * NULL if a record could not be allocated or not enough * val;id info in the parameters *********************************************************************/ rpc_err_rec_t * agt_rpcerr_gen_unique_error (xml_msg_hdr_t *msghdr, ncx_layer_t layer, status_t interr, const dlq_hdr_t *valuniqueQ, xmlChar *error_path) { rpc_err_rec_t *err; xmlChar *pathbuff; const xmlChar *msg; val_unique_t *unival; status_t res; /* get a new error record */ err = rpc_err_new_record(); if (!err) { return NULL; } msg = (const xmlChar *)get_error_string(interr); /* setup the return record */ set_error_record(err, interr, RPC_ERR_OPERATION_FAILED, layer, RPC_ERR_SEV_ERROR, rpc_err_get_errtag(RPC_ERR_OPERATION_FAILED), RPC_ERR_APPTAG_UNIQUE_FAILED, error_path, msg); for (unival = (val_unique_t *)dlq_firstEntry(valuniqueQ); unival != NULL; unival = (val_unique_t *)dlq_nextEntry(unival)) { pathbuff = NULL; xpath_resnode_t *resnode = xpath_get_first_resnode(unival->pcb->result); if (resnode == NULL) { // should not happen! continue; } val_value_t *valptr = xpath_get_resnode_valptr(resnode); if (valptr == NULL) { // should not happen! continue; } res = val_gen_instance_id(msghdr, valptr, NCX_IFMT_XPATH1, &pathbuff); if (res == NO_ERR) { res = enque_error_info(err, xmlns_yang_id(), NCX_EL_NON_UNIQUE, FALSE, NCX_BT_INSTANCE_ID, 0, NULL, pathbuff, NULL); } if (pathbuff) { m__free(pathbuff); } if (res != NO_ERR) { log_error("\nError: could not add unique-error info"); } } /* ignore result and use err anyway */ add_error_number(err, interr); return err; } /* agt_rpcerr_gen_unique_error */ /******************************************************************** * FUNCTION agt_rpc_gen_attr_error * * Generate an internal record for an attribute * * INPUTS: * layer == protocol layer where the error occurred * interr == internal error code * if NO_ERR than use the rpcerr only * attr == attribute that had the error * errnode == XML node where error occurred * == NULL then there is no valid XML node (maybe the error!) * errnodeval == valuse struct for the error node id errnode NULL * == NULL if not used * badns == URI string of the namespace that is bad (or NULL) * * RETURNS: * pointer to allocated and filled in rpc_err_rec_t struct * ready to add to the msg->rpc_errQ (or add more error-info) * NULL if a record could not be allocated or not enough * valid info in the parameters to generate an error report *********************************************************************/ rpc_err_rec_t * agt_rpcerr_gen_attr_error (ncx_layer_t layer, status_t interr, const xml_attr_t *attr, const xml_node_t *errnode, const val_value_t *errnodeval, const xmlChar *badns, xmlChar *error_path) { rpc_err_rec_t *err; rpc_err_sev_t errsev; rpc_err_t rpcerr; xmlChar *badval = NULL; const void *err2 = NULL, *err4 = NULL; const xmlChar *msg, *apptag = NULL; xmlns_id_t badnsid1 = 0, badnsid2 = 0; /* get a new error record */ err = rpc_err_new_record(); if (!err) { return NULL; } if (attr) { badnsid1 = attr->attr_ns; err2 = (const void *)attr->attr_name; } if (errnode == NULL && errnodeval != NULL) { badnsid2 = val_get_nsid(errnodeval); err4 = errnodeval->name; } rpcerr = get_rpcerr(interr, FALSE, &errsev, &apptag); /* check the rpcerr out the required error-info */ switch (rpcerr) { case RPC_ERR_UNKNOWN_ATTRIBUTE: case RPC_ERR_MISSING_ATTRIBUTE: case RPC_ERR_UNKNOWN_NAMESPACE: break; case RPC_ERR_INVALID_VALUE: rpcerr = RPC_ERR_BAD_ATTRIBUTE; /* fall through */ case RPC_ERR_BAD_ATTRIBUTE: if (attr) { badval = attr->attr_val; } break; default: SET_ERROR(ERR_INTERNAL_VAL); } /* generate a default error message */ msg = (const xmlChar *)get_error_string(interr); set_error_record(err, interr, rpcerr, layer, errsev, rpc_err_get_errtag(rpcerr), apptag, error_path, msg); /* add the required error-info */ /* ignore result and use err anyway */ add_base_vars(err, rpcerr, errnode, badval, badns, badnsid1, badnsid2, NULL, err2, err4); /* ignore result and use err anyway */ add_error_number(err, interr); return err; } /* agt_rpcerr_gen_attr_error */ /* END file agt_rpcerr.c */ yuma123_2.14/netconf/src/agt/Makefile.am0000664000175000017500000000355014770023131020160 0ustar vladimirvladimirlib_LTLIBRARIES = libyumaagt.la libyumaagt_la_SOURCES = \ $(top_srcdir)/netconf/src/agt/agt_acm.c \ $(top_srcdir)/netconf/src/agt/agt.c \ $(top_srcdir)/netconf/src/agt/agt_cap.c \ $(top_srcdir)/netconf/src/agt/agt_cb.c \ $(top_srcdir)/netconf/src/agt/agt_cli.c \ $(top_srcdir)/netconf/src/agt/agt_connect.c \ $(top_srcdir)/netconf/src/agt/agt_hello.c \ $(top_srcdir)/netconf/src/agt/agt_ncx.c \ $(top_srcdir)/netconf/src/agt/agt_ncxserver.c \ $(top_srcdir)/netconf/src/agt/agt_nmda.c \ $(top_srcdir)/netconf/src/agt/agt_not.c \ $(top_srcdir)/netconf/src/agt/agt_plock.c \ $(top_srcdir)/netconf/src/agt/agt_proc.c \ $(top_srcdir)/netconf/src/agt/agt_rpc.c \ $(top_srcdir)/netconf/src/agt/agt_rpcerr.c \ $(top_srcdir)/netconf/src/agt/agt_ses.c \ $(top_srcdir)/netconf/src/agt/agt_signal.c \ $(top_srcdir)/netconf/src/agt/agt_state.c \ $(top_srcdir)/netconf/src/agt/agt_yang_library.c \ $(top_srcdir)/netconf/src/agt/agt_sys.c \ $(top_srcdir)/netconf/src/agt/agt_time_filter.c \ $(top_srcdir)/netconf/src/agt/agt_timer.c \ $(top_srcdir)/netconf/src/agt/agt_top.c \ $(top_srcdir)/netconf/src/agt/agt_tree.c \ $(top_srcdir)/netconf/src/agt/agt_util.c \ $(top_srcdir)/netconf/src/agt/agt_val.c \ $(top_srcdir)/netconf/src/agt/agt_val_parse.c \ $(top_srcdir)/netconf/src/agt/agt_xml.c \ $(top_srcdir)/netconf/src/agt/agt_xpath.c \ $(top_srcdir)/netconf/src/agt/agt_cfg.c \ $(top_srcdir)/netconf/src/agt/agt_commit_complete.c \ $(top_srcdir)/netconf/src/agt/agt_commit_validate.c \ $(top_srcdir)/netconf/src/agt/agt_not_queue_notification_cb.c libyumaagt_la_CPPFLAGS = -DDISABLE_YUMA_INTERFACES -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump $(XML_CPPFLAGS) libyumaagt_la_LDFLAGS = -version-info 2:0:0 $(top_builddir)/netconf/src/ncx/libyumancx.la $(XML_LIBS) -lcrypto -lrt $(LIBS) yuma123_2.14/netconf/src/agt/agt_cb.h0000664000175000017500000001611414770023131017514 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_cb #define _H_agt_cb /* FILE: agt_cb.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Server Data Model callback handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 16-apr-07 abb Begun; split out from agt_ps.h 01-aug-08 abb Remove NCX specific stuff; YANG only now */ #include "agt.h" #include "op.h" #include "rpc.h" #include "ses.h" #include "status.h" #include "val.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* symbolic tags for agt_cb_register_callback 'forall' boolean */ #define FORALL TRUE #define FORONE FALSE /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* Callback function for server object handler * Used to provide a callback sub-mode for * a specific named object * * INPUTS: * scb == session control block making the request * msg == incoming rpc_msg_t in progress * cbtyp == reason for the callback * editop == the parent edit-config operation type, which * is also used for all other callbacks * that operate on objects * newval == container object holding the proposed changes to * apply to the current config, depending on * the editop value. Will not be NULL. * curval == current container values from the * or configuration, if any. Could be NULL * for create and other operations. * * RETURNS: * status: */ typedef status_t (*agt_cb_fn_t) (ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval); /* set of server object callback functions */ typedef struct agt_cb_fnset_t_ { agt_cb_fn_t cbfn[AGT_NUM_CB]; } agt_cb_fnset_t; typedef struct agt_cb_fnset_node_t_ { dlq_hdr_t qhdr; agt_cb_fnset_t* fnset_ptr; } agt_cb_fnset_node_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_cb_init * * Init the server callback module * *********************************************************************/ extern void agt_cb_init (void); /******************************************************************** * FUNCTION agt_cb_cleanup * * Cleanup the server callback module * *********************************************************************/ extern void agt_cb_cleanup (void); /******************************************************************** * FUNCTION agt_cb_register_callback * * Register an object specific callback function * use the same fn for all callback phases * all phases will be invoked * * * INPUTS: * modname == module that defines the target object for * these callback functions * defpath == Xpath with default (or no) prefixes * defining the object that will get the callbacks * version == exact module revision date expected * if condition not met then an error will * be logged (TBD: force unload of module!) * == NULL means use any version of the module * cbfn == address of callback function to use for * all callback phases * * RETURNS: * status *********************************************************************/ extern status_t agt_cb_register_callback (const xmlChar *modname, const xmlChar *defpath, const xmlChar *version, const agt_cb_fn_t cbfn); /******************************************************************** * FUNCTION agt_cb_register_callbacks * !!!DEPRECATED - breaks multiple callbacks per obj id * Use agt_cb_register_callback instead. * * Register an object specific callback function * setup array of callbacks, could be different or NULL * to skip that phase * * INPUTS: * modname == module that defines the target object for * these callback functions * defpath == Xpath with default (or no) prefixes * defining the object that will get the callbacks * version == exact module revision date expected * if condition not met then an error will * be logged (TBD: force unload of module!) * == NULL means use any version of the module * cbfnset == address of callback function set to copy * * RETURNS: * status *********************************************************************/ extern status_t agt_cb_register_callbacks (const xmlChar *modname, const xmlChar *defpath, const xmlChar *version, const agt_cb_fnset_t *cbfnset); /******************************************************************** * FUNCTION agt_cb_unregister_callbacks * !!!DEPRECATED - breaks multiple callbacks per obj id * Use agt_cb_unregister_callbacks instead. * * * Unregister all callback functions for a specific object * * INPUTS: * modname == module containing the object for this callback * defpath == definition XPath location * * RETURNS: * none *********************************************************************/ extern void agt_cb_unregister_callbacks (const xmlChar *modname, const xmlChar *defpath); /******************************************************************** * FUNCTION agt_cb_unregister_callback * * Unregister all callback functions for a specific object with mathching * callback function pointers. * * INPUTS: * modname == module containing the object for this callback * defpath == definition XPath location * cbfn == pointer to registered callback function, if NULL will remove * the first matching callback * * RETURNS: * none *********************************************************************/ extern void agt_cb_unregister_callback(const xmlChar *modname, const xmlChar *defpath, const agt_cb_fn_t cbfn); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_cb */ yuma123_2.14/netconf/src/agt/agt_if.c0000664000175000017500000005223114770023131017521 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_if.c interfaces module ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 18jul09 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_if.h" #include "agt_rpc.h" #include "agt_util.h" #include "cfg.h" #include "getcb.h" #include "log.h" #include "ncxmod.h" #include "ncxtypes.h" #include "rpc.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define interfaces_MOD (const xmlChar *)"yuma-interfaces" #define interfaces_MOD_REV NULL #define interfaces_N_interfaces (const xmlChar *)"yuma-interfaces" #define interfaces_N_interface (const xmlChar *)"interface" #define interfaces_N_name (const xmlChar *)"name" #define interfaces_N_counters (const xmlChar *)"counters" #define interfaces_OID_counters (const xmlChar *)\ "/interfaces/interface/counters" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_if_init_done = FALSE; static boolean agt_if_not_supported; static ncx_module_t *ifmod; /******************************************************************** * FUNCTION is_interfaces_supported * * Check if at least /proc/net/dev exists * * RETURNS: * TRUE if minimum if support found * FALSE if minimum if support not found *********************************************************************/ static boolean is_interfaces_supported (void) { struct stat statbuf; int ret; memset(&statbuf, 0x0, sizeof(statbuf)); ret = stat("/proc/net/dev", &statbuf); if (ret == 0 && S_ISREG(statbuf.st_mode)) { return TRUE; } return FALSE; } /* is_interfaces_supported */ /******************************************************************** * FUNCTION get_ifname_string * * Get the name field from the /proc/net/dev line * * INPUTS: * buffer == line from the /proc/net/dev file to read * nameptr == address of return name string pointer * namelen == address of return name string length * * OUTPUTS: * *nameptr set to the start of the name string * *namelen == name string length * * RETURNS: * status * NO_ERR if outputs filled in OK * some other error like ERR_INTERNAL_MEM *********************************************************************/ static status_t get_ifname_string (xmlChar *buffer, xmlChar **nameptr, int *namelen) { xmlChar *name, *str; *nameptr = NULL; *namelen = 0; /* get the start of the interface name */ str = buffer; while (*str && xml_isspace(*str)) { str++; } if (*str == '\0') { /* not expecting a line with just whitespace on it */ return ERR_NCX_SKIPPED; } else { name = str++; *nameptr = name; } /* get the end of the interface name */ while (*str && *str != ':') { str++; } if (*str == '\0') { /* not expecting a line with just foo on it */ return ERR_NCX_SKIPPED; } *namelen = (str - name); return NO_ERR; } /* get_ifname_string */ /******************************************************************** * FUNCTION find_interface_entry * * Get the specified internface entry * * INPUTS: * interfacesval == parent entry to check * nameptr == name string * namelen == name length * * RETURNS: * pointer to the found entry or NULL if not found *********************************************************************/ static val_value_t * find_interface_entry (val_value_t *interfacesval, const xmlChar *nameptr, int namelen) { val_value_t *childval, *nameval; for (childval = val_get_first_child(interfacesval); childval != NULL; childval = val_get_next_child(childval)) { nameval = val_find_child(childval, interfaces_MOD, interfaces_N_name); if (nameval == NULL) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } if (!xml_strncmp(VAL_STR(nameval), nameptr, (uint32)namelen) && xml_strlen(VAL_STR(nameval)) == (uint32)namelen) { return childval; } } return NULL; } /* find_interface_entry */ /******************************************************************** * FUNCTION make_interface_entry * * Make the starter interface entry for the specified name * * INPUTS: * interfaceobj == object template to use * nameptr == name string, zero-terminated * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to the new entry or NULL if malloc failed *********************************************************************/ static val_value_t * make_interface_entry (obj_template_t *interfaceobj, xmlChar *nameptr, status_t *res) { obj_template_t *nameobj; val_value_t *interfaceval, *nameval; *res = NO_ERR; nameobj = obj_find_child(interfaceobj, interfaces_MOD, interfaces_N_name); if (nameobj == NULL) { *res = SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return NULL; } interfaceval = val_new_value(); if (interfaceval == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(interfaceval, interfaceobj); nameval = val_make_simval_obj(nameobj, nameptr, res); if (nameval == NULL) { val_free_value(interfaceval); return NULL; } val_add_child(nameval, interfaceval); *res = val_gen_index_chain(interfaceobj, interfaceval); if (*res != NO_ERR) { val_free_value(interfaceval); return NULL; } return interfaceval; } /* make_interface_entry */ /******************************************************************** * FUNCTION fill_if_counters * * operation handler for the interfaces/interface/counters node * * INPUTS: * countersobj == object template with all the child node to use * nameval == value node for the key that is desired * buffer == line from the /proc/net/dev file to read * dstval == destination value to fill in * * OUTPUTS: * child nodes added to dstval->v.childQ if NO_ERR returned * * RETURNS: * status * NO_ERR if this is the right line for the key * and everything filled in OK * ERR_NCX_SKIPPED if this is the wrong line for this * some other error like ERR_INTERNAL_MEM *********************************************************************/ static status_t fill_if_counters (obj_template_t *countersobj, val_value_t *nameval, xmlChar *buffer, val_value_t *dstval) { obj_template_t *childobj; val_value_t *childval; xmlChar *str, *name; char *endptr; status_t res; uint32 leafcount; uint64 counter; boolean done; res = NO_ERR; leafcount = 0; counter = 0; /* get the start of the interface name */ str = buffer; while (*str && isspace(*str)) { str++; } if (*str == '\0') { /* not expecting a line with just whitespace on it */ return ERR_NCX_SKIPPED; } else { name = str++; } /* get the end of the interface name */ while (*str && *str != ':') { str++; } if (*str == '\0') { /* not expecting a line with just foo on it */ return ERR_NCX_SKIPPED; } /* is this the requested interface line? */ if (xml_strncmp((const xmlChar *)name, VAL_STR(nameval), (uint32)(str - name))) { /* not the right interface name */ return ERR_NCX_SKIPPED; } /* get the str pointed at the first byte of the * 16 ordered counter values */ str++; /* get the first counter object ready */ childobj = obj_first_child(countersobj); if (childobj == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* keep getting counters until the line runs out */ done = FALSE; while (!done) { endptr = NULL; counter = strtoull((const char *)str, &endptr, 10); if (counter == 0 && str == (xmlChar *)endptr) { /* number conversion failed */ log_error("\nError: /proc/net/dev number conversion failed"); return ERR_NCX_OPERATION_FAILED; } childval = val_new_value(); if (childval == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(childval, childobj); VAL_ULONG(childval) = counter; val_add_child(childval, dstval); leafcount++; str = (xmlChar *)endptr; if (*str == '\0' || *str == '\n') { done = TRUE; } else { childobj = obj_next_child(childobj); if (childobj == NULL) { done = TRUE; } } } if (LOGDEBUG2) { log_debug2("\nagt_if: filled %u of 16 counters for '%s'", leafcount, VAL_STR(nameval)); } return res; } /* fill_if_counters */ /******************************************************************** * FUNCTION get_if_counters * * operation handler for the interfaces/interface/counters node * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ static status_t get_if_counters (ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *virval, val_value_t *dstval) { FILE *countersfile; obj_template_t *countersobj; val_value_t *parentval, *nameval; xmlChar *buffer, *readtest; boolean done; status_t res; uint32 linecount; (void)scb; res = NO_ERR; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } countersobj = virval->obj; /* get the interface parent entry for this counters entry */ parentval = virval->parent; if (parentval == NULL) { /* the parent should be valid */ return SET_ERROR(ERR_INTERNAL_VAL); } /* get the name key leaf from the parent */ nameval = val_find_child(parentval, interfaces_MOD, interfaces_N_name); if (nameval == NULL) { /* there should be a key present */ return SET_ERROR(ERR_INTERNAL_VAL); } /* open the /proc/net/dev file for reading */ countersfile = fopen("/proc/net/dev", "r"); if (countersfile == NULL) { return errno_to_status(); } /* get a file read line buffer */ buffer = m__getMem(NCX_MAX_LINELEN); if (buffer == NULL) { fclose(countersfile); return ERR_INTERNAL_MEM; } /* loop through the file until done */ res = NO_ERR; done = FALSE; linecount = 0; while (!done) { readtest = (xmlChar *) fgets((char *)buffer, NCX_MAX_LINELEN, countersfile); if (readtest == NULL) { done = TRUE; continue; } else { linecount++; } if (linecount < 3) { /* skip the header junk on the first 2 lines */ continue; } res = fill_if_counters(countersobj, nameval, buffer, dstval); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } else { done = TRUE; } } fclose(countersfile); m__free(buffer); return res; } /* get_if_counters */ /******************************************************************** * FUNCTION add_interface_entries * * make a val_value_t struct for each interface line found * in the /proc/net/dev file * and add it as a child to the specified container value * INPUTS: * interfaacesval == parent value struct to add each * interface list entry * * RETURNS: * status *********************************************************************/ static status_t add_interface_entries (val_value_t *interfacesval) { FILE *countersfile; obj_template_t *interfaceobj, *countersobj; val_value_t *interfaceval, *countersval; xmlChar *buffer, *readtest, *ifname; boolean done; status_t res; uint32 linecount; int ifnamelen; res = NO_ERR; interfaceobj = obj_find_child(interfacesval->obj, interfaces_MOD, interfaces_N_interface); if (interfaceobj == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } countersobj = obj_find_child(interfaceobj, interfaces_MOD, interfaces_N_counters); if (countersobj == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* open the /proc/net/dev file for reading */ countersfile = fopen("/proc/net/dev", "r"); if (countersfile == NULL) { return errno_to_status(); } /* get a file read line buffer */ buffer = m__getMem(NCX_MAX_LINELEN); if (buffer == NULL) { fclose(countersfile); return ERR_INTERNAL_MEM; } /* loop through the file until done */ res = NO_ERR; done = FALSE; linecount = 0; while (!done) { readtest = (xmlChar *) fgets((char *)buffer, NCX_MAX_LINELEN, countersfile); if (readtest == NULL) { done = TRUE; continue; } else { linecount++; } if (linecount < 3) { /* skip the header junk on the first 2 lines */ continue; } ifname = NULL; ifnamelen = 0; res = get_ifname_string(buffer, &ifname, &ifnamelen); if (res != NO_ERR) { done = TRUE; } else { /* got the name string * see if this entry is already present */ interfaceval = find_interface_entry(interfacesval, ifname, ifnamelen); if (interfaceval == NULL) { /* create a new entry */ res = NO_ERR; ifname[ifnamelen] = 0; interfaceval = make_interface_entry(interfaceobj, ifname, &res); if (interfaceval == NULL) { done = TRUE; continue; } else { val_add_child(interfaceval, interfacesval); } } /* add the counters virtual node to the entry */ countersval = val_new_value(); if (countersval == NULL) { res = ERR_INTERNAL_MEM; done = TRUE; continue; } else { val_init_virtual(countersval, get_if_counters, countersobj); val_add_child(countersval, interfaceval); } } } fclose(countersfile); m__free(buffer); return res; } /* add_interface_entries */ /************* E X T E R N A L F U N C T I O N S ***************/ /******************************************************************** * FUNCTION agt_if_init * * INIT 1: * Initialize the interfaces monitor module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t agt_if_init (void) { status_t res; agt_profile_t *agt_profile; if (agt_if_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } log_debug2("\nagt: Loading interfaces module"); ifmod = NULL; agt_if_not_supported = FALSE; agt_if_init_done = TRUE; agt_profile = agt_get_profile(); /* load the yuma-interfaces module */ res = ncxmod_load_module(interfaces_MOD, interfaces_MOD_REV, &agt_profile->agt_savedevQ, &ifmod); /* check if /interfaces file system supported */ if (!is_interfaces_supported()) { if (LOGDEBUG) { log_debug("\nagt_interfaces: no /interfaces support found"); } agt_if_not_supported = TRUE; } return res; } /* agt_if_init */ /******************************************************************** * FUNCTION agt_if_init2 * * INIT 2: * Initialize the monitoring data structures * This must be done after the config is loaded * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t agt_if_init2 (void) { cfg_template_t *runningcfg; obj_template_t *interfacesobj; val_value_t *interfacesval; status_t res; res = NO_ERR; if (!agt_if_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (!runningcfg || !runningcfg->root) { return SET_ERROR(ERR_INTERNAL_VAL); } interfacesobj = obj_find_template_top(ifmod, interfaces_MOD, interfaces_N_interfaces); if (!interfacesobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } if (!agt_if_not_supported) { /* check to see if the /interfaces subtree already exists */ interfacesval = val_find_child(runningcfg->root, interfaces_MOD, interfaces_N_interfaces); if (interfacesval == NULL) { /* need to create the /interfaces root */ interfacesval = val_new_value(); if (interfacesval == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(interfacesval, interfacesobj); /* handing off the malloced memory here */ val_add_child_sorted(interfacesval, runningcfg->root); } res = add_interface_entries(interfacesval); } return res; } /* agt_if_init2 */ /******************************************************************** * FUNCTION agt_if_cleanup * * Cleanup the module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ void agt_if_cleanup (void) { if (agt_if_init_done) { ifmod = NULL; agt_if_init_done = FALSE; } } /* agt_if_cleanup */ /* END file agt_if.c */ yuma123_2.14/netconf/src/agt/agt_top.h0000664000175000017500000000404314770023131017730 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_top #define _H_agt_top /* FILE: agt_top.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Server Top Element module Manage callback registry for received XML messages ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30-dec-05 abb Begun */ #ifndef _H_ses #include "ses.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_top_dispatch_msg * * Find the appropriate top node handler and call it * called by the transport manager (through the session manager) * when a new message is detected * * INPUTS: * scb == session control block containing the xmlreader * set at the start of an incoming message. * * NOTES: * This function might de-allocate the scb, if it does scb will be * set to NULL * * RETURNS: * none *********************************************************************/ extern void agt_top_dispatch_msg (ses_cb_t **scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_top */ yuma123_2.14/netconf/src/agt/agt_plock.h0000664000175000017500000000557214770023131020246 0ustar vladimirvladimir #ifndef _H_agt_plock #define _H_agt_plock /* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * module ietf-netconf-partial-lock revision 2009-10-19 namespace urn:ietf:params:xml:ns:netconf:partial-lock:1.0 organization IETF Network Configuration (netconf) Working Group */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif #define y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock \ (const xmlChar *)"ietf-netconf-partial-lock" #define y_ietf_netconf_partial_lock_R_ietf_netconf_partial_lock \ (const xmlChar *)"2009-10-19" #define y_ietf_netconf_partial_lock_N_lock_id (const xmlChar *)"lock-id" #define y_ietf_netconf_partial_lock_N_locked_node (const xmlChar *)"locked-node" #define y_ietf_netconf_partial_lock_N_partial_lock \ (const xmlChar *)"partial-lock" #define y_ietf_netconf_partial_lock_N_partial_unlock \ (const xmlChar *)"partial-unlock" #define y_ietf_netconf_partial_lock_N_select (const xmlChar *)"select" /* leaf-list /partial-lock/input/select */ typedef struct y_ietf_netconf_partial_lock_T_partial_lock_input_select_ { dlq_hdr_t qhdr; xmlChar *myselect; } y_ietf_netconf_partial_lock_T_partial_lock_input_select; /* container /partial-lock/input */ typedef struct y_ietf_netconf_partial_lock_T_partial_lock_input_ { dlq_hdr_t myselect; } y_ietf_netconf_partial_lock_T_partial_lock_input; /* leaf-list /partial-lock/output/locked-node */ typedef struct y_ietf_netconf_partial_lock_T_partial_lock_output_locked_node_ { dlq_hdr_t qhdr; xmlChar *locked_node; } y_ietf_netconf_partial_lock_T_partial_lock_output_locked_node; /* container /partial-lock/output */ typedef struct y_ietf_netconf_partial_lock_T_partial_lock_output_ { uint32 lock_id; dlq_hdr_t locked_node; } y_ietf_netconf_partial_lock_T_partial_lock_output; /* container /partial-unlock/input */ typedef struct y_ietf_netconf_partial_lock_T_partial_unlock_input_ { uint32 lock_id; } y_ietf_netconf_partial_lock_T_partial_unlock_input; /* ietf-netconf-partial-lock module init 1 */ extern status_t y_ietf_netconf_partial_lock_init ( const xmlChar *modname, const xmlChar *revision); /* ietf-netconf-partial-lock module init 2 */ extern status_t y_ietf_netconf_partial_lock_init2 (void); /* ietf-netconf-partial-lock module cleanup */ extern void y_ietf_netconf_partial_lock_cleanup (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif yuma123_2.14/netconf/src/agt/agt_cli.h0000664000175000017500000001004614770023131017675 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_cli #define _H_agt_cli /* FILE: agt_cli.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Server Command Line Interface handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 27-oct-06 abb Begun 01-aug-08 abb Convert from NCX PSD to YANG OBJ */ #ifndef _H_agt #include "agt.h" #endif #ifndef _H_help #include "help.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * *********************************************************************/ #define AGT_CLI_MODULE (const xmlChar *)"netconfd" #define AGT_CLI_MODULE_EX (const xmlChar *)"netconfd-ex" #define AGT_CLI_CONTAINER (const xmlChar *)"netconfd" #define AGT_CLI_NOSTARTUP (const xmlChar *)"no-startup" #define AGT_CLI_STARTUP (const xmlChar *)"startup" #define AGT_CLI_FACTORY_STARTUP (const xmlChar *)"factory-startup" #define AGT_CLI_RUNNING_ERROR (const xmlChar *)"running-error" #define AGT_CLI_STARTUP_ERROR (const xmlChar *)"startup-error" #define AGT_CLI_STARTUP_STOP (const xmlChar *)"stop" #define AGT_CLI_STARTUP_CONTINUE (const xmlChar *)"continue" #define AGT_CLI_DELETE_EMPTY_NPCONTAINERS \ (const xmlChar *)"delete-empty-npcontainers" #define AGT_CLI_SUPERUSER NCX_EL_SUPERUSER #define AGT_CLI_MAX_BURST NCX_EL_MAX_BURST /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_cli_process_input * * Process the param line parameters against the hardwired * parmset for the netconfd program * * INPUTS: * argc == argument count * argv == array of command line argument strings * agt_profile == server profile struct to fill in * showver == address of version return quick-exit status * showhelpmode == address of help return quick-exit status * * OUTPUTS: * *agt_profile is filled in, with parms gathered or defaults * *showver == TRUE if user requsted version quick-exit mode * *showhelpmode == requested help mode * (none, breief, normal, full) * * RETURNS: * NO_ERR if all goes well *********************************************************************/ extern status_t agt_cli_process_input (int argc, char *argv[], agt_profile_t *agt_profile, boolean *showver, help_mode_t *showhelpmode); /******************************************************************** * FUNCTION agt_cli_get_valset * * Retrieve the command line parameter set from boot time * * RETURNS: * pointer to parmset or NULL if none *********************************************************************/ extern val_value_t * agt_cli_get_valset (void); /******************************************************************** * FUNCTION agt_cli_cleanup * * Cleanup the module static data * *********************************************************************/ extern void agt_cli_cleanup (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_cli */ yuma123_2.14/netconf/src/agt/agt_sys.h0000664000175000017500000001551014770023131017745 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_sys #define _H_agt_sys /* FILE: agt_sys.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF system.yang DM module support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 04-jun-09 abb Begun. */ #include "dlq.h" #include "ncxtypes.h" #include "obj.h" #include "ses.h" #include "status.h" #include "tstamp.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define AGT_IETF_SYS_MODULE (const xmlChar *)"ietf-system" #define AGT_SYS_MODULE (const xmlChar *)"yuma123-system" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_sys_init * * INIT 1: * Initialize the server notification module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_sys_init (void); /******************************************************************** * FUNCTION agt_sys_init2 * * INIT 2: * Initialize the monitoring data structures * This must be done after the config is loaded * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_sys_init2 (void); /******************************************************************** * FUNCTION agt_sys_cleanup * * Cleanup the module data structures * * INPUTS: * * RETURNS: * none *********************************************************************/ extern void agt_sys_cleanup (void); /******************************************************************** * FUNCTION agt_sys_send_netconf_session_start * * Queue the notification * * INPUTS: * scb == session control block to use for payload values * * OUTPUTS: * notification generated and added to notificationQ * *********************************************************************/ extern void agt_sys_send_netconf_session_start (const ses_cb_t *scb); /******************************************************************** * FUNCTION agt_sys_send_netconf_session_end * * Queue the notification * * INPUTS: * scb == session control block to use for payload values * termreason == enum for the termination-reason leaf * killedby == session-id for killedBy leaf if termination-reason == "killed" * ignored otherwise * * OUTPUTS: * notification generated and added to notificationQ * *********************************************************************/ extern void agt_sys_send_netconf_session_end (const ses_cb_t *scb, ses_term_reason_t termreason, ses_id_t killedby); /******************************************************************** * FUNCTION agt_sys_send_netconf_config_change * * Queue the notification * * INPUTS: * scb == session control block to use for payload values * auditrecQ == Q of rpc_audit_rec_t structs to use * for the notification payload contents * * OUTPUTS: * notification generated and added to notificationQ * *********************************************************************/ extern void agt_sys_send_netconf_config_change (const ses_cb_t *scb, dlq_hdr_t *auditrecQ); /******************************************************************** * FUNCTION agt_sys_send_netconf_capability_change * * Send a event for a module * being added * * Queue the notification * * INPUTS: * changed_by == session control block that made the * change to add this module * == NULL if the server made the change * is_add == TRUE if the capability is being added * FALSE if the capability is being deleted * capstr == capability string that was added or deleted * * OUTPUTS: * notification generated and added to notificationQ * *********************************************************************/ extern void agt_sys_send_netconf_capability_change (ses_cb_t *changed_by, boolean is_add, const xmlChar *capstr); /******************************************************************** * FUNCTION agt_sys_send_netconf_confirmed_commit * * Queue the notification * * INPUTS: * scb == session control block to use for payload values * event == enum for the confirmEvent leaf * * OUTPUTS: * notification generated and added to notificationQ * *********************************************************************/ extern void agt_sys_send_netconf_confirmed_commit (const ses_cb_t *scb, ncx_confirm_event_t event); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_sys */ yuma123_2.14/netconf/src/agt/agt_connect.c0000664000175000017500000002163614770023131020561 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_connect.c Handle the (top-level) element. This message is used for thin clients to connect to the ncxserver. Client --> SSH2 --> OpenSSH.subsystem(netconf) --> ncxserver_connect --> AF_LOCAL/ncxserver.sock --> ncxserver.listen --> top_dispatch -> ncx_connect_handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 15jan07 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include "procdefs.h" #include "agt.h" #include "agt_connect.h" #include "agt_hello.h" #include "agt_rpcerr.h" #include "agt_ses.h" #include "agt_state.h" #include "agt_sys.h" #include "agt_util.h" #include "cap.h" #include "cfg.h" #include "log.h" #include "ncx.h" #include "ncx_num.h" #include "ses.h" #include "status.h" #include "top.h" #include "val.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_connect_init_done = FALSE; /******************************************************************** * FUNCTION agt_connect_init * * Initialize the agt_connect module * Adds the agt_connect_dispatch function as the handler * for the NCX top-level element. * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ status_t agt_connect_init (void) { status_t res; if (!agt_connect_init_done) { res = top_register_node(NCX_MODULE, NCX_EL_NCXCONNECT, agt_connect_dispatch); if (res != NO_ERR) { return res; } agt_connect_init_done = TRUE; } return NO_ERR; } /* agt_connect_init */ /******************************************************************** * FUNCTION agt_connect_cleanup * * Cleanup the agt_connect module. * Unregister the top-level NCX element * *********************************************************************/ void agt_connect_cleanup (void) { if (agt_connect_init_done) { top_unregister_node(NCX_MODULE, NCX_EL_NCXCONNECT); agt_connect_init_done = FALSE; } } /* agt_connect_cleanup */ /******************************************************************** * FUNCTION agt_connect_dispatch * * Handle an incoming request * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ void agt_connect_dispatch (ses_cb_t *scb, xml_node_t *top) { xml_attr_t *attr; status_t res; ncx_num_t num; #ifdef DEBUG if (!scb || !top) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif log_debug("\nagt_connect got node"); res = NO_ERR; /* make sure 'top' is the right kind of node */ if (top->nodetyp != XML_NT_EMPTY) { res = ERR_NCX_WRONG_NODETYP; /* TBD: stats update */ } /* only process this message in session init state */ if (res==NO_ERR && scb->state != SES_ST_INIT) { /* TBD: stats update */ res = ERR_NCX_NO_ACCESS_STATE; } else { scb->state = SES_ST_IN_MSG; } /* check the ncxserver version */ if (res == NO_ERR) { attr = xml_find_attr(top, 0, NCX_EL_VERSION); if (attr && attr->attr_val) { res = ncx_convert_num(attr->attr_val, NCX_NF_DEC, NCX_BT_UINT32, &num); if (res == NO_ERR) { if (num.u != NCX_SERVER_VERSION) { res = ERR_NCX_WRONG_VERSION; } } } else { res = ERR_NCX_MISSING_ATTR; } } /* check the magic password string */ if (res == NO_ERR) { attr = xml_find_attr(top, 0, NCX_EL_MAGIC); if (attr && attr->attr_val) { if (xml_strcmp(attr->attr_val, (const xmlChar *)NCX_SERVER_MAGIC)) { res = ERR_NCX_ACCESS_DENIED; } } else { res = ERR_NCX_MISSING_ATTR; } } /* check the transport */ if (res == NO_ERR) { attr = xml_find_attr(top, 0, NCX_EL_TRANSPORT); if (attr && attr->attr_val) { if ( 0 == xml_strcmp(attr->attr_val, (const xmlChar *)NCX_SERVER_TRANSPORT)) { /* transport indicates an external connection over * ssh, check the ncxserver port number */ attr = xml_find_attr(top, 0, NCX_EL_PORT); if (attr && attr->attr_val) { res = ncx_convert_num(attr->attr_val, NCX_NF_DEC, NCX_BT_UINT16, &num); if (res == NO_ERR) { if (!agt_ses_ssh_port_allowed((uint16)num.u)) { res = ERR_NCX_ACCESS_DENIED; } } } else { res = ERR_NCX_MISSING_ATTR; } } else if ( xml_strcmp(attr->attr_val, (const xmlChar *)NCX_SERVER_TRANSPORT_LOCAL)) { /* transport is unsupported (i.e. not SSH or 'local' ) */ res = ERR_NCX_ACCESS_DENIED; } } else { res = ERR_NCX_MISSING_ATTR; } } /* get the username */ if (res == NO_ERR) { attr = xml_find_attr(top, 0, NCX_EL_USER); if (attr && attr->attr_val) { scb->username = xml_strdup(attr->attr_val); if (!scb->username) { res = ERR_INTERNAL_MEM; } } else { res = ERR_NCX_MISSING_ATTR; } } /* get the client address */ if (res == NO_ERR) { attr = xml_find_attr(top, 0, NCX_EL_ADDRESS); if (attr && attr->attr_val) { scb->peeraddr = xml_strdup(attr->attr_val); if (!scb->peeraddr) { res = ERR_INTERNAL_MEM; } } else { res = ERR_NCX_MISSING_ATTR; } } if (res == NO_ERR) { /* add the session to the netconf-state DM */ res = agt_state_add_session(scb); /* bump the session state and send the agent hello message */ if (res == NO_ERR) { res = agt_hello_send(scb); if (res != NO_ERR) { agt_state_remove_session(scb->sid); } } if (res == NO_ERR) { scb->state = SES_ST_HELLO_WAIT; } } /* report first error and close session */ if (res != NO_ERR) { agt_ses_request_close(scb, scb->sid, SES_TR_BAD_START); if (LOGINFO) { log_info("\nagt_connect error (%s)\n" " dropping session %d", get_error_string(res), scb->sid); } } else { log_debug("\nagt_connect msg ok"); agt_sys_send_netconf_session_start(scb); } } /* agt_connect_dispatch */ /* END file agt_connect.c */ yuma123_2.14/netconf/src/agt/agt_cfg.h0000664000175000017500000003176514770023131017700 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_cfg #define _H_agt_cfg /* FILE: agt_cfg.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Manage Server configuration edit transactions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 22dec11 abb begun; split out from ncx/rpc ... */ #ifndef _H_agt #include "agt.h" #endif #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h.h" #endif #ifndef _H_op #include "op.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* classify the config edit action type */ typedef enum agt_cfg_edit_action_t_ { AGT_CFG_EDIT_ACTION_NONE, AGT_CFG_EDIT_ACTION_ADD, AGT_CFG_EDIT_ACTION_SET, AGT_CFG_EDIT_ACTION_MOVE, AGT_CFG_EDIT_ACTION_REPLACE, AGT_CFG_EDIT_ACTION_DELETE, AGT_CFG_EDIT_ACTION_DELETE_DEFAULT } agt_cfg_edit_action_t; /* classify the config edit type */ typedef enum agt_cfg_edit_type_t_ { AGT_CFG_EDIT_TYPE_NONE, AGT_CFG_EDIT_TYPE_FULL, // REPLACE = full database replace AGT_CFG_EDIT_TYPE_PARTIAL // REPLACE = subtree replace } agt_cfg_edit_type_t; /* struct representing 1 configuration database edit transaction * - A NETCONF transaction is any write attempt to a specific config. * * - Each on candidate or running is a separate transaction. * * - A commit is a separate transaction, and all individual edits to * candidate are ignored, and a new transaction change set is calculated. * * - Save from running to startup is a separate transaction. * * - The validate operation (or edit-config in test-only mode) * will cause a transaction ID to be used, even though no writes * are ever done. The txid must be the same for validate and * apply/commit phases. * * Each transaction gets a new auto-incremented transaction ID * which is saved in a file across reboots. It is only updated * when the NV-storage version of the config is written, or upon * a clean exit. This means that a program or computer crash could cause * transaction IDs to be reused upon a restart, that were previously * used for candidate or running. * * Incremental rollback based on undoQ contents is still TBD, * so qhdr and last_transaction_id are not really used yet */ typedef struct agt_cfg_transaction_t_ { dlq_hdr_t qhdr; cfg_transaction_id_t txid; agt_cfg_edit_type_t edit_type; ncx_cfg_t cfg_id; status_t apply_res; status_t commit_res; status_t rollback_res; boolean start_bad; // running config has errors boolean rootcheck; boolean commitcheck; boolean is_validate; /* each distinct effective edit point in the data tree will * have a separate undo record in the undoQ */ dlq_hdr_t undoQ; /* Q of agt_cfg_undo_rec_t */ /* TBD: this is redundant and can be derived from the undoQ * contains edit record highlights used in the sysConfigChange * notification and netconfd audit log */ dlq_hdr_t auditQ; /* Q of agt_cfg_audit_rec_t */ /* contains nodes marked as deleted by the delete_dead_nodes test */ dlq_hdr_t deadnodeQ; /* Q of agt_cfg_nodeptr_t */ } agt_cfg_transaction_t; /* struct of pointers to extra deleted objects * This occurs when a new case within a choice is selected * and any nodes from other cases are deleted */ typedef struct agt_cfg_nodeptr_t_ { dlq_hdr_t qhdr; val_value_t *node; } agt_cfg_nodeptr_t; /* struct of params to undo an edit opration * The actual nodes used depend on the edit operation value */ typedef struct agt_cfg_undo_rec_t_ { dlq_hdr_t qhdr; boolean free_curnode; op_editop_t editop; agt_cfg_edit_action_t edit_action; val_value_t *newnode; val_value_t *newnode_marker; val_value_t *curnode; val_value_t *curnode_marker; val_value_t *curnode_clone; val_value_t *parentnode; dlq_hdr_t extra_deleteQ; /* Q of agt_cfg_nodeptr_t */ status_t apply_res; status_t commit_res; status_t rollback_res; } agt_cfg_undo_rec_t; /* struct of params to use when generating sysConfigChange * notification. */ typedef struct agt_cfg_audit_rec_t_ { dlq_hdr_t qhdr; xmlChar *target; op_editop_t editop; } agt_cfg_audit_rec_t; /* struct for the commit-time tests for a single object */ typedef struct agt_cfg_commit_test_t_ { dlq_hdr_t qhdr; obj_template_t *obj; xpath_pcb_t *objpcb; xpath_result_t *result; cfg_transaction_id_t result_txid; ncx_btype_t btyp; uint32 testflags; /* AGT_TEST_FL_FOO bits */ } agt_cfg_commit_test_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_cfg_new_transaction * * Malloc and initialize agt_cfg_transaction_t struct * * INPUTS: * cfgid == config ID to use * edit_type == database edit type * rootcheck == TRUE if root_check needs to be done before commit * during agt_val_apply_write; FALSE if it is done * manually via agt_val_root_check * is_validate == TRUE if this is a operation * the target data nodes will not be altered * at all during the transaction * FALSE if this is some sort of real edit * res == address of return status * OUTPUTS: * *res == return status * RETURNS: * malloced transaction struct; need to call agt_cfg_free_transaction *********************************************************************/ agt_cfg_transaction_t * agt_cfg_new_transaction (ncx_cfg_t cfgid, agt_cfg_edit_type_t edit_type, boolean rootcheck, boolean is_validate, status_t *res); /******************************************************************** * FUNCTION agt_cfg_free_transaction * * Clean and free a agt_cfg_transaction_t struct * * INPUTS: * txcb == transaction struct to free *********************************************************************/ extern void agt_cfg_free_transaction (agt_cfg_transaction_t *txcb); /******************************************************************** * FUNCTION agt_cfg_init_transactions * * Initialize the transaction ID functionality * * INPUTS: * txidfile == TXID filespec to use * foundfile == TRUE if file found; FALSE if this is first write * RETURNS: * status *********************************************************************/ extern status_t agt_cfg_init_transactions (const xmlChar *txidfile, boolean foundfile); /******************************************************************** * FUNCTION agt_cfg_txid_in_progress * * Return the ID of the current transaction ID in progress * * INPUTS: * cfgid == config ID to check * RETURNS: * txid of transaction in progress or 0 if none *********************************************************************/ extern cfg_transaction_id_t agt_cfg_txid_in_progress (ncx_cfg_t cfgid); /******************************************************************** * FUNCTION agt_cfg_new_undorec * * Malloc and initialize a new agt_cfg_undo_rec_t struct * * INPUTS: * none * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ extern agt_cfg_undo_rec_t * agt_cfg_new_undorec (void); /******************************************************************** * FUNCTION agt_cfg_init_undorec * * Initialize a new agt_cfg_undo_rec_t struct * * INPUTS: * undo == cfg_undo_rec_t memory to initialize * RETURNS: * none *********************************************************************/ extern void agt_cfg_init_undorec (agt_cfg_undo_rec_t *undo); /******************************************************************** * FUNCTION agt_cfg_free_undorec * * Free all the memory used by the specified agt_cfg_undo_rec_t * * INPUTS: * undo == agt_cfg_undo_rec_t to clean and delete * RETURNS: * none *********************************************************************/ extern void agt_cfg_free_undorec (agt_cfg_undo_rec_t *undorec); /******************************************************************** * FUNCTION agt_cfg_clean_undorec * * Clean all the memory used by the specified agt_cfg_undo_rec_t * but do not free the struct itself * * !!! The caller must free internal pointers that were malloced * !!! instead of copied. This function does not check them!!! * * INPUTS: * undo == agt_cfg_undo_rec_t to clean * RETURNS: * none *********************************************************************/ extern void agt_cfg_clean_undorec (agt_cfg_undo_rec_t *undo); /******************************************************************** * FUNCTION agt_cfg_new_auditrec * * Malloc and initialize a new agt_cfg_audit_rec_t struct * * INPUTS: * target == i-i string of edit target * editop == edit operation enum * * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ extern agt_cfg_audit_rec_t * agt_cfg_new_auditrec (const xmlChar *target, op_editop_t editop); /******************************************************************** * FUNCTION agt_cfg_free_auditrec * * Free all the memory used by the specified agt_cfg_audit_rec_t * * INPUTS: * auditrec == agt_cfg_audit_rec_t to clean and delete * * RETURNS: * none *********************************************************************/ extern void agt_cfg_free_auditrec (agt_cfg_audit_rec_t *auditrec); /******************************************************************** * FUNCTION agt_cfg_new_commit_test * * Malloc a agt_cfg_commit_test_t struct * * RETURNS: * malloced commit test struct or NULL if ERR_INTERNAL_MEM *********************************************************************/ extern agt_cfg_commit_test_t * agt_cfg_new_commit_test (void); /******************************************************************** * FUNCTION agt_cfg_free_commit_test * * Free a previously malloced agt_cfg_commit_test_t struct * * INPUTS: * commit_test == commit test record to free * *********************************************************************/ extern void agt_cfg_free_commit_test (agt_cfg_commit_test_t *commit_test); /******************************************************************** * FUNCTION agt_cfg_new_nodeptr * * Malloc and initialize a new agt_cfg_nodeptr_t struct * * INPUTS: * node == node to point at * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ extern agt_cfg_nodeptr_t * agt_cfg_new_nodeptr (val_value_t *node); /******************************************************************** * FUNCTION agt_cfg_free_nodeptr * * Free all the memory used by the specified agt_cfg_nodeptr_t * * INPUTS: * nodeptr == agt_cfg_nodeptr_t to clean and delete *********************************************************************/ extern void agt_cfg_free_nodeptr (agt_cfg_nodeptr_t *nodeptr); /******************************************************************** * FUNCTION agt_cfg_update_txid * * Update the TXID file with the latest value transaction ID * * RETURNS: * status *********************************************************************/ extern status_t agt_cfg_update_txid (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_cfg */ yuma123_2.14/netconf/src/agt/agt_cap.h0000664000175000017500000001040414770023131017667 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_cap #define _H_agt_cap /* FILE: agt_cap.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Server capabilities handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 03-feb-06 abb Begun; split out from base/cap.h */ #ifndef _H_agt #include "agt.h" #endif #ifndef _H_cap #include "cap.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_cap_cleanup * * Clean the NETCONF server capabilities * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void agt_cap_cleanup (void); /******************************************************************** * FUNCTION agt_cap_set_caps * * Initialize the NETCONF server capabilities * * INPUTS: * agttarg == the target of edit-config for this server * agtstart == the type of startup configuration for this server * defstyle == default with-defaults style for the entire server * * RETURNS: * NO_ERR if all goes well *********************************************************************/ extern status_t agt_cap_set_caps (ncx_agttarg_t agttarg, ncx_agtstart_t agtstart, const xmlChar *defstyle); /******************************************************************** * FUNCTION agt_cap_set_modules * * Initialize the NETCONF server capabilities modules list * MUST call after agt_cap_set_caps * * INPUTS: * profile == server profile control block to use * * RETURNS: * status *********************************************************************/ extern status_t agt_cap_set_modules (agt_profile_t *profile); /******************************************************************** * FUNCTION agt_cap_add_module * * Add a module at runtime, after the initial set has been set * MUST call after agt_cap_set_caps * * RETURNS: * status *********************************************************************/ extern status_t agt_cap_add_module (ncx_module_t *mod); /******************************************************************** * FUNCTION agt_cap_get_caps * * Get the NETCONF server capabilities * * INPUTS: * none * RETURNS: * pointer to the server caps list *********************************************************************/ extern cap_list_t * agt_cap_get_caps (void); /******************************************************************** * FUNCTION agt_cap_get_capsval * * Get the NETCONF server capabilities in val_value_t format * * INPUTS: * none * RETURNS: * pointer to the server caps list *********************************************************************/ extern val_value_t * agt_cap_get_capsval (void); /******************************************************************** * FUNCTION agt_cap_std_set * * Check if the STD capability is set for the server * * INPUTS: * cap == ID of capability to check * * RETURNS: * TRUE is STD cap set, FALSE otherwise *********************************************************************/ extern boolean agt_cap_std_set (cap_stdid_t cap); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_cap */ yuma123_2.14/netconf/src/agt/agt_not_queue_notification_cb.h0000664000175000017500000000462314770023131024350 0ustar vladimirvladimir#ifndef _H_agt_not_queue_notification_cb #define _H_agt_not_queue_notification_cb /* FILE: agt_not_queue_notification_cb.h ********************************************************************* * P U R P O S E ********************************************************************* NETCONF Server agt_not_queue_notification callback handler This file contains functions to support registering, unregistering and execution of agt_not_queue_notification callbacks. */ #include "procdefs.h" #include "status.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * T Y P E D E F S *********************************************************************/ /** Typedef of the queue_notification callback */ typedef status_t (*agt_not_queue_notification_cb_t)(agt_not_msg_t *notif); /******************************************************************** * F U N C T I O N S *********************************************************************/ /** * Initialise the callback commit module. */ extern void agt_not_queue_notification_cb_init( void ); /** * Cleanup the callback commit module. */ extern void agt_not_queue_notification_cb_cleanup( void ); /** * Register a queue notification callback. * This function registers a queue-notification callback that will be * called when any module calls agt_not_queue_notification * * \param modname the name of the module registering the callback * \param cb the commit complete function. * \return the status of the operation. */ extern status_t agt_not_queue_notification_cb_register( const xmlChar *modname, agt_not_queue_notification_cb_t cb ); /** * Unregister a queue notification callback. * This function unregisters a queue-notification callback. * * \param modname the name of the module unregistering the callback */ extern void agt_not_queue_notification_cb_unregister( const xmlChar *modname ); /** * This function simply calls each registered queue notification * callback. If a operation fails the status of the * failing operation is returned immediately and no further queue * notification callbacks are made. * * \return the ERR_OK or the status of the first failing callback. */ extern status_t agt_not_queue_notification_cb( agt_not_msg_t *notif ); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif // _H_agt_not_queue_notification_cb yuma123_2.14/netconf/src/agt/agt_ncxserver.c0000664000175000017500000004503414770023131021145 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_ncxserver.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11-jan-07 abb begun; gathered from glibc documentation ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_ncxserver.h" #include "agt_not.h" #include "agt_rpc.h" #include "agt_ses.h" #include "agt_timer.h" #include "def_reg.h" #include "log.h" #include "ncx.h" #include "ncxmod.h" #include "ncxconst.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "xmlns.h" #include "agt_fd_event_cb.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* how often to check for agent shutown (in seconds) */ #define AGT_NCXSERVER_TIMEOUT 1 /* number of notifications to send out in 1 timeout interval */ #define MAX_NOTIFICATION_BURST 10 static fd_set active_fd_set; static fd_set read_fd_set; static fd_set write_fd_set; //static fd_set event_fd_set; int event_fd = -1; agt_fd_event_cb_fn_t event_cb_fn; /******************************************************************** * FUNCTION make_named_socket * * Create an AF_LOCAL socket for the ncxserver * * INPUTS: * filename == full filespec of the socket filename * sock == ptr to return value * * OUTPUTS: * *sock == the FD for the socket if return ok * * RETURNS: * status *********************************************************************/ static status_t make_named_socket (const char *filename, int *sock) { int ret; size_t size; struct sockaddr_un name; *sock = 0; /* Create the socket. */ *sock = socket(PF_LOCAL, SOCK_STREAM, 0); if (*sock < 0) { perror ("socket"); return ERR_NCX_OPERATION_FAILED; } /* Give the socket a name. */ name.sun_family = AF_LOCAL; strncpy(name.sun_path, filename, sizeof(name.sun_path)-1); name.sun_path[sizeof(name.sun_path)-1] = 0; size = SUN_LEN(&name); ret = bind(*sock, (struct sockaddr *)&name, size); if (ret != 0) { perror ("bind"); return ERR_NCX_OPERATION_FAILED; } /* change the permissions */ ret = chmod(filename, (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)); if (ret != 0) { perror ("chmod"); return ERR_NCX_OPERATION_FAILED; } return NO_ERR; } /* make_named_socket */ /******************************************************************** * FUNCTION make_tcp_socket * * Create an AF_INET socket for the ncxserver * * INPUTS: * port == port * sock == ptr to return value * * OUTPUTS: * *sock == the FD for the socket if return ok * * RETURNS: * status *********************************************************************/ static status_t make_tcp_socket (const char *server_address, int port, int *sock) { int ret; size_t size; struct sockaddr_in name; int so_reuse_address_option=1; struct hostent* hp; *sock = 0; /* Create the socket. */ *sock = socket(AF_INET, SOCK_STREAM, 0); if (*sock < 0) { perror ("socket"); return ERR_NCX_OPERATION_FAILED; } if (setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char*) &so_reuse_address_option, sizeof(so_reuse_address_option)) < 0) { perror ("setsockopt"); return ERR_NCX_OPERATION_FAILED; } hp = gethostbyname(server_address); if (hp == NULL) { perror ("gethostbyname"); return ERR_NCX_OPERATION_FAILED; } size = sizeof(name); memset((char *) &name, 0, size); name.sin_family = AF_INET; name.sin_port = htons((unsigned short)port); memcpy((char *) &name.sin_addr, hp->h_addr, hp->h_length); ret = bind(*sock, (struct sockaddr *)&name, size); if (ret != 0) { perror ("bind"); return ERR_NCX_OPERATION_FAILED; } return NO_ERR; } /* make_tcp_socket */ /******************************************************************** * FUNCTION send_some_notifications * * Send some notifications as needed * * INPUTS: * filename == full filespec of the socket filename * sock == ptr to return value * * OUTPUTS: * *sock == the FD for the socket if return ok * * RETURNS: * status *********************************************************************/ static void send_some_notifications (void) { const agt_profile_t *agt_profile; uint32 sendcount, sendtotal, sendmax; boolean done; sendcount = 0; sendtotal = 0; /* get --maxburst CLI param value */ agt_profile = agt_get_profile(); sendmax = agt_profile->agt_maxburst; done = FALSE; while (!done) { sendcount = agt_not_send_notifications(); if (sendcount) { sendtotal += sendcount; if (sendmax && (sendtotal >= sendmax)) { done = TRUE; } } else { done = TRUE; } } if (agt_profile->agt_eventlog_size == 0) { agt_not_clean_eventlog(); } } /* send_some_notifications */ /*********** E X P O R T E D F U N C T I O N S *************/ /******************************************************************** * FUNCTION agt_ncxserver_run * * IO server loop for the ncxserver socket * * RETURNS: * status *********************************************************************/ static int ncxsock, maxwrnum, maxrdnum; status_t agt_ncxserver_run (void) { ses_cb_t *scb; agt_profile_t *profile; int i, new, ret; struct sockaddr_un clientname; struct timeval timeout; socklen_t size; status_t res; boolean done, done2, stream_output; val_value_t *clivalset; val_value_t *val; profile = agt_get_profile(); if (profile == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } if(profile->agt_tcp_direct_port!=-1) { res = make_tcp_socket(profile->agt_tcp_direct_address, profile->agt_tcp_direct_port, &ncxsock); if (res != NO_ERR) { log_error("\n*** Cannot connect to ncxserver socket listen tcp port: %d\n",profile->agt_tcp_direct_port); return res; } } else { res = make_named_socket(profile->agt_ncxserver_sockname, &ncxsock); if (res != NO_ERR) { log_error("\n*** Cannot connect to ncxserver socket" "\n*** If no other instances of netconfd are running," "\n*** try deleting %s\n",profile->agt_ncxserver_sockname); return res; } } stream_output = profile->agt_stream_output; if (listen(ncxsock, 1) < 0) { log_error("\nError: listen failed"); return ERR_NCX_OPERATION_FAILED; } /* Initialize the set of active sockets. */ FD_ZERO(&read_fd_set); FD_ZERO(&write_fd_set); FD_ZERO(&active_fd_set); //FD_ZERO(&event_fd_set); FD_SET(ncxsock, &active_fd_set); maxwrnum = maxrdnum = ncxsock; done = FALSE; while (!done) { /* check exit program */ if (agt_shutdown_requested()) { done = TRUE; continue; } ret = 0; done2 = FALSE; while (!done2) { read_fd_set = active_fd_set; agt_ses_fill_writeset(&write_fd_set, &maxwrnum); timeout.tv_sec = AGT_NCXSERVER_TIMEOUT; timeout.tv_usec = 0; /* Block until input arrives on one or more active sockets. * or the timer expires */ ret = select(max(maxrdnum+1, maxwrnum+1), &read_fd_set, &write_fd_set, NULL, &timeout); if (ret > 0) { done2 = TRUE; } else if (ret < 0) { if (!(errno == EINTR || errno==EAGAIN)) { done2 = TRUE; } } else if (ret == 0) { /* should only happen if a timeout occurred */ if (agt_shutdown_requested()) { done2 = TRUE; } else { /* !! put all polling callbacks here for now !! */ agt_ses_check_timeouts(); agt_timer_handler(); send_some_notifications(); } } else { /* normal return with some bytes */ done2 = TRUE; } } /* check exit program */ if (agt_shutdown_requested()) { done = TRUE; continue; } /* check select return status for non-recoverable error */ if (ret < 0) { res = ERR_NCX_OPERATION_FAILED; log_error("\nncxserver select failed (%s)", strerror(errno)); agt_request_shutdown(NCX_SHUT_EXIT); done = TRUE; continue; } if ((event_fd!=-1) && FD_ISSET(event_fd, &read_fd_set)) { event_cb_fn(event_fd); continue; } /* Service all the sockets with input and/or output pending */ done2 = FALSE; for (i = 0; i < max(maxrdnum+1, maxwrnum+1) && !done2; i++) { scb=NULL; /* check write output to client sessions */ if (!stream_output && FD_ISSET(i, &write_fd_set)) { /* try to send 1 packet worth of buffers for a session */ scb = def_reg_find_scb(i); if (scb) { /* check if anything to write */ if (!dlq_empty(&scb->outQ)) { res = ses_msg_send_buffs(scb); if (res != NO_ERR) { if (LOGINFO) { log_info("\nagt_ncxserver write failed; " "closing session %d ", scb->sid); } } if (res != NO_ERR) { agt_ses_kill_session(scb, scb->sid, SES_TR_OTHER); scb = NULL; } else if (scb->state == SES_ST_SHUTDOWN_REQ) { /* close-session reply sent, now kill ses */ agt_ses_kill_session(scb, scb->killedbysid, scb->termreason); scb = NULL; } } /* check if any buffers left over for next loop */ if (scb && !dlq_empty(&scb->outQ)) { ses_msg_make_outready(scb); } } } /* check read input from client sessions */ if (FD_ISSET(i, &read_fd_set)) { if (i == ncxsock) { /* Connection request on original socket. */ size = (socklen_t)sizeof(clientname); new = accept(ncxsock, (struct sockaddr *)&clientname, &size); if (new < 0) { if (LOGINFO) { log_info("\nagt_ncxserver accept " "connection failed (%d)", new); } continue; } /* get a new session control block */ if (!agt_ses_new_session(SES_TRANSPORT_SSH, new)) { close(new); if (LOGINFO) { log_info("\nagt_ncxserver new " "session failed (%d)", new); } } else { /* set non-blocking IO */ if (fcntl(new, F_SETFD, O_NONBLOCK)) { if (LOGINFO) { log_info("\nfnctl failed"); } } FD_SET(new, &active_fd_set); if (new > maxrdnum) { maxrdnum = new; } } } else { /* Data arriving on an already-connected socket. * Need to have the xmlreader for this session */ scb = def_reg_find_scb(i); ses_accept_defered_input: if (scb != NULL) { res = ses_accept_input(scb); if (res != NO_ERR) { if (i >= maxrdnum) { maxrdnum = i-1; } if (res != ERR_NCX_SESSION_CLOSED) { if (LOGINFO) { log_info("\nagt_ncxserver: input failed" " for session %d (%s)", scb->sid, get_error_string(res)); } /* send an error reply instead of * killing the session right now */ agt_rpc_send_error_reply(scb, res); agt_ses_request_close(scb, 0, SES_TR_OTHER); } else { /* connection already closed * so kill session right now */ agt_ses_kill_session(scb, scb->sid, SES_TR_DROPPED); scb = NULL; } } } } } } /* drain the ready queue before accepting new input */ if (!done) { done2 = FALSE; while (!done2) { if (!agt_ses_process_first_ready()) { done2 = TRUE; scb = NULL; } else if (agt_shutdown_requested()) { done = done2 = TRUE; } else { send_some_notifications(); } } if(scb && scb->indefer_len>0) { /* * input defered until previous message * is processed e.g. trailing */ log_debug3("\nagt_ncxserver: goto deferred trailing input processing."); goto ses_accept_defered_input; } } } /* end select loop */ /* all open client sockets will be closed as the sessions are * torn down, but the original ncxserver socket needs to be closed now */ close(ncxsock); unlink(NCXSERVER_SOCKNAME); return NO_ERR; } /* agt_ncxserver_run */ /******************************************************************** * FUNCTION agt_ncxserver_clear_fd * * Clear a dead session from the select loop * * INPUTS: * fd == file descriptor number for the socket to clear *********************************************************************/ void agt_ncxserver_clear_fd (int fd) { FD_CLR(fd, &active_fd_set); } /* agt_ncxserver_clear_fd */ /******************************************************************** * FUNCTION agt_fd_event_cb_register * * Register event file descriptor in the select loop with * corresponding callback * * INPUTS: * fd == file descriptor number * cb_fn == callback function pointer *********************************************************************/ void agt_fd_event_cb_register(int fd, agt_fd_event_cb_fn_t cb_fn) { FD_SET(fd, &active_fd_set); if (fd > maxrdnum) { maxrdnum = fd; } event_fd = fd; event_cb_fn = cb_fn; } /* agt_fd_event_cb_register */ /******************************************************************** * FUNCTION agt_fd_event_cb_unregister * * Unregister event file descriptor from the select loop * * INPUTS: * fd == file descriptor number *********************************************************************/ void agt_fd_event_cb_unregister(int fd) { // FD_CLR(fd, &event_fd_set); event_fd = -1; FD_CLR(fd, &active_fd_set); } /* agt_fd_event_cb_register */ /* END agt_ncxserver.c */ yuma123_2.14/netconf/src/agt/agt_ncx.h0000664000175000017500000001550714770023131017725 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_ncx #define _H_agt_ncx /* FILE: agt_ncx.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Server standard method routines ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 04-feb-06 abb Begun */ #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_ncx_init * * Initialize the NETCONF Server standard method routines * * RETURNS: * status of the initialization procedure *********************************************************************/ extern status_t agt_ncx_init (void); /******************************************************************** * FUNCTION agt_ncx_cleanup * * Cleanup the NETCONF Server standard method routines * *********************************************************************/ extern void agt_ncx_cleanup (void); /******************************************************************** * FUNCTION agt_ncx_cfg_load * * Load the specifed config from the indicated source * Called just once from agt.c at boot or reload time! * * This function should only be used to load an empty config * in CFG_ST_INIT state * * INPUTS: * cfg = Config template to load data into * cfgloc == enum for the config source location * cfgparm == string parameter used in different ways * depending on the cfgloc value * For cfgloc==CFG_LOC_FILE, this is a system-dependent filespec * * OUTPUTS: * errQ contains any rpc_err_rec_t structs (if non-NULL) * * RETURNS: * overall status; may be the last of multiple error conditions *********************************************************************/ extern status_t agt_ncx_cfg_load (cfg_template_t *cfg, cfg_location_t cfgloc, const xmlChar *cfgparm); /******************************************************************** * FUNCTION agt_ncx_cfg_save * * Save the specified cfg to the its startup source, which should * be stored in the cfg struct * * INPUTS: * cfg = Config template to save from * bkup = TRUE if the current startup config should * be saved before it is overwritten * = FALSE to just overwrite the old startup cfg * RETURNS: * status *********************************************************************/ extern status_t agt_ncx_cfg_save (cfg_template_t *cfg, boolean bkup); /******************************************************************** * FUNCTION agt_ncx_cfg_save_inline * * Save the specified cfg to the its startup source, which should * be stored in the cfg struct * * INPUTS: * source_url == filespec where to save newroot * newroot == value root to save * * RETURNS: * status *********************************************************************/ extern status_t agt_ncx_cfg_save_inline (const xmlChar *source_url, val_value_t *newroot); /******************************************************************** * FUNCTION agt_ncx_load_backup * * Load a backup config into the specified config template * * INPUTS: * filespec == complete path for the input file * cfg == config template to load * use_sid == session ID of user to use * * RETURNS: * status *********************************************************************/ extern status_t agt_ncx_load_backup (const xmlChar *filespec, cfg_template_t *cfg, ses_id_t use_sid); /******************************************************************** * FUNCTION agt_ncx_cc_active * * Check if a confirmed-commit is active, and the timeout * may need to be processed * * RETURNS: * TRUE if confirmed-commit is active * FALSE otherwise *********************************************************************/ extern boolean agt_ncx_cc_active (void); /******************************************************************** * FUNCTION agt_ncx_cc_ses_id * * Get the confirmed commit session ID * * RETURNS: * session ID for the confirmed commit *********************************************************************/ extern ses_id_t agt_ncx_cc_ses_id (void); /******************************************************************** * FUNCTION agt_ncx_clear_cc_ses_id * * Clear the confirmed commit session ID * This will be called by agt_ses when the current * session exits during a persistent confirmed-commit * *********************************************************************/ extern void agt_ncx_clear_cc_ses_id (void); /******************************************************************** * FUNCTION agt_ncx_cc_persist_id * * Get the confirmed commit persist ID * * RETURNS: * session ID for the confirmed commit *********************************************************************/ extern const xmlChar * agt_ncx_cc_persist_id (void); /******************************************************************** * FUNCTION agt_ncx_check_cc_timeout * * Check if a confirmed-commit has timed out, and needs to be canceled * *********************************************************************/ extern void agt_ncx_check_cc_timeout (void); /******************************************************************** * FUNCTION agt_ncx_cancel_confirmed_commit * * Cancel the confirmed-commit in progress and rollback * to the backup-cfg.xml file * * INPUTS: * scb == session control block making this change, may be NULL * event == confirmEvent enumeration value to use * *********************************************************************/ extern void agt_ncx_cancel_confirmed_commit (ses_cb_t *scb, ncx_confirm_event_t event); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_ncx */ yuma123_2.14/netconf/src/agt/agt_state.c0000664000175000017500000015232214770023131020245 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_state.c NETCONF State Data Model implementation: Agent Side Support From draft-13: identifiers: container /netconf-state container /netconf-state/capabilities leaf-list /netconf-state/capabilities/capability container /netconf-state/datastores list /netconf-state/datastores/datastore leaf /netconf-state/datastores/datastore/name container /netconf-state/datastores/datastore/locks choice /netconf-state/datastores/datastore/locks/lock-type case /netconf-state/datastores/datastore/locks/lock-type/global-lock container /netconf-state/datastores/datastore/locks/lock-type/global-lock/ global-lock leaf /netconf-state/datastores/datastore/locks/lock-type/global-lock/ global-lock/locked-by-session leaf /netconf-state/datastores/datastore/locks/lock-type/global-lock/ global-lock/locked-time case /netconf-state/datastores/datastore/locks/lock-type/partial-locks list /netconf-state/datastores/datastore/locks/lock-type/partial-locks/ partial-locks leaf /netconf-state/datastores/datastore/locks/lock-type/partial-locks/ partial-locks/lock-id leaf /netconf-state/datastores/datastore/locks/lock-type/partial-locks/ partial-locks/locked-by-session leaf /netconf-state/datastores/datastore/locks/lock-type/partial-locks/ partial-locks/locked-time leaf-list /netconf-state/datastores/datastore/locks/lock-type/partial-locks/ partial-locks/select leaf-list /netconf-state/datastores/datastore/locks/lock-type/partial-locks/ partial-locks/locked-nodes container /netconf-state/schemas list /netconf-state/schemas/schema leaf /netconf-state/schemas/schema/identifier leaf /netconf-state/schemas/schema/version leaf /netconf-state/schemas/schema/format leaf /netconf-state/schemas/schema/namespace leaf-list /netconf-state/schemas/schema/location container /netconf-state/sessions list /netconf-state/sessions/session leaf /netconf-state/sessions/session/session-id leaf /netconf-state/sessions/session/transport leaf /netconf-state/sessions/session/username leaf /netconf-state/sessions/session/source-host leaf /netconf-state/sessions/session/login-time leaf /netconf-state/sessions/session/in-rpcs leaf /netconf-state/sessions/session/in-bad-rpcs leaf /netconf-state/sessions/session/out-rpc-errors leaf /netconf-state/sessions/session/out-notifications container /netconf-state/statistics leaf /netconf-state/statistics/netconf-start-time leaf /netconf-state/statistics/in-bad-hellos leaf /netconf-state/statistics/in-sessions leaf /netconf-state/statistics/dropped-sessions leaf /netconf-state/statistics/in-rpcs leaf /netconf-state/statistics/in-bad-rpcs leaf /netconf-state/statistics/out-rpc-errors leaf /netconf-state/statistics/out-notifications rpc /get-schema container /get-schema/input leaf /get-schema/input/identifier leaf /get-schema/input/version leaf /get-schema/input/format container /get-schema/output anyxml /get-schema/output/data ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24feb09 abb begun 02dec09 abb redo with new names ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cap.h" #include "agt_cb.h" #include "agt_rpc.h" #include "agt_ses.h" #include "agt_state.h" #include "agt_time_filter.h" #include "agt_util.h" #include "cfg.h" #include "getcb.h" #include "log.h" #include "ncxmod.h" #include "ncxtypes.h" #include "rpc.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "tstamp.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define AGT_STATE_TOP_CONTAINER (const xmlChar *)"netconf-state" #define AGT_STATE_GET_SCHEMA (const xmlChar *)"get-schema" #define AGT_STATE_OBJ_CAPABILITIES (const xmlChar *)"capabilities" #define AGT_STATE_OBJ_DATASTORE (const xmlChar *)"datastore" #define AGT_STATE_OBJ_DATASTORES (const xmlChar *)"datastores" #define AGT_STATE_OBJ_LOCKS (const xmlChar *)"locks" #define AGT_STATE_OBJ_SCHEMA (const xmlChar *)"schema" #define AGT_STATE_OBJ_SCHEMAS (const xmlChar *)"schemas" #define AGT_STATE_OBJ_IDENTIFIER (const xmlChar *)"identifier" #define AGT_STATE_OBJ_FORMAT (const xmlChar *)"format" #define AGT_STATE_OBJ_VERSION (const xmlChar *)"version" #define AGT_STATE_OBJ_NAMESPACE (const xmlChar *)"namespace" #define AGT_STATE_OBJ_LOCATION (const xmlChar *)"location" #define AGT_STATE_OBJ_SESSION (const xmlChar *)"session" #define AGT_STATE_OBJ_SESSIONS (const xmlChar *)"sessions" #define AGT_STATE_OBJ_SESSIONID (const xmlChar *)"session-id" #define AGT_STATE_OBJ_TRANSPORT (const xmlChar *)"transport" #define AGT_STATE_OBJ_USERNAME (const xmlChar *)"username" #define AGT_STATE_OBJ_SOURCEHOST (const xmlChar *)"source-host" #define AGT_STATE_OBJ_LOGINTIME (const xmlChar *)"login-time" #define AGT_STATE_OBJ_IN_RPCS (const xmlChar *)"in-rpcs" #define AGT_STATE_OBJ_IN_SESSIONS (const xmlChar *)"in-sessions" #define AGT_STATE_OBJ_NETCONF_START_TIME \ (const xmlChar *)"netconf-start-time" #define AGT_STATE_OBJ_IN_BAD_RPCS (const xmlChar *)"in-bad-rpcs" #define AGT_STATE_OBJ_IN_BAD_HELLOS (const xmlChar *)"in-bad-hellos" #define AGT_STATE_OBJ_OUT_RPC_ERRORS (const xmlChar *)"out-rpc-errors" #define AGT_STATE_OBJ_OUT_NOTIFICATIONS (const xmlChar *)"out-notifications" #define AGT_STATE_OBJ_DROPPED_SESSIONS (const xmlChar *)"dropped-sessions" #define AGT_STATE_OBJ_STATISTICS (const xmlChar *)"statistics" #define AGT_STATE_OBJ_GLOBAL_LOCK (const xmlChar *)"global-lock" #define AGT_STATE_OBJ_NAME (const xmlChar *)"name" #define AGT_STATE_OBJ_LOCKED_BY_SESSION (const xmlChar *)"locked-by-session" #define AGT_STATE_OBJ_LOCKED_TIME (const xmlChar *)"locked-time" #define AGT_STATE_OBJ_PARTIAL_LOCK (const xmlChar *)"partial-lock" #define AGT_STATE_OBJ_LOCK_ID (const xmlChar *)"lock-id" #define AGT_STATE_OBJ_SELECT (const xmlChar *)"select" #define AGT_STATE_OBJ_LOCKED_NODES (const xmlChar *)"locked-nodes" #define AGT_STATE_FORMAT_YANG (const xmlChar *)"yang" #define AGT_STATE_FORMAT_YIN (const xmlChar *)"yin" #define AGT_STATE_ENUM_NETCONF (const xmlChar *)"NETCONF" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_state_init_done = FALSE; static ncx_module_t *statemod = NULL; static val_value_t *mysessionsval; static val_value_t *myschemasval; static obj_template_t *mysessionobj; static obj_template_t *myschemaobj; /** * \fn get_caps * \brief operation for the capabilities NP container * \param scb session that issued the get (may be NULL) * \param cbmode reason for the callback * \param virval place-holder node in data model for this virtual * value node * \param dstval pointer to value output struct * \return status */ static status_t get_caps (ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *virval, val_value_t *dstval) { val_value_t *capsval; status_t res; (void)scb; (void)virval; res = NO_ERR; if (cbmode == GETCB_GET_VALUE) { capsval = val_clone(agt_cap_get_capsval()); if (!capsval) { return ERR_INTERNAL_MEM; } /* change the namespace to this module, * and get rid of the netconf NSID */ val_change_nsid(capsval, statemod->nsid); val_move_children(capsval, dstval); val_free_value(capsval); } else { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } return res; } /* get_caps */ // ----------------------------------------------------------------------------! /** * \fn make_plock_entry * \brief Make a partial-lock list entry * \param plcb partial lock control block to use * \param plockobj object template to use for the list struct * \param res address of return status * \return pointer to malloc value struct for this data; NULL if * malloc error */ static val_value_t * make_plock_entry (plock_cb_t *plcb, obj_template_t *plockobj, status_t *res) { val_value_t *plockval, *leafval, *valptr; obj_template_t *selectobj, *lockednodeobj; xpath_pcb_t *xpathpcb; xpath_resnode_t *resnode; xpath_result_t *result; xmlChar *pathbuff; *res = NO_ERR; selectobj = obj_find_child(plockobj, AGT_STATE_MODULE, AGT_STATE_OBJ_SELECT); if (selectobj == NULL) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } lockednodeobj = obj_find_child(plockobj, AGT_STATE_MODULE, AGT_STATE_OBJ_LOCKED_NODES); if (lockednodeobj == NULL) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } plockval = val_new_value(); if (plockval == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(plockval, plockobj); /* add the list key: lock-id */ leafval = agt_make_uint_leaf(plockobj, AGT_STATE_OBJ_LOCK_ID, plock_get_id(plcb), res); if (leafval == NULL) { val_free_value(plockval); return NULL; } val_add_child(leafval, plockval); /* add the session ID of the lock-owner */ leafval = agt_make_uint_leaf(plockobj, AGT_STATE_OBJ_LOCKED_BY_SESSION, plock_get_sid(plcb), res); if (leafval == NULL) { val_free_value(plockval); return NULL; } val_add_child(leafval, plockval); /* add the lock start timestamp */ leafval = agt_make_leaf(plockobj, AGT_STATE_OBJ_LOCKED_TIME, plock_get_timestamp(plcb), res); if (leafval == NULL) { val_free_value(plockval); return NULL; } val_add_child(leafval, plockval); /* add a 'select' leaf for each parm in the request */ for (xpathpcb = plock_get_first_select(plcb); xpathpcb != NULL; xpathpcb = plock_get_next_select(xpathpcb)) { /* cobble an XPath string together making sure to * preserve the XML prefix mappings originally parsed */ leafval = val_new_value(); if (leafval == NULL) { val_free_value(plockval); *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(leafval, selectobj); VAL_STR(leafval) = xml_strdup(xpathpcb->exprstr); if (VAL_STR(leafval) == NULL) { val_free_value(plockval); *res = ERR_INTERNAL_MEM; return NULL; } leafval->xpathpcb = xpath_clone_pcb(xpathpcb); if (leafval->xpathpcb == NULL) { val_free_value(plockval); *res = ERR_INTERNAL_MEM; return NULL; } val_add_child(leafval, plockval); } /* add a 'locked-nodes' leaf for each valptr in the * final node-set result */ pathbuff = NULL; result = plock_get_final_result(plcb); for (resnode = xpath_get_first_resnode(result); resnode != NULL; resnode = xpath_get_next_resnode(resnode)) { /* cobble an i-i string together making sure to * preserve the XML prefix mappings originally parsed */ leafval = val_new_value(); if (leafval == NULL) { val_free_value(plockval); *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(leafval, lockednodeobj); valptr = xpath_get_resnode_valptr(resnode); *res = val_gen_instance_id(NULL, valptr, NCX_IFMT_XPATH1, &pathbuff); if (*res != NO_ERR) { if (pathbuff != NULL) { m__free(pathbuff); } val_free_value(leafval); val_free_value(plockval); return NULL; } *res = val_set_simval_obj(leafval, lockednodeobj, pathbuff); if (*res != NO_ERR) { m__free(pathbuff); val_free_value(leafval); val_free_value(plockval); return NULL; } val_add_child(leafval, plockval); m__free(pathbuff); pathbuff = NULL; } *res = val_gen_index_chain(plockobj, plockval); if (*res != NO_ERR) { val_free_value(plockval); return NULL; } *res = NO_ERR; return plockval; } /* make_plock_entry */ // ----------------------------------------------------------------------------! /** * \fn get_locks * \brief operation handler for the locks NP container * \param scb session that issued the get (may be NULL) * \param cbmode reason for the callback * \param virval place-holder node in data model for this virtual * value node * \param dstval pointer to value output struct * \return status */ static status_t get_locks (ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *virval, val_value_t *dstval) { val_value_t *nameval, *newval, *globallockval, *plockval; obj_template_t *globallock, *plockobj; cfg_template_t *cfg; const xmlChar *locktime; plock_cb_t *plcb; status_t res; ses_id_t sid; (void)scb; res = NO_ERR; if (cbmode == GETCB_GET_VALUE) { globallock = obj_find_child(virval->obj, AGT_STATE_MODULE, AGT_STATE_OBJ_GLOBAL_LOCK); if (!globallock) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } plockobj = obj_find_child(virval->obj, AGT_STATE_MODULE, AGT_STATE_OBJ_PARTIAL_LOCK); if (!plockobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } nameval = val_find_child(virval->parent, AGT_STATE_MODULE, AGT_STATE_OBJ_NAME); if (!nameval) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } cfg = cfg_get_config(VAL_ENUM_NAME(nameval)); if (!cfg) { return SET_ERROR(ERR_NCX_CFG_NOT_FOUND); } if (cfg_is_global_locked(cfg)) { /* make the 2 return value leafs to put into * the retval container */ res = cfg_get_global_lock_info(cfg, &sid, &locktime); if (res == NO_ERR) { /* add locks/global-lock */ globallockval = val_new_value(); if (!globallockval) { return ERR_INTERNAL_MEM; } val_init_from_template(globallockval, globallock); val_add_child(globallockval, dstval); /* add locks/global-lock/locked-by-session */ newval = agt_make_uint_leaf(globallock, AGT_STATE_OBJ_LOCKED_BY_SESSION, sid, &res); if (newval) { val_add_child(newval, globallockval); } /* add locks/global-lock/locked-time */ newval = agt_make_leaf(globallock, AGT_STATE_OBJ_LOCKED_TIME, locktime, &res); if (newval) { val_add_child(newval, globallockval); } else { res = ERR_INTERNAL_MEM; } } } else if (cfg_is_partial_locked(cfg)) { for (plcb = cfg_first_partial_lock(cfg); plcb != NULL && res == NO_ERR; plcb = cfg_next_partial_lock(plcb)) { plockval = make_plock_entry(plcb, plockobj, &res); if (plockval) { val_add_child(plockval, dstval); } } } else { res = ERR_NCX_SKIPPED; } } else { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } return res; } /* get_locks */ // ----------------------------------------------------------------------------! /** * \fn get_datastore_name * \brief Get the datastore name for the virtual timestamp entry that * was called from a operation * \param virval virtual value * \param retname address of return name string pointer * \return status */ static status_t get_datastore_name (const val_value_t *virval, const xmlChar **retname) { const val_value_t *parentval, *nameval; parentval = virval->parent; if (parentval == NULL) { return ERR_NCX_DEF_NOT_FOUND; } nameval = val_find_child(parentval, obj_get_mod_name(parentval->obj), (const xmlChar *)"name"); if (nameval == NULL) { return ERR_NCX_DEF_NOT_FOUND; } *retname = VAL_STR(nameval); return NO_ERR; } /* get_datastore_name */ // ----------------------------------------------------------------------------! /** * \fn get_last_modified * \brief operation handler for the datastore/last-modified timestamp * \param scb session that issued the get (may be NULL) * \param cbmode reason for the callback * \param virval place-holder node in data model for this virtual * value node * \param dstval pointer to value output struct * \return status */ static status_t get_last_modified (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { const xmlChar *name = NULL; cfg_template_t *cfg; status_t res; (void)scb; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } res = get_datastore_name(virval, &name); if (res != NO_ERR) { return res; } cfg = cfg_get_config(name); if (cfg == NULL) { return ERR_DB_NOT_FOUND; } VAL_STR(dstval) = xml_strdup(cfg->last_ch_time); if (VAL_STR(dstval) == NULL) { return ERR_INTERNAL_MEM; } return NO_ERR; } /* get_last_modified */ // ----------------------------------------------------------------------------! /** * \fn make_datastore_val * \brief make a val_value_t struct for a specified configuration * \param confname config name * \param confobj config object to use * \param res address of return status * \return malloced value struct or NULL if some error */ static val_value_t * make_datastore_val (const xmlChar *confname, obj_template_t *confobj, status_t *res) { obj_template_t *testobj; val_value_t *confval, *nameval, *leafval; *res = NO_ERR; /* create datastore node */ confval = val_new_value(); if (!confval) { *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(confval, confobj); nameval = agt_make_leaf(confobj, NCX_EL_NAME, confname, res); if (*res != NO_ERR) { if (nameval != NULL) { val_free_value(nameval); } val_free_value(confval); return NULL; } val_add_child(nameval, confval); /* create datastore/locks */ testobj = obj_find_child(confobj, AGT_STATE_MODULE, AGT_STATE_OBJ_LOCKS); if (!testobj) { val_free_value(confval); *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } leafval = val_new_value(); if (!leafval) { val_free_value(confval); *res = ERR_INTERNAL_MEM; return NULL; } val_init_virtual(leafval, get_locks, testobj); val_add_child(leafval, confval); /* create datastore/last-modified */ testobj = obj_find_child(confobj, y_yuma_time_filter_M_yuma_time_filter, NCX_EL_LAST_MODIFIED); if (!testobj) { val_free_value(confval); *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } leafval = val_new_value(); if (!leafval) { val_free_value(confval); *res = ERR_INTERNAL_MEM; return NULL; } val_init_virtual(leafval, get_last_modified, testobj); val_add_child(leafval, confval); *res = NO_ERR; return confval; } /* make_datastore_val */ // ----------------------------------------------------------------------------! /** * \fn make_schema_val * \brief make a val_value_t struct for a specified module * \param mod module control block to use * \param schemaobj object to use * \param res address of return status * \return malloced value struct or NULL if some error */ static val_value_t * make_schema_val (ncx_module_t *mod, obj_template_t *schemaobj, status_t *res) { val_value_t *schemaval, *childval; *res = NO_ERR; /* create schema node */ schemaval = val_new_value(); if (!schemaval) { *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(schemaval, schemaobj); /* create schema/identifier */ childval = agt_make_leaf(schemaobj, AGT_STATE_OBJ_IDENTIFIER, ncx_get_modname(mod), res); if (!childval) { val_free_value(schemaval); return NULL; } val_add_child(childval, schemaval); /* create schema/version */ childval = agt_make_leaf(schemaobj, AGT_STATE_OBJ_VERSION, ncx_get_modversion(mod), res); if (!childval) { val_free_value(schemaval); return NULL; } val_add_child(childval, schemaval); /* create schema/format */ childval = agt_make_leaf(schemaobj, AGT_STATE_OBJ_FORMAT, AGT_STATE_FORMAT_YANG, res); if (!childval) { val_free_value(schemaval); return NULL; } val_add_child(childval, schemaval); /* create schema/namespace */ childval = agt_make_leaf(schemaobj, AGT_STATE_OBJ_NAMESPACE, ncx_get_modnamespace(mod), res); if (!childval) { val_free_value(schemaval); return NULL; } val_add_child(childval, schemaval); /* create schema/location */ childval = agt_make_leaf(schemaobj, AGT_STATE_OBJ_LOCATION, AGT_STATE_ENUM_NETCONF, res); if (!childval) { val_free_value(schemaval); return NULL; } val_add_child(childval, schemaval); *res = val_gen_index_chain(schemaobj, schemaval); if (*res != NO_ERR) { val_free_value(schemaval); return NULL; } return schemaval; } /* make_schema_val */ // ----------------------------------------------------------------------------! /** * \fn make_session_val * \brief make a val_value_t struct for a specified session * \param scb session control block to use * \param sessionobj object to use * \param res address of return status * \return malloced value struct or NULL if some error */ static val_value_t * make_session_val (ses_cb_t *scb, obj_template_t *sessionobj, status_t *res) { val_value_t *sessionval, *childval; xmlChar numbuff[NCX_MAX_NUMLEN]; *res = NO_ERR; /* create session node */ sessionval = val_new_value(); if (!sessionval) { *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(sessionval, sessionobj); /* create session/sessionId */ snprintf((char *)numbuff, sizeof(numbuff), "%u", scb->sid); childval = agt_make_leaf(sessionobj, AGT_STATE_OBJ_SESSIONID, numbuff, res); if (!childval) { val_free_value(sessionval); return NULL; } val_add_child(childval, sessionval); /* create session/transport */ childval = agt_make_leaf(sessionobj, AGT_STATE_OBJ_TRANSPORT, ses_get_transport_name(scb->transport), res); if (!childval) { val_free_value(sessionval); return NULL; } val_add_child(childval, sessionval); /* create session/username */ childval = agt_make_leaf(sessionobj, AGT_STATE_OBJ_USERNAME, scb->username, res); if (!childval) { val_free_value(sessionval); return NULL; } val_add_child(childval, sessionval); /* create session/sourceHost */ childval = agt_make_leaf(sessionobj, AGT_STATE_OBJ_SOURCEHOST, scb->peeraddr, res); if (!childval) { val_free_value(sessionval); return NULL; } val_add_child(childval, sessionval); /* create session/loginTime */ childval = agt_make_leaf(sessionobj, AGT_STATE_OBJ_LOGINTIME, scb->start_time, res); if (!childval) { val_free_value(sessionval); return NULL; } val_add_child(childval, sessionval); /* create session/inRpcs */ childval = agt_make_virtual_leaf(sessionobj, AGT_STATE_OBJ_IN_RPCS, agt_ses_get_session_inRpcs, res); if (!childval) { val_free_value(sessionval); return NULL; } val_add_child(childval, sessionval); /* create session/inBadRpcs */ childval = agt_make_virtual_leaf(sessionobj, AGT_STATE_OBJ_IN_BAD_RPCS, agt_ses_get_session_inBadRpcs, res); if (!childval) { val_free_value(sessionval); return NULL; } val_add_child(childval, sessionval); /* create session/outRpcErrors */ childval = agt_make_virtual_leaf(sessionobj, AGT_STATE_OBJ_OUT_RPC_ERRORS, agt_ses_get_session_outRpcErrors, res); if (!childval) { val_free_value(sessionval); return NULL; } val_add_child(childval, sessionval); /* create session/outNotifications */ childval = agt_make_virtual_leaf(sessionobj, AGT_STATE_OBJ_OUT_NOTIFICATIONS, agt_ses_get_session_outNotifications, res); if (!childval) { val_free_value(sessionval); return NULL; } val_add_child(childval, sessionval); *res = val_gen_index_chain(sessionobj, sessionval); if (*res != NO_ERR) { val_free_value(sessionval); return NULL; } return sessionval; } /* make_session_val */ // ----------------------------------------------------------------------------! /** * \fn make_statistics_val * \brief statisticsobj object to use * \param res address of return status * \return malloced value struct or NULL if some error */ static val_value_t * make_statistics_val (obj_template_t *statisticsobj, status_t *res) { val_value_t *statsval, *childval; xmlChar tbuff[TSTAMP_MIN_SIZE+1]; /* create statistics node */ statsval = val_new_value(); if (!statsval) { *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(statsval, statisticsobj); /* add statistics/netconfStartTime static leaf */ tstamp_datetime(tbuff); childval = agt_make_leaf(statisticsobj, AGT_STATE_OBJ_NETCONF_START_TIME, tbuff, res); if (!childval) { val_free_value(statsval); return NULL; } val_add_child(childval, statsval); /* add statistics/inBadHellos virtual leaf */ childval = agt_make_virtual_leaf(statisticsobj, AGT_STATE_OBJ_IN_BAD_HELLOS, agt_ses_get_inBadHellos, res); if (!childval) { val_free_value(statsval); return NULL; } val_add_child(childval, statsval); /* add statistics/inSessions virtual leaf */ childval = agt_make_virtual_leaf(statisticsobj, AGT_STATE_OBJ_IN_SESSIONS, agt_ses_get_inSessions, res); if (!childval) { val_free_value(statsval); return NULL; } val_add_child(childval, statsval); /* add statistics/droppedSessions virtual leaf */ childval = agt_make_virtual_leaf(statisticsobj, AGT_STATE_OBJ_DROPPED_SESSIONS, agt_ses_get_droppedSessions, res); if (!childval) { val_free_value(statsval); return NULL; } val_add_child(childval, statsval); /* add statistics/inRpcs virtual leaf */ childval = agt_make_virtual_leaf(statisticsobj, AGT_STATE_OBJ_IN_RPCS, agt_ses_get_inRpcs, res); if (!childval) { val_free_value(statsval); return NULL; } val_add_child(childval, statsval); /* add statistics/inBadRpcs virtual leaf */ childval = agt_make_virtual_leaf(statisticsobj, AGT_STATE_OBJ_IN_BAD_RPCS, agt_ses_get_inBadRpcs, res); if (!childval) { val_free_value(statsval); return NULL; } val_add_child(childval, statsval); /* add statistics/outRpcErrors virtual leaf */ childval = agt_make_virtual_leaf(statisticsobj, AGT_STATE_OBJ_OUT_RPC_ERRORS, agt_ses_get_outRpcErrors, res); if (!childval) { val_free_value(statsval); return NULL; } val_add_child(childval, statsval); /* add statistics/outNotifications virtual leaf */ childval = agt_make_virtual_leaf(statisticsobj, AGT_STATE_OBJ_OUT_NOTIFICATIONS, agt_ses_get_outNotifications, res); if (!childval) { val_free_value(statsval); return NULL; } val_add_child(childval, statsval); return statsval; } /* make_statistics_val */ // ----------------------------------------------------------------------------! /** * \fn get_schema_validate * \brief get-schema : validate params callback * \param scb session control block to use * \param see agt_rpc.h * \param see agt_rpc.h * \return status ******************************************************************************/ static status_t get_schema_validate( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { assert( scb && "scb is NULL" ); assert( msg && "msg is NULL" ); assert( methnode && "methnode is NULL" ); ncx_module_t *findmod; const xmlChar *identifier, *version, *format; xmlChar *source; val_value_t *validentifier, *valversion, *valformat; xmlns_id_t nsid; status_t res; uint32 revcount; ncx_modformat_t modformat; boolean found; res = NO_ERR; findmod = NULL; identifier = NULL; version = NULL; format = NULL; source = NULL; nsid = 0; found = FALSE; /* get the identifier parameter */ validentifier = val_find_child(msg->rpc_input, AGT_STATE_MODULE, AGT_STATE_OBJ_IDENTIFIER); if (validentifier && validentifier->res == NO_ERR) { identifier = VAL_STR(validentifier); } /* get the version parameter */ valversion = val_find_child(msg->rpc_input, AGT_STATE_MODULE, AGT_STATE_OBJ_VERSION); if (valversion && valversion->res == NO_ERR) { version = VAL_STR(valversion); } /* get the format parameter */ valformat = val_find_child(msg->rpc_input, AGT_STATE_MODULE, AGT_STATE_OBJ_FORMAT); if (valformat && valformat->res == NO_ERR) { format = VAL_IDREF_NAME(valformat); nsid = VAL_IDREF_NSID(valformat); if (nsid != statemod->nsid) { res = ERR_NCX_INVALID_VALUE; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_VAL, valformat, NCX_NT_VAL, valformat); } } if (identifier == NULL) { /* should already be reported */ return ERR_NCX_MISSING_PARM; } /* set default format to YANG if needed */ if (format == NULL) { format = AGT_STATE_FORMAT_YANG; nsid = statemod->nsid; } /* check format parameter: only YANG and YIN supported for now */ if (!xml_strcmp(format, AGT_STATE_FORMAT_YANG)) { modformat = NCX_MODFORMAT_YANG; } else if (!xml_strcmp(format, AGT_STATE_FORMAT_YIN)) { modformat = NCX_MODFORMAT_YIN; } else { modformat = NCX_MODFORMAT_NONE; } if (nsid != statemod->nsid) { res = ERR_NCX_WRONG_NS; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_VAL, valformat, NCX_NT_VAL, valformat); } else if (modformat == NCX_MODFORMAT_NONE) { res = ERR_NCX_VALUE_NOT_SUPPORTED; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, format, NCX_NT_VAL, valformat); } /* check the identifier: must be valid module name */ if (!ncx_valid_name2(identifier)) { res = ERR_NCX_NO_MATCHES; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, identifier, NCX_NT_VAL, validentifier); } /* do not check the revision now * it must be an exact match if non-empty string */ if (version && !*version) { /* send NULL instead of empty string */ version = NULL; } if(0==strcmp(identifier,NCXMOD_IETF_NETCONF) && (version==NULL || 0==strcmp(version,NCXMOD_IETF_NETCONF_REVISION)) && modformat==NCX_MODFORMAT_YANG || modformat==NCX_MODFORMAT_NONE) { static char* mod_path=NULL; if(mod_path==NULL) { mod_path=ncxmod123_find_module_filespec(identifier, version); assert(mod_path!=NULL); } msg->rpc_user1 = mod_path; msg->rpc_user2 = (void *)NCX_MODFORMAT_YANG; msg->rpc_data_type = RPC_DATA_STD; msg->rpc_datacb = agt_output_schema; return NO_ERR; } /* client must specify a revision if the server has more than 1 */ if (version == NULL) { revcount = ncx_mod_revision_count(identifier); if (revcount > 1) { res = ERR_NCX_GET_SCHEMA_DUPLICATES; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } } /* find the module that is loaded; this is the only format * that is supported at this time; TBD: registry of filespecs * or URLs that are supported for each [sub]module * instead of just returning the format loaded into memory */ if (res == NO_ERR) { findmod = ncx_find_module(identifier, version); if (findmod) { source = xml_strdup(findmod->source); found = TRUE; } else { ncxmod_search_result_t *searchresult; searchresult = ncxmod_find_module(identifier, version); if (searchresult) { source = xml_strdup(searchresult->source); found = TRUE; } } if (found == FALSE) { val_value_t *errval = validentifier; res = ERR_NCX_NO_MATCHES; if (version != NULL) { findmod = ncx_find_module(identifier, NULL); if (findmod != NULL) { errval = valversion; } } agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_VAL, errval, NCX_NT_VAL, errval); } else { if (source == NULL) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, ERR_INTERNAL_MEM, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return ERR_INTERNAL_MEM; } /* check if format is correct for request */ switch (modformat) { case NCX_MODFORMAT_YANG: if (yang_fileext_is_yin(source)) { res = ERR_NCX_VALUE_NOT_SUPPORTED; } break; case NCX_MODFORMAT_YIN: if (yang_fileext_is_yang(source)) { res = ERR_NCX_VALUE_NOT_SUPPORTED; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, format, (valformat) ? NCX_NT_VAL : NCX_NT_NONE, valformat); } } } if (res == NO_ERR) { /* save the found module and format * setup the automatic output using the callback * function in agt_util.c */ msg->rpc_user1 = (void *)source; msg->rpc_user2 = (void *)NCX_MODFORMAT_YANG; msg->rpc_data_type = RPC_DATA_STD; msg->rpc_datacb = agt_output_schema; } return res; } /* get_schema_validate */ /************* E X T E R N A L F U N C T I O N S ***************/ /** * \fn agt_state_init * \brief Initialize the agent state monitor module data structures * \return status */ status_t agt_state_init (void) { agt_profile_t *agt_profile; status_t res; if (agt_state_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } log_debug2("\nagt: Loading netconf-state module"); agt_profile = agt_get_profile(); /* load the netconf-state module */ res = ncxmod_load_module(AGT_STATE_MODULE, NULL, &agt_profile->agt_savedevQ, &statemod); if (res != NO_ERR) { return res; } mysessionsval = NULL; myschemasval = NULL; mysessionobj = NULL; myschemaobj = NULL; agt_state_init_done = TRUE; return NO_ERR; } /* agt_state_init */ // ----------------------------------------------------------------------------! /** * \fn agt_state_init2 * \brief Initialize the monitoring data structures. This must be done * after the config is loaded * \return status */ status_t agt_state_init2 (void) { obj_template_t *topobj, *confsobj, *confobj; obj_template_t *sessionsobj, *statisticsobj; obj_template_t *schemasobj, *capsobj; val_value_t *topval, *capsval; val_value_t *confsval, *confval; val_value_t *sessionsval, *statisticsval; cfg_template_t *runningcfg; ncx_module_t *mod; status_t res; if (!agt_state_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } /* set up get-schema RPC operation */ res = agt_rpc_register_method(AGT_STATE_MODULE, AGT_STATE_GET_SCHEMA, AGT_RPC_PH_VALIDATE, get_schema_validate); if (res != NO_ERR) { return SET_ERROR(res); } runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (!runningcfg || !runningcfg->root) { return SET_ERROR(ERR_INTERNAL_VAL); } /* get all the object nodes first */ topobj = obj_find_template_top(statemod, AGT_STATE_MODULE, AGT_STATE_TOP_CONTAINER); if (!topobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } capsobj = obj_find_child(topobj, AGT_STATE_MODULE, AGT_STATE_OBJ_CAPABILITIES); if (!capsobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } confsobj = obj_find_child(topobj, AGT_STATE_MODULE, AGT_STATE_OBJ_DATASTORES); if (!confsobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } confobj = obj_find_child(confsobj, AGT_STATE_MODULE, AGT_STATE_OBJ_DATASTORE); if (!confobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } schemasobj = obj_find_child(topobj, AGT_STATE_MODULE, AGT_STATE_OBJ_SCHEMAS); if (!schemasobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } myschemaobj = obj_find_child(schemasobj, AGT_STATE_MODULE, AGT_STATE_OBJ_SCHEMA); if (!myschemaobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } sessionsobj = obj_find_child(topobj, AGT_STATE_MODULE, AGT_STATE_OBJ_SESSIONS); if (!sessionsobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } mysessionobj = obj_find_child(sessionsobj, AGT_STATE_MODULE, AGT_STATE_OBJ_SESSION); if (!mysessionobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } statisticsobj = obj_find_child(topobj, AGT_STATE_MODULE, AGT_STATE_OBJ_STATISTICS); if (!statisticsobj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* add /ietf-netconf-state */ topval = val_new_value(); if (!topval) { return ERR_INTERNAL_MEM; } val_init_from_template(topval, topobj); /* handing off the malloced memory here */ val_add_child_sorted(topval, runningcfg->root); /* add /ietf-netconf-state/capabilities virtual node */ capsval = val_new_value(); if (!capsval) { return ERR_INTERNAL_MEM; } val_init_virtual(capsval, get_caps, capsobj); val_add_child(capsval, topval); /* add /ietf-netconf-state/datastores */ confsval = val_new_value(); if (!confsval) { return ERR_INTERNAL_MEM; } val_init_from_template(confsval, confsobj); val_add_child(confsval, topval); /* add /ietf-netconf-state/datastores/datastore[1] */ if (agt_cap_std_set(CAP_STDID_CANDIDATE)) { confval = make_datastore_val(NCX_EL_CANDIDATE, confobj, &res); if (!confval) { return res; } val_add_child(confval, confsval); } /* add /ietf-netconf-state/datastores/datastore[2] */ confval = make_datastore_val(NCX_EL_RUNNING, confobj, &res); if (!confval) { return res; } val_add_child(confval, confsval); /* add /ietf-netconf-state/datastores/datastore[3] */ if (agt_cap_std_set(CAP_STDID_STARTUP)) { confval = make_datastore_val(NCX_EL_STARTUP, confobj, &res); if (!confval) { return res; } val_add_child(confval, confsval); } /* add /ietf-netconf-state/schemas */ myschemasval = val_new_value(); if (!myschemasval) { return ERR_INTERNAL_MEM; } val_init_from_template(myschemasval, schemasobj); val_add_child(myschemasval, topval); /* add all the /ietf-netconf-state/schemas/schema nodes */ for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { res = agt_state_add_module_schema(mod); if (res != NO_ERR) { return res; } } /* add /ietf-netconf-state/sessions */ sessionsval = val_new_value(); if (!sessionsval) { return ERR_INTERNAL_MEM; } val_init_from_template(sessionsval, sessionsobj); val_add_child(sessionsval, topval); mysessionsval = sessionsval; /* add /ietf-netconf-state/statistics */ statisticsval = make_statistics_val(statisticsobj, &res); if (!statisticsval) { return ERR_INTERNAL_MEM; } val_add_child(statisticsval, topval); return NO_ERR; } /* agt_state_init2 */ // ----------------------------------------------------------------------------! /** * \fn agt_state_cleanup * \brief Cleanup the module data structures * \return none */ void agt_state_cleanup (void) { if (agt_state_init_done) { statemod = NULL; mysessionsval = NULL; myschemasval = NULL; mysessionobj = NULL; myschemaobj = NULL; agt_rpc_unregister_method(AGT_STATE_MODULE, AGT_STATE_GET_SCHEMA); agt_state_init_done = FALSE; } } /* agt_state_cleanup */ // ----------------------------------------------------------------------------! /** * \fn agt_state_add_session * \brief Add a session entry to the netconf-state DM * \param scb session control block to use for the info * \return status */ status_t agt_state_add_session (ses_cb_t *scb) { val_value_t *session; status_t res; assert (scb && " param scb is NULL"); res = NO_ERR; session = make_session_val(scb, mysessionobj, &res); if (!session) { return res; } val_add_child(session, mysessionsval); return NO_ERR; } /* agt_state_add_session */ // ----------------------------------------------------------------------------! /** * \fn agt_state_remove_session * \brief Remove a session entry from the netconf-state DM * \param sid session ID to find and delete * \return none */ void agt_state_remove_session (ses_id_t sid) { val_value_t *sessionval, *idval; if (mysessionsval == NULL) { /* cleanup already done */ return; } for (sessionval = val_get_first_child(mysessionsval); sessionval != NULL; sessionval = val_get_next_child(sessionval)) { idval = val_find_child(sessionval, AGT_STATE_MODULE, AGT_STATE_OBJ_SESSIONID); if (!idval) { SET_ERROR(ERR_INTERNAL_VAL); } else if (VAL_UINT(idval) == sid) { dlq_remove(sessionval); val_free_value(sessionval); return; } } /* session already removed -- ignore the error */ } /* agt_state_remove_session */ // ----------------------------------------------------------------------------! /** * \fn agt_state_add_module_schema * \brief Add a schema entry to the netconf-state DM * \param mod module to add * \return status */ status_t agt_state_add_module_schema (ncx_module_t *mod) { val_value_t *schema; status_t res; assert ( mod && " param mod is NULL"); if (!agt_advertise_module_needed(mod->name)) { return NO_ERR; } res = NO_ERR; schema = make_schema_val(mod, myschemaobj, &res); if (!schema) { return res; } val_add_child(schema, myschemasval); if(0==strcmp(ncx_get_modname(mod), NCXMOD_NETCONF)) { /* ietf-netconf is overloaded internally */ val_value_t* newval; val_value_t* curval; /* identifier */ newval = agt_make_leaf(schema->obj, "identifier", NCXMOD_IETF_NETCONF, &res); assert(res==NO_ERR && newval); curval = val_find_child(schema, AGT_STATE_MODULE, "identifier"); assert(curval); val_swap_child(newval,curval); val_free_value(curval); /* version */ newval = agt_make_leaf(schema->obj, "version", NCXMOD_IETF_NETCONF_REVISION, &res); assert(res==NO_ERR && newval); curval = val_find_child(schema, AGT_STATE_MODULE, "version"); assert(curval); val_swap_child(newval,curval); val_free_value(curval); } return res; } /* agt_state_add_module_schema */ /* END file agt_state.c */ yuma123_2.14/netconf/src/agt/agt_util.c0000664000175000017500000030725014770023131020104 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_util.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24may06 abb begun; split out from agt_ncx.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cap.h" #include "agt_rpc.h" #include "agt_rpcerr.h" #include "agt_tree.h" #include "agt_util.h" #include "agt_xpath.h" #include "agt_val.h" #include "cfg.h" #include "dlq.h" #include "getcb.h" #include "log.h" #include "ncx.h" #include "ncxmod.h" #include "ncx_feature.h" #include "ncxconst.h" #include "obj.h" #include "op.h" #include "rpc.h" #include "rpc_err.h" #include "ses.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_msg.h" #include "xml_util.h" #include "xml_wr.h" #include "xpath.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ typedef struct agt_keywalker_parms_t_ { val_value_t *lastkey; val_value_t *retkey; boolean usenext; boolean done; } agt_keywalker_parms_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION is_default * * Check if the node is set to the default value * * INPUTS: * withdef == type of default requested * val == node to check * * RETURNS: * TRUE if set to the default value (by user or agent) * FALSE if no default applicable or not set to default *********************************************************************/ static boolean is_default (ncx_withdefaults_t withdef, val_value_t *val) { boolean retval; retval = FALSE; switch (withdef) { case NCX_WITHDEF_REPORT_ALL: case NCX_WITHDEF_REPORT_ALL_TAGGED: break; case NCX_WITHDEF_TRIM: retval = val_is_default(val); break; case NCX_WITHDEF_EXPLICIT: retval = val_set_by_default(val); break; case NCX_WITHDEF_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); } return retval; } /* is_default */ /******************************************************************** * FUNCTION check_withdef * * Check the with-defaults flag * * INPUTS: * withdef == requested with-defaults action * node == value node to check * * RETURNS: * TRUE if node should be output * FALSE if node is filtered out *********************************************************************/ static boolean check_withdef (ncx_withdefaults_t withdef, val_value_t *node) { const agt_profile_t *profile; boolean ret; ncx_withdefaults_t defwithdef; /* check if defaults are suppressed */ ret = TRUE; switch (withdef) { case NCX_WITHDEF_NONE: profile = agt_get_profile(); defwithdef = profile->agt_defaultStyleEnum; if (is_default(defwithdef, node)) { ret = FALSE; } break; case NCX_WITHDEF_REPORT_ALL: case NCX_WITHDEF_REPORT_ALL_TAGGED: case NCX_WITHDEF_TRIM: case NCX_WITHDEF_EXPLICIT: if (is_default(withdef, node)) { ret = FALSE; } break; default: SET_ERROR(ERR_INTERNAL_VAL); } return ret; } /* check_withdef */ /******************************************************************** * Make a default leaf and add it to the running config * * \param parentval parent complex type to add the default as the new last * child (could be cfg->root) * \param defobj object template for the default leaf * \param defstr string containing the default value to use * \return status *********************************************************************/ static status_t add_default_leaf( val_value_t *parentval, obj_template_t *defobj, const xmlChar *defstr ) { val_value_t *newval; status_t res = NO_ERR; newval = val_find_child( parentval, obj_get_mod_name(defobj), obj_get_name(defobj) ); if ( newval ) { /* node already exists */ return NO_ERR; } newval = val_make_simval_obj(defobj, defstr, &res); if ( !newval ) { return ( res == NO_ERR ? ERR_INTERNAL_PTR : res ); } if (res != NO_ERR) { val_free_value(newval); return res; } newval->flags |= VAL_FL_DEFSET; val_add_child(newval, parentval); return res; } /* add_default_leaf */ /******************************************************************** * Get the next expected key value in the ancestor chain * val_walker_fn prototype * * \param val the value node found in descendant search. * \param cookie1 a cookie value passed to start of walk. * \param cookie2 a cookie value passed to start of walk. * \return TRUE if walk should continue, FALSE if walk should terminate. */ static boolean get_key_value ( val_value_t *val, void *cookie1, void *cookie2 ) { assert( val && "val is NULL" ); assert( cookie1 && "val is NULL" ); (void)cookie2; agt_keywalker_parms_t *parms = (agt_keywalker_parms_t *)cookie1; if (parms->done) { return FALSE; } else if (parms->lastkey == NULL || parms->usenext) { parms->lastkey = val; parms->retkey = val; parms->done = TRUE; return FALSE; } else if (parms->lastkey == val) { parms->usenext = TRUE; return TRUE; } return TRUE; } /* get_key_value */ /******************************************************************** * Get the filter type * * \param filter the input filter * \return the type of filter * *********************************************************************/ static op_filtertyp_t get_filter_type( val_value_t* filter ) { /* setup the filter parameters */ val_value_t* filtertype = val_find_meta( filter, 0, NCX_EL_TYPE ); if ( !filtertype ) { /* should not happen; the default is subtree */ return OP_FILTER_SUBTREE; } return op_filtertyp_id( VAL_STR(filtertype) ); } /******************************************************************** * validate an xpath filter the filter type * * \param scb session control block * \param msg rpc_msg_t in progress * \param filter the filter element to use * \return the type of filter * *********************************************************************/ static status_t validate_xpath_filter( ses_cb_t* scb, rpc_msg_t* msg, val_value_t* filter ) { val_value_t *sel; if(0==strcmp(filter->name,"xpath-filter")) { sel = filter; } else { sel = val_find_meta(filter, 0, NCX_EL_SELECT); } status_t res = NO_ERR; if (!sel || !sel->xpathpcb) { res = ERR_NCX_MISSING_ATTRIBUTE; } else if (sel->xpathpcb->parseres != NO_ERR) { res = sel->xpathpcb->parseres; } if ( NO_ERR == res ) { msg->rpc_filter.op_filtyp = OP_FILTER_XPATH; msg->rpc_filter.op_filter = sel; } else { xml_attr_t selattr; memset(&selattr, 0x0, sizeof(xml_attr_t)); selattr.attr_ns = 0; selattr.attr_name = NCX_EL_SELECT; agt_record_attr_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, &selattr, NULL, NULL, NCX_NT_VAL, filter ); } return res; } /******************************************************************** * validate a subtree filter the filter type * * \param msg rpc_msg_t in progress * \param filter the filter element to use * \return the type of filter * *********************************************************************/ static status_t validate_subtree_filter( rpc_msg_t* msg, val_value_t* filter ) { msg->rpc_filter.op_filtyp = OP_FILTER_SUBTREE; msg->rpc_filter.op_filter = filter; return NO_ERR; } /************ E X T E R N A L F U N C T I O N S **************/ /******************************************************************** * FUNCTION agt_get_cfg_from_parm * * Get the cfg_template_t for the config in the target param * * INPUTS: * parmname == parameter to get from (e.g., target) * msg == incoming rpc_msg_t in progress * methnode == XML node for RPC method (for errors) * retcfg == address of return cfg pointer * * OUTPUTS: * *retcfg is set to the address of the cfg_template_t struct * RETURNS: * status *********************************************************************/ status_t agt_get_cfg_from_parm (const xmlChar *parmname, rpc_msg_t *msg, xml_node_t *methnode, cfg_template_t **retcfg) { agt_profile_t *profile; cfg_template_t *cfg; val_value_t *val; val_value_t *errval; const xmlChar *cfgname; status_t res; #ifdef DEBUG if (!parmname || !msg || !methnode || !retcfg) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif val = val_find_child(msg->rpc_input, val_get_mod_name(msg->rpc_input), parmname); if (!val || val->res != NO_ERR) { if (!val) { res = ERR_NCX_DEF_NOT_FOUND; } else { res = val->res; } agt_record_error(NULL, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, msg->rpc_input); return res; } errval = val; cfgname = NULL; cfg = NULL; res = NO_ERR; /* got some value in *val */ switch (val->btyp) { case NCX_BT_STRING: cfgname = VAL_STR(val); break; case NCX_BT_EMPTY: cfgname = val->name; break; case NCX_BT_CONTAINER: val = val_get_first_child(val); if (val) { switch (val->btyp) { case NCX_BT_STRING: if (!xml_strcmp(val->name, NCX_EL_URL)) { return ERR_NCX_FOUND_URL; } else { cfgname = VAL_STR(val); } break; case NCX_BT_EMPTY: cfgname = val->name; break; case NCX_BT_CONTAINER: if (!xml_strcmp(parmname, NCX_EL_SOURCE) && !xml_strcmp(val->name, NCX_EL_CONFIG)) { return ERR_NCX_FOUND_INLINE; } else { res = ERR_NCX_INVALID_VALUE; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } break; default: res = ERR_NCX_OPERATION_NOT_SUPPORTED; } if (cfgname != NULL && res == NO_ERR) { /* check if the param was given */ if (!xml_strcmp(cfgname, NCX_EL_URL)) { profile = agt_get_profile(); if (profile->agt_useurl) { return ERR_NCX_FOUND_URL; } else { res = ERR_NCX_OPERATION_NOT_SUPPORTED; } } else { /* get the config template from the config name */ cfg = cfg_get_config(cfgname); if (!cfg) { res = ERR_NCX_CFG_NOT_FOUND; } else { *retcfg = cfg; } } } if (res != NO_ERR) { agt_record_error(NULL, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, (cfgname) ? NCX_NT_STRING : NCX_NT_NONE, (const void *)cfgname, NCX_NT_VAL, errval); } return res; } /* agt_get_cfg_from_parm */ /******************************************************************** * FUNCTION agt_get_inline_cfg_from_parm * * Get the val_value_t node for the inline config element * * INPUTS: * parmname == parameter to get from (e.g., source) * msg == incoming rpc_msg_t in progress * methnode == XML node for RPC method (for errors) * retval == address of return value node pointer * * OUTPUTS: * *retval is set to the address of the val_value_t struct * * RETURNS: * status *********************************************************************/ status_t agt_get_inline_cfg_from_parm (const xmlChar *parmname, rpc_msg_t *msg, xml_node_t *methnode, val_value_t **retval) { val_value_t *val, *childval; val_value_t *errval; status_t res; #ifdef DEBUG if (!parmname || !msg || !methnode || !retval) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif val = val_find_child(msg->rpc_input, val_get_mod_name(msg->rpc_input), parmname); if (!val || val->res != NO_ERR) { if (!val) { res = ERR_NCX_DEF_NOT_FOUND; } else { res = val->res; } agt_record_error(NULL, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, msg->rpc_input); return res; } errval = val; res = NO_ERR; /* got some value in *val */ switch (val->btyp) { case NCX_BT_STRING: case NCX_BT_EMPTY: res = ERR_NCX_INVALID_VALUE; break; case NCX_BT_CONTAINER: childval = val_get_first_child(val); if (childval) { if (!xml_strcmp(childval->name, NCX_EL_CONFIG)) { *retval = childval; return NO_ERR; } else { errval = childval; res = ERR_NCX_INVALID_VALUE; } } else { res = ERR_NCX_INVALID_VALUE; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { agt_record_error(NULL, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, errval); } return res; } /* agt_get_inline_cfg_from_parm */ /******************************************************************** * FUNCTION agt_get_url_from_parm * * Get the URL string for the config in the target param * * INPUTS: * parmname == parameter to get from (e.g., target) * msg == incoming rpc_msg_t in progress * methnode == XML node for RPC method (for errors) * returl == address of return URL string pointer * retval == address of value node from input used * OUTPUTS: * *returl is set to the address of the URL string * pointing to the memory inside the found parameter * *retval is set to parm node found, if return NO_ERR * RETURNS: * status *********************************************************************/ status_t agt_get_url_from_parm (const xmlChar *parmname, rpc_msg_t *msg, xml_node_t *methnode, const xmlChar **returl, val_value_t **retval) { agt_profile_t *profile; val_value_t *val; val_value_t *errval; status_t res; #ifdef DEBUG if (!parmname || !msg || !methnode || !returl || !retval) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif *retval = NULL; val = val_find_child(msg->rpc_input, val_get_mod_name(msg->rpc_input), parmname); if (!val || val->res != NO_ERR) { if (!val) { res = ERR_NCX_MISSING_PARM; } else { res = val->res; } agt_record_error(NULL, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, msg->rpc_input); return res; } errval = val; res = NO_ERR; /* got some value in *val */ switch (val->btyp) { case NCX_BT_STRING: if (xml_strcmp(parmname, NCX_EL_URL)) { res = ERR_NCX_INVALID_VALUE; } else { *returl = VAL_STR(val); } break; case NCX_BT_EMPTY: res = ERR_NCX_INVALID_VALUE; break; case NCX_BT_CONTAINER: val = val_get_first_child(val); if (val) { errval = val; switch (val->btyp) { case NCX_BT_STRING: if (xml_strcmp(val->name, NCX_EL_URL)) { res = ERR_NCX_INVALID_VALUE; } else { *returl = VAL_STRING(val); } break; case NCX_BT_EMPTY: res = ERR_NCX_INVALID_VALUE; break; case NCX_BT_CONTAINER: res = ERR_NCX_INVALID_VALUE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } break; default: res = ERR_NCX_OPERATION_NOT_SUPPORTED; } if (res == NO_ERR) { /* check if the param was given */ profile = agt_get_profile(); if (!profile->agt_useurl) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; *returl = NULL; } } if (res != NO_ERR) { agt_record_error(NULL, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, errval); } else { *retval = errval; } return res; } /* agt_get_url_from_parm */ /******************************************************************** * FUNCTION agt_get_filespec_from_url * * Check the URL and get the filespec part out of it * * INPUTS: * urlstr == URL to check * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced URL string; must be freed by caller!! * NULL if some error *********************************************************************/ xmlChar * agt_get_filespec_from_url (const xmlChar *urlstr, status_t *res) { const xmlChar *str; xmlChar *retstr; uint32 schemelen, urlstrlen; #ifdef DEBUG if (urlstr == NULL || res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif schemelen = xml_strlen(AGT_FILE_SCHEME); urlstrlen = xml_strlen(urlstr); if (urlstrlen <= (schemelen+1)) { *res = ERR_NCX_INVALID_VALUE; return NULL; } /* only the file scheme file:///foospec is supported at this time */ if (xml_strncmp(urlstr, AGT_FILE_SCHEME, schemelen)) { *res = ERR_NCX_INVALID_VALUE; return NULL; } /* convert URL to a regular string */ /****/ /* check for whitespace and other chars */ str = &urlstr[schemelen]; while (*str) { // if (xml_isspace(*str) || *str==';' /*|| *str==NCXMOD_PSCHAR*/) { if (xml_isspace(*str) || *str==';' || *str==NCXMOD_PSCHAR) { *res = ERR_NCX_INVALID_VALUE; return NULL; } str++; } retstr = xml_strdup(&urlstr[schemelen]); if (retstr == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } *res = NO_ERR; return retstr; } /* agt_get_filespec_from_url */ /******************************************************************** * FUNCTION agt_get_parmval * * Get the identified val_value_t for a given parameter * Used for error purposes! * INPUTS: * parmname == parameter to get * msg == incoming rpc_msg_t in progress * * RETURNS: * status *********************************************************************/ const val_value_t * agt_get_parmval (const xmlChar *parmname, rpc_msg_t *msg) { val_value_t *val; val = val_find_child(msg->rpc_input, val_get_mod_name(msg->rpc_input), parmname); return val; } /* agt_get_parmval */ /******************************************************************** * FUNCTION agt_record_error * * Generate an rpc_err_rec_t and save it in the msg * * INPUTS: * scb == session control block * == NULL and no stats will be recorded * msghdr == XML msg header with error Q * == NULL, no errors will be recorded! * layer == netconf layer error occured * res == internal error code * xmlnode == XML node causing error * == NULL if not available * parmtyp == type of node in 'error_info' * error_info == error data, specific to 'res' * == NULL if not available (then nodetyp ignored) * nodetyp == type of node in 'error_path' * error_path == internal data node with the error * == NULL if not available or not used * OUTPUTS: * errQ has error message added if no malloc errors * scb->stats may be updated if scb non-NULL * * RETURNS: * none *********************************************************************/ void agt_record_error (ses_cb_t *scb, xml_msg_hdr_t *msghdr, ncx_layer_t layer, status_t res, const xml_node_t *xmlnode, ncx_node_t parmtyp, const void *error_info, ncx_node_t nodetyp, void *error_path) { agt_record_error_errinfo(scb, msghdr, layer, res, xmlnode, parmtyp, error_info, nodetyp, error_path, NULL); } /* agt_record_error */ /******************************************************************** * FUNCTION agt_record_error_errinfo * * Generate an rpc_err_rec_t and save it in the msg * Use the provided error info record for fields * * INPUTS: * scb == session control block * == NULL and no stats will be recorded * msghdr == XML msg header with error Q * == NULL, no errors will be recorded! * layer == netconf layer error occured * res == internal error code * xmlnode == XML node causing error * == NULL if not available * parmtyp == type of node in 'error_info' * error_info == error data, specific to 'res' * == NULL if not available (then nodetyp ignored) * nodetyp == type of node in 'error_path' * error_path == internal data node with the error * == NULL if not available or not used * errinfo == error info record to use * * OUTPUTS: * errQ has error message added if no malloc errors * scb->stats may be updated if scb non-NULL * RETURNS: * none *********************************************************************/ void agt_record_error_errinfo (ses_cb_t *scb, xml_msg_hdr_t *msghdr, ncx_layer_t layer, status_t res, const xml_node_t *xmlnode, ncx_node_t parmtyp, const void *error_info, ncx_node_t nodetyp, void *error_path, const ncx_errinfo_t *errinfo) { rpc_err_rec_t *err = NULL; dlq_hdr_t *errQ = (msghdr) ? &msghdr->errQ : NULL; xmlChar *pathbuff = NULL; /* dump some error info to the log */ if (LOGDEBUG3) { log_debug3("\nagt_record_error for session %u:", scb ? SES_MY_SID(scb) : 0); if (xmlnode) { if (xmlnode->qname) { log_debug3(" xml: %s", xmlnode->qname); } else { log_debug3(" xml: %s:%s", xmlns_get_ns_prefix(xmlnode->nsid), xmlnode->elname ? xmlnode->elname : (const xmlChar *)"--"); } } if (nodetyp == NCX_NT_VAL && error_path) { const val_value_t *logval = (const val_value_t *)error_path; log_debug3(" error-path object:"); if (obj_is_root(logval->obj)) { log_debug3(" root\n"); } else { log_debug3(" <%s:%s>\n", val_get_mod_name(logval), logval->name); } } } /* generate an error only if there is a Q to hold the result */ if (errQ) { /* get the error-path */ if (error_path) { status_t res2; switch (nodetyp) { case NCX_NT_STRING: pathbuff = xml_strdup((const xmlChar *)error_path); break; case NCX_NT_VAL: if(res == ERR_NCX_WRONG_NODETYP && ((val_value_t *)error_path)->obj->objtype == OBJ_TYP_LEAF_LIST) { log_error("\nError: To generate valid instance-id is impossible for leaf-list value " "parsed with ERR_NCX_WRONG_NODETYP since the value self " "is needed as key in the instance-id"); break; } res2 = val_gen_instance_id_ex(msghdr, (val_value_t *)error_path, NCX_IFMT_XPATH1, FALSE, &pathbuff); if (res2 != NO_ERR) { log_error("\nError: Generate instance id failed (%s)", get_error_string(res2)); } break; case NCX_NT_OBJ: res2 = obj_gen_object_id(error_path, &pathbuff); if (res2 != NO_ERR) { log_error("\nError: Generate object id failed (%s)", get_error_string(res2)); } break; default: SET_ERROR(ERR_INTERNAL_VAL); } } err = agt_rpcerr_gen_error_ex(layer, res, xmlnode, parmtyp, error_info, pathbuff, errinfo, nodetyp, error_path); if (err) { /* pass off pathbuff memory here */ dlq_enque(err, errQ); } else { if (pathbuff) { m__free(pathbuff); } } } } /* agt_record_error_errinfo */ /******************************************************************** * FUNCTION agt_record_attr_error * * Generate an rpc_err_rec_t and save it in the msg * * INPUTS: * scb == session control block * msghdr == XML msg header with error Q * == NULL, no errors will be recorded! * layer == netconf layer error occured * res == internal error code * xmlattr == XML attribute node causing error * (NULL if not available) * xmlnode == XML node containing the attr * badns == bad namespace string value * nodetyp == type of node in 'errnode' * errnode == internal data node with the error * == NULL if not used * * OUTPUTS: * errQ has error message added if no malloc errors * * RETURNS: * none *********************************************************************/ void agt_record_attr_error (ses_cb_t *scb, xml_msg_hdr_t *msghdr, ncx_layer_t layer, status_t res, const xml_attr_t *xmlattr, const xml_node_t *xmlnode, const xmlChar *badns, ncx_node_t nodetyp, const void *errnode) { rpc_err_rec_t *err; xmlChar *buff; dlq_hdr_t *errQ; const val_value_t *errnodeval; (void)scb; errQ = (msghdr) ? &msghdr->errQ : NULL; errnodeval = NULL; if (errQ) { buff = NULL; if (errnode) { if (nodetyp==NCX_NT_STRING) { buff = xml_strdup((const xmlChar *)errnode); } else if (nodetyp==NCX_NT_VAL) { errnodeval = (const val_value_t *)errnode; status_t res2 = val_gen_instance_id_ex(msghdr, errnodeval, NCX_IFMT_XPATH1, FALSE, &buff); if (res2 != NO_ERR) { log_error("\nError: Generate instance id failed (%s)", get_error_string(res2)); } } } err = agt_rpcerr_gen_attr_error(layer, res, xmlattr, xmlnode, errnodeval, badns, buff); if (err) { dlq_enque(err, errQ); } else { if (buff) { m__free(buff); } } } } /* agt_record_attr_error */ /******************************************************************** * FUNCTION agt_record_insert_error * * Generate an rpc_err_rec_t and save it in the msg * Use the provided error info record for fields * * For YANG 'missing-instance' error-app-tag * * INPUTS: * scb == session control block * == NULL and no stats will be recorded * msghdr == XML msg header with error Q * == NULL, no errors will be recorded! * res == internal error code * errval == value node generating the insert error * * OUTPUTS: * errQ has error message added if no malloc errors * scb->stats may be updated if scb non-NULL * RETURNS: * none *********************************************************************/ void agt_record_insert_error (ses_cb_t *scb, xml_msg_hdr_t *msghdr, status_t res, val_value_t *errval) { rpc_err_rec_t *err; dlq_hdr_t *errQ; xmlChar *pathbuff; #ifdef DEBUG if (!errval) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif errQ = (msghdr) ? &msghdr->errQ : NULL; /* dump some error info to the log */ if (LOGDEBUG3) { log_debug3("\nagt_record_insert_error: "); val_dump_value(errval, (scb) ? ses_indent_count(scb) : NCX_DEF_INDENT); log_debug3("\n"); } /* generate an error only if there is a Q to hold the result */ if (errQ) { /* get the error-path */ pathbuff = NULL; status_t res2 = val_gen_instance_id_ex(msghdr, errval, NCX_IFMT_XPATH1, FALSE, &pathbuff); err = agt_rpcerr_gen_insert_error(NCX_LAYER_CONTENT, res, errval, (res2 == NO_ERR) ? pathbuff : NULL); if (err) { dlq_enque(err, errQ); } else { if (pathbuff) { m__free(pathbuff); } } } } /* agt_record_insert_error */ /******************************************************************** * FUNCTION agt_record_unique_error * * Generate an rpc_err_rec_t and save it in the msg * Use the provided error info record for fields * * For YANG 'data-not-unique' error-app-tag * * INPUTS: * scb == session control block * == NULL and no stats will be recorded * msghdr == XML msg header with error Q * == NULL, no errors will be recorded! * errval == list value node that contains the unique-stmt * valuniqueQ == Q of val_unique_t structs for error-info * * OUTPUTS: * errQ has error message added if no malloc errors * scb->stats may be updated if scb non-NULL * RETURNS: * none *********************************************************************/ void agt_record_unique_error (ses_cb_t *scb, xml_msg_hdr_t *msghdr, val_value_t *errval, dlq_hdr_t *valuniqueQ) { rpc_err_rec_t *err; dlq_hdr_t *errQ; xmlChar *pathbuff; status_t interr; #ifdef DEBUG if (!errval) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif interr = ERR_NCX_UNIQUE_TEST_FAILED; errQ = (msghdr) ? &msghdr->errQ : NULL; /* dump some error info to the log */ if (LOGDEBUG3) { log_debug3("\nagt_record_unique_error: "); val_dump_value(errval, (scb) ? ses_indent_count(scb) : NCX_DEF_INDENT); log_debug3("\n"); } /* generate an error only if there is a Q to hold the result */ if (errQ) { /* get the error-path */ pathbuff = NULL; /* this instance ID is never sourced from the * so the plain 'stop_at_root' variant is used here */ status_t res = val_gen_instance_id(msghdr, errval, NCX_IFMT_XPATH1, &pathbuff); err = agt_rpcerr_gen_unique_error(msghdr, NCX_LAYER_CONTENT, interr, valuniqueQ, (res == NO_ERR) ? pathbuff : NULL); if (err) { dlq_enque(err, errQ); } else { if (pathbuff) { m__free(pathbuff); } } } } /* agt_record_unique_error */ /******************************************************************** * Validate the parameter if present. * If the filter is valid msg->rpc_filter is filled in; * * \param scb session control block * \param msg rpc_msg_t in progress * \return status *********************************************************************/ status_t agt_validate_filter ( ses_cb_t *scb, rpc_msg_t *msg ) { val_value_t *filter=NULL; assert( scb && "scb is NULL" ); assert( msg && "msg is NULL" ); if(0!=strcmp(obj_get_name(msg->rpc_method),"get-data")) { /* filter parm is optional */ filter = val_find_child( msg->rpc_input, NC_MODULE, NCX_EL_FILTER ); } else { /* filter parm is optional */ val_value_t *xpath_filter; val_value_t *subtree_filter; xpath_filter = val_find_child( msg->rpc_input, "ietf-netconf-datastores", "xpath-filter"); subtree_filter = val_find_child( msg->rpc_input, "ietf-netconf-datastores", "subtree-filter"); if(xpath_filter) { filter = xpath_filter; } else if(subtree_filter) { filter = subtree_filter; } } if (!filter) { msg->rpc_filter.op_filtyp = OP_FILTER_NONE; msg->rpc_filter.op_filter = NULL; return NO_ERR; /* not an error */ } return agt_validate_filter_ex( scb, msg, filter ); } /* agt_validate_filter */ /******************************************************************** * Validate the parameter if present * If the filter is valid msg->rpc_filter is filled in; * * \param scb session control block * \param msg rpc_msg_t in progress * \param filter the filter element to use * \return status *********************************************************************/ status_t agt_validate_filter_ex( ses_cb_t *scb, rpc_msg_t *msg, val_value_t *filter ) { assert( scb && "scb is NULL" ); assert( msg && "msg is NULL" ); /* filter parm is optional */ if ( !filter || filter->res != NO_ERR ) { return NO_ERR; } status_t res = NO_ERR; if(0==strcmp(filter->name,NCX_EL_FILTER)) { op_filtertyp_t filtyp = get_filter_type( filter );; /* check if the select attribute is needed */ switch (filtyp) { case OP_FILTER_SUBTREE: res = validate_subtree_filter( msg, filter ); break; case OP_FILTER_XPATH: res = validate_xpath_filter( scb, msg, filter ); break; default: res = ERR_NCX_INVALID_VALUE; agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, NULL, NCX_NT_NONE, NULL, NCX_NT_VAL, filter ); } } else { if(0==strcmp(filter->name,"subtree-filter")) { res = validate_subtree_filter( msg, filter); } else if(0==strcmp(filter->name,"xpath-filter")) { res = validate_xpath_filter( scb, msg, filter); } else { assert(0); } } if ( NO_ERR == res && LOGDEBUG3 ) { log_debug3("\nagt_util_validate_filter:"); val_dump_value(msg->rpc_input, 0); } return res; } /* agt_validate_filter_ex */ /******************************************************************** * val_nodetest_fn_t callback * Used by the operation to return any type of * configuration data * * \param withdef - see ncx/val_util.h (val_nodetest_fn_t) * \param realtest - see ncx/val_util.h (val_nodetest_fn_t) * \param node - see ncx/val_util.h (val_nodetest_fn_t) * \return * TRUE if config; FALSE if non-config *********************************************************************/ boolean agt_check_config ( ncx_withdefaults_t withdef, boolean realtest, val_value_t *node ) { if (realtest) { if (node->dataclass == NCX_DC_CONFIG) { return check_withdef(withdef, node); } /* not a node that should be saved with a copy-config to NVRAM */ return FALSE; } if (node->obj) { return obj_is_config(node->obj); } return TRUE; } /* agt_check_config */ /******************************************************************** * FUNCTION agt_check_default * * val_nodetest_fn_t callback * * Used by the operation to return only values * not set to the default * * INPUTS: * see ncx/val_util.h (val_nodetest_fn_t) * * RETURNS: * status *********************************************************************/ boolean agt_check_default (ncx_withdefaults_t withdef, boolean realtest, val_value_t *node) { boolean ret; ret = TRUE; /* check if defaults are suppressed */ if (realtest) { if (is_default(withdef, node)) { ret = FALSE; } } return ret; } /* agt_check_default */ /******************************************************************** * FUNCTION agt_check_save * * val_nodetest_fn_t callback * * Used by agt_ncx_cfg_save function to filter just what * is supposed to be saved in the config file * * INPUTS: * see ncx/val_util.h (val_nodetest_fn_t) * * RETURNS: * status *********************************************************************/ boolean agt_check_save (ncx_withdefaults_t withdef, boolean realtest, val_value_t *node) { boolean ret; ret = TRUE; if (realtest) { if (node->dataclass==NCX_DC_CONFIG) { if (is_default(withdef, node)) { ret = FALSE; } } else { /* not a node that should be saved with a copy-config * to NVRAM */ ret = FALSE; } } else if (node->obj != NULL) { ret = obj_is_config(node->obj); } return ret; } /* agt_check_save */ /******************************************************************** * FUNCTION agt_output_filter * * output the proper data for the get or get-config operation * generate the data that matched the subtree or XPath filter * * INPUTS: * see rpc/agt_rpc.h (agt_rpc_data_cb_t) * RETURNS: * status *********************************************************************/ status_t agt_output_filter (ses_cb_t *scb, rpc_msg_t *msg, int32 indent) { cfg_template_t *source; ncx_filptr_t *top; boolean getop=FALSE; boolean rpc_is_get=FALSE; boolean rpc_is_get_data=FALSE; status_t res; rpc_is_get = !xml_strcmp(obj_get_name(msg->rpc_method), NCX_EL_GET); rpc_is_get_data = !xml_strcmp(obj_get_name(msg->rpc_method), "get-data"); if(rpc_is_get || rpc_is_get_data) { getop = TRUE; } if (rpc_is_get) { source = cfg_get_config_id(NCX_CFGID_RUNNING); } else { source = (cfg_template_t *)msg->rpc_user1; } if (!source) { return SET_ERROR(ERR_INTERNAL_PTR); } if (source->root == NULL) { /* if a database was deleted, it will * have a NULL root until it gets replaced */ return NO_ERR; } res = NO_ERR; switch (msg->rpc_filter.op_filtyp) { case OP_FILTER_NONE: switch (msg->mhdr.withdef) { case NCX_WITHDEF_REPORT_ALL: case NCX_WITHDEF_REPORT_ALL_TAGGED: /* return everything */ if (getop) { /* all config and state data */ xml_wr_val(scb, &msg->mhdr, source->root, indent); } else { /* all config nodes */ xml_wr_check_val(scb, &msg->mhdr, source->root, indent, agt_check_config); } break; case NCX_WITHDEF_TRIM: case NCX_WITHDEF_EXPLICIT: /* with-defaults=false: return only non-defaults */ if (getop) { /* all non-default config and state data */ xml_wr_check_val(scb, &msg->mhdr, source->root, indent, agt_check_default); } else { /* all non-default config data */ xml_wr_check_val(scb, &msg->mhdr, source->root, indent, agt_check_config); } break; case NCX_WITHDEF_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); } break; case OP_FILTER_SUBTREE: if (source->root) { top = agt_tree_prune_filter(scb, msg, source, getop); if (top) { agt_tree_output_filter(scb, msg, top, indent, getop); ncx_free_filptr(top); break; } } break; case OP_FILTER_XPATH: if (source->root) { res = agt_xpath_output_filter(scb, msg, source, getop, indent); } break; default: res = SET_ERROR(ERR_INTERNAL_PTR); } return res; } /* agt_output_filter */ /******************************************************************** * FUNCTION agt_output_schema * * generate the YANG file contents for the get-schema operation * * INPUTS: * see rpc/agt_rpc.h (agt_rpc_data_cb_t) * RETURNS: * status *********************************************************************/ status_t agt_output_schema (ses_cb_t *scb, rpc_msg_t *msg, int32 indent) { ncx_module_t *findmod; FILE *fil; char *buffer; boolean done; status_t res; buffer = m__getMem(NCX_MAX_LINELEN+1); if (!buffer) { return ERR_INTERNAL_MEM; } memset(buffer, 0x0, NCX_MAX_LINELEN+1); //findmod = (ncx_module_t *)msg->rpc_user1; /*** ignoring the format for now; assume YANG ***/ res = NO_ERR; fil = fopen((const char *)msg->rpc_user1, "r"); if (fil) { ses_putstr(scb, (const xmlChar *)"\n"); done = FALSE; while (!done) { if (fgets(buffer, NCX_MAX_LINELEN, fil)) { ses_putcstr(scb, (const xmlChar *)buffer, indent); } else { fclose(fil); done = TRUE; } } } else { res = ERR_FIL_OPEN; } m__free(msg->rpc_user1); m__free(buffer); msg->rpc_user1 = NULL; return res; } /* agt_output_schema */ /******************************************************************** * FUNCTION agt_output_empty * * output no data for the get or get-config operation * because the if-modified-since fileter did not pass * * INPUTS: * see rpc/agt_rpc.h (agt_rpc_data_cb_t) * RETURNS: * status *********************************************************************/ status_t agt_output_empty (ses_cb_t *scb, rpc_msg_t *msg, int32 indent) { scb = scb; /* not used warning */ msg = msg; /* not used warning */ indent = indent; /* not used warning */ return NO_ERR; } /* agt_output_empty */ /******************************************************************** * FUNCTION agt_check_max_access * * Check if the max-access for a parameter is exceeded * * INPUTS: * newval == value node from PDU to check * cur_exists == TRUE if the corresponding node in the target exists * RETURNS: * status *********************************************************************/ status_t agt_check_max_access (val_value_t *newval, boolean cur_exists) { op_editop_t op; ncx_access_t acc; boolean iskey; if (newval == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } op = newval->editop; acc = obj_get_max_access(newval->obj); iskey = obj_is_key(newval->obj); switch (op) { case OP_EDITOP_NONE: return NO_ERR; case OP_EDITOP_MERGE: switch (acc) { case NCX_ACCESS_NONE: return ERR_NCX_NO_ACCESS_MAX; case NCX_ACCESS_RO: return ERR_NCX_ACCESS_READ_ONLY; case NCX_ACCESS_RW: /* edit but not create is allowed */ return (cur_exists) ? NO_ERR : ERR_NCX_NO_ACCESS_MAX; case NCX_ACCESS_RC: return NO_ERR; default: return SET_ERROR(ERR_INTERNAL_VAL); } case OP_EDITOP_REPLACE: switch (acc) { case NCX_ACCESS_NONE: return ERR_NCX_NO_ACCESS_MAX; case NCX_ACCESS_RO: return ERR_NCX_ACCESS_READ_ONLY; case NCX_ACCESS_RW: return (cur_exists) ? NO_ERR : ERR_NCX_NO_ACCESS_MAX; case NCX_ACCESS_RC: return NO_ERR; default: return SET_ERROR(ERR_INTERNAL_VAL); } case OP_EDITOP_CREATE: switch (acc) { case NCX_ACCESS_NONE: return ERR_NCX_NO_ACCESS_MAX; case NCX_ACCESS_RO: return ERR_NCX_ACCESS_READ_ONLY; case NCX_ACCESS_RW: return ERR_NCX_NO_ACCESS_MAX; case NCX_ACCESS_RC: /* create/delete allowed */ return NO_ERR; default: return SET_ERROR(ERR_INTERNAL_VAL); } case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: switch (acc) { case NCX_ACCESS_NONE: return ERR_NCX_NO_ACCESS_MAX; case NCX_ACCESS_RO: return ERR_NCX_ACCESS_READ_ONLY; case NCX_ACCESS_RW: return ERR_NCX_NO_ACCESS_MAX; case NCX_ACCESS_RC: /* create/delete allowed */ if (iskey) { if (newval->parent) { op_editop_t pop = newval->parent->editop; if (pop == OP_EDITOP_DELETE || pop == OP_EDITOP_REMOVE) { /* delete operation is derived from parent */ return NO_ERR; } else { /* deleting the key leaf but not the parent */ return ERR_NCX_NO_ACCESS_MAX; } } else { /* key leaf with no parent?? */ return SET_ERROR(ERR_INTERNAL_VAL); } } return NO_ERR; default: return SET_ERROR(ERR_INTERNAL_VAL); } case OP_EDITOP_LOAD: /* allow for agent loading of read-write objects */ switch (acc) { case NCX_ACCESS_NONE: return ERR_NCX_NO_ACCESS_MAX; case NCX_ACCESS_RO: return ERR_NCX_ACCESS_READ_ONLY; case NCX_ACCESS_RW: case NCX_ACCESS_RC: /* create/edit/delete allowed */ return NO_ERR; default: return SET_ERROR(ERR_INTERNAL_VAL); } default: return SET_ERROR(ERR_INTERNAL_VAL); } /*NOTREACHED*/ } /* agt_check_max_access */ /******************************************************************** * FUNCTION agt_check_editop * * Check if the edit operation is okay * * INPUTS: * pop == parent edit operation in affect * Starting at the data root, the parent edit-op * is derived from the default-operation parameter * cop == address of child operation (MAY BE ADJUSTED ON EXIT!!!) * This is the edit-op field in the new node corresponding * to the curnode position in the data model * newnode == pointer to new node in the edit-config PDU * curnode == pointer to the current node in the data model * being affected by this operation, if any. * == NULL if no current node exists * iqual == effective instance qualifier for this value * proto == protocol in use * * OUTPUTS: * *cop may be adjusted to simplify further processing, * based on the following reduction algorithm: * * create, replace, and delete operations are 'sticky'. * Once set, any nested edit-ops must be valid * within the context of the fixed operation (create or delete) * and the child operation gets changed to the sticky parent edit-op * * if the parent is create or delete, and the child * is merge or replace, then the child can be ignored * and will be changed to the parent, since the create * or delete must be carried through all the way to the leaves * * RETURNS: * status *********************************************************************/ status_t agt_check_editop (op_editop_t pop, op_editop_t *cop, val_value_t *newnode, val_value_t *curnode, ncx_iqual_t iqual, ncx_protocol_t proto) { status_t res; agt_profile_t *profile; res = NO_ERR; profile = agt_get_profile(); /* adjust the child if it has not been set * this heuristic saves the real operation on the way down * the tree as each node is checked for a new operation value * * This will either be the default-operation or a value * set in an anscestor node with an operation attribute * * If this is the internal startup mode (OP_EDITOP_LOAD) * then the load edit-op will get set and quick exit */ if (*cop==OP_EDITOP_NONE) { *cop = pop; } /* check remove allowed */ if (*cop == OP_EDITOP_REMOVE || pop == OP_EDITOP_REMOVE) { if (proto != NCX_PROTO_NETCONF11) { return ERR_NCX_PROTO11_NOT_ENABLED; } } /* check the child editop against the parent editop */ switch (*cop) { case OP_EDITOP_NONE: /* no operation set in the child or the parent yet * need to check the entire subtree from this node * to see if any operations set at all; if so then * it matters if the corresponding node exists * at this level of the data tree */ if (curnode) { res = NO_ERR; } else if (newnode) { if (agt_any_operations_set(newnode)) { res = ERR_NCX_DATA_MISSING; } else { res = NO_ERR; } } else { /* should not happen */ res = NO_ERR; } break; case OP_EDITOP_MERGE: case OP_EDITOP_REPLACE: switch (pop) { case OP_EDITOP_NONE: /* this child contains the merge or replace operation * attribute; which may be an index node; although * loose from a DB API POV, NETCONF will allow an * entry to be renamed via a merge or replace edit-op */ break; case OP_EDITOP_MERGE: case OP_EDITOP_REPLACE: /* merge or replace inside merge or replace is okay */ break; case OP_EDITOP_CREATE: /* a merge or replace within a create is okay * but it is really a create because the parent * operation is an explicit create, so the current * node is not allowed to exist yet, unless multiple * instances of the node are allowed */ *cop = OP_EDITOP_CREATE; if (curnode) { switch (iqual) { case NCX_IQUAL_ONE: case NCX_IQUAL_OPT: switch (profile->agt_defaultStyleEnum) { case NCX_WITHDEF_REPORT_ALL: res = ERR_NCX_DATA_EXISTS; break; case NCX_WITHDEF_TRIM: if (!val_is_default(curnode)) { res = ERR_NCX_DATA_EXISTS; } break; case NCX_WITHDEF_EXPLICIT: if (!val_set_by_default(curnode)) { res = ERR_NCX_DATA_EXISTS; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; default: ; } } break; case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: /* this is an error since the merge or replace * cannot be performed and its parent node is * also getting deleted at the same time */ res = ERR_NCX_DATA_MISSING; break; case OP_EDITOP_LOAD: /* LOAD op not allowed here */ res = ERR_NCX_BAD_ATTRIBUTE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case OP_EDITOP_CREATE: /* the child op is an explicit create * the current node cannot exist unless multiple * instances are allowed; depends on the defaults style */ if (curnode) { switch (curnode->obj->objtype) { case OBJ_TYP_LEAF: switch (profile->agt_defaultStyleEnum) { case NCX_WITHDEF_REPORT_ALL: res = ERR_NCX_DATA_EXISTS; break; case NCX_WITHDEF_TRIM: if (!val_is_default(curnode)) { res = ERR_NCX_DATA_EXISTS; } break; case NCX_WITHDEF_EXPLICIT: if (!val_set_by_default(curnode)) { res = ERR_NCX_DATA_EXISTS; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; default: res = ERR_NCX_DATA_EXISTS; } if (res != NO_ERR) { return res; } } /* check the create op against the parent edit-op */ switch (pop) { case OP_EDITOP_NONE: /* make sure the create edit-op is in a correct place */ res = (val_create_allowed(newnode)) ? NO_ERR : ERR_NCX_OPERATION_FAILED; break; case OP_EDITOP_MERGE: case OP_EDITOP_REPLACE: /* create within merge or replace okay since these * operations silently create any missing nodes * and the curnode test already passed */ break; case OP_EDITOP_CREATE: /* create inside create okay */ break; case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: /* create inside a delete is an error */ res = ERR_NCX_DATA_MISSING; break; case OP_EDITOP_LOAD: /* LOAD op not allowed here */ res = ERR_NCX_BAD_ATTRIBUTE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case OP_EDITOP_REMOVE: case OP_EDITOP_DELETE: /* explicit delete means the current node must exist * unlike a remove which removes nodes if they exist, * without any error checking for curnode exists */ if (curnode == NULL) { if (*cop == OP_EDITOP_DELETE) { /* delete on non-existing node is always an error */ res = ERR_NCX_DATA_MISSING; } } else { /* check if the node is really present for delete */ switch (profile->agt_defaultStyleEnum) { case NCX_WITHDEF_REPORT_ALL: break; case NCX_WITHDEF_TRIM: if (*cop == OP_EDITOP_DELETE && val_is_default(curnode)) { res = ERR_NCX_DATA_MISSING; } break; case NCX_WITHDEF_EXPLICIT: if (*cop == OP_EDITOP_DELETE && val_set_by_default(curnode)) { res = ERR_NCX_DATA_MISSING; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { return res; } /* check the delete against the parent edit-op */ switch (pop) { case OP_EDITOP_NONE: res = (val_delete_allowed(curnode)) ? NO_ERR : ERR_NCX_BAD_ATTRIBUTE; break; case OP_EDITOP_MERGE: /* delete within merge or ok */ break; case OP_EDITOP_REPLACE: /* this is a corner case; delete within a replace * the application could have just left this node * out instead, but allow this form too */ break; case OP_EDITOP_CREATE: /* create within a delete always an error */ res = ERR_NCX_DATA_MISSING; break; case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: /* delete within delete always okay */ break; case OP_EDITOP_LOAD: /* LOAD op not allowed here */ res = ERR_NCX_BAD_ATTRIBUTE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } break; case OP_EDITOP_LOAD: if (pop != OP_EDITOP_LOAD) { res = ERR_NCX_BAD_ATTRIBUTE; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* agt_check_editop */ /******************************************************************** * FUNCTION agt_enable_feature * * Enable a YANG feature in the agent * This will not be detected by any sessions in progress!!! * It will take affect the next time a message * is sent by the agent * * INPUTS: * modname == module name containing the feature * featurename == feature name to enable * * RETURNS: * status *********************************************************************/ status_t agt_enable_feature (const xmlChar *modname, const xmlChar *featurename) { ncx_module_t *mod; ncx_feature_t *feature; #ifdef DEBUG if (!modname || !featurename) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif mod = ncx_find_module(modname, NULL); if (!mod) { return ERR_NCX_MOD_NOT_FOUND; } feature = ncx_find_feature(mod, featurename); if (!feature) { return ERR_NCX_DEF_NOT_FOUND; } feature->enabled = TRUE; return NO_ERR; } /* agt_enable_feature */ /******************************************************************** * FUNCTION agt_disable_feature * * Disable a YANG feature in the agent * This will not be detected by any sessions in progress!!! * It will take affect the next time a message * is sent by the agent * * INPUTS: * modname == module name containing the feature * featurename == feature name to disable * * RETURNS: * status *********************************************************************/ status_t agt_disable_feature (const xmlChar *modname, const xmlChar *featurename) { ncx_module_t *mod; ncx_feature_t *feature; #ifdef DEBUG if (!modname || !featurename) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif mod = ncx_find_module(modname, NULL); if (!mod) { return ERR_NCX_MOD_NOT_FOUND; } feature = ncx_find_feature(mod, featurename); if (!feature) { return ERR_NCX_DEF_NOT_FOUND; } feature->enabled = FALSE; return NO_ERR; } /* agt_disable_feature */ status_t agt_check_feature (const xmlChar *modname, const xmlChar *featurename, int* enabled) { ncx_module_t *mod; ncx_feature_t *feature; #ifdef DEBUG if (!modname || !featurename) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif mod = ncx_find_module(modname, NULL); if (!mod) { return ERR_NCX_MOD_NOT_FOUND; } feature = ncx_find_feature(mod, featurename); if (!feature) { return ERR_NCX_DEF_NOT_FOUND; } *enabled = feature->enabled; return NO_ERR; } /******************************************************************** * make a val_value_t struct for a specified leaf or leaf-list * * \param parentobj parent object to find child leaf object * \param leafname name of leaf to find (namespace hardwired) * \param leafstrval== string version of value to set for leaf * \param res address of return status * \return :malloced value struct or NULL if some error *********************************************************************/ val_value_t* agt_make_leaf ( obj_template_t *parentobj, const xmlChar *leafname, const xmlChar *leafstrval, status_t *res ) { assert( parentobj && "parentobj is NULL" ); assert( leafname && "leafname is NULL" ); assert( res && "res is NULL" ); obj_template_t *leafobj = obj_find_child( parentobj, obj_get_mod_name(parentobj), leafname ); if ( !leafobj ) { *res =ERR_NCX_DEF_NOT_FOUND; return NULL; } if (!(leafobj->objtype == OBJ_TYP_LEAF || leafobj->objtype == OBJ_TYP_LEAF_LIST)) { *res = ERR_NCX_WRONG_TYPE; return NULL; } val_value_t *leafval = val_make_simval_obj( leafobj, leafstrval, res ); return leafval; } /* agt_make_leaf */ /******************************************************************** * make a val_value_t struct for a specified leaf or leaf-list * * \param parentobj parent object to find child leaf object * \param leafname name of leaf to find (namespace hardwired) * \param leafval number value for leaf * \param res address of return status * \return :malloced value struct or NULL if some error *********************************************************************/ val_value_t * agt_make_uint_leaf ( obj_template_t *parentobj, const xmlChar *leafname, uint32 leafval, status_t *res ) { assert( parentobj && "parentobj is NULL" ); assert( leafname && "leafname is NULL" ); assert( res && "res is NULL" ); xmlChar numbuff[NCX_MAX_NUMLEN]; snprintf((char *)numbuff, NCX_MAX_NUMLEN, "%u", leafval); return agt_make_leaf( parentobj, leafname, numbuff, res); } /* agt_make_uint_leaf */ /******************************************************************** * make a val_value_t struct for a specified leaf or leaf-list * * \param parentobj parent object to find child leaf object * \param leafname name of leaf to find (namespace hardwired) * \param leafval number value for leaf * \param res address of return status * \return :malloced value struct or NULL if some error *********************************************************************/ val_value_t *agt_make_int_leaf( obj_template_t *parentobj, const xmlChar *leafname, int32 leafval, status_t *res ) { assert( parentobj && "parentobj is NULL" ); assert( leafname && "leafname is NULL" ); assert( res && "res is NULL" ); xmlChar numbuff[NCX_MAX_NUMLEN]; snprintf((char *)numbuff, NCX_MAX_NUMLEN, "%d", leafval); return agt_make_leaf(parentobj, leafname, numbuff, res); } /* agt_make_int_leaf */ /******************************************************************** * make a val_value_t struct for a specified leaf or leaf-list * * \param parentobj parent object to find child leaf object * \param leafname name of leaf to find (namespace hardwired) * \param leafval number value for leaf * \param res address of return status * \return :malloced value struct or NULL if some error *********************************************************************/ val_value_t *agt_make_uint64_leaf ( obj_template_t *parentobj, const xmlChar *leafname, uint64 leafval, status_t *res) { assert( parentobj && "parentobj is NULL" ); assert( leafname && "leafname is NULL" ); assert( res && "res is NULL" ); xmlChar numbuff[NCX_MAX_NUMLEN]; snprintf((char *)numbuff, NCX_MAX_NUMLEN, "%llu", (long long unsigned int) leafval); return agt_make_leaf( parentobj, leafname, numbuff, res); } /* agt_make_uint64_leaf */ /******************************************************************** * make a val_value_t struct for a specified leaf or leaf-list * * \param parentobj parent object to find child leaf object * \param leafname name of leaf to find (namespace hardwired) * \param leafval number value for leaf * \param res address of return status * \return :malloced value struct or NULL if some error *********************************************************************/ val_value_t *agt_make_int64_leaf( obj_template_t *parentobj, const xmlChar *leafname, int64 leafval, status_t *res ) { assert( parentobj && "parentobj is NULL" ); assert( leafname && "leafname is NULL" ); assert( res && "res is NULL" ); xmlChar numbuff[NCX_MAX_NUMLEN]; snprintf((char *)numbuff, NCX_MAX_NUMLEN, "%lld", (long long int)leafval); return agt_make_leaf( parentobj, leafname, numbuff, res); } /* agt_make_int64_leaf */ /******************************************************************** * FUNCTION agt_make_idref_leaf * * make a val_value_t struct for a specified leaf or leaf-list * INPUTS: * parentobj == parent object to find child leaf object * leafname == name of leaf to find (namespace hardwired) * leafval == identityref value for leaf * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct or NULL if some error *********************************************************************/ val_value_t * agt_make_idref_leaf (obj_template_t *parentobj, const xmlChar *leafname, const val_idref_t *leafval, status_t *res) { obj_template_t *leafobj; val_value_t *newval; #ifdef DEBUG if (parentobj == NULL || leafname == NULL || res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif leafobj = obj_find_child(parentobj, obj_get_mod_name(parentobj), leafname); if (!leafobj) { *res =ERR_NCX_DEF_NOT_FOUND; return NULL; } if (!(leafobj->objtype == OBJ_TYP_LEAF || leafobj->objtype == OBJ_TYP_LEAF_LIST)) { *res = ERR_NCX_WRONG_TYPE; return NULL; } if (obj_get_basetype(leafobj) != NCX_BT_IDREF) { *res = ERR_NCX_WRONG_TYPE; return NULL; } newval = val_new_value(); if (newval == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(newval, leafobj); newval->v.idref.name = xml_strdup(leafval->name); if (newval->v.idref.name == NULL) { val_free_value(newval); *res = ERR_INTERNAL_MEM; return NULL; } newval->v.idref.nsid = leafval->nsid; newval->v.idref.identity = leafval->identity; return newval; } /* agt_make_idref_leaf */ /******************************************************************** * FUNCTION agt_make_list * * make a val_value_t struct for a specified list * INPUTS: * parentobj == parent object to find child leaf object * listame == name of list object to find (namespace hardwired) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct for the list or NULL if some error *********************************************************************/ val_value_t * agt_make_list (obj_template_t *parentobj, const xmlChar *listname, status_t *res) { return agt_make_object(parentobj, listname, res); } /* agt_make_list */ /******************************************************************** * FUNCTION agt_make_object * * make a val_value_t struct for a specified node * INPUTS: * parentobj == parent object to find child leaf object * objname == name of the object to find (namespace hardwired) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct for the list or NULL if some error *********************************************************************/ val_value_t * agt_make_object (obj_template_t *parentobj, const xmlChar *objname, status_t *res) { obj_template_t *obj; val_value_t *val; assert( parentobj && "parentobj == NULL!" ); assert( objname && "objname == NULL!" ); assert( res && "res == NULL!" ); obj = obj_find_child(parentobj, obj_get_mod_name(parentobj), objname); if (!obj) { *res =ERR_NCX_DEF_NOT_FOUND; return NULL; } val = val_new_value(); if (val == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(val, obj); return val; } /* agt_make_object */ /******************************************************************** * FUNCTION agt_make_virtual_leaf * * make a val_value_t struct for a specified virtual * leaf or leaf-list * INPUTS: * parentobj == parent object to find child leaf object * leafname == name of leaf to find (namespace hardwired) * callbackfn == get callback function to install * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct or NULL if some error *********************************************************************/ val_value_t * agt_make_virtual_leaf (obj_template_t *parentobj, const xmlChar *leafname, getcb_fn_t callbackfn, status_t *res) { obj_template_t *leafobj; val_value_t *leafval; #ifdef DEBUG if (parentobj == NULL || leafname == NULL || callbackfn == NULL || res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif leafobj = obj_find_child(parentobj, obj_get_mod_name(parentobj), leafname); if (!leafobj) { *res =ERR_NCX_DEF_NOT_FOUND; return NULL; } if (!(leafobj->objtype == OBJ_TYP_LEAF || leafobj->objtype == OBJ_TYP_LEAF_LIST)) { *res = ERR_NCX_WRONG_TYPE; return NULL; } leafval = val_new_value(); if (!leafval) { *res = ERR_INTERNAL_MEM; return NULL; } val_init_virtual(leafval, callbackfn, leafobj); return leafval; } /* agt_make_virtual_leaf */ /******************************************************************** * FUNCTION agt_add_top_virtual * * make a val_value_t struct for a specified virtual * top-level data node * * INPUTS: * obj == object node of the virtual data node to create * callbackfn == get callback function to install * * RETURNS: * status *********************************************************************/ status_t agt_add_top_virtual (obj_template_t *obj, getcb_fn_t callbackfn) { val_value_t *rootval, *nodeval; #ifdef DEBUG if (obj == NULL || callbackfn == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif rootval = cfg_get_root(NCX_CFGID_RUNNING); if (rootval == NULL) { return ERR_NCX_OPERATION_FAILED; } nodeval = val_new_value(); if (!nodeval) { return ERR_INTERNAL_MEM; } val_init_virtual(nodeval, callbackfn, obj); val_add_child_sorted(nodeval, rootval); return NO_ERR; } /* agt_add_top_virtual */ /******************************************************************** * FUNCTION agt_add_top_container * * make a val_value_t struct for a specified * top-level container data node. This is used * by SIL functions to create a top-level container * which may have virtual nodes within it * * TBD: fine-control over SIL C code generation to * allow mix of container virtual callback plus * child node virtual node callbacks * INPUTS: * obj == object node of the container data node to create * val == address of return val pointer * * OUTPUTS: * *val == pointer to node created in the database * this is not live memory! It will be freed * by the database management code * * RETURNS: * status *********************************************************************/ status_t agt_add_top_container (obj_template_t *obj, val_value_t **val) { val_value_t *rootval, *nodeval; #ifdef DEBUG if (obj == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif rootval = cfg_get_root(NCX_CFGID_RUNNING); if (rootval == NULL) { return ERR_NCX_OPERATION_FAILED; } nodeval = val_new_value(); if (!nodeval) { return ERR_INTERNAL_MEM; } val_init_from_template(nodeval, obj); val_add_child_sorted(nodeval, rootval); if (val != NULL) { *val = nodeval; } return NO_ERR; } /* agt_add_top_container */ /******************************************************************** * FUNCTION agt_add_container * * make a val_value_t struct for a specified * nested container data node. This is used * by SIL functions to create a nested container * which may have virtual nodes within it * * TBD: fine-control over SIL C code generation to * allow mix of container virtual callback plus * child node virtual node callbacks * INPUTS: * modname == module name defining objname * objname == name of object node to create * parentval == parent value node to add container to * val == address of return val pointer * * OUTPUTS: * *val == pointer to node created in the database * this is not live memory! It will be freed * by the database management code * * RETURNS: * status *********************************************************************/ status_t agt_add_container (const xmlChar *modname, const xmlChar *objname, val_value_t *parentval, val_value_t **val) { val_value_t *nodeval; obj_template_t *obj; #ifdef DEBUG if (objname == NULL || parentval == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif obj = obj_find_child(parentval->obj, modname, objname); if (obj == NULL) { return ERR_NCX_DEF_NOT_FOUND; } nodeval = val_new_value(); if (!nodeval) { return ERR_INTERNAL_MEM; } val_init_from_template(nodeval, obj); val_add_child_sorted(nodeval, parentval); if (val != NULL) { *val = nodeval; } return NO_ERR; } /* agt_add_container */ /******************************************************************** * FUNCTION agt_init_cache * * init a cache pointer during the init2 callback * * INPUTS: * modname == name of module defining the top-level object * objname == name of the top-level database object * res == address of return status * * OUTPUTS: * *res is set to the return status * * RETURNS: * pointer to object value node from running config, * or NULL if error or not found *********************************************************************/ val_value_t * agt_init_cache (const xmlChar *modname, const xmlChar *objname, status_t *res) { cfg_template_t *cfg; val_value_t *retval; #ifdef DEBUG if (modname == NULL || objname == NULL || res==NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif cfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (cfg == NULL) { *res = ERR_NCX_CFG_NOT_FOUND; return NULL; } if (cfg->root == NULL) { *res = NO_ERR; return NULL; } retval = val_find_child(cfg->root, modname, objname); *res = NO_ERR; return retval; } /* agt_init_cache */ /******************************************************************** * FUNCTION agt_check_cache * * check if a cache pointer needs to be changed or NULLed out * INPUTS: * cacheptr == address of pointer to cache value node * newval == newval from the callback function * curval == curval from the callback function * editop == editop from the callback function * * OUTPUTS: * *cacheptr may be changed, depending on the operation * * RETURNS: * status *********************************************************************/ status_t agt_check_cache (val_value_t **cacheptr, val_value_t *newval, val_value_t *curval, op_editop_t editop) { #ifdef DEBUG if (cacheptr == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif switch (editop) { case OP_EDITOP_MERGE: if (newval && curval) { if (typ_is_simple(newval->btyp)) { *cacheptr = newval; } else { *cacheptr = curval; } } else if (newval) { *cacheptr = newval; } else if (curval) { *cacheptr = curval; } else { *cacheptr = NULL; } break; case OP_EDITOP_REPLACE: case OP_EDITOP_CREATE: *cacheptr = newval; break; case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: *cacheptr = NULL; break; case OP_EDITOP_LOAD: case OP_EDITOP_COMMIT: *cacheptr = newval; break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return NO_ERR; } /* agt_check_cache */ /******************************************************************** * FUNCTION agt_new_xpath_pcb * * Get a new XPath parser control block and * set up the server variable bindings * * INPUTS: * scb == session evaluating the XPath expression * expr == expression string to use (may be NULL) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced and initialied xpath_pcb_t structure * NULL if some error *********************************************************************/ xpath_pcb_t * agt_new_xpath_pcb (ses_cb_t *scb, const xmlChar *expr, status_t *res) { val_value_t *userval; xpath_pcb_t *pcb; dlq_hdr_t *varbindQ; #ifdef DEBUG if (scb == NULL || res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (scb->username == NULL) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif pcb = xpath_new_pcb(expr, NULL); if (pcb == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } userval = val_make_string(0, AGT_USER_VAR, scb->username); if (userval == NULL) { xpath_free_pcb(pcb); *res = ERR_INTERNAL_MEM; return NULL; } varbindQ = xpath_get_varbindQ(pcb); /* pass off userval memory here, even if error returned */ *res = var_set_move_que(varbindQ, AGT_USER_VAR, userval); if (*res != NO_ERR) { xpath_free_pcb(pcb); pcb = NULL; } /* else userval memory stored in varbindQ now */ return pcb; } /* agt_new_xpath_pcb */ /******************************************************************** * FUNCTION agt_get_startup_filespec * * Figure out where to store the startup file * * INPUTS: * res == address of return status * * OUTPUTS: * *res == return status * RETURNS: * malloced and filled in filespec string; must be freed by caller * NULL if malloc error *********************************************************************/ xmlChar * agt_get_startup_filespec (status_t *res) { cfg_template_t *startup, *running; const xmlChar *yumahome; xmlChar *filename; #ifdef DEBUG if (res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif *res = NO_ERR; running = cfg_get_config_id(NCX_CFGID_RUNNING); if (running == NULL) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } startup = cfg_get_config_id(NCX_CFGID_STARTUP); yumahome = ncxmod_get_yuma_home(); /* get the right filespec to use * * 1) use the startup filespec * 2) use the running filespec * 3) use $YUMA_HOME/data/startup-cfg.xml * 4) use $HOME/.yuma/startup-cfg.xml */ if (startup && startup->src_url) { filename = xml_strdup(startup->src_url); if (filename == NULL) { *res = ERR_INTERNAL_MEM; } } else if (running && running->src_url) { filename = xml_strdup(running->src_url); if (filename == NULL) { *res = ERR_INTERNAL_MEM; } } else if (yumahome != NULL) { filename = ncx_get_source(NCX_YUMA_HOME_STARTUP_FILE, res); } else { filename = ncx_get_source(NCX_DOT_YUMA_STARTUP_FILE, res); } return filename; } /* agt_get_startup_filespec */ /******************************************************************** * FUNCTION agt_get_target_filespec * * Figure out where to store the URL target file * * INPUTS: * target_url == target url spec to use; this is * treated as a relative pathspec, and * the appropriate data directory is used * to create this file * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced and filled in filespec string; must be freed by caller * NULL if some error *********************************************************************/ xmlChar * agt_get_target_filespec (const xmlChar *target_url, status_t *res) { cfg_template_t *startup, *running; const xmlChar *yumahome; xmlChar *filename, *tempbuff, *str; uint32 len; #ifdef DEBUG if (target_url == NULL || res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif *res = NO_ERR; filename = NULL; tempbuff = NULL; running = cfg_get_config_id(NCX_CFGID_RUNNING); if (running == NULL) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } startup = cfg_get_config_id(NCX_CFGID_STARTUP); yumahome = ncxmod_get_yuma_home(); /* get the right filespec to use * * 1) use the startup filespec * 2) use the running filespec * 3) use $YUMA_HOME/data/startup-cfg.xml * 4) use $HOME/.yuma/startup-cfg.xml */ if (startup && startup->src_url) { len = ncxmod_get_pathlen_from_filespec(startup->src_url); filename = m__getMem(len + xml_strlen(target_url) + 1); if (filename == NULL) { *res = ERR_INTERNAL_MEM; } else { str = filename; str += xml_strncpy(str, startup->src_url, len); xml_strcpy(str, target_url); } } else if (running && running->src_url) { len = ncxmod_get_pathlen_from_filespec(running->src_url); filename = m__getMem(len + xml_strlen(target_url) + 1); if (filename == NULL) { *res = ERR_INTERNAL_MEM; } else { str = filename; str += xml_strncpy(str, running->src_url, len); xml_strcpy(str, target_url); } } else if (yumahome != NULL) { len = xml_strlen(NCX_YUMA_HOME_STARTUP_DIR); tempbuff = m__getMem(len + xml_strlen(target_url) + 1); if (tempbuff == NULL) { *res = ERR_INTERNAL_MEM; } else { filename = ncx_get_source(tempbuff, res); } } else { len = xml_strlen(NCX_DOT_YUMA_STARTUP_DIR); tempbuff = m__getMem(len + xml_strlen(target_url) + 1); if (tempbuff == NULL) { *res = ERR_INTERNAL_MEM; } else { filename = ncx_get_source(tempbuff, res); } } if (tempbuff != NULL) { m__free(tempbuff); } return filename; } /* agt_get_target_filespec */ /******************************************************************** * FUNCTION agt_set_mod_defaults * * Check for any top-level config leafs that have a default * value, and add them to the running configuration. * * INPUTS: * mod == module that was just added and should be used * to check for top-level database leafs with a default * * RETURNS: * status *********************************************************************/ status_t agt_set_mod_defaults (ncx_module_t *mod) { cfg_template_t *running; obj_template_t *defobj, *defcase, *childobj; const xmlChar *defstr; status_t res; #ifdef DEBUG if (mod == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif running = cfg_get_config_id(NCX_CFGID_RUNNING); if (running == NULL || running->root == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } res = NO_ERR; for (defobj = ncx_get_first_data_object(mod); defobj != NULL && res == NO_ERR; defobj = ncx_get_next_data_object(mod, defobj)) { /* only care about top-level leafs and choices */ if (defobj->objtype == OBJ_TYP_CHOICE) { defcase = obj_get_default_case(defobj); if (defcase != NULL) { /* check the default case for any default leafs, * there should be at least one of them */ for (childobj = obj_first_child(defcase); childobj != NULL; childobj = obj_next_child(childobj)) { /* only care about config leafs */ /* !!! should dive into choices with default cases !!! */ if (childobj->objtype == OBJ_TYP_LEAF && obj_get_config_flag(childobj)) { /* only care about leafs with default values */ defstr = obj_get_default(childobj); if (defstr != NULL) { /* create this top-level leaf */ res = add_default_leaf(running->root, childobj, defstr); } } } } } else if (defobj->objtype == OBJ_TYP_LEAF && obj_get_config_flag(defobj)) { /* only care about config leafs with default values */ defstr = obj_get_default(defobj); if (defstr != NULL) { /* create this top-level leaf */ res = add_default_leaf(running->root, defobj, defstr); } } } return res; } /* agt_set_mod_defaults */ /******************************************************************** * FUNCTION agt_set_with_defaults * * Check if the parameter is set * in the request message, and if so, is it * one of the server's supported values * * If not, then record an error * If so, then set the msg->mhdr.withdef enum * * INPUTS: * scb == session control block to use * msg == rpc message in progress * methnode == XML node for the method name * * OUTPUTS: * msg->mhdr.withdef set if NO_ERR * rpc-error recorded if any error detected * * RETURNS: * status *********************************************************************/ status_t agt_set_with_defaults (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { val_value_t *parm; status_t res; #ifdef DEBUG if (scb == NULL || msg == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #else (void)scb; #endif (void)methnode; res = NO_ERR; /* check the with-defaults parameter */ parm = val_find_child(msg->rpc_input, NCXMOD_WITH_DEFAULTS, NCX_EL_WITH_DEFAULTS); if (parm == NULL) { /* optional parameter not found; * the proper with-defaults enum should * have been set when the msg was initialized */ return NO_ERR; } if (parm->res != NO_ERR) { /* error should already be recorded */ return parm->res; } msg->mhdr.withdef = ncx_get_withdefaults_enum(VAL_ENUM_NAME(parm)); return res; } /* agt_set_with_defaults */ /******************************************************************** * Get the next expected key value in the ancestor chain * Used in Yuma SIL code to invoke User SIL callbacks with key values. * The caller should maintain the lastkey as a state-var as the anscestor, * keys are traversed. If lastkey == NULL the first key is returned. * * \param startval value node to start from * \param lastkey address of last key leaf found in ancestor chain. * \return value node to use (do not free!!!) *********************************************************************/ val_value_t* agt_get_key_value ( val_value_t *startval, val_value_t **lastkey ) { assert( startval && "startval is NULL" ); assert( lastkey && "lastkey is NULL" ); agt_keywalker_parms_t parms; parms.lastkey = *lastkey; parms.retkey = NULL; parms.usenext = FALSE; parms.done = FALSE; if ( LOGDEBUG3 ) { log_debug3( "\nStart key walk for %s", startval->name ); if ( *lastkey ) { log_debug3(" lastkey=%s", (*lastkey)->name); } } val_traverse_keys(startval, (void *)&parms, NULL, get_key_value); if ( LOGDEBUG3 ) { log_debug3("\nEnd key walk for %s:", startval->name); if (parms.retkey) { log_debug3(" retkey:\n"); val_dump_value(parms.retkey, 2); } } *lastkey = parms.retkey; return parms.retkey; } /* agt_get_key_value */ /******************************************************************** * FUNCTION agt_add_top_node_if_missing * * SIL Phase 2 init: Check the running config to see if * the specified node is there; if not create one * return the node either way * INPUTS: * mod == module containing object * objname == object name * added == address of return added flag * res == address of return status * * OUTPUTS: * *added == TRUE if new node was added * *res == return status, return NULL unless NO_ERR * * RETURNS: * if *res==NO_ERR, * pointer to new or existing node for modname:objname * this is added to running config and does not have to * be deleted. * !!!! No MRO nodes or default nodes have been added !!!! *********************************************************************/ val_value_t * agt_add_top_node_if_missing (ncx_module_t *mod, const xmlChar *objname, boolean *added, status_t *res) { obj_template_t *nodeobj; cfg_template_t *runningcfg; val_value_t *nodeval; const xmlChar *modname; #ifdef DEBUG if (!mod || !objname || !added || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif *res = NO_ERR; *added = FALSE; modname = ncx_get_modname(mod); /* make sure the running config root is set */ runningcfg = cfg_get_config(NCX_EL_RUNNING); if (!runningcfg || !runningcfg->root) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } nodeobj = obj_find_template_top(mod, modname, objname); if (nodeobj == NULL) { *res = SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return NULL; } /* check to see if the /nacm branch already exists * if so, then skip all this init stuff */ nodeval = val_find_child(runningcfg->root, modname, objname); if (nodeval == NULL) { /* did not find the /modname:objname node so create one; * get all the static object nodes first */ /* create the static structure for the /nacm data model * start with the top node /nacm */ nodeval = val_new_value(); if (nodeval == NULL) { *res = ERR_INTERNAL_MEM; } else { *added = TRUE; val_init_from_template(nodeval, nodeobj); /* handing off the malloced memory here */ val_add_child_sorted(nodeval, runningcfg->root); } } return nodeval; } /* agt_add_top_node_if_missing */ /******************************************************************** * FUNCTION agt_any_operations_set * * Check the new node and all descendants * for any operation attibutes prsent * * INPUTS: * val == value node to check * * RETURNS: * TRUE if any operation attributes found * FALSE if no operation attributes found */ boolean agt_any_operations_set (val_value_t *val) { val_value_t *childval; if (val->editvars && val->editvars->operset) { return TRUE; } for (childval = val_get_first_child(val); childval != NULL; childval = val_get_next_child(childval)) { boolean anyset = agt_any_operations_set(childval); if (anyset) { return TRUE; } } return FALSE; } /* agt_any_operations_set */ /******************************************************************** * FUNCTION agt_apply_this_node * * Check if the write operation applies to the current node * * INPUTS: * editop == edit operation value * newnode == pointer to new node (if any) * curnode == pointer to current value node (if any) * (just used to check if non-NULL or compare leaf) * * RETURNS: * TRUE if the current node needs the write operation applied * FALSE if this is a NO=OP node (either explicit or special merge) *********************************************************************/ boolean agt_apply_this_node (op_editop_t editop, const val_value_t *newnode, const val_value_t *curnode) { boolean retval = FALSE; switch (editop) { case OP_EDITOP_NONE: /* never apply here when operation is none */ break; case OP_EDITOP_MERGE: /* if no current node then always merge here * merge child nodes into an existing complex type * except for the index nodes, which are kept */ if (!curnode) { retval = TRUE; } else { /* if this is a leaf and not an index leaf, then * apply the merge here, if value changed */ if (curnode && !curnode->index) { if (typ_is_simple(obj_get_basetype(curnode->obj))) { if (newnode == NULL) { retval = TRUE; // should not happen! } else if (newnode->editvars != NULL && newnode->editvars->insertop != OP_INSOP_NONE) { retval = TRUE; } else if (val_compare(newnode, curnode) != 0) { retval = TRUE; } } /* else this is a complex node, keep checking * all the descendents in case an insert operation * is present in a nested list or leaf list * this will compare the same for a move op */ } } break; case OP_EDITOP_REPLACE: if (curnode == NULL) { retval = TRUE; } else if (!obj_is_root(curnode->obj)) { /* apply here if value has changed */ if (newnode == NULL) { SET_ERROR(ERR_INTERNAL_VAL); } else if (newnode->editvars != NULL && newnode->editvars->insertop != OP_INSOP_NONE) { retval = TRUE; } else if (val_compare_max(newnode, curnode, TRUE, TRUE, TRUE) != 0) { retval = TRUE; } /* else if this is a complex node, keep checking * all the descendents in case an insert operation * is present in a nested list or leaf list * this will compare the same for a move op */ } break; case OP_EDITOP_COMMIT: case OP_EDITOP_CREATE: case OP_EDITOP_LOAD: case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: retval = TRUE; break; default: SET_ERROR(ERR_INTERNAL_VAL); } return retval; } /* agt_apply_this_node */ /* END file agt_util.c */ yuma123_2.14/netconf/src/agt/agt_acm.c0000664000175000017500000024277514770023131017701 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_acm.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 04feb06 abb begun 01aug08 abb convert from NCX PSD to YANG OBJ design 20feb10 abb add enable-nacm leaf and notification-rules change indexing to user-ordered rule-name instead of allowed-rights bits field ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_acm.h" #include "agt_cb.h" #include "agt_not.h" #include "agt_ses.h" #include "agt_util.h" #include "agt_val.h" #include "def_reg.h" #include "dlq.h" #include "ncx.h" #include "ncx_num.h" #include "ncx_list.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xpath.h" #include "xpath1.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define AGT_ACM_MODULE (const xmlChar *)"ietf-netconf-acm" #define nacm_N_groups (const xmlChar *)"groups" #define nacm_N_group (const xmlChar *)"group" #define nacm_N_name (const xmlChar *)"name" #define nacm_N_userName (const xmlChar *)"user-name" #define nacm_N_ruleList (const xmlChar *)"rule-list" #define nacm_N_rule (const xmlChar *)"rule" #define nacm_N_accessOperations (const xmlChar *)"access-operations" #define nacm_N_comment (const xmlChar *)"comment" #define nacm_N_enableNacm (const xmlChar *)"enable-nacm" #define nacm_N_group (const xmlChar *)"group" #define nacm_N_name (const xmlChar *)"name" #define nacm_N_groups (const xmlChar *)"groups" #define nacm_N_moduleName (const xmlChar *)"module-name" #define nacm_N_moduleRule (const xmlChar *)"module-rule" #define nacm_N_nacm (const xmlChar *)"nacm" #define nacm_N_rule_name (const xmlChar *)"rule-name" #define nacm_N_notificationName \ (const xmlChar *)"notification-name" #define nacm_N_readDefault (const xmlChar *)"read-default" #define nacm_N_writeDefault (const xmlChar *)"write-default" #define nacm_N_execDefault (const xmlChar *)"exec-default" #define nacm_N_path (const xmlChar *)"path" #define nacm_N_rpcName (const xmlChar *)"rpc-name" #define nacm_N_notificationName (const xmlChar *)"notification-name" #define nacm_N_denied_operations (const xmlChar *)"denied-operations" #define nacm_N_deniedDataWrites (const xmlChar *)"denied-data-writes" #define nacm_N_deniedNotifications (const xmlChar *)"denied-notifications" #define nacm_OID_nacm (const xmlChar *)"/nacm" #define nacm_OID_nacm_enable_nacm (const xmlChar *)"/nacm/enable-nacm" #define nacm_E_noRuleDefault_permit (const xmlChar *)"permit" #define nacm_E_noRuleDefault_deny (const xmlChar *)"deny" #define nacm_E_allowedRights_read (const xmlChar *)"read" #define nacm_E_allowedRights_create (const xmlChar *)"create" #define nacm_E_allowedRights_update (const xmlChar *)"update" #define nacm_E_allowedRights_delete (const xmlChar *)"delete" #define nacm_E_allowedRights_exec (const xmlChar *)"exec" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_acm_init_done = FALSE; static ncx_module_t *nacmmod; static const xmlChar *superuser; static agt_acmode_t acmode; static uint32 denied_operations_count; static uint32 denied_data_writes_count; static agt_acm_cache_t *notif_cache; static boolean log_reads; static boolean log_writes; /******************************************************************** * FUNCTION is_superuser * * Check if the specified user name is the superuser * * INPUTS: * username == username to check * * RETURNS: * TRUE if username is the superuser * FALSE if username is not the superuser *********************************************************************/ static boolean is_superuser (const xmlChar *username) { if (!superuser || !*superuser) { return FALSE; } if (!username || !*username) { return FALSE; } return (xml_strcmp(superuser, username)) ? FALSE : TRUE; } /* is_superuser */ /******************************************************************** * FUNCTION check_mode * * Check the access-control mode being used * * INPUTS: * access == requested access mode * obj == object template to check * * RETURNS: * TRUE if access granted * FALSE to check the rules and find out *********************************************************************/ static boolean check_mode (const xmlChar *access, const obj_template_t *obj) { boolean isread; /* check if this is a read or a write */ if (!xml_strcmp(access, nacm_E_allowedRights_create)) { isread = FALSE; } else if (!xml_strcmp(access, nacm_E_allowedRights_update)) { isread = FALSE; } else if (!xml_strcmp(access, nacm_E_allowedRights_delete)) { isread = FALSE; } else if (!xml_strcmp(access, nacm_E_allowedRights_exec)) { isread = FALSE; } else { isread = TRUE; } switch (acmode) { case AGT_ACMOD_ENFORCING: break; case AGT_ACMOD_PERMISSIVE: if (isread && !obj_is_very_secure(obj)) { return TRUE; } break; case AGT_ACMOD_DISABLED: if (isread) { if (!obj_is_very_secure(obj)) { return TRUE; } } else { if (!(obj_is_secure(obj) || obj_is_very_secure(obj))) { return TRUE; } } break; case AGT_ACMOD_OFF: return TRUE; default: SET_ERROR(ERR_INTERNAL_VAL); } return FALSE; } /* check_mode */ /******************************************************************** * FUNCTION new_modrule * * create a moduleRule cache entry * * INPUTS: * nsid == module namspace ID * rule == back-ptr to moduleRule entry * * RETURNS: * filled in, malloced struct or NULL if malloc error *********************************************************************/ static agt_acm_modrule_t * new_modrule (xmlns_id_t nsid, val_value_t *rule) { agt_acm_modrule_t *modrule; modrule = m__getObj(agt_acm_modrule_t); if (!modrule) { return NULL; } memset(modrule, 0x0, sizeof(agt_acm_modrule_t)); modrule->nsid = nsid; modrule->modrule = rule; return modrule; } /* new_modrule */ /******************************************************************** * FUNCTION free_modrule * * free a moduleRule cache entry * * INPUTS: * modrule == entry to free * *********************************************************************/ static void free_modrule (agt_acm_modrule_t *modrule) { m__free(modrule); } /* free_modrule */ /******************************************************************** * FUNCTION new_datarule * * create a data rule cache entry * * INPUTS: * pcb == parser control block to cache (live memory freed later) * result == XPath result to cache (live memory freed later) * rule == back-ptr to dataRule entry * * RETURNS: * filled in, malloced struct or NULL if malloc error *********************************************************************/ static agt_acm_datarule_t * new_datarule (xpath_pcb_t *pcb, xpath_result_t *result, val_value_t *rule) { agt_acm_datarule_t *datarule; datarule = m__getObj(agt_acm_datarule_t); if (!datarule) { return NULL; } memset(datarule, 0x0, sizeof(agt_acm_datarule_t)); datarule->pcb = pcb; datarule->result = result; datarule->datarule = rule; return datarule; } /* new_datarule */ /******************************************************************** * FUNCTION free_datarule * * free a dataRule cache entry * * INPUTS: * datarule == entry to free * *********************************************************************/ static void free_datarule (agt_acm_datarule_t *datarule) { xpath_free_result(datarule->result); xpath_free_pcb(datarule->pcb); m__free(datarule); } /* free_datarule */ /******************************************************************** * FUNCTION new_group_ptr * * create a group pointer * * INPUTS: * name == group identity name to use * * RETURNS: * filled in, malloced struct or NULL if malloc error *********************************************************************/ static agt_acm_group_t * new_group_ptr (const xmlChar *groupname) { agt_acm_group_t *grptr; grptr = m__getObj(agt_acm_group_t); if (!grptr) { return NULL; } memset(grptr, 0x0, sizeof(agt_acm_group_t)); grptr->groupname = groupname; return grptr; } /* new_group_ptr */ /******************************************************************** * FUNCTION free_group_ptr * * free a group pointer * * INPUTS: * grptr == group to free * * RETURNS: * filled in, malloced struct or NULL if malloc error *********************************************************************/ static void free_group_ptr (agt_acm_group_t *grptr) { m__free(grptr); } /* free_group_ptr */ /******************************************************************** * FUNCTION find_group_ptr * * find a group pointer in a user group record * * INPUTS: * usergroups == user-to-groups struct to check * name == group identity name to find * * RETURNS: * pointer to found record or NULL if not found *********************************************************************/ static agt_acm_group_t * find_group_ptr (agt_acm_usergroups_t *usergroups, const xmlChar *name) { agt_acm_group_t *grptr; for (grptr = (agt_acm_group_t *) dlq_firstEntry(&usergroups->groupQ); grptr != NULL; grptr = (agt_acm_group_t *)dlq_nextEntry(grptr)) { if (!xml_strcmp(grptr->groupname, name)) { return grptr; } } return NULL; } /* find_group_ptr */ /******************************************************************** * FUNCTION add_group_ptr * * add a group pointer in a user group record * if it is not already there * * INPUTS: * usergroups == user-to-groups struct to use * name == group identity name to add * * RETURNS: * status *********************************************************************/ static status_t add_group_ptr (agt_acm_usergroups_t *usergroups, const xmlChar *name) { agt_acm_group_t *grptr; grptr = find_group_ptr(usergroups, name); if (grptr) { return NO_ERR; } grptr = new_group_ptr(name); if (!grptr) { return ERR_INTERNAL_MEM; } dlq_enque(grptr, &usergroups->groupQ); return NO_ERR; } /* add_group_ptr */ /******************************************************************** * FUNCTION new_usergroups * * create a user-to-groups struct * * INPUTS: * username == name of user to use * * RETURNS: * filled in, malloced struct or NULL if malloc error *********************************************************************/ static agt_acm_usergroups_t * new_usergroups (const xmlChar *username) { agt_acm_usergroups_t *usergroups; usergroups = m__getObj(agt_acm_usergroups_t); if (!usergroups) { return NULL; } memset(usergroups, 0x0, sizeof(agt_acm_usergroups_t)); dlq_createSQue(&usergroups->groupQ); usergroups->username = xml_strdup(username); if ( !usergroups->username ) { m__free(usergroups); return NULL; } return usergroups; } /* new_usergroups */ /******************************************************************** * FUNCTION free_usergroups * * free a user-to-groups struct * * INPUTS: * usergroups == struct to free * *********************************************************************/ static void free_usergroups (agt_acm_usergroups_t *usergroups) { agt_acm_group_t *grptr; while (!dlq_empty(&usergroups->groupQ)) { grptr = (agt_acm_group_t *) dlq_deque(&usergroups->groupQ); free_group_ptr(grptr); } if (usergroups->username) { m__free(usergroups->username); } m__free(usergroups); } /* free_usergroups */ /******************************************************************** * FUNCTION get_usergroups_entry * * create (TBD: cache) a user-to-groups entry * for the specified username, based on the /nacm/groups * contents at this time * * INPUTS: * nacmroot == root of the nacm tree, already fetched * username == user name to create mapping for * groupcount == address of return group count field * * OUTPUTS: * *groupcount == number of groups that the specified * user is part of (i.e., number of * agt_acm_group_t structs in the groups Q * * RETURNS: * malloced usergroups entry for the specified user *********************************************************************/ static agt_acm_usergroups_t * get_usergroups_entry (val_value_t *nacmroot, const xmlChar *username, uint32 *groupcount) { agt_acm_usergroups_t *usergroups; val_value_t *groupsval, *groupval, *group_name_val, *userval; boolean done; status_t res; *groupcount = 0; res = NO_ERR; /*** no cache yet -- just create entry each time for now ***/ usergroups = new_usergroups(username); if (!usergroups) { return NULL; } /* get /nacm/groups node */ groupsval = val_find_child(nacmroot, AGT_ACM_MODULE, nacm_N_groups); if (!groupsval) { return usergroups; } /* check each /nacm/groups/group node */ for (groupval = val_get_first_child(groupsval); groupval != NULL && res == NO_ERR; groupval = val_get_next_child(groupval)) { done = FALSE; group_name_val = val_find_child(groupval, AGT_ACM_MODULE, nacm_N_name); assert(group_name_val!=NULL); /* check each /nacm/groups/group/user-name node */ for (userval = val_find_child(groupval, AGT_ACM_MODULE, nacm_N_userName); userval != NULL && !done; userval = val_find_next_child(groupval, AGT_ACM_MODULE, nacm_N_userName, userval)) { if (!xml_strcmp(username, VAL_STR(userval))) { /* user is a member of this group * get the groupIdentity key leaf */ done = TRUE; res = add_group_ptr(usergroups, VAL_STRING(group_name_val)); (*groupcount)++; } } } if (res != NO_ERR) { log_error("\nError: agt_acm add user2group entry failed"); } return usergroups; } /* get_usergroups_entry */ /******************************************************************** * FUNCTION new_acm_cache * * Malloc and initialize an agt_acm_cache_t stuct * * RETURNS: * malloced msg cache or NULL if error *********************************************************************/ static agt_acm_cache_t * new_acm_cache (void) { agt_acm_cache_t *acm_cache; int i; acm_cache = m__getObj(agt_acm_cache_t); if (!acm_cache) { return NULL; } memset(acm_cache, 0x0, sizeof(agt_acm_cache_t)); dlq_createSQue(&acm_cache->modruleQ); for(i=0;idataruleQ[i]); } acm_cache->mode = acmode; acm_cache->flags = FL_ACM_CACHE_VALID; return acm_cache; } /* new_acm_cache */ /******************************************************************** * FUNCTION free_acm_cache * * Clean and free a agt_acm_cache_t struct * * INPUTS: * acm_cache == cache struct to free *********************************************************************/ static void free_acm_cache (agt_acm_cache_t *acm_cache) { agt_acm_modrule_t *modrule; agt_acm_datarule_t *datarule; int i; while (!dlq_empty(&acm_cache->modruleQ)) { modrule = (agt_acm_modrule_t *) dlq_deque(&acm_cache->modruleQ); free_modrule(modrule); } for(i=0;idataruleQ[i])) { datarule = (agt_acm_datarule_t *) dlq_deque(&acm_cache->dataruleQ[i]); free_datarule(datarule); } } if (acm_cache->usergroups) { free_usergroups(acm_cache->usergroups); } m__free(acm_cache); } /* free_acm_cache */ /******************************************************************** * FUNCTION check_access_bit * * * INPUTS: * ruleval == /nacm/rule-list/rule node, pre-fetched * access-operation == string (enum name) for the requested access * (read, create, update, delete, exec) * usergroups == user-to-group mapping for access processing * done == address of return done processing flag * * OUTPUTS: * *done == TRUE if a matching group was found, * so return value is the final answer * == FALSE if no matching group was found * * RETURNS: * only valid if *done == TRUE: * TRUE if requested access right found * FALSE if requested access right not found *********************************************************************/ static boolean check_access_bit (val_value_t *ruleval, const xmlChar *access, agt_acm_usergroups_t *usergroups, boolean *done) { val_value_t *group, *nameval, *rights; boolean granted; *done = FALSE; granted = FALSE; for (group = val_find_child(ruleval->parent, AGT_ACM_MODULE, nacm_N_group); group != NULL && !*done; group = val_find_next_child(ruleval->parent, AGT_ACM_MODULE, nacm_N_name, group)) { if ((0==strcmp(VAL_STRING(group),"*")) || find_group_ptr(usergroups, VAL_STRING(group))) { /* this group is a match */ *done = TRUE; /* get the allowedRights leaf * and check if the exec bit is set */ rights = val_find_child(ruleval, AGT_ACM_MODULE, nacm_N_accessOperations); assert(rights); if (0==strcmp(VAL_STRING(rights),"*")) { granted=TRUE; } else { #if 0 ncx_list_t *bits; bits = &VAL_BITS(rights); granted = ncx_string_in_list(access, bits); #else if(strstr(VAL_STRING(rights), access) != NULL) { granted = TRUE; } #endif } } } return granted; } /* check_access_bit */ /******************************************************************** * FUNCTION check_rule_group * * * INPUTS: * ruleval == /nacm/rule-list/rule node, pre-fetched * usergroups == user-to-group mapping for access processing * done == address of return done processing flag * * RETURNS: * TRUE if group was found * FALSE if group was not found *********************************************************************/ static boolean check_rule_group (val_value_t *ruleval, agt_acm_usergroups_t *usergroups) { val_value_t *group, *nameval, *rights; for (group = val_find_child(ruleval->parent, AGT_ACM_MODULE, nacm_N_group); group != NULL; group = val_find_next_child(ruleval->parent, AGT_ACM_MODULE, nacm_N_name, group)) { if ((0==strcmp(VAL_STRING(group),"*")) || find_group_ptr(usergroups, VAL_STRING(group))) { return TRUE; } } return FALSE; } /* check_rule_group */ /******************************************************************** * FUNCTION get_nacm_root * * get the /nacm root object * * RETURNS: * pointer to root or NULL if none *********************************************************************/ static val_value_t * get_nacm_root (void) { cfg_template_t *runningcfg; val_value_t *nacmval; /* make sure the running config root is set */ runningcfg = cfg_get_config(NCX_EL_RUNNING); if (!runningcfg || !runningcfg->root) { return NULL; } nacmval = val_find_child(runningcfg->root, AGT_ACM_MODULE, nacm_N_nacm); return nacmval; } /* get_nacm_root */ /******************************************************************** * FUNCTION get_default_rpc_response * * get the default response for the specified RPC object * there are no rules that match any groups with this user * * INPUTS: * cache == agt_acm cache to check * nacmroot == pre-fectched NACM root * rpcobj == RPC template for this request * * RETURNS: * TRUE if access granted * FALSE if access denied *********************************************************************/ static boolean get_default_rpc_response (agt_acm_cache_t *cache, val_value_t *nacmroot, const obj_template_t *rpcobj) { val_value_t *noRule; boolean retval; /* check if the RPC method is tagged as * ncx:secure or ncx:very-secure and * deny access if so */ if (obj_is_secure(rpcobj) || obj_is_very_secure(rpcobj)) { return FALSE; } /* check the noDefaultRule setting on this agent */ if (cache->flags & FL_ACM_DEFEXEC_SET) { return (cache->flags & FL_ACM_DEFEXEC_OK) ? TRUE : FALSE; } noRule = val_find_child(nacmroot, AGT_ACM_MODULE, nacm_N_execDefault); if (!noRule) { cache->flags |= (FL_ACM_DEFEXEC_SET | FL_ACM_DEFEXEC_OK); return TRUE; /* default is TRUE */ } if (!xml_strcmp(VAL_ENUM_NAME(noRule), nacm_E_noRuleDefault_permit)) { retval = TRUE; } else { retval = FALSE; } cache->flags |= FL_ACM_DEFEXEC_SET; if (retval) { cache->flags |= FL_ACM_DEFEXEC_OK; } return retval; } /* get_default_rpc_response */ /******************************************************************** * FUNCTION check_rpc_rules * * Check the configured /nacm/rule-list/rule lists to see if the * user is allowed to invoke the specified RPC method * * INPUTS: * nacmrootval == /nacm node, pre-fetched * rpcobj == RPC template requested * usergroups == user-to-group mapping for access processing * done == address of return done processing flag * * OUTPUTS: * *done == TRUE if a rule was found, so return value is * the final answer * == FALSE if no rpcRule was found to match * * RETURNS: * only valid if *done == TRUE: * TRUE if authorization to invoke the RPC op is granted * FALSE if authorization to invoke the RPC op is not granted *********************************************************************/ static boolean check_rpc_rules (val_value_t *nacmroot, const obj_template_t *rpcobj, agt_acm_usergroups_t *usergroups, boolean *done) { val_value_t *rule_list, *rule, *modname, *rpcname; boolean granted, done2; status_t res; *done = FALSE; granted = FALSE; res = NO_ERR; /* check all the entries */ for (rule_list = val_find_child(nacmroot, AGT_ACM_MODULE, nacm_N_ruleList); rule_list != NULL && res == NO_ERR && !*done; rule_list = val_find_next_child(nacmroot, AGT_ACM_MODULE, nacm_N_ruleList, rule_list)) { for (rule = val_find_child(rule_list, AGT_ACM_MODULE, nacm_N_rule); rule != NULL && res == NO_ERR && !*done; rule = val_find_next_child(rule_list, AGT_ACM_MODULE, nacm_N_rule, rule)) { /* get the rpc operation name key */ rpcname = val_find_child(rule, AGT_ACM_MODULE, nacm_N_rpcName); if (!rpcname) { continue; } /* get the module name key */ modname = val_find_child(rule, AGT_ACM_MODULE, nacm_N_moduleName); /* check if this is the right module */ if ((modname != NULL) && xml_strcmp("*", VAL_STR(modname)) && xml_strcmp(obj_get_mod_name(rpcobj), VAL_STR(modname)) && (xml_strcmp(obj_get_mod_name(rpcobj), "yuma-netconf") && xml_strcmp("ietf-netconf", VAL_STR(modname))) ) { continue; } /* check if this is the right RPC operation */ if (xml_strcmp(obj_get_name(rpcobj), VAL_STR(rpcname))) { continue; } /* this rpc rule is for the specified RPC operation * check if exec access is allowed */ done2 = FALSE; granted = check_access_bit(rule, nacm_E_allowedRights_exec, usergroups, &done2); if (done2) { *done = TRUE; } else { granted = FALSE; } } } if (res != NO_ERR) { granted = FALSE; *done = TRUE; } return granted; } /* check_rpc_rules */ /******************************************************************** * FUNCTION check_module_rules * * Check the configured /nacm/rules/moduleRule list to see if the * user is allowed access the specified module * * INPUTS: * cache == cache to use * rulesval == /nacm/rules node, pre-fetched * obj == RPC or data template requested * access == string (enum name) for the requested access * (read, write, exec) * usergroups == user-to-group mapping for access processing * done == address of return done processing flag * * OUTPUTS: * *done == TRUE if a rule was found, so return value is * the final answer * == FALSE if no rpcRule was found to match * * RETURNS: * only valid if *done == TRUE: * TRUE if authorization to invoke the RPC op is granted * FALSE if authorization to invoke the RPC op is not granted *********************************************************************/ static boolean check_module_rules (agt_acm_cache_t *cache, val_value_t *nacmroot, const obj_template_t *obj, const xmlChar *access, agt_acm_usergroups_t *usergroups, boolean *done) { val_value_t *rule_list, *modrule, *modname; agt_acm_modrule_t *modrule_cache; boolean granted; xmlns_id_t nsid; status_t res; *done = FALSE; granted = FALSE; res = NO_ERR; return FALSE; /*TODO: Not ready. */ rule_list = val_find_child(nacmroot, AGT_ACM_MODULE, nacm_N_ruleList); if (!(cache->flags & FL_ACM_MODRULES_SET)) { cache->flags |= FL_ACM_MODRULES_SET; /* check all the moduleRule entries */ for (modrule = val_find_child(rule_list, AGT_ACM_MODULE, nacm_N_moduleRule); modrule != NULL && res == NO_ERR; modrule = val_find_next_child(rule_list, AGT_ACM_MODULE, nacm_N_moduleRule, modrule)) { /* get the module name key */ modname = val_find_child(modrule, AGT_ACM_MODULE, nacm_N_moduleName); if (!modname) { res = SET_ERROR(ERR_INTERNAL_VAL); continue; } nsid = xmlns_find_ns_by_module(VAL_STR(modname)); if (nsid) { modrule_cache = new_modrule(nsid, modrule); if (!modrule_cache) { res = ERR_INTERNAL_MEM; } else { dlq_enque(modrule_cache, &cache->modruleQ); } } else { /* this rule is for a module that is not * loaded into the system at this time * just skip this entry; */ ; } } } /* get the namespace ID to check against */ nsid = obj_get_nsid(obj); /* go through the cache and exit if any matches are found */ for (modrule_cache = (agt_acm_modrule_t *) dlq_firstEntry(&cache->modruleQ); modrule_cache != NULL && res == NO_ERR && !*done; modrule_cache = (agt_acm_modrule_t *) dlq_nextEntry(modrule_cache)) { /* check if this is the right module */ if (nsid != modrule_cache->nsid) { continue; } /* this module rule is for the specified module * check if the requested access is allowed */ granted = check_access_bit(modrule_cache->modrule, access, usergroups, done); } if (res != NO_ERR) { granted = FALSE; *done = TRUE; } return granted; } /* check_module_rules */ /******************************************************************** * FUNCTION get_default_data_response * * get the default response for the specified data object * there are no rules that match any groups with this user * * INPUTS: * cache == agt_acm cache to use * nacmroot == pre-fectched NACM root * obj == data object template for this request * iswrite == TRUE for write access * FALSE for read access * * RETURNS: * TRUE if access granted * FALSE if access denied *********************************************************************/ static boolean get_default_data_response (agt_acm_cache_t *cache, val_value_t *nacmroot, const val_value_t *val, boolean iswrite) { const obj_template_t *testobj; val_value_t *noRule; boolean retval; /* check if the data node is tagged as * ncx:secure or ncx:very-secure and * deny access if so */ testobj = val->obj; /* special case -- there are no ACM rules for the * config root, so allow all writes on this * container and start checking at the top-level * YANG nodes instead */ if (iswrite && obj_is_root(val->obj)) { return TRUE; } /* make sure this is not an nested object within a * object tagged as ncx:secure or ncx:very-secure */ while (testobj) { if (iswrite) { /* reject any ncx:secure or ncx:very-secure object */ if (obj_is_secure(testobj) || obj_is_very_secure(testobj)) { return FALSE; } } else { /* allow ncx:secure to be read; reject ncx:very-secure */ if (obj_is_very_secure(testobj)) { return FALSE; } } /* stop at root */ if (obj_is_root(testobj)) { testobj = NULL; } else { testobj = testobj->parent; } /* no need to check further if the parent was the root * need to make sure not to go past the * config parameter into the rpc input * then the secret rpc */ if (testobj && obj_is_root(testobj)) { testobj = NULL; } } /* check the noDefaultRule setting on this agent */ if (iswrite) { if (cache->flags & FL_ACM_DEFWRITE_SET) { return (cache->flags & FL_ACM_DEFWRITE_OK) ? TRUE : FALSE; } noRule = val_find_child(nacmroot, AGT_ACM_MODULE, nacm_N_writeDefault); if (!noRule) { cache->flags |= FL_ACM_DEFWRITE_SET; return FALSE; /* default is FALSE */ } if (!xml_strcmp(VAL_ENUM_NAME(noRule), nacm_E_noRuleDefault_permit)) { retval = TRUE; } else { retval = FALSE; } cache->flags |= FL_ACM_DEFWRITE_SET; if (retval) { cache->flags |= FL_ACM_DEFWRITE_OK; } } else { if (cache->flags & FL_ACM_DEFREAD_SET) { return (cache->flags & FL_ACM_DEFREAD_OK) ? TRUE : FALSE; } noRule = val_find_child(nacmroot, AGT_ACM_MODULE, nacm_N_readDefault); if (!noRule) { cache->flags |= (FL_ACM_DEFREAD_SET | FL_ACM_DEFREAD_OK); return TRUE; /* default is TRUE */ } if (!xml_strcmp(VAL_ENUM_NAME(noRule), nacm_E_noRuleDefault_permit)) { retval = TRUE; } else { retval = FALSE; } cache->flags |= FL_ACM_DEFREAD_SET; if (retval) { cache->flags |= FL_ACM_DEFREAD_OK; } } return retval; } /* get_default_data_response */ static const char* get_rule_queue_access_str(unsigned int access_id) { if(access_id==DATA_RULE_QUEUE_READ) { return "read"; } else if(access_id==DATA_RULE_QUEUE_UPDATE) { return "update"; } else if(access_id==DATA_RULE_QUEUE_CREATE) { return "create"; } else if(access_id==DATA_RULE_QUEUE_DELETE) { return "delete"; } else { assert(0); } } static unsigned int get_rule_queue_access_id(const char* access_str) { if(0==strcmp("read", access_str)) { return DATA_RULE_QUEUE_READ; } else if(0==strcmp("update", access_str)) { return DATA_RULE_QUEUE_UPDATE; } else if(0==strcmp("create", access_str)) { return DATA_RULE_QUEUE_CREATE; } else if(0==strcmp("delete", access_str)) { return DATA_RULE_QUEUE_DELETE; } else { assert(0); } } /******************************************************************** * FUNCTION cache_data_rules * * Cache the data rules. * -- If there is a failure partway through caching the items * all the new datarules will be deleted * -- Do not set the flag unless the entire operation succeeds * -- On failure remove all added datarules * * INPUTS: * cache == agt_acm cache to use * nacmroot == /nacm node * * OUTPUTS: * *cache is modified with the cached datarules items. * * RETURNS: * NO_ERR on success or an error if the operation failed. * *********************************************************************/ static status_t cache_data_rules( agt_acm_cache_t *cache, val_value_t *nacmroot) { status_t res = NO_ERR; val_value_t *rule, *rule_list; val_value_t *valroot; int i; if (cache->flags & FL_ACM_DATARULES_SET) { // datarules have already been already cached, return no error //return NO_ERR; /* regenerate all cached datarules since no mechanism for updating config=false rules is in place yet */ agt_acm_datarule_t *freerule; for(i=0;idataruleQ[i])) { freerule = (agt_acm_datarule_t *)dlq_deque(&cache->dataruleQ[i]); if (freerule) { free_datarule(freerule); } else { return SET_ERROR(ERR_INTERNAL_VAL); } } } } /* the /nacm node is supposed to be a child of */ valroot = nacmroot->parent; if (!valroot || !obj_is_root(valroot->obj)) { return SET_ERROR(ERR_INTERNAL_VAL); } for ( rule_list = val_find_child( nacmroot, AGT_ACM_MODULE, nacm_N_ruleList ); rule_list != NULL; rule_list = val_find_next_child( rule_list, AGT_ACM_MODULE, nacm_N_ruleList, rule_list ) ) { /* check all the rule entries */ for ( rule = val_find_child( rule_list, AGT_ACM_MODULE, nacm_N_rule ); rule != NULL; rule = val_find_next_child( rule_list, AGT_ACM_MODULE, nacm_N_rule, rule ) ) { val_value_t *path; val_value_t *access_operations; xpath_pcb_t *pcb; xpath_result_t *result; /* get the XPath expression leaf */ path = val_find_child( rule, AGT_ACM_MODULE, nacm_N_path ); if ( !path || !path->xpathpcb ) { continue; } access_operations = val_find_child( rule, AGT_ACM_MODULE, nacm_N_accessOperations ); if ( !access_operations ) { continue; } for(i=0;ixpathpcb ); if(!pcb) { res = ERR_INTERNAL_MEM; break; } pcb->source = XP_SRC_YANG; result = xpath1_eval_expr( pcb, valroot, valroot, FALSE, (i==DATA_RULE_QUEUE_READ)?FALSE:TRUE /*configonly*/, &res ); if ( !result ) { res = ERR_INTERNAL_MEM; xpath_free_pcb(pcb); break; } if ( res == NO_ERR) { agt_acm_datarule_t *datarule_cache; datarule_cache = new_datarule(pcb, result, rule); if ( datarule_cache ) { /* pass off 'pcb' and 'result' memory here */ dlq_enque( datarule_cache, &cache->dataruleQ[i] ); } else { res = ERR_INTERNAL_MEM; } } if ( res != NO_ERR ) { xpath_free_pcb( pcb ); xpath_free_result( result ); break; } } } } if (res == NO_ERR) { cache->flags |= FL_ACM_DATARULES_SET; } else { for(i=0;idataruleQ[i])) { freerule = (agt_acm_datarule_t *)dlq_deque(&cache->dataruleQ[i]); if (freerule) { free_datarule(freerule); } else { SET_ERROR(ERR_INTERNAL_VAL); } } log_error("\nError: cache NACM data rules failed! (%s)", get_error_string(res)); } } return res; } /******************************************************************** * FUNCTION check_data_rules * * Check the configured /nacm/rules/dataRule list to see if the * user is allowed access the specified data object * * INPUTS: * cache == agt_acm cache to use * nacmroot == /nacm node prefetched * val == value node requested * access == string (enum name) for the requested access * (read, write) * usergroups == user-to-group mapping for access processing * done == address of return done processing flag * * OUTPUTS: * *done == TRUE if a rule was found, so return value is * the final answer * == FALSE if no dataRule was found to match * * RETURNS: * only valid if *done == TRUE: * TRUE if authorization to access data is granted * FALSE if authorization to access data is not granted *********************************************************************/ static boolean check_data_rules (agt_acm_cache_t *cache, val_value_t *nacmroot, const val_value_t *val, const xmlChar *access, agt_acm_usergroups_t *usergroups, boolean *done) { dlq_hdr_t *resnodeQ; agt_acm_datarule_t *datarule_cache; boolean granted = FALSE; status_t res = NO_ERR; int access_id; *done = FALSE; /* fill the dataruleQ in the cache if needed */ #if 0 if (!(cache->flags & FL_ACM_DATARULES_SET)) #else if (1) #endif { res = cache_data_rules( cache, nacmroot); } if ( res != NO_ERR ) { *done = FALSE; return FALSE; } access_id = get_rule_queue_access_id(access); { /* go through the cache and exit if any matches are found */ for ( datarule_cache = (agt_acm_datarule_t *) dlq_firstEntry(&cache->dataruleQ[access_id]); datarule_cache != NULL && !*done; datarule_cache = (agt_acm_datarule_t *) dlq_nextEntry(datarule_cache)) { if(!check_rule_group (datarule_cache->datarule, usergroups)) { continue; } resnodeQ = xpath_get_resnodeQ(datarule_cache->result); assert(resnodeQ); if ( ((access_id==DATA_RULE_QUEUE_READ) && xpath1_check_node_child_exists_slow( datarule_cache->pcb,resnodeQ, val )) || ((access_id==DATA_RULE_QUEUE_UPDATE) && !obj_is_leaf(val->obj)) || xpath1_check_node_exists_slow( datarule_cache->pcb, resnodeQ, val )) { val_value_t* action_val; *done = TRUE; action_val = val_find_child(datarule_cache->datarule, AGT_ACM_MODULE, "action"); if(action_val && 0==strcmp(VAL_STRING(action_val),"deny")) { granted = FALSE; } else { granted = TRUE; } } } } if ( res != NO_ERR ) { *done = TRUE; } return granted; } /* check_data_rules */ /******************************************************************** * FUNCTION valnode_access_allowed * * Check if the specified user is allowed to access a value node * The val->obj template will be checked against the val->editop * requested ac cess and the user's configured max-access * * INPUTS: * cache == cache for this session/message * user == user name string * val == val_value_t in progress to check * newval == newval val_value_t in progress to check (write only) * curval == curval val_value_t in progress to check (write only) * editop == edit operation if this is a write; ignored otherwise * RETURNS: * TRUE if user allowed this level of access to the value node *********************************************************************/ static boolean valnode_access_allowed (agt_acm_cache_t *cache, const xmlChar *user, const val_value_t *val, const val_value_t *newval, const val_value_t *curval, op_editop_t editop) { char* access; val_value_t *nacmroot = NULL; boolean iswrite; logfn_t logfn; /* check if this is a read or a write */ if ((newval!=NULL) || (curval!=NULL)) { iswrite = TRUE; logfn = (log_writes) ? log_debug2 : log_noop; } else { iswrite = FALSE; logfn = (log_reads) ? log_debug4 : log_noop; } /* make sure object is not the special node */ if (obj_is_root(val->obj)) { (*logfn)("\nagt_acm: PERMIT (root-node)"); return TRUE; } /* super user is allowed to access anything except user-write blocked */ if (is_superuser(user)) { (*logfn)("\nagt_acm: PERMIT (superuser)"); return TRUE; } /* ncx:user-write blocking has highest priority */ if(iswrite) { switch (editop) { case OP_EDITOP_CREATE: access = "create"; if (obj_is_block_user_create(val->obj)) { (*logfn)("\nagt_acm: DENY (block-user-create)"); return FALSE; } break; case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: access = "delete"; if (obj_is_block_user_delete(val->obj)) { (*logfn)("\nagt_acm: DENY (block-user-delete)"); return FALSE; } break; default: /* This is a merge or replace. If blocked, * first make sure this requested operation is * also the effective operation; otherwise * the user will never be able to update a sub-node * of a list or container with update access blocked */ access = "update"; if (obj_is_block_user_update(val->obj)) { if (agt_apply_this_node(editop, newval, curval)) { (*logfn)("\nagt_acm: DENY (block-user-update)"); return FALSE; } } } } else { access = "read"; } if (cache->mode == AGT_ACMOD_DISABLED) { (*logfn)("\nagt_acm: PERMIT (NACM disabled)"); return TRUE; } /* check if access granted without any rules */ if (check_mode(access, val->obj)) { (*logfn)("\nagt_acm: PERMIT (permissive mode)"); return TRUE; } /* get the NACM root to decide any more */ if (cache->nacmroot) { nacmroot = cache->nacmroot; } else { nacmroot = get_nacm_root(); if (!nacmroot) { SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } else { cache->nacmroot = nacmroot; } } uint32 groupcnt = 0; agt_acm_usergroups_t *usergroups = NULL; if (cache->usergroups) { usergroups = cache->usergroups; groupcnt = cache->groupcnt; } else { usergroups = get_usergroups_entry(nacmroot, user, &groupcnt); if (!usergroups) { /* out of memory! deny all access! */ return FALSE; } else { cache->usergroups = usergroups; cache->groupcnt = groupcnt; } } /* usergroups malloced at this point */ boolean retval = FALSE; boolean done = FALSE; const xmlChar *substr = iswrite ? (const xmlChar *)"write-default" : (const xmlChar *)"read-default"; if (groupcnt == 0) { /* just check the default for this RPC operation */ retval = get_default_data_response(cache, nacmroot, val, iswrite); // substr set already } else { /* there is a rules node so check the dataRule list */ if (!done) { retval = check_data_rules(cache, nacmroot, val, access, usergroups, &done); if (done) { substr = (const xmlChar *)"data-rule"; } else { /* no data rule found; try a module namespace rule */ retval = check_module_rules(cache, nacmroot, val->obj, access, usergroups, &done); if (done) { substr = (const xmlChar *)"module-rule"; } else { /* no module rule so use the default */ retval = get_default_data_response(cache, nacmroot, val, iswrite); // substr already set } } } } if (iswrite) { (*logfn)("\nagt_acm: %s write (%s)", retval ? "PERMIT" : "DENY", substr ? substr : NCX_EL_NONE); } else { (*logfn)("\nagt_acm: %s read (%s)", retval ? "PERMIT" : "DENY", substr ? substr : NCX_EL_NONE); } return retval; } /* valnode_access_allowed */ /******************************************************************** * FUNCTION check_notif_rules * * Check the configured /nacm/rules/notification-rule list to see if the * user is allowed to invoke the specified RPC method * * INPUTS: * nacmrootval == /nacm node, pre-fetched * notifobj == notification template requested * usergroups == user-to-group mapping for access processing * done == address of return done processing flag * * OUTPUTS: * *done == TRUE if a rule was found, so return value is * the final answer * == FALSE if no rpcRule was found to match * * RETURNS: * only valid if *done == TRUE: * TRUE if authorization to receive the notification is granted * FALSE if authorization to receive the notification is not granted *********************************************************************/ static boolean check_notif_rules (val_value_t *nacmrootval, const obj_template_t *notifobj, agt_acm_usergroups_t *usergroups, boolean *done) { val_value_t *notifrule, *modname, *notifname, *rule_list, *rule; boolean granted, done2; status_t res; *done = FALSE; granted = FALSE; res = NO_ERR; /* check all the notification rule entries */ for (rule_list = val_find_child(nacmrootval, AGT_ACM_MODULE, nacm_N_ruleList); rule_list != NULL && res == NO_ERR && !*done; rule_list = val_find_next_child(nacmrootval, AGT_ACM_MODULE, nacm_N_ruleList, rule_list)) { for (rule = val_find_child(rule_list, AGT_ACM_MODULE, nacm_N_rule); rule != NULL && res == NO_ERR && !*done; rule = val_find_next_child(rule_list, AGT_ACM_MODULE, nacm_N_rule, rule)) { /* get the module name key */ modname = val_find_child(rule, AGT_ACM_MODULE, nacm_N_moduleName); /* get the notification name key */ notifname = val_find_child(rule, AGT_ACM_MODULE, nacm_N_notificationName); if (!notifname) { continue; } /* check if this is the right module */ if ((modname!=NULL) && xml_strcmp("*", VAL_STR(modname)) && xml_strcmp(obj_get_mod_name(notifobj), VAL_STR(modname)) && (xml_strcmp(obj_get_mod_name(notifobj), "yuma-netconf") && xml_strcmp("ietf-netconf", VAL_STR(modname))) ) { continue; } /* check if this is the right notification event */ if (xml_strcmp(obj_get_name(notifobj), VAL_STR(notifname))) { continue; } /* this notification-rule is for the specified event * check if any of the groups in the usergroups * list for this user match any of the groups in * the allowed-group leaf-list */ done2 = FALSE; granted = check_access_bit(rule, nacm_E_allowedRights_read, usergroups, &done2); if (done2) { *done = TRUE; } else { granted = FALSE; } } } return granted; } /* check_notif_rules */ /******************************************************************** * FUNCTION get_default_notif_response * * get the default response for the specified notification object * there are no rules that match any groups with this user * * INPUTS: * cache == agt_acm cache to check * nacmroot == pre-fectched NACM root * notifobj == notification template for this request * * RETURNS: * TRUE if access granted * FALSE if access denied *********************************************************************/ static boolean get_default_notif_response (agt_acm_cache_t *cache, val_value_t *nacmroot, const obj_template_t *notifobj) { val_value_t *noRule; boolean retval; /* check if the notification event is tagged as * nacm:secure or nacm:very-secure and * deny access if so */ if (obj_is_secure(notifobj) || obj_is_very_secure(notifobj)) { return FALSE; } /* check the read-default setting */ if (cache->flags & FL_ACM_DEFREAD_SET) { return (cache->flags & FL_ACM_DEFREAD_OK) ? TRUE : FALSE; } noRule = val_find_child(nacmroot, AGT_ACM_MODULE, nacm_N_readDefault); if (!noRule) { cache->flags |= (FL_ACM_DEFREAD_SET | FL_ACM_DEFREAD_OK); return TRUE; /* default is TRUE */ } if (!xml_strcmp(VAL_ENUM_NAME(noRule), nacm_E_noRuleDefault_permit)) { retval = TRUE; } else { retval = FALSE; } cache->flags |= FL_ACM_DEFREAD_SET; if (retval) { cache->flags |= FL_ACM_DEFREAD_OK; } return retval; } /* get_default_notif_response */ /******************************************************************** * FUNCTION get_denied_perations * * operation handler for the nacm/denied_operations counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ static status_t get_denied_operations (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { (void)scb; (void)virval; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } VAL_UINT(dstval) = denied_operations_count; return NO_ERR; } /* get_denied_operations */ /******************************************************************** * FUNCTION get_deniedDataWrites * * operation handler for the nacm/deniedDataWrites counter * * INPUTS: * see ncx/getcb.h getcb_fn_t for details * * RETURNS: * status *********************************************************************/ static status_t get_deniedDataWrites (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { (void)scb; (void)virval; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } VAL_UINT(dstval) = denied_data_writes_count; return NO_ERR; } /* get_deniedDataWrites */ /******************************************************************** * FUNCTION nacm_callback * * top-level nacm callback function * * INPUTS: * see agt/agt_cb.h (agt_cb_pscb_t) * * RETURNS: * status *********************************************************************/ static status_t nacm_callback (ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; boolean clear_cache; val_value_t *useval = NULL; (void)scb; (void)msg; if (newval != NULL) { useval = newval; } else if (curval != NULL) { useval = curval; } if (LOGDEBUG2) { log_debug2("\nServer %s callback: t: %s:%s, op:%s\n", agt_cbtype_name(cbtyp), (useval) ? val_get_mod_name(useval) : NCX_EL_NONE, (useval) ? useval->name : NCX_EL_NONE, op_editop_name(editop)); } res = NO_ERR; clear_cache = FALSE; switch (cbtyp) { case AGT_CB_VALIDATE: break; case AGT_CB_APPLY: break; case AGT_CB_COMMIT: switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: case OP_EDITOP_REPLACE: case OP_EDITOP_CREATE: case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: clear_cache = TRUE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: clear_cache = TRUE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (clear_cache) { if (notif_cache != NULL) { free_acm_cache(notif_cache); notif_cache = NULL; } agt_ses_invalidate_session_acm_caches(); } return res; } /* nacm_callback */ /******************************************************************** * FUNCTION nacm_enable_nacm_callback * * /nacm/enable-nacm callback function * * INPUTS: * see agt/agt_cb.h (agt_cb_pscb_t) * * RETURNS: * status *********************************************************************/ static status_t nacm_enable_nacm_callback (ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; (void)scb; (void)msg; if (LOGDEBUG2) { val_value_t *useval = NULL; if (newval != NULL) { useval = newval; } else if (curval != NULL) { useval = curval; } log_debug2("\nServer %s callback: t: %s:%s, op:%s\n", agt_cbtype_name(cbtyp), (useval != NULL) ? val_get_mod_name(useval) : NCX_EL_NONE, (useval != NULL) ? useval->name : NCX_EL_NONE, op_editop_name(editop)); } res = NO_ERR; switch (cbtyp) { case AGT_CB_VALIDATE: break; case AGT_CB_APPLY: break; case AGT_CB_COMMIT: switch (editop) { case OP_EDITOP_LOAD: case OP_EDITOP_MERGE: case OP_EDITOP_REPLACE: case OP_EDITOP_CREATE: if (newval != NULL && VAL_BOOL(newval)) { if (acmode != AGT_ACMOD_ENFORCING) { log_info("\nEnabling NACM Enforcing mode"); } acmode = AGT_ACMOD_ENFORCING; } else { log_warn("\nWarning: Disabling NACM"); acmode = AGT_ACMOD_DISABLED; } break; case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: log_warn("\nWarning: Disabling NACM"); acmode = AGT_ACMOD_DISABLED; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* nacm_enable_nacm_callback */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION agt_acm_init * * Initialize the NCX Agent access control module * * INPUTS: * none * RETURNS: * status of the initialization procedure *********************************************************************/ status_t agt_acm_init (void) { status_t res; agt_profile_t *agt_profile; if (agt_acm_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } log_debug2("\nagt: Loading NETCONF Access Control module"); agt_profile = agt_get_profile(); nacmmod = NULL; notif_cache = NULL; /* load in the access control parameters */ res = ncxmod_load_module(AGT_ACM_MODULE, NULL, &agt_profile->agt_savedevQ, &nacmmod); if (res != NO_ERR) { return res; } superuser = NULL; acmode = AGT_ACMOD_ENFORCING; denied_operations_count = 0; denied_data_writes_count = 0; agt_acm_init_done = TRUE; log_reads = FALSE; log_writes = TRUE; res = agt_cb_register_callback(AGT_ACM_MODULE, nacm_OID_nacm, NULL, nacm_callback); if (res != NO_ERR) { return res; } res = agt_cb_register_callback(AGT_ACM_MODULE, nacm_OID_nacm_enable_nacm, NULL, nacm_enable_nacm_callback); if (res != NO_ERR) { return res; } return NO_ERR; } /* agt_acm_init */ /******************************************************************** * FUNCTION agt_acm_init2 * * Phase 2: * Initialize the nacm.yang configuration data structures * * INPUTS: * none * RETURNS: * status of the initialization procedure *********************************************************************/ status_t agt_acm_init2 (void) { const agt_profile_t *profile; val_value_t *nacmval, *childval; status_t res = NO_ERR; boolean added = FALSE; if (!agt_acm_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } profile = agt_get_profile(); superuser = profile->agt_superuser; if (profile->agt_accesscontrol_enum != AGT_ACMOD_NONE) { acmode = profile->agt_accesscontrol_enum; } log_reads = profile->agt_log_acm_reads; log_writes = profile->agt_log_acm_writes; // no controls for RPC; notification treated as a read nacmval = agt_add_top_node_if_missing(nacmmod, nacm_N_nacm, &added, &res); if (res != NO_ERR || nacmval == NULL) { return res; } if (added) { /* add following leafs: leaf /nacm/enable-nacm leaf /nacm/read-default leaf /nacm/write-default leaf /nacm/exec-default These values are saved in NV-store, even if the CLI config has disabled NACM. TBD: what to do about operator thinking NACM is enabled but it is really turned off!! */ res = val_add_defaults(nacmval, NULL, NULL, FALSE); if (res != NO_ERR) { return res; } } /* add read-only virtual leafs to the nacm value node * create /nacm/denied-operations */ childval = agt_make_virtual_leaf(nacmval->obj, nacm_N_denied_operations, get_denied_operations, &res); if (childval != NULL) { val_add_child_sorted(childval, nacmval); } /* create /nacm/deniedDataWrites */ if (res == NO_ERR) { childval = agt_make_virtual_leaf(nacmval->obj, nacm_N_deniedDataWrites, get_deniedDataWrites, &res); if (childval != NULL) { val_add_child_sorted(childval, nacmval); } } notif_cache = new_acm_cache(); if (notif_cache == NULL) { res = ERR_INTERNAL_MEM; } return res; } /* agt_acm_init2 */ /******************************************************************** * FUNCTION agt_acm_cleanup * * Cleanup the NCX Agent access control module * *********************************************************************/ void agt_acm_cleanup (void) { if (!agt_acm_init_done) { return; } agt_cb_unregister_callbacks(AGT_ACM_MODULE, nacm_OID_nacm); agt_cb_unregister_callbacks(AGT_ACM_MODULE, nacm_OID_nacm_enable_nacm); nacmmod = NULL; if (notif_cache != NULL) { free_acm_cache(notif_cache); } agt_acm_init_done = FALSE; } /* agt_acm_cleanup */ /******************************************************************** * FUNCTION agt_acm_rpc_allowedg * * Check if the specified user is allowed to invoke an RPC * * INPUTS: * msg == XML header in incoming message in progress * user == user name string * rpcobj == obj_template_t for the RPC method to check * * RETURNS: * TRUE if user allowed invoke this RPC; FALSE otherwise *********************************************************************/ boolean agt_acm_rpc_allowed (xml_msg_hdr_t *msg, const xmlChar *user, const obj_template_t *rpcobj) { val_value_t *nacmroot, *rulesval; agt_acm_usergroups_t *usergroups; agt_acm_cache_t *cache; uint32 groupcnt; boolean retval, done; assert( msg && "msg is NULL!" ); assert( user && "user is NULL!" ); assert( rpcobj && "rpcobj is NULL!" ); log_debug2("\nagt_acm: check <%s> RPC allowed for user '%s'", obj_get_name(rpcobj), user); if (acmode == AGT_ACMOD_DISABLED) { log_debug2("\nagt_acm: PERMIT (NACM disabled)"); return TRUE; } /* super user is allowed to access anything */ if (is_superuser(user)) { log_debug2("\nagt_acm: PERMIT (superuser)"); return TRUE; } /* everybody is allowed to close their own session */ if (obj_get_nsid(rpcobj) == xmlns_nc_id() && !xml_strcmp(obj_get_name(rpcobj), NCX_EL_CLOSE_SESSION)) { log_debug2("\nagt_acm: PERMIT (close-session)"); return TRUE; } /* check if access granted without any rules */ if (check_mode(nacm_E_allowedRights_exec, rpcobj)) { log_debug2("\nagt_acm: PERMIT (permissive mode)"); return TRUE; } cache = msg->acm_cache; /* get the NACM root to decide any more */ if (cache->nacmroot) { nacmroot = cache->nacmroot; } else { nacmroot = get_nacm_root(); if (!nacmroot) { SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } else { cache->nacmroot = nacmroot; } } groupcnt = 0; if (cache->usergroups) { usergroups = cache->usergroups; groupcnt = cache->groupcnt; } else { usergroups = get_usergroups_entry(nacmroot, user, &groupcnt); if (!usergroups) { /* out of memory! deny all access! */ log_debug2("\nagt_acm: DENY (out of memory)"); return FALSE; } else { cache->usergroups = usergroups; cache->groupcnt = groupcnt; } } /* usergroups malloced at this point */ retval = FALSE; const xmlChar *substr = NULL; if (groupcnt == 0) { /* just check the default for this RPC operation */ retval = get_default_rpc_response(cache, nacmroot, rpcobj); substr = (const xmlChar *)"exec-default"; } else { { /* there is a rules node so check the rpcRules */ done = FALSE; retval = check_rpc_rules(nacmroot, rpcobj, usergroups, &done); if (done) { substr = (const xmlChar *)"rpc-rule"; } else { /* no RPC rule found; try a module namespace rule */ retval = check_module_rules(cache, rulesval, rpcobj, nacm_E_allowedRights_exec, usergroups, &done); if (done) { substr = (const xmlChar *)"module-rule"; } else { /* no module rule so use the default */ retval = get_default_rpc_response(cache, nacmroot, rpcobj); substr = (const xmlChar *)"exec-default"; } } } } if (!retval) { denied_operations_count++; } log_debug2("\nagt_acm: %s (%s)", retval ? "PERMIT" : "DENY", substr ? substr : NCX_EL_NONE); return retval; } /* agt_acm_rpc_allowed */ /******************************************************************** * FUNCTION agt_acm_notif_allowed * * Check if the specified user is allowed to receive * a notification event * * INPUTS: * user == user name string * notifobj == obj_template_t for the notification event to check * * RETURNS: * TRUE if user allowed receive this notification event; * FALSE otherwise *********************************************************************/ boolean agt_acm_notif_allowed (const xmlChar *user, const obj_template_t *notifobj) { val_value_t *nacmroot, *rulesval; agt_acm_usergroups_t *usergroups; uint32 groupcnt; boolean retval, done; logfn_t logfn; assert( user && "user is NULL!" ); assert( notifobj && "notifobj is NULL!" ); logfn = (log_reads) ? log_debug2 : log_noop; (*logfn)("\nagt_acm: check <%s> Notification allowed for user '%s'", obj_get_name(notifobj), user); if (acmode == AGT_ACMOD_DISABLED) { (*logfn)("\nagt_acm: PERMIT (NACM disabled)"); return TRUE; } /* super user is allowed to access anything */ if (is_superuser(user)) { (*logfn)("\nagt_acm: PERMIT (superuser)"); return TRUE; } /* do not block a replayComplete or notificationComplete event */ if (agt_not_is_replay_event(notifobj)) { (*logfn)("\nagt_acm: PERMIT (not a replay event)"); return TRUE; } /* check if access granted without any rules */ if (check_mode(nacm_E_allowedRights_read, notifobj)) { (*logfn)("\nagt_acm: PERMIT (permissive mode)"); return TRUE; } /* get the notification acm cache */ if (notif_cache == NULL) { notif_cache = new_acm_cache(); if (notif_cache == NULL) { log_error("\nagt_acm: malloc failed"); return FALSE; } } /* get the NACM root to decide any more */ if (notif_cache->nacmroot) { nacmroot = notif_cache->nacmroot; } else { nacmroot = get_nacm_root(); if (!nacmroot) { SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } else { notif_cache->nacmroot = nacmroot; } } groupcnt = 0; if (notif_cache->usergroups && !xml_strcmp(notif_cache->usergroups->username, user)) { usergroups = notif_cache->usergroups; groupcnt = notif_cache->groupcnt; } else { usergroups = get_usergroups_entry(nacmroot, user, &groupcnt); if (!usergroups) { /* out of memory! deny all access! */ (*logfn)("\nagt_acm: DENY (out of memory)"); return FALSE; } else { if (notif_cache->usergroups) { free_usergroups(notif_cache->usergroups); } notif_cache->usergroups = usergroups; notif_cache->groupcnt = groupcnt; } } /* usergroups malloced at this point */ retval = FALSE; const xmlChar *substr = NULL; if (groupcnt == 0) { /* just check the default for this notification */ retval = get_default_notif_response(notif_cache, nacmroot, notifobj); substr = (const xmlChar *)"read-default"; } else { done = FALSE; retval = check_notif_rules(nacmroot, notifobj, usergroups, &done); if (done) { substr = (const xmlChar *)"notification-rule"; } else { /* no notification rule found; * try a module namespace rule */ retval = check_module_rules(notif_cache, rulesval, notifobj, nacm_E_allowedRights_read, usergroups, &done); if (done) { substr = (const xmlChar *)"module-rule"; } else { /* no module rule so use the default */ retval = get_default_notif_response(notif_cache, nacmroot, notifobj); substr = (const xmlChar *)"read-default"; } } } (*logfn)("\nagt_acm: %s (%s)", retval ? "PERMIT" : "DENY", substr ? substr : NCX_EL_NONE); return retval; } /* agt_acm_notif_allowed */ /******************************************************************** * FUNCTION agt_acm_val_write_allowed * * Check if the specified user is allowed to access a value node * The val->obj template will be checked against the val->editop * requested access and the user's configured max-access * * INPUTS: * msg == XML header from incoming message in progress * newval == val_value_t in progress to check * (may be NULL, if curval set) * curval == val_value_t in progress to check * (may be NULL, if newval set) * editop == requested CRUD operation * * RETURNS: * TRUE if user allowed this level of access to the value node *********************************************************************/ boolean agt_acm_val_write_allowed (xml_msg_hdr_t *msg, const xmlChar *user, const val_value_t *newval, const val_value_t *curval, op_editop_t editop) { boolean retval; const val_value_t *val = (newval) ? newval : curval; logfn_t logfn = (log_writes) ? log_debug2 : log_noop; (*logfn)("\nagt_acm: check write <%s> allowed for user '%s'", val->name, user); /* do not check writes during the bootup process * cannot compare 'superuser' name in case it is * disabled or changed from the default */ if (editop == OP_EDITOP_LOAD) { (*logfn)("\nagt_acm: PERMIT (OP_EDITOP_LOAD)"); return TRUE; } /* defer check if no edit op requested on this node */ if (editop == OP_EDITOP_NONE) { (*logfn)("\nagt_acm: PERMIT (OP_EDITOP_NONE)"); return TRUE; } assert( msg && "msg is NULL!" ); assert( user && "user is NULL!" ); assert( val && "val is NULL!" ); if (msg->acm_cache == NULL) { /* this is a rollback operation so just allow it */ (*logfn)("\nagt_acm: PERMIT (rollback)"); return TRUE; } retval = valnode_access_allowed(msg->acm_cache, user, val, newval, curval, editop); if (!retval) { denied_data_writes_count++; } return retval; } /* agt_acm_val_write_allowed */ /******************************************************************** * FUNCTION agt_acm_val_read_allowed * * Check if the specified user is allowed to read a value node * * INPUTS: * msg == XML header from incoming message in progress * user == user name string * val == val_value_t in progress to check * * RETURNS: * TRUE if user allowed read access to the value node *********************************************************************/ boolean agt_acm_val_read_allowed (xml_msg_hdr_t *msg, const xmlChar *user, const val_value_t *val) { assert( msg && "msg is NULL!" ); assert( msg->acm_cache && "cache is NULL!" ); assert( user && "user is NULL!" ); assert( val && "val is NULL!" ); if (log_reads) { log_debug4("\nagt_acm: check read on <%s> allowed for user '%s'", val->name, user); } return valnode_access_allowed(msg->acm_cache, user, val, NULL, NULL, OP_EDITOP_NONE); } /* agt_acm_val_read_allowed */ /******************************************************************** * FUNCTION agt_acm_init_msg_cache * * Malloc and initialize an agt_acm_cache_t struct * and attach it to the incoming message * * INPUTS: * scb == session control block to use * msg == message to use * * OUTPUTS: * scb->acm_cache pointer may be set, if it was NULL * msg->acm_cache pointer set * * RETURNS: * status *********************************************************************/ status_t agt_acm_init_msg_cache (ses_cb_t *scb, xml_msg_hdr_t *msg) { assert( scb && "scb is NULL!" ); assert( msg && "msg is NULL!" ); if (msg->acm_cache) { SET_ERROR(ERR_INTERNAL_INIT_SEQ); agt_acm_clear_msg_cache(msg); } msg->acm_cbfn = agt_acm_val_read_allowed; if (agt_acm_session_cache_valid(scb)) { msg->acm_cache = scb->acm_cache; } else { if (scb->acm_cache != NULL) { free_acm_cache(scb->acm_cache); } scb->acm_cache = new_acm_cache(); msg->acm_cache = scb->acm_cache; } if (msg->acm_cache == NULL) { return ERR_INTERNAL_MEM; } else { return NO_ERR; } } /* agt_acm_init_msg_cache */ /******************************************************************** * FUNCTION agt_acm_clear_msg_cache * * Clear an agt_acm_cache_t struct * attached to the specified message * * INPUTS: * msg == message to use * * OUTPUTS: * msg->acm_cache pointer is freed and set to NULL * *********************************************************************/ void agt_acm_clear_msg_cache (xml_msg_hdr_t *msg) { assert( msg && "msg is NULL!" ); msg->acm_cbfn = NULL; msg->acm_cache = NULL; } /* agt_acm_clear_msg_cache */ /******************************************************************** * FUNCTION agt_acm_clear_session_cache * * Clear an agt_acm_cache_t struct in a session control block * * INPUTS: * scb == sesion control block to use * * OUTPUTS: * scb->acm_cache pointer is freed and set to NULL * *********************************************************************/ void agt_acm_clear_session_cache (ses_cb_t *scb) { assert( scb && "scb is NULL!" ); if (scb->acm_cache != NULL) { free_acm_cache(scb->acm_cache); scb->acm_cache = NULL; } } /* agt_acm_clear_session_cache */ /******************************************************************** * FUNCTION agt_acm_invalidate_session_cache * * Mark an agt_acm_cache_t struct in a session control block * as invalid so it will be refreshed next use * * INPUTS: * scb == sesion control block to use * *********************************************************************/ void agt_acm_invalidate_session_cache (ses_cb_t *scb) { assert( scb && "scb is NULL!" ); if (scb->acm_cache != NULL) { scb->acm_cache->flags &= ~FL_ACM_CACHE_VALID; } } /* agt_acm_invalidate_session_cache */ /******************************************************************** * FUNCTION agt_acm_session_cache_valid * * Check if a session ACM cache is valid * * INPUTS: * scb == sesion control block to check * * RETURNS: * TRUE if cache calid * FALSE if cache invalid or NULL *********************************************************************/ boolean agt_acm_session_cache_valid (const ses_cb_t *scb) { assert( scb && "scb is NULL!" ); if (scb->acm_cache != NULL) { return (scb->acm_cache->flags & FL_ACM_CACHE_VALID) ? TRUE : FALSE; } else { return FALSE; } } /* agt_acm_session_cache_valid */ /******************************************************************** * FUNCTION agt_acm_session_is_superuser * * Check if the specified session is the superuser * * INPUTS: * scb == session to check * * RETURNS: * TRUE if session is for the superuser * FALSE if session is not for the superuser *********************************************************************/ boolean agt_acm_session_is_superuser (const ses_cb_t *scb) { assert( scb && "scb is NULL!" ); return is_superuser(scb->username); } /* agt_acm_session_is_superuser */ /* END file agt_acm.c */ yuma123_2.14/netconf/src/agt/agt_util.h0000664000175000017500000010547314770023131020114 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_util #define _H_agt_util /* FILE: agt_util.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Utility Functions for NCX Server method routines ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24-may-06 abb Begun */ #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_getcb #include "getcb.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xml_msg #include "xml_msg.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_get_cfg_from_parm * * Get the cfg_template_t for the config in the target param * * INPUTS: * parmname == parameter to get from (e.g., target) * msg == incoming rpc_msg_t in progress * methnode == XML node for RPC method (for errors) * retcfg == address of return cfg pointer * * OUTPUTS: * *retcfg is set to the address of the cfg_template_t struct * RETURNS: * status *********************************************************************/ extern status_t agt_get_cfg_from_parm (const xmlChar *parmname, rpc_msg_t *msg, xml_node_t *methnode, cfg_template_t **retcfg); /******************************************************************** * FUNCTION agt_get_inline_cfg_from_parm * * Get the val_value_t node for the inline config element * * INPUTS: * parmname == parameter to get from (e.g., source) * msg == incoming rpc_msg_t in progress * methnode == XML node for RPC method (for errors) * retval == address of return value node pointer * * OUTPUTS: * *retval is set to the address of the val_value_t struct * * RETURNS: * status *********************************************************************/ extern status_t agt_get_inline_cfg_from_parm (const xmlChar *parmname, rpc_msg_t *msg, xml_node_t *methnode, val_value_t **retval); /******************************************************************** * FUNCTION agt_get_url_from_parm * * Get the URL string for the config in the target param * * 1) an edit-content choice for edit-config * 2) a source or target config-choice option for copy-config * 3) a target of delete-config * 4) config-source choice option for validate * * INPUTS: * parmname == parameter to get from (e.g., target) * msg == incoming rpc_msg_t in progress * methnode == XML node for RPC method (for errors) * returl == address of return URL string pointer * retval == address of value node from input used * OUTPUTS: * *returl is set to the address of the URL string * pointing to the memory inside the found parameter * *retval is set to parm node found, if return NO_ERR * RETURNS: * status *********************************************************************/ extern status_t agt_get_url_from_parm (const xmlChar *parmname, rpc_msg_t *msg, xml_node_t *methnode, const xmlChar **returl, val_value_t **retval); /******************************************************************** * FUNCTION agt_get_filespec_from_url * * Check the URL and get the filespec part out of it * * INPUTS: * urlstr == URL to check * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced URL string; must be freed by caller!! * NULL if some error *********************************************************************/ extern xmlChar * agt_get_filespec_from_url (const xmlChar *urlstr, status_t *res); /******************************************************************** * FUNCTION agt_get_parmval * * Get the identified val_value_t for a given parameter * Used for error purposes! * INPUTS: * parmname == parameter to get * msg == incoming rpc_msg_t in progress * * RETURNS: * status *********************************************************************/ extern const val_value_t * agt_get_parmval (const xmlChar *parmname, rpc_msg_t *msg); /******************************************************************** * FUNCTION agt_record_error * * Generate an rpc_err_rec_t and save it in the msg * * INPUTS: * scb == session control block * == NULL and no stats will be recorded * msghdr == XML msg header with error Q * == NULL, no errors will be recorded! * layer == netconf layer error occured * res == internal error code * xmlnode == XML node causing error * == NULL if not available * parmtyp == type of node in 'error_info' * error_info == error data, specific to 'res' * == NULL if not available (then nodetyp ignored) * nodetyp == type of node in 'error_path' * error_path == internal data node with the error * == NULL if not available or not used * OUTPUTS: * errQ has error message added if no malloc errors * scb->stats may be updated if scb non-NULL * * RETURNS: * none *********************************************************************/ extern void agt_record_error (ses_cb_t *scb, xml_msg_hdr_t *msghdr, ncx_layer_t layer, status_t res, const xml_node_t *xmlnode, ncx_node_t parmtyp, const void *error_info, ncx_node_t nodetyp, void *error_path); /******************************************************************** * FUNCTION agt_record_error_errinfo * * Generate an rpc_err_rec_t and save it in the msg * Use the provided error info record for fields * * INPUTS: * scb == session control block * == NULL and no stats will be recorded * msghdr == XML msg header with error Q * == NULL, no errors will be recorded! * layer == netconf layer error occured * res == internal error code * xmlnode == XML node causing error * == NULL if not available * parmtyp == type of node in 'error_info' * error_info == error data, specific to 'res' * == NULL if not available (then nodetyp ignored) * nodetyp == type of node in 'error_path' * error_path == internal data node with the error * == NULL if not available or not used * errinfo == error info record to use * * OUTPUTS: * errQ has error message added if no malloc errors * scb->stats may be updated if scb non-NULL * RETURNS: * none *********************************************************************/ extern void agt_record_error_errinfo (ses_cb_t *scb, xml_msg_hdr_t *msghdr, ncx_layer_t layer, status_t res, const xml_node_t *xmlnode, ncx_node_t parmtyp, const void *error_parm, ncx_node_t nodetyp, void *error_path, const ncx_errinfo_t *errinfo); /******************************************************************** * FUNCTION agt_record_attr_error * * Generate an rpc_err_rec_t and save it in the msg * * INPUTS: * scb == session control block * msghdr == XML msg header with error Q * == NULL, no errors will be recorded! * layer == netconf layer error occured * res == internal error code * xmlattr == XML attribute node causing error * (NULL if not available) * xmlnode == XML node containing the attr * badns == bad namespace string value * nodetyp == type of node in 'errnode' * errnode == internal data node with the error * == NULL if not used * * OUTPUTS: * errQ has error message added if no malloc errors * * RETURNS: * none *********************************************************************/ extern void agt_record_attr_error (ses_cb_t *scb, xml_msg_hdr_t *msghdr, ncx_layer_t layer, status_t res, const xml_attr_t *xmlattr, const xml_node_t *xmlnode, const xmlChar *badns, ncx_node_t nodetyp, const void *errnode); /******************************************************************** * FUNCTION agt_record_insert_error * * Generate an rpc_err_rec_t and save it in the msg * Use the provided error info record for fields * * For YANG 'missing-instance' error-app-tag * * INPUTS: * scb == session control block * == NULL and no stats will be recorded * msghdr == XML msg header with error Q * == NULL, no errors will be recorded! * res == internal error code * errval == value node generating the insert error * * OUTPUTS: * errQ has error message added if no malloc errors * scb->stats may be updated if scb non-NULL * RETURNS: * none *********************************************************************/ extern void agt_record_insert_error (ses_cb_t *scb, xml_msg_hdr_t *msghdr, status_t res, val_value_t *errval); /******************************************************************** * FUNCTION agt_record_unique_error * * Generate an rpc_err_rec_t and save it in the msg * Use the provided error info record for fields * * For YANG 'data-not-unique' error-app-tag * * INPUTS: * scb == session control block * == NULL and no stats will be recorded * msghdr == XML msg header with error Q * == NULL, no errors will be recorded! * errval == list value node that contains the unique-stmt * valuniqueQ == Q of val_unique_t structs for error-info * * OUTPUTS: * errQ has error message added if no malloc errors * scb->stats may be updated if scb non-NULL * RETURNS: * none *********************************************************************/ extern void agt_record_unique_error (ses_cb_t *scb, xml_msg_hdr_t *msghdr, val_value_t *errval, dlq_hdr_t *valuniqueQ); /******************************************************************** * FUNCTION agt_validate_filter * * Validate the parameter if present * * INPUTS: * scb == session control block * msg == rpc_msg_t in progress * * OUTPUTS: * msg->rpc_filter is filled in if NO_ERR; type could be NONE * RETURNS: * status *********************************************************************/ extern status_t agt_validate_filter (ses_cb_t *scb, rpc_msg_t *msg); /******************************************************************** * FUNCTION agt_validate_filter_ex * * Validate the parameter if present * * INPUTS: * scb == session control block * msg == rpc_msg_t in progress * filter == filter element to use * OUTPUTS: * msg->rpc_filter is filled in if NO_ERR; type could be NONE * RETURNS: * status *********************************************************************/ extern status_t agt_validate_filter_ex (ses_cb_t *scb, rpc_msg_t *msg, val_value_t *filter); /******************************************************************** * FUNCTION agt_check_config * * val_nodetest_fn_t callback * * Used by the operation to return any type of * configuration data * * INPUTS: * see ncx/val_util.h (val_nodetest_fn_t) * * RETURNS: * TRUE if config; FALSE if non-config *********************************************************************/ extern boolean agt_check_config (ncx_withdefaults_t withdef, boolean realtest, val_value_t *node); /******************************************************************** * FUNCTION agt_check_default * * val_nodetest_fn_t callback * * Used by the operation to return only values * not set to the default * * INPUTS: * see ncx/val_util.h (val_nodetest_fn_t) * * RETURNS: * status *********************************************************************/ extern boolean agt_check_default (ncx_withdefaults_t withdef, boolean realtest, val_value_t *node); /******************************************************************** * FUNCTION agt_check_save * * val_nodetest_fn_t callback * * Used by agt_ncx_cfg_save function to filter just what * is supposed to be saved in the config file * * INPUTS: * see ncx/val_util.h (val_nodetest_fn_t) * * RETURNS: * status *********************************************************************/ extern boolean agt_check_save (ncx_withdefaults_t withdef, boolean realtest, val_value_t *node); /******************************************************************** * FUNCTION agt_output_filter * * output the proper data for the get or get-config operation * generate the data that matched the subtree or XPath filter * * INPUTS: * see rpc/agt_rpc.h (agt_rpc_data_cb_t) * RETURNS: * status *********************************************************************/ extern status_t agt_output_filter (ses_cb_t *scb, rpc_msg_t *msg, int32 indent); /******************************************************************** * FUNCTION agt_output_schema * * generate the YANG file contents for the get-schema operation * * INPUTS: * see rpc/agt_rpc.h (agt_rpc_data_cb_t) * RETURNS: * status *********************************************************************/ extern status_t agt_output_schema (ses_cb_t *scb, rpc_msg_t *msg, int32 indent); /******************************************************************** * FUNCTION agt_output_empty * * output no data for the get or get-config operation * because the if-modified-since fileter did not pass * * INPUTS: * see rpc/agt_rpc.h (agt_rpc_data_cb_t) * RETURNS: * status *********************************************************************/ extern status_t agt_output_empty (ses_cb_t *scb, rpc_msg_t *msg, int32 indent); /******************************************************************** * FUNCTION agt_check_max_access * * Check if the max-access for a parameter is exceeded * * INPUTS: * newval == value node from PDU to check * cur_exists == TRUE if the corresponding node in the target exists * RETURNS: * status *********************************************************************/ extern status_t agt_check_max_access (val_value_t *newval, boolean cur_exists); /******************************************************************** * FUNCTION agt_check_editop * * Check if the edit operation is okay * * INPUTS: * pop == parent edit operation in affect * Starting at the data root, the parent edit-op * is derived from the default-operation parameter * cop == address of child operation (MAY BE ADJUSTED ON EXIT!!!) * This is the edit-op field in the new node corresponding * to the curnode position in the data model * newnode == pointer to new node in the edit-config PDU * curnode == pointer to the current node in the data model * being affected by this operation, if any. * == NULL if no current node exists * iqual == effective instance qualifier for this value * proto == protocol in use * * OUTPUTS: * *cop may be adjusted to simplify further processing, * based on the following reduction algorithm: * * create, replace, and delete operations are 'sticky'. * Once set, any nested edit-ops must be valid * within the context of the fixed operation (create or delete) * and the child operation gets changed to the sticky parent edit-op * * if the parent is create or delete, and the child * is merge or replace, then the child can be ignored * and will be changed to the parent, since the create * or delete must be carried through all the way to the leaves * * RETURNS: * status *********************************************************************/ extern status_t agt_check_editop (op_editop_t pop, op_editop_t *cop, val_value_t *newnode, val_value_t *curnode, ncx_iqual_t iqual, ncx_protocol_t proto); /******************************************************************** * FUNCTION agt_enable_feature * * Enable a YANG feature in the server * This will not be detected by any sessions in progress!!! * It will take affect the next time a message * is sent by the server * * INPUTS: * modname == module name containing the feature * featurename == feature name to enable * * RETURNS: * status *********************************************************************/ extern status_t agt_enable_feature (const xmlChar *modname, const xmlChar *featurename); /******************************************************************** * FUNCTION agt_disable_feature * * Disable a YANG feature in the server * This will not be detected by any sessions in progress!!! * It will take affect the next time a message * is sent by the server * * INPUTS: * modname == module name containing the feature * featurename == feature name to disable * * RETURNS: * status *********************************************************************/ extern status_t agt_disable_feature (const xmlChar *modname, const xmlChar *featurename); extern status_t agt_check_feature (const xmlChar *modname, const xmlChar *featurename, int* enabled/*output*/); /******************************************************************** * FUNCTION agt_make_leaf * * make a val_value_t struct for a specified leaf or leaf-list * INPUTS: * parentobj == parent object to find child leaf object * leafname == name of leaf to find (namespace hardwired) * leafstrval == string version of value to set for leaf * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct or NULL if some error *********************************************************************/ extern val_value_t * agt_make_leaf (obj_template_t *parentobj, const xmlChar *leafname, const xmlChar *leafstrval, status_t *res); /******************************************************************** * FUNCTION agt_make_uint_leaf * * make a val_value_t struct for a specified leaf or leaf-list * INPUTS: * parentobj == parent object to find child leaf object * leafname == name of leaf to find (namespace hardwired) * leafval == number value for leaf * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct or NULL if some error *********************************************************************/ extern val_value_t * agt_make_uint_leaf (obj_template_t *parentobj, const xmlChar *leafname, uint32 leafval, status_t *res); /******************************************************************** * FUNCTION agt_make_int_leaf * * make a val_value_t struct for a specified leaf or leaf-list * INPUTS: * parentobj == parent object to find child leaf object * leafname == name of leaf to find (namespace hardwired) * leafval == integer number value for leaf * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct or NULL if some error *********************************************************************/ extern val_value_t * agt_make_int_leaf (obj_template_t *parentobj, const xmlChar *leafname, int32 leafval, status_t *res); /******************************************************************** * FUNCTION agt_make_uint64_leaf * * make a val_value_t struct for a specified leaf or leaf-list * INPUTS: * parentobj == parent object to find child leaf object * leafname == name of leaf to find (namespace hardwired) * leafval == number value for leaf * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct or NULL if some error *********************************************************************/ extern val_value_t * agt_make_uint64_leaf (obj_template_t *parentobj, const xmlChar *leafname, uint64 leafval, status_t *res); /******************************************************************** * FUNCTION agt_make_int64_leaf * * make a val_value_t struct for a specified leaf or leaf-list * INPUTS: * parentobj == parent object to find child leaf object * leafname == name of leaf to find (namespace hardwired) * leafval == integer number value for leaf * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct or NULL if some error *********************************************************************/ extern val_value_t * agt_make_int64_leaf (obj_template_t *parentobj, const xmlChar *leafname, int64 leafval, status_t *res); /******************************************************************** * FUNCTION agt_make_idref_leaf * * make a val_value_t struct for a specified leaf or leaf-list * INPUTS: * parentobj == parent object to find child leaf object * leafname == name of leaf to find (namespace hardwired) * leafval == identityref value for leaf * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct or NULL if some error *********************************************************************/ extern val_value_t * agt_make_idref_leaf (obj_template_t *parentobj, const xmlChar *leafname, const val_idref_t *leafval, status_t *res); /******************************************************************** * FUNCTION agt_make_list * * make a val_value_t struct for a specified list * INPUTS: * parentobj == parent object to find child leaf object * listname == name of list object to find (namespace hardwired) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct for the list or NULL if some error *********************************************************************/ extern val_value_t * agt_make_list (obj_template_t *parentobj, const xmlChar *listname, status_t *res); /******************************************************************** * FUNCTION agt_make_object * * make a val_value_t struct for a specified node * INPUTS: * parentobj == parent object to find child leaf object * objname == name of the object to find (namespace hardwired) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct for the list or NULL if some error *********************************************************************/ extern val_value_t * agt_make_object (obj_template_t *parentobj, const xmlChar *objname, status_t *res); /******************************************************************** * FUNCTION agt_make_virtual_leaf * * make a val_value_t struct for a specified virtual * leaf or leaf-list * INPUTS: * parentobj == parent object to find child leaf object * leafname == name of leaf to find (namespace hardwired) * callbackfn == get callback function to install * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value struct or NULL if some error *********************************************************************/ extern val_value_t * agt_make_virtual_leaf (obj_template_t *parentobj, const xmlChar *leafname, getcb_fn_t callbackfn, status_t *res); /******************************************************************** * FUNCTION agt_add_top_virtual * * make a val_value_t struct for a specified virtual * top-level data node * INPUTS: * obj == object node of the virtual data node to create * callbackfn == get callback function to install * * RETURNS: * status *********************************************************************/ extern status_t agt_add_top_virtual (obj_template_t *obj, getcb_fn_t callbackfn); /******************************************************************** * FUNCTION agt_add_top_container * * make a val_value_t struct for a specified * top-level container data node. This is used * by SIL functions to create a top-level container * which may have virtual nodes within in it * * TBD: fine-control over SIL C code generation to * allow mix of container virtual callback plus * child node virtual node callbacks * INPUTS: * obj == object node of the container data node to create * val == address of return val pointer * * OUTPUTS: * *val == pointer to node created in the database * this is not live memory! It will be freed * by the database management code * * RETURNS: * status *********************************************************************/ extern status_t agt_add_top_container (obj_template_t *obj, val_value_t **val); /******************************************************************** * FUNCTION agt_add_container * * make a val_value_t struct for a specified * nested container data node. This is used * by SIL functions to create a nested container * which may have virtual nodes within it * * TBD: fine-control over SIL C code generation to * allow mix of container virtual callback plus * child node virtual node callbacks * INPUTS: * modname == module name defining objname * objname == name of object node to create * parentval == parent value node to add container to * val == address of return val pointer * * OUTPUTS: * *val == pointer to node created in the database * this is not live memory! It will be freed * by the database management code * * RETURNS: * status *********************************************************************/ extern status_t agt_add_container (const xmlChar *modname, const xmlChar *objname, val_value_t *parentval, val_value_t **val); /******************************************************************** * FUNCTION agt_init_cache * * init a cache pointer during the init2 callback * * INPUTS: * modname == name of module defining the top-level object * objname == name of the top-level database object * res == address of return status * * OUTPUTS: * *res is set to the return status * * RETURNS: * pointer to object value node from running config, * or NULL if error or not found *********************************************************************/ extern val_value_t * agt_init_cache (const xmlChar *modname, const xmlChar *objname, status_t *res); /******************************************************************** * FUNCTION agt_check_cache * * check if a cache pointer needs to be changed or NULLed out * INPUTS: * cacheptr == address of pointer to cache value node * newval == newval from the callback function * curval == curval from the callback function * editop == editop from the callback function * * OUTPUTS: * *cacheptr may be changed, depending on the operation * * RETURNS: * status *********************************************************************/ extern status_t agt_check_cache (val_value_t **cacheptr, val_value_t *newval, val_value_t *curval, op_editop_t editop); /******************************************************************** * FUNCTION agt_new_xpath_pcb * * Get a new XPath parser control block and * set up the server variable bindings * * INPUTS: * scb == session evaluating the XPath expression * expr == expression string to use (may be NULL) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced and initialied xpath_pcb_t structure * NULL if some error *********************************************************************/ extern xpath_pcb_t * agt_new_xpath_pcb (ses_cb_t *scb, const xmlChar *expr, status_t *res); /******************************************************************** * FUNCTION agt_get_startup_filespec * * Figure out where to store the startup file * * INPUTS: * res == address of return status * * OUTPUTS: * *res == return status * RETURNS: * malloced and filled in filespec string; must be freed by caller * NULL if malloc error *********************************************************************/ extern xmlChar * agt_get_startup_filespec (status_t *res); /******************************************************************** * FUNCTION agt_get_target_filespec * * Figure out where to store the URL target file * * INPUTS: * target_url == target url spec to use; this is * treated as a relative pathspec, and * the appropriate data directory is used * to create this file * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced and filled in filespec string; must be freed by caller * NULL if some error *********************************************************************/ extern xmlChar * agt_get_target_filespec (const xmlChar *target_url, status_t *res); /******************************************************************** * FUNCTION agt_set_mod_defaults * * Check for any top-level config leafs that have a default * value, and add them to the running configuration. * * INPUTS: * mod == module that was just added and should be used * to check for top-level database leafs with a default * * RETURNS: * status *********************************************************************/ extern status_t agt_set_mod_defaults (ncx_module_t *mod); /******************************************************************** * FUNCTION agt_set_with_defaults * * Check if the parameter is set * in the request message, and if so, is it * one of the server's supported values * * If not, then record an error * If so, then set the msg->mhdr.withdef enum * * INPUTS: * scb == session control block to use * msg == rpc message in progress * methnode == XML node for the method name * * OUTPUTS: * msg->mhdr.withdef set if NO_ERR * rpc-error recorded if any error detected * * RETURNS: * status *********************************************************************/ extern status_t agt_set_with_defaults (ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode); /******************************************************************** * FUNCTION agt_get_key_value * * Get the next expected key value in the ancestor chain * Used in Yuma SIL code to invoke User SIL callbacks with key values * * INPUTS: * startval == value node to start from * lastkey == address of last key leaf found in ancestor chain * caller maintains this state-var as the anscestor * keys are traversed * *lastkey == NULL to get the first key * * OUTPUTS: * *lastkey updated with the return value if key found * * RETURNS: * value node to use (do not free!!!) * NULL if some bad error like no next key found *********************************************************************/ extern val_value_t * agt_get_key_value (val_value_t *startval, val_value_t **lastkey); /******************************************************************** * FUNCTION agt_add_top_node_if_missing * * SIL Phase 2 init: Check the running config to see if * the specified node is there; if not create one * return the node either way * INPUTS: * mod == module containing object * objname == object name * added == address of return added flag * res == address of return status * * OUTPUTS: * *added == TRUE if new node was added * *res == return status, return NULL unless NO_ERR * * RETURNS: * if *res==NO_ERR, * pointer to new or existing node for modname:objname * this is added to running config and does not have to * be deleted. * !!!! No MRO nodes or default nodes have been added !!!! *********************************************************************/ extern val_value_t * agt_add_top_node_if_missing (ncx_module_t *mod, const xmlChar *objname, boolean *added, status_t *res); /******************************************************************** * FUNCTION agt_any_operations_set * * Check the new node and all descendants * for any operation attibutes prsent * * INPUTS: * val == value node to check * * RETURNS: * TRUE if any operation attributes found * FALSE if no operation attributes found */ extern boolean agt_any_operations_set (val_value_t *val); /******************************************************************** * FUNCTION agt_apply_this_node * * Check if the write operation applies to the current node * * INPUTS: * editop == edit operation value * newnode == pointer to new node (if any) * curnode == pointer to current value node (if any) * (just used to check if non-NULL or compare leaf) * * RETURNS: * TRUE if the current node needs the write operation applied * FALSE if this is a NO=OP node (either explicit or special merge) *********************************************************************/ extern boolean agt_apply_this_node (op_editop_t editop, const val_value_t *newnode, const val_value_t *curnode); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_util */ yuma123_2.14/netconf/src/agt/agt_commit_validate.h0000664000175000017500000000521114770023131022265 0ustar vladimirvladimir#ifndef _H_agt_commit_validate_cb #define _H_agt_commit_validate_cb /* FILE: agt_cb.h ********************************************************************* * P U R P O S E ********************************************************************* NETCONF Server Commit Validate callback handler This file contains functions to support registering, unregistering and execution of commit validate callbacks. */ #include "procdefs.h" #include "status.h" #include "procdefs.h" #include "agt.h" #include "ses.h" #include "ncx.h" #include "ncxconst.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * T Y P E D E F S *********************************************************************/ /** Typedef of the commit_validate callback */ typedef status_t (*agt_commit_validate_cb_t)(ses_cb_t *scb, xml_msg_hdr_t *msghdr, val_value_t *root); /******************************************************************** * F U N C T I O N S *********************************************************************/ /** * Initialise the callback commit module. */ extern void agt_commit_validate_init( void ); /** * Cleanup the callback commit module. */ extern void agt_commit_validate_cleanup( void ); /** * Register a commit validate callback. * This function registers a commit-validate callback that will be * called after all changes to the candidate database have been * validated. The function will be called before that final SIL commit * operation. If a commit validate operation is already registered for * the module it will be replaced. * * \param modname the name of the module registering the callback * \param cb the commit complete function. * \return the status of the operation. */ extern status_t agt_commit_validate_register( const xmlChar *modname, agt_commit_validate_cb_t cb ); /** * Unregister a commit validate callback. * This function unregisters a commit-validate callback. * * \param modname the name of the module unregistering the callback */ extern void agt_commit_validate_unregister( const xmlChar *modname ); /** * Validate a commit operation. * This function simply calls each registered commit validate * callback. If a commit validate operation fails the status of the * failing operation is returned immediately and no further commit * validate callbacks are made. * * \return the ERR_OK or the status of the first failing callback. */ extern status_t agt_commit_validate( ses_cb_t *scb, xml_msg_hdr_t *msghdr, val_value_t *root ); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif // _H_agt_commit_validate_cb yuma123_2.14/netconf/src/agt/agt_plock.c0000664000175000017500000005675014770023131020245 0ustar vladimirvladimir/* * * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * module ietf-netconf-partial-lock revision 2009-10-19 file: agt_plock.c: namespace urn:ietf:params:xml:ns:netconf:partial-lock:1.0 organization IETF Network Configuration (netconf) Working Group */ #include #include "procdefs.h" #include "agt.h" #include "agt_acm.h" #include "agt_cb.h" #include "agt_ncx.h" #include "agt_plock.h" #include "agt_rpc.h" #include "agt_timer.h" #include "agt_util.h" #include "dlq.h" #include "ncx.h" #include "ncx_num.h" #include "ncxmod.h" #include "ncxtypes.h" #include "plock.h" #include "plock_cb.h" #include "rpc.h" #include "ses.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xml_util.h" #include "xml_val.h" #include "xpath.h" #include "xpath1.h" /* module static variables */ static ncx_module_t *ietf_netconf_partial_lock_mod; static obj_template_t *partial_lock_obj; static obj_template_t *partial_unlock_obj; /* put your static variables here */ static boolean agt_plock_init_done = FALSE; /******************************************************************** * FUNCTION y_ietf_netconf_partial_lock_partial_lock_validate * * RPC validation phase * All YANG constraints have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_ietf_netconf_partial_lock_partial_lock_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { plock_cb_t *plcb; cfg_template_t *running; val_value_t *select_val, *testval; xpath_result_t *result; xpath_resnode_t *resnode; status_t res, retres; ses_id_t lockowner; res = NO_ERR; retres = NO_ERR; plcb = NULL; result = NULL; running = cfg_get_config_id(NCX_CFGID_RUNNING); /* make sure the running config state is READY */ res = cfg_ok_to_partial_lock(running); if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } /* the stack has already made sure at least 1 of these * is present because min-elements=1 */ select_val = val_find_child (msg->rpc_input, y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, y_ietf_netconf_partial_lock_N_select); if (select_val == NULL || select_val->res != NO_ERR) { /* should not happen */ return SET_ERROR(ERR_INTERNAL_VAL); } /* allocate a new lock cb * the plcb pointer will be NULL if the result is not NO_ERR */ res = NO_ERR; plcb = plock_cb_new(SES_MY_SID(scb), &res); if (res == ERR_NCX_RESOURCE_DENIED && !plcb && !cfg_is_partial_locked(running)) { /* no partial locks so it is safe to reset the lock index */ plock_cb_reset_id(); res = NO_ERR; plcb = plock_cb_new(SES_MY_SID(scb), &res); } if (res != NO_ERR || !plcb ) { if ( res==NO_ERR && !plcb ) { res = ERR_INTERNAL_MEM; } agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); if ( plcb ) { plock_cb_free(plcb); } return res; } /* get all the select parm instances and save them * in the partial lock cb */ while (select_val != NULL) { result = xpath1_eval_xmlexpr(scb->reader, select_val->xpathpcb, running->root, running->root, FALSE, /* logerrors */ TRUE, /* config-only */ &res); if (result == NULL || res != NO_ERR) { /* should not get invalid XPath expression * but maybe something was valid in the * object tree but not in the value tree */ agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, VAL_STRING(select_val), NCX_NT_VAL, select_val); retres = res; xpath_free_result(result); } else if (result->restype != XP_RT_NODESET) { res = ERR_NCX_XPATH_NOT_NODESET; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, VAL_STRING(select_val), NCX_NT_VAL, select_val); retres = res; xpath_free_result(result); } else { /* save these pointers; the result will be * pruned for redundant nodes after all the * select expressions have been processed; * transfer the memory straight across */ plock_add_select(plcb, select_val->xpathpcb, result); select_val->xpathpcb = NULL; result = NULL; } /* set up the next select value even if errors so far */ select_val = val_find_next_child (msg->rpc_input, y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, y_ietf_netconf_partial_lock_N_select, select_val); } /* check the result for non-empty only if all * the select stmts were valid */ if (retres == NO_ERR) { res = plock_make_final_result(plcb); if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, select_val); retres = res; } } /* make sure all the nodeptrs identify subtrees in the * running config that can really be locked right now * only if the final result is a non-empty nodeset */ if (retres == NO_ERR) { result = plock_get_final_result(plcb); for (resnode = xpath_get_first_resnode(result); resnode != NULL; resnode = xpath_get_next_resnode(resnode)) { testval = xpath_get_resnode_valptr(resnode); /* !!! Update 2011-09-27 * Just talked to the RFC author; Martin and I * agree RFC 5717 does not specify that write * access be required to create a partial lock * In fact -- a user may want to lock a node * to do a stable read **** deleted agt_acm check ***/ /* make sure there is a plock slot available * and no part of this subtree is already locked * do not check lock conflicts if this subtree * is unauthorized for writing */ lockowner = 0; res = val_ok_to_partial_lock(testval, SES_MY_SID(scb), &lockowner); if (res != NO_ERR) { if (res == ERR_NCX_LOCK_DENIED) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_UINT32_PTR, &lockowner, NCX_NT_VAL, testval); } else { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, testval); } retres = res; } } // for loop } // if retres == NO_ERR /* make sure there is no partial commit in progress */ if (agt_ncx_cc_active()) { res = ERR_NCX_IN_USE_COMMIT; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); retres = res; } /* save the plock CB only if there were no errors; * otherwise the invoke function will not get called */ if (retres == NO_ERR) { /* the invoke function must free this pointer */ msg->rpc_user1 = plcb; } else { plock_cb_free(plcb); } return retres; } /* y_ietf_netconf_partial_lock_partial_lock_validate */ /******************************************************************** * FUNCTION y_ietf_netconf_partial_lock_partial_lock_invoke * * RPC invocation phase * All constraints have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_ietf_netconf_partial_lock_partial_lock_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { plock_cb_t *plcb; val_value_t *testval, *newval; xpath_result_t *result; xpath_resnode_t *resnode, *clearnode; xmlChar *pathbuff; cfg_template_t *running; status_t res; ncx_num_t num; res = NO_ERR; plcb = (plock_cb_t *)msg->rpc_user1; result = plock_get_final_result(plcb); running = cfg_get_config_id(NCX_CFGID_RUNNING); /* try to lock all the target nodes */ for (resnode = xpath_get_first_resnode(result); resnode != NULL && res == NO_ERR; resnode = xpath_get_next_resnode(resnode)) { testval = xpath_get_resnode_valptr(resnode); res = val_set_partial_lock(testval, plcb); if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, testval); } } /* unlock any nodes already attempted if any fail */ if (res != NO_ERR) { for (clearnode = xpath_get_first_resnode(result); clearnode != NULL; clearnode = xpath_get_next_resnode(clearnode)) { testval = xpath_get_resnode_valptr(clearnode); val_clear_partial_lock(testval, plcb); if (clearnode == resnode) { return res; } } return res; } /* add this partial lock to the running config */ res = cfg_add_partial_lock(running, plcb); if (res != NO_ERR) { /* should not happen since config lock state could * not have changed since validate callback */ agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); for (clearnode = xpath_get_first_resnode(result); clearnode != NULL; clearnode = xpath_get_next_resnode(clearnode)) { testval = xpath_get_resnode_valptr(clearnode); val_clear_partial_lock(testval, plcb); } plock_cb_free(plcb); return res; } /* setup return data only if lock successful * cache the reply instead of stream the reply * in case there is any error; if so; the partial * lock will be backed out and the dataQ cleaned * for an error exit; add lock-id leaf first */ msg->rpc_data_type = RPC_DATA_YANG; ncx_init_num(&num); num.u = plock_get_id(plcb); newval = xml_val_new_number (y_ietf_netconf_partial_lock_N_lock_id, val_get_nsid(msg->rpc_input), &num, NCX_BT_UINT32); ncx_clean_num(NCX_BT_UINT32, &num); if (newval == NULL) { res = ERR_INTERNAL_MEM; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } else { dlq_enque(newval, &msg->rpc_dataQ); } /* add lock-node leaf-list instance for each resnode */ for (resnode = xpath_get_first_resnode(result); resnode != NULL && res == NO_ERR; resnode = xpath_get_next_resnode(resnode)) { /* Q&D method: generate the i-i string as a plain * string and add any needed prefixes to the global * prefix map for the reply message (in mhdr) */ pathbuff = NULL; testval = xpath_get_resnode_valptr(resnode); res = val_gen_instance_id(&msg->mhdr, testval, NCX_IFMT_XPATH1, &pathbuff); if (res == NO_ERR) { /* make leaf; pass off pathbuff malloced memory */ newval = xml_val_new_string (y_ietf_netconf_partial_lock_N_locked_node, val_get_nsid(msg->rpc_input), pathbuff); if (newval == NULL) { res = ERR_INTERNAL_MEM; m__free(pathbuff); pathbuff = NULL; } } if (res == NO_ERR) { dlq_enque(newval, &msg->rpc_dataQ); } else { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } } if (res != NO_ERR) { /* back out everything, except waste the lock ID */ for (clearnode = xpath_get_first_resnode(result); clearnode != NULL; clearnode = xpath_get_next_resnode(clearnode)) { testval = xpath_get_resnode_valptr(clearnode); val_clear_partial_lock(testval, plcb); } cfg_delete_partial_lock(running, plock_get_id(plcb)); /* clear any data already queued */ while (!dlq_empty(&msg->rpc_dataQ)) { testval = (val_value_t *) dlq_deque(&msg->rpc_dataQ); val_free_value(testval); } msg->rpc_data_type = RPC_DATA_NONE; } return res; } /* y_ietf_netconf_partial_lock_partial_lock_invoke */ /******************************************************************** * FUNCTION y_ietf_netconf_partial_lock_partial_unlock_validate * * RPC validation phase * All YANG constraints have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_ietf_netconf_partial_lock_partial_unlock_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { val_value_t *lock_id_val; cfg_template_t *running; plock_cb_t *plcb; uint32 lock_id; status_t res; res = NO_ERR; lock_id_val = val_find_child( msg->rpc_input, y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, y_ietf_netconf_partial_lock_N_lock_id); if (lock_id_val == NULL || lock_id_val->res != NO_ERR) { return ERR_NCX_INVALID_VALUE; } lock_id = VAL_UINT(lock_id_val); running = cfg_get_config_id(NCX_CFGID_RUNNING); plcb = cfg_find_partial_lock(running, lock_id); if (plcb == NULL || (plock_get_sid(plcb) != SES_MY_SID(scb))) { res = ERR_NCX_INVALID_VALUE; agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, lock_id_val); } else { msg->rpc_user1 = plcb; } return res; } /* y_ietf_netconf_partial_lock_partial_unlock_validate */ /******************************************************************** * FUNCTION y_ietf_netconf_partial_lock_partial_unlock_invoke * * RPC invocation phase * All constraints have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_ietf_netconf_partial_lock_partial_unlock_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { plock_cb_t *plcb; cfg_template_t *running; (void)scb; (void)methnode; plcb = (plock_cb_t *)msg->rpc_user1; running = cfg_get_config_id(NCX_CFGID_RUNNING); cfg_delete_partial_lock(running, plock_get_id(plcb)); return NO_ERR; } /* y_ietf_netconf_partial_lock_partial_unlock_invoke */ /******************************************************************** * FUNCTION y_ietf_netconf_partial_lock_init_static_vars * * initialize module static variables * ********************************************************************/ static void y_ietf_netconf_partial_lock_init_static_vars (void) { ietf_netconf_partial_lock_mod = NULL; partial_lock_obj = NULL; partial_unlock_obj = NULL; /* init your static variables here */ } /* y_ietf_netconf_partial_lock_init_static_vars */ /******************************************************************** * FUNCTION y_ietf_netconf_partial_lock_init * * initialize the ietf-netconf-partial-lock server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ status_t y_ietf_netconf_partial_lock_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; y_ietf_netconf_partial_lock_init_static_vars(); /* change if custom handling done */ if (xml_strcmp(modname, y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock)) { return ERR_NCX_UNKNOWN_MODULE; } if (revision && xml_strcmp (revision, y_ietf_netconf_partial_lock_R_ietf_netconf_partial_lock)) { return ERR_NCX_WRONG_VERSION; } agt_profile = agt_get_profile(); res = ncxmod_load_module( y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, y_ietf_netconf_partial_lock_R_ietf_netconf_partial_lock, &agt_profile->agt_savedevQ, &ietf_netconf_partial_lock_mod); if (res != NO_ERR) { return res; } agt_plock_init_done = TRUE; partial_lock_obj = ncx_find_object( ietf_netconf_partial_lock_mod, y_ietf_netconf_partial_lock_N_partial_lock); if (ietf_netconf_partial_lock_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } partial_unlock_obj = ncx_find_object( ietf_netconf_partial_lock_mod, y_ietf_netconf_partial_lock_N_partial_unlock); if (ietf_netconf_partial_lock_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } res = agt_rpc_register_method( y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, y_ietf_netconf_partial_lock_N_partial_lock, AGT_RPC_PH_VALIDATE, y_ietf_netconf_partial_lock_partial_lock_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, y_ietf_netconf_partial_lock_N_partial_lock, AGT_RPC_PH_INVOKE, y_ietf_netconf_partial_lock_partial_lock_invoke); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, y_ietf_netconf_partial_lock_N_partial_unlock, AGT_RPC_PH_VALIDATE, y_ietf_netconf_partial_lock_partial_unlock_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, y_ietf_netconf_partial_lock_N_partial_unlock, AGT_RPC_PH_INVOKE, y_ietf_netconf_partial_lock_partial_unlock_invoke); if (res != NO_ERR) { return res; } /* put your module initialization code here */ return res; } /* y_ietf_netconf_partial_lock_init */ /******************************************************************** * FUNCTION y_ietf_netconf_partial_lock_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ status_t y_ietf_netconf_partial_lock_init2 (void) { status_t res; res = NO_ERR; /* put your init2 code here */ return res; } /* y_ietf_netconf_partial_lock_init2 */ /******************************************************************** * FUNCTION y_ietf_netconf_partial_lock_cleanup * cleanup the server instrumentation library * ********************************************************************/ void y_ietf_netconf_partial_lock_cleanup (void) { if (agt_plock_init_done) { agt_rpc_unregister_method (y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, y_ietf_netconf_partial_lock_N_partial_lock); agt_rpc_unregister_method (y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, y_ietf_netconf_partial_lock_N_partial_unlock); } } /* y_ietf_netconf_partial_lock_cleanup */ /* END agt_plock.c */ yuma123_2.14/netconf/src/agt/agt_xpath.h0000664000175000017500000000712514770023131020256 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_xpath #define _H_agt_xpath /* FILE: agt_xpath.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Server XPath filter processing for select attribute in element in and operations ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 27-jan-09 abb Begun */ #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xml_msg #include "xml_msg.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_xpath_output_filter * * Evaluate the XPath filter against the specified * config root, and output the result of the * or operation to the specified session * * INPUTS: * scb == session control block * msg == rpc_msg_t in progress * cfg == config target to check against * getop == TRUE if this is a and not a * The target is expected to be the * config, and all state data will be available for the * filter output. * FALSE if this is a and only the * specified target in available for filter output * indent == start indent amount * * RETURNS: * status *********************************************************************/ extern status_t agt_xpath_output_filter (ses_cb_t *scb, rpc_msg_t *msg, const cfg_template_t *cfg, boolean getop, int32 indent); /******************************************************************** * FUNCTION agt_xpath_test_filter * * Evaluate the XPath filter against the specified * config root, and just test if there would be any * element generated at all * * Does not write any output based on the XPath evaluation * * INPUTS: * msghdr == message header in progress (for access control) * scb == session control block * selectval == filter value struct to use * val == top of value tree to compare the filter against * !!! not written -- not const in XPath in case * !!! a set-by-xpath function ever implemented * * RETURNS: * status *********************************************************************/ extern boolean agt_xpath_test_filter (xml_msg_hdr_t *msghdr, ses_cb_t *scb, const val_value_t *selectval, val_value_t *val); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_xpath */ yuma123_2.14/netconf/src/agt/agt_xpath.c0000664000175000017500000004300714770023131020250 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_tree.c NETCONF XPath Filtering Implementation ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 27jan09 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_acm.h" #include "agt_rpc.h" #include "agt_rpcerr.h" #include "agt_util.h" #include "agt_val.h" #include "agt_xpath.h" #include "cfg.h" #include "def_reg.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "ncxconst.h" #include "obj.h" #include "op.h" #include "rpc.h" #include "rpc_err.h" #include "ses.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xml_wr.h" #include "xpath.h" #include "xpath1.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION find_val_resnode * * Check if the specified resnode ptr is already in the Q * * INPUTS: * resultQ == Q of xpath_resnode_t structs to check * DOES NOT HAVE TO BE WITHIN A RESULT NODE Q * valptr == pointer value to find * * RETURNS: * found resnode or NULL if not found *********************************************************************/ static xpath_resnode_t * find_val_resnode (dlq_hdr_t *resultQ, val_value_t *valptr) { xpath_resnode_t *resnode; for (resnode = (xpath_resnode_t *)dlq_firstEntry(resultQ); resnode != NULL; resnode = (xpath_resnode_t *)dlq_nextEntry(resnode)) { if (resnode->node.valptr == valptr) { return resnode; } } return NULL; } /* find_val_resnode */ /******************************************************************** * FUNCTION output_resnode * * Out the result node in NETCONF format * Collapse all the subtrees together whenever possible * Add key leafs in lists if the filter left them out * * INPUTS: * scb == session control block * msg == rpc_msg_t in progress * pcb == XPath parser control block to use * resultQ == current Q of xpath_resnode_t to use for the * pool of potential nodes to gather into * the curval output * curval == current resnode value to output w/ path to root * ceilingval == current root of the tree * getop == TRUE for ; FALSE for * indent == start indent amount * *********************************************************************/ static void output_resnode (ses_cb_t *scb, rpc_msg_t *msg, xpath_pcb_t *pcb, dlq_hdr_t *resultQ, val_value_t *curval, val_value_t *ceilingval, boolean getop, int32 indent) { val_value_t *topval; val_index_t *valindex; xpath_resnode_t *testnode, *nextnode, dummynode; dlq_hdr_t dummyQ, descendantQ; int32 indentamount; boolean dowrite; dlq_createSQue(&dummyQ); dlq_createSQue(&descendantQ); indentamount = ses_indent_count(scb); /* find the top-level node for this result node */ topval = curval; while (topval->parent && topval->parent != ceilingval) { topval = topval->parent; } if (topval == curval) { /* no other result nodes are going to be * present which are descendants of this node * since it was explicitly set in the result * * special check to make sure not duplicating * a key leaf; cannot use the obj_is_key() * function because the object is a generic string */ if (!curval->index) { if (getop) { xml_wr_full_check_val(scb, &msg->mhdr, curval, indent, agt_check_default); } else { xml_wr_full_check_val(scb, &msg->mhdr, curval, indent, agt_check_config); } } return; } /* have the top-level node to output which * is different than the selected result node * make a dummy node to check all the * resnodes for this common ancestor */ xpath_init_resnode(&dummynode); dummynode.node.valptr = topval; dlq_enque(&dummynode, &dummyQ); /* gather all the remaining resnodes * that also have this value node as its top * level parent */ for (testnode = (xpath_resnode_t *)dlq_firstEntry(resultQ); testnode != NULL; testnode = nextnode) { nextnode = (xpath_resnode_t *)dlq_nextEntry(testnode); if (xpath1_check_node_exists(pcb, &dummyQ, testnode->node.valptr)) { dlq_remove(testnode); dlq_enque(testnode, &descendantQ); } } dlq_remove(&dummynode); xpath_clean_resnode(&dummynode); /* check if access control is allowing this user * to retrieve this value node */ dowrite = agt_acm_val_read_allowed(&msg->mhdr, scb->username, topval); /* have all the nodes required for this subtree * start with the top and keep going until * curval and all the dummyQ contents * have been output */ if (dowrite) { xml_wr_begin_elem_ex(scb, &msg->mhdr, ceilingval->nsid, topval->nsid, topval->name, &topval->metaQ, FALSE, indent, FALSE); } if (indent >= 0) { indent += indentamount; } /* check special case for lists; generate key leafs first */ if (val_has_index(topval)) { /* write all key leafs in order * remove any of them that happen to be * in the descendantQ */ for (valindex = val_get_first_index(topval); valindex != NULL; valindex = val_get_next_index(valindex)) { if (dowrite) { xml_wr_full_val(scb, &msg->mhdr, valindex->val, indent); } testnode = find_val_resnode(&descendantQ, valindex->val); if (testnode) { dlq_remove(testnode); xpath_free_resnode(testnode); } } } /* clear any nodes from the descendantQ which are child * nodes of 'topval' */ for (testnode = (xpath_resnode_t *)dlq_firstEntry(&descendantQ); testnode != NULL; testnode = nextnode) { nextnode = (xpath_resnode_t *)dlq_nextEntry(testnode); if (testnode->node.valptr->parent == topval) { /* this descendant is a sibling or higher-up * descendant of the topval, so output it now */ dlq_remove(testnode); if (getop) { if (dowrite) { xml_wr_full_val(scb, &msg->mhdr, testnode->node.valptr, indent); } } else { if (dowrite) { xml_wr_full_check_val(scb, &msg->mhdr, testnode->node.valptr, indent, agt_check_config); } } xpath_free_resnode(testnode); } } if (dowrite) { /* move the ceiling closer to the curval and try again * with the current value */ output_resnode(scb, msg, pcb, &descendantQ, curval, topval, getop, indent); } /* need to clear the descendant nodes before * generating the topval end tag */ testnode = (xpath_resnode_t *)dlq_deque(&descendantQ); while (testnode) { if (dowrite) { output_resnode(scb, msg, pcb, &descendantQ, testnode->node.valptr, topval, getop, indent); } xpath_free_resnode(testnode); testnode = (xpath_resnode_t *)dlq_deque(&descendantQ); } if (indent >= 0) { indent -= indentamount; } /* finish off the topval node */ if (dowrite) { xml_wr_end_elem(scb, &msg->mhdr, topval->nsid, topval->name, indent); } } /* output_resnode */ /******************************************************************** * FUNCTION output_result * * Out the result nodeset in NETCONF format * Collapse all the subtrees together whenever possible * Add key leafs in lists if the filter left them out * * INPUTS: * scb == session control block * msg == rpc_msg_t in progress * pcb == XPath parser control block to use * result == XPath result to use * getop == TRUE for ; FALSE for * indent == start indent amount * *********************************************************************/ static void output_result (ses_cb_t *scb, rpc_msg_t *msg, xpath_pcb_t *pcb, xpath_result_t *result, boolean getop, int32 indent) { val_value_t *curval; xpath_resnode_t *resnode; resnode = (xpath_resnode_t *)dlq_deque(&result->r.nodeQ); while (resnode) { curval = resnode->node.valptr; /* check corner case, output entire tree */ if (curval == pcb->val_docroot) { if (getop) { xml_wr_val(scb, &msg->mhdr, curval, indent); } else { xml_wr_check_val(scb, &msg->mhdr, curval, indent, agt_check_config); } xpath_free_resnode(resnode); return; } output_resnode(scb, msg, pcb, &result->r.nodeQ, curval, pcb->val_docroot, getop, indent); xpath_free_resnode(resnode); resnode = (xpath_resnode_t *)dlq_deque(&result->r.nodeQ); } } /* output_result */ /************ E X T E R N A L F U N C T I O N S **************/ /******************************************************************** * FUNCTION agt_xpath_output_filter * * Evaluate the XPath filter against the specified * config root, and output the result of the * or operation to the specified session * * INPUTS: * scb == session control block * msg == rpc_msg_t in progress * cfg == config target to check against * getop == TRUE if this is a and not a * The target is expected to be the * config, and all state data will be available for the * filter output. * FALSE if this is a and only the * specified target in available for filter output * indent == start indent amount * * RETURNS: * status *********************************************************************/ status_t agt_xpath_output_filter (ses_cb_t *scb, rpc_msg_t *msg, const cfg_template_t *cfg, boolean getop, int32 indent) { val_value_t *selectval; xpath_result_t *result; status_t res; #ifdef DEBUG if (!scb || !msg || !cfg || !msg->rpc_filter.op_filter) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* make sure the config has some data in it */ if (!cfg->root) { return NO_ERR; } res = NO_ERR; /* the msg filter should be non-NULL */ selectval = msg->rpc_filter.op_filter; result = xpath1_eval_xmlexpr(scb->reader, selectval->xpathpcb, cfg->root, cfg->root, FALSE, !getop, &res); if (result && (res == NO_ERR) && (result->restype == XP_RT_NODESET)) { /* prune result of redundant nodes */ xpath1_prune_nodeset(selectval->xpathpcb, result); /* output filter */ output_result(scb, msg, selectval->xpathpcb, result, getop, indent); } xpath_free_result(result); return res; } /* agt_xpath_output_filter */ /******************************************************************** * FUNCTION agt_xpath_test_filter * * Evaluate the XPath filter against the specified * config root, and just test if there would be any * element generated at all * * Does not write any output based on the XPath evaluation * * INPUTS: * msghdr == message header in progress (for access control) * scb == session control block * selectval == filter value struct to use * val == top of value tree to compare the filter against * !!! not written -- not const in XPath in case * !!! a set-by-xpath function ever implemented * * RETURNS: * status *********************************************************************/ boolean agt_xpath_test_filter (xml_msg_hdr_t *msghdr, ses_cb_t *scb, const val_value_t *selectval, val_value_t *val) { xpath_result_t *result; status_t res; boolean retval, getop, readallowed; #ifdef DEBUG if (!msghdr || !scb || !selectval || !val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif /* make sure the is allowed to be * viewed by the specified session */ readallowed = agt_acm_val_read_allowed(msghdr, scb->username, val); if (!readallowed) { return FALSE; } res = NO_ERR; getop = TRUE; /* do not skip config=fale nodes */ /* evaluate the XPath expression * any nodeset result that is non-empty * will pass the filter test */ result = xpath1_eval_xmlexpr(scb->reader, selectval->xpathpcb, val, val, FALSE, !getop, &res); if (result && (res == NO_ERR) && (result->restype == XP_RT_NODESET)) { retval = xpath_cvt_boolean(result); } else { retval = FALSE; } if (result) { xpath_free_result(result); } return retval; } /* agt_xpath_test_filter */ /* END file agt_xpath.c */ yuma123_2.14/netconf/src/agt/agt_state.h0000664000175000017500000001142214770023131020245 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_state #define _H_agt_state /* FILE: agt_state.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF State Monitoring Data Model Module support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24-feb-09 abb Begun. */ #ifndef _H_agt_not #include "agt_not.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define AGT_STATE_MODULE (const xmlChar *)"ietf-netconf-monitoring" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_state_init * * INIT 1: * Initialize the server state monitor module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_state_init (void); /******************************************************************** * FUNCTION agt_state_init2 * * INIT 2: * Initialize the monitoring data structures * This must be done after the config is loaded * * INPUTS: * none * RETURNS: * status *********************************************************************/ extern status_t agt_state_init2 (void); /******************************************************************** * FUNCTION agt_state_cleanup * * Cleanup the module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void agt_state_cleanup (void); /******************************************************************** * FUNCTION agt_state_add_session * * Add a session entry to the netconf-state DM * * INPUTS: * scb == session control block to use for the info * * RETURNS: * status *********************************************************************/ extern status_t agt_state_add_session (ses_cb_t *scb); /******************************************************************** * FUNCTION agt_state_remove_session * * Remove a session entry from the netconf-state DM * * INPUTS: * sid == session ID to find and delete * *********************************************************************/ extern void agt_state_remove_session (ses_id_t sid); /******************************************************************** * FUNCTION agt_state_add_module_schema * * Add a schema entry to the netconf-state DM * * INPUTS: * mod == module to add * * RETURNS: * status *********************************************************************/ extern status_t agt_state_add_module_schema (ncx_module_t *mod); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_state */ yuma123_2.14/netconf/src/agt/agt_not_queue_notification_cb.c0000664000175000017500000001201714770023131024337 0ustar vladimirvladimir/* FILE: agt_not_queue_notification_cb.c ********************************************************************* * P U R P O S E ********************************************************************* NETCONF Server agt_not_queue_notification callback handler This file contains functions to support registering, unregistering and execution of agt_not_queue_notification callbacks. */ /******************************************************************** * Y U M A H E A D E R S *********************************************************************/ #include "agt_not.h" #include "agt_not_queue_notification_cb.h" #include "dlq.h" #include "xml_util.h" /******************************************************************** * S T A N D A R D H E A D E R S *********************************************************************/ #include #include /******************************************************************** * T Y P E S *********************************************************************/ typedef struct agt_cb_queue_notification_set_t_ { dlq_hdr_t qhdr; xmlChar *modname; agt_not_queue_notification_cb_t callback; } agt_cb_queue_notification_set_t; /************** S T A T I C D A T A ****************************/ static bool initialised = false; static dlq_hdr_t callbackQ; /************** S T A T I C F U N C T I O N S ******************/ /********************************************************************/ static void free_callback_set( agt_cb_queue_notification_set_t* cbSet ) { if ( cbSet->modname ) { m__free( cbSet->modname ); } m__free( cbSet ); } /********************************************************************/ static agt_cb_queue_notification_set_t* new_callback_set( const xmlChar *modname ) { agt_cb_queue_notification_set_t* cbSet = m__getObj( agt_cb_queue_notification_set_t ); if ( !cbSet ) { return NULL; } memset( cbSet, 0, sizeof( agt_cb_queue_notification_set_t ) ); cbSet->modname = xml_strdup( modname ); if ( !cbSet->modname ) { m__free( cbSet ); return NULL; } return cbSet; } /********************************************************************/ static agt_cb_queue_notification_set_t* find_callback_set( const xmlChar *modname ) { agt_cb_queue_notification_set_t* cbSet; for ( cbSet = ( agt_cb_queue_notification_set_t* )dlq_firstEntry( &callbackQ ); cbSet != NULL; cbSet = ( agt_cb_queue_notification_set_t* )dlq_nextEntry( cbSet ) ) { if ( 0==xml_strcmp( modname, cbSet->modname ) ) { return cbSet; } } return NULL; } /************** E X T E R N A L F U N C T I O N S **************/ /********************************************************************/ void agt_not_queue_notification_cb_init( void ) { if ( !initialised ) { dlq_createSQue( &callbackQ ); initialised = true; } } /* agt_not_queue_notification_callbacks_init */ /********************************************************************/ void agt_not_queue_notification_cb_cleanup( void ) { if ( initialised ) { agt_cb_queue_notification_set_t* cbSet; while ( !dlq_empty( &callbackQ ) ) { cbSet = ( agt_cb_queue_notification_set_t* )dlq_deque( &callbackQ ); free_callback_set( cbSet ); } initialised = false; } } /* agt_not_queue_notification_callbacks_cleanup */ /********************************************************************/ status_t agt_not_queue_notification_cb_register( const xmlChar *modname, agt_not_queue_notification_cb_t cb ) { assert( modname ); agt_cb_queue_notification_set_t* cbSet = find_callback_set( modname ); if ( !cbSet ) { cbSet = new_callback_set( modname ); if ( !cbSet ) { return ERR_INTERNAL_MEM; } dlq_enque( cbSet, &callbackQ ); } cbSet->callback = cb; return NO_ERR; } /********************************************************************/ void agt_not_queue_notification_cb_unregister( const xmlChar *modname ) { assert( modname ); agt_cb_queue_notification_set_t* cbSet = find_callback_set( modname ); if ( cbSet ) { dlq_remove( cbSet ); free_callback_set( cbSet ); } } /********************************************************************/ status_t agt_not_queue_notification_cb( agt_not_msg_t *notif ) { agt_cb_queue_notification_set_t* cbSet; for ( cbSet = ( agt_cb_queue_notification_set_t* )dlq_firstEntry( &callbackQ ); cbSet != NULL; cbSet = ( agt_cb_queue_notification_set_t* )dlq_nextEntry( cbSet ) ) { if ( cbSet->callback ) { status_t res = cbSet->callback(notif); if ( NO_ERR != res ) { return res; } } } return NO_ERR; } /* END file agt_not_queue_notification_cb.c */ yuma123_2.14/netconf/src/agt/agt.c0000664000175000017500000013753114770023131017052 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 04nov05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #ifndef STATIC_SERVER #include #endif #include "procdefs.h" #include "agt.h" #include "agt_acm.h" #include "agt_cap.h" #include "agt_cb.h" #include "agt_cfg.h" #include "agt_commit_complete.h" #include "agt_commit_validate.h" #include "agt_cli.h" #include "agt_connect.h" #include "agt_hello.h" #include "agt_ncx.h" #include "agt_nmda.h" #include "agt_not.h" #include "agt_not_queue_notification_cb.h" #include "agt_plock.h" #include "agt_proc.h" #include "agt_rpc.h" #include "agt_ses.h" #include "agt_signal.h" #include "agt_state.h" #include "agt_sys.h" #include "agt_val.h" #include "agt_time_filter.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_yang_library.h" #include "log.h" #include "ncx.h" #include "ncx_str.h" #include "ncxconst.h" #include "ncxmod.h" #include "status.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define TX_ID_MODULE (const xmlChar *)"yuma123-system" #define TX_ID_OBJECT (const xmlChar *)"transaction-id" /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_init_done = FALSE; static agt_profile_t agt_profile; static boolean agt_shutdown; static boolean agt_shutdown_started; static ncx_shutdowntyp_t agt_shutmode; static dlq_hdr_t agt_dynlibQ; /******************************************************************** * FUNCTION init_server_profile * * Hardwire the initial server profile variables * * OUTPUTS: * *agt_profile is filled in with params of defaults * *********************************************************************/ static void init_server_profile (void) { memset(&agt_profile, 0x0, sizeof(agt_profile_t)); /* init server state variables stored here */ dlq_createSQue(&agt_profile.agt_savedevQ); dlq_createSQue(&agt_profile.agt_commit_testQ); agt_profile.agt_config_state = AGT_CFG_STATE_INIT; /* Set the default values for the user parameters * these may be overridden from the command line; */ agt_profile.agt_targ = NCX_AGT_TARG_CANDIDATE; agt_profile.agt_start = NCX_AGT_START_MIRROR; agt_profile.agt_loglevel = log_get_debug_level(); agt_profile.agt_log_acm_reads = FALSE; agt_profile.agt_log_acm_writes = TRUE; /* T: op on candidate will call all SILs * F: only SILs for nodes changed in the candidate will * be called; only affects command */ agt_profile.agt_validate_all = TRUE; agt_profile.agt_has_startup = FALSE; agt_profile.agt_usestartup = TRUE; agt_profile.agt_factorystartup = FALSE; agt_profile.agt_startup_error = TRUE; agt_profile.agt_running_error = TRUE; agt_profile.agt_logappend = FALSE; agt_profile.agt_xmlorder = FALSE; agt_profile.agt_deleteall_ok = FALSE; agt_profile.agt_stream_output = TRUE; /* this flag is no longer supported -- it must be * set to false for XPath to work correctly */ agt_profile.agt_delete_empty_npcontainers = FALSE; /* this flag adds the leaf to notifications * It was default true for yuma through 2.2-3 release * Starting 2.2-4 it is removed by default since the XSD * in RFC 5277 does not allow extensions */ agt_profile.agt_notif_sequence_id = FALSE; agt_profile.agt_accesscontrol = NULL; agt_profile.agt_conffile = NULL; agt_profile.agt_logfile = NULL; agt_profile.agt_startup = NULL; agt_profile.agt_defaultStyle = NCX_EL_EXPLICIT; agt_profile.agt_superuser = NULL; agt_profile.agt_eventlog_size = 1000; agt_profile.agt_maxburst = 10; agt_profile.agt_hello_timeout = 300; agt_profile.agt_idle_timeout = 3600; agt_profile.agt_linesize = 72; agt_profile.agt_indent = 1; agt_profile.agt_usevalidate = TRUE; agt_profile.agt_useurl = TRUE; agt_profile.agt_defaultStyleEnum = NCX_WITHDEF_EXPLICIT; agt_profile.agt_accesscontrol_enum = AGT_ACMOD_ENFORCING; agt_profile.agt_system_sorted = AGT_DEF_SYSTEM_SORTED; agt_profile.agt_max_sessions = 1024; } /* init_server_profile */ /******************************************************************** * FUNCTION clean_server_profile * * Clean the server profile variables * * OUTPUTS: * *agt_profile is filled in with params of defaults * *********************************************************************/ static void clean_server_profile (void) { ncx_clean_save_deviationsQ(&agt_profile.agt_savedevQ); while (!dlq_empty(&agt_profile.agt_commit_testQ)) { agt_cfg_commit_test_t *commit_test = (agt_cfg_commit_test_t *) dlq_deque(&agt_profile.agt_commit_testQ); agt_cfg_free_commit_test(commit_test); } if (agt_profile.agt_startup_txid_file) { m__free(agt_profile.agt_startup_txid_file); agt_profile.agt_startup_txid_file = NULL; } } /* clean_server_profile */ /******************************************************************** * FUNCTION load_running_config * * Load the NV startup config into the running config * * INPUTS: * startup == startup filespec provided by the user * == NULL if not set by user * (use default name and specified search path instead) * loaded == address of return config loaded flag * * OUTPUTS: * *loaded == TRUE if some config file was loaded * * The config is loaded from NV-storage, * if the NV-storage config can be found an read * RETURNS: * status *********************************************************************/ static status_t load_running_config (const xmlChar *startup, boolean *loaded) { cfg_template_t *cfg; xmlChar *fname; agt_profile_t *profile; status_t res; res = NO_ERR; *loaded = FALSE; profile = agt_get_profile(); cfg = cfg_get_config(NCX_CFG_RUNNING); if (!cfg) { log_error("\nagt: No running config found!!"); return SET_ERROR(ERR_INTERNAL_VAL); } /* use the user-set startup or default filename */ if (startup) { /* relative filespec, use search path */ fname = ncxmod_find_data_file(startup, FALSE, &res); } else { /* search for the default startup-cfg.xml filename */ fname = ncxmod_find_data_file(NCX_DEF_STARTUP_FILE, FALSE, &res); } /* check if error finding the filespec */ if (!fname) { if (startup) { if (res == NO_ERR) { res = ERR_NCX_MISSING_FILE; } log_error("\nError: Startup config file (%s) not found (%s).", startup, get_error_string(res)); return res; } else { log_info("\nDefault startup config file (%s) not found." "\n Booting with default running configuration!\n", NCX_DEF_STARTUP_FILE); return NO_ERR; } } else if (LOGDEBUG2) { log_debug2("\nFound startup config: '%s'", fname); } /* try to load the config file that was found or given */ res = agt_ncx_cfg_load(cfg, CFG_LOC_FILE, fname); if (res == ERR_XML_READER_START_FAILED) { log_error("\nagt: Error: Could not open startup config file" "\n (%s)\n", fname); } else if (res != NO_ERR) { /* if an error is returned then it was a hard error * since the startup_error and running_error profile * variables have already been accounted for. * An error in the setup or in the AGT_RPC_PH_INVOKE phase * of the operation occurred */ log_error("\nError: load startup config failed (%s)", get_error_string(res)); if (!dlq_empty(&cfg->load_errQ)) { *loaded = TRUE; } } else { /* assume OK or startup and running continue; if 1 or both is not * set then the server will exit anyway and the config state * will not matter */ profile->agt_config_state = AGT_CFG_STATE_OK; *loaded = TRUE; boolean errdone = FALSE; boolean errcontinue = FALSE; if (profile->agt_load_validate_errors) { if (profile->agt_startup_error) { /* quit if any startup validation errors */ log_error("\nError: validation errors occurred loading the " " database\n from NV-storage" " (%s)\n", fname); errdone = TRUE; } else { /* continue if any startup errors */ log_warn("\nWarning: validation errors occurred loading " "the database\n from NV-storage" " (%s)\n", fname); errcontinue = TRUE; } } if (!errdone && profile->agt_load_rootcheck_errors) { if (profile->agt_startup_error) { /* quit if any startup root-check validation errors */ log_error("\nError: root-check validation errors " "occurred loading the database\n" " from NV-storage (%s)\n", fname); errdone = TRUE; } else { /* continue if any root-check validation errors */ log_warn("\nWarning: root-check validation errors " "occurred loading the database\n" " from NV-storage (%s)\n", fname); errcontinue = TRUE; } } if (!errdone && profile->agt_load_top_rootcheck_errors) { if (profile->agt_running_error) { /* quit if any top-level root-check validation errors */ log_error("\nError: top-node root-check validation errors " "occurred loading the database\n" " from NV-storage (%s)\n", fname); errdone = TRUE; } else { /* continue if any startup errors */ log_warn("\nWarning: top-node root-check validation errors " "occurred loading the database\n" " from NV-storage (%s)\n", fname); profile->agt_config_state = AGT_CFG_STATE_BAD; errcontinue = TRUE; } } if (!errdone && profile->agt_load_apply_errors) { /* quit if any apply-to-running SIL errors */ errdone = TRUE; log_error("\nError: fatal errors " "occurred loading the database " "from NV-storage\n (%s)\n", fname); } if (errdone) { res = ERR_NCX_OPERATION_FAILED; } else if (errcontinue) { val_purge_errors_from_root(cfg->root); log_info("\nagt: Startup config loaded after pruning error nodes\n" "Source: %s\n", fname); } else { log_info("\nagt: Startup config loaded OK\n Source: %s\n", fname); } } if (LOGDEBUG) { log_debug("\nContents of %s configuration:", cfg->name); val_dump_value(cfg->root, 0); log_debug("\n"); } if (fname) { m__free(fname); } return res; } /* load_running_config */ /******************************************************************** * FUNCTION new_dynlib_cb * * Malloc and init a new dynamic library control block * * RETURNS: * malloced struct or NULL if malloc error *********************************************************************/ static agt_dynlib_cb_t * new_dynlib_cb (void) { agt_dynlib_cb_t *dynlib; dynlib = m__getObj(agt_dynlib_cb_t); if (dynlib == NULL) { return NULL; } memset(dynlib, 0x0, sizeof(agt_dynlib_cb_t)); return dynlib; } /* new_dynlib_cb */ /******************************************************************** * FUNCTION free_dynlib_cb * * Clean and free a new dynamic library control block * * INPUTS: * dynlib == dynamic library control block to free *********************************************************************/ static void free_dynlib_cb (agt_dynlib_cb_t *dynlib) { if (dynlib->modname) { m__free(dynlib->modname); } if (dynlib->revision) { m__free(dynlib->revision); } m__free(dynlib); } /* free_dynlib_cb */ /******************************************************************** * FUNCTION set_initial_transaction_id * * Set the last_transaction_id for the running config * Will check for sys:transaction-id leaf in config->root * * RETURNS: * status *********************************************************************/ static status_t set_initial_transaction_id (void) { cfg_template_t *cfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (cfg == NULL) { return ERR_NCX_CFG_NOT_FOUND; } if (cfg->root == NULL) { return ERR_NCX_EMPTY_VAL; } agt_profile_t *profile = agt_get_profile(); status_t res = NO_ERR; boolean foundfile = FALSE; /* figure out which transaction ID file to use */ if (profile->agt_startup_txid_file == NULL) { /* search for the default startup-cfg.xml filename */ xmlChar *fname = ncxmod_find_data_file(NCX_DEF_STARTUP_TXID_FILE, FALSE, &res); if (fname == NULL || res != NO_ERR || *fname == 0) { /* need to set the default startup transaction ID file name */ log_debug("\nSetting initial transaction ID file to default"); if (fname) { m__free(fname); } res = NO_ERR; fname = agt_get_startup_filespec(&res); if (fname == NULL || res != NO_ERR) { if (res == NO_ERR) { res = ERR_NCX_OPERATION_FAILED; } log_error("\nFailed to set initial transaction ID file (%s)", get_error_string(res)); if (fname) { m__free(fname); } return res; } /* get dir part and append the TXID file name to it */ uint32 fnamelen = xml_strlen(fname); xmlChar *str = &fname[fnamelen - 1]; while (str >= fname && *str && *str != NCX_PATHSEP_CH) { str--; } if (*str != NCX_PATHSEP_CH) { log_error("\nFailed to set initial transaction ID file"); m__free(fname); return ERR_NCX_INVALID_VALUE; } /* copy base filespec + 1 for the path-sep-ch */ uint32 baselen = (uint32)(str - fname) + 1; uint32 newlen = baselen + xml_strlen(NCX_DEF_STARTUP_TXID_FILE); xmlChar *newstr = m__getMem(newlen + 1); if (newstr == NULL) { m__free(fname); return ERR_INTERNAL_MEM; } str = newstr; str += xml_strncpy(str, fname, baselen); xml_strcpy(str, NCX_DEF_STARTUP_TXID_FILE); m__free(fname); profile->agt_startup_txid_file = newstr; // pass off memory here } else { foundfile = TRUE; profile->agt_startup_txid_file = fname; // pass off memory here } } /* initialize the starting transaction ID in the config module */ res = agt_cfg_init_transactions(profile->agt_startup_txid_file, foundfile); if (res != NO_ERR) { log_error("\nError: cfg-init transaction ID failed (%s)", get_error_string(res)); } return res; } /* set_initial_transaction_id */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION agt_init1 * * Initialize the Server Library: stage 1: CLI and profile * * TBD -- put platform-specific server init here * * INPUTS: * argc == command line argument count * argv == array of command line strings * showver == address of version return quick-exit status * showhelp == address of help return quick-exit status * * OUTPUTS: * *showver == TRUE if user requsted version quick-exit mode * *showhelp == TRUE if user requsted help quick-exit mode * * RETURNS: * status of the initialization procedure *********************************************************************/ status_t agt_init1 (int argc, char *argv[], boolean *showver, help_mode_t *showhelpmode) { status_t res; if (agt_init_done) { return NO_ERR; } log_debug3("\nServer Init Starting..."); res = NO_ERR; /* initialize shutdown variables */ agt_shutdown = FALSE; agt_shutdown_started = FALSE; agt_shutmode = NCX_SHUT_NONE; dlq_createSQue(&agt_dynlibQ); *showver = FALSE; *showhelpmode = HELP_MODE_NONE; /* allow cleanup to run even if this fn does not complete */ agt_init_done = TRUE; init_server_profile(); /* setup $HOME/.yuma directory */ res = ncxmod_setup_yumadir(); if (res != NO_ERR) { return res; } /* get the command line params and also any config file params */ res = agt_cli_process_input(argc, argv, &agt_profile, showver, showhelpmode); if (res != NO_ERR) { return res; } /* else the agt_profile is filled in */ /* check if quick exit mode */ if (*showver || (*showhelpmode != HELP_MODE_NONE)) { return NO_ERR; } /* loglevel and log file already set */ return res; } /* agt_init1 */ /******************************************************************** * FUNCTION agt_init2 * * Initialize the Server Library * The agt_profile is set and the object database is * ready to have YANG modules loaded * * RPC and data node callbacks should be installed * after the module is loaded, and before the running config * is loaded. * * RETURNS: * status *********************************************************************/ status_t agt_init2 (void) { val_value_t *clivalset; ncx_module_t *retmod; val_value_t *val; agt_dynlib_cb_t *dynlib; xmlChar *savestr, *revision, savechar; cfg_template_t *cfg; status_t res; uint32 modlen; boolean startup_loaded; ncx_save_deviations_t *savedev; log_debug3("\nServer Init-2 Starting..."); startup_loaded = FALSE; /* init user callback support */ agt_cb_init(); agt_commit_complete_init(); agt_commit_validate_init(); agt_not_queue_notification_cb_init(); /* initial signal handler first to allow clean exit */ agt_signal_init(); /* initialize the server timer service */ agt_timer_init(); /* initialize the RPC server callback structures */ res = agt_rpc_init(); if (res != NO_ERR) { return res; } /* initialize the NCX connect handler */ res = agt_connect_init(); if (res != NO_ERR) { return res; } /* initialize the NCX hello handler */ res = agt_hello_init(); if (res != NO_ERR) { return res; } /* setup an empty config * The config state is still CFG_ST_INIT * so no user access can occur yet (except OP_LOAD by root) */ res = cfg_init_static_db(NCX_CFGID_RUNNING); if (res != NO_ERR) { return res; } /* set the 'ordered-by system' sorted/not-sorted flag */ ncx_set_system_sorted(agt_profile.agt_system_sorted); /* set the 'top-level mandatory objects allowed' flag */ ncx_set_top_mandatory_allowed(!agt_profile.agt_running_error); /*** All Server profile parameters should be set by now ***/ /* must set the server capabilities after the profile is set */ res = agt_cap_set_caps(agt_profile.agt_targ, agt_profile.agt_start, agt_profile.agt_defaultStyle); if (res != NO_ERR) { return res; } /* setup the candidate config if it is used */ if (agt_profile.agt_targ==NCX_AGT_TARG_CANDIDATE) { res = cfg_init_static_db(NCX_CFGID_CANDIDATE); if (res != NO_ERR) { return res; } } /* setup the startup config if it is used */ if (agt_profile.agt_start==NCX_AGT_START_DISTINCT) { res = cfg_init_static_db(NCX_CFGID_STARTUP); if (res != NO_ERR) { return res; } } /* initialize the server access control model */ res = agt_acm_init(); if (res != NO_ERR) { return res; } /* initialize the session handler data structures */ agt_ses_init(); /* load the yang library module */ res = agt_yang_library_init(); if (res != NO_ERR) { return res; } /* load the nmda module */ res = agt_nmda_init(); if (res != NO_ERR) { return res; } /* load the system module */ res = agt_sys_init(); if (res != NO_ERR) { return res; } /* load the NETCONF state monitoring data model module */ res = agt_state_init(); if (res != NO_ERR) { return res; } /* load the NETCONF Notifications data model module */ res = agt_not_init(); if (res != NO_ERR) { return res; } /* load the NETCONF /proc monitoring data model module */ res = agt_proc_init(); if (res != NO_ERR) { return res; } /* load the partial lock module */ res = y_ietf_netconf_partial_lock_init (y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, NULL); if (res != NO_ERR) { return res; } /* initialize the NCX server core callback functions. * the schema (yuma-netconf.yang) for these callbacks was * already loaded in the common ncx_init * */ res = agt_ncx_init(); if (res != NO_ERR) { return res; } /* load the yuma-time-filter module */ res = y_yuma_time_filter_init (y_yuma_time_filter_M_yuma_time_filter, NULL); if (res != NO_ERR) { return res; } /* check the module parameter set from CLI or conf file * for any modules to pre-load */ res = NO_ERR; clivalset = agt_cli_get_valset(); if (clivalset) { if (LOGDEBUG) { log_debug("\n\nnetconfd final CLI + .conf parameters:\n"); val_dump_value_max(clivalset, 0, NCX_DEF_INDENT, DUMP_VAL_LOG, NCX_DISPLAY_MODE_PLAIN, FALSE, /* withmeta */ TRUE /* config only */); log_debug("\n"); } /* first check if there are any deviations to load */ val = val_find_child(clivalset, NCXMOD_NETCONFD, NCX_EL_DEVIATION); while (val) { res = ncxmod_load_deviation(VAL_STR(val), &agt_profile.agt_savedevQ); if (res != NO_ERR) { return res; } else { val = val_find_next_child(clivalset, NCXMOD_NETCONFD, NCX_EL_DEVIATION, val); } } val = val_find_child(clivalset, NCXMOD_NETCONFD, NCX_EL_MODULE); /* attempt all dynamically loaded modules */ while (val && res == NO_ERR) { /* see if the revision is present in the * module parameter or not */ modlen = 0; revision = NULL; savestr = NULL; savechar = '\0'; if (yang_split_filename(VAL_STR(val), &modlen)) { savestr = &(VAL_STR(val)[modlen]); savechar = *savestr; *savestr = '\0'; revision = savestr + 1; } #ifdef STATIC_SERVER /* load just the module * SIL initialization is assumed to be * handled elsewhere */ res = ncxmod_load_module(VAL_STR(val), revision, &agt_profile.agt_savedevQ, &retmod); } #else /* load the SIL and it will load its own module */ res = agt_load_sil_code(VAL_STR(val), revision, FALSE); if (res == ERR_NCX_SKIPPED) { log_warn("\nWarning: SIL code for module '%s' not found", VAL_STR(val)); res = ncxmod_load_module(VAL_STR(val), revision, &agt_profile.agt_savedevQ, &retmod); } #endif if (savestr != NULL) { *savestr = savechar; } if (res == NO_ERR) { val = val_find_next_child(clivalset, NCXMOD_NETCONFD, NCX_EL_MODULE, val); } } } /*** ALL INITIAL YANG MODULES SHOULD BE LOADED AT THIS POINT ***/ if (res != NO_ERR) { log_error("\nError: one or more modules could not be loaded"); return ERR_NCX_OPERATION_FAILED; } /* Finish filling in the deviation modules' import structures. * Loading deviation modules skips over this step, but since all * modules should be loaded by this point, iterate through the * imports and search for each missing module. */ for (savedev = (ncx_save_deviations_t *)dlq_firstEntry(&agt_profile.agt_savedevQ); savedev && res == NO_ERR; savedev = (ncx_save_deviations_t *)dlq_nextEntry(savedev)) { res = ncxmod_process_deviation_imports(savedev); } if (res != NO_ERR) { log_error("\nError: one or more modules imported by %s could not be found.", savedev->devmodule); return ERR_NCX_OPERATION_FAILED; } /* After the deviations have been resolved, any nodes to be deleted * will have been marked as such. ncxmod_apply_deviations() will * cause those nodes to actually be deleted. This happens in a * separate loop because, in the case that the target node is the * result of a yang augment statement, the module in which the target * obj_template_t will be found may (generally will) be different * than the module in which the deviation was resolved. */ for (retmod = ncx_get_first_module(); retmod; retmod = ncx_get_next_module(retmod)) { res = ncxmod_resolve_deviations(retmod, &agt_profile.agt_savedevQ); if (res != NO_ERR) return ERR_NCX_OPERATION_FAILED; } for (retmod = ncx_get_first_module(); retmod; retmod = ncx_get_next_module(retmod)) { res = ncxmod_apply_deviations(retmod); if (res != NO_ERR) return ERR_NCX_OPERATION_FAILED; } if (ncx_any_mod_errors()) { log_error("\nError: one or more pre-loaded YANG modules" " has fatal errors\n"); return ERR_NCX_OPERATION_FAILED; } /* set the initial module capabilities in the server message */ res = agt_cap_set_modules(&agt_profile); if (res != NO_ERR) { return res; } /* prune all the obsolete objects */ ncx_delete_all_obsolete_objects(); /* safe to create the commit-task object list now */ res = agt_val_init_commit_tests(); if (res != NO_ERR) { return res; } /* get or initialize the startup and running transaction-id */ res = set_initial_transaction_id(); if (res != NO_ERR) { return res; } /************* L O A D R U N N I N G C O N F I G ***************/ /* load the NV startup config into the running config if it exists */ if (agt_profile.agt_factorystartup) { /* force the startup to be the factory startup */ log_info("\nagt: Startup configuration skipped due " "to factory-startup CLI option\n"); } else if (agt_profile.agt_usestartup) { if (LOGDEBUG2) { log_debug2("\nAttempting to load running config from startup"); } res = load_running_config(agt_profile.agt_startup, &startup_loaded); if (res != NO_ERR) { return res; } } else { log_info("\nagt: Startup configuration skipped due " "to no-startup CLI option\n"); } /** P H A S E 2 I N I T **** ** add system-config and non-config data ** check existing startup config and add factory default ** nodes as needed ** Note: if startup_loaded == TRUE then all the config nodes ** that may get added during init2 will not get checked ** by agt_val_root_check(). It is assumed that SIL modules ** will not add invalid data nodes; Any such nodes will only ** be detected if a client causes a root check (commit ** or edit running config */ /* load the nacm access control DM module */ res = agt_acm_init2(); if (res != NO_ERR) { return res; } /* load any yang library module non-config data */ res = agt_yang_library_init2(); if (res != NO_ERR) { return res; } /* load any nmda module non-config data */ res = agt_nmda_init2(); if (res != NO_ERR) { return res; } /* load any system module non-config data */ res = agt_sys_init2(); if (res != NO_ERR) { return res; } /* load any server state monitoring data */ res = agt_state_init2(); if (res != NO_ERR) { return res; } /* load any partial lock runtime data */ res = y_ietf_netconf_partial_lock_init2(); if (res != NO_ERR) { return res; } /* load the notifications callback functions and data */ res = agt_not_init2(); if (res != NO_ERR) { return res; } /* load the /proc monitoring callback functions and data */ res = agt_proc_init2(); if (res != NO_ERR) { return res; } /* TBD: load the time filter callbacks * this currently does not do anything */ res = y_yuma_time_filter_init2(); if (res != NO_ERR) { return res; } #ifdef STATIC_SERVER /* init phase 2 for static modules should be * initialized here */ #else for (dynlib = (agt_dynlib_cb_t *)dlq_firstEntry(&agt_dynlibQ); dynlib != NULL; dynlib = (agt_dynlib_cb_t *)dlq_nextEntry(dynlib)) { if (dynlib->init_status == NO_ERR && !dynlib->init2_done && !dynlib->cleanup_done) { if (LOGDEBUG2) { log_debug2("\nCalling SIL Init2 fn for module '%s'", dynlib->modname); } res = (*dynlib->init2fn)(); dynlib->init2_status = res; dynlib->init2_done = TRUE; if (res != NO_ERR) { log_error("\nError: SIL init2 function " "failed for module '%s' (%s)", dynlib->modname, get_error_string(res)); return res; /* TBD: should cleanup be called and somehow * remove the module that was loaded? * Currently abort entire server startup */ } } } #endif /* add any top-level defaults to the running config * that were not covered by SIL callbacks */ for (retmod = ncx_get_first_module(); retmod != NULL; retmod = ncx_get_next_module(retmod)) { res = agt_set_mod_defaults(retmod); if (res != NO_ERR) { log_error("\nError: Set mod defaults for module '%s' failed (%s)", retmod->name, get_error_string(res)); return res; } } /* dump the running config at boot-time */ if (LOGDEBUG3) { if (startup_loaded) { log_debug3("\nRunning config contents after all init done"); } else { log_debug3("\nFactory default running config contents"); } cfg = cfg_get_config_id(NCX_CFGID_RUNNING); val_dump_value_max(cfg->root, 0, NCX_DEF_INDENT, DUMP_VAL_LOG, NCX_DISPLAY_MODE_PREFIX, FALSE, TRUE); log_debug3("\n"); } if (startup_loaded) { /* the config was loaded */ /* TBD: run top-level mandatory object checks */ } else { /* the factory default config was used; * if so, then the root has not been checked at all * this is needed in case there are modules with top-level * mandatory nodes; if so, and init2 phase did not add them * then the running config will be invalid */ xml_msg_hdr_t mhdr; xml_msg_init_hdr(&mhdr); cfg = cfg_get_config_id(NCX_CFGID_RUNNING); /* allocate a transaction control block */ agt_cfg_transaction_t *txcb = agt_cfg_new_transaction(NCX_CFGID_RUNNING, AGT_CFG_EDIT_TYPE_FULL, FALSE, FALSE, &res); if (txcb == NULL || res != NO_ERR) { if (res == NO_ERR) { res = ERR_NCX_OPERATION_FAILED; } } else { res = agt_val_root_check(NULL, &mhdr, txcb, cfg->root); if (res != NO_ERR) { agt_profile.agt_load_rootcheck_errors = TRUE; agt_profile.agt_config_state = AGT_CFG_STATE_BAD; if (agt_profile.agt_running_error) { /* quit if any running config errors */ log_error("\nError: validation errors occurred loading the " "factory default database\n"); } else { /* continue if any running errors */ log_warn("\nWarning: validation errors occurred " "loading the factory default database\n"); res = NO_ERR; } /* move any error messages to the config error Q */ dlq_block_enque(&mhdr.errQ, &cfg->load_errQ); } else { agt_profile.agt_config_state = AGT_CFG_STATE_OK; } } xml_msg_clean_hdr(&mhdr); agt_cfg_free_transaction(txcb); /* check to see if factory default startup mode is active */ if (agt_profile.agt_factorystartup && res == NO_ERR) { log_info("\nSaving factory default config to startup config"); res = agt_ncx_cfg_save(cfg, FALSE); if (res == NO_ERR) { agt_profile.agt_factorystartup = FALSE; } } if (res != NO_ERR) { return res; } } /* allow users to access the configuration databases now */ cfg_set_state(NCX_CFGID_RUNNING, CFG_ST_READY); /* set the correct configuration target */ if (agt_profile.agt_targ==NCX_AGT_TARG_CANDIDATE) { res = cfg_fill_candidate_from_running(); if (res != NO_ERR) { return res; } cfg_set_state(NCX_CFGID_CANDIDATE, CFG_ST_READY); cfg_set_target(NCX_CFGID_CANDIDATE); } else { cfg_set_target(NCX_CFGID_RUNNING); } /* setup the startup config only if used */ if (agt_profile.agt_start==NCX_AGT_START_DISTINCT) { cfg_set_state(NCX_CFGID_STARTUP, CFG_ST_READY); } /* data modules can be accessed now, and still added * and deleted dynamically as well * * No sessions can actually be started until the * agt_ncxserver_run function is called from netconfd_run */ return NO_ERR; } /* agt_init2 */ /******************************************************************** * FUNCTION agt_cleanup * * Cleanup the Server Library * * TBD -- put platform-specific server cleanup here * *********************************************************************/ void agt_cleanup (void) { agt_dynlib_cb_t *dynlib; if (agt_init_done) { log_debug3("\nServer Cleanup Starting...\n"); /* cleanup all the dynamically loaded modules */ while (!dlq_empty(&agt_dynlibQ)) { dynlib = (agt_dynlib_cb_t *)dlq_deque(&agt_dynlibQ); #ifndef STATIC_SERVER if (!dynlib->cleanup_done) { (*dynlib->cleanupfn)(); } dlclose(dynlib->handle); #endif free_dynlib_cb(dynlib); } clean_server_profile(); agt_acm_cleanup(); agt_ncx_cleanup(); agt_hello_cleanup(); agt_nmda_cleanup(); agt_cli_cleanup(); agt_yang_library_cleanup(); agt_sys_cleanup(); agt_state_cleanup(); agt_not_cleanup(); agt_proc_cleanup(); y_ietf_netconf_partial_lock_cleanup(); y_yuma_time_filter_cleanup(); agt_ses_cleanup(); agt_cap_cleanup(); agt_rpc_cleanup(); agt_signal_cleanup(); agt_timer_cleanup(); agt_connect_cleanup(); agt_commit_complete_cleanup(); agt_cb_cleanup(); print_errors(); log_audit_close(); log_close(); agt_init_done = FALSE; } } /* agt_cleanup */ /******************************************************************** * FUNCTION agt_get_profile * * Get the server profile struct * * INPUTS: * none * RETURNS: * pointer to the server profile *********************************************************************/ agt_profile_t * agt_get_profile (void) { return &agt_profile; } /* agt_get_profile */ /******************************************************************** * FUNCTION agt_request_shutdown * * Request some sort of server shutdown * * INPUTS: * mode == requested shutdown mode * *********************************************************************/ void agt_request_shutdown (ncx_shutdowntyp_t mode) { #ifdef DEBUG if (mode <= NCX_SHUT_NONE || mode > NCX_SHUT_EXIT) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif /* don't allow the shutdown mode to change in mid-stream */ if (agt_shutdown_started) { return; } agt_shutdown = TRUE; agt_shutdown_started = TRUE; agt_shutmode = mode; } /* agt_request_shutdown */ /******************************************************************** * FUNCTION agt_shutdown_requested * * Check if some sort of server shutdown is in progress * * RETURNS: * TRUE if shutdown mode has been started * *********************************************************************/ boolean agt_shutdown_requested (void) { return agt_shutdown; } /* agt_shutdown_requested */ /******************************************************************** * FUNCTION agt_shutdown_mode_requested * * Check what shutdown mode was requested * * RETURNS: * shutdown mode * *********************************************************************/ ncx_shutdowntyp_t agt_shutdown_mode_requested (void) { return agt_shutmode; } /* agt_shutdown_mode_requested */ /******************************************************************** * FUNCTION agt_cbtype_name * * Get the string for the server callback phase * * INPUTS: * cbtyp == callback type enum * * RETURNS: * const string for this enum *********************************************************************/ const xmlChar * agt_cbtype_name (agt_cbtyp_t cbtyp) { switch (cbtyp) { case AGT_CB_VALIDATE: return (const xmlChar *)"validate"; case AGT_CB_APPLY: return (const xmlChar *)"apply"; case AGT_CB_COMMIT: return (const xmlChar *)"commit"; case AGT_CB_ROLLBACK: return (const xmlChar *)"rollback"; default: SET_ERROR(ERR_INTERNAL_VAL); return (const xmlChar *)"invalid"; } } /* agt_cbtyp_name */ #ifndef STATIC_SERVER /******************************************************************** * FUNCTION agt_load_sil_code * * Load the Server Instrumentation Library for the specified module * * INPUTS: * modname == name of the module to load * revision == revision date of the module to load (may be NULL) * cfgloaded == TRUE if running config has already been done * FALSE if running config not loaded yet * * RETURNS: * const string for this enum *********************************************************************/ status_t agt_load_sil_code (const xmlChar *modname, const xmlChar *revision, boolean cfgloaded) { agt_dynlib_cb_t *dynlib; void *handle; agt_sil_init_fn_t initfn; agt_sil_init2_fn_t init2fn; agt_sil_cleanup_fn_t cleanupfn; char *error; xmlChar *buffer, *p, *pathspec; uint32 bufflen; status_t res; assert ( modname && "param modname is NULL" ); res = NO_ERR; handle = NULL; /* create a buffer for the library name * and later for function names */ bufflen = xml_strlen(modname) + 32; buffer = m__getMem(bufflen); if (buffer == NULL) { return ERR_INTERNAL_MEM; } p = buffer; p += xml_strcpy(p, (const xmlChar *)"lib"); p += xml_strcpy(p, modname); xml_strcpy(p, (const xmlChar *)".so"); /* try to find it directly for loading */ pathspec = ncxmod_find_sil_file(buffer, FALSE, &res); if (pathspec == NULL) { m__free(buffer); return ERR_NCX_SKIPPED; } handle = dlopen((const char *)pathspec, RTLD_NOW); if (handle == NULL) { log_error("\nError: could not open '%s' (%s)\n", pathspec, dlerror()); m__free(buffer); m__free(pathspec); return ERR_NCX_OPERATION_FAILED; } else if (LOGDEBUG2) { log_debug2("\nOpened SIL (%s) OK", pathspec); } m__free(pathspec); pathspec = NULL; p = buffer; p += xml_strcpy(p, Y_PREFIX); p += ncx_copy_c_safe_str(p, modname); xml_strcpy(p, INIT_SUFFIX); initfn = dlsym(handle, (const char *)buffer); error = dlerror(); if (error != NULL) { log_error("\nError: could not open '%s' (%s)\n", buffer, dlerror()); m__free(buffer); dlclose(handle); return ERR_NCX_OPERATION_FAILED; } xml_strcpy(p, INIT2_SUFFIX); init2fn = dlsym(handle, (const char *)buffer); error = dlerror(); if (error != NULL) { log_error("\nError: could not open '%s' (%s)\n", buffer, dlerror()); m__free(buffer); dlclose(handle); return ERR_NCX_OPERATION_FAILED; } xml_strcpy(p, CLEANUP_SUFFIX); cleanupfn = dlsym(handle, (const char *)buffer); error = dlerror(); if (error != NULL) { log_error("\nError: could not open '%s' (%s)\n", buffer, dlerror()); m__free(buffer); dlclose(handle); return ERR_NCX_OPERATION_FAILED; } if (LOGDEBUG2) { log_debug2("\nLoaded SIL functions OK"); } m__free(buffer); buffer = NULL; dynlib = new_dynlib_cb(); if (dynlib == NULL) { log_error("\nError: dynlib CB malloc failed"); dlclose(handle); return ERR_INTERNAL_MEM; } dynlib->handle = handle; dynlib->modname = xml_strdup(modname); if (dynlib->modname == NULL) { log_error("\nError: dynlib CB malloc failed"); dlclose(handle); free_dynlib_cb(dynlib); return ERR_INTERNAL_MEM; } if (revision) { dynlib->revision = xml_strdup(revision); if (dynlib->revision == NULL) { log_error("\nError: dynlib CB malloc failed"); dlclose(handle); free_dynlib_cb(dynlib); return ERR_INTERNAL_MEM; } } dynlib->initfn = initfn; dynlib->init2fn = init2fn; dynlib->cleanupfn = cleanupfn; dlq_enque(dynlib, &agt_dynlibQ); res = (*initfn)(modname, revision); dynlib->init_status = res; if (res != NO_ERR) { log_error("\nError: SIL init function " "failed for module %s (%s)", modname, get_error_string(res)); (*cleanupfn)(); dynlib->cleanup_done = TRUE; return res; } else if (LOGDEBUG2) { log_debug2("\nRan SIL init function OK for module '%s'", modname); } if (cfgloaded) { res = (*init2fn)(); dynlib->init2_done = TRUE; dynlib->init2_status = res; if (res != NO_ERR) { log_error("\nError: SIL init2 function " "failed for module %s (%s)", modname, get_error_string(res)); (*cleanupfn)(); dynlib->cleanup_done = TRUE; return res; } else if (LOGDEBUG2) { log_debug2("\nRan SIL init2 function OK for module '%s'", modname); } } return NO_ERR; } /* agt_load_sil_code */ #endif /******************************************************************** * FUNCTION agt_advertise_module_needed * * Check if the module should be advertised or not * Hard-wired hack at this time * * INPUTS: * modname == module name to check * RETURNS: * none *********************************************************************/ boolean agt_advertise_module_needed (const xmlChar *modname) { val_value_t* val; val_value_t* clivalset; if (!xml_strcmp(modname, NCX_EL_XSD)) { return FALSE; } if (!xml_strcmp(modname, NCXMOD_NETCONFD)) { return FALSE; } if (!xml_strcmp(modname, NCXMOD_NETCONFD_EX)) { return FALSE; } clivalset = agt_cli_get_valset(); val = val_find_child(clivalset, NCXMOD_NETCONFD_EX, NCX_EL_NON_ADVERTISED_MODULE); while (val) { if (!xml_strcmp(modname, VAL_STRING(val))) { return FALSE; } val = val_find_next_child(clivalset, NCXMOD_NETCONFD_EX, NCX_EL_NON_ADVERTISED_MODULE, val); } return TRUE; } /* agt_advertise_module_needed */ /* END file agt.c */ yuma123_2.14/netconf/src/agt/agt_hello.h0000664000175000017500000000571614770023131020241 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_hello #define _H_agt_hello /* FILE: agt_hello.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Handle the NETCONF (top-level) element. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 19-jan-07 abb Begun */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_hello_init * * Initialize the agt_hello module * Adds the agt_hello_dispatch function as the handler * for the NETCONF top-level element. * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ extern status_t agt_hello_init (void); /******************************************************************** * FUNCTION agt_hello_cleanup * * Cleanup the agt_hello module. * Unregister the top-level NETCONF element * *********************************************************************/ extern void agt_hello_cleanup (void); /******************************************************************** * FUNCTION agt_hello_dispatch * * Handle an incoming message from the client * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ extern void agt_hello_dispatch (ses_cb_t *scb, xml_node_t *top); /******************************************************************** * FUNCTION agt_hello_send * * Send the server message to the manager on the * specified session * * INPUTS: * scb == session control block * * RETURNS: * status *********************************************************************/ extern status_t agt_hello_send (ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_hello */ yuma123_2.14/netconf/src/agt/agt_connect.h0000664000175000017500000000555614770023131020571 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_connect #define _H_agt_connect /* FILE: agt_connect.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Handle the (top-level) element. This message is used for thin clients to connect to the ncxserver. Client --> SSH2 --> OpenSSH.subsystem(netconf) --> ncxserver_connect --> AF_LOCAL/ncxserver.sock --> ncxserver.listen --> top_dispatch -> ncx_connect_handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 15-jan-07 abb Begun */ #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_connect_init * * Initialize the agt_connect module * Adds the agt_connect_dispatch function as the handler * for the NCX top-level element. * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ extern status_t agt_connect_init (void); /******************************************************************** * FUNCTION agt_connect_cleanup * * Cleanup the agt_connect module. * Unregister the top-level NCX element * *********************************************************************/ extern void agt_connect_cleanup (void); /******************************************************************** * FUNCTION agt_connect_dispatch * * Handle an incoming request * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ extern void agt_connect_dispatch (ses_cb_t *scb, xml_node_t *top); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_connect */ yuma123_2.14/netconf/src/agt/agt_commit_validate.c0000664000175000017500000001304414770023131022263 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_commit_validate.c ********************************************************************* * P U R P O S E ********************************************************************* NETCONF Server Commit Validate callback handler This file contains functions to support registering, unregistering and execution of commit validate callbacks. ********************************************************************* * C H A N G E H I S T O R Y ********************************************************************* date init comment ---------------------------------------------------------------------- 24-oct-11 mp First draft. */ /******************************************************************** * Y U M A H E A D E R S *********************************************************************/ #include "agt_commit_validate.h" #include "dlq.h" #include "xml_util.h" /******************************************************************** * S T A N D A R D H E A D E R S *********************************************************************/ #include #include /******************************************************************** * T Y P E S *********************************************************************/ typedef struct agt_cb_commit_validate_set_t_ { dlq_hdr_t qhdr; xmlChar *modname; agt_commit_validate_cb_t callback; } agt_cb_commit_validate_set_t; /************** S T A T I C D A T A ****************************/ static bool initialised = false; static dlq_hdr_t callbackQ; /************** S T A T I C F U N C T I O N S ******************/ /********************************************************************/ static void free_callback_set( agt_cb_commit_validate_set_t* cbSet ) { if ( cbSet->modname ) { m__free( cbSet->modname ); } m__free( cbSet ); } /********************************************************************/ static agt_cb_commit_validate_set_t* new_callback_set( const xmlChar *modname ) { agt_cb_commit_validate_set_t* cbSet = m__getObj( agt_cb_commit_validate_set_t ); if ( !cbSet ) { return NULL; } memset( cbSet, 0, sizeof( agt_cb_commit_validate_set_t ) ); cbSet->modname = xml_strdup( modname ); if ( !cbSet->modname ) { m__free( cbSet ); return NULL; } return cbSet; } /********************************************************************/ static agt_cb_commit_validate_set_t* find_callback_set( const xmlChar *modname ) { agt_cb_commit_validate_set_t* cbSet; for ( cbSet = ( agt_cb_commit_validate_set_t* )dlq_firstEntry( &callbackQ ); cbSet != NULL; cbSet = ( agt_cb_commit_validate_set_t* )dlq_nextEntry( cbSet ) ) { if ( 0 == xml_strcmp( modname, cbSet->modname ) ) { return cbSet; } } return NULL; } /************** E X T E R N A L F U N C T I O N S **************/ /********************************************************************/ void agt_commit_validate_init( void ) { if ( !initialised ) { dlq_createSQue( &callbackQ ); initialised = true; } } /* agt_commit_validate_init */ /********************************************************************/ void agt_commit_validate_cleanup( void ) { if ( initialised ) { agt_cb_commit_validate_set_t* cbSet; while ( !dlq_empty( &callbackQ ) ) { cbSet = ( agt_cb_commit_validate_set_t* )dlq_deque( &callbackQ ); free_callback_set( cbSet ); } initialised = false; } } /* agt_commit_validate_cleanup */ /********************************************************************/ status_t agt_commit_validate_register( const xmlChar *modname, agt_commit_validate_cb_t cb ) { assert( modname ); agt_cb_commit_validate_set_t* cbSet = find_callback_set( modname ); if ( !cbSet ) { cbSet = new_callback_set( modname ); if ( !cbSet ) { return ERR_INTERNAL_MEM; } dlq_enque( cbSet, &callbackQ ); } cbSet->callback = cb; return NO_ERR; } /********************************************************************/ void agt_commit_validate_unregister( const xmlChar *modname ) { assert( modname ); agt_cb_commit_validate_set_t* cbSet = find_callback_set( modname ); if ( cbSet ) { dlq_remove( cbSet ); free_callback_set( cbSet ); } } /********************************************************************/ status_t agt_commit_validate( ses_cb_t *scb, xml_msg_hdr_t *msghdr, val_value_t *root ) { agt_cb_commit_validate_set_t* cbSet; for ( cbSet = ( agt_cb_commit_validate_set_t* )dlq_firstEntry( &callbackQ ); cbSet != NULL; cbSet = ( agt_cb_commit_validate_set_t* )dlq_nextEntry( cbSet ) ) { if ( cbSet->callback ) { status_t res = cbSet->callback(scb, msghdr, root); if ( NO_ERR != res ) { return res; } } } return NO_ERR; } /* END file agt_commit_validate.c */ yuma123_2.14/netconf/src/agt/agt_yang_library.c0000664000175000017500000003257514770023131021616 0ustar vladimirvladimir/* * Copyright (c) 2017, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: agt_yang_library.c NETCONF ietf-yang-library.yang Data Model implementation: Server Side Support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 26jul17 vv begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cap.h" #include "agt_cb.h" #include "agt_cfg.h" #include "agt_cli.h" #include "agt_not.h" #include "agt_rpc.h" #include "agt_ses.h" #include "agt_sys.h" #include "agt_util.h" #include "cfg.h" #include "getcb.h" #include "log.h" #include "ncxmod.h" #include "ncxtypes.h" #include "ncx_feature.h" #include "ncx_list.h" #include "rpc.h" #include "rpc_err.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "tstamp.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xml_wr.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define ietf_yang_library_N_modules_state (const xmlChar *)"modules-state" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean agt_yang_library_init_done = FALSE; /* ietf-yang-library.yang */ static ncx_module_t *ietf_yang_library_mod; /* cached pointers*/ static obj_template_t *ietf_yang_library_modules_state_obj; /************* I N T E R N A L F U N C T I O N S ***************/ static status_t get_modules_state(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res; ncx_module_t *mod; obj_template_t *module_obj; obj_template_t *deviation_obj; obj_template_t *submodule_obj; val_value_t *module_set_id_val; val_value_t *module_val; val_value_t *deviation_val; val_value_t *submodule_val; val_value_t *childval; ncx_feature_t *feature; ncx_save_deviations_t *devmod; ncx_include_t *inc; module_obj = obj_find_child(ietf_yang_library_modules_state_obj, "ietf-yang-library", "module"); assert(module_obj); /* add all modules */ for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { if (!agt_advertise_module_needed(mod->name)) { continue; } /* create schema node */ module_val = val_new_value(); assert(module_val); val_init_from_template(module_val, module_obj); /* name */ childval = agt_make_leaf(module_obj, "name", ncx_get_modname(mod), &res); assert(res==NO_ERR && childval); val_add_child(childval, module_val); /* revision */ childval = agt_make_leaf(module_obj, "revision", ncx_get_modversion(mod)?ncx_get_modversion(mod):(xmlChar *)"", &res); assert(res==NO_ERR && childval); val_add_child(childval, module_val); /* namespace */ childval = agt_make_leaf(module_obj, "namespace", ncx_get_modnamespace(mod), &res); assert(res==NO_ERR && childval); val_add_child(childval, module_val); /* conformance-type */ childval = agt_make_leaf(module_obj, "conformance-type", mod->implemented?"implement":"import", &res); assert(res==NO_ERR && childval); val_add_child(childval, module_val); /* feature */ for (feature = (ncx_feature_t *)dlq_firstEntry(&mod->featureQ); feature != NULL; feature = (ncx_feature_t *)dlq_nextEntry(feature)) { if (ncx_feature_enabled(feature)) { childval = agt_make_leaf(module_obj, "feature", feature->name, &res); assert(res==NO_ERR && childval); val_add_child(childval, module_val); } } /* deviation */ deviation_obj = obj_find_child(module_obj, "ietf-yang-library", "deviation"); assert(deviation_obj); for (devmod = (ncx_save_deviations_t *)dlq_firstEntry(&mod->devmodlist); devmod != NULL; devmod = (ncx_save_deviations_t *)dlq_nextEntry(devmod)) { ncx_module_t *match_mod; char* revision_str; deviation_val = val_new_value(); assert(deviation_val); val_init_from_template(deviation_val, deviation_obj); childval = agt_make_leaf(deviation_obj, "name", devmod->devmodule, &res); assert(res==NO_ERR && childval); val_add_child(childval, deviation_val); childval = agt_make_leaf(deviation_obj, "revision", devmod->devrevision, &res); assert(res==NO_ERR && childval); val_add_child(childval, deviation_val); res = val_gen_index_chain(deviation_obj, deviation_val); assert(res == NO_ERR); val_add_child(deviation_val, module_val); } /* submodule */ submodule_obj = obj_find_child(module_obj, "ietf-yang-library", "submodule"); assert(submodule_obj); for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { submodule_val = val_new_value(); assert(submodule_val); val_init_from_template(submodule_val, submodule_obj); /* name */ childval = agt_make_leaf(submodule_obj, "name", inc->submodule, &res); assert(res==NO_ERR && childval); val_add_child(childval, submodule_val); /* revision */ childval = agt_make_leaf(submodule_obj, "revision", inc->revision?inc->revision:(xmlChar *)"", &res); assert(res==NO_ERR && childval); val_add_child(childval, submodule_val); val_add_child(submodule_val, module_val); res = val_gen_index_chain(submodule_obj, submodule_val); assert(res == NO_ERR); } /* submodule - ends */ res = val_gen_index_chain(module_obj, module_val); assert(res == NO_ERR); val_add_child(module_val, dst_val); if(0==strcmp(ncx_get_modname(mod), NCXMOD_NETCONF)) { /* ietf-netconf is overloaded internally */ val_value_t* newval; val_value_t* curval; /* name */ newval = agt_make_leaf(module_obj, "name", NCXMOD_IETF_NETCONF, &res); assert(res==NO_ERR && newval); curval = val_find_child(module_val, "ietf-yang-library", "name"); assert(curval); val_swap_child(newval,curval); val_free_value(curval); /* revision */ newval = agt_make_leaf(module_obj, "revision", NCXMOD_IETF_NETCONF_REVISION, &res); assert(res==NO_ERR && newval); curval = val_find_child(module_val, "ietf-yang-library", "revision"); assert(curval); val_swap_child(newval,curval); val_free_value(curval); } } /* module-set-id */ { unsigned int i; char* modules_state_str; unsigned char hash[SHA_DIGEST_LENGTH]; unsigned char hash_str[SHA_DIGEST_LENGTH*2+1]; res=val_make_serialized_string(dst_val, NCX_DISPLAY_MODE_XML, (xmlChar **)&modules_state_str); assert(res==NO_ERR); SHA1(modules_state_str, strlen(modules_state_str), hash); free(modules_state_str); for(i=0;iagt_savedevQ, &ietf_yang_library_mod); assert(res == NO_ERR); /* find the object definition for the modules-state container */ ietf_yang_library_modules_state_obj = ncx_find_object(ietf_yang_library_mod, ietf_yang_library_N_modules_state); assert(ietf_yang_library_modules_state_obj); return NO_ERR; } /* agt_yang_library_init */ /******************************************************************** * FUNCTION agt_yang_library_init2 * * INIT 2: * Initialize the yang library data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t agt_yang_library_init2 (void) { val_value_t *ietf_yang_library_modules_state_val; cfg_template_t *runningcfg; status_t res; /* get the running config */ runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (!runningcfg || !runningcfg->root) { return SET_ERROR(ERR_INTERNAL_VAL); } /* add /system-state */ ietf_yang_library_modules_state_val = val_new_value(); assert(ietf_yang_library_modules_state_val); val_init_virtual(ietf_yang_library_modules_state_val, get_modules_state, ietf_yang_library_modules_state_obj); /* handing off the malloced memory here */ val_add_child_sorted(ietf_yang_library_modules_state_val, runningcfg->root); return NO_ERR; } /* agt_yang_library_init2 */ /******************************************************************** * FUNCTION agt_yang_library_cleanup * * Cleanup the module data structures * * INPUTS: * * RETURNS: * none *********************************************************************/ void agt_yang_library_cleanup (void) { } /* agt_yang_library_cleanup */ /* END file agt_yang_library.c */ yuma123_2.14/netconf/src/agt/agt_acm.h0000664000175000017500000002437314770023131017676 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_acm #define _H_agt_acm /* FILE: agt_acm.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Server Access Control handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 03-feb-06 abb Begun 14-may-09 abb add per-msg cache to speed up performance */ #ifndef _H_agt #include "agt.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xml_msg #include "xml_msg.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* flags fields for the agt_acm_cache_t */ #define FL_ACM_DEFREAD_SET bit0 #define FL_ACM_DEFREAD_OK bit1 #define FL_ACM_DEFWRITE_SET bit2 #define FL_ACM_DEFWRITE_OK bit3 #define FL_ACM_DEFEXEC_SET bit4 #define FL_ACM_DEFEXEC_OK bit5 #define FL_ACM_MODRULES_SET bit6 #define FL_ACM_DATARULES_SET bit7 #define FL_ACM_CACHE_VALID bit8 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* 1 group that the user is a member */ typedef struct agt_acm_group_t_ { dlq_hdr_t qhdr; const xmlChar *groupname; } agt_acm_group_t; /* list of group identities that the user is a member */ typedef struct agt_acm_usergroups_t_ { dlq_hdr_t qhdr; xmlChar *username; dlq_hdr_t groupQ; /* Q of agt_acm_group_t */ } agt_acm_usergroups_t; /* cache for 1 NACM moduleRule entry */ typedef struct agt_acm_modrule_t_ { dlq_hdr_t qhdr; xmlns_id_t nsid; val_value_t *modrule; /* back-ptr */ } agt_acm_modrule_t; /* cache for 1 NACM dataRule entry */ typedef struct agt_acm_datarule_t_ { dlq_hdr_t qhdr; xpath_pcb_t *pcb; xpath_result_t *result; val_value_t *datarule; /* back-ptr */ } agt_acm_datarule_t; /* NACM cache control block */ #define DATA_RULE_QUEUE_READ 0 #define DATA_RULE_QUEUE_UPDATE 1 #define DATA_RULE_QUEUE_CREATE 2 #define DATA_RULE_QUEUE_DELETE 3 #define DATA_RULE_QUEUE_NUM 4 typedef struct agt_acm_cache_t_ { agt_acm_usergroups_t *usergroups; val_value_t *nacmroot; /* back-ptr */ val_value_t *rulesval; /* back-ptr */ uint32 groupcnt; uint32 flags; agt_acmode_t mode; dlq_hdr_t modruleQ; /* Q of agt_acm_modrule_t */ dlq_hdr_t dataruleQ[4]; /* Q of agt_acm_datarule_t */ } agt_acm_cache_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_acm_init * * Initialize the NETCONF Server access control module * * INPUTS: * none * RETURNS: * status of the initialization procedure *********************************************************************/ extern status_t agt_acm_init (void); /******************************************************************** * FUNCTION agt_acm_init2 * * Phase 2: * Initialize the nacm.yang configuration data structures * * INPUTS: * none * RETURNS: * status of the initialization procedure *********************************************************************/ extern status_t agt_acm_init2 (void); /******************************************************************** * FUNCTION agt_acm_cleanup * * Cleanup the NETCONF Server access control module * *********************************************************************/ extern void agt_acm_cleanup (void); /******************************************************************** * FUNCTION agt_acm_rpc_allowed * * Check if the specified user is allowed to invoke an RPC * * INPUTS: * msg == XML header in incoming message in progress * user == user name string * rpcobj == obj_template_t for the RPC method to check * * RETURNS: * TRUE if user allowed invoke this RPC; FALSE otherwise *********************************************************************/ extern boolean agt_acm_rpc_allowed (xml_msg_hdr_t *msg, const xmlChar *user, const obj_template_t *rpcobj); /******************************************************************** * FUNCTION agt_acm_notif_allowed * * Check if the specified user is allowed to receive * a notification event * d * INPUTS: * user == user name string * notifobj == obj_template_t for the notification event to check * * RETURNS: * TRUE if user allowed receive this notification event; * FALSE otherwise *********************************************************************/ extern boolean agt_acm_notif_allowed (const xmlChar *user, const obj_template_t *notifobj); /******************************************************************** * FUNCTION agt_acm_val_write_allowed * * Check if the specified user is allowed to access a value node * The val->obj template will be checked against the val->editop * requested access and the user's configured max-access * * INPUTS: * msg == XML header from incoming message in progress * user == user name string * newval == val_value_t in progress to check * (may be NULL, if curval set) * curval == val_value_t in progress to check * (may be NULL, if newval set) * editop == requested CRUD operation * * RETURNS: * TRUE if user allowed this level of access to the value node *********************************************************************/ extern boolean agt_acm_val_write_allowed (xml_msg_hdr_t *msg, const xmlChar *user, const val_value_t *newval, const val_value_t *curval, op_editop_t editop); /******************************************************************** * FUNCTION agt_acm_val_read_allowed * * Check if the specified user is allowed to read a value node * * INPUTS: * msg == XML header from incoming message in progress * user == user name string * val == val_value_t in progress to check * * RETURNS: * TRUE if user allowed read access to the value node *********************************************************************/ extern boolean agt_acm_val_read_allowed (xml_msg_hdr_t *msg, const xmlChar *user, const val_value_t *val); /******************************************************************** * FUNCTION agt_acm_init_msg_cache * * Malloc and initialize an agt_acm_cache_t struct * and attach it to the incoming message * * INPUTS: * scb == session control block to use * msg == message to use * * OUTPUTS: * scb->acm_cache pointer may be set, if it was NULL * msg->acm_cache pointer set * * RETURNS: * status *********************************************************************/ extern status_t agt_acm_init_msg_cache (ses_cb_t *scb, xml_msg_hdr_t *msg); /******************************************************************** * FUNCTION agt_acm_clear_msg_cache * * Clear an agt_acm_cache_t struct * attached to the specified message * * INPUTS: * msg == message to use * * OUTPUTS: * msg->acm_cache pointer is freed and set to NULL * *********************************************************************/ extern void agt_acm_clear_msg_cache (xml_msg_hdr_t *msg); /******************************************************************** * FUNCTION agt_acm_clear_session_cache * * Clear an agt_acm_cache_t struct in a session control block * * INPUTS: * scb == sesion control block to use * * OUTPUTS: * scb->acm_cache pointer is freed and set to NULL * *********************************************************************/ extern void agt_acm_clear_session_cache (ses_cb_t *scb); /******************************************************************** * FUNCTION agt_acm_invalidate_session_cache * * Mark an agt_acm_cache_t struct in a session control block * as invalid so it will be refreshed next use * * INPUTS: * scb == sesion control block to use * *********************************************************************/ extern void agt_acm_invalidate_session_cache (ses_cb_t *scb); /******************************************************************** * FUNCTION agt_acm_session_cache_valid * * Check if the specified session NACM cache is valid * * INPUTS: * scb == session to check * * RETURNS: * TRUE if session acm_cache is valid * FALSE if session acm_cache is NULL or not valid *********************************************************************/ extern boolean agt_acm_session_cache_valid (const ses_cb_t *scb); /******************************************************************** * FUNCTION agt_acm_session_is_superuser * * Check if the specified session is the superuser * * INPUTS: * scb == session to check * * RETURNS: * TRUE if session is for the superuser * FALSE if session is not for the superuser *********************************************************************/ extern boolean agt_acm_session_is_superuser (const ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_acm */ yuma123_2.14/netconf/src/agt/agt_tree.h0000664000175000017500000001156314770023131020072 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_agt_tree #define _H_agt_tree /* FILE: agt_tree.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Server subtree filter processing for element in and operations ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 16-jun-06 abb Begun */ #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION agt_tree_prune_filter * * get and get-config step 1 * Need to evaluate the entire subtree filter and remove nodes * which are not in the result set * * The filter subtree usually starts out as type NCX_BT_CONTAINER * but a single select node could be present instead. * * The subtree starts out as type NCX_BT_CONTAINER (root) * and is converted by val_parse as follows: * * Subtree Filter: * NCX_BT_ANYXML -- start type * changed to real types during parsing * NCX_BT_CONTAINER --> container node * NCX_BT_EMPTY --> select node * NCX_BT_STRING --> content select node * * INPUTS: * scb == session control block * == NULL if access control should not be applied * msg == rpc_msg_t in progress * cfg == config target to check against * getop == TRUE if this is a and not a * The target is expected to be the * config, and all state data will be available for the * filter output. * FALSE if this is a and only the * specified target in available for filter output * * OUTPUTS: * msg->rpc_filter.op_filter is pruned as needed by setting * the VAL_FL_FILTERED bit.in the val->flags field * for the start of subtrees which failed the filter test. * Only nodes which result in non-NULL output should * remain unchanged at the end of this procedure. * * RETURNS: * pointer to generated tree of matching nodes * NULL if no match *********************************************************************/ extern ncx_filptr_t * agt_tree_prune_filter (ses_cb_t *scb, rpc_msg_t *msg, const cfg_template_t *cfg, boolean getop); /******************************************************************** * FUNCTION agt_tree_output_filter * * get and get-config step 2 * Output the pruned subtree filter to the specified session * * INPUTS: * scb == session control block * == NULL if access control should not be applied * msg == rpc_msg_t in progress * top == ncx_filptr tree to output * indent == start indent amount * getop == TRUE if , FALSE if * * RETURNS: * none *********************************************************************/ extern void agt_tree_output_filter (ses_cb_t *scb, rpc_msg_t *msg, ncx_filptr_t *top, int32 indent, boolean getop); /******************************************************************** * FUNCTION agt_tree_test_filter * * notification filter evaluation * Need to evaluate the entire subtree filter and return 'FALSE' * if any nodes in the filter are not in the result set * (this is probably a notification payload) * * INPUTS: * msghdr == message in progress; needed for access control * scb == session control block; needed for access control * filter == subtree filter to use * topval == value tree to check against * * RETURNS: * TRUE if the filter matched; notification can be sent * FALSE if the filter did not match; notification not sent *********************************************************************/ extern boolean agt_tree_test_filter (xml_msg_hdr_t *msghdr, ses_cb_t *scb, val_value_t *filter, val_value_t *topval); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_agt_tree */ yuma123_2.14/netconf/src/yangdiff/0000775000175000017500000000000014770023131017135 5ustar vladimirvladimiryuma123_2.14/netconf/src/yangdiff/yangdiff_grp.c0000664000175000017500000002621014770023131021741 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangdiff_grp.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 10-jul-08 abb split out from yangdiff.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangdiff #include "yangdiff.h" #endif #ifndef _H_yangdiff_grp #include "yangdiff_grp.h" #endif #ifndef _H_yangdiff_obj #include "yangdiff_obj.h" #endif #ifndef _H_yangdiff_typ #include "yangdiff_typ.h" #endif #ifndef _H_yangdiff_util #include "yangdiff_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define YANGDIFF_GRP_DEBUG 1 /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION output_one_grouping_diff * * Output the differences report for one typedef definition * * INPUTS: * cp == parameter block to use * oldgrp == old grouping * newgrp == new grouping * *********************************************************************/ static void output_one_grouping_diff (yangdiff_diffparms_t *cp, grp_template_t *oldgrp, grp_template_t *newgrp) { yangdiff_cdb_t cdb[3]; uint32 changecnt, i; boolean isrev, tchanged, gchanged, dchanged; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; tchanged = FALSE; gchanged = FALSE; dchanged = FALSE; changecnt = 0; if (typedefQ_changed(cp, &oldgrp->typedefQ, &newgrp->typedefQ)) { tchanged = TRUE; changecnt++; } if (groupingQ_changed(cp, &oldgrp->groupingQ, &newgrp->groupingQ)) { gchanged = TRUE; changecnt++; } if (datadefQ_changed(cp, &oldgrp->datadefQ, &newgrp->datadefQ)) { dchanged = TRUE; changecnt++; } changecnt += status_field_changed(YANG_K_STATUS, oldgrp->status, newgrp->status, isrev, &cdb[0]); changecnt += str_field_changed(YANG_K_DESCRIPTION, oldgrp->descr, newgrp->descr, isrev, &cdb[1]); changecnt += str_field_changed(YANG_K_REFERENCE, oldgrp->ref, newgrp->ref, isrev, &cdb[2]); if (changecnt == 0) { return; } /* generate the diff output, based on the requested format */ output_mstart_line(cp, YANG_K_GROUPING, oldgrp->name, TRUE); if (cp->edifftype == YANGDIFF_DT_TERSE) { return; } indent_in(cp); for (i=0; i<3; i++) { if (cdb[i].changed) { output_cdb_line(cp, &cdb[i]); } } if (tchanged) { output_typedefQ_diff(cp, &oldgrp->typedefQ, &newgrp->typedefQ); } if (gchanged) { output_groupingQ_diff(cp, &oldgrp->groupingQ, &newgrp->groupingQ); } if (dchanged) { output_datadefQ_diff(cp, &oldgrp->datadefQ, &newgrp->datadefQ); } indent_out(cp); } /* output_one_grouping_diff */ /******************************************************************** * FUNCTION grouping_changed * * Check if a (nested) grouping changed * * INPUTS: * cp == parameter block to use * oldgrp == old grp_template_t to use * newgrp == new grp_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 grouping_changed (yangdiff_diffparms_t *cp, grp_template_t *oldgrp, grp_template_t *newgrp) { if (status_field_changed(YANG_K_STATUS, oldgrp->status, newgrp->status, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_DESCRIPTION, oldgrp->descr, newgrp->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, oldgrp->ref, newgrp->ref, FALSE, NULL)) { return 1; } if (typedefQ_changed(cp, &oldgrp->typedefQ, &newgrp->typedefQ)) { return 1; } if (groupingQ_changed(cp, &oldgrp->groupingQ, &newgrp->groupingQ)) { return 1; } if (datadefQ_changed(cp, &oldgrp->datadefQ, &newgrp->datadefQ)) { return 1; } return 0; } /* grouping_changed */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION output_groupingQ_diff * * Output the differences report for a Q of grouping definitions * Not always called for top-level groupings; Can be called * for nested groupings * * INPUTS: * cp == parameter block to use * oldQ == Q of old grp_template_t to use * newQ == Q of new grp_template_t to use * *********************************************************************/ void output_groupingQ_diff (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { grp_template_t *oldgrp, *newgrp; /* borrowing the 'used' flag for marking matched groupings * first set all these flags to FALSE */ for (newgrp = (grp_template_t *)dlq_firstEntry(newQ); newgrp != NULL; newgrp = (grp_template_t *)dlq_nextEntry(newgrp)) { newgrp->used = FALSE; } /* look through the old Q for matching entries in the new Q */ for (oldgrp = (grp_template_t *)dlq_firstEntry(oldQ); oldgrp != NULL; oldgrp = (grp_template_t *)dlq_nextEntry(oldgrp)) { /* find this grouping in the new Q */ newgrp = ncx_find_grouping_que(newQ, oldgrp->name); if (newgrp) { output_one_grouping_diff(cp, oldgrp, newgrp); newgrp->used = TRUE; } else { /* grouping was removed from the new module */ output_diff(cp, YANG_K_GROUPING, oldgrp->name, NULL, TRUE); } } /* look for groupings that were added in the new module */ for (newgrp = (grp_template_t *)dlq_firstEntry(newQ); newgrp != NULL; newgrp = (grp_template_t *)dlq_nextEntry(newgrp)) { if (!newgrp->used) { /* this grouping was added in the new version */ output_diff(cp, YANG_K_GROUPING, NULL, newgrp->name, TRUE); } } } /* output_groupingQ_diff */ /******************************************************************** * FUNCTION groupingQ_changed * * Check if a (nested) Q of groupings changed * * INPUTS: * cp == parameter block to use * oldQ == Q of old grp_template_t to use * newQ == Q of new grp_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ uint32 groupingQ_changed (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { grp_template_t *oldgrp, *newgrp; if (dlq_count(oldQ) != dlq_count(newQ)) { return 1; } /* borrowing the 'used' flag for marking matched groupings * first set all these flags to FALSE */ for (newgrp = (grp_template_t *)dlq_firstEntry(oldQ); newgrp != NULL; newgrp = (grp_template_t *)dlq_nextEntry(newgrp)) { newgrp->used = FALSE; } /* look through the old type Q for matching types in the new type Q */ for (oldgrp = (grp_template_t *)dlq_firstEntry(oldQ); oldgrp != NULL; oldgrp = (grp_template_t *)dlq_nextEntry(oldgrp)) { newgrp = ncx_find_grouping_que(newQ, oldgrp->name); if (newgrp) { if (grouping_changed(cp, oldgrp, newgrp)) { return 1; } } else { return 1; } } /* look for groupings that were added in the new module */ for (newgrp = (grp_template_t *)dlq_firstEntry(newQ); newgrp != NULL; newgrp = (grp_template_t *)dlq_nextEntry(newgrp)) { if (!newgrp->used) { return 1; } } return 0; } /* groupingQ_changed */ /* END yangdiff_grp.c */ yuma123_2.14/netconf/src/yangdiff/yangdiff_obj.c0000664000175000017500000017301614770023131021732 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangdiff_obj.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 10-jul-08 abb split out from yangdiff.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangdiff #include "yangdiff.h" #endif #ifndef _H_yangdiff_grp #include "yangdiff_grp.h" #endif #ifndef _H_yangdiff_obj #include "yangdiff_obj.h" #endif #ifndef _H_yangdiff_typ #include "yangdiff_typ.h" #endif #ifndef _H_yangdiff_util #include "yangdiff_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define YANGDIFF_GRP_DEBUG 1 /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION mustQ_changed * * Check if a Q of must-stmt definitions changed * * INPUTS: * oldQ == Q of old xpath_pcb_t to use * newQ == Q of new xpath_pcb_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 mustQ_changed (dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { xpath_pcb_t *oldm, *newm; uint32 oldcnt, newcnt; oldcnt = dlq_count(oldQ); newcnt = dlq_count(newQ); if (oldcnt != newcnt) { return 1; } if (oldcnt == 0) { return 0; } for (newm = (xpath_pcb_t *)dlq_firstEntry(newQ); newm != NULL; newm = (xpath_pcb_t *)dlq_nextEntry(newm)) { newm->seen = FALSE; } /* look through the old Q for matching entries in the new Q */ for (oldm = (xpath_pcb_t *)dlq_firstEntry(oldQ); oldm != NULL; oldm = (xpath_pcb_t *)dlq_nextEntry(oldm)) { newm = xpath_find_pcb(newQ, oldm->exprstr); if (newm) { if (errinfo_changed(&oldm->errinfo, &newm->errinfo)) { return 1; } else { newm->seen = TRUE; } } else { return 1; } } /* look for must-stmts that were added in the new module */ for (newm = (xpath_pcb_t *)dlq_firstEntry(newQ); newm != NULL; newm = (xpath_pcb_t *)dlq_nextEntry(newm)) { if (!newm->seen) { return 1; } } return 0; } /* mustQ_changed */ /******************************************************************** * FUNCTION output_mustQ_diff * * Output any changes in a Q of must-stmt definitions * * INPUTS: * cp == comparison paraeters to use * oldQ == Q of old xpath_pcb_t to use * newQ == Q of new xpath_pcb_t to use * *********************************************************************/ static void output_mustQ_diff (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { xpath_pcb_t *oldm, *newm; for (newm = (xpath_pcb_t *)dlq_firstEntry(newQ); newm != NULL; newm = (xpath_pcb_t *)dlq_nextEntry(newm)) { newm->seen = FALSE; } /* look through the old Q for matching entries in the new Q */ for (oldm = (xpath_pcb_t *)dlq_firstEntry(oldQ); oldm != NULL; oldm = (xpath_pcb_t *)dlq_nextEntry(oldm)) { newm = xpath_find_pcb(newQ, oldm->exprstr); if (newm) { newm->seen = TRUE; if (errinfo_changed(&oldm->errinfo, &newm->errinfo)) { output_mstart_line(cp, YANG_K_MUST, oldm->exprstr, FALSE); indent_in(cp); output_errinfo_diff(cp, &oldm->errinfo, &newm->errinfo); indent_out(cp); } } else { /* must-stmt was removed from the new module */ output_diff(cp, YANG_K_MUST, oldm->exprstr, NULL, FALSE); } } /* look for must-stmts that were added in the new module */ for (newm = (xpath_pcb_t *)dlq_firstEntry(newQ); newm != NULL; newm = (xpath_pcb_t *)dlq_nextEntry(newm)) { if (!newm->seen) { /* must-stmt was added in the new module */ output_diff(cp, YANG_K_MUST, NULL, newm->exprstr, FALSE); } } } /* output_mustQ_diff */ /******************************************************************** * FUNCTION uniqueQ_changed * * Check if a Q of unique-stmt definitions changed * * INPUTS: * oldQ == Q of old obj_unique_t to use * newQ == Q of new obj_unique_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 uniqueQ_changed (dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { obj_unique_t *oldun, *newun; if (dlq_count(oldQ) != dlq_count(newQ)) { return 1; } for (newun = (obj_unique_t *)dlq_firstEntry(newQ); newun != NULL; newun = (obj_unique_t *)dlq_nextEntry(newun)) { newun->seen = FALSE; } /* look through the old Q for matching entries in the new Q */ for (oldun = (obj_unique_t *)dlq_firstEntry(oldQ); oldun != NULL; oldun = (obj_unique_t *)dlq_nextEntry(oldun)) { newun = obj_find_unique(newQ, oldun->xpath); if (newun) { newun->seen = TRUE; } else { return 1; } } /* look for unique-stmts that were added in the new module */ for (newun = (obj_unique_t *)dlq_firstEntry(newQ); newun != NULL; newun = (obj_unique_t *)dlq_nextEntry(newun)) { if (!newun->seen) { return 1; } } return 0; } /* uniqueQ_changed */ /******************************************************************** * FUNCTION output_uniqueQ_diff * * Output any changes in a Q of unique-stmt definitions * * INPUTS: * cp == comparison paraeters to use * oldQ == Q of old obj_unique_t to use * newQ == Q of new obj_unique_t to use * *********************************************************************/ static void output_uniqueQ_diff (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { obj_unique_t *oldu, *newu; for (newu = (obj_unique_t *)dlq_firstEntry(newQ); newu != NULL; newu = (obj_unique_t *)dlq_nextEntry(newu)) { newu->seen = FALSE; } /* look through the old Q for matching entries in the new Q */ for (oldu = (obj_unique_t *)dlq_firstEntry(oldQ); oldu != NULL; oldu = (obj_unique_t *)dlq_nextEntry(oldu)) { newu = obj_find_unique(newQ, oldu->xpath); if (newu) { newu->seen = TRUE; } else { /* must-stmt was removed from the new module */ output_diff(cp, YANG_K_UNIQUE, oldu->xpath, NULL, FALSE); } } /* look for unique-stmts that were added in the new module */ for (newu = (obj_unique_t *)dlq_firstEntry(newQ); newu != NULL; newu = (obj_unique_t *)dlq_nextEntry(newu)) { if (!newu->seen) { /* must-stmt was added in the new module */ output_diff(cp, YANG_K_UNIQUE, NULL, newu->xpath, FALSE); } } } /* output_uniqueQ_diff */ /******************************************************************** * FUNCTION container_changed * * Check if a container definition changed * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 container_changed (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_container_t *old, *new; old = oldobj->def.container; new = newobj->def.container; if (mustQ_changed(&old->mustQ, &new->mustQ)) { return 1; } if (str_field_changed(YANG_K_PRESENCE, old->presence, new->presence, FALSE, NULL)) { return 1; } if (bool_field_changed(YANG_K_CONFIG, (oldobj->flags & OBJ_FL_CONFIG), (newobj->flags & OBJ_FL_CONFIG), FALSE, NULL)) { return 1; } if (status_field_changed(YANG_K_STATUS, old->status, new->status, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, FALSE, NULL)) { return 1; } if (typedefQ_changed(cp, old->typedefQ, new->typedefQ)) { return 1; } if (groupingQ_changed(cp, old->groupingQ, new->groupingQ)) { return 1; } if (datadefQ_changed(cp, old->datadefQ, new->datadefQ)) { return 1; } return 0; } /* container_changed */ /******************************************************************** * FUNCTION output_container_diff * * Output the differences for a container definition * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * *********************************************************************/ static void output_container_diff (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_container_t *old, *new; yangdiff_cdb_t cdb; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; old = oldobj->def.container; new = newobj->def.container; output_mustQ_diff(cp, &old->mustQ, &new->mustQ); if (str_field_changed(YANG_K_PRESENCE, old->presence, new->presence, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (bool_field_changed(YANG_K_CONFIG, (oldobj->flags & OBJ_FL_CONFIG), (newobj->flags & OBJ_FL_CONFIG), isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (status_field_changed(YANG_K_STATUS, old->status, new->status, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, isrev, &cdb)) { output_cdb_line(cp, &cdb); } output_typedefQ_diff(cp, old->typedefQ, new->typedefQ); output_groupingQ_diff(cp, old->groupingQ, new->groupingQ); output_datadefQ_diff(cp, old->datadefQ, new->datadefQ); } /* output_container_diff */ /******************************************************************** * FUNCTION leaf_changed * * Check if a leaf definition changed * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 leaf_changed (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_leaf_t *old, *new; old = oldobj->def.leaf; new = newobj->def.leaf; if (type_changed(cp, old->typdef, new->typdef)) { return 1; } if (str_field_changed(YANG_K_UNITS, old->units, new->units, FALSE, NULL)) { return 1; } if (mustQ_changed(&old->mustQ, &new->mustQ)) { return 1; } if (str_field_changed(YANG_K_DEFAULT, old->defval, new->defval, FALSE, NULL)) { return 1; } if (bool_field_changed(YANG_K_CONFIG, (oldobj->flags & OBJ_FL_CONFIG), (newobj->flags & OBJ_FL_CONFIG), FALSE, NULL)) { return 1; } if (bool_field_changed(YANG_K_MANDATORY, (oldobj->flags & OBJ_FL_MANDATORY), (newobj->flags & OBJ_FL_MANDATORY), FALSE, NULL)) { return 1; } if (status_field_changed(YANG_K_STATUS, old->status, new->status, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, FALSE, NULL)) { return 1; } return 0; } /* leaf_changed */ /******************************************************************** * FUNCTION anyxml_changed * * Check if an anyxml definition changed * * INPUTS: * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 anyxml_changed (obj_template_t *oldobj, obj_template_t *newobj) { obj_leaf_t *old, *new; old = oldobj->def.leaf; new = newobj->def.leaf; if (mustQ_changed(&old->mustQ, &new->mustQ)) { return 1; } if (bool_field_changed(YANG_K_CONFIG, (oldobj->flags & OBJ_FL_CONFIG), (newobj->flags & OBJ_FL_CONFIG), FALSE, NULL)) { return 1; } if (bool_field_changed(YANG_K_MANDATORY, (oldobj->flags & OBJ_FL_MANDATORY), (newobj->flags & OBJ_FL_MANDATORY), FALSE, NULL)) { return 1; } if (status_field_changed(YANG_K_STATUS, old->status, new->status, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, FALSE, NULL)) { return 1; } return 0; } /* anyxml_changed */ /******************************************************************** * FUNCTION output_leaf_diff * * Output the differences for a leaf definition * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * *********************************************************************/ static void output_leaf_diff (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_leaf_t *old, *new; yangdiff_cdb_t cdb; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; old = oldobj->def.leaf; new = newobj->def.leaf; if (type_changed(cp, old->typdef, new->typdef)) { output_one_type_diff(cp, old->typdef, new->typdef); } if (str_field_changed(YANG_K_UNITS, old->units, new->units, isrev, &cdb)) { output_cdb_line(cp, &cdb); } output_mustQ_diff(cp, &old->mustQ, &new->mustQ); if (str_field_changed(YANG_K_DEFAULT, old->defval, new->defval, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (bool_field_changed(YANG_K_CONFIG, (oldobj->flags & OBJ_FL_CONFIG), (newobj->flags & OBJ_FL_CONFIG), isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (bool_field_changed(YANG_K_MANDATORY, (oldobj->flags & OBJ_FL_MANDATORY), (newobj->flags & OBJ_FL_MANDATORY), isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (status_field_changed(YANG_K_STATUS, old->status, new->status, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, isrev, &cdb)) { output_cdb_line(cp, &cdb); } } /* output_leaf_diff */ /******************************************************************** * FUNCTION output_anyxml_diff * * Output the differences for an anyxml definition * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * *********************************************************************/ static void output_anyxml_diff (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_leaf_t *old, *new; yangdiff_cdb_t cdb; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; old = oldobj->def.leaf; new = newobj->def.leaf; output_mustQ_diff(cp, &old->mustQ, &new->mustQ); if (bool_field_changed(YANG_K_CONFIG, (oldobj->flags & OBJ_FL_CONFIG), (newobj->flags & OBJ_FL_CONFIG), isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (bool_field_changed(YANG_K_MANDATORY, (oldobj->flags & OBJ_FL_MANDATORY), (newobj->flags & OBJ_FL_MANDATORY), isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (status_field_changed(YANG_K_STATUS, old->status, new->status, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, isrev, &cdb)) { output_cdb_line(cp, &cdb); } } /* output_anyxml_diff */ /******************************************************************** * FUNCTION leaf_list_changed * * Check if a leaf-list definition changed * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 leaf_list_changed (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_leaflist_t *old, *new; old = oldobj->def.leaflist; new = newobj->def.leaflist; if (type_changed(cp, old->typdef, new->typdef)) { return 1; } if (str_field_changed(YANG_K_UNITS, old->units, new->units, FALSE, NULL)) { return 1; } if (mustQ_changed(&old->mustQ, &new->mustQ)) { return 1; } if (bool_field_changed(YANG_K_CONFIG, (oldobj->flags & OBJ_FL_CONFIG), (newobj->flags & OBJ_FL_CONFIG), FALSE, NULL)) { return 1; } if (bool_field_changed(YANG_K_MIN_ELEMENTS, old->minset, new->minset, FALSE, NULL)) { return 1; } if (new->minelems != old->minelems) { return 1; } if (bool_field_changed(YANG_K_MAX_ELEMENTS, old->maxset, new->maxset, FALSE, NULL)) { return 1; } if (new->maxelems != old->maxelems) { return 1; } if (old->ordersys != new->ordersys) { return 1; } if (status_field_changed(YANG_K_STATUS, old->status, new->status, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, FALSE, NULL)) { return 1; } return 0; } /* leaf_list_changed */ /******************************************************************** * FUNCTION output_leaf_list_diff * * Output the differences for a leaf-list definition * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * *********************************************************************/ static void output_leaf_list_diff (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_leaflist_t *old, *new; yangdiff_cdb_t cdb; char oldnum[NCX_MAX_NUMLEN]; char newnum[NCX_MAX_NUMLEN]; const xmlChar *oldn, *newn; const xmlChar *oldorder, *neworder; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; old = oldobj->def.leaflist; new = newobj->def.leaflist; if (type_changed(cp, old->typdef, new->typdef)) { output_one_type_diff(cp, old->typdef, new->typdef); } if (str_field_changed(YANG_K_UNITS, old->units, new->units, isrev, &cdb)) { output_cdb_line(cp, &cdb); } output_mustQ_diff(cp, &old->mustQ, &new->mustQ); if (bool_field_changed(YANG_K_CONFIG, (oldobj->flags & OBJ_FL_CONFIG), (newobj->flags & OBJ_FL_CONFIG), isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (old->minset) { sprintf(oldnum, "%u", old->minelems); oldn = (const xmlChar *)oldnum; } else { oldn = NULL; } if (new->minset) { sprintf(newnum, "%u", new->minelems); newn = (const xmlChar *)newnum; } else { newn = NULL; } if (str_field_changed(YANG_K_MIN_ELEMENTS, oldn, newn, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (old->maxset) { sprintf(oldnum, "%u", old->maxelems); oldn = (const xmlChar *)oldnum; } else { oldn = NULL; } if (new->maxset) { sprintf(newnum, "%u", new->maxelems); newn = (const xmlChar *)newnum; } else { newn = NULL; } if (str_field_changed(YANG_K_MAX_ELEMENTS, oldn, newn, isrev, &cdb)) { output_cdb_line(cp, &cdb); } oldorder = (old->ordersys) ? YANG_K_SYSTEM : YANG_K_USER; neworder = (new->ordersys) ? YANG_K_SYSTEM : YANG_K_USER; if (str_field_changed(YANG_K_ORDERED_BY, oldorder, neworder, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (status_field_changed(YANG_K_STATUS, old->status, new->status, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, isrev, &cdb)) { output_cdb_line(cp, &cdb); } } /* output_leaf_list_diff */ /******************************************************************** * FUNCTION list_changed * * Check if a list definition changed * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 list_changed (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_list_t *old, *new; old = oldobj->def.list; new = newobj->def.list; if (mustQ_changed(&old->mustQ, &new->mustQ)) { return 1; } if (str_field_changed(YANG_K_KEY, obj_get_keystr(oldobj), obj_get_keystr(newobj), FALSE, NULL)) { return 1; } if (uniqueQ_changed(&old->uniqueQ, &new->uniqueQ)) { return 1; } if (bool_field_changed(YANG_K_CONFIG, (oldobj->flags & OBJ_FL_CONFIG), (newobj->flags & OBJ_FL_CONFIG), FALSE, NULL)) { return 1; } if (bool_field_changed(YANG_K_MIN_ELEMENTS, old->minset, new->minset, FALSE, NULL)) { return 1; } if (new->minelems != old->minelems) { return 1; } if (bool_field_changed(YANG_K_MAX_ELEMENTS, old->maxset, new->maxset, FALSE, NULL)) { return 1; } if (new->maxelems != old->maxelems) { return 1; } if (old->ordersys != new->ordersys) { return 1; } if (status_field_changed(YANG_K_STATUS, old->status, new->status, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, FALSE, NULL)) { return 1; } if (typedefQ_changed(cp, old->typedefQ, new->typedefQ)) { return 1; } if (groupingQ_changed(cp, old->groupingQ, new->groupingQ)) { return 1; } if (datadefQ_changed(cp, old->datadefQ, new->datadefQ)) { return 1; } return 0; } /* list_changed */ /******************************************************************** * FUNCTION output_list_diff * * Output the differences for a list definition * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * *********************************************************************/ static void output_list_diff (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_list_t *old, *new; yangdiff_cdb_t cdb; char oldnum[NCX_MAX_NUMLEN]; char newnum[NCX_MAX_NUMLEN]; const xmlChar *oldn, *newn; const xmlChar *oldorder, *neworder; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; old = oldobj->def.list; new = newobj->def.list; output_mustQ_diff(cp, &old->mustQ, &new->mustQ); if (str_field_changed(YANG_K_KEY, obj_get_keystr(oldobj), obj_get_keystr(newobj), isrev, &cdb)) { output_cdb_line(cp, &cdb); } output_uniqueQ_diff(cp, &old->uniqueQ, &new->uniqueQ); if (bool_field_changed(YANG_K_CONFIG, (oldobj->flags & OBJ_FL_CONFIG), (newobj->flags & OBJ_FL_CONFIG), isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (old->minset) { sprintf(oldnum, "%u", old->minelems); oldn = (const xmlChar *)oldnum; } else { oldn = NULL; } if (new->minset) { sprintf(newnum, "%u", new->minelems); newn = (const xmlChar *)newnum; } else { newn = NULL; } if (str_field_changed(YANG_K_MIN_ELEMENTS, oldn, newn, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (old->maxset) { sprintf(oldnum, "%u", old->maxelems); oldn = (const xmlChar *)oldnum; } else { oldn = NULL; } if (new->maxset) { sprintf(newnum, "%u", new->maxelems); newn = (const xmlChar *)newnum; } else { newn = NULL; } if (str_field_changed(YANG_K_MAX_ELEMENTS, oldn, newn, isrev, &cdb)) { output_cdb_line(cp, &cdb); } oldorder = (old->ordersys) ? YANG_K_SYSTEM : YANG_K_USER; neworder = (new->ordersys) ? YANG_K_SYSTEM : YANG_K_USER; if (str_field_changed(YANG_K_ORDERED_BY, oldorder, neworder, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (status_field_changed(YANG_K_STATUS, old->status, new->status, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, isrev, &cdb)) { output_cdb_line(cp, &cdb); } output_typedefQ_diff(cp, old->typedefQ, new->typedefQ); output_groupingQ_diff(cp, old->groupingQ, new->groupingQ); output_datadefQ_diff(cp, old->datadefQ, new->datadefQ); } /* output_list_diff */ /******************************************************************** * FUNCTION choice_changed * * Check if a choice definition changed * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 choice_changed (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_choice_t *old, *new; old = oldobj->def.choic; new = newobj->def.choic; if (str_field_changed(YANG_K_DEFAULT, old->defval, new->defval, FALSE, NULL)) { return 1; } if (bool_field_changed(YANG_K_MANDATORY, (oldobj->flags & OBJ_FL_MANDATORY), (newobj->flags & OBJ_FL_MANDATORY), FALSE, NULL)) { return 1; } if (status_field_changed(YANG_K_STATUS, old->status, new->status, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, FALSE, NULL)) { return 1; } if (datadefQ_changed(cp, old->caseQ, new->caseQ)) { return 1; } return 0; } /* choice_changed */ /******************************************************************** * FUNCTION output_choice_diff * * Output the differences for a choice definition * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * *********************************************************************/ static void output_choice_diff (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_choice_t *old, *new; yangdiff_cdb_t cdb; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; old = oldobj->def.choic; new = newobj->def.choic; if (str_field_changed(YANG_K_DEFAULT, old->defval, new->defval, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (bool_field_changed(YANG_K_MANDATORY, (oldobj->flags & OBJ_FL_MANDATORY), (newobj->flags & OBJ_FL_MANDATORY), isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (status_field_changed(YANG_K_STATUS, old->status, new->status, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, isrev, &cdb)) { output_cdb_line(cp, &cdb); } output_datadefQ_diff(cp, old->caseQ, new->caseQ); } /* output_choice_diff */ /******************************************************************** * FUNCTION case_changed * * Check if a case definition changed * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 case_changed (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_case_t *old, *new; old = oldobj->def.cas; new = newobj->def.cas; if (status_field_changed(YANG_K_STATUS, old->status, new->status, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, FALSE, NULL)) { return 1; } if (datadefQ_changed(cp, old->datadefQ, new->datadefQ)) { return 1; } return 0; } /* case_changed */ /******************************************************************** * FUNCTION output_case_diff * * Output the differences for a case definition * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * *********************************************************************/ static void output_case_diff (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_case_t *old, *new; yangdiff_cdb_t cdb; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; old = oldobj->def.cas; new = newobj->def.cas; if (status_field_changed(YANG_K_STATUS, old->status, new->status, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, isrev, &cdb)) { output_cdb_line(cp, &cdb); } output_datadefQ_diff(cp, old->datadefQ, new->datadefQ); } /* output_case_diff */ /******************************************************************** * FUNCTION rpc_changed * * Check if a rpc definition changed * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 rpc_changed (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_rpc_t *old, *new; old = oldobj->def.rpc; new = newobj->def.rpc; if (status_field_changed(YANG_K_STATUS, old->status, new->status, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, FALSE, NULL)) { return 1; } if (typedefQ_changed(cp, &old->typedefQ, &new->typedefQ)) { return 1; } if (groupingQ_changed(cp, &old->groupingQ, &new->groupingQ)) { return 1; } if (datadefQ_changed(cp, &old->datadefQ, &new->datadefQ)) { return 1; } return 0; } /* rpc_changed */ /******************************************************************** * FUNCTION output_rpc_diff * * Output the differences for an RPC definition * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * *********************************************************************/ static void output_rpc_diff (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_rpc_t *old, *new; yangdiff_cdb_t cdb; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; old = oldobj->def.rpc; new = newobj->def.rpc; if (status_field_changed(YANG_K_STATUS, old->status, new->status, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, isrev, &cdb)) { output_cdb_line(cp, &cdb); } output_typedefQ_diff(cp, &old->typedefQ, &new->typedefQ); output_groupingQ_diff(cp, &old->groupingQ, &new->groupingQ); output_datadefQ_diff(cp, &old->datadefQ, &new->datadefQ); } /* output_rpc_diff */ /******************************************************************** * FUNCTION rpcio_changed * * Check if a rpc IO definition changed * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 rpcio_changed (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_rpcio_t *old, *new; old = oldobj->def.rpcio; new = newobj->def.rpcio; if (typedefQ_changed(cp, &old->typedefQ, &new->typedefQ)) { return 1; } if (groupingQ_changed(cp, &old->groupingQ, &new->groupingQ)) { return 1; } if (datadefQ_changed(cp, &old->datadefQ, &new->datadefQ)) { return 1; } return 0; } /* rpcio_changed */ /******************************************************************** * FUNCTION output_rpcio_diff * * Output the differences for an RPC IO definition * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * *********************************************************************/ static void output_rpcio_diff (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_rpcio_t *old, *new; old = oldobj->def.rpcio; new = newobj->def.rpcio; output_typedefQ_diff(cp, &old->typedefQ, &new->typedefQ); output_groupingQ_diff(cp, &old->groupingQ, &new->groupingQ); output_datadefQ_diff(cp, &old->datadefQ, &new->datadefQ); } /* output_rpcio_diff */ /******************************************************************** * FUNCTION notif_changed * * Check if a notification definition changed * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 notif_changed (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_notif_t *old, *new; old = oldobj->def.notif; new = newobj->def.notif; if (status_field_changed(YANG_K_STATUS, old->status, new->status, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, FALSE, NULL)) { return 1; } if (typedefQ_changed(cp, &old->typedefQ, &new->typedefQ)) { return 1; } if (groupingQ_changed(cp, &old->groupingQ, &new->groupingQ)) { return 1; } if (datadefQ_changed(cp, &old->datadefQ, &new->datadefQ)) { return 1; } return 0; } /* notif_changed */ /******************************************************************** * FUNCTION output_notif_diff * * Output the differences for a notification definition * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * *********************************************************************/ static void output_notif_diff (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { obj_notif_t *old, *new; yangdiff_cdb_t cdb; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; old = oldobj->def.notif; new = newobj->def.notif; if (status_field_changed(YANG_K_STATUS, old->status, new->status, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_DESCRIPTION, old->descr, new->descr, isrev, &cdb)) { output_cdb_line(cp, &cdb); } if (str_field_changed(YANG_K_REFERENCE, old->ref, new->ref, isrev, &cdb)) { output_cdb_line(cp, &cdb); } output_typedefQ_diff(cp, &old->typedefQ, &new->typedefQ); output_groupingQ_diff(cp, &old->groupingQ, &new->groupingQ); output_datadefQ_diff(cp, &old->datadefQ, &new->datadefQ); } /* output_notif_diff */ /******************************************************************** * FUNCTION object_changed * * Check if a object definition changed * * INPUTS: * cp == parameter block to use * oldobj == old obj_template_t to use * newobj == new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 object_changed (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { if (oldobj->objtype != newobj->objtype) { return 1; } if (iffeatureQ_changed(obj_get_mod_prefix(oldobj), &oldobj->iffeatureQ, &newobj->iffeatureQ)) { return 1; } switch (oldobj->objtype) { case OBJ_TYP_CONTAINER: return container_changed(cp, oldobj, newobj); case OBJ_TYP_ANYXML: return anyxml_changed(oldobj, newobj); case OBJ_TYP_LEAF: return leaf_changed(cp, oldobj, newobj); case OBJ_TYP_LEAF_LIST: return leaf_list_changed(cp, oldobj, newobj); case OBJ_TYP_LIST: return list_changed(cp, oldobj, newobj); case OBJ_TYP_CHOICE: return choice_changed(cp, oldobj, newobj); case OBJ_TYP_CASE: return case_changed(cp, oldobj, newobj); case OBJ_TYP_RPC: return rpc_changed(cp, oldobj, newobj); case OBJ_TYP_RPCIO: return rpcio_changed(cp, oldobj, newobj); case OBJ_TYP_NOTIF: return notif_changed(cp, oldobj, newobj); default: SET_ERROR(ERR_INTERNAL_VAL); return 0; } /*NOTREACHED*/ } /* object_changed */ /******************************************************************** * FUNCTION output_one_object_diff * * Output the differences report for one object definition * * INPUTS: * cp == parameter block to use * oldobj == old object * newobj == new object * *********************************************************************/ static void output_one_object_diff (yangdiff_diffparms_t *cp, obj_template_t *oldobj, obj_template_t *newobj) { output_mstart_line(cp, obj_get_typestr(oldobj), obj_get_name(oldobj), TRUE); if (cp->edifftype == YANGDIFF_DT_TERSE) { return; } indent_in(cp); if (oldobj->objtype != newobj->objtype) { output_diff(cp, (const xmlChar *)"object-type", obj_get_typestr(oldobj), obj_get_typestr(newobj), TRUE); } else { output_iffeatureQ_diff(cp, obj_get_mod_prefix(oldobj), &oldobj->iffeatureQ, &newobj->iffeatureQ); switch (oldobj->objtype) { case OBJ_TYP_CONTAINER: output_container_diff(cp, oldobj, newobj); break; case OBJ_TYP_LEAF: output_leaf_diff(cp, oldobj, newobj); break; case OBJ_TYP_ANYXML: output_anyxml_diff(cp, oldobj, newobj); break; case OBJ_TYP_LEAF_LIST: output_leaf_list_diff(cp, oldobj, newobj); break; case OBJ_TYP_LIST: output_list_diff(cp, oldobj, newobj); break; case OBJ_TYP_CHOICE: output_choice_diff(cp, oldobj, newobj); break; case OBJ_TYP_CASE: output_case_diff(cp, oldobj, newobj); break; case OBJ_TYP_RPC: output_rpc_diff(cp, oldobj, newobj); break; case OBJ_TYP_RPCIO: output_rpcio_diff(cp, oldobj, newobj); break; case OBJ_TYP_NOTIF: output_notif_diff(cp, oldobj, newobj); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } indent_out(cp); } /* output_one_object_diff */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION output_datadefQ_diff * * Output the differences report for a Q of data definitions * Not always called for top-level objects; Can be called * for nested objects * * INPUTS: * cp == parameter block to use * oldQ == Q of old obj_template_t to use * newQ == Q of new obj_template_t to use * *********************************************************************/ void output_datadefQ_diff (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { obj_template_t *oldobj, *newobj; boolean anyout; anyout = FALSE; for (newobj = (obj_template_t *)dlq_firstEntry(newQ); newobj != NULL; newobj = (obj_template_t *)dlq_nextEntry(newobj)) { newobj->flags &= ~OBJ_FL_SEEN; } /* look through the old Q for matching entries in the new Q */ for (oldobj = (obj_template_t *)dlq_firstEntry(oldQ); oldobj != NULL; oldobj = (obj_template_t *)dlq_nextEntry(oldobj)) { if (obj_has_name(oldobj)) { newobj = obj_find_template(newQ, cp->newmod->name, obj_get_name(oldobj)); } else { continue; } if (newobj) { newobj->flags |= OBJ_FL_SEEN; if (oldobj->flags & OBJ_FL_SEEN) { if (oldobj->flags & OBJ_FL_DIFF) { output_one_object_diff(cp, oldobj, newobj); anyout = TRUE; } } else if (object_changed(cp, oldobj, newobj)) { oldobj->flags |= (OBJ_FL_SEEN | OBJ_FL_DIFF); output_one_object_diff(cp, oldobj, newobj); anyout = TRUE; } else { oldobj->flags |= OBJ_FL_SEEN; } } else { /* object was removed from the new module */ oldobj->flags |= OBJ_FL_SEEN; output_diff(cp, obj_get_typestr(oldobj), obj_get_name(oldobj), NULL, TRUE); anyout = TRUE; } } /* look for objects that were added in the new module */ for (newobj = (obj_template_t *)dlq_firstEntry(newQ); newobj != NULL; newobj = (obj_template_t *)dlq_nextEntry(newobj)) { if (!obj_has_name(newobj)) { continue; } if (!(newobj->flags & OBJ_FL_SEEN)) { /* this object was added in the new version */ output_diff(cp, obj_get_typestr(newobj), NULL, obj_get_name(newobj), TRUE); anyout = TRUE; } } /* check if the object order changed at all, but only if this * is not a top-level object. Use a little hack to test here */ if (!anyout && (oldQ != &cp->oldmod->datadefQ)) { oldobj = (obj_template_t *)dlq_firstEntry(oldQ); newobj = (obj_template_t *)dlq_firstEntry(newQ); while (oldobj && newobj) { if (obj_has_name(oldobj)) { if (!obj_is_match(oldobj, newobj)) { output_mstart_line(cp, (const xmlChar *) "child node order", NULL, FALSE); return; } } oldobj = (obj_template_t *)dlq_nextEntry(oldobj); newobj = (obj_template_t *)dlq_nextEntry(newobj); } } } /* output_datadefQ_diff */ /******************************************************************** * FUNCTION datadefQ_changed * * Check if a Q of data definitions changed * * INPUTS: * cp == parameter block to use * oldQ == Q of old obj_template_t to use * newQ == Q of new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ uint32 datadefQ_changed (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { obj_template_t *oldobj, *newobj; if (dlq_count(oldQ) != dlq_count(newQ)) { return 1; } for (newobj = (obj_template_t *)dlq_firstEntry(newQ); newobj != NULL; newobj = (obj_template_t *)dlq_nextEntry(newobj)) { newobj->flags &= ~OBJ_FL_SEEN; } /* look through the old Q for matching entries in the new Q */ for (oldobj = (obj_template_t *)dlq_firstEntry(oldQ); oldobj != NULL; oldobj = (obj_template_t *)dlq_nextEntry(oldobj)) { if (obj_is_hidden(oldobj)) { continue; } if (obj_has_name(oldobj)) { newobj = obj_find_template(newQ, cp->newmod->name, obj_get_name(oldobj)); } else { continue; } if (newobj) { if (!obj_is_hidden(newobj)) { if (object_changed(cp, oldobj, newobj)) { /* use the seen flag in the old tree to indicate * that a node has been visited and the 'diff' flag * to indicate a node has changed */ oldobj->flags |= (OBJ_FL_SEEN | OBJ_FL_DIFF); return 1; } else { newobj->flags |= OBJ_FL_SEEN; } } } else { return 1; } } /* look for objects that were added in the new module */ for (newobj = (obj_template_t *)dlq_firstEntry(newQ); newobj != NULL; newobj = (obj_template_t *)dlq_nextEntry(newobj)) { if (!obj_has_name(newobj) || obj_is_hidden(newobj)) { continue; } if (!(newobj->flags & OBJ_FL_SEEN)) { return 1; } } /* check if the object order changed at all, but only if this * is not a top-level object. Use a little hack to test here */ if (oldQ != &cp->oldmod->datadefQ) { oldobj = (obj_template_t *)dlq_firstEntry(oldQ); newobj = (obj_template_t *)dlq_firstEntry(newQ); while (oldobj && newobj) { if (obj_has_name(oldobj) && obj_has_name(newobj) && !(obj_is_hidden(oldobj) || obj_is_hidden(newobj))) { if (!obj_is_match(oldobj, newobj)) { return 1; } } oldobj = (obj_template_t *)dlq_nextEntry(oldobj); newobj = (obj_template_t *)dlq_nextEntry(newobj); } } return 0; } /* datadefQ_changed */ /* END yangdiff_obj.c */ yuma123_2.14/netconf/src/yangdiff/yangdiff_grp.h0000664000175000017500000000505414770023131021751 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangdiff_grp #define _H_yangdiff_grp /* FILE: yangdiff_grp.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Report differences related to YANG groupings ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 10-jul-08 abb Split out from yangdiff.c */ #ifndef _H_yangdiff #include "yangdiff.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION output_groupingQ_diff * * Output the differences report for a Q of grouping definitions * Not always called for top-level groupings; Can be called * for nested groupings * * INPUTS: * cp == parameter block to use * oldQ == Q of old grp_template_t to use * newQ == Q of new grp_template_t to use * *********************************************************************/ extern void output_groupingQ_diff (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ); /******************************************************************** * FUNCTION groupingQ_changed * * Check if a (nested) Q of groupings changed * * INPUTS: * cp == parameter block to use * oldQ == Q of old grp_template_t to use * newQ == Q of new grp_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ extern uint32 groupingQ_changed (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangdiff_grp */ yuma123_2.14/netconf/src/yangdiff/yangdiff_typ.c0000664000175000017500000012326014770023131021770 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangdiff_typ.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 10-jul-08 abb split out from yangdiff.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangdiff #include "yangdiff.h" #endif #ifndef _H_yangdiff_typ #include "yangdiff_typ.h" #endif #ifndef _H_yangdiff_util #include "yangdiff_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define YANGDIFF_TYP_DEBUG 1 /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION output_range_diff * * Output the differences report for one range or length * clause and any sub-clauses, * within a leaf, leaf-list, or typedef definition * * Only the top-level typedef is checked, instead of * following the type chain looking for the first rangedef. * This prevents chain mis-alignment false positives * * Only the top-level rangedef is relevant for validation, * since each refinement must be a valid subset of its parent * range definition * * INPUTS: * cp == parameter block to use * keyword == range or length keyword * oldtypdef == old internal typedef * newtypdef == new internal typedef * *********************************************************************/ static void output_range_diff (yangdiff_diffparms_t *cp, const xmlChar *keyword, typ_def_t *oldtypdef, typ_def_t *newtypdef) { typ_range_t *oldrange, *newrange; const xmlChar *oldstr, *newstr; ncx_errinfo_t *olderr, *newerr; yangdiff_cdb_t cdb; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; oldrange = typ_get_range_con(oldtypdef); oldstr = oldrange ? oldrange->rangestr : NULL; newrange = typ_get_range_con(newtypdef); newstr = newrange ? newrange->rangestr : NULL; olderr = typ_get_range_errinfo(oldtypdef); newerr = typ_get_range_errinfo(newtypdef); if (str_field_changed(keyword, oldstr, newstr, isrev, &cdb)) { output_cdb_line(cp, &cdb); indent_in(cp); output_errinfo_diff(cp, olderr, newerr); indent_out(cp); } } /* output_range_diff */ /******************************************************************** * FUNCTION output_pattern_diff * * Output the differences report for one pattern clause and * any of its sub-clauses, within a leaf, leaf-list, or * typedef definition * * INPUTS: * cp == parameter block to use * oldpat == old internal pattern * newpat == new internal pattern * patnum == index number of pattern in the Q [1 .. N] * *********************************************************************/ static void output_pattern_diff (yangdiff_diffparms_t *cp, const typ_pattern_t *oldpat, const typ_pattern_t *newpat, uint32 patnum) { const ncx_errinfo_t *olderr, *newerr; const xmlChar *oldstr, *newstr; xmlChar buff[NCX_MAX_NUMLEN+16], *p; oldstr = (oldpat) ? oldpat->pat_str : NULL; newstr = (newpat) ? newpat->pat_str : NULL; olderr = (oldpat) ? &oldpat->pat_errinfo : NULL; newerr = (newpat) ? &newpat->pat_errinfo : NULL; p = buff; p += xml_strcpy(p, YANG_K_PATTERN); *p++ = ' '; *p++ = '['; p += (uint32)sprintf((char *)p, "%u", patnum); *p++ = ']'; *p = 0; if (!oldstr && newstr) { /* pattern added in new revision */ output_diff(cp, buff, oldstr, newstr, FALSE); indent_in(cp); output_errinfo_diff(cp, olderr, newerr); indent_out(cp); } else if (oldstr && !newstr) { /* pattern removed in new revision */ output_diff(cp, buff, oldstr, newstr, FALSE); indent_in(cp); output_errinfo_diff(cp, olderr, newerr); indent_out(cp); } else if (oldstr && newstr) { /* check if pattern changed */ if (xml_strcmp(oldstr, newstr)) { output_diff(cp, buff, oldstr, newstr, FALSE); indent_in(cp); output_errinfo_diff(cp, olderr, newerr); indent_out(cp); } else if (errinfo_changed(olderr, newerr)) { output_mstart_line(cp, buff, oldstr, FALSE); indent_in(cp); output_errinfo_diff(cp, olderr, newerr); indent_out(cp); } } } /* output_pattern_diff */ /******************************************************************** * FUNCTION output_patternQ_diff * * Output the differences report for the Q of pattern clauses * * INPUTS: * cp == parameter block to use * oldtypdef == old internal typedef * newtypdef == new internal typedef * *********************************************************************/ static void output_patternQ_diff (yangdiff_diffparms_t *cp, const typ_def_t *oldtypdef, const typ_def_t *newtypdef) { const typ_pattern_t *oldpat, *newpat; uint32 cnt; oldpat = typ_get_first_cpattern(oldtypdef); newpat = typ_get_first_cpattern(newtypdef); cnt = 1; while (oldpat || newpat) { output_pattern_diff(cp, oldpat, newpat, cnt); oldpat = (oldpat) ? typ_get_next_cpattern(oldpat) : NULL; newpat = (newpat) ? typ_get_next_cpattern(newpat) : NULL; cnt++; } } /* output_patternQ_diff */ /******************************************************************** * FUNCTION output_eb_type_diff * * Output the differences report for one enum or bits sub-clauses * within a leaf, leaf-list, or typedef definition * * INPUTS: * cp == parameter block to use * name == type name to use * oldtypdef == old internal typedef * newtypdef == new internal typedef * isbits == TRUE if NCX_BT_BITS * FALSE if NCX_BT_ENUM *********************************************************************/ static void output_eb_type_diff (yangdiff_diffparms_t *cp, typ_def_t *oldtypdef, typ_def_t *newtypdef, boolean isbits) { typ_enum_t *oldval, *newval; dlq_hdr_t *oldQ, *newQ; typ_def_t *oldbase, *newbase; const xmlChar *kw; xmlChar oldnum[NCX_MAX_NUMLEN]; xmlChar newnum[NCX_MAX_NUMLEN]; yangdiff_cdb_t cdb[4]; boolean isrev; uint32 chcount, i; oldbase = typ_get_base_typdef(oldtypdef); newbase = typ_get_base_typdef(newtypdef); oldQ = &oldbase->def.simple.valQ; newQ = &newbase->def.simple.valQ; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; kw = isbits ? YANG_K_BIT : YANG_K_ENUM; /* clear the seen flag to find new enums/bits */ for (newval = (typ_enum_t *)dlq_firstEntry(newQ); newval != NULL; newval = (typ_enum_t *)dlq_nextEntry(newval)) { newval->flags &= ~TYP_FL_SEEN; } /* check for matching entries */ for (oldval = (typ_enum_t *)dlq_firstEntry(oldQ); oldval != NULL; oldval = (typ_enum_t *)dlq_nextEntry(oldval)) { chcount = 0; newval = typ_find_enumdef(newQ, oldval->name); if (newval) { if (isbits) { sprintf((char *)oldnum, "%u", oldval->pos); sprintf((char *)newnum, "%u", newval->pos); chcount += str_field_changed(YANG_K_POSITION, oldnum, newnum, isrev, &cdb[0]); } else { sprintf((char *)oldnum, "%d", oldval->val); sprintf((char *)newnum, "%d", newval->val); chcount += str_field_changed(YANG_K_VALUE, oldnum, newnum, isrev, &cdb[0]); } chcount += status_field_changed(YANG_K_STATUS, oldval->status, newval->status, isrev, &cdb[1]); chcount += str_field_changed(YANG_K_DESCRIPTION, oldval->descr, newval->descr, isrev, &cdb[2]); chcount += str_field_changed(YANG_K_REFERENCE, oldval->ref, newval->ref, isrev, &cdb[3]); newval->flags |= TYP_FL_SEEN; if (chcount) { output_mstart_line(cp, kw, oldval->name, isbits); if (cp->edifftype != YANGDIFF_DT_TERSE) { indent_in(cp); for (i=0; i<4; i++) { output_cdb_line(cp, &cdb[i]); } indent_out(cp); } } } else { /* removed name in new version */ output_diff(cp, kw, oldval->name, NULL, isbits); } } /* check for new entries */ for (newval = (typ_enum_t *)dlq_firstEntry(newQ); newval != NULL; newval = (typ_enum_t *)dlq_nextEntry(newval)) { if ((newval->flags & TYP_FL_SEEN) == 0) { output_diff(cp, kw, NULL, newval->name, isbits); } } } /* output_eb_type_diff */ /******************************************************************** * FUNCTION unQ_changed * * Check if the union Q in the typdef has changed * * INPUTS: * cp == comparison parameter block to use * oldQ == Q of old typ_unionnode_t structs to check * newQ == Q of new typ_unionnode_t structs to check * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 unQ_changed (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { typ_unionnode_t *oldval, *newval; typ_def_t *olddef, *newdef; boolean done; if (dlq_count(oldQ) != dlq_count(newQ)) { return 1; } /* clear the seen flag to be safe */ for (newval = (typ_unionnode_t *)dlq_firstEntry(newQ); newval != NULL; newval = (typ_unionnode_t *)dlq_nextEntry(newval)) { newval->seen = FALSE; } oldval = (typ_unionnode_t *)dlq_firstEntry(oldQ); newval = (typ_unionnode_t *)dlq_firstEntry(newQ); /* check for matching entries */ done = FALSE; while (!done) { if (!oldval && !newval) { return 0; } else if (!oldval && newval) { return 1; } else if (oldval && !newval) { return 1; } /* else both old val and new val exist */ olddef = typ_get_unionnode_ptr(oldval); newdef = typ_get_unionnode_ptr(newval); if (type_changed(cp, olddef, newdef)) { return 1; } newval->seen = TRUE; oldval = (typ_unionnode_t *)dlq_nextEntry(oldval); newval = (typ_unionnode_t *)dlq_nextEntry(newval); } /* check for new entries */ for (newval = (typ_unionnode_t *)dlq_firstEntry(newQ); newval != NULL; newval = (typ_unionnode_t *)dlq_nextEntry(newval)) { if (!newval->seen) { return 1; } } return 0; } /* unQ_changed */ /******************************************************************** * FUNCTION unQ_match * * Find the specified type in the unionnode Q * * INPUTS: * cp == comparison parameters to use * oldval == old typ_unionnode_t struct to check * newQ == Q of new typ_unionnode_t structs to check * idx == address of return index * * OUTPUTS: * *idx == index of the unionnode entry found [0..N-1 numbering] * * RETURNS: * pointer to the found entry * NULL if not found *********************************************************************/ static typ_unionnode_t * unQ_match (yangdiff_diffparms_t *cp, typ_unionnode_t *oldval, dlq_hdr_t *newQ, uint32 *idx) { typ_unionnode_t *newval; typ_def_t *olddef, *newdef; *idx = 0; olddef = typ_get_unionnode_ptr(oldval); for (newval = (typ_unionnode_t *)dlq_firstEntry(newQ); newval != NULL; newval = (typ_unionnode_t *)dlq_nextEntry(newval)) { if (!newval->seen) { newdef = typ_get_unionnode_ptr(newval); if (!type_changed(cp, olddef, newdef)) { return newval; } } (*idx)++; } return NULL; } /* unQ_match */ /******************************************************************** * FUNCTION unQ_match_id * * Find the specified type by position in the unionnode Q * * INPUTS: * oldid == index of typ_unionnode_t struct to match * newQ == Q of new typ_unionnode_t structs to check * * RETURNS: * pointer to the found entry * NULL if not found *********************************************************************/ static typ_unionnode_t * unQ_match_id (uint32 oldid, dlq_hdr_t *newQ) { typ_unionnode_t *newval; uint32 idx; idx = 0; for (newval = (typ_unionnode_t *)dlq_firstEntry(newQ); newval != NULL; newval = (typ_unionnode_t *)dlq_nextEntry(newval)) { if (idx==oldid) { return (newval->seen) ? NULL : newval; } else if (idx>oldid) { return NULL; } idx++; } return NULL; } /* unQ_match_id */ /******************************************************************** * FUNCTION output_union_diff * * Output the differences report for one union sub-clauses * within a leaf, leaf-list, or typedef definition * * INPUTS: * cp == parameter block to use * oldtypdef == old internal typedef * newtypdef == new internal typedef *********************************************************************/ static void output_union_diff (yangdiff_diffparms_t *cp, typ_def_t *oldtypdef, typ_def_t *newtypdef) { typ_unionnode_t *oldval, *newval, *curnew; dlq_hdr_t *oldQ, *newQ; typ_def_t *olddef, *newdef; uint32 oldid, newid; char oldnum[NCX_MAX_NUMLEN]; char newnum[NCX_MAX_NUMLEN]; olddef = typ_get_base_typdef(oldtypdef); newdef = typ_get_base_typdef(newtypdef); oldQ = &olddef->def.simple.unionQ, newQ = &newdef->def.simple.unionQ; if (!unQ_changed(cp, oldQ, newQ)) { return; } oldid = 0; newid = 0; /* clear the seen flag to be safe */ for (newval = (typ_unionnode_t *)dlq_firstEntry(newQ); newval != NULL; newval = (typ_unionnode_t *)dlq_nextEntry(newval)) { newval->seen = FALSE; } /* start the diff output */ output_mstart_line(cp, YANG_K_UNION, NULL, FALSE); if (cp->edifftype == YANGDIFF_DT_TERSE) { return; } indent_in(cp); /* check for matching unionnode entries */ for (oldval = (typ_unionnode_t *)dlq_firstEntry(oldQ); oldval != NULL; oldval = (typ_unionnode_t *)dlq_nextEntry(oldval), oldid++) { curnew = NULL; sprintf(oldnum, "[%u]", oldid); olddef = typ_get_unionnode_ptr(oldval); /* first try the corresponded entry if available */ newval = unQ_match_id(oldid, newQ); if (newval) { curnew = newval; newid = oldid; sprintf(newnum, "[%u]", newid); newdef = typ_get_unionnode_ptr(newval); /* if the corresponding entry did not change * then this is a match and continue to next type */ if (!type_changed(cp, olddef, newdef)) { newval->seen = TRUE; continue; } } /* did not match the corresponding entry, * so see if the typdef moved in the new union */ newval = unQ_match(cp, oldval, newQ, &newid); if (newval) { newval->seen = TRUE; if (oldid != newid) { sprintf(newnum, "[%u]", newid); /* old union node was moved in new version */ output_diff(cp, YANG_K_TYPE, (const xmlChar *)oldnum, (const xmlChar *)newnum, TRUE); } } else if (curnew) { /* type node was changed in the new union */ curnew->seen = TRUE; newdef = typ_get_unionnode_ptr(curnew); sprintf(newnum, "[%u]", oldid); output_one_type_diff(cp, olddef, newdef); } else { /* old union node was removed in new version */ output_diff(cp, YANG_K_TYPE, (const xmlChar *)oldnum, NULL, TRUE); } } indent_out(cp); /* check for new entries */ newid = 0; for (newval = (typ_unionnode_t *)dlq_firstEntry(newQ); newval != NULL; newval = (typ_unionnode_t *)dlq_nextEntry(newval)) { if (!newval->seen) { sprintf(newnum, "[%u]", newid); output_diff(cp, YANG_K_TYPE, NULL, (const xmlChar *)newnum, TRUE); } newid++; } } /* output_union_diff */ /******************************************************************** * FUNCTION pattern_changed * * Check if the pattern-stmt changed at all * * INPUTS: * oldpat == old pattern struct to check * newpat == new pattern struct to check * * RETURNS: * 1 if struct changed * 0 if struct not changed *********************************************************************/ static uint32 pattern_changed (const typ_pattern_t *oldpat, const typ_pattern_t *newpat) { const ncx_errinfo_t *olderr, *newerr; /* check pattern string is the same */ if ((!oldpat && newpat) || (oldpat && !newpat)) { return 1; } else if (oldpat && newpat) { if (xml_strcmp(oldpat->pat_str, newpat->pat_str)) { return 1; } } else { return 0; /* no pattern defined */ } olderr = (oldpat) ? &oldpat->pat_errinfo : NULL; newerr = (newpat) ? &newpat->pat_errinfo : NULL; /* pattern string is the same, check error stuff */ return errinfo_changed(olderr, newerr); } /* pattern_changed */ /******************************************************************** * FUNCTION patternQ_changed * * Check if the Q of pattern-stmts changed at all * * INPUTS: * oldtypdef == old type def struct to check * newtypdef == new type def struct to check * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 patternQ_changed (const typ_def_t *oldtypdef, const typ_def_t *newtypdef) { const typ_pattern_t *oldpat, *newpat; uint32 oldpatcnt, newpatcnt; oldpatcnt = typ_get_pattern_count(oldtypdef); newpatcnt = typ_get_pattern_count(newtypdef); if (oldpatcnt != newpatcnt) { return 1; } if (!oldpatcnt) { return 0; } oldpat = typ_get_first_cpattern(oldtypdef); newpat = typ_get_first_cpattern(newtypdef); while (oldpat && newpat) { if (pattern_changed(oldpat, newpat)) { return 1; } oldpat = typ_get_next_cpattern(oldpat); newpat = typ_get_next_cpattern(newpat); } return 0; } /* patternQ_changed */ /******************************************************************** * FUNCTION range_changed * * Check if the range-stmt or length-stmt changed at all * * INPUTS: * oldtypdef == old type def struct to check * newtypdef == new type def struct to check * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 range_changed (typ_def_t *oldtypdef, typ_def_t *newtypdef) { typ_range_t *oldr, *newr; const xmlChar *oldstr, *newstr; ncx_errinfo_t *olderr, *newerr; oldr = typ_get_range_con(oldtypdef); oldstr = (oldr) ? oldr->rangestr : NULL; newr = typ_get_range_con(newtypdef); newstr = (newr) ? newr->rangestr : NULL; /* check range string is the same */ if (str_field_changed(YANG_K_RANGE, oldstr, newstr, FALSE, NULL)) { return 1; } /* range string is the same, check error stuff */ olderr = typ_get_range_errinfo(oldtypdef); newerr = typ_get_range_errinfo(newtypdef); return errinfo_changed(olderr, newerr); } /* range_changed */ /******************************************************************** * FUNCTION ebQ_changed * * Check if the enum/bits Q in the typdef has changed, used for * enums and bits * * INPUTS: * oldQ == Q of old typ_enum_t structs to check * newQ == Q of new typ_enum_t structs to check * isbits == TRUE if checking for a bits datatype * FALSE if checking for an enum datatype * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ static uint32 ebQ_changed (dlq_hdr_t *oldQ, dlq_hdr_t *newQ, boolean isbits) { typ_enum_t *oldval, *newval; if (dlq_count(oldQ) != dlq_count(newQ)) { return 1; } /* clear the seen flag to be safe */ for (newval = (typ_enum_t *)dlq_firstEntry(newQ); newval != NULL; newval = (typ_enum_t *)dlq_nextEntry(newval)) { newval->flags &= ~TYP_FL_SEEN; } /* check for matching entries */ for (oldval = (typ_enum_t *)dlq_firstEntry(oldQ); oldval != NULL; oldval = (typ_enum_t *)dlq_nextEntry(oldval)) { newval = typ_find_enumdef(newQ, oldval->name); if (newval) { if (isbits) { if (oldval->pos != newval->pos) { return 1; } } else { if (oldval->val != newval->val) { return 1; } } if (str_field_changed(YANG_K_DESCRIPTION, oldval->descr, newval->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, oldval->ref, newval->ref, FALSE, NULL)) { return 1; } if (status_field_changed(YANG_K_STATUS, oldval->status, newval->status, FALSE, NULL)) { return 1; } /* mark new enum as used */ newval->flags |= TYP_FL_SEEN; } else { /* removed name in new version */ return 1; } } /* check for new entries */ for (newval = (typ_enum_t *)dlq_firstEntry(newQ); newval != NULL; newval = (typ_enum_t *)dlq_nextEntry(newval)) { if ((newval->flags & TYP_FL_SEEN) == 0) { return 1; } } return 0; } /* ebQ_changed */ /******************************************************************** * FUNCTION output_one_typedef_diff * * Output the differences report for one typedef definition * * INPUTS: * cp == parameter block to use * oldtyp == old typedef * newtyp == new typedef * *********************************************************************/ static void output_one_typedef_diff (yangdiff_diffparms_t *cp, typ_template_t *oldtyp, typ_template_t *newtyp) { yangdiff_cdb_t typcdb[5]; uint32 changecnt, i; boolean isrev, tchanged; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; tchanged = FALSE; changecnt = 0; if (type_changed(cp, &oldtyp->typdef, &newtyp->typdef)) { tchanged = TRUE; changecnt++; } changecnt += str_field_changed(YANG_K_UNITS, oldtyp->units, newtyp->units, isrev, &typcdb[0]); changecnt += str_field_changed(YANG_K_DEFAULT, oldtyp->defval, newtyp->defval, isrev, &typcdb[1]); changecnt += status_field_changed(YANG_K_STATUS, oldtyp->status, newtyp->status, isrev, &typcdb[2]); changecnt += str_field_changed(YANG_K_DESCRIPTION, oldtyp->descr, newtyp->descr, isrev, &typcdb[3]); changecnt += str_field_changed(YANG_K_REFERENCE, oldtyp->ref, newtyp->ref, isrev, &typcdb[4]); if (changecnt == 0) { return; } /* generate the diff output, based on the requested format */ output_mstart_line(cp, YANG_K_TYPEDEF, oldtyp->name, TRUE); if (cp->edifftype == YANGDIFF_DT_TERSE) { return; } indent_in(cp); for (i=0; i<5; i++) { if (typcdb[i].changed) { output_cdb_line(cp, &typcdb[i]); } } if (tchanged) { output_one_type_diff(cp, &oldtyp->typdef, &newtyp->typdef); } indent_out(cp); } /* output_one_typedef_diff */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION type_changed * * Check if the type clause and sub-clauses changed at all * * INPUTS: * cp == compare parameters to use * oldtypdef == old type def struct to check * newtypdef == new type def struct to check * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ uint32 type_changed (yangdiff_diffparms_t *cp, typ_def_t *oldtypdef, typ_def_t *newtypdef) { typ_def_t *oldtest, *newtest; const xmlChar *oldpath, *newpath; ncx_btype_t oldbtyp, newbtyp; #ifdef DEBUG if (!oldtypdef || !newtypdef) { SET_ERROR(ERR_INTERNAL_PTR); return 1; } #endif if (oldtypdef->tclass != newtypdef->tclass) { return 1; } if (prefix_field_changed(cp->oldmod, cp->newmod, oldtypdef->prefix, newtypdef->prefix)) { return 1; } if (str_field_changed(NULL, oldtypdef->typenamestr, newtypdef->typenamestr, FALSE, NULL)) { return 1; } switch (oldtypdef->tclass) { case NCX_CL_BASE: return (uint32)((oldtypdef->def.base == newtypdef->def.base) ? 0 : 1); case NCX_CL_SIMPLE: oldbtyp = typ_get_basetype(oldtypdef); newbtyp = typ_get_basetype(newtypdef); if (oldbtyp != newbtyp) { return 1; } if (oldbtyp == NCX_BT_LEAFREF) { oldpath = typ_get_leafref_path(oldtypdef); newpath = typ_get_leafref_path(newtypdef); return str_field_changed(YANG_K_PATH, oldpath, newpath, FALSE, NULL); } else if (typ_is_string(oldbtyp)) { if (patternQ_changed(oldtypdef, newtypdef)) { return 1; } if (range_changed(oldtypdef, newtypdef)) { return 1; } } else if (typ_is_number(oldbtyp)) { if (range_changed(oldtypdef, newtypdef)) { return 1; } } else { switch (oldbtyp) { case NCX_BT_BITS: return ebQ_changed(&oldtypdef->def.simple.valQ, &newtypdef->def.simple.valQ, TRUE); case NCX_BT_ENUM: return ebQ_changed(&oldtypdef->def.simple.valQ, &newtypdef->def.simple.valQ, FALSE); case NCX_BT_UNION: return unQ_changed(cp, &oldtypdef->def.simple.unionQ, &newtypdef->def.simple.unionQ); default: ; } } return 0; case NCX_CL_COMPLEX: /* not supported for NCX complex types!!! */ return 0; case NCX_CL_NAMED: oldtest = typ_get_new_named(oldtypdef); newtest = typ_get_new_named(newtypdef); if ((!oldtest && newtest) || (oldtest && !newtest)) { return 1; } else if (oldtest && newtest) { if (type_changed(cp, oldtest, newtest)) { return 1; } } return 0; /*** do not look deep into named typed *** add switch to check this *** type_changed(cp, &oldtypdef->def.named.typ->typdef, *** &newtypdef->def.named.typ->typdef); ***/ case NCX_CL_REF: return type_changed(cp, oldtypdef->def.ref.typdef, newtypdef->def.ref.typdef); default: SET_ERROR(ERR_INTERNAL_VAL); return 0; } /*NOTREACHED*/ } /* type_changed */ /******************************************************************** * FUNCTION typedef_changed * * Check if a (nested) typedef changed * * INPUTS: * cp == parameter block to use * oldtyp == old typ_template_t to use * newtyp == new typ_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ uint32 typedef_changed (yangdiff_diffparms_t *cp, typ_template_t *oldtyp, typ_template_t *newtyp) { if (type_changed(cp, &oldtyp->typdef, &newtyp->typdef)) { return 1; } if (str_field_changed(YANG_K_UNITS, oldtyp->units, newtyp->units, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_DEFAULT, oldtyp->defval, newtyp->defval, FALSE, NULL)) { return 1; } if (status_field_changed(YANG_K_STATUS, oldtyp->status, newtyp->status, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_DESCRIPTION, oldtyp->descr, newtyp->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, oldtyp->ref, newtyp->ref, FALSE, NULL)) { return 1; } return 0; } /* typedef_changed */ /******************************************************************** * FUNCTION typedefQ_changed * * Check if a (nested) Q of typedefs changed * * INPUTS: * cp == parameter block to use * oldQ == Q of old typ_template_t to use * newQ == Q of new typ_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ uint32 typedefQ_changed (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { typ_template_t *oldtyp, *newtyp; if (dlq_count(oldQ) != dlq_count(newQ)) { return 1; } /* borrowing the 'used' flag for marking matched typedefs * first set all these flags to FALSE */ for (newtyp = (typ_template_t *)dlq_firstEntry(oldQ); newtyp != NULL; newtyp = (typ_template_t *)dlq_nextEntry(newtyp)) { newtyp->used = FALSE; } /* look through the old type Q for matching types in the new type Q */ for (oldtyp = (typ_template_t *)dlq_firstEntry(oldQ); oldtyp != NULL; oldtyp = (typ_template_t *)dlq_nextEntry(oldtyp)) { /* find this revision in the new module */ newtyp = ncx_find_type_que(newQ, oldtyp->name); if (newtyp) { if (typedef_changed(cp, oldtyp, newtyp)) { return 1; } } else { return 1; } } /* look for typedefs that were added in the new module */ for (newtyp = (typ_template_t *)dlq_firstEntry(newQ); newtyp != NULL; newtyp = (typ_template_t *)dlq_nextEntry(newtyp)) { if (!newtyp->used) { return 1; } } return 0; } /* typedefQ_changed */ /******************************************************************** * FUNCTION output_typedefQ_diff * * Output the differences report for a Q of typedef definitions * Not always called for top-level typedefs; Can be called * for nested typedefs * * INPUTS: * cp == parameter block to use * oldQ == Q of old typ_template_t to use * newQ == Q of new typ_template_t to use * *********************************************************************/ void output_typedefQ_diff (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { typ_template_t *oldtyp, *newtyp; /* borrowing the 'used' flag for marking matched typedefs * first set all these flags to FALSE */ for (newtyp = (typ_template_t *)dlq_firstEntry(newQ); newtyp != NULL; newtyp = (typ_template_t *)dlq_nextEntry(newtyp)) { newtyp->used = FALSE; } /* look through the old type Q for matching types in the new type Q */ for (oldtyp = (typ_template_t *)dlq_firstEntry(oldQ); oldtyp != NULL; oldtyp = (typ_template_t *)dlq_nextEntry(oldtyp)) { /* find this revision in the new module */ newtyp = ncx_find_type_que(newQ, oldtyp->name); if (newtyp) { output_one_typedef_diff(cp, oldtyp, newtyp); newtyp->used = TRUE; } else { /* typedef was removed from the new module */ output_diff(cp, YANG_K_TYPEDEF, oldtyp->name, NULL, TRUE); } } /* look for typedefs that were added in the new module */ for (newtyp = (typ_template_t *)dlq_firstEntry(newQ); newtyp != NULL; newtyp = (typ_template_t *)dlq_nextEntry(newtyp)) { if (!newtyp->used) { /* this typedef was added in the new version */ output_diff(cp, YANG_K_TYPEDEF, NULL, newtyp->name, TRUE); } } } /* output_typedefQ_diff */ /******************************************************************** * FUNCTION output_one_type_diff * * Output the differences report for one type section * within a leaf, leaf-list, or typedef definition * * type_changed should be called first to determine * if the type actually changed. Otherwise a 'M typedef foo' * output line will result and be a false positive * * INPUTS: * cp == parameter block to use * oldtypdef == old internal typedef * newtypdef == new internal typedef * *********************************************************************/ void output_one_type_diff (yangdiff_diffparms_t *cp, typ_def_t *oldtypdef, typ_def_t *newtypdef) { xmlChar *p, *oldp, *newp; const xmlChar *oldname, *newname; const xmlChar *oldpath, *newpath; yangdiff_cdb_t typcdb[5]; ncx_btype_t oldbtyp, newbtyp; ncx_tclass_t oldclass, newclass; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; oldclass = oldtypdef->tclass; newclass = newtypdef->tclass; oldname = typ_get_name(oldtypdef); newname = typ_get_name(newtypdef); oldbtyp = typ_get_basetype(oldtypdef); newbtyp = typ_get_basetype(newtypdef); if (oldbtyp == NCX_BT_LEAFREF) { oldpath = typ_get_leafref_path(oldtypdef); } else { oldpath = NULL; } if (newbtyp==NCX_BT_LEAFREF) { newpath = typ_get_leafref_path(newtypdef); } else { newpath = NULL; } /* check if there is a module prefix involved * in the change. This may be a false positive * if the prefix simply changed * create YANG QNames for the type change record * use the scratch buffer cp->buff for both strings */ p = cp->buff; if (oldtypdef->prefix) { oldp = p; p += xml_strcpy(p, oldtypdef->prefix); *p++ = ':'; p += xml_strcpy(p, oldname); if (oldtypdef->tclass==NCX_CL_NAMED) { *p++ = ' '; *p++ = '('; p += xml_strcpy(p, (const xmlChar *) tk_get_btype_sym(oldbtyp)); *p++ = ')'; *p = 0; } p++; /* leave last NULL char in place */ oldname = oldp; } if (newtypdef->prefix) { newp = p; p += xml_strcpy(p, newtypdef->prefix); *p++ = ':'; p += xml_strcpy(p, newname); if (newtypdef->tclass==NCX_CL_NAMED) { *p++ = ' '; *p++ = '('; p += xml_strcpy(p, (const xmlChar *) tk_get_btype_sym(newbtyp)); *p++ = ')'; *p = 0; } newname = newp; } /* Print the type name change set only if it really * changed; otherwise force an M line to be printed */ if (str_field_changed(YANG_K_TYPE, oldname, newname, isrev, &typcdb[0])) { output_cdb_line(cp, &typcdb[0]); } else { output_mstart_line(cp, YANG_K_TYPE, NULL, FALSE); } /* check invalid change of builtin type */ if (oldbtyp != newbtyp) { /* need to figure out what type changes are really allowed */ if (!((typ_is_number(oldbtyp) && typ_is_number(newbtyp)) || (typ_is_string(oldbtyp) && typ_is_string(newbtyp)))) { /* ses_putstr(cp->scb, (const xmlChar *)" (invalid)"); */ return; } } /* check if that is all the data requested */ if (cp->edifftype == YANGDIFF_DT_TERSE) { return; } /* check corner-case, no sub-fields to compare */ if (oldclass==NCX_CL_BASE && newclass==NCX_CL_BASE) { /* type field is a plain builtin like 'int32;' */ return; } /* in all modes except 'normal', indent 1 level and * show the specific sub-clauses that have changed */ indent_in(cp); /* special case -- check leafref here */ if (oldpath || newpath) { output_diff(cp, YANG_K_PATH, oldpath, newpath, FALSE); } if (typ_is_number(oldbtyp)) { output_range_diff(cp, YANG_K_RANGE, oldtypdef, newtypdef); } else if (typ_is_string(oldbtyp)) { output_range_diff(cp, YANG_K_LENGTH, oldtypdef, newtypdef); output_patternQ_diff(cp, oldtypdef, newtypdef); } else { switch (oldbtyp) { case NCX_BT_ENUM: output_eb_type_diff(cp, oldtypdef, newtypdef, FALSE); break; case NCX_BT_BITS: output_eb_type_diff(cp, oldtypdef, newtypdef, TRUE); break; case NCX_BT_UNION: output_union_diff(cp, oldtypdef, newtypdef); break; default: ; } } indent_out(cp); } /* output_one_type_diff */ /* END yangdiff_typ.c */ yuma123_2.14/netconf/src/yangdiff/yangdiff.h0000664000175000017500000001230014770023131021071 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangdiff #define _H_yangdiff /* FILE: yangdiff.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Exports yangdiff.ncx conversion CLI parameter struct ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 01-mar-08 abb Begun; moved from ncx/ncxtypes.h */ #ifndef _H_help #include "help.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define YANGDIFF_PROGNAME (const xmlChar *)"yangdiff" #define YANGDIFF_BUFFSIZE 0x7fff #define YANGDIFF_DEF_MAXSIZE 20 #define YANGDIFF_DIFFTYPE_TERSE (const xmlChar *)"terse" #define YANGDIFF_DIFFTYPE_NORMAL (const xmlChar *)"normal" #define YANGDIFF_DIFFTYPE_REVISION (const xmlChar *)"revision" #define YANGDIFF_DEF_OUTPUT (const xmlChar *)"stdout" #define YANGDIFF_DEF_DIFFTYPE (const xmlChar *)"summary" #define YANGDIFF_DEF_DT YANGDIFF_DT_NORMAL #define YANGDIFF_DEF_CONFIG (const xmlChar *)"/etc/yuma/yangdiff.conf" #define YANGDIFF_DEF_FILENAME (const xmlChar *)"yangdiff.log" #define YANGDIFF_MOD (const xmlChar *)"yangdiff" #define YANGDIFF_CONTAINER (const xmlChar *)"yangdiff" #define YANGDIFF_PARM_CONFIG (const xmlChar *)"config" #define YANGDIFF_PARM_NEW (const xmlChar *)"new" #define YANGDIFF_PARM_NEWPATH (const xmlChar *)"newpath" #define YANGDIFF_PARM_OLD (const xmlChar *)"old" #define YANGDIFF_PARM_OLDPATH (const xmlChar *)"oldpath" #define YANGDIFF_PARM_DIFFTYPE (const xmlChar *)"difftype" #define YANGDIFF_PARM_OUTPUT (const xmlChar *)"output" #define YANGDIFF_PARM_INDENT (const xmlChar *)"indent" #define YANGDIFF_PARM_HEADER (const xmlChar *)"header" #define YANGDIFF_LINE (const xmlChar *)\ "\n===================================================================" #define FROM_STR (const xmlChar *)"from " #define TO_STR (const xmlChar *)"to " #define A_STR (const xmlChar *)"A " #define D_STR (const xmlChar *)"D " #define M_STR (const xmlChar *)"M " #define ADD_STR (const xmlChar *)"- Added " #define DEL_STR (const xmlChar *)"- Removed " #define MOD_STR (const xmlChar *)"- Changed " /******************************************************************** * * * T Y P E S * * * *********************************************************************/ typedef enum yangdiff_difftype_t_ { YANGDIFF_DT_NONE, YANGDIFF_DT_TERSE, YANGDIFF_DT_NORMAL, YANGDIFF_DT_REVISION } yangdiff_difftype_t; /* struct of yangdiff conversion parameters */ typedef struct yangdiff_diffparms_t_ { /* external parameters */ xmlChar *old; /* not malloced */ xmlChar *new; /* not malloced */ const xmlChar *oldpath; const xmlChar *modpath; const xmlChar *newpath; const xmlChar *output; const xmlChar *difftype; const xmlChar *logfilename; const xmlChar *config; boolean helpmode; help_mode_t helpsubmode; int32 indent; boolean logappend; log_debug_t log_level; boolean header; boolean subdirs; boolean versionmode; uint32 maxlen; /* internal vars */ dlq_hdr_t oldmodQ; dlq_hdr_t newmodQ; ses_cb_t *scb; xmlChar *curold; xmlChar *curnew; xmlChar *buff; ncx_module_t *oldmod; ncx_module_t *newmod; xmlChar *full_old; xmlChar *full_new; xmlChar *full_output; xmlChar *full_logfilename; uint32 bufflen; uint32 modbufflen; boolean firstdone; boolean old_isdir; boolean new_isdir; boolean output_isdir; yangdiff_difftype_t edifftype; int32 curindent; } yangdiff_diffparms_t; /* change description block */ typedef struct yangdiff_cdb_t_ { const xmlChar *fieldname; const xmlChar *chtyp; const xmlChar *oldval; const xmlChar *newval; const xmlChar *useval; boolean changed; } yangdiff_cdb_t; #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangdiff */ yuma123_2.14/netconf/src/yangdiff/yangdiff_obj.h0000664000175000017500000000502714770023131021733 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangdiff_obj #define _H_yangdiff_obj /* FILE: yangdiff_obj.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Report differences related to YANG objects ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 10-jul-08 abb Split out from yangdiff.c */ #ifndef _H_yangdiff #include "yangdiff.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION output_datadefQ_diff * * Output the differences report for a Q of data definitions * Not always called for top-level objects; Can be called * for nested objects * * INPUTS: * cp == parameter block to use * oldQ == Q of old obj_template_t to use * newQ == Q of new obj_template_t to use * *********************************************************************/ extern void output_datadefQ_diff (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ); /******************************************************************** * FUNCTION datadefQ_changed * * Check if a Q of data definitions changed * * INPUTS: * cp == parameter block to use * oldQ == Q of old obj_template_t to use * newQ == Q of new obj_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ extern uint32 datadefQ_changed (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangdiff_obj */ yuma123_2.14/netconf/src/yangdiff/yangdiff_typ.h0000664000175000017500000001051614770023131021774 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangdiff_typ #define _H_yangdiff_typ /* FILE: yangdiff_typ.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Report differences related to YANG typedefs and types ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 10-jul-08 abb Split out from yangdiff.c */ #ifndef _H_typ #include "typ.h" #endif #ifndef _H_yangdiff #include "yangdiff.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION type_changed * * Check if the type clause and sub-clauses changed at all * * INPUTS: * cp == compare parameters to use * oldtypdef == old type def struct to check * newtypdef == new type def struct to check * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ extern uint32 type_changed (yangdiff_diffparms_t *cp, typ_def_t *oldtypdef, typ_def_t *newtypdef); /******************************************************************** * FUNCTION typedef_changed * * Check if a (nested) typedef changed * * INPUTS: * cp == parameter block to use * oldtyp == old typ_template_t to use * newtyp == new typ_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ extern uint32 typedef_changed (yangdiff_diffparms_t *cp, typ_template_t *oldtyp, typ_template_t *newtyp); /******************************************************************** * FUNCTION typedefQ_changed * * Check if a (nested) Q of typedefs changed * * INPUTS: * cp == parameter block to use * oldQ == Q of old typ_template_t to use * newQ == Q of new typ_template_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ extern uint32 typedefQ_changed (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ); /******************************************************************** * FUNCTION output_typedefQ_diff * * Output the differences report for a Q of typedef definitions * Not always called for top-level typedefs; Can be called * for nested typedefs * * INPUTS: * cp == parameter block to use * oldQ == Q of old typ_template_t to use * newQ == Q of new typ_template_t to use * *********************************************************************/ extern void output_typedefQ_diff (yangdiff_diffparms_t *cp, dlq_hdr_t *oldQ, dlq_hdr_t *newQ); /******************************************************************** * FUNCTION output_one_type_diff * * Output the differences report for one type section * within a leaf, leaf-list, or typedef definition * * type_changed should be called first to determine * if the type actually changed. Otherwise a 'M typedef foo' * output line will result and be a false positive * * INPUTS: * cp == parameter block to use * oldtypdef == old internal typedef * newtypdef == new internal typedef * *********************************************************************/ extern void output_one_type_diff (yangdiff_diffparms_t *cp, typ_def_t *oldtypdef, typ_def_t *newtypdef); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangdiff_typ */ yuma123_2.14/netconf/src/yangdiff/Makefile.am0000664000175000017500000000166514770023131021201 0ustar vladimirvladimirbin_PROGRAMS = yangdiff yangdiff_SOURCES = \ $(top_srcdir)/netconf/src/yangdiff/yangdiff.c \ $(top_srcdir)/netconf/src/yangdiff/yangdiff_grp.c \ $(top_srcdir)/netconf/src/yangdiff/yangdiff_obj.c \ $(top_srcdir)/netconf/src/yangdiff/yangdiff_typ.c \ $(top_srcdir)/netconf/src/yangdiff/yangdiff_util.c yangdiff_CPPFLAGS = -I $(top_srcdir)/libtecla -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump -I$(top_srcdir)/netconf/src/yangrpc -I$(top_srcdir)/netconf/src/ydump yangdiff_LDFLAGS = $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la $(top_builddir)/netconf/src/yangrpc/libyangrpc.la -lz $(LIBS) -lssh2 -lncurses netconfcentral_yang_DATA = \ $(top_srcdir)/netconf/modules/netconfcentral/yangdiff.yang yuma123_2.14/netconf/src/yangdiff/yangdiff.c0000664000175000017500000016413114770023131021076 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangdiff.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 10-jun-08 abb begun; used yangdump.c as a template 31-jul-08 abb convert from NCX based CLI to YANG based CLI ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include /* #define MEMORY_DEBUG 1 */ #ifdef MEMORY_DEBUG #include #endif #define _C_main 1 #include "procdefs.h" #include "cli.h" #include "conf.h" #include "ext.h" #include "help.h" #include "log.h" #include "ncx.h" #include "ncx_feature.h" #include "ncxconst.h" #include "ncxmod.h" #include "ses.h" #include "status.h" #include "tstamp.h" #include "val.h" #include "val_util.h" #include "xml_util.h" #include "yang.h" #include "yangconst.h" #include "yangdiff.h" #include "yangdiff_grp.h" #include "yangdiff_obj.h" #include "yangdiff_typ.h" #include "yangdiff_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define YANGDIFF_DEBUG 1 /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static val_value_t *cli_val; static yangdiff_diffparms_t diffparms; /******************************************************************** * FUNCTION pr_err * * Print an error message * * INPUTS: * res == error result * *********************************************************************/ static void pr_err (status_t res) { const char *msg; msg = get_error_string(res); log_error("\n%s: Error exit (%s)\n", YANGDIFF_PROGNAME, msg); } /* pr_err */ /******************************************************************** * FUNCTION pr_usage * * Print a usage message * *********************************************************************/ static void pr_usage (void) { log_error("\nError: No parameters entered." "\nTry '%s --help' for usage details\n", YANGDIFF_PROGNAME); } /* pr_usage */ /******************************************************************** * FUNCTION output_import_diff * * Output the differences report for the imports portion * of two 2 parsed modules. import-stmt order is not * a semantic change, so it is not checked * * INPUTS: * cp == parameter block to use * oldpcb == old module * newpcb == new module * *********************************************************************/ static void output_import_diff (yangdiff_diffparms_t *cp, yang_pcb_t *oldpcb, yang_pcb_t *newpcb) { ncx_import_t *oldimp, *newimp; /* clear the used flag, for yangdiff reuse */ for (newimp = (ncx_import_t *)dlq_firstEntry(&newpcb->top->importQ); newimp != NULL; newimp = (ncx_import_t *)dlq_nextEntry(newimp)) { newimp->used = FALSE; } /* look for matching imports */ for (oldimp = (ncx_import_t *)dlq_firstEntry(&oldpcb->top->importQ); oldimp != NULL; oldimp = (ncx_import_t *)dlq_nextEntry(oldimp)) { /* find this import in the new module */ newimp = ncx_find_import(newpcb->top, oldimp->module); if (newimp) { if (xml_strcmp(oldimp->prefix, newimp->prefix)) { /* prefix was changed in the new module */ output_mstart_line(cp, YANG_K_IMPORT, oldimp->module, TRUE); if (cp->edifftype != YANGDIFF_DT_TERSE) { indent_in(cp); output_diff(cp, YANG_K_PREFIX, oldimp->prefix, newimp->prefix, TRUE); indent_out(cp); } } newimp->used = TRUE; } else { /* import was removed from the new module */ output_diff(cp, YANG_K_IMPORT, oldimp->module, NULL, TRUE); } } for (newimp = (ncx_import_t *)dlq_firstEntry(&newpcb->top->importQ); newimp != NULL; newimp = (ncx_import_t *)dlq_nextEntry(newimp)) { if (!newimp->used) { /* this import was added in the new revision */ output_diff(cp, YANG_K_IMPORT, NULL, newimp->module, TRUE); } } } /* output_import_diff */ /******************************************************************** * FUNCTION output_include_diff * * Output the differences report for the includes portion * of two 2 parsed modules * * INPUTS: * cp == parameter block to use * oldpcb == old module * newpcb == new module * *********************************************************************/ static void output_include_diff (yangdiff_diffparms_t *cp, yang_pcb_t *oldpcb, yang_pcb_t *newpcb) { ncx_include_t *oldinc, *newinc; /* clear usexsd field for yangdiff reuse */ for (newinc = (ncx_include_t *)dlq_firstEntry(&newpcb->top->includeQ); newinc != NULL; newinc = (ncx_include_t *)dlq_nextEntry(newinc)) { newinc->usexsd = FALSE; } /* look for matchine entries */ for (oldinc = (ncx_include_t *)dlq_firstEntry(&oldpcb->top->includeQ); oldinc != NULL; oldinc = (ncx_include_t *)dlq_nextEntry(oldinc)) { /* find this include in the new module */ newinc = ncx_find_include(newpcb->top, oldinc->submodule); if (newinc) { newinc->usexsd = TRUE; } else { /* include was removed from the new module */ output_diff(cp, YANG_K_INCLUDE, oldinc->submodule, NULL, TRUE); } } for (newinc = (ncx_include_t *)dlq_firstEntry(&newpcb->top->includeQ); newinc != NULL; newinc = (ncx_include_t *)dlq_nextEntry(newinc)) { if (!newinc->usexsd) { /* this include was added in the new revision */ output_diff(cp, YANG_K_INCLUDE, NULL, newinc->submodule, TRUE); } } } /* output_include_diff */ /******************************************************************** * FUNCTION output_revision_diff * * Output the differences report for the revisions list portion * of two 2 parsed modules * * INPUTS: * cp == parameter block to use * oldpcb == old module * newpcb == new module * *********************************************************************/ static void output_revision_diff (yangdiff_diffparms_t *cp, yang_pcb_t *oldpcb, yang_pcb_t *newpcb) { yangdiff_cdb_t revcdb[2]; ncx_revhist_t *oldrev, *newrev; boolean isrev; uint32 changecnt, i; isrev = (cp->edifftype == YANGDIFF_DT_REVISION) ? TRUE : FALSE; /* clear res field for yangdiff reuse */ for (newrev = (ncx_revhist_t *)dlq_firstEntry(&newpcb->top->revhistQ); newrev != NULL; newrev = (ncx_revhist_t *)dlq_nextEntry(newrev)) { newrev->res = ERR_NCX_INVALID_STATUS; } for (oldrev = (ncx_revhist_t *)dlq_firstEntry(&oldpcb->top->revhistQ); oldrev != NULL; oldrev = (ncx_revhist_t *)dlq_nextEntry(oldrev)) { /* find this revision in the new module */ newrev = ncx_find_revhist(newpcb->top, oldrev->version); if (newrev) { changecnt = 0; changecnt += str_field_changed(YANG_K_DESCRIPTION, oldrev->descr, newrev->descr, isrev, &revcdb[0]); changecnt += str_field_changed(YANG_K_REFERENCE, oldrev->ref, newrev->ref, isrev, &revcdb[1]); if (changecnt > 0) { switch (cp->edifftype) { case YANGDIFF_DT_TERSE: case YANGDIFF_DT_NORMAL: case YANGDIFF_DT_REVISION: output_mstart_line(cp, YANG_K_REVISION, oldrev->version, FALSE); if (cp->edifftype != YANGDIFF_DT_TERSE) { indent_in(cp); for (i=0; i<2; i++) { if (revcdb[i].changed) { output_cdb_line(cp, &revcdb[i]); } } indent_out(cp); } break; default: SET_ERROR(ERR_INTERNAL_VAL); } } newrev->res = NO_ERR; } else { /* revision was removed from the new module */ output_diff(cp, YANG_K_REVISION, oldrev->version, NULL, FALSE); } } for (newrev = (ncx_revhist_t *)dlq_firstEntry(&newpcb->top->revhistQ); newrev != NULL; newrev = (ncx_revhist_t *)dlq_nextEntry(newrev)) { if (newrev->res != NO_ERR) { /* this revision-stmt was added in the new version */ output_diff(cp, YANG_K_REVISION, NULL, newrev->version, FALSE); } } } /* output_revision_diff */ /******************************************************************** * FUNCTION output_one_extension_diff * * Output the differences report for an extension * * INPUTS: * cp == parameter block to use * oldext == old extension * newext == new extension * *********************************************************************/ static void output_one_extension_diff (yangdiff_diffparms_t *cp, ext_template_t *oldext, ext_template_t *newext) { yangdiff_cdb_t extcdb[5]; uint32 changecnt, i; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; /* figure out what changed */ changecnt = 0; changecnt += str_field_changed(YANG_K_DESCRIPTION, oldext->descr, newext->descr, isrev, &extcdb[0]); changecnt += str_field_changed(YANG_K_REFERENCE, oldext->ref, newext->ref, isrev, &extcdb[1]); changecnt += str_field_changed(YANG_K_ARGUMENT, oldext->arg, newext->arg, isrev, &extcdb[2]); changecnt += bool_field_changed(YANG_K_YIN_ELEMENT, oldext->argel, newext->argel, isrev, &extcdb[3]); changecnt += status_field_changed(YANG_K_STATUS, oldext->status, newext->status, isrev, &extcdb[4]); if (changecnt == 0) { return; } /* generate the diff output, based on the requested format */ switch (cp->edifftype) { case YANGDIFF_DT_TERSE: case YANGDIFF_DT_NORMAL: case YANGDIFF_DT_REVISION: output_mstart_line(cp, YANG_K_EXTENSION, oldext->name, TRUE); if (cp->edifftype != YANGDIFF_DT_TERSE) { indent_in(cp); for (i=0; i<5; i++) { if (extcdb[i].changed) { output_cdb_line(cp, &extcdb[i]); } } indent_out(cp); } break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* output_one_extension_diff */ /******************************************************************** * FUNCTION output_extensions_diff * * Output the differences report for the extensions portion * of two 2 parsed modules * * INPUTS: * cp == parameter block to use * oldpcb == old module * newpcb == new module * *********************************************************************/ static void output_extensions_diff (yangdiff_diffparms_t *cp, yang_pcb_t *oldpcb, yang_pcb_t *newpcb) { ext_template_t *oldext, *newext; /* make sure the new 'used' flags are cleared */ for (newext = (ext_template_t *) dlq_firstEntry(&newpcb->top->extensionQ); newext != NULL; newext = (ext_template_t *)dlq_nextEntry(newext)) { newext->used = FALSE; } for (oldext = (ext_template_t *) dlq_firstEntry(&oldpcb->top->extensionQ); oldext != NULL; oldext = (ext_template_t *)dlq_nextEntry(oldext)) { /* find this extension in the new module */ newext = ext_find_extension(newpcb->top, oldext->name); if (newext) { output_one_extension_diff(cp, oldext, newext); newext->used = TRUE; } else { /* extension was removed from the new module */ output_diff(cp, YANG_K_EXTENSION, oldext->name, NULL, TRUE); } } for (newext = (ext_template_t *) dlq_firstEntry(&newpcb->top->extensionQ); newext != NULL; newext = (ext_template_t *)dlq_nextEntry(newext)) { if (!newext->used) { /* this extension-stmt was added in the new version */ output_diff(cp, YANG_K_EXTENSION, NULL, newext->name, TRUE); } } } /* output_extension_diff */ /******************************************************************** * FUNCTION output_one_feature_diff * * Output the differences report for a YANG feature * * INPUTS: * cp == parameter block to use * oldfeat == old feature * newfeat == new feature * *********************************************************************/ static void output_one_feature_diff (yangdiff_diffparms_t *cp, const xmlChar *modprefix, ncx_feature_t *oldfeat, ncx_feature_t *newfeat) { yangdiff_cdb_t featcdb[3]; uint32 changecnt, i; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; /* figure out what changed */ changecnt = 0; changecnt += status_field_changed(YANG_K_STATUS, oldfeat->status, newfeat->status, isrev, &featcdb[0]); changecnt += str_field_changed(YANG_K_DESCRIPTION, oldfeat->descr, newfeat->descr, isrev, &featcdb[1]); changecnt += str_field_changed(YANG_K_REFERENCE, oldfeat->ref, newfeat->ref, isrev, &featcdb[2]); changecnt += iffeatureQ_changed(modprefix, &oldfeat->iffeatureQ, &newfeat->iffeatureQ); if (changecnt == 0) { return; } /* generate the diff output, based on the requested format */ switch (cp->edifftype) { case YANGDIFF_DT_TERSE: case YANGDIFF_DT_NORMAL: case YANGDIFF_DT_REVISION: output_mstart_line(cp, YANG_K_FEATURE, oldfeat->name, TRUE); if (cp->edifftype != YANGDIFF_DT_TERSE) { indent_in(cp); for (i=0; i<3; i++) { if (featcdb[i].changed) { output_cdb_line(cp, &featcdb[i]); } } output_iffeatureQ_diff(cp, modprefix, &oldfeat->iffeatureQ, &newfeat->iffeatureQ); indent_out(cp); } break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* output_one_feature_diff */ /******************************************************************** * FUNCTION output_features_diff * * Output the differences report for the features portion * of two 2 parsed modules * * INPUTS: * cp == parameter block to use * oldpcb == old module * newpcb == new module * *********************************************************************/ static void output_features_diff (yangdiff_diffparms_t *cp, yang_pcb_t *oldpcb, yang_pcb_t *newpcb) { ncx_feature_t *oldfeat, *newfeat; /* make sure the new 'used' flags are cleared */ for (newfeat = (ncx_feature_t *) dlq_firstEntry(&newpcb->top->featureQ); newfeat != NULL; newfeat = (ncx_feature_t *)dlq_nextEntry(newfeat)) { newfeat->seen = FALSE; } for (oldfeat = (ncx_feature_t *) dlq_firstEntry(&oldpcb->top->featureQ); oldfeat != NULL; oldfeat = (ncx_feature_t *)dlq_nextEntry(oldfeat)) { /* find this feature in the new module */ newfeat = ncx_find_feature(newpcb->top, oldfeat->name); if (newfeat) { output_one_feature_diff(cp, oldpcb->top->prefix, oldfeat, newfeat); newfeat->seen = TRUE; } else { /* feature was removed from the new module */ output_diff(cp, YANG_K_FEATURE, oldfeat->name, NULL, TRUE); } } for (newfeat = (ncx_feature_t *) dlq_firstEntry(&newpcb->top->featureQ); newfeat != NULL; newfeat = (ncx_feature_t *)dlq_nextEntry(newfeat)) { if (!newfeat->seen) { /* this feature-stmt was added in the new version */ output_diff(cp, YANG_K_FEATURE, NULL, newfeat->name, TRUE); } } } /* output_features_diff */ /******************************************************************** * FUNCTION output_one_identity_diff * * Output the differences report for a YANG identity * * INPUTS: * cp == parameter block to use * oldident == old identity * newident == new identity * *********************************************************************/ static void output_one_identity_diff (yangdiff_diffparms_t *cp, ncx_identity_t *oldident, ncx_identity_t *newident) { yangdiff_cdb_t identcdb[5]; uint32 changecnt, i; boolean isrev; isrev = (cp->edifftype==YANGDIFF_DT_REVISION) ? TRUE : FALSE; /* figure out what changed */ changecnt = 0; changecnt += status_field_changed(YANG_K_STATUS, oldident->status, newident->status, isrev, &identcdb[0]); changecnt += str_field_changed(YANG_K_DESCRIPTION, oldident->descr, newident->descr, isrev, &identcdb[1]); changecnt += str_field_changed(YANG_K_REFERENCE, oldident->ref, newident->ref, isrev, &identcdb[2]); changecnt += str_field_changed((const xmlChar *)"base prefix", oldident->baseprefix, newident->baseprefix, isrev, &identcdb[3]); changecnt += str_field_changed(YANG_K_BASE, oldident->basename, newident->basename, isrev, &identcdb[4]); if (changecnt == 0) { return; } /* generate the diff output, based on the requested format */ switch (cp->edifftype) { case YANGDIFF_DT_TERSE: case YANGDIFF_DT_NORMAL: case YANGDIFF_DT_REVISION: output_mstart_line(cp, YANG_K_IDENTITY, oldident->name, TRUE); if (cp->edifftype != YANGDIFF_DT_TERSE) { indent_in(cp); for (i=0; i<5; i++) { if (identcdb[i].changed) { output_cdb_line(cp, &identcdb[i]); } } indent_out(cp); } break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* output_one_identity_diff */ /******************************************************************** * FUNCTION output_identities_diff * * Output the differences report for the identities portion * of two 2 parsed modules * * INPUTS: * cp == parameter block to use * oldpcb == old module * newpcb == new module * *********************************************************************/ static void output_identities_diff (yangdiff_diffparms_t *cp, yang_pcb_t *oldpcb, yang_pcb_t *newpcb) { ncx_identity_t *oldident, *newident; /* make sure the new 'used' flags are cleared */ for (newident = (ncx_identity_t *) dlq_firstEntry(&newpcb->top->identityQ); newident != NULL; newident = (ncx_identity_t *)dlq_nextEntry(newident)) { newident->seen = FALSE; } for (oldident = (ncx_identity_t *) dlq_firstEntry(&oldpcb->top->identityQ); oldident != NULL; oldident = (ncx_identity_t *)dlq_nextEntry(oldident)) { /* find this identure in the new module */ newident = ncx_find_identity(newpcb->top, oldident->name, FALSE); if (newident) { output_one_identity_diff(cp, oldident, newident); newident->seen = TRUE; } else { /* identure was removed from the new module */ output_diff(cp, YANG_K_IDENTITY, oldident->name, NULL, TRUE); } } for (newident = (ncx_identity_t *) dlq_firstEntry(&newpcb->top->identityQ); newident != NULL; newident = (ncx_identity_t *)dlq_nextEntry(newident)) { if (!newident->seen) { /* this identure-stmt was added in the new version */ output_diff(cp, YANG_K_IDENTITY, NULL, newident->name, TRUE); } } } /* output_identities_diff */ /******************************************************************** * FUNCTION output_hdr_diff * * Output the differences report for the header portion * of two 2 parsed modules * * INPUTS: * cp == parameter block to use * oldpcb == old module * newpcb == new module * *********************************************************************/ static void output_hdr_diff (yangdiff_diffparms_t *cp, yang_pcb_t *oldpcb, yang_pcb_t *newpcb) { char oldnumbuff[NCX_MAX_NUMLEN]; char newnumbuff[NCX_MAX_NUMLEN]; /* module name, module/submodule mismatch */ if (oldpcb->top->ismod != newpcb->top->ismod || xml_strcmp(oldpcb->top->name, newpcb->top->name)) { output_diff(cp, oldpcb->top->ismod ? YANG_K_MODULE : YANG_K_SUBMODULE, oldpcb->top->name, newpcb->top->name, TRUE); } /* yang-version */ if (oldpcb->top->langver != newpcb->top->langver) { sprintf(oldnumbuff, "%u", oldpcb->top->langver); sprintf(newnumbuff, "%u", newpcb->top->langver); output_diff(cp, YANG_K_YANG_VERSION, (const xmlChar *)oldnumbuff, (const xmlChar *)newnumbuff, TRUE); } /* namespace */ output_diff(cp, YANG_K_NAMESPACE, oldpcb->top->ns, newpcb->top->ns, FALSE); /* prefix */ output_diff(cp, YANG_K_PREFIX, oldpcb->top->prefix, newpcb->top->prefix, FALSE); /* imports */ output_import_diff(cp, oldpcb, newpcb); /* includes */ output_include_diff(cp, oldpcb, newpcb); /* organization */ output_diff(cp, YANG_K_ORGANIZATION, oldpcb->top->organization, newpcb->top->organization, FALSE); /* contact */ output_diff(cp, YANG_K_CONTACT, oldpcb->top->contact_info, newpcb->top->contact_info, FALSE); /* description */ output_diff(cp, YANG_K_DESCRIPTION, oldpcb->top->descr, newpcb->top->descr, FALSE); /* reference */ output_diff(cp, YANG_K_REFERENCE, oldpcb->top->ref, newpcb->top->ref, FALSE); /* revisions */ output_revision_diff(cp, oldpcb, newpcb); } /* output_hdr_diff */ /******************************************************************** * FUNCTION output_diff_banner * * Output the banner for the start of a diff report * * INPUTS: * cp == parameter block to use * oldpcb == old module * newpcb == new module * *********************************************************************/ static void output_diff_banner (yangdiff_diffparms_t *cp, yang_pcb_t *oldpcb, yang_pcb_t *newpcb) { status_t res; xmlChar versionbuffer[NCX_VERSION_BUFFSIZE]; if (!cp->firstdone) { ses_putstr(cp->scb, (const xmlChar *)"\n// Generated by "); ses_putstr(cp->scb, YANGDIFF_PROGNAME); ses_putchar(cp->scb, ' '); res = ncx_get_version(versionbuffer, NCX_VERSION_BUFFSIZE); if (res == NO_ERR) { ses_putstr(cp->scb, versionbuffer); ses_putstr(cp->scb, (const xmlChar *)"\n// "); ses_putstr(cp->scb, (const xmlChar *)COPYRIGHT_STRING_LINE0); } else { SET_ERROR(res); } cp->firstdone = TRUE; } #ifdef ADD_SEP_LINE if (cp->new_isdir) { ses_putstr(cp->scb, YANGDIFF_LINE); } #endif ses_putstr(cp->scb, (const xmlChar *)"\n// old: "); ses_putstr(cp->scb, oldpcb->top->name); ses_putchar(cp->scb, ' '); ses_putchar(cp->scb, '('); ses_putstr(cp->scb, oldpcb->top->version); ses_putchar(cp->scb, ')'); ses_putchar(cp->scb, ' '); ses_putstr(cp->scb, (cp->old_isdir) ? oldpcb->top->source : oldpcb->top->sourcefn); ses_putstr(cp->scb, (const xmlChar *)"\n// new: "); ses_putstr(cp->scb, newpcb->top->name); ses_putchar(cp->scb, ' '); ses_putchar(cp->scb, '('); ses_putstr(cp->scb, newpcb->top->version); ses_putchar(cp->scb, ')'); ses_putchar(cp->scb, ' '); ses_putstr(cp->scb, (cp->new_isdir) ? newpcb->top->source : newpcb->top->sourcefn); ses_putchar(cp->scb, '\n'); if (cp->edifftype == YANGDIFF_DT_REVISION) { indent_in(cp); ses_putstr_indent(cp->scb, (const xmlChar *)"revision ", cp->curindent); tstamp_date(cp->buff); ses_putstr(cp->scb, cp->buff); ses_putstr(cp->scb, (const xmlChar *)" {"); indent_in(cp); ses_putstr_indent(cp->scb, (const xmlChar *)"description \"", cp->curindent); indent_in(cp); } } /* output_diff_banner */ /******************************************************************** * FUNCTION generate_diff_report * * Generate the differences report for 2 parsed modules * * INPUTS: * cp == parameter block to use * oldpcb == old module * newpcb == new module * * RETURNS: * status *********************************************************************/ static status_t generate_diff_report (yangdiff_diffparms_t *cp, yang_pcb_t *oldpcb, yang_pcb_t *newpcb) { status_t res; res = NO_ERR; /* generate the banner indicating the start of diff report */ output_diff_banner(cp, oldpcb, newpcb); /* header fields */ if (cp->header) { output_hdr_diff(cp, oldpcb, newpcb); } /* all extensions */ output_extensions_diff(cp, oldpcb, newpcb); /* all features */ output_features_diff(cp, oldpcb, newpcb); /* all identities */ output_identities_diff(cp, oldpcb, newpcb); /* global typedefs */ output_typedefQ_diff(cp, &oldpcb->top->typeQ, &newpcb->top->typeQ); /* global groupings */ output_groupingQ_diff(cp, &oldpcb->top->groupingQ, &newpcb->top->groupingQ); /* global data definitions */ output_datadefQ_diff(cp, &oldpcb->top->datadefQ, &newpcb->top->datadefQ); /* finish off revision statement if that is the diff mode */ if (cp->edifftype == YANGDIFF_DT_REVISION) { /* end description clause */ indent_out(cp); ses_putstr_indent(cp->scb, (const xmlChar *)"\";", cp->curindent); /* end revision clause */ indent_out(cp); ses_putstr_indent(cp->scb, (const xmlChar *)"}", cp->curindent); indent_out(cp); } ses_putchar(cp->scb, '\n'); return res; } /* generate_diff_report */ /******************************************************************** * FUNCTION make_curold_filename * * Construct an output filename spec, based on the * comparison parameters * * INPUTS: * newfn == new filename -- just the module.ext part * cp == comparison parameters to use * * RETURNS: * malloced string or NULL if malloc error *********************************************************************/ static xmlChar * make_curold_filename (const xmlChar *newfn, const yangdiff_diffparms_t *cp) { xmlChar *buff, *p; const xmlChar *newstart; uint32 len, pslen; pslen = 0; /* setup source filespec pointer */ newstart = newfn; if (cp->new_isdir) { newstart += xml_strlen(cp->full_new); } /* check length: subtree mode vs 1 file mode */ if (cp->old_isdir) { len = xml_strlen(cp->full_old); if ((cp->full_old[len-1] != NCXMOD_PSCHAR) && (*newstart != NCXMOD_PSCHAR)) { pslen = 1; } len += xml_strlen(newstart); } else { len = xml_strlen(cp->full_old); } /* get a buffer */ buff = m__getMem(len+pslen+1); if (!buff) { return NULL; } /* fill in buffer: subtree mode vs 1 file mode */ if (cp->old_isdir) { p = buff; p += xml_strcpy(p, cp->full_old); if (pslen) { *p++ = NCXMOD_PSCHAR; } xml_strcpy(p, newstart); } else { xml_strcpy(buff, cp->full_old); } return buff; } /* make_curold_filename */ /******************************************************************** * FUNCTION compare_one * * Validate and then compare one module to another * * load in the requested 'new' module to compare * if this is a subtree call, then the curnew pointer * will be set, otherwise the 'new' pointer must be set * * * INPUTS: * cp == parameter block to use * * RETURNS: * status *********************************************************************/ static status_t compare_one (yangdiff_diffparms_t *cp) { yang_pcb_t *oldpcb, *newpcb; const xmlChar *logsource, *modpath, *revision; xmlChar *modparm, *savestr, savechar; status_t res; boolean skipreport; uint32 modlen; res = NO_ERR; skipreport = FALSE; cp->curindent = 0; savestr = NULL; savechar = '\0'; revision = NULL; modlen = 0; modparm = (cp->curnew) ? cp->curnew : cp->new; /* force modules to be reloaded */ ncx_set_cur_modQ(&cp->newmodQ); /* pick which module source path to use */ modpath = NULL; if (cp->newpath) { ncxmod_set_modpath(cp->newpath); } else if (cp->curnew) { modpath = cp->full_new; } else if (cp->modpath) { ncxmod_set_modpath(cp->modpath); } else { /* clear out any old value */ ncxmod_set_modpath(EMPTY_STRING); } if (yang_split_filename(modparm, &modlen)) { savestr = &modparm[modlen]; savechar = *savestr; *savestr = '\0'; revision = savestr + 1; } newpcb = ncxmod_load_module_diff(modparm, revision, FALSE, modpath, NULL, &res); if (savestr != NULL) { *savestr = savechar; } if (res == ERR_NCX_SKIPPED) { if (newpcb) { yang_free_pcb(newpcb); } return NO_ERR; } else if (res != NO_ERR) { if (newpcb && newpcb->top) { logsource = (LOGDEBUG) ? newpcb->top->source : newpcb->top->sourcefn; if (newpcb->top->errors) { log_error("\n*** %s: %u Errors, %u Warnings\n", logsource, newpcb->top->errors, newpcb->top->warnings); } else if (newpcb->top->warnings) { log_warn("\n*** %s: %u Errors, %u Warnings\n", logsource, newpcb->top->errors, newpcb->top->warnings); } } else { /* make sure next task starts on a newline */ log_error("\n"); } if (!newpcb || !newpcb->top || newpcb->top->errors) { if (newpcb) { yang_free_pcb(newpcb); } return res; } else { /* just warnings reported */ res = NO_ERR; } } else if (LOGDEBUG && newpcb && newpcb->top) { log_debug("\n*** %s: %u Errors, %u Warnings\n", newpcb->top->source, newpcb->top->errors, newpcb->top->warnings); } /* figure out where to get the requested 'old' file */ if (cp->old) { cp->curold = xml_strdup(cp->old); } else { cp->curold = make_curold_filename((cp->new_isdir) ? cp->curnew : newpcb->top->sourcefn, cp); } if (!cp->curold) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(NULL, NULL, res); yang_free_pcb(newpcb); return res; } modlen = 0; savestr = NULL; savechar ='\0'; revision = NULL; if (yang_split_filename(cp->curold, &modlen)) { savestr = &cp->curold[modlen]; savechar = *savestr; *savestr = '\0'; revision = savestr + 1; } /* force modules to be reloaded */ ncx_set_cur_modQ(&cp->oldmodQ); /* pick which module source path to use */ modpath = NULL; if (cp->oldpath) { ncxmod_set_modpath(cp->oldpath); } else if (cp->full_old) { modpath = cp->full_old; } else if (cp->modpath) { ncxmod_set_modpath(cp->modpath); } else { ncxmod_set_modpath(EMPTY_STRING); } /* load in the requested 'old' module to compare * if this is a subtree call, then the curnew pointer * will be set, otherwise the 'new' pointer must be set */ oldpcb = ncxmod_load_module_diff(cp->curold, revision, FALSE, modpath, NULL, &res); if (savestr != NULL) { *savestr = savechar; } if (res == ERR_NCX_SKIPPED) { /* this is probably a submodule being skipped in subtree mode */ log_debug("\nyangdiff: New PCB OK but old PCB skipped (%s)", newpcb->top->sourcefn); if (oldpcb) { yang_free_pcb(oldpcb); } yang_free_pcb(newpcb); return NO_ERR; } else if (res != NO_ERR) { if (oldpcb && oldpcb->top) { logsource = (LOGDEBUG) ? oldpcb->top->source : oldpcb->top->sourcefn; if (oldpcb->top->errors) { log_error("\n*** %s: %u Errors, %u Warnings\n", logsource, oldpcb->top->errors, oldpcb->top->warnings); } else if (oldpcb->top->warnings) { log_warn("\n*** %s: %u Errors, %u Warnings\n", logsource, oldpcb->top->errors, oldpcb->top->warnings); } } else { /* make sure next task starts on a newline */ log_error("\n"); } if (!oldpcb || !oldpcb->top || oldpcb->top->errors) { if (newpcb) { yang_free_pcb(newpcb); } if (oldpcb) { yang_free_pcb(oldpcb); } return res; } else { /* just warnings reported */ res = NO_ERR; } return res; } else if (LOGDEBUG && oldpcb && oldpcb->top) { log_debug("\n*** %s: %u Errors, %u Warnings\n", oldpcb->top->source, oldpcb->top->errors, oldpcb->top->warnings); } /* allow new modules to be available for ncx_find_module */ ncx_set_cur_modQ(&cp->newmodQ); /* check if old and new files parsed okay */ if (ncx_any_dependency_errors(newpcb->top)) { log_error("\nError: one or more modules imported into new '%s' " "had errors", newpcb->top->sourcefn); skipreport = TRUE; } else { cp->newmod = newpcb->top; } /* allow old modules to be available for ncx_find_module */ ncx_set_cur_modQ(&cp->oldmodQ); if (ncx_any_dependency_errors(oldpcb->top)) { log_error("\nError: one or more modules imported into old '%s' " "had errors", oldpcb->top->sourcefn); skipreport = TRUE; } else { cp->oldmod = oldpcb->top; } /* generate compare output to the dummy session */ if (!skipreport) { res = generate_diff_report(cp, oldpcb, newpcb); } else { res = ERR_NCX_IMPORT_ERRORS; ncx_print_errormsg(NULL, NULL, res); } /* clean up the parser control blocks */ if (newpcb) { yang_free_pcb(newpcb); } if (oldpcb) { yang_free_pcb(oldpcb); } /* cleanup the altered module Q */ ncx_reset_modQ(); return res; } /* compare_one */ /******************************************************************** * FUNCTION subtree_callback * * Handle the current filename in the subtree traversal * Parse the module and generate. * * Follows ncxmod_callback_fn_t template * * INPUTS: * fullspec == absolute or relative path spec, with filename and ext. * this regular file exists, but has not been checked for * read access of * cookie == NOT USED * * RETURNS: * status * * Return fatal error to stop the traversal or NO_ERR to * keep the traversal going. Do not return any warning or * recoverable error, just log and move on *********************************************************************/ static status_t subtree_callback (const char *fullspec, void *cookie) { yangdiff_diffparms_t *cp; status_t res; cp = cookie; res = NO_ERR; if (cp->curnew) { m__free(cp->curnew); } cp->curnew = ncx_get_source((const xmlChar *)fullspec, &res); if (!cp->curnew) { return res; } log_debug2("\nStart subtree file:\n%s\n", fullspec); res = compare_one(cp); if (res != NO_ERR) { if (!NEED_EXIT(res)) { res = NO_ERR; } } return res; } /* subtree_callback */ /******************************************************************** * FUNCTION make_output_filename * * Construct an output filename spec, based on the * comparison parameters * * INPUTS: * cp == comparison parameters to use * * RETURNS: * malloced string or NULL if malloc error *********************************************************************/ static xmlChar * make_output_filename (const yangdiff_diffparms_t *cp) { xmlChar *buff, *p; uint32 len; if (cp->output && *cp->output) { len = xml_strlen(cp->output); if (cp->output_isdir) { if (cp->output[len-1] != NCXMOD_PSCHAR) { len++; } len += xml_strlen(YANGDIFF_DEF_FILENAME); } } else { len = xml_strlen(YANGDIFF_DEF_FILENAME); } buff = m__getMem(len+1); if (!buff) { return NULL; } if (cp->output && *cp->output) { p = buff; p += xml_strcpy(p, cp->output); if (cp->output_isdir) { if (*(p-1) != NCXMOD_PSCHAR) { *p++ = NCXMOD_PSCHAR; } xml_strcpy(p, YANGDIFF_DEF_FILENAME); } } else { xml_strcpy(buff, YANGDIFF_DEF_FILENAME); } return buff; } /* make_output_filename */ /******************************************************************** * FUNCTION get_output_session * * Malloc and init an output session. * Open the correct output file if needed * * INPUTS: * cp == compare parameters to use * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to new session control block, or NULL if some error *********************************************************************/ static ses_cb_t * get_output_session (yangdiff_diffparms_t *cp, status_t *res) { FILE *fp; ses_cb_t *scb; xmlChar *namebuff; fp = NULL; scb = NULL; namebuff = NULL; *res = NO_ERR; /* open the output file if not STDOUT */ if (cp->output && *cp->output) { namebuff = make_output_filename(cp); if (!namebuff) { *res = ERR_INTERNAL_MEM; return NULL; } fp = fopen((const char *)namebuff, "w"); if (!fp) { *res = ERR_FIL_OPEN; m__free(namebuff); return NULL; } } /* get a dummy session control block */ scb = ses_new_dummy_scb(); if (!scb) { *res = ERR_INTERNAL_MEM; } else { scb->fp = fp; ses_set_mode(scb, SES_MODE_TEXT); } if (namebuff) { m__free(namebuff); } return scb; } /* get_output_session */ /******************************************************************** * FUNCTION main_run * * Run the compare program, based on the input parameters * * RETURNS: * status *********************************************************************/ static status_t main_run (void) { status_t res; xmlChar versionbuffer[NCX_VERSION_BUFFSIZE]; res = NO_ERR; if (diffparms.versionmode) { res = ncx_get_version(versionbuffer, NCX_VERSION_BUFFSIZE); if (res == NO_ERR) { log_write("yangdiff %s\n", versionbuffer); } else { SET_ERROR(res); } } if (diffparms.helpmode) { help_program_module(YANGDIFF_MOD, YANGDIFF_CONTAINER, diffparms.helpsubmode); } if ((diffparms.helpmode || diffparms.versionmode)) { return res; } /* check if subdir search suppression is requested */ if (!diffparms.subdirs) { ncxmod_set_subdirs(FALSE); } /* setup the output session to a file or STDOUT */ diffparms.scb = get_output_session(&diffparms, &res); if (!diffparms.scb || res != NO_ERR) { return res; } /* reset the current indent from default (3) to 0 */ /* ses_set_indent(diffparms.scb, 0); */ /* make sure the mandatory parameters are set */ if (!diffparms.old) { log_error("\nError: The 'old' parameter is required."); res = ERR_NCX_MISSING_PARM; ncx_print_errormsg(NULL, NULL, res); } if (!diffparms.new) { log_error("\nError: The 'new' parameter is required."); res = ERR_NCX_MISSING_PARM; ncx_print_errormsg(NULL, NULL, res); } if (!diffparms.difftype) { log_error("\nError: The 'difftype' parameter is required."); res = ERR_NCX_MISSING_PARM; ncx_print_errormsg(NULL, NULL, res); } if (diffparms.edifftype==YANGDIFF_DT_NONE) { log_error("\nError: Invalid 'difftype' parameter value."); res = ERR_NCX_INVALID_VALUE; ncx_print_errormsg(NULL, NULL, res); } if (diffparms.new_isdir && !diffparms.old_isdir) { log_error("\nError: The 'old' parameter " "must identify a directory."); res = ERR_NCX_INVALID_VALUE; ncx_print_errormsg(NULL, NULL, res); } if (diffparms.old != NULL && diffparms.new != NULL && !xml_strcmp(diffparms.old, diffparms.new)) { log_error("\nError: The 'old' and 'new' " "parameters must be different."); res = ERR_NCX_INVALID_VALUE; ncx_print_errormsg(NULL, NULL, res); } if (res == NO_ERR) { /* prevent back-ptrs in corner-case error parse modes */ ncx_set_use_deadmodQ(); /* compare one file to another or 1 subtree to another */ if (diffparms.new_isdir) { res = ncxmod_process_subtree((const char *)diffparms.new, subtree_callback, &diffparms); } else { res = compare_one(&diffparms); } } return res; } /* main_run */ /******************************************************************** * FUNCTION process_cli_input * * Process the param line parameters against the hardwired * parmset for the yangdiff program * * get all the parms and store them in the diffparms struct * !!! Relies on CLI parmset 'cp' remaining valid until program cleanup !!! * * INPUTS: * argc == argument count * argv == array of command line argument strings * cp == address of returned values * * OUTPUTS: * parmset values will be stored in *diffparms if NO_ERR * errors will be written to STDOUT * * RETURNS: * NO_ERR if all goes well *********************************************************************/ static status_t process_cli_input (int argc, char *argv[], yangdiff_diffparms_t *cp) { obj_template_t *obj; val_value_t *valset, *val; ncx_module_t *mod; status_t res; res = NO_ERR; valset = NULL; cp->buff = m__getMem(YANGDIFF_BUFFSIZE); if (!cp->buff) { return ERR_INTERNAL_MEM; } cp->bufflen = YANGDIFF_BUFFSIZE; cp->maxlen = YANGDIFF_DEF_MAXSIZE; /* find the parmset definition in the registry */ obj = NULL; mod = ncx_find_module(YANGDIFF_MOD, NULL); if (mod) { obj = ncx_find_object(mod, YANGDIFF_CONTAINER); } if (!obj) { res = ERR_NCX_NOT_FOUND; } /* parse the command line against the PSD */ if (res == NO_ERR) { valset = cli_parse(NULL, argc, argv, obj, FULLTEST, PLAINMODE, TRUE, CLI_MODE_PROGRAM, &res); } if (res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } else if (!valset) { pr_usage(); return ERR_NCX_SKIPPED; } else { cli_val = valset; } /* next get any params from the conf file */ val = val_find_child(valset, YANGDIFF_MOD, YANGDIFF_PARM_CONFIG); if (val) { /* try the specified config location */ cp->config = VAL_STR(val); res = conf_parse_val_from_filespec(cp->config, valset, TRUE, TRUE); if (res != NO_ERR) { return res; } } else { /* try default config location */ res = conf_parse_val_from_filespec(YANGDIFF_DEF_CONFIG, valset, TRUE, FALSE); if (res != NO_ERR) { return res; } } /* set the logging control parameters */ val_set_logging_parms(valset); /* set the file search path parameters */ val_set_path_parms(valset); /* set the warning control parameters */ val_set_warning_parms(valset); /* set the feature code generation parameters */ val_set_feature_parms(valset); /*** ORDER DOES NOT MATTER FOR REST OF PARAMETERS ***/ /* difftype parameter */ val = val_find_child(valset, YANGDIFF_MOD, YANGDIFF_PARM_DIFFTYPE); if (val && val->res == NO_ERR) { cp->difftype = VAL_STR(val); if (!xml_strcmp(cp->difftype, YANGDIFF_DIFFTYPE_TERSE)) { cp->edifftype = YANGDIFF_DT_TERSE; } else if (!xml_strcmp(cp->difftype, YANGDIFF_DIFFTYPE_NORMAL)) { cp->edifftype = YANGDIFF_DT_NORMAL; } else if (!xml_strcmp(cp->difftype, YANGDIFF_DIFFTYPE_REVISION)) { cp->edifftype = YANGDIFF_DT_REVISION; } else { cp->edifftype = YANGDIFF_DT_NONE; } } else { cp->difftype = YANGDIFF_DEF_DIFFTYPE; cp->edifftype = YANGDIFF_DEF_DT; } /* indent parameter */ val = val_find_child(valset, YANGDIFF_MOD, YANGDIFF_PARM_INDENT); if (val && val->res == NO_ERR) { cp->indent = (int32)VAL_UINT(val); } else { cp->indent = NCX_DEF_INDENT; } /* help parameter */ val = val_find_child(valset, YANGDIFF_MOD, NCX_EL_HELP); if (val && val->res == NO_ERR) { cp->helpmode = TRUE; } /* help submode parameter (brief/normal/full) */ val = val_find_child(valset, YANGDIFF_MOD, NCX_EL_BRIEF); if (val && val->res == NO_ERR) { cp->helpsubmode = HELP_MODE_BRIEF; } else { /* full parameter */ val = val_find_child(valset, YANGDIFF_MOD, NCX_EL_FULL); if (val) { cp->helpsubmode = HELP_MODE_FULL; } else { cp->helpsubmode = HELP_MODE_NORMAL; } } /* modpath parameter */ val = val_find_child(valset, YANGDIFF_MOD, NCX_EL_MODPATH); if (val && val->res == NO_ERR) { cp->modpath = VAL_STR(val); ncxmod_set_modpath(VAL_STR(val)); } /* old parameter */ val = val_find_child(valset, YANGDIFF_MOD, YANGDIFF_PARM_OLD); if (val && val->res == NO_ERR) { cp->old = VAL_STR(val); res = NO_ERR; cp->full_old = ncx_get_source_ex(VAL_STR(val), FALSE, &res); if (!cp->full_old) { return res; } else { cp->old_isdir = ncxmod_test_subdir(cp->full_old); } } /* new parameter */ val = val_find_child(valset, YANGDIFF_MOD, YANGDIFF_PARM_NEW); if (val && val->res == NO_ERR) { cp->new = VAL_STR(val); res = NO_ERR; cp->full_new = ncx_get_source_ex(VAL_STR(val), FALSE, &res); if (!cp->full_new) { return res; } else { cp->new_isdir = ncxmod_test_subdir(cp->full_new); } } /***** THESE 2 PARMS ARE NOT VISIBLE IN yangdiff.yang *****/ /* oldpath parameter */ val = val_find_child(valset, YANGDIFF_MOD, YANGDIFF_PARM_OLDPATH); if (val && val->res == NO_ERR) { cp->oldpath = VAL_STR(val); } /* newpath parameter */ val = val_find_child(valset, YANGDIFF_MOD, YANGDIFF_PARM_NEWPATH); if (val && val->res == NO_ERR) { cp->newpath = VAL_STR(val); } /* header parameter */ val = val_find_child(valset, YANGDIFF_MOD, YANGDIFF_PARM_HEADER); if (val && val->res == NO_ERR) { cp->header = VAL_BOOL(val); } else { cp->header = TRUE; } /* subdirs parameter */ val = val_find_child(valset, YANGDIFF_MOD, NCX_EL_SUBDIRS); if (val && val->res == NO_ERR) { cp->subdirs = VAL_BOOL(val); } else { cp->subdirs = TRUE; } /* output parameter */ val = val_find_child(valset, YANGDIFF_MOD, YANGDIFF_PARM_OUTPUT); if (val && val->res == NO_ERR) { cp->output = VAL_STR(val); res = NO_ERR; cp->full_output = ncx_get_source_ex(VAL_STR(val), FALSE, &res); if (!cp->full_output) { return res; } else { cp->output_isdir = ncxmod_test_subdir(cp->full_output); } } /* else use default output -- STDOUT */ /* version parameter */ val = val_find_child(valset, YANGDIFF_MOD, NCX_EL_VERSION); if (val && val->res == NO_ERR) { cp->versionmode = TRUE; } return NO_ERR; } /* process_cli_input */ /******************************************************************** * FUNCTION main_init * * * *********************************************************************/ static status_t main_init (int argc, char *argv[]) { status_t res; /* init module static variables */ memset(&diffparms, 0x0, sizeof(yangdiff_diffparms_t)); dlq_createSQue(&diffparms.oldmodQ); dlq_createSQue(&diffparms.newmodQ); cli_val = NULL; /* initialize the NCX Library first to allow NCX modules * to be processed. No module can get its internal config * until the NCX module parser and definition registry is up * parm(TRUE) indicates all description clauses should be saved * Set debug cutoff filter to user errors */ res = ncx_init(TRUE, LOG_DEBUG_INFO, FALSE, NULL, argc, argv); if (res == NO_ERR) { /* load in the YANG converter CLI definition file */ res = ncxmod_load_module(YANGDIFF_MOD, NULL, NULL, NULL); } if (res == NO_ERR) { res = process_cli_input(argc, argv, &diffparms); } if (res != NO_ERR && res != ERR_NCX_SKIPPED) { pr_err(res); } return res; } /* main_init */ /******************************************************************** * FUNCTION main_cleanup * * * *********************************************************************/ static void main_cleanup (void) { ncx_module_t *mod; if (cli_val) { val_free_value(cli_val); } while (!dlq_empty(&diffparms.oldmodQ)) { mod = dlq_deque(&diffparms.oldmodQ); ncx_free_module(mod); } while (!dlq_empty(&diffparms.newmodQ)) { mod = dlq_deque(&diffparms.newmodQ); ncx_free_module(mod); } /* free the input parameters */ if (diffparms.scb) { ses_free_scb(diffparms.scb); } if (diffparms.curold) { m__free(diffparms.curold); } if (diffparms.curnew) { m__free(diffparms.curnew); } if (diffparms.buff) { m__free(diffparms.buff); } if (diffparms.full_old) { m__free(diffparms.full_old); } if (diffparms.full_new) { m__free(diffparms.full_new); } if (diffparms.full_output) { m__free(diffparms.full_output); } /* cleanup the NCX engine and registries */ ncx_cleanup(); } /* main_cleanup */ /******************************************************************** * * * FUNCTION main * * * *********************************************************************/ int main (int argc, char *argv[]) { status_t res; #ifdef MEMORY_DEBUG mtrace(); #endif res = main_init(argc, argv); if (res == NO_ERR) { res = main_run(); } /* if warnings+ are enabled, then res could be NO_ERR and still * have output to STDOUT */ if (res == NO_ERR) { log_warn("\n"); /*** producing extra blank lines ***/ } else { pr_err(res); } print_errors(); print_error_count(); main_cleanup(); print_error_count(); #ifdef MEMORY_DEBUG muntrace(); #endif return (res == NO_ERR) ? 0 : 1; } /* main */ /* END yangdiff.c */ yuma123_2.14/netconf/src/yangdiff/yangdiff_util.c0000664000175000017500000006254514770023131022141 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangdiff_util.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 10-jul-08 abb split out from yangdiff.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_feature #include "ncx_feature.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangdiff #include "yangdiff.h" #endif #ifndef _H_yangdiff_util #include "yangdiff_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define YANGDIFF_UTIL_DEBUG 1 /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION indent_in * * Move curindent to the right * * INPUTS: * cp == compare paraeters to use * *********************************************************************/ void indent_in (yangdiff_diffparms_t *cp) { if (cp->indent) { cp->curindent += cp->indent; ses_set_indent(cp->scb, cp->curindent); } } /* indent_in */ /******************************************************************** * FUNCTION indent_out * * Move curindent to the left * * INPUTS: * cp == compare paraeters to use * *********************************************************************/ void indent_out (yangdiff_diffparms_t *cp) { if (cp->indent) { if (cp->curindent >= cp->indent) { cp->curindent -= cp->indent; } else { cp->curindent = 0; } ses_set_indent(cp->scb, cp->curindent); } } /* indent_out */ /******************************************************************** * FUNCTION str_field_changed * * Check if the string field changed at all * * INPUTS: * fieldname == fieldname to use (may be NULL) * oldstr == old string to check (may be NULL) * newstr == new string to check (may be NULL) * isrev == TRUE if revision mode, FALSE otherwise * cdb == pointer to change description block to fill in (may be NULL) * * OUTPUTS: * *cdb is filled in, if cdb is non-NULL * * RETURNS: * 1 if field changed * 0 if field not changed * *********************************************************************/ uint32 str_field_changed (const xmlChar *fieldname, const xmlChar *oldstr, const xmlChar *newstr, boolean isrev, yangdiff_cdb_t *cdb) { uint32 ret; ret = 0; if (cdb) { cdb->fieldname = fieldname; cdb->oldval = oldstr; cdb->newval = newstr; } if (!oldstr && newstr) { ret = 1; if (cdb) { cdb->changed = TRUE; cdb->useval = newstr; cdb->chtyp = (isrev) ? ADD_STR : A_STR; } } else if (oldstr && !newstr) { ret = 1; if (cdb) { cdb->changed = TRUE; cdb->useval = oldstr; cdb->chtyp = (isrev) ? DEL_STR : D_STR; } } else if (oldstr && newstr && xml_strcmp_nosp(oldstr, newstr)) { ret = 1; if (cdb) { cdb->changed = TRUE; cdb->useval = newstr; cdb->chtyp = (isrev) ? MOD_STR : M_STR; } } else if (cdb) { cdb->changed = FALSE; cdb->useval = NULL; cdb->chtyp = NULL; } return ret; } /* str_field_changed */ /******************************************************************** * FUNCTION bool_field_changed * * Check if the boolean field changed at all * * INPUTS: * oldbool == old boolean to check * newbool == new boolean to check * isrev == TRUE if revision mode, FALSE otherwise * cdb == pointer to change description block to fill in * * OUTPUTS: * *cdb is filled in * * RETURNS: * 1 if field changed * 0 if field not changed * *********************************************************************/ uint32 bool_field_changed (const xmlChar *fieldname, boolean oldbool, boolean newbool, boolean isrev, yangdiff_cdb_t *cdb) { uint32 ret; ret = 0; if (cdb) { cdb->fieldname = fieldname; cdb->oldval = oldbool ? NCX_EL_TRUE : NCX_EL_FALSE; cdb->newval = newbool ? NCX_EL_TRUE : NCX_EL_FALSE; } if (oldbool != newbool) { ret = 1; if (cdb) { cdb->changed = TRUE; cdb->useval = cdb->newval; cdb->chtyp = (isrev) ? MOD_STR : M_STR; } } else if (cdb) { cdb->changed = FALSE; cdb->useval = NULL; cdb->chtyp = NULL; } return ret; } /* bool_field_changed */ /******************************************************************** * FUNCTION status_field_changed * * Check if the status field changed at all * * INPUTS: * oldstat == old status to check * newstat == new status to check * isrev == TRUE if revision mode, FALSE otherwise * cdb == pointer to change description block to fill in * * OUTPUTS: * *cdb is filled in * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ uint32 status_field_changed (const xmlChar *fieldname, ncx_status_t oldstat, ncx_status_t newstat, boolean isrev, yangdiff_cdb_t *cdb) { uint32 ret; ret = 0; if (cdb) { cdb->fieldname = fieldname; cdb->oldval = ncx_get_status_string(oldstat); cdb->newval = ncx_get_status_string(newstat); } if (oldstat != newstat) { ret = 1; if (cdb) { cdb->changed = TRUE; cdb->useval = cdb->newval; cdb->chtyp = (isrev) ? MOD_STR : M_STR; } } else if (cdb) { cdb->changed = FALSE; cdb->useval = NULL; cdb->chtyp = NULL; } return ret; } /* status_field_changed */ /******************************************************************** * FUNCTION prefix_field_changed * * Check if the status field changed at all * * INPUTS: * oldmod == old module to check * newmod == new module to check * oldprefix == old prefix to check * newprefix == new prefix to check * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ uint32 prefix_field_changed (const ncx_module_t *oldmod, const ncx_module_t *newmod, const xmlChar *oldprefix, const xmlChar *newprefix) { const ncx_import_t *oldimp, *newimp; if (str_field_changed(NULL, oldprefix, newprefix, FALSE, NULL)) { if (oldprefix && newprefix) { /* check if the different prefix values reference * the same module name */ oldimp = ncx_find_pre_import(oldmod, oldprefix); newimp = ncx_find_pre_import(newmod, newprefix); if (oldimp && newimp && !xml_strcmp(oldimp->module, newimp->module)) { return 0; } else { return 1; } } else if (oldprefix && !xml_strcmp(oldprefix, oldmod->prefix)) { return 0; /* using own module prefix */ } else if (newprefix && !xml_strcmp(newprefix, newmod->prefix)) { return 0; /* using own module prefix */ } else { return 1; } } else { return 0; } /*NOTREACHED*/ } /* prefix_field_changed */ /******************************************************************** * FUNCTION output_val * * Output one value; will add truncate! * * INPUTS: * cp == parameter block to use * strval == str value to output *********************************************************************/ void output_val (yangdiff_diffparms_t *cp, const xmlChar *strval) { uint32 i; ses_putchar(cp->scb, '\''); if (xml_strlen(strval) > cp->maxlen) { for (i=0; imaxlen; i++) { ses_putchar(cp->scb, strval[i]); } ses_putstr(cp->scb, (const xmlChar *)"..."); } else { ses_putstr(cp->scb, strval); } ses_putchar(cp->scb, '\''); } /* output_val */ /******************************************************************** * FUNCTION output_mstart_line * * Output one line for a comparison where the change is 'modify' * Just print the field name and identifier * * INPUTS: * cp == parameter block to use * fieldname == fieldname that is different to print * val == string value to print (may be NULL) * isid == TRUE if val is an ID * == FALSE if val should be single-quoted * (ignored in val == NULL) *********************************************************************/ void output_mstart_line (yangdiff_diffparms_t *cp, const xmlChar *fieldname, const xmlChar *val, boolean isid) { ses_putstr_indent(cp->scb, (cp->edifftype==YANGDIFF_DT_REVISION) ? MOD_STR : M_STR, cp->curindent); ses_putstr(cp->scb, fieldname); if (val) { ses_putchar(cp->scb, ' '); if (isid) { ses_putstr(cp->scb, val); } else { output_val(cp, val); } } } /* output_mstart_line */ /******************************************************************** * FUNCTION output_cdb_line * * Output one line for a comparison, from a change description block * * INPUTS: * cp == parameter block to use * cdb == change descriptor block to use *********************************************************************/ void output_cdb_line (yangdiff_diffparms_t *cp, const yangdiff_cdb_t *cdb) { boolean showval; if (!cdb->changed) { return; } showval = (cp->edifftype == YANGDIFF_DT_TERSE) ? FALSE : TRUE; ses_putstr_indent(cp->scb, cdb->chtyp, cp->curindent); ses_putstr(cp->scb, cdb->fieldname); if (showval) { ses_putchar(cp->scb, ' '); if (cdb->oldval && cdb->newval) { if ((xml_strlen(cdb->oldval) < cp->maxlen) && (xml_strlen(cdb->newval) < cp->maxlen)) { ses_putstr(cp->scb, FROM_STR); output_val(cp, cdb->oldval); ses_putchar(cp->scb, ' '); ses_putstr(cp->scb, TO_STR); output_val(cp, cdb->newval); } } else if (cdb->useval) { output_val(cp, cdb->useval); } else { ses_putstr(cp->scb, (const xmlChar *)" ''"); } } } /* output_cdb_line */ /******************************************************************** * FUNCTION output_diff * * Output one detailed difference * * INPUTS: * cp == parameter block to use * fieldname == fieldname that is different to print * oldval == string value for old rev (may be NULL) * newval == string value for new rev (may be NULL) * isid == TRUE if value is really an identifier, not just a string * *********************************************************************/ void output_diff (yangdiff_diffparms_t *cp, const xmlChar *fieldname, const xmlChar *oldval, const xmlChar *newval, boolean isid) { const xmlChar *useval, *astr, *dstr, *mstr; boolean finish, useto; useval = NULL; useto = FALSE; finish = TRUE; if (cp->edifftype==YANGDIFF_DT_REVISION) { astr = ADD_STR; dstr = DEL_STR; mstr = MOD_STR; } else { astr = A_STR; dstr = D_STR; mstr = M_STR; } if (!oldval && newval) { ses_putstr_indent(cp->scb, astr, cp->curindent); useval = newval; } else if (oldval && !newval) { ses_putstr_indent(cp->scb, dstr, cp->curindent); useval = oldval; } else if (oldval && newval) { if (xml_strcmp(oldval, newval)) { ses_putstr_indent(cp->scb, mstr, cp->curindent); useto = TRUE; } else { finish = FALSE; } } else { finish = FALSE; } if (finish) { ses_putstr(cp->scb, fieldname); if (useto) { if ((xml_strlen(oldval) < cp->maxlen) && (xml_strlen(newval) < cp->maxlen)) { ses_putchar(cp->scb, ' '); ses_putstr(cp->scb, FROM_STR); output_val(cp, oldval); ses_putchar(cp->scb, ' '); ses_putstr(cp->scb, TO_STR); output_val(cp, newval); } } else { ses_putchar(cp->scb, ' '); if (isid) { ses_putstr(cp->scb, useval); } else { output_val(cp, useval); } } } } /* output_diff */ /******************************************************************** * FUNCTION output_errinfo_diff * * Output the differences report for one ncx_errinfo_t struct * Used for may clauses, such as must, range, length, pattern; * within a leaf, leaf-list, or typedef definition * * INPUTS: * cp == parameter block to use * olderr == old err info struct (may be NULL) * newerr == new err info struct (may be NULL) * *********************************************************************/ void output_errinfo_diff (yangdiff_diffparms_t *cp, const ncx_errinfo_t *olderr, const ncx_errinfo_t *newerr) { if (!olderr && newerr) { /* errinfo added in new revision */ output_diff(cp, YANG_K_ERROR_MESSAGE, NULL, newerr->error_message, FALSE); output_diff(cp, YANG_K_ERROR_APP_TAG, NULL, newerr->error_app_tag, FALSE); output_diff(cp, YANG_K_DESCRIPTION, NULL, newerr->descr, FALSE); output_diff(cp, YANG_K_REFERENCE, NULL, newerr->ref, FALSE); } else if (olderr && !newerr) { /* errinfo removed in new revision */ output_diff(cp, YANG_K_ERROR_MESSAGE, olderr->error_message, NULL, FALSE); output_diff(cp, YANG_K_ERROR_APP_TAG, olderr->error_app_tag, NULL, FALSE); output_diff(cp, YANG_K_DESCRIPTION, olderr->descr, NULL, FALSE); output_diff(cp, YANG_K_REFERENCE, olderr->ref, NULL, FALSE); } else if (olderr && newerr) { /* errinfo maybe changed in new revision */ output_diff(cp, YANG_K_ERROR_MESSAGE, olderr->error_message, newerr->error_message, FALSE); output_diff(cp, YANG_K_ERROR_APP_TAG, olderr->error_app_tag, newerr->error_app_tag, FALSE); output_diff(cp, YANG_K_DESCRIPTION, olderr->descr, newerr->descr, FALSE); output_diff(cp, YANG_K_REFERENCE, olderr->ref, newerr->ref, FALSE); } } /* output_errinfo_diff */ /******************************************************************** * FUNCTION errinfo_changed * * Check if the ncx_errinfo_t struct changed at all * * INPUTS: * olderr == old error struct to check * newerr == new error struct to check * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ uint32 errinfo_changed (const ncx_errinfo_t *olderr, const ncx_errinfo_t *newerr) { if ((!olderr && newerr) || (olderr && !newerr)) { return 1; } else if (olderr && newerr) { if (str_field_changed(YANG_K_DESCRIPTION, olderr->descr, newerr->descr, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_REFERENCE, olderr->ref, newerr->ref, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_ERROR_APP_TAG, olderr->error_app_tag, newerr->error_app_tag, FALSE, NULL)) { return 1; } if (str_field_changed(YANG_K_ERROR_MESSAGE, olderr->error_message, newerr->error_message, FALSE, NULL)) { return 1; } return 0; } else { return 0; } /*NOTREACHED*/ } /* errinfo_changed */ /******************************************************************** * FUNCTION iffeature_changed * * Check if the ncx_iffeature_t struct changed at all * * INPUTS: * modprefix == module prefix value to use as default * oldif == old if-feature struct to check * newif == new if-feature struct to check * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ uint32 iffeature_changed (const xmlChar *modprefix, const ncx_iffeature_t *oldif, const ncx_iffeature_t *newif) { if ((!oldif && newif) || (oldif && !newif)) { return 1; } else if (oldif && newif) { if (ncx_prefix_different(oldif->prefix, newif->prefix, modprefix)) { return 1; } if (str_field_changed(YANG_K_IF_FEATURE, oldif->name, newif->name, FALSE, NULL)) { return 1; } } return 0; } /* iffeature_changed */ /******************************************************************** * FUNCTION iffeatureQ_changed * * Check if a Q of must-stmt definitions changed * * INPUTS: * modprefix == module prefix in use * oldQ == Q of old ncx_iffeature_t to use * newQ == Q of new ncx_iffeature_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ uint32 iffeatureQ_changed (const xmlChar *modprefix, dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { ncx_iffeature_t *oldif, *newif; uint32 oldcnt, newcnt; #ifdef DEBUG if (oldQ == NULL || newQ == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif oldcnt = dlq_count(oldQ); newcnt = dlq_count(newQ); if (oldcnt != newcnt) { return 1; } if (oldcnt == 0) { return 0; } for (newif = (ncx_iffeature_t *)dlq_firstEntry(newQ); newif != NULL; newif = (ncx_iffeature_t *)dlq_nextEntry(newif)) { newif->seen = FALSE; } /* look through the old Q for matching entries in the new Q */ for (oldif = (ncx_iffeature_t *)dlq_firstEntry(oldQ); oldif != NULL; oldif = (ncx_iffeature_t *)dlq_nextEntry(oldif)) { newif = ncx_find_iffeature(newQ, oldif->prefix, oldif->name, modprefix); if (newif) { if (iffeature_changed(modprefix, oldif, newif)) { return 1; } else { newif->seen = TRUE; } } else { return 1; } } /* look for iffeature-stmts that were added in the new module */ for (newif = (ncx_iffeature_t *)dlq_firstEntry(newQ); newif != NULL; newif = (ncx_iffeature_t *)dlq_nextEntry(newif)) { if (!newif->seen) { return 1; } } return 0; } /* iffeatureQ_changed */ /******************************************************************** * FUNCTION output_iffeatureQ_diff * * Output any changes in a Q of if-feature-stmt definitions * * INPUTS: * cp == comparison paraeters to use * modprefix == module prefix in use * oldQ == Q of old ncx_iffeature_t to use * newQ == Q of new ncx_iffeature_t to use * *********************************************************************/ void output_iffeatureQ_diff (yangdiff_diffparms_t *cp, const xmlChar *modprefix, dlq_hdr_t *oldQ, dlq_hdr_t *newQ) { ncx_iffeature_t *oldif, *newif; #ifdef DEBUG if (cp == NULL || oldQ == NULL || newQ == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif for (newif = (ncx_iffeature_t *)dlq_firstEntry(newQ); newif != NULL; newif = (ncx_iffeature_t *)dlq_nextEntry(newif)) { newif->seen = FALSE; } /* look through the old Q for matching entries in the new Q */ for (oldif = (ncx_iffeature_t *)dlq_firstEntry(oldQ); oldif != NULL; oldif = (ncx_iffeature_t *)dlq_nextEntry(oldif)) { newif = ncx_find_iffeature(newQ, oldif->prefix, oldif->name, modprefix); if (newif) { newif->seen = TRUE; if (iffeature_changed(modprefix, oldif, newif)) { output_mstart_line(cp, YANG_K_IF_FEATURE, oldif->name, FALSE); if (cp->edifftype != YANGDIFF_DT_TERSE) { indent_in(cp); output_diff(cp, YANG_K_IF_FEATURE, oldif->name, newif->name, TRUE); indent_out(cp); } } } else { /* if-feature-stmt was removed from the new module */ output_diff(cp, YANG_K_IF_FEATURE, oldif->name, NULL, FALSE); } } /* look for must-stmts that were added in the new module */ for (newif = (ncx_iffeature_t *)dlq_firstEntry(newQ); newif != NULL; newif = (ncx_iffeature_t *)dlq_nextEntry(newif)) { if (!newif->seen) { /* must-stmt was added in the new module */ output_diff(cp, YANG_K_IF_FEATURE, NULL, newif->name, FALSE); } } } /* output_iffeatureQ_diff */ /* END yangdiff_util.c */ yuma123_2.14/netconf/src/yangdiff/yangdiff_util.h0000664000175000017500000002341014770023131022132 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangdiff_util #define _H_yangdiff_util /* FILE: yangdiff_util.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Report differences utilities ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 10-jul-08 abb Split out from yangdiff.c */ #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_yangdiff #include "yangdiff.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION indent_in * * Move curindent to the right * * INPUTS: * cp == compare paraeters to use * *********************************************************************/ extern void indent_in (yangdiff_diffparms_t *cp); /******************************************************************** * FUNCTION indent_out * * Move curindent to the left * * INPUTS: * cp == compare paraeters to use * *********************************************************************/ extern void indent_out (yangdiff_diffparms_t *cp); /******************************************************************** * FUNCTION str_field_changed * * Check if the string field changed at all * * INPUTS: * fieldname == fieldname to use (may be NULL) * oldstr == old string to check (may be NULL) * newstr == new string to check (may be NULL) * isrev == TRUE if revision mode, FALSE otherwise * cdb == pointer to change description block to fill in (may be NULL) * * OUTPUTS: * *cdb is filled in, if cdb is non-NULL * * RETURNS: * 1 if field changed * 0 if field not changed * *********************************************************************/ extern uint32 str_field_changed (const xmlChar *fieldname, const xmlChar *oldstr, const xmlChar *newstr, boolean isrev, yangdiff_cdb_t *cdb); /******************************************************************** * FUNCTION bool_field_changed * * Check if the boolean field changed at all * * INPUTS: * oldbool == old boolean to check * newbool == new boolean to check * isrev == TRUE if revision mode, FALSE otherwise * cdb == pointer to change description block to fill in * * OUTPUTS: * *cdb is filled in * * RETURNS: * 1 if field changed * 0 if field not changed * *********************************************************************/ extern uint32 bool_field_changed (const xmlChar *fieldname, boolean oldbool, boolean newbool, boolean isrev, yangdiff_cdb_t *cdb); /******************************************************************** * FUNCTION status_field_changed * * Check if the status field changed at all * * INPUTS: * oldstat == old status to check * newstat == new status to check * isrev == TRUE if revision mode, FALSE otherwise * cdb == pointer to change description block to fill in * * OUTPUTS: * *cdb is filled in * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ extern uint32 status_field_changed (const xmlChar *fieldname, ncx_status_t oldstat, ncx_status_t newstat, boolean isrev, yangdiff_cdb_t *cdb); /******************************************************************** * FUNCTION prefix_field_changed * * Check if the status field changed at all * * INPUTS: * oldmod == old module to check * newmod == new module to check * oldprefix == old prefix to check * newprefix == new prefix to check * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ extern uint32 prefix_field_changed (const ncx_module_t *oldmod, const ncx_module_t *newmod, const xmlChar *oldprefix, const xmlChar *newprefix); /******************************************************************** * FUNCTION output_val * * Output one value; will add truncate! * * INPUTS: * cp == parameter block to use * strval == str value to output *********************************************************************/ extern void output_val (yangdiff_diffparms_t *cp, const xmlChar *strval); /******************************************************************** * FUNCTION output_mstart_line * * Output one line for a comparison where the change is 'modify' * Just print the field name and identifier * * INPUTS: * cp == parameter block to use * fieldname == fieldname that is different to print * val == string value to print (may be NULL) * isid == TRUE if val is an ID * == FALSE if val should be single-quoted * (ignored in val == NULL) *********************************************************************/ extern void output_mstart_line (yangdiff_diffparms_t *cp, const xmlChar *fieldname, const xmlChar *val, boolean isid); /******************************************************************** * FUNCTION output_cdb_line * * Output one line for a comparison, from a change description block * * INPUTS: * cp == parameter block to use * cdb == change descriptor block to use *********************************************************************/ extern void output_cdb_line (yangdiff_diffparms_t *cp, const yangdiff_cdb_t *cdb); /******************************************************************** * FUNCTION output_diff * * Output one detailed difference * * INPUTS: * cp == parameter block to use * fieldname == fieldname that is different to print * oldval == string value for old rev (may be NULL) * newval == string value for new rev (may be NULL) * isid == TRUE if value is really an identifier, not just a string * *********************************************************************/ extern void output_diff (yangdiff_diffparms_t *cp, const xmlChar *fieldname, const xmlChar *oldval, const xmlChar *newval, boolean isid); /******************************************************************** * FUNCTION output_errinfo_diff * * Output the differences report for one ncx_errinfo_t struct * Used for may clauses, such as must, range, length, pattern; * within a leaf, leaf-list, or typedef definition * * INPUTS: * cp == parameter block to use * olderr == old err info struct (may be NULL) * newerr == new err info struct (may be NULL) * *********************************************************************/ extern void output_errinfo_diff (yangdiff_diffparms_t *cp, const ncx_errinfo_t *olderr, const ncx_errinfo_t *newerr); /******************************************************************** * FUNCTION errinfo_changed * * Check if the ncx_errinfo_t struct changed at all * * INPUTS: * olderr == old error struct to check * newerr == new error struct to check * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ extern uint32 errinfo_changed (const ncx_errinfo_t *olderr, const ncx_errinfo_t *newerr); /******************************************************************** * FUNCTION iffeature_changed * * Check if the ncx_iffeature_t struct changed at all * * INPUTS: * modprefix == module prefix value to use as default * oldif == old if-feature struct to check * newif == new if-feature struct to check * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ extern uint32 iffeature_changed (const xmlChar *modprefix, const ncx_iffeature_t *oldif, const ncx_iffeature_t *newif); /******************************************************************** * FUNCTION iffeatureQ_changed * * Check if a Q of must-stmt definitions changed * * INPUTS: * modprefix == module prefix in use * oldQ == Q of old ncx_iffeature_t to use * newQ == Q of new ncx_iffeature_t to use * * RETURNS: * 1 if field changed * 0 if field not changed *********************************************************************/ extern uint32 iffeatureQ_changed (const xmlChar *modprefix, dlq_hdr_t *oldQ, dlq_hdr_t *newQ); /******************************************************************** * FUNCTION output_iffeatureQ_diff * * Output any changes in a Q of if-feature-stmt definitions * * INPUTS: * cp == comparison paraeters to use * modprefix == module prefix in use * oldQ == Q of old ncx_iffeature_t to use * newQ == Q of new ncx_iffeature_t to use * *********************************************************************/ extern void output_iffeatureQ_diff (yangdiff_diffparms_t *cp, const xmlChar *modprefix, dlq_hdr_t *oldQ, dlq_hdr_t *newQ); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangdiff_util */ yuma123_2.14/netconf/src/yangrpc/0000775000175000017500000000000014770023131017011 5ustar vladimirvladimiryuma123_2.14/netconf/src/yangrpc/libapache2-mod-yangrpc-example/0000775000175000017500000000000014770023131024652 5ustar vladimirvladimiryuma123_2.14/netconf/src/yangrpc/libapache2-mod-yangrpc-example/yangrpc_example.load0000664000175000017500000000012214770023131030664 0ustar vladimirvladimirLoadModule yangrpc_example_module /usr/lib/apache2/modules/mod_yangrpc_example.so yuma123_2.14/netconf/src/yangrpc/libapache2-mod-yangrpc-example/README0000664000175000017500000000156014770023131025534 0ustar vladimirvladimir=Description= Example module for Apache using the Yuma API to execute RPCs over netconf =Configuration= #edit yangrpc_example.conf =Dependencies= sudo apt-get install apache2-dev =Building and installing= autoreconf -i -f ./configure CFLAGS="-g -O0" CXXFLAGS="-g -O0" --prefix=/usr make sudo make install Enable the module: sudo a2enmod yangrpc_example =Generating SSH keys for passwordless access= To avoid keeping passwords in plaintext generate dedicated key pair: sudo su mkdir /var/www/.ssh ssh-keygen -t rsa -f /var/www/.ssh/id_rsa -P "" chown www-data:www-data /var/www/.ssh/id_rsa* cat /var/www/.ssh/id_rsa.pub | tee -a /root/.ssh/authorized_keys =Running= Start netconfd: sudo netconfd --no-startup Start apache: sudo su source /etc/apache2/envvars gdb --args /usr/sbin/apache2 -k start -X -e Debug Load the web link: wget http://localhost/state.xml yuma123_2.14/netconf/src/yangrpc/libapache2-mod-yangrpc-example/debian/0000775000175000017500000000000014770023131026074 5ustar vladimirvladimiryuma123_2.14/netconf/src/yangrpc/libapache2-mod-yangrpc-example/debian/control0000664000175000017500000000126214770023131027500 0ustar vladimirvladimirSource: libapache2-mod-yangrpc-example Section: net Priority: optional Maintainer: Vladimir Vassilev Build-Depends: debhelper (>= 10), apache2-dev, autoconf, libtool, libxml2-dev, libyangrpc-dev (>= 2.14), libyuma-dev (>= 2.14), pkg-config Standards-Version: 4.1.4 Homepage: https://yuma123.org Package: libapache2-mod-yangrpc-example Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, debconf, lsb-base, libyangrpc2 (>= 2.12), apache2 Description: Example apache2 module using libyangrpc2 API to retrieve YANG data over NETCONF and represent it as HTML. . The module needs to be configured to connect to running NETCONF server. yuma123_2.14/netconf/src/yangrpc/libapache2-mod-yangrpc-example/debian/rules0000775000175000017500000000005714770023131027156 0ustar vladimirvladimir#!/usr/bin/make -f %: dh $@ --with autoreconf yuma123_2.14/netconf/src/yangrpc/libapache2-mod-yangrpc-example/debian/compat0000664000175000017500000000000314770023131027273 0ustar vladimirvladimir10 yuma123_2.14/netconf/src/yangrpc/libapache2-mod-yangrpc-example/debian/changelog0000664000175000017500000000026514770023131027751 0ustar vladimirvladimirlibapache2-mod-yangrpc-example (2.14-0) stable; urgency=medium * Unofficial package -- Vladimir Vassilev Sun, 23 Mar 2025 15:25:36 +0100 yuma123_2.14/netconf/src/yangrpc/libapache2-mod-yangrpc-example/yangrpc_example.conf0000664000175000017500000000036514770023131030703 0ustar vladimirvladimir ServerAddress 127.0.0.1 ServerPort 830 Username root Password mysecretpass PrivateKeyPath /var/www/.ssh/id_rsa PublicKeyPath /var/www/.ssh/id_rsa.pub LogLevel debug yuma123_2.14/netconf/src/yangrpc/libapache2-mod-yangrpc-example/mod_yangrpc_example.c0000664000175000017500000006716114770023131031046 0ustar vladimirvladimir/* Include the required headers from httpd */ #define _GNU_SOURCE #include "httpd.h" #include "http_core.h" #include "http_protocol.h" #include "http_request.h" #include "http_config.h" #include "http_log.h" #include #include #include #include #include #include #include #include #include #include #include /* YANG database access headers */ #include "ncx.h" #include "obj.h" #include "val.h" #include "val123.h" #include "val_util.h" #include "val_set_cplxval_obj.h" #include "xml_rd.h" #include "xml_wr.h" #include "yangrpc.h" static char* server_address; static int server_port; static char* username; static char* password; static char* private_key_path; static char* public_key_path; static char* init_args; /* Define prototypes of our functions in this module */ static void register_hooks(apr_pool_t *pool); static int example_handler(request_rec *r); typedef struct { yangrpc_cb_ptr_t yangrpc_cb_ptr; } my_svr_cfg ; void my_ap_rprintf_uint64(request_rec * r, uint64_t u64) { char buf[20+1]; sprintf(buf,"%llu",u64); ap_rprintf(r, "%s", buf); } static void* my_create_svr_conf(apr_pool_t* pool, server_rec* svr) { my_svr_cfg* svr_cfg = (my_svr_cfg*)apr_pcalloc(pool, sizeof(my_svr_cfg)); /* Set up the default values for fields of svr */ svr_cfg->yangrpc_cb_ptr=NULL; return svr_cfg; } const char* server_address_cmd_func(cmd_parms* cmd, void* cfg, const char* arg) { server_address=strdup(arg); return NULL; } const char* server_port_cmd_func(cmd_parms* cmd, void* cfg, const char* arg) { server_port=atoi(arg); return NULL; } const char* username_cmd_func(cmd_parms* cmd, void* cfg, const char* arg) { username=strdup(arg); return NULL; } const char* password_cmd_func(cmd_parms* cmd, void* cfg, const char* arg) { password=strdup(arg); return NULL; } const char* private_key_path_cmd_func(cmd_parms* cmd, void* cfg, const char* arg) { private_key_path=strdup(arg); return NULL; } const char* public_key_path_cmd_func(cmd_parms* cmd, void* cfg, const char* arg) { public_key_path=strdup(arg); return NULL; } const char* init_args_cmd_func(cmd_parms* cmd, void* cfg, const char* arg) { init_args=strdup(arg); return NULL; } static const command_rec my_cmds[] = { AP_INIT_TAKE1("ServerAddress", server_address_cmd_func, NULL/*my_ptr*/, OR_ALL, "Server address e.g. 127.0.0.1 or myserver.org"), AP_INIT_TAKE1("ServerPort", server_port_cmd_func, NULL/*my_ptr*/, OR_ALL, "Server port e.g. 830"), AP_INIT_TAKE1("Username", username_cmd_func, NULL/*my_ptr*/, OR_ALL, "Username e.g. root"), AP_INIT_TAKE1("Password", password_cmd_func, NULL/*my_ptr*/, OR_ALL, "Password e.g. mypass"), AP_INIT_TAKE1("PrivateKeyPath", private_key_path_cmd_func, NULL/*my_ptr*/, OR_ALL, "Private key path e.g. /root/.ssh/id_rsa"), AP_INIT_TAKE1("PublicKeyPath", public_key_path_cmd_func, NULL/*my_ptr*/, OR_ALL, "Public key path e.g. /root/.ssh/id_rsa.pub"), AP_INIT_TAKE1("InitArgs", init_args_cmd_func, NULL/*my_ptr*/, OR_ALL, "Init args e.g. --log-level=debug4"), /* more directives as applicable */ { NULL } }; /* Define our module as an entity and assign a function for registering hooks */ module AP_MODULE_DECLARE_DATA yangrpc_example_module = { STANDARD20_MODULE_STUFF, NULL, // Per-directory configuration handler NULL, // Merge handler for per-directory configurations my_create_svr_conf, // Per-server configuration handler NULL, // Merge handler for per-server configurations my_cmds, // Any directives we may have for httpd register_hooks // Our hook registering function }; static char* get_username(int uid) { struct passwd pwd; struct passwd *result; char* username; char *buf; size_t bufsize; int s; bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) /* Value was indeterminate */ bufsize = 16384; /* Should be more than enough */ buf = malloc(bufsize); if (buf == NULL) { return NULL; } s = getpwuid_r(uid, &pwd, buf, bufsize, &result); if (result == NULL) { free(buf); return NULL; } username = malloc(strlen(pwd.pw_gecos)+1); strcpy(username, pwd.pw_name); free(buf); return username; } static int example_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) { server_address = strdup("127.0.0.1"); server_port = 830; username = strdup("root"); password = strdup("mysecretpass"); private_key_path = strdup("/var/www/.ssh/id_rsa"); public_key_path = strdup("/var/www/.ssh/id_rsa.pub"); init_args = "--keep-session-model-copies-after-compilation=false"; return OK; } /* register_hooks: Adds a hook to the httpd process */ static void register_hooks(apr_pool_t *pool) { ap_hook_pre_config(example_pre_config, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(example_handler, NULL, NULL, APR_HOOK_LAST); } static ssize_t writer_fn(void *cookie, const void *buffer, size_t size) { size_t bytes_sent=0; while(bytes_sentparent->parent,"ietf-interfaces","name"); ret = snprintf(xpath_str, 0,"/interfaces-state/interface[name='%s']/statistics/%s", VAL_STRING(name_val), obj_get_name(counter_abs_val->obj)); xpath_str=(char*)malloc(ret+1); assert(xpath_str); sprintf(xpath_str, "/interfaces-state/interface[name='%s']/statistics/%s", VAL_STRING(name_val), obj_get_name(counter_abs_val->obj)); res = xpath_find_val_target(root_base_val, NULL/*mod*/, xpath_str , &base_val); assert(res==NO_ERR); if(base_val) { counter -= VAL_UINT64(base_val); } free(xpath_str); return counter; } void serialize_ietf_interfaces_state_val(request_rec *r, val_value_t* root_val) { int i; char timestamp_string[]="2017-03-05T14:59:00+1234"; struct tm* time_info; static val_value_t* root_epoch_val=NULL; static val_value_t* root_prev_val=NULL; static time_t ts_epoch=0; static time_t ts_cur; static time_t ts_prev; obj_template_t* interfaces_state_obj; obj_template_t* interface_obj; obj_template_t* statistics_obj; obj_template_t* statistic_obj; val_value_t* interfaces_state_val; val_value_t* interface_val; val_value_t* interfaces_state_select_val; boolean xpath_filtered=FALSE; ts_cur=time(NULL); if(r->args && strlen(r->args)>strlen("xpath=") && 0==memcmp(r->args,"xpath=",strlen("xpath="))) { xpath_filtered=TRUE; } interfaces_state_val = val_find_child(root_val, "ietf-interfaces", "interfaces-state"); if(interfaces_state_val==NULL) { ap_rprintf(r, "Seems the ietf-interfaces:interfaces-state container does not exist on your device. No statistics!"); return; } interface_obj = obj_find_child(interfaces_state_val->obj, "ietf-interfaces", "interface"); assert(interface_obj); statistics_obj = obj_find_child(interface_obj, "ietf-interfaces", "statistics"); assert(statistics_obj); if(r->args && 0==strcmp(r->args,"clear=1")) { if(root_epoch_val!=NULL) { val_free_value(root_epoch_val); } ts_epoch=ts_cur; root_epoch_val = val_clone(root_val); if(root_prev_val!=NULL) { val_free_value(root_prev_val); } root_prev_val = NULL; } ap_rprintf(r, "\ \ \ \

Statistics

\
"); ap_rprintf(r, "\ \ \ "); time_info = localtime(&ts_epoch); strftime(timestamp_string, sizeof(timestamp_string), "%Y-%m-%dT%H:%M:%S%z", time_info); ap_rprintf(r, "", timestamp_string); if(root_prev_val!=NULL) { time_info = localtime(&ts_prev); strftime(timestamp_string, sizeof(timestamp_string), "%Y-%m-%dT%H:%M:%S%z", time_info); ap_rprintf(r, "", timestamp_string); time_info = localtime(&ts_cur); strftime(timestamp_string, sizeof(timestamp_string), "%Y-%m-%dT%H:%M:%S%z", time_info); ap_rprintf(r, "", timestamp_string); ap_rprintf(r, "", ts_cur-ts_prev); } ap_rprintf(r, "
Epoch:%s
Tic:%s
Toc:%s
Interval (sec):%lu
"); ap_rprintf(r, "\ \ \ "); for(statistic_obj = obj_first_child(statistics_obj); statistic_obj != NULL; statistic_obj = obj_next_child(statistic_obj)) { ncx_btype_t btyp; int colspan; btyp=obj_get_basetype(statistic_obj); if(btyp!=NCX_BT_UINT32 && btyp!=NCX_BT_UINT64) { continue; } /* check if objects with no data should be skipped */ if(xpath_filtered) { interfaces_state_select_val = val123_select_obj(interfaces_state_val, statistic_obj); if(interfaces_state_select_val==NULL) { continue; } else { val_free_value(interfaces_state_select_val); } } if(0==strcmp(obj_get_name(statistic_obj),"in-octets") || 0==strcmp(obj_get_name(statistic_obj),"out-octets")) { colspan=3; } else { colspan=2; } ap_rprintf(r, "", obj_get_description(statistic_obj)?(char*)obj_get_description(statistic_obj):"", colspan, obj_get_name(statistic_obj)); } ap_rprintf(r, ""); ap_rprintf(r, "\ "); for(statistic_obj = obj_first_child(statistics_obj); statistic_obj != NULL; statistic_obj = obj_next_child(statistic_obj)) { ncx_btype_t btyp; btyp=obj_get_basetype(statistic_obj); if(btyp!=NCX_BT_UINT32 && btyp!=NCX_BT_UINT64) { continue; } /* check if objects with no data should be skipped */ if(xpath_filtered) { interfaces_state_select_val = val123_select_obj(interfaces_state_val, statistic_obj); if(interfaces_state_select_val==NULL) { continue; } else { val_free_value(interfaces_state_select_val); } } ap_rprintf(r, ""); if(0==strcmp(obj_get_name(statistic_obj),"in-octets") || 0==strcmp(obj_get_name(statistic_obj),"out-octets")) { ap_rprintf(r, ""); } } ap_rprintf(r, ""); for (interface_val = val_get_first_child(interfaces_state_val); interface_val != NULL; interface_val = val_get_next_child(interface_val)) { val_value_t* name_val; val_value_t* statistics_val; val_value_t* speed_val; name_val = val_find_child(interface_val,"ietf-interfaces","name"); ap_rprintf(r, "", VAL_STRING(name_val), VAL_STRING(name_val)); statistics_val = val_find_child(interface_val, "ietf-interfaces", "statistics"); speed_val = val_find_child(interface_val, "ietf-interfaces", "speed"); for(statistic_obj = obj_first_child(statistics_obj); statistic_obj != NULL; statistic_obj = obj_next_child(statistic_obj)) { val_value_t* val; uint64_t rate; uint64_t speed_in_bytes; ncx_btype_t btyp; btyp=obj_get_basetype(statistic_obj); if(btyp!=NCX_BT_UINT32 && btyp!=NCX_BT_UINT64) { continue; } /* check if objects with no data should be skipped */ if(xpath_filtered) { interfaces_state_select_val = val123_select_obj(interfaces_state_val, statistic_obj); if(interfaces_state_select_val==NULL) { continue; } else { val_free_value(interfaces_state_select_val); } } if(speed_val) { speed_in_bytes=VAL_UINT64(speed_val)/8; } else { speed_in_bytes=100000000/8; /* workaround use 100Mb */ } val = val_find_child(statistics_val, obj_get_mod_name(statistic_obj), obj_get_name(statistic_obj)); ap_rprintf(r, ""); /* per sec. */ ap_rprintf(r, ""); if(0!=strcmp(obj_get_name(statistic_obj),"in-octets") && 0!=strcmp(obj_get_name(statistic_obj),"out-octets")) { continue; } /* % */ /* per sec. */ ap_rprintf(r, ""); } } ap_rprintf(r, "%s","
name%s
typeabs"); ap_rprintf(r, "rate%%
%s"); if(val!=NULL) { uint64_t counter; //counter = VAL_UINT64(val); counter=get_counter(val,root_epoch_val); my_ap_rprintf_uint64(r,counter); } ap_rprintf(r, ""); if(val!=NULL && root_prev_val!=NULL && (ts_cur-ts_prev)!=0) { uint64_t counter; //counter = VAL_UINT64(val); counter=get_counter(val,root_prev_val); rate=counter/(ts_cur-ts_prev); if(root_prev_val!=NULL) { my_ap_rprintf_uint64(r,rate); } } ap_rprintf(r, ""); if(val!=NULL && root_prev_val!=NULL && (ts_cur-ts_prev)!=0) { uint64_t counter; //counter = VAL_UINT64(val); counter=get_counter(val,root_prev_val); rate=counter/(ts_cur-ts_prev); if(root_prev_val!=NULL) { my_ap_rprintf_uint64(r,100*rate/speed_in_bytes); ap_rprintf(r, " %%"); } } ap_rprintf(r, "
"); if(root_prev_val!=NULL) { val_free_value(root_prev_val); } root_prev_val = val_clone(root_val); ts_prev=ts_cur; } void serialize_val(request_rec *r, val_value_t* root_val) { status_t res; FILE* fp; cookie_io_functions_t io_functions; xml_attrs_t attrs; val_nodetest_fn_t testfn; xml_init_attrs(&attrs); io_functions.read=NULL; io_functions.write=writer_fn; io_functions.seek=NULL; io_functions.close=NULL; fp=fopencookie (r, "w", io_functions); ap_set_content_type(r, "application/xml;charset=utf-8"); if(0==strcmp(r->uri, "/config.xml")) { testfn=nodetest_fn; } else { testfn=NULL; } res = xml_wr_check_open_file(fp, root_val, &attrs, TRUE/*docmode*/, FALSE/*xmlhdr*/, TRUE/*withns*/, 0/*startindent*/, 4/*indent*/, testfn); fclose(fp); } static int util_read(request_rec *r, const char **rbuf) { int rc; if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) != OK) { return rc; } if (ap_should_client_block(r)) { char buff[AP_IOBUFSIZE]; int rsize, len_read, rpos=0; long length = r->remaining; *rbuf = apr_pcalloc(r->pool, length + 1); while ((len_read = ap_get_client_block(r, buff, sizeof(buff)))>0) { if ((rpos + len_read) > length) { rsize = length - rpos; } else { rsize = len_read; } memcpy((char*)*rbuf + rpos, buff, rsize); rpos += rsize; } } return rc; } #define DEFAULT_ENCTYPE "application/x-www-form-urlencoded" static int read_post(request_rec *r, apr_table_t **tab) { const char *data; const char *key, *val, *type; int rc = OK; if(r->method_number != M_POST) { return rc; } type = apr_table_get(r->headers_in, "Content-Type"); if(strcasecmp(type, DEFAULT_ENCTYPE) != 0) { return DECLINED; } if((rc = util_read(r, &data)) != OK) { return rc; } *tab = apr_table_make(r->pool, 8); while(*data && (val = ap_getword(r->pool, &data, '&'))) { key = ap_getword(r->pool, &val, '='); ap_unescape_url((char*)key); { char *s; for(s = val; *s; ++s) { if ('+' == *s) { *s = ' '; } } } ap_unescape_url((char*)val); apr_table_merge(*tab, key, val); } return OK; } static int edit_config_form(request_rec *r) { ap_rprintf(r, "%s","
"); return OK; } static int edit_config(request_rec *r) { status_t res; ncx_module_t * netconf_mod; apr_table_t *tab=NULL; const char* config_xml_str; obj_template_t* root_obj; val_value_t* root_val; int rc; obj_template_t* edit_config_rpc_obj; obj_template_t* input_obj; obj_template_t* commit_rpc_obj; val_value_t* edit_config_rpc_val; val_value_t* edit_config_rpc_reply_val; val_value_t* commit_rpc_val; val_value_t* commit_rpc_reply_val; char* rpc_format_str; char* rpc_str; my_svr_cfg* svr_cfg; FILE *fp = NULL; rc = read_post(r, &tab); if(rc!=OK) return rc; config_xml_str = apr_table_get(tab, "config") ; if(config_xml_str==NULL) { return DECLINED; } //ap_rprintf(r,"%s",config_xml_str); svr_cfg = ap_get_module_config(r->server->module_config, &yangrpc_example_module); #if 0 rc = val_set_cplxval_obj(dst_val,dst_val->obj,config_xml_str); if(rc != NO_ERR) { return DECLINED; } #else fp = fmemopen((void*)config_xml_str, strlen(config_xml_str), "r"); res = ncxmod_load_module (NCXMOD_NETCONF, NULL, NULL, &netconf_mod); root_obj = ncx_find_object(netconf_mod, "config"); rc = xml_rd_open_file (fp, root_obj, &root_val); edit_config_rpc_obj = ncx_find_object(netconf_mod, "edit-config"); assert(obj_is_rpc(edit_config_rpc_obj)); input_obj = obj_find_child(edit_config_rpc_obj, NULL, "input"); assert(input_obj!=NULL); rpc_format_str = "replace%s"; rpc_str = malloc(strlen(rpc_format_str)+strlen(config_xml_str)+1); sprintf(rpc_str, rpc_format_str, config_xml_str); edit_config_rpc_val = val_new_value(); val_init_from_template(edit_config_rpc_val, edit_config_rpc_obj); res = val_set_cplxval_obj(edit_config_rpc_val, input_obj, rpc_str); free(rpc_str); if(res != NO_ERR) { val_free_value(edit_config_rpc_val); edit_config_rpc_val = NULL; edit_config_rpc_reply_val=NULL; } else { res = yangrpc_exec(svr_cfg->yangrpc_cb_ptr, edit_config_rpc_val, &edit_config_rpc_reply_val); assert(res==NO_ERR); } if(edit_config_rpc_reply_val!=NULL && NULL!=val_find_child(edit_config_rpc_reply_val,NULL,"ok")) { commit_rpc_obj = ncx_find_object(netconf_mod, "commit"); assert(obj_is_rpc(commit_rpc_obj)); commit_rpc_val = val_new_value(); val_init_from_template(commit_rpc_val, commit_rpc_obj); res = yangrpc_exec(svr_cfg->yangrpc_cb_ptr, commit_rpc_val, &commit_rpc_reply_val); assert(res==NO_ERR); } else { commit_rpc_val=NULL; commit_rpc_reply_val=NULL; } #endif ap_rprintf(r,""); if(edit_config_rpc_val) { serialize_val(r, edit_config_rpc_val); val_free_value(edit_config_rpc_val); } if(edit_config_rpc_reply_val) { serialize_val(r, edit_config_rpc_reply_val); val_free_value(edit_config_rpc_reply_val); } if(commit_rpc_val) { serialize_val(r, commit_rpc_val); val_free_value(commit_rpc_val); } if(commit_rpc_reply_val) { serialize_val(r, commit_rpc_reply_val); val_free_value(commit_rpc_reply_val); } ap_rprintf(r,""); apr_table_clear(tab); return OK; } static int ietf_interfaces_state_report(request_rec *r) { status_t res; ncx_module_t * netconf_mod; apr_table_t *tab=NULL; const char* config_xml_str; obj_template_t* root_obj; obj_template_t* rpc_obj; obj_template_t* filter_obj; val_value_t* root_val; val_value_t* request_val; val_value_t* filter_val; val_value_t* select_meta_val; val_value_t* type_meta_val; val_value_t* reply_val; int rc; obj_template_t* input_obj; char* rpc_format_str; char* rpc_str; my_svr_cfg* svr_cfg; FILE *fp = NULL; rc = read_post(r, &tab); if(rc!=OK) return rc; svr_cfg = ap_get_module_config(r->server->module_config, &yangrpc_example_module); res = ncxmod_load_module (NCXMOD_NETCONF, NULL, NULL, &netconf_mod); assert(res==NO_ERR); rpc_obj = ncx_find_object(netconf_mod, "get"); assert(obj_is_rpc(rpc_obj)); input_obj = obj_find_child(rpc_obj, NULL, "input"); assert(input_obj!=NULL); filter_obj = obj_find_child(input_obj, NULL, "filter"); assert(filter_obj!=NULL); request_val = val_new_value(); val_init_from_template(request_val, rpc_obj); filter_val = val_new_value(); val_init_from_template(filter_val, filter_obj); type_meta_val = val_make_string(0, "type","xpath"); if(r->args && strlen(r->args)>strlen("xpath=") && 0==memcmp(r->args,"xpath=",strlen("xpath="))) { char* xpath_buf = strdup(r->args+strlen("xpath=")); ap_unescape_url(xpath_buf); select_meta_val = val_make_string(0, "select", xpath_buf); free(xpath_buf); } else { select_meta_val = val_make_string(0, "select", "/interfaces-state"); } val_add_meta(select_meta_val, filter_val); val_add_meta(type_meta_val, filter_val); val_add_child(filter_val, request_val); res = yangrpc_exec(svr_cfg->yangrpc_cb_ptr, request_val, &reply_val); assert(res==NO_ERR); { obj_template_t* config_obj; val_value_t* config_val; val_value_t* data_val; val_value_t* interfaces_val; val_value_t* interface_val; char* interface_row_str[512]; data_val = val_find_child(reply_val,NULL,"data"); config_obj = ncx_find_object(netconf_mod, "config"); config_val = val_new_value(); val_init_from_template(config_val, config_obj); val_move_children(data_val,config_val); //serialize_val(r, config_val); serialize_ietf_interfaces_state_val(r,config_val); val_free_value(config_val); } val_free_value(request_val); val_free_value(reply_val); //ap_rprintf(r,""); return OK; } /* The handler function for our module. * This is where all the fun happens! */ static int example_handler(request_rec *r) { status_t res; ncx_module_t * netconf_mod; obj_template_t* rpc_obj; obj_template_t* input_obj; obj_template_t* filter_obj; val_value_t* request_val; val_value_t* reply_val; val_value_t* filter_val; val_value_t* type_meta_val; val_value_t* select_meta_val; my_svr_cfg* svr_cfg; /* First off, we need to check if this is a call for the "example" handler. * If it is, we accept it and do our things, it not, we simply return DECLINED, * and Apache will try somewhere else. */ if (!r->handler || (0!=strcmp(r->uri, "/config.xml") && 0!=strcmp(r->uri, "/state.xml") && 0!=strcmp(r->uri, "/edit-config.html") && 0!=strcmp(r->uri, "/edit-config.xml") && 0!=strcmp(r->uri, "/ietf-interfaces-state.html") )) return (DECLINED); svr_cfg = ap_get_module_config(r->server->module_config, &yangrpc_example_module); if(svr_cfg->yangrpc_cb_ptr == NULL) { int res; res = yangrpc_init(init_args); assert(res==NO_ERR); res = yangrpc_connect(server_address, server_port, username, password, public_key_path, private_key_path, NULL /*extra_args*/, &svr_cfg->yangrpc_cb_ptr); if(res!=NO_ERR) { assert(0); return OK; } } res = ncxmod_load_module (NCXMOD_NETCONF, NULL, NULL, &netconf_mod); assert(res==NO_ERR); if(0==strcmp(r->uri, "/edit-config.html")) { return edit_config_form(r); } else if(0==strcmp(r->uri, "/ietf-interfaces-state.html")) { return ietf_interfaces_state_report(r); } else if(0==strcmp(r->uri, "/edit-config.xml")) { return edit_config(r); } rpc_obj = ncx_find_object(netconf_mod, "get"); assert(obj_is_rpc(rpc_obj)); input_obj = obj_find_child(rpc_obj, NULL, "input"); assert(input_obj!=NULL); filter_obj = obj_find_child(input_obj, NULL, "filter"); assert(filter_obj!=NULL); request_val = val_new_value(); val_init_from_template(request_val, rpc_obj); filter_val = val_new_value(); val_init_from_template(filter_val, filter_obj); type_meta_val = val_make_string(0, "type","xpath"); select_meta_val = val_make_string(0, "select", "/"); val_add_meta(select_meta_val, filter_val); val_add_meta(type_meta_val, filter_val); val_add_child(filter_val, request_val); res = yangrpc_exec(svr_cfg->yangrpc_cb_ptr, request_val, &reply_val); assert(res==NO_ERR); { obj_template_t* config_obj; val_value_t* config_val; val_value_t* data_val; val_value_t* interfaces_val; val_value_t* interface_val; char* interface_row_str[512]; data_val = val_find_child(reply_val,NULL,"data"); config_obj = ncx_find_object(netconf_mod, "config"); config_val = val_new_value(); val_init_from_template(config_val, config_obj); val_move_children(data_val,config_val); serialize_val(r, config_val); val_free_value(config_val); } val_free_value(request_val); val_free_value(reply_val); return OK; } yuma123_2.14/netconf/src/yangrpc/libapache2-mod-yangrpc-example/Makefile.am0000775000175000017500000000073314770023131026714 0ustar vladimirvladimirapachemodulelib_LTLIBRARIES = mod_yangrpc_example.la mod_yangrpc_example_la_SOURCES = \ $(top_srcdir)/mod_yangrpc_example.c mod_yangrpc_example_la_CPPFLAGS = -I$(includedir) -I$(includedir)/yuma/agt -I$(includedir)/yuma/ncx -I$(includedir)/yuma/platform -I$(includedir)/yuma/yangrpc -I$(includedir)/apache2 -I$(includedir)/apr-1.0 mod_yangrpc_example_la_LDFLAGS = -module -lyumancx -lyangrpc $(LIBS) apachemoduleconfig_DATA = \ yangrpc_example.load \ yangrpc_example.conf yuma123_2.14/netconf/src/yangrpc/libapache2-mod-yangrpc-example/configure.ac0000664000175000017500000000100214770023131027131 0ustar vladimirvladimirAC_INIT([apache-yangrpc-example-module], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL apachemodulelibdir="$prefix/lib/apache2/modules" AC_SUBST(apachemodulelibdir) apachemoduleconfigdir="/etc/apache2/mods-available" AC_SUBST(apachemoduleconfigdir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1 -DRELEASE=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/netconf/src/yangrpc/yangrpc.c0000664000175000017500000007414614770023131020634 0ustar vladimirvladimir/* * Copyright (c) 2013 - 2016, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG # include #endif #include "mgr.h" #include "procdefs.h" #include "cli.h" #include "conf.h" #include "help.h" #include "json_wr.h" #include "log.h" #include "ncxmod.h" #include "mgr.h" #include "mgr_hello.h" #include "mgr_io.h" #include "mgr_not.h" #include "mgr_rpc.h" #include "mgr_ses.h" #include "ncx.h" #include "ncx_list.h" #include "ncx_num.h" #include "ncx_str.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "op.h" #include "rpc.h" #include "runstack.h" #include "ses_msg.h" #include "status.h" #include "val.h" #include "val_util.h" #include "var.h" #include "xml_util.h" #include "xml_val.h" #include "xml_wr.h" #include "yangconst.h" #include "yangcli.h" #include "yangcli_cmd.h" #include "yangcli_alias.h" #include "yangcli_autoload.h" #include "yangcli_autolock.h" #include "yangcli_yang_library.h" #include "yangcli_save.h" #include "yangcli_tab.h" #include "yangcli_uservars.h" #include "yangcli_util.h" #include "yangcli_globals.h" #include "yangcli_wordexp.h" #include "yangrpc.h" extern void create_session (server_cb_t *server_cb); static void yangrpc_notification_handler (ses_cb_t *scb, mgr_not_msg_t *msg, boolean *consumed) { log_debug2("\n%s called", __func__); } status_t yangrpc_init(char* args) { int ret; obj_template_t *obj; status_t res; log_debug_t log_level; yangcli_wordexp_t p; char* prog_w_args; #ifdef YANGCLI_DEBUG int i; #endif prog_w_args = malloc(strlen("prog-placeholder ") + ((args==NULL)?0:strlen(args)) + 1); sprintf(prog_w_args, "prog-placeholder %s",(args==NULL)?"":args); ret = yangcli_wordexp(prog_w_args, &p, 0); free(prog_w_args); if(ret!=0) { perror(args); return ERR_CMDLINE_OPT_UNKNOWN; } /* set the default debug output level */ log_level = LOG_DEBUG_INFO; yangcli_init_module_static_vars(); /* initialize the NCX Library first to allow NCX modules * to be processed. No module can get its internal config * until the NCX module parser and definition registry is up */ res = ncx_init(NCX_SAVESTR, log_level, TRUE, NULL, p.we_wordc, p.we_wordv); if (res != NO_ERR) { yangcli_wordfree(&p); return res; } #ifdef YANGCLI_DEBUG if (p.we_wordc>1 && LOGDEBUG2) { log_debug2("\nCommand line parameters:"); for (i=0; i operation to fill in any missing modules * * INPUTS: * server_cb == server session control block to use * scb == session control block to use * * OUTPUTS: * $HOME/.yuma/tmp/// filled with * the the specified YANG files that are ertrieved from * the device with * * RETURNS: * status *********************************************************************/ status_t autoload_blocking_get_modules (server_cb_t *server_cb, ses_cb_t *scb) { ncxmod_search_result_t *searchresult; status_t res; obj_template_t *rpc; val_value_t *reqdata; val_value_t *reply_val; #ifdef DEBUG if (!server_cb || !scb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; /* find first file that needs to be retrieved with get-schema */ for (searchresult = (ncxmod_search_result_t *) dlq_firstEntry(&server_cb->searchresultQ); searchresult != NULL; searchresult = (ncxmod_search_result_t *) dlq_nextEntry(searchresult)) { /* skip found entries */ if (searchresult->source != NULL) { continue; } /* skip found modules with errors */ if (searchresult->res != NO_ERR && !(searchresult->res == ERR_NCX_WRONG_VERSION || searchresult->res == ERR_NCX_MOD_NOT_FOUND)) { continue; } server_cb->cursearchresult = searchresult; #if 0 res = send_get_schema_to_server(server_cb, scb, searchresult->module, searchresult->revision); if (res == NO_ERR) { server_cb->command_mode = CMD_MODE_AUTOLOAD; server_cb->cursearchresult = searchresult; } /* exit loop if we get here */ #else log_info("\n:%s",searchresult->module); res = make_get_schema_reqdata(server_cb, scb, searchresult->module, searchresult->revision, &rpc, &reqdata); if (res != NO_ERR) return res; res = yangrpc_exec((yangrpc_cb_ptr_t) server_cb, reqdata, &reply_val); res = get_schema_reply_to_temp_filcb(server_cb, (mgr_scb_t *)scb->mgrcb /*mscb*/, searchresult->module, searchresult->revision, reply_val); if (res != NO_ERR) { log_error("\nError: save content " " for module '%s' revision '%s' failed (%s)", searchresult->module, (searchresult->revision) ? searchresult->revision : EMPTY_STRING, get_error_string(res)); searchresult->res = res; } continue; #endif } return res; } /* autoload_blocking_get_modules */ /******************************************************************** * FUNCTION yang_library_blocking_get_modules * * Send for /modules-state * * INPUTS: * server_cb == server session control block to use * scb == session control block to use * * OUTPUTS: * * * RETURNS: * status *********************************************************************/ status_t yang_library_blocking_get_module_set (server_cb_t *server_cb, ses_cb_t *scb) { status_t res; obj_template_t *rpc; val_value_t *reqdata; val_value_t *reply_val; res = make_get_yang_library_modules_state_reqdata(server_cb, scb, &rpc, &reqdata); if (res != NO_ERR) return res; res = yangrpc_exec((yangrpc_cb_ptr_t) server_cb, reqdata, &reply_val); if (res != NO_ERR) { val_free_value(reqdata); return res; } res = get_yang_library_modules_state_reply_to_searchresult_entries(server_cb, scb, reply_val); val_free_value(reqdata); val_free_value(reply_val); return res; } /* yang_library_blocking_get_modules */ status_t yangrpc_connect(const char * const server, uint16_t port, const char * const user, const char * const password, const char * const public_key, const char * const private_key, const char * const extra_args, yangrpc_cb_ptr_t* yangrpc_cb_ptr) { char* server_arg; char* port_arg; char* user_arg; char* password_arg; char* public_key_arg; char* private_key_arg; char* mandatory_argv[]={"exec-name-dummy", "--server=?", "--port=?", "--user=?", "--password=?", "--private-key=?", "--public-key=?"}; int mandatory_argc=sizeof(mandatory_argv)/sizeof(char*); char** argv; int argc; int extra_argc; server_cb_t *server_cb; ses_cb_t *ses_cb; status_t res; xmlChar versionbuffer[NCX_VERSION_BUFFSIZE]; val_value_t *parm, *modval; dlq_hdr_t savedevQ; xmlChar *savestr, *revision; uint32 modlen; int ret; yangcli_wordexp_t p; obj_template_t *obj; int i; /* deviations processing */ ncx_save_deviations_t *savedev; ncx_clear_temp_modQ(); ncx_clear_session_modQ(); /* new connect_valset */ if(connect_valset!=NULL) { val_free_value(connect_valset); connect_valset=NULL; } connect_valset = val_new_value(); if (connect_valset == NULL) return ERR_INTERNAL_MEM; obj = ncx_find_object(yangcli_mod, YANGCLI_CONNECT); if (obj == NULL) return ERR_NCX_DEF_NOT_FOUND; val_init_from_template(connect_valset, obj); dlq_createSQue(&savedevQ); /* create a default server control block */ server_cb = new_server_cb(YANGCLI_DEF_SERVER); if (server_cb==NULL) { log_error("\n new_server_cb failed (%s)", get_error_string(res)); return ERR_INTERNAL_PTR; } argv = mandatory_argv; argc=0; argv[argc++]="yangrpc-conn-instance"; server_arg = malloc(strlen("--server=")+strlen(server)+1); if (server_arg==NULL) return ERR_INTERNAL_MEM; sprintf(server_arg,"--server=%s",server); argv[argc++]=server_arg; port_arg = malloc(strlen("--ncport=")+strlen("65535")+1); if (port_arg==NULL) return ERR_INTERNAL_MEM; sprintf(port_arg,"--ncport=%u", (unsigned int)port); argv[argc++]=port_arg; user_arg = malloc(strlen("--user=")+strlen(user)+1); if (user_arg==NULL) return ERR_INTERNAL_MEM; sprintf(user_arg,"--user=%s",user); argv[argc++]=user_arg; /* at least password or public_key and private_key pair has to be specified */ if (!(password!=NULL || (public_key!=NULL && private_key!=NULL))) return ERR_INTERNAL_MEM; if(password!=NULL) { password_arg = malloc(strlen("--password=")+strlen(password)+1); if (password_arg==NULL) return ERR_INTERNAL_MEM; sprintf(password_arg,"--password=%s",password); argv[argc++]=password_arg; } if(public_key!=NULL && private_key!=NULL) { public_key_arg = malloc(strlen("--public-key=")+strlen(public_key)+1); if (public_key_arg==NULL) return ERR_INTERNAL_MEM; sprintf(public_key_arg,"--public-key=%s",public_key); argv[argc++]=public_key_arg; private_key_arg = malloc(strlen("--private-key=")+strlen(private_key)+1); if (private_key_arg==NULL) return ERR_INTERNAL_MEM; sprintf(private_key_arg,"--private-key=%s",private_key); argv[argc++]=private_key_arg; } /* process extra args */ if(extra_args!=NULL) { ret = yangcli_wordexp(extra_args, &p, 0); if(ret!=0) { perror(extra_args); log_error("\n yangcli_wordexp failed (%s)", get_error_string(ERR_CMDLINE_OPT_UNKNOWN)); return ERR_CMDLINE_OPT_UNKNOWN; } extra_argc = p.we_wordc; argv = malloc((argc+extra_argc)*sizeof(char*)); memcpy(argv,mandatory_argv,argc*sizeof(char*)); memcpy(argv+argc,p.we_wordv,p.we_wordc*sizeof(char*)); argc+=extra_argc; yangcli_wordfree(&p); } else { argv = malloc((argc)*sizeof(char*)); memcpy(argv,mandatory_argv,argc*sizeof(char*)); } /* Get any command line and conf file parameters */ res = process_cli_input(server_cb, argc, argv); /* Skip argv[0] - it was not allocated with malloc */ for(i=1;i if the server parameter is set a connect will * --> be attempted * * The yangcli_stdin_handler will call the finish_start_session * function when the user enters a line of keyboard text */ server_cb->state = MGR_IO_ST_IDLE; if (connect_valset) { parm = val_find_child(connect_valset, YANGCLI_MOD, YANGCLI_SERVER); if (parm && parm->res == NO_ERR) { res = do_connect(server_cb, NULL, NULL, 0, TRUE); if (res != NO_ERR) { if (FALSE /*!batchmode*/) { res = NO_ERR; } } } } /* the request will be stored if this returns NO_ERR */ //res = mgr_rpc_send_request(scb, req, yangcli_reply_handler_); //assert(res==NO_ERR); //mgr_io_run(); { ses_cb_t* scb; mgr_scb_t* mscb; scb = mgr_ses_get_scb(server_cb->mysid); if (scb == NULL) return ERR_INTERNAL_VAL; res = ses_msg_send_buffs(scb); if (res != NO_ERR) return res; while(1) { res = ses_accept_input(scb); if(res!=NO_ERR) { log_error("\n ses_accept failed (%s)", get_error_string(res)); return res; } if(mgr_ses_process_first_ready()) { break; } } /* incoming hello OK and outgoing hello is sent */ server_cb->state = MGR_IO_ST_CONN_IDLE; mscb = (mgr_scb_t *)scb->mgrcb; ncx_set_temp_modQ(&mscb->temp_modQ); report_capabilities(server_cb, scb, TRUE, HELP_MODE_NONE); check_module_capabilities(server_cb, scb, autoload_blocking_get_modules, yang_library_blocking_get_module_set); if (scb != NULL && server_cb->deviations_applied == FALSE) { ncx_set_session_modQ(&mscb->temp_modQ); ncx_set_cur_modQ(&mscb->temp_modQ); process_module_deviations(&server_cb->autoload_savedevQ); server_cb->deviations_applied = TRUE; ncx_reset_modQ(); ncx_clear_session_modQ(); } } *yangrpc_cb_ptr = (yangrpc_cb_ptr_t)server_cb; return NO_ERR; } val_value_t* global_reply_val; status_t global_reply_status; /******************************************************************** * FUNCTION yangcli_reply_handler_ * * handle incoming messages * * INPUTS: * scb == session receiving RPC reply * req == original request returned for freeing or reusing * rpy == reply received from the server (for checking then freeing) * * RETURNS: * none *********************************************************************/ void yangcli_reply_handler_ (ses_cb_t *scb, mgr_rpc_req_t *req, mgr_rpc_rpy_t *rpy) { server_cb_t *server_cb; val_value_t *val; mgr_scb_t *mgrcb; lock_cb_t *lockcb; rpc_err_t rpcerrtyp; status_t res; boolean anyout, anyerrors, done; uint32 usesid; #ifdef DEBUG if (!scb || !req || !rpy) { assert(0); } #endif /* check the contents of the reply */ if (rpy && rpy->reply) { #if 0 if (val_find_child(rpy->reply, NC_MODULE, NCX_EL_RPC_ERROR)) { if (server_cb->command_mode == CMD_MODE_NORMAL || LOGDEBUG) { log_error("\nRPC Error Reply %s for session %u:\n", rpy->msg_id, usesid); val_dump_value_max(rpy->reply, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); log_error("\n"); anyout = TRUE; } } else if (val_find_child(rpy->reply, NC_MODULE, NCX_EL_OK)) { global_reply_val = val_clone(rpy->reply); if (server_cb->command_mode == CMD_MODE_NORMAL || LOGDEBUG2) { if (server_cb->echo_replies) { log_info("\nRPC OK Reply %s for session %u:\n", rpy->msg_id, usesid); anyout = TRUE; } } } #else //val_dump_value(rpy->reply,0); global_reply_val = val_clone(rpy->reply); if (global_reply_val == NULL) { log_error("\nRPC Failed to clone reply"); global_reply_status = ERR_INTERNAL_VAL; } #endif } /* free the request and reply */ mgr_rpc_free_request(req); if (rpy) { mgr_rpc_free_reply(rpy); } } /* yangcli_reply_handler_ */ #include "yangcli_cmd.h" status_t yangrpc_parse_cli(yangrpc_cb_ptr_t yangrpc_cb_ptr, const char * const original_line, val_value_t** request_val) { obj_template_t *rpc, *input; val_value_t *reqdata = NULL, *valset = NULL, *parm; xmlChar *newline = NULL, *useline = NULL; uint32 len, linelen; status_t res = NO_ERR; boolean shut = FALSE; ncx_node_t dtyp; char* line; ses_cb_t* scb; mgr_scb_t* mscb; server_cb_t* server_cb; server_cb = (server_cb_t*)yangrpc_cb_ptr; scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { res = SET_ERROR(ERR_INTERNAL_PTR); return res; } mscb = (mgr_scb_t *)scb->mgrcb; ncx_set_temp_modQ(&mscb->temp_modQ); ncx_set_session_modQ(&mscb->temp_modQ); line = strdup(original_line); #ifdef DEBUG if (!server_cb || !line) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* make sure there is something to parse */ linelen = xml_strlen(line); if (!linelen) { return res; } #if 0 /* first check the command keyword to see if it is an alias */ newline = expand_alias(line, &res); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; useline = line; } else if (res == NO_ERR) { if (newline == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } useline = newline; linelen = xml_strlen(newline); } else { log_error("\nError: %s\n", get_error_string(res)); if (newline) { m__free(newline); } return res; } #else useline = line; #endif /* get the RPC method template */ dtyp = NCX_NT_OBJ; rpc = (obj_template_t *)parse_def(server_cb, &dtyp, useline, &len, &res); if (rpc == NULL || !obj_is_rpc(rpc)) { if (server_cb->result_name || server_cb->result_filename) { res = finish_result_assign(server_cb, NULL, useline); } else { if (res == ERR_NCX_DEF_NOT_FOUND) { /* this is an unknown command */ log_error("\nError: Unrecognized command"); } else if (res == ERR_NCX_AMBIGUOUS_CMD) { log_error("\n"); } else { log_error("\nError: %s", get_error_string(res)); } } if (newline) { m__free(newline); } return res; } /* check local commands */ if (is_yangcli_ns(obj_get_nsid(rpc))) { if (!xml_strcmp(obj_get_name(rpc), YANGCLI_CONNECT)) { res = ERR_NCX_OPERATION_FAILED; log_stdout("\nError: Already connected"); } else { uint32 timeval; res = do_local_conn_command_reqdata(server_cb, rpc, useline, len, &reqdata, &timeval); if (res == ERR_NCX_SKIPPED) { #ifdef DEBUG assert(0); #else log_error("\nError: %s\n", get_error_string(res)); if (newline) { m__free(newline); } return res; #endif /* DEBUG */ /*res = do_local_command(server_cb, rpc, useline, len);*/ } } if (newline) { m__free(newline); } } else { /* else treat this as an RPC request going to the server * make sure this is a TRUE conditional command */ /* construct a method + parameter tree */ reqdata = xml_val_new_struct(obj_get_name(rpc), obj_get_nsid(rpc)); if (!reqdata) { log_error("\nError allocating a new RPC request"); res = ERR_INTERNAL_MEM; input = NULL; } else { /* should find an input node */ input = obj_find_child(rpc, NULL, YANG_K_INPUT); } /* check if any params are expected */ if (res == NO_ERR && input) { while (useline[len] && xml_isspace(useline[len])) { len++; } if (len < linelen) { valset = parse_rpc_cli(server_cb, rpc, &useline[len], &res); if (res != NO_ERR) { log_error("\nError in the parameters for '%s' command (%s)", obj_get_name(rpc), get_error_string(res)); } } /* check no input from user, so start a parmset */ if (res == NO_ERR && !valset) { valset = val_new_value(); if (!valset) { res = ERR_INTERNAL_MEM; } else { val_init_from_template(valset, input); } } } #if 0 /* fill in any missing parameters from the CLI */ if (res == NO_ERR) { if (interactive_mode()) { res = fill_valset(server_cb, rpc, valset, NULL, TRUE, FALSE); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } } } #endif /* make sure the values are in canonical order * so compliant some servers will not complain */ val_set_canonical_order(valset); /* go through the parm list and move the values * to the reqdata struct. */ if (res == NO_ERR) { parm = val_get_first_child(valset); while (parm) { val_remove_child(parm); val_add_child(parm, reqdata); parm = val_get_first_child(valset); } } } *request_val = reqdata; free(line); return res; } status_t yangrpc_exec(yangrpc_cb_ptr_t yangrpc_cb_ptr, val_value_t* request_val, val_value_t** reply_val) { status_t res; ses_cb_t* scb; mgr_scb_t* mscb; mgr_rpc_req_t *req; server_cb_t* server_cb; server_cb = (server_cb_t*)yangrpc_cb_ptr; scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { res = SET_ERROR(ERR_INTERNAL_PTR); return res; } mscb = (mgr_scb_t *)scb->mgrcb; ncx_set_temp_modQ(&mscb->temp_modQ); ncx_set_session_modQ(&mscb->temp_modQ); req = mgr_rpc_new_request(scb); if (!req) { res = ERR_INTERNAL_MEM; log_error("\nError allocating a new RPC request"); return res; } req->data = val_clone(request_val);/*reqdata*/ req->rpc = request_val->obj; req->timeout = 1000/*timeoutval*/; /* the request will be stored if this returns NO_ERR */ global_reply_val=NULL; global_reply_status=NO_ERR; res = mgr_rpc_send_request(scb, req, yangcli_reply_handler_); //mgr_io_run(); res = ses_msg_send_buffs(scb); if (res != NO_ERR) return res; while(1) { res = ses_accept_input(scb); if(res!=NO_ERR) { log_error("\nerror: ses_accept_input res=%d",res); return res; } if(global_reply_status != NO_ERR || (mgr_ses_process_first_ready() && global_reply_val!=NULL)) { break; } } *reply_val = global_reply_val; return global_reply_status; } void yangrpc_close(yangrpc_cb_ptr_t yangrpc_cb_ptr) { server_cb_t* server_cb; server_cb = (server_cb_t*)yangrpc_cb_ptr; log_info("Closing session\n"); if(connect_valset!=NULL) { val_free_value(connect_valset); } connect_valset = val_new_value(); free_server_cb(server_cb); /* Cleanup the Netconf Server Library */ mgr_cleanup(); /* cleanup the module library search results */ ncxmod_clean_search_result_queue(&modlibQ); /* cleanup the NCX engine and registries */ ncx_cleanup(); } yuma123_2.14/netconf/src/yangrpc/Makefile.am0000664000175000017500000000326714770023131021055 0ustar vladimirvladimirlib_LTLIBRARIES = libyangrpc.la libyangrpc_la_SOURCES = \ $(top_srcdir)/netconf/src/yangrpc/yangrpc.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_autoload.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_yang_library.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_cmd.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_util.c\ $(top_srcdir)/netconf/src/yangcli/yangcli_autolock.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_cond.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_eval.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_list.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_save.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_show.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_tab.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_timer.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_alias.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_uservars.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_wordexp.c \ $(top_srcdir)/netconf/src/yangcli/yangcli.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_globals.c libyangrpc_la_CPPFLAGS = -DSKIP_MAIN -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform $(XML_CPPFLAGS) -I$(top_srcdir)/netconf/src/yangrpc -I$(top_srcdir)/netconf/src/yangcli -I$(top_srcdir)/libtecla libyangrpc_la_LDFLAGS = -version-info 2:0:0 $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/ncx/libyumancx.la if WITH_TECLA libyangrpc_la_CPPFLAGS += -I $(top_srcdir)/libtecla libyangrpc_la_LDFLAGS += $(top_builddir)/libtecla/libtecla.la else libyangrpc_la_SOURCES += $(top_srcdir)/netconf/src/yangcli/tecla2readline/tecla2readline.c libyangrpc_la_CPPFLAGS += -I $(top_srcdir)/libtecla libyangrpc_la_LDFLAGS += -lreadline endif yuma123_2.14/netconf/src/yangrpc/example/0000775000175000017500000000000014770023131020444 5ustar vladimirvladimiryuma123_2.14/netconf/src/yangrpc/example/yangrpc-example.c0000664000175000017500000001035514770023131023710 0ustar vladimirvladimir/* Usage: ./yangrpc-example --server=myserver.com --port=830 --user=vladimir --password='mypass' \ --private-key=/home/vladimir/.ssh/id_rsa \ --public-key=/home/vladimir/.ssh/id_rsa.pub */ #include #include #include #include #include #include "ncx.h" #include "ncxmod.h" #include "val.h" #include "yangrpc.h" static struct option const long_options[] = { {"autoload", optional_argument, NULL, 'a'}, {"server", required_argument, NULL, 's'}, {"port", required_argument, NULL, 'p'}, {"user", required_argument, NULL, 'u'}, {"password", required_argument, NULL, 'P'}, {"private-key", required_argument, NULL, 'k'}, {"public-key", required_argument, NULL, 'K'}, {NULL, 0, NULL, 0} }; int main(int argc, char* argv[]) { status_t res; yangrpc_cb_ptr_t yangrpc_cb_ptr; ncx_module_t * ietf_netconf_mod; obj_template_t* rpc_obj; obj_template_t* input_obj; obj_template_t* filter_obj; val_value_t* request_val; val_value_t* reply_val; val_value_t* filter_val; val_value_t* type_meta_val; val_value_t* select_meta_val; int optc; char* server="127.0.0.1"; unsigned int port=830; char* user=getlogin(); char* password=NULL; char private_key[1024]; char public_key[1024]; boolean autoload = TRUE; sprintf(private_key,"/home/%s/.ssh/id_rsa",user); sprintf(public_key,"/home/%s/.ssh/id_rsa.pub",user); while ((optc = getopt_long (argc, argv, "a:s:p:u:P:k:K", long_options, NULL)) != -1) { switch (optc) { case 'a': if (optarg != NULL && strcasecmp(optarg, "false") == 0) autoload = FALSE; break; case 's': server=optarg; break; case 'p': port = atoi(optarg); break; case 'u': user = optarg; break; case 'P': password = optarg; break; case 'k': strcpy(private_key,optarg); break; case 'K': strcpy(public_key,optarg); break; default: exit (-1); } } res = yangrpc_init(NULL); assert(res==NO_ERR); res = yangrpc_connect(server /*127.0.0.1*/, port /*830*/, user /*vladimir*/, password /* "" */, public_key /* "/home/vladimir/.ssh/id_rsa.pub" */, private_key /* "/home/vladimir/.ssh/id_rsa" */, autoload ? "--autoload=true" : "--autoload=false", &yangrpc_cb_ptr); assert(res==NO_ERR); res = ncxmod_load_module ("ietf-netconf", NULL, NULL, &ietf_netconf_mod); assert(res==NO_ERR); rpc_obj = ncx_find_object(ietf_netconf_mod, "get"); assert(obj_is_rpc(rpc_obj)); input_obj = obj_find_child(rpc_obj, NULL, "input"); assert(input_obj!=NULL); filter_obj = obj_find_child(input_obj, NULL, "filter"); assert(filter_obj!=NULL); request_val = val_new_value(); val_init_from_template(request_val, rpc_obj); filter_val = val_new_value(); val_init_from_template(filter_val, filter_obj); type_meta_val = val_make_string(0, "type","xpath"); select_meta_val = val_make_string(0, "select", "/"); val_add_meta(select_meta_val, filter_val); val_add_meta(type_meta_val, filter_val); val_add_child(filter_val, request_val); res = yangrpc_exec(yangrpc_cb_ptr, request_val, &reply_val); assert(res==0); val_dump_value(reply_val,0); { val_value_t* data_val; val_value_t* interfaces_state_val; data_val = val_find_child(reply_val,NULL,"data"); interfaces_state_val = val_find_child(data_val,"ietf-interfaces","interfaces-state"); if (interfaces_state_val) { val_dump_value(interfaces_state_val,0); } else { log_info("\nReply to 'get' RPC did not contain interfaces-state."); } } val_free_value(request_val); val_free_value(reply_val); yangrpc_close(yangrpc_cb_ptr); return 0; } yuma123_2.14/netconf/src/yangrpc/example/Makefile.am0000664000175000017500000000163014770023131022500 0ustar vladimirvladimirbin_PROGRAMS = yangrpc-example yangrpc_example_SOURCES = \ $(top_srcdir)/netconf/src/yangrpc/example/yangrpc-example.c yangrpc_example_CPPFLAGS = -I $(top_srcdir)/libtecla -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump -I$(top_srcdir)/netconf/src/yangrpc yangrpc_example_LDFLAGS = $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la $(top_builddir)/netconf/src/yangrpc/libyangrpc.la -lz $(LIBS) -lssh2 -lncurses bin_PROGRAMS += yangrpc-parse-example yangrpc_parse_example_SOURCES = \ $(top_srcdir)/netconf/src/yangrpc/example/yangrpc-parse-example.c yangrpc_parse_example_CPPFLAGS = $(yangrpc_example_CPPFLAGS) yangrpc_parse_example_LDFLAGS = $(yangrpc_example_LDFLAGS) yuma123_2.14/netconf/src/yangrpc/example/yangrpc-parse-example.c0000664000175000017500000000257414770023131025024 0ustar vladimirvladimir#include #include #include "ncx.h" #include "val.h" #include "yangrpc.h" int main(int argc, char* argv[]) { status_t res; yangrpc_cb_ptr_t yangrpc_cb_ptr; ncx_module_t * ietf_netconf_mod; obj_template_t* rpc_obj; obj_template_t* input_obj; obj_template_t* filter_obj; val_value_t* request_val; val_value_t* reply_val; val_value_t* filter_val; val_value_t* type_meta_val; val_value_t* select_meta_val; res = yangrpc_init(NULL); assert(res==NO_ERR); res = yangrpc_connect("localhost"/*server*/, 830/*port*/, "vladimir"/*user*/,"mysecretpass"/*password*/,"/home/vladimir/.ssh/id_rsa.pub"/*public_key*/, "/home/vladimir/.ssh/id_rsa"/*private_key*/, NULL, &yangrpc_cb_ptr); assert(res==NO_ERR); res = yangrpc_parse_cli(yangrpc_cb_ptr, "xget /interfaces-state", &request_val); assert(res==0); res = yangrpc_exec(yangrpc_cb_ptr, request_val, &reply_val); assert(res==0); val_dump_value(reply_val,0); { val_value_t* data_val; val_value_t* interfaces_state_val; data_val = val_find_child(reply_val,NULL,"data"); interfaces_state_val = val_find_child(data_val,"ietf-interfaces","interfaces-state"); val_dump_value(interfaces_state_val,0); } val_free_value(request_val); val_free_value(reply_val); yangrpc_close(yangrpc_cb_ptr); return 0; } yuma123_2.14/netconf/src/yangrpc/yangrpc.h0000664000175000017500000000765014770023131020635 0ustar vladimirvladimir/* * Copyright (c) 2013 - 2016, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "val.h" typedef void* yangrpc_cb_ptr_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION yangrpc_init * * Function initializing the Yuma environment * * MUST BE CALLED BEFORE yangrpc_connect calls * * INPUTS: * args == space separated list of arguments e.g. "--log-level=debug --display-mode=xml" * * RETURNS: * status *********************************************************************/ status_t yangrpc_init(char* args); /******************************************************************** * FUNCTION yangrpc_connect * * Function connecting to YANG modeled device * * MUST CALL yangrpc_init FIRST * * INPUTS: * server == netconf server address * ncport == netconf server port * user == user name * password == plaintext password * public_key == path to the public key to be used * private_key == path to the private key to be used * extra_args == string with extra arguments e.g. * "--timeout=30 --dump-session=/tmp/mysession-dump-" * OUTPUTS: * yangrpc_cb == returns pointer to control block with the yangrpc session context * * RETURNS: * status *********************************************************************/ status_t yangrpc_connect(const char * const server, uint16_t port, const char * const user, const char * const password, const char * const public_key, const char * const private_key, const char * const extra_args, yangrpc_cb_ptr_t* yangrpc_cb_ptr); /******************************************************************** * FUNCTION yangrpc_parse_cli * * Function parsing RPC encoded in yangcli syntax e.g. "xget /" * and returning corresponding request_val * * INPUTS: * server == netconf server address * ncport == netconf server port * user == user name * password == plaintext password * public_key == path to the public key to be used * private_key == path to the private key to be used * extra_args == string with extra arguments e.g. * "--timeout=30 --dump-session=/tmp/mysession-dump-" * OUTPUTS: * yangrpc_cb_ptr == returns pointer to control block with the yangrpc session context * * RETURNS: * status *********************************************************************/ status_t yangrpc_parse_cli(yangrpc_cb_ptr_t yangrpc_cb_ptr, const char * const cli_cmd_str, val_value_t** request_val); /******************************************************************** * FUNCTION yangrpc_exec * * Function sending the RPC request specified in request_val and returns the RPC reply in reply_val * * INPUTS: * yangrpc_cb_ptr == control block pointer * request_val == RPC request value pointer * OUTPUTS: * reply_val == pointer to RPC reply value pointer * * RETURNS: * status *********************************************************************/ status_t yangrpc_exec(yangrpc_cb_ptr_t yangrpc_cb_ptr, val_value_t* request_val, val_value_t** reply_val); /******************************************************************** * FUNCTION yangrpc_close * * Function closing yangrpc session * * INPUTS: * yangrpc_cb_ptr == control block pointer * *********************************************************************/ void yangrpc_close(yangrpc_cb_ptr_t yangrpc_cb_ptr); yuma123_2.14/netconf/src/subsys/0000775000175000017500000000000014770023131016676 5ustar vladimirvladimiryuma123_2.14/netconf/src/subsys/netconf-subsystem.c0000664000175000017500000004111214770023131022531 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: netconf-subsystem.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 14-jan-07 abb begun; 03-mar-11 abb get rid of usleeps and replace with design that checks for EAGAIN ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _C_main 1 #include "procdefs.h" #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_agt #include "agt.h" #endif #ifndef _H_agt_ncxserver #include "agt_ncxserver.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_send_buff #include "send_buff.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define BUFFLEN 2000 #define MAX_READ_TRIES 1000 /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static char *client_addr; static struct sockaddr *ncxname; static struct sockaddr_un ncxname_unix; static struct sockaddr_in ncxname_inet; static int ncxport_inet; static int ncxsock; static char *user; static char *port; static int traceLevel = 0; static FILE *errfile; #define SUBSYS_TRACE1(fmt, ...) if (traceLevel && errfile) \ { \ fprintf( errfile, fmt, ##__VA_ARGS__); \ fflush( errfile ); \ } #define SUBSYS_TRACE2(fmt, ...) if (traceLevel > 1 && errfile) \ { \ fprintf( errfile, fmt, ##__VA_ARGS__); \ fflush( errfile ); \ } #define SUBSYS_TRACE3(fmt, ...) if (traceLevel > 2 && errfile) \ { \ fprintf( errfile, fmt, ##__VA_ARGS__); \ fflush( errfile ); \ } static boolean ncxconnect; static char msgbuff[BUFFLEN]; /****************************************************************** * FUNCTION configure_logging * * Configure debug logging. This function evaluates command line * arguments to configure debug logging. * ******************************************************************/ static void configure_logging( int argc, char** argv ) { char* err_filename; int arg_idx = 1; char defname[21]; strncpy(defname, "/tmp/subsys-err.log", 20); err_filename = defname; while ( arg_idx < argc-1 ) { if ( !strcmp( argv[arg_idx], "-filename" ) || !strcmp( argv[arg_idx], "-f" ) ) { err_filename = argv[++arg_idx]; if ( !traceLevel ) { traceLevel = 1; } } else if ( !strcmp( argv[arg_idx], "-trace" ) || !strcmp( argv[arg_idx], "-t" ) ) { traceLevel = atoi( argv[++arg_idx] ); } ++arg_idx; } if ( traceLevel ) { errfile = fopen( err_filename, "a" ); SUBSYS_TRACE1( "\n*** New netconf Session Started ***\n" ); } } static char* ncxserver_sockname(int argc, char** argv, char* port) { int i; char match[] = "--ncxserver-sockname=65535@"; sprintf(match,"--ncxserver-sockname=%s@",port); for(i=1;istrlen(match) && 0==memcmp(argv[i],match,strlen(match))) { return argv[i]+strlen(match); } } return NCXSERVER_SOCKNAME; } /******************************************************************** * FUNCTION init_subsys * * Initialize the subsystem, and get it ready to send and receive * the first message of any kind * * RETURNS: * status *********************************************************************/ static status_t init_subsys (int argc, char** argv) { char *cp, *con; int ret; int name_size; int i; client_addr = NULL; port = NULL; user = NULL; ncxsock = -1; ncxconnect = FALSE; ncxport_inet = -1; for(i=1;istrlen("--tcp-direct-port=") && 0==memcmp(argv[i],"--tcp-direct-port=",strlen("--tcp-direct-port="))) { ncxport_inet = atoi(argv[i]+strlen("--tcp-direct-port=")); } } /* get the client address */ con = getenv("SSH_CONNECTION"); if (!con) { SUBSYS_TRACE1( "ERROR: init_subsys(): " "Get SSH_CONNECTION variable failed\n" ); return ERR_INTERNAL_VAL; } /* get the client addr */ client_addr = strdup(con); if (!client_addr) { SUBSYS_TRACE1( "ERROR: init_subsys(): strdup(client_addr) failed\n" ); return ERR_INTERNAL_MEM; } cp = strchr(client_addr, ' '); if (!cp) { SUBSYS_TRACE1( "ERROR: init_subsys(): " "Malformed SSH_CONNECTION variable\n" ); return ERR_INTERNAL_VAL; } else { *cp = 0; } /* get the server connect port */ cp = strrchr(con, ' '); if (cp && cp[1]) { port = strdup(++cp); } if (!port) { SUBSYS_TRACE1( "ERROR: init_subsys(): " "Malformed SSH_CONNECTION variable\n" ); return ERR_INTERNAL_VAL; } /* get the username */ cp = getenv("USER"); if (!cp) { SUBSYS_TRACE1( "ERROR: init_subsys(): Get USER variable failed\n"); return ERR_INTERNAL_VAL; } user = strdup(cp); if (!user) { SUBSYS_TRACE1( "ERROR: init_subsys(): strdup(user) failed\n" ); return ERR_INTERNAL_MEM; } if(ncxport_inet != -1) { struct hostent* hp; int tcp_nodelay_option=1; /* make a socket to connect to the NCX server */ ncxsock = socket(AF_INET, SOCK_STREAM, 0); if (ncxsock < 0) { SUBSYS_TRACE1( "ERROR: init_subsys(): NCX Socket Creation failed\n" ); return ERR_NCX_CONNECT_FAILED; } if (setsockopt(ncxsock, IPPROTO_TCP, TCP_NODELAY, (char*) &tcp_nodelay_option, sizeof(tcp_nodelay_option)) < 0) { SUBSYS_TRACE1( "ERROR: init_subsys(): NCX Socket Creation failed\n" ); return ERR_NCX_CONNECT_FAILED; } hp = gethostbyname("localhost"); if (hp == NULL) { SUBSYS_TRACE1( "ERROR: init_subsys(): NCX Socket Creation failed\n" ); return ERR_NCX_CONNECT_FAILED; } memset((char *) &ncxname_inet, 0, sizeof(ncxname_inet)); ncxname_inet.sin_family = AF_INET; ncxname_inet.sin_port = htons((unsigned short)ncxport_inet); memcpy((char *) &ncxname_inet.sin_addr, hp->h_addr, hp->h_length); name_size = sizeof(ncxname_inet); ncxname = (struct sockaddr *) &ncxname_inet; } else { /* make a socket to connect to the NCX server */ ncxsock = socket(PF_LOCAL, SOCK_STREAM, 0); if (ncxsock < 0) { SUBSYS_TRACE1( "ERROR: init_subsys(): NCX Socket Creation failed\n" ); return ERR_NCX_CONNECT_FAILED; } ncxname_unix.sun_family = AF_LOCAL; strncpy(ncxname_unix.sun_path, ncxserver_sockname(argc, argv, port), sizeof(ncxname_unix.sun_path)); name_size = SUN_LEN(&ncxname_unix); ncxname = (struct sockaddr *)&ncxname_unix; } /* try to connect to the NCX server */ ret = connect(ncxsock, ncxname, name_size); if (ret != 0) { SUBSYS_TRACE1( "ERROR: init_subsys(): NCX Socket Connect failed\n" ); return ERR_NCX_OPERATION_FAILED; } else { SUBSYS_TRACE2( "INFO: init_subsys(): " "NCX Socket Connected on FD: %d \n", ncxsock ); ncxconnect = TRUE; } #ifdef USE_NONBLOCKING_IO /* set non-blocking IO */ if (fcntl(ncxsock, F_SETFD, O_NONBLOCK)) { SUBSYS_TRACE1( "ERROR: init_subsys(): fnctl() failed\n" ); } #endif /* connected to the ncxserver and setup the ENV vars ok */ return NO_ERR; } /* init_subsys */ /******************************************************************** * FUNCTION cleanup_subsys * * Cleanup the subsystem * *********************************************************************/ static void cleanup_subsys (void) { if (client_addr) { m__free(client_addr); } if (user) { m__free(user); } if (ncxconnect) { close(ncxsock); } if (errfile) { fclose(errfile); } } /* cleanup_subsys */ /******************************************************************** * FUNCTION send_ncxconnect * * Send the message to the ncxserver * * RETURNS: * status *********************************************************************/ static status_t send_ncxconnect (void) { status_t res; char connectmsg[] = "%s\n\n%s"; memset(msgbuff, 0x0, BUFFLEN); snprintf(msgbuff, BUFFLEN, connectmsg, (const char *)XML_START_MSG, NCX_URN, user, client_addr, NCX_SERVER_MAGIC, port, NC_SSH_END); res = send_buff(ncxsock, msgbuff, strlen(msgbuff)); return res; } /* send_ncxconnect */ /******************************************************************** * FUNCTION do_read * * Read from a FD * * INPUTS: * * RETURNS: * return byte count *********************************************************************/ static ssize_t do_read (int readfd, char *readbuff, size_t readcnt, status_t *retres) { boolean readdone; ssize_t retcnt; readdone = FALSE; retcnt = 0; *retres = NO_ERR; while (!readdone && *retres == NO_ERR) { retcnt = read(readfd, readbuff, readcnt); if (retcnt < 0) { if (errno != EAGAIN) { SUBSYS_TRACE1( "ERROR: do_read(): read of FD(%d): " "failed with error: %s\n", readfd, strerror( errno ) ); *retres = ERR_NCX_READ_FAILED; continue; } } else if (retcnt == 0) { SUBSYS_TRACE1( "INFO: do_read(): closed connection\n"); *retres = ERR_NCX_EOF; readdone = TRUE; continue; } else { /* retcnt is the number of bytes read */ readdone = TRUE; } } /*end readdone loop */ return retcnt; } /* do_read */ /******************************************************************** * FUNCTION io_loop * * Handle the IO for the program * * INPUTS: * * RETURNS: * status *********************************************************************/ static status_t io_loop (void) { status_t res; boolean done; fd_set fds; int ret; ssize_t retcnt; res = NO_ERR; done = FALSE; FD_ZERO(&fds); while (!done) { FD_SET(STDIN_FILENO, &fds); FD_SET(ncxsock, &fds); ret = select(FD_SETSIZE, &fds, NULL, NULL, NULL); if (ret < 0) { if ( errno != EINTR ) { SUBSYS_TRACE1( "ERROR: io_loop(): select() " "failed with error: %s\n", strerror( errno ) ); res = ERR_NCX_OPERATION_FAILED; done = TRUE; } else { SUBSYS_TRACE2( "INFO: io_loop(): select() " "failed with error: %s\n", strerror( errno ) ); } continue; } else if (ret == 0) { SUBSYS_TRACE1( "ERROR: io_loop(): select() " "returned 0, exiting...\n" ); res = NO_ERR; done = TRUE; continue; } /* else some IO to process */ /* check any input from client */ if (FD_ISSET(STDIN_FILENO, &fds)) { /* get buff from openssh */ retcnt = do_read(STDIN_FILENO, msgbuff, (size_t)BUFFLEN, &res); if (res == ERR_NCX_EOF) { res = NO_ERR; done = TRUE; continue; } else if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } else if (res == NO_ERR && retcnt > 0) { /* send this buffer to the ncxserver */ res = send_buff(ncxsock, msgbuff, (size_t)retcnt); if (res != NO_ERR) { SUBSYS_TRACE1( "ERROR: io_loop(): send_buff() to ncxserver " "failed with %s\n", strerror( errno ) ); done = TRUE; continue; } } } /* if STDIN set */ /* check any input from the ncxserver */ if (FD_ISSET(ncxsock, &fds)) { res = NO_ERR; retcnt = do_read(ncxsock, msgbuff, (size_t)BUFFLEN, &res); if (res == ERR_NCX_EOF) { res = NO_ERR; done = TRUE; continue; } else if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } else if (res == NO_ERR && retcnt > 0) { /* send this buffer to STDOUT */ res = send_buff(STDOUT_FILENO, msgbuff, (size_t)retcnt); if (res != NO_ERR) { SUBSYS_TRACE1( "ERROR: io_loop(): send_buff() to client " "failed with %s\n", strerror( errno ) ); done = TRUE; continue; } } } } return res; } /* io_loop */ /******************************************************************** * FUNCTION main * * STDIN is input from the SSH client (sent to ncxserver) * STDOUT is output to the SSH client (rcvd from ncxserver) * * RETURNS: * 0 if NO_ERR * 1 if error connecting or logging into ncxserver *********************************************************************/ int main (int argc, char **argv) { status_t res; const char *msg; configure_logging( argc, argv ); res = init_subsys(argc, argv); if (res != NO_ERR) { msg = "init failed"; } if (res == NO_ERR) { res = send_ncxconnect(); if (res != NO_ERR) { msg = "connect failed"; } } if (res == NO_ERR) { res = io_loop(); if (res != NO_ERR) { msg = "IO error"; } } if (res != NO_ERR) { SUBSYS_TRACE1( "ERROR: io_loop(): exited with error %s \n", msg ); } cleanup_subsys(); if (res != NO_ERR) { return 1; } else { return 0; } } /* main */ yuma123_2.14/netconf/src/subsys/Makefile.am0000775000175000017500000000073014770023131020735 0ustar vladimirvladimirsbin_PROGRAMS = netconf-subsystem netconf_subsystem_SOURCES = \ $(top_srcdir)/netconf/src/subsys/netconf-subsystem.c netconf_subsystem_CPPFLAGS = -I $(top_srcdir)/netconf/src/subsys/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump netconf_subsystem_LDFLAGS = $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la yuma123_2.14/netconf/src/yangcli/0000775000175000017500000000000014770023131016774 5ustar vladimirvladimiryuma123_2.14/netconf/src/yangcli/yangcli_wordexp.c0000664000175000017500000000423514770023131022342 0ustar vladimirvladimir/* * Copyright (c) 2013 - 2016, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include #include #include #include "log.h" #include "yangcli_wordexp.h" int yangcli_wordexp (const char* words, yangcli_wordexp_t* pwordexp, int flags) { unsigned int i; unsigned int len; unsigned char quoted; pwordexp->we_wordv=(char**)malloc(YANGCLI_WORDEXP_MAXPARAMS_NUM*sizeof(char*)); pwordexp->we_word_line_offset=(int*)malloc(YANGCLI_WORDEXP_MAXPARAMS_NUM*sizeof(int)); pwordexp->we_wordc=0; quoted=0; for(i=0,len=0;i0) { pwordexp->we_word_line_offset[pwordexp->we_wordc]=i-len; pwordexp->we_wordv[pwordexp->we_wordc]=malloc(len+1); memcpy(pwordexp->we_wordv[pwordexp->we_wordc],&words[i-len],len); pwordexp->we_wordv[pwordexp->we_wordc][len]=0; pwordexp->we_wordc++; } len=0; } else { len++; } } /*last*/ if(len>0) { pwordexp->we_word_line_offset[pwordexp->we_wordc]=i-len; pwordexp->we_wordv[pwordexp->we_wordc]=malloc(len+1); memcpy(pwordexp->we_wordv[pwordexp->we_wordc],&words[i-len],len); pwordexp->we_wordv[pwordexp->we_wordc][len]=0; pwordexp->we_wordc++; } return 0; } void yangcli_wordfree (yangcli_wordexp_t * pwordexp) { free(pwordexp->we_word_line_offset); free(pwordexp->we_wordv); } void yangcli_wordexp_dump(yangcli_wordexp_t * pwordexp) { int i; for(i=0;iwe_wordc;i++) { log_debug("\n[%d] %s",i,pwordexp->we_wordv[i]); } } yuma123_2.14/netconf/src/yangcli/yangcli_cmd.h0000664000175000017500000002254514770023131021426 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangcli_cmd #define _H_yangcli_cmd /* FILE: yangcli_cmd.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11-apr-09 abb Begun; moved from yangcli.c */ #ifndef _H_obj #include "obj.h" #endif #ifndef _H_rpc_err #include "rpc_err.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION top_command * * Top-level command handler * * INPUTS: * server_cb == server control block to use * line == input command line from user * * OUTPUTS: * state may be changed or other action taken * the line buffer is NOT consumed or freed by this function * * RETURNS: * status *********************************************************************/ extern status_t top_command (server_cb_t *server_cb, xmlChar *line); /******************************************************************** * FUNCTION conn_command * * Connection level command handler * * INPUTS: * server_cb == server control block to use * line == input command line from user * * OUTPUTS: * state may be changed or other action taken * the line buffer is NOT consumed or freed by this function * * RETURNS: * status *********************************************************************/ extern status_t conn_command (server_cb_t *server_cb, xmlChar *line); /******************************************************************** * FUNCTION do_startup_script * * Process run-script CLI parameter * * INPUTS: * server_cb == server control block to use * runscript == name of the script to run (could have path) * * SIDE EFFECTS: * runstack start with the runscript script if no errors * * RETURNS: * status *********************************************************************/ extern status_t do_startup_script (server_cb_t *server_cb, const xmlChar *runscript); /******************************************************************** * FUNCTION do_startup_command * * Process run-command CLI parameter * * INPUTS: * server_cb == server control block to use * runcommand == command string to run * * RETURNS: * status *********************************************************************/ extern status_t do_startup_command (server_cb_t *server_cb, const xmlChar *runcommand); /******************************************************************** * FUNCTION get_cmd_line * * Read the current runstack context and construct * a command string for processing by do_run. * - Extended lines will be concatenated in the * buffer. If a buffer overflow occurs due to this * concatenation, an error will be returned * * INPUTS: * server_cb == server control block to use * res == address of status result * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to the command line to process (should treat as CONST !!!) * NULL if some error *********************************************************************/ extern xmlChar * get_cmd_line (server_cb_t *server_cb, status_t *res); /******************************************************************** * FUNCTION do_connect * * INPUTS: * server_cb == server control block to use * rpc == rpc header for 'connect' command * line == input text from readline call, not modified or freed here * start == byte offset from 'line' where the parse RPC method * left off. This is eiother empty or contains some * parameters from the user * climode == TRUE if starting from CLI and should try * to connect right away if the mandatory parameters * are present * * OUTPUTS: * connect_valset parms may be set * create_session may be called * * RETURNS: * status *********************************************************************/ extern status_t do_connect (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 start, boolean climode); /******************************************************************** * FUNCTION parse_def * * Definitions have two forms: * def (default module used) * module:def (explicit module name used) * prefix:def (if prefix-to-module found, explicit module name used) * * Parse the possibly module-qualified definition (module:def) * and find the template for the requested definition * * INPUTS: * server_cb == server control block to use * dtyp == definition type * (NCX_NT_OBJ or NCX_NT_TYP) * line == input command line from user * len == address of output var for number of bytes parsed * retres == address of return status * * OUTPUTS: * *dtyp is set if it started as NONE * *len == number of bytes parsed * *retres == return status * * RETURNS: * pointer to the found definition template or NULL if not found *********************************************************************/ extern void * parse_def (server_cb_t *server_cb, ncx_node_t *dtyp, xmlChar *line, uint32 *len, status_t *retres); /******************************************************************** * FUNCTION send_keepalive_get * * Send a operation to the server to keep the session * from getting timed out; server sent a keepalive request * and SSH will drop the session unless data is sent * within a configured time * * INPUTS: * server_cb == server control block to use * * OUTPUTS: * state may be changed or other action taken * * RETURNS: * status *********************************************************************/ extern status_t send_keepalive_get (server_cb_t *server_cb); /******************************************************************** * FUNCTION get_valset * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the command being processed * line == CLI input in progress * res == address of status result * * OUTPUTS: * *res is set to the status * * RETURNS: * malloced valset filled in with the parameters for * the specified RPC * *********************************************************************/ extern val_value_t * get_valset (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, status_t *res); /******************************************************************** * FUNCTION do_line_recall (execute the recall local RPC) * * recall n * * INPUTS: * server_cb == server control block to use * num == entry number of history entry entry to recall * * RETURNS: * status *********************************************************************/ extern status_t do_line_recall (server_cb_t *server_cb, unsigned long num); /******************************************************************** * FUNCTION do_line_recall_string * * bang recall support * * INPUTS: * server_cb == server control block to use * line == command line to recall * * RETURNS: * status *********************************************************************/ extern status_t do_line_recall_string (server_cb_t *server_cb, const xmlChar *line); /******************************************************************** * FUNCTION parse_rpc_cli * * Call the cli_parse for an RPC input value set * * INPUTS: * server_cb == server control block to use * rpc == RPC to parse CLI for * line == input line to parse, starting with the parms to parse * res == pointer to status output * * OUTPUTS: * *res == status * * RETURNS: * pointer to malloced value set or NULL if none created, * may have errors, check *res *********************************************************************/ val_value_t* parse_rpc_cli ( server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *args, status_t *res ); status_t do_local_conn_command_reqdata(server_cb_t *server_cb, obj_template_t *rpc, xmlChar *line, uint32 len, val_value_t** reqdata, uint32* timeoutval); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli_cmd */ yuma123_2.14/netconf/src/yangcli/yangcli_uservars.h0000664000175000017500000000613714770023131022534 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangcli_uservars #define _H_yangcli_uservars /* FILE: yangcli_uservars.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* implement yangcli uservars command ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 01-oct-11 abb Begun; */ #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION do_uservars (local RPC) * * uservars clear * uservars load[=filespec] * uservars save[=filespec] * * Handle the uservars command * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the uservars command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_uservars (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); /******************************************************************** * FUNCTION load_uservars * * Load the user variables from the specified filespec * * INPUT: * server_cb == server control block to use * fspec == input filespec to use (NULL == default) * * RETURNS: * status *********************************************************************/ extern status_t load_uservars (server_cb_t *server_cb, const xmlChar *fspec); /******************************************************************** * FUNCTION save_uservars * * Save the uservares to the specified filespec * * INPUT: * server_cb == server control block to use * fspec == output filespec to use (NULL == default) * * RETURNS: * status *********************************************************************/ extern status_t save_uservars (server_cb_t *server_cb, const xmlChar *fspec); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli_uservars */ yuma123_2.14/netconf/src/yangcli/yangcli_show.c0000664000175000017500000006241714770023131021640 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_show.c NETCONF YANG-based CLI Tool show command ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13-aug-09 abb begun; moved from yangcli_cmd.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" #include "procdefs.h" #include "log.h" #include "mgr.h" #include "mgr_ses.h" #include "ncx.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "obj_help.h" #include "runstack.h" #include "status.h" #include "val.h" #include "val_util.h" #include "var.h" #include "xmlns.h" #include "xml_util.h" #include "xml_val.h" #include "xml_wr.h" #include "yangconst.h" #include "yangcli.h" #include "yangcli_cmd.h" #include "yangcli_show.h" #include "yangcli_util.h" /******************************************************************** * FUNCTION show_user_var * * generate the output for a global or local variable * * INPUTS: * server_cb == server control block to use * varname == variable name to show * vartype == type of user variable * val == value associated with this variable * mode == help mode in use * * RETURNS: * status *********************************************************************/ static status_t show_user_var (server_cb_t *server_cb, const xmlChar *varname, var_type_t vartype, val_value_t *val, help_mode_t mode) { xmlChar *objbuff; logfn_t logfn; boolean imode; int32 doubleindent; status_t res; res = NO_ERR; doubleindent = 1; imode = interactive_mode(); if (imode) { logfn = log_stdout; } else { logfn = log_write; } switch (vartype) { case VAR_TYP_GLOBAL: case VAR_TYP_LOCAL: case VAR_TYP_SESSION: case VAR_TYP_SYSTEM: case VAR_TYP_CONFIG: if (xml_strcmp(varname, val->name)) { doubleindent = 2; (*logfn)("\n %s ", varname); if (val->obj && obj_is_data_db(val->obj)) { res = obj_gen_object_id(val->obj, &objbuff); if (res != NO_ERR) { (*logfn)("[no object id]\n "); } else { (*logfn)("[%s]\n ", objbuff); m__free(objbuff); } } } else if (server_cb->display_mode == NCX_DISPLAY_MODE_JSON) { (*logfn)("\n %s: ", varname); if (!typ_is_simple(val->btyp)) { (*logfn)("\n"); } } break; default: ; } if (!typ_is_simple(val->btyp) && mode == HELP_MODE_BRIEF) { if (doubleindent == 1) { (*logfn)("\n %s (%s)", varname, tk_get_btype_sym(val->btyp)); } else { (*logfn)("\n (%s)", tk_get_btype_sym(val->btyp)); } } else { val_dump_value_max(val, server_cb->defindent * doubleindent, server_cb->defindent, (imode) ? DUMP_VAL_STDOUT : DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); } return res; } /* show_user_var */ /******************************************************************** * FUNCTION do_show_cli (sub-mode of local RPC) * * show CLI parms * * INPUTS: * server_cb == server control block to use *********************************************************************/ static void do_show_cli (server_cb_t *server_cb) { val_value_t *mgrset; logfn_t logfn; boolean imode; imode = interactive_mode(); if (imode) { logfn = log_stdout; } else { logfn = log_write; } mgrset = get_mgr_cli_valset(); /* CLI Parameters */ if (mgrset && val_child_cnt(mgrset)) { (*logfn)("\nCLI Variables\n"); val_dump_value_max(mgrset, 0, server_cb->defindent, (imode) ? DUMP_VAL_STDOUT : DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); (*logfn)("\n"); } else { (*logfn)("\nNo CLI variables\n"); } } /* do_show_cli */ /******************************************************************** * FUNCTION do_show_session (sub-mode of local RPC) * * show session startup screen * * INPUTS: * server_cb == server control block to use * mode == help mode *********************************************************************/ static void do_show_session (server_cb_t *server_cb, help_mode_t mode) { ses_cb_t *scb; scb = mgr_ses_get_scb(server_cb->mysid); if (scb == NULL) { /* session was dropped */ log_write("\nError: No session available." " Not connected to any server.\n"); return; } report_capabilities(server_cb, scb, FALSE, mode); log_write("\n"); } /* do_show_session */ /******************************************************************** * FUNCTION do_show_system (sub-mode of local RPC) * * show system parms * * INPUTS: * server_cb == server control block to use * mode == help mode *********************************************************************/ static void do_show_system (server_cb_t *server_cb, help_mode_t mode) { ncx_var_t *var; dlq_hdr_t *que; logfn_t logfn; boolean imode, first; imode = interactive_mode(); if (imode) { logfn = log_stdout; } else { logfn = log_write; } /* System Script Variables */ que = runstack_get_que(server_cb->runstack_context, ISGLOBAL); first = TRUE; for (var = (ncx_var_t *)dlq_firstEntry(que); var != NULL; var = (ncx_var_t *)dlq_nextEntry(var)) { if (var->vartype != VAR_TYP_SYSTEM) { continue; } if (first) { (*logfn)("\nRead-only environment variables\n"); first = FALSE; } show_user_var(server_cb, var->name, var->vartype, var->val, mode); } if (first) { (*logfn)("\nNo read-only environment variables\n"); } (*logfn)("\n"); /* System Config Variables */ que = runstack_get_que(server_cb->runstack_context, ISGLOBAL); first = TRUE; for (var = (ncx_var_t *)dlq_firstEntry(que); var != NULL; var = (ncx_var_t *)dlq_nextEntry(var)) { if (var->vartype != VAR_TYP_CONFIG) { continue; } if (first) { (*logfn)("\nRead-write system variables\n"); first = FALSE; } show_user_var(server_cb, var->name, var->vartype, var->val, mode); } if (first) { (*logfn)("\nNo system config variables\n"); } (*logfn)("\n"); } /* do_show_system */ /******************************************************************** * FUNCTION do_show_vars (sub-mode of local RPC) * * show brief info for all user variables * * INPUTS: * server_cb == server control block to use * mode == help mode requested * shortmode == TRUE if printing just global or local variables * FALSE to print everything * isglobal == TRUE if print just globals * FALSE to print just locals * Ignored unless shortmode==TRUE * isany == TRUE to choose global or local * FALSE to use 'isglobal' valuse only * RETURNS: * status *********************************************************************/ static status_t do_show_vars (server_cb_t *server_cb, help_mode_t mode, boolean shortmode, boolean isglobal, boolean isany) { ncx_var_t *var; dlq_hdr_t *que; logfn_t logfn; boolean first, imode; imode = interactive_mode(); if (imode) { logfn = log_stdout; } else { logfn = log_write; } if (mode > HELP_MODE_BRIEF && !shortmode) { /* CLI Parameters */ do_show_cli(server_cb); } /* System Script Variables */ if (!shortmode) { do_show_system(server_cb, mode); } /* Global Script Variables */ if (!shortmode || isglobal) { que = runstack_get_que(server_cb->runstack_context, ISGLOBAL); first = TRUE; for (var = (ncx_var_t *)dlq_firstEntry(que); var != NULL; var = (ncx_var_t *)dlq_nextEntry(var)) { if (var->vartype != VAR_TYP_GLOBAL) { continue; } if (first) { (*logfn)("\nGlobal variables\n"); first = FALSE; } show_user_var(server_cb, var->name, var->vartype, var->val, mode); } if (first) { (*logfn)("\nNo global variables\n"); } (*logfn)("\n"); } /* Local Script Variables */ if (!shortmode || !isglobal || isany) { que = runstack_get_que(server_cb->runstack_context, ISLOCAL); first = TRUE; for (var = (ncx_var_t *)dlq_firstEntry(que); var != NULL; var = (ncx_var_t *)dlq_nextEntry(var)) { if (first) { (*logfn)("\nLocal variables\n"); first = FALSE; } show_user_var(server_cb, var->name, var->vartype, var->val, mode); } if (first) { (*logfn)("\nNo local variables\n"); } (*logfn)("\n"); } return NO_ERR; } /* do_show_vars */ /******************************************************************** * FUNCTION do_show_var (sub-mode of local RPC) * * show full info for one user var * * INPUTS: * server_cb == server control block to use * name == variable name to find * isglobal == TRUE if global var, FALSE if local var * isany == TRUE if don't care (global or local) * == FALSE to force local or global with 'isglobal' * mode == help mode requested * * RETURNS: * status *********************************************************************/ static status_t do_show_var (server_cb_t *server_cb, const xmlChar *name, var_type_t vartype, boolean isany, help_mode_t mode) { val_value_t *val; logfn_t logfn; boolean imode; imode = interactive_mode(); if (imode) { logfn = log_stdout; } else { logfn = log_write; } if (isany) { /* skipping VAR_TYP_SESSION for now */ val = var_get_local(server_cb->runstack_context, name); if (val) { vartype = VAR_TYP_LOCAL; } else { val = var_get(server_cb->runstack_context, name, VAR_TYP_GLOBAL); if (val) { vartype = VAR_TYP_GLOBAL; } else { val = var_get(server_cb->runstack_context, name, VAR_TYP_CONFIG); if (val) { vartype = VAR_TYP_CONFIG; } else { val = var_get(server_cb->runstack_context, name, VAR_TYP_SYSTEM); if (val) { vartype = VAR_TYP_SYSTEM; } } } } } else { val = var_get(server_cb->runstack_context, name, vartype); } if (val) { show_user_var(server_cb, name, vartype, val, mode); (*logfn)("\n"); } else { (*logfn)("\nVariable '%s' not found", name); return ERR_NCX_DEF_NOT_FOUND; } return NO_ERR; } /* do_show_var */ /******************************************************************** * FUNCTION do_show_module (sub-mode of local RPC) * * show module=mod-name * * INPUTS: * mod == module to show * mode == requested help mode * * RETURNS: * status *********************************************************************/ static status_t do_show_module (const ncx_module_t *mod, help_mode_t mode) { help_data_module(mod, mode); return NO_ERR; } /* do_show_module */ /******************************************************************** * FUNCTION do_show_one_module (sub-mode of show modules RPC) * * for 1 of N: show modules * * INPUTS: * mod == module to show * mode == requested help mode * * RETURNS: * status *********************************************************************/ static status_t do_show_one_module (ncx_module_t *mod, help_mode_t mode) { boolean imode; imode = interactive_mode(); if (mode == HELP_MODE_BRIEF) { if (imode) { log_stdout("\n %s", mod->name); } else { log_write("\n %s", mod->name); } } else if (mode == HELP_MODE_NORMAL) { if (imode) { if (mod->version) { log_stdout("\n %s:%s@%s", ncx_get_mod_xmlprefix(mod), mod->name, mod->version); } else { log_stdout("\n %s:%s", ncx_get_mod_xmlprefix(mod), mod->name); } } else { if (mod->version) { log_write("\n %s@%s", mod->name, mod->version); } else { log_write("\n %s", mod->name); } } } else { help_data_module(mod, HELP_MODE_BRIEF); } return NO_ERR; } /* do_show_one_module */ /******************************************************************** * FUNCTION do_show_modules (sub-mode of local RPC) * * show modules * * INPUTS: * server_cb == server control block to use * mode == requested help mode * * RETURNS: * status *********************************************************************/ static status_t do_show_modules (server_cb_t *server_cb, help_mode_t mode) { ncx_module_t *mod; modptr_t *modptr; boolean anyout, imode; status_t res; imode = interactive_mode(); anyout = FALSE; res = NO_ERR; if (use_servercb(server_cb)) { for (modptr = (modptr_t *)dlq_firstEntry(&server_cb->modptrQ); modptr != NULL && res == NO_ERR; modptr = (modptr_t *)dlq_nextEntry(modptr)) { res = do_show_one_module(modptr->mod, mode); anyout = TRUE; } } else { mod = ncx_get_first_module(); while (mod && res == NO_ERR) { res = do_show_one_module(mod, mode); anyout = TRUE; mod = ncx_get_next_module(mod); } } if (anyout) { if (imode) { log_stdout("\n"); } else { log_write("\n"); } } else { if (imode) { log_stdout("\nyangcli: no modules loaded\n"); } else { log_error("\nyangcli: no modules loaded\n"); } } return res; } /* do_show_modules */ /******************************************************************** * FUNCTION do_show_one_object (sub-mode of show objects local RPC) * * show objects: 1 of N * * INPUTS: * obj == object to show * mode == requested help mode * anyout == address of return anyout status * * OUTPUTS: * *anyout set to TRUE only if any suitable objects found * * RETURNS: * status *********************************************************************/ static status_t do_show_one_object (obj_template_t *obj, help_mode_t mode, boolean *anyout) { boolean imode; imode = interactive_mode(); if (obj_is_data_db(obj) && obj_has_name(obj) && !obj_is_hidden(obj) && !obj_is_abstract(obj)) { if (mode == HELP_MODE_BRIEF) { if (imode) { log_stdout("\n%s:%s", obj_get_mod_name(obj), obj_get_name(obj)); } else { log_write("\n%s:%s", obj_get_mod_name(obj), obj_get_name(obj)); } } else { obj_dump_template(obj, mode-1, 0, 0); } *anyout = TRUE; } return NO_ERR; } /* do_show_one_object */ /******************************************************************** * FUNCTION do_show_objects (sub-mode of local RPC) * * show objects * * INPUTS: * server_cb == server control block to use * mode == requested help mode * * RETURNS: * status *********************************************************************/ static status_t do_show_objects (server_cb_t *server_cb, help_mode_t mode) { ncx_module_t *mod; obj_template_t *obj; modptr_t *modptr; boolean anyout, imode; status_t res; imode = interactive_mode(); anyout = FALSE; res = NO_ERR; if (use_servercb(server_cb)) { for (modptr = (modptr_t *) dlq_firstEntry(&server_cb->modptrQ); modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { for (obj = ncx_get_first_object(modptr->mod); obj != NULL && res == NO_ERR; obj = ncx_get_next_object(modptr->mod, obj)) { res = do_show_one_object(obj, mode, &anyout); } } } else { mod = ncx_get_first_module(); while (mod) { for (obj = ncx_get_first_object(mod); obj != NULL && res == NO_ERR; obj = ncx_get_next_object(mod, obj)) { res = do_show_one_object(obj, mode, &anyout); } mod = (ncx_module_t *)ncx_get_next_module(mod); } } if (anyout) { if (imode) { log_stdout("\n"); } else { log_write("\n"); } } return res; } /* do_show_objects */ /******************************************************************** * FUNCTION do_show (local RPC) * * show module=mod-name * modules * def=def-nmae * * Get the specified parameter and show the internal info, * based on the parameter * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_show (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *parm; ncx_module_t *mod; status_t res; boolean imode, done; help_mode_t mode; xmlChar versionbuffer[NCX_VERSION_BUFFSIZE]; res = NO_ERR; imode = interactive_mode(); valset = get_valset(server_cb, rpc, &line[len], &res); if (valset && res == NO_ERR) { mode = HELP_MODE_NORMAL; /* check if the 'brief' flag is set first */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_BRIEF); if (parm && parm->res == NO_ERR) { mode = HELP_MODE_BRIEF; } else { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_FULL); if (parm && parm->res == NO_ERR) { mode = HELP_MODE_FULL; } } /* get the 1 of N 'showtype' choice */ done = FALSE; /* show cli */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_CLI); if (parm) { do_show_cli(server_cb); done = TRUE; } /* show local */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_LOCAL); if (parm) { res = do_show_var(server_cb, VAL_STR(parm), VAR_TYP_LOCAL, FALSE, mode); done = TRUE; } } /* show locals */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_LOCALS); if (parm) { res = do_show_vars(server_cb, mode, TRUE, FALSE, FALSE); done = TRUE; } } /* show objects */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_OBJECTS); if (parm) { res = do_show_objects(server_cb, mode); done = TRUE; } } /* show global */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_GLOBAL); if (parm) { res = do_show_var(server_cb, VAL_STR(parm), VAR_TYP_GLOBAL, FALSE, mode); done = TRUE; } } /* show globals */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_GLOBALS); if (parm) { res = do_show_vars(server_cb, mode, TRUE, TRUE, FALSE); done = TRUE; } } /* show session */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_SESSION); if (parm) { do_show_session(server_cb, mode); done = TRUE; } /* show system */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_SYSTEM); if (parm) { do_show_system(server_cb, mode); done = TRUE; } /* show var */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_VAR); if (parm) { res = do_show_var(server_cb, VAL_STR(parm), VAR_TYP_NONE, TRUE, mode); done = TRUE; } } /* show vars */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_VARS); if (parm) { res = do_show_vars(server_cb, mode, FALSE, FALSE, TRUE); done = TRUE; } } /* show module */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_MODULE); if (parm) { mod = find_module(server_cb, VAL_STR(parm)); if (mod) { res = do_show_module(mod, mode); } else { if (imode) { log_stdout("\nyangcli: module (%s) not loaded", VAL_STR(parm)); } else { log_error("\nyangcli: module (%s) not loaded", VAL_STR(parm)); } } done = TRUE; } } /* show modules */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_MODULES); if (parm) { res = do_show_modules(server_cb, mode); done = TRUE; } } /* show version */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_VERSION); if (parm) { res = ncx_get_version(versionbuffer, NCX_VERSION_BUFFSIZE); if (res == NO_ERR) { if (imode) { log_stdout("\nyangcli version %s\n", versionbuffer); } else { log_write("\nyangcli version %s\n", versionbuffer); } } done = TRUE; } } } if (valset) { val_free_value(valset); } return res; } /* do_show */ /* END yangcli_show.c */ yuma123_2.14/netconf/src/yangcli/yangcli.h0000664000175000017500000006572614770023131020613 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2012 - 2016, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangcli #define _H_yangcli /* FILE: yangcli.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 27-mar-09 abb Begun; moved from yangcli.c */ #include //#include #include "libtecla.h" #include "ncxconst.h" #include "ncxmod.h" #include "ncxtypes.h" #include "mgr_io.h" #include "mgr_rpc.h" #include "runstack.h" #include "ses.h" #include "status.h" #include "val.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define PROGNAME "yangcli" #define MAX_PROMPT_LEN 56 #define YANGCLI_MAX_NEST 16 #define YANGCLI_MAX_RUNPARMS 9 #define YANGCLI_LINELEN 4095 /* 8K CLI buffer per server session */ #define YANGCLI_BUFFLEN 8192 #define YANGCLI_HISTLEN 4095 #define YANGCLI_MOD (const xmlChar *)"yangcli" #define YANGCLI_EX_MOD (const xmlChar *)"yangcli-ex" #define YANGCLI_NUM_TIMERS 16 /* look in yangcli.c:yangcli_init for defaults not listed here */ #define YANGCLI_DEF_HISTORY_FILE (const xmlChar *)"~/.yuma/.yangcli_history" #define YANGCLI_DEF_ALIASES_FILE (const xmlChar *)"~/.yuma/.yangcli_aliases" #define YANGCLI_DEF_USERVARS_FILE \ (const xmlChar *)"~/.yuma/yangcli_uservars.xml" #define YANGCLI_DEF_TIMEOUT 30 #define YANGCLI_DEF_SERVER (const xmlChar *)"default" #define YANGCLI_DEF_DISPLAY_MODE NCX_DISPLAY_MODE_PLAIN #define YANGCLI_DEF_FIXORDER TRUE #define YANGCLI_DEF_CONF_FILE (const xmlChar *)"/etc/yuma/yangcli.conf" #define YANGCLI_DEF_SERVER (const xmlChar *)"default" #define YANGCLI_DEF_TEST_OPTION OP_TESTOP_SET #define YANGCLI_DEF_ERROR_OPTION OP_ERROP_NONE #define YANGCLI_DEF_DEFAULT_OPERATION OP_DEFOP_MERGE #define YANGCLI_DEF_WITH_DEFAULTS NCX_WITHDEF_NONE #define YANGCLI_DEF_INDENT 2 #define YANGCLI_DEF_BAD_DATA NCX_BAD_DATA_CHECK #define YANGCLI_DEF_MAXLOOPS 65535 #define YANGCLI_DEF_HISTORY_LINES 25 #define YANGCLI_RECALL_CHAR '!' #ifdef MACOSX #define ENV_HOST (const char *)"HOST" #else #define ENV_HOST (const char *)"HOSTNAME" #endif #define ENV_SHELL (const char *)"SHELL" #define ENV_USER (const char *)"USER" #define ENV_LANG (const char *)"LANG" /* CLI parmset for the ncxcli application */ #define YANGCLI_BOOT YANGCLI_MOD /* core modules auto-loaded at startup */ #define NCXDTMOD (const xmlChar *)"yuma-types" #define XSDMOD (const xmlChar *)"yuma-xsd" #define DEF_PROMPT (const xmlChar *)"yangcli> " #define DEF_FALSE_PROMPT (const xmlChar *)"yangcli[F]> " #define DEF_FN_PROMPT (const xmlChar *)"yangcli:" #define MORE_PROMPT (const xmlChar *)" more> " #define FALSE_PROMPT (const xmlChar *)"[F]" #define YESNO_NODEF 0 #define YESNO_CANCEL 0 #define YESNO_YES 1 #define YESNO_NO 2 /* YANGCLI boot and operation parameter names * matches parm clauses in yangcli container in yangcli.yang */ #define YANGCLI_ALIASES_FILE (const xmlChar *)"aliases-file" #define YANGCLI_ALT_NAMES (const xmlChar *)"alt-names" #define YANGCLI_AUTOALIASES (const xmlChar *)"autoaliases" #define YANGCLI_AUTOCOMP (const xmlChar *)"autocomp" #define YANGCLI_AUTOHISTORY (const xmlChar *)"autohistory" #define YANGCLI_AUTOLOAD (const xmlChar *)"autoload" #define YANGCLI_AUTOUSERVARS (const xmlChar *)"autouservars" #define YANGCLI_BADDATA (const xmlChar *)"bad-data" #define YANGCLI_BATCHMODE (const xmlChar *)"batch-mode" #define YANGCLI_BRIEF (const xmlChar *)"brief" #define YANGCLI_CLEANUP (const xmlChar *)"cleanup" #define YANGCLI_CLEAR (const xmlChar *)"clear" #define YANGCLI_CLI (const xmlChar *)"cli" #define YANGCLI_COMMAND (const xmlChar *)"command" #define YANGCLI_COMMANDS (const xmlChar *)"commands" #define YANGCLI_CONFIG (const xmlChar *)"config" #define YANGCLI_DEF_MODULE (const xmlChar *)"default-module" #define YANGCLI_DELTA (const xmlChar *)"delta" #define YANGCLI_DIR (const xmlChar *)"dir" #define YANGCLI_DISPLAY_MODE (const xmlChar *)"display-mode" #define YANGCLI_ECHO (const xmlChar *)"echo" #define YANGCLI_ECHO_REPLIES (const xmlChar *)"echo-replies" #define YANGCLI_ECHO_REQUESTS (const xmlChar *)"echo-requests" #define YANGCLI_EDIT_TARGET (const xmlChar *)"edit-target" #define YANGCLI_ERROR_OPTION (const xmlChar *)"error-option" #define YANGCLI_FILES (const xmlChar *)"files" #define YANGCLI_FIXORDER (const xmlChar *)"fixorder" #define YANGCLI_FROM_CLI (const xmlChar *)"from-cli" #define YANGCLI_FORCE_TARGET (const xmlChar *)"force-target" #define YANGCLI_FULL (const xmlChar *)"full" #define YANGCLI_GLOBAL (const xmlChar *)"global" #define YANGCLI_GLOBALS (const xmlChar *)"globals" #define YANGCLI_ID (const xmlChar *)"id" #define YANGCLI_INDEX (const xmlChar *)"index" #define YANGCLI_LOAD (const xmlChar *)"load" #define YANGCLI_LOCK_TIMEOUT (const xmlChar *)"lock-timeout" #define YANGCLI_LOCAL (const xmlChar *)"local" #define YANGCLI_LOCALS (const xmlChar *)"locals" #define YANGCLI_MATCH_NAMES (const xmlChar *)"match-names" #define YANGCLI_MODULE (const xmlChar *)"module" #define YANGCLI_MODULES (const xmlChar *)"modules" #define YANGCLI_NCPORT (const xmlChar *)"ncport" #define YANGCLI_NOFILL (const xmlChar *)"nofill" #define YANGCLI_OBJECTS (const xmlChar *)"objects" #define YANGCLI_OIDS (const xmlChar *)"oids" #define YANGCLI_OPERATION (const xmlChar *)"operation" #define YANGCLI_OPTIONAL (const xmlChar *)"optional" #define YANGCLI_ORDER (const xmlChar *)"order" #define YANGCLI_PASSWORD (const xmlChar *)"password" #define YANGCLI_PROTOCOLS (const xmlChar *)"protocols" #define YANGCLI_PRIVATE_KEY (const xmlChar *)"private-key" #define YANGCLI_PRIVATE_KEY_PASS (const xmlChar *)"private-key-pass" #define YANGCLI_PUBLIC_KEY (const xmlChar *)"public-key" #define YANGCLI_RESTART_OK (const xmlChar *)"restart-ok" #define YANGCLI_RETRY_INTERVAL (const xmlChar *)"retry-interval" #define YANGCLI_RUN_COMMAND (const xmlChar *)"run-command" #define YANGCLI_RUN_SCRIPT (const xmlChar *)"run-script" #define YANGCLI_START (const xmlChar *)"start" #define YANGCLI_SCRIPTS (const xmlChar *)"scripts" #define YANGCLI_SERVER (const xmlChar *)"server" #define YANGCLI_SESSION (const xmlChar *)"session" #define YANGCLI_SYSTEM (const xmlChar *)"system" #define YANGCLI_TCP_DIRECT_ENABLE (const xmlChar *)"tcp-direct-enable" #define YANGCLI_TEST_OPTION (const xmlChar *)"test-option" #define YANGCLI_TIMEOUT (const xmlChar *)"timeout" #define YANGCLI_TIME_RPCS (const xmlChar *)"time-rpcs" #define YANGCLI_TRANSPORT (const xmlChar *)"transport" #define YANGCLI_USE_AGENT (const xmlChar *)"use-agent" #define YANGCLI_USE_XMLHEADER (const xmlChar *)"use-xmlheader" #define YANGCLI_USER (const xmlChar *)"user" #define YANGCLI_USERVARS_FILE (const xmlChar *)"uservars-file" #define YANGCLI_VALUE (const xmlChar *)"value" #define YANGCLI_VAR (const xmlChar *)"var" #define YANGCLI_VARREF (const xmlChar *)"varref" #define YANGCLI_VARS (const xmlChar *)"vars" #define YANGCLI_VARTYPE (const xmlChar *)"vartype" #define YANGCLI_WITH_DEFAULTS (const xmlChar *)"with-defaults" /* * matches augmented parm clauses in yangcli container * from yangcli-ex.yang */ #define YANGCLI_KEEP_SESSION_MODEL_COPIES_AFTER_COMPILATION (const xmlChar *)"keep-session-model-copies-after-compilation" #define YANGCLI_DUMP_SESSION (const xmlChar *)"dump-session" /* YANGCLI local RPC commands */ #define YANGCLI_ALIAS (const xmlChar *)"alias" #define YANGCLI_ALIASES (const xmlChar *)"aliases" #define YANGCLI_CD (const xmlChar *)"cd" #define YANGCLI_CONNECT (const xmlChar *)"connect" #define YANGCLI_CREATE (const xmlChar *)"create" #define YANGCLI_DELETE (const xmlChar *)"delete" #define YANGCLI_ELSE (const xmlChar *)"else" #define YANGCLI_ELIF (const xmlChar *)"elif" #define YANGCLI_END (const xmlChar *)"end" #define YANGCLI_EVAL (const xmlChar *)"eval" #define YANGCLI_EVENTLOG (const xmlChar *)"eventlog" #define YANGCLI_FILL (const xmlChar *)"fill" #define YANGCLI_GET_LOCKS (const xmlChar *)"get-locks" #define YANGCLI_HELP (const xmlChar *)"help" #define YANGCLI_HISTORY (const xmlChar *)"history" #define YANGCLI_INSERT (const xmlChar *)"insert" #define YANGCLI_IF (const xmlChar *)"if" #define YANGCLI_LIST (const xmlChar *)"list" #define YANGCLI_LOG_ERROR (const xmlChar *)"log-error" #define YANGCLI_LOG_WARN (const xmlChar *)"log-warn" #define YANGCLI_LOG_INFO (const xmlChar *)"log-info" #define YANGCLI_LOG_DEBUG (const xmlChar *)"log-debug" #define YANGCLI_MERGE (const xmlChar *)"merge" #define YANGCLI_MGRLOAD (const xmlChar *)"mgrload" #define YANGCLI_PWD (const xmlChar *)"pwd" #define YANGCLI_QUIT (const xmlChar *)"quit" #define YANGCLI_RECALL (const xmlChar *)"recall" #define YANGCLI_RELEASE_LOCKS (const xmlChar *)"release-locks" #define YANGCLI_REMOVE (const xmlChar *)"remove" #define YANGCLI_REPLACE (const xmlChar *)"replace" #define YANGCLI_RUN (const xmlChar *)"run" #define YANGCLI_SAVE (const xmlChar *)"save" #define YANGCLI_SET (const xmlChar *)"set" #define YANGCLI_SGET (const xmlChar *)"sget" #define YANGCLI_SGET_CONFIG (const xmlChar *)"sget-config" #define YANGCLI_SHOW (const xmlChar *)"show" #define YANGCLI_START_TIMER (const xmlChar *)"start-timer" #define YANGCLI_STOP_TIMER (const xmlChar *)"stop-timer" #define YANGCLI_WHILE (const xmlChar *)"while" #define YANGCLI_XGET (const xmlChar *)"xget" #define YANGCLI_XGET_CONFIG (const xmlChar *)"xget-config" #define YANGCLI_UNSET (const xmlChar *)"unset" #define YANGCLI_USERVARS (const xmlChar *)"uservars" /* specialized prompts for the fill command */ #define YANGCLI_PR_LLIST (const xmlChar *)"Add another leaf-list?" #define YANGCLI_PR_LIST (const xmlChar *)"Add another list?" /* retry for a lock request once per second */ #define YANGCLI_RETRY_INTERNVAL 1 #define YANGCLI_EXPR (const xmlChar *)"expr" #define YANGCLI_DOCROOT (const xmlChar *)"docroot" #define YANGCLI_CONTEXT (const xmlChar *)"context" #define YANGCLI_MSG (const xmlChar *)"msg" #define YANGCLI_MAXLOOPS (const xmlChar *)"maxloops" #define YANGCLI_MAX_INDENT 9 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* cache the module pointers known by a particular server, * as reported in the session message */ typedef struct modptr_t_ { dlq_hdr_t qhdr; ncx_module_t *mod; /* back-ptr, not live */ ncx_list_t *feature_list; /* back-ptr, not live */ ncx_list_t *deviation_list; /* back-ptr, not live */ } modptr_t; /* save the requested result format type */ typedef enum result_format_t { RF_NONE, RF_TEXT, RF_XML, RF_JSON } result_format_t; /* command state enumerations for each situation * where the tecla get_line function is called */ typedef enum command_state_t { CMD_STATE_NONE, CMD_STATE_FULL, CMD_STATE_GETVAL, CMD_STATE_YESNO, CMD_STATE_MORE } command_state_t; /* command mode enumerations */ typedef enum command_mode_t { CMD_MODE_NONE, CMD_MODE_NORMAL, CMD_MODE_AUTOLOAD, CMD_MODE_AUTOLOCK, CMD_MODE_AUTOUNLOCK, CMD_MODE_AUTODISCARD, CMD_MODE_SAVE, CMD_MODE_YANG_LIBRARY } command_mode_t; /* saved state for libtecla command line completion */ typedef struct completion_state_t_ { obj_template_t *cmdobj; obj_template_t *cmdinput; obj_template_t *cmdcurparm; struct server_cb_t_ *server_cb; ncx_module_t *cmdmodule; command_state_t cmdstate; boolean assignstmt; } completion_state_t; /* save the server lock state for get-locks and release-locks */ typedef enum lock_state_t { LOCK_STATE_NONE, LOCK_STATE_IDLE, LOCK_STATE_REQUEST_SENT, LOCK_STATE_TEMP_ERROR, LOCK_STATE_FATAL_ERROR, LOCK_STATE_ACTIVE, LOCK_STATE_RELEASE_SENT, LOCK_STATE_RELEASED } lock_state_t; /* autolock control block, used by get-locks, release-locks */ typedef struct lock_cb_t_ { ncx_cfg_t config_id; const xmlChar *config_name; lock_state_t lock_state; boolean lock_used; time_t start_time; time_t last_msg_time; } lock_cb_t; /* auto-get-schema control block for a module */ typedef struct autoload_modcb_t_ { dlq_hdr_t qhdr; xmlChar *module; xmlChar *source; xmlChar *revision; const ncx_list_t *features; const ncx_list_t *deviations; status_t res; boolean retrieved; } autoload_modcb_t; /* auto-get-schema control block for a deviation */ typedef struct autoload_devcb_t_ { dlq_hdr_t qhdr; xmlChar *module; xmlChar *source; status_t res; boolean retrieved; } autoload_devcb_t; /* yangcli command alias control block */ typedef struct alias_cb_t_ { dlq_hdr_t qhdr; xmlChar *name; xmlChar *value; uint8 quotes; } alias_cb_t; /* NETCONF server control block */ typedef struct server_cb_t_ { dlq_hdr_t qhdr; xmlChar *name; xmlChar *address; xmlChar *password; xmlChar *publickey; xmlChar *privatekey; const xmlChar *default_target; val_value_t *connect_valset; /* assignment statement support */ xmlChar *result_name; var_type_t result_vartype; xmlChar *result_filename; result_format_t result_format; val_value_t *local_result; /* per-server shadows of global config vars */ boolean get_optional; ncx_display_mode_t display_mode; uint32 timeout; uint32 lock_timeout; ncx_bad_data_t baddata; log_debug_t log_level; boolean autoload; boolean keep_session_model_copies_after_compilation; boolean fixorder; op_testop_t testoption; op_errop_t erroption; op_defop_t defop; ncx_withdefaults_t withdefaults; int32 defindent; boolean echo_replies; boolean echo_requests; boolean time_rpcs; ncx_name_match_t match_names; boolean alt_names; boolean overwrite_filevars; boolean use_xmlheader; /* session support */ mgr_io_state_t state; ses_id_t mysid; mgr_io_returncode_t returncode; int32 errnocode; command_mode_t command_mode; /* get-locks and release-locks support * there is one entry for each database * indexed by the ncx_cfg_t enum */ boolean locks_active; boolean locks_waiting; ncx_cfg_t locks_cur_cfg; uint32 locks_timeout; uint32 locks_retry_interval; boolean locks_cleanup; time_t locks_start_time; lock_cb_t lock_cb[NCX_NUM_CFGS]; /* TBD: session-specific user variables */ dlq_hdr_t varbindQ; /* Q of ncx_var_t */ /* before any server modules are loaded, all the * modules are checked out, and the results are stored in * this Q of ncxmod_search_result_t */ dlq_hdr_t searchresultQ; /* Q of ncxmod_search_result_t */ ncxmod_search_result_t *cursearchresult; /* contains only the modules that the server is using * plus the 'netconf.yang' module */ dlq_hdr_t modptrQ; /* Q of modptr_t */ /* contains received notifications */ dlq_hdr_t notificationQ; /* Q of mgr_not_msg_t */ /* support fot auto-get-schema feature */ dlq_hdr_t autoload_savedevQ; /* Q of ncx_save_deviations_t */ boolean deviations_applied; /* support for temp directory for downloaded modules */ ncxmod_temp_progcb_t *temp_progcb; ncxmod_temp_sescb_t *temp_sescb; /* runstack context for script processing */ runstack_context_t *runstack_context; /* per session timer support */ struct timeval timers[YANGCLI_NUM_TIMERS]; /* per-session CLI support */ const xmlChar *cli_fn; GetLine *cli_gl; xmlChar *history_filename; xmlChar *history_line; boolean history_line_active; boolean history_auto; uint32 history_size; completion_state_t completion_state; boolean climore; xmlChar clibuff[YANGCLI_BUFFLEN]; } server_cb_t; /******************************************************************** * FUNCTION yangcli_init_module_static_vars * * Init the NCX CLI application static vars * * INPUTS: * argc == number of strings in argv array * argv == array of command line strings * * RETURNS: * status *********************************************************************/ void yangcli_init_module_static_vars(); /******************************************************************** * FUNCTION get_autocomp * * Get the autocomp parameter value * * RETURNS: * autocomp boolean value *********************************************************************/ extern boolean get_autocomp (void); /******************************************************************** * FUNCTION get_autoload * * Get the autoload parameter value * * RETURNS: * autoload boolean value *********************************************************************/ extern boolean get_autoload (void); /******************************************************************** * FUNCTION get_batchmode * * Get the batchmode parameter value * * RETURNS: * batchmode boolean value *********************************************************************/ extern boolean get_batchmode (void); /******************************************************************** * FUNCTION get_default_module * * Get the default module * * RETURNS: * default module value *********************************************************************/ extern const xmlChar * get_default_module (void); /******************************************************************** * FUNCTION get_runscript * * Get the runscript variable * * RETURNS: * runscript value *********************************************************************/ extern const xmlChar * get_runscript (void); /******************************************************************** * FUNCTION get_baddata * * Get the baddata parameter * * RETURNS: * baddata enum value *********************************************************************/ extern ncx_bad_data_t get_baddata (void); /******************************************************************** * FUNCTION get_yangcli_mod * * Get the yangcli module * * RETURNS: * yangcli module *********************************************************************/ extern ncx_module_t * get_yangcli_mod (void); /******************************************************************** * FUNCTION get_mgr_cli_valset * * Get the CLI value set * * RETURNS: * mgr_cli_valset variable *********************************************************************/ extern val_value_t * get_mgr_cli_valset (void); /******************************************************************** * FUNCTION get_connect_valset * * Get the connect value set * * RETURNS: * connect_valset variable *********************************************************************/ extern val_value_t * get_connect_valset (void); /******************************************************************** * FUNCTION get_aliases_file * * Get the aliases-file value * * RETURNS: * aliases_file variable *********************************************************************/ extern const xmlChar * get_aliases_file (void); /******************************************************************** * FUNCTION get_uservars_file * * Get the uservars-file value * * RETURNS: * aliases_file variable *********************************************************************/ extern const xmlChar * get_uservars_file (void); /******************************************************************** * FUNCTION replace_connect_valset * * Replace the current connect value set with a clone * of the specified connect valset * * INPUTS: * valset == value node to clone that matches the object type * of the input section of the connect operation * * RETURNS: * status *********************************************************************/ extern status_t replace_connect_valset (const val_value_t *valset); /******************************************************************** * FUNCTION get_aliasQ * * Get the aliasQ value pointer * * RETURNS: * aliasQ variable *********************************************************************/ extern dlq_hdr_t * get_aliasQ (void); /******************************************************************** * FUNCTION yangcli_reply_handler * * handle incoming messages * * INPUTS: * scb == session receiving RPC reply * req == original request returned for freeing or reusing * rpy == reply received from the server (for checking then freeing) * * RETURNS: * none *********************************************************************/ extern void yangcli_reply_handler (ses_cb_t *scb, mgr_rpc_req_t *req, mgr_rpc_rpy_t *rpy); /******************************************************************** * FUNCTION finish_result_assign * * finish the assignment to result_name or result_filename * use 1 of these 2 parms: * resultval == result to output to file * resultstr == result to output as string * * INPUTS: * server_cb == server control block to use * * RETURNS: * status *********************************************************************/ extern status_t finish_result_assign (server_cb_t *server_cb, val_value_t *resultvar, const xmlChar *resultstr); /******************************************************************** * FUNCTION report_capabilities * * Generate a start session report, listing the capabilities * of the NETCONF server * * INPUTS: * server_cb == server control block to use * scb == session control block * isfirst == TRUE if first call when session established * FALSE if this is from show session command * mode == help mode; ignored unless first == FALSE *********************************************************************/ extern void report_capabilities (server_cb_t *server_cb, const ses_cb_t *scb, boolean isfirst, help_mode_t mode); /******************************************************************** * FUNCTION load_base_schema * * Load the following YANG modules: * yangcli * yuma-netconf * * RETURNS: * status *********************************************************************/ extern status_t load_base_schema (void); /******************************************************************** * FUNCTION load_core_schema * * Load the following YANG modules: * yuma-xsd * yuma-types * * RETURNS: * status *********************************************************************/ extern status_t load_core_schema (void); /******************************************************************** * FUNCTION free_server_cb * * Clean and free an server control block * * INPUTS: * server_cb == control block to free * MUST BE REMOVED FROM ANY Q FIRST * *********************************************************************/ extern void free_server_cb (server_cb_t *server_cb); /******************************************************************** * FUNCTION enable_server_cb_interactive_mode * * Add/Initialize interactive context properties. * * INPUTS: * server_cb == server_cb struct * * RETURNS: * status *********************************************************************/ extern status_t enable_server_cb_interactive_mode (server_cb_t* server_cb); /******************************************************************** * FUNCTION new_server_cb * * Malloc and init a new server control block * * INPUTS: * name == name of server record * * RETURNS: * malloced server_cb struct or NULL of malloc failed *********************************************************************/ extern server_cb_t * new_server_cb (const xmlChar *name); /******************************************************************** * FUNCTION init_config_vars * * create the read-write global variables * * INPUTS: * server_cb == server control block to use * * RETURNS: * status *********************************************************************/ extern status_t init_config_vars (server_cb_t *server_cb); /******************************************************************** * FUNCTION init_system_vars * * create the read-only system variables * * INPUTS: * server_cb == server control block to use * * RETURNS: * status *********************************************************************/ extern status_t init_system_vars (server_cb_t *server_cb); extern void check_module_capabilities (server_cb_t *server_cb, ses_cb_t *scb, status_t (*get_modules_fn)(server_cb_t*,ses_cb_t*), status_t (*get_module_set_fn)(server_cb_t*,ses_cb_t*)); status_t process_module_deviations (dlq_hdr_t *savedevQ); extern status_t process_cli_input (server_cb_t *server_cb, int argc, char *argv[]); extern void update_server_cb_vars (server_cb_t *server_cb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli */ yuma123_2.14/netconf/src/yangcli/yangcli_uservars.c0000664000175000017500000003544114770023131022527 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_uservars.c NETCONF YANG-based CLI Tool uservars command ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 01-oct-11 abb begun; ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include "procdefs.h" #include "log.h" #include "mgr_load.h" #include "ncx.h" #include "status.h" #include "xml_util.h" #include "xml_wr.h" #include "yangcli.h" #include "yangcli_cmd.h" #include "yangcli_uservars.h" #include "yangcli_util.h" /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION do_uservars (local RPC) * * uservars clear * uservars load[=filespec] * uservars save[=filespec] * * Handle the uservars command * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the uservars command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_uservars (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset; status_t res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (res == NO_ERR && valset) { /* get the 1 of N 'alias-action' choice */ val_value_t *parm; const xmlChar *parmval = NULL; boolean done = FALSE; /* uservars clear */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_CLEAR); if (parm) { dlq_hdr_t *que = runstack_get_que(server_cb->runstack_context, ISGLOBAL); if (que == NULL) { res = SET_ERROR(ERR_INTERNAL_VAL); } else { if (!dlq_empty(que)) { var_clean_type_from_varQ(que, VAR_TYP_GLOBAL); log_info("\nDeleted all global user variables " "from memory\n"); }else { log_info("\nNo global user variables found\n"); } } done = TRUE; } } /* uservars load */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_LOAD); if (parm) { if (xml_strlen(VAL_STR(parm))) { parmval = VAL_STR(parm); } else { parmval = get_uservars_file(); } res = load_uservars(server_cb, parmval); if (res == NO_ERR) { log_info("\nLoaded global user variables OK from '%s'\n", parmval); } else { log_error("\nLoad global user variables from '%s' " "failed (%s)\n", parmval, get_error_string(res)); } done = TRUE; } } /* uservars save */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_SAVE); if (parm) { if (xml_strlen(VAL_STR(parm))) { parmval = VAL_STR(parm); } else { parmval = get_uservars_file(); } res = save_uservars(server_cb, parmval); if (res == NO_ERR) { log_info("\nSaved global user variables OK to '%s'\n", parmval); } else { log_error("\nSave global user variables to '%s' " "failed (%s)\n", parmval, get_error_string(res)); } done = TRUE; } } if (!done) { ;// missing mandatory choice error already reported } } if (valset) { val_free_value(valset); } return res; } /* do_uservars */ /******************************************************************** * FUNCTION load_uservars * * Load the user variables from the specified filespec * * INPUT: * server_cb == server control block to use * fspec == input filespec to use (NULL == default) * * RETURNS: * status *********************************************************************/ status_t load_uservars (server_cb_t *server_cb, const xmlChar *fspec) { xmlChar *fullspec; ncx_module_t *mod; dlq_hdr_t *que; obj_template_t *varsobj; status_t res = NO_ERR; if (fspec == NULL) { fspec = get_uservars_file(); } mod = ncx_find_module(YANGCLI_MOD, NULL); if (mod == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } varsobj = obj_find_template_top(mod, YANGCLI_MOD, YANGCLI_VARS); if (varsobj == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } que = runstack_get_que(server_cb->runstack_context, ISGLOBAL); if (que == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } fullspec = ncx_get_source(fspec, &res); if (res == NO_ERR && fullspec) { val_value_t *varsval = mgr_load_extern_file(fullspec, varsobj, &res); if (varsval && res == NO_ERR) { val_value_t *varval; for (varval = val_get_first_child(varsval); varval != NULL; varval = val_get_next_child(varval)) { val_value_t *nameval, *childval; if (xml_strcmp(varval->name, YANGCLI_VAR)) { log_error("\nError: user variable missing 'var' element, " "from file '%s'\n", fullspec); res = ERR_NCX_INVALID_VALUE; continue; } /* get var/var/name and save */ nameval = val_find_child(varval, YANGCLI_MOD, NCX_EL_NAME); if (nameval == NULL) { log_error("\nError: user variable missing 'name' element, " "from file '%s'\n", fullspec); res = ERR_NCX_MISSING_PARM; continue; } /* check /var/var/vartype = 'global' */ childval = val_find_child(varval, YANGCLI_MOD, YANGCLI_VARTYPE); if (childval && xml_strcmp(VAL_ENUM_NAME(childval), YANGCLI_GLOBAL)) { log_error("\nError: wrong user variable type '%s' " "from file '%s'\n", VAL_ENUM_NAME(childval), fullspec); res = ERR_NCX_OPERATION_NOT_SUPPORTED; continue; } /* get /vars/var/value, rename and transfer it */ childval = val_find_child(varval, YANGCLI_MOD, YANGCLI_VALUE); if (childval == NULL) { log_error("\nError: user variable '%s' missing 'value' " "element, from file '%s'\n", VAL_STR(nameval), fullspec); res = ERR_NCX_MISSING_PARM; continue; } val_remove_child(childval); /***!!! have wrong name? !!! ignoring target object for now; !!! was parsed as an anyxml ***/ val_set_name(childval, VAL_STR(nameval), xml_strlen(VAL_STR(nameval))); res = var_set_move(server_cb->runstack_context, VAL_STR(nameval), xml_strlen(VAL_STR(nameval)), VAR_TYP_GLOBAL, childval); if (res != NO_ERR) { log_error("\nError: could not create user " "variable '%s' (%s)", VAL_STR(nameval), get_error_string(res)); val_free_value(childval); } else if (LOGDEBUG2) { log_debug2("\nAdded user variable '%s' OK from file '%s'", VAL_STR(nameval), fullspec); } } } if (varsval) { val_free_value(varsval); } if (res == ERR_XML_READER_START_FAILED) { log_debug("\nUser variables file '%s' not found", fullspec); res = NO_ERR; } } if (fullspec) { m__free(fullspec); } return res; } /* load_uservars */ /******************************************************************** * FUNCTION save_uservars * * Save the uservares to the specified filespec * * INPUT: * server_cb == server control block to use * fspec == output filespec to use (NULL == default) * * RETURNS: * status *********************************************************************/ status_t save_uservars (server_cb_t *server_cb, const xmlChar *fspec) { xmlChar *fullspec; ncx_module_t *mod; dlq_hdr_t *que; obj_template_t *varsobj, *varobj, *childobj; val_value_t *varsval, *varval, *childval; status_t res = NO_ERR; if (fspec == NULL) { fspec = get_uservars_file(); } mod = ncx_find_module(YANGCLI_MOD, NULL); if (mod == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } varsobj = obj_find_template_top(mod, YANGCLI_MOD, YANGCLI_VARS); if (varsobj == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } varobj = obj_find_child(varsobj, YANGCLI_MOD, YANGCLI_VAR); if (varobj == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } que = runstack_get_que(server_cb->runstack_context, ISGLOBAL); if (que == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } /* make vars container */ varsval = val_new_value(); if (varsval == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(varsval, varsobj); fullspec = ncx_get_source(fspec, &res); if (res == NO_ERR && fullspec) { ncx_var_t *var; for (var = (ncx_var_t *)dlq_firstEntry(que); var != NULL && res == NO_ERR; var = (ncx_var_t *)dlq_nextEntry(var)) { if (var->vartype != VAR_TYP_GLOBAL || var->val == NULL) { continue; } /* make a element for this global uservar */ varval = val_new_value(); if (varval == NULL) { res = ERR_INTERNAL_MEM; continue; } val_init_from_template(varval, varobj); /* pass off memory to parent here */ val_add_child(varval, varsval); /* add var/name */ childobj = obj_find_child(varobj, YANGCLI_MOD, NCX_EL_NAME); if (childobj == NULL) { res = SET_ERROR(ERR_INTERNAL_VAL); continue; } childval = val_make_simval_obj(childobj, var->name, &res); if (childval == NULL) { continue; } /* pass off memory to parent here */ val_add_child(childval, varval); /* leave out var/vartype because it is default (global) */ #if 0 /* add var/target if needed * FIXME: the var->val->obj pointers are stale * if they were from session data node objects * and the memory has already been freed for these * objects. No generic replacement was done! */ if (var->val->obj && obj_is_data_db(var->val->obj)) { xmlChar *objbuff = NULL; childobj = obj_find_child(varobj, YANGCLI_MOD, NCX_EL_TARGET); if (childobj == NULL) { res = SET_ERROR(ERR_INTERNAL_VAL); continue; } res = obj_gen_object_id(var->val->obj, &objbuff); if (res != NO_ERR) { continue; } childval = val_make_simval_obj(childobj, objbuff, &res); m__free(objbuff); if (childval == NULL) { continue; } /* pass off memory to parent here */ val_add_child(childval, varval); } #endif /* for now just clone the value and put it in the * tree instead of manipulating the var structs * and temporarily adding the var->val node to * the node and removing it later to avoid * a double-free */ childval = val_clone2(var->val); if (childval == NULL) { res = ERR_INTERNAL_MEM; continue; } childval->nsid = varval->nsid; val_set_name(childval, NCX_EL_VALUE, xml_strlen(NCX_EL_VALUE)); val_add_child(childval, varval); } /* got a vars tree. now output it to the file */ if (res == NO_ERR) { xml_attrs_t attrs; xml_init_attrs(&attrs); res = xml_wr_file(fullspec, varsval, &attrs, XMLMODE, TRUE, /* xmlhdr */ TRUE, /* withns */ 0, /* startindent */ NCX_DEF_INDENT); xml_clean_attrs(&attrs); } } if (fullspec) { m__free(fullspec); } val_free_value(varsval); return res; } /* save_uservars */ /* END yangcli_uservars.c */ yuma123_2.14/netconf/src/yangcli/yangcli_timer.h0000664000175000017500000000551514770023131022001 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangcli_timer #define _H_yangcli_timer /* FILE: yangcli_timer.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 26-may-11 abb Begun */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION yangcli_timer_start (local RPC) * * timer-start [timernum] [restart-ok=false] * * Start the specified performance timer * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t yangcli_timer_start (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); /******************************************************************** * FUNCTION yangcli_timer_stop (local RPC) * * delta = timer-stop [timernum] [echo=false] * * Start the specified performance timer * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t yangcli_timer_stop (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli_timer */ yuma123_2.14/netconf/src/yangcli/yangcli.c0000664000175000017500000052100414770023131020570 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli.c NETCONF YANG-based CLI Tool See ./README for details ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 01-jun-08 abb begun; started from ncxcli.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #define MEMORY_DEBUG 1 */ #ifdef MEMORY_DEBUG #include #endif #include "libtecla.h" #define _C_main 1 #include "procdefs.h" #include "cli.h" #include "conf.h" #include "help.h" #include "json_wr.h" #include "log.h" #include "ncxmod.h" #include "mgr.h" #include "mgr_hello.h" #include "mgr_io.h" #include "mgr_not.h" #include "mgr_rpc.h" #include "mgr_ses.h" #include "ncx.h" #include "ncx_list.h" #include "ncx_num.h" #include "ncx_str.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "op.h" #include "rpc.h" #include "runstack.h" #include "status.h" #include "val.h" #include "val_util.h" #include "var.h" #include "xml_util.h" #include "xml_wr.h" #include "yangconst.h" #include "yangcli.h" #include "yangcli_cmd.h" #include "yangcli_alias.h" #include "yangcli_autoload.h" #include "yangcli_yang_library.h" #include "yangcli_autolock.h" #include "yangcli_save.h" #include "yangcli_tab.h" #include "yangcli_uservars.h" #include "yangcli_util.h" #include "yangcli_globals.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define YANGCLI_DEBUG 1 #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F O R W A R D D E C L A R A T I O N S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /***************** I N T E R N A L V A R S ****************/ /* TBD: use multiple server control blocks, stored in this Q */ static dlq_hdr_t server_cbQ; /* hack for now instead of lookup functions to get correct * server processing context; later search by session ID */ static server_cb_t *cur_server_cb; /* true if running a script from the invocation and exiting */ static boolean batchmode; /* true if printing program help and exiting */ static boolean helpmode; static help_mode_t helpsubmode; /* true if printing program version and exiting */ static boolean versionmode; /* name of script passed at invocation to auto-run */ static xmlChar *runscript; /* TRUE if runscript has been completed */ static boolean runscriptdone; /* command string passed at invocation to auto-run */ static xmlChar *runcommand; /* TRUE if runscript has been completed */ static boolean runcommanddone; /* controls automaic command line history buffer load/save */ static boolean autohistory; /* Q of alias_cb_t structs representing all command aliases */ static dlq_hdr_t aliasQ; /* flag to indicate init never completed OK; used during cleanup */ static boolean init_done; /***************** C O N F I G V A R S ****************/ /* TRUE if OK to load aliases automatically * FALSE if --autoaliases=false set by user * when yangcli starts, this var controls * whether the ~/.yuma/.yangcli_aliases file will be loaded * into this application automatically */ static boolean autoaliases; /* set if the --aliases-file parameter is present */ static const xmlChar *aliases_file; /* TRUE if OK to load modules automatically * FALSE if --autoload=false set by user * when server connection is made, and module discovery is done * then this var controls whether the matching modules * will be loaded into this application automatically */ static boolean autoload; /* TRUE if OK to check for partial command names and parameter * names by the user. First match (TBD: longest match!!) * will be used if no exact match found * FALSE if only exact match should be used */ static boolean autocomp; /* TRUE if OK to load user vars automatically * FALSE if --autouservars=false set by user * when yangcli starts, this var controls * whether the ~/.yuma/.yangcli_uservars file will be loaded * into this application automatically */ static boolean autouservars; /* set if the --uservars=filespec parameter is set */ static const xmlChar *uservars_file; /* NCX_BAD_DATA_IGNORE to silently accept invalid input values * NCX_BAD_DATA_WARN to warn and accept invalid input values * NCX_BAD_DATA_CHECK to prompt user to keep or re-enter value * NCX_BAD_DATA_ERROR to prompt user to re-enter value */ static ncx_bad_data_t baddata; /* name of external CLI config file used on invocation */ static xmlChar *confname; /* the module to check first when no prefix is given and there * is no parent node to check; * usually set to module 'netconf' */ static xmlChar *default_module; /* 0 for no timeout; N for N seconds message timeout */ static uint32 default_timeout; /* TRUE if OK to keep model copies stored in $USER/.yuma/tmp/<>/ until yangcli is terminated. * FALSE if --keep-session-model-copies-after-compilation=false */ static boolean keep_session_model_copies_after_compilation; /* default value for val_dump_value display mode */ static ncx_display_mode_t display_mode; /* FALSE to send PDUs in manager-specified order * TRUE to always send in correct canonical order */ static boolean fixorder; /* FALSE to skip optional nodes in do_fill * TRUE to check optional nodes in do_fill */ static boolean optional; /* default NETCONF test-option value */ static op_testop_t testoption; /* default NETCONF error-option value */ static op_errop_t erroption; /* default NETCONF default-operation value */ static op_defop_t defop; /* default NETCONF with-defaults value */ static ncx_withdefaults_t withdefaults; /* default indent amount */ static int32 defindent; /* default echo-replies */ static boolean echo_replies; /* default echo-requests */ static boolean echo_requests; /* default time-rpcs */ static boolean time_rpcs; /* default match-names */ static ncx_name_match_t match_names; /* default alt-names */ static boolean alt_names; /* default force-target */ static const xmlChar *force_target; /* default use-xmlheader */ static boolean use_xmlheader; /******************************************************************** * FUNCTION get_line_timeout * * Callback function for libtecla when the inactivity * timeout occurs. * * This function checks to see: * 1) if the session is still active * 2) if any notifications are pending * * INPUTS: * gl == line in progress * data == server control block passed as cookie * * OUTPUTS: * prints/logs notifications pending * may generate log output and/or change session state * * RETURNS: * if session state changed (session lost) * then GLTO_ABORT will be returned * * if any text written to STDOUT, then GLTO_REFRESH * will be returned * * if nothing done, then GLTO_CONTINUE will be returned *********************************************************************/ static GlAfterTimeout get_line_timeout (GetLine *gl, void *data) { server_cb_t *server_cb; ses_cb_t *scb; boolean retval, wantdata, anystdout; (void)gl; server_cb = (server_cb_t *)data; server_cb->returncode = MGR_IO_RC_NONE; if (server_cb->state != MGR_IO_ST_CONN_IDLE) { server_cb->returncode = MGR_IO_RC_IDLE; return GLTO_CONTINUE; } scb = mgr_ses_get_scb(server_cb->mysid); if (scb == NULL) { /* session was dropped */ server_cb->returncode = MGR_IO_RC_DROPPED; server_cb->state = MGR_IO_ST_IDLE; return GLTO_ABORT; } wantdata = FALSE; anystdout = FALSE; retval = mgr_io_process_timeout(scb->sid, &wantdata, &anystdout); if (retval) { /* this session is probably still alive */ if (wantdata) { server_cb->returncode = MGR_IO_RC_WANTDATA; } else { server_cb->returncode = MGR_IO_RC_PROCESSED; } return (anystdout) ? GLTO_REFRESH : GLTO_CONTINUE; } else { /* this session was dropped just now */ server_cb->returncode = MGR_IO_RC_DROPPED_NOW; server_cb->state = MGR_IO_ST_IDLE; return GLTO_ABORT; } } /* get_line_timeout */ /******************************************************************** * FUNCTION do_startup_screen * * Print the startup messages to the log and stdout output * *********************************************************************/ static void do_startup_screen (void) { logfn_t logfn; boolean imode; status_t res; xmlChar versionbuffer[NCX_VERSION_BUFFSIZE]; imode = interactive_mode(); if (imode) { logfn = log_stdout; } else { logfn = log_write; } res = ncx_get_version(versionbuffer, NCX_VERSION_BUFFSIZE); if (res == NO_ERR) { (*logfn)("\n yangcli version %s", versionbuffer); if (LOGINFO) { (*logfn)("\n "); mgr_print_libssh2_version(!imode); } } else { SET_ERROR(res); } (*logfn)("\n\n "); (*logfn)(COPYRIGHT_STRING_LINE0); (*logfn)(" "); (*logfn)(COPYRIGHT_STRING_LINE1); if (!imode) { return; } (*logfn)("\n Type 'help' or 'help ' to get started"); (*logfn)("\n Use the key for command and value completion"); (*logfn)("\n Use the key to accept the default value "); (*logfn)("in brackets"); (*logfn)("\n\n These escape sequences are available "); (*logfn)("when filling parameter values:"); (*logfn)("\n\n\t?\thelp"); (*logfn)("\n\t??\tfull help"); (*logfn)("\n\t?s\tskip current parameter"); (*logfn)("\n\t?c\tcancel current command"); (*logfn)("\n\n These assignment statements are available "); (*logfn)("when entering commands:"); (*logfn)("\n\n\t$ = \tLocal user variable assignment"); (*logfn)("\n\t$$ = \tGlobal user variable assignment"); (*logfn)("\n\t@ = \tFile assignment\n"); } /* do_startup_screen */ /******************************************************************** * FUNCTION free_server_cb * * Clean and free an server control block * * INPUTS: * server_cb == control block to free * MUST BE REMOVED FROM ANY Q FIRST * *********************************************************************/ void free_server_cb (server_cb_t *server_cb) { modptr_t *modptr; mgr_not_msg_t *notif; int retval; /* save the history buffer if needed */ if (server_cb->cli_gl != NULL && server_cb->history_auto) { retval = gl_save_history(server_cb->cli_gl, (const char *)server_cb->history_filename, "#", /* comment prefix */ -1); /* save all entries */ if (retval) { log_error("\nError: could not save command line " "history file '%s'", server_cb->history_filename); } } if (server_cb->name) { m__free(server_cb->name); } if (server_cb->address) { m__free(server_cb->address); } if (server_cb->password) { m__free(server_cb->password); } if (server_cb->local_result) { val_free_value(server_cb->local_result); } if (server_cb->result_name) { m__free(server_cb->result_name); } if (server_cb->result_filename) { m__free(server_cb->result_filename); } if (server_cb->history_filename) { m__free(server_cb->history_filename); } if (server_cb->history_line) { m__free(server_cb->history_line); } if (server_cb->connect_valset) { val_free_value(server_cb->connect_valset); } /* cleanup the user edit buffer */ if (server_cb->cli_gl) { (void)del_GetLine(server_cb->cli_gl); } var_clean_varQ(&server_cb->varbindQ); ncxmod_clean_search_result_queue(&server_cb->searchresultQ); while (!dlq_empty(&server_cb->modptrQ)) { modptr = (modptr_t *)dlq_deque(&server_cb->modptrQ); free_modptr(modptr); } while (!dlq_empty(&server_cb->notificationQ)) { notif = (mgr_not_msg_t *)dlq_deque(&server_cb->notificationQ); mgr_not_free_msg(notif); } if (server_cb->runstack_context) { runstack_free_context(server_cb->runstack_context); } ncx_clean_save_deviationsQ(&server_cb->autoload_savedevQ); m__free(server_cb); } /* free_server_cb */ /******************************************************************** * FUNCTION enable_server_cb_interactive_mode * * Add/Initialize interactive context properties. * * INPUTS: * server_cb == server_cb struct * * RETURNS: * status *********************************************************************/ status_t enable_server_cb_interactive_mode (server_cb_t* server_cb) { int retval; /* get a tecla CLI control block */ server_cb->cli_gl = new_GetLine(YANGCLI_LINELEN, YANGCLI_HISTLEN); if (server_cb->cli_gl == NULL) { log_error("\nError: cannot allocate a new GL"); return SET_ERROR(ERR_INTERNAL_VAL); } /* setup CLI tab line completion */ retval = gl_customize_completion(server_cb->cli_gl, &server_cb->completion_state, yangcli_tab_callback); if (retval != 0) { log_error("\nError: cannot set GL tab completion"); return SET_ERROR(ERR_INTERNAL_VAL); } /* setup the inactivity timeout callback function */ retval = gl_inactivity_timeout(server_cb->cli_gl, get_line_timeout, server_cb, 1, 0); if (retval != 0) { log_error("\nError: cannot set GL inactivity timeout"); return SET_ERROR(ERR_INTERNAL_VAL); } /* setup the history buffer if needed */ if (server_cb->history_auto) { retval = gl_load_history(server_cb->cli_gl, (const char *)server_cb->history_filename, "#"); /* comment prefix */ if (retval) { log_error("\nError: cannot load command line history buffer"); return SET_ERROR(ERR_INTERNAL_VAL); } } return NO_ERR; } /******************************************************************** * FUNCTION new_server_cb * * Malloc and init a new server control block * * INPUTS: * name == name of server record * * RETURNS: * malloced server_cb struct or NULL of malloc failed *********************************************************************/ server_cb_t * new_server_cb (const xmlChar *name) { server_cb_t *server_cb; status_t res; server_cb = m__getObj(server_cb_t); if (server_cb == NULL) { return NULL; } memset(server_cb, 0x0, sizeof(server_cb_t)); server_cb->runstack_context = runstack_new_context(); if (server_cb->runstack_context == NULL) { m__free(server_cb); return NULL; } dlq_createSQue(&server_cb->varbindQ); dlq_createSQue(&server_cb->searchresultQ); dlq_createSQue(&server_cb->modptrQ); dlq_createSQue(&server_cb->notificationQ); dlq_createSQue(&server_cb->autoload_savedevQ); /* set the default CLI history file (may not get used) */ server_cb->history_filename = xml_strdup(YANGCLI_DEF_HISTORY_FILE); if (server_cb->history_filename == NULL) { free_server_cb(server_cb); return NULL; } server_cb->history_auto = autohistory; /* create the program instance temporary directory */ if(yangcli_progcb==NULL) { server_cb->temp_progcb = ncxmod_new_program_tempdir(&res); if(res==NO_ERR && server_cb->temp_progcb!=NULL) { yangcli_progcb=server_cb->temp_progcb; } else { return NULL; } } else { server_cb->temp_progcb=yangcli_progcb; } /* the name is not used yet; needed when multiple * server profiles are needed at once instead * of 1 session at a time */ server_cb->name = xml_strdup(name); if (server_cb->name == NULL) { free_server_cb(server_cb); return NULL; } /* set up lock control blocks for get-locks */ server_cb->locks_active = FALSE; server_cb->locks_waiting = FALSE; server_cb->locks_cur_cfg = NCX_CFGID_RUNNING; server_cb->locks_timeout = 120; server_cb->locks_retry_interval = 1; server_cb->locks_cleanup = FALSE; server_cb->locks_start_time = (time_t)0; server_cb->lock_cb[NCX_CFGID_RUNNING].config_id = NCX_CFGID_RUNNING; server_cb->lock_cb[NCX_CFGID_RUNNING].config_name = NCX_CFG_RUNNING; server_cb->lock_cb[NCX_CFGID_RUNNING].lock_state = LOCK_STATE_IDLE; server_cb->lock_cb[NCX_CFGID_CANDIDATE].config_id = NCX_CFGID_CANDIDATE; server_cb->lock_cb[NCX_CFGID_CANDIDATE].config_name = NCX_CFG_CANDIDATE; server_cb->lock_cb[NCX_CFGID_CANDIDATE].lock_state = LOCK_STATE_IDLE; server_cb->lock_cb[NCX_CFGID_STARTUP].config_id = NCX_CFGID_STARTUP; server_cb->lock_cb[NCX_CFGID_STARTUP].config_name = NCX_CFG_STARTUP; server_cb->lock_cb[NCX_CFGID_STARTUP].lock_state = LOCK_STATE_IDLE; /* set default server flags to current settings */ server_cb->state = MGR_IO_ST_INIT; server_cb->baddata = baddata; server_cb->log_level = log_get_debug_level(); server_cb->autoload = autoload; server_cb->keep_session_model_copies_after_compilation = keep_session_model_copies_after_compilation; server_cb->fixorder = fixorder; server_cb->get_optional = optional; server_cb->testoption = testoption; server_cb->erroption = erroption; server_cb->defop = defop; server_cb->timeout = default_timeout; server_cb->display_mode = display_mode; server_cb->withdefaults = withdefaults; server_cb->history_size = YANGCLI_HISTLEN; server_cb->command_mode = CMD_MODE_NORMAL; server_cb->defindent = defindent; server_cb->echo_replies = echo_replies; server_cb->echo_requests = echo_requests; server_cb->time_rpcs = time_rpcs; server_cb->match_names = match_names; server_cb->alt_names = alt_names; /* TBD: add user config for this knob */ server_cb->overwrite_filevars = TRUE; server_cb->use_xmlheader = use_xmlheader; return server_cb; } /* new_server_cb */ /******************************************************************** * FUNCTION update_server_cb_vars * * Update the * * INPUTS: * server_cb == server_cb to update * * OUTPUTS: * server_cb->foo is updated if it is a shadow of a global var * *********************************************************************/ void update_server_cb_vars (server_cb_t *server_cb) { server_cb->baddata = baddata; server_cb->log_level = log_get_debug_level(); server_cb->autoload = autoload; server_cb->keep_session_model_copies_after_compilation = keep_session_model_copies_after_compilation; server_cb->fixorder = fixorder; server_cb->get_optional = optional; server_cb->testoption = testoption; server_cb->erroption = erroption; server_cb->defop = defop; server_cb->timeout = default_timeout; server_cb->display_mode = display_mode; server_cb->withdefaults = withdefaults; server_cb->defindent = defindent; server_cb->echo_replies = echo_replies; server_cb->echo_requests = echo_requests; server_cb->time_rpcs = time_rpcs; server_cb->match_names = match_names; server_cb->alt_names = alt_names; server_cb->use_xmlheader = use_xmlheader; } /* update_server_cb_vars */ /******************************************************************** * FUNCTION handle_config_assign * * handle a user assignment of a config variable * consume memory 'newval' if it is non-NULL * * INPUTS: * server_cb == server control block to use * configval == value to set * use 1 of: * newval == value to use for changing 'configval' * newvalstr == value to use as string form * * RETURNS: * status *********************************************************************/ static status_t handle_config_assign (server_cb_t *server_cb, val_value_t *configval, val_value_t *newval, const xmlChar *newvalstr) { const xmlChar *usestr; xmlChar *dupval; val_value_t *testval; obj_template_t *testobj; status_t res; log_debug_t testloglevel; ncx_bad_data_t testbaddata; op_testop_t testop; op_errop_t errop; op_defop_t mydefop; ncx_num_t testnum; ncx_display_mode_t dmode; res = NO_ERR; if (newval) { if (!typ_is_string(newval->btyp)) { val_free_value( newval ); return ERR_NCX_WRONG_TYPE; } if (VAL_STR(newval) == NULL) { val_free_value( newval ); return ERR_NCX_INVALID_VALUE; } usestr = VAL_STR(newval); } else if (newvalstr) { usestr = newvalstr; } else { log_error("\nError: NULL value in config assignment"); return ERR_NCX_INVALID_VALUE; } if (!xml_strcmp(configval->name, YANGCLI_SERVER)) { /* should check for valid IP address!!! */ if (val_need_quotes(usestr)) { /* using this dumb test as a placeholder */ log_error("\nError: invalid hostname"); } else { /* save or update the connnect_valset */ testval = val_find_child(connect_valset, NULL, YANGCLI_SERVER); if (testval) { res = val_set_simval(testval, testval->typdef, testval->nsid, testval->name, usestr); if (res != NO_ERR) { log_error("\nError: changing 'server' failed"); } } else { testobj = obj_find_child(connect_valset->obj, NULL, YANGCLI_SERVER); if (testobj) { testval = val_make_simval_obj(testobj, usestr, &res); if (testval) { val_add_child(testval, connect_valset); } } } } } else if (!xml_strcmp(configval->name, YANGCLI_AUTOALIASES)) { if (ncx_is_true(usestr)) { autoaliases = TRUE; } else if (ncx_is_false(usestr)) { autoaliases = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_ALIASES_FILE)) { aliases_file = usestr; } else if (!xml_strcmp(configval->name, YANGCLI_AUTOCOMP)) { if (ncx_is_true(usestr)) { autocomp = TRUE; } else if (ncx_is_false(usestr)) { autocomp = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_AUTOHISTORY)) { if (ncx_is_true(usestr)) { autohistory = TRUE; } else if (ncx_is_false(usestr)) { autohistory = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_AUTOLOAD)) { if (ncx_is_true(usestr)) { server_cb->autoload = TRUE; autoload = TRUE; } else if (ncx_is_false(usestr)) { server_cb->autoload = FALSE; autoload = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_KEEP_SESSION_MODEL_COPIES_AFTER_COMPILATION)) { if (ncx_is_true(usestr)) { server_cb->keep_session_model_copies_after_compilation = TRUE; keep_session_model_copies_after_compilation = TRUE; } else if (ncx_is_false(usestr)) { server_cb->keep_session_model_copies_after_compilation = FALSE; keep_session_model_copies_after_compilation = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_AUTOUSERVARS)) { if (ncx_is_true(usestr)) { autouservars = TRUE; } else if (ncx_is_false(usestr)) { autouservars = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_USERVARS_FILE)) { uservars_file = usestr; } else if (!xml_strcmp(configval->name, YANGCLI_BADDATA)) { testbaddata = ncx_get_baddata_enum(usestr); if (testbaddata != NCX_BAD_DATA_NONE) { server_cb->baddata = testbaddata; baddata = testbaddata; } else { log_error("\nError: value must be 'ignore', 'warn', " "'check', or 'error'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_DEF_MODULE)) { if (!ncx_valid_name2(usestr)) { log_error("\nError: must be a valid module name"); res = ERR_NCX_INVALID_VALUE; } else { /* save a copy of the string value */ dupval = xml_strdup(usestr); if (dupval == NULL) { log_error("\nError: malloc failed"); res = ERR_INTERNAL_MEM; } else { if (default_module) { m__free(default_module); } default_module = dupval; } } } else if (!xml_strcmp(configval->name, YANGCLI_DISPLAY_MODE)) { dmode = ncx_get_display_mode_enum(usestr); if (dmode != NCX_DISPLAY_MODE_NONE) { display_mode = dmode; server_cb->display_mode = dmode; } else { log_error("\nError: value must be 'plain', 'prefix', " "'json, 'xml', or 'xml-nons'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_ECHO_REPLIES)) { if (ncx_is_true(usestr)) { server_cb->echo_replies = TRUE; echo_replies = TRUE; } else if (ncx_is_false(usestr)) { server_cb->echo_replies = FALSE; echo_replies = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_ECHO_REQUESTS)) { if (ncx_is_true(usestr)) { server_cb->echo_requests = TRUE; echo_requests = TRUE; } else if (ncx_is_false(usestr)) { server_cb->echo_requests = FALSE; echo_requests = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_TIME_RPCS)) { if (ncx_is_true(usestr)) { server_cb->time_rpcs = TRUE; time_rpcs = TRUE; } else if (ncx_is_false(usestr)) { server_cb->time_rpcs = FALSE; time_rpcs = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_MATCH_NAMES)) { ncx_name_match_t match = ncx_get_name_match_enum(usestr); if (match == NCX_MATCH_NONE) { log_error("\nError: value must be 'exact', " "'exact-nocase', 'one', 'one-nocase', " "'first', or 'first-nocase'"); res = ERR_NCX_INVALID_VALUE; } else { server_cb->match_names = match; match_names = match; } } else if (!xml_strcmp(configval->name, YANGCLI_ALT_NAMES)) { if (ncx_is_true(usestr)) { server_cb->alt_names = TRUE; alt_names = TRUE; } else if (ncx_is_false(usestr)) { server_cb->alt_names = FALSE; alt_names = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_USE_XMLHEADER)) { if (ncx_is_true(usestr)) { server_cb->use_xmlheader = TRUE; use_xmlheader = TRUE; } else if (ncx_is_false(usestr)) { server_cb->use_xmlheader = FALSE; use_xmlheader = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_USER)) { if (!ncx_valid_name2(usestr)) { log_error("\nError: must be a valid user name"); res = ERR_NCX_INVALID_VALUE; } else { /* save or update the connnect_valset */ testval = val_find_child(connect_valset, NULL, YANGCLI_USER); if (testval) { res = val_set_simval(testval, testval->typdef, testval->nsid, testval->name, usestr); if (res != NO_ERR) { log_error("\nError: changing user name failed"); } } else { testobj = obj_find_child(connect_valset->obj, NULL, YANGCLI_USER); if (testobj) { testval = val_make_simval_obj(testobj, usestr, &res); } if (testval) { val_add_child(testval, connect_valset); } } } } else if (!xml_strcmp(configval->name, YANGCLI_TEST_OPTION)) { if (!xml_strcmp(usestr, NCX_EL_NONE)) { server_cb->testoption = OP_TESTOP_NONE; testop = OP_TESTOP_NONE; } else { testop = op_testop_enum(usestr); if (testop != OP_TESTOP_NONE) { server_cb->testoption = testop; testoption = testop; } else { log_error("\nError: must be a valid 'test-option'"); log_error("\n (none, test-then-set, set, " "test-only)\n"); res = ERR_NCX_INVALID_VALUE; } } } else if (!xml_strcmp(configval->name, YANGCLI_ERROR_OPTION)) { if (!xml_strcmp(usestr, NCX_EL_NONE)) { server_cb->erroption = OP_ERROP_NONE; erroption = OP_ERROP_NONE; } else { errop = op_errop_id(usestr); if (errop != OP_ERROP_NONE) { server_cb->erroption = errop; erroption = errop; } else { log_error("\nError: must be a valid 'error-option'"); log_error("\n (none, stop-on-error, " "continue-on-error, rollback-on-error)\n"); res = ERR_NCX_INVALID_VALUE; } } } else if (!xml_strcmp(configval->name, NCX_EL_DEFAULT_OPERATION)) { mydefop = op_defop_id2(usestr); if (mydefop != OP_DEFOP_NOT_SET) { server_cb->defop = mydefop; defop = mydefop; } else { log_error("\nError: must be a valid 'default-operation'"); log_error("\n (none, merge, " "replace, not-used)\n"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, YANGCLI_TIMEOUT)) { ncx_init_num(&testnum); res = ncx_decode_num(usestr, NCX_BT_UINT32, &testnum); if (res == NO_ERR) { server_cb->timeout = testnum.u; default_timeout = testnum.u; } else { log_error("\nError: must be valid uint32 value"); } ncx_clean_num(NCX_BT_UINT32, &testnum); } else if (!xml_strcmp(configval->name, YANGCLI_OPTIONAL)) { if (ncx_is_true(usestr)) { optional = TRUE; server_cb->get_optional = TRUE; } else if (ncx_is_false(usestr)) { optional = FALSE; server_cb->get_optional = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, NCX_EL_LOGLEVEL)) { testloglevel = log_get_debug_level_enum((const char *)usestr); if (testloglevel == LOG_DEBUG_NONE) { log_error("\nError: value must be valid log-level:"); log_error("\n (off, error," "warn, info, debug, debug2)\n"); res = ERR_NCX_INVALID_VALUE; } else { log_set_debug_level(testloglevel); server_cb->log_level = testloglevel; } } else if (!xml_strcmp(configval->name, YANGCLI_FIXORDER)) { if (ncx_is_true(usestr)) { fixorder = TRUE; server_cb->fixorder = TRUE; } else if (ncx_is_false(usestr)) { fixorder = FALSE; server_cb->fixorder = FALSE; } else { log_error("\nError: value must be 'true' or 'false'"); res = ERR_NCX_INVALID_VALUE; } } else if (!xml_strcmp(configval->name, NCX_EL_WITH_DEFAULTS)) { if (!xml_strcmp(usestr, NCX_EL_NONE)) { withdefaults = NCX_WITHDEF_NONE; } else { if (ncx_get_withdefaults_enum(usestr) == NCX_WITHDEF_NONE) { log_error("\nError: value must be 'none', " "'report-all', 'trim', or 'explicit'"); res = ERR_NCX_INVALID_VALUE; } else { withdefaults = ncx_get_withdefaults_enum(usestr); server_cb->withdefaults = withdefaults; } } } else if (!xml_strcmp(configval->name, NCX_EL_INDENT)) { ncx_init_num(&testnum); res = ncx_decode_num(usestr, NCX_BT_UINT32, &testnum); if (res == NO_ERR) { if (testnum.u > YANGCLI_MAX_INDENT) { log_error("\nError: value must be a uint32 (0..9)"); res = ERR_NCX_INVALID_VALUE; } else { /* indent value is valid */ defindent = (int32)testnum.u; server_cb->defindent = defindent; } } else { log_error("\nError: must be valid uint32 value"); } ncx_clean_num(NCX_BT_INT32, &testnum); } else { /* unknown parameter name */ res = SET_ERROR(ERR_INTERNAL_VAL); } /* update the variable value for user access */ if (res == NO_ERR) { if (newval) { res = var_set_move(server_cb->runstack_context, configval->name, xml_strlen(configval->name), VAR_TYP_CONFIG, newval); } else { res = var_set_from_string(server_cb->runstack_context, configval->name, newvalstr, VAR_TYP_CONFIG); } if (res != NO_ERR) { log_error("\nError: set result for '%s' failed (%s)", server_cb->result_name, get_error_string(res)); } } if (res == NO_ERR) { log_info("\nSystem variable set\n"); } return res; } /* handle_config_assign */ /******************************************************************** * FUNCTION handle_delete_result * * Delete the specified file, if it is ASCII and a regular file * * INPUTS: * server_cb == server control block to use * * OUTPUTS: * server_cb->result_filename will get deleted if NO_ERR * * RETURNS: * status *********************************************************************/ static status_t handle_delete_result (server_cb_t *server_cb) { status_t res; struct stat statbuf; int statresult; res = NO_ERR; if (LOGDEBUG2) { log_debug2("\n*** delete file result '%s'", server_cb->result_filename); } /* see if file already exists */ statresult = stat((const char *)server_cb->result_filename, &statbuf); if (statresult != 0) { log_error("\nError: assignment file '%s' could not be opened", server_cb->result_filename); res = errno_to_status(); } else if (!S_ISREG(statbuf.st_mode)) { log_error("\nError: assignment file '%s' is not a regular file", server_cb->result_filename); res = ERR_NCX_OPERATION_FAILED; } else { statresult = remove((const char *)server_cb->result_filename); if (statresult == -1) { log_error("\nError: assignment file '%s' could not be deleted", server_cb->result_filename); res = errno_to_status(); } } clear_result(server_cb); return res; } /* handle_delete_result */ /******************************************************************** * FUNCTION output_file_result * * Check the filespec string for a file assignment statement * Save it if it si good * * INPUTS: * server_cb == server control block to use * use 1 of these 2 parms: * resultval == result to output to file * resultstr == result to output as string * * OUTPUTS: * server_cb->result_filename will get set if NO_ERR * * RETURNS: * status *********************************************************************/ static status_t output_file_result (server_cb_t *server_cb, val_value_t *resultval, const xmlChar *resultstr) { FILE *fil; status_t res; xml_attrs_t attrs; struct stat statbuf; int statresult; res = NO_ERR; if (LOGDEBUG2) { log_debug2("\n*** output file result to '%s'", server_cb->result_filename); } /* see if file already exists, unless overwrite_filevars is set */ if (!server_cb->overwrite_filevars) { statresult = stat((const char *)server_cb->result_filename, &statbuf); if (statresult == 0) { log_error("\nError: assignment file '%s' already exists", server_cb->result_filename); clear_result(server_cb); return ERR_NCX_DATA_EXISTS; } } if (resultval) { result_format_t rf = get_file_result_format(server_cb->result_filename); switch (rf) { case RF_TEXT: /* output in text format to the specified file */ res = log_alt_open((const char *) server_cb->result_filename); if (res != NO_ERR) { log_error("\nError: assignment file '%s' could " "not be opened (%s)", server_cb->result_filename, get_error_string(res)); } else { val_dump_value_max(resultval, 0, /* startindent */ server_cb->defindent, DUMP_VAL_ALT_LOG, /* dumpmode */ server_cb->display_mode, FALSE, /* withmeta */ FALSE); /* configonly */ log_alt_close(); } break; case RF_XML: /* output in XML format to the specified file */ xml_init_attrs(&attrs); res = xml_wr_file(server_cb->result_filename, resultval, &attrs, XMLMODE, server_cb->use_xmlheader, (server_cb->display_mode == NCX_DISPLAY_MODE_XML_NONS) ? FALSE : TRUE, 0, server_cb->defindent); xml_clean_attrs(&attrs); break; case RF_JSON: /* output in JSON format to the specified file */ res = json_wr_file(server_cb->result_filename, resultval, 0, server_cb->defindent); break; case RF_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); } } else if (resultstr) { fil = fopen((const char *)server_cb->result_filename, "w"); if (fil == NULL) { log_error("\nError: assignment file '%s' could " "not be opened", server_cb->result_filename); res = errno_to_status(); } else { statresult = fputs((const char *)resultstr, fil); if (statresult == EOF) { log_error("\nError: assignment file '%s' could " "not be written", server_cb->result_filename); res = errno_to_status(); } else { statresult = fputc('\n', fil); if (statresult == EOF) { log_error("\nError: assignment file '%s' could " "not be written", server_cb->result_filename); res = errno_to_status(); } } } if (fil != NULL) { statresult = fclose(fil); if (statresult == EOF) { log_error("\nError: assignment file '%s' could " "not be closed", server_cb->result_filename); res = errno_to_status(); } } } else { SET_ERROR(ERR_INTERNAL_VAL); } clear_result(server_cb); return res; } /* output_file_result */ /******************************************************************** * FUNCTION check_assign_statement * * Check if the command line is an assignment statement * * E.g., * * $foo = $bar * $foo = get-config filter=@filter.xml * $foo = "quoted string literal" * $foo = [] * $foo = @datafile.xml * * INPUTS: * server_cb == server control block to use * line == command line string to expand * len == address of number chars parsed so far in line * getrpc == address of return flag to parse and execute an * RPC, which will be assigned to the var found * in the 'result' module variable * fileassign == address of file assign flag * * OUTPUTS: * *len == number chars parsed in the assignment statement * *getrpc == TRUE if the rest of the line represent an * RPC command that needs to be evaluated * *fileassign == TRUE if the assignment is for a * file output (starts with @) * FALSE if not a file assignment * * SIDE EFFECTS: * If this is an 'rpc' assignment statement, * (*dorpc == TRUE && *len > 0 && return NO_ERR), * then the global result and result_pending variables will * be set upon return. For the other (direct) assignment * statement variants, the statement is completely handled * here. * * RETURNS: * status *********************************************************************/ static status_t check_assign_statement (server_cb_t *server_cb, const xmlChar *line, uint32 *len, boolean *getrpc, boolean *fileassign) { const xmlChar *str, *name, *filespec; val_value_t *curval; obj_template_t *obj; val_value_t *val; xmlChar *tempstr; uint32 nlen, tlen; var_type_t vartype; status_t res; /* save start point in line */ str = line; *len = 0; *getrpc = FALSE; *fileassign = FALSE; vartype = VAR_TYP_NONE; curval = NULL; /* skip leading whitespace */ while (*str && isspace(*str)) { str++; } if (*str == '@') { /* check if valid file assignment is being made */ *fileassign = TRUE; str++; } if (*str == '$') { /* check if a valid variable assignment is being made */ res = var_check_ref(server_cb->runstack_context, str, ISLEFT, &tlen, &vartype, &name, &nlen); if (res != NO_ERR) { /* error in the varref */ return res; } else if (tlen == 0) { /* should not happen: returned not a varref */ *getrpc = TRUE; return NO_ERR; } else if (*fileassign) { /* file assignment complex form: * * @$foo = bar or @$$foo = bar * * get the var reference for real because * it is supposed to contain the filespec * for the output file */ curval = var_get_str(server_cb->runstack_context, name, nlen, vartype); if (curval == NULL) { log_error("\nError: file assignment variable " "not found"); return ERR_NCX_VAR_NOT_FOUND; } /* variable must be a string */ if (!typ_is_string(curval->btyp)) { log_error("\nError: file assignment variable '%s' " "is wrong type '%s'", curval->name, tk_get_btype_sym(curval->btyp)); return ERR_NCX_VAR_NOT_FOUND; } filespec = VAL_STR(curval); res = check_filespec(server_cb, filespec, curval->name); if (res != NO_ERR) { return res; } } else { /* variable reference: * * $foo or $$foo * * check for a valid varref, get the data type, which * will also indicate if the variable exists yet */ switch (vartype) { case VAR_TYP_SYSTEM: log_error("\nError: system variables " "are read-only"); return ERR_NCX_VAR_READ_ONLY; case VAR_TYP_GLOBAL: case VAR_TYP_CONFIG: curval = var_get_str(server_cb->runstack_context, name, nlen, vartype); break; case VAR_TYP_LOCAL: case VAR_TYP_SESSION: curval = var_get_local_str(server_cb->runstack_context, name, nlen); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } } /* move the str pointer past the variable name */ str += tlen; } else if (*fileassign) { /* file assignment, simple form: * * @foo.txt = bar * * get the length of the filename */ name = str; while (*str && !isspace(*str) && *str != '=') { str++; } nlen = (uint32)(str-name); /* save the filename in a temp string */ tempstr = xml_strndup(name, nlen); if (tempstr == NULL) { return ERR_INTERNAL_MEM; } /* check filespec and save filename for real */ res = check_filespec(server_cb, tempstr, NULL); m__free(tempstr); if (res != NO_ERR) { return res; } } else { /* not an assignment statement at all */ *getrpc = TRUE; return NO_ERR; } /* skip any more whitespace, after the LHS term */ while (*str && xml_isspace(*str)) { str++; } /* check end of string */ if (!*str) { log_error("\nError: truncated assignment statement"); clear_result(server_cb); return ERR_NCX_DATA_MISSING; } /* check for the equals sign assignment char */ if (*str == NCX_ASSIGN_CH) { /* move past assignment char */ str++; } else { log_error("\nError: equals sign '=' expected"); clear_result(server_cb); return ERR_NCX_WRONG_TKTYPE; } /* skip any more whitespace */ while (*str && xml_isspace(*str)) { str++; } /* check EO string == unset command, but only if in a TRUE conditional */ if (!*str && runstack_get_cond_state(server_cb->runstack_context)) { if (*fileassign) { /* got file assignment (@foo) = EOLN * treat this as a request to delete the file */ res = handle_delete_result(server_cb); } else { /* got $foo = EOLN * treat this as a request to unset the variable */ if (vartype == VAR_TYP_SYSTEM || vartype == VAR_TYP_CONFIG) { log_error("\nError: cannot remove system variables"); clear_result(server_cb); return ERR_NCX_OPERATION_FAILED; } /* else try to unset this variable */ res = var_unset(server_cb->runstack_context, name, nlen, vartype); } *len = str - line; return res; } /* the variable name and equals sign is parsed * and the current char is either '$', '"', '<', * or a valid first name * * if *fileassign: * * @foo.xml = blah * ^ * else: * $foo = blah * ^ */ if (*fileassign) { obj = NULL; } else { obj = (curval) ? curval->obj : NULL; } /* get the script or CLI input as a new val_value_t struct */ val = var_check_script_val(server_cb->runstack_context, obj, str, ISTOP, &res); if (val) { if (obj == NULL) { obj = val->obj; } /* a script value reference was found */ if (obj == NULL || obj == ncx_get_gen_string()) { /* the generic name needs to be overwritten */ val_set_name(val, name, nlen); } if (*fileassign) { /* file assignment of a variable value * @foo.txt=$bar or @$foo=$bar */ res = output_file_result(server_cb, val, NULL); val_free_value(val); } else { /* this is a plain assignment statement * first check if the input is VAR_TYP_CONFIG */ if (vartype == VAR_TYP_CONFIG) { if (curval==NULL) { val_free_value(val); res = SET_ERROR(ERR_INTERNAL_VAL); } else { /* hand off 'val' memory here */ res = handle_config_assign(server_cb, curval, val, NULL); } } else { /* val is a malloced struct, pass it over to the * var struct instead of cloning it */ res = var_set_move(server_cb->runstack_context, name, nlen, vartype, val); if (res != NO_ERR) { val_free_value(val); } } } } else if (res==NO_ERR) { /* this is as assignment to the results * of an RPC function call */ if (server_cb->result_name) { log_error("\nError: result already pending for %s", server_cb->result_name); m__free(server_cb->result_name); server_cb->result_name = NULL; } if (!*fileassign) { /* save the variable result name */ server_cb->result_name = xml_strndup(name, nlen); if (server_cb->result_name == NULL) { *len = 0; res = ERR_INTERNAL_MEM; } else { server_cb->result_vartype = vartype; } } if (res == NO_ERR) { *len = str - line; *getrpc = TRUE; } } else { /* there was some error in the statement processing */ *len = 0; clear_result(server_cb); } return res; } /* check_assign_statement */ /******************************************************************** * FUNCTION create_system_var * * create a read-only system variable * * INPUTS: * server_cb == server control block to use * varname == variable name * varval == variable string value (may be NULL) * * RETURNS: * status *********************************************************************/ static status_t create_system_var (server_cb_t *server_cb, const char *varname, const char *varval) { status_t res; res = var_set_from_string(server_cb->runstack_context, (const xmlChar *)varname, (const xmlChar *)varval, VAR_TYP_SYSTEM); return res; } /* create_system_var */ /******************************************************************** * FUNCTION create_config_var * * create a read-write system variable * * INPUTS: * server_cb == server control block * varname == variable name * varval == variable string value (may be NULL) * * RETURNS: * status *********************************************************************/ static status_t create_config_var (server_cb_t *server_cb, const xmlChar *varname, const xmlChar *varval) { status_t res; res = var_set_from_string(server_cb->runstack_context, varname, varval, VAR_TYP_CONFIG); return res; } /* create_config_var */ /******************************************************************** * FUNCTION init_system_vars * * create the read-only system variables * * INPUTS: * server_cb == server control block to use * * RETURNS: * status *********************************************************************/ status_t init_system_vars (server_cb_t *server_cb) { const char *envstr; status_t res; envstr = getenv(NCXMOD_PWD); res = create_system_var(server_cb, NCXMOD_PWD, envstr); if (res != NO_ERR) { return res; } envstr = (const char *)ncxmod_get_home(); res = create_system_var(server_cb, USER_HOME, envstr); if (res != NO_ERR) { return res; } envstr = getenv(ENV_HOST); res = create_system_var(server_cb, ENV_HOST, envstr); if (res != NO_ERR) { return res; } envstr = getenv(ENV_SHELL); res = create_system_var(server_cb, ENV_SHELL, envstr); if (res != NO_ERR) { return res; } envstr = getenv(ENV_USER); res = create_system_var(server_cb, ENV_USER, envstr); if (res != NO_ERR) { return res; } envstr = getenv(ENV_LANG); res = create_system_var(server_cb, ENV_LANG, envstr); if (res != NO_ERR) { return res; } envstr = getenv(NCXMOD_HOME); res = create_system_var(server_cb, NCXMOD_HOME, envstr); if (res != NO_ERR) { return res; } envstr = getenv(NCXMOD_MODPATH); res = create_system_var(server_cb, NCXMOD_MODPATH, envstr); if (res != NO_ERR) { return res; } envstr = getenv(NCXMOD_DATAPATH); res = create_system_var(server_cb, NCXMOD_DATAPATH, envstr); if (res != NO_ERR) { return res; } envstr = getenv(NCXMOD_RUNPATH); res = create_system_var(server_cb, NCXMOD_RUNPATH, envstr); if (res != NO_ERR) { return res; } return NO_ERR; } /* init_system_vars */ /******************************************************************** * FUNCTION init_config_vars * * create the read-write global variables * * INPUTS: * server_cb == server control block to use * * RETURNS: * status *********************************************************************/ status_t init_config_vars (server_cb_t *server_cb) { val_value_t *parm; const xmlChar *strval; status_t res; xmlChar numbuff[NCX_MAX_NUMLEN]; /* $$server = ip-address */ strval = NULL; parm = val_find_child(mgr_cli_valset, NULL, YANGCLI_SERVER); if (parm) { strval = VAL_STR(parm); } res = create_config_var(server_cb, YANGCLI_SERVER, strval); if (res != NO_ERR) { return res; } /* $$ autoaliases = boolean */ res = create_config_var(server_cb, YANGCLI_AUTOALIASES, (autoaliases) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$ aliases-file = filespec */ res = create_config_var(server_cb, YANGCLI_ALIASES_FILE, aliases_file); if (res != NO_ERR) { return res; } /* $$autocomp = boolean */ res = create_config_var(server_cb, YANGCLI_AUTOCOMP, (autocomp) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$ autohistory = boolean */ res = create_config_var(server_cb, YANGCLI_AUTOHISTORY, (autohistory) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$ autoload = boolean */ res = create_config_var(server_cb, YANGCLI_AUTOLOAD, (autoload) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$ keep_session_model_copies_after_compilation = boolean */ res = create_config_var(server_cb, YANGCLI_KEEP_SESSION_MODEL_COPIES_AFTER_COMPILATION, (keep_session_model_copies_after_compilation) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$ autouservars = boolean */ res = create_config_var(server_cb, YANGCLI_AUTOUSERVARS, (autouservars) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$ uservars-file = filespec */ res = create_config_var(server_cb, YANGCLI_USERVARS_FILE, uservars_file); if (res != NO_ERR) { return res; } /* $$baddata = enum */ res = create_config_var(server_cb, YANGCLI_BADDATA, ncx_get_baddata_string(baddata)); if (res != NO_ERR) { return res; } /* $$default-module = string */ res = create_config_var(server_cb, YANGCLI_DEF_MODULE, default_module); if (res != NO_ERR) { return res; } /* $$display-mode = enum */ res = create_config_var(server_cb, YANGCLI_DISPLAY_MODE, ncx_get_display_mode_str(display_mode)); if (res != NO_ERR) { return res; } /* $$echo-replies = boolean */ res = create_config_var(server_cb, YANGCLI_ECHO_REPLIES, (echo_replies) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$echo-requests = boolean */ res = create_config_var(server_cb, YANGCLI_ECHO_REQUESTS, (echo_requests) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$ time-rpcs = boolean */ res = create_config_var(server_cb, YANGCLI_TIME_RPCS, (time_rpcs) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$ match-names = enum */ res = create_config_var(server_cb, YANGCLI_MATCH_NAMES, ncx_get_name_match_string(match_names)); if (res != NO_ERR) { return res; } /* $$ alt-names = boolean */ res = create_config_var(server_cb, YANGCLI_ALT_NAMES, (alt_names) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$ use-xmlheader = boolean */ res = create_config_var(server_cb, YANGCLI_USE_XMLHEADER, (use_xmlheader) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$user = string */ strval = NULL; parm = val_find_child(mgr_cli_valset, NULL, YANGCLI_USER); if (parm) { strval = VAL_STR(parm); } else { strval = (const xmlChar *)getenv(ENV_USER); } res = create_config_var(server_cb, YANGCLI_USER, strval); if (res != NO_ERR) { return res; } /* $$test-option = enum */ res = create_config_var(server_cb, YANGCLI_TEST_OPTION, op_testop_name(testoption)); if (res != NO_ERR) { return res; } /* $$error-optiona = enum */ res = create_config_var(server_cb, YANGCLI_ERROR_OPTION, op_errop_name(erroption)); if (res != NO_ERR) { return res; } /* $$default-timeout = uint32 */ sprintf((char *)numbuff, "%u", default_timeout); res = create_config_var(server_cb, YANGCLI_TIMEOUT, numbuff); if (res != NO_ERR) { return res; } /* $$indent = int32 */ sprintf((char *)numbuff, "%d", defindent); res = create_config_var(server_cb, NCX_EL_INDENT, numbuff); if (res != NO_ERR) { return res; } /* $$optional = boolean */ res = create_config_var(server_cb, YANGCLI_OPTIONAL, (server_cb->get_optional) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$log-level = enum * could have changed during CLI processing; do not cache */ res = create_config_var(server_cb, NCX_EL_LOGLEVEL, log_get_debug_level_string (log_get_debug_level())); if (res != NO_ERR) { return res; } /* $$fixorder = boolean */ res = create_config_var(server_cb, YANGCLI_FIXORDER, (fixorder) ? NCX_EL_TRUE : NCX_EL_FALSE); if (res != NO_ERR) { return res; } /* $$with-defaults = enum */ res = create_config_var(server_cb, YANGCLI_WITH_DEFAULTS, ncx_get_withdefaults_string(withdefaults)); if (res != NO_ERR) { return res; } /* $$default-operation = enum */ res = create_config_var(server_cb, NCX_EL_DEFAULT_OPERATION, op_defop_name(defop)); if (res != NO_ERR) { return res; } return NO_ERR; } /* init_config_vars */ /******************************************************************** * FUNCTION process_cli_input * * Process the param line parameters against the hardwired * parmset for the ncxmgr program * * INPUTS: * server_cb == server control block to use * argc == argument count * argv == array of command line argument strings * * OUTPUTS: * global vars that are present are filled in, with parms * gathered or defaults * * RETURNS: * NO_ERR if all goes well *********************************************************************/ status_t process_cli_input (server_cb_t *server_cb, int argc, char *argv[]) { obj_template_t *obj; val_value_t *parm; status_t res; ncx_display_mode_t dmode; boolean defs_done; res = NO_ERR; mgr_cli_valset = NULL; defs_done = FALSE; /* find the parmset definition in the registry */ obj = ncx_find_object(yangcli_mod, YANGCLI_BOOT); if (obj == NULL) { res = ERR_NCX_NOT_FOUND; } if (res == NO_ERR) { /* check no command line parms */ if (argc <= 1) { mgr_cli_valset = val_new_value(); if (mgr_cli_valset == NULL) { res = ERR_INTERNAL_MEM; } else { val_init_from_template(mgr_cli_valset, obj); } } else { /* parse the command line against the object template */ mgr_cli_valset = cli_parse(server_cb->runstack_context, argc, argv, obj, FULLTEST, PLAINMODE, autocomp, CLI_MODE_PROGRAM, &res); defs_done = TRUE; } } if (res != NO_ERR) { if (mgr_cli_valset) { val_free_value(mgr_cli_valset); mgr_cli_valset = NULL; } return res; } /* next get any params from the conf file */ confname = get_strparm(mgr_cli_valset, YANGCLI_MOD, YANGCLI_CONFIG); if (confname != NULL) { res = conf_parse_val_from_filespec(confname, mgr_cli_valset, TRUE, TRUE); } else { res = conf_parse_val_from_filespec(YANGCLI_DEF_CONF_FILE, mgr_cli_valset, TRUE, FALSE); } if (res == NO_ERR && defs_done == FALSE) { res = val_add_defaults(mgr_cli_valset, NULL, NULL, FALSE); } if (res != NO_ERR) { return res; } /**************************************************** * go through the yangcli params in order, * after setting up the logging parameters ****************************************************/ /* set the logging control parameters */ val_set_logging_parms(mgr_cli_valset); /* set the file search path parms */ val_set_path_parms(mgr_cli_valset); /* set the warning control parameters */ val_set_warning_parms(mgr_cli_valset); /* set the subdirs parm */ val_set_subdirs_parm(mgr_cli_valset); /* set the protocols parm */ res = val_set_protocols_parm(mgr_cli_valset); if (res != NO_ERR) { return res; } /* set the feature code generation parameters */ val_set_feature_parms(mgr_cli_valset); /* get the server parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_SERVER); if (parm && parm->res == NO_ERR) { /* save to the connect_valset parmset */ res = add_clone_parm(parm, connect_valset); if (res != NO_ERR) { return res; } } /* get the autoaliases parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_AUTOALIASES); if (parm && parm->res == NO_ERR) { autoaliases = VAL_BOOL(parm); } else { autoaliases = TRUE; } /* get the aliases-file parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_ALIASES_FILE); if (parm && parm->res == NO_ERR) { aliases_file = VAL_STR(parm); } else { aliases_file = YANGCLI_DEF_ALIASES_FILE; } /* get the autocomp parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_AUTOCOMP); if (parm && parm->res == NO_ERR) { autocomp = VAL_BOOL(parm); } else { autocomp = TRUE; } /* get the autohistory parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_AUTOHISTORY); if (parm && parm->res == NO_ERR) { autohistory = VAL_BOOL(parm); } else { autohistory = TRUE; } /* get the autoload parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_AUTOLOAD); if (parm && parm->res == NO_ERR) { autoload = VAL_BOOL(parm); } else { autoload = TRUE; } /* get the keep-session-model-copies-after-compilation parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_EX_MOD, YANGCLI_KEEP_SESSION_MODEL_COPIES_AFTER_COMPILATION); if (parm && parm->res == NO_ERR) { keep_session_model_copies_after_compilation = VAL_BOOL(parm); } else { keep_session_model_copies_after_compilation = FALSE; } /* get the autouservars parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_AUTOUSERVARS); if (parm && parm->res == NO_ERR) { autouservars = VAL_BOOL(parm); } else { autouservars = TRUE; } /* get the baddata parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_BADDATA); if (parm && parm->res == NO_ERR) { baddata = ncx_get_baddata_enum(VAL_ENUM_NAME(parm)); if (baddata == NCX_BAD_DATA_NONE) { SET_ERROR(ERR_INTERNAL_VAL); baddata = YANGCLI_DEF_BAD_DATA; } } else { baddata = YANGCLI_DEF_BAD_DATA; } /* get the batch-mode parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_BATCHMODE); if (parm && parm->res == NO_ERR) { batchmode = TRUE; } /* get the default module for unqualified module addesses */ default_module = get_strparm(mgr_cli_valset, YANGCLI_MOD, YANGCLI_DEF_MODULE); /* get the display-mode parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_DISPLAY_MODE); if (parm && parm->res == NO_ERR) { dmode = ncx_get_display_mode_enum(VAL_ENUM_NAME(parm)); if (dmode != NCX_DISPLAY_MODE_NONE) { display_mode = dmode; } else { display_mode = YANGCLI_DEF_DISPLAY_MODE; } } else { display_mode = YANGCLI_DEF_DISPLAY_MODE; } /* get the echo-replies parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_ECHO_REPLIES); if (parm && parm->res == NO_ERR) { echo_replies = VAL_BOOL(parm); } else { echo_replies = TRUE; } /* get the echo-requests parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_ECHO_REQUESTS); if (parm && parm->res == NO_ERR) { echo_requests = VAL_BOOL(parm); } else { echo_requests = FALSE; } /* get the time-rpcs parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_TIME_RPCS); if (parm && parm->res == NO_ERR) { time_rpcs = VAL_BOOL(parm); } else { time_rpcs = FALSE; } /* get the fixorder parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_FIXORDER); if (parm && parm->res == NO_ERR) { fixorder = VAL_BOOL(parm); } else { fixorder = YANGCLI_DEF_FIXORDER; } /* get the help flag */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_HELP); if (parm && parm->res == NO_ERR) { helpmode = TRUE; } /* help submode parameter (brief/normal/full) */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, NCX_EL_BRIEF); if (parm) { helpsubmode = HELP_MODE_BRIEF; } else { /* full parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, NCX_EL_FULL); if (parm) { helpsubmode = HELP_MODE_FULL; } else { helpsubmode = HELP_MODE_NORMAL; } } /* get indent param */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, NCX_EL_INDENT); if (parm && parm->res == NO_ERR) { defindent = (int32)VAL_UINT(parm); } else { defindent = NCX_DEF_INDENT; } /* get the password parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_PASSWORD); if (parm && parm->res == NO_ERR) { /* save to the connect_valset parmset */ res = add_clone_parm(parm, connect_valset); if (res != NO_ERR) { return res; } } /* get the --ncport parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_NCPORT); if (parm && parm->res == NO_ERR) { /* save to the connect_valset parmset */ res = add_clone_parm(parm, connect_valset); if (res != NO_ERR) { return res; } } /* get the --private-key parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_PRIVATE_KEY); if (parm && parm->res == NO_ERR) { /* save to the connect_valset parmset */ res = add_clone_parm(parm, connect_valset); if (res != NO_ERR) { return res; } } /* get the --public-key parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_PUBLIC_KEY); if (parm && parm->res == NO_ERR) { /* save to the connect_valset parmset */ res = add_clone_parm(parm, connect_valset); if (res != NO_ERR) { return res; } } /* get the --use-agent parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_USE_AGENT); if (parm && parm->res == NO_ERR) { /* save to the connect_valset parmset */ res = add_clone_parm(parm, connect_valset); if (res != NO_ERR) { return res; } } /* get the --transport parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_TRANSPORT); if (parm && parm->res == NO_ERR) { /* save to the connect_valset parmset */ res = add_clone_parm(parm, connect_valset); if (res != NO_ERR) { return res; } } /* get the --tcp-direct-enable parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_TCP_DIRECT_ENABLE); if (parm && parm->res == NO_ERR) { /* save to the connect_valset parmset */ res = add_clone_parm(parm, connect_valset); if (res != NO_ERR) { return res; } } /* get the --dump-session parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_EX_MOD, YANGCLI_DUMP_SESSION); if (parm && parm->res == NO_ERR) { /* save to the connect_valset parmset */ res = add_clone_parm(parm, connect_valset); if (res != NO_ERR) { return res; } } /* get the run-script parameter */ runscript = get_strparm(mgr_cli_valset, YANGCLI_MOD, YANGCLI_RUN_SCRIPT); /* get the run-command parameter */ runcommand = get_strparm(mgr_cli_valset, YANGCLI_MOD, YANGCLI_RUN_COMMAND); /* get the timeout parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_TIMEOUT); if (parm && parm->res == NO_ERR) { default_timeout = VAL_UINT(parm); /* save to the connect_valset parmset */ res = add_clone_parm(parm, connect_valset); if (res != NO_ERR) { return res; } } else { default_timeout = YANGCLI_DEF_TIMEOUT; } /* get the user name */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_USER); if (parm && parm->res == NO_ERR) { res = add_clone_parm(parm, connect_valset); if (res != NO_ERR) { return res; } } /* get the version parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, NCX_EL_VERSION); if (parm && parm->res == NO_ERR) { versionmode = TRUE; } /* get the match-names parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_MATCH_NAMES); if (parm && parm->res == NO_ERR) { match_names = ncx_get_name_match_enum(VAL_ENUM_NAME(parm)); if (match_names == NCX_MATCH_NONE) { return ERR_NCX_INVALID_VALUE; } } else { match_names = NCX_MATCH_EXACT; } /* get the alt-names parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_ALT_NAMES); if (parm && parm->res == NO_ERR) { alt_names = VAL_BOOL(parm); } else { alt_names = FALSE; } /* get the force-target parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_FORCE_TARGET); if (parm && parm->res == NO_ERR) { force_target = VAL_ENUM_NAME(parm); } else { force_target = NULL; } /* get the use-xmlheader parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_USE_XMLHEADER); if (parm && parm->res == NO_ERR) { use_xmlheader = VAL_BOOL(parm); } else { use_xmlheader = TRUE; } /* get the uservars-file parameter */ parm = val_find_child(mgr_cli_valset, YANGCLI_MOD, YANGCLI_USERVARS_FILE); if (parm && parm->res == NO_ERR) { uservars_file = VAL_STR(parm); } else { uservars_file = YANGCLI_DEF_USERVARS_FILE; } return NO_ERR; } /* process_cli_input */ /******************************************************************** * FUNCTION load_base_schema * * Load the following YANG modules: * yangcli * yuma-netconf * * RETURNS: * status *********************************************************************/ status_t load_base_schema (void) { status_t res; log_debug2("\nyangcli: Loading NCX yangcli-cli Parmset"); /* load in the NETCONF data types and RPC methods */ res = ncxmod_load_module(NCXMOD_NETCONF, NULL, NULL, &netconf_mod); if (res != NO_ERR) { return res; } /* load in the server boot parameter definition file */ res = ncxmod_load_module(YANGCLI_MOD, NULL, NULL, &yangcli_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module(YANGCLI_EX_MOD, NULL, NULL, &yangcli_ex_mod); if (res != NO_ERR) { return res; } /* load the netconf-state module to use * the operation */ res = ncxmod_load_module(NCXMOD_IETF_NETCONF_STATE, NULL, NULL, NULL); if (res != NO_ERR) { return res; } return NO_ERR; } /* load_base_schema */ /******************************************************************** * FUNCTION load_core_schema * * Load the following YANG modules: * yuma-xsd * yuma-types * * RETURNS: * status *********************************************************************/ status_t load_core_schema (void) { status_t res; log_debug2("\nNcxmgr: Loading NETCONF Module"); /* load in the XSD data types */ res = ncxmod_load_module(XSDMOD, NULL, NULL, NULL); if (res != NO_ERR) { return res; } /* load in the NCX data types */ res = ncxmod_load_module(NCXDTMOD, NULL, NULL, NULL); if (res != NO_ERR) { return res; } return NO_ERR; } /* load_core_schema */ /******************************************************************** * FUNCTION namespace_mismatch_warning * * Issue a namespace mismatch warning * * INPUTS: * module == module name * revision == revision date (may be NULL) * namespacestr == server expected URI * clientns == client provided URI *********************************************************************/ static void namespace_mismatch_warning (const xmlChar *module, const xmlChar *revision, const xmlChar *namespacestr, const xmlChar *clientns) { /* !!! FIXME: need a warning number for suppression */ log_warn("\nWarning: module namespace URI mismatch:" "\n module: '%s'" "\n revision: '%s'" "\n server ns: '%s'" "\n client ns: '%s'", module, (revision) ? revision : EMPTY_STRING, namespacestr, clientns); } /* namespace_mismatch_warning */ static const xmlChar *IYL = (const xmlChar *)"ietf-yang-library"; static const xmlChar *DEV = (const xmlChar *)"deviation"; static status_t check_module_deviations (server_cb_t *server_cb, const val_value_t *module_val, ncx_list_t *devlist) { val_value_t *dev; val_value_t *name; val_value_t *revision; const xmlChar *str; status_t res; ncxmod_search_result_t *searchresult; for (dev = val_find_child(module_val, IYL, DEV); dev; dev = val_find_next_child(module_val, IYL, DEV, dev)) { name = val_find_child(dev, IYL, "name"); if (name == NULL) { log_debug("\nmodule-state deviation has no name"); continue; } revision = val_find_child(dev, IYL, "revision"); if (revision == NULL) { log_debug("\nmodule-state deviation %s has no revision", VAL_STR(name)); continue; } str = xml_strdup(VAL_STR(name)); if (str == NULL) return ERR_INTERNAL_MEM; res = ncx_set_list(NCX_BT_STRING, str, devlist); if (res != NO_ERR) { m__free((void *)str); return res; } searchresult = ncxmod_find_module(VAL_STR(name), VAL_STR(revision)); if (searchresult == NULL) { searchresult = ncxmod_new_search_result(); if (searchresult == NULL) return ERR_INTERNAL_MEM; searchresult->module_val = dev; searchresult->module = xml_strdup(VAL_STR(name)); searchresult->revision = xml_strdup(VAL_STR(revision)); } dlq_enque(searchresult, &server_cb->searchresultQ); } return NO_ERR; } /******************************************************************** * FUNCTION check_module_capabilities * * Check the modules reported by the server * If autoload is TRUE then load any missing modules * otherwise just warn which modules are missing * Also check for wrong module module and version errors * * The server_cb->searchresultQ is filled with * records for each module or deviation specified in * the module capability URIs. * * After determining all the files that the server has, * the operation is used (if :schema-retrieval * advertised by the device and --autoload=true) * * All the files are copied into the session work directory * to make sure the correct versions are used when compiling * these files and applying features and deviations * * All files are compiled against the versions of the imports * advertised in the capabilities, to make sure that imports * without revision-date statements will still select the * same revision as the server (INSTEAD OF ALWAYS SELECTING * THE LATEST VERSION). * * If the device advertises an incomplete set of modules, * then searches for any missing imports will be done * using the normal search path, including YUMA_MODPATH. * * If yang library is enabled and supported get_module_set_fn is * called to resolve the yang library module set. * * INPUTS: * server_cb == server control block to use * scb == session control block * get_modules_fn == function retrieving schema with calls. * can be either blocking or async * get_module_set_fn == when yang-library is supported /module-state * can be either bloocking or async * *********************************************************************/ void check_module_capabilities (server_cb_t *server_cb, ses_cb_t *scb, status_t (*get_modules_fn)(server_cb_t*,ses_cb_t*), status_t (*get_module_set_fn)(server_cb_t*,ses_cb_t*)) { mgr_scb_t *mscb; ncx_module_t *mod; cap_rec_t *cap; const xmlChar *module, *revision, *namespacestr; ncxmod_search_result_t *searchresult, *libresult; status_t res; boolean retrieval_supported; val_value_t *module_set_id_val; val_value_t *module_val; val_value_t *submodule_val; mscb = (mgr_scb_t *)scb->mgrcb; if(cap_std_set(&mscb->caplist, CAP_STDID_YANG_LIBRARY) && mscb->modules_state_val==NULL ) { assert(get_module_set_fn); /* use yang-library /modules-state to figure module-set instead of the capabilities */ res = get_module_set_fn(server_cb, scb); if (res != NO_ERR) { log_error("\nError: get_module_set_fn failed (%s)", get_error_string(res)); return; } if (server_cb->command_mode == CMD_MODE_YANG_LIBRARY) { log_info("\n\nGetting yang-library module set ...\n"); return; } } if(cap_std_set(&mscb->caplist, CAP_STDID_YANG_LIBRARY)) { assert(mscb->modules_state_val); module_set_id_val = val_find_child(mscb->modules_state_val, "ietf-yang-library", "module-set-id"); assert(module_set_id_val); log_info("Current module-set-id: %s.\n", VAL_STRING(module_set_id_val)); } /* switch to the session module queue */ ncx_set_cur_modQ(&mscb->temp_modQ); retrieval_supported = cap_set(&mscb->caplist, CAP_SCHEMA_RETRIEVAL); log_info("\n\nChecking Server Modules...\n"); if (!cap_std_set(&mscb->caplist, CAP_STDID_V1)) { log_warn("\nWarning: NETCONF v1 capability not found"); } /* check all the YANG modules; * build a list of modules that * the server needs to get somehow * or proceed without them * save the results in the server_cb->searchresultQ */ if(mscb->modules_state_val) { /* yang-library supported */ cap=NULL; module_val = val_find_child(mscb->modules_state_val, "ietf-yang-library", "module"); submodule_val=NULL; } else { cap = cap_first_modcap(&mscb->caplist); module_val=NULL; submodule_val=NULL; } do { mod = NULL; module = NULL; revision = NULL; namespacestr = NULL; libresult = NULL; if(module_val) { val_value_t* parent_val; val_value_t* val; if(!submodule_val) { parent_val=module_val; } else { parent_val=submodule_val; } val = val_find_child(parent_val, "ietf-yang-library", "name"); assert(val); module = VAL_STRING(val); val = val_find_child(parent_val, "ietf-yang-library", "revision"); assert(val); revision = VAL_STRING(val); if(*revision==0) { revision=NULL; } val = val_find_child(module_val, "ietf-yang-library", "namespace"); assert(val); namespacestr = VAL_STRING(val); } else { if(cap==NULL) { break; } cap_split_modcap(cap, &module, &revision, &namespacestr); if (namespacestr == NULL) { /* try the entire base part of the URI if there was * no module capability parsed */ namespacestr = cap->cap_uri; } if (namespacestr == NULL) { if (ncx_warning_enabled(ERR_NCX_RCV_INVALID_MODCAP)) { log_warn("\nWarning: skipping enterprise capability " "for URI '%s'", cap->cap_uri); } cap = cap_next_modcap(cap); continue; } } if (module == NULL) { assert(cap); /* check if there is a module in the modlibQ that * has the same namespace URI as 'namespacestr' base */ libresult = ncxmod_find_search_result(&modlibQ, NULL, NULL, namespacestr); if (libresult != NULL) { module = libresult->module; revision = libresult->revision; } else { /* nothing found and modname is NULL, so continue */ if (ncx_warning_enabled(ERR_NCX_RCV_INVALID_MODCAP)) { log_warn("\nWarning: skipping enterprise capability " "for URI '%s'", cap->cap_uri); } cap = cap_next_modcap(cap); continue; } } /* Make an exception for ietf-netconf. Yuma123 quietly replaces * this module with yuma123-netconf. */ if (strcmp(module, NCXMOD_IETF_NETCONF) == 0) { log_debug2("\nUsing module %s in place of %s\n", NCXMOD_NETCONF, NCXMOD_IETF_NETCONF); module = NCXMOD_NETCONF; revision = NULL; /* namespace is already "urn:ietf:params:xml:ns:netconf:base:1.0" */ } mod = ncx_find_module(module, revision); if (mod != NULL) { /* make sure that the namespace URIs match */ if (ncx_compare_base_uris(mod->ns, namespacestr)) { namespace_mismatch_warning(module, revision, namespacestr, mod->ns); /* force a new search or auto-load */ mod = NULL; } } if (mod == NULL) { /* module was not found in the module search path * of this instance of yangcli * try to auto-load the module if enabled */ if (server_cb->autoload) { if (libresult) { searchresult = ncxmod_clone_search_result(libresult); if (searchresult == NULL) { log_error("\nError: cannot load file, " "malloc failed"); goto out; } } else { searchresult = ncxmod_find_module(module, revision); } if (searchresult) { if (submodule_val) check_module_deviations(server_cb, submodule_val, &searchresult->devlist); else if (module_val) check_module_deviations(server_cb, module_val, &searchresult->devlist); searchresult->cap = cap; searchresult->module_val = module_val; searchresult->submodule_val = submodule_val; if (searchresult->res != NO_ERR) { if (LOGDEBUG2) { log_debug2("\nLocal module search failed (%s)", get_error_string(searchresult->res)); } } else if (searchresult->source) { /* module with matching name, revision * was found on the local system; * check if the namespace also matches */ if (searchresult->namespacestr) { if (ncx_compare_base_uris (searchresult->namespacestr, namespacestr)) { /* cannot use this local file because * it has a different namespace */ namespace_mismatch_warning (module, revision, namespacestr, searchresult->namespacestr); } else { /* can use the local system file found */ searchresult->capmatch = TRUE; } } else { /* this module found is invalid; * has no namespace statement */ log_error("\nError: found module '%s' " "revision '%s' " "has no namespace-stmt", module, (revision) ? revision : EMPTY_STRING); } } else { /* the search result is valid, but no source; * specified module is not available * on the manager platform; see if the * get-schema operation is available */ if (!retrieval_supported) { /* no so SOL, do without this module * !!! FIXME: need warning number */ if (revision != NULL) { log_warn("\nWarning: module '%s' " "revision '%s' not available", module, revision); } else { log_warn("\nWarning: module '%s' " "(no revision) not available", module); } } else if (LOGDEBUG) { log_debug("\nautoload: Module '%s' not available," " will try ", module); } } /* save the search result no matter what */ dlq_enque(searchresult, &server_cb->searchresultQ); } else { /* libresult was NULL, so there was no searchresult * ncxmod did not find any YANG module with this namespace * the module is not available */ if (!retrieval_supported) { /* no so SOL, do without this module * !!! need warning number */ if (revision != NULL) { log_warn("\nWarning: module '%s' " "revision '%s' not available", module, revision); } else { log_warn("\nWarning: module '%s' " "(no revision) not available", module); } } else { /* setup a blank searchresult so auto-load * will attempt to retrieve it */ searchresult = ncxmod_new_search_result_str(module, revision); if (searchresult) { if (submodule_val) check_module_deviations(server_cb, submodule_val, &searchresult->devlist); else if (module_val) check_module_deviations(server_cb, module_val, &searchresult->devlist); searchresult->cap = cap; searchresult->module_val = module_val; dlq_enque(searchresult, &server_cb->searchresultQ); if (LOGDEBUG) { log_debug("\nyangcli_autoload: Module '%s' " "not available, will try " "", module); } } else { log_error("\nError: cannot load file, " "malloc failed"); goto out; } } } } else { /* --autoload=false */ if (LOGINFO) { log_info("\nModule '%s' " "revision '%s' not " "loaded, autoload disabled", module, (revision) ? revision : EMPTY_STRING); } } } else { /* since the module was already loaded, it is * OK to use, even if --autoload=false * just copy the info into a search result record * so it will be copied and recompiled with the * correct features and deviations applied */ searchresult = ncxmod_new_search_result_ex(mod); if (searchresult == NULL) { log_error("\nError: cannot load file, malloc failed"); goto out; } else { searchresult->cap = cap; searchresult->module_val = module_val; searchresult->submodule_val = submodule_val; searchresult->capmatch = TRUE; dlq_enque(searchresult, &server_cb->searchresultQ); } } /* move on to the next module */ if(cap) { cap = cap_next_modcap(cap); } else { if(!submodule_val) { /*submodules?*/ submodule_val = val_find_child(module_val, "ietf-yang-library", "submodule"); } else { /*more submodules?*/ submodule_val = val_find_next_child(module_val, "ietf-yang-library", "submodule", submodule_val); } if(!submodule_val) { /*next module*/ module_val = val_find_next_child(mscb->modules_state_val, "ietf-yang-library", "module", module_val); } } } while(cap || module_val); /* get all the advertised YANG data model modules into the * session temp work directory that are local to the system */ res = autoload_setup_tempdir(server_cb, scb); if (res != NO_ERR) { log_error("\nError: autoload setup temp files failed (%s)", get_error_string(res)); } /* go through all the search results (if any) * and see if is needed to pre-load * the session work directory YANG files */ if (res == NO_ERR && retrieval_supported && server_cb->autoload) { /* * get-schema operations are done or just started */ ncx_reset_modQ(); res = get_modules_fn(server_cb, scb); ncx_set_cur_modQ(&mscb->temp_modQ); if (res != NO_ERR) { log_error("\nError: autoload get modules failed (%s)", get_error_string(res)); } } /* check autoload state did not start or was not requested */ if (res == NO_ERR && server_cb->command_mode != CMD_MODE_AUTOLOAD) { /* parse and hold the modules with the correct deviations, * features and revisions. The returned modules * from yang_parse.c will not be stored in the local module * directory -- just used for this one session then deleted */ res = autoload_compile_modules(server_cb, scb); if (res != NO_ERR) { log_error("\nError: autoload compile modules failed (%s)", get_error_string(res)); } } out: ncx_reset_modQ(); } /* check_module_capabilities */ status_t process_module_deviations (dlq_hdr_t *savedevQ) { ncx_save_deviations_t *savedev; ncx_module_t *retmod; status_t res = NO_ERR; /* Finish filling in the deviation modules' import structures. * Loading deviation modules skips over this step, but since all * modules should be loaded by this point, iterate through the * imports and search for each missing module. * * NOTE: ncxmod_process_deviation_imports() uses ncx_find_module() * which honors the "session" module queue. */ for (savedev = (ncx_save_deviations_t *)dlq_firstEntry(savedevQ); savedev; savedev = (ncx_save_deviations_t *)dlq_nextEntry(savedev)) { res = ncxmod_process_deviation_imports(savedev); if (res != NO_ERR) { log_error("\nError: one or more modules imported by %s is not loaded.", savedev->devmodule); return ERR_NCX_OPERATION_FAILED; } } /* After the deviations have been resolved, any nodes to be deleted * will have been marked as such. ncxmod_apply_deviations() will * cause those nodes to actually be deleted. This happens in a * separate loop because, in the case that the target node is the * result of a yang augment statement, the module in which the target * obj_template_t will be found may (generally will) be different * than the module in which the deviation was resolved. * * NOTE: ncx_get_first_module() honors the "current" module queue, * NOT the "session" queue. Be sure to call ncx_set_cur_modQ() * first if proessing deviations for a particular session. */ for (retmod = ncx_get_first_module(); retmod; retmod = ncx_get_next_module(retmod)) { res = ncxmod_resolve_deviations(retmod, savedevQ); if (res != NO_ERR) return ERR_NCX_OPERATION_FAILED; } for (retmod = ncx_get_first_module(); retmod; retmod = ncx_get_next_module(retmod)) { res = ncxmod_apply_deviations(retmod); if (res != NO_ERR) return ERR_NCX_OPERATION_FAILED; } return res; } /******************************************************************** * message_timed_out * * Check if the request in progress (!) has timed out * TBD: check for a specific message if N requests per * session are ever supported * * INPUTS: * scb == session control block to use * * RETURNS: * TRUE if any messages have timed out * FALSE if no messages have timed out *********************************************************************/ static boolean message_timed_out (ses_cb_t *scb) { mgr_scb_t *mscb; uint32 deletecount; mscb = (mgr_scb_t *)scb->mgrcb; if (mscb) { deletecount = mgr_rpc_timeout_requestQ(&mscb->reqQ); if (deletecount) { log_error("\nError: request to server timed out"); } return (deletecount) ? TRUE : FALSE; } /* else mgr_shutdown could have been issued via control-C * and the session control block has been deleted */ return FALSE; } /* message_timed_out */ /******************************************************************** * FUNCTION get_lock_worked * * Check if the get-locks function ended up with * all its locks or not * * INPUTS: * server_cb == server control block to use * *********************************************************************/ static boolean get_lock_worked (server_cb_t *server_cb) { ncx_cfg_t cfgid; for (cfgid = NCX_CFGID_RUNNING; cfgid <= NCX_CFGID_STARTUP; cfgid++) { if (server_cb->lock_cb[cfgid].lock_used && server_cb->lock_cb[cfgid].lock_state != LOCK_STATE_ACTIVE) { return FALSE; } } return TRUE; } /* get_lock_worked */ /******************************************************************** * FUNCTION get_input_line * * get a line of input text from the appropriate source * * INPUTS: * server_sb == server control block to use * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to the line to use; do not free this memory *********************************************************************/ static xmlChar * get_input_line (server_cb_t *server_cb, status_t *res) { xmlChar *line; boolean done; runstack_src_t rsrc; /* get a line of user input * this is really a const pointer */ *res = NO_ERR; done = FALSE; line = NULL; while (!done && *res == NO_ERR) { /* figure out where to get the next input line */ rsrc = runstack_get_source(server_cb->runstack_context); switch (rsrc) { case RUNSTACK_SRC_NONE: *res = SET_ERROR(ERR_INTERNAL_VAL); break; case RUNSTACK_SRC_USER: /* block until user enters some input */ line = get_cmd_line(server_cb, res); break; case RUNSTACK_SRC_SCRIPT: /* get one line of script text */ line = runstack_get_cmd(server_cb->runstack_context, res); break; case RUNSTACK_SRC_LOOP: /* get one line of script text */ line = runstack_get_loop_cmd(server_cb->runstack_context, res); if (line == NULL || *res != NO_ERR) { if (*res == ERR_NCX_LOOP_ENDED) { /* did not get a loop line, try again */ *res = NO_ERR; continue; } return NULL; } break; default: *res = SET_ERROR(ERR_INTERNAL_VAL); } if (*res == NO_ERR) { *res = runstack_save_line(server_cb->runstack_context, line); } /* only 1 time through loop in normal conditions */ done = TRUE; } return line; } /* get_input_line */ /******************************************************************** * FUNCTION do_bang_recall (local !string) * * Do Command line history support operations * * !sget-config target=running * !42 * * INPUTS: * server_cb == server control block to use * line == CLI input in progress * * RETURNS: * status *********************************************************************/ static status_t do_bang_recall (server_cb_t *server_cb, const xmlChar *line) { status_t res; res = NO_ERR; if (line && *line == YANGCLI_RECALL_CHAR && line[1] != 0) { if (isdigit(line[1])) { /* assume the form is !42 to recall by line number */ ncx_num_t num; ncx_init_num(&num); res = ncx_decode_num(&line[1], NCX_BT_UINT64, &num); if (res != NO_ERR) { log_error("\nError: invalid number '%s'", get_error_string(res)); } else { res = do_line_recall(server_cb, num.ul); } ncx_clean_num(NCX_BT_UINT64, &num); } else { /* assume form is !command string and recall by * most recent match of the partial command line given */ res = do_line_recall_string(server_cb, &line[1]); } } else { res = ERR_NCX_MISSING_PARM; log_error("\nError: missing recall string\n"); } return res; } /* do_bang_recall */ /******************************************************************** * yangcli_stdin_handler * * Temp: Calling readline which will block other IO while the user * is editing the line. This is okay for this CLI application * but not multi-session applications; * Need to prevent replies from popping up on the screen * while new commands are being edited anyway * * RETURNS: * new program state *********************************************************************/ static mgr_io_state_t yangcli_stdin_handler (void) { server_cb_t *server_cb; xmlChar *line; const xmlChar *resultstr; ses_cb_t *scb = NULL; mgr_scb_t *mscb; boolean getrpc, fileassign, done; status_t res; uint32 len; runstack_src_t rsrc; /* assuming cur_server_cb will be set dynamically * at some point; currently 1 session supported in yangcli */ server_cb = cur_server_cb; if (server_cb->returncode == MGR_IO_RC_WANTDATA) { if (LOGDEBUG2) { log_debug2("\nyangcli: sending dummy keepalive"); } (void)send_keepalive_get(server_cb); server_cb->returncode = MGR_IO_RC_NONE; } if (server_cb->cli_fn == NULL && !server_cb->climore) { init_completion_state(&server_cb->completion_state, server_cb, CMD_STATE_FULL); } if (mgr_shutdown_requested()) { server_cb->state = MGR_IO_ST_SHUT; } switch (server_cb->state) { case MGR_IO_ST_INIT: return server_cb->state; case MGR_IO_ST_IDLE: break; case MGR_IO_ST_CONN_IDLE: /* check if session was dropped by remote peer */ scb = mgr_ses_get_scb(server_cb->mysid); if (scb==NULL || scb->state == SES_ST_SHUTDOWN_REQ) { if (scb) { (void)mgr_ses_free_session(server_cb->mysid); } clear_server_cb_session(server_cb); } else { res = NO_ERR; mscb = (mgr_scb_t *)scb->mgrcb; ncx_set_temp_modQ(&mscb->temp_modQ); /* check locks timeout */ if (!(server_cb->command_mode == CMD_MODE_NORMAL || server_cb->command_mode == CMD_MODE_AUTOLOAD || server_cb->command_mode == CMD_MODE_YANG_LIBRARY)) { if (check_locks_timeout(server_cb)) { res = ERR_NCX_TIMEOUT; if (runstack_level(server_cb->runstack_context)) { runstack_cancel(server_cb->runstack_context); } switch (server_cb->command_mode) { case CMD_MODE_AUTODISCARD: case CMD_MODE_AUTOLOCK: handle_locks_cleanup(server_cb); if (server_cb->command_mode != CMD_MODE_NORMAL) { return server_cb->state; } break; case CMD_MODE_AUTOUNLOCK: clear_lock_cbs(server_cb); break; case CMD_MODE_AUTOLOAD: case CMD_MODE_NORMAL: default: SET_ERROR(ERR_INTERNAL_VAL); } } else if (server_cb->command_mode == CMD_MODE_AUTOLOCK) { if (server_cb->locks_waiting) { server_cb->command_mode = CMD_MODE_AUTOLOCK; done = FALSE; res = handle_get_locks_request_to_server(server_cb, FALSE, &done); if (done) { /* check if the locks are all good */ if (get_lock_worked(server_cb)) { log_info("\nget-locks finished OK"); server_cb->command_mode = CMD_MODE_NORMAL; server_cb->locks_waiting = FALSE; } else { log_error("\nError: get-locks failed, " "starting cleanup"); handle_locks_cleanup(server_cb); } } } } } } break; case MGR_IO_ST_CONN_START: /* waiting until processing complete */ scb = mgr_ses_get_scb(server_cb->mysid); if (scb == NULL) { /* session startup failed */ server_cb->state = MGR_IO_ST_IDLE; if (batchmode) { mgr_request_shutdown(); return server_cb->state; } } else if (scb->state == SES_ST_IDLE && dlq_empty(&scb->outQ)) { /* incoming hello OK and outgoing hello is sent */ server_cb->state = MGR_IO_ST_CONN_IDLE; mscb = (mgr_scb_t *)scb->mgrcb; report_capabilities(server_cb, scb, TRUE, HELP_MODE_NONE); check_module_capabilities(server_cb, scb, autoload_start_get_modules, yang_library_start_get_module_set); ncx_set_temp_modQ(&mscb->temp_modQ); } else { /* check timeout */ if (message_timed_out(scb)) { server_cb->state = MGR_IO_ST_IDLE; if (batchmode) { mgr_request_shutdown(); return server_cb->state; } break; } /* else still setting up session */ return server_cb->state; } break; case MGR_IO_ST_CONN_RPYWAIT: /* check if session was dropped by remote peer */ scb = mgr_ses_get_scb(server_cb->mysid); if (scb==NULL || scb->state == SES_ST_SHUTDOWN_REQ) { if (scb) { (void)mgr_ses_free_session(server_cb->mysid); } clear_server_cb_session(server_cb); } else { res = NO_ERR; mscb = (mgr_scb_t *)scb->mgrcb; ncx_set_temp_modQ(&mscb->temp_modQ); /* check timeout */ if (message_timed_out(scb)) { res = ERR_NCX_TIMEOUT; } else if (!(server_cb->command_mode == CMD_MODE_NORMAL || server_cb->command_mode == CMD_MODE_AUTOLOAD || server_cb->command_mode == CMD_MODE_YANG_LIBRARY || server_cb->command_mode == CMD_MODE_SAVE)) { if (check_locks_timeout(server_cb)) { res = ERR_NCX_TIMEOUT; } } if (res != NO_ERR) { if (runstack_level(server_cb->runstack_context)) { runstack_cancel(server_cb->runstack_context); } server_cb->state = MGR_IO_ST_CONN_IDLE; switch (server_cb->command_mode) { break; case CMD_MODE_NORMAL: break; case CMD_MODE_AUTOLOAD: /* even though a timeout occurred * attempt to compile the modules * that are already present */ autoload_compile_modules(server_cb, scb); break; case CMD_MODE_AUTODISCARD: server_cb->command_mode = CMD_MODE_AUTOLOCK; /* fall through */ case CMD_MODE_AUTOLOCK: handle_locks_cleanup(server_cb); if (server_cb->command_mode != CMD_MODE_NORMAL) { return server_cb->state; } break; case CMD_MODE_AUTOUNLOCK: clear_lock_cbs(server_cb); break; case CMD_MODE_SAVE: server_cb->command_mode = CMD_MODE_NORMAL; break; default: SET_ERROR(ERR_INTERNAL_VAL); } } else { /* keep waiting for reply */ return server_cb->state; } } break; case MGR_IO_ST_CONNECT: case MGR_IO_ST_SHUT: case MGR_IO_ST_CONN_CANCELWAIT: case MGR_IO_ST_CONN_SHUT: case MGR_IO_ST_CONN_CLOSEWAIT: /* check timeout */ scb = mgr_ses_get_scb(server_cb->mysid); if (scb==NULL || scb->state == SES_ST_SHUTDOWN_REQ) { if (scb) { (void)mgr_ses_free_session(server_cb->mysid); } clear_server_cb_session(server_cb); } else if (message_timed_out(scb)) { clear_server_cb_session(server_cb); } /* do not accept chars in these states */ return server_cb->state; default: SET_ERROR(ERR_INTERNAL_VAL); return server_cb->state; } /* check if some sort of auto-mode, and the * CLI is skipped until that mode is done */ if (server_cb->command_mode != CMD_MODE_NORMAL) { return server_cb->state; } /* The scb pointer will only have been initialized if a connection * was established. If not, mscb will not have been initialized * and it's not safe to use temp_modQ. * * However, if a connection has been established, then at this point * all of the modules, including deviations, should have been loaded. * Now let's apply the deviations. The "session" module queue is * already set (ncx_set_session_modQ()). */ if (scb != NULL && server_cb->deviations_applied == FALSE) { ncx_set_cur_modQ(&mscb->temp_modQ); process_module_deviations(&server_cb->autoload_savedevQ); server_cb->deviations_applied = TRUE; ncx_reset_modQ(); } /* check the run-script parameters */ if (runscript) { if (!runscriptdone) { runscriptdone = TRUE; (void)do_startup_script(server_cb, runscript); /* drop through and get input line from runstack */ } } else if (runcommand) { if (!runcommanddone) { runcommanddone = TRUE; (void)do_startup_command(server_cb, runcommand); /* exit now in case there was a session started up and a remote * command sent as part of run-command. * May need to let write_sessions() run in mgr_io.c. * If not, 2nd loop through this fn will hit get_input_line */ return server_cb->state; } else if (batchmode) { /* run command is done at this point */ mgr_request_shutdown(); return server_cb->state; } } /* get a line of user input * this is really a const pointer */ res = NO_ERR; line = get_input_line(server_cb, &res); /* figure out where to get the next input line */ rsrc = runstack_get_source(server_cb->runstack_context); runstack_clear_cancel(server_cb->runstack_context); switch (rsrc) { case RUNSTACK_SRC_NONE: res = SET_ERROR(ERR_INTERNAL_VAL); break; case RUNSTACK_SRC_USER: if (line == NULL) { /* could have just transitioned from script mode to * user mode; check batch-mode exit; */ if (batchmode) { mgr_request_shutdown(); } return server_cb->state; } break; case RUNSTACK_SRC_SCRIPT: /* get one line of script text */ if (line == NULL || res != NO_ERR) { if (res == ERR_NCX_EOF) { ; } if (batchmode && res != NO_ERR) { mgr_request_shutdown(); } return server_cb->state; } break; case RUNSTACK_SRC_LOOP: if (line==NULL || res != NO_ERR) { if (batchmode && res != NO_ERR) { mgr_request_shutdown(); } return server_cb->state; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { return server_cb->state; } /* check bang recall statement */ if (*line == YANGCLI_RECALL_CHAR) { res = do_bang_recall(server_cb, line); return server_cb->state; } /* check if this is an assignment statement */ res = check_assign_statement(server_cb, line, &len, &getrpc, &fileassign); if (res != NO_ERR) { log_error("\nyangcli: Variable assignment failed (%s) (%s)", line, get_error_string(res)); if (runstack_level(server_cb->runstack_context)) { runstack_cancel(server_cb->runstack_context); } switch (server_cb->command_mode) { break; case CMD_MODE_AUTODISCARD: case CMD_MODE_AUTOLOCK: handle_locks_cleanup(server_cb); break; case CMD_MODE_AUTOUNLOCK: clear_lock_cbs(server_cb); break; case CMD_MODE_AUTOLOAD: case CMD_MODE_NORMAL: break; default: SET_ERROR(ERR_INTERNAL_VAL); } } else if (getrpc) { res = NO_ERR; switch (server_cb->state) { case MGR_IO_ST_IDLE: /* waiting for top-level commands */ res = top_command(server_cb, &line[len]); break; case MGR_IO_ST_CONN_IDLE: /* waiting for session commands */ res = conn_command(server_cb, &line[len]); break; case MGR_IO_ST_CONN_RPYWAIT: /* waiting for RPC reply while more input typed */ break; case MGR_IO_ST_CONN_CANCELWAIT: break; default: break; } if (res != NO_ERR) { if (runstack_level(server_cb->runstack_context)) { runstack_cancel(server_cb->runstack_context); } } switch (server_cb->state) { case MGR_IO_ST_IDLE: case MGR_IO_ST_CONN_IDLE: /* check assignment statement active */ if (server_cb->result_name != NULL || server_cb->result_filename != NULL) { if (server_cb->local_result != NULL) { val_value_t *resval = server_cb->local_result; server_cb->local_result = NULL; /* pass off malloced local_result here */ res = finish_result_assign(server_cb, resval, NULL); /* clear_result already called */ } else { /* save the filled in value */ resultstr = (res == NO_ERR) ? (const xmlChar *)"ok" : (const xmlChar *)get_error_string(res); res = finish_result_assign(server_cb, NULL, resultstr); /* clear_result already called */ } } else { clear_result(server_cb); } break; default: ; } } else { log_info("\nOK\n"); } return server_cb->state; } /* yangcli_stdin_handler */ /******************************************************************** * FUNCTION yangcli_notification_handler * * matches callback template mgr_not_cbfn_t * * INPUTS: * scb == session receiving RPC reply * msg == notification msg that was parsed * consumed == address of return consumed message flag * * OUTPUTS: * *consumed == TRUE if msg has been consumed so * it will not be freed by mgr_not_dispatch * == FALSE if msg has been not consumed so * it will be freed by mgr_not_dispatch *********************************************************************/ static void yangcli_notification_handler (ses_cb_t *scb, mgr_not_msg_t *msg, boolean *consumed) { server_cb_t *server_cb; mgr_scb_t *mgrcb; #ifdef DEBUG if (!scb || !msg || !consumed) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif *consumed = FALSE; mgrcb = scb->mgrcb; if (mgrcb) { ncx_set_temp_modQ(&mgrcb->temp_modQ); } server_cb = (server_cb_t*) mgrcb->context_ptr; /* check the contents of the notification */ if (msg && msg->notification) { if (LOGWARN) { gl_normal_io(server_cb->cli_gl); if (LOGINFO) { log_info("\n\nIncoming notification:"); val_dump_value_max(msg->notification, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); log_info("\n\n"); } else if (msg->eventType) { log_warn("\n\nIncoming <%s> " "notification\n\n", msg->eventType->name); } } /* Log the notification by removing it from the message * and storing it in the server notification log */ dlq_enque(msg, &server_cb->notificationQ); *consumed = TRUE; } } /* yangcli_notification_handler */ /******************************************************************** * FUNCTION yangcli_init_module_static_vars * * Init the NCX CLI application static vars * * INPUTS: * argc == number of strings in argv array * argv == array of command line strings * * RETURNS: * status *********************************************************************/ void yangcli_init_module_static_vars() { /* init the module static vars */ dlq_createSQue(&server_cbQ); cur_server_cb = NULL; yangcli_mod = NULL; netconf_mod = NULL; mgr_cli_valset = NULL; batchmode = FALSE; helpmode = FALSE; helpsubmode = HELP_MODE_NONE; versionmode = FALSE; runscript = NULL; runscriptdone = FALSE; runcommand = NULL; runcommanddone = FALSE; aliases_file = YANGCLI_DEF_ALIASES_FILE; autoaliases = TRUE; autocomp = TRUE; autohistory = TRUE; autoload = TRUE; autouservars = TRUE; baddata = YANGCLI_DEF_BAD_DATA; connect_valset = NULL; confname = NULL; default_module = NULL; default_timeout = 30; display_mode = NCX_DISPLAY_MODE_PLAIN; fixorder = TRUE; optional = FALSE; testoption = YANGCLI_DEF_TEST_OPTION; erroption = YANGCLI_DEF_ERROR_OPTION; defop = YANGCLI_DEF_DEFAULT_OPERATION; withdefaults = YANGCLI_DEF_WITH_DEFAULTS; echo_replies = TRUE; echo_requests = TRUE; time_rpcs = FALSE; uservars_file = YANGCLI_DEF_USERVARS_FILE; dlq_createSQue(&modlibQ); dlq_createSQue(&aliasQ); /* set the character set LOCALE to the user default */ setlocale(LC_CTYPE, ""); } /******************************************************************** * FUNCTION yangcli_init * * Init the NCX CLI application * * INPUTS: * argc == number of strings in argv array * argv == array of command line strings * * RETURNS: * status *********************************************************************/ static status_t yangcli_init (int argc, char *argv[]) { obj_template_t *obj; server_cb_t *server_cb; val_value_t *parm, *modval; xmlChar *savestr, *revision; status_t res; uint32 modlen; log_debug_t log_level; dlq_hdr_t savedevQ; xmlChar versionbuffer[NCX_VERSION_BUFFSIZE]; /* deviations processing */ ncx_module_t *retmod; ncx_save_deviations_t *savedev; #ifdef YANGCLI_DEBUG int i; #endif /* set the default debug output level */ log_level = LOG_DEBUG_INFO; dlq_createSQue(&savedevQ); yangcli_init_module_static_vars(); /* initialize the NCX Library first to allow NCX modules * to be processed. No module can get its internal config * until the NCX module parser and definition registry is up */ res = ncx_init(NCX_SAVESTR, log_level, TRUE, NULL, argc, argv); if (res != NO_ERR) { return res; } #ifdef YANGCLI_DEBUG if (argc>1 && LOGDEBUG2) { log_debug2("\nCommand line parameters:"); for (i=0; i if the server parameter is set a connect will * --> be attempted * * The yangcli_stdin_handler will call the finish_start_session * function when the user enters a line of keyboard text */ server_cb->state = MGR_IO_ST_IDLE; if (connect_valset) { parm = val_find_child(connect_valset, YANGCLI_MOD, YANGCLI_SERVER); if (parm && parm->res == NO_ERR) { res = do_connect(server_cb, NULL, NULL, 0, TRUE); if (res != NO_ERR) { if (!batchmode) { res = NO_ERR; } } } } return res; } /* yangcli_init */ /******************************************************************** * FUNCTION yangcli_cleanup * * * *********************************************************************/ static void yangcli_cleanup (void) { server_cb_t *server_cb; modptr_t *modptr; status_t res = NO_ERR; log_debug2("\nShutting down yangcli\n"); /* save the user variables */ if (autouservars && init_done) { if (cur_server_cb) { res = save_uservars(cur_server_cb, uservars_file); } else { res = ERR_NCX_MISSING_PARM; } if (res != NO_ERR) { log_error("\nError: yangcli user variables could " "not be saved (%s)", get_error_string(res)); } } /* save the user aliases */ if (autoaliases && init_done) { res = save_aliases(aliases_file); if (res != NO_ERR) { log_error("\nError: yangcli command aliases could " "not be saved (%s)", get_error_string(res)); } } while (!dlq_empty(&server_cbQ)) { server_cb = (server_cb_t *)dlq_deque(&server_cbQ); free_server_cb(server_cb); } if (mgr_cli_valset) { val_free_value(mgr_cli_valset); mgr_cli_valset = NULL; } if (connect_valset) { val_free_value(connect_valset); connect_valset = NULL; } if (default_module) { m__free(default_module); default_module = NULL; } if (confname) { m__free(confname); confname = NULL; } if (runscript) { m__free(runscript); runscript = NULL; } if (runcommand) { m__free(runcommand); runcommand = NULL; } /* Cleanup the Netconf Server Library */ mgr_cleanup(); /* cleanup the module library search results */ ncxmod_clean_search_result_queue(&modlibQ); if(yangcli_progcb!=NULL) { ncxmod_free_program_tempdir(yangcli_progcb); } free_aliases(); /* cleanup the NCX engine and registries */ ncx_cleanup(); } /* yangcli_cleanup */ /******************************************************************** * FUNCTION get_rpc_error_tag * * Determine why the RPC operation failed * * INPUTS: * replyval == to use to look for s * * RETURNS: * the RPC error code for the that was found *********************************************************************/ static rpc_err_t get_rpc_error_tag (val_value_t *replyval) { val_value_t *errval, *tagval; errval = val_find_child(replyval, NC_MODULE, NCX_EL_RPC_ERROR); if (errval == NULL) { log_error("\nError: No elements found"); return RPC_ERR_NONE; } tagval = val_find_child(errval, NC_MODULE, NCX_EL_ERROR_TAG); if (tagval == NULL) { log_error("\nError: did not contain an "); return RPC_ERR_NONE; } return rpc_err_get_errtag_enum(VAL_ENUM_NAME(tagval)); } /* get_rpc_error_tag */ /******************************************************************** * FUNCTION handle_rpc_timing * * Get the roundtrip time and print the roundtrip time * * INPUTS: * server_cb == server control block to use * req == original message request * *********************************************************************/ static void handle_rpc_timing (server_cb_t *server_cb, mgr_rpc_req_t *req) { struct timeval now; long int sec, usec; xmlChar numbuff[NCX_MAX_NUMLEN]; if (!server_cb->time_rpcs) { return; } gettimeofday(&now, NULL); /* get the resulting delta */ if (now.tv_usec < req->perfstarttime.tv_usec) { now.tv_usec += 1000000; now.tv_sec--; } sec = now.tv_sec - req->perfstarttime.tv_sec; usec = now.tv_usec - req->perfstarttime.tv_usec; sprintf((char *)numbuff, "%ld.%06ld", sec, usec); if (interactive_mode()) { log_stdout("\nRoundtrip time: %s seconds\n", numbuff); if (log_get_logfile() != NULL) { log_write("\nRoundtrip time: %s seconds\n", numbuff); } } else { log_write("\nRoundtrip time: %s seconds\n", numbuff); } } /* handle_rpc_timing */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION get_autocomp * * Get the autocomp parameter value * * RETURNS: * autocomp boolean value *********************************************************************/ boolean get_autocomp (void) { return autocomp; } /* get_autocomp */ /******************************************************************** * FUNCTION get_autoload * * Get the autoload parameter value * * RETURNS: * autoload boolean value *********************************************************************/ boolean get_autoload (void) { return autoload; } /* get_autoload */ /******************************************************************** * FUNCTION get_batchmode * * Get the batchmode parameter value * * RETURNS: * batchmode boolean value *********************************************************************/ boolean get_batchmode (void) { return batchmode; } /* get_batchmode */ /******************************************************************** * FUNCTION get_default_module * * Get the default module * * RETURNS: * default module value *********************************************************************/ const xmlChar * get_default_module (void) { return default_module; } /* get_default_module */ /******************************************************************** * FUNCTION get_runscript * * Get the runscript variable * * RETURNS: * runscript value *********************************************************************/ const xmlChar * get_runscript (void) { return runscript; } /* get_runscript */ /******************************************************************** * FUNCTION get_baddata * * Get the baddata parameter * * RETURNS: * baddata enum value *********************************************************************/ ncx_bad_data_t get_baddata (void) { return baddata; } /* get_baddata */ /******************************************************************** * FUNCTION get_yangcli_mod * * Get the yangcli module * * RETURNS: * yangcli module *********************************************************************/ ncx_module_t * get_yangcli_mod (void) { return yangcli_mod; } /* get_yangcli_mod */ /******************************************************************** * FUNCTION get_mgr_cli_valset * * Get the CLI value set * * RETURNS: * mgr_cli_valset variable *********************************************************************/ val_value_t * get_mgr_cli_valset (void) { return mgr_cli_valset; } /* get_mgr_cli_valset */ /******************************************************************** * FUNCTION get_connect_valset * * Get the connect value set * * RETURNS: * connect_valset variable *********************************************************************/ val_value_t * get_connect_valset (void) { return connect_valset; } /* get_connect_valset */ /******************************************************************** * FUNCTION get_aliases_file * * Get the aliases-file value * * RETURNS: * aliases_file variable *********************************************************************/ const xmlChar * get_aliases_file (void) { return aliases_file; } /* get_aliases_file */ /******************************************************************** * FUNCTION get_uservars_file * * Get the uservars-file value * * RETURNS: * aliases_file variable *********************************************************************/ const xmlChar * get_uservars_file (void) { return uservars_file; } /* get_uservars_file */ /******************************************************************** * FUNCTION replace_connect_valset * * Replace the current connect value set with a clone * of the specified connect valset * * INPUTS: * valset == value node to clone that matches the object type * of the input section of the connect operation * * RETURNS: * status *********************************************************************/ status_t replace_connect_valset (const val_value_t *valset) { val_value_t *findval, *replaceval; #ifdef DEBUG if (valset == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (connect_valset == NULL) { connect_valset = val_clone(valset); if (connect_valset == NULL) { return ERR_INTERNAL_MEM; } else { return NO_ERR; } } /* go through the connect value set passed in and compare * to the existing connect_valset; only replace the * parameters that are not already set */ for (findval = val_get_first_child(valset); findval != NULL; findval = val_get_next_child(findval)) { replaceval = val_find_child(connect_valset, val_get_mod_name(findval), findval->name); if (replaceval == NULL) { replaceval = val_clone(findval); if (replaceval == NULL) { return ERR_INTERNAL_MEM; } val_add_child(replaceval, connect_valset); } } return NO_ERR; } /* replace_connect_valset */ /******************************************************************** * FUNCTION get_aliasQ * * Get the aliasQ value pointer * * RETURNS: * aliasQ variable *********************************************************************/ dlq_hdr_t * get_aliasQ (void) { return &aliasQ; } /* get_aliasQ */ /******************************************************************** * FUNCTION yangcli_reply_handler * * handle incoming messages * * INPUTS: * scb == session receiving RPC reply * req == original request returned for freeing or reusing * rpy == reply received from the server (for checking then freeing) * * RETURNS: * none *********************************************************************/ void yangcli_reply_handler (ses_cb_t *scb, mgr_rpc_req_t *req, mgr_rpc_rpy_t *rpy) { server_cb_t *server_cb; val_value_t *val; mgr_scb_t *mgrcb; lock_cb_t *lockcb; rpc_err_t rpcerrtyp; status_t res; boolean anyout, anyerrors, done; uint32 usesid; #ifdef DEBUG if (!scb || !req || !rpy) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif res = NO_ERR; mgrcb = scb->mgrcb; if (mgrcb) { usesid = mgrcb->agtsid; ncx_set_temp_modQ(&mgrcb->temp_modQ); } else { usesid = 0; } /*** TBD: multi-session support ***/ server_cb = (server_cb_t*) mgrcb->context_ptr; anyout = FALSE; anyerrors = FALSE; /* check the contents of the reply */ if (rpy && rpy->reply) { if (val_find_child(rpy->reply, NC_MODULE, NCX_EL_RPC_ERROR)) { if (server_cb->command_mode == CMD_MODE_NORMAL || LOGDEBUG) { log_error("\nRPC Error Reply %s for session %u:\n", rpy->msg_id, usesid); val_dump_value_max(rpy->reply, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); log_error("\n"); anyout = TRUE; } if (server_cb->time_rpcs) { handle_rpc_timing(server_cb, req); anyout = TRUE; } anyerrors = TRUE; } else if (val_find_child(rpy->reply, NC_MODULE, NCX_EL_OK)) { if (server_cb->command_mode == CMD_MODE_NORMAL || LOGDEBUG2) { if (server_cb->echo_replies) { log_info("\nRPC OK Reply %s for session %u:\n", rpy->msg_id, usesid); anyout = TRUE; } } if (server_cb->time_rpcs) { handle_rpc_timing(server_cb, req); anyout = TRUE; } } else if ((server_cb->command_mode == CMD_MODE_NORMAL && LOGINFO) || (server_cb->command_mode != CMD_MODE_NORMAL && LOGDEBUG2)) { if (server_cb->echo_replies) { log_info("\nRPC Data Reply %s for session %u:\n", rpy->msg_id, usesid); val_dump_value_max(rpy->reply, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); log_info("\n"); anyout = TRUE; } if (server_cb->time_rpcs) { handle_rpc_timing(server_cb, req); anyout = TRUE; } } else if (server_cb->time_rpcs) { handle_rpc_timing(server_cb, req); anyout = TRUE; } /* output data even if there were errors * TBD: use a CLI switch to control whether * to save if received */ if (server_cb->result_name || server_cb->result_filename) { /* save the data element if it exists */ val = val_first_child_name(rpy->reply, NCX_EL_DATA); if (val) { val_remove_child(val); } else { if (val_child_cnt(rpy->reply) == 1) { val = val_get_first_child(rpy->reply); val_remove_child(val); } else { /* not 1 child node, so save the entire reply * need a single top-level element to be a * valid XML document */ val = rpy->reply; rpy->reply = NULL; } } /* hand off the malloced 'val' node here */ res = finish_result_assign(server_cb, val, NULL); } else if (LOGINFO && !anyout && !anyerrors && server_cb->command_mode == CMD_MODE_NORMAL && interactive_mode()) { log_stdout("\nOK\n"); } } else { log_error("\nError: yangcli: no reply parsed\n"); } /* check if a script is running */ if ((anyerrors || res != NO_ERR) && runstack_level(server_cb->runstack_context)) { runstack_cancel(server_cb->runstack_context); } if (anyerrors && rpy->reply) { rpcerrtyp = get_rpc_error_tag(rpy->reply); } else { /* default error: may not get used */ rpcerrtyp = RPC_ERR_OPERATION_FAILED; } switch (server_cb->state) { case MGR_IO_ST_CONN_CLOSEWAIT: if (mgrcb->closed) { mgr_ses_free_session(server_cb->mysid); server_cb->mysid = 0; server_cb->state = MGR_IO_ST_IDLE; } /* else keep waiting */ break; case MGR_IO_ST_CONN_RPYWAIT: server_cb->state = MGR_IO_ST_CONN_IDLE; switch (server_cb->command_mode) { case CMD_MODE_NORMAL: break; case CMD_MODE_AUTOLOAD: (void)autoload_handle_rpc_reply(server_cb, scb, rpy->reply, anyerrors); break; case CMD_MODE_YANG_LIBRARY: (void)yang_library_handle_rpc_reply(server_cb, scb, rpy->reply, anyerrors); /* re-check now with the yang library module set */ check_module_capabilities(server_cb, scb, autoload_start_get_modules, NULL); break; case CMD_MODE_AUTOLOCK: done = FALSE; lockcb = &server_cb->lock_cb[server_cb->locks_cur_cfg]; if (anyerrors) { if (rpcerrtyp == RPC_ERR_LOCK_DENIED) { lockcb->lock_state = LOCK_STATE_TEMP_ERROR; } else if (lockcb->config_id == NCX_CFGID_CANDIDATE) { res = send_discard_changes_pdu_to_server(server_cb); if (res != NO_ERR) { handle_locks_cleanup(server_cb); } done = TRUE; } else { lockcb->lock_state = LOCK_STATE_FATAL_ERROR; done = TRUE; } } else { lockcb->lock_state = LOCK_STATE_ACTIVE; } if (!done) { res = handle_get_locks_request_to_server(server_cb, FALSE, &done); if (done) { /* check if the locks are all good */ if (get_lock_worked(server_cb)) { log_info("\nget-locks finished OK"); server_cb->command_mode = CMD_MODE_NORMAL; server_cb->locks_waiting = FALSE; } else { log_error("\nError: get-locks failed, " "starting cleanup"); handle_locks_cleanup(server_cb); } } else if (res != NO_ERR) { log_error("\nError: get-locks failed, no cleanup"); } } break; case CMD_MODE_AUTOUNLOCK: lockcb = &server_cb->lock_cb[server_cb->locks_cur_cfg]; if (anyerrors) { lockcb->lock_state = LOCK_STATE_FATAL_ERROR; } else { lockcb->lock_state = LOCK_STATE_RELEASED; } (void)handle_release_locks_request_to_server(server_cb, FALSE, &done); if (done) { clear_lock_cbs(server_cb); } break; case CMD_MODE_AUTODISCARD: lockcb = &server_cb->lock_cb[server_cb->locks_cur_cfg]; server_cb->command_mode = CMD_MODE_AUTOLOCK; if (anyerrors) { handle_locks_cleanup(server_cb); } else { res = handle_get_locks_request_to_server(server_cb, FALSE, &done); if (done) { /* check if the locks are all good */ if (get_lock_worked(server_cb)) { log_info("\nget-locks finished OK"); server_cb->command_mode = CMD_MODE_NORMAL; server_cb->locks_waiting = FALSE; } else { log_error("\nError: get-locks failed, " "starting cleanup"); handle_locks_cleanup(server_cb); } } else if (res != NO_ERR) { log_error("\nError: get-locks failed, no cleanup"); } } break; case CMD_MODE_SAVE: if (anyerrors) { log_warn("\nWarning: Final canceled because " " failed"); } else { (void)finish_save(server_cb); } server_cb->command_mode = CMD_MODE_NORMAL; break; default: SET_ERROR(ERR_INTERNAL_VAL); } break; default: break; } /* free the request and reply */ mgr_rpc_free_request(req); if (rpy) { mgr_rpc_free_reply(rpy); } } /* yangcli_reply_handler */ /******************************************************************** * FUNCTION finish_result_assign * * finish the assignment to result_name or result_filename * use 1 of these 2 parms: * resultval == result to output to file * !!! This is a live var that is freed by this function * * resultstr == result to output as string * * INPUTS: * server_cb == server control block to use * * RETURNS: * status *********************************************************************/ status_t finish_result_assign (server_cb_t *server_cb, val_value_t *resultvar, const xmlChar *resultstr) { val_value_t *configvar; status_t res; #ifdef DEBUG if (server_cb == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; if (resultvar != NULL) { /* force the name strings to be * malloced instead of backptrs to * the object name */ res = val_force_dname(resultvar); if (res != NO_ERR) { val_free_value(resultvar); } } if (res != NO_ERR) { ; } else if (server_cb->result_filename != NULL) { res = output_file_result(server_cb, resultvar, resultstr); if (resultvar) { val_free_value(resultvar); } } else if (server_cb->result_name != NULL) { if (server_cb->result_vartype == VAR_TYP_CONFIG) { configvar = var_get(server_cb->runstack_context, server_cb->result_name, VAR_TYP_CONFIG); if (configvar==NULL) { res = SET_ERROR(ERR_INTERNAL_VAL); } else { res = handle_config_assign(server_cb, configvar, resultvar, resultstr); if (res == NO_ERR) { log_info("\nOK\n"); } } } else if (resultvar != NULL) { /* save the filled in value * hand off the malloced 'resultvar' here */ if (res == NO_ERR) { res = var_set_move(server_cb->runstack_context, server_cb->result_name, xml_strlen(server_cb->result_name), server_cb->result_vartype, resultvar); } if (res != NO_ERR) { /* resultvar is freed even if error returned */ log_error("\nError: set result for '%s' failed (%s)", server_cb->result_name, get_error_string(res)); } else { log_info("\nOK\n"); } } else { /* this is just a string assignment */ res = var_set_from_string(server_cb->runstack_context, server_cb->result_name, resultstr, server_cb->result_vartype); if (res != NO_ERR) { log_error("\nyangcli: Error setting variable %s (%s)", server_cb->result_name, get_error_string(res)); } else { log_info("\nOK\n"); } } } clear_result(server_cb); return res; } /* finish_result_assign */ /******************************************************************** * FUNCTION report_capabilities * * Generate a start session report, listing the capabilities * of the NETCONF server * * INPUTS: * server_cb == server control block to use * scb == session control block * isfirst == TRUE if first call when session established * FALSE if this is from show session command * mode == help mode; ignored unless first == FALSE *********************************************************************/ void report_capabilities (server_cb_t *server_cb, const ses_cb_t *scb, boolean isfirst, help_mode_t mode) { const mgr_scb_t *mscb; const xmlChar *server; const val_value_t *parm; if (!LOGINFO) { /* skip unless log level is INFO or higher */ return; } mscb = (const mgr_scb_t *)scb->mgrcb; parm = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_SERVER); if (parm && parm->res == NO_ERR) { server = VAL_STR(parm); } else { server = (const xmlChar *)"--"; } log_write("\n\nNETCONF session established for %s on %s", scb->username, mscb->target ? mscb->target : server); log_write("\n\nClient Session Id: %u", scb->sid); log_write("\nServer Session Id: %u", mscb->agtsid); if (isfirst || mode > HELP_MODE_BRIEF) { log_write("\n\nServer Protocol Capabilities"); cap_dump_stdcaps(&mscb->caplist); log_write("\n\nServer Module Capabilities"); cap_dump_modcaps(&mscb->caplist); log_write("\n\nServer Enterprise Capabilities"); cap_dump_entcaps(&mscb->caplist); log_write("\n"); } log_write("\nProtocol version set to: "); switch (ses_get_protocol(scb)) { case NCX_PROTO_NETCONF10: log_write("RFC 4741 (base:1.0)"); break; case NCX_PROTO_NETCONF11: log_write("RFC 6241 (base:1.1)"); break; default: log_write("unknown"); } if (!isfirst && (mode <= HELP_MODE_BRIEF)) { return; } log_write("\nDefault target set to: "); switch (mscb->targtyp) { case NCX_AGT_TARG_NONE: if (isfirst) { server_cb->default_target = NULL; } log_write("none"); break; case NCX_AGT_TARG_CANDIDATE: if (isfirst) { server_cb->default_target = NCX_EL_CANDIDATE; } log_write(""); break; case NCX_AGT_TARG_RUNNING: if (isfirst) { server_cb->default_target = NCX_EL_RUNNING; } log_write(""); break; case NCX_AGT_TARG_CAND_RUNNING: if (force_target != NULL && !xml_strcmp(force_target, NCX_EL_RUNNING)) { /* set to running */ if (isfirst) { server_cb->default_target = NCX_EL_RUNNING; } log_write(" ( also supported)"); } else { /* set to candidate */ if (isfirst) { server_cb->default_target = NCX_EL_CANDIDATE; } log_write(" ( also supported)"); } break; case NCX_AGT_TARG_LOCAL: if (isfirst) { server_cb->default_target = NULL; } log_write("none -- local file"); break; case NCX_AGT_TARG_REMOTE: if (isfirst) { server_cb->default_target = NULL; } log_write("none -- remote file"); break; default: if (isfirst) { server_cb->default_target = NULL; } SET_ERROR(ERR_INTERNAL_VAL); log_write("none -- unknown (%d)", mscb->targtyp); break; } log_write("\nSave operation mapped to: "); switch (mscb->targtyp) { case NCX_AGT_TARG_NONE: log_write("none"); break; case NCX_AGT_TARG_CANDIDATE: case NCX_AGT_TARG_CAND_RUNNING: if (!xml_strcmp(server_cb->default_target, NCX_EL_CANDIDATE)) { log_write("commit"); if (mscb->starttyp == NCX_AGT_START_DISTINCT) { log_write(" + copy-config "); } } else { if (mscb->starttyp == NCX_AGT_START_DISTINCT) { log_write("copy-config "); } else { log_write("none"); } } break; case NCX_AGT_TARG_RUNNING: if (mscb->starttyp == NCX_AGT_START_DISTINCT) { log_write("copy-config "); } else { log_write("none"); } break; case NCX_AGT_TARG_LOCAL: case NCX_AGT_TARG_REMOTE: /* no way to assign these enums from the capabilities alone! */ if (cap_std_set(&mscb->caplist, CAP_STDID_URL)) { log_write("copy-config "); } else { log_write("none"); } break; default: SET_ERROR(ERR_INTERNAL_VAL); log_write("none"); break; } log_write("\nDefault with-defaults behavior: "); if (mscb->caplist.cap_defstyle) { log_write("%s", mscb->caplist.cap_defstyle); } else { log_write("unknown"); } log_write("\nAdditional with-defaults behavior: "); if (mscb->caplist.cap_supported) { log_write("%s", mscb->caplist.cap_supported); } else { log_write("unknown"); } log_write("\n"); } /* report_capabilities */ /******************************************************************** * * * FUNCTION main * * * *********************************************************************/ #ifdef SKIP_MAIN static int main2 (int argc, char *argv[]) #else int main (int argc, char *argv[]) #endif { status_t res; #ifdef MEMORY_DEBUG mtrace(); #endif res = yangcli_init(argc, argv); init_done = (res == NO_ERR) ? TRUE : FALSE; if (res != NO_ERR) { log_error("\nyangcli: init returned error (%s)\n", get_error_string(res)); } else if (!(helpmode || versionmode)) { /* normal run mode */ res = mgr_io_run(); if (res != NO_ERR) { log_error("\nmgr_io failed (%d)\n", res); } else { log_write("\n"); } } print_errors(); print_error_count(); yangcli_cleanup(); print_error_count(); #ifdef MEMORY_DEBUG muntrace(); #endif return 0; } /* main */ /* END yangcli.c */ yuma123_2.14/netconf/src/yangcli/README0000664000175000017500000001371314770023131017661 0ustar vladimirvladimiryangcli README version 1 Overview Simple single-threaded, single-session NETCONF client Provides CLI interface to NETCONF operations for testing purposes Interactive mode provides a basic CLI interface for NETCONF operations. Commands are defined in ncxcli.ncx as RPC methods. Commands in yangcli are defined with the 'rpc' construct in YANG data models. NETCONF operations defined in netconf.yang (rpc constructs) are available as commands when connected to a server. Local commands available at any time are defined in yangcli.yang. Any RPC method defined in any NCX module is available as a command when connected to a server. Script mode provides a basic script environment. A script can be invoked with the 'run script=script-name' command. Scripts can call other scripts via the same mechanism, but cannot be called recusively (yet). Script parameters are hard-wired to be named P1 .. P9. These parameters are accessed within the script using the syntax $1, $2, etc. run script=test-interface P1=eth0 P2=300 P3="my itf test" Interactive and CLI syntax is virtually the same Interactive CLI mode is script level 0. There are 2 types of statements: - Assignment statements - Commands An assignment statement assigns some value to a user variable. There are 2 user variable namespaces: - global (visible to all scripts and interactive CLI mode) - local (visible just to the current script run level) An assignment statement for a local variable has the form: $foo = assign-expression An assignment expression can span multiple lines if each line except the last ends with a backslash '\' char, followed by a newline '\n' char. Lines within a quoted string do not need to be escaped in this manner. Double quoted or single quoted strings are permitted. An assignment statement for a global variable has the form: $$foo = assign-expression An assignment expression has several forms: # Note: all examples could be local ($foo) or global ($$foo) # set to the contents of another var $foo = $bar # set to the results of an RPC operation $foo = operation-expresion # set to the contents of an NCX_BT_STRING quoted string $foo = "quoted string literal" # set an NCX_BT_INTERNAL raw XML variable # The 2 char sequence [< must start the raw XML value # The 2 char sequence >] must end the raw XML value $foo = [ \ \ ] $filter = [] # set an NCX_BT_EXTERNAL var # to the contents of an XML data file $foo = @somedata.xml A command expression has the form [module-prefix:]command-name [parm]* There cannot be any whitespace between the module-prefix, colon, and command name. If the module prefix is present, then only RPC methods from the associated module will be checked. acme:reset-subsystem reset-target="/foo/bar[i=23]" If the module prefix is not present, then the default-module will be used instead first, and then (if connected) the server RPC methods will be checked. edit-config test-option=test-only config=$myconfig \ error-option=stop-on-error default-operation=none If present, then only the module associated with that prefix string is used to look up definitions in the registry. A parm has several forms: - string literal (quoted or unquoted) - var reference ($bar or $$bar) - extern reference (@data.xml) - inline XML ([]) Parameter prefix chars are optional. Zero to two dashes are permitted. For example, all of these forms below are permitted to supply the RPC parameter foo with the value 7 A string data type is used in this example: foo=7 -foo=7 --foo=7 foo 7 -foo 7 --foo 7 --foo "7" Whitespace is permitted before and after the equals sign, if used. It is also permitted before and after commands. Each command line translates to one RPC operation. A 'command line' starts with an RPC method as described above, and are terminated with an EOLN Multiple user or script file lines are concatenated to form one conceptual command line, if the EOLN is preceded by the backslash char: edit-config \ default-operation=none \ config=@myconfig.xml \ error-option=rollback-on-error Multi-line commands must use the escape-EOLN sequence to indicate line continuation. No whitespace is added during line-splicing Comment lines are any lines that start with zero or more whitespace chars, followed by a hash char '#'. The rest of the input line, up to but not including the EOLN char, is skipped. A comment will terminate a multi-line command, if embedded within the command. # this is a multi-line edit-config operation edit-config target=running \ default-operation=none \ test-option=test-then-set \ error-option=continue-on-error \ config=@test-config.xml Local user variables are specified with a dollar sign '$' followed by a valid NcxName string. Local variables must be unique within the script that they are defined. Global user variables are specified with 2 dollar signs '$$' followed by a valid NcxName string. Global variables are saved in a shared database, and exist after the script ends (like environment vars). Script parameters are specified with 1 dollar sign '$' followed by a number from 1 to 9. # typed from the command line or within a script run script=connect-script P1=myserver.example.com # script connect using global user and password connect user=$$username password=$$pass server=$1 yuma123_2.14/netconf/src/yangcli/yangcli_yang_library.h0000664000175000017500000000330614770023131023337 0ustar vladimirvladimir/* * Copyright (c) 2017, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "ses.h" #include "status.h" #include "yangcli.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ extern status_t yang_library_start_get_module_set (server_cb_t *server_cb, ses_cb_t *scb); extern status_t yang_library_handle_rpc_reply (server_cb_t *server_cb, ses_cb_t *scb, val_value_t *reply, boolean anyerrors); status_t make_get_yang_library_modules_state_reqdata(server_cb_t *server_cb, ses_cb_t *scb, obj_template_t** out_rpc, val_value_t** out_reqdata); status_t get_yang_library_modules_state_reply_to_searchresult_entries(server_cb_t * server_cb, ses_cb_t *scb, val_value_t* reply); status_t get_yang_library_modules_state_reply_to_searchresult_entries(server_cb_t * server_cb, ses_cb_t *scb, val_value_t* reply); #ifdef __cplusplus } /* end extern 'C' */ #endif yuma123_2.14/netconf/src/yangcli/yangcli_eval.c0000664000175000017500000002627214770023131021606 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_eval.c NETCONF YANG-based CLI Tool XPath evaluation support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13-aug-09 abb begun; started from yangcli_cmd.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_num #include "ncx_num.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifndef _H_var #include "var.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xpath1 #include "xpath1.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifndef _H_yangcli_eval #include "yangcli_eval.h" #endif #ifndef _H_yangcli_cmd #include "yangcli_cmd.h" #endif #ifndef _H_yangcli_util #include "yangcli_util.h" #endif /******************************************************************** * FUNCTION convert_result_to_val * * Convert an Xpath result to a val_value_t struct * representing the rpc output in xpath-result-group * in yuma-xpath.yang * * INPUTS: * result == XPath result to convert * * RETURNS: * malloced and filled out value node *********************************************************************/ static val_value_t * convert_result_to_val (xpath_result_t *result) { val_value_t *resultval, *childval; xpath_resnode_t *resnode; xmlns_id_t ncid; resultval = NULL; ncid = xmlns_nc_id(); switch (result->restype) { case XP_RT_NONE: /* this should be an error, but treat as an empty result */ resultval = xml_val_new_flag(NCX_EL_DATA, ncid); break; case XP_RT_NODESET: if (dlq_empty(&result->r.nodeQ)) { resultval = xml_val_new_flag(NCX_EL_DATA, ncid); } else { resultval = xml_val_new_struct(NCX_EL_DATA, ncid); for (resnode = (xpath_resnode_t *) dlq_firstEntry(&result->r.nodeQ); resnode != NULL; resnode = (xpath_resnode_t *) dlq_nextEntry(resnode)) { childval = val_clone(resnode->node.valptr); if (childval == NULL) { val_free_value(resultval); return NULL; } val_add_child(childval, resultval); } } break; case XP_RT_NUMBER: resultval = xml_val_new_number(NCX_EL_DATA, ncid, &result->r.num, NCX_BT_FLOAT64); break; case XP_RT_STRING: resultval = xml_val_new_cstring(NCX_EL_DATA, ncid, result->r.str); break; case XP_RT_BOOLEAN: /* !!! this is wrong but not sure if any XML conversion used it !!! * contains empty string if FALSE; if TUR *resultval = xml_val_new_boolean(NCX_EL_DATA, ncid, result->r.boo); */ resultval = xml_val_new_cstring(NCX_EL_DATA, ncid, (result->r.boo) ? NCX_EL_TRUE : NCX_EL_FALSE); break; default: SET_ERROR(ERR_INTERNAL_VAL); } return resultval; } /* convert_result_to_val */ /******************************************************************** * FUNCTION do_eval (local RPC) * * eval select='expr' docroot=$myvar * * Evaluate the XPath expression * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the exec command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_eval (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *docroot, *expr; val_value_t *resultval, *dummydoc, *childval; xpath_pcb_t *pcb; xpath_result_t *result; xmlChar *resultstr; status_t res; uint32 childcnt; docroot = NULL; expr = NULL; pcb = NULL; result = NULL; resultval = NULL; resultstr = NULL; dummydoc = NULL; res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (valset == NULL) { return res; } if (res != NO_ERR) { val_free_value(valset); return res; } if (valset->res != NO_ERR) { res = valset->res; val_free_value(valset); return res; } /* get the expr parameter */ expr = val_find_child(valset, YANGCLI_MOD, YANGCLI_EXPR); if (expr == NULL) { res = ERR_NCX_MISSING_PARM; } else if (expr->res != NO_ERR) { res = expr->res; } if (res == NO_ERR) { /* get the optional docroot parameter */ docroot = val_find_child(valset, YANGCLI_MOD, YANGCLI_DOCROOT); if (docroot && docroot->res != NO_ERR) { res = docroot->res; } } if (res == NO_ERR && docroot == NULL) { dummydoc = xml_val_new_struct(NCX_EL_DATA, xmlns_nc_id()); if (dummydoc == NULL) { res = ERR_INTERNAL_MEM; } else { docroot = dummydoc; } } if (res == NO_ERR) { /* got all the parameters, and setup the XPath control block */ pcb = xpath_new_pcb_ex(VAL_STR(expr), xpath_getvar_fn, server_cb->runstack_context); if (pcb == NULL) { res = ERR_INTERNAL_MEM; } else { /* try to parse the XPath expression */ result = xpath1_eval_expr(pcb, docroot, /* context */ docroot, TRUE, FALSE, &res); if (result != NULL && res == NO_ERR) { /* create a result val out of the XPath result */ resultval = convert_result_to_val(result); if (resultval == NULL) { res = ERR_INTERNAL_MEM; } } } } /* check save result or clear it */ if (res == NO_ERR) { if (server_cb->result_name || server_cb->result_filename) { /* decide if the saved variable should be * a string or a element * * Convert to string: * is a simple type * contains 1 node that is a simple type * * Remove the container: * contains a single complex element * * Retain the container: * contains multiple elements */ if (typ_is_simple(resultval->btyp)) { resultstr = val_make_sprintf_string(resultval); if (resultstr == NULL) { res = ERR_INTERNAL_MEM; } else { val_free_value(resultval); resultval = NULL; } } else { childcnt = val_child_cnt(resultval); if (childcnt == 1) { /* check if the child is a simple * type or complex type */ childval = val_get_first_child(resultval); if (childval == NULL) { res = SET_ERROR(ERR_INTERNAL_VAL); } else if (typ_is_simple(childval->btyp)) { /* convert the simple val to a string */ resultstr = val_make_sprintf_string(childval); if (resultstr == NULL) { res = ERR_INTERNAL_MEM; } else { val_free_value(resultval); resultval = NULL; } } else { /* remove the complex node from the * data container */ val_remove_child(childval); val_free_value(resultval); resultval = childval; } } else { /* either 0 or 2+ entries so leave the * data container in place */ ; } } if (res == NO_ERR) { /* save the filled in value */ res = finish_result_assign(server_cb, resultval, resultstr); /* resultvar is freed no matter what */ resultval = NULL; } } } else { clear_result(server_cb); } /* cleanup and exit */ val_free_value(valset); xpath_free_pcb(pcb); xpath_free_result(result); val_free_value(resultval); if (resultstr) { m__free(resultstr); } val_free_value(dummydoc); if (res != NO_ERR) { log_error("\nError: XPath evaluation failed (%s)", get_error_string(res)); } return res; } /* do_eval */ /* END yangcli_eval.c */ yuma123_2.14/netconf/src/yangcli/yangcli_list.c0000664000175000017500000004240214770023131021623 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_list.c NETCONF YANG-based CLI Tool list command ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13-aug-09 abb begun; started from yangcli_cmd.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_mgr #include "mgr.h" #endif #ifndef _H_mgr_ses #include "mgr_ses.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_op #include "op.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifndef _H_var #include "var.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifndef _H_yangcli_list #include "yangcli_list.h" #endif #ifndef _H_yangcli_cmd #include "yangcli_cmd.h" #endif #ifndef _H_yangcli_util #include "yangcli_util.h" #endif /******************************************************************** * FUNCTION do_list_one_oid (sub-mode of list oids RPC) * * list oids: 1 of N * * INPUTS: * obj == the object to use * help_mode == verbosity level to use * * RETURNS: * status *********************************************************************/ static status_t do_list_one_oid (obj_template_t *obj, help_mode_t help_mode) { xmlChar *buffer; boolean imode; status_t res; res = NO_ERR; if (obj_is_data_db(obj) && obj_has_name(obj) && !obj_is_hidden(obj) && !obj_is_abstract(obj)) { imode = interactive_mode(); buffer = NULL; res = obj_gen_object_id(obj, &buffer); if (res != NO_ERR) { log_error("\nError: list OID failed (%s)", get_error_string(res)); } else if (help_mode == HELP_MODE_FULL) { if (imode) { log_stdout("\n %s %s", obj_get_typestr(obj), buffer); } else { log_write("\n %s %s", obj_get_typestr(obj), buffer); } } else { if (imode) { log_stdout("\n %s", buffer); } else { log_write("\n %s", buffer); } } if (buffer) { m__free(buffer); } } return res; } /* do_list_one_oid */ /******************************************************************** * FUNCTION do_list_oid (sub-mode of local RPC) * * list oids * * INPUTS: * obj == object to use * nestlevel to stop at * help_mode == verbosity level to use * * RETURNS: * status *********************************************************************/ static status_t do_list_oid (obj_template_t *obj, uint32 level, help_mode_t help_mode) { obj_template_t *chobj; status_t res; res = NO_ERR; if (obj_get_level(obj) <= level) { res = do_list_one_oid(obj, help_mode); for (chobj = obj_first_child(obj); chobj != NULL && res == NO_ERR; chobj = obj_next_child(chobj)) { res = do_list_oid(chobj, level, help_mode); } } return res; } /* do_list_oid */ /******************************************************************** * FUNCTION do_list_oids (sub-mode of local RPC) * * list oids * * INPUTS: * server_cb == server control block to use * mod == the 1 module to use * NULL to use all available modules instead * mode == requested help mode * * RETURNS: * status *********************************************************************/ static status_t do_list_oids (server_cb_t *server_cb, ncx_module_t *mod, help_mode_t mode) { modptr_t *modptr; obj_template_t *obj; boolean imode; uint32 level; status_t res; res = NO_ERR; switch (mode) { case HELP_MODE_NONE: return res; case HELP_MODE_BRIEF: level = 3; break; case HELP_MODE_NORMAL: level = 10; break; case HELP_MODE_FULL: level = 999; break; default: return SET_ERROR(ERR_INTERNAL_VAL); } imode = interactive_mode(); if (mod) { obj = ncx_get_first_object(mod); while (obj && res == NO_ERR) { res = do_list_oid(obj, level, mode); obj = ncx_get_next_object(mod, obj); } } else if (use_servercb(server_cb)) { for (modptr = (modptr_t *) dlq_firstEntry(&server_cb->modptrQ); modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { obj = ncx_get_first_object(modptr->mod); while (obj && res == NO_ERR) { res = do_list_oid(obj, level, mode); obj = ncx_get_next_object(modptr->mod, obj); } } } else { return res; } if (imode) { log_stdout("\n"); } else { log_write("\n"); } return res; } /* do_list_oids */ /******************************************************************** * FUNCTION do_list_one_command (sub-mode of list command RPC) * * list commands: 1 of N * * INPUTS: * obj == object template for the RPC command to use * mode == requested help mode * * RETURNS: * status *********************************************************************/ static status_t do_list_one_command (obj_template_t *obj, help_mode_t mode) { if (interactive_mode()) { if (mode == HELP_MODE_BRIEF) { log_stdout("\n %s", obj_get_name(obj)); } else if (mode == HELP_MODE_NORMAL) { log_stdout("\n %s:%s", obj_get_mod_xmlprefix(obj), obj_get_name(obj)); } else { log_stdout("\n %s:%s", obj_get_mod_name(obj), obj_get_name(obj)); } } else { if (mode == HELP_MODE_BRIEF) { log_write("\n %s", obj_get_name(obj)); } else if (mode == HELP_MODE_NORMAL) { log_write("\n %s:%s", obj_get_mod_xmlprefix(obj), obj_get_name(obj)); } else { log_write("\n %s:%s", obj_get_mod_name(obj), obj_get_name(obj)); } } return NO_ERR; } /* do_list_one_command */ /******************************************************************** * FUNCTION do_list_objects (sub-mode of local RPC) * * list objects * * INPUTS: * server_cb == server control block to use * mod == the 1 module to use * NULL to use all available modules instead * mode == requested help mode * * RETURNS: * status *********************************************************************/ static status_t do_list_objects (server_cb_t *server_cb, ncx_module_t *mod, help_mode_t mode) { modptr_t *modptr; obj_template_t *obj; logfn_t logfn; boolean anyout; status_t res; res = NO_ERR; anyout = FALSE; if (interactive_mode()) { logfn = log_stdout; } else { logfn = log_write; } if (mod) { obj = ncx_get_first_object(mod); while (obj && res == NO_ERR) { if (obj_is_data_db(obj) && obj_has_name(obj) && !obj_is_hidden(obj) && !obj_is_abstract(obj)) { anyout = TRUE; res = do_list_one_command(obj, mode); } obj = ncx_get_next_object(mod, obj); } } else { if (use_servercb(server_cb)) { for (modptr = (modptr_t *) dlq_firstEntry(&server_cb->modptrQ); modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { obj = ncx_get_first_object(modptr->mod); while (obj && res == NO_ERR) { if (obj_is_data_db(obj) && obj_has_name(obj) && !obj_is_hidden(obj) && !obj_is_abstract(obj)) { anyout = TRUE; res = do_list_one_command(obj, mode); } obj = ncx_get_next_object(modptr->mod, obj); } } } } if (!anyout) { (*logfn)("\nNo objects found to list"); } (*logfn)("\n"); return res; } /* do_list_objects */ /******************************************************************** * FUNCTION do_list_commands (sub-mode of local RPC) * * list commands * * INPUTS: * server_cb == server control block to use * mod == the 1 module to use * NULL to use all available modules instead * mode == requested help mode * * RETURNS: * status *********************************************************************/ static status_t do_list_commands (server_cb_t *server_cb, ncx_module_t *mod, help_mode_t mode) { modptr_t *modptr; obj_template_t *obj; logfn_t logfn; boolean imode, anyout; status_t res; res = NO_ERR; imode = interactive_mode(); if (imode) { logfn = log_stdout; } else { logfn = log_write; } if (mod) { anyout = FALSE; obj = ncx_get_first_object(mod); while (obj && res == NO_ERR) { if (obj_is_rpc(obj)) { res = do_list_one_command(obj, mode); anyout = TRUE; } obj = ncx_get_next_object(mod, obj); } if (!anyout) { (*logfn)("\nNo commands found in module '%s'", mod->name); } } else { if (use_servercb(server_cb)) { (*logfn)("\nServer Commands:"); for (modptr = (modptr_t *) dlq_firstEntry(&server_cb->modptrQ); modptr != NULL && res == NO_ERR; modptr = (modptr_t *)dlq_nextEntry(modptr)) { obj = ncx_get_first_object(modptr->mod); while (obj) { if (obj_is_rpc(obj)) { res = do_list_one_command(obj, mode); } obj = ncx_get_next_object(modptr->mod, obj); } } } (*logfn)("\n\nLocal Commands:"); obj = ncx_get_first_object(get_yangcli_mod()); while (obj && res == NO_ERR) { if (obj_is_rpc(obj)) { if (use_servercb(server_cb)) { /* list a local command */ res = do_list_one_command(obj, mode); } else { /* session not active so filter out * all the commands except top command */ if (is_top_command(obj_get_name(obj))) { res = do_list_one_command(obj, mode); } } } obj = ncx_get_next_object(get_yangcli_mod(), obj); } } (*logfn)("\n"); return res; } /* do_list_commands */ /******************************************************************** * FUNCTION do_list (local RPC) * * list objects [module=mod-name] * ids * commands * * List the specified information based on the parameters * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_list (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *parm; ncx_module_t *mod; status_t res; boolean imode, done; help_mode_t mode; mod = NULL; done = FALSE; res = NO_ERR; imode = interactive_mode(); valset = get_valset(server_cb, rpc, &line[len], &res); if (valset && res == NO_ERR) { mode = HELP_MODE_NORMAL; /* check if the 'brief' flag is set first */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_BRIEF); if (parm && parm->res == NO_ERR) { mode = HELP_MODE_BRIEF; } else { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_FULL); if (parm && parm->res == NO_ERR) { mode = HELP_MODE_FULL; } } parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_MODULE); if (parm && parm->res == NO_ERR) { mod = find_module(server_cb, VAL_STR(parm)); if (!mod) { if (imode) { log_stdout("\nError: no module found named '%s'", VAL_STR(parm)); } else { log_write("\nError: no module found named '%s'", VAL_STR(parm)); } done = TRUE; } } /* find the 1 of N choice */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_COMMANDS); if (parm) { /* do list commands */ res = do_list_commands(server_cb, mod, mode); done = TRUE; } } if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_FILES); if (parm) { /* list the data files */ res = ncxmod_list_data_files(mode, imode); done = TRUE; } } if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_OBJECTS); if (parm) { /* do list objects */ res = do_list_objects(server_cb, mod, mode); done = TRUE; } } if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_OIDS); if (parm) { /* do list oids */ res = do_list_oids(server_cb, mod, mode); done = TRUE; } } if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_MODULES); if (parm) { /* list the YANG files */ res = ncxmod_list_yang_files(mode, imode); done = TRUE; } } if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_SCRIPTS); if (parm) { /* list the script files */ res = ncxmod_list_script_files(mode, imode); done = TRUE; } } } if (valset) { val_free_value(valset); } return res; } /* do_list */ /* END yangcli_list.c */ yuma123_2.14/netconf/src/yangcli/yangcli_timer.c0000664000175000017500000002604014770023131021770 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_timer.c NETCONF YANG-based CLI Tool timers support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 26-may-11 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_mgr #include "mgr.h" #endif #ifndef _H_mgr_ses #include "mgr_ses.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_feature #include "ncx_feature.h" #endif #ifndef _H_ncx_list #include "ncx_list.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_op #include "op.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_rpc_err #include "rpc_err.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifndef _H_var #include "var.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifndef _H_yangcli_autoload #include "yangcli_autoload.h" #endif #ifndef _H_yangcli_cmd #include "yangcli_cmd.h" #endif #ifndef _H_yangcli_timer #include "yangcli_timer.h" #endif #ifndef _H_yangcli_util #include "yangcli_util.h" #endif #ifdef DEBUG #define YANGCLI_TIMER_DEBUG 1 #endif /* macros from http://linux.die.net/man/2/gettimeofday */ #define timerisset(tvp)\ ((tvp)->tv_sec || (tvp)->tv_usec) #define timer_clear(tvp)\ ((tvp)->tv_sec = (tvp)->tv_usec = 0) /******************************************************************** * FUNCTION timer_running * * Check if a timer is running * * INPUTS: * server_cb == server control block to use * timer_id == timer to use * * RETURNS: * TRUE if timer running; FALSE otherwise *********************************************************************/ static boolean timer_running (server_cb_t *server_cb, uint8 timer_id) { return (timerisset(&server_cb->timers[timer_id])) ? TRUE : FALSE; } /* timer_running */ /******************************************************************** * FUNCTION set_timer * * Set timer to now * * INPUTS: * server_cb == server control block to use * timer_id == timer to use *********************************************************************/ static void set_timer (server_cb_t *server_cb, uint8 timer_id) { gettimeofday(&server_cb->timers[timer_id], NULL); } /* set_timer */ /******************************************************************** * FUNCTION clear_timer * * Clear a timer * * INPUTS: * server_cb == server control block to use * timer_id == timer to use * *********************************************************************/ static void clear_timer (server_cb_t *server_cb, uint8 timer_id) { timer_clear(&server_cb->timers[timer_id]); } /* clear_timer */ /******************************************************************** * FUNCTION clear_timer * * Clear a timer * * INPUTS: * server_cb == server control block to use * timer_id == timer to use * result == address of return timer value * OUTPUTS: * *result filled in with timer value *********************************************************************/ static void get_timer (server_cb_t *server_cb, uint8 timer_id, struct timeval *result) { *result = server_cb->timers[timer_id]; } /* get_timer */ /******************************************************************** * FUNCTION yangcli_timer_start (local RPC) * * timer-start [timernum] [restart-ok=false] * * Start the specified performance timer * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t yangcli_timer_start (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *parm; status_t res; boolean restart_ok; uint8 timer_id; res = NO_ERR; timer_id = 0; restart_ok = FALSE; valset = get_valset(server_cb, rpc, &line[len], &res); /* check if the 'id' field is set */ if (res == NO_ERR) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_ID); if (parm && parm->res == NO_ERR) { timer_id = VAL_UINT8(parm); } else { log_error("\nError: missing 'id' parameter"); res = ERR_NCX_MISSING_PARM; } } /* check if the 'restart-ok' field is set */ if (res == NO_ERR) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_RESTART_OK); if (parm && parm->res == NO_ERR) { restart_ok = VAL_BOOL(parm); } else { restart_ok = TRUE; } } if (res == NO_ERR) { if (!restart_ok && timer_running(server_cb, timer_id)) { log_error("\nError: timer '%u' is already running", timer_id); res = ERR_NCX_IN_USE; } } if (res == NO_ERR) { set_timer(server_cb, timer_id); log_info("\nOK\n"); } if (valset != NULL) { val_free_value(valset); } return res; } /* yangcli_timer_start */ /******************************************************************** * FUNCTION yangcli_timer_stop (local RPC) * * delta = timer-stop [timernum] [echo=false] * * Start the specified performance timer * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t yangcli_timer_stop (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *parm; obj_template_t *obj; struct timeval now, timerval; status_t res; long int sec, usec; boolean imode, echo; xmlChar numbuff[NCX_MAX_NUMLEN]; uint8 timer_id; gettimeofday(&now, NULL); timer_id = 0; res = NO_ERR; imode = interactive_mode(); valset = get_valset(server_cb, rpc, &line[len], &res); if (res == NO_ERR) { obj = obj_find_child(rpc, obj_get_mod_name(rpc), YANG_K_OUTPUT); if (obj == NULL) { res = SET_ERROR(ERR_INTERNAL_VAL); } else { obj = obj_find_child(obj, obj_get_mod_name(obj), YANGCLI_DELTA); if (obj == NULL) { res = SET_ERROR(ERR_INTERNAL_VAL); } } } /* check if the 'id' field is set */ if (res == NO_ERR) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_ID); if (parm && parm->res == NO_ERR) { timer_id = VAL_UINT8(parm); } else { log_error("\nError: missing 'id' parameter"); res = ERR_NCX_MISSING_PARM; } } /* make sure timer is running */ if (res == NO_ERR) { if (!timer_running(server_cb, timer_id)) { log_error("\nError: timer '%u' is not running", timer_id); res = ERR_NCX_OPERATION_FAILED; } } if (res == NO_ERR) { get_timer(server_cb, timer_id, &timerval); /* check if the 'echo' field is set */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_ECHO); if (parm && parm->res == NO_ERR) { echo = VAL_BOOL(parm); } else { echo = TRUE; } /* get the resulting delta */ if (now.tv_usec < timerval.tv_usec) { now.tv_usec += 1000000; now.tv_sec--; } sec = now.tv_sec - timerval.tv_sec; usec = now.tv_usec - timerval.tv_usec; sprintf((char *)numbuff, "%ld.%06ld", sec, usec); if (echo) { if (imode) { log_stdout("\nTimer %u value: %s seconds\n", timer_id, numbuff); if (log_get_logfile() != NULL) { log_write("\nTimer %u value: %s seconds\n", timer_id, numbuff); } } else { log_write("\nTimer %u value: %s seconds\n", timer_id, numbuff); } } if (server_cb->local_result != NULL) { log_debug3("\nDeleting old local result"); val_free_value(server_cb->local_result); } server_cb->local_result = val_make_simval_obj(obj, numbuff, &res); if (res != NO_ERR) { log_error("\nError: set value failed (%s)", get_error_string(res)); } } if (valset != NULL) { val_free_value(valset); } clear_timer(server_cb, timer_id); return res; } /* yangcli_timer_stop */ /* END yangcli_timer.c */ yuma123_2.14/netconf/src/yangcli/yangcli_util.h0000664000175000017500000003227214770023131021636 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangcli_util #define _H_yangcli_util /* FILE: yangcli_util.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 27-mar-09 abb Begun; moved from yangcli.c */ #ifndef _H_help #include "help.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifndef _H_var #include "var.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION is_top_command * * Check if command name is a top command * Must be full name * * INPUTS: * rpcname == command name to check * * RETURNS: * TRUE if this is a top command * FALSE if not *********************************************************************/ extern boolean is_top_command (const xmlChar *rpcname); /******************************************************************** * FUNCTION new_modptr * * Malloc and init a new module pointer block * * INPUTS: * mod == module to cache in this struct * malloced == TRUE if mod is malloced * FALSE if mod is a back-ptr * feature_list == feature list from capability * deviation_list = deviations list from capability * * RETURNS: * malloced modptr_t struct or NULL of malloc failed *********************************************************************/ extern modptr_t * new_modptr (ncx_module_t *mod, ncx_list_t *feature_list, ncx_list_t *deviation_list); /******************************************************************** * FUNCTION free_modptr * * Clean and free a module pointer block * * INPUTS: * modptr == mod pointer block to free * MUST BE REMOVED FROM ANY Q FIRST * *********************************************************************/ extern void free_modptr (modptr_t *modptr); /******************************************************************** * FUNCTION find_modptr * * Find a specified module name * * INPUTS: * modptrQ == Q of modptr_t to check * modname == module name to find * revision == module revision (may be NULL) * * RETURNS: * pointer to found entry or NULL if not found * *********************************************************************/ extern modptr_t * find_modptr (dlq_hdr_t *modptrQ, const xmlChar *modname, const xmlChar *revision); /******************************************************************** * FUNCTION clear_server_cb_session * * Clean the current session data from an server control block * * INPUTS: * server_cb == control block to use for clearing * the session data *********************************************************************/ extern void clear_server_cb_session (server_cb_t *server_cb); /******************************************************************** * FUNCTION is_top * * Check the state and determine if the top or conn * mode is active * * INPUTS: * server state to use * * RETURNS: * TRUE if this is TOP mode * FALSE if this is CONN mode (or associated states) *********************************************************************/ extern boolean is_top (mgr_io_state_t state); /******************************************************************** * FUNCTION use_servercb * * Check if the server_cb should be used for modules right now * * INPUTS: * server_cb == server control block to check * * RETURNS: * TRUE to use server_cb * FALSE if not *********************************************************************/ extern boolean use_servercb (server_cb_t *server_cb); /******************************************************************** * FUNCTION find_module * * Check the server_cb for the specified module; if not found * then try ncx_find_module * * INPUTS: * server_cb == control block to free * modname == module name * * RETURNS: * pointer to the requested module * using the registered 'current' version * NULL if not found *********************************************************************/ extern ncx_module_t * find_module (server_cb_t *server_cb, const xmlChar *modname); /******************************************************************** * FUNCTION get_strparm * * Get the specified string parm from the parmset and then * make a strdup of the value * * INPUTS: * valset == value set to check if not NULL * modname == module defining parmname * parmname == name of parm to get * * RETURNS: * pointer to string !!! THIS IS A MALLOCED COPY !!! *********************************************************************/ extern xmlChar * get_strparm (val_value_t *valset, const xmlChar *modname, const xmlChar *parmname); /******************************************************************** * FUNCTION findparm * * Get the specified string parm from the parmset and then * make a strdup of the value * * INPUTS: * valset == value set to search * modname == optional module name defining the parameter to find * parmname == name of parm to get, or partial name to get * * RETURNS: * pointer to val_value_t if found *********************************************************************/ extern val_value_t * findparm (val_value_t *valset, const xmlChar *modname, const xmlChar *parmname); /******************************************************************** * FUNCTION add_clone_parm * * Create a parm * * INPUTS: * val == value to clone and add * valset == value set to add parm into * * RETURNS: * status *********************************************************************/ extern status_t add_clone_parm (const val_value_t *val, val_value_t *valset); /******************************************************************** * FUNCTION is_yangcli_ns * * Check the namespace and make sure this is an YANGCLI command * * INPUTS: * ns == namespace ID to check * * RETURNS: * TRUE if this is the YANGCLI namespace ID *********************************************************************/ extern boolean is_yangcli_ns (xmlns_id_t ns); /******************************************************************** * FUNCTION clear_result * * clear out the pending result info * * INPUTS: * server_cb == server control block to use * *********************************************************************/ extern void clear_result (server_cb_t *server_cb); /******************************************************************** * FUNCTION check_filespec * * Check the filespec string for a file assignment statement * Save it if it si good * * INPUTS: * server_cb == server control block to use * filespec == string to check * varname == variable name to use in log_error * if this is complex form * * OUTPUTS: * server_cb->result_filename will get set if NO_ERR * * RETURNS: * status *********************************************************************/ extern status_t check_filespec (server_cb_t *server_cb, const xmlChar *filespec, const xmlChar *varname); /******************************************************************** * FUNCTION get_instanceid_parm * * Validate an instance identifier parameter * Return the target object * Return a value struct from root containing * all the predicate assignments in the stance identifier * * INPUTS: * target == XPath expression for the instance-identifier * schemainst == TRUE if ncx:schema-instance string * FALSE if instance-identifier * configonly == TRUE if there should be an error given * if the target does not point to a config node * FALSE if the target can be config false * targobj == address of return target object for this expr * targval == address of return pointer to target value * node within the value subtree returned * retres == address of return status * * OUTPUTS: * *targobj == the object template for the target * *targval == the target node within the returned subtree * from root * *retres == return status for the operation * * RETURNS: * If NO_ERR: * malloced value node representing the instance-identifier * from root to the targobj * else: * NULL, check *retres *********************************************************************/ extern val_value_t * get_instanceid_parm (const xmlChar *target, boolean schemainst, boolean configonly, obj_template_t **targobj, val_value_t **targval, status_t *retres); /******************************************************************** * FUNCTION get_file_result_format * * Check the filespec string for a file assignment statement * to see if it is text, XML, or JSON * * INPUTS: * filespec == string to check * * RETURNS: * result format enumeration; RF_NONE if some error *********************************************************************/ extern result_format_t get_file_result_format (const xmlChar *filespec); /******************************************************************** * FUNCTION interactive_mode * * Check if the program is in interactive mode * * RETURNS: * TRUE if insteractive mode, FALSE if batch mode *********************************************************************/ extern boolean interactive_mode (void); /******************************************************************** * FUNCTION init_completion_state * * init the completion_state struct for a new command * * INPUTS: * completion_state == record to initialize * server_cb == server control block to use * cmdstate ==initial calling state *********************************************************************/ extern void init_completion_state (completion_state_t *completion_state, server_cb_t *server_cb, command_state_t cmdstate); /******************************************************************** * FUNCTION set_completion_state * * set the completion_state struct for a new mode or sub-command * * INPUTS: * completion_state == record to set * rpc == rpc operation in progress (may be NULL) * parm == parameter being filled in * cmdstate ==current calling state *********************************************************************/ extern void set_completion_state (completion_state_t *completion_state, obj_template_t *rpc, obj_template_t *parm, command_state_t cmdstate); /******************************************************************** * FUNCTION xpath_getvar_fn * * see ncx/xpath.h -- matches xpath_getvar_fn_t template * * Callback function for retrieval of a variable binding * * INPUTS: * pcb == XPath parser control block in use * varname == variable name requested * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to the ncx_var_t data structure * for the specified varbind *********************************************************************/ extern ncx_var_t * xpath_getvar_fn (struct xpath_pcb_t_ *pcb, const xmlChar *varname, status_t *res); /******************************************************************** * FUNCTION get_netconf_mod * * Get the netconf module * * INPUTS: * server_cb == server control block to use * * RETURNS: * netconf module *********************************************************************/ extern ncx_module_t * get_netconf_mod (server_cb_t *server_cb); /******************************************************************** * FUNCTION clone_old_parm * * Clone a parameter value from the 'old' value set * if it exists there, and add it to the 'new' value set * only if the new value set does not have this parm * * The old and new pvalue sets must be complex types * NCX_BT_LIST, NCX_BT_CONTAINER, or NCX_BT_ANYXML * * INPUTS: * oldvalset == value set to copy from * newvalset == value set to copy into * parm == object template to find and copy * * RETURNS: * status *********************************************************************/ extern status_t clone_old_parm (val_value_t *oldvalset, val_value_t *newvalset, obj_template_t *parm); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli_util */ yuma123_2.14/netconf/src/yangcli/yangcli_wordexp.h0000664000175000017500000000102714770023131022343 0ustar vladimirvladimir/* Structure describing a word-expansion run.*/ #define YANGCLI_WORDEXP_MAXPARAMS_NUM 1024 typedef struct { size_t we_wordc; /* Count of words matched. */ char **we_wordv; /* List of expanded words. */ int *we_word_line_offset; /* List of offsets to words in the original line */ } yangcli_wordexp_t; int yangcli_wordexp (const char* words, yangcli_wordexp_t* pwordexp, int flags); void yangcli_wordfree (yangcli_wordexp_t * pwordexp); void yangcli_wordexp_dump(yangcli_wordexp_t * pwordexp); yuma123_2.14/netconf/src/yangcli/yangcli_save.c0000664000175000017500000002620014770023131021604 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_save.c NETCONF YANG-based CLI Tool save command ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13-aug-09 abb begun; started from yangcli_cmd.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_mgr #include "mgr.h" #endif #ifndef _H_mgr_ses #include "mgr_ses.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_op #include "op.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_rpc_err #include "rpc_err.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifndef _H_var #include "var.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifndef _H_yangcli_cmd #include "yangcli_cmd.h" #endif #ifndef _H_yangcli_save #include "yangcli_save.h" #endif #ifndef _H_yangcli_util #include "yangcli_util.h" #endif /******************************************************************** * FUNCTION send_copy_config_to_server * * Send a operation to the server to support * the save operation * * INPUTS: * server_cb == server control block to use * * OUTPUTS: * state may be changed or other action taken * config_content is consumed -- freed or transfered * * RETURNS: * status *********************************************************************/ static status_t send_copy_config_to_server (server_cb_t *server_cb) { obj_template_t *rpc, *input, *child; mgr_rpc_req_t *req; val_value_t *reqdata, *parm, *target, *source; ses_cb_t *scb; status_t res; req = NULL; reqdata = NULL; res = NO_ERR; rpc = NULL; /* get the template */ rpc = ncx_find_object(get_netconf_mod(server_cb), NCX_EL_COPY_CONFIG); if (!rpc) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* get the 'input' section container */ input = obj_find_child(rpc, NULL, YANG_K_INPUT); if (!input) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* construct a method + parameter tree */ reqdata = xml_val_new_struct(obj_get_name(rpc), obj_get_nsid(rpc)); if (!reqdata) { log_error("\nError allocating a new RPC request"); return ERR_INTERNAL_MEM; } /* set the edit-config/input/target node to the default_target */ child = obj_find_child(input, NC_MODULE, NCX_EL_TARGET); parm = val_new_value(); if (!parm) { val_free_value(reqdata); return ERR_INTERNAL_MEM; } val_init_from_template(parm, child); val_add_child(parm, reqdata); target = xml_val_new_flag((const xmlChar *)"startup", obj_get_nsid(child)); if (!target) { val_free_value(reqdata); return ERR_INTERNAL_MEM; } val_add_child(target, parm); /* set the edit-config/input/default-operation node to 'none' */ child = obj_find_child(input, NC_MODULE, NCX_EL_SOURCE); parm = val_new_value(); if (!parm) { val_free_value(reqdata); return ERR_INTERNAL_MEM; } val_init_from_template(parm, child); val_add_child(parm, reqdata); source = xml_val_new_flag((const xmlChar *)"running", obj_get_nsid(child)); if (!source) { val_free_value(reqdata); return ERR_INTERNAL_MEM; } val_add_child(source, parm); /* allocate an RPC request and send it */ scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { res = SET_ERROR(ERR_INTERNAL_PTR); } else { req = mgr_rpc_new_request(scb); if (!req) { res = ERR_INTERNAL_MEM; log_error("\nError allocating a new RPC request"); } else { req->data = reqdata; req->rpc = rpc; req->timeout = server_cb->timeout; } } if (res == NO_ERR) { if (LOGDEBUG2) { log_debug2("\nabout to send RPC request with reqdata:"); val_dump_value_max(reqdata, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); } /* the request will be stored if this returns NO_ERR */ res = mgr_rpc_send_request(scb, req, yangcli_reply_handler); } if (res != NO_ERR) { if (req) { mgr_rpc_free_request(req); } else if (reqdata) { val_free_value(reqdata); } } else { server_cb->state = MGR_IO_ST_CONN_RPYWAIT; } return res; } /* send_copy_config_to_server */ /******************************************************************** * FUNCTION do_save * * INPUTS: * server_cb == server control block to use * * OUTPUTS: * copy-config and/or commit operation will be sent to server * * RETURNS: * status *********************************************************************/ status_t do_save (server_cb_t *server_cb) { const ses_cb_t *scb; const mgr_scb_t *mscb; xmlChar *line; status_t res; res = NO_ERR; /* get the session info */ scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { return SET_ERROR(ERR_INTERNAL_VAL); } mscb = (const mgr_scb_t *)scb->mgrcb; log_info("\nSaving configuration to non-volative storage"); /* determine which commands to send */ switch (mscb->targtyp) { case NCX_AGT_TARG_NONE: log_stdout("\nWarning: No writable targets supported on this server"); break; case NCX_AGT_TARG_CAND_RUNNING: if (!xml_strcmp(server_cb->default_target, NCX_EL_CANDIDATE)) { line = xml_strdup(NCX_EL_COMMIT); if (line) { res = conn_command(server_cb, line); m__free(line); } else { res = ERR_INTERNAL_MEM; log_stdout("\nError: Malloc failed"); } if (res == NO_ERR && mscb->starttyp == NCX_AGT_START_DISTINCT) { /* need 2 operations so set the command mode and the * reply handler will initiate the 2nd command * if the first one worked */ server_cb->command_mode = CMD_MODE_SAVE; } } else { if (mscb->starttyp == NCX_AGT_START_DISTINCT) { res = send_copy_config_to_server(server_cb); if (res != NO_ERR) { log_stdout("\nError: send copy-config failed (%s)", get_error_string(res)); } } else { log_stdout("\nWarning: No distinct save operation needed " "for this server"); } } break; case NCX_AGT_TARG_CANDIDATE: line = xml_strdup(NCX_EL_COMMIT); if (line) { res = conn_command(server_cb, line); m__free(line); } else { res = ERR_INTERNAL_MEM; log_stdout("\nError: Malloc failed"); } if (res == NO_ERR && mscb->starttyp == NCX_AGT_START_DISTINCT) { /* need 2 operations so set the command mode and the * reply handler will initiate the 2nd command * if the first one worked */ server_cb->command_mode = CMD_MODE_SAVE; } break; case NCX_AGT_TARG_RUNNING: if (mscb->starttyp == NCX_AGT_START_DISTINCT) { res = send_copy_config_to_server(server_cb); if (res != NO_ERR) { log_stdout("\nError: send copy-config failed (%s)", get_error_string(res)); } } else { log_stdout("\nWarning: No distinct save operation needed " "for this server"); } break; case NCX_AGT_TARG_LOCAL: log_stdout("Error: Local URL target not supported"); break; case NCX_AGT_TARG_REMOTE: log_stdout("Error: Local URL target not supported"); break; default: log_stdout("Error: Internal target not set"); res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* do_save */ /******************************************************************** * FUNCTION finish_save * * INPUTS: * server_cb == server control block to use * * OUTPUTS: * copy-config will be sent to server * * RETURNS: * status *********************************************************************/ status_t finish_save (server_cb_t *server_cb) { const ses_cb_t *scb; const mgr_scb_t *mscb; status_t res; res = NO_ERR; /* get the session info */ scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { return SET_ERROR(ERR_INTERNAL_VAL); } mscb = (const mgr_scb_t *)scb->mgrcb; log_info("\nFinal step saving configuration to non-volative storage"); if (mscb->starttyp == NCX_AGT_START_DISTINCT) { res = send_copy_config_to_server(server_cb); if (res != NO_ERR) { log_stdout("\nError: send copy-config failed (%s)", get_error_string(res)); } } else { log_stdout("\nWarning: No distinct save operation needed " "for this server"); } return res; } /* finish_save */ /* END yangcli_save.c */ yuma123_2.14/netconf/src/yangcli/yangcli_util.c0000664000175000017500000006400714770023131021632 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_util.c Utilities for NETCONF YANG-based CLI Tool ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 01-jun-08 abb begun; started from ncxcli.c 27-mar-09 abb split out from yangcli.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" #include "procdefs.h" #include "log.h" #include "mgr.h" #include "ncx.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "runstack.h" #include "status.h" #include "val.h" #include "val_util.h" #include "var.h" #include "xmlns.h" #include "xml_util.h" #include "xml_val.h" #include "xpath.h" #include "xpath_yang.h" #include "yangconst.h" #include "yangcli.h" #include "yangcli_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION is_top_command * * Check if command name is a top command * Must be full name * * INPUTS: * rpcname == command name to check * * RETURNS: * TRUE if this is a top command * FALSE if not *********************************************************************/ boolean is_top_command (const xmlChar *rpcname) { #ifdef DEBUG if (!rpcname) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (!xml_strcmp(rpcname, YANGCLI_ALIAS)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_ALIASES)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_CD)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_CONNECT)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_EVAL)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_EVENTLOG)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_ELIF)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_ELSE)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_END)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_FILL)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_HELP)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_IF)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_HISTORY)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_LIST)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_LOG_ERROR)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_LOG_WARN)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_LOG_INFO)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_LOG_DEBUG)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_MGRLOAD)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_PWD)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_QUIT)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_RECALL)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_RUN)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_SHOW)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_WHILE)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_UNSET)) { ; } else if (!xml_strcmp(rpcname, YANGCLI_USERVARS)) { ; } else { return FALSE; } return TRUE; } /* is_top_command */ /******************************************************************** * FUNCTION new_modptr * * Malloc and init a new module pointer block * * INPUTS: * mod == module to cache in this struct * malloced == TRUE if mod is malloced * FALSE if mod is a back-ptr * feature_list == feature list from capability * deviation_list = deviations list from capability * * RETURNS: * malloced modptr_t struct or NULL of malloc failed *********************************************************************/ modptr_t * new_modptr (ncx_module_t *mod, ncx_list_t *feature_list, ncx_list_t *deviation_list) { modptr_t *modptr; #ifdef DEBUG if (!mod) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif modptr = m__getObj(modptr_t); if (!modptr) { return NULL; } memset(modptr, 0x0, sizeof(modptr_t)); modptr->mod = mod; modptr->feature_list = feature_list; modptr->deviation_list = deviation_list; return modptr; } /* new_modptr */ /******************************************************************** * FUNCTION free_modptr * * Clean and free a module pointer block * * INPUTS: * modptr == mod pointer block to free * MUST BE REMOVED FROM ANY Q FIRST * *********************************************************************/ void free_modptr (modptr_t *modptr) { #ifdef DEBUG if (!modptr) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif m__free(modptr); } /* free_modptr */ /******************************************************************** * FUNCTION find_modptr * * Find a specified module name * * INPUTS: * modptrQ == Q of modptr_t to check * modname == module name to find * revision == module revision (may be NULL) * * RETURNS: * pointer to found entry or NULL if not found * *********************************************************************/ modptr_t * find_modptr (dlq_hdr_t *modptrQ, const xmlChar *modname, const xmlChar *revision) { modptr_t *modptr; #ifdef DEBUG if (!modptrQ || !modname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (modptr = (modptr_t *)dlq_firstEntry(modptrQ); modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { if (xml_strcmp(modptr->mod->name, modname)) { continue; } if (revision && modptr->mod->version && !xml_strcmp(modptr->mod->version, revision)) { return modptr; } if (revision == NULL) { return modptr; } } return NULL; } /* find_modptr */ /******************************************************************** * FUNCTION clear_server_cb_session * * Clean the current session data from an server control block * * INPUTS: * server_cb == control block to use for clearing * the session data *********************************************************************/ void clear_server_cb_session (server_cb_t *server_cb) { modptr_t *modptr; #ifdef DEBUG if (!server_cb) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* get rid of the val->obj pointers that reference * server-specific object trees that have been freed * already by mgr_ses_free_session */ runstack_session_cleanup(server_cb->runstack_context); while (!dlq_empty(&server_cb->modptrQ)) { modptr = (modptr_t *)dlq_deque(&server_cb->modptrQ); free_modptr(modptr); } server_cb->mysid = 0; server_cb->state = MGR_IO_ST_IDLE; if (server_cb->connect_valset) { val_free_value(server_cb->connect_valset); server_cb->connect_valset = NULL; } } /* clear_server_cb_session */ /******************************************************************** * FUNCTION is_top * * Check the state and determine if the top or conn * mode is active * * INPUTS: * server state to use * * RETURNS: * TRUE if this is TOP mode * FALSE if this is CONN mode (or associated states) *********************************************************************/ boolean is_top (mgr_io_state_t state) { switch (state) { case MGR_IO_ST_INIT: case MGR_IO_ST_IDLE: return TRUE; case MGR_IO_ST_CONNECT: case MGR_IO_ST_CONN_START: case MGR_IO_ST_SHUT: case MGR_IO_ST_CONN_IDLE: case MGR_IO_ST_CONN_RPYWAIT: case MGR_IO_ST_CONN_CANCELWAIT: case MGR_IO_ST_CONN_CLOSEWAIT: case MGR_IO_ST_CONN_SHUT: return FALSE; default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } } /* is_top */ /******************************************************************** * FUNCTION use_servercb * * Check if the server_cb should be used for modules right now * * INPUTS: * server_cb == server control block to check * * RETURNS: * TRUE to use server_cb * FALSE if not *********************************************************************/ boolean use_servercb (server_cb_t *server_cb) { if (!server_cb || is_top(server_cb->state)) { return FALSE; } else if (dlq_empty(&server_cb->modptrQ)) { return FALSE; } return TRUE; } /* use_servercb */ /******************************************************************** * FUNCTION find_module * * Check the server_cb for the specified module; if not found * then try ncx_find_module * * INPUTS: * server_cb == control block to free * modname == module name * * RETURNS: * pointer to the requested module * using the registered 'current' version * NULL if not found *********************************************************************/ ncx_module_t * find_module (server_cb_t *server_cb, const xmlChar *modname) { modptr_t *modptr; ncx_module_t *mod; #ifdef DEBUG if (!modname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (use_servercb(server_cb)) { for (modptr = (modptr_t *)dlq_firstEntry(&server_cb->modptrQ); modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { if (!xml_strcmp(modptr->mod->name, modname)) { return modptr->mod; } } } mod = ncx_find_module(modname, NULL); return mod; } /* find_module */ /******************************************************************** * FUNCTION get_strparm * * Get the specified string parm from the parmset and then * make a strdup of the value * * INPUTS: * valset == value set to check if not NULL * modname == module defining parmname * parmname == name of parm to get * * RETURNS: * pointer to string !!! THIS IS A MALLOCED COPY !!! *********************************************************************/ xmlChar * get_strparm (val_value_t *valset, const xmlChar *modname, const xmlChar *parmname) { val_value_t *parm; xmlChar *str; #ifdef DEBUG if (!valset || !parmname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif str = NULL; parm = findparm(valset, modname, parmname); if (parm) { str = xml_strdup(VAL_STR(parm)); if (!str) { log_error("\nyangcli: Out of Memory error"); } } return str; } /* get_strparm */ /******************************************************************** * FUNCTION findparm * * Get the specified string parm from the parmset and then * make a strdup of the value * * INPUTS: * valset == value set to search * modname == optional module name defining the parameter to find * parmname == name of parm to get, or partial name to get * * RETURNS: * pointer to val_value_t if found *********************************************************************/ val_value_t * findparm (val_value_t *valset, const xmlChar *modname, const xmlChar *parmname) { val_value_t *parm; #ifdef DEBUG if (!parmname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (!valset) { return NULL; } parm = val_find_child(valset, modname, parmname); if (!parm && get_autocomp()) { parm = val_match_child(valset, modname, parmname); } return parm; } /* findparm */ /******************************************************************** * FUNCTION add_clone_parm * * Create a parm * * INPUTS: * val == value to clone and add * valset == value set to add parm into * * RETURNS: * status *********************************************************************/ status_t add_clone_parm (const val_value_t *val, val_value_t *valset) { val_value_t *parm; #ifdef DEBUG if (!val || !valset) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif parm = val_clone(val); if (!parm) { log_error("\nyangcli: val_clone failed"); return ERR_INTERNAL_MEM; } else { val_add_child(parm, valset); } return NO_ERR; } /* add_clone_parm */ /******************************************************************** * FUNCTION is_yangcli_ns * * Check the namespace and make sure this is an YANGCLI command * * INPUTS: * ns == namespace ID to check * * RETURNS: * TRUE if this is the YANGCLI namespace ID *********************************************************************/ boolean is_yangcli_ns (xmlns_id_t ns) { const xmlChar *modname; modname = xmlns_get_module(ns); if (modname && !xml_strcmp(modname, YANGCLI_MOD)) { return TRUE; } else { return FALSE; } } /* is_yangcli_ns */ /******************************************************************** * FUNCTION clear_result * * clear out the pending result info * * INPUTS: * server_cb == server control block to use * *********************************************************************/ void clear_result (server_cb_t *server_cb) { #ifdef DEBUG if (!server_cb) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (server_cb->local_result) { val_free_value(server_cb->local_result); server_cb->local_result = NULL; } if (server_cb->result_name) { m__free(server_cb->result_name); server_cb->result_name = NULL; } if (server_cb->result_filename) { m__free(server_cb->result_filename); server_cb->result_filename = NULL; } server_cb->result_vartype = VAR_TYP_NONE; server_cb->result_format = RF_NONE; } /* clear_result */ /******************************************************************** * FUNCTION check_filespec * * Check the filespec string for a file assignment statement * Save it if it si good * * INPUTS: * server_cb == server control block to use * filespec == string to check * varname == variable name to use in log_error * if this is complex form * * OUTPUTS: * server_cb->result_filename will get set if NO_ERR * * RETURNS: * status *********************************************************************/ status_t check_filespec (server_cb_t *server_cb, const xmlChar *filespec, const xmlChar *varname) { xmlChar *newstr; const xmlChar *teststr; status_t res; #ifdef DEBUG if (!server_cb || !filespec) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (!*filespec) { if (varname) { log_error("\nError: file assignment variable '%s' " "is empty string", varname); } else { log_error("\nError: file assignment filespec " "is empty string"); } return ERR_NCX_INVALID_VALUE; } /* variable must be a string with only * valid filespec chars in it; no spaces * are allowed; too many security holes * if arbitrary strings are allowed here */ if (val_need_quotes(filespec)) { if (varname) { log_error("\nError: file assignment variable '%s' " "contains whitespace (%s)", varname, filespec); } else { log_error("\nError: file assignment filespec '%s' " "contains whitespace", filespec); } return ERR_NCX_INVALID_VALUE; } /* check for acceptable chars */ res = NO_ERR; newstr = ncx_get_source_ex(filespec, FALSE, &res); if (newstr == NULL || res != NO_ERR) { log_error("\nError: get source for '%s' failed (%s)", filespec, res); if (newstr != NULL) { m__free(newstr); } return res; } teststr = newstr; while (*teststr) { if (*teststr == NCXMOD_PSCHAR || *teststr == '.' || #ifdef WINDOWS *teststr == ':' || #endif ncx_valid_name_ch(*teststr)) { teststr++; } else { if (varname) { log_error("\nError: file assignment variable '%s' " "contains invalid filespec (%s)", varname, filespec); } else { log_error("\nError: file assignment filespec '%s' " "contains invalid filespec", filespec); } m__free(newstr); return ERR_NCX_INVALID_VALUE; } } /* toss out the old value, if any */ if (server_cb->result_filename) { m__free(server_cb->result_filename); } /* save the filename, may still be an invalid fspec * pass off newstr memory here */ server_cb->result_filename = newstr; if (!server_cb->result_filename) { return ERR_INTERNAL_MEM; } return NO_ERR; } /* check_filespec */ /******************************************************************** * FUNCTION get_file_result_format * * Check the filespec string for a file assignment statement * to see if it is text, XML, or JSON * * INPUTS: * filespec == string to check * * RETURNS: * result format enumeration; RF_NONE if some error *********************************************************************/ result_format_t get_file_result_format (const xmlChar *filespec) { const xmlChar *teststr; uint32 len; #ifdef DEBUG if (!filespec) { SET_ERROR(ERR_INTERNAL_PTR); return RF_NONE; } #endif len = xml_strlen(filespec); if (len < 5) { return RF_TEXT; } teststr = &filespec[len-1]; while (teststr > filespec && *teststr != '.') { teststr--; } if (teststr == filespec) { return RF_TEXT; } teststr++; if (!xml_strcmp(teststr, NCX_EL_XML)) { return RF_XML; } if (!xml_strcmp(teststr, NCX_EL_JSON)) { return RF_JSON; } if (!xml_strcmp(teststr, NCX_EL_YANG)) { return RF_TEXT; } if (!xml_strcmp(teststr, NCX_EL_TXT)) { return RF_TEXT; } if (!xml_strcmp(teststr, NCX_EL_TEXT)) { return RF_TEXT; } if (!xml_strcmp(teststr, NCX_EL_LOG)) { return RF_TEXT; } return RF_TEXT; // default to text instead of error! } /* get_file_result_format */ /******************************************************************** * FUNCTION interactive_mode * * Check if the program is in interactive mode * * RETURNS: * TRUE if insteractive mode, FALSE if batch mode *********************************************************************/ boolean interactive_mode (void) { return get_batchmode() ? FALSE : TRUE; } /* interactive_mode */ /******************************************************************** * FUNCTION init_completion_state * * init the completion_state struct for a new command * * INPUTS: * completion_state == record to initialize * server_cb == server control block to use * cmdstate ==initial calling state *********************************************************************/ void init_completion_state (completion_state_t *completion_state, server_cb_t *server_cb, command_state_t cmdstate) { #ifdef DEBUG if (!completion_state) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif memset(completion_state, 0x0, sizeof(completion_state_t)); completion_state->server_cb = server_cb; completion_state->cmdstate = cmdstate; } /* init_completion_state */ /******************************************************************** * FUNCTION set_completion_state * * set the completion_state struct for a new mode or sub-command * * INPUTS: * completion_state == record to set * rpc == rpc operation in progress (may be NULL) * parm == parameter being filled in * cmdstate ==current calling state *********************************************************************/ void set_completion_state (completion_state_t *completion_state, obj_template_t *rpc, obj_template_t *parm, command_state_t cmdstate) { #ifdef DEBUG if (!completion_state) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif completion_state->cmdstate = cmdstate; completion_state->cmdobj = rpc; if (rpc) { completion_state->cmdinput = obj_find_child(rpc, NULL, YANG_K_INPUT); } else { completion_state->cmdinput = NULL; } completion_state->cmdcurparm = parm; } /* set_completion_state */ /******************************************************************** * FUNCTION xpath_getvar_fn * * see ncx/xpath.h -- matches xpath_getvar_fn_t template * * Callback function for retrieval of a variable binding * * INPUTS: * pcb == XPath parser control block in use * varname == variable name requested * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to the ncx_var_t data structure * for the specified varbind *********************************************************************/ ncx_var_t * xpath_getvar_fn (struct xpath_pcb_t_ *pcb, const xmlChar *varname, status_t *res) { ncx_var_t *retvar; runstack_context_t *rcxt; #ifdef DEBUG if (varname == NULL || res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif /* if the runstack context is not set then the default * context will be used */ rcxt = (runstack_context_t *)pcb->cookie; retvar = var_find(rcxt, varname, 0); if (retvar == NULL) { *res = ERR_NCX_DEF_NOT_FOUND; } else { *res = NO_ERR; } return retvar; } /* xpath_getvar_fn */ /******************************************************************** * FUNCTION get_netconf_mod * * Get the netconf module * * INPUTS: * server_cb == server control block to use * * RETURNS: * netconf module *********************************************************************/ ncx_module_t * get_netconf_mod (server_cb_t *server_cb) { ncx_module_t *mod; #ifdef DEBUG if (server_cb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif mod = find_module(server_cb, NCXMOD_YUMA_NETCONF); return mod; } /* get_netconf_mod */ /******************************************************************** * FUNCTION clone_old_parm * * Clone a parameter value from the 'old' value set * if it exists there, and add it to the 'new' value set * only if the new value set does not have this parm * * The old and new pvalue sets must be complex types * NCX_BT_LIST, NCX_BT_CONTAINER, or NCX_BT_ANYXML * * INPUTS: * oldvalset == value set to copy from * newvalset == value set to copy into * parm == object template to find and copy * * RETURNS: * status *********************************************************************/ status_t clone_old_parm (val_value_t *oldvalset, val_value_t *newvalset, obj_template_t *parm) { val_value_t *findval, *newval; #ifdef DEBUG if (oldvalset == NULL || newvalset == NULL || parm == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!typ_has_children(oldvalset->btyp)) { return ERR_NCX_INVALID_VALUE; } if (!typ_has_children(newvalset->btyp)) { return ERR_NCX_INVALID_VALUE; } #endif findval = val_find_child(newvalset, obj_get_mod_name(parm), obj_get_name(parm)); if (findval != NULL) { return NO_ERR; } findval = val_find_child(oldvalset, obj_get_mod_name(parm), obj_get_name(parm)); if (findval == NULL) { return NO_ERR; } newval = val_clone(findval); if (newval == NULL) { return ERR_INTERNAL_MEM; } val_add_child(newval, newvalset); return NO_ERR; } /* clone_old_parm */ /* END yangcli_util.c */ yuma123_2.14/netconf/src/yangcli/yangcli_autoload.c0000664000175000017500000012063114770023131022461 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_autoload.c NETCONF YANG-based CLI Tool autoload support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13-aug-09 abb begun; started from yangcli.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" #include "procdefs.h" #include "log.h" #include "mgr.h" #include "mgr_ses.h" #include "ncx.h" #include "ncx_feature.h" #include "ncx_list.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "op.h" #include "rpc.h" #include "rpc_err.h" #include "status.h" #include "val_util.h" #include "var.h" #include "xmlns.h" #include "xml_util.h" #include "xml_val.h" #include "yangconst.h" #include "yangcli.h" #include "yangcli_autoload.h" #include "yangcli_cmd.h" #include "yangcli_util.h" #include "yangcli_yang_library.h" #ifdef DEBUG #define YANGCLI_AUTOLOAD_DEBUG 1 #endif /******************************************************************** * FUNCTION make_get_schema_reqdata * * Allocate and initialize reqdata value for * * format will be hard-wired to yang * * INPUTS: * server_cb == server control block to use * scb == session control block to use * module == module to get * revision == revision to get * * OUTPUTS: * out_rpc == obj_template_t** of the get-schema RPC * out_reqdata == val_value_t** of the get-schema data value * * RETURNS: * status *********************************************************************/ status_t make_get_schema_reqdata(server_cb_t *server_cb, ses_cb_t *scb, const xmlChar *module, const xmlChar *revision, obj_template_t** out_rpc, val_value_t** out_reqdata) { ncx_module_t *mod; obj_template_t *rpc, *input, *parmobj; val_value_t *reqdata, *parmval; status_t res; xmlns_id_t nsid; reqdata = NULL; res = NO_ERR; input = NULL; nsid = 0; mod = ncx_find_module(NCXMOD_IETF_NETCONF_STATE, NULL); if (mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* get the input template */ rpc = ncx_find_rpc(mod, NCX_EL_GET_SCHEMA); assert(rpc); nsid = obj_get_nsid(rpc); input = obj_find_child(rpc, NULL, YANG_K_INPUT); assert(input); /* construct a method + parameter tree */ reqdata = val_new_value(); assert(reqdata); val_init_from_template(reqdata, rpc); /* add /get-schema/input/identifier */ parmobj = obj_find_child(input, NCXMOD_IETF_NETCONF_STATE, NCX_EL_IDENTIFIER); assert(parmobj); parmval = val_make_simval_obj(parmobj, module, &res); assert(parmval); val_add_child(parmval, reqdata); /* add /get-schema/input/version */ parmobj = obj_find_child(input, NCXMOD_IETF_NETCONF_STATE, NCX_EL_VERSION); assert(parmobj); parmval = val_make_simval_obj(parmobj, (revision) ? revision : EMPTY_STRING, &res); assert(parmval); val_add_child(parmval, reqdata); /* add /get-schema/input/format */ parmobj = obj_find_child(input, NCXMOD_IETF_NETCONF_STATE, NCX_EL_FORMAT); assert(parmobj); parmval = val_make_simval_obj(parmobj, "yang", &res); assert(parmval); val_add_child(parmval, reqdata); /* check any errors so far */ if (res != NO_ERR) { val_free_value(reqdata); return res; } *out_rpc=rpc; *out_reqdata=reqdata; return res; } /* make_get_schema_reqdata */ /******************************************************************** * FUNCTION send_get_schema_to_server * * Send an operation to the specified server * in MGR_IO_ST_AUTOLOAD state * * format will be hard-wired to yang * * INPUT: * server_cb == server control block to use * scb == session control block to use * module == module to get * revision == revision to get * * OUTPUTS: * server_cb->state may be changed or other action taken * * RETURNS: * status *********************************************************************/ static status_t send_get_schema_to_server (server_cb_t *server_cb, ses_cb_t *scb, const xmlChar *module, const xmlChar *revision) { status_t res; obj_template_t* rpc; val_value_t* reqdata; mgr_rpc_req_t *req; req = NULL; res = make_get_schema_reqdata(server_cb, scb, module, revision, &rpc, &reqdata); if(res!=NO_ERR) { return NO_ERR; } /* allocate an RPC request and send it */ req = mgr_rpc_new_request(scb); if (!req) { res = ERR_INTERNAL_MEM; log_error("\nError allocating a new RPC request"); } else { req->data = reqdata; req->rpc = rpc; req->timeout = server_cb->timeout; } if (res == NO_ERR) { if (LOGDEBUG) { log_debug("\nSending autoload request for '%s', r'%s'", module, (revision) ? revision : EMPTY_STRING); } if (LOGDEBUG2) { log_debug2("\nabout to send RPC request with reqdata:"); val_dump_value_max(reqdata, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); } /* the request will be stored if this returns NO_ERR */ res = mgr_rpc_send_request(scb, req, yangcli_reply_handler); } if (res != NO_ERR) { if (req) { mgr_rpc_free_request(req); } else if (reqdata) { val_free_value(reqdata); } } else { server_cb->state = MGR_IO_ST_CONN_RPYWAIT; } return res; } /* send_get_schema_to_server */ /******************************************************************** * FUNCTION save_schema_file * * Save the node in the as the * specified YANG file in the session work directory * * INPUTS: * server_cb == server control block to use * module == module name * revision == revision date * targetfile == filespec of the output file * resultval == result to output to file * * RETURNS: * status *********************************************************************/ static status_t save_schema_file (server_cb_t *server_cb, const xmlChar *module, const xmlChar *revision, const xmlChar *targetfile, val_value_t *resultval) { status_t res; struct stat statbuf; int statresult; res = NO_ERR; if (LOGDEBUG) { log_debug("\nGot autoload reply for '%s' r'%s'", module, (revision) ? revision : EMPTY_STRING); } if (LOGDEBUG2) { log_debug2("\n*** output result " "\n module '%s'" "\n revision '%'s" "\n target '%s'", module, (revision) ? revision : EMPTY_STRING, targetfile); } /* see if file already exists */ statresult = stat((const char *)targetfile, &statbuf); if (statresult == 0) { log_error("\nError: temporary file '%s' already exists", targetfile); return ERR_NCX_DATA_EXISTS; } /* output in text format to the specified file */ res = log_alt_open((const char *)targetfile); if (res != NO_ERR) { log_error("\nError: temporary file '%s' could " "not be opened (%s)", targetfile, get_error_string(res)); } else { /* do not use session display mode and other parameters * when saving a schema file; save as-is */ val_dump_alt_value(resultval, 0); log_alt_close(); /* copy the target filename into the search result */ server_cb->cursearchresult->source = xml_strdup(targetfile); if (server_cb->cursearchresult->source == NULL) { log_error("\nError: malloc failed for temporary file '%s'", targetfile); return ERR_INTERNAL_MEM; } } return res; } /* save_schema_file */ /******************************************************************** * FUNCTION reset_feature * * Go through the feature list and see if the specified * feature should be enabled or not * * INPUTS: * mod == module containing this feature * feature == feature found * cookie == cookie passed in (feature_list) * * RETURNS: * TRUE if processing should continue, FALSE if done *********************************************************************/ static boolean reset_feature (const ncx_module_t *mod, ncx_feature_t *feature, void *cookie) { const ncx_list_t *feature_list; (void)mod; feature_list = (const ncx_list_t *)cookie; feature->enabled = (ncx_string_in_list(feature->name, feature_list)) ? TRUE : FALSE; return TRUE; } /* reset_feature */ static ncxmod_temp_filcb_t * get_new_temp_filcb (mgr_scb_t *mscb, const xmlChar *module, const xmlChar *revision, boolean isyang, status_t *res) { xmlChar *filebuffer, *p; ncxmod_temp_filcb_t *temp_filcb; uint32 len_needed; *res = NO_ERR; /* figure out how big the filename will be */ len_needed = xml_strlen(module); if (revision) { len_needed += (xml_strlen(revision) + 1); } if (isyang) { len_needed += 5; /* .yang */ } else { len_needed += 4; /* .yin */ } filebuffer = m__getMem(len_needed+1); if (filebuffer == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } /* construct a file name for the target file */ p = filebuffer; p += xml_strcpy(p, module); if (revision && *revision!=0) { *p++ = '@'; p += xml_strcpy(p, revision); } if (isyang) { p += xml_strcpy(p, (const xmlChar *)".yang"); } else { p += xml_strcpy(p, (const xmlChar *)".yin"); } /* get a temp file control block * it will be stored in the session control block * so it is not a live malloced pointer */ temp_filcb = ncxmod_new_session_tempfile(mscb->temp_sescb, filebuffer, res); m__free(filebuffer); return temp_filcb; } /* get_new_temp_filcb */ status_t get_schema_reply_to_temp_filcb(server_cb_t * server_cb, mgr_scb_t *mscb, const xmlChar* module, const xmlChar* revision, val_value_t* reply) { ncxmod_search_result_t *searchresult; ncxmod_temp_filcb_t *temp_filcb; val_value_t *dataval; status_t res; /* get the data node out of the reply; * it contains the requested YANG module * in raw text form */ dataval = val_find_child(reply, NULL, NCX_EL_DATA); if (dataval == NULL) { res = SET_ERROR(ERR_NCX_DATA_MISSING); } else { /* get a file handle in the temp session * directory */ temp_filcb = get_new_temp_filcb(mscb, module, revision, TRUE, /* isyang */ &res); if (temp_filcb != NULL) { /* copy the value node to the work directory * as a YANG file */ res = save_schema_file(server_cb, module, revision, temp_filcb->source, dataval); } } return res; } /******************************************************************** * FUNCTION copy_module_to_tempdir * * Copy the YANG source file to the session work directory * * INPUTS: * mscb == manager session control block to use * module == module name to copy * revision == revision date to copy * source == complete pathspec of source YANG file * * RETURNS: * status *********************************************************************/ static status_t copy_module_to_tempdir (mgr_scb_t *mscb, const xmlChar *module, const xmlChar *revision, const xmlChar *source) { xmlChar *linebuffer; FILE *srcfile, *destfile; ncxmod_temp_filcb_t *temp_filcb; boolean done, isyang; status_t res; res = NO_ERR; linebuffer = NULL; srcfile = NULL; destfile = NULL; if (yang_fileext_is_yang(source)) { isyang = TRUE; } else if (yang_fileext_is_yin(source)) { isyang = FALSE; } else { return SET_ERROR(ERR_INTERNAL_VAL); } temp_filcb = get_new_temp_filcb(mscb, module, revision, isyang, &res); if (temp_filcb == NULL) { return res; } /* get a buffer for transferring lines */ linebuffer = m__getMem(NCX_MAX_LINELEN+1);; if (linebuffer == NULL) { ncxmod_free_session_tempfile(temp_filcb); return ERR_INTERNAL_MEM; } #ifdef YANGCLI_AUTOLOAD_DEBUG if (LOGDEBUG2) { log_debug2("\nyangcli_autoload: Copying '%s' to '%s'", source, temp_filcb->source); } #endif /* open the destination file for writing */ destfile = fopen((const char *)temp_filcb->source, "w+"); if (destfile == NULL) { res = errno_to_status(); ncxmod_free_session_tempfile(temp_filcb); m__free(linebuffer); return res; } /* open the YANG or YIN source file for reading */ srcfile = fopen((const char *)source, "r"); if (srcfile == NULL) { res = errno_to_status(); fclose(destfile); ncxmod_free_session_tempfile(temp_filcb); m__free(linebuffer); return res; } done = FALSE; while (!done) { if (!fgets((char *)linebuffer, NCX_MAX_LINELEN, srcfile)) { /* read line failed, not an error */ done = TRUE; continue; } if (fputs((const char *)linebuffer, destfile) == EOF) { log_error("\nError: copy to temp file failed"); /*** keeping partial file around!!! ***/ done = TRUE; res = ERR_FIL_WRITE; } } fclose(srcfile); fclose(destfile); m__free(linebuffer); return res; } /* copy_module_to_tempdir */ /******************************************************************** * FUNCTION set_temp_ync_features * * Set the features list for the new session * based on the capabilities reported by the server * * INPUTS: * mscb == manager session control block to use * * RETURNS: * status *********************************************************************/ static status_t set_temp_ync_features (mgr_scb_t *mscb) { status_t res; if (cap_std_set(&mscb->caplist, CAP_STDID_WRITE_RUNNING)) { res = ncx_set_list(NCX_BT_STRING, NCX_EL_WRITABLE_RUNNING, &mscb->temp_ync_features); if (res != NO_ERR) { return res; } } if (cap_std_set(&mscb->caplist, CAP_STDID_CANDIDATE)) { res = ncx_set_list(NCX_BT_STRING, NCX_EL_CANDIDATE, &mscb->temp_ync_features); if (res != NO_ERR) { return res; } } if (cap_std_set(&mscb->caplist, CAP_STDID_CONF_COMMIT)) { res = ncx_set_list(NCX_BT_STRING, NCX_EL_CONFIRMED_COMMIT, &mscb->temp_ync_features); if (res != NO_ERR) { return res; } } if (cap_std_set(&mscb->caplist, CAP_STDID_ROLLBACK_ERR)) { res = ncx_set_list(NCX_BT_STRING, NCX_EL_ROLLBACK_ON_ERROR, &mscb->temp_ync_features); if (res != NO_ERR) { return res; } } if (cap_std_set(&mscb->caplist, CAP_STDID_VALIDATE)) { res = ncx_set_list(NCX_BT_STRING, NCX_EL_VALIDATE, &mscb->temp_ync_features); if (res != NO_ERR) { return res; } } if (cap_std_set(&mscb->caplist, CAP_STDID_STARTUP)) { res = ncx_set_list(NCX_BT_STRING, NCX_EL_STARTUP, &mscb->temp_ync_features); if (res != NO_ERR) { return res; } } if (cap_std_set(&mscb->caplist, CAP_STDID_URL)) { res = ncx_set_list(NCX_BT_STRING, NCX_EL_URL, &mscb->temp_ync_features); if (res != NO_ERR) { return res; } } if (cap_std_set(&mscb->caplist, CAP_STDID_XPATH)) { res = ncx_set_list(NCX_BT_STRING, NCX_EL_XPATH, &mscb->temp_ync_features); if (res != NO_ERR) { return res; } } return NO_ERR; } /* set_temp_ync_features */ /******************************************************************** * FUNCTION autoload_module * * auto-load the specified module * * INPUTS: * modname == module name * revision == module revision * devlist == deviation list * retmod == address of return module * * OUTPUTS: * *retmod == loaded module (if NO_ERR) * * RETURNS: * status *********************************************************************/ static status_t autoload_module (const xmlChar *modname, const xmlChar *revision, ncx_list_t *devlist, dlq_hdr_t *savedevQ_ptr, ncx_module_t **retmod) { ncx_lmem_t *listmember; status_t res; log_debug_t loglevel; if (LOGDEBUG2) { log_debug2("\nStarting autoload for module '%s', " "revision '%s'", modname, (revision) ? revision : EMPTY_STRING); } res = NO_ERR; /* first load any deviations */ if (devlist != NULL) { for (listmember = ncx_first_lmem(devlist); listmember != NULL && res == NO_ERR; listmember = (ncx_lmem_t *)dlq_nextEntry(listmember)) { res = ncxmod_load_deviation(listmember->val.str, savedevQ_ptr); if (res != NO_ERR) { log_error("\nError: Deviation module %s not loaded (%s)!!", listmember->val.str, get_error_string(res)); } } } /* parse the requested module now * retmod is a live pointer */ if (res == NO_ERR) { if (LOGDEBUG) { res = ncxmod_parse_module(modname, revision, savedevQ_ptr, retmod); } else { /* ignore parse warnings during autoload unless debug mode */ loglevel = log_get_debug_level(); log_set_debug_level(LOG_DEBUG_ERROR); res = ncxmod_parse_module(modname, revision, savedevQ_ptr, retmod); log_set_debug_level(loglevel); } if (res != NO_ERR) { log_error("\nError: Auto-load for module '%s' failed (%s)", modname, get_error_string(res)); } else if (retmod != NULL && *retmod != NULL) { (*retmod)->defaultrev = TRUE; } } return res; } /* autoload_module */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION autoload_setup_tempdir * * copy all the advertised YANG modules into the * session temp files directory * * DOES NOT COPY FILES UNLESS THE 'source' FIELD IS SET * IN THE SEARCH RESULT (ONLY LOCAL FILES COPIED) * * INPUTS: * server_cb == server session control block to use * scb == current session in progress * * OUTPUTS: * $HOME/.yuma/tmp/// filled with * the the specified YANG files that are already available * on this system. * * These search records will be removed from the * server_cb->searchresultQ and modptr records * added to the server_cb->modptrQ * * RETURNS: * status *********************************************************************/ status_t autoload_setup_tempdir (server_cb_t *server_cb, ses_cb_t *scb) { mgr_scb_t *mscb; ncxmod_search_result_t *searchresult; ncx_module_t *testmod; status_t res, retres; boolean need_yt, need_ncx, need_nacm, need_ync; dlq_hdr_t savedevQ; #ifdef DEBUG if (!server_cb || !scb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; retres = NO_ERR; mscb = (mgr_scb_t *)scb->mgrcb; need_yt = TRUE; need_ncx = TRUE; need_nacm = TRUE; need_ync = TRUE; /* try to copy as many files as possible, even if some errors */ for (searchresult = (ncxmod_search_result_t *) dlq_firstEntry(&server_cb->searchresultQ); searchresult != NULL; searchresult = (ncxmod_search_result_t *) dlq_nextEntry(searchresult)) { /* skip bad entries and not-found entries */ if (searchresult->module == NULL || searchresult->source == NULL || searchresult->res != NO_ERR) { continue; } /* check if the URI was not the same, even though * the module was found, so the source was set */ if (!searchresult->capmatch) { m__free(searchresult->source); searchresult->source = NULL; continue; } /* check yuma-netconf hack; remove this code * when ietf-netconf is supported */ if (!xml_strcmp(searchresult->module, NCXMOD_NCX)) { need_ncx = FALSE; } else if (!xml_strcmp(searchresult->module, NCXMOD_IETF_YANG_TYPES)) { need_yt = FALSE; } else if (!xml_strcmp(searchresult->module, NCXMOD_IETF_NETCONF_ACM)) { need_nacm = FALSE; } else if (!xml_strcmp(searchresult->module, NCXMOD_YUMA_NETCONF)) { need_ync = FALSE; } /* copy this local module to the work dir so * it will be found in an import, even if the * revision-date is missing from the import */ res = copy_module_to_tempdir(mscb, searchresult->module, searchresult->revision, searchresult->source); if (res != NO_ERR) { searchresult->res = res; retres = res; } } if (retres == NO_ERR) { dlq_createSQue(&savedevQ); if (need_yt) { res = ncxmod_load_module(NCXMOD_IETF_YANG_TYPES, NULL, &savedevQ, &testmod); if (res == NO_ERR) { res = copy_module_to_tempdir(mscb, testmod->name, testmod->version, testmod->source); } else { SET_ERROR(res); } } if (need_ncx) { res = ncxmod_load_module(NCXMOD_NCX, NULL, &savedevQ, &testmod); if (res == NO_ERR) { res = copy_module_to_tempdir(mscb, testmod->name, testmod->version, testmod->source); } else { SET_ERROR(res); } } if (need_nacm) { res = ncxmod_load_module(NCXMOD_IETF_NETCONF_ACM, NULL, &savedevQ, &testmod); if (res == NO_ERR) { res = copy_module_to_tempdir(mscb, testmod->name, testmod->version, testmod->source); } else { SET_ERROR(res); } } if (need_ync) { res = ncxmod_load_module(NCXMOD_YUMA_NETCONF, NULL, &savedevQ, &testmod); if (res == NO_ERR) { res = copy_module_to_tempdir(mscb, testmod->name, testmod->version, testmod->source); } else { SET_ERROR(res); } } ncx_clean_save_deviationsQ(&savedevQ); } return retres; } /* autoload_setup_tempdir */ /******************************************************************** * FUNCTION autoload_start_get_modules * * Start the MGR_SES_IO_CONN_AUTOLOAD state * * Go through all the search result records and * make sure all files are present. Try to use the * operation to fill in any missing modules * * INPUTS: * server_cb == server session control block to use * scb == session control block to use * * OUTPUTS: * $HOME/.yuma/tmp/// filled with * the the specified YANG files that are ertrieved from * the device with * * RETURNS: * status *********************************************************************/ status_t autoload_start_get_modules (server_cb_t *server_cb, ses_cb_t *scb) { ncxmod_search_result_t *searchresult; status_t res; boolean done; #ifdef DEBUG if (!server_cb || !scb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif done = FALSE; res = NO_ERR; /* find first file that needs to be retrieved with get-schema */ for (searchresult = (ncxmod_search_result_t *) dlq_firstEntry(&server_cb->searchresultQ); searchresult != NULL && !done; searchresult = (ncxmod_search_result_t *) dlq_nextEntry(searchresult)) { /* skip found entries */ if (searchresult->source != NULL) { continue; } /* skip found modules with errors */ if (searchresult->res != NO_ERR && !(searchresult->res == ERR_NCX_WRONG_VERSION || searchresult->res == ERR_NCX_MOD_NOT_FOUND)) { continue; } /* found an entry that needs to be retrieved * either module not found or wrong version found */ done = TRUE; res = send_get_schema_to_server(server_cb, scb, searchresult->module, searchresult->revision); if (res == NO_ERR) { server_cb->command_mode = CMD_MODE_AUTOLOAD; server_cb->cursearchresult = searchresult; } /* exit loop if we get here */ } return res; } /* autoload_start_get_modules */ /******************************************************************** * FUNCTION autoload_handle_rpc_reply * * Handle the current response * * INPUTS: * server_cb == server session control block to use * scb == session control block to use * reply == data node from the PDU * anyerrors == TRUE if detected instead * of * == FALSE if no elements detected * * OUTPUTS: * $HOME/.yuma/tmp/// filled with * the the specified YANG files that was retrieved from * the device with * * Next request is started, or autoload process is completed * and the command_mode is changed back to CMD_MODE_NORMAL * * RETURNS: * status *********************************************************************/ status_t autoload_handle_rpc_reply (server_cb_t *server_cb, ses_cb_t *scb, val_value_t *reply, boolean anyerrors) { mgr_scb_t *mscb; ncxmod_search_result_t *searchresult; const xmlChar *module, *revision; status_t res; boolean done; #ifdef DEBUG if (!server_cb || !scb || !reply) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif done = FALSE; res = NO_ERR; mscb = (mgr_scb_t *)scb->mgrcb; searchresult = server_cb->cursearchresult; if(searchresult==NULL) { /* check if this is yang-library for /modules-state and add new searchresult entries */ res = get_yang_library_modules_state_reply_to_searchresult_entries(server_cb, scb, reply); if (res == NO_ERR) { searchresult = server_cb->cursearchresult; } else { assert(0); } if(searchresult==NULL) { /* no search results left to get */ return autoload_compile_modules(server_cb, scb); } } module = searchresult->module; revision = searchresult->revision; if (anyerrors) { /* going to skip this module any try to * compile without it */ log_error("\nError: for module '%s', " "revision '%s' failed", module, (revision) ? revision : EMPTY_STRING); if (!LOGDEBUG2) { /* the error was never printed */ if (LOGINFO) { val_dump_value_max(reply, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); } } } else { res = get_schema_reply_to_temp_filcb(server_cb, mscb, module, revision, reply); if (res != NO_ERR) { log_error("\nError: save content " " for module '%s' revision '%s' failed (%s)", module, (revision) ? revision : EMPTY_STRING, get_error_string(res)); server_cb->cursearchresult->res = res; } } /* find next file that needs to be retrieved with get-schema */ for (searchresult = (ncxmod_search_result_t *) dlq_nextEntry(server_cb->cursearchresult); searchresult != NULL && !done; searchresult = (ncxmod_search_result_t *) dlq_nextEntry(searchresult)) { /* skip found entries */ if (searchresult->source != NULL) { continue; } /* skip found modules with errors */ if (searchresult->res != NO_ERR && !(searchresult->res == ERR_NCX_WRONG_VERSION || searchresult->res == ERR_NCX_MOD_NOT_FOUND)) { continue; } /* found an entry that needs to be retrieved */ server_cb->command_mode = CMD_MODE_AUTOLOAD; server_cb->cursearchresult = searchresult; done = TRUE; res = send_get_schema_to_server(server_cb, scb, searchresult->module, searchresult->revision); } if (!done) { /* no search results left to get */ return autoload_compile_modules(server_cb, scb); } return res; } /* autoload_handle_rpc_reply */ /******************************************************************** * FUNCTION autoload_compile_modules * * Go through all the search result records and parse * the modules that the device advertised. * DOES NOT LOAD THESE MODULES INTO THE MODULE DIRECTORY * THE RETURNED ncx_module_t STRUCT IS JUST FOR ONE SESSION USE * * Apply the deviations and features specified in * the search result cap back-ptr, to the module * * INPUTS: * server_cb == server session control block to use * scb == session control block to use * * OUTPUTS: * $HOME/.yuma/tmp/// filled with * the the specified YANG files that are already available * on this system. * * These search records will be removed from the * server_cb->searchresultQ and modptr records * added to the server_cb->modptrQ * * RETURNS: * status *********************************************************************/ status_t autoload_compile_modules (server_cb_t *server_cb, ses_cb_t *scb) { mgr_scb_t *mscb; ncxmod_search_result_t *searchresult; modptr_t *modptr; ncx_module_t *mod, *ncmod; status_t res; #ifdef DEBUG if (!server_cb || !scb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* should not happen, but it is possible that the * server did not send any YANG module capabilities */ if (dlq_empty(&server_cb->searchresultQ)) { return NO_ERR; } mod = NULL; ncmod = NULL; res = NO_ERR; mscb = (mgr_scb_t *)scb->mgrcb; /* set the alternate path to point at the * session work directory; this will cause * the server revision date of each module to be * used instead of a random version that the * yangcli registry contains */ ncxmod_set_altpath(mscb->temp_sescb->source); /* set the altername module Q so the imports * do not get re-compiled over and over */ ncx_set_cur_modQ(&mscb->temp_modQ); /* !!! temp until the ietf-netconf.yang module * is fully supported. The yuma-netconf.yang * module is pre-loaded as the first module */ res = autoload_module(NCXMOD_YUMA_NETCONF, NULL, NULL, &server_cb->autoload_savedevQ, &ncmod); if (res == NO_ERR && ncmod != NULL) { /* Set the features in yuma-netconf.yang according * to the standard capabilities that were announced * by the server */ set_temp_ync_features(mscb); modptr = new_modptr(ncmod, &mscb->temp_ync_features, NULL); if (modptr == NULL) { res = ERR_INTERNAL_MEM; log_error("\nMalloc failure"); } else { dlq_enque(modptr, &server_cb->modptrQ); } } /* go through all the search results and load * the modules in order; all imports should already * be preloaded into the session work directory */ while (/*res == NO_ERR && */ !dlq_empty(&server_cb->searchresultQ)) { searchresult = (ncxmod_search_result_t *) dlq_deque(&server_cb->searchresultQ); if (searchresult->res == ERR_NCX_MOD_NOT_FOUND){ searchresult->res = NO_ERR; } if (searchresult->res != NO_ERR || searchresult->source == NULL) { ncxmod_free_search_result(searchresult); continue; } mod = NULL; mod = ncx_find_module(searchresult->module, searchresult->revision); if(mod==NULL) { ncx_list_t *devlist = NULL; /* prefer the list of deviations from modules-state */ if (ncx_list_cnt(&searchresult->devlist) > 0) { devlist = &searchresult->devlist; } else if (searchresult->cap) { devlist = &searchresult->cap->cap_deviation_list; } res = autoload_module(searchresult->module, searchresult->revision, devlist, &server_cb->autoload_savedevQ, &mod); searchresult->res = res; } if (res == NO_ERR) { if (mod == NULL) { /* ??? not sure if this could happen ?? */ mod = ncx_find_module(searchresult->module, searchresult->revision); if (mod == NULL) { log_warn("\nWarning: no module parsed " "for module %s, rev %s", searchresult->module, (searchresult->revision) ? searchresult->revision : NCX_EL_NONE); } } /* make sure this module is not stored more than once */ modptr = find_modptr(&server_cb->modptrQ, searchresult->module, searchresult->revision); if (mod != NULL && modptr == NULL) { /* mod can be NULL if the module was already * in the temp_modQ */ modptr = new_modptr(mod, searchresult->cap?&searchresult->cap->cap_feature_list:NULL, searchresult->cap?&searchresult->cap->cap_deviation_list:NULL); if (modptr == NULL) { log_error("\nMalloc failure"); } else { dlq_enque(modptr, &server_cb->modptrQ); } } } ncxmod_free_search_result(searchresult); } /* undo the temporary MODPATH setting */ ncxmod_clear_altpath(); /* undo the temporary module Q */ ncx_reset_modQ(); /* set the session module Q so the val.c * functions will find the server modules * instead of the pre-loaded versions */ ncx_set_session_modQ(&mscb->temp_modQ); if (!server_cb->keep_session_model_copies_after_compilation && mscb->temp_progcb && mscb->temp_sescb) { /* we won't be keeping the compiled yang sources */ ncxmod_free_session_tempdir(mscb->temp_progcb, mscb->temp_sescb->sidnum); mscb->temp_sescb = NULL; } /* need to wait until all the modules are loaded to * go through the modptr list and enable/disable the features * to match what the server has reported */ for (modptr = (modptr_t *) dlq_firstEntry(&server_cb->modptrQ); modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { if (modptr->feature_list) { ncx_for_all_features(modptr->mod, reset_feature, modptr->feature_list, FALSE); } } server_cb->command_mode = CMD_MODE_NORMAL; server_cb->cursearchresult = NULL; return res; } /* autoload_compile_modules */ /* END yangcli_autoload.c */ yuma123_2.14/netconf/src/yangcli/yangcli_cmd.c0000664000175000017500000076717514770023131021440 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2013 - 2017, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_cmd.c NETCONF YANG-based CLI Tool See ./README for details ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11-apr-09 abb begun; started from yangcli.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" #include "yangcli_wordexp.h" #include "procdefs.h" #include "cli.h" #include "conf.h" #include "help.h" #include "log.h" #include "mgr.h" #include "mgr_io.h" #include "mgr_load.h" #include "mgr_not.h" #include "mgr_rpc.h" #include "mgr_ses.h" #include "ncx.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "obj_help.h" #include "op.h" #include "rpc.h" #include "rpc_err.h" #include "runstack.h" #include "status.h" #include "val.h" #include "val123.h" #include "val_util.h" #include "var.h" #include "xmlns.h" #include "xml_util.h" #include "xml_val.h" #include "xml_wr.h" #include "xpath.h" #include "xpath1.h" #include "xpath_yang.h" #include "yangconst.h" #include "yangcli.h" #include "yangcli_alias.h" #include "yangcli_autolock.h" #include "yangcli_cond.h" #include "yangcli_cmd.h" #include "yangcli_eval.h" #include "yangcli_list.h" #include "yangcli_save.h" #include "yangcli_show.h" #include "yangcli_tab.h" #include "yangcli_timer.h" #include "yangcli_uservars.h" #include "yangcli_util.h" /* get_case needs to recurse */ static status_t fill_valset (server_cb_t *server_cb, obj_template_t *rpc, val_value_t *valset, val_value_t *oldvalset, boolean iswrite, boolean isdelete); /******************************************************************** * FUNCTION need_get_parm * * Check if the get_foo function should fill in this parameter * or not * * INPUTS: * parm == object template to check * * RETURNS: * TRUE if value should be filled in * FALSE if it should be skipped *********************************************************************/ static boolean need_get_parm (obj_template_t *parm) { if (!obj_is_config(parm) || obj_is_abstract(parm) || obj_get_status(parm) == NCX_STATUS_OBSOLETE) { return FALSE; } return TRUE; } /* need_get_parm */ /******************************************************************** * FUNCTION check_external_rpc_input * * Check for the foo @parms.xml command form * * INPUTS: * rpc == RPC to parse CLI for * line == input line to parse, starting with the parms to parse * res == pointer to status output * * OUTPUTS: * *res == status * will be ERR_NCX_SKIPPED if not the external input form * * RETURNS: * pointer to malloced value set or NULL if none created, * may have errors, check *res * == NULL if *res == ERR_NCX_SKIPPED *********************************************************************/ static val_value_t* check_external_rpc_input (obj_template_t *rpc, const xmlChar *args, status_t *res) { obj_template_t *obj = obj_find_child(rpc, NULL, YANG_K_INPUT); const xmlChar *str = args; xmlChar *sourcefile = NULL; xmlChar *fname = NULL; val_value_t *retval = NULL; if (obj == NULL) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } if (args == NULL) { *res = ERR_NCX_SKIPPED; return NULL; } while (*str && xml_isspace(*str)) { str++; } if (*str != '@') { *res = ERR_NCX_SKIPPED; return NULL; } /* stopped on the @ char. Expecting @filespec * this is a NCX_BT_EXTERN value * find the file with the raw XML data * treat this as a source file name which * may need to be expanded (e.g., @~/filespec.xml) */ *res = NO_ERR; sourcefile = ncx_get_source_ex(&str[1], FALSE, res); if (*res == NO_ERR && sourcefile != NULL) { fname = ncxmod_find_data_file(sourcefile, TRUE, res); if (fname != NULL && *res == NO_ERR) { retval = mgr_load_extern_file(fname, obj, res); } } if (sourcefile != NULL) { m__free(sourcefile); } if (fname != NULL) { m__free(fname); } return retval; } /* check_external_rpc_input */ /******************************************************************** * FUNCTION parse_rpc_cli * * Call the cli_parse for an RPC input value set * * INPUTS: * server_cb == server control block to use * rpc == RPC to parse CLI for * args_in == input line to parse, starting with the parms to parse * res == pointer to status output * * OUTPUTS: * *res == status * * RETURNS: * pointer to malloced value set or NULL if none created, * may have errors, check *res *********************************************************************/ val_value_t* parse_rpc_cli ( server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *args_in, status_t *res ) { obj_template_t *obj; char *myargv[2]; val_value_t *retval = NULL; if(!args_in) { return NULL; } /* construct an argv array, convert the CLI into a parmset */ obj = obj_find_child(rpc, NULL, YANG_K_INPUT); if (!obj) { return NULL; } /* check if this is the special command form * foo-command @parms.xml */ if (args_in) { *res = NO_ERR; retval = check_external_rpc_input(rpc, args_in, res); if (*res != ERR_NCX_SKIPPED) { return retval; } assert (retval==NULL); } myargv[0] = strdup(obj_get_name(rpc)); assert(myargv[0]); myargv[1] = strdup(args_in); assert(myargv[1]); retval = cli_parse( server_cb->runstack_context, 2, myargv, obj, VALONLY, SCRIPTMODE, get_autocomp(), CLI_MODE_COMMAND, res ); free(myargv[0]); free(myargv[1]); return retval; } /* parse_rpc_cli */ /******************************************************************** * FUNCTION get_prompt * * Construct the CLI prompt for the current state * * INPUTS: * buff == bufffer to hold prompt (zero-terminated string) * bufflen == length of buffer (max bufflen-1 chars can be printed * *********************************************************************/ static void get_prompt (server_cb_t *server_cb, xmlChar *buff, uint32 bufflen) { xmlChar *p; val_value_t *parm; uint32 len, condlen; boolean cond; if (server_cb->climore) { xml_strncpy(buff, MORE_PROMPT, bufflen); return; } cond = runstack_get_cond_state(server_cb->runstack_context); condlen = (cond) ? 0: xml_strlen(FALSE_PROMPT); switch (server_cb->state) { case MGR_IO_ST_INIT: case MGR_IO_ST_IDLE: case MGR_IO_ST_CONNECT: case MGR_IO_ST_CONN_START: case MGR_IO_ST_SHUT: if (server_cb->cli_fn) { len = xml_strlen(DEF_FN_PROMPT) + xml_strlen(server_cb->cli_fn) + condlen + 2; if (len < bufflen) { p = buff; p += xml_strcpy(p, DEF_FN_PROMPT); p += xml_strcpy(p, server_cb->cli_fn); if (condlen) { p += xml_strcpy(p, FALSE_PROMPT); } xml_strcpy(p, (const xmlChar *)"> "); } else if (cond) { xml_strncpy(buff, DEF_PROMPT, bufflen); } else { xml_strncpy(buff, DEF_FALSE_PROMPT, bufflen); } } else if (cond ) { xml_strncpy(buff, DEF_PROMPT, bufflen); } else { xml_strncpy(buff, DEF_FALSE_PROMPT, bufflen); } break; case MGR_IO_ST_CONN_IDLE: case MGR_IO_ST_CONN_RPYWAIT: case MGR_IO_ST_CONN_CANCELWAIT: case MGR_IO_ST_CONN_CLOSEWAIT: case MGR_IO_ST_CONN_SHUT: p = buff; len = xml_strncpy(p, (const xmlChar *)"yangcli ", bufflen); p += len; bufflen -= len; if (bufflen == 0) { return; } parm = NULL; if (server_cb->connect_valset) { parm = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_USER); } if (!parm && get_connect_valset()) { parm = val_find_child(get_connect_valset(), YANGCLI_MOD, YANGCLI_USER); } if (parm) { len = xml_strncpy(p, VAL_STR(parm), bufflen); p += len; bufflen -= len; if (bufflen == 0) { return; } *p++ = NCX_AT_CH; --bufflen; if (bufflen == 0) { return; } } parm = NULL; if (server_cb->connect_valset) { parm = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_SERVER); } if (!parm && get_connect_valset()) { parm= val_find_child(get_connect_valset(), YANGCLI_MOD, YANGCLI_SERVER); } if (parm) { len = xml_strncpy(p, VAL_STR(parm), bufflen); p += len; bufflen -= len; if (bufflen == 0) { return; } } if (server_cb->cli_fn && bufflen > 3) { *p++ = ':'; len = xml_strncpy(p, server_cb->cli_fn, --bufflen); p += len; bufflen -= len; } if (!cond) { len = xml_strlen(FALSE_PROMPT); if (bufflen > len) { p += xml_strcpy(p, FALSE_PROMPT); bufflen -= len; } } if (bufflen > 2) { *p++ = '>'; *p++ = ' '; *p = 0; bufflen -= 2; } break; default: SET_ERROR(ERR_INTERNAL_VAL); xml_strncpy(buff, DEF_PROMPT, bufflen); break; } } /* get_prompt */ /******************************************************************** * get_line * * Generate a prompt based on the program state and * use the tecla library read function to handle * user keyboard input * * * Do not free this line after use, just discard it * It will be freed by the tecla lib * * INPUTS: * server_cb == server control block to use * * RETURNS: * static line from tecla read line, else NULL if some error *********************************************************************/ static xmlChar * get_line (server_cb_t *server_cb) { xmlChar *line; xmlChar prompt[MAX_PROMPT_LEN]; GlReturnStatus returnstatus; line = NULL; get_prompt(server_cb, prompt, MAX_PROMPT_LEN-1); if (!server_cb->climore) { log_stdout("\n"); } server_cb->returncode = 0; if (server_cb->history_line_active) { line = (xmlChar *) gl_get_line(server_cb->cli_gl, (const char *)prompt, (const char *)server_cb->history_line, -1); server_cb->history_line_active = FALSE; } else { line = (xmlChar *) gl_get_line(server_cb->cli_gl, (const char *)prompt, NULL, -1); } if (!line) { if (server_cb->returncode == MGR_IO_RC_DROPPED || server_cb->returncode == MGR_IO_RC_DROPPED_NOW) { log_write("\nSession was dropped by the server"); clear_server_cb_session(server_cb); return NULL; } returnstatus = gl_return_status(server_cb->cli_gl); /* treat signal as control-C warning; about to exit */ if (returnstatus == GLR_SIGNAL) { log_warn("\nWarning: Control-C exit\n\n"); return NULL; } /* skip ordinary line canceled code except if debug2 */ if (returnstatus == GLR_TIMEOUT && !LOGDEBUG2) { return NULL; } log_error("\nError: GetLine returned "); switch (returnstatus) { case GLR_NEWLINE: log_error("NEWLINE"); break; case GLR_BLOCKED: log_error("BLOCKED"); break; case GLR_SIGNAL: log_error("SIGNAL"); break; case GLR_TIMEOUT: log_error("TIMEOUT"); break; case GLR_FDABORT: log_error("FDABORT"); break; case GLR_EOF: log_error("EOF"); break; case GLR_ERROR: log_error("ERROR"); break; default: log_error(""); } log_error(" (rt:%u errno:%u)", server_cb->returncode, server_cb->errnocode); } return line; } /* get_line */ /******************************************************************** * FUNCTION try_parse_def * * Parse the possibly module-qualified definition (module:def) * and find the template for the requested definition * * !!! DO NOT USE DIRECTLY !!! * !!! CALLED ONLY BY parse_def !!! * * INPUTS: * server_cb == server control block to use * mymod == module struct if available (may be NULL) * modname == module name to try * defname == definition name to try * dtyp == definition type * * OUTPUTS: * *dtyp is set if it started as NONE * * RETURNS: * pointer to the found definition template or NULL if not found *********************************************************************/ static void * try_parse_def (server_cb_t *server_cb, ncx_module_t *mymod, const xmlChar *modname, const xmlChar *defname, ncx_node_t *dtyp) { void *def; ncx_module_t *mod; if (mymod != NULL) { mod = mymod; } else { mod = find_module(server_cb, modname); if (!mod) { return NULL; } } def = NULL; switch (*dtyp) { case NCX_NT_NONE: def = ncx_find_object(mod, defname); if (def) { *dtyp = NCX_NT_OBJ; break; } def = ncx_find_grouping(mod, defname, FALSE); if (def) { *dtyp = NCX_NT_GRP; break; } def = ncx_find_type(mod, defname, FALSE); if (def) { *dtyp = NCX_NT_TYP; break; } break; case NCX_NT_OBJ: def = ncx_find_object(mod, defname); break; case NCX_NT_GRP: def = ncx_find_grouping(mod, defname, FALSE); break; case NCX_NT_TYP: def = ncx_find_type(mod, defname, FALSE); break; default: def = NULL; SET_ERROR(ERR_INTERNAL_VAL); } return def; } /* try_parse_def */ /******************************************************************** * FUNCTION get_yesno * * Get the user-selected choice, yes or no * * INPUTS: * server_cb == server control block to use * prompt == prompt message * defcode == default answer code * 0 == no def answer * 1 == def answer is yes * 2 == def answer is no * retcode == address of return code * * OUTPUTS: * *retcode is set if return NO_ERR * 0 == cancel * 1 == yes * 2 == no * * RETURNS: * status *********************************************************************/ static status_t get_yesno (server_cb_t *server_cb, const xmlChar *prompt, uint32 defcode, uint32 *retcode) { xmlChar *myline, *str; status_t res; boolean done; done = FALSE; res = NO_ERR; set_completion_state(&server_cb->completion_state, NULL, NULL, CMD_STATE_YESNO); if (prompt) { log_stdout("\n%s", prompt); } log_stdout("\nEnter Y for yes, N for no, or C to cancel:"); switch (defcode) { case YESNO_NODEF: break; case YESNO_YES: log_stdout(" [default: Y]"); break; case YESNO_NO: log_stdout(" [default: N]"); break; default: res = SET_ERROR(ERR_INTERNAL_VAL); done = TRUE; } while (!done) { /* get input from the user STDIN */ myline = get_cmd_line(server_cb, &res); if (!myline) { done = TRUE; continue; } /* strip leading whitespace */ str = myline; while (*str && xml_isspace(*str)) { str++; } /* convert to a number, check [ENTER] for default */ if (*str) { if (*str == 'Y' || *str == 'y') { *retcode = YESNO_YES; done = TRUE; } else if (*str == 'N' || *str == 'n') { *retcode = YESNO_NO; done = TRUE; } else if (*str == 'C' || *str == 'c') { *retcode = YESNO_CANCEL; done = TRUE; } } else { /* default accepted */ switch (defcode) { case YESNO_NODEF: break; case YESNO_YES: *retcode = YESNO_YES; done = TRUE; break; case YESNO_NO: *retcode = YESNO_NO; done = TRUE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); done = TRUE; } } if (res == NO_ERR && !done) { log_stdout("\nError: invalid value '%s'\n", str); } } return res; } /* get_yesno */ /******************************************************************** * FUNCTION get_parm * * Fill the specified parm * Use the old value for the default if any * This function will block on readline to get input from the user * * INPUTS: * server_cb == server control block to use * rpc == RPC method that is being called * parm == parm to get from the CLI * valset == value set being filled * oldvalset == last set of values (NULL if none) * * OUTPUTS: * new val_value_t node will be added to valset if NO_ERR * * RETURNS: * status *********************************************************************/ static status_t get_parm (server_cb_t *server_cb, obj_template_t *rpc, obj_template_t *parm, val_value_t *valset, val_value_t *oldvalset) { const xmlChar *def, *parmname, *str; val_value_t *oldparm, *newparm; xmlChar *line, *start, *objbuff, *buff; xmlChar *line2, *start2, *saveline; status_t res; ncx_btype_t btyp; boolean done, ispassword, iscomplex; uint32 len; if (!obj_is_mandatory(parm) && !server_cb->get_optional) { return NO_ERR; } def = NULL; iscomplex = FALSE; btyp = obj_get_basetype(parm); res = NO_ERR; ispassword = obj_is_password(parm); if (obj_is_data_db(parm) && parm->objtype != OBJ_TYP_RPCIO) { objbuff = NULL; res = obj_gen_object_id(parm, &objbuff); if (res != NO_ERR) { log_error("\nError: generate object ID failed (%s)", get_error_string(res)); return res; } /* let the user know about the new nest level */ if (obj_is_key(parm)) { str = YANG_K_KEY; } else if (ispassword) { str = YANGCLI_PASSWORD; } else if (obj_is_mandatory(parm)) { str = YANG_K_MANDATORY; } else { str = YANGCLI_OPTIONAL; } log_stdout("\nFilling %s %s %s:", str, obj_get_typestr(parm), objbuff); m__free(objbuff); } switch (btyp) { case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_CONTAINER: iscomplex = TRUE; break; /* return get_complex_parm(server_cb, parm, valset); */ default: ; } parmname = obj_get_name(parm); btyp = obj_get_basetype(parm); res = NO_ERR; oldparm = NULL; done = FALSE; while (!done) { if (btyp==NCX_BT_EMPTY) { log_stdout("\nShould flag %s be set? [Y, N]", parmname); } else if (iscomplex) { log_stdout("\nEnter value for %s <%s>", obj_get_typestr(parm), parmname); } else { log_stdout("\nEnter %s value for %s <%s>", (const xmlChar *)tk_get_btype_sym(btyp), obj_get_typestr(parm), parmname); } if (oldvalset) { oldparm = val_find_child(oldvalset, obj_get_mod_name(parm), parmname); } /* pick a default value, either old value or default clause */ if (oldparm) { /* use the old value for the default */ if (btyp==NCX_BT_EMPTY) { log_stdout(" [Y]"); } else if (iscomplex) { log_stdout(" [%s contents not shown]", obj_get_typestr(parm)); } else { res = val_sprintf_simval_nc(NULL, oldparm, &len); if (res != NO_ERR) { return SET_ERROR(res); } buff = m__getMem(len+1); if (!buff) { return ERR_INTERNAL_MEM; } res = val_sprintf_simval_nc(buff, oldparm, &len); if (res == NO_ERR) { log_stdout(" [%s]", buff); } m__free(buff); } } else { def = NULL; /* try to get the defined default value */ if (btyp != NCX_BT_EMPTY && !iscomplex) { def = obj_get_default(parm); if (!def && (obj_get_nsid(rpc) == xmlns_nc_id() && (!xml_strcmp(parmname, NCX_EL_TARGET) || !xml_strcmp(parmname, NCX_EL_SOURCE)))) { /* offer the default target for the NETCONF * and parameters */ def = server_cb->default_target; } } if (def) { log_stdout(" [%s]\n", def); } else if (btyp==NCX_BT_EMPTY) { log_stdout(" [N]\n"); } } set_completion_state(&server_cb->completion_state, rpc, parm, CMD_STATE_GETVAL); /* get a line of input from the user * but don't echo if this is a password parm */ if (ispassword) { /* ignore return value which is previous mode */ (void)gl_echo_mode(server_cb->cli_gl, 0); } line = get_cmd_line(server_cb, &res); if (ispassword) { /* ignore return value which is previous mode */ (void)gl_echo_mode(server_cb->cli_gl, 1); } if (!line) { return res; } /* skip whitespace */ start = line; while (*start && xml_isspace(*start)) { start++; } /* check for question-mark char sequences */ if (*start == '?') { if (start[1] == '?') { /* ?? == full help */ obj_dump_template(parm, HELP_MODE_FULL, 0, server_cb->defindent); } else if (start[1] == 'C' || start[1] == 'c') { /* ?c or ?C == cancel the operation */ log_stdout("\n%s command canceled", obj_get_name(rpc)); return ERR_NCX_CANCELED; } else if (start[1] == 'S' || start[1] == 's') { /* ?s or ?S == skip this parameter */ log_stdout("\n%s parameter skipped", obj_get_name(parm)); return ERR_NCX_SKIPPED; } else { /* ? == normal help mode */ obj_dump_template(parm, HELP_MODE_NORMAL, 4, server_cb->defindent); } log_stdout("\n"); continue; } else { /* top loop to get_parm is only executed once */ done = TRUE; } } /* check if any non-whitespace chars entered */ if (!*start) { /* no input, use default or old value or EMPTY_STRING */ if (def) { /* use default */ res = cli_parse_parm_ex(server_cb->runstack_context, valset, parm, def, SCRIPTMODE, get_baddata()); } else if (oldparm) { /* no default, try old value */ if (btyp==NCX_BT_EMPTY) { res = cli_parse_parm_ex(server_cb->runstack_context, valset, parm, NULL, SCRIPTMODE, get_baddata()); } else { /* use a copy of the last value */ newparm = val_clone(oldparm); if (!newparm) { res = ERR_INTERNAL_MEM; } else { val_add_child(newparm, valset); } } } else if (btyp == NCX_BT_EMPTY) { res = cli_parse_parm_ex(server_cb->runstack_context, valset, parm, NULL, SCRIPTMODE, get_baddata()); } else if (!iscomplex && (val_simval_ok(obj_get_typdef(parm), EMPTY_STRING) == NO_ERR)) { res = cli_parse_parm_ex(server_cb->runstack_context, valset, parm, EMPTY_STRING, SCRIPTMODE, get_baddata()); } else { /* data type requires some form of input */ res = ERR_NCX_DATA_MISSING; } } else if (btyp==NCX_BT_EMPTY) { /* empty data type handled special Y: set, N: leave out */ if (*start=='Y' || *start=='y') { res = cli_parse_parm_ex(server_cb->runstack_context, valset, parm, NULL, SCRIPTMODE, get_baddata()); } else if (*start=='N' || *start=='n') { ; /* skip; do not add the flag */ } else if (oldparm) { /* previous value was set, so add this flag */ res = cli_parse_parm_ex(server_cb->runstack_context, valset, parm, NULL, SCRIPTMODE, get_baddata()); } else { /* some value was entered, other than Y or N */ res = ERR_NCX_WRONG_VAL; } } else { /* normal case: input for regular data type */ res = cli_parse_parm_ex(server_cb->runstack_context, valset, parm, start, SCRIPTMODE, get_baddata()); } /* got some value, now check if it is OK * depending on the --bad-data CLI parameter, * the value may be entered over again, accepted, * or rejected with an invalid-value error */ if (res != NO_ERR) { switch (get_baddata()) { case NCX_BAD_DATA_IGNORE: case NCX_BAD_DATA_WARN: /* if these modes had error return status then the * problem was not invalid value; maybe malloc error */ break; case NCX_BAD_DATA_CHECK: if (NEED_EXIT(res)) { break; } saveline = (start) ? xml_strdup(start) : xml_strdup(EMPTY_STRING); if (!saveline) { res = ERR_INTERNAL_MEM; break; } done = FALSE; while (!done) { log_stdout("\nError: parameter '%s' value '%s' is invalid" "\nShould this value be used anyway? [Y, N]" " [N]", obj_get_name(parm), (start) ? start : EMPTY_STRING); /* save the previous value because it is about * to get trashed by getting a new line */ /* get a line of input from the user */ line2 = get_cmd_line(server_cb, &res); if (!line2) { m__free(saveline); return res; } /* skip whitespace */ start2 = line2; while (*start2 && xml_isspace(*start2)) { start2++; } /* check for question-mark char sequences */ if (!*start2) { /* default N: try again for a different input */ m__free(saveline); res = get_parm(server_cb, rpc, parm, valset, oldvalset); done = TRUE; } else if (*start2 == '?') { if (start2[1] == '?') { /* ?? == full help */ obj_dump_template(parm, HELP_MODE_FULL, 0, server_cb->defindent); } else if (start2[1] == 'C' || start2[1] == 'c') { /* ?c or ?C == cancel the operation */ log_stdout("\n%s command canceled", obj_get_name(rpc)); m__free(saveline); res = ERR_NCX_CANCELED; done = TRUE; } else if (start2[1] == 'S' || start2[1] == 's') { /* ?s or ?S == skip this parameter */ log_stdout("\n%s parameter skipped", obj_get_name(parm)); m__free(saveline); res = ERR_NCX_SKIPPED; done = TRUE; } else { /* ? == normal help mode */ obj_dump_template(parm, HELP_MODE_NORMAL, 4, server_cb->defindent); } log_stdout("\n"); continue; } else if (*start2 == 'Y' || *start2 == 'y') { /* use the invalid value */ res = cli_parse_parm_ex(server_cb->runstack_context, valset, parm, saveline, SCRIPTMODE, NCX_BAD_DATA_IGNORE); m__free(saveline); done = TRUE; } else if (*start2 == 'N' || *start2 == 'n') { /* recurse: try again for a different input */ m__free(saveline); res = get_parm(server_cb, rpc, parm, valset, oldvalset); done = TRUE; } else { log_stdout("\nInvalid input."); } } break; case NCX_BAD_DATA_ERROR: log_stdout("\nError: set parameter '%s' failed (%s)\n", obj_get_name(parm), get_error_string(res)); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } return res; } /* get_parm */ /******************************************************************** * FUNCTION get_case * * Get the user-selected case, which is not fully set * Use values from the last set (if any) for defaults. * This function will block on readline if mandatory parms * are needed from the CLI * * INPUTS: * server_cb == server control block to use * cas == case object template header * valset == value set to fill * oldvalset == last set of values (or NULL if none) * iswrite == TRUE if write op; FALSE if not * isdelete == TRUE if delete op; FALSE if not * OUTPUTS: * new val_value_t nodes may be added to valset * * RETURNS: * status *********************************************************************/ static status_t get_case (server_cb_t *server_cb, obj_template_t *cas, val_value_t *valset, val_value_t *oldvalset, boolean iswrite, boolean isdelete) { status_t res = NO_ERR; boolean saveopt = server_cb->get_optional; /* make sure a case was selected or found */ if (!obj_is_config(cas) || obj_is_abstract(cas)) { log_stdout("\nError: No writable objects to fill for this case"); return ERR_NCX_SKIPPED; } if (obj_is_data_db(cas)) { xmlChar *objbuff = NULL; const xmlChar *str = NULL; res = obj_gen_object_id(cas, &objbuff); if (res != NO_ERR) { log_error("\nError: generate object ID failed (%s)", get_error_string(res)); return res; } /* let the user know about the new nest level */ if (obj_is_mandatory(cas)) { str = YANG_K_MANDATORY; } else { str = (const xmlChar *)"optional"; } log_stdout("\nFilling %s case %s:", str, objbuff); m__free(objbuff); } /* corner-case: user selected a case, and that case has * one empty leaf in it; * e.g., and parms */ if (obj_get_child_count(cas) == 1) { obj_template_t *parm = obj_first_child(cas); if (parm && obj_get_basetype(parm)==NCX_BT_EMPTY) { /* user picked a case that is an empty flag so * do not call fill_valset for this corner-case * because the user will be prompted again * 'should this flag be set? y/n' * Instead, call cli_parse_parm directly */ val_value_t *parmval = val_find_child(valset, obj_get_mod_name(parm), obj_get_name(parm)); if (parmval == NULL || parmval->obj->objtype == OBJ_TYP_LEAF_LIST) { return cli_parse_parm(server_cb->runstack_context, valset, parm, NULL, FALSE); } } } res = fill_valset(server_cb, cas, valset, oldvalset, iswrite, isdelete); server_cb->get_optional = saveopt; return res; } /* get_case */ /******************************************************************** * FUNCTION enabled_case_count * * Get the number of enabled cases * * INPUTS: * server_cb == server control block to use * choic == choice object template header * iswrite == TRUE for write operation * RETURNS: * number of enabled cases *********************************************************************/ static uint32 enabled_case_count (server_cb_t *server_cb, obj_template_t *choic, boolean iswrite) { uint32 case_count = 0; obj_template_t *cas = obj_first_child(choic); for (; cas != NULL; cas = obj_next_child(cas)) { if ((!server_cb->get_optional && !iswrite) || !need_get_parm(cas)) { continue; } obj_template_t *parm = obj_first_child(cas); if (parm) { case_count++; } } return case_count; } /* enabled_case_count */ /******************************************************************** * FUNCTION first_enabled_case * * Get the first enabled case * * INPUTS: * server_cb == server control block to use * choic == choice object template header * iswrite == TRUE for write operation * RETURNS: * number of enabled cases *********************************************************************/ static obj_template_t * first_enabled_case (server_cb_t *server_cb, obj_template_t *choic, boolean iswrite) { obj_template_t *cas = obj_first_child(choic); for (; cas != NULL; cas = obj_next_child(cas)) { if ((!server_cb->get_optional && !iswrite) || !need_get_parm(cas)) { continue; } obj_template_t *parm = obj_first_child(cas); if (parm) { return cas; } } return NULL; } /* first_enabled_case */ /******************************************************************** * FUNCTION get_choice * * Get the user-selected choice, which is not fully set * Use values from the last set (if any) for defaults. * This function will block on readline if mandatory parms * are needed from the CLI * * INPUTS: * server_cb == server control block to use * rpc == RPC template in progress * choic == choice object template header * valset == value set to fill * oldvalset == last set of values (or NULL if none) * iswrite == TRUE if write op; FALSE if not * isdelete == TRUE if delete op; FALSE if not * * OUTPUTS: * new val_value_t nodes may be added to valset * * RETURNS: * status *********************************************************************/ static status_t get_choice (server_cb_t *server_cb, obj_template_t *rpc, obj_template_t *choic, val_value_t *valset, val_value_t *oldvalset, boolean iswrite, boolean isdelete) { obj_template_t *cas = NULL; status_t res = NO_ERR; boolean done = FALSE; if (!obj_is_config(choic) || obj_is_abstract(choic)) { log_stdout("\nError: choice '%s' has no configurable parameters", obj_get_name(choic)); return ERR_NCX_ACCESS_DENIED; } int casenum = 0; if (obj_is_data_db(choic)) { xmlChar *objbuff = NULL; res = obj_gen_object_id(choic, &objbuff); if (res != NO_ERR) { log_error("\nError: generate object ID failed (%s)", get_error_string(res)); return res; } /* let the user know about the new nest level */ log_stdout("\nFilling choice %s:", objbuff); m__free(objbuff); } boolean saveopt = server_cb->get_optional; /* only force optional=true if this is a mandatory choice * otherwise, a mandatory choice of all optional cases * will just get skipped over; * only set to true for reads if --optional also set */ if (((saveopt && !iswrite) && obj_is_mandatory(choic)) || (iswrite && obj_is_mandatory(choic))) { server_cb->get_optional = TRUE; } /* first check the partial block corner case */ val_value_t *pval = val_get_choice_first_set(valset, choic); if (pval != NULL) { /* found something set from this choice, finish the case */ log_stdout("\nEnter more parameters to complete the choice:"); cas = pval->casobj; if (cas == NULL) { server_cb->get_optional = saveopt; return SET_ERROR(ERR_INTERNAL_VAL); } obj_template_t *parm = obj_first_child(cas); for (; parm != NULL; parm = obj_next_child(parm)) { if ((!server_cb->get_optional && !iswrite) || !need_get_parm(parm)) { continue; } pval = val_find_child(valset, obj_get_mod_name(parm), obj_get_name(parm)); if (pval) { continue; /* node within case already set */ } res = get_parm(server_cb, rpc, parm, valset, oldvalset); switch (res) { case NO_ERR: break; case ERR_NCX_SKIPPED: res = NO_ERR; break; case ERR_NCX_CANCELED: server_cb->get_optional = saveopt; return res; default: server_cb->get_optional = saveopt; return res; } } server_cb->get_optional = saveopt; return NO_ERR; } /* get number of enabled cases */ uint32 case_count = enabled_case_count(server_cb, choic, iswrite); boolean usedef = FALSE; /* check corner-case -- choice with no cases defined */ cas = obj_first_child(choic); if (cas == NULL) { if (case_count) { log_stdout("\nNo case nodes enabled for choice %s\n", obj_get_name(choic)); } else { log_stdout("\nNo case nodes defined for choice %s\n", obj_get_name(choic)); } server_cb->get_optional = saveopt; return NO_ERR; } if (case_count == 1) { cas = first_enabled_case(server_cb, choic, iswrite); } else { /* else not a partial block corner case but a normal * situation where no case has been selected at all */ log_stdout("\nEnter the number of the selected case statement:\n"); int num = 1; for (; cas != NULL; cas = obj_next_child(cas)) { if ((!server_cb->get_optional && !iswrite) || !need_get_parm(cas)) { continue; } boolean first = TRUE; obj_template_t *parm = obj_first_child(cas); for (; parm != NULL; parm = obj_next_child(parm)) { if (!obj_is_config(parm) || obj_is_abstract(parm)) { continue; } if (first) { log_stdout("\n %d: case %s:", num++, obj_get_name(cas)); first = FALSE; } log_stdout("\n %s %s", obj_get_typestr(parm), obj_get_name(parm)); } } done = FALSE; log_stdout("\n"); while (!done) { boolean redo = FALSE; /* Pick a prompt, depending on the choice default case */ if (obj_get_default(choic)) { log_stdout("\nEnter choice number [%d - %d], " "[ENTER] for default (%s): ", 1, num-1, obj_get_default(choic)); } else { log_stdout("\nEnter choice number [%d - %d]: ", 1, num-1); } /* get input from the user STDIN */ xmlChar *myline = get_cmd_line(server_cb, &res); if (!myline) { server_cb->get_optional = saveopt; return res; } /* strip leading whitespace */ xmlChar *str = myline; while (*str && xml_isspace(*str)) { str++; } /* convert to a number, check [ENTER] for default */ if (!*str) { usedef = TRUE; } else if (*str == '?') { redo = TRUE; if (str[1] == '?') { obj_dump_template(choic, HELP_MODE_FULL, 0, server_cb->defindent); } else if (str[1] == 'C' || str[1] == 'c') { log_stdout("\n%s command canceled\n", obj_get_name(rpc)); server_cb->get_optional = saveopt; return ERR_NCX_CANCELED; } else if (str[1] == 'S' || str[1] == 's') { log_stdout("\n%s choice skipped\n", obj_get_name(choic)); server_cb->get_optional = saveopt; return ERR_NCX_SKIPPED; } else { obj_dump_template(choic, HELP_MODE_NORMAL, 4, server_cb->defindent); } log_stdout("\n"); } else { casenum = atoi((const char *)str); usedef = FALSE; } if (redo) { continue; } /* check if default requested */ if (usedef) { if (obj_get_default(choic)) { done = TRUE; } else { log_stdout("\nError: Choice does not have " "a default case\n"); usedef = FALSE; } } else if (casenum < 0 || casenum >= num) { log_stdout("\nError: invalid value '%s'\n", str); } else { done = TRUE; } } } /* got a valid choice number or use the default case * now get the object template for the correct case */ if (usedef) { cas = obj_find_child(choic, obj_get_mod_name(choic), obj_get_default(choic)); } else if (case_count > 1) { int num = 1; done = FALSE; obj_template_t *usecase = NULL; for (cas = obj_first_child(choic); cas != NULL && !done; cas = obj_next_child(cas)) { if ((!server_cb->get_optional && !iswrite) || !need_get_parm(cas)) { continue; } if (obj_first_child(cas) == NULL) { /* skip case if no enabled children */ continue; } if (casenum == num) { done = TRUE; usecase = cas; } else { num++; } } cas = usecase; } /* make sure a case was selected or found */ if (!cas) { log_stdout("\nError: No case to fill for this choice"); server_cb->get_optional = saveopt; return ERR_NCX_SKIPPED; } res = get_case(server_cb, cas, valset, oldvalset, iswrite, isdelete); switch (res) { case NO_ERR: break; case ERR_NCX_SKIPPED: res = NO_ERR; break; case ERR_NCX_CANCELED: break; default: ; } server_cb->get_optional = saveopt; return res; } /* get_choice */ /******************************************************************** * FUNCTION fill_value * * Malloc and fill the specified value * Use the last value for the value if it is present and valid * This function will block on readline for user input if no * valid useval is given * * INPUTS: * server_cb == server control block to use * rpc == RPC method that is being called * parm == object for value to fill * useval == value to use (or NULL if none) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced value, filled in or NULL if some error *********************************************************************/ static val_value_t * fill_value (server_cb_t *server_cb, obj_template_t *rpc, obj_template_t *parm, val_value_t *useval, status_t *res) { obj_template_t *parentobj; val_value_t *dummy, *newval; boolean saveopt; switch (parm->objtype) { case OBJ_TYP_ANYXML: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: break; default: *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } if (obj_is_abstract(parm)) { *res = ERR_NCX_NO_ACCESS_MAX; log_error("\nError: no access to abstract objects"); return NULL; } /* just copy the useval if it is given */ if (useval) { newval = val_clone(useval); if (!newval) { log_error("\nError: malloc failed"); return NULL; } if (newval->obj != parm) { /* force the new value to have the QName of 'parm' */ val_change_nsid(newval, obj_get_nsid(parm)); val_set_name(newval, obj_get_name(parm), xml_strlen(obj_get_name(parm))); } return newval; } /* since the CLI and original NCX language were never designed * to support top-level leafs, a dummy container must be * created for the new and old leaf or leaf-list entries */ if (parm->parent && !obj_is_root(parm->parent)) { parentobj = parm->parent; } else { parentobj = ncx_get_gen_container(); } dummy = val_new_value(); if (!dummy) { *res = ERR_INTERNAL_MEM; log_error("\nError: malloc failed"); return NULL; } val_init_from_template(dummy, parentobj); server_cb->cli_fn = obj_get_name(rpc); newval = NULL; saveopt = server_cb->get_optional; server_cb->get_optional = TRUE; set_completion_state(&server_cb->completion_state, rpc, parm, CMD_STATE_GETVAL); *res = get_parm(server_cb, rpc, parm, dummy, NULL); server_cb->get_optional = saveopt; if (*res == NO_ERR) { newval = val_get_first_child(dummy); if (newval) { val_remove_child(newval); } } server_cb->cli_fn = NULL; val_free_value(dummy); return newval; } /* fill_value */ /******************************************************************** * FUNCTION fill_valset * * Fill the specified value set with any missing parameters. * Use values from the last set (if any) for defaults. * This function will block on readline if mandatory parms * are needed from the CLI * * INPUTS: * server_cb == current server control block (NULL if none) * rpc == RPC method that is being called * valset == value set to fill * oldvalset == last set of values (or NULL if none) * iswrite == TRUE if write command; FALSE if not * isdelete == TRUE if delete operation; FALSE if not * OUTPUTS: * new val_value_t nodes may be added to valset * * RETURNS: * status,, valset may be partially filled if not NO_ERR *********************************************************************/ static status_t fill_valset (server_cb_t *server_cb, obj_template_t *rpc, val_value_t *valset, val_value_t *oldvalset, boolean iswrite, boolean isdelete) { obj_template_t *parm, *target; val_value_t *val, *oldval; val_value_t *firstchoice = NULL; status_t res = NO_ERR; boolean done; uint32 yesnocode; if (rpc->objtype == OBJ_TYP_CASE) { /* called by get_case */ target = rpc; } else if (rpc->objtype == OBJ_TYP_RPC) { /* called by RPC invocation or fill data fn */ server_cb->cli_fn = obj_get_name(rpc); if(valset->obj->objtype == OBJ_TYP_RPC) { target = obj_find_child(valset->obj, NULL, YANG_K_INPUT); } else { target = valset->obj; } } else { /* called by fill data fn */ target = valset->obj; } if (!(target->objtype == OBJ_TYP_RPC || target->objtype == OBJ_TYP_RPCIO || target->objtype == OBJ_TYP_CASE)) { xmlChar *objbuff = NULL; res = obj_gen_object_id(target, &objbuff); if (res != NO_ERR) { log_error("\nError: generate object ID failed (%s)", get_error_string(res)); return res; } /* let the user know about the new nest level */ log_stdout("\nFilling %s %s:", obj_get_typestr(target), objbuff); m__free(objbuff); } for (parm = obj_first_child(target); parm != NULL && res==NO_ERR; parm = obj_next_child(parm)) { if ((!server_cb->get_optional && !iswrite) || !need_get_parm(parm)) { if (oldvalset != NULL) { res = clone_old_parm(oldvalset, valset, parm); } continue; } if (isdelete && !obj_is_key(parm)) { continue; } if (!server_cb->get_optional) { if (!obj_is_mandatory(parm)) { if (oldvalset != NULL) { res = clone_old_parm(oldvalset, valset, parm); } continue; } if (!iswrite && !obj_is_key(parm)) { if (oldvalset != NULL) { res = clone_old_parm(oldvalset, valset, parm); } continue; } if(obj_is_mandatory(parm) && parm->augobj!=NULL && parm->augobj->when) { /* mandatory object added through augment YANG 1.1 and above */ if (oldvalset != NULL) { res = clone_old_parm(oldvalset, valset, parm); } continue; } } set_completion_state(&server_cb->completion_state, rpc, parm, CMD_STATE_GETVAL); switch (parm->objtype) { case OBJ_TYP_CHOICE: firstchoice = val_get_choice_first_set(valset, parm); if (firstchoice) { assert( firstchoice->casobj && "case backptr is NULL!" ); /* a case is already selected so try finishing that */ res = get_case(server_cb, firstchoice->casobj, valset, oldvalset, iswrite, isdelete); } else { res = get_choice(server_cb, rpc, parm, valset, oldvalset, iswrite, isdelete); switch (res) { case NO_ERR: break; case ERR_NCX_SKIPPED: res = NO_ERR; break; case ERR_NCX_CANCELED: break; default: ; } } break; case OBJ_TYP_ANYXML: case OBJ_TYP_LEAF: val = val_find_child(valset, obj_get_mod_name(parm), obj_get_name(parm)); if (val == NULL) { res = get_parm(server_cb, rpc, parm, valset, oldvalset); switch (res) { case NO_ERR: break; case ERR_NCX_SKIPPED: res = NO_ERR; break; case ERR_NCX_CANCELED: break; default: ; } } break; case OBJ_TYP_LEAF_LIST: done = FALSE; while (!done && res == NO_ERR) { res = get_parm(server_cb, rpc, parm, valset, oldvalset); switch (res) { case NO_ERR: /* prompt for more leaf-list objects */ res = get_yesno(server_cb, YANGCLI_PR_LLIST, YESNO_NO, &yesnocode); if (res == NO_ERR) { switch (yesnocode) { case YESNO_CANCEL: res = ERR_NCX_CANCELED; break; case YESNO_YES: break; case YESNO_NO: done = TRUE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } break; case ERR_NCX_SKIPPED: done = TRUE; res = NO_ERR; break; case ERR_NCX_CANCELED: break; default: ; } } break; case OBJ_TYP_CONTAINER: if(!obj_is_mandatory(parm)) { /* prompt for confirmation non-mandatory container is to be filled */ char* buf; char fmt[]="Fill non-mandatory container %s?"; buf=malloc(strlen(fmt)+strlen(obj_get_name(parm))+1); assert(buf); sprintf(buf,fmt,obj_get_name(parm)); res = get_yesno(server_cb, buf, YESNO_NO, &yesnocode); free(buf); if (res == NO_ERR) { switch (yesnocode) { case YESNO_CANCEL: res = ERR_NCX_CANCELED; break; case YESNO_NO: continue; case YESNO_YES: break; default: assert(0); } if(res!=NO_ERR) { break; } } } case OBJ_TYP_NOTIF: case OBJ_TYP_RPCIO: /* if the parm is not already set and is not read-only * then try to get a value from the user at the CLI */ val = val_find_child(valset, obj_get_mod_name(parm), obj_get_name(parm)); if (val) { break; } if (oldvalset) { oldval = val_find_child(oldvalset, obj_get_mod_name(parm), obj_get_name(parm)); } else { oldval = NULL; } val = val_new_value(); if (!val) { res = ERR_INTERNAL_MEM; log_error("\nError: malloc of new value failed"); break; } else { val_init_from_template(val, parm); val_add_child(val, valset); } /* recurse with the child nodes */ res = fill_valset(server_cb, parm, val, oldval, iswrite, isdelete); switch (res) { case NO_ERR: break; case ERR_NCX_SKIPPED: res = NO_ERR; break; case ERR_NCX_CANCELED: break; default: ; } break; case OBJ_TYP_LIST: done = FALSE; while (!done && res == NO_ERR) { val = val_new_value(); if (!val) { res = ERR_INTERNAL_MEM; log_error("\nError: malloc of new value failed"); continue; } else { val_init_from_template(val, parm); val_add_child(val, valset); } /* recurse with the child node -- NO OLD VALUE * TBD: get keys, then look up old matching entry */ res = fill_valset(server_cb, parm, val, NULL, iswrite, isdelete); switch (res) { case NO_ERR: /* prompt for more list entries */ res = get_yesno(server_cb, YANGCLI_PR_LIST, YESNO_NO, &yesnocode); if (res == NO_ERR) { switch (yesnocode) { case YESNO_CANCEL: res = ERR_NCX_CANCELED; break; case YESNO_YES: break; case YESNO_NO: done = TRUE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } break; case ERR_NCX_SKIPPED: res = NO_ERR; break; case ERR_NCX_CANCELED: break; default: ; } } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } server_cb->cli_fn = NULL; return res; } /* fill_valset */ /******************************************************************** * FUNCTION create_session * * Start a NETCONF session and change the program state * Since this is called in sequence with readline, the STDIN IO * handler may get called if the user enters keyboard text * * The STDIN handler will not do anything with incoming chars * while state == MGR_IO_ST_CONNECT * * INPUTS: * server_cb == server control block to use * * OUTPUTS: * 'server_cb->mysid' is set to the output session ID, if NO_ERR * 'server_cb->state' is changed based on the success of * the session setup * *********************************************************************/ void create_session (server_cb_t *server_cb) { const xmlChar *server, *username, *password; const xmlChar *privkeypass; const char *publickey, *privatekey; modptr_t *modptr; ncxmod_search_result_t *searchresult; val_value_t *val; status_t res; uint16 port; boolean startedsession, tcp, portbydefault; boolean tcp_direct_enable; boolean ssh_use_agent; if (LOGDEBUG) { log_debug("\nConnect attempt with following parameters:"); val_dump_value_max(server_cb->connect_valset, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); log_debug("\n"); } /* make sure session not already running */ if (server_cb->mysid) { if (mgr_ses_get_scb(server_cb->mysid)) { /* already connected; fn should not have been called */ SET_ERROR(ERR_INTERNAL_INIT_SEQ); return; } else { /* OK: reset session ID */ server_cb->mysid = 0; } } /* make sure no stale search results in the control block */ while (!dlq_empty(&server_cb->searchresultQ)) { searchresult = (ncxmod_search_result_t *) dlq_deque(&server_cb->searchresultQ); ncxmod_free_search_result(searchresult); } /* make sure no stale modules in the control block */ while (!dlq_empty(&server_cb->modptrQ)) { modptr = (modptr_t *)dlq_deque(&server_cb->modptrQ); free_modptr(modptr); } /* retrieving the parameters should not fail */ username = NULL; val = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_USER); if (val && val->res == NO_ERR) { username = VAL_STR(val); } else { SET_ERROR(ERR_INTERNAL_VAL); return; } server = NULL; val = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_SERVER); if (val && val->res == NO_ERR) { server = VAL_STR(val); } else { SET_ERROR(ERR_INTERNAL_VAL); return; } password = NULL; val = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_PASSWORD); if (val && val->res == NO_ERR) { password = VAL_STR(val); } port = 0; portbydefault = FALSE; val = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_NCPORT); if (val && val->res == NO_ERR) { port = VAL_UINT16(val); portbydefault = val_set_by_default(val); } publickey = NULL; val = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_PUBLIC_KEY); if (val && val->res == NO_ERR) { publickey = (const char *)VAL_STR(val); } privatekey = NULL; val = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_PRIVATE_KEY); if (val && val->res == NO_ERR) { privatekey = (const char *)VAL_STR(val); } privkeypass = NULL; val = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_PRIVATE_KEY_PASS); if (val && val->res == NO_ERR) { privkeypass = (const xmlChar *)VAL_STR(val); } tcp = FALSE; val = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_TRANSPORT); if (val != NULL && val->res == NO_ERR && !xml_strcmp(VAL_ENUM_NAME(val), (const xmlChar *)"tcp")) { tcp = TRUE; } tcp_direct_enable = FALSE; val = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_TCP_DIRECT_ENABLE); if(val == NULL) log_error("\nval is NULL."); if(val->res == NO_ERR) log_debug("\nval->res is NO_ERR."); if (val != NULL && val->res == NO_ERR && VAL_BOOL(val)) { tcp_direct_enable = TRUE; } if (tcp) { if (port == 0 || portbydefault) { port = SES_DEF_TCP_PORT; } } ssh_use_agent = FALSE; val = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_USE_AGENT); if (val != NULL && val->res == NO_ERR && VAL_BOOL(val)) { ssh_use_agent = TRUE; } log_info("\nyangcli: Starting NETCONF session for %s on %s", username, server); startedsession = FALSE; server_cb->state = MGR_IO_ST_CONNECT; /* this function call will cause us to block while the * protocol layer connect messages are processed */ res = mgr_ses_new_session2(username, password, publickey, privatekey, privkeypass, ssh_use_agent, server, port, (tcp) ? ((tcp_direct_enable) ? SES_TRANSPORT_TCP_DIRECT : SES_TRANSPORT_TCP) : SES_TRANSPORT_SSH, server_cb->temp_progcb, &server_cb->mysid, xpath_getvar_fn, server_cb->connect_valset); if (res == NO_ERR) { ses_cb_t *scb; mgr_scb_t *mscb; startedsession = TRUE; server_cb->state = MGR_IO_ST_CONN_START; log_debug("\nyangcli: Start session %d OK for server '%s'", server_cb->mysid, server_cb->name); res = mgr_set_getvar_fn(server_cb->mysid, xpath_getvar_fn); if (res != NO_ERR) { log_error("\nError: Could not set XPath variable callback"); } /* associate the server_cb with the session */ scb = mgr_ses_get_scb(server_cb->mysid); if(scb!=NULL) { assert(scb->mgrcb); mscb = (mgr_scb_t *)scb->mgrcb; mscb->context_ptr=(void*)server_cb; } } if (res != NO_ERR) { if (startedsession) { mgr_ses_free_session(server_cb->mysid); server_cb->mysid = 0; } log_info("\nyangcli: Start session failed for user %s on " "%s (%s)\n", username, server, get_error_string(res)); server_cb->state = MGR_IO_ST_IDLE; } } /* create_session */ /******************************************************************** * FUNCTION do_mgrload (local RPC) * * mgrload module=mod-name * * Get the module parameter and load the specified NCX module * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the load command * line == CLI input in progress * len == offset into line buffer to start parsing * * OUTPUTS: * specified module is loaded into the definition registry, if NO_ERR * * RETURNS: * status *********************************************************************/ static status_t do_mgrload (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *modval, *revval, *devval; ncx_module_t *mod; modptr_t *modptr; dlq_hdr_t savedevQ; logfn_t logfn; status_t res; modval = NULL; revval = NULL; res = NO_ERR; dlq_createSQue(&savedevQ); if (interactive_mode()) { logfn = log_stdout; } else { logfn = log_write; } valset = get_valset(server_cb, rpc, &line[len], &res); /* get the module name */ if (res == NO_ERR) { modval = val_find_child(valset, YANGCLI_MOD, NCX_EL_MODULE); if (!modval) { res = ERR_NCX_DEF_NOT_FOUND; } else if (modval->res != NO_ERR) { res = modval->res; } } /* get the module revision */ if (res == NO_ERR) { revval = val_find_child(valset, YANGCLI_MOD, NCX_EL_REVISION); } /* check if any version of the module is loaded already */ if (res == NO_ERR) { mod = ncx_find_module(VAL_STR(modval), NULL); if (mod) { if (mod->version) { (*logfn)("\nModule '%s' revision '%s' already loaded", mod->name, mod->version); } else { (*logfn)("\nModule '%s' already loaded", mod->name); } if (valset) { val_free_value(valset); } return res; } } /* check if there are any deviation parameters to load first */ if (res == NO_ERR) { for (devval = val_find_child(valset, YANGCLI_MOD, NCX_EL_DEVIATION); devval != NULL && res == NO_ERR; devval = val_find_next_child(valset, YANGCLI_MOD, NCX_EL_DEVIATION, devval)) { res = ncxmod_load_deviation(VAL_STR(devval), &savedevQ); } } /* load the module */ if (res == NO_ERR) { mod = NULL; res = ncxmod_load_module(VAL_STR(modval), (revval) ? VAL_STR(revval) : NULL, &savedevQ, &mod); if (res == NO_ERR) { /*** TBD: prompt user for features to enable/disable ***/ modptr = new_modptr(mod, NULL, NULL); if (!modptr) { res = ERR_INTERNAL_MEM; } else { dlq_enque(modptr, &server_cb->modptrQ); } } } /* print the result to stdout */ if (res == NO_ERR) { if (revval) { (*logfn)("\nLoad module '%s' revision '%s' OK", VAL_STR(modval), VAL_STR(revval)); } else { (*logfn)("\nLoad module '%s' OK", VAL_STR(modval)); } } else { (*logfn)("\nError: Load module failed (%s)", get_error_string(res)); } (*logfn)("\n"); if (valset) { val_free_value(valset); } ncx_clean_save_deviationsQ(&savedevQ); return res; } /* do_mgrload */ /******************************************************************** * FUNCTION do_help_commands (sub-mode of local RPC) * * help commands * * INPUTS: * server_cb == server control block to use * mode == requested help mode * * RETURNS: * status *********************************************************************/ static status_t do_help_commands (server_cb_t *server_cb, help_mode_t mode) { modptr_t *modptr; obj_template_t *obj; boolean anyout, imode; imode = interactive_mode(); anyout = FALSE; if (use_servercb(server_cb)) { if (imode) { log_stdout("\nServer Commands:\n"); } else { log_write("\nServer Commands:\n"); } for (modptr = (modptr_t *) dlq_firstEntry(&server_cb->modptrQ); modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { obj = ncx_get_first_object(modptr->mod); while (obj) { if (obj_is_rpc(obj) && !obj_is_hidden(obj)) { if (mode == HELP_MODE_BRIEF) { obj_dump_template(obj, mode, 1, 0); } else { obj_dump_template(obj, mode, 0, 0); } anyout = TRUE; } obj = ncx_get_next_object(modptr->mod, obj); } } } if (imode) { log_stdout("\nLocal Commands:\n"); } else { log_write("\nLocal Commands:\n"); } obj = ncx_get_first_object(get_yangcli_mod()); while (obj) { if (obj_is_rpc(obj) && !obj_is_hidden(obj)) { if (mode == HELP_MODE_BRIEF) { obj_dump_template(obj, mode, 1, 0); } else { obj_dump_template(obj, mode, 0, 0); } anyout = TRUE; } obj = ncx_get_next_object(get_yangcli_mod(), obj); } if (anyout) { if (imode) { log_stdout("\n"); } else { log_write("\n"); } } return NO_ERR; } /* do_help_commands */ /******************************************************************** * FUNCTION do_help * * Print the general yangcli help text to STDOUT * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the load command * line == CLI input in progress * len == offset into line buffer to start parsing * * OUTPUTS: * log_stdout global help message * * RETURNS: * status *********************************************************************/ static status_t do_help (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { typ_template_t *typ; obj_template_t *obj; val_value_t *valset, *parm; status_t res; help_mode_t mode; boolean imode; ncx_node_t dtyp; uint32 dlen; res = NO_ERR; imode = interactive_mode(); valset = get_valset(server_cb, rpc, &line[len], &res); if (res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } mode = HELP_MODE_NORMAL; /* look for the 'brief' parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_BRIEF); if (parm && parm->res == NO_ERR) { mode = HELP_MODE_BRIEF; } else { /* look for the 'full' parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_FULL); if (parm && parm->res == NO_ERR) { mode = HELP_MODE_FULL; } } parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_COMMAND); if (parm && parm->res == NO_ERR) { dtyp = NCX_NT_OBJ; res = NO_ERR; obj = parse_def(server_cb, &dtyp, VAL_STR(parm), &dlen, &res); if (obj != NULL) { if (obj->objtype == OBJ_TYP_RPC && !obj_is_hidden(obj)) { help_object(obj, mode); } else { res = ERR_NCX_DEF_NOT_FOUND; if (imode) { log_stdout("\nError: command (%s) not found", VAL_STR(parm)); } else { log_error("\nError: command (%s) not found", VAL_STR(parm)); } } } else { if (res == ERR_NCX_DEF_NOT_FOUND) { if (imode) { log_stdout("\nError: command (%s) not found", VAL_STR(parm)); } else { log_error("\nError: command (%s) not found", VAL_STR(parm)); } } else if (res == ERR_NCX_AMBIGUOUS_CMD) { if (imode) { log_stdout("\n"); } else { log_error("\n"); } } else { if (imode) { log_stdout("\nError: command error (%s)", get_error_string(res)); } else { log_error("\nError: command error (%s)", get_error_string(res)); } } } val_free_value(valset); return res; } /* look for the specific definition parameters */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_COMMANDS); if (parm && parm->res==NO_ERR) { res = do_help_commands(server_cb, mode); val_free_value(valset); return res; } parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_TYPE); if (parm && parm->res==NO_ERR) { res = NO_ERR; dtyp = NCX_NT_TYP; typ = parse_def(server_cb, &dtyp, VAL_STR(parm), &dlen, &res); if (typ) { help_type(typ, mode); } else { if (res == ERR_NCX_DEF_NOT_FOUND) { if (imode) { log_stdout("\nError: type definition (%s) not found", VAL_STR(parm)); } else { log_error("\nError: type definition (%s) not found", VAL_STR(parm)); } } else if (res == ERR_NCX_AMBIGUOUS_CMD) { if (imode) { log_stdout("\n"); } else { log_error("\n"); } } else { if (imode) { log_stdout("\nError: type error (%s)", get_error_string(res)); } else { log_error("\nError: type error (%s)", get_error_string(res)); } } } val_free_value(valset); return res; } parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_OBJECT); if (parm && parm->res == NO_ERR) { xmlChar *valstr = VAL_STR(parm); obj = NULL; if (valstr && *valstr == '/') { /* union matched UrlPath type */ xmlChar *urlpath; urlpath = xpath_convert_url_to_path(valstr, server_cb->match_names, server_cb->alt_names, FALSE, /* wildcards */ FALSE, /* withkeys */ &res); if (urlpath != NULL && res == NO_ERR) { res = xpath_find_schema_target_int(urlpath, &obj); } if (urlpath != NULL) { m__free(urlpath); } } else { /* union match NcxIdentifier type */ res = NO_ERR; dtyp = NCX_NT_OBJ; obj = parse_def(server_cb, &dtyp, valstr, &dlen, &res); } if (obj) { if (obj_is_data(obj) && !obj_is_hidden(obj)) { help_object(obj, mode); if (obj_is_leafy(obj)) { if (imode) { log_stdout("\n"); } else { log_write("\n"); } } } else { if (imode) { log_stdout("\nError: object definition (%s) not found", VAL_STR(parm)); } else { log_error("\nError: object definition (%s) not found", VAL_STR(parm)); } } } else { if (res == ERR_NCX_DEF_NOT_FOUND) { if (imode) { log_stdout("\nError: object definition (%s) not found", VAL_STR(parm)); } else { log_error("\nError: object definition (%s) not found", VAL_STR(parm)); } } else if (res == ERR_NCX_AMBIGUOUS_CMD) { if (imode) { log_stdout("\n"); } else { log_error("\n"); } } else { if (imode) { log_stdout("\nError: object error (%s)", get_error_string(res)); } else { log_error("\nError: object error (%s)", get_error_string(res)); } } } val_free_value(valset); return res; } parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_NOTIFICATION); if (parm && parm->res == NO_ERR) { res = NO_ERR; dtyp = NCX_NT_OBJ; obj = parse_def(server_cb, &dtyp, VAL_STR(parm), &dlen, &res); if (obj != NULL) { if (obj->objtype == OBJ_TYP_NOTIF && !obj_is_hidden(obj)) { help_object(obj, mode); } else { res = ERR_NCX_DEF_NOT_FOUND; if (imode) { log_stdout("\nError: notification definition " "(%s) not found", VAL_STR(parm)); } else { log_error("\nError: notification definition " "(%s) not found", VAL_STR(parm)); } } } else { if (res == ERR_NCX_DEF_NOT_FOUND) { if (imode) { log_stdout("\nError: notification definition " "(%s) not found", VAL_STR(parm)); } else { log_error("\nError: notification definition " "(%s) not found", VAL_STR(parm)); } } else if (res == ERR_NCX_AMBIGUOUS_CMD) { if (imode) { log_stdout("\n"); } else { log_error("\n"); } } else { if (imode) { log_stdout("\nError: notification error (%s)", get_error_string(res)); } else { log_error("\nError: notification error (%s)", get_error_string(res)); } } } val_free_value(valset); return res; } /* no parameters entered except maybe brief or full */ switch (mode) { case HELP_MODE_BRIEF: case HELP_MODE_NORMAL: log_stdout("\n\nyangcli summary:"); log_stdout("\n\n Commands are defined with YANG rpc statements."); log_stdout("\n Use 'help commands' to see current list of commands."); log_stdout("\n\n Global variables are created with 2 dollar signs" "\n in assignment statements ($$foo = 7)."); log_stdout("\n Use 'show globals' to see current list " "of global variables."); log_stdout("\n\n Local variables (within a stack frame) are created" "\n with 1 dollar sign in assignment" " statements ($foo = $bar)."); log_stdout("\n Use 'show locals' to see current list " "of local variables."); log_stdout("\n\n Use 'show vars' to see all program variables.\n"); if (mode==HELP_MODE_BRIEF) { break; } obj = ncx_find_object(get_yangcli_mod(), YANGCLI_HELP); if (obj && obj->objtype == OBJ_TYP_RPC) { help_object(obj, HELP_MODE_FULL); } else { SET_ERROR(ERR_INTERNAL_VAL); } break; case HELP_MODE_FULL: help_program_module(YANGCLI_MOD, YANGCLI_BOOT, HELP_MODE_FULL); break; default: SET_ERROR(ERR_INTERNAL_VAL); } val_free_value(valset); return res; } /* do_help */ /******************************************************************** * FUNCTION do_start_script (sub-mode of run local RPC) * * run script=script-filespec * * run the specified script * * INPUTS: * server_cb == server control block to use * source == file source * valset == value set for the run script parameters * * RETURNS: * status *********************************************************************/ static status_t do_start_script (server_cb_t *server_cb, const xmlChar *source, val_value_t *valset) { xmlChar *fspec; FILE *fp; val_value_t *parm; status_t res; int num; xmlChar buff[4]; res = NO_ERR; /* saerch for the script */ fspec = ncxmod_find_script_file(source, &res); if (!fspec) { return res; } /* open a new runstack frame if the file exists */ fp = fopen((const char *)fspec, "r"); if (!fp) { m__free(fspec); return ERR_NCX_MISSING_FILE; } else { res = runstack_push(server_cb->runstack_context, fspec, fp); m__free(fspec); if (res != NO_ERR) { fclose(fp); return res; } } /* setup the numeric script parameters */ memset(buff, 0x0, 4); buff[0] = 'P'; /* add the P1 through P9 parameters that are present */ for (num=1; num<=YANGCLI_MAX_RUNPARMS; num++) { buff[1] = '0' + num; parm = (valset) ? val_find_child(valset, YANGCLI_MOD, buff) : NULL; if (parm) { /* store P7 named as ASCII 7 */ res = var_set_str(server_cb->runstack_context, buff+1, 1, parm, VAR_TYP_LOCAL); if (res != NO_ERR) { runstack_pop(server_cb->runstack_context); return res; } } } return res; } /* do_start_script */ /******************************************************************** * FUNCTION do_run (local RPC) * * run script=script-filespec * * * Get the specified parameter and run the specified script * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ static status_t do_run (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *parm; status_t res; res = NO_ERR; /* get the 'script' parameter */ valset = get_valset(server_cb, rpc, &line[len], &res); if (valset && res == NO_ERR) { /* there is 1 parm */ parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_SCRIPT); if (!parm) { res = ERR_NCX_DEF_NOT_FOUND; } else if (parm->res != NO_ERR) { res = parm->res; } else { /* the parm val is the script filespec */ res = do_start_script(server_cb, VAL_STR(parm), valset); if (res != NO_ERR) { log_write("\nError: start script %s failed (%s)", obj_get_name(rpc), get_error_string(res)); } } } if (valset) { val_free_value(valset); } return res; } /* do_run */ /******************************************************************** * FUNCTION do_log (local RPC) * * log-error msg=string * log-warn msg=string * log-info msg=string * log-debug msg=string * * Print the log message if the log-level is high enough * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * level == required log level * * RETURNS: * status *********************************************************************/ static status_t do_log (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len, log_debug_t level) { val_value_t *valset, *parm; status_t res; res = NO_ERR; /* get the 'script' parameter */ valset = get_valset(server_cb, rpc, &line[len], &res); if (valset && res == NO_ERR) { /* there is 1 parm */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_MSG); if (!parm) { res = ERR_NCX_DEF_NOT_FOUND; } else if (parm->res != NO_ERR) { res = parm->res; } else if (VAL_STR(parm)) { switch (level) { case LOG_DEBUG_ERROR: log_error("\nError: %s\n", VAL_STR(parm)); break; case LOG_DEBUG_WARN: log_warn("\nWarning: %s\n", VAL_STR(parm)); break; case LOG_DEBUG_INFO: log_info("\nInfo: %s\n", VAL_STR(parm)); break; case LOG_DEBUG_DEBUG: log_debug("\nDebug: %s\n", VAL_STR(parm)); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } } if (valset) { val_free_value(valset); } return res; } /* do_log */ /******************************************************************** * FUNCTION pwd * * Print the current working directory * * INPUTS: * rpc == RPC method for the load command * line == CLI input in progress * len == offset into line buffer to start parsing * * OUTPUTS: * log_stdout global help message * *********************************************************************/ static void pwd (void) { char *buff; boolean imode; imode = interactive_mode(); buff = m__getMem(YANGCLI_BUFFLEN); if (!buff) { if (imode) { log_stdout("\nMalloc failure\n"); } else { log_write("\nMalloc failure\n"); } return; } if (!getcwd(buff, YANGCLI_BUFFLEN)) { if (imode) { log_stdout("\nGet CWD failure\n"); } else { log_write("\nGet CWD failure\n"); } m__free(buff); return; } if (imode) { log_stdout("\nCurrent working directory is %s\n", buff); } else { log_write("\nCurrent working directory is %s\n", buff); } m__free(buff); } /* pwd */ /******************************************************************** * FUNCTION do_pwd * * Print the current working directory * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the load command * line == CLI input in progress * len == offset into line buffer to start parsing * * OUTPUTS: * log_stdout global help message * * RETURNS: * status *********************************************************************/ static status_t do_pwd (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset; status_t res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (res == NO_ERR || res == ERR_NCX_SKIPPED) { pwd(); } if (valset) { val_free_value(valset); } return res; } /* do_pwd */ /******************************************************************** * FUNCTION do_cd * * Change the current working directory * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the load command * line == CLI input in progress * len == offset into line buffer to start parsing * * OUTPUTS: * log_stdout global help message * * RETURNS: * status *********************************************************************/ static status_t do_cd (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *parm; xmlChar *pathstr; status_t res; int ret; boolean imode; res = NO_ERR; imode = interactive_mode(); valset = get_valset(server_cb, rpc, &line[len], &res); if (res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_DIR); if (parm == NULL || parm->res != NO_ERR || xml_strlen(VAL_STR(parm)) == 0) { val_free_value(valset); log_error("\nError: 'dir' parameter is missing"); return ERR_NCX_MISSING_PARM; } res = NO_ERR; pathstr = ncx_get_source_ex(VAL_STR(parm), FALSE, &res); if (!pathstr) { val_free_value(valset); log_error("\nError: get path string failed (%s)", get_error_string(res)); return res; } ret = chdir((const char *)pathstr); if (ret) { res = ERR_NCX_INVALID_VALUE; if (imode) { log_stdout("\nChange CWD failure (%s)\n", get_error_string(errno_to_status())); } else { log_write("\nChange CWD failure (%s)\n", get_error_string(errno_to_status())); } } else { pwd(); } val_free_value(valset); m__free(pathstr); return res; } /* do_cd */ /******************************************************************** * FUNCTION do_fill * * Fill an object for use in a PDU * * INPUTS: * server_cb == server control block to use (NULL if none) * rpc == RPC method for the load command * line == CLI input in progress * len == offset into line buffer to start parsing * iswrite == TRUE if write op; FALSE if read op * * OUTPUTS: * the completed data node is output and * is usually part of an assignment statement * * RETURNS: * status *********************************************************************/ static status_t do_fill (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len, boolean iswrite) { val_value_t *valset, *valroot, *parm, *newparm, *curparm; val_value_t *targval; obj_template_t *targobj; const xmlChar *target; status_t res; boolean save_getopt; res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } target = NULL; parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_TARGET); if (!parm || parm->res != NO_ERR) { val_free_value(valset); return ERR_NCX_MISSING_PARM; } else { target = VAL_STR(parm); } save_getopt = server_cb->get_optional; newparm = NULL; curparm = NULL; targobj = NULL; targval = NULL; /* get the root object and the target node * if target == /foo/bar/baz then * valroot --> /foo and targobj --> /foo/bar/baz */ res = val123_new_value_from_instance_id(/*parent_obj*/NULL, target, TRUE, /*childval*/&valroot, &targobj, &targval); if(res==NO_ERR) { targobj=targval->obj; } if (res != NO_ERR) { if (valroot) { val_free_value(valroot); } val_free_value(valset); clear_result(server_cb); return res; } else if (valroot == NULL) { /* this means the docroot was selected */ log_error("\nError: The document root is not allowed here as a target"); res = ERR_NCX_OPERATION_NOT_SUPPORTED; val_free_value(valset); clear_result(server_cb); return res; } else if (targval != valroot) { /* keep targval, toss valroot */ val_remove_child(targval); val_free_value(valroot); valroot = NULL; } /* check if targval is valid if it is an empty string * this corner-case is what the get_instanceid_parm will * return if the last node was a leaf */ if (targval && targval->btyp == NCX_BT_STRING && VAL_STR(targval) == NULL) { /* the NULL string was not entered; * if a value was entered as '' it would be recorded * as a zero-length string, not a NULL string */ val_free_value(targval); targval = NULL; } /* find the value to use as content value or template, if any */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_VALUE); if (parm && parm->res == NO_ERR) { curparm = parm; } /* find the --optional flag */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_OPTIONAL); if (parm && parm->res == NO_ERR) { server_cb->get_optional = TRUE; } /* fill in the value based on all the parameters */ switch (targobj->objtype) { case OBJ_TYP_ANYXML: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: /* make a new leaf, toss the targval if any */ newparm = fill_value(server_cb, rpc, targobj, curparm, &res); if (targval) { val_free_value(targval); targval = NULL; } break; case OBJ_TYP_CHOICE: if (targval) { newparm = targval; } else { newparm = val_new_value(); if (!newparm) { log_error("\nError: malloc failure"); res = ERR_INTERNAL_MEM; } else { val_init_from_template(newparm, targobj); } } if (res == NO_ERR) { res = get_choice(server_cb, rpc, targobj, newparm, curparm, iswrite, FALSE); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } } break; case OBJ_TYP_CASE: if (targval) { newparm = targval; } else { newparm = val_new_value(); if (!newparm) { log_error("\nError: malloc failure"); res = ERR_INTERNAL_MEM; } else { val_init_from_template(newparm, targobj); } } res = get_case(server_cb, targobj, newparm, curparm, iswrite, FALSE); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } break; default: if (targval) { newparm = targval; } else { newparm = val_new_value(); if (!newparm) { log_error("\nError: malloc failure"); res = ERR_INTERNAL_MEM; } else { val_init_from_template(newparm, targobj); } } res = fill_valset(server_cb, rpc, newparm, curparm, TRUE, FALSE); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } } /* check save result or clear it */ if (res == NO_ERR) { if (server_cb->result_name || server_cb->result_filename) { /* set the index chains because the value is being saved */ res = val_build_index_chains(newparm); /* save the filled in value */ if (res == NO_ERR) { res = finish_result_assign(server_cb, newparm, NULL); newparm = NULL; } } } else { clear_result(server_cb); } /* cleanup */ val_free_value(valset); if (newparm) { val_free_value(newparm); } server_cb->get_optional = save_getopt; return res; } /* do_fill */ /******************************************************************** * FUNCTION add_content * * Add the config nodes to the parent * * INPUTS: * server_cb == server control block to use * rpc == RPC method in progress * config_content == the node associated with the target * to be used as content nested within the * element * curobj == the current object node for config_content, going * up the chain to the root object. * First call should pass config_content->obj * dofill == TRUE if interactive mode and mandatory parms * need to be specified (they can still be skipped) * FALSE if no checking for mandatory parms * Just fill out the minimum path to root from * the 'config_content' node * curtop == address of steady-storage node to add the * next new level * * OUTPUTS: * config node is filled in with child nodes * curtop is set to the latest top value * It is not needed after the last call and should be ignored * * RETURNS: * status; config_content is NOT freed if returning an error *********************************************************************/ static status_t add_content (server_cb_t *server_cb, obj_template_t *rpc, val_value_t *config_content, obj_template_t *curobj, boolean dofill, val_value_t **curtop) { obj_key_t *curkey; val_value_t *newnode, *keyval, *lastkey; status_t res; boolean done, content_used; res = NO_ERR; newnode = NULL; set_completion_state(&server_cb->completion_state, rpc, curobj, CMD_STATE_GETVAL); /* add content based on the current node type */ switch (curobj->objtype) { case OBJ_TYP_ANYXML: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: if (curobj != config_content->obj) { return SET_ERROR(ERR_INTERNAL_VAL); } if (!obj_is_key(config_content->obj)) { val_add_child(config_content, *curtop); *curtop = config_content; } break; case OBJ_TYP_LIST: /* get all the key nodes for the current object, * if they do not already exist */ if (curobj == config_content->obj) { val_add_child(config_content, *curtop); *curtop = config_content; return NO_ERR; } /* else need to fill in the keys for this content layer */ newnode = val_new_value(); if (!newnode) { return ERR_INTERNAL_MEM; } val_init_from_template(newnode, curobj); val_add_child(newnode, *curtop); *curtop = newnode; content_used = FALSE; lastkey = NULL; for (curkey = obj_first_key(curobj); curkey != NULL; curkey = obj_next_key(curkey)) { keyval = val_find_child(*curtop, obj_get_mod_name(curkey->keyobj), obj_get_name(curkey->keyobj)); if (!keyval) { if (curkey->keyobj == config_content->obj) { keyval = config_content; val_insert_child(keyval, lastkey, *curtop); content_used = TRUE; lastkey = keyval; res = NO_ERR; } else if (dofill) { res = get_parm(server_cb, rpc, curkey->keyobj, *curtop, NULL); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } else if (res != NO_ERR) { return res; } else { keyval = val_find_child(*curtop, obj_get_mod_name (curkey->keyobj), obj_get_name (curkey->keyobj)); if (!keyval) { return SET_ERROR(ERR_INTERNAL_VAL); } val_remove_child(keyval); val_insert_child(keyval, lastkey, *curtop); lastkey = keyval; } /* else skip this key (for debugging server) */ } /* else --nofill; skip this node */ } } /* wait until all the key leafs are accounted for before * changing the *curtop pointer */ if (content_used) { *curtop = config_content; } break; case OBJ_TYP_CONTAINER: if (curobj == config_content->obj) { val_add_child(config_content, *curtop); *curtop = config_content; } else { newnode = val_new_value(); if (!newnode) { return ERR_INTERNAL_MEM; } val_init_from_template(newnode, curobj); val_add_child(newnode, *curtop); *curtop = newnode; } break; case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: if (curobj != config_content->obj) { /* nothing to do for the choice level if the target is a case */ res = NO_ERR; break; } done = FALSE; while (!done) { newnode = val_get_first_child(config_content); if (newnode) { val_remove_child(newnode); res = add_content(server_cb, rpc, newnode, newnode->obj, dofill, curtop); if (res != NO_ERR) { val_free_value(newnode); newnode = NULL; done = TRUE; } } else { done = TRUE; } } if (res == NO_ERR) { val_free_value(config_content); } *curtop = newnode; break; default: /* any other object type is an error */ res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* add_content */ /******************************************************************** * FUNCTION complete_path_content * * Use the valroot and content pointer to work up the already * constructed value tree to find any missing mandatory * nodes or key leafs * * INPUTS: * server_cb == server control block to use * rpc == RPC method in progress * valroot == value root of content * == NULL if not used (config_content used instead) * config_content == the node associated with the target * within valroot (if valroot non-NULL) * dofill == TRUE if fill mode * == FALSE if --nofill is in effect * iswrite == TRUE if write op; FALSE if read op * * OUTPUTS: * valroot tree is filled out as needed and requested * * RETURNS: * status; config_content is NOT freed if returning an error *********************************************************************/ static status_t complete_path_content (server_cb_t *server_cb, obj_template_t *rpc, val_value_t *valroot, val_value_t *config_content, boolean dofill, boolean iswrite) { obj_key_t *curkey; val_value_t *curnode, *keyval, *lastkey; status_t res; boolean done; if (!server_cb->get_optional && !iswrite) { return NO_ERR; } res = NO_ERR; curnode = config_content; /* handle all the nodes from bottom to valroot */ done = FALSE; while (!done) { if (curnode->btyp == NCX_BT_LIST) { lastkey = NULL; for (curkey = obj_first_key(curnode->obj); curkey != NULL; curkey = obj_next_key(curkey)) { keyval = val_find_child(curnode, obj_get_mod_name(curkey->keyobj), obj_get_name(curkey->keyobj)); if (keyval == NULL && dofill) { res = get_parm(server_cb, rpc, curkey->keyobj, curnode, NULL); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } else if (res != NO_ERR) { return res; } else { keyval = val_find_child(curnode, obj_get_mod_name (curkey->keyobj), obj_get_name(curkey->keyobj)); if (!keyval) { return SET_ERROR(ERR_INTERNAL_VAL); } val_remove_child(keyval); val_insert_child(keyval, lastkey, curnode); lastkey = keyval; } /* else skip this key (for debugging server) */ } /* else --nofill; skip this node */ } /* for all the keys in the list */ } /* else not list so skip this node */ /* move up the tree */ if (curnode != valroot && curnode->parent != NULL) { curnode = curnode->parent; } else { done = TRUE; } } return res; } /* complete_path_content */ /******************************************************************** * FUNCTION add_filter_from_content_node * * Add the filter node content for the get or get-config operation * Build the node top-down, by recursing bottom-up * from the node to be edited. * * INPUTS: * server_cb == server control block to use * rpc == RPC method in progress * get_content == the node associated with the target * to be used as content nested within the * element * curobj == the current object node for get_content, going * up the chain to the root object. * First call should pass get_content->obj * filter == the starting node to add the data into * dofill == TRUE for fill mode * FALSE to skip filling any nodes * curtop == address of stable storage for current add-to node * This pointer MUST be set to NULL upon first fn call * OUTPUTS: * filter node is filled in with child nodes * * RETURNS: * status; get_content is NOT freed if returning an error *********************************************************************/ static status_t add_filter_from_content_node (server_cb_t *server_cb, obj_template_t *rpc, val_value_t *get_content, obj_template_t *curobj, val_value_t *filter, boolean dofill, val_value_t **curtop) { obj_template_t *parent; status_t res; /* get to the root of the object chain */ parent = obj_get_parent(curobj); if (parent && !obj_is_root(parent)) { res = add_filter_from_content_node(server_cb, rpc, get_content, parent, filter, dofill, curtop); if (res != NO_ERR) { return res; } } /* set the current target, working down the stack * on the way back from the initial dive */ if (!*curtop) { /* first time through to this point */ *curtop = filter; } res = add_content(server_cb, rpc, get_content, curobj, dofill, curtop); return res; } /* add_filter_from_content_node */ /******************************************************************** * FUNCTION edit_config_to_server_reqdata * * Generate an rpc data to the server * * Fills out the node based on the config_target node * Any missing key nodes will be collected (via CLI prompt) * along the way. * * INPUTS: * server_cb == server control block to use * valroot == value tree root * config_content == the node associated with the target * to be used as content nested within the * element * timeoutval == timeout value to use * def_editop == default-operation to use (or NOT_SET) * * OUTPUTS: * server_cb->state may be changed or other action taken * * * RETURNS: * status *********************************************************************/ static status_t edit_config_to_server_reqdata (server_cb_t *server_cb, val_value_t *valroot, val_value_t *config_content, op_defop_t def_editop, val_value_t** reqdata_out) { obj_template_t *rpc, *input, *child; const xmlChar *defopstr; mgr_rpc_req_t *req; val_value_t *reqdata, *parm, *target, *dummy_parm; ses_cb_t *scb; status_t res; boolean freeroot, dofill; req = NULL; reqdata = NULL; res = NO_ERR; dofill = TRUE; assert(valroot); assert(config_content); if (LOGDEBUG) { log_debug("\nSending request"); } /* make sure there is an edit target on this server */ if (!server_cb->default_target) { log_error("\nError: no target available on server"); return ERR_NCX_OPERATION_FAILED; } /* get the template */ rpc = ncx_find_object(get_netconf_mod(server_cb), NCX_EL_EDIT_CONFIG); if (!rpc) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* get the 'input' section container */ input = obj_find_child(rpc, NULL, YANG_K_INPUT); if (!input) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* construct a method + parameter tree */ reqdata = xml_val_new_struct(obj_get_name(rpc), obj_get_nsid(rpc)); assert(reqdata); /* set the edit-config/input/target node to the default_target */ child = obj_find_child(input, NC_MODULE, NCX_EL_TARGET); assert(child); parm = val_new_value(); assert(parm); val_init_from_template(parm, child); val_add_child(parm, reqdata); target = xml_val_new_flag(server_cb->default_target, obj_get_nsid(child)); assert(target); val_add_child(target, parm); /* set the edit-config/input/default-operation node */ if (!(def_editop == OP_DEFOP_NOT_USED || def_editop == OP_DEFOP_NOT_SET)) { child = obj_find_child(input, NC_MODULE, NCX_EL_DEFAULT_OPERATION); res = NO_ERR; defopstr = op_defop_name(def_editop); parm = val_make_simval_obj(child, defopstr, &res); assert(parm); val_add_child(parm, reqdata); } /* set the test-option to the user-configured or default value */ if (server_cb->testoption != OP_TESTOP_NONE) { /* set the edit-config/input/test-option node to * the user-specified value */ child = obj_find_child(input, NC_MODULE, NCX_EL_TEST_OPTION); res = NO_ERR; if(child!=NULL /*if-feature dependency on the validate server capability*/) { parm = val_make_simval_obj(child, op_testop_name(server_cb->testoption), &res); assert(parm); val_add_child(parm, reqdata); } } /* set the error-option to the user-configured or default value */ if (server_cb->erroption != OP_ERROP_NONE) { /* set the edit-config/input/error-option node to * the user-specified value */ child = obj_find_child(input, NC_MODULE, NCX_EL_ERROR_OPTION); res = NO_ERR; parm = val_make_simval_obj(child, op_errop_name(server_cb->erroption), &res); assert(parm); val_add_child(parm, reqdata); } /* create the node */ child = obj_find_child(input, NC_MODULE, NCX_EL_CONFIG); parm = val_new_value(); assert(parm); val_init_from_template(parm, child); val_add_child(parm, reqdata); /* set the edit-config/input/config node to the * config_content, but after filling in any * missing nodes from the root to the target */ res = complete_path_content(server_cb, rpc, valroot, config_content, dofill, TRUE); if (res != NO_ERR) { return res; } val_add_child(val_clone(valroot), parm); /* now that all the content is gathered the OBJ_TYP_CHOICE * and OBJ_TYP_CASE place-holder nodes can be deleted * this can only happen if the edit target node requested * is a choice or a case; */ if (config_content->btyp == NCX_BT_CHOICE || config_content->btyp == NCX_BT_CASE) { /* insert all of the children of this choice * in place of itself in the parent node */ val_move_children(config_content, config_content->parent); /* now config_content should be empty with all its child * nodes now siblings; remove it and discard */ val_remove_child(config_content); val_free_value(config_content); config_content = NULL; } /* rearrange the nodes to canonical order if requested */ if (server_cb->fixorder) { /* must set the order of a root container seperately */ val_set_canonical_order(parm); } /* cleanup and set next state */ if (res != NO_ERR) { if (reqdata) { val_free_value(reqdata); } } else { server_cb->state = MGR_IO_ST_CONN_RPYWAIT; } *reqdata_out=reqdata; return res; } /* edit_config_to_server_reqdata */ /******************************************************************** * FUNCTION add_filter_attrs * * Add the type and possibly the select * attribute to a value node * * INPUTS: * val == value node to set * selectval == select node to use, (type=xpath) * == NULL for type = subtree * !!! this memory is consumed here !!! * * RETURNS: * status *********************************************************************/ static status_t add_filter_attrs (val_value_t *val, val_value_t *selectval) { val_value_t *metaval; /* create a value node for the meta-value */ metaval = val_make_string(0, NCX_EL_TYPE, (selectval) ? NCX_EL_XPATH : NCX_EL_SUBTREE); if (!metaval) { return ERR_INTERNAL_MEM; } dlq_enque(metaval, &val->metaQ); if (selectval) { val_set_qname(selectval, 0, NCX_EL_SELECT, xml_strlen(NCX_EL_SELECT)); dlq_enque(selectval, &val->metaQ); } return NO_ERR; } /* add_filter_attrs */ /******************************************************************** * FUNCTION get_to_server_reqdata * * Generate a operation data to the specified server * * Fills out the node based on the config_target node * Any missing key nodes will be collected (via CLI prompt) * along the way. * * INPUTS: * server_cb == server control block to use * valroot == root of get_data if set via * the CLI Xpath code; if so, then the * get_content is the target within this * subtree (may == get_content) * == NULL if not used and get_content is * the only parameter gathered from input * get_content == the node associated with the target * to be used as content nested within the * element * == NULL to use selectval instead * selectval == value with select string in it * == NULL to use get_content instead * * (if both == NULL then no filter is added) * * source == optional database source * , * timeoutval == timeout value to use * dofill == TRUE if interactive mode and mandatory parms * need to be specified (they can still be skipped) * FALSE if no checking for mandatory parms * Just fill out the minimum path to root from * the 'get_content' node * withdef == the desired with-defaults parameter * It may be ignored or altered, depending on * whether the server supports the capability or not * reqdata_out == request data value * * OUTPUTS: * * !!! valroot is consumed -- freed or transfered to a PDU * !!! that will be freed later * * !!! get_content is consumed -- freed or transfered to a PDU * !!! that will be freed later * * !!! source is consumed -- freed or transfered to a PDU * !!! that will be freed later * * RETURNS: * status *********************************************************************/ static status_t get_to_server_reqdata(server_cb_t *server_cb, val_value_t *valroot, val_value_t *get_content, val_value_t *selectval, val_value_t *source, boolean dofill, ncx_withdefaults_t withdef, val_value_t** reqdata_out) { obj_template_t *rpc, *input, *withdefobj; val_value_t *reqdata, *filter; val_value_t *withdefval, *dummy_parm; ses_cb_t *scb; mgr_scb_t *mscb; status_t res; boolean freeroot; reqdata = NULL; res = NO_ERR; input = NULL; filter = NULL; *reqdata_out = NULL; if (LOGDEBUG) { if (source != NULL) { log_debug("\nSending request"); } else { log_debug("\nSending request"); } } /* either going to free valroot or config_content */ if (valroot == NULL || valroot == get_content) { freeroot = FALSE; } else { freeroot = TRUE; } /* get the or input template */ rpc = ncx_find_object(get_netconf_mod(server_cb), (source) ? NCX_EL_GET_CONFIG : NCX_EL_GET); if (rpc) { input = obj_find_child(rpc, NULL, YANG_K_INPUT); } if (!input) { res = SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } else { reqdata = val_new_value(); if (!reqdata) { log_error("\nError allocating a new RPC request"); res = ERR_INTERNAL_MEM; } val_init_from_template(reqdata,rpc); } /* add /get-star/input/source */ if (res == NO_ERR) { if (source) { val_add_child(source, reqdata); } } /* add /get-star/input/filter */ if (res == NO_ERR && (get_content || selectval)) { if (get_content) { /* set the get/input/filter node to the * get_content, but after filling in any * missing nodes from the root to the target */ filter = xml_val_new_struct(NCX_EL_FILTER, xmlns_nc_id()); } else { filter = xml_val_new_flag(NCX_EL_FILTER, xmlns_nc_id()); } if (!filter) { log_error("\nError: malloc failure"); res = ERR_INTERNAL_MEM; } else { val_add_child(filter, reqdata); } /* add the type and maybe select attributes */ if (res == NO_ERR) { res = add_filter_attrs(filter, selectval); } } /* check any errors so far */ if (res != NO_ERR) { if (freeroot) { val_free_value(valroot); } else if (get_content) { val_free_value(get_content); } else if (selectval) { val_free_value(selectval); } val_free_value(reqdata); return res; } /* add the content to the filter element * building the path from the content node * to the root; fill if dofill is true */ if (valroot) { val_add_child(valroot, filter); res = complete_path_content(server_cb, rpc, valroot, get_content, dofill, FALSE); if (res != NO_ERR) { val_free_value(valroot); val_free_value(reqdata); return res; } } else if (get_content) { dummy_parm = NULL; res = add_filter_from_content_node(server_cb, rpc, get_content, get_content->obj, filter, dofill, &dummy_parm); if (res != NO_ERR && res != ERR_NCX_SKIPPED) { /* val_free_value(get_content); already freed! */ val_free_value(reqdata); return res; } res = NO_ERR; } /* get the session control block */ scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { res = SET_ERROR(ERR_INTERNAL_PTR); } /* !!! get_content consumed at this point !!! * check if the with-defaults parmaeter should be added */ if (res == NO_ERR) { mscb = mgr_ses_get_mscb(scb); if (cap_std_set(&mscb->caplist, CAP_STDID_WITH_DEFAULTS)) { switch (withdef) { case NCX_WITHDEF_NONE: break; case NCX_WITHDEF_TRIM: case NCX_WITHDEF_EXPLICIT: case NCX_WITHDEF_REPORT_ALL_TAGGED: /*** !!! NEED TO CHECK IF TRIM / EXPLICT / TAGGED *** !!! REALLY SUPPORTED IN THE caplist ***/ /* fall through */ case NCX_WITHDEF_REPORT_ALL: /* it is OK to send a with-defaults to this server */ withdefobj = obj_find_child(input, NULL, NCX_EL_WITH_DEFAULTS); if (withdefobj == NULL) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } else { withdefval = val_make_simval_obj (withdefobj, ncx_get_withdefaults_string(withdef), &res); if (withdefval != NULL) { val_add_child(withdefval, reqdata); } } break; default: SET_ERROR(ERR_INTERNAL_VAL); } } else if (withdef != NCX_WITHDEF_NONE) { log_warn("\nWarning: 'with-defaults' " "capability not-supported so parameter ignored"); } } if (res != NO_ERR) { if (reqdata) { val_free_value(reqdata); } } *reqdata_out=reqdata; return res; } /******************************************************************** * FUNCTION get_content_from_choice * * Get the content input for the EditOps from choice * * If the choice is 'from-cli' and this is interactive mode * then the fill_valset will be called to get input * based on the 'target' parameter also in the valset * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the load command * valset == parsed CLI valset * getoptional == TRUE if optional nodes are desired * isdelete == TRUE if this is for an edit-config delete * FALSE for some other operation * dofill == TRUE to fill the content, * FALSE to skip fill phase * iswrite == TRUE if write operation * FALSE if a read operation * retres == address of return status * valroot == address of return value root pointer * used when the content is from the CLI XPath * secondary_args == secondary command line arguments e.g. relevant params * when filling container specified after -- empty parameter. * * OUTPUTS: * *valroot == pointer to malloced value tree content root. * This is the top of the tree that will be added * as a child of the node for the edit-config op * If this is non-NULL, then the return value should * not be freed. This pointer should be freed instead. * == NULL if the from choice mode does not use * an XPath schema-instance node as input * *retres == return status * * RETURNS: * result of the choice; this is the content * that will be affected by the edit-config operation * via create, merge, or replace * * This is the specified target node in the * content target, which may be the * same as the content root or will, if *valroot != NULL * be a descendant of the content root * * If *valroot == NULL then this is a malloced pointer that * must be freed by the caller * * If the retun value is NULL and the *retres is NO_ERR * then the document root was selected so there is no * specific node to return as a subtree. *********************************************************************/ static val_value_t * get_content_from_choice (server_cb_t *server_cb, obj_template_t *rpc, val_value_t *valset, boolean getoptional, boolean isdelete, boolean dofill, boolean iswrite, status_t *retres, val_value_t **valroot, const char* secondary_args) { val_value_t *parm, *curparm = NULL, *newparm = NULL; const val_value_t *userval = NULL; val_value_t *targval = NULL; obj_template_t *targobj = NULL; const xmlChar *fromstr = NULL, *target = NULL; xmlChar *fromurl = NULL; var_type_t vartype = VAR_TYP_NONE; boolean isvarref = FALSE, iscli = FALSE; boolean isselect = FALSE, saveopt = FALSE; boolean isextern = FALSE, alt_names = FALSE; status_t res = NO_ERR; ncx_name_match_t match_names = FALSE; *valroot = NULL; *retres = NO_ERR; /* look for the 'match-names' parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_MATCH_NAMES); if (parm != NULL) { match_names = ncx_get_name_match_enum(VAL_ENUM_NAME(parm)); if (match_names == NCX_MATCH_NONE) { *retres = ERR_NCX_INVALID_VALUE; return NULL; } } else { match_names = server_cb->match_names; } /* look for the 'alt-names' parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_ALT_NAMES); if (parm != NULL) { alt_names = VAL_BOOL(parm); } else { alt_names = server_cb->alt_names; } /* look for the 'from' parameter variant */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_VARREF); if (parm != NULL) { /* content = uservar or $varref */ isvarref = TRUE; fromstr = VAL_STR(parm); } else { parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_SELECT); if (parm != NULL) { /* content == select string for xget or xget-config */ isselect = TRUE; fromstr = VAL_STR(parm); } } if (parm == NULL && !iswrite && !xml_strncmp(obj_get_name(rpc), (const xmlChar *)"xget", 4)) { /* try urltarget; convert to XPath first */ parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_URLTARGET); if (parm != NULL) { fromurl = xpath_convert_url_to_path(VAL_STR(parm), match_names, alt_names, !iswrite, /* wildcards */ TRUE, /* withkeys */ &res); if (fromurl == NULL || res != NO_ERR) { log_error("\nError: urltarget '%s' has " "invalid format (%s)", VAL_STR(parm), get_error_string(res)); *retres = res; if (fromurl != NULL) { m__free(fromurl); } return NULL; } else { fromstr = fromurl; isselect = TRUE; } } else { /* last choice; content is from the CLI */ iscli = TRUE; } } else if (!(isvarref || isselect)) { iscli = TRUE; } if (iscli) { saveopt = server_cb->get_optional; server_cb->get_optional = getoptional; /* from CLI -- look for the 'target' parameter */ parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_TARGET); if (parm != NULL) { target = VAL_STR(parm); } else { parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_URLTARGET); if (parm != NULL) { res = NO_ERR; fromurl = xpath_convert_url_to_path(VAL_STR(parm), match_names, alt_names, !iswrite, TRUE, /* withkeys */ &res); if (fromurl == NULL || res != NO_ERR) { log_error("\nError: urltarget '%s' has " "invalid format (%s)\n", VAL_STR(parm), get_error_string(res)); server_cb->get_optional = saveopt; *retres = res; if (fromurl != NULL) { m__free(fromurl); } return NULL; } else { target = fromurl; } } } if (parm == NULL) { log_error("\nError: target or urltarget parameter is missing"); server_cb->get_optional = saveopt; *retres = ERR_NCX_MISSING_PARM; return NULL; } if(strlen(target)>1 && target[strlen(target)-1]=='/') { /* tolerate paths with trailing forward slash e.g. create /interfaces/interface/ */ char* t; t = strdup(target); t[strlen(target)-1]=0; res = val123_new_value_from_instance_id(/*parent_obj*/NULL, t, TRUE, /*childval*/valroot, &targobj, &targval); free(t); } else { res = val123_new_value_from_instance_id(/*parent_obj*/NULL, target, TRUE, /*childval*/valroot, &targobj, &targval); } if(res==NO_ERR) { if(iswrite && !obj_is_config(targobj)) { res = ERR_NCX_ACCESS_READ_ONLY; } } if (res != NO_ERR) { log_error("\nError: invalid target parameter (%s)", get_error_string(res)); if (*valroot) { val_free_value(*valroot); *valroot = NULL; } if (fromurl != NULL) { m__free(fromurl); } server_cb->get_optional = saveopt; *retres = res; return NULL; } else if (*valroot == NULL) { /* indicates the docroot was selected or path not found */ if (fromurl != NULL) { m__free(fromurl); } return NULL; } if (fromurl != NULL) { m__free(fromurl); fromurl = NULL; } curparm = NULL; parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_VALUE); if (parm && parm->res == NO_ERR) { const xmlChar *parmval = NULL; if (parm->btyp == NCX_BT_EXTERN) { isextern = TRUE; curparm = mgr_load_extern_file(VAL_EXTERN(parm), NULL, &res); if (res != NO_ERR) { *retres = res; return NULL; } } else { if (typ_is_string(parm->btyp)) { parmval = VAL_STR(parm); } else if (typ_is_enum(parm->btyp)) { parmval = VAL_ENUM_NAME(parm); } curparm = var_get_script_val_ex(server_cb->runstack_context, NULL, targobj, NULL, parmval, ISPARM, (parmval) ? NULL : parm, &res); } if (curparm == NULL || res != NO_ERR) { log_error("\nError: Script value '%s' invalid (%s)", (parmval) ? parmval : (const xmlChar *)"", get_error_string(res)); server_cb->get_optional = saveopt; val_free_value(*valroot); *valroot = NULL; if (curparm) { val_free_value(curparm); } *retres = res; return NULL; } if (curparm->obj != targobj && !isextern) { res = ERR_NCX_INVALID_VALUE; log_error("\nError: current value '%s' " "object type is incorrect.", VAL_STR(parm)); server_cb->get_optional = saveopt; val_free_value(*valroot); *valroot = NULL; *retres = res; val_free_value(curparm); return NULL; } } switch (targobj->objtype) { case OBJ_TYP_ANYXML: case OBJ_TYP_LEAF: if (isdelete) { dofill = FALSE; } else if (obj_get_basetype(targobj) == NCX_BT_EMPTY) { /* already filled in the target value by creating it */ dofill = FALSE; } else if (!iswrite && !server_cb->get_optional) { /* for get operations, need --optional * set to prompt for the target leaf */ dofill = FALSE; } /* else fall through */ case OBJ_TYP_LEAF_LIST: if (dofill) { /* if targval is non-NULL then it is an empty * value struct because it was the deepest node * specified in the schema-instance string */ newparm = fill_value(server_cb, rpc, targobj, curparm, &res); if (newparm && res == NO_ERR) { if (targval) { if (targval == *valroot) { /* get a top-level leaf or leaf-list * so get rid of the instance ID and * just use the fill_value result */ val_free_value(*valroot); *valroot = NULL; } else { /* got a leaf or leaf-list that is not * a top-level node so just swap * out the old leaf for the new leaf */ val_swap_child(newparm, targval); val_free_value(targval); targval = NULL; } } } } else if (targval == NULL) { newparm = val_new_value(); if (!newparm) { log_error("\nError: malloc failure"); res = ERR_INTERNAL_MEM; } else { val_init_from_template(newparm, targobj); } if (isdelete) { val_reset_empty(newparm); } } else if (isdelete) { val_reset_empty(targval); } /* else going to use targval, not newval */ break; case OBJ_TYP_CHOICE: if (isdelete) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; log_error("\nError: delete operation not supported " "for choice"); } else if (targval == NULL) { newparm = val_new_value(); if (!newparm) { log_error("\nError: malloc failure"); res = ERR_INTERNAL_MEM; } else { val_init_from_template(newparm, targobj); } } if (res == NO_ERR && dofill) { res = get_choice(server_cb, rpc, targobj, (newparm) ? newparm : targval, curparm, iswrite, isdelete); } break; case OBJ_TYP_CASE: if (isdelete) { res = ERR_NCX_OPERATION_NOT_SUPPORTED; log_error("\nError: delete operation not supported " "for case"); } else if (targval == NULL) { newparm = val_new_value(); if (!newparm) { log_error("\nError: malloc failure"); res = ERR_INTERNAL_MEM; } else { val_init_from_template(newparm, targobj); } } if (res == NO_ERR && dofill) { res = get_case(server_cb, targobj, (newparm) ? newparm : targval, curparm, iswrite, isdelete); } break; default: if (targobj->objtype == OBJ_TYP_LIST) { ; // if delete, need to fill in just the keys } else if (isdelete) { dofill = FALSE; } if (targval == NULL) { newparm = val_new_value(); if (!newparm) { log_error("\nError: malloc failure"); res = ERR_INTERNAL_MEM; } else { val_init_from_template(newparm, targobj); } } if (res == NO_ERR && dofill) { val_value_t *mytarg = (newparm) ? newparm : targval; if (isextern) { if (curparm != NULL && mytarg != NULL && !typ_is_simple(curparm->btyp) && !typ_is_simple(mytarg->btyp)) { val_change_nsid(curparm, val_get_nsid(mytarg)); val_move_children(curparm, mytarg); } else if (curparm != NULL && mytarg != NULL && typ_is_simple(curparm->btyp) && !typ_is_simple(mytarg->btyp)) { val_change_nsid(curparm, val_get_nsid(mytarg)); val_add_child(curparm, mytarg); curparm = NULL; } else if (curparm != NULL && mytarg != NULL) { val_change_nsid(curparm, val_get_nsid(mytarg)); res = val_replace(curparm, mytarg); } /* else should not happen */ } else { /* secondary_args = "mtu=1500"; */ if(secondary_args) { char* argv[2]; argv[0] = strdup(obj_get_name(mytarg->obj)); argv[1] = (char*)secondary_args; curparm = cli_parse(server_cb->runstack_context, /*argc=*/2, argv, mytarg->obj, /*valonly=*/true, /*script=*/true, /*autocomp=*/true, /*mode=*/CLI_MODE_COMMAND, &res); free(argv[0]); /*TODO check for conflicting keys*/ if(res == NO_ERR) { res = val123_merge_cplx(mytarg, curparm); } } if (res==NO_ERR && !get_batchmode() && !secondary_args) { res = fill_valset(server_cb, rpc, mytarg, curparm, iswrite, isdelete); } } } } /* done with the current value */ if (curparm) { val_free_value(curparm); } server_cb->get_optional = saveopt; if (res == ERR_NCX_SKIPPED) { res = NO_ERR; if (newparm) { val_free_value(newparm); } newparm = xml_val_new_flag(obj_get_name(targobj), obj_get_nsid(targobj)); if (!newparm) { res = ERR_INTERNAL_MEM; log_error("\nError: malloc failure"); } else { /* need to set the real object so the * path to root will be built correctly */ newparm->obj = targobj; } if (res != NO_ERR) { val_free_value(*valroot); *valroot = NULL; } /* else *valroot is active and in use */ *retres = res; return newparm; } else if (res != NO_ERR) { if (newparm) { val_free_value(newparm); } val_free_value(*valroot); *valroot = NULL; *retres = res; return NULL; } else { /* *valroot is active and in use */ if (newparm != NULL) { *retres = res; return newparm; } else if (targval == *valroot) { /* need to make sure that a top-level * leaf is not treated as match-node * instead of a selection node */ if (obj_is_leafy(targval->obj)) { /* force the data type to be * empty so it will be treated * as a selection node */ val_force_empty(targval); } } return (newparm) ? newparm : targval; } } else if (isselect) { newparm = val_new_value(); if (!newparm) { log_error("\nError: malloc failed"); } else { val_init_from_template(newparm, parm->obj); res = val_set_simval_obj(newparm, parm->obj, fromstr); if (res != NO_ERR) { log_error("\nError: set 'select' failed (%s)", get_error_string(res)); val_free_value(newparm); newparm = NULL; } } if (fromurl != NULL) { m__free(fromurl); } /* *valroot == NULL and not in use */ *retres = res; return newparm; } else if (isvarref) { /* from global or local variable * *valroot == NULL and not used */ if (!fromstr) { ; /* should not be NULL */ } else if (*fromstr == '$' && fromstr[1] == '$') { /* $$foo */ vartype = VAR_TYP_GLOBAL; fromstr += 2; } else if (*fromstr == '$') { /* $foo */ vartype = VAR_TYP_LOCAL; fromstr++; } else { /* 'foo' : just assume local, not error */ vartype = VAR_TYP_LOCAL; } if (fromstr) { userval = var_get(server_cb->runstack_context, fromstr, vartype); if (!userval) { log_error("\nError: variable '%s' not found", fromstr); *retres = ERR_NCX_NOT_FOUND; return NULL; } else { newparm = val_clone(userval); if (!newparm) { log_error("\nError: malloc failed"); } *retres = ERR_INTERNAL_MEM; return newparm; } } } else { res = SET_ERROR(ERR_INTERNAL_VAL); } *retres = res; return NULL; } /* get_content_from_choice */ /******************************************************************** * FUNCTION add_one_operation_attr * * Add the nc:operation attribute to a value node * * INPUTS: * server_cb == server control block to use * val == value node to set * op == edit operation to use * * RETURNS: * status *********************************************************************/ static status_t add_one_operation_attr (server_cb_t *server_cb, val_value_t *val, op_editop_t op) { obj_template_t *operobj; const xmlChar *editopstr; val_value_t *metaval; status_t res; /* get the internal nc:operation object */ operobj = ncx_find_object(get_netconf_mod(server_cb), NC_OPERATION_ATTR_NAME); if (operobj == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* create a value node for the meta-value */ metaval = val_new_value(); if (!metaval) { return ERR_INTERNAL_MEM; } val_init_from_template(metaval, operobj); /* get the string value for the edit operation */ editopstr = op_editop_name(op); if (editopstr == NULL) { val_free_value(metaval); return SET_ERROR(ERR_INTERNAL_VAL); } /* set the meta variable value and other fields */ res = val_set_simval(metaval, obj_get_typdef(operobj), obj_get_nsid(operobj), obj_get_name(operobj), editopstr); if (res != NO_ERR) { val_free_value(metaval); return res; } dlq_enque(metaval, &val->metaQ); return NO_ERR; } /* add_one_operation_attr */ /******************************************************************** * FUNCTION add_operation_attr * * Add the nc:operation attribute to a value node * * INPUTS: * server_cb == server control block to use * val == value node to set * op == edit operation to use * topcontainer == TRUE if a top-level container * is passed in 'val' * == FALSE otherwise * * RETURNS: * status *********************************************************************/ static status_t add_operation_attr (server_cb_t *server_cb, val_value_t *val, op_editop_t op, boolean topcontainer) { val_value_t *childval; status_t res; res = NO_ERR; switch (val->obj->objtype) { case OBJ_TYP_CONTAINER: if (!topcontainer) { res = add_one_operation_attr(server_cb, val, op); break; } /* else fall through */ case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: for (childval = val_get_first_child(val); childval != NULL; childval = val_get_next_child(childval)) { res = add_one_operation_attr(server_cb, childval, op); if (res != NO_ERR) { return res; } } break; default: res = add_one_operation_attr(server_cb, val, op); } return res; } /* add_operation_attr */ /******************************************************************** * FUNCTION add_one_insert_attrs * * Add the yang:insert attribute(s) to a value node * * INPUTS: * val == value node to set * insop == insert operation to use * edit_target == edit target to use for key or value attr * * RETURNS: * status *********************************************************************/ static status_t add_one_insert_attrs (val_value_t *val, op_insertop_t insop, const xmlChar *edit_target) { obj_template_t *operobj; const xmlChar *insopstr; val_value_t *metaval; status_t res; xmlns_id_t yangid; yangid = xmlns_yang_id(); /* get the internal nc:operation object */ operobj = ncx_get_gen_string(); if (!operobj) { return SET_ERROR(ERR_INTERNAL_VAL); } insopstr = op_insertop_name(insop); /* create a value node for the meta-value */ metaval = val_new_value(); if (!metaval) { return ERR_INTERNAL_MEM; } val_init_from_template(metaval, operobj); val_set_qname(metaval, yangid, YANG_K_INSERT, xml_strlen(YANG_K_INSERT)); /* set the meta variable value and other fields */ res = val_set_simval(metaval, metaval->typdef, yangid, YANG_K_INSERT, insopstr); if (res != NO_ERR) { val_free_value(metaval); return res; } else { dlq_enque(metaval, &val->metaQ); } if (insop == OP_INSOP_BEFORE || insop == OP_INSOP_AFTER) { /* create a value node for the meta-value */ metaval = val_new_value(); if (!metaval) { return ERR_INTERNAL_MEM; } val_init_from_template(metaval, operobj); /* set the attribute name */ if (val->obj->objtype==OBJ_TYP_LEAF_LIST) { val_set_qname(metaval, yangid, YANG_K_VALUE, xml_strlen(YANG_K_VALUE)); } else { val_set_qname(metaval, yangid, YANG_K_KEY, xml_strlen(YANG_K_KEY)); } /* set the meta variable value and other fields */ res = val_set_simval(metaval, metaval->typdef, yangid, NULL, edit_target); if (res != NO_ERR) { val_free_value(metaval); return res; } else { dlq_enque(metaval, &val->metaQ); } } return NO_ERR; } /* add_one_insert_attrs */ /******************************************************************** * FUNCTION add_insert_attrs * * Add the yang:insert attribute(s) to a value node * * INPUTS: * val == value node to set * insop == insert operation to use * * RETURNS: * status *********************************************************************/ static status_t add_insert_attrs (val_value_t *val, op_insertop_t insop, const xmlChar *edit_target) { val_value_t *childval; status_t res; res = NO_ERR; switch (val->obj->objtype) { case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: for (childval = val_get_first_child(val); childval != NULL; childval = val_get_next_child(childval)) { res = add_one_insert_attrs(childval, insop, edit_target); if (res != NO_ERR) { return res; } } break; default: res = add_one_insert_attrs(val, insop, edit_target); } return res; } /* add_insert_attrs */ /* the function returns TRUE is there is an instance of -- parameter. The offset of the first '--' is returned. e.g. "create /a -- foo=1" wil return 10 */ static boolean detect_secondary_args_offset(const char* line, unsigned int* secondary_args_offset) { unsigned i; boolean secondary_args_flag=FALSE; yangcli_wordexp_t p; yangcli_wordexp(line, &p, 0); for (i = 0; i < p.we_wordc; i++) { if(0==strcmp("--",p.we_wordv[i])) { secondary_args_flag=TRUE; *secondary_args_offset=p.we_word_line_offset[i]; break; } } yangcli_wordfree(&p); return secondary_args_flag; } /******************************************************************** * FUNCTION do_edit * * Edit some database object on the server * operation attribute: * create/delete/merge/replace * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the create command * line == CLI input in progress * len == offset into line buffer to start parsing * reqdata = remote rpc val * timeoutval = timeout param * * OUTPUTS: * the completed data node is output and * an edit-config operation is sent to the server * * RETURNS: * status *********************************************************************/ static status_t do_edit (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len, op_editop_t editop, val_value_t** reqdata, uint32* timeoutval) { val_value_t *valset, *content, *parm, *valroot; status_t res; boolean getoptional, dofill; boolean isdelete, topcontainer, doattr; op_defop_t def_editop; boolean secondary_args_flag; unsigned int secondary_args_offset; char *primary_args; const char *secondary_args; /* init locals */ res = NO_ERR; content = NULL; valroot = NULL; dofill = TRUE; doattr = TRUE; topcontainer = FALSE; def_editop = server_cb->defop; if (editop == OP_EDITOP_DELETE || editop == OP_EDITOP_REMOVE) { isdelete = TRUE; } else { isdelete = FALSE; } secondary_args_flag = detect_secondary_args_offset(line, &secondary_args_offset); if(secondary_args_flag) { assert(secondary_args_offset>len); primary_args = malloc(secondary_args_offset-len+1); assert(primary_args); memcpy(primary_args,&line[len],secondary_args_offset-len); primary_args[secondary_args_offset-len]=0; secondary_args = line + secondary_args_offset + strlen("--"); } else { primary_args = strdup(&line[len]); assert(primary_args); secondary_args=NULL; } /* get the command line parameters for this command */ valset = get_valset(server_cb, rpc, primary_args, &res); free(primary_args); if (!valset || res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } /* get the timeout parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_TIMEOUT); if (parm && parm->res == NO_ERR) { *timeoutval = VAL_UINT(parm); } else { *timeoutval = server_cb->timeout; } /* get the 'fill-in optional parms' parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_OPTIONAL); if (parm && parm->res == NO_ERR) { getoptional = TRUE; } else { getoptional = server_cb->get_optional; } /* get the --nofill parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_NOFILL); if (parm && parm->res == NO_ERR) { dofill = FALSE; } /* get the contents specified in the 'from' choice */ content = get_content_from_choice(server_cb, rpc, valset, getoptional, isdelete, dofill, TRUE, /* iswrite */ &res, &valroot, secondary_args); if (content == NULL) { if (res != NO_ERR) { if (LOGDEBUG2) { log_debug2("get_content error %d = (%s)", res, get_error_string(res)); } res = ERR_NCX_MISSING_PARM; } else { log_error("\nError: operations on root '/' not supported"); res = ERR_NCX_OPERATION_NOT_SUPPORTED; } val_free_value(valset); return res; } if (valroot == NULL && content->btyp == NCX_BT_CONTAINER) { switch (editop) { case OP_EDITOP_CREATE: case OP_EDITOP_DELETE: case OP_EDITOP_REMOVE: topcontainer = TRUE; break; case OP_EDITOP_MERGE: def_editop = OP_DEFOP_MERGE; break; case OP_EDITOP_REPLACE: def_editop = OP_DEFOP_REPLACE; break; default: SET_ERROR(ERR_INTERNAL_VAL); } } if (doattr) { /* add nc:operation attribute to the value node */ res = add_operation_attr(server_cb, content, editop, topcontainer); if (res != NO_ERR) { log_error("\nError: Creation of nc:operation" " attribute failed"); val_free_value(valset); if (valroot) { val_free_value(valroot); } else { val_free_value(content); } return res; } } /* construct an edit-config PDU with default parameters */ res = edit_config_to_server_reqdata(server_cb, valroot, content, def_editop, reqdata); val_free_value(valroot); if (res != NO_ERR) { log_error("\nError: send %s operation failed (%s)", op_editop_name(editop), get_error_string(res)); } val_free_value(valset); return res; } /* do_edit */ /******************************************************************** * FUNCTION do_insert * * Insert a database object on the server * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the create command * line == CLI input in progress * len == offset into line buffer to start parsing * * OUTPUTS: * the completed data node is output and * an edit-config operation is sent to the server * * RETURNS: * status *********************************************************************/ static status_t do_insert (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len, val_value_t** reqdata, uint32* timeoutval) { val_value_t *valset, *content, *tempval, *parm, *valroot; const xmlChar *edit_target; op_editop_t editop; op_insertop_t insertop; status_t res; boolean getoptional, dofill; /* init locals */ res = NO_ERR; content = NULL; valroot = NULL; dofill = TRUE; /* get the command line parameters for this command */ valset = get_valset(server_cb, rpc, &line[len], &res); if (!valset || res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } /* get the 'fill-in optional parms' parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_OPTIONAL); if (parm && parm->res == NO_ERR) { getoptional = TRUE; } else { getoptional = server_cb->get_optional; } /* get the --nofill parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_NOFILL); if (parm && parm->res == NO_ERR) { dofill = FALSE; } /* get the contents specified in the 'from' choice */ content = get_content_from_choice(server_cb, rpc, valset, getoptional, FALSE, /* isdelete */ dofill, TRUE, /* iswrite */ &res, &valroot, NULL); if (!content) { val_free_value(valset); return (res == NO_ERR) ? ERR_NCX_MISSING_PARM : res; } /* get the timeout parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_TIMEOUT); if (parm && parm->res == NO_ERR) { *timeoutval = VAL_UINT(parm); } else { *timeoutval = server_cb->timeout; } /* get the insert order */ tempval = val_find_child(valset, YANGCLI_MOD, YANGCLI_ORDER); if (tempval && tempval->res == NO_ERR) { insertop = op_insertop_id(VAL_ENUM_NAME(tempval)); } else { insertop = OP_INSOP_LAST; } /* get the edit-config operation */ tempval = val_find_child(valset, YANGCLI_MOD, YANGCLI_OPERATION); if (tempval && tempval->res == NO_ERR) { editop = op_editop_id(VAL_ENUM_NAME(tempval)); } else { editop = OP_EDITOP_MERGE; } /* get the edit-target parameter only if the * order is 'before' or 'after'; ignore otherwise */ tempval = val_find_child(valset, YANGCLI_MOD, YANGCLI_EDIT_TARGET); if (tempval && tempval->res == NO_ERR) { edit_target = VAL_STR(tempval); } else { edit_target = NULL; } /* check if the edit-target is needed */ switch (insertop) { case OP_INSOP_BEFORE: case OP_INSOP_AFTER: if (!edit_target) { log_error("\nError: edit-target parameter missing"); if (valroot) { val_free_value(valroot); } else { val_free_value(content); } val_free_value(valset); return ERR_NCX_MISSING_PARM; } break; default: ; } /* add nc:operation attribute to the value node */ res = add_operation_attr(server_cb, content, editop, FALSE); if (res != NO_ERR) { log_error("\nError: Creation of nc:operation" " attribute failed"); } /* add yang:insert attribute and possibly a key or value * attribute as well */ if (res == NO_ERR) { res = add_insert_attrs(content, insertop, edit_target); if (res != NO_ERR) { log_error("\nError: Creation of yang:insert" " attribute(s) failed"); } } /* send the PDU, hand off the content node */ if (res == NO_ERR) { /* construct an edit-config PDU with default parameters */ res = edit_config_to_server_reqdata(server_cb, valroot, content, server_cb->defop, reqdata); if (res != NO_ERR) { log_error("\nError: send create operation failed (%s)", get_error_string(res)); } val_free_value(valroot); valroot = NULL; content = NULL; } /* cleanup and exit */ if (valroot) { val_free_value(valroot); } else if (content) { val_free_value(content); } val_free_value(valset); return res; } /* do_insert */ /******************************************************************** * FUNCTION do_sget * * Get some running config and/or state data with the operation, * using an optional subtree * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the create command * line == CLI input in progress * len == offset into line buffer to start parsing * * OUTPUTS: * the completed data node is output and * a operation is sent to the server * * RETURNS: * status *********************************************************************/ static status_t do_sget (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len, val_value_t** reqdata, uint32* timeoutval) { val_value_t *valset, *content, *parm, *valroot; status_t res; boolean dofill, getoptional; ncx_withdefaults_t withdef; /* init locals */ res = NO_ERR; content = NULL; valroot = NULL; dofill = TRUE; /* get the command line parameters for this command */ valset = get_valset(server_cb, rpc, &line[len], &res); if (!valset || res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_TIMEOUT); if (parm && parm->res == NO_ERR) { *timeoutval = VAL_UINT(parm); } else { *timeoutval = server_cb->timeout; } parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_NOFILL); if (parm && parm->res == NO_ERR) { dofill = FALSE; } parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_OPTIONAL); if (parm && parm->res == NO_ERR) { getoptional = TRUE; } else { getoptional = server_cb->get_optional; } parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_WITH_DEFAULTS); if (parm && parm->res == NO_ERR) { withdef = ncx_get_withdefaults_enum(VAL_STR(parm)); } else { withdef = server_cb->withdefaults; } /* get the contents specified in the 'from' choice */ content = get_content_from_choice(server_cb, rpc, valset, getoptional, FALSE, /* isdelete */ dofill, FALSE, /* iswrite */ &res, &valroot, NULL); if (res != NO_ERR) { val_free_value(valset); return (res==NO_ERR) ? ERR_NCX_MISSING_PARM : res; } /* construct a get PDU with the content as the filter */ res = get_to_server_reqdata(server_cb, valroot, content, NULL, NULL, dofill, withdef, reqdata); if (res != NO_ERR) { log_error("\nError: send get operation failed (%s)", get_error_string(res)); } val_free_value(valset); return res; } /* do_sget */ /******************************************************************** * FUNCTION do_sget_config * * Get some config data with the operation, * using an optional subtree * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the create command * line == CLI input in progress * len == offset into line buffer to start parsing * * OUTPUTS: * the completed data node is output and * a operation is sent to the server * * RETURNS: * status *********************************************************************/ static status_t do_sget_config (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len, val_value_t** reqdata, uint32* timeoutval) { val_value_t *valset, *content, *source, *parm, *valroot; status_t res; boolean dofill, getoptional; ncx_withdefaults_t withdef; dofill = TRUE; valroot = NULL; content = NULL; res = NO_ERR; /* get the command line parameters for this command */ valset = get_valset(server_cb, rpc, &line[len], &res); if (!valset || res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } /* get the timeout parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_TIMEOUT); if (parm && parm->res == NO_ERR) { *timeoutval = VAL_UINT(parm); } else { *timeoutval = server_cb->timeout; } /* get the'fill-in optional' parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_OPTIONAL); if (parm && parm->res == NO_ERR) { getoptional = TRUE; } else { getoptional = server_cb->get_optional; } /* get the --nofill parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_NOFILL); if (parm && parm->res == NO_ERR) { dofill = FALSE; } /* get the config source parameter */ source = val_find_child(valset, NULL, NCX_EL_SOURCE); if (!source) { log_error("\nError: mandatory source parameter missing"); val_free_value(valset); return ERR_NCX_MISSING_PARM; } /* get the with-defaults parameter */ parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_WITH_DEFAULTS); if (parm && parm->res == NO_ERR) { withdef = ncx_get_withdefaults_enum(VAL_STR(parm)); } else { withdef = server_cb->withdefaults; } /* get the contents specified in the 'from' choice */ content = get_content_from_choice(server_cb, rpc, valset, getoptional, FALSE, /* isdelete */ dofill, FALSE, /* iswrite */ &res, &valroot, NULL); if (res != NO_ERR) { val_free_value(valset); return res; } /* hand off this malloced node to send_get_to_server */ val_remove_child(source); val_change_nsid(source, xmlns_nc_id()); /* construct a get PDU with the content as the filter */ res = get_to_server_reqdata(server_cb, valroot, content, NULL, source, FALSE, /* dofill; don't fill again */ withdef, reqdata); if (res != NO_ERR) { log_error("\nError: send get-config operation failed (%s)", get_error_string(res)); } val_free_value(valset); return res; } /* do_sget_config */ /******************************************************************** * FUNCTION do_xget * * Get some running config and/or state data with the operation, * using an optional XPath filter * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the create command * line == CLI input in progress * len == offset into line buffer to start parsing * reqdata = remote rpc val * timeoutval = timeout param * * OUTPUTS: * the completed data node is output and * a operation is sent to the server * * RETURNS: * status *********************************************************************/ static status_t do_xget (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len, val_value_t** reqdata, uint32* timeoutval) { const ses_cb_t *scb; const mgr_scb_t *mscb; val_value_t *valset, *content, *parm, *valroot; const xmlChar *str; status_t res; uint32 retcode; ncx_withdefaults_t withdef; /* init locals */ res = NO_ERR; content = NULL; valroot = NULL; /* get the session info */ scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { return SET_ERROR(ERR_INTERNAL_VAL); } mscb = (const mgr_scb_t *)scb->mgrcb; /* get the command line parameters for this command */ valset = get_valset(server_cb, rpc, &line[len], &res); if (!valset || res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } /* get the timeout parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_TIMEOUT); if (parm && parm->res == NO_ERR) { *timeoutval = VAL_UINT(parm); } else { *timeoutval = server_cb->timeout; } /* get the with-defaults parameter */ parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_WITH_DEFAULTS); if (parm && parm->res == NO_ERR) { withdef = ncx_get_withdefaults_enum(VAL_ENUM_NAME(parm)); } else { withdef = server_cb->withdefaults; } /* check if the server supports :xpath */ if (!cap_std_set(&mscb->caplist, CAP_STDID_XPATH)) { switch (server_cb->baddata) { case NCX_BAD_DATA_IGNORE: break; case NCX_BAD_DATA_WARN: log_warn("\nWarning: server does not have :xpath support"); break; case NCX_BAD_DATA_CHECK: retcode = 0; log_warn("\nWarning: server does not have :xpath support"); res = get_yesno(server_cb, (const xmlChar *)"Send request anyway?", YESNO_NO, &retcode); if (res == NO_ERR) { switch (retcode) { case YESNO_CANCEL: case YESNO_NO: res = ERR_NCX_CANCELED; break; case YESNO_YES: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } break; case NCX_BAD_DATA_ERROR: log_error("\nError: server does not have :xpath support"); res = ERR_NCX_OPERATION_NOT_SUPPORTED; break; case NCX_BAD_DATA_NONE: default: res = SET_ERROR(ERR_INTERNAL_VAL); } } /* check any error so far */ if (res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } /* get the contents specified in the 'from' choice */ content = get_content_from_choice(server_cb, rpc, valset, FALSE, /* getoptional */ FALSE, /* isdelete */ FALSE, /* dofill */ FALSE, /* iswrite */ &res, &valroot, NULL); if (content) { if (content->btyp == NCX_BT_STRING && VAL_STR(content)) { str = VAL_STR(content); while (*str && *str != '"') { str++; } if (*str) { log_error("\nError: select string cannot " "contain a double quote"); } else { /* construct a get PDU with the content * as the filter * !! passing off content memory even on error !! */ res = get_to_server_reqdata(server_cb, valroot, NULL, content, NULL, FALSE, withdef, reqdata); content = NULL; if (res != NO_ERR) { log_error("\nError: send get operation" " failed (%s)", get_error_string(res)); } } } else { log_error("\nError: xget content wrong type"); } if (valroot) { /* should not happen */ val_free_value(valroot); } if (content) { val_free_value(content); } } val_free_value(valset); return res; } /* do_xget */ /******************************************************************** * FUNCTION do_xget_config * * Get some config data with the operation, * using an optional XPath filter * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the create command * line == CLI input in progress * len == offset into line buffer to start parsing * * OUTPUTS: * the completed data node is output and * a operation is sent to the server * * RETURNS: * status *********************************************************************/ static status_t do_xget_config (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len, val_value_t** reqdata, uint32* timeoutval) { const ses_cb_t *scb; const mgr_scb_t *mscb; val_value_t *valset, *content, *source, *parm, *valroot; const xmlChar *str; status_t res; uint32 retcode; ncx_withdefaults_t withdef; /* init locals */ res = NO_ERR; content = NULL; valroot = NULL; /* get the session info */ scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { return SET_ERROR(ERR_INTERNAL_VAL); } mscb = (const mgr_scb_t *)scb->mgrcb; /* get the command line parameters for this command */ valset = get_valset(server_cb, rpc, &line[len], &res); if (!valset || res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } /* get the source parameter */ source = val_find_child(valset, NULL, NCX_EL_SOURCE); if (!source) { log_error("\nError: mandatory source parameter missing"); val_free_value(valset); return ERR_NCX_MISSING_PARM; } /* get the with-defaults parameter */ parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_WITH_DEFAULTS); if (parm && parm->res == NO_ERR) { withdef = ncx_get_withdefaults_enum(VAL_ENUM_NAME(parm)); } else { withdef = server_cb->withdefaults; } /* get the timeout parameter */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_TIMEOUT); if (parm && parm->res == NO_ERR) { *timeoutval = VAL_UINT(parm); } else { *timeoutval = server_cb->timeout; } /* check if the server supports :xpath */ if (!cap_std_set(&mscb->caplist, CAP_STDID_XPATH)) { switch (server_cb->baddata) { case NCX_BAD_DATA_IGNORE: break; case NCX_BAD_DATA_WARN: log_warn("\nWarning: server does not have :xpath support"); break; case NCX_BAD_DATA_CHECK: retcode = 0; log_warn("\nWarning: server does not have :xpath support"); res = get_yesno(server_cb, (const xmlChar *)"Send request anyway?", YESNO_NO, &retcode); if (res == NO_ERR) { switch (retcode) { case YESNO_CANCEL: case YESNO_NO: res = ERR_NCX_CANCELED; break; case YESNO_YES: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } break; case NCX_BAD_DATA_ERROR: log_error("\nError: server does not have :xpath support"); res = ERR_NCX_OPERATION_NOT_SUPPORTED; break; case NCX_BAD_DATA_NONE: default: res = SET_ERROR(ERR_INTERNAL_VAL); } } /* check any error so far */ if (res != NO_ERR) { if (valset) { val_free_value(valset); } return res; } /* get the contents specified in the 'from' choice */ content = get_content_from_choice(server_cb, rpc, valset, FALSE, /* getoptional */ FALSE, /* isdelete */ FALSE, /* dofill */ FALSE, /* iswrite */ &res, &valroot, NULL); if (content) { if (content->btyp == NCX_BT_STRING && VAL_STR(content)) { str = VAL_STR(content); while (*str && *str != '"') { str++; } if (*str) { log_error("\nError: select string cannot " "contain a double quote"); } else { /* hand off this malloced node to send_get_to_server */ val_remove_child(source); val_change_nsid(source, xmlns_nc_id()); /* construct a get PDU with the content as the filter * !! hand off content memory here, even on error !! */ res = get_to_server_reqdata(server_cb, valroot, NULL, content, source, FALSE, withdef, reqdata); content = NULL; if (res != NO_ERR) { log_error("\nError: send get-config " "operation failed (%s)", get_error_string(res)); } } } else { log_error("\nError: xget content wrong type"); } if (valroot) { /* should not happen */ val_free_value(valroot); } if (content) { val_free_value(content); } } val_free_value(valset); return res; } /* do_xget_config */ /******************************************************************** * FUNCTION do_history_show (sub-mode of history RPC) * * history show [-1] * * INPUTS: * server_cb == server control block to use * maxlines == max number of history entries to show * mode == requested help mode * * RETURNS: * status *********************************************************************/ static status_t do_history_show (server_cb_t *server_cb, int maxlines, help_mode_t mode) { FILE *outputfile; const char *format; int glstatus; status_t res = NO_ERR; outputfile = log_get_logfile(); if (!outputfile) { /* use STDOUT instead */ outputfile = stdout; } if (mode == HELP_MODE_FULL) { format = "\n [%N]\t%D %T %H"; } else { format = "\n [%N]\t%H"; } glstatus = gl_show_history(server_cb->cli_gl, outputfile, format, 1, maxlines); if (glstatus) { log_error("\nError: show history failed"); res = ERR_NCX_OPERATION_FAILED; } fputc('\n', outputfile); return res; } /* do_history_show */ /******************************************************************** * FUNCTION do_history_clear (sub-mode of history RPC) * * history clear * * INPUTS: * server_cb == server control block to use * * RETURNS: * status *********************************************************************/ static status_t do_history_clear (server_cb_t *server_cb) { gl_clear_history(server_cb->cli_gl, 1); return NO_ERR; } /* do_history_clear */ /******************************************************************** * FUNCTION do_history_load (sub-mode of history RPC) * * history load * * INPUTS: * server_cb == server control block to use * fname == filename parameter value * if missing then try the previous history file name * * RETURNS: * status *********************************************************************/ static status_t do_history_load (server_cb_t *server_cb, const xmlChar *fname) { status_t res; int glstatus; res = NO_ERR; if (fname && *fname) { if (server_cb->history_filename) { m__free(server_cb->history_filename); } server_cb->history_filename = xml_strdup(fname); if (!server_cb->history_filename) { res = ERR_INTERNAL_MEM; } } else if (server_cb->history_filename) { fname = server_cb->history_filename; } else { res = ERR_NCX_INVALID_VALUE; } if (res == NO_ERR) { glstatus = gl_load_history(server_cb->cli_gl, (const char *)fname, "#"); /* comment prefix */ if (glstatus) { res = ERR_NCX_OPERATION_FAILED; } } return res; } /* do_history_load */ /******************************************************************** * FUNCTION do_history_save (sub-mode of history RPC) * * history save * * INPUTS: * server_cb == server control block to use * fname == filename parameter value * if missing then try the previous history file name * * RETURNS: * status *********************************************************************/ static status_t do_history_save (server_cb_t *server_cb, const xmlChar *fname) { status_t res; int glstatus; res = NO_ERR; if (fname && *fname) { if (server_cb->history_filename) { m__free(server_cb->history_filename); } server_cb->history_filename = xml_strdup(fname); if (!server_cb->history_filename) { res = ERR_INTERNAL_MEM; } } else if (server_cb->history_filename) { fname = server_cb->history_filename; } else { res = ERR_NCX_INVALID_VALUE; } if (res == NO_ERR) { glstatus = gl_save_history(server_cb->cli_gl, (const char *)fname, "#", /* comment prefix */ -1); /* save all entries */ if (glstatus) { res = ERR_NCX_OPERATION_FAILED; } } return res; } /* do_history_save */ /******************************************************************** * FUNCTION do_history (local RPC) * * Do Command line history support operations * * history * show * clear * load * save * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the history command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ static status_t do_history (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *parm; status_t res; boolean done; help_mode_t mode; done = FALSE; res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (valset && res == NO_ERR) { mode = HELP_MODE_NORMAL; /* check if the 'brief' flag is set first */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_BRIEF); if (parm && parm->res == NO_ERR) { mode = HELP_MODE_BRIEF; } else { /* check if the 'full' flag is set first */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_FULL); if (parm && parm->res == NO_ERR) { mode = HELP_MODE_FULL; } } /* find the 1 of N choice */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_SHOW); if (parm) { /* do show history */ res = do_history_show(server_cb, VAL_INT(parm), mode); done = TRUE; } if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_CLEAR); if (parm) { /* do clear history */ res = do_history_clear(server_cb); done = TRUE; if (res == NO_ERR) { log_info("\nOK\n"); } else { log_error("\nError: clear history failed\n"); } } } if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_LOAD); if (parm) { if (!VAL_STR(parm) || !*VAL_STR(parm)) { /* do history load buffer: default filename */ res = do_history_load(server_cb, YANGCLI_DEF_HISTORY_FILE); } else { /* do history load buffer */ res = do_history_load(server_cb, VAL_STR(parm)); } done = TRUE; if (res == NO_ERR) { log_info("\nOK\n"); } else { log_error("\nError: load history failed\n"); } } } if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_SAVE); if (parm) { if (!VAL_STR(parm) || !*VAL_STR(parm)) { /* do history save buffer: default filename */ res = do_history_save(server_cb, YANGCLI_DEF_HISTORY_FILE); } else { /* do history save buffer */ res = do_history_save(server_cb, VAL_STR(parm)); } done = TRUE; if (res == NO_ERR) { log_info("\nOK\n"); } else { log_error("\nError: save history failed\n"); } } } if (!done) { res = do_history_show(server_cb, YANGCLI_DEF_HISTORY_LINES, mode); } } if (valset) { val_free_value(valset); } return res; } /* do_history */ /******************************************************************** * FUNCTION do_recall (local RPC) * * Do Command line history support operations * * recall index=n * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the history command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ static status_t do_recall (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *parm; status_t res; res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (valset && res == NO_ERR) { /* find the mandatory index */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_INDEX); if (parm) { /* do show history */ res = do_line_recall(server_cb, VAL_UINT(parm)); } else { res = ERR_NCX_MISSING_PARM; log_error("\nError: missing index parameter"); } } if (valset) { val_free_value(valset); } return res; } /* do_recall */ /******************************************************************** * FUNCTION do_eventlog_show (sub-mode of eventlog RPC) * * eventlog show=-1 start=0 full * * INPUTS: * server_cb == server control block to use * maxevents == max number of eventlog entries to show * startindex == start event index number to show * mode == requested help mode * * RETURNS: * status *********************************************************************/ static status_t do_eventlog_show (server_cb_t *server_cb, int32 maxevents, uint32 startindex, help_mode_t mode) { mgr_not_msg_t *notif; val_value_t *sequence_id; logfn_t logfn; boolean done, imode; uint32 eventindex, maxindex, eventsdone; if (maxevents > 0) { maxindex = (uint32)maxevents; } else if (maxevents == -1) { maxindex = 0; } else { return ERR_NCX_INVALID_VALUE; } imode = interactive_mode(); if (imode) { logfn = log_stdout; } else { logfn = log_write; } done = FALSE; eventindex = 0; eventsdone = 0; for (notif = (mgr_not_msg_t *) dlq_firstEntry(&server_cb->notificationQ); notif != NULL && !done; notif = (mgr_not_msg_t *)dlq_nextEntry(notif)) { if (eventindex >= startindex) { sequence_id = val_find_child(notif->notification, NULL, NCX_EL_SEQUENCE_ID); (*logfn)("\n [%u]\t", eventindex); if (mode != HELP_MODE_BRIEF && notif->eventTime) { (*logfn)(" [%s] ", VAL_STR(notif->eventTime)); } if (mode != HELP_MODE_BRIEF) { if (sequence_id) { (*logfn)("(%u)\t", VAL_UINT(sequence_id)); } else { (*logfn)("(--)\t"); } } /* print the eventType in the desired format */ if (notif->eventType) { switch (server_cb->display_mode) { case NCX_DISPLAY_MODE_PLAIN: (*logfn)("<%s>", notif->eventType->name); break; case NCX_DISPLAY_MODE_PREFIX: case NCX_DISPLAY_MODE_XML: (*logfn)("<%s:%s>", val_get_mod_prefix(notif->eventType), notif->eventType->name); break; case NCX_DISPLAY_MODE_MODULE: (*logfn)("<%s:%s>", val_get_mod_name(notif->eventType), notif->eventType->name); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } else { (*logfn)("<>"); } if (mode == HELP_MODE_FULL) { val_dump_value_max(notif->notification, 0, server_cb->defindent, (imode) ? DUMP_VAL_STDOUT : DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); (*logfn)("\n"); } eventsdone++; } if (maxindex && (eventsdone == maxindex)) { done = TRUE; } eventindex++; } if (eventsdone) { (*logfn)("\n"); } return NO_ERR; } /* do_eventlog_show */ /******************************************************************** * FUNCTION do_eventlog_clear (sub-mode of eventlog RPC) * * eventlog clear * * INPUTS: * server_cb == server control block to use * maxevents == max number of events to clear * zero means clear all * * RETURNS: * status *********************************************************************/ static status_t do_eventlog_clear (server_cb_t *server_cb, int32 maxevents) { mgr_not_msg_t *notif; int32 eventcount; if (maxevents > 0) { eventcount = 0; while (!dlq_empty(&server_cb->notificationQ) && eventcount < maxevents) { notif = (mgr_not_msg_t *) dlq_deque(&server_cb->notificationQ); mgr_not_free_msg(notif); eventcount++; } } else if (maxevents == -1) { while (!dlq_empty(&server_cb->notificationQ)) { notif = (mgr_not_msg_t *) dlq_deque(&server_cb->notificationQ); mgr_not_free_msg(notif); } } else { return ERR_NCX_INVALID_VALUE; } return NO_ERR; } /* do_eventlog_clear */ /******************************************************************** * FUNCTION do_eventlog (local RPC) * * Do Notification Event Log support operations * * eventlog * show * clear * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the history command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ static status_t do_eventlog (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *parm, *start; status_t res; boolean done; help_mode_t mode; done = FALSE; res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (valset && res == NO_ERR) { mode = HELP_MODE_NORMAL; /* check if the 'brief' flag is set first */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_BRIEF); if (parm && parm->res == NO_ERR) { mode = HELP_MODE_BRIEF; } else { /* check if the 'full' flag is set first */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_FULL); if (parm && parm->res == NO_ERR) { mode = HELP_MODE_FULL; } } /* optional start position for show */ start = val_find_child(valset, YANGCLI_MOD, YANGCLI_START); /* find the 1 of N choice */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_SHOW); if (parm) { /* do show eventlog */ res = do_eventlog_show(server_cb, VAL_INT(parm), (start) ? VAL_UINT(start) : 0, mode); done = TRUE; } if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_CLEAR); if (parm) { /* do clear event log */ res = do_eventlog_clear(server_cb, VAL_INT(parm)); done = TRUE; if (res == NO_ERR) { log_info("\nOK\n"); } else { log_error("\nError: clear event log failed\n"); } } } if (!done) { res = do_eventlog_show(server_cb, -1, (start) ? VAL_UINT(start) : 0, mode); } } if (valset) { val_free_value(valset); } return res; } /* do_eventlog */ /******************************************************************** * FUNCTION do_local_conn_command_reqdata * * Handle local connection mode RPC operations from yangcli.yang * for those commands that translate to single standard netconf RPC * * INPUTS: * server_cb == server control block to use * rpc == template for the local RPC * line == input command line from user * len == line length * * OUTPUTS: * reqdata = remote rpc val * timeoutval = timeout val * * RETURNS: * NO_ERR if a RPC reqdata was created OK * ERR_NCX_SKIPPED if no matching command was detected * some other error if command execution failed *********************************************************************/ status_t do_local_conn_command_reqdata(server_cb_t *server_cb, obj_template_t *rpc, xmlChar *line, uint32 len, val_value_t** reqdata, uint32* timeoutval) { const xmlChar *rpcname; status_t res; res = NO_ERR; rpcname = obj_get_name(rpc); if (!xml_strcmp(rpcname, YANGCLI_CREATE)) { res = do_edit(server_cb, rpc, line, len, OP_EDITOP_CREATE, reqdata, timeoutval); } else if (!xml_strcmp(rpcname, YANGCLI_DELETE)) { res = do_edit(server_cb, rpc, line, len, OP_EDITOP_DELETE, reqdata, timeoutval); } else if (!xml_strcmp(rpcname, YANGCLI_REMOVE)) { res = do_edit(server_cb, rpc, line, len, OP_EDITOP_REMOVE, reqdata, timeoutval); } else if (!xml_strcmp(rpcname, YANGCLI_INSERT)) { res = do_insert(server_cb, rpc, line, len, reqdata, timeoutval); } else if (!xml_strcmp(rpcname, YANGCLI_MERGE)) { res = do_edit(server_cb, rpc, line, len, OP_EDITOP_MERGE, reqdata, timeoutval); } else if (!xml_strcmp(rpcname, YANGCLI_REPLACE)) { res = do_edit(server_cb, rpc, line, len, OP_EDITOP_REPLACE, reqdata, timeoutval); } else if (!xml_strcmp(rpcname, YANGCLI_SGET)) { res = do_sget(server_cb, rpc, line, len, reqdata, timeoutval); } else if (!xml_strcmp(rpcname, YANGCLI_SGET_CONFIG)) { res = do_sget_config(server_cb, rpc, line, len, reqdata, timeoutval); } else if (!xml_strcmp(rpcname, YANGCLI_XGET)) { res = do_xget(server_cb, rpc, line, len, reqdata, timeoutval); } else if (!xml_strcmp(rpcname, YANGCLI_XGET_CONFIG)) { res = do_xget_config(server_cb, rpc, line, len, reqdata, timeoutval); } else { res = ERR_NCX_SKIPPED; } return res; } /* do_local_conn_command_reqdata */ /******************************************************************** * FUNCTION do_local_conn_command * * Handle local connection mode RPC operations from yangcli.yang * * INPUTS: * server_cb == server control block to use * rpc == template for the local RPC * line == input command line from user * len == line length * * OUTPUTS: * server_cb->state may be changed or other action taken * the line buffer is NOT consumed or freed by this function * * RETURNS: * NO_ERR if a RPC was executed OK * ERR_NCX_SKIPPED if no command was invoked * some other error if command execution failed *********************************************************************/ static status_t do_local_conn_command (server_cb_t *server_cb, obj_template_t *rpc, xmlChar *line, uint32 len) { const xmlChar *rpcname; status_t res; boolean cond; val_value_t* reqdata; uint32 timeoutval; ses_cb_t *scb; mgr_rpc_req_t *req = NULL; rpcname = obj_get_name(rpc); cond = runstack_get_cond_state(server_cb->runstack_context); res = do_local_conn_command_reqdata(server_cb, rpc, line, len, &reqdata, &timeoutval); if(res==NO_ERR) { /* get the session control block */ if (!cond) { val_free_value(reqdata); if(LOGDEBUG) { log_debug("\nrSkipping false conditional command '%s'", rpcname); } return res; } scb = mgr_ses_get_scb(server_cb->mysid); assert(scb); /* allocate an RPC request and send it */ req = mgr_rpc_new_request(scb); if (!req) { res = ERR_INTERNAL_MEM; log_error("\nError allocating a new RPC request"); } else { req->data = reqdata; req->rpc = reqdata->obj; req->timeout = timeoutval; if (server_cb->command_mode == CMD_MODE_NORMAL && LOGINFO && server_cb->echo_requests) { log_info("\nRPC Request %s for session %u:\n", req->msg_id, server_cb->mysid); val_dump_value_max(reqdata, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); log_info("\n"); } /* the request will be stored if this returns NO_ERR */ res = mgr_rpc_send_request(scb, req, yangcli_reply_handler); if (res == NO_ERR) { server_cb->state = MGR_IO_ST_CONN_RPYWAIT; } } } if(res != ERR_NCX_SKIPPED) { return res; } if (!xml_strcmp(rpcname, YANGCLI_GET_LOCKS)) { if (cond) { res = do_get_locks(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_RELEASE_LOCKS)) { if (cond) { res = do_release_locks(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_SAVE)) { if (cond) { if (len < xml_strlen(line)) { res = ERR_NCX_INVALID_VALUE; log_error("\nError: Extra characters found (%s)", &line[len]); } else { res = do_save(server_cb); } } } else { res = ERR_NCX_SKIPPED; } if (res == NO_ERR && !cond && LOGDEBUG) { log_debug("\nrSkipping false conditional command '%s'", rpcname); } return res; } /******************************************************************** * FUNCTION do_local_command * * Handle local RPC operations from yangcli.yang * * INPUTS: * server_cb == server control block to use * rpc == template for the local RPC * line == input command line from user * len == length of line in bytes * * OUTPUTS: * state may be changed or other action taken * the line buffer is NOT consumed or freed by this function * * RETURNS: * status *********************************************************************/ static status_t do_local_command (server_cb_t *server_cb, obj_template_t *rpc, xmlChar *line, uint32 len) { const xmlChar *rpcname; status_t res; boolean cond, didcmd; res = NO_ERR; didcmd = FALSE; rpcname = obj_get_name(rpc); cond = runstack_get_cond_state(server_cb->runstack_context); if (!xml_strcmp(rpcname, YANGCLI_ALIAS)) { if (cond) { res = do_alias(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_ALIASES)) { if (cond) { res = do_aliases(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_CD)) { if (cond) { res = do_cd(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_CONNECT)) { if (cond) { res = do_connect(server_cb, rpc, line, len, FALSE); } } else if (!xml_strcmp(rpcname, YANGCLI_ELIF)) { res = do_elif(server_cb, rpc, line, len); didcmd = TRUE; } else if (!xml_strcmp(rpcname, YANGCLI_ELSE)) { res = do_else(server_cb, rpc, line, len); didcmd = TRUE; } else if (!xml_strcmp(rpcname, YANGCLI_END)) { res = do_end(server_cb, rpc, line, len); didcmd = TRUE; } else if (!xml_strcmp(rpcname, YANGCLI_EVAL)) { if (cond) { res = do_eval(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_EVENTLOG)) { if (cond) { res = do_eventlog(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_FILL)) { if (cond) { res = do_fill(server_cb, rpc, line, len, TRUE); } } else if (!xml_strcmp(rpcname, YANGCLI_HELP)) { if (cond) { res = do_help(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_HISTORY)) { if (cond) { res = do_history(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_IF)) { res = do_if(server_cb, rpc, line, len); didcmd = TRUE; } else if (!xml_strcmp(rpcname, YANGCLI_LIST)) { if (cond) { res = do_list(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_LOG_ERROR)) { if (cond) { res = do_log(server_cb, rpc, line, len, LOG_DEBUG_ERROR); } } else if (!xml_strcmp(rpcname, YANGCLI_LOG_WARN)) { if (cond) { res = do_log(server_cb, rpc, line, len, LOG_DEBUG_WARN); } } else if (!xml_strcmp(rpcname, YANGCLI_LOG_INFO)) { if (cond) { res = do_log(server_cb, rpc, line, len, LOG_DEBUG_INFO); } } else if (!xml_strcmp(rpcname, YANGCLI_LOG_DEBUG)) { if (cond) { res = do_log(server_cb, rpc, line, len, LOG_DEBUG_DEBUG); } } else if (!xml_strcmp(rpcname, YANGCLI_MGRLOAD)) { if (cond) { res = do_mgrload(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_PWD)) { if (cond) { res = do_pwd(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_QUIT)) { if (cond) { server_cb->state = MGR_IO_ST_SHUT; mgr_request_shutdown(); } } else if (!xml_strcmp(rpcname, YANGCLI_RECALL)) { if (cond) { res = do_recall(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_RUN)) { if (cond) { res = do_run(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_SHOW)) { if (cond) { res = do_show(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_START_TIMER)) { if (cond) { res = yangcli_timer_start(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_STOP_TIMER)) { if (cond) { res = yangcli_timer_stop(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_WHILE)) { res = do_while(server_cb, rpc, line, len); didcmd = TRUE; } else if (!xml_strcmp(rpcname, YANGCLI_UNSET)) { if (cond) { res = do_unset(server_cb, rpc, line, len); } } else if (!xml_strcmp(rpcname, YANGCLI_USERVARS)) { if (cond) { res = do_uservars(server_cb, rpc, line, len); } } else { res = ERR_NCX_INVALID_VALUE; log_error("\nError: The %s command is not allowed in this mode", rpcname); } if (res == NO_ERR && !cond && !didcmd && LOGDEBUG) { log_debug("\nrSkipping false conditional command '%s'", rpcname); } if (res != NO_ERR) { log_error("\n"); } return res; } /* do_local_command */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION top_command * * Top-level command handler * * INPUTS: * server_cb == server control block to use * line == input command line from user * * OUTPUTS: * state may be changed or other action taken * the line buffer is NOT consumed or freed by this function * * RETURNS: * status *********************************************************************/ status_t top_command (server_cb_t *server_cb, xmlChar *line) { obj_template_t *rpc = NULL; xmlChar *newline = NULL; xmlChar *useline = NULL; uint32 len = 0; ncx_node_t dtyp = NCX_NT_OBJ; status_t res = NO_ERR; #ifdef DEBUG if (!server_cb || !line) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (xml_strlen(line) == 0) { return res; } /* first check the command keyword to see if it is an alias */ newline = expand_alias(line, &res); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; useline = line; } else if (res == NO_ERR) { if (newline == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } useline = newline; } else { log_error("\nError: %s\n", get_error_string(res)); if (newline) { m__free(newline); } return res; } /* look for an RPC command match for the command keyword */ rpc = (obj_template_t *)parse_def(server_cb, &dtyp, useline, &len, &res); if (rpc==NULL || !obj_is_rpc(rpc)) { if (server_cb->result_name || server_cb->result_filename) { res = finish_result_assign(server_cb, NULL, useline); } else { if (res == ERR_NCX_DEF_NOT_FOUND) { /* this is an unknown command */ log_error("\nError: Unrecognized command\n"); } else if (res == ERR_NCX_AMBIGUOUS_CMD) { log_error("\n"); } else { log_error("\nError: %s\n", get_error_string(res)); } } } else if (is_yangcli_ns(obj_get_nsid(rpc))) { /* check handful of yangcli commands */ res = do_local_command(server_cb, rpc, useline, len); } else { res = ERR_NCX_OPERATION_FAILED; log_error("\nError: Not connected to server." "\nLocal commands only in this mode.\n"); } if (newline) { m__free(newline); } return res; } /* top_command */ /******************************************************************** * FUNCTION conn_command * * Connection level command handler * * INPUTS: * server_cb == server control block to use * line == input command line from user * * OUTPUTS: * state may be changed or other action taken * the line buffer is NOT consumed or freed by this function * * RETURNS: * status *********************************************************************/ status_t conn_command (server_cb_t *server_cb, xmlChar *line) { obj_template_t *rpc, *input; val_value_t *reqdata = NULL, *valset = NULL, *parm; ses_cb_t *scb; xmlChar *newline, *useline = NULL; uint32 len, linelen; status_t res = NO_ERR; boolean shut = FALSE; ncx_node_t dtyp; mgr_rpc_req_t *req = NULL; #ifdef DEBUG if (!server_cb || !line) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* make sure there is something to parse */ linelen = xml_strlen(line); if (!linelen) { return res; } /* first check the command keyword to see if it is an alias */ newline = expand_alias(line, &res); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; useline = line; } else if (res == NO_ERR) { if (newline == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } useline = newline; linelen = xml_strlen(newline); } else { log_error("\nError: %s\n", get_error_string(res)); if (newline) { m__free(newline); } return res; } /* get the RPC method template */ dtyp = NCX_NT_OBJ; rpc = (obj_template_t *)parse_def(server_cb, &dtyp, useline, &len, &res); if (rpc == NULL || !obj_is_rpc(rpc)) { if (server_cb->result_name || server_cb->result_filename) { res = finish_result_assign(server_cb, NULL, useline); } else { if (res == ERR_NCX_DEF_NOT_FOUND) { /* this is an unknown command */ log_error("\nError: Unrecognized command"); } else if (res == ERR_NCX_AMBIGUOUS_CMD) { log_error("\n"); } else { log_error("\nError: %s", get_error_string(res)); } } if (newline) { m__free(newline); } return res; } /* check local commands */ if (is_yangcli_ns(obj_get_nsid(rpc))) { if (!xml_strcmp(obj_get_name(rpc), YANGCLI_CONNECT)) { res = ERR_NCX_OPERATION_FAILED; log_stdout("\nError: Already connected"); } else { res = do_local_conn_command(server_cb, rpc, useline, len); if (res == ERR_NCX_SKIPPED) { res = do_local_command(server_cb, rpc, useline, len); } } if (newline) { m__free(newline); } return res; } /* else treat this as an RPC request going to the server * make sure this is a TRUE conditional command */ /* construct a method + parameter tree */ reqdata = xml_val_new_struct(obj_get_name(rpc), obj_get_nsid(rpc)); if (!reqdata) { log_error("\nError allocating a new RPC request"); res = ERR_INTERNAL_MEM; input = NULL; } else { /* should find an input node */ input = obj_find_child(rpc, NULL, YANG_K_INPUT); } /* check if any params are expected */ if (res == NO_ERR && input) { while (useline[len] && xml_isspace(useline[len])) { len++; } if (len < linelen) { valset = parse_rpc_cli(server_cb, rpc, &useline[len], &res); if (res != NO_ERR) { log_error("\nError in the parameters for '%s' command (%s)", obj_get_name(rpc), get_error_string(res)); } } /* check no input from user, so start a parmset */ if (res == NO_ERR && !valset) { valset = val_new_value(); if (!valset) { res = ERR_INTERNAL_MEM; } else { val_init_from_template(valset, input); } } /* fill in any missing parameters from the CLI */ if (res == NO_ERR) { if (interactive_mode()) { res = fill_valset(server_cb, rpc, valset, NULL, TRUE, FALSE); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } } } /* make sure the values are in canonical order * so compliant some servers will not complain */ val_set_canonical_order(valset); /* go through the parm list and move the values * to the reqdata struct. */ if (res == NO_ERR) { parm = val_get_first_child(valset); while (parm) { val_remove_child(parm); val_add_child(parm, reqdata); parm = val_get_first_child(valset); } } } /* check the close-session corner case */ if (res == NO_ERR && (obj_get_nsid(rpc) == xmlns_nc_id()) && !xml_strcmp(obj_get_name(rpc), NCX_EL_CLOSE_SESSION)) { shut = TRUE; } /* allocate an RPC request and send it */ if (res == NO_ERR) { scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { res = SET_ERROR(ERR_INTERNAL_PTR); } else { req = mgr_rpc_new_request(scb); if (!req) { res = ERR_INTERNAL_MEM; log_error("\nError allocating a new RPC request"); } else { req->data = reqdata; req->rpc = rpc; req->timeout = server_cb->timeout; } } if (res == NO_ERR) { if (server_cb->command_mode == CMD_MODE_NORMAL && LOGINFO && server_cb->echo_requests) { log_info("\nRPC Request %s for session %u:\n", req->msg_id, server_cb->mysid); val_dump_value_max(reqdata, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); log_info("\n"); } /* the request will be stored if this returns NO_ERR */ res = mgr_rpc_send_request(scb, req, yangcli_reply_handler); if (res != NO_ERR) { log_error("\nError: send <%s> message failed (%s)", obj_get_name(rpc), get_error_string(res)); } } } if (res != NO_ERR) { log_error("\n"); } if (valset) { val_free_value(valset); } if (newline) { m__free(newline); } if (res != NO_ERR) { if (req) { mgr_rpc_free_request(req); } else if (reqdata) { val_free_value(reqdata); } } else if (shut) { server_cb->state = MGR_IO_ST_CONN_CLOSEWAIT; } else { server_cb->state = MGR_IO_ST_CONN_RPYWAIT; } return res; } /* conn_command */ /******************************************************************** * FUNCTION do_startup_script * * Process run-script CLI parameter * * INPUTS: * server_cb == server control block to use * runscript == name of the script to run (could have path) * * SIDE EFFECTS: * runstack start with the runscript script if no errors * * RETURNS: * status *********************************************************************/ status_t do_startup_script (server_cb_t *server_cb, const xmlChar *runscript) { obj_template_t *rpc; xmlChar *line, *p; status_t res; uint32 linelen; #ifdef DEBUG if (!server_cb || !runscript) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (!*runscript) { return ERR_NCX_INVALID_VALUE; } /* get the 'run' RPC method template */ rpc = ncx_find_object(get_yangcli_mod(), YANGCLI_RUN); if (!rpc) { return ERR_NCX_DEF_NOT_FOUND; } /* create a dummy command line 'script ' */ linelen = xml_strlen(runscript) + xml_strlen(NCX_EL_SCRIPT) + 1; line = m__getMem(linelen+1); if (!line) { return ERR_INTERNAL_MEM; } p = line; p += xml_strcpy(p, NCX_EL_SCRIPT); *p++ = ' '; xml_strcpy(p, runscript); if (LOGDEBUG) { log_debug("\nBegin startup script '%s'", runscript); } /* fill in the value set for the input parameters */ res = do_run(server_cb, rpc, line, 0); m__free(line); return res; } /* do_startup_script */ /******************************************************************** * FUNCTION do_startup_command * * Process run-command CLI parameter * * INPUTS: * server_cb == server control block to use * runcommand == command string to run * * RETURNS: * status *********************************************************************/ status_t do_startup_command (server_cb_t *server_cb, const xmlChar *runcommand) { xmlChar *copystring; status_t res; #ifdef DEBUG if (!server_cb || !runcommand) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (!*runcommand) { return ERR_NCX_INVALID_VALUE; } if (xml_strlen(runcommand) > YANGCLI_LINELEN) { return ERR_NCX_RESOURCE_DENIED; } /* the top_command and conn_command functions * expect this buffer to be wriable */ copystring = xml_strdup(runcommand); if (!copystring) { return ERR_INTERNAL_MEM; } if (LOGDEBUG) { log_debug("\nBegin startup command '%s'", copystring); } /* only invoke the command in idle or connection idle states */ switch (server_cb->state) { case MGR_IO_ST_IDLE: res = top_command(server_cb, copystring); break; case MGR_IO_ST_CONN_IDLE: res = conn_command(server_cb, copystring); break; default: res = ERR_NCX_OPERATION_FAILED; } m__free(copystring); return res; } /* do_startup_command */ /******************************************************************** * FUNCTION get_cmd_line * * Read the current runstack context and construct * a command string for processing by do_run. * - Extended lines will be concatenated in the * buffer. If a buffer overflow occurs due to this * concatenation, an error will be returned * * INPUTS: * server_cb == server control block to use * res == address of status result * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to the command line to process (should treat as CONST !!!) * NULL if some error *********************************************************************/ xmlChar * get_cmd_line (server_cb_t *server_cb, status_t *res) { xmlChar *start, *str, *clibuff; boolean done; int len, total, maxlen; #ifdef DEBUG if (!server_cb || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif /* init locals */ clibuff = server_cb->clibuff; total = 0; str = NULL; maxlen = YANGCLI_BUFFLEN; done = FALSE; start = clibuff; /* get a command line, handling comment and continuation lines */ while (!done) { /* read the next line from the user */ str = get_line(server_cb); if (!str) { *res = ERR_NCX_READ_FAILED; done = TRUE; continue; } /* find end of string */ len = xml_strlen(str); /* get rid of EOLN if present */ if (len && str[len-1]=='\n') { str[--len] = 0; } /* check line continuation */ if (len && str[len-1]=='\\') { /* get rid of the final backslash */ str[--len] = 0; server_cb->climore = TRUE; } else { /* done getting lines */ *res = NO_ERR; done = TRUE; } /* copy the string to the clibuff */ if (total + len < maxlen) { xml_strcpy(start, str); start += len; total += len; } else { *res = ERR_BUFF_OVFL; done = TRUE; } str = NULL; } server_cb->climore = FALSE; if (*res == NO_ERR) { /* trim all the trailing whitespace * the user or the tab completion might * add an extra space at the end of * value, and this will cause an invalid-value * error to be incorrectly generated */ len = xml_strlen(clibuff); if (len > 0) { while (len > 0 && isspace(clibuff[len-1])) { len--; } clibuff[len] = 0; } return clibuff; } else { return NULL; } } /* get_cmd_line */ /******************************************************************** * FUNCTION do_connect * * INPUTS: * server_cb == server control block to use * rpc == rpc header for 'connect' command * line == input text from readline call, not modified or freed here * start == byte offset from 'line' where the parse RPC method * left off. This is eiother empty or contains some * parameters from the user * startupmode == TRUE if starting from init and should try * to connect right away if the mandatory parameters * are present. * == FALSE to check --optional and add parameters * if set or any missing mandatory parms * * OUTPUTS: * connect_valset parms may be set * create_session may be called * * RETURNS: * status *********************************************************************/ status_t do_connect (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 start, boolean startupmode) { obj_template_t *obj; val_value_t *connect_valset; val_value_t *valset, *testval; status_t res; boolean s1, s2, s3, s4, s5, tcp; #ifdef DEBUG if (server_cb == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* retrieve the 'connect' RPC template, if not done already */ if (rpc == NULL) { rpc = ncx_find_object(get_yangcli_mod(), YANGCLI_CONNECT); if (rpc == NULL) { server_cb->state = MGR_IO_ST_IDLE; log_write("\nError finding the 'connect' RPC method"); return ERR_NCX_DEF_NOT_FOUND; } } obj = obj_find_child(rpc, NULL, YANG_K_INPUT); if (obj == NULL) { server_cb->state = MGR_IO_ST_IDLE; log_write("\nError finding the connect RPC 'input' node"); return SET_ERROR(ERR_INTERNAL_VAL); } res = NO_ERR; tcp = FALSE; connect_valset = get_connect_valset(); /* process any parameters entered on the command line */ valset = NULL; if (line != NULL) { while (line[start] && xml_isspace(line[start])) { start++; } if (line[start]) { valset = parse_rpc_cli(server_cb, rpc, &line[start], &res); if (valset == NULL || res != NO_ERR) { if (valset != NULL) { val_free_value(valset); } log_write("\nError in the parameters for '%s' command (%s)", obj_get_name(rpc), get_error_string(res)); server_cb->state = MGR_IO_ST_IDLE; return res; } } } if (valset == NULL) { if (startupmode) { /* just clone the connect valset to start with */ valset = val_clone(connect_valset); if (valset == NULL) { server_cb->state = MGR_IO_ST_IDLE; log_write("\nError: malloc failed"); return ERR_INTERNAL_MEM; } } else { valset = val_new_value(); if (valset == NULL) { log_write("\nError: malloc failed"); server_cb->state = MGR_IO_ST_IDLE; return ERR_INTERNAL_MEM; } else { val_init_from_template(valset, obj); } } } /* make sure the 3 required parms are set */ s1 = val_find_child(valset, YANGCLI_MOD, YANGCLI_SERVER) ? TRUE : FALSE; s2 = val_find_child(valset, YANGCLI_MOD, YANGCLI_USER) ? TRUE : FALSE; s3 = val_find_child(valset, YANGCLI_MOD, YANGCLI_PASSWORD) ? TRUE : FALSE; s4 = val_find_child(valset, YANGCLI_MOD, YANGCLI_PUBLIC_KEY) ? TRUE : FALSE; s5 = val_find_child(valset, YANGCLI_MOD, YANGCLI_PRIVATE_KEY) ? TRUE : FALSE; /* check the transport parameter */ testval = val_find_child(valset, YANGCLI_MOD, YANGCLI_TRANSPORT); if (testval != NULL && testval->res == NO_ERR && !xml_strcmp(VAL_ENUM_NAME(testval), (const xmlChar *)"tcp")) { tcp = TRUE; } /* complete the connect valset if needed * and transfer it to the server_cb version * * try to get any missing params in valset */ if (interactive_mode()) { if (startupmode && s1 && s2 && (s3 || (s4 && s5) || tcp)) { if (LOGDEBUG3) { log_debug3("\nyangcli: CLI direct connect mode"); } } else { res = fill_valset(server_cb, rpc, valset, connect_valset, TRUE, FALSE); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } } } /* check error or operation canceled */ if (res != NO_ERR) { if (res != ERR_NCX_CANCELED) { log_write("\nError: Connect failed (%s)", get_error_string(res)); } server_cb->state = MGR_IO_ST_IDLE; val_free_value(valset); return res; } /* passing off valset memory here */ s1 = s2 = s3 = FALSE; if (valset != NULL) { /* save the malloced valset */ if (server_cb->connect_valset != NULL) { val_free_value(server_cb->connect_valset); } server_cb->connect_valset = valset; /* make sure the 3 required parms are set */ s1 = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_SERVER) ? TRUE : FALSE; s2 = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_USER) ? TRUE : FALSE; s3 = val_find_child(server_cb->connect_valset, YANGCLI_MOD, YANGCLI_PASSWORD) ? TRUE : FALSE; s4 = val_find_child(valset, YANGCLI_MOD, YANGCLI_PUBLIC_KEY) ? TRUE : FALSE; s5 = val_find_child(valset, YANGCLI_MOD, YANGCLI_PRIVATE_KEY) ? TRUE : FALSE; } /* check if all params present yet */ if (s1 && s2 && (s3 || (s4 && s5) || tcp)) { res = replace_connect_valset(server_cb->connect_valset); if (res != NO_ERR) { log_warn("\nWarning: connection parameters could not be saved"); res = NO_ERR; } create_session(server_cb); } else { res = ERR_NCX_MISSING_PARM; log_write("\nError: Connect failed due to missing parameter(s)"); server_cb->state = MGR_IO_ST_IDLE; } return res; } /* do_connect */ /******************************************************************** * FUNCTION parse_def * * Definitions have two forms: * def (all available modules searched) * module:def (explicit module name used) * prefix:def (if prefix-to-module found, explicit module name used) * * Parse the possibly module-qualified definition (module:def) * and find the template for the requested definition * * INPUTS: * server_cb == server control block to use * dtyp == definition type * (NCX_NT_OBJ or NCX_NT_TYP) * line == input command line from user * len == address of output var for number of bytes parsed * retres == address of return status * * OUTPUTS: * *dtyp is set if it started as NONE * *len == number of bytes parsed * *retres == return status * * RETURNS: * pointer to the found definition template or NULL if not found *********************************************************************/ void * parse_def (server_cb_t *server_cb, ncx_node_t *dtyp, xmlChar *line, uint32 *len, status_t *retres) { void *def, *lastmatch; xmlChar *start, *p, *q, oldp, oldq; const xmlChar *prefix, *defname, *modname, *defmod; ncx_module_t *mod; modptr_t *modptr; uint32 tempcount, matchcount; xmlns_id_t nsid; status_t res; boolean first; def = NULL; q = NULL; oldq = 0; *len = 0; start = line; modname = NULL; res = NO_ERR; /* skip any leading whitespace */ while (*start && xml_isspace(*start)) { start++; } p = start; /* look for a colon or EOS or whitespace to end command name */ while (*p && (*p != ':') && !xml_isspace(*p)) { p++; } /* make sure got something */ if (p==start) { return NULL; } /* search for a module prefix if a separator was found */ if (*p == ':') { /* use an explicit module prefix in YANG * only this exact module will be tried */ q = p+1; while (*q && !xml_isspace(*q)) { q++; } *len = q - line; /* save the old char and zero-terminate the definition * that will be parsed as a command name */ oldq = *q; *q = 0; oldp = *p; *p = 0; prefix = start; defname = p+1; } else { /* no prefix given so search all the available modules */ *len = p - line; oldp = *p; *p = 0; prefix = NULL; defname = start; } /* look in the registry for the definition name * first check if only the user supplied a module name */ if (prefix) { nsid = xmlns_find_ns_by_prefix(prefix); if (nsid) { modname = xmlns_get_module(nsid); } if (modname) { /* try exact match in specified module */ def = try_parse_def(server_cb, NULL, modname, defname, dtyp); } else { log_error("\nError: no module found for prefix '%s'", prefix); } } else { /* search for the command; try YANGCLI module first * do not care about duplicate matches in other * modules. An unprefixed keyword will match * a yangcli command first */ def = try_parse_def(server_cb, NULL, YANGCLI_MOD, defname, dtyp); if (def == NULL) { /* 2) try an exact match in the default module * do not care about duplicates because the purpose * of the default-module parm is to resolve any name * collisions by picking the default module */ defmod = get_default_module(); if (defmod != NULL) { /* do not re-check the yangcli and netconf modules */ if (xml_strcmp(defmod, NC_MODULE) && xml_strcmp(defmod, NCXMOD_IETF_NETCONF)) { def = try_parse_def(server_cb, NULL, defmod, defname, dtyp); } } } /* 3) if not found, try any server advertised module */ if (def == NULL && use_servercb(server_cb)) { /* try any of the server modules first * make sure there is only 1 exact match */ matchcount = 0; lastmatch = NULL; for (modptr = (modptr_t *) dlq_firstEntry(&server_cb->modptrQ); modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { def = try_parse_def(server_cb, modptr->mod, modptr->mod->name, defname, dtyp); if (def != NULL) { matchcount++; lastmatch = def; } } if (matchcount > 1) { /* generate ambiguous command error */ res = ERR_NCX_AMBIGUOUS_CMD; first = TRUE; for (modptr = (modptr_t *) dlq_firstEntry(&server_cb->modptrQ); modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { ncx_match_rpc_error(modptr->mod, modptr->mod->name, defname, FALSE, first); if (first) { first = FALSE; } } /* check any matches for local commands too */ ncx_match_rpc_error(get_yangcli_mod(), NULL, defname, FALSE, FALSE); } else { /* 0 or 1 matches found */ def = lastmatch; } } /* 4) try any of the manager-loaded modules */ if (res == NO_ERR && def == NULL) { /* make sure there is only 1 exact match */ matchcount = 0; lastmatch = NULL; for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { def = try_parse_def(server_cb, mod, mod->name, defname, dtyp); if (def != NULL) { lastmatch = def; matchcount++; } } if (matchcount > 1) { /* generate ambiguous command error */ res = ERR_NCX_AMBIGUOUS_CMD; ncx_match_rpc_error(NULL, NULL, defname, FALSE, TRUE); } else { /* 0 or 1 matches found */ def = lastmatch; } } } /* if not found and no error, try a partial command name */ if (res == NO_ERR && def == NULL && get_autocomp()) { matchcount = 0; lastmatch = NULL; if (prefix != NULL) { if (modname != NULL) { /* try to match a command from this module */ def = ncx_match_any_rpc(modname, defname, &matchcount); if (matchcount > 1) { res = ERR_NCX_AMBIGUOUS_CMD; ncx_match_rpc_error(NULL, modname, defname, TRUE, TRUE); } /* else 0 or 1 matches found */ } /* else module not found error already done */ } else { switch (*dtyp) { case NCX_NT_NONE: case NCX_NT_OBJ: if (use_servercb(server_cb)) { /* check for 1 or more matches */ for (modptr = (modptr_t *) dlq_firstEntry(&server_cb->modptrQ); modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { tempcount = 0; def = ncx_match_any_rpc_mod(modptr->mod, defname, &tempcount); if (def) { lastmatch = def; matchcount += tempcount; } } if (matchcount > 1) { /* server_cb had multiple matches */ res = ERR_NCX_AMBIGUOUS_CMD; first = TRUE; for (modptr = (modptr_t *) dlq_firstEntry(&server_cb->modptrQ); modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { ncx_match_rpc_error(modptr->mod, modptr->mod->name, defname, TRUE, first); if (first) { first = FALSE; } } /* list any partial local command matches */ ncx_match_rpc_error(get_yangcli_mod(), NULL, defname, TRUE, FALSE); } } if (res == NO_ERR && lastmatch == NULL) { /* did not match any of the server modules * or maybe no session active right now * check the modules that the client has loaded * with mgrload or at boot-time with the CLI */ matchcount = 0; def = ncx_match_any_rpc(NULL, defname, &matchcount); if (matchcount > 1) { res = ERR_NCX_AMBIGUOUS_CMD; ncx_match_rpc_error(NULL, NULL, defname, TRUE, TRUE); } } break; default: /* return NULL because only object types or * match anything mode will cause a partial match */ ; } } } /* restore string as needed */ *p = oldp; if (q) { *q = oldq; } /* return status if requested */ if (retres != NULL) { if (res != NO_ERR) { *retres = res; } else if (def == NULL) { *retres = ERR_NCX_DEF_NOT_FOUND; } else { *retres = NO_ERR; } } if (res == NO_ERR) { return def; } else { return NULL; } } /* parse_def */ /******************************************************************** * FUNCTION send_keepalive_get * * Send a operation to the server to keep the session * from getting timed out; server sent a keepalive request * and SSH will drop the session unless data is sent * within a configured time * * !!! NOT IMPLEMENTED !!! * * INPUTS: * server_cb == server control block to use * * OUTPUTS: * state may be changed or other action taken * * RETURNS: * status *********************************************************************/ status_t send_keepalive_get (server_cb_t *server_cb) { (void)server_cb; return NO_ERR; } /* send_keepalive_get */ /******************************************************************** * FUNCTION get_valset * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the command being processed * line == CLI input in progress * res == address of status result * * OUTPUTS: * *res is set to the status * * RETURNS: * malloced valset filled in with the parameters for * the specified RPC * *********************************************************************/ val_value_t * get_valset (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, status_t *res) { obj_template_t *obj; val_value_t *valset; uint32 len; *res = NO_ERR; valset = NULL; len = 0; set_completion_state(&server_cb->completion_state, rpc, NULL, CMD_STATE_GETVAL); /* skip leading whitespace */ while (line[len] && xml_isspace(line[len])) { len++; } /* check any non-whitespace entered after RPC method name */ if (line[len]) { valset = parse_rpc_cli(server_cb, rpc, &line[len], res); if (*res == ERR_NCX_SKIPPED) { log_stdout("\nError: no parameters defined for '%s' command", obj_get_name(rpc)); } else if (*res != NO_ERR) { log_stdout("\nError in the parameters for '%s' command (%s)", obj_get_name(rpc), get_error_string(*res)); } } obj = obj_find_child(rpc, NULL, YANG_K_INPUT); if (!obj || !obj_get_child_count(obj)) { *res = ERR_NCX_SKIPPED; if (valset) { val_free_value(valset); } return NULL; } /* check no input from user, so start a parmset */ if (*res == NO_ERR && !valset) { valset = val_new_value(); if (!valset) { *res = ERR_INTERNAL_MEM; } else { val_init_from_template(valset, obj); *res = val_add_defaults(valset, NULL, NULL, SCRIPTMODE); } } /* fill in any missing parameters from the CLI */ if (*res==NO_ERR && interactive_mode()) { *res = fill_valset(server_cb, rpc, valset, NULL, TRUE, FALSE); } if (*res==NO_ERR) { *res = val_instance_check(valset, valset->obj); } return valset; } /* get_valset */ /******************************************************************** * FUNCTION do_line_recall (execute the recall local RPC) * * recall n * * INPUTS: * server_cb == server control block to use * num == entry number of history entry entry to recall * * RETURNS: * status *********************************************************************/ status_t do_line_recall (server_cb_t *server_cb, unsigned long num) { GlHistoryLine history_line; int glstatus; server_cb->history_line_active = FALSE; memset(&history_line, 0x0, sizeof(GlHistoryLine)); glstatus = gl_lookup_history(server_cb->cli_gl, num, &history_line); if (glstatus == 0) { log_error("\nError: lookup command line history failed"); return ERR_NCX_OPERATION_FAILED; } if (server_cb->history_line) { m__free(server_cb->history_line); } /* save the line in the server_cb for next call * to get_line */ server_cb->history_line = xml_strdup((const xmlChar *)history_line.line); if (!server_cb->history_line) { return ERR_INTERNAL_MEM; } server_cb->history_line_active = TRUE; return NO_ERR; } /* do_line_recall */ /******************************************************************** * FUNCTION do_line_recall_string * * bang recall support * * INPUTS: * server_cb == server control block to use * line == command line to recall * * RETURNS: * status *********************************************************************/ status_t do_line_recall_string (server_cb_t *server_cb, const xmlChar *line) { GlHistoryLine history_line; GlHistoryRange history_range; int glstatus; uint32 len; unsigned long curindex; boolean done; len = xml_strlen(line); if (len == 0) { log_error("\nError: missing recall string\n"); return ERR_NCX_MISSING_PARM; } server_cb->history_line_active = FALSE; memset(&history_line, 0x0, sizeof(GlHistoryLine)); memset(&history_range, 0x0, sizeof(GlHistoryRange)); gl_range_of_history(server_cb->cli_gl, &history_range); if (history_range.nlines == 0) { log_error("\nError: no command line history found\n"); return ERR_NCX_OPERATION_FAILED; } /* look backwards through history buffer */ done = FALSE; for (curindex = history_range.newest; curindex >= history_range.oldest && !done; curindex--) { glstatus = gl_lookup_history(server_cb->cli_gl, curindex, &history_line); if (glstatus == 0) { continue; } if (!xml_strnicmp((const xmlChar *)history_line.line, line, len)) { done = TRUE; continue; } if (curindex == history_range.oldest) { log_error("\nError: command line '%s' not found\n", line); return ERR_NCX_OPERATION_FAILED; } } if (server_cb->history_line != NULL) { m__free(server_cb->history_line); } /* save the line in the server_cb for next call * to get_line */ server_cb->history_line = xml_strdup((const xmlChar *)history_line.line); if (server_cb->history_line == NULL) { return ERR_INTERNAL_MEM; } server_cb->history_line_active = TRUE; return NO_ERR; } /* do_line_recall_string */ /* END yangcli_cmd.c */ yuma123_2.14/netconf/src/yangcli/yangcli_cond.h0000664000175000017500000001121014770023131021571 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangcli_cond #define _H_yangcli_cond /* FILE: yangcli_cond.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Conditional commands for script looping and if-stmt ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 27-apr-10 abb Begun */ #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION do_while (local RPC) * * Handle the while command; start a new loopcb context * * while expr='xpath-str' [docroot=$foo] * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_while (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); /******************************************************************** * FUNCTION do_if (local RPC) * * Handle the if command; start a new ifcb context * * if expr='xpath-str' [docroot=$foo] * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_if (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); /******************************************************************** * FUNCTION do_elif (local RPC) * * Handle the if command; start a new ifcb context * * elif expr='xpath-str' [docroot=$foo] * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_elif (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); /******************************************************************** * FUNCTION do_else (local RPC) * * Handle the else command; start another conditional sub-block * to the current if-block * * else * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_else (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); /******************************************************************** * FUNCTION do_end (local RPC) * * Handle the end command; end an if or while block * * end * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_end (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli_cond */ yuma123_2.14/netconf/src/yangcli/yangcli_autolock.h0000664000175000017500000001426014770023131022477 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangcli_autolock #define _H_yangcli_autolock /* FILE: yangcli_autolock.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13-augr-09 abb Begun */ #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION do_get_locks (local RPC) * * get all the locks on the server * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the history command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_get_locks (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); /******************************************************************** * FUNCTION do_release_locks (local RPC) * * release all the locks on the server * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the history command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_release_locks (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); /******************************************************************** * FUNCTION handle_get_locks_request_to_server * * Send the first operation to the server * in a get-locks command * * INPUTS: * server_cb == server control block to use * first == TRUE if this is the first call; FALSE otherwise * done == address of return final done flag * * OUTPUTS: * server_cb->state may be changed or other action taken * *done == TRUE when the return code is NO_ERR and all * the locks are granted * FALSE otherwise * RETURNS: * status; if NO_ERR then check *done flag * otherwise done is true on any error *********************************************************************/ extern status_t handle_get_locks_request_to_server (server_cb_t *server_cb, boolean first, boolean *done); /******************************************************************** * FUNCTION handle_release_locks_request_to_server * * Send an operation to the server * in a get-locks command teardown or a release-locks * operation * * INPUTS: * server_cb == server control block to use * first == TRUE if this is the first call; FALSE otherwise * done == address of return final done flag * * OUTPUTS: * server_cb->state may be changed or other action taken * *done == TRUE when the return code is NO_ERR and all * the locks are granted * FALSE otherwise * RETURNS: * status; if NO_ERR then check *done flag * otherwise done is true on any error *********************************************************************/ extern status_t handle_release_locks_request_to_server (server_cb_t *server_cb, boolean first, boolean *done); /******************************************************************** * FUNCTION handle_locks_cleanup * * Deal with the cleanup for the get-locks or release-locks * * INPUTS: * server_cb == server control block to use * * OUTPUTS: * server_cb->state may be changed or other action taken * *********************************************************************/ extern void handle_locks_cleanup (server_cb_t *server_cb); /******************************************************************** * FUNCTION check_locks_timeout * * Check if the locks_timeout is active and if it expired yet * * INPUTS: * server_cb == server control block to use * * RETURNS: * TRUE if locks_timeout expired * FALSE if no timeout has occurred *********************************************************************/ extern boolean check_locks_timeout (server_cb_t *server_cb); /******************************************************************** * FUNCTION send_discard_changes_pdu_to_server * * Send a operation to the server * * INPUTS: * server_cb == server control block to use * * RETURNS: * status *********************************************************************/ extern status_t send_discard_changes_pdu_to_server (server_cb_t *server_cb); /******************************************************************** * FUNCTION clear_lock_cbs * * Clear the lock state info in all the lock control blocks * in the specified server_cb * * INPUTS: * server_cb == server control block to use * *********************************************************************/ extern void clear_lock_cbs (server_cb_t *server_cb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli_autolock */ yuma123_2.14/netconf/src/yangcli/yangcli_yang_library.c0000664000175000017500000002302314770023131023330 0ustar vladimirvladimir/* * Copyright (c) 2017, VLadimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_yang_library.c NETCONF YANG-based CLI Tool yang library support ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" #include "procdefs.h" #include "log.h" #include "mgr.h" #include "mgr_ses.h" #include "ncx.h" #include "ncx_feature.h" #include "ncx_list.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "op.h" #include "rpc.h" #include "rpc_err.h" #include "status.h" #include "val_util.h" #include "var.h" #include "xmlns.h" #include "xml_util.h" #include "xml_val.h" #include "yangconst.h" #include "yangcli.h" #include "yangcli_autoload.h" #include "yangcli_cmd.h" #include "yangcli_util.h" /******************************************************************** * FUNCTION make_get_yang_library_modules_state_reqdata * * Allocate and initialize reqdata value for /modules-state * * INPUTS: * server_cb == server control block to use * scb == session control block to use * * OUTPUTS: * out_rpc == obj_template_t** of the get-schema RPC * out_reqdata == val_value_t** of the get-schema data value * * RETURNS: * status *********************************************************************/ status_t make_get_yang_library_modules_state_reqdata(server_cb_t *server_cb, ses_cb_t *scb, obj_template_t** out_rpc, val_value_t** out_reqdata) { ncx_module_t *ietf_netconf_mod; ncx_module_t *ietf_yang_library_mod; obj_template_t *rpc_obj, *input_obj, *filter_obj, *modules_state_obj; val_value_t *request_val, *filter_val, *modules_state_val; val_value_t *type_meta_val; status_t res; xmlns_id_t nsid; res = NO_ERR; res = ncxmod_load_module (NCXMOD_NETCONF, NULL, NULL, &ietf_netconf_mod); assert(res==NO_ERR); rpc_obj = ncx_find_rpc(ietf_netconf_mod, "get"); assert(obj_is_rpc(rpc_obj)); input_obj = obj_find_child(rpc_obj, NULL, "input"); assert(input_obj!=NULL); filter_obj = obj_find_child(input_obj, NULL, "filter"); assert(filter_obj!=NULL); res = ncxmod_load_module ("ietf-yang-library", "2016-06-21", NULL, &ietf_yang_library_mod); assert(res==NO_ERR); modules_state_obj = ncx_find_object(ietf_yang_library_mod, "modules-state"); assert(modules_state_obj); request_val = val_new_value(); val_init_from_template(request_val, rpc_obj); filter_val = val_new_value(); val_init_from_template(filter_val, filter_obj); modules_state_val = val_new_value(); val_init_from_template(modules_state_val, modules_state_obj); type_meta_val = val_make_string(0, "type","subtree"); val_add_meta(type_meta_val, filter_val); val_add_child(filter_val, request_val); val_add_child(modules_state_val, filter_val); input_obj = obj_find_child(rpc_obj, NULL, YANG_K_INPUT); assert(input_obj); *out_rpc=rpc_obj; *out_reqdata=request_val; return res; } /* make_get_yang_library_modules_state_reqdata */ /******************************************************************** * FUNCTION send_get_yang_library_modules_state_to_server * * Send an /modules-state operation to the specified server * in MGR_IO_ST_AUTOLOAD state * * INPUTS: * server_cb == server control block to use * scb == session control block to use * * OUTPUTS: * server_cb->state may be changed or other action taken * * RETURNS: * status *********************************************************************/ static status_t send_get_yang_library_modules_state_to_server (server_cb_t *server_cb, ses_cb_t *scb) { status_t res; obj_template_t* rpc; val_value_t* reqdata; mgr_rpc_req_t *req; req = NULL; res = make_get_yang_library_modules_state_reqdata(server_cb, scb, &rpc, &reqdata); if(res!=NO_ERR) { return NO_ERR; } /* allocate an RPC request and send it */ req = mgr_rpc_new_request(scb); if (!req) { res = ERR_INTERNAL_MEM; log_error("\nError allocating a new RPC request"); } else { req->data = reqdata; req->rpc = rpc; req->timeout = server_cb->timeout; } if (res == NO_ERR) { if (LOGDEBUG) { log_debug("\nSending yang-library /modules-state autoload request."); } if (LOGDEBUG2) { log_debug2("\nabout to send RPC request with reqdata:"); val_dump_value_max(reqdata, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); } /* the request will be stored if this returns NO_ERR */ res = mgr_rpc_send_request(scb, req, yangcli_reply_handler); } if (res != NO_ERR) { if (req) { mgr_rpc_free_request(req); } else if (reqdata) { val_free_value(reqdata); } } else { server_cb->state = MGR_IO_ST_CONN_RPYWAIT; } return res; } /* send_get_yang_library_modules_state_to_server */ status_t get_yang_library_modules_state_reply_to_searchresult_entries(server_cb_t * server_cb, ses_cb_t *scb, val_value_t* reply) { ncxmod_search_result_t *searchresult; val_value_t *data_val; val_value_t *modules_state_val; val_value_t *module_val; mgr_scb_t *mscb; status_t res = NO_ERR; mscb = (mgr_scb_t *)scb->mgrcb; data_val = val_find_child(reply, NULL, NCX_EL_DATA); if (data_val == NULL) { res = SET_ERROR(ERR_NCX_DATA_MISSING); } modules_state_val = val_find_child(data_val, "ietf-yang-library", "modules-state"); if (modules_state_val == NULL) { res = SET_ERROR(ERR_NCX_DATA_MISSING); } #if 0 for(module_val = val_find_child(modules_state_val, "ietf-yang-library", "module"); module_val != NULL; module_val = val_find_next_child(modules_state_val, "ietf-yang-library", "module", module_val)) { val_dump_value(module_val,1); } #endif mscb->modules_state_val = val_clone(modules_state_val); return res; } /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION yang_library_start_get_module_set * * Start the MGR_SES_IO_CONN_YANG_LIBRARY state * * Seng for /modules-state yang library container. * * INPUTS: * server_cb == server session control block to use * scb == session control block to use * * OUTPUTS: * * RETURNS: * status *********************************************************************/ status_t yang_library_start_get_module_set (server_cb_t *server_cb, ses_cb_t *scb) { ncxmod_search_result_t *searchresult; status_t res; boolean done; #ifdef DEBUG if (!server_cb || !scb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif done = FALSE; res = NO_ERR; res = send_get_yang_library_modules_state_to_server(server_cb, scb); if (res == NO_ERR) { server_cb->command_mode = CMD_MODE_YANG_LIBRARY; } return res; } /* yang_library_start_get_module_set */ /******************************************************************** * FUNCTION yang_library_handle_rpc_reply * * Handle the current response * * INPUTS: * server_cb == server session control block to use * scb == session control block to use * reply == data node from the PDU * anyerrors == TRUE if detected instead * of * == FALSE if no elements detected * * OUTPUTS: * yang library module set retrieval process is completed * * RETURNS: * status *********************************************************************/ status_t yang_library_handle_rpc_reply (server_cb_t *server_cb, ses_cb_t *scb, val_value_t *reply, boolean anyerrors) { mgr_scb_t *mscb; ncxmod_search_result_t *searchresult; const xmlChar *module, *revision; status_t res; boolean done; res = get_yang_library_modules_state_reply_to_searchresult_entries(server_cb, scb, reply); return res; } /* yang_library_handle_rpc_reply */ /* END yangcli_yang_library.c */ yuma123_2.14/netconf/src/yangcli/yangcli_globals.c0000664000175000017500000001501214770023131022270 0ustar vladimirvladimir#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #define MEMORY_DEBUG 1 */ #ifdef MEMORY_DEBUG #include #endif #include "libtecla.h" #define _C_main 1 #include "procdefs.h" #include "cli.h" #include "conf.h" #include "help.h" #include "json_wr.h" #include "log.h" #include "ncxmod.h" #include "mgr.h" #include "mgr_hello.h" #include "mgr_io.h" #include "mgr_not.h" #include "mgr_rpc.h" #include "mgr_ses.h" #include "ncx.h" #include "ncx_list.h" #include "ncx_num.h" #include "ncx_str.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "op.h" #include "rpc.h" #include "runstack.h" #include "status.h" #include "val.h" #include "val_util.h" #include "var.h" #include "xml_util.h" #include "xml_wr.h" #include "yangconst.h" #include "yangcli.h" #include "yangcli_cmd.h" #include "yangcli_alias.h" #include "yangcli_autoload.h" #include "yangcli_autolock.h" #include "yangcli_save.h" #include "yangcli_tab.h" #include "yangcli_uservars.h" #include "yangcli_util.h" /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /***************** GLOBAL VARS ****************/ /* yangcli.yang module */ ncx_module_t *yangcli_mod; /* yangcli-ex.yang module */ ncx_module_t *yangcli_ex_mod; /* netconf.yang module */ ncx_module_t *netconf_mod; /* need to save CLI parameters: other vars are back-pointers */ val_value_t *mgr_cli_valset; /* Q of ncxmod_search_result_t structs representing all modules * and submodules found in the module library path at boot-time */ dlq_hdr_t modlibQ; ncxmod_temp_progcb_t *yangcli_progcb=NULL; #if 0 /* true if running a script from the invocation and exiting */ static boolean batchmode; /* true if printing program help and exiting */ static boolean helpmode; static help_mode_t helpsubmode; /* true if printing program version and exiting */ static boolean versionmode; /* name of script passed at invocation to auto-run */ static xmlChar *runscript; /* TRUE if runscript has been completed */ static boolean runscriptdone; /* command string passed at invocation to auto-run */ static xmlChar *runcommand; /* TRUE if runscript has been completed */ static boolean runcommanddone; /* controls automaic command line history buffer load/save */ static boolean autohistory; /* temporary file control block for the program instance */ static ncxmod_temp_progcb_t *temp_progcb; /* Q of alias_cb_t structs representing all command aliases */ static dlq_hdr_t aliasQ; /* flag to indicate init never completed OK; used during cleanup */ static boolean init_done; /***************** C O N F I G V A R S ****************/ /* TRUE if OK to load aliases automatically * FALSE if --autoaliases=false set by user * when yangcli starts, this var controls * whether the ~/.yuma/.yangcli_aliases file will be loaded * into this application automatically */ static boolean autoaliases; /* set if the --aliases-file parameter is present */ static const xmlChar *aliases_file; /* TRUE if OK to load modules automatically * FALSE if --autoload=false set by user * when server connection is made, and module discovery is done * then this var controls whether the matching modules * will be loaded into this application automatically */ static boolean autoload; /* TRUE if OK to check for partial command names and parameter * names by the user. First match (TBD: longest match!!) * will be used if no exact match found * FALSE if only exact match should be used */ static boolean autocomp; /* TRUE if OK to load user vars automatically * FALSE if --autouservars=false set by user * when yangcli starts, this var controls * whether the ~/.yuma/.yangcli_uservars file will be loaded * into this application automatically */ static boolean autouservars; /* set if the --uservars=filespec parameter is set */ static const xmlChar *uservars_file; /* NCX_BAD_DATA_IGNORE to silently accept invalid input values * NCX_BAD_DATA_WARN to warn and accept invalid input values * NCX_BAD_DATA_CHECK to prompt user to keep or re-enter value * NCX_BAD_DATA_ERROR to prompt user to re-enter value */ static ncx_bad_data_t baddata; #endif /* global connect param set, copied to server connect parmsets */ val_value_t *connect_valset; #if 0 /* name of external CLI config file used on invocation */ static xmlChar *confname; /* the module to check first when no prefix is given and there * is no parent node to check; * usually set to module 'netconf' */ static xmlChar *default_module; /* 0 for no timeout; N for N seconds message timeout */ static uint32 default_timeout; /* TRUE if OK to keep model copies stored in $USER/.yuma/tmp/<>/ until yangcli is terminated. * FALSE if --keep-session-model-copies-after-compilation=false */ static boolean keep_session_model_copies_after_compilation; /* default value for val_dump_value display mode */ static ncx_display_mode_t display_mode; /* FALSE to send PDUs in manager-specified order * TRUE to always send in correct canonical order */ static boolean fixorder; /* FALSE to skip optional nodes in do_fill * TRUE to check optional nodes in do_fill */ static boolean optional; /* default NETCONF test-option value */ static op_testop_t testoption; /* default NETCONF error-option value */ static op_errop_t erroption; /* default NETCONF default-operation value */ static op_defop_t defop; /* default NETCONF with-defaults value */ static ncx_withdefaults_t withdefaults; /* default indent amount */ static int32 defindent; /* default echo-replies */ static boolean echo_replies; /* default echo-requests */ static boolean echo_requests; /* default time-rpcs */ static boolean time_rpcs; /* default match-names */ static ncx_name_match_t match_names; /* default alt-names */ static boolean alt_names; /* default force-target */ static const xmlChar *force_target; /* default use-xmlheader */ static boolean use_xmlheader; #endif /* END yangcli_globals.c */ yuma123_2.14/netconf/src/yangcli/yangcli_list.h0000664000175000017500000000414214770023131021627 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangcli_list #define _H_yangcli_list /* FILE: yangcli_list.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13-augr-09 abb Begun */ #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION do_list (local RPC) * * list objects [module=mod-name] * ids * commands * * List the specified information based on the parameters * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_list (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli_list */ yuma123_2.14/netconf/src/yangcli/Makefile.am0000664000175000017500000000464014770023131021034 0ustar vladimirvladimirbin_PROGRAMS = yangcli noinst_HEADERS= \ $(top_srcdir)/netconf/src/yangcli/yangcli_alias.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_autoload.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_autolock.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_cmd.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_cond.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_eval.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_globals.h \ $(top_srcdir)/netconf/src/yangcli/yangcli.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_list.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_save.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_show.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_tab.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_timer.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_uservars.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_util.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_wordexp.h \ $(top_srcdir)/netconf/src/yangcli/yangcli_yang_library.h yangcli_SOURCES = \ $(top_srcdir)/netconf/src/yangcli/yangcli_autoload.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_yang_library.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_autolock.c \ $(top_srcdir)/netconf/src/yangcli/yangcli.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_cmd.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_cond.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_eval.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_list.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_save.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_show.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_tab.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_timer.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_util.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_alias.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_uservars.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_wordexp.c \ $(top_srcdir)/netconf/src/yangcli/yangcli_globals.c yangcli_CPPFLAGS = -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump $(XML_CPPFLAGS) yangcli_LDFLAGS = $(top_builddir)/netconf/src/mgr/libyumamgr.la $(top_builddir)/netconf/src/ncx/libyumancx.la if WITH_TECLA yangcli_CPPFLAGS += -I $(top_srcdir)/libtecla yangcli_LDFLAGS += $(top_builddir)/libtecla/libtecla.la else yangcli_SOURCES += $(top_srcdir)/netconf/src/yangcli/tecla2readline/tecla2readline.c yangcli_CPPFLAGS += -I $(top_srcdir)/libtecla yangcli_LDFLAGS += -lreadline endif yuma123_2.14/netconf/src/yangcli/tecla2readline/0000775000175000017500000000000014770023131021652 5ustar vladimirvladimiryuma123_2.14/netconf/src/yangcli/tecla2readline/tecla2readline.c0000664000175000017500000002306114770023131024676 0ustar vladimirvladimir/* * Copyright (c) 2013 - 2016, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: tecla2readline.c Thin wrapper implementing tecla API using readline */ #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" struct GetLine { int dummy; }; char* my_line = NULL; void process_line(char *line) { if( line == NULL ) { fprintf(stderr, "ENOTY LINE!\n"); } my_line = line; /* done here in order to prevent readline printing newline and prompt */ rl_callback_handler_remove(); } static GetLine my_gl; static tcflag_t my_old_lflag; static cc_t my_old_vtime; struct termios my_term; GetLine *new_GetLine(size_t linelen, size_t histlen) { /* Allow conditional parsing of the ~/.inputrc file. */ rl_readline_name = "yangcli"; #if 1 /* Adjust the terminal slightly before the handler is installed. Disable * canonical mode processing and set the input character time flag to be * non-blocking. */ if( tcgetattr(STDIN_FILENO, &my_term) < 0 ) { perror("tcgetattr"); exit(1); } my_old_lflag = my_term.c_lflag; my_old_vtime = my_term.c_cc[VTIME]; my_term.c_lflag &= ~ICANON; my_term.c_cc[VTIME] = 1; /* COMMENT LINE BELOW - see above */ if( tcsetattr(STDIN_FILENO, TCSANOW, &my_term) < 0 ) { perror("tcsetattr"); exit(1); } #endif //rl_add_defun("change-prompt", change_prompt, CTRL('t')); //return (GetLine *)malloc(sizeof(struct GetLine)); return &my_gl; } GetLine *del_GetLine(GetLine *gl) { /* reset the old terminal setting before exiting */ my_term.c_lflag = my_old_lflag; my_term.c_cc[VTIME] = my_old_vtime; if( tcsetattr(STDIN_FILENO, TCSANOW, &my_term) < 0 ) { perror("tcsetattr"); exit(1); } //free(gl); return gl; } static char* expand_path_with_home_prefix(const char* filename) { char* expanded; assert(filename!=NULL); if(strlen(filename)>0 && filename[0]=='~') { struct passwd *pw = getpwuid(getuid()); expanded = malloc(strlen(pw->pw_dir) + strlen(filename) + 1); sprintf(expanded,"%s%s",pw->pw_dir,&filename[1]); } else { expanded = strdup(filename); } return expanded; } int gl_save_history(GetLine *gl, const char *filename, const char *comment, int max_lines) { char* expanded_filename; expanded_filename = expand_path_with_home_prefix(filename); write_history(expanded_filename); free(expanded_filename); return 0; } int gl_load_history(GetLine *gl, const char *filename, const char *comment) { char* expanded_filename; if(comment != NULL) { assert(strlen(comment)==1); history_comment_char = comment[0]; } expanded_filename = expand_path_with_home_prefix(filename); read_history(expanded_filename); free(expanded_filename); return 0; } struct WordCompletion { int dummy; }; static WordCompletion my_word_completion; CplMatchFn * tecla_match_fn=NULL; void* tecla_match_fn_data=NULL; static char** my_completions; static unsigned int my_completion_counter=0; static unsigned int my_completions_max_len=0; static int my_cpl_word_start; /* value determined by the completion callback strips the prefix base e.g. path /interfaces/.../ or optional arg prefix "--" */ static void add_cpl_prefix(const char* text, int start) { char* str; int prefix_len; if(my_completions==NULL || my_completions[0]==NULL) { return; } prefix_len = my_cpl_word_start-start; str = malloc(prefix_len + strlen(my_completions[0])+1); memcpy(str,text,prefix_len); strcpy(str+prefix_len, my_completions[0]); free(my_completions[0]); my_completions[0]=str; } char** my_completion (const char *text, int start, int end) { my_completions = malloc(1024*sizeof(char*)); rl_attempted_completion_over = 1; rl_completion_query_items=64; tecla_match_fn(&my_word_completion, tecla_match_fn_data, rl_line_buffer, end); if(my_completion_counter==0) { free(my_completions); return NULL; } else { //rl_display_match_list (my_completions, my_completion_counter, my_completions_max_len); add_cpl_prefix(text, start); my_completions[my_completion_counter]=NULL; my_completion_counter=0; my_completions_max_len=0; return my_completions; } } int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn) { assert(tecla_match_fn==NULL); assert(tecla_match_fn_data==NULL); tecla_match_fn = match_fn; tecla_match_fn_data = data; rl_attempted_completion_function = my_completion; return 0; } int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *line) { line = NULL; return 0; } int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, int max_lines) { int i; HIST_ENTRY *h; if(max_lines=-1) { max_lines=history_length; } for(i=0;iline); } else { break; } } return 0; } int gl_echo_mode(GetLine *gl, int enable) { return 0; } unsigned int inactivity_sec=0; unsigned int inactivity_nsec=0; GlTimeoutFn* my_timeout_fn; void* my_timeout_fn_data; int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *timeout_fn, void *data, unsigned long sec, unsigned long nsec) { inactivity_sec = sec; inactivity_nsec = nsec; my_timeout_fn = timeout_fn; my_timeout_fn_data = data; return 0; } int gl_normal_io(GetLine *gl) { return 0; } GlReturnStatus return_status; char *gl_get_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos) { struct timeval tv; char* line = NULL; #if 0 /*blocking mode*/ line=readline(prompt); return_status=GLR_NEWLINE; #else { fd_set fds; if(my_line != NULL) { free(my_line); my_line=NULL; } tv.tv_sec=inactivity_sec; tv.tv_usec=inactivity_nsec/1000; rl_callback_handler_install(prompt, process_line); while(1) { FD_ZERO(&fds); FD_SET(fileno(stdin), &fds); if( select(FD_SETSIZE, &fds, NULL, NULL, &tv) < 0) { perror("Terminating"); del_GetLine(gl); exit(0); } if( FD_ISSET(fileno(stdin), &fds) ) { rl_callback_read_char(); } else { GlAfterTimeout after_timeout = my_timeout_fn(gl, my_timeout_fn_data); if(after_timeout == GLTO_ABORT) { break; } else if(after_timeout == GLTO_CONTINUE) { tv.tv_sec=inactivity_sec; tv.tv_usec=inactivity_nsec/1000; continue; } else if (after_timeout == GLTO_REFRESH) { rl_callback_handler_install(prompt, process_line); continue; } else { assert(0); } } if(my_line!=NULL) { line = my_line; break; } } } #endif if(line!=NULL) { return_status=GLR_NEWLINE; add_history(line); } else { return_status=GLR_TIMEOUT; } return line; } GlReturnStatus gl_return_status(GetLine *gl) { return return_status; } void gl_clear_history(GetLine *gl, int all_groups) { clear_history(); } void gl_range_of_history(GetLine *gl, GlHistoryRange *range) { range->oldest = history_base; range->newest = history_base + history_length; range->nlines = history_length; } int cpl_add_completion(WordCompletion *cpl, const char *line, int word_start, int word_end, const char *suffix, const char *type_suffix, const char *cont_suffix) { char* str; unsigned int len; int suffix_len; suffix_len = strlen(suffix); len = word_end-word_start + suffix_len; if(len>my_completions_max_len) { my_completions_max_len = len; } /* we need that in order to add the prefix e.g. -- or /interfaces/.../ */ my_cpl_word_start = word_start; str=(char*)malloc(len+1); memcpy(str+word_end-word_start,suffix,suffix_len); memcpy(str,line+word_start,word_end-word_start); str[suffix_len + word_end-word_start] = 0; if(my_completion_counter==0) { my_completions[0]=strdup(str); my_completion_counter++; } else { int i; /* find the max common completion */ for(i=0;i// filled with * the the specified YANG files that are already available * on this system. * * These search records will be removed from the * server_cb->searchresultQ and modptr records * added to the server_cb->modptrQ * * RETURNS: * status *********************************************************************/ extern status_t autoload_setup_tempdir (server_cb_t *server_cb, ses_cb_t *scb); /******************************************************************** * FUNCTION autoload_start_get_modules * * Start the MGR_SES_IO_CONN_AUTOLOAD state * * Go through all the search result records and * make sure all files are present. Try to use the * operation to fill in any missing modules * * INPUTS: * server_cb == server session control block to use * scb == session control block to use * * OUTPUTS: * $HOME/.yuma/tmp/// filled with * the the specified YANG files that are ertrieved from * the device with * * RETURNS: * status *********************************************************************/ extern status_t autoload_start_get_modules (server_cb_t *server_cb, ses_cb_t *scb); /******************************************************************** * FUNCTION autoload_handle_rpc_reply * * Handle the current response * * INPUTS: * server_cb == server session control block to use * scb == session control block to use * reply == data node from the PDU * anyerrors == TRUE if detected instead * of * == FALSE if no elements detected * * OUTPUTS: * $HOME/.yuma/tmp/// filled with * the the specified YANG files that was retrieved from * the device with * * Next request is started, or autoload process is completed * and the command_mode is changed back to CMD_MODE_NORMAL * * RETURNS: * status *********************************************************************/ extern status_t autoload_handle_rpc_reply (server_cb_t *server_cb, ses_cb_t *scb, val_value_t *reply, boolean anyerrors); /******************************************************************** * FUNCTION autoload_compile_modules * * Go through all the search result records and parse * the modules that the device advertised. * DOES NOT LOAD THESE MODULES INTO THE MODULE DIRECTORY * THE RETURNED ncx_module_t STRUCT IS JUST FOR ONE SESSION USE * * Apply the deviations and features specified in * the search result cap back-ptr, to the module * * INPUTS: * server_cb == server session control block to use * scb == session control block to use * * OUTPUTS: * $HOME/.yuma/tmp/// filled with * the the specified YANG files that are already available * on this system. * * These search records will be removed from the * server_cb->searchresultQ and modptr records * added to the server_cb->modptrQ * * RETURNS: * status *********************************************************************/ extern status_t autoload_compile_modules (server_cb_t *server_cb, ses_cb_t *scb); /******************************************************************** * FUNCTION make_get_schema_reqdata * * Allocate and initialize reqdata value for * * format will be hard-wired to yang * * INPUTS: * server_cb == server control block to use * scb == session control block to use * module == module to get * revision == revision to get * * OUTPUTS: * out_rpc == obj_template_t** of the get-schema RPC * out_reqdata == val_value_t** of the get-schema data value * * RETURNS: * status *********************************************************************/ status_t make_get_schema_reqdata(server_cb_t *server_cb, ses_cb_t *scb, const xmlChar *module, const xmlChar *revision, obj_template_t** out_rpc, val_value_t** out_reqdata); status_t get_schema_reply_to_temp_filcb(server_cb_t * server_cb, mgr_scb_t *mscb, const xmlChar* module, const xmlChar* revision, val_value_t* reply); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli_autoload */ yuma123_2.14/netconf/src/yangcli/yangcli_alias.c0000664000175000017500000007064214770023131021750 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_alias.c NETCONF YANG-based CLI Tool alias command ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30-sep-11 abb begun; ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include "procdefs.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "status.h" #include "xml_util.h" #include "yangcli.h" #include "yangcli_alias.h" #include "yangcli_cmd.h" #include "yangcli_util.h" /******************************************************************** * FUNCTION get_first_alias * * Get the first alias record * * RETURNS: * pointer to alias record or NULL if none *********************************************************************/ static alias_cb_t * get_first_alias (void) { dlq_hdr_t *aliasQ = get_aliasQ(); if (aliasQ) { return (alias_cb_t *)dlq_firstEntry(aliasQ); } return NULL; } /* get_first_alias */ /******************************************************************** * FUNCTION get_next_alias * * Get the next alias record * * RETURNS: * pointer to alias record or NULL if none *********************************************************************/ static alias_cb_t * get_next_alias (alias_cb_t *curalias) { if (curalias) { return (alias_cb_t *)dlq_nextEntry(curalias); } return NULL; } /* get_next_alias */ /******************************************************************** * FUNCTION free_alias * * Free the alias record * * INPUTS: * alias == pointer to alias record to free *********************************************************************/ static void free_alias (alias_cb_t *alias) { if (alias == NULL) { return; } if (alias->name != NULL) { m__free(alias->name); } if (alias->value != NULL) { m__free(alias->value); } m__free(alias); } /* free_alias */ /******************************************************************** * FUNCTION new_alias * * Malloc and fill in an alias record * * INPUTS: * name == alias name (not z-terminated) * namelen == length of name string * * RETURNS: * pointer to alias record or NULL if none *********************************************************************/ static alias_cb_t * new_alias (const xmlChar *name, uint32 namelen) { alias_cb_t *alias; if (namelen == 0) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } alias = m__getObj(alias_cb_t); if (alias == NULL) { return NULL; } memset(alias, 0x0, sizeof(alias_cb_t)); alias->name = xml_strndup(name, namelen); if (alias->name == NULL) { free_alias(alias); return NULL; } return alias; } /* new_alias */ /******************************************************************** * FUNCTION add_alias * * Add the alias record * * INPUTS: * alias == alias to add * * RETURNS: * status *********************************************************************/ static status_t add_alias (alias_cb_t *alias) { dlq_hdr_t *aliasQ = get_aliasQ(); alias_cb_t *curalias; int ret; if (aliasQ == NULL) { SET_ERROR(ERR_INTERNAL_VAL); free_alias(alias); return ERR_INTERNAL_VAL; } for (curalias = (alias_cb_t *)dlq_firstEntry(aliasQ); curalias != NULL; curalias = (alias_cb_t *)dlq_nextEntry(curalias)) { ret = xml_strcmp(curalias->name, alias->name); if (ret == 0) { SET_ERROR(ERR_NCX_DUP_ENTRY); free_alias(alias); return ERR_NCX_DUP_ENTRY; } else if (ret > 0) { dlq_insertAhead(alias, curalias); return NO_ERR; } } /* new last entry */ dlq_enque(alias, aliasQ); return NO_ERR; } /* add_alias */ /******************************************************************** * FUNCTION find_alias * * Find the alias record * * INPUTS: * name == alias name to find (not assumed to be z-terminated * namelen == length of name string * RETURNS: * alias record; NULL if not found *********************************************************************/ static alias_cb_t * find_alias (const xmlChar *name, uint32 namelen) { dlq_hdr_t *aliasQ = get_aliasQ(); alias_cb_t *curalias; if (aliasQ == NULL) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } for (curalias = (alias_cb_t *)dlq_firstEntry(aliasQ); curalias != NULL; curalias = (alias_cb_t *)dlq_nextEntry(curalias)) { uint32 curlen = xml_strlen(curalias->name); int ret = xml_strncmp(curalias->name, name, namelen); if (ret == 0) { if (curlen == namelen) { return curalias; } } else if (ret > 0) { /* already past where this name should be sorted */ return NULL; } } return NULL; } /* find_alias */ /******************************************************************** * FUNCTION show_alias_ptr * * Output 1 alias record * * INPUTS: * alias == alias record to show *********************************************************************/ static void show_alias_ptr (const alias_cb_t *alias) { const xmlChar *qchar = NULL; switch (alias->quotes) { case 0: qchar = EMPTY_STRING; break; case 1: qchar = (const xmlChar *)"'"; break; case 2: qchar = (const xmlChar *)"\""; break; default: SET_ERROR(ERR_INTERNAL_VAL); return; } log_write("\n%s=%s%s%s", alias->name, qchar, (alias->value) ? alias->value : EMPTY_STRING, qchar); } /* show_alias_ptr */ /******************************************************************** * FUNCTION write_alias * * Output 1 alias record to a file * * INPUTS: * fp == FILE pointer to use for writing at current pos * alias == alias record to write *********************************************************************/ static void write_alias (FILE *fp, const alias_cb_t *alias) { const xmlChar *qchar = NULL; switch (alias->quotes) { case 0: qchar = EMPTY_STRING; break; case 1: qchar = (const xmlChar *)"'"; break; case 2: qchar = (const xmlChar *)"\""; break; default: SET_ERROR(ERR_INTERNAL_VAL); return; } fprintf(fp, "%s=%s%s%s\n", alias->name, qchar, (alias->value) ? alias->value : EMPTY_STRING, qchar); } /* write_alias */ /******************************************************************** * FUNCTION show_alias_name * * Output 1 alias by name * * INPUTS: * name == name of alias to show (not z-terminated) * namelen == length of name *********************************************************************/ static void show_alias_name (const xmlChar *name, uint32 namelen) { alias_cb_t *alias = find_alias(name, namelen); if (alias) { show_alias_ptr(alias); log_write("\n"); } else { uint32 i; log_error("\nError: alias '"); for (i = 0; i < namelen; i++) { log_error("%c", name[i]); } log_error("' not found\n"); } } /* show_alias_name */ /******************************************************************** * FUNCTION parse_alias * * Parse a string into its components * * INPUTS: * parmstr == parameter string to parse * namelen == address of return name length * valstr == address of return value string pointer * * OUTPUTS: * *namelen == return name length * *valstr == return value string pointer (NULL if no value) * * RETURNS: * status *********************************************************************/ static status_t parse_alias (const xmlChar *parmstr, uint32 *namelen, const xmlChar **valstr) { const xmlChar *p; boolean done; *namelen = 0; *valstr = NULL; /* check string for OK chars */ p = parmstr; while (*p) { if (*p == '\n' || *p == '\t') { ; // just skip this char; TBD: config param for this } else if (!isprint((int)*p)) { return ERR_NCX_INVALID_VALUE; } p++; } /* look for end of name */ p = parmstr; if (!ncx_valid_fname_ch(*p++)) { return ERR_NCX_INVALID_NAME; } done = FALSE; while (*p && !done) { if (*p == '=') { done = TRUE; } else if (!ncx_valid_name_ch(*p)) { return ERR_NCX_INVALID_NAME; } else { p++; } } /* set output parms */ *namelen = (uint32)(p - parmstr); if (done) { /* stopped at equals sign; check next char OK */ if (p[1] == 0 || xml_isspace(p[1])) { return ERR_NCX_INVALID_VALUE; } *valstr = &p[1]; } /* else stopped at end of valid name string */ return NO_ERR; } /* parse_alias */ /******************************************************************** * FUNCTION set_alias * * Set an alias value field * * INPUTS: * alias == alias record to use * valstr == value string to use * * RETURNS: * status *********************************************************************/ static status_t set_alias (alias_cb_t *alias, const xmlChar *valstr) { status_t res = NO_ERR; if (alias->value) { m__free(alias->value); alias->value = NULL; } alias->quotes = 0; if (valstr) { if (*valstr == '"' || *valstr == '\'') { /* preseve quotes used */ alias->quotes = (*valstr == '"') ? 2 : 1; /* check trim quoted string */ uint32 len = xml_strlen(valstr); if (len > 2) { const xmlChar *endstr = &valstr[len-1]; if (*endstr == *valstr) { /* remove paired quotes */ alias->value = xml_strndup(valstr+1, len-2); if (alias->value == NULL) { res = ERR_INTERNAL_MEM; } } else { /* improper string; unmatched quotes */ res = ERR_NCX_INVALID_VALUE; } } else { /* else got just a quote char */ res = ERR_NCX_INVALID_VALUE; } } else { alias->value = xml_strdup(valstr); if (alias->value == NULL) { res = ERR_INTERNAL_MEM; } } } /* else cleared value if not already */ return res; } /* set_alias */ /******************************************************************** * FUNCTION handle_alias_parm * * alias def * alias def=def-value * * Handle the alias command, based on the parameter * * INPUTS: * varstr == alias command line * setonly == TRUE if expecting set version only; ignore show alias * FALSE if expecting show alias or set alias * loginfo == TRUE if log-level=info should be used * FALSE if log-level=debug2 should be used * RETURNS: * status *********************************************************************/ static status_t handle_alias_parm (const xmlChar *varstr, boolean setonly, boolean loginfo) { const xmlChar *valptr = NULL; uint32 nlen = 0; status_t res; res = parse_alias(varstr, &nlen, &valptr); if (res == NO_ERR) { if (valptr) { /* setting an alias */ alias_cb_t *alias = find_alias(varstr, nlen); if (alias) { if (LOGDEBUG3) { log_debug3("\nAbout to replace alias '%s'" "\n old value: '%s'" "\n new value: '%s'", alias->name, alias->value ? alias->value : EMPTY_STRING, valptr); } /* modify an existing alias */ res = set_alias(alias, valptr); if (res == NO_ERR) { if (loginfo) { log_info("\nUpdated alias '%s'\n", alias->name); } else { log_debug2("\nUpdated alias '%s'", alias->name); } } else { log_error("\nError: invalid alias value '%s'\n", valptr); } } else { /* create a new alias */ alias = new_alias(varstr, nlen); if (alias == NULL) { res = ERR_INTERNAL_MEM; } else { res = set_alias(alias, valptr); if (res == NO_ERR) { res = add_alias(alias); if (res == NO_ERR) { if (loginfo) { log_info("\nAdded alias '%s'\n", alias->name); } else { log_debug2("\nAdded alias '%s'", alias->name); } } else { log_error("\nError: alias was not added '%s'\n", get_error_string(res)); } } else { log_error("\nError: invalid alias value '%s'\n", valptr); free_alias(alias); } } } } else if (!setonly) { /* just provided a name; show alias */ show_alias_name(varstr, nlen); } else if (LOGDEBUG) { log_debug("\nSkipping alias '%s' because no value set", varstr); } } else if (res == ERR_NCX_INVALID_NAME) { log_error("\nError: invalid alias (%s)", get_error_string(res)); } else { log_error("\nError: invalid alias '%s' (%s)", varstr, get_error_string(res)); } return res; } /* handle_alias_parm */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION do_alias (local RPC) * * alias * alias def * alias def=def-value * * Handle the alias command, based on the parameter * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the alias command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_alias (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *parm; status_t res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (res == NO_ERR && valset) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_VAR); if (parm) { const xmlChar *varstr = VAL_STR(parm); res = handle_alias_parm(varstr, FALSE, TRUE); } else { /* no parameter; show all aliases */ show_aliases(); } } if (valset) { val_free_value(valset); } return res; } /* do_alias */ /******************************************************************** * FUNCTION do_aliases (local RPC) * * aliases * aliases clear * aliases show * aliases load[=filespec] * aliases save[=filespec] * * Handle the aliases command, based on the parameter * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the aliases command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_aliases (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset; status_t res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (res == NO_ERR && valset) { /* get the 1 of N 'alias-action' choice */ val_value_t *parm; const xmlChar *parmval = NULL; boolean done = FALSE; /* aliases show */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_SHOW); if (parm) { show_aliases(); done = TRUE; } /* aliases clear */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_CLEAR); if (parm) { dlq_hdr_t *aliasQ = get_aliasQ(); if (!dlq_empty(aliasQ)) { free_aliases(); log_info("\nDeleted all aliases from memory\n"); }else { log_info("\nNo aliases found\n"); } done = TRUE; } } /* aliases load */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_LOAD); if (parm) { if (xml_strlen(VAL_STR(parm))) { parmval = VAL_STR(parm); } else { parmval = get_aliases_file(); } res = load_aliases(parmval); if (res == NO_ERR) { log_info("\nLoaded aliases OK from '%s'\n", parmval); } else { log_error("\nLoad aliases from '%s' failed (%s)\n", parmval, get_error_string(res)); } done = TRUE; } } /* aliases save */ if (!done) { parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_SAVE); if (parm) { if (xml_strlen(VAL_STR(parm))) { parmval = VAL_STR(parm); } else { parmval = get_aliases_file(); } res = save_aliases(parmval); if (res == NO_ERR) { log_info("\nSaved aliases OK to '%s'\n", parmval); } else { log_error("\nSave aliases to '%s' failed (%s)\n", parmval, get_error_string(res)); } done = TRUE; } } if (!done) { /* no parameters; show all aliases */ show_aliases(); } } if (valset) { val_free_value(valset); } return res; } /* do_aliases */ /******************************************************************** * FUNCTION do_unset (local RPC) * * unset def * * Handle the unset command; remove the specified alias * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the unset command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_unset (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *parm; status_t res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (res == NO_ERR && valset) { parm = val_find_child(valset, YANGCLI_MOD, NCX_EL_NAME); if (parm) { const xmlChar *varstr = VAL_STR(parm); alias_cb_t *alias = find_alias(varstr, xml_strlen(varstr)); if (alias) { dlq_remove(alias); free_alias(alias); log_info("\nDeleted alias '%s'\n", varstr); } else { res = ERR_NCX_INVALID_VALUE; log_error("\nError: unknown alias '%s'\n", varstr); } } /* else missing parameter already reported */ } /* else no valset already reported */ if (valset) { val_free_value(valset); } return res; } /* do_unset */ /******************************************************************** * FUNCTION show_aliases * * Output all the alias values * *********************************************************************/ void show_aliases (void) { alias_cb_t *alias; boolean anyout = FALSE; for (alias = get_first_alias(); alias != NULL; alias = get_next_alias(alias)) { show_alias_ptr(alias); anyout = TRUE; } if (anyout) { log_write("\n"); } } /* show_aliases */ /******************************************************************** * FUNCTION show_alias * * Output 1 alias by name * * INPUTS: * name == name of alias to show *********************************************************************/ void show_alias (const xmlChar *name) { show_alias_name(name, xml_strlen(name)); } /* show_alias */ /******************************************************************** * FUNCTION free_aliases * * Free all the command aliases * *********************************************************************/ void free_aliases (void) { dlq_hdr_t *aliasQ = get_aliasQ(); alias_cb_t *alias; while (!dlq_empty(aliasQ)) { alias = (alias_cb_t *)dlq_deque(aliasQ); free_alias(alias); } } /* free_aliases */ /******************************************************************** * FUNCTION load_aliases * * Load the aliases from the specified filespec * * INPUT: * fspec == input filespec to use (NULL == default) * * RETURNS: * status *********************************************************************/ status_t load_aliases (const xmlChar *fspec) { FILE *fp; xmlChar *fullspec, *buffer; status_t res = NO_ERR; buffer = m__getMem(NCX_MAX_LINELEN+1); if (buffer == NULL) { log_error("\nError: malloc failed\n"); return ERR_INTERNAL_MEM; } if (fspec == NULL) { fspec = get_aliases_file(); } fullspec = ncx_get_source(fspec, &res); if (res == NO_ERR && fullspec) { fp = fopen((const char *)fullspec, "r"); if (fp) { #define MAX_ALIAS_ERRORS 5 boolean done = FALSE; uint32 errorcnt = 0; while (!done) { char *retstr = fgets((char *)buffer, NCX_MAX_LINELEN+1, fp); if (retstr) { uint32 len = xml_strlen(buffer); if (len) { if (*buffer != '#' && *buffer != '\n') { if (buffer[len-1] == '\n') { buffer[len-1] = 0; } res = handle_alias_parm(buffer, TRUE, FALSE); if (res != NO_ERR) { if (++errorcnt == MAX_ALIAS_ERRORS) { log_error("\nError: skipping aliases; " "too many errors\n"); done = TRUE; } } } // else skip a newline or comment line } // else skip empty line } else { done = TRUE; } } fclose(fp); } else if (LOGDEBUG) { log_debug("\nAliases file '%s' could not be opened\n", fullspec); } } else { log_error("\nError: Expand source '%s' failed (%s)", fspec, get_error_string(res)); } if (fullspec) { m__free(fullspec); } if (buffer) { m__free(buffer); } return res; } /* load_aliases */ /******************************************************************** * FUNCTION save_aliases * * Save the aliases to the specified filespec * * INPUT: * fspec == output filespec to use (NULL == default) * * RETURNS: * status *********************************************************************/ status_t save_aliases (const xmlChar *fspec) { xmlChar *fullspec; FILE *fp = NULL; status_t res = NO_ERR; if (fspec == NULL) { fspec = get_aliases_file(); } fullspec = ncx_get_source(fspec, &res); if (res == NO_ERR && fullspec) { fp = fopen((const char *)fullspec, "w"); if (fp) { /* this will truncate the file if there are no aliases */ alias_cb_t *alias; for (alias = get_first_alias(); alias != NULL; alias = get_next_alias(alias)) { write_alias(fp, alias); } fclose(fp); } else { res = errno_to_status(); log_error("\nError: Open aliases file '%s' failed (%s)\n", fullspec, get_error_string(res)); } } else { log_error("\nError: Expand source '%s' failed (%s)\n", fspec, get_error_string(res)); } if (fullspec) { m__free(fullspec); } return res; } /* save_aliases */ /******************************************************************** * FUNCTION expand_alias * * Check if the first token is an alias name * If so, construct a new command line with the alias contents * * INPUT: * line == command line to check and possibly convert * res == address of return status * * OUTPUTS: * *res == return status (ERR_NCX_SKIPPED if nothing done) * * RETURNS: * pointer to malloced command string if *res==NO_ERR * NULL if *res==ERR_NCX_SKIPPED or some real error *********************************************************************/ xmlChar * expand_alias (xmlChar *line, status_t *res) { xmlChar *start, *p = line, *newline; alias_cb_t *alias; uint32 namelen, newlen; boolean done = FALSE; /* skip any leading whitespace; not expected from yangcli */ while (*p && xml_isspace(*p)) { p++; } if (*p == 0) { *res = ERR_NCX_SKIPPED; return NULL; } /* look for end of name */ start = p; if (!ncx_valid_fname_ch(*p++)) { *res = ERR_NCX_SKIPPED; return NULL; } while (*p && !done) { if (xml_isspace(*p)) { done = TRUE; } else if (!ncx_valid_name_ch(*p)) { *res = ERR_NCX_SKIPPED; return NULL; } else { p++; } } /* look for the alias */ namelen = (uint32)(p - start); alias = find_alias(start, namelen); if (alias == NULL) { *res = ERR_NCX_SKIPPED; return NULL; } /* replace the alias name with its contents and make a new string */ if (alias->value) { newlen = xml_strlen(p) + xml_strlen(alias->value); } else { newlen = xml_strlen(p); } newline = m__getMem(newlen + 1); if (newline == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } start = newline; if (alias->value) { start += xml_strcpy(start, alias->value); } xml_strcpy(start, p); if (LOGDEBUG2) { log_debug2("\nExpanded alias '%s'; new line: '%s'", alias->name, newline); } *res = NO_ERR; return newline; } /* expand_alias */ /* END yangcli_alias.c */ yuma123_2.14/netconf/src/yangcli/yangcli_tab.c0000664000175000017500000020402614770023131021420 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2013 - 2016, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_tab.c NETCONF YANG-based CLI Tool See ./README for details ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 18-apr-09 abb begun; split out from yangcli.c 25-jan-12 abb Add xpath tab completion provided by Zesi Cai ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" #include "yangcli_wordexp.h" #include "procdefs.h" #include "log.h" #include "ncx.h" #include "ncxtypes.h" #include "obj.h" #include "rpc.h" #include "status.h" #include "val.h" #include "val123.h" #include "val_util.h" #include "var.h" #include "xml_util.h" #include "yangcli.h" #include "yangcli_cmd.h" #include "yangcli_tab.h" #include "yangcli_util.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* make sure this is not enabled in the checked in version * will mess up STDOUT diplsay with debug messages!!! */ /* #define YANGCLI_TAB_DEBUG 1 */ /* #define DEBUG_TRACE 1 */ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F O R W A R D D E C L A R A T I O N S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION cpl_add_completion * * COPIED FROM /usr/local/include/libtecla.h * * Used by libtecla to store one completion possibility * for the current command line in progress * * cpl WordCompletion * The argument of the same name that was passed * to the calling CPL_MATCH_FN() callback function. * line const char * The input line, as received by the callback * function. * word_start int The index within line[] of the start of the * word that is being completed. If an empty * string is being completed, set this to be * the same as word_end. * word_end int The index within line[] of the character which * follows the incomplete word, as received by the * callback function. * suffix const char * The appropriately quoted string that could * be appended to the incomplete token to complete * it. A copy of this string will be allocated * internally. * type_suffix const char * When listing multiple completions, gl_get_line() * appends this string to the completion to indicate * its type to the user. If not pertinent pass "". * Otherwise pass a literal or static string. * cont_suffix const char * If this turns out to be the only completion, * gl_get_line() will append this string as * a continuation. For example, the builtin * file-completion callback registers a directory * separator here for directory matches, and a * space otherwise. If the match were a function * name you might want to append an open * parenthesis, etc.. If not relevant pass "". * Otherwise pass a literal or static string. * Output: * return int 0 - OK. * 1 - Error. */ /******************************************************************** * FUNCTION fill_one_parm_completion * * fill the command struct for one RPC parameter value * check all the parameter values that match, if possible * * command state is CMD_STATE_FULL or CMD_STATE_GETVAL * * INPUTS: * cpl == word completion struct to fill in * comstate == completion state to use * line == line passed to callback * parmval == parm value string to check * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * parmlen == length of parameter value already entered * * OUTPUTS: * cpl filled in if any matching commands found * * RETURNS: * status *********************************************************************/ static status_t fill_one_parm_completion (WordCompletion *cpl, completion_state_t *comstate, const char *line, const char *parmval, int word_start, int word_end, int parmlen) { const char *endchar; int retval; /* check no partial match so need to skip this one */ if (parmlen > 0 && strncmp(parmval, &line[word_start], parmlen)) { /* parameter value start is not the same so skip it */ return NO_ERR; } if (comstate->cmdstate == CMD_STATE_FULL) { endchar = " "; } else { endchar = ""; } retval = cpl_add_completion(cpl, line, word_start, word_end, (const char *)&parmval[parmlen], (const char *)"", endchar); if (retval != 0) { return ERR_NCX_OPERATION_FAILED; } return NO_ERR; } /* fill_one_parm_completion */ /******************************************************************** * FUNCTION fill_parm_completion_ex * * fill the command struct for one RPC parameter value * check all the parameter values that match, if possible * * command state is CMD_STATE_FULL or CMD_STATE_GETVAL * * INPUTS: * parmobj == RPC input parameter template to use * cpl == word completion struct to fill in * comstate == completion state record to use * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * parmlen == length of parameter name already entered * this may not be the same as * word_end - word_start if the cursor was * moved within a long line * typdef == the typdef of paramobj * basetypdef == the basetypdef of typdef * btyp == the btyp of paramobj * * OUTPUTS: * cpl filled in if any matching commands found * * RETURNS: * status *********************************************************************/ static status_t fill_parm_completion_ex (obj_template_t *parmobj, WordCompletion *cpl, completion_state_t *comstate, const char *line, int word_start, int word_end, int parmlen, typ_def_t *typdef, typ_def_t *basetypdef, ncx_btype_t btyp ) { const char *defaultstr; typ_enum_t *typenum; status_t res; typ_unionnode_t *un = NULL; res = NO_ERR; switch (btyp) { case NCX_BT_IDREF: { const typ_idref_t *idref; ncx_module_t *mod; ncx_identity_t* identity; idref = typ_get_cidref(typdef); for (mod = ncx_get_first_session_module(); mod != NULL; mod = ncx_get_next_session_module(mod)) { for(identity=(ncx_identity_t*)dlq_firstEntry(&mod->identityQ); identity!=NULL; identity=(ncx_identity_t*)dlq_nextEntry(identity)) { if(ncx123_identity_is_derived_from(identity, idref->base)) { res = fill_one_parm_completion(cpl, comstate, line, (const char *)identity->name, word_start, word_end, parmlen); } } } } return res; case NCX_BT_ENUM: case NCX_BT_BITS: for (typenum = typ_first_enumdef(basetypdef); typenum != NULL && res == NO_ERR; typenum = typ_next_enumdef(typenum)) { #ifdef YANGCLI_TAB_DEBUG log_debug2("\n*** found enubit %s ***\n", typenum->name); #endif res = fill_one_parm_completion(cpl, comstate, line, (const char *)typenum->name, word_start, word_end, parmlen); } return res; case NCX_BT_BOOLEAN: res = fill_one_parm_completion(cpl, comstate, line, (const char *)NCX_EL_TRUE, word_start, word_end, parmlen); if (res == NO_ERR) { res = fill_one_parm_completion(cpl, comstate, line, (const char *)NCX_EL_FALSE, word_start, word_end, parmlen); } return res; case NCX_BT_INSTANCE_ID: break; case NCX_BT_UNION: for (un = (typ_unionnode_t *)dlq_firstEntry(&typdef->def.simple.unionQ); un != NULL && res == NO_ERR; un = (typ_unionnode_t *)dlq_nextEntry(un)) { /* recursive call for each member of the union */ res = fill_parm_completion_ex (parmobj,cpl,comstate,line,word_start,word_end,parmlen, un->typdef, typ_get_base_typdef(un->typdef), typ_get_basetype(un->typdef)); } break; default: break; } /* no current values to show; * just use the default if any */ defaultstr = (const char *)obj_get_default(parmobj); if (defaultstr) { res = fill_one_parm_completion(cpl, comstate, line, defaultstr, word_start, word_end, parmlen); } return NO_ERR; } /* fill_parm_completion_ex */ /******************************************************************** * FUNCTION fill_parm_completion * * fill the command struct for one RPC parameter value * check all the parameter values that match, if possible * * command state is CMD_STATE_FULL or CMD_STATE_GETVAL * * INPUTS: * parmobj == RPC input parameter template to use * cpl == word completion struct to fill in * comstate == completion state record to use * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * parmlen == length of parameter name already entered * this may not be the same as * word_end - word_start if the cursor was * moved within a long line * * OUTPUTS: * cpl filled in if any matching commands found * * RETURNS: * status *********************************************************************/ static status_t fill_parm_completion (obj_template_t *parmobj, WordCompletion *cpl, completion_state_t *comstate, const char *line, int word_start, int word_end, int parmlen) { typ_def_t *typdef, *basetypdef; ncx_btype_t btyp; #ifdef YANGCLI_TAB_DEBUG log_debug2("\n*** fill parm %s ***\n", obj_get_name(parmobj)); #endif typdef = obj_get_typdef(parmobj); if (typdef == NULL) { return NO_ERR; } basetypdef = typ_get_base_typdef(typdef); btyp = obj_get_basetype(parmobj); return fill_parm_completion_ex (parmobj, cpl, comstate, line, word_start, word_end, parmlen, typdef, basetypdef, btyp); } static status_t fill_xpath_predicate_completion (obj_template_t *rpc, obj_template_t *parentObj, WordCompletion *cpl, const char *line, int word_start, int word_end, int cmdlen); /******************************************************************** * FUNCTION xpath_path_completion_common * * check and if obj is valid completion and register it with cpl_add_completion * * INPUTS: * obj == object template to check * cpl == word completion struct to fill in * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * cmdlen == command length * * OUTPUTS: * cpl filled in if any matching commands found * * RETURNS: * status *********************************************************************/ static int xpath_path_completion_common(obj_template_t *rpc, obj_template_t * obj, WordCompletion *cpl, const char *line, int word_start, int word_end, int cmdlen) { int retval = 0; xmlChar *pathname; do { /* check if there is a partial command name */ if (cmdlen > 0 && strncmp((const char *)obj_get_name(obj), &line[word_start], cmdlen)) { /* command start is not the same so skip it */ continue; } if( !obj_is_data_db(obj)) { /* object is either rpc or notification*/ continue; } if(!obj_get_config_flag(obj)) { const xmlChar* rpc_name; rpc_name = obj_get_name(rpc); if(0==strcmp((const char*)rpc_name, "create")) continue; if(0==strcmp((const char*)rpc_name, "replace")) continue; if(0==strcmp((const char*)rpc_name, "delete")) continue; } if(obj->objtype==OBJ_TYP_CONTAINER || obj->objtype==OBJ_TYP_LIST) { pathname = malloc(strlen(obj_get_name(obj))+2); assert(pathname); strcpy(pathname, obj_get_name(obj)); pathname[strlen(obj_get_name(obj))] ='/'; pathname[strlen(obj_get_name(obj))+1] =0; } else { pathname = malloc(strlen(obj_get_name(obj))+1); assert(pathname); strcpy(pathname, obj_get_name(obj)); pathname[strlen(obj_get_name(obj))] =0; } retval = cpl_add_completion(cpl, line, word_start, word_end, (const char *)&pathname[cmdlen], "", ""); free(pathname); } while(0); return retval; } /******************************************************************** * FUNCTION fill_xpath_children_completion * * fill the command struct for one XPath child node * * INPUTS: * parentObj == object template of parent to check * cpl == word completion struct to fill in * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * cmdlen == command length * * OUTPUTS: * cpl filled in if any matching commands found * * RETURNS: * status *********************************************************************/ static status_t fill_xpath_children_completion (obj_template_t *rpc, obj_template_t *parentObj, WordCompletion *cpl, const char *line, int word_start, int word_end, int cmdlen) { int retval; int word_iter = word_start + 1; // line[word_start] == '/' word_start ++; cmdlen --; while(word_iter <= word_end) { if ((line[word_iter] == '/') || (line[word_iter] == '[')) { /* The second '/' or predicate condition starting with '[' is found * find the top level obj and fill its child completion */ char childName[128]; int child_name_len = word_iter - word_start; strncpy (childName, &line[word_start], child_name_len); childName[child_name_len] = '\0'; if (parentObj == NULL) return NO_ERR; obj_template_t *childObj = obj_find_child(parentObj, NULL/*obj_get_mod_name(parentObj)*/, (const xmlChar *)childName); if(childObj==NULL) { /* no completion possible - the text before / or [ does not resolve to valid obj */ return NO_ERR; } cmdlen = word_end - word_iter; /* put the children path with topObj into the recursive * lookup function */ if(line[word_iter] == '/') { return fill_xpath_children_completion(rpc, childObj, cpl,line, word_iter, word_end, cmdlen); } else if(line[word_iter] == '[') { return fill_xpath_predicate_completion(rpc, childObj, cpl,line, word_iter, word_end, cmdlen); } } word_iter ++; } obj_template_t * childObj = obj_first_child_deep(parentObj); for(;childObj!=NULL; childObj = obj_next_child_deep(childObj)) { retval = xpath_path_completion_common(rpc, childObj, cpl, line, word_start, word_end, cmdlen); if (retval != 0) { return ERR_NCX_OPERATION_FAILED; } } return NO_ERR; } /* fill_xpath_children_completion */ /******************************************************************** * FUNCTION fill_xpath_predicate_completion * * fill the command struct for one XPath predicate * * INPUTS: * parentObj == object template of parent to check * cpl == word completion struct to fill in * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * cmdlen == command length * * OUTPUTS: * cpl filled in if any matching commands found * * RETURNS: * status *********************************************************************/ static status_t fill_xpath_predicate_completion (obj_template_t *rpc, obj_template_t *parentObj, WordCompletion *cpl, const char *line, int word_start, int word_end, int cmdlen) { const xmlChar *pathname; int retval; int word_iter = word_start + 1; // line[word_start] == '/' word_start ++; cmdlen --; while(word_iter <= word_end) { if (line[word_iter] == ']') { // The second ']' // fill parentObj child completion int predicate_len = word_iter - word_start; if (parentObj == NULL) return NO_ERR; if(((word_iter+1)>word_end) || (line[word_iter+1] != '/')) { return NO_ERR; } word_iter ++; cmdlen = word_end - word_iter; return fill_xpath_children_completion(rpc, parentObj, cpl,line, word_iter, word_end, cmdlen); } word_iter ++; } obj_template_t * childObj = obj_first_child_deep(parentObj); for(;childObj!=NULL; childObj = obj_next_child_deep(childObj)) { pathname = obj_get_name(childObj); /* check if there is a partial command name */ if (cmdlen > 0 && strncmp((const char *)pathname, &line[word_start], cmdlen)) { /* command start is not the same so skip it */ continue; } if( !obj_is_data_db(childObj)) { /* object is either rpc or notification*/ continue; } if(!obj_get_config_flag(childObj)) { const xmlChar* rpc_name; rpc_name = obj_get_name(rpc); if(0==strcmp((const char*)rpc_name, "create")) continue; if(0==strcmp((const char*)rpc_name, "replace")) continue; if(0==strcmp((const char*)rpc_name, "delete")) continue; } if(!obj_is_leaf(childObj)) { const xmlChar* rpc_name; continue; } retval = cpl_add_completion(cpl, line, word_start, word_end, (const char *)&pathname[cmdlen], "", ""); if (retval != 0) { return ERR_NCX_OPERATION_FAILED; } } return NO_ERR; } /* fill_xpath_predicate_completion */ /******************************************************************** * FUNCTION check_find_xpath_top_obj * * Check a module for a specified object (full or partial name) * * INPUTS: * mod == module to check * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * cmdlen == command length * * RETURNS: * pointer to object if found, NULL if not found *********************************************************************/ static obj_template_t * check_find_xpath_top_obj (ncx_module_t *mod, const char *line, int word_start, int cmdlen) { obj_template_t *modObj; assert(cmdlen>0); modObj = ncx_get_first_object(mod); for(; modObj!=NULL; modObj = ncx_get_next_object(mod, modObj)) { const xmlChar *pathname = obj_get_name(modObj); /* check if there is a partial command name */ if (strlen(pathname)==cmdlen && !strncmp((const char *)pathname, &line[word_start], cmdlen)) { return modObj; } } return NULL; } /* check_find_xpath_top_obj */ /******************************************************************** * FUNCTION find_xpath_top_obj * * Check all modules for top-level data nodes to save * * INPUTS: * cpl == word completion struct to fill in * comstate == completion state in progress * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * cmdlen == command length * * OUTPUTS: * cpl filled in if any matching commands found * * RETURNS: * status *********************************************************************/ static obj_template_t * find_xpath_top_obj (completion_state_t *comstate, const char *line, int word_start, int word_end) { int cmdlen = word_end - word_start; obj_template_t *modObj; if(cmdlen==0) { return NULL; } if (use_servercb(comstate->server_cb)) { modptr_t *modptr; for (modptr = (modptr_t *) dlq_firstEntry(&comstate->server_cb->modptrQ); modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { modObj = check_find_xpath_top_obj(modptr->mod, line, word_start, cmdlen); if (modObj != NULL) { return modObj; } } } else { ncx_module_t * mod = ncx_get_first_session_module(); for(;mod!=NULL; mod = ncx_get_next_session_module(mod)) { modObj = check_find_xpath_top_obj(mod, line, word_start, cmdlen); if (modObj != NULL) { return modObj; } } } return NULL; } /* find_xpath_top_obj */ /******************************************************************** * FUNCTION check_save_xpath_completion * * Check a module for saving top-level data objects * * INPUTS: * cpl == word completion struct to fill in * mod == module to check * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * cmdlen == command length * * OUTPUTS: * cpl filled in if any matching commands found * * RETURNS: * status *********************************************************************/ static status_t check_save_xpath_completion ( obj_template_t *rpc, WordCompletion *cpl, ncx_module_t *mod, const char *line, int word_start, int word_end, int cmdlen) { int retval; obj_template_t * modObj = ncx_get_first_object(mod); for (; modObj != NULL; modObj = ncx_get_next_object(mod, modObj)) { retval = xpath_path_completion_common(rpc, modObj, cpl, line, word_start, word_end, cmdlen); if (retval != 0) { return ERR_NCX_OPERATION_FAILED; } } return NO_ERR; } /* check_save_xpath_completion */ /******************************************************************** * FUNCTION fill_xpath_root_completion * * Check all modules for top-level data nodes to save * * INPUTS: * cpl == word completion struct to fill in * comstate == completion state in progress * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * cmdlen == command length * * OUTPUTS: * cpl filled in if any matching commands found * * RETURNS: * status *********************************************************************/ static status_t fill_xpath_root_completion ( obj_template_t *rpc, WordCompletion *cpl, completion_state_t *comstate, const char *line, int word_start, int word_end, int cmdlen) { status_t res; if (use_servercb(comstate->server_cb)) { modptr_t *modptr = (modptr_t *) dlq_firstEntry(&comstate->server_cb->modptrQ); for (; modptr != NULL; modptr = (modptr_t *)dlq_nextEntry(modptr)) { res = check_save_xpath_completion(rpc, cpl, modptr->mod, line, word_start, word_end, cmdlen); if (res != NO_ERR) { return res; } } } else { ncx_module_t * mod = ncx_get_first_session_module(); for (;mod!=NULL; mod = ncx_get_next_session_module(mod)) { res = check_save_xpath_completion(rpc, cpl, mod, line, word_start, word_end, cmdlen); if (res != NO_ERR) { return res; } } } return NO_ERR; } /* fill_xpath_root_completion */ /******************************************************************** * TODO: * FUNCTION fill_one_xpath_completion * * fill the command struct for one RPC operation * check all the xpath that match * * command state is CMD_STATE_FULL or CMD_STATE_GETVAL * * INPUTS: * rpc == rpc operation to use * cpl == word completion struct to fill in * comstate == completion state in progress * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * cmdlen == length of parameter name already entered * this may not be the same as * word_end - word_start if the cursor was * moved within a long line * * OUTPUTS: * cpl filled in if any matching commands found * * RETURNS: * status *********************************************************************/ static status_t fill_one_xpath_completion (obj_template_t *rpc, WordCompletion *cpl, completion_state_t *comstate, const char *line, int word_start, int word_end, int cmdlen) { (void)rpc; int word_iter = word_start + 1; // line[word_start] == '/' if(word_start==word_end) { // this is the case where the cursor is under the / (not after) ignore completion return NO_ERR; } word_start ++; cmdlen --; while(word_iter <= word_end) { // log_write("%c", line[word_iter]); if (line[word_iter] == '/') { // The second '/' is found // TODO: find the top level obj and fill its child completion // log_write("more than 1\n"); obj_template_t * top_obj = find_xpath_top_obj(comstate, line, word_start, word_iter); if(top_obj==NULL) { return NO_ERR; } cmdlen = word_end - word_iter; // put the children path with topObj into the recursive // lookup function return fill_xpath_children_completion (rpc, top_obj, cpl, line, word_iter, word_end, cmdlen); } word_iter ++; } // The second '/' is not found return fill_xpath_root_completion(rpc, cpl, comstate, line, word_start, word_end, cmdlen); } /* fill_one_xpath_completion */ /******************************************************************** * FUNCTION fill_one_rpc_completion_parms * * fill the command struct for one RPC operation * check all the parameters that match * * command state is CMD_STATE_FULL or CMD_STATE_GETVAL * * INPUTS: * rpc == rpc operation to use * cpl == word completion struct to fill in * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * cmdlen == length of parameter name already entered * this may not be the same as * word_end - word_start if the cursor was * moved within a long line * * OUTPUTS: * cpl filled in if any matching commands found * * RETURNS: * status *********************************************************************/ static status_t fill_one_rpc_completion_parms (obj_template_t *rpc, WordCompletion *cpl, completion_state_t *comstate, const char *line, int word_start, int word_end, int cmdlen) { obj_template_t *inputobj, *obj; const xmlChar *parmname; int retval; ncx_btype_t btyp; boolean hasval; if(line[word_start] == '/') { return fill_one_xpath_completion(rpc, cpl, comstate, line, word_start, word_end, cmdlen); } inputobj = obj_find_child(rpc, NULL, YANG_K_INPUT); if (inputobj == NULL || !obj_has_children(inputobj)) { /* no input parameters */ return NO_ERR; } for (obj = obj_first_child_deep(inputobj); obj != NULL; obj = obj_next_child_deep(obj)) { parmname = obj_get_name(obj); /* check if there is a partial command name */ if (cmdlen > 0 && strncmp((const char *)parmname, &line[word_start], cmdlen)) { /* command start is not the same so skip it */ continue; } btyp = obj_get_basetype(obj); hasval = (btyp == NCX_BT_EMPTY) ? FALSE : TRUE; retval = cpl_add_completion(cpl, line, word_start, word_end, (const char *)&parmname[cmdlen], "", (hasval) ? "=" : " "); if (retval != 0) { return ERR_NCX_OPERATION_FAILED; } } return NO_ERR; } /* fill_one_rpc_completion_parms */ /******************************************************************** * FUNCTION fill_one_module_completion_commands * * fill the command struct for one command string * for one module; check all the commands in the module * * command state is CMD_STATE_FULL * * INPUTS: * mod == module to use * cpl == word completion struct to fill in * comstate == completion state in progress * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * cmdlen == length of command already entered * this may not be the same as * word_end - word_start if the cursor was * moved within a long line * * OUTPUTS: * cpl filled in if any matching parameters found * * RETURNS: * status *********************************************************************/ static status_t fill_one_module_completion_commands (ncx_module_t *mod, WordCompletion *cpl, completion_state_t *comstate, const char *line, int word_start, int word_end, int cmdlen) { obj_template_t *obj; const xmlChar *cmdname; ncx_module_t *yangcli_mod; int retval; boolean toponly; yangcli_mod = get_yangcli_mod(); toponly = FALSE; if (mod == yangcli_mod && !use_servercb(comstate->server_cb)) { toponly = TRUE; } /* check all the OBJ_TYP_RPC objects in the module */ for (obj = ncx_get_first_object(mod); obj != NULL; obj = ncx_get_next_object(mod, obj)) { if (!obj_is_rpc(obj)) { continue; } cmdname = obj_get_name(obj); if (toponly && !is_top_command(cmdname)) { continue; } /* check if there is a partial command name */ if (cmdlen > 0 && strncmp((const char *)cmdname, &line[word_start], cmdlen)) { /* command start is not the same so skip it */ continue; } retval = cpl_add_completion(cpl, line, word_start, word_end, (const char *)&cmdname[cmdlen], (const char *)"", (const char *)" "); if (retval != 0) { return ERR_NCX_OPERATION_FAILED; } } return NO_ERR; } /* fill_one_module_completion_commands */ /******************************************************************** * FUNCTION fill_one_completion_commands * * fill the command struct for one command string * * command state is CMD_STATE_FULL * * INPUTS: * cpl == word completion struct to fill in * comstate == completeion state struct in progress * line == line passed to callback * word_start == start position within line of the * word being completed * word_end == word_end passed to callback * cmdlen == length of command already entered * this may not be the same as * word_end - word_start if the cursor was * moved within a long line * * OUTPUTS: * cpl filled in if any matching commands found * * RETURNS: * status *********************************************************************/ static status_t fill_one_completion_commands (WordCompletion *cpl, completion_state_t *comstate, const char *line, int word_start, int word_end, int cmdlen) { modptr_t *modptr; status_t res; res = NO_ERR; /* figure out which modules to use */ if (comstate->cmdmodule) { /* if foo:\t entered as the current token, then * the comstate->cmdmodule pointer will be set * to limit the definitions to that module */ res = fill_one_module_completion_commands (comstate->cmdmodule, cpl, comstate, line, word_start, word_end, cmdlen); } else { if (use_servercb(comstate->server_cb)) { /* list server commands first */ for (modptr = (modptr_t *) dlq_firstEntry(&comstate->server_cb->modptrQ); modptr != NULL && res == NO_ERR; modptr = (modptr_t *)dlq_nextEntry(modptr)) { #ifdef DEBUG_TRACE log_debug("\nFilling from server_cb module %s", modptr->mod->name); #endif res = fill_one_module_completion_commands (modptr->mod, cpl, comstate, line, word_start, word_end, cmdlen); } } #ifdef DEBUG_TRACE /* use the yangcli top commands every time */ log_debug("\nFilling from yangcli module"); #endif res = fill_one_module_completion_commands (get_yangcli_mod(), cpl, comstate, line, word_start, word_end, cmdlen); } return res; } /* fill_one_completion_commands */ /******************************************************************** * FUNCTION is_parm_start * * check if current backwards sequence is the start of * a parameter or not * * command state is CMD_STATE_FULL or CMD_STATE_GETVAL * * INPUTS: * line == current line in progress * word_start == lower bound of string to check * word_cur == current internal parser position * * RETURN: * TRUE if start of parameter found * FALSE if not found * *********************************************************************/ static boolean is_parm_start (const char *line, int word_start, int word_cur) { const char *str; if (word_start >= word_cur) { return FALSE; } str = &line[word_cur]; if (*str == '-') { if ((word_cur - 2) >= word_start) { /* check for previous char before '-' */ if (line[word_cur - 2] == '-') { if ((word_cur - 3) >= word_start) { /* check for previous char before '--' */ if (isspace((int)line[word_cur - 3])) { /* this a real '--' command start */ return TRUE; } else { /* dash dash part of some value string */ return FALSE; } } else { /* start of line is '--' */ return TRUE; } } else if (isspace((int)line[word_cur - 2])) { /* this is a real '-' command start */ return TRUE; } else { return FALSE; } } else { /* start of line is '-' */ return TRUE; } } else { /* previous char is not '-' */ return FALSE; } } /* is_parm_start */ static obj_template_t* get_unique_param_w_value_compl_match(obj_template_t* inputobj, char* str) { obj_template_t* childobj; unsigned int matchcount; /* try to find this parameter name */ childobj = obj_find_child_str(inputobj, NULL, (const xmlChar *)str, strlen(str)); if (childobj == NULL) { matchcount = 0; /* try to match this parameter name */ childobj = obj_match_child_str(inputobj, NULL, (const xmlChar *)str, strlen(str), &matchcount); if (childobj && matchcount > 1) { /* ambiguous command error * but just return no completions */ childobj=NULL; } } /* check if the parameter is an empty, which * means another parameter is expected, not a value */ if (childobj!=NULL && obj_get_basetype(childobj) == NCX_BT_EMPTY) { childobj=NULL; } return childobj; } void parse_cmdline_completion_variable(char* str, int* param_index, int* value_index) { int i; *param_index = -1; *value_index=-1; /* abc=def - *param_index=0 *eq_sign_index=3 */ /* -abc=def - *param_index=1 *eq_sign_index=4 */ /* --abc=def - *param_index=2 *eq_sign_index=5 */ if(str[0]=='-' && str[1]=='-' && str[2]!='-') { *param_index=2; } else if(str[0]=='-' && str[1]!='-') { *param_index=1; } else { *param_index=0; } i=*param_index; assert(i>=0); while(str[i]!=0 && str[i]!='=') { i++; } if(str[i]=='=') { *value_index=i+1; } } /******************************************************************** * FUNCTION find_parm_start * * go backwards and figure out the previous token * from word_end to word_start * * command state is CMD_STATE_FULL or CMD_STATE_GETVAL * * INPUTS: * inputobj == RPC operation input node * used for parameter context * cpl == word completion struct to fill in * comstate == completion state struct to use * line == current line in progress * word_start == lower bound of string to check * word_end == current cursor pos in the 'line' * may be in the middle if the user * entered editing keys; libtecla will * handle the text insertion if needed * expectparm == address of return expect--parmaeter flag * emptyexit == address of return empty exit flag * parmobj == address of return parameter in progress * tokenstart == address of return start of the current token * * * OUTPUTS: * *expectparm == TRUE if expecting a parameter * FALSE if expecting a value * *emptyexit == TRUE if bailing due to no matches possible * FALSE if there is something to process * *parmobj == found parameter that was entered * == NULL if no parameter expected * == NULL if expected parameter not found * (error returned) * *tokenstart == index within line that the current * token starts; will be equal to word_end * if not in the middle of an assignment * sequence * * RETURN: * status *********************************************************************/ static status_t find_parm_start (obj_template_t *inputobj, const char *line, int word_start, int word_end, boolean *expectparm, boolean *emptyexit, obj_template_t **parmobj, int *tokenstart) { yangcli_wordexp_t p; int i; *parmobj = NULL; *emptyexit= FALSE; *expectparm=TRUE; yangcli_wordexp(line, &p, 0); //yangcli_wordexp_dump(&p); for (i = 0; i < p.we_wordc; i++) { if(p.we_word_line_offset[i]+strlen(p.we_wordv[i])==word_end) { /*cursor at the end of a parameter[=value] - completion possible*/ break; } else if((p.we_word_line_offset[i]<=word_end) && (p.we_word_line_offset[i]+strlen(p.we_wordv[i])>word_end)) { /*cursor in the middle of parameter[=value] - completion not possible*/ *emptyexit=TRUE; return NO_ERR; } else if(0==strcmp("--",p.we_wordv[i])) { /* no completion currently supported for secondary arguments e.g. create /interfaces/interface -- loopback=...*/ *emptyexit=TRUE; return NO_ERR; } } if(i==p.we_wordc) { /*next parameter starts from scratch*/ *emptyexit=FALSE; *tokenstart = word_end; } else if(p.we_wordv[i][0]=='/') { /* ignore tokens that are probably default argument Xpath */ *tokenstart = p.we_word_line_offset[i]; *emptyexit=FALSE; } else { int param_index; int value_index; char* param; obj_template_t* childobj; *tokenstart = p.we_word_line_offset[i]; parse_cmdline_completion_variable(p.we_wordv[i], ¶m_index, &value_index); if(value_index!=-1) { unsigned int param_len = value_index-1; param = malloc(param_len+1); memcpy(param,p.we_wordv[i],param_len); param[param_len]=0; *tokenstart=param_len+*tokenstart; childobj=get_unique_param_w_value_compl_match(inputobj,param); free(param); if(childobj==NULL) { *emptyexit = TRUE; } else { *parmobj = childobj; *expectparm=FALSE; } } else if(param_index!=-1) { *tokenstart=param_index+*tokenstart; *emptyexit = FALSE; } } yangcli_wordfree(&p); return NO_ERR; } /******************************************************************** * FUNCTION parse_backwards_parm * * go through the command line backwards * from word_end to word_start, figuring * * command state is CMD_STATE_FULL * * This function only used when there are allowed * to be multiple parm=value pairs on the same line * * INPUTS: * cpl == word completion struct to fill in * comstate == completion state struct to use * line == current line in progress * word_start == lower bound of string to check * word_end == current cursor pos in the 'line' * may be in the middle if the user * entered editing keys; libtecla will * handle the text insertion if needed * * OUTPUTS: * parameter or value completions addeed if found * * RETURN: * status *********************************************************************/ static status_t parse_backwards_parm (WordCompletion *cpl, completion_state_t *comstate, const char *line, int word_start, int word_end) { obj_template_t *rpc, *inputobj, *parmobj; status_t res; int tokenstart; boolean expectparm, emptyexit; if (word_end == 0) { return SET_ERROR(ERR_INTERNAL_VAL); } res = NO_ERR; parmobj = NULL; tokenstart = 0; expectparm = FALSE; emptyexit = FALSE; rpc = comstate->cmdobj; inputobj = comstate->cmdinput; res = find_parm_start(inputobj, line, word_start, word_end, &expectparm, &emptyexit, &parmobj, &tokenstart); if (res != NO_ERR || emptyexit) { return res; } if (expectparm) { /* add all the parameters, even those that * might already be entered (oh well) * this is OK for leaf-lists but not leafs */ #ifdef YANGCLI_TAB_DEBUG log_debug2("\n*** fill one RPC %s parms ***\n", obj_get_name(rpc)); #endif res = fill_one_rpc_completion_parms(rpc, cpl, comstate, line, tokenstart, word_end, word_end - tokenstart); } else if (parmobj) { /* have a parameter in progress and the * token start is supposed to be the value * for this parameter */ #ifdef YANGCLI_TAB_DEBUG log_debug2("\n*** fill one parm in backwards ***\n"); #endif res = fill_parm_completion(parmobj, cpl, comstate, line, tokenstart, word_end, word_end - tokenstart); } /* else nothing to do */ return res; } /* parse_backwards_parm */ /******************************************************************** * FUNCTION fill_parm_values * * go through the command line * from word_start to word_end, figuring * out which value is entered * * command state is CMD_STATE_GETVAL * * INPUTS: * cpl == word completion struct to fill in * comstate == completion state struct to use * line == current line in progress * word_start == lower bound of string to check * word_end == current cursor pos in the 'line' * may be in the middle if the user * entered editing keys; libtecla will * handle the text insertion if needed * * OUTPUTS: * value completions addeed if found * * RETURN: * status *********************************************************************/ static status_t fill_parm_values (WordCompletion *cpl, completion_state_t *comstate, const char *line, int word_start, int word_end) { status_t res; /* skip starting whitespace */ while (word_start < word_end && isspace((int)line[word_start])) { word_start++; } /*** NEED TO CHECK FOR STRING QUOTES ****/ #ifdef YANGCLI_TAB_DEBUG log_debug2("\n*** fill parm values ***\n"); #endif res = fill_parm_completion(comstate->cmdcurparm, cpl, comstate, line, word_start, word_end, word_end - word_start); return res; } /* fill_parm_values */ /******************************************************************** * FUNCTION strip_predicate * * INPUTS: * line == original line containing predicates * * OUTPUTS: * line_striped == at least equal in size buffer to the original line to contain the result * * EXAMPLE: * /interfaces/interface[name='ge0']/mtu -> /interfaces/interface/mtu *********************************************************************/ static void strip_predicate(char* line, char* line_striped) { unsigned int i,j; unsigned int len; int predicate_open=0; int double_quote_open=0; int single_quote_open=0; unsigned int predicate_open_j; len = strlen(line); for(i=0,j=0;iassignstmt = TRUE; equaldone = FALSE; while ((str < &line[word_end]) && !isspace((int)*str) && (*str != '=')) { str++; } /* check where the string search stopped */ if (isspace((int)*str) || *str == '=') { /* stopped past the first word * so need to skip further */ if (isspace((int)*str)) { /* find equals sign or word_end */ while ((str < &line[word_end]) && isspace((int)*str)) { str++; } } if ((*str == '=') && (str < &line[word_end])) { equaldone = TRUE; str++; /* skip equals sign */ /* skip more whitespace */ while ((str < &line[word_end]) && isspace((int)*str)) { str++; } } /* got past the '$foo =' part * now looking for a command */ if (str < &line[word_end]) { /* go to next part and look for * the end of this start command */ word_start = (int)(str - line); } else if (equaldone) { res = fill_one_completion_commands(cpl, comstate, line, word_end, word_end, 0); if (res != NO_ERR) { cpl_record_error(cpl, get_error_string(res)); } return res; } else { /* still inside the file or variable name */ return res; } } else { /* word_end is still inside the first * word, which is an assignment of * some sort; not going to show * any completions for user vars and files yet */ return res; } } else { /* first word starts with a normal char */ word_start = (int)(str - line); } /* the word_start var is set to the first char * that is supposed to be start command name * the 'str' var points to that char * check if it is an entire or partial command name */ cmdend = str; while (cmdend < &line[word_end] && !isspace((int)*cmdend)) { cmdend++; } cmdlen = (int)(cmdend - str); /* check if still inside the start command */ if (cmdend == &line[word_end]) { /* get all the commands that start with the same * characters for length == 'cmdlen' */ res = fill_one_completion_commands(cpl, comstate, line, word_start, word_end, cmdlen); if (res != NO_ERR) { cpl_record_error(cpl, get_error_string(res)); } return res; } /* not inside the start command so get the command that * was selected if it exists; at this point the command * should be known, so find it */ cmdname = &line[word_start]; buffer = xml_strndup((const xmlChar *)cmdname, (uint32)cmdlen); if (buffer == NULL) { if (cpl != NULL) { cpl_record_error(cpl, get_error_string(ERR_INTERNAL_MEM)); } return 1; } retlen = 0; dtyp = NCX_NT_OBJ; res = NO_ERR; comstate->cmdobj = (obj_template_t *) parse_def(comstate->server_cb, &dtyp, buffer, &retlen, &res); m__free(buffer); if (comstate->cmdobj == NULL) { if (cpl != NULL) { cpl_record_error(cpl, get_error_string(res)); } return 1; } /* have a command that is valid * first skip any whitespace after the * command name */ str = cmdend; while (str < &line[word_end] && isspace((int)*str)) { str++; } /* set new word_start == word_end */ word_start = (int)(str - line); /* check where E-O-WSP search stopped */ if (str == &line[word_end]) { /* stopped before entering any parameters */ res = fill_one_rpc_completion_parms(comstate->cmdobj, cpl, comstate, line, word_start, word_end, 0); return res; } /* there is more text entered; check if this rpc * really has any input parameters or not */ inputobj = obj_find_child(comstate->cmdobj, NULL, YANG_K_INPUT); if (inputobj == NULL || !obj_has_children(inputobj)) { /* no input parameters expected */ return NO_ERR; } else { comstate->cmdinput = inputobj; } /* got some edited text past the last * quoted string so figure out the * parm in progress and check for completions */ res = parse_backwards_parm(cpl, comstate, line, word_start, word_end); return res; } /* fill_completion_commands */ /******************************************************************** * FUNCTION fill_completion_yesno * * go through the available commands to see what * matches should be returned * * command state is CMD_STATE_YESNO * * INPUTS: * cpl == word completion struct to fill in * line == current line in progress * word_end == current cursor pos in the 'line' * may be in the middle if the user * entered editing keys; libtecla will * handle the text insertion if needed * OUTPUTS: * comstate * RETURN: * status *********************************************************************/ static status_t fill_completion_yesno (WordCompletion *cpl, const char *line, int word_end) { const char *str; int word_start, retval; str = line; word_start = 0; /* skip starting whitespace */ while ((str < &line[word_end]) && isspace((int)*str)) { str++; } /* check if any real characters entered yet */ if (word_end == 0 || str == &line[word_end]) { /* found only spaces so far or * nothing entered yet */ retval = cpl_add_completion(cpl, line, word_start, word_end, (const char *)NCX_EL_YES, (const char *)"", (const char *)" "); if (retval != 0) { return ERR_NCX_OPERATION_FAILED; } retval = cpl_add_completion(cpl, line, word_start, word_end, (const char *)NCX_EL_NO, (const char *)"", (const char *)" "); if (retval != 0) { return ERR_NCX_OPERATION_FAILED; } } /*** !!!! ELSE NOT GIVING ANY PARTIAL COMPLETIONS YET !!! ***/ return NO_ERR; } /* fill_completion_yesno */ /*....................................................................... * * FUNCTION yangcli_tab_callback (word_complete_cb) * * libtecla tab-completion callback function * * Matches the CplMatchFn typedef * * From /usr/lib/include/libtecla.h: * * Callback functions declared and prototyped using the following macro * are called upon to return an array of possible completion suffixes * for the token that precedes a specified location in the given * input line. It is up to this function to figure out where the token * starts, and to call cpl_add_completion() to register each possible * completion before returning. * * Input: * cpl WordCompletion * An opaque pointer to the object that will * contain the matches. This should be filled * via zero or more calls to cpl_add_completion(). * data void * The anonymous 'data' argument that was * passed to cpl_complete_word() or * gl_customize_completion()). * line const char * The current input line. * word_end int The index of the character in line[] which * follows the end of the token that is being * completed. * Output * return int 0 - OK. * 1 - Error. */ int yangcli_tab_callback (WordCompletion *cpl, void *data, const char *line, int word_end) { completion_state_t *comstate; status_t res; int retval; #ifdef DEBUG if (!cpl || !data || !line) { SET_ERROR(ERR_INTERNAL_PTR); return 1; } #endif retval = 0; /* check corner case that never has any completions * no matter what state; backslash char only allowed * for escaped-EOLN or char-sequence that we can't guess */ if (word_end > 0 && line[word_end] == '\\') { return retval; } comstate = (completion_state_t *)data; if (comstate->server_cb && comstate->server_cb->climore) { comstate->cmdstate = CMD_STATE_MORE; } switch (comstate->cmdstate) { case CMD_STATE_FULL: res = fill_completion_commands(cpl, comstate, line, word_end); if (res != NO_ERR) { retval = 1; } break; case CMD_STATE_GETVAL: res = fill_parm_values(cpl, comstate, line, 0, word_end); if (res != NO_ERR) { retval = 1; } break; case CMD_STATE_YESNO: res = fill_completion_yesno(cpl, line, word_end); if (res != NO_ERR) { retval = 1; } break; case CMD_STATE_MORE: /*** NO SUPPORT FOR MORE MODE YET ***/ break; case CMD_STATE_NONE: /* command state not initialized */ SET_ERROR(ERR_INTERNAL_VAL); retval = 1; break; default: /* command state garbage value */ SET_ERROR(ERR_INTERNAL_VAL); retval = 1; } return retval; } /* yangcli_tab_callback */ /* END yangcli.c */ yuma123_2.14/netconf/src/yangcli/yangcli_eval.h0000664000175000017500000000406114770023131021603 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangcli_eval #define _H_yangcli_eval /* FILE: yangcli_eval.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 14-apr-10 abb Begun */ #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION do_eval (local RPC) * * eval select='expr' docroot=$myvar * * Evaluate the XPath expression * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the exec command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_eval (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli_eval */ yuma123_2.14/netconf/src/yangcli/yangcli_autolock.c0000664000175000017500000006454214770023131022502 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_autolock.c NETCONF YANG-based CLI Tool autolock support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13-aug-09 abb begun; started from yangcli.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_mgr #include "mgr.h" #endif #ifndef _H_mgr_ses #include "mgr_ses.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_op #include "op.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_rpc_err #include "rpc_err.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifndef _H_var #include "var.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifndef _H_yangcli_autolock #include "yangcli_autolock.h" #endif #ifndef _H_yangcli_cmd #include "yangcli_cmd.h" #endif #ifndef _H_yangcli_util #include "yangcli_util.h" #endif #include "uptime.h" /******************************************************************** * FUNCTION setup_lock_cbs * * Setup the lock state info in all the lock control blocks * in the specified server_cb; call when a new sesion is started * * INPUTS: * server_cb == server control block to use *********************************************************************/ static void setup_lock_cbs (server_cb_t *server_cb) { ses_cb_t *scb; mgr_scb_t *mscb; ncx_cfg_t cfg_id; scb = mgr_ses_get_scb(server_cb->mysid); if (scb == NULL) { log_error("\nError: active session dropped, cannot lock"); return; } mscb = (mgr_scb_t *)scb->mgrcb; server_cb->locks_active = TRUE; server_cb->locks_waiting = FALSE; server_cb->locks_cur_cfg = NCX_CFGID_RUNNING; for (cfg_id = NCX_CFGID_RUNNING; cfg_id <= NCX_CFGID_STARTUP; cfg_id++) { server_cb->lock_cb[cfg_id].lock_state = LOCK_STATE_IDLE; server_cb->lock_cb[cfg_id].lock_used = FALSE; server_cb->lock_cb[cfg_id].start_time = (time_t)0; server_cb->lock_cb[cfg_id].last_msg_time = (time_t)0; } /* always request the lock on running */ server_cb->lock_cb[NCX_CFGID_RUNNING].lock_used = TRUE; server_cb->lock_cb[NCX_CFGID_CANDIDATE].lock_used = (cap_std_set(&mscb->caplist, CAP_STDID_CANDIDATE)) ? TRUE : FALSE; server_cb->lock_cb[NCX_CFGID_STARTUP].lock_used = (cap_std_set(&mscb->caplist, CAP_STDID_STARTUP)) ? TRUE : FALSE; } /* setup_lock_cbs */ /******************************************************************** * FUNCTION setup_unlock_cbs * * Setup the lock state info in all the lock control blocks * in the specified server_cb; call when release-locks or cleanup * is releasing all the locks gained so far * * INPUTS: * server_cb == server control block to use * * RETURNS: * TRUE if sending unlocks needed * FALSE if sending unlocks not needed *********************************************************************/ static boolean setup_unlock_cbs (server_cb_t *server_cb) { boolean needed; ncx_cfg_t cfg_id; if (!server_cb->locks_active) { return FALSE; } needed = FALSE; for (cfg_id = NCX_CFGID_RUNNING; cfg_id <= NCX_CFGID_STARTUP; cfg_id++) { server_cb->lock_cb[cfg_id].start_time = (time_t)0; server_cb->lock_cb[cfg_id].last_msg_time = (time_t)0; if (server_cb->lock_cb[cfg_id].lock_used && server_cb->lock_cb[cfg_id].lock_state == LOCK_STATE_ACTIVE) { needed = TRUE; } } return needed; } /* setup_unlock_cbs */ /******************************************************************** * FUNCTION send_lock_pdu_to_server * * Send a or operation to the server * * INPUTS: * server_cb == server control block to use * lockcb == lock control block to use within server_cb * islock == TRUE for lock; FALSE for unlock * * RETURNS: * status *********************************************************************/ static status_t send_lock_pdu_to_server (server_cb_t *server_cb, lock_cb_t *lockcb, boolean islock) { obj_template_t *rpc, *input; mgr_rpc_req_t *req; val_value_t *reqdata, *targetval, *parmval; ses_cb_t *scb; status_t res; xmlns_id_t obj_nsid; req = NULL; reqdata = NULL; res = NO_ERR; if (LOGDEBUG) { log_debug("\nSending <%s> request", (islock) ? NCX_EL_LOCK : NCX_EL_UNLOCK); } if (islock) { rpc = ncx_find_object(get_netconf_mod(server_cb), NCX_EL_LOCK); } else { rpc = ncx_find_object(get_netconf_mod(server_cb), NCX_EL_UNLOCK); } if (!rpc) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } obj_nsid = obj_get_nsid(rpc); /* get the 'input' section container */ input = obj_find_child(rpc, NULL, YANG_K_INPUT); if (!input) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } /* construct a method + parameter tree */ reqdata = xml_val_new_struct(obj_get_name(rpc), obj_nsid); if (!reqdata) { log_error("\nError allocating a new RPC request"); return ERR_INTERNAL_MEM; } /* set the [un]lock/input/target node XML namespace */ targetval = xml_val_new_struct(NCX_EL_TARGET, obj_nsid); if (!targetval) { log_error("\nError allocating a new RPC request"); val_free_value(reqdata); return ERR_INTERNAL_MEM; } else { val_add_child(targetval, reqdata); } parmval = xml_val_new_flag(lockcb->config_name, obj_nsid); if (!parmval) { val_free_value(reqdata); return ERR_INTERNAL_MEM; } else { val_add_child(parmval, targetval); } scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { res = SET_ERROR(ERR_INTERNAL_PTR); } else { req = mgr_rpc_new_request(scb); if (!req) { res = ERR_INTERNAL_MEM; log_error("\nError allocating a new RPC request"); } else { req->data = reqdata; req->rpc = rpc; req->timeout = server_cb->timeout; } } /* if all OK, send the RPC request */ if (res == NO_ERR) { if (LOGDEBUG2) { log_debug2("\nabout to send RPC request with reqdata:"); val_dump_value_max(reqdata, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); } /* the request will be stored if this returns NO_ERR */ res = mgr_rpc_send_request(scb, req, yangcli_reply_handler); if (res == NO_ERR) { if (islock) { lockcb->lock_state = LOCK_STATE_REQUEST_SENT; } else { lockcb->lock_state = LOCK_STATE_RELEASE_SENT; } (void)uptime(&lockcb->last_msg_time); server_cb->locks_cur_cfg = lockcb->config_id; } } /* cleanup and set next state */ if (res != NO_ERR) { if (req) { mgr_rpc_free_request(req); } else if (reqdata) { val_free_value(reqdata); } } else { server_cb->state = MGR_IO_ST_CONN_RPYWAIT; } return res; } /* send_lock_pdu_to_server */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION do_get_locks (local RPC) * * get all the locks on the server * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the history command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_get_locks (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { ses_cb_t *scb; val_value_t *valset, *parm; uint32 locks_timeout, retry_interval; boolean cleanup, done; status_t res; if (server_cb->locks_active) { log_error("\nError: locks are already active"); return ERR_NCX_OPERATION_FAILED; } if (server_cb->state != MGR_IO_ST_CONN_IDLE) { log_error("\nError: no active session to lock"); return ERR_NCX_OPERATION_FAILED; } scb = mgr_ses_get_scb(server_cb->mysid); if (scb == NULL) { log_error("\nError: active session dropped, cannot lock"); return ERR_NCX_OPERATION_FAILED; } locks_timeout = server_cb->locks_timeout; retry_interval = server_cb->locks_retry_interval; cleanup = TRUE; res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (valset && res == NO_ERR) { /* get the overall lock timeout */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_LOCK_TIMEOUT); if (parm && parm->res == NO_ERR) { locks_timeout = VAL_UINT(parm); } /* get the retry interval between failed locks */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_RETRY_INTERVAL); if (parm && parm->res == NO_ERR) { retry_interval = VAL_UINT(parm); } /* get the auto-cleanup flag */ parm = val_find_child(valset, YANGCLI_MOD, YANGCLI_CLEANUP); if (parm && parm->res == NO_ERR) { cleanup = VAL_BOOL(parm); } } /* start the auto-lock procedure */ setup_lock_cbs(server_cb); server_cb->locks_timeout = locks_timeout; server_cb->locks_retry_interval = retry_interval; server_cb->locks_cleanup = cleanup; done = FALSE; if (LOGINFO) { log_info("\nSending operations for get-locks...\n"); } res = handle_get_locks_request_to_server(server_cb, TRUE, &done); if (res != NO_ERR && done) { /* need to undo the whole thing; whatever got done */ } if (valset != NULL) { val_free_value(valset); } return res; } /* do_get_locks */ /******************************************************************** * FUNCTION do_release_locks (local RPC) * * release all the locks on the server * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the history command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_release_locks (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { ses_cb_t *scb; val_value_t *valset; uint32 locks_timeout, retry_interval; boolean cleanup, done, needed; status_t res; if (!server_cb->locks_active) { log_error("\nError: locks are not active"); return ERR_NCX_OPERATION_FAILED; } scb = mgr_ses_get_scb(server_cb->mysid); if (scb == NULL) { log_error("\nError: active session dropped, cannot lock"); return ERR_NCX_OPERATION_FAILED; } locks_timeout = server_cb->locks_timeout; retry_interval = server_cb->locks_retry_interval; cleanup = TRUE; res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (res == NO_ERR || res == ERR_NCX_SKIPPED) { /* start the auto-unlock procedure */ server_cb->locks_timeout = locks_timeout; server_cb->locks_retry_interval = retry_interval; server_cb->locks_cleanup = cleanup; needed = setup_unlock_cbs(server_cb); if (LOGINFO && needed) { log_info("\nSending operations for release-locks...\n"); } if (needed) { done = FALSE; res = handle_release_locks_request_to_server(server_cb, TRUE, &done); if (done) { /* need to close the session or report fatal error */ clear_lock_cbs(server_cb); } } } if (valset != NULL) { val_free_value(valset); } return res; } /* do_release_locks */ /******************************************************************** * FUNCTION handle_get_locks_request_to_server * * Send the first operation to the server * in a get-locks command * * INPUTS: * server_cb == server control block to use * first == TRUE if this is the first call; FALSE otherwise * done == address of return final done flag * * OUTPUTS: * server_cb->state may be changed or other action taken * *done == TRUE when the return code is NO_ERR and all * the locks are granted * FALSE otherwise * RETURNS: * status; if NO_ERR then check *done flag * otherwise done is true on any error *********************************************************************/ status_t handle_get_locks_request_to_server (server_cb_t *server_cb, boolean first, boolean *done) { lock_cb_t *lockcb; ncx_cfg_t cfg_id; time_t timenow; double timediff; status_t res; boolean finddone, stillwaiting; #ifdef DEBUG if (!server_cb || !done) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; *done = FALSE; finddone = FALSE; stillwaiting = FALSE; lockcb = NULL; (void)uptime(&timenow); if (first) { /* this is the first try, start the overall timer */ (void)uptime(&server_cb->locks_start_time); } else if (check_locks_timeout(server_cb)) { log_error("\nError: get-locks timeout"); handle_locks_cleanup(server_cb); return ERR_NCX_TIMEOUT; } /* find a config that needs a lock PDU sent * first check for a config that has not sent * any request yet */ for (cfg_id = NCX_CFGID_RUNNING; cfg_id <= NCX_CFGID_STARTUP && !finddone; cfg_id++) { lockcb = &server_cb->lock_cb[cfg_id]; if (lockcb->lock_used) { if (lockcb->lock_state == LOCK_STATE_IDLE) { finddone = TRUE; } else if (lockcb->lock_state == LOCK_STATE_FATAL_ERROR) { log_error("\nError: fatal error getting lock" " on the %s config", lockcb->config_name); return ERR_NCX_OPERATION_FAILED; } } } if (!finddone) { /* all entries that need to send a lock * request have tried at least once to do that * now go through all the states and see if any * retries are needed; exit if a fatal error is found */ stillwaiting = FALSE; for (cfg_id = NCX_CFGID_RUNNING; cfg_id <= NCX_CFGID_STARTUP && !finddone; cfg_id++) { lockcb = &server_cb->lock_cb[cfg_id]; if (lockcb->lock_used) { if (lockcb->lock_state == LOCK_STATE_TEMP_ERROR) { timediff = difftime(timenow, lockcb->last_msg_time); if (timediff >= (double)server_cb->locks_retry_interval) { finddone = TRUE; } else { server_cb->locks_waiting = TRUE; stillwaiting = TRUE; } } } } /* check if there is more work to do after this * function exits */ if (!finddone && !stillwaiting) { *done = TRUE; } } /* check if a request needs to be sent */ if (finddone && lockcb) { server_cb->command_mode = CMD_MODE_AUTOLOCK; res = send_lock_pdu_to_server(server_cb, lockcb, TRUE); } return res; } /* handle_get_locks_request_to_server */ /******************************************************************** * FUNCTION handle_release_locks_request_to_server * * Send an operation to the server * in a get-locks command teardown or a release-locks * operation * * INPUTS: * server_cb == server control block to use * first == TRUE if this is the first call; FALSE otherwise * done == address of return final done flag * * OUTPUTS: * server_cb->state may be changed or other action taken * *done == TRUE when the return code is NO_ERR and all * the locks are granted * FALSE otherwise * RETURNS: * status; if NO_ERR then check *done flag * otherwise done is true on any error *********************************************************************/ status_t handle_release_locks_request_to_server (server_cb_t *server_cb, boolean first, boolean *done) { lock_cb_t *lockcb; ncx_cfg_t cfg_id; status_t res; boolean finddone; #ifdef DEBUG if (!server_cb || !done) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif lockcb = NULL; res = NO_ERR; finddone = FALSE; *done = FALSE; if (first) { server_cb->command_mode = CMD_MODE_AUTOUNLOCK; (void)uptime(&server_cb->locks_start_time); } else if (check_locks_timeout(server_cb)) { log_error("\nError: release-locks timeout"); clear_lock_cbs(server_cb); return ERR_NCX_TIMEOUT; } /* do these in order because there are no * temporary errors for unlock */ for (cfg_id = NCX_CFGID_RUNNING; cfg_id <= NCX_CFGID_STARTUP && !finddone; cfg_id++) { lockcb = &server_cb->lock_cb[cfg_id]; if (lockcb->lock_used && lockcb->lock_state == LOCK_STATE_ACTIVE) { finddone = TRUE; } } if (!finddone) { /* nothing to do */ if (first) { log_info("\nNo locks to release"); } server_cb->state = MGR_IO_ST_CONN_IDLE; clear_lock_cbs(server_cb); *done = TRUE; } else { res = send_lock_pdu_to_server(server_cb, lockcb, FALSE); } return res; } /* handle_release_locks_request_to_server */ /******************************************************************** * FUNCTION handle_locks_cleanup * * Deal with the cleanup for the get-locks or release-locks * * INPUTS: * server_cb == server control block to use * * OUTPUTS: * server_cb->state may be changed or other action taken * *********************************************************************/ void handle_locks_cleanup (server_cb_t *server_cb) { status_t res; boolean done; #ifdef DEBUG if (!server_cb) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (!use_servercb(server_cb)) { log_error("\nError: connection lost, canceling release-locks"); clear_lock_cbs(server_cb); return; } if (server_cb->locks_cleanup) { server_cb->command_mode = CMD_MODE_AUTOUNLOCK; done = FALSE; res = handle_release_locks_request_to_server(server_cb, TRUE, &done); if (res != NO_ERR) { log_error("\nError: handle lock request failed (%)", get_error_string(res)); } if (done) { clear_lock_cbs(server_cb); } } else { clear_lock_cbs(server_cb); } } /* handle_locks_cleanup */ /******************************************************************** * FUNCTION check_locks_timeout * * Check if the locks_timeout is active and if it expired yet * * INPUTS: * server_cb == server control block to use * * RETURNS: * TRUE if locks_timeout expired * FALSE if no timeout has occurred *********************************************************************/ boolean check_locks_timeout (server_cb_t *server_cb) { time_t timenow; double timediff; #ifdef DEBUG if (!server_cb) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (server_cb->locks_timeout) { /* check if there is an overall timeout yet */ (void)uptime(&timenow); timediff = difftime(timenow, server_cb->locks_start_time); if (timediff >= (double)server_cb->locks_timeout) { log_debug("\nlock timeout"); return TRUE; } } return FALSE; } /* check_locks_timeout */ /******************************************************************** * FUNCTION send_discard_changes_pdu_to_server * * Send a operation to the server * * INPUTS: * server_cb == server control block to use * * RETURNS: * status *********************************************************************/ status_t send_discard_changes_pdu_to_server (server_cb_t *server_cb) { obj_template_t *rpc; mgr_rpc_req_t *req; val_value_t *reqdata; ses_cb_t *scb; status_t res; xmlns_id_t obj_nsid; req = NULL; reqdata = NULL; res = NO_ERR; if (LOGDEBUG) { log_debug("\nSending request"); } rpc = ncx_find_object(get_netconf_mod(server_cb), NCX_EL_DISCARD_CHANGES); if (!rpc) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } obj_nsid = obj_get_nsid(rpc); /* construct a method node */ reqdata = xml_val_new_flag(obj_get_name(rpc), obj_nsid); if (!reqdata) { log_error("\nError allocating a new RPC request"); return ERR_INTERNAL_MEM; } scb = mgr_ses_get_scb(server_cb->mysid); if (!scb) { res = SET_ERROR(ERR_INTERNAL_PTR); } else { req = mgr_rpc_new_request(scb); if (!req) { res = ERR_INTERNAL_MEM; log_error("\nError allocating a new RPC request"); } else { req->data = reqdata; req->rpc = rpc; req->timeout = server_cb->timeout; } } /* if all OK, send the RPC request */ if (res == NO_ERR) { if (LOGDEBUG2) { log_debug2("\nabout to send RPC request with reqdata:"); val_dump_value_max(reqdata, 0, server_cb->defindent, DUMP_VAL_LOG, server_cb->display_mode, FALSE, FALSE); } /* the request will be stored if this returns NO_ERR */ res = mgr_rpc_send_request(scb, req, yangcli_reply_handler); if (res == NO_ERR) { server_cb->command_mode = CMD_MODE_AUTODISCARD; } } /* cleanup and set next state */ if (res != NO_ERR) { if (req) { mgr_rpc_free_request(req); } else if (reqdata) { val_free_value(reqdata); } } else { server_cb->state = MGR_IO_ST_CONN_RPYWAIT; } return res; } /* send_discard_changes_pdu_to_server */ /******************************************************************** * FUNCTION clear_lock_cbs * * Clear the lock state info in all the lock control blocks * in the specified server_cb * * INPUTS: * server_cb == server control block to use * *********************************************************************/ void clear_lock_cbs (server_cb_t *server_cb) { ncx_cfg_t cfg_id; /* set up lock control blocks for get-locks */ server_cb->locks_active = FALSE; server_cb->locks_waiting = FALSE; server_cb->locks_cur_cfg = NCX_CFGID_RUNNING; server_cb->command_mode = CMD_MODE_NORMAL; for (cfg_id = NCX_CFGID_RUNNING; cfg_id <= NCX_CFGID_STARTUP; cfg_id++) { server_cb->lock_cb[cfg_id].lock_state = LOCK_STATE_IDLE; server_cb->lock_cb[cfg_id].lock_used = FALSE; server_cb->lock_cb[cfg_id].start_time = (time_t)0; server_cb->lock_cb[cfg_id].last_msg_time = (time_t)0; } } /* clear_lock_cbs */ /* END yangcli_autolock.c */ yuma123_2.14/netconf/src/yangcli/yangcli_show.h0000664000175000017500000000422214770023131021633 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangcli_show #define _H_yangcli_show /* FILE: yangcli_show.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13-augr-09 abb Begun; move from yangcli_cmd.c */ #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION do_show (local RPC) * * show module=mod-name * modules * def=def-nmae * * Get the specified parameter and show the internal info, * based on the parameter * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_show (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli_show */ yuma123_2.14/netconf/src/yangcli/yangcli_globals.h0000664000175000017500000000107214770023131022276 0ustar vladimirvladimir#include "ncx.h" /* base schema modules */ extern ncx_module_t *yangcli_mod; extern ncx_module_t *yangcli_ex_mod; extern ncx_module_t *netconf_mod; /* global connect param set, copied to server connect parmsets */ extern val_value_t *connect_valset; /* need to save CLI parameters: other vars are back-pointers */ extern val_value_t *mgr_cli_valset; /* Q of ncxmod_search_result_t structs representing all modules * and submodules found in the module library path at boot-time */ extern dlq_hdr_t modlibQ; extern ncxmod_temp_progcb_t *yangcli_progcb; yuma123_2.14/netconf/src/yangcli/yangcli_cond.c0000664000175000017500000003267114770023131021602 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangcli_cond.c NETCONF YANG-based CLI Tool conditional command - if - elif - else - end - while ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 25-apr-10 abb begun; ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "libtecla.h" #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_mgr #include "mgr.h" #endif #ifndef _H_mgr_ses #include "mgr_ses.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifndef _H_var #include "var.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xpath1 #include "xpath1.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifndef _H_yangcli_cond #include "yangcli_cond.h" #endif #ifndef _H_yangcli_cmd #include "yangcli_cmd.h" #endif #ifndef _H_yangcli_util #include "yangcli_util.h" #endif /******************************************************************** * FUNCTION do_elif (local RPC) * * Handle the if command; start a new ifcb context * * elif expr='xpath-str' [docroot=$foo] * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * isif == TRUE for if, FALSE for elif * RETURNS: * status *********************************************************************/ static status_t do_if_elif (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len, boolean isif) { val_value_t *valset, *docroot, *expr, *dummydoc; xpath_pcb_t *pcb; xpath_result_t *result; status_t res; boolean cond; docroot = NULL; expr = NULL; pcb = NULL; dummydoc = NULL; cond = FALSE; res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (valset == NULL) { return res; } if (res != NO_ERR) { val_free_value(valset); return res; } if (valset->res != NO_ERR) { res = valset->res; val_free_value(valset); return res; } /* get the expr parameter */ expr = val_find_child(valset, YANGCLI_MOD, YANGCLI_EXPR); if (expr == NULL) { res = ERR_NCX_MISSING_PARM; } else if (expr->res != NO_ERR) { res = expr->res; } if (res == NO_ERR) { /* get the optional docroot parameter */ docroot = val_find_child(valset, YANGCLI_MOD, YANGCLI_DOCROOT); if (docroot && docroot->res != NO_ERR) { res = docroot->res; } } if (res == NO_ERR && docroot == NULL) { dummydoc = xml_val_new_struct(NCX_EL_DATA, xmlns_nc_id()); if (dummydoc == NULL) { res = ERR_INTERNAL_MEM; } else { docroot = dummydoc; } } if (res == NO_ERR) { /* got all the parameters, and setup the XPath control block */ pcb = xpath_new_pcb_ex(VAL_STR(expr), xpath_getvar_fn, server_cb->runstack_context); if (pcb == NULL) { res = ERR_INTERNAL_MEM; } else if ((isif && runstack_get_cond_state(server_cb->runstack_context)) || (!isif && !runstack_get_if_used(server_cb->runstack_context))) { /* figure out if this if or elif block is enabled or not */ result = xpath1_eval_expr(pcb, docroot, /* context */ docroot, TRUE, FALSE, &res); if (result != NULL && res == NO_ERR) { /* get new condition state for this loop */ cond = xpath_cvt_boolean(result); } xpath_free_result(result); } if (res == NO_ERR) { if (isif) { res = runstack_handle_if(server_cb->runstack_context, cond); } else { res = runstack_handle_elif(server_cb->runstack_context, cond); } } } /* cleanup and exit */ if (valset) { val_free_value(valset); } if (pcb) { xpath_free_pcb(pcb); } if (dummydoc) { val_free_value(dummydoc); } return res; } /* do_if_elif */ /************** E X P O R T E D F U N C T I O N S *************/ /******************************************************************** * FUNCTION do_while (local RPC) * * Handle the while command; start a new loopcb context * * while expr='xpath-str' [docroot=$foo] * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_while (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset, *docroot, *expr, *dummydoc, *maxloops; xpath_pcb_t *pcb; status_t res; uint32 maxloopsval; docroot = NULL; expr = NULL; pcb = NULL; dummydoc = NULL; res = NO_ERR; maxloopsval = YANGCLI_DEF_MAXLOOPS; valset = get_valset(server_cb, rpc, &line[len], &res); if (valset == NULL) { return res; } if (res != NO_ERR) { val_free_value(valset); return res; } if (valset->res != NO_ERR) { res = valset->res; val_free_value(valset); return res; } /* get the expr parameter */ expr = val_find_child(valset, YANGCLI_MOD, YANGCLI_EXPR); if (expr == NULL) { res = ERR_NCX_MISSING_PARM; } else if (expr->res != NO_ERR) { res = expr->res; } if (res == NO_ERR) { /* get the optional docroot parameter */ docroot = val_find_child(valset, YANGCLI_MOD, YANGCLI_DOCROOT); if (docroot != NULL) { if (docroot->res != NO_ERR) { res = docroot->res; } else { val_remove_child(docroot); } } } if (res == NO_ERR && docroot == NULL) { dummydoc = xml_val_new_struct(NCX_EL_DATA, xmlns_nc_id()); if (dummydoc == NULL) { res = ERR_INTERNAL_MEM; } else { docroot = dummydoc; } } if (res == NO_ERR) { /* get the optional maxloops parameter */ maxloops = val_find_child(valset, YANGCLI_MOD, YANGCLI_MAXLOOPS); if (maxloops != NULL) { if (maxloops->res != NO_ERR) { res = maxloops->res; } else { maxloopsval = VAL_UINT(maxloops); } } } if (res == NO_ERR) { /* got all the parameters, and setup the XPath control block */ pcb = xpath_new_pcb_ex(VAL_STR(expr), xpath_getvar_fn, server_cb->runstack_context); if (pcb == NULL) { res = ERR_INTERNAL_MEM; } else { /* save these parameter and start a new while context block */ res = runstack_handle_while(server_cb->runstack_context, maxloopsval, pcb, /* hand off memory here */ docroot); /* hand off memory here */ if (res == NO_ERR) { /* hand off the pcb and docroot memory above */ pcb = NULL; docroot = NULL; } } } /* cleanup and exit */ if (valset) { val_free_value(valset); } if (pcb) { xpath_free_pcb(pcb); } if (docroot) { val_free_value(docroot); } return res; } /* do_while */ /******************************************************************** * FUNCTION do_if (local RPC) * * Handle the if command; start a new ifcb context * * if expr='xpath-str' [docroot=$foo] * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_if (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { return do_if_elif(server_cb, rpc, line, len, TRUE); } /* do_if */ /******************************************************************** * FUNCTION do_elif (local RPC) * * Handle the if command; start a new ifcb context * * elif expr='xpath-str' [docroot=$foo] * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_elif (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { return do_if_elif(server_cb, rpc, line, len, FALSE); } /* do_elif */ /******************************************************************** * FUNCTION do_else (local RPC) * * Handle the else command; start another conditional sub-block * to the current if-block * * else * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_else (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset; status_t res; res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (valset == NULL) { if (res != ERR_NCX_SKIPPED) { return res; } } else { val_free_value(valset); return ERR_NCX_INVALID_VALUE; } res = runstack_handle_else(server_cb->runstack_context); return res; } /* do_else */ /******************************************************************** * FUNCTION do_end (local RPC) * * Handle the end command; end an if or while block * * end * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the show command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ status_t do_end (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len) { val_value_t *valset; status_t res; res = NO_ERR; valset = get_valset(server_cb, rpc, &line[len], &res); if (valset == NULL) { if (res != ERR_NCX_SKIPPED) { return res; } } else { val_free_value(valset); return ERR_NCX_INVALID_VALUE; } res = runstack_handle_end(server_cb->runstack_context); return res; } /* do_end */ /* END yangcli_cond.c */ yuma123_2.14/netconf/src/yangcli/yangcli_alias.h0000664000175000017500000001316014770023131021745 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangcli_alias #define _H_yangcli_alias /* FILE: yangcli_alias.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* implement yangcli alias command ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 01-oct-11 abb Begun; */ #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yangcli #include "yangcli.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION do_alias (local RPC) * * alias * alias def * alias def=def-value * * Handle the alias command, based on the parameter * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the alias command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_alias (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); /******************************************************************** * FUNCTION do_aliases (local RPC) * * aliases * aliases clear * aliases show * aliases load[=filespec] * aliases save[=filespec] * * Handle the aliases command, based on the parameter * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the aliases command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_aliases (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); /******************************************************************** * FUNCTION do_unset (local RPC) * * unset def * * Handle the unset command; remove the specified alias * * INPUTS: * server_cb == server control block to use * rpc == RPC method for the unset command * line == CLI input in progress * len == offset into line buffer to start parsing * * RETURNS: * status *********************************************************************/ extern status_t do_unset (server_cb_t *server_cb, obj_template_t *rpc, const xmlChar *line, uint32 len); /******************************************************************** * FUNCTION show_aliases * * Output all the alias values * *********************************************************************/ extern void show_aliases (void); /******************************************************************** * FUNCTION show_alias * * Output 1 alias by name * * INPUTS: * name == name of alias to show *********************************************************************/ extern void show_alias (const xmlChar *name); /******************************************************************** * FUNCTION free_aliases * * Free all the command aliases * *********************************************************************/ extern void free_aliases (void); /******************************************************************** * FUNCTION load_aliases * * Load the aliases from the specified filespec * * INPUT: * fspec == input filespec to use (NULL == default) * * RETURNS: * status *********************************************************************/ extern status_t load_aliases (const xmlChar *fspec); /******************************************************************** * FUNCTION save_aliases * * Save the aliases to the specified filespec * * INPUT: * fspec == output filespec to use (NULL == default) * * RETURNS: * status *********************************************************************/ extern status_t save_aliases (const xmlChar *fspec); /******************************************************************** * FUNCTION expand_alias * * Check if the first token is an alias name * If so, construct a new command line with the alias contents * * INPUT: * line == command line to check and possibly convert * res == address of return status * * OUTPUTS: * *res == return status (ERR_NCX_SKIPPED if nothing done) * * RETURNS: * pointer to malloced command string if *res==NO_ERR * NULL if *res==ERR_NCX_SKIPPED or some real error *********************************************************************/ extern xmlChar * expand_alias (xmlChar *line, status_t *res); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli_alias */ yuma123_2.14/netconf/src/yangcli/yangcli_tab.h0000664000175000017500000000554314770023131021430 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangcli_tab #define _H_yangcli_tab /* FILE: yangcli_tab.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Tab word completion callback support for libtecla ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 18-apr-09 abb Begun; */ #include "libtecla.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /*....................................................................... * * FUNCTION yangcli_tab_callback (word_complete_cb) * * libtecla tab-completion callback function * * Matches the CplMatchFn typedef * * From /usr/lib/include/libtecla.h: * * Callback functions declared and prototyped using the following macro * are called upon to return an array of possible completion suffixes * for the token that precedes a specified location in the given * input line. It is up to this function to figure out where the token * starts, and to call cpl_add_completion() to register each possible * completion before returning. * * Input: * cpl WordCompletion * An opaque pointer to the object that will * contain the matches. This should be filled * via zero or more calls to cpl_add_completion(). * data void * The anonymous 'data' argument that was * passed to cpl_complete_word() or * gl_customize_completion()). * line const char * The current input line. * word_end int The index of the character in line[] which * follows the end of the token that is being * completed. * Output * return int 0 - OK. * 1 - Error. */ extern int yangcli_tab_callback (WordCompletion *cpl, void *data, const char *line, int word_end); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangcli_tab */ yuma123_2.14/netconf/src/netconfd/0000775000175000017500000000000014770023131017146 5ustar vladimirvladimiryuma123_2.14/netconf/src/netconfd/Makefile.am0000775000175000017500000000070014770023131021202 0ustar vladimirvladimirsbin_PROGRAMS = netconfd netconfd_SOURCES = \ $(top_srcdir)/netconf/src/netconfd/netconfd.c netconfd_CPPFLAGS = -I $(top_srcdir)/netconf/src/netconfd/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump $(XML_CPPFLAGS) netconfd_LDFLAGS = $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la yuma123_2.14/netconf/src/netconfd/netconfd.c0000664000175000017500000002653014770023131021120 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: netconfd.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 04-jun-06 abb begun; cloned from ncxmain.c 250aug-06 abb renamed from ncxagtd.c to netconfd.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #ifdef MEMORY_DEBUG #include #endif #include #define _C_main 1 #include "procdefs.h" #include "agt.h" #include "agt_ncxserver.h" #include "agt_util.h" #include "agt_cli.h" #include "help.h" #include "log.h" #include "ncx.h" #include "ncxconst.h" #include "ncxmod.h" #include "status.h" #include "xmlns.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define NETCONFD_MOD (const xmlChar *)"netconfd" #define NETCONFD_EX_MOD (const xmlChar *)"netconfd-ex" #define NETCONFD_CLI (const xmlChar *)"netconfd" #define START_MSG "Starting netconfd...\n" /******************************************************************** * FUNCTION load_base_schema * * RETURNS: * status *********************************************************************/ static status_t load_base_schema (void) { status_t res; /* load in the NETCONF data types and RPC methods */ res = ncxmod_load_module( NCXMOD_YUMA_NETCONF, NULL, NULL, NULL ); if (res != NO_ERR) { return res; } /* load in the server boot parameter definition file */ res = ncxmod_load_module( NCXMOD_NETCONFD_EX, NULL, NULL, NULL ); if (res != NO_ERR) { return res; } return res; } /* load_base_schema */ #ifdef NETCONFD_DEBUG_LOAD_TEST /******************************************************************** * FUNCTION load_debug_test_module * * RETURNS: * status *********************************************************************/ static status_t load_debug_test_module( agt_profile_t* profile ) { #define TESTMOD (const xmlChar *)"test" #define TESTFEATURE1 (const xmlChar *)"feature1" #define TESTFEATURE2 (const xmlChar *)"feature2" #define TESTFEATURE3 (const xmlChar *)"feature3" #define TESTFEATURE4 (const xmlChar *)"feature4" log_debug2("\nnetconfd: Loading Debug Test Module"); /* Load test module */ status_t res = ncxmod_load_module( TESTMOD, NULL, &profile->agt_savedevQ, NULL ); if (res != NO_ERR) { return res; } else { agt_enable_feature(TESTMOD, TESTFEATURE1); agt_disable_feature(TESTMOD, TESTFEATURE2); agt_enable_feature(TESTMOD, TESTFEATURE3); agt_disable_feature(TESTMOD, TESTFEATURE4); } } #endif /******************************************************************** * FUNCTION load_core_schema * * RETURNS: * status *********************************************************************/ static status_t load_core_schema ( agt_profile_t *profile ) { log_debug2("\nnetconfd: Loading NCX Module"); /* load in the with-defaults extension module */ status_t res = ncxmod_load_module( NCXMOD_WITH_DEFAULTS, NULL, &profile->agt_savedevQ, NULL ); if (res != NO_ERR) { return res; } #ifdef NETCONFD_DEBUG_LOAD_TEST res = load_debug_test_module( profile ); #endif return res; } /* load_core_schema */ /******************************************************************** * FUNCTION cmn_init * * * *********************************************************************/ static status_t cmn_init ( int argc, char *argv[], boolean *showver, help_mode_t *showhelpmode, boolean *validate_config_only_mode) { #define BUFFLEN 256 status_t res; log_debug_t dlevel; int len; char buff[BUFFLEN]; val_value_t* val; val_value_t* cli_valset; *validate_config_only_mode=FALSE; /* set the default debug output level */ dlevel = LOG_DEBUG_INFO; /* initialize the NCX Library first to allow NCX modules to be processed. * No module can get its internal config until the NCX module parser and * definition registry is up */ len = strlen(START_MSG) + strlen(COPYRIGHT_STRING_LINE0) + strlen(COPYRIGHT_STRING_LINE1) + 2; if (len < BUFFLEN) { strcpy(buff, START_MSG); strcat(buff, COPYRIGHT_STRING_LINE0); strcat(buff, COPYRIGHT_STRING_LINE1); } else { return ERR_BUFF_OVFL; } res = ncx_init( FALSE, dlevel, TRUE, buff, argc, argv); if (res != NO_ERR) { return res; } log_debug2("\nnetconfd: Loading Netconf Server Library"); /* at this point, modules that need to read config params can be * initialized */ /* Load the core modules (netconfd and netconf) */ res = load_base_schema(); if (res != NO_ERR) { return res; } /* Initialize the Netconf Server Library with command line and conf file * parameters */ res = agt_init1(argc, argv, showver, showhelpmode); if (res != NO_ERR) { return res; } /* check quick-exit mode */ if (*showver || *showhelpmode != HELP_MODE_NONE) { return NO_ERR; } cli_valset = agt_cli_get_valset(); val = val_find_child(cli_valset, NETCONFD_EX_MOD, "validate-config-only"); if(val!=NULL) { *validate_config_only_mode=TRUE; } /* Load the core modules (netconfd and netconf) */ res = load_core_schema(agt_get_profile()); if (res != NO_ERR) { return res; } /* finish initializing server data structures */ res = agt_init2(); if (res != NO_ERR) { return res; } log_debug("\nnetconfd init OK, ready for sessions\n"); return NO_ERR; } /* cmn_init */ /******************************************************************** * FUNCTION show_server_banner * * Show startup server string * *********************************************************************/ static void show_server_banner (void) { #define BANNER_BUFFLEN 32 xmlChar buff[BANNER_BUFFLEN]; status_t res; if (LOGINFO) { res = ncx_get_version(buff, BANNER_BUFFLEN); if (res == NO_ERR) { log_info("\nRunning netconfd server (%s)\n", buff); } else { log_info("\nRunning netconfd server\n"); } } } /* show_server_banner */ /******************************************************************** * FUNCTION netconfd_run * * Startup and run the NCX server loop * * RETURNS: * status: NO_ERR if startup OK and then run OK this will be a delayed * return code some error if server startup failed * e.g., socket already in use *********************************************************************/ static status_t netconfd_run (void) { status_t res; show_server_banner(); res = agt_ncxserver_run(); if (res != NO_ERR) { log_error("\nncxserver failed (%s)", get_error_string(res)); } return res; } /* netconfd_run */ /******************************************************************** * FUNCTION netconfd_cleanup *********************************************************************/ static void netconfd_cleanup (void) { if (LOGINFO) { log_info("\nShutting down the netconfd server\n"); } /* Cleanup the Netconf Server Library */ agt_cleanup(); /* cleanup the NCX engine and registries */ ncx_cleanup(); } /* netconfd_cleanup */ /******************************************************************** * FUNCTION show_version *********************************************************************/ static void show_version(void) { xmlChar versionbuffer[NCX_VERSION_BUFFSIZE]; status_t res = ncx_get_version(versionbuffer, NCX_VERSION_BUFFSIZE); if (res == NO_ERR) { log_write( "\nnetconfd version %s\n", versionbuffer ); } else { SET_ERROR( res ); } agt_request_shutdown(NCX_SHUT_EXIT); } /******************************************************************** * * * FUNCTION main * * * *********************************************************************/ int main (int argc, char *argv[]) { status_t res; boolean showver = FALSE; boolean done = FALSE; help_mode_t showhelpmode; boolean validate_config_only_mode = FALSE; #ifdef MEMORY_DEBUG mtrace(); #endif /* this loop is used to implement the restart command the sw image is not * reloaded; instead everything is cleaned up and re-initialized from * scratch. If the shutdown operation (or Ctl-C exit) is used instead of * restart, then the loop will only be executed once */ while (!done) { res = cmn_init( argc, argv, &showver, &showhelpmode, &validate_config_only_mode); if (res != NO_ERR) { log_error( "\nnetconfd: init returned (%s)", get_error_string(res) ); agt_request_shutdown(NCX_SHUT_EXIT); } else { if (showver) { show_version(); } else if (showhelpmode != HELP_MODE_NONE) { help_program_module( NETCONFD_MOD, NETCONFD_CLI, showhelpmode ); agt_request_shutdown(NCX_SHUT_EXIT); } else if (validate_config_only_mode) { agt_request_shutdown(NCX_SHUT_EXIT); } else { res = netconfd_run(); if (res != NO_ERR) { agt_request_shutdown(NCX_SHUT_EXIT); } } } netconfd_cleanup(); print_error_count(); if ( NCX_SHUT_EXIT == agt_shutdown_mode_requested() ) { done = TRUE; } } print_errors(); print_error_count(); if ( !log_is_open() ) { printf("\n"); } #ifdef MEMORY_DEBUG muntrace(); #endif return res; } /* main */ /* END netconfd.c */ yuma123_2.14/netconf/src/mgr/0000775000175000017500000000000014770023131016133 5ustar vladimirvladimiryuma123_2.14/netconf/src/mgr/mgr_val_parse.c0000664000175000017500000001712714770023131021130 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: mgr_val_parse.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11feb06 abb begun; hack, clone agent code and remove all the rpc-error handling code; later a proper libxml2 docPtr interface will be used instead ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include "procdefs.h" #include "b64.h" #include "cfg.h" #include "def_reg.h" #include "dlq.h" #include "log.h" #include "mgr.h" #include "mgr_val_parse.h" #include "val_parse.h" #include "mgr_xml.h" #include "ncx.h" #include "ncx_num.h" #include "ncx_str.h" #include "ncx_list.h" #include "ncxconst.h" #include "obj.h" #include "status.h" #include "tk.h" #include "typ.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xpath.h" #include "xpath_yang.h" #include "xpath1.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define MGR_VAL_PARSE_DEBUG 1 #endif /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION mgr_val_parse * * parse a value for a YANG type from a NETCONF PDU XML stream * * Parse NETCONF PDU sub-contents into value fields * This module does not enforce complex type completeness. * Different subsets of configuration data are permitted * in several standard (and any proprietary) RPC methods * * A seperate parsing phase is used to validate the input * contained in the returned val_value_t struct. * * This parsing phase checks that simple types are complete * and child members of complex types are valid (but maybe * missing or incomplete child nodes. * * INPUTS: * scb == session control block * obj == obj_template_t for the object type to parse * startnode == top node of the parameter to be parsed * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * * RETURNS: * status *********************************************************************/ status_t mgr_val_parse (ses_cb_t *scb, obj_template_t *obj, const xml_node_t *startnode, val_value_t *retval) { return val_parse(scb, obj, startnode, retval); } /* mgr_val_parse */ /******************************************************************** * FUNCTION mgr_val_parse_reply * * parse an element * parse a value for a YANG type from a NETCONF PDU XML stream * Use the RPC object output type to parse any data * * Parse NETCONF PDU sub-contents into value fields * This module does not enforce complex type completeness. * Different subsets of configuration data are permitted * in several standard (and any proprietary) RPC methods * * A seperate parsing phase is used to validate the input * contained in the returned val_value_t struct. * * This parsing phase checks that simple types are complete * and child members of complex types are valid (but maybe * missing or incomplete child nodes. * * INPUTS: * scb == session control block * obj == obj_template_t for the top-level reply to parse * rpc == RPC template to use for any data in the output * startnode == top node of the parameter to be parsed * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * * RETURNS: * status *********************************************************************/ status_t mgr_val_parse_reply (ses_cb_t *scb, obj_template_t *obj, obj_template_t *rpc, const xml_node_t *startnode, val_value_t *retval) { obj_template_t *output; status_t res; #ifdef DEBUG if (!scb || !obj || !startnode || !retval) { /* non-recoverable error */ return SET_ERROR(ERR_INTERNAL_PTR); } #endif #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG3) { log_debug3("\nmgr_val_parse_reply: %s:%s btyp:%s", obj_get_mod_prefix(obj), obj_get_name(obj), tk_get_btype_sym(obj_get_basetype(obj))); } #endif output = (rpc) ? obj_find_child(rpc, NULL, NCX_EL_OUTPUT) : NULL; /* get the element values */ res = val_parse_split(scb, obj, output, startnode, retval); return res; } /* mgr_val_parse_reply */ /******************************************************************** * FUNCTION mgr_val_parse_notification * * parse a element * parse a value for a YANG type from a NETCONF PDU XML stream * Use the notification object output type to parse any data * * This parsing phase checks that simple types are complete * and child members of complex types are valid (but maybe * missing or incomplete child nodes. * * INPUTS: * scb == session control block * notobj == obj_template_t for the top-level notification * startnode == top node of the parameter to be parsed * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * * RETURNS: * status *********************************************************************/ status_t mgr_val_parse_notification (ses_cb_t *scb, obj_template_t *notobj, const xml_node_t *startnode, val_value_t *retval) { status_t res; #ifdef DEBUG if (!scb || !notobj || !startnode || !retval) { /* non-recoverable error */ return SET_ERROR(ERR_INTERNAL_PTR); } #endif #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG3) { log_debug3("\nmgr_val_parse_notification: start"); } #endif /* get the element values */ res = val_parse_split(scb, notobj, NULL, startnode, retval); return res; } /* mgr_val_parse_notification */ /* END file mgr_val_parse.c */ yuma123_2.14/netconf/src/mgr/mgr.h0000664000175000017500000001553214770023131017077 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_mgr #define _H_mgr /* FILE: mgr.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Manager message handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 03-feb-06 abb Begun */ /* used by the manager for the SSH2 interface */ #include #include "cap.h" #include "cfg.h" #include "ncxmod.h" #include "status.h" #include "var.h" #include "xpath.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define MGR_MAX_REQUEST_ID 0xfffffffe /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* extension to the ses_cb_t for a manager session */ typedef struct mgr_scb_t_ { /* agent info */ ncx_agttarg_t targtyp; ncx_agtstart_t starttyp; cap_list_t caplist; uint32 agtsid; /* agent assigned session ID */ boolean closed; /* temp directory for downloaded modules */ ncxmod_temp_progcb_t *temp_progcb; ncxmod_temp_sescb_t *temp_sescb; dlq_hdr_t temp_modQ; /* Q of ncx_module_t */ ncx_list_t temp_ync_features; /* running config cached info */ val_value_t *root; xmlChar *chtime; val_value_t *lastroot; xmlChar *lastchtime; /* yang-library */ val_value_t *modules_state_val; /* transport info */ xmlChar *target; LIBSSH2_SESSION *session; LIBSSH2_CHANNEL *channel; LIBSSH2_AGENT *agent; int returncode; /* RPC request info */ uint32 next_id; dlq_hdr_t reqQ; /* XPath variable binding callback function */ xpath_getvar_fn_t getvar_fn; /* User defined context */ void* context_ptr; } mgr_scb_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION mgr_disable_sighandlers * * Prevent mgr library from registering signal handlers. Call this * BEFORE mgr_init() if the application will install its own handlers. * In this case, the application may need to call mgr_request_shutdown() * at the appropriate times. mgr_cleanup() will restore the default * behavior of allowing the library to register handlers. * *********************************************************************/ extern void mgr_disable_sighandlers(void); /******************************************************************** * FUNCTION mgr_init * * Initialize the Manager Library * * RETURNS: * status of the initialization procedure *********************************************************************/ extern status_t mgr_init (void); /******************************************************************** * FUNCTION mgr_cleanup * * Cleanup the Manager Library * *********************************************************************/ extern void mgr_cleanup (void); /******************************************************************** * FUNCTION mgr_new_scb * * Malloc and Initialize the Manager Session Control Block * * RETURNS: * manager session control block struct or NULL if malloc error *********************************************************************/ extern mgr_scb_t * mgr_new_scb (void); /******************************************************************** * FUNCTION mgr_init_scb * * Initialize the Manager Session Control Block * * INPUTS: * mscb == manager session control block struct to initialize * *********************************************************************/ extern void mgr_init_scb (mgr_scb_t *mscb); /******************************************************************** * FUNCTION mgr_free_scb * * Clean and Free a Manager Session Control Block * * INPUTS: * mscb == manager session control block struct to free *********************************************************************/ extern void mgr_free_scb (mgr_scb_t *mscb); /******************************************************************** * FUNCTION mgr_clean_scb * * Clean a Manager Session Control Block * * INPUTS: * mscb == manager session control block struct to clean *********************************************************************/ extern void mgr_clean_scb (mgr_scb_t *mscb); /******************************************************************** * FUNCTION mgr_request_shutdown * * Request a manager shutdown * *********************************************************************/ extern void mgr_request_shutdown (void); /******************************************************************** * FUNCTION mgr_shutdown_requested * * Check if a manager shutdown is in progress * * RETURNS: * TRUE if shutdown mode has been started * *********************************************************************/ extern boolean mgr_shutdown_requested (void); /******************************************************************** * FUNCTION mgr_set_getvar_fn * * Set the getvar_fn callback for the session * * INPUTS: * sid == manager session ID to use * getvar_fn == function to use * * RETURNS: * status *********************************************************************/ extern status_t mgr_set_getvar_fn (ses_id_t sid, xpath_getvar_fn_t getvar_fn); /******************************************************************** * FUNCTION mgr_print_libssh2_version * * Print the version of libssh2 used by the manager * Indenting must already be done! * * INPUTS: * tolog == TRUE to print to log; FALSE to print to stdout *********************************************************************/ extern void mgr_print_libssh2_version (boolean tolog); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_mgr */ yuma123_2.14/netconf/src/mgr/mgr_not.c0000664000175000017500000002547514770023131017761 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: mgr_not.c NETCONF Protocol Operations: RPC Manager Side Support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17may05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_mgr #include "mgr.h" #endif #ifndef _H_mgr_not #include "mgr_not.h" #endif #ifndef _H_mgr_ses #include "mgr_ses.h" #endif #ifndef _H_mgr_val_parse #include "mgr_val_parse.h" #endif #ifndef _H_mgr_xml #include "mgr_xml.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_rpc_err #include "rpc_err.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_top #include "top.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_msg #include "xml_msg.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_wr #include "xml_wr.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define MGR_NOT_DEBUG 1 #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean mgr_not_init_done = FALSE; static obj_template_t *notification_obj; mgr_not_cbfn_t callbackfn; /******************************************************************** * FUNCTION new_msg * * Malloc and initialize a new mgr_not_msg_t struct * * INPUTS: * none * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ static mgr_not_msg_t * new_msg (void) { mgr_not_msg_t *msg; msg = m__getObj(mgr_not_msg_t); if (!msg) { return NULL; } memset(msg, 0x0, sizeof(mgr_not_msg_t)); msg->notification = val_new_value(); if (!msg->notification) { m__free(msg); return NULL; } /* xml_msg_init_hdr(&msg->mhdr); */ return msg; } /* new_msg */ /************** E X T E R N A L F U N C T I O N S ***************/ /******************************************************************** * FUNCTION mgr_not_init * * Initialize the mgr_not module * call once to init module * Adds the mgr_not_dispatch function as the handler * for the NETCONF top-level element. * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ status_t mgr_not_init (void) { status_t res; if (!mgr_not_init_done) { res = top_register_node(NCN_MODULE, NCX_EL_NOTIFICATION, mgr_not_dispatch); if (res != NO_ERR) { return res; } notification_obj = NULL; callbackfn = NULL; mgr_not_init_done = TRUE; } return NO_ERR; } /* mgr_not_init */ /******************************************************************** * FUNCTION mgr_not_cleanup * * Cleanup the mgr_not module. * call once to cleanup module * Unregister the top-level NETCONF element * *********************************************************************/ void mgr_not_cleanup (void) { if (mgr_not_init_done) { top_unregister_node(NCN_MODULE, NCX_EL_NOTIFICATION); notification_obj = NULL; callbackfn = NULL; mgr_not_init_done = FALSE; } } /* mgr_not_cleanup */ /******************************************************************** * FUNCTION mgr_not_free_msg * * Free a mgr_not_msg_t struct * * INPUTS: * msg == struct to free * * RETURNS: * none *********************************************************************/ void mgr_not_free_msg (mgr_not_msg_t *msg) { #ifdef DEBUG if (!msg) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (msg->notification) { val_free_value(msg->notification); } m__free(msg); } /* mgr_not_free_msg */ /******************************************************************** * FUNCTION mgr_not_clean_msgQ * * Clean the msg Q of mgr_not_msg_t entries * * INPUTS: * msgQ == Q of entries to free; the Q itself is not freed * * RETURNS: * none *********************************************************************/ void mgr_not_clean_msgQ (dlq_hdr_t *msgQ) { mgr_not_msg_t *msg; #ifdef DEBUG if (!msgQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif msg = (mgr_not_msg_t *)dlq_deque(msgQ); while (msg) { mgr_not_free_msg(msg); msg = (mgr_not_msg_t *)dlq_deque(msgQ); } } /* mgr_not_clean_msgQ */ /******************************************************************** * FUNCTION mgr_not_dispatch * * Dispatch an incoming response * handle the element * called by mgr_top.c: * This function is registered with top_register_node * for the module 'notification', top-node 'notification' * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ void mgr_not_dispatch (ses_cb_t *scb, xml_node_t *top) { obj_template_t *notobj; mgr_not_msg_t *msg; ncx_module_t *mod; val_value_t *child; boolean consumed; #ifdef DEBUG if (!scb || !top) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* check if the notification template is already cached */ if (notification_obj) { notobj = notification_obj; } else { /* no: get the notification template */ notobj = NULL; mod = ncx_find_module(NCN_MODULE, NULL); if (mod) { notobj = ncx_find_object(mod, NCX_EL_NOTIFICATION); } if (notobj) { notification_obj = notobj; } else { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); mgr_xml_skip_subtree(scb->reader, top); return; } } /* the current node is 'notification' in the notifications namespace * First get a new notification msg struct */ msg = new_msg(); if (!msg) { log_error("\nError: mgr_not: skipping incoming message"); mgr_xml_skip_subtree(scb->reader, top); return; } /* parse the notification as a val_value_t tree, * stored in msg->notification */ msg->res = mgr_val_parse_notification(scb, notobj, top, msg->notification); if (msg->res != NO_ERR && LOGINFO) { log_info("\nmgr_not: got invalid notification on session %d (%s)", scb->sid, get_error_string(msg->res)); } /* check that there is nothing after the element */ if (msg->res==NO_ERR && !xml_docdone(scb->reader) && LOGINFO) { log_info("\nmgr_not: got extra nodes in notification on session %d", scb->sid); } consumed = FALSE; if (msg->res == NO_ERR && msg->notification) { child = val_get_first_child(msg->notification); if (child) { if (!xml_strcmp(child->name, (const xmlChar *)"eventTime")) { msg->eventTime = child; } else { log_error("\nError: expected 'eventTime' in " "notification, got '%s'", child->name); } child = val_get_next_child(child); if (child) { /* eventType is expected to be next!! */ msg->eventType = child; } } else { log_error("\nError: expected 'eventTime' in " "notification, got nothing"); } /* invoke the notification handler */ if (callbackfn) { (*callbackfn)(scb, msg, &consumed); } } if (!consumed) { mgr_not_free_msg(msg); } } /* mgr_not_dispatch */ /******************************************************************** * FUNCTION mgr_not_set_callback_fn * * Set the application callback function to handle * notifications when they arrive * * INPUTS: * cbfn == callback function to use * == NULL to clear the callback function *********************************************************************/ void mgr_not_set_callback_fn (mgr_not_cbfn_t cbfn) { callbackfn = cbfn; } /* mgr_not_set_callback_fn */ /* END file mgr_not.c */ yuma123_2.14/netconf/src/mgr/mgr_top.c0000664000175000017500000001206714770023131017754 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: mgr_top.c NCX Manager Top Element Handler This module uses a simple queue of top-level entries because there are not likely to be very many of them. Each top-level node is keyed by the owner name and the element name. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11feb07 abb begun; start from agt/agt_top.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_mgr #include "mgr.h" #endif #ifndef _H_mgr_top #include "mgr_top.h" #endif #ifndef _H_mgr_xml #include "mgr_xml.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_top #include "top.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define MGR_TOP_DEBUG 1 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION mgr_top_dispatch_msg * * Find the appropriate top node handler and call it * * INPUTS: * scb == session control block containing the xmlreader * set at the start of an incoming message. * * RETURNS: * none *********************************************************************/ void mgr_top_dispatch_msg (ses_cb_t *scb) { xml_node_t top; status_t res; top_handler_t handler; #ifdef DEBUG if (!scb) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif xml_init_node(&top); /* get the first node */ res = mgr_xml_consume_node(scb->reader, &top); if (res != NO_ERR) { log_info("\nmgr_top: get node failed (%s); session dropped", get_error_string(res)); xml_clean_node(&top); scb->state = SES_ST_SHUTDOWN_REQ; return; } #ifdef MGR_TOP_DEBUG if (LOGDEBUG3) { log_debug3("\nmgr_top: got node"); xml_dump_node(&top); } #endif /* check node type and if handler exists, then call it */ if (top.nodetyp==XML_NT_START || top.nodetyp==XML_NT_EMPTY) { /* find the module, elname tuple in the topQ */ handler = top_find_handler(top.module, top.elname); if (handler) { /* call the handler */ (*handler)(scb, &top); } else { res = ERR_NCX_DEF_NOT_FOUND; } } else { res = ERR_NCX_WRONG_NODETYP; } /* check any error trying to invoke the top handler */ if (res != NO_ERR) { log_error("\nError: agt_top skipped msg for session %d (%s)", scb->sid, get_error_string(res)); } xml_clean_node(&top); } /* mgr_top_dispatch_msg */ /* END file mgr_top.c */ yuma123_2.14/netconf/src/mgr/mgr_val_parse.h0000664000175000017500000001176714770023131021141 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_mgr_val_parse #define _H_mgr_val_parse /* FILE: mgr_val_parse.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Parameter Value Parser Module ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 18-feb-07 abb Begun; start from agt_val_parse.c */ #ifndef _H_obj #include "obj.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION mgr_val_parse * * parse a value for a YANG type from a NETCONF PDU XML stream * * Parse NETCONF PDU sub-contents into value fields * This module does not enforce complex type completeness. * Different subsets of configuration data are permitted * in several standard (and any proprietary) RPC methods * * A seperate parsing phase is used to validate the input * contained in the returned val_value_t struct. * * This parsing phase checks that simple types are complete * and child members of complex types are valid (but maybe * missing or incomplete child nodes. * * INPUTS: * scb == session control block * obj == obj_template_t for the object type to parse * startnode == top node of the parameter to be parsed * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * * RETURNS: * status *********************************************************************/ extern status_t mgr_val_parse (ses_cb_t *scb, obj_template_t *obj, const xml_node_t *startnode, val_value_t *retval); /******************************************************************** * FUNCTION mgr_val_parse_reply * * parse an element * parse a value for a YANG type from a NETCONF PDU XML stream * Use the RPC object output type to parse any data * * Parse NETCONF PDU sub-contents into value fields * This module does not enforce complex type completeness. * Different subsets of configuration data are permitted * in several standard (and any proprietary) RPC methods * * A seperate parsing phase is used to validate the input * contained in the returned val_value_t struct. * * This parsing phase checks that simple types are complete * and child members of complex types are valid (but maybe * missing or incomplete child nodes. * * INPUTS: * scb == session control block * obj == obj_template_t for the top-level reply to parse * rpc == RPC template to use for any data in the output * startnode == top node of the parameter to be parsed * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * * RETURNS: * status *********************************************************************/ extern status_t mgr_val_parse_reply (ses_cb_t *scb, obj_template_t *obj, obj_template_t *rpc, const xml_node_t *startnode, val_value_t *retval); /******************************************************************** * FUNCTION mgr_val_parse_notification * * parse a element * parse a value for a YANG type from a NETCONF PDU XML stream * Use the notification object output type to parse any data * * This parsing phase checks that simple types are complete * and child members of complex types are valid (but maybe * missing or incomplete child nodes. * * INPUTS: * scb == session control block * notobj == obj_template_t for the top-level notification * startnode == top node of the parameter to be parsed * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * * RETURNS: * status *********************************************************************/ extern status_t mgr_val_parse_notification (ses_cb_t *scb, obj_template_t *notobj, const xml_node_t *startnode, val_value_t *retval); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_mgr_val_parse */ yuma123_2.14/netconf/src/mgr/mgr_cap.c0000664000175000017500000001541414770023131017714 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: mgr_cap.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 03feb06 abb begun; split out from base/cap.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_mgr_cap #include "mgr_cap.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* #define MGR_CAP_DEBUG 1 */ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static val_value_t *mgr_caps = NULL; static cap_list_t *my_mgr_caps = NULL; /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION mgr_cap_cleanup * * Clean the NETCONF manager capabilities * * INPUTS: * none * RETURNS: * none *********************************************************************/ void mgr_cap_cleanup (void) { if (mgr_caps) { val_free_value(mgr_caps); mgr_caps = NULL; } if (my_mgr_caps) { cap_clean_caplist(my_mgr_caps); m__free(my_mgr_caps); my_mgr_caps = NULL; } } /* mgr_cap_cleanup */ /******************************************************************** * FUNCTION mgr_cap_set_caps * * Initialize the NETCONF manager capabilities * * TEMP !!! JUST SET EVERYTHING, WILL REALLY GET FROM MGR STARTUP * * INPUTS: * none * RETURNS: * NO_ERR if all goes well *********************************************************************/ status_t mgr_cap_set_caps (void) { val_value_t *oldcaps, *newcaps; cap_list_t *oldmycaps,*newmycaps; xmlns_id_t nc_id; status_t res; res = NO_ERR; newcaps = NULL; nc_id = xmlns_nc_id(); oldcaps = mgr_caps; oldmycaps = my_mgr_caps; /* get a new cap_list */ newmycaps = cap_new_caplist(); if (!newmycaps) { res = ERR_INTERNAL_MEM; } /* get a new val_value_t cap list for manager messages */ if (res == NO_ERR) { newcaps = xml_val_new_struct(NCX_EL_CAPABILITIES, nc_id); if (!newcaps) { res = ERR_INTERNAL_MEM; } } /* add capability for NETCONF version 1.0 support */ if (res == NO_ERR) { res = cap_add_std(newmycaps, CAP_STDID_V1); if (res == NO_ERR) { res = cap_add_stdval(newcaps, CAP_STDID_V1); } } /* check the return value */ if (res != NO_ERR) { /* toss the new, put back the old */ cap_free_caplist(newmycaps); val_free_value(newcaps); my_mgr_caps = oldmycaps; mgr_caps = oldcaps; } else { /* toss the old, install the new */ if (oldmycaps) { cap_free_caplist(oldmycaps); } if (oldcaps) { val_free_value(oldcaps); } my_mgr_caps = newmycaps; mgr_caps = newcaps; } return res; } /* mgr_cap_set_caps */ /******************************************************************** * FUNCTION mgr_cap_get_caps * * Get the NETCONF manager capabilities * * INPUTS: * none * RETURNS: * pointer to the manager caps list *********************************************************************/ cap_list_t * mgr_cap_get_caps (void) { return my_mgr_caps; } /* mgr_cap_get_caps */ /******************************************************************** * FUNCTION mgr_cap_get_capsval * * Get the NETCONF manager capabilities ain val_value_t format * * INPUTS: * none * RETURNS: * pointer to the manager caps list *********************************************************************/ val_value_t * mgr_cap_get_capsval (void) { return mgr_caps; } /* mgr_cap_get_capsval */ /******************************************************************** * FUNCTION mgr_cap_get_ses_capsval * * Get the NETCONF manager capabilities ain val_value_t format * for a specific session, v2 supports base1.0 and/or base1.1 * INPUTS: * scb == session control block to use * RETURNS: * MALLOCED pointer to the manager caps list to use * and then discard with val_free_value *********************************************************************/ val_value_t * mgr_cap_get_ses_capsval (ses_cb_t *scb) { val_value_t *newcaps; xmlns_id_t nc_id; status_t res; nc_id = xmlns_nc_id(); res = NO_ERR; /* get a new val_value_t cap list for manager messages */ newcaps = xml_val_new_struct(NCX_EL_CAPABILITIES, nc_id); if (newcaps == NULL) { return NULL; } /* add capability for NETCONF version 1.0 support */ if (ses_protocol_requested(scb, NCX_PROTO_NETCONF10)) { res = cap_add_stdval(newcaps, CAP_STDID_V1); } /* add capability for NETCONF version 1.1 support */ if (res == NO_ERR && ses_protocol_requested(scb, NCX_PROTO_NETCONF11)) { res = cap_add_stdval(newcaps, CAP_STDID_V11); } /* check the return value */ if (res != NO_ERR) { val_free_value(newcaps); newcaps = NULL; } return newcaps;; } /* mgr_cap_get_ses_capsval */ /* END file mgr_cap.c */ yuma123_2.14/netconf/src/mgr/mgr_signal.h0000664000175000017500000000575114770023131020436 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_mgr_signal #define _H_mgr_signal /* FILE: mgr_signal.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Handle interrupt signals for the manager ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 20-feb-07 abb Begun */ #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* don't rely on GNU extension being defined */ typedef void (*sighandler_t)(int signum); /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION mgr_signal_init * * Initialize the mgr_signal module * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ extern void mgr_signal_init (void); /******************************************************************** * FUNCTION mgr_signal_cleanup * * Cleanup the mgr_signal module. * *********************************************************************/ extern void mgr_signal_cleanup (void); /******************************************************************** * FUNCTION mgr_signal_handler * * Handle an incoming interrupt signal * * INPUTS: * intr == interrupt numer * *********************************************************************/ extern void mgr_signal_handler (int intr); /******************************************************************** * FUNCTION mgr_signal_install_break_handler (control-c) * * Install an application-specific handler for the break interrupt * * INPUTS: * handler == interrupt handler to call when control-C is entered * *********************************************************************/ extern void mgr_signal_install_break_handler (sighandler_t handler); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_mgr_signal */ yuma123_2.14/netconf/src/mgr/mgr_hello.c0000664000175000017500000003754414770023131020264 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: mgr_hello.c Handle the NETCONF (top-level) element. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 15jan07 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_cap #include "cap.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_mgr #include "mgr.h" #endif #ifndef _H_mgr_cap #include "mgr_cap.h" #endif #ifndef _H_mgr_hello #include "mgr_hello.h" #endif #ifndef _H_mgr_ses #include "mgr_ses.h" #endif #ifndef _H_mgr_val_parse #include "mgr_val_parse.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_op #include "op.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_top #include "top.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_wr #include "xml_wr.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define MGR_HELLO_DEBUG 1 #endif #define MGR_SERVER_HELLO_OBJ ((const xmlChar *)"server-hello") /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean mgr_hello_init_done = FALSE; /******************************************************************** * FUNCTION process_server_hello * * Process the NETCONF server contents * * 1) Protocol capabilities * 2) Module capabilities * 3) Unrecognized capabilities * * INPUTS: * scb == session control block to set * hello == value struct for the hello message to check * * OUTPUTS: * server caps in the scb->mgrcb is set * * RETURNS: * status *********************************************************************/ static status_t process_server_hello (ses_cb_t *scb, val_value_t *hello) { val_value_t *caps, *sidval, *cap; mgr_scb_t *mscb; boolean c1, c2; status_t res; mscb = mgr_ses_get_mscb(scb); /* make sure the capabilities element is present * This should not fail, since already parsed this far */ caps = val_find_child(hello, NC_MODULE, NCX_EL_CAPABILITIES); if (!caps || caps->res != NO_ERR) { log_error("\nError: no found in server "); return ERR_NCX_MISSING_VAL_INST; } /* make sure the session-id element is present * This should not fail, since already parsed this far */ sidval = val_find_child(hello, NC_MODULE, NCX_EL_SESSION_ID); if (!sidval || sidval->res != NO_ERR) { log_error("\nError: no found in server "); return ERR_NCX_MISSING_VAL_INST; } else { mscb->agtsid = VAL_UINT(sidval); } /* go through the capability nodes and construct a caplist */ for (cap = val_find_child(caps, NC_MODULE, NCX_EL_CAPABILITY); cap != NULL; cap = val_find_next_child(caps, NC_MODULE, NCX_EL_CAPABILITY, cap)) { if (cap->res != NO_ERR) { continue; } res = cap_add_std_string(&mscb->caplist, VAL_STR(cap)); if (res == ERR_NCX_SKIPPED) { res = cap_add_module_string(&mscb->caplist, VAL_STR(cap)); if (res == ERR_NCX_SKIPPED) { /* * if (ncx_warning_enabled(ERR_NCX_RCV_UNKNOWN_CAP)) { * log_warn("\nWarning: received unknown capability '%s'", * VAL_STR(cap)); * } */ if (LOGDEBUG2) { log_debug2("\nmgr: Got enterprise capability %s", VAL_STR(cap)); } /* hack: check for juniper 1.0 server * change the useprefix mode to TRUE to get * operations to work with this server */ if (!xml_strcmp(VAL_STR(cap), CAP_JUNOS)) { if (LOGDEBUG) { log_debug("\nUsing XML prefixes to work " "with Junos 1.0 server\n"); } ncx_set_useprefix(TRUE); } res = cap_add_ent(&mscb->caplist, VAL_STR(cap)); if (res != NO_ERR) { return res; } } } } /* check if the mandatory base protocol capability was set */ res = NO_ERR; c1 = cap_std_set(&mscb->caplist, CAP_STDID_V1); c2 = cap_std_set(&mscb->caplist, CAP_STDID_V11); if (c1 && c2) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: server supports " "base:1.0 and base:1.1"); } if (ses_protocol_requested(scb, NCX_PROTO_NETCONF11)) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: set protocol to base:1.1 " "for session '%d'", scb->sid); } ses_set_protocol(scb, NCX_PROTO_NETCONF11); } else if (ses_protocol_requested(scb, NCX_PROTO_NETCONF10)) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: set protocol to base:1.0 " "for session '%d'", scb->sid); } ses_set_protocol(scb, NCX_PROTO_NETCONF10); } else { log_error("\nError: Internal: no protocols requested, " "dropping session '%d'", scb->sid); res = ERR_NCX_MISSING_VAL_INST; } } else if (c1) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: server supports " "base:1.0 only"); } if (ses_protocol_requested(scb, NCX_PROTO_NETCONF10)) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: set protocol to base:1.0 " "for session '%d'", scb->sid); } ses_set_protocol(scb, NCX_PROTO_NETCONF10); } else { log_error("\nError: Server supports base:1.0 only;" "\n Protocol 'netconf1.0' not enabled, " "dropping session '%d'", scb->sid); res = ERR_NCX_MISSING_VAL_INST; } } else if (c2) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: server supports " "base:1.1 only"); } if (ses_protocol_requested(scb, NCX_PROTO_NETCONF11)) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: set protocol to base:1.1 " "for session '%d'", scb->sid); } ses_set_protocol(scb, NCX_PROTO_NETCONF11); } else { log_error("\nError: Server supports base:1.1 only;" "\n Protocol 'netconf1.1' not enabled, " "dropping session '%d'", scb->sid); res = ERR_NCX_MISSING_VAL_INST; } } else { log_error("\nError: no support for base:1.0 " "or base:1.1 found in server ;" "\n dropping session '%d'", scb->sid); return ERR_NCX_MISSING_VAL_INST; } /* set target type var in the manager session control block */ c1 = cap_std_set(&mscb->caplist, CAP_STDID_WRITE_RUNNING); c2 = cap_std_set(&mscb->caplist, CAP_STDID_CANDIDATE); if (c1 && c2) { mscb->targtyp = NCX_AGT_TARG_CAND_RUNNING; } else if (c1) { mscb->targtyp = NCX_AGT_TARG_RUNNING; } else if (c2) { mscb->targtyp = NCX_AGT_TARG_CANDIDATE; } else { mscb->targtyp = NCX_AGT_TARG_NONE; if (LOGINFO) { log_info("\nmgr_hello: no writable target found for" " session %u (a:%u)", scb->sid, mscb->agtsid); } } /* set the startup type in the mscb */ if (cap_std_set(&mscb->caplist, CAP_STDID_STARTUP)) { mscb->starttyp = NCX_AGT_START_DISTINCT; } else { mscb->starttyp = NCX_AGT_START_MIRROR; } return NO_ERR; } /* process_server_hello */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION mgr_hello_init * * Initialize the mgr_hello module * Adds the mgr_hello_dispatch function as the handler * for the NETCONF top-level element. * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ status_t mgr_hello_init (void) { status_t res; if (!mgr_hello_init_done) { res = top_register_node(NC_MODULE, NCX_EL_HELLO, mgr_hello_dispatch); if (res != NO_ERR) { return res; } mgr_hello_init_done = TRUE; } return NO_ERR; } /* mgr_hello_init */ /******************************************************************** * FUNCTION mgr_hello_cleanup * * Cleanup the mgr_hello module. * Unregister the top-level NETCONF element * *********************************************************************/ void mgr_hello_cleanup (void) { if (mgr_hello_init_done) { top_unregister_node(NC_MODULE, NCX_EL_HELLO); mgr_hello_init_done = FALSE; } } /* mgr_hello_cleanup */ /******************************************************************** * FUNCTION mgr_hello_dispatch * * Handle an incoming message from the client * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ void mgr_hello_dispatch (ses_cb_t *scb, xml_node_t *top) { val_value_t *val; ncx_module_t *mod; obj_template_t *obj; mgr_scb_t *mscb; xml_msg_hdr_t msg; status_t res; #ifdef DEBUG if (!scb || !top) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif #ifdef MGR_HELLO_DEBUG if (LOGDEBUG) { log_debug("\nmgr_hello got node"); } if (LOGDEBUG2) { xml_dump_node(top); } #endif mscb = mgr_ses_get_mscb(scb); /* only process this message in hello wait state */ if (scb->state != SES_ST_HELLO_WAIT) { /* TBD: stats update */ if (LOGINFO) { log_info("\nmgr_hello dropped, wrong state for session %d", scb->sid); } return; } /* init local vars */ res = NO_ERR; val = NULL; obj = NULL; xml_msg_init_hdr(&msg); /* get a value struct to hold the server hello msg */ val = val_new_value(); if (!val) { res = ERR_INTERNAL_MEM; } /* get the type definition from the registry */ if (res == NO_ERR) { mod = ncx_find_module(NC_MODULE, NULL); if (mod) { obj = ncx_find_object(mod, MGR_SERVER_HELLO_OBJ); } if (!obj) { /* netconf module should have loaded this definition */ res = SET_ERROR(ERR_INTERNAL_PTR); } } /* parse an server hello message */ if (res == NO_ERR) { res = mgr_val_parse(scb, obj, top, val); } /* examine the server capability list * and it matches the server protocol version */ if (res == NO_ERR) { res = process_server_hello(scb, val); } /* report first error and close session */ if (res != NO_ERR) { if (LOGINFO) { log_info("\nmgr_connect error (%s)\n dropping session %u (a:%u)", get_error_string(res), scb->sid, mscb->agtsid, res); } } else { scb->state = SES_ST_IDLE; if (LOGDEBUG) { log_debug("\nmgr_hello manager hello ok"); } } if (val) { val_free_value(val); } } /* mgr_hello_dispatch */ /******************************************************************** * FUNCTION mgr_hello_send * * Send the manager message to the server on the * specified session * * INPUTS: * scb == session control block * * RETURNS: * status *********************************************************************/ status_t mgr_hello_send (ses_cb_t *scb) { val_value_t *mycaps; xml_msg_hdr_t msg; status_t res; xml_attrs_t attrs; boolean anyout; xmlns_id_t nc_id; #ifdef DEBUG if (!scb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif #ifdef MGR_HELLO_DEBUG if (LOGDEBUG2) { log_debug2("\nmgr sending hello on session %d", scb->sid); } #endif res = NO_ERR; anyout = FALSE; xml_msg_init_hdr(&msg); xml_init_attrs(&attrs); nc_id = xmlns_nc_id(); /* get my client caps, custom made for this session */ mycaps = mgr_cap_get_ses_capsval(scb); if (!mycaps) { res = SET_ERROR(ERR_INTERNAL_PTR); } /* setup the prefix map with the NETCONF namespace */ if (res == NO_ERR) { res = xml_msg_build_prefix_map(&msg, &attrs, TRUE, FALSE); } /* send the directive */ if (res == NO_ERR) { res = ses_start_msg(scb); } /* start the hello element */ if (res == NO_ERR) { anyout = TRUE; xml_wr_begin_elem_ex(scb, &msg, 0, nc_id, NCX_EL_HELLO, &attrs, ATTRQ, 0, START); } /* send the capabilities list */ if (res == NO_ERR) { xml_wr_full_val(scb, &msg, mycaps, NCX_DEF_INDENT); } /* finish the hello element */ if (res == NO_ERR) { xml_wr_end_elem(scb, &msg, nc_id, NCX_EL_HELLO, 0); } /* finish the message */ if (anyout) { ses_finish_msg(scb); } xml_clean_attrs(&attrs); xml_msg_clean_hdr(&msg); if (mycaps != NULL) { val_free_value(mycaps); } return res; } /* mgr_hello_send */ /* END file mgr_hello.c */ yuma123_2.14/netconf/src/mgr/mgr_xml.c0000664000175000017500000001476314770023131017757 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: mgr_xml.c Manager XML Reader interface ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11feb07 abb begun; split from xml_util.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_mgr_xml #include "mgr_xml.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /************** E X T E R N A L F U N C T I O N S **************/ /******************************************************************** * FUNCTION mgr_xml_consume_node * * Parse the next node and return its namespace, type and name * The xml_init_node or xml_clean_node API must be called before * this function for the node parameter * * There are 2 types of XML element start nodes * - empty node (XML_NT_EMPTY) * - start of a simple or complex type (XML_NT_START) * * There is one string content node for simpleType content * - string node (XML_NT_STRING) * * There is one end node to end both simple and complex types * - end node (XML_NT_END) * * If nodetyp==XML_NT_EMPTY, then no further nodes will occur * for this element. This node may contain attributes. The * naming parameters will all be set. * * If nodetyp==XML_NT_START, then the caller should examine * the schema for that start node. * For complex types, the next node is probably another XML_NT_START. * For simple types, the next node will be XML_NT_STRING, * followed by an XML_NT_END node. This node may contain attributes. * The naming parameters will all be set. * * If the nodetype==XML_NT_STRING, then the simval and simlen * fields will be set. There are no attributes or naming parameters * for this node type. * * IF the nodetype==XML_NT_END, then no further nodes for this element * will occur. This node should not contain attributes. * All of the naming parameters will be set. The xml_endnode_match * function should be used to confirm that the XML_NT_START and * XML_NT_END nodes are paired correctly. * * The node pointer for the reader will be advanced before the * node is read. * * INPUTS: * reader == XmlReader already initialized from File, Memory, * or whatever * node == pointer to an initialized xml_node_t struct * to be filled in * * OUTPUTS: * *node == xml_node_t struct filled in * reader will be advanced * * RETURNS: * status of the operation * Try to fail on fatal errors only *********************************************************************/ status_t mgr_xml_consume_node (xmlTextReaderPtr reader, xml_node_t *node) { return xml_consume_node(reader, node, TRUE, TRUE); } /* mgr_xml_consume_node */ status_t mgr_xml_consume_node_nons (xmlTextReaderPtr reader, xml_node_t *node) { return xml_consume_node(reader, node, FALSE, TRUE); } /* mgr_xml_consume_node_nons */ status_t mgr_xml_consume_node_noadv (xmlTextReaderPtr reader, xml_node_t *node) { return xml_consume_node(reader, node, TRUE, FALSE); } /* mgr_xml_consume_node_noadv */ /******************************************************************** * FUNCTION mgr_xml_skip_subtree * * Already encountered an error, so advance nodes until the * matching start-node is reached or a terminating error occurs * - end of input * - start depth level reached * * INPUTS: * reader == XmlReader already initialized from File, Memory, * or whatever * startnode == xml_node_t of the start node of the sub-tree to skip * RETURNS: * status of the operation * SIDE EFFECTS: * the xmlreader state is advanced until the current node is the * end node of the specified start node or a fatal error occurs *********************************************************************/ status_t mgr_xml_skip_subtree (xmlTextReaderPtr reader, const xml_node_t *startnode) { return xml_skip_subtree(reader,startnode); } /* mgr_xml_skip_subtree */ /* END mgr_xml.c */ yuma123_2.14/netconf/src/mgr/mgr_not.h0000664000175000017500000001416714770023131017762 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_mgr_not #define _H_mgr_not /* FILE: mgr_not.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF protocol notification manager-side definitions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 03-jun-09 abb Begun; */ #include #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tstamp #include "tstamp.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* struct to save and process an incoming notification */ typedef struct mgr_not_msg_t_ { dlq_hdr_t qhdr; val_value_t *notification; /* parsed message */ val_value_t *eventTime; /* ptr into notification */ val_value_t *eventType; /* ptr into notification */ status_t res; /* parse result */ } mgr_not_msg_t; /* manager notification callback function * * INPUTS: * scb == session control block for session that got the reply * msg == incoming notification msg * consumed == address of return message consumed flag * * OUTPUTS: * *consumed == TRUE if msg has been consumed so * it will not be freed by mgr_not_dispatch * == FALSE if msg has been not consumed so * it will be freed by mgr_not_dispatch */ typedef void (*mgr_not_cbfn_t) (ses_cb_t *scb, mgr_not_msg_t *msg, boolean *consumed); /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION mgr_not_init * * Initialize the mgr_not module * call once to init module * Adds the mgr_not_dispatch function as the handler * for the NETCONF top-level element. * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ extern status_t mgr_not_init (void); /******************************************************************** * FUNCTION mgr_not_cleanup * * Cleanup the mgr_not module. * call once to cleanup module * Unregister the top-level NETCONF element * *********************************************************************/ extern void mgr_not_cleanup (void); /******************************************************************** * FUNCTION mgr_not_free_msg * * Free a mgr_not_msg_t struct * * INPUTS: * msg == struct to free * * RETURNS: * none *********************************************************************/ extern void mgr_not_free_msg (mgr_not_msg_t *msg); /******************************************************************** * FUNCTION mgr_not_clean_msgQ * * Clean the msg Q of mgr_not_msg_t entries * * INPUTS: * msgQ == Q of entries to free; the Q itself is not freed * * RETURNS: * none *********************************************************************/ extern void mgr_not_clean_msgQ (dlq_hdr_t *msgQ); /******************************************************************** * FUNCTION mgr_not_dispatch * * Dispatch an incoming response * handle the element * called by mgr_top.c: * This function is registered with top_register_node * for the module 'notification', top-node 'notification' * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ extern void mgr_not_dispatch (ses_cb_t *scb, xml_node_t *top); /******************************************************************** * FUNCTION mgr_not_set_callback_fn * * Set the application callback function to handle * notifications when they arrive * * INPUTS: * cbfn == callback function to use * == NULL to clear the callback function *********************************************************************/ extern void mgr_not_set_callback_fn (mgr_not_cbfn_t cbfn); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_mgr_not */ yuma123_2.14/netconf/src/mgr/mgr_io.c0000664000175000017500000004122414770023131017556 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: mgr_io.c 1) call mgr_io_init 2) call mgr_io_set_stdin_handler 3a) When a session socket has been created, call mgr_io_activate_session. 3b) When a session socket is being closed, call mgr_io_deactivate_session. 4) call mgr_io_run to loop until program exits Step 2 or 3 can occur N times, and be changed while mgr_io_run is active (i.e., via stdin_handler) ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 18-feb-07 abb begun; start from agt_ncxserver.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_mgr #include "mgr.h" #endif #ifndef _H_mgr_io #include "mgr_io.h" #endif #ifndef _H_mgr_ses #include "mgr_ses.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_ses_msg #include "ses_msg.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG /* #define MGR_IO_DEBUG 1 */ #endif /******************************************************************** * * * V A R I A B L E S * * * ********************************************************************/ static mgr_io_stdin_fn_t stdin_handler; static fd_set active_fd_set, read_fd_set, write_fd_set; static int maxwrnum; static int maxrdnum; /******************************************************************** * FUNCTION any_fd_set * * Check if any bits are set in the fd_set * INPUTS: * fs == fs_set to check * maxfd == max FD number possibly in use * * RETURNS: * TRUE if any bits set * FALSE if no bits set *********************************************************************/ static boolean any_fd_set (fd_set *fd, int maxfd) { int i; for (i=0; i<=maxfd; i++) { if (FD_ISSET(i, fd)) { return TRUE; } } return FALSE; } /* any_fd_set */ /******************************************************************** * FUNCTION write_sessions * * Go through any sessions ready to write and send * the buffers ready to send * *********************************************************************/ static void write_sessions (void) { ses_cb_t *scb; mgr_scb_t *mscb; status_t res; scb = mgr_ses_get_first_outready(); while (scb) { res = ses_msg_send_buffs(scb); if (res != NO_ERR) { if (LOGINFO) { mscb = mgr_ses_get_mscb(scb); log_info("\nmgr_io write failed; " "closing session %u (a:%u)", scb->sid, mscb->agtsid); } mgr_ses_free_session(scb->sid); scb = NULL; } /* check if any buffers left over for next loop */ if (scb && !dlq_empty(&scb->outQ)) { ses_msg_make_outready(scb); } scb = mgr_ses_get_first_outready(); } } /* write_sessions */ /******************************************************************** * FUNCTION read_session * * * *********************************************************************/ static boolean read_session (int fd, ses_id_t cursid) { ses_cb_t *scb; mgr_scb_t *mscb; status_t res; boolean retval; retval = TRUE; if (LOGDEBUG3) { log_debug3("\nmgr_io: read FD %d for session %u start", fd, cursid); } /* Data arriving on an already-connected socket. * Need to have the xmlreader for this session * unless it is input from STDIO */ scb = def_reg_find_scb(fd); if (scb) { mscb = mgr_ses_get_mscb(scb); if (!mscb) { SET_ERROR(ERR_INTERNAL_VAL); } else { res = ses_accept_input(scb); if (res == NO_ERR) { if (mscb->returncode == LIBSSH2_ERROR_EAGAIN) { /* this is assumed to be the openssh * keepalive message, requesting * some data be sent on the channel */ } } else { /* accept input failed because session died */ if (scb->sid == cursid) { retval = FALSE; } if (res != ERR_NCX_SESSION_CLOSED) { if (LOGDEBUG) { log_debug("\nmgr_io: input failed" " for session %u (a:%u) tr-err:%d (%s)", scb->sid, mscb->agtsid, mscb->returncode, get_error_string(res)); } } mgr_ses_free_session(scb->sid); FD_CLR(fd, &active_fd_set); if (fd >= maxrdnum) { maxrdnum = fd-1; } } } } return retval; } /* read_session */ /******************************************************************** * FUNCTION io_process * * mini server loop while waiting for KBD input * * INPUTS: * cursid == current session ID to check * wantdata == address of return wantdata flag * anystdout == address of return anystdout flag * * OUTPUTS: * *wantdata == TRUE if the agent has sent a keepalive * and is expecting a request * == FALSE if no keepalive received this time * *anystdout == TRUE if maybe STDOUT was written * FALSE if definately no STDOUT written * * RETURNS: * TRUE if session alive or not confirmed * FALSE if cursid confirmed dropped *********************************************************************/ static boolean io_process (ses_id_t cursid, boolean *wantdata, boolean *anystdout) { struct timeval timeout; int i, ret; boolean done, retval, anyread; #ifdef DEBUG if (wantdata == NULL || anystdout == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return TRUE; } #endif /* !!! the client keepalive polling is not implemented !!!! */ *wantdata = FALSE; *anystdout = FALSE; write_sessions(); /* setup select parameters */ anyread = FALSE; timeout.tv_sec = 0; timeout.tv_usec = 100; read_fd_set = active_fd_set; if (!any_fd_set(&read_fd_set, maxrdnum)) { return TRUE; } /* go through the file descriptor numbers and * service the corresponding libssh2 channels with input buffers pending */ for (i = 0; i <= maxrdnum; i++) { /* check read input from agent */ if (FD_ISSET(i, &read_fd_set)) { ret = read_session(i,0); if(ret!=0) { /* drain the ready queue before accepting new input */ while (mgr_ses_process_first_ready()); write_sessions(); } } } /* Block until input arrives on one or more active sockets. * or the short timer expires */ ret = select(maxrdnum+1, &read_fd_set, &write_fd_set, NULL, &timeout); if (ret > 0) { /* normal return with some bytes */ anyread = TRUE; } else if (ret < 0) { /* some error, don't care about EAGAIN here */ return TRUE; } else { /* == 0: timeout */ return TRUE; } retval = TRUE; /* loop: go through the file descriptor numbers and * service all the sockets with input pending */ if (anyread) { for (i = 0; i <= maxrdnum; i++) { /* check read input from agent */ if (FD_ISSET(i, &read_fd_set)) { retval = read_session(i, cursid); } } } /* drain the ready queue before accepting new input */ if (anyread) { done = FALSE; while (!done) { if (!mgr_ses_process_first_ready()) { /* did not write to any session */ anyread = FALSE; done = TRUE; } else if (mgr_shutdown_requested()) { done = TRUE; } } write_sessions(); } *anystdout = anyread; return retval; } /* io_process */ /************** E X T E R N A L F U N C T I O N S *************/ /******************************************************************** * FUNCTION mgr_io_init * * Init the IO server loop vars for the ncx manager * Must call this function before any other function can * be used from this module *********************************************************************/ void mgr_io_init (void) { stdin_handler = NULL; FD_ZERO(&write_fd_set); FD_ZERO(&active_fd_set); maxwrnum = 0; maxrdnum = 0; } /* mgr_io_init */ /******************************************************************** * FUNCTION mgr_io_set_stdin_handler * * Set the callback function for STDIN processing * * INPUTS: * handler == address of the STDIN handler function *********************************************************************/ void mgr_io_set_stdin_handler (mgr_io_stdin_fn_t handler) { stdin_handler = handler; } /* mgr_io_set_stdin_handler */ /******************************************************************** * FUNCTION mgr_io_activate_session * * Tell the IO manager to start listening on the specified socket * * INPUTS: * fd == file descriptor number of the socket *********************************************************************/ void mgr_io_activate_session (int fd) { FD_SET(fd, &active_fd_set); /* if (fd+1 > maxwrnum) { maxwrnum = fd+1; } */ if (fd+1 > maxrdnum) { maxrdnum = fd+1; } } /* mgr_io_activate_session */ /******************************************************************** * FUNCTION mgr_io_deactivate_session * * Tell the IO manager to stop listening on the specified socket * * INPUTS: * fd == file descriptor number of the socket *********************************************************************/ void mgr_io_deactivate_session (int fd) { FD_CLR(fd, &active_fd_set); /* if (fd+1 == maxwrnum) { maxwrnum--; } */ if (fd+1 == maxrdnum) { maxrdnum--; } } /* mgr_io_deactivate_session */ /******************************************************************** * FUNCTION mgr_io_run * * IO server loop for the ncx manager * * RETURNS: * status *********************************************************************/ status_t mgr_io_run (void) { struct timeval timeout; int i, ret; boolean done, done2; mgr_io_state_t state; state = MGR_IO_ST_INIT; /* first loop, handle user IO and get some data to read or write */ done = FALSE; while (!done) { /* check exit program */ if (mgr_shutdown_requested()) { done = TRUE; continue; } done2 = FALSE; ret = 0; while (!done2) { /* will block in idle states waiting for user KBD input * while no command is active */ if (stdin_handler != NULL) { state = (*stdin_handler)(); } /* check exit program or session */ if (mgr_shutdown_requested()) { done = done2 = TRUE; continue; } else if (state == MGR_IO_ST_CONN_SHUT) { done2 = TRUE; continue; } else if (state == MGR_IO_ST_SHUT) { done = done2 = TRUE; continue; } switch (state) { case MGR_IO_ST_INIT: case MGR_IO_ST_IDLE: /* user didn't do anything at the KBD * skip the IO loop and try again */ continue; case MGR_IO_ST_CONNECT: case MGR_IO_ST_CONN_START: case MGR_IO_ST_CONN_IDLE: case MGR_IO_ST_CONN_RPYWAIT: case MGR_IO_ST_CONN_CANCELWAIT: case MGR_IO_ST_CONN_CLOSEWAIT: /* input could be expected */ break; case MGR_IO_ST_CONN_SHUT: /* session shutdown in progress */ done2 = TRUE; continue; case MGR_IO_ST_SHUT: /* shutdown in progress */ done = done2 = TRUE; continue; default: SET_ERROR(ERR_INTERNAL_VAL); done = done2 = TRUE; } /* check error exit from this loop */ if (done2) { continue; } { boolean wantdata = FALSE; boolean anystdout = FALSE; boolean retval; retval = io_process(0, &wantdata, &anystdout); if(!retval) { done = TRUE; } } } /* check exit program */ if (mgr_shutdown_requested()) { done = TRUE; continue; } /* check select return status for non-recoverable error */ if (ret < 0) { log_error("\nmgr_io select failed (%s)", strerror(errno)); mgr_request_shutdown(); done = TRUE; continue; } } /* end outer loop */ /* all open client sockets will be closed as the sessions are * torn down, but the original ncxserver socket needs to be closed now */ return NO_ERR; } /* mgr_io_run */ /******************************************************************** * FUNCTION mgr_io_process_timeout * * mini server loop while waiting for KBD input * * INPUTS: * cursid == current session ID to check * wantdata == address of return wantdata flag * anystdout == address of return anystdout flag * * OUTPUTS: * *wantdata == TRUE if the agent has sent a keepalive * and is expecting a request * == FALSE if no keepalive received this time * *anystdout == TRUE if maybe STDOUT was written * FALSE if definately no STDOUT written * * RETURNS: * TRUE if session alive or not confirmed * FALSE if cursid confirmed dropped *********************************************************************/ boolean mgr_io_process_timeout (ses_id_t cursid, boolean *wantdata, boolean *anystdout) { return io_process(cursid, wantdata, anystdout); } /* mgr_io_process_timeout */ /* END mgr_io.c */ yuma123_2.14/netconf/src/mgr/mgr_hello.h0000664000175000017500000001021214770023131020250 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_mgr_hello #define _H_mgr_hello /* FILE: mgr_hello.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF protocol hello message ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17-may-05 abb Begun. */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define NC_HELLO_STR "hello" #define NC_SESSION_ID "session-id" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION mgr_hello_init * * Initialize the mgr_hello module * Adds the mgr_hello_dispatch function as the handler * for the NETCONF top-level element. * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ extern status_t mgr_hello_init (void); /******************************************************************** * FUNCTION mgr_hello_cleanup * * Cleanup the mgr_hello module. * Unregister the top-level NETCONF element * *********************************************************************/ extern void mgr_hello_cleanup (void); /******************************************************************** * FUNCTION mgr_hello_dispatch * * Handle an incoming message from the client * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ extern void mgr_hello_dispatch (ses_cb_t *scb, xml_node_t *top); /******************************************************************** * FUNCTION mgr_hello_send * * Send the manager message to the agent on the * specified session * * INPUTS: * scb == session control block * * RETURNS: * status *********************************************************************/ extern status_t mgr_hello_send (ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_mgr_hello */ yuma123_2.14/netconf/src/mgr/Makefile.am0000664000175000017500000000157414770023131020176 0ustar vladimirvladimirlib_LTLIBRARIES = libyumamgr.la libyumamgr_la_SOURCES = \ $(top_srcdir)/netconf/src/mgr/mgr.c \ $(top_srcdir)/netconf/src/mgr/mgr_cap.c \ $(top_srcdir)/netconf/src/mgr/mgr_hello.c \ $(top_srcdir)/netconf/src/mgr/mgr_io.c \ $(top_srcdir)/netconf/src/mgr/mgr_load.c \ $(top_srcdir)/netconf/src/mgr/mgr_not.c \ $(top_srcdir)/netconf/src/mgr/mgr_rpc.c \ $(top_srcdir)/netconf/src/mgr/mgr_ses.c \ $(top_srcdir)/netconf/src/mgr/mgr_signal.c \ $(top_srcdir)/netconf/src/mgr/mgr_top.c \ $(top_srcdir)/netconf/src/mgr/mgr_val_parse.c \ $(top_srcdir)/netconf/src/mgr/mgr_xml.c libyumamgr_la_CPPFLAGS = -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump $(XML_CPPFLAGS) libyumamgr_la_LDFLAGS = -version-info 2:0:0 $(top_builddir)/netconf/src/ncx/libyumancx.la $(XML_LIBS) -lrt -lssh2 yuma123_2.14/netconf/src/mgr/mgr_rpc.c0000664000175000017500000004532414770023131017740 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: mgr_rpc.c NETCONF Protocol Operations: RPC Manager Side Support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17may05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_mgr #include "mgr.h" #endif #ifndef _H_mgr_rpc #include "mgr_rpc.h" #endif #ifndef _H_mgr_ses #include "mgr_ses.h" #endif #ifndef _H_mgr_val_parse #include "mgr_val_parse.h" #endif #ifndef _H_mgr_xml #include "mgr_xml.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_num #include "ncx_num.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_rpc_err #include "rpc_err.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_top #include "top.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_msg #include "xml_msg.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_wr #include "xml_wr.h" #endif #include "uptime.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define MGR_RPC_DEBUG 1 #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean mgr_rpc_init_done = FALSE; /******************************************************************** * FUNCTION new_reply * * Malloc and initialize a new mgr_rpc_rpy_t struct * * INPUTS: * none * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ static mgr_rpc_rpy_t * new_reply (void) { mgr_rpc_rpy_t *rpy; rpy = m__getObj(mgr_rpc_rpy_t); if (!rpy) { return NULL; } memset(rpy, 0x0, sizeof(mgr_rpc_rpy_t)); rpy->reply = val_new_value(); if (!rpy->reply) { m__free(rpy); return NULL; } /* xml_msg_init_hdr(&rpy->mhdr); */ return rpy; } /* new_reply */ /******************************************************************** * FUNCTION add_request * * Add an RPC request to the Q * * INPUTS: * scb == session control block * req == mgr_rpc_req_t to add * * RETURNS: * none *********************************************************************/ static void add_request (ses_cb_t *scb, mgr_rpc_req_t *req) { mgr_scb_t *mscb; mscb = mgr_ses_get_mscb(scb); (void)uptime(&req->starttime); dlq_enque(req, &mscb->reqQ); } /* add_request */ /******************************************************************** * FUNCTION find_request * * Find an RPC request but do not remove it * * * The NETCONF protocol actually allows duplicates in * the message-id attribute, but if a message is cancelled * then only the first match is deleted * * INPUTS: * scb == session control block * msg_id == ID for the mgr_rpc_req_t to find * * RETURNS: * pointer to the request or NULL if not found *********************************************************************/ static mgr_rpc_req_t * find_request (ses_cb_t *scb, const xmlChar *msg_id) { mgr_scb_t *mscb; mgr_rpc_req_t *req; mscb = mgr_ses_get_mscb(scb); for (req = (mgr_rpc_req_t *)dlq_firstEntry(&mscb->reqQ); req != NULL; req = (mgr_rpc_req_t *)dlq_nextEntry(req)) { if (!xml_strcmp(msg_id, req->msg_id)) { return req; } } return NULL; } /* find_request */ /************** E X T E R N A L F U N C T I O N S ***************/ /******************************************************************** * FUNCTION mgr_rpc_init * * Initialize the mgr_rpc module * call once to init RPC mgr module * Adds the mgr_rpc_dispatch function as the handler * for the NETCONF top-level element. * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ status_t mgr_rpc_init (void) { status_t res; if (!mgr_rpc_init_done) { res = top_register_node(NC_MODULE, NCX_EL_RPC_REPLY, mgr_rpc_dispatch); if (res != NO_ERR) { return res; } mgr_rpc_init_done = TRUE; } return NO_ERR; } /* mgr_rpc_init */ /******************************************************************** * FUNCTION mgr_rpc_cleanup * * Cleanup the mgr_rpc module. * call once to cleanup RPC mgr module * Unregister the top-level NETCONF element * *********************************************************************/ void mgr_rpc_cleanup (void) { if (mgr_rpc_init_done) { top_unregister_node(NC_MODULE, NCX_EL_RPC_REPLY); mgr_rpc_init_done = FALSE; } } /* mgr_rpc_cleanup */ /******************************************************************** * FUNCTION mgr_rpc_new_request * * Malloc and initialize a new mgr_rpc_req_t struct * * INPUTS: * scb == session control block * * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ mgr_rpc_req_t * mgr_rpc_new_request (ses_cb_t *scb) { mgr_scb_t *mscb; mgr_rpc_req_t *req; char numbuff[NCX_MAX_NUMLEN]; req = m__getObj(mgr_rpc_req_t); if (!req) { return NULL; } memset(req, 0x0, sizeof(mgr_rpc_req_t)); mscb = mgr_ses_get_mscb(scb); sprintf(numbuff, "%u", mscb->next_id); if (mscb->next_id >= MGR_MAX_REQUEST_ID) { mscb->next_id = 0; } else { mscb->next_id++; } req->msg_id = xml_strdup((const xmlChar *)numbuff); if (req->msg_id) { xml_msg_init_hdr(&req->mhdr); xml_init_attrs(&req->attrs); } else { m__free(req); req = NULL; } return req; } /* mgr_rpc_new_request */ /******************************************************************** * FUNCTION mgr_rpc_free_request * * Free a mgr_rpc_req_t struct * * INPUTS: * req == struct to free * * RETURNS: * none *********************************************************************/ void mgr_rpc_free_request (mgr_rpc_req_t *req) { #ifdef DEBUG if (!req) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (req->msg_id) { m__free(req->msg_id); } xml_clean_attrs(&req->attrs); if (req->data) { val_free_value(req->data); } m__free(req); } /* mgr_rpc_free_request */ /******************************************************************** * FUNCTION mgr_rpc_free_reply * * Free a mgr_rpc_rpy_t struct * * INPUTS: * rpy == struct to free * * RETURNS: * none *********************************************************************/ void mgr_rpc_free_reply (mgr_rpc_rpy_t *rpy) { #ifdef DEBUG if (!rpy) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (rpy->msg_id) { m__free(rpy->msg_id); } if (rpy->reply) { val_free_value(rpy->reply); } m__free(rpy); } /* mgr_rpc_free_reply */ /******************************************************************** * FUNCTION mgr_rpc_clean_requestQ * * Clean the request Q of mgr_rpc_req_t entries * * INPUTS: * reqQ == Q of entries to free; the Q itself is not freed * * RETURNS: * none *********************************************************************/ void mgr_rpc_clean_requestQ (dlq_hdr_t *reqQ) { mgr_rpc_req_t *req; #ifdef DEBUG if (!reqQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif req = (mgr_rpc_req_t *)dlq_deque(reqQ); while (req) { mgr_rpc_free_request(req); req = (mgr_rpc_req_t *)dlq_deque(reqQ); } } /* mgr_rpc_clean_requestQ */ /******************************************************************** * FUNCTION mgr_rpc_timeout_requestQ * * Clean the request Q of mgr_rpc_req_t entries * Only remove the entries that have timed out * * returning number of msgs timed out * need a callback-based cleanup later on * to support N concurrent requests per agent * * INPUTS: * reqQ == Q of entries to check * * RETURNS: * number of request timed out *********************************************************************/ uint32 mgr_rpc_timeout_requestQ (dlq_hdr_t *reqQ) { mgr_rpc_req_t *req, *nextreq; time_t timenow; double timediff; uint32 deletecount; #ifdef DEBUG if (!reqQ) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif deletecount = 0; (void)uptime(&timenow); for (req = (mgr_rpc_req_t *)dlq_firstEntry(reqQ); req != NULL; req = nextreq) { nextreq = (mgr_rpc_req_t *)dlq_nextEntry(req); if (!req->timeout) { continue; } timediff = difftime(timenow, req->starttime); if (timediff >= (double)req->timeout) { log_info("\nmgr_rpc: deleting timed out request '%s'", req->msg_id); deletecount++; dlq_remove(req); mgr_rpc_free_request(req); } } return deletecount; } /* mgr_rpc_timeout_requestQ */ /******************************************************************** * FUNCTION mgr_rpc_send_request * * Send an request to the agent on the specified session * non-blocking send, reply function will be called when * one is received or a timeout occurs * * INPUTS: * scb == session control block * req == request to send * rpyfn == reply callback function * * RETURNS: * status *********************************************************************/ status_t mgr_rpc_send_request (ses_cb_t *scb, mgr_rpc_req_t *req, mgr_rpc_cbfn_t rpyfn) { xml_msg_hdr_t msg; xml_attr_t *attr; status_t res; boolean anyout; xmlns_id_t nc_id; #ifdef DEBUG if (!scb || !req || !rpyfn) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif #ifdef MGR_HELLO_DEBUG log_debug2("\nmgr sending RPC request %s on session %d", req->msg_id, scb->sid); #endif anyout = FALSE; xml_msg_init_hdr(&msg); nc_id = xmlns_nc_id(); /* make sure the message-id attribute is not already present */ attr = xml_find_attr_q(&req->attrs, 0, NCX_EL_MESSAGE_ID); if (attr) { dlq_remove(attr); xml_free_attr(attr); } /* setup the prefix map with the NETCONF (and maybe NCX) namespace */ res = xml_msg_build_prefix_map(&msg, &req->attrs, FALSE, (req->data->nsid == xmlns_ncx_id())); /* add the message-id attribute */ if (res == NO_ERR) { res = xml_add_attr(&req->attrs, 0, NCX_EL_MESSAGE_ID, req->msg_id); } /* set perf timestamp in case response timing active */ gettimeofday(&req->perfstarttime, NULL); /* send the directive */ if (res == NO_ERR) { res = ses_start_msg(scb); } /* start the element */ if (res == NO_ERR) { anyout = TRUE; xml_wr_begin_elem_ex(scb, &msg, 0, nc_id, NCX_EL_RPC, &req->attrs, ATTRQ, 0, START); } /* send the method and parameters */ if (res == NO_ERR) { xml_wr_full_val(scb, &msg, req->data, NCX_DEF_INDENT); } /* finish the element */ if (res == NO_ERR) { xml_wr_end_elem(scb, &msg, nc_id, NCX_EL_RPC, 0); } /* finish the message */ if (anyout) { ses_finish_msg(scb); } if (res == NO_ERR) { req->replycb = rpyfn; add_request(scb, req); } xml_msg_clean_hdr(&msg); return res; } /* mgr_rpc_send_request */ /******************************************************************** * FUNCTION mgr_rpc_dispatch * * Dispatch an incoming response * handle the element * called by mgr_top.c: * This function is registered with top_register_node * for the module 'netconf', top-node 'rpc-reply' * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ void mgr_rpc_dispatch (ses_cb_t *scb, xml_node_t *top) { obj_template_t *rpyobj; mgr_rpc_rpy_t *rpy; mgr_rpc_req_t *req; xml_attr_t *attr; xmlChar *msg_id; ncx_module_t *mod; mgr_rpc_cbfn_t handler; ncx_num_t num; status_t res; #ifdef DEBUG if (!scb || !top) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* init local vars */ res = NO_ERR; msg_id = NULL; req = NULL; /* make sure any real session has been properly established */ if (scb->type != SES_TYP_DUMMY && scb->state != SES_ST_IDLE) { log_error("\nError: mgr_rpc: skipping incoming message '%s'", top->qname); mgr_xml_skip_subtree(scb->reader, top); return; } /* check if the reply template is already cached */ rpyobj = NULL; mod = ncx_find_module(NC_MODULE, NULL); if (mod != NULL) { rpyobj = ncx_find_object(mod, NC_RPC_REPLY_TYPE); } if (rpyobj == NULL) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); mgr_xml_skip_subtree(scb->reader, top); return; } /* get the NC RPC message-id attribute; should be present * because the send-rpc function put a message-id in */ attr = xml_find_attr(top, 0, NCX_EL_MESSAGE_ID); if (attr && attr->attr_val) { msg_id = xml_strdup(attr->attr_val); } if (msg_id == NULL) { mgr_xml_skip_subtree(scb->reader, top); log_info("\nmgr_rpc: incoming message with no message-id"); return; } /* the current node is 'rpc-reply' in the netconf namespace * First get a new RPC reply struct */ rpy = new_reply(); if (rpy == NULL) { m__free(msg_id); log_error("\nError: mgr_rpc: skipping incoming message"); mgr_xml_skip_subtree(scb->reader, top); return; } else { rpy->msg_id = msg_id; } /* get the NCX RPC group-id attribute if present */ attr = xml_find_attr(top, xmlns_ncx_id(), NCX_EL_GROUP_ID); if (attr && attr->attr_val) { res = ncx_decode_num(attr->attr_val, NCX_BT_UINT32, &num); if (res == NO_ERR) { rpy->group_id = num.u; } } /* find the request that goes with this reply */ if (rpy->msg_id != NULL) { req = find_request(scb, rpy->msg_id); if (req == NULL) { #ifdef MGR_RPC_DEBUG log_debug("\nmgr_rpc: got request found for msg (%s) " "on session %d", rpy->msg_id, scb->sid); #endif mgr_xml_skip_subtree(scb->reader, top); mgr_rpc_free_reply(rpy); return; } else { dlq_remove(req); } } /* have a request/reply pair, so parse the reply * as a val_value_t tree, stored in rpy->reply */ rpy->res = mgr_val_parse_reply(scb, rpyobj, (req != NULL) ? req->rpc : ncx_get_gen_anyxml(), top, rpy->reply); if (rpy->res != NO_ERR && LOGINFO) { log_info("\nmgr_rpc: got invalid reply on session %d (%s)", scb->sid, get_error_string(rpy->res)); } /* check that there is nothing after the element */ if (rpy->res==NO_ERR && !xml_docdone(scb->reader) && LOGINFO) { log_info("\nmgr_rpc: got extra nodes in reply on session %d", scb->sid); } /* invoke the reply handler */ if (req != NULL) { handler = (mgr_rpc_cbfn_t)req->replycb; (*handler)(scb, req, rpy); } /* only reset the session state to idle if was not changed * to SES_ST_SHUTDOWN_REQ during this RPC call */ if (scb->state == SES_ST_IN_MSG) { scb->state = SES_ST_IDLE; } #ifdef MGR_RPC_DEBUG print_errors(); clear_errors(); #endif } /* mgr_rpc_dispatch */ /* END file mgr_rpc.c */ yuma123_2.14/netconf/src/mgr/mgr_ses.h0000664000175000017500000003104214770023131017743 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_mgr_ses #define _H_mgr_ses /* FILE: mgr_ses.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Session Manager definitions module ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 08-feb-07 abb Begun; started from agt_ses.h */ #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION mgr_ses_init * * Initialize the session manager module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void mgr_ses_init (void); /******************************************************************** * FUNCTION mgr_ses_cleanup * * Cleanup the session manager module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void mgr_ses_cleanup (void); /******************************************************************** * FUNCTION mgr_ses_new_session * * Create a new session control block and * start a NETCONF session with to the specified server * * After this functions returns OK, the session state will * be in HELLO_WAIT state. An server must be received * before any requests can be sent * * The pubkeyfile and privkeyfile parameters will be used first. * If this fails, the password parameter will be checked * * INPUTS: * user == user name * password == user password * pubkeyfile == filespec for client public key * privkeyfile == filespec for client private key * privkeypass == passphrase to unlock private key * ssh_use_agent == TRUE if the agent to be used for authentication * target == ASCII IP address or DNS hostname of target * port == NETCONF port number to use, or 0 to use defaults * transport == enum for the transport to use (SSH or TCP) * progcb == temp program instance control block, * == NULL if a session temp files control block is not * needed * retsid == address of session ID output * getvar_cb == XPath get varbind callback function * protocols_parent == pointer to val_value_t that may contain * a session-specific --protocols variable to set * == NULL if not used * * OUTPUTS: * *retsid == session ID, if no error * * RETURNS: * status *********************************************************************/ extern status_t mgr_ses_new_session (const xmlChar *user, const xmlChar *password, const char *pubkeyfile, const char *privkeyfile, boolean ssh_use_agent, const xmlChar *target, uint16 port, ses_transport_t transport, ncxmod_temp_progcb_t *progcb, ses_id_t *retsid, xpath_getvar_fn_t getvar_fn, val_value_t *protocols_parent); /******************************************************************** * FUNCTION mgr_ses_new_session2 * * Create a new session control block and * start a NETCONF session with to the specified server * * After this functions returns OK, the session state will * be in HELLO_WAIT state. An server must be received * before any requests can be sent * * The pubkeyfile and privkeyfile parameters will be used first. * If this fails, the password parameter will be checked * * INPUTS: * user == user name * password == user password * pubkeyfile == filespec for client public key * privkeyfile == filespec for client private key * privkeypass == passphrase to unlock private key * ssh_use_agent == TRUE if the agent to be used for authentication * target == ASCII IP address or DNS hostname of target * port == NETCONF port number to use, or 0 to use defaults * transport == enum for the transport to use (SSH or TCP) * progcb == temp program instance control block, * == NULL if a session temp files control block is not * needed * retsid == address of session ID output * getvar_cb == XPath get varbind callback function * protocols_parent == pointer to val_value_t that may contain * a session-specific --protocols variable to set * == NULL if not used * * OUTPUTS: * *retsid == session ID, if no error * * RETURNS: * status *********************************************************************/ extern status_t mgr_ses_new_session2 (const xmlChar *user, const xmlChar *password, const char *pubkeyfile, const char *privkeyfile, const xmlChar *privkeypass, boolean ssh_use_agent, const xmlChar *target, uint16 port, ses_transport_t transport, ncxmod_temp_progcb_t *progcb, ses_id_t *retsid, xpath_getvar_fn_t getvar_fn, val_value_t *protocols_parent); /******************************************************************** * FUNCTION mgr_ses_free_session * * Free a real session control block * * INPUTS: * scb == session control block to free * *********************************************************************/ extern void mgr_ses_free_session (ses_id_t sid); /******************************************************************** * FUNCTION mgr_ses_new_dummy_session * * Create a dummy session control block * * INPUTS: * none * RETURNS: * pointer to initialized dummy SCB, or NULL if malloc error *********************************************************************/ extern ses_cb_t * mgr_ses_new_dummy_session (void); /******************************************************************** * FUNCTION mgr_ses_free_dummy_session * * Free a dummy session control block * * INPUTS: * scb == session control block to free * *********************************************************************/ extern void mgr_ses_free_dummy_session (ses_cb_t *scb); /******************************************************************** * FUNCTION mgr_ses_process_first_ready * * Check the readyQ and process the first message, if any * * RETURNS: * TRUE if a message was processed * FALSE if the readyQ was empty *********************************************************************/ extern boolean mgr_ses_process_first_ready (void); /******************************************************************** * FUNCTION mgr_ses_fill_writeset * * Drain the ses_msg outreadyQ and set the specified fdset * Used by mgr_ncxserver write_fd_set * * INPUTS: * fdset == pointer to fd_set to fill * maxfdnum == pointer to max fd int to fill in * * OUTPUTS: * *fdset is updated in * *maxfdnum may be updated * * RETURNS: * number of ready-for-output sessions found *********************************************************************/ extern uint32 mgr_ses_fill_writeset (fd_set *fdset, int *maxfdnum); /******************************************************************** * FUNCTION mgr_ses_get_first_outready * * Get the first ses_msg outreadyQ entry * Will remove the entry from the Q * * RETURNS: * pointer to the session control block of the * first session ready to write output to a socket * or the screen or a file *********************************************************************/ extern ses_cb_t * mgr_ses_get_first_outready (void); /******************************************************************** * FUNCTION mgr_ses_get_first_session * * Get the first active session * * RETURNS: * pointer to the session control block of the * first active session found *********************************************************************/ extern ses_cb_t * mgr_ses_get_first_session (void); /******************************************************************** * FUNCTION mgr_ses_get_next_session * * Get the next active session * * INPUTS: * curscb == current session control block to use * * RETURNS: * pointer to the session control block of the * next active session found * *********************************************************************/ extern ses_cb_t * mgr_ses_get_next_session (ses_cb_t *curscb); /******************************************************************** * FUNCTION mgr_ses_readfn * * Read callback function for a manager SSH2 session * * INPUTS: * s == session control block * buff == buffer to fill * bufflen == length of buff * erragain == address of return EAGAIN flag * * OUTPUTS: * *erragain == TRUE if ret value < zero means * read would have blocked (not really an error) * FALSE if the ret value is not < zero, or * if it is, it is a real error * * RETURNS: * number of bytes read; -1 for error; 0 for connection closed *********************************************************************/ extern ssize_t mgr_ses_readfn (void *scb, char *buff, size_t bufflen, boolean *erragain); /******************************************************************** * FUNCTION mgr_ses_writefn * * Write callback function for a manager SSH2 session * * INPUTS: * s == session control block * * RETURNS: * status of the write operation *********************************************************************/ extern status_t mgr_ses_writefn (void *scb); /******************************************************************** * FUNCTION mgr_ses_get_scb * * Retrieve the session control block * * INPUTS: * sid == manager session ID (not agent assigned ID) * * RETURNS: * pointer to session control block or NULL if invalid *********************************************************************/ extern ses_cb_t * mgr_ses_get_scb (ses_id_t sid); /******************************************************************** * FUNCTION mgr_ses_get_mscb * * Retrieve the manager session control block * * INPUTS: * scb == session control block to use * * RETURNS: * pointer to manager session control block or NULL if invalid *********************************************************************/ extern mgr_scb_t * mgr_ses_get_mscb (ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_mgr_ses */ yuma123_2.14/netconf/src/mgr/mgr_load.h0000664000175000017500000000530614770023131020074 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_mgr_load #define _H_mgr_load /* FILE: mgr_load.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Load Data File into an object template ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 27-jun-11 abb Begun; */ #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION mgr_load_extern_file * * Read an external file and convert it into a real data structure * * INPUTS: * filespec == XML filespec to load * targetobj == template to use to validate XML and encode as this object * !! must not be ncx:root!! * == NULL to use OBJ_TYP_ANYXML as the target object template * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced val_value_t struct filled in with the contents *********************************************************************/ extern val_value_t * mgr_load_extern_file (const xmlChar *filespec, obj_template_t *targetobj, status_t *res); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_mgr_load */ yuma123_2.14/netconf/src/mgr/mgr_io.h0000664000175000017500000001324614770023131017566 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_mgr_io #define _H_mgr_io /* FILE: mgr_io.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Manager IO Handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 18-feb-07 abb Begun; start from agt_ncxserver.h */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* SSH layer debugging return codes */ typedef enum mgr_io_returncode_t_ { MGR_IO_RC_NONE, MGR_IO_RC_IDLE, MGR_IO_RC_DROPPED, MGR_IO_RC_WANTDATA, MGR_IO_RC_PROCESSED, MGR_IO_RC_DROPPED_NOW } mgr_io_returncode_t; /* Manager application IO states */ typedef enum mgr_io_state_t_ { MGR_IO_ST_NONE, MGR_IO_ST_INIT, /* session starting up */ MGR_IO_ST_IDLE, /* top-level wait for CLI */ MGR_IO_ST_CONNECT, /* conn attempt in progress */ MGR_IO_ST_CONN_START, /* session waiting for */ MGR_IO_ST_CONN_IDLE, /* session ready for commands */ MGR_IO_ST_CONN_RPYWAIT, /* ses wait for */ MGR_IO_ST_CONN_CANCELWAIT, /* ses wait for cancel to complete */ MGR_IO_ST_CONN_CLOSEWAIT, /* wait for own session to close */ MGR_IO_ST_CONN_SHUT, /* session shutting down */ MGR_IO_ST_SHUT /* application shutting down */ } mgr_io_state_t; /* Callback function for STDIN input * * Handle any input from the user keyboard * * INPUTS: * none * RETURNS: * new manager IO state (upon exit from the function */ typedef mgr_io_state_t (*mgr_io_stdin_fn_t) (void); /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION mgr_io_init * * Init the IO server loop vars for the ncx manager * Must call this function before any other function can * be used from this module *********************************************************************/ extern void mgr_io_init (void); /******************************************************************** * FUNCTION mgr_io_set_stdin_handler * * Set the callback function for STDIN processing * * INPUTS: * handler == address of the STDIN handler function *********************************************************************/ extern void mgr_io_set_stdin_handler (mgr_io_stdin_fn_t handler); /******************************************************************** * FUNCTION mgr_io_activate_session * * Tell the IO manager to start listening on the specified socket * * INPUTS: * fd == file descriptor number of the socket *********************************************************************/ extern void mgr_io_activate_session (int fd); /******************************************************************** * FUNCTION mgr_io_deactivate_session * * Tell the IO manager to stop listening on the specified socket * * INPUTS: * fd == file descriptor number of the socket *********************************************************************/ extern void mgr_io_deactivate_session (int fd); /******************************************************************** * FUNCTION mgr_io_run * * IO server loop for the ncx manager * * RETURNS: * status *********************************************************************/ extern status_t mgr_io_run (void); /******************************************************************** * FUNCTION mgr_io_process_timeout * * mini server loop while waiting for KBD input * * INPUTS: * cursid == current session ID to check * wantdata == address of return wantdata flag * anystdout == address of return anystdout flag * * OUTPUTS: * *wantdata == TRUE if the agent has sent a keepalive * and is expecting a request * == FALSE if no keepalive received this time * *anystdout == TRUE if maybe STDOUT was written * FALSE if definately no STDOUT written * * RETURNS: * TRUE if session alive or not confirmed * FALSE if cursid confirmed dropped *********************************************************************/ extern boolean mgr_io_process_timeout (ses_id_t cursid, boolean *wantdata, boolean *anystdout); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_mgr_io */ yuma123_2.14/netconf/src/mgr/mgr.c0000664000175000017500000002631214770023131017070 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: mgr.c NETCONF Manager Access Library ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 03feb06 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_cap #include "cap.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_mgr #include "mgr.h" #endif #ifndef _H_mgr_cap #include "mgr_cap.h" #endif #ifndef _H_mgr_hello #include "mgr_hello.h" #endif #ifndef _H_mgr_io #include "mgr_io.h" #endif #ifndef _H_mgr_not #include "mgr_not.h" #endif #ifndef _H_mgr_rpc #include "mgr_rpc.h" #endif #ifndef _H_mgr_ses #include "mgr_ses.h" #endif #ifndef _H_mgr_signal #include "mgr_signal.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncx_list #include "ncx_list.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_var #include "var.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define MGR_DEBUG 1 #endif /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean mgr_init_done = FALSE; static boolean mgr_shutdown; static boolean mgr_allow_sighandlers = TRUE; /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION mgr_disable_sighandlers * * Prevent mgr library from registering signal handlers. Call this * BEFORE mgr_init() if the application will install its own handlers. * In this case, the application may need to call mgr_request_shutdown() * at the appropriate times. mgr_cleanup() will restore the default * behavior of allowing the library to register handlers. * *********************************************************************/ void mgr_disable_sighandlers(void) { mgr_allow_sighandlers = FALSE; } /******************************************************************** * FUNCTION mgr_init * * Initialize the Manager Library * * RETURNS: * status of the initialization procedure *********************************************************************/ status_t mgr_init (void) { status_t res; if (mgr_init_done) { log_info("\nManager Init Redo skipped..."); return NO_ERR; } #ifdef MGR_DEBUG log_debug3("\nManager Init Starting..."); #endif mgr_shutdown = FALSE; res = mgr_cap_set_caps(); if (res != NO_ERR) { return res; } res = mgr_rpc_init(); if (res != NO_ERR) { return res; } res = mgr_not_init(); if (res != NO_ERR) { return res; } res = mgr_hello_init(); if (res != NO_ERR) { return res; } mgr_ses_init(); mgr_io_init(); if (mgr_allow_sighandlers) mgr_signal_init(); mgr_init_done = TRUE; return NO_ERR; } /* mgr_init */ /******************************************************************** * FUNCTION mgr_cleanup * * Cleanup the Manager Library * * TBD -- put platform-specific manager cleanup here * *********************************************************************/ void mgr_cleanup (void) { if (mgr_init_done) { #ifdef MGR_DEBUG log_debug3("\nManager Cleanup Starting...\n"); #endif mgr_cap_cleanup(); mgr_rpc_cleanup(); mgr_not_cleanup(); mgr_ses_cleanup(); mgr_hello_cleanup(); if (mgr_allow_sighandlers) mgr_signal_cleanup(); mgr_allow_sighandlers = TRUE; mgr_shutdown = FALSE; mgr_init_done = FALSE; } } /* mgr_cleanup */ /******************************************************************** * FUNCTION mgr_new_scb * * Malloc and Initialize the Manager Session Control Block * * RETURNS: * manager session control block struct or NULL if malloc error *********************************************************************/ mgr_scb_t * mgr_new_scb (void) { mgr_scb_t *mscb; mscb = m__getObj(mgr_scb_t); if (mscb) { mgr_init_scb(mscb); } return mscb; } /* mgr_new_scb */ /******************************************************************** * FUNCTION mgr_init_scb * * Initialize the Manager Session Control Block * * INPUTS: * mscb == manager session control block struct to initialize * *********************************************************************/ void mgr_init_scb (mgr_scb_t *mscb) { #ifdef DEBUG if (!mscb) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif memset(mscb, 0x0, sizeof(mgr_scb_t)); cap_init_caplist(&mscb->caplist); dlq_createSQue(&mscb->reqQ); dlq_createSQue(&mscb->temp_modQ); ncx_init_list(&mscb->temp_ync_features, NCX_BT_STRING); mscb->next_id = 1; } /* mgr_init_scb */ /******************************************************************** * FUNCTION mgr_free_scb * * Clean and Free a Manager Session Control Block * * INPUTS: * mscb == manager session control block struct to free *********************************************************************/ void mgr_free_scb (mgr_scb_t *mscb) { #ifdef DEBUG if (!mscb) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif mgr_clean_scb(mscb); m__free(mscb); } /* mgr_free_scb */ /******************************************************************** * FUNCTION mgr_clean_scb * * Clean a Manager Session Control Block * * INPUTS: * mscb == manager session control block struct to clean *********************************************************************/ void mgr_clean_scb (mgr_scb_t *mscb) { ncx_module_t *mod; #ifdef DEBUG if (!mscb) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (mscb->temp_progcb && mscb->temp_sescb) { ncxmod_free_session_tempdir(mscb->temp_progcb, mscb->temp_sescb->sidnum); mscb->temp_progcb = NULL; mscb->temp_sescb = NULL; } ncx_clean_list(&mscb->temp_ync_features); cap_clean_caplist(&mscb->caplist); if (mscb->root) { val_free_value(mscb->root); mscb->root = NULL; } if (mscb->lastroot) { val_free_value(mscb->lastroot); mscb->lastroot = NULL; } if (mscb->chtime) { m__free(mscb->chtime); mscb->chtime = NULL; } if (mscb->lastchtime) { m__free(mscb->lastchtime); mscb->lastchtime = NULL; } if (mscb->target) { m__free(mscb->target); mscb->target = NULL; } if (mscb->modules_state_val) { val_free_value(mscb->modules_state_val); mscb->modules_state_val = NULL; } if (mscb->agent) { libssh2_agent_disconnect(mscb->agent); libssh2_agent_free(mscb->agent); mscb->agent = NULL; } if (mscb->channel) { libssh2_channel_free(mscb->channel); mscb->channel = NULL; } if (mscb->session) { libssh2_session_disconnect(mscb->session, "SSH2 session closed"); libssh2_session_free(mscb->session); mscb->session = NULL; } while (!dlq_empty(&mscb->temp_modQ)) { mod = (ncx_module_t *)dlq_deque(&mscb->temp_modQ); ncx_free_module(mod); } mgr_rpc_clean_requestQ(&mscb->reqQ); } /* mgr_clean_scb */ /******************************************************************** * FUNCTION mgr_request_shutdown * * Request a manager shutdown * *********************************************************************/ void mgr_request_shutdown (void) { log_debug3("\nmgr: shutdown requested\n"); mgr_shutdown = TRUE; } /* mgr_request_shutdown */ /******************************************************************** * FUNCTION mgr_shutdown_requested * * Check if a manager shutdown is in progress * * RETURNS: * TRUE if shutdown mode has been started * *********************************************************************/ boolean mgr_shutdown_requested (void) { return mgr_shutdown; } /* mgr_shutdown_requested */ /******************************************************************** * FUNCTION mgr_set_getvar_fn * * Set the getvar_fn callback for the session * * INPUTS: * sid == manager session ID to use * getvar_fn == function to use * * RETURNS: * status *********************************************************************/ extern status_t mgr_set_getvar_fn (ses_id_t sid, xpath_getvar_fn_t getvar_fn) { ses_cb_t *scb; mgr_scb_t *mscb; scb = mgr_ses_get_scb(sid); if (scb == NULL) { return ERR_NCX_NOT_FOUND; } mscb = mgr_ses_get_mscb(scb); mscb->getvar_fn = getvar_fn; return NO_ERR; } /* mgr_set_getvar_fn */ /******************************************************************** * FUNCTION mgr_print_libssh2_version * * Print the version of libssh2 used by the manager * Indenting must already be done! * * INPUTS: * tolog == TRUE to print to log; FALSE to print to stdout *********************************************************************/ void mgr_print_libssh2_version (boolean tolog) { #ifdef LIBSSH2_VERSION_MAJOR #ifdef LIBSSH2_VERSION_MINOR #if (LIBSSH2_VERSION_MAJOR >= 1 && LIBSSH2_VERSION_MINOR >= 1) if (tolog) { log_write("libssh2 version %s", libssh2_version(0)); } else { log_stdout("libssh2 version %s", libssh2_version(0)); } #else if (tolog) { log_write("libssh2 version lower than 1.1 (unavailable)"); } else { log_stdout("libssh2 version lower than 1.1 (unavailable)"); } #endif #endif #endif } /* mgr_print_libssh2_version */ /* END file mgr.c */ yuma123_2.14/netconf/src/mgr/mgr_load.c0000664000175000017500000000710614770023131020067 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: mgr_load.c Load Data File into an object template ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 27jun11 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include "procdefs.h" #include "log.h" #include "mgr.h" #include "mgr_load.h" #include "mgr_ses.h" #include "mgr_val_parse.h" #include "ses.h" #include "status.h" #include "xml_util.h" /******************************************************************** * FUNCTION mgr_load_extern_file * * Read an external file and convert it into a real data structure * * INPUTS: * filespec == XML filespec to load * targetobj == template to use to validate XML and encode as this object * !! must not be ncx:root!! * == NULL to use OBJ_TYP_ANYXML as the target object template * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced val_value_t struct filled in with the contents *********************************************************************/ val_value_t * mgr_load_extern_file (const xmlChar *filespec, obj_template_t *targetobj, status_t *res) { ses_cb_t *scb; val_value_t *topval; boolean isany; *res = NO_ERR; isany = FALSE; topval = NULL; /* only supporting parsing of XML files for now */ if (!yang_fileext_is_xml(filespec)) { *res = ERR_NCX_OPERATION_NOT_SUPPORTED; return NULL; } /* create a dummy session control block */ scb = mgr_ses_new_dummy_session(); if (scb == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } if (targetobj == NULL) { isany = TRUE; targetobj = ncx_get_gen_anyxml(); } if (LOGDEBUG2) { log_debug2("\nLoading extern XML file as '%s'", filespec, (isany) ? NCX_EL_ANYXML : obj_get_name(targetobj)); } /* setup the config file as the xmlTextReader input */ *res = xml_get_reader_from_filespec((const char *)filespec, &scb->reader); if (*res == NO_ERR) { topval = val_new_value(); if (topval == NULL) { *res = ERR_INTERNAL_MEM; } else { /* do not initialize the new value! */ *res = mgr_val_parse(scb, targetobj, NULL, topval); } } mgr_ses_free_dummy_session(scb); return topval; } /* mgr_load_extern_file */ /* END file mgr_load.c */ yuma123_2.14/netconf/src/mgr/mgr_signal.c0000664000175000017500000001260714770023131020427 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: mgr_signal.c Handle interrupt signals for the manager ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 20jan07 abb begun; start from agt_signal.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_mgr #include "mgr.h" #endif #ifndef _H_mgr_signal #include "mgr_signal.h" #endif #ifdef NOT_YET #ifndef _H_mgr_timer #include "mgr_timer.h" #endif #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_status #include "status.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define MGR_SIGNAL_DEBUG 1 #endif /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean mgr_signal_init_done = FALSE; static sighandler_t sh_int, sh_hup, sh_term, sh_pipe, sh_alarm, ctl_c; /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION mgr_signal_init * * Initialize the mgr_signal module * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ void mgr_signal_init (void) { if (!mgr_signal_init_done) { ctl_c = NULL; /* setup mgr_signal_handler */ sh_int = signal(SIGINT, mgr_signal_handler); sh_hup = signal(SIGHUP, mgr_signal_handler); sh_term = signal(SIGTERM, mgr_signal_handler); sh_pipe = signal(SIGPIPE, mgr_signal_handler); sh_alarm = signal(SIGALRM, mgr_signal_handler); mgr_signal_init_done = TRUE; } } /* mgr_signal_init */ /******************************************************************** * FUNCTION mgr_signal_cleanup * * Cleanup the mgr_signal module. * *********************************************************************/ void mgr_signal_cleanup (void) { if (mgr_signal_init_done) { /* restore previous handlers */ signal(SIGINT, sh_int); signal(SIGHUP, sh_hup); signal(SIGTERM, sh_term); signal(SIGPIPE, sh_pipe); signal(SIGALRM, sh_alarm); ctl_c = NULL; mgr_signal_init_done = FALSE; } } /* mgr_signal_cleanup */ /******************************************************************** * FUNCTION mgr_signal_handler * * Handle an incoming interrupt signal * * INPUTS: * intr == interrupt numer * *********************************************************************/ void mgr_signal_handler (int intr) { switch (intr) { case SIGINT: /* control-C */ if (ctl_c) { (*ctl_c)(intr); } else { mgr_request_shutdown(); } break; case SIGHUP: /* hang up treated as reset */ mgr_request_shutdown(); break; case SIGTERM: /* kill -1 */ mgr_request_shutdown(); break; case SIGPIPE: /* broken pipe ignored */ break; case SIGALRM: /* dispatch to the agent timer handler */ /* mgr_timer_handler(); */ break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* mgr_signal_handler */ /******************************************************************** * FUNCTION mgr_signal_install_break_handler (control-c) * * Install an application-specific handler for the break interrupt * * INPUTS: * handler == interrupt handler to call when control-C is entered * *********************************************************************/ void mgr_signal_install_break_handler (sighandler_t handler) { ctl_c = handler; } /* mgr_signal_install_break_handler */ /* END file mgr_signal.c */ yuma123_2.14/netconf/src/mgr/mgr_rpc.h0000664000175000017500000001747614770023131017754 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_mgr_rpc #define _H_mgr_rpc /* FILE: mgr_rpc.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF protocol remote procedure call manager-side definitions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13-feb-07 abb Begun; started from agt_rpc.h */ #include #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define MGR_RPC_NUM_PHASES 6 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* NETCONF client message header */ typedef struct mgr_rpc_req_t_ { dlq_hdr_t qhdr; xml_msg_hdr_t mhdr; uint32 group_id; obj_template_t *rpc; xmlChar *msg_id; /* malloced message ID */ xml_attrs_t attrs; /* Extra attrs */ val_value_t *data; /* starts with the method */ time_t starttime; /* tstamp for timeout */ struct timeval perfstarttime; /* tstamp to perf meas */ uint32 timeout; /* timeout in seconds */ void *replycb; /* mgr_rpc_cbfn_t */ } mgr_rpc_req_t; /* NETCONF client message header */ typedef struct mgr_rpc_rpy_t_ { dlq_hdr_t qhdr; /* xml_msg_hdr_t mhdr; */ uint32 group_id; xmlChar *msg_id; status_t res; val_value_t *reply; } mgr_rpc_rpy_t; /* manager RPC reply callback function * * INPUTS: * scb == session control block for session that got the reply * req == RPC request returned to the caller (for reuse or free) * rpy == RPY reply header; this will be NULL if the timeout * occurred so there is no reply */ typedef void (*mgr_rpc_cbfn_t) (ses_cb_t *scb, mgr_rpc_req_t *req, mgr_rpc_rpy_t *rpy); /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION mgr_rpc_init * * Initialize the mgr_rpc module * call once to init RPC mgr module * Adds the mgr_rpc_dispatch function as the handler * for the NETCONF top-level element. * * INPUTS: * none * RETURNS: * NO_ERR if all okay, the minimum spare requests will be malloced *********************************************************************/ extern status_t mgr_rpc_init (void); /******************************************************************** * FUNCTION mgr_rpc_cleanup * * Cleanup the mgr_rpc module. * call once to cleanup RPC mgr module * Unregister the top-level NETCONF element * *********************************************************************/ extern void mgr_rpc_cleanup (void); /******************************************************************** * FUNCTION mgr_rpc_new_request * * Malloc and initialize a new mgr_rpc_req_t struct * * INPUTS: * scb == session control block * * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ extern mgr_rpc_req_t * mgr_rpc_new_request (ses_cb_t *scb); /******************************************************************** * FUNCTION mgr_rpc_free_request * * Free a mgr_rpc_req_t struct * * INPUTS: * req == struct to free * * RETURNS: * none *********************************************************************/ extern void mgr_rpc_free_request (mgr_rpc_req_t *req); /******************************************************************** * FUNCTION mgr_rpc_free_reply * * Free a mgr_rpc_rpy_t struct * * INPUTS: * rpy == struct to free * * RETURNS: * none *********************************************************************/ extern void mgr_rpc_free_reply (mgr_rpc_rpy_t *rpy); /******************************************************************** * FUNCTION mgr_rpc_clean_requestQ * * Clean the request Q of mgr_rpc_req_t entries * * INPUTS: * reqQ == Q of entries to free; the Q itself is not freed * * RETURNS: * none *********************************************************************/ extern void mgr_rpc_clean_requestQ (dlq_hdr_t *reqQ); /******************************************************************** * FUNCTION mgr_rpc_timeout_requestQ * * Clean the request Q of mgr_rpc_req_t entries * Only remove the entries that have timed out * * returning number of msgs timed out * need a callback-based cleanup later on * to support N concurrent requests per agent * * INPUTS: * reqQ == Q of entries to check * * RETURNS: * number of request timed out *********************************************************************/ extern uint32 mgr_rpc_timeout_requestQ (dlq_hdr_t *reqQ); /******************************************************************** * FUNCTION mgr_rpc_send_request * * Send an request to the agent on the specified session * non-blocking send, reply function will be called when * one is received or a timeout occurs * * INPUTS: * scb == session control block * req == request to send * rpyfn == reply callback function * * RETURNS: * status *********************************************************************/ extern status_t mgr_rpc_send_request (ses_cb_t *scb, mgr_rpc_req_t *req, mgr_rpc_cbfn_t rpyfn); /******************************************************************** * FUNCTION mgr_rpc_dispatch * * Dispatch an incoming response * handle the element * called by mgr_top.c: * This function is registered with top_register_node * for the module 'netconf', top-node 'rpc-reply' * * INPUTS: * scb == session control block * top == top element descriptor *********************************************************************/ extern void mgr_rpc_dispatch (ses_cb_t *scb, xml_node_t *top); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_mgr_rpc */ yuma123_2.14/netconf/src/mgr/mgr_top.h0000664000175000017500000000356514770023131017764 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_mgr_top #define _H_mgr_top /* FILE: mgr_top.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Manager Top Element module Manage callback registry for received XML messages ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11-feb-07 abb Begun; start from agt_top.c */ #ifndef _H_ses #include "ses.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION mgr_top_dispatch_msg * * Find the appropriate top node handler and call it * * INPUTS: * scb == session control block containing the xmlreader * set at the start of an incoming message. * * RETURNS: * none *********************************************************************/ extern void mgr_top_dispatch_msg (ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_mgr_top */ yuma123_2.14/netconf/src/mgr/mgr_cap.h0000664000175000017500000000662114770023131017721 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_mgr_cap #define _H_mgr_cap /* FILE: mgr_cap.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Manager capabilities handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 03-feb-06 abb Begun; split out from base/cap.h */ #ifndef _H_cap #include "cap.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION mgr_cap_cleanup * * Clean the NETCONF manager capabilities * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void mgr_cap_cleanup (void); /******************************************************************** * FUNCTION mgr_cap_set_caps * * Initialize the NETCONF manager capabilities * * TEMP !!! JUST SET EVERYTHING, WILL REALLY GET FROM MGR STARTUP * * INPUTS: * none * RETURNS: * NO_ERR if all goes well *********************************************************************/ extern status_t mgr_cap_set_caps (void); /******************************************************************** * FUNCTION mgr_cap_get_caps * * Get the NETCONF manager capabilities * * INPUTS: * none * RETURNS: * pointer to the manager caps list *********************************************************************/ extern cap_list_t * mgr_cap_get_caps (void); /******************************************************************** * FUNCTION mgr_cap_get_capsval * * Get the NETCONF manager capabilities ain val_value_t format * * INPUTS: * none * RETURNS: * pointer to the manager caps list *********************************************************************/ extern val_value_t * mgr_cap_get_capsval (void); /******************************************************************** * FUNCTION mgr_cap_get_ses_capsval * * Get the NETCONF manager capabilities ain val_value_t format * for a specific session, v2 supports base1.0 and/or base1.1 * INPUTS: * scb == session control block to use * RETURNS: * MALLOCED pointer to the manager caps list to use * and then discard with val_free_value *********************************************************************/ extern val_value_t * mgr_cap_get_ses_capsval (ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_mgr_cap */ yuma123_2.14/netconf/src/mgr/mgr_xml.h0000664000175000017500000001262714770023131017761 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_mgr_xml #define _H_mgr_xml /* FILE: mgr_xml.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Manager XML Reader interface ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 12-feb-07 abb begun; start from agt_xml.c */ /* From /usr/include/libxml2/libxml/ */ #include #include #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /**************** XMLTextReader APIs ******************/ /******************************************************************** * FUNCTION mgr_xml_consume_node * * Parse the next node and return its namespace, type and name * The xml_init_node or xml_clean_node API must be called before * this function for the node parameter * * There are 2 types of XML element start nodes * - empty node (XML_NT_EMPTY) * - start of a simple or complex type (XML_NT_START) * * There is one string content node for simpleType content * - string node (XML_NT_STRING) * * There is one end node to end both simple and complex types * - end node (XML_NT_END) * * If nodetyp==XML_NT_EMPTY, then no further nodes will occur * for this element. This node may contain attributes. The * naming parameters will all be set. * * If nodetyp==XML_NT_START, then the caller should examine * the schema for that start node. * For complex types, the next node is probably another XML_NT_START. * For simple types, the next node will be XML_NT_STRING, * followed by an XML_NT_END node. This node may contain attributes. * The naming parameters will all be set. * * If the nodetype==XML_NT_STRING, then the simval and simlen * fields will be set. There are no attributes or naming parameters * for this node type. * * IF the nodetype==XML_NT_END, then no further nodes for this element * will occur. This node should not contain attributes. * All of the naming parameters will be set. The xml_endnode_match * function should be used to confirm that the XML_NT_START and * XML_NT_END nodes are paired correctly. * * The node pointer for the reader will be advanced before the * node is read. * * INPUTS: * reader == XmlReader already initialized from File, Memory, * or whatever * node == pointer to an initialized xml_node_t struct * to be filled in * * OUTPUTS: * *node == xml_node_t struct filled in * reader will be advanced * * RETURNS: * status of the operation * Try to fail on fatal errors only *********************************************************************/ extern status_t mgr_xml_consume_node (xmlTextReaderPtr reader, xml_node_t *node); /* consume node but do not generate namespace errors if seen * needed to process subtree filters properly */ extern status_t mgr_xml_consume_node_nons (xmlTextReaderPtr reader, xml_node_t *node); /* re-get the current node */ extern status_t mgr_xml_consume_node_noadv (xmlTextReaderPtr reader, xml_node_t *node); /******************************************************************** * FUNCTION mgr_xml_skip_subtree * * Already encountered an error, so advance nodes until the * matching start-node is reached or a terminating error occurs * - end of input * - start depth level reached * * INPUTS: * reader == XmlReader already initialized from File, Memory, * or whatever * startnode == xml_node_t of the start node of the sub-tree to skip * RETURNS: * status of the operation * SIDE EFFECTS: * the xmlreader state is advanced until the current node is the * end node of the specified start node or a fatal error occurs *********************************************************************/ extern status_t mgr_xml_skip_subtree (xmlTextReaderPtr reader, const xml_node_t *startnode); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_mgr_xml */ yuma123_2.14/netconf/src/mgr/mgr_ses.c0000664000175000017500000015513014770023131017743 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2012, YumaWorks, Inc., All Rights Reserved. * Copyright (c) 2013 - 2016, Vladimir Vassilev, All Rights Reserved. * Copyright (c) 2013 - 2016, Transpacket AS, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: mgr_ses.c NETCONF Session Manager: Manager Side Support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 19may05 abb begun 08feb07 abb start rewrite, using new NCX IO functions ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ /* #include #include #include #include #include */ #include #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "def_reg.h" #include "dlq.h" #include "log.h" #include "mgr.h" #include "mgr_hello.h" #include "mgr_io.h" #include "mgr_rpc.h" #include "mgr_ses.h" #include "mgr_top.h" #include "rpc.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "xml_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define MGR_SES_DEBUG 1 #define MGR_SES_SSH2_TRACE 1 /* #define MGR_SES_DEBUG_EOF 1 */ #endif /* maximum number of concurrent outbound sessions * Must be >= 2 since session 0 is used for the dummy scb */ #define MGR_SES_MAX_SESSIONS 16 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean mgr_ses_init_done = FALSE; static uint32 next_sesid; static ses_cb_t *mgrses[MGR_SES_MAX_SESSIONS]; /******************************************************************** * * * STATIC FUNCTIONS - START * * * *********************************************************************/ static int set_nonblock(int fd) { int val; val = fcntl(fd, F_GETFL); if (val < 0) { log_error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); return (-1); } if (val & O_NONBLOCK) { log_debug3("fd %d is O_NONBLOCK", fd); return (0); } log_debug2("fd %d setting O_NONBLOCK", fd); val |= O_NONBLOCK; if (fcntl(fd, F_SETFL, val) == -1) { log_debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, strerror(errno)); return (-1); } return (0); } static void monotime_ts(struct timespec *ts) { struct timeval tv; static int gettime_failed = 0; if (!gettime_failed) { if (clock_gettime(CLOCK_MONOTONIC, ts) == 0) return; } gettimeofday(&tv, NULL); ts->tv_sec = tv.tv_sec; ts->tv_nsec = (long)tv.tv_usec * 1000; } static void monotime_tv(struct timeval *tv) { struct timespec ts; monotime_ts(&ts); tv->tv_sec = ts.tv_sec; tv->tv_usec = ts.tv_nsec / 1000; } static void ms_subtract_diff(struct timeval *start, int *ms) { struct timeval diff, finish; monotime_tv(&finish); timersub(&finish, start, &diff); *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); } static int waitrfd(int fd, int *timeoutp) { struct pollfd pfd; struct timeval t_start; int oerrno, r; monotime_tv(&t_start); pfd.fd = fd; pfd.events = POLLIN; for (; *timeoutp >= 0;) { r = poll(&pfd, 1, *timeoutp); oerrno = errno; ms_subtract_diff(&t_start, timeoutp); errno = oerrno; if (r > 0) return 0; else if (r == -1 && errno != EAGAIN) return -1; else if (r == 0) break; } /* timeout */ errno = ETIMEDOUT; return -1; } /******************************************************************** * * * STATIC FUNCTIONS - END * * * *********************************************************************/ /******************************************************************** * FUNCTION connect_to_server * * Try to connect to the NETCONF server indicated by the host entry * Only trying one address at this time * * Only supports the SSH transport at this time * * !!! hack -- hardwired to try only the default netconf-ssh port * * INPUTS: * scb == session control block * hent == host entry struct * port == port number to try, or 0 for defaults * timeout == connection establishment timeout in milliseconds * * RETURNS: * status *********************************************************************/ static status_t connect_to_server (ses_cb_t *scb, struct hostent *hent, uint16_t port, uint32_t timeout) { struct sockaddr_in targ; int ret; boolean userport, done; int optval = 0; socklen_t optlen = sizeof(optval); /* get a file descriptor for the new socket */ scb->fd = socket(AF_INET, SOCK_STREAM, 0); if (scb->fd == -1) { return ERR_NCX_RESOURCE_DENIED; } set_nonblock(scb->fd); /* set the NETCONF server address */ memset(&targ, 0x0, sizeof(targ)); targ.sin_family = AF_INET; memcpy((char *)&targ.sin_addr.s_addr, hent->h_addr_list[0], (size_t)hent->h_length); /* try to connect to user-provided port or port 830 */ done = FALSE; if (port) { userport = TRUE; } else { port = NCX_NCSSH_PORT; userport = FALSE; } targ.sin_port = htons(port); ret = connect(scb->fd, (struct sockaddr *)&targ, sizeof(struct sockaddr_in)); if (!ret) { goto done; } if (errno!=EINPROGRESS) { close(scb->fd); return ERR_NCX_CONNECT_FAILED; } if (waitrfd(scb->fd, &timeout) == -1) { close(scb->fd); return ERR_NCX_CONNECT_FAILED; } /* Completed or failed */ if (getsockopt(scb->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { log_debug("getsockopt: %s", strerror(errno)); close(scb->fd); return ERR_NCX_CONNECT_FAILED; } if (optval != 0) { close(scb->fd); errno = optval; return ERR_NCX_CONNECT_FAILED; } done: mgr_io_activate_session(scb->fd); return NO_ERR; } /* connect_to_server */ /******************************************************************** * FUNCTION ssh2_setup * * Setup the SSH2 session and channel * Connection MUST already established * * The pubkeyfile and privkeyfile parameters will be used first. * If this fails, the password parameter will be checked * * INPUTS: * scb == session control block * hent == host entry struct * user == username to connect as * password == password to authenticate with * * OUTPUTS: * scb->mgrcb fileds set for libssh2 data * * RETURNS: * status *********************************************************************/ static status_t ssh2_setup (ses_cb_t *scb, const char *user, const char *password, const char *pubkeyfile, const char *privkeyfile, const char *privkeypass, boolean ssh_use_agent) { mgr_scb_t *mscb; /* const char *fingerprint; */ char *userauthlist; int ret; boolean authdone; char *pkstr; /* pointer to string "publickey" in userauthlist */ authdone = FALSE; mscb = mgr_ses_get_mscb(scb); mscb->agent = NULL; mscb->session = libssh2_session_init(); if (!mscb->session) { if (LOGINFO) { log_info("\nmgr_ses: SSH2 new session failed"); } return ERR_NCX_SESSION_FAILED; } ret = libssh2_session_startup(mscb->session, scb->fd); if (ret) { if (LOGINFO) { log_info("\nmgr_ses: SSH2 establishment failed"); } return ERR_NCX_SESSION_FAILED; } #ifdef MGR_SES_SSH2_TRACE if (LOGDEBUG3) { libssh2_trace(mscb->session, LIBSSH2_TRACE_TRANS | LIBSSH2_TRACE_KEX | LIBSSH2_TRACE_AUTH | LIBSSH2_TRACE_CONN | LIBSSH2_TRACE_SCP | LIBSSH2_TRACE_SFTP | LIBSSH2_TRACE_ERROR | LIBSSH2_TRACE_PUBLICKEY); } #endif /* * this does not work because the AUTH phase will not * handle the EAGAIN errors correctly * * libssh2_session_set_blocking(mscb->session, 0); */ /* get the host fingerprint */ /* fingerprint = libssh2_hostkey_hash(mscb->session, LIBSSH2_HOSTKEY_HASH_MD5); */ /* TBD: check fingerprint against known hosts files !!! */ /* get the userauth info by sending an SSH_USERAUTH_NONE * request to the server, hoping it will fail, and the * list of supported auth methods will be returned */ userauthlist = libssh2_userauth_list(mscb->session, user, strlen(user)); if (!userauthlist) { /* check if the server accepted NONE as an auth method */ if (libssh2_userauth_authenticated(mscb->session)) { if (LOGINFO) { log_info("\nmgr_ses: server accepted SSH_AUTH_NONE"); } authdone = TRUE; } else { if (LOGINFO) { log_info("\nmgr_ses: SSH2 get authlist failed"); } return ERR_NCX_SESSION_FAILED; } } if (LOGDEBUG2) { log_debug2("\nmgr_ses: Got server authentication methods: %s\n", userauthlist); } pkstr = strstr(userauthlist, "publickey"); if (1) { if (!authdone && pkstr != NULL && ssh_use_agent) { int rc; struct libssh2_agent_publickey *identity; struct libssh2_agent_publickey *prev_identity = NULL; mscb->agent = libssh2_agent_init(mscb->session); if (mscb->agent == NULL) { log_info("\nmgr_ses: SSH2 failure initializing agent support"); goto agent_done; } if (libssh2_agent_connect(mscb->agent)) { log_info("\nmgr_ses: SSH2 failure connecting to ssh agent"); libssh2_agent_free(mscb->agent); mscb->agent = NULL; goto agent_done; } if (libssh2_agent_list_identities(mscb->agent)) { log_info("\nmgr_ses: SSH2 failure requesting identities to ssh agent"); libssh2_agent_disconnect(mscb->agent); libssh2_agent_free(mscb->agent); mscb->agent = NULL; goto agent_done; } while (1) { rc = libssh2_agent_get_identity(mscb->agent, &identity, prev_identity); if (rc == 1) break; if (rc < 0) { log_info("\nmgr_ses: SSH2 failure obtaining identity from ssh agent"); rc = 1; break; } if (libssh2_agent_userauth(mscb->agent, user, identity)) { log_info("\nmgr_ses: SSH2 authentication with username %s and " "public key %s failed!", user, identity->comment); } else { log_info("\nmgr_ses: SSH2 authentication with username %s and " "public key %s succeeded!", user, identity->comment); authdone = TRUE; break; } prev_identity = identity; } agent_done: if (!authdone) log_info("\nmgr_ses: SSH2 trying next authentication method"); } if (!authdone && pkstr != NULL && pubkeyfile != NULL && privkeyfile != NULL) { boolean keyauthdone = FALSE; while (!keyauthdone) { status_t res = NO_ERR; xmlChar *expand_pubkey = ncx_get_source((const xmlChar *)pubkeyfile, &res); if (res != NO_ERR) { log_error("\nError: expand public key '%s' failed (%s)", pubkeyfile, get_error_string(res)); return res; } xmlChar *expand_privkey = ncx_get_source((const xmlChar *)privkeyfile, &res); if (res != NO_ERR) { log_error("\nError: expand private key '%s' failed (%s)", privkeyfile, get_error_string(res)); m__free(expand_pubkey); return res; } ret = libssh2_userauth_publickey_fromfile (mscb->session, user, (const char *)expand_pubkey, (const char *)expand_privkey, privkeypass ? privkeypass : ""); m__free(expand_pubkey); m__free(expand_privkey); if (ret) { if (ret == LIBSSH2_ERROR_EAGAIN) { if (LOGDEBUG2) { log_debug2("\nlibssh2 public key connect EAGAIN"); } usleep(10); continue; } else { keyauthdone = TRUE; } if ((LOGINFO && password == NULL) || LOGDEBUG2) { const char *logstr = NULL; switch (ret) { #ifdef LIBSSH2_ERROR_ALLOC case LIBSSH2_ERROR_ALLOC: logstr = "libssh2 internal memory error"; break; #endif #ifdef LIBSSH2_ERROR_SOCKET_SEND case LIBSSH2_ERROR_SOCKET_SEND: logstr = "libssh2 unable to send data on socket"; break; #endif #ifdef LIBSSH2_ERROR_SOCKET_TIMEOUT case LIBSSH2_ERROR_SOCKET_TIMEOUT: logstr = "libssh2 socket timeout"; break; #endif #ifdef LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED case LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED: logstr = "libssh2 username/public key invalid"; break; #endif #ifdef LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED: logstr = "libssh2 username/public key invalid"; break; #endif #ifdef LIBSSH2_ERROR_FILE case LIBSSH2_ERROR_FILE: logstr = "libssh2 password file needed"; break; #endif default: logstr = "libssh2 general error"; } log_info("\nmgr_ses: SSH2 publickey authentication " "failed:\n %s\n key file was '%s'\n", logstr, pubkeyfile); if (password == NULL) { return ERR_NCX_AUTH_FAILED; } } } else { if (LOGDEBUG2) { log_debug2("\nmgr_ses: public key login succeeded"); } authdone = TRUE; keyauthdone = TRUE; } } } if (!authdone && password != NULL && strstr(userauthlist, "password") != NULL) { boolean passauthdone = FALSE; while (!passauthdone) { ret = libssh2_userauth_password(mscb->session, user, password); if (ret) { if (ret == LIBSSH2_ERROR_EAGAIN) { if (LOGDEBUG2) { log_debug2("\nlibssh2 password connect EAGAIN"); } usleep(10); continue; } else { passauthdone = TRUE; } if (LOGINFO) { const char *logstr = NULL; switch (ret) { #ifdef LIBSSH2_ERROR_ALLOC case LIBSSH2_ERROR_ALLOC: logstr = "libssh2 internal memory error"; break; #endif #ifdef LIBSSH2_ERROR_SOCKET_SEND case LIBSSH2_ERROR_SOCKET_SEND: logstr = "libssh2 unable to send data on socket"; break; #endif #ifdef LIBSSH2_ERROR_PASSWORD_EXPIRED case LIBSSH2_ERROR_PASSWORD_EXPIRED: logstr = "libssh2 password expired"; break; #endif #ifdef LIBSSH2_ERROR_AUTHENTICATION_FAILED case LIBSSH2_ERROR_AUTHENTICATION_FAILED: logstr = "libssh2 username/password invalid"; break; #endif default: logstr = "libssh2 general error"; } log_info("\nmgr_ses: SSH2 password authentication " "failed: %s", logstr); } return ERR_NCX_AUTH_FAILED; } else { if (LOGDEBUG2) { log_debug2("\nmgr_ses: password login succeeded"); } authdone = TRUE; passauthdone = TRUE; } } } } if (!authdone) { if (password == NULL) { log_error("\nError: New session failed: no password " "provided\n"); } else { log_error("\nError: New session failed: authentication " "failed!\n"); } return ERR_NCX_AUTH_FAILED; } /* Request a shell */ mscb->channel = libssh2_channel_open_session(mscb->session); if (!mscb->channel) { if (LOGINFO) { log_info("\nmgr_ses: SSH2 channel open failed"); } return ERR_NCX_SESSION_FAILED; } /* Connect to the NETCONF subsystem */ ret = libssh2_channel_subsystem(mscb->channel, "netconf"); if (ret) { if (LOGINFO) { log_info("\nmgr_ses: Unable to request netconf subsystem"); } return ERR_NCX_SESSION_FAILED; } /* set IO mode to non-blocking; * the mgr_io module will use 'select' to control read/write * to the TCP connection, but the libssh2 IO functions will * still be used */ libssh2_channel_set_blocking(mscb->channel, 0); libssh2_channel_handle_extended_data2 (mscb->channel, LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE); return NO_ERR; } /* ssh2_setup */ /******************************************************************** * FUNCTION tcp_setup * * Setup the NETCONF over TCP session * Connection MUST already established * Sends tail-f session start message * * INPUTS: * scb == session control block * user == username to connect as * *********************************************************************/ static void tcp_setup (ses_cb_t *scb, const xmlChar *user) { const char *str; char buffer[64]; /* send the tail-f TCP startup message */ ses_putchar(scb, '['); ses_putstr(scb, user); ses_putchar(scb, ';'); #if 0 char addrbuff[16]; socklen_t socklen = 16; int ret = getsockname(scb->fd, addrbuff, &socklen); inet_ntop(AF_INET, addrbuff, buffer, 64); ses_putstr(scb, (const xmlChar *)buffer); #else /* use bogus address for now */ ses_putstr(scb, (const xmlChar *)"10.0.0.0"); #endif ses_putchar(scb, '/'); /* print bogus port number for now */ ses_putstr(scb, (const xmlChar *)"10000"); ses_putstr(scb, (const xmlChar *)";tcp;"); snprintf(buffer, sizeof(buffer), "%u;", (uint32)getuid()); ses_putstr(scb, (const xmlChar *)buffer); snprintf(buffer, sizeof(buffer), "%u;", (uint32)getgid()); ses_putstr(scb, (const xmlChar *)buffer); /* additional group IDs is empty */ ses_putchar(scb, ';'); str = (const char *)ncxmod_get_home(); if (str != NULL) { ses_putstr(scb, (const xmlChar *)str); ses_putchar(scb, ';'); } else { ses_putstr(scb, (const xmlChar *)"/tmp;"); } /* groups list is empty */ ses_putchar(scb, ';'); ses_putchar(scb, ']'); ses_putchar(scb, '\n'); ses_msg_finish_outmsg(scb); } /* tcp_setup */ /******************************************************************** * FUNCTION tcp_setup_direct * * Setup the NETCONF over TCP session * Connection MUST already established * Sends ssh subsystem session start message * * INPUTS: * scb == session control block * user == username to connect as * *********************************************************************/ static void tcp_setup_direct (ses_cb_t *scb, const xmlChar *user, unsigned short port) { const char *str; char buffer[1024]; sprintf(buffer, "\n]]>]]>", user, (unsigned int)port); ses_putstr(scb, (const xmlChar *)buffer); ses_msg_finish_outmsg(scb); } /* tcp_setup_direct */ /******************************************************************** * FUNCTION log_ssh2_error * * Get the ssh2 error message and print to error log * * INPUTS: * scb == session control block to use * mscb == manager control block to use * operation == string to use for operation (read, write, open) * *********************************************************************/ static void log_ssh2_error (ses_cb_t *scb, mgr_scb_t *mscb, const char *operation) { char *errormsg; int errcode; errcode = 0; errormsg = NULL; errcode = libssh2_session_last_error(mscb->session, &errormsg, NULL, 0); if (errcode != LIBSSH2_ERROR_EAGAIN) { log_error("\nmgr_ses: channel %s failed on session %u (a:%u)", operation, scb->sid, mscb->agtsid); if (LOGINFO) { log_info(" (%d:%s)", errcode, (errormsg) ? errormsg : "--"); } } } /* log_ssh2_error */ /******************************************************************** * FUNCTION check_channel_eof * * Check if the channel_eof condition is true * * INPUTS: * scb == session control block to use * mscb == manager control block to use * * RETURNS: * 1 if channel EOF condition is true * 0 if channel OK *********************************************************************/ static int check_channel_eof (ses_cb_t *scb, mgr_scb_t *mscb) { int ret; ret = libssh2_channel_eof(mscb->channel); #ifdef MGR_SES_DEBUG_EOF if (LOGDEBUG4) { log_debug4("\nmgr_ses: test channel EOF: ses(%u) ret(%d)", scb->sid, ret); } #endif if (LOGDEBUG && ret) { log_debug("\nmgr_ses: channel closed by server: ses(%u)", scb->sid); } return ret; } /* check_channel_eof */ /************ E X T E R N A L F U N C T I O N S *****************/ /******************************************************************** * FUNCTION mgr_ses_init * * Initialize the session manager module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ void mgr_ses_init (void) { uint32 i; if (!mgr_ses_init_done) { for (i=0; i must be received * before any requests can be sent * * The pubkeyfile and privkeyfile parameters will be used first. * If this fails, the password parameter will be checked * * INPUTS: * user == user name * password == user password * pubkeyfile == filespec for client public key * privkeyfile == filespec for client private key * privkeypass == passphrase to unlock private key * target == ASCII IP address or DNS hostname of target * port == NETCONF port number to use, or 0 to use defaults * transport == enum for the transport to use (SSH or TCP) * progcb == temp program instance control block, * == NULL if a session temp files control block is not * needed * retsid == address of session ID output * getvar_cb == XPath get varbind callback function * params_val == pointer to val_value_t that may contain * a session-specific --protocols variable to set * == NULL if not used * * OUTPUTS: * *retsid == session ID, if no error * * RETURNS: * status *********************************************************************/ status_t mgr_ses_new_session2 (const xmlChar *user, const xmlChar *password, const char *pubkeyfile, const char *privkeyfile, const xmlChar *privkeypass, boolean ssh_use_agent, const xmlChar *target, uint16 port, ses_transport_t transport, ncxmod_temp_progcb_t *progcb, ses_id_t *retsid, xpath_getvar_fn_t getvar_fn, val_value_t *params_val) { ses_cb_t *scb; mgr_scb_t *mscb; status_t res; struct hostent *hent; uint i, slot; int tcp_direct_enable = 0; val_value_t* input_val; val_value_t* val; uint32_t timeout; if(transport == SES_TRANSPORT_TCP_DIRECT) { tcp_direct_enable = 1; transport = SES_TRANSPORT_TCP; } #ifdef DEBUG if (user == NULL || target == NULL || retsid == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif if (!mgr_ses_init_done) { mgr_ses_init(); } *retsid = 0; /* check if any sessions are available */ if (!next_sesid) { /* end has already been reached, so now in brute force * session reclaim mode */ slot = 0; for (i=1; imgrcb = mscb; /* make sure at least 1 outbuff buffer is available */ res = ses_msg_new_buff(scb, TRUE, &scb->outbuff); if (res != NO_ERR) { mgr_free_scb(scb->mgrcb); scb->mgrcb = NULL; ses_free_scb(scb); return res; } /* initialize the static fields */ scb->type = SES_TYP_NETCONF; scb->transport = transport; scb->state = SES_ST_INIT; scb->mode = SES_MODE_XML; scb->state = SES_ST_INIT; scb->instate = SES_INST_IDLE; if (transport == SES_TRANSPORT_SSH) { scb->rdfn = mgr_ses_readfn; scb->wrfn = mgr_ses_writefn; } /* get a temp files directory for this session */ if (progcb != NULL) { res = NO_ERR; mscb->temp_progcb = progcb; mscb->temp_sescb = ncxmod_new_session_tempdir(progcb, slot, &res); if (mscb->temp_sescb == NULL) { mgr_free_scb(scb->mgrcb); scb->mgrcb = NULL; ses_free_scb(scb); return res; } } /* not important if the user name is missing on the manager */ scb->username = xml_strdup((const xmlChar *)user); mscb->target = xml_strdup(target); mscb->getvar_fn = getvar_fn; //input_val = val_find_child(params_val, NULL, "input"); //assert(input_val!=NULL); val = val_find_child(params_val, NULL, "timeout"); assert(val); //timeout = 30*1000; timeout = VAL_UINT32(val)*1000; /* get the hostname for the specified target */ hent = gethostbyname((const char *)target); if (hent) { /* entry OK, try to get a TCP connection to server */ res = connect_to_server(scb, hent, port, timeout); } else { res = ERR_NCX_UNKNOWN_HOST; } /* if TCP OK, check get an SSH connection and channel */ if (res == NO_ERR && transport == SES_TRANSPORT_SSH) { res = ssh2_setup(scb, (const char *)user, (const char *)password, pubkeyfile, privkeyfile, (const char *)privkeypass, ssh_use_agent); } else if (res == NO_ERR && transport == SES_TRANSPORT_TCP) { int flags = fcntl(scb->fd, F_GETFL, 0); fcntl(scb->fd, F_SETFL, flags | O_NONBLOCK); } if (res != NO_ERR) { mgr_free_scb(scb->mgrcb); scb->mgrcb = NULL; ses_free_scb(scb); return res; } /* do not have a session-ID from the server yet * so use the address of the scb in the ready blocks */ scb->sid = slot; scb->inready.sid = slot; scb->outready.sid = slot; /* add the FD to SCB mapping in the definition registry */ if (res == NO_ERR) { res = def_reg_add_scb(scb->fd, scb); } if (res == NO_ERR) { if (transport == SES_TRANSPORT_TCP) { /* force base:1.0 framing for TCP */ ses_set_protocols_requested(scb, NCX_PROTO_NETCONF10); } else if (params_val != NULL) { res = val_set_ses_protocols_parm(scb, params_val); } } if (res == NO_ERR && transport == SES_TRANSPORT_TCP) { if(tcp_direct_enable) { tcp_setup_direct(scb, user, port); } else { tcp_setup(scb, user); } } scb->dump_output_data=NULL; scb->dump_input_data=NULL; scb->dump_output_timestamps=NULL; scb->dump_input_timestamps=NULL; if (res == NO_ERR) { if (params_val != NULL) { char* str; val_value_t* val; val = val_find_child(params_val, NULL, "dump-session"); if(val!=NULL) { str=malloc(strlen("out")+strlen(VAL_STRING(val))+1); sprintf(str,"%sout",VAL_STRING(val)); scb->dump_output_data=fopen(str,"w"); assert(scb->dump_output_data!=NULL); free(str); str=malloc(strlen("out.ts")+strlen(VAL_STRING(val))+1); sprintf(str,"%sout.ts",VAL_STRING(val)); scb->dump_output_timestamps=fopen(str,"w"); assert(scb->dump_output_timestamps!=NULL); free(str); str=malloc(strlen("in")+strlen(VAL_STRING(val))+1); sprintf(str,"%sin",VAL_STRING(val)); scb->dump_input_data=fopen(str,"w"); assert(scb->dump_input_data!=NULL); free(str); str=malloc(strlen("in.ts")+strlen(VAL_STRING(val))+1); sprintf(str,"%sin.ts",VAL_STRING(val)); scb->dump_input_timestamps=fopen(str,"w"); assert(scb->dump_input_timestamps!=NULL); free(str); } } } /* send the manager hello to the server */ if (res == NO_ERR) { res = mgr_hello_send(scb); } if (res != NO_ERR) { mgr_free_scb(scb->mgrcb); scb->mgrcb = NULL; ses_free_scb(scb); scb = NULL; } else { ncx_set_temp_modQ(&mscb->temp_modQ); scb->state = SES_ST_HELLO_WAIT; mgrses[slot] = scb; if (++next_sesid==MGR_SES_MAX_SESSIONS) { /* reached the end, start hunting for open slots instead */ next_sesid = 0; } } *retsid = slot; return res; } /* mgr_ses_new_session */ /******************************************************************** * FUNCTION mgr_ses_new_session * * Create a new session control block and * start a NETCONF session with to the specified server * * After this functions returns OK, the session state will * be in HELLO_WAIT state. An server must be received * before any requests can be sent * * The pubkeyfile and privkeyfile parameters will be used first. * If this fails, the password parameter will be checked * * INPUTS: * user == user name * password == user password * pubkeyfile == filespec for client public key * privkeyfile == filespec for client private key * target == ASCII IP address or DNS hostname of target * port == NETCONF port number to use, or 0 to use defaults * transport == enum for the transport to use (SSH or TCP) * progcb == temp program instance control block, * == NULL if a session temp files control block is not * needed * retsid == address of session ID output * getvar_cb == XPath get varbind callback function * params_val == pointer to val_value_t that may contain * a session-specific --protocols variable to set * == NULL if not used * * OUTPUTS: * *retsid == session ID, if no error * * RETURNS: * status *********************************************************************/ status_t mgr_ses_new_session (const xmlChar *user, const xmlChar *password, const char *pubkeyfile, const char *privkeyfile, boolean ssh_use_agent, const xmlChar *target, uint16 port, ses_transport_t transport, ncxmod_temp_progcb_t *progcb, ses_id_t *retsid, xpath_getvar_fn_t getvar_fn, val_value_t *params_val) { return mgr_ses_new_session2 (user, password, pubkeyfile, privkeyfile, ""/*privkeypass*/, ssh_use_agent, target, port, transport, progcb, retsid, getvar_fn, params_val); } /******************************************************************** * FUNCTION mgr_ses_free_session * * Free a real session control block * * INPUTS: * scb == session control block to free * *********************************************************************/ void mgr_ses_free_session (ses_id_t sid) { ses_cb_t *scb; #ifdef DEBUG if (!mgr_ses_init_done) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif /* check if the session exists */ scb = mgrses[sid]; if (!scb) { if (LOGINFO) { log_info("\nmgr_ses: delete invalid session (%d)", sid); } return; } /* close the session before deactivating the IO */ if (scb->mgrcb) { ncx_clear_temp_modQ(); mgr_free_scb(scb->mgrcb); scb->mgrcb = NULL; } /* deactivate the session IO */ if (scb->fd) { mgr_io_deactivate_session(scb->fd); def_reg_del_scb(scb->fd); } ncxmod_clear_altpath(); ncx_reset_modQ(); ncx_clear_session_modQ(); ses_free_scb(scb); mgrses[sid] = NULL; } /* mgr_ses_free_session */ /******************************************************************** * FUNCTION mgr_ses_new_dummy_session * * Create a dummy session control block * * INPUTS: * none * RETURNS: * pointer to initialized dummy SCB, or NULL if malloc error *********************************************************************/ ses_cb_t * mgr_ses_new_dummy_session (void) { ses_cb_t *scb; if (!mgr_ses_init_done) { mgr_ses_init(); } /* check if dummy session already cached */ if (mgrses[0] != NULL) { SET_ERROR(ERR_INTERNAL_INIT_SEQ); return NULL; } /* no, so create it */ scb = ses_new_dummy_scb(); if (!scb) { return NULL; } mgrses[0] = scb; return scb; } /* mgr_ses_new_dummy_session */ /******************************************************************** * FUNCTION mgr_ses_free_dummy_session * * Free a dummy session control block * * INPUTS: * scb == session control block to free * *********************************************************************/ void mgr_ses_free_dummy_session (ses_cb_t *scb) { #ifdef DEBUG if (scb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (!mgr_ses_init_done) { SET_ERROR(ERR_INTERNAL_VAL); return; } if (scb->sid != 0 || mgrses[0] == NULL) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif ses_free_scb(scb); mgrses[0] = NULL; } /* mgr_ses_free_dummy_session */ /******************************************************************** * FUNCTION mgr_ses_process_first_ready * * Check the readyQ and process the first message, if any * * RETURNS: * TRUE if a message was processed * FALSE if the readyQ was empty *********************************************************************/ boolean mgr_ses_process_first_ready (void) { ses_cb_t *scb; ses_ready_t *rdy; ses_msg_t *msg; status_t res; uint32 cnt; xmlChar buff[32]; rdy = ses_msg_get_first_inready(); if (!rdy) { return FALSE; } /* get the session control block that rdy is embedded into */ scb = mgrses[rdy->sid]; if (!scb) { return FALSE; } #ifdef MGR_SES_DEBUG if (LOGDEBUG2) { log_debug2("\nmgr_ses: msg ready for session"); } #endif /* check the session control block state */ if (scb->state >= SES_ST_SHUTDOWN_REQ) { /* don't process the message or even it mark it * It will be cleaned up when the session is freed */ return TRUE; } /* make sure a message is really there */ msg = (ses_msg_t *)dlq_firstEntry(&scb->msgQ); if (!msg || !msg->ready) { SET_ERROR(ERR_INTERNAL_PTR); log_error("\nmgr_ses: ready Q message not correct"); return FALSE; } else if (LOGDEBUG2) { cnt = xml_strcpy(buff, (const xmlChar *)"Incoming msg for session "); sprintf((char *)(&buff[cnt]), "%u", scb->sid); ses_msg_dump(msg, buff); } /* setup the XML parser */ if (scb->reader) { /* reset the xmlreader */ res = xml_reset_reader_for_session(ses_read_cb, NULL, scb, scb->reader); } else { res = xml_get_reader_for_session(ses_read_cb, NULL, scb, &scb->reader); } /* process the message */ if (res == NO_ERR) { /* process the message */ mgr_top_dispatch_msg(scb); } else { log_error("\nReset xmlreader failed for session"); scb->state = SES_ST_SHUTDOWN_REQ; } /* free the message that was just processed */ dlq_remove(msg); ses_msg_free_msg(scb, msg); /* check if any messages left for this session */ msg = (ses_msg_t *)dlq_firstEntry(&scb->msgQ); if (msg && msg->ready) { ses_msg_make_inready(scb); } return TRUE; } /* mgr_ses_process_first_ready */ /******************************************************************** * FUNCTION mgr_ses_fill_writeset * * Drain the ses_msg outreadyQ and set the specified fdset * Used by mgr_ncxserver write_fd_set * * INPUTS: * fdset == pointer to fd_set to fill * maxfdnum == pointer to max fd int to fill in * * OUTPUTS: * *fdset is updated in * *maxfdnum may be updated * * RETURNS: * number of ready-for-output sessions found *********************************************************************/ uint32 mgr_ses_fill_writeset (fd_set *fdset, int *maxfdnum) { ses_ready_t *rdy; ses_cb_t *scb; boolean done; uint32 cnt; cnt = 0; FD_ZERO(fdset); done = FALSE; while (!done) { rdy = ses_msg_get_first_outready(); if (!rdy) { done = TRUE; } else { scb = mgrses[rdy->sid]; if (scb && scb->state < SES_ST_SHUTDOWN_REQ) { cnt++; FD_SET(scb->fd, fdset); if (scb->fd > *maxfdnum) { *maxfdnum = scb->fd; } } } } return cnt; } /* mgr_ses_fill_writeset */ /******************************************************************** * FUNCTION mgr_ses_get_first_outready * * Get the first ses_msg outreadyQ entry * Will remove the entry from the Q * * RETURNS: * pointer to the session control block of the * first session ready to write output to a socket * or the screen or a file *********************************************************************/ ses_cb_t * mgr_ses_get_first_outready (void) { ses_ready_t *rdy; ses_cb_t *scb; rdy = ses_msg_get_first_outready(); if (rdy) { scb = mgrses[rdy->sid]; if (scb && scb->state < SES_ST_SHUTDOWN_REQ) { return scb; } } return NULL; } /* mgr_ses_get_first_outready */ /******************************************************************** * FUNCTION mgr_ses_get_first_session * * Get the first active session * * RETURNS: * pointer to the session control block of the * first active session found *********************************************************************/ ses_cb_t * mgr_ses_get_first_session (void) { ses_cb_t *scb; uint32 i; /* skip zero (dummy) session */ for (i = 1; i < MGR_SES_MAX_SESSIONS; i++) { scb = mgrses[i]; if (scb && scb->state < SES_ST_SHUTDOWN_REQ) { return scb; } } return NULL; } /* mgr_ses_get_first_session */ /******************************************************************** * FUNCTION mgr_ses_get_next_session * * Get the next active session * * INPUTS: * curscb == current session control block to use * * RETURNS: * pointer to the session control block of the * next active session found * *********************************************************************/ ses_cb_t * mgr_ses_get_next_session (ses_cb_t *curscb) { ses_cb_t *scb; uint32 i; for (i = curscb->sid+1; i < MGR_SES_MAX_SESSIONS; i++) { scb = mgrses[i]; if (scb && scb->state < SES_ST_SHUTDOWN_REQ) { return scb; } } return NULL; } /* mgr_ses_get_next_session */ /******************************************************************** * FUNCTION mgr_ses_readfn * * Read callback function for a manager SSH2 session * * INPUTS: * s == session control block * buff == buffer to fill * bufflen == length of buff * erragain == address of return EAGAIN flag * * OUTPUTS: * *erragain == TRUE if ret value < zero means * read would have blocked (not really an error) * FALSE if the ret value is not < zero, or * if it is, it is a real error * * RETURNS: * number of bytes read; -1 for error; 0 for connection closed *********************************************************************/ ssize_t mgr_ses_readfn (void *s, char *buff, size_t bufflen, boolean *erragain) { ses_cb_t *scb; mgr_scb_t *mscb; int ret; int bytes_read; #ifdef DEBUG if (!s || !buff || !erragain) { SET_ERROR(ERR_INTERNAL_PTR); return (ssize_t) -1; } #endif *erragain = FALSE; scb = (ses_cb_t *)s; mscb = mgr_ses_get_mscb(scb); if (check_channel_eof(scb, mscb)) { ret = 0; } else { ret = libssh2_channel_read(mscb->channel, buff, bufflen); if (ret != LIBSSH2_ERROR_EAGAIN && LOGDEBUG3) { log_debug3("\nmgr_ses: read channel ses(%u) ret(%d)", scb->sid, ret); } } mscb->returncode = ret; if (ret < 0) { if (ret == LIBSSH2_ERROR_EAGAIN) { *erragain = TRUE; } else { log_ssh2_error(scb, mscb, "read"); } } else if (ret > 0) { bytes_read = ret; if (LOGDEBUG2) { log_debug2("\nmgr_ses: channel read %d bytes OK " "on session %u (a:%u)", bytes_read, scb->sid, mscb->agtsid); } if (scb->dump_input_data != NULL) { if(fwrite(buff,bytes_read,1,scb->dump_input_data) != 1) { assert(0); } fflush(scb->dump_input_data); } if (scb->dump_input_timestamps != NULL) { int ret; struct timespec tp; char tsbuf[]="0123456789.123456789 0123456789\n"; ret = clock_gettime(CLOCK_MONOTONIC, &tp); sprintf(tsbuf,"%010u.%09u %u\n",(unsigned int)tp.tv_sec,(unsigned int)tp.tv_nsec, (unsigned int)bytes_read); if (ret || (fwrite (tsbuf, strlen(tsbuf), 1, scb->dump_input_timestamps) != 1)) { assert(0); } fflush(scb->dump_input_timestamps); } } else { if (LOGDEBUG2) { log_debug2("\nmgr_ses: channel closed on session %u (a:%u)", scb->sid, mscb->agtsid); } mscb->closed = TRUE; } /* check if the buffer ended with an EOF * !!! this is not supported; any SSH-EOF message from * !!! the server will cause the client to drop the session * !!! right away and not process the bytes in the return buffer * !!! */ if (mscb->closed == FALSE && ret > 0) { if (check_channel_eof(scb, mscb)) { mscb->closed = TRUE; if (LOGINFO) { /* buffer is not a z-terminated string */ size_t maxlen = min(bufflen-1, (size_t)ret); buff[maxlen] = 0; log_info("\nDiscarding final buffer with EOF " "on session %u\n%s", mscb->agtsid, buff); } ret = 0; } } return (ssize_t)ret; } /* mgr_ses_readfn */ /******************************************************************** * FUNCTION mgr_ses_writefn * * Write callback function for a manager SSH2 session * * INPUTS: * s == session control block * * RETURNS: * status of the write operation *********************************************************************/ status_t mgr_ses_writefn (void *s) { ses_cb_t *scb; mgr_scb_t *mscb; ses_msg_buff_t *buff; int ret; uint32 i; boolean done; status_t res; scb = (ses_cb_t *)s; mscb = mgr_ses_get_mscb(scb); ret = 0; res = NO_ERR; /* go through buffer outQ */ buff = (ses_msg_buff_t *)dlq_deque(&scb->outQ); if (!buff) { if (LOGINFO) { log_info("\nmgr_ses: channel write no out buffer"); } } if (check_channel_eof(scb, mscb)) { res = ERR_NCX_SESSION_CLOSED; } while (buff) { if (res == NO_ERR) { ses_msg_add_framing(scb, buff); done = FALSE; while (!done) { ret = libssh2_channel_write(mscb->channel, (char *)&buff->buff[buff->buffstart], buff->bufflen); if (ret < 0 || ret != (int)buff->bufflen) { if (ret == LIBSSH2_ERROR_EAGAIN) { continue; } log_ssh2_error(scb, mscb, "write"); } done = TRUE; } if (ret == 0) { res = ERR_NCX_SESSION_CLOSED; } else if (ret > 0) { if (LOGDEBUG2) { log_debug2("\nmgr_ses: channel write %u bytes OK " "on session %u (a:%u)", buff->bufflen, scb->sid, mscb->agtsid); } if (LOGDEBUG3) { for (i=0; i < buff->bufflen; i++) { log_debug3("%c", buff->buff[i]); } } if (scb->dump_output_data != NULL) { if(fwrite(&buff->buff[buff->buffstart],buff->bufflen,1,scb->dump_output_data) != 1) { assert(0); } fflush(scb->dump_output_data); } if (scb->dump_output_timestamps != NULL) { struct timespec tp; char buf[]="0123456789.123456789 0123456789\n"; ret = clock_gettime(CLOCK_MONOTONIC, &tp); sprintf(buf,"%010u.%09u %d\n",(unsigned int)tp.tv_sec,(unsigned int)tp.tv_nsec, (unsigned int)buff->bufflen); if (ret || (fwrite (buf, strlen(buf), 1, scb->dump_output_timestamps) != 1)) { assert(0); } fflush(scb->dump_output_timestamps); } } else { res = ERR_NCX_OPERATION_FAILED; } } ses_msg_free_buff(scb, buff); if (res == NO_ERR) { buff = (ses_msg_buff_t *)dlq_deque(&scb->outQ); } else { buff = NULL; } } return res; } /* mgr_ses_writefn */ /******************************************************************** * FUNCTION mgr_ses_get_scb * * Retrieve the session control block * * INPUTS: * sid == manager session ID (not server assigned ID) * * RETURNS: * pointer to session control block or NULL if invalid *********************************************************************/ ses_cb_t * mgr_ses_get_scb (ses_id_t sid) { if (sid==0 || sid>=MGR_SES_MAX_SESSIONS) { return NULL; } return mgrses[sid]; } /* mgr_ses_get_scb */ /******************************************************************** * FUNCTION mgr_ses_get_mscb * * Retrieve the manager session control block * * INPUTS: * scb == session control block to use * * RETURNS: * pointer to manager session control block or NULL if invalid *********************************************************************/ mgr_scb_t * mgr_ses_get_mscb (ses_cb_t *scb) { #ifdef DEBUG if (!scb) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (mgr_scb_t *)scb->mgrcb; } /* mgr_ses_get_mscb */ /* END file mgr_ses.c */ yuma123_2.14/netconf/src/check-copyright.py0000664000175000017500000001003214770023131020777 0ustar vladimirvladimir#!/usr/bin/env python import sys import os import subprocess copyright = " * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved.\n" copyright_start = " * Copyright (c) " # ----------------------------------------------------------------------------| class NullFilter: def apply( self, filename ): """Apply a 'null' filter to the filename supplied.""" return True # ----------------------------------------------------------------------------| class EndswithFilenameFilter: def __init__( self, filters = [ '.c', '.h', '.cpp', '.inl' ] ): self.filters_ = filters def apply( self, filename ): """Apply an 'endswith' filter to the filename supplied.""" for f in self.filters_: if filename.endswith( f ): return True return False # ----------------------------------------------------------------------------| class StartswithFilenameFilter: def __init__( self, filters = [ 'M ', 'A ' ] ): self.filters_ = filters def apply( self, filename ): """Apply an 'startswith' filter to the filename supplied.""" for f in self.filters_: if filename.startswith( f ): return True return False # ----------------------------------------------------------------------------| class SVNModifiedFilenameFilter: def __init__( self ): self.startFilter = StartswithFilenameFilter() self.endFilter = EndswithFilenameFilter() def apply( self, filename ): """Apply the filter""" if self.startFilter.apply( filename ) and self.endFilter.apply( filename ): return True return False # ----------------------------------------------------------------------------| def DisplayUsage(): print ( """Usage: check-len.py [-a] Check the line length of modified netconf source files. [-a] : Check all source files.""" ) sys.exit(-1) # ----------------------------------------------------------------------------| def GetAllFilenames( rootDir = "./", filenameFilter = NullFilter() ): """Get the list of all filenames matching the supplied filter""" filenames = [] for root, dirs, files in os.walk( rootDir ): filtered = [ n for n in files if filenameFilter.apply( n ) ] filenames += [ root + "/" + n for n in filtered ] return filenames # ----------------------------------------------------------------------------| def GetModifiedFiles(): svnOp = subprocess.getoutput( "svn status" ) svnOp = svnOp.split( '\n' ) filenameFilter = SVNModifiedFilenameFilter() filenames = [] for entry in svnOp: if filenameFilter.apply( entry ): filenames.append( entry.lstrip( "MA ") ) return filenames # ----------------------------------------------------------------------------| def CheckFile( filename ): """Check the copyright line as the file is being copied""" print("Checking %s...." %filename) copydone = 0 outfilename = filename + ".tmp" f = open( filename, 'r' ) of = open( outfilename, 'w' ) lines = f.readlines() for line in lines: if copydone == 0 and line.startswith(copyright_start): copydone = 1 of.write(copyright) else: of.write(line) f.close() of.close() if copydone: os.remove( filename ) os.rename( outfilename, filename ) else: os.remove( outfilename ) # ----------------------------------------------------------------------------| def CheckFiles( filenames ): """Check each of the files""" for filename in filenames: CheckFile( filename ) # ----------------------------------------------------------------------------| if __name__ == '__main__': if len ( sys.argv ) >1 : if sys.argv[1] == "-a": filenames = GetAllFilenames( filenameFilter = EndswithFilenameFilter() ) else: DisplayUsage() else: filenames = GetModifiedFiles() CheckFiles( filenames ) yuma123_2.14/netconf/src/yangdump/0000775000175000017500000000000014770023131017172 5ustar vladimirvladimiryuma123_2.14/netconf/src/yangdump/yangdump.c0000664000175000017500000000712414770023131021166 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangdump.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 01-nov-06 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include /* #define MEMORY_DEBUG 1 */ #ifdef MEMORY_DEBUG #include #endif #define _C_main 1 #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifndef _H_ydump #include "ydump.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static yangdump_cvtparms_t mycvtparms; /******************************************************************** * * * FUNCTION main * * * *********************************************************************/ int main (int argc, char *argv[]) { status_t res; #ifdef MEMORY_DEBUG mtrace(); #endif res = ydump_init(argc, argv, TRUE, &mycvtparms); if (res == NO_ERR) { res = ydump_main(&mycvtparms); /* if warnings+ are enabled, then res could be NO_ERR and still * have output to STDOUT */ if (res == NO_ERR) { if(mycvtparms.format!=NCX_CVTTYP_TREE) { log_warn("\n"); /*** producing extra blank lines ***/ } } print_errors(); print_error_count(); ydump_cleanup(&mycvtparms); } else { print_errors(); } print_error_count(); yang_final_memcheck(); #ifdef MEMORY_DEBUG muntrace(); #endif return (res == NO_ERR) ? 0 : 1; } /* main */ /* END yangdump.c */ yuma123_2.14/netconf/src/yangdump/Makefile.am0000664000175000017500000000404214770023131021226 0ustar vladimirvladimirbin_PROGRAMS = yangdump yangdump_SOURCES = \ $(top_srcdir)/netconf/src/yangdump/yangdump.c \ $(top_srcdir)/netconf/src/ydump/ydump.c \ $(top_srcdir)/netconf/src/ydump/c.c \ $(top_srcdir)/netconf/src/ydump/c_util.c \ $(top_srcdir)/netconf/src/ydump/cyang.c \ $(top_srcdir)/netconf/src/ydump/h.c \ $(top_srcdir)/netconf/src/ydump/html.c \ $(top_srcdir)/netconf/src/ydump/py_util.c \ $(top_srcdir)/netconf/src/ydump/sql.c \ $(top_srcdir)/netconf/src/ydump/tg2.c \ $(top_srcdir)/netconf/src/ydump/xsd.c \ $(top_srcdir)/netconf/src/ydump/xsd_typ.c \ $(top_srcdir)/netconf/src/ydump/xsd_util.c \ $(top_srcdir)/netconf/src/ydump/xsd_yang.c \ $(top_srcdir)/netconf/src/ydump/yangdump_util.c \ $(top_srcdir)/netconf/src/ydump/yangstats.c \ $(top_srcdir)/netconf/src/ydump/yangyin.c \ $(top_srcdir)/netconf/src/ydump/yangtree.c noinst_HEADERS= \ $(top_srcdir)/netconf/src/ydump/c.h \ $(top_srcdir)/netconf/src/ydump/c_util.h \ $(top_srcdir)/netconf/src/ydump/cyang.h \ $(top_srcdir)/netconf/src/ydump/h.h \ $(top_srcdir)/netconf/src/ydump/html.h \ $(top_srcdir)/netconf/src/ydump/py_util.h \ $(top_srcdir)/netconf/src/ydump/sql.h \ $(top_srcdir)/netconf/src/ydump/tg2.h \ $(top_srcdir)/netconf/src/ydump/xsd.h \ $(top_srcdir)/netconf/src/ydump/xsd_typ.h \ $(top_srcdir)/netconf/src/ydump/xsd_util.h \ $(top_srcdir)/netconf/src/ydump/xsd_yang.h \ $(top_srcdir)/netconf/src/ydump/yangdump.h \ $(top_srcdir)/netconf/src/ydump/yangdump_util.h \ $(top_srcdir)/netconf/src/ydump/yangstats.h \ $(top_srcdir)/netconf/src/ydump/yangtree.h \ $(top_srcdir)/netconf/src/ydump/yangyin.h \ $(top_srcdir)/netconf/src/ydump/ydump.h yangdump_CPPFLAGS = -I $(top_srcdir)/libtecla -I $(top_srcdir)/netconf/src/yangcli/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump -I$(top_srcdir)/netconf/src/yangrpc -I$(top_srcdir)/netconf/src/ydump yangdump_LDFLAGS = $(top_builddir)/netconf/src/ncx/libyumancx.la $(LIBS) dist_yuma123_yang_DATA = \ $(top_srcdir)/netconf/modules/yuma123/yangdump.yang yuma123_2.14/netconf/src/yangdump/wsdl_ex.txt0000664000175000017500000000125514770023131021403 0ustar vladimirvladimir yuma123_2.14/netconf/src/platform/0000775000175000017500000000000014770023131017172 5ustar vladimirvladimiryuma123_2.14/netconf/src/platform/platform.profile.depend0000664000175000017500000000172714770023131023645 0ustar vladimirvladimir# # platform.profile.depend # ######################### PROFILE ######################## # platform.profile must be included before this file!!! ################## MAKE DEPENDENCIES ##################### # following depend rule is the GNU version! Other versions TBD # this rule cannot be included by the top level makefile # so platform.profile.cmn was created to allow that Makefile # to define a different 'depend' rule. depend: dependencies dependencies: $(DEPS) @if [ ! -f Makefile ]; then \ echo "Error: Makefile missing!"; \ exit 1; \ fi @rm -f dependencies @for i in $(DEPS); do \ if [ -f $$i ] ; then \ (cat $$i >> dependencies; echo "" >> dependencies) ; \ else \ (echo "*** Warning: Dependency file $i.D is missing! (Skipping...) ***"; \ echo "# Warning: Missing file $$i !!!") ; \ fi; \ done @echo "" >> dependencies # delete the .D files to force make depend to rebuild them each time # that target is built # @rm -f $(DEPS) yuma123_2.14/netconf/src/platform/platform.profile0000664000175000017500000001162514770023131022405 0ustar vladimirvladimir# # platform.profile # # libncx and libagt versions (use the same!) # need some way to set versions without using # environment vars, so it works in plain debuild, rpmbuild # LIBNCX_MAJOR_VERSION=2 LIBNCX_MINOR_VERSION=2 SOVERSION=$(LIBNCX_MAJOR_VERSION).$(LIBNCX_MINOR_VERSION) # default DESTDIR is NULL; it is only used by packaging builds # set default PREFIX to /usr ifndef PREFIX PREFIX=/usr endif CINC= ifdef WINDOWS CINC += -I/usr/i586-mingw32msvc/include endif CINC +=-I. -I../agt -I../mgr \ -I../ncx -I../platform \ -I../ydump \ -I$(DESTDIR)$(PREFIX)/include \ -I$(DESTDIR)$(PREFIX)/include/libxml2 \ -I$(DESTDIR)$(PREFIX)/include/libxml2/libxml ifdef FREEBSD FPATH=-L/usr/local/lib CINC += -I/usr/local/include \ -I/usr/local/include/libxml2 \ -I/usr/local/include/libxml2/libxml endif # added /sw/include for MacOSX ifdef MAC # MACOSX version CINC +=-I/sw/include CFLAGS += -DMACOSX=1 endif TBASE=../../target ifdef DESTDIR LBASE=$(DESTDIR)$(PREFIX)/lib else LBASE=$(TBASE)/lib endif ### platform.profile.cmn included inline here!!! # # platform.profile.cmn # ifndef OWNER ifdef DESTDIR OWNER= else ifdef FREEBSD OWNER=-o root else ifdef MAC OWNER=-oroot else ifdef CYGWIN OWNER= else OWNER= --owner=root endif endif endif endif endif ifdef USE_WERROR WERROR=-Werror else WERROR= endif CWARN=-Wall -Wno-long-long -Wformat-y2k -Winit-self \ -Wswitch-default -Wunused-parameter \ -Wextra -Wundef -Wshadow -Wpointer-arith \ -Wwrite-strings -Wbad-function-cast -Wcast-qual \ -Waggregate-return -Wstrict-prototypes -Wold-style-definition \ -Wmissing-prototypes -Wmissing-declarations \ -Wpacked -Winvalid-pch \ -Wredundant-decls -Wnested-externs -Winline -std=gnu99 $(WERROR) # -Wunreachable-code removed due to -O3 # -O3 changed to -O2 due to code bloat from inline functions ifdef WINDOWS CDEFS=-DWINDOWS=1 -DGCC=1 else CDEFS=-DLINUX=1 -DGCC=1 endif ifndef PRODUCTION CDEFS += -DDEBUG endif ifdef FREEBSD CDEFS += -DFREEBSD endif ifdef KEY CDEFS += -DKEY=$(KEY) endif ifndef NOFLOAT CDEFS += -DHAS_FLOAT=1 endif ifdef LIB64 CDEFS += -DLIB64=1 endif CFLAGS+=$(CDEFS) $(CWARN) ifndef CYGWIN CFLAGS += -fPIC endif ifdef DEBUG CFLAGS += -ggdb3 else CFLAGS += -O2 endif # memory leak debugging mode ifdef MEMTRACE CFLAGS += -DMEMORY_DEBUG=1 endif # check if debian or RPM release build ifdef RELEASE CFLAGS += -DRELEASE=$(RELEASE) endif # check if cygwin build ifdef CYGWIN CFLAGS += -DCYGWIN=1 endif ifndef GRP ifdef MAC GRP= else ifdef FREEBSD GRP=-g wheel else ifdef DESTDIR GRP= else ifdef CYGWIN GRP= else GRP=--group=root endif endif endif endif endif ifdef STATIC LIBSUFFIX=a LIBNCXSUFFIX=a else ifdef MAC LIBSUFFIX=dylib LIBNCXSUFFIX=dylib else LIBSUFFIX=so LIBNCXSUFFIX=so.$(SOVERSION) endif endif ifdef WINDOWS CC=i586-mingw32msvc-gcc LINK=i586-mingw32msvc-gcc LIBTOOL=i586-mingw32msvc-ar RANLIB=i586-mingw32msvc-ranlib else CC=$(CROSS_TARGET)gcc LINK=$(CC) LIBTOOL=$(CROSS_TARGET)ar RANLIB=$(CROSS_TARGET)ranlib endif LINT=splint LINTFLAGS= '-weak -macrovarprefix "m_"' ##LIBFLAGS=-lsocket ifdef FREEBSD FPATH=-L/usr/local/lib CDEFS += -DFREEBSD endif #LFLAGS=-v --no-as-needed LFLAGS=-lm LPATH=-L$(LBASE) CEES = $(wildcard *.c) HEES = $(wildcard *.h) ################ OBJS RULE ############# OBJS = $(patsubst %.c,$(TARGET)/%.o,$(CEES)) ################ DEPS RULE ############# DEPS = $(patsubst %.c,%.D,$(wildcard *.c)) ######################## PLATFORM DEFINITIONS ############# PLATFORM_CPP= .PHONY: all superclean clean test install uninstall \ distclean depend lint notabs addheader ######################### MAKE RULES ###################### COMPILE.c= $(CC) $(CFLAGS) $(CPPFLAGS) $(PLATFORM_CPP) \ $(CINC) $(SUBDIR_CPP) $(TARGET_ARCH) -c $(TARGET)/%.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) $(PLATFORM_CPP) \ $(CINC) $(SUBDIR_CPP) $(TARGET_ARCH) -c -o $@ $< # Common library rule $(LBASE)/lib%.a: $(OBJS) $(LIBTOOL) cr $@ $(OBJS) $(RANLIB) $@ #### common cleanup rules lint: $(LINT) $(LINTFLAGS) $(CDEFS) $(CPPFLAGS) $(PLATFORM_CPP) \ $(CINC) $(SUBDIR_CPP) *.c # dependency rule to make temp .D files from .c sources # all the .D files are collected and appended to the # appropriate Makefile when 'make depend' is run # this rule is kept here to make sure it matches COMPILE.c %.D: %.c $(CC) -MM -MG -MT $(TARGET)/$(patsubst %.c,%.o,$<) \ -Wall -Wcomment $(CPPFLAGS) $(PLATFORM_CPP) $(CINC) \ $(SUBDIR_CPP) $(TARGET_ARCH) -c $< > $@ notabs: for c in $(CEES); do\ cp $$c $$c.save;\ expand $$c > $$c.ex;\ mv $$c.ex $$c;\ done addheader: if [ ! -f ../platform/header.txt]; then \ echo "Error: platform/header.txt is missing!"; \ exit 1; \ fi for c in $(CEES); do\ cp $$c $$c.save;\ cp ../platform/header.txt $$c.hdr;\ cat $$c >> $$c.hdr;\ mv $$c.hdr $$c;\ done for h in $(HEES); do\ cp $$h $$h.save;\ cp ../platform/header.txt $$h.hdr;\ cat $$h >> $$h.hdr;\ mv $$h.hdr $$h;\ done yuma123_2.14/netconf/src/platform/procdefs.h0000664000175000017500000001325214770023131021153 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_procdefs #define _H_procdefs #define _INC_PROCDEFS /* * File: procdefs.h * * Contains global constants and types used for portability * defines UNIX/MSDOS variant strings * */ /* no user source files should include limits.h * they should include procdefs.h instead, * and use the defined base types provided below */ #include #include #ifdef __cplusplus extern "C" { #endif #ifdef DEBUG #define CPP_DEBUG 1 #endif /* DEBUG */ #ifdef CPP_FULL_DEBUG #define CPP_MTRACE 1 #define CPP_ICHK 1 #define CPP_CHK_HEAP 1 #define CPP_FULL_TRACE 1 #endif /* CPP_FULL_DEBUG */ /* program environment and personality switches * SUN is the only supported unix platform at this time */ #if defined(SUN) || defined(LINUX) || defined(MACOSX) #define CPV_UNIX 1 #define CPV_MSC 0 #define CPV_ALLOW_FAR 0 #define CPV_DOS 0 #else /* !!! ASSUME MSC VERSION !!! */ #define CPV_UNIX 0 #define CPV_MSC 1 #define CPV_ALLOW_FAR 0 #define CPV_DOS 1 #endif /* SUN */ /* #define CPP_NO_MACROS 0 -- ifdefs still used */ /* CPP_DEBUG assigned at the command line */ /* CPP_CHK_HEAP assigned at the command line */ /* CPP_ICHK assigned at the command line */ #if CPV_UNIX #define NEWLN 0xa #else #define NEWLN 0xd #endif /* CPV_UNIX */ /* only the module that contains 'main' should define _C_main */ #ifdef _C_main #define PUBLIC #else #define PUBLIC extern #endif /* _C_main */ /* 16-bit word bit definitions */ #define bit0 0x0001 #define bit1 0x0002 #define bit2 0x0004 #define bit3 0x0008 #define bit4 0x0010 #define bit5 0x0020 #define bit6 0x0040 #define bit7 0x0080 #define bit8 0x0100 #define bit9 0x0200 #define bit10 0x0400 #define bit11 0x0800 #define bit12 0x1000 #define bit13 0x2000 #define bit14 0x4000 #define bit15 0x8000 /* 32 bit extended bit definitions */ #define bit16 0x00010000 #define bit17 0x00020000 #define bit18 0x00040000 #define bit19 0x00080000 #define bit20 0x00100000 #define bit21 0x00200000 #define bit22 0x00400000 #define bit23 0x00800000 #define bit24 0x01000000 #define bit25 0x02000000 #define bit26 0x04000000 #define bit27 0x08000000 #define bit28 0x10000000 #define bit29 0x20000000 #define bit30 0x40000000 #define bit31 0x80000000 #define m__setbit(F,B) F |= (B) #define m__clrbit(F,B) F &= ~(B) #define m__bitset(F,B) F & B #if CPV_UNIX #define STATIC #define CONST #define REG register #define REGVAR #define VOLATILE #define FAR #define NEAR #define PASCAL /* #define signed */ #define cdecl #define memicmp memcmp #define strcmpi strcasecmp #define _strcmpi strcasecmp #define _strdup strdup #define _chdir chdir #define _getcwd getcwd #else /*** CPV_DOS ***/ #define STATIC static #define CONST const #define REG register #define REGVAR register #define VOLATILE volatile #ifdef __STDC__ #ifndef WINDOWS #define strcmpi _strcmpi #endif #endif #if CPV_ALLOW_FAR #define FAR far #else #define FAR #endif /* CPV_ALLOW_FAR */ #endif /* CPV_UNIX */ #ifndef __cplusplus #ifndef min #define min(A,B) ((AB)?A:B) #endif #endif #ifdef CHAR_BIT #define BITS_PER_BYTE CHAR_BIT #else #define BITS_PER_BYTE 8 #endif /* CHAR_BIT */ /* assign debug flag */ #ifdef CPP_DEBUG #define DBG_STAMP "DEBUG" #else #define DBG_STAMP "NODEBUG" #endif /* CPP_DEBUG */ #if !CPV_UNIX #ifndef WINDOWS typedef unsigned int uint; #endif #else #include #endif /* !CPV_UNIX */ #include #include typedef uint8_t uchar; typedef uint8_t uint8; typedef uint16_t uint16; typedef uint32_t uint32; typedef uint64_t uint64; typedef int8_t int8; typedef int16_t int16; typedef int32_t int32; typedef int64_t int64; typedef uint8_t byte; typedef bool boolean; #ifdef SUN413_GNU typedef unsigned long ulong; #endif #if !CPV_UNIX #ifndef __STDC__ #ifndef _WCHAR_T_DEFINED /** MSC 7.0 **/ #define _WCHAR_T_DEFINED typedef unsigned short wchar_t; #endif /* WCHAR_T_DEFINED */ #endif /* __STDC__ */ #endif /* !CPV_UNIX */ #ifndef WCHAR_MAX #define WCHAR_MAX 0xffff #endif /* WCHAR_MAX */ #if CPV_UNIX typedef double LDBL; #else typedef long double LDBL; #endif /* CPV_UNIX */ /* MSC symbolic constants have to be faked in unix */ #if CPV_UNIX #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #endif /* CPV_UNIX */ /* c-portability adjustment ...(avoid lint error)--version for runtine code */ #if !defined(FALSE) && !defined(TRUE) #define FALSE false #define TRUE true #endif /* FALSE | TRUE */ /* memory for these vars in ncx/ncx.c */ extern uint32 malloc_cnt; extern uint32 free_cnt; #ifndef m__getMem #define m__getMem(X) malloc(X);malloc_cnt++ #endif /* m__getMem */ #ifndef m__free #define m__free(X) do { if ( X ) { free(X); free_cnt++; } } while(0) #endif /* m__free */ #ifndef m__getObj #define m__getObj(OBJ) (OBJ *)malloc(sizeof(OBJ));malloc_cnt++ #endif /* m__getObj */ #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_procdefs */ yuma123_2.14/netconf/src/platform/setversion.sh0000775000175000017500000000006114770023131021727 0ustar vladimirvladimirecho "#define RELEASE 5" > platform/curversion.h yuma123_2.14/netconf/src/platform/header.txt0000664000175000017500000000057714770023131021174 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ yuma123_2.14/netconf/src/ydump/0000775000175000017500000000000014770023131016504 5ustar vladimirvladimiryuma123_2.14/netconf/src/ydump/xsd_yang.c0000664000175000017500000020114414770023131020466 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xsd_yang.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 04feb08 abb split out from xsd_typ.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_xsd_typ #include "xsd_typ.h" #endif #ifndef _H_xsd_util #include "xsd_util.h" #endif #ifndef _H_xsd_yang #include "xsd_yang.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * F O R W A R D - - R E F S T A T I C F N S * * * *********************************************************************/ static status_t do_yang_elem_btype (ncx_module_t *mod, obj_template_t *obj, obj_template_t *augtargobj, boolean iskey, val_value_t *val); static status_t do_local_typedefs (ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *typnameQ); /******************************************************************** * FUNCTION add_type_mapping * * Create a xsd_typname_t struct for a local typedef, * and add it to the specified Q * * INPUTS * typ == typ_template_t struct to add to the Q * que == Q of xsd_typname_t struct to check * * RETURNS: * status *********************************************************************/ static status_t add_type_mapping (typ_template_t *typ, dlq_hdr_t *que) { ncx_typname_t *tn; xmlChar *buff, *p; uint32 idnum; boolean done; /* !!! THIS MAY NOT PRODUCE THE SAME XSD TYPENAMES IF * !!! RUN FROM A SUBMODULE, OR FROM THE TOP MODULE * !!! A SUBMODULE TOP FILE XSD IS ONLY SELF-CONSISTENT * !!! DEPENDING ON THE SUBSET OF SUBMODULES INCLUDED * !!! DIFFERENT TYPENAMES ACROSS INCLUDES == BROKEN XSDs */ tn = ncx_new_typname(); if (!tn) { return ERR_INTERNAL_MEM; } if (ncx_find_typname_type(que, typ->name)) { /* need to create a mapped typename */ buff = m__getMem(xml_strlen(typ->name) + 8); if (!buff) { ncx_free_typname(tn); return ERR_INTERNAL_MEM; } p = buff; p += xml_strcpy(p, typ->name); *p++ = '_'; /* separate the typename and the mapping ID */ idnum = 1; done = FALSE; while (!done) { sprintf((char *)p, "%u", idnum); if (ncx_find_typname_type(que, buff)) { if (++idnum > 999999) { ncx_free_typname(tn); m__free(buff); return SET_ERROR(ERR_BUFF_OVFL); } } else { tn->typ = typ; tn->typname_malloc = buff; tn->typname = tn->typname_malloc; dlq_enque(tn, que); done = TRUE; } } } else { /* this type name not in use yet -- use a direct mapping */ tn->typ = typ; tn->typname = typ->name; dlq_enque(tn, que); } return NO_ERR; } /* add_type_mapping */ /******************************************************************** * FUNCTION add_augtarget_subgrp * * Add a substitutionGroup attribute for an augment target * * INPUTS: * targobj == obj_template_t for the augment target parent node * elem == element value strut to add the substitutionGroup attribute into * * OUTPUT: * elem has substitutionGroup attribute added * * RETURNS: * status *********************************************************************/ static status_t add_augtarget_subgrp (obj_template_t *targobj, val_value_t *elem) { xmlChar *qname, *buff; status_t res; res = obj_gen_aughook_id(targobj, &buff); if (res != NO_ERR) { return res; } qname = xml_val_make_qname(targobj->tkerr.mod->nsid, buff); m__free(buff); if (!qname) { return res; } /* add the substitutionGroup attribute */ res = xml_val_add_attr(XSD_SUB_GRP, 0, qname, elem); if (res != NO_ERR) { m__free(qname); } return res; } /* add_augtarg_subgrp */ /******************************************************************** * FUNCTION do_typedefs_container * * Generate the typedef elements for any child type * * INPUTS: * mod == module conversion in progress * obj == obj_template_t for the child node to use * typnameQ == Q of typname_map_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each local type within this * container or any groups, or any children * * RETURNS: * status *********************************************************************/ static status_t do_typedefs_container (ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *typnameQ) { obj_container_t *con; typ_template_t *typ; status_t res; con = obj->def.container; /* check any local typedefs in this container */ for (typ = (typ_template_t *)dlq_firstEntry(con->typedefQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { res = add_type_mapping(typ, typnameQ); if (res != NO_ERR) { return res; } } /* check any local typedefs in any child objects */ res = xsd_do_typedefs_datadefQ(mod, con->datadefQ, typnameQ); if (res != NO_ERR) { return res; } /* check any typedefs in any groupings in this object */ res = xsd_do_typedefs_groupingQ(mod, con->groupingQ, typnameQ); return res; } /* do_typedefs_container */ /******************************************************************** * FUNCTION do_yang_elem_container * * Generate the element node for a container object type * * INPUTS: * mod == module conversion in progress * obj == object to convert to XSD element * augtargobj == obj_template_t for the target of the augment clause * if this is a call from add_yang_augment. This node will * be used to create the substitutionGroup for the top-level * element. Set to NULL if not used. * val == struct parent to contain child nodes for each type * of the schema element to contain types if * this is a typonly * * OUTPUTS: * val->v.childQ has entries added for simple element * * RETURNS: * status *********************************************************************/ static status_t do_yang_elem_container (ncx_module_t *mod, obj_template_t *obj, obj_template_t *augtargobj, val_value_t *val) { val_value_t *elem, *cpx, *seq, *annot; obj_template_t *child; status_t res; xmlns_id_t xsd_id; res = NO_ERR; xsd_id = xmlns_xs_id(); /* element struct of N arbitrary nodes */ elem = xml_val_new_struct(XSD_ELEMENT, xsd_id); if (!elem) { return ERR_INTERNAL_MEM; } else { val_add_child(elem, val); /* add early */ } /* add the name attribute */ res = xml_val_add_cattr(NCX_EL_NAME, 0, obj_get_name(obj), elem); if (res != NO_ERR) { return res; } /* check if a substitutionGroup is needed */ if (augtargobj) { res = add_augtarget_subgrp(augtargobj, elem); if (res != NO_ERR) { return res; } } /* check if an annotation is needed for the element */ annot = xsd_make_obj_annotation(obj, &res); if (res != NO_ERR) { return res; } else if (annot) { val_add_child(annot, elem); } /* next level is complexType */ cpx = xml_val_new_struct(XSD_CPX_TYP, xsd_id); if (!cpx) { return ERR_INTERNAL_MEM; } else { val_add_child(cpx, elem); /* add early */ } /* struct of N arbitrary nodes */ seq = xml_val_new_struct(XSD_SEQUENCE, xsd_id); if (!seq) { return ERR_INTERNAL_MEM; } else { val_add_child(seq, cpx); /* add early */ } /* go through all the child nodes and generate elements for them */ for (child = (obj_template_t *) dlq_firstEntry(obj_get_datadefQ(obj)); child != NULL && res==NO_ERR; child = (obj_template_t *)dlq_nextEntry(child)) { res = do_yang_elem_btype(mod, child, NULL, FALSE, seq); } if (res == NO_ERR) { res = xsd_add_aughook(seq); } return res; } /* do_yang_elem_container */ /******************************************************************** * FUNCTION do_yang_elem_anyxml * * Generate the element node for an anyxml object type * * INPUTS: * obj == object to convert to XSD element * val == struct parent to contain child nodes * * OUTPUTS: * val->v.childQ has entries added for complex content (xs:anyType) * * RETURNS: * status *********************************************************************/ static status_t do_yang_elem_anyxml (obj_template_t *obj, val_value_t *val) { val_value_t *elem, *cpx, *cpxcon, *annot, *ext; xmlChar *qname; status_t res; xmlns_id_t xsd_id; res = NO_ERR; xsd_id = xmlns_xs_id(); /* element struct of N arbitrary nodes */ elem = xml_val_new_struct(XSD_ELEMENT, xsd_id); if (!elem) { return ERR_INTERNAL_MEM; } else { val_add_child(elem, val); /* add early */ } /* add the name attribute */ res = xml_val_add_cattr(NCX_EL_NAME, 0, obj_get_name(obj), elem); if (res != NO_ERR) { return res; } /* add the minOccurs="0" attribute */ res = xml_val_add_cattr(XSD_MIN_OCCURS, 0, XSD_ZERO, elem); if (res != NO_ERR) { return res; } /* check if an annotation is needed for the element */ annot = xsd_make_obj_annotation(obj, &res); if (res != NO_ERR) { return res; } else if (annot) { val_add_child(annot, elem); } /* next level is complexType */ cpx = xml_val_new_struct(XSD_CPX_TYP, xsd_id); if (!cpx) { return ERR_INTERNAL_MEM; } else { val_add_child(cpx, elem); /* add early */ } /* complex content */ cpxcon = xml_val_new_struct(XSD_CPX_CON, xsd_id); if (!cpxcon) { return ERR_INTERNAL_MEM; } else { val_add_child(cpxcon, cpx); /* add early */ } /* extension */ ext = xml_val_new_flag(XSD_EXTENSION, xsd_id); if (!ext) { return ERR_INTERNAL_MEM; } else { val_add_child(ext, cpxcon); /* add early */ } /* add baseType attribute */ qname = xml_val_make_qname(xsd_id, XSD_ANY_TYPE); if (!qname) { return ERR_INTERNAL_MEM; } /* pass off qname memory here */ res = xml_val_add_attr(XSD_BASE, 0, qname, ext); if (res != NO_ERR) { m__free(qname); } return res; } /* do_yang_elem_anyxml */ /******************************************************************** * FUNCTION do_typedefs_list * * Generate the typedef elements for any child type * * INPUTS: * mod == module conversion in progress * obj == obj_template_t for the child node to use * typnameQ == Q of typname_map_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each local type within this * list or any groups, or any children * * RETURNS: * status *********************************************************************/ static status_t do_typedefs_list (ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *typnameQ) { obj_list_t *list; typ_template_t *typ; status_t res; list = obj->def.list; /* check any local typedefs in this list */ for (typ = (typ_template_t *)dlq_firstEntry(list->typedefQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { res = add_type_mapping(typ, typnameQ); if (res != NO_ERR) { return res; } } /* check any local typedefs in any child objects */ res = xsd_do_typedefs_datadefQ(mod, list->datadefQ, typnameQ); if (res != NO_ERR) { return res; } /* check any typedefs in any groupings in this object */ res = xsd_do_typedefs_groupingQ(mod, list->groupingQ, typnameQ); return res; } /* do_typedefs_list */ /******************************************************************** * FUNCTION do_yang_elem_list * * Generate the element node for a list object type * * INPUTS: * mod == module conversion in progress * obj == object to convert to XSD element * augtargobj == obj_template_t for the target of the augment clause * if this is a call from add_yang_augment. This node will * be used to create the substitutionGroup for the top-level * element. Set to NULL if not used. * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for simple element * * RETURNS: * status *********************************************************************/ static status_t do_yang_elem_list (ncx_module_t *mod, obj_template_t *obj, obj_template_t *augtargobj, val_value_t *val) { val_value_t *elem, *cpx, *annot, *seq, *key, *chnode, *uniqval; obj_template_t *child; obj_list_t *list; obj_key_t *idx; obj_unique_t *uniq; obj_unique_comp_t *uniqcomp; xmlChar *buff, *str; status_t res; xmlns_id_t xsd_id; boolean iskey; uint32 uniqcnt, namelen; xmlChar numbuff[NCX_MAX_NUMLEN]; res = NO_ERR; xsd_id = xmlns_xs_id(); list = obj->def.list; elem = xml_val_new_struct(XSD_ELEMENT, xsd_id); if (!elem) { return ERR_INTERNAL_MEM; } else { val_add_child(elem, val); } /* add the name attribute to the element */ res = xml_val_add_cattr(NCX_EL_NAME, 0, list->name, elem); if (res != NO_ERR) { return res; } /* check if a substitutionGroup is needed */ if (augtargobj) { res = add_augtarget_subgrp(augtargobj, elem); if (res != NO_ERR) { return res; } } /* do not put minOccurs or maxOccurs on top-level elements */ if (!obj_is_top(obj)) { /* add minOccurs and maxOccurs attributes to the element */ if (list->minset) { sprintf((char *)numbuff, "%u", list->minelems); res = xml_val_add_cattr(XSD_MIN_OCCURS, 0, numbuff, elem); } else { res = xml_val_add_cattr(XSD_MIN_OCCURS, 0, XSD_ZERO, elem); } if (res != NO_ERR) { return res; } if (list->maxset && list->maxelems) { sprintf((char *)numbuff, "%u", list->maxelems); res = xml_val_add_cattr(XSD_MAX_OCCURS, 0, numbuff, elem); } else { res = xml_val_add_cattr(XSD_MAX_OCCURS, 0, XSD_UNBOUNDED, elem); } if (res != NO_ERR) { return res; } } /* add an annotation node if needed */ annot = xsd_make_obj_annotation(obj, &res); if (res != NO_ERR) { return res; } else if (annot) { val_add_child(annot, elem); } /* next level is complexType */ cpx = xml_val_new_struct(XSD_CPX_TYP, xsd_id); if (!cpx) { return ERR_INTERNAL_MEM; } else { val_add_child(cpx, elem); } /* next level is sequence */ seq = xml_val_new_struct(XSD_SEQUENCE, xsd_id); if (!seq) { return ERR_INTERNAL_MEM; } else { val_add_child(seq, cpx); } /* go through all the key nodes and generate elements for them */ for (idx = (obj_key_t *)dlq_firstEntry(&list->keyQ); idx != NULL; idx = (obj_key_t *)dlq_nextEntry(idx)) { res = do_yang_elem_btype(mod, idx->keyobj, NULL, TRUE, seq); if (res != NO_ERR) { return res; } } /* go through all the child nodes and generate elements for them, * skipping any key leafs already done */ for (child = (obj_template_t *)dlq_firstEntry(list->datadefQ); child != NULL; child = (obj_template_t *)dlq_nextEntry(child)) { if (child->objtype == OBJ_TYP_LEAF) { iskey = (obj_find_key(&list->keyQ, obj_get_name(child))) ? TRUE : FALSE; } else { iskey = FALSE; } if (!iskey) { res = do_yang_elem_btype(mod, child, NULL, iskey, seq); if (res != NO_ERR) { return res; } } } res = xsd_add_aughook(seq); if (res != NO_ERR) { return res; } if (!dlq_empty(&list->keyQ)) { /* create a 'key' node */ key = xml_val_new_struct(XSD_KEY, xsd_id); if (!key) { return ERR_INTERNAL_MEM; } else { val_add_child(key, elem); } /* generate a key name */ buff = m__getMem(xml_strlen(list->name) + xml_strlen(XSD_KEY) + NCX_MAX_NUMLEN + 1); if (!buff) { return ERR_INTERNAL_MEM; } str = buff; str += xml_strcpy(str, list->name); str += xml_strcpy(str, XSD_KEY); sprintf((char *)str, "%u", get_next_seqnum()); /* add the name attribute to the key node */ res = xml_val_add_attr(NCX_EL_NAME, 0, buff, key); if (res != NO_ERR) { m__free(buff); return ERR_INTERNAL_MEM; } /* add the selector child node */ chnode = xml_val_new_flag(XSD_SELECTOR, xsd_id); if (!chnode) { return ERR_INTERNAL_MEM; } else { val_add_child(chnode, key); /* add early */ } /* selector is the current node * add the xpath attribute to the selector node */ res = xml_val_add_cattr(NCX_EL_XPATH, 0, (const xmlChar *)".", chnode); if (res != NO_ERR) { return res; } /* add all the field child nodes * go through all the index nodes as field nodes */ for (idx = (obj_key_t *)dlq_firstEntry(&list->keyQ); idx != NULL; idx = (obj_key_t *)dlq_nextEntry(idx)) { /* add the selector child node */ chnode = xml_val_new_flag(XSD_FIELD, xsd_id); if (!chnode) { return ERR_INTERNAL_MEM; } else { val_add_child(chnode, key); } /* add the xpath attribute to the field node */ res = xml_val_add_cattr(NCX_EL_XPATH, 0, obj_get_name(idx->keyobj), chnode); if (res != NO_ERR) { return res; } } } /* add a unique element for each unique-stmt in this list */ uniqcnt = 0; namelen = xml_strlen(list->name); for (uniq = (obj_unique_t *)dlq_firstEntry(&list->uniqueQ); uniq != NULL; uniq = (obj_unique_t *)dlq_nextEntry(uniq)) { uniqval = xml_val_new_struct(XSD_UNIQUE, xsd_id); if (!uniqval) { return ERR_INTERNAL_MEM; } else { val_add_child(uniqval, elem); } sprintf((char *)numbuff, "%u", ++uniqcnt); /* generate a unique name */ buff = m__getMem(namelen + xml_strlen(XSD_UNIQUE_SUFFIX) + xml_strlen(numbuff) + 1); if (!buff) { return ERR_INTERNAL_MEM; } str = buff; str += xml_strcpy(str, list->name); str += xml_strcpy(str, XSD_UNIQUE_SUFFIX); str += xml_strcpy(str, numbuff); /* add the name attribute to the unique node */ res = xml_val_add_attr(NCX_EL_NAME, 0, buff, uniqval); if (res != NO_ERR) { m__free(buff); return ERR_INTERNAL_MEM; } /* add the selector child node */ chnode = xml_val_new_flag(XSD_SELECTOR, xsd_id); if (!chnode) { return ERR_INTERNAL_MEM; } else { val_add_child(chnode, uniqval); /* add early */ } /* selector is the current node * add the xpath attribute to the selector node */ res = xml_val_add_cattr(NCX_EL_XPATH, 0, (const xmlChar *)".", chnode); if (res != NO_ERR) { return res; } /* add all the field child nodes * go through all the unique comp nodes as field nodes */ for (uniqcomp = (obj_unique_comp_t *)dlq_firstEntry(&uniq->compQ); uniqcomp != NULL; uniqcomp = (obj_unique_comp_t *)dlq_nextEntry(uniqcomp)) { /* add the selector child node */ chnode = xml_val_new_flag(XSD_FIELD, xsd_id); if (!chnode) { return ERR_INTERNAL_MEM; } else { val_add_child(chnode, uniqval); } /* add the xpath attribute to the field node */ res = xml_val_add_cattr(NCX_EL_XPATH, 0, uniqcomp->xpath, chnode); if (res != NO_ERR) { return res; } } } return res; } /* do_yang_elem_list */ /******************************************************************** * FUNCTION do_typedefs_case * * Generate the typedef elements for any child type * * INPUTS: * mod == module conversion in progress * obj == obj_template_t for the child node to use * typnameQ == Q of typname_map_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each local type within any * children data definitions * * RETURNS: * status *********************************************************************/ static status_t do_typedefs_case (ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *typnameQ) { const obj_case_t *cas; status_t res; cas = obj->def.cas; /* check any local typedefs in any objects in any cases */ res = xsd_do_typedefs_datadefQ(mod, cas->datadefQ, typnameQ); return res; } /* do_typedefs_case */ /******************************************************************** * FUNCTION do_typedefs_choice * * Generate the typedef elements for any child type * * INPUTS: * mod == module conversion in progress * obj == obj_template_t for the child node to use * typnameQ == Q of typname_map_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each local type within any * children data definitions * * RETURNS: * status *********************************************************************/ static status_t do_typedefs_choice (ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *typnameQ) { const obj_choice_t *choic; status_t res; choic = obj->def.choic; res = xsd_do_typedefs_datadefQ(mod, choic->caseQ, typnameQ); return res; } /* do_typedefs_choice */ /******************************************************************** * FUNCTION do_yang_elem_choice * * Generate the element node for a choice object type * * INPUTS: * mod == module conversion in progress * obj == object to convert to XSD element * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for simple element * * RETURNS: * status *********************************************************************/ static status_t do_yang_elem_choice (ncx_module_t *mod, obj_template_t *obj, val_value_t *val) { val_value_t *top, *seq, *annot; obj_choice_t *choic; obj_template_t *casobj, *child; obj_case_t *cas; xmlns_id_t xsd_id; status_t res; choic = obj->def.choic; xsd_id = xmlns_xs_id(); res = NO_ERR; /* YANG choices do not take up a node, so start with a * xs:choice instead of a named element, then a choice */ top = xml_val_new_struct(NCX_EL_CHOICE, xsd_id); if (!top) { return ERR_INTERNAL_MEM; } else { val_add_child(top, val); /* add early */ } /* check if an annotation is needed for the element */ annot = xsd_make_obj_annotation(obj, &res); if (res != NO_ERR) { return res; } else if (annot) { val_add_child(annot, top); } for (casobj = (obj_template_t *)dlq_firstEntry(choic->caseQ); casobj != NULL && res==NO_ERR; casobj = (obj_template_t *)dlq_nextEntry(casobj)) { /* next level is sequence */ seq = xml_val_new_struct(XSD_SEQUENCE, xsd_id); if (!seq) { return ERR_INTERNAL_MEM; } else { val_add_child(seq, top); /* add early */ } /* check if an annotation is needed for the sequence */ annot = xsd_make_obj_annotation(casobj, &res); if (res != NO_ERR) { return res; } else if (annot) { val_add_child(annot, seq); } cas = casobj->def.cas; for (child = (obj_template_t *)dlq_firstEntry(cas->datadefQ); child != NULL && res==NO_ERR; child = (obj_template_t *)dlq_nextEntry(child)) { res = do_yang_elem_btype(mod, child, NULL, FALSE, seq); } if (res == NO_ERR) { res = xsd_add_aughook(seq); } } if (res == NO_ERR) { res = xsd_add_aughook(top); } return res; } /* do_yang_elem_choice */ /******************************************************************** * FUNCTION do_yang_elem_union * * Generate the element node for a union object type * * INPUTS: * mod == module conversion in progress * obj == object to convert to XSD element * augtargobj == obj_template_t for the target of the augment clause * if this is a call from add_yang_augment. This node will * be used to create the substitutionGroup for the top-level * element. Set to NULL if not used. * iskey == TRUE if this is a key node, FALSE if not * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for simple element * * RETURNS: * status *********************************************************************/ static status_t do_yang_elem_union (ncx_module_t *mod, obj_template_t *obj, obj_template_t *augtargobj, boolean iskey, val_value_t *val) { val_value_t *elem, *typnode, *annot; typ_def_t *typdef; status_t res; typdef = obj_get_typdef(obj); annot = xsd_make_obj_annotation(obj, &res); if (res != NO_ERR) { return res; } elem = xsd_new_leaf_element(mod, obj, (annot) ? TRUE : FALSE, (typdef->tclass == NCX_CL_NAMED), iskey); if (!elem) { if (annot) { val_free_value(annot); } return ERR_INTERNAL_MEM; } if (annot) { val_add_child(annot, elem); } val_add_child(elem, val); /* check if a substitutionGroup is needed */ if (augtargobj) { res = add_augtarget_subgrp(augtargobj, elem); if (res != NO_ERR) { return res; } } if (elem->btyp == NCX_BT_EMPTY) { return NO_ERR; } /* next level is simpleType */ typnode = xml_val_new_struct(XSD_SIM_TYP, xmlns_xs_id()); if (!typnode) { return ERR_INTERNAL_MEM; } else { val_add_child(typnode, elem); /* add early */ } /* generate the union of simpleTypes or list of memberTypes */ res = xsd_finish_union(mod, typdef, typnode); return res; } /* do_yang_elem_union */ /******************************************************************** * FUNCTION do_yang_elem_simple * * Generate the element node for a leaf object * * INPUTS: * mod == module conversion in progress * obj == object to convert to XSD element * augtargobj == obj_template_t for the target of the augment clause * if this is a call from add_yang_augment. This node will * be used to create the substitutionGroup for the top-level * element. Set to NULL if not used. * iskey == TRUE if this is a key node, FALSE if not * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for simple element * * RETURNS: * status *********************************************************************/ static status_t do_yang_elem_simple (ncx_module_t *mod, obj_template_t *obj, obj_template_t *augtargobj, boolean iskey, val_value_t *val) { typ_def_t *typdef; val_value_t *elem, *toptyp, *annot; typ_simple_t *simtyp; boolean empty, simtop; status_t res; res = NO_ERR; empty = FALSE; simtop = TRUE; typdef = obj_get_typdef(obj); if (!typdef) { return SET_ERROR(ERR_INTERNAL_VAL); } /* check if an annotation is needed for the element */ annot = xsd_make_obj_annotation(obj, &res); if (res != NO_ERR) { return res; } res = test_basetype_attr(mod, typdef); switch (typdef->tclass) { case NCX_CL_BASE: empty = TRUE; break; case NCX_CL_SIMPLE: simtyp = &typdef->def.simple; empty = (dlq_empty(&simtyp->range.rangeQ) && dlq_empty(&simtyp->unionQ) && dlq_empty(&simtyp->valQ) && dlq_empty(&simtyp->patternQ) && dlq_empty(&simtyp->metaQ)); break; case NCX_CL_COMPLEX: switch (typ_get_basetype(typdef)) { case NCX_BT_ANYDATA: case NCX_BT_ANYXML: empty = TRUE; simtop = FALSE; break; default: return SET_ERROR(ERR_INTERNAL_VAL); } break; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { simtyp = &typdef->def.named.newtyp->def.simple; empty = (dlq_empty(&simtyp->range.rangeQ) && dlq_empty(&simtyp->valQ) && dlq_empty(&simtyp->patternQ) && dlq_empty(&simtyp->metaQ)); } else { empty = TRUE; switch (typ_get_basetype(typdef)) { case NCX_BT_ANYDATA: case NCX_BT_ANYXML: simtop = FALSE; break; default: break; } } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } if (empty) { if (annot) { elem = xsd_new_leaf_element(mod, obj, TRUE, TRUE, iskey); if (elem) { val_add_child(annot, elem); } } else { /* get an empty element */ elem = xsd_new_leaf_element(mod, obj, FALSE, TRUE, iskey); } } else { elem = xsd_new_leaf_element(mod, obj, TRUE, FALSE, iskey); if (elem && annot) { val_add_child(annot, elem); } } if (!elem) { if (annot) { val_free_value(annot); } return ERR_INTERNAL_MEM; } /* check if a substitutionGroup is needed */ if (augtargobj) { res = add_augtarget_subgrp(augtargobj, elem); if (res != NO_ERR) { return res; } } if (!empty) { if (simtop) { toptyp = xml_val_new_struct(XSD_SIM_TYP, xmlns_xs_id()); } else { toptyp = xml_val_new_struct(XSD_CPX_TYP, xmlns_xs_id()); } if (!toptyp) { val_free_value(elem); return ERR_INTERNAL_MEM; } else { val_add_child(toptyp, elem); /* add early */ } switch (typdef->tclass) { case NCX_CL_SIMPLE: res = xsd_finish_simpleType(mod, typdef, toptyp); break; case NCX_CL_NAMED: res = xsd_finish_namedType(mod, typdef, toptyp); break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { val_free_value(elem); return res; } } val_add_child(elem, val); return NO_ERR; } /* do_yang_elem_simple */ /******************************************************************** * FUNCTION do_yang_elem_btype * * Generate the element node for any child type * * INPUTS: * mod == module conversion in progress * obj == obj_template_t for the child node to use * augtargobj == obj_template_t for the target of the augment clause * if this is a call from add_yang_augment. This node will * be used to create the substitutionGroup for the top-level * element. Set to NULL if not used. * iskey == TRUE if this is a key node, FALSE if not * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the child type * * RETURNS: * status *********************************************************************/ static status_t do_yang_elem_btype (ncx_module_t *mod, obj_template_t *obj, obj_template_t *augtargobj, boolean iskey, val_value_t *val) { typ_def_t *typdef; status_t res; /* augmented objects from a different module or submodule should * be skipped, and handled in the module that defined the node * * may want to disable this check in the CLI to provide a complete * fully-augmented XSD for a given target, except there are other * details, like complete module tree mode instead of subtree mode */ if (obj->tkerr.mod != mod) { return NO_ERR; } typdef = obj_get_typdef(obj); switch (obj->objtype) { case OBJ_TYP_CONTAINER: res = do_yang_elem_container(mod, obj, augtargobj, val); break; case OBJ_TYP_ANYXML: res = do_yang_elem_anyxml(obj, val); break; case OBJ_TYP_LEAF: if (typ_get_basetype(typdef) == NCX_BT_UNION) { res = do_yang_elem_union(mod, obj, augtargobj, iskey, val); } else { res = do_yang_elem_simple(mod, obj, augtargobj, iskey, val); } break; case OBJ_TYP_LEAF_LIST: if (typ_get_basetype(typdef) == NCX_BT_UNION) { res = do_yang_elem_union(mod, obj, augtargobj, iskey, val); } else { res = do_yang_elem_simple(mod, obj, augtargobj, iskey, val); } break; case OBJ_TYP_LIST: res = do_yang_elem_list(mod, obj, augtargobj, val); break; case OBJ_TYP_CHOICE: /* XSD does not allow a choice to be the substitutionGroup * replacement for an abstract element, so ignoring choice * augment for now!!! */ if (augtargobj) { log_debug("\nSkipping augment choice node " "'%s', not supported in XSD", obj_get_name(obj)); } res = do_yang_elem_choice(mod, obj, val); break; case OBJ_TYP_CASE: /* case objects not handled here */ log_warn("\nSkipping case node " "'%s', not supported in XSD", obj_get_name(obj)); res = NO_ERR; break; case OBJ_TYP_USES: /* skip all uses because it may be expanded with * possibly 1 or more modified child nodes, or * a node in the group may be in a key * or augmented somehow (inside the grouping or * inside the object tree * */ res = NO_ERR; break; case OBJ_TYP_AUGMENT: /* skip this nested augment -- it is already been added * into the object tree and will be generated there */ res = NO_ERR; break; case OBJ_TYP_RPC: case OBJ_TYP_RPCIO: case OBJ_TYP_NOTIF: res = SET_ERROR(ERR_INTERNAL_VAL); break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* do_yang_elem_btype */ /******************************************************************** * FUNCTION do_typedefs_uses * * Generate the typedef elements for any child type * * INPUTS: * mod == module conversion in progress * obj == obj_template_t for the child node to use * typnameQ == Q of typname_map_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each local type within any * children data definitions * * RETURNS: * status *********************************************************************/ static status_t do_typedefs_uses (ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *typnameQ) { obj_uses_t *uses; status_t res; uses = obj->def.uses; res = xsd_do_typedefs_datadefQ(mod, uses->datadefQ, typnameQ); return res; } /* do_typedefs_uses */ /******************************************************************** * FUNCTION do_typedefs_augment * * Generate the typedef elements for any child type * * INPUTS: * mod == module conversion in progress * obj == obj_template_t for the child node to use * typnameQ == Q of typname_map_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each local type within any * children data definitions * * RETURNS: * status *********************************************************************/ static status_t do_typedefs_augment (ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *typnameQ) { obj_augment_t *aug; status_t res; aug = obj->def.augment; res = xsd_do_typedefs_datadefQ(mod, &aug->datadefQ, typnameQ); return res; } /* do_typedefs_augment */ /******************************************************************** * FUNCTION do_typedefs_rpc * * Generate the typedef elements for any child type * * INPUTS: * mod == module conversion in progress * obj == obj_template_t for the child node to use * typnameQ == Q of typname_map_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each local type within this * RPC or any groups, or any children * * RETURNS: * status *********************************************************************/ static status_t do_typedefs_rpc (ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *typnameQ) { obj_rpc_t *rpc; typ_template_t *typ; status_t res; rpc = obj->def.rpc; /* check any local typedefs in this RPC */ for (typ = (typ_template_t *)dlq_firstEntry(&rpc->typedefQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { res = add_type_mapping(typ, typnameQ); if (res != NO_ERR) { return res; } } /* check any local typedefs in any child objects */ res = xsd_do_typedefs_datadefQ(mod, &rpc->datadefQ, typnameQ); if (res != NO_ERR) { return res; } /* check any typedefs in any groupings in this RPC */ res = xsd_do_typedefs_groupingQ(mod, &rpc->groupingQ, typnameQ); return res; } /* do_typedefs_rpc */ /******************************************************************** * FUNCTION do_typedefs_rpcio * * Generate the typedef elements for any child type * * INPUTS: * mod == module conversion in progress * obj == obj_template_t for the child node to use * typnameQ == Q of typname_map_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each local type within this * RPCIO or any groups, or any children * * RETURNS: * status *********************************************************************/ static status_t do_typedefs_rpcio (ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *typnameQ) { obj_rpcio_t *rpcio; typ_template_t *typ; status_t res; rpcio = obj->def.rpcio; /* check any local typedefs in this RPC input/output */ for (typ = (typ_template_t *)dlq_firstEntry(&rpcio->typedefQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { res = add_type_mapping(typ, typnameQ); if (res != NO_ERR) { return res; } } /* check any local typedefs in any child objects */ res = xsd_do_typedefs_datadefQ(mod, &rpcio->datadefQ, typnameQ); if (res != NO_ERR) { return res; } /* check any typedefs in any groupings in this RPC */ res = xsd_do_typedefs_groupingQ(mod, &rpcio->groupingQ, typnameQ); return res; } /* do_typedefs_rpcio */ /******************************************************************** * FUNCTION do_typedefs_notif * * Generate the typedef elements for any child type * * INPUTS: * mod == module conversion in progress * obj == obj_template_t for the child node to use * typnameQ == Q of typname_map_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each local type within this * notification or any groups, or any children * * RETURNS: * status *********************************************************************/ static status_t do_typedefs_notif (ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *typnameQ) { obj_notif_t *notif; typ_template_t *typ; status_t res; notif = obj->def.notif; /* check any local typedefs in this notification */ for (typ = (typ_template_t *)dlq_firstEntry(¬if->typedefQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { res = add_type_mapping(typ, typnameQ); if (res != NO_ERR) { return res; } } /* check any local typedefs in any child objects */ res = xsd_do_typedefs_datadefQ(mod, ¬if->datadefQ, typnameQ); if (res != NO_ERR) { return res; } /* check any typedefs in any groupings in this RPC */ res = xsd_do_typedefs_groupingQ(mod, ¬if->groupingQ, typnameQ); return res; } /* do_typedefs_notif */ /******************************************************************** * FUNCTION do_local_typedefs * * Generate the typedef elements for any child type * * INPUTS: * mod == module conversion in progress * obj == obj_template_t for the child node to use * typnameQ == Q of xsd_typname_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each type * * RETURNS: * status *********************************************************************/ static status_t do_local_typedefs (ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *typnameQ) { status_t res; if (obj_is_cloned(obj)) { return NO_ERR; } res = NO_ERR; switch (obj->objtype) { case OBJ_TYP_CONTAINER: res = do_typedefs_container(mod, obj, typnameQ); break; case OBJ_TYP_ANYXML: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: res = NO_ERR; break; case OBJ_TYP_LIST: res = do_typedefs_list(mod, obj, typnameQ); break; case OBJ_TYP_CHOICE: res = do_typedefs_choice(mod, obj, typnameQ); break; case OBJ_TYP_CASE: res = do_typedefs_case(mod, obj, typnameQ); break; case OBJ_TYP_USES: res = do_typedefs_uses(mod, obj, typnameQ); break; case OBJ_TYP_AUGMENT: res = do_typedefs_augment(mod, obj, typnameQ); break; case OBJ_TYP_RPC: res = do_typedefs_rpc(mod, obj, typnameQ); break; case OBJ_TYP_RPCIO: res = do_typedefs_rpcio(mod, obj, typnameQ); break; case OBJ_TYP_NOTIF: res = do_typedefs_notif(mod, obj, typnameQ); break; case OBJ_TYP_REFINE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* do_local_typedefs */ /******************************************************************** * FUNCTION add_yang_rpcio * * Add the complexType subtree for input or output parameters * * INPUTS: * mod == module in progress * obj == RPC object to add in XSD format * iobj == input or output object * addtypename == TRUE if a type name attributes should be added * to the complexType or FALSE if not * val == struct parent to contain the parameters * * OUTPUTS: * val->childQ has one entry added for the the RPC function * and maybe one complexType for the RPC output * * RETURNS: * status *********************************************************************/ static status_t add_yang_rpcio (ncx_module_t *mod, obj_template_t *obj, obj_template_t *iobj, boolean addtypename, val_value_t *val) { val_value_t *cpxtyp, *cpxcon, *ext, *seq, *annot; obj_template_t *chobj; obj_rpcio_t *rpcio; xmlChar *typename, *qname; xmlns_id_t xsd_id, nc_id; status_t res; xsd_id = xmlns_xs_id(); nc_id = xmlns_nc_id(); rpcio = (iobj) ? iobj->def.rpcio : NULL; cpxtyp = xml_val_new_struct(XSD_CPX_TYP, xsd_id); if (!cpxtyp) { return ERR_INTERNAL_MEM; } else { val_add_child(cpxtyp, val); } if (addtypename) { typename = xsd_make_rpc_output_typename(obj); if (!typename) { return ERR_INTERNAL_MEM; } res = xml_val_add_attr(NCX_EL_NAME, 0, typename, cpxtyp); if (res != NO_ERR) { m__free(typename); return res; } } if (iobj) { annot = xsd_make_obj_annotation(iobj, &res); if (res != NO_ERR) { return res; } else if (annot) { val_add_child(annot, cpxtyp); } } cpxcon = xml_val_new_struct(XSD_CPX_CON, xsd_id); if (!cpxcon) { return ERR_INTERNAL_MEM; } else { val_add_child(cpxcon, cpxtyp); } ext = xml_val_new_struct(XSD_EXTENSION, xsd_id); if (!ext) { return ERR_INTERNAL_MEM; } else { val_add_child(ext, cpxcon); } if (addtypename) { qname = xml_val_make_qname(nc_id, XSD_DATA_INLINE); } else { qname = xml_val_make_qname(nc_id, XSD_RPC_OPTYPE); } if (!qname) { return ERR_INTERNAL_MEM; } res = xml_val_add_attr(XSD_BASE, 0, qname, ext); if (res != NO_ERR) { m__free(qname); return res; } seq = xml_val_new_struct(XSD_SEQUENCE, xsd_id); if (!seq) { return ERR_INTERNAL_MEM; } else { val_add_child(seq, ext); } if (rpcio) { for (chobj = (obj_template_t *)dlq_firstEntry(&rpcio->datadefQ); chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { res = do_yang_elem_btype(mod, chobj, NULL, FALSE, seq); if (res != NO_ERR) { return res; } } } res = xsd_add_aughook(seq); return res; } /* add_yang_rpcio */ /******************************************************************** * FUNCTION add_yang_rpc * * Add one or 2 elements representing an RPC function * * INPUTS: * mod == module in progress * obj == RPC object to add in XSD format * val == struct parent to contain the RPC function element * * OUTPUTS: * val->childQ has one entry added for the the RPC function * and maybe one complexType for the RPC output * * RETURNS: * status *********************************************************************/ static status_t add_yang_rpc (ncx_module_t *mod, obj_template_t *obj, val_value_t *val) { val_value_t *elem, *annot; obj_template_t *inputobj, *outputobj; xmlChar *qname; xmlns_id_t xsd_id, nc_id; status_t res; xsd_id = xmlns_xs_id(); nc_id = xmlns_nc_id(); inputobj = obj_find_template(obj_get_datadefQ(obj), NULL, YANG_K_INPUT); outputobj = obj_find_template(obj_get_datadefQ(obj), NULL, YANG_K_OUTPUT); /* add a named typedef for the output if needed */ if (outputobj) { res = add_yang_rpcio(mod, obj, outputobj, TRUE, val); if (res != NO_ERR) { return res; } } /* An RPC method is simply an element that * hooks into the NETCONF rpcOperation element */ elem = xml_val_new_struct(XSD_ELEMENT, xsd_id); if (!elem) { return ERR_INTERNAL_MEM; } else { val_add_child(elem, val); /* add early */ } /* add the name attribute */ res = xml_val_add_cattr(NCX_EL_NAME, 0, obj_get_name(obj), elem); if (res != NO_ERR) { return res; } /* create the NETCONF rpcOperation QName */ qname = xml_val_make_qname(nc_id, XSD_RPC_OP); if (!qname) { return res; } /* add the substitutionGroup attribute */ res = xml_val_add_attr(XSD_SUB_GRP, 0, qname, elem); if (res != NO_ERR) { m__free(qname); return res; } /* add the object annotation if any */ annot = xsd_make_obj_annotation(obj, &res); if (res != NO_ERR) { return res; } else if (annot) { val_add_child(annot, elem); } /* add an inline typedef for the input * an extension node is generated even if inputobj is NULL */ res = add_yang_rpcio(mod, obj, inputobj, FALSE, elem); return res; } /* add_yang_rpc */ /******************************************************************** * FUNCTION add_yang_notif * * Add one element representing a notification * * INPUTS: * mod == module in progress * obj == notification object to add in XSD format * val == struct parent to contain the notification element * * OUTPUTS: * val->childQ has one entry added for the the notification * * RETURNS: * status *********************************************************************/ static status_t add_yang_notif (ncx_module_t *mod, obj_template_t *obj, val_value_t *val) { val_value_t *elem, *annot, *cpxtyp, *cpxcon, *ext, *seq; obj_notif_t *notif; obj_template_t *chobj; xmlChar *qname; xmlns_id_t xsd_id, ncn_id; status_t res; xsd_id = xmlns_xs_id(); ncn_id = xmlns_ncn_id(); notif = obj->def.notif; /* A notification is simply an element that * hooks into the NETCONF notificationrpcOperation element */ elem = xml_val_new_struct(XSD_ELEMENT, xsd_id); if (!elem) { return ERR_INTERNAL_MEM; } else { val_add_child(elem, val); /* add early */ } /* add the name attribute */ res = xml_val_add_cattr(NCX_EL_NAME, 0, notif->name, elem); if (res != NO_ERR) { return res; } /* create the NETCONF notificationContent QName */ qname = xml_val_make_qname(ncn_id, XSD_NOTIF_CONTENT); if (!qname) { return res; } /* add the substitutionGroup attribute */ res = xml_val_add_attr(XSD_SUB_GRP, 0, qname, elem); if (res != NO_ERR) { m__free(qname); return res; } /* add the object annotation if any */ annot = xsd_make_obj_annotation(obj, &res); if (res != NO_ERR) { return res; } else if (annot) { val_add_child(annot, elem); } /* next node is complexType */ cpxtyp = xml_val_new_struct(XSD_CPX_TYP, xsd_id); if (!cpxtyp) { return ERR_INTERNAL_MEM; } else { val_add_child(cpxtyp, elem); } /* next node is complexContent */ cpxcon = xml_val_new_struct(XSD_CPX_CON, xsd_id); if (!cpxcon) { return ERR_INTERNAL_MEM; } else { val_add_child(cpxcon, cpxtyp); } /* next node is extension */ ext = xml_val_new_struct(XSD_EXTENSION, xsd_id); if (!ext) { return ERR_INTERNAL_MEM; } else { val_add_child(ext, cpxcon); } qname = xml_val_make_qname(ncn_id, XSD_NOTIF_CTYPE); if (!qname) { return ERR_INTERNAL_MEM; } res = xml_val_add_attr(XSD_BASE, 0, qname, ext); if (res != NO_ERR) { m__free(qname); return res; } /* next node is sequence */ seq = xml_val_new_struct(XSD_SEQUENCE, xsd_id); if (!seq) { return ERR_INTERNAL_MEM; } else { val_add_child(seq, ext); } /* add any data defined for this notification */ for (chobj = (obj_template_t *)dlq_firstEntry(¬if->datadefQ); chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { res = do_yang_elem_btype(mod, chobj, NULL, FALSE, seq); if (res != NO_ERR) { return res; } } res = xsd_add_aughook(seq); return res; } /* add_yang_notif */ /******************************************************************** * FUNCTION add_yang_augment * * Add one element representing a top-level augment for * a target in a different namespace. Only top-level augment * clauses can have this form. Nested YANG augment-stmts * are ignored for XSD purposes, since the cloned objects * will get generated instead. * * INPUTS: * mod == module in progress * obj == augment object to add in XSD format * val == struct parent to contain the augment element * * OUTPUTS: * val->childQ has one entry added for the the augmenting element * * RETURNS: * status *********************************************************************/ static status_t add_yang_augment (ncx_module_t *mod, obj_template_t *obj, val_value_t *val) { obj_augment_t *aug; obj_template_t *chobj; status_t res; aug = obj->def.augment; /* check that the target is in another namespace */ if (!aug->targobj || !aug->targobj->tkerr.mod || (aug->targobj->tkerr.mod->nsid == mod->nsid)) { return NO_ERR; } /* A top-level augment is a top-level element that is a substitutionGroup * replacement for the abstract element identified in the augment target. * Each datadef node declared in the augment clause generates a top-level * element to replace the abstract element representing the target. */ for (chobj = (obj_template_t *)dlq_firstEntry(&aug->datadefQ); chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { res = do_yang_elem_btype(mod, chobj, aug->targobj, FALSE, val); if (res != NO_ERR) { return res; } } return NO_ERR; } /* add_yang_augment */ /************* E X T E R N A L F U N C T I O N S *****************/ /******************************************************************** * FUNCTION xsd_add_groupings * * Add the required group nodes * * INPUTS: * mod == module in progress * val == struct parent to contain child nodes for each group * * OUTPUTS: * val->childQ has entries added for the groupings required * * RETURNS: * status *********************************************************************/ status_t xsd_add_groupings (ncx_module_t *mod, val_value_t *val) { grp_template_t *grp; obj_template_t *obj; val_value_t *grpval, *seqval, *annot; xmlns_id_t xsd_id; status_t res; xsd_id = xmlns_xs_id(); /* generate top-level groupings only */ for (grp = (grp_template_t *)dlq_firstEntry(&mod->groupingQ); grp != NO_ERR; grp = (grp_template_t *)dlq_nextEntry(grp)) { /* check if an annotation node is needed */ annot = xsd_make_group_annotation(grp, &res); if (res != NO_ERR) { return res; } /* check if an empty group or regular group is needed */ if (!annot && dlq_empty(&grp->datadefQ)) { grpval = xml_val_new_flag(XSD_GROUP, xsd_id); } else { grpval = xml_val_new_struct(XSD_GROUP, xsd_id); } if (!grpval) { if (annot) { val_free_value(annot); } return ERR_INTERNAL_MEM; } else { val_add_child(grpval, val); if (annot) { val_add_child(annot, grpval); } } /* set the group name */ res = xml_val_add_cattr(NCX_EL_NAME, 0, grp->name, grpval); if (res != NO_ERR) { return res; } /* generate all the objects in the datadefQ */ if (!dlq_empty(&grp->datadefQ)) { seqval = xml_val_new_struct(XSD_SEQUENCE, xsd_id); if (!seqval) { return ERR_INTERNAL_MEM; } else { val_add_child(seqval, grpval); } for (obj = (obj_template_t *)dlq_firstEntry(&grp->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { res = do_yang_elem_btype(mod, obj, NULL, FALSE, seqval); if (res != NO_ERR) { return res; } } } } return NO_ERR; } /* xsd_add_groupings */ /******************************************************************** * FUNCTION xsd_add_objects * * Add the required element nodes for each object in the module * RPC methods and notifications are mixed in with the objects * * INPUTS: * mod == module in progress * val == struct parent to contain child nodes for each object * * OUTPUTS: * val->childQ has entries added for the RPC methods in this module * * RETURNS: * status *********************************************************************/ status_t xsd_add_objects (ncx_module_t *mod, val_value_t *val) { obj_template_t *obj; status_t res; /* go through all the objects and create complexType constructs */ for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (obj_is_hidden(obj)) { continue; } switch (obj->objtype) { case OBJ_TYP_RPC: res = add_yang_rpc(mod, obj, val); break; case OBJ_TYP_NOTIF: res = add_yang_notif(mod, obj, val); break; case OBJ_TYP_AUGMENT: res = add_yang_augment(mod, obj, val); break; case OBJ_TYP_USES: /* A uses-stmt is ignored in XSD translation and only the * cloned objects from the uses expansion are included instead */ res = NO_ERR; break; default: res = do_yang_elem_btype(mod, obj, NULL, FALSE, val); } if (res != NO_ERR) { return res; } } return NO_ERR; } /* xsd_add_objects */ /******************************************************************** * FUNCTION xsd_do_typedefs_groupingQ * * Analyze the entire 'groupingQ' within the module struct * Generate local typedef mappings as needed * * INPUTS: * mod == module conversion in progress * groupingQ == Q of grp_template_t structs to check * typnameQ == Q of xsd_typname_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each type * * RETURNS: * status *********************************************************************/ status_t xsd_do_typedefs_groupingQ (ncx_module_t *mod, dlq_hdr_t *groupingQ, dlq_hdr_t *typnameQ) { grp_template_t *grp; typ_template_t *typ; obj_template_t *obj; status_t res; /* go through the groupingQ and check the local types/groupings * and data-def statements */ for (grp = (grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL; grp = (grp_template_t *)dlq_nextEntry(grp)) { /* check any local typedefs in this grouping */ for (typ = (typ_template_t *)dlq_firstEntry(&grp->typedefQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { res = add_type_mapping(typ, typnameQ); if (res != NO_ERR) { return res; } } /* check any local typedefs in the objects in this grouping */ for (obj = (obj_template_t *)dlq_firstEntry(&grp->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { res = do_local_typedefs(mod, obj, typnameQ); if (res != NO_ERR) { return res; } } /* check any nested groupings */ res = xsd_do_typedefs_groupingQ(mod, &grp->groupingQ, typnameQ); if (res != NO_ERR) { return res; } } return NO_ERR; } /* xsd_do_typedefs_groupingQ */ /******************************************************************** * FUNCTION xsd_do_typedefs_datadefQ * * Analyze the entire 'datadefQ' within the module struct * Generate local typedef mappings as needed * * INPUTS: * mod == module conversion in progress * datadefQ == Q of obj_template_t structs to check * typnameQ == Q of xsd_typname_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each type * * RETURNS: * status *********************************************************************/ status_t xsd_do_typedefs_datadefQ (ncx_module_t *mod, dlq_hdr_t *datadefQ, dlq_hdr_t *typnameQ) { obj_template_t *obj; status_t res; for (obj = (obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { res = do_local_typedefs(mod, obj, typnameQ); if (res != NO_ERR) { return res; } } return NO_ERR; } /* xsd_do_typedefs_datadefQ */ /* END file xsd_yang.c */ yuma123_2.14/netconf/src/ydump/yangdump.h0000664000175000017500000002234114770023131020503 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangdump #define _H_yangdump /* FILE: yangdump.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Exports yangdump.yang conversion CLI parameter struct ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 01-mar-08 abb Begun; moved from ncx/ncxtypes.h 16-apr-11 abb Add support for string token preservation */ #ifndef _H_help #include "help.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define PROGNAME "yangdump" #define EMPTY_STRING (const xmlChar *)"" #define EL_A (const xmlChar *)"a" #define EL_BODY (const xmlChar *)"body" #define EL_DIV (const xmlChar *)"div" #define EL_H1 (const xmlChar *)"h1" #define EL_PRE (const xmlChar *)"pre" #define EL_SPAN (const xmlChar *)"span" #define EL_UL (const xmlChar *)"ul" #define EL_LI (const xmlChar *)"li" #define CL_YANG (const xmlChar *)"yang" #define CL_TOCMENU (const xmlChar *)"tocmenu" #define CL_TOCPLAIN (const xmlChar *)"tocplain" #define CL_DADDY (const xmlChar *)"daddy" #define ID_NAV (const xmlChar *)"nav" #define OBJVIEW_RAW "raw" #define OBJVIEW_COOKED "cooked" /* this should match the buffer size in ncx/tk.h */ #define YANGDUMP_BUFFSIZE 0xffff #define YANGDUMP_DEF_OUTPUT "stdout" #define YANGDUMP_DEF_CONFIG (const xmlChar *)"/etc/yuma/yangdump.conf" #define YANGDUMP_DEF_TOC (const xmlChar *)"menu" #define YANGDUMP_DEF_OBJVIEW OBJVIEW_RAW #define YANGDUMP_MOD (const xmlChar *)"yangdump" #define YANGDUMP_CONTAINER (const xmlChar *)"yangdump" #define YANGDUMP_PARM_CONFIG (const xmlChar *)"config" #define YANGDUMP_PARM_DEFNAMES (const xmlChar *)"defnames" #define YANGDUMP_PARM_DEPENDENCIES (const xmlChar *)"dependencies" #define YANGDUMP_PARM_DEVIATION (const xmlChar *)"deviation" #define YANGDUMP_PARM_EXPORTS (const xmlChar *)"exports" #define YANGDUMP_PARM_FORMAT (const xmlChar *)"format" #define YANGDUMP_PARM_HTML_DIV (const xmlChar *)"html-div" #define YANGDUMP_PARM_HTML_TOC (const xmlChar *)"html-toc" #define YANGDUMP_PARM_IDENTIFIERS (const xmlChar *)"identifiers" #define YANGDUMP_PARM_INDENT (const xmlChar *)"indent" #define YANGDUMP_PARM_MODULE (const xmlChar *)"module" #define YANGDUMP_PARM_MODVERSION (const xmlChar *)"modversion" #define YANGDUMP_PARM_VERSIONNAMES (const xmlChar *)"versionnames" #define YANGDUMP_PARM_OUTPUT (const xmlChar *)"output" #define YANGDUMP_PARM_OBJVIEW (const xmlChar *)"objview" #define YANGDUMP_PARM_XSD_SCHEMALOC (const xmlChar *)"xsd-schemaloc" #define YANGDUMP_PARM_SIMURLS (const xmlChar *)"simurls" #define YANGDUMP_PARM_SUBTREE (const xmlChar *)"subtree" #define YANGDUMP_PARM_STATS (const xmlChar *)"stats" #define YANGDUMP_PARM_STAT_TOTALS (const xmlChar *)"totals" #define YANGDUMP_PARM_TREE_IDENTIFIERS (const xmlChar *)"tree-identifiers" #define YANGDUMP_PARM_URLSTART (const xmlChar *)"urlstart" #define YANGDUMP_PARM_UNIFIED (const xmlChar *)"unified" #define YANGDUMP_PV_STATS_NONE (const xmlChar *)"none" #define YANGDUMP_PV_STATS_BRIEF (const xmlChar *)"brief" #define YANGDUMP_PV_STATS_BASIC (const xmlChar *)"basic" #define YANGDUMP_PV_STATS_ADVANCED (const xmlChar *)"advanced" #define YANGDUMP_PV_STATS_ALL (const xmlChar *)"all" #define YANGDUMP_PV_TOTALS_NONE (const xmlChar *)"none" #define YANGDUMP_PV_TOTALS_SUMMARY (const xmlChar *)"summary" #define YANGDUMP_PV_TOTALS_SUMMARY_ONLY (const xmlChar *)"summary-only" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* --stats CLI parameter */ typedef enum yangdump_statreport_t_ { YANGDUMP_REPORT_NONE, /* significant value */ YANGDUMP_REPORT_BRIEF, YANGDUMP_REPORT_BASIC, YANGDUMP_REPORT_ADVANCED, YANGDUMP_REPORT_ALL } yangdump_statreport_t; /* --totals CLI parameter */ typedef enum yangdump_totals_t_ { YANGDUMP_TOTALS_NONE, /* significant value */ YANGDUMP_TOTALS_SUMMARY, YANGDUMP_TOTALS_SUMMARY_ONLY } yangdump_totals_t; /* this structure represents all the statistics that can * be reported with the --stats parameter, and can be collected * by the yangdump program. */ typedef struct yangdump_stats_t_ { /* brief */ uint32 complexity_score; /* total nodes derived from num_config_objs * + num_state_objs */ /* basic */ uint32 num_extensions; uint32 num_features; uint32 num_groupings; uint32 num_typedefs; uint32 num_deviations; uint32 num_top_datanodes; uint32 num_config_objs; uint32 num_state_objs; /* advanced */ uint32 num_mandatory_nodes; uint32 num_optional_nodes; uint32 num_notifications; uint32 num_rpcs; uint32 num_inputs; uint32 num_outputs; uint32 num_augments; uint32 num_uses; uint32 num_choices; uint32 num_cases; uint32 num_anyxmls; uint32 num_np_containers; uint32 num_p_containers; uint32 num_lists; uint32 num_leaf_lists; uint32 num_key_leafs; uint32 num_plain_leafs; /* all */ uint32 num_imports; uint32 num_numbers; uint32 num_dec64s; uint32 num_enums; uint32 num_bits; uint32 num_booleans; uint32 num_emptys; uint32 num_strings; uint32 num_binarys; uint32 num_iis; uint32 num_leafrefs; uint32 num_idrefs; uint32 num_unions; } yangdump_stats_t; /* struct of yangdump conversion parameters */ typedef struct yangdump_cvtparms_t_ { /* external parameters */ char *module; /* malloced due to subtree design */ char *curmodule; /* not malloced when 2+ modules */ const char *output; const char *subtree; const char *objview; const xmlChar *schemaloc; const xmlChar *urlstart; const xmlChar *modpath; const xmlChar *config; const xmlChar *html_toc; const xmlChar *css_file; int32 indent; uint32 modcount; uint32 subtreecount; ncx_cvttyp_t format; boolean helpmode; help_mode_t helpsubmode; boolean defnames; boolean dependencies; boolean exports; boolean collect_stats; yangdump_statreport_t stats; yangdump_totals_t stat_totals; boolean showerrorsmode; boolean identifiers; boolean tree_identifiers; boolean html_div; boolean modversion; boolean subdirs; boolean versionnames; boolean output_isdir; boolean simurls; boolean unified; boolean versionmode; boolean rawmode; boolean allowcode; boolean onemodule; /* processing MODULE, force unified */ /* internal vars */ xmlChar *full_output; ncx_module_t *mod; char *srcfile; char *buff; val_value_t *cli_val; yangdump_stats_t *cur_stats; yangdump_stats_t *final_stats; uint32 stat_reports; uint32 bufflen; boolean firstdone; boolean isuser; dlq_hdr_t savedevQ; tk_chain_t *tkc; /* if docmode for format=html|yang */ } yangdump_cvtparms_t; #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangdump */ yuma123_2.14/netconf/src/ydump/yangyin.c0000664000175000017500000005467614770023131020350 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangyin.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 23feb08 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xsd_util #include "xsd_util.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifndef _H_yangdump_util #include "yangdump_util.h" #endif #ifndef _H_yangyin #include "yangyin.h" #endif #ifndef _H_yin #include "yin.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* #define YANGYIN_DEBUG 1 */ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION start_yin_elem * * Generate a start tag * * INPUTS: * scb == session control block to use for writing * name == element name * indent == indent count * *********************************************************************/ static void start_yin_elem (ses_cb_t *scb, const xmlChar *name, int32 indent) { ses_putstr_indent(scb, (const xmlChar *)"<", indent); ses_putstr(scb, name); } /* start_yin_elem */ /******************************************************************** * FUNCTION end_yin_elem * * Generate an end tag * * INPUTS: * scb == session control block to use for writing * name == element name * indent == indent count * *********************************************************************/ static void end_yin_elem (ses_cb_t *scb, const xmlChar *name, int32 indent) { ses_putstr_indent(scb, (const xmlChar *)"'); } /* end_yin_elem */ /******************************************************************** * FUNCTION start_ext_elem * * Generate a start tag for an extension * * INPUTS: * scb == session control block to use for writing * prefix == prefix string to use * name == element name * indent == indent count * *********************************************************************/ static void start_ext_elem (ses_cb_t *scb, const xmlChar *prefix, const xmlChar *name, int32 indent) { ses_putstr_indent(scb, (const xmlChar *)"<", indent); ses_putstr(scb, prefix); ses_putchar(scb, ':'); ses_putstr(scb, name); } /* start_ext_elem */ /******************************************************************** * FUNCTION end_ext_elem * * Generate an end tag * * INPUTS: * scb == session control block to use for writing * name == element name * indent == indent count * *********************************************************************/ static void end_ext_elem (ses_cb_t *scb, const xmlChar *prefix, const xmlChar *name, int32 indent) { ses_putstr_indent(scb, (const xmlChar *)"'); } /* end_ext_elem */ /******************************************************************** * FUNCTION write_import_xmlns * * Generate an xmlns attribute for the import * * INPUTS: * scb == session control block to use for writing * imp == ncx_import_t struct to use * indent == start indent count * *********************************************************************/ static void write_import_xmlns (ses_cb_t *scb, ncx_import_t *imp, int32 indent) { if (imp->mod == NULL) { return; } ses_putstr_indent(scb, XMLNS, indent); ses_putchar(scb, ':'); ses_putstr(scb, imp->prefix); ses_putchar(scb, '='); ses_putchar(scb, '"'); ses_putstr(scb, ncx_get_modnamespace(imp->mod)); ses_putchar(scb, '"'); } /* write_import_xmlns */ /******************************************************************** * FUNCTION write_yin_attr * * Generate an attibute declaration * * INPUTS: * scb == session control block to use for writing * attr == attribute name * value == attribute value string * indent == start indent count * *********************************************************************/ static void write_yin_attr (ses_cb_t *scb, const xmlChar *attr, const xmlChar *value, int32 indent) { ses_putstr_indent(scb, attr, indent); ses_putchar(scb, '='); ses_putchar(scb, '"'); ses_putstr(scb, value); ses_putchar(scb, '"'); } /* write_yin_attr */ /******************************************************************** * FUNCTION advance_token * * Move to the next token * * INPUTS: * *********************************************************************/ static status_t advance_token (yang_pcb_t *pcb) { status_t res; res = TK_ADV(pcb->tkc); #ifdef YANGYIN_DEBUG if (res == NO_ERR && LOGDEBUG3) { tk_dump_token(pcb->tkc->cur); } #endif return res; } /* advance_token */ /******************************************************************** * FUNCTION write_cur_token * * Write the full value of the current token to the session * * INPUTS: * scb == session control block * pcb == YANG parser control block * elem == TRUE if this is element content; FALSE if attribute *********************************************************************/ static void write_cur_token (ses_cb_t *scb, yang_pcb_t *pcb, boolean elem) { const xmlChar *prefix, *value; prefix = TK_CUR_MOD(pcb->tkc); if (prefix != NULL) { if (elem) { ses_putcstr(scb, prefix, -1); } else { ses_putastr(scb, prefix, -1); } ses_putchar(scb, ':'); } value = TK_CUR_VAL(pcb->tkc); if (value != NULL) { if (elem) { ses_putcstr(scb, value, -1); } else { ses_putastr(scb, value, -1); } } } /* write_cur_token */ /******************************************************************** * FUNCTION write_yin_stmt * * Go through the token chain and write YIN stmts * recursively if needed, until 1 YANG stmt is handled * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * startindent == start indent count * done == address of done return var to use * * RETURNS: * status *********************************************************************/ static status_t write_yin_stmt (yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb, int32 startindent, boolean *done) { const yin_mapping_t *mapping; ncx_import_t *import; ext_template_t *extension; const xmlChar *prefix, *modprefix; status_t res; boolean loopdone; res = NO_ERR; *done = FALSE; /* expecting a keyword [string] stmt-end sequence * or the very last closing right brace */ if (TK_CUR_TYP(pcb->tkc) == TK_TT_RBRACE) { if (tk_next_typ(pcb->tkc) == TK_TT_NONE) { *done = TRUE; return NO_ERR; } else { return ERR_NCX_WRONG_TKTYPE; } } else if (!TK_CUR_ID(pcb->tkc)) { return ERR_NCX_WRONG_TKTYPE; } /* check the keyword type */ switch (TK_CUR_TYP(pcb->tkc)) { case TK_TT_TSTRING: /* YANG keyword */ mapping = yin_find_mapping(TK_CUR_VAL(pcb->tkc)); if (mapping == NULL) { return ERR_NCX_DEF_NOT_FOUND; } /* output keyword part */ start_yin_elem(scb, mapping->keyword, startindent); /* output [string] part if expected */ if (mapping->argname == NULL) { if (tk_next_typ(pcb->tkc) == TK_TT_LBRACE) { ses_putchar(scb, '>'); } } else { /* move token pointer to the argument string */ res = advance_token(pcb); if (res != NO_ERR) { return res; } /* write the string part * do not add any extra whiespace to the XML string */ if (mapping->elem) { ses_putchar(scb, '>'); /* encode argname,value as an element */ start_yin_elem(scb, mapping->argname, startindent + cp->indent); ses_putchar(scb, '>'); write_cur_token(scb, pcb, TRUE); end_yin_elem(scb, mapping->argname, -1); } else { /* encode argname,value as an attribute */ ses_putchar(scb, ' '); ses_putstr(scb, mapping->argname); ses_putchar(scb, '='); ses_putchar(scb, '"'); write_cur_token(scb, pcb, FALSE); ses_putchar(scb, '"'); if (tk_next_typ(pcb->tkc) != TK_TT_SEMICOL) { ses_putchar(scb, '>'); } /* else end with empty element */ } } /* move token pointer to the stmt-end char */ res = advance_token(pcb); if (res != NO_ERR) { return res; } switch (TK_CUR_TYP(pcb->tkc)) { case TK_TT_SEMICOL: /* advance to next stmt, this one is done */ res = advance_token(pcb); if (res != NO_ERR) { return res; } if (mapping->elem) { /* end the complex element */ end_yin_elem(scb, mapping->keyword, startindent); } else { /* end the empty element */ ses_putstr(scb, (const xmlChar *)" />"); } break; case TK_TT_LBRACE: /* advance to next sub-stmt, this one has child nodes */ res = advance_token(pcb); if (res != NO_ERR) { return res; } /* write the nested sub-stmts as child nodes */ if (TK_CUR_TYP(pcb->tkc) != TK_TT_RBRACE) { loopdone = FALSE; while (!loopdone) { res = write_yin_stmt(pcb, cp, scb, startindent + cp->indent, done); if (res != NO_ERR) { return res; } if (TK_CUR_TYP(pcb->tkc) == TK_TT_RBRACE) { loopdone = TRUE; } } } /* move to next stmt, this one is done */ res = advance_token(pcb); if (res != NO_ERR) { return res; } /* end the complex element */ end_yin_elem(scb, mapping->keyword, startindent); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } break; case TK_TT_MSTRING: /* extension keyword */ prefix = TK_CUR_MOD(pcb->tkc); modprefix = ncx_get_mod_prefix(pcb->top); if (modprefix != NULL && !xml_strcmp(prefix, modprefix)) { /* local module */ extension = ext_find_extension(pcb->top, TK_CUR_VAL(pcb->tkc)); } else { import = ncx_find_pre_import(pcb->top, prefix); if (import == NULL || import->mod == NULL) { return ERR_NCX_IMP_NOT_FOUND; } extension = ext_find_extension(import->mod, TK_CUR_VAL(pcb->tkc)); } if (extension == NULL) { return ERR_NCX_DEF_NOT_FOUND; } /* got the extension for this external keyword * output keyword part */ start_ext_elem(scb, prefix, TK_CUR_VAL(pcb->tkc), startindent); /* output [string] part if expected */ if (extension->arg == NULL) { if (tk_next_typ(pcb->tkc) == TK_TT_LBRACE) { ses_putchar(scb, '>'); } } else { /* move token pointer to the argument string */ res = advance_token(pcb); if (res != NO_ERR) { return res; } /* write the string part * do not add any extra whiespace chars to the string */ if (extension->argel) { ses_putchar(scb, '>'); /* encode argname,value as an element */ start_ext_elem(scb, prefix, extension->arg, startindent + cp->indent); ses_putchar(scb, '>'); write_cur_token(scb, pcb, TRUE); end_ext_elem(scb, prefix, extension->arg, -1); } else { /* encode argname,value as an attribute */ ses_putchar(scb, ' '); ses_putstr(scb, extension->arg); ses_putchar(scb, '='); ses_putchar(scb, '"'); write_cur_token(scb, pcb, FALSE); ses_putchar(scb, '"'); if (tk_next_typ(pcb->tkc) != TK_TT_SEMICOL) { ses_putchar(scb, '>'); } /* else end with empty element */ } } /* move token pointer to the stmt-end char */ res = advance_token(pcb); if (res != NO_ERR) { return res; } switch (TK_CUR_TYP(pcb->tkc)) { case TK_TT_SEMICOL: /* advance to next stmt, this one is done */ res = advance_token(pcb); if (res != NO_ERR) { return res; } if (extension->arg != NULL && extension->argel) { /* end the complex element */ end_ext_elem(scb, prefix, extension->name, startindent); } else { /* end the empty element */ ses_putstr(scb, (const xmlChar *)" />"); } break; case TK_TT_LBRACE: /* advance to next sub-stmt, this one has child nodes */ res = advance_token(pcb); if (res != NO_ERR) { return res; } /* write the nested sub-stmts as child nodes */ if (TK_CUR_TYP(pcb->tkc) != TK_TT_RBRACE) { loopdone = FALSE; while (!loopdone) { res = write_yin_stmt(pcb, cp, scb, startindent + cp->indent, done); if (res != NO_ERR) { return res; } if (TK_CUR_TYP(pcb->tkc) == TK_TT_RBRACE) { loopdone = TRUE; } } } /* move to next stmt, this one is done */ res = advance_token(pcb); if (res != NO_ERR) { return res; } /* end the complex element */ end_ext_elem(scb, prefix, extension->name, startindent); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* write_yin_stmt */ /******************************************************************** * FUNCTION write_yin_contents * * Go through the token chain and write all the * YIN elements according to algoritm in sec. 11 * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * * RETURNS: * status *********************************************************************/ static status_t write_yin_contents (yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb) { status_t res; boolean done; int i; tk_reset_chain(pcb->tkc); res = NO_ERR; /* need to skip the first 3 tokens because the * [sub]module name { tokens have already been * handled as a special case */ for (i = 0; i < 4; i++) { res = advance_token(pcb); if (res != NO_ERR) { return res; } } done = FALSE; while (!done && res == NO_ERR) { res = write_yin_stmt(pcb, cp, scb, cp->indent, &done); } return res; } /* write_yin_contents */ /********* E X P O R T E D F U N C T I O N S **************/ /******************************************************************** * FUNCTION yangyin_convert_module * * The YIN namespace will be the default namespace * The imported modules will use the xmlprefix in use * which is the YANG prefix unless it is a duplicate * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * * RETURNS: * status *********************************************************************/ status_t yangyin_convert_module (yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb) { ncx_import_t *import; status_t res; int32 indent; res = NO_ERR; indent = cp->indent; /* start the XML document */ ses_putstr(scb, XML_START_MSG); /* write the top-level start-tag for [sub]module * do this special case so the XMLNS attributes * can be generated as well as the 'name' attribute */ start_yin_elem(scb, (pcb->top->ismod) ? YANG_K_MODULE : YANG_K_SUBMODULE, 0); ses_putchar(scb, ' '); /* write the name attribute */ write_yin_attr(scb, YANG_K_NAME, pcb->top->name, indent); /* write the YIN namespace decl as the default namespace */ write_yin_attr(scb, XMLNS, YIN_URN, indent); /* write the module prefix and module namespace */ if (pcb->top->ismod) { ses_putstr_indent(scb, XMLNS, indent); ses_putchar(scb, ':'); ses_putstr(scb, ncx_get_mod_prefix(pcb->top)); ses_putchar(scb, '='); ses_putchar(scb, '"'); ses_putstr(scb, ncx_get_modnamespace(pcb->top)); ses_putchar(scb, '"'); } /* write the xmlns decls for all the imports used * by this [sub]module */ for (import = (ncx_import_t *)dlq_firstEntry(&pcb->top->importQ); import != NULL; import = (ncx_import_t *)dlq_nextEntry(import)) { if (import->used) { write_import_xmlns(scb, import, indent); } } /* finish the top-level start tag */ ses_putchar(scb, '>'); res = write_yin_contents(pcb, cp, scb); /* finish the top-level element */ end_yin_elem(scb, (pcb->top->ismod) ? YANG_K_MODULE : YANG_K_SUBMODULE, 0); return res; } /* yangyin_convert_module */ /* END file yin.c */ yuma123_2.14/netconf/src/ydump/h.c0000664000175000017500000006031714770023131017106 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: h.c Generate H file output from YANG module Identifier type codes: Features --> 'F' Identities --> 'I' Data Nodes --> 'N' Typedef --> 'T' Identifier format: __ Identifiers are converted as follows: '.' --> '_' '-' --> '_' Collisions on pathalogical collisions are not supported yet ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 28mar09 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "c.h" #include "c_util.h" #include "dlq.h" #include "ext.h" #include "h.h" #include "ncx.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "rpc.h" #include "ses.h" #include "status.h" #include "typ.h" #include "yang.h" #include "yangconst.h" #include "yangdump.h" #include "yangdump_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION write_end_cplusplus * * Generate the end __cplusplus wrapper * * INPUTS: * scb == session control block to use for writing * *********************************************************************/ static void write_end_cplusplus (ses_cb_t *scb) { ses_putstr(scb, (const xmlChar *)"\n#ifdef __cplusplus"); ses_putstr(scb, (const xmlChar *)"\n} /* end extern 'C' */"); ses_putstr(scb, (const xmlChar *)"\n#endif"); } /* write_end_cplusplus */ /******************************************************************** * FUNCTION write_start_cplusplus * * Generate the start __cplusplus wrapper * * INPUTS: * scb == session control block to use for writing * *********************************************************************/ static void write_start_cplusplus (ses_cb_t *scb) { ses_putstr(scb, (const xmlChar *)"\n#ifdef __cplusplus"); ses_putstr(scb, (const xmlChar *)"\nextern \"C\" {"); ses_putstr(scb, (const xmlChar *)"\n#endif"); } /* write_start_cplusplus */ /******************************************************************** * FUNCTION write_h_object_typdef * * Generate the H file typdefs definitions for 1 data node * * INPUTS: * scb == session control block to use for writing * obj == obj_template_t to use * cdefQ == Q of c_define_t structs to check for identity binding **********************************************************************/ static void write_h_object_typdef (ses_cb_t *scb, obj_template_t *obj, dlq_hdr_t *cdefQ) { c_define_t *cdef; obj_template_t *childobj; ncx_btype_t btyp; boolean isleaflist, isanyxml; isleaflist = FALSE; isanyxml = FALSE; /* typedefs not needed for simple objects */ if (obj->objtype == OBJ_TYP_LEAF) { return; } else if (obj->objtype == OBJ_TYP_LEAF_LIST) { isleaflist = TRUE; } else if (obj->objtype == OBJ_TYP_ANYXML) { isanyxml = TRUE; } cdef = find_path_cdefine(cdefQ, obj); if (cdef == NULL) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return; } btyp = obj_get_basetype(obj); ses_putchar(scb, '\n'); write_h_iffeature_start(scb, &obj->iffeatureQ); /* generate the YOID as a comment */ write_c_oid_comment(scb, obj); /* start a new line and a C type definition */ ses_putstr(scb, START_TYPEDEF); if (isleaflist || isanyxml) { ses_putstr(scb, STRUCT); } else { /* print the C top-level data type */ switch (btyp) { case NCX_BT_CONTAINER: case NCX_BT_CASE: case NCX_BT_LIST: ses_putstr(scb, STRUCT); break; case NCX_BT_CHOICE: ses_putstr(scb, UNION); break; default: SET_ERROR(ERR_INTERNAL_VAL); return; } } ses_putchar(scb, ' '); /* the first 'real' name has an underscore on the end */ ses_putstr(scb, cdef->idstr); ses_putchar(scb, '_'); /* start the guts of the typedef */ ses_putstr(scb, START_BLOCK); /* generate a line for a Q header or a Queue */ if (obj->objtype == OBJ_TYP_LIST || obj->objtype == OBJ_TYP_LEAF_LIST || isanyxml) { write_qheader(scb); } if (isleaflist) { /* generate a line for the leaf-list data type */ ses_indent(scb, ses_indent_count(scb)); write_c_objtype_ex(scb, obj, cdefQ, ';', FALSE, FALSE); } else if (!isanyxml) { /* generate a line for each child node */ for (childobj = obj_first_child(obj); childobj != NULL; childobj = obj_next_child(childobj)) { if (!obj_has_name(childobj) || obj_is_cli(childobj) || obj_is_abstract(childobj)) { continue; } write_h_iffeature_start(scb, &childobj->iffeatureQ); if (childobj->objtype == OBJ_TYP_LEAF_LIST) { ses_indent(scb, ses_indent_count(scb)); ses_putstr(scb, QUEUE); ses_putchar(scb, ' '); write_c_safe_str(scb, obj_get_name(childobj)); ses_putchar(scb, ';'); } else { ses_indent(scb, ses_indent_count(scb)); write_c_objtype_ex(scb, childobj, cdefQ, ';', FALSE, FALSE); } write_h_iffeature_end(scb, &childobj->iffeatureQ); } } ses_putstr(scb, END_BLOCK); ses_putchar(scb, ' '); ses_putstr(scb, cdef->idstr); ses_putchar(scb, ';'); write_h_iffeature_end(scb, &obj->iffeatureQ); } /* write_h_object_typdef */ /******************************************************************** * FUNCTION write_h_objects * * Generate the YANG for the specified datadefQ * * INPUTS: * scb == session control block to use for writing * datadefQ == Q of obj_template_t to use * typcdefQ == Q of typename binding c_define_t structs * cp == conversion parms to use *********************************************************************/ static void write_h_objects (ses_cb_t *scb, dlq_hdr_t *datadefQ, dlq_hdr_t *typcdefQ, const yangdump_cvtparms_t *cp) { obj_template_t *obj; dlq_hdr_t *childdatadefQ; for (obj = (obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } childdatadefQ = obj_get_datadefQ(obj); if (childdatadefQ) { write_h_objects(scb, childdatadefQ, typcdefQ, cp); } write_h_object_typdef(scb, obj, typcdefQ); } } /* write_h_objects */ /******************************************************************** * FUNCTION write_h_identity * * Generate the #define for 1 identity * * INPUTS: * scb == session control block to use for writing * identity == ncx_identity_t to use * cp == conversion parms to use * *********************************************************************/ static void write_h_identity (ses_cb_t *scb, const ncx_identity_t *identity, const yangdump_cvtparms_t *cp) { /* define the identity constant */ ses_putstr(scb, POUND_DEFINE); write_identifier(scb, identity->tkerr.mod->name, BAR_ID, identity->name, cp->isuser); ses_putchar(scb, ' '); ses_putstr(scb, BAR_CONST); ses_putchar(scb, '"'); ses_putstr(scb, identity->name); ses_putchar(scb, '"'); } /* write_h_identity */ /******************************************************************** * FUNCTION write_h_identities * * Generate the H file delcs for the specified identityQ * * INPUTS: * scb == session control block to use for writing * identityQ == que of ncx_identity_t to use * cp == conversion parms to use *********************************************************************/ static void write_h_identities (ses_cb_t *scb, const dlq_hdr_t *identityQ, const yangdump_cvtparms_t *cp) { const ncx_identity_t *identity; if (dlq_empty(identityQ)) { return; } for (identity = (const ncx_identity_t *) dlq_firstEntry(identityQ); identity != NULL; identity = (const ncx_identity_t *) dlq_nextEntry(identity)) { write_h_identity(scb, identity, cp); } ses_putchar(scb, '\n'); } /* write_h_identities */ /******************************************************************** * FUNCTION write_h_oid * * Generate the #define for 1 object node identifier * * INPUTS: * scb == session control block to use for writing * cdef == c_define_t struct to use * *********************************************************************/ static void write_h_oid (ses_cb_t *scb, const c_define_t *cdef) { /* define the identity constant */ ses_putstr(scb, POUND_DEFINE); ses_putstr(scb, cdef->idstr); ses_putchar(scb, ' '); ses_putstr(scb, BAR_CONST); write_c_str(scb, cdef->valstr, 2); } /* write_h_oid */ /******************************************************************** * FUNCTION write_h_oids * * Generate the #defines for the specified #define statements * for name identifier strings * * INPUTS: * scb == session control block to use for writing * cdefineQ == que of c_define_t structs to use * *********************************************************************/ static void write_h_oids (ses_cb_t *scb, const dlq_hdr_t *cdefineQ) { const c_define_t *cdef; for (cdef = (const c_define_t *)dlq_firstEntry(cdefineQ); cdef != NULL; cdef = (const c_define_t *)dlq_nextEntry(cdef)) { write_h_oid(scb, cdef); } } /* write_h_oids */ /******************************************************************** * FUNCTION save_h_oids * * Save the identity to name bindings for the object names * * INPUTS: * scb == session control block to use for writing * cdefineQ == que of c_define_t structs to use * * RETURNS: * status *********************************************************************/ static status_t save_h_oids (ses_cb_t *scb, const dlq_hdr_t *datadefQ, dlq_hdr_t *cdefineQ) { const dlq_hdr_t *childQ; const obj_template_t *obj; status_t res; if (dlq_empty(datadefQ)) { return NO_ERR; } for (obj = (const obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (const obj_template_t *)dlq_nextEntry(obj)) { if (obj_has_name(obj) && !obj_is_cli(obj) && !obj_is_abstract(obj)) { if (obj->objtype != OBJ_TYP_RPCIO) { res = save_oid_cdefine(cdefineQ, obj_get_mod_name(obj), obj_get_name(obj)); if (res != NO_ERR) { return res; } } childQ = obj_get_cdatadefQ(obj); if (childQ) { res = save_h_oids(scb, childQ, cdefineQ); if (res != NO_ERR) { return res; } } } } return NO_ERR; } /* save_h_oids */ /******************************************************************** * FUNCTION write_h_feature * * Generate the #define for 1 feature statement * * INPUTS: * scb == session control block to use for writing * feature == ncx_feature_t to use *********************************************************************/ static void write_h_feature (ses_cb_t *scb, const ncx_feature_t *feature) { write_h_iffeature_start(scb, &feature->iffeatureQ); /* define feature constant */ ses_putstr(scb, (const xmlChar *)"\n/* Feature "); ses_putstr(scb, feature->tkerr.mod->name); ses_putchar(scb, ':'); ses_putstr(scb, feature->name); ses_putstr(scb, (const xmlChar *)"\n * Comment out to disable */"); ses_putstr(scb, POUND_DEFINE); write_identifier(scb, feature->tkerr.mod->name, BAR_FEAT, feature->name, TRUE); ses_putchar(scb, ' '); ses_putchar(scb, '1'); write_h_iffeature_end(scb, &feature->iffeatureQ); ses_putchar(scb, '\n'); } /* write_h_feature */ /******************************************************************** * FUNCTION write_h_features * * Generate the H file decls for the specified featureQ * * INPUTS: * scb == session control block to use for writing * featureQ == que of ncx_feature_t to use * *********************************************************************/ static void write_h_features (ses_cb_t *scb, const dlq_hdr_t *featureQ) { const ncx_feature_t *feature; if (dlq_empty(featureQ)) { return; } for (feature = (const ncx_feature_t *) dlq_firstEntry(featureQ); feature != NULL; feature = (const ncx_feature_t *) dlq_nextEntry(feature)) { write_h_feature(scb, feature); } ses_putchar(scb, '\n'); } /* write_h_features */ /******************************************************************** * FUNCTION write_h_includes * * Write the H file #include statements * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * *********************************************************************/ static void write_h_includes (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp) { boolean needrpc, neednotif; needrpc = need_rpc_includes(mod, cp); neednotif = need_notif_includes(mod, cp); /* add xmlChar include */ write_ext_include(scb, (const xmlChar *)"libxml/xmlstring.h"); if (cp->isuser) { write_ncx_include(scb, (const xmlChar *)"agt"); write_ncx_include(scb, (const xmlChar *)"agt_cb"); if (neednotif) { write_ncx_include(scb, (const xmlChar *)"agt_not"); } if (needrpc) { write_ncx_include(scb, (const xmlChar *)"agt_rpc"); } } write_ncx_include(scb, (const xmlChar *)"dlq"); write_ncx_include(scb, (const xmlChar *)"ncxtypes"); write_ncx_include(scb, (const xmlChar *)"op"); if (cp->isuser) { write_ncx_include(scb, (const xmlChar *)"rpc"); write_ncx_include(scb, (const xmlChar *)"ses"); } write_ncx_include(scb, (const xmlChar *)"status"); write_ncx_include(scb, (const xmlChar *)"val"); if (cp->format == NCX_CVTTYP_UH) { write_cvt_include(scb, ncx_get_modname(mod), NCX_CVTTYP_YH); } /* includes for submodules */ if (!cp->unified) { const ncx_include_t *inc; for (inc = (const ncx_include_t *) dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (const ncx_include_t *)dlq_nextEntry(inc)) { write_ncx_include(scb, inc->submodule); } } ses_putchar(scb, '\n'); } /* write_h_includes */ /******************************************************************** * FUNCTION write_h_file * * Generate the module start and header * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * * RETURNS: * status *********************************************************************/ static status_t write_h_file (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp) { yang_node_t *node; const xmlChar *prefix; dlq_hdr_t cdefineQ, typenameQ, objnameQ; status_t res = NO_ERR; switch (cp->format) { case NCX_CVTTYP_C: case NCX_CVTTYP_CPP_TEST: case NCX_CVTTYP_H: prefix = EMPTY_STRING; break; case NCX_CVTTYP_YC: case NCX_CVTTYP_YH: prefix = Y_PREFIX; break; case NCX_CVTTYP_UC: case NCX_CVTTYP_UH: prefix = U_PREFIX; break; default: return SET_ERROR(ERR_INTERNAL_VAL); } dlq_createSQue(&cdefineQ); dlq_createSQue(&typenameQ); dlq_createSQue(&objnameQ); /* Write the start of the H file */ ses_putstr(scb, POUND_IFNDEF); ses_putstr(scb, BAR_H); ses_putstr(scb, prefix); write_c_safe_str(scb, mod->name); ses_putstr(scb, POUND_DEFINE); ses_putstr(scb, BAR_H); ses_putstr(scb, prefix); write_c_safe_str(scb, mod->name); write_c_header(scb, mod, cp); write_h_includes(scb, mod, cp); write_start_cplusplus(scb); if (!cp->isuser) { ses_putchar(scb, '\n'); /* mod name */ ses_putstr(scb, POUND_DEFINE); write_identifier(scb, mod->name, BAR_MOD, mod->name, FALSE); ses_putchar(scb, ' '); ses_putstr(scb, BAR_CONST); ses_putchar(scb, '"'); ses_putstr(scb, mod->name); ses_putchar(scb, '"'); /* mod version */ ses_putstr(scb, POUND_DEFINE); write_identifier(scb, mod->name, BAR_REV, mod->name, FALSE); ses_putchar(scb, ' '); ses_putstr(scb, BAR_CONST); ses_putchar(scb, '"'); if (mod->version) { ses_putstr(scb, mod->version); } ses_putchar(scb, '"'); ses_putchar(scb, '\n'); } if (cp->format == NCX_CVTTYP_H || cp->format == NCX_CVTTYP_UH) { /* 1) features */ write_h_features(scb, &mod->featureQ); if (cp->unified && mod->ismod) { for (node = (yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_h_features(scb, &node->submod->featureQ); } } } } if (!cp->isuser) { /* 2) identities */ write_h_identities(scb, &mod->identityQ, cp); if (cp->unified && mod->ismod) { for (node = (yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_h_identities(scb, &node->submod->identityQ, cp); } } } /* 3) object node identifiers */ res = save_h_oids(scb, &mod->datadefQ, &cdefineQ); if (res == NO_ERR) { if (cp->unified && mod->ismod) { for (node = (yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL && res == NO_ERR; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { res = save_h_oids(scb, &node->submod->datadefQ, &cdefineQ); } } } } if (res == NO_ERR) { write_h_oids(scb, &cdefineQ); } } /* 4) typedefs for objects */ if (res == NO_ERR) { res = save_c_objects(mod, &mod->datadefQ, &typenameQ, C_MODE_TYPEDEF); if (res == NO_ERR) { if (cp->unified && mod->ismod) { for (node = (yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL && res == NO_ERR; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { res = save_c_objects(node->submod, &node->submod->datadefQ, &typenameQ, C_MODE_TYPEDEF); } } } } } if (res == NO_ERR && (cp->format == NCX_CVTTYP_H || cp->format == NCX_CVTTYP_UH)) { /* write typedefs for objects, not actually used in SIL code */ write_h_objects(scb, &mod->datadefQ, &typenameQ, cp); if (cp->unified && mod->ismod) { for (node = (yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_h_objects(scb, &node->submod->datadefQ, &typenameQ, cp); } } } } if (res == NO_ERR) { res = save_all_c_objects(mod, cp, &objnameQ, C_MODE_CALLBACK); if (res == NO_ERR) { c_write_fn_prototypes(mod, cp, scb, &objnameQ); } } write_end_cplusplus(scb); /* Write the end of the H file */ ses_putchar(scb, '\n'); ses_putstr(scb, POUND_ENDIF); clean_cdefineQ(&cdefineQ); clean_cdefineQ(&typenameQ); clean_cdefineQ(&objnameQ); return res; } /* write_h_file */ /********* E X P O R T E D F U N C T I O N S **************/ /******************************************************************** * FUNCTION h_convert_module * * Generate the SIL H file code for the specified module(s) * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * * RETURNS: * status *********************************************************************/ status_t h_convert_module (yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb) { ncx_module_t *mod; status_t res; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { return SET_ERROR(ERR_NCX_MOD_NOT_FOUND); } /* do not generate C code for obsolete objects */ ncx_delete_all_obsolete_objects(); res = write_h_file(scb, mod, cp); return res; } /* h_convert_module */ /* END file h.c */ yuma123_2.14/netconf/src/ydump/xsd_typ.c0000664000175000017500000015722314770023131020354 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xsd_typ.c Generate the XSD type constructs for yangdump: - simpleType - complexType ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24nov06 abb split out from xsd.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "cfg.h" #include "def_reg.h" #include "dlq.h" #include "ncx.h" #include "ncx_num.h" #include "ncxconst.h" #include "ncxtypes.h" #include "ncxmod.h" #include "obj.h" #include "rpc.h" #include "status.h" #include "typ.h" #include "val.h" #include "xmlns.h" #include "xml_util.h" #include "xml_val.h" #include "xsd_typ.h" #include "xsd_util.h" #include "yang.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* memberTypes per line hack */ #define PER_LINE 2 /******************************************************************** * FUNCTION add_err_annotation * * Check if an annotation element is needed * If so, create error-app-tag and/or error-message * appinfo elements and add the result to 'val * * INPUTS: * errmsg == error-message value * errtag == error-app-tag value * descr == description clause * ref == reference clause * val == value struct to add annotation as a child node * * RETURNS: * status *********************************************************************/ static status_t add_err_annotation (const xmlChar *errmsg, const xmlChar *errtag, const xmlChar *descr, const xmlChar *ref, val_value_t *val) { val_value_t *anval, *chval; status_t res; /* create the pattern element */ if (errmsg || errtag || descr || ref) { anval = xml_val_new_struct(XSD_ANNOTATION, xmlns_xs_id()); if (!anval) { return ERR_INTERNAL_MEM; } else { val_add_child(anval, val); } if (descr) { res = xsd_do_documentation(descr, anval); if (res != NO_ERR) { return res; } } if (errmsg || errtag || ref) { chval = xsd_make_err_appinfo(ref, errmsg, errtag); if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, anval); } } } return NO_ERR; } /* add_err_annotation */ /******************************************************************** * FUNCTION new_restriction * * Set up a new restriction struct * This is not complete if the 'hasnodes' parameter is TRUE * * INPUTS: * mod == module in progress (need target namespace ID) * base_nsid == namespace ID of the basetype name * basename == base type that this is a restriction from * hasnodes == TRUE if there will be child elements added * == FALSE if this restriction has no child nodes * * RETURNS: * new struct or NULL if malloc error *********************************************************************/ static val_value_t * new_restriction (const ncx_module_t *mod, xmlns_id_t base_nsid, const xmlChar *basename, boolean hasnodes) { val_value_t *val; xmlChar *str; status_t res; xmlns_id_t xsd_id; res = NO_ERR; xsd_id = xmlns_xs_id(); /* create a struct named 'restriction' to hold the entire result */ if (hasnodes) { val = xml_val_new_struct(XSD_RESTRICTION, xsd_id); } else { val = xml_val_new_flag(XSD_RESTRICTION, xsd_id); } if (!val) { return NULL; } /* add the base=QName attribute */ if (basename) { if (mod->nsid == base_nsid) { /* do not use the prefix if this is the target namespace */ res = xml_val_add_cattr(XSD_BASE, 0, basename, val); } else { /* not the target namespace, so need a QName instead of Name */ str = xml_val_make_qname(base_nsid, basename); if (str) { res = xml_val_add_attr(XSD_BASE, 0, str, val); if (res != NO_ERR) { m__free(str); } } else { res = ERR_INTERNAL_MEM; } } } if (res != NO_ERR) { val_free_value(val); return NULL; } else { return val; } /*NOTREACHED*/ } /* new_restriction */ /******************************************************************** * FUNCTION add_pattern * * Set up new patterns and add them as child nodes of 'val' * * INPUTS: * pat == pattern in progress * val == value struct to hold the pattern element * * RETURNS: * status *********************************************************************/ static status_t add_pattern (const typ_pattern_t *pat, val_value_t *val) { val_value_t *patval; status_t res; xmlns_id_t xsd_id; boolean errinfo_set; xsd_id = xmlns_xs_id(); errinfo_set = ncx_errinfo_set(&pat->pat_errinfo); /* create the pattern element */ if (errinfo_set || pat->pat_errinfo.descr || pat->pat_errinfo.ref) { patval = xml_val_new_struct(NCX_EL_PATTERN, xsd_id); if (!patval) { return ERR_INTERNAL_MEM; } else { val_add_child(patval, val); res = add_err_annotation(pat->pat_errinfo.error_message, pat->pat_errinfo.error_app_tag, pat->pat_errinfo.descr, pat->pat_errinfo.ref, patval); if (res != NO_ERR) { return res; } } } else { patval = xml_val_new_flag(NCX_EL_PATTERN, xsd_id); if (!patval) { return ERR_INTERNAL_MEM; } else { val_add_child(patval, val); } } res = xml_val_add_cattr(NCX_EL_VALUE, 0, pat->pat_str, patval); if (res != NO_ERR) { return res; } return NO_ERR; } /* add_pattern */ /******************************************************************** * FUNCTION add_patterns * * Set up new patterns and add them as child nodes of 'val' * * INPUTS: * typdef == typdef in progress * basename == basetype name string; NULL == xs:string * val == value struct to hold the pattern element * * RETURNS: * status *********************************************************************/ static status_t add_patterns (const ncx_module_t *mod, const typ_def_t *typdef, xmlns_id_t base_nsid, const xmlChar *basename, val_value_t *val) { val_value_t *simval, *restval, *curtop; const typ_pattern_t *pat; status_t res; xmlns_id_t xsd_id; uint32 patcnt, patnum; curtop = NULL; pat = NULL; xsd_id = xmlns_xs_id(); patcnt = typ_get_pattern_count(typdef); if (patcnt == 0) { return NO_ERR; } else if (patcnt == 1) { return add_pattern(typ_get_first_cpattern(typdef), val); } /* else multiple patterns require N-1 nested anonymous types * create simplType+restriction containers for the 1st through * N-1th pattern */ for (patnum = 1; patnum < patcnt; patnum++) { if (patnum == 1) { pat = typ_get_first_cpattern(typdef); } else { pat = typ_get_next_cpattern(pat); } simval = xml_val_new_struct(XSD_SIM_TYP, xsd_id); if (!simval) { if (curtop) { val_free_value(curtop); } return ERR_INTERNAL_MEM; } if (patnum == 1) { restval = new_restriction(mod, base_nsid, basename, TRUE); } else { restval = new_restriction(mod, 0, NULL, TRUE); } if (!restval) { if (curtop) { val_free_value(curtop); } val_free_value(simval); return ERR_INTERNAL_MEM; } else { val_add_child(restval, simval); } if (!curtop) { curtop = simval; } else { val_add_child(curtop, restval); curtop = simval; } res = add_pattern(pat, restval); if (res != NO_ERR) { val_free_value(curtop); return res; } } /* add the constructed tree to the restriction node passed in */ val_add_child(curtop, val); /* add the last pattern as a sibling of the new simpleType tree */ pat = typ_get_next_cpattern(pat); res = add_pattern(pat, val); return res; } /* add_patterns */ /******************************************************************** * FUNCTION add_enum_restriction * * Add an enumeration element to the parent restriction element * * INPUTS: * enu == enum struct (contains name, description, etc.) * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the types in this module * * RETURNS: * status *********************************************************************/ static status_t add_enum_restriction (const typ_enum_t *enu, val_value_t *val) { val_value_t *enuval, *annot, *appval; status_t res; xmlns_id_t xsd_id; xsd_id = xmlns_xs_id(); enuval = xml_val_new_struct(XSD_ENUMERATION, xsd_id); if (!enuval) { return ERR_INTERNAL_MEM; } else { val_add_child(enuval, val); /* add early */ } /* add value attribute */ res = xml_val_add_cattr(NCX_EL_VALUE, 0, enu->name, enuval); if (res != NO_ERR) { return res; } /* add annotation child element */ annot = xml_val_new_struct(XSD_ANNOTATION, xsd_id); if (!annot) { return ERR_INTERNAL_MEM; } else { val_add_child(annot, enuval); /* add early */ } /* add documentation child element */ if (enu->descr) { res = xsd_do_documentation(enu->descr, annot); if (res != NO_ERR) { return res; } } if (enu->ref) { res = xsd_do_reference(enu->ref, annot); if (res != NO_ERR) { return res; } } /* add appinfo child element */ if (enu->flags & TYP_FL_ISBITS) { appval = xsd_make_bit_appinfo(enu->pos, &enu->appinfoQ, enu->status); } else { appval = xsd_make_enum_appinfo(enu->val, &enu->appinfoQ, enu->status); } if (!appval) { return ERR_INTERNAL_MEM; } else { val_add_child(appval, annot); } return NO_ERR; } /* add_enum_restriction */ /******************************************************************** * FUNCTION make_enum * * Create a value struct representing an enumerated type * This is complete; The val_free_value function must be called * with the return value if it is non-NULL; * * INPUTS: * mod = module in progress * typdef == typ def for the type definition template * * RETURNS: * value struct or NULL if malloc error *********************************************************************/ static val_value_t * make_enum (const ncx_module_t *mod, const typ_def_t *typdef) { val_value_t *chval; const typ_enum_t *typ_enu; status_t res; /* build a restriction element */ chval = new_restriction(mod, xmlns_xs_id(), NCX_EL_STRING, TRUE); if (!chval) { return NULL; } /* add all the enumeration strings */ for (typ_enu = typ_first_con_enumdef(typdef); typ_enu != NULL; typ_enu = (const typ_enum_t *)dlq_nextEntry(typ_enu)) { res = add_enum_restriction(typ_enu, chval); if (res != NO_ERR) { val_free_value(chval); return NULL; } } return chval; } /* make_enum */ /******************************************************************** * FUNCTION finish_range * * Generate XSD constructs for a range * * INPUTS: * typdef == typ def for the type definition in progress * range = range in progress * rdef == first rangedef record (1 of N) * val == struct parent to contain child nodes for each range * * OUTPUTS: * val->v.childQ has entries added for the ranges found * * RETURNS: * status *********************************************************************/ static status_t finish_range (const typ_def_t *typdef, const typ_range_t *range, const typ_rangedef_t *rdef, val_value_t *val) { val_value_t *chval; xmlChar numbuff[NCX_MAX_NUMLEN]; uint32 len; xmlns_id_t xsd_id; boolean skipit, srange, errinfo_set; status_t res; xsd_id = xmlns_xs_id(); /* check if appinfo provided for the range; if so, * add it to the restriction of all the range-part simpleTypes */ skipit = FALSE; switch (typ_get_basetype(typdef)) { case NCX_BT_STRING: case NCX_BT_BINARY: srange = TRUE; break; default: srange = FALSE; } /* handle minInclusive first */ switch (rdef->btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: if (rdef->flags & TYP_FL_LBMIN) { skipit = TRUE; } else { res = ncx_sprintf_num(numbuff, &rdef->lb, rdef->btyp, &len); if (res != NO_ERR) { return res; } } break; case NCX_BT_FLOAT64: if (rdef->flags & TYP_FL_LBINF) { skipit = TRUE; } else { res = ncx_sprintf_num(numbuff, &rdef->lb, rdef->btyp, &len); if (res != NO_ERR) { return res; } } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } errinfo_set = ncx_errinfo_set(&range->range_errinfo) || range->range_errinfo.descr || range->range_errinfo.ref; if (!skipit) { if (errinfo_set) { if (srange) { chval = xml_val_new_struct(XSD_MIN_LEN, xsd_id); } else { chval = xml_val_new_struct(XSD_MIN_INCL, xsd_id); } } else { if (srange) { chval = xml_val_new_flag(XSD_MIN_LEN, xsd_id); } else { chval = xml_val_new_flag(XSD_MIN_INCL, xsd_id); } } if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, val); } res = xml_val_add_cattr(NCX_EL_VALUE, 0, numbuff, chval); if (res != NO_ERR) { return res; } if (errinfo_set) { res = add_err_annotation(range->range_errinfo.error_message, range->range_errinfo.error_app_tag, range->range_errinfo.descr, range->range_errinfo.ref, chval); if (res != NO_ERR) { return res; } } } /* handle maxInclusive next */ skipit = FALSE; switch (rdef->btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: if (rdef->flags & TYP_FL_UBMAX) { skipit = TRUE; } else { res = ncx_sprintf_num(numbuff, &rdef->ub, rdef->btyp, &len); if (res != NO_ERR) { return res; } } break; case NCX_BT_FLOAT64: if (rdef->flags & TYP_FL_UBINF) { skipit = TRUE; } else { res = ncx_sprintf_num(numbuff, &rdef->ub, rdef->btyp, &len); if (res != NO_ERR) { return res; } } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } if (!skipit) { if (errinfo_set) { if (srange) { chval = xml_val_new_struct(XSD_MAX_LEN, xsd_id); } else { chval = xml_val_new_struct(XSD_MAX_INCL, xsd_id); } } else { if (srange) { chval = xml_val_new_flag(XSD_MAX_LEN, xsd_id); } else { chval = xml_val_new_flag(XSD_MAX_INCL, xsd_id); } } if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, val); } res = xml_val_add_cattr(NCX_EL_VALUE, 0, numbuff, chval); if (res != NO_ERR) { return res; } if (errinfo_set) { res = add_err_annotation(range->range_errinfo.error_message, range->range_errinfo.error_app_tag, range->range_errinfo.descr, range->range_errinfo.ref, chval); if (res != NO_ERR) { return res; } } } return NO_ERR; } /* finish_range */ /******************************************************************** * FUNCTION new_range_union * * Create a union of simple types in order to handle * a range definition with multiple parts, such as: * * range "min .. 4 | 7 | 11"; * * INPUTS: * mod == module in progress (need target namespace ID) * range == range struct to use * rangeQ == Q of typ_rangedef_t * typdef == type definition for the simple or named type * generating the range * base_id == NS id of the base type name * basename == name string for the base type name * * RETURNS: * new struct or NULL if malloc error *********************************************************************/ static val_value_t * new_range_union (const ncx_module_t *mod, const typ_range_t *range, const dlq_hdr_t *rangeQ, const typ_def_t *typdef, xmlns_id_t base_id, const xmlChar *basename) { val_value_t *val, *chval, *rval; const typ_rangedef_t *rv; status_t res; xmlns_id_t xsd_id; uint32 pattern_cnt; xsd_id = xmlns_xs_id(); pattern_cnt = typ_get_pattern_count(typdef); val = xml_val_new_struct(XSD_UNION, xsd_id); if (!val) { return NULL; } /* go through the Q of range parts and add a simpleType * for each one */ for (rv = (const typ_rangedef_t *)dlq_firstEntry(rangeQ); rv != NULL; rv = (const typ_rangedef_t *)dlq_nextEntry(rv)) { chval = xml_val_new_struct(XSD_SIM_TYP, xsd_id); if (!chval) { val_free_value(val); return NULL; } else { val_add_child(chval, val); /* add early */ } rval = new_restriction(mod, base_id, (pattern_cnt < 1) ? basename : NULL, TRUE); if (!rval) { val_free_value(val); return NULL; } else { val_add_child(rval, chval); /* add early */ } res = add_patterns(mod, typdef, base_id, basename, rval); if (res != NO_ERR) { val_free_value(val); return NULL; } res = finish_range(typdef, range, rv, rval); if (res != NO_ERR) { val_free_value(val); return NULL; } } return val; } /* new_range_union */ /******************************************************************** * FUNCTION finish_list * * Generate the XSD for a list element * * INPUTS: * mod == module in progress * btyp == type of the typedef (NCX_BT_SLIST) * typdef == typdef for the list * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the nodes in this typdef * * RETURNS: * status *********************************************************************/ static status_t finish_list (const ncx_module_t *mod, ncx_btype_t btyp, typ_def_t *typdef, val_value_t *val) { typ_template_t *listtyp; const typ_def_t *rtypdef; const ncx_num_t *lb, *ub; const xmlChar *typname; val_value_t *top, *simtyp, *rest, *lval; status_t res; int32 ret; uint32 len; xmlns_id_t xsd_id, list_id; ncx_btype_t range_btyp; xmlChar *str, numbuff[NCX_MAX_NUMLEN]; res = NO_ERR; xsd_id = xmlns_xs_id(); /* get the list member typedef */ if (btyp == NCX_BT_SLIST) { listtyp = typ_get_listtyp(typdef); list_id = typ_get_nsid(listtyp); typname = listtyp->name; } else { list_id = xsd_id; typname = NCX_EL_STRING; } /* see if there are any length restrictions on the list typedef * not the list member -- this controls the number of entries * allowed */ rtypdef = typ_get_cqual_typdef(typdef, NCX_SQUAL_RANGE); if (rtypdef) { /* complex form with inline restriction */ top = xml_val_new_struct(XSD_LIST, xsd_id); if (!top) { return ERR_INTERNAL_MEM; } } else { /* simple form with itemType='QName' */ top = xml_val_new_flag(XSD_LIST, xsd_id); if (top) { /* construct the itemType="foo:bar" attribute */ if (list_id != mod->nsid) { str = xml_val_make_qname(list_id, typname); } else { str = xml_strdup(typname); } if (!str) { val_free_value(top); return ERR_INTERNAL_MEM; } /* add the itemType attribute to the list element */ res = xml_val_add_attr(XSD_ITEMTYPE, 0, str, top); if (res != NO_ERR) { m__free(str); val_free_value(top); return res; } } else { return ERR_INTERNAL_MEM; } } /* add the length constraints if needed */ if (rtypdef) { res = typ_get_rangebounds_con(rtypdef, &range_btyp,&lb, &ub); if (res != NO_ERR) { /* should not happen since rtypdef is non-NULL */ val_free_value(top); return SET_ERROR(res); } /* need a simple type wrapper */ simtyp = xml_val_new_struct(XSD_SIM_TYP, xsd_id); if (!simtyp) { val_free_value(top); return ERR_INTERNAL_MEM; } else { val_add_child(simtyp, top); } /* need a restriction wrapper */ rest = new_restriction(mod, list_id, typname, TRUE); if (!rest) { val_free_value(top); return ERR_INTERNAL_MEM; } else { val_add_child(rest, simtyp); } /* check fixed length or a range given */ ret = ncx_compare_nums(lb, ub, range_btyp); if (ret) { /* range given, gen lower bound if not zero */ if (!ncx_num_zero(lb, range_btyp)) { lval = xml_val_new_flag(XSD_MIN_LEN, xsd_id); if (!lval) { res = ERR_INTERNAL_MEM; } else { val_add_child(lval, rest); res = ncx_sprintf_num(numbuff, lb, range_btyp, &len); if (res == NO_ERR) { res = xml_val_add_cattr(XSD_VALUE, 0, numbuff, lval); } } if (res != NO_ERR) { val_free_value(top); return res; } } /* gen the upper bound */ lval = xml_val_new_flag(XSD_MAX_LEN, xsd_id); if (!lval) { res = ERR_INTERNAL_MEM; } else { val_add_child(lval, rest); res = ncx_sprintf_num(numbuff, ub, range_btyp, &len); if (res == NO_ERR) { res = xml_val_add_cattr(XSD_VALUE, 0, numbuff, lval); } } } else { /* lb and ub equal, fixed range given */ lval = xml_val_new_flag(XSD_LENGTH, xsd_id); if (!lval) { res = ERR_INTERNAL_MEM; } else { val_add_child(lval, rest); res = xml_val_add_cattr(XSD_FIXED, 0, XSD_TRUE, lval); if (res == NO_ERR) { res = ncx_sprintf_num(numbuff, lb, range_btyp, &len); if (res == NO_ERR) { res = xml_val_add_cattr(XSD_VALUE, 0, numbuff, lval); } } } } if (res != NO_ERR) { val_free_value(top); return res; } } val_add_child(top, val); return NO_ERR; } /* finish_list */ #if 0 /* attributes not supported in YANG */ /******************************************************************** * FUNCTION finish_attributes * * Generate all the attribute nodes declared in this typ def * * INPUTS: * mod == module conversion in progress * typdef == typ_def for the typ_template struct to use for * the attribute list source * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the attributes in this typdef * * RETURNS: * status *********************************************************************/ static status_t finish_attributes (const ncx_module_t *mod, const typ_def_t *typdef, val_value_t *val) { val_value_t *aval; const typ_child_t *typ_attr; const xmlChar *name; ncx_iqual_t iqual; status_t res; xmlns_id_t xsd_id; xsd_id = xmlns_xs_id(); for (typ_attr = typ_first_meta_con(typdef); typ_attr != NULL; typ_attr = (typ_child_t *)dlq_nextEntry(typ_attr)) { /* get an attribute node */ aval = xml_val_new_flag(XSD_ATTRIBUTE, xsd_id); if (!aval) { return ERR_INTERNAL_MEM; } /* set the attribute name */ res = xml_val_add_attr(NCX_EL_NAME, 0, typ_attr->name, aval); if (res != NO_ERR) { val_free_value(aval); return res; } /* set the data type */ res = xsd_add_type_attr(mod, &typ_attr->typdef, aval); if (res != NO_ERR) { val_free_value(aval); return res; } /* add the use attribute */ iqual = typ_get_iqualval_def(&typ_attr->typdef); switch (iqual) { case NCX_IQUAL_ONE: case NCX_IQUAL_1MORE: res = xml_val_add_cattr(XSD_USE, 0, XSD_REQUIRED, aval); break; case NCX_IQUAL_OPT: case NCX_IQUAL_ZMORE: /* do nothing since this is the default */ /* res = xml_val_add_cattr(XSD_USE, 0, XSD_OPTIONAL, aval); */ break; case NCX_IQUAL_NONE: default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { val_free_value(aval); return res; } /* check if there is a default value */ name = typ_get_default(&typ_attr->typdef); if (name) { res = xml_val_add_cattr(NCX_EL_DEFAULT, 0, name, aval); if (res != NO_ERR) { val_free_value(aval); return res; } } /* add the attribute node to the parent node * inside a complexType */ val_add_child(aval, val); } return NO_ERR; } /* finish_attributes */ #endif /******************************************************************** * FUNCTION finish_simpleMetaType * * Generate a complexType element based on the typdef for a * simple type that has attributes defined * * INPUTS: * mod == module in progress * typ == typ_template struct to convert to a simpleType * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the types in this module * * RETURNS: * status *********************************************************************/ static status_t finish_simpleMetaType (const ncx_module_t *mod, const typ_template_t *typ, val_value_t *val) { val_value_t *simcon, *ext; xmlns_id_t xsd_id; xmlChar *basename; status_t res; mod = mod; /*** lint error **/ xsd_id = xmlns_xs_id(); simcon = xml_val_new_struct(XSD_SIM_CON, xsd_id); if (!simcon) { return ERR_INTERNAL_MEM; } ext = xml_val_new_struct(XSD_EXTENSION, xsd_id); if (!ext) { val_free_value(simcon); return ERR_INTERNAL_MEM; } val_add_child(ext, simcon); /* add early */ basename = xsd_make_basename(typ); if (!basename) { val_free_value(simcon); return ERR_INTERNAL_MEM; } /* Note: Since the targetNamespace is set to the default namespace * the basename is not converted to a QName here, since no prefix * will be added anyway */ res = xml_val_add_attr(XSD_BASE, 0, basename, ext); if (res != NO_ERR) { m__free(basename); val_free_value(simcon); return ERR_INTERNAL_MEM; } #if 0 /* attributes not supported in YANG */ /* convert all the metadata to attribute nodes */ res = finish_attributes(mod, &typ->typdef, ext); if (res != NO_ERR) { val_free_value(simcon); return ERR_INTERNAL_MEM; } #endif /* add the simpleContent node and exit */ val_add_child(simcon, val); return NO_ERR; } /* finish_simpleMetaType */ /************* E X T E R N A L F U N C T I O N S *****************/ /******************************************************************** * FUNCTION xsd_add_types * * Add the required type nodes * * INPUTS: * mod == module in progress * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->childQ has entries added for the types in this module * * RETURNS: * status *********************************************************************/ status_t xsd_add_types (const ncx_module_t *mod, val_value_t *val) { typ_template_t *typ; typ_def_t *typdef; val_value_t *tval; xmlChar *basename; xmlns_id_t xsd_id; ncx_btype_t btyp; ncx_tclass_t tclass; status_t res; boolean sim, base; xsd_id = xmlns_xs_id(); for (typ = (typ_template_t *)dlq_firstEntry(&mod->typeQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { /* get type info */ typdef = &typ->typdef; btyp = typ_get_basetype(typdef); tclass = typdef->tclass; /* A simple type that has attributes defined needs * to be declared in a simpleType for the base without * any attributes, and a complexType that is an extension * of the base type */ /* base = (typ_first_meta(&typ->typdef)) ? TRUE : FALSE; */ base = FALSE; /* figure out if this is a complexType or a simpleType * The ename and flag data types are simple in NCX but * need to be complexTypes in XSD */ sim = typ_is_xsd_simple(btyp); if (sim) { tval = xml_val_new_struct(XSD_SIM_TYP, xsd_id); } else { tval = xml_val_new_struct(XSD_CPX_TYP, xsd_id); } if (!tval) { return ERR_INTERNAL_MEM; } /* if this is a base type then 2 types will be generated * 1) base type * 2) extension of base type with attributes added * * First get the struct for the type */ if (base) { basename = xsd_make_basename(typ); if (!basename) { val_free_value(tval); return ERR_INTERNAL_MEM; } res = xml_val_add_attr(NCX_EL_NAME, 0, basename, tval); if (res != NO_ERR) { m__free(basename); } } else { res = xml_val_add_cattr(NCX_EL_NAME, 0, typ->name, tval); } if (res != NO_ERR) { val_free_value(tval); return res; } if (base) { /* create the base type */ if (tclass==NCX_CL_NAMED) { res = xsd_finish_namedType(mod, &typ->typdef, tval); } else if (sim) { res = xsd_finish_simpleType(mod, &typ->typdef, tval); } if (res != NO_ERR) { val_free_value(tval); return res; } else { val_add_child(tval, val); } /* start the real type, reuse tval */ tval = xml_val_new_struct(XSD_CPX_TYP, xsd_id); if (!tval) { return ERR_INTERNAL_MEM; } res = xml_val_add_cattr(NCX_EL_NAME, 0, typ->name, tval); if (res != NO_ERR) { val_free_value(tval); return res; } } /* add an annotation node if there are any * 'extra' or appinfo clauses present */ res = xsd_do_type_annotation(typ, tval); if (res != NO_ERR) { val_free_value(tval); return res; } if (base) { res = finish_simpleMetaType(mod, typ, tval); } else if (tclass==NCX_CL_NAMED) { res = xsd_finish_namedType(mod, &typ->typdef, tval); } else if (sim) { res = xsd_finish_simpleType(mod, &typ->typdef, tval); } else { /* res = finish_complexType(mod, &typ->typdef, tval); */ res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { val_free_value(tval); return res; } else { val_add_child(tval, val); } } return NO_ERR; } /* xsd_add_types */ /******************************************************************** * FUNCTION xsd_add_local_types * * Add the required type nodes for the local types in the typnameQ * YANG Only -- NCX does not have local types * * INPUTS: * mod == module in progress * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->childQ has entries added for the types in this module * * RETURNS: * status *********************************************************************/ status_t xsd_add_local_types (const ncx_module_t *mod, val_value_t *val) { ncx_typname_t *typnam; typ_template_t *typ; typ_def_t *typdef; val_value_t *tval; xmlns_id_t xsd_id; ncx_btype_t btyp; ncx_tclass_t tclass; status_t res; boolean sim; res = NO_ERR; xsd_id = xmlns_xs_id(); for (typnam = (ncx_typname_t *)dlq_firstEntry(&mod->typnameQ); typnam != NULL; typnam = (ncx_typname_t *)dlq_nextEntry(typnam)) { /* get type info */ typ = typnam->typ; typdef = &typ->typdef; btyp = typ_get_basetype(typdef); tclass = typdef->tclass; /* figure out if this is a complexType or a simpleType * The ename and flag data types are simple in NCX but * need to be complexTypes in XSD */ sim = typ_is_xsd_simple(btyp); if (sim) { tval = xml_val_new_struct(XSD_SIM_TYP, xsd_id); } else { tval = xml_val_new_struct(XSD_CPX_TYP, xsd_id); } if (!tval) { return ERR_INTERNAL_MEM; } else { val_add_child(tval, val); } res = xml_val_add_cattr(NCX_EL_NAME, 0, typnam->typname, tval); if (res != NO_ERR) { return res; } /* add an annotation node if there are any * 'extra' or appinfo clauses present */ res = xsd_do_type_annotation(typ, tval); if (res != NO_ERR) { return res; } if (tclass==NCX_CL_NAMED) { res = xsd_finish_namedType(mod, &typ->typdef, tval); } else if (sim) { res = xsd_finish_simpleType(mod, &typ->typdef, tval); } else { res = SET_ERROR(ERR_INTERNAL_VAL); /* res = finish_complexType(mod, &typ->typdef, tval); */ } } return res; } /* xsd_add_local_types */ /******************************************************************** * FUNCTION xsd_finish_simpleType * * Generate a simpleType elment based on the typdef * Note: NCX_BT_ENAME is considered a * complexType for XSD conversion purposes * * INPUTS: * mod == module in progress * typdef == typ def for the typ_template struct to convert * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the types in this module * * RETURNS: * status *********************************************************************/ status_t xsd_finish_simpleType (const ncx_module_t *mod, typ_def_t *typdef, val_value_t *val) { val_value_t *topcon, *simtyp, *chval; const typ_rangedef_t *typ_rdef; const xmlChar *typename; const dlq_hdr_t *rangeQ; const typ_range_t *range; uint32 rangecnt, patterncnt; xmlns_id_t xsd_id; ncx_btype_t btyp; status_t res; xsd_id = xmlns_xs_id(); btyp = typ_get_basetype(typdef); typename = xsd_typename(btyp); range = typ_get_crange_con(typdef); rangeQ = typ_get_crangeQ_con(typdef); rangecnt = (rangeQ) ? dlq_count(rangeQ) : 0; typ_rdef = typ_first_rangedef_con(typdef); res = NO_ERR; if (rangecnt > 1) { chval = new_range_union(mod, range, rangeQ, typdef, xsd_id, typename); if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, val); return NO_ERR; } } switch (btyp) { case NCX_BT_ENUM: chval = make_enum(mod, typdef); if (!chval) { return ERR_INTERNAL_MEM; } else { /* add the restriction element to the simpleType */ val_add_child(chval, val); } break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: case NCX_BT_BINARY: case NCX_BT_BOOLEAN: case NCX_BT_INSTANCE_ID: chval = new_restriction(mod, xsd_id, typename, (rangecnt) ? TRUE : FALSE); if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, val); } if (rangecnt) { res = finish_range(typdef, range, typ_rdef, chval); if (res != NO_ERR) { return res; } } break; case NCX_BT_STRING: switch (typdef->def.simple.strrest) { case NCX_SR_NONE: /* build a restriction element */ chval = new_restriction(mod, xsd_id, typename, (rangecnt) ? TRUE : FALSE); if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, val); } if (rangecnt) { res = finish_range(typdef, range, typ_rdef, chval); if (res != NO_ERR) { return res; } } break; case NCX_SR_PATTERN: /* get the pattern count */ patterncnt = typ_get_pattern_count(typdef); if (patterncnt == 0) { return SET_ERROR(ERR_INTERNAL_VAL); } /* build a restriction element */ chval = new_restriction(mod, xsd_id, (patterncnt == 1) ? NCX_EL_STRING : NULL, TRUE); if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, val); } /* create the pattern element */ res = add_patterns(mod, typdef, xsd_id, NCX_EL_STRING, chval); if (res != NO_ERR) { return res; } if (rangecnt) { res = finish_range(typdef, range, typ_rdef, chval); } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } break; case NCX_BT_SLIST: res = finish_list(mod, btyp, typdef, val); break; case NCX_BT_UNION: res = xsd_finish_union(mod, typdef, val); break; case NCX_BT_LEAFREF: case NCX_BT_IDREF: break; case NCX_BT_BITS: /* a bits type is list of identifier strings */ topcon = xml_val_new_struct(NCX_EL_LIST, xsd_id); if (!topcon) { return ERR_INTERNAL_MEM; } else { val_add_child(topcon, val); /* add early */ } /* add inline simpleType */ simtyp = xml_val_new_struct(XSD_SIM_TYP, xsd_id); if (!simtyp) { return ERR_INTERNAL_MEM; } else { val_add_child(simtyp, topcon); /* add early */ } /* build an enumeration restriction */ chval = make_enum(mod, typdef); if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, simtyp); } break; case NCX_BT_EMPTY: /* treat this as a string, max length is zero * !!!! this is not used at this time !!!!! * !!!! the NCX_BT_EMPTY is a complexType !!!! */ topcon = new_restriction(mod, xsd_id, typename, TRUE); if (!topcon) { return ERR_INTERNAL_MEM; } else { val_add_child(topcon, val); } chval = xml_val_new_flag(XSD_MAX_LEN, xsd_id); if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, topcon); } res = xml_val_add_cattr(NCX_EL_VALUE, 0, XSD_ZERO, chval); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* xsd_finish_simpleType */ /******************************************************************** * FUNCTION xsd_finish_namedType * * Generate a complexType elment based on the typdef for a named type * The tclass must be NCX_CL_NAMED for the typdef data struct * * Note: The "+=" type extension mechanism in NCX is not supportable * in XSD for strings, so it must not be used at this time. * It is not supported for any data type, even enum. * * INPUTS: * mod == module in progress * typdef == typ def for the typ_template struct to convert * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the types in this module * * RETURNS: * status *********************************************************************/ status_t xsd_finish_namedType (const ncx_module_t *mod, typ_def_t *typdef, val_value_t *val) { val_value_t *topcon, *chval; const typ_range_t *range; const dlq_hdr_t *rangeQ; const typ_rangedef_t *rdef; const typ_sval_t *sdef; const xmlChar *typename; const obj_template_t *mdef; /**** not used yet ****/ xmlChar *str; uint32 patcnt; ncx_btype_t btyp; status_t res; xmlns_id_t xsd_id, test_id; boolean issim, isext, hasnodes; /* init local vars */ res = NO_ERR; xsd_id = xmlns_xs_id(); btyp = typ_get_basetype(typdef); /* NCX base type */ rdef = typ_first_rangedef_con(typdef); /* range def Q */ range = typ_get_crange_con(typdef); mdef = NULL; /*** meta-data not checked yet ***/ sdef = typ_first_strdef(typdef); /* string Q */ patcnt = typ_get_pattern_count(typdef); /* pattern Q */ issim = typ_is_xsd_simple(btyp); /* container type */ typename = typ_get_named_typename(typdef); test_id = typdef->def.named.typ->nsid; /* NS of the name */ if (!test_id) { test_id = mod->nsid; } /* check if this is simpleContent or complexContent */ if (!issim) { topcon = xml_val_new_struct(XSD_CPX_CON, xsd_id); if (!topcon) { return ERR_INTERNAL_MEM; } else { val_add_child(topcon, val); /* add early */ } } else { /* simpleType does not have a simpleContent container * so use the top-level 'val' node instead */ topcon = val; } /* check if this is a restriction or an extension from the base type * if a range def exists (rdef) then an extension is needed; * attributes require a complexType container; * * Only the semantics defined in the particular type * are examined, and just the extensions or restrictions * from the named parent type */ switch (btyp) { case NCX_BT_BOOLEAN: case NCX_BT_INSTANCE_ID: isext = FALSE; hasnodes = (mdef) ? TRUE : FALSE; break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: isext = FALSE; hasnodes = (mdef || rdef) ? TRUE : FALSE; break; case NCX_BT_STRING: case NCX_BT_BINARY: isext = FALSE; hasnodes = (rdef || patcnt || mdef) ? TRUE : FALSE; break; case NCX_BT_ENUM: case NCX_BT_BITS: hasnodes = (sdef || mdef) ? TRUE : FALSE; isext = hasnodes; break; case NCX_BT_UNION: case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_EMPTY: case NCX_BT_SLIST: case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_LIST: hasnodes = (mdef) ? TRUE : FALSE; isext = hasnodes; break; case NCX_BT_LEAFREF: isext = FALSE; hasnodes = TRUE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); return res; } /* check if this is a restriction type with a multi-part range */ if (!isext && rdef) { rangeQ = typ_get_crangeQ_con(typdef); if (dlq_count(rangeQ) > 1) { chval = new_range_union(mod, range, rangeQ, typdef, test_id, typename); if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, topcon); if (!issim) { val_add_child(topcon, val); } return NO_ERR; } } } /* create an extension or restriction */ if (isext) { if (hasnodes) { chval = xml_val_new_struct(XSD_EXTENSION, xsd_id); } else { chval = xml_val_new_flag(XSD_EXTENSION, xsd_id); } } else { if (hasnodes) { chval = xml_val_new_struct(XSD_RESTRICTION, xsd_id); } else { chval = xml_val_new_flag(XSD_RESTRICTION, xsd_id); } } if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, topcon); /* add early */ } /* add the base=QName attribute */ if (patcnt <= 1) { if (test_id == mod->nsid) { /* named type is a top-level definition */ res = xml_val_add_cattr(XSD_BASE, 0, typename, chval); if (res != NO_ERR) { return ERR_INTERNAL_MEM; } } else if (!test_id) { /* named type is a local type */ res = xml_val_add_cattr (XSD_BASE, 0, ncx_find_typname(typdef->def.named.typ, &mod->typnameQ), chval); if (res != NO_ERR) { return ERR_INTERNAL_MEM; } } else { /* add base=QName attribute */ str = xml_val_make_qname(test_id, typename); if (!str) { return ERR_INTERNAL_MEM; } res = xml_val_add_attr(XSD_BASE, 0, str, chval); if (res != NO_ERR) { m__free(str); return ERR_INTERNAL_MEM; } } } if (hasnodes && typdef->def.named.newtyp) { res = NO_ERR; if (!isext && patcnt) { res = add_patterns(mod, typdef, test_id, typename, chval); } /* check if this is an range restriction */ if (res == NO_ERR && !isext && rdef) { res = finish_range(typdef, range, rdef, chval); } #if 0 /* attributes not supported in YANG */ /* check if this is an attribute extension */ if (res==NO_ERR && isext && mdef) { /* add any attributes defined for this type */ res = finish_attributes(mod, typdef->def.named.newtyp, chval); } #endif } return res; } /* xsd_finish_namedType */ /******************************************************************** * FUNCTION xsd_finish_union * * Generate the XSD for a union element * * INPUTS: * mod == module in progress * typdef == typdef for the list * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the nodes in this typdef * * RETURNS: * status *********************************************************************/ status_t xsd_finish_union (const ncx_module_t *mod, typ_def_t *typdef, val_value_t *val) { typ_unionnode_t *un; const xmlChar *typname; val_value_t *top, *simtyp; typ_def_t *untypdef; xmlChar *str, *p; uint32 len, siz, perline; xmlns_id_t union_id, xsd_id; status_t res; boolean simple_form; res = NO_ERR; len = 0; perline = PER_LINE; simple_form = TRUE; xsd_id = xmlns_xs_id(); un = typ_first_unionnode(typdef); if (!un) { return SET_ERROR(ERR_INTERNAL_VAL); } /* determine the length of memberTypes attribute string */ while (un && simple_form) { if (un->typ) { /* the union member is an NCX named type only * or a YANG named type clause */ union_id = typ_get_nsid(un->typ); if (!union_id) { union_id = mod->nsid; } typname = un->typ->name; if (union_id != mod->nsid) { len += xml_val_qname_len(union_id, typname) + 1; } else { len += xml_strlen(typname) + 1; } un = (typ_unionnode_t *)dlq_nextEntry(un); } else { /* the union member is an unnamed YANG type clause */ simple_form = FALSE; } } if (simple_form) { /* simple form with memberTypes=list of 'QName' */ top = xml_val_new_flag(XSD_UNION, xsd_id); if (!top) { return ERR_INTERNAL_MEM; } /* get the string buffer */ str = m__getMem(++len); if (!str) { val_free_value(top); return ERR_INTERNAL_MEM; } /* reset the union type list pointer */ un = typ_first_unionnode(typdef); p = str; /* construct the memberTypes attribute value string */ while (un) { union_id = typ_get_nsid(un->typ); if (!union_id) { union_id = mod->nsid; } typname = un->typ->name; if (union_id != mod->nsid) { siz = xml_val_sprintf_qname(p, len, union_id, typname); } else { siz = xml_strcpy(p, typname); } p += siz; if (!--perline) { *p++ = '\n'; perline = PER_LINE; } else { *p++ = ' '; } len -= siz+1; un = (typ_unionnode_t *)dlq_nextEntry(un); } /* undo the last (extra) space */ *--p = 0; /* add the itemType attribute to the list element */ res = xml_val_add_attr(XSD_MEMBERTYPES, 0, str, top); if (res != NO_ERR) { m__free(str); val_free_value(top); return res; } } else { /* at least one unnamed type within the union * so all member types have to be in the inline form */ top = xml_val_new_struct(XSD_UNION, xsd_id); if (!top) { return ERR_INTERNAL_MEM; } /* reset the union type list pointer */ un = typ_first_unionnode(typdef); while (un && res==NO_ERR) { simtyp = xml_val_new_struct(XSD_SIM_TYP, xsd_id); if (!simtyp) { val_free_value(top); return ERR_INTERNAL_MEM; } else { val_add_child(simtyp, top); /* add early */ } untypdef = un->typdef; if (untypdef->tclass == NCX_CL_NAMED) { res = xsd_finish_namedType(mod, untypdef, simtyp); } else { res = xsd_finish_simpleType(mod, untypdef, simtyp); } if (res == NO_ERR) { un = (typ_unionnode_t *)dlq_nextEntry(un); } } if (res != NO_ERR) { val_free_value(top); return res; } } val_add_child(top, val); return NO_ERR; } /* xsd_finish_union */ /* END file xsd_typ.c */ yuma123_2.14/netconf/src/ydump/xsd.c0000664000175000017500000004015414770023131017452 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xsd.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 15nov06 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "cfg.h" #include "dlq.h" #include "ncx.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "rpc.h" #include "status.h" #include "typ.h" #include "val.h" #include "xmlns.h" #include "xml_util.h" #include "xml_val.h" #include "xsd.h" #include "xsd_typ.h" #include "xsd_util.h" #include "xsd_yang.h" #include "yang.h" #include "yangdump.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION add_one_prefix * * Add one prefix clause, after checking if it is needed first * * INPUTS: * pcb == parser control block * modname == import module name to check * revision == module revision to find (may be NULL) * top_attrs == pointer to receive the top element attributes * HACK: rpc_agt needs the top-level attrs to * be in xml_attr_t format, not val_value_t metaval format * * OUTPUTS: * *retattrs = may have an attribute added to this Q * * RETURNS: * status *********************************************************************/ static status_t add_one_prefix (yang_pcb_t *pcb, const xmlChar *modname, const xmlChar *revision, xml_attrs_t *top_attrs) { const xmlChar *cstr; ncx_module_t *immod; xml_attr_t *test; status_t res; boolean found; res = NO_ERR; /* don't use the NCX XSD module, use XSD directly */ if (!xml_strcmp(modname, (const xmlChar *)"xsd")) { return NO_ERR; } /* first load the module to get the NS ID * It will probably be loaded anyway to find external names. */ immod = ncx_find_module(modname, revision); if (!immod) { res = ncxmod_load_module(modname, revision, pcb->savedevQ, &immod); } if (!immod) { return SET_ERROR(ERR_INTERNAL_VAL); } /* make sure this NS has not already been added */ found = FALSE; for (test = xml_first_attr(top_attrs); test != NULL && !found; test = xml_next_attr(test)) { if (immod->nsid == test->attr_xmlns_ns) { /* the xmlns_ns field is only set when the attr * is an xmlns declaration */ found = TRUE; } } if (!found) { cstr = xmlns_get_ns_prefix(immod->nsid); res = xml_add_xmlns_attr(top_attrs, immod->nsid, cstr); } return res; } /* add_one_prefix */ /******************************************************************** * FUNCTION add_module_defs * * Add the module definitions for the main module or a submodule * * INPUTS: * mod == module in progress * val == val_value_t to receive child nodes * * OUTPUTS: * val->v.childQ may have entries added * * RETURNS: * status *********************************************************************/ static status_t add_module_defs (ncx_module_t *mod, val_value_t *val) { status_t res; /* convert NCX/YANG types to XSD types */ res = xsd_add_types(mod, val); if (res != NO_ERR) { return res; } /* setup the local typedefs in all groupings * after this fn call, there might be malloced data in the * mod->typnameQ that has to be freed before exiting */ res = xsd_load_typenameQ(mod); if (res != NO_ERR) { return res; } /* convert YANG local types to XSD types */ res = xsd_add_local_types(mod, val); if (res != NO_ERR) { return res; } /* convert YANG groupings to XSD groups */ res = xsd_add_groupings(mod, val); if (res != NO_ERR) { return res; } /* convert YANG Objects to XSD elements */ res = xsd_add_objects(mod, val); if (res != NO_ERR) { return res; } return NO_ERR; } /* add_module_defs */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION xsd_convert_module * * Convert a [sub]module to XSD 1.0 format * module entry point: convert an entire module to a value struct * representing an XML element which represents an XSD * * INPUTS: * mod == module to convert * cp == conversion parms struct to use * retval == address of val_value_t to receive a malloced value struct * representing the XSD. Extra mallocs will be avoided by * referencing the mod_module_t strings. * Do not remove the module from the registry while this * val struct is being used!!! * top_attrs == pointer to receive the top element attributes * HACK: rpc_agt needs the top-level attrs to * be in xml_attr_t format, not val_value_t metaval format * * OUTPUTS: * *retval = malloced return value; MUST FREE THIS LATER * *top_attrs = filled in attrs queue * * RETURNS: * status *********************************************************************/ status_t xsd_convert_module (yang_pcb_t *pcb, ncx_module_t *mod, yangdump_cvtparms_t *cp, val_value_t **retval, xml_attrs_t *top_attrs) { val_value_t *val, *annot; const xmlChar *cstr; xmlChar *str; ncx_import_t *import; yang_node_t *node; yang_import_ptr_t *impptr; status_t res; xmlns_id_t xsd_id, ncx_id, xsi_id, nc_id, ncn_id; boolean needed; xsd_id = xmlns_xs_id(); *retval = NULL; needed = FALSE; /* create a struct named 'schema' to hold the entire result */ val = xml_val_new_struct(XSD_SCHEMA, xsd_id); if (!val) { return ERR_INTERNAL_MEM; } /* set the XSD NS for schema */ cstr = xmlns_get_ns_prefix(xsd_id); res = xml_add_xmlns_attr(top_attrs, xsd_id, cstr); if (res != NO_ERR) { val_free_value(val); return res; } cstr = (mod->ns) ? mod->ns : EMPTY_STRING; /* Set the xmlns directive for the target namespace * !!! This is set as the default namespace, and code in other * !!! modules is hardwired to know that the default namespace * !!! is set to the target namespace */ if (mod->nsid) { res = xml_add_xmlns_attr(top_attrs, mod->nsid, NULL); } else { res = xml_add_xmlns_attr_string(top_attrs, cstr, NULL); } if (res != NO_ERR) { val_free_value(val); return res; } /* set the target namespace */ res = xml_add_attr(top_attrs, 0, XSD_TARG_NS, cstr); if (res != NO_ERR) { val_free_value(val); return res; } if (cp->schemaloc) { /* set the XSI NS (for schemaLocation) */ xsi_id = xmlns_xsi_id(); cstr = xmlns_get_ns_prefix(xsi_id); res = xml_add_xmlns_attr(top_attrs, xsi_id, cstr); if (res != NO_ERR) { val_free_value(val); return res; } /* set the schema location */ str = xsd_make_schema_location(mod, cp->schemaloc, cp->versionnames); if (str) { res = xml_add_attr(top_attrs, xsi_id, XSD_LOC, str); m__free(str); if (res != NO_ERR) { val_free_value(val); return res; } } else { val_free_value(val); return ERR_INTERNAL_MEM; } } /* set the elementFormDefault */ res = xml_add_attr(top_attrs, 0, XSD_EF_DEF, XSD_QUAL); if (res != NO_ERR) { val_free_value(val); return res; } /* set the attributeFormDefault */ res = xml_add_attr(top_attrs, 0, XSD_AF_DEF, XSD_UNQUAL); if (res != NO_ERR) { val_free_value(val); return res; } /* set the language attribute */ res = xml_add_attr(top_attrs, 0, XSD_LANG, XSD_EN); if (res != NO_ERR) { val_free_value(val); return res; } /* set the version attribute */ if (mod->version) { res = xml_add_attr(top_attrs, 0, NCX_EL_VERSION, mod->version); if (res != NO_ERR) { val_free_value(val); return res; } } /* Add the NCX namespace for appinfo stuff; * not sure if it will get used */ ncx_id = xmlns_ncx_id(); if (ncx_id != mod->nsid) { res = xml_add_xmlns_attr(top_attrs, ncx_id, xmlns_get_ns_prefix(ncx_id)); if (res != NO_ERR) { val_free_value(val); return res; } } /* add the NETCONF NS if any RPC or OBJECT definitions */ nc_id = xmlns_nc_id(); if (!dlq_empty(&mod->datadefQ)) { res = xml_add_xmlns_attr(top_attrs, nc_id, xmlns_get_ns_prefix(nc_id)); if (res != NO_ERR) { val_free_value(val); return res; } } /* add the NETCONF Notification NS if any Notification definitions */ if (obj_any_notifs(&mod->datadefQ)) { needed = TRUE; } else if (cp->unified && mod->ismod) { for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (obj_any_notifs(&node->submod->datadefQ)) { needed = TRUE; } } } else { needed = FALSE; } if (needed) { ncn_id = xmlns_ncn_id(); if (mod->nsid != ncn_id) { res = xml_add_xmlns_attr(top_attrs, ncn_id, xmlns_get_ns_prefix(ncn_id)); if (res != NO_ERR) { val_free_value(val); return res; } } } /* add xmlns declarations for all the imported modules */ if (cp->unified && mod->ismod) { for (impptr = (yang_import_ptr_t *)dlq_firstEntry(&mod->saveimpQ); impptr != NULL; impptr = (yang_import_ptr_t *)dlq_nextEntry(impptr)) { res = add_one_prefix(pcb, impptr->modname, impptr->revision, top_attrs); if (res != NO_ERR) { val_free_value(val); return res; } } } else { for (import = (ncx_import_t *)dlq_firstEntry(&mod->importQ); import != NULL; import = (ncx_import_t *)dlq_nextEntry(import)) { res = add_one_prefix(pcb, import->module, import->revision, top_attrs); if (res != NO_ERR) { val_free_value(val); return res; } } } /* add an annotation node if there are any * module meta clauses or appinfo clauses present * There will always be at least one auto-generated * documentation element */ annot = xml_val_new_struct(XSD_ANNOTATION, xsd_id); if (!annot) { val_free_value(val); return ERR_INTERNAL_MEM; } else { val_add_child(annot, val); } /* generate all the module meta-data for the XSD schema element */ res = xsd_add_mod_documentation(mod, annot); if (res != NO_ERR) { val_free_value(val); return res; } /* convert YANG imports to XSD imports */ if (cp->schemaloc) { res = xsd_add_imports(pcb, mod, cp, val); if (res != NO_ERR) { val_free_value(val); return res; } } if (!cp->unified) { /* convert YANG includes to XSD includes */ res = xsd_add_includes(mod, cp, val); if (res != NO_ERR) { val_free_value(val); return res; } } res = add_module_defs(mod, val); if (res != NO_ERR) { val_free_value(val); return res; } if (cp->unified && mod->ismod) { for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { res = add_module_defs(node->submod, val); if (res != NO_ERR) { val_free_value(val); return res; } } } } *retval = val; return NO_ERR; } /* xsd_convert_module */ /******************************************************************** * FUNCTION xsd_load_typenameQ * * Generate unique type names for the entire module * including any submodules that were parsed as well * * INPUTS: * mod == module to process * * OUTPUTS: * *val = malloced return value * *retattrs = malled attrs queue * RETURNS: * status *********************************************************************/ status_t xsd_load_typenameQ (ncx_module_t *mod) { yang_node_t *node; status_t res; res = NO_ERR; /* setup the local typedefs in all groupings * after this fn call, there might be malloced data in the * mod->typnameQ that has to be freed before exiting */ for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL && res==NO_ERR; node = (yang_node_t *)dlq_nextEntry(node)) { res = xsd_do_typedefs_groupingQ(node->submod, &node->submod->groupingQ, &mod->typnameQ); /* setup the local typedefs in all objects */ if (res == NO_ERR) { res = xsd_do_typedefs_datadefQ(node->submod, &node->submod->datadefQ, &mod->typnameQ); } } /* setup the local typedefs in all objects */ if (res == NO_ERR) { res = xsd_do_typedefs_datadefQ(mod, &mod->datadefQ, &mod->typnameQ); } if (res == NO_ERR) { res = xsd_do_typedefs_groupingQ(mod, &mod->groupingQ, &mod->typnameQ); } return res; } /* xsd_load_typenameQ */ /* END file xsd.c */ yuma123_2.14/netconf/src/ydump/yangstats.c0000664000175000017500000005736414770023131020704 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangstats.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30-may-10 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifndef _H_yangdump_util #include "yangdump_util.h" #endif #ifndef _H_yangstats #include "yangstats.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* #define YANGSTATS_DEBUG 1 */ #define YS_WEIGHT_EXTENSION 10 #define YS_WEIGHT_FEATURE 5 #define YS_WEIGHT_GROUPING 3 #define YS_WEIGHT_TYPEDEF 1 #define YS_WEIGHT_DEVIATION 10 #define YS_WEIGHT_IMPORT 5 #define YS_WEIGHT_NOTIFICATION 10 #define YS_WEIGHT_RPC 10 #define YS_WEIGHT_RPCIO 3 #define YS_WEIGHT_ANYXML 30 #define YS_WEIGHT_NP_CONTAINER 10 #define YS_WEIGHT_P_CONTAINER 10 #define YS_WEIGHT_CONFIG 5 #define YS_WEIGHT_STATE 2 #define YS_WEIGHT_AUGMENT 15 #define YS_WEIGHT_USES 10 #define YS_WEIGHT_CHOICE 10 #define YS_WEIGHT_CASE 5 #define YS_WEIGHT_LIST 15 #define YS_WEIGHT_LEAF_LIST 8 #define YS_WEIGHT_LEAF 5 #define YS_WEIGHT_BITS 5 #define YS_WEIGHT_ENUMERATION 3 #define YS_WEIGHT_EMPTY 1 #define YS_WEIGHT_BOOLEAN 1 #define YS_WEIGHT_DECIMAL64 6 #define YS_WEIGHT_BINARY 15 #define YS_WEIGHT_II 10 #define YS_WEIGHT_LEAFREF 12 #define YS_WEIGHT_IDREF 8 #define YS_WEIGHT_UNION 10 #define YS_WEIGHT_NUMBER 2 #define YS_WEIGHT_STRING 4 #define YS_WEIGHT_SUBMODULE 20 /******************************************************************** * FUNCTION output_stat * * Generate a statistics report for 1 module or submodule * * INPUTS: * cp == conversion parameters to use * scb == session control block to use for output * labelstr == stat label string to use * statval == number to output *********************************************************************/ static void output_stat (yangdump_cvtparms_t *cp, ses_cb_t *scb, const char *labelstr, uint32 statval) { ses_putchar(scb, '\n'); ses_putstr(scb, (const xmlChar *)labelstr); ses_putchar(scb, ':'); ses_putchar(scb, ' '); sprintf(cp->buff, "%u", statval); ses_putstr(scb, (const xmlChar *)cp->buff); } /* output_stat */ /******************************************************************** * FUNCTION output_stats * * Generate a statistics report for 1 module or submodule * * INPUTS: * scb == session control block to use for output * stats == stats block to use * cp == conversion parameters to use *********************************************************************/ static void output_stats (ses_cb_t *scb, yangdump_stats_t *stats, yangdump_cvtparms_t *cp) { /* --stats=brief */ output_stat(cp, scb, "Complexity score", stats->complexity_score); output_stat(cp, scb, "Total Nodes", stats->num_config_objs + stats->num_state_objs); if (cp->stats == YANGDUMP_REPORT_BRIEF) { ses_putchar(scb, '\n'); return; } /* --stats=basic */ output_stat(cp, scb, "Extensions", stats->num_extensions); output_stat(cp, scb, "Features", stats->num_features); output_stat(cp, scb, "Groupings", stats->num_groupings); output_stat(cp, scb, "Typedefs", stats->num_typedefs); output_stat(cp, scb, "Deviations", stats->num_deviations); output_stat(cp, scb, "Top Data Nodes", stats->num_top_datanodes); output_stat(cp, scb, "Config nodes", stats->num_config_objs); output_stat(cp, scb, "Non-config nodes", stats->num_state_objs); if (cp->stats == YANGDUMP_REPORT_BASIC) { ses_putchar(scb, '\n'); return; } /* --stats=advanced */ output_stat(cp, scb, "Mandatory nodes", stats->num_mandatory_nodes); output_stat(cp, scb, "Optional nodes", stats->num_optional_nodes); output_stat(cp, scb, "Notifications", stats->num_notifications); output_stat(cp, scb, "Rpcs", stats->num_rpcs); output_stat(cp, scb, "Rpc inputs", stats->num_inputs); output_stat(cp, scb, "Rpc outputs", stats->num_outputs); output_stat(cp, scb, "Augments", stats->num_augments); output_stat(cp, scb, "Uses", stats->num_uses); output_stat(cp, scb, "Choices", stats->num_choices); output_stat(cp, scb, "Cases", stats->num_cases); output_stat(cp, scb, "Anyxmls", stats->num_anyxmls); output_stat(cp, scb, "NP Containers", stats->num_np_containers); output_stat(cp, scb, "P Containers", stats->num_p_containers); output_stat(cp, scb, "Lists", stats->num_lists); output_stat(cp, scb, "Leaf-lists", stats->num_leaf_lists); output_stat(cp, scb, "Key leafs", stats->num_key_leafs); output_stat(cp, scb, "Plain leafs", stats->num_plain_leafs); if (cp->stats == YANGDUMP_REPORT_ADVANCED) { ses_putchar(scb, '\n'); return; } /* --stats=all */ output_stat(cp, scb, "Imports", stats->num_imports); output_stat(cp, scb, "Integral numbers", stats->num_numbers); output_stat(cp, scb, "Decimal64s", stats->num_dec64s); output_stat(cp, scb, "Enumerations", stats->num_enums); output_stat(cp, scb, "Bits", stats->num_bits); output_stat(cp, scb, "Booleans", stats->num_booleans); output_stat(cp, scb, "Emptys", stats->num_emptys); output_stat(cp, scb, "Strings", stats->num_strings); output_stat(cp, scb, "Binarys", stats->num_binarys); output_stat(cp, scb, "Instance Identifiers", stats->num_iis); output_stat(cp, scb, "Leafrefs", stats->num_leafrefs); output_stat(cp, scb, "Identityrefs", stats->num_idrefs); output_stat(cp, scb, "Unions", stats->num_unions); ses_putchar(scb, '\n'); } /* output_stats */ /******************************************************************** * FUNCTION collect_type_stats * * Collect the statistics for 1 YANG leaf or leaf-list data type * * INPUTS: * obj == object template to check * stats == statistics block to use *********************************************************************/ static void collect_type_stats (obj_template_t *obj, yangdump_stats_t *stats) { ncx_btype_t btyp; btyp = obj_get_basetype(obj); switch (btyp) { case NCX_BT_BITS: stats->num_bits++; stats->complexity_score += YS_WEIGHT_BITS; break; case NCX_BT_ENUM: stats->num_enums++; stats->complexity_score += YS_WEIGHT_ENUMERATION; break; case NCX_BT_EMPTY: stats->num_emptys++; stats->complexity_score += YS_WEIGHT_EMPTY; break; case NCX_BT_BOOLEAN: stats->num_booleans++; stats->complexity_score += YS_WEIGHT_BOOLEAN; break; case NCX_BT_DECIMAL64: stats->num_dec64s++; stats->complexity_score += YS_WEIGHT_DECIMAL64; break; case NCX_BT_BINARY: stats->num_binarys++; stats->complexity_score += YS_WEIGHT_BINARY; break; case NCX_BT_INSTANCE_ID: stats->num_iis++; stats->complexity_score += YS_WEIGHT_II; break; case NCX_BT_LEAFREF: stats->num_leafrefs++; stats->complexity_score += YS_WEIGHT_LEAFREF; break; case NCX_BT_IDREF: stats->num_idrefs++; stats->complexity_score += YS_WEIGHT_IDREF; break; case NCX_BT_UNION: stats->num_unions++; stats->complexity_score += YS_WEIGHT_UNION; break; default: if (typ_is_number(btyp)) { stats->num_numbers++; stats->complexity_score += YS_WEIGHT_NUMBER; } else if (typ_is_string(btyp)) { stats->num_strings++; stats->complexity_score += YS_WEIGHT_STRING; } else { SET_ERROR(ERR_INTERNAL_VAL); } } } /* collect_type_stats */ /******************************************************************** * FUNCTION collect_object_stats * * Collect the statistics for 1 YANG object * * INPUTS: * cp == conversion parameters to use * obj == object template to check * stats == statistics block to use *********************************************************************/ static void collect_object_stats (yangdump_cvtparms_t *cp, obj_template_t *obj, yangdump_stats_t *stats) { obj_template_t *chobj; dlq_hdr_t *que; uint32 level; level = obj_get_level(obj); stats->complexity_score += level; if (obj_is_data_db(obj)) { if (level == 1) { stats->num_top_datanodes++; } } if (obj_is_mandatory(obj)) { stats->num_mandatory_nodes++; } else { stats->num_optional_nodes++; } if (obj_get_config_flag(obj)) { stats->num_config_objs++; stats->complexity_score += YS_WEIGHT_CONFIG; } else { stats->num_state_objs++; stats->complexity_score += YS_WEIGHT_STATE; } switch (obj->objtype) { case OBJ_TYP_ANYXML: stats->num_anyxmls++; stats->complexity_score += YS_WEIGHT_ANYXML; return; case OBJ_TYP_CONTAINER: if (obj_get_presence_string(obj) != NULL) { stats->num_p_containers++; stats->complexity_score += YS_WEIGHT_P_CONTAINER; } else { stats->num_np_containers++; stats->complexity_score += YS_WEIGHT_NP_CONTAINER; } break; case OBJ_TYP_LEAF: if (obj_is_key(obj)) { stats->num_key_leafs++; } else { stats->num_plain_leafs++; } stats->complexity_score += YS_WEIGHT_LEAF; collect_type_stats(obj, stats); return; case OBJ_TYP_LEAF_LIST: stats->num_leaf_lists++; stats->complexity_score += YS_WEIGHT_LEAF_LIST; collect_type_stats(obj, stats); return; case OBJ_TYP_LIST: stats->num_lists++; stats->complexity_score += YS_WEIGHT_LIST; break; case OBJ_TYP_CHOICE: stats->num_choices++; stats->complexity_score += YS_WEIGHT_CHOICE; break; case OBJ_TYP_CASE: stats->num_cases++; stats->complexity_score += YS_WEIGHT_CASE; break; case OBJ_TYP_USES: stats->num_uses++; stats->complexity_score += YS_WEIGHT_USES; return; case OBJ_TYP_REFINE: /* should not happen */ SET_ERROR(ERR_INTERNAL_VAL); return; case OBJ_TYP_AUGMENT: stats->num_augments++; stats->complexity_score += YS_WEIGHT_AUGMENT; return; case OBJ_TYP_RPC: stats->num_rpcs++; stats->complexity_score += YS_WEIGHT_RPC; break; case OBJ_TYP_RPCIO: if (!xml_strcmp(obj_get_name(obj), YANG_K_INPUT)) { stats->num_inputs++; } else { stats->num_outputs++; } stats->complexity_score += YS_WEIGHT_RPCIO; break; case OBJ_TYP_NOTIF: stats->num_notifications++; stats->complexity_score += YS_WEIGHT_NOTIFICATION; break; default: SET_ERROR(ERR_INTERNAL_VAL); return; } que = obj_get_datadefQ(obj); if (que != NULL) { for (chobj = (obj_template_t *)dlq_firstEntry(que); chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { collect_object_stats(cp, chobj, stats); } } } /* collect_object_stats */ /******************************************************************** * FUNCTION collect_final_stats * * Add in the module stats to the final stats * * INPUTS: * modstats == module stats struct to use * finstats === final stats struct to use *********************************************************************/ static void collect_final_stats (yangdump_stats_t *modstats, yangdump_stats_t *finstats) { finstats->complexity_score += modstats->complexity_score; finstats->num_extensions += modstats->num_extensions; finstats->num_features += modstats->num_features; finstats->num_groupings += modstats->num_groupings; finstats->num_typedefs += modstats->num_typedefs; finstats->num_deviations += modstats->num_deviations; finstats->num_top_datanodes += modstats->num_top_datanodes; finstats->num_config_objs += modstats->num_config_objs; finstats->num_state_objs += modstats->num_state_objs; finstats->num_mandatory_nodes += modstats->num_mandatory_nodes; finstats->num_optional_nodes += modstats->num_optional_nodes; finstats->num_notifications += modstats->num_notifications; finstats->num_rpcs += modstats->num_rpcs; finstats->num_inputs += modstats->num_inputs; finstats->num_outputs += modstats->num_outputs; finstats->num_augments += modstats->num_augments; finstats->num_uses += modstats->num_uses; finstats->num_choices += modstats->num_choices; finstats->num_cases += modstats->num_cases; finstats->num_anyxmls += modstats->num_anyxmls; finstats->num_np_containers += modstats->num_np_containers; finstats->num_p_containers += modstats->num_p_containers; finstats->num_lists += modstats->num_lists; finstats->num_leaf_lists += modstats->num_leaf_lists; finstats->num_key_leafs += modstats->num_key_leafs; finstats->num_plain_leafs += modstats->num_plain_leafs; finstats->num_imports += modstats->num_imports; finstats->num_numbers += modstats->num_numbers; finstats->num_dec64s += modstats->num_dec64s; finstats->num_enums += modstats->num_enums; finstats->num_bits += modstats->num_bits; finstats->num_booleans += modstats->num_booleans; finstats->num_emptys += modstats->num_emptys; finstats->num_strings += modstats->num_strings; finstats->num_binarys += modstats->num_binarys; finstats->num_iis += modstats->num_iis; finstats->num_leafrefs += modstats->num_leafrefs; finstats->num_idrefs += modstats->num_idrefs; finstats->num_unions += modstats->num_unions; #ifdef YANGSTATS_DEBUG /* check if the totals add up */ { uint32 total = 0; uint32 items = 0; total = modstats->num_config_objs + modstats->num_state_objs; if ((modstats->num_mandatory_nodes + modstats->num_optional_nodes) != total) { SET_ERROR(ERR_INTERNAL_VAL); } items += modstats->num_notifications; items += modstats->num_rpcs; items += modstats->num_inputs; items += modstats->num_outputs; items += modstats->num_augments; items += modstats->num_uses; items += modstats->num_choices; items += modstats->num_cases; items += modstats->num_anyxmls; items += modstats->num_np_containers; items += modstats->num_p_containers; items += modstats->num_lists; items += modstats->num_leaf_lists; items += modstats->num_key_leafs; items += modstats->num_plain_leafs; if (items != total) { SET_ERROR(ERR_INTERNAL_VAL); } } #endif } /* collect_final_stats */ /******************************************************************** * FUNCTION collect_module_stats * * Collect the statistics for 1 module * * INPUTS: * mod == module struct to check * cp == conversion parameters to use *********************************************************************/ static void collect_module_stats (ncx_module_t *mod, yangdump_cvtparms_t *cp) { yangdump_stats_t *stats; obj_template_t *obj; uint32 cval; stats = cp->cur_stats; /* get all the module global counters */ cval = dlq_count(&mod->extensionQ); stats->num_extensions += cval; stats->complexity_score += (cval * YS_WEIGHT_EXTENSION); cval = dlq_count(&mod->featureQ); stats->num_features += cval; stats->complexity_score += (cval * YS_WEIGHT_FEATURE); cval = dlq_count(&mod->groupingQ); stats->num_groupings += cval; stats->complexity_score += (cval * YS_WEIGHT_GROUPING); cval = dlq_count(&mod->typeQ); stats->num_typedefs += cval; stats->complexity_score += (cval * YS_WEIGHT_TYPEDEF); cval = dlq_count(&mod->deviationQ); stats->num_deviations += cval; stats->complexity_score += (cval * YS_WEIGHT_DEVIATION); cval = dlq_count(&mod->importQ); stats->num_imports += cval; stats->complexity_score += (cval * YS_WEIGHT_IMPORT); /* get the requested object statistics */ for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { collect_object_stats(cp, obj, stats); } /* update the running total if needed */ if (cp->final_stats != NULL) { collect_final_stats(stats, cp->final_stats); } } /* collect_module_stats */ /************* E X T E R N A L F U N C T I O N S ************/ /******************************************************************** * FUNCTION yangstats_collect_module_stats * * Generate a statistics report for a module * * INPUTS: * pcb == parser control block with module to use * cp == conversion parameters to use *********************************************************************/ void yangstats_collect_module_stats (yang_pcb_t *pcb, yangdump_cvtparms_t *cp) { ncx_module_t *mod; yang_node_t *node; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { SET_ERROR(ERR_NCX_MOD_NOT_FOUND); return; } collect_module_stats(mod, cp); if (cp->unified && mod->ismod) { for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { cp->cur_stats->complexity_score += YS_WEIGHT_SUBMODULE; collect_module_stats(node->submod, cp); } } } } /* yangstats_collect_module_stats */ /******************************************************************** * FUNCTION yangstats_output_module_stats * * Generate a statistics report for a module * * INPUTS: * pcb == parser control block with module to use * cp == conversion parameters to use * scb == session control block to use for output *********************************************************************/ void yangstats_output_module_stats (yang_pcb_t *pcb, yangdump_cvtparms_t *cp, ses_cb_t *scb) { ncx_module_t *mod; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { SET_ERROR(ERR_NCX_MOD_NOT_FOUND); return; } ses_putstr(scb, (const xmlChar *)"\nStatistics:"); /* check subtree mode */ if (cp->subtree) { print_subtree_banner(cp, mod, scb); } output_stats(scb, cp->cur_stats, cp); memset(cp->cur_stats, 0x0, sizeof(yangdump_stats_t)); cp->stat_reports++; } /* yangstats_output_module_stats */ /******************************************************************** * FUNCTION yangstats_output_final_stats * * Generate the final stats report as requested * by the --stats and --stat-totals CLI parameters * * INPUTS: * cp == conversion parameters to use * scb == session control block to use for output *********************************************************************/ void yangstats_output_final_stats (yangdump_cvtparms_t *cp, ses_cb_t *scb) { switch (cp->stat_totals) { case YANGDUMP_TOTALS_NONE: /* skip this step */ break; case YANGDUMP_TOTALS_SUMMARY: /* do a summary only if more than 1 module reported */ if (cp->stat_reports < 2) { break; } /* else fall through */ case YANGDUMP_TOTALS_SUMMARY_ONLY: if (cp->final_stats != NULL) { ses_putstr(scb, (const xmlChar *)"\n\nStatistics totals:"); output_stats(scb, cp->final_stats, cp); } else { SET_ERROR(ERR_INTERNAL_VAL); } break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* yangstats_output_final_stats */ /* END yangstats.c */ yuma123_2.14/netconf/src/ydump/h.h0000664000175000017500000000471714770023131017115 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_h #define _H_h /* FILE: h.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert YANG module to H file format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 28-mar-09 abb Begun */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION h_convert_module * * Generate the SIL H file code for the specified module(s) * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * * RETURNS: * status *********************************************************************/ extern status_t h_convert_module (yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_h */ yuma123_2.14/netconf/src/ydump/sql.c0000664000175000017500000007775514770023131017474 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: sql.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 19feb08 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_sql #include "sql.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tstamp #include "tstamp.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xsd_util #include "xsd_util.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define SQL_BUFFSIZE 32768 #define EMPTY_COLUMN (const xmlChar *)"'', " #define EMPTY_COLUMN_LAST (const xmlChar *)"''" #define SEP_BAR (const xmlChar *)\ "\n#################################################" #define MAX_ABSTRACT_LEN 128 /******************************************************************** * FUNCTION write_empty_col * * Write an empty column entry * * INPUTS: * scb == session control block to use *********************************************************************/ static void write_empty_col (ses_cb_t *scb) { ses_putstr(scb, (const xmlChar *)"\n '',"); } /* write_empty_col */ /******************************************************************** * FUNCTION write_cstring * * Write a user content string which might have a single quote * Need to escape any single quote found * * INPUTS: * scb == session control block to use for writing * cstr == content string to write *********************************************************************/ static void write_cstring (ses_cb_t *scb, const xmlChar *cstr) { while (*cstr) { if (*cstr == '\'') { ses_putchar(scb, '\\'); } ses_putchar(scb, *cstr); cstr++; } } /* write_cstring */ /******************************************************************** * FUNCTION write_cstring_n * * Write a user content string which might have a single quote * Need to escape any single quote found * * INPUTS: * scb == session control block to use for writing * cstr == content string to write * count == max number of characters to write *********************************************************************/ static void write_cstring_n (ses_cb_t *scb, const xmlChar *cstr, uint32 count) { while (*cstr && count) { if (*cstr == '\'') { ses_putchar(scb, '\\'); } ses_putchar(scb, *cstr); cstr++; count--; } } /* write_cstring_n */ /******************************************************************** * FUNCTION write_end_tstamps * * Write the created_on and updated_on tstamp values * * INPUTS: * scb == session control block to use for writing * buff == malloced buff to use *********************************************************************/ static void write_end_tstamps (ses_cb_t *scb, char *buff) { /* column: created_on */ ses_putstr(scb, (const xmlChar *)"\n '"); tstamp_datetime_sql((xmlChar *)buff); ses_putstr(scb, (const xmlChar *)buff); ses_putstr(scb, (const xmlChar *)"', "); /* column: updated_on */ ses_putstr(scb, (const xmlChar *)"\n '"); ses_putstr(scb, (const xmlChar *)buff); ses_putchar(scb, '\''); } /* write_end_tstamps */ /******************************************************************** * FUNCTION write_docurl * * Write the docurl URL for some type of entry * * INPUTS: * scb == session control block to use for writing * mod == module that will be used for the URL * cp == conversion parameters to use * fragname == name of fragment to use (NULL == none) * linenum == line number to add to the fragment because * many symbol types can overlap maning scopes, * and the [name, linenum] tuple is needed * to make the URLs unique (instead of broken) *********************************************************************/ static void write_docurl (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const xmlChar *fragname, uint32 linenum) { char numbuff[NCX_MAX_NUMLEN]; /* column: docurl */ if (cp->urlstart && *cp->urlstart) { ses_putstr(scb, (const xmlChar *)"\n '/modules/"); } else { ses_putstr(scb, (const xmlChar *)"\n "); } if (cp->unified && !mod->ismod) { ses_putstr(scb, mod->belongs); } else { ses_putstr(scb, mod->name); } if (cp->versionnames && mod->version) { if (cp->simurls) { ses_putchar(scb, NCXMOD_PSCHAR); } else { ses_putchar(scb, YANG_FILE_SEPCHAR); } ses_putstr(scb, mod->version); } if (!cp->simurls) { ses_putstr(scb, (const xmlChar *)".html"); } if (fragname) { ses_putchar(scb, '#'); if (cp->unified && !mod->ismod) { /* add the submodule to the fragment ID since * the line numbers may collide with another definition * on the same line in 2 different submodules */ ses_putstr(scb, mod->name); ses_putchar(scb, '.'); } ses_putstr(scb, fragname); if (linenum) { sprintf(numbuff, ".%u", linenum); ses_putstr(scb, (const xmlChar *)numbuff); } } ses_putstr(scb, (const xmlChar *)"', "); } /* write_docurl */ /******************************************************************** * FUNCTION write_first_tuple * * Write the columns for the * [modname, submodname, version, name, linenum] tuple * * INPUTS: * scb == session control block to use for writing * mod == module that will be used for the URL * name == myname field value to write * linenum == line number for 'name' * buff == scratch buff to use *********************************************************************/ static void write_first_tuple (ses_cb_t *scb, const ncx_module_t *mod, const xmlChar *name, uint32 linenum, char *buff) { /* columns: ID, modname, submodname, version, name, linenum */ sprintf(buff, "\n '', '%s', '%s', '%s', '%s', '%u',", (mod->ismod) ? mod->name : mod->belongs, mod->name, (mod->version) ? mod->version : NCX_EL_NONE, name, linenum); ses_putstr(scb, (const xmlChar *)buff); } /* write_first_tuple */ /******************************************************************** * FUNCTION write_descr_ref * * Write the description, reference columns * * INPUTS: * scb == session control block to use for writing * descr == description string to use (may be NULL) * ref == reference string to use (may be NULL) *********************************************************************/ static void write_descr_ref (ses_cb_t *scb, const xmlChar *descr, const xmlChar *ref) { /* column: description */ ses_putstr(scb, (const xmlChar *)"\n '"); write_cstring(scb, (descr) ? descr : EMPTY_STRING); ses_putstr(scb, (const xmlChar *)"',"); /* column: reference */ ses_putstr(scb, (const xmlChar *)"\n '"); write_cstring(scb, (ref) ? ref : EMPTY_STRING); ses_putstr(scb, (const xmlChar *)"',"); } /* write_descr_ref */ /******************************************************************** * FUNCTION write_object_list * * Write an object list column * 'objname objname ...' * * INPUTS: * scb == session control block to use for writing * datadefQ == Q of obj_template_t to use * *********************************************************************/ static void write_object_list (ses_cb_t *scb, const dlq_hdr_t *datadefQ) { obj_template_t *obj, *nextobj; /* column: object list */ ses_putstr(scb, (const xmlChar *)"\n '"); for (obj = (obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = nextobj) { nextobj = (obj_template_t *)dlq_nextEntry(obj); if (obj_has_name(obj)) { ses_putstr(scb, obj_get_name(obj)); if (nextobj && obj_has_name(nextobj)) { ses_putchar(scb, ' '); } } } ses_putstr(scb, (const xmlChar *)"',"); } /* write_object_list */ /******************************************************************** * FUNCTION write_object_id * * Write an object ID (Xpath absolute expression * '/obj1/chobj/chobj ...' * * INPUTS: * scb == session control block to use for writing * obj == obj_template_t to write * buff == scratch buff to use * *********************************************************************/ static void write_object_id (ses_cb_t *scb, const obj_template_t *obj, char *buff) { status_t res; uint32 retlen; res = obj_copy_object_id(obj, (xmlChar *)buff, SQL_BUFFSIZE, &retlen); if (res != NO_ERR) { /* may ruin the SQL output to SSTDOUT */ log_error("\nError: Write SQL Object ID %s failed", obj_get_name(obj)); return; } if (retlen > MAX_OBJECTID_LEN) { log_error("\nError: Write SQL Object ID %s too big (%u)", obj_get_name(obj), retlen); return; } ses_putstr(scb, (const xmlChar *)"\n '"); ses_putstr(scb, (const xmlChar *)buff); ses_putstr(scb, (const xmlChar *)"',"); } /* write_object_id */ /******************************************************************** * FUNCTION write_banner * * Generate the SQL comments to start the file * * INPUTS: * mod == module in progress * scb == session control block to use for writing * *********************************************************************/ static void write_banner (const ncx_module_t *mod, ses_cb_t *scb) { status_t res; xmlChar buffer[NCX_VERSION_BUFFSIZE]; ses_putstr(scb, (const xmlChar *)"\n#"); ses_putstr(scb, (const xmlChar *)"\n# Generated by yangdump version "); res = ncx_get_version(buffer, NCX_VERSION_BUFFSIZE); if (res == NO_ERR) { ses_putstr(scb, buffer); } else { SET_ERROR(res); } ses_putstr(scb, (const xmlChar *)"\n#"); ses_putstr(scb, (const xmlChar *)"\n# mySQL database: netconfcentral"); ses_putstr(scb, (const xmlChar *)"\n# source: "); ses_putstr(scb, mod->source); if (mod->ismod) { ses_putstr(scb, (const xmlChar *)"\n# module: "); } else { ses_putstr(scb, (const xmlChar *)"\n# submodule: "); } ses_putstr(scb, mod->name); if (!mod->ismod) { ses_putstr(scb, (const xmlChar *)"\n# belongs-to: "); ses_putstr(scb, mod->belongs); } ses_putstr(scb, (const xmlChar *)"\n# version: "); if (mod->version) { write_cstring(scb, mod->version); } else { write_cstring(scb, NCX_EL_NONE); } ses_putstr(scb, (const xmlChar *)"\n#"); ses_putstr(scb, SEP_BAR); } /* write_banner */ /******************************************************************** * FUNCTION write_quick_module_entry * * Generate the SQL code to add the module to * the 'ncquickmod' table * * INPUTS: * mod == module in progress * scb == session control block to use for writing * buff == scratch buff to use *********************************************************************/ static void write_quick_module_entry (const ncx_module_t *mod, ses_cb_t *scb, char *buff) { if (!mod->ismod || !mod->defaultrev) { return; } ses_putstr(scb, (const xmlChar *)"\n\nINSERT INTO ncquickmod VALUES ("); /* columns: ID, modname, submodname */ sprintf(buff, "\n '', '%s');", mod->name); ses_putstr(scb, (const xmlChar *)buff); } /* write_quick_module_entry */ /******************************************************************** * FUNCTION write_module_entry * * Generate the SQL code to add the module to * the 'ncmodule' table * * INPUTS: * mod == module in progress * cp == conversion parameters to use * scb == session control block to use for writing * buff == scratch buff to use * *********************************************************************/ static void write_module_entry (const ncx_module_t *mod, const yangdump_cvtparms_t *cp, ses_cb_t *scb, char *buff) { ncx_revhist_t *revhist; ses_putstr(scb, (const xmlChar *)"\n\nINSERT INTO ncmodule VALUES ("); /* columns: ID, modname, submodname */ sprintf(buff, "\n '', '%s', '%s', ", (mod->ismod) ? mod->name : mod->belongs, mod->name); ses_putstr(scb, (const xmlChar *)buff); /* columns: version, modprefix */ ses_putchar(scb, '\''); if (mod->version) { write_cstring(scb, mod->version); } else { write_cstring(scb, NCX_EL_NONE); } ses_putstr(scb, (const xmlChar *)"', "); sprintf(buff, "'%s',", (mod->prefix) ? mod->prefix : mod->name); ses_putstr(scb, (const xmlChar *)buff); /* column: namespace */ ses_putstr(scb, (const xmlChar *)"\n '"); if (mod->nsid) { write_cstring(scb, xmlns_get_ns_name(mod->nsid)); } else { write_cstring(scb, EMPTY_STRING); } ses_putstr(scb, (const xmlChar *)"',"); /* column: organization */ ses_putstr(scb, (const xmlChar *)"\n '"); write_cstring(scb, (mod->organization) ? mod->organization : EMPTY_STRING); ses_putstr(scb, (const xmlChar *)"',"); /* column: abstract -- need to use different abstract!!! */ ses_putstr(scb, (const xmlChar *)"\n '"); write_cstring_n(scb, (mod->descr) ? mod->descr : EMPTY_STRING, MAX_ABSTRACT_LEN); if (mod->descr && (xml_strlen(mod->descr) > MAX_ABSTRACT_LEN)) { ses_putstr(scb, (const xmlChar *)"..."); } ses_putstr(scb, (const xmlChar *)"',"); /* columns: description, reference */ write_descr_ref(scb, mod->descr, mod->ref); /* column: contact */ ses_putstr(scb, (const xmlChar *)"\n '"); write_cstring(scb, (mod->contact_info) ? mod->contact_info : EMPTY_STRING); ses_putstr(scb, (const xmlChar *)"',"); /* column: revcomment */ revhist = NULL; if (mod->version) { revhist = ncx_find_revhist(mod, mod->version); } if (revhist && revhist->descr) { ses_putstr(scb, (const xmlChar *)"\n '"); write_cstring(scb, revhist->descr); ses_putstr(scb, (const xmlChar *)"',"); } else { write_empty_col(scb); } /* column: xsdurl * HARD-WIRED TO * /xsd/modname.xsd OR /xsd/modname@modversion.xsd */ ses_putstr(scb, (const xmlChar *)"\n '/xsd/"); if (cp->unified && !mod->ismod) { ses_putstr(scb, mod->belongs); } else { ses_putstr(scb, mod->name); } if (cp->versionnames && mod->version) { ses_putchar(scb, YANG_FILE_SEPCHAR); ses_putstr(scb, mod->version); } ses_putstr(scb, (const xmlChar *)".xsd',"); /* column: docurl -- not hard-wired, based on cp */ write_docurl(scb, mod, cp, NULL, 0); /* column: srcurl * HARD-WIRED TO * /src/modname.yang * OR * /src/modname@modversion.yang */ ses_putstr(scb, (const xmlChar *)"\n '/src/"); if (cp->unified && !mod->ismod) { ses_putstr(scb, mod->belongs); } else { ses_putstr(scb, mod->name); } if (cp->versionnames && mod->version) { ses_putchar(scb, YANG_FILE_SEPCHAR); ses_putstr(scb, mod->version); } ses_putstr(scb, (const xmlChar *)".yang',"); /* column: sourcespec */ sprintf(buff, "\n '%s',", mod->source); ses_putstr(scb, (const xmlChar *)buff); /* column: sourcename */ sprintf(buff, "\n '%s',", mod->sourcefn); ses_putstr(scb, (const xmlChar *)buff); /* columns: iscurrent, islatest **** !!!! ****/ ses_putstr(scb, (const xmlChar *)" '1', '1',"); /* columns: ismod, isyang */ sprintf(buff, " '%u', '1',", (mod->ismod) ? 1 : 0); ses_putstr(scb, (const xmlChar *)buff); /* columns: created_on, updated_on */ write_end_tstamps(scb, buff); /* end the VALUES clause */ ses_putstr(scb, (const xmlChar *)");"); } /* write_module_entry */ /******************************************************************** * FUNCTION write_typedef_entry * * Generate the SQL code to add the typ_template_t to * the 'typedef' table * * INPUTS: * mod == module in progress * typ == type template to process * cp == conversion parameters to use * scb == session control block to use for writing * buff == scratch buff to use * *********************************************************************/ static void write_typedef_entry (const ncx_module_t *mod, const typ_template_t *typ, const yangdump_cvtparms_t *cp, ses_cb_t *scb, char *buff) { const typ_template_t *parenttyp; ses_putstr(scb, (const xmlChar *)"\n\nINSERT INTO nctypedef VALUES ("); /* columns: ID, modname, submodname, version, name, linenum */ write_first_tuple(scb, mod, typ->name, typ->tkerr.linenum, buff); /* columns: description, reference */ write_descr_ref(scb, typ->descr, typ->ref); /* column: docurl */ write_docurl(scb, mod, cp, typ->name, typ->tkerr.linenum); /* column: basetypename */ sprintf(buff, "'%s', ", (const char *)typ_get_basetype_name(typ)); ses_putstr(scb, (const xmlChar *)buff); /* column: parenttypename */ sprintf(buff, "'%s', ", (const char *)typ_get_parenttype_name(typ)); ses_putstr(scb, (const xmlChar *)buff); /* columns: parentmodname, parentlinenum */ parenttyp = typ_get_parent_type(typ); if (parenttyp) { sprintf(buff, "\n '%s', '%u', ", parenttyp->tkerr.mod->name, parenttyp->tkerr.linenum); ses_putstr(scb, (const xmlChar *)buff); } else { write_empty_col(scb); write_empty_col(scb); } /* columns: iscurrent, islatest **** !!!! ****/ ses_putstr(scb, (const xmlChar *)"\n '1', '1',"); /* columns: created_on, updated_on */ write_end_tstamps(scb, buff); /* end the VALUES clause */ ses_putstr(scb, (const xmlChar *)");"); } /* write_typedef_entry */ /******************************************************************** * FUNCTION write_grouping_entry * * Generate the SQL code to add the grp_template_t to * the 'ncgrouping' table * * INPUTS: * mod == module in progress * grp == grouping template to process * cp == conversion parameters to use * scb == session control block to use for writing * buff == scratch buff to use * *********************************************************************/ static void write_grouping_entry (const ncx_module_t *mod, const grp_template_t *grp, const yangdump_cvtparms_t *cp, ses_cb_t *scb, char *buff) { ses_putstr(scb, (const xmlChar *)"\n\nINSERT INTO ncgrouping VALUES ("); /* columns: ID, modname, submodname, version, name, linenum */ write_first_tuple(scb, mod, grp->name, grp->tkerr.linenum, buff); /* columns: description, reference */ write_descr_ref(scb, grp->descr, grp->ref); /* column: docurl */ write_docurl(scb, mod, cp, grp->name, grp->tkerr.linenum); /* column: object list */ write_object_list(scb, &grp->datadefQ); /* columns: iscurrent, islatest **** !!!! ****/ ses_putstr(scb, (const xmlChar *)"\n '1', '1',"); /* columns: created_on, updated_on */ write_end_tstamps(scb, buff); /* end the VALUES clause */ ses_putstr(scb, (const xmlChar *)");"); } /* write_grouping_entry */ /******************************************************************** * FUNCTION write_object_entry * * Generate the SQL code to add the obj_template_t to * the 'ncobject' table * * INPUTS: * mod == module in progress * obj == object template to process * cp == conversion parameters to use * scb == session control block to use for writing * buff == scratch buff to use * *********************************************************************/ static void write_object_entry (const ncx_module_t *mod, obj_template_t *obj, const yangdump_cvtparms_t *cp, ses_cb_t *scb, char *buff) { dlq_hdr_t *datadefQ; obj_template_t *chobj; const xmlChar *name, *defval; if (!obj_has_name(obj)) { return; } name = obj_get_name(obj); datadefQ = obj_get_datadefQ(obj); ses_putstr(scb, (const xmlChar *)"\n\nINSERT INTO ncobject VALUES ("); /* columns: ID, modname, submodname, version, name, linenum */ write_first_tuple(scb, mod, name, obj->tkerr.linenum, buff); /* column: objectid */ write_object_id(scb, obj, buff); /* columns: description, reference */ write_descr_ref(scb, obj_get_description(obj), obj_get_reference(obj)); /* column: docurl */ write_docurl(scb, mod, cp, name, obj->tkerr.linenum); /* column: objtyp */ sprintf(buff, "\n '%s', ", (const char *)obj_get_typestr(obj)); ses_putstr(scb, (const xmlChar *)buff); /* column: parentid */ if (obj->parent && !obj_is_root(obj->parent)) { write_object_id(scb, obj->parent, buff); } else { write_empty_col(scb); } /* column: istop */ sprintf(buff, "\n '%u',", (obj->parent && !obj_is_root(obj->parent)) ? 0 : 1); ses_putstr(scb, (const xmlChar *)buff); /* column: isdata -- config DB data only */ sprintf(buff, "\n '%u',", obj_is_data_db(obj) ? 1 : 0); ses_putstr(scb, (const xmlChar *)buff); /* column: typename, or groupname if uses */ switch (obj->objtype) { case OBJ_TYP_LEAF: sprintf(buff, "\n '%s',", typ_get_name(obj->def.leaf->typdef)); break; case OBJ_TYP_LEAF_LIST: sprintf(buff, "\n '%s',", typ_get_name(obj->def.leaflist->typdef)); break; case OBJ_TYP_USES: sprintf(buff, "\n '%s',", obj->def.uses->grp->name); break; default: sprintf(buff, "\n '',"); } ses_putstr(scb, (const xmlChar *)buff); /* column: augwhen */ if (obj->augobj && obj->augobj->when && obj->augobj->when->exprstr) { ses_putstr(scb, (const xmlChar *)"\n '"); write_cstring(scb, obj->augobj->when->exprstr); ses_putstr(scb, (const xmlChar *)"',"); } else { write_empty_col(scb); } /* column: childlist */ if (datadefQ) { write_object_list(scb, datadefQ); } else { write_empty_col(scb); } /* column: defval */ defval = obj_get_default(obj); if (defval) { sprintf(buff, "\n '%s',", defval); ses_putstr(scb, (const xmlChar *)buff); } else { write_empty_col(scb); } /* column: listkey */ if (obj_get_keystr(obj) != NULL) { sprintf(buff, "\n '%s',", obj_get_keystr(obj)); ses_putstr(scb, (const xmlChar *)buff); } else { write_empty_col(scb); } /* columns: config, mandatory, level */ sprintf(buff, "\n '%u', '%u', '%u',", obj_get_config_flag(obj) ? 1 : 0, obj_is_mandatory(obj) ? 1 : 0, obj_get_level(obj)); ses_putstr(scb, (const xmlChar *)buff); /* columns: minelements, maxelements */ if (obj->objtype == OBJ_TYP_LEAF_LIST) { sprintf(buff, "\n '%u', '%u',", obj->def.leaflist->minelems, obj->def.leaflist->maxelems); ses_putstr(scb, (const xmlChar *)buff); } else if (obj->objtype == OBJ_TYP_LIST) { sprintf(buff, "\n '%u', '%u',", obj->def.list->minelems, obj->def.list->maxelems); ses_putstr(scb, (const xmlChar *)buff); } else { ses_putstr(scb, (const xmlChar *)"\n '0', '0',"); } /* columns: iscurrent, islatest **** !!!! ****/ ses_putstr(scb, (const xmlChar *)"\n '1', '1',"); /* columns: created_on, updated_on */ write_end_tstamps(scb, buff); /* end the VALUES clause */ ses_putstr(scb, (const xmlChar *)");"); /* write an entry for each child node */ if (datadefQ) { for (chobj = (obj_template_t *)dlq_firstEntry(datadefQ); chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { write_object_entry(mod, chobj, cp, scb, buff); } } } /* write_object_entry */ /******************************************************************** * FUNCTION write_extension_entry * * Generate the SQL code to add the ext_template_t to * the 'ncextension' table * * INPUTS: * mod == module in progress * ext == extension template to process * cp == conversion parameters to use * scb == session control block to use for writing * buff == scratch buff to use * *********************************************************************/ static void write_extension_entry (const ncx_module_t *mod, const ext_template_t *ext, const yangdump_cvtparms_t *cp, ses_cb_t *scb, char *buff) { ses_putstr(scb, (const xmlChar *)"\n\nINSERT INTO ncextension VALUES ("); /* columns: ID, modname, submodname, version, myid, myname, linenum */ write_first_tuple(scb, mod, ext->name, ext->tkerr.linenum, buff); /* columns: description, reference */ write_descr_ref(scb, ext->descr, ext->ref); /* column: docurl */ write_docurl(scb, mod, cp, ext->name, ext->tkerr.linenum); /* columns: argument, yinelement */ if (ext->arg) { sprintf(buff, "\n '%s', '%u',", (const char *)ext->arg, (ext->argel) ? 1 : 0); ses_putstr(scb, (const xmlChar *)buff); } else { ses_putstr(scb, (const xmlChar *)"\n '', '0',"); } /* columns: iscurrent, islatest **** !!!! ****/ ses_putstr(scb, (const xmlChar *)"\n '1', '1',"); /* columns: created_on, updated_on */ write_end_tstamps(scb, buff); /* end the VALUES clause */ ses_putstr(scb, (const xmlChar *)");"); } /* write_extension_entry */ /******************************************************************** * FUNCTION convert_one_module * * Convert the YANG or NCX module to SQL for input to * the netconfcentral object dictionary database * * INPUTS: * mod == module to convert * cp == convert parms struct to use * scb == session to use for output * *********************************************************************/ static void convert_one_module (const ncx_module_t *mod, const yangdump_cvtparms_t *cp, ses_cb_t *scb) { const typ_template_t *typ; const grp_template_t *grp; obj_template_t *obj; const ext_template_t *ext; #ifdef DEBUG /* copy namespace ID if this is a submodule */ if (!mod->ismod && !mod->nsid) { SET_ERROR(ERR_INTERNAL_VAL); } #endif /* write the application banner */ write_banner(mod, scb); /* write the ncquickmod table entry */ write_quick_module_entry(mod, scb, cp->buff); /* write the ncmodule table entry */ write_module_entry(mod, cp, scb, cp->buff); /* write the typedef table entries */ for (typ = (const typ_template_t *)dlq_firstEntry(&mod->typeQ); typ != NULL; typ = (const typ_template_t *)dlq_nextEntry(typ)) { write_typedef_entry(mod, typ, cp, scb, cp->buff); } /* write the ncgrouping table entries */ for (grp = (const grp_template_t *)dlq_firstEntry(&mod->groupingQ); grp != NULL; grp = (const grp_template_t *)dlq_nextEntry(grp)) { write_grouping_entry(mod, grp, cp, scb, cp->buff); } /* write the ncobject table entries */ for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (obj_is_hidden(obj)) { continue; } write_object_entry(mod, obj, cp, scb, cp->buff); } /* write the ncextension table entries */ for (ext = (const ext_template_t *)dlq_firstEntry(&mod->extensionQ); ext != NULL; ext = (const ext_template_t *)dlq_nextEntry(ext)) { write_extension_entry(mod, ext, cp, scb, cp->buff); } ses_putchar(scb, '\n'); } /* convert_one_module */ /********* E X P O R T E D F U N C T I O N S **************/ /******************************************************************** * FUNCTION sql_convert_module * * Convert the YANG module to SQL for input to * the netconfcentral object dictionary database * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == convert parms struct to use * scb == session to use for output * * RETURNS: * status *********************************************************************/ status_t sql_convert_module (const yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb) { const ncx_module_t *mod; const yang_node_t *node; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { return SET_ERROR(ERR_NCX_MOD_NOT_FOUND); } convert_one_module(mod, cp, scb); if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { convert_one_module(node->submod, cp, scb); } } } return NO_ERR; } /* sql_convert_module */ /* END file sql.c */ yuma123_2.14/netconf/src/ydump/ydump.c0000664000175000017500000020437414770023131020020 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2012, YumaWorks, Inc., All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: ydump.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 01-nov-06 abb begun 26-mar-10 abb split out into library ydump ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include "procdefs.h" #include "c.h" #include "cli.h" #include "conf.h" #include "cyang.h" #include "h.h" #include "help.h" #include "html.h" #include "log.h" #include "ncx.h" #include "ncx_feature.h" #include "ncxconst.h" #include "ncxmod.h" #include "sql.h" #include "status.h" #include "tg2.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xml_wr.h" #include "xsd.h" #include "xsd_util.h" #include "yang.h" #include "yangconst.h" #include "yangdump.h" #include "yangdump_util.h" #include "yangstats.h" #include "yangyin.h" #include "yangtree.h" #include "ydump.h" /******************************************************************** * FUNCTION init_cvtparms * * Initialize a preallocated yangdump_cvtparms_t struct * * INPUTS: * cp == conversion parms struct to init * allowcode == TRUE to allow code generation * * RETURNS: * status *********************************************************************/ static status_t init_cvtparms (yangdump_cvtparms_t *cp, boolean allowcode) { /* memset(cp, 0x0, sizeof(yangdump_cvtparms_t)); */ cp->allowcode = allowcode; dlq_createSQue(&cp->savedevQ); cp->buff = m__getMem(YANGDUMP_BUFFSIZE); if (!cp->buff) { return ERR_INTERNAL_MEM; } cp->bufflen = YANGDUMP_BUFFSIZE; return NO_ERR; } /* init_cvtparms */ /******************************************************************** * FUNCTION init_cvtparms_stats * * Initialize a preallocated yangdump_cvtparms_t struct * for statistics collection * * INPUTS: * cp == conversion parms struct to init * * RETURNS: * status *********************************************************************/ static status_t init_cvtparms_stats (yangdump_cvtparms_t *cp) { cp->cur_stats = m__getObj(yangdump_stats_t); if (cp->cur_stats == NULL) { return ERR_INTERNAL_MEM; } memset(cp->cur_stats, 0x0, sizeof(yangdump_stats_t)); if (cp->stat_totals != YANGDUMP_TOTALS_NONE) { cp->final_stats = m__getObj(yangdump_stats_t); if (cp->final_stats == NULL) { return ERR_INTERNAL_MEM; } memset(cp->final_stats, 0x0, sizeof(yangdump_stats_t)); } return NO_ERR; } /* init_cvtparms_stats */ /******************************************************************** * FUNCTION clean_cvtparms * * Clean a preallocated yangdump_cvtparms_t struct * but do not free it * * INPUTS: * cp == conversion parms struct to clean * *********************************************************************/ static void clean_cvtparms (yangdump_cvtparms_t *cp) { if (cp->cli_val) { val_free_value(cp->cli_val); } if (cp->mod) { ncx_free_module(cp->mod); } if (cp->srcfile) { m__free(cp->srcfile); } if (cp->module) { m__free(cp->module); } if (cp->full_output) { m__free(cp->full_output); } if (cp->buff) { m__free(cp->buff); } if (cp->cur_stats) { m__free(cp->cur_stats); } if (cp->final_stats) { m__free(cp->final_stats); } ncx_clean_save_deviationsQ(&cp->savedevQ); memset(cp, 0x0, sizeof(yangdump_cvtparms_t)); } /* clean_cvtparms */ /******************************************************************** * FUNCTION pr_err * * Print an error message * * INPUTS: * res == error result * *********************************************************************/ static void pr_err (status_t res) { const char *msg; msg = get_error_string(res); log_error("\n%s: Error exit (%s)\n", PROGNAME, msg); } /* pr_err */ /******************************************************************** * FUNCTION pr_usage * * Print a usage message * *********************************************************************/ static void pr_usage (void) { log_error("\nError: No parameters entered." "\nTry '%s --help' for usage details\n", PROGNAME); } /* pr_usage */ /******************************************************************** * FUNCTION process_cli_input * * Process the param line parameters against the hardwired * parmset for the yangdump program * * get all the parms and store them in the cvtparms struct * * INPUTS: * argc == argument count * argv == array of command line argument strings * cp == address of returned values * * OUTPUTS: * parmset values will be stored in *cvtparms if NO_ERR * errors will be written to STDOUT * * RETURNS: * NO_ERR if all goes well *********************************************************************/ static status_t process_cli_input (int argc, char *argv[], yangdump_cvtparms_t *cp) { obj_template_t *obj; ncx_module_t *mod; val_value_t *valset, *val; status_t res; boolean defs_done; res = NO_ERR; valset = NULL; obj = NULL; defs_done = FALSE; /* find the CLI container definition */ mod = ncx_find_module(YANGDUMP_MOD, NULL); if (mod != NULL) { obj = ncx_find_object(mod, YANGDUMP_CONTAINER); } if (obj == NULL) { log_error("\nError: yangdump module with CLI definitions not loaded"); res = ERR_NCX_NOT_FOUND; } /* parse the command line against the object template */ if (res == NO_ERR) { if (argv != NULL) { valset = cli_parse(NULL, argc, argv, obj, FULLTEST, PLAINMODE, TRUE, CLI_MODE_PROGRAM, &res); defs_done = TRUE; } else { valset = val_new_value(); if (valset == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(valset, obj); } } if (res != NO_ERR) { if (valset != NULL) { val_free_value(valset); } return res; } else if (valset == NULL) { pr_usage(); return ERR_NCX_SKIPPED; } else { cp->cli_val = valset; } /* next get any params from the conf file */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_CONFIG); if (val) { if (val->res == NO_ERR) { /* try the specified config location */ cp->config = VAL_STR(val); res = conf_parse_val_from_filespec(cp->config, valset, TRUE, TRUE); if (res != NO_ERR) { return res; } } } else { /* try default config location */ res = conf_parse_val_from_filespec(YANGDUMP_DEF_CONFIG, valset, TRUE, FALSE); if (res != NO_ERR) { return res; } } if (!defs_done) { res = val_add_defaults(valset, NULL, NULL, FALSE); if (res != NO_ERR) { return res; } } /* set the logging control parameters */ val_set_logging_parms(valset); /* set the file search path parms */ val_set_path_parms(valset); /* set the warning control parameters */ val_set_warning_parms(valset); /* set the feature code generation parameters */ val_set_feature_parms(valset); /*** ORDER DOES NOT MATTER FOR REST OF PARAMETERS ***/ /* defnames parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_DEFNAMES); if (val && val->res == NO_ERR) { cp->defnames = VAL_BOOL(val); } else { cp->defnames = FALSE; } /* dependencies parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_DEPENDENCIES); if (val && val->res == NO_ERR) { cp->dependencies = TRUE; } /* exports parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_EXPORTS); if (val && val->res == NO_ERR) { cp->exports = TRUE; } /* format parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_FORMAT); if (val && val->res == NO_ERR) { /* format -- use string provided */ cp->format = ncx_get_cvttyp_enum((const char *)VAL_ENUM_NAME(val)); } else { cp->format = NCX_CVTTYP_NONE; } /* identifiers parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_IDENTIFIERS); if (val && val->res == NO_ERR) { cp->identifiers = TRUE; } /* indent parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_INDENT); if (val && val->res == NO_ERR) { cp->indent = (int32)VAL_UINT(val); } else { cp->indent = NCX_DEF_INDENT; } /* help parameter */ val = val_find_child(valset, YANGDUMP_MOD, NCX_EL_HELP); if (val && val->res == NO_ERR) { cp->helpmode = TRUE; } /* help submode parameter (brief/normal/full) */ val = val_find_child(valset, YANGDUMP_MOD, NCX_EL_BRIEF); if (val && val->res == NO_ERR) { cp->helpsubmode = HELP_MODE_BRIEF; } else { /* full parameter */ val = val_find_child(valset, YANGDUMP_MOD, NCX_EL_FULL); if (val && val->res == NO_ERR) { cp->helpsubmode = HELP_MODE_FULL; } else { cp->helpsubmode = HELP_MODE_NORMAL; } } /* html-div parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_HTML_DIV); if (val && val->res == NO_ERR) { cp->html_div = VAL_BOOL(val); } else { cp->html_div = FALSE; } /* html-toc parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_HTML_TOC); if (val && val->res == NO_ERR) { cp->html_toc = VAL_STR(val); } else { cp->html_toc = YANGDUMP_DEF_TOC; } /* module parameter */ cp->modcount = val_instance_count(valset, NULL, YANGDUMP_PARM_MODULE); /* modversion parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_MODVERSION); if (val && val->res == NO_ERR) { cp->modversion = TRUE; } /* subdirs parameter */ val = val_find_child(valset, YANGDUMP_MOD, NCX_EL_SUBDIRS); if (val && val->res == NO_ERR) { cp->subdirs = VAL_BOOL(val); } else { cp->subdirs = TRUE; } /* tree-identifiers parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_TREE_IDENTIFIERS); if (val && val->res == NO_ERR) { cp->tree_identifiers = TRUE; } /* versionnames */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_VERSIONNAMES); if (val && val->res == NO_ERR) { cp->versionnames = VAL_BOOL(val); } else { cp->versionnames = TRUE; } /* objview parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_OBJVIEW); if (val && val->res == NO_ERR) { cp->objview = (const char *)VAL_STR(val); } else { cp->objview = YANGDUMP_DEF_OBJVIEW; } /* rawmode derived parameter */ cp->rawmode = strcmp(cp->objview, OBJVIEW_RAW) ? FALSE : TRUE; /* output parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_OUTPUT); if (val && val->res == NO_ERR) { /* output -- use filename provided */ cp->output = (const char *)VAL_STR(val); res = NO_ERR; cp->full_output = ncx_get_source_ex(VAL_STR(val), FALSE, &res); if (!cp->full_output) { return res; } cp->output_isdir = ncxmod_test_subdir(cp->full_output); } /* show-errors parameter */ val = val_find_child(valset, YANGDUMP_MOD, NCX_EL_SHOW_ERRORS); if (val && val->res == NO_ERR) { cp->showerrorsmode = TRUE; } /* simurls parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_SIMURLS); if (val && val->res == NO_ERR) { cp->simurls = TRUE; } /* stats parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_STATS); if (val && val->res == NO_ERR) { if (!xml_strcmp(VAL_ENUM_NAME(val), YANGDUMP_PV_STATS_NONE)) { cp->stats = YANGDUMP_REPORT_NONE; } else { cp->collect_stats = TRUE; if (!xml_strcmp(VAL_ENUM_NAME(val), YANGDUMP_PV_STATS_BRIEF)) { cp->stats = YANGDUMP_REPORT_BRIEF; } else if (!xml_strcmp(VAL_ENUM_NAME(val), YANGDUMP_PV_STATS_BASIC)) { cp->stats = YANGDUMP_REPORT_BASIC; } else if (!xml_strcmp(VAL_ENUM_NAME(val), YANGDUMP_PV_STATS_ADVANCED)) { cp->stats = YANGDUMP_REPORT_ADVANCED; } else if (!xml_strcmp(VAL_ENUM_NAME(val), YANGDUMP_PV_STATS_ALL)) { cp->stats = YANGDUMP_REPORT_ALL; } else { SET_ERROR(ERR_INTERNAL_VAL); } } } /* stat-totals parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_STAT_TOTALS); if (val && val->res == NO_ERR) { if (!xml_strcmp(VAL_ENUM_NAME(val), YANGDUMP_PV_TOTALS_NONE)) { cp->stat_totals = YANGDUMP_TOTALS_NONE; } else { if (!xml_strcmp(VAL_ENUM_NAME(val), YANGDUMP_PV_TOTALS_SUMMARY)) { if (cp->collect_stats) { cp->stat_totals = YANGDUMP_TOTALS_SUMMARY; } } else if (!xml_strcmp(VAL_ENUM_NAME(val), YANGDUMP_PV_TOTALS_SUMMARY_ONLY)) { if (cp->collect_stats) { cp->stat_totals = YANGDUMP_TOTALS_SUMMARY_ONLY; } } else { SET_ERROR(ERR_INTERNAL_VAL); } } } else if (val == NULL) { if (cp->collect_stats) { cp->stat_totals = YANGDUMP_TOTALS_SUMMARY; } } /* subtree parameter */ cp->subtreecount = val_instance_count(valset, NULL, YANGDUMP_PARM_SUBTREE); /* unified parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_UNIFIED); if (val && val->res == NO_ERR) { cp->unified = VAL_BOOL(val); } else { cp->unified = FALSE; } /* urlstart parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_URLSTART); if (val && val->res == NO_ERR) { cp->urlstart = VAL_STR(val); } /* version parameter */ val = val_find_child(valset, YANGDUMP_MOD, NCX_EL_VERSION); if (val && val->res == NO_ERR) { cp->versionmode = TRUE; } /* xsd-schemaloc parameter */ val = val_find_child(valset, YANGDUMP_MOD, YANGDUMP_PARM_XSD_SCHEMALOC); if (val && val->res == NO_ERR) { cp->schemaloc = VAL_STR(val); } return NO_ERR; } /* process_cli_input */ /******************************************************************** * FUNCTION get_output_session * * Malloc and init an output session. * Open the correct output file if needed * * INPUTS: * pcb == parser control block * cp == conversion parameter struct to use * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to new session control block, or NULL if some error *********************************************************************/ static ses_cb_t * get_output_session (yang_pcb_t *pcb, yangdump_cvtparms_t *cp, status_t *res) { ncx_module_t *mod; FILE *fp; ses_cb_t *scb; xmlChar *namebuff; fp = NULL; scb = NULL; mod = NULL; namebuff = NULL; *res = NO_ERR; if (pcb != NULL) { mod = pcb->top; if (mod == NULL) { *res = SET_ERROR(ERR_NCX_MOD_NOT_FOUND); } } /* open the output file if not STDOUT */ if (*res == NO_ERR) { if (mod != NULL && ((cp->defnames && cp->format != NCX_CVTTYP_NONE) || (cp->output && cp->output_isdir))) { namebuff = xsd_make_output_filename(mod, cp); if (!namebuff) { *res = ERR_INTERNAL_MEM; } else { fp = fopen((const char *)namebuff, "w"); if (!fp) { *res = ERR_FIL_OPEN; } } } else if (cp->output) { if (!cp->firstdone) { fp = fopen(cp->output, "w"); cp->firstdone = TRUE; } else if (cp->subtree) { fp = fopen(cp->output, "a"); } else { fp = fopen(cp->output, "w"); } if (!fp) { *res = ERR_FIL_OPEN; } } } /* get a dummy session control block */ if (*res == NO_ERR) { scb = ses_new_dummy_scb(); if (!scb) { *res = ERR_INTERNAL_MEM; } else { scb->fp = fp; ses_set_mode(scb, SES_MODE_TEXT); } } if (namebuff) { m__free(namebuff); } return scb; } /* get_output_session */ /******************************************************************** * FUNCTION output_one_module_exports * * Generate a list of top-level symbols for a module * * INPUTS: * pcb == parser control block with module to use * scb == session control block to use for output * buff == scratch buffer to use *********************************************************************/ static void output_one_module_exports (ncx_module_t *mod, ses_cb_t *scb, char *buff) { typ_template_t *typ; grp_template_t *grp; obj_template_t *obj; ext_template_t *ext; ncx_feature_t *feature; boolean anyout = FALSE; if (mod->ismod) { anyout = TRUE; sprintf(buff, "\nnamespace %s", mod->ns); ses_putstr(scb, (const xmlChar *)buff); if (mod->prefix) { sprintf(buff, "\nprefix %s", mod->prefix); ses_putstr(scb, (const xmlChar *)buff); } else { sprintf(buff, "\nprefix %s", mod->name); ses_putstr(scb, (const xmlChar *)buff); } } for (typ = (typ_template_t *)dlq_firstEntry(&mod->typeQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { sprintf(buff, "\ntypedef %s", typ->name); ses_putstr(scb, (const xmlChar *)buff); anyout = TRUE; } for (grp = (grp_template_t *)dlq_firstEntry(&mod->groupingQ); grp != NULL; grp = (grp_template_t *)dlq_nextEntry(grp)) { sprintf(buff, "\ngrouping %s", grp->name); ses_putstr(scb, (const xmlChar *)buff); anyout = TRUE; } for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { switch (obj->objtype) { case OBJ_TYP_AUGMENT: case OBJ_TYP_USES: break; default: if (obj_is_cloned(obj) && !obj_is_augclone(obj)) { break; } sprintf(buff, "\n%s %s", obj_get_typestr(obj), obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)buff); anyout = TRUE; } } for (ext = (ext_template_t *)dlq_firstEntry(&mod->extensionQ); ext != NULL; ext = (ext_template_t *)dlq_nextEntry(ext)) { sprintf(buff, "\nextension %s", ext->name); ses_putstr(scb, (const xmlChar *)buff); anyout = TRUE; } for (feature = (ncx_feature_t *)dlq_firstEntry(&mod->featureQ); feature != NULL; feature = (ncx_feature_t *)dlq_nextEntry(feature)) { sprintf(buff, "\nfeature %s", feature->name); ses_putstr(scb, (const xmlChar *)buff); anyout = TRUE; } if (anyout) { ses_putchar(scb, '\n'); } } /* output_one_module_exports */ /******************************************************************** * FUNCTION output_module_exports * * Generate a list of top-level symbols for a module * * INPUTS: * pcb == parser control block with module to use * cp == coversion param struct to use * scb == session control block to use for output *********************************************************************/ static void output_module_exports (yang_pcb_t *pcb, yangdump_cvtparms_t *cp, ses_cb_t *scb) { ncx_module_t *mod; yang_node_t *node; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { SET_ERROR(ERR_NCX_MOD_NOT_FOUND); return; } ses_putstr(scb, (const xmlChar *)"\nexports:"); /* check subtree mode */ if (cp->subtree) { print_subtree_banner(cp, mod, scb); } output_one_module_exports(mod, scb, cp->buff); if ((cp->unified || cp->onemodule) && mod->ismod) { for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { output_one_module_exports(node->submod, scb, cp->buff); } } } } /* output_module_exports */ /******************************************************************** * FUNCTION output_one_module_dependencies * * Generate a list of dependencies for a module * * INPUTS: * mod == module in progress * scb == session control block to use for output * cp == conversion parameters in use *********************************************************************/ static void output_one_module_dependencies (ncx_module_t *mod, ses_cb_t *scb, yangdump_cvtparms_t *cp) { ncx_module_t *testmod; yang_import_ptr_t *impptr; yang_node_t *node; boolean anyout = FALSE; for (impptr = (yang_import_ptr_t *)dlq_firstEntry(&mod->saveimpQ); impptr != NULL; impptr = (yang_import_ptr_t *)dlq_nextEntry(impptr)) { testmod = ncx_find_module(impptr->modname, impptr->revision); sprintf(cp->buff, "\nimport %s", impptr->modname); ses_putstr(scb,(const xmlChar *)cp->buff); if (testmod && testmod->version) { sprintf(cp->buff, "@%s", testmod->version); ses_putstr(scb,(const xmlChar *)cp->buff); } anyout = TRUE; } if (!cp->unified) { for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { sprintf(cp->buff, "\ninclude %s", node->name); ses_putstr(scb,(const xmlChar *)cp->buff); if (node->submod && node->submod->version) { sprintf(cp->buff, "@%s", node->submod->version); ses_putstr(scb,(const xmlChar *)cp->buff); } anyout = TRUE; } } if (anyout) { ses_putchar(scb, '\n'); } } /* output_one_module_dependencies */ /******************************************************************** * FUNCTION output_module_dependencies * * Generate a list of dependencies for a module * * INPUTS: * pcb == parser control block with module to use * cp == conversion parameters to use * scb == session control block to write output *********************************************************************/ static void output_module_dependencies (yang_pcb_t *pcb, yangdump_cvtparms_t *cp, ses_cb_t *scb) { ncx_module_t *mod; yang_node_t *node; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { SET_ERROR(ERR_NCX_MOD_NOT_FOUND); return; } ses_putstr(scb, (const xmlChar *)"\ndependencies:"); /* check subtree mode */ if (cp->subtree) { print_subtree_banner(cp, mod, scb); } output_one_module_dependencies(mod, scb, cp); if ((cp->unified || cp->onemodule) && mod->ismod) { for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { output_one_module_dependencies(node->submod, scb, cp); } } } } /* output_module_dependencies */ /******************************************************************** * FUNCTION output_identifiers * * Generate a list of object identifiers for an object * YANG support only!!! * * INPUTS: * datadefQ == Q of obj_template_t to use * scb == session control block for output * buff == scratch buffer to use * bufflen == size of buff in bytes * treeformat == TRUE for tree format; FALSE for list forma * startindent == startindent amount * indent == indent amount * helpmode == verbosity level * * RETURNS: * status *********************************************************************/ static status_t output_identifiers (dlq_hdr_t *datadefQ, ses_cb_t *scb, xmlChar *buff, uint32 bufflen, boolean treeformat, uint32 startindent, uint32 indent, help_mode_t helpmode) { status_t res = NO_ERR; obj_template_t *obj; for (obj = (obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj)) { continue; /* skip uses and augment */ } if (!treeformat) { uint32 reallen = 0; if (helpmode == HELP_MODE_FULL) { res = obj_copy_object_id_mod(obj, buff, bufflen, &reallen); } else { res = obj_copy_object_id(obj, buff, bufflen, &reallen); } if (res != NO_ERR) { log_error("\nError: copy object ID failed (%s)", get_error_string(res)); return res; } } ses_putchar(scb, '\n'); if (treeformat) { uint32 i; for (i=0; i < startindent; i++) { ses_putchar(scb, ' '); } } ses_putstr(scb, obj_get_typestr(obj)); ses_putchar(scb, ' '); if (treeformat) { ses_putstr(scb, obj_get_name(obj)); } else { ses_putstr(scb, buff); } dlq_hdr_t *childQ = obj_get_datadefQ(obj); if (childQ) { res = output_identifiers(childQ, scb, buff, bufflen, treeformat, startindent+indent, indent, helpmode); if (res != NO_ERR) { return res; } } } return NO_ERR; } /* output_identifiers */ /******************************************************************** * FUNCTION output_one_module_identifiers * * Generate a list of object identifiers for a module * YANG support only!!! * * INPUTS: * pcb == parser control block with module to use * scb == session control block to use for output * buff == scratch buffer to use * bufflen == size of buff in bytes * treeformat == TRUE for tree format; FALSE for list format * indent == indent amount * helpmode == verbosity level *********************************************************************/ static void output_one_module_identifiers (ncx_module_t *mod, ses_cb_t *scb, xmlChar *buff, uint32 bufflen, boolean treeformat, uint32 indent, help_mode_t helpmode) { status_t res; res = output_identifiers(&mod->datadefQ, scb, buff, bufflen, treeformat, indent, indent, helpmode); if (res == NO_ERR) { ses_putchar(scb, '\n'); } } /* output_one_module_identifiers */ /******************************************************************** * FUNCTION output_module_identifiers * * Generate a list of object identifiers for a module * YANG support only!!! * * INPUTS: * pcb == parser control block with module to use * cp == conversion parameters to use * scb == session control block for output * treeformat == TRUE for tree format; FALSE for list format * helpmode == verbosity level *********************************************************************/ static void output_module_identifiers (yang_pcb_t *pcb, yangdump_cvtparms_t *cp, ses_cb_t *scb, boolean treeformat, help_mode_t helpmode) { ncx_module_t *mod; yang_node_t *node; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { SET_ERROR(ERR_NCX_MOD_NOT_FOUND); return; } ses_putstr(scb, (const xmlChar *)"\nidentifiers:"); /* check subtree mode */ if (cp->subtree) { print_subtree_banner(cp, mod, scb); } output_one_module_identifiers(mod, scb, (xmlChar *)cp->buff, cp->bufflen, treeformat, cp->indent, helpmode); if ((cp->unified || cp->onemodule) && mod->ismod) { for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { output_one_module_identifiers(node->submod, scb, (xmlChar *)cp->buff, cp->bufflen, treeformat, cp->indent, helpmode); } } } } /* output_module_identifiers */ /******************************************************************** * FUNCTION output_one_module_version * * Generate the module or submodule version * * INPUTS: * pcb == parser control block with module to use * scb == session control block to use for output * buff == scratch buffer to use *********************************************************************/ static void output_one_module_version (ncx_module_t *mod, ses_cb_t *scb, char *buff) { if (mod->ismod) { sprintf(buff, "\nmodule %s", mod->name); } else { sprintf(buff, "\nsubmodule %s", mod->name); } ses_putstr(scb, (const xmlChar *)buff); sprintf(buff, "@%s", (mod->version) ? mod->version : NCX_EL_NONE); ses_putstr(scb, (const xmlChar *)buff); if (mod->source && LOGDEBUG2) { ses_putchar(scb, ' '); ses_putstr(scb, mod->source); } } /* output_one_module_version */ /******************************************************************** * FUNCTION output_module_version * * Generate the module version * YANG support only!!! * * INPUTS: * pcb == parser control block with module to use * cp == conversion parameters to use * scb == session control block for output *********************************************************************/ static void output_module_version (yang_pcb_t *pcb, yangdump_cvtparms_t *cp, ses_cb_t *scb) { ncx_module_t *mod; yang_node_t *node; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { SET_ERROR(ERR_NCX_MOD_NOT_FOUND); return; } ses_putstr(scb, (const xmlChar *)"\nmodversion:"); output_one_module_version(mod, scb, cp->buff); if (cp->unified && mod->ismod) { for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { output_one_module_version(node->submod, scb, cp->buff); } } } ses_putchar(scb, '\n'); } /* output_module_version */ /******************************************************************** * FUNCTION copy_module * * Copy the source file to a different location and possibly * change the filename * * INPUTS: * pcb == parser control block with module to use * cp == conversion parameters to use * scb == session control block for output * * RETURNS: * status *********************************************************************/ static status_t copy_module (yang_pcb_t *pcb, yangdump_cvtparms_t *cp, ses_cb_t *scb) { ncx_module_t *mod; FILE *srcfile; boolean done; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { return ERR_NCX_MOD_NOT_FOUND; } /* open the YANG source file for reading */ srcfile = fopen((const char *)mod->source, "r"); if (!srcfile) { return ERR_NCX_MISSING_FILE; } done = FALSE; while (!done) { if (!fgets((char *)cp->buff, (int)cp->bufflen, srcfile)) { /* read line failed, not an error */ done = TRUE; continue; } ses_putstr(scb, (const xmlChar *)cp->buff); } fclose(srcfile); return NO_ERR; } /* copy_module */ /******************************************************************** * FUNCTION print_score_banner * * Generate the banner for the yangdump results * * INPUTS: * pcb == parser control block to use * *********************************************************************/ static void print_score_banner (yang_pcb_t *pcb) { log_write("\n*** %s\n*** %u Errors, %u Warnings\n", pcb->top->source, pcb->top->errors, pcb->top->warnings); } /* print_score_banner */ /******************************************************************** * FUNCTION format_allowed * * Check if the conversion format is allowed for yangdump or * yangdumpcode * * INPUTS: * cp == conversion parameters * * RETURNS: * TRUE if format conversion allowed; FALSE if not * *********************************************************************/ static boolean format_allowed (const yangdump_cvtparms_t *cp) { if (cp->allowcode) { return TRUE; } switch (cp->format) { case NCX_CVTTYP_NONE: case NCX_CVTTYP_XSD: case NCX_CVTTYP_HTML: case NCX_CVTTYP_YANG: case NCX_CVTTYP_COPY: case NCX_CVTTYP_YIN: case NCX_CVTTYP_TREE: return TRUE; case NCX_CVTTYP_SQL: case NCX_CVTTYP_SQLDB: case NCX_CVTTYP_H: case NCX_CVTTYP_C: case NCX_CVTTYP_CPP_TEST: case NCX_CVTTYP_YH: case NCX_CVTTYP_YC: case NCX_CVTTYP_UH: case NCX_CVTTYP_UC: case NCX_CVTTYP_TG2: return FALSE; default: SET_ERROR(ERR_INTERNAL_VAL); } return FALSE; } /* format_allowed */ /******************************************************************** * FUNCTION convert_one * * Validate and then perhaps convert one module to the specified format * The global params in cvtparms are used (should change that!) * * INPUTS: * cp == parameter block to use * * RETURNS: * status *********************************************************************/ static status_t convert_one (yangdump_cvtparms_t *cp) { ses_cb_t *scb; ncx_module_t *mainmod; val_value_t *val; yang_pcb_t *pcb; xmlChar *modname, *namebuff, *savestr, savechar; const xmlChar *revision; xml_attrs_t attrs; status_t res; boolean bannerdone; uint32 modlen; scb = NULL; mainmod = NULL; revision = NULL; savestr = NULL; savechar = '\0'; bannerdone = FALSE; modlen = 0; modname = (xmlChar *)cp->curmodule; res = NO_ERR; if (yang_split_filename(modname, &modlen)) { savestr = &modname[modlen]; savechar = *savestr; *savestr = '\0'; revision = savestr + 1; } /* load in the requested module to convert */ pcb = ncxmod_load_module_ex(modname, revision, cp->unified, /* with_submods */ /* savetkc for yin, yang, html */ !cp->rawmode || cp->format == NCX_CVTTYP_YIN || cp->format == NCX_CVTTYP_YANG || cp->format == NCX_CVTTYP_HTML, TRUE, /* keepmode to force new load */ /* docmode for --format=html or yang */ cp->format == NCX_CVTTYP_YANG || cp->format == NCX_CVTTYP_HTML, &cp->savedevQ, &res); if (savestr != NULL) { *savestr = savechar; } if (res == ERR_NCX_SKIPPED) { if (pcb) { yang_free_pcb(pcb); } return NO_ERR; } else if (res != NO_ERR) { if (pcb && pcb->top) { print_score_banner(pcb); } else if (res == ERR_NCX_MOD_NOT_FOUND) { log_error("\nError: [sub]module '%s' not found\n", modname); } else if (LOGDEBUG2) { /* invalid module name and/or revision date */ if (revision) { log_debug2("\n[sub]module '%s' revision '%s' " "not loaded", modname, revision); } else { log_debug2("\n[sub]module '%s' not loaded", modname); } log_debug2(" (%s)\n", get_error_string(res)); } if (!pcb || !pcb->top || pcb->top->errors) { if (pcb) { yang_free_pcb(pcb); } return res; } else { /* just warnings reported */ res = NO_ERR; } } else if (pcb && pcb->top) { if (LOGINFO) { /* generate banner everytime yangdump runs */ if(cp->format!=NCX_CVTTYP_TREE) { print_score_banner(pcb); } bannerdone = TRUE; } else { if (!(cp->format == NCX_CVTTYP_XSD || cp->format == NCX_CVTTYP_HTML || cp->format == NCX_CVTTYP_H || cp->format == NCX_CVTTYP_C || cp->format == NCX_CVTTYP_CPP_TEST || cp->format == NCX_CVTTYP_UC || cp->format == NCX_CVTTYP_UH || cp->format == NCX_CVTTYP_YC || cp->format == NCX_CVTTYP_YH || cp->format == NCX_CVTTYP_TG2 || cp->format == NCX_CVTTYP_TREE)) { print_score_banner(pcb); bannerdone = TRUE; } } } /* check if output session needed, any reports requested or * any format except XSD, and NONE means a session will be used * to write output. */ if (cp->modversion || cp->exports || cp->dependencies || cp->identifiers || cp->tree_identifiers || cp->collect_stats || !(cp->format == NCX_CVTTYP_NONE || cp->format == NCX_CVTTYP_XSD)) { scb = get_output_session(pcb, cp, &res); if (!scb || res != NO_ERR) { log_error("\nError: open session failed (%s)\n", get_error_string(res)); yang_free_pcb(pcb); if (scb) { ses_free_scb(scb); } return res; } if (cp->indent != ses_indent_count(scb)) { ses_set_indent(scb, cp->indent); } } /* check if modversion and other report modes */ if (scb) { if (cp->modversion) { output_module_version(pcb, cp, scb); } if (cp->exports) { output_module_exports(pcb, cp, scb); } if (cp->dependencies) { output_module_dependencies(pcb, cp, scb); } if (cp->identifiers) { output_module_identifiers(pcb, cp, scb, FALSE, cp->helpsubmode); } if (cp->tree_identifiers) { output_module_identifiers(pcb, cp, scb, TRUE, cp->helpsubmode); } if (cp->collect_stats) { yangstats_collect_module_stats(pcb, cp); if (cp->stat_totals != YANGDUMP_TOTALS_SUMMARY_ONLY) { yangstats_output_module_stats(pcb, cp, scb); } } } /* get the namespace info for submodules */ if (cp->format == NCX_CVTTYP_XSD || cp->format == NCX_CVTTYP_SQLDB || cp->format == NCX_CVTTYP_TG2 || cp->format == NCX_CVTTYP_HTML || cp->format == NCX_CVTTYP_TREE) { if (!pcb->top->ismod) { /* need to load the entire module now, * in order to get a namespace ID for * the main module, which is also used in the submodule * !!! DO NOT KNOW THE REVISION OF THE PARENT !!! */ res = ncxmod_load_module(ncx_get_modname(pcb->top), NULL, &cp->savedevQ, &mainmod); if (res != NO_ERR) { log_error("\nError: main module '%s' had errors." "\n XSD conversion of '%s' terminated.", ncx_get_modname(pcb->top), pcb->top->sourcefn); ncx_print_errormsg(NULL, pcb->top, res); } else { /* make a copy of the module namespace URI * so the XSD targetNamespace will be generated */ pcb->top->ns = mainmod->ns; pcb->top->nsid = mainmod->nsid; } } } /* should be ready to convert the requested [sub]module now */ if (res == NO_ERR && pcb->top) { /* check the type of translation requested */ switch (cp->format) { case NCX_CVTTYP_NONE: if (ncx_any_dependency_errors(pcb->top)) { if (ncx_warning_enabled(ERR_NCX_DEPENDENCY_ERRORS)) { log_warn("\nWarning: one or more modules " "imported into '%s' " "had errors", pcb->top->sourcefn); } } if (LOGDEBUG2) { log_debug2("\nFile '%s' compiled without errors", pcb->top->sourcefn); } break; case NCX_CVTTYP_XSD: if (ncx_any_dependency_errors(pcb->top)) { log_error("\nError: one or more imported modules had errors." "\n XSD conversion of '%s' terminated.", pcb->top->sourcefn); res = ERR_NCX_IMPORT_ERRORS; ncx_print_errormsg(NULL, pcb->top, res); } else { val = NULL; xml_init_attrs(&attrs); ncx_set_useprefix(TRUE); res = xsd_convert_module(pcb, pcb->top, cp, &val, &attrs); if (res != NO_ERR) { pr_err(res); } else { if (cp->defnames || (cp->output && cp->output_isdir)) { namebuff = xsd_make_output_filename(pcb->top, cp); if (!namebuff) { res = ERR_INTERNAL_MEM; } else { /* output to the expanded file name from * the specified file, or generated * with the default name */ res = xml_wr_file(namebuff, val, &attrs, DOCMODE, WITHHDR, TRUE, 0, cp->indent); m__free(namebuff); } } else if (cp->output) { /* output to the specified file */ res = xml_wr_file((const xmlChar *)cp->output, val, &attrs, DOCMODE, WITHHDR, TRUE, 0, cp->indent); } else { /* output to the specified file */ res = xml_wr_check_open_file(stdout, val, &attrs, DOCMODE, WITHHDR, TRUE, 0, cp->indent, NULL); } } if (res != NO_ERR) { log_error("\nError: cannot write output file '%s'", (cp->output) ? cp->output : "stdout"); } xml_clean_attrs(&attrs); if (val) { val_free_value(val); } } break; case NCX_CVTTYP_SQL: res = ERR_NCX_OPERATION_NOT_SUPPORTED; pr_err(res); break; case NCX_CVTTYP_SQLDB: if (ncx_any_dependency_errors(pcb->top)) { log_error("\nError: one or more imported modules had errors." "\n SQL object database conversion of " "'%s' terminated.", pcb->top->sourcefn); res = ERR_NCX_IMPORT_ERRORS; ncx_print_errormsg(NULL, pcb->top, res); } else { res = sql_convert_module(pcb, cp, scb); if (res != NO_ERR) { pr_err(res); } } break; case NCX_CVTTYP_COPY: if (ncx_any_dependency_errors(pcb->top)) { log_error("\nError: one or more imported modules had errors." "\n Copy source for '%s' terminated.", pcb->top->sourcefn); res = ERR_NCX_IMPORT_ERRORS; ncx_print_errormsg(NULL, pcb->top, res); } else { res = copy_module(pcb, cp, scb); if (res != NO_ERR) { pr_err(res); } } break; case NCX_CVTTYP_HTML: if (ncx_any_dependency_errors(pcb->top)) { log_error("\nError: one or more imported modules had errors." "\n HTML conversion of '%s' terminated.", pcb->top->sourcefn); res = ERR_NCX_IMPORT_ERRORS; ncx_print_errormsg(NULL, pcb->top, res); } else { cp->tkc = pcb->tkc; res = html_convert_module(pcb, cp, scb); if (res != NO_ERR) { pr_err(res); } } break; case NCX_CVTTYP_H: case NCX_CVTTYP_UH: case NCX_CVTTYP_YH: cp->isuser = (cp->format == NCX_CVTTYP_UH) ? TRUE : FALSE; if (ncx_any_dependency_errors(pcb->top)) { log_error("\nError: one or more imported modules had errors." "\n H file conversion of '%s' terminated.", pcb->top->sourcefn); res = ERR_NCX_IMPORT_ERRORS; ncx_print_errormsg(NULL, pcb->top, res); } else { res = h_convert_module(pcb, cp, scb); if (res != NO_ERR) { pr_err(res); } } break; case NCX_CVTTYP_UC: case NCX_CVTTYP_C: case NCX_CVTTYP_CPP_TEST: case NCX_CVTTYP_YC: cp->isuser = (cp->format == NCX_CVTTYP_UC) ? TRUE : FALSE; if (ncx_any_dependency_errors(pcb->top)) { log_error("\nError: one or more imported modules had errors." "\n C file conversion of '%s' terminated.", pcb->top->sourcefn); res = ERR_NCX_IMPORT_ERRORS; ncx_print_errormsg(NULL, pcb->top, res); } else { res = c_convert_module(pcb, cp, scb); if (res != NO_ERR) { pr_err(res); } } break; case NCX_CVTTYP_YANG: if (ncx_any_dependency_errors(pcb->top)) { log_error("\nError: one or more imported modules had errors." "\n YANG conversion of '%s' terminated.", pcb->top->sourcefn); res = ERR_NCX_IMPORT_ERRORS; ncx_print_errormsg(NULL, pcb->top, res); } else { cp->tkc = pcb->tkc; res = cyang_convert_module(pcb, cp, scb); if (res != NO_ERR) { pr_err(res); } } break; case NCX_CVTTYP_YIN: if (ncx_any_dependency_errors(pcb->top)) { log_error("\nError: one or more imported modules had errors." "\n YIN conversion of '%s' terminated.", pcb->top->sourcefn); res = ERR_NCX_IMPORT_ERRORS; ncx_print_errormsg(NULL, pcb->top, res); } else { res = yangyin_convert_module(pcb, cp, scb); if (res != NO_ERR) { pr_err(res); } } break; case NCX_CVTTYP_TG2: if (ncx_any_dependency_errors(pcb->top)) { log_error("\nError: one or more imported modules had errors." "\n TG2 source code conversion of " "'%s' terminated.", pcb->top->sourcefn); res = ERR_NCX_IMPORT_ERRORS; ncx_print_errormsg(NULL, pcb->top, res); } else { res = tg2_convert_module_model(pcb, cp, scb); if (res != NO_ERR) { pr_err(res); } } break; case NCX_CVTTYP_TREE: if (ncx_any_dependency_errors(pcb->top)) { log_error("\nError: one or more imported modules had errors." "\n TREE conversion of " "'%s' terminated.", pcb->top->sourcefn); res = ERR_NCX_IMPORT_ERRORS; ncx_print_errormsg(NULL, pcb->top, res); } else { res = yangtree_convert_module(pcb, cp, scb); if (res != NO_ERR) { pr_err(res); } } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); pr_err(res); } } if (res != NO_ERR || LOGDEBUG2) { if (pcb && pcb->top && !bannerdone) { print_score_banner(pcb); } else if (res != NO_ERR) { log_write("\n"); } } if (pcb) { yang_free_pcb(pcb); } if (scb) { ses_free_scb(scb); } return res; } /* convert_one */ /******************************************************************** * FUNCTION subtree_callback * * Handle the current filename in the subtree traversal * Parse the module and generate. * * Follows ncxmod_callback_fn_t template * * INPUTS: * fullspec == absolute or relative path spec, with filename and ext. * this regular file exists, but has not been checked for * read access of * cookie == yangcli conversion parms * * RETURNS: * status * * Return fatal error to stop the traversal or NO_ERR to * keep the traversal going. Do not return any warning or * recoverable error, just log and move on *********************************************************************/ static status_t subtree_callback (const char *fullspec, void *cookie) { yangdump_cvtparms_t *cp; status_t res; res = NO_ERR; cp = cookie; if (cp->module) { m__free(cp->module); } cp->module = (char *)ncx_get_source((const xmlChar *)fullspec, &res); if (!cp->module) { return res; } cp->curmodule = cp->module; log_debug2("\nStart subtree file:\n%s\n", cp->module); res = convert_one(cp); if (res != NO_ERR) { if (!NEED_EXIT(res)) { res = NO_ERR; } } return res; } /* subtree_callback */ /************ E X T E R N A L F U N C T I O N S ************/ /******************************************************************** * FUNCTION ydump_init * * Parse all CLI parameters and fill in the conversion parameters * * INPUTS: * argc == number of argv parameters * argv == array of CLI parameters (may be NULL to skip) * allowcode == TRUE if code generation should be allowed * FALSE otherwise * cvtparms == address of conversion parms to fill in * * OUTPUTS: * *cvtparms filled in, maybe partial if errors * * RETURNS: * status *********************************************************************/ status_t ydump_init (int argc, char *argv[], boolean allowcode, yangdump_cvtparms_t *cvtparms) { status_t res; xmlns_id_t nsid; #ifdef DEBUG if (cvtparms == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif memset(cvtparms, 0x0, sizeof(yangdump_cvtparms_t)); /* initialize the NCX Library first to allow NCX modules * to be processed. No module can get its internal config * until the NCX module parser and definition registry is up * parm(TRUE) indicates all description clauses should be saved * Set debug cutoff filter to user errors */ res = ncx_init(TRUE, LOG_DEBUG_INFO, FALSE, NULL, argc, argv); if (res == NO_ERR) { res = init_cvtparms(cvtparms, allowcode); } if (res == NO_ERR) { /* load in the YANGDUMP converter parmset definition file */ res = ncxmod_load_module(YANGDUMP_MOD, NULL, NULL, NULL); } /* Initialize the Notifications namespace */ if (res == NO_ERR) { res = xmlns_register_ns(NCN_URN, NCN_PREFIX, NCX_MODULE, NULL, &nsid); } if (res == NO_ERR) { res = process_cli_input(argc, argv, cvtparms); } if (res == NO_ERR && cvtparms->collect_stats) { res = init_cvtparms_stats(cvtparms); } if (res != NO_ERR && res != ERR_NCX_SKIPPED) { pr_err(res); } return res; } /* ydump_init */ /******************************************************************** * FUNCTION ydump_main * * Run the yangdump conversion, according to the specified * yangdump conversion parameters set * * INPUTS: * cvtparms == conversion parameters to use * * OUTPUTS: * the conversion (if any) will be done and output to * the specified files, or STDOUT * * RETURNS: * status *********************************************************************/ status_t ydump_main (yangdump_cvtparms_t *cvtparms) { val_value_t *val; const char *progname; ses_cb_t *scb; status_t res; boolean done, quickexit; xmlChar buffer[NCX_VERSION_BUFFSIZE]; done = FALSE; progname = "yangdump"; quickexit = cvtparms->helpmode || cvtparms->versionmode || cvtparms->showerrorsmode; if (cvtparms->versionmode || cvtparms->showerrorsmode) { res = ncx_get_version(buffer, NCX_VERSION_BUFFSIZE); if (res == NO_ERR) { log_write("\n%s %s", progname, buffer); if (cvtparms->versionmode) { log_write("\n"); } } else { SET_ERROR(res); } } if (cvtparms->helpmode) { help_program_module(YANGDUMP_MOD, YANGDUMP_CONTAINER, cvtparms->helpsubmode); } if (cvtparms->showerrorsmode) { log_write(" errors and warnings\n"); print_error_messages(); } if (quickexit) { return NO_ERR; } /* check if the requested format is allowed */ if (!format_allowed(cvtparms)) { log_error("\nError: The requested conversion format " "is not supported by yangdump."); return ERR_NCX_OPERATION_NOT_SUPPORTED; } /* check if subdir search suppression is requested */ if (!cvtparms->subdirs) { ncxmod_set_subdirs(FALSE); } if (LOGINFO) { /* generate banner everytime yangdump runs */ if(cvtparms->format!=NCX_CVTTYP_TREE) { write_banner(); } } else { if (!(cvtparms->format == NCX_CVTTYP_XSD || cvtparms->format == NCX_CVTTYP_HTML || cvtparms->format == NCX_CVTTYP_H || cvtparms->format == NCX_CVTTYP_C || cvtparms->format == NCX_CVTTYP_CPP_TEST)) { write_banner(); } } /* first check if there are any deviations to load */ res = NO_ERR; val = val_find_child(cvtparms->cli_val, YANGDUMP_MOD, YANGDUMP_PARM_DEVIATION); while (val) { res = ncxmod_load_deviation(VAL_STR(val), &cvtparms->savedevQ); if (NEED_EXIT(res)) { val = NULL; } else { val = val_find_next_child(cvtparms->cli_val, YANGDUMP_MOD, YANGDUMP_PARM_DEVIATION, val); } } /* save intermediate files to prevent expanded augments, etc. * from pointing at deleted modules */ ncx_set_use_deadmodQ(); /* force the --feature-enable-default=true for generating C code */ switch (cvtparms->format) { case NCX_CVTTYP_H: case NCX_CVTTYP_UH: case NCX_CVTTYP_YH: case NCX_CVTTYP_C: case NCX_CVTTYP_UC: case NCX_CVTTYP_YC: case NCX_CVTTYP_CPP_TEST: ncx_set_feature_enable_default(TRUE); break; default: ; } /* convert one file or N files or 1 subtree */ res = NO_ERR; val = val_find_child(cvtparms->cli_val, YANGDUMP_MOD, YANGDUMP_PARM_MODULE); while (val) { done = TRUE; cvtparms->curmodule = (char *)VAL_STR(val); cvtparms->onemodule = TRUE; res = convert_one(cvtparms); if (NEED_EXIT(res)) { val = NULL; } else { val = val_find_next_child(cvtparms->cli_val, YANGDUMP_MOD, YANGDUMP_PARM_MODULE, val); } } cvtparms->onemodule = FALSE; if (res == NO_ERR && cvtparms->subtreecount >= 1) { if (cvtparms->format == NCX_CVTTYP_XSD || cvtparms->format == NCX_CVTTYP_HTML || cvtparms->format == NCX_CVTTYP_YANG || cvtparms->format == NCX_CVTTYP_COPY) { /* force separate file names in subtree mode */ cvtparms->defnames = TRUE; } val = val_find_child(cvtparms->cli_val, YANGDUMP_MOD, YANGDUMP_PARM_SUBTREE); while (val) { done = TRUE; /* this var cvtparms->subtree will be non-NULL * if there are any subtrees to process; * this will cause subtree mode to be true, so * a banner is printed after every file */ cvtparms->subtree = (const char *)VAL_STR(val); if (ncxmod_test_subdir((const xmlChar *)cvtparms->subtree)) { res = ncxmod_process_subtree(cvtparms->subtree, subtree_callback, cvtparms); } else { res = ERR_NCX_NOT_FOUND; log_error("\nError: directory '%s' not found\n", cvtparms->subtree); } if (NEED_EXIT(res)) { val = NULL; } else { val = val_find_next_child(cvtparms->cli_val, YANGDUMP_MOD, YANGDUMP_PARM_SUBTREE, val); } } } if (res == NO_ERR && !done) { res = ERR_NCX_MISSING_PARM; log_error("\n%s: Error: missing parameter (%s or %s)\n", progname, NCX_EL_MODULE, NCX_EL_SUBTREE); } if (res == NO_ERR && cvtparms->final_stats != NULL) { scb = get_output_session(NULL, cvtparms, &res); if (scb != NULL) { yangstats_output_final_stats(cvtparms, scb); ses_free_scb(scb); } } return res; } /* ydump_main */ /******************************************************************** * FUNCTION ydump_cleanup * * INPUTS: * cvtparms == conversion parameters to clean but not free * *********************************************************************/ void ydump_cleanup (yangdump_cvtparms_t *cvtparms) { #ifdef DEBUG if (cvtparms == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif clean_cvtparms(cvtparms); /* cleanup the NCX engine and registries */ ncx_cleanup(); } /* ydump_cleanup */ /* END ydump.c */ yuma123_2.14/netconf/src/ydump/py_util.h0000664000175000017500000001222314770023131020342 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_py_util #define _H_py_util /* FILE: py_util.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* YANGDUMP Python code generation utilities ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11-mar-10 abb Begun */ #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define PY_COMMENT (const xmlChar *)"\"\"\" " /* SQL Alchemy 0.5.6 generic data types */ #define SA_STRING (const xmlChar *)"String" #define SA_UNICODE (const xmlChar *)"Unicode" #define SA_TEXT (const xmlChar *)"Text" #define SA_UNICODE_TEXT (const xmlChar *)"UnicodeText" #define SA_INTEGER (const xmlChar *)"Integer" #define SA_SMALL_INTEGER (const xmlChar *)"SmallInteger" #define SA_NUMERIC (const xmlChar *)"Numeric" #define SA_INTEGER (const xmlChar *)"Integer" #define SA_FLOAT (const xmlChar *)"DateTime" #define SA_DATE_TIME (const xmlChar *)"DateTime" #define SA_DATE (const xmlChar *)"Date" #define SA_TIME (const xmlChar *)"Time" #define SA_INTERVAL (const xmlChar *)"Interval" #define SA_BOOLEAN (const xmlChar *)"Boolean" #define SA_BINARY (const xmlChar *)"Binary" #define SA_PICKLE_TYPE (const xmlChar *)"PickleType" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION get_sa_datatype * * Get the SQLAlchemy data type for the associated NCX internal type * * INPUTS: * btyp == NCX base type * * RETURNS: * SA type enumeration string *********************************************************************/ extern const xmlChar * get_sa_datatype (ncx_btype_t btyp); /******************************************************************** * FUNCTION write_py_header * * Write the Python file header * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * *********************************************************************/ extern void write_py_header (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp); /******************************************************************** * FUNCTION write_py_footer * * Write the Python file footer * * INPUTS: * scb == session control block to use for writing * mod == module in progress * *********************************************************************/ extern void write_py_footer (ses_cb_t *scb, const ncx_module_t *mod); #if 0 /******************************************************************* * FUNCTION write_py_objtype * * Generate the Python data type for the YANG data type * * INPUTS: * scb == session control block to use for writing * obj == object template to check * **********************************************************************/ extern void write_py_objtype (ses_cb_t *scb, const obj_template_t *obj); #endif #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_py_util */ yuma123_2.14/netconf/src/ydump/py_util.c0000664000175000017500000001624114770023131020341 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: py_util.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11-mar-10 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_c_util #include "c_util.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_str #include "ncx_str.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_py_util #include "py_util.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifndef _H_yangdump_util #include "yangdump_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION get_sa_datatype * * Get the SQLAlchemy data type for the associated NCX internal type * * INPUTS: * btyp == NCX base type * * RETURNS: * SA type enumeration string *********************************************************************/ const xmlChar * get_sa_datatype (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_NONE: return NCX_EL_NONE; case NCX_BT_ANYDATA: case NCX_BT_ANYXML: return SA_PICKLE_TYPE; case NCX_BT_BITS: case NCX_BT_ENUM: case NCX_BT_EMPTY: return SA_STRING; case NCX_BT_BOOLEAN: return SA_BOOLEAN; case NCX_BT_INT8: case NCX_BT_INT16: return SA_INTEGER; case NCX_BT_INT32: case NCX_BT_INT64: return SA_INTEGER; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: return SA_INTEGER; case NCX_BT_DECIMAL64: return SA_NUMERIC; case NCX_BT_FLOAT64: return SA_FLOAT; case NCX_BT_STRING: return SA_STRING; case NCX_BT_BINARY: return SA_BINARY; case NCX_BT_INSTANCE_ID: case NCX_BT_UNION: case NCX_BT_LEAFREF: case NCX_BT_IDREF: case NCX_BT_SLIST: return SA_STRING; case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_CASE: case NCX_BT_LIST: case NCX_BT_EXTERN: return SA_PICKLE_TYPE; case NCX_BT_INTERN: return SA_STRING; default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_EL_NONE; } /*NOTREACHED*/ } /* get_sa_datatype */ /******************************************************************** * FUNCTION write_py_header * * Write the Python file header * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * *********************************************************************/ void write_py_header (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp) { int32 indent; indent = cp->indent; if (indent <= 0) { indent = NCX_DEF_INDENT; } /* utf-8 decl */ ses_putstr(scb, PY_CODING); /* start comment */ ses_putchar(scb, '\n'); ses_putstr(scb, PY_COMMENT); /* banner comments */ ses_putstr(scb, COPYRIGHT_HEADER); /* generater tag */ write_banner_session_ex(scb, FALSE); /* module name */ if (mod->ismod) { ses_putstr_indent(scb, YANG_K_MODULE, indent); } else { ses_putstr_indent(scb, YANG_K_SUBMODULE, indent); } ses_putchar(scb, ' '); ses_putstr(scb, mod->name); /* version */ if (mod->version) { write_c_simple_str(scb, YANG_K_REVISION, mod->version, indent, 0); ses_putchar(scb, '\n'); } /* namespace or belongs-to */ if (mod->ismod) { write_c_simple_str(scb, YANG_K_NAMESPACE, mod->ns, indent, 0); } else { write_c_simple_str(scb, YANG_K_BELONGS_TO, mod->belongs, indent, 0); } /* organization */ if (mod->organization) { write_c_simple_str(scb, YANG_K_ORGANIZATION, mod->organization, indent, 0); } ses_putchar(scb, '\n'); /* end comment */ ses_putchar(scb, '\n'); ses_putstr(scb, PY_COMMENT); ses_putchar(scb, '\n'); } /* write_py_header */ /******************************************************************** * FUNCTION write_py_footer * * Write the Python file footer * * INPUTS: * scb == session control block to use for writing * mod == module in progress * *********************************************************************/ void write_py_footer (ses_cb_t *scb, const ncx_module_t *mod) { ses_putchar(scb, '\n'); ses_putstr(scb, PY_COMMENT); ses_putstr(scb, (const xmlChar *)" END "); write_c_safe_str(scb, ncx_get_modname(mod)); ses_putstr(scb, (const xmlChar *)".py "); ses_putstr(scb, PY_COMMENT); ses_putchar(scb, '\n'); } /* write_py_footer */ /* END py_util.c */ yuma123_2.14/netconf/src/ydump/yangyin.h0000664000175000017500000000517414770023131020342 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangyin #define _H_yangyin /* FILE: yangyin.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert a YANG module to YIN format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 06-dec-09 abb Begun */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION yangyin_convert_module * * The YIN namespace will be the default namespace * The imported modules will use the xmlprefix in use * which is the YANG prefix unless it is a duplicate * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * * RETURNS: * status *********************************************************************/ extern status_t yangyin_convert_module (yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangyin */ yuma123_2.14/netconf/src/ydump/cyang.c0000664000175000017500000034227214770023131017763 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: cyang.c Generate YANG file output in canonical format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 08apr08 abb begun; started from html.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_cyang #include "cyang.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xsd_util #include "xsd_util.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define START_SEC (const xmlChar *)" {" #define END_SEC (const xmlChar *)"}" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static void write_cyang_groupings (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *groupingQ, int32 startindent); static void write_cyang_type_clause (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, typ_def_t *typdef, int32 startindent); static void write_cyang_objects (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *datadefQ, int32 startindent); /******************************************************************** * FUNCTION has_newlines * * Check if string has newlines * * INPUTS: * str == string to use * *********************************************************************/ static boolean has_newlines (const xmlChar *str) { while (*str && *str != '\n') { str++; } return (*str) ? TRUE : FALSE; } /* has_newlines */ /******************************************************************** * FUNCTION write_cyang_extkw * * Generate a language extension keyword at the current line location * * INPUTS: * scb == session control block to use for writing * kwpfix == keyword prefix to use * kwname == keyword name * *********************************************************************/ static void write_cyang_extkw (ses_cb_t *scb, const xmlChar *kwpfix, const xmlChar *kwname) { if (kwpfix != NULL) { ses_putstr(scb, kwpfix); ses_putchar(scb, ':'); } ses_putstr(scb, kwname); } /* write_cyang_extkw */ /******************************************************************** * FUNCTION write_cyang_banner_cmt * * Generate a comment at the current location for a section banner * Check that only the first one is printed * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * cmval == comment value string * indent == current indent count *********************************************************************/ static void write_cyang_banner_cmt (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const xmlChar *cmval, int32 indent) { boolean needed; needed = FALSE; if (cp->unified) { if (mod->ismod) { needed = TRUE; } } else { needed = TRUE; } if (needed) { ses_putchar(scb, '\n'); ses_indent(scb, indent); ses_putstr(scb, (const xmlChar *)"// "); ses_putstr(scb, cmval); } } /* write_cyang_banner_cmt */ /******************************************************************** * FUNCTION write_cyang_endsec_cmt * * Generate a comment at the current location for an end of a section * No comment tokens are given * * INPUTS: * scb == session control block to use for writing * cmtype == comment type string * cmname == comment value string * *********************************************************************/ static void write_cyang_endsec_cmt (ses_cb_t *scb, const xmlChar *cmtype, const xmlChar *cmname) { ses_putstr(scb, (const xmlChar *)" // "); if (cmtype) { ses_putstr(scb, cmtype); ses_putchar(scb, ' '); } if (cmname) { ses_putstr(scb, cmname); } } /* write_cyang_endsec_cmt */ /******************************************************************** * FUNCTION write_cyang_str * * Generate a string token at the current line location * * INPUTS: * scb == session control block to use for writing * strval == string value * quotes == quotes style (0, 1, 2) *********************************************************************/ static void write_cyang_str (ses_cb_t *scb, const xmlChar *strval, uint32 quotes) { switch (quotes) { case 1: ses_putchar(scb, '\''); break; case 2: ses_putchar(scb, '"'); break; default: ; } ses_putstr(scb, strval); switch (quotes) { case 1: ses_putchar(scb, '\''); break; case 2: ses_putchar(scb, '"'); break; default: ; } } /* write_cyang_str */ /******************************************************************** * FUNCTION write_cyang_comstr * * Generate a complex string token sequence * at the current line location * * INPUTS: * scb == session control block to use for writing * tkptr == token ptr to token with original strings * strval == string value passed to write_cyang_complex_str * indent == current indent count *********************************************************************/ static void write_cyang_comstr (ses_cb_t *scb, const tk_token_ptr_t *tkptr, const xmlChar *strval, int32 indent) { const xmlChar *str; const tk_origstr_t *origstr; uint32 numquotes; boolean dquotes, moreflag, newline; /* put the first string (maybe only fragment) */ dquotes = FALSE; moreflag = FALSE; newline = FALSE; str = tk_get_first_origstr(tkptr, &dquotes, &moreflag); if (str == NULL) { str = strval; numquotes = tk_tkptr_quotes(tkptr); moreflag = FALSE; } else { numquotes = dquotes ? 2 : 1; } /* write first string at current indent point */ write_cyang_str(scb, str, numquotes); if (!moreflag) { return; } indent += ses_indent_count(scb); for (origstr = tk_first_origstr_rec(tkptr); origstr != NULL; origstr = tk_next_origstr_rec(origstr)) { dquotes = FALSE; newline = FALSE; str = tk_get_origstr_parts(origstr, &dquotes, &newline); if (str == NULL) { SET_ERROR(ERR_INTERNAL_VAL); return; } else { if (newline) { ses_putstr_indent(scb, (const xmlChar *)"+ ", indent); } else { ses_putstr(scb, (const xmlChar *)" + "); } write_cyang_str(scb, str, dquotes ? 2 : 1); } } } /* write_cyang_comstr */ /******************************************************************** * FUNCTION write_cyang_id * * Generate a simple clause with an identifier, on 1 line * * INPUTS: * scb == session control block to use for writing * kwname == keyword name * idname == identifier name (may be NULL) * indent == indent count to use * finsemi == TRUE if end in ';', FALSE if '{' * newln == TRUE if a newline should be output first * FALSE if newline should not be output first *********************************************************************/ static void write_cyang_id (ses_cb_t *scb, const xmlChar *kwname, const xmlChar *idname, int32 indent, boolean finsemi, boolean newln) { if (newln) { ses_putchar(scb, '\n'); } ses_putstr_indent(scb, kwname, indent); if (idname) { ses_putchar(scb, ' '); ses_putstr(scb, idname); } if (finsemi) { ses_putchar(scb, ';'); } else { ses_putstr(scb, START_SEC); } } /* write_cyang_id */ /******************************************************************** * FUNCTION write_cyang_complex_str * * Generate a complex string from original tokens or * just a simple clause on 1 line if complex not needed * * INPUTS: * scb == session control block to use for writing * tkc == token chain to search for token ptr records * field == address of string token (key to lookup) * kwname == keyword name * strval == string value * indent == indent count to use * quotes == quotes style (0, 1, 2) * finsemi == TRUE if end in ';', FALSE if '{' *********************************************************************/ static void write_cyang_complex_str (ses_cb_t *scb, tk_chain_t *tkc, const void *field, const xmlChar *kwname, const xmlChar *strval, int32 indent, uint32 quotes, boolean finsemi) { const tk_token_ptr_t *tkptr; if (tkc != NULL && field != NULL) { tkptr = tk_find_tkptr(tkc, field); } else { tkptr = NULL; } ses_putstr_indent(scb, kwname, indent); if (strval) { if (!has_newlines(strval) && (xml_strlen(strval) + 1) < ses_line_left(scb)) { ses_putchar(scb, ' '); } else { indent += ses_indent_count(scb); ses_indent(scb, indent); } if (tkptr != NULL) { write_cyang_comstr(scb, tkptr, strval, indent); } else { write_cyang_str(scb, strval, quotes); } } if (finsemi) { ses_putchar(scb, ';'); } else { ses_putstr(scb, START_SEC); } } /* write_cyang_complex_str */ /******************************************************************** * FUNCTION write_cyang_simple_str * * Generate a simple clause on 1 line * * INPUTS: * scb == session control block to use for writing * kwname == keyword name * strval == string value * indent == indent count to use * quotes == quotes style (0, 1, 2) * finsemi == TRUE if end in ';', FALSE if '{' *********************************************************************/ static void write_cyang_simple_str (ses_cb_t *scb, const xmlChar *kwname, const xmlChar *strval, int32 indent, uint32 quotes, boolean finsemi) { write_cyang_complex_str(scb, NULL, NULL, kwname, strval, indent, quotes, finsemi); } /* write_cyang_simple_str */ /******************************************************************** * FUNCTION write_cyang_status * * Generate the YANG for a status clause only if the value * is other than current * * INPUTS: * scb == session control block to use for writing * status == status field * indent == start indent count * *********************************************************************/ static void write_cyang_status (ses_cb_t *scb, ncx_status_t status, int32 indent) { if (status != NCX_STATUS_CURRENT && status != NCX_STATUS_NONE) { write_cyang_simple_str(scb, YANG_K_STATUS, ncx_get_status_string(status), indent, 0, TRUE); } } /* write_cyang_status */ /******************************************************************** * FUNCTION write_cyang_type * * Generate the YANG for the specified type name * Does not generate the entire clause, just the type name * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * typdef == typ_def_t to use * indent == start indent count * *********************************************************************/ static void write_cyang_type (ses_cb_t *scb, const ncx_module_t *mod, const typ_def_t *typdef, int32 indent) { ses_putstr_indent(scb, YANG_K_TYPE, indent); ses_putchar(scb, ' '); if (typdef->prefix && xml_strcmp(typdef->prefix, mod->prefix)) { write_cyang_extkw(scb, typdef->prefix, typdef->typenamestr); } else { ses_putstr(scb, typdef->typenamestr); } } /* write_cyang_type */ /******************************************************************** * FUNCTION write_cyang_errinfo * * Generate the YANG for the specified error info struct * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * errinfo == ncx_errinfo_t struct to use * indent == start indent count * *********************************************************************/ static void write_cyang_errinfo (ses_cb_t *scb, const yangdump_cvtparms_t *cp, const ncx_errinfo_t *errinfo, int32 indent) { if (errinfo->error_message) { write_cyang_complex_str(scb, cp->tkc, &errinfo->error_message, YANG_K_ERROR_MESSAGE, errinfo->error_message, indent, 2, TRUE); } if (errinfo->error_app_tag) { write_cyang_complex_str(scb, cp->tkc, &errinfo->error_app_tag, YANG_K_ERROR_APP_TAG, errinfo->error_app_tag, indent, 2, TRUE); } if (errinfo->descr) { write_cyang_complex_str(scb, cp->tkc, &errinfo->descr, YANG_K_DESCRIPTION, errinfo->descr, indent, 2, TRUE); } if (errinfo->ref) { write_cyang_complex_str(scb, cp->tkc, &errinfo->ref, YANG_K_REFERENCE, errinfo->ref, indent, 2, TRUE); } } /* write_cyang_errinfo */ /******************************************************************** * FUNCTION write_cyang_musts * * Generate the YANG for a Q of ncx_errinfo_t representing * must-stmts, not just error info * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * mustQ == Q of xpath_pcb_t to use * indent == start indent count * *********************************************************************/ static void write_cyang_musts (ses_cb_t *scb, const yangdump_cvtparms_t *cp, const dlq_hdr_t *mustQ, int32 indent) { const xpath_pcb_t *must; const ncx_errinfo_t *errinfo; for (must = (const xpath_pcb_t *)dlq_firstEntry(mustQ); must != NULL; must = (const xpath_pcb_t *)dlq_nextEntry(must)) { errinfo = &must->errinfo; if (errinfo->descr || errinfo->ref || errinfo->error_app_tag || errinfo->error_message) { write_cyang_complex_str(scb, cp->tkc, &must->exprstr, YANG_K_MUST, must->exprstr, indent, 2, FALSE); write_cyang_errinfo(scb, cp, errinfo, indent + ses_indent_count(scb)); ses_putstr_indent(scb, END_SEC, indent); } else { write_cyang_complex_str(scb, cp->tkc, &must->exprstr, YANG_K_MUST, must->exprstr, indent, 2, TRUE); } } } /* write_cyang_musts */ /******************************************************************** * FUNCTION write_cyang_appinfoQ * * Generate the YANG for a Q of ncx_appinfo_t representing * vendor extensions entered with the YANG statements * * INPUTS: * scb == session control block to use for writing * mod == module in progress * appinfoQ == Q of ncx_appinfo_t to use * indent == start indent count * *********************************************************************/ static void write_cyang_appinfoQ (ses_cb_t *scb, const ncx_module_t *mod, const dlq_hdr_t *appinfoQ, int32 indent) { const ncx_appinfo_t *appinfo; for (appinfo = (const ncx_appinfo_t *)dlq_firstEntry(appinfoQ); appinfo != NULL; appinfo = (const ncx_appinfo_t *)dlq_nextEntry(appinfo)) { ses_indent(scb, indent); write_cyang_extkw(scb, appinfo->prefix, appinfo->name); if (appinfo->value) { ses_putchar(scb, ' '); write_cyang_str(scb, appinfo->value, 2); } if (!dlq_empty(appinfo->appinfoQ)) { ses_putstr(scb, START_SEC); write_cyang_appinfoQ(scb, mod, appinfo->appinfoQ, indent+ses_indent_count(scb)); ses_putstr_indent(scb, END_SEC, indent); } else { ses_putchar(scb, ';'); } } } /* write_cyang_appinfoQ */ /******************************************************************** * FUNCTION write_type_contents * * Generate the YANG for the specified type * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * typdef == typ_def_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_type_contents (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, typ_def_t *typdef, int32 startindent) { typ_unionnode_t *un; const typ_enum_t *bit, *enu; const xmlChar *str; const typ_range_t *range; const typ_pattern_t *pat; const typ_idref_t *idref; char buff[NCX_MAX_NUMLEN]; int32 indent; boolean errinfo_set, constrained_set; indent = startindent + ses_indent_count(scb); write_cyang_appinfoQ(scb, mod, &typdef->appinfoQ, startindent); switch (typdef->tclass) { case NCX_CL_BASE: break; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { typdef = typdef->def.named.newtyp; } else { break; } /* fall through if typdef set */ case NCX_CL_SIMPLE: switch (typ_get_basetype(typdef)) { case NCX_BT_UNION: for (un = typ_first_unionnode(typdef); un != NULL; un = (typ_unionnode_t *)dlq_nextEntry(un)) { if (un->typdef) { write_cyang_type_clause(scb, mod, cp, un->typdef, startindent); } else if (un->typ) { write_cyang_type_clause(scb, mod, cp, &un->typ->typdef, startindent); } else { SET_ERROR(ERR_INTERNAL_VAL); } } break; case NCX_BT_BITS: for (bit = typ_first_con_enumdef(typdef); bit != NULL; bit = (const typ_enum_t *)dlq_nextEntry(bit)) { write_cyang_complex_str(scb, cp->tkc, &bit->name, YANG_K_BIT, bit->name, startindent, 0, FALSE); write_cyang_appinfoQ(scb, mod, &bit->appinfoQ, indent); sprintf(buff, "%u", bit->pos); write_cyang_simple_str(scb, YANG_K_POSITION, (const xmlChar *)buff, indent, 0, TRUE); write_cyang_status(scb, bit->status, indent); if (bit->descr) { write_cyang_complex_str(scb, cp->tkc, &bit->descr, YANG_K_DESCRIPTION, bit->descr, indent, 2, TRUE); } if (bit->ref) { write_cyang_complex_str(scb, cp->tkc, &bit->ref, YANG_K_REFERENCE, bit->ref, indent, 2, TRUE); } ses_putstr_indent(scb, END_SEC, startindent); } break; case NCX_BT_ENUM: for (enu = typ_first_con_enumdef(typdef); enu != NULL; enu = (const typ_enum_t *)dlq_nextEntry(enu)) { write_cyang_complex_str(scb, cp->tkc, &enu->name, YANG_K_ENUM, enu->name, startindent, 2, FALSE); write_cyang_appinfoQ(scb, mod, &enu->appinfoQ, indent); sprintf(buff, "%d", enu->val); write_cyang_simple_str(scb, YANG_K_VALUE, (const xmlChar *)buff, indent, 0, TRUE); write_cyang_status(scb, enu->status, indent); if (enu->descr) { write_cyang_complex_str(scb, cp->tkc, &enu->descr, YANG_K_DESCRIPTION, enu->descr, indent, 2, TRUE); } if (enu->ref) { write_cyang_complex_str(scb, cp->tkc, &enu->ref, YANG_K_REFERENCE, enu->ref, indent, 2, TRUE); } ses_putstr_indent(scb, END_SEC, startindent); } break; case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: /* appinfo only */ break; case NCX_BT_DECIMAL64: sprintf(buff, "%d", typ_get_fraction_digits(typdef)); write_cyang_simple_str(scb, YANG_K_FRACTION_DIGITS, (const xmlChar *)buff, startindent, 0, TRUE); /* fall through to check range */ case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_FLOAT64: range = typ_get_crange_con(typdef); if (range && range->rangestr) { errinfo_set = ncx_errinfo_set(&range->range_errinfo); write_cyang_simple_str(scb, YANG_K_RANGE, range->rangestr, startindent, 2, !errinfo_set); if (errinfo_set) { write_cyang_errinfo(scb, cp, &range->range_errinfo, indent); ses_putstr_indent(scb, END_SEC, startindent); } } break; case NCX_BT_STRING: case NCX_BT_BINARY: range = typ_get_crange_con(typdef); if (range && range->rangestr) { errinfo_set = ncx_errinfo_set(&range->range_errinfo); write_cyang_simple_str(scb, YANG_K_LENGTH, range->rangestr, startindent, 2, !errinfo_set); if (errinfo_set) { write_cyang_errinfo(scb, cp, &range->range_errinfo, indent); ses_putstr_indent(scb, END_SEC, startindent); } } for (pat = typ_get_first_cpattern(typdef); pat != NULL; pat = typ_get_next_cpattern(pat)) { errinfo_set = ncx_errinfo_set(&pat->pat_errinfo); write_cyang_complex_str(scb, cp->tkc, &pat->pat_str, YANG_K_PATTERN, pat->pat_str, startindent, 1, !errinfo_set); if (errinfo_set) { write_cyang_errinfo(scb, cp, &pat->pat_errinfo, indent); ses_putstr_indent(scb, END_SEC, startindent); } } break; case NCX_BT_SLIST: break; case NCX_BT_LEAFREF: str = typ_get_leafref_path(typdef); if (str) { write_cyang_complex_str(scb, cp->tkc, typ_get_leafref_path_addr(typdef), YANG_K_PATH, str, startindent, 2, TRUE); } break; case NCX_BT_INSTANCE_ID: constrained_set = typ_get_constrained(typdef); write_cyang_simple_str(scb, YANG_K_REQUIRE_INSTANCE, (constrained_set) ? NCX_EL_TRUE : NCX_EL_FALSE, startindent, 0, TRUE); break; case NCX_BT_IDREF: idref = typ_get_idref(typdef); ses_putstr_indent(scb, YANG_K_BASE, startindent); ses_putchar(scb, ' '); if (idref->baseprefix) { write_cyang_extkw(scb, idref->baseprefix, idref->basename); } else { ses_putstr(scb, idref->basename); } ses_putchar(scb, ';'); break; default: break; } break; case NCX_CL_COMPLEX: SET_ERROR(ERR_INTERNAL_VAL); break; case NCX_CL_REF: SET_ERROR(ERR_INTERNAL_VAL); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* write_cyang_type_contents */ /******************************************************************** * FUNCTION write_cyang_type_clause * * Generate the YANG for the specified type clause * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * typdef == typ_def_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_type_clause (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, typ_def_t *typdef, int32 startindent) { write_cyang_type(scb, mod, typdef, startindent); if (typ_has_subclauses(typdef)) { ses_putstr(scb, START_SEC); write_cyang_type_contents(scb, mod, cp, typdef, startindent + ses_indent_count(scb)); ses_putstr_indent(scb, END_SEC, startindent); } else { ses_putchar(scb, ';'); } } /* write_cyang_type_clause */ /******************************************************************** * FUNCTION write_cyang_typedef * * Generate the YANG for 1 typedef * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * typ == typ_template_t to use * startindent == start indent count * first == TRUE if this is the first typedef at this indent level * FALSE if not the first typedef at this indent level **********************************************************************/ static void write_cyang_typedef (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, typ_template_t *typ, int32 startindent, boolean first) { int32 indent; indent = startindent + ses_indent_count(scb); write_cyang_id(scb, YANG_K_TYPEDEF, typ->name, startindent, FALSE, !first); /* appinfoQ */ write_cyang_appinfoQ(scb, mod, &typ->appinfoQ, indent); /* type field */ write_cyang_type_clause(scb, mod, cp, &typ->typdef, indent); /* units field */ if (typ->units) { write_cyang_simple_str(scb, YANG_K_UNITS, typ->units, indent, 2, TRUE); } /* default field */ if (typ->defval) { write_cyang_complex_str(scb, cp->tkc, &typ->defval, YANG_K_DEFAULT, typ->defval, indent, 2, TRUE); } /* status field */ write_cyang_status(scb, typ->status, indent); /* description field */ if (typ->descr) { write_cyang_complex_str(scb, cp->tkc, &typ->descr, YANG_K_DESCRIPTION, typ->descr, indent, 2, TRUE); } /* reference field */ if (typ->ref) { write_cyang_complex_str(scb, cp->tkc, &typ->ref, YANG_K_REFERENCE, typ->ref, indent, 2, TRUE); } /* end typedef clause */ ses_putstr_indent(scb, END_SEC, startindent); } /* write_cyang_typedef */ /******************************************************************** * FUNCTION write_cyang_typedefs * * Generate the YANG for the specified typedefQ * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * typedefQ == que of typ_template_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_typedefs (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *typedefQ, int32 startindent) { typ_template_t *typ; boolean first; if (dlq_empty(typedefQ)) { return; } if (typedefQ == &mod->typeQ) { write_cyang_banner_cmt(scb, mod, cp, (const xmlChar *)"typedefs", startindent); } first = TRUE; for (typ = (typ_template_t *)dlq_firstEntry(typedefQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { write_cyang_typedef(scb, mod, cp, typ, startindent, first); first = FALSE; } } /* write_cyang_typedefs */ /******************************************************************** * FUNCTION write_cyang_grouping * * Generate the YANG for 1 grouping * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * grp == grp_template_t to use * startindent == start indent count * first == TRUE if this is the first grouping at this indent level * FALSE if not the first grouping at this indent level **********************************************************************/ static void write_cyang_grouping (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const grp_template_t *grp, int32 startindent, boolean first) { int32 indent; boolean cooked; cooked = !cp->rawmode; indent = startindent + ses_indent_count(scb); if (cooked && !grp_has_typedefs(grp)) { return; } write_cyang_id(scb, YANG_K_GROUPING, grp->name, startindent, FALSE, !first); /* appinfoQ */ write_cyang_appinfoQ(scb, mod, &grp->appinfoQ, indent); /* status field */ write_cyang_status(scb, grp->status, indent); /* description field */ if (grp->descr) { write_cyang_complex_str(scb, cp->tkc, &grp->descr, YANG_K_DESCRIPTION, grp->descr, indent, 2, TRUE); } /* reference field */ if (grp->ref) { write_cyang_complex_str(scb, cp->tkc, &grp->ref, YANG_K_REFERENCE, grp->ref, indent, 2, TRUE); } write_cyang_typedefs(scb, mod, cp, &grp->typedefQ, indent); write_cyang_groupings(scb, mod, cp, &grp->groupingQ, indent); if (!cooked) { write_cyang_objects(scb, mod, cp, &grp->datadefQ, indent); } /* end grouping clause */ ses_putstr_indent(scb, END_SEC, startindent); /* end grouping comment */ write_cyang_endsec_cmt(scb, YANG_K_GROUPING, grp->name); } /* write_cyang_grouping */ /******************************************************************** * FUNCTION write_cyang_groupings * * Generate the YANG for the specified groupingQ * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * groupingQ == que of grp_template_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_groupings (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *groupingQ, int32 startindent) { const grp_template_t *grp; boolean needed, cooked, first; if (dlq_empty(groupingQ)) { return; } cooked = !cp->rawmode; /* groupings are only generated in cooked mode if they have * typedefs, and then just the typedefs are generated */ if (cooked) { needed = FALSE; for (grp = (const grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL && needed==FALSE; grp = (const grp_template_t *)dlq_nextEntry(grp)) { needed = grp_has_typedefs(grp); } if (!needed) { return; } } /* put comment for first grouping only */ if (groupingQ == &mod->groupingQ) { write_cyang_banner_cmt(scb, mod, cp, (const xmlChar *)"groupings", startindent); } first = TRUE; for (grp = (const grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL; grp = (const grp_template_t *)dlq_nextEntry(grp)) { write_cyang_grouping(scb, mod, cp, grp, startindent, first); first = FALSE; } } /* write_cyang_groupings */ /******************************************************************** * FUNCTION write_cyang_iffeature * * Generate the YANG for 1 if-feature statement * * INPUTS: * scb == session control block to use for writing * iffeature == ncx_iffeature_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_iffeature (ses_cb_t *scb, const ncx_iffeature_t *iffeature, int32 startindent) { ses_putstr_indent(scb, YANG_K_IF_FEATURE, startindent); ses_putchar(scb, ' '); if (iffeature->prefix) { ses_putstr(scb, iffeature->prefix); ses_putchar(scb, ':'); } ses_putstr(scb, iffeature->name); ses_putchar(scb, ';'); } /* write_cyang_iffeature */ /******************************************************************** * FUNCTION write_cyang_iffeatureQ * * Generate the YANG for a Q of if-feature statements * * INPUTS: * scb == session control block to use for writing * iffeatureQ == Q of ncx_iffeature_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_iffeatureQ (ses_cb_t *scb, const dlq_hdr_t *iffeatureQ, int32 startindent) { const ncx_iffeature_t *iffeature; for (iffeature = (const ncx_iffeature_t *) dlq_firstEntry(iffeatureQ); iffeature != NULL; iffeature = (const ncx_iffeature_t *) dlq_nextEntry(iffeature)) { write_cyang_iffeature(scb, iffeature, startindent); } } /* write_cyang_iffeatureQ */ /******************************************************************** * FUNCTION write_cyang_whenif * * Check then when-stmt and if-featureQ for an object * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * obj == object to check * indent = indent amount *********************************************************************/ static void write_cyang_whenif (ses_cb_t *scb, const yangdump_cvtparms_t *cp, const obj_template_t *obj, int32 indent) { const ncx_errinfo_t *errinfo; /* when-stmt? */ if (obj->when && obj->when->exprstr) { errinfo = &obj->when->errinfo; if (errinfo->descr || errinfo->ref) { write_cyang_complex_str(scb, cp->tkc, &obj->when->exprstr, YANG_K_WHEN, obj->when->exprstr, indent, 2, FALSE); write_cyang_errinfo(scb, cp, errinfo, indent + ses_indent_count(scb)); ses_putstr_indent(scb, END_SEC, indent); } else { write_cyang_complex_str(scb, cp->tkc, &obj->when->exprstr, YANG_K_WHEN, obj->when->exprstr, indent, 2, TRUE); } } /* if-feature-stmt* */ write_cyang_iffeatureQ(scb, &obj->iffeatureQ, indent); } /* write_cyang_whenif */ /******************************************************************** * FUNCTION write_cyang_sdr * * Write the status-stmt, description-stmt, and reference-stmt * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * obj == object to check * indent = indent amount *********************************************************************/ static void write_cyang_sdr (ses_cb_t *scb, const yangdump_cvtparms_t *cp, const obj_template_t *obj, int32 indent) { const xmlChar *str; /* status-stmt? (only written if not 'current' */ write_cyang_status(scb, obj_get_status(obj), indent); /* description-stmt? */ str = obj_get_description(obj); if (str) { write_cyang_complex_str(scb, cp->tkc, obj_get_description_addr(obj), YANG_K_DESCRIPTION, str, indent, 2, TRUE); } /* reference-stmt? */ str = obj_get_reference(obj); if (str) { write_cyang_complex_str(scb, cp->tkc, obj_get_reference_addr(obj), YANG_K_REFERENCE, str, indent, 2, TRUE); } } /* write_cyang_sdr */ /******************************************************************** * FUNCTION write_cyang_config_stmt * * Write the config-stmt * * INPUTS: * scb == session control block to use for writing * obj == object to check * indent = indent amount * force = force output even if not default *********************************************************************/ static void write_cyang_config_stmt (ses_cb_t *scb, const obj_template_t *obj, int32 indent, boolean force) { boolean flag; /* config-stmt, only if actually set to false */ if (obj->flags & OBJ_FL_CONFSET) { flag = (obj->flags & OBJ_FL_CONFIG); if (force || !flag) { write_cyang_simple_str(scb, YANG_K_CONFIG, (flag) ? NCX_EL_TRUE : NCX_EL_FALSE, indent, 0, TRUE); } } } /* write_cyang_config_stmt */ /******************************************************************** * FUNCTION write_cyang_mandatory_stmt * * Write the mandatory-stmt * * INPUTS: * scb == session control block to use for writing * obj == object to check * indent = indent amount * force == TRUE if the statement should be printed even * when not the default (needed for refine-stmt) *********************************************************************/ static void write_cyang_mandatory_stmt (ses_cb_t *scb, const obj_template_t *obj, int32 indent, boolean force) { boolean flag; /* mandatory field, only if actually set to true */ if (obj->flags & OBJ_FL_MANDSET) { flag = (obj->flags & OBJ_FL_MANDATORY); if (force || flag) { write_cyang_simple_str(scb, YANG_K_MANDATORY, (flag) ? NCX_EL_TRUE : NCX_EL_FALSE, indent, 0, TRUE); } } } /* write_cyang_mandatory_stmt */ /******************************************************************** * FUNCTION write_cyang_presence_stmt * * Write the presence-stmt * * INPUTS: * scb == session control block to use for writing * tkc == token chain to use * field == address of presence field * presence == presence string (may be NULL to skip) * indent = indent amount *********************************************************************/ static void write_cyang_presence_stmt (ses_cb_t *scb, tk_chain_t *tkc, void *field, const xmlChar *presence, int32 indent) { if (presence) { write_cyang_complex_str(scb, tkc, field, YANG_K_PRESENCE, presence, indent, 2, TRUE); } } /* write_cyang_presence_stmt */ /******************************************************************** * FUNCTION write_cyang_default_stmt * * Write the default-stmt * * INPUTS: * scb == session control block to use for writing * tkc == token chain to use * field == address of field containing defval * defval == default string (may be NULL to skip) * indent = indent amount *********************************************************************/ static void write_cyang_default_stmt (ses_cb_t *scb, tk_chain_t *tkc, const void *field, const xmlChar *defval, int32 indent) { if (defval != NULL) { write_cyang_complex_str(scb, tkc, field, YANG_K_DEFAULT, defval, indent, 2, TRUE); } } /* write_cyang_default_stmt */ /******************************************************************** * FUNCTION write_cyang_minmax * * Write the min-elements and/or max-elements stmts if needed * * INPUTS: * scb == session control block to use for writing * minset == TRUE if min-elements set * minval == min-elements value * maxset == TRUE if max-elements set * maxval == min-elements value * indent == indent amount *********************************************************************/ static void write_cyang_minmax (ses_cb_t *scb, boolean minset, uint32 minval, boolean maxset, uint32 maxval, int32 indent) { char buff[NCX_MAX_NUMLEN]; if (minset) { sprintf(buff, "%u", minval); write_cyang_simple_str(scb, YANG_K_MIN_ELEMENTS, (const xmlChar *)buff, indent, 0, TRUE); } if (maxset) { if (maxval == 0) { write_cyang_simple_str(scb, YANG_K_MAX_ELEMENTS, YANG_K_UNBOUNDED, indent, 0, TRUE); } else { sprintf(buff, "%u", maxval); write_cyang_simple_str(scb, YANG_K_MAX_ELEMENTS, (const xmlChar *)buff, indent, 0, TRUE); } } } /* write_cyang_minmax */ /******************************************************************** * FUNCTION write_cyang_unique_stmts * * Write the unique-stmts if needed * * INPUTS: * scb == session control block to use for writing * uniqueQ == Q of obj_unique_t to use * indent == indent amount *********************************************************************/ static void write_cyang_unique_stmts (ses_cb_t *scb, const dlq_hdr_t *uniqueQ, int32 indent) { const obj_unique_t *uni; const obj_unique_comp_t *unicomp, *nextunicomp; for (uni = (const obj_unique_t *)dlq_firstEntry(uniqueQ); uni != NULL; uni = (const obj_unique_t *)dlq_nextEntry(uni)) { ses_indent(scb, indent); ses_putstr(scb, YANG_K_UNIQUE); ses_putstr(scb, (const xmlChar *)" \""); for (unicomp = (const obj_unique_comp_t *) dlq_firstEntry(&uni->compQ); unicomp != NULL; unicomp = nextunicomp) { nextunicomp = (const obj_unique_comp_t *) dlq_nextEntry(unicomp); ses_putstr(scb, unicomp->xpath); if (nextunicomp) { ses_putstr(scb, (const xmlChar *)" "); } } ses_putstr(scb, (const xmlChar *)"\";"); } } /* write_cyang_unique_stmts */ /******************************************************************** * FUNCTION write_cyang_object * * Generate the YANG for 1 datadef * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * obj == obj_template_t to use * startindent == start indent count * first == TRUE if this is the first object at this indent level * FALSE if not the first object at this indent level * RETURNS: * status (usually NO_ERR or ERR_NCX_SKIPPED) **********************************************************************/ static status_t write_cyang_object (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, obj_template_t *obj, int32 startindent, boolean first) { obj_container_t *con; obj_leaf_t *leaf; obj_leaflist_t *leaflist; obj_list_t *list; obj_choice_t *choic; obj_case_t *cas; obj_uses_t *uses; obj_augment_t *aug; obj_rpc_t *rpc; obj_rpcio_t *rpcio; obj_notif_t *notif; obj_refine_t *refine; obj_key_t *key, *nextkey; int32 indent; boolean isempty, fullcase; if (obj_is_cloned(obj) && cp->rawmode) { /* skip cloned objects in 'raw' object view mode */ return ERR_NCX_SKIPPED; } if (cp->rawmode) { isempty = obj_is_empty(obj); } else { isempty = FALSE; } indent = startindent + ses_indent_count(scb); fullcase = !obj_is_short_case(obj); /* generate start of statement with the object type * except a short-form case-stmt */ if (fullcase && obj_has_name(obj) && obj->objtype != OBJ_TYP_RPCIO) { write_cyang_id(scb, obj_get_typestr(obj), obj_get_name(obj), startindent, isempty, !first); if (isempty) { return NO_ERR; } } switch (obj->objtype) { case OBJ_TYP_ANYXML: write_cyang_appinfoQ(scb, mod, &obj->appinfoQ, indent); write_cyang_whenif(scb, cp, obj, indent); write_cyang_musts(scb, cp, obj_get_mustQ(obj), indent); write_cyang_config_stmt(scb, obj, indent, FALSE); write_cyang_mandatory_stmt(scb, obj, indent, FALSE); write_cyang_sdr(scb, cp, obj, indent); ses_putstr_indent(scb, END_SEC, startindent); break; case OBJ_TYP_CONTAINER: con = obj->def.container; write_cyang_appinfoQ(scb, mod, &obj->appinfoQ, indent); write_cyang_whenif(scb, cp, obj, indent); write_cyang_musts(scb, cp, obj_get_mustQ(obj), indent); write_cyang_presence_stmt(scb, cp->tkc, obj_get_presence_string_field(obj), obj_get_presence_string(obj), indent); write_cyang_config_stmt(scb, obj, indent, FALSE); write_cyang_sdr(scb, cp, obj, indent); write_cyang_typedefs(scb, mod, cp, con->typedefQ, indent); write_cyang_groupings(scb, mod, cp, con->groupingQ, indent); write_cyang_objects(scb, mod, cp, con->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); write_cyang_endsec_cmt(scb, YANG_K_CONTAINER, con->name); break; case OBJ_TYP_LEAF: leaf = obj->def.leaf; write_cyang_appinfoQ(scb, mod, &obj->appinfoQ, indent); write_cyang_whenif(scb, cp, obj, indent); write_cyang_type_clause(scb, mod, cp, leaf->typdef, indent); if (leaf->units) { write_cyang_simple_str(scb, YANG_K_UNITS, leaf->units, indent, 2, TRUE); } write_cyang_musts(scb, cp, obj_get_mustQ(obj), indent); write_cyang_default_stmt(scb, cp->tkc, &leaf->defval, leaf->defval, indent); write_cyang_config_stmt(scb, obj, indent, FALSE); write_cyang_mandatory_stmt(scb, obj, indent, FALSE); write_cyang_sdr(scb, cp, obj, indent); ses_putstr_indent(scb, END_SEC, startindent); break; case OBJ_TYP_LEAF_LIST: leaflist = obj->def.leaflist; write_cyang_appinfoQ(scb, mod, &obj->appinfoQ, indent); write_cyang_whenif(scb, cp, obj, indent); write_cyang_type_clause(scb, mod, cp, leaflist->typdef, indent); if (leaflist->units) { write_cyang_simple_str(scb, YANG_K_UNITS, leaflist->units, indent, 2, TRUE); } write_cyang_musts(scb, cp, obj_get_mustQ(obj), indent); write_cyang_config_stmt(scb, obj, indent, FALSE); write_cyang_minmax(scb, leaflist->minset, leaflist->minelems, leaflist->maxset, leaflist->maxelems, indent); if (!leaflist->ordersys) { write_cyang_simple_str(scb, YANG_K_ORDERED_BY, YANG_K_USER, indent, 0, TRUE); } write_cyang_sdr(scb, cp, obj, indent); ses_putstr_indent(scb, END_SEC, startindent); break; case OBJ_TYP_LIST: list = obj->def.list; write_cyang_appinfoQ(scb, mod, &obj->appinfoQ, indent); write_cyang_whenif(scb, cp, obj, indent); write_cyang_musts(scb, cp, obj_get_mustQ(obj), indent); /* key field, manual generation to make links */ if (!dlq_empty(&list->keyQ)) { ses_indent(scb, indent); ses_putstr(scb, YANG_K_KEY); ses_putstr(scb, (const xmlChar *)" \""); for (key = obj_first_key(obj); key != NULL; key = nextkey) { nextkey = obj_next_key(key); ses_putstr(scb, obj_get_name(key->keyobj)); if (nextkey) { ses_putchar(scb, ' '); } } ses_putstr(scb, (const xmlChar *)"\";"); } write_cyang_unique_stmts(scb, &list->uniqueQ, indent); write_cyang_config_stmt(scb, obj, indent, FALSE); /* min-elements */ write_cyang_minmax(scb, list->minset, list->minelems, list->maxset, list->maxelems, indent); /* ordered-by field, only if non-default (user) */ if (!list->ordersys) { write_cyang_simple_str(scb, YANG_K_ORDERED_BY, YANG_K_USER, indent, 0, TRUE); } write_cyang_sdr(scb, cp, obj, indent); write_cyang_typedefs(scb, mod, cp, list->typedefQ, indent); write_cyang_groupings(scb, mod, cp, list->groupingQ, indent); write_cyang_objects(scb, mod, cp, list->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); write_cyang_endsec_cmt(scb, YANG_K_LIST, list->name); break; case OBJ_TYP_CHOICE: choic = obj->def.choic; write_cyang_appinfoQ(scb, mod, &obj->appinfoQ, indent); write_cyang_whenif(scb, cp, obj, indent); /* default case field */ write_cyang_default_stmt(scb, cp->tkc, &choic->defval, choic->defval, indent); write_cyang_mandatory_stmt(scb, obj, indent, FALSE); write_cyang_sdr(scb, cp, obj, indent); write_cyang_objects(scb, mod, cp, choic->caseQ, indent); ses_putstr_indent(scb, END_SEC, startindent); write_cyang_endsec_cmt(scb, YANG_K_CHOICE, choic->name); break; case OBJ_TYP_CASE: cas = obj->def.cas; if (fullcase) { write_cyang_appinfoQ(scb, mod, &obj->appinfoQ, indent); write_cyang_whenif(scb, cp, obj, indent); write_cyang_sdr(scb, cp, obj, indent); } write_cyang_objects(scb, mod, cp, cas->datadefQ, (fullcase) ? indent : startindent); if (fullcase) { ses_putstr_indent(scb, END_SEC, startindent); write_cyang_endsec_cmt(scb, YANG_K_CASE, cas->name); } break; case OBJ_TYP_USES: if (!cp->rawmode) { return ERR_NCX_SKIPPED; } uses = obj->def.uses; if (!first) { ses_putchar(scb, '\n'); } ses_indent(scb, startindent); ses_putstr(scb, YANG_K_USES); ses_putchar(scb, ' '); if (uses->prefix && xml_strcmp(uses->prefix, mod->prefix)) { write_cyang_extkw(scb, uses->prefix, uses->name); } else { ses_putstr(scb, uses->name); } if (uses->descr || uses->ref || (obj->when && obj->when->exprstr) || uses->status != NCX_STATUS_CURRENT || !dlq_empty(&obj->iffeatureQ) || !dlq_empty(uses->datadefQ) || !dlq_empty(&obj->appinfoQ)) { ses_putstr(scb, START_SEC); write_cyang_appinfoQ(scb, mod, &obj->appinfoQ, indent); write_cyang_whenif(scb, cp, obj, indent); write_cyang_sdr(scb, cp, obj, indent); write_cyang_objects(scb, mod, cp, uses->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); } else { ses_putchar(scb, ';'); } break; case OBJ_TYP_AUGMENT: if (!cp->rawmode) { return ERR_NCX_SKIPPED; } aug = obj->def.augment; if (!first) { ses_putchar(scb, '\n'); } write_cyang_complex_str(scb, cp->tkc, &aug->target, YANG_K_AUGMENT, aug->target, startindent, 0, (isempty || aug->targobj == NULL)); if (isempty || aug->targobj == NULL) { return NO_ERR; } write_cyang_appinfoQ(scb, mod, &obj->appinfoQ, indent); write_cyang_whenif(scb, cp, obj, indent); write_cyang_sdr(scb, cp, obj, indent); write_cyang_objects(scb, mod, cp, &aug->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); break; case OBJ_TYP_RPC: rpc = obj->def.rpc; write_cyang_appinfoQ(scb, mod, &obj->appinfoQ, indent); write_cyang_whenif(scb, cp, obj, indent); /* when-stmt NULL */ write_cyang_sdr(scb, cp, obj, indent); write_cyang_typedefs(scb, mod, cp, &rpc->typedefQ, indent); write_cyang_groupings(scb, mod, cp, &rpc->groupingQ, indent); write_cyang_objects(scb, mod, cp, &rpc->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); write_cyang_endsec_cmt(scb, YANG_K_RPC, rpc->name); break; case OBJ_TYP_RPCIO: rpcio = obj->def.rpcio; if (!dlq_empty(&rpcio->typedefQ) || !dlq_empty(&rpcio->groupingQ) || !dlq_empty(&rpcio->datadefQ) || !dlq_empty(&obj->appinfoQ)) { write_cyang_id(scb, obj_get_name(obj), NULL, startindent, FALSE, !first); write_cyang_appinfoQ(scb, mod, &obj->appinfoQ, indent); write_cyang_typedefs(scb, mod, cp, &rpcio->typedefQ, indent); write_cyang_groupings(scb, mod, cp, &rpcio->groupingQ, indent); write_cyang_objects(scb, mod, cp, &rpcio->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); } break; case OBJ_TYP_NOTIF: notif = obj->def.notif; write_cyang_appinfoQ(scb, mod, &obj->appinfoQ, indent); write_cyang_whenif(scb, cp, obj, indent); /* when-stmt NULL */ write_cyang_sdr(scb, cp, obj, indent); write_cyang_typedefs(scb, mod, cp, ¬if->typedefQ, indent); write_cyang_groupings(scb, mod, cp, ¬if->groupingQ, indent); write_cyang_objects(scb, mod, cp, ¬if->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); write_cyang_endsec_cmt(scb, YANG_K_NOTIFICATION, notif->name); break; case OBJ_TYP_REFINE: if (!cp->rawmode) { return ERR_NCX_SKIPPED; } refine = obj->def.refine; if (!first) { ses_putchar(scb, '\n'); } write_cyang_complex_str(scb, cp->tkc, &refine->target, YANG_K_REFINE, refine->target, startindent, 0, (isempty || refine->targobj == NULL)); if (isempty || refine->targobj == NULL) { return NO_ERR; } switch (refine->targobj->objtype) { case OBJ_TYP_ANYXML: /* must-stmt refine not in -07*/ write_cyang_musts(scb, cp, obj_get_mustQ(obj), indent); write_cyang_config_stmt(scb, obj, indent, TRUE); write_cyang_mandatory_stmt(scb, obj, indent, TRUE); write_cyang_sdr(scb, cp, obj, indent); break; case OBJ_TYP_CONTAINER: write_cyang_musts(scb, cp, obj_get_mustQ(obj), indent); write_cyang_presence_stmt(scb, cp->tkc, &refine->presence, refine->presence, indent); write_cyang_config_stmt(scb, obj, indent, TRUE); write_cyang_sdr(scb, cp, obj, indent); break; case OBJ_TYP_LEAF: write_cyang_musts(scb, cp, obj_get_mustQ(obj), indent); write_cyang_default_stmt(scb, cp->tkc, &refine->def, refine->def, indent); write_cyang_config_stmt(scb, obj, indent, TRUE); write_cyang_mandatory_stmt(scb, obj, indent, TRUE); write_cyang_sdr(scb, cp, obj, indent); break; case OBJ_TYP_LEAF_LIST: case OBJ_TYP_LIST: write_cyang_musts(scb, cp, obj_get_mustQ(obj), indent); write_cyang_config_stmt(scb, obj, indent, TRUE); write_cyang_minmax(scb, refine->minelems_tkerr.linenum != 0, refine->minelems, refine->maxelems_tkerr.linenum != 0, refine->maxelems, indent); write_cyang_sdr(scb, cp, obj, indent); break; case OBJ_TYP_CHOICE: write_cyang_default_stmt(scb, cp->tkc, &refine->def, refine->def, indent); write_cyang_config_stmt(scb, obj, indent, TRUE); write_cyang_mandatory_stmt(scb, obj, indent, TRUE); write_cyang_sdr(scb, cp, obj, indent); break; case OBJ_TYP_CASE: write_cyang_sdr(scb, cp, obj, indent); break; default: ; } ses_putstr_indent(scb, END_SEC, startindent); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return NO_ERR; } /* write_cyang_object */ /******************************************************************** * FUNCTION write_cyang_objects * * Generate the YANG for the specified datadefQ * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * datadefQ == que of obj_template_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_objects (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *datadefQ, int32 startindent) { obj_template_t *obj; status_t res; boolean first; if (dlq_empty(datadefQ)) { return; } if (datadefQ == &mod->datadefQ) { write_cyang_banner_cmt(scb, mod, cp, (const xmlChar *)"objects", startindent); } first = TRUE; for (obj = (obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (obj_is_hidden(obj)) { continue; } res = write_cyang_object(scb, mod, cp, obj, startindent, first); if (first && res == NO_ERR) { first = FALSE; } } } /* write_cyang_objects */ /******************************************************************** * FUNCTION write_cyang_extension * * Generate the YANG for 1 extension * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * ext == ext_template_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_extension (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const ext_template_t *ext, int32 startindent) { int32 indent; indent = startindent + ses_indent_count(scb); write_cyang_id(scb, YANG_K_EXTENSION, ext->name, startindent, FALSE, FALSE); write_cyang_appinfoQ(scb, mod, &ext->appinfoQ, indent); /* argument sub-clause */ if (ext->arg) { write_cyang_simple_str(scb, YANG_K_ARGUMENT, ext->arg, indent, 2, FALSE); write_cyang_simple_str(scb, YANG_K_YIN_ELEMENT, ext->argel ? NCX_EL_TRUE : NCX_EL_FALSE, indent + ses_indent_count(scb), 0, TRUE); ses_putstr_indent(scb, END_SEC, indent); } /* status field */ write_cyang_status(scb, ext->status, indent); /* description field */ if (ext->descr) { write_cyang_complex_str(scb, cp->tkc, &ext->descr, YANG_K_DESCRIPTION, ext->descr, indent, 2, TRUE); } /* reference field */ if (ext->ref) { write_cyang_complex_str(scb, cp->tkc, &ext->ref, YANG_K_REFERENCE, ext->ref, indent, 2, TRUE); } /* end extension clause */ ses_putstr_indent(scb, END_SEC, startindent); } /* write_cyang_extension */ /******************************************************************** * FUNCTION write_cyang_extensions * * Generate the YANG for the specified extensionQ * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * extensionQ == que of ext_template_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_extensions (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *extensionQ, int32 startindent) { const ext_template_t *ext; if (dlq_empty(extensionQ)) { return; } write_cyang_banner_cmt(scb, mod, cp, (const xmlChar *)"extensions", startindent); for (ext = (const ext_template_t *)dlq_firstEntry(extensionQ); ext != NULL; ext = (const ext_template_t *)dlq_nextEntry(ext)) { write_cyang_extension(scb, mod, cp, ext, startindent); } } /* write_cyang_extensions */ /******************************************************************** * FUNCTION write_cyang_identity * * Generate the YANG for 1 identity * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * identity == ncx_identity_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_identity (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const ncx_identity_t *identity, int32 startindent) { int32 indent; indent = startindent + ses_indent_count(scb); write_cyang_id(scb, YANG_K_IDENTITY, identity->name, startindent, FALSE, FALSE); write_cyang_appinfoQ(scb, mod, &identity->appinfoQ, indent); #if 0 /* base sub-clause */ if (identity->base) { ses_putstr_indent(scb, YANG_K_BASE, indent); ses_putchar(scb, ' '); write_cyang_extkw(scb, (identity->baseprefix) ? identity->baseprefix : ncx_get_mod_prefix(mod), identity->basename); ses_putchar(scb, ';'); } #endif /* status field */ write_cyang_status(scb, identity->status, indent); /* description field */ if (identity->descr) { write_cyang_complex_str(scb, cp->tkc, &identity->descr, YANG_K_DESCRIPTION, identity->descr, indent, 2, TRUE); } /* reference field */ if (identity->ref) { write_cyang_complex_str(scb, cp->tkc, &identity->ref, YANG_K_REFERENCE, identity->ref, indent, 2, TRUE); } /* end identity clause */ ses_putstr_indent(scb, END_SEC, startindent); } /* write_cyang_identity */ /******************************************************************** * FUNCTION write_cyang_identities * * Generate the YANG for the specified identityQ * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * identityQ == que of ncx_identity_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_identities (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *identityQ, int32 startindent) { const ncx_identity_t *identity; if (dlq_empty(identityQ)) { return; } write_cyang_banner_cmt(scb, mod, cp, (const xmlChar *)"identities", startindent); for (identity = (const ncx_identity_t *) dlq_firstEntry(identityQ); identity != NULL; identity = (const ncx_identity_t *) dlq_nextEntry(identity)) { write_cyang_identity(scb, mod, cp, identity, startindent); } } /* write_cyang_identities */ /******************************************************************** * FUNCTION write_cyang_feature * * Generate the YANG for 1 feature statement * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * feature == ncx_feature_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_feature (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const ncx_feature_t *feature, int32 startindent) { int32 indent; indent = startindent + ses_indent_count(scb); write_cyang_id(scb, YANG_K_FEATURE, feature->name, startindent, FALSE, FALSE); write_cyang_appinfoQ(scb, mod, &feature->appinfoQ, indent); /* optional Q of if-feature statements */ write_cyang_iffeatureQ(scb, &feature->iffeatureQ, indent); /* status field */ write_cyang_status(scb, feature->status, indent); /* description field */ if (feature->descr) { write_cyang_complex_str(scb, cp->tkc, &feature->descr, YANG_K_DESCRIPTION, feature->descr, indent, 2, TRUE); } /* reference field */ if (feature->ref) { write_cyang_complex_str(scb, cp->tkc, &feature->ref, YANG_K_REFERENCE, feature->ref, indent, 2, TRUE); } /* end feature clause */ ses_putstr_indent(scb, END_SEC, startindent); } /* write_cyang_feature */ /******************************************************************** * FUNCTION write_cyang_features * * Generate the YANG for the specified featureQ * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * featureQ == que of ncx_feature_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_features (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *featureQ, int32 startindent) { const ncx_feature_t *feature; if (dlq_empty(featureQ)) { return; } write_cyang_banner_cmt(scb, mod, cp, (const xmlChar *)"features", startindent); for (feature = (const ncx_feature_t *) dlq_firstEntry(featureQ); feature != NULL; feature = (const ncx_feature_t *) dlq_nextEntry(feature)) { write_cyang_feature(scb, mod, cp, feature, startindent); } } /* write_cyang_features */ /******************************************************************** * FUNCTION write_cyang_deviate * * Generate the YANG for 1 deviate statement * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * deviate == obj_deviate_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_deviate (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const obj_deviate_t *deviate, int32 startindent) { int32 indent; indent = startindent + ses_indent_count(scb); ses_indent(scb, startindent); ses_putstr(scb, YANG_K_DEVIATE); ses_putchar(scb, ' '); ses_putstr(scb, obj_get_deviate_arg(deviate->arg)); if (deviate->empty || deviate->arg == OBJ_DARG_NOT_SUPPORTED) { ses_putchar(scb, ';'); return; } /* go through the deviate record and generate all * sub-statements that are present */ ses_putstr(scb, START_SEC); write_cyang_appinfoQ(scb, mod, &deviate->appinfoQ, indent); /* type-stmt */ if (deviate->typdef) { write_cyang_type_clause(scb, mod, cp, deviate->typdef, indent); } /* units-stmt */ if (deviate->units) { write_cyang_simple_str(scb, YANG_K_UNITS, deviate->units, indent, 2, TRUE); } /* default-stmt */ write_cyang_default_stmt(scb, cp->tkc, &deviate->defval, deviate->defval, indent); /* config-stmt, only if actually set */ if (deviate->config_tkerr.linenum != 0) { write_cyang_simple_str(scb, YANG_K_CONFIG, (deviate->config) ? NCX_EL_TRUE : NCX_EL_FALSE, indent, 0, TRUE); } /* mandatory-stmt, only if actually set */ if (deviate->mandatory_tkerr.linenum != 0) { write_cyang_simple_str(scb, YANG_K_MANDATORY, (deviate->mandatory) ? NCX_EL_TRUE : NCX_EL_FALSE, indent, 0, TRUE); } write_cyang_minmax(scb, deviate->minelems_tkerr.linenum != 0, deviate->minelems, deviate->maxelems_tkerr.linenum != 0, deviate->maxelems, indent); write_cyang_musts(scb, cp, &deviate->mustQ, indent); write_cyang_unique_stmts(scb, &deviate->uniqueQ, indent); ses_putstr_indent(scb, END_SEC, startindent); } /* write_cyang_deviate */ /******************************************************************** * FUNCTION write_cyang_deviation * * Generate the YANG for 1 deviation statement * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * deviation == obj_deviation_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_deviation (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const obj_deviation_t *deviation, int32 startindent) { const obj_deviate_t *deviate; int32 indent; indent = startindent + ses_indent_count(scb); ses_indent(scb, startindent); ses_putstr(scb, YANG_K_DEVIATION); ses_putchar(scb, ' '); ses_putstr(scb, deviation->target); if (deviation->empty) { ses_putchar(scb, ';'); return; } ses_putstr(scb, START_SEC); write_cyang_appinfoQ(scb, mod, &deviation->appinfoQ, indent); /* description field */ if (deviation->descr) { write_cyang_complex_str(scb, cp->tkc, &deviation->descr, YANG_K_DESCRIPTION, deviation->descr, indent, 2, TRUE); } /* reference field */ if (deviation->ref) { write_cyang_complex_str(scb, cp->tkc, &deviation->ref, YANG_K_REFERENCE, deviation->ref, indent, 2, TRUE); } /* 0 or more deviate-stmts */ for (deviate = (const obj_deviate_t *) dlq_firstEntry(&deviation->deviateQ); deviate != NULL; deviate = (const obj_deviate_t *) dlq_nextEntry(deviate)) { write_cyang_deviate(scb, mod, cp, deviate, indent); } ses_putstr_indent(scb, END_SEC, startindent); } /* write_cyang_deviation */ /******************************************************************** * FUNCTION write_cyang_deviations * * Generate the YANG for the specified deviationQ * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * deviationQ == que of obj_deviation_t to use * startindent == start indent count * *********************************************************************/ static void write_cyang_deviations (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *deviationQ, int32 startindent) { const obj_deviation_t *deviation; if (dlq_empty(deviationQ)) { return; } write_cyang_banner_cmt(scb, mod, cp, (const xmlChar *)"deviations", startindent); for (deviation = (const obj_deviation_t *) dlq_firstEntry(deviationQ); deviation != NULL; deviation = (const obj_deviation_t *) dlq_nextEntry(deviation)) { write_cyang_deviation(scb, mod, cp, deviation, startindent); } } /* write_cyang_deviations */ /******************************************************************** * FUNCTION write_cyang_import * * Generate the YANG for an import statement * * INPUTS: * scb == session control block to use for writing * mod == module in progress * modprefix == module prefix value for import * modname == module name to use in import clause * modrevision == module revision date (may be NULL) * appinfoQ == import appinfo Q (may be NULL) * indent == start indent count * *********************************************************************/ static void write_cyang_import (ses_cb_t *scb, const ncx_module_t *mod, const xmlChar *modprefix, const xmlChar *modname, const xmlChar *modrevision, const dlq_hdr_t *appinfoQ, int32 indent) { ses_indent(scb, indent); ses_putstr(scb, YANG_K_IMPORT); ses_putchar(scb, ' '); ses_putstr(scb, modname); ses_putstr(scb, START_SEC); if (appinfoQ) { write_cyang_appinfoQ(scb, mod, appinfoQ, indent + ses_indent_count(scb)); } write_cyang_simple_str(scb, YANG_K_PREFIX, modprefix, indent + ses_indent_count(scb), 0, TRUE); if (modrevision) { write_cyang_simple_str(scb, YANG_K_REVISION_DATE, modrevision, indent + ses_indent_count(scb), 2, TRUE); } ses_putstr_indent(scb, END_SEC, indent); } /* write_cyang_import */ /******************************************************************** * FUNCTION write_cyang_header * * Generate the YANG for the module header info * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * * OUTPUTS: * current indent count will be ses_indent_count(scb) upon exit *********************************************************************/ static void write_cyang_header (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp) { const ncx_import_t *imp; const yang_import_ptr_t *impptr; const ncx_include_t *inc; const ncx_revhist_t *rev; char buff[NCX_MAX_NUMLEN]; int indent; /* [sub]module name { */ if (mod->ismod) { ses_putstr(scb, YANG_K_MODULE); } else { ses_putstr(scb, YANG_K_SUBMODULE); } ses_putchar(scb, ' '); ses_putstr(scb, mod->name); ses_putstr(scb, START_SEC); ses_putchar(scb, '\n'); /* set indent count to one level */ indent = ses_indent_count(scb); /* yang-version */ sprintf(buff, "%u", mod->langver); write_cyang_simple_str(scb, YANG_K_YANG_VERSION, (const xmlChar *)buff, indent, 0, TRUE); ses_putchar(scb, '\n'); /* namespace or belongs-to */ if (mod->ismod) { write_cyang_complex_str(scb, cp->tkc, &mod->ns, YANG_K_NAMESPACE, mod->ns, indent, 2, TRUE); ses_putchar(scb, '\n'); } else { write_cyang_complex_str(scb, cp->tkc, &mod->belongs, YANG_K_BELONGS_TO, mod->belongs, indent, 0, FALSE); } if (mod->ismod) { write_cyang_simple_str(scb, YANG_K_PREFIX, mod->prefix, indent, 0, TRUE); } else { write_cyang_simple_str(scb, YANG_K_PREFIX, mod->prefix, indent + indent, 0, TRUE); ses_putstr_indent(scb, (const xmlChar *)"}", indent); } /* blank line */ ses_putchar(scb, '\n'); /* imports section */ if (cp->unified) { for (impptr = (const yang_import_ptr_t *) dlq_firstEntry(&mod->saveimpQ); impptr != NULL; impptr = (const yang_import_ptr_t *) dlq_nextEntry(impptr)) { /* the appinfoQ info is not saved in unified mode ouput!! */ write_cyang_import(scb, mod, impptr->modprefix, impptr->modname, impptr->revision, NULL, indent); } if (!dlq_empty(&mod->saveimpQ)) { ses_putchar(scb, '\n'); } } else { for (imp = (const ncx_import_t *) dlq_firstEntry(&mod->importQ); imp != NULL; imp = (const ncx_import_t *) dlq_nextEntry(imp)) { write_cyang_import(scb, mod, imp->prefix, imp->module, imp->revision, &imp->appinfoQ, indent); } if (!dlq_empty(&mod->importQ)) { ses_putchar(scb, '\n'); } } /* includes section */ if (!cp->unified) { for (inc = (const ncx_include_t *) dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (const ncx_include_t *)dlq_nextEntry(inc)) { ses_putstr_indent(scb, YANG_K_INCLUDE, indent); ses_putchar(scb, ' '); ses_putstr(scb, inc->submodule); if (inc->revision || !dlq_empty(&inc->appinfoQ)) { ses_putstr(scb, START_SEC); write_cyang_appinfoQ(scb, mod, &inc->appinfoQ, indent + ses_indent_count(scb)); if (inc->revision) { write_cyang_simple_str(scb, YANG_K_REVISION_DATE, inc->revision, indent + ses_indent_count(scb), 2, TRUE); } ses_putstr_indent(scb, END_SEC, indent); } else { ses_putchar(scb, ';'); } } if (!dlq_empty(&mod->includeQ)) { ses_putchar(scb, '\n'); } } /* organization */ if (mod->organization) { write_cyang_complex_str(scb, cp->tkc, &mod->organization, YANG_K_ORGANIZATION, mod->organization, indent, 2, TRUE); ses_putchar(scb, '\n'); } /* contact */ if (mod->contact_info) { write_cyang_complex_str(scb, cp->tkc, &mod->contact_info, YANG_K_CONTACT, mod->contact_info, indent, 2, TRUE); ses_putchar(scb, '\n'); } /* description */ if (mod->descr) { write_cyang_complex_str(scb, cp->tkc, &mod->descr, YANG_K_DESCRIPTION, mod->descr, indent, 2, TRUE); ses_putchar(scb, '\n'); } /* reference */ if (mod->ref) { write_cyang_complex_str(scb, cp->tkc, &mod->ref, YANG_K_REFERENCE, mod->ref, indent, 2, TRUE); ses_putchar(scb, '\n'); } /* revision history section */ for (rev = (const ncx_revhist_t *) dlq_firstEntry(&mod->revhistQ); rev != NULL; rev = (const ncx_revhist_t *)dlq_nextEntry(rev)) { write_cyang_simple_str(scb, YANG_K_REVISION, rev->version, indent, 2, FALSE); if (rev->descr != NULL) { write_cyang_complex_str(scb, cp->tkc, &rev->descr, YANG_K_DESCRIPTION, rev->descr, indent + ses_indent_count(scb), 2, TRUE); } else { write_cyang_simple_str(scb, YANG_K_DESCRIPTION, EMPTY_STRING, indent + ses_indent_count(scb), 2, TRUE); } if (rev->ref) { write_cyang_complex_str(scb, cp->tkc, &rev->ref, YANG_K_REFERENCE, rev->ref, indent + ses_indent_count(scb), 2, TRUE); } ses_putstr_indent(scb, END_SEC, indent); } if (!dlq_empty(&mod->revhistQ)) { ses_putchar(scb, '\n'); } } /* write_cyang_header */ /******************************************************************** * FUNCTION write_cyang_module * * Generate the module start and header * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * *********************************************************************/ static void write_cyang_module (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp) { const yang_node_t *node; const yang_stmt_t *stmt; boolean stmtmode; write_cyang_header(scb, mod, cp); /* if the top-level statement order was saved, it was only for * the YANG_PT_TOP module, and none of the sub-modules */ if (cp->unified) { stmtmode = FALSE; } else { stmtmode = dlq_empty(&mod->stmtQ) ? FALSE : TRUE; } /* TBD: need a better way to generate all the top-level * extensions. This approach is broken because it gathers * them and puts them at the start, after the header * the relative order with top-level stmts is not preserved * * will need to check line numbers as other constructs * are generated to find any extensions that 'fit' * in particuolar line number ranges */ write_cyang_appinfoQ(scb, mod, &mod->appinfoQ, cp->indent); if (cp->unified && mod->ismod) { for (node = (const yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_cyang_appinfoQ(scb, node->submod, &node->submod->appinfoQ, cp->indent); } } } /* 1) features */ if (!stmtmode) { write_cyang_features(scb, mod, cp, &mod->featureQ, 2*cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_cyang_features(scb, node->submod, cp, &node->submod->featureQ, 2*cp->indent); } } } /* 2) identities */ if (!stmtmode) { write_cyang_identities(scb, mod, cp, &mod->identityQ, cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_cyang_identities(scb, node->submod, cp, &node->submod->identityQ, cp->indent); } } } /* 3) typedefs */ if (!stmtmode) { write_cyang_typedefs(scb, mod, cp, &mod->typeQ, cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_cyang_typedefs(scb, node->submod, cp, &node->submod->typeQ, cp->indent); } } } /* 4) groupings */ if (!stmtmode) { write_cyang_groupings(scb, mod, cp, &mod->groupingQ, cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_cyang_groupings(scb, node->submod, cp, &node->submod->groupingQ, cp->indent); } } } /* 5) extensions */ if (!stmtmode) { write_cyang_extensions(scb, mod, cp, &mod->extensionQ, cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_cyang_extensions(scb, node->submod, cp, &node->submod->extensionQ, cp->indent); } } } /* 6) objects */ if (!stmtmode) { write_cyang_objects(scb, mod, cp, &mod->datadefQ, cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_cyang_objects(scb, node->submod, cp, &node->submod->datadefQ, cp->indent); } } } /* 7) deviations */ if (!stmtmode) { write_cyang_deviations(scb, mod, cp, &mod->deviationQ, cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_cyang_deviations(scb, node->submod, cp, &node->submod->deviationQ, cp->indent); } } } /* check statement mode on top-level module only */ if (stmtmode) { for (stmt = (const yang_stmt_t *) dlq_firstEntry(&mod->stmtQ); stmt != NULL; stmt = (const yang_stmt_t *)dlq_nextEntry(stmt)) { switch (stmt->stmttype) { case YANG_ST_NONE: SET_ERROR(ERR_INTERNAL_VAL); break; case YANG_ST_TYPEDEF: write_cyang_typedef(scb, mod, cp, stmt->s.typ, cp->indent, FALSE); break; case YANG_ST_GROUPING: write_cyang_grouping(scb, mod, cp, stmt->s.grp, cp->indent, FALSE); break; case YANG_ST_EXTENSION: write_cyang_extension(scb, mod, cp, stmt->s.ext, cp->indent); break; case YANG_ST_OBJECT: write_cyang_object(scb, mod, cp, stmt->s.obj, cp->indent, FALSE); break; case YANG_ST_IDENTITY: write_cyang_identity(scb, mod, cp, stmt->s.identity, cp->indent); break; case YANG_ST_FEATURE: write_cyang_feature(scb, mod, cp, stmt->s.feature, cp->indent); break; case YANG_ST_DEVIATION: write_cyang_deviation(scb, mod, cp, stmt->s.deviation, cp->indent); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } } /* end module */ ses_putstr(scb, (const xmlChar *)"\n}\n"); } /* write_cyang_module */ /********* E X P O R T E D F U N C T I O N S **************/ /******************************************************************** * FUNCTION cyang_convert_module * * Generate Canonical YANG from the specified module(s) * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * * RETURNS: * status *********************************************************************/ status_t cyang_convert_module (const yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb) { const ncx_module_t *mod; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { return SET_ERROR(ERR_NCX_MOD_NOT_FOUND); } write_cyang_module(scb, mod, cp); return NO_ERR; } /* cyang_convert_module */ /* END file cyang.c */ yuma123_2.14/netconf/src/ydump/yangdump_util.h0000664000175000017500000001061014770023131021534 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangdump_util #define _H_yangdump_util /* FILE: yangdump_util.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* YANGDUMP utilities ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 23-oct-09 abb Begun */ #ifndef _H_ses #include "ses.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define COPYRIGHT_HEADER (const xmlChar *)\ "\n * Copyright (c) 2008-2012, Andy Bierman, All Rights Reserved.\ \n *\ \n * Unless required by applicable law or agreed to in writing,\ \n * software distributed under the License is distributed on an\ \n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\ \n * KIND, either express or implied. See the License for the\ \n * specific language governing permissions and limitations\ \n * under the License.\ \n *" #define PY_CODING (const xmlChar *)"# -*- coding: utf-8 -*-" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * write_banner * * Write the yangdump startup banner * *********************************************************************/ extern void write_banner (void); /******************************************************************** * write_banner_session * * Write the yangdump startup banner to a session * * INPUTS: * scb == session control block to use * *********************************************************************/ extern void write_banner_session (ses_cb_t *scb); /******************************************************************** * write_banner_session_ex * * Write the yangdump startup banner to a session * * INPUTS: * scb == session control block to use * wcopy == TRUE if the copyright string should be used * FALSE if not *********************************************************************/ extern void write_banner_session_ex (ses_cb_t *scb, boolean wcopy); /******************************************************************** * FUNCTION find_reference * * Find the next 'RFC' or 'draft-' string in the buffer * Find the end of the RFC or draft name * * INPUTS: * buffer == buffer containing reference clause to use * ref == address of return start pointer * reflen == addredd of return 'ref' length * * OUTPUTS: * *ref == address of start of reference word * == NULL if no RFC xxxx of draft- found * *reflen == number of chars in *ref, or 0 if *ref is NULL * * RETURNS: * total number of chars processed *********************************************************************/ extern uint32 find_reference (const xmlChar *buffer, const xmlChar **ref, uint32 *reflen); /******************************************************************** * FUNCTION print_subtree_banner * * Print the banner for the next module starting when * multiple modules are being processed by yangdump * * INPUTS: * cp == conversion parms to use * mod == module to use * *********************************************************************/ extern void print_subtree_banner (yangdump_cvtparms_t *cp, ncx_module_t *mod, ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangdump_util */ yuma123_2.14/netconf/src/ydump/yangtree.c0000664000175000017500000003655014770023131020477 0ustar vladimirvladimir /* * Copyright (c) 2018, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangtree.c Generate YANG tree output for a specific YANG module /******************************************************************** * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "c_util.h" #include "dlq.h" #include "ext.h" #include "grp.h" #include "ncx.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "py_util.h" #include "ses.h" #include "yangtree.h" #include "status.h" #include "tstamp.h" #include "typ.h" #include "xmlns.h" #include "xml_util.h" #include "xml_val.h" #include "xpath.h" #include "xsd_util.h" #include "yang.h" #include "yangconst.h" #include "yangdump.h" #include "yangdump_util.h" /* if the object is input it checks if the following output is empty of children or not */ boolean is_last_non_empty_rpcio(obj_template_t* obj) { obj_template_t* prev_obj=obj; obj_template_t* next_obj; if(obj->objtype!=OBJ_TYP_RPCIO) { return FALSE; } while(next_obj=(obj_template_t *)dlq_nextEntry(prev_obj)) { if(next_obj->objtype==OBJ_TYP_RPCIO) { dlq_hdr_t* ccq=obj_get_datadefQ(next_obj); if(ccq && dlq_firstEntry(ccq)) { /*do not print empty input and output*/ return FALSE; } } prev_obj = next_obj; } return TRUE; } boolean is_last_non_uses(obj_template_t* obj) { obj_template_t* prev_obj=obj; obj_template_t* next_obj; while(next_obj=(obj_template_t *)dlq_nextEntry(prev_obj)) { if(next_obj->objtype!=OBJ_TYP_USES) { return FALSE; } prev_obj = next_obj; } return TRUE; } boolean is_last_data(obj_template_t* obj) { obj_template_t* prev_obj=obj; obj_template_t* next_obj; while(next_obj=(obj_template_t *)dlq_nextEntry(prev_obj)) { if(next_obj->objtype==OBJ_TYP_CONTAINER || next_obj->objtype==OBJ_TYP_LEAF || next_obj->objtype==OBJ_TYP_LIST) { return FALSE; } prev_obj = next_obj; } return TRUE; } boolean is_last_rpc(obj_template_t* obj) { obj_template_t* prev_obj=obj; obj_template_t* next_obj; while(next_obj=(obj_template_t *)dlq_nextEntry(prev_obj)) { if(next_obj->objtype==OBJ_TYP_RPC) { return FALSE; } prev_obj = next_obj; } return TRUE; } boolean is_last_notification(obj_template_t* obj) { obj_template_t* prev_obj=obj; obj_template_t* next_obj; while(next_obj=(obj_template_t *)dlq_nextEntry(prev_obj)) { if(next_obj->objtype==OBJ_TYP_NOTIF) { return FALSE; } prev_obj = next_obj; } return TRUE; } unsigned int get_child_name_width_max(obj_template_t *obj, boolean from_top) { obj_template_t* chobj; dlq_hdr_t* childdatadefQ; unsigned int len_max; childdatadefQ = obj_get_datadefQ(obj); assert(childdatadefQ); len_max=0; if(!from_top && (obj->objtype == OBJ_TYP_CHOICE || obj->objtype == OBJ_TYP_CASE)) { return get_child_name_width_max(obj->parent, FALSE); } for (chobj = (obj_template_t *)dlq_firstEntry(childdatadefQ); chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { unsigned int len; if(chobj->objtype == OBJ_TYP_USES) { continue; } else if(chobj->objtype == OBJ_TYP_CHOICE || chobj->objtype == OBJ_TYP_CASE) { len = get_child_name_width_max(chobj, TRUE); } else { len = strlen(obj_get_name(chobj)); } if(len_max*/ if(!dlq_empty(&obj->iffeatureQ)){ ncx_iffeature_t *iff; printf(" {"); for (iff = (ncx_iffeature_t *) dlq_firstEntry(&obj->iffeatureQ); iff != NULL; iff = (ncx_iffeature_t *)dlq_nextEntry(iff)) { if(iff->tkerr.mod!=obj->tkerr.mod) { printf("%s:%s",iff->tkerr.mod->prefix,iff->name); } else { printf("%s",iff->name); } printf("%s",dlq_nextEntry(iff)?" ":""); } printf("}?"); } } void print_data_obj(obj_template_t *obj, char* line_prefix) { obj_template_t* chobj; dlq_hdr_t* childdatadefQ; int j; unsigned int name_width_max; char* child_line_prefix; childdatadefQ = obj_get_datadefQ(obj); if (!childdatadefQ) { return; } name_width_max = get_child_name_width_max(obj, FALSE); for (chobj = (obj_template_t *)dlq_firstEntry(childdatadefQ); chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { boolean check_for_subnodes; unsigned int name_width_cur=0; if(chobj->objtype==OBJ_TYP_USES) { continue; } if(chobj->objtype==OBJ_TYP_RPCIO) { dlq_hdr_t* ccq=obj_get_datadefQ(chobj); if(!ccq || !dlq_firstEntry(ccq)) { /*do not print empty input and output*/ continue; } } printf("%s",line_prefix); /**/ printf("%s--",get_status_letter(chobj)); /**/ if(chobj->objtype!=OBJ_TYP_CASE) { if(obj_in_rpc(chobj) || obj_in_rpc_reply(chobj)) { printf("%s ", obj_in_rpc(chobj)?"-w":"ro"); } else if(chobj->objtype==OBJ_TYP_RPC /*should be OBJ_TYP_ACTION*/) { printf("%s ", "-x"); } else if(chobj->objtype==OBJ_TYP_NOTIF) { printf("%s ", "-n"); } else { printf("%s ", obj_get_config_flag(chobj)?"rw":"ro"); } } /**/ if(chobj->objtype==OBJ_TYP_CHOICE) { name_width_cur+=printf("(%s)", obj_get_name(chobj)); } else if(chobj->objtype==OBJ_TYP_CASE) { name_width_cur+=printf(":(%s)", obj_get_name(chobj)); } else { name_width_cur+=printf("%s", obj_get_name(chobj)); } /**/ if((chobj->objtype == OBJ_TYP_LEAF || chobj->objtype == OBJ_TYP_CHOICE || chobj->objtype == OBJ_TYP_ANYDATA || chobj->objtype == OBJ_TYP_ANYXML) && !obj_is_mandatory(chobj)) { /* ? for an optional leaf, choice, anydata or anyxml */ name_width_cur+=printf("?"); } else if((chobj->objtype == OBJ_TYP_CONTAINER) && !obj_is_np_container(chobj)) { /* ! for a presence container */ name_width_cur+=printf("!"); } else if((chobj->objtype == OBJ_TYP_LEAF_LIST)) { /* * for a leaf-list or list */ name_width_cur+=printf("*"); } else if(chobj->objtype == OBJ_TYP_LIST) { /* [] for a list's keys */ obj_key_t *key; printf("* ["); for (key = obj_first_key(chobj); key != NULL; key = obj_next_key(key)) { printf("%s%s",obj_get_name(key->keyobj),obj_next_key(key)?" ":""); } printf("]"); } /* / for a top-level data node in a mounted module */ /* @ for a top-level data node in a parent referenced module */ if(chobj->objtype == OBJ_TYP_LIST || chobj->objtype == OBJ_TYP_CONTAINER || chobj->objtype == OBJ_TYP_CHOICE || chobj->objtype == OBJ_TYP_CASE || chobj->objtype == OBJ_TYP_RPCIO || chobj->objtype == OBJ_TYP_RPC /*should be OBJ_TYP_ACTION*/ || chobj->objtype == OBJ_TYP_NOTIF) { check_for_subnodes=TRUE; } else { check_for_subnodes=FALSE; /*type alignment - line += "%s %-*s %s" % (flags, width+1, name, t)*/ for(j=0;j<(int)name_width_max+1-(int)name_width_cur;j++) { printf(" "); } printf(" "); /**/ { typ_def_t * typdef; typdef = obj_get_typdef(chobj); if(typdef && typdef->typenamestr) { if(typdef->prefix) { printf(" %s:%s", typdef->prefix, typdef->typenamestr); } else { if(0==strcmp("leafref",typdef->typenamestr)) { printf(" -> %s", typdef->def.simple.xleafref->exprstr); } else { printf(" %s", typdef->typenamestr); } } } else if(chobj->objtype==OBJ_TYP_ANYXML) { printf(" %s", ""); } else if(chobj->objtype==OBJ_TYP_ANYDATA) { printf(" %s", ""); } } } // printf(obj_get_type_name(chobj)); // printf(" %s",obj_get_basetype(obj)); /**/ print_feature_deps(chobj); if(check_for_subnodes) { child_line_prefix=malloc(strlen(line_prefix)+3+1); if(!is_last_non_uses(chobj) && !is_last_non_empty_rpcio(chobj)) { sprintf(child_line_prefix, "%s| ",line_prefix); } else { sprintf(child_line_prefix, "%s ",line_prefix); } print_data_obj(chobj, child_line_prefix); free(child_line_prefix); } } } void print_data(const ncx_module_t *mod) { obj_template_t* obj; if (dlq_empty(&mod->datadefQ)) { return; } for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || obj_is_cli(obj) || obj_is_abstract(obj) || obj->objtype==OBJ_TYP_AUGMENT || obj->objtype==OBJ_TYP_RPC || obj->objtype==OBJ_TYP_NOTIF) { continue; } printf("\n %s--",get_status_letter(obj)); // printf("\n +--"); printf("%s ", obj_get_config_flag(obj)?"rw":"ro"); printf("%s", obj_get_name(obj)); if((obj->objtype == OBJ_TYP_CONTAINER) && !obj_is_np_container(obj)) { /* ! for a presence container */ printf("!"); } print_feature_deps(obj); if(!is_last_data(obj)) { print_data_obj(obj, "\n | "); } else { print_data_obj(obj, "\n "); } } } void print_augmentations(const ncx_module_t *mod) { obj_template_t* obj; if (dlq_empty(&mod->datadefQ)) { return; } for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (obj->objtype!=OBJ_TYP_AUGMENT) { continue; } printf("\n augment %s:", obj->def.augment->target); print_data_obj(obj, "\n "); } } void print_rpcs(const ncx_module_t *mod) { obj_template_t* obj; unsigned int cnt=0; if (dlq_empty(&mod->datadefQ)) { return; } for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (obj->objtype!=OBJ_TYP_RPC) { continue; } if(cnt==0) { printf("\n rpcs:"); } printf("\n %s---x %s", get_status_letter(obj), obj_get_name(obj)); print_feature_deps(obj); if(!is_last_rpc(obj)) { print_data_obj(obj, "\n | "); } else { print_data_obj(obj, "\n "); } cnt++; } } void print_notifications(const ncx_module_t *mod) { obj_template_t* obj; unsigned int cnt=0; if (dlq_empty(&mod->datadefQ)) { return; } for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (obj->objtype!=OBJ_TYP_NOTIF) { continue; } if(cnt==0) { printf("\n notifications:"); } printf("\n %s---n %s", get_status_letter(obj), obj_get_name(obj)); print_feature_deps(obj); if(!is_last_notification(obj)) { print_data_obj(obj, "\n | "); } else { print_data_obj(obj, "\n "); } cnt++; } } #if 0 if (obj->objtype == OBJ_TYP_NOTIF) { return TRUE; } #endif /********* E X P O R T E D F U N C T I O N S **************/ /******************************************************************** * FUNCTION yangtree_convert_module * * Convert the YANG module to YANG tree format * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == convert parms struct to use * scb == session to use for output * * RETURNS: * status *********************************************************************/ status_t yangtree_convert_module(const yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb) { const ncx_module_t *mod; ncx_include_t *inc; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { return SET_ERROR(ERR_NCX_MOD_NOT_FOUND); } //ses_putstr(scb, "module: "); if (dlq_empty(&mod->datadefQ)) { return NO_ERR; } printf("module: %s", mod->name); /*draft-ietf-netmod-yang-tree-diagrams-04 sec.2*/ print_data(mod); /* check for submodule match */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { ncx_module_t *submod = inc->submod; print_data(submod); } print_augmentations(mod); /* check for submodule match */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { ncx_module_t *submod = inc->submod; print_augmentations(submod); } print_rpcs(mod); /* check for submodule match */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { ncx_module_t *submod = inc->submod; print_rpcs(submod); } print_notifications(mod); /* check for submodule match */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { ncx_module_t *submod = inc->submod; print_notifications(submod); } #if 0 print_groupings(); print_yangdata(); #endif return NO_ERR; } /* yangtree_convert_module */ /* END file yangtree.c */ yuma123_2.14/netconf/src/ydump/xsd_util.h0000664000175000017500000005326214770023131020520 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xsd_util #define _H_xsd_util /* FILE: xsd_util.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Utility functions for converting NCX modules to XSD format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24-nov-06 abb Begun; split from xsd.c */ #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define XSD_ABSTRACT (const xmlChar *)"abstract" #define XSD_AF_DEF (const xmlChar *)"attributeFormDefault" #define XSD_ANNOTATION (const xmlChar *)"annotation" #define XSD_ANY (const xmlChar *)"any" #define XSD_ANY_TYPE (const xmlChar *)"anyType" #define XSD_APP_SUFFIX (const xmlChar *)"AppType" #define XSD_ATTRIBUTE (const xmlChar *)"attribute" #define XSD_BANNER_0 (const xmlChar *)"Converted from NCX file '" #define XSD_BANNER_0Y (const xmlChar *)"Converted from YANG file '" #define XSD_BANNER_0END (const xmlChar *)"' by yangdump version " #define XSD_BANNER_1 (const xmlChar *)"\n\nModule: " #define XSD_BANNER_1S (const xmlChar *)"\n\nSubmodule: " #define XSD_BANNER_1B (const xmlChar *)"\nBelongs to module: " #define XSD_BANNER_2 (const xmlChar *)"\nOrganization: " #define XSD_BANNER_3 (const xmlChar *)"\nVersion: " #define XSD_BANNER_4 (const xmlChar *)"\nCopyright: " #define XSD_BANNER_5 (const xmlChar *)"\nContact: " #define XSD_BASE (const xmlChar *)"base" #define XSD_BASE64 (const xmlChar *)"base64Binary" #define XSD_BT (const xmlChar *)"BT" #define XSD_CPX_CON (const xmlChar *)"complexContent" #define XSD_CPX_TYP (const xmlChar *)"complexType" #define XSD_DATA_INLINE (const xmlChar *)"dataInlineType" #define XSD_DOCUMENTATION (const xmlChar *)"documentation" #define XSD_DRAFT_URL \ (const xmlChar *)"http://www.ietf.org/internet-drafts/" #define XSD_EF_DEF (const xmlChar *)"elementFormDefault" #define XSD_ELEMENT (const xmlChar *)"element" #define XSD_EN (const xmlChar *)"en" #define XSD_EXTENSION (const xmlChar *)"extension" #define XSD_ENUMERATION (const xmlChar *)"enumeration" #define XSD_FALSE (const xmlChar *)"false" #define XSD_FIELD (const xmlChar *)"field" #define XSD_FIXED (const xmlChar *)"fixed" #define XSD_GROUP (const xmlChar *)"group" #define XSD_HEX_BINARY (const xmlChar *)"hexBinary" #define XSD_ITEMTYPE (const xmlChar *)"itemType" #define XSD_KEY (const xmlChar *)"key" #define XSD_LANG (const xmlChar *)"xml:lang" #define XSD_LAX (const xmlChar *)"lax" #define XSD_LENGTH (const xmlChar *)"length" #define XSD_LIST (const xmlChar *)"list" #define XSD_LOC (const xmlChar *)"schemaLocation" #define XSD_MEMBERTYPES (const xmlChar *)"memberTypes" #define XSD_MAX_INCL (const xmlChar *)"maxInclusive" #define XSD_MAX_LEN (const xmlChar *)"maxLength" #define XSD_MAX_OCCURS (const xmlChar *)"maxOccurs" #define XSD_MIN_INCL (const xmlChar *)"minInclusive" #define XSD_MIN_LEN (const xmlChar *)"minLength" #define XSD_MIN_OCCURS (const xmlChar *)"minOccurs" #define XSD_NAMESPACE (const xmlChar *)"namespace" #define XSD_NOTIF_CONTENT (const xmlChar *)"notificationContent" #define XSD_NOTIF_CTYPE (const xmlChar *)"NotificationContentType" #define XSD_OPTIONAL (const xmlChar *)"optional" #define XSD_OTHER (const xmlChar *)"##other" #define XSD_OUTPUT_TYPEEXT (const xmlChar *)"_output_type__" #define XSD_PROC_CONTENTS (const xmlChar *)"processContents" #define XSD_PS_SUFFIX (const xmlChar *)"PSType" #define XSD_QNAME (const xmlChar *)"QName" #define XSD_QUAL (const xmlChar *)"qualified" #define XSD_REQUIRED (const xmlChar *)"required" #define XSD_REF (const xmlChar *)"ref" #define XSD_RFC_URL (const xmlChar *)"http://www.ietf.org/rfc/rfc" #define XSD_RPC_OP (const xmlChar *)"rpcOperation" #define XSD_RPC_OPTYPE (const xmlChar *)"rpcOperationType" #define XSD_RESTRICTION (const xmlChar *)"restriction" #define XSD_SCHEMA (const xmlChar *)"schema" #define XSD_SELECTOR (const xmlChar *)"selector" #define XSD_SEQUENCE (const xmlChar *)"sequence" #define XSD_SIM_CON (const xmlChar *)"simpleContent" #define XSD_SIM_TYP (const xmlChar *)"simpleType" #define XSD_SUB_GRP (const xmlChar *)"substitutionGroup" #define XSD_TARG_NS (const xmlChar *)"targetNamespace" #define XSD_TRUE (const xmlChar *)"true" #define XSD_UNBOUNDED (const xmlChar *)"unbounded" #define XSD_UNION (const xmlChar *)"union" #define XSD_UNIQUE (const xmlChar *)"unique" #define XSD_UNIQUE_SUFFIX (const xmlChar *)"_Unique" #define XSD_UNQUAL (const xmlChar *)"unqualified" #define XSD_USE (const xmlChar *)"use" #define XSD_VALUE (const xmlChar *)"value" #define XSD_ZERO (const xmlChar *)"0" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ typedef struct xsd_keychain_t_ { dlq_hdr_t qhdr; val_value_t *val; } xsd_keychain_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xsd_typename * * Get the corresponding XSD type name for the NCX base type * * INPUTS: * btyp == base type * * RETURNS: * const pointer to the XSD base type name string, NULL if error *********************************************************************/ extern const xmlChar * xsd_typename (ncx_btype_t btyp); /******************************************************************** * FUNCTION xsd_make_basename * * Malloc a string buffer and create a basename string * for a base type and metaSimpleType pair * * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * INPUTS: * nsid == namespace ID to use * name == condition clause (may be NULL) * * RETURNS: * malloced value string or NULL if malloc error *********************************************************************/ extern xmlChar * xsd_make_basename (const typ_template_t *typ); /******************************************************************** * FUNCTION xsd_make_enum_appinfo * * Go through the appinfoQ and add a child node to the * parent struct, which should be named 'appinfo' * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * INPUTS: * enuval == enum value * appinfoQ == queue to check * status == definition status * * RETURNS: * malloced value containing all the appinfo or NULL if malloc error *********************************************************************/ extern val_value_t * xsd_make_enum_appinfo (int32 enuval, const dlq_hdr_t *appinfoQ, ncx_status_t status); /******************************************************************** * FUNCTION xsd_make_bit_appinfo * * Go through the appinfoQ and add a child node to the * parent struct, which should be named 'appinfo' * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * INPUTS: * bitupos == bit position * appinfoQ == queue to check * status == definition status * * RETURNS: * malloced value containing all the appinfo or NULL if malloc error *********************************************************************/ extern val_value_t * xsd_make_bit_appinfo (uint32 bitpos, const dlq_hdr_t *appinfoQ, ncx_status_t status); /******************************************************************** * FUNCTION xsd_make_err_appinfo * * Make an appinfo node for the error-app-tag and or * error-message extensions * * INPUTS: * ref == reference clause (may be NULL) * errmsg == error msg string (1 may be NULL) * errtag == error app tag string (1 may be NULL) * * RETURNS: * malloced value containing the requested appinfo * or NULL if malloc error *********************************************************************/ extern val_value_t * xsd_make_err_appinfo (const xmlChar *ref, const xmlChar *errmsg, const xmlChar *errtag); /******************************************************************** * FUNCTION xsd_make_schema_location * * Get the schemaLocation string for this module * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * INPUTS: * mod == module * schemaloc == CLI schmeloc parameter for URL start string * versionnames == TRUE if version in filename requested (CLI default) * == FALSE for no version names in the filenames * * RETURNS: * malloced string or NULL if malloc error *********************************************************************/ extern xmlChar * xsd_make_schema_location (const ncx_module_t *mod, const xmlChar *schemaloc, boolean versionnames); /******************************************************************** * FUNCTION xsd_make_output_filename * * Construct an output filename spec, based on the * conversion parameters * * INPUTS: * mod == module * cp == conversion parameters to use * * RETURNS: * malloced string or NULL if malloc error *********************************************************************/ extern xmlChar * xsd_make_output_filename (const ncx_module_t *mod, const yangdump_cvtparms_t *cp); /******************************************************************** * FUNCTION xsd_do_documentation * * Create a value struct representing the description node * This is complete; The val_free_value function must be called * with the return value if it is non-NULL; * * INPUTS: * descr == reference clause * val == struct to contain added clause * * OUTPUT: * val->v.childQ has entries added if descr is non-NULL * * RETURNS: * status *********************************************************************/ extern status_t xsd_do_documentation (const xmlChar *descr, val_value_t *val); /******************************************************************** * FUNCTION xsd_do_reference * * Create a value struct representing the reference node * This is complete; The val_free_value function must be called * with the return value if it is non-NULL; * * INPUTS: * ref == reference clause * val == struct to contain added clause * * OUTPUT: * val->v.childQ has entries added if descr and ref are non-NULL * * RETURNS: * status *********************************************************************/ extern status_t xsd_do_reference (const xmlChar *ref, val_value_t *val); /******************************************************************** * FUNCTION xsd_add_mod_documentation * * Create multiple elements representing the * module-level documentation node * * INPUTS: * mod == module in progress * annot == annotation node to add the documentation node into * * OUTPUTS: * annot->a.childQ has nodes added if NO_ERR * * RETURNS: * status *********************************************************************/ extern status_t xsd_add_mod_documentation (const ncx_module_t *mod, val_value_t *annot); /******************************************************************** * FUNCTION xsd_add_imports * * Add the required imports nodes * * INPUTS: * pcb == parser control block * mod == module in progress * cp == conversion parameters in use * val == struct parent to contain child nodes for each import * * OUTPUTS: * val->childQ has entries added for the imports required * * RETURNS: * status *********************************************************************/ extern status_t xsd_add_imports (yang_pcb_t *pcb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, val_value_t *val); /******************************************************************** * FUNCTION xsd_add_includes * * Add the required include nodes * * INPUTS: * mod == module in progress * cp == conversion parameters in use * val == struct parent to contain child nodes for each include * * OUTPUTS: * val->childQ has entries added for the includes required * * RETURNS: * status *********************************************************************/ extern status_t xsd_add_includes (const ncx_module_t *mod, const yangdump_cvtparms_t *cp, val_value_t *val); /******************************************************************** * FUNCTION xsd_new_element * * Set up a new struct as an element * * INPUTS: * mod == module conversion in progress * name == element name * typdef == typ def for child node in progress * parent == typ def for parent node in progress (may be NULL) * hasnodes == TRUE if there are child nodes for this element * == FALSE if this is an empty node, maybe with attributes * hasindex == TRUE if this type has an index clause * == FALSE if this type does not have an index clause * * RETURNS: * new element data struct or NULL if malloc error *********************************************************************/ extern val_value_t * xsd_new_element (const ncx_module_t *mod, const xmlChar *name, const typ_def_t *typdef, const typ_def_t *parent, boolean hasnodes, boolean hasindex); /******************************************************************** * FUNCTION xsd_new_leaf_element * * Set up a new YANG leaf or leaf-list as an element * * INPUTS: * mod == module conversion in progress * obj == object to use (leaf of leaf-list) * hasnodes == TRUE if a struct should be used * FALSE if an empty element should be used * addtype == TRUE if a type attribute should be added * FALSE if type attribute should not be added * iskey == TRUE if a this is a key leaf * FALSE if this is not a key leaf * * RETURNS: * new element data struct or NULL if malloc error *********************************************************************/ extern val_value_t * xsd_new_leaf_element (const ncx_module_t *mod, const obj_template_t *obj, boolean hasnodes, boolean addtype, boolean iskey); /******************************************************************** * FUNCTION xsd_add_parmtype_attr * * Generate a type attribute for a type template * * INPUTS: * targns == targetNamespace ID * typ == type template for the parm * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->metaQ has an entry added for this attribute * * RETURNS: * status *********************************************************************/ extern status_t xsd_add_parmtype_attr (xmlns_id_t targns, const typ_template_t *typ, val_value_t *val); /**** REMOVED *** no deep keys in YANG! * extern status_t * xsd_add_key (const xmlChar *name, * const typ_def_t *typdef, * val_value_t *val); */ /******************************************************************** * FUNCTION xsd_do_annotation * * Add an annotation element if needed * Deprecated -- used by NCX only * * INPUTS: * descr == description string (may be NULL) * ref == reference string (may be NULL) * condition == condition string (may be NULL) * units == units clause contents (or NULL if not used) * maxacc == max-access clause (NONE == omit) * status == status clause (NONE or CURRENT to omit * appinfoQ == queue of cx_appinfo_t records (may be NULL) * val == struct parent to contain child nodes for this annotation * * OUTPUTS: * val->v.childQ has an entry added for the annotation if * any of the content parameters are actually present * * RETURNS: * status *********************************************************************/ extern status_t xsd_do_annotation (const xmlChar *descr, const xmlChar *ref, const xmlChar *condition, const xmlChar *units, ncx_access_t maxacc, ncx_status_t status, const dlq_hdr_t *appinfoQ, val_value_t *val); /******************************************************************** * FUNCTION xsd_make_obj_annotation * * Make an annotation element for an object, if needed * * INPUTS: * obj == obj_template to check * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced object value struct * NULL if nothing to do (*res == NO_ERR) * NULL if some error (*res != NO_ERR) *********************************************************************/ extern val_value_t * xsd_make_obj_annotation (obj_template_t *obj, status_t *res); /******************************************************************** * FUNCTION xsd_do_type_annotation * * Add an annotation element for a typedef, if needed * * INPUTS: * typ == typ_template to check * val == value struct to add nodes to * * OUTPUTS: * val->v.childQ has an node added, if needed * * RETURNS: * status *********************************************************************/ extern status_t xsd_do_type_annotation (const typ_template_t *typ, val_value_t *val); /******************************************************************** * FUNCTION xsd_make_group_annotation * * Make an annotation element for a grouping, if needed * * INPUTS: * grp == grp_template to check * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced object value struct * NULL if nothing to do (*res == NO_ERR) * NULL if some error (*res != NO_ERR) *********************************************************************/ extern val_value_t * xsd_make_group_annotation (const grp_template_t *grp, status_t *res); /******************************************************************** * FUNCTION xsd_add_aughook * * Create an xs:any hook to add augments * * INPUTS: * val == val_value_t struct to add new last child leaf * * RETURNS: * status *********************************************************************/ extern status_t xsd_add_aughook (val_value_t *val); /******************************************************************** * FUNCTION xsd_make_rpc_output_typename * * Create a type name for the RPC function output data structure * * INPUTS: * obj == reference clause (may be NULL) * * RETURNS: * malloced buffer with the typename *********************************************************************/ extern xmlChar * xsd_make_rpc_output_typename (const obj_template_t *obj); /******************************************************************** * FUNCTION xsd_add_type_attr * * Generate a type attribute * * INPUTS: * mod == module in progress * contains targetNamespace ID * typdef == typ_def for the typ_template struct to use for * the attribute list source * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->metaQ has an entry added for this attribute * * RETURNS: * status *********************************************************************/ extern status_t xsd_add_type_attr (const ncx_module_t *mod, const typ_def_t *typdef, val_value_t *val); /******************************************************************** * FUNCTION test_basetype_attr * * Test for the OK generate a type or base attribute * * INPUTS: * mod == module in progress * typdef == typ_def for the typ_template struct to use for * the attribute list source * * RETURNS: * status *********************************************************************/ extern status_t test_basetype_attr (const ncx_module_t *mod, const typ_def_t *typdef); /******************************************************************** * FUNCTION get_next_seqnum * * Get a unique integer * * RETURNS: * next seqnum *********************************************************************/ extern uint32 get_next_seqnum (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xsd_util */ yuma123_2.14/netconf/src/ydump/xsd_typ.h0000664000175000017500000001215414770023131020352 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xsd_typ #define _H_xsd_typ /* FILE: xsd_typ.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert NCX Types to XSD format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24-nov-06 abb Begun; split from xsd.c */ #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xsd_add_types * * Add the required type nodes * * INPUTS: * mod == module in progress * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->childQ has entries added for the types in this module * * RETURNS: * status *********************************************************************/ extern status_t xsd_add_types (const ncx_module_t *mod, val_value_t *val); /******************************************************************** * FUNCTION xsd_add_local_types * * Add the required type nodes for the local types in the typnameQ * YANG Only -- NCX does not have local types * * INPUTS: * mod == module in progress * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->childQ has entries added for the types in this module * * RETURNS: * status *********************************************************************/ extern status_t xsd_add_local_types (const ncx_module_t *mod, val_value_t *val); /******************************************************************** * FUNCTION xsd_finish_simpleType * * Generate a simpleType elment based on the typdef * Note: NCX_BT_ENAME is considered a * complexType for XSD conversion purposes * * INPUTS: * mod == module in progress * typdef == typ def for the typ_template struct to convert * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the types in this module * * RETURNS: * status *********************************************************************/ extern status_t xsd_finish_simpleType (const ncx_module_t *mod, typ_def_t *typdef, val_value_t *val); /******************************************************************** * FUNCTION xsd_finish_namedType * * Generate a complexType elment based on the typdef for a named type * The tclass must be NCX_CL_NAMED for the typdef data struct * * Note: The "+=" type extension mechanism in NCX is not supportable * in XSD for strings, so it must not be used at this time. * It is not supported for any data type, even enum. * * INPUTS: * mod == module in progress * typdef == typ def for the typ_template struct to convert * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the types in this module * * RETURNS: * status *********************************************************************/ extern status_t xsd_finish_namedType (const ncx_module_t *mod, typ_def_t *typdef, val_value_t *val); /******************************************************************** * FUNCTION xsd_finish_union * * Generate the XSD for a union element * * INPUTS: * mod == module in progress * typdef == typdef for the list * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the nodes in this typdef * * RETURNS: * status *********************************************************************/ extern status_t xsd_finish_union (const ncx_module_t *mod, typ_def_t *typdef, val_value_t *val); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xsd_typ */ yuma123_2.14/netconf/src/ydump/tg2.c0000664000175000017500000001357714770023131017361 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: tg2.c Generate turbogears 2 files for a specific YANG module For module foo and bar: The TG2 format is: tg2env/myproject/myproject/ templates/ foo.html Genshi bar.html controllers/ root.py foo.py TG2::RootController bar.py model/ __init__.py foo.py SQLAlchemy::DeclarativeBase bar.py Mapping from YANG to SQL: Everything maps to a table class, even a leaf. Nested lists and leaf-lists need to be split out into their own table, inherited all the ancestor keys as unique columns. Foreign keys link the tables to manage fate-sharing. Choice nodes are collapsed so the first visible descendant node within each case is raised to the the same level as the highest-level choice. This is OK since node names are not allowed to collide within a choice/case meta-containers. Container nodes are collapsed to the child nodes, but the container name is retained as a prefix because child nodes of the container may collide with siblings of the container. Nested lists and leaf-lists are unfolded as follows: list top | +----------+-----------+ | | | key a list b leaf c | +----------+-----------+-------+ | | | | key d key e leaf f leaf-list g table: top: id: primary key; a: unique column; c: column; table top_b : id: primary key; top_id: foreign key; top_a: unique column; d: unique column; e: unique column; f: column; table top_b_g : id: primary key; top_id: foreign key; top_b_id: foreign key; top_a: unique column; top_b_d: unique column; top_b_e: unique column; g: column; ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11mar10 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_c_util #include "c_util.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_py_util #include "py_util.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_tg2 #include "tg2.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tstamp #include "tstamp.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xsd_util #include "xsd_util.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifndef _H_yangdump_util #include "yangdump_util.h" #endif /********* E X P O R T E D F U N C T I O N S **************/ /******************************************************************** * FUNCTION tg2_convert_module_model * * Convert the YANG module to TG2 code for the SQL model * for the YANG data nodes; used by the Yuma Tools Workbench * application * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == convert parms struct to use * scb == session to use for output * * RETURNS: * status *********************************************************************/ status_t tg2_convert_module_model (const yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb) { const ncx_module_t *mod; const yang_node_t *node; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { return SET_ERROR(ERR_NCX_MOD_NOT_FOUND); } /* start the python file */ write_py_header(scb, mod, cp); /* convert_one_module_model(mod, cp, scb); */ if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { /* convert_one_module_model(node->submod, cp, scb); */ } } } /* end the python file */ write_py_footer(scb, mod); return NO_ERR; } /* tg2_convert_module_model */ /* END file tg2.c */ yuma123_2.14/netconf/src/ydump/xsd_util.c0000664000175000017500000027616314770023131020522 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xsd_util.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24nov06 abb begun; split from xsd.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_num #include "ncx_num.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tstamp #include "tstamp.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xsd_util #include "xsd_util.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define DATETIME_BUFFSIZE 64 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static uint32 seqnum = 1; /******************************************************************** * FUNCTION make_include_location * * Get the schemaLocation string for this module * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * !!! TEMP: Location Hardwired to the following string!!! * * /.xsd * * INPUTS: * mod == module * * RETURNS: * malloced string or NULL if malloc error *********************************************************************/ static xmlChar * make_include_location (const xmlChar *modname, const xmlChar *schemaloc) { xmlChar *str, *str2; uint32 len, slen, inlen; len = inlen = (schemaloc) ? xml_strlen(schemaloc) : 0; slen = (uint32)((inlen && schemaloc[inlen-1] != '/') ? 1 : 0); len += slen; len += xml_strlen(modname); len += 6; /* 1 backslash, file ext and terminating zero */ str = m__getMem(len); if (!str) { return NULL; } str2 = str; if (schemaloc && *schemaloc) { str2 += xml_strcpy(str2, schemaloc); if (slen) { *str2++ = '/'; } } str2 += xml_strcpy(str2, modname); *str2++ = '.'; str2 += xml_strcpy(str2, NCX_XSD_EXT); return str; } /* make_include_location */ /******************************************************************** * FUNCTION add_revhist * * Add the revision history entry or just return the size required * * INPUTS: * mod == module in progress * appinfo == appinfo value struct to add revision child nodes * * RETURNS: * status *********************************************************************/ static status_t add_revhist (const ncx_module_t *mod, val_value_t *appinfo) { const ncx_revhist_t *rh; val_value_t *rev, *chnode; xmlns_id_t ncx_id; ncx_id = xmlns_ncx_id(); for (rh = (const ncx_revhist_t *)dlq_firstEntry(&mod->revhistQ); rh != NULL; rh = (const ncx_revhist_t *)dlq_nextEntry(rh)) { if (!rh->version && !rh->descr) { SET_ERROR(ERR_INTERNAL_VAL); continue; } rev = xml_val_new_struct(YANG_K_REVISION, ncx_id); if (!rev) { return ERR_INTERNAL_MEM; } else { val_add_child(rev, appinfo); /* add early */ } if (rh->version) { chnode = xml_val_new_cstring(NCX_EL_VERSION, ncx_id, rh->version); if (!chnode) { return ERR_INTERNAL_MEM; } else { val_add_child(chnode, rev); } } if (rh->descr) { chnode = xml_val_new_cstring(YANG_K_DESCRIPTION, ncx_id, rh->descr); if (!chnode) { return ERR_INTERNAL_MEM; } else { val_add_child(chnode, rev); } } } return NO_ERR; } /* add_revhist */ /******************************************************************** * FUNCTION add_appinfoQ * * Add all the ncx_appinfo_t structs in the specified Q * into one element. Look up each extension * for encoding rules. They should have been validated * already. * * INPUTS: * appinfoQ == Q of ncx_appinfo_t use * appval == address of container node to * get the ncx_appinfo_t elements * * OUTPUTS: * annot->v.childQ has 1 entry added for each appinfo * may be a nested element if the appinfo * has additional entries in its appinfoQ * * RETURNS: * status *********************************************************************/ static status_t add_appinfoQ (const dlq_hdr_t *appinfoQ, val_value_t *appval) { val_value_t *newval, *chval; const ncx_appinfo_t *appinfo; xmlns_id_t ncx_id; status_t res; ncx_id = xmlns_ncx_id(); /* add an element for each appinfo record */ for (appinfo = (const ncx_appinfo_t *)dlq_firstEntry(appinfoQ); appinfo != NULL; appinfo = (const ncx_appinfo_t *)dlq_nextEntry(appinfo)) { if (!appinfo->ext) { /* NCX parsed appinfo */ if (appinfo->value) { newval = xml_val_new_cstring(appinfo->name, ncx_id, appinfo->value); } else { newval = xml_val_new_flag(appinfo->name, ncx_id); } if (!newval) { return ERR_INTERNAL_MEM; } else { val_add_child(newval, appval); } continue; } /* else a YANG parsed appinfo */ if ((appinfo->ext->arg && appinfo->ext->argel) || !dlq_empty(appinfo->appinfoQ)) { newval = xml_val_new_struct(appinfo->ext->name, appinfo->ext->nsid); } else { newval = xml_val_new_flag(appinfo->ext->name, appinfo->ext->nsid); } if (!newval) { return ERR_INTERNAL_MEM; } else { val_add_child(newval, appval); } if (appinfo->ext->arg) { if (appinfo->ext->argel) { chval = xml_val_new_cstring(appinfo->ext->arg, appinfo->ext->nsid, appinfo->value); if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, newval); } } else { res = xml_val_add_cattr(appinfo->ext->arg, appinfo->ext->nsid, appinfo->value, newval); if (res != NO_ERR) { return res; } } } if (!dlq_empty(appinfo->appinfoQ)) { res = add_appinfoQ(appinfo->appinfoQ, newval); if (res != NO_ERR) { return res; } } } return NO_ERR; } /* add_appinfoQ */ /******************************************************************** * FUNCTION make_reference * * Create a value struct representing the documentation node * Add this element to an element * * INPUTS: * ref == reference clause (may be NULL) * * RETURNS: * value struct or NULL if malloc error *********************************************************************/ static val_value_t * make_reference (const xmlChar *ref) { val_value_t *retval, *textval, *urlval; const xmlChar *str, *p; xmlChar *buff, *q; uint32 len, numlen, i; xmlns_id_t ncx_id; if (!ref) { return NULL; } ncx_id = xmlns_ncx_id(); len = xml_strlen(ref); retval = xml_val_new_struct(YANG_K_REFERENCE, ncx_id); if (!retval) { return NULL; } textval = xml_val_new_cstring(NCX_EL_TEXT, ncx_id, ref); if (!textval) { val_free_value(retval); return NULL; } else { val_add_child(textval, retval); } /* check if the reference is to an RFC and if so generate * a 'url' element as well */ if (len > 4 && !xml_strncmp(ref, (const xmlChar *)"RFC ", 4)) { str = &ref[4]; p = str; while (*p && isdigit(*p)) { p++; } numlen = (uint32)(p-str); /* IETF RFC URLs are currently hard-wired 4 digit number */ if (numlen && (numlen <= 4) && (len >= numlen+4)) { buff = m__getMem(xml_strlen(XSD_RFC_URL) + 9); if (!buff) { val_free_value(retval); return NULL; } q = buff; q += xml_strcpy(q, XSD_RFC_URL); for (i = 4 - numlen; i; i--) { *q++ = '0'; } for (i=0; i 6 && !xml_strncmp(ref, (const xmlChar *)"draft-", 6)) { str = &ref[6]; while (*str && (!xml_isspace(*str)) && (*str != ';') && (*str != ':') && (*str != ',')) { str++; } if (str != &ref[6] && *str == '.') { \ str--; } numlen = (uint32)(str-ref); buff = m__getMem(xml_strlen(XSD_DRAFT_URL) + numlen + 1); if (!buff) { val_free_value(retval); return NULL; } q = buff; q += xml_strcpy(q, XSD_DRAFT_URL); q += xml_strncpy(q, ref, numlen); urlval = xml_val_new_string(NCX_EL_URL, ncx_id, buff); if (!urlval) { m__free(buff); val_free_value(retval); return NULL; } else { val_add_child(urlval, retval); } } return retval; } /* make_reference */ /******************************************************************** * FUNCTION make_obj_appinfo * * Make an appinfo element for an object, if needed * * INPUTS: * obj == obj_template to check * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced appinfo struct * NULL if nothing to do (*res == NO_ERR) * NULL if some error (*res != NO_ERR) *********************************************************************/ static val_value_t * make_obj_appinfo (obj_template_t *obj, status_t *res) { val_value_t *newval, *appval, *mustval, *refval; dlq_hdr_t *mustQ, *appinfoQ, *datadefQ; xpath_pcb_t *must; ncx_errinfo_t *errinfo; obj_template_t *outputobj; const xmlChar *units, *presence, *ref; xmlChar *buff; ncx_status_t status; xmlns_id_t ncx_id; boolean needed, config_needed, config; #ifdef DEBUG if (!obj || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif outputobj = NULL; needed = FALSE; config_needed = FALSE; ncx_id = xmlns_ncx_id(); *res = ERR_INTERNAL_MEM; /* setup only error for now */ status = obj_get_status(obj); config = obj_get_config_flag2(obj, &config_needed); mustQ = obj_get_mustQ(obj); appinfoQ = obj_get_appinfoQ(obj); if (mustQ && !dlq_empty(mustQ)) { needed = TRUE; } if (appinfoQ && !dlq_empty(appinfoQ)) { needed = TRUE; } switch (obj->objtype) { case OBJ_TYP_ANYXML: case OBJ_TYP_CONTAINER: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: case OBJ_TYP_LIST: case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: needed = TRUE; break; case OBJ_TYP_USES: needed = FALSE; break; case OBJ_TYP_AUGMENT: needed = (obj->flags & OBJ_FL_TOP) ? TRUE : FALSE; break; case OBJ_TYP_RPC: datadefQ = obj_get_datadefQ(obj); outputobj = obj_find_template(datadefQ, NULL, YANG_K_OUTPUT); if (outputobj) { needed = TRUE; } break; case OBJ_TYP_RPCIO: break; case OBJ_TYP_NOTIF: break; default: *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } if (!needed) { /* appinfo not needed so exit now */ *res = NO_ERR; return NULL; } /* appinfo may still not be needed for many object types * If not then delete appval at the end */ needed = FALSE; appval = xml_val_new_struct(NCX_EL_APPINFO, xmlns_xs_id()); if (!appval) { return NULL; } /* add choice or case name if needed */ if (obj->objtype==OBJ_TYP_CHOICE) { needed = TRUE; newval = xml_val_new_cstring(NCX_EL_CHOICE_NAME, ncx_id, obj_get_name(obj)); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } else if (obj->objtype==OBJ_TYP_CASE) { needed = TRUE; newval = xml_val_new_cstring(NCX_EL_CASE_NAME, ncx_id, obj_get_name(obj)); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add status clause if needed */ if (status != NCX_STATUS_NONE && status != NCX_STATUS_CURRENT) { needed = TRUE; newval = xml_val_new_cstring(YANG_K_STATUS, ncx_id, ncx_get_status_string(status)); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add config clause if needed */ if (config_needed) { needed = TRUE; newval = xml_val_new_cstring(YANG_K_CONFIG, ncx_id, config ? NCX_EL_TRUE : NCX_EL_FALSE); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add when clause if needed */ if (obj->when && obj->when->exprstr) { needed = TRUE; newval = xml_val_new_cstring(YANG_K_WHEN, ncx_id, obj->when->exprstr); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add must entries if needed */ if (mustQ) { for (must = (xpath_pcb_t *)dlq_firstEntry(mustQ); must != NULL; must = (xpath_pcb_t *)dlq_nextEntry(must)) { errinfo = &must->errinfo; needed = TRUE; mustval = xml_val_new_struct(YANG_K_MUST, ncx_id); if (!mustval) { val_free_value(appval); return NULL; } else { val_add_child(mustval, appval); } if (must->exprstr) { newval = xml_val_new_cstring(NCX_EL_XPATH, ncx_id, must->exprstr); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, mustval); } } if (errinfo->descr) { newval = xml_val_new_cstring(YANG_K_DESCRIPTION, ncx_id, errinfo->descr); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, mustval); } } if (errinfo->ref) { newval = xml_val_new_cstring(YANG_K_REFERENCE, ncx_id, errinfo->ref); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, mustval); } } if (errinfo->error_app_tag) { newval = xml_val_new_cstring(YANG_K_ERROR_APP_TAG, ncx_id, errinfo->error_app_tag); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, mustval); } } if (errinfo->error_message) { newval = xml_val_new_cstring(YANG_K_ERROR_MESSAGE, ncx_id, errinfo->error_message); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, mustval); } } } } switch (obj->objtype) { case OBJ_TYP_CONTAINER: presence = obj_get_presence_string(obj); if (presence) { needed = TRUE; newval = xml_val_new_cstring(YANG_K_PRESENCE, ncx_id, presence); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } break; case OBJ_TYP_LEAF: units = obj_get_units(obj); if (units) { needed = TRUE; newval = xml_val_new_cstring(YANG_K_UNITS, ncx_id, units); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* fall through */ case OBJ_TYP_ANYXML: if (obj->flags & OBJ_FL_MANDSET) { needed = TRUE; newval = xml_val_new_cstring(YANG_K_MANDATORY, ncx_id, (obj->flags & OBJ_FL_MANDATORY) ? NCX_EL_TRUE : NCX_EL_FALSE); if (!newval) { *res = ERR_INTERNAL_MEM; val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } break; case OBJ_TYP_LEAF_LIST: units = obj_get_units(obj); if (units) { needed = TRUE; newval = xml_val_new_cstring(YANG_K_UNITS, ncx_id, units); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } newval = xml_val_new_cstring(YANG_K_ORDERED_BY, ncx_id, obj_is_system_ordered(obj) ? YANG_K_SYSTEM : YANG_K_USER); if (!newval) { val_free_value(appval); return NULL; } else { needed = TRUE; val_add_child(newval, appval); } break; case OBJ_TYP_LIST: needed = TRUE; newval = xml_val_new_cstring(YANG_K_ORDERED_BY, ncx_id, obj_is_system_ordered(obj) ? YANG_K_SYSTEM : YANG_K_USER); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } break; case OBJ_TYP_CHOICE: if (obj->flags & OBJ_FL_MANDSET) { needed = TRUE; newval = xml_val_new_cstring(YANG_K_MANDATORY, ncx_id, (obj->flags & OBJ_FL_MANDATORY) ? NCX_EL_TRUE : NCX_EL_FALSE); if (!newval) { val_free_value(appval); } else { val_add_child(newval, appval); } } break; case OBJ_TYP_CASE: break; case OBJ_TYP_USES: break; case OBJ_TYP_AUGMENT: break; case OBJ_TYP_RPC: if (outputobj) { needed = TRUE; buff = xsd_make_rpc_output_typename(obj); newval = xml_val_new_string(NCX_EL_RPC_OUTPUT, ncx_id, buff); if (!newval) { m__free(buff); val_free_value(appval); } else { val_add_child(newval, appval); } } break; case OBJ_TYP_RPCIO: break; case OBJ_TYP_NOTIF: break; default: *res = SET_ERROR(ERR_INTERNAL_VAL); val_free_value(appval); return NULL; } /* check if reference needed */ ref = obj_get_reference(obj); if (ref != NULL) { needed = TRUE; refval = make_reference(ref); if (!refval) { val_free_value(appval); return NULL; } else { val_add_child(refval, appval); } } /* add an element for each appinfo record */ if (appinfoQ && !dlq_empty(appinfoQ)) { needed = TRUE; *res = add_appinfoQ(appinfoQ, appval); } if (!needed) { val_free_value(appval); appval = NULL; } *res = NO_ERR; return appval; } /* make_obj_appinfo */ /******************************************************************** * FUNCTION new_element * * Set up a new struct as an element * * INPUTS: * mod == module conversion in progress * name == element name * typdef == typ def for child node in progress * parent == typ def for parent node in progress (may be NULL) * hasnodes == TRUE if there are child nodes for this element * == FALSE if this is an empty node, maybe with attributes * hasindex == TRUE if this type has an index clause * == FALSE if this type does not have an index clause * forcetyp == typ_template_t to use if this is a parm element * == NULL if not a parm element * RETURNS: * new element data struct or NULL if malloc error *********************************************************************/ static val_value_t * new_element (const ncx_module_t *mod, const xmlChar *name, const typ_def_t *typdef, const typ_def_t *parent, boolean hasnodes, boolean hasindex, const typ_template_t *forcetyp) { val_value_t *val; const xmlChar *def; uint32 maxrows; ncx_iqual_t iqual; ncx_btype_t btyp; status_t res; xmlChar numbuff[NCX_MAX_NUMLEN]; btyp = typ_get_basetype(typdef); /* create a node named 'element' to hold the result */ if (hasnodes || hasindex) { val = xml_val_new_struct(XSD_ELEMENT, xmlns_xs_id()); } else { val = xml_val_new_flag(XSD_ELEMENT, xmlns_xs_id()); } if (!val) { return NULL; } /* add the name attribute */ res = xml_val_add_cattr(NCX_EL_NAME, 0, name, val); if (res != NO_ERR) { val_free_value(val); return NULL; } /* add the type attribute unless this is a complex element */ if (forcetyp) { res = xsd_add_parmtype_attr(mod->nsid, forcetyp, val); if (res != NO_ERR) { val_free_value(val); return NULL; } } else if (!hasnodes && btyp != NCX_BT_EMPTY) { /* add the type attribute */ res = xsd_add_type_attr(mod, typdef, val); if (res != NO_ERR) { val_free_value(val); return NULL; } } /* else the type definition should be part of the element */ /* check if a default attribute is needed */ def = typ_get_default(typdef); if (def) { res = xml_val_add_cattr(NCX_EL_DEFAULT, 0, def, val); if (res != NO_ERR) { val_free_value(val); return NULL; } } /* if this is the child node of a container, then * override the iqual values in the child and use * the parent container node iqual values instead * * A container is always allowed to be empty, so * it can have a consistent start state validatation check */ if (parent) { switch (typ_get_basetype(parent)) { case NCX_BT_LIST: /************/ iqual = NCX_IQUAL_ZMORE; maxrows = typ_get_maxrows(parent); break; default: iqual = typ_get_iqualval_def(typdef); maxrows = typ_get_maxrows(typdef); } } else { iqual = typ_get_iqualval_def(typdef); maxrows = typ_get_maxrows(typdef); } /* add the minOccurs and maxOccurs as needed */ if (maxrows) { res = xml_val_add_cattr(XSD_MIN_OCCURS, 0, XSD_ZERO, val); if (res == NO_ERR) { sprintf((char *)numbuff, "%u", maxrows); res = xml_val_add_cattr(XSD_MAX_OCCURS, 0, numbuff, val); } } else { switch (iqual) { case NCX_IQUAL_ONE: /* do nothing since this is the default */ break; case NCX_IQUAL_1MORE: res = xml_val_add_cattr(XSD_MAX_OCCURS, 0, XSD_UNBOUNDED, val); break; case NCX_IQUAL_OPT: res = xml_val_add_cattr(XSD_MIN_OCCURS, 0, XSD_ZERO, val); break; case NCX_IQUAL_ZMORE: res = xml_val_add_cattr(XSD_MIN_OCCURS, 0, XSD_ZERO, val); if (res == NO_ERR) { res = xml_val_add_cattr(XSD_MAX_OCCURS, 0, XSD_UNBOUNDED, val); } break; case NCX_IQUAL_NONE: default: res = SET_ERROR(ERR_INTERNAL_VAL); } } if (res != NO_ERR) { val_free_value(val); return NULL; } return val; } /* new_element */ /******************************************************************** * FUNCTION add_basetype_attr * * Generate a type or base attribute * * INPUTS: * istype == TRUE if this is a type attribute * == FALSE if this is a base attribute * mod == module in progress * typdef == typ_def for the typ_template struct to use for * the attribute list source * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the attributes in this typdef * * RETURNS: * status *********************************************************************/ static status_t add_basetype_attr (boolean istype, const ncx_module_t *mod, const typ_def_t *typdef, val_value_t *val) { xmlChar *str; const xmlChar *name, *attrname; status_t res; xmlns_id_t typ_id, targns; ncx_btype_t btyp; targns = mod->nsid; /* figure out the correct namespace and name for the typename */ switch (typdef->tclass) { case NCX_CL_BASE: case NCX_CL_SIMPLE: typ_id = xmlns_xs_id(); name = xsd_typename(typ_get_basetype(typdef)); break; case NCX_CL_COMPLEX: btyp = typ_get_basetype(typdef); switch (btyp) { case NCX_BT_ANYDATA: case NCX_BT_ANYXML: typ_id = xmlns_xs_id(); name = xsd_typename(btyp); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } break; case NCX_CL_NAMED: typ_id = typdef->def.named.typ->nsid; if (typ_id) { /* namespace assigned means it is a top-level exported def */ name = typdef->def.named.typ->name; } else { /* inside a grouping, include submod, etc. * this has to be a type in the current [sub]module * or it would have a NSID assigned already * * The design of the local typename to * global XSD typename translation does not * put top-level typedefs in the typenameQ. * They are already in the registry, so name collision * can be checked that way */ typ_id = mod->nsid; name = ncx_find_typname(typdef->def.named.typ, &mod->typnameQ); if (!name) { /* this must be a local type name in a grouping. * it could also be a local type inside a node */ name = typdef->def.named.typ->name; } } break; case NCX_CL_REF: default: return SET_ERROR(ERR_INTERNAL_VAL); } if (istype) { attrname = NCX_EL_TYPE; } else { attrname = XSD_BASE; } /* set the data type */ if (typ_id == targns) { /* this is in the targetNamespace */ return xml_val_add_cattr(attrname, 0, name, val); } else { /* construct the QName for the attribute type */ str = xml_val_make_qname(typ_id, name); if (!str) { return ERR_INTERNAL_MEM; } /* add the base or type attribute */ res = xml_val_add_attr(attrname, 0, str, val); if (res != NO_ERR) { m__free(str); return res; } } return NO_ERR; } /* add_basetype_attr */ /******************************************************************** * FUNCTION make_documentation * * Create a value struct representing the documentation node * This is complete; The val_free_value function must be called * with the return value if it is non-NULL; * * INPUTS: * descr == description clause (may be NULL) * * RETURNS: * value struct or NULL if malloc error *********************************************************************/ static val_value_t * make_documentation (const xmlChar *descr) { if (!descr) { return NULL; } else { return xml_val_new_cstring(XSD_DOCUMENTATION, xmlns_xs_id(), descr); } } /* make_documentation */ /******************************************************************** * FUNCTION make_schema_loc * * Get the schemaLocation string for this module * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * !!! TEMP: Location Hardwired to the following string!!! * * /.xsd * * INPUTS: * schemaloc == URL prefix parameter from user (may be NULL) * modname == module name * modname == module version string (may be NULL) * cstr == const URI string to use * versionnames == TRUE if version in filename requested (CLI default) * == FALSE for no version names in the filenames * Ignored if modversion is NULL * RETURNS: * malloced string or NULL if malloc error *********************************************************************/ static xmlChar * make_schema_loc (const xmlChar *schemaloc, const xmlChar *modname, const xmlChar *modversion, const xmlChar *cstr, boolean versionnames) { xmlChar *str, *str2; uint32 len, slen, plen; slen = (schemaloc) ? xml_strlen(schemaloc) : 0; plen = (uint32)((slen && schemaloc[slen-1] != '/') ? 1 : 0); len = 9; /* newline + 4 spaces + file ext */ len += slen; len += plen; len += xml_strlen(cstr); len += xml_strlen(modname); if (versionnames && modversion) { len += (xml_strlen(modversion) + 1); } str = m__getMem(len+1); if (!str) { return NULL; } str2 = str; str2 += xml_strcpy(str2, cstr); str2 += xml_strcpy(str2, (const xmlChar *)"\n "); if (schemaloc && *schemaloc) { str2 += xml_strcpy(str2, schemaloc); if (plen) { *str2++ = '/'; } } str2 += xml_strcpy(str2, modname); if (versionnames && modversion) { *str2++ = '_'; /***** CHANGE TO DOT *****/ str2 += xml_strcpy(str2, modversion); } *str2++ = '.'; str2 += xml_strcpy(str2, NCX_XSD_EXT); return str; } /* make_schema_loc */ /******************************************************************** * FUNCTION make_appinfo * * Go through the appinfoQ and add a child node to the * parent struct, which should be named 'appinfo' * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * INPUTS: * appinfoQ == queue to check * maxacc == value > none if max access clause should be added * condition == condition string (may be NULL) * units == units string (may be NULL) * ref == reference statement (may be NULL) * status == status value (may be NCX_STATUS_NONE) * clause not generated for default (current) * * RETURNS: * malloced value containing all the appinfo or NULL if malloc error *********************************************************************/ static val_value_t * make_appinfo (const dlq_hdr_t *appinfoQ, ncx_access_t maxacc, const xmlChar *condition, const xmlChar *units, const xmlChar *ref, ncx_status_t status) { val_value_t *newval, *appval; xmlns_id_t ncx_id; status_t res; appval = xml_val_new_struct(NCX_EL_APPINFO, xmlns_xs_id()); if (!appval) { return NULL; } ncx_id = xmlns_ncx_id(); /* add status clause if needed */ if (status != NCX_STATUS_NONE && status != NCX_STATUS_CURRENT) { newval = xml_val_new_cstring(NCX_EL_STATUS, ncx_id, ncx_get_status_string(status)); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add units clause if needed */ if (units) { newval = xml_val_new_cstring(NCX_EL_UNITS, ncx_id, units); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add max-access clause if needed */ if (maxacc != NCX_ACCESS_NONE) { newval = xml_val_new_cstring(NCX_EL_MAX_ACCESS, ncx_id, ncx_get_access_str(maxacc)); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add condition clause if needed */ if (condition) { newval = xml_val_new_cstring(NCX_EL_CONDITION, ncx_id, condition); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add reference clause if needed */ if (ref) { newval = make_reference(ref); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add an element for each appinfo record */ if (appinfoQ && !dlq_empty(appinfoQ)) { res = add_appinfoQ(appinfoQ, appval); if (res != NO_ERR) { val_free_value(appval); return NULL; } } return appval; } /* make_appinfo */ /******************************************************************** * FUNCTION make_type_appinfo * * Go through the appinfoQ and add a child node to the * parent struct, which should be named 'appinfo' * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * INPUTS: * typ == typ_template_t to check * path == path strnig for leafref or NULL if N/A * * RETURNS: * malloced value containing all the appinfo or NULL if malloc error *********************************************************************/ static val_value_t * make_type_appinfo (const typ_template_t *typ, const xmlChar *path) { val_value_t *newval, *appval; xmlns_id_t ncx_id; status_t res; appval = xml_val_new_struct(NCX_EL_APPINFO, xmlns_xs_id()); if (!appval) { return NULL; } ncx_id = xmlns_ncx_id(); /* add reference clause if needed */ if (typ->ref) { newval = make_reference(typ->ref); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add status clause if needed */ if (typ->status != NCX_STATUS_NONE && typ->status != NCX_STATUS_CURRENT) { newval = xml_val_new_cstring(NCX_EL_STATUS, ncx_id, ncx_get_status_string(typ->status)); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add units clause if needed */ if (typ->units) { newval = xml_val_new_cstring(NCX_EL_UNITS, ncx_id, typ->units); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add default clause if needed */ if (typ->defval) { newval = xml_val_new_cstring(NCX_EL_DEFAULT, ncx_id, typ->defval); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add path for leafref if needed */ if (path) { newval = xml_val_new_cstring(YANG_K_PATH, ncx_id, path); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } /* add an element for each appinfo record */ if (!dlq_empty(&typ->appinfoQ)) { res = add_appinfoQ(&typ->appinfoQ, appval); if (res != NO_ERR) { val_free_value(appval); return NULL; } } return appval; } /* make_type_appinfo */ /******************************************************************** * FUNCTION add_one_import * * Add one import node * * INPUTS: * pcb == parser control block * modname == name of module * revision == revision date of module * cp == conversion parameters in use * val == struct parent to contain child nodes for each import * * OUTPUTS: * val->childQ has entries added for the imports required * * RETURNS: * status *********************************************************************/ static status_t add_one_import (yang_pcb_t *pcb, const xmlChar *modname, const xmlChar *revision, const yangdump_cvtparms_t *cp, val_value_t *val) { val_value_t *imval; ncx_module_t *immod; xmlChar *str; xmlns_id_t id; status_t res; id = xmlns_xs_id(); immod = ncx_find_module(modname, revision); if (!immod) { res = ncxmod_load_module(modname, revision, pcb->savedevQ, &immod); if (!immod) { return res; } } imval = xml_val_new_flag(NCX_EL_IMPORT, id); if (!imval) { return ERR_INTERNAL_MEM; } else { val_add_child(imval, val); } res = xml_val_add_cattr(NCX_EL_NAMESPACE, 0, xmlns_get_ns_name(immod->nsid), imval); if (res == NO_ERR) { str = xsd_make_schema_location(immod, cp->schemaloc, cp->versionnames); if (str) { res = xml_val_add_attr(XSD_LOC, 0, str, imval); if (res != NO_ERR) { m__free(str); } } } return res; } /* add_one_import */ /************ E X T E R N A L F U N C T I O N S ******/ /******************************************************************** * FUNCTION xsd_typename * * Get the corresponding XSD type name for the NCX base type * * INPUTS: * btyp == base type * * RETURNS: * const pointer to the XSD base type name string, NULL if error *********************************************************************/ const xmlChar * xsd_typename (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_ANYDATA: case NCX_BT_ANYXML: return XSD_ANY_TYPE; case NCX_BT_BITS: case NCX_BT_ENUM: return NCX_EL_STRING; case NCX_BT_EMPTY: return NCX_EL_STRING; case NCX_BT_BOOLEAN: return NCX_EL_BOOLEAN; case NCX_BT_INT8: return NCX_EL_BYTE; case NCX_BT_INT16: return NCX_EL_SHORT; case NCX_BT_INT32: return NCX_EL_INT; case NCX_BT_INT64: return NCX_EL_LONG; case NCX_BT_UINT8: return NCX_EL_UNSIGNED_BYTE; case NCX_BT_UINT16: return NCX_EL_UNSIGNED_SHORT; case NCX_BT_UINT32: return NCX_EL_UNSIGNED_INT; case NCX_BT_UINT64: return NCX_EL_UNSIGNED_LONG; case NCX_BT_DECIMAL64: return NCX_EL_LONG; case NCX_BT_FLOAT64: return NCX_EL_DOUBLE; case NCX_BT_STRING: case NCX_BT_LEAFREF: /***/ case NCX_BT_INSTANCE_ID: return NCX_EL_STRING; case NCX_BT_IDREF: return XSD_QNAME; case NCX_BT_BINARY: return XSD_BASE64; case NCX_BT_SLIST: return XSD_LIST; case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_LIST: return NCX_EL_NONE; /***/ case NCX_BT_UNION: return NCX_EL_UNION; case NCX_BT_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_EL_NONE; /***/ } /*NOTREACHED*/ } /* xsd_typename */ /******************************************************************** * FUNCTION xsd_make_basename * * Malloc a string buffer and create a basename string * for a base type and metaSimpleType pair * * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * INPUTS: * nsid == namespace ID to use * name == condition clause (may be NULL) * * RETURNS: * malloced value string or NULL if malloc error *********************************************************************/ xmlChar * xsd_make_basename (const typ_template_t *typ) { xmlChar *basename, *str2; /* hack: make a different name for the base type * that will not be hidden */ basename = m__getMem(xml_strlen(typ->name) + xml_strlen(XSD_BT) + 1); if (!basename) { return NULL; } str2 = basename; str2 += xml_strcpy(str2, typ->name); str2 += xml_strcpy(str2, XSD_BT); return basename; } /* xsd_make_basename */ /******************************************************************** * FUNCTION xsd_make_enum_appinfo * * Go through the appinfoQ and add a child node to the * parent struct, which should be named 'appinfo' * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * INPUTS: * enuval == enum value * appinfoQ == queue to check * status == definition status * * RETURNS: * malloced value containing all the appinfo or NULL if malloc error *********************************************************************/ val_value_t * xsd_make_enum_appinfo (int32 enuval, const dlq_hdr_t *appinfoQ, ncx_status_t status) { val_value_t *newval, *appval; ncx_num_t num; xmlChar numbuff[NCX_MAX_NUMLEN]; xmlns_id_t ncx_id; status_t res; uint32 len; appval = xml_val_new_struct(NCX_EL_APPINFO, xmlns_xs_id()); if (!appval) { return NULL; } ncx_id = xmlns_ncx_id(); /* add status clause if not set to default (current) */ switch (status) { case NCX_STATUS_CURRENT: case NCX_STATUS_NONE: break; case NCX_STATUS_DEPRECATED: case NCX_STATUS_OBSOLETE: newval = xml_val_new_cstring(NCX_EL_STATUS, ncx_id, ncx_get_status_string(status)); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } break; default: SET_ERROR(ERR_INTERNAL_VAL); } /* add enum value clause */ num.i = enuval; res = ncx_sprintf_num(numbuff, &num, NCX_BT_INT32, &len); if (res != NO_ERR) { val_free_value(appval); return NULL; } newval = xml_val_new_cstring(NCX_EL_VALUE, ncx_id, numbuff); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } /* add an element for each appinfo record */ if (appinfoQ) { res = add_appinfoQ(appinfoQ, appval); if (res != NO_ERR) { val_free_value(appval); return NULL; } } return appval; } /* xsd_make_enum_appinfo */ /******************************************************************** * FUNCTION xsd_make_bit_appinfo * * Go through the appinfoQ and add a child node to the * parent struct, which should be named 'appinfo' * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * INPUTS: * bitupos == bit position * appinfoQ == queue to check * status == definition status * * RETURNS: * malloced value containing all the appinfo or NULL if malloc error *********************************************************************/ val_value_t * xsd_make_bit_appinfo (uint32 bitpos, const dlq_hdr_t *appinfoQ, ncx_status_t status) { val_value_t *newval, *appval; ncx_num_t num; xmlChar numbuff[NCX_MAX_NUMLEN]; xmlns_id_t ncx_id; status_t res; uint32 len; appval = xml_val_new_struct(NCX_EL_APPINFO, xmlns_xs_id()); if (!appval) { return NULL; } ncx_id = xmlns_ncx_id(); /* add status clause if not set to default (current) */ switch (status) { case NCX_STATUS_CURRENT: case NCX_STATUS_NONE: break; case NCX_STATUS_DEPRECATED: case NCX_STATUS_OBSOLETE: newval = xml_val_new_cstring(NCX_EL_STATUS, ncx_id, ncx_get_status_string(status)); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } break; default: SET_ERROR(ERR_INTERNAL_VAL); } /* add bit position clause */ num.u = bitpos; res = ncx_sprintf_num(numbuff, &num, NCX_BT_UINT32, &len); if (res != NO_ERR) { val_free_value(appval); return NULL; } newval = xml_val_new_cstring(NCX_EL_POSITION, ncx_id, numbuff); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } /* add an element for each appinfo record */ if (appinfoQ) { res = add_appinfoQ(appinfoQ, appval); if (res != NO_ERR) { val_free_value(appval); return NULL; } } return appval; } /* xsd_make_bit_appinfo */ /******************************************************************** * FUNCTION xsd_make_err_appinfo * * Make an appinfo node for the error-app-tag and or * error-message extensions * * INPUTS: * ref == reference clause (may be NULL) * errmsg == error msg string (1 may be NULL) * errtag == error app tag string (1 may be NULL) * * RETURNS: * malloced value containing the requested appinfo * or NULL if malloc error *********************************************************************/ val_value_t * xsd_make_err_appinfo (const xmlChar *ref, const xmlChar *errmsg, const xmlChar *errtag) { val_value_t *appval, *newval; xmlns_id_t ncx_id; #ifdef DEBUG if (!(ref || errmsg || errtag)) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif appval = xml_val_new_struct(NCX_EL_APPINFO, xmlns_xs_id()); if (!appval) { return NULL; } ncx_id = xmlns_ncx_id(); if (ref) { newval = make_reference(ref); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } if (errtag) { newval = xml_val_new_cstring(NCX_EL_ERROR_APP_TAG, ncx_id, errtag); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } if (errmsg) { newval = xml_val_new_cstring(NCX_EL_ERROR_MESSAGE, ncx_id, errmsg); if (!newval) { val_free_value(appval); return NULL; } else { val_add_child(newval, appval); } } return appval; } /* xsd_make_err_appinfo */ /******************************************************************** * FUNCTION xsd_make_schema_location * * Get the schemaLocation string for this module * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * INPUTS: * mod == module * schemaloc == CLI schmeloc parameter for URL start string * versionnames == TRUE if version in filename requested (CLI default) * == FALSE for no version names in the filenames * * RETURNS: * malloced string or NULL if malloc error *********************************************************************/ xmlChar * xsd_make_schema_location (const ncx_module_t *mod, const xmlChar *schemaloc, boolean versionnames) { const xmlChar *cstr; if (mod->nsid) { cstr = xmlns_get_ns_name(mod->nsid); } else { cstr = EMPTY_STRING; } if (!cstr) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } return make_schema_loc(schemaloc, mod->name, mod->version, cstr, versionnames); } /* xsd_make_schema_location */ /******************************************************************** * FUNCTION xsd_make_output_filename * * Construct an output filename spec, based on the * conversion parameters * * INPUTS: * mod == module * cp == conversion parameters to use * * RETURNS: * malloced string or NULL if malloc error *********************************************************************/ xmlChar * xsd_make_output_filename (const ncx_module_t *mod, const yangdump_cvtparms_t *cp) { xmlChar *buff, *p; const xmlChar *ext; uint32 len; switch (cp->format) { case NCX_CVTTYP_SQL: case NCX_CVTTYP_SQLDB: ext = (const xmlChar *)".sql"; break; case NCX_CVTTYP_HTML: if (cp->html_div) { ext = (const xmlChar *)".div"; } else { ext = (const xmlChar *)".html"; } break; case NCX_CVTTYP_XSD: ext = (const xmlChar *)".xsd"; break; case NCX_CVTTYP_H: case NCX_CVTTYP_UH: case NCX_CVTTYP_YH: ext = (const xmlChar *)".h"; break; case NCX_CVTTYP_C: case NCX_CVTTYP_UC: case NCX_CVTTYP_YC: ext = (const xmlChar *)".c"; break; case NCX_CVTTYP_CPP_TEST: ext = (const xmlChar *)".cpp"; break; case NCX_CVTTYP_YANG: case NCX_CVTTYP_COPY: ext = (const xmlChar *)".yang"; break; case NCX_CVTTYP_YIN: ext = (const xmlChar *)".yin"; break; case NCX_CVTTYP_TG2: /*** check cp for submode of TG2 ***/ ext = (const xmlChar *)".py"; break; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } if (cp->output && *cp->output) { len = xml_strlen(cp->full_output); if (cp->output_isdir) { if (cp->full_output[len-1] != NCXMOD_PSCHAR) { len++; } if (!cp->versionnames) { len += xml_strlen(mod->name) + xml_strlen(ext); } else if (mod->version) { len += xml_strlen(mod->name) + xml_strlen(mod->version) + 1 + xml_strlen(ext); } else { len += xml_strlen(mod->name) + xml_strlen(ext); } } } else { if (!cp->versionnames) { len = xml_strlen(mod->name) + xml_strlen(ext); } else if (mod->version) { len = xml_strlen(mod->name) + xml_strlen(mod->version) + 1 + xml_strlen(ext); } else { len = xml_strlen(mod->name) + xml_strlen(ext); } } switch (cp->format) { case NCX_CVTTYP_UC: case NCX_CVTTYP_UH: len += xml_strlen(NCX_USER_SIL_PREFIX); break; case NCX_CVTTYP_YC: case NCX_CVTTYP_YH: len += xml_strlen(NCX_YUMA_SIL_PREFIX); break; default: ; } buff = m__getMem(len+1); if (!buff) { return NULL; } p = buff; if (cp->output && *cp->output) { if (cp->output_isdir) { p += xml_strcpy(p, (const xmlChar *)cp->full_output); if (*(p-1) != NCXMOD_PSCHAR) { *p++ = NCXMOD_PSCHAR; } } else { /* !!! y_ and u_ not supported here !!! * user has specified the full file name */ xml_strcpy(p, (const xmlChar *)cp->full_output); return buff; } } switch (cp->format) { case NCX_CVTTYP_UC: case NCX_CVTTYP_UH: p += xml_strcpy(p, NCX_USER_SIL_PREFIX); break; case NCX_CVTTYP_YC: case NCX_CVTTYP_YH: p += xml_strcpy(p, NCX_YUMA_SIL_PREFIX); break; default: ; } p += xml_strcpy(p, mod->name); if (cp->versionnames && mod->version) { *p++ = YANG_FILE_SEPCHAR; p += xml_strcpy(p, mod->version); } xml_strcpy(p, ext); return buff; } /* xsd_make_output_filename */ /******************************************************************** * FUNCTION xsd_do_documentation * * Create a value struct representing the description node * This is complete; The val_free_value function must be called * with the return value if it is non-NULL; * * INPUTS: * descr == reference clause * val == struct to contain added clause * * OUTPUT: * val->v.childQ has entries added if descr is non-NULL * * RETURNS: * status *********************************************************************/ status_t xsd_do_documentation (const xmlChar *descr, val_value_t *val) { val_value_t *chnode; if (descr) { chnode = make_documentation(descr); if (!chnode) { return ERR_INTERNAL_MEM; } else { val_add_child(chnode, val); } } return NO_ERR; } /* xsd_do_documentation */ /******************************************************************** * FUNCTION xsd_do_reference * * Create a value struct representing the reference node * This is complete; The val_free_value function must be called * with the return value if it is non-NULL; * * INPUTS: * ref == reference clause * val == struct to contain added clause * * OUTPUT: * val->v.childQ has entries added if descr and ref are non-NULL * * RETURNS: * status *********************************************************************/ status_t xsd_do_reference (const xmlChar *ref, val_value_t *val) { val_value_t *chnode; if (ref) { chnode = make_reference(ref); if (!chnode) { return ERR_INTERNAL_MEM; } else { val_add_child(chnode, val); } } return NO_ERR; } /* xsd_do_reference */ /******************************************************************** * FUNCTION xsd_add_mod_documentation * * Create multiple elements representing the * module-level documentation node * * INPUTS: * mod == module in progress * annot == annotation node to add the documentation node into * * OUTPUTS: * annot->a.childQ has nodes added if NO_ERR * * RETURNS: * status *********************************************************************/ status_t xsd_add_mod_documentation (const ncx_module_t *mod, val_value_t *annot) { val_value_t *doc, *appinfo, *appval; const xmlChar *banner0, *banner1, *banner1b; xmlChar *str, *str2; uint32 len, versionlen; xmlns_id_t xsd_id, ncx_id; status_t res; xmlChar versionbuffer[NCX_VERSION_BUFFSIZE]; xsd_id = xmlns_xs_id(); ncx_id = xmlns_ncx_id(); res = ncx_get_version(versionbuffer, NCX_VERSION_BUFFSIZE); if (res != NO_ERR) { SET_ERROR(res); xml_strcpy(versionbuffer, (const xmlChar *)"unknown"); } versionlen = xml_strlen(versionbuffer); /* create first element */ banner0 = XSD_BANNER_0Y; banner1 = (mod->ismod) ? XSD_BANNER_1 : XSD_BANNER_1S; banner1b = (mod->ismod) ? NULL : XSD_BANNER_1B; /* figure out the buffer length first */ len = xml_strlen(banner0) + xml_strlen(mod->sourcefn) + xml_strlen(XSD_BANNER_0END) + versionlen + xml_strlen(banner1) + /* (Sub)Module */ xml_strlen(XSD_BANNER_3); /* Version */ if (mod->organization) { len += xml_strlen(XSD_BANNER_2) + /* organization */ xml_strlen(mod->organization); } if (banner1b) { len += xml_strlen(XSD_BANNER_1B) + xml_strlen(mod->belongs); } len += xml_strlen(mod->name); if (mod->version) { len += xml_strlen(mod->version); } else { len += xml_strlen(NCX_EL_NONE); } if (mod->contact_info) { len += (xml_strlen(XSD_BANNER_5) + xml_strlen(mod->contact_info)); } str = m__getMem(len+1); if (!str) { return ERR_INTERNAL_MEM; } /* build the module documentation in one buffer */ str2 = str; str2 += xml_strcpy(str2, banner0); str2 += xml_strcpy(str2, mod->sourcefn); str2 += xml_strcpy(str2, XSD_BANNER_0END); str2 += xml_strcpy(str2, versionbuffer); str2 += xml_strcpy(str2, banner1); str2 += xml_strcpy(str2, mod->name); if (banner1b) { str2 += xml_strcpy(str2, XSD_BANNER_1B); str2 += xml_strcpy(str2, mod->belongs); } if (mod->organization) { str2 += xml_strcpy(str2, XSD_BANNER_2); str2 += xml_strcpy(str2, mod->organization); } str2 += xml_strcpy(str2, XSD_BANNER_3); if (mod->version) { str2 += xml_strcpy(str2, mod->version); } else { str2 += xml_strcpy(str2, NCX_EL_NONE); } if (mod->contact_info) { str2 += xml_strcpy(str2, XSD_BANNER_5); str2 += xml_strcpy(str2, mod->contact_info); } doc = xml_val_new_string(XSD_DOCUMENTATION, xsd_id, str); if (!doc) { m__free(str); return ERR_INTERNAL_MEM; } else { val_add_child(doc, annot); } if (mod->descr) { doc = make_documentation(mod->descr); if (!doc) { return ERR_INTERNAL_MEM; } else { val_add_child(doc, annot); } } if (mod->ref || mod->source || mod->belongs || mod->organization || mod->contact_info) { appinfo = xml_val_new_struct(NCX_EL_APPINFO, xsd_id); if (!appinfo) { return ERR_INTERNAL_MEM; } else { val_add_child(appinfo, annot); } if (mod->ref) { appval = make_reference(mod->ref); if (!appval) { return ERR_INTERNAL_MEM; } else { val_add_child(appval, appinfo); } } if (mod->source) { appval = xml_val_new_cstring(NCX_EL_SOURCE, ncx_id, mod->source); if (!appval) { return ERR_INTERNAL_MEM; } else { val_add_child(appval, appinfo); } } if (mod->belongs) { appval = xml_val_new_cstring(YANG_K_BELONGS_TO, ncx_id, mod->belongs); if (!appval) { return ERR_INTERNAL_MEM; } else { val_add_child(appval, appinfo); } } if (mod->organization) { appval = xml_val_new_cstring(YANG_K_ORGANIZATION, ncx_id, mod->organization); if (!appval) { return ERR_INTERNAL_MEM; } else { val_add_child(appval, appinfo); } } if (mod->contact_info) { appval = xml_val_new_cstring(YANG_K_CONTACT, ncx_id, mod->contact_info); if (!appval) { return ERR_INTERNAL_MEM; } else { val_add_child(appval, appinfo); } } } if (!dlq_empty(&mod->revhistQ)) { appinfo = xml_val_new_struct(NCX_EL_APPINFO, xsd_id); if (!appinfo) { return ERR_INTERNAL_MEM; } else { val_add_child(appinfo, annot); } res = add_revhist(mod, appinfo); if (res != NO_ERR) { return res; } } if (!dlq_empty(&mod->appinfoQ)) { appval = make_appinfo(&mod->appinfoQ, NCX_ACCESS_NONE, NULL, NULL, NULL, NCX_STATUS_CURRENT); if (!appval) { return ERR_INTERNAL_MEM; } else { val_add_child(appval, annot); } } return NO_ERR; } /* xsd_add_mod_documentation */ /******************************************************************** * FUNCTION xsd_add_imports * * Add the required imports nodes * * INPUTS: * pcb == parser control block * mod == module in progress * cp == conversion parameters in use * val == struct parent to contain child nodes for each import * * OUTPUTS: * val->childQ has entries added for the imports required * * RETURNS: * status *********************************************************************/ status_t xsd_add_imports (yang_pcb_t *pcb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, val_value_t *val) { val_value_t *imval; const ncx_import_t *import; const yang_import_ptr_t *impptr; xmlChar *str; xmlns_id_t id; status_t res; boolean use_xsi; use_xsi = FALSE; id = xmlns_xs_id(); if (use_xsi) { /* add element to val for the XML namespace XSD */ imval = xml_val_new_flag(NCX_EL_IMPORT, id); if (!imval) { return ERR_INTERNAL_MEM; } else { val_add_child(imval, val); } res = xml_val_add_cattr(NCX_EL_NAMESPACE, 0, NCX_XML_URN, imval); if (res == NO_ERR) { res = xml_val_add_cattr(XSD_LOC, 0, NCX_XML_SLOC, imval); } if (res != NO_ERR) { return res; } } /* add import for NCX extensions used in the XSD, * but not imported into the data model module */ imval = xml_val_new_flag(NCX_EL_IMPORT, id); if (!imval) { return ERR_INTERNAL_MEM; } else { val_add_child(imval, val); } res = xml_val_add_cattr(NCX_EL_NAMESPACE, 0, NCX_URN, imval); if (res != NO_ERR) { return res; } str = make_schema_loc(cp->schemaloc, NCX_MOD, (cp->versionnames) ? mod->version : NULL, NCX_URN, cp->versionnames); if (str) { res = xml_val_add_attr(XSD_LOC, 0, str, imval); if (res != NO_ERR) { m__free(str); return res; } } else { return ERR_INTERNAL_MEM; } /* add XSD import for each YANG import */ if (cp->unified && mod->ismod) { for (impptr = (const yang_import_ptr_t *)dlq_firstEntry(&mod->saveimpQ); impptr != NULL; impptr = (const yang_import_ptr_t *)dlq_nextEntry(impptr)) { res = add_one_import(pcb, impptr->modname, impptr->revision, cp, val); if (res != NO_ERR) { return res; } } } else { for (import = (const ncx_import_t *)dlq_firstEntry(&mod->importQ); import != NULL; import = (const ncx_import_t *)dlq_nextEntry(import)) { res = add_one_import(pcb, import->module, import->revision, cp, val); if (res != NO_ERR) { return res; } } } return NO_ERR; } /* xsd_add_imports */ /******************************************************************** * FUNCTION xsd_add_includes * * Add the required include nodes * * INPUTS: * mod == module in progress * cp == conversion parameters in use * val == struct parent to contain child nodes for each include * * OUTPUTS: * val->childQ has entries added for the includes required * * RETURNS: * status *********************************************************************/ status_t xsd_add_includes (const ncx_module_t *mod, const yangdump_cvtparms_t *cp, val_value_t *val) { val_value_t *incval; const ncx_include_t *inc; xmlChar *str; xmlns_id_t id; status_t res; id = xmlns_xs_id(); for (inc = (const ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NO_ERR; inc = (const ncx_include_t *)dlq_nextEntry(inc)) { incval = xml_val_new_flag(NCX_EL_INCLUDE, id); if (!incval) { return ERR_INTERNAL_MEM; } else { val_add_child(incval, val); /* add early */ } str = make_include_location(inc->submodule, cp->schemaloc); if (!str) { return ERR_INTERNAL_MEM; } res = xml_val_add_attr(XSD_LOC, 0, str, incval); if (res != NO_ERR) { m__free(str); return res; } } return NO_ERR; } /* xsd_add_includes */ /******************************************************************** * FUNCTION xsd_new_element * * Set up a new struct as an element * * INPUTS: * mod == module conversion in progress * name == element name * typdef == typ def for child node in progress * parent == typ def for parent node in progress (may be NULL) * hasnodes == TRUE if there are child nodes for this element * == FALSE if this is an empty node, maybe with attributes * hasindex == TRUE if this type has an index clause * == FALSE if this type does not have an index clause * * RETURNS: * new element data struct or NULL if malloc error *********************************************************************/ val_value_t * xsd_new_element (const ncx_module_t *mod, const xmlChar *name, const typ_def_t *typdef, const typ_def_t *parent, boolean hasnodes, boolean hasindex) { return new_element(mod, name, typdef, parent, hasnodes, hasindex, NULL); } /* xsd_new_element */ /******************************************************************** * FUNCTION xsd_new_leaf_element * * Set up a new YANG leaf or leaf-list as an element * * INPUTS: * mod == module conversion in progress * obj == object to use (leaf of leaf-list) * hasnodes == TRUE if a struct should be used * FALSE if an empty element should be used * addtype == TRUE if a type attribute should be added * FALSE if type attribute should not be added * iskey == TRUE if a this is a key leaf * FALSE if this is not a key leaf * * RETURNS: * new element data struct or NULL if malloc error *********************************************************************/ val_value_t * xsd_new_leaf_element (const ncx_module_t *mod, const obj_template_t *obj, boolean hasnodes, boolean addtype, boolean iskey) { val_value_t *elem; const typ_def_t *typdef; const xmlChar *def, *leafdef; xmlChar numbuff[NCX_MAX_NUMLEN]; ncx_btype_t btyp; boolean isleaf; status_t res; if (obj->objtype == OBJ_TYP_LEAF) { isleaf = TRUE; typdef = obj->def.leaf->typdef; leafdef = obj->def.leaf->defval; def = typ_get_default(typdef); } else { isleaf = FALSE; typdef = obj->def.leaflist->typdef; leafdef = NULL; def = NULL; } if (hasnodes) { elem = xml_val_new_struct(XSD_ELEMENT, xmlns_xs_id()); } else { elem = xml_val_new_flag(XSD_ELEMENT, xmlns_xs_id()); } if (!elem) { return NULL; } /* add the name attribute */ res = xml_val_add_cattr(NCX_EL_NAME, 0, obj_get_name(obj), elem); if (res != NO_ERR) { val_free_value(elem); return NULL; } if (addtype) { btyp = typ_get_basetype(typdef); if (btyp != NCX_BT_EMPTY) { /* add the type attribute */ res = xsd_add_type_attr(mod, typdef, elem); if (res != NO_ERR) { val_free_value(elem); return NULL; } } } /* check if a default attribute is needed */ if (!iskey) { if (leafdef) { res = xml_val_add_cattr(NCX_EL_DEFAULT, 0, leafdef, elem); if (res != NO_ERR) { val_free_value(elem); return NULL; } } else if (def) { res = xml_val_add_cattr(NCX_EL_DEFAULT, 0, def, elem); if (res != NO_ERR) { val_free_value(elem); return NULL; } } } if (isleaf) { if (!iskey) { /* add the attributes for a leaf */ if (!(obj->flags & OBJ_FL_MANDATORY)) { res = xml_val_add_cattr(XSD_MIN_OCCURS, 0, XSD_ZERO, elem); } } } else { /* add the attributes for a leaf-list */ if (obj->def.leaflist->minset) { sprintf((char *)numbuff, "%u", obj->def.leaflist->minelems); res = xml_val_add_cattr(XSD_MIN_OCCURS, 0, numbuff, elem); } else { res = xml_val_add_cattr(XSD_MIN_OCCURS, 0, XSD_ZERO, elem); } if (res != NO_ERR) { val_free_value(elem); return NULL; } if (obj->def.leaflist->maxset) { if (obj->def.leaflist->maxelems) { sprintf((char *)numbuff, "%u", obj->def.leaflist->maxelems); } else { sprintf((char *)numbuff, "%s", XSD_UNBOUNDED); } res = xml_val_add_cattr(XSD_MAX_OCCURS, 0, numbuff, elem); } else { res = xml_val_add_cattr(XSD_MAX_OCCURS, 0, XSD_UNBOUNDED, elem); } if (res != NO_ERR) { val_free_value(elem); return NULL; } } return elem; } /* xsd_new_leaf_element */ /******************************************************************** * FUNCTION xsd_add_parmtype_attr * * Generate a type attribute for a type template * * INPUTS: * targns == targetNamespace ID * typ == type template for the parm * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->metaQ has an entry added for this attribute * * RETURNS: * status *********************************************************************/ status_t xsd_add_parmtype_attr (xmlns_id_t targns, const typ_template_t *typ, val_value_t *val) { xmlChar *str; status_t res; /* set the data type */ if (typ->nsid == targns) { /* this is in the targetNamespace */ return xml_val_add_cattr(NCX_EL_TYPE, 0, typ->name, val); } else { /* construct the QName for the attribute type */ str = xml_val_make_qname(typ->nsid, typ->name); if (!str) { return ERR_INTERNAL_MEM; } /* add the base or type attribute */ res = xml_val_add_attr(NCX_EL_TYPE, 0, str, val); if (res != NO_ERR) { m__free(str); return res; } } return NO_ERR; } /* xsd_add_parmtype_attr */ #if 0 /* complex types not supported in YANG */ /******************************************************************** * FUNCTION xsd_add_key * * Generate the 'key' node for a table or container element * * INPUTS: * name == name of node that contains the key * typch == typ_def_t for the child node to use to gen a key * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->v.childQ has entries added for the key * * RETURNS: * status *********************************************************************/ status_t xsd_add_key (const xmlChar *name, const typ_def_t *typdef, val_value_t *val) { val_value_t *node, *chnode; const typ_child_t *ch; const typ_index_t *in; const typ_def_t *realtypdef; const xmlChar *cstr; xmlChar *buff, *str; xmlns_id_t xsd_id; ncx_btype_t btyp; status_t res; xsd_id = xmlns_xs_id(); /* create a 'key' node */ node = xml_val_new_struct(XSD_KEY, xsd_id); if (!node) { return ERR_INTERNAL_MEM; } /* generate a key name */ buff = m__getMem(xml_strlen(name) + xml_strlen(XSD_KEY_SUFFIX) + 1); if (!buff) { val_free_value(node); return ERR_INTERNAL_MEM; } str = buff; str += xml_strcpy(str, name); str += xml_strcpy(str, XSD_KEY_SUFFIX); /* add the name attribute to the key node */ res = xml_val_add_attr(NCX_EL_NAME, 0, buff, node); if (res != NO_ERR) { m__free(buff); val_free_value(node); return ERR_INTERNAL_MEM; } /* add the selector child node */ chnode = xml_val_new_flag(XSD_SELECTOR, xsd_id); if (!chnode) { val_free_value(node); return ERR_INTERNAL_MEM; } else { val_add_child(chnode, node); /* add early */ } realtypdef = typ_get_cbase_typdef(typdef); btyp = typ_get_basetype(typdef); /* get the selector xpath value */ if (btyp == NCX_BT_LIST) { /* selector is the current node */ cstr = (const xmlChar *)"."; } else { /* type is NCX_BT_XCONTAINER and the selector is * the one and only child node */ ch = typ_first_child(&realtypdef->def.complex); if (!ch) { res = SET_ERROR(ERR_INTERNAL_VAL); } else { cstr = ch->name; } } /* add the xpath attribute to the selector node */ if (res == NO_ERR) { res = xml_val_add_cattr(NCX_EL_XPATH, 0, cstr, chnode); } if (res != NO_ERR) { val_free_value(node); return ERR_INTERNAL_MEM; } /* add all the field child nodes */ /* go through all the index nodes as field nodes */ for (in = typ_first_index(&realtypdef->def.complex); in != NULL; in = typ_next_index(in)) { /* add the selector child node */ chnode = xml_val_new_flag(XSD_FIELD, xsd_id); if (!chnode) { val_free_value(node); return ERR_INTERNAL_MEM; } else { val_add_child(chnode, node); /* add early */ } /* get the field string value based on the index type */ switch (in->ityp) { case NCX_IT_INLINE: case NCX_IT_NAMED: case NCX_IT_LOCAL: case NCX_IT_REMOTE: str = xml_strdup(in->typch.name); break; case NCX_IT_SLOCAL: case NCX_IT_SREMOTE: if (in->sname) { str = NULL; str = xml_strdup(in->sname); if (str) { /* convert the dot chars to forward slashes */ buff = str; while (*buff) { if (*buff==NCX_SCOPE_CH) { *buff = (xmlChar)'/'; } buff++; } } } else { str = xml_strdup(in->typch.name); } break; default: val_free_value(node); return SET_ERROR(ERR_INTERNAL_VAL); } if (!str) { val_free_value(node); return ERR_INTERNAL_MEM; } /* add the xpath attribute to the field node */ res = xml_val_add_attr(NCX_EL_XPATH, 0, str, chnode); if (res != NO_ERR) { m__free(str); val_free_value(node); return ERR_INTERNAL_MEM; } } val_add_child(node, val); return NO_ERR; } /* xsd_add_key */ #endif /******************************************************************** * FUNCTION xsd_do_annotation * * Add an annotation element if needed * Deprecated -- used by NCX only * * INPUTS: * descr == description string (may be NULL) * ref == reference string (may be NULL) * condition == condition string (may be NULL) * units == units clause contents (or NULL if not used) * maxacc == max-access clause (NONE == omit) * status == status clause (NONE or CURRENT to omit * appinfoQ == queue of cx_appinfo_t records (may be NULL) * val == struct parent to contain child nodes for this annotation * * OUTPUTS: * val->v.childQ has an entry added for the annotation if * any of the content parameters are actually present * * RETURNS: * status *********************************************************************/ status_t xsd_do_annotation (const xmlChar *descr, const xmlChar *ref, const xmlChar *condition, const xmlChar *units, ncx_access_t maxacc, ncx_status_t status, const dlq_hdr_t *appinfoQ, val_value_t *val) { val_value_t *annot, *chval; /* add an annotation node if there are any * description, condition, or appinfo clauses present */ if (descr || ref || condition || units || (appinfoQ && !dlq_empty(appinfoQ)) || maxacc != NCX_ACCESS_NONE || (status != NCX_STATUS_NONE && status != NCX_STATUS_CURRENT)) { annot = xml_val_new_struct(XSD_ANNOTATION, xmlns_xs_id()); if (!annot) { return ERR_INTERNAL_MEM; } if (descr) { chval = make_documentation(descr); if (!chval) { val_free_value(annot); return ERR_INTERNAL_MEM; } else { val_add_child(chval, annot); } } if ((appinfoQ && !dlq_empty(appinfoQ)) || maxacc != NCX_ACCESS_NONE || condition || units || ref || (status != NCX_STATUS_NONE && status != NCX_STATUS_CURRENT)) { chval = make_appinfo(appinfoQ, maxacc, condition, units, ref, status); if (!chval) { val_free_value(annot); return ERR_INTERNAL_MEM; } else { val_add_child(chval, annot); } } val_add_child(annot, val); } return NO_ERR; } /* xsd_do_annotation */ /******************************************************************** * FUNCTION xsd_make_obj_annotation * * Make an annotation element for an object, if needed * * INPUTS: * obj == obj_template to check * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced object value struct * NULL if nothing to do (*res == NO_ERR) * NULL if some error (*res != NO_ERR) *********************************************************************/ val_value_t * xsd_make_obj_annotation (obj_template_t *obj, status_t *res) { val_value_t *annot, *doc, *appinfo; const xmlChar *descr; #ifdef DEBUG if (!obj || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif annot = NULL; doc = NULL; appinfo = NULL; descr = obj_get_description(obj); if (descr) { doc = make_documentation(descr); if (!doc) { *res = ERR_INTERNAL_MEM; return NULL; } } appinfo = make_obj_appinfo(obj, res); if (*res != NO_ERR) { if (doc) { val_free_value(doc); } return NULL; } if (doc != NULL || appinfo != NULL) { annot = xml_val_new_struct(XSD_ANNOTATION, xmlns_xs_id()); if (!annot) { if (appinfo) { val_free_value(appinfo); } if (doc) { val_free_value(doc); } *res = ERR_INTERNAL_MEM; return NULL; } else { if (doc) { val_add_child(doc, annot); } if (appinfo) { val_add_child(appinfo, annot); } } } *res = NO_ERR; return annot; } /* xsd_make_obj_annotation */ /******************************************************************** * FUNCTION xsd_do_type_annotation * * Add an annotation element for a typedef, if needed * * INPUTS: * typ == typ_template to check * val == value struct to add nodes to * * OUTPUTS: * val->v.childQ has an node added, if needed * * RETURNS: * status *********************************************************************/ status_t xsd_do_type_annotation (const typ_template_t *typ, val_value_t *val) { val_value_t *annot, *chval; const xmlChar *path; xmlns_id_t xsd_id; status_t res; #ifdef DEBUG if (!typ || !val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif path = NULL; annot = NULL; res = NO_ERR; if (typ->typdef.tclass == NCX_CL_SIMPLE && typ_get_basetype(&typ->typdef)==NCX_BT_LEAFREF) { path = typ_get_leafref_path(&typ->typdef); } if (typ->descr || typ->ref || typ->defval || typ->units || path || !dlq_empty(&typ->appinfoQ) || (typ->status != NCX_STATUS_NONE && typ->status != NCX_STATUS_CURRENT)) { xsd_id = xmlns_xs_id(); annot = xml_val_new_struct(XSD_ANNOTATION, xsd_id); if (!annot) { return ERR_INTERNAL_MEM; } else { val_add_child(annot, val); } if (typ->descr) { chval = make_documentation(typ->descr); if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, annot); } } if (!dlq_empty(&typ->appinfoQ) || typ->ref || typ->units || typ->defval || path || (typ->status != NCX_STATUS_NONE && typ->status != NCX_STATUS_CURRENT)) { chval = make_type_appinfo(typ, path); if (!chval) { return ERR_INTERNAL_MEM; } else { val_add_child(chval, annot); } } } return res; } /* xsd_make_type_annotation */ /******************************************************************** * FUNCTION xsd_make_group_annotation * * Make an annotation element for a grouping, if needed * * INPUTS: * grp == grp_template to check * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced object value struct * NULL if nothing to do (*res == NO_ERR) * NULL if some error (*res != NO_ERR) *********************************************************************/ val_value_t * xsd_make_group_annotation (const grp_template_t *grp, status_t *res) { val_value_t *annot, *chval; xmlns_id_t xsd_id; #ifdef DEBUG if (!grp || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif annot = NULL; if (grp->descr || grp->ref || !dlq_empty(&grp->appinfoQ) || (grp->status != NCX_STATUS_NONE && grp->status != NCX_STATUS_CURRENT)) { xsd_id = xmlns_xs_id(); annot = xml_val_new_struct(XSD_ANNOTATION, xsd_id); if (!annot) { *res = ERR_INTERNAL_MEM; return NULL; } if (grp->descr) { chval = make_documentation(grp->descr); if (!chval) { val_free_value(annot); *res = ERR_INTERNAL_MEM; return NULL; } else { val_add_child(chval, annot); } } if (grp->ref || !dlq_empty(&grp->appinfoQ) || (grp->status != NCX_STATUS_NONE && grp->status != NCX_STATUS_CURRENT)) { chval = make_appinfo(&grp->appinfoQ, NCX_ACCESS_NONE, NULL, NULL, grp->ref, grp->status); if (!chval) { val_free_value(annot); *res = ERR_INTERNAL_MEM; return NULL; } else { val_add_child(chval, annot); } } } *res = NO_ERR; return annot; } /* xsd_make_group_annotation */ /******************************************************************** * FUNCTION xsd_add_aughook * * Add an abstract element to set a substitutionGroup that * can be used by an augment clause to create new nodes * within an existing structure. This allows any namespace, * not just external namespaces like xsd_add_any * * augment /foo/bar --> substitutionGroup='__.foo.bar.A__' * * This function is called for /foo and /foo/bar, which * creates abstract elements /foo/__.foo.A__ and /foo/bar/__.foo.bar.A__ * * The YANG name length restrictions, and internal NcxName * restrictions do not matter since these constructed element names * are only used within the XSD * * INPUTS: * val == val_value_t struct to add new last child leaf * obj == object to use as the template for the new last leaf * * RETURNS: * status *********************************************************************/ status_t xsd_add_aughook (val_value_t *val) { val_value_t *aug; status_t res; aug = xml_val_new_flag(XSD_ANY, xmlns_xs_id()); if (!aug) { return ERR_INTERNAL_MEM; } else { val_add_child(aug, val); /* add early */ } res = xml_val_add_cattr(XSD_MIN_OCCURS, 0, XSD_ZERO, aug); if (res != NO_ERR) { return res; } res = xml_val_add_cattr(XSD_MAX_OCCURS, 0, XSD_UNBOUNDED, aug); if (res != NO_ERR) { return res; } res = xml_val_add_cattr(XSD_NAMESPACE, 0, XSD_OTHER, aug); if (res != NO_ERR) { return res; } res = xml_val_add_cattr(XSD_PROC_CONTENTS, 0, XSD_LAX, aug); if (res != NO_ERR) { return res; } return NO_ERR; } /* xsd_add_aughook */ /******************************************************************** * FUNCTION xsd_make_rpc_output_typename * * Create a type name for the RPC function output data structure * * INPUTS: * obj == object to make an RPC name from * * RETURNS: * malloced buffer with the typename *********************************************************************/ xmlChar * xsd_make_rpc_output_typename (const obj_template_t *obj) { const xmlChar *name; xmlChar *buff, *p; uint32 len; name = obj_get_name(obj); len = xml_strlen(name) + xml_strlen(XSD_OUTPUT_TYPEEXT); buff = m__getMem(len+1); if (!buff) { return NULL; } p = buff; p += xml_strcpy(p, name); p += xml_strcpy(p, XSD_OUTPUT_TYPEEXT); /*** ADD NAME COLLISION DETECTION LATER !!! ***/ return buff; } /* xsd_make_rpc_output_typename */ /******************************************************************** * FUNCTION xsd_add_type_attr * * Generate a type attribute * * INPUTS: * mod == module in progress * contains targetNamespace ID * typdef == typ_def for the typ_template struct to use for * the attribute list source * val == struct parent to contain child nodes for each type * * OUTPUTS: * val->metaQ has an entry added for this attribute * * RETURNS: * status *********************************************************************/ status_t xsd_add_type_attr (const ncx_module_t *mod, const typ_def_t *typdef, val_value_t *val) { return add_basetype_attr(TRUE, mod, typdef, val); } /* xsd_add_type_attr */ /******************************************************************** * FUNCTION test_basetype_attr * * Test for the OK generate a type or base attribute * * INPUTS: * mod == module in progress * typdef == typ_def for the typ_template struct to use for * the attribute list source * * RETURNS: * status *********************************************************************/ status_t test_basetype_attr (const ncx_module_t *mod, const typ_def_t *typdef) { status_t res; xmlns_id_t typ_id; ncx_btype_t btyp; const xmlChar *test; #ifdef DEBUG if (mod == NULL || typdef == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; /* figure out the correct namespace and name for the typename */ switch (typdef->tclass) { case NCX_CL_BASE: case NCX_CL_SIMPLE: break; case NCX_CL_COMPLEX: btyp = typ_get_basetype(typdef); switch (btyp) { case NCX_BT_ANYDATA: case NCX_BT_ANYXML: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case NCX_CL_NAMED: typ_id = typdef->def.named.typ->nsid; if (typ_id == 0) { /* inside a grouping, include submod, etc. * this has to be a type in the current [sub]module * or it would have a NSID assigned already * * The design of the local typename to * global XSD typename translation does not * put top-level typedefs in the typenameQ. * They are already in the registry, so name collision * can be checked that way */ test = ncx_find_typname(typdef->def.named.typ, &mod->typnameQ); if (test == NULL) { res = ERR_NCX_SKIPPED; } } break; case NCX_CL_REF: default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* test_basetype_attr */ /******************************************************************** * FUNCTION get_next_seqnum * * Get a unique integer * * RETURNS: * next seqnum *********************************************************************/ uint32 get_next_seqnum (void) { return seqnum++; } /* get_next_seqnum */ /* END file xsd_util.c */ yuma123_2.14/netconf/src/ydump/html.h0000664000175000017500000000473514770023131017632 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_html #define _H_html /* FILE: html.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert YANG module to HTML documentation format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 23-feb-08 abb Begun */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION html_convert_module * * Generate HTML version of the specified module(s) * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * * RETURNS: * status *********************************************************************/ extern status_t html_convert_module (yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_html */ yuma123_2.14/netconf/src/ydump/c_util.h0000664000175000017500000005626014770023131020145 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_c_util #define _H_c_util /* FILE: c_util.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* YANGDUMP C code generation utilities ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24-oct-09 abb Begun */ #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define START_COMMENT (const xmlChar *)"\n/* " #define END_COMMENT (const xmlChar *)" */" #define START_BLOCK (const xmlChar *)" {" #define END_BLOCK (const xmlChar *)"\n}" #define BAR_H (const xmlChar *)"_H_" #define DOT_H (const xmlChar *)".h" #define BAR_FEAT (const xmlChar *)"F" #define BAR_ID (const xmlChar *)"I" #define BAR_NODE (const xmlChar *)"N" #define BAR_MOD (const xmlChar *)"M" #define BAR_REV (const xmlChar *)"R" #define DEF_TYPE (const xmlChar *)"T" #define BAR_CONST (const xmlChar *)"(const xmlChar *)" #define POUND_DEFINE (const xmlChar *)"\n#define " #define POUND_ENDIF (const xmlChar *)"\n#endif" #define POUND_IF (const xmlChar *)"\n#if " #define POUND_IFDEF (const xmlChar *)"\n#ifdef " #define POUND_IFNDEF (const xmlChar *)"\n#ifndef " #define POUND_INCLUDE (const xmlChar *)"\n#include " #define POUND_ELSE (const xmlChar *)"\n#else" #define START_DEFINED (const xmlChar *)"defined(" #define START_TYPEDEF (const xmlChar *)"\ntypedef " #define INT8 (const xmlChar *)"int8" #define INT16 (const xmlChar *)"int16" #define INT32 (const xmlChar *)"int32" #define INT64 (const xmlChar *)"int64" #define UINT8 (const xmlChar *)"uint8" #define UINT16 (const xmlChar *)"uint16" #define UINT32 (const xmlChar *)"uint32" #define UINT64 (const xmlChar *)"uint64" #define STRING (const xmlChar *)"xmlChar *" #define IDREF (const xmlChar *)"val_idref_t *" #define BOOLEAN (const xmlChar *)"boolean" #define FLOAT (const xmlChar *)"float" #define DOUBLE (const xmlChar *)"double" #define BITS (const xmlChar *)"ncx_list_t" #define STRUCT (const xmlChar *)"struct" #define UNION (const xmlChar *)"union" #define QHEADER (const xmlChar *)"dlq_hdr_t qhdr;" #define QUEUE (const xmlChar *)"dlq_hdr_t" #define START_LINE (const xmlChar *)"\n " #define PARM_DSTVAL (const xmlChar *)"dstval" #define PARM_ERRORVAL (const xmlChar *)"errorval" #define FN_BANNER_START (const xmlChar *)\ "\n/*******************************************************"\ "*************\n* FUNCTION " #define FN_BANNER_LN (const xmlChar *)"\n* " #define FN_BANNER_INPUT (const xmlChar *)"\n* INPUTS:\n* " #define FN_BANNER_RETURN (const xmlChar *)"\n* RETURNS:\n* " #define FN_BANNER_RETURN_STATUS (const xmlChar *)\ "\n* RETURNS:\n* error status" #define FN_BANNER_END (const xmlChar *)\ "\n********************************************************************/" #define FN_INIT_STATIC_VARS (const xmlChar *)"init_static_vars" #define FN_EDIT (const xmlChar *)"edit" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ typedef enum c_mode_t_ { C_MODE_NONE, C_MODE_OID, C_MODE_TYPEDEF, C_MODE_CALLBACK, C_MODE_VARNAME } c_mode_t; /* ID to name string binding for #define statements */ typedef struct c_define_t_ { dlq_hdr_t qhdr; xmlChar *idstr; xmlChar *typstr; /* when c_def for object */ xmlChar *varstr; /* when c_def for object */ xmlChar *valstr; obj_template_t *obj; /* back-ptr to object for typdef */ } c_define_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION need_rpc_includes * * Check if the include-stmts for RPC methods are needed * * INPUTS: * mod == module in progress * cp == conversion parameters to use * * RETURNS: * TRUE if RPCs found * FALSE if no RPCs found *********************************************************************/ extern boolean need_rpc_includes (const ncx_module_t *mod, const yangdump_cvtparms_t *cp); /******************************************************************** * FUNCTION need_notif_includes * * Check if the include-stmts for notifications are needed * * INPUTS: * mod == module in progress * cp == conversion parameters to use * * RETURNS: * TRUE if notifcations found * FALSE if no notifications found *********************************************************************/ extern boolean need_notif_includes (const ncx_module_t *mod, const yangdump_cvtparms_t *cp); /******************************************************************** * FUNCTION write_c_safe_str * * Generate a string token at the current line location * * INPUTS: * scb == session control block to use for writing * strval == string value *********************************************************************/ extern void write_c_safe_str (ses_cb_t *scb, const xmlChar *strval); /******************************************************************** * FUNCTION write_c_str * * Generate a string token at the current line location * * INPUTS: * scb == session control block to use for writing * strval == string value * quotes == quotes style (0, 1, 2) *********************************************************************/ extern void write_c_str (ses_cb_t *scb, const xmlChar *strval, uint32 quotes); /******************************************************************** * FUNCTION write_c_simple_str * * Generate a simple clause on 1 line * * INPUTS: * scb == session control block to use for writing * kwname == keyword name * strval == string value * indent == indent count to use * quotes == quotes style (0, 1, 2) *********************************************************************/ extern void write_c_simple_str (ses_cb_t *scb, const xmlChar *kwname, const xmlChar *strval, int32 indent, uint32 quotes); /******************************************************************** * * FUNCTION write_identifier * * Generate an identifier * * #module_DEFTYPE_idname * * INPUTS: * scb == session control block to use for writing * modname == module name start-string to use * defpart == internal string for deftype part (may be NULL) * idname == identifier name * isuser == TRUE if USER SIL file * FALSE if YUMA SIL file *********************************************************************/ extern void write_identifier (ses_cb_t *scb, const xmlChar *modname, const xmlChar *defpart, const xmlChar *idname, boolean isuser); /******************************************************************** * FUNCTION write_ext_include * * Generate an include statement for an external file * * #include * * INPUTS: * scb == session control block to use for writing * hfile == H file name == file name to include (foo.h) * *********************************************************************/ extern void write_ext_include (ses_cb_t *scb, const xmlChar *hfile); /******************************************************************** * FUNCTION write_ncx_include * * Generate an include statement for an NCX file * * #ifndef _H_foo * #include "foo,h" * * INPUTS: * scb == session control block to use for writing * modname == module name to include (foo) * *********************************************************************/ extern void write_ncx_include (ses_cb_t *scb, const xmlChar *modname); /******************************************************************** * FUNCTION write_cvt_include * * Generate an include statement for an NCX split SIL file * based on the format type * * INPUTS: * scb == session control block to use for writing * modname == module name to include (foo) * cvttyp == format enum to use *********************************************************************/ extern void write_cvt_include (ses_cb_t *scb, const xmlChar *modname, ncx_cvttyp_t cvttyp); /******************************************************************** * FUNCTION write_qheader * * Generate a QHEADER with indentation * * \n{indentcnt}QHEADER * * INPUTS: * scb == session control block to use for writing * *********************************************************************/ extern void write_qheader (ses_cb_t *scb); /******************************************************************** * FUNCTION save_oid_cdefine * * Generate a #define binding for a definition and save it in the * specified Q of c_define_t structs * * INPUTS: * cdefineQ == Q of c_define_t structs to use * modname == module name to use * defname == object definition name to use * * OUTPUTS: * a new c_define_t is allocated and added to the cdefineQ * if returning NO_ERR; * * RETURNS: * status; duplicate C identifiers not supported yet * foo-1 --> foo_1 * foo.1 --> foo_1 * An error message will be generated if this type of error occurs *********************************************************************/ extern status_t save_oid_cdefine (dlq_hdr_t *cdefineQ, const xmlChar *modname, const xmlChar *defname); /******************************************************************** * FUNCTION save_path_cdefine * * Generate a #define binding for a definition and save it in the * specified Q of c_define_t structs * * INPUTS: * cdefineQ == Q of c_define_t structs to use * modname == base module name to use * obj == object struct to use to generate path * cmode == mode to use * * OUTPUTS: * a new c_define_t is allocated and added to the cdefineQ * if returning NO_ERR; * * RETURNS: * status; duplicate C identifiers not supported yet * foo-1/a/b --> foo_1_a_b * foo.1/a.2 --> foo_1_a_b * An error message will be generated if this type of error occurs *********************************************************************/ extern status_t save_path_cdefine (dlq_hdr_t *cdefineQ, const xmlChar *modname, obj_template_t *obj, c_mode_t cmode); /******************************************************************** * FUNCTION find_path_cdefine * * Find a #define binding for a definition in the * specified Q of c_define_t structs * * INPUTS: * cdefineQ == Q of c_define_t structs to use * obj == object struct to find * * RETURNS: * pointer to found entry * NULL if not found *********************************************************************/ extern c_define_t * find_path_cdefine (dlq_hdr_t *cdefineQ, const obj_template_t *obj); /******************************************************************** * FUNCTION clean_cdefineQ * * Clean a Q of c_define_t structs * * INPUTS: * cdefineQ == Q of c_define_t structs to use *********************************************************************/ extern void clean_cdefineQ (dlq_hdr_t *cdefineQ); /******************************************************************** * FUNCTION write_c_header * * Write the C file header * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * *********************************************************************/ extern void write_c_header (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp); /******************************************************************** * FUNCTION write_c_footer * * Write the C file footer * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use *********************************************************************/ extern void write_c_footer (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp); /******************************************************************* * FUNCTION write_c_objtype * * Generate the C data type for the NCX data type * * INPUTS: * scb == session control block to use for writing * obj == object template to check * **********************************************************************/ extern void write_c_objtype (ses_cb_t *scb, const obj_template_t *obj); /******************************************************************* * FUNCTION write_c_objtype_ex * * Generate the C data type for the NCX data type * * INPUTS: * scb == session control block to use for writing * obj == object template to check * cdefQ == Q of c_define_t to check for obj * endchar == char to use at end (semi-colon, comma, right-paren) * isconst == TRUE if a const pointer is needed * FALSE if pointers should not be 'const' * needstar == TRUE if this object reference is a reference * or a pointer to the data type * FALSE if this is a direct usage of the object data type * !! only affects complex types, not simple types **********************************************************************/ extern void write_c_objtype_ex (ses_cb_t *scb, const obj_template_t *obj, dlq_hdr_t *cdefQ, xmlChar endchar, boolean isconst, boolean needstar); /******************************************************************* * FUNCTION write_c_objtype_max * * Generate the C data type for the NCX data type * * INPUTS: * scb == session control block to use for writing * obj == object template to check * cdefQ == Q of c_define_t to check for obj * endchar == char to use at end (semi-colon, comma, right-paren) * isconst == TRUE if a const pointer is needed * FALSE if pointers should not be 'const' * needstar == TRUE if this object reference is a reference * or a pointer to the data type * FALSE if this is a direct usage of the object data type * !! only affects complex types, not simple types * usename == TRUE to use object name as the variable name * FALSE to use the idstr as the variable name * useprefix == TRUE to use object name as the variable name * FALSE to use the idstr as the variable name; * ignored if usename == TRUE * isuser == TRUE if format is NCX_CVTTYP_UC or NCX_CVTTYP_UH * FALSE otherwise; ignored if useprefix == FALSE * isvar == TRUE if cdef->varstr should be used * FALSE if cdef->idstr should be used; * ignored if usename == TRUE **********************************************************************/ extern void write_c_objtype_max (ses_cb_t *scb, const obj_template_t *obj, dlq_hdr_t *cdefQ, xmlChar endchar, boolean isconst, boolean needstar, boolean usename, boolean useprefix, boolean isuser, boolean isvar); /******************************************************************* * FUNCTION write_c_val_macro_type * * Generate the C VAL_FOO macro name for the data type * * INPUTS: * scb == session control block to use for writing * obj == object template to check * **********************************************************************/ extern void write_c_val_macro_type (ses_cb_t *scb, const obj_template_t *obj); /******************************************************************* * FUNCTION write_c_oid_comment * * Generate the object OID as a comment line * * INPUTS: * scb == session control block to use for writing * obj == object template to check * **********************************************************************/ extern void write_c_oid_comment (ses_cb_t *scb, const obj_template_t *obj); /******************************************************************** * FUNCTION save_c_objects * * save the path name bindings for C typdefs * * INPUTS: * mod == module in progress * datadefQ == que of obj_template_t to use * savecdefQ == Q of c_define_t structs to use * cmode == C code generating mode to use * * OUTPUTS: * savecdefQ may get new structs added * * RETURNS: * status *********************************************************************/ extern status_t save_c_objects (ncx_module_t *mod, dlq_hdr_t *datadefQ, dlq_hdr_t *savecdefQ, c_mode_t cmode); /******************************************************************** * FUNCTION save_all_c_objects * * save the path name bindings for C typdefs for mod and all submods * * INPUTS: * mod == module in progress * cp == conversion parameters to use * savecdefQ == Q of c_define_t structs to use * cmode == C code generating mode to use * * OUTPUTS: * savecdefQ may get new structs added * * RETURNS: * status *********************************************************************/ extern status_t save_all_c_objects (ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *savecdefQ, c_mode_t cmode); /******************************************************************** * FUNCTION skip_c_top_object * * Check if a top-level object should be skipped * in C file SIL code generation * * INPUTS: * obj == object to check * * RETURNS: * TRUE if object should be skipped * FALSE if object should not be skipped *********************************************************************/ extern boolean skip_c_top_object (obj_template_t *obj); /******************************************************************** * FUNCTION write_c_key_params * * Write all the keys in C function aprameter list format * * INPUTS: * scb == session to use * obj == object to start from (ancestor-or-self) * objnameQ == Q of name-to-idstr mappings * keycount == number of key leafs expected; used to * identify last key to suppress ending comma * startindent == start indent count * *********************************************************************/ extern void write_c_key_params(ses_cb_t *scb, obj_template_t *obj, dlq_hdr_t *objnameQ, uint32 keycount, int32 startindent); /******************************************************************** * FUNCTION write_c_key_vars * * Write all the local key variables in the SIL C function * * INPUTS: * scb == session to use * obj == object to start from (ancestor-or-self) * objnameQ == Q of name-to-idstr mappings * parmname == name of parameter used in C code * keycount == number of key leafs expected; used to * identify last key to suppress ending comma * startindent == start indent count * *********************************************************************/ extern void write_c_key_vars (ses_cb_t *scb, obj_template_t *obj, dlq_hdr_t *objnameQ, const xmlChar *parmname, uint32 keycount, int32 startindent); /******************************************************************** * FUNCTION write_c_key_values * * Write all the keys in call-C-function-to-get-key-value format * * INPUTS: * scb == session to use * obj == object to start from (ancestor-or-self) * objnameQ == Q of name-to-idstr mappings * keycount == number of key leafs expected; used to * identify last key to suppress ending comma * startindent == start indent count * *********************************************************************/ extern void write_c_key_values (ses_cb_t *scb, obj_template_t *obj, dlq_hdr_t *objnameQ, uint32 keycount, int32 startindent); /******************************************************************** * FUNCTION write_h_iffeature_start * * Generate the start C for 1 if-feature conditional; * * INPUTS: * scb == session control block to use for writing * iffeatureQ == Q of ncx_feature_t to use * *********************************************************************/ extern void write_h_iffeature_start (ses_cb_t *scb, const dlq_hdr_t *iffeatureQ); /******************************************************************** * FUNCTION write_h_iffeature_end * * Generate the end C for 1 if-feature conditiona; * * INPUTS: * scb == session control block to use for writing * iffeatureQ == Q of ncx_feature_t to use * *********************************************************************/ extern void write_h_iffeature_end (ses_cb_t *scb, const dlq_hdr_t *iffeatureQ); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_c_util */ yuma123_2.14/netconf/src/ydump/c.h0000664000175000017500000000634314770023131017105 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_c #define _H_c /* FILE: c.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert YANG module to C file format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 27-oct-09 abb Begun */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION c_convert_module * * Generate the SIL C code for the specified module(s) * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * * RETURNS: * status *********************************************************************/ extern status_t c_convert_module (yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb); /******************************************************************** * FUNCTION c_write_fn_prototypes * * Generate the SIL H code for the external function definitions * in the specified module, already parsed and H file generation * is in progress * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * objnameQ == Q of c_define_t mapping structs to use * *********************************************************************/ extern void c_write_fn_prototypes (ncx_module_t *mod, const yangdump_cvtparms_t *cp, ses_cb_t *scb, dlq_hdr_t *objnameQ); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_c */ yuma123_2.14/netconf/src/ydump/tg2.h0000664000175000017500000000532414770023131017355 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_tg2 #define _H_tg2 /* FILE: tg2.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert YANG module to Turbogears 2 Source Files ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11-mar-10 abb Begun */ #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* SQL database definition limits */ #define MAX_VERSION_LEN 32 #define MAX_URL_LEN 255 #define MAX_OBJECTID_LEN 900 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION tg2_convert_module_model * * Convert the YANG module to python for the * TG2 model/foo.py file * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == convert parms struct to use * scb == session to use for output * * RETURNS: * status *********************************************************************/ extern status_t tg2_convert_module_model (const yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_tg2 */ yuma123_2.14/netconf/src/ydump/ydump.h0000664000175000017500000000741314770023131020020 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ydump #define _H_ydump /* FILE: ydump.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Exports yangdump.yang conversion CLI parameter struct ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 01-mar-08 abb Begun; moved from ncx/ncxtypes.h */ #ifndef _H_help #include "help.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * FUNCTION ydump_init * * Parse all CLI parameters and fill in the conversion parameters * * INPUTS: * argc == number of argv parameters * argv == array of CLI parameters * allowcode == TRUE if code generation should be allowed * FALSE otherwise * cvtparms == address of conversion parms to fill in * * OUTPUTS: * *cvtparms filled in, maybe partial if errors * * RETURNS: * status *********************************************************************/ extern status_t ydump_init (int argc, char *argv[], boolean allowcode, yangdump_cvtparms_t *cvtparms); /******************************************************************** * FUNCTION ydump_main * * Run the yangdump conversion, according to the specified * yangdump conversion parameters set * * INPUTS: * cvtparms == conversion parameters to use * * OUTPUTS: * the conversion (if any) will be done and output to * the specified files, or STDOUT * * RETURNS: * status *********************************************************************/ extern status_t ydump_main (yangdump_cvtparms_t *cvtparms); /******************************************************************** * FUNCTION ydump_cleanup * * INPUTS: * cvtparms == conversion parameters to clean but not free * *********************************************************************/ extern void ydump_cleanup (yangdump_cvtparms_t *cvtparms); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_ydump */ yuma123_2.14/netconf/src/ydump/xsd_yang.h0000664000175000017500000001050514770023131020472 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xsd_yang #define _H_xsd_yang /* FILE: xsd_yang.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert YANG-specific constructs to XSD format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 04-feb-08 abb Begun; split from xsd_typ.h */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xsd_add_groupings * * Add the required group nodes * * INPUTS: * mod == module in progress * val == struct parent to contain child nodes for each group * * OUTPUTS: * val->childQ has entries added for the groupings required * * RETURNS: * status *********************************************************************/ extern status_t xsd_add_groupings (ncx_module_t *mod, val_value_t *val); /******************************************************************** * FUNCTION xsd_add_objects * * Add the required element nodes for each object in the module * RPC methods and notifications are mixed in with the objects * * INPUTS: * mod == module in progress * val == struct parent to contain child nodes for each object * * OUTPUTS: * val->childQ has entries added for the RPC methods in this module * * RETURNS: * status *********************************************************************/ extern status_t xsd_add_objects (ncx_module_t *mod, val_value_t *val); /******************************************************************** * FUNCTION xsd_do_typedefs_groupingQ * * Analyze the entire 'groupingQ' within the module struct * Generate local typedef mappings as needed * * INPUTS: * mod == module conversion in progress * groupingQ == Q of grp_template_t structs to check * typnameQ == Q of xsd_typname_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each type * * RETURNS: * status *********************************************************************/ extern status_t xsd_do_typedefs_groupingQ (ncx_module_t *mod, dlq_hdr_t *groupingQ, dlq_hdr_t *typnameQ); /******************************************************************** * FUNCTION xsd_do_typedefs_datadefQ * * Analyze the entire 'datadefQ' within the module struct * Generate local typedef mappings as needed * * INPUTS: * mod == module conversion in progress * datadefQ == Q of obj_template_t structs to check * typnameQ == Q of xsd_typname_t to hold new local type names * * OUTPUTS: * typnameQ has entries added for each type * * RETURNS: * status *********************************************************************/ extern status_t xsd_do_typedefs_datadefQ (ncx_module_t *mod, dlq_hdr_t *datadefQ, dlq_hdr_t *typnameQ); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xsd_yang */ yuma123_2.14/netconf/src/ydump/html.c0000664000175000017500000047367514770023131017642 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: html.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 23feb08 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_html #include "html.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xsd_util #include "xsd_util.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifndef _H_yangdump_util #include "yangdump_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define CSS_CONTENT_FILE (const xmlChar *)"yangdump-css-contents.txt" #define SPACE_CH (const xmlChar *)" " #define START_SEC (const xmlChar *)" {" #define END_SEC (const xmlChar *)"}" #define MENU_NEST_LEVEL 4 #define MENU_LABEL_LEN 30 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static void write_typedefs (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *typedefQ, int32 startindent); static void write_groupings (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *groupingQ, int32 startindent); static void write_type_clause (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, typ_def_t *typdef, obj_template_t *obj, int32 startindent); static void write_objects (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *datadefQ, int32 startindent); /******************************************************************** * FUNCTION has_nl * * Check if string has newlines * * INPUTS: * str == string to use * *********************************************************************/ static boolean has_nl (const xmlChar *str) { while (*str && *str != '\n') { str++; } return (*str) ? TRUE : FALSE; } /* has_nl */ /******************************************************************** * FUNCTION start_elem * * Generate a start tag * * INPUTS: * scb == session control block to use for writing * name == element name * css_class == CSS class to use (may be NULL) * indent == indent count * *********************************************************************/ static void start_elem (ses_cb_t *scb, const xmlChar *name, const xmlChar *css_class, int32 indent) { ses_putstr_indent(scb, (const xmlChar *)"<", indent); ses_putstr(scb, name); if (css_class) { ses_putstr(scb, (const xmlChar *)" class=\""); ses_putstr(scb, css_class); ses_putstr(scb, (const xmlChar *)"\">"); } else { ses_putchar(scb, '>'); } } /* start_elem */ /******************************************************************** * FUNCTION start_id_elem * * Generate a start tag with an id instead of class attribute * * INPUTS: * scb == session control block to use for writing * name == element name * id == CSS id to use (may be NULL) * indent == indent count * *********************************************************************/ static void start_id_elem (ses_cb_t *scb, const xmlChar *name, const xmlChar *id, int32 indent) { ses_putstr_indent(scb, (const xmlChar *)"<", indent); ses_putstr(scb, name); if (id) { ses_putstr(scb, (const xmlChar *)" id=\""); ses_putstr(scb, id); ses_putstr(scb, (const xmlChar *)"\">"); } else { ses_putchar(scb, '>'); } } /* start_id_elem */ /******************************************************************** * FUNCTION end_elem * * Generate an end tag * * INPUTS: * scb == session control block to use for writing * name == element name * indent == indent count * *********************************************************************/ static void end_elem (ses_cb_t *scb, const xmlChar *name, int32 indent) { ses_putstr_indent(scb, (const xmlChar *)"'); } /* end_elem */ /******************************************************************** * FUNCTION write_kw * * Generate a keyword at the current line location * * INPUTS: * scb == session control block to use for writing * kwname == keyword name * *********************************************************************/ static void write_kw (ses_cb_t *scb, const xmlChar *kwname) { ses_putstr(scb, (const xmlChar *)""); ses_putstr(scb, kwname); ses_putstr(scb, (const xmlChar *)""); } /* write_kw */ /******************************************************************** * FUNCTION write_extkw * * Generate a language extension keyword at the current line location * * INPUTS: * scb == session control block to use for writing * kwpfix == keyword prefix to use * kwname == keyword name * *********************************************************************/ static void write_extkw (ses_cb_t *scb, const xmlChar *kwpfix, const xmlChar *kwname) { ses_putstr(scb, (const xmlChar *)""); if (kwpfix != NULL) { ses_putstr(scb, kwpfix); ses_putchar(scb, ':'); } ses_putstr(scb, kwname); ses_putstr(scb, (const xmlChar *)""); } /* write_extkw */ /******************************************************************** * FUNCTION write_banner_cmt * * Generate a comment at the current location for a section banner * Check that only the first one is printed * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * cmval == comment value string * indent == current indent count *********************************************************************/ static void write_banner_cmt (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const xmlChar *cmval, int32 indent) { boolean needed; needed = FALSE; if (cp->unified) { if (mod->ismod) { needed = TRUE; } } else { needed = TRUE; } if (needed) { ses_putchar(scb, '\n'); ses_indent(scb, indent); ses_putstr(scb, (const xmlChar *)""); ses_putstr(scb, (const xmlChar *)"// "); ses_putstr(scb, cmval); ses_putstr(scb, (const xmlChar *)""); } } /* write_banner_cmt */ /******************************************************************** * FUNCTION write_endsec_cmt * * Generate a comment at the current location for an end of a section * No comment tokens are given * * INPUTS: * scb == session control block to use for writing * cmtype == comment type string * cmname == comment value string * *********************************************************************/ static void write_endsec_cmt (ses_cb_t *scb, const xmlChar *cmtype, const xmlChar *cmname) { ses_putstr(scb, (const xmlChar *)" "); ses_putstr(scb, (const xmlChar *)"// "); if (cmtype) { ses_putstr(scb, cmtype); ses_putchar(scb, ' '); } if (cmname) { ses_putstr(scb, cmname); } ses_putstr(scb, (const xmlChar *)""); } /* write_endsec_cmt */ /******************************************************************** * FUNCTION write_id * * Generate an identifier at the current line location * * INPUTS: * scb == session control block to use for writing * idname == identifier name * *********************************************************************/ static void write_id (ses_cb_t *scb, const xmlChar *idname) { ses_putstr(scb, (const xmlChar *)""); ses_putstr(scb, idname); ses_putstr(scb, (const xmlChar *)""); } /* write_id */ /******************************************************************** * FUNCTION write_id_a * * Generate an anchor for an identifier on the current line * No visible HTML is generated * * INPUTS: * scb == session control block to use for writing * submod == submodule name (if any) * idname == identifier name * linenum == identifier line number (used in URL) * *********************************************************************/ static void write_id_a (ses_cb_t *scb, const xmlChar *submod, const xmlChar *idname, uint32 linenum) { char buff[NCX_MAX_NUMLEN]; ses_putstr(scb, (const xmlChar *)""); } /* write_id_a */ /******************************************************************** * FUNCTION write_str * * Generate a string token at the current line location * * INPUTS: * scb == session control block to use for writing * strval == string value * quotes == quotes style (0, 1, 2) *********************************************************************/ static void write_str (ses_cb_t *scb, const xmlChar *strval, uint32 quotes) { ses_putstr(scb, (const xmlChar *)""); switch (quotes) { case 1: ses_putchar(scb, '\''); break; case 2: ses_putchar(scb, '"'); break; default: ; } ses_puthstr(scb, strval); switch (quotes) { case 1: ses_putchar(scb, '\''); break; case 2: ses_putchar(scb, '"'); break; default: ; } ses_putstr(scb, (const xmlChar *)""); } /* write_str */ /******************************************************************** * FUNCTION write_comstr * * Generate a complex string token sequence * at the current line location * * INPUTS: * scb == session control block to use for writing * tkptr == token ptr to token with original strings * strval == string val pased to write_complex_str * indent == current indent count *********************************************************************/ static void write_comstr (ses_cb_t *scb, const tk_token_ptr_t *tkptr, const xmlChar *strval, int32 indent) { const xmlChar *str; const tk_origstr_t *origstr; uint32 numquotes; boolean dquotes, moreflag, newline; /* put the first string (maybe only fragment) */ dquotes = FALSE; moreflag = FALSE; newline = FALSE; str = tk_get_first_origstr(tkptr, &dquotes, &moreflag); if (str == NULL) { str = strval; numquotes = tk_tkptr_quotes(tkptr); moreflag = FALSE; } else { numquotes = dquotes ? 2 : 1; } /* write first string at current indent point */ write_str(scb, str, numquotes); if (!moreflag) { return; } indent += ses_indent_count(scb); for (origstr = tk_first_origstr_rec(tkptr); origstr != NULL; origstr = tk_next_origstr_rec(origstr)) { dquotes = FALSE; newline = FALSE; str = tk_get_origstr_parts(origstr, &dquotes, &newline); if (str == NULL) { SET_ERROR(ERR_INTERNAL_VAL); return; } else { if (newline) { ses_putstr_indent(scb, (const xmlChar *)"+ ", indent); } else { ses_putstr(scb, (const xmlChar *)" + "); } write_str(scb, str, numquotes); } } } /* write_comstr */ /******************************************************************** * FUNCTION write_a * * Generate an anchor element * * At least one of [fname, idname] must be non-NULL (not checked!) * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * fname == filename field to use in URL (usually module name) * fversion == file version field (usually latest revision date) * submod == submodule name (may be NULL) * idpfix == prefix for identifier name to use (if idname non-NULL) * idname == identifier name to use in URL and 'a' content * linenum == line number to use in URL *********************************************************************/ static void write_a (ses_cb_t *scb, const yangdump_cvtparms_t *cp, const xmlChar *fname, const xmlChar *fversion, const xmlChar *submod, const xmlChar *idpfix, const xmlChar *idname, uint32 linenum) { char buff[NCX_MAX_NUMLEN]; ses_putstr(scb, (const xmlChar *)"urlstart && *cp->urlstart) { ses_putstr(scb, cp->urlstart); if (cp->urlstart[xml_strlen(cp->urlstart)-1] != NCXMOD_PSCHAR) { ses_putchar(scb, NCXMOD_PSCHAR); } } ses_putstr(scb, fname); if (fversion && cp->versionnames) { if (cp->simurls) { ses_putchar(scb, NCXMOD_PSCHAR); } else { ses_putchar(scb, '.'); } ses_putstr(scb, fversion); } if (!cp->simurls) { ses_putstr(scb, (const xmlChar *)".html"); } } if (idname) { ses_putchar(scb, '#'); if (submod) { ses_putstr(scb, submod); ses_putchar(scb, '.'); } ses_putstr(scb, idname); if (linenum) { sprintf(buff, ".%u", linenum); ses_putstr(scb, (const xmlChar *)buff); } } ses_putstr(scb, (const xmlChar *)"\">"); if (idpfix && idname) { ses_putstr(scb, idpfix); ses_putchar(scb, ':'); ses_putstr(scb, idname); } else if (idname) { ses_putstr(scb, idname); } else if (fname) { ses_putstr(scb, fname); } ses_putstr(scb, (const xmlChar *)""); } /* write_a */ /******************************************************************** * FUNCTION write_a2 * * Generate an anchor element with a separate display than URL * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * fname == filename field to use in URL (usually module name) * fversion == file version field (usually latest revision date) * submod == submodule name (may be NULL) * idname == identifier name to use in URL and 'a' content * linenum == line number to use in URL * idshow == ID string to show in the href construct * (may be NULL to leave off the content and final *********************************************************************/ static void write_a2 (ses_cb_t *scb, const yangdump_cvtparms_t *cp, const xmlChar *fname, const xmlChar *fversion, const xmlChar *submod, const xmlChar *idname, uint32 linenum, const xmlChar *idshow) { char buff[NCX_MAX_NUMLEN]; ses_putstr(scb, (const xmlChar *)"urlstart && *cp->urlstart) { ses_putstr(scb, cp->urlstart); if (cp->urlstart[xml_strlen(cp->urlstart)-1] != NCXMOD_PSCHAR) { ses_putchar(scb, NCXMOD_PSCHAR); } } ses_putstr(scb, fname); if (fversion && cp->versionnames) { if (cp->simurls) { ses_putchar(scb, NCXMOD_PSCHAR); } else { ses_putchar(scb, '.'); } ses_putstr(scb, fversion); } if (!cp->simurls) { ses_putstr(scb, (const xmlChar *)".html"); } } if (idname) { ses_putchar(scb, '#'); if (submod) { ses_putstr(scb, submod); ses_putchar(scb, '.'); } ses_putstr(scb, idname); if (linenum) { sprintf(buff, ".%u", linenum); ses_putstr(scb, (const xmlChar *)buff); } } ses_putstr(scb, (const xmlChar *)"\">"); if (idshow) { ses_putstr(scb, idshow); ses_putstr(scb, (const xmlChar *)""); } } /* write_a2 */ /******************************************************************** * FUNCTION write_a_ref * * Generate an anchor element for a reference * * INPUTS: * scb == session control block to use for writing * ref == reference string to use in URL; counted string * reflen == length of 'ref' * *********************************************************************/ static void write_a_ref (ses_cb_t *scb, const xmlChar *ref, uint32 reflen) { const xmlChar *num; uint32 len; if (*ref == 'R') { /* assume this is an RFC xxxx reference, find the number */ ses_putstr(scb, (const xmlChar *)""); ses_putstr(scb, (const xmlChar *)""); for (len = 0; len < reflen; len++) { ses_putchar(scb, ref[len]); } ses_putstr(scb, (const xmlChar *)""); ses_putstr(scb, (const xmlChar *)""); } /* write_a_ref */ /******************************************************************** * FUNCTION write_idref_base * * Generate a base QName; clause for an identityref * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * idref == identity ref struct to use * startindent == start indent count *********************************************************************/ static void write_idref_base (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const typ_idref_t *idref, int32 startindent) { const xmlChar *fname, *fversion, *submod; uint32 linenum; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; fname = NULL; fversion = NULL; linenum = 0; ses_indent(scb, startindent); write_kw(scb, YANG_K_BASE); ses_putchar(scb, ' '); /* get filename if identity-stmt found */ if (idref->base) { linenum = idref->base->tkerr.linenum; fname = idref->base->tkerr.mod->name; fversion = idref->base->tkerr.mod->version; } ses_putstr(scb, (const xmlChar *)""); write_a(scb, cp, fname, fversion, submod, idref->baseprefix, idref->basename, linenum); ses_putstr(scb, (const xmlChar *)""); ses_putchar(scb, ';'); } /* write_idref_base */ /******************************************************************** * FUNCTION write_identity_base * * Generate a base QName; clause for an identity * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * identity == identity struct to use * startindent == start indent count *********************************************************************/ static void write_identity_base (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const ncx_identity_t *identity, int32 startindent) { const xmlChar *fname, *fversion, *submod; uint32 linenum; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; fname = NULL; fversion = NULL; linenum = 0; ses_indent(scb, startindent); write_kw(scb, YANG_K_BASE); ses_putchar(scb, ' '); #if 0 /* get filename if identity-stmt found */ if (identity->base) { linenum = identity->base->tkerr.linenum; fname = identity->base->tkerr.mod->name; fversion = identity->base->tkerr.mod->version; } ses_putstr(scb, (const xmlChar *)""); write_a(scb, cp, fname, fversion, submod, identity->baseprefix, identity->basename, linenum); ses_putstr(scb, (const xmlChar *)""); ses_putchar(scb, ';'); #endif } /* write_identity_base */ /******************************************************************** * FUNCTION write_href_id * * Generate a simple clause on 1 line with a name anchor for the ID * * INPUTS: * scb == session control block to use for writing * submod == submodule name (may be NULL) * kwname == keyword name * idname == identifier name (may be NULL) * indent == indent count to use * linenum == line number for this identifier to use in URLs * finsemi == TRUE if end in ';', FALSE if '{' * newln == TRUE if a newline should be output first * FALSE if newline should not be output first *********************************************************************/ static void write_href_id (ses_cb_t *scb, const xmlChar *submod, const xmlChar *kwname, const xmlChar *idname, int32 indent, uint32 linenum, boolean finsemi, boolean newln) { if (newln) { ses_putchar(scb, '\n'); } ses_indent(scb, indent); if (idname) { write_id_a(scb, submod, idname, linenum); } else { write_id_a(scb, submod, kwname, linenum); } write_kw(scb, kwname); if (idname) { ses_putchar(scb, ' '); write_id(scb, idname); } if (finsemi) { ses_putchar(scb, ';'); } else { ses_putstr(scb, START_SEC); } } /* write_href_id */ /******************************************************************** * FUNCTION write_complex_str * * Generate a complex string from original tokens or * just a simple clause on 1 line if complex not needed * * INPUTS: * scb == session control block to use for writing * tkc == token chain to search for token ptr records * field == address of string token (key to lookup) * kwname == keyword name * strval == string value * indent == indent count to use * quotes == quotes style (0, 1, 2) * finsemi == TRUE if end in ';', FALSE if '{' *********************************************************************/ static void write_complex_str (ses_cb_t *scb, tk_chain_t *tkc, const void *field, const xmlChar *kwname, const xmlChar *strval, int32 indent, uint32 quotes, boolean finsemi) { const tk_token_ptr_t *tkptr; if (tkc != NULL && field != NULL) { tkptr = tk_find_tkptr(tkc, field); } else { tkptr = NULL; } ses_indent(scb, indent); write_kw(scb, kwname); if (strval) { if (!has_nl(strval) && (xml_strlen(strval) + 1) < ses_line_left(scb)) { ses_putchar(scb, ' '); } else { indent += ses_indent_count(scb); ses_indent(scb, indent); } if (tkptr != NULL) { write_comstr(scb, tkptr, strval, indent); } else { write_str(scb, strval, quotes); } } if (finsemi) { ses_putchar(scb, ';'); } else { ses_putstr(scb, START_SEC); } } /* write_complex_str */ /******************************************************************** * FUNCTION write_leafref_path * * Generate a leafref path-stmt * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * typdef == typdef for the leafref * obj == object pointer if this is from a leaf or leaf-list * NULL if this is froma typedef * startindent == start indent count *********************************************************************/ static void write_leafref_path (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, typ_def_t *typdef, obj_template_t *obj, int32 startindent) { const xmlChar *submod, *pathstr; obj_template_t *targobj = NULL; xpath_pcb_t *testpath; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; pathstr = typ_get_leafref_path(typdef); if (pathstr == NULL) { SET_ERROR(ERR_INTERNAL_VAL); return; } testpath = typ_get_leafref_pcb(typdef); if ((testpath == NULL || testpath->targobj == NULL) && (obj != NULL)) { targobj = obj_get_leafref_targobj(obj); } else { targobj = testpath->targobj; } if (targobj == NULL) { write_complex_str(scb, cp->tkc, typ_get_leafref_path_addr(typdef), YANG_K_PATH, pathstr, startindent, 2, TRUE); } else { ncx_module_t *pathmod = obj_get_mod(targobj); ses_indent(scb, startindent); write_kw(scb, YANG_K_PATH); ses_putchar(scb, ' '); write_a2(scb, cp, (pathmod != mod) ? ncx_get_modname(pathmod) : NULL, (pathmod != mod) ? pathmod->version : NULL, submod, obj_get_name(targobj), targobj->tkerr.linenum, NULL); write_str(scb, pathstr, 2); ses_putstr(scb, (const xmlChar *)""); ses_putchar(scb, ';'); } } /* write_leafref_path */ /******************************************************************** * FUNCTION write_simple_str * * Generate a simple clause on 1 line * * INPUTS: * scb == session control block to use for writing * kwname == keyword name * strval == string value * indent == indent count to use * quotes == quotes style (0, 1, 2) * finsemi == TRUE if end in ';', FALSE if '{' *********************************************************************/ static void write_simple_str (ses_cb_t *scb, const xmlChar *kwname, const xmlChar *strval, int32 indent, uint32 quotes, boolean finsemi) { write_complex_str(scb, NULL, NULL, kwname, strval, indent, quotes, finsemi); } /* write_simple_str */ /******************************************************************** * FUNCTION write_status * * Generate the HTML for a status clause only if the value * is other than current * * INPUTS: * scb == session control block to use for writing * status == status field * indent == start indent count * *********************************************************************/ static void write_status (ses_cb_t *scb, ncx_status_t status, int32 indent) { if (status != NCX_STATUS_CURRENT && status != NCX_STATUS_NONE) { write_simple_str(scb, YANG_K_STATUS, ncx_get_status_string(status), indent, 0, TRUE); } } /* write_status */ /******************************************************************** * FUNCTION write_reference_str * * Generate a simple clause on 1 line * * INPUTS: * scb == session control block to use for writing * ref == reference string to use * indent == indent count to use *********************************************************************/ static void write_reference_str (ses_cb_t *scb, const xmlChar *ref, int32 indent) { const xmlChar *str, *start; boolean done; uint32 reflen, len; if (ref == NULL) { return; } ses_indent(scb, indent); write_kw(scb, YANG_K_REFERENCE); indent += ses_indent_count(scb); ses_indent(scb, indent); ses_putstr(scb, (const xmlChar *)""); ses_putchar(scb, '"'); done = FALSE; while (!done) { str = NULL; start = ref; reflen = 0; len = find_reference(ref, &str, &reflen); if (str != NULL) { while (start < str) { if (*start == '\n') { ses_indent(scb, indent); ses_putchar(scb, ' '); start++; } else { ses_putcchar(scb, *start++); } } write_a_ref(scb, str, reflen); ref += len; } else { while (len > 0) { if (*ref == '\n') { ses_indent(scb, indent); ref++; } else { ses_putcchar(scb, *ref++); } len--; } } /* only allow 1 reference per line at this time */ while (*ref && *ref != '\n') { ses_putcchar(scb, *ref++); } if (*ref != '\n') { done = TRUE; } } ses_putchar(scb, '"'); ses_putstr(scb, (const xmlChar *)""); ses_putstr(scb, (const xmlChar *)";\n"); } /* write_reference_str */ /******************************************************************** * FUNCTION write_type * * Generate the HTML for the specified type name * Does not generate the entire clause, just the type name * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * typdef == typ_def_t to use * indent == start indent count * *********************************************************************/ static void write_type (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const typ_def_t *typdef, int32 indent) { const xmlChar *fname, *fversion, *submod; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; ses_indent(scb, indent); write_kw(scb, YANG_K_TYPE); ses_putchar(scb, ' '); /* get filename if external module */ fname = NULL; fversion = NULL; if (typdef->prefix && xml_strcmp(typdef->prefix, mod->prefix)) { if (typdef->tclass == NCX_CL_NAMED && typdef->def.named.typ && typdef->def.named.typ->tkerr.mod) { fname = typdef->def.named.typ->tkerr.mod->name; fversion = typdef->def.named.typ->tkerr.mod->version; } else { SET_ERROR(ERR_INTERNAL_VAL); } } /* write the identifier with an href to the type * unless the type is one of the yang builtin types */ if (typdef->tclass == NCX_CL_NAMED) { ses_putstr(scb, (const xmlChar *)""); write_a(scb, cp, fname, fversion, submod, (fname) ? typdef->prefix : NULL, typdef->typenamestr, typ_get_named_type_linenum(typdef)); ses_putstr(scb, (const xmlChar *)""); } else { write_id(scb, typdef->typenamestr); } } /* write_type */ /******************************************************************** * FUNCTION write_errinfo * * Generate the HTML for the specified error info struct * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * errinfo == ncx_errinfo_t struct to use * indent == start indent count * *********************************************************************/ static void write_errinfo (ses_cb_t *scb, const yangdump_cvtparms_t *cp, const ncx_errinfo_t *errinfo, int32 indent) { if (errinfo->error_message) { write_complex_str(scb, cp->tkc, &errinfo->error_message, YANG_K_ERROR_MESSAGE, errinfo->error_message, indent, 2, TRUE); } if (errinfo->error_app_tag) { write_complex_str(scb, cp->tkc, &errinfo->error_app_tag, YANG_K_ERROR_APP_TAG, errinfo->error_app_tag, indent, 2, TRUE); } if (errinfo->descr) { write_complex_str(scb, cp->tkc, &errinfo->descr, YANG_K_DESCRIPTION, errinfo->descr, indent, 2, TRUE); } if (errinfo->ref) { write_reference_str(scb, errinfo->ref, indent); } } /* write_errinfo */ /******************************************************************** * FUNCTION write_musts * * Generate the HTML for a Q of ncx_errinfo_t representing * must-stmts, not just error info * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * mustQ == Q of xpath_pcb_t to use * indent == start indent count * *********************************************************************/ static void write_musts (ses_cb_t *scb, const yangdump_cvtparms_t *cp, const dlq_hdr_t *mustQ, int32 indent) { const xpath_pcb_t *must; const ncx_errinfo_t *errinfo; for (must = (const xpath_pcb_t *)dlq_firstEntry(mustQ); must != NULL; must = (const xpath_pcb_t *)dlq_nextEntry(must)) { errinfo = &must->errinfo; if (errinfo->descr || errinfo->ref || errinfo->error_app_tag || errinfo->error_message) { write_complex_str(scb, cp->tkc, &must->exprstr, YANG_K_MUST, must->exprstr, indent, 2, FALSE); write_errinfo(scb, cp, errinfo, indent + ses_indent_count(scb)); ses_putstr_indent(scb, END_SEC, indent); } else { write_complex_str(scb, cp->tkc, &must->exprstr, YANG_K_MUST, must->exprstr, indent, 2, TRUE); } } } /* write_musts */ /******************************************************************** * FUNCTION write_appinfoQ * * Generate the HTML for a Q of ncx_appinfo_t representing * vendor extensions entered with the YANG statements * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * appinfoQ == Q of ncx_appinfo_t to use * indent == start indent count * *********************************************************************/ static void write_appinfoQ (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *appinfoQ, int32 indent) { const ncx_appinfo_t *appinfo; const xmlChar *fname, *fversion, *submod; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; for (appinfo = (const ncx_appinfo_t *)dlq_firstEntry(appinfoQ); appinfo != NULL; appinfo = (const ncx_appinfo_t *)dlq_nextEntry(appinfo)) { ses_indent(scb, indent); if (appinfo->ext) { fname = NULL; fversion = NULL; if (appinfo->ext->tkerr.mod && (appinfo->ext->tkerr.mod != mod)) { fname = appinfo->ext->tkerr.mod->name; fversion = appinfo->ext->tkerr.mod->version; } ses_putstr(scb, (const xmlChar *)""); write_a(scb, cp, fname, fversion, submod, appinfo->prefix, appinfo->name, appinfo->ext->tkerr.linenum); ses_putstr(scb, (const xmlChar *)""); } else { write_extkw(scb, appinfo->prefix, appinfo->name); } if (appinfo->value) { ses_putchar(scb, ' '); write_str(scb, appinfo->value, 2); } if (!dlq_empty(appinfo->appinfoQ)) { ses_putstr(scb, START_SEC); write_appinfoQ(scb, mod, cp, appinfo->appinfoQ, indent+ses_indent_count(scb)); ses_putstr_indent(scb, END_SEC, indent); } else { ses_putchar(scb, ';'); } } } /* write_appinfoQ */ /******************************************************************** * FUNCTION write_type_contents * * Generate the HTML for the specified type * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * typdef == typ_def_t to use * obj == object template if this is from a leaf or leaf-list * NULL if this is from a typedef * startindent == start indent count * *********************************************************************/ static void write_type_contents (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, typ_def_t *typdef, obj_template_t *obj, int32 startindent) { typ_unionnode_t *un; const typ_enum_t *bit, *enu; const typ_range_t *range; const typ_pattern_t *pat; const typ_idref_t *idref; char buff[NCX_MAX_NUMLEN]; int32 indent; boolean errinfo_set, constrained_set; indent = startindent + ses_indent_count(scb); write_appinfoQ(scb, mod, cp, &typdef->appinfoQ, startindent); switch (typdef->tclass) { case NCX_CL_BASE: break; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { typdef = typdef->def.named.newtyp; } else { break; } /* fall through if typdef set */ case NCX_CL_SIMPLE: switch (typdef->def.simple.btyp) { case NCX_BT_UNION: for (un = typ_first_unionnode(typdef); un != NULL; un = (typ_unionnode_t *)dlq_nextEntry(un)) { if (un->typdef) { write_type_clause(scb, mod, cp, un->typdef, NULL, startindent); } else if (un->typ) { write_type_clause(scb, mod, cp, &un->typ->typdef, NULL, startindent); } else { SET_ERROR(ERR_INTERNAL_VAL); } } break; case NCX_BT_BITS: for (bit = typ_first_con_enumdef(typdef); bit != NULL; bit = (const typ_enum_t *)dlq_nextEntry(bit)) { write_complex_str(scb, cp->tkc, &bit->name, YANG_K_BIT, bit->name, startindent, 0, FALSE); write_appinfoQ(scb, mod, cp, &bit->appinfoQ, indent); sprintf(buff, "%u", bit->pos); write_simple_str(scb, YANG_K_POSITION, (const xmlChar *)buff, indent, 0, TRUE); write_status(scb, bit->status, indent); if (bit->descr) { write_complex_str(scb, cp->tkc, &bit->descr, YANG_K_DESCRIPTION, bit->descr, indent, 2, TRUE); } if (bit->ref) { write_reference_str(scb, bit->ref, indent); } ses_putstr_indent(scb, END_SEC, startindent); } break; case NCX_BT_ENUM: for (enu = typ_first_con_enumdef(typdef); enu != NULL; enu = (const typ_enum_t *)dlq_nextEntry(enu)) { write_complex_str(scb, cp->tkc, &enu->name, YANG_K_ENUM, enu->name, startindent, 2, FALSE); write_appinfoQ(scb, mod, cp, &enu->appinfoQ, indent); sprintf(buff, "%d", enu->val); write_simple_str(scb, YANG_K_VALUE, (const xmlChar *)buff, indent, 0, TRUE); write_status(scb, enu->status, indent); if (enu->descr) { write_complex_str(scb, cp->tkc, &enu->descr, YANG_K_DESCRIPTION, enu->descr, indent, 2, TRUE); } if (enu->ref) { write_reference_str(scb, enu->ref, indent); } ses_putstr_indent(scb, END_SEC, startindent); } break; case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: /* appinfo only */ break; case NCX_BT_DECIMAL64: sprintf(buff, "%d", typ_get_fraction_digits(typdef)); write_simple_str(scb, YANG_K_FRACTION_DIGITS, (const xmlChar *)buff, startindent, 0, TRUE); /* fall through to check range */ case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_FLOAT64: range = typ_get_crange_con(typdef); if (range && range->rangestr) { errinfo_set = ncx_errinfo_set(&range->range_errinfo); write_simple_str(scb, YANG_K_RANGE, range->rangestr, startindent, 2, !errinfo_set); if (errinfo_set) { write_errinfo(scb, cp, &range->range_errinfo, indent); ses_putstr_indent(scb, END_SEC, startindent); } } break; case NCX_BT_STRING: case NCX_BT_BINARY: range = typ_get_crange_con(typdef); if (range && range->rangestr) { errinfo_set = ncx_errinfo_set(&range->range_errinfo); write_simple_str(scb, YANG_K_LENGTH, range->rangestr, startindent, 2, !errinfo_set); if (errinfo_set) { write_errinfo(scb, cp, &range->range_errinfo, indent); ses_putstr_indent(scb, END_SEC, startindent); } } for (pat = typ_get_first_cpattern(typdef); pat != NULL; pat = typ_get_next_cpattern(pat)) { errinfo_set = ncx_errinfo_set(&pat->pat_errinfo); write_complex_str(scb, cp->tkc, &pat->pat_str, YANG_K_PATTERN, pat->pat_str, startindent, 1, !errinfo_set); if (errinfo_set) { write_errinfo(scb, cp, &pat->pat_errinfo, indent); ses_putstr_indent(scb, END_SEC, startindent); } } break; case NCX_BT_SLIST: break; case NCX_BT_LEAFREF: write_leafref_path(scb, mod, cp, typdef, obj, startindent); break; case NCX_BT_INSTANCE_ID: constrained_set = typ_get_constrained(typdef); write_simple_str(scb, YANG_K_REQUIRE_INSTANCE, (constrained_set) ? NCX_EL_TRUE : NCX_EL_FALSE, startindent, 0, TRUE); break; case NCX_BT_IDREF: idref = typ_get_cidref(typdef); if (idref) { write_idref_base(scb, mod, cp, idref, startindent); } break; default: break; } break; case NCX_CL_COMPLEX: SET_ERROR(ERR_INTERNAL_VAL); break; case NCX_CL_REF: SET_ERROR(ERR_INTERNAL_VAL); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* write_type_contents */ /******************************************************************** * FUNCTION write_type_clause * * Generate the HTML for the specified type clause * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * typdef == typ_def_t to use * obj == object template if this is from a leaf or leaf-list * NULL if this is from a typedef * startindent == start indent count * *********************************************************************/ static void write_type_clause (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, typ_def_t *typdef, obj_template_t *obj, int32 startindent) { write_type(scb, mod, cp, typdef, startindent); if (typ_has_subclauses(typdef)) { ses_putstr(scb, START_SEC); write_type_contents(scb, mod, cp, typdef, obj, startindent + ses_indent_count(scb)); ses_putstr_indent(scb, END_SEC, startindent); } else { ses_putchar(scb, ';'); } } /* write_type_clause */ /******************************************************************** * FUNCTION write_typedef * * Generate the HTML for 1 typedef * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * typ == typ_template_t to use * startindent == start indent count * first == TRUE if this is the first object at this indent level * FALSE if not the first object at this indent level *********************************************************************/ static void write_typedef (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, typ_template_t *typ, int32 startindent, boolean first) { const xmlChar *submod; int32 indent; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; indent = startindent + ses_indent_count(scb); write_href_id(scb, submod, YANG_K_TYPEDEF, typ->name, startindent, typ_get_typ_linenum(typ), FALSE, !first); /* appinfoQ */ write_appinfoQ(scb, mod, cp, &typ->appinfoQ, indent); /* type field */ write_type_clause(scb, mod, cp, &typ->typdef, NULL, indent); /* units field */ if (typ->units) { write_simple_str(scb, YANG_K_UNITS, typ->units, indent, 2, TRUE); } /* default field */ if (typ->defval) { write_complex_str(scb, cp->tkc, &typ->defval, YANG_K_DEFAULT, typ->defval, indent, 2, TRUE); } /* status field */ write_status(scb, typ->status, indent); /* description field */ if (typ->descr) { write_complex_str(scb, cp->tkc, &typ->descr, YANG_K_DESCRIPTION, typ->descr, indent, 2, TRUE); } /* reference field */ if (typ->ref) { write_reference_str(scb, typ->ref, indent); } /* end typedef clause */ ses_putstr_indent(scb, END_SEC, startindent); } /* write_typedef */ /******************************************************************** * FUNCTION write_typedefs * * Generate the HTML for the specified typedefQ * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * typedefQ == que of typ_template_t to use * startindent == start indent count * *********************************************************************/ static void write_typedefs (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *typedefQ, int32 startindent) { typ_template_t *typ; boolean first; if (dlq_empty(typedefQ)) { return; } if (typedefQ == &mod->typeQ) { write_banner_cmt(scb, mod, cp, (const xmlChar *)"typedefs", startindent); } first = TRUE; for (typ = (typ_template_t *)dlq_firstEntry(typedefQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { write_typedef(scb, mod, cp, typ, startindent, first); first = FALSE; } } /* write_typedefs */ /******************************************************************** * FUNCTION write_grouping * * Generate the HTML for 1 grouping * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * grp == grp_template_t to use * startindent == start indent count * first == TRUE if this is the first object at this indent level * FALSE if not the first object at this indent level *********************************************************************/ static void write_grouping (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, grp_template_t *grp, int32 startindent, boolean first) { const xmlChar *submod; int32 indent; boolean cooked; cooked = (strcmp(cp->objview, OBJVIEW_COOKED)) ? FALSE : TRUE; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; indent = startindent + ses_indent_count(scb); if (cooked && !grp_has_typedefs(grp)) { return; } write_href_id(scb, submod, YANG_K_GROUPING, grp->name, startindent, grp->tkerr.linenum, FALSE, !first); /* appinfoQ */ write_appinfoQ(scb, mod, cp, &grp->appinfoQ, indent); /* status field */ write_status(scb, grp->status, indent); /* description field */ if (grp->descr) { write_complex_str(scb, cp->tkc, &grp->descr, YANG_K_DESCRIPTION, grp->descr, indent, 2, TRUE); } /* reference field */ if (grp->ref) { write_reference_str(scb, grp->ref, indent); } write_typedefs(scb, mod, cp, &grp->typedefQ, indent); write_groupings(scb, mod, cp, &grp->groupingQ, indent); if (!cooked) { write_objects(scb, mod, cp, &grp->datadefQ, indent); } /* end grouping clause */ ses_putstr_indent(scb, END_SEC, startindent); /* end grouping comment */ write_endsec_cmt(scb, YANG_K_GROUPING, grp->name); } /* write_grouping */ /******************************************************************** * FUNCTION write_groupings * * Generate the HTML for the specified groupingQ * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * groupingQ == que of grp_template_t to use * startindent == start indent count * *********************************************************************/ static void write_groupings (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *groupingQ, int32 startindent) { grp_template_t *grp; boolean needed, cooked, first; if (dlq_empty(groupingQ)) { return; } cooked = (strcmp(cp->objview, OBJVIEW_COOKED)) ? FALSE : TRUE; /* groupings are only generated in cooked mode if they have * typedefs, and then just the typedefs are generated */ if (cooked) { needed = FALSE; for (grp = (grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL && needed==FALSE; grp = (grp_template_t *)dlq_nextEntry(grp)) { needed = grp_has_typedefs(grp); } if (!needed) { return; } } /* put comment for first grouping only */ if (groupingQ == &mod->groupingQ) { write_banner_cmt(scb, mod, cp, (const xmlChar *)"groupings", startindent); } first = TRUE; for (grp = (grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL; grp = (grp_template_t *)dlq_nextEntry(grp)) { write_grouping(scb, mod, cp, grp, startindent, first); first = FALSE; } } /* write_groupings */ /******************************************************************** * FUNCTION write_iffeature * * Generate the HTML for 1 if-feature statement * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * iffeature == ncx_iffeature_t to use * startindent == start indent count * *********************************************************************/ static void write_iffeature (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const ncx_iffeature_t *iffeature, int32 startindent) { const xmlChar *fname, *fversion, *submod; uint32 linenum; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; fname = NULL; fversion = NULL; linenum = 0; ses_indent(scb, startindent); write_kw(scb, YANG_K_IF_FEATURE); ses_putchar(scb, ' '); /* get filename if-feature-stmt filled in */ if (iffeature->feature) { linenum = iffeature->feature->tkerr.linenum; fname = iffeature->feature->tkerr.mod->name; fversion = iffeature->feature->tkerr.mod->version; } ses_putstr(scb, (const xmlChar *)""); write_a(scb, cp, fname, fversion, submod, iffeature->prefix, iffeature->name, linenum); ses_putstr(scb, (const xmlChar *)""); ses_putchar(scb, ';'); } /* write_iffeature */ /******************************************************************** * FUNCTION write_iffeatureQ * * Generate the HTML for a Q of if-feature statements * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * iffeatureQ == Q of ncx_iffeature_t to use * startindent == start indent count * *********************************************************************/ static void write_iffeatureQ (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *iffeatureQ, int32 startindent) { const ncx_iffeature_t *iffeature; for (iffeature = (const ncx_iffeature_t *) dlq_firstEntry(iffeatureQ); iffeature != NULL; iffeature = (const ncx_iffeature_t *) dlq_nextEntry(iffeature)) { write_iffeature(scb, mod, cp, iffeature, startindent); } } /* write_iffeatureQ */ /******************************************************************** * FUNCTION write_when * * Check then when-stmt for an object * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * obj == object to check * indent = indent amount *********************************************************************/ static void write_when (ses_cb_t *scb, const yangdump_cvtparms_t *cp, const obj_template_t *obj, int32 indent) { const ncx_errinfo_t *errinfo; /* when-stmt? */ if (obj->when && obj->when->exprstr) { errinfo = &obj->when->errinfo; if (errinfo->descr || errinfo->ref) { write_complex_str(scb, cp->tkc, &obj->when->exprstr, YANG_K_WHEN, obj->when->exprstr, indent, 2, FALSE); write_errinfo(scb, cp, errinfo, indent + ses_indent_count(scb)); ses_putstr_indent(scb, END_SEC, indent); } else { write_complex_str(scb, cp->tkc, &obj->when->exprstr, YANG_K_WHEN, obj->when->exprstr, indent, 2, TRUE); } } } /* write_when */ /******************************************************************** * FUNCTION write_presence_stmt * * Write the presence-stmt * * INPUTS: * scb == session control block to use for writing * tkc == token chain to use * field == address of presence field * presence == presence string (may be NULL to skip) * indent = indent amount *********************************************************************/ static void write_presence_stmt (ses_cb_t *scb, tk_chain_t *tkc, void *field, const xmlChar *presence, int32 indent) { if (presence) { write_complex_str(scb, tkc, field, YANG_K_PRESENCE, presence, indent, 2, TRUE); } } /* write_presence_stmt */ /******************************************************************** * FUNCTION write_sdr * * Write the status-stmt, description-stmt, and reference-stmt * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * obj == object to check * indent = indent amount *********************************************************************/ static void write_sdr (ses_cb_t *scb, const yangdump_cvtparms_t *cp, const obj_template_t *obj, int32 indent) { const xmlChar *str; /* status-stmt? (only written if not 'current' */ write_status(scb, obj_get_status(obj), indent); /* description-stmt? */ str = obj_get_description(obj); if (str) { write_complex_str(scb, cp->tkc, obj_get_description_addr(obj), YANG_K_DESCRIPTION, str, indent, 2, TRUE); } /* reference-stmt? */ str = obj_get_reference(obj); if (str) { write_reference_str(scb, str, indent); } } /* write_sdr */ /******************************************************************** * FUNCTION write_config_stmt * * Write the config-stmt * * INPUTS: * scb == session control block to use for writing * obj == object to check * indent = indent amount *********************************************************************/ static void write_config_stmt (ses_cb_t *scb, const obj_template_t *obj, int32 indent) { /* config-stmt, only if actually set to false */ if (obj->flags & OBJ_FL_CONFSET) { if (!(obj->flags & OBJ_FL_CONFIG)) { write_simple_str(scb, YANG_K_CONFIG, NCX_EL_FALSE, indent, 0, TRUE); } } } /* write_config_stmt */ /******************************************************************** * FUNCTION write_config_stmt_force * * Write the config-stmt; force output even if default * * INPUTS: * scb == session control block to use for writing * obj == object to check * indent = indent amount *********************************************************************/ static void write_config_stmt_force (ses_cb_t *scb, const obj_template_t *obj, int32 indent) { boolean flag; /* config-stmt */ if (obj->flags & OBJ_FL_CONFSET) { flag = (obj->flags & OBJ_FL_CONFIG); write_simple_str(scb, YANG_K_CONFIG, (flag) ? NCX_EL_TRUE : NCX_EL_FALSE, indent, 0, TRUE); } } /* write_config_stmt_force */ /******************************************************************** * FUNCTION write_mandatory_stmt * * Write the mandatory-stmt * * INPUTS: * scb == session control block to use for writing * obj == object to check * indent = indent amount *********************************************************************/ static void write_mandatory_stmt (ses_cb_t *scb, const obj_template_t *obj, int32 indent) { /* mandatory field, only if actually set to true */ if (obj->flags & OBJ_FL_MANDSET) { if (obj->flags & OBJ_FL_MANDATORY) { write_simple_str(scb, YANG_K_MANDATORY, NCX_EL_TRUE, indent, 0, TRUE); } } } /* write__mandatory_stmt */ /******************************************************************** * FUNCTION write_mandatory_stmt_force * * Write the mandatory-stmt; force output even if default * * INPUTS: * scb == session control block to use for writing * obj == object to check * indent = indent amount *********************************************************************/ static void write_mandatory_stmt_force (ses_cb_t *scb, const obj_template_t *obj, int32 indent) { boolean flag; /* mandatory field, only if actually set to true */ if (obj->flags & OBJ_FL_MANDSET) { flag = (obj->flags & OBJ_FL_MANDATORY); write_simple_str(scb, YANG_K_MANDATORY, (flag) ? NCX_EL_TRUE : NCX_EL_FALSE, indent, 0, TRUE); } } /* write__mandatory_stmt_force */ /******************************************************************** * FUNCTION write_minmax * * Write the min-elements and/or max-elements stmts if needed * * INPUTS: * scb == session control block to use for writing * minset == TRUE if min-elements set * minval == min-elements value * maxset == TRUE if max-elements set * maxval == min-elements value * indent == indent amount *********************************************************************/ static void write_minmax (ses_cb_t *scb, boolean minset, uint32 minval, boolean maxset, uint32 maxval, int32 indent) { char buff[NCX_MAX_NUMLEN]; if (minset) { sprintf(buff, "%u", minval); write_simple_str(scb, YANG_K_MIN_ELEMENTS, (const xmlChar *)buff, indent, 0, TRUE); } if (maxset) { if (maxval == 0) { write_simple_str(scb, YANG_K_MAX_ELEMENTS, YANG_K_UNBOUNDED, indent, 0, TRUE); } else { sprintf(buff, "%u", maxval); write_simple_str(scb, YANG_K_MAX_ELEMENTS, (const xmlChar *)buff, indent, 0, TRUE); } } } /* write_minmax */ /******************************************************************** * FUNCTION write_unique_stmts * * Generate the HTML for the specified uniqueQ * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * uniqueQ == que of obj_unique_t to use * startindent == start indent count * *********************************************************************/ static void write_unique_stmts (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *uniqueQ, int32 startindent) { const obj_unique_t *uni; const obj_unique_comp_t *unicomp, *nextunicomp; const xmlChar *submod; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; for (uni = (const obj_unique_t *)dlq_firstEntry(uniqueQ); uni != NULL; uni = (const obj_unique_t *)dlq_nextEntry(uni)) { ses_indent(scb, startindent); write_kw(scb, YANG_K_UNIQUE); ses_putstr(scb, (const xmlChar *)" \""); for (unicomp = (obj_unique_comp_t *) dlq_firstEntry(&uni->compQ); unicomp != NULL; unicomp = nextunicomp) { nextunicomp = (obj_unique_comp_t *) dlq_nextEntry(unicomp); write_a2(scb, cp, NULL, NULL, submod, obj_get_name(unicomp->unobj), unicomp->unobj->tkerr.linenum, unicomp->xpath); if (nextunicomp) { ses_putstr(scb, (const xmlChar *)" "); } } ses_putstr(scb, (const xmlChar *)"\";"); } } /* write_unique_stmts */ /******************************************************************** * FUNCTION write_object * * Generate the HTML for the 1 datadef * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * obj == obj_template_t to use * startindent == start indent count * first == TRUE if this is the first object at this indent level * FALSE if not the first object at this indent level * RETURNS: * status (usually NO_ERR or ERR_NCX_SKIPPED) *********************************************************************/ static status_t write_object (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, obj_template_t *obj, int32 startindent, boolean first) { obj_container_t *con; obj_leaf_t *leaf; obj_leaflist_t *leaflist; obj_list_t *list; obj_choice_t *choic; obj_case_t *cas; obj_uses_t *uses; obj_augment_t *aug; obj_rpc_t *rpc; obj_rpcio_t *rpcio; obj_notif_t *notif; obj_refine_t *refine; obj_key_t *key, *nextkey; const xmlChar *fname, *fversion, *submod; int32 indent; boolean isempty, fullcase; if (obj_is_cloned(obj) && cp->rawmode) { /* skip cloned objects in 'raw' object view mode */ return ERR_NCX_SKIPPED; } if (cp->rawmode) { isempty = obj_is_empty(obj); } else { isempty = FALSE; } submod = (cp->unified && !mod->ismod) ? mod->name : NULL; indent = startindent + ses_indent_count(scb); fullcase = !obj_is_short_case(obj); /* generate start of statement with the object type * except a short-form case-stmt */ if (fullcase && obj_has_name(obj) && obj->objtype != OBJ_TYP_RPCIO) { write_href_id(scb, submod, obj_get_typestr(obj), obj_get_name(obj), startindent, obj->tkerr.linenum, isempty, !first); if (isempty) { return NO_ERR; } } switch (obj->objtype) { case OBJ_TYP_ANYXML: write_appinfoQ(scb, mod, cp, &obj->appinfoQ, indent); write_when(scb, cp, obj, indent); write_iffeatureQ(scb, mod, cp, &obj->iffeatureQ, indent); write_musts(scb, cp, obj_get_mustQ(obj), indent); write_config_stmt(scb, obj, indent); write_mandatory_stmt(scb, obj, indent); write_sdr(scb, cp, obj, indent); ses_putstr_indent(scb, END_SEC, startindent); break; case OBJ_TYP_CONTAINER: con = obj->def.container; write_appinfoQ(scb, mod, cp, &obj->appinfoQ, indent); write_when(scb, cp, obj, indent); write_iffeatureQ(scb, mod, cp, &obj->iffeatureQ, indent); write_musts(scb, cp, obj_get_mustQ(obj), indent); write_presence_stmt(scb, cp->tkc, obj_get_presence_string_field(obj), obj_get_presence_string(obj), indent); write_config_stmt(scb, obj, indent); write_sdr(scb, cp, obj, indent); write_typedefs(scb, mod, cp, con->typedefQ, indent); write_groupings(scb, mod, cp, con->groupingQ, indent); write_objects(scb, mod, cp, con->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); write_endsec_cmt(scb, YANG_K_CONTAINER, con->name); break; case OBJ_TYP_LEAF: leaf = obj->def.leaf; write_appinfoQ(scb, mod, cp, &obj->appinfoQ, indent); write_when(scb, cp, obj, indent); write_iffeatureQ(scb, mod, cp, &obj->iffeatureQ, indent); write_type_clause(scb, mod, cp, leaf->typdef, obj, indent); /* units clause */ if (leaf->units) { write_simple_str(scb, YANG_K_UNITS, leaf->units, indent, 2, TRUE); } write_musts(scb, cp, obj_get_mustQ(obj), indent); if (leaf->defval) { write_complex_str(scb, cp->tkc, &leaf->defval, YANG_K_DEFAULT, leaf->defval, indent, 2, TRUE); } write_config_stmt(scb, obj, indent); write_mandatory_stmt(scb, obj, indent); write_sdr(scb, cp, obj, indent); ses_putstr_indent(scb, END_SEC, startindent); break; case OBJ_TYP_LEAF_LIST: leaflist = obj->def.leaflist; write_appinfoQ(scb, mod, cp, &obj->appinfoQ, indent); write_when(scb, cp, obj, indent); write_iffeatureQ(scb, mod, cp, &obj->iffeatureQ, indent); write_type_clause(scb, mod, cp, leaflist->typdef, obj, indent); if (leaflist->units) { write_simple_str(scb, YANG_K_UNITS, leaflist->units, indent, 2, TRUE); } write_musts(scb, cp, obj_get_mustQ(obj), indent); write_config_stmt(scb, obj, indent); write_minmax(scb, leaflist->minset, leaflist->minelems, leaflist->maxset, leaflist->maxelems, indent); if (!leaflist->ordersys) { write_simple_str(scb, YANG_K_ORDERED_BY, YANG_K_USER, indent, 0, TRUE); } write_sdr(scb, cp, obj, indent); ses_putstr_indent(scb, END_SEC, startindent); break; case OBJ_TYP_LIST: list = obj->def.list; write_appinfoQ(scb, mod, cp, &obj->appinfoQ, indent); write_when(scb, cp, obj, indent); write_iffeatureQ(scb, mod, cp, &obj->iffeatureQ, indent); write_musts(scb, cp, obj_get_mustQ(obj), indent); /* key field, manual generation to make links */ if (!dlq_empty(&list->keyQ)) { ses_indent(scb, indent); write_kw(scb, YANG_K_KEY); ses_putstr(scb, (const xmlChar *)" \""); for (key = obj_first_key(obj); key != NULL; key = nextkey) { nextkey = obj_next_key(key); write_a(scb, cp, NULL, NULL, submod, NULL, obj_get_name(key->keyobj), key->keyobj->tkerr.linenum); if (nextkey) { ses_putchar(scb, ' '); } } ses_putstr(scb, (const xmlChar *)"\";"); } write_unique_stmts(scb, mod, cp, &list->uniqueQ, indent); write_config_stmt(scb, obj, indent); write_minmax(scb, list->minset, list->minelems, list->maxset, list->maxelems, indent); /* ordered-by field */ if (!list->ordersys) { write_simple_str(scb, YANG_K_ORDERED_BY, YANG_K_USER, indent, 0, TRUE); } write_sdr(scb, cp, obj, indent); write_typedefs(scb, mod, cp, list->typedefQ, indent); write_groupings(scb, mod, cp, list->groupingQ, indent); write_objects(scb, mod, cp, list->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); write_endsec_cmt(scb, YANG_K_LIST, list->name); break; case OBJ_TYP_CHOICE: choic = obj->def.choic; write_appinfoQ(scb, mod, cp, &obj->appinfoQ, indent); write_when(scb, cp, obj, indent); write_iffeatureQ(scb, mod, cp, &obj->iffeatureQ, indent); if (choic->defval) { write_complex_str(scb, cp->tkc, &choic->defval, YANG_K_DEFAULT, choic->defval, indent, 2, TRUE); } write_mandatory_stmt(scb, obj, indent); write_sdr(scb, cp, obj, indent); write_objects(scb, mod, cp, choic->caseQ, indent); ses_putstr_indent(scb, END_SEC, startindent); write_endsec_cmt(scb, YANG_K_CHOICE, choic->name); break; case OBJ_TYP_CASE: cas = obj->def.cas; if (fullcase) { write_appinfoQ(scb, mod, cp, &obj->appinfoQ, indent); write_when(scb, cp, obj, indent); write_iffeatureQ(scb, mod, cp, &obj->iffeatureQ, indent); write_sdr(scb, cp, obj, indent); } write_objects(scb, mod, cp, cas->datadefQ, (fullcase) ? indent : startindent); if (fullcase) { ses_putstr_indent(scb, END_SEC, startindent); write_endsec_cmt(scb, YANG_K_CASE, cas->name); } break; case OBJ_TYP_USES: if (!cp->rawmode) { return ERR_NCX_SKIPPED; } uses = obj->def.uses; if (!first) { ses_putchar(scb, '\n'); } ses_indent(scb, startindent); write_id_a(scb, submod, YANG_K_USES, obj->tkerr.linenum); write_kw(scb, YANG_K_USES); ses_putchar(scb, ' '); fname = NULL; fversion = NULL; if (uses->prefix && xml_strcmp(uses->prefix, mod->prefix)) { if (uses->grp && uses->grp->tkerr.mod) { fname = uses->grp->tkerr.mod->name; fversion = uses->grp->tkerr.mod->version; } } write_a(scb, cp, fname, fversion, submod, uses->prefix, uses->name, uses->grp->tkerr.linenum); if (uses->descr || uses->ref || (obj->when && obj->when->exprstr) || uses->status != NCX_STATUS_CURRENT || !dlq_empty(uses->datadefQ) || !dlq_empty(&obj->appinfoQ)) { ses_putstr(scb, START_SEC); write_appinfoQ(scb, mod, cp, &obj->appinfoQ, indent); write_when(scb, cp, obj, indent); write_iffeatureQ(scb, mod, cp, &obj->iffeatureQ, indent); write_sdr(scb, cp, obj, indent); write_objects(scb, mod, cp, uses->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); } else { ses_putchar(scb, ';'); } break; case OBJ_TYP_AUGMENT: if (!cp->rawmode) { return ERR_NCX_SKIPPED; } aug = obj->def.augment; if (!first) { ses_putchar(scb, '\n'); } ses_indent(scb, startindent); write_id_a(scb, submod, YANG_K_AUGMENT, obj->tkerr.linenum); write_kw(scb, YANG_K_AUGMENT); ses_putchar(scb, ' '); fname = NULL; fversion = NULL; if (aug->targobj && aug->targobj->tkerr.mod && aug->targobj->tkerr.mod != mod) { fname = aug->targobj->tkerr.mod->name; fversion = aug->targobj->tkerr.mod->version; } write_a2(scb, cp, fname, fversion, submod, obj_get_name(aug->targobj), aug->targobj->tkerr.linenum, aug->target); if (isempty) { ses_putchar(scb, ';'); return NO_ERR; } ses_putstr(scb, START_SEC); write_appinfoQ(scb, mod, cp, &obj->appinfoQ, indent); write_when(scb, cp, obj, indent); write_iffeatureQ(scb, mod, cp, &obj->iffeatureQ, indent); write_sdr(scb, cp, obj, indent); write_objects(scb, mod, cp, &aug->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); break; case OBJ_TYP_RPC: rpc = obj->def.rpc; write_appinfoQ(scb, mod, cp, &obj->appinfoQ, indent); write_iffeatureQ(scb, mod, cp, &obj->iffeatureQ, indent); write_sdr(scb, cp, obj, indent); write_typedefs(scb, mod, cp, &rpc->typedefQ, indent); write_groupings(scb, mod, cp, &rpc->groupingQ, indent); write_objects(scb, mod, cp, &rpc->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); write_endsec_cmt(scb, YANG_K_RPC, rpc->name); break; case OBJ_TYP_RPCIO: rpcio = obj->def.rpcio; if (!dlq_empty(&rpcio->typedefQ) || !dlq_empty(&rpcio->groupingQ) || !dlq_empty(&rpcio->datadefQ) || !dlq_empty(&obj->appinfoQ)) { write_href_id(scb, submod, obj_get_name(obj), NULL, startindent, obj->tkerr.linenum, FALSE, !first); write_appinfoQ(scb, mod, cp, &obj->appinfoQ, indent); write_typedefs(scb, mod, cp, &rpcio->typedefQ, indent); write_groupings(scb, mod, cp, &rpcio->groupingQ, indent); write_objects(scb, mod, cp, &rpcio->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); } break; case OBJ_TYP_NOTIF: notif = obj->def.notif; write_appinfoQ(scb, mod, cp, &obj->appinfoQ, indent); write_iffeatureQ(scb, mod, cp, &obj->iffeatureQ, indent); write_sdr(scb, cp, obj, indent); write_typedefs(scb, mod, cp, ¬if->typedefQ, indent); write_groupings(scb, mod, cp, ¬if->groupingQ, indent); write_objects(scb, mod, cp, ¬if->datadefQ, indent); ses_putstr_indent(scb, END_SEC, startindent); write_endsec_cmt(scb, YANG_K_NOTIFICATION, notif->name); break; case OBJ_TYP_REFINE: if (!cp->rawmode) { return ERR_NCX_SKIPPED; } refine = obj->def.refine; if (!first) { ses_putchar(scb, '\n'); } ses_indent(scb, startindent); write_id_a(scb, submod, YANG_K_REFINE, obj->tkerr.linenum); write_kw(scb, YANG_K_REFINE); ses_putchar(scb, ' '); fname = NULL; fversion = NULL; if (refine->targobj && refine->targobj->tkerr.mod && refine->targobj->tkerr.mod != mod) { fname = refine->targobj->tkerr.mod->name; fversion = refine->targobj->tkerr.mod->version; } write_a2(scb, cp, fname, fversion, submod, obj_get_name(refine->targobj), refine->targobj->tkerr.linenum, refine->target); if (isempty) { ses_putchar(scb, ';'); return NO_ERR; } ses_putstr(scb, START_SEC); switch (refine->targobj->objtype) { case OBJ_TYP_ANYXML: /* must-stmt refine not in -07*/ write_musts(scb, cp, obj_get_mustQ(obj), indent); write_config_stmt_force(scb, obj, indent); write_mandatory_stmt_force(scb, obj, indent); write_sdr(scb, cp, obj, indent); break; case OBJ_TYP_CONTAINER: write_musts(scb, cp, obj_get_mustQ(obj), indent); write_presence_stmt(scb, cp->tkc, &refine->presence, refine->presence, indent); write_config_stmt_force(scb, obj, indent); write_sdr(scb, cp, obj, indent); break; case OBJ_TYP_LEAF: write_musts(scb, cp, obj_get_mustQ(obj), indent); if (refine->def) { write_complex_str(scb, cp->tkc, &refine->def, YANG_K_DEFAULT, refine->def, indent, 2, TRUE); } write_config_stmt_force(scb, obj, indent); write_mandatory_stmt_force(scb, obj, indent); write_sdr(scb, cp, obj, indent); break; case OBJ_TYP_LEAF_LIST: case OBJ_TYP_LIST: write_musts(scb, cp, obj_get_mustQ(obj), indent); write_config_stmt_force(scb, obj, indent); write_minmax(scb, refine->minelems_tkerr.linenum != 0, refine->minelems, refine->maxelems_tkerr.linenum != 0, refine->maxelems, indent); write_sdr(scb, cp, obj, indent); break; case OBJ_TYP_CHOICE: if (refine->def) { write_complex_str(scb, cp->tkc, &refine->def, YANG_K_DEFAULT, refine->def, indent, 2, TRUE); } write_config_stmt_force(scb, obj, indent); write_mandatory_stmt_force(scb, obj, indent); write_sdr(scb, cp, obj, indent); break; case OBJ_TYP_CASE: write_sdr(scb, cp, obj, indent); break; default: ; } ses_putstr_indent(scb, END_SEC, startindent); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return NO_ERR; } /* write_object */ /******************************************************************** * FUNCTION write_objects * * Generate the HTML for the specified datadefQ * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * datadefQ == que of obj_template_t to use * startindent == start indent count * *********************************************************************/ static void write_objects (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *datadefQ, int32 startindent) { obj_template_t *obj; status_t res; boolean first; if (dlq_empty(datadefQ)) { return; } if (datadefQ == &mod->datadefQ) { write_banner_cmt(scb, mod, cp, (const xmlChar *)"objects", startindent); } first = TRUE; for (obj = (obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (obj_is_hidden(obj)) { continue; } res = write_object(scb, mod, cp, obj, startindent, first); if (first && res == NO_ERR) { first = FALSE; } } } /* write_objects */ /******************************************************************** * FUNCTION write_extension * * Generate the HTML for 1 extension * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * ext == ext_template_t to use * startindent == start indent count * *********************************************************************/ static void write_extension (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const ext_template_t *ext, int32 startindent) { const xmlChar *submod; int32 indent; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; indent = startindent + ses_indent_count(scb); write_href_id(scb, submod, YANG_K_EXTENSION, ext->name, startindent, ext->tkerr.linenum, FALSE, TRUE); write_appinfoQ(scb, mod, cp, &ext->appinfoQ, indent); /* argument sub-clause */ if (ext->arg) { write_simple_str(scb, YANG_K_ARGUMENT, ext->arg, indent, 2, FALSE); write_simple_str(scb, YANG_K_YIN_ELEMENT, ext->argel ? NCX_EL_TRUE : NCX_EL_FALSE, indent + ses_indent_count(scb), 0, TRUE); ses_putstr_indent(scb, END_SEC, indent); } /* status field */ write_status(scb, ext->status, indent); /* description field */ if (ext->descr) { write_complex_str(scb, cp->tkc, &ext->descr, YANG_K_DESCRIPTION, ext->descr, indent, 2, TRUE); } /* reference field */ if (ext->ref) { write_reference_str(scb, ext->ref, indent); } /* end extension clause */ ses_putstr_indent(scb, END_SEC, startindent); } /* write_extension */ /******************************************************************** * FUNCTION write_extensions * * Generate the HTML for the specified extensionQ * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * extensionQ == que of ext_template_t to use * startindent == start indent count * *********************************************************************/ static void write_extensions (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *extensionQ, int32 startindent) { const ext_template_t *ext; if (dlq_empty(extensionQ)) { return; } write_banner_cmt(scb, mod, cp, (const xmlChar *)"extensions", startindent); for (ext = (const ext_template_t *)dlq_firstEntry(extensionQ); ext != NULL; ext = (const ext_template_t *)dlq_nextEntry(ext)) { write_extension(scb, mod, cp, ext, startindent); } } /* write_extensions */ /******************************************************************** * FUNCTION write_identity * * Generate the HTML for 1 identity statement * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * identity == ncx_identity_t to use * startindent == start indent count * *********************************************************************/ static void write_identity (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const ncx_identity_t *identity, int32 startindent) { const xmlChar *submod; int32 indent; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; indent = startindent + ses_indent_count(scb); write_href_id(scb, submod, YANG_K_IDENTITY, identity->name, startindent, identity->tkerr.linenum, FALSE, TRUE); write_appinfoQ(scb, mod, cp, &identity->appinfoQ, indent); #if 0 /* optional base sub-clause */ if (identity->base) { write_identity_base(scb, mod, cp, identity, indent); } #endif /* status field */ write_status(scb, identity->status, indent); /* description field */ if (identity->descr) { write_complex_str(scb, cp->tkc, &identity->descr, YANG_K_DESCRIPTION, identity->descr, indent, 2, TRUE); } /* reference field */ if (identity->ref) { write_reference_str(scb, identity->ref, indent); } /* end identity clause */ ses_putstr_indent(scb, END_SEC, startindent); } /* write_identity */ /******************************************************************** * FUNCTION write_identities * * Generate the HTML for the specified identityQ * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * identityQ == que of ncx_identity_t to use * startindent == start indent count * *********************************************************************/ static void write_identities (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *identityQ, int32 startindent) { const ncx_identity_t *identity; if (dlq_empty(identityQ)) { return; } write_banner_cmt(scb, mod, cp, (const xmlChar *)"identities", startindent); for (identity = (const ncx_identity_t *) dlq_firstEntry(identityQ); identity != NULL; identity = (const ncx_identity_t *) dlq_nextEntry(identity)) { write_identity(scb, mod, cp, identity, startindent); } } /* write_identities */ /******************************************************************** * FUNCTION write_feature * * Generate the HTML for 1 feature statement * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * feature == ncx_feature_t to use * startindent == start indent count * *********************************************************************/ static void write_feature (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const ncx_feature_t *feature, int32 startindent) { const xmlChar *submod; int32 indent; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; indent = startindent + ses_indent_count(scb); write_href_id(scb, submod, YANG_K_FEATURE, feature->name, startindent, feature->tkerr.linenum, FALSE, TRUE); write_appinfoQ(scb, mod, cp, &feature->appinfoQ, indent); /* optional Q of if-feature statements */ write_iffeatureQ(scb, mod, cp, &feature->iffeatureQ, indent); /* status field */ write_status(scb, feature->status, indent); /* description field */ if (feature->descr) { write_complex_str(scb, cp->tkc, &feature->descr, YANG_K_DESCRIPTION, feature->descr, indent, 2, TRUE); } /* reference field */ if (feature->ref) { write_reference_str(scb, feature->ref, indent); } /* end feature clause */ ses_putstr_indent(scb, END_SEC, startindent); } /* write_feature */ /******************************************************************** * FUNCTION write_features * * Generate the HTML for the specified featureQ * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * featureQ == que of ncx_feature_t to use * startindent == start indent count * *********************************************************************/ static void write_features (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *featureQ, int32 startindent) { const ncx_feature_t *feature; if (dlq_empty(featureQ)) { return; } write_banner_cmt(scb, mod, cp, (const xmlChar *)"features", startindent); for (feature = (const ncx_feature_t *) dlq_firstEntry(featureQ); feature != NULL; feature = (const ncx_feature_t *) dlq_nextEntry(feature)) { write_feature(scb, mod, cp, feature, startindent); } } /* write_features */ /******************************************************************** * FUNCTION write_deviate * * Generate the HTML for 1 deviate statement * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * deviate== obj_deviate_t to use * startindent == start indent count * *********************************************************************/ static void write_deviate (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const obj_deviate_t *deviate, int32 startindent) { const xmlChar *submod; int32 indent; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; indent = startindent + ses_indent_count(scb); ses_indent(scb, startindent); write_id_a(scb, submod, YANG_K_DEVIATE, deviate->tkerr.linenum); write_kw(scb, YANG_K_DEVIATE); ses_putchar(scb, ' '); ses_putstr(scb, obj_get_deviate_arg(deviate->arg)); if (deviate->empty) { ses_putchar(scb, ';'); return; } ses_putstr(scb, START_SEC); /* extension usage */ write_appinfoQ(scb, mod, cp, &deviate->appinfoQ, indent); /* type-stmt */ if (deviate->typdef) { write_type_clause(scb, mod, cp, deviate->typdef, NULL, indent); } /* units-stmt */ if (deviate->units) { write_simple_str(scb, YANG_K_UNITS, deviate->units, indent, 2, TRUE); } /* default-stmt */ if (deviate->defval) { write_complex_str(scb, cp->tkc, &deviate->defval, YANG_K_DEFAULT, deviate->defval, indent, 2, TRUE); } /* config-stmt, only if actually set */ if (deviate->config_tkerr.linenum != 0) { write_simple_str(scb, YANG_K_CONFIG, (deviate->config) ? NCX_EL_TRUE : NCX_EL_FALSE, indent, 0, TRUE); } /* mandatory-stmt, only if actually set */ if (deviate->mandatory_tkerr.linenum != 0) { write_simple_str(scb, YANG_K_MANDATORY, (deviate->mandatory) ? NCX_EL_TRUE : NCX_EL_FALSE, indent, 0, TRUE); } /* min-elements, max-elements */ write_minmax(scb, deviate->minelems_tkerr.linenum != 0, deviate->minelems, deviate->maxelems_tkerr.linenum != 0, deviate->maxelems, indent); /* must-stmts */ write_musts(scb, cp, &deviate->mustQ, indent); /* unique-stmts */ write_unique_stmts(scb, mod, cp, &deviate->uniqueQ, indent); ses_putstr_indent(scb, END_SEC, startindent); } /* write_deviate */ /******************************************************************** * FUNCTION write_deviation * * Generate the HTML for 1 deviation statement * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * deviation == obj_deviation_t to use * startindent == start indent count * *********************************************************************/ static void write_deviation (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const obj_deviation_t *deviation, int32 startindent) { const obj_deviate_t *deviate; const xmlChar *submod, *fname, *fversion; int32 indent; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; indent = startindent + ses_indent_count(scb); ses_indent(scb, startindent); write_id_a(scb, submod, YANG_K_DEVIATION, deviation->tkerr.linenum); write_kw(scb, YANG_K_DEVIATION); ses_putchar(scb, ' '); fname = NULL; fversion = NULL; if (deviation->targobj && deviation->targobj->tkerr.mod && deviation->targobj->tkerr.mod != mod) { fname = deviation->targobj->tkerr.mod->name; fversion = deviation->targobj->tkerr.mod->version; } write_a2(scb, cp, fname, fversion, submod, obj_get_name(deviation->targobj), deviation->targobj->tkerr.linenum, deviation->target); if (deviation->empty) { ses_putchar(scb, ';'); return; } ses_putstr(scb, START_SEC); write_appinfoQ(scb, mod, cp, &deviation->appinfoQ, indent); /* description field */ if (deviation->descr) { write_complex_str(scb, cp->tkc, &deviation->descr, YANG_K_DESCRIPTION, deviation->descr, indent, 2, TRUE); } /* reference field */ if (deviation->ref) { write_reference_str(scb, deviation->ref, indent); } /* 0 or more deviate-stmts */ for (deviate = (const obj_deviate_t *) dlq_firstEntry(&deviation->deviateQ); deviate != NULL; deviate = (const obj_deviate_t *) dlq_nextEntry(deviate)) { write_deviate(scb, mod, cp, deviate, indent); } /* end deviation statement */ ses_putstr_indent(scb, END_SEC, startindent); } /* write_deviation */ /******************************************************************** * FUNCTION write_deviations * * Generate the HTML for the specified deviationQ * * INPUTS: * scb == session control block to use for writing * mod == ncx_module_t struct in progress * cp == conversion parameters in use * deviationQ == que of obj_deviation_t to use * startindent == start indent count * *********************************************************************/ static void write_deviations (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *deviationQ, int32 startindent) { const obj_deviation_t *deviation; if (dlq_empty(deviationQ)) { return; } write_banner_cmt(scb, mod, cp, (const xmlChar *)"deviations", startindent); for (deviation = (const obj_deviation_t *) dlq_firstEntry(deviationQ); deviation != NULL; deviation = (const obj_deviation_t *) dlq_nextEntry(deviation)) { write_deviation(scb, mod, cp, deviation, startindent); } } /* write_deviations */ /******************************************************************** * FUNCTION write_import * * Generate the HTML for an import statement * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * modprefix == module prefix value for import * modname == module name to use in import clause * modrevision == module revision date (may be NULL) * submod == submodule name to use in URLs in unified mode only * appinfoQ == import appinfo Q (may be NULL) * indent == start indent count * *********************************************************************/ static void write_import (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const xmlChar *modprefix, const xmlChar *modname, const xmlChar *modrevision, const xmlChar *submod, const dlq_hdr_t *appinfoQ, int32 indent) { ses_indent(scb, indent); write_kw(scb, YANG_K_IMPORT); ses_putchar(scb, ' '); write_a(scb, cp, modname, (modrevision) ? modrevision : NULL, submod, NULL, NULL, 0); ses_putstr(scb, START_SEC); if (appinfoQ) { write_appinfoQ(scb, mod, cp, appinfoQ, indent + ses_indent_count(scb)); } write_simple_str(scb, YANG_K_PREFIX, modprefix, indent + ses_indent_count(scb), 0, TRUE); if (modrevision) { write_simple_str(scb, YANG_K_REVISION_DATE, modrevision, indent + ses_indent_count(scb), 2, TRUE); } ses_putstr_indent(scb, END_SEC, indent); } /* write_import */ /******************************************************************** * FUNCTION write_mod_header * * Generate the HTML for the module header info * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * startindent == start indent count * * OUTPUTS: * current indent count will be startindent + ses_indent_count(scb) * upon exit *********************************************************************/ static void write_mod_header (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, int32 startindent) { const ncx_import_t *imp; const yang_import_ptr_t *impptr; const ncx_include_t *inc; const ncx_revhist_t *rev; const xmlChar *submod; char buff[NCX_MAX_NUMLEN]; int indent; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; /* [sub]module name { */ ses_indent(scb, startindent); if (mod->ismod) { write_kw(scb, YANG_K_MODULE); } else { write_kw(scb, YANG_K_SUBMODULE); } ses_putchar(scb, ' '); write_id(scb, mod->name); ses_putstr(scb, START_SEC); ses_putchar(scb, '\n'); /* bump indent one level */ indent = startindent + ses_indent_count(scb); /* yang-version */ sprintf(buff, "%u", mod->langver); write_simple_str(scb, YANG_K_YANG_VERSION, (const xmlChar *)buff, indent, 0, TRUE); ses_putchar(scb, '\n'); /* namespace or belongs-to */ if (mod->ismod) { write_complex_str(scb, cp->tkc, &mod->ns, YANG_K_NAMESPACE, mod->ns, indent, 2, TRUE); ses_putchar(scb, '\n'); } else { write_complex_str(scb, cp->tkc, &mod->belongs, YANG_K_BELONGS_TO, mod->belongs, indent, 0, FALSE); } if (mod->ismod) { write_simple_str(scb, YANG_K_PREFIX, mod->prefix, indent, 0, TRUE); } else { write_simple_str(scb, YANG_K_PREFIX, mod->prefix, indent + indent, 0, TRUE); ses_putstr_indent(scb, (const xmlChar *)"}", indent); } /* blank line */ ses_putchar(scb, '\n'); /* imports section */ if (cp->unified) { for (impptr = (const yang_import_ptr_t *) dlq_firstEntry(&mod->saveimpQ); impptr != NULL; impptr = (const yang_import_ptr_t *)dlq_nextEntry(impptr)) { /* the appinfoQ info is not saved in unified mode ouput!! */ write_import(scb, mod, cp, impptr->modprefix, impptr->modname, impptr->revision, submod, NULL, indent); } if (!dlq_empty(&mod->saveimpQ)) { ses_putchar(scb, '\n'); } } else { for (imp = (const ncx_import_t *)dlq_firstEntry(&mod->importQ); imp != NULL; imp = (const ncx_import_t *)dlq_nextEntry(imp)) { write_import(scb, mod, cp, imp->prefix, imp->module, imp->revision, submod, &imp->appinfoQ, indent); } if (!dlq_empty(&mod->importQ)) { ses_putchar(scb, '\n'); } } /* includes section */ if (!cp->unified) { for (inc = (const ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (const ncx_include_t *)dlq_nextEntry(inc)) { ses_indent(scb, indent); write_kw(scb, YANG_K_INCLUDE); ses_putchar(scb, ' '); write_a(scb, cp, inc->submodule, (inc->submod) ? inc->submod->version : NULL, submod, NULL, NULL, 0); if (inc->revision || !dlq_empty(&inc->appinfoQ)) { ses_putstr(scb, START_SEC); write_appinfoQ(scb, mod, cp, &inc->appinfoQ, indent + ses_indent_count(scb)); if (inc->revision) { write_simple_str(scb, YANG_K_REVISION_DATE, inc->revision, indent + ses_indent_count(scb), 2, TRUE); } ses_putstr_indent(scb, END_SEC, indent); } else { ses_putchar(scb, ';'); } } if (!dlq_empty(&mod->includeQ)) { ses_putchar(scb, '\n'); } } /* organization */ if (mod->organization) { write_complex_str(scb, cp->tkc, &mod->organization, YANG_K_ORGANIZATION, mod->organization, indent, 2, TRUE); ses_putchar(scb, '\n'); } /* contact */ if (mod->contact_info) { write_complex_str(scb, cp->tkc, &mod->contact_info, YANG_K_CONTACT, mod->contact_info, indent, 2, TRUE); ses_putchar(scb, '\n'); } /* description */ if (mod->descr) { write_complex_str(scb, cp->tkc, &mod->descr, YANG_K_DESCRIPTION, mod->descr, indent, 2, TRUE); ses_putchar(scb, '\n'); } /* reference */ if (mod->ref) { write_reference_str(scb, mod->ref, indent); ses_putchar(scb, '\n'); } /* revision history section */ for (rev = (const ncx_revhist_t *) dlq_firstEntry(&mod->revhistQ); rev != NULL; rev = (const ncx_revhist_t *)dlq_nextEntry(rev)) { write_simple_str(scb, YANG_K_REVISION, rev->version, indent, 2, FALSE); if (rev->descr != NULL) { write_complex_str(scb, cp->tkc, &rev->descr, YANG_K_DESCRIPTION, rev->descr, indent + ses_indent_count(scb), 2, TRUE); } else { write_simple_str(scb, YANG_K_DESCRIPTION, EMPTY_STRING, indent + ses_indent_count(scb), 2, TRUE); } if (rev->ref) { write_reference_str(scb, rev->ref, indent + ses_indent_count(scb)); } ses_putstr_indent(scb, END_SEC, indent); ses_putchar(scb, '\n'); } } /* write_mod_header */ /******************************************************************** * FUNCTION datadefQ_toc_needed * * Check if a sub-menu is needed for a datadefQ * * INPUTS: * cp == conversion parameters to use * datadefQ == Q of obj_template_t to check * curlevel == current object nest level * * RETURNS: * TRUE if child nodes need to be included in a ToC sub-menu * FALSE if no child nodes, and a plain ToC entry is needed instead *********************************************************************/ static boolean datadefQ_toc_needed (const yangdump_cvtparms_t *cp, const dlq_hdr_t *datadefQ, uint32 curlevel) { const obj_template_t *obj; boolean cooked; if (curlevel >= MENU_NEST_LEVEL) { return FALSE; } cooked = strcmp(cp->objview, OBJVIEW_COOKED) ? FALSE : TRUE; for (obj = (const obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (const obj_template_t *)dlq_nextEntry(obj)) { if (obj_is_hidden(obj)) { continue; } if (cooked) { if (obj_is_data(obj) && obj_has_name(obj)) { return TRUE; } } else { if (obj_is_data(obj) && !obj_is_cloned(obj)) { return TRUE; } } } return FALSE; } /* datadefQ_toc_needed */ /******************************************************************** * FUNCTION write_toc_menu_datadefQ * * Generate the table of contents menu for a Q of object nodes * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * datadefQ == Q of obj_template_t to check * indent == starting indent count *********************************************************************/ static void write_toc_menu_datadefQ (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, const dlq_hdr_t *datadefQ, int32 indent) { const xmlChar *submod; const obj_template_t *obj; const dlq_hdr_t *childQ; boolean cooked; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; cooked = strcmp(cp->objview, OBJVIEW_COOKED) ? FALSE : TRUE; for (obj = (const obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (const obj_template_t *)dlq_nextEntry(obj)) { if (obj_is_hidden(obj)) { continue; } if (!obj_is_data(obj)) { continue; } if (obj->objtype == OBJ_TYP_RPCIO && obj_get_child_count(obj) == 0) { /* skip placeholder rpc/input and rpc/output */ continue; } if (cooked) { /* skip uses and augment objects in this mode */ if (!obj_has_name(obj)) { continue; } } else { /* skip over cloned objects */ if (obj_is_cloned(obj)) { continue; } } childQ = obj_get_cdatadefQ(obj); if (!childQ || !datadefQ_toc_needed(cp, childQ, obj_get_level(obj))) { start_elem(scb, EL_LI, NULL, indent); write_a(scb, cp, NULL, NULL, submod, NULL, obj_get_name(obj), obj->tkerr.linenum); end_elem(scb, EL_LI, -1); } else { start_elem(scb, EL_LI, CL_DADDY, indent); write_a(scb, cp, NULL, NULL, submod, NULL, obj_get_name(obj), obj->tkerr.linenum); start_elem(scb, EL_UL, NULL, indent + ses_indent_count(scb)); write_toc_menu_datadefQ(scb, mod, cp, childQ, indent + 2*ses_indent_count(scb)); end_elem(scb, EL_UL, indent + ses_indent_count(scb)); end_elem(scb, EL_LI, indent); } } } /* write_toc_menu_datadefQ */ /******************************************************************** * FUNCTION write_toc_menu_grp * * Generate the table of contents menu for a grouping in the module * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * *********************************************************************/ static void write_toc_menu_grp (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, int32 indent) { const xmlChar *submod; const grp_template_t *grp; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; for (grp = (const grp_template_t *)dlq_firstEntry(&mod->groupingQ); grp != NULL; grp = (const grp_template_t *)dlq_nextEntry(grp)) { if (!datadefQ_toc_needed(cp, &grp->datadefQ, 1)) { start_elem(scb, EL_LI, NULL, indent); write_a(scb, cp, NULL, NULL, submod, NULL, grp->name, grp->tkerr.linenum); end_elem(scb, EL_LI, -1); } else { start_elem(scb, EL_LI, CL_DADDY, indent); write_a(scb, cp, NULL, NULL, submod, NULL, grp->name, grp->tkerr.linenum); start_elem(scb, EL_UL, NULL, indent + ses_indent_count(scb)); write_toc_menu_datadefQ(scb, mod, cp, &grp->datadefQ, indent + 2*ses_indent_count(scb)); end_elem(scb, EL_UL, indent + ses_indent_count(scb)); end_elem(scb, EL_LI, indent); } } } /* write_toc_menu_grp */ /******************************************************************** * FUNCTION check_obj_toc_needed * * Check if ToC entries needed for a module or submodule * * INPUTS: * mod == module or submodule to check * cooked == TRUE if cooked mode, FALSE if raw mode * anyobj == address of any object return flag * anrpc == address of any RPC return flag * anynotif == address of any notification return flag * * OUTPUTS: * *anyobj == object return flag * *anrpc == RPC return flag * *anynotif == notification return flag * *********************************************************************/ static void check_obj_toc_needed (ncx_module_t *mod, boolean cooked, boolean *anyobj, boolean *anyrpc, boolean *anynotif) { const obj_template_t *obj; *anyobj = FALSE; *anyrpc = FALSE; *anynotif = FALSE; for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (obj_is_hidden(obj)) { continue; } if (cooked) { if (obj_is_data(obj)) { *anyobj = TRUE; } } else { if (!obj_is_cloned(obj) && obj_is_data(obj)) { *anyobj = TRUE; } } if (obj->objtype == OBJ_TYP_RPC) { *anyrpc = TRUE; } else if (obj->objtype == OBJ_TYP_NOTIF) { *anynotif = TRUE; } } } /* check_obj_toc_needed */ /******************************************************************** * FUNCTION write_toc_menu_rpc * * Write the ToC entries for the RPCs in a module or submodule * * INPUTS: * scb == session to use for writing * mod == module or submodule to check * cp == conversion parameters to use * indent == start indent count *********************************************************************/ static void write_toc_menu_rpc (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp, int32 indent) { obj_template_t *obj; const xmlChar *submod; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (obj->objtype != OBJ_TYP_RPC) { continue; } if (obj_is_hidden(obj)) { continue; } if (!(obj_rpc_has_input(obj) || obj_rpc_has_output(obj))) { start_elem(scb, EL_LI, NULL, indent); write_a(scb, cp, NULL, NULL, submod, NULL, obj_get_name(obj), obj->tkerr.linenum); end_elem(scb, EL_LI, -1); } else { start_elem(scb, EL_LI, CL_DADDY, indent); write_a(scb, cp, NULL, NULL, submod, NULL, obj_get_name(obj), obj->tkerr.linenum); start_elem(scb, EL_UL, NULL, indent + ses_indent_count(scb)); write_toc_menu_datadefQ(scb, mod, cp, &obj->def.rpc->datadefQ, indent + 2*ses_indent_count(scb)); end_elem(scb, EL_UL, indent + ses_indent_count(scb)); end_elem(scb, EL_LI, indent); } } } /* write_toc_menu_rpc */ /******************************************************************** * FUNCTION write_toc_menu_notif * * Write the ToC entries for the notifications in a module or submodule * * INPUTS: * scb == session to use for writing * mod == module or submodule to check * cp == conversion parameters to use * indent == start indent count *********************************************************************/ static void write_toc_menu_notif (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp, int32 indent) { const obj_template_t *obj; const xmlChar *submod; submod = (cp->unified && !mod->ismod) ? mod->name : NULL; for (obj = (const obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (const obj_template_t *)dlq_nextEntry(obj)) { if (obj->objtype != OBJ_TYP_NOTIF) { continue; } if (obj_is_hidden(obj)) { continue; } if (!datadefQ_toc_needed(cp, &obj->def.notif->datadefQ, 1)) { start_elem(scb, EL_LI, NULL, indent); write_a(scb, cp, NULL, NULL, submod, NULL, obj_get_name(obj), obj->tkerr.linenum); end_elem(scb, EL_LI, -1); } else { start_elem(scb, EL_LI, CL_DADDY, indent); write_a(scb, cp, NULL, NULL, submod, NULL, obj_get_name(obj), obj->tkerr.linenum); start_elem(scb, EL_UL, NULL, indent + ses_indent_count(scb)); write_toc_menu_datadefQ(scb, mod, cp, &obj->def.notif->datadefQ, indent + 2*ses_indent_count(scb)); end_elem(scb, EL_UL, indent + ses_indent_count(scb)); end_elem(scb, EL_LI, indent); } } } /* write_toc_menu_notif */ /******************************************************************** * FUNCTION write_toc_menu * * Generate the table of contents menu for the module * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * *********************************************************************/ static void write_toc_menu (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp) { const typ_template_t *typ; const ext_template_t *ext; const xmlChar *submod; const yang_node_t *node; boolean anytyp, anygrp, anyobj, anyrpc, anynotif, anyext; boolean isplain, cooked; int32 indent; if (!xml_strcmp(cp->html_toc, (const xmlChar *)"plain")) { isplain = TRUE; } else if (!xml_strcmp(cp->html_toc, (const xmlChar *)"menu")) { isplain = FALSE; } else if (!xml_strcmp(cp->html_toc, (const xmlChar *)"none")) { return; } else { SET_ERROR(ERR_INTERNAL_VAL); return; } submod = (cp->unified && !mod->ismod) ? mod->name : NULL; cooked = strcmp(cp->objview, OBJVIEW_COOKED) ? FALSE : TRUE; anyobj = FALSE; anyrpc = FALSE; anynotif = FALSE; indent = ses_indent_count(scb); /* start menu list */ start_id_elem(scb, EL_UL, (isplain) ? NULL : ID_NAV, indent); /* check if any typedef ToC entries to generate */ anytyp = dlq_empty(&mod->typeQ) ? FALSE : TRUE; if (cp->unified && mod->ismod) { node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); while (!anytyp && node) { anytyp = dlq_empty(&node->submod->typeQ) ? FALSE : TRUE; node = (const yang_node_t *)dlq_nextEntry(node); } } /* generate typedef ToC entries */ if (anytyp) { start_elem(scb, EL_LI, NULL, 2*indent); write_a2(scb, cp, NULL, NULL, NULL, EMPTY_STRING, 0, (const xmlChar *)"Typedefs"); start_elem(scb, EL_UL, NULL, 3*indent); for (typ = (const typ_template_t *)dlq_firstEntry(&mod->typeQ); typ != NULL; typ = (const typ_template_t *)dlq_nextEntry(typ)) { start_elem(scb, EL_LI, NULL, 4*indent); write_a(scb, cp, NULL, NULL, submod, NULL, typ->name, typ_get_typ_linenum(typ)); end_elem(scb, EL_LI, -1); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { for (typ = (const typ_template_t *) dlq_firstEntry(&node->submod->typeQ); typ != NULL; typ = (const typ_template_t *)dlq_nextEntry(typ)) { start_elem(scb, EL_LI, NULL, 4*indent); write_a(scb, cp, NULL, NULL, node->submod->name, NULL, typ->name, typ_get_typ_linenum(typ)); end_elem(scb, EL_LI, -1); } } } end_elem(scb, EL_UL, 3*indent); end_elem(scb, EL_LI, 2*indent); } /* check if any grouping ToC entries to generate */ if (cooked) { anygrp = FALSE; } else { anygrp = dlq_empty(&mod->groupingQ) ? FALSE : TRUE; if (cp->unified && mod->ismod) { node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); while (!anygrp && node) { anygrp = dlq_empty(&node->submod->groupingQ) ? FALSE : TRUE; node = (const yang_node_t *)dlq_nextEntry(node); } } } /* generate grouping ToC entries in raw objview mode only */ if (anygrp) { start_elem(scb, EL_LI, NULL, 2*indent); write_a2(scb, cp, NULL, NULL, NULL, EMPTY_STRING, 0, (const xmlChar *)"Groupings"); start_elem(scb, EL_UL, NULL, 3*indent); write_toc_menu_grp(scb, mod, cp, 4*indent); if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { write_toc_menu_grp(scb, node->submod, cp, 4*indent); } } end_elem(scb, EL_UL, 3*indent); end_elem(scb, EL_LI, 2*indent); } /* check if any object, RPC, or notification ToC entries to generate */ check_obj_toc_needed(mod, cooked, &anyobj, &anyrpc, &anynotif); if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { check_obj_toc_needed(node->submod, cooked, &anyobj, &anyrpc, &anynotif); } } /* generate data object ToC entries */ if (anyobj) { start_elem(scb, EL_LI, NULL, 2*indent); write_a2(scb, cp, NULL, NULL, NULL, EMPTY_STRING, 0, (const xmlChar *)"Objects"); start_elem(scb, EL_UL, NULL, 3*indent); write_toc_menu_datadefQ(scb, mod, cp, &mod->datadefQ, 4*indent); if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { write_toc_menu_datadefQ(scb, node->submod, cp, &node->submod->datadefQ, 4*indent); } } end_elem(scb, EL_UL, 3*indent); end_elem(scb, EL_LI, 2*indent); } /* generate RPC method ToC entries */ if (anyrpc) { start_elem(scb, EL_LI, NULL, 2*indent); write_a2(scb, cp, NULL, NULL, NULL, EMPTY_STRING, 0, (const xmlChar *)"RPC Methods"); start_elem(scb, EL_UL, NULL, 3*indent); write_toc_menu_rpc(scb, mod, cp, 4*indent); if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { write_toc_menu_rpc(scb, node->submod, cp, 4*indent); } } end_elem(scb, EL_UL, 3*indent); end_elem(scb, EL_LI, 2*indent); } /* generate notofocation ToC entries */ if (anynotif) { start_elem(scb, EL_LI, NULL, 2*indent); write_a2(scb, cp, NULL, NULL, NULL, EMPTY_STRING, 0, (const xmlChar *)"Notifications"); start_elem(scb, EL_UL, NULL, 3*indent); write_toc_menu_notif(scb, mod, cp, 4*indent); if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { write_toc_menu_notif(scb, node->submod, cp, 4*indent); } } end_elem(scb, EL_UL, 3*indent); end_elem(scb, EL_LI, 2*indent); } /* check if any extension ToC entries to generate */ anyext = dlq_empty(&mod->extensionQ) ? FALSE : TRUE; if (cp->unified && mod->ismod) { node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); while (!anyext && node) { anyext = dlq_empty(&node->submod->extensionQ) ? FALSE : TRUE; node = (const yang_node_t *)dlq_nextEntry(node); } } /* generate extension ToC entries */ if (anyext) { start_elem(scb, EL_LI, NULL, 2*indent); write_a2(scb, cp, NULL, NULL, NULL, EMPTY_STRING, 0, (const xmlChar *)"Extensions"); start_elem(scb, EL_UL, NULL, 3*indent); /* generate actual drop-down list items */ for (ext = (const ext_template_t *)dlq_firstEntry(&mod->extensionQ); ext != NULL; ext = (const ext_template_t *)dlq_nextEntry(ext)) { start_elem(scb, EL_LI, NULL, 4*indent); write_a(scb, cp, NULL, NULL, submod, NULL, ext->name, ext->tkerr.linenum); end_elem(scb, EL_LI, -1); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { for (ext = (const ext_template_t *) dlq_firstEntry(&node->submod->extensionQ); ext != NULL; ext = (const ext_template_t *)dlq_nextEntry(ext)) { start_elem(scb, EL_LI, NULL, 4*indent); write_a(scb, cp, NULL, NULL, submod, NULL, ext->name, ext->tkerr.linenum); end_elem(scb, EL_LI, -1); } } } end_elem(scb, EL_UL, 3*indent); end_elem(scb, EL_LI, 2*indent); } /* end the entire ToC list */ end_elem(scb, EL_UL, indent); ses_putchar(scb, '\n'); } /* write_toc_menu */ /******************************************************************** * FUNCTION write_html_header * * Generate the HTML header section for the specified module * * INPUTS: * scb == session control block to use for writing * mod == module in progress * indent == the indent amount per line, starting at 0 * css_file == optional CSS inline file name *********************************************************************/ static void write_html_header (ses_cb_t *scb, const ncx_module_t *mod, int32 indent, const xmlChar *css_file) { xmlChar *filespec; status_t res; xmlChar buffer[NCX_VERSION_BUFFSIZE]; ses_putstr_indent(scb, (const xmlChar *) "", 0); ses_putstr_indent(scb, (const xmlChar *)"", 0); ses_putstr_indent(scb, (const xmlChar *)"", 0); ses_putstr_indent(scb, (const xmlChar *)"", indent); ses_putstr(scb, (const xmlChar *)"YANG "); ses_putstr(scb, (mod->ismod) ? (const xmlChar *)"Module " : (const xmlChar *)"Submodule "); ses_putstr(scb, mod->name); ses_putstr(scb, (const xmlChar *)" Version "); if (mod->version) { ses_putstr(scb, mod->version); } else { ses_putstr(scb, NCX_EL_NONE); } ses_putstr(scb, (const xmlChar *)""); ses_putstr_indent(scb, (const xmlChar *) "", indent); ses_putstr_indent(scb, (const xmlChar *) "", 2*indent); ses_putstr_indent(scb, (const xmlChar *) ""); filespec = NULL; if (css_file) { filespec = ncxmod_find_data_file(css_file, FALSE, &res); if (filespec) { ses_putstr_indent(scb, (const xmlChar *) "", indent); } } if (filespec == NULL) { ses_putstr_indent(scb, (const xmlChar *) "", indent); } ses_putstr_indent(scb, (const xmlChar *)"", 0); if (filespec) { m__free(filespec); } } /* write_html_header */ /******************************************************************** * FUNCTION write_html_body * * Generate the HTML body section for the specified module * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * *********************************************************************/ static void write_html_body (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp) { const yang_node_t *node; const yang_stmt_t *stmt; boolean stmtmode; if (cp->html_div) { /* start wrapper div */ start_elem(scb, EL_DIV, NULL, 0); } else { /* */ start_elem(scb, EL_BODY, NULL, 0); } /* title */ start_elem(scb, EL_H1, CL_YANG, cp->indent); #ifdef MATCH_HTML_TITLE if (mod->ismod) { ses_putstr(scb, YANG_K_MODULE); } else { ses_putstr(scb, YANG_K_SUBMODULE); } ses_putstr(scb, (const xmlChar *)": "); ses_putstr(scb, mod->name); ses_putstr(scb, (const xmlChar *)", version: "); if (mod->version) { ses_putstr(scb, mod->version); } else { ses_putstr(scb, NCX_EL_NONE); } #else ses_putstr(scb, mod->sourcefn); #endif end_elem(scb, EL_H1, -1); ses_putchar(scb, '\n'); /* table of contents */ if (cp->html_toc) { write_toc_menu(scb, mod, cp); ses_putstr(scb, (const xmlChar *)"\n
"); } /* start yellow box div */ start_elem(scb, EL_DIV, CL_YANG, 0); /* rest of module body is pre-formatted */ start_elem(scb, EL_PRE, NULL, 0); ses_putchar(scb, '\n'); /* module header */ write_mod_header(scb, mod, cp, cp->indent); /* if the top-level statement order was saved, it was only for * the YANG_PT_TOP module, and none of the sub-modules * generate the canonical order for all the submodule content * if 'unified mode' * * Generate the main module canonical order only * if top-level statement order is ever disabled */ stmtmode = dlq_empty(&mod->stmtQ) ? FALSE : TRUE; /* TBD: need a better way to generate all the top-level * extensions. This approach is broken because it gathers * them and puts them at the start after the header * * will need to check line numbers as other constructs * are generated to find any extensions that 'fit' * in particuolar line number ranges */ write_appinfoQ(scb, mod, cp, &mod->appinfoQ, 2*cp->indent); if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_appinfoQ(scb, node->submod, cp, &node->submod->appinfoQ, 2*cp->indent); } } } /* 1) features */ if (!stmtmode) { write_features(scb, mod, cp, &mod->featureQ, 2*cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_features(scb, node->submod, cp, &node->submod->featureQ, 2*cp->indent); } } } /* 2) identities */ if (!stmtmode) { write_identities(scb, mod, cp, &mod->identityQ, 2*cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_identities(scb, node->submod, cp, &node->submod->identityQ, 2*cp->indent); } } } /* 3) typedefs */ if (!stmtmode) { write_typedefs(scb, mod, cp, &mod->typeQ, 2*cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_typedefs(scb, node->submod, cp, &node->submod->typeQ, 2*cp->indent); } } } /* 4) groupings */ if (!stmtmode) { write_groupings(scb, mod, cp, &mod->groupingQ, 2*cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_groupings(scb, node->submod, cp, &node->submod->groupingQ, 2*cp->indent); } } } /* 5) extensions */ if (!stmtmode) { write_extensions(scb, mod, cp, &mod->extensionQ, 2*cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_extensions(scb, node->submod, cp, &node->submod->extensionQ, 2*cp->indent); } } } /* 6) objects */ if (!stmtmode) { write_objects(scb, mod, cp, &mod->datadefQ, 2*cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_objects(scb, node->submod, cp, &node->submod->datadefQ, 2*cp->indent); } } } /* 6) deviations */ if (!stmtmode) { write_deviations(scb, mod, cp, &mod->deviationQ, 2*cp->indent); } if (cp->unified && mod->ismod) { for (node = (const yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (const yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_deviations(scb, node->submod, cp, &node->submod->deviationQ, 2*cp->indent); } } } /* check statement mode on top-level module only */ if (stmtmode) { for (stmt = (const yang_stmt_t *)dlq_firstEntry(&mod->stmtQ); stmt != NULL; stmt = (const yang_stmt_t *)dlq_nextEntry(stmt)) { switch (stmt->stmttype) { case YANG_ST_NONE: SET_ERROR(ERR_INTERNAL_VAL); break; case YANG_ST_TYPEDEF: write_typedef(scb, mod, cp, stmt->s.typ, 2*cp->indent, FALSE); break; case YANG_ST_GROUPING: write_grouping(scb, mod, cp, stmt->s.grp, 2*cp->indent, FALSE); break; case YANG_ST_EXTENSION: write_extension(scb, mod, cp, stmt->s.ext, 2*cp->indent); break; case YANG_ST_OBJECT: write_object(scb, mod, cp, stmt->s.obj, 2*cp->indent, FALSE); break; case YANG_ST_IDENTITY: write_identity(scb, mod, cp, stmt->s.identity, 2*cp->indent); break; case YANG_ST_FEATURE: write_feature(scb, mod, cp, stmt->s.feature, 2*cp->indent); break; case YANG_ST_DEVIATION: write_deviation(scb, mod, cp, stmt->s.deviation, 2*cp->indent); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } } /* end module and page */ ses_indent(scb, cp->indent); ses_putchar(scb, '}'); write_endsec_cmt(scb, (mod->ismod) ? YANG_K_MODULE : YANG_K_SUBMODULE, mod->name); end_elem(scb, EL_PRE, 0); /* end yellow box div */ end_elem(scb, EL_DIV, 0); if (cp->html_div) { end_elem(scb, EL_DIV, 0); } else { end_elem(scb, EL_BODY, 0); } } /* write_html_body */ /********* E X P O R T E D F U N C T I O N S **************/ /******************************************************************** * FUNCTION html_convert_module * * Generate HTML version of the specified module(s) * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * * RETURNS: * status *********************************************************************/ status_t html_convert_module (yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb) { ncx_module_t *mod; status_t res; res = NO_ERR; /* the module should already be parsed and loaded */ mod = pcb->top; if (!mod) { return SET_ERROR(ERR_NCX_MOD_NOT_FOUND); } if (!cp->html_div) { write_html_header(scb, mod, cp->indent, cp->css_file); } write_html_body(scb, mod, cp); if (!cp->html_div) { ses_putstr(scb, (const xmlChar *)"\n\n"); } return res; } /* html_convert_module */ /* END file html.c */ yuma123_2.14/netconf/src/ydump/yangtree.h0000664000175000017500000000271114770023131020474 0ustar vladimirvladimir/* * Copyright (c) 2018, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangtree.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert YANG module to YANG tree format */ #include "ncxtypes.h" #include "ses.h" #include "status.h" #include "yang.h" #include "yangdump.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * FUNCTION yangtree_convert_module * * Convert the YANG module to YANG tree format * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == convert parms struct to use * scb == session to use for output * * RETURNS: * status *********************************************************************/ extern status_t yangtree_convert_module(const yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif yuma123_2.14/netconf/src/ydump/cyang.h0000664000175000017500000000475214770023131017766 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_cyang #define _H_cyang /* FILE: cyang.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert YANG module to canonical YANG format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 07-apr-08 abb Begun */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION cyang_convert_module * * Generate Canonical YANG from the specified module(s) * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * * RETURNS: * status *********************************************************************/ extern status_t cyang_convert_module (const yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_cyang */ yuma123_2.14/netconf/src/ydump/yangstats.h0000664000175000017500000000614014770023131020673 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangstats #define _H_yangstats /* FILE: yangstats.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* YANG metrics and statistics module ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 29-mar-10 abb Begun */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * FUNCTION yangstats_collect_module_stats * * Generate a statistics report for a module * * INPUTS: * pcb == parser control block with module to use * cp == conversion parameters to use *********************************************************************/ extern void yangstats_collect_module_stats (yang_pcb_t *pcb, yangdump_cvtparms_t *cp); /******************************************************************** * FUNCTION yangstats_output_module_stats * * Generate a statistics report for a module * * INPUTS: * pcb == parser control block with module to use * cp == conversion parameters to use * scb == session control block to use for output *********************************************************************/ extern void yangstats_output_module_stats (yang_pcb_t *pcb, yangdump_cvtparms_t *cp, ses_cb_t *scb); /******************************************************************** * FUNCTION yangstats_output_final_stats * * Generate the final stats report as requested * by the --stats and --stat-totals CLI parameters * * INPUTS: * cp == conversion parameters to use * scb == session control block to use for output *********************************************************************/ extern void yangstats_output_final_stats (yangdump_cvtparms_t *cp, ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangstats */ yuma123_2.14/netconf/src/ydump/sql.h0000664000175000017500000000527314770023131017463 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_sql #define _H_sql /* FILE: sql.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert YANG module to SQL format for the netconfcentral.org DB ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 18-feb-08 abb Begun */ #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* SQL database definition limits */ #define MAX_VERSION_LEN 32 #define MAX_URL_LEN 255 #define MAX_OBJECTID_LEN 900 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION sql_convert_module * * Convert the YANG module to SQL for input to * the netconfcentral object dictionary database * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == convert parms struct to use * scb == session to use for output * * RETURNS: * status *********************************************************************/ extern status_t sql_convert_module (const yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_sql */ yuma123_2.14/netconf/src/ydump/yangdump_util.c0000664000175000017500000001762114770023131021540 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yangdump_util.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 23-oct-09 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifndef _H_yangdump_util #include "yangdump_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * write_banner * * Write the yangdump startup banner * *********************************************************************/ void write_banner (void) { status_t res; xmlChar versionbuffer[NCX_VERSION_BUFFSIZE]; res = ncx_get_version(versionbuffer, NCX_VERSION_BUFFSIZE); if (res == NO_ERR) { log_write("\n\n*** Generated by yangdump %s", versionbuffer); log_write("\n*** "); log_write(COPYRIGHT_STRING_LINE0); } else { SET_ERROR(res); } } /* write_banner */ /******************************************************************** * write_banner_session * * Write the yangdump startup banner to a session * * INPUTS: * scb == session control block to use * *********************************************************************/ void write_banner_session (ses_cb_t *scb) { write_banner_session_ex(scb, TRUE); } /* write_banner_session */ /******************************************************************** * write_banner_session_ex * * Write the yangdump startup banner to a session * * INPUTS: * scb == session control block to use * wcopy == TRUE if the copyright string should be used * FALSE if not *********************************************************************/ void write_banner_session_ex (ses_cb_t *scb, boolean wcopy) { status_t res; xmlChar versionbuffer[NCX_VERSION_BUFFSIZE]; #ifdef DEBUG if (scb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); } #endif res = ncx_get_version(versionbuffer, NCX_VERSION_BUFFSIZE); if (res == NO_ERR) { ses_putstr(scb, (const xmlChar *)"\n\n*** Generated by yangdump "); ses_putstr(scb, versionbuffer); if (wcopy) { ses_putstr(scb, (const xmlChar *)"\n*** "); ses_putstr(scb, (const xmlChar *)COPYRIGHT_STRING_LINE0); } else { ses_putchar(scb, '\n'); } } else { SET_ERROR(res); } } /* write_banner_session_ex */ /******************************************************************** * FUNCTION find_reference * * Find the next 'RFC' or 'draft-' string in the buffer * Find the end of the RFC or draft name * * INPUTS: * buffer == buffer containing reference clause to use * ref == address of return start pointer * reflen == addredd of return 'ref' length * * OUTPUTS: * *ref == address of start of reference word * == NULL if no RFC xxxx of draft- found * *reflen == number of chars in *ref, or 0 if *ref is NULL * * RETURNS: * total number of chars processed *********************************************************************/ uint32 find_reference (const xmlChar *buffer, const xmlChar **ref, uint32 *reflen) { const xmlChar *str, *num, *p; uint32 numlen; #ifdef DEBUG if (buffer == NULL || ref == NULL || reflen == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif *ref = NULL; *reflen = 0; /* skip starting whitespace */ str = buffer; while (*str && xml_isspace(*str)) { str++; } /* check if the reference is to an RFC */ if (xml_strlen(str) >= 7 && !xml_strncmp(str, (const xmlChar *)"RFC", 3)) { /* look for the number to follow RFC; skip any whitespace */ p = &str[3]; while (*p && isspace(*p)) { p++; } /* find the number of consecutive digits */ num = p; while (*p && isdigit(*p)) { p++; } numlen = (uint32)(p - num); /* IETF RFC URLs are currently hard-wired 4 digit number */ if (numlen > 0 && numlen <= 4) { *ref = str; *reflen = (uint32)(p - str); } return (uint32)(p - buffer); } else if (!xml_strncmp(str, (const xmlChar *)"draft-", 6)) { num = &str[6]; while (*num && (!xml_isspace(*num)) && (*num != ';') && (*num != ':') && (*num != ',')) { num++; } /* make sure did not end on a dot char */ if (num != &str[6] && *num == '.') { num--; } numlen = (uint32)(num-str); if (numlen > 0) { *ref = str; *reflen = (uint32)(num-str); } return (uint32)(num - buffer); } else { return (uint32)(str - buffer); } } /* find_reference */ /******************************************************************** * FUNCTION print_subtree_banner * * Print the banner for the next module starting when * multiple modules are being processed by yangdump * * INPUTS: * cp == conversion parms to use * mod == module to use * *********************************************************************/ void print_subtree_banner (yangdump_cvtparms_t *cp, ncx_module_t *mod, ses_cb_t *scb) { #ifdef DEBUG if (cp == NULL || mod == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (mod->ismod) { sprintf((char *)cp->buff, "\nmodule %s", mod->name); } else { sprintf((char *)cp->buff, "\nsubmodule %s", mod->name); } ses_putstr(scb, (const xmlChar *)cp->buff); if (mod->version) { sprintf((char *)cp->buff, "@%s", mod->version); ses_putstr(scb, (const xmlChar *)cp->buff); } } /* print_subtree_banner */ /* END yangdump_util.c */ yuma123_2.14/netconf/src/ydump/xsd.h0000664000175000017500000001066114770023131017457 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xsd #define _H_xsd /* FILE: xsd.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert NCX module to XSD format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 15-nov-06 abb Begun */ #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangdump #include "yangdump.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xsd_convert_module * * Convert a [sub]module to XSD 1.0 format * module entry point: convert an entire module to a value struct * representing an XML element which represents an XSD * * INPUTS: * mod == module to convert * cp == conversion parms struct to use * retval == address of val_value_t to receive a malloced value struct * representing the XSD. Extra mallocs will be avoided by * referencing the mod_module_t strings. * Do not remove the module from the registry while this * val struct is being used!!! * top_attrs == pointer to receive the top element attributes * HACK: rpc_agt needs the top-level attrs to * be in xml_attr_t format, not val_value_t metaval format * * OUTPUTS: * *retval = malloced return value; MUST FREE THIS LATER * *top_attrs = filled in attrs queue * * RETURNS: * status *********************************************************************/ extern status_t xsd_convert_module (yang_pcb_t *pcb, ncx_module_t *mod, yangdump_cvtparms_t *cp, val_value_t **retval, xml_attrs_t *top_attrs); /******************************************************************** * FUNCTION xsd_load_typenameQ * * Generate unique type names for the entire module * including any submodules that were parsed as well * * INPUTS: * mod == module to process * * OUTPUTS: * *val = malloced return value * *retattrs = malled attrs queue * RETURNS: * status *********************************************************************/ extern status_t xsd_load_typenameQ (ncx_module_t *mod); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xsd */ yuma123_2.14/netconf/src/ydump/c_util.c0000664000175000017500000014333414770023131020137 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: c_util.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24-oct-09 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include "procdefs.h" #include "c_util.h" #include "log.h" #include "ncx.h" #include "ncx_str.h" #include "ncxmod.h" #include "ncxtypes.h" #include "ses.h" #include "status.h" #include "yangconst.h" #include "yangdump.h" #include "yangdump_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG /* #define C_UTIL_DEBUG 1 */ #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* used by write_key_parm */ typedef struct c_keywalker_parms_t_ { ses_cb_t *scb; dlq_hdr_t *objnameQ; const xmlChar *parmname; uint32 keycount; uint32 keynum; int32 startindent; boolean done; } c_keywalker_parms_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION new_id_string * * Allocate and fill in a constructed identifier to string binding * for an object struct of some kind * * INPUTS: * modname == module name to use * defname == definition name to use * cdefmode == c definition mode *********************************************************************/ static xmlChar * new_id_string (const xmlChar *modname, const xmlChar *defname, c_mode_t cmode) { xmlChar *buffer, *p; uint32 len = 0; /* get the idstr length */ switch (cmode) { case C_MODE_CALLBACK: break; case C_MODE_VARNAME: len++; break; default: len += xml_strlen(Y_PREFIX); } if (cmode != C_MODE_VARNAME) { len += xml_strlen(modname); } switch (cmode) { case C_MODE_OID: len += 3; /* _N_ */ break; case C_MODE_TYPEDEF: len += 2; /* _T */ break; case C_MODE_CALLBACK: case C_MODE_VARNAME: break; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } len += xml_strlen(defname); buffer = m__getMem(len+1); if (buffer == NULL) { return NULL; } /* fill in the idstr buffer */ p = buffer; switch (cmode) { case C_MODE_CALLBACK: break; case C_MODE_VARNAME: *p++ = 'k'; break; default: p += xml_strcpy(p, Y_PREFIX); } if (cmode != C_MODE_VARNAME) { p += ncx_copy_c_safe_str(p, modname); } switch (cmode) { case C_MODE_OID: p += xml_strcpy(p, (const xmlChar *)"_N_"); break; case C_MODE_TYPEDEF: p += xml_strcpy(p, (const xmlChar *)"_T"); break; case C_MODE_CALLBACK: case C_MODE_VARNAME: break; default: SET_ERROR(ERR_INTERNAL_VAL); } p += ncx_copy_c_safe_str(p, defname); #ifdef C_UTIL_DEBUG if (LOGDEBUG4) { log_debug4("\nnew-id(%s, %s, %d) = %s", modname, defname, cmode, buffer); } #endif return buffer; /* transfer buffer memory here */ } /* new_id_string */ /******************************************************************** * FUNCTION free_c_define * * Free an identifier to string binding * * INPUTS: * cdef == struct to free *********************************************************************/ static void free_c_define (c_define_t *cdef) { if (cdef->idstr != NULL) { m__free(cdef->idstr); } if (cdef->typstr != NULL) { m__free(cdef->typstr); } if (cdef->varstr != NULL) { m__free(cdef->varstr); } if (cdef->valstr != NULL) { m__free(cdef->valstr); } m__free(cdef); } /* free_c_define */ /******************************************************************** * FUNCTION new_c_define * * Allocate and fill in a constructed identifier to string binding * for an object struct of some kind * * INPUTS: * modname == module name to use * defname == definition name to use * cdefmode == c definition mode *********************************************************************/ static c_define_t * new_c_define (const xmlChar *modname, const xmlChar *defname, c_mode_t cmode) { c_define_t *cdef; cdef = m__getObj(c_define_t); if (cdef == NULL) { return NULL; } memset(cdef, 0x0, sizeof(c_define_t)); cdef->idstr = new_id_string(modname, defname, cmode); if (cdef->idstr == NULL) { free_c_define(cdef); return NULL; } if (cmode == C_MODE_CALLBACK) { cdef->typstr = new_id_string(modname, defname, C_MODE_TYPEDEF); if (cdef->typstr == NULL) { free_c_define(cdef); return NULL; } cdef->varstr = new_id_string(modname, defname, C_MODE_VARNAME); if (cdef->varstr == NULL) { free_c_define(cdef); return NULL; } } cdef->valstr = xml_strdup(defname); if (cdef->valstr == NULL) { free_c_define(cdef); return NULL; } return cdef; } /* new_c_define */ /******************************************************************** * FUNCTION write_key_parm * * Write the key parameter; walker function * * INPUTS: * obj == the key object * cookie1 == the key walker parameter block to use * cookie2 = not used * RETURNS: * TRUE to keep walking *********************************************************************/ static boolean write_key_parm (obj_template_t *obj, void *cookie1, void *cookie2) { c_keywalker_parms_t *parms = (c_keywalker_parms_t *)cookie1; xmlChar endchar; boolean isconst = FALSE; (void)cookie2; if (parms->done) { /* 'done' not used yet -- always walks until the end */ return FALSE; } if (++parms->keynum == parms->keycount) { endchar = 0; } else { endchar = ','; } if (typ_is_string(obj_get_basetype(obj))) { isconst = TRUE; } ses_indent(parms->scb, parms->startindent); /* write the data type, name and then endchar */ write_c_objtype_max(parms->scb, obj, parms->objnameQ, endchar, isconst, FALSE, /* needstar */ FALSE, /* usename */ FALSE, /* useprefix */ FALSE, /* isuser */ TRUE); /* isvar */ return TRUE; } /* write_key_parm */ /******************************************************************** * FUNCTION write_key_var * * Write the key variable; walker function * * INPUTS: * obj == the key object * cookie1 == the key walker parameter block to use * cookie2 = not used * RETURNS: * TRUE to keep walking *********************************************************************/ static boolean write_key_var (obj_template_t *obj, void *cookie1, void *cookie2) { c_keywalker_parms_t *parms = (c_keywalker_parms_t *)cookie1; xmlChar endchar = 0; boolean isconst = typ_is_string(obj_get_basetype(obj)) ? TRUE : FALSE; boolean notunion = (obj_get_basetype(obj) != NCX_BT_UNION); (void)cookie2; if (parms->done) { /* 'done' not used yet -- always walks until the end */ return FALSE; } ses_indent(parms->scb, parms->startindent); /* write the data type, name and then endchar */ write_c_objtype_max(parms->scb, obj, parms->objnameQ, endchar, isconst, FALSE, /* needstar */ FALSE, /* usename */ FALSE, /* useprefix */ FALSE, /* isuser */ TRUE); /* isvar */ ses_putstr(parms->scb, (const xmlChar *)" = "); if (notunion) { write_c_val_macro_type(parms->scb, obj); ses_putchar(parms->scb, '('); } ses_putstr(parms->scb, (const xmlChar *)"agt_get_key_value("); ses_putstr(parms->scb, parms->parmname); ses_putstr(parms->scb, (const xmlChar *)", &lastkey)"); if (notunion) { ses_putchar(parms->scb, ')'); } ses_putchar(parms->scb, ';'); return TRUE; } /* write_key_var */ /******************************************************************** * FUNCTION write_key_value * * Write the key value get-function-call; walker function * * INPUTS: * obj == the key object * cookie1 == the key walker parameter block to use * cookie2 = not used * RETURNS: * TRUE to keep walking *********************************************************************/ static boolean write_key_value (obj_template_t *obj, void *cookie1, void *cookie2) { c_keywalker_parms_t *parms = (c_keywalker_parms_t *)cookie1; const c_define_t *cdef = NULL; xmlChar endchar; (void)cookie2; if (++parms->keynum == parms->keycount) { endchar = 0; } else { endchar = ','; } ses_indent(parms->scb, parms->startindent); /* write the variable name and then endchar */ cdef = find_path_cdefine(parms->objnameQ, obj); if (cdef == NULL) { SET_ERROR(ERR_INTERNAL_VAL); } else { ses_putstr(parms->scb, cdef->varstr); if (endchar) { ses_putchar(parms->scb, endchar); } } return TRUE; } /* write_key_value */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION need_rpc_includes * * Check if the include-stmts for RPC methods are needed * * INPUTS: * mod == module in progress * cp == conversion parameters to use * * RETURNS: * TRUE if RPCs found * FALSE if no RPCs found *********************************************************************/ boolean need_rpc_includes (const ncx_module_t *mod, const yangdump_cvtparms_t *cp) { const ncx_include_t *inc; if (obj_any_rpcs(&mod->datadefQ)) { return TRUE; } if (cp->unified) { for (inc = (const ncx_include_t *) dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (const ncx_include_t *)dlq_nextEntry(inc)) { if (inc->submod && obj_any_rpcs(&inc->submod->datadefQ)) { return TRUE; } } } return FALSE; } /* need_rpc_includes */ /******************************************************************** * FUNCTION need_notif_includes * * Check if the include-stmts for notifications are needed * * INPUTS: * mod == module in progress * cp == conversion parameters to use * * RETURNS: * TRUE if notifcations found * FALSE if no notifications found *********************************************************************/ boolean need_notif_includes (const ncx_module_t *mod, const yangdump_cvtparms_t *cp) { const ncx_include_t *inc; if (obj_any_notifs(&mod->datadefQ)) { return TRUE; } if (cp->unified) { for (inc = (const ncx_include_t *) dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (const ncx_include_t *)dlq_nextEntry(inc)) { if (inc->submod && obj_any_notifs(&inc->submod->datadefQ)) { return TRUE; } } } return FALSE; } /* need_notif_includes */ /******************************************************************** * FUNCTION write_c_safe_str * * Generate a string token at the current line location * * INPUTS: * scb == session control block to use for writing * strval == string value *********************************************************************/ void write_c_safe_str (ses_cb_t *scb, const xmlChar *strval) { const xmlChar *s; s = strval; while (*s) { if (*s == '.' || *s == '-' || *s == NCXMOD_PSCHAR) { ses_putchar(scb, '_'); } else { ses_putchar(scb, *s); } s++; } } /* write_c_safe_str */ /******************************************************************** * FUNCTION write_c_str * * Generate a string token at the current line location * * INPUTS: * scb == session control block to use for writing * strval == string value * quotes == quotes style (0, 1, 2) *********************************************************************/ void write_c_str (ses_cb_t *scb, const xmlChar *strval, uint32 quotes) { switch (quotes) { case 1: ses_putchar(scb, '\''); break; case 2: ses_putchar(scb, '"'); break; default: ; } ses_putstr(scb, strval); switch (quotes) { case 1: ses_putchar(scb, '\''); break; case 2: ses_putchar(scb, '"'); break; default: ; } } /* write_c_str */ /******************************************************************** * FUNCTION write_c_simple_str * * Generate a simple clause on 1 line * * INPUTS: * scb == session control block to use for writing * kwname == keyword name * strval == string value * indent == indent count to use * quotes == quotes style (0, 1, 2) *********************************************************************/ void write_c_simple_str (ses_cb_t *scb, const xmlChar *kwname, const xmlChar *strval, int32 indent, uint32 quotes) { ses_putstr_indent(scb, kwname, indent); if (strval) { ses_putchar(scb, ' '); write_c_str(scb, strval, quotes); } } /* write_c_simple_str */ /******************************************************************** * * FUNCTION write_identifier * * Generate an identifier * * #module_DEFTYPE_idname * * INPUTS: * scb == session control block to use for writing * modname == module name start-string to use * defpart == internal string for deftype part (may be NULL) * idname == identifier name * isuser == TRUE if USER SIL file * FALSE if YUMA SIL file *********************************************************************/ void write_identifier (ses_cb_t *scb, const xmlChar *modname, const xmlChar *defpart, const xmlChar *idname, boolean isuser) { if (isuser) { ses_putstr(scb, U_PREFIX); } else { ses_putstr(scb, Y_PREFIX); } write_c_safe_str(scb, modname); ses_putchar(scb, '_'); if (defpart != NULL) { ses_putstr(scb, defpart); ses_putchar(scb, '_'); } write_c_safe_str(scb, idname); /***/ } /* write_identifier */ /******************************************************************** * FUNCTION write_ext_include * * Generate an include statement for an external file * * #include * * INPUTS: * scb == session control block to use for writing * hfile == H file name == file name to include (foo.h) * *********************************************************************/ void write_ext_include (ses_cb_t *scb, const xmlChar *hfile) { ses_putstr(scb, POUND_INCLUDE); ses_putstr(scb, (const xmlChar *)"<"); ses_putstr(scb, hfile); ses_putstr(scb, (const xmlChar *)">\n"); } /* write_ext_include */ /******************************************************************** * FUNCTION write_ncx_include * * Generate an include statement for an NCX file * * #ifndef _H_foo * #include "foo,h" * * INPUTS: * scb == session control block to use for writing * modname == module name to include (foo) * *********************************************************************/ void write_ncx_include (ses_cb_t *scb, const xmlChar *modname) { ses_putstr(scb, POUND_INCLUDE); ses_putchar(scb, '"'); ses_putstr(scb, modname); ses_putstr(scb, (const xmlChar *)".h\""); } /* write_ncx_include */ /******************************************************************** * FUNCTION write_cvt_include * * Generate an include statement for an NCX split SIL file * based on the format type * * INPUTS: * scb == session control block to use for writing * modname == module name to include (foo) * cvttyp == format enum to use *********************************************************************/ void write_cvt_include (ses_cb_t *scb, const xmlChar *modname, ncx_cvttyp_t cvttyp) { ses_putstr(scb, POUND_INCLUDE); ses_putchar(scb, '"'); switch (cvttyp) { case NCX_CVTTYP_C: case NCX_CVTTYP_CPP_TEST: case NCX_CVTTYP_H: break; case NCX_CVTTYP_UC: case NCX_CVTTYP_UH: ses_putstr(scb, NCX_USER_SIL_PREFIX); break; case NCX_CVTTYP_YC: case NCX_CVTTYP_YH: ses_putstr(scb, NCX_YUMA_SIL_PREFIX); break; default: SET_ERROR(ERR_INTERNAL_VAL); } ses_putstr(scb, modname); ses_putstr(scb, (const xmlChar *)".h\""); } /* write_cvt_include */ /******************************************************************** * FUNCTION write_qheader * * Generate a QHEADER with indentation * * \n{indentcnt}QHEADER * * INPUTS: * scb == session control block to use for writing * *********************************************************************/ void write_qheader (ses_cb_t *scb) { ses_indent(scb, ses_indent_count(scb)); ses_putstr(scb, QHEADER); } /* write_qheader */ /******************************************************************** * FUNCTION save_oid_cdefine * * Generate a #define binding for a definition and save it in the * specified Q of c_define_t structs * * INPUTS: * cdefineQ == Q of c_define_t structs to use * modname == module name to use * defname == object definition name to use * * OUTPUTS: * a new c_define_t is allocated and added to the cdefineQ * if returning NO_ERR; * * RETURNS: * status; duplicate C identifiers not supported yet * foo-1 --> foo_1 * foo.1 --> foo_1 * An error message will be generated if this type of error occurs *********************************************************************/ status_t save_oid_cdefine (dlq_hdr_t *cdefineQ, const xmlChar *modname, const xmlChar *defname) { c_define_t *newcdef, *testcdef; status_t res; int32 retval; #ifdef DEBUG if (cdefineQ == NULL || modname == NULL || defname == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif newcdef = new_c_define(modname, defname, C_MODE_OID); if (newcdef == NULL) { return ERR_INTERNAL_MEM; } /* keep the cdefineQ sorted by the idstr value */ res = NO_ERR; for (testcdef = (c_define_t *)dlq_firstEntry(cdefineQ); testcdef != NULL; testcdef = (c_define_t *)dlq_nextEntry(testcdef)) { retval = xml_strcmp(newcdef->idstr, testcdef->idstr); if (retval == 0) { if (xml_strcmp(newcdef->valstr, testcdef->valstr)) { /* error - duplicate ID with different original value */ res = ERR_NCX_INVALID_VALUE; log_error("\nError: C idenitifer conflict between " "'%s' and '%s'", newcdef->valstr, testcdef->valstr); } /* else duplicate is not a problem */ free_c_define(newcdef); return res; } else if (retval < 0) { dlq_insertAhead(newcdef, testcdef); return NO_ERR; } /* else try next entry */ } /* new last entry */ dlq_enque(newcdef, cdefineQ); return NO_ERR; } /* save_oid_cdefine */ /******************************************************************** * FUNCTION save_path_cdefine * * Generate a #define binding for a definition and save it in the * specified Q of c_define_t structs * * INPUTS: * cdefineQ == Q of c_define_t structs to use * modname == base module name to use * obj == object struct to use to generate path * cmode == mode to use * * OUTPUTS: * a new c_define_t is allocated and added to the cdefineQ * if returning NO_ERR; * * RETURNS: * status; duplicate C identifiers not supported yet * foo-1/a/b --> foo_1_a_b * foo.1/a.2 --> foo_1_a_b * An error message will be generated if this type of error occurs *********************************************************************/ status_t save_path_cdefine (dlq_hdr_t *cdefineQ, const xmlChar *modname, obj_template_t *obj, c_mode_t cmode) { c_define_t *newcdef, *testcdef; xmlChar *buffer; status_t res; int32 retval; #ifdef DEBUG if (cdefineQ == NULL || modname == NULL || obj == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif buffer = NULL; res = obj_gen_object_id(obj, &buffer); if (res != NO_ERR) { return res; } newcdef = new_c_define(modname, buffer, cmode); m__free(buffer); buffer = NULL; if (newcdef == NULL) { return ERR_INTERNAL_MEM; } newcdef->obj = obj; /* keep the cdefineQ sorted by the idstr value */ res = NO_ERR; for (testcdef = (c_define_t *)dlq_firstEntry(cdefineQ); testcdef != NULL; testcdef = (c_define_t *)dlq_nextEntry(testcdef)) { retval = xml_strcmp(newcdef->idstr, testcdef->idstr); if (retval == 0) { if (xml_strcmp(newcdef->valstr, testcdef->valstr)) { /* error - duplicate ID with different original value */ res = ERR_NCX_INVALID_VALUE; log_error("\nError: C idenitifer conflict between " "'%s' and '%s'", newcdef->valstr, testcdef->valstr); } /* else duplicate is not a problem */ free_c_define(newcdef); return res; } else if (retval < 0) { dlq_insertAhead(newcdef, testcdef); return NO_ERR; } /* else try next entry */ } /* new last entry */ dlq_enque(newcdef, cdefineQ); return NO_ERR; } /* save_path_cdefine */ /******************************************************************** * FUNCTION find_path_cdefine * * Find a #define binding for a definition in the * specified Q of c_define_t structs * * INPUTS: * cdefineQ == Q of c_define_t structs to use * obj == object struct to find * * RETURNS: * pointer to found entry * NULL if not found *********************************************************************/ c_define_t * find_path_cdefine (dlq_hdr_t *cdefineQ, const obj_template_t *obj) { c_define_t *testcdef; #ifdef DEBUG if (cdefineQ == NULL || obj == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif /* find the cdefineQ sorted by the object struct back-ptr */ for (testcdef = (c_define_t *)dlq_firstEntry(cdefineQ); testcdef != NULL; testcdef = (c_define_t *)dlq_nextEntry(testcdef)) { if (testcdef->obj == obj) { return testcdef; } } return NULL; } /* find_path_cdefine */ /******************************************************************** * FUNCTION clean_cdefineQ * * Clean a Q of c_define_t structs * * INPUTS: * cdefineQ == Q of c_define_t structs to use *********************************************************************/ void clean_cdefineQ (dlq_hdr_t *cdefineQ) { c_define_t *cdef; #ifdef DEBUG if (cdefineQ == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (!dlq_empty(cdefineQ)) { cdef = (c_define_t *)dlq_deque(cdefineQ); free_c_define(cdef); } } /* clean_cdefineQ */ /******************************************************************** * FUNCTION write_c_header * * Write the C file header * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * *********************************************************************/ void write_c_header (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp) { int32 indent; indent = cp->indent; /* banner comments */ ses_putstr(scb, START_COMMENT); ses_putstr(scb, COPYRIGHT_HEADER); /* generater tag */ write_banner_session_ex(scb, FALSE); /* module format */ switch (cp->format) { case NCX_CVTTYP_C: case NCX_CVTTYP_CPP_TEST: ses_putstr_indent(scb, (const xmlChar *)"Combined SIL module", indent); break; case NCX_CVTTYP_H: ses_putstr_indent(scb, (const xmlChar *)"Combined SIL header", indent); break; case NCX_CVTTYP_UC: ses_putstr_indent(scb, (const xmlChar *)"User SIL module", indent); break; case NCX_CVTTYP_UH: ses_putstr_indent(scb, (const xmlChar *)"User SIL header", indent); break; case NCX_CVTTYP_YC: ses_putstr_indent(scb, (const xmlChar *)"Yuma SIL module", indent); break; case NCX_CVTTYP_YH: ses_putstr_indent(scb, (const xmlChar *)"Yuma SIL header", indent); break; default: SET_ERROR(ERR_INTERNAL_VAL); } /* module name */ if (mod->ismod) { ses_putstr_indent(scb, YANG_K_MODULE, indent); } else { ses_putstr_indent(scb, YANG_K_SUBMODULE, indent); } ses_putchar(scb, ' '); ses_putstr(scb, mod->name); /* version */ if (mod->version) { write_c_simple_str(scb, YANG_K_REVISION, mod->version, indent, 0); } /* namespace or belongs-to */ if (mod->ismod) { write_c_simple_str(scb, YANG_K_NAMESPACE, mod->ns, indent, 0); } else { write_c_simple_str(scb, YANG_K_BELONGS_TO, mod->belongs, indent, 0); } /* organization */ if (mod->organization) { write_c_simple_str(scb, YANG_K_ORGANIZATION, mod->organization, indent, 0); } ses_putchar(scb, '\n'); ses_putchar(scb, '\n'); ses_putstr(scb, END_COMMENT); ses_putchar(scb, '\n'); } /* write_c_header */ /******************************************************************** * FUNCTION write_c_footer * * Write the C file footer * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use *********************************************************************/ void write_c_footer (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp) { ses_putstr(scb, (const xmlChar *)"\n/* END "); switch (cp->format) { case NCX_CVTTYP_C: case NCX_CVTTYP_CPP_TEST: case NCX_CVTTYP_H: break; case NCX_CVTTYP_UC: case NCX_CVTTYP_UH: ses_putstr(scb, U_PREFIX); break; case NCX_CVTTYP_YC: case NCX_CVTTYP_YH: ses_putstr(scb, Y_PREFIX); break; default: ; } write_c_safe_str(scb, ncx_get_modname(mod)); ses_putstr(scb, (const xmlChar *)".c */\n"); } /* write_c_footer */ /******************************************************************* * FUNCTION write_c_objtype * * Generate the C data type for the NCX data type * * INPUTS: * scb == session control block to use for writing * obj == object template to check * **********************************************************************/ void write_c_objtype (ses_cb_t *scb, const obj_template_t *obj) { write_c_objtype_max(scb, obj, NULL, ';', FALSE, FALSE, TRUE, FALSE, FALSE, FALSE); } /* write_c_objtype */ /******************************************************************* * FUNCTION write_c_objtype_ex * * Generate the C data type for the NCX data type * * INPUTS: * scb == session control block to use for writing * obj == object template to check * cdefQ == Q of c_define_t to check for obj * endchar == char to use at end (semi-colon, comma, right-paren) * isconst == TRUE if a const pointer is needed * FALSE if pointers should not be 'const' * needstar == TRUE if this object reference is a reference * or a pointer to the data type * FALSE if this is a direct usage of the object data type * !! only affects complex types, not simple types **********************************************************************/ void write_c_objtype_ex (ses_cb_t *scb, const obj_template_t *obj, dlq_hdr_t *cdefQ, xmlChar endchar, boolean isconst, boolean needstar) { write_c_objtype_max(scb, obj, cdefQ, endchar, isconst, needstar, TRUE, FALSE, FALSE, FALSE); } /* write_c_objtype_ex */ /******************************************************************* * FUNCTION write_c_objtype_max * * Generate the C data type for the NCX data type * * INPUTS: * scb == session control block to use for writing * obj == object template to check * cdefQ == Q of c_define_t to check for obj * endchar == char to use at end (semi-colon, comma, right-paren) * isconst == TRUE if a const pointer is needed * FALSE if pointers should not be 'const' * needstar == TRUE if this object reference is a reference * or a pointer to the data type * FALSE if this is a direct usage of the object data type * !! only affects complex types, not simple types * usename == TRUE to use object name as the variable name * FALSE to use the idstr as the variable name * useprefix == TRUE to use object name as the variable name * FALSE to use the idstr as the variable name; * ignored if usename == TRUE * isuser == TRUE if format is NCX_CVTTYP_UC or NCX_CVTTYP_UH * FALSE otherwise; ignored if useprefix == FALSE * isvar == TRUE if cdef->varstr should be used * FALSE if cdef->idstr should be used; * ignored if usename == TRUE **********************************************************************/ void write_c_objtype_max (ses_cb_t *scb, const obj_template_t *obj, dlq_hdr_t *cdefQ, xmlChar endchar, boolean isconst, boolean needstar, boolean usename, boolean useprefix, boolean isuser, boolean isvar) { const c_define_t *cdef = NULL; boolean needspace; ncx_btype_t btyp; #ifdef DEBUG if (scb == NULL || obj == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif needspace = TRUE; btyp = obj_get_basetype(obj); if (typ_is_simple(btyp)) { needstar = FALSE; } switch (btyp) { case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: ses_putstr(scb, BOOLEAN); break; case NCX_BT_INT8: ses_putstr(scb, INT8); break; case NCX_BT_INT16: ses_putstr(scb, INT16); break; case NCX_BT_INT32: ses_putstr(scb, INT32); break; case NCX_BT_INT64: ses_putstr(scb, INT64); break; case NCX_BT_UINT8: ses_putstr(scb, UINT8); break; case NCX_BT_UINT16: ses_putstr(scb, UINT16); break; case NCX_BT_UINT32: ses_putstr(scb, UINT32); break; case NCX_BT_UINT64: ses_putstr(scb, UINT64); break; case NCX_BT_DECIMAL64: ses_putstr(scb, INT64); break; case NCX_BT_FLOAT64: ses_putstr(scb, DOUBLE); break; case NCX_BT_ENUM: case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: case NCX_BT_SLIST: if (isconst) { ses_putstr(scb, (const xmlChar *)"const "); } ses_putstr(scb, STRING); needspace = FALSE; break; case NCX_BT_IDREF: if (isconst) { ses_putstr(scb, (const xmlChar *)"const "); } ses_putstr(scb, IDREF); needspace = FALSE; break; case NCX_BT_LIST: ses_putstr(scb, QUEUE); break; case NCX_BT_UNION: if (isconst) { ses_putstr(scb, (const xmlChar *)"const "); } ses_putstr(scb, (const xmlChar *)"val_value_t"); needstar = TRUE; break; case NCX_BT_BITS: ses_putstr(scb, BITS); break; default: /* assume complex type */ if (cdefQ == NULL) { SET_ERROR(ERR_INTERNAL_VAL); } else { cdef = find_path_cdefine(cdefQ, obj); if (cdef == NULL) { SET_ERROR(ERR_INTERNAL_VAL); } else { if (cdef->typstr) { ses_putstr(scb, cdef->typstr); } else { if (useprefix) { if (isuser) { ses_putstr(scb, U_PREFIX); } else { ses_putstr(scb, Y_PREFIX); } } if (isvar) { ses_putstr(scb, cdef->varstr); } else { ses_putstr(scb, cdef->idstr); } } } } } if (needspace) { ses_putchar(scb, ' '); } if (needstar) { ses_putchar(scb, '*'); } if (usename) { write_c_safe_str(scb, obj_get_name(obj)); } else { if (cdef == NULL) { if (cdefQ == NULL) { SET_ERROR(ERR_INTERNAL_VAL); } else { cdef = find_path_cdefine(cdefQ, obj); if (cdef == NULL) { SET_ERROR(ERR_INTERNAL_VAL); } } } if (cdef != NULL) { if (useprefix) { if (isuser) { ses_putstr(scb, U_PREFIX); } else { ses_putstr(scb, Y_PREFIX); } } if (isvar) { ses_putstr(scb, cdef->varstr); } else { ses_putstr(scb, cdef->idstr); } } /* else generating a syntax error */ } if (endchar != '\0') { ses_putchar(scb, endchar); } } /* write_c_objtype_max */ /******************************************************************* * FUNCTION write_c_val_macro_type * * Generate the C VAL_FOO macro name for the data type * * INPUTS: * scb == session control block to use for writing * obj == object template to check * **********************************************************************/ void write_c_val_macro_type (ses_cb_t *scb, const obj_template_t *obj) { ncx_btype_t btyp; btyp = obj_get_basetype(obj); switch (btyp) { case NCX_BT_BITS: ses_putstr(scb, (const xmlChar *)"VAL_BITS"); break; case NCX_BT_EMPTY: ses_putstr(scb, (const xmlChar *)"VAL_EMPTY"); break; case NCX_BT_BOOLEAN: ses_putstr(scb, (const xmlChar *)"VAL_BOOL"); break; case NCX_BT_INT8: ses_putstr(scb, (const xmlChar *)"VAL_INT8"); break; case NCX_BT_INT16: ses_putstr(scb, (const xmlChar *)"VAL_INT16"); break; case NCX_BT_INT32: ses_putstr(scb, (const xmlChar *)"VAL_INT"); break; case NCX_BT_INT64: ses_putstr(scb, (const xmlChar *)"VAL_LONG"); break; case NCX_BT_UINT8: ses_putstr(scb, (const xmlChar *)"VAL_UINT8"); break; case NCX_BT_UINT16: ses_putstr(scb, (const xmlChar *)"VAL_UINT16"); break; case NCX_BT_UINT32: ses_putstr(scb, (const xmlChar *)"VAL_UINT"); break; case NCX_BT_UINT64: ses_putstr(scb, (const xmlChar *)"VAL_ULONG"); break; case NCX_BT_DECIMAL64: ses_putstr(scb, (const xmlChar *)"VAL_DEC64"); break; case NCX_BT_FLOAT64: ses_putstr(scb, (const xmlChar *)"VAL_DOUBLE"); break; case NCX_BT_ENUM: ses_putstr(scb, (const xmlChar *)"VAL_ENUM_NAME"); break; case NCX_BT_STRING: ses_putstr(scb, (const xmlChar *)"VAL_STRING"); break; case NCX_BT_UNION: //ses_putstr(scb, (const xmlChar *)"VAL_STRING"); break; case NCX_BT_BINARY: ses_putstr(scb, (const xmlChar *)"VAL_BINARY"); break; case NCX_BT_INSTANCE_ID: ses_putstr(scb, (const xmlChar *)"VAL_INSTANCE_ID"); break; case NCX_BT_LEAFREF: ses_putstr(scb, (const xmlChar *)"VAL_STRING"); break; case NCX_BT_IDREF: ses_putstr(scb, (const xmlChar *)"VAL_IDREF"); break; case NCX_BT_SLIST: ses_putstr(scb, (const xmlChar *)"VAL_LIST"); break; case NCX_BT_LIST: ses_putstr(scb, QUEUE); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* write_c_val_macro_type */ /******************************************************************* * FUNCTION write_c_oid_comment * * Generate the object OID as a comment line * * INPUTS: * scb == session control block to use for writing * obj == object template to check * **********************************************************************/ void write_c_oid_comment (ses_cb_t *scb, const obj_template_t *obj) { xmlChar *buffer; status_t res; /* generate the YOID as a comment */ res = obj_gen_object_id(obj, &buffer); if (res != NO_ERR) { SET_ERROR(res); return; } ses_putstr(scb, START_COMMENT); ses_putstr(scb, obj_get_typestr(obj)); ses_putchar(scb, ' '); ses_putstr(scb, buffer); ses_putstr(scb, END_COMMENT); m__free(buffer); } /* write_c_oid_comment */ /******************************************************************** * FUNCTION save_c_objects * * save the path name bindings for C typdefs * * INPUTS: * mod == module in progress * datadefQ == que of obj_template_t to use * savecdefQ == Q of c_define_t structs to use * cmode == C code generating mode to use * * OUTPUTS: * savecdefQ may get new structs added * * RETURNS: * status *********************************************************************/ status_t save_c_objects (ncx_module_t *mod, dlq_hdr_t *datadefQ, dlq_hdr_t *savecdefQ, c_mode_t cmode) { obj_template_t *obj; dlq_hdr_t *childdatadefQ; status_t res; if (dlq_empty(datadefQ)) { return NO_ERR; } for (obj = (obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } res = save_path_cdefine(savecdefQ, ncx_get_modname(mod), obj, cmode); if (res != NO_ERR) { return res; } childdatadefQ = obj_get_datadefQ(obj); if (childdatadefQ) { res = save_c_objects(mod, childdatadefQ, savecdefQ, cmode); if (res != NO_ERR) { return res; } } } return NO_ERR; } /* save_c_objects */ /******************************************************************** * FUNCTION save_all_c_objects * * save the path name bindings for C typdefs for mod and all submods * * INPUTS: * mod == module in progress * cp == conversion parameters to use * savecdefQ == Q of c_define_t structs to use * cmode == C code generating mode to use * * OUTPUTS: * savecdefQ may get new structs added * * RETURNS: * status *********************************************************************/ status_t save_all_c_objects (ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *savecdefQ, c_mode_t cmode) { status_t res; res = save_c_objects(mod, &mod->datadefQ, savecdefQ, cmode); if (res == NO_ERR) { if (cp->unified && mod->ismod) { yang_node_t *node; for (node = (yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL && res == NO_ERR; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { res = save_c_objects(node->submod, &node->submod->datadefQ, savecdefQ, cmode); } } } } return res; } /* save_all_c_objects */ /******************************************************************** * FUNCTION skip_c_top_object * * Check if a top-level object should be skipped * in C file SIL code generation * * INPUTS: * obj == object to check * * RETURNS: * TRUE if object should be skipped * FALSE if object should not be skipped *********************************************************************/ boolean skip_c_top_object (obj_template_t *obj) { if (!obj_has_name(obj) || !obj_is_config(obj) || obj_is_cli(obj) || obj_is_abstract(obj) || obj_is_rpc(obj) || obj_is_notif(obj)) { return TRUE; } return FALSE; } /* END skip_c_top_object */ /******************************************************************** * FUNCTION write_c_key_params * * Write all the keys in C function parameter list format * * INPUTS: * scb == session to use * obj == object to start from (ancestor-or-self) * objnameQ == Q of name-to-idstr mappings * keycount == number of key leafs expected; used to * identify last key to suppress ending comma * startindent == start indent count * *********************************************************************/ void write_c_key_params (ses_cb_t *scb, obj_template_t *obj, dlq_hdr_t *objnameQ, uint32 keycount, int32 startindent) { c_keywalker_parms_t parms; #ifdef DEBUG if (!scb || !obj || !objnameQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (keycount == 0) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif parms.scb = scb; parms.objnameQ = objnameQ; parms.parmname = NULL; parms.keycount = keycount; parms.keynum = 0; parms.startindent = startindent; parms.done = FALSE; obj_traverse_keys(obj, &parms, NULL, write_key_parm); } /* write_c_key_params */ /******************************************************************** * FUNCTION write_c_key_vars * * Write all the local key variables in the SIL C function * * INPUTS: * scb == session to use * obj == object to start from (ancestor-or-self) * objnameQ == Q of name-to-idstr mappings * parmname == name of parameter used in C code * keycount == number of key leafs expected; used to * identify last key to suppress ending comma * startindent == start indent count * *********************************************************************/ void write_c_key_vars (ses_cb_t *scb, obj_template_t *obj, dlq_hdr_t *objnameQ, const xmlChar *parmname, uint32 keycount, int32 startindent) { c_keywalker_parms_t parms; #ifdef DEBUG if (!scb || !obj || !objnameQ || !parmname) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (keycount == 0) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif parms.scb = scb; parms.objnameQ = objnameQ; parms.parmname = parmname; parms.keycount = keycount; parms.keynum = 0; parms.startindent = startindent; parms.done = FALSE; obj_traverse_keys(obj, &parms, NULL, write_key_var); } /* write_c_key_vars */ /******************************************************************** * FUNCTION write_c_key_values * * Write all the keys in call-C-function-to-get-key-value format * * INPUTS: * scb == session to use * obj == object to start from (ancestor-or-self) * objnameQ == Q of name-to-idstr mappings * keycount == number of key leafs expected; used to * identify last key to suppress ending comma * startindent == start indent count * *********************************************************************/ void write_c_key_values (ses_cb_t *scb, obj_template_t *obj, dlq_hdr_t *objnameQ, uint32 keycount, int32 startindent) { c_keywalker_parms_t parms; #ifdef DEBUG if (!scb || !obj || !objnameQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (keycount == 0) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif parms.scb = scb; parms.objnameQ = objnameQ; parms.parmname = NULL; parms.keycount = keycount; parms.keynum = 0; parms.startindent = startindent; parms.done = FALSE; obj_traverse_keys(obj, &parms, NULL, write_key_value); } /* write_c_key_params */ /******************************************************************** * FUNCTION write_h_iffeature_start * * Generate the start C for 1 if-feature conditional; * * INPUTS: * scb == session control block to use for writing * iffeatureQ == Q of ncx_feature_t to use * *********************************************************************/ void write_h_iffeature_start (ses_cb_t *scb, const dlq_hdr_t *iffeatureQ) { ncx_iffeature_t *iffeature, *nextif; uint32 iffeaturecnt; iffeaturecnt = dlq_count(iffeatureQ); /* check if conditional wrapper needed */ if (iffeaturecnt == 1) { iffeature = (ncx_iffeature_t *) dlq_firstEntry(iffeatureQ); ses_putchar(scb, '\n'); ses_putstr(scb, POUND_IFDEF); write_identifier(scb, iffeature->feature->tkerr.mod->name, BAR_FEAT, iffeature->feature->name, TRUE); } else if (iffeaturecnt > 1) { ses_putchar(scb, '\n'); ses_putstr(scb, POUND_IF); ses_putchar(scb, '('); for (iffeature = (ncx_iffeature_t *) dlq_firstEntry(iffeatureQ); iffeature != NULL; iffeature = nextif) { nextif = (ncx_iffeature_t *)dlq_nextEntry(iffeature); ses_putstr(scb, START_DEFINED); write_identifier(scb, iffeature->feature->tkerr.mod->name, BAR_FEAT, iffeature->feature->name, TRUE); ses_putchar(scb, ')'); if (nextif) { ses_putstr(scb, (const xmlChar *)" && "); } } ses_putchar(scb, ')'); } } /* write_h_iffeature_start */ /******************************************************************** * FUNCTION write_h_iffeature_end * * Generate the end C for 1 if-feature conditiona; * * INPUTS: * scb == session control block to use for writing * iffeatureQ == Q of ncx_feature_t to use * *********************************************************************/ void write_h_iffeature_end (ses_cb_t *scb, const dlq_hdr_t *iffeatureQ) { if (!dlq_empty(iffeatureQ)) { ses_putstr(scb, POUND_ENDIF); ses_putstr(scb, (const xmlChar *)" /* "); ncx_iffeature_t *iffeature = (ncx_iffeature_t *)dlq_firstEntry(iffeatureQ); ncx_iffeature_t *nexif = NULL; for (; iffeature != NULL; iffeature = nexif) { write_identifier(scb, iffeature->feature->tkerr.mod->name, BAR_FEAT, iffeature->feature->name, TRUE); nexif = (ncx_iffeature_t *)dlq_nextEntry(iffeature); if (nexif) { ses_putstr(scb, (const xmlChar *)", "); } } ses_putstr(scb, (const xmlChar *)" */"); } } /* write_h_iffeature_end */ /* END c_util.c */ yuma123_2.14/netconf/src/ydump/c.c0000664000175000017500000037710514770023131017107 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: c.c Generate C file output from YANG module ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 27oct09 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "c.h" #include "c_util.h" #include "dlq.h" #include "ext.h" #include "ncx.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "rpc.h" #include "ses.h" #include "status.h" #include "typ.h" #include "yang.h" #include "yangconst.h" #include "yangdump.h" #include "yangdump_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION write_cb_logging_init * * Generate the initialisation of callback logging * * INPUTS: * scb == session control block to use for writing * idstr == id of the call to be logged * suffix == suffix of the call to be logged * indent == level of indentation for generated code * *********************************************************************/ static void write_cb_logging_init (ses_cb_t *scb, const xmlChar * idstr, const xmlChar * suffix, int32 indent) { ses_putstr_indent(scb, (const xmlChar *)"YumaTest::SILCallbackLog& cbLog " "= YumaTest::SILCallbackLog::getInstance();", indent); ses_putstr_indent(scb, (const xmlChar *)"YumaTest::SILCallbackLog::Callback" "Info cbData;", indent); ses_putstr_indent(scb, (const xmlChar *)"cbData.cbName = \"", indent); ses_putstr(scb, idstr); ses_putstr(scb, suffix); ses_putstr(scb, (const xmlChar *)"\";"); } /* write_cb_logging_init */ /******************************************************************** * FUNCTION write_cb_logging_add_cb * * Generate the adding of a callback to the callback log * * INPUTS: * scb == session control block to use for writing * module_name == session control block to use for writing * is_string == TRUE if the module name given should be generated * as a string (in quotation marks) * FALSE if the module name given should be generated as * a variable name * indent == level of indentation for generated code * *********************************************************************/ static void write_cb_logging_add_cb (ses_cb_t *scb, const xmlChar *mod_name, boolean is_string, int32 indent) { ses_putstr_indent(scb, (const xmlChar *)"cbLog.addCallback(", indent); if (is_string) { ses_putstr(scb, (const xmlChar *)"\""); } ses_putstr(scb, mod_name); if (is_string) { ses_putstr(scb, (const xmlChar *)"\""); } ses_putstr(scb, (const xmlChar *)", cbData);"); } /* write_cb_logging_add_cb */ /******************************************************************** * FUNCTION write_if_res * * Write if (res != NO_ERR) ... statements * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * startindent == start indent amount * *********************************************************************/ static void write_if_res (ses_cb_t *scb, const yangdump_cvtparms_t *cp, int32 startindent) { ses_putstr_indent(scb, (const xmlChar *)"if (res != NO_ERR) {", startindent); ses_putstr_indent(scb, (const xmlChar *)"return res;", startindent + cp->indent); ses_putstr_indent(scb, (const xmlChar *)"}", startindent); } /* write_if_res */ /******************************************************************** * FUNCTION write_if_record_error * * Write if (...) { agt_record_error } stmts * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * startindent == start indent amount * oper_layer == TRUE if operation layer * FALSE if application layer * with_return == TRUE if return res; added * FALSE if not added * with_methnode == TRUE if methnode should be in error call * FALSE to use NULL instead of methnode * * *********************************************************************/ static void write_if_record_error (ses_cb_t *scb, const yangdump_cvtparms_t *cp, int32 startindent, boolean oper_layer, boolean with_return, boolean with_methnode) { int32 indent; indent = cp->indent; ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"if (res != NO_ERR) {", startindent); ses_putstr_indent(scb, (const xmlChar *)"agt_record_error(", startindent + indent); indent += cp->indent; ses_putstr_indent(scb, (const xmlChar *)"scb,", startindent + indent); ses_putstr_indent(scb, (const xmlChar *)"&msg->mhdr,", startindent + indent); if (oper_layer) { ses_putstr_indent(scb, (const xmlChar *)"NCX_LAYER_OPERATION,", startindent + indent); } else { ses_putstr_indent(scb, (const xmlChar *)"NCX_LAYER_CONTENT,", startindent + indent); } ses_putstr_indent(scb, (const xmlChar *)"res,", startindent + indent); ses_putstr_indent(scb, (with_methnode) ? (const xmlChar *)"methnode," : (const xmlChar *)"NULL,", startindent + indent); ses_putstr_indent(scb, (const xmlChar *)"(errorval) ? NCX_NT_VAL : NCX_NT_NONE,", startindent + indent); ses_putstr_indent(scb, (const xmlChar *)"errorval,", startindent + indent); ses_putstr_indent(scb, (const xmlChar *)"(errorval) ? NCX_NT_VAL : NCX_NT_NONE,", startindent + indent); ses_putstr_indent(scb, (const xmlChar *)"errorval);", startindent + indent); if (with_return) { indent -= cp->indent; ses_putstr_indent(scb, (const xmlChar *)"return res;", startindent + indent); } ses_indent(scb, startindent); ses_putchar(scb, '}'); } /* write_if_record_error */ /******************************************************************** * FUNCTION write_c_iffeature_start * * Generate the start C for 1 dynamic if (ncx_feature_enabled() * * INPUTS: * scb == session control block to use for writing * iffeatureQ == Q of ncx_feature_t to use * cp == conversion parameters to use * startindent == starting indent amount to use * RETURNS: * TRUE if conditional was printed; FALSE if nothing done *********************************************************************/ static boolean write_c_iffeature_start (ses_cb_t *scb, const dlq_hdr_t *iffeatureQ, const yangdump_cvtparms_t *cp, int32 startindent) { /* check if conditional wrapper needed */ ncx_iffeature_t *iffeature = (ncx_iffeature_t *)dlq_firstEntry(iffeatureQ); if (iffeature == NULL) { return FALSE; } const xmlChar *modname = ncx_get_modname(iffeature->feature->tkerr.mod); if (modname == NULL) { return FALSE; } const xmlChar *modversion = ncx_get_modversion(iffeature->feature->tkerr.mod); if (modversion == NULL) { } ses_indent(scb, startindent); ses_putstr(scb, (const xmlChar *)"if (ncx_feature_enabled_str("); ses_indent(scb, startindent + cp->indent); write_identifier(scb, modname, BAR_MOD, modname, FALSE); ses_putstr(scb, (const xmlChar *)", "); ses_indent(scb, startindent + cp->indent); if (modversion) { write_identifier(scb, modname, BAR_REV, modname, FALSE); } else { ses_putstr(scb, (const xmlChar *)"NULL"); } ses_putchar(scb, ','); ses_putstr_indent(scb, (const xmlChar *)"(const xmlChar *)\"", startindent + cp->indent); ses_putstr(scb, iffeature->feature->name); ses_putstr(scb, (const xmlChar *)"\")"); iffeature = (ncx_iffeature_t *)dlq_nextEntry(iffeature); while (iffeature) { ses_indent(scb, startindent + cp->indent); ses_putstr(scb, (const xmlChar *)"&& ncx_feature_enabled_str("); ses_indent(scb, startindent + cp->indent + cp->indent); write_identifier(scb, modname, BAR_MOD, modname, FALSE); ses_putstr(scb, (const xmlChar *)", "); ses_indent(scb, startindent + cp->indent + cp->indent); if (modversion) { write_identifier(scb, modname, BAR_REV, modname, FALSE); } else { ses_putstr(scb, (const xmlChar *)"NULL"); } ses_putchar(scb, ','); ses_putstr_indent(scb, (const xmlChar *)"(const xmlChar *)\"", startindent + cp->indent + cp->indent); ses_putstr(scb, iffeature->feature->name); ses_putstr(scb, (const xmlChar *)"\")"); iffeature = (ncx_iffeature_t *)dlq_nextEntry(iffeature); } ses_putstr(scb, (const xmlChar *)") {"); return TRUE; } /* write_c_iffeature_start */ /******************************************************************** * FUNCTION write_c_iffeature_end * * Generate the end C for 1 dynamic if-feature conditiona; * * INPUTS: * scb == session control block to use for writing * iffeatureQ == Q of ncx_feature_t to use * *********************************************************************/ static void write_c_iffeature_end (ses_cb_t *scb, const dlq_hdr_t *iffeatureQ, int32 startindent) { if (!dlq_empty(iffeatureQ)) { ses_indent(scb, startindent); ses_putstr(scb, (const xmlChar *)"} /* "); ncx_iffeature_t *iffeature = (ncx_iffeature_t *)dlq_firstEntry(iffeatureQ); ncx_iffeature_t *nexif = NULL; for (; iffeature != NULL; iffeature = nexif) { write_identifier(scb, iffeature->feature->tkerr.mod->name, BAR_FEAT, iffeature->feature->name, TRUE); nexif = (ncx_iffeature_t *)dlq_nextEntry(iffeature); if (nexif) { ses_putstr(scb, (const xmlChar *)", "); } } ses_putstr(scb, (const xmlChar *)" */"); } } /* write_c_iffeature_end */ /******************************************************************** * FUNCTION write_c_includes * * Write the C file #include statements * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * *********************************************************************/ static void write_c_includes (ses_cb_t *scb, const ncx_module_t *mod, const yangdump_cvtparms_t *cp) { #ifdef LEAVE_OUT_FOR_NOW const ncx_include_t *inc; #endif boolean needrpc, neednotif; if (cp->format == NCX_CVTTYP_CPP_TEST) { /* allow correct compiling with cpp flags */ ses_putchar(scb, '\n'); ses_putstr(scb, (const xmlChar *)"extern \"C\" {"); } needrpc = need_rpc_includes(mod, cp); neednotif = need_notif_includes(mod, cp); /* add xmlChar include */ write_ext_include(scb, (const xmlChar *)"libxml/xmlstring.h"); /* add procdefs include first NCX include */ write_ncx_include(scb, (const xmlChar *)"procdefs"); /* add includes even if they may not get used * TBD: prune all unused include files */ write_ncx_include(scb, (const xmlChar *)"agt"); write_ncx_include(scb, (const xmlChar *)"agt_cb"); if (neednotif) { write_ncx_include(scb, (const xmlChar *)"agt_not"); } if (needrpc) { write_ncx_include(scb, (const xmlChar *)"agt_rpc"); } write_ncx_include(scb, (const xmlChar *)"agt_timer"); write_ncx_include(scb, (const xmlChar *)"agt_util"); write_ncx_include(scb, (const xmlChar *)"dlq"); write_ncx_include(scb, (const xmlChar *)"ncx"); write_ncx_include(scb, (const xmlChar *)"ncx_feature"); write_ncx_include(scb, (const xmlChar *)"ncxmod"); write_ncx_include(scb, (const xmlChar *)"ncxtypes"); if (needrpc) { write_ncx_include(scb, (const xmlChar *)"rpc"); } if (needrpc || neednotif) { write_ncx_include(scb, (const xmlChar *)"ses"); } write_ncx_include(scb, (const xmlChar *)"status"); if (needrpc || neednotif) { write_ncx_include(scb, (const xmlChar *)"val"); write_ncx_include(scb, (const xmlChar *)"val_util"); write_ncx_include(scb, (const xmlChar *)"xml_util"); } /* include for the module H file */ switch (cp->format) { case NCX_CVTTYP_C: case NCX_CVTTYP_CPP_TEST: write_ncx_include(scb, ncx_get_modname(mod)); break; case NCX_CVTTYP_UC: case NCX_CVTTYP_YC: /* write includes for paired yuma USER and then SIL file */ write_cvt_include(scb, ncx_get_modname(mod), NCX_CVTTYP_UC); write_cvt_include(scb, ncx_get_modname(mod), NCX_CVTTYP_YC); break; default: SET_ERROR(ERR_INTERNAL_VAL); } #ifdef LEAVE_OUT_FOR_NOW /* includes for submodules */ if (!cp->unified) { for (inc = (const ncx_include_t *) dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (const ncx_include_t *)dlq_nextEntry(inc)) { write_ncx_include(scb, inc->submodule); } } #endif if (cp->format == NCX_CVTTYP_CPP_TEST) { /* close extern */ ses_putstr(scb, (const xmlChar *)"\n}\n"); /* add include for callback logging */ write_ncx_include(scb, (const xmlChar *) "test/support/callbacks/sil-callback-log"); } ses_putchar(scb, '\n'); } /* write_c_includes */ /******************************************************************** * FUNCTION write_c_object_var * * Write the C file object variable name * * INPUTS: * scb == session control block to use for writing * objname == object name to use * *********************************************************************/ static void write_c_object_var (ses_cb_t *scb, const xmlChar *objname) { write_c_safe_str(scb, objname); ses_putstr(scb, (const xmlChar *)"_obj"); } /* write_c_object_var */ /******************************************************************** * FUNCTION write_c_value_var * * Write the C file value node variable name * * INPUTS: * scb == session control block to use for writing * valname == value name to use * *********************************************************************/ static void write_c_value_var (ses_cb_t *scb, const xmlChar *valname) { write_c_safe_str(scb, valname); ses_putstr(scb, (const xmlChar *)"_val"); } /* write_c_value_var */ /******************************************************************** * FUNCTION write_c_static_vars * * Write the C file static variable declarations * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use *********************************************************************/ static void write_c_static_vars (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp) { obj_template_t *obj; #ifdef LEAVE_OUT_FOR_NOW const ncx_include_t *inc; #endif /* create a static object template pointer for * each top-level real object, RPC, or notification */ if (cp->format != NCX_CVTTYP_UC) { if (mod->ismod) { /* print a banner */ ses_putstr(scb, (const xmlChar *)"\n/* module static variables */"); /* create a static module back-pointer */ ses_putstr(scb, (const xmlChar *)"\nstatic ncx_module_t *"); write_c_safe_str(scb, ncx_get_modname(mod)); ses_putstr(scb, (const xmlChar *)"_mod;"); } } for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } if ((cp->format == NCX_CVTTYP_UC && obj_is_notif(obj)) || (cp->format == NCX_CVTTYP_YC && !obj_is_notif(obj)) || !(cp->format == NCX_CVTTYP_UC || cp->format == NCX_CVTTYP_YC)) { write_h_iffeature_start(scb, &obj->iffeatureQ); ses_putstr(scb, (const xmlChar *)"\nstatic obj_template_t *"); write_c_object_var(scb, obj_get_name(obj)); ses_putchar(scb, ';'); write_h_iffeature_end(scb, &obj->iffeatureQ); } } for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (skip_c_top_object(obj)) { continue; } if (cp->format != NCX_CVTTYP_UC) { write_h_iffeature_start(scb, &obj->iffeatureQ); ses_putstr(scb, (const xmlChar *)"\nstatic val_value_t *"); write_c_value_var(scb, obj_get_name(obj)); ses_putchar(scb, ';'); write_h_iffeature_end(scb, &obj->iffeatureQ); } } ses_putchar(scb, '\n'); #ifdef LEAVE_OUT_FOR_NOW /* includes for submodules */ if (!cp->unified) { for (inc = (const ncx_include_t *) dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (const ncx_include_t *)dlq_nextEntry(inc)) { write_c_static_vars(scb, inc->submodule); } } #endif if (cp->format != NCX_CVTTYP_YC) { /* user can put static var init inline */ ses_putstr(scb, (const xmlChar *)"\n/* put your static " "variables here */\n"); } } /* write_c_static_vars */ /******************************************************************** * FUNCTION write_c_init_static_vars_fn * * Write the C file init static variable function * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use *********************************************************************/ static void write_c_init_static_vars_fn (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp) { if (cp->format == NCX_CVTTYP_UC) { return; } const xmlChar *modname = mod->name; obj_template_t *obj = NULL; int32 indent = cp->indent; /* generate function banner comment */ ses_putstr(scb, FN_BANNER_START); write_identifier(scb, modname, NULL, FN_INIT_STATIC_VARS, cp->isuser); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"initialize module static variables"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_END); ses_putstr(scb, (const xmlChar *)"\nstatic void "); write_identifier(scb, modname, NULL, FN_INIT_STATIC_VARS, cp->isuser); ses_putstr(scb, (const xmlChar *)" (void)\n{"); if (mod->ismod) { ses_indent(scb, indent); write_c_safe_str(scb, ncx_get_modname(mod)); ses_putstr(scb, (const xmlChar *)"_mod = NULL;"); } /* create an init line for each top-level * real object, RPC, or notification */ for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } if ((cp->format == NCX_CVTTYP_YC && !obj_is_notif(obj)) || cp->format != NCX_CVTTYP_YC) { write_h_iffeature_start(scb, &obj->iffeatureQ); ses_indent(scb, indent); write_c_object_var(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" = NULL;"); write_h_iffeature_end(scb, &obj->iffeatureQ); } } /* create an init line for each top-level * real value cache pointer for a database object */ for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (skip_c_top_object(obj)) { continue; } write_h_iffeature_start(scb, &obj->iffeatureQ); ses_indent(scb, indent); write_c_value_var(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" = NULL;"); write_h_iffeature_end(scb, &obj->iffeatureQ); } if (cp->format != NCX_CVTTYP_YC) { /* put inline user marker comment */ ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"/* init your " "static variables here */", indent); } ses_putchar(scb, '\n'); /* end the function */ ses_putstr(scb, (const xmlChar *)"\n} /* "); write_identifier(scb, modname, NULL, (const xmlChar *)"init_static_vars", cp->isuser); ses_putstr(scb, (const xmlChar *)" */\n"); } /* write_c_init_static_vars_fn */ /******************************************************************** * FUNCTION write_c_edit_cbfn * * Generate the C code for the foo_object_edit function * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * obj == object struct for the database object * objnameQ == Q of c_define_t structs to search for this object * uprotomode == TRUE: just generate a format==NCX_CVTTYP_UH prototype * FALSE: normal mode *********************************************************************/ static void write_c_edit_cbfn (ses_cb_t *scb, const yangdump_cvtparms_t *cp, obj_template_t *obj, dlq_hdr_t *objnameQ, boolean uprotomode) { c_define_t *cdef; int32 indent = cp->indent, i = 0; uint32 keycount; cdef = find_path_cdefine(objnameQ, obj); if (cdef == NULL) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return; } keycount = obj_key_count_to_root(obj); /* generate function banner comment */ if (dlq_empty(&obj->iffeatureQ)) { ses_putchar(scb, '\n'); } ses_putstr(scb, FN_BANNER_START); if (cp->isuser) { ses_putstr(scb, U_PREFIX); } /* else static function so no prefix */ ses_putstr(scb, cdef->idstr); ses_putstr(scb, EDIT_SUFFIX); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Edit database object callback"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Path: "); ses_putstr(scb, cdef->valstr); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Add object instrumentation " "in COMMIT phase."); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_INPUT); ses_putstr(scb, (const xmlChar *)" see agt/agt_cb.h for details"); if (cp->isuser && keycount) { ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)" k_ parameters are ancestor " "list key values."); } ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_RETURN_STATUS); ses_putstr(scb, FN_BANNER_END); /* generate the function prototype lines */ if (uprotomode) { ses_putstr(scb, (const xmlChar *)"\nextern status_t "); ses_putstr(scb, U_PREFIX); } else if (cp->isuser) { ses_putstr(scb, (const xmlChar *)"\nstatus_t "); ses_putstr(scb, U_PREFIX); } else { ses_putstr(scb, (const xmlChar *)"\nstatic status_t "); } ses_putstr(scb, cdef->idstr); ses_putstr(scb, EDIT_SUFFIX); ses_putstr(scb, (const xmlChar *)" ("); ses_putstr_indent(scb, (const xmlChar *)"ses_cb_t *scb,", indent); ses_putstr_indent(scb, (const xmlChar *)"rpc_msg_t *msg,", indent); ses_putstr_indent(scb, (const xmlChar *)"agt_cbtyp_t cbtyp,", indent); ses_putstr_indent(scb, (const xmlChar *)"op_editop_t editop,", indent); ses_putstr_indent(scb, (const xmlChar *)"val_value_t *newval,", indent); ses_putstr_indent(scb, (const xmlChar *)"val_value_t *curval", indent); /* add the key parameters, if any */ if (cp->isuser && keycount) { ses_putchar(scb, ','); write_c_key_params(scb, obj, objnameQ, keycount, indent); } ses_putchar(scb, ')'); if (uprotomode) { ses_putstr(scb, (const xmlChar *)";\n"); return; } /* start function body */ ses_putstr(scb, (const xmlChar *)"\n{"); ses_putstr_indent(scb, (const xmlChar *)"status_t res = NO_ERR;", indent); if (!cp->isuser) { /* generate static vars */ ses_putstr_indent(scb, (const xmlChar *)"val_value_t *errorval = " "(curval) ? curval : newval;", indent); if (cp->format == NCX_CVTTYP_YC && keycount) { ses_putstr_indent(scb, (const xmlChar *)"val_value_t *lastkey = NULL;", indent); /* add the key local variables */ write_c_key_vars(scb, obj, objnameQ, PARM_ERRORVAL, keycount, indent); } } /* print an enter fn DEBUG trace */ ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"if (LOGDEBUG) {", indent); ses_putstr_indent(scb, (const xmlChar *)"log_debug(\"\\nEnter ", indent+indent); if (cp->isuser) { ses_putstr(scb, U_PREFIX); } ses_putstr(scb, cdef->idstr); ses_putstr(scb, EDIT_SUFFIX); ses_putstr(scb, (const xmlChar *)" callback for %s phase\","); ses_putstr_indent(scb, (const xmlChar *)"agt_cbtype_name(cbtyp));", indent*3); ses_putstr_indent(scb, (const xmlChar *)"}", indent); /* generate call to user edit function if format is yuma C * first need to get all the local variables */ if (cp->format == NCX_CVTTYP_YC) { ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"res = u_", indent); ses_putstr(scb, cdef->idstr); ses_putstr(scb, EDIT_SUFFIX); ses_putstr(scb, (const xmlChar *)"(scb, msg, cbtyp, editop, newval, curval"); if (keycount) { ses_putchar(scb, ','); write_c_key_values(scb, obj, objnameQ, keycount, indent+indent); } ses_putchar(scb, ')'); ses_putchar(scb, ';'); } /* generate switch on callback type */ if (cp->format != NCX_CVTTYP_YC) { if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putchar(scb, '\n'); write_cb_logging_init(scb, cdef->idstr, EDIT_SUFFIX, indent); ses_putstr_indent(scb, (const xmlChar *)"val_value_t* value = " "(newval) ? newval : curval;", indent); ses_putstr_indent(scb, (const xmlChar *)"std::string module_name = " "(char*) val_get_mod_name(value);", indent); } ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"switch (cbtyp) {", indent); ses_putstr_indent(scb, (const xmlChar *)"case AGT_CB_VALIDATE:", indent); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putstr_indent(scb, (const xmlChar *)"cbData.cbType = \"validate\";", indent+indent); write_cb_logging_add_cb(scb, (const xmlChar *)"module_name", FALSE, indent+indent); } ses_putstr_indent(scb, (const xmlChar *)"/* description-stmt " "validation here */", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"break;", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"case AGT_CB_APPLY:", indent); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putstr_indent(scb, (const xmlChar *)"cbData.cbType = \"apply\";", indent+indent); write_cb_logging_add_cb(scb, (const xmlChar *)"module_name", FALSE, indent+indent); } ses_putstr_indent(scb, (const xmlChar *)"/* database manipulation " "done here */", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"break;", indent+indent); /* commit case arm */ ses_putstr_indent(scb, (const xmlChar *)"case AGT_CB_COMMIT:", indent); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putstr_indent(scb, (const xmlChar *)"cbData.cbType = \"commit\";", indent+indent); } ses_putstr_indent(scb, (const xmlChar *)"/* device instrumentation " "done here */", indent+indent); /* generate switch on editop */ ses_putstr_indent(scb, (const xmlChar *)"switch (editop) {", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"case OP_EDITOP_LOAD:", indent+indent); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putstr_indent(scb, (const xmlChar *)"cbData.cbPhase = \"load\";", indent*3); write_cb_logging_add_cb(scb, (const xmlChar *)"module_name", FALSE, indent*3); } ses_putstr_indent(scb, (const xmlChar *)"break;", indent*3); ses_putstr_indent(scb, (const xmlChar *)"case OP_EDITOP_MERGE:", indent+indent); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putstr_indent(scb, (const xmlChar *)"cbData.cbPhase = \"merge\";", indent*3); write_cb_logging_add_cb(scb, (const xmlChar *)"module_name", FALSE, indent*3); } ses_putstr_indent(scb, (const xmlChar *)"break;", indent*3); ses_putstr_indent(scb, (const xmlChar *)"case OP_EDITOP_REPLACE:", indent+indent); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putstr_indent(scb, (const xmlChar *)"cbData.cbPhase = \"replace\";", indent*3); write_cb_logging_add_cb(scb, (const xmlChar *)"module_name", FALSE, indent*3); } ses_putstr_indent(scb, (const xmlChar *)"break;", indent*3); ses_putstr_indent(scb, (const xmlChar *)"case OP_EDITOP_CREATE:", indent+indent); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putstr_indent(scb, (const xmlChar *)"cbData.cbPhase = \"create\";", indent*3); write_cb_logging_add_cb(scb, (const xmlChar *)"module_name", FALSE, indent*3); } ses_putstr_indent(scb, (const xmlChar *)"break;", indent*3); ses_putstr_indent(scb, (const xmlChar *)"case OP_EDITOP_DELETE:", indent+indent); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putstr_indent(scb, (const xmlChar *)"cbData.cbPhase = \"delete\";", indent*3); write_cb_logging_add_cb(scb, (const xmlChar *)"module_name", FALSE, indent*3); } ses_putstr_indent(scb, (const xmlChar *)"break;", indent*3); ses_putstr_indent(scb, (const xmlChar *)"default:", indent+indent); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putstr_indent(scb, (const xmlChar *)"cbData.cbPhase = \"default\";", indent*3); write_cb_logging_add_cb(scb, (const xmlChar *)"module_name", FALSE, indent*3); } ses_putstr_indent(scb, (const xmlChar *)"res = SET_ERROR(ERR_INTERNAL_VAL);", indent*3); ses_putstr_indent(scb, (const xmlChar *)"}", indent+indent); } /* generate cache object check */ if (!cp->isuser && obj_is_top(obj)) { ses_putchar(scb, '\n'); if (cp->format == NCX_CVTTYP_C || cp->format == NCX_CVTTYP_CPP_TEST) { ses_putstr_indent(scb, (const xmlChar *)"if (res == NO_ERR) {", indent+indent); i = 3; } else { ses_putstr_indent(scb, (const xmlChar *)"if (res == NO_ERR " "&& cbtyp == AGT_CB_COMMIT) {", indent); i = 2; } ses_putstr_indent(scb, (const xmlChar *)"res = agt_check_cache(&", indent*i); write_c_value_var(scb, obj_get_name(obj)); ses_putchar(scb, ','); ses_putstr(scb, (const xmlChar *)" newval, curval, editop);"); if (cp->format == NCX_CVTTYP_C || cp->format == NCX_CVTTYP_CPP_TEST) { ses_putstr_indent(scb, (const xmlChar *)"}\n", indent+indent); } else { ses_putstr_indent(scb, (const xmlChar *)"}\n", indent); } } /* generate check on read-only child nodes */ if (!cp->isuser && obj_has_ro_children(obj)) { if (cp->format == NCX_CVTTYP_C || cp->format == NCX_CVTTYP_CPP_TEST) { i = 2; ses_putstr_indent(scb, (const xmlChar *) "if (res == NO_ERR && curval == NULL) {", indent*i); } else { i = 1; ses_putstr_indent(scb, (const xmlChar *) "if (res == NO_ERR && cbtyp == AGT_CB_COMMIT " "&& curval == NULL) {", indent); } ses_putstr_indent(scb, (const xmlChar *)"res = ", indent*(i+1)); ses_putstr(scb, cdef->idstr); ses_putstr(scb, MRO_SUFFIX); ses_putstr(scb, (const xmlChar *)"(newval);"); ses_indent(scb, indent*i); ses_putchar(scb, '}'); } if (cp->format != NCX_CVTTYP_YC) { ses_putstr_indent(scb, (const xmlChar *)"break;", indent+indent); /* rollback case arm */ ses_putstr_indent(scb, (const xmlChar *)"case AGT_CB_ROLLBACK:", indent); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putstr_indent(scb, (const xmlChar *)"cbData.cbType = \"rollback\";", indent+indent); write_cb_logging_add_cb(scb, (const xmlChar *)"module_name", FALSE, indent+indent); } ses_putstr_indent(scb, (const xmlChar *)"/* undo device instrumentation " "here */", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"break;", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"default:", indent); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putstr_indent(scb, (const xmlChar *)"cbData.cbType = \"default\";", indent+indent); write_cb_logging_add_cb(scb, (const xmlChar *)"module_name", FALSE, indent+indent); } ses_putstr_indent(scb, (const xmlChar *)"res = SET_ERROR(ERR_INTERNAL_VAL);", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"}", indent); } if (!cp->isuser) { write_if_record_error(scb, cp, indent, FALSE, FALSE, FALSE); } /* return result */ ses_indent(scb, indent); ses_putstr(scb, (const xmlChar *)"return res;"); /* end the function */ ses_putstr(scb, (const xmlChar *)"\n\n} /* "); if (cp->isuser) { ses_putstr(scb, U_PREFIX); } ses_putstr(scb, cdef->idstr); ses_putstr(scb, EDIT_SUFFIX); ses_putstr(scb, (const xmlChar *)" */"); if (dlq_empty(&obj->iffeatureQ)) { ses_putchar(scb, '\n'); } } /* write_c_edit_cbfn */ /******************************************************************** * FUNCTION write_c_get_cbfn * * Generate the C code for the foo_object_get function * * INPUTS: * scb == session control block to use for writing * cp == conversion parameters to use * obj == object struct for the database object * objnameQ == Q of c_define_t structs to search for this object * uprotomode == TRUE: just generate a format==NCX_CVTTYP_UH prototype * FALSE: normal mode *********************************************************************/ static void write_c_get_cbfn (ses_cb_t *scb, const yangdump_cvtparms_t *cp, obj_template_t *obj, dlq_hdr_t *objnameQ, boolean uprotomode) { const xmlChar *defval; c_define_t *cdef; typ_enum_t *typ_enum; int32 indent; uint32 keycount; ncx_btype_t btyp; indent = cp->indent; cdef = find_path_cdefine(objnameQ, obj); if (cdef == NULL) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return; } keycount = obj_key_count_to_root(obj); /* generate function banner comment */ ses_putstr(scb, FN_BANNER_START); if (cp->isuser) { ses_putstr(scb, U_PREFIX); } ses_putstr(scb, cdef->idstr); ses_putstr(scb, GET_SUFFIX); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Get database object callback"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Path: "); ses_putstr(scb, cdef->valstr); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Fill in 'dstval' contents"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_INPUT); ses_putstr(scb, (const xmlChar *)" see ncx/getcb.h for details"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_RETURN_STATUS); ses_putstr(scb, FN_BANNER_END); /* generate the function prototype lines */ if (uprotomode) { ses_putstr(scb, (const xmlChar *)"\nextern status_t "); ses_putstr(scb, U_PREFIX); } else if (cp->isuser) { ses_putstr(scb, (const xmlChar *)"\nstatus_t "); ses_putstr(scb, U_PREFIX); } else { ses_putstr(scb, (const xmlChar *)"\nstatic status_t "); } ses_putstr(scb, cdef->idstr); ses_putstr(scb, GET_SUFFIX); ses_putstr(scb, (const xmlChar *)" ("); if (cp->isuser) { ses_putstr_indent(scb, (const xmlChar *)"val_value_t *dstval", indent); if (keycount) { ses_putchar(scb, ','); write_c_key_params(scb, obj, objnameQ, keycount, indent); } ses_putchar(scb, ')'); } else { ses_putstr_indent(scb, (const xmlChar *)"ses_cb_t *scb,", indent); ses_putstr_indent(scb, (const xmlChar *)"getcb_mode_t cbmode,", indent); ses_putstr_indent(scb, (const xmlChar *)"const val_value_t *virval,", indent); ses_putstr_indent(scb, (const xmlChar *)"val_value_t *dstval)", indent); } if (uprotomode) { ses_putstr(scb, (const xmlChar *)";\n"); return; } /* start function body */ ses_putstr(scb, (const xmlChar *)"\n{"); /* declare static vars */ ses_putstr_indent(scb, (const xmlChar *)"status_t res = NO_ERR;", indent); switch (cp->format) { case NCX_CVTTYP_C: case NCX_CVTTYP_CPP_TEST: case NCX_CVTTYP_UC: ses_indent(scb, indent); write_c_objtype_ex(scb, obj, objnameQ, ';', TRUE, FALSE); break; case NCX_CVTTYP_YC: if (keycount) { ses_putstr_indent(scb, (const xmlChar *)"val_value_t *lastkey = NULL;", indent); /* add the key local variables */ write_c_key_vars(scb, obj, objnameQ, PARM_DSTVAL, keycount, indent); } break; default: ; } /* print an enter fn DEBUG trace */ ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"if (LOGDEBUG) {", indent); ses_putstr_indent(scb, (const xmlChar *)"log_debug(\"\\nEnter ", indent+indent); if (cp->isuser) { ses_putstr(scb, U_PREFIX); } ses_putstr(scb, cdef->idstr); ses_putstr(scb, GET_SUFFIX); ses_putstr(scb, (const xmlChar *)" callback\");"); ses_putstr_indent(scb, (const xmlChar *)"}", indent); ses_putchar(scb, '\n'); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ write_cb_logging_init(scb, cdef->idstr, GET_SUFFIX, indent); ses_putstr_indent(scb, (const xmlChar *)"std::string module_name = " "(char*) val_get_mod_name(dstval);", indent); write_cb_logging_add_cb(scb, (const xmlChar *)"module_name", FALSE, indent); } switch (cp->format) { case NCX_CVTTYP_C: case NCX_CVTTYP_CPP_TEST: case NCX_CVTTYP_YC: /* print message about removing (void)foo; line */ ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"/* remove the next line " "if scb is used */", indent); ses_putstr_indent(scb, (const xmlChar *)"(void)scb;", indent); ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"/* remove the next line " "if virval is used */", indent); ses_putstr_indent(scb, (const xmlChar *)"(void)virval;", indent); /* generate if-stmt to screen out any other mode except 'get' */ ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"if (cbmode != GETCB_GET_VALUE) {", indent); ses_putstr_indent(scb, (const xmlChar *)"return ERR_NCX_OPERATION" "_NOT_SUPPORTED;", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"}", indent); break; default: ; } ses_putchar(scb, '\n'); switch (cp->format) { case NCX_CVTTYP_C: case NCX_CVTTYP_CPP_TEST: case NCX_CVTTYP_UC: ses_putstr_indent(scb, (const xmlChar *)"/* set the ", indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" var here, "); btyp = obj_get_basetype(obj); defval = obj_get_default(obj); if (defval) { ses_putstr(scb, (const xmlChar *)"replace (void) or use " "default value */"); ses_putstr_indent(scb, (const xmlChar *)"(void)", indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putchar(scb, ';'); ses_putstr_indent(scb, (const xmlChar *)"res = val_set_simval_obj(", indent); ses_putstr_indent(scb, (const xmlChar *)"dstval,", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"dstval->obj,", indent+indent); ses_indent(scb, indent+indent); ses_putstr(scb, (const xmlChar *)"(const xmlChar *)\""); ses_putstr(scb, defval); ses_putstr(scb, (const xmlChar *)"\");"); } else if (typ_is_string(btyp) || btyp == NCX_BT_IDREF || btyp == NCX_BT_INSTANCE_ID || btyp == NCX_BT_UNION || btyp == NCX_BT_LEAFREF || btyp == NCX_BT_BINARY) { ses_putstr(scb, (const xmlChar *)"change EMPTY_STRING */"); ses_indent(scb, indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" = EMPTY_STRING;"); ses_putstr_indent(scb, (const xmlChar *)"res = val_set_simval_obj(", indent); ses_putstr_indent(scb, (const xmlChar *)"dstval,", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"dstval->obj,", indent+indent); ses_indent(scb, indent+indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)");"); } else if (btyp == NCX_BT_DECIMAL64) { ses_putstr(scb, (const xmlChar *)"change zero */"); ses_indent(scb, indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" = (const xmlChar *)\"0.0\";"); ses_putstr_indent(scb, (const xmlChar *)"res = val_set_simval_obj(", indent); ses_putstr_indent(scb, (const xmlChar *)"dstval,", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"dstval->obj,", indent+indent); ses_indent(scb, indent+indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)");"); } else if (typ_is_number(btyp)) { ses_putstr(scb, (const xmlChar *)"change zero */"); ses_indent(scb, indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" = 0;"); ses_indent(scb, indent); write_c_val_macro_type(scb, obj); ses_putstr(scb, (const xmlChar *)"(dstval) = "); write_c_safe_str(scb, obj_get_name(obj)); ses_putchar(scb, ';'); } else { switch (btyp) { case NCX_BT_ENUM: case NCX_BT_BITS: ses_putstr(scb, (const xmlChar *)"change enum */"); ses_indent(scb, indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" = "); typ_enum = typ_first_enumdef(obj_get_typdef(obj)); if (typ_enum != NULL) { ses_putstr(scb, (const xmlChar *)"(const xmlChar *)\""); ses_putstr(scb, typ_enum->name); ses_putstr(scb, (const xmlChar *)"\";"); } else { ses_putstr(scb, (const xmlChar *)"EMPTY_STRING;"); } ses_putstr_indent(scb, (const xmlChar *)"res = val_set_simval_obj(", indent); ses_putstr_indent(scb, (const xmlChar *)"dstval,", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"dstval->obj,", indent+indent); ses_indent(scb, indent+indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)");"); break; case NCX_BT_BOOLEAN: case NCX_BT_EMPTY: ses_putstr(scb, (const xmlChar *)"change TRUE if needed */"); ses_indent(scb, indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" = TRUE;"); write_c_val_macro_type(scb, obj); ses_putstr(scb, (const xmlChar *)"(dstval) = "); write_c_safe_str(scb, obj_get_name(obj)); ses_putchar(scb, ';'); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } break; case NCX_CVTTYP_YC: /* call the user get function */ ses_putstr_indent(scb, (const xmlChar *)"res = u_", indent); ses_putstr(scb, cdef->idstr); ses_putstr(scb, GET_SUFFIX); ses_putstr(scb, (const xmlChar *)"(dstval"); if (keycount) { ses_putchar(scb, ','); write_c_key_values(scb, obj, objnameQ, keycount, indent+indent); } ses_putchar(scb, ')'); ses_putchar(scb, ';'); break; default: ; } /* return result */ ses_putchar(scb, '\n'); ses_indent(scb, indent); ses_putstr(scb, (const xmlChar *)"return res;"); /* end the function */ ses_putstr(scb, (const xmlChar *)"\n\n} /* "); if (cp->isuser) { ses_putstr(scb, U_PREFIX); } ses_putstr(scb, cdef->idstr); ses_putstr(scb, GET_SUFFIX); ses_putstr(scb, (const xmlChar *)" */\n"); } /* write_c_get_cbfn */ /******************************************************************** * FUNCTION write_c_mro_fn * * Generate the C code for the foo_object_mro function * Make read-only children * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * fullmode == TRUE for full function * FALSE for just C statement guts * obj == object struct for the database object * objnameQ == Q of c_define_t structs to search for this object *********************************************************************/ static void write_c_mro_fn (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp, obj_template_t *obj, boolean fullmode, dlq_hdr_t *objnameQ) { const xmlChar *modname; c_define_t *cdef, *childcdef; obj_template_t *childobj; int32 indent; boolean child_done; modname = ncx_get_modname(mod); indent = cp->indent; cdef = find_path_cdefine(objnameQ, obj); if (cdef == NULL) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return; } /* generate function banner comment */ if (fullmode) { ses_putstr(scb, FN_BANNER_START); ses_putstr(scb, cdef->idstr); ses_putstr(scb, MRO_SUFFIX); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Make read-only child nodes"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Path: "); ses_putstr(scb, cdef->valstr); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_INPUT); ses_putstr(scb, (const xmlChar *)" parentval == the parent struct to " "use for new child nodes"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_RETURN_STATUS); ses_putstr(scb, FN_BANNER_END); /* generate the function prototype lines */ ses_putstr(scb, (const xmlChar *)"\nstatic status_t"); ses_putstr_indent(scb, cdef->idstr, indent); ses_putstr(scb, MRO_SUFFIX); ses_putstr(scb, (const xmlChar *)" (val_value_t *parentval)"); ses_putstr(scb, (const xmlChar *)"\n{"); /* generate static vars */ ses_putstr_indent(scb, (const xmlChar *)"status_t res = NO_ERR;", indent); ses_putstr_indent(scb, (const xmlChar *)"val_value_t *childval = NULL;\n", indent); } if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ write_cb_logging_init(scb, cdef->idstr, MRO_SUFFIX, indent); write_cb_logging_add_cb(scb, ncx_get_modname(mod), TRUE, indent); ses_putchar(scb, '\n'); } /* create read-only child nodes as needed */ for (childobj = obj_first_child(obj); childobj != NULL; childobj = obj_next_child(childobj)) { if (!obj_has_name(childobj) || obj_is_cli(childobj) || obj_is_abstract(childobj) || obj_get_config_flag(childobj)) { continue; } /* this is a real data node config = false */ childcdef = find_path_cdefine(objnameQ, childobj); if (childcdef == NULL) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return; } child_done = FALSE; switch (childobj->objtype) { case OBJ_TYP_LEAF: ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"/* add ", indent); ses_putstr(scb, childcdef->valstr); ses_putstr(scb, (const xmlChar *)" */"); ses_putstr_indent(scb, (const xmlChar *)"childval = agt_make_" "virtual_leaf(", indent); ses_putstr_indent(scb, (const xmlChar *)"parentval->obj,", indent+indent); ses_indent(scb, indent+indent); write_identifier(scb, modname, BAR_NODE, obj_get_name(childobj), cp->isuser); ses_putchar(scb, ','); ses_indent(scb, indent+indent); ses_putstr(scb, childcdef->idstr); ses_putstr(scb, GET_SUFFIX); ses_putchar(scb, ','); ses_putstr_indent(scb, (const xmlChar *)"&res);", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"if (childval != NULL) {", indent); ses_putstr_indent(scb, (const xmlChar *)"val_add_child(childval, " "parentval);", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"} else {", indent); ses_putstr_indent(scb, (const xmlChar *)"return res;", indent+indent); ses_indent(scb, indent); ses_putchar(scb, '}'); child_done = TRUE; break; case OBJ_TYP_LEAF_LIST: break; case OBJ_TYP_LIST: break; case OBJ_TYP_CONTAINER: /* make the container */ ses_putstr_indent(scb, (const xmlChar *)"res = ", indent); ses_putstr(scb, (const xmlChar *)"agt_add_container("); ses_indent(scb, indent+indent); write_identifier(scb, modname, BAR_MOD, modname, cp->isuser); ses_putchar(scb, ','); ses_indent(scb, indent+indent); write_identifier(scb, modname, BAR_NODE, obj_get_name(childobj), cp->isuser); ses_putchar(scb, ','); ses_putstr_indent(scb, (const xmlChar *)"parentval,", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"&childval);", indent+indent); write_if_res(scb, cp, indent); ses_putchar(scb, '\n'); /* any config=false container gets an MRO function */ ses_putstr_indent(scb, (const xmlChar *)"res = ", indent); ses_putstr(scb, childcdef->idstr); ses_putstr(scb, MRO_SUFFIX); ses_putstr(scb, (const xmlChar *)"(childval);"); write_if_res(scb, cp, indent); ses_putchar(scb, '\n'); child_done = TRUE; break; case OBJ_TYP_ANYXML: break; case OBJ_TYP_CHOICE: child_done = TRUE; // nothing to do break; case OBJ_TYP_CASE: child_done = TRUE; // nothing to do break; default: SET_ERROR(ERR_INTERNAL_VAL); } if (!child_done) { /* treat as not-handled flag!!! */ ses_putstr_indent(scb, (const xmlChar *)"/* ", indent); ses_putstr(scb, obj_get_typestr(obj)); ses_putchar(scb, ' '); ses_putstr(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" not handled!!! */"); } } if (fullmode) { /* return result */ ses_putchar(scb, '\n'); ses_indent(scb, indent); ses_putstr(scb, (const xmlChar *)"return res;"); /* end the function */ ses_putstr(scb, (const xmlChar *)"\n\n} /* "); ses_putstr(scb, cdef->idstr); ses_putstr(scb, MRO_SUFFIX); ses_putstr(scb, (const xmlChar *)" */"); if (dlq_empty(&obj->iffeatureQ)) { ses_putchar(scb, '\n'); } } } /* write_c_mro_fn */ /******************************************************************** * FUNCTION write_c_top_mro_fn * * Generate the C code for the foo_object_mro function * Make virtual read-only top-level object * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * obj == object struct for the database object * objnameQ == Q of c_define_t structs to search for this object *********************************************************************/ static void write_c_top_mro_fn (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp, obj_template_t *obj, dlq_hdr_t *objnameQ) { c_define_t *cdef; int32 indent; mod = mod; // Warning suppression switch (obj->objtype) { case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: case OBJ_TYP_ANYXML: return; default: ; } indent = cp->indent; cdef = find_path_cdefine(objnameQ, obj); if (cdef == NULL) { SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return; } /* generate function banner comment */ if (dlq_empty(&obj->iffeatureQ)) { ses_putchar(scb, '\n'); } ses_putstr(scb, FN_BANNER_START); ses_putstr(scb, cdef->idstr); ses_putstr(scb, MRO_SUFFIX); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Make read-only top-level node"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Path: "); ses_putstr(scb, cdef->valstr); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_RETURN_STATUS); ses_putstr(scb, FN_BANNER_END); /* generate the function prototype lines */ ses_putstr(scb, (const xmlChar *)"\nstatic status_t"); ses_putstr_indent(scb, cdef->idstr, indent); ses_putstr(scb, MRO_SUFFIX); ses_putstr(scb, (const xmlChar *)" (void)"); ses_putstr(scb, (const xmlChar *)"\n{"); /* generate static vars */ if (obj_is_np_container(obj)) { ses_putstr_indent(scb, (const xmlChar *) "val_value_t *parentval = NULL, " "*childval = NULL;", indent); } ses_putstr_indent(scb, (const xmlChar *)"status_t res = NO_ERR;", indent); ses_putchar(scb, '\n'); if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ write_cb_logging_init(scb, cdef->idstr, MRO_SUFFIX, indent); write_cb_logging_add_cb(scb, ncx_get_modname(mod), TRUE, indent); } /* generate 'add ' comment */ ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"/* add ", indent); ses_putstr(scb, cdef->valstr); ses_putstr(scb, (const xmlChar *)" */"); /* generate 'add ' code */ if (obj_is_np_container(obj)) { /* call agt_add_top_container(...) */ ses_putstr_indent(scb, (const xmlChar *) "res = agt_add_top_container(", indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)"_obj, &parentval);"); write_if_res(scb, cp, indent); write_c_mro_fn(scb, mod, cp, obj, FALSE, objnameQ); } else if (obj_is_leafy(obj)) { /* call agt_add_top_virtual(...) */ ses_putstr_indent(scb, (const xmlChar *) "res = agt_add_top_virtual(", indent); ses_indent(scb, indent+indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)"_obj,"); ses_indent(scb, indent+indent); ses_putstr(scb, cdef->idstr); ses_putstr(scb, GET_SUFFIX); ses_putstr(scb, (const xmlChar *)");"); } else { ses_putstr_indent(scb, (const xmlChar *) "/* add code to generate top-level " "node here */", indent); ses_putstr_indent(scb, (const xmlChar *)"res = NO_ERR;", indent); } /* return result */ ses_putchar(scb, '\n'); ses_indent(scb, indent); ses_putstr(scb, (const xmlChar *)"return res;"); /* end the function */ ses_putstr(scb, (const xmlChar *)"\n\n} /* "); ses_putstr(scb, cdef->idstr); ses_putstr(scb, MRO_SUFFIX); ses_putstr(scb, (const xmlChar *)" */"); if (dlq_empty(&obj->iffeatureQ)) { ses_putchar(scb, '\n'); } } /* write_c_top_mro_fn */ /******************************************************************** * FUNCTION write_c_objects * * Generate the callback functions for each object found * * INPUTS: * scb == session to use * mod == module in progress * cp == conversion parameters to use * datadefQ == que of obj_template_t to use * objnameQ == Q of c_define_t structs to use * uprotomode == TRUE: just generate a format==NCX_CVTTYP_UH prototype * FALSE: normal mode * *********************************************************************/ static void write_c_objects (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *datadefQ, dlq_hdr_t *objnameQ, boolean uprotomode) { obj_template_t *obj; dlq_hdr_t *childdatadefQ; for (obj = (obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || !obj_is_data_db(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } write_h_iffeature_start(scb, &obj->iffeatureQ); /* generate functions in reverse order so parent functions * will be able to access decendant functions without * any forward function declarations */ childdatadefQ = obj_get_datadefQ(obj); if (childdatadefQ) { write_c_objects(scb, mod, cp, childdatadefQ, objnameQ, uprotomode); } /* generate the callback function: edit or get */ if (obj_get_config_flag(obj)) { if (!uprotomode) { /* check if this node has any non-config children */ if (!cp->isuser && obj_has_ro_children(obj)) { write_c_mro_fn(scb, mod, cp, obj, TRUE, objnameQ); } } /* generate the foo_edit function, except for * choice nodes and case nodes */ if (!(obj->objtype == OBJ_TYP_CHOICE || obj->objtype == OBJ_TYP_CASE)) { write_c_edit_cbfn(scb, cp, obj, objnameQ, uprotomode); } } else { if (obj_is_leafy(obj)) { /* generate the foo_get function */ write_c_get_cbfn(scb, cp, obj, objnameQ, uprotomode); } else if (cp->isuser) { ; // do nothing } else if (obj_is_np_container(obj) && !obj_is_top(obj)) { write_c_mro_fn(scb, mod, cp, obj, TRUE, objnameQ); } else { log_warn("\nWarning: no get-CB generated " "for top-level operational " "%s '%s'", obj_get_typestr(obj), obj_get_name(obj)); } if (!cp->isuser && obj_is_top(obj)) { /* handles top-level leafs and NP containers only ! */ write_c_top_mro_fn(scb, mod, cp, obj, objnameQ); } } write_h_iffeature_end(scb, &obj->iffeatureQ); } } /* write_c_objects */ /******************************************************************** * FUNCTION write_c_rpc_fn * * Generate the C code for the foo_rpc_fn_validate function * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * rpcobj == object struct for the RPC method * is_validate == TRUE for _validate, FALSE for _invoke * objnameQ == Q of c_define_t structs to use to find * the mapped C identifier for each object * uprotomode == TRUE: just generate a format==NCX_CVTTYP_UH prototype * FALSE: normal mode *********************************************************************/ static void write_c_rpc_fn (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp, obj_template_t *rpcobj, boolean is_validate, dlq_hdr_t *objnameQ, boolean uprotomode) { const xmlChar *modname = ncx_get_modname(mod); int32 indent = cp->indent; obj_template_t *inputobj = obj_find_child(rpcobj, NULL, YANG_K_INPUT); boolean hasinput = (inputobj && obj_has_children(inputobj)); /* generate function banner comment */ if (dlq_empty(&rpcobj->iffeatureQ)) { ses_putchar(scb, '\n'); } ses_putstr(scb, FN_BANNER_START); write_identifier(scb, modname, NULL, obj_get_name(rpcobj), cp->isuser); if (is_validate) { ses_putstr(scb, (const xmlChar *)"_validate"); } else { ses_putstr(scb, (const xmlChar *)"_invoke"); } ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_LN); if (is_validate) { ses_putstr(scb, (const xmlChar *)"RPC validation phase"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"All YANG constraints have " "passed at this point."); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Add description-stmt checks " "in this function."); } else { ses_putstr(scb, (const xmlChar *)"RPC invocation phase"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"All constraints have " "passed at this point."); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Call " "device instrumentation code " "in this function."); } ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_INPUT); ses_putstr(scb, (const xmlChar *)" see agt/agt_rpc.h for details"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_RETURN_STATUS); ses_putstr(scb, FN_BANNER_END); /* generate the function prototype lines */ if (uprotomode) { ses_putstr(scb, (const xmlChar *)"\nextern status_t "); } else if (cp->isuser) { ses_putstr(scb, (const xmlChar *)"\nstatus_t "); } else { ses_putstr(scb, (const xmlChar *)"\nstatic status_t "); } write_identifier(scb, modname, NULL, obj_get_name(rpcobj), cp->isuser); if (is_validate) { ses_putstr(scb, (const xmlChar *)"_validate ("); } else { ses_putstr(scb, (const xmlChar *)"_invoke ("); } ses_putstr_indent(scb, (const xmlChar *)"ses_cb_t *scb,", indent); ses_putstr_indent(scb, (const xmlChar *)"rpc_msg_t *msg,", indent); ses_putstr_indent(scb, (const xmlChar *)"xml_node_t *methnode)", indent); if (uprotomode) { ses_putstr(scb, (const xmlChar *)";\n"); return; } /* begin function body */ ses_putstr(scb, (const xmlChar *)"\n{"); ses_putstr_indent(scb, (const xmlChar *)"status_t res = NO_ERR;", indent); if (is_validate) { ses_putstr_indent(scb, (const xmlChar *)"val_value_t *errorval = NULL;", indent); } ses_putchar(scb, '\n'); if (hasinput) { /* declare value pointer node variables for * the RPC input parameters */ obj_template_t *obj; for (obj = obj_first_child(inputobj); obj != NULL; obj = obj_next_child(obj)) { if (obj_is_abstract(obj) || !obj_has_name(obj)) { continue; } write_h_iffeature_start(scb, &obj->iffeatureQ); ses_putstr_indent(scb, (const xmlChar *)"val_value_t *", indent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)"_val;"); ses_indent(scb, indent); write_c_objtype_ex(scb, obj, objnameQ, ';', TRUE, FALSE); write_h_iffeature_end(scb, &obj->iffeatureQ); } /* retrieve the parameter from the input */ for (obj = obj_first_child(inputobj); obj != NULL; obj = obj_next_child(obj)) { if (obj_is_abstract(obj) || !obj_has_name(obj)) { continue; } write_h_iffeature_start(scb, &obj->iffeatureQ); boolean iffdone = write_c_iffeature_start(scb, &obj->iffeatureQ, cp, indent); int32 extraindent = (iffdone) ? indent : 0; ses_putchar(scb, '\n'); ses_indent(scb, indent+extraindent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)"_val = val_find_child("); ses_putstr_indent(scb, (const xmlChar *)"msg->rpc_input,", indent+indent+extraindent); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, BAR_MOD, modname, FALSE); ses_putchar(scb, ','); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, BAR_NODE, obj_get_name(obj), FALSE); ses_putstr(scb, (const xmlChar *)");"); ses_putstr_indent(scb, (const xmlChar *)"if (", indent+extraindent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)"_val != NULL && "); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)"_val->res == NO_ERR) {"); if (obj_is_leafy(obj)) { ses_indent(scb, indent+indent+extraindent); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" = "); write_c_val_macro_type(scb, obj); ses_putchar(scb, '('); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)"_val);"); } else { ses_putstr_indent(scb, (const xmlChar *)"/* replace the " "following line with real code to " "fill in structure */", indent+indent+extraindent); ses_putstr_indent(scb, (const xmlChar *)"(void)", indent+indent+extraindent); write_c_safe_str(scb, obj_get_name(obj)); ses_putchar(scb, ';'); } ses_indent(scb, indent+extraindent); ses_putchar(scb, '}'); write_c_iffeature_end(scb, &obj->iffeatureQ, indent); write_h_iffeature_end(scb, &obj->iffeatureQ); } } if (is_validate) { /* write generic record error code */ write_if_record_error(scb, cp, indent, TRUE, FALSE, TRUE); } else { ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"/* remove the next line " "if scb is used */", indent); ses_putstr_indent(scb, (const xmlChar *)"(void)scb;", indent); if (!hasinput) { ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"/* remove the next line " "if msg is used */", indent); ses_putstr_indent(scb, (const xmlChar *)"(void)msg;", indent); } ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"/* remove the next line " "if methnode is used */", indent); ses_putstr_indent(scb, (const xmlChar *)"(void)methnode;", indent); ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"/* invoke your device " "instrumentation code here */\n", indent); } if (cp->format == NCX_CVTTYP_CPP_TEST) { /* generate callback logging */ ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"YumaTest::SILCallbackLog& " "cbLog = YumaTest::SILCallbackLog::getInstance();", indent); ses_putstr_indent(scb, (const xmlChar *) "YumaTest::SILCallbackLog::CallbackInfo cbData;", indent); ses_putstr_indent(scb, (const xmlChar *)"cbData.cbName = \"", indent); write_identifier(scb, modname, NULL, obj_get_name(rpcobj), cp->isuser); if (is_validate) { ses_putstr(scb, (const xmlChar *)"_validate"); } else { ses_putstr(scb, (const xmlChar *)"_invoke"); } ses_putstr(scb, (const xmlChar *)"\";"); write_cb_logging_add_cb(scb, ncx_get_modname(mod), TRUE, indent); ses_putchar(scb, '\n'); } /* return NO_ERR */ ses_indent(scb, indent); ses_putstr(scb, (const xmlChar *)"return res;"); /* end the function */ ses_putstr(scb, (const xmlChar *)"\n\n} /* "); write_identifier(scb, modname, NULL, obj_get_name(rpcobj), cp->isuser); if (is_validate) { ses_putstr(scb, (const xmlChar *)"_validate */"); } else { ses_putstr(scb, (const xmlChar *)"_invoke */"); } if (dlq_empty(&rpcobj->iffeatureQ) || is_validate) { ses_putchar(scb, '\n'); } } /* write_c_rpc_fn */ /******************************************************************** * FUNCTION write_c_rpcs * * Generate the C file decls for the RPC methods in the * specified module * * INPUTS: * scb == session control block to use for writing * mod == que of obj_template_t to use * cp == conversion parameters to use * objnameQ == Q of c_define_t structs to use to find * the mapped C identifier for each object * uprotomode == TRUE: just generate a format==NCX_CVTTYP_UH prototype * FALSE: normal mode *********************************************************************/ static void write_c_rpcs (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *objnameQ, boolean uprotomode) { obj_template_t *obj; for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_is_rpc(obj) || obj_is_abstract(obj)) { continue; } write_h_iffeature_start(scb, &obj->iffeatureQ); write_c_rpc_fn(scb, mod, cp, obj, TRUE, objnameQ, uprotomode); write_c_rpc_fn(scb, mod, cp, obj, FALSE, objnameQ, uprotomode); write_h_iffeature_end(scb, &obj->iffeatureQ); } } /* write_c_rpcs */ /******************************************************************** * FUNCTION write_c_notif * * Generate the C file decls for 1 notification * * INPUTS: * scb == session control block to use for writing * datadefQ == que of obj_template_t to use * cp == conversion parms to use * objnameQ == Q of c_define_t structs to use to find * the mapped C identifier for each object * uprotomode == TRUE: just generate a format==NCX_CVTTYP_UH prototype * FALSE: normal mode *********************************************************************/ static void write_c_notif (ses_cb_t *scb, obj_template_t *notifobj, const yangdump_cvtparms_t *cp, dlq_hdr_t *objnameQ, boolean uprotomode) { const xmlChar *modname = obj_get_mod_name(notifobj); int32 indent = cp->indent; boolean haspayload = obj_has_children(notifobj); /* generate function banner comment */ if (dlq_empty(¬ifobj->iffeatureQ)) { ses_putchar(scb, '\n'); } ses_putstr(scb, FN_BANNER_START); write_identifier(scb, modname, NULL, obj_get_name(notifobj), cp->isuser); ses_putstr(scb, (const xmlChar *)"_send"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Send a "); write_identifier(scb, obj_get_mod_name(notifobj), NULL, obj_get_name(notifobj), cp->isuser); ses_putstr(scb, (const xmlChar *)" notification"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Called by your code when " "notification event occurs"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_END); /* generate the function prototype lines */ if (uprotomode) { ses_putstr(scb, (const xmlChar *)"\nextern void "); } else { ses_putstr(scb, (const xmlChar *)"\nvoid "); } write_identifier(scb, obj_get_mod_name(notifobj), NULL, obj_get_name(notifobj), cp->isuser); ses_putstr(scb, (const xmlChar *)"_send ("); boolean anydone = FALSE; obj_template_t *obj, *nextobj; for (obj = obj_first_child(notifobj); obj != NULL; obj = nextobj) { nextobj = obj_next_child(obj); if (!obj_has_name(obj)) { continue; } anydone = TRUE; ses_indent(scb, indent); write_c_objtype_ex(scb, obj, objnameQ, (nextobj == NULL) ? ')' : ',', TRUE, TRUE); } if (!anydone) { ses_putstr(scb, (const xmlChar *)"void)"); } if (uprotomode) { ses_putstr(scb, (const xmlChar *)";\n"); return; } /* start function body */ ses_putchar(scb, '\n'); ses_putchar(scb, '{'); /* declare local variables */ ses_putstr_indent(scb, (const xmlChar *)"agt_not_msg_t *notif;", indent); if (haspayload) { ses_putstr_indent(scb, (const xmlChar *)"val_value_t *parmval;", indent); ses_putstr_indent(scb, (const xmlChar *)"status_t res = NO_ERR;", indent); } /* make the entire contents conditional */ ses_putchar(scb, '\n'); boolean iffdone = write_c_iffeature_start(scb, ¬ifobj->iffeatureQ, cp, indent); int32 extraindent = (iffdone) ? indent : 0; /* print a debug message */ ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"if (LOGDEBUG) {", indent+extraindent); ses_putstr_indent(scb, (const xmlChar *)"log_debug(\"\\nGenerating <", indent+indent+extraindent); ses_putstr(scb, obj_get_name(notifobj)); ses_putstr(scb, (const xmlChar *)"> notification\");"); ses_putstr_indent(scb, (const xmlChar *)"}\n", indent+extraindent); /* allocate a new notification */ ses_putstr_indent(scb, (const xmlChar *)"notif = agt_not_new_notification(", indent+extraindent); write_c_safe_str(scb, obj_get_name(notifobj)); ses_putstr(scb, (const xmlChar *)"_obj);"); ses_putstr_indent(scb, (const xmlChar *)"if (notif == NULL) {", indent+extraindent); ses_putstr_indent(scb, (const xmlChar *)"log_error(\"\\nError: " "malloc failed, cannot send \"", indent+indent+extraindent); ses_putstr_indent(scb, (const xmlChar *)"\"<", indent+indent+extraindent); ses_putstr(scb, obj_get_name(notifobj)); ses_putstr(scb, (const xmlChar *)"> notification\");"); ses_putstr_indent(scb, (const xmlChar *)"return;", indent+indent+extraindent); ses_putstr_indent(scb, (const xmlChar *)"}\n", indent+extraindent); /* generate code to construct the notification payload */ for (obj = obj_first_child(notifobj); obj != NULL; obj = obj_next_child(obj)) { if (!obj_has_name(obj)) { continue; } write_h_iffeature_start(scb, &obj->iffeatureQ); boolean objiffdone = write_c_iffeature_start(scb, &obj->iffeatureQ, cp, indent+extraindent); int32 extra2 = (objiffdone) ? indent : 0; if (obj->objtype != OBJ_TYP_LEAF) { /* TBD: need to handle non-leafs automatically */ ses_putstr_indent(scb, (const xmlChar *)"/* add ", indent+extraindent+extra2); ses_putstr(scb, obj_get_typestr(obj)); ses_putchar(scb, ' '); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" to payload"); ses_putstr_indent(scb, (const xmlChar *)" * replace following line" " with real code", indent+extraindent+extra2); ses_putstr_indent(scb, (const xmlChar *)" */", indent+extraindent+extra2); ses_putstr_indent(scb, (const xmlChar *)"(void)", indent+extraindent+extra2); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)";\n"); write_c_iffeature_end(scb, &obj->iffeatureQ, indent+extraindent); write_h_iffeature_end(scb, &obj->iffeatureQ); continue; } /* this is a leaf parameter -- add starting comment */ ses_putstr_indent(scb, (const xmlChar *)"/* add ", indent+extraindent+extra2); write_c_safe_str(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" to payload */"); ncx_btype_t btyp = obj_get_basetype(obj); const xmlChar *fname; switch (btyp) { case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: fname = (const xmlChar *)"agt_make_uint_leaf"; break; case NCX_BT_UINT64: fname = (const xmlChar *)"agt_make_uint64_leaf"; break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: fname = (const xmlChar *)"agt_make_int_leaf"; break; case NCX_BT_INT64: fname = (const xmlChar *)"agt_make_int64_leaf"; break; case NCX_BT_IDREF: fname = (const xmlChar *)"agt_make_idref_leaf"; break; default: /* not all types are covered yet!!! such as: * uint64, int64, decimal64, binary, boolean * instance-identifier, leafref */ fname = (const xmlChar *)"agt_make_leaf"; } /* agt_make_leaf stmt */ ses_putstr_indent(scb, (const xmlChar *)"parmval = ", indent+extraindent+extra2); ses_putstr(scb, fname); ses_putchar(scb, '('); ses_indent(scb, indent+indent+extraindent+extra2); write_c_safe_str(scb, obj_get_name(notifobj)); ses_putstr(scb, (const xmlChar *)"_obj,"); ses_indent(scb, indent+indent+extraindent+extra2); write_identifier(scb, modname, BAR_NODE, obj_get_name(obj), FALSE); ses_putchar(scb, ','); ses_indent(scb, indent+indent+extraindent+extra2); write_c_safe_str(scb, obj_get_name(obj)); ses_putchar(scb, ','); ses_putstr_indent(scb, (const xmlChar *)"&res);", indent+indent+extraindent); /* check result stmt, Q leaf if non-NULL */ ses_putstr_indent(scb, (const xmlChar *)"if (parmval == NULL) {", indent+extraindent); ses_putstr_indent(scb, (const xmlChar *)"log_error(", indent+indent+extraindent+extra2); ses_putstr_indent(scb, (const xmlChar *)"\"\\nError: " "make leaf failed (%s), cannot send \"", (indent*3)+extraindent+extra2); ses_putstr_indent(scb, (const xmlChar *)"\"<", (indent*3)+extraindent+extra2); ses_putstr(scb, obj_get_name(notifobj)); ses_putstr(scb, (const xmlChar *)"> notification\","); ses_putstr_indent(scb, (const xmlChar *)"get_error_string(res));", (indent*3)+extraindent+extra2); ses_putstr_indent(scb, (const xmlChar *)"} else {", indent+extraindent); ses_putstr_indent(scb, (const xmlChar *)"agt_not_add_to_payload" "(notif, parmval);", indent+indent+extraindent+extra2); ses_putstr_indent(scb, (const xmlChar *)"}\n", indent+extraindent+extra2); write_c_iffeature_end(scb, &obj->iffeatureQ, indent+extraindent); write_h_iffeature_end(scb, &obj->iffeatureQ); } /* save the malloced notification struct */ ses_putstr_indent(scb, (const xmlChar *)"agt_not_queue_notification(notif);\n", indent+extraindent); write_c_iffeature_end(scb, ¬ifobj->iffeatureQ, indent); /* end the function */ ses_putstr(scb, (const xmlChar *)"\n} /* "); write_identifier(scb, modname, NULL, obj_get_name(notifobj), cp->isuser); ses_putstr(scb, (const xmlChar *)"_send */"); if (dlq_empty(¬ifobj->iffeatureQ)) { ses_putchar(scb, '\n'); } } /* write_c_notif */ /******************************************************************** * FUNCTION write_c_notifs * * Generate the C file decls for the notifications in the * specified datadefQ * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parms to use * objnameQ == Q of c_define_t structs to use to find * the mapped C identifier for each object * uprotomode == TRUE: just generate a format==NCX_CVTTYP_UH prototype * FALSE: normal mode *********************************************************************/ static void write_c_notifs (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *objnameQ, boolean uprotomode) { obj_template_t *obj; for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_is_notif(obj) || obj_is_abstract(obj)) { continue; } write_h_iffeature_start(scb, &obj->iffeatureQ); write_c_notif(scb, obj, cp, objnameQ, uprotomode); write_h_iffeature_end(scb, &obj->iffeatureQ); } } /* write_c_notifs */ /******************************************************************** * FUNCTION write_c_register_datacb * * Generate the C code for registering a data node callback fn * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * obj == object to generate init function calls for * cdefQ == Q of ncx_define_t structs to search * startindent == starting indent amount *********************************************************************/ static void write_c_register_datacb (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp, obj_template_t *obj, dlq_hdr_t *cdefQ, int32 startindent) { if (!obj_get_config_flag(obj)) { /* nothing to do now for read-only objects */ return; } int32 indent = startindent; if (obj->objtype != OBJ_TYP_CHOICE && obj->objtype != OBJ_TYP_CASE) { const c_define_t *cdef = find_path_cdefine(cdefQ, obj); if (cdef == NULL) { SET_ERROR(ERR_INTERNAL_VAL); return; } write_h_iffeature_start(scb, &obj->iffeatureQ); boolean iffdone = write_c_iffeature_start(scb, &obj->iffeatureQ, cp, startindent); if (iffdone) { indent += cp->indent; } /* register the edit callback */ ses_putstr_indent(scb, (const xmlChar *)"res = agt_cb_" "register_callback(", indent); /* modname parameter */ const xmlChar *modname = ncx_get_modname(mod); ses_indent(scb, indent + cp->indent); write_identifier(scb, modname, BAR_MOD, modname, FALSE); ses_putchar(scb, ','); /* defpath parameter */ ses_indent(scb, indent + cp->indent); ses_putstr(scb, (const xmlChar *)"(const xmlChar *)\""); ses_putstr(scb, cdef->valstr); ses_putchar(scb, '"'); ses_putchar(scb, ','); /* version parameter */ if (mod->version) { ses_indent(scb, indent + cp->indent); write_identifier(scb, modname, BAR_REV, modname, cp->isuser); ses_putchar(scb, ','); } else { ses_putstr_indent(scb, (const xmlChar *)"NULL,", indent + cp->indent); } /* cbfn parameter: * use the static function in the YC or C file */ ses_indent(scb, indent + cp->indent); ses_putstr(scb, cdef->idstr); ses_putstr(scb, (const xmlChar *)"_edit);"); write_if_res(scb, cp, indent); ses_putchar(scb, '\n'); } obj_template_t *childobj = obj_first_child(obj); for (; childobj != NULL; childobj = obj_next_child(childobj)) { if (obj_has_name(childobj) && !obj_is_cli(childobj) && !obj_is_abstract(childobj) && obj_is_data_db(childobj)) { write_c_register_datacb(scb, mod, cp, childobj, cdefQ, indent); } } if (obj->objtype != OBJ_TYP_CHOICE && obj->objtype != OBJ_TYP_CASE) { write_c_iffeature_end(scb, &obj->iffeatureQ, startindent); write_h_iffeature_end(scb, &obj->iffeatureQ); } } /* write_c_register_datacb */ /******************************************************************** * FUNCTION write_c_feature * * Generate the enable or disable code for 1 feature statement * * #ifdef * ncx_set_feature_enable_entry(, TRUE); * #else * ncx_set_feature_enable_entry(, FALSE); * #endif * * INPUTS: * scb == session control block to use for writing * feature == ncx_feature_t to use * cp == conversion parameters to use *********************************************************************/ static void write_c_feature (ses_cb_t *scb, const ncx_feature_t *feature, const yangdump_cvtparms_t *cp) { int32 indent = cp->indent; const xmlChar *modname = feature->tkerr.mod->name; ses_putstr(scb, POUND_IFDEF); write_identifier(scb, modname, BAR_FEAT, feature->name, TRUE); ses_putstr_indent(scb, (const xmlChar *)"ncx_set_feature_enable(", indent); ses_indent(scb, indent+indent); write_identifier(scb, modname, BAR_MOD, modname, FALSE); ses_putchar(scb, ','); ses_indent(scb, indent+indent); ses_putstr(scb, (const xmlChar *)"(const xmlChar *)\""); ses_putstr(scb, feature->name); ses_putstr(scb, (const xmlChar *)"\","); ses_indent(scb, indent+indent); ses_putstr(scb, (const xmlChar *)"TRUE);"); ses_putstr(scb, POUND_ELSE); ses_putstr_indent(scb, (const xmlChar *)"ncx_set_feature_enable(", indent); ses_indent(scb, indent+indent); write_identifier(scb, modname, BAR_MOD, modname, FALSE); ses_putchar(scb, ','); ses_indent(scb, indent+indent); ses_putstr(scb, (const xmlChar *)"(const xmlChar *)\""); ses_putstr(scb, feature->name); ses_putstr(scb, (const xmlChar *)"\","); ses_indent(scb, indent+indent); ses_putstr(scb, (const xmlChar *)"FALSE);"); ses_putstr(scb, POUND_ENDIF); ses_putchar(scb, '\n'); } /* write_c_feature */ /******************************************************************** * FUNCTION write_c_features * * Generate the C code to enable or disable the module YANG features * * INPUTS: * scb == session control block to use for writing * featureQ == que of ncx_feature_t to use * cp == conversion parameters to use * *********************************************************************/ static void write_c_features (ses_cb_t *scb, const dlq_hdr_t *featureQ, const yangdump_cvtparms_t *cp) { const ncx_feature_t *feature; if (dlq_empty(featureQ)) { return; } ses_putchar(scb, '\n'); for (feature = (const ncx_feature_t *)dlq_firstEntry(featureQ); feature != NULL; feature = (const ncx_feature_t *)dlq_nextEntry(feature)) { write_c_feature(scb, feature, cp); } } /* write_c_features */ /******************************************************************** * FUNCTION write_c_init_fn * * Generate the C code for the foo_init function * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * objnameQ == Q of c_define_t structs to use to find * the mapped C identifier for each object * protomode == TRUE: just generate any H format prototype * FALSE: normal mode *********************************************************************/ static void write_c_init_fn (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *objnameQ, boolean protomode) { const xmlChar *modname = ncx_get_modname(mod); int32 indent = cp->indent; /* generate function banner comment */ ses_putstr(scb, FN_BANNER_START); write_identifier(scb, modname, NULL, (const xmlChar *)"init", cp->isuser); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"initialize the "); ses_putstr(scb, mod->name); ses_putstr(scb, (const xmlChar *)" server instrumentation library"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_INPUT); ses_putstr(scb, (const xmlChar *)" modname == requested module name"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)" revision == requested " "version (NULL for any)"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_RETURN_STATUS); ses_putstr(scb, FN_BANNER_END); /* generate the function prototype lines */ if (protomode) { ses_putstr(scb, (const xmlChar *)"\nextern status_t "); } else { ses_putstr(scb, (const xmlChar *)"\nstatus_t "); } write_identifier(scb, modname, NULL, (const xmlChar *)"init", cp->isuser); ses_putstr(scb, (const xmlChar *)" ("); ses_putstr_indent(scb, (const xmlChar *)"const xmlChar *modname,", indent); ses_putstr_indent(scb, (const xmlChar *)"const xmlChar *revision)", indent); if (protomode) { ses_putstr(scb, (const xmlChar *)";\n"); return; } /* start function body */ ses_putstr(scb, (const xmlChar *)"\n{"); ses_putstr_indent(scb, (const xmlChar *)"status_t res = NO_ERR;", indent); if (cp->isuser) { /* --format=uc so this init section not needed; just init * a local module var to get the module loaded by the y_init fn */ ses_putstr_indent(scb, (const xmlChar *)"ncx_module_t *", indent); write_c_safe_str(scb, modname); ses_putstr(scb, (const xmlChar *)"_mod = NULL;"); } else { /* declare the function variables */ ses_putstr_indent(scb, (const xmlChar *) "agt_profile_t *agt_profile = agt_get_profile();", indent); /* call the init_static_vars function */ ses_putchar(scb, '\n'); ses_indent(scb, indent); write_identifier(scb, modname, NULL, (const xmlChar *)"init_static_vars", cp->isuser); ses_putstr(scb, (const xmlChar *)"();\n"); /* check the input variables */ ses_putstr_indent(scb, (const xmlChar *)"/* change if custom handling " "done */", indent); ses_putstr_indent(scb, (const xmlChar *)"if (xml_strcmp(modname, ", indent); write_identifier(scb, modname, BAR_MOD, modname, cp->isuser); ses_putstr(scb, (const xmlChar *)")) {"); ses_putstr_indent(scb, (const xmlChar *)"return ERR_NCX_UNKNOWN_MODULE;", indent+indent); ses_indent(scb, indent); ses_putstr(scb, (const xmlChar *)"}\n"); ses_putstr_indent(scb, (const xmlChar *)"if (revision && " "xml_strcmp(revision, ", indent); write_identifier(scb, modname, BAR_REV, modname, cp->isuser); ses_putstr(scb, (const xmlChar *)")) {"); ses_putstr_indent(scb, (const xmlChar *)"return ERR_NCX_WRONG_VERSION;", indent+indent); ses_indent(scb, indent); ses_putstr(scb, (const xmlChar *)"}"); } /* load-time: enable or disable the module features set at * compile time; the user can override this action at run-time */ write_c_features(scb, &mod->featureQ, cp); /* load the module */ if (mod->ismod) { /* user C so find the module already loaded */ if (cp->format == NCX_CVTTYP_UC) { ses_indent(scb, indent); write_c_safe_str(scb, modname); ses_putstr(scb, (const xmlChar *)"_mod = ncx_find_module("); ses_indent(scb, indent+indent); write_identifier(scb, modname, BAR_MOD, modname, FALSE); ses_putchar(scb, ','); if (mod->version) { ses_indent(scb, indent+indent); write_identifier(scb, modname, BAR_REV, modname, FALSE); } else { ses_putstr_indent(scb, (const xmlChar *)" NULL", indent+indent); } ses_putstr(scb, (const xmlChar *)");"); ses_putstr_indent(scb, (const xmlChar *)"if (", indent); write_c_safe_str(scb, modname); ses_putstr(scb, (const xmlChar *)"_mod == NULL) {"); ses_putstr_indent(scb, (const xmlChar *) "return ERR_NCX_OPERATION_FAILED;", indent+indent); ses_putstr_indent(scb, (const xmlChar *)"}", indent); } else { /* main C or y_c so load the module */ ses_putstr_indent(scb, (const xmlChar *)"res = ncxmod_load_module(", indent); ses_indent(scb, indent+indent); write_identifier(scb, modname, BAR_MOD, modname, FALSE); ses_putchar(scb, ','); if (mod->version) { ses_indent(scb, indent+indent); write_identifier(scb, modname, BAR_REV, modname, FALSE); ses_putchar(scb, ','); } else { ses_putstr_indent(scb, (const xmlChar *)" NULL,", indent+indent); } ses_putstr_indent(scb, (const xmlChar *)"&agt_profile->agt_savedevQ,", indent+indent); ses_indent(scb, indent+indent); ses_putchar(scb, '&'); write_c_safe_str(scb, modname); ses_putstr(scb, (const xmlChar *)"_mod);"); write_if_res(scb, cp, indent); ses_putchar(scb, '\n'); } } /* load the static object variable pointers */ boolean iffdone = FALSE; int32 extraindent = 0; obj_template_t *obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); for (; obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } if ((cp->format == NCX_CVTTYP_UC && obj_is_notif(obj)) || (cp->format == NCX_CVTTYP_YC && !obj_is_notif(obj)) || !(cp->format == NCX_CVTTYP_UC || cp->format == NCX_CVTTYP_YC)) { write_h_iffeature_start(scb, &obj->iffeatureQ); iffdone = write_c_iffeature_start(scb, &obj->iffeatureQ, cp, indent); extraindent = (iffdone) ? indent : 0; ses_indent(scb, indent+extraindent); write_c_object_var(scb, obj_get_name(obj)); ses_putstr(scb, (const xmlChar *)" = ncx_find_object("); ses_indent(scb, indent+indent+extraindent); write_c_safe_str(scb, modname); ses_putstr(scb, (const xmlChar *)"_mod,"); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, BAR_NODE, obj_get_name(obj), FALSE); ses_putstr(scb, (const xmlChar *)");"); ses_putstr_indent(scb, (const xmlChar *)"if (", indent+extraindent); write_c_safe_str(scb, modname); ses_putstr(scb, (const xmlChar *)"_mod == NULL) {"); ses_putstr_indent(scb, (const xmlChar *)"return SET_ERROR(" "ERR_NCX_DEF_NOT_FOUND);", indent+indent+extraindent); ses_putstr_indent(scb, (const xmlChar *)"}", indent+extraindent); write_c_iffeature_end(scb, &obj->iffeatureQ, indent); write_h_iffeature_end(scb, &obj->iffeatureQ); } } if (!cp->isuser) { /* initialize any RPC methods */ for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_is_rpc(obj) || obj_is_abstract(obj)) { continue; } write_h_iffeature_start(scb, &obj->iffeatureQ); iffdone = write_c_iffeature_start(scb, &obj->iffeatureQ, cp, indent); extraindent = (iffdone) ? indent : 0; /* register validate function */ ses_putstr_indent(scb, (const xmlChar *)"res = agt_rpc_register_method(", indent+extraindent); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, BAR_MOD, modname, FALSE); ses_putchar(scb, ','); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, BAR_NODE, obj_get_name(obj), FALSE); ses_putchar(scb, ','); ses_putstr_indent(scb, (const xmlChar *)"AGT_RPC_PH_VALIDATE,", indent+indent+extraindent); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, NULL, obj_get_name(obj), cp->format == NCX_CVTTYP_YC); ses_putstr(scb, (const xmlChar *)"_validate);"); write_if_res(scb, cp, indent+extraindent); ses_putchar(scb, '\n'); /* register invoke function */ ses_putstr_indent(scb, (const xmlChar *)"res = agt_rpc_register_method(", indent+extraindent); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, BAR_MOD, modname, FALSE); ses_putchar(scb, ','); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, BAR_NODE, obj_get_name(obj), FALSE); ses_putchar(scb, ','); ses_putstr_indent(scb, (const xmlChar *)"AGT_RPC_PH_INVOKE,", indent+indent+extraindent); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, NULL, obj_get_name(obj), cp->format == NCX_CVTTYP_YC); ses_putstr(scb, (const xmlChar *)"_invoke);"); write_if_res(scb, cp, indent+extraindent); ses_putchar(scb, '\n'); write_c_iffeature_end(scb, &obj->iffeatureQ, indent); write_h_iffeature_end(scb, &obj->iffeatureQ); } /* initialize any object callbacks here */ for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (obj_has_name(obj) && obj_is_data_db(obj) && !obj_is_cli(obj) && !obj_is_abstract(obj)) { write_c_register_datacb(scb, mod, cp, obj, objnameQ, indent); } } } if (cp->format == NCX_CVTTYP_YC) { /* call the user init1 function */ ses_putstr_indent(scb, (const xmlChar *)"res = ", indent); write_identifier(scb, modname, NULL, (const xmlChar *)"init", TRUE); ses_putstr(scb, (const xmlChar *)"(modname, revision);"); } else { ses_putstr_indent(scb,(const xmlChar *)"/* put your module " "initialization code here */\n", indent); } /* return res; */ ses_indent(scb, indent); ses_putstr(scb, (const xmlChar *)"return res;"); /* end the function */ ses_putstr(scb, (const xmlChar *)"\n} /* "); write_identifier(scb, modname, NULL, (const xmlChar *)"init", cp->isuser); ses_putstr(scb, (const xmlChar *)" */\n"); } /* write_c_init_fn */ /******************************************************************** * FUNCTION write_c_init2_fn * * Generate the C code for the foo_init2 function * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * objnameQ == Q of object ID bindings to use * protomode == TRUE: just generate any H format prototype * FALSE: normal mode *********************************************************************/ static void write_c_init2_fn (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *objnameQ, boolean protomode) { c_define_t *cdef; const xmlChar *modname; int32 indent; modname = ncx_get_modname(mod); indent = cp->indent; /* generate function banner comment */ ses_putstr(scb, FN_BANNER_START); write_identifier(scb, modname, NULL, (const xmlChar *)"init2", cp->isuser); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"SIL init phase 2: " "non-config data structures"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)"Called after running config is loaded"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_RETURN_STATUS); ses_putstr(scb, FN_BANNER_END); /* generate the function prototype lines */ if (protomode) { ses_putstr(scb, (const xmlChar *)"\nextern status_t "); } else { ses_putstr(scb, (const xmlChar *)"\nstatus_t "); } write_identifier(scb, modname, NULL, (const xmlChar *)"init2", cp->isuser); ses_putstr(scb, (const xmlChar *)" (void)"); if (protomode) { ses_putstr(scb, (const xmlChar *)";\n"); return; } /* start function body */ ses_putstr(scb, (const xmlChar *)"\n{"); ses_putstr_indent(scb, (const xmlChar *)"status_t res = NO_ERR;", indent); if (!cp->isuser) { /* generate any cache inits here; top-level data-db objects only! */ for (cdef = (c_define_t *)dlq_firstEntry(objnameQ); cdef != NULL; cdef = (c_define_t *)dlq_nextEntry(cdef)) { if (!obj_is_data_db(cdef->obj) || !obj_is_top(cdef->obj) || obj_is_cli(cdef->obj) || obj_is_abstract(cdef->obj)) { continue; } write_h_iffeature_start(scb, &cdef->obj->iffeatureQ); boolean iffdone = write_c_iffeature_start(scb, &cdef->obj->iffeatureQ, cp, indent); int32 extraindent = (iffdone) ? indent : 0; /* check if this is a top-level config=false node */ if (!obj_get_config_flag(cdef->obj)) { if (obj_is_leafy(cdef->obj) || obj_is_np_container(cdef->obj)) { /* OBJ_TYP_CONTAINER(NP) or OBJ_TYP_LEAF * or OBJ_TYP_LEAFLIST so create a * top-level virtual leaf for this node * TBD: support _mro functions for more * complex types */ ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"res = ", indent+extraindent); ses_putstr(scb, cdef->idstr); ses_putstr(scb, MRO_SUFFIX); ses_putstr(scb, (const xmlChar *)"();"); write_if_res(scb, cp, indent+extraindent); } write_c_iffeature_end(scb, &cdef->obj->iffeatureQ, indent); write_h_iffeature_end(scb, &cdef->obj->iffeatureQ); continue; } /* init the cache value pointer for config=true top nodes */ ses_putchar(scb, '\n'); ses_indent(scb, indent+extraindent); write_c_value_var(scb, obj_get_name(cdef->obj)); ses_putstr(scb, (const xmlChar *)" = agt_init_cache("); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, BAR_MOD, modname, cp->isuser); ses_putchar(scb, ','); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, BAR_NODE, obj_get_name(cdef->obj), cp->isuser); ses_putchar(scb, ','); ses_putstr_indent(scb, (const xmlChar *)"&res);", indent+indent+extraindent); write_if_res(scb, cp, indent+extraindent); write_c_iffeature_end(scb, &cdef->obj->iffeatureQ, indent); write_h_iffeature_end(scb, &cdef->obj->iffeatureQ); } } ses_putchar(scb, '\n'); if (cp->format == NCX_CVTTYP_YC) { /* call the user init2 function */ ses_putstr_indent(scb, (const xmlChar *)"res = ", indent); write_identifier(scb, modname, NULL, (const xmlChar *)"init2", TRUE); ses_putstr(scb, (const xmlChar *)"();"); } else { ses_putstr_indent(scb, (const xmlChar *)"/* put your init2 code here */", indent); } ses_putchar(scb, '\n'); ses_putstr_indent(scb, (const xmlChar *)"return res;", indent); /* end the function */ ses_putstr(scb, (const xmlChar *)"\n} /* "); write_identifier(scb, modname, NULL, (const xmlChar *)"init2", cp->isuser); ses_putstr(scb, (const xmlChar *)" */\n"); } /* write_c_init2_fn */ /******************************************************************** * FUNCTION write_c_register_datacb * * Generate the C code for registering a data node callback fn * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * obj == object to generate init function calls for * cdefQ == Q of ncx_define_t structs to checl * startindent == starting indent amount *********************************************************************/ static void write_c_unregister_datacb (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp, obj_template_t *obj, dlq_hdr_t *cdefQ, int32 startindent) { if (!obj_get_config_flag(obj)) { /* nothing to do now for read-only objects */ return; } int32 indent = startindent; if (obj->objtype != OBJ_TYP_CHOICE && obj->objtype != OBJ_TYP_CASE) { const c_define_t *cdef = find_path_cdefine(cdefQ, obj); if (cdef == NULL) { SET_ERROR(ERR_INTERNAL_VAL); return; } write_h_iffeature_start(scb, &obj->iffeatureQ); boolean iffdone = write_c_iffeature_start(scb, &obj->iffeatureQ, cp, startindent); /* unregister the edit callback */ if (iffdone) { indent += cp->indent; } ses_putstr_indent(scb, (const xmlChar *)"agt_cb_unregister_callbacks(", indent); ses_indent(scb, indent + cp->indent); const xmlChar *modname = ncx_get_modname(mod); write_identifier(scb, modname, BAR_MOD, modname, FALSE); ses_putchar(scb, ','); ses_indent(scb, indent + cp->indent); ses_putstr(scb, (const xmlChar *)"(const xmlChar *)\""); ses_putstr(scb, cdef->valstr); ses_putstr(scb, (const xmlChar *)"\");\n"); } obj_template_t *childobj = obj_first_child(obj); for (; childobj != NULL; childobj = obj_next_child(childobj)) { if (obj_has_name(childobj) && !obj_is_cli(childobj) && !obj_is_abstract(childobj) && obj_is_data_db(childobj)) { write_c_unregister_datacb(scb, mod, cp, childobj, cdefQ, indent); } } if (obj->objtype != OBJ_TYP_CHOICE && obj->objtype != OBJ_TYP_CASE) { write_c_iffeature_end(scb, &obj->iffeatureQ, startindent); write_h_iffeature_end(scb, &obj->iffeatureQ); } } /* write_c_unregister_datadb */ /******************************************************************** * FUNCTION write_c_cleanup_fn * * Generate the C code for the foo_cleanup function * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * objnameQ == Q of object ID bindings to use * protomode == TRUE: just generate any H format prototype * FALSE: normal mode *********************************************************************/ static void write_c_cleanup_fn (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp, dlq_hdr_t *objnameQ, boolean protomode) { const xmlChar *modname = ncx_get_modname(mod); int32 indent = cp->indent; /* generate function banner comment */ ses_putstr(scb, FN_BANNER_START); write_identifier(scb, modname, NULL, (const xmlChar *)"cleanup", cp->isuser); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, (const xmlChar *)" cleanup the server " "instrumentation library"); ses_putstr(scb, FN_BANNER_LN); ses_putstr(scb, FN_BANNER_END); /* generate the function prototype lines */ if (protomode) { ses_putstr(scb, (const xmlChar *)"\nextern void "); } else { ses_putstr(scb, (const xmlChar *)"\nvoid "); } write_identifier(scb, modname, NULL, (const xmlChar *)"cleanup", cp->isuser); ses_putstr(scb, (const xmlChar *)" (void)"); if (protomode) { ses_putstr(scb, (const xmlChar *)";\n"); return; } /* start function body */ ses_putstr(scb, (const xmlChar *)"\n{"); /* function contents */ if (!cp->isuser) { /* cleanup any RPC methods */ obj_template_t *obj; for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_is_rpc(obj) || obj_is_abstract(obj)) { continue; } write_h_iffeature_start(scb, &obj->iffeatureQ); boolean iffdone = write_c_iffeature_start(scb, &obj->iffeatureQ, cp, indent); int32 extraindent = (iffdone) ? indent : 0; /* unregister all callback fns for this RPC function */ ses_putstr_indent(scb, (const xmlChar *)"\nagt_rpc_unregister_method(", indent+extraindent); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, BAR_MOD, modname, FALSE); ses_putchar(scb, ','); ses_indent(scb, indent+indent+extraindent); write_identifier(scb, modname, BAR_NODE, obj_get_name(obj), FALSE); ses_putstr(scb, (const xmlChar *)");"); write_c_iffeature_end(scb, &obj->iffeatureQ, indent); write_h_iffeature_end(scb, &obj->iffeatureQ); } /* cleanup any registered callbacks */ for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (obj_has_name(obj) && obj_is_data_db(obj) && !obj_is_cli(obj) && !obj_is_abstract(obj)) { write_c_unregister_datacb(scb, mod, cp, obj, objnameQ, indent); } } } if (cp->format == NCX_CVTTYP_YC) { /* call the user cleanup function */ ses_indent(scb, indent); write_identifier(scb, modname, NULL, (const xmlChar *)"cleanup", TRUE); ses_putstr(scb, (const xmlChar *)"();\n"); } else { ses_putstr_indent(scb, (const xmlChar *)"/* put your cleanup code " "here */\n", indent); } /* end the function */ ses_putstr(scb, (const xmlChar *)"\n} /* "); write_identifier(scb, modname, NULL, (const xmlChar *)"cleanup", cp->isuser); ses_putstr(scb, (const xmlChar *)" */\n"); } /* write_c_cleanup_fn */ /******************************************************************** * FUNCTION write_c_file * * Generate the C file for the module * * INPUTS: * scb == session control block to use for writing * mod == module in progress * cp == conversion parameters to use * * RETURNS: * status *********************************************************************/ static status_t write_c_file (ses_cb_t *scb, ncx_module_t *mod, const yangdump_cvtparms_t *cp) { yang_node_t *node; dlq_hdr_t objnameQ; status_t res; dlq_createSQue(&objnameQ); res = NO_ERR; /* name strings for callback fns for objects */ res = save_all_c_objects(mod, cp, &objnameQ, C_MODE_CALLBACK); if (res == NO_ERR) { /* file header, meta-data, static data decls */ write_c_header(scb, mod, cp); write_c_includes(scb, mod, cp); write_c_static_vars(scb, mod, cp); write_c_init_static_vars_fn(scb, mod, cp); /* static functions */ write_c_objects(scb, mod, cp, &mod->datadefQ, &objnameQ, FALSE); if (cp->unified && mod->ismod) { for (node = (yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL && res == NO_ERR; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { write_c_objects(scb, node->submod, cp, &node->submod->datadefQ, &objnameQ, FALSE); } } } if (cp->format != NCX_CVTTYP_YC) { write_c_rpcs(scb, mod, cp, &objnameQ, FALSE); } /* external functions */ if (cp->format != NCX_CVTTYP_YC) { write_c_notifs(scb, mod, cp, &objnameQ, FALSE); } write_c_init_fn(scb, mod, cp, &objnameQ, FALSE); write_c_init2_fn(scb, mod, cp, &objnameQ, FALSE); write_c_cleanup_fn(scb, mod, cp, &objnameQ, FALSE); /* end comment */ write_c_footer(scb, mod, cp); } clean_cdefineQ(&objnameQ); return res; } /* write_c_file */ /********* E X P O R T E D F U N C T I O N S **************/ /******************************************************************** * FUNCTION c_convert_module * * Generate the SIL C code for the specified module(s) * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * * RETURNS: * status *********************************************************************/ status_t c_convert_module (yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb) { ncx_module_t *mod = pcb->top; #ifdef DEBUG if (!pcb || !cp || !scb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* the module should already be parsed and loaded */ if (!mod) { return SET_ERROR(ERR_NCX_MOD_NOT_FOUND); } /* do not generate C code for obsolete objects */ ncx_delete_all_obsolete_objects(); return write_c_file(scb, mod, cp); } /* c_convert_module */ /******************************************************************** * FUNCTION c_write_fn_prototypes * * Generate the SIL H code for the external function definitions * in the specified module, already parsed and H file generation * is in progress * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * objnameQ == Q of c_define_t mapping structs to use * *********************************************************************/ void c_write_fn_prototypes (ncx_module_t *mod, const yangdump_cvtparms_t *cp, ses_cb_t *scb, dlq_hdr_t *objnameQ) { yang_node_t *node; #ifdef DEBUG if (!mod || !cp || !scb) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (cp->isuser) { write_c_objects(scb, mod, cp, &mod->datadefQ, objnameQ, TRUE); write_c_rpcs(scb, mod, cp, objnameQ, TRUE); } if (cp->format == NCX_CVTTYP_H || cp->format == NCX_CVTTYP_UH) { write_c_notifs(scb, mod, cp, objnameQ, TRUE); } if (cp->unified && mod->ismod) { for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { if (cp->isuser) { write_c_objects(scb, node->submod, cp, &node->submod->datadefQ, objnameQ, TRUE); write_c_rpcs(scb, node->submod, cp, objnameQ, TRUE); } else { write_c_notifs(scb, node->submod, cp, objnameQ, TRUE); } } } } if (mod->ismod) { write_c_init_fn(scb, mod, cp, objnameQ, TRUE); write_c_init2_fn(scb, mod, cp, objnameQ, TRUE); write_c_cleanup_fn(scb, mod, cp, objnameQ, TRUE); } } /* c_write_fn_prototypes */ /* END file c.c */ yuma123_2.14/netconf/src/ncx/0000775000175000017500000000000014770023131016136 5ustar vladimirvladimiryuma123_2.14/netconf/src/ncx/rpc_err.h0000664000175000017500000004624114770023131017752 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_rpc_err #define _H_rpc_err /* FILE: rpc_err.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF protocol standard error definitions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 06-apr-05 abb Begun. */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /*** From draft-ietf-netconf-prot-12.txt: Tag: in-use Error-type: protocol, application Severity: error Error-info: none Description: The request requires a resource that already in use. Tag: invalid-value Error-type: protocol, application Severity: error Error-info: none Description: The request specifies an unacceptable value for one or more parameters. Tag: too-big Error-type: transport, rpc, protocol, application Severity: error Error-info: none Description: The request or response (that would be generated) is too large for the implementation to handle. Tag: missing-attribute Error-type: rpc, protocol, application Severity: error Error-info: : name of the missing attribute : name of the element that should contain the missing attribute Description: An expected attribute is missing Tag: bad-attribute Error-type: rpc, protocol, application Severity: error Error-info: : name of the attribute w/ bad value : name of the element that contains the attribute with the bad value Description: An attribute value is not correct; e.g., wrong type, out of range, pattern mismatch Tag: unknown-attribute Error-type: rpc, protocol, application Severity: error Error-info: : name of the unexpected attribute : name of the element that contains the unexpected attribute Description: An unexpected attribute is present Tag: missing-element Error-type: rpc, protocol, application Severity: error Error-info: : name of the missing element Description: An expected element is missing Tag: bad-element Error-type: rpc, protocol, application Severity: error Error-info: : name of the element w/ bad value Description: An element value is not correct; e.g., wrong type, out of range, pattern mismatch Tag: unknown-element Error-type: rpc, protocol, application Severity: error Error-info: : name of the unexpected element Description: An unexpected element is present Tag: unknown-namespace Error-type: rpc, protocol, application Severity: error Error-info: : name of the element that contains the unexpected namespace : name of the unexpected namespace Description: An unexpected namespace is present Tag: access-denied Error-type: rpc, protocol, application Severity: error Error-info: none Description: Access to the requested RPC, protocol operation, or data model is denied because authorization failed Tag: lock-denied Error-type: protocol Severity: error Error-info: : session ID of session holding the requested lock, or zero to indicate a non-NETCONF entity holds the lock Description: Access to the requested lock is denied because the lock is currently held by another entity Tag: resource-denied Error-type: transport, rpc, protocol, application Severity: error Error-info: none Description: Request could not be completed because of insufficient resources Tag: rollback-failed Error-type: protocol, application Severity: error Error-info: none Description: Request to rollback some configuration change (via rollback-on-error or discard-changes operations) was not completed for some reason. Tag: data-exists Error-type: application Severity: error Error-info: none Description: Request could not be completed because the relevant data model content already exists. For example, a 'create' operation was attempted on data which already exists. Tag: data-missing Error-type: application Severity: error Error-info: none Description: Request could not be completed because the relevant data model content does not exist. For example, a 'replace' or 'delete' operation was attempted on data which does not exist. Tag: operation-not-supported Error-type: rpc, protocol, application Severity: error Error-info: none Description: Request could not be completed because the requested operation is not supported by this implementation. Tag: operation-failed Error-type: rpc, protocol, application Severity: error Error-info: none Description: Request could not be completed because the requested operation failed for some reason not covered by any other error condition. Tag: partial-operation Error-type: application Severity: error Error-info: : identifies an element in the data model for which the requested operation has been completed for that node and all its child nodes. This element can appear zero or more times in the container. : identifies an element in the data model for which the requested operation has failed for that node and all its child nodes. This element can appear zero or more times in the container. : identifies an element in the data model for which the requested operation was not attempted for that node and all its child nodes. This element can appear zero or more times in the container. Description: Some part of the requested operation failed or was not attempted for some reason. Full cleanup has not been performed (e.g., rollback not supported) by the server. The error-info container is used to identify which portions of the application data model content for which the requested operation has succeeded (), failed (), or not attempted (). [New in NETCONF base:1.1; do not send to NETCONF:base:1.0 sessions] error-tag: malformed-message error-type: rpc error-severity: error error-info: none Description: A message could not be handled because it failed to be parsed correctly. For example, the message is not well-formed XML or it uses an invalid character set. This error-tag is new in :base:1.1 and MUST NOT be sent to old clients. ***/ #ifndef _H_dlq #include "dlq.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /*** YANG standard error-app-tag values ***/ /* 13.1 */ #define RPC_ERR_APPTAG_UNIQUE_FAILED (const xmlChar *)"data-not-unique" /* 13.2 */ #define RPC_ERR_APPTAG_MAX_ELEMS (const xmlChar *)"too-many-elements" /* 13.3 */ #define RPC_ERR_APPTAG_MIN_ELEMS (const xmlChar *)"too-few-elements" /* 13.4 */ #define RPC_ERR_APPTAG_MUST (const xmlChar *)"must-violation" /* 13.5 */ #define RPC_ERR_APPTAG_INSTANCE_REQ (const xmlChar *)"instance-required" /* 13.6 */ #define RPC_ERR_APPTAG_CHOICE (const xmlChar *)"missing-choice" /* 13.7 */ #define RPC_ERR_APPTAG_INSERT (const xmlChar *)"missing-instance" #define RPC_ERR_APPTAG_RANGE (const xmlChar *)"not-in-range" #define RPC_ERR_APPTAG_VALUE_SET (const xmlChar *)"not-in-value-set" #define RPC_ERR_APPTAG_PATTERN (const xmlChar *)"pattern-test-failed" #define RPC_ERR_APPTAG_DATA_REST \ (const xmlChar *)"data-restriction-violation" #define RPC_ERR_APPTAG_DATA_NOT_UNIQUE \ (const xmlChar *)"data-not-unique" /* ietf-netconf-state:get-schema */ /* ietf-netconf-partial-lock:partial-lock */ #define RPC_ERR_APPTAG_NO_MATCHES (const xmlChar *)"no-matches" /* ietf-netconf-partial-lock:partial-lock */ #define RPC_ERR_APPTAG_NOT_NODESET (const xmlChar *)"not-a-node-set" /* ietf-netconf-partial-lock:partial-lock */ #define RPC_ERR_APPTAG_LOCKED (const xmlChar *)"locked" /* ietf-netconf-partial-lock:partial-lock */ #define RPC_ERR_APPTAG_COMMIT (const xmlChar *)\ "outstanding-confirmed-commit" /*** netconfcentral errror-app-tag values ***/ #define RPC_ERR_APPTAG_GEN_WARNING (const xmlChar *)"general-warning" #define RPC_ERR_APPTAG_GEN_ERROR (const xmlChar *)"general-error" #define RPC_ERR_APPTAG_INT_ERROR (const xmlChar *)"internal-error" #define RPC_ERR_APPTAG_IO_ERROR (const xmlChar *)"io-error" #define RPC_ERR_APPTAG_MALLOC_ERROR (const xmlChar *)"malloc-error" #define RPC_ERR_APPTAG_LIMIT_REACHED (const xmlChar *)"limit-reached" #define RPC_ERR_APPTAG_LIBXML2_ERROR (const xmlChar *)"libxml2-error" #define RPC_ERR_APPTAG_SQL_ERROR (const xmlChar *)"sql-error" #define RPC_ERR_APPTAG_SSH_ERROR (const xmlChar *)"ssh-error" #define RPC_ERR_APPTAG_BEEP_ERROR (const xmlChar *)"beep-error" #define RPC_ERR_APPTAG_HTML_ERROR (const xmlChar *)"html-error" #define RPC_ERR_APPTAG_DATA_INCOMPLETE (const xmlChar *)"data-incomplete" #define RPC_ERR_APPTAG_DATA_INVALID (const xmlChar *)"data-invalid" #define RPC_ERR_APPTAG_DUPLICATE_ERROR (const xmlChar *)"duplicate-error" #define RPC_ERR_APPTAG_RESOURCE_IN_USE (const xmlChar *)"resource-in-use" #define RPC_ERR_APPTAG_NO_ACCESS (const xmlChar *)"no-access" #define RPC_ERR_APPTAG_RECOVER_FAILED (const xmlChar *)"recover-failed" #define RPC_ERR_APPTAG_NO_SUPPORT (const xmlChar *)"no-support" #define RPC_ERR_LAST_ERROR RPC_ERR_MALFORMED_MESSAGE /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* enumerations for NETCONF standard errors */ typedef enum rpc_err_t_ { RPC_ERR_NONE, RPC_ERR_IN_USE, RPC_ERR_INVALID_VALUE, RPC_ERR_TOO_BIG, RPC_ERR_MISSING_ATTRIBUTE, RPC_ERR_BAD_ATTRIBUTE, RPC_ERR_UNKNOWN_ATTRIBUTE, RPC_ERR_MISSING_ELEMENT, RPC_ERR_BAD_ELEMENT, RPC_ERR_UNKNOWN_ELEMENT, RPC_ERR_UNKNOWN_NAMESPACE, RPC_ERR_ACCESS_DENIED, RPC_ERR_LOCK_DENIED, RPC_ERR_RESOURCE_DENIED, RPC_ERR_ROLLBACK_FAILED, RPC_ERR_DATA_EXISTS, RPC_ERR_DATA_MISSING, RPC_ERR_OPERATION_NOT_SUPPORTED, RPC_ERR_OPERATION_FAILED, RPC_ERR_PARTIAL_OPERATION, /* deprecated; not used */ RPC_ERR_MALFORMED_MESSAGE } rpc_err_t; /* enumerations for NETCONF standard error severities */ typedef enum rpc_err_sev_t_ { RPC_ERR_SEV_NONE, RPC_ERR_SEV_WARNING, RPC_ERR_SEV_ERROR } rpc_err_sev_t; /* one error-info sub-element */ typedef struct rpc_err_info_ { dlq_hdr_t qhdr; xmlns_id_t name_nsid; const xmlChar *name; xmlChar *dname; boolean isqname; /* val_nsid + v.strval == QName */ ncx_btype_t val_btype; uint8 val_digits; /* used for decimal64 only */ xmlns_id_t val_nsid; xmlChar *badns; /* if val_nsid INVALID namespace */ xmlChar *dval; union { const xmlChar *strval; /* for string error content */ ncx_num_t numval; /* for number error content */ void *cpxval; /* val_value_t */ } v; } rpc_err_info_t; typedef struct rpc_err_rec_t_ { dlq_hdr_t error_qhdr; status_t error_res; ncx_layer_t error_type; rpc_err_t error_id; rpc_err_sev_t error_severity; const xmlChar *error_tag; const xmlChar *error_app_tag; xmlChar *error_path; xmlChar *error_message; const xmlChar *error_message_lang; dlq_hdr_t error_info; /* Q of rpc_err_info_t */ } rpc_err_rec_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION rpc_err_get_errtag * * Get the RPC error-tag for an rpc_err_t enumeration * * INPUTS: * errid == rpc error enum to convert to a string * * RETURNS: * string for the specified error-tag enum *********************************************************************/ extern const xmlChar * rpc_err_get_errtag (rpc_err_t errid); /******************************************************************** * FUNCTION rpc_err_get_errtag_enum * * Get the RPC error-tag enum for an error-tag string * * INPUTS: * errtag == error-tag string to check * * RETURNS: * enum for this error-tag *********************************************************************/ extern rpc_err_t rpc_err_get_errtag_enum (const xmlChar *errtag); /******************************************************************** * FUNCTION rpc_err_new_record * * Malloc and init an rpc_err_rec_t struct * * RETURNS: * malloced error record or NULL if memory error *********************************************************************/ extern rpc_err_rec_t * rpc_err_new_record (void); /******************************************************************** * FUNCTION rpc_err_init_record * * Init an rpc_err_rec_t struct * * INPUTS: * err == rpc_err_rec_t struct to init * RETURNS: * none *********************************************************************/ extern void rpc_err_init_record (rpc_err_rec_t *err); /******************************************************************** * FUNCTION rpc_err_free_record * * Clean and free an rpc_err_rec_t struct * * INPUTS: * err == rpc_err_rec_t struct to clean and free * RETURNS: * none *********************************************************************/ extern void rpc_err_free_record (rpc_err_rec_t *err); /******************************************************************** * FUNCTION rpc_err_clean_record * * Clean an rpc_err_rec_t struct * * INPUTS: * err == rpc_err_rec_t struct to clean * RETURNS: * none *********************************************************************/ extern void rpc_err_clean_record (rpc_err_rec_t *err); /******************************************************************** * FUNCTION rpc_err_new_info * * Malloc and init an rpc_err_info_t struct * * RETURNS: * malloced error-info record, or NULL if memory error *********************************************************************/ extern rpc_err_info_t * rpc_err_new_info (void); /******************************************************************** * FUNCTION rpc_err_free_info * * Clean and free an rpc_err_info_t struct * * INPUTS: * errinfo == rpc_err_info_t struct to clean and free * RETURNS: * none *********************************************************************/ extern void rpc_err_free_info (rpc_err_info_t *errinfo); /******************************************************************** * FUNCTION rpc_err_dump_errors * * Dump the error messages in the RPC message error Q * * INPUTS: * msg == rpc_msg_t struct to check for errors * *********************************************************************/ extern void rpc_err_dump_errors (const rpc_msg_t *msg); /******************************************************************** * FUNCTION rpc_err_get_severity * * Translate an rpc_err_sev_t to a string * * INPUTS: * sev == rpc_err_sev_t enum to translate * * RETURNS: * const pointer to the enum string *********************************************************************/ extern const xmlChar * rpc_err_get_severity (rpc_err_sev_t sev); /******************************************************************** * FUNCTION rpc_err_clean_errQ * * Clean all the entries from a Q of rpc_err_rec_t * * INPUTS: * errQ == Q of rpc_err_rec_t to clean * RETURNS: * none *********************************************************************/ extern void rpc_err_clean_errQ (dlq_hdr_t *errQ); /******************************************************************** * FUNCTION rpc_err_any_errors * * Check if there are any errors in the RPC message error Q * * INPUTS: * msg == rpc_msg_t struct to check for errors * * RETURNS: * TRUE if any errors recorded; FALSE if none *********************************************************************/ extern boolean rpc_err_any_errors (const rpc_msg_t *msg); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_rpc_err */ yuma123_2.14/netconf/src/ncx/json_wr.h0000664000175000017500000001100714770023131017767 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_json_wr #define _H_json_wr /* FILE: json_wr.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* JSON Write functions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 01-sep-11 abb Begun; */ #include #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION json_wr_full_check_val * * generate entire val_value_t *w/filter) * Write an entire val_value_t out as XML, including the top level * Using an optional testfn to filter output * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * startindent == start indent amount if indent enabled * testcb == callback function to use, NULL if not used * * RETURNS: * status *********************************************************************/ extern status_t json_wr_full_check_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 startindent, val_nodetest_fn_t testfn); /******************************************************************** * FUNCTION json_wr_check_open_file * * Write the specified value to an open FILE in JSON format * * INPUTS: * fp == open FILE control block * val == value for output * startindent == starting indent point * indent == indent amount (0..9 spaces) * testfn == callback test function to use * * RETURNS: * status *********************************************************************/ extern status_t json_wr_check_open_file (FILE *fp, val_value_t *val, int32 startindent, int32 indent, val_nodetest_fn_t testfn); /******************************************************************** * FUNCTION json_wr_check_file * * Write the specified value to a FILE in JSON format * * INPUTS: * filespec == exact path of filename to open * val == value for output * startindent == starting indent point * indent == indent amount (0..9 spaces) * testfn == callback test function to use * * RETURNS: * status *********************************************************************/ extern status_t json_wr_check_file (const xmlChar *filespec, val_value_t *val, int32 startindent, int32 indent, val_nodetest_fn_t testfn); /******************************************************************** * FUNCTION json_wr_file * * Write the specified value to a FILE in JSON format * * INPUTS: * filespec == exact path of filename to open * val == value for output * startindent == starting indent point * indent == indent amount (0..9 spaces) * * RETURNS: * status *********************************************************************/ extern status_t json_wr_file (const xmlChar *filespec, val_value_t *val, int32 startindent, int32 indent); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_json_wr */ yuma123_2.14/netconf/src/ncx/xpath_yang.c0000664000175000017500000025524414770023131020460 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xpath_yang.c Schema and data model Xpath search support for YANG specific 'XPath-like' expressions. This module provides validation only. Once this module validates that the YANG-specific syntax is followed, then xpath1.c can be used to evaluate the expression and generate a node-set result. The following YANG features are supported: - augment target - refine target - leafref path target - key attribute for insert operation ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13nov08 abb begun; split out from xpath.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include "procdefs.h" #include "ncx.h" #include "ncxconst.h" #include "ncxtypes.h" #include "obj.h" #include "tk.h" #include "typ.h" #include "val.h" #include "val123.h" #include "val_util.h" #include "xmlns.h" #include "xpath.h" #include "xpath_yang.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * Utility function for selecting the object template to use. * * \param pcb the parser control block * \param res the status of the selection * \return useobj the selected object template ********************************************************************/ static obj_template_t** select_pcb_object( xpath_pcb_t* pcb, status_t* res ) { switch (pcb->curmode) { case XP_CM_TARGET: *res = NO_ERR; return &pcb->targobj; case XP_CM_ALT: *res = NO_ERR; return &pcb->altobj; case XP_CM_KEYVAR: *res = NO_ERR; return &pcb->varobj; default: *res = ERR_INTERNAL_VAL; return NULL; } } /******************************************************************** * Utility function for conditionally calling ncx_print_errormsg * depending on pcb->logerrors. * * \param pcb the parser control block * \param mod the module * \param res the status error * \return the status error ********************************************************************/ static status_t pcb_print_errormsg( xpath_pcb_t* pcb, ncx_module_t *mod, status_t res ) { if ( pcb->logerrors ) { ncx_print_errormsg( pcb->tkc, mod, res ); } return res; } /******************************************************************** * Utility function for conditionally calling log_error * depending on pcb->logerrors. * * \param pcb the parser control block * \param fmtstr the error message format string ********************************************************************/ static void pcb_log_error( xpath_pcb_t* pcb, const char* fmtstr, ... ) { if ( pcb->logerrors ) { va_list args; va_start(args, fmtstr); vlog_error( fmtstr, args ); va_end( args ); } } /******************************************************************** * Utility function for conditionally calling log_error * depending on pcb->logerrors. * * \param pcb the parser control block * \param mod the module * \param res the status error * \param fmtstr the error message format string * \return the status error ********************************************************************/ static status_t pcb_log_error_msg( xpath_pcb_t* pcb, ncx_module_t *mod, status_t res, const char* fmtstr, ... ) { if ( pcb->logerrors ) { ncx_print_errormsg( pcb->tkc, mod, res ); va_list args; va_start(args, fmtstr); vlog_error( fmtstr, args ); va_end( args ); } return res; } /******************************************************************** * Utility function for getting the target module for the nsid. * * \param nsid the nsid * \return the corresponding module. ********************************************************************/ static ncx_module_t* get_target_module_for_nsid( xmlns_id_t nsid ) { const xmlChar* modname = xmlns_get_module(nsid); if (modname) { return ncx_find_module(modname, NULL); } return NULL; } /******************************************************************** * Utility function for testing if a target object is a leaf list * * \param pcb the parser control block in progress| * \return true if the target object is a leaf list *********************************************************************/ static bool targ_obj_is_leaflist( xpath_pcb_t* pcb ) { return (pcb->targobj && pcb->targobj->objtype == OBJ_TYP_LEAF_LIST); } static ncx_module_t* find_module(xmlns_id_t nsid) { ncx_module_t* targmod = NULL; if (nsid) { dlq_hdr_t *temp_modQ = ncx_get_temp_modQ(); if (nsid == xmlns_nc_id() || !temp_modQ ) { targmod = get_target_module_for_nsid( nsid ); } else if ( temp_modQ ) { /* try the temp Q first */ targmod = ncx_find_module_que_nsid(temp_modQ, nsid); if ( !targmod ) { targmod = get_target_module_for_nsid( nsid ); } } } return targmod; } /******************************************************************** * Utility function for getting the target module for selecting the * target module. * * \param pcb the parser control block in progress * \param prefix the prefix value used if any * \param nsid the namespace id for the module * \param laxnamespaces flag indicating if lax namespaces are being used. * \return the corresponding module. ********************************************************************/ static ncx_module_t* select_target_module( xpath_pcb_t* pcb, const xmlChar* prefix, xmlns_id_t nsid, boolean laxnamespaces, status_t* res ) { *res = NO_ERR; ncx_module_t* targmod = NULL; if( pcb->source == XP_SRC_XML) { if(nsid) { targmod=find_module(nsid); if ( !targmod ) { *res = ERR_NCX_DEF_NOT_FOUND; pcb_log_error( pcb, "\nError: module not found in expr '%s'", pcb->exprstr ); } } else if (!laxnamespaces) { *res = ERR_NCX_UNKNOWN_NAMESPACE; pcb_log_error( pcb, "\nError: no namespace in expr '%s'", pcb->exprstr); } } else { ncx_module_t* imports_mod; if((prefix==NULL && nsid==0) && pcb->obj && obj_is_root(pcb->obj)) { return NULL; } else if(pcb->obj && NULL!=obj123_get_top_uses(pcb->obj)) { /* * it is obj from expanded groupings in uses and belongs * to the same module as the top uses in the * chain of ancestor nodes. */ obj_template_t* top_uses_obj; typ_def_t* named_typdef; typ_def_t* first_named_typdef; if(pcb->obj->objtype==OBJ_TYP_LEAF && pcb->obj->def.leaf->typdef->tclass==NCX_CL_NAMED) { named_typdef = pcb->obj->def.leaf->typdef; } else if (pcb->obj->objtype==OBJ_TYP_LEAF_LIST && pcb->obj->def.leaflist->typdef->tclass==NCX_CL_NAMED) { named_typdef = pcb->obj->def.leaflist->typdef; } else { named_typdef=NULL; } if(named_typdef) { first_named_typdef=typ123_get_first_named_typdef(named_typdef); imports_mod = first_named_typdef->def.named.typ->tkerr.mod; } else { top_uses_obj = obj123_get_top_uses(pcb->obj); imports_mod = top_uses_obj->tkerr.mod; } } else { imports_mod = pcb->tkerr.mod; assert(imports_mod==NULL || pcb->obj==NULL || pcb->obj->mod==NULL || pcb->obj->mod==pcb->tkerr.mod); } if (imports_mod==NULL) { if(nsid) { /* brute force since not context module is available */ targmod=find_module(nsid); if(targmod==NULL) { *res = ERR_NCX_UNKNOWN_NAMESPACE; pcb_log_error( pcb, "\nError: unknown namespace in expr '%s'", pcb->exprstr); } } else { /* target module can't be determined from the provided input */ *res = NO_ERR; targmod=NULL; } return targmod; } else { *res = xpath_get_curmod_from_prefix( prefix, imports_mod, &targmod); if(*res==NO_ERR) { return targmod; } } if ( !prefix && laxnamespaces) { *res = NO_ERR; } else { pcb_log_error( pcb, "\nError: Module for prefix '%s' not found in %s should check %s", (prefix) ? prefix : EMPTY_STRING, pcb->tkerr.mod->name, pcb->obj?(char*)get_target_module_for_nsid(pcb->obj->nsid)->name:" pcb->obj is NULL"); } } return targmod; } /******************************************************************** * Utility function for finding any object based on the node name. * * \param nodename the name of the object to find. * \return the corresponding object template. ********************************************************************/ static obj_template_t* find_any_object( const xmlChar* nodename ) { obj_template_t *foundobj = NULL; dlq_hdr_t *temp_modQ = ncx_get_temp_modQ(); if ( temp_modQ ) { foundobj = ncx_find_any_object_que( temp_modQ, nodename ); } if ( !foundobj ) { foundobj = ncx_find_any_object(nodename); } return foundobj; } /******************************************************************** * Utility function for finding any object from root, using the node name * and nsid * * \param nodename the name of the object to find. * \param nsid the nsid * \return the corresponding object template. ********************************************************************/ static obj_template_t* find_object_from_root( const xmlChar* nodename, xmlns_id_t nsid ) { obj_template_t *foundobj = find_any_object( nodename ); if ( foundobj && nsid && obj_get_nsid(foundobj) != nsid ) { /* did not match the specified namespace ID */ foundobj = NULL; } return foundobj; } static obj_template_t* ensure_found_object_is_rpc( obj_template_t* foundobj ) { if (foundobj && foundobj->objtype == OBJ_TYP_RPC) { return foundobj; } return NULL; } /******************************************************************** * Utility function for finding all object matches even in cases where * neither the prefix nor the nsid are specified. * * \param pcb the parser control block in progress * \param prefix the prefix value used if any * \param nsid the namespace id for the module * \param nodename the name of the node to find * \param targobj the target module * \param matched_objs user provided buffer for storing matched objs * \param matched_objs_limit max count of matched objs, when 0 only count * \return the number of matching objects found ********************************************************************/ static unsigned int find_all_object_matches( xpath_pcb_t* pcb, const xmlChar* prefix, xmlns_id_t nsid, const xmlChar* nodename, obj_template_t** matched_objs, unsigned int matched_objs_limit) { obj_template_t* foundobj = NULL; unsigned int matched_cnt; if ( !pcb->targobj ) { pcb->targobj = pcb->obj; } if ( !pcb->targobj ) { return 0; } if (nsid==0 && prefix==NULL && pcb->targobj) { if(obj_is_root(pcb->targobj)) { unsigned int matched_cnt_temp = 0; unsigned int matched_cnt_cur = 0; dlq_hdr_t *modQ = ncx_get_temp_modQ(); if(modQ!=NULL) { /* check temp context - e.g. mgr/yangcli modules */ matched_cnt_temp = ncx123_find_all_homonym_top_objs(modQ, nodename, matched_objs, matched_objs_limit); } modQ=ncx_get_cur_modQ(); matched_cnt_cur=ncx123_find_all_homonym_top_objs(modQ, nodename, matched_objs?matched_objs+matched_cnt_temp:NULL, matched_objs?matched_objs_limit-matched_cnt_temp:0); matched_cnt = matched_cnt_cur + matched_cnt_temp; // remove duplicated mod:name entries .e.g /nacm - temp modules take precedence if(matched_objs && matched_cnt_cur>0 && matched_cnt_temp>0) { unsigned int matched_cnt_unique = matched_cnt_temp; unsigned int i,j; for(i=0;itargobj, nodename, matched_objs, matched_objs_limit); } return matched_cnt; } else if(obj_is_root(pcb->targobj)) { foundobj = find_object_from_root( nodename, nsid ); } else if ( obj_get_nsid(pcb->targobj) == xmlns_nc_id() && ( !xml_strcmp(obj_get_name(pcb->targobj), NCX_EL_RPC)) ) { /* find an RPC method with the nodename */ if ( prefix && nsid == 0) { nsid = xmlns_find_ns_by_prefix(prefix); } if ( !nsid ) { foundobj = find_any_object( nodename ); } else { ncx_module_t* targmod = NULL; dlq_hdr_t *temp_modQ = ncx_get_temp_modQ(); if (temp_modQ) { targmod = ncx_find_module_que_nsid(temp_modQ, nsid); } else { targmod = get_target_module_for_nsid( nsid ); } if ( targmod ) { foundobj = ncx_find_object( targmod, nodename); } } foundobj = ensure_found_object_is_rpc( foundobj ); } else { foundobj = obj_find_child( pcb->targobj, obj_get_mod_name(pcb->targobj), nodename); if ( !foundobj && !prefix ) { foundobj = obj_find_child( pcb->targobj, NULL, nodename); } } if(foundobj) { if(matched_objs_limit>0 && matched_objs!=NULL) { matched_objs[0]=foundobj; } return 1; } else { return 0; } } /******************************************************************** * Get the object identifier based on the ueeobj * * \param useobj the object being used * \param targnod the target module * \param nodename the node name to find (must be present) * \param laxnamespaces flag indicating if lax namespaces are being used. * \return status *********************************************************************/ static obj_template_t* find_object_from_use_obj( obj_template_t* useobj, ncx_module_t* targmod, const xmlChar* nodename, boolean laxnamespaces ) { obj_template_t* foundobj = NULL; if (obj_is_root(useobj)) { if ( targmod ) { foundobj = obj_find_template_top( targmod, ncx_get_modname(targmod), nodename); } } else if ( obj_get_nsid(useobj) == xmlns_nc_id() && !xml_strcmp(obj_get_name(useobj), NCX_EL_RPC)) { /* get the rpc template ode for this object */ if ( targmod ) { foundobj = obj_find_template_top(targmod, ncx_get_modname(targmod), nodename); foundobj = ensure_found_object_is_rpc( foundobj ); } } else { /* get child node of this object */ obj_template_t* parentobj; if(useobj->objtype == OBJ_TYP_AUGMENT) { parentobj = useobj->def.augment->targobj; } else { parentobj = useobj; } if ( targmod ) { foundobj = obj_find_child( parentobj, ncx_get_modname(targmod), nodename); } else if ( !foundobj && laxnamespaces ) { foundobj = obj_find_child(parentobj, NULL, nodename); } } return foundobj; } static char* matched_objs_to_str(obj_template_t** matched_objs, unsigned int matched_cnt) { char* buf; unsigned int i; unsigned int len=0; for(i=0;i0) { len+=strlen(", "); } len+=strlen(get_target_module_for_nsid(matched_objs[i]->nsid)->name); len+=strlen(":"); len+=strlen(obj_get_name(matched_objs[i])); } buf=malloc(len+1); buf[0]=0; for(i=0;i0) { strcat(buf,", "); } strcat(buf,get_target_module_for_nsid(matched_objs[i]->nsid)->name); strcat(buf,":"); strcat(buf,obj_get_name(matched_objs[i])); } return buf; } /******************************************************************** * Get the object identifier associated with * QName in an leafref XPath expression * * \note Error messages are printed by this function!! * Do not duplicate error messages upon error return * * \param pcb the parser control block in progress * \param prefix the prefix value used if any * \param nsid the namespace id for the module * \param nodename the node name to find (must be present) * \return status *********************************************************************/ static status_t set_next_objnode( xpath_pcb_t *pcb, const xmlChar *prefix, xmlns_id_t nsid, const xmlChar *nodename ) { /* TBD: change this to an agt_profile 'namespaces' */ boolean laxnamespaces = TRUE; /* set the pcb target to get the result */ status_t res = NO_ERR; obj_template_t** useobj = select_pcb_object( pcb, &res ); if ( res != NO_ERR ) { return pcb_print_errormsg( pcb, pcb->tkerr.mod, res ); } /* get the module from the NSID or the prefix */ ncx_module_t* targmod = select_target_module( pcb, prefix, nsid, laxnamespaces, &res ); if ( res != NO_ERR ) { return pcb_print_errormsg( pcb, pcb->tkerr.mod, res ); } /* check if no NSID or prefix used: instead of rejecting * the request, check any top-level object, if allowed * by the laxnamespaces parameter */ obj_template_t *foundobj = NULL; if ( !targmod && laxnamespaces && ( !nsid || !prefix )) { unsigned int matched_cnt; obj_template_t** matched_objs; matched_cnt = find_all_object_matches( pcb, prefix, nsid, nodename, NULL, 0); if ( matched_cnt==0 ) { return pcb_log_error_msg(pcb, pcb->tkerr.mod, ERR_NCX_DEF_NOT_FOUND, "\nError: No object match for node '%s' in expr '%s'", nodename, pcb->exprstr); } matched_objs=(obj_template_t**)malloc(sizeof(obj_template_t*)*matched_cnt); matched_cnt = find_all_object_matches( pcb, prefix, nsid, nodename, matched_objs, matched_cnt); if(matched_cnt>1) { char* matched_objs_str; matched_objs_str=matched_objs_to_str(matched_objs, matched_cnt); res = pcb_log_error_msg(pcb, pcb->tkerr.mod, ERR_NCX_MULTIPLE_MATCHES, "\nError: Multiple object matches for node '%s' in expr '%s': %s", nodename, pcb->exprstr, matched_objs_str); free(matched_objs); free(matched_objs_str); return res; } assert(matched_cnt==1); foundobj=matched_objs[0]; free(matched_objs); } else if ( *useobj ) { foundobj = find_object_from_use_obj( *useobj, targmod, nodename, laxnamespaces ); } else if ( pcb->curmode == XP_CM_KEYVAR || pcb->curmode == XP_CM_ALT ) { /* setting object for the first time, get child node of the current * context object */ if ( pcb->targobj && targmod ) { if(pcb->targobj->objtype != OBJ_TYP_LEAF_LIST) { foundobj = obj_find_child( pcb->targobj, ncx_get_modname(targmod), nodename ); } else { assert(0==strcmp(obj_get_name(pcb->targobj),nodename)); foundobj = pcb->targobj; } } } else if ( ( pcb->flags & XP_FL_ABSPATH ) ) { /* setting object for the first time get top-level object from * object module */ if ( targmod ) { foundobj = obj_find_template_top(targmod, ncx_get_modname(targmod), nodename); } } else { /* setting object for the first time but the context node is a leaf, * so there is no possible child node of the start object */ log_debug2( "\n%s setting object for the first time but the context " "node is a leaf, so there is no possible child node " "of the start object", __func__ ); } if ( !foundobj ) { if (prefix) { pcb_log_error( pcb, "\nError: object not found '%s:%s'", prefix, nodename); } else { pcb_log_error( pcb, "\nError: object not found '%s'", nodename); } return pcb_print_errormsg( pcb, pcb->objmod, ERR_NCX_DEF_NOT_FOUND ); } *useobj = foundobj; return NO_ERR; } /* set_next_objnode */ /******************************************************************** * FUNCTION move_up_obj * * Get the parent object identifier * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block in progress * * RETURNS: * status *********************************************************************/ static status_t move_up_obj (xpath_pcb_t *pcb) { obj_template_t *foundobj = NULL; status_t res = NO_ERR; obj_template_t** useobj = select_pcb_object( pcb, &res ); if ( NO_ERR != res ) { pcb_print_errormsg(pcb, pcb->objmod, res); return res; } if (*useobj) { /* get child node of this object */ if ((*useobj)->parent) { foundobj = obj_get_real_parent(*useobj); } else if (*useobj != pcb->docroot) { foundobj = pcb->docroot; } } else { /* setting object for the first time * get parent node of the the start object */ foundobj = obj_get_real_parent(pcb->obj); if (foundobj == NULL) { foundobj = pcb->docroot; } } if (foundobj == NULL) { res = ERR_NCX_DEF_NOT_FOUND; if (pcb->logerrors) { log_error("\nError: parent not found for object '%s'", obj_get_name(*useobj)); ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } } else { *useobj = foundobj; } return res; } /* move_up_obj */ /******************************************************************** * retrieve the associated node name for the current tk_tt_period * pcb token. * * \param pcb the parser control block in progress| * \param prefix the prefix * \param nsid the nsid * \param res the result status * \return the associated node name *********************************************************************/ static const xmlChar* parse_node_identifier_tk_tt_period( xpath_pcb_t* pcb, const xmlChar** prefix, xmlns_id_t* nsid, status_t* res ) { if (pcb->flags & XP_FL_INSTANCEID) { if(pcb->obj==NULL) { /* parsing wo schema "." can't be interpreted */ *res = NO_ERR; return NULL; } else if(targ_obj_is_leaflist( pcb )) { *res = NO_ERR; *nsid = obj_get_nsid(pcb->targobj); *prefix = obj_get_mod_prefix(pcb->targobj); return obj_get_name(pcb->targobj); } else { *res = ERR_NCX_INVALID_VALUE; } } else { *res = pcb_log_error_msg( pcb, NULL, ERR_NCX_WRONG_TKTYPE, "\nError: '.' not allowed here" ); } return NULL; } /******************************************************************** * retrieve the associated node name for the current tk_tt_string * pcb token. * * * \param pcb the parser control block in progress| * \param prefix the prefix * \param nsid the nsid * \param res the result status * \return the associated node name *********************************************************************/ static const xmlChar* parse_node_identifier_tk_tt_string( xpath_pcb_t* pcb, const xmlChar** prefix, xmlns_id_t* nsid, status_t* res ) { /* pfix:identifier */ if (pcb->source != XP_SRC_XML) { if(pcb->obj && pcb->tkc->cur->mod==NULL) { *prefix = pcb->obj->mod->prefix; *nsid = pcb->obj->mod->nsid; } else if (pcb->tkerr.mod) { *prefix = TK_CUR_MOD(pcb->tkc); if ( xml_strcmp(pcb->tkerr.mod->prefix, *prefix)) { ncx_import_t* import = ncx_find_pre_import( pcb->tkerr.mod, *prefix); if (!import) { *res = pcb_log_error_msg( pcb, pcb->tkerr.mod, ERR_NCX_PREFIX_NOT_FOUND, "\nError: '.' not allowed here" ); return NULL; } else { ncx_module_t* testmod = ncx_find_module( import->module, import->revision ); if (testmod) { *nsid = testmod->nsid; } } } else { *nsid = pcb->tkerr.mod->nsid; } } else { *nsid = xmlns_find_ns_by_module(TK_CUR_MOD(pcb->tkc)); if ( *nsid != XMLNS_NULL_NS_ID ) { *prefix = xmlns_get_ns_prefix(*nsid); } else { *nsid = xmlns_find_ns_by_prefix(TK_CUR_MOD(pcb->tkc)); if ( *nsid != XMLNS_NULL_NS_ID ) { *prefix = TK_CUR_MOD(pcb->tkc); } } if ( *nsid == XMLNS_NULL_NS_ID ) { *res = pcb_log_error_msg( pcb, pcb->tkerr.mod, ERR_NCX_PREFIX_NOT_FOUND, "\nError: module for prefix '%s' not found", *prefix ); return NULL; } } } else { *prefix = TK_CUR_MOD(pcb->tkc); *res = xml_get_namespace_id( pcb->reader, *prefix, TK_CUR_MODLEN(pcb->tkc), nsid); if ( *res != NO_ERR ) { *res = pcb_log_error_msg( pcb, pcb->tkerr.mod, *res, "\nError: unknown XML prefix '%s'", *prefix ); return NULL; } } /* save the NSID in the token for printing later */ TK_CUR_NSID(pcb->tkc) = *nsid; return TK_CUR_VAL(pcb->tkc); } /******************************************************************** * Parse the leafref node-identifier string * It has already been tokenized * * \note Error messages are printed by this function!! * Do not duplicate error messages upon error return * * node-identifier = [prefix ":"] identifier * * \param pcb the parser control block in progress| * \return the status result *********************************************************************/ static status_t parse_node_identifier (xpath_pcb_t *pcb) { const xmlChar *nodename = NULL; const xmlChar *prefix = NULL; xmlns_id_t nsid = 0; /* get the next token in the step, node-identifier */ status_t res = TK_ADV(pcb->tkc); if (res != NO_ERR) { pcb_print_errormsg(pcb, pcb->tkerr.mod, res); return res; } switch (TK_CUR_TYP(pcb->tkc)) { case TK_TT_PERIOD: nodename = parse_node_identifier_tk_tt_period( pcb, &prefix, &nsid, &res ); break; case TK_TT_MSTRING: nodename = parse_node_identifier_tk_tt_string( pcb, &prefix, &nsid, &res ); break; /* fall through to check QName */ case TK_TT_TSTRING: nodename = TK_CUR_VAL(pcb->tkc); break; default: res = ERR_NCX_WRONG_TKTYPE; if (pcb->logerrors) { ncx_mod_exp_err(pcb->tkc, pcb->tkerr.mod, res, "node identifier"); } break; } /* FIXME Should this function simply return NO_ERR at the top if * pcb->obj is NULL */ if ( res == NO_ERR && pcb->obj ) { res = set_next_objnode( pcb, prefix, nsid, nodename ); } return res; } /* parse_node_identifier */ /******************************************************************** * FUNCTION parse_current_fn * * Parse the leafref current-function token sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * current-function-invocation = 'current()' * * INPUTS: * pcb == parser control block in progress * * RETURNS: * status *********************************************************************/ static status_t parse_current_fn (xpath_pcb_t *pcb) { status_t res; /* get the function name 'current' */ res = xpath_parse_token(pcb, TK_TT_TSTRING); if (res != NO_ERR) { return res; } if (xml_strcmp(TK_CUR_VAL(pcb->tkc), (const xmlChar *)"current")) { res = ERR_NCX_WRONG_VAL; if (pcb->logerrors) { ncx_mod_exp_err(pcb->tkc, pcb->tkerr.mod, res, "current() function"); } return res; } /* get the left paren '(' */ res = xpath_parse_token(pcb, TK_TT_LPAREN); if (res != NO_ERR) { return res; } /* get the right paren ')' */ res = xpath_parse_token(pcb, TK_TT_RPAREN); if (res != NO_ERR) { return res; } /* assign the context node to the start object node */ if (pcb->obj) { switch (pcb->curmode) { case XP_CM_TARGET: pcb->targobj = pcb->obj; break; case XP_CM_ALT: pcb->altobj = pcb->obj; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } return NO_ERR; } /* parse_current_fn */ /******************************************************************** * FUNCTION parse_path_key_expr * * Parse the leafref *path-key-expr token sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * path-key-expr = current-function-invocation "/" * rel-path-keyexpr * * rel-path-keyexpr = 1*(".." "/") *(node-identifier "/") * node-identifier * * INPUTS: * pcb == parser control block in progress * * RETURNS: * status *********************************************************************/ static status_t parse_path_key_expr (xpath_pcb_t *pcb) { tk_type_t nexttyp; status_t res; boolean done; res = NO_ERR; done = FALSE; pcb->altobj = NULL; pcb->curmode = XP_CM_ALT; /* get the current function call 'current()' */ res = parse_current_fn(pcb); if (res != NO_ERR) { return res; } /* get the path separator '/' */ res = xpath_parse_token(pcb, TK_TT_FSLASH); if (res != NO_ERR) { return res; } /* make one loop for each step of the first part of * the rel-path-keyexpr; the '../' token pairs */ while (!done) { /* get the parent marker '..' */ res = xpath_parse_token(pcb, TK_TT_RANGESEP); if (res != NO_ERR) { done = TRUE; continue; } /* get the path separator '/' */ res = xpath_parse_token(pcb, TK_TT_FSLASH); if (res != NO_ERR) { done = TRUE; continue; } /* move the context node up one level */ if (pcb->obj) { res = move_up_obj(pcb); if (res != NO_ERR) { done = TRUE; continue; } } /* check the next token; * it may be the start of another '../' pair */ nexttyp = tk_next_typ(pcb->tkc); if (nexttyp != TK_TT_RANGESEP) { done = TRUE; } /* else keep going to the next '../' pair */ } if (res != NO_ERR) { return res; } /* get the node-identifier sequence */ done = FALSE; while (!done) { res = parse_node_identifier(pcb); if (res != NO_ERR) { done = TRUE; continue; } /* check the next token; * it may be the fwd slash to start another step */ nexttyp = tk_next_typ(pcb->tkc); if (nexttyp != TK_TT_FSLASH) { done = TRUE; } else { res = xpath_parse_token(pcb, TK_TT_FSLASH); if (res != NO_ERR) { done = TRUE; } } } return res; } /* parse_path_key_expr */ /******************************************************************** * FUNCTION get_key_number * * Get the key number for the specified key * It has already been tokenized * * INPUTS: * obj == list object to check * keyobj == key leaf object to find * * RETURNS: * int32 [0..N-1] key number or -1 if not a key in this obj *********************************************************************/ static int32 get_key_number (obj_template_t *obj, obj_template_t *keyobj) { obj_key_t *key; int32 keynum; keynum = 0; key = obj_first_key(obj); while (key) { if (key->keyobj == keyobj) { return keynum; } keynum++; key = obj_next_key(key); } return -1; } /* get_key_number */ /******************************************************************** * FUNCTION parse_path_predicate * * Parse the leafref *path-predicate token sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * path-predicate = "[" *WSP path-equality-expr *WSP "]" * * path-equality-expr = node-identifier *WSP "=" *WSP path-key-expr * * path-key-expr = current-function-invocation "/" * rel-path-keyexpr * * node-identifier = [prefix ":"] identifier * * current-function-invocation = 'current()' * * For instance-identifiers (XP_FL_INSTANCEID) the * following syntax is used: * * path-predicate = "[" *WSP instpath-equality-expr *WSP "]" * * instpath-equality-expr = node-identifier *WSP "=" *WSP quoted-string * * INPUTS: * pcb == parser control block in progress * * RETURNS: * status *********************************************************************/ static status_t parse_path_predicate (xpath_pcb_t *pcb) { #define MAX_KEYS 64 tk_type_t nexttyp; status_t res; boolean done, leaflist; uint64 keyflags, keybit; uint32 keytotal, keycount, loopcount; int32 keynum; res = NO_ERR; done = FALSE; keyflags = 0; keytotal = 0; keycount = 0; loopcount = 0; leaflist = FALSE; if (pcb->targobj && pcb->targobj->objtype == OBJ_TYP_LIST) { keytotal = obj_key_count(pcb->targobj); if (keytotal > MAX_KEYS) { if (pcb->logerrors && ncx_warning_enabled(ERR_NCX_MAX_KEY_CHECK)) { log_warn("\nWarning: Only first %u keys in list '%s'" " can be checked in XPath expression", MAX_KEYS, obj_get_name(pcb->obj)); } else if (pcb->objmod != NULL) { ncx_inc_warnings(pcb->objmod); } } else if (keytotal == 0) { res = ERR_NCX_INVALID_VALUE; log_error("\nError: Key predicates found for list " "'%s' which does not define any keys", obj_get_name(pcb->targobj)); return res; } } else if ((pcb->flags & XP_FL_INSTANCEID) && targ_obj_is_leaflist( pcb ) ) { keytotal = 1; } /* make one loop for each step */ while (!done) { leaflist = FALSE; /* only used if pcb->obj set and validating expr */ loopcount++; /* get the left bracket '[' */ res = xpath_parse_token(pcb, TK_TT_LBRACK); if (res != NO_ERR) { done = TRUE; continue; } pcb->varobj = NULL; pcb->curmode = XP_CM_KEYVAR; /* get the node-identifier next */ res = parse_node_identifier(pcb); if (res != NO_ERR) { done = TRUE; continue; } /* validate the variable object foo in [foo = expr] */ if (pcb->obj && pcb->varobj) { if ((pcb->flags & XP_FL_INSTANCEID) && pcb->varobj->objtype == OBJ_TYP_LEAF_LIST) { if (!(pcb->targobj && pcb->targobj == pcb->varobj)) { res = ERR_NCX_WRONG_INDEX_TYPE; if (pcb->logerrors) { log_error("\nError: wrong index type '%s' for " "leaf-list '%s'", obj_get_typestr(pcb->varobj), obj_get_name(pcb->targobj)); ncx_print_errormsg(pcb->tkc, NULL, res); } } else { leaflist = TRUE; } } else if (pcb->varobj->objtype != OBJ_TYP_LEAF) { res = ERR_NCX_WRONG_TYPE; if (pcb->logerrors) { log_error("\nError: path predicate found is %s '%s'", obj_get_typestr(pcb->varobj), obj_get_name(pcb->varobj)); ncx_mod_exp_err(pcb->tkc, pcb->objmod, res, "leaf"); } done = TRUE; continue; } if (leaflist) { keynum = 0; } else { keynum = get_key_number(pcb->targobj, pcb->varobj); } if (keynum == -1) { ; /* SET_ERROR(ERR_INTERNAL_VAL); */ /* not key value but still OK to have as predicate */ } else if (keynum < MAX_KEYS) { keybit = (uint64)(1 << keynum); if (keyflags & keybit) { res = ERR_NCX_EXTRA_PARM; if (pcb->logerrors) { log_error("\nError: key '%s' already specified " "in XPath expression for object '%s'", obj_get_name(pcb->varobj), obj_get_name(pcb->targobj)); ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } } else { keycount++; keyflags |= keybit; } } else { if (pcb->logerrors && ncx_warning_enabled(ERR_NCX_MAX_KEY_CHECK)) { log_warn("\nWarning: Key '%s' skipped " "in validation test", obj_get_name(pcb->varobj)); } else if (pcb->objmod != NULL) { ncx_inc_warnings(pcb->objmod); } } } /* get the equals sign '=' */ res = xpath_parse_token(pcb, TK_TT_EQUAL); if (res != NO_ERR) { done = TRUE; continue; } if (pcb->flags & (XP_FL_INSTANCEID | XP_FL_SCHEMA_INSTANCEID)) { pcb->altobj = NULL; /* parse literal */ res = TK_ADV(pcb->tkc); if (res == NO_ERR) { if (!(TK_CUR_TYP(pcb->tkc) == TK_TT_QSTRING || TK_CUR_TYP(pcb->tkc) == TK_TT_SQSTRING)) { res = ERR_NCX_WRONG_TKTYPE; } } if (res != NO_ERR) { if (pcb->logerrors) { log_error("\nError: invalid predicate in " "expression '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, NULL, res); } done = TRUE; continue; } } else { /* get the path-key-expr next */ res = parse_path_key_expr(pcb); if (res != NO_ERR) { done = TRUE; continue; } } /* check the object specified in the key expression */ if (pcb->obj && pcb->altobj) { if (pcb->altobj->objtype != OBJ_TYP_LEAF) { res = ERR_NCX_WRONG_TYPE; if (pcb->logerrors) { log_error("\nError: path target found is %s '%s'", obj_get_typestr(pcb->altobj), obj_get_name(pcb->altobj)); ncx_mod_exp_err(pcb->tkc, pcb->objmod, res, "leaf"); } done = TRUE; continue; } else { /* check the predicate for errors */ if (pcb->altobj == pcb->obj) { res = ERR_NCX_DEF_LOOP; if (pcb->logerrors) { log_error("\nError: path target '%s' is set to " "the target object", obj_get_name(pcb->altobj)); ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } done = TRUE; continue; } else if (0 && (pcb->altobj == pcb->varobj)) { /* TODO should only get here if the path target is abosolute and not relevant to a child leaf e.g. /user-plane-configuration/tx-array-carriers[name=current()/../name]/type is OK */ res = ERR_NCX_DEF_LOOP; if (pcb->logerrors) { log_error("\nError: path target '%s' is set to " "the key leaf object", obj_get_name(pcb->altobj)); ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } done = TRUE; continue; } } } /* get the right bracket ']' */ res = xpath_parse_token(pcb, TK_TT_RBRACK); if (res != NO_ERR) { done = TRUE; continue; } pcb->curmode = XP_CM_TARGET; /* check the next token; * it may be the start of another path-predicate '[' */ nexttyp = tk_next_typ(pcb->tkc); if (nexttyp != TK_TT_LBRACK) { done = TRUE; } /* else keep going to the next path-predicate */ } if (pcb->obj) { if (loopcount < keytotal) { if (keycount < keytotal) { if (pcb->flags & XP_FL_SCHEMA_INSTANCEID || pcb->source == XP_SRC_LEAFREF) { /* schema-instance allowed to skip keys * leafref path-expr allowed to skip keys */ ; } else { /* regular instance-identifier must have all keys */ res = ERR_NCX_MISSING_INDEX; if (pcb->logerrors) { log_error("\nError: missing key components in" " XPath expression for list '%s'", obj_get_name(pcb->targobj)); ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } } } else { res = ERR_NCX_EXTRA_VAL_INST; if (pcb->logerrors) { log_error("\nError: extra key components in" " XPath expression for list '%s'", obj_get_name(pcb->targobj)); ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } } } } return res; } /* parse_path_predicate */ /******************************************************************** * FUNCTION parse_absolute_path * * Parse the leafref path-arg string * It has already been tokenized * * The exact syntax below is not followed!!! * The first '/' to start the absolute path * is allowed to be omitted in XPath, so * allow that here as well * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * absolute-path = 1*("/" (node-identifier *path-predicate)) * * path-predicate = "[" *WSP path-equality-expr *WSP "]" * * path-equality-expr = node-identifier *WSP "=" *WSP path-key-expr * * path-key-expr = current-function-invocation "/" * rel-path-keyexpr * * node-identifier = [prefix ":"] identifier * * current-function-invocation = 'current()' * * INPUTS: * pcb == parser control block in progress * * RETURNS: * status *********************************************************************/ static status_t parse_absolute_path (xpath_pcb_t *pcb) { tk_type_t nexttyp; status_t res; boolean done, first; res = NO_ERR; done = FALSE; first = TRUE; /* make one loop for each step */ while (!done) { /* get the first token in the step, '/' */ if (first) { /* allow the first '/' to be omitted */ if (tk_next_typ(pcb->tkc) == TK_TT_FSLASH) { res = xpath_parse_token(pcb, TK_TT_FSLASH); } first = FALSE; } else { /* get the first token in the step, '/' */ res = xpath_parse_token(pcb, TK_TT_FSLASH); } if (res != NO_ERR) { done = TRUE; continue; } if (pcb->flags & XP_FL_SCHEMA_INSTANCEID && tk_next_typ(pcb->tkc) == TK_TT_NONE) { /* corner-case: a schema-instance-string * is allowed to be a single forward slash * to indicate the entire document */ pcb->targobj = pcb->docroot; done = TRUE; continue; } /* get the node-identifier next */ res = parse_node_identifier(pcb); if (res != NO_ERR) { done = TRUE; continue; } /* check the next token; * it may be the start of a path-predicate '[' * or the start of another step '/' */ nexttyp = tk_next_typ(pcb->tkc); if (nexttyp == TK_TT_LBRACK) { res = parse_path_predicate(pcb); if (res != NO_ERR) { done = TRUE; continue; } nexttyp = tk_next_typ(pcb->tkc); if (nexttyp != TK_TT_FSLASH) { done = TRUE; } } else if (nexttyp != TK_TT_FSLASH) { done = TRUE; } /* else keep going to the next step */ } /* check that the string ended properly */ if (res == NO_ERR) { nexttyp = tk_next_typ(pcb->tkc); if (nexttyp != TK_TT_NONE) { res = ERR_NCX_INVALID_TOKEN; if (pcb->logerrors) { log_error("\nError: wrong token at end of absolute-path '%s'", tk_get_token_name(nexttyp)); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, res); } } } return res; } /* parse_absolute_path */ /******************************************************************** * FUNCTION parse_relative_path * * Parse the leafref relative-path string * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * relative-path = descendant-path / * (".." "/" * *relative-path) * * descendant-path = node-identifier *path-predicate * absolute-path * * Real implementation: * * relative-path = *(".." "/") descendant-path * * descendant-path = node-identifier *path-predicate * [absolute-path] * * INPUTS: * pcb == parser control block in progress * * RETURNS: * status *********************************************************************/ static status_t parse_relative_path (xpath_pcb_t *pcb) { tk_type_t nexttyp; status_t res; res = NO_ERR; if(pcb->obj && pcb->targobj==NULL) { pcb->targobj=pcb->obj; } /* check the next token; * it may be the start of a '../' pair or a * descendant-path (node-identifier) */ nexttyp = tk_next_typ(pcb->tkc); while (nexttyp == TK_TT_RANGESEP && res == NO_ERR) { res = xpath_parse_token(pcb, TK_TT_RANGESEP); if (res == NO_ERR) { res = xpath_parse_token(pcb, TK_TT_FSLASH); if (res == NO_ERR) { if (pcb->obj) { res = move_up_obj(pcb); } /* check the next token; * it may be the start of another ../ pair * or a node identifier */ nexttyp = tk_next_typ(pcb->tkc); } } } if (res == NO_ERR) { /* expect a node identifier first */ res = parse_node_identifier(pcb); if (res == NO_ERR) { /* check the next token; * it may be the start of a path-predicate '[' * or the start of another step '/' */ nexttyp = tk_next_typ(pcb->tkc); if (nexttyp == TK_TT_LBRACK) { res = parse_path_predicate(pcb); } if (res == NO_ERR) { /* check the next token; * it may be the start of another step '/' */ nexttyp = tk_next_typ(pcb->tkc); if (nexttyp == TK_TT_FSLASH) { res = parse_absolute_path(pcb); } } } } return res; } /* parse_relative_path */ /******************************************************************** * FUNCTION parse_path_arg * * Parse the leafref path-arg string * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * path-arg-str = < a string which matches the rule * path-arg > * * path-arg = absolute-path / relative-path * * INPUTS: * pcb == parser control block in progress * * RETURNS: * status *********************************************************************/ static status_t parse_path_arg (xpath_pcb_t *pcb) { tk_type_t nexttyp; nexttyp = tk_next_typ(pcb->tkc); if (nexttyp == TK_TT_FSLASH) { pcb->flags |= XP_FL_ABSPATH; return parse_absolute_path(pcb); } else if (pcb->obj != NULL && pcb->obj == pcb->docroot) { pcb->flags |= XP_FL_ABSPATH; return parse_absolute_path(pcb); } else { return parse_relative_path(pcb); } } /* parse_path_arg */ /******************************************************************** * FUNCTION parse_keyexpr * * Parse the key attribute expression * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * keyexprr = 1*path-predicate * * path-predicate = "[" *WSP path-equality-expr *WSP "]" * * path-equality-expr = node-identifier *WSP "=" *WSP quoted-string * * INPUTS: * pcb == parser control block in progress * * RETURNS: * status *********************************************************************/ static status_t parse_keyexpr (xpath_pcb_t *pcb) { tk_type_t nexttyp; status_t res; boolean done; res = NO_ERR; done = FALSE; /* make one loop for each step */ while (!done) { /* check the next token; * it may be the start of a path-predicate '[' * or the start of another step '/' */ nexttyp = tk_next_typ(pcb->tkc); if (nexttyp == TK_TT_LBRACK) { res = parse_path_predicate(pcb); if (res != NO_ERR) { done = TRUE; } } else if (nexttyp == TK_TT_NONE) { done = TRUE; continue; } else { res = ERR_NCX_INVALID_VALUE; done = TRUE; if (pcb->logerrors) { log_error("\nError: wrong token in key-expr '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, res); } } } return res; } /* parse_keyexpr */ /************ E X T E R N A L F U N C T I O N S ************/ /******************************************************************** * FUNCTION xpath_yang_parse_path * * Parse the leafref path as a leafref path * * DOES NOT VALIDATE PATH NODES USED IN THIS PHASE * A 2-pass validation is used in case the path expression * is defined within a grouping. This pass is * used on all objects, even in groupings * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * * INPUTS: * tkc == parent token chain (may be NULL) * mod == module in progress * obj == context node * source == context for this expression * XP_SRC_LEAFREF or XP_SRC_INSTANCEID * pcb == initialized xpath parser control block * for the leafref path; use xpath_new_pcb * to initialize before calling this fn. * The pcb->exprstr field must be set * * OUTPUTS: * pcb->tkc is filled and then validated for well-formed * leafref or instance-identifier syntax * * RETURNS: * status *********************************************************************/ status_t xpath_yang_parse_path (tk_chain_t *tkc, ncx_module_t *mod, xpath_source_t source, xpath_pcb_t *pcb) { return xpath_yang_parse_path_ex(tkc, mod, source, pcb, TRUE); } /* xpath_yang_parse_path */ /******************************************************************** * FUNCTION xpath_yang_parse_path_ex * * Parse the leafref path as a leafref path * * DOES NOT VALIDATE PATH NODES USED IN THIS PHASE * A 2-pass validation is used in case the path expression * is defined within a grouping. This pass is * used on all objects, even in groupings * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * * INPUTS: * tkc == parent token chain (may be NULL) * mod == module in progress * obj == context node * source == context for this expression * XP_SRC_LEAFREF or XP_SRC_INSTANCEID * pcb == initialized xpath parser control block * for the leafref path; use xpath_new_pcb * to initialize before calling this fn. * The pcb->exprstr field must be set * logerrors == TRUE to log errors; * == FALSE to suppress error messages * OUTPUTS: * pcb->tkc is filled and then validated for well-formed * leafref or instance-identifier syntax * * RETURNS: * status *********************************************************************/ status_t xpath_yang_parse_path_ex (tk_chain_t *tkc, ncx_module_t *mod, xpath_source_t source, xpath_pcb_t *pcb, boolean logerrors) { status_t res; uint32 linenum, linepos; #ifdef DEBUG if (!pcb || !pcb->exprstr) { return SET_ERROR(ERR_INTERNAL_PTR); } switch (source) { case XP_SRC_LEAFREF: case XP_SRC_INSTANCEID: case XP_SRC_SCHEMA_INSTANCEID: break; default: return SET_ERROR(ERR_INTERNAL_VAL); } #endif pcb->curmode = XP_CM_TARGET; pcb->logerrors = logerrors; if (tkc && tkc->cur) { linenum = TK_CUR_LNUM(tkc); linepos = TK_CUR_LPOS(tkc); } else { linenum = pcb->tkerr.linenum; linepos = pcb->tkerr.linepos; } if (pcb->tkc) { tk_reset_chain(pcb->tkc); } else { pcb->tkc = tk_tokenize_xpath_string(mod, pcb->exprstr, linenum, linepos, &res); if (!pcb->tkc || res != NO_ERR) { if (pcb->logerrors) { log_error("\nError: Invalid path string '%s'", pcb->exprstr); ncx_print_errormsg(tkc, mod, res); } return res; } } #if 1 /* the module that contains the leafref is the one * that will always be used to resolve prefixes * within the XPath expression */ pcb->tkerr.mod = mod; #endif pcb->source = source; if (source == XP_SRC_INSTANCEID) { pcb->flags |= XP_FL_INSTANCEID; } else if (source == XP_SRC_SCHEMA_INSTANCEID) { pcb->flags |= XP_FL_SCHEMA_INSTANCEID; } /* since the pcb->obj is not set, this validation * phase will skip identifier tests, predicate tests * and completeness tests */ pcb->parseres = parse_path_arg(pcb); /* the expression will not be processed further if the * parseres is other than NO_ERR */ return pcb->parseres; } /* xpath_yang_parse_path_ex */ /******************************************************************** * FUNCTION xpath_yang_validate_path * * Validate the previously parsed leafref path * - QNames are valid * - object structure referenced is valid * - objects are all 'config true' * - target object is a leaf * - leafref represents a single instance * * A 2-pass validation is used in case the path expression * is defined within a grouping. This pass is * used only on cooked (real) objects * * Called after all 'uses' and 'augment' expansion * so validation against cooked object tree can be done * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * mod == module containing the 'obj' (in progress) * == NULL if no object in progress * obj == object using the leafref data type * pcb == the leafref parser control block, possibly * cloned from from the typdef * schemainst == TRUE if ncx:schema-instance string * == FALSE to use the pcb->source field * to determine the exact parse mode * leafobj == address of the return target object * * OUTPUTS: * *leafobj == the target leaf found by parsing the path (NO_ERR) * * RETURNS: * status *********************************************************************/ status_t xpath_yang_validate_path (ncx_module_t *mod, obj_template_t *obj, xpath_pcb_t *pcb, boolean schemainst, obj_template_t **leafobj) { return xpath_yang_validate_path_ex(mod, obj, pcb, schemainst, leafobj, TRUE); } /* xpath_yang_validate_path */ /******************************************************************** * FUNCTION xpath_yang_validate_path_ex * * Validate the previously parsed leafref path * - QNames are valid * - object structure referenced is valid * - objects are all 'config true' * - target object is a leaf * - leafref represents a single instance * * A 2-pass validation is used in case the path expression * is defined within a grouping. This pass is * used only on cooked (real) objects * * Called after all 'uses' and 'augment' expansion * so validation against cooked object tree can be done * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * mod == module containing the 'obj' (in progress) * == NULL if no object in progress * obj == object using the leafref data type * pcb == the leafref parser control block, possibly * cloned from from the typdef * schemainst == TRUE if ncx:schema-instance string * == FALSE to use the pcb->source field * to determine the exact parse mode * leafobj == address of the return target object * logerrors == TRUE to log errors, FALSE to suppress errors * val_parse uses FALSE if basetype == NCX_BT_UNION * * OUTPUTS: * *leafobj == the target leaf found by parsing the path (NO_ERR) * * RETURNS: * status *********************************************************************/ status_t xpath_yang_validate_path_ex (ncx_module_t *mod, obj_template_t *obj, xpath_pcb_t *pcb, boolean schemainst, obj_template_t **leafobj, boolean logerrors) { status_t res; boolean doerror; ncx_btype_t btyp; #ifdef DEBUG if (!obj || !pcb || !leafobj || !pcb->exprstr) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!pcb->tkc) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif if (schemainst) { /* relax errors for missing keys in instance-identifier */ pcb->flags |= XP_FL_SCHEMA_INSTANCEID; } pcb->logerrors = logerrors; *leafobj = NULL; if (pcb->parseres != NO_ERR) { /* errors already reported, skip this one */ return NO_ERR; } pcb->docroot = ncx_get_gen_root(); if (!pcb->docroot) { return SET_ERROR(ERR_INTERNAL_VAL); } tk_reset_chain(pcb->tkc); pcb->objmod = mod; pcb->obj = obj; pcb->targobj = NULL; pcb->altobj = NULL; pcb->varobj = NULL; pcb->curmode = XP_CM_TARGET; /* validate the XPath expression against the * full cooked object tree */ pcb->validateres = parse_path_arg(pcb); /* check leafref is config but target is not */ if (pcb->validateres == NO_ERR && pcb->targobj) { /* return this object even if errors * it will get ignored unless return NO_ERR */ *leafobj = pcb->targobj; /* make sure the config vs. non-config rules are followed * if obj is config, it can point at only config targobj * if obj not config, it can point at any targobj node */ if (mod && mod->langvertargobj)) { doerror = TRUE; btyp = obj_get_basetype(obj); /* only some instance-identifier and leafref * objects will ever return TRUE */ if ((btyp == NCX_BT_INSTANCE_ID || btyp == NCX_BT_LEAFREF) && !typ_get_constrained(obj_get_ctypdef(obj))) { doerror = FALSE; } /* yangcli_util/get_instanceid_parm sets the * object field to the config root instead * of the leafref object, so this hack is used * to suppress the error intended for leafrefs */ if (obj_is_root(obj)) { doerror = FALSE; } if (doerror) { res = ERR_NCX_NOT_CONFIG; if (pcb->logerrors) { log_error("\nError: XPath target '%s' for leafref '%s'" " must be a config object", obj_get_name(pcb->targobj), obj_get_name(obj)); ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } pcb->validateres = res; } } /* check for a leaf, then if that is OK */ if (pcb->source == XP_SRC_LEAFREF) { if (!obj_is_leafy(pcb->targobj)) { res = ERR_NCX_INVALID_VALUE; if (pcb->logerrors) { log_error("\nError: invalid path target %s '%s'", obj_get_typestr(pcb->targobj), obj_get_name(pcb->targobj)); ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } pcb->validateres = res; } } /* this test is probably not worth doing, * but check for the loop anyway * special case: allow docroot as return target */ if (pcb->targobj == pcb->obj && pcb->targobj != pcb->docroot) { res = ERR_NCX_DEF_LOOP; if (pcb->logerrors) { log_error("\nError: path target '%s' is set to " "the target object", obj_get_name(pcb->targobj)); ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } pcb->validateres = res; } /**** NEED TO CHECK THE CORNER CASE WHERE A LEAFREF **** AND/OR INSTANCE-IDENTIFIER COMBOS CAUSE A LOOP **** THAT WILL PREVENT AGENT VALIDATION FROM COMPLETING ****/ } return pcb->validateres; } /* xpath_yang_validate_path */ /******************************************************************** * FUNCTION xpath_yang_validate_xmlpath * * Validate an instance-identifier expression * within an XML PDU context * * INPUTS: * reader == XML reader to use * pcb == initialized XPath parser control block * with a possibly unchecked pcb->exprstr. * This function will call tk_tokenize_xpath_string * if it has not already been called. * logerrors == TRUE if log_error and ncx_print_errormsg * should be used to log XPath errors and warnings * FALSE if internal error info should be recorded * in the xpath_result_t struct instead * !!! use FALSE unless DEBUG mode !!! * targobj == address of return target object * * OUTPUTS: * *targobj is set to the object that this instance-identifier * references, if NO_ERR * RETURNS: * status *********************************************************************/ status_t xpath_yang_validate_xmlpath (xmlTextReaderPtr reader, xpath_pcb_t *pcb, obj_template_t *pathobj, boolean logerrors, obj_template_t **targobj) { status_t res; #ifdef DEBUG if (!reader || !pcb || !targobj || !pcb->exprstr) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; *targobj = NULL; pcb->logerrors = logerrors; if (pcb->tkc) { tk_reset_chain(pcb->tkc); } else { pcb->tkc = tk_tokenize_xpath_string(NULL, pcb->exprstr, 0, 0, &res); } if (!pcb->tkc || res != NO_ERR) { if (pcb->logerrors) { log_error("\nError: Invalid path string '%s'", pcb->exprstr); } pcb->parseres = res; return res; } pcb->docroot = ncx_get_gen_root(); if (!pcb->docroot) { return SET_ERROR(ERR_INTERNAL_VAL); } if (pathobj && obj_is_schema_instance_string(pathobj)) { pcb->flags |= XP_FL_SCHEMA_INSTANCEID; } else { pcb->flags |= XP_FL_INSTANCEID; } pcb->obj = pcb->docroot; pcb->reader = reader; pcb->source = XP_SRC_XML; pcb->objmod = NULL; pcb->val = NULL; pcb->val_docroot = NULL; pcb->targobj = NULL; pcb->altobj = NULL; pcb->varobj = NULL; pcb->curmode = XP_CM_TARGET; pcb->flags |= XP_FL_ABSPATH; /* validate the XPath expression against the * full cooked object tree */ pcb->validateres = parse_absolute_path(pcb); /* check leafref is config but target is not */ if (pcb->validateres == NO_ERR && pcb->targobj) { *targobj = pcb->targobj; } pcb->reader = NULL; return pcb->validateres; } /* xpath_yang_validate_xmlpath */ /******************************************************************** * FUNCTION xpath_yang_validate_xmlkey * * Validate a key XML attribute value given in * an operation with an 'insert' attribute * Check that a complete set of predicates is present * for the specified list of leaf-list * * INPUTS: * reader == XML reader to use * pcb == initialized XPath parser control block * with a possibly unchecked pcb->exprstr. * This function will call tk_tokenize_xpath_string * if it has not already been called. * obj == list or leaf-list object associated with * the pcb->exprstr predicate expression * (MAY be NULL if first-pass parsing and * object is not known yet -- parsed in XML attribute) * logerrors == TRUE if log_error and ncx_print_errormsg * should be used to log XPath errors and warnings * FALSE if internal error info should be recorded * in the xpath_result_t struct instead * !!! use FALSE unless DEBUG mode !!! * * RETURNS: * status *********************************************************************/ status_t xpath_yang_validate_xmlkey (xmlTextReaderPtr reader, xpath_pcb_t *pcb, obj_template_t *obj, boolean logerrors) { status_t res; #ifdef DEBUG if (!reader || !pcb || !pcb->exprstr) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; pcb->logerrors = logerrors; if (pcb->tkc) { tk_reset_chain(pcb->tkc); } else { pcb->tkc = tk_tokenize_xpath_string(NULL, pcb->exprstr, 0, 0, &res); } if (!pcb->tkc || res != NO_ERR) { if (pcb->logerrors) { log_error("\nError: Invalid path string '%s'", pcb->exprstr); } pcb->parseres = res; return res; } pcb->docroot = ncx_get_gen_root(); if (!pcb->docroot) { return SET_ERROR(ERR_INTERNAL_VAL); } pcb->obj = obj; pcb->reader = reader; pcb->flags = XP_FL_INSTANCEID; pcb->source = XP_SRC_XML; pcb->objmod = NULL; pcb->val = NULL; pcb->val_docroot = NULL; pcb->targobj = obj; pcb->altobj = NULL; pcb->varobj = NULL; pcb->curmode = XP_CM_TARGET; /* validate the XPath expression against the * full cooked object tree */ pcb->validateres = parse_keyexpr(pcb); pcb->reader = NULL; return pcb->validateres; } /* xpath_yang_validate_xmlkey */ static const xmlChar* get_cur_modname(xpath_pcb_t *pcb) { const xmlChar* modname=NULL; if (TK_CUR_MOD(pcb->tkc)) { xmlns_id_t nsid=XMLNS_NULL_NS_ID; if(pcb->objmod==NULL) { nsid = xmlns_find_ns_by_module(TK_CUR_MOD(pcb->tkc)); } if(nsid==XMLNS_NULL_NS_ID) { nsid = xmlns_find_ns_by_prefix(TK_CUR_MOD(pcb->tkc)); } if(nsid!=XMLNS_NULL_NS_ID) { modname = xmlns_get_module(nsid); } } return modname; } /******************************************************************** * FUNCTION xpath_yang_make_instanceid_val * * Make a value subtree out of an instance-identifier * Used by yangcli to send PDUs from CLI target parameters * * The XPath pcb must be previously parsed and found valid * It must be an instance-identifier value, * not a leafref path * * INPUTS: * pcb == the leafref parser control block, possibly * cloned from from the typdef * retres == address of return status (may be NULL) * deepest == address of return deepest node created (may be NULL) * * OUTPUTS: * if (non-NULL) * *retres == return status * *deepest == pointer to end of instance-id chain node * RETURNS: * malloced value subtree representing the instance-identifier * in internal val_value_t data structures *********************************************************************/ val_value_t * xpath_yang_make_instanceid_val (xpath_pcb_t *pcb, status_t *retres, val_value_t **deepest) { val_value_t *childval, *top, *curtop; obj_template_t *curobj, *childobj; const xmlChar *objprefix, *objname, *modname; status_t res; boolean done, done2; #ifdef DEBUG if (!pcb) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (!pcb->tkc) { if (retres) { *retres = SET_ERROR(ERR_INTERNAL_VAL); } return NULL; } #endif if (retres) { *retres = NO_ERR; } if (deepest) { *deepest = NULL; } top = NULL; curtop = NULL; if (pcb->parseres != NO_ERR) { /* errors already reported, skip this one */ if (retres) { *retres = pcb->parseres; } return NULL; } /* get first token */ tk_reset_chain(pcb->tkc); res = TK_ADV(pcb->tkc); if (res != NO_ERR) { if (retres) { *retres = res; } return NULL; } /* get the start object */ if (TK_CUR_TYP(pcb->tkc) == TK_TT_FSLASH) { /* absolute path, use objroot to start */ curobj = pcb->docroot; res = TK_ADV(pcb->tkc); if (res == ERR_NCX_EOF && (pcb->flags & XP_FL_SCHEMA_INSTANCEID)) { /* return NO_ERR and NULL to indicate that the * docroot is requested */ return NULL; } if (res != NO_ERR) { if (retres) { *retres = res; } return NULL; } } else { /* relative path, use context object to start */ curobj = pcb->obj; } if (!curobj) { SET_ERROR(ERR_INTERNAL_VAL); if (retres) { *retres = ERR_INTERNAL_VAL; } return NULL; } /* get all the path steps */ res = NO_ERR; done = FALSE; while (!done && res == NO_ERR) { /* this is expected to be a QName or LCName identifier */ modname = get_cur_modname(pcb); objname = TK_CUR_VAL(pcb->tkc); /* find the child node and add it to the curtop */ if (obj_is_root(curobj)) { if (modname) { childobj = ncx_find_object(ncx_find_module(modname, NULL), objname); } else { childobj = ncx_find_any_object(objname); } } else { childobj = obj_find_child(curobj, modname, objname); } if (!childobj) { res = ERR_NCX_DEF_NOT_FOUND; if (retres) { *retres = res; } continue; } childval = val_new_value(); if (!childval) { res = ERR_INTERNAL_MEM; if (retres) { *retres = res; } continue; } /* if this is the last node, then treat it as a select * node since no value will be set; force type empty */ if (tk_next_typ(pcb->tkc) == TK_TT_NONE && obj_get_datadefQ(childobj) == NULL) { /* terminal leaf or leaf-list, create an empty node */ const xmlChar *name = obj_get_name(childobj); val_init_from_template(childval, ncx_get_gen_empty()); val_set_name(childval, name, xml_strlen(name)); val_change_nsid(childval, obj_get_nsid(childobj)); } else { val_init_from_template(childval, childobj); } if (curtop) { val_add_child(childval, curtop); } else { top = childval; } curtop = childval; curobj = childobj; if (deepest) { *deepest = curtop; } /* move on to the next token */ res = TK_ADV(pcb->tkc); if (res != NO_ERR) { /* reached end of the line at an OK point */ res = NO_ERR; done = TRUE; continue; } switch (TK_CUR_TYP(pcb->tkc)) { case TK_TT_FSLASH: /* normal identifier expected next, go back to start */ res = TK_ADV(pcb->tkc); continue; case TK_TT_LBRACK: /* predicate expected next, keep going */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); continue; } done2 = FALSE; while (!done2 && res == NO_ERR) { /* got a start of predicate, get the key leaf name */ res = TK_ADV(pcb->tkc); if (res != NO_ERR) { continue; } /* this is expected to be a QName or LCName identifier */ modname = get_cur_modname(pcb); objname = TK_CUR_VAL(pcb->tkc); /* find the child node and add it to the curtop */ childobj = obj_find_child(curobj, modname, objname); if (!childobj) { res = ERR_NCX_DEF_NOT_FOUND; continue; } childval = val_new_value(); if (!childval) { res = ERR_INTERNAL_MEM; continue; } val_init_from_template(childval, childobj); val_add_child(childval, curtop); /* get the = sign */ res = TK_ADV(pcb->tkc); if (res != NO_ERR) { continue; } if (TK_CUR_TYP(pcb->tkc) != TK_TT_EQUAL) { res = SET_ERROR(ERR_INTERNAL_VAL); continue; } /* get the key leaf value */ res = TK_ADV(pcb->tkc); if (res != NO_ERR) { continue; } if (!TK_CUR_STR(pcb->tkc)) { res = SET_ERROR(ERR_INTERNAL_VAL); continue; } /* set the new leaf with the value */ res = val_set_simval(childval, obj_get_typdef(childobj), obj_get_nsid(childobj), obj_get_name(childobj), TK_CUR_VAL(pcb->tkc)); if (res != NO_ERR) { continue; } /* get the closing predicate bracket */ res = TK_ADV(pcb->tkc); if (res != NO_ERR) { continue; } if (TK_CUR_TYP(pcb->tkc) != TK_TT_RBRACK) { res = SET_ERROR(ERR_INTERNAL_VAL); continue; } /* check any more predicates or path steps */ res = TK_ADV(pcb->tkc); if (res != NO_ERR) { /* no more steps, stopped at an OK spot */ done = TRUE; done2 = TRUE; res = NO_ERR; continue; } switch (TK_CUR_TYP(pcb->tkc)) { case TK_TT_LBRACK: /* get another predicate */ break; case TK_TT_FSLASH: /* done with predicates for now * setup the next path step */ res = TK_ADV(pcb->tkc); if (res != NO_ERR) { continue; } done2 = TRUE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } if(curtop->obj->objtype == OBJ_TYP_LIST) { res = val_gen_index_chain(curtop->obj, curtop); } } /* cleanup and exit */ if (res != NO_ERR && top) { val_free_value(top); top = NULL; } if (LOGDEBUG2 && top) { log_debug2("\nval_inst for expr '%s'\n", pcb->exprstr); val_dump_value(top, NCX_DEF_INDENT); } if (retres != NULL) { *retres = res; } return top; } /* xpath_yang_make_instanceid_val */ /******************************************************************** * FUNCTION xpath_yang_get_namespaces * * Get the namespace URI IDs used in the specified * XPath expression; * * usually an instance-identifier or schema-instance node * but this function simply reports all the TK_TT_MSTRING * tokens that have an nsid set * * The XPath pcb must be previously parsed and found valid * * INPUTS: * pcb == the XPath parser control block to use * nsid_array == address of return array of xmlns_id_t * max_nsids == number of NSIDs that can be held * num_nsids == address of return number of NSIDs * written to the buffer. No duplicates * will be present in the buffer * * OUTPUTS: * nsid_array[0..*num_nsids] == returned NSIDs used * in the XPath expression * *num_nsids == number of NSIDs written to the array * * RETURNS: * status *********************************************************************/ status_t xpath_yang_get_namespaces (const xpath_pcb_t *pcb, xmlns_id_t *nsid_array, uint32 max_nsids, uint32 *num_nsids) { const tk_token_t *tk; boolean done, found; uint32 i, next; xmlns_id_t cur_nsid; status_t res; #ifdef DEBUG if (!pcb || !nsid_array || !num_nsids) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!pcb->tkc) { return SET_ERROR(ERR_INTERNAL_VAL); } if (max_nsids == 0) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif if (pcb->parseres != NO_ERR) { return pcb->parseres; } res = NO_ERR; next = 0; *num_nsids = 0; done = FALSE; for (tk = (const tk_token_t *)dlq_firstEntry(&pcb->tkc->tkQ); tk != NULL && !done; tk = (const tk_token_t *)dlq_nextEntry(tk)) { /* only MSTRING and QVARBIND tokens have relevant NSID fields * as used in instance-identifier or schema-instance * path syntax */ switch (tk->typ) { case TK_TT_MSTRING: case TK_TT_QVARBIND: case TK_TT_NCNAME_STAR: break; default: continue; } cur_nsid = tk->nsid; if (cur_nsid == 0) { /* this token never had an NSID set */ continue; } /* check if this NSID already recorded */ found = FALSE; for (i = 0; i < next && !found; i++) { if (nsid_array[i] == cur_nsid) { found = TRUE; } } if (found) { continue; } /* need to add this entry * check if there would be a buffer overflow */ if (next >= max_nsids) { res = ERR_BUFF_OVFL; done = TRUE; } else { nsid_array[next++] = cur_nsid; } } *num_nsids = next; return res; } /* xpath_yang_get_namespaces */ /* END xpath_yang.c */ yuma123_2.14/netconf/src/ncx/cli.h0000664000175000017500000003145514770023131017066 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_cli #define _H_cli /* FILE: cli.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* command line interpreter parsing to internal val_value_t format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 21-oct-05 abb Begun 09-feb-06 abb Change from xmlTextReader to rpc_agt callback API format 10-feb-07 abb Split out common functions from agt_ps_parse.h 29-jul-08 abb change to cli.h; remove all by CLI parsing; conversion from NCX parmset to YANG object 07-feb-09 abb Add cli_rawparm_t and cli_parse_raw for bootstrap CLI support */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_runstack #include "runstack.h" #endif #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* value only of full test values for the valonly parameter */ #define VALONLY TRUE #define FULLTEST FALSE /* plain or script values for the climode parameter */ #define SCRIPTMODE TRUE #define PLAINMODE FALSE /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* CLI parsing modes */ typedef enum cli_mode_t_ { CLI_MODE_NONE, CLI_MODE_PROGRAM, /* real argc, argv */ CLI_MODE_COMMAND /* called from yangcli command parser */ } cli_mode_t; /* used for bootstrap CLI parms only, no validation */ typedef struct cli_rawparm_t_ { dlq_hdr_t qhdr; const char *name; char *value; boolean hasvalue; int32 count; } cli_rawparm_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION cli_new_rawparm * * bootstrap CLI support * Malloc and init a raw parm entry * * INPUTS: * name == name of parm (static const string) * * RETURNS: * new parm entry, NULL if malloc failed *********************************************************************/ extern cli_rawparm_t * cli_new_rawparm (const xmlChar *name); /******************************************************************** * FUNCTION cli_new_empty_rawparm * * Malloc and init a raw parm entry that has no value (NCX_BT_EMPTY) * * INPUTS: * name == name of parm (static const string) * * RETURNS: * new parm entry, NULL if malloc failed *********************************************************************/ extern cli_rawparm_t * cli_new_empty_rawparm (const xmlChar *name); /******************************************************************** * FUNCTION cli_free_rawparm * * Clean and free a raw parm entry * * INPUTS: * parm == raw parm entry to free *********************************************************************/ extern void cli_free_rawparm (cli_rawparm_t *parm); /******************************************************************** * FUNCTION cli_clean_rawparmQ * * Clean and free a Q of raw parm entries * * INPUTS: * parmQ == Q of raw parm entry to free *********************************************************************/ extern void cli_clean_rawparmQ (dlq_hdr_t *parmQ); /******************************************************************** * FUNCTION cli_find_rawparm * * Find the specified raw parm entry * * INPUTS: * name == object name to really use * parmQ == Q of cli_rawparm_t * * RETURNS: * raw parm entry if found, NULL if not *********************************************************************/ extern cli_rawparm_t * cli_find_rawparm (const xmlChar *name, dlq_hdr_t *parmQ); /******************************************************************** * FUNCTION cli_parse_raw * * Generate N sets of variable/value pairs for the * specified boot-strap CLI parameters * * There are no modules loaded yet, and nothing * has been initialized, not even logging * This function is almost the first thing done * by the application * * CLI Syntax Supported * * [prefix] parmname * * [prefix] parmname=value * * prefix == 0 to 2 dashes foo -foo --foo * parmname == any valid NCX identifier string * value == string * * No spaces are allowed after 'parmname' if 'value' is entered * Each parameter is allowed to occur zero or one times. * If multiple instances of a parameter are entered, then * the last one entered will win. * The 'autocomp' parameter is set to TRUE * * The 'value' string cannot be split across multiple argv segments. * Use quotation chars within the CLI shell to pass a string * containing whitespace to the CLI parser: * * --foo="quoted string if whitespace needed" * --foo="quoted string if setting a variable \ * as a top-level assignment" * --foo=unquoted-string-without-whitespace * * - There are no 1-char aliases for CLI parameters. * - Position-dependent, unnamed parameters are not supported * * INPUTS: * argc == number of strings passed in 'argv' * argv == array of command line argument strings * parmQ == Q of cli_rawparm_t entries * that should be used to validate the CLI input * and store the results * * OUTPUTS: * *rawparm (within parmQ): * - 'value' will be recorded if it is present * - count will be set to the number of times this * parameter was entered (set even if no value) * * RETURNS: * status *********************************************************************/ extern status_t cli_parse_raw (int argc, char *argv[], dlq_hdr_t *rawparmQ); /******************************************************************** * FUNCTION cli_parse * * schema based CLI support * Generate 1 val_value_t struct from a Unix Command Line, * which should conform to the specified obj_template_t definition. * * For CLI interfaces, only one container object can be specified * at this time. * * CLI Syntax Supported * * [prefix] parmname [separator] [value] * * prefix == 0 to 2 dashes foo -foo --foo * parmname == any valid NCX identifier string * separator == - equals char (=) * - whitespace (sp, ht) * value == any NCX number or NCX string * == 'enum' data type * == not present (only for 'flag' data type) * == extended * This value: string will converted to appropriate * simple type in val_value_t format. * * The format "--foo=bar" must be processed within one argv segment. * If the separator is a whitespace char, then the next string * in the argv array must contain the expected value. * Whitespace can be preserved using single or double quotes * * DESIGN NOTES: * * 1) parse the (argc, argv) input against the specified object * 2) add any missing mandatory and condition-met parameters * to the container. * 3) Add defaults if valonly is false * 4) Validate any 'choice' constructs within the parmset * 5) Validate the proper number of instances (missing or extra) * (unless valonly is TRUE) * * The 'value' string cannot be split across multiple argv segments. * Use quotation chars within the CLI shell to pass a string * containing whitespace to the CLI parser: * * --foo="quoted string if whitespace needed" * --foo="quoted string if setting a variable \ * as a top-level assignment" * --foo=unquoted-string-without-whitespace * * The input is parsed against the specified obj_template_t struct. * A val_value_t tree is built as the input is read. * Each parameter is syntax checked as is is parsed. * * If possible, the parser will skip to next parmameter in the parmset, * in order to support 'continue-on-error' type of operations. * * Any agent callback functions that might be registered for * the specified container (or its sub-nodes) are not called * by this function. This must be done after this function * has been called, and returns NO_ERR. * * INPUTS: * rcxt == runstack context to use * argc == number of strings passed in 'argv' * argv == array of command line argument strings * obj == obj_template_t of the container * that should be used to validate the input * against the child nodes of this container * valonly == TRUE if only the values presented should * be checked, no defaults, missing parms (Step 1 & 2 only) * == FALSE if all the tests and procedures should be done * autocomp == TRUE if parameter auto-completion should be * tried if any specified parameters are not matches * for the specified parmset * == FALSE if exact match only is desired * mode == CLI_MODE_PROGRAM if calling with real (argc, argv) * parameters; these may need some prep work * == CLI_MODE_COMMAND if calling from yangcli or * some other internal command parser. * These strings will not be preped at all * * status == pointer to status_t to get the return value * * OUTPUTS: * *status == the final function return status * * Just as the NETCONF parser does, the CLI parser will not add a parameter * to the val_value_t if any errors occur related to the initial parsing. * * RETURNS: * pointer to the malloced and filled in val_value_t *********************************************************************/ extern val_value_t * cli_parse (runstack_context_t *rcxt, int argc, char *argv[], obj_template_t *obj, boolean valonly, boolean script, boolean autocomp, cli_mode_t mode, status_t *status); /******************************************************************** * FUNCTION cli_parse_parm * * Create a val_value_t struct for the specified parm value, * and insert it into the parent container value * * ONLY CALLED FROM CLI PARSING FUNCTIONS IN ncxcli.c * ALLOWS SCRIPT EXTENSIONS TO BE PRESENT * * INPUTS: * rcxt == runstack context to use * val == parent value struct to adjust * parm == obj_template_t descriptor for the missing parm * strval == string representation of the parm value * (may be NULL if parm btype is NCX_BT_EMPTY * script == TRUE if CLI script mode * == FALSE if CLI plain mode * * OUTPUTS: * A new val_value_t will be inserted in the val->v.childQ * as required to fill in the parm. * * RETURNS: * status *********************************************************************/ extern status_t cli_parse_parm (runstack_context_t *rcxt, val_value_t *val, obj_template_t *obj, const xmlChar *strval, boolean script); /******************************************************************** * FUNCTION cli_parse_parm_ex * * Create a val_value_t struct for the specified parm value, * and insert it into the parent container value * Allow different bad data error handling vioa parameter * * ONLY CALLED FROM CLI PARSING FUNCTIONS IN ncxcli.c * ALLOWS SCRIPT EXTENSIONS TO BE PRESENT * * INPUTS: * rcxt == runstack context to use * val == parent value struct to adjust * parm == obj_template_t descriptor for the missing parm * strval == string representation of the parm value * (may be NULL if parm btype is NCX_BT_EMPTY * script == TRUE if CLI script mode * == FALSE if CLI plain mode * bad_data == enum defining how bad data should be handled * * OUTPUTS: * A new val_value_t will be inserted in the val->v.childQ * as required to fill in the parm. * * RETURNS: * status *********************************************************************/ extern status_t cli_parse_parm_ex (runstack_context_t *rcxt, val_value_t *val, obj_template_t *obj, const xmlChar *strval, boolean script, ncx_bad_data_t bad_data); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_cli */ yuma123_2.14/netconf/src/ncx/yang_grp.c0000664000175000017500000007031014770023131020111 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yang_grp.c YANG module parser, grouping statement support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 09dec07 abb begun; start from yang_typ.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_appinfo #include "ncx_appinfo.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yang_grp #include "yang_grp.h" #endif #ifndef _H_yang_obj #include "yang_obj.h" #endif #ifndef _H_yang_typ #include "yang_typ.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define YANG_GRP_DEBUG 1 /* #define YANG_GRP_USES_DEBUG 1 */ #endif /******************************************************************** * * * M A C R O S * * * *********************************************************************/ /* used in parser routines to decide if processing can continue * will exit the function if critical error or continue if not */ #define CHK_GRP_EXIT(res, retres) \ if (res != NO_ERR) { \ if (res < ERR_LAST_SYS_ERR || res==ERR_NCX_EOF) { \ grp_free_template(grp); \ return res; \ } else { \ retres = res; \ } \ } /******************************************************************** * FUNCTION follow_loop * * Follow the uses chain, looking for a loop * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * grp == grp_template_t that this 'obj' is using * * RETURNS: * status of the operation *********************************************************************/ static status_t follow_loop (tk_chain_t *tkc, ncx_module_t *mod, grp_template_t *grp, grp_template_t *testgrp) { obj_template_t *testobj; status_t res, retres; retres = NO_ERR; for (testobj = (obj_template_t *)dlq_firstEntry(&grp->datadefQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { if (testobj->objtype != OBJ_TYP_USES) { continue; } if (testobj->def.uses->grp == testgrp) { log_error("\nError: grouping '%s'" " on line %u loops in uses, defined " "in module %s, line %u", testgrp->name, testgrp->tkerr.linenum, testobj->tkerr.mod->name, testobj->tkerr.linenum); retres = ERR_NCX_DEF_LOOP; tkc->curerr = &testgrp->tkerr; ncx_print_errormsg(tkc, mod, retres); } else if (testobj->def.uses->grp) { res = follow_loop(tkc, mod, testobj->def.uses->grp, testgrp); CHK_EXIT(res, retres); } } return retres; } /* follow_loop */ /******************************************************************** * FUNCTION check_chain_loop * * Check the 'group' object and determine if there is a chained loop * where another group eventually uses the group that started * the test. * * grouping A { * uses B; * } * * grouping B { * uses C; * } * * grouping C { * uses A; * } * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * obj == 'uses' obj_template containing the ref to 'grp' * grp == grp_template_t that this 'obj' is using * * RETURNS: * status of the operation *********************************************************************/ static status_t check_chain_loop (tk_chain_t *tkc, ncx_module_t *mod, grp_template_t *grp) { status_t res; #ifdef DEBUG if (!tkc || !grp) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = follow_loop(tkc, mod, grp, grp); return res; } /* check_chain_loop */ /*************** E X T E R N A L F U N C T I O N S ***********/ /******************************************************************** * FUNCTION yang_grp_consume_grouping * * 2nd pass parsing * Parse the next N tokens as a grouping-stmt * Create a grp_template_t struct and add it to the specified Q * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'grouping' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == queue will get the grp_template_t * parent == parent object or NULL if top-level grouping-stmt * * RETURNS: * status of the operation *********************************************************************/ status_t yang_grp_consume_grouping (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent) { grp_template_t *grp, *testgrp; typ_template_t *testtyp; const xmlChar *val; const char *expstr; yang_stmt_t *stmt; tk_type_t tktyp; boolean done, stat, desc, ref; status_t res, retres; #ifdef DEBUG if (!pcb || !tkc || !mod || !que) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif grp = NULL; testgrp = NULL; val = NULL; expstr = "keyword"; done = FALSE; stat = FALSE; desc = FALSE; ref = FALSE; res = NO_ERR; retres = NO_ERR; /* Get a new grp_template_t to fill in */ grp = grp_new_template(); if (!grp) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&grp->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); grp->parent = parent; grp->istop = (que == &mod->groupingQ) ? TRUE : FALSE; /* Get the mandatory grouping name */ res = yang_consume_id_string(tkc, mod, &grp->name); CHK_GRP_EXIT(res, retres); /* Get the starting left brace for the sub-clauses * or a semi-colon to end the grouping clause */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); grp_free_template(grp); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: done = TRUE; break; case TK_TT_LBRACE: break; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; } /* get the grouping statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); grp_free_template(grp); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); grp_free_template(grp); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &grp->appinfoQ); CHK_GRP_EXIT(res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_TYPEDEF)) { res = yang_typ_consume_typedef(pcb, tkc, mod, &grp->typedefQ); } else if (!xml_strcmp(val, YANG_K_GROUPING)) { res = yang_grp_consume_grouping(pcb, tkc, mod, &grp->groupingQ, parent); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &grp->status, &stat, &grp->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &grp->descr, &desc, &grp->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &grp->ref, &ref, &grp->appinfoQ); } else if (!xml_strcmp(val, YANG_K_NOTIFICATION)) { res = yang_obj_consume_notification(pcb, tkc, mod, &grp->datadefQ, parent, grp); } else { res = yang_obj_consume_datadef_grp(pcb, tkc, mod, &grp->datadefQ, parent, grp); } CHK_GRP_EXIT(res, retres); } /* save or delete the grp_template_t struct */ if (grp->name && ncx_valid_name2(grp->name)) { boolean errone; testgrp = ncx_find_grouping_que(que, grp->name); if (testgrp == NULL) { errone = FALSE; testgrp = ncx_find_grouping(mod, grp->name, TRUE); } else { errone = TRUE; } if (testgrp) { if (errone) { log_error("\nError: grouping '%s' already " "defined at line %u", testgrp->name, testgrp->tkerr.linenum); } else { log_error("\nError: grouping '%s' already " "defined in '%s' at line %u", testgrp->name, testgrp->tkerr.mod->name, testgrp->tkerr.linenum); } retres = ERR_NCX_DUP_ENTRY; ncx_print_errormsg(tkc, mod, retres); grp_free_template(grp); } else { for (testgrp = (grp_template_t *) dlq_firstEntry(&grp->groupingQ); testgrp != NULL; testgrp = (grp_template_t *)dlq_nextEntry(testgrp)) { testgrp->parentgrp = grp; } for (testtyp = (typ_template_t *) dlq_firstEntry(&grp->typedefQ); testtyp != NULL; testtyp = (typ_template_t *)dlq_nextEntry(testtyp)) { testtyp->grp = grp; } #ifdef YANG_GRP_DEBUG if (LOGDEBUG4) { log_debug4("\nyang_grp: adding grouping (%s) to mod (%s)", grp->name, mod->name); } #endif dlq_enque(grp, que); /* may have some errors */ if (mod->stmtmode && que==&mod->groupingQ) { /* save stmt record for top-level groupings only */ stmt = yang_new_grp_stmt(grp); if (stmt) { dlq_enque(stmt, &mod->stmtQ); } else { log_error("\nError: malloc failure for grp_stmt"); retres = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, retres); } } } } else { grp_free_template(grp); } return retres; } /* yang_grp_consume_grouping */ /******************************************************************** * FUNCTION yang_grp_resolve_groupings * * 3rd pass parsing * Analyze the entire 'groupingQ' within the module struct * Finish all the clauses within this struct that * may have been defered because of possible forward references * * Any uses or augment within the grouping is deferred * until later passes because of forward references * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * groupingQ == Q of grp_template_t structs to check * parent == obj_template containing this groupingQ * == NULL if this is a module-level groupingQ * * RETURNS: * status of the operation *********************************************************************/ status_t yang_grp_resolve_groupings (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *groupingQ, obj_template_t *parent) { grp_template_t *grp, *nextgrp, *errgrp; obj_template_t *testobj; status_t res, retres; #ifdef DEBUG if (!pcb || !tkc || !mod || !groupingQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; retres = NO_ERR; /* go through the groupingQ and check the local types/groupings * and data-def statements */ for (grp = (grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL; grp = (grp_template_t *)dlq_nextEntry(grp)) { #ifdef YANG_GRP_DEBUG if (LOGDEBUG4) { log_debug4("\nyang_grp: resolve grouping '%s'", (grp->name) ? grp->name : EMPTY_STRING); } #endif /* check the appinfoQ */ res = ncx_resolve_appinfoQ(pcb, tkc, mod, &grp->appinfoQ); CHK_EXIT(res, retres); /* check any local typedefs */ res = yang_typ_resolve_typedefs_grp(pcb, tkc, mod, &grp->typedefQ, parent, grp); CHK_EXIT(res, retres); /* check any local groupings */ res = yang_grp_resolve_groupings(pcb, tkc, mod, &grp->groupingQ, parent); CHK_EXIT(res, retres); /* check any local objects */ res = yang_obj_resolve_datadefs(pcb, tkc, mod, &grp->datadefQ); CHK_EXIT(res, retres); } /* go through and check grouping shadow error * this cannot be checked until the previous loop completes */ for (grp = (grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL; grp = (grp_template_t *)dlq_nextEntry(grp)) { res = NO_ERR; errgrp = NULL; nextgrp = grp->parentgrp; while (nextgrp && res==NO_ERR) { if (!xml_strcmp(nextgrp->name, grp->name)) { res = ERR_NCX_DUP_ENTRY; errgrp = nextgrp; } else if (&nextgrp->groupingQ != groupingQ) { /* check local Q if it is at least 2 layers up */ errgrp = ncx_find_grouping_que(&nextgrp->groupingQ, grp->name); if (errgrp) { res = ERR_NCX_DUP_ENTRY; } } nextgrp = nextgrp->parentgrp; } if (res != NO_ERR) { log_error("\nError: local grouping '%s' shadows" " definition on line %u", grp->name, errgrp->tkerr.linenum); tkc->curerr = &grp->tkerr; ncx_print_errormsg(tkc, mod, res); } else if (parent) { testobj = parent->parent; if (testobj) { nextgrp = obj_find_grouping(testobj, grp->name); if (nextgrp) { log_error("\nError: local grouping '%s' shadows" " definition on line %u", grp->name, nextgrp->tkerr.linenum); tkc->curerr = &grp->tkerr; ncx_print_errormsg(tkc, mod, res); } } } /* check nested groupings for module-level conflict */ if (grp->parent) { nextgrp = ncx_find_grouping(mod, grp->name, TRUE); if (nextgrp) { log_error("\nError: local grouping '%s' shadows" " module definition in '%s' on line %u", grp->name, nextgrp->tkerr.mod->name, nextgrp->tkerr.linenum); res = ERR_NCX_DUP_ENTRY; tkc->curerr = &grp->tkerr; ncx_print_errormsg(tkc, mod, res); } CHK_EXIT(res, retres); } } /* go through the groupingQ again and check for uses chain loops */ for (grp = (grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL; grp = nextgrp) { nextgrp = (grp_template_t *)dlq_nextEntry(grp); /* check any group/uses loops */ res = check_chain_loop(tkc, mod, grp); CHK_EXIT(res, retres); if (res != NO_ERR) { dlq_remove(grp); grp_free_template(grp); } } return retres; } /* yang_grp_resolve_groupings */ /******************************************************************** * FUNCTION yang_grp_resolve_complete * * 4th pass parsing * Analyze the entire 'groupingQ' within the module struct * Expand any uses and augment statements within the group and * validate as much as possible * * Completes processing for all the groupings * sust that it is safe to expand any uses clauses * within objects, via the yang_obj_resolve_final fn * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * groupingQ == Q of grp_template_t structs to check * parent == parent object contraining groupingQ (may be NULL) * * RETURNS: * status of the operation *********************************************************************/ status_t yang_grp_resolve_complete (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *groupingQ, obj_template_t *parent) { grp_template_t *grp; status_t res, retres; #ifdef DEBUG if (!pcb || !tkc || !mod || !groupingQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; retres = NO_ERR; /* go through the groupingQ and check the local types/groupings * and data-def statements */ for (grp = (grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL; grp = (grp_template_t *)dlq_nextEntry(grp)) { /* check any local groupings */ res = yang_grp_resolve_complete(pcb, tkc, mod, &grp->groupingQ, parent); CHK_EXIT(res, retres); } for (grp = (grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL; grp = (grp_template_t *)dlq_nextEntry(grp)) { #ifdef YANG_GRP_DEBUG if (LOGDEBUG4) { log_debug4("\nyang_grp_resolve: %s", grp->name); } #endif if (grp->expand_done) { #ifdef YANG_GRP_USES_DEBUG if (LOGDEBUG4) { log_debug4("\n skip expanded group %s", grp->name); } #endif continue; } /* check any local objects for uses clauses */ res = yang_obj_resolve_uses(pcb, tkc, mod, &grp->datadefQ); CHK_EXIT(res, retres); grp->expand_done = TRUE; #ifdef YANG_GRP_USES_DEBUG if (LOGDEBUG4) { log_debug4("\nyang_grp: '%s' after expand", grp->name); obj_dump_child_list(&grp->datadefQ, NCX_DEF_INDENT, NCX_DEF_INDENT); } #endif } return retres; } /* yang_grp_resolve_complete */ /******************************************************************** * FUNCTION yang_grp_resolve_final * * Analyze the entire 'groupingQ' within the module struct * Check final warnings etc. * * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * groupingQ == Q of grp_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ status_t yang_grp_resolve_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *groupingQ) { grp_template_t *grp; status_t res, retres; #ifdef DEBUG if (!pcb || !tkc || !mod || !groupingQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; retres = NO_ERR; /* go through the groupingQ and check the local types/groupings * and data-def statements */ for (grp = (grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL && res==NO_ERR; grp = (grp_template_t *)dlq_nextEntry(grp)) { /* check any local groupings */ res = yang_grp_resolve_final(pcb, tkc, mod, &grp->groupingQ); CHK_EXIT(res, retres); /* final check on all objects within groupings */ res = yang_obj_resolve_final(pcb, tkc, mod, &grp->datadefQ, TRUE); CHK_EXIT(res, retres); yang_check_obj_used(tkc, mod, &grp->typedefQ, &grp->groupingQ); } return retres; } /* yang_grp_resolve_final */ /******************************************************************** * FUNCTION yang_grp_check_nest_loop * * Check the 'uses' object and determine if it is contained * within the group being used. * * grouping A { * uses A; * } * * grouping B { * container C { * grouping BB { * uses B; * } * } * } * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * obj == 'uses' obj_template containing the ref to 'grp' * grp == grp_template_t that this 'obj' is using * * RETURNS: * status of the operation *********************************************************************/ status_t yang_grp_check_nest_loop (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, grp_template_t *grp) { obj_template_t *testobj; grp_template_t *testgrp; status_t retres; #ifdef DEBUG if (!tkc || !obj || !grp) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif testobj = obj; while (testobj) { if (testobj->grp == grp) { log_error("\nError: uses of '%s'" " is contained within that grouping, " "defined on line %u", grp->name, grp->tkerr.linenum); retres = ERR_NCX_DEF_LOOP; tkc->curerr = &obj->tkerr; ncx_print_errormsg(tkc, mod, retres); return retres; } else if (testobj->grp) { testgrp = testobj->grp->parentgrp; while (testgrp) { if (testgrp == grp) { log_error("\nError: uses of '%s'" " is contained within " "that grouping, defined on line %u", grp->name, grp->tkerr.linenum); retres = ERR_NCX_DEF_LOOP; tkc->curerr = &obj->tkerr; ncx_print_errormsg(tkc, mod, retres); return retres; } else { testgrp = testgrp->parentgrp; } } } testobj = testobj->parent; } return NO_ERR; } /* yang_grp_check_nest_loop */ /* END file yang_grp.c */ yuma123_2.14/netconf/src/ncx/ncx_appinfo.h0000664000175000017500000002237614770023131020625 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ncx_appinfo #define _H_ncx_appinfo /* FILE: ncx_appinfo.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Module Library Extension (Application Info) Utility Functions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17-feb-10 abb Begun; split out from ncx.c */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_appinfo #include "ncx_appinfo.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION ncx_new_appinfo * * Create an appinfo entry * * INPOUTS: * isclone == TRUE if this is for a cloned object * * RETURNS: * malloced appinfo entry or NULL if malloc error *********************************************************************/ extern ncx_appinfo_t * ncx_new_appinfo (boolean isclone); /******************************************************************** * FUNCTION ncx_free_appinfo * * Free an appinfo entry * * INPUTS: * appinfo == ncx_appinfo_t data structure to free *********************************************************************/ extern void ncx_free_appinfo (ncx_appinfo_t *appinfo); /******************************************************************** * FUNCTION ncx_find_appinfo * * Find an appinfo entry by name (First match is returned) * The entry returned is not removed from the Q * * INPUTS: * appinfoQ == pointer to Q of ncx_appinfo_t data structure to check * prefix == module prefix that defines the extension * == NULL to pick the first match (not expecting * appinfo name collisions) * varname == name string of the appinfo variable to find * * RETURNS: * pointer to the ncx_appinfo_t struct for the entry if found * NULL if the entry is not found *********************************************************************/ extern ncx_appinfo_t * ncx_find_appinfo (dlq_hdr_t *appinfoQ, const xmlChar *prefix, const xmlChar *varname); /******************************************************************** * FUNCTION ncx_find_const_appinfo * * Find an appinfo entry by name (First match is returned) * The entry returned is not removed from the Q * * INPUTS: * appinfoQ == pointer to Q of ncx_appinfo_t data structure to check * prefix == module prefix that defines the extension * == NULL to pick the first match (not expecting * appinfo name collisions) * varname == name string of the appinfo variable to find * * RETURNS: * pointer to the ncx_appinfo_t struct for the entry if found * NULL if the entry is not found *********************************************************************/ extern const ncx_appinfo_t * ncx_find_const_appinfo (const dlq_hdr_t *appinfoQ, const xmlChar *prefix, const xmlChar *varname); /******************************************************************** * FUNCTION ncx_find_next_appinfo * * Find the next instance of an appinfo entry by name * (First match is returned) * The entry returned is not removed from the Q * * INPUTS: * current == pointer to current ncx_appinfo_t data structure to check * prefix == module prefix that defines the extension * == NULL to pick the first match (not expecting * appinfo name collisions) * varname == name string of the appinfo variable to find * * RETURNS: * pointer to the ncx_appinfo_t struct for the entry if found * NULL if the entry is not found *********************************************************************/ extern const ncx_appinfo_t * ncx_find_next_appinfo (const ncx_appinfo_t *current, const xmlChar *prefix, const xmlChar *varname); /******************************************************************** * FUNCTION ncx_find_next_appinfo2 * * Find the next instance of an appinfo entry by name * (First match is returned) * The entry returned is not removed from the Q * * INPUTS: * current == pointer to current ncx_appinfo_t data structure to check * prefix == module prefix that defines the extension * == NULL to pick the first match (not expecting * appinfo name collisions) * varname == name string of the appinfo variable to find * * RETURNS: * pointer to the ncx_appinfo_t struct for the entry if found * NULL if the entry is not found *********************************************************************/ extern ncx_appinfo_t * ncx_find_next_appinfo2 (ncx_appinfo_t *current, const xmlChar *prefix, const xmlChar *varname); /******************************************************************** * FUNCTION ncx_clone_appinfo * * Clone an appinfo value * * INPUTS: * appinfo == ncx_appinfo_t data structure to clone * * RETURNS: * pointer to the malloced ncx_appinfo_t struct clone of appinfo * NULL if a malloc error *********************************************************************/ extern ncx_appinfo_t * ncx_clone_appinfo (ncx_appinfo_t *appinfo); /******************************************************************** * FUNCTION ncx_clean_appinfoQ * * Check an initialized appinfoQ for any entries * Remove them from the queue and delete them * * INPUTS: * appinfoQ == Q of ncx_appinfo_t data structures to free *********************************************************************/ extern void ncx_clean_appinfoQ (dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION ncx_consume_appinfo * * Check if an appinfo clause is present * * Save in appinfoQ if non-NULL * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == ncx_module_t in progress (NULL if none) * appinfoQ == queue to use for any found entries (may be NULL) * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncx_consume_appinfo (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION ncx_consume_appinfo2 * * Check if an appinfo clause is present * Do not backup the current token * The TK_TT_MSTRING token has not been seen yet * Called from yang_consume_semiapp * * Save in appinfoQ if non-NULL * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == ncx_module_t in progress (NULL if none) * appinfoQ == queue to use for any found entries (may be NULL) * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncx_consume_appinfo2 (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION ncx_resolve_appinfoQ * * Validate all the appinfo clauses present in the specified Q * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == ncx_module_t in progress * appinfoQ == queue to check * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncx_resolve_appinfoQ (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION ncx_get_appinfo_value * * Get the value string from an appinfo struct * * INPUTS: * appinfo == ncx_appinfo_t data structure to use * * RETURNS: * pointer to the string value if name * NULL if no value *********************************************************************/ extern const xmlChar * ncx_get_appinfo_value (const ncx_appinfo_t *appinfo); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_ncx_appinfo */ yuma123_2.14/netconf/src/ncx/tstamp.h0000664000175000017500000000752614770023131017631 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_tstamp #define _H_tstamp /* FILE: tstamp.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Timestamp utilities ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17-apr-06 abb begun */ #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define TSTAMP_MIN_SIZE 22 #define TSTAMP_DATE_SIZE 12 #define TSTAMP_SQL_SIZE 20 /******************************************************************** * FUNCTION tstamp_datetime * * Set the current date and time in an XML dateTime string format * * INPUTS: * buff == pointer to buffer to hold output * MUST BE AT LEAST 21 CHARS * OUTPUTS: * buff is filled in *********************************************************************/ extern void tstamp_datetime (xmlChar *buff); /******************************************************************** * FUNCTION tstamp_date * * Set the current date in an XML dateTime string format * * INPUTS: * buff == pointer to buffer to hold output * MUST BE AT LEAST 11 CHARS * OUTPUTS: * buff is filled in *********************************************************************/ extern void tstamp_date (xmlChar *buff); /******************************************************************** * FUNCTION tstamp_datetime_sql * * Set the current date and time in an XML dateTime string format * * INPUTS: * buff == pointer to buffer to hold output * MUST BE AT LEAST 20 CHARS * OUTPUTS: * buff is filled in *********************************************************************/ extern void tstamp_datetime_sql (xmlChar *buff); /******************************************************************** * FUNCTION tstamp_convert_to_utctime * * Check if the specified string is a valid dateTime or * date-and-time string is valid and if so, convert it * to UTC time * * INPUTS: * buff == pointer to buffer to check * isNegative == address of return negative date flag * res == address of return status * * OUTPUTS: * *isNegative == TRUE if a negative dateTime string is given * FALSE if no starting '-' sign found * *res == return status * * RETURNS: * malloced pointer to converted date time string * or NULL if some error *********************************************************************/ extern xmlChar * tstamp_convert_to_utctime (const xmlChar *timestr, boolean *isNegative, status_t *res); /******************************************************************** * FUNCTION tstamp_datetime_dirname * * Set the current date and time in an XML dateTime string format * * INPUTS: * buff == pointer to buffer to hold output * MUST BE AT LEAST 21 CHARS * OUTPUTS: * buff is filled in *********************************************************************/ extern void tstamp_datetime_dirname (xmlChar *buff); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_tstamp */ yuma123_2.14/netconf/src/ncx/val_get_leafref_targval.h0000664000175000017500000000034014770023131023131 0ustar vladimirvladimir#include #include #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" val_value_t* val_get_leafref_targval(val_value_t *leafref_val, val_value_t *root_val); yuma123_2.14/netconf/src/ncx/yang_typ.h0000664000175000017500000002620114770023131020142 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yang_typ #define _H_yang_typ /* FILE: yang_typ.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* YANG Module parser typedef and type statement support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 15-nov-07 abb Begun; start from yang_parse.c */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION yang_typ_consume_type * * Parse the next N tokens as a type clause * Add to the typ_template_t struct in progress * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'type' keyword * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * intypdef == struct that will get the type info * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_typ_consume_type (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef); /******************************************************************** * FUNCTION yang_typ_consume_metadata_type * * Parse the next token as a type declaration * for an ncx:metadata definition * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * intypdef == struct that will get the type info * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_typ_consume_metadata_type (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *intypdef); /******************************************************************** * FUNCTION yang_typ_consume_typedef * * Parse the next N tokens as a typedef clause * Create a typ_template_t struct and add it to the specified module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'typedef' keyword * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * que == queue will get the typ_template_t * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_typ_consume_typedef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que); /******************************************************************** * FUNCTION yang_typ_resolve_typedefs * * Analyze the entire typeQ within the module struct * Finish all the named types and range clauses, * which were left unfinished due to possible forward references * * Check all the types in the Q * If a type has validation errors, it will be removed * from the typeQ * * Algorithm for checking named types in 4 separate loops: * 1) resolve all open type name references * 2) check for any name loops in all named types * 3) check that all base types and builtin types * in each type chain are correct. Also check that * all restrictions given are correct for that type * 4) Check all range clauses and resolve all min/max * keyword uses to decimal numbers and validate that * each range is well-formed. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typeQ == Q of typ_template_t structs t0o check * parent == obj_template containing this typeQ * == NULL if this is a module-level typeQ * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_typ_resolve_typedefs (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *typeQ, obj_template_t *parent); /******************************************************************** * FUNCTION yang_typ_resolve_typedefs_final * * Analyze the entire typeQ within the module struct * Finish all default value checking that was not done before * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typeQ == Q of typ_template_t structs t0o check * parent == obj_template containing this typeQ * == NULL if this is a module-level typeQ * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_typ_resolve_typedefs_final (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *typeQ); /******************************************************************** * FUNCTION yang_typ_resolve_typedefs_grp * * Analyze the entire typeQ within the module struct * Finish all the named types and range clauses, * which were left unfinished due to possible forward references * * Check all the types in the Q * If a type has validation errors, it will be removed * from the typeQ * * Algorithm for checking named types in 4 separate loops: * 1) resolve all open type name references * 2) check for any name loops in all named types * 3) check that all base types and builtin types * in each type chain are correct. Also check that * all restrictions given are correct for that type * 4) Check all range clauses and resolve all min/max * keyword uses to decimal numbers and validate that * each range is well-formed. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typeQ == Q of typ_template_t structs t0o check * parent == obj_template containing this typeQ * == NULL if this is a module-level typeQ * grp == grp_template containing this typedef * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_typ_resolve_typedefs_grp (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *typeQ, obj_template_t *parent, grp_template_t *grp); /******************************************************************** * FUNCTION yang_typ_resolve_type * * Analyze the typdef within a single leaf or leaf-list statement * Finish if a named type, and/or range clauses present, * which were left unfinished due to possible forward references * * Algorithm for checking named types in 4 separate loops: * 1) resolve all open type name references * 2) check for any name loops in all named types * 3) check that all base types and builtin types * in each type chain are correct. Also check that * all restrictions given are correct for that type * 4) Check all range clauses and resolve all min/max * keyword uses to decimal numbers and validate that * each range is well-formed. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typdef == typdef struct from leaf or leaf-list to check * defval == default value string for this leaf (may be NULL) * obj == obj_template containing this typdef * == NULL if this is a top-level union typedef, * checking its nested unnamed type clauses * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_typ_resolve_type (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, const xmlChar *defval, obj_template_t *obj); /******************************************************************** * FUNCTION yang_typ_resolve_type_final * * Check default values for XPath types * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typdef == typdef struct from leaf or leaf-list to check * defval == default value string for this leaf (may be NULL) * obj == obj_template containing this typdef * == NULL if this is a top-level union typedef, * checking its nested unnamed type clauses * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_typ_resolve_type_final (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, const xmlChar *defval, obj_template_t *obj); /******************************************************************** * FUNCTION yang_typ_rangenum_ok * * Check a typdef for range definitions * and check if the specified number passes all * the range checks (if any) * * INPUTS: * typdef == typdef to check * num == number to check * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_typ_rangenum_ok (typ_def_t *typdef, const ncx_num_t *num); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yang_typ */ yuma123_2.14/netconf/src/ncx/obj.h0000664000175000017500000034570114770023131017073 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_obj #define _H_obj /* FILE: obj.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Data Object Support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 09-dec-07 abb Begun 21jul08 abb start obj-based rewrite */ #include "grp.h" #include "ncxconst.h" #include "ncxtypes.h" #include "status.h" #include "tk.h" #include "rpc.h" #include "typ.h" #include "xmlns.h" #include "xml_util.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* default vaule for config statement */ #define OBJ_DEF_CONFIG TRUE /* default vaule for mandatory statement */ #define OBJ_DEF_MANDATORY FALSE /* flags field in obj_template_t */ /* object is cloned from a grouping, for a uses statement */ #define OBJ_FL_CLONE bit0 /* def is cloned flag * == 0 : obj.def.foo is malloced, but the typdef is cloned * == 1 : obj.def.foo: foo is cloned */ #define OBJ_FL_DEFCLONE bit1 /* clone source * == 0 : cloned object from uses * == 1 : cloned object from augment */ #define OBJ_FL_AUGCLONE bit2 /* object is marked for deletion */ #define OBJ_FL_DELETED bit3 /* object is conditional, via a when-stmt expression */ #define OBJ_FL_CONDITIONAL bit4 /* object is a top-level definition within a module or submodule */ #define OBJ_FL_TOP bit5 /* object was entered with a 'kw name;' format and is * considered empty by the yangdump program */ #define OBJ_FL_EMPTY bit6 /* object has been visited by the yangdiff program */ #define OBJ_FL_SEEN bit7 /* object marked as changed by the yangdiff program */ #define OBJ_FL_DIFF bit8 /* object is marked as ncx:hidden */ #define OBJ_FL_HIDDEN bit9 /* object is marked as ncx:root */ #define OBJ_FL_ROOT bit10 /* object is marked as a password */ #define OBJ_FL_PASSWD bit11 /* object is marked as a CLI-only node */ #define OBJ_FL_CLI bit12 /* object is marked as an XSD list data type */ #define OBJ_FL_XSDLIST bit13 /* OBJ_TYP_LEAF object is being uses as a key */ #define OBJ_FL_KEY bit14 /* object is marked as abstract: not CLI or config data */ #define OBJ_FL_ABSTRACT bit15 /* object is marked as config set */ #define OBJ_FL_CONFSET bit16 /* object config value */ #define OBJ_FL_CONFIG bit17 /* object is marked as mandatory set */ #define OBJ_FL_MANDSET bit18 /* object mandatory value */ #define OBJ_FL_MANDATORY bit19 /* object used in a unique-stmt within a list */ #define OBJ_FL_UNIQUE bit20 /* object data type is an XPath string */ #define OBJ_FL_XPATH bit21 /* object data type is a QName string */ #define OBJ_FL_QNAME bit22 /* object data type is a schema-instance string */ #define OBJ_FL_SCHEMAINST bit23 /* object is tagged ncx:secure */ #define OBJ_FL_SECURE bit24 /* object is tagged ncx:very-secure */ #define OBJ_FL_VERY_SECURE bit25 /* object is tagged ncx:default-parm-equals-ok */ #define OBJ_FL_CLI_EQUALS_OK bit26 /* object is tagged ncx:sil-delete-children-first */ #define OBJ_FL_SIL_DELETE_CHILDREN_FIRST bit27 /* object is tagged as ncx:user-write with no create access */ #define OBJ_FL_BLOCK_CREATE bit28 /* object is tagged as ncx:user-write with no update access */ #define OBJ_FL_BLOCK_UPDATE bit29 /* object is tagged as ncx:user-write with no delete access */ #define OBJ_FL_BLOCK_DELETE bit30 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* enumeration for different YANG data def statement types * the enum order is significant!!! do not change!!! */ typedef enum obj_type_t_ { OBJ_TYP_NONE, OBJ_TYP_ANYDATA, OBJ_TYP_ANYXML, OBJ_TYP_CONTAINER, OBJ_TYP_LEAF, OBJ_TYP_LEAF_LIST, OBJ_TYP_LIST, /* last real database object */ OBJ_TYP_CHOICE, OBJ_TYP_CASE, /* last named database object */ OBJ_TYP_USES, OBJ_TYP_REFINE, /* child of uses only */ OBJ_TYP_NOTIF, OBJ_TYP_AUGMENT, OBJ_TYP_RPC, OBJ_TYP_RPCIO, } obj_type_t; /* enumeration for different YANG augment statement types */ typedef enum obj_augtype_t_ { OBJ_AUGTYP_NONE, OBJ_AUGTYP_RPCIN, OBJ_AUGTYP_RPCOUT, OBJ_AUGTYP_CASE, OBJ_AUGTYP_DATA } obj_augtype_t; /* One YANG list key component */ typedef struct obj_key_t_ { dlq_hdr_t qhdr; struct obj_template_t_ *keyobj; boolean seen; /* used by yangdiff */ } obj_key_t; /* One component in a YANG list unique target */ typedef struct obj_unique_comp_t_ { dlq_hdr_t qhdr; struct obj_template_t_ *unobj; xmlChar *xpath; /* saved unique str for this obj */ boolean isduplicate; /* will be ignored by server */ } obj_unique_comp_t; /* One component in a YANG list unique target */ typedef struct obj_unique_t_ { dlq_hdr_t qhdr; xmlChar *xpath; /* complete saved unique str */ dlq_hdr_t compQ; /* Q of obj_unique_comp_t */ boolean seen; /* needed by yangdiff */ boolean isconfig; /* constraint is on config */ ncx_error_t tkerr; } obj_unique_t; /* One YANG leaf-list default value */ typedef struct obj_leaflist_defval_t_ { dlq_hdr_t qhdr; xmlChar *defval; } obj_leaflist_defval_t; /* One YANG 'container' definition */ typedef struct obj_container_t_ { xmlChar *name; xmlChar *descr; xmlChar *ref; xmlChar *presence; dlq_hdr_t *typedefQ; /* Q of typ_template_t */ dlq_hdr_t *groupingQ; /* Q of grp_template_t */ dlq_hdr_t *datadefQ; /* Q of obj_template_t */ boolean datadefclone; ncx_status_t status; dlq_hdr_t mustQ; /* Q of xpath_pcb_t */ struct obj_template_t_ *defaultparm; } obj_container_t; /* One YANG 'leaf' or 'anyxml' definition */ typedef struct obj_leaf_t_ { xmlChar *name; xmlChar *units; xmlChar *defval; xmlChar *descr; xmlChar *ref; typ_def_t *typdef; ncx_status_t status; dlq_hdr_t mustQ; /* Q of xpath_pcb_t */ struct obj_template_t_ *leafrefobj; } obj_leaf_t; /* One YANG 'leaf-list' definition */ typedef struct obj_leaflist_t_ { xmlChar *name; xmlChar *units; xmlChar *descr; xmlChar *ref; typ_def_t *typdef; dlq_hdr_t *defvalsQ; /* Q of obj_leaflist_defval_t */ boolean ordersys; /* ordered-by system or user */ boolean minset; uint32 minelems; boolean maxset; uint32 maxelems; ncx_status_t status; dlq_hdr_t mustQ; /* Q of xpath_pcb_t */ struct obj_template_t_ *leafrefobj; } obj_leaflist_t; /* One YANG 'list' definition */ typedef struct obj_list_t_ { xmlChar *name; xmlChar *keystr; xmlChar *descr; xmlChar *ref; dlq_hdr_t *typedefQ; /* Q of typ_template_t */ dlq_hdr_t *groupingQ; /* Q of grp_template_t */ dlq_hdr_t *datadefQ; /* Q of obj_template_t */ dlq_hdr_t keyQ; /* Q of obj_key_t */ dlq_hdr_t uniqueQ; /* Q of obj_unique_t */ boolean datadefclone; boolean ordersys; /* ordered-by system or user */ boolean minset; uint32 minelems; boolean maxset; uint32 maxelems; ncx_status_t status; dlq_hdr_t mustQ; /* Q of xpath_pcb_t */ ncx_error_t keytkerr; } obj_list_t; /* One YANG 'choice' definition */ typedef struct obj_choice_t_ { xmlChar *name; xmlChar *defval; xmlChar *descr; xmlChar *ref; dlq_hdr_t *caseQ; /* Q of obj_template_t */ boolean caseQclone; ncx_status_t status; } obj_choice_t; /* One YANG 'case' definition */ typedef struct obj_case_t_ { xmlChar *name; xmlChar *descr; xmlChar *ref; dlq_hdr_t *datadefQ; /* Q of obj_template_t */ boolean nameclone; boolean datadefclone; ncx_status_t status; } obj_case_t; /* YANG uses statement struct */ typedef struct obj_uses_t_ { xmlChar *prefix; xmlChar *name; xmlChar *descr; xmlChar *ref; grp_template_t *grp; /* const back-ptr to grouping */ dlq_hdr_t *datadefQ; /* Q of obj_template_t */ ncx_status_t status; boolean expand_done; } obj_uses_t; /* YANG refine statement struct */ typedef struct obj_refine_t_ { xmlChar *target; struct obj_template_t_ *targobj; /* the token for each sub-clause is saved because * when the refine-stmt is parsed, the target is not * known yet so picking the correct variant * such as refine-leaf-stmts or refine-list-stmts * needs to wait until the resolve phase */ xmlChar *descr; ncx_error_t descr_tkerr; xmlChar *ref; ncx_error_t ref_tkerr; xmlChar *presence; ncx_error_t presence_tkerr; xmlChar *def; ncx_error_t def_tkerr; /* config and confset are in the object flags */ ncx_error_t config_tkerr; /* mandatory and mandset are in the object flags */ ncx_error_t mandatory_tkerr; uint32 minelems; ncx_error_t minelems_tkerr; /* also minset */ uint32 maxelems; ncx_error_t maxelems_tkerr; /* also maxset */ dlq_hdr_t mustQ; } obj_refine_t; /* YANG input-stmt or output-stmt struct */ typedef struct obj_rpcio_t_ { xmlChar *name; /* input or output */ dlq_hdr_t typedefQ; /* Q of typ_template_t */ dlq_hdr_t groupingQ; /* Q of gtp_template_t */ dlq_hdr_t datadefQ; /* Q of obj_template_t */ struct obj_template_t_ *defaultparm; } obj_rpcio_t; /* YANG rpc-stmt struct; used for augment and name collision detect */ typedef struct obj_rpc_t_ { xmlChar *name; xmlChar *descr; xmlChar *ref; ncx_status_t status; dlq_hdr_t typedefQ; /* Q of typ_template_t */ dlq_hdr_t groupingQ; /* Q of gtp_template_t */ dlq_hdr_t datadefQ; /* Q of obj_template_t */ /* internal fields for manager and agent */ xmlns_id_t nsid; boolean supported; /* mod loaded, not implemented */ } obj_rpc_t; /* YANG augment statement struct */ typedef struct obj_augment_t_ { xmlChar *target; xmlChar *descr; xmlChar *ref; struct obj_template_t_ *targobj; obj_augtype_t augtype; ncx_status_t status; dlq_hdr_t datadefQ; /* Q of obj_template_t */ #ifdef ENABLE_DIRECT_MUST_AUGMENT_EX dlq_hdr_t mustQ; /* Q of xpath_pcb_t */ boolean direct_must_augment_ex; #endif } obj_augment_t; /* One YANG 'notification' clause definition */ typedef struct obj_notif_t_ { xmlChar *name; xmlChar *descr; xmlChar *ref; ncx_status_t status; dlq_hdr_t typedefQ; /* Q of typ_template_t */ dlq_hdr_t groupingQ; /* Q of gtp_template_t */ dlq_hdr_t datadefQ; /* Q of obj_template_t */ } obj_notif_t; /* back-pointer to inherited if-feature statements */ typedef struct obj_iffeature_ptr_t_ { dlq_hdr_t qhdr; ncx_iffeature_t *iffeature; } obj_iffeature_ptr_t; /* back-pointer to inherited when statements */ typedef struct obj_xpath_ptr_t_ { dlq_hdr_t qhdr; struct xpath_pcb_t_ *xpath; } obj_xpath_ptr_t; /* One YANG data-def-stmt */ typedef struct obj_template_t_ { dlq_hdr_t qhdr; obj_type_t objtype; uint32 flags; /* see OBJ_FL_* definitions */ ncx_error_t tkerr; grp_template_t *grp; /* non-NULL == in a grp.datadefQ */ /* 3 back pointers */ struct obj_template_t_ *parent; struct obj_template_t_ *usesobj; struct obj_template_t_ *augobj; struct xpath_pcb_t_ *when; /* optional when clause */ dlq_hdr_t metadataQ; /* Q of obj_metadata_t */ dlq_hdr_t appinfoQ; /* Q of ncx_appinfo_t */ dlq_hdr_t iffeatureQ; /* Q of ncx_iffeature_t */ dlq_hdr_t inherited_iffeatureQ; /* Q of obj_iffeature_ptr_t */ dlq_hdr_t inherited_whenQ; /* Q of obj_xpath_ptr_t */ /* callbacks */ /* agt_rpc_fnset_t for RPC */ void *rpc_cbset; /* ...or agt_cb_fnset_node_t queue for data OBJ */ dlq_hdr_t cbsetQ; /* object module and namespace ID * assigned at runtime * this can be changed over and over as a * uses statement is expanded. The final * expansion into a real object will leave * the correct value in place */ struct ncx_module_t_ *mod; xmlns_id_t nsid; union def_ { obj_container_t *container; obj_leaf_t *leaf; obj_leaflist_t *leaflist; obj_list_t *list; obj_choice_t *choic; obj_case_t *cas; obj_uses_t *uses; obj_refine_t *refine; obj_augment_t *augment; obj_rpc_t *rpc; obj_rpcio_t *rpcio; obj_notif_t *notif; } def; } obj_template_t; /* One YANG metadata (XML attribute) node */ typedef struct obj_metadata_t_ { dlq_hdr_t qhdr; struct obj_template_t_ *parent; /* obj containing metadata */ xmlChar *name; typ_def_t *typdef; xmlns_id_t nsid; /* in case parent == NULL */ ncx_error_t tkerr; } obj_metadata_t; /* type of deviation for each deviate entry */ typedef enum obj_deviate_arg_t_ { OBJ_DARG_NONE, OBJ_DARG_ADD, OBJ_DARG_DELETE, OBJ_DARG_REPLACE, OBJ_DARG_NOT_SUPPORTED } obj_deviate_arg_t; /* YANG deviate statement struct */ typedef struct obj_deviate_t_ { dlq_hdr_t qhdr; /* the error info for each sub-clause is saved because * when the deviation-stmt is parsed, the target is not * known yet so picking the correct variant * such as type-stmt or refine-list-stmts * needs to wait until the resolve phase * */ ncx_error_t tkerr; boolean empty; obj_deviate_arg_t arg; ncx_error_t arg_tkerr; typ_def_t *typdef; ncx_error_t type_tkerr; xmlChar *units; ncx_error_t units_tkerr; xmlChar *defval; ncx_error_t default_tkerr; boolean config; ncx_error_t config_tkerr; boolean mandatory; ncx_error_t mandatory_tkerr; uint32 minelems; ncx_error_t minelems_tkerr; /* also minset */ uint32 maxelems; ncx_error_t maxelems_tkerr; /* also maxset */ dlq_hdr_t mustQ; /* Q of xpath_pcb_t */ dlq_hdr_t uniqueQ; /* Q of obj_unique_t */ dlq_hdr_t appinfoQ; /* Q of ncx_appinfo_t */ } obj_deviate_t; /* YANG deviate statement struct */ typedef struct obj_deviation_t_ { dlq_hdr_t qhdr; xmlChar *target; xmlChar *targmodname; obj_template_t *targobj; xmlChar *descr; xmlChar *ref; ncx_error_t tkerr; xmlChar *devmodname; /* set if not the targmod */ boolean empty; status_t res; dlq_hdr_t deviateQ; /* Q of obj_deviate_t */ dlq_hdr_t appinfoQ; /* Q of ncx_appinfo_t */ } obj_deviation_t; /* child or descendant node search walker function * * INPUTS: * obj == object node found in descendant search * cookie1 == cookie1 value passed to start of walk * cookie2 == cookie2 value passed to start of walk * * RETURNS: * TRUE if walk should continue * FALSE if walk should terminate */ typedef boolean (*obj_walker_fn_t) (obj_template_t *obj, void *cookie1, void *cookie2); /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION obj_new_template * * Malloc and initialize the fields in a an object template * * INPUTS: * objtype == the specific object type to create * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern obj_template_t * obj_new_template (obj_type_t objtype); /******************************************************************** * FUNCTION obj_free_template * * Scrub the memory in a obj_template_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * obj == obj_template_t data structure to free *********************************************************************/ extern void obj_free_template (obj_template_t *obj); /******************************************************************** * FUNCTION obj_find_template * * Find an object with the specified name * * INPUTS: * que == Q of obj_template_t to search * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find * * RETURNS: * pointer to obj_template_t or NULL if not found in 'que' *********************************************************************/ extern obj_template_t * obj_find_template (dlq_hdr_t *que, const xmlChar *modname, const xmlChar *objname); /******************************************************************** * FUNCTION obj_find_template_con * * Find an object with the specified name * Return a const pointer; used by yangdump * * INPUTS: * que == Q of obj_template_t to search * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find * * RETURNS: * pointer to obj_template_t or NULL if not found in 'que' *********************************************************************/ extern const obj_template_t * obj_find_template_con (dlq_hdr_t *que, const xmlChar *modname, const xmlChar *objname); /******************************************************************** * FUNCTION obj_find_template_test * * Find an object with the specified name * * INPUTS: * que == Q of obj_template_t to search * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find * * RETURNS: * pointer to obj_template_t or NULL if not found in 'que' *********************************************************************/ extern obj_template_t * obj_find_template_test (dlq_hdr_t *que, const xmlChar *modname, const xmlChar *objname); /******************************************************************** * FUNCTION obj_find_template_top * * Check if an obj_template_t in the mod->datadefQ or any * of the include files visible to this module * * Top-level access is not tracked, so the 'test' variable * is hard-wired to FALSE * * INPUTS: * mod == ncx_module to check * modname == module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * objname == object name to find * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern obj_template_t * obj_find_template_top (ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname); /******************************************************************** * FUNCTION obj_find_template_top_ex * * Check if an obj_template_t in the mod->datadefQ or any * of the include files visible to this module * * Top-level access is not tracked, so the 'test' variable * is hard-wired to FALSE * * INPUTS: * mod == ncx_module to check * modname == module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * objname == object name to find * match_names == enum for selected match names mode * alt_names == TRUE if alt-name should be checked in addition * to the YANG node name * == FALSE to check YANG names only * dataonly == TRUE to check just data nodes * FALSE to check all nodes * retres == address of return status * * OUTPUTS: * if retres not NULL, *retres set to return status * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern obj_template_t * obj_find_template_top_ex (ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, ncx_name_match_t match_names, boolean alt_names, boolean dataonly, status_t *retres); /******************************************************************** * FUNCTION obj_find_template_all * * Check if an obj_template_t in the mod->datadefQ or any * of the include files used within the entire main module * * Top-level access is not tracked, so the 'test' variable * is hard-wired to FALSE * * INPUTS: * mod == ncx_module to check * modname == module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * objname == object name to find * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern obj_template_t * obj_find_template_all (ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname); /******************************************************************** * FUNCTION obj_find_child * * Find a child object with the specified Qname * * !!! This function checks for accessible names only!!! * !!! That means child nodes of choice->case will be * !!! present instead of the choice name or case name * * INPUTS: * obj == obj_template_t to check * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find * * RETURNS: * pointer to obj_template_t or NULL if not found *********************************************************************/ extern obj_template_t * obj_find_child (obj_template_t *obj, const xmlChar *modname, const xmlChar *objname); /******************************************************************** * FUNCTION obj_find_child_ex * * Find a child object with the specified Qname * extended match modes * * !!! This function checks for accessible names only!!! * !!! That means child nodes of choice->case will be * !!! present instead of the choice name or case name * * INPUTS: * obj == obj_template_t to check * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find * match_names == enum for selected match names mode * alt_names == TRUE if alt-name should be checked in addition * to the YANG node name * == FALSE to check YANG names only * dataonly == TRUE to check just data nodes * FALSE to check all nodes * retres == address of return status * * OUTPUTS: * if retres not NULL, *retres set to return status * * RETURNS: * pointer to obj_template_t or NULL if not found *********************************************************************/ extern obj_template_t * obj_find_child_ex (obj_template_t *obj, const xmlChar *modname, const xmlChar *objname, ncx_name_match_t match_names, boolean alt_names, boolean dataonly, status_t *retres); /******************************************************************** * FUNCTION obj_find_child_str * * Find a child object with the specified Qname * * INPUTS: * obj == obj_template_t to check * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find, not Z-terminated * objnamelen == length of objname string * * RETURNS: * pointer to obj_template_t or NULL if not found *********************************************************************/ extern obj_template_t * obj_find_child_str (obj_template_t *obj, const xmlChar *modname, const xmlChar *objname, uint32 objnamelen); /******************************************************************** * FUNCTION obj_match_child_str * * Match a child object with the specified Qname * Find first command that matches all N chars of objname * * !!! This function checks for accessible names only!!! * !!! That means child nodes of choice->case will be * !!! present instead of the choice name or case name * * INPUTS: * obj == obj_template_t to check * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find, not Z-terminated * objnamelen == length of objname string * matchcount == address of return parameter match count * (may be NULL) * OUTPUTS: * if non-NULL: * *matchcount == number of parameters that matched * only the first match will be returned * * RETURNS: * pointer to obj_template_t or NULL if not found *********************************************************************/ extern obj_template_t * obj_match_child_str (obj_template_t *obj, const xmlChar *modname, const xmlChar *objname, uint32 objnamelen, uint32 *matchcount); /******************************************************************** * FUNCTION obj_first_child * * Get the first child object if the specified object * has any children * * !!!! SKIPS OVER AUGMENT AND USES !!!! * * INPUTS: * obj == obj_template_t to check * RETURNS: * pointer to first child obj_template_t or * NULL if not found *********************************************************************/ extern obj_template_t * obj_first_child (obj_template_t *obj); /******************************************************************** * FUNCTION obj_last_child * * Get the last child object if the specified object * has any children * * !!!! SKIPS OVER AUGMENT AND USES !!!! * * INPUTS: * obj == obj_template_t to check * RETURNS: * pointer to first child obj_template_t or * NULL if not found *********************************************************************/ extern obj_template_t * obj_last_child (obj_template_t *obj); /******************************************************************** * FUNCTION obj_next_child * * Get the next child object if the specified object * has any children * * !!!! SKIPS OVER AUGMENT AND USES !!!! * * INPUTS: * obj == obj_template_t to check * RETURNS: * pointer to next child obj_template_t or * NULL if not found *********************************************************************/ extern obj_template_t * obj_next_child (obj_template_t *obj); /******************************************************************** * FUNCTION obj_previous_child * * Get the previous child object if the specified object * has any children * * !!!! SKIPS OVER AUGMENT AND USES !!!! * * INPUTS: * obj == obj_template_t to check * RETURNS: * pointer to next child obj_template_t or * NULL if not found *********************************************************************/ extern obj_template_t * obj_previous_child (obj_template_t *obj); /******************************************************************** * FUNCTION obj_first_child_deep * * Get the first child object if the specified object * has any children. Look past choices and cases to * the real nodes within them * * !!!! SKIPS OVER AUGMENT AND USES AND CHOICES AND CASES !!!! * * INPUTS: * obj == obj_template_t to check * RETURNS: * pointer to first child obj_template_t or * NULL if not found *********************************************************************/ extern obj_template_t * obj_first_child_deep (obj_template_t *obj); /******************************************************************** * FUNCTION obj_next_child_deep * * Get the next child object if the specified object * has any children. Look past choice and case nodes * to the real nodes within them * * !!!! SKIPS OVER AUGMENT AND USES !!!! * * INPUTS: * obj == obj_template_t to check * * RETURNS: * pointer to next child obj_template_t or * NULL if not found *********************************************************************/ extern obj_template_t * obj_next_child_deep (obj_template_t *obj); /******************************************************************** * FUNCTION obj_find_all_children * * Find all occurances of the specified node(s) * within the children of the current node. * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * This function skips choice and case nodes and * only processes real data nodes * * INPUTS: * exprmod == module containing XPath expression * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == start node to check * modname == module name; * only matches in this module namespace * will be returned * == NULL: * namespace matching will be skipped * childname == name of child node to find * == NULL to match any child name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if childname == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * useroot == TRUE is it is safe to use the toproot * FALSE if not, use all moduleQ search instead * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ extern boolean obj_find_all_children (ncx_module_t *exprmod, obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *startnode, const xmlChar *modname, const xmlChar *childname, boolean configonly, boolean textmode, boolean useroot); /******************************************************************** * FUNCTION obj_find_all_ancestors * * Find all occurances of the specified node(s) * within the ancestors of the current node. * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * This function skips choice and case nodes and * only processes real data nodes * * INPUTS: * exprmod == module containing XPath object * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == start node to check * modname == module name; * only matches in this module namespace * will be returned * == NULL: * namespace matching will be skipped * name == name of ancestor node to find * == NULL to match any ancestor name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if name == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * useroot == TRUE is it is safe to use the toproot * FALSE if not, use all moduleQ search instead * orself == TRUE if axis is really ancestor-or-self * FALSE if axis is ancestor * fncalled == address of return function called flag * * OUTPUTS: * *fncalled set to TRUE if a callback function was called * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ extern boolean obj_find_all_ancestors (ncx_module_t *exprmod, obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode, boolean useroot, boolean orself, boolean *fncalled); /******************************************************************** * FUNCTION obj_find_all_descendants * * Find all occurances of the specified node(s) * within the descendants of the current node. * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * This function skips choice and case nodes and * only processes real data nodes * * INPUTS: * exprmod == module containing XPath expression * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == start node to check * modname == module name; * only matches in this module namespace * will be returned * == NULL: * namespace matching will be skipped * name == name of descendant node to find * == NULL to match any descendant name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if name == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * useroot == TRUE is it is safe to use the toproot * FALSE if not, use all moduleQ search instead * orself == TRUE if axis is really ancestor-or-self * FALSE if axis is ancestor * fncalled == address of return function called flag * * OUTPUTS: * *fncalled set to TRUE if a callback function was called * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ extern boolean obj_find_all_descendants (ncx_module_t *exprmod, obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode, boolean useroot, boolean orself, boolean *fncalled); /******************************************************************** * FUNCTION obj_find_all_pfaxis * * Find all occurances of the specified preceding * or following node(s). Could also be * within the descendants of the current node. * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * This function skips choice and case nodes and * only processes real data nodes * * INPUTS: * exprmod == module containing object * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == starting sibling node to check * modname == module name; * only matches in this module namespace * will be returned * == NULL: * namespace matching will be skipped * * name == name of preceding or following node to find * == NULL to match any name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if name == NULL * dblslash == TRUE if all decendents of the preceding * or following nodes should be checked * FALSE only 1 level is checked * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * axis == axis enum to use * fncalled == address of return function called flag * * OUTPUTS: * *fncalled set to TRUE if a callback function was called * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ extern boolean obj_find_all_pfaxis (ncx_module_t *exprmod, obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean dblslash, boolean textmode, boolean useroot, ncx_xpath_axis_t axis, boolean *fncalled); /******************************************************************** * FUNCTION obj_find_case * * Find a specified case arm by name * * INPUTS: * choic == choice struct to check * modname == name of the module that added this case (may be NULL) * casname == name of the case to find * * RETURNS: * pointer to obj_case_t for requested case, NULL if not found *********************************************************************/ extern obj_case_t * obj_find_case (obj_choice_t *choic, const xmlChar *modname, const xmlChar *casname); /******************************************************************** * FUNCTION obj_new_rpcio * * Malloc and initialize the fields in a an obj_rpcio_t * Fields are setup within the new obj_template_t, based * on the values in rpcobj * * INPUTS: * rpcobj == parent OBJ_TYP_RPC template * name == name string of the node (input or output) * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern obj_template_t * obj_new_rpcio (obj_template_t *rpcobj, const xmlChar *name); /******************************************************************** * FUNCTION obj_clean_datadefQ * * Clean and free all the obj_template_t structs in the specified Q * * INPUTS: * datadefQ == Q of obj_template_t to clean *********************************************************************/ extern void obj_clean_datadefQ (dlq_hdr_t *que); /******************************************************************** * FUNCTION obj_find_type * * Check if a typ_template_t in the obj typedefQ hierarchy * * INPUTS: * obj == obj_template using the typedef * typname == type name to find * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern typ_template_t * obj_find_type (obj_template_t *obj, const xmlChar *typname); /******************************************************************** * FUNCTION obj_first_typedef * * Get the first local typedef for this object, if any * * INPUTS: * obj == obj_template to use * * RETURNS: * pointer to first typ_template_t struct if present, NULL otherwise *********************************************************************/ extern typ_template_t * obj_first_typedef (obj_template_t *obj); /******************************************************************** * FUNCTION obj_find_grouping * * Check if a grp_template_t in the obj groupingQ hierarchy * * INPUTS: * obj == obj_template using the grouping * grpname == grouping name to find * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern grp_template_t * obj_find_grouping (obj_template_t *obj, const xmlChar *grpname); /******************************************************************** * FUNCTION obj_first_grouping * * Get the first local grouping if any * * INPUTS: * obj == obj_template to use * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern grp_template_t * obj_first_grouping (obj_template_t *obj); /******************************************************************** * FUNCTION obj_set_named_type * * Resolve type test * Called during phase 2 of module parsing * * INPUTS: * tkc == token chain * mod == module in progress * typname == name field from typ->name (may be NULL) * typdef == typdef in progress * parent == obj_template containing this typedef * == NULL if this is the top-level, use mod->typeQ * grp == grp_template containing this typedef * == NULL if the typedef is not contained in a grouping * * RETURNS: * status *********************************************************************/ extern status_t obj_set_named_type (tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *typname, typ_def_t *typdef, obj_template_t *parent, grp_template_t *grp); /******************************************************************** * FUNCTION obj_clone_template * * Clone an obj_template_t * Copy the pointers from the srcobj into the new obj * * If the mobj is non-NULL, then the non-NULL revisable * fields in the mobj struct will be merged into the new object * * INPUTS: * mod == module struct that is defining the new cloned data * this may be different than the module that will * contain the cloned data (except top-level objects) * srcobj == obj_template to clone * !!! This struct MUST NOT be deleted!!! * !!! Unless all of its clones are also deleted !!! * mobjQ == merge object Q (may be NULL) * datadefQ to check for OBJ_TYP_REFINE nodes * If the target of the refine node matches the * srcobj (e.g., from same grouping), then the * sub-clauses in that refinement-stmt that * are allowed to be revised will be checked * * RETURNS: * pointer to malloced clone obj_template_t * NULL if malloc failer error or internal error *********************************************************************/ extern obj_template_t * obj_clone_template (ncx_module_t *mod, obj_template_t *srcobj, dlq_hdr_t *mobjQ); /******************************************************************** * FUNCTION obj_clone_template_case * * Clone an obj_template_t but make sure it is wrapped * in a OBJ_TYP_CASE layer * * Copy the pointers from the srcobj into the new obj * * Create an OBJ_TYP_CASE wrapper if needed, * for a short-case-stmt data def * * If the mobj is non-NULL, then the non-NULL revisable * fields in the mobj struct will be merged into the new object * * INPUTS: * mod == module struct that is defining the new cloned data * this may be different than the module that will * contain the cloned data (except top-level objects) * srcobj == obj_template to clone * !!! This struct MUST NOT be deleted!!! * !!! Unless all of its clones are also deleted !!! * mobjQ == Q of obj_refine_t objects to merge (may be NULL) * only fields allowed to be revised will be checked * even if other fields are set in this struct * * RETURNS: * pointer to malloced clone obj_template_t * NULL if malloc failer error or internal error *********************************************************************/ extern obj_template_t * obj_clone_template_case (ncx_module_t *mod, obj_template_t *srcobj, dlq_hdr_t *mobjQ); /******************** obj_unique_t ********************/ /******************************************************************** * FUNCTION obj_new_unique * * Alloc and Init a obj_unique_t struct * * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ extern obj_unique_t * obj_new_unique (void); /******************************************************************** * FUNCTION obj_init_unique * * Init a obj_unique_t struct * * INPUTS: * un == obj_unique_t struct to init *********************************************************************/ extern void obj_init_unique (obj_unique_t *un); /******************************************************************** * FUNCTION obj_free_unique * * Free a obj_unique_t struct * * INPUTS: * un == obj_unique_t struct to free *********************************************************************/ extern void obj_free_unique (obj_unique_t *un); /******************************************************************** * FUNCTION obj_clean_unique * * Clean a obj_unique_t struct * * INPUTS: * un == obj_unique_t struct to clean *********************************************************************/ extern void obj_clean_unique (obj_unique_t *un); /******************************************************************** * FUNCTION obj_new_unique_comp * * Alloc and Init a obj_unique_comp_t struct * * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ extern obj_unique_comp_t * obj_new_unique_comp (void); /******************************************************************** * FUNCTION obj_free_unique_comp * * Free a obj_unique_comp_t struct * * INPUTS: * unc == obj_unique_comp_t struct to free *********************************************************************/ extern void obj_free_unique_comp (obj_unique_comp_t *unc); /******************************************************************** * FUNCTION obj_find_unique * * Find a specific unique-stmt * * INPUTS: * que == queue of obj_unique_t to check * xpath == relative path expression for the * unique node to find * * RETURNS: * pointer to found entry or NULL if not found *********************************************************************/ extern obj_unique_t * obj_find_unique (dlq_hdr_t *que, const xmlChar *xpath); /******************************************************************** * FUNCTION obj_first_unique * * Get the first unique-stmt for a list * * INPUTS: * listobj == (list) object to check for unique structs * * RETURNS: * pointer to found entry or NULL if not found *********************************************************************/ extern obj_unique_t * obj_first_unique (obj_template_t *listobj); /******************************************************************** * FUNCTION obj_next_unique * * Get the next unique-stmt for a list * * INPUTS: * un == current unique node * * RETURNS: * pointer to found entry or NULL if not found *********************************************************************/ extern obj_unique_t * obj_next_unique (obj_unique_t *un); /******************************************************************** * FUNCTION obj_first_unique_comp * * Get the first identifier in a unique-stmt for a list * * INPUTS: * un == unique struct to check * * RETURNS: * pointer to found entry or NULL if not found *********************************************************************/ extern obj_unique_comp_t * obj_first_unique_comp (obj_unique_t *un); /******************************************************************** * FUNCTION obj_next_unique_comp * * Get the next unique-stmt component for a list * * INPUTS: * uncomp == current unique component node * * RETURNS: * pointer to next entry or NULL if none *********************************************************************/ extern obj_unique_comp_t * obj_next_unique_comp (obj_unique_comp_t *uncomp); /******************************************************************** * FUNCTION obj_new_key * * Alloc and Init a obj_key_t struct * * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ extern obj_key_t * obj_new_key (void); /******************************************************************** * FUNCTION obj_free_key * * Free a obj_key_t struct * * INPUTS: * key == obj_key_t struct to free *********************************************************************/ extern void obj_free_key (obj_key_t *key); /******************************************************************** * FUNCTION obj_find_key * * Find a specific key component by key leaf identifier name * Assumes deep keys are not supported!!! * * INPUTS: * que == Q of obj_key_t to check * keycompname == key component name to find * * RETURNS: * pointer to found key component or NULL if not found *********************************************************************/ extern obj_key_t * obj_find_key (dlq_hdr_t *que, const xmlChar *keycompname); /******************************************************************** * FUNCTION obj_find_key2 * * Find a specific key component, check for a specific node * in case deep keys are supported, and to check for duplicates * * INPUTS: * que == Q of obj_key_t to check * keyobj == key component object to find * * RETURNS: * pointer to found key component or NULL if not found *********************************************************************/ extern obj_key_t * obj_find_key2 (dlq_hdr_t *que, obj_template_t *keyobj); /******************************************************************** * FUNCTION obj_first_key * * Get the first key record * * INPUTS: * obj == object to check * * RETURNS: * pointer to first key component or NULL if not found *********************************************************************/ extern obj_key_t * obj_first_key (obj_template_t *obj); /******************************************************************** * FUNCTION obj_first_ckey * * Get the first key record: Const version * * INPUTS: * obj == object to check * * RETURNS: * pointer to first key component or NULL if not found *********************************************************************/ extern const obj_key_t * obj_first_ckey (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_next_key * * Get the next key record * * INPUTS: * objkey == current key record * * RETURNS: * pointer to next key component or NULL if not found *********************************************************************/ extern obj_key_t * obj_next_key (obj_key_t *objkey); /******************************************************************** * FUNCTION obj_next_ckey * * Get the next key record: Const version * * INPUTS: * objkey == current key record * * RETURNS: * pointer to next key component or NULL if not found *********************************************************************/ extern const obj_key_t * obj_next_ckey (const obj_key_t *objkey); /******************************************************************** * FUNCTION obj_key_count * * Get the number of keys for this object * * INPUTS: * obj == object to check * * RETURNS: * number of keys in the obj_key_t Q *********************************************************************/ extern uint32 obj_key_count (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_key_count_to_root * * Check ancestor-or-self nodes until root reached * Find all lists; Count the number of keys * * INPUTS: * obj == object to start check from * RETURNS: * number of keys in ancestor-or-self nodes *********************************************************************/ extern uint32 obj_key_count_to_root (obj_template_t *obj); /******************************************************************** * FUNCTION obj_traverse_keys * * Check ancestor-or-self nodes until root reached * Find all lists; For each list, starting with the * closest to root, invoke the callback function * for each of the key objects in order * * INPUTS: * obj == object to start check from * cookie1 == cookie1 to pass to the callback function * cookie2 == cookie2 to pass to the callback function * walkerfn == walker callback function * returns FALSE to terminate traversal * *********************************************************************/ extern void obj_traverse_keys (obj_template_t *obj, void *cookie1, void *cookie2, obj_walker_fn_t walkerfn); /******************************************************************** * FUNCTION obj_any_rpcs * * Check if there are any RPC methods in the datadefQ * * INPUTS: * que == Q of obj_template_t to check * * RETURNS: * TRUE if any OBJ_TYP_RPC found, FALSE if not *********************************************************************/ extern boolean obj_any_rpcs (const dlq_hdr_t *datadefQ); /******************************************************************** * FUNCTION obj_any_notifs * * Check if there are any notifications in the datadefQ * * INPUTS: * que == Q of obj_template_t to check * * RETURNS: * TRUE if any OBJ_TYP_NOTIF found, FALSE if not *********************************************************************/ extern boolean obj_any_notifs (const dlq_hdr_t *datadefQ); /******************************************************************** * FUNCTION obj_new_deviate * * Malloc and initialize the fields in a an object deviate statement * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern obj_deviate_t * obj_new_deviate (void); /******************************************************************** * FUNCTION obj_free_deviate * * Clean and free an object deviate statement * * INPUTS: * deviate == pointer to the struct to clean and free *********************************************************************/ extern void obj_free_deviate (obj_deviate_t *deviate); /******************************************************************** * FUNCTION obj_get_deviate_arg * * Get the deviate-arg string from its enumeration * * INPUTS: * devarg == enumeration to convert * RETURNS: * const string version of the enum *********************************************************************/ extern const xmlChar * obj_get_deviate_arg (obj_deviate_arg_t devarg); /******************************************************************** * FUNCTION obj_new_deviation * * Malloc and initialize the fields in a an object deviation statement * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern obj_deviation_t * obj_new_deviation (void); /******************************************************************** * FUNCTION obj_free_deviation * * Clean and free an object deviation statement * * INPUTS: * deviation == pointer to the struct to clean and free *********************************************************************/ extern void obj_free_deviation (obj_deviation_t *deviation); /******************************************************************** * FUNCTION obj_clean_deviationQ * * Clean and free an Q of object deviation statements * * INPUTS: * deviationQ == pointer to Q of the structs to clean and free *********************************************************************/ extern void obj_clean_deviationQ (dlq_hdr_t *deviationQ); /******************************************************************** * FUNCTION obj_gen_object_id * * Malloc and Generate the object ID for an object node * * INPUTS: * obj == node to generate the instance ID for * buff == pointer to address of buffer to use * * OUTPUTS * *buff == malloced buffer with the instance ID * * RETURNS: * status *********************************************************************/ extern status_t obj_gen_object_id (const obj_template_t *obj, xmlChar **buff); /******************************************************************** * Malloc and Generate the object ID for an object node * Remove all conceptual OBJ_TYP_CHOICE and OBJ_TYP_CASE nodes * so the resulting string will represent the structure of the * value tree for XPath searching * * \param obj the node to generate the instance ID for * \param buff the pointer to address of buffer to use * \return status *********************************************************************/ extern status_t obj_gen_object_id_xpath (const obj_template_t *obj, xmlChar **buff); /******************************************************************** * Malloc and Generate the object ID for a unique-stmt test * * \param obj the node to generate the instance ID for * \param stopobj the ancestor node to stop at * \param buff the pointer to address of buffer to use * \return status *********************************************************************/ extern status_t obj_gen_object_id_unique (const obj_template_t *obj, const obj_template_t *stopobj, xmlChar **buff); /******************************************************************** * FUNCTION obj_gen_object_id_code * * Malloc and Generate the object ID for an object node * for C code usage * generate a unique name for C code; handles augments * * INPUTS: * mod == current module in progress * obj == node to generate the instance ID for * buff == pointer to address of buffer to use * * OUTPUTS * *buff == malloced buffer with the instance ID * * RETURNS: * status *********************************************************************/ extern status_t obj_gen_object_id_code (ncx_module_t *mod, const obj_template_t *obj, xmlChar **buff); /******************************************************************** * FUNCTION obj_copy_object_id * * Generate the object ID for an object node and copy to the buffer * copy an object ID to a buffer * * INPUTS: * obj == node to generate the instance ID for * buff == buffer to use * bufflen == size of buff * reallen == address of return length of actual identifier * (may be NULL) * * OUTPUTS * buff == filled in with the object ID * if reallen not NULL: * *reallen == length of identifier, even if error occurred * * RETURNS: * status *********************************************************************/ extern status_t obj_copy_object_id (const obj_template_t *obj, xmlChar *buff, uint32 bufflen, uint32 *reallen); /******************************************************************** * FUNCTION obj_copy_object_id_mod * * Generate the object ID for an object node and copy to the buffer * copy an object ID to a buffer; Use modname in object identifier * * INPUTS: * obj == node to generate the instance ID for * buff == buffer to use * bufflen == size of buff * reallen == address of return length of actual identifier * (may be NULL) * * OUTPUTS * buff == filled in with the object ID * if reallen not NULL: * *reallen == length of identifier, even if error occurred * * RETURNS: * status *********************************************************************/ extern status_t obj_copy_object_id_mod (const obj_template_t *obj, xmlChar *buff, uint32 bufflen, uint32 *reallen); /******************************************************************** * FUNCTION obj_gen_aughook_id * * Malloc and Generate the augment hook element name for * the specified object. This will be a child node of the * specified object. * * INPUTS: * obj == node to generate the augment hook ID for * buff == pointer to address of buffer to use * * OUTPUTS * *buff == malloced buffer with the instance ID * * RETURNS: * status *********************************************************************/ extern status_t obj_gen_aughook_id (const obj_template_t *obj, xmlChar **buff); /******************************************************************** * FUNCTION obj_get_name * * Get the name field for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * pointer to the name field, NULL if some error or unnamed *********************************************************************/ extern const xmlChar * obj_get_name (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_set_name * * Set the name field for this obj * * INPUTS: * obj == the specific object to set or change the name * objname == new name string to use * * RETURNS: * status *********************************************************************/ extern status_t obj_set_name (obj_template_t *obj, const xmlChar *objname); /******************************************************************** * FUNCTION obj_has_name * * Check if the specified object type has a name * * this function is used throughout the code to * filter out uses and augment nodes from the * real nodes. Those are the only YANG nodes that * do not have a name assigned to them * * INPUTS: * obj == the specific object to check * * RETURNS: * TRUE if obj has a name * FALSE otherwise *********************************************************************/ extern boolean obj_has_name (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_has_text_content * * Check if the specified object type has a text content * for XPath purposes * * INPUTS: * obj == the specific object to check * * RETURNS: * TRUE if obj has text content * FALSE otherwise *********************************************************************/ extern boolean obj_has_text_content (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_status * * Get the status field for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * YANG status clause for this object *********************************************************************/ extern ncx_status_t obj_get_status (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_description * * Get the description field for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * YANG description string for this object *********************************************************************/ extern const xmlChar * obj_get_description (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_alt_description * * Get the alternate description field for this obj * Check if any 'info', then 'help' appinfo nodes present * * INPUTS: * obj == the specific object to check * * RETURNS: * YANG description string for this object *********************************************************************/ extern const xmlChar * obj_get_alt_description (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_description_addr * * Get the address of the description field for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * YANG description string for this object *********************************************************************/ extern const void * obj_get_description_addr (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_reference * * Get the reference field for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * YANG reference string for this object *********************************************************************/ extern const xmlChar * obj_get_reference (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_reference_addr * * Get the reference field for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * YANG reference string for this object *********************************************************************/ extern const void * obj_get_reference_addr (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_config * * Get the config flag for an obj_template_t * Return the explicit value or the inherited value * Also return if the config-stmt is really set or not * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if config set to TRUE * FALSE if config set to FALSE * *********************************************************************/ #define obj_is_config obj_get_config_flag_deep /******************************************************************** * FUNCTION obj_get_config_flag * * Get the config flag for an obj_template_t * Return the explicit value or the inherited value * Also return if the config-stmt is really set or not * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if config set to TRUE * FALSE if config set to FALSE * *********************************************************************/ extern boolean obj_get_config_flag (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_config_flag2 * * Get the config flag for an obj_template_t * Return the explicit value or the inherited value * Also return if the config-stmt is really set or not * * INPUTS: * obj == obj_template to check * setflag == address of return config-stmt set flag * * OUTPUTS: * *setflag == TRUE if the config-stmt is set in this * node, or if it is a top-level object * == FALSE if the config-stmt is inherited from its parent * * RETURNS: * TRUE if config set to TRUE * FALSE if config set to FALSE * *********************************************************************/ extern boolean obj_get_config_flag2 (const obj_template_t *obj, boolean *setflag); /******************************************************************** * FUNCTION obj_get_max_access * * Get the NCX max-access enum for an obj_template_t * Return the explicit value or the inherited value * * INPUTS: * obj == obj_template to check * * RETURNS: * ncx_access_t enumeration *********************************************************************/ extern ncx_access_t obj_get_max_access (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_appinfoQ * * Get the appinfoQ for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * pointer to the appinfoQ for this object *********************************************************************/ extern dlq_hdr_t * obj_get_appinfoQ (obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_mustQ * * Get the mustQ for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * pointer to the mustQ for this object *********************************************************************/ extern dlq_hdr_t * obj_get_mustQ (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_typestr * * Get the name of the object type * * INPUTS: * obj == obj_template to check * * RETURNS: * name string for this object type *********************************************************************/ extern const xmlChar * obj_get_typestr (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_datadefQ * * Get the datadefQ (or caseQ) if this object has one * * INPUTS: * obj == object to check * * RETURNS: * pointer to Q of obj_template, or NULL if none *********************************************************************/ extern dlq_hdr_t * obj_get_datadefQ (obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_cdatadefQ * * Get a const pointer to the datadefQ (or caseQ) if this object has one * * INPUTS: * obj == object to check * * RETURNS: * pointer to Q of obj_template, or NULL if none *********************************************************************/ extern const dlq_hdr_t * obj_get_cdatadefQ (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_default * * Get the default value for the specified object * Only OBJ_TYP_LEAF objtype is supported * If the leaf has nodefault, then the type is checked * Choice defaults are ignored. * * INPUTS: * obj == object to check * * RETURNS: * pointer to default value string or NULL if none *********************************************************************/ extern const xmlChar * obj_get_default (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_default_case * * Get the default case for the specified OBJ_TYP_CHOICE object * * INPUTS: * obj == object to check * * RETURNS: * pointer to default case object template OBJ_TYP_CASE *********************************************************************/ extern obj_template_t * obj_get_default_case (obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_first_default * * Get the first default value for this object, if any. Only works * for leaf-lists. * * INPUTS: * obj == object to check * * RETURNS: * pointer to first obj_leaflist_defval_t in queue, or NULL if none *********************************************************************/ obj_leaflist_defval_t * obj_get_first_default (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_next_default * * Get the next default value for this object, if any. Only works * for leaf-lists. * * INPUTS: * obj == object to check * * RETURNS: * pointer to the next obj_leaflist_defval_t in queue, or NULL if none *********************************************************************/ obj_leaflist_defval_t * obj_get_next_default (const obj_leaflist_defval_t *def); /******************************************************************** * FUNCTION obj_get_defaults_count * * Get the next number of default values for this leaf-list object. * * INPUTS: * obj == object to check * * RETURNS: * the number of default values in the schema for this object *********************************************************************/ unsigned int obj_get_defaults_count (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_level * * Get the nest level for the specified object * Top-level is '1' * Does not count groupings as a level * * INPUTS: * obj == object to check * * RETURNS: * level that this object is located, by checking the parent chain *********************************************************************/ extern uint32 obj_get_level (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_has_typedefs * * Check if the object has any nested typedefs in it * This will obly be called if the object is defined in a * grouping. * * INPUTS: * obj == object to check * * RETURNS: * TRUE if any nested typedefs, FALSE otherwise *********************************************************************/ extern boolean obj_has_typedefs (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_typdef * * Get the typdef for the leaf or leaf-list * * INPUTS: * obj == object to check * * RETURNS: * pointer to the typdef or NULL if this object type does not * have a typdef *********************************************************************/ extern typ_def_t * obj_get_typdef (obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_ctypdef * * Get the typdef for the leaf or leaf-list : Const version * * INPUTS: * obj == object to check * * RETURNS: * pointer to the typdef or NULL if this object type does not * have a typdef *********************************************************************/ extern const typ_def_t * obj_get_ctypdef (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_basetype * * Get the NCX base type enum for the object type * * INPUTS: * obj == object to check * * RETURNS: * base type enumeration *********************************************************************/ extern ncx_btype_t obj_get_basetype (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_mod_prefix * * Get the module prefix for this object * * INPUTS: * obj == object to check * * RETURNS: * const pointer to mod prefix *********************************************************************/ extern const xmlChar * obj_get_mod_prefix (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_mod_xmlprefix * * Get the module prefix for this object * * INPUTS: * obj == object to check * * RETURNS: * const pointer to mod XML prefix *********************************************************************/ extern const xmlChar * obj_get_mod_xmlprefix (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_mod_name * * Get the module name for this object * * INPUTS: * obj == object to check * * RETURNS: * const pointer to mod prefix *********************************************************************/ extern const xmlChar * obj_get_mod_name (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_mod * * Get the module pointer for this object * * INPUTS: * obj == object to check * * RETURNS: * pointer to module *********************************************************************/ extern ncx_module_t * obj_get_mod (obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_mod_version * * Get the module version for this object * * INPUTS: * obj == object to check * * RETURNS: * const pointer to mod version or NULL if none *********************************************************************/ extern const xmlChar * obj_get_mod_version (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_type_name * * Get the typename for an object * * INPUTS: * obj == object to check * * RETURNS: * const pointer to type name string *********************************************************************/ extern const xmlChar * obj_get_type_name (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_nsid * * Get the namespace ID for this object * * INPUTS: * obj == object to check * * RETURNS: * namespace ID *********************************************************************/ extern xmlns_id_t obj_get_nsid (const obj_template_t *); /******************************************************************** * FUNCTION obj_get_iqualval * * Get the instance qualifier for this object * * INPUTS: * obj == object to check * * RETURNS: * instance qualifier enumeration *********************************************************************/ extern ncx_iqual_t obj_get_iqualval (obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_iqualval_ex * * Get the instance qualifier for this object * * INPUTS: * obj == object to check * required == value to use for 'is_mandatory()' logic * * RETURNS: * instance qualifier enumeration *********************************************************************/ extern ncx_iqual_t obj_get_iqualval_ex (obj_template_t *obj, boolean required); /******************************************************************** * FUNCTION obj_get_min_elements * * Get the min-elements clause for this object, if any * * INPUTS: * obj == object to check * minelems == address of return min-elements value * * OUTPUTS: * *minelems == min-elements value if it is set for this object * * RETURNS: * TRUE if min-elements is set, FALSE if not or N/A *********************************************************************/ extern boolean obj_get_min_elements (obj_template_t *obj, uint32 *minelems); /******************************************************************** * FUNCTION obj_get_max_elements * * Get the max-elements clause for this object, if any * * INPUTS: * obj == object to check * maxelems == address of return max-elements value * * OUTPUTS: * *maxelems == max-elements value if it is set for this object * * RETURNS: * TRUE if max-elements is set, FALSE if not or N/A *********************************************************************/ extern boolean obj_get_max_elements (obj_template_t *obj, uint32 *maxelems); /******************************************************************** * FUNCTION obj_get_units * * Get the units clause for this object, if any * * INPUTS: * obj == object to check * * RETURNS: * pointer to units clause, or NULL if none *********************************************************************/ extern const xmlChar * obj_get_units (obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_parent * * Get the parent of the current object * * INPUTS: * obj == object to check * * RETURNS: * pointer to the parent of this object or NULL if none *********************************************************************/ extern obj_template_t * obj_get_parent (obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_cparent * * Get the parent of the current object * CONST POINTER VERSION * * INPUTS: * obj == object to check * * RETURNS: * pointer to the parent of this object or NULL if none *********************************************************************/ extern const obj_template_t * obj_get_cparent (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_real_parent * * Get the parent of the current object; * skip OBJ_TYP_CHOICE and OBJ_TYP_CASE * * INPUTS: * obj == object to check * * RETURNS: * pointer to the parent of this object or NULL if none *********************************************************************/ extern obj_template_t * obj_get_real_parent (obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_presence_string * * Get the present-stmt value, if any * * INPUTS: * obj == obj_template to check * * RETURNS: * pointer to string * NULL if none *********************************************************************/ extern const xmlChar * obj_get_presence_string (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_presence_string_field * * Get the address ot the presence-stmt value, if any * * INPUTS: * obj == obj_template to check * * RETURNS: * pointer to address of presence string * NULL if none *********************************************************************/ extern void * obj_get_presence_string_field (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_child_node * * Get the correct child node for the specified parent and * current XML node * complex logic for finding the right module namespace * and child node, given the current context * * * INPUTS: * obj == parent object template * chobj == current child node (may be NULL if the * xmlorder param is FALSE * xmlorder == TRUE if should follow strict XML element order * == FALSE if sibling node order errors should be * ignored; find child nodes out of order * and check too-many-instances later * curnode == current XML start or empty node to check * force_modQ == Q of ncx_module_t to check, if set * == NULL and the xmlns registry of module pointers * will be used instead (except netconf.yang) * rettop == address of return topchild object * retobj == address of return object to use * * OUTPUTS: * *rettop set to top-level found object if return OK * and currently within a choice * *retobj set to found object if return OK * * RETURNS: * status *********************************************************************/ extern status_t obj_get_child_node (obj_template_t *obj, obj_template_t *chobj, const xml_node_t *curnode, boolean xmlorder, dlq_hdr_t *force_modQ, obj_template_t **rettop, obj_template_t **retobj); /******************************************************************** * FUNCTION obj_get_child_count * * Get the number of child nodes the object has * * INPUTS: * obj == obj_template to check * * RETURNS: * number of child nodes *********************************************************************/ extern uint32 obj_get_child_count (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_default_parm * * Get the ncx:default-parm object for this object * Only supported for OBJ_TYP_CONTAINER and OBJ_TYP_RPCIO (input) * * INPUTS: * obj == the specific object to check * * RETURNS: * pointer to the name field, NULL if some error or unnamed *********************************************************************/ extern obj_template_t * obj_get_default_parm (obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_config_flag_deep * * get config flag during augment expand * Get the config flag for an obj_template_t * Go all the way up the tree until an explicit * set node or the root is found * * Used by get_list_key because the config flag * of the parent is not set yet when a key leaf is expanded * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if config set to TRUE * FALSE if config set to FALSE *********************************************************************/ extern boolean obj_get_config_flag_deep (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_config_flag_check * * get config flag during YANG module checking * Used by yang_obj.c to make sure ncx:root objects * are not treated as 'config', like obj_get_config_deep * * INPUTS: * obj == obj_template to check * ingrp == address if in grouping flag * * OUTPUTS: * *ingrp == TRUE if hit grouping top without finding * a definitive answer * RETURNS: * TRUE if config set to TRUE * FALSE if config set to FALSE * !!! ignore if *ingrp == TRUE *********************************************************************/ extern boolean obj_get_config_flag_check (const obj_template_t *obj, boolean *ingrp); /******************************************************************** * FUNCTION obj_get_fraction_digits * * Get the fraction-digits field from the object typdef * * INPUTS: * obj == object template to check * * RETURNS: * number of fixed decimal digits expected (1..18) * 0 if some error *********************************************************************/ extern uint8 obj_get_fraction_digits (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_first_iffeature * * Get the first if-feature clause (if any) for the specified object * * INPUTS: * obj == object template to check * * RETURNS: * pointer to first if-feature struct * NULL if none available *********************************************************************/ extern const ncx_iffeature_t * obj_get_first_iffeature (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_next_iffeature * * Get the next if-feature clause (if any) * * INPUTS: * iffeature == current iffeature struct * * RETURNS: * pointer to next if-feature struct * NULL if none available *********************************************************************/ extern const ncx_iffeature_t * obj_get_next_iffeature (const ncx_iffeature_t *iffeature); /******************************************************************** * FUNCTION obj_is_leaf * * Check if object is a proper leaf * * INPUTS: * obj == object to check * * RETURNS: * TRUE if proper leaf * FALSE if not *********************************************************************/ extern boolean obj_is_leaf (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_leafy * * Check if object is a proper leaf or leaflist * * INPUTS: * obj == object to check * * RETURNS: * TRUE if proper leaf or leaf-list * FALSE if not *********************************************************************/ extern boolean obj_is_leafy (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_mandatory * * Figure out if the obj is YANG mandatory or not * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is mandatory * FALSE if object is not mandatory *********************************************************************/ extern boolean obj_is_mandatory (obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_mandatory_when_ex * * Figure out if the obj is YANG mandatory or not * Check the when-stmts, not just mandatory-stmt * * INPUTS: * obj == obj_template to check * config_only == TRUE to check config only and ignore non-config * == FALSE to check mandatory confoig or non-config * RETURNS: * TRUE if object is mandatory * FALSE if object is not mandatory *********************************************************************/ extern boolean obj_is_mandatory_when_ex (obj_template_t *obj, boolean config_only); /******************************************************************** * FUNCTION obj_is_mandatory_when * * Figure out if the obj is YANG mandatory or not * Check the when-stmts, not just mandatory-stmt * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is mandatory * FALSE if object is not mandatory *********************************************************************/ extern boolean obj_is_mandatory_when (obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_cloned * * Figure out if the obj is a cloned object, inserted via uses * or augment statements * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is cloned * FALSE if object is not cloned *********************************************************************/ extern boolean obj_is_cloned (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_augclone * * Figure out if the obj is a cloned object, inserted via an * augment statement * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is sourced from an augment * FALSE if object is not sourced from an augment *********************************************************************/ extern boolean obj_is_augclone (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_refine * * Figure out if the obj is a refinement object, within a uses-stmt * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is a refinement * FALSE if object is not a refinement *********************************************************************/ extern boolean obj_is_refine (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_data * * Check if the object is defined within data or within a * notification or RPC instead * * INPUTS: * obj == object to check * * RETURNS: * TRUE if data object (could be in a grouping or real data) * FALSE if defined within notification or RPC (or some error) *********************************************************************/ extern boolean obj_is_data (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_data_db * * Check if the object is some sort of data * Constrained to only check the config DB objects, * not any notification or RPC objects * * INPUTS: * obj == object to check * * RETURNS: * TRUE if data object (could be in a grouping or real data) * FALSE if defined within notification or RPC (or some error) *********************************************************************/ extern boolean obj_is_data_db (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_in_rpc * * Check if the object is in an rpc/input section * * INPUTS: * obj == object to check * * RETURNS: * TRUE if /rpc/input object * FALSE otherwise *********************************************************************/ extern boolean obj_in_rpc (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_in_rpc_reply * * Check if the object is in an rpc-reply/output section * * INPUTS: * obj == object to check * * RETURNS: * TRUE if /rpc-reply/output object * FALSE otherwise *********************************************************************/ extern boolean obj_in_rpc_reply (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_in_notif * * Check if the object is in a notification * * INPUTS: * obj == object to check * * RETURNS: * TRUE if /notification object * FALSE otherwise *********************************************************************/ extern boolean obj_in_notif (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_rpc * * Check if the object is an RPC method * * INPUTS: * obj == object to check * * RETURNS: * TRUE if RPC method * FALSE if not an RPC method *********************************************************************/ extern boolean obj_is_rpc (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_notif * * Check if the object is a notification * * INPUTS: * obj == object to check * * RETURNS: * TRUE if notification * FALSE if not *********************************************************************/ extern boolean obj_is_notif (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_empty * * Check if object was entered in empty fashion: * list foo; * uses grpx; * ** INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is empty of subclauses * FALSE if object is not empty of subclauses *********************************************************************/ extern boolean obj_is_empty (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_match * * Check if one object is a match in identity with another one * * INPUTS: * obj1 == first object to match * obj2 == second object to match * * RETURNS: * TRUE is a match, FALSE otherwise *********************************************************************/ extern boolean obj_is_match (const obj_template_t *obj1, const obj_template_t *obj2); /******************************************************************** * FUNCTION obj_is_hidden * * Check if object is marked as a hidden object * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked as ncx:hidden * FALSE if not *********************************************************************/ extern boolean obj_is_hidden (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_root * * Check if object is marked as a root object * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked as ncx:root * FALSE if not *********************************************************************/ extern boolean obj_is_root (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_password * * Check if object is marked as a password object * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked as ncx:password * FALSE if not *********************************************************************/ extern boolean obj_is_password (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_xsdlist * * Check if object is marked as an XSD list * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked as ncx:xsdlist * FALSE if not *********************************************************************/ extern boolean obj_is_xsdlist (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_cli * * Check if object is marked as a CLI object * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked as ncx:cli * FALSE if not *********************************************************************/ extern boolean obj_is_cli (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_key * * Check if object is being used as a key leaf within a list * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is a key leaf * FALSE if not *********************************************************************/ extern boolean obj_is_key (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_abstract * * Check if object is being used as an object identifier or error-info * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked as ncx:abstract * FALSE if not *********************************************************************/ extern boolean obj_is_abstract (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_xpath_string * * Check if object is an XPath string * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked as ncx:xpath * FALSE if not *********************************************************************/ extern boolean obj_is_xpath_string (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_schema_instance_string * * Check if object is a schema-instance string * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked as ncx:schema-instance * FALSE if not *********************************************************************/ extern boolean obj_is_schema_instance_string (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_secure * * Check if object is tagged ncx:secure * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked as ncx:secure * FALSE if not *********************************************************************/ extern boolean obj_is_secure (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_very_secure * * Check if object is tagged ncx:very-secure * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked as ncx:very-secure * FALSE if not *********************************************************************/ extern boolean obj_is_very_secure (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_system_ordered * * Check if the object is system or user-ordered * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is system ordered * FALSE if object is user-ordered *********************************************************************/ extern boolean obj_is_system_ordered (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_np_container * * Check if the object is an NP-container * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is an NP-container * FALSE if object is not an NP-container *********************************************************************/ extern boolean obj_is_np_container (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_enabled * * Check any if-feature statement that may * cause the specified object to be invisible * * INPUTS: * obj == obj_template_t to check * RETURNS: * TRUE if object is enabled * FALSE if any if-features are present and FALSE *********************************************************************/ extern boolean obj_is_enabled (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_single_instance * * Check if the object is a single instance of if it * allows multiple instances; check all of the * ancestors if needed * * INPUTS: * obj == object template to check * RETURNS: * TRUE if object is a single instance object * FALSE if multiple instances are allowed *********************************************************************/ extern boolean obj_is_single_instance (obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_short_case * * Check if the object is a short case statement * * INPUTS: * obj == object template to check * * RETURNS: * TRUE if object is a 1 object case statement * FALSE otherwise *********************************************************************/ extern boolean obj_is_short_case (obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_top * * Check if the object is top-level object within * the YANG module that defines it * * INPUTS: * obj == object template to check * * RETURNS: * TRUE if obj is a top-level object * FALSE otherwise *********************************************************************/ extern boolean obj_is_top (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_ok_for_cli * * Figure out if the obj is OK for current CLI implementation * Top object must be a container * Child objects must be only choices of leafs, * plain leafs, or leaf lists are allowed * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is OK for CLI * FALSE if object is not OK for CLI *********************************************************************/ extern boolean obj_ok_for_cli (obj_template_t *obj); /******************************************************************** * FUNCTION obj_has_children * * Check if there are any accessible nodes within the object * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if there are any accessible children * FALSE if no datadb child nodes found *********************************************************************/ extern boolean obj_has_children (obj_template_t *obj); /******************************************************************** * FUNCTION obj_has_ro_children * * Check if there are any accessible read-only nodes within the object * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if there are any accessible read-only children * FALSE if no datadb read-only child nodes found *********************************************************************/ extern boolean obj_has_ro_children (obj_template_t *obj); /******************************************************************** * FUNCTION obj_rpc_has_input * * Check if the RPC object has any real input children * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if there are any input children * FALSE otherwise *********************************************************************/ extern boolean obj_rpc_has_input (obj_template_t *obj); /******************************************************************** * FUNCTION obj_rpc_has_output * * Check if the RPC object has any real output children * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if there are any output children * FALSE otherwise *********************************************************************/ extern boolean obj_rpc_has_output (obj_template_t *obj); /******************************************************************** * FUNCTION obj_has_when_stmts * * Check if any when-stmts apply to this object * Does not check if they are true, just any when-stmts present * * INPUTS: * obj == object template to check * * RETURNS: * TRUE if object has any when-stmts associated with it * FALSE otherwise *********************************************************************/ extern boolean obj_has_when_stmts (obj_template_t *obj); /******************************************************************** * FUNCTION obj_new_metadata * * Malloc and initialize the fields in a an obj_metadata_t * * INPUTS: * isreal == TRUE if this is for a real object * == FALSE if this is a cloned object * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern obj_metadata_t * obj_new_metadata (void); /******************************************************************** * FUNCTION obj_free_metadata * * Scrub the memory in a obj_metadata_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * meta == obj_metadata_t data structure to free *********************************************************************/ extern void obj_free_metadata (obj_metadata_t *meta); /******************************************************************** * FUNCTION obj_add_metadata * * Add the filled out object metadata definition to the object * * INPUTS: * meta == obj_metadata_t data structure to add * obj == object template to add meta to * * RETURNS: * status *********************************************************************/ extern status_t obj_add_metadata (obj_metadata_t *meta, obj_template_t *obj); /******************************************************************** * FUNCTION obj_find_metadata * * Find the object metadata definition in the object * * INPUTS: * obj == object template to check * name == name of obj_metadata_t data structure to find * * RETURNS: * pointer to found entry, NULL if not found *********************************************************************/ extern obj_metadata_t * obj_find_metadata (const obj_template_t *obj, const xmlChar *name); /******************************************************************** * FUNCTION obj_first_metadata * * Get the first object metadata definition in the object * * INPUTS: * obj == object template to check * * RETURNS: * pointer to first entry, NULL if none *********************************************************************/ extern obj_metadata_t * obj_first_metadata (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_next_metadata * * Get the next object metadata definition in the object * * INPUTS: * meta == current meta object template * * RETURNS: * pointer to next entry, NULL if none *********************************************************************/ extern obj_metadata_t * obj_next_metadata (const obj_metadata_t *meta); /******************************************************************** * FUNCTION obj_sort_children * * Check all the child nodes of the specified object * and rearrange them into alphabetical order, * based on the element local-name. * * ONLY SAFE TO USE FOR ncx:cli CONTAINERS * YANG DATA CONTENT ORDER NEEDS TO BE PRESERVED * * INPUTS: * obj == object template to reorder *********************************************************************/ extern void obj_sort_children (obj_template_t *obj); /******************************************************************** * FUNCTION obj_set_ncx_flags * * Check the NCX appinfo extensions and set flags as needed * ** INPUTS: * obj == obj_template to check * * OUTPUTS: * may set additional bits in the obj->flags field * *********************************************************************/ extern void obj_set_ncx_flags (obj_template_t *obj); /******************************************************************** * FUNCTION obj_enabled_child_count * * Get the count of the number of enabled child nodes * for the object template * * INPUTS: * obj == obj_template to check * * RETURNS: * number of enabled child nodes *********************************************************************/ extern uint32 obj_enabled_child_count (obj_template_t *obj); /******************************************************************** * FUNCTION obj_dump_child_list * * Dump the object names in a datadefQ -- just child level * uses log_debug for writing * * INPUTS: * datadefQ == Q of obj_template_t to dump * startindent == start-indent columns * indent == indent amount *********************************************************************/ extern void obj_dump_child_list (dlq_hdr_t *datadefQ, uint32 startindent, uint32 indent); /******************************************************************** * FUNCTION obj_get_keystr * * Get the key string for this list object * * INPUTS: * obj == obj_template to check * * RETURNS: * pointer to key string or NULL if none or not a list *********************************************************************/ extern const xmlChar * obj_get_keystr (obj_template_t *obj); /******************************************************************** * FUNCTION obj_delete_obsolete * * Delete any obsolete child nodes within the specified object subtree * * INPUTS: * objQ == Q of obj_template to check * *********************************************************************/ extern void obj_delete_obsolete (dlq_hdr_t *objQ); /******************************************************************** * FUNCTION obj_get_altname * * Get the alt-name for this object, if any * * INPUTS: * obj == obj_template to check * * RETURNS: * pointer to alt-name of NULL if none *********************************************************************/ extern const xmlChar * obj_get_altname (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_get_leafref_targobj * * Get the target object for a leafref leaf or leaf-list * * INPUTS: * obj == object to check * * RETURNS: * pointer to the target object or NULL if this object type does not * have a leafref target object *********************************************************************/ extern obj_template_t * obj_get_leafref_targobj (obj_template_t *obj); /******************************************************************** * Get the target object for an augments object * \param obj the object to check * \return pointer to the augment context target object * or NULL if this object type does not have an augment target object *********************************************************************/ extern obj_template_t * obj_get_augment_targobj (obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_cli_equals_ok * * Check if object is marked as ncx:default-parm-equals-ok * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked as ncx:default-parm-equals-ok * FALSE if not *********************************************************************/ extern boolean obj_is_cli_equals_ok (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_sil_delete_children_first * * Check if object is marked as ncx:sil-delete-children-first * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked as ncx:sil-delete-children-first * FALSE if not *********************************************************************/ extern boolean obj_is_sil_delete_children_first (const obj_template_t *obj); /******************************************************************** * Add a child object to the specified complex node * * \param child the obj_template to add * \param parent the obj_template of the parent *********************************************************************/ extern void obj_add_child (obj_template_t *child, obj_template_t *parent); /******************************************************************** * FUNCTION obj_is_block_user_create * * Check if object is marked as ncx:user-write with create * access disabled * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked to block user create access * FALSE if not *********************************************************************/ extern boolean obj_is_block_user_create (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_block_user_update * * Check if object is marked as ncx:user-write with update * access disabled * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked to block user update access * FALSE if not *********************************************************************/ extern boolean obj_is_block_user_update (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_is_block_user_delete * * Check if object is marked as ncx:user-write with delete * access disabled * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked to block user delete access * FALSE if not *********************************************************************/ extern boolean obj_is_block_user_delete (const obj_template_t *obj); /******************************************************************** * FUNCTION obj_new_iffeature_ptr * * Malloc and initialize a new obj_iffeature_ptr_t struct * * INPUTS: * iff == iffeature to point at * RETURNS: * malloced struct or NULL if memory error *********************************************************************/ extern obj_iffeature_ptr_t * obj_new_iffeature_ptr (ncx_iffeature_t *iff); /******************************************************************** * FUNCTION obj_free_iffeature_ptr * * Free an obj_iffeature_ptr_t struct * * INPUTS: * iffptr == struct to free *********************************************************************/ extern void obj_free_iffeature_ptr (obj_iffeature_ptr_t *iffptr); /******************************************************************** * Get first if-feature pointer * * \param obj the obj_template to check * \return pointer to first entry or NULL if none *********************************************************************/ extern obj_iffeature_ptr_t * obj_first_iffeature_ptr (obj_template_t *obj); /******************************************************************** * Get the next if-feature pointer * * \param iffptr the current iffeature ptr struct * \return pointer to next entry or NULL if none *********************************************************************/ extern obj_iffeature_ptr_t * obj_next_iffeature_ptr (obj_iffeature_ptr_t *iffptr); /******************************************************************** * FUNCTION obj_new_xpath_ptr * * Malloc and initialize a new obj_xpath_ptr_t struct * * INPUTS: * xpath == Xpath PCB to point at * RETURNS: * malloced struct or NULL if memory error *********************************************************************/ extern obj_xpath_ptr_t * obj_new_xpath_ptr (struct xpath_pcb_t_ *xpath); /******************************************************************** * FUNCTION obj_free_xpath_ptr * * Free an obj_xpath_ptr_t struct * * INPUTS: * xptr == struct to free *********************************************************************/ extern void obj_free_xpath_ptr (obj_xpath_ptr_t *xptr); /******************************************************************** * Get first xpath pointer struct * * \param obj the obj_template to check * \return pointer to first entry or NULL if none *********************************************************************/ extern obj_xpath_ptr_t * obj_first_xpath_ptr (obj_template_t *obj); /******************************************************************** * Get the next xpath pointer struct * * \param xptr the current xpath ptr struct * \return pointer to next entry or NULL if none *********************************************************************/ extern obj_xpath_ptr_t * obj_next_xpath_ptr (obj_xpath_ptr_t *xptr); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_obj */ yuma123_2.14/netconf/src/ncx/blob.c0000664000175000017500000001173614770023131017230 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: blob.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 22apr05 abb begun, borrowed from gr8cxt2 code ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_blob #include "blob.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION c2i * Convert an ASCII HEX char ('0'..'F') into a number (0..15) * Only Works on CAPITAL hex letters: ABCDEF * INPUTS: * c: char to convert * RETURNS: * value converted to its numeric value *********************************************************************/ static uint32 c2i (const char *c) { switch (*c) { case 'A': return 10; case 'B': return 11; case 'C': return 12; case 'D': return 13; case 'E': return 14; case 'F': return 15; default: return (uint32)(*c-'0'); /* assume a number 0..9 */ } /*@notreached@*/ } /* c2i */ /******************************************************************** * FUNCTION i2c * Convert a number to its ASCII HEX char value ('0'..'F') * Only Works on CAPITAL hex letters: ABCDEF * INPUTS: * i: number to convert (0..15) * RETURNS: * value converted to its HEX character ('0'..'F') *********************************************************************/ static char i2c (uint32 i) { if (i<10) { return (char)(i+(uint32)'0'); } else /* if (i<16) */ { return (char)(i-10+(uint32)'A'); } /*@notreached@*/ } /* i2c */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION blob2bin * * Convert a mySQL BLOB to a binary string * * INPUTS: * pblob == pointer to BLOB to convert; must be of the * same type as the binary object; this blob will be * bsize * 2 + 1 bytes in length * pbuff == pointer to buffer to fill in; must be at least * bsize+1 bytes in length * bsize == binary object size * OUTPUTS: * pbuff is filled in *********************************************************************/ void blob2bin (const char *pblob, unsigned char *pbuff, uint32 bsize) { uint32 i, b1, b2; for (i=0;i> 4); b2 = (uint32)(*pbuff++ & 0xf); *pblob++ = i2c(b1); *pblob++ = i2c(b2); } *pblob=0x0; } /* bin2blob */ /* END file blob.c */ yuma123_2.14/netconf/src/ncx/val_parse.c0000664000175000017500000021102314770023131020255 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2013 - 2018, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: val_parse.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11feb06 abb begun; hack, clone agent code and remove all the rpc-error handling code; later a proper libxml2 docPtr interface will be used instead ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include "procdefs.h" #include "b64.h" #include "cfg.h" #include "def_reg.h" #include "dlq.h" #include "log.h" #include "mgr.h" #include "mgr_val_parse.h" #include "mgr_xml.h" #include "ncx.h" #include "ncx_num.h" #include "ncx_str.h" #include "ncx_list.h" #include "ncxconst.h" #include "obj.h" #include "status.h" #include "tk.h" #include "typ.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xpath.h" #include "xpath_yang.h" #include "xpath1.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define MGR_VAL_PARSE_DEBUG 1 #endif /* forward declaration for recursive calls */ static status_t parse_btype (ses_cb_t *scb, obj_template_t *obj, const xml_node_t *startnode, val_value_t *retval); static status_t parse_btype_split (ses_cb_t *scb, obj_template_t *obj, obj_template_t *output, const xml_node_t *startnode, val_value_t *retval); /******************************************************************** * FUNCTION get_xml_node * * Get the next (or maybe current) XML node in the reader stream * This hack needed because xmlTextReader cannot look ahead or * back up during processing. * * The YANG leaf-list is really a collection of sibling nodes * and there is no way to tell where it ends without reading * past the end of it. * * This hack relies on the fact that a top-levelleaf-list could * never show up in a real NETCONF PDU * * INPUTS: * scb == session control block * xmlnode == xml_node_t to fill in * * OUTPUTS: * *xmlnode filled in * * RETURNS: * status *********************************************************************/ static status_t get_xml_node (ses_cb_t *scb, xml_node_t *xmlnode) { status_t res; res = xml_consume_node(scb->reader, xmlnode, TRUE, TRUE); return res; } /* get_xml_node */ /******************************************************************** * FUNCTION gen_index_chain * * Create an index chain for the just-parsed table or container struct * * INPUTS: * instart == first obj_key_t in the chain to process * val == the just parsed list entry with the childQ containing * nodes to check as index nodes * * RETURNS: * status *********************************************************************/ static status_t gen_index_chain (obj_key_t *instart, val_value_t *val) { obj_key_t *key; status_t res = NO_ERR; /* 0 or more index components expected */ for (key = instart; key != NULL; key = obj_next_key(key)) { res = val_gen_index_comp(key, val); if (res != NO_ERR) { return res; } } return res; } /* gen_index_chain */ /******************************************************************** * FUNCTION get_editop * * Check the node for operation="foo" attribute * and convert its value to an op_editop_t enum * * INPUTS: * node == xml_node_t to check * RETURNS: * editop == will be OP_EDITOP_NONE if explicitly set, * not-present, or error *********************************************************************/ static op_editop_t get_editop (const xml_node_t *node) { const xml_attr_t *attr; attr = xml_find_ro_attr(node, xmlns_nc_id(), NC_OPERATION_ATTR_NAME); if (!attr) { return OP_EDITOP_NONE; } return op_editop_id(attr->attr_val); } /* get_editop */ /******************************************************************** * FUNCTION parse_any * * Parse the XML input as an 'any' type * * INPUTS: * scb == session in progress * startnode == XML to start processing at * retval == address of value struct to fill in * * OUTPUTS: * *retval filled in * * RETURNS: * status *********************************************************************/ static status_t parse_any (ses_cb_t *scb, const xml_node_t *startnode, val_value_t *retval) { val_value_t *chval; status_t res, res2; boolean done, getstrend; xml_node_t nextnode; /* init local vars */ chval = NULL; res = NO_ERR; res2 = NO_ERR; done = FALSE; getstrend = FALSE; xml_init_node(&nextnode); /* make sure the startnode is correct */ switch (startnode->nodetyp) { case XML_NT_START: /* do not set the object template yet, in case * there is a form of empty element * or another start (child) node */ break; case XML_NT_EMPTY: /* treat this 'any' is an 'empty' data type */ val_init_from_template(retval, ncx_get_gen_empty()); retval->v.boo = TRUE; retval->nsid = startnode->nsid; return NO_ERR; default: res = ERR_NCX_WRONG_NODETYP; } if (res == NO_ERR) { /* at this point have either a simple type or a complex type * get the next node which could be any type */ res = xml_consume_node(scb->reader, &nextnode, FALSE, TRUE); } if (res == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_any: expecting any node type"); xml_dump_node(&nextnode); } #endif /* decide the base type from the child node type */ switch (nextnode.nodetyp) { case XML_NT_START: case XML_NT_EMPTY: /* A nested start or empty element means the parent is * treated as a 'container' data type */ val_init_from_template(retval, ncx_get_gen_container()); retval->nsid = startnode->nsid; break; case XML_NT_STRING: /* treat this string child node as the string * content for the parent node */ val_init_from_template(retval, ncx_get_gen_string()); retval->nsid = startnode->nsid; retval->v.str = xml_strdup(nextnode.simval); res = (retval->v.str) ? NO_ERR : ERR_INTERNAL_MEM; getstrend = TRUE; break; case XML_NT_END: res = xml_endnode_match(startnode, &nextnode); if (res == NO_ERR) { /* treat this start + end pair as an 'empty' data type */ val_init_from_template(retval, ncx_get_gen_empty()); retval->v.boo = TRUE; retval->nsid = startnode->nsid; xml_clean_node(&nextnode); return NO_ERR; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } /* check if processing a simple type as a string */ if (getstrend) { /* need to get the endnode for startnode then exit */ xml_clean_node(&nextnode); res2 = xml_consume_node(scb->reader, &nextnode, FALSE, TRUE); if (res2 == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_any: expecting end node for %s", startnode->qname); xml_dump_node(&nextnode); } #endif res2 = xml_endnode_match(startnode, &nextnode); } } /* check if there were any errors in the startnode */ if (res != NO_ERR || res2 != NO_ERR) { xml_clean_node(&nextnode); return (res==NO_ERR) ? res2 : res; } if (getstrend) { xml_clean_node(&nextnode); return NO_ERR; } /* if we get here, then the startnode is a container */ while (!done) { /* At this point have a nested start node * Allocate a new val_value_t for the child value node */ res = NO_ERR; chval = val_new_child_val(nextnode.nsid, nextnode.elname, TRUE, retval, get_editop(&nextnode), ncx_get_gen_anyxml()); if (!chval) { res = ERR_INTERNAL_MEM; } /* check any error setting up the child node */ if (res == NO_ERR) { /* recurse through and get whatever nodes are present * in the child node; call it an 'any' type * make sure this function gets called again * so the namespace errors can be ignored properly ;-) * * Cannot call this function directly or the * XML attributes will not get processed */ res = parse_btype(scb, ncx_get_gen_anyxml(), &nextnode, chval); chval->res = res; xml_clean_node(&nextnode); val_add_child(chval, retval); chval = NULL; } /* record any error, if not already done */ if (res != NO_ERR) { xml_clean_node(&nextnode); if (chval) { val_free_value(chval); } return res; } /* get the next node */ res = xml_consume_node(scb->reader, &nextnode, FALSE, TRUE); if (res == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_any: expecting start, empty, or end node"); xml_dump_node(&nextnode); } #endif res = xml_endnode_match(startnode, &nextnode); if (res == NO_ERR) { done = TRUE; } } else { /* error already recorded */ done = TRUE; } } xml_clean_node(&nextnode); return res; } /* parse_any */ /******************************************************************** * FUNCTION parse_enum * * Parse the XML input as a 'enumeration' type * e.g.. * * fred * * These NCX variants are no longer supported: * 11 * fred(11) * * INPUTS: * scb == session in progress * obj == object template to parse against * startnode == XML to start processing at * retval == address of value struct to fill in * * OUTPUTS: * *retval filled in * * RETURNS: * status *********************************************************************/ static status_t parse_enum (ses_cb_t *scb, obj_template_t *obj, const xml_node_t *startnode, val_value_t *retval) { xml_node_t valnode, endnode; status_t res, res2; /* init local vars */ xml_init_node(&valnode); xml_init_node(&endnode); res2 = NO_ERR; val_init_from_template(retval, obj); /* make sure the startnode is correct */ res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_START); if (res == NO_ERR) { /* get the next node which should be a string node */ res = get_xml_node(scb, &valnode); } if (res == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_enum: expecting string node"); xml_dump_node(&valnode); } #endif /* validate the node type and enum string or number content */ switch (valnode.nodetyp) { case XML_NT_START: res = ERR_NCX_WRONG_NODETYP_CPX; break; case XML_NT_STRING: /* get the non-whitespace string here */ res = val_enum_ok(obj_get_typdef(obj), valnode.simval, &retval->v.enu.val, &retval->v.enu.name); break; default: res = ERR_NCX_WRONG_NODETYP; } /* get the matching end node for startnode */ res2 = get_xml_node(scb, &endnode); if (res2 == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_enum: expecting end for %s", startnode->qname); xml_dump_node(&endnode); } #endif res2 = xml_endnode_match(startnode, &endnode); } } if (res == NO_ERR) { res = res2; } xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_enum */ /******************************************************************** * FUNCTION parse_empty * For NCX_BT_EMPTY * * Parse the XML input as an 'empty' or 'boolean' type * e.g.: * * * * * * INPUTS: * scb == session in progress * obj == object template to parse against * startnode == XML to start processing at * retval == address of value struct to fill in * * OUTPUTS: * *retval filled in * * RETURNS: * status *********************************************************************/ static status_t parse_empty (ses_cb_t *scb, obj_template_t *obj, const xml_node_t *startnode, val_value_t *retval) { xml_node_t endnode; status_t res; /* init local vars */ xml_init_node(&endnode); val_init_from_template(retval, obj); /* validate the node type and enum string or number content */ switch (startnode->nodetyp) { case XML_NT_EMPTY: res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_NONE); break; case XML_NT_START: res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_NONE); if (res == NO_ERR) { res = get_xml_node(scb, &endnode); if (res == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_empty: expecting end for %s", startnode->qname); xml_dump_node(&endnode); } #endif res = xml_endnode_match(startnode, &endnode); if (res != NO_ERR) { if (endnode.nodetyp != XML_NT_STRING || !xml_isspace_str(endnode.simval)) { res = ERR_NCX_WRONG_NODETYP; } else { /* that was an empty string -- try again */ xml_clean_node(&endnode); res = get_xml_node(scb, &endnode); if (res == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_empty: expecting " "end for %s", startnode->qname); xml_dump_node(&endnode); } #endif res = xml_endnode_match(startnode, &endnode); } } } } } break; default: res = ERR_NCX_WRONG_NODETYP; } /* record the value if no errors */ if (res == NO_ERR) { retval->v.boo = TRUE; } xml_clean_node(&endnode); return res; } /* parse_empty */ /******************************************************************** * FUNCTION parse_boolean * * Parse the XML input as a 'boolean' type * e.g.. * * true * false * 1 * 0 * * INPUTS: * scb == session in progress * obj == object template to parse against * startnode == XML to start processing at * retval == address of value struct to fill in * * OUTPUTS: * *retval filled in * * RETURNS: * status *********************************************************************/ static status_t parse_boolean (ses_cb_t *scb, obj_template_t *obj, const xml_node_t *startnode, val_value_t *retval) { xml_node_t valnode, endnode; status_t res, res2; /* init local vars */ xml_init_node(&valnode); xml_init_node(&endnode); res2 = NO_ERR; val_init_from_template(retval, obj); /* make sure the startnode is correct */ res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_START); if (res == NO_ERR) { /* get the next node which should be a string node */ res = get_xml_node(scb, &valnode); } if (res == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_boolean: expecting string node."); xml_dump_node(&valnode); } #endif /* validate the node type and enum string or number content */ switch (valnode.nodetyp) { case XML_NT_START: res = ERR_NCX_WRONG_NODETYP_CPX; break; case XML_NT_STRING: /* get the non-whitespace string here */ if (ncx_is_true(valnode.simval)) { retval->v.boo = TRUE; } else if (ncx_is_false(valnode.simval)) { retval->v.boo = FALSE; } else { res = ERR_NCX_INVALID_VALUE; } break; default: res = ERR_NCX_WRONG_NODETYP; } /* get the matching end node for startnode */ res2 = get_xml_node(scb, &endnode); if (res2 == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_boolean: expecting end for %s", startnode->qname); xml_dump_node(&endnode); } #endif res2 = xml_endnode_match(startnode, &endnode); } } if (res == NO_ERR) { res = res2; } xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_boolean */ /******************************************************************** * FUNCTION parse_num * * Parse the XML input as a numeric data type * * INPUTS: * scb == session control block * Input is read from scb->reader. * obj == object template for this number type * btyp == base type of the expected ordinal number type * startnode == top node of the parameter to be parsed * Parser function will attempt to consume all the * nodes until the matching endnode is reached * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * msg->errQ may be appended with new errors or warnings * * RETURNS: * status *********************************************************************/ static status_t parse_num (ses_cb_t *scb, obj_template_t *obj, ncx_btype_t btyp, const xml_node_t *startnode, val_value_t *retval) { ncx_errinfo_t *errinfo; xml_node_t valnode, endnode; status_t res, res2; ncx_numfmt_t numfmt; /* init local vars */ xml_init_node(&valnode); xml_init_node(&endnode); errinfo = NULL; val_init_from_template(retval, obj); /* make sure the startnode is correct */ res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_START); if (res == NO_ERR) { /* get the next node which should be a string node */ res = get_xml_node(scb, &valnode); } if (res != NO_ERR) { /* fatal error */ xml_clean_node(&valnode); return res; } #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_num: expecting string node."); xml_dump_node(&valnode); } #endif /* validate the number content */ switch (valnode.nodetyp) { case XML_NT_START: res = ERR_NCX_WRONG_NODETYP_CPX; break; case XML_NT_STRING: /* convert the non-whitespace string to a number */ numfmt = ncx_get_numfmt(valnode.simval); if (numfmt == NCX_NF_OCTAL) { numfmt = NCX_NF_DEC; } if (numfmt == NCX_NF_DEC || numfmt == NCX_NF_REAL) { if (btyp == NCX_BT_DECIMAL64) { res = ncx_convert_dec64(valnode.simval, numfmt, obj_get_fraction_digits(obj), &retval->v.num); } else { res = ncx_convert_num(valnode.simval, numfmt, btyp, &retval->v.num); } } else { res = ERR_NCX_WRONG_NUMTYP; } if (res == NO_ERR) { res = val_range_ok_errinfo(obj_get_typdef(obj), btyp, &retval->v.num, &errinfo); } break; default: res = ERR_NCX_WRONG_NODETYP; } /* get the matching end node for startnode */ res2 = get_xml_node(scb, &endnode); if (res2 == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_num: expecting end for %s", startnode->qname); xml_dump_node(&endnode); } #endif res2 = xml_endnode_match(startnode, &endnode); } if (res == NO_ERR) { res = res2; } xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_num */ /******************************************************************** * FUNCTION parse_string * * Parse the XML input as a string data type * * INPUTS: * scb == session control block * Input is read from scb->reader. * obj == object template for this string type * btyp == base type of the expected string type * startnode == top node of the parameter to be parsed * Parser function will attempt to consume all the * nodes until the matching endnode is reached * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * msg->errQ may be appended with new errors or warnings * * RETURNS: * status *********************************************************************/ static status_t parse_string (ses_cb_t *scb, obj_template_t *obj, ncx_btype_t btyp, const xml_node_t *startnode, val_value_t *retval) { ncx_errinfo_t *errinfo; typ_template_t *listtyp; obj_template_t *targobj; xpath_result_t *result; xml_node_t valnode, endnode; status_t res, res2; boolean empty; ncx_btype_t listbtyp; /* init local vars */ xml_init_node(&valnode); xml_init_node(&endnode); empty = FALSE; errinfo = NULL; listbtyp = NCX_BT_NONE; listtyp = NULL; val_init_from_template(retval, obj); /* make sure the startnode is correct */ res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_START); if (res != NO_ERR) { res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_EMPTY); if (res == NO_ERR) { empty = TRUE; } } /* get the value string node */ if (res == NO_ERR && !empty) { /* get the next node which should be a string node */ res = get_xml_node(scb, &valnode); if (res == NO_ERR && valnode.nodetyp == XML_NT_END) { empty = TRUE; } } /* check empty string corner case */ if (empty) { if (btyp==NCX_BT_SLIST || btyp==NCX_BT_BITS) { /* no need to check the empty list */ ; } else if (btyp == NCX_BT_INSTANCE_ID) { res = ERR_NCX_INVALID_VALUE; } else { /* check the empty string */ res = val_string_ok_errinfo(obj_get_typdef(obj), btyp, EMPTY_STRING, &errinfo); retval->v.str = xml_strdup(EMPTY_STRING); if (!retval->v.str) { res = ERR_INTERNAL_MEM; } } } if (res != NO_ERR || empty) { xml_clean_node(&valnode); return res; } #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_string: expecting string node."); xml_dump_node(&valnode); } #endif /* validate the string content */ switch (valnode.nodetyp) { case XML_NT_START: res = ERR_NCX_WRONG_NODETYP_CPX; break; case XML_NT_STRING: switch (btyp) { case NCX_BT_SLIST: case NCX_BT_BITS: if (btyp==NCX_BT_SLIST) { /* get the list of strings, then check them */ listtyp = typ_get_listtyp(obj_get_typdef(obj)); listbtyp = typ_get_basetype(&listtyp->typdef); } else { listbtyp = NCX_BT_BITS; } res = ncx_set_list(listbtyp, valnode.simval, &retval->v.list); if (res == NO_ERR) { if (btyp == NCX_BT_SLIST) { res = ncx_finish_list(&listtyp->typdef, &retval->v.list); } else { res = ncx_finish_list(obj_get_typdef(obj), &retval->v.list); } } if (res == NO_ERR) { res = val_list_ok_errinfo(obj_get_typdef(obj), btyp, &retval->v.list, &errinfo); } break; default: if (!obj_is_xpath_string(obj)) { /* check the non-whitespace string */ res = val_string_ok_errinfo(obj_get_typdef(obj), btyp, valnode.simval, &errinfo); break; } /* else fall through and parse XPath string */ case NCX_BT_INSTANCE_ID: retval->xpathpcb = xpath_new_pcb(valnode.simval, NULL); if (!retval->xpathpcb) { res = ERR_INTERNAL_MEM; } else if (btyp == NCX_BT_INSTANCE_ID || obj_is_schema_instance_string(obj)) { /* do a first pass parsing to resolve all * the prefixes and check well-formed XPath */ res = xpath_yang_validate_xmlpath(scb->reader, retval->xpathpcb, obj, FALSE, &targobj); } else { result = xpath1_eval_xmlexpr(scb->reader, retval->xpathpcb, NULL, NULL, FALSE, FALSE, &res); // not interested in the actual result, just the status in res! xpath_free_result(result); } if (res != NO_ERR) { res = ERR_NCX_INVALID_XPATH_EXPR; } } /* record the value even if there are errors */ switch (btyp) { case NCX_BT_BINARY: if (valnode.simval) { /* result is going to be less than the encoded length */ retval->v.binary.ustr = m__getMem(valnode.simlen); retval->v.binary.ubufflen = valnode.simlen; if (!retval->v.binary.ustr) { res = ERR_INTERNAL_MEM; } else { res = b64_decode(valnode.simval, valnode.simlen, retval->v.binary.ustr, retval->v.binary.ubufflen, &retval->v.binary.ustrlen); } } break; case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: if (valnode.simval) { retval->v.str = xml_strdup(valnode.simval); if (!retval->v.str) { res = ERR_INTERNAL_MEM; } } break; case NCX_BT_SLIST: case NCX_BT_BITS: break; /* value already set */ default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; default: res = ERR_NCX_WRONG_NODETYP; } /* get the matching end node for startnode */ res2 = get_xml_node(scb, &endnode); if (res2 == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_string: expecting end for %s", startnode->qname); xml_dump_node(&endnode); } #endif res2 = xml_endnode_match(startnode, &endnode); } if (res == NO_ERR) { res = res2; } xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_string */ /******************************************************************** * FUNCTION parse_idref * * Parse the XML input as an 'identityref' type * e.g.. * * acme:card-type3 * * INPUTS: * scb == session control block * Input is read from scb->reader. * obj == object template for this identityref type * startnode == top node of the parameter to be parsed * Parser function will attempt to consume all the * nodes until the matching endnode is reached * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * msg->errQ may be appended with new errors or warnings * * RETURNS: * status *********************************************************************/ static status_t parse_idref (ses_cb_t *scb, obj_template_t *obj, const xml_node_t *startnode, val_value_t *retval) { ncx_module_t *mod; const xmlChar *str; xml_node_t valnode, endnode; status_t res, res2; boolean enddone; /* init local vars */ xml_init_node(&valnode); xml_init_node(&endnode); enddone = FALSE; res = NO_ERR; res2 = NO_ERR; val_init_from_template(retval, obj); /* make sure the startnode is correct */ res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_START); if (res == NO_ERR) { /* get the next node which should be a string node */ res = get_xml_node(scb, &valnode); } if (res == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_idref: expecting string node"); xml_dump_node(&valnode); } #endif /* validate the node type and enum string or number content */ switch (valnode.nodetyp) { case XML_NT_START: case XML_NT_EMPTY: res = ERR_NCX_WRONG_NODETYP_CPX; break; case XML_NT_STRING: if (val_all_whitespace(valnode.simval)) { res = ERR_NCX_INVALID_VALUE; } else { mgr_scb_t* mscb; ncx_module_t* firstmod; mscb = (mgr_scb_t *)scb->mgrcb; /* get the name field and verify identity is valid * for the identity base in the typdef */ str = NULL; if(mscb==NULL) { for (mod = obj->tkerr.mod; mod != NULL; mod = (ncx_module_t *)dlq_prevEntry(mod)) { firstmod = mod; } } else { firstmod = (ncx_module_t *)dlq_firstEntry(&mscb->temp_modQ); } for (mod = firstmod; mod != NULL; mod = (ncx_module_t *)dlq_nextEntry(mod)) { res = val_idref_ok(obj_get_typdef(obj), valnode.simval, mod->nsid, &str, &retval->v.idref.identity); if (res == NO_ERR) { retval->v.idref.nsid = mod->nsid; break; } } if (str) { retval->v.idref.name = xml_strdup(str); if (!retval->v.idref.name) { res = ERR_INTERNAL_MEM; } } else { /* save name given anyway or NULL string will * get printed */ retval->v.idref.name = xml_strdup(valnode.simval); if (!retval->v.idref.name) { res = ERR_INTERNAL_MEM; } } } break; case XML_NT_END: enddone = TRUE; res = ERR_NCX_INVALID_VALUE; break; default: res = SET_ERROR(ERR_NCX_WRONG_NODETYP); enddone = TRUE; } /* get the matching end node for startnode */ if (!enddone) { res2 = get_xml_node(scb, &endnode); if (res2 == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_idref: expecting end for %s", startnode->qname); xml_dump_node(&endnode); } #endif res2 = xml_endnode_match(startnode, &endnode); } } } if (res == NO_ERR) { res = res2; } xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_idref */ /******************************************************************** * FUNCTION parse_union * * Parse the XML input as a 'union' type * e.g.. * * fred * 11 * * * * INPUTS: * see parse_btype parameter list * * OUTPUTS: * *retval will be filled in * * RETURNS: * status *********************************************************************/ static status_t parse_union (ses_cb_t *scb, obj_template_t *obj, const xml_node_t *startnode, val_value_t *retval) { ncx_errinfo_t *errinfo; xml_node_t valnode, endnode; status_t res, res2; boolean stopnow; boolean empty; /* init local vars */ xml_init_node(&valnode); xml_init_node(&endnode); res2 = NO_ERR; errinfo = NULL; stopnow = FALSE; empty = FALSE; val_init_from_template(retval, obj); /* make sure the startnode is correct */ res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_START); if (res != NO_ERR) { res = xml_node_match(startnode, obj_get_nsid(obj), NULL, XML_NT_EMPTY); if (res == NO_ERR) { empty = TRUE; } } /* get the value string node */ if (res == NO_ERR && !empty) { /* get the next node which should be a string node */ res = get_xml_node(scb, &valnode); if (res == NO_ERR && valnode.nodetyp == XML_NT_END) { res = xml_endnode_match(startnode, &valnode); if(res==NO_ERR) { empty = TRUE; xml_clean_node(&valnode); } } } if (empty && res==NO_ERR) { /* check the empty string */ res = val_union_ok_errinfo(obj_get_typdef(obj), EMPTY_STRING, retval, &errinfo); if(res==NO_ERR) { retval->v.str = xml_strdup(EMPTY_STRING); if (!retval->v.str) { res = ERR_INTERNAL_MEM; } } } if(res == NO_ERR && !empty) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_union: expecting string or number node."); xml_dump_node(&valnode); } #endif /* validate the node type and union node content */ switch (valnode.nodetyp) { case XML_NT_STRING: /* get the non-whitespace string here */ res = val_union_ok_errinfo(obj_get_typdef(obj), valnode.simval, retval, &errinfo); break; default: stopnow = TRUE; SET_ERROR(ERR_INTERNAL_VAL); res = ERR_NCX_WRONG_NODETYP; } if (stopnow) { res2 = val_set_simval(retval, retval->typdef, retval->nsid, retval->name, EMPTY_STRING); } else { res = val_set_simval(retval, retval->typdef, retval->nsid, retval->name, valnode.simval); /* get the matching end node for startnode */ res2 = get_xml_node(scb, &endnode); if (res2 == NO_ERR) { #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_union: expecting end for %s", startnode->qname); xml_dump_node(&endnode); } #endif res2 = xml_endnode_match(startnode, &endnode); } } } if (res == NO_ERR) { res = res2; } xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_union */ /******************************************************************** * FUNCTION parse_complex * * Parse the XML input as a complex type * * Handles the following base types: * NCX_BT_CONTAINER * NCX_BT_LIST * * E.g., container: * * * blah * 7 * * * * In an instance document, containers and lists look * the same. The validation is different of course, but the * parsing is basically the same. * * INPUTS: * scb == session control block * Input is read from scb->reader. * obj == object template for this complex type * output == foo-rpc/output object, if any * btyp == base type of the expected complex type * startnode == top node of the parameter to be parsed * Parser function will attempt to consume all the * nodes until the matching endnode is reached * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * msg->errQ may be appended with new errors or warnings * * RETURNS: * status *********************************************************************/ static status_t parse_complex (ses_cb_t *scb, obj_template_t *obj, obj_template_t *output, ncx_btype_t btyp, const xml_node_t *startnode, val_value_t *retval) { obj_template_t *chobj, *curchild, *curtop; obj_template_t *outchobj; mgr_scb_t *mscb; val_value_t *chval; dlq_hdr_t *force_modQ; xml_node_t chnode; status_t res, retres; boolean done, empty, errmode; ncx_btype_t chbtyp; /* setup local vars */ chobj = NULL; curtop = NULL; curchild = NULL; outchobj = NULL; res = NO_ERR; retres = NO_ERR; done = FALSE; empty = FALSE; chbtyp = NCX_BT_NONE; mscb = (mgr_scb_t *)scb->mgrcb; if (mscb == NULL || dlq_empty(&mscb->temp_modQ)) { force_modQ = NULL; } else { force_modQ = &mscb->temp_modQ; } val_init_from_template(retval, obj); /* check the XML start node type */ switch (startnode->nodetyp) { case XML_NT_START: break; case XML_NT_EMPTY: empty = TRUE; break; case XML_NT_STRING: case XML_NT_END: res = ERR_NCX_WRONG_NODETYP_SIM; break; default: res = ERR_NCX_WRONG_NODETYP; } if (res == NO_ERR) { /* start setting up the return value */ retval->editop = get_editop(startnode); /* setup the first child in the complex object * Allowed be NULL in some cases so do not check * the result yet */ chobj = obj_first_child(obj); outchobj = (output) ? obj_first_child(output) : NULL; } else { return res; } if (empty) { return NO_ERR; } xml_init_node(&chnode); /* go through each child node until the parent end node */ while (!done) { /* init per-loop vars */ empty = FALSE; chval = NULL; errmode = FALSE; /* get the next node which should be a child or end node */ res = get_xml_node(scb, &chnode); if (res != NO_ERR) { if (res == ERR_NCX_UNKNOWN_NS && chnode.elname) { retval->res = res; errmode = TRUE; res = NO_ERR; } else { done = TRUE; continue; } } #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG4) { log_debug4("\nparse_complex: expecting " "start-child or end node."); xml_dump_node(&chnode); } #endif /* validate the child member node type */ switch (chnode.nodetyp) { case XML_NT_START: case XML_NT_EMPTY: /* any namespace OK for now, * check in obj_get_child_node */ break; case XML_NT_STRING: res = ERR_NCX_WRONG_NODETYP_SIM; break; case XML_NT_END: res = xml_endnode_match(startnode, &chnode); if (res == NO_ERR) { /* no error exit */ done = TRUE; continue; } break; default: res = ERR_NCX_WRONG_NODETYP; } /* if we get here, there is a START or EMPTY node * that could be a valid child node * * if xmlorder enforced then check if the * node is the correct child node * * if no xmlorder, then check for any valid child */ if (res==NO_ERR) { curchild = NULL; if (!errmode) { res = obj_get_child_node(obj, chobj, &chnode, FALSE, force_modQ, &curtop, &curchild); } if (res != NO_ERR && output) { /* 2) try child of output object */ res = obj_get_child_node(output, outchobj, &chnode, FALSE, force_modQ, &curtop, &curchild); } if (!curchild || res != NO_ERR) { if (ncx_warning_enabled(ERR_NCX_USING_ANYXML)) { log_warn("\nWarning: '%s' has no child node " "'%s'. Using anyxml", retval->name, chnode.qname); } curchild = ncx_get_gen_anyxml(); res = NO_ERR; errmode = TRUE; } } /* try to setup a new child node */ if (res == NO_ERR) { /* save the child base type */ chbtyp = obj_get_basetype(curchild); /* at this point, the 'curchild' template matches the * 'chnode' namespace and name; * Allocate a new val_value_t for the child value node */ chval = val_new_child_val((errmode) ? xmlns_inv_id() : obj_get_nsid(curchild), (errmode) ? chnode.elname : obj_get_name(curchild), errmode, retval, get_editop(&chnode), curchild); if (!chval) { res = ERR_INTERNAL_MEM; } } /* check any errors in setting up the child node */ if (res != NO_ERR) { retres = res; /* try to skip just the child node sub-tree */ xml_clean_node(&chnode); if (chval) { val_free_value(chval); chval = NULL; } if (NEED_EXIT(res) || res == ERR_XML_READER_EOF) { done = TRUE; } else { /* skip the entire value subtree */ (void)xml_skip_subtree(scb->reader, startnode); } continue; } /* recurse through and get whatever nodes are present * in the child node */ res = parse_btype(scb, curchild, &chnode, chval); chval->res = res; val_add_child(chval, retval); if (res == NO_ERR) { /* setup the next child unless the current child * is a list */ if (chbtyp != NCX_BT_LIST) { /* setup next child if the cur child * is 0 - 1 instance */ switch (obj_get_iqualval(curchild)) { case NCX_IQUAL_ONE: case NCX_IQUAL_OPT: if (chobj) { chobj = obj_next_child(chobj); } if (outchobj) { outchobj = obj_next_child(outchobj); } break; default: break; } } } else { /* did not parse the child node correctly */ retres = res; if (NEED_EXIT(res) || res==ERR_XML_READER_EOF) { done = TRUE; continue; } } xml_clean_node(&chnode); /* check if this is the internal load-config RPC method, * in which case there will not be an end tag for the * start tag passed to this function. * Need to quick exit to prevent reading past the * end of the XML config file, * which just contains the element * * know there is just one parameter named so just * go through the loop once because the tag * will not be present */ if ((startnode->nsid == xmlns_ncx_id() || startnode->nsid == 0) && !xml_strcmp(startnode->elname, NCX_EL_LOAD_CONFIG)) { done = TRUE; } } /* check if the index ID needs to be set */ if (retres==NO_ERR && btyp==NCX_BT_LIST) { res = gen_index_chain(obj_first_key(obj), retval); if (res != NO_ERR) { retres = res; } } xml_clean_node(&chnode); return retres; } /* parse_complex */ /******************************************************************** * FUNCTION parse_metadata * * Parse all the XML attributes in the specified xml_node_t struct * * Only XML_NT_START or XML_NT_EMPTY nodes will have attributes * * INPUTS: * obj == object template containing meta-data definition * nserr == TRUE if namespace errors should be checked * == FALSE if not, and any attribute is accepted * node == node of the parameter maybe with attributes to be parsed * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * msg->errQ may be appended with new errors or warnings * retval->editop contains the last value of the operation attribute * seen, if any; will be OP_EDITOP_NONE if not set * * RETURNS: * status *********************************************************************/ static status_t parse_metadata (obj_template_t *obj, const xml_node_t *node, boolean nserr, val_value_t *retval) { obj_metadata_t *meta; typ_def_t *metadef; xml_attr_t *attr; val_value_t *metaval; xmlns_id_t ncid, yangid, xmlid, wdaid; status_t res, retres; boolean iskey, isvalue, islang, islastmodified; retres = NO_ERR; ncid = xmlns_nc_id(); yangid = xmlns_yang_id(); xmlid = xmlns_xml_id(); wdaid = xmlns_wda_id(); /* go through all the attributes in the node and convert * to val_value_t structs * find the correct typedef to match each attribute */ for (attr = xml_get_first_attr(node); attr != NULL; attr = xml_next_attr(attr)) { if (attr->attr_dname && !xml_strncmp(attr->attr_dname, XMLNS, xml_strlen(XMLNS))) { /* skip this 'xmlns' attribute */ continue; } /* init per-loop locals */ res = NO_ERR; meta = NULL; metadef = NULL; iskey = FALSE; isvalue = FALSE; islang = FALSE; islastmodified = FALSE; /* check qualified and unqualified operation attribute, * then the 'xmlns' attribute, then a defined attribute */ if (val_match_metaval(attr, ncid, NC_OPERATION_ATTR_NAME)) { retval->editop = op_editop_id(attr->attr_val); if (retval->editop == OP_EDITOP_NONE) { retres = ERR_NCX_INVALID_VALUE; } continue; } else if (val_match_metaval(attr, yangid, YANG_K_INSERT)) { if (retval->editvars == NULL) { res = val_new_editvars(retval); if (res != NO_ERR) { return res; } } retval->editvars->insertop = op_insertop_id(attr->attr_val); if (retval->editvars->insertop == OP_INSOP_NONE) { retres = ERR_NCX_INVALID_VALUE; } continue; } else if (val_match_metaval(attr, yangid, YANG_K_KEY)) { iskey = TRUE; } else if (val_match_metaval(attr, yangid, YANG_K_VALUE)) { isvalue = TRUE; } else if (val_match_metaval(attr, xmlid, (const xmlChar *)"lang")) { islang = TRUE; } else if (val_match_metaval(attr, wdaid, YANG_K_DEFAULT)) { if (ncx_is_true(attr->attr_val)) { val_set_withdef_default(retval); } else if (ncx_is_false(attr->attr_val)) { ; } else { log_error("\nError: wd:default attribute has " "invalid value '%s'\n", attr->attr_val); res = ERR_NCX_INVALID_VALUE; } continue; } else if (val_match_metaval(attr, 0, NCX_EL_LAST_MODIFIED)) { islastmodified = TRUE; } else { /* find the attribute definition in this typdef */ meta = obj_find_metadata(obj, attr->attr_name); if (meta) { metadef = meta->typdef; } else if (!nserr) { metadef = typ_get_basetype_typdef(NCX_BT_STRING); } } if (iskey || isvalue || islang || islastmodified) { metadef = typ_get_basetype_typdef(NCX_BT_STRING); } if (metadef) { /* alloc a new value struct for rhe attribute */ metaval = val_new_value(); if (!metaval) { res = ERR_INTERNAL_MEM; } else { /* parse the attribute string against the typdef */ res = val_parse_meta(metadef, attr, metaval); if (res == NO_ERR) { dlq_enque(metaval, &retval->metaQ); } else { val_free_value(metaval); } } } else { log_warn("\nWarning: unknown attribute found %s='%s'", attr->attr_qname, attr->attr_val); } if (res != NO_ERR) { retres = res; } } return retres; } /* parse_metadata */ /******************************************************************** * FUNCTION metadata_inst_check * * Validate that all the XML attributes in the specified * xml_node_t struct are pesent in appropriate numbers * * Since attributes are unordered, they all have to be parsed * before they can be checked for instance count * * INPUTS: * scb == session control block * msg == incoming RPC message * Errors are appended to msg->errQ * val == value to check for metadata errors * * OUTPUTS: * msg->errQ may be appended with new errors or warnings * * RETURNS: * status *********************************************************************/ static status_t metadata_inst_check (val_value_t *val) { obj_metadata_t *meta; uint32 cnt; xmlns_id_t yangid; yangid = xmlns_yang_id(); /* first check the inst count of the YANG attributes * may not need to do this at all if XmlTextReader * rejects duplicate XML attributes */ cnt = val_metadata_inst_count(val, yangid, YANG_K_KEY); if (cnt > 1) { return ERR_NCX_EXTRA_ATTR; } cnt = val_metadata_inst_count(val, yangid, YANG_K_VALUE); if (cnt > 1) { return ERR_NCX_EXTRA_ATTR; } if (!val->obj) { return NO_ERR; } for (meta = obj_first_metadata(val->obj); meta != NO_ERR; meta = obj_next_metadata(meta)) { cnt = val_metadata_inst_count(val, meta->nsid, meta->name); /* check the instance qualifier from the metadata * continue the loop if there is no error */ if (cnt > 1) { return ERR_NCX_EXTRA_ATTR; } } return NO_ERR; } /* metadata_inst_check */ /******************************************************************** * FUNCTION parse_btype * * Switch to dispatch to specific base type handler * * INPUTS: * scb == session control block * Input is read from scb->reader. * obj == object template to use for parsing * startnode == top node of the parameter to be parsed * Parser function will attempt to consume all the * nodes until the matching endnode is reached * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * msg->errQ may be appended with new errors or warnings * * RETURNS: * status *********************************************************************/ static status_t parse_btype (ses_cb_t *scb, obj_template_t *obj, const xml_node_t *startnode, val_value_t *retval) { ncx_btype_t btyp; status_t res, res2, res3; boolean nserr; if (LOGDEBUG3) { log_debug3("%s: %s:%s btyp:%s", __FUNCTION__, obj_get_mod_prefix(obj), obj_get_name(obj), tk_get_btype_sym(obj_get_basetype(obj))); } btyp = obj_get_basetype(obj); /* get the attribute values from the start node */ retval->nsid = startnode->nsid; /* check namespace errors except if the type is ANY* */ nserr = (btyp != NCX_BT_ANYDATA && btyp != NCX_BT_ANYXML); /* parse the attributes, if any; do not quick exit on this error */ res2 = parse_metadata(obj, startnode, nserr, retval); /* continue to parse the startnode depending on the base type * to record as many errors as possible */ switch (btyp) { case NCX_BT_ANYDATA: case NCX_BT_ANYXML: res = parse_any(scb, startnode, retval); break; case NCX_BT_ENUM: res = parse_enum(scb, obj, startnode, retval); break; case NCX_BT_EMPTY: res = parse_empty(scb, obj, startnode, retval); break; case NCX_BT_BOOLEAN: res = parse_boolean(scb, obj, startnode, retval); break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: res = parse_num(scb, obj, btyp, startnode, retval); break; case NCX_BT_LEAFREF: case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_SLIST: case NCX_BT_BITS: case NCX_BT_INSTANCE_ID: res = parse_string(scb, obj, btyp, startnode, retval); break; case NCX_BT_IDREF: res = parse_idref(scb, obj, startnode, retval); break; case NCX_BT_UNION: res = parse_union(scb, obj, startnode, retval); break; case NCX_BT_CONTAINER: case NCX_BT_LIST: res = parse_complex(scb, obj, NULL, btyp, startnode, retval); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } /* set the config flag for this value */ res3 = NO_ERR; if (res == NO_ERR && res2 == NO_ERR) { /* this has to be after the retval typdef is set */ res3 = metadata_inst_check(retval); } if (res != NO_ERR) { res3 = res; } else if (res2 != NO_ERR) { res3 = res2; } /* Reporting errors only once */ if(res3!=NO_ERR) { if(retval->res == NO_ERR) { if (LOGDEBUG3) { SET_ERROR(res3); } retval->res = res3; } else { if (LOGDEBUG3) { log_debug3("%s: %s:%s btyp:%s overwriting retval->res=%s with res=%s", __FUNCTION__, obj_get_mod_prefix(obj), obj_get_name(obj), tk_get_btype_sym(obj_get_basetype(obj)), get_error_string(retval->res), get_error_string(res3)); } } } return retval->res; } /* parse_btype */ /******************************************************************** * FUNCTION parse_btype_split * * Switch to dispatch to specific base type handler * * INPUTS: * scb == session control block * Input is read from scb->reader. * obj == object template to use for parsing output == foo-rpc/output object (if any) * startnode == top node of the parameter to be parsed * Parser function will attempt to consume all the * nodes until the matching endnode is reached * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * msg->errQ may be appended with new errors or warnings * * RETURNS: * status *********************************************************************/ static status_t parse_btype_split (ses_cb_t *scb, obj_template_t *obj, obj_template_t *output, const xml_node_t *startnode, val_value_t *retval) { ncx_btype_t btyp; status_t res, res2, res3; boolean nserr; btyp = obj_get_basetype(obj); /* get the attribute values from the start node */ retval->nsid = startnode->nsid; /* check namespace errors except if the type is ANY* */ nserr = (btyp != NCX_BT_ANYDATA && btyp != NCX_BT_ANYXML); /* parse the attributes, if any; do not quick exit on this error */ res2 = parse_metadata(obj, startnode, nserr, retval); /* continue to parse the startnode depending on the base type * to record as many errors as possible */ switch (btyp) { case NCX_BT_ANYDATA: case NCX_BT_ANYXML: res = parse_any(scb, startnode, retval); break; case NCX_BT_ENUM: res = parse_enum(scb, obj, startnode, retval); break; case NCX_BT_EMPTY: res = parse_empty(scb, obj, startnode, retval); break; case NCX_BT_BOOLEAN: res = parse_boolean(scb, obj, startnode, retval); break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: res = parse_num(scb, obj, btyp, startnode, retval); break; case NCX_BT_LEAFREF: case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_SLIST: case NCX_BT_BITS: case NCX_BT_INSTANCE_ID: res = parse_string(scb, obj, btyp, startnode, retval); break; case NCX_BT_UNION: res = parse_union(scb, obj, startnode, retval); break; case NCX_BT_CONTAINER: case NCX_BT_LIST: res = parse_complex(scb, obj, output, btyp, startnode, retval); break; default: log_error("\nError: got invalid btype '%d'", btyp); return SET_ERROR(ERR_INTERNAL_VAL); } /* set the config flag for this value */ res3 = NO_ERR; if (res == NO_ERR && res2 == NO_ERR) { /* this has to be after the retval typdef is set */ res3 = metadata_inst_check(retval); } if (res != NO_ERR) { retval->res = res; return res; } else if (res2 != NO_ERR) { retval->res = res2; return res2; } retval->res = res3; return res3; } /* parse_btype_split */ /************** E X T E R N A L F U N C T I O N S **********/ status_t val_parse_split (ses_cb_t *scb, obj_template_t *obj, obj_template_t *output, const xml_node_t *startnode, val_value_t *retval) { return parse_btype_split (scb, obj, output, startnode, retval); } /******************************************************************** * FUNCTION val_parse * * parse a value for a YANG type from a NETCONF PDU XML stream * * Parse NETCONF PDU sub-contents into value fields * This module does not enforce complex type completeness. * Different subsets of configuration data are permitted * in several standard (and any proprietary) RPC methods * * A seperate parsing phase is used to validate the input * contained in the returned val_value_t struct. * * This parsing phase checks that simple types are complete * and child members of complex types are valid (but maybe * missing or incomplete child nodes. * * INPUTS: * scb == session control block * obj == obj_template_t for the object type to parse * startnode == top node of the parameter to be parsed * retval == val_value_t that should get the results of the parsing * * OUTPUTS: * *retval will be filled in * * RETURNS: * status *********************************************************************/ status_t val_parse (ses_cb_t *scb, obj_template_t *obj, const xml_node_t *startnode, val_value_t *retval) { const xml_node_t *usenode; status_t res; xml_node_t topnode; #ifdef DEBUG if (scb == NULL || obj == NULL || retval == NULL) { /* non-recoverable error */ return SET_ERROR(ERR_INTERNAL_PTR); } #endif #ifdef MGR_VAL_PARSE_DEBUG if (LOGDEBUG3) { log_debug3("\nmgr_val_parse: %s:%s btyp:%s", obj_get_mod_prefix(obj), obj_get_name(obj), tk_get_btype_sym(obj_get_basetype(obj))); } #endif res = NO_ERR; usenode = NULL; xml_init_node(&topnode); if (startnode == NULL) { res = get_xml_node(scb, &topnode); if (res == NO_ERR) { val_set_name(retval, topnode.elname, xml_strlen(topnode.elname)); val_change_nsid(retval, topnode.nsid); usenode = &topnode; } } else { usenode = startnode; } /* get the element values */ if (res == NO_ERR) { res = parse_btype(scb, obj, usenode, retval); } xml_clean_node(&topnode); return res; } /* val_parse */ /* END file val_parse.c */ yuma123_2.14/netconf/src/ncx/xpath.c0000664000175000017500000030723014770023131017433 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xpath.c Schema and data model Xpath search support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 31dec07 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "dlq.h" #include "grp.h" #include "ncxconst.h" #include "ncx.h" #include "ncx_num.h" #include "obj.h" #include "tk.h" #include "typ.h" #include "var.h" #include "xpath.h" #include "xpath1.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define MAX_FILL 255 /******************************************************************** * FUNCTION do_errmsg * * Generate the errormsg * * INPUTS: * tkc == token chain in progress (may be NULL) * mod == module in progress * tkerr == error struct to use * res == error enumeration * *********************************************************************/ static void do_errmsg (tk_chain_t *tkc, ncx_module_t *mod, ncx_error_t *tkerr, status_t res) { if (tkc) { tkc->curerr = tkerr; } ncx_print_errormsg(tkc, mod, res); } /* do_errmsg */ /******************************************************************** * FUNCTION next_nodeid * * Get the next Name of QName segment of an Xpath schema-nodeid * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain in progress (may be NULL) * mod == module in progress * obj == object calling this fn (for error purposes) * target == Xpath expression string to evaluate * prefix == address for return buffer to store * prefix portion of QName (if any) * name == address for return buffer to store * name portion of segment * len == address of return byte count * * OUTPUTS: * *prefix == malloced buffer with prefix portion of QName * *name == malloced name name portion of QName * *cnt == number of bytes used in target * * RETURNS: * status *********************************************************************/ static status_t next_nodeid (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, const xmlChar *target, xmlChar **prefix, xmlChar **name, uint32 *len) { const xmlChar *p, *q; uint32 cnt; status_t res; *prefix = NULL; *name = NULL; *len = 0; /* find the EOS or a separator */ p = target; while (*p && *p != ':' && *p != '/') { p++; } cnt = (uint32)(p-target); if (!ncx_valid_name(target, cnt)) { log_error("\nError: invalid name string (%s)", target); res = ERR_NCX_INVALID_NAME; do_errmsg(tkc, mod, &obj->tkerr, res); return res; } if (*p==':') { /* copy prefix, then get name portion */ *prefix = m__getMem(cnt+1); if (!*prefix) { log_error("\nError: malloc failed"); res = ERR_INTERNAL_MEM; do_errmsg(tkc, mod, &obj->tkerr, res); return res; } xml_strncpy(*prefix, target, cnt); q = ++p; while (*q && *q != '/') { q++; } cnt = (uint32)(q-p); if (!ncx_valid_name(p, cnt)) { log_error("\nError: invalid name string (%s)", target); res = ERR_NCX_INVALID_NAME; do_errmsg(tkc, mod, &obj->tkerr, res); if (*prefix) { m__free(*prefix); *prefix = NULL; } return res; } *name = m__getMem(cnt+1); if (!*name) { log_error("\nError: malloc failed"); res = ERR_INTERNAL_MEM; do_errmsg(tkc, mod, &obj->tkerr, res); if (*prefix) { m__free(*prefix); *prefix = NULL; } return res; } xml_strncpy(*name, p, cnt); *len = (uint32)(q-target); } else { /* found EOS or pathsep, got just one 'name' string */ *name = m__getMem(cnt+1); if (!*name) { log_error("\nError: malloc failed"); res = ERR_INTERNAL_MEM; do_errmsg(tkc, mod, &obj->tkerr, res); return res; } xml_strncpy(*name, target, cnt); *len = cnt; } return NO_ERR; } /* next_nodeid */ /******************************************************************** * FUNCTION next_nodeid_noerr * * Get the next Name of QName segment of an Xpath schema-nodeid * * Error messages are not printed by this function!! * * INPUTS: * target == Xpath expression string in progress to evaluate * prefix == address for return buffer to store * prefix portion of QName (if any) * name == address for return buffer to store * name portion of segment * len == address of return byte count * * OUTPUTS: * *prefix == malloced buffer with prefix portion of QName * *name == malloced name name portion of QName * *cnt == number of bytes used in target * * RETURNS: * status *********************************************************************/ static status_t next_nodeid_noerr (const xmlChar *target, xmlChar **prefix, xmlChar **name, uint32 *len) { const xmlChar *p, *q; uint32 cnt; *prefix = NULL; *name = NULL; *len = 0; /* find the EOS or a separator */ p = target; while (*p && *p != ':' && *p != '/') { p++; } cnt = (uint32)(p-target); if (!ncx_valid_name(target, cnt)) { return ERR_NCX_INVALID_NAME; } if (*p==':') { *prefix = m__getMem(cnt+1); if (!*prefix) { return ERR_INTERNAL_MEM; } /* copy prefix, then get name portion */ xml_strncpy(*prefix, target, cnt); q = ++p; while (*q && *q != '/') { q++; } cnt = (uint32)(q-p); if (!ncx_valid_name(p, cnt)) { if (*prefix) { m__free(*prefix); *prefix = NULL; } return ERR_NCX_INVALID_NAME; } *name = m__getMem(cnt+1); if (!*name) { if (*prefix) { m__free(*prefix); *prefix = NULL; } return ERR_INTERNAL_MEM; } xml_strncpy(*name, p, cnt); *len = (uint32)(q-target); } else { /* found EOS or pathsep, got just one 'name' string */ *name = m__getMem(cnt+1); if (!*name) { return ERR_INTERNAL_MEM; } xml_strncpy(*name, target, cnt); *len = cnt; } return NO_ERR; } /* next_nodeid_noerr */ /******************************************************************** * FUNCTION next_val_nodeid * * Get the next Name of QName segment of an Xpath schema-nodeid * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * target == Xpath expression string to evaluate * logerrors = TRUE to use log_error, FALSE to skip it * prefix == address for return buffer to store * prefix portion of QName (if any) * name == address for return buffer to store * name portion of segment * len == address of return byte count * * OUTPUTS: * *prefix == malloced buffer with prefix portion of QName * *name == malloced name name portion of QName * *cnt == number of bytes used in target * * RETURNS: * status *********************************************************************/ static status_t next_val_nodeid (const xmlChar *target, boolean logerrors, xmlChar **prefix, xmlChar **name, uint32 *len) { const xmlChar *p, *q; uint32 cnt; *prefix = NULL; *name = NULL; *len = 0; /* find the EOS or a separator */ p = target; while (*p && *p != ':' && *p != '/') { p++; } cnt = (uint32)(p-target); if (!ncx_valid_name(target, cnt)) { if (logerrors) { log_error("\nError: invalid name string (%s)", target); } return ERR_NCX_INVALID_NAME; } if (*p==':') { *prefix = m__getMem(cnt+1); if (!*prefix) { return ERR_INTERNAL_MEM; } /* copy prefix, then get name portion */ xml_strncpy(*prefix, target, cnt); q = ++p; while (*q && *q != '/') { q++; } cnt = (uint32)(q-p); if (!ncx_valid_name(p, cnt)) { if (logerrors) { log_error("\nError: invalid name string (%s)", target); } m__free(*prefix); *prefix = NULL; return ERR_NCX_INVALID_NAME; } *name = m__getMem(cnt+1); if (!*name) { if (*prefix) { m__free(*prefix); *prefix = NULL; } return ERR_INTERNAL_MEM; } xml_strncpy(*name, p, cnt); *len = (uint32)(q-target); } else { *name = m__getMem(cnt+1); if (!*name) { return ERR_INTERNAL_MEM; } /* found EOS or pathsep, got just one 'name' string */ xml_strncpy(*name, target, cnt); *len = cnt; } return NO_ERR; } /* next_val_nodeid */ /******************************************************************** * FUNCTION find_schema_node * * Follow the absolute-path or descendant node path expression * and return the obj_template_t that it indicates, and the * que that the object is in * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parswer control block to use * tkc == token chain in progress * mod == module in progress * obj == object calling this fn (for error purposes) * datadefQ == Q of obj_template_t containing 'obj' * target == Xpath expression string to evaluate * targobj == address of return object (may be NULL) * targQ == address of return target queue (may be NULL) * tkerr == error struct to use * * OUTPUTS: * if non-NULL inputs: * *targobj == target object * *targQ == datadefQ header for targobj * * RETURNS: * status *********************************************************************/ static status_t find_schema_node (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *datadefQ, const xmlChar *target, obj_template_t **targobj, dlq_hdr_t **targQ, ncx_error_t *tkerr) { ncx_import_t *imp; obj_template_t *curobj, *nextobj; dlq_hdr_t *curQ; const xmlChar *str; uint32 len; status_t res; ncx_node_t dtyp; xmlChar *prefix; xmlChar *name; imp = NULL; dtyp = NCX_NT_OBJ; curQ = NULL; prefix = NULL; name = NULL; /* skip the first fwd slash, if any */ if (*target == '/') { str = target+1; } else { str = target; } /* get the first QName (prefix, name) */ res = next_nodeid(tkc, mod, obj, str, &prefix, &name, &len); if (res != NO_ERR) { if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } else { str += len; } /* get the import if there is a real prefix entered */ if (prefix && xml_strcmp(prefix, mod->prefix)) { imp = ncx_find_pre_import(mod, prefix); if (!imp) { log_error("\nError: prefix '%s' not found in module imports" " in Xpath target %s", prefix, target); res = ERR_NCX_INVALID_NAME; do_errmsg(tkc, mod, tkerr, res); m__free(prefix); if (name) { m__free(name); } return res; } } /* get the first object template */ if (imp) { curobj = ncx_locate_modqual_import(pcb, imp, name, &dtyp); } else if (*target == '/') { curobj = obj_find_template_top(mod, ncx_get_modname(mod), name); } else { curobj = obj_find_template(datadefQ, ncx_get_modname(mod), name); } if (!curobj) { if (ncx_valid_name2(name)) { res = ERR_NCX_DEF_NOT_FOUND; } else { res = ERR_NCX_INVALID_NAME; } log_error("\nError: object '%s' not found in module %s" " in Xpath target %s", name, (imp && imp->mod) ? imp->mod->name : mod->name, target); do_errmsg(tkc, mod, tkerr, res); if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } else { curQ = datadefQ; } if (obj_is_augclone(curobj)) { res = ERR_NCX_INVALID_VALUE; log_error("\nError: augment is external: node '%s'" " from module %s, line %u in Xpath target %s", (name) ? name : NCX_EL_NONE, curobj->tkerr.mod->name, curobj->tkerr.linenum, target); do_errmsg(tkc, mod, tkerr, res); if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } if (prefix) { m__free(prefix); prefix = NULL; } if (name) { m__free(name); name = NULL; } /* got the first object; keep parsing node IDs * until the Xpath expression is done or an error occurs */ while (*str == '/') { str++; /* check for trailing '/' */ if (*str == '\0') { continue; } /* get the next QName (prefix, name) */ res = next_nodeid(tkc, mod, obj, str, &prefix, &name, &len); if (res != NO_ERR) { if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } else { str += len; } /* make sure the prefix is valid, if present */ if (prefix && xml_strcmp(prefix, mod->prefix)) { imp = ncx_find_pre_import(mod, prefix); if (!imp) { log_error("\nError: prefix '%s' not found in module" " imports in Xpath target '%s'", prefix, target); res = ERR_NCX_INVALID_NAME; do_errmsg(tkc, mod, tkerr, res); m__free(prefix); if (name) { m__free(name); } return res; } } else { imp = NULL; } /* make sure the name is a valid name string */ if (name && !ncx_valid_name2(name)) { log_error("\nError: object name '%s' not a valid " "identifier in Xpath target '%s'", name, target); res = ERR_NCX_INVALID_NAME; do_errmsg(tkc, mod, tkerr, res); if (prefix) { m__free(prefix); } m__free(name); return res; } /* determine 'nextval' based on [curval, prefix, name] */ curQ = obj_get_datadefQ(curobj); if (name && curQ) { nextobj = obj_find_template(curQ, (imp && imp->mod) ? imp->mod->name : ncx_get_modname(mod), name); } else { res = ERR_NCX_DEFSEG_NOT_FOUND; log_error("\nError: '%s' in Xpath target '%s' invalid: " "%s on line %u is a %s", name, target, obj_get_name(curobj), curobj->tkerr.linenum, obj_get_typestr(curobj)); do_errmsg(tkc, mod, tkerr, res); if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } if(nextobj->parent != curobj) { res = ERR_NCX_DEFSEG_NOT_FOUND; log_error("\nError: in '%s' the schema node parent of '%s' is '%s' while '%s' is its document node parent. Schema nodes like (case, choice, input or output) are mandatory in schema node identifier expressions: " "%s on line %u", target, obj_get_name(nextobj), obj_get_name(nextobj->parent), obj_get_name(curobj), tkerr->mod->name, tkerr->linenum); do_errmsg(tkc, mod, tkerr, res); if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } if (nextobj) { curobj = nextobj; } else { res = ERR_NCX_DEFSEG_NOT_FOUND; log_error("\nError: object '%s' not found in module %s", name, (imp && imp->mod) ? imp->mod->name : mod->name); do_errmsg(tkc, mod, tkerr, res); if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } if (prefix) { m__free(prefix); prefix = NULL; } if (name) { m__free(name); name = NULL; } } if (targobj) { *targobj = curobj; } if (targQ) { *targQ = curQ; } if (prefix) { m__free(prefix); } if (name) { m__free(name); } return NO_ERR; } /* find_schema_node */ /******************************************************************** * FUNCTION find_schema_node_int * * Follow the absolute-path expression * and return the obj_template_t that it indicates * A missing prefix means check any namespace for the symbol * * !!! Internal version !!! * !!! Error messages are not printed by this function!! * * INPUTS: * target == Absolute Xpath expression string to evaluate * targobj == address of return object (may be NULL) * * OUTPUTS: * if non-NULL inputs: * *targobj == target object * * RETURNS: * status *********************************************************************/ static status_t find_schema_node_int (const xmlChar *target, obj_template_t **targobj) { obj_template_t *curobj, *nextobj; dlq_hdr_t *curQ; ncx_module_t *mod; const xmlChar *str; xmlChar *prefix; xmlChar *name; uint32 len; status_t res; prefix = NULL; name = NULL; /* skip the first fwd slash, if any * the target must be from the config root * so if the first fwd-slash is missing then * just keep going and assume the config root anyway */ if (*target == '/') { str = ++target; } else { str = target; } /* get the first QName (prefix, name) */ res = next_nodeid_noerr(str, &prefix, &name, &len); if (res != NO_ERR) { if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } else { str += len; } /* get the import if there is a real prefix entered */ if (prefix) { mod = (ncx_module_t *)xmlns_get_modptr(xmlns_find_ns_by_prefix(prefix)); if (!mod) { if (prefix) { m__free(prefix); } if (name) { m__free(name); } return ERR_NCX_INVALID_NAME; } /* get the first object template */ curobj = obj_find_template_top(mod, ncx_get_modname(mod), name); } else { /* no prefix given, check all top-level objects */ curobj = ncx_find_any_object(name); } /* check if first level object found */ if (!curobj) { if (ncx_valid_name2(name)) { res = ERR_NCX_DEF_NOT_FOUND; } else { res = ERR_NCX_INVALID_NAME; } if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } if (prefix) { m__free(prefix); prefix = NULL; } if (name) { m__free(name); name = NULL; } if (obj_is_augclone(curobj)) { return ERR_NCX_INVALID_VALUE; } /* got the first object; keep parsing node IDs * until the Xpath expression is done or an error occurs */ while (*str == '/') { str++; /* get the next QName (prefix, name) */ res = next_nodeid_noerr(str, &prefix, &name, &len); if (res != NO_ERR) { if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } else { str += len; } /* make sure the name is a valid name string */ if (!name || !ncx_valid_name2(name)) { if (prefix) { m__free(prefix); } if (name) { m__free(name); } return ERR_NCX_INVALID_NAME; } /* determine 'nextval' based on [curval, prefix, name] */ curQ = obj_get_datadefQ(curobj); if (!curQ) { if (prefix) { m__free(prefix); } if (name) { m__free(name); } return ERR_NCX_DEFSEG_NOT_FOUND; } /* make sure the prefix is valid, if present */ if (prefix) { mod = (ncx_module_t *)xmlns_get_modptr (xmlns_find_ns_by_prefix(prefix)); if (!mod) { m__free(prefix); m__free(name); return ERR_NCX_INVALID_NAME; } nextobj = obj_find_template(curQ, ncx_get_modname(mod), name); } else { /* no prefix given; try current module first */ nextobj = obj_find_template(curQ, obj_get_mod_name(curobj), name); if (!nextobj) { nextobj = obj_find_template(curQ, NULL, name); } } if (prefix) { m__free(prefix); prefix = NULL; } if (name) { m__free(name); name = NULL; } /* setup next loop or error exit because last node not found */ if (nextobj) { curobj = nextobj; } else { return ERR_NCX_DEFSEG_NOT_FOUND; } } if (targobj) { *targobj = curobj; } return NO_ERR; } /* find_schema_node_int */ /******************************************************************** * FUNCTION find_val_node * * Follow the Xpath expression * and return the val_value_t that it indicates, if any * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * startval == val_value_t node to start search * mod == module to use for the default context * and prefixes will be relative to this module's * import statements. * == NULL and the default registered prefixes * will be used * target == Xpath expression string to evaluate * targval == address of return value (may be NULL) * * OUTPUTS: * if non-NULL inputs: * *targval == target value node * * RETURNS: * status *********************************************************************/ static status_t find_val_node (val_value_t *startval, ncx_module_t *mod, const xmlChar *target, val_value_t **targval) { xpath_resnode_t *resnode; xpath_pcb_t *xpathpcb; xpath_result_t *result; val_value_t* root_val; status_t res = NO_ERR; assert(startval!=NULL); *targval=NULL; for(root_val=startval;!obj_is_root(root_val->obj) && root_val->parent!=NULL;root_val=root_val->parent); xpathpcb = xpath_new_pcb(target, NULL); if(!obj_is_root(root_val->obj)) { root_val = NULL; /* the chain ends before root - only relative targets can be resolved */ xpathpcb->tkerr.mod = mod; } result = xpath1_eval_expr(xpathpcb, startval, root_val, FALSE /* logerrors */, FALSE /* non-configonly */, &res); if(res!=NO_ERR) { xpath_free_pcb(xpathpcb); return res; } assert(result); /* return the first value even if there are more */ for (resnode = (xpath_resnode_t *)dlq_firstEntry(&result->r.nodeQ); resnode != NULL; resnode = (xpath_resnode_t *)dlq_nextEntry(resnode)) { *targval = resnode->node.valptr; } free(result); xpath_free_pcb(xpathpcb); return NO_ERR; } /* find_val_node */ #if 0 // NOT USED SINCE UNIQUE ALLOWS NODE-SETS /******************************************************************** * FUNCTION find_val_node_unique * * Follow the relative-path schema-nodeid expression * and return the val_value_t that it indicates, if any * Used in the YANG unique-stmt component * * [/] foo/bar/baz * * No predicates are expected, but choice and case nodes * are expected and will be accounted for if present * * XML mode - unique-smmt processing * check first before logging errors; * * If logerrors: * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * startval == val_value_t node to start search * mod == module to use for the default context * and prefixes will be relative to this module's * import statements. * == NULL and the default registered prefixes * will be used * target == relative path expr to evaluate * logerrors == TRUE to log_error, FALSE to skip * targval == address of return value (may be NULL) * * OUTPUTS: * if non-NULL inputs: * *targval == target value node * * RETURNS: * status *********************************************************************/ static status_t find_val_node_unique (val_value_t *startval, ncx_module_t *mod, const xmlChar *target, boolean logerrors, val_value_t **targval) { val_value_t *curval; ncx_module_t *usemod; const xmlChar *str; xmlChar *prefix; xmlChar *name; uint32 len; status_t res; prefix = NULL; name = NULL; /* skip the first fwd slash, if any */ if (*target == '/') { str = ++target; } else { str = target; } /* get the first QName (prefix, name) */ res = next_val_nodeid(str, logerrors, &prefix, &name, &len); if (res != NO_ERR) { if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } else { str += len; } res = xpath_get_curmod_from_prefix(prefix, mod, &usemod); if (res != NO_ERR) { if (prefix) { if (logerrors) { log_error("\nError: module not found for prefix %s" " in Xpath target %s", prefix, target); } m__free(prefix); } else { if (logerrors) { log_error("\nError: no module prefix specified" " in Xpath target %s", target); } } if (name) { m__free(name); } return ERR_NCX_MOD_NOT_FOUND; } /* get the first value node */ curval = val_find_child(startval, usemod->name, name); if (!curval) { if (ncx_valid_name2(name)) { res = ERR_NCX_DEF_NOT_FOUND; } else { res = ERR_NCX_INVALID_NAME; } if (logerrors) { log_error("\nError: value node '%s' not found for module %s" " in Xpath target %s", name, usemod->name, target); } if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } if (prefix) { m__free(prefix); prefix = NULL; } if (name) { m__free(name); name = NULL; } /* got the first object; keep parsing node IDs * until the Xpath expression is done or an error occurs */ while (*str == '/') { str++; /* get the next QName (prefix, name) */ res = next_val_nodeid(str, logerrors, &prefix, &name, &len); if (res != NO_ERR) { if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } else { str += len; } res = xpath_get_curmod_from_prefix(prefix, mod, &usemod); if (res != NO_ERR) { if (prefix) { if (logerrors) { log_error("\nError: module not found for prefix %s" " in Xpath target %s", prefix, target); } m__free(prefix); } else { if (logerrors) { log_error("\nError: no module prefix specified" " in Xpath target %s", target); } } if (name) { m__free(name); } return ERR_NCX_MOD_NOT_FOUND; } /* determine 'nextval' based on [curval, prefix, name] */ switch (curval->obj->objtype) { case OBJ_TYP_CONTAINER: case OBJ_TYP_LIST: case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: case OBJ_TYP_RPC: case OBJ_TYP_RPCIO: case OBJ_TYP_NOTIF: curval = val_find_child(curval, usemod->name, name); if (!curval) { if (ncx_valid_name2(name)) { res = ERR_NCX_DEF_NOT_FOUND; } else { res = ERR_NCX_INVALID_NAME; } if (logerrors) { log_error("\nError: value node '%s' not found for module %s" " in Xpath target %s", (name) ? name : NCX_EL_NONE, usemod->name, target); } if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } break; case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: res = ERR_NCX_DEFSEG_NOT_FOUND; if (logerrors) { log_error("\nError: '%s' in Xpath target '%s' invalid: " "%s is a %s", (name) ? name : NCX_EL_NONE, target, curval->name, obj_get_typestr(curval->obj)); } if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; default: res = SET_ERROR(ERR_INTERNAL_VAL); if (logerrors) { do_errmsg(NULL, mod, NULL, res); } if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } if (prefix) { m__free(prefix); prefix = NULL; } if (name) { m__free(name); name = NULL; } } if (prefix) { m__free(prefix); } if (name) { m__free(name); } if (targval) { *targval = curval; } return NO_ERR; } /* find_val_node_unique */ #endif /******************************************************************** * FUNCTION find_obj_node_unique * * Follow the relative-path schema-nodeid expression * and return the object node that it indicates, if any * Used in the YANG unique-stmt component * * [/] foo/bar/baz * * No predicates are expected, but choice and case nodes * are expected and will be accounted for if present * * XML mode - unique-smmt processing * check first before logging errors; * * If logerrors: * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * startobj == object node to start search * mod == module to use for the default context * and prefixes will be relative to this module's * import statements. * == NULL and the default registered prefixes * will be used * target == relative path expr to evaluate * logerrors == TRUE to log_error, FALSE to skip * targobj == address of return value (may be NULL) * * OUTPUTS: * if non-NULL inputs: * *targobj == target object node * * RETURNS: * status *********************************************************************/ static status_t find_obj_node_unique (obj_template_t *startobj, ncx_module_t *mod, const xmlChar *target, boolean logerrors, obj_template_t **targobj) { ncx_module_t *usemod = NULL; const xmlChar *str = NULL; xmlChar *prefix = NULL; xmlChar *name = NULL; uint32 len = 0; /* skip the first fwd slash, if any */ if (*target == '/') { str = ++target; } else { str = target; } /* get the first QName (prefix, name) */ status_t res = next_val_nodeid(str, logerrors, &prefix, &name, &len); if (res != NO_ERR) { if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } else { str += len; } res = xpath_get_curmod_from_prefix(prefix, mod, &usemod); if (res != NO_ERR) { if (prefix) { if (logerrors) { log_error("\nError: module not found for prefix %s" " in Xpath target %s", prefix, target); } m__free(prefix); } else { if (logerrors) { log_error("\nError: no module prefix specified" " in Xpath target %s", target); } } if (name) { m__free(name); } return ERR_NCX_MOD_NOT_FOUND; } /* get the first value node */ obj_template_t *curobj = obj_find_child(startobj, usemod->name, name); if (!curobj) { if (ncx_valid_name2(name)) { res = ERR_NCX_DEF_NOT_FOUND; } else { res = ERR_NCX_INVALID_NAME; } if (logerrors) { log_error("\nError: value node '%s' not found for module %s" " in Xpath target %s", name, usemod->name, target); } if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } if (prefix) { m__free(prefix); prefix = NULL; } if (name) { m__free(name); name = NULL; } /* got the first object; keep parsing node IDs * until the Xpath expression is done or an error occurs */ while (*str == '/') { str++; /* get the next QName (prefix, name) */ res = next_val_nodeid(str, logerrors, &prefix, &name, &len); if (res != NO_ERR) { if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } else { str += len; } res = xpath_get_curmod_from_prefix(prefix, mod, &usemod); if (res != NO_ERR) { if (prefix) { if (logerrors) { log_error("\nError: module not found for prefix %s" " in Xpath target %s", prefix, target); } m__free(prefix); } else { if (logerrors) { log_error("\nError: no module prefix specified" " in Xpath target %s", target); } } if (name) { m__free(name); } return ERR_NCX_MOD_NOT_FOUND; } /* determine 'nextval' based on [curval, prefix, name] */ switch (curobj->objtype) { case OBJ_TYP_CONTAINER: case OBJ_TYP_LIST: case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: case OBJ_TYP_RPC: case OBJ_TYP_RPCIO: case OBJ_TYP_NOTIF: curobj = obj_find_child(curobj, usemod->name, name); if (!curobj) { if (ncx_valid_name2(name)) { res = ERR_NCX_DEF_NOT_FOUND; } else { res = ERR_NCX_INVALID_NAME; } if (logerrors) { log_error("\nError: value node '%s' not found for module %s" " in Xpath target %s", (name) ? name : NCX_EL_NONE, usemod->name, target); } if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } break; case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: res = ERR_NCX_DEFSEG_NOT_FOUND; if (logerrors) { log_error("\nError: '%s' in Xpath target '%s' invalid: " "%s is a %s", (name) ? name : NCX_EL_NONE, target, obj_get_name(curobj), obj_get_typestr(curobj)); } if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; default: res = SET_ERROR(ERR_INTERNAL_VAL); if (logerrors) { do_errmsg(NULL, mod, NULL, res); } if (prefix) { m__free(prefix); } if (name) { m__free(name); } return res; } if (prefix) { m__free(prefix); prefix = NULL; } if (name) { m__free(name); name = NULL; } } if (prefix) { m__free(prefix); } if (name) { m__free(name); } if (targobj) { *targobj = curobj; } return NO_ERR; } /* find_obj_node_unique */ /******************************************************************** * FUNCTION decode_url_string * * Fill buffer with a plain string from a URL string * * INPUTS: * urlstr == string to convert * urlstrlen == number of bytes to check * buffer == buffer to fill; may be NULL to get count * cnt == address of return cnt * * OUTPUTS: * if non-NULL inputs: * *cnt = number bytes required * * RETURNS: * status *********************************************************************/ static status_t decode_url_string (const xmlChar *urlstr, uint32 urlstrlen, xmlChar *buffer, uint32 *cnt) { const xmlChar *instr; xmlChar *writeptr, ch, numbuff[4]; uint32 inlen, outlen; boolean done; ncx_num_t num; status_t res; *cnt = 0; instr = urlstr; writeptr = buffer; inlen = 0; outlen = 0; ncx_init_num(&num); /* normal case, there is some top-level node next */ done = FALSE; while (!done) { /* check end of input string */ if (inlen == urlstrlen) { if (writeptr != NULL) { *writeptr = 0; } done = TRUE; continue; } ch = 0; if (*instr == '%') { /* hex encoded char */ instr++; inlen++; if (inlen + 2 <= urlstrlen) { numbuff[0] = instr[0]; numbuff[1] = instr[1]; numbuff[2] = 0; res = ncx_convert_num(numbuff, NCX_NF_HEX, NCX_BT_UINT8, &num); if (res != NO_ERR) { ncx_clean_num(NCX_BT_UINT8, &num); return res; } ch = (xmlChar)num.u; ncx_clean_num(NCX_BT_UINT8, &num); instr += 2; inlen += 2; } else { /* invalid escaped char found */ return ERR_NCX_INVALID_VALUE; } } else { /* normal char */ ch = *instr++; inlen++; } if (ch == 0) { return ERR_NCX_INVALID_VALUE; } else { outlen++; if (writeptr != NULL) { *writeptr++ = ch; } } } *cnt = outlen; return NO_ERR; } /* decode_url_string */ /******************************************************************** * FUNCTION decode_url_esc_string * * Fill buffer with a plain string from a * yangcli escaped URL content string * * INPUTS: * urlstr == string to convert, must be 0-terminated * buffer == buffer to fill; may be NULL to get count * incnt == address of return cnt of bytes used in urlstr * outcnt == address of return cnt of bytes written to buffer * * OUTPUTS: * if non-NULL inputs: * *incnt = number bytes used in urlstr * *outcnt = number bytes required for buffer write * * RETURNS: * status *********************************************************************/ static status_t decode_url_esc_string (const xmlChar *urlstr, xmlChar *buffer, uint32 *incnt, uint32 *outcnt) { const xmlChar *instr; xmlChar *writeptr, ch, numbuff[4]; uint32 outlen; boolean done; ncx_num_t num; status_t res; *incnt = 0; *outcnt = 0; instr = urlstr; writeptr = buffer; outlen = 0; done = FALSE; ncx_init_num(&num); res = NO_ERR; /* normal case, there is some top-level node next */ while (!done) { if (*instr == 0) { done = TRUE; continue; } ch = 0; /* check end of input string */ if (*instr == '/' && instr[1] != '/') { /* reached the end of the content string */ if (writeptr != NULL) { *writeptr = 0; } done = TRUE; continue; } else if (*instr == '/' && instr[1] == '/') { /* found an escaped forward slash */ ch = '/'; instr += 2; } else if (*instr == '%') { /* hex encoded char */ instr++; if (instr[0] && instr[1]) { numbuff[0] = instr[0]; numbuff[1] = instr[1]; numbuff[2] = 0; res = ncx_convert_num(numbuff, NCX_NF_HEX, NCX_BT_UINT8, &num); if (res != NO_ERR) { ncx_clean_num(NCX_BT_UINT8, &num); return res; } ch = (xmlChar)num.u; ncx_clean_num(NCX_BT_UINT8, &num); instr += 2; } else { /* invalid escaped char found */ return ERR_NCX_INVALID_VALUE; } } else { /* normal char */ ch = *instr++; } if (ch == 0) { return ERR_NCX_INVALID_VALUE; } else { outlen++; if (writeptr != NULL) { *writeptr++ = ch; } } } *incnt = (uint32)(instr - urlstr); *outcnt = outlen; return NO_ERR; } /* decode_url_esc_string */ /******************************************************************** * FUNCTION fill_xpath_string * * Fill buffer with an XPath string from a URL string * * INPUTS: * urlpath == string to convert * buff == buffer to fill; may be NULL to get count * match_names == enum for selected match names mode * alt_naming == TRUE if alt-name and cli-drop-node-name * containers should be checked * wildcards == TRUE if 'skip key wildcard' allowed * FALSE if not allowed * withkeys == TRUE if keys are expected * FALSE if just nodes are expected * cnt == address of return cnt * * OUTPUTS: * if non-NULL inputs: * *cnt = number bytes required * * RETURNS: * status *********************************************************************/ static status_t fill_xpath_string (const xmlChar *urlpath, xmlChar *buffer, ncx_name_match_t match_names, boolean alt_naming, boolean wildcards, boolean withkeys, uint32 *cnt) { const xmlChar *startstr, *p; obj_template_t *targobj; obj_key_t *objkey; xmlChar *writeptr, *tempbuff, *usebuff; uint32 inlen, outlen, outtotal; boolean done, expectnode; status_t res; xmlChar namebuff[MAX_FILL+1]; *cnt = 0; if (*urlpath != '/') { return ERR_NCX_INVALID_VALUE; } startstr = urlpath+1; /* check special case; path is just docroot */ if (*startstr == 0) { if (buffer != NULL) { buffer[0] = '/'; buffer[1] = 0; } *cnt = 1; return NO_ERR; } res = NO_ERR; targobj = NULL; expectnode = TRUE; inlen = 0; outlen = 0; objkey = NULL; tempbuff = NULL; usebuff = NULL; /* account for output of starting '/' */ outtotal = 1; writeptr = buffer; if (writeptr != NULL) { *writeptr++ = '/'; } /* check corner-case: URL starts with // - not allowed */ if (*startstr == '/') { return ERR_NCX_INVALID_VALUE; } /* keep getting nodes until string ends or error out */ done = FALSE; while (!done) { /* get the next string token to process */ if (expectnode) { /* get the chars that represent the content * in this mode just skip to the end or the next '/' */ p = startstr; while (*p != 0 && *p != '/') { p++; } inlen = (uint32)(p - startstr); if (inlen == 0) { /* invalid URL probably out of synch * empty identifier string */ return ERR_NCX_INVALID_VALUE; } /* check corner-case wildcard in wrong place */ if (inlen == 1 && *startstr == XP_URL_ESC_WILDCARD) { return ERR_NCX_INVALID_VALUE; } /* got some string content; convert to plain string */ if (inlen >= MAX_FILL) { tempbuff = m__getMem(inlen + 1); if (tempbuff == NULL) { return ERR_INTERNAL_MEM; } usebuff = tempbuff; } else { usebuff = namebuff; } /* get the identifier into the name buffer first */ outlen = 0; res = decode_url_string(startstr, inlen, usebuff, &outlen); if (res == NO_ERR) { const xmlChar *testname; /* get the target database object to use */ if (targobj == NULL) { /* find a top-level data node object */ targobj = ncx_match_any_object(usebuff, match_names, alt_naming, &res); } else { targobj = obj_find_child_ex(targobj, NULL, /* match any module */ usebuff, match_names, alt_naming, TRUE, /* dataonly */ &res); } if (targobj != NULL) { testname = obj_get_name(targobj); if (writeptr != NULL) { writeptr += xml_strcpy(writeptr, testname); } outtotal += xml_strlen(testname); } } /* cleanup temp buff */ if (tempbuff != NULL) { m__free(tempbuff); tempbuff = NULL; } /* check error exit */ if (targobj == NULL || res != NO_ERR) { if (res == NO_ERR) { return ERR_NCX_INVALID_VALUE; } else { return res; } } /* skip to the rpc input node if needed * this code does not yet support * custom RPC operations; just data tree access * this code will not be reached for a custom RPC * if dataonly is set to TRUE above */ if (targobj->objtype == OBJ_TYP_RPC) { targobj = obj_find_child(targobj, NULL, YANG_K_INPUT); if (targobj == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } } objkey = NULL; if (withkeys) { /* check list keys to follow */ objkey = obj_first_key(targobj); if (objkey != NULL) { expectnode = FALSE; } } if (objkey == NULL) { if (*p == '/') { outtotal++; if (writeptr != NULL) { *writeptr++ = '/'; } } } } else { /* expecting a key value */ const xmlChar *namestr = obj_get_name(objkey->keyobj); if (namestr == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } /* check if this key is being skipped */ if (startstr[0] == XP_URL_ESC_WILDCARD && startstr[1] == '/' && startstr[2] != '/') { if (wildcards) { /* skip this key in the output */ p = &startstr[1]; } else { return ERR_NCX_INVALID_VALUE; } } else { /* treat whatever string that is present * as a key content node * account for [foo='val'] predicate * first the [foo=' part */ outtotal += (1 + xml_strlen(namestr) + 2); if (writeptr != NULL) { *writeptr++ = '['; writeptr += xml_strcpy(writeptr, namestr); *writeptr++ = '='; *writeptr++ = '\''; } /* get the decoded value into the real buffer if used */ inlen = 0; outlen = 0; res = decode_url_esc_string(startstr, writeptr, &inlen, &outlen); if (res != NO_ERR) { return res; } outtotal += outlen; if (writeptr != NULL) { writeptr += outlen; } /* account for the '] part */ outtotal += 2; if (writeptr != NULL) { *writeptr++ = '\''; *writeptr++ = ']'; } p = &startstr[inlen]; } /* set up next key if any * account for next '/' if needed */ objkey = obj_next_key(objkey); if (objkey == NULL) { expectnode = TRUE; if (*p == '/') { outtotal++; if (writeptr != NULL) { *writeptr++ = '/'; } } } } /* input string is either done or pointing at next '/' char */ if (*p == 0) { done = TRUE; } else { /* skip '/' input char */ startstr = ++p; } } if (writeptr != NULL) { *writeptr = 0; } *cnt = outtotal; return NO_ERR; } /* fill_xpath_string */ /************ E X T E R N A L F U N C T I O N S ************/ /********* S C H E M A N O D E I D S U P P O R T ***********/ /******************************************************************** * FUNCTION xpath_find_schema_target * * find target, save in *targobj * Follow the absolute-path or descendant-node path expression * and return the obj_template_t that it indicates, and the * que that the object is in * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain in progress (may be NULL: errmsg only) * mod == module in progress * obj == augment object initiating search, NULL to start at top * datadefQ == Q of obj_template_t containing 'obj' * target == Xpath expression string to evaluate * targobj == address of return object (may be NULL) * targQ == address of return target queue (may be NULL) * * OUTPUTS: * if non-NULL inputs: * *targobj == target object * *targQ == datadefQ Q header which contains targobj * * RETURNS: * status *********************************************************************/ status_t xpath_find_schema_target (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *datadefQ, const xmlChar *target, obj_template_t **targobj, dlq_hdr_t **targQ) { return xpath_find_schema_target_err(pcb, tkc, mod, obj, datadefQ, target, targobj, targQ, NULL); } /* xpath_find_schema_target */ /******************************************************************** * FUNCTION xpath_find_schema_target_err * * find target, save in *targobj, use the errtk if error * Same as xpath_find_schema_target except a token struct * is provided to use for the error token, instead of 'obj' * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain in progress (may be NULL: errmsg only) * mod == module in progress * obj == augment object initiating search, NULL to start at top * datadefQ == Q of obj_template_t containing 'obj' * target == Xpath expression string to evaluate * targobj == address of return object (may be NULL) * targQ == address of return target queue (may be NULL) * tkerr == error struct to use if any messages generated * * OUTPUTS: * if non-NULL inputs: * *targobj == target object * *targQ == datadefQ header for targobj * * RETURNS: * status *********************************************************************/ status_t xpath_find_schema_target_err (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *datadefQ, const xmlChar *target, obj_template_t **targobj, dlq_hdr_t **targQ, ncx_error_t *tkerr) { status_t res; #ifdef DEBUG if (!mod || !datadefQ || !target) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (*target == '/') { /* check error: if nested object is using an abs. Xpath */ if (obj && obj->parent && !obj_is_root(obj->parent)) { log_error("\nError: Absolute Xpath expression not " "allowed here (%s)", target); res = ERR_NCX_INVALID_VALUE; do_errmsg(tkc, mod, tkerr ? tkerr : &obj->tkerr, res); return res; } } res = find_schema_node(pcb, tkc, mod, obj, datadefQ, target, targobj, targQ, tkerr ? tkerr : (obj ? &obj->tkerr : NULL)); return res; } /* xpath_find_schema_target_err */ /******************************************************************** * FUNCTION xpath_find_schema_target_int * * internal find target, without any error reporting * Follow the absolute-path expression * and return the obj_template_t that it indicates * * Internal access version * Error messages are not printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * target == absolute Xpath expression string to evaluate * targobj == address of return object (may be NULL) * * OUTPUTS: * if non-NULL inputs: * *targobj == target object * * RETURNS: * status *********************************************************************/ status_t xpath_find_schema_target_int (const xmlChar *target, obj_template_t **targobj) { #ifdef DEBUG if (!target) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return find_schema_node_int(target, targobj); } /* xpath_find_schema_target_int */ /******************************************************************** * FUNCTION xpath_find_val_target * * used by cfg.c to find parms in the value struct for * a config file (ncx:cli) * * Follow the absolute-path Xpath expression as used * internally to identify a config DB node * and return the val_value_t that it indicates * * Expression must be the node-path from root for * the desired node. * * Error messages are logged by this function * * INPUTS: * startval == top-level start element to search * mod == module to use for the default context * and prefixes will be relative to this module's * import statements. * == NULL and the default registered prefixes * will be used * target == Xpath expression string to evaluate * targval == address of return value (may be NULL) * * OUTPUTS: * if non-NULL inputs and value node found: * *targval == target value node * If non-NULL targval and error exit: * *targval == last good node visited in expression (if any) * * RETURNS: * status *********************************************************************/ status_t xpath_find_val_target (val_value_t *startval, ncx_module_t *mod, const xmlChar *target, val_value_t **targval) { #ifdef DEBUG if (!startval || !target) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return find_val_node(startval, mod, target, targval); } /* xpath_find_val_target */ /******************************************************************** * FUNCTION xpath_find_val_unique * * called by server to find a descendant value node * based on a relative-path sub-clause of a unique-stmt * * Follow the relative-path Xpath expression as used * internally to identify a config DB node * and return the val_value_t that it indicates * * Error messages are logged by this function * only if logerrors is TRUE * * INPUTS: * startval == starting context node (contains unique-stmt) * mod == module to use for the default context * and prefixes will be relative to this module's * import statements. * == NULL and the default registered prefixes * will be used * target == Xpath expression string to evaluate * root = XPath docroot to use * logerrors == TRUE to use log_error, FALSE to skip it * retpcb == address of return value * * OUTPUTS: * if value node found: * *retpcb == malloced XPath PCB with result * * RETURNS: * status *********************************************************************/ status_t xpath_find_val_unique (val_value_t *startval, ncx_module_t *mod, const xmlChar *target, val_value_t *root, boolean logerrors, xpath_pcb_t **retpcb) { assert( startval && "startval is NULL!" ); assert( target && "target is NULL!" ); assert( root && "root is NULL!" ); assert( retpcb && "retpcb is NULL!" ); *retpcb = NULL; /* normalize the target by finding the object node and * generating an object ID string; the prefixes in * the node-identifier are relative to the module * containing the unique-stmt */ obj_template_t *targobj = NULL; status_t res = find_obj_node_unique(startval->obj, mod, target, logerrors, &targobj); if (res != NO_ERR) { return res; } if (targobj == NULL) { return ERR_NCX_OPERATION_FAILED; } xpath_pcb_t *pcb = xpath_new_pcb(NULL, NULL); if (pcb == NULL) { return ERR_INTERNAL_MEM; } res = obj_gen_object_id_unique(targobj, startval->obj, &pcb->exprstr); if (res != NO_ERR) { xpath_free_pcb(pcb); return res; } pcb->result = xpath1_eval_expr(pcb, startval, root, logerrors, FALSE, &res); if (pcb->result == NULL || res != NO_ERR) { xpath_free_pcb(pcb); if (res == NO_ERR) { res = ERR_NCX_OPERATION_FAILED; } } else { *retpcb = pcb; } return res; } /* xpath_find_val_unique */ /******* X P A T H and K E Y R E F S U P P O R T *******/ /******************************************************************** * FUNCTION xpath_new_pcb * * malloc a new XPath parser control block * xpathstr is allowed to be NULL, otherwise * a strdup will be made and exprstr will be set * * Create and initialize an XPath parser control block * * INPUTS: * xpathstr == XPath expression string to save (a copy will be made) * == NULL if this step should be skipped * getvar_fn == callback function to retirieve an XPath * variable binding * NULL if no variables are used * * RETURNS: * pointer to malloced struct, NULL if malloc error *********************************************************************/ xpath_pcb_t * xpath_new_pcb (const xmlChar *xpathstr, xpath_getvar_fn_t getvar_fn) { return xpath_new_pcb_ex(xpathstr, getvar_fn, NULL); } /* xpath_new_pcb */ /******************************************************************** * FUNCTION xpath_new_pcb_ex * * malloc a new XPath parser control block * xpathstr is allowed to be NULL, otherwise * a strdup will be made and exprstr will be set * * Create and initialize an XPath parser control block * * INPUTS: * xpathstr == XPath expression string to save (a copy will be made) * == NULL if this step should be skipped * getvar_fn == callback function to retirieve an XPath * variable binding * NULL if no variables are used * cookie == runstack context pointer to use, cast as a cookie * * RETURNS: * pointer to malloced struct, NULL if malloc error *********************************************************************/ xpath_pcb_t * xpath_new_pcb_ex (const xmlChar *xpathstr, xpath_getvar_fn_t getvar_fn, void *cookie) { xpath_pcb_t *pcb; pcb = m__getObj(xpath_pcb_t); if (!pcb) { return NULL; } memset(pcb, 0x0, sizeof(xpath_pcb_t)); if (xpathstr) { pcb->exprstr = xml_strdup(xpathstr); if (!pcb->exprstr) { m__free(pcb); return NULL; } } ncx_init_errinfo(&pcb->errinfo); pcb->functions = xpath1_get_functions_ptr(); pcb->getvar_fn = getvar_fn; pcb->cookie = cookie; pcb->missing_errors = TRUE; dlq_createSQue(&pcb->result_cacheQ); dlq_createSQue(&pcb->resnode_cacheQ); dlq_createSQue(&pcb->varbindQ); return pcb; } /* xpath_new_pcb_ex */ /******************************************************************** * FUNCTION xpath_clone_pcb * * Clone an XPath PCB for a must clause copy * copy from typdef to object for leafref * of object to value for NETCONF PDU processing * * INPUTS: * srcpcb == struct with starting contents * * RETURNS: * new xpatyh_pcb_t clone of the srcmust, NULL if malloc error * It will not be processed or parsed. Only the starter * data will be set *********************************************************************/ xpath_pcb_t * xpath_clone_pcb (const xpath_pcb_t *srcpcb) { xpath_pcb_t *newpcb; status_t res; #ifdef DEBUG if (!srcpcb) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif newpcb = xpath_new_pcb(srcpcb->exprstr, srcpcb->getvar_fn); if (!newpcb) { return NULL; } if (srcpcb->tkc) { newpcb->tkc = tk_clone_chain(srcpcb->tkc); if (!newpcb->tkc) { xpath_free_pcb(newpcb); return NULL; } } ncx_set_error(&newpcb->tkerr, srcpcb->tkerr.mod, srcpcb->tkerr.linenum, srcpcb->tkerr.linepos); newpcb->reader = srcpcb->reader; newpcb->source = srcpcb->source; res = ncx_copy_errinfo(&srcpcb->errinfo, &newpcb->errinfo); if (res != NO_ERR) { xpath_free_pcb(newpcb); return NULL; } newpcb->logerrors = srcpcb->logerrors; newpcb->targobj = srcpcb->targobj; newpcb->altobj = srcpcb->altobj; newpcb->varobj = srcpcb->varobj; newpcb->curmode = srcpcb->curmode; newpcb->obj = srcpcb->obj; newpcb->objmod = srcpcb->objmod; newpcb->docroot = srcpcb->docroot; newpcb->doctype = srcpcb->doctype; newpcb->val = srcpcb->val; newpcb->val_docroot = srcpcb->val_docroot; newpcb->flags = srcpcb->flags; /*** skip copying the scratch result ***/ /*** ??? context ??? ***/ newpcb->functions = srcpcb->functions; /* result_cacheQ not copied */ /* resnode_cacheQ not copied */ /* result_count not copied */ /* resnode_count not copied */ newpcb->parseres = srcpcb->parseres; newpcb->validateres = srcpcb->validateres; newpcb->valueres = srcpcb->valueres; newpcb->seen = srcpcb->seen; /*** does the varbindQ need to be cloned? ***/ return newpcb; } /* xpath_clone_pcb */ /******************************************************************** * FUNCTION xpath_find_pcb * * Find an XPath PCB * find by exact match of the expressions string * * INPUTS: * pcbQ == Q of xpath_pcb_t structs to check * exprstr == XPath expression string to find * * RETURNS: * pointer to found xpath_pcb_t or NULL if not found *********************************************************************/ xpath_pcb_t * xpath_find_pcb (dlq_hdr_t *pcbQ, const xmlChar *exprstr) { xpath_pcb_t *pcb; #ifdef DEBUG if (!pcbQ || !exprstr) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (pcb = (xpath_pcb_t *)dlq_firstEntry(pcbQ); pcb != NULL; pcb = (xpath_pcb_t *)dlq_nextEntry(pcb)) { if (pcb->exprstr && !xml_strcmp(exprstr, pcb->exprstr)) { return pcb; } } return NULL; } /* xpath_find_pcb */ /******************************************************************** * Free a malloced XPath parser control block * * \param pcb pointer to parser control block to free *********************************************************************/ void xpath_free_pcb (xpath_pcb_t *pcb) { xpath_result_t *result; xpath_resnode_t *resnode; if (!pcb) { return; } if (pcb->tkc) { tk_free_chain(pcb->tkc); } if (pcb->exprstr) { m__free(pcb->exprstr); } if (pcb->result) { xpath_free_result(pcb->result); } ncx_clean_errinfo(&pcb->errinfo); while (!dlq_empty(&pcb->result_cacheQ)) { result = (xpath_result_t *) dlq_deque(&pcb->result_cacheQ); xpath_free_result(result); } while (!dlq_empty(&pcb->resnode_cacheQ)) { resnode = (xpath_resnode_t *) dlq_deque(&pcb->resnode_cacheQ); xpath_free_resnode(resnode); } var_clean_varQ(&pcb->varbindQ); m__free(pcb); } /* xpath_free_pcb */ /******************************************************************** * FUNCTION xpath_new_result * * malloc an XPath result * Create and initialize an XPath result struct * * INPUTS: * restype == the desired result type * * RETURNS: * pointer to malloced struct, NULL if malloc error *********************************************************************/ xpath_result_t * xpath_new_result (xpath_restype_t restype) { xpath_result_t *result; result = m__getObj(xpath_result_t); if (!result) { return NULL; } xpath_init_result(result, restype); return result; } /* xpath_new_result */ /******************************************************************** * FUNCTION xpath_init_result * * Initialize an XPath result struct * malloc an XPath result node * * INPUTS: * result == pointer to result struct to initialize * restype == the desired result type *********************************************************************/ void xpath_init_result (xpath_result_t *result, xpath_restype_t restype) { #ifdef DEBUG if (!result) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif memset(result, 0x0, sizeof(xpath_result_t)); result->restype = restype; switch (restype) { case XP_RT_NODESET: dlq_createSQue(&result->r.nodeQ); break; case XP_RT_NUMBER: ncx_init_num(&result->r.num); ncx_set_num_zero(&result->r.num, NCX_BT_FLOAT64); result->isval = TRUE; break; case XP_RT_STRING: case XP_RT_BOOLEAN: result->isval = TRUE; break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* xpath_init_result */ /******************************************************************** * FUNCTION xpath_free_result * * Free a malloced XPath result struct * * INPUTS: * result == pointer to result struct to free *********************************************************************/ void xpath_free_result (xpath_result_t *result) { if (!result) { return; } xpath_clean_result(result); m__free(result); } /* xpath_free_result */ /******************************************************************** * FUNCTION xpath_clean_result * * Clean an XPath result struct * * INPUTS: * result == pointer to result struct to clean *********************************************************************/ void xpath_clean_result (xpath_result_t *result) { xpath_resnode_t *resnode; #ifdef DEBUG if (!result) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif switch (result->restype) { case XP_RT_NODESET: while (!dlq_empty(&result->r.nodeQ)) { resnode = (xpath_resnode_t *)dlq_deque(&result->r.nodeQ); xpath_free_resnode(resnode); } break; case XP_RT_NUMBER: ncx_clean_num(NCX_BT_FLOAT64, &result->r.num); break; case XP_RT_STRING: if (result->r.str) { m__free(result->r.str); result->r.str = NULL; } break; case XP_RT_BOOLEAN: result->r.boo = FALSE; break; case XP_RT_NONE: break; default: SET_ERROR(ERR_INTERNAL_VAL); } result->restype = XP_RT_NONE; result->res = NO_ERR; } /* xpath_clean_result */ /******************************************************************** * FUNCTION xpath_new_resnode * * Create and initialize an XPath result node struct * * INPUTS: * restype == the desired result type * * RETURNS: * pointer to malloced struct, NULL if malloc error *********************************************************************/ xpath_resnode_t * xpath_new_resnode (void) { xpath_resnode_t *resnode; resnode = m__getObj(xpath_resnode_t); if (!resnode) { return NULL; } xpath_init_resnode(resnode); return resnode; } /* xpath_new_resnode */ /******************************************************************** * FUNCTION xpath_init_resnode * * Initialize an XPath result node struct * * INPUTS: * resnode == pointer to result node struct to initialize *********************************************************************/ void xpath_init_resnode (xpath_resnode_t *resnode) { #ifdef DEBUG if (!resnode) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif memset(resnode, 0x0, sizeof(xpath_resnode_t)); } /* xpath_init_resnode */ /******************************************************************** * FUNCTION xpath_free_resnode * * Free a malloced XPath result node struct * * INPUTS: * resnode == pointer to result node struct to free *********************************************************************/ void xpath_free_resnode (xpath_resnode_t *resnode) { #ifdef DEBUG if (!resnode) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif xpath_clean_resnode(resnode); m__free(resnode); } /* xpath_free_resnode */ /******************************************************************** * FUNCTION xpath_delete_resnode * * Delete and free a malloced XPath result node struct * * INPUTS: * resnode == pointer to result node struct to free *********************************************************************/ void xpath_delete_resnode (xpath_resnode_t *resnode) { #ifdef DEBUG if (!resnode) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif dlq_remove(resnode); xpath_clean_resnode(resnode); m__free(resnode); } /* xpath_delete_resnode */ /******************************************************************** * FUNCTION xpath_clean_resnode * * Clean an XPath result node struct * * INPUTS: * resnode == pointer to result node struct to clean *********************************************************************/ void xpath_clean_resnode (xpath_resnode_t *resnode) { #ifdef DEBUG if (!resnode) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif memset(resnode, 0x0, sizeof(xpath_resnode_t)); } /* xpath_clean_resnode */ /******************************************************************** * FUNCTION xpath_get_curmod_from_prefix * * Get the correct module to use for a given prefix * * INPUTS: * prefix == string to check * mod == module to use for the default context * and prefixes will be relative to this module's * import statements. * == NULL and the default registered prefixes * will be used * targmod == address of return module * * OUTPUTS: * *targmod == target moduke to use * * RETURNS: * status *********************************************************************/ status_t xpath_get_curmod_from_prefix (const xmlChar *prefix, ncx_module_t *mod, ncx_module_t **targmod) { ncx_import_t *imp; status_t res; #ifdef DEBUG if (!targmod) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; /* get the import if there is a real prefix entered */ if (prefix && *prefix) { if (!mod) { *targmod = (ncx_module_t *)xmlns_get_modptr (xmlns_find_ns_by_prefix(prefix)); if (!*targmod) { res = ERR_NCX_MOD_NOT_FOUND; } } else if (xml_strcmp(prefix, mod->prefix)) { imp = ncx_find_pre_import(mod, prefix); if (!imp) { res = ERR_NCX_INVALID_NAME; } else { if (imp->mod) { *targmod = imp->mod; } else { *targmod = ncx_find_module(imp->module, imp->revision); } if (!*targmod) { res = ERR_NCX_MOD_NOT_FOUND; } else { imp->mod = *targmod; } } } else { *targmod = mod; res = NO_ERR; } } else if (mod) { *targmod = mod; res = NO_ERR; } else { *targmod = NULL; res = ERR_NCX_DATA_MISSING; } return res; } /* xpath_get_curmod_from_prefix */ /******************************************************************** * FUNCTION xpath_get_curmod_from_prefix_str * * Get the correct module to use for a given prefix * Unended string version * * INPUTS: * prefix == string to check * prefixlen == length of prefix * mod == module to use for the default context * and prefixes will be relative to this module's * import statements. * == NULL and the default registered prefixes * will be used * targmod == address of return module * * OUTPUTS: * *targmod == target moduke to use * * RETURNS: * status *********************************************************************/ status_t xpath_get_curmod_from_prefix_str (const xmlChar *prefix, uint32 prefixlen, ncx_module_t *mod, ncx_module_t **targmod) { xmlChar *buff; status_t res; #ifdef DEBUG if (!targmod) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (prefix && prefixlen) { buff = m__getMem(prefixlen+1); if (!buff) { return ERR_INTERNAL_MEM; } xml_strncpy(buff, prefix, prefixlen); res = xpath_get_curmod_from_prefix(buff, mod, targmod); m__free(buff); return res; } else { return xpath_get_curmod_from_prefix(NULL, mod, targmod); } /*NOTREACHED*/ } /* xpath_get_curmod_from_prefix_str */ /******************************************************************** * FUNCTION xpath_parse_token * * Parse the XPath token sequence for a specific token type * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block in progress * tktyp == expected token type * * RETURNS: * status *********************************************************************/ status_t xpath_parse_token (xpath_pcb_t *pcb, tk_type_t tktype) { status_t res; #ifdef DEBUG if (!pcb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* get the next token */ res = TK_ADV(pcb->tkc); if (res != NO_ERR) { if (pcb->logerrors) { ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, res); } return res; } if (TK_CUR_TYP(pcb->tkc) != tktype) { res = ERR_NCX_WRONG_TKTYPE; if (pcb->logerrors) { ncx_mod_exp_err(pcb->tkc, pcb->tkerr.mod, res, tk_get_token_name(tktype)); } return res; } return NO_ERR; } /* xpath_parse_token */ /******************************************************************** * FUNCTION xpath_cvt_boolean * * Convert an XPath result to a boolean answer * * INPUTS: * result == result struct to convert to boolean * * RETURNS: * TRUE or FALSE depending on conversion *********************************************************************/ boolean xpath_cvt_boolean (const xpath_result_t *result) { #ifdef DEBUG if (!result) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (result->restype) { case XP_RT_NONE: return FALSE; case XP_RT_NODESET: return (dlq_empty(&result->r.nodeQ)) ? FALSE : TRUE; case XP_RT_NUMBER: return (ncx_num_zero(&result->r.num, NCX_BT_FLOAT64)) ? FALSE : TRUE; case XP_RT_STRING: return (result->r.str && xml_strlen(result->r.str)) ? TRUE : FALSE; case XP_RT_BOOLEAN: return result->r.boo; default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* xpath_cvt_boolean */ /******************************************************************** * FUNCTION xpath_cvt_number * * Convert an XPath result to a number answer * * INPUTS: * result == result struct to convert to a number * num == pointer to ncx_num_t to hold the conversion result * * OUTPUTS: * *num == numeric result from conversion * *********************************************************************/ void xpath_cvt_number (const xpath_result_t *result, ncx_num_t *num) { const xpath_resnode_t *resnode; val_value_t *val; status_t res; ncx_num_t testnum; ncx_numfmt_t numformat; #ifdef DEBUG if (!result || !num) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif res = NO_ERR; switch (result->restype) { case XP_RT_NONE: ncx_set_num_nan(num, NCX_BT_FLOAT64); break; case XP_RT_NODESET: if (dlq_empty(&result->r.nodeQ)) { ncx_set_num_nan(num, NCX_BT_FLOAT64); } else { if (result->isval) { resnode = (const xpath_resnode_t *) dlq_firstEntry(&result->r.nodeQ); val = val_get_first_leaf(resnode->node.valptr); if (val && typ_is_number(val->btyp)) { res = ncx_cast_num(&val->v.num, val->btyp, num, NCX_BT_FLOAT64); if (res != NO_ERR) { ncx_set_num_nan(num, NCX_BT_FLOAT64); } } else if (val && val->btyp == NCX_BT_STRING) { ncx_init_num(&testnum); res = ncx_convert_num(result->r.str, NCX_NF_NONE, NCX_BT_FLOAT64, &testnum); if (res == NO_ERR) { (void)ncx_copy_num(&testnum, num, NCX_BT_FLOAT64); } else { ncx_set_num_nan(num, NCX_BT_FLOAT64); } ncx_clean_num(NCX_BT_FLOAT64, &testnum); } else { ncx_set_num_nan(num, NCX_BT_FLOAT64); } } else { /* does not matter */ ncx_set_num_zero(num, NCX_BT_FLOAT64); } } break; case XP_RT_NUMBER: ncx_copy_num(&result->r.num, num, NCX_BT_FLOAT64); break; case XP_RT_STRING: if (result->r.str) { numformat = ncx_get_numfmt(result->r.str); switch (numformat) { case NCX_NF_DEC: case NCX_NF_REAL: break; case NCX_NF_NONE: case NCX_NF_OCTAL: case NCX_NF_HEX: res = ERR_NCX_WRONG_NUMTYP; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res == NO_ERR) { ncx_init_num(&testnum); res = ncx_convert_num(result->r.str, numformat, NCX_BT_FLOAT64, &testnum); if (res == NO_ERR) { (void)ncx_copy_num(&testnum, num, NCX_BT_FLOAT64); } else { ncx_set_num_nan(num, NCX_BT_FLOAT64); } ncx_clean_num(NCX_BT_FLOAT64, &testnum); } else { ncx_set_num_nan(num, NCX_BT_FLOAT64); } } else { ncx_set_num_nan(num, NCX_BT_FLOAT64); } break; case XP_RT_BOOLEAN: if (result->r.boo) { ncx_set_num_one(num, NCX_BT_FLOAT64); } else { ncx_set_num_zero(num, NCX_BT_FLOAT64); } break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* xpath_cvt_number */ /******************************************************************** * FUNCTION xpath_cvt_string * * Convert an XPath result to a string answer * * INPUTS: * pcb == parser control block to use * result == result struct to convert to a number * str == pointer to xmlChar * to hold the conversion result * * OUTPUTS: * *str == pointer to malloced string from conversion * * RETURNS: * status; could get an ERR_INTERNAL_MEM error or NO_RER *********************************************************************/ status_t xpath_cvt_string (xpath_pcb_t *pcb, const xpath_result_t *result, xmlChar **str) { status_t res; uint32 len; #ifdef DEBUG if (!result || !str) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; *str = NULL; switch (result->restype) { case XP_RT_NONE: *str = xml_strdup(EMPTY_STRING); break; case XP_RT_NODESET: if (dlq_empty(&result->r.nodeQ)) { *str = xml_strdup(EMPTY_STRING); } else { if (result->isval) { res = xpath1_stringify_nodeset(pcb, result, str); } else { *str = xml_strdup(EMPTY_STRING); } } break; case XP_RT_NUMBER: res = ncx_sprintf_num(NULL, &result->r.num, NCX_BT_FLOAT64, &len); if (res != NO_ERR) { return res; } *str = m__getMem(len+1); if (*str) { res = ncx_sprintf_num(*str, &result->r.num, NCX_BT_FLOAT64, &len); if (res != NO_ERR) { m__free(*str); *str = NULL; return res; } } break; case XP_RT_STRING: if (result->r.str) { *str = xml_strdup(result->r.str); } else { *str = xml_strdup(EMPTY_STRING); } break; case XP_RT_BOOLEAN: if (result->r.boo) { *str = xml_strdup(NCX_EL_TRUE); } else { *str = xml_strdup(NCX_EL_FALSE); } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } if (!*str) { res = ERR_INTERNAL_MEM; } return res; } /* xpath_cvt_string */ /******************************************************************** * FUNCTION xpath_get_resnodeQ * * Get the renodeQ from a result struct * * INPUTS: * result == result struct to check * * RETURNS: * pointer to resnodeQ or NULL if some error *********************************************************************/ dlq_hdr_t * xpath_get_resnodeQ (xpath_result_t *result) { #ifdef DEBUG if (!result) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (result->restype != XP_RT_NODESET) { return NULL; } return &result->r.nodeQ; } /* xpath_get_resnodeQ */ /******************************************************************** * FUNCTION xpath_get_first_resnode * * Get the first result in the renodeQ from a result struct * * INPUTS: * result == result struct to check * * RETURNS: * pointer to resnode or NULL if some error *********************************************************************/ xpath_resnode_t * xpath_get_first_resnode (xpath_result_t *result) { #ifdef DEBUG if (!result) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (result->restype != XP_RT_NODESET) { return NULL; } return (xpath_resnode_t *) dlq_firstEntry(&result->r.nodeQ); } /* xpath_get_first_resnode */ /******************************************************************** * FUNCTION xpath_get_next_resnode * * Get the first result in the renodeQ from a result struct * * INPUTS: * result == result struct to check * * RETURNS: * pointer to resnode or NULL if some error *********************************************************************/ xpath_resnode_t * xpath_get_next_resnode (xpath_resnode_t *resnode) { #ifdef DEBUG if (!resnode) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (xpath_resnode_t *)dlq_nextEntry(resnode); } /* xpath_get_next_resnode */ /******************************************************************** * FUNCTION xpath_get_resnode_valptr * * Get the first result in the renodeQ from a result struct * * INPUTS: * result == result struct to check * * RETURNS: * pointer to resnode or NULL if some error *********************************************************************/ val_value_t * xpath_get_resnode_valptr (xpath_resnode_t *resnode) { #ifdef DEBUG if (!resnode) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return resnode->node.valptr; } /* xpath_get_resnode_valptr */ /******************************************************************** * FUNCTION xpath_get_varbindQ * * Get the varbindQ from a parser control block struct * * INPUTS: * pcb == parser control block to use * * RETURNS: * pointer to varbindQ or NULL if some error *********************************************************************/ dlq_hdr_t * xpath_get_varbindQ (xpath_pcb_t *pcb) { #ifdef DEBUG if (!pcb) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return &pcb->varbindQ; } /* xpath_get_varbindQ */ /******************************************************************** * FUNCTION xpath_move_nodeset * * Move the nodes from a nodeset reult into the * target nodeset result. * This is needed to support partial lock * because multiple select expressions are allowed * for the same partial lock * * INPUTS: * srcresult == XPath result nodeset source * destresult == XPath result nodeset target * * OUTPUTS: * srcresult nodes will be moved to the target result *********************************************************************/ void xpath_move_nodeset (xpath_result_t *srcresult, xpath_result_t *destresult) { #ifdef DEBUG if (srcresult == NULL || destresult == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (srcresult->restype != XP_RT_NODESET) { SET_ERROR(ERR_INTERNAL_VAL); return; } if (destresult->restype != XP_RT_NODESET) { SET_ERROR(ERR_INTERNAL_VAL); return; } dlq_block_enque(&srcresult->r.nodeQ, &destresult->r.nodeQ); } /* xpath_move_nodeset */ /******************************************************************** * FUNCTION xpath_nodeset_empty * * Check if the result is an empty nodeset * * INPUTS: * result == XPath result to check * * RETURNS: * TRUE if this is an empty nodeset * FALSE if not empty or not a nodeset *********************************************************************/ boolean xpath_nodeset_empty (const xpath_result_t *result) { #ifdef DEBUG if (result == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (result->restype != XP_RT_NODESET) { return FALSE; } return dlq_empty(&result->r.nodeQ) ? TRUE : FALSE; } /* xpath_nodeset_empty */ /******************************************************************** * FUNCTION xpath_nodeset_swap_valptr * * Check if the result has the oldval ptr and if so, * replace it with the newval ptr * * INPUTS: * result == result struct to check * oldval == value ptr to find * newval == new value replace it with if oldval is found * *********************************************************************/ void xpath_nodeset_swap_valptr (xpath_result_t *result, val_value_t *oldval, val_value_t *newval) { xpath_resnode_t *resnode; #ifdef DEBUG if (result == NULL || oldval == NULL || newval == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (result->restype != XP_RT_NODESET) { SET_ERROR(ERR_INTERNAL_VAL); } #endif for (resnode = xpath_get_first_resnode(result); resnode != NULL; resnode = xpath_get_next_resnode(resnode)) { if (resnode->node.valptr == oldval) { resnode->node.valptr = newval; } } } /* xpath_nodeset_swap_valptr */ /******************************************************************** * FUNCTION xpath_nodeset_delete_valptr * * Check if the result has the oldval ptr and if so, * delete it * * INPUTS: * result == result struct to check * oldval == value ptr to find * *********************************************************************/ void xpath_nodeset_delete_valptr (xpath_result_t *result, val_value_t *oldval) { xpath_resnode_t *resnode, *nextnode; val_value_t *valptr; boolean done; #ifdef DEBUG if (result == NULL || oldval == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (result->restype != XP_RT_NODESET) { SET_ERROR(ERR_INTERNAL_VAL); } #endif for (resnode = xpath_get_first_resnode(result); resnode != NULL; resnode = nextnode) { nextnode = xpath_get_next_resnode(resnode); /* check if the oldval is an ancestor-or-self * node for this valptr in the result node-set */ valptr = resnode->node.valptr; done = FALSE; while (!done) { if (valptr == oldval) { dlq_remove(resnode); xpath_free_resnode(resnode); done = TRUE; } else if (valptr->parent != NULL && !obj_is_root(valptr->parent->obj)) { valptr = valptr->parent; } else { done = TRUE; } } } } /* xpath_nodeset_delete_valptr */ /******************************************************************** * FUNCTION xpath_convert_url_to_path * * Convert a URL format path to XPath format path * * INPUTS: * urlpath == URL path string to convert to XPath * match_names == enum for selected match names mode * alt_naming == TRUE if alt-name and cli-drop-node-name * containers should be checked * wildcards == TRUE if wildcards allowed instead of key values * FALSE if the '-' wildcard mechanism not allowed * withkeys == TRUE if keys are expected * FALSE if just nodes are expected * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced string containing XPath expression from conversion * NULL if some error *********************************************************************/ xmlChar * xpath_convert_url_to_path (const xmlChar *urlpath, ncx_name_match_t match_names, boolean alt_naming, boolean wildcards, boolean withkeys, status_t *res) { xmlChar *buff; uint32 cnt; #ifdef DEBUG if (urlpath == NULL || res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif buff = NULL; *res = fill_xpath_string(urlpath, NULL, match_names, alt_naming, wildcards, withkeys, &cnt); if (*res == NO_ERR) { buff = m__getMem(cnt+1); if (buff == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } *res = fill_xpath_string(urlpath, buff, match_names, alt_naming, wildcards, withkeys, &cnt); if (*res != NO_ERR) { m__free(buff); buff = NULL; } else if (LOGDEBUG2) { log_debug2("\nConverted urlstring '%s' to XPath '%s'", urlpath, buff); } } return buff; } /* xpath_convert_url_to_path */ /* END xpath.c */ yuma123_2.14/netconf/src/ncx/xml_wr.h0000664000175000017500000003721614770023131017630 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xml_wr #define _H_xml_wr /* FILE: xml_wr.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* XML Write functions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 12-feb-07 abb Begun; split out from xml_wr_util.c */ #include #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifndef _H_xml_msg #include "xml_msg.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* isattrq labels */ #define ATTRQ TRUE #define METAQ FALSE /* empty labels */ #define START FALSE #define EMPTY TRUE /* docmode labels */ #define XMLMODE FALSE #define DOCMODE TRUE /* xmlhdr labels */ #define NOHDR FALSE #define WITHHDR TRUE /* buffer size used in begin_elem_val function * this is sort of a hack -- a hard limit in the code: * this limits the number of different namespace IDs * that can be present in a single XPath expression * and the xmlns attributes will be corrected generated * in element start tags for the XPath expression */ #define XML_WR_MAX_NAMESPACES 512 /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xml_wr_buff * * Write some xmlChars to the specified session * * INPUTS: * scb == session control block to start msg * buff == buffer to write * bufflen == number of bytes to write, not including any * EOS char at the end of the buffer * RETURNS: * none *********************************************************************/ extern void xml_wr_buff (ses_cb_t *scb, const xmlChar *buff, uint32 bufflen); /******************************************************************** * FUNCTION xml_wr_begin_elem_ex * * Write a start or empty XML tag to the specified session * * INPUTS: * scb == session control block * msg == top header from message in progress * parent_nsid == namespace ID of the parent element, if known * nsid == namespace ID of the element to write * elname == unqualified name of element to write * attrQ == Q of xml_attr_t or val_value_t records to write in * the element; NULL == none * isattrq == TRUE if the qQ contains xml_attr_t nodes * FALSE if the Q contains val_value_t nodes (metadata) * indent == number of chars to indent after a newline * == -1 means no newline or indent * == 0 means just newline * empty == TRUE for empty node * == FALSE for start node * * RETURNS: * none *********************************************************************/ extern void xml_wr_begin_elem_ex (ses_cb_t *scb, xml_msg_hdr_t *msg, xmlns_id_t parent_nsid, xmlns_id_t nsid, const xmlChar *elname, const dlq_hdr_t *attrQ, boolean isattrq, int32 indent, boolean empty); /******************************************************************** * FUNCTION xml_wr_begin_elem * * Write a start XML tag to the specified session without attributes * * INPUTS: * scb == session control block * msg == top header from message in progress * parent_nsid == namespace ID of the parent element * nsid == namespace ID of the element to write * elname == unqualified name of element to write * indent == number of chars to indent after a newline * == -1 means no newline or indent * == 0 means just newline * * RETURNS: * none *********************************************************************/ extern void xml_wr_begin_elem (ses_cb_t *scb, xml_msg_hdr_t *msg, xmlns_id_t parent_nsid, xmlns_id_t nsid, const xmlChar *elname, int32 indent); /******************************************************************** * FUNCTION xml_wr_empty_elem * * Write an empty XML tag to the specified session without attributes * * INPUTS: * scb == session control block * msg == top header from message in progress * parent_nsid == namespace ID of the parent element * nsid == namespace ID of the element to write * elname == unqualified name of element to write * indent == number of chars to indent after a newline * == -1 means no newline or indent * == 0 means just newline * * RETURNS: * none *********************************************************************/ extern void xml_wr_empty_elem (ses_cb_t *scb, xml_msg_hdr_t *msg, xmlns_id_t parent_nsid, xmlns_id_t nsid, const xmlChar *elname, int32 indent); /******************************************************************** * FUNCTION xml_wr_end_elem * * Write an end tag to the specified session * * INPUTS: * scb == session control block to start msg * msg == header from message in progress * nsid == namespace ID of the element to write * == zero to force no prefix lookup; use default NS * elname == unqualified name of element to write * indent == number of chars to indent after a newline * will be ignored if indent is turned off * in the agent profile * == -1 means no newline or indent * == 0 means just newline * * RETURNS: * none *********************************************************************/ extern void xml_wr_end_elem (ses_cb_t *scb, xml_msg_hdr_t *msg, xmlns_id_t nsid, const xmlChar *elname, int32 indent); /******************************************************************** * FUNCTION xml_wr_string_elem * * Write a start tag, simple string content, and an end tag * to the specified session. A flag element and * ename will vary from this format. * * Simple content nodes are completed on a single line to * prevent introduction of extra whitespace * * INPUTS: * scb == session control block * msg == header from message in progress * str == simple string to write as element content * parent_nsid == namespace ID of the parent element * nsid == namespace ID of the element to write * elname == unqualified name of element to write * attrQ == Q of xml_attr_t records to write in * the element; NULL == none * isattrq == TRUE for Q of xml_attr_t, FALSE for val_value_t * indent == number of chars to indent after a newline * == -1 means no newline or indent * == 0 means just newline * * RETURNS: * none *********************************************************************/ extern void xml_wr_string_elem (ses_cb_t *scb, xml_msg_hdr_t *msg, const xmlChar *str, xmlns_id_t parent_nsid, xmlns_id_t nsid, const xmlChar *elname, const dlq_hdr_t *attrQ, boolean isattrq, int32 indent); /******************************************************************** * FUNCTION xml_wr_qname_elem * * Write a start tag, QName string content, and an end tag * to the specified session. * * The ses_start_msg must be called before this * function, in order for it to allow any writes * * INPUTS: * scb == session control block * msg == header from message in progres * val_nsid == namespace ID of the QName prefix * str == local-name part of the QName * parent_nsid == namespace ID of the parent element * nsid == namespace ID of the element to write * elname == unqualified name of element to write * attrQ == Q of xml_attr_t records to write in * the element; NULL == none * isattrq == TRUE for Q of xml_attr_t, FALSE for val_value_t * indent == number of chars to indent after a newline * == -1 means no newline or indent * == 0 means just newline * isdefault == TRUE if the XML value node represents a default leaf * == FALSE otherwise * RETURNS: * none *********************************************************************/ extern void xml_wr_qname_elem (ses_cb_t *scb, xml_msg_hdr_t *msg, xmlns_id_t val_nsid, const xmlChar *str, xmlns_id_t parent_nsid, xmlns_id_t nsid, const xmlChar *elname, const dlq_hdr_t *attrQ, boolean isattrq, int32 indent, boolean isdefault); /******************************************************************** * FUNCTION xml_wr_check_val * * Write an NCX value in XML encoding * while checking nodes for suppression of output with * the supplied test fn * * !!! NOTE !!! * * This function generates the contents of the val_value_t * but not the top node itself. This function is called * recursively and this is the intended behavior. * * To generate XML for an entire val_value_t, including * the top-level node, use the xml_wr_full_val fn. * * If the acm_cache and acm_cbfn fields are set in * the msg header then access control will be checked * If FALSE, then nothing will be written to the output session * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * indent == start indent amount if indent enabled * testcb == callback function to use, NULL if not used * * RETURNS: * none *********************************************************************/ extern void xml_wr_check_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 indent, val_nodetest_fn_t testfn); /******************************************************************** * FUNCTION xml_wr_val * * output val_value_t node contents only * Write an NCX value node in XML encoding * See xml_wr_check_write for full details of this fn. * It is the same, except a NULL testfn is supplied. * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * indent == start indent amount if indent enabled * * RETURNS: * none *********************************************************************/ extern void xml_wr_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 indent); /******************************************************************** * FUNCTION xml_wr_full_check_val * * generate entire val_value_t *w/filter) * Write an entire val_value_t out as XML, including the top level * Using an optional testfn to filter output * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * indent == start indent amount if indent enabled * testcb == callback function to use, NULL if not used * * RETURNS: * none *********************************************************************/ extern void xml_wr_full_check_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 indent, val_nodetest_fn_t testfn); /******************************************************************** * FUNCTION xml_wr_full_val * * generate entire val_value_t * Write an entire val_value_t out as XML, including the top level * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * indent == start indent amount if indent enabled * * RETURNS: * none *********************************************************************/ extern void xml_wr_full_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 indent); /******************************************************************** * FUNCTION xml_wr_check_open_file * * Write the specified value to an open FILE in XML format * * INPUTS: * fp == open FILE control block * val == value for output * attrs == top-level attributes to generate * docmode == TRUE if XML_DOC output mode should be used * == FALSE if XML output mode should be used * xmlhdr == TRUE if directive should be output * == FALSE if not * withns == TRUE if xmlns attributes should be used * FALSE to leave them out * startindent == starting indent point * indent == indent amount (0..9 spaces) * testfn == callback test function to use * * RETURNS: * status *********************************************************************/ extern status_t xml_wr_check_open_file (FILE *fp, val_value_t *val, xml_attrs_t *attrs, boolean docmode, boolean xmlhdr, boolean withns, int32 startindent, int32 indent, val_nodetest_fn_t testfn); /******************************************************************** * FUNCTION xml_wr_check_file * * Write the specified value to a FILE in XML format * * INPUTS: * filespec == exact path of filename to open * val == value for output * attrs == top-level attributes to generate * docmode == TRUE if XML_DOC output mode should be used * == FALSE if XML output mode should be used * xmlhdr == TRUE if directive should be output * == FALSE if not * withns == TRUE if xmlns attributes should be used * FALSE to leave them out * startindent == starting indent point * indent == indent amount (0..9 spaces) * testfn == callback test function to use * * RETURNS: * status *********************************************************************/ extern status_t xml_wr_check_file (const xmlChar *filespec, val_value_t *val, xml_attrs_t *attrs, boolean docmode, boolean xmlhdr, boolean withns, int32 startindent, int32 indent, val_nodetest_fn_t testfn); /******************************************************************** * FUNCTION xml_wr_file * * Write the specified value to a FILE in XML format * * INPUTS: * filespec == exact path of filename to open * val == value for output * attrs == top-level attributes to generate * docmode == TRUE if XML_DOC output mode should be used * == FALSE if XML output mode should be used * xmlhdr == TRUE if directive should be output * == FALSE if not * withns == TRUE if xmlns attributes should be used * FALSE to leave them out * startindent == starting indent point * indent == indent amount (0..9 spaces) * * RETURNS: * status *********************************************************************/ extern status_t xml_wr_file (const xmlChar *filespec, val_value_t *val, xml_attrs_t *attrs, boolean docmode, boolean xmlhdr, boolean withns, int32 startindent, int32 indent); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xml_wr */ yuma123_2.14/netconf/src/ncx/typ.h0000664000175000017500000020003114770023131017117 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_typ #define _H_typ /* FILE: typ.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Parameter Type Handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 22-oct-05 abb Begun 13-oct-08 abb Moved pattern from typ_sval_t to ncx_pattern_t to support N patterns per typdef */ #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* decimal64 constants */ #define TYP_DEC64_MIN_DIGITS 1 #define TYP_DEC64_MAX_DIGITS 18 /* typ_rangedef_t flags field */ #define TYP_FL_LBINF bit0 /* lower bound = -INF */ #define TYP_FL_LBINF2 bit1 /* lower bound = INF */ #define TYP_FL_UBINF bit2 /* upper bound = INF */ #define TYP_FL_UBINF2 bit3 /* upper bound = -INF */ #define TYP_FL_LBMIN bit4 /* lower bound is set to 'min' */ #define TYP_FL_LBMAX bit5 /* lower bound is set to 'max' */ #define TYP_FL_UBMAX bit6 /* upper bound is set to 'max' */ #define TYP_FL_UBMIN bit7 /* upper bound is set to 'min' */ #define TYP_RANGE_FLAGS 0xff /* typ_enum_t flags field */ #define TYP_FL_ESET bit0 /* value explicitly set */ #define TYP_FL_ISBITS bit1 /* enum really used in bits */ #define TYP_FL_SEEN bit2 /* used by yangdiff */ /* typ_sval_t flags field */ #define TYP_FL_USTRING bit0 /* value is ustring, not string */ /* typ_named_t flags field */ #define TYP_FL_REPLACE bit0 /* Replace if set; extend if not */ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* type parser used in 3 separate modes */ typedef enum typ_pmode_t_ { TYP_PM_NONE, TYP_PM_NORMAL, /* normal parse mode */ TYP_PM_INDEX, /* index clause parse mode */ TYP_PM_MDATA /* metadata clause parse mode */ } typ_pmode_t; /* one list member * stored in simple.queue of instance-qualified strings */ typedef struct typ_listval_t_ { dlq_hdr_t qhdr; dlq_hdr_t strQ; /* Q of typ_sval_t */ ncx_iqual_t iqual; /* instance qualifier for this listval */ } typ_listval_t; /* one member of a range definition -- stored in simple.rangeQ * * range components in YANG are not allowed to appear out of order * and they are not allowed to overlap. * * A single number component is represented * as a range where lower bound == upper bound * * (1 | 5 | 10) saved as (1..1 | 5..5 | 10..10) * * symbolic range terms are stored in flags, and processed * in the 2nd pass of the compiler: * * lower bound: * min TYP_FL_LBMIN * max TYP_FL_LBMAX * -INF TYP_FL_LBINF * INF TYP_FL_LBINF2 * * upper bound: * min TYP_FL_UBMIN * max TYP_FL_UBMAX * INF TYP_FL_UBINF * -INF TYP_FL_UBINF2 * * If range parsed with btyp == NCX_BT_NONE, then lbstr * and ubstr will be used (for numbers). Otherwise * the number bounds of the range part are stored in lb and ub * * The entire range string is saved in rangestr for ncxdump * and yangcli help text * * The token associated with the range part is saved for * error messages in YANG phase 2 compiling */ typedef struct typ_rangedef_t_ { dlq_hdr_t qhdr; xmlChar *rangestr; /* saved in YANG only */ ncx_num_t lb; /* lower bound */ ncx_num_t ub; ncx_btype_t btyp; uint32 flags; xmlChar *lbstr; /* saved if range deferred */ xmlChar *ubstr; /* saved if range deferred */ ncx_error_t tkerr; } typ_rangedef_t; /* one ENUM typdef value -- stored in simple.valQ * Used for NCX_BT_ENUM and NCX_BT_BITS data type */ typedef struct typ_enum_t_ { dlq_hdr_t qhdr; xmlChar *name; xmlChar *descr; xmlChar *ref; ncx_status_t status; int32 val; uint32 pos; uint32 flags; dlq_hdr_t appinfoQ; dlq_hdr_t iffeatureQ; } typ_enum_t; /* one STRING typdef value, pattern value * -- stored in simple.valQ */ typedef struct typ_sval_t_ { dlq_hdr_t qhdr; ncx_str_t val; uint32 flags; } typ_sval_t; /* one range description */ typedef struct typ_range_t_ { xmlChar *rangestr; dlq_hdr_t rangeQ; /* Q of typ_rangedef_t */ ncx_errinfo_t range_errinfo; ncx_status_t res; ncx_error_t tkerr; } typ_range_t; /* YANG pattern struct : N per typedef and also * across N typdefs in a chain: all are ANDed together like RelaxNG * instead of ORed together within the same type step like XSD */ typedef struct typ_pattern_t_ { dlq_hdr_t qhdr; xmlRegexpPtr pattern; xmlChar *pat_str; ncx_errinfo_t pat_errinfo; } typ_pattern_t; /* YANG identityref struct * the value is an identity-stmt QName * that has a base-stmt that resolves to the same value */ typedef struct typ_idref_t { xmlChar *baseprefix; xmlChar *basename; ncx_identity_t *base; /* back-ptr to base (if found ) */ const xmlChar *modname; /* back-ptr to the main mod name */ } typ_idref_t; /* NCX_CL_SIMPLE * * The following enums defined in ncxconst.h are supported in this struct * NCX_BT_BITS -- set of bit definitions (like list of enum) * NCX_BT_BOOLEAN -- true, 1, false, 0 * NCX_BT_ENUM -- XSD like enumeration * NCX_BT_EMPTY -- empty element type like * NCX_BT_INT8 -- 8 bit signed integer value * NCX_BT_INT16 -- 16 bit signed integer value * NCX_BT_INT32 -- 32 bit signed integer value * NCX_BT_INT64 -- 64 bit signed integer value * NCX_BT_UINT8 -- 8 bit unsigned integer value * NCX_BT_UINT16 -- 16 bit unsigned integer value * NCX_BT_UINT32 -- 32 bit unsigned integer value * NCX_BT_UINT64 -- 64 bit unsigned integer value * NCX_BT_DECIMAL64 -- 64 bit fixed-point number value * NCX_BT_FLOAT64 -- 64 bit floating-point number value * NCX_BT_STRING -- char string * NCX_BT_BINARY -- binary string (base64 from RFC 4648) * NCX_BT_LEAFREF -- YANG leafref (XPath path expression) * NCX_BT_IDREF -- YANG identityref (QName) * NCX_BT_INSTANCE_ID -- YANG instance-identifier (XPath expression) * NCX_BT_SLIST -- simple list of string or number type (xsd:list) * NCX_BT_UNION -- C-type union of any simtype except leafref and empty */ typedef struct typ_simple_t_ { ncx_btype_t btyp; /* NCX base type */ struct typ_template_t_ *listtyp; /* template for NCX_BT_SLIST */ struct xpath_pcb_t_ *xleafref; /* saved for NCX_BT_LEAFREF only */ /* pointer to resolved typedef for NCX_BT_LEAFREF/NCX_BT_INSTANCE_ID */ struct typ_def_t_ *xrefdef; boolean constrained; /* set when require-instance=TRUE */ typ_range_t range; /* for all num types and string length */ typ_idref_t idref; /* for NCX_BT_IDREF only */ dlq_hdr_t valQ; /* bit, enum, string, list vals/patterns */ dlq_hdr_t metaQ; /* Q of obj_template_t structs */ dlq_hdr_t unionQ; /* Q of typ_unionnode_t for NCX_BT_UNION */ dlq_hdr_t patternQ; /* Q of ncx_pattern_t for NCX_BT_STRING */ ncx_strrest_t strrest; /* string/type restriction type in valQ */ uint32 flags; uint32 maxbit; /* max bit position in valQ */ uint32 maxenum; /* max enum value in valQ */ uint8 digits; /* fraction-digits for decimal64 */ } typ_simple_t; /* NCX_CL_NAMED * * typ_named_t * - user defined type using another named type (not a base type) * and therefore pointing to another typ_template (typ) * - also used for derived type from another named type or * extending the parent type in some way (newtyp) */ typedef struct typ_named_t_ { struct typ_template_t_ *typ; /* derived-from type */ struct typ_def_t_ *newtyp; /* opt. additional semantics */ uint32 flags; } typ_named_t; /* NCX_CL_REF * * typ_ref_t * struct pointing to another typ_def_t * used internally within inline data types * (child nodes or scoped index nodes of complex types) */ typedef struct typ_ref_t_ { struct typ_def_t_ *typdef; /* fwd pointer */ } typ_ref_t; /* Union of all the typdef variants */ typedef union typ_def_u_t_ { ncx_btype_t base; typ_simple_t simple; typ_named_t named; typ_ref_t ref; } typ_def_u_t; /* Discriminated union for all data typedefs. * This data structure is repeated in every * node of every data structure, starting with * the typ_def_t struct in the type template */ typedef struct typ_def_t_ { ncx_tclass_t tclass; ncx_iqual_t iqual; ncx_access_t maxaccess; ncx_data_class_t dataclass; ncx_merge_t mergetype; xmlChar *prefix; /* pfix used in type field */ xmlChar *typenamestr; /* typename used in type field */ dlq_hdr_t appinfoQ; /* Q of ncx_appinfo_t */ ncx_error_t tkerr; typ_def_u_t def; uint32 linenum; } typ_def_t; /* One YANG 'type' definition -- top-level type template */ typedef struct typ_template_t_ { dlq_hdr_t qhdr; xmlChar *name; xmlChar *descr; xmlChar *ref; xmlChar *defval; xmlChar *units; xmlns_id_t nsid; boolean used; ncx_status_t status; typ_def_t typdef; dlq_hdr_t appinfoQ; void *grp; /* const back-ptr to direct grp parent */ status_t res; ncx_error_t tkerr; } typ_template_t; /* One YANG union node * One of the 2 pointers (typ or typdef will be NULL * If a named type is used, then 'typ' is active * If an inline type is used, then typdef is active */ typedef struct typ_unionnode_t_ { dlq_hdr_t qhdr; typ_template_t *typ; /* not malloced, just back-ptr */ typ_def_t *typdef; /* malloced for unnamed inline type */ boolean seen; /* needed for yangdiff */ } typ_unionnode_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION typ_load_basetypes * * Create typ_template_t structs for the base types * Must be called before any modules are loaded * load the typ_template_t structs for the ncx_btype_t types * MUST be called during ncx_init startup * * RETURNS: * status *********************************************************************/ extern status_t typ_load_basetypes (void); /******************************************************************** * FUNCTION typ_unload_basetypes * * Unload and destroy the typ_template_t structs for the base types * unload the typ_template_t structs for the ncx_btype_t types * SHOULD be called during ncx_cleanup * *********************************************************************/ extern void typ_unload_basetypes (void); /******************************************************************** * FUNCTION typ_new_template * * Malloc and initialize the fields in a typ_template_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern typ_template_t * typ_new_template (void); /******************************************************************** * FUNCTION typ_free_template * * Scrub the memory in a typ_template_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * typ == typ_template_t to delete *********************************************************************/ extern void typ_free_template (typ_template_t *typ); /******************************************************************** * FUNCTION typ_new_typdef * * Malloc and initialize the fields in a typ_def_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern typ_def_t * typ_new_typdef (void); /******************************************************************** * FUNCTION typ_init_typdef * * init a pre-allocated typdef (done first) * Initialize the fields in a typ_def_t * !! Still need to call typ_init_simple * !! when the actual builting type is determined * * INPUTS: * typdef == pointer to the struct to initialize *********************************************************************/ extern void typ_init_typdef (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_init_simple * * Init a typ_simple_t struct inside a typ_def_t * init a simple data type after typ_init_typdef * * INPUTS: * typdef == pointer to the typ_def_t struct to init * as a NCX_CL_SIMPLE variant *********************************************************************/ extern void typ_init_simple (typ_def_t *tdef, ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_init_named * * Init a typ_named_t struct inside a typ_def_t * init a named data type after typ_init_typdef * * INPUTS: * typdef == pointer to the typ_def_t struct to init * as a NCX_CL_SIMPLE variant *********************************************************************/ extern void typ_init_named (typ_def_t *tdef); /******************************************************************** * FUNCTION typ_free_typdef * * Scrub the memory in a typ_def_t by freeing all * Then free the typdef itself * * INPUTS: * typdef == typ_def_t to delete *********************************************************************/ extern void typ_free_typdef (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_clean_typdef * * Clean a typ_def_t struct, but do not delete it * * INPUTS: * typdef == pointer to the typ_def_t struct to clean *********************************************************************/ extern void typ_clean_typdef (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_set_named_typdef * * Set the fields in a named typedef (used by YANG parser) * * INPUTS: * typdef == type def struct to set * imptyp == named type to set within 'typ.typdef' * *********************************************************************/ extern void typ_set_named_typdef (typ_def_t *typdef, typ_template_t *imptyp); /******************************************************************** * FUNCTION typ_get_named_typename * * Get the type name of the named typ * * INPUTS: * typdef == pointer to the typ_def_t struct to check * * RETURNS: * pointer to type name, NULL if some error *********************************************************************/ extern const xmlChar * typ_get_named_typename (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_named_type_linenum * * Get the line number of the type template of the named type * * INPUTS: * typdef == pointer to the typ_def_t struct to check * * RETURNS: * pointer to type name, NULL if some error *********************************************************************/ extern uint32 typ_get_named_type_linenum (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_set_new_named * * Create a new typdef inside a typ_named_t struct inside a typ_def_t * * INPUTS: * typdef == pointer to the typ_def_t struct to setup * btyp == builtin type (NCX_BT_NONE if local type extension) * * RETURNS: * status *********************************************************************/ extern status_t typ_set_new_named (typ_def_t *typdef, ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_get_new_named * * Access the new typdef inside a typ_named_t struct inside a typ_def_t * * INPUTS: * typdef == pointer to the typ_def_t struct to check * * RETURNS: * pointer to new typ_def_t or NULL if none *********************************************************************/ extern typ_def_t * typ_get_new_named (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_cget_new_named * * Access the new typdef inside a typ_named_t struct inside a typ_def_t * * INPUTS: * typdef == pointer to the typ_def_t struct to check * * RETURNS: * pointer to new typ_def_t or NULL if none *********************************************************************/ extern const typ_def_t * typ_cget_new_named (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_set_simple_typdef * * Set the fields in a simple typedef (used by YANG parser) * * INPUTS: * typ == type template to set * btyp == builtin type to set within 'typ.typdef' * *********************************************************************/ extern void typ_set_simple_typdef (typ_template_t *typ, ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_new_enum * * Alloc and Init a typ_enum_t struct * malloc and init an enumeration descriptor, strdup name ptr * * INPUTS: * name == name string for the enumeration * RETURNS: * pointer to malloced struct or NULL if memory error * Note that the enum integer value is initialized to zero *********************************************************************/ extern typ_enum_t * typ_new_enum (const xmlChar *name); /******************************************************************** * FUNCTION typ_new_enum2 * * Alloc and Init a typ_enum_t struct * Use the string value as-=is, instead of mallocing a new one * malloc and init an enumeration descriptor, pass off name ptr * * INPUTS: * name == name string for the enumeration (will get free-ed later!!) * * RETURNS: * pointer to malloced struct or NULL if memory error * Note that the enum integer value is initialized to zero *********************************************************************/ extern typ_enum_t * typ_new_enum2 (xmlChar *name); /******************************************************************** * FUNCTION typ_free_enum * * Free a typ_enum_t struct * free an enumeration descriptor * * INPUTS: * en == enum struct to free *********************************************************************/ extern void typ_free_enum (typ_enum_t *en); /******************************************************************** * FUNCTION typ_new_rangedef * * Alloc and Init a typ_rangedef_t struct (range-stmt) * * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ extern typ_rangedef_t * typ_new_rangedef (void); /******************************************************************** * FUNCTION typ_free_rangedef * * Free a typ_rangedef_t struct (range-stmt) * * INPUTS: * rv == rangeval struct to delete * btyp == base type of range (float and double have malloced strings) *********************************************************************/ extern void typ_free_rangedef (typ_rangedef_t *rv, ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_normalize_rangeQ * * Start with a valid rangedef chain * Combine any consecutive range definitions like * 1..4|5|6|7..9 would break replaced with 1..9 * concat consecutive rangedef sections for integral numbers * * Not done for NCX_BT_FLOAT64 data types * * INPUTS: * rangeQ == Q of typ_rangeval_t structs to normalize * btyp == base type of range *********************************************************************/ extern void typ_normalize_rangeQ (dlq_hdr_t *rangeQ, ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_get_rangeQ * * Return the rangeQ for the given typdef * Follow typdef chains if needed until first range found * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first rangedef struct or NULL if none *********************************************************************/ extern dlq_hdr_t * typ_get_rangeQ (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_rangeQ_con * * Return the rangeQ for the given typdef * Do not follow typdef chains * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the rangeQ from this typdef, or NULL if none *********************************************************************/ extern dlq_hdr_t * typ_get_rangeQ_con (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_crangeQ * * Return the rangeQ for the given typdef * Follow typdef chains if needed until first range found * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first rangedef struct or NULL if none *********************************************************************/ extern const dlq_hdr_t * typ_get_crangeQ (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_rangeQ_con * * Return the rangeQ for the given typdef * Do not follow typdef chains * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the rangeQ from this typdef, or NULL if none *********************************************************************/ extern const dlq_hdr_t * typ_get_crangeQ_con (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_range_con * * Return the range struct for the given typdef * Do not follow typdef chains * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the range struct for this typdef *********************************************************************/ extern typ_range_t * typ_get_range_con (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_crange_con * * Return the range struct for the given typdef * Do not follow typdef chains * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the range struct for this typdef *********************************************************************/ extern const typ_range_t * typ_get_crange_con (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_rangestr * * Return the range string for the given typdef chain * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the range string for this typdef, NULL if none *********************************************************************/ extern const xmlChar * typ_get_rangestr (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_first_rangedef * * Return the lower bound range definition struct * Follow typdef chains if needed until first range found * * INPUTS: * typ_def == typ def struct to check * * RETURNS: * pointer to the first rangedef struct or NULL if none *********************************************************************/ extern const typ_rangedef_t * typ_first_rangedef (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_first_rangedef_con * * Return the lower bound range definition struct * Constain search to this typdef * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first rangedef struct or NULL if none *********************************************************************/ extern const typ_rangedef_t * typ_first_rangedef_con (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_rangebounds_con * * Return the lower and upper bound range number * Constain search to this typdef * deprecated -- does not support multi-part ranges * * INPUTS: * typ_def == typ def struct to check * btyp == pointer to output range number type * lb == pointer to output lower bound number * ub == pointer to output upper bound number * * OUTPUTS: * *btyp == the type of number in the return type, if non-NULL * *lb == lower bound number * *ub == upper bound number * * RETURNS: * status, NO_ERR == something found *********************************************************************/ extern status_t typ_get_rangebounds_con (const typ_def_t *typdef, ncx_btype_t *btyp, const ncx_num_t **lb, const ncx_num_t **ub); /******************************************************************** * FUNCTION typ_get_strrest * * Get the string restrinvtion type set for this typdef * * INPUTS: * typdef == typdef to check * * RETURNS: * string restrinction enumeration value *********************************************************************/ extern ncx_strrest_t typ_get_strrest (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_set_strrest * * Set the string restrinvtion type set for this typdef * * INPUTS: * typdef == typdef to check * strrest == string restriction enum value to set * *********************************************************************/ extern void typ_set_strrest (typ_def_t *typdef, ncx_strrest_t strrest); /******************************************************************** * FUNCTION typ_new_sval * * Alloc and Init a typ_sval_t struct * malloc and init a string descriptor * * INPUTS: * str == string value inside token to copy * btyp == type of string (NCX_BT_STRING/LIST, OSTRING/OLIST) * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ extern typ_sval_t * typ_new_sval (const xmlChar *str, ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_free_sval * * Free a typ_sval_t struct * free a string descriptor * * INPUTS: * sv == typ_sval_t struct to free *********************************************************************/ extern void typ_free_sval (typ_sval_t *sv); /******************************************************************** * FUNCTION typ_new_listval * * Alloc and Init a typ_listval_t struct * malloc and init a list descriptor * * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ extern typ_listval_t * typ_new_listval (void); /******************************************************************** * FUNCTION typ_free_listval * * Free a typ_listval_t struct * free a list descriptor * * INPUTS: * lv == typ_listval_t struct to free *********************************************************************/ extern void typ_free_listval (typ_listval_t *lv); /******************************************************************** * FUNCTION typ_get_range_type * * Get the correct typ_rangedef_t data type for the * indicated base type * get the proper range base type to use for a given base type * * INPUTS: * btyp == base type enum * RETURNS: * base type enum of the range data type *********************************************************************/ extern ncx_btype_t typ_get_range_type (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_get_basetype * * Get the final base type of the specified typ_def_t * Follow any typdef links and get the actual base type of * the specified typedef * * INPUTS: * typdef == typdef to check * RETURNS: * base type of final typ_def_t *********************************************************************/ extern ncx_btype_t typ_get_basetype (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_name * * Get the name for the specified typdef * * INPUTS: * typdef == type definition to check * * RETURNS: * type name or empty string if some error *********************************************************************/ extern const xmlChar * typ_get_name (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_basetype_name * * Get the name of the final base type of the specified typ_template_t * * INPUTS: * typ == template containing the typdef to check * * RETURNS: * base type name of final embedded typ_def_t *********************************************************************/ extern const xmlChar * typ_get_basetype_name (const typ_template_t *typ); /******************************************************************** * FUNCTION typ_get_parenttype_name * * Get the final base type of the specified typ_def_t * * INPUTS: * typdef == typdef to check * RETURNS: * base type of final typ_def_t *********************************************************************/ extern const xmlChar * typ_get_parenttype_name (const typ_template_t *typ); /******************************************************************** * FUNCTION typ_get_base_class * * Follow any typdef links and get the class of the base typdef * for the specified typedef * * INPUTS: * typdef == typdef to check * RETURNS: * base class of final typ_def_t *********************************************************************/ extern ncx_tclass_t typ_get_base_class (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_basetype_typ * * Get the default typ_template_t for the specified base type * Get the default type template for the specified base type * * INPUTS: * btyp == base type to get * RETURNS: * pointer to the type template for the specified basetype *********************************************************************/ extern typ_template_t * typ_get_basetype_typ (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_get_basetype_typdef * * Get the default typdef for the specified base type * * INPUTS: * btyp == base type to get * RETURNS: * pointer to the typdef for the specified basetype *********************************************************************/ extern typ_def_t * typ_get_basetype_typdef (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_get_parent_typdef * * Get the next typ_def_t in a chain -- for NCX_CL_NAMED chained typed * Also NCX_CL_REF pointer typdefs * Ignores current named type even if if has new restrictions * Get the parent typdef for NCX_CL_NAMED and NCX_CL_REF * Returns NULL for all other classes * * INPUTS: * typdef == typdef to check * RETURNS: * pointer to next non-empty typ_def_t *********************************************************************/ extern typ_def_t * typ_get_parent_typdef (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_parent_type * * Get the next typ_template_t in a chain -- for NCX_CL_NAMED only * * INPUTS: * typ == type template to check * RETURNS: * pointer to next non-empty typ_template_t for a named type *********************************************************************/ extern const typ_template_t * typ_get_parent_type (const typ_template_t *typ); /******************************************************************** * FUNCTION typ_get_cparent_typdef * * Get the next typ_def_t in a chain -- for NCX_CL_NAMED chained typed * Also NCX_CL_REF pointer typdefs * Ignores current named type even if if has new restrictions * INPUTS: * typdef == typdef to check * RETURNS: * pointer to next non-empty typ_def_t *********************************************************************/ extern const typ_def_t * typ_get_cparent_typdef (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_next_typdef * * Get the next typ_def_t in a chain -- for NCX_CL_NAMED chained typed * Also NCX_CL_REF pointer typdefs * Get the next typdef in the chain for NCX_CL_NAMED or NCX_CL_REF * Returns the input typdef for all other typdef classes * * INPUTS: * typdef == typdef to check * RETURNS: * pointer to next non-empty typ_def_t *********************************************************************/ extern typ_def_t * typ_get_next_typdef (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_base_typdef * * Get the base typ_def_t in a chain -- for NCX_CL_NAMED chained typed * Also NCX_CL_REF pointer typdefs * get the real typdef that describes the type, if the * input is one of the 'pointer' typdef classes. Otherwise, * just return the input typdef * * INPUTS: * typdef == typdef to check * RETURNS: * pointer to base typ_def_t *********************************************************************/ extern typ_def_t * typ_get_base_typdef (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_cbase_typdef * * Get the base typ_def_t in a chain -- for NCX_CL_NAMED chained typed * Also NCX_CL_REF pointer typdefs * * INPUTS: * typdef == typdef to check * RETURNS: * pointer to base typ_def_t *********************************************************************/ extern const typ_def_t * typ_get_cbase_typdef (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_qual_typdef * * Get the final typ_def_t of the specified typ_def_t * based on the qualifier * Get the next typdef in the chain for NCX_CL_NAMED or NCX_CL_REF * Skip any named types without the specific restriction defined * * Returns the input typdef for simple typdef classes * * INPUTS: * typdef == typdef to check * squal == type of search qualifier desired * NCX_SQUAL_NONE == get first non-empty typdef * NCX_SQUAL_RANGE == find the first w/ range definition * NCX_SQUAL_VAL == find the first w/ stringval/pattern def * NCX_SQUAL_META == find the first typdef w/ meta-data def * * RETURNS: * pointer to found typ_def_t or NULL if none found *********************************************************************/ extern typ_def_t * typ_get_qual_typdef (typ_def_t *typdef, ncx_squal_t squal); /******************************************************************** * FUNCTION typ_get_cqual_typdef * * Get the final typ_def_t of the specified typ_def_t * based on the qualifier * INPUTS: * typdef == typdef to check * squal == type of search qualifier desired * NCX_SQUAL_NONE == get first non-empty typdef * NCX_SQUAL_RANGE == find the first w/ range definition * NCX_SQUAL_VAL == find the first w/ stringval/pattern def * NCX_SQUAL_META == find the first typdef w/ meta-data def * * RETURNS: * pointer to found typ_def_t or NULL if none found *********************************************************************/ extern const typ_def_t * typ_get_cqual_typdef (const typ_def_t *typdef, ncx_squal_t squal); /******************************************************************** * FUNCTION typ_find_appinfo * * Find the specified appinfo variable by its prefix and name * * INPUTS: * typdef == typedef to check * prefix == module prefix (may be NULL) * name == name of the appinfo var to find * * RETURNS: * pointer to found appinfo struct or NULL if not found *********************************************************************/ extern const ncx_appinfo_t * typ_find_appinfo (const typ_def_t *typdef, const xmlChar *prefix, const xmlChar *name); /******************************************************************** * FUNCTION typ_find_appinfo_con * * Find the specified appinfo name, constrained to the current typdef * * INPUTS: * typdef == typedef to check * prefix == appinfo module prefix (may be NULL) * name == name of the appinfo var to find * * RETURNS: * pointer to found appinfo struct or NULL if not found *********************************************************************/ extern const ncx_appinfo_t * typ_find_appinfo_con (const typ_def_t *typdef, const xmlChar *prefix, const xmlChar *name); /******************************************************************** * FUNCTION typ_is_xpath_string * * Find the ncx:xpath extension within the specified typdef chain * * INPUTS: * typdef == start of typ_def_t chain to check * * RETURNS: * TRUE if ncx:xpath extension found * FALSE otherwise *********************************************************************/ extern boolean typ_is_xpath_string (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_is_qname_string * * Find the ncx:qname extension within the specified typdef chain * * INPUTS: * typdef == start of typ_def_t chain to check * * RETURNS: * TRUE if ncx:qname extension found * FALSE otherwise *********************************************************************/ extern boolean typ_is_qname_string (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_is_schema_instance_string * * Find the ncx:schema-instance extension within * the specified typdef chain * * INPUTS: * typdef == start of typ_def_t chain to check * * RETURNS: * TRUE if ncx:schema-instance extension found * FALSE otherwise *********************************************************************/ extern boolean typ_is_schema_instance_string (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_defval * * Find the default value string for the specified type template * get default from template * * INPUTS: * typ == typ_template_t struct to check * RETURNS: * pointer to found defval string or NULL if none *********************************************************************/ extern const xmlChar * typ_get_defval (const typ_template_t *typ); /******************************************************************** * FUNCTION typ_get_default * * Check if this typdef has a default value defined * get default from typdef * * INPUTS: * typdef == typ_def_t struct to check * RETURNS: * pointer to default or NULL if there is none *********************************************************************/ extern const xmlChar * typ_get_default (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_iqualval * * Find the instance qualifier value enum for the specified type template * * INPUTS: * typ == typ_template_t struct to check * RETURNS: * iqual value enum *********************************************************************/ extern ncx_iqual_t typ_get_iqualval (const typ_template_t *typ); /******************************************************************** * FUNCTION typ_get_iqualval_def * * Find the instance qualifier value enum for the specified type template * * INPUTS: * typdef == typ_def_t struct to check * RETURNS: * iqual value enum *********************************************************************/ extern ncx_iqual_t typ_get_iqualval_def (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_units * * Find the units string for the specified type template * * INPUTS: * typ == typ_template_t struct to check * RETURNS: * pointer to found units string or NULL if none *********************************************************************/ extern const xmlChar * typ_get_units (const typ_template_t *typ); /******************************************************************** * FUNCTION typ_get_units_from_typdef * * Find the units string for the specified typdef template * Follow any NCX_CL_NAMED typdefs and check for a units * clause in the the nearest ancestor typdef * get units from named type if any * * INPUTS: * typdef == typ_def_t struct to check * * RETURNS: * pointer to found units string or NULL if none *********************************************************************/ extern const xmlChar * typ_get_units_from_typdef (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_has_children * * Check if this is a data type that uses the val.v.childQ * * INPUTS: * btyp == base type enum * * RETURNS: * TRUE if the childQ is used, FALSE otherwise *********************************************************************/ extern boolean typ_has_children (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_has_index * * Check if this is a data type that has an index * * INPUTS: * btyp == base type enum * * RETURNS: * TRUE if the indexQ is used, FALSE otherwise *********************************************************************/ extern boolean typ_has_index (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_is_simple * * Check if this is a simple data type * * INPUTS: * btyp == base type enum * * RETURNS: * TRUE if this is a simple data type, FALSE otherwise *********************************************************************/ extern boolean typ_is_simple (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_is_xsd_simple * * Check if this is a simple data type in XSD encoding * * INPUTS: * btyp == base type enum * * RETURNS: * TRUE if this is a simple data type, FALSE otherwise *********************************************************************/ extern boolean typ_is_xsd_simple (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_first_enumdef * * Get the first enum def struct * looks past named typedefs to base typedef * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first enum def of NULL if none *********************************************************************/ extern typ_enum_t * typ_first_enumdef (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_next_enumdef * * Get the next enum def struct * * INPUTS: * enumdef == typ enum struct to check * * RETURNS: * pointer to the first enum def of NULL if none *********************************************************************/ extern typ_enum_t * typ_next_enumdef (typ_enum_t *enumdef); /******************************************************************** * FUNCTION typ_first_enumdef2 * * Get the first enum def struct * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first enum def of NULL if none *********************************************************************/ extern typ_enum_t * typ_first_enumdef2 (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_first_con_enumdef * * Get the first enum def struct * constrained to this typdef * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first enum def of NULL if none *********************************************************************/ extern const typ_enum_t * typ_first_con_enumdef (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_find_enumdef * * Get the specified enum def struct * * INPUTS: * ebQ == enum/bits Q to check * name == name of the enum to find * RETURNS: * pointer to the specified enum def of NULL if none *********************************************************************/ extern typ_enum_t * typ_find_enumdef (dlq_hdr_t *ebQ, const xmlChar *name); /******************************************************************** * FUNCTION typ_enumdef_count * * Get the number of typ_enum_t Q entries * * INPUTS: * typdef == typ def struct to check * * RETURNS: * number of entries *********************************************************************/ extern uint32 typ_enumdef_count (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_first_strdef * * Get the first string def struct * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first string def of NULL if none *********************************************************************/ extern const typ_sval_t * typ_first_strdef (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_maxrows * * Get the maxrows value if it exists or zero if not * * INPUTS: * typdef == typdef to check * RETURNS: * max number of rows or zero if not applicable *********************************************************************/ extern uint32 typ_get_maxrows (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_maxaccess * * Find the max-access value for the specified typdef * Follow named types to see if any parent typdef has a * maxaccess clause, if none found in the parameter * * INPUTS: * typdef == typ_def_t struct to check * RETURNS: * maxaccess enumeration *********************************************************************/ extern ncx_access_t typ_get_maxaccess (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_dataclass * * Find the data-class value for the specified typdef * Follow named types to see if any parent typdef has a * data-class clause, if none found in the parameter * * INPUTS: * typdef == typ_def_t struct to check * RETURNS: * data class enumeration *********************************************************************/ extern ncx_data_class_t typ_get_dataclass (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_mergetype * * Get the merge type for a specified type def * * INPUTS: * typdef == typ_def_t struct to check * RETURNS: * merge type enumeration *********************************************************************/ extern ncx_merge_t typ_get_mergetype (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_nsid * * Return the namespace ID * * INPUTS: * typ == typ_template_t struct to check * RETURNS: * namespace ID of the type *********************************************************************/ extern xmlns_id_t typ_get_nsid (const typ_template_t *typ); /******************************************************************** * FUNCTION typ_get_listtyp * * Return the typ_template for the list type, if the supplied * typ_template contains a list typ_def, or named type chain * leads to a NCX_BT_SLIST or NCX_BT_BITS typdef * * INPUTS: * typ == typ_template_t struct to check * RETURNS: * namespace ID of the type *********************************************************************/ extern typ_template_t * typ_get_listtyp (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_clisttyp * * Return the typ_template for the list type, if the supplied * typ_template contains a list typ_def, or named type chain * leads to a NCX_BT_SLIST typdef * * INPUTS: * typ == typ_template_t struct to check * RETURNS: * namespace ID of the type *********************************************************************/ extern const typ_template_t * typ_get_clisttyp (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_new_unionnode * * Alloc and Init a typ_unionnode_t struct * * INPUTS: * typ == pointer to type template for this union node * * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ extern typ_unionnode_t * typ_new_unionnode (typ_template_t *typ); /******************************************************************** * FUNCTION typ_free_unionnode * * Free a typ_unionnode_t struct * * INPUTS: * un == union node to free *********************************************************************/ extern void typ_free_unionnode (typ_unionnode_t *un); /******************************************************************** * FUNCTION typ_get_unionnode_ptr * * Get the proper typdef pointer from a unionnode * * INPUTS: * un == union node to check * * RETURNS: * pointer to the typ_def_t inside *********************************************************************/ extern typ_def_t * typ_get_unionnode_ptr (typ_unionnode_t *un); /******************************************************************** * FUNCTION typ_first_unionnode * * Get the first union node in the queue for a given typdef * * INPUTS: * typdef == pointer to type definition for the union node * * RETURNS: * pointer to first typ_unionnode struct or NULL if none *********************************************************************/ extern typ_unionnode_t * typ_first_unionnode (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_first_con_unionnode * * Get the first union node in the queue for a given typdef * constrained * * INPUTS: * typdef == pointer to type definition for the union node * * RETURNS: * pointer to first typ_unionnode struct or NULL if none *********************************************************************/ extern const typ_unionnode_t * typ_first_con_unionnode (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_is_number * * Check if the base type is numeric * * INPUTS: * btype == basetype enum to check * * RETURNS: * TRUE if base type is numeric * FALSE if some other type *********************************************************************/ extern boolean typ_is_number (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_is_string * * Check if the base type is a simple string (not list) * * INPUTS: * btyp == base type enum to check * * RETURNS: * TRUE if base type is textual * FALSE if some other type *********************************************************************/ extern boolean typ_is_string (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_is_enum * * Check if the base type is an enumeration * * INPUTS: * btyp == base type enum to check * * RETURNS: * TRUE if base type is an enumeration * FALSE if some other type *********************************************************************/ extern boolean typ_is_enum (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_new_pattern * * Malloc and init a pattern struct * * INPUTS: * pat_str == pattern string to copy and save * * RETURNS: * malloced struct or NULL if memory error *********************************************************************/ extern typ_pattern_t * typ_new_pattern (const xmlChar *pat_str); /******************************************************************** * FUNCTION typ_free_pattern * * Free a pattern struct * Must be freed from any Q before calling this function * * INPUTS: * pat == typ_pattern_t struct to free * *********************************************************************/ extern void typ_free_pattern (typ_pattern_t *pat); /******************************************************************** * FUNCTION typ_compile_pattern * * Compile a pattern as into a regex_t struct * * INPUTS: * btyp == base type of the string * sv == ncx_sval_t holding the pattern to compile * * OUTPUTS: * pat->pattern is set if NO_ERR * * RETURNS: * status *********************************************************************/ extern status_t typ_compile_pattern (typ_pattern_t *pat); /******************************************************************** * FUNCTION typ_get_first_pattern * * Get the first pattern struct for a typdef * * INPUTS: * typdef == typ_def_t to check * * RETURNS: * pointer to pattern string or NULL if none *********************************************************************/ extern typ_pattern_t * typ_get_first_pattern (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_next_pattern * * Get the next pattern struct for a typdef * * INPUTS: * curpat == current typ_pattern_t to check * * RETURNS: * pointer to next pattern struct or NULL if none *********************************************************************/ extern typ_pattern_t * typ_get_next_pattern (typ_pattern_t *curpat); /******************************************************************** * FUNCTION typ_get_first_cpattern * * Get the first pattern struct for a typdef * Const version * * INPUTS: * typdef == typ_def_t to check * * RETURNS: * pointer to pattern string or NULL if none *********************************************************************/ extern const typ_pattern_t * typ_get_first_cpattern (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_next_cpattern * * Get the next pattern struct for a typdef * Const version * * INPUTS: * curpat == current typ_pattern_t to check * * RETURNS: * pointer to next pattern struct or NULL if none *********************************************************************/ extern const typ_pattern_t * typ_get_next_cpattern (const typ_pattern_t *curpat); /******************************************************************** * FUNCTION typ_get_pattern_count * * Get the number of pattern structs in a typdef * * INPUTS: * typdef == typ_def_t to check * * RETURNS: * count of the typ_pattern_t structs found *********************************************************************/ extern uint32 typ_get_pattern_count (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_range_errinfo * * Get the range errinfo for a typdef * * INPUTS: * typdef == typ_def_t to check * * RETURNS: * pointer to pattern string or NULL if none *********************************************************************/ extern ncx_errinfo_t * typ_get_range_errinfo (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_clean_typeQ * * Clean a queue of typ_template_t structs * * INPUTS: * que == Q of typ_template_t to clean * *********************************************************************/ extern void typ_clean_typeQ (dlq_hdr_t *que); /******************************************************************** * FUNCTION typ_ok_for_inline_index * * Check if the base type is okay to use in an inline index decl * * INPUTS: * btyp == base type enum * RETURNS: * TRUE if okay, FALSE if not *********************************************************************/ extern boolean typ_ok_for_inline_index (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_ok_for_metadata * * Check if the base type is okay to use in an XML attribute * * INPUTS: * btyp == base type enum * RETURNS: * TRUE if okay, FALSE if not *********************************************************************/ extern boolean typ_ok_for_metadata (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_ok_for_index * * Check if the base type is okay to use in an index decl * * INPUTS: * typdef == type def struct to check * * RETURNS: * TRUE if okay, FALSE if not *********************************************************************/ extern boolean typ_ok_for_index (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_ok_for_union * * Check if the base type is okay to use in an union decl * * INPUTS: * btyp == base type enum * RETURNS: * TRUE if okay, FALSE if not *********************************************************************/ extern boolean typ_ok_for_union (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_ok * * Check if the typdef chain has any errors * Checks the named types in the typdef chain to * see if they were already flagged as invalid * * INPUTS: * typdef == starting typdef to check * RETURNS: * TRUE if okay, FALSE if any errors so far *********************************************************************/ extern boolean typ_ok (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_ok_for_xsdlist * * Check if the base type is okay to use in an ncx:xsdlist typedef * * INPUTS: * btyp == base type enum * RETURNS: * TRUE if okay, FALSE if not *********************************************************************/ extern boolean typ_ok_for_xsdlist (ncx_btype_t btyp); /******************************************************************** * FUNCTION typ_get_leafref_path * * Get the path argument for the leafref data type * * INPUTS: * typdef == typdef for the the leafref * * RETURNS: * pointer to the path argument or NULL if some error *********************************************************************/ extern const xmlChar * typ_get_leafref_path (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_leafref_path_addr * * Get the address of the path argument for the leafref data type * * INPUTS: * typdef == typdef for the the leafref * * RETURNS: * pointer to the path argument or NULL if some error *********************************************************************/ extern const void * typ_get_leafref_path_addr (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_leafref_pcb * * Get the XPath parser control block for the leafref data type * returns xpath_pcb_t but cannot import due to H file loop * INPUTS: * typdef == typdef for the the leafref * * RETURNS: * pointer to the PCB struct or NULL if some error *********************************************************************/ extern struct xpath_pcb_t_ * typ_get_leafref_pcb (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_constrained * * Get the constrained true/false field for the data type * leafref or instance-identifier constrained flag * * INPUTS: * typdef == typdef for the the leafref or instance-identifier * * RETURNS: * TRUE if constrained; FALSE if not *********************************************************************/ extern boolean typ_get_constrained (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_set_xref_typdef * * Set the target typdef for a leafref or instance-identifier * NCX_BT_LEAFREF or NCX_BT_INSTANCE_ID * * INPUTS: * typdef == typdef for the the leafref or instance-identifier * *********************************************************************/ extern void typ_set_xref_typdef (typ_def_t *typdef, typ_def_t *target); /******************************************************************** * FUNCTION typ_get_xref_typdef * * Get the xrefdef target typdef from a leafref * or instance-identifier * NCX_BT_LEAFREF or NCX_BT_INSTANCE_ID * * INPUTS: * typdef == typdef for the the leafref or instance-identifier * * RETURNS: * pointer to the PCB struct or NULL if some error *********************************************************************/ extern typ_def_t * typ_get_xref_typdef (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_has_subclauses * * Check if the specified typdef has any sub-clauses * Used by yangdump to reverse-engineer the YANG from the typdef * If any appinfo clauses present, then the result will be TRUE * * INPUTS: * typdef == typdef to check * * RETURNS: * TRUE if any sub-clauses, FALSE otherwise *********************************************************************/ extern boolean typ_has_subclauses (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_idref * * Get the idref field if this is an NCX_BT_IDREF typdef * * INPUTS: * typdef == typdef to check * * RETURNS: * pointer to idref field or NULL if wrong type *********************************************************************/ extern typ_idref_t * typ_get_idref (typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_cidref * * Get the idref field if this is an NCX_BT_IDREF typdef * Const version * * INPUTS: * typdef == typdef to check * * RETURNS: * pointer to idref field or NULL if wrong type *********************************************************************/ extern const typ_idref_t * typ_get_cidref (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_get_fraction_digits * * Get the fraction-digits field from the typdef chain * typdef must be an NCX_BT_DECIMAL64 or 0 will be returned * valid values are 1..18 * * INPUTS: * typdef == typdef to check * * RETURNS: * number of fixed decimal digits expected (1..18) * 0 if some error *********************************************************************/ extern uint8 typ_get_fraction_digits (const typ_def_t *typdef); /******************************************************************** * FUNCTION typ_set_fraction_digits * * Set the fraction-digits field from the typdef chain * * INPUTS: * typdef == typdef to set (must be TYP_CL_SIMPLE) * digits == digits value to set * * RETURNS: * status *********************************************************************/ extern status_t typ_set_fraction_digits (typ_def_t *typdef, uint8 digits); /******************************************************************** * FUNCTION typ_get_typ_linenum * * Get the line number for the typ_template_t * * INPUTS: * typ == type template to check * RETURNS: * line number *********************************************************************/ extern uint32 typ_get_typ_linenum (const typ_template_t *typ); /******************************************************************** * FUNCTION typ_get_typdef_linenum * * Get the line number for the typ_def_t * * INPUTS: * typ == typdef to check * RETURNS: * line number *********************************************************************/ extern uint32 typ_get_typdef_linenum (const typ_def_t *typdef); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_typ */ yuma123_2.14/netconf/src/ncx/uptime.h0000664000175000017500000000005514770023131017612 0ustar vladimirvladimir#include time_t uptime(time_t *t); yuma123_2.14/netconf/src/ncx/tk.h0000664000175000017500000006664114770023131016742 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_tk #define _H_tk /* FILE: tk.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* YANG Module Compact Syntax Token Handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 12-nov-05 abb Begun */ #include #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* maximum line size allowed in an YANG module */ #define TK_BUFF_SIZE 0xffff /* macros for quick processing of token chains * All these macros take 1 parameter * INPUTS: * T == pointer to the tk_chain_t in progress */ /* advance the current token pointer */ #define TK_ADV(T) \ ((T)->cur ? (((T)->cur = (tk_token_t *)dlq_nextEntry((T)->cur)) \ ? NO_ERR : ERR_NCX_EOF) : ERR_NCX_EOF) /* back-up the current token pointer */ #define TK_BKUP(T) \ if (! ((T)->cur = (tk_token_t *)dlq_prevEntry((T)->cur))) \ (T)->cur = (tk_token_t *)&((T)->tkQ) /* return the current token */ #define TK_CUR(T) ((T)->cur) /* return the current token type */ #define TK_CUR_TYP(T) ((T)->cur->typ) /* return the current token value */ #define TK_CUR_VAL(T) ((T)->cur->val) /* return the current token value length */ #define TK_CUR_LEN(T) ((T)->cur->len) /* return the current token module qualifier value */ #define TK_CUR_MOD(T) ((T)->cur->mod) /* return the current token module qualifier value length */ #define TK_CUR_MODLEN(T) ((T)->cur->modlen) /* return TRUE if the current token type is a string */ #define TK_CUR_STR(T) ((T)->cur->typ >= TK_TT_STRING && \ (T)->cur->typ <= TK_TT_SQSTRING) /* return TRUE if the specified token type is a string */ #define TK_TYP_STR(T) ((T) >= TK_TT_STRING && (T) <= TK_TT_SQSTRING) /* return TRUE if the cur tk type allows a non-whitespace string */ #define TK_CUR_NOWSTR(T) ((T)->cur->typ >= TK_TT_STRING && \ (T)->cur->typ <= TK_TT_SQSTRING) /* return TRUE if the cu token type allows whitespace string */ #define TK_CUR_WSTR(T) ((T)->cur->typ==TK_TT_STRING || \ (T)->cur->typ==TK_TT_SSTRING || \ (T)->cur->typ==TK_TT_TSTRING || \ (T)->cur->typ==TK_TT_QSTRING || \ (T)->cur->typ==TK_TT_SQSTRING) /* return TRUE if the current token type is a number */ #define TK_CUR_NUM(T) ((T)->cur->typ==TK_TT_DNUM || \ (T)->cur->typ==TK_TT_HNUM || \ (T)->cur->typ==TK_TT_RNUM) /* return TRUE if the current token type is an integral number */ #define TK_CUR_INUM(T) ((T)->cur->typ==TK_TT_DNUM || \ (T)->cur->typ==TK_TT_HNUM) /* return TRUE if the current token type is an instance qualifier */ #define TK_CUR_IQUAL(T) ((T)->cur->typ==TK_TT_QMARK || \ (T)->cur->typ==TK_TT_STAR || \ (T)->cur->typ==TK_TT_PLUS) /* return TRUE if the current token type is an identifier */ #define TK_CUR_ID(T) ((T)->cur->typ==TK_TT_TSTRING || \ (T)->cur->typ==TK_TT_MSTRING) /* return TRUE if the current token type is a scoped identifier */ #define TK_CUR_SID(T) ((T)->cur->typ==TK_TT_SSTRING || \ (T)->cur->typ==TK_TT_MSSTRING) /* return the current line number */ #define TK_CUR_LNUM(T) ((T)->cur->linenum) /* return the current line position */ #define TK_CUR_LPOS(T) ((T)->cur->linepos) /* return TRUE if the token is a text or number type, * as opposed to a 1 or 2 char non-text token type */ #define TK_CUR_TEXT(T) ((T)->cur->typ >= TK_TT_SSTRING &&\ (T)->cur->typ <= TK_TT_RNUM) /* return the namespace ID field in the token */ #define TK_CUR_NSID(T) ((T)->cur->nsid) /* return non-zero if token preservation docmode */ #define TK_DOCMODE(TKC) ((TKC)->flags & TK_FL_DOCMODE) #define TK_HAS_ORIGTK(TK) (((TK)->typ == TK_TT_QSTRING || \ (TK)->typ == TK_TT_SQSTRING) && \ (TK)->origval != NULL) /* bits for tk_chain_t flags field */ /* == 1: the current parse call is to redo a previously * parsed token * == 0: normal parse mode */ #define TK_FL_REDO bit0 /* == 1: the tkc->buff field is malloced by this module * and will be freed in tk_free_chain * == 0: the tkc->buff memory was provided externally * and will not be freed in tk_free_chain */ #define TK_FL_MALLOC bit1 /* == 1: DOC mode: the tk->origtkQ field will be used * to preserve pre-string concat and processing * == 0: normal mode: the rk-origtkQ will not be used * and only the result token will be saved */ #define TK_FL_DOCMODE bit2 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* different types of tokens parsed during 1st pass */ typedef enum tk_type_t_ { TK_TT_NONE, /* PUT ALL 1-CHAR TOKENS FIRST */ TK_TT_LBRACE, /* left brace '{' */ TK_TT_RBRACE, /* right brace '}' */ TK_TT_SEMICOL, /* semi-colon ';' */ TK_TT_LPAREN, /* left paren '(' */ TK_TT_RPAREN, /* right paren ')' */ TK_TT_LBRACK, /* left bracket '[' */ TK_TT_RBRACK, /* right bracket ']' */ TK_TT_COMMA, /* comma ',' */ TK_TT_EQUAL, /* equal sign '=' */ TK_TT_BAR, /* bar '|' */ TK_TT_STAR, /* star '*' */ TK_TT_ATSIGN, /* at sign '@' */ TK_TT_PLUS, /* plus mark '+' */ TK_TT_COLON, /* colon char ':' */ TK_TT_PERIOD, /* period char '.' */ TK_TT_FSLASH, /* forward slash char '/' */ TK_TT_MINUS, /* minus char '-' */ TK_TT_LT, /* less than char '<' */ TK_TT_GT, /* greater than char '>' */ /* PUT ALL 2-CHAR TOKENS SECOND */ TK_TT_RANGESEP, /* range sep, parent node '..' */ TK_TT_DBLCOLON, /* 2 colon chars '::' */ TK_TT_DBLFSLASH, /* 2 fwd slashes '//' */ TK_TT_NOTEQUAL, /* not equal '!=' */ TK_TT_LEQUAL, /* less than or equal '<=' */ TK_TT_GEQUAL, /* greater than or equal '>=' */ /* PUT ALL STRING CLASSIFICATION TOKENS THIRD */ TK_TT_STRING, /* unquoted string */ TK_TT_SSTRING, /* scoped token string */ TK_TT_TSTRING, /* token string */ TK_TT_MSTRING, /* module-qualified token string */ TK_TT_MSSTRING, /* module-qualified scoped string */ TK_TT_QSTRING, /* double quoted string */ TK_TT_SQSTRING, /* single quoted string */ TK_TT_VARBIND, /* XPath varbind '$NCName' */ TK_TT_QVARBIND, /* XPath varbind '$prefix:NCName' */ TK_TT_NCNAME_STAR, /* XPath NCName:* sequence */ /* PUT ALL NUMBER CLASSIFICATION TOKENS FOURTH */ TK_TT_DNUM, /* decimal number */ TK_TT_HNUM, /* hex number */ TK_TT_RNUM, /* real number */ /* PUT ALL SPECIAL CASE TOKENS LAST */ TK_TT_NEWLINE /* \n is significant in conf files */ } tk_type_t; typedef enum tk_source_t_ { TK_SOURCE_CONF, TK_SOURCE_YANG, TK_SOURCE_XPATH, TK_SOURCE_REDO } tk_source_t; typedef enum tk_origstr_typ_t_ { TK_ORIGSTR_NONE, TK_ORIGSTR_DQUOTE, TK_ORIGSTR_DQUOTE_NL, TK_ORIGSTR_SQUOTE, TK_ORIGSTR_SQUOTE_NL } tk_origstr_typ_t; /* each entry in the origstrQ is the 2nd through Nth string * to be concated. The first string is in the tk->origval * string for double quote and tk->val for single quote */ typedef struct tk_origstr_t_ { dlq_hdr_t qhdr; tk_origstr_typ_t origtyp; xmlChar *str; } tk_origstr_t; /* single YANG language token type */ typedef struct tk_token_t_ { dlq_hdr_t qhdr; tk_type_t typ; xmlChar *mod; /* only used if prefix found */ uint32 modlen; /* length of 'mod'; not Z-string! */ xmlChar *val; xmlChar *origval; /* used in DOCMODE for yangdump */ uint32 len; uint32 linenum; uint32 linepos; xmlns_id_t nsid; /* only used for TK_TT_MSTRING tokens */ dlq_hdr_t origstrQ; /* Q of tk_origstr_t only used in DOCMODE */ } tk_token_t; /* token backptr to get at original token chain for strings * used only by yangdump --format=yang|html */ typedef struct tk_token_ptr_t_ { dlq_hdr_t qhdr; tk_token_t *tk; const void *field; } tk_token_ptr_t; /* token parsing chain */ typedef struct tk_chain_t_ { dlq_hdr_t qhdr; dlq_hdr_t tkQ; /* Q of tk_token_t */ dlq_hdr_t tkptrQ; /* Q of tk_token_ptr_t */ tk_token_t *cur; ncx_error_t *curerr; const xmlChar *filename; FILE *fp; xmlChar *buff; xmlChar *bptr; uint32 linenum; uint32 linepos; uint32 flags; tk_source_t source; } tk_chain_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION tk_new_chain * * Allocatate a new token parse chain * * RETURNS: * new parse chain or NULL if memory error *********************************************************************/ extern tk_chain_t * tk_new_chain (void); /******************************************************************** * FUNCTION tk_setup_chain_conf * * Setup a previously allocated chain for a text config file * * INPUTS * tkc == token chain to setup * fp == open file to use for text source * filename == source filespec *********************************************************************/ extern void tk_setup_chain_conf (tk_chain_t *tkc, FILE *fp, const xmlChar *filename); /******************************************************************** * FUNCTION tk_setup_chain_yang * * Setup a previously allocated chain for a YANG file * * INPUTS * tkc == token chain to setup * fp == open file to use for text source * filename == source filespec *********************************************************************/ extern void tk_setup_chain_yang (tk_chain_t *tkc, FILE *fp, const xmlChar *filename); /******************************************************************** * FUNCTION tk_setup_chain_yin * * Setup a previously allocated chain for a YIN file * * INPUTS * tkc == token chain to setup * filename == source filespec *********************************************************************/ extern void tk_setup_chain_yin (tk_chain_t *tkc, const xmlChar *filename); /******************************************************************** * FUNCTION tk_setup_chain_docmode * * Setup a previously allocated chain for a yangdump docmode output * * INPUTS * tkc == token chain to setup *********************************************************************/ extern void tk_setup_chain_docmode (tk_chain_t *tkc); /******************************************************************** * FUNCTION tk_free_chain * * Cleanup and deallocate a tk_chain_t * INPUTS: * tkc == TK chain to delete * RETURNS: * none *********************************************************************/ extern void tk_free_chain (tk_chain_t *tkc); /******************************************************************** * FUNCTION tk_get_yang_btype_id * * Check if the specified string is a YANG builtin type name * checks for valid YANG data type name * * INPUTS: * buff == token string to check -- NOT ZERO-TERMINATED * len == length of string to check * RETURNS: * btype found or NCX_BT_NONE if no match *********************************************************************/ extern ncx_btype_t tk_get_yang_btype_id (const xmlChar *buff, uint32 len); /******************************************************************** * FUNCTION tk_get_token_name * * Get the symbolic token name * * INPUTS: * ttyp == token type * RETURNS: * const string to the name; will not be NULL *********************************************************************/ extern const char * tk_get_token_name (tk_type_t ttyp); /******************************************************************** * FUNCTION tk_get_token_sym * * Get the symbolic token symbol * * INPUTS: * ttyp == token type * RETURNS: * const string to the symbol; will not be NULL *********************************************************************/ extern const char * tk_get_token_sym (tk_type_t ttyp); /******************************************************************** * FUNCTION tk_get_btype_sym * * Get the symbolic token symbol for one of the base types * * INPUTS: * btyp == base type * RETURNS: * const string to the symbol; will not be NULL *********************************************************************/ extern const char * tk_get_btype_sym (ncx_btype_t btyp); /******************************************************************** * FUNCTION tk_next_typ * * Get the token type of the next token * * INPUTS: * tkc == token chain * RETURNS: * token type *********************************************************************/ extern tk_type_t tk_next_typ (tk_chain_t *tkc); /******************************************************************** * FUNCTION tk_next_typ2 * * Get the token type of the token after the next token * * INPUTS: * tkc == token chain * RETURNS: * token type *********************************************************************/ extern tk_type_t tk_next_typ2 (tk_chain_t *tkc); /******************************************************************** * FUNCTION tk_next_val * * Get the token type of the next token * * INPUTS: * tkc == token chain * RETURNS: * token type *********************************************************************/ extern const xmlChar * tk_next_val (tk_chain_t *tkc); /******************************************************************** * FUNCTION tk_dump_token * * Debug printf the specified token * !!! Very verbose !!! * * INPUTS: * tk == token * *********************************************************************/ extern void tk_dump_token (const tk_token_t *tk); /******************************************************************** * FUNCTION tk_dump_chain * * Debug printf the token chain * !!! Very verbose !!! * * INPUTS: * tkc == token chain * * RETURNS: * none *********************************************************************/ extern void tk_dump_chain (const tk_chain_t *tkc); /******************************************************************** * FUNCTION tk_is_wsp_string * * Check if the current token is a string with whitespace in it * * INPUTS: * tk == token to check * * RETURNS: * TRUE if a string with whitespace in it * FALSE if not a string or no whitespace in the string *********************************************************************/ extern boolean tk_is_wsp_string (const tk_token_t *tk); /******************************************************************** * FUNCTION tk_tokenize_input * * Parse the input (FILE or buffer) into tk_token_t structs * * The tkc param must be initialized to use the internal * buffer to read from the specified filespec: * * tkc->filename * tkc->flags * tkc->fp * tkc->source * * External buffer mode: * * If no filename is provided, the the TK_FL_MALLOC * flag will not be set, and the tkc->buff field * must be initialized before this function is called. * This function will not free the buffer, * but just read from it until a '0' char is reached. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress (NULL if not used) * !!! Just used for error messages !!! * * RETURNS: * status of the operation *********************************************************************/ extern status_t tk_tokenize_input (tk_chain_t *tkc, ncx_module_t *mod); /******************************************************************** * FUNCTION tk_retokenize_cur_string * * The current token is some sort of a string * Reparse it according to the full YANG token list, as needed * * The current token may be replaced with one or more tokens * * INPUTS: * tkc == token chain * mod == module in progress (NULL if not used) * * RETURNS: * status of the operation *********************************************************************/ extern status_t tk_retokenize_cur_string (tk_chain_t *tkc, ncx_module_t *mod); /******************************************************************** * FUNCTION tk_tokenize_metadata_string * * The specified ncx:metadata string is parsed into tokens * convert the ncx:metadata content to 1 or 2 tokens * * INPUTS: * mod == module in progress for error purposes (may be NULL) * str == string to tokenize * res == address of return status * * OUTPUTS: * *res == error status, if return NULL or non-NULL * * RETURNS: * pointer to malloced and filled in token chain * ready to be traversed; always check *res for valid syntax *********************************************************************/ extern tk_chain_t * tk_tokenize_metadata_string (ncx_module_t *mod, xmlChar *str, status_t *res); /******************************************************************** * FUNCTION tk_tokenize_xpath_string * * The specified XPath string is parsed into tokens * * INPUTS: * mod == module in progress for error purposes (may be NULL) * str == string to tokenize * curlinenum == current line number * curlinepos == current line position * res == address of return status * * OUTPUTS: * *res == error status, if return NULL or non-NULL * * RETURNS: * pointer to malloced and filled in token chain * ready to be traversed; always check *res for valid syntax *********************************************************************/ extern tk_chain_t * tk_tokenize_xpath_string (ncx_module_t *mod, xmlChar *str, uint32 curlinenum, uint32 curlinepos, status_t *res); /******************************************************************** * FUNCTION tk_token_count * * Get the number of tokens in the queue * * INPUTS: * tkc == token chain to check * * RETURNS: * number of tokens in the queue *********************************************************************/ extern uint32 tk_token_count (const tk_chain_t *tkc); /******************************************************************** * FUNCTION tk_reset_chain * * Reset the token chain current pointer to the start * * INPUTS: * tkc == token chain to reset * *********************************************************************/ extern void tk_reset_chain (tk_chain_t *tkc); /******************************************************************** * FUNCTION tk_clone_chain * * Allocatate and a new token parse chain and fill * it with the specified token chain contents * * INPUTS: * oldtkc == token chain to clone * * RETURNS: * new cloned parse chain or NULL if memory error *********************************************************************/ extern tk_chain_t * tk_clone_chain (tk_chain_t *oldtkc); /******************************************************************** * FUNCTION tk_add_id_token * * Allocatate a new ID token and add it to the parse chain * * INPUTS: * tkc == token chain to use * valstr == ID name * * RETURNS: * status *********************************************************************/ extern status_t tk_add_id_token (tk_chain_t *tkc, const xmlChar *valstr); /******************************************************************** * FUNCTION tk_add_pid_token * * Allocatate a new prefixed ID token and add it to * the parse chain * * INPUTS: * tkc == token chain to use * prefix == ID prefix * prefixlen == 'prefix' length in bytes * valstr == ID name * * RETURNS: * status *********************************************************************/ extern status_t tk_add_pid_token (tk_chain_t *tkc, const xmlChar *prefix, uint32 prefixlen, const xmlChar *valstr); /******************************************************************** * FUNCTION tk_add_string_token * * Allocatate a new string token and add it to the parse chain * * INPUTS: * tkc == token chain to use * valstr == string value to use * * RETURNS: * status *********************************************************************/ extern status_t tk_add_string_token (tk_chain_t *tkc, const xmlChar *valstr); /******************************************************************** * FUNCTION tk_add_lbrace_token * * Allocatate a new left brace token and add it to the parse chain * * INPUTS: * tkc == token chain to use * * RETURNS: * status *********************************************************************/ extern status_t tk_add_lbrace_token (tk_chain_t *tkc); /******************************************************************** * FUNCTION tk_add_rbrace_token * * Allocatate a new right brace token and add it to the parse chain * * INPUTS: * tkc == token chain to use * * RETURNS: * status *********************************************************************/ extern status_t tk_add_rbrace_token (tk_chain_t *tkc); /******************************************************************** * FUNCTION tk_add_semicol_token * * Allocatate a new semi-colon token and add it to the parse chain * * INPUTS: * tkc == token chain to use * * RETURNS: * status *********************************************************************/ extern status_t tk_add_semicol_token (tk_chain_t *tkc); /******************************************************************** * FUNCTION tk_check_save_origstr * * Check the docmode and the specified token; * Save a tk_origptr_t if needed with the field address * * INPUTS: * tkc = token chain to use * tk = token to use (usually TK_CUR()) * field == address of string field to save as the key * * RETURNS: * status; ERR_INTERNAL_MEM if entry cannot be malloced *********************************************************************/ extern status_t tk_check_save_origstr (tk_chain_t *tkc, tk_token_t *tk, const void *field); /******************************************************************** * FUNCTION tk_get_first_origstr * * Get the first original string to use * * INPUTS: * tkptr = token pointer to use * dquote = address of return double quote flag * morestr == addres of return more string fragments flag * OUTPUTS: * *dquote = return double quote flag * TRUE == TK_TT_QSTRING * FALSE == TK_TTSQSTRING * *morestr == addres of return more string fragments flag * TRUE == more string fragments after first one * RETURNS: * pointer to the first string fragment *********************************************************************/ extern const xmlChar * tk_get_first_origstr (const tk_token_ptr_t *tkptr, boolean *dquote, boolean *morestr); /******************************************************************** * FUNCTION tk_first_origstr_rec * * Get the first tk_origstr_t struct (if any) * * INPUTS: * tkptr = token pointer to use * * RETURNS: * pointer to the first original string record *********************************************************************/ extern const tk_origstr_t * tk_first_origstr_rec (const tk_token_ptr_t *tkptr); /******************************************************************** * FUNCTION tk_next_origstr_rec * * Get the next tk_origstr_t struct (if any) * * INPUTS: * origstr = origisnal string record pointer to use * * RETURNS: * pointer to the next original string record *********************************************************************/ extern const tk_origstr_t * tk_next_origstr_rec (const tk_origstr_t *origstr); /******************************************************************** * FUNCTION tk_get_origstr_parts * * Get the fields from the original string record * * INPUTS: * origstr = original string record pointer to use * dquote = address of return double quote flag * newline == addres of return need newline flag * OUTPUTS: * *dquote = return double quote flag * TRUE == TK_TT_QSTRING * FALSE == TK_TTSQSTRING * *newline == return need newline flag * TRUE == need newline + indent before this string * RETURNS: * pointer to the string fragment *********************************************************************/ extern const xmlChar * tk_get_origstr_parts (const tk_origstr_t *origstr, boolean *dquote, boolean *newline); /******************************************************************** * FUNCTION tk_find_tkptr * * Find the specified token pointer record * * INPUTS: * tkc = token chain to use * field == address of field to use as key to find * * RETURNS: * pointer to the token pointer record or NULL if not found *********************************************************************/ extern const tk_token_ptr_t * tk_find_tkptr (const tk_chain_t *tkc, const void *field); /******************************************************************** * FUNCTION tk_tkptr_quotes * * Get the specified token pointer record token ID type * Use the first string or only string * * INPUTS: * tkptr = token pointer to use * * RETURNS: * number of quotes used in first string *********************************************************************/ extern uint32 tk_tkptr_quotes (const tk_token_ptr_t *tkptr); /******************************************************************** * FUNCTION set_tkc_error * * utility function for setting and reporting tkc errors. * * INPUTS: * tkc the parser token chain (may be NULL) * mod the module * res the error status. * * RETURNS: * the error status ***************************************************************/ extern status_t set_tkc_error( tk_chain_t *tkc, ncx_module_t *mod, ncx_error_t *err, status_t res ); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_tk */ yuma123_2.14/netconf/src/ncx/val_set_cplxval_obj.h0000664000175000017500000000056114770023131022331 0ustar vladimirvladimir#include #include #include "procdefs.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" status_t val_set_cplxval_obj(val_value_t *val, obj_template_t *obj, char* xmlstr); //status_t val_set_cplxval_obj_recursive(val_value_t *val, obj_template_t *obj, xmlDocPtr doc, xmlNode *top); yuma123_2.14/netconf/src/ncx/cfg.h0000664000175000017500000005255014770023131017055 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_cfg #define _H_cfg /* FILE: cfg.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX configuration database manager Configuration segments are stored in sequential order. NEW YANG OBJECT MODEL ===================== Configuration database (running, candidate, startup, etc.) + | +-- (root: /) + | +--- object X (netconf, security, routing, etc.) | | | +---- object A , B, C | +--- object Y | | | +---- object D , E V MODULE USAGE ============ 1) call cfg_init() 2) call cfg_init_static_db for the various hard-wired databases that need to be created by the agent 3) call cfg_apply_load_root() with the startup database contents to load into the running config 4) call cfg_set_target() [NCX_CFGID_CANDIDATE or NCX_CFGID_RUNNING] 4a) call cfg_fill_candidate_from_running if the target is candidate; after agt.c/load_running_config is called 5) call cfg_set_state() to setup config db access, when ready for NETCONF operations 6) Use cfg_ok_to_* functions to test config access 7) use cfg_lock and cfg_unlock as desired to set global write locks 8) use cfg_release_locks when a session terminates 9) call cfg_cleanup() when program is terminating ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 15-apr-06 abb Begun. 29-may-08 abb Converted NCX database model to simplified YANG object model */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_plock #include "plock.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* bit definitions for the cfg_template->flags field */ #define CFG_FL_TARGET bit0 #define CFG_FL_DIRTY bit1 #define CFG_INITIAL_TXID (cfg_transaction_id_t)0 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* current configuration state */ typedef enum cfg_state_t_ { CFG_ST_NONE, /* not set */ CFG_ST_INIT, /* init in progress */ CFG_ST_READY, /* ready and no locks */ CFG_ST_PLOCK, /* partial lock active */ CFG_ST_FLOCK, /* full lock active */ CFG_ST_CLEANUP /* cleanup in progress */ } cfg_state_t; /* classify the config source */ typedef enum cfg_source_t_ { CFG_SRC_NONE, CFG_SRC_INTERNAL, CFG_SRC_NETCONF, CFG_SRC_CLI, CFG_SRC_SNMP, CFG_SRC_HTTP, CFG_SRC_OTHER } cfg_source_t; /* classify the config location */ typedef enum cfg_location_t_ { CFG_LOC_NONE, CFG_LOC_INTERNAL, CFG_LOC_FILE, CFG_LOC_NAMED, CFG_LOC_LOCAL_URL, CFG_LOC_REMOTE_URL } cfg_location_t; /* transaction is scoped to single session write operation on a config */ typedef uint64 cfg_transaction_id_t; /* struct representing 1 configuration database */ typedef struct cfg_template_t_ { ncx_cfg_t cfg_id; cfg_location_t cfg_loc; cfg_state_t cfg_state; cfg_transaction_id_t last_txid; cfg_transaction_id_t cur_txid; xmlChar *name; xmlChar *src_url; xmlChar lock_time[TSTAMP_MIN_SIZE]; xmlChar last_ch_time[TSTAMP_MIN_SIZE]; uint32 flags; ses_id_t locked_by; cfg_source_t lock_src; dlq_hdr_t load_errQ; /* Q of rpc_err_rec_t */ dlq_hdr_t plockQ; /* Q of plock_cb_t */ val_value_t *root; /* btyp == NCX_BT_CONTAINER */ } cfg_template_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION cfg_init * * Initialize the config manager * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void cfg_init (void); /******************************************************************** * FUNCTION cfg_cleanup * * Cleanup the config manager * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void cfg_cleanup (void); /******************************************************************** * FUNCTION cfg_init_static_db * * Initialize the specified static configuration slot * * INPUTS: * id == cfg ID to intialize * * RETURNS: * status *********************************************************************/ extern status_t cfg_init_static_db (ncx_cfg_t cfg_id); /******************************************************************** * FUNCTION cfg_new_template * * Malloc and initialize a cfg_template_t struct * * INPUTS: * name == cfg name * cfg_id == cfg ID * RETURNS: * malloced struct or NULL if some error * This struct needs to be freed by the caller *********************************************************************/ extern cfg_template_t * cfg_new_template (const xmlChar *name, ncx_cfg_t cfg_id); /******************************************************************** * FUNCTION cfg_free_template * * Clean and free the cfg_template_t struct * * INPUTS: * cfg = cfg_template_t to clean and free * RETURNS: * none *********************************************************************/ extern void cfg_free_template (cfg_template_t *cfg); /******************************************************************** * FUNCTION cfg_set_state * * Change the state of the specified static config * * INPUTS: * cfg_id = Config ID to change * new_state == new config state to set * RETURNS: * none *********************************************************************/ extern void cfg_set_state (ncx_cfg_t cfg_id, cfg_state_t new_state); /******************************************************************** * FUNCTION cfg_get_state * * Get the state of the specified static config * * INPUTS: * cfg_id = Config ID * RETURNS: * config state (CFG_ST_NONE if some error) *********************************************************************/ extern cfg_state_t cfg_get_state (ncx_cfg_t cfg_id); /******************************************************************** * FUNCTION cfg_get_config * * Get the config struct from its name * * INPUTS: * cfgname = Config Name * RETURNS: * pointer to config struct or NULL if not found *********************************************************************/ extern cfg_template_t * cfg_get_config (const xmlChar *cfgname); /******************************************************************** * FUNCTION cfg_get_config_name * * Get the config name from its ID * * INPUTS: * cfgid == config ID * RETURNS: * pointer to config name or NULL if not found *********************************************************************/ extern const xmlChar * cfg_get_config_name (ncx_cfg_t cfgid); /******************************************************************** * FUNCTION cfg_get_config_id * * Get the config struct from its ID * * INPUTS: * cfgid == config ID * RETURNS: * pointer to config struct or NULL if not found *********************************************************************/ extern cfg_template_t * cfg_get_config_id (ncx_cfg_t cfgid); /******************************************************************** * FUNCTION cfg_set_target * * Set the CFG_FL_TARGET flag in the specified config * * INPUTS: * cfg_id = Config ID to set as a valid target * *********************************************************************/ extern void cfg_set_target (ncx_cfg_t cfg_id); /******************************************************************** * FUNCTION cfg_fill_candidate_from_running * * Fill the config with the config contents * of the config * * RETURNS: * status *********************************************************************/ extern status_t cfg_fill_candidate_from_running (void); /******************************************************************** * FUNCTION cfg_fill_candidate_from_startup * * Fill the config with the config contents * of the config * * RETURNS: * status *********************************************************************/ extern status_t cfg_fill_candidate_from_startup (void); /******************************************************************** * FUNCTION cfg_fill_candidate_from_inline * * Fill the config with the config contents * of the inline XML node * * INPUTS: * newroot == new root for the candidate config * * RETURNS: * status *********************************************************************/ extern status_t cfg_fill_candidate_from_inline (val_value_t *newroot); /******************************************************************** * FUNCTION cfg_set_dirty_flag * * Mark the config as 'changed' * * INPUTS: * cfg == configuration template to set * *********************************************************************/ extern void cfg_set_dirty_flag (cfg_template_t *cfg); /******************************************************************** * FUNCTION cfg_get_dirty_flag * * Get the config dirty flag value * * INPUTS: * cfg == configuration template to check * *********************************************************************/ extern boolean cfg_get_dirty_flag (const cfg_template_t *cfg); /******************************************************************** * FUNCTION cfg_ok_to_lock * * Check if the specified config can be locked right now * for global lock only * * INPUTS: * cfg = Config template to check * * RETURNS: * status *********************************************************************/ extern status_t cfg_ok_to_lock (const cfg_template_t *cfg); /******************************************************************** * FUNCTION cfg_ok_to_unlock * * Check if the specified config can be unlocked right now * by the specified session ID; for global lock only * * INPUTS: * cfg = Config template to check * sesid == session ID requesting to unlock the config * RETURNS: * status *********************************************************************/ extern status_t cfg_ok_to_unlock (const cfg_template_t *cfg, ses_id_t sesid); /******************************************************************** * FUNCTION cfg_ok_to_read * * Check if the specified config can be read right now * * INPUTS: * cfg = Config template to check * RETURNS: * status *********************************************************************/ extern status_t cfg_ok_to_read (const cfg_template_t *cfg); /******************************************************************** * FUNCTION cfg_ok_to_write * * Check if the specified config can be written right now * by the specified session ID * * This is not an access control check, * only locks and config state will be checked * * INPUTS: * cfg = Config template to check * sesid == session ID requesting to write to the config * RETURNS: * status *********************************************************************/ extern status_t cfg_ok_to_write (const cfg_template_t *cfg, ses_id_t sesid); /******************************************************************** * FUNCTION cfg_is_global_locked * * Check if the specified config has ab active global lock * * INPUTS: * cfg = Config template to check * * RETURNS: * TRUE if global lock active, FALSE if not *********************************************************************/ extern boolean cfg_is_global_locked (const cfg_template_t *cfg); /******************************************************************** * FUNCTION cfg_is_partial_locked * * Check if the specified config has any active partial locks * * INPUTS: * cfg = Config template to check * * RETURNS: * TRUE if partial lock active, FALSE if not *********************************************************************/ extern boolean cfg_is_partial_locked (const cfg_template_t *cfg); /******************************************************************** * FUNCTION cfg_get_global_lock_info * * Get the current global lock info * * INPUTS: * cfg = Config template to check * sid == address of return session ID * locktime == address of return locktime pointer * * OUTPUTS: * *sid == session ID of lock holder * *locktime == pointer to lock time string * * RETURNS: * status, NCX_ERR_SKIPPED if not locked *********************************************************************/ extern status_t cfg_get_global_lock_info (const cfg_template_t *cfg, ses_id_t *sid, const xmlChar **locktime); /******************************************************************** * FUNCTION cfg_lock * * Lock the specified config. * This will not really have an effect unless the * CFG_FL_TARGET flag in the specified config is also set * * INPUTS: * cfg = Config template to lock * locked_by == session ID of the lock owner * lock_src == enum classifying the lock source * RETURNS: * status *********************************************************************/ extern status_t cfg_lock (cfg_template_t *cfg, ses_id_t locked_by, cfg_source_t lock_src); /******************************************************************** * FUNCTION cfg_unlock * * Unlock the specified config. * * INPUTS: * cfg = Config template to unlock * locked_by == session ID of the lock owner * RETURNS: * status *********************************************************************/ extern status_t cfg_unlock (cfg_template_t *cfg, ses_id_t locked_by); /******************************************************************** * FUNCTION cfg_release_locks * * Release any configuration locks held by the specified session * * INPUTS: * sesid == session ID to check for * *********************************************************************/ extern void cfg_release_locks (ses_id_t sesid); /******************************************************************** * FUNCTION cfg_release_partial_locks * * Release any configuration locks held by the specified session * * INPUTS: * sesid == session ID to check for * *********************************************************************/ extern void cfg_release_partial_locks (ses_id_t sesid); /******************************************************************** * FUNCTION cfg_get_lock_list * * Get a list of all the locks held by a session * * INPUTS: * sesid == session ID to check for any locks * retval == pointer to malloced and initialized NCX_BT_SLIST * * OUTPUTS: * *retval is filled in with any lock entryies or left empty * if none found * *********************************************************************/ extern void cfg_get_lock_list (ses_id_t sesid, val_value_t *retval); /******************************************************************** * FUNCTION cfg_apply_load_root * * Apply the AGT_CB_APPLY function for the OP_EDITOP_LOAD operation * * INPUTS: * cfg == config target * newroot == new config tree * *********************************************************************/ extern void cfg_apply_load_root (cfg_template_t *cfg, val_value_t *newroot); /******************************************************************** * FUNCTION cfg_update_last_ch_time * * Update the last-modified timestamp * * INPUTS: * cfg == config target *********************************************************************/ extern void cfg_update_last_ch_time (cfg_template_t *cfg); /******************************************************************** * FUNCTION cfg_update_last_txid * * Update the last good transaction ID * * INPUTS: * cfg == config target * txid == trnasaction ID to use *********************************************************************/ extern void cfg_update_last_txid (cfg_template_t *cfg, cfg_transaction_id_t txid); /******************************************************************** * FUNCTION cfg_add_partial_lock * * Add a partial lock the specified config. * This will not really have an effect unless the * CFG_FL_TARGET flag in the specified config is also set * For global lock only * * INPUTS: * cfg = Config template to lock * plcb == partial lock control block, already filled * out, to add to this configuration * * RETURNS: * status; if NO_ERR then the memory in plcb is handed off * and will be released when cfg_remove_partial_lock * is called for 'plcb' *********************************************************************/ extern status_t cfg_add_partial_lock (cfg_template_t *cfg, plock_cb_t *plcb); /******************************************************************** * FUNCTION cfg_find_partial_lock * * Find a partial lock in the specified config. * * INPUTS: * cfg = Config template to use * lockid == lock-id for the plcb to find * * RETURNS: * pointer to the partial lock control block found * NULL if not found *********************************************************************/ extern plock_cb_t * cfg_find_partial_lock (cfg_template_t *cfg, plock_id_t lockid); /******************************************************************** * FUNCTION cfg_first_partial_lock * * Get the first partial lock in the specified config. * * INPUTS: * cfg = Config template to use * * RETURNS: * pointer to the first partial lock control block * NULL if none exist at this time *********************************************************************/ extern plock_cb_t * cfg_first_partial_lock (cfg_template_t *cfg); /******************************************************************** * FUNCTION cfg_next_partial_lock * * Get the next partial lock in the specified config. * * INPUTS: * curplockcb == current lock control block; get next CB * * RETURNS: * pointer to the next partial lock control block * NULL if none exist at this time *********************************************************************/ extern plock_cb_t * cfg_next_partial_lock (plock_cb_t *curplockcb); /******************************************************************** * FUNCTION cfg_delete_partial_lock * * Remove a partial lock from the specified config. * * INPUTS: * cfg = Config template to use * lockid == lock-id for the plcb to remove * *********************************************************************/ extern void cfg_delete_partial_lock (cfg_template_t *cfg, plock_id_t lockid); /******************************************************************** * FUNCTION cfg_ok_to_partial_lock * * Check if the specified config can be locked right now * for partial lock only * * INPUTS: * cfg = Config template to check * * RETURNS: * status *********************************************************************/ extern status_t cfg_ok_to_partial_lock (const cfg_template_t *cfg); /******************************************************************** * FUNCTION cfg_get_root * * Get the config root for the specified config * * INPUTS: * cfgid == config ID to get root from * * RETURNS: * config root or NULL if none or error *********************************************************************/ extern val_value_t * cfg_get_root (ncx_cfg_t cfgid); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_cfg */ yuma123_2.14/netconf/src/ncx/cap.h0000664000175000017500000005105114770023131017054 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_cap #define _H_cap /* FILE: cap.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF protocol capabilities ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 28-apr-05 abb Begun. */ #include "ncxtypes.h" #include "status.h" #include "val.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define MAX_STD_CAP_NAME_LEN 31 #define CAP_VERSION_LEN 15 /* NETCONF Base Protocol Capability String (base:1.0) */ #define CAP_BASE_URN ((const xmlChar *) \ "urn:ietf:params:netconf:base:1.0") /* NETCONF Base Protocol Capability String (base:1.1) */ #define CAP_BASE_URN11 ((const xmlChar *) \ "urn:ietf:params:netconf:base:1.1") /* NETCONF Capability Identifier Base String */ #define CAP_URN ((const xmlChar *)"urn:ietf:params:netconf:capability:") /* NETCONF Capability Identifier Base String Implemented by Juniper */ #define CAP_J_URN \ (const xmlChar *)"urn:ietf:params:xml:ns:netconf:capability:" #define CAP_JUNOS \ (const xmlChar *)"http://xml.juniper.net/netconf/junos/1.0" #define CAP_SEP_CH '/' /************************************************************ * * * The following 2 sets of definitions must be kept aligned * * * ************************************************************/ /* fast lookup -- standard capability bit ID */ #define CAP_BIT_V1 bit0 #define CAP_BIT_WR_RUN bit1 #define CAP_BIT_CANDIDATE bit2 #define CAP_BIT_CONF_COMMIT bit3 #define CAP_BIT_ROLLBACK_ERR bit4 #define CAP_BIT_VALIDATE bit5 #define CAP_BIT_STARTUP bit6 #define CAP_BIT_URL bit7 #define CAP_BIT_XPATH bit8 #define CAP_BIT_NOTIFICATION bit9 #define CAP_BIT_INTERLEAVE bit10 #define CAP_BIT_PARTIAL_LOCK bit11 #define CAP_BIT_WITH_DEFAULTS bit12 #define CAP_BIT_V11 bit13 #define CAP_BIT_CONF_COMMIT11 bit14 #define CAP_BIT_VALIDATE11 bit15 #define CAP_BIT_YANG_LIBRARY bit16 /* put the version numbers in the capability names for now */ #define CAP_NAME_V1 ((const xmlChar *)"base:1.0") #define CAP_NAME_WR_RUN ((const xmlChar *)"writable-running:1.0") #define CAP_NAME_CANDIDATE ((const xmlChar *)"candidate:1.0") #define CAP_NAME_CONF_COMMIT ((const xmlChar *)"confirmed-commit:1.0") #define CAP_NAME_ROLLBACK_ERR ((const xmlChar *)"rollback-on-error:1.0") #define CAP_NAME_VALIDATE ((const xmlChar *)"validate:1.0") #define CAP_NAME_STARTUP ((const xmlChar *)"startup:1.0") #define CAP_NAME_URL ((const xmlChar *)"url:1.0") #define CAP_NAME_XPATH ((const xmlChar *)"xpath:1.0") #define CAP_NAME_NOTIFICATION ((const xmlChar *)"notification:1.0") #define CAP_NAME_INTERLEAVE ((const xmlChar *)"interleave:1.0") #define CAP_NAME_PARTIAL_LOCK ((const xmlChar *)"partial-lock:1.0") #define CAP_NAME_WITH_DEFAULTS ((const xmlChar *)"with-defaults:1.0") #define CAP_NAME_V11 ((const xmlChar *)"base:1.1") #define CAP_NAME_VALIDATE11 ((const xmlChar *)"validate:1.1") #define CAP_NAME_CONF_COMMIT11 ((const xmlChar *)"confirmed-commit:1.1") #define CAP_NAME_YANG_LIBRARY ((const xmlChar *)"yang-library:1.0") /* some YANG capability details */ #define CAP_REVISION_EQ (const xmlChar *)"revision=" #define CAP_MODULE_EQ (const xmlChar *)"module=" #define CAP_FEATURES_EQ (const xmlChar *)"features=" #define CAP_DEVIATIONS_EQ (const xmlChar *)"deviations=" #define CAP_SCHEME_EQ (const xmlChar *)"scheme=" #define CAP_PROTOCOL_EQ (const xmlChar *)"protocol=" #define CAP_BASIC_EQ (const xmlChar *)"basic-mode=" #define CAP_SUPPORTED_EQ (const xmlChar *)"also-supported=" #define CAP_MODULE_SET_ID_EQ (const xmlChar *)"module-set-id=" #define CAP_SCHEMA_RETRIEVAL \ (const xmlChar *)"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* NETCONF capability subject types */ typedef enum cap_subjtyp_t_ { CAP_SUBJTYP_NONE, CAP_SUBJTYP_PROT, /* capability is a protocol extension */ CAP_SUBJTYP_DM, /* capability is a data model */ CAP_SUBJTYP_OTHER /* capability is other than prot or DM */ } cap_subjtyp_t; /* enumerated list of standard capability IDs */ typedef enum cap_stdid_t_ { CAP_STDID_V1, CAP_STDID_WRITE_RUNNING, CAP_STDID_CANDIDATE, CAP_STDID_CONF_COMMIT, CAP_STDID_ROLLBACK_ERR, CAP_STDID_VALIDATE, CAP_STDID_STARTUP, CAP_STDID_URL, CAP_STDID_XPATH, CAP_STDID_NOTIFICATION, CAP_STDID_INTERLEAVE, CAP_STDID_PARTIAL_LOCK, CAP_STDID_WITH_DEFAULTS, CAP_STDID_V11, CAP_STDID_VALIDATE11, CAP_STDID_CONF_COMMIT11, CAP_STDID_YANG_LIBRARY, CAP_STDID_LAST_MARKER } cap_stdid_t; /* 1 capabilities list */ typedef struct cap_list_t_ { uint32 cap_std; /* bitset of std caps */ xmlChar *cap_schemes; /* URL capability protocol list */ xmlChar *cap_defstyle; /* with-defaults 'basic' parm */ xmlChar *cap_supported; /* with-defaults 'also-supported' parm */ dlq_hdr_t capQ; /* queue of non-std caps */ } cap_list_t; /* array of this structure for list of standard capabilities */ typedef struct cap_stdrec_t_ { cap_stdid_t cap_idnum; uint32 cap_bitnum; const xmlChar *cap_name; } cap_stdrec_t; /* queue of this structure for list of enterprise capabilities */ typedef struct cap_rec_t_ { dlq_hdr_t cap_qhdr; cap_subjtyp_t cap_subject; xmlChar *cap_uri; xmlChar *cap_namespace; xmlChar *cap_module; xmlChar *cap_revision; ncx_list_t cap_feature_list; ncx_list_t cap_deviation_list; } cap_rec_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION cap_new_caplist * * Malloc and initialize the fields in a cap_list_t struct * * INPUTS: * none * RETURNS: * malloced cap_list or NULL if memory error *********************************************************************/ extern cap_list_t * cap_new_caplist (void); /******************************************************************** * FUNCTION cap_init_caplist * * Initialize the fields in a pre-allocated cap_list_t struct * memory for caplist already allocated -- this just inits fields * * INPUTS: * caplist == struct to initialize * RETURNS: * status, should always be NO_ERR *********************************************************************/ extern void cap_init_caplist (cap_list_t *caplist); /******************************************************************** * FUNCTION cap_clean_caplist * * Clean the fields in a pre-allocated cap_list_t struct * Memory for caplist not deallocated -- this just cleans fields * * INPUTS: * caplist == struct to clean * RETURNS: * none, silent programming errors ignored *********************************************************************/ extern void cap_clean_caplist (cap_list_t *caplist); /******************************************************************** * FUNCTION cap_free_caplist * * Clean the fields in a pre-allocated cap_list_t struct * Then free the caplist memory * * INPUTS: * caplist == struct to free * *********************************************************************/ extern void cap_free_caplist (cap_list_t *caplist); /******************************************************************** * FUNCTION cap_add_std * * Add a standard protocol capability to the list * * INPUTS: * caplist == capability list that will contain the standard cap * capstd == the standard capability ID * RETURNS: * status, should always be NO_ERR *********************************************************************/ extern status_t cap_add_std (cap_list_t *caplist, cap_stdid_t capstd); /******************************************************************** * FUNCTION cap_add_stdval * * Add a standard protocol capability to the list (val_value_t version) * * INPUTS: * caplist == capability list that will contain the standard cap * capstd == the standard capability ID * OUTPUTS: * status *********************************************************************/ extern status_t cap_add_stdval (val_value_t *caplist, cap_stdid_t capstd); /******************************************************************** * FUNCTION cap_add_std_string * * Add a standard protocol capability to the list by URI string * * INPUTS: * caplist == capability list that will contain the standard cap * uri == the string holding the capability URI * * RETURNS: * status, NO_ERR if valid STD capability * ERR_NCX_SKIPPED if this is not a standard capability * any other result is a non-recoverable error *********************************************************************/ extern status_t cap_add_std_string (cap_list_t *caplist, const xmlChar *uri); /******************************************************************** * FUNCTION cap_add_module_string * * Add a standard protocol capability to the list by URI string * * INPUTS: * caplist == capability list that will contain the standard cap * uri == the URI string holding the capability identifier * * RETURNS: * status, NO_ERR if valid STD capability * ERR_NCX_SKIPPED if this is not a module capability * any other result is a non-recoverable error *********************************************************************/ extern status_t cap_add_module_string (cap_list_t *caplist, const xmlChar *uri); /******************************************************************** * FUNCTION cap_add_url * * Add the #url capability to the list * * INPUTS: * caplist == capability list that will contain the standard cap * scheme_list == the scheme list for the :url capability * * RETURNS: * status, should always be NO_ERR *********************************************************************/ extern status_t cap_add_url (cap_list_t *caplist, const xmlChar *scheme_list); /******************************************************************** * FUNCTION cap_add_urlval * * Add the :url capability to the list * value struct version * * INPUTS: * caplist == capability list that will contain the standard cap * scheme_list == the list of schemes supported * * OUTPUTS: * status *********************************************************************/ extern status_t cap_add_urlval (val_value_t *caplist, const xmlChar *scheme_list); /******************************************************************** * FUNCTION cap_add_withdef * * Add the :with-defaults capability to the list * * INPUTS: * caplist == capability list that will contain the standard cap * defstyle == the basic-mode with-default style * * RETURNS: * status, should always be NO_ERR *********************************************************************/ extern status_t cap_add_withdef (cap_list_t *caplist, const xmlChar *defstyle); /******************************************************************** * FUNCTION cap_add_withdefval * * Add the :with-defaults capability to t`he list * value struct version * * INPUTS: * caplist == capability list that will contain the standard cap * defstyle == the basic-mode with-default style * * OUTPUTS: * status *********************************************************************/ extern status_t cap_add_withdefval (val_value_t *caplist, const xmlChar *defstyle); /******************************************************************** * FUNCTION cap_add_yang_library_val * * Add the :yang-library:1.0 capability to the list * value struct version * * INPUTS: * caplist == capability list that will contain the standard cap * revision == the basic-mode with-default style * module_set_id == the module_set_id hash * * OUTPUTS: * status *********************************************************************/ extern status_t cap_add_yang_library_val (val_value_t *caplist, const xmlChar *revision, const xmlChar *module_set_id); /******************************************************************** * FUNCTION cap_update_yang_library_val * * Update the :yang-library:1.0 capability to the list * value struct version * * INPUTS: * caplist == capability list that will contain the standard cap * revision == the basic-mode with-default style * module_set_id == the module_set_id hash * * OUTPUTS: * status *********************************************************************/ extern status_t cap_update_yang_library_val (val_value_t *caplist, const xmlChar *revision, const xmlChar *module_set_id); /******************************************************************** * FUNCTION cap_add_ent * * Add an enterprise capability to the list * * INPUTS: * caplist == capability list that will contain the module caps * uristr == URI string to add * * RETURNS: * status *********************************************************************/ extern status_t cap_add_ent (cap_list_t *caplist, const xmlChar *uristr); /******************************************************************** * FUNCTION cap_add_modval * * Add a module capability to the list (val_value_t version) * * INPUTS: * caplist == capability list that will contain the enterprise cap * mod == module to add * * RETURNS: * status *********************************************************************/ extern status_t cap_add_modval (val_value_t *caplist, ncx_module_t *mod); /******************************************************************** * FUNCTION cap_add_devmodval * * Add a deviation module capability to the list (val_value_t version) * * INPUTS: * caplist == capability list that will contain the enterprise cap * savedev == save_deviations struct to use * * RETURNS: * status *********************************************************************/ extern status_t cap_add_devmodval (val_value_t *caplist, ncx_save_deviations_t *savedev); /******************************************************************** * FUNCTION cap_std_set * * fast search of standard protocol capability set * * INPUTS: * caplist == capability list to check * capstd == the standard capability ID * RETURNS: * TRUE if indicated std capability is set, FALSE if not *********************************************************************/ extern boolean cap_std_set (const cap_list_t *caplist, cap_stdid_t capstd); /******************************************************************** * FUNCTION cap_set * * linear search of capability list, will check for std uris as well * * INPUTS: * caplist == capability list to check * capuri == the capability URI to find * RETURNS: * TRUE if indicated capability is set, FALSE if not *********************************************************************/ extern boolean cap_set (const cap_list_t *caplist, const xmlChar *capuri); /******************************************************************** * FUNCTION cap_get_protos * * get the #url capability protocols list if it exists * get the protocols field for the :url capability * * INPUTS: * caplist == capability list to check * RETURNS: * pointer to protocols string if any, or NULL if not *********************************************************************/ extern const xmlChar * cap_get_protos (cap_list_t *caplist); /******************************************************************** * FUNCTION cap_dump_stdcaps * * debug function * Printf the standard protocol capabilities list * * INPUTS: * caplist == capability list to print * *********************************************************************/ extern void cap_dump_stdcaps (const cap_list_t *caplist); /******************************************************************** * FUNCTION cap_dump_modcaps * * Printf the standard data model module capabilities list * debug function * * INPUTS: * caplist == capability list to print *********************************************************************/ extern void cap_dump_modcaps (const cap_list_t *caplist); /******************************************************************** * FUNCTION cap_dump_entcaps * * Printf the enterprise capabilities list * debug function * * INPUTS: * caplist == capability list to print * *********************************************************************/ extern void cap_dump_entcaps (const cap_list_t *caplist); /******************************************************************** * FUNCTION cap_first_modcap * * Get the first module capability in the list * * INPUTS: * caplist == capability list to check * * RETURNS: * pointer to first record, to use for next record * NULL if no first record *********************************************************************/ extern cap_rec_t * cap_first_modcap (cap_list_t *caplist); /******************************************************************** * FUNCTION cap_next_modcap * * Get the next module capability in the list * * INPUTS: * curcap == current mod_cap entry * * RETURNS: * pointer to next record * NULL if no next record *********************************************************************/ extern cap_rec_t * cap_next_modcap (cap_rec_t *curcap); /******************************************************************** * FUNCTION cap_split_modcap * * Split the modcap string into 3 parts * * INPUTS: * cap == capability rec to parse * module == address of return module name * revision == address of return module revision date string * namespacestr == address of return module namespace * * OUTPUTS: * *module == return module name * *revision == return module revision date string * *namespacestr == return module namepsace * * RETURNS: * status *********************************************************************/ extern void cap_split_modcap (cap_rec_t *cap, const xmlChar **module, const xmlChar **revision, const xmlChar **namespacestr); /******************************************************************** * FUNCTION cap_make_moduri * * Malloc and construct a module URI for the specified module * make the module URI string (for sysCapabilityChange event) * * INPUTS: * mod == module to use * * RETURNS: * malloced string containing the URI !!! must be freed later !!! *********************************************************************/ extern xmlChar * cap_make_moduri (ncx_module_t *mod); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_cap */ yuma123_2.14/netconf/src/ncx/cap.c0000664000175000017500000016541014770023131017054 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: cap.c The capabilities module constructs two versions/ The cap_list_t version is the internal struct used by the agent or manager. The val_value_t version is used to cache the capabilities that will actually be used within an rpc_msg_t when the manager or agent actually sends a hello message. Debugging and schema-discovery module support is also provided. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 28apr05 abb begun 20sep07 abb add support for schema-discovery data model ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "cap.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "ncxmod.h" #include "ncx_feature.h" #include "ncx_list.h" #include "ncxconst.h" #include "ncxtypes.h" #include "status.h" #include "xmlns.h" #include "xml_util.h" #include "xml_val.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* controls extra attributes in the element */ /* #define USE_EXTENDED_HELLO 0 */ #define URL_START (const xmlChar *)"xsd/" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /* hardwired list of NETCONF v1.0 capabilities */ static cap_stdrec_t stdcaps[] = { { CAP_STDID_V1, CAP_BIT_V1, CAP_NAME_V1 }, { CAP_STDID_WRITE_RUNNING, CAP_BIT_WR_RUN, CAP_NAME_WR_RUN }, { CAP_STDID_CANDIDATE, CAP_BIT_CANDIDATE, CAP_NAME_CANDIDATE }, { CAP_STDID_CONF_COMMIT, CAP_BIT_CONF_COMMIT, CAP_NAME_CONF_COMMIT }, { CAP_STDID_ROLLBACK_ERR, CAP_BIT_ROLLBACK_ERR, CAP_NAME_ROLLBACK_ERR }, { CAP_STDID_VALIDATE, CAP_BIT_VALIDATE, CAP_NAME_VALIDATE }, { CAP_STDID_STARTUP, CAP_BIT_STARTUP, CAP_NAME_STARTUP }, { CAP_STDID_URL, CAP_BIT_URL, CAP_NAME_URL }, { CAP_STDID_XPATH, CAP_BIT_XPATH, CAP_NAME_XPATH }, { CAP_STDID_NOTIFICATION, CAP_BIT_NOTIFICATION, CAP_NAME_NOTIFICATION }, { CAP_STDID_INTERLEAVE, CAP_BIT_INTERLEAVE, CAP_NAME_INTERLEAVE }, { CAP_STDID_PARTIAL_LOCK, CAP_BIT_PARTIAL_LOCK, CAP_NAME_PARTIAL_LOCK }, { CAP_STDID_WITH_DEFAULTS, CAP_BIT_WITH_DEFAULTS, CAP_NAME_WITH_DEFAULTS }, { CAP_STDID_V11, CAP_BIT_V11, CAP_NAME_V11 }, { CAP_STDID_VALIDATE11, CAP_BIT_VALIDATE11, CAP_NAME_VALIDATE11 }, { CAP_STDID_CONF_COMMIT11, CAP_BIT_CONF_COMMIT11, CAP_NAME_CONF_COMMIT11 }, { CAP_STDID_YANG_LIBRARY, CAP_BIT_YANG_LIBRARY, CAP_NAME_YANG_LIBRARY }, { CAP_STDID_LAST_MARKER, 0x0, (const xmlChar *)"" } /* end-of-list marker */ }; /******************************************************************** * FUNCTION new_cap * * Malloc and init a new cap_rec_t struct * * RETURNS: * malloced cap rec or NULL if error *********************************************************************/ static cap_rec_t * new_cap (void) { cap_rec_t *cap; cap = m__getObj(cap_rec_t); if (!cap) { return NULL; } memset(cap, 0x0, sizeof(cap_rec_t)); ncx_init_list(&cap->cap_feature_list, NCX_BT_STRING); ncx_init_list(&cap->cap_deviation_list, NCX_BT_STRING); return cap; } /* new_cap */ /******************************************************************** * FUNCTION free_cap * * Clean and free the fields in a pre-allocated cap_rec_t struct * * INPUTS: * cap == struct to free * RETURNS: * none, silent programming errors ignored *********************************************************************/ static void free_cap (cap_rec_t *cap) { if (cap->cap_uri) { m__free(cap->cap_uri); } if (cap->cap_namespace) { m__free(cap->cap_namespace); } if (cap->cap_module) { m__free(cap->cap_module); } if (cap->cap_revision) { m__free(cap->cap_revision); } ncx_clean_list(&cap->cap_feature_list); ncx_clean_list(&cap->cap_deviation_list); m__free(cap); } /* free_cap */ /******************************************************************** * FUNCTION get_features_len * * Get the name length of each enabled feature * * INPUTS: * mod == original module * feature == feature found * cookie == original cookie * * RETURNS: * TRUE if processing should continue, FALSE if done *********************************************************************/ static boolean get_features_len (const ncx_module_t *mod, ncx_feature_t *feature, void *cookie) { uint32 *count; (void)mod; count = (uint32 *)cookie; *count += (xml_strlen(feature->name) + 1); return TRUE; } /* get_features_len */ /******************************************************************** * FUNCTION add_features * * Add a name, string for each enabled feature * * INPUTS: * mod == original module * feature == feature found * cookie == original cookie * * RETURNS: * TRUE if processing should continue, FALSE if done *********************************************************************/ static boolean add_features (const ncx_module_t *mod, ncx_feature_t *feature, void *cookie) { xmlChar **str; (void)mod; str = (xmlChar **)cookie; *str += xml_strcpy(*str, feature->name); **str = (xmlChar)','; (*str)++; return TRUE; } /* add_features */ /******************************************************************** * FUNCTION make_mod_urn_ex * * Construct and malloc a module capability URN string * * From yang-07: section 5.6.4: The namespace URI is advertised as a capability in the NETCONF message to indicate support for the YANG module by a NETCONF server. The capability URI advertised MUST be on the form: capability-string = namespace-uri [ parameter-list ] parameter-list = "?" parameter *( "&" parameter ) parameter = revision-parameter / module-parameter / feature-parameter / deviation-parameter revision-parameter = "revision=" revision-date module-parameter = "module=" module-name feature-parameter = "features=" feature *( "," feature ) deviation-parameter = "deviations=" deviation *( "," deviation ) Where "revision-date" is the revision of the module (see Section 7.1.9) that the NETCONF server implements, "module-name" is the name of module as it appears in the "module" statement (see Section 7.1), "namespace-uri" is the namespace for the module as it appears in the "namespace" statement, "feature" is the name of an optional feature implemented by the device (see Section 7.18.1), and "deviation" is the name of a module defining device deviations (see Section 7.18.3). In the parameter list, each named parameter MUST occur at most once. * INPUTS: * mod == module to use * * RETURNS: * status *********************************************************************/ static xmlChar * make_mod_urn_ex (const xmlChar *modname, const xmlChar *revision, const xmlChar *namespace, ncx_module_t *mod) { xmlChar *str, *p; ncx_save_deviations_t *devmod; uint32 len, feature_count, deviation_count; feature_count = 0; deviation_count = 0; /* get the length of the string needed by doing a dry run */ len = xml_strlen(namespace); len++; /* '?' char */ len += xml_strlen(CAP_MODULE_EQ); len += xml_strlen(modname); if (revision) { len++; /* & char */ len += xml_strlen(CAP_REVISION_EQ); len += xml_strlen(revision); } if (mod) { feature_count = ncx_feature_count(mod, TRUE); if (feature_count > 0) { len++; /* & char */ len += xml_strlen(CAP_FEATURES_EQ); len += (feature_count-1); /* all the commas */ /* add in all the enabled features length */ ncx_for_all_features(mod, get_features_len, &len, TRUE); } deviation_count = dlq_count(&mod->devmodlist); if (deviation_count > 0) { len++; /* & char */ len += xml_strlen(CAP_DEVIATIONS_EQ); len += (deviation_count-1); /* all the commas */ for (devmod = (ncx_save_deviations_t *)dlq_firstEntry(&mod->devmodlist); devmod != NULL; devmod = (ncx_save_deviations_t *)dlq_nextEntry(devmod)) { len += xml_strlen(devmod->devmodule); } } } /* get a string to hold the result */ str = m__getMem(len+1); if (!str) { return NULL; } /* repeat the previous steps for real */ p = str; p += xml_strcpy(p, namespace); *p++ = (xmlChar)'?'; p += xml_strcpy(p, CAP_MODULE_EQ); p += xml_strcpy(p, modname); if (revision) { *p++ = (xmlChar)'&'; p += xml_strcpy(p, CAP_REVISION_EQ); p += xml_strcpy(p, revision); } if (mod) { if (feature_count > 0) { *p++ = (xmlChar)'&'; p += xml_strcpy(p, CAP_FEATURES_EQ); /* add in all the enabled 'feature-name,' strings */ ncx_for_all_features(mod, add_features, &p, TRUE); /* the last entry will have a comma that has to be removed */ *--p = 0; } if (deviation_count > 0) { *p++ = (xmlChar)'&'; p += xml_strcpy(p, CAP_DEVIATIONS_EQ); for (devmod = (ncx_save_deviations_t *)dlq_firstEntry(&mod->devmodlist); devmod != NULL; devmod = (ncx_save_deviations_t *)dlq_nextEntry(devmod)) { p += xml_strcpy(p, devmod->devmodule); *p++ = ','; } /* the last entry will have a comma that has to be removed */ *--p = 0; } } return str; } /* make_mod_urn_ex */ /******************************************************************** * FUNCTION make_mod_urn * * Construct and malloc a module capability URN string * * INPUTS: * mod == module to use * * RETURNS: * status *********************************************************************/ static xmlChar * make_mod_urn (ncx_module_t *mod) { return make_mod_urn_ex(mod->name, mod->version, mod->ns, mod); } /* make_mod_urn */ /******************************************************************** * FUNCTION make_devmod_urn * * Construct and malloc a module capability URN string * for a deviations module, from a save deviations struct * These are the same as regular modules externally, * but internally, they are not fully parsed. * * If the deviations module contains any YANG statements * other than deviation-stmts, then it MUST be loaded * into the system with ncxmod_load_module. * If so, then a regular module capability URI MUST already * be generated for that module INSTEAD of this function * * INPUTS: * savedev == save deviations struct to use * * RETURNS: * status *********************************************************************/ static xmlChar * make_devmod_urn (ncx_save_deviations_t *savedev) { return make_mod_urn_ex(savedev->devmodule, savedev->devrevision, savedev->devnamespace, NULL); } /* make_devmod_urn */ /******************************************************************** * FUNCTION parse_uri_parm * * Parse a read-only substring as a foo=bar parameter * Setup the next string after that is done * * INPUTS: * parmname = start of the sub-string, stopped on * what should be the start of a parameter * parmnamelen == address of parm name return length * parmval == address of return parmval pointer * parmvallen == address of return parmval length * nextparmname == address of return pointer to next parmname * * RETURNS: * status, NO_ERR if valid parm-pair parsed *********************************************************************/ static status_t parse_uri_parm (const xmlChar *parmname, uint32 *parmnamelen, const xmlChar **parmval, uint32 *parmvallen, const xmlChar **nextparmname) { const xmlChar *str, *equal; *parmnamelen = 0; *parmval = NULL; *parmvallen = 0; *nextparmname = NULL; /* find the equals sign after the parameter name */ equal = parmname; while (*equal && *equal != (xmlChar)'=') { equal++; } if (!*equal || equal == parmname) { /* error: skip to next parm or EOS */ while (*equal && *equal != (xmlChar)'&') { equal++; } if (*equal) { /* stopped on ampersand */ *nextparmname = ++equal; } return ERR_NCX_INVALID_VALUE; } /* OK: got an equals sign after some non-zero string */ *parmnamelen = (uint32)(equal - parmname); *parmval = str = equal+1; while (*str && *str != (xmlChar)'&') { str++; } *parmvallen = (uint32)(str - *parmval); if (*str) { /* stopped on ampersand so another param is expected */ *nextparmname = str+1; } return NO_ERR; } /* parse_uri_parm */ /************** E X T E R N A L F U N C T I O N S ************/ /******************************************************************** * FUNCTION cap_new_caplist * * Malloc and initialize the fields in a cap_list_t struct * * INPUTS: * none * RETURNS: * malloced cap_list or NULL if memory error *********************************************************************/ cap_list_t * cap_new_caplist (void) { cap_list_t *caplist; caplist = m__getObj(cap_list_t); if (caplist) { cap_init_caplist(caplist); } return caplist; } /* cap_new_caplist */ /******************************************************************** * FUNCTION cap_init_caplist * * Initialize the fields in a pre-allocated cap_list_t struct * memory for caplist already allocated -- this just inits fields * * INPUTS: * caplist == struct to initialize * RETURNS: * status, should always be NO_ERR *********************************************************************/ void cap_init_caplist (cap_list_t *caplist) { #ifdef DEBUG if (!caplist) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif memset(caplist, 0x0, sizeof(cap_list_t)); dlq_createSQue(&caplist->capQ); } /* cap_init_caplist */ /******************************************************************** * FUNCTION cap_clean_caplist * * Clean the fields in a pre-allocated cap_list_t struct * Memory for caplist not deallocated -- this just cleans fields * * INPUTS: * caplist == struct to clean * RETURNS: * none, silent programming errors ignored *********************************************************************/ void cap_clean_caplist (cap_list_t *caplist) { cap_rec_t *cap; #ifdef DEBUG if (!caplist) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif caplist->cap_std = 0; if (caplist->cap_schemes) { m__free(caplist->cap_schemes); caplist->cap_schemes = NULL; } if (caplist->cap_defstyle) { m__free(caplist->cap_defstyle); caplist->cap_defstyle = NULL; } if (caplist->cap_supported) { m__free(caplist->cap_supported); caplist->cap_supported = NULL; } /* drain the capability Q and free the memory */ cap = (cap_rec_t *)dlq_deque(&caplist->capQ); while (cap != NULL) { free_cap(cap); cap = (cap_rec_t *)dlq_deque(&caplist->capQ); } } /* cap_clean_caplist */ /******************************************************************** * FUNCTION cap_free_caplist * * Clean the fields in a pre-allocated cap_list_t struct * Then free the caplist memory * * INPUTS: * caplist == struct to free * *********************************************************************/ void cap_free_caplist (cap_list_t *caplist) { #ifdef DEBUG if (!caplist) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif cap_clean_caplist(caplist); m__free(caplist); } /* cap_free_caplist */ /******************************************************************** * FUNCTION cap_add_std * * Add a standard protocol capability to the list * * INPUTS: * caplist == capability list that will contain the standard cap * capstd == the standard capability ID * RETURNS: * status, should always be NO_ERR *********************************************************************/ status_t cap_add_std (cap_list_t *caplist, cap_stdid_t capstd) { #ifdef DEBUG if (!caplist) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (capstd < CAP_STDID_LAST_MARKER) { m__setbit(caplist->cap_std, stdcaps[capstd].cap_bitnum); return NO_ERR; } else { return ERR_NCX_WRONG_VAL; } } /* cap_add_std */ /******************************************************************** * FUNCTION cap_add_stdval * * Add a standard protocol capability to the list (val_value_t version) * * INPUTS: * caplist == capability list that will contain the standard cap * capstd == the standard capability ID * OUTPUTS: * status *********************************************************************/ status_t cap_add_stdval (val_value_t *caplist, cap_stdid_t capstd) { val_value_t *capval; xmlChar *str, *p; const xmlChar *pfix, *cap; uint32 len; #ifdef DEBUG if (!caplist || capstd >= CAP_STDID_LAST_MARKER) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* setup the string */ if (capstd==CAP_STDID_V1) { pfix = CAP_BASE_URN; cap = NULL; len = xml_strlen(pfix); } else if (capstd==CAP_STDID_V11) { pfix = CAP_BASE_URN11; cap = NULL; len = xml_strlen(pfix); } else { pfix = CAP_URN; cap = stdcaps[capstd].cap_name; len = xml_strlen(pfix) + xml_strlen(cap); } /* make the string */ str = m__getMem(len+1); if (!str) { return ERR_INTERNAL_MEM; } /* concat the capability name if not the base string */ p = str; p += xml_strcpy(str, pfix); if (cap) { xml_strcpy(p, cap); } /* make the capability element */ capval = xml_val_new_string(NCX_EL_CAPABILITY, xmlns_nc_id(), str); if (!capval) { m__free(str); return ERR_INTERNAL_MEM; } val_add_child(capval, caplist); return NO_ERR; } /* cap_add_stdval */ /******************************************************************** * FUNCTION cap_add_std_string * * Add a standard protocol capability to the list by URI string * * INPUTS: * caplist == capability list that will contain the standard cap * uri == the string holding the capability URI * * RETURNS: * status, NO_ERR if valid STD capability * ERR_NCX_SKIPPED if this is not a standard capability * any other result is a non-recoverable error *********************************************************************/ status_t cap_add_std_string (cap_list_t *caplist, const xmlChar *uri) { const xmlChar *str; uint32 caplen, namelen, schemelen, basiclen; cap_stdid_t stdid; status_t res; #ifdef DEBUG if (!caplist || !uri) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* the base:1.0 capability is a different form than the rest */ if (!xml_strcmp(uri, CAP_BASE_URN)) { return cap_add_std(caplist, CAP_STDID_V1); } /* the base:1.1 capability is a different form than the rest */ if (!xml_strcmp(uri, CAP_BASE_URN11)) { return cap_add_std(caplist, CAP_STDID_V11); } /* hack: support juniper servers which send the NETCONF * XML namespace instead of the NETCONF base URI * as the protocol indicator */ if (!xml_strcmp(uri, NC_URN)) { return cap_add_std(caplist, CAP_STDID_V1); } caplen = xml_strlen(CAP_URN); if (!xml_strncmp(uri, CAP_URN, caplen)) { /* matched the standard capability prefix string; * get the suffix with the capability name and version */ str = uri + caplen; } else { caplen = xml_strlen(CAP_J_URN); if (!xml_strncmp(uri, CAP_J_URN, caplen)) { /* matched the juniper standard capability prefix string; * get the suffix with the capability name and version */ str = uri + caplen; } else { return ERR_NCX_SKIPPED; } } /* go through the standard capability suffix strings */ for (stdid=CAP_STDID_WRITE_RUNNING; stdid < CAP_STDID_LAST_MARKER; stdid++) { switch (stdid) { case CAP_STDID_URL: namelen = xml_strlen(stdcaps[stdid].cap_name); if (!xml_strncmp(str, stdcaps[stdid].cap_name, namelen)) { str += namelen; if (*str == (xmlChar)'?') { str++; /* first check standard scheme= string */ schemelen = xml_strlen(CAP_SCHEME_EQ); if (!xml_strncmp(str, CAP_SCHEME_EQ, schemelen)) { str += schemelen; if (*str) { return cap_add_url(caplist, str); } } else { /* check juniper bug: protocol= */ schemelen = xml_strlen(CAP_PROTOCOL_EQ); if (!xml_strncmp(str, CAP_PROTOCOL_EQ, schemelen)) { str += schemelen; if (*str) { return cap_add_url(caplist, str); } } } } } break; case CAP_STDID_WITH_DEFAULTS: namelen = xml_strlen(stdcaps[stdid].cap_name); if (!xml_strncmp(str, stdcaps[stdid].cap_name, namelen)) { str += namelen; if (*str == (xmlChar)'?') { str++; basiclen = xml_strlen(CAP_BASIC_EQ); if (!xml_strncmp(str, CAP_BASIC_EQ, basiclen)) { str += basiclen; if (*str) { return cap_add_withdef(caplist, str); } } } } break; case CAP_STDID_YANG_LIBRARY: namelen = xml_strlen(stdcaps[stdid].cap_name); if (!xml_strncmp(str, stdcaps[stdid].cap_name, namelen)) { char* params_str; char* revision_str; char* module_set_id_str; int i; /* parsing ?revision=2016-06-21&module-set-id=.. */ str += namelen; params_str=strdup(str); revision_str = strstr(params_str,CAP_REVISION_EQ); module_set_id_str = strstr(params_str,CAP_MODULE_SET_ID_EQ); for(i=0;params_str[i]!=0;i++) { if(params_str[i]=='&') params_str[i]=0; } if(params_str[0]!='?' || revision_str==NULL || module_set_id_str==NULL || strlen(revision_str)!=(strlen(CAP_REVISION_EQ)+strlen("2017-07-28")) || strlen(module_set_id_str)<=strlen(CAP_MODULE_SET_ID_EQ) ) { log_warn("Warning: Bad yang-library:1.0 capability uri: %s\n", uri); } res=cap_add_std(caplist, stdid); free(params_str); return res; } break; default: if (!xml_strcmp(str, stdcaps[stdid].cap_name)) { return cap_add_std(caplist, stdid); } } } return ERR_NCX_SKIPPED; } /* cap_add_std_string */ /******************************************************************** * FUNCTION cap_add_module_string * * Add a standard protocol capability to the list by URI string * * INPUTS: * caplist == capability list that will contain the standard cap * uri == the URI string holding the capability identifier * * RETURNS: * status, NO_ERR if valid STD capability * ERR_NCX_SKIPPED if this is not a module capability * any other result is a non-recoverable error *********************************************************************/ status_t cap_add_module_string (cap_list_t *caplist, const xmlChar *uri) { cap_rec_t *cap; const xmlChar *qmark, *parmname, *parmval, *nextparmname; const xmlChar *module, *revision, *features, *deviations; xmlChar *liststr, *commastr; uint32 parmnamelen, parmvallen, baselen, i; uint32 modulelen, revisionlen, featureslen, deviationslen; boolean curmod, currev, curfeat, curdev, done, usewarning; status_t res; xmlns_id_t foundnsid; #ifdef DEBUG if (!caplist || !uri) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* look for the end of the URI, for any parameters */ qmark = uri; while (*qmark && *qmark != (xmlChar)'?') { qmark++; } if (!*qmark) { return ERR_NCX_SKIPPED; } baselen = (uint32)(qmark-uri); res = NO_ERR; module = NULL; revision = NULL; features = NULL; deviations = NULL; modulelen = 0; revisionlen = 0; deviationslen = 0; featureslen = 0; curmod = FALSE; currev = FALSE; curfeat = FALSE; curdev = FALSE; parmnamelen = 0; usewarning = ncx_warning_enabled(ERR_NCX_RCV_INVALID_MODCAP); /* lookup this namespace to see if it is already loaded */ foundnsid = xmlns_find_ns_by_name_str(uri, baselen); /* setup the start of the parameter name for each loop iteration */ parmname = qmark + 1; parmval = NULL; nextparmname = parmname; done = FALSE; while (!done) { if (nextparmname) { parmname = nextparmname; } else { done = TRUE; continue; } res = parse_uri_parm(parmname, &parmnamelen, &parmval, &parmvallen, &nextparmname); if (res != NO_ERR) { if (usewarning) { log_warn("\nWarning: skipping invalid " "parameter syntax (%s) " "in capability URI '%s'", get_error_string(res), uri); } if (NEED_EXIT(res)) { done = TRUE; } continue; } /* check that the parameter name matches one of the expected names */ if (!xml_strncmp(parmname, YANG_K_MODULE, xml_strlen(YANG_K_MODULE))) { if (curmod) { if (usewarning) { log_warn("\nWarning: skipping duplicate " "'module' parameter " "in capability URI '%s'", uri); } } else { curmod = TRUE; module = parmval; modulelen = parmvallen; } } else if (!xml_strncmp(parmname, YANG_K_REVISION, xml_strlen(YANG_K_REVISION))) { if (currev) { if (usewarning) { log_warn("\nWarning: skipping duplicate " "'revision' parameter " "in capability URI '%s'", uri); } } else { currev = TRUE; revision = parmval; revisionlen = parmvallen; } } else if (!xml_strncmp(parmname, YANG_K_FEATURES, xml_strlen(YANG_K_FEATURES))) { if (curfeat) { if (usewarning) { log_warn("\nWarning: skipping duplicate " "'features' parameter " "in capability URI '%s'", uri); } } else { curfeat = TRUE; features = parmval; featureslen = parmvallen; } } else if (!xml_strncmp(parmname, YANG_K_DEVIATIONS, xml_strlen(YANG_K_DEVIATIONS))) { if (curdev) { if (usewarning) { log_warn("\nWarning: skipping duplicate " "'deviations' parameter " "in capability URI '%s'", uri); } } else { curdev = TRUE; deviations = parmval; deviationslen = parmvallen; } } else if (usewarning) { /* skip over this unknown parameter */ log_warn("\nWarning: skipping unknown parameter '"); for (i=0; icap_uri = xml_strdup(uri); if (!cap->cap_uri) { free_cap(cap); return ERR_INTERNAL_MEM; } cap->cap_namespace = xml_strndup(uri, baselen); if (!cap->cap_namespace) { free_cap(cap); return ERR_INTERNAL_MEM; } if (module != NULL && modulelen > 0) { cap->cap_module = xml_strndup(module, modulelen); if (!cap->cap_module) { free_cap(cap); return ERR_INTERNAL_MEM; } } /* check wrong module namespace base URI */ if (foundnsid) { parmname = xmlns_get_module(foundnsid); if (cap->cap_module != NULL && xml_strcmp(parmname, cap->cap_module)) { if (usewarning) { if(!(0==strcmp(cap->cap_module, NCXMOD_IETF_NETCONF) && 0==strcmp(parmname,NCXMOD_NETCONF))) { log_warn("\nWarning: capability base URI mismatch, " "got '%s' not '%s''", cap->cap_module, parmname); } } } } if (revision) { cap->cap_revision = xml_strndup(revision, revisionlen); if (!cap->cap_revision) { free_cap(cap); return ERR_INTERNAL_MEM; } } else if (usewarning) { log_warn("\nWarning: 'revision' parameter missing from " "capability URI '%s'", uri); } if (features) { liststr = xml_strndup(features, featureslen); if (!liststr) { free_cap(cap); return ERR_INTERNAL_MEM; } commastr = liststr; while (*commastr) { if (*commastr == (xmlChar)',') { *commastr = (xmlChar)' '; } commastr++; } res = ncx_set_list(NCX_BT_STRING, liststr, &cap->cap_feature_list); m__free(liststr); if (res != NO_ERR) { free_cap(cap); return res; } } if (deviations) { liststr = xml_strndup(deviations, deviationslen); if (!liststr) { free_cap(cap); return ERR_INTERNAL_MEM; } commastr = liststr; while (*commastr) { if (*commastr == (xmlChar)',') { *commastr = (xmlChar)' '; } commastr++; } res = ncx_set_list(NCX_BT_STRING, liststr, &cap->cap_deviation_list); m__free(liststr); if (res != NO_ERR) { free_cap(cap); return res; } } cap->cap_subject = CAP_SUBJTYP_DM; dlq_enque(cap, &caplist->capQ); return NO_ERR; } /* cap_add_module_string */ /******************************************************************** * FUNCTION cap_add_url * * Add the #url capability to the list * * INPUTS: * caplist == capability list that will contain the standard cap * scheme_list == the protocol list for the :url capability * * RETURNS: * status, should always be NO_ERR *********************************************************************/ status_t cap_add_url (cap_list_t *caplist, const xmlChar *scheme_list) { #ifdef DEBUG if (!caplist || !scheme_list) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif m__setbit(caplist->cap_std, stdcaps[CAP_STDID_URL].cap_bitnum); caplist->cap_schemes = xml_strdup(scheme_list); if (!caplist->cap_schemes) { return ERR_INTERNAL_MEM; } return NO_ERR; } /* cap_add_url */ /******************************************************************** * FUNCTION cap_add_urlval * * Add the :url capability to the list * value struct version * * INPUTS: * caplist == capability list that will contain the standard cap * scheme_list == the list of schemes supported * * OUTPUTS: * status *********************************************************************/ status_t cap_add_urlval (val_value_t *caplist, const xmlChar *scheme_list) { val_value_t *capval; xmlChar *str, *p; const xmlChar *pfix, *cap; const xmlChar *scheme; uint32 len; #ifdef DEBUG if (!caplist || !scheme_list) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* setup the string * :url:1.0?scheme=file */ pfix = CAP_URN; scheme = CAP_SCHEME_EQ; cap = stdcaps[CAP_STDID_URL].cap_name; /* get the total length */ len = xml_strlen(pfix) + xml_strlen(cap) + 1 + xml_strlen(scheme) + xml_strlen(scheme_list) + 1; /* make the string */ str = m__getMem(len+1); if (!str) { return ERR_INTERNAL_MEM; } /* build the capability string */ p = str; p += xml_strcpy(p, pfix); p += xml_strcpy(p, cap); *p++ = '?'; p += xml_strcpy(p, scheme); xml_strcpy(p, scheme_list); /* make the capability element */ capval = xml_val_new_string(NCX_EL_CAPABILITY, xmlns_nc_id(), str); if (!capval) { m__free(str); return ERR_INTERNAL_MEM; } val_add_child(capval, caplist); return NO_ERR; } /* cap_add_urlval */ /******************************************************************** * FUNCTION cap_add_withdef * * Add the :with-defaults capability to the list * * INPUTS: * caplist == capability list that will contain the standard cap * defstyle == the basic-mode with-default style * * RETURNS: * status, should always be NO_ERR *********************************************************************/ status_t cap_add_withdef (cap_list_t *caplist, const xmlChar *defstyle) { const xmlChar *str; uint32 featureslen, basiclen; #ifdef DEBUG if (!caplist || !defstyle) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif m__setbit(caplist->cap_std, stdcaps[CAP_STDID_WITH_DEFAULTS].cap_bitnum); str = defstyle; while (*str && *str != '&') { str++; } if (*str) { featureslen = xml_strlen(CAP_SUPPORTED_EQ); basiclen = (uint32)(str - defstyle); if (!xml_strncmp(++str, CAP_SUPPORTED_EQ, featureslen)) { str += featureslen; caplist->cap_supported = xml_strdup(str); if (!caplist->cap_supported) { return ERR_INTERNAL_MEM; } caplist->cap_defstyle = xml_strndup(defstyle, basiclen); if (!caplist->cap_defstyle) { return ERR_INTERNAL_MEM; } } } else { caplist->cap_defstyle = xml_strdup(defstyle); if (!caplist->cap_defstyle) { return ERR_INTERNAL_MEM; } } return NO_ERR; } /* cap_add_withdef */ /******************************************************************** * FUNCTION cap_add_withdefval * * Add the :with-defaults capability to the list * value struct version * * INPUTS: * caplist == capability list that will contain the standard cap * defstyle == the basic-mode with-default style * * OUTPUTS: * status *********************************************************************/ status_t cap_add_withdefval (val_value_t *caplist, const xmlChar *defstyle) { #define SUPPORTED_BUFFSIZE 64 val_value_t *capval; xmlChar *str, *p; const xmlChar *pfix, *cap; const xmlChar *basic, *supported; uint32 len; ncx_withdefaults_t withdef; xmlChar buffer[SUPPORTED_BUFFSIZE]; #ifdef DEBUG if (!caplist || !defstyle) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* setup the string * :with-defaults:1.0?basic-mode= * &also-supported= */ pfix = CAP_URN; basic = CAP_BASIC_EQ; supported = CAP_SUPPORTED_EQ; cap = stdcaps[CAP_STDID_WITH_DEFAULTS].cap_name; /* figure out what the also-supported parameter value * should be and fill the buffer */ withdef = ncx_get_withdefaults_enum(defstyle); if (withdef == NCX_WITHDEF_NONE) { return SET_ERROR(ERR_INTERNAL_VAL); } str = buffer; switch (withdef) { case NCX_WITHDEF_REPORT_ALL: str += xml_strcpy(str, NCX_EL_TRIM); *str++ = ','; str += xml_strcpy(str, NCX_EL_EXPLICIT); *str++ = ','; xml_strcpy(str, NCX_EL_REPORT_ALL_TAGGED); break; case NCX_WITHDEF_TRIM: str += xml_strcpy(str, NCX_EL_EXPLICIT); *str++ = ','; str += xml_strcpy(str, NCX_EL_REPORT_ALL); *str++ = ','; xml_strcpy(str, NCX_EL_REPORT_ALL_TAGGED); break; case NCX_WITHDEF_EXPLICIT: str += xml_strcpy(str, NCX_EL_TRIM); *str++ = ','; str += xml_strcpy(str, NCX_EL_REPORT_ALL); *str++ = ','; xml_strcpy(str, NCX_EL_REPORT_ALL_TAGGED); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } /* get the total length */ len = xml_strlen(pfix) + xml_strlen(cap) + 1 + xml_strlen(basic) + xml_strlen(defstyle) + 1 + xml_strlen(supported) + xml_strlen(buffer); /* make the string */ str = m__getMem(len+1); if (!str) { return ERR_INTERNAL_MEM; } /* build the capability string */ p = str; p += xml_strcpy(p, pfix); p += xml_strcpy(p, cap); *p++ = '?'; p += xml_strcpy(p, basic); p += xml_strcpy(p, defstyle); *p++ = '&'; p += xml_strcpy(p, supported); xml_strcpy(p, buffer); /* make the capability element */ capval = xml_val_new_string(NCX_EL_CAPABILITY, xmlns_nc_id(), str); if (!capval) { m__free(str); return ERR_INTERNAL_MEM; } val_add_child(capval, caplist); return NO_ERR; } /* cap_add_withdefval */ /******************************************************************** * FUNCTION cap_add_yang_library_val * * Add the :yang-library:1.0 capability to the list * value struct version * * INPUTS: * caplist == capability list that will contain the standard cap * revision == the basic-mode with-default style * module_set_id == the module_set_id hash * * OUTPUTS: * status *********************************************************************/ status_t cap_add_yang_library_val (val_value_t *caplist, const xmlChar *revision, const xmlChar *module_set_id) { char* format = "urn:ietf:params:netconf:capability:yang-library:1.0?revision=%s&module-set-id=%s"; val_value_t *capval; char* buf; assert(caplist && revision && module_set_id); /* setup the string */ buf = (char*)malloc(strlen(format)+strlen(revision)+strlen(module_set_id)+1); assert(buf); sprintf(buf,format,revision,module_set_id); /* make the capability element */ capval = xml_val_new_string(NCX_EL_CAPABILITY, xmlns_nc_id(), buf); val_add_child(capval, caplist); return NO_ERR; } /* cap_add_yang_library_val */ /******************************************************************** * FUNCTION cap_update_yang_library_val * * Update the :yang-library:1.0 capability * value struct version * * INPUTS: * caplist == capability list that will contain the standard cap * revision == the basic-mode with-default style * module_set_id == the module_set_id hash * * OUTPUTS: * status *********************************************************************/ status_t cap_update_yang_library_val (val_value_t *caplist, const xmlChar *revision, const xmlChar *module_set_id) { char* prefix = "urn:ietf:params:netconf:capability:yang-library:1.0"; int prefixlen = strlen(prefix); val_value_t* childval; for (childval = val_get_first_child(caplist); childval != NULL; childval = val_get_next_child(childval)) { if(strlen(VAL_STRING(childval))cap_subject = CAP_SUBJTYP_OTHER; cap->cap_uri = xml_strdup(uristr); if (!cap->cap_uri) { free_cap(cap); return ERR_INTERNAL_MEM; } dlq_enque(cap, &caplist->capQ); return NO_ERR; } /* cap_add_ent */ /******************************************************************** * FUNCTION cap_add_modval * * Add a module capability to the list (val_value_t version) * * INPUTS: * caplist == capability list that will contain the enterprise cap * mod == module to add * * RETURNS: * status *********************************************************************/ status_t cap_add_modval (val_value_t *caplist, ncx_module_t *mod) { xmlChar *str; val_value_t *capval; #ifdef DEBUG if (!caplist || !mod) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!mod->name || !mod->ns || !mod->ismod) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif /* construct the module URN string */ str = make_mod_urn(mod); if (!str) { return ERR_INTERNAL_MEM; } if(0==strcmp(mod->name, NCXMOD_NETCONF)) { /* ietf-netconf is overloaded internally */ char* newstr; char* features_str; char* pre_features_str = "urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2011-06-01&"; features_str = strstr(str, "features="); assert(features_str); newstr = malloc(strlen(pre_features_str)+strlen(features_str)+1); assert(newstr); sprintf(newstr,"%s%s",pre_features_str, features_str); free(str); str = newstr; } /* make the capability element */ capval = xml_val_new_string(NCX_EL_CAPABILITY, xmlns_nc_id(), str); if (!capval) { m__free(str); return ERR_INTERNAL_MEM; } val_add_child(capval, caplist); return NO_ERR; } /* cap_add_modval */ /******************************************************************** * FUNCTION cap_add_devmodval * * Add a deviation module capability to the list (val_value_t version) * * INPUTS: * caplist == capability list that will contain the enterprise cap * savedev == save_deviations struct to use * * RETURNS: * status *********************************************************************/ status_t cap_add_devmodval (val_value_t *caplist, ncx_save_deviations_t *savedev) { xmlChar *str; val_value_t *capval; #ifdef DEBUG if (!caplist || !savedev) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!savedev->devmodule || !savedev->devnamespace) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif /* construct the module URN string */ str = make_devmod_urn(savedev); if (!str) { return ERR_INTERNAL_MEM; } /* make the capability element */ capval = xml_val_new_string(NCX_EL_CAPABILITY, xmlns_nc_id(), str); if (!capval) { m__free(str); return ERR_INTERNAL_MEM; } val_add_child(capval, caplist); return NO_ERR; } /* cap_add_devmodval */ /******************************************************************** * FUNCTION cap_std_set * * fast search of standard protocol capability set * * INPUTS: * caplist == capability list to check * capstd == the standard capability ID * RETURNS: * TRUE if indicated std capability is set, FALSE if not *********************************************************************/ boolean cap_std_set (const cap_list_t *caplist, cap_stdid_t capstd) { if (!caplist) { return FALSE; } if (capstd < CAP_STDID_LAST_MARKER) { return (m__bitset(caplist->cap_std, stdcaps[capstd].cap_bitnum)) ? TRUE : FALSE; } else { return FALSE; } } /* cap_std_set */ /******************************************************************** * FUNCTION cap_set * * linear search of capability list, will check for std uris as well * * INPUTS: * caplist == capability list to check * capuri == the capability URI to find * RETURNS: * TRUE if indicated capability is set, FALSE if not *********************************************************************/ boolean cap_set (const cap_list_t *caplist, const xmlChar *capuri) { const xmlChar *str; cap_rec_t *cap; int i; uint32 len, capurilen; if (!caplist || !capuri) { return FALSE; } capurilen = xml_strlen(capuri); /* check if this is the NETCONF V1 Base URN capability */ if (!xml_strcmp(capuri, NC_URN)) { return (m__bitset(caplist->cap_std, stdcaps[CAP_STDID_V1].cap_bitnum)) ? TRUE : FALSE; } /* check if this is a NETCONF standard capability */ len = xml_strlen(CAP_URN); if ((capurilen > len+1) && !xml_strncmp(capuri, (const xmlChar *)CAP_URN, len)) { /* set str to the 'capability-name:version-number' */ str = &capuri[len]; /* check all the capability names */ for (i=1; icap_std, stdcaps[i].cap_bitnum)) ? TRUE : FALSE; } } } /* check the enterprise capability queue * try exact match first */ for (cap=(cap_rec_t *)dlq_firstEntry(&caplist->capQ); cap != NULL; cap=(cap_rec_t *)dlq_nextEntry(cap)) { if (!xml_strcmp(cap->cap_uri, capuri)) { return TRUE; } } /* check the enterprise capability queue * match the beginning part, not requiring a complete match */ for (cap=(cap_rec_t *)dlq_firstEntry(&caplist->capQ); cap != NULL; cap=(cap_rec_t *)dlq_nextEntry(cap)) { if (!xml_strncmp(cap->cap_uri, capuri, capurilen)) { return TRUE; } } return FALSE; } /* cap_set */ /******************************************************************** * FUNCTION cap_get_protos * * get the #url capability protocols list if it exists * get the protocols field for the :url capability * * INPUTS: * caplist == capability list to check * RETURNS: * pointer to protocols string if any, or NULL if not *********************************************************************/ const xmlChar * cap_get_protos (cap_list_t *caplist) { #ifdef DEBUG if (!caplist) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (const xmlChar *)caplist->cap_schemes; } /* cap_get_protos */ /******************************************************************** * FUNCTION cap_dump_stdcaps * * debug function * Printf the standard protocol capabilities list * * INPUTS: * caplist == capability list to print * *********************************************************************/ void cap_dump_stdcaps (const cap_list_t *caplist) { cap_stdid_t capid; #ifdef DEBUG if (!caplist) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif for (capid = CAP_STDID_V1; capid < CAP_STDID_LAST_MARKER; capid++) { if (cap_std_set(caplist, capid)) { log_write("\n %s", stdcaps[capid].cap_name); } } } /* cap_dump_stdcaps */ /******************************************************************** * FUNCTION cap_dump_modcaps * * Printf the standard data model module capabilities list * debug function * * INPUTS: * caplist == capability list to print *********************************************************************/ void cap_dump_modcaps (const cap_list_t *caplist) { const cap_rec_t *cap; const ncx_lmem_t *lmem; boolean anycaps; #ifdef DEBUG if (!caplist) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif anycaps = FALSE; for (cap = (cap_rec_t *)dlq_firstEntry(&caplist->capQ); cap != NULL; cap = (cap_rec_t *)dlq_nextEntry(cap)) { if (cap->cap_subject != CAP_SUBJTYP_DM) { continue; } anycaps = TRUE; if (cap->cap_module && cap->cap_revision) { log_write("\n %s@%s", cap->cap_module, cap->cap_revision); } else if (cap->cap_revision) { log_write("\n ??@%s", cap->cap_revision); } else if (cap->cap_module) { log_write("\n %s", cap->cap_module); } else if (cap->cap_namespace) { log_write("\n %s", cap->cap_namespace); } else if (cap->cap_uri) { log_write("\n %s", cap->cap_uri); } else { log_write("\n ??"); } if (!dlq_empty(&cap->cap_feature_list.memQ)) { log_write("\n Features: "); for (lmem = (ncx_lmem_t *) dlq_firstEntry(&cap->cap_feature_list.memQ); lmem != NULL; lmem = (ncx_lmem_t *)dlq_nextEntry(lmem)) { log_write("\n %s ", lmem->val.str); } } if (!dlq_empty(&cap->cap_deviation_list.memQ)) { log_write("\n Deviations: "); for (lmem = (ncx_lmem_t *) dlq_firstEntry(&cap->cap_deviation_list.memQ); lmem != NULL; lmem = (ncx_lmem_t *)dlq_nextEntry(lmem)) { log_write("\n %s ", lmem->val.str); } } } if (!anycaps) { log_write("\n None"); } } /* cap_dump_modcaps */ /******************************************************************** * FUNCTION cap_dump_entcaps * * Printf the enterprise capabilities list * debug function * * INPUTS: * caplist == capability list to print * *********************************************************************/ void cap_dump_entcaps (const cap_list_t *caplist) { const cap_rec_t *cap; boolean anycaps; #ifdef DEBUG if (!caplist) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif anycaps = FALSE; for (cap = (cap_rec_t *)dlq_firstEntry(&caplist->capQ); cap != NULL; cap = (cap_rec_t *)dlq_nextEntry(cap)) { if (cap->cap_subject != CAP_SUBJTYP_DM) { anycaps = TRUE; log_write("\n %s", cap->cap_uri); } } if (!anycaps) { log_write("\n None"); } } /* cap_dump_entcaps */ /******************************************************************** * FUNCTION cap_first_modcap * * Get the first module capability in the list * * INPUTS: * caplist == capability list to check * * RETURNS: * pointer to first record, to use for next record * NULL if no first record *********************************************************************/ cap_rec_t * cap_first_modcap (cap_list_t *caplist) { #ifdef DEBUG if (!caplist) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (cap_rec_t *)dlq_firstEntry(&caplist->capQ); } /* cap_first_modcap */ /******************************************************************** * FUNCTION cap_next_modcap * * Get the next module capability in the list * * INPUTS: * curcap == current mod_cap entry * * RETURNS: * pointer to next record * NULL if no next record *********************************************************************/ cap_rec_t * cap_next_modcap (cap_rec_t *curcap) { #ifdef DEBUG if (!curcap) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (cap_rec_t *)dlq_nextEntry(curcap); } /* cap_next_modcap */ /******************************************************************** * FUNCTION cap_split_modcap * * Split the modcap string into 3 parts * * INPUTS: * cap == capability rec to parse * module == address of return module name * revision == address of return module revision date string * namespacestr == address of return module namespace * * OUTPUTS: * *module == return module name * *revision == return module revision date string * *namespacestr == return module namepsace * * RETURNS: * status *********************************************************************/ void cap_split_modcap (cap_rec_t *cap, const xmlChar **module, const xmlChar **revision, const xmlChar **namespacestr) { #ifdef DEBUG if (!cap || !module || !revision || !namespacestr) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif *module = cap->cap_module; *revision = cap->cap_revision; *namespacestr = cap->cap_namespace; } /* cap_split_modcap */ /******************************************************************** * FUNCTION cap_make_moduri * * Malloc and construct a module URI for the specified module * make the module URI string (for sysCapabilityChange event) * * INPUTS: * mod == module to use * * RETURNS: * malloced string containing the URI !!! must be freed later !!! *********************************************************************/ xmlChar * cap_make_moduri (ncx_module_t *mod) { #ifdef DEBUG if (!mod) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return make_mod_urn(mod); } /* cap_make_moduri */ /* END file cap.c */ yuma123_2.14/netconf/src/ncx/log.c0000664000175000017500000005264214770023131017074 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: log.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 08jan06 abb begun, borrowed from openssh code ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include "procdefs.h" #include "log.h" #include "ncxconst.h" #include "status.h" #include "tstamp.h" #include "xml_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* #define LOG_DEBUG_TRACE 1 */ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /* global logging only at this time * per-session logging is TBD, and would need * to be in the agent or mgr specific session handlers, * not in the ncx directory */ static log_debug_t debug_level = LOG_DEBUG_NONE; static boolean use_tstamps, use_audit_tstamps; static FILE *logfile = NULL; static FILE *altlogfile = NULL; static FILE *auditlogfile = NULL; /******************************************************************** * FUNCTION log_open * * Open a logfile for writing * DO NOT use this function to send log entries to STDOUT * Leave the logfile NULL instead. * * INPUTS: * fname == full filespec string for logfile * append == TRUE if the log should be appended * == FALSE if it should be rewriten * tstamps == TRUE if the datetime stamp should be generated * at log-open and log-close time * == FALSE if no open and close timestamps should be generated * * RETURNS: * status *********************************************************************/ status_t log_open (const char *fname, boolean append, boolean tstamps) { const char *str; xmlChar buff[TSTAMP_MIN_SIZE]; #ifdef DEBUG if (!fname) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (logfile) { return ERR_NCX_DATA_EXISTS; } if (append) { str="a"; } else { str="w"; } logfile = fopen(fname, str); if (!logfile) { return ERR_FIL_OPEN; } use_tstamps = tstamps; if (tstamps) { tstamp_datetime(buff); fprintf(logfile, "\n*** log open at %s ***\n", buff); } return NO_ERR; } /* log_open */ /******************************************************************** * FUNCTION log_close * * Close the logfile * * RETURNS: * none *********************************************************************/ void log_close (void) { xmlChar buff[TSTAMP_MIN_SIZE]; if (!logfile) { return; } if (use_tstamps) { tstamp_datetime(buff); fprintf(logfile, "\n*** log close at %s ***\n", buff); } fclose(logfile); logfile = NULL; } /* log_close */ /******************************************************************** * FUNCTION log_audit_open * * Open the audit logfile for writing * DO NOT use this function to send log entries to STDOUT * Leave the audit_logfile NULL instead. * * INPUTS: * fname == full filespec string for audit logfile * append == TRUE if the log should be appended * == FALSE if it should be rewriten * tstamps == TRUE if the datetime stamp should be generated * at log-open and log-close time * == FALSE if no open and close timestamps should be generated * * RETURNS: * status *********************************************************************/ status_t log_audit_open (const char *fname, boolean append, boolean tstamps) { const char *str; xmlChar buff[TSTAMP_MIN_SIZE]; #ifdef DEBUG if (fname == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (auditlogfile != NULL) { return ERR_NCX_DATA_EXISTS; } if (append) { str="a"; } else { str="w"; } auditlogfile = fopen(fname, str); if (auditlogfile == NULL) { return ERR_FIL_OPEN; } use_audit_tstamps = tstamps; if (tstamps) { tstamp_datetime(buff); fprintf(auditlogfile, "\n*** audit log open at %s ***\n", buff); } return NO_ERR; } /* log_audit_open */ /******************************************************************** * FUNCTION log_audit_close * * Close the audit_logfile * * RETURNS: * none *********************************************************************/ void log_audit_close (void) { xmlChar buff[TSTAMP_MIN_SIZE]; if (auditlogfile == NULL) { return; } if (use_audit_tstamps) { tstamp_datetime(buff); fprintf(auditlogfile, "\n*** audit log close at %s ***\n", buff); } fclose(auditlogfile); auditlogfile = NULL; } /* log_audit_close */ /******************************************************************** * FUNCTION log_audit_is_open * * Check if the audit log is open * * RETURNS: * TRUE if audit log is open; FALSE if not *********************************************************************/ boolean log_audit_is_open (void) { return (auditlogfile == NULL) ? FALSE : TRUE; } /* log_audit_is_open */ /******************************************************************** * FUNCTION log_alt_open * * Open an alternate logfile for writing * DO NOT use this function to send log entries to STDOUT * Leave the logfile NULL instead. * * INPUTS: * fname == full filespec string for logfile * * RETURNS: * status *********************************************************************/ status_t log_alt_open (const char *fname) { const char *str; #ifdef DEBUG if (!fname) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (altlogfile) { return ERR_NCX_DATA_EXISTS; } str="w"; altlogfile = fopen(fname, str); if (!altlogfile) { return ERR_FIL_OPEN; } return NO_ERR; } /* log_alt_open */ /******************************************************************** * FUNCTION log_alt_close * * Close the alternate logfile * * RETURNS: * none *********************************************************************/ void log_alt_close (void) { if (!altlogfile) { return; } fclose(altlogfile); altlogfile = NULL; } /* log_alt_close */ /******************************************************************** * FUNCTION log_stdout * * Write lines of text to STDOUT, even if the logfile * is open, unless the debug mode is set to NONE * to indicate silent batch mode * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ void log_stdout (const char *fstr, ...) { va_list args; if (log_get_debug_level() == LOG_DEBUG_NONE) { return; } va_start(args, fstr); vprintf(fstr, args); fflush(stdout); va_end(args); } /* log_stdout */ /******************************************************************** * FUNCTION log_write * * Generate a log entry, regardless of log level * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ void log_write (const char *fstr, ...) { va_list args; if (log_get_debug_level() == LOG_DEBUG_NONE) { return; } va_start(args, fstr); if (logfile) { vfprintf(logfile, fstr, args); fflush(logfile); } else { vprintf(fstr, args); fflush(stdout); } va_end(args); } /* log_write */ /******************************************************************** * FUNCTION log_audit_write * * Generate an audit log entry, regardless of log level * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ void log_audit_write (const char *fstr, ...) { va_list args; va_start(args, fstr); if (auditlogfile != NULL) { vfprintf(auditlogfile, fstr, args); fflush(auditlogfile); } va_end(args); } /* log_audit_write */ /******************************************************************** * FUNCTION log_alt_write * * Write to the alternate log file * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for fprintf * *********************************************************************/ void log_alt_write (const char *fstr, ...) { va_list args; va_start(args, fstr); if (altlogfile) { vfprintf(altlogfile, fstr, args); fflush(altlogfile); } va_end(args); } /* log_alt_write */ /******************************************************************** * FUNCTION log_alt_indent * * Printf a newline to the alternate logfile, * then the specified number of space chars * * INPUTS: * indentcnt == number of indent chars, -1 == skip everything * *********************************************************************/ void log_alt_indent (int32 indentcnt) { int32 i; if (indentcnt >= 0) { log_alt_write("\n"); for (i=0; i LOG_DEBUG_DEBUG4) { dlevel = LOG_DEBUG_DEBUG4; } if (dlevel != debug_level) { #ifdef LOG_DEBUG_TRACE log_write("\n\nChanging log-level from '%s' to '%s'\n", log_get_debug_level_string(debug_level), log_get_debug_level_string(dlevel)); #endif debug_level = dlevel; } } /* log_set_debug_level */ /******************************************************************** * FUNCTION log_get_debug_level * * Get the global debug filter threshold level * * RETURNS: * the global debug level *********************************************************************/ log_debug_t log_get_debug_level (void) { return debug_level; } /* log_get_debug_level */ /******************************************************************** * FUNCTION log_get_debug_level_enum * * Get the corresponding debug enum for the specified string * * INPUTS: * str == string value to convert * * RETURNS: * the corresponding enum for the specified debug level *********************************************************************/ log_debug_t log_get_debug_level_enum (const char *str) { #ifdef DEBUG if (!str) { SET_ERROR(ERR_INTERNAL_PTR); return LOG_DEBUG_NONE; } #endif if (!xml_strcmp((const xmlChar *)str, LOG_DEBUG_STR_OFF)) { return LOG_DEBUG_OFF; } else if (!xml_strcmp((const xmlChar *)str, LOG_DEBUG_STR_ERROR)) { return LOG_DEBUG_ERROR; } else if (!xml_strcmp((const xmlChar *)str, LOG_DEBUG_STR_WARN)) { return LOG_DEBUG_WARN; } else if (!xml_strcmp((const xmlChar *)str, LOG_DEBUG_STR_INFO)) { return LOG_DEBUG_INFO; } else if (!xml_strcmp((const xmlChar *)str, LOG_DEBUG_STR_DEBUG)) { return LOG_DEBUG_DEBUG; } else if (!xml_strcmp((const xmlChar *)str, LOG_DEBUG_STR_DEBUG2)) { return LOG_DEBUG_DEBUG2; } else if (!xml_strcmp((const xmlChar *)str, LOG_DEBUG_STR_DEBUG3)) { return LOG_DEBUG_DEBUG3; } else if (!xml_strcmp((const xmlChar *)str, LOG_DEBUG_STR_DEBUG4)) { return LOG_DEBUG_DEBUG4; } else { return LOG_DEBUG_NONE; } } /* log_get_debug_level_enum */ /******************************************************************** * FUNCTION log_get_debug_level_string * * Get the corresponding string for the debug enum * * INPUTS: * level == the enum for the specified debug level * * RETURNS: * the string value for this enum *********************************************************************/ const xmlChar * log_get_debug_level_string (log_debug_t level) { switch (level) { case LOG_DEBUG_NONE: case LOG_DEBUG_OFF: return LOG_DEBUG_STR_OFF; case LOG_DEBUG_ERROR: return LOG_DEBUG_STR_ERROR; case LOG_DEBUG_WARN: return LOG_DEBUG_STR_WARN; case LOG_DEBUG_INFO: return LOG_DEBUG_STR_INFO; case LOG_DEBUG_DEBUG: return LOG_DEBUG_STR_DEBUG; case LOG_DEBUG_DEBUG2: return LOG_DEBUG_STR_DEBUG2; case LOG_DEBUG_DEBUG3: return LOG_DEBUG_STR_DEBUG3; case LOG_DEBUG_DEBUG4: return LOG_DEBUG_STR_DEBUG4; default: SET_ERROR(ERR_INTERNAL_VAL); return LOG_DEBUG_STR_OFF; } /*NOTREACHED*/ } /* log_get_debug_level_string */ /******************************************************************** * FUNCTION log_is_open * * Check if the logfile is active * * RETURNS: * TRUE if logfile open, FALSE otherwise *********************************************************************/ boolean log_is_open (void) { return (logfile) ? TRUE : FALSE; } /* log_is_open */ /******************************************************************** * FUNCTION log_indent * * Printf a newline, then the specified number of chars * * INPUTS: * indentcnt == number of indent chars, -1 == skip everything * *********************************************************************/ void log_indent (int32 indentcnt) { int32 i; if (indentcnt >= 0) { log_write("\n"); for (i=0; i= 0) { log_stdout("\n"); for (i=0; i #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_send_buff #include "send_buff.h" #endif #ifndef _H_status #include "status.h" #endif /******************************************************************** * FUNCTION send_buff * * Send the buffer to the ncxserver * * This function is used by applications which do not * select for write_fds, and may not block (if fnctl used) * * INPUTS: * fd == the socket to write to * buffer == the buffer to write * cnt == the number of bytes to write * * RETURNS: * status *********************************************************************/ status_t send_buff (int fd, const char *buffer, size_t cnt) { size_t sent, left; ssize_t retsiz; uint32 retry_cnt; retry_cnt = 1000; sent = 0; left = cnt; while (sent < cnt) { retsiz = write(fd, buffer, left); if (retsiz < 0) { switch (errno) { case EAGAIN: case EBUSY: if (--retry_cnt) { break; } /* else fall through */ default: return errno_to_status(); } } else { sent += (size_t)retsiz; buffer += retsiz; left -= (size_t)retsiz; } } return NO_ERR; } /* send_buff */ /* END file send_buff.c */ yuma123_2.14/netconf/src/ncx/xml_rd.h0000664000175000017500000000244514770023131017601 0ustar vladimirvladimir#ifndef _H_xml_rd #define _H_xml_rd /* FILE: xml_rd.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* XML Read functions */ #include #include "cfg.h" #include "dlq.h" #include "ncxtypes.h" #include "ses.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xml_msg.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xml_rd_open_file * * Read the value for the specified obj from an open FILE in XML format * * INPUTS: * fp == open FILE control block * obj == object template for the output value * val == address of value for output * * RETURNS: * status *********************************************************************/ extern status_t xml_rd_open_file (FILE *fp, obj_template_t *obj, val_value_t **val); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xml_rd */ yuma123_2.14/netconf/src/ncx/val123.c0000664000175000017500000013024114770023131017313 0ustar vladimirvladimir/* * Copyright (c) 2013 - 2018, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: val123.c Support functions not part of the original libyumancx API /******************************************************************** * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include "obj.h" #include "val.h" #include "val_util.h" #include "val_get_leafref_targval.h" #include "cli.h" #include "val123.h" #include "xpath_yang.h" /******************************************************************** * FUNCTION val123_deref * * Resolving leafref and returns its target value. * * INPUTS: * leafref_val == ptr to val_value_t of type leafref * OUTPUTS: * None * RETURNS: * target val instance *********************************************************************/ val_value_t* val123_deref(val_value_t* leafref_val) { val_value_t* val; val_value_t* parent_val; val_value_t* root_val; assert(leafref_val->parent); parent_val=leafref_val->parent; while(parent_val->parent) { parent_val = parent_val->parent; } root_val=parent_val; val=val_get_leafref_targval(leafref_val, root_val); return val; } /******************************************************************** * FUNCTION val123_find_match * * Function finding value with identical instance identifier * in another root. * * INPUTS: * haystack_root_val == ptr to root val to search * * needle_val == ptr to val to match * * OUTPUTS: * None * RETURNS: * ptr to val with matching instance identifier * or NULL if value with identical instance does not exist *********************************************************************/ val_value_t* val123_find_match(val_value_t* haystack_root_val, val_value_t* needle_val) { val_value_t* val=NULL; char* pathbuff; if(haystack_root_val==NULL) { return NULL; } status_t res = val_gen_instance_id(NULL, needle_val, NCX_IFMT_XPATH1, (xmlChar **) &pathbuff); assert(res==NO_ERR); if(obj_is_root(haystack_root_val->obj)) { /* absolute mode */ res = xpath_find_val_target(haystack_root_val, NULL/*mod*/, pathbuff, &val); } else { /* relative mode */ char* root_pathbuff; val_value_t* needle_root_val; needle_root_val = needle_val->parent; while(needle_root_val->obj != haystack_root_val->obj && needle_root_val->parent) { needle_root_val = needle_root_val->parent; } assert(needle_root_val->obj == haystack_root_val->obj); res = val_gen_instance_id(NULL, needle_root_val, NCX_IFMT_XPATH1, (xmlChar **) &root_pathbuff); assert(res==NO_ERR); assert(strlen(pathbuff)>strlen(root_pathbuff)); res = xpath_find_val_target(haystack_root_val, obj_get_mod(needle_val->obj), pathbuff+strlen(root_pathbuff)+1, &val); free(root_pathbuff); } free(pathbuff); return val; } /******************************************************************** * FUNCTION val123_clone_instance_ex * * Clones a val node and adds it to another root. Adds also parent * container/list nodes if they are missing in the target root. * * INPUTS: * root_val == root val to attach the cloned original_val * * original_val == ptr to val to clone and attach to root_val * * without_non_index_children == if TRUE only list key values are * cloned * * OUTPUTS: * clone_val == return ptr to the cloned and attached value * RETURNS: * status_t *********************************************************************/ static status_t val123_clone_instance_ex(val_value_t* clone_root_val, val_value_t* original_val, val_value_t** return_clone_val, boolean without_non_index_children) { status_t res; val_value_t* clone_parent_val; val_value_t* clone_val; val_index_t* index; val_value_t* val; if(obj_is_root(original_val->obj) || (original_val->parent==NULL)) { return ERR_NCX_INVALID_VALUE; } if(original_val->parent->obj==clone_root_val->obj) { clone_parent_val = clone_root_val; } else { res = val123_clone_instance_ex(clone_root_val, original_val->parent, &clone_parent_val, TRUE); if(res != NO_ERR) { return res; } } clone_val = val_clone(original_val); assert(clone_val); if(without_non_index_children) { clone_val = val_new_value(); val_init_from_template(clone_val, original_val->obj); for(index = val_get_first_index(original_val); index != NULL; index = val_get_next_index(index)) { val = val_clone(index->val); assert(val!=NULL); val_add_child(val, clone_val); } } else { clone_val = val_clone(original_val); } val_add_child(clone_val,clone_parent_val); *return_clone_val = clone_val; return NO_ERR; } /******************************************************************** * FUNCTION val123_clone_instance * * Clones a val node and adds it to another root. Adds also parent * container/list nodes if they are missing in the target root. * * INPUTS: * root_val == root val to attach the cloned original_val * * original_val == ptr to val to clone and attach to root_val * * OUTPUTS: * clone_val == return ptr to the cloned and attached value * RETURNS: * status_t *********************************************************************/ status_t val123_clone_instance(val_value_t* root_val, val_value_t* original_val, val_value_t** clone_val) { return val123_clone_instance_ex(root_val, original_val, clone_val, FALSE); } /* val123_clone_instance */ /******************************************************************** * FUNCTION obj123_get_child_ancestor_of_descendant * * Provided top_obj is ancestor of obj this function returns a child * of top_obj that also is ancestor of obj. * * INPUTS: * top_obj == the ancestor obj * * obj == obj descendant of top_obj * * OUTPUTS: * None * RETURNS: * obj_template_t ptr of child to top_obj or NULL *********************************************************************/ obj_template_t* obj123_get_child_ancestor_of_descendant(obj_template_t* top_obj, obj_template_t* obj) { obj_template_t* child_obj = obj; obj_template_t* last_data_obj = NULL; while(child_obj->parent!=NULL) { if(obj_is_leafy(child_obj) || child_obj->objtype==OBJ_TYP_CONTAINER || OBJ_TYP_LIST==child_obj->objtype) { last_data_obj=child_obj; } if(child_obj->parent==top_obj || (obj_is_root(child_obj->parent) && obj_is_root(top_obj))) { return last_data_obj; } child_obj=child_obj->parent; } return NULL; } /* obj123_get_child_ancestor_of_descendant */ /******************************************************************** * FUNCTION val123_get_first_obj_instance * * val tree is searched for the first instance of obj_template_t type. * * INPUTS: * top_val == val tree to search * * obj == obj template to search for * * OUTPUTS: * None * RETURNS: * ptr to matching val or NULL if no matching val found *********************************************************************/ val_value_t* val123_get_first_obj_instance(val_value_t* top_val, obj_template_t* obj) { obj_template_t* child_obj; val_value_t* child_val; val_value_t* result_val=NULL; assert(obj); if(top_val==NULL) { return NULL; } if(top_val->obj==obj) { return top_val; } child_obj = obj123_get_child_ancestor_of_descendant(top_val->obj, obj); child_val = val_find_child(top_val, obj_get_mod_name(child_obj), obj_get_name(child_obj)); while(child_val) { result_val = val123_get_first_obj_instance(child_val,obj); if(result_val!=NULL) { break; } if(child_val->obj->objtype==OBJ_TYP_LIST) { child_val = val_find_next_child(top_val, obj_get_mod_name(child_val->obj), obj_get_name(child_val->obj),child_val); } else { break; } }; return result_val; } /* val123_get_first_obj_instance */ /******************************************************************** * FUNCTION val123_get_next_obj_instance * * val tree is searched for the next instance of obj_template_t type. * useful in case the obj template is a child deep into list multi * key list. * * INPUTS: * top_val == val tree to search * * cur_val == val to continue the search from * * OUTPUTS: * None * RETURNS: * ptr to matching val or NULL if no matching val found *********************************************************************/ val_value_t* val123_get_next_obj_instance(val_value_t* top_val, val_value_t* cur_val) { val_value_t* next_val; val_value_t* ancestor_val; if(top_val==cur_val) { return NULL; } if(OBJ_TYP_LIST==cur_val->obj->objtype) { next_val = val_find_next_child(cur_val->parent, obj_get_mod_name(cur_val->obj), obj_get_name(cur_val->obj),cur_val); if(next_val!=NULL) { return next_val; } } /*Climb up and use val123_get_first_obj_instance on every OBJ_TYP_LIST branch*/ ancestor_val = cur_val->parent; while(ancestor_val!=NULL && ancestor_val!=top_val) { val_value_t* next_ancestor_val; if(ancestor_val->obj->objtype==OBJ_TYP_LIST) { next_ancestor_val = val_find_next_child(ancestor_val->parent, obj_get_mod_name(ancestor_val->obj), obj_get_name(ancestor_val->obj), ancestor_val); while(next_ancestor_val!=NULL) { if(next_ancestor_val!=NULL) { next_val = val123_get_first_obj_instance(next_ancestor_val, cur_val->obj); if(next_val!=NULL) { return next_val; } } next_ancestor_val = val_find_next_child(next_ancestor_val->parent, obj_get_mod_name(ancestor_val->obj), obj_get_name(ancestor_val->obj), next_ancestor_val); } } ancestor_val = ancestor_val->parent; } return NULL; } /* val123_get_next_obj_instance */ /******************************************************************** * FUNCTION ncx123_identity_get_first_base * * Returns the first base of identity (in YANG 1.0 there is only * 1 base) * * INPUTS: * identity == return the first base of this identity * * * OUTPUTS: * None * RETURNS: * ptr to ncx_identity_t or NULL if identity is abstract *********************************************************************/ ncx_identity_t* ncx123_identity_get_first_base(const ncx_identity_t* identity) { ncx_identity_base_t *base; base=(ncx_identity_base_t *)dlq_firstEntry(&identity->baseQ); if(base) { return base->identity; } else { return NULL; } } /* ncx123_identity_get_first_base */ /******************************************************************** * FUNCTION ncx123_identity_get_next_base * * Returns the next base of identity * * INPUTS: * identity == return the next base of this identity * * identity_base == return the base after this base * * OUTPUTS: * None * RETURNS: * ptr to ncx_identity_t or * NULL if there are no more bases (always on YANG 1.0) *********************************************************************/ ncx_identity_t* ncx123_identity_get_next_base(const ncx_identity_t* identity, const ncx_identity_t *identity_base) { ncx_identity_base_t *base; ncx_identity_base_t *next_base; assert(identity); assert(identity_base); base=(ncx_identity_base_t *)dlq_firstEntry(&identity->baseQ); assert(base); for(;base!=NULL;base=(ncx_identity_base_t *)dlq_nextEntry(base)) { if(base->identity==identity_base) { next_base=(ncx_identity_base_t *)dlq_nextEntry(base); if(next_base) { return next_base->identity; } else { return NULL; } } } assert(NULL); /*never found identity_base in baseQ*/ } /* ncx123_identity_get_next_base */ /******************************************************************** * FUNCTION ncx123_identity_is_derived_from * * Determine if identity is derived form identity_base. * * INPUTS: * identity == the identity * * identity_base == supposed base ancestor * * OUTPUTS: * None * RETURNS: * TRUE if identity_base is base ancestor of identity *********************************************************************/ boolean ncx123_identity_is_derived_from(const ncx_identity_t * identity, const ncx_identity_t *identity_base) { ncx_identity_t * b; assert(identity); assert(identity_base); for(b = ncx123_identity_get_first_base(identity); b != NULL; b = ncx123_identity_get_next_base(identity, b)) { if(identity_base==b) { return TRUE; } if(ncx123_identity_is_derived_from(b,identity_base)) { return TRUE; } } return FALSE; } /* ncx123_identity_is_derived_from */ /******************************************************************** * FUNCTION ncx123_find_matching_identities * * Find identities that match module context, qname and idref type. * 2 of the 3 match tuple members (mod and idref) can be omitted when * their value is a NULL pointer. * * INPUTS: * mod == ncx_module_t ptr to use or NULL for search without module * context * qname == modprefix:base-identity-name if mod!=NULL or * modulename:base-identity-name if mod==NULL or * modprefix:base-identity-name if mod==NULL * base-identity-name if no ':' character is present in qname * idref == typ_idref_t pointer specifying required base identities * dependency rules or NULL * * OUTPUTS: * ids == NULL or pointer to preallocated array with space to store * matched_ids_limit ncx_identity_t pointers. * matched_ids_limit == when ids!=NULL indicates the size of the ids array * * RETURNS: * count of matched identities *********************************************************************/ unsigned int ncx123_find_matching_identities(ncx_module_t* mod, const xmlChar * qname, const typ_idref_t *idref, ncx_identity_t **ids, unsigned int matched_ids_limit) { const xmlChar *str; xmlChar *qname_mod_id_buf; unsigned int qname_mod_id_len; unsigned int matched_ids; ncx_module_t* testmod; ncx_identity_t *identity; ncx_identity_t *idref_base=NULL; assert(qname); if(idref) { idref_base = idref->base; } /* find the local-name in the prefix:local-name combo */ str = qname; while (*str && *str != ':') { str++; } if (*str == ':') { str++; qname_mod_id_len=str-qname-1; qname_mod_id_buf=malloc(qname_mod_id_len+1); memcpy(qname_mod_id_buf,qname,qname_mod_id_len); qname_mod_id_buf[qname_mod_id_len]=0; } else { str = qname; qname_mod_id_buf=NULL; qname_mod_id_len=0; } matched_ids=0; if (mod) { if(qname_mod_id_buf!=NULL && 0!=strcmp(qname_mod_id_buf,mod->prefix)) { /*imported identity*/ ncx_import_t * import; import = ncx_find_pre_import(mod, qname_mod_id_buf); if(import) { testmod = ncx_find_module(import->module, import->revision); if (testmod) { identity = ncx_find_identity(testmod, str, FALSE); } } } else { identity = ncx_find_identity(mod, str, FALSE); } if(identity && (idref_base==NULL || ncx123_identity_is_derived_from(identity, idref_base))) { if(matched_ids_limit>matched_ids) { ids[matched_ids]=identity; } matched_ids++; } } else { for (testmod = ncx_get_first_module(); testmod != NULL; testmod = ncx_get_next_module(testmod)) { if(qname_mod_id_buf && !(0==strcmp(testmod->prefix,qname_mod_id_buf) || 0==strcmp(testmod->name,qname_mod_id_buf))) { continue; } identity = ncx_find_identity(testmod, str, FALSE); if(identity && (idref_base==NULL || ncx123_identity_is_derived_from(identity, idref_base))) { if(matched_ids_limit>matched_ids) { ids[matched_ids]=identity; } matched_ids++; } } /* yangcli needs this .. to be fixed */ for (testmod = ncx_get_first_session_module(); testmod != NULL; testmod = ncx_get_next_session_module(testmod)) { if(qname_mod_id_buf && !(0==strcmp(testmod->prefix,qname_mod_id_buf) || 0==strcmp(testmod->name,qname_mod_id_buf))) { continue; } identity = ncx_find_identity(testmod, str, FALSE); if(identity && (idref_base==NULL || ncx123_identity_is_derived_from(identity, idref_base))) { if(matched_ids_limit>matched_ids) { ids[matched_ids]=identity; } matched_ids++; } } } if(qname_mod_id_buf) { free(qname_mod_id_buf); } return matched_ids; } /* ncx123_find_matching_identities */ /******************************************************************** * FUNCTION val123_bit_is_set * * Determine if bit is set. * * INPUTS: * bits_val == val of btyp NCX_BT_BITS * * bit_str == bit name to check * * OUTPUTS: * None * RETURNS: * TRUE if the specified bit is set *********************************************************************/ boolean val123_bit_is_set(val_value_t* bits_val, const char* bit_str) { ncx_lmem_t *listmem; assert(bits_val); assert(bit_str); if(dlq_empty(&bits_val->v.list.memQ)) { return FALSE; } assert(NCX_BT_BITS == bits_val->v.list.btyp); for (listmem = (ncx_lmem_t *) dlq_firstEntry(&bits_val->v.list.memQ); listmem != NULL; listmem = (ncx_lmem_t *)dlq_nextEntry(listmem)) { assert(listmem->val.str); if(0==strcmp(listmem->val.str, bit_str)) { return TRUE; } } return FALSE; } /* val123_bit_is_set */ /******************************************************************** * FUNCTION cli123_parse_value_instance * * Create a val_value_t struct for the specified parm value, * and insert it into the parent container value * * ONLY CALLED FROM CLI PARSING FUNCTIONS IN ncxcli.c * ALLOWS SCRIPT EXTENSIONS TO BE PRESENT * * INPUTS: * rcxt == runstack context to use * val == parent value struct to adjust * parm == obj_template_t descriptor for the missing parm * instance_id_str == instance identifier string of the parameter value * e.g foo or foo[name='bar']/foo etc. * strval == string representation of the parm value * (may be NULL if parm btype is NCX_BT_EMPTY * script == TRUE if CLI script mode * == FALSE if CLI plain mode * * OUTPUTS: * A new val_value_t will be inserted in the val->v.childQ or of a child * sub-container or list value as required to fill in the parm. * * RETURNS: * status *********************************************************************/ status_t cli123_parse_value_instance(runstack_context_t *rcxt, val_value_t *parent_val, obj_template_t *obj, const xmlChar * instance_id_str, const xmlChar *strval, boolean script) { val_value_t* temp_parent_val; if(obj_is_cli(obj) || (parent_val->obj->objtype!=OBJ_TYP_CONTAINER && parent_val->obj->objtype!=OBJ_TYP_LIST) || parent_val->obj==obj123_get_first_data_parent(obj)) { return cli_parse_parm(rcxt, parent_val, obj, strval, script); } else { status_t res; val_value_t* val; val_value_t* childval; obj_template_t* targobj; val_value_t* targval; res = val123_new_value_from_instance_id(parent_val->obj, instance_id_str, FALSE, &childval, &targobj, &targval); if(res!=NO_ERR) { return res; } if(targobj->objtype!=OBJ_TYP_LIST && targobj->objtype!=OBJ_TYP_CONTAINER) { res = val_set_simval_obj(targval,targobj,strval); if(res!=NO_ERR) { val_free_value(childval); return res; } } temp_parent_val = val_new_value(); assert(temp_parent_val); val_init_from_template(temp_parent_val, parent_val->obj); val_add_child(childval,temp_parent_val); res = val123_merge_cplx(parent_val, temp_parent_val); val_free_value(temp_parent_val); return res; } } /* cli123_parse_value_instance */ /******************************************************************** * FUNCTION val123_new_value_from_instance_id * * Validate an instance identifier parameter * Return the target object * Return a value struct from root/parent containing * all the predicate assignments in the stance identifier * * INPUTS: * parent_obj == template of the context/parent node in case of * relative instance identifier, NULL in case of * absolute e.g. /.../... * instance_id_str == XPath expression for the instance-identifier * schemainst == TRUE if ncx:schema-instance string * FALSE if instance-identifier * * OUTPUTS: * childval == address of return pointer to child of parent_obj value * (first/top node) in the chain leading to targval * targobj == address of return pointer to target obj template * node. Only useful if targval is a simple type leaf that * the user must initialize with value. * targval == address of return pointer to target value * node within the value subtree returned * * RETURNS: * If NO_ERR: * malloced value node chain with keys representing the instance-identifier * from childval to the targval *********************************************************************/ status_t val123_new_value_from_instance_id(obj_template_t* parent_obj, const xmlChar* instance_id_str, boolean schemainst, val_value_t** childval, obj_template_t** targobj, val_value_t** targval) { xpath_pcb_t *xpathpcb; status_t res; *targobj = NULL; *childval = NULL; *targval = NULL; /* get a parser block for the instance-id */ xpathpcb = xpath_new_pcb(instance_id_str, NULL); assert(xpathpcb); /* initial parse into a token chain * this is only for parsing leafref paths! */ res = xpath_yang_parse_path(NULL, NULL, schemainst?XP_SRC_SCHEMA_INSTANCEID : XP_SRC_INSTANCEID, xpathpcb); if (res != NO_ERR) { log_error("\nError: parse XPath target '%s' failed", xpathpcb->exprstr); xpath_free_pcb(xpathpcb); return res; } /* validate against the object tree */ res = xpath_yang_validate_path(parent_obj?obj_get_mod(parent_obj):NULL, parent_obj?parent_obj:ncx_get_gen_root(), xpathpcb, schemainst, targobj); if (res != NO_ERR) { log_error("\nError: validate XPath target '%s' failed", xpathpcb->exprstr); xpath_free_pcb(xpathpcb); return res; } /* have a valid target object, so follow the * parser chain and build a value subtree * from the XPath expression */ *childval = xpath_yang_make_instanceid_val(xpathpcb, &res, targval); xpath_free_pcb(xpathpcb); return res; } /* val123_new_value_from_instance_id */ /******************************************************************** * FUNCTION val123_merge_cplx * * Merge src val tree into dst. * * INPUTS: * dst == destination val to merge into * * src == source val * * OUTPUTS: * None * RETURNS: * status_t *********************************************************************/ status_t val123_merge_cplx(val_value_t* dst, val_value_t* src) { val_value_t* chval; val_value_t* match_val; for (chval = val_get_first_child(src); chval != NULL; chval = val_get_next_child(chval)) { #if 0 if(obj_is_key(chval->obj)) { continue; } #endif match_val = val123_find_match(dst, chval); if(match_val==NULL) { val_add_child(val_clone(chval),dst); } else { if(typ_is_simple(match_val->btyp)) { val_merge(match_val, chval); } else { val123_merge_cplx(match_val, chval); } } } return NO_ERR; } /* val123_merge_cplx */ /******************************************************************** * FUNCTION obj123_get_first_data_parent * * Find the first data parent e.g. list or container * * INPUTS: * obj == obj to start from * * OUTPUTS: * None * RETURNS: * obj found or NULL *********************************************************************/ obj_template_t* obj123_get_first_data_parent(obj_template_t* obj) { obj_template_t* parent_obj; do { parent_obj = obj_get_parent(obj); if(parent_obj->objtype==OBJ_TYP_CONTAINER || parent_obj->objtype==OBJ_TYP_LIST) { break; } obj = parent_obj; } while(parent_obj); return parent_obj; } /* obj123_get_first_data_parent */ /******************************************************************** * FUNCTION cli123_parse_value_string * * Parses value specified as single quote string * * INPUTS: * cli_str == command line string starting with ' * * OUTPUTS: * len == length of the string * * valstr == allocated buffer with the value string * * RETURNS: * status_t *********************************************************************/ status_t cli123_parse_value_string(const char* cli_str, unsigned int* len, char** valstr) { const char* ptr = cli_str; *valstr=NULL; if(*ptr == '\'') { do { ptr++; } while(*ptr!='\'' && *ptr!=' '); if(*ptr=='\'') { char* buf; unsigned int len_wo_quotes; len_wo_quotes=ptr-cli_str-1; buf=(char*)malloc(len_wo_quotes+1); memcpy(buf,cli_str+1,len_wo_quotes); buf[len_wo_quotes]=0; *len = ptr-cli_str+1; *valstr=buf; } else { assert(0); } } else { /*TODO*/ assert(0); } return NO_ERR; } /* cli123_parse_value_string */ /******************************************************************** * FUNCTION cli123_parse_parm_assignment * * Parses cli parameter assignment tuple e.g. foo="bar" and creates value * * INPUTS: * obj == parent container * autocomp == attempt to autocomplete parameter identifiers * cli_str == start of the parameter identifier, not necessary 0 terminated * * OUTPUTS: * len_out == length of detected parameter identifier * chval_out == allocated val for the parameter[=value] expression * * RETURNS: * NO_ERR, ERR_NCX_AMBIGUOUS_CMD, other *********************************************************************/ status_t cli123_parse_parm_assignment(obj_template_t* obj, boolean autocomp, const char* cli_str, unsigned int* len_out, val_value_t** chval_out) { status_t res; obj_template_t* chobj; val_value_t* chval; const char* ptr; unsigned int len; char* valstr; *len_out = 0; *chval_out = NULL; ptr = cli_str; res = cli123_parse_next_child_obj_from_path(obj, autocomp, ptr, &len, &chobj); if(res!=NO_ERR) { return res; } ptr+=len; if(*ptr!='=') { return ERR_NCX_WRONG_TKVAL; } ptr++; res = cli123_parse_value_string(ptr, &len, &valstr); if(res!=NO_ERR) { return res; } ptr+=len; chval=val_new_value(); assert(chval); res = val_set_simval_obj(chval,chobj,valstr); free(valstr); if(res==NO_ERR) { *len_out = ptr-cli_str; *chval_out = chval; } return res; } /* cli123_parse_parm_assignment */ /******************************************************************** * FUNCTION cli123_parse_next_child_obj_from_path * * Attempts to find child object from parent obj and instance identifier * in a private case a simple parameter name string. * Optionally autocompletion can be attempted. * * INPUTS: * obj == parent container * autocomp == attempt to autocomplete parameter identifiers * parmname == start of the parameter identifier, not necessary 0 terminated * * OUTPUTS: * len_out == length of detected parameter identifier * chobj_out == obj template of the detected parameter * * RETURNS: * NO_ERR, ERR_NCX_AMBIGUOUS_CMD, other *********************************************************************/ status_t cli123_parse_next_child_obj_from_path(obj_template_t* obj, boolean autocomp, const char* parmname, unsigned int* len_out, obj_template_t** chobj_out) { status_t res; unsigned int parmnamelen, copylen, matchcount; const char* str; unsigned int len; obj_template_t* chobj; char* modname_or_prefix=NULL; char* modname=NULL; const char* name; res=NO_ERR; len = 0; chobj=NULL; /* check the parmname string for a terminating char */ parmnamelen = 0; if (ncx_valid_fname_ch(*parmname)) { str = &parmname[1]; name=parmname; while (*str && (ncx_valid_name_ch(*str) || *str==':')) { if(*str==':') { modname_or_prefix=malloc(str-parmname); memcpy(modname_or_prefix,parmname,str-parmname); modname_or_prefix[str-parmname]=0; name=str+1; } str++; } parmnamelen = (uint32)(str - name); len = (uint32)(str - parmname); /* is prefix or modname */ if(modname_or_prefix) { ncx_module_t* mod; modname=modname_or_prefix; mod = obj123_find_child_mod_from_name(obj,modname_or_prefix); if(mod==NULL) { /* try to match prefix */ mod = obj123_find_child_mod_from_prefix(obj,modname_or_prefix); if(mod!=NULL) { modname=mod->name; } } } /* check if this parameter name is in the parmset def */ chobj = obj_find_child_str(obj, modname, (const xmlChar *)parmname, parmnamelen); /* check if parm was found, try partial name if not */ if (!chobj && autocomp) { matchcount = 0; chobj = obj_match_child_str(obj, modname, (const xmlChar *)name, parmnamelen, &matchcount); if (chobj) { if (matchcount > 1) { res = ERR_NCX_AMBIGUOUS_CMD; } } else { len = 0; } } } /* else it could be a default-parm value */ if(modname_or_prefix!=NULL) { free(modname_or_prefix); } *chobj_out = chobj; *len_out = len; return res; } /* cli123_parse_next_child_obj_from_path */ /******************************************************************** * FUNCTION val123_select_obj * * Finds all child values of the specified object template. Returns * value of the original parent_val obj template with all descendant * node instances of matching obj template. e.g. selecting all in-octets * in /interfaces-state * * INPUTS: * parent_val == top level node to search in * child_obj == obj template instances to select * * RETURNS: * NULL in case there were no matches, allocated value of identical * parent_val->obj containing all selected matches. *********************************************************************/ val_value_t* val123_select_obj(val_value_t* parent_val, obj_template_t* child_obj) { status_t res; val_value_t* match_val; val_value_t* parent_select_val; val_value_t* clone_val; match_val = val123_get_first_obj_instance(parent_val, child_obj); if(match_val==NULL) { return NULL; } parent_select_val = val_new_value(); val_init_from_template(parent_select_val, parent_val->obj); while(match_val) { res = val123_clone_instance(parent_select_val, match_val, &clone_val); assert(res==NO_ERR); match_val = val123_get_next_obj_instance(parent_val, match_val); } return parent_select_val; } /* val123_select_obj */ /******************************************************************** * FUNCTION val123_clone_real * * Makes a copy of a val node resolving all virtual nodes part of it * so that the returned result has no virtual data nodes. * * INPUTS: * val == input val node * * RETURNS: * Real val == clone of val with only real (non-virtual) values * *********************************************************************/ val_value_t* val123_clone_real(val_value_t* val) { status_t res; val_value_t* child_val; val_value_t* real_val; val_value_t* real_child_val; val_value_t* useval; if(val_is_virtual(val)) { useval = val_get_virtual_value(NULL, val, &res); if(res != NO_ERR) { return NULL; } } else { useval = val; } if(obj_is_leafy(useval->obj)) { real_val = val_clone(useval); return real_val; } else { real_val = val_new_value(); assert(real_val); val_init_from_template(real_val, val->obj); } child_val = val_get_first_child(useval); while(child_val) { val_value_t* next_child_val; next_child_val = val_get_next_child(child_val); real_child_val=val123_clone_real(child_val); if(real_child_val) { val_add_child(real_child_val,real_val); if(real_child_val->btyp == NCX_BT_LIST) { res = val_gen_index_chain(real_child_val->obj, real_child_val); assert(res == NO_ERR); } } child_val = next_child_val; } return real_val; } /* val123_clone_real */ /******************************************************************** * FUNCTION obj123_get_top_uses * * Checks the ancestor nodes of obj and returns the uses * node where the ancestor closes to root was instantiated * or NULL if the obj node or any of its ancestors is not * instantiated through uses. * * INPUTS: * obj == obj template of node * * RETURNS: * pointer to obj_template_t of uses node or NULL *********************************************************************/ obj_template_t* obj123_get_top_uses(obj_template_t* obj) { obj_template_t* top_uses_obj=NULL; for(;obj->parent!=NULL;obj=obj->parent) { if(obj->usesobj!=NULL) { top_uses_obj=obj->usesobj; } } return top_uses_obj; } /* obj123_get_top_uses */ /******************************************************************** * FUNCTION typ123_get_first_named_typdef * * Follows a chain of named typdefs and returns pointer to * the first (base) typ_def_t node of type class NCX_CL_NAMED. * * * INPUTS: * typdef == pointer to typ_def_t named type * * RETURNS: * pointer to typ_def_t node first in chain of names type leading * to typdef or NULL if the typdef class was not named or the chain did not lead to simple type. *********************************************************************/ typ_def_t* typ123_get_first_named_typdef(typ_def_t* typdef) { typ_def_t* parent=NULL; typ_def_t* child = typdef; while(child->tclass==NCX_CL_NAMED) { parent=typ_get_parent_typdef(child); if(parent==NULL || parent->tclass!=NCX_CL_NAMED) { return child; } child = parent; } return NULL; } /* typ123_get_first_named_typdef */ /******************************************************************** * FUNCTION ncx123_find_all_homonym_top_objs * * Find all top obj_template_t in in any module that * matches the object name string * * INPUTS: * modQ == module queue * * objname == obj name to match * * OUTPUTS: * matched_objs == pointer to array for retuning matches or NULL * * matched_objs_limit == limit of the matched_objs array * * RETURNS: * count of matched objs *********************************************************************/ unsigned int ncx123_find_all_homonym_top_objs(dlq_hdr_t *modQ, const xmlChar *objname, obj_template_t **matched_objs, unsigned int matched_objs_limit) { assert ( modQ && " param modQ is NULL" ); assert ( objname && " param objname is NULL" ); obj_template_t *obj = NULL; ncx_module_t *mod; unsigned int matched_cnt=0; for (mod = (ncx_module_t *)dlq_firstEntry(modQ); mod != NULL; mod = (ncx_module_t *)dlq_nextEntry(mod)) { obj = obj_find_template_top(mod, ncx_get_modname(mod), objname); if (obj) { if(matched_objs!=NULL && (matched_objs_limit>matched_cnt)) { matched_objs[matched_cnt]=obj; } matched_cnt++; } } return matched_cnt; } /* ncx123_find_all_homonym_top_objs */ /******************************************************************** * FUNCTION obj123_find_all_homonym_child_objs * * Find all homonym child objs - having the same name but different * mod e.g /a/foo:b and /a/bar:b * * INPUTS: * parent == parent obj * * objname == name to match * * OUTPUTS: * matched_objs == pointer to array for retuning matches or NULL * * matched_objs_limit == limit of the matched_objs array * * RETURNS: * count of matched objs *********************************************************************/ unsigned int obj123_find_all_homonym_child_objs (obj_template_t *parent, const xmlChar *objname, obj_template_t **matched_objs, unsigned int matched_objs_limit) { assert ( parent && " param parent is NULL" ); assert ( objname && " param objname is NULL" ); obj_template_t *obj; unsigned int matched_cnt=0; for( obj = obj_first_child_deep(parent); obj != NULL; obj = obj_next_child_deep(obj)) { if (0==strcmp(objname,obj_get_name(obj))) { if(matched_objs!=NULL && (matched_objs_limit>matched_cnt)) { matched_objs[matched_cnt]=obj; } matched_cnt++; } } return matched_cnt; } /* obj123_find_all_homonym_child_objs */ /******************************************************************** * FUNCTION obj123_find_child_mod_from_name * * Looks for a child from a module with specified name * and returns pointer to the module. * * * INPUTS: * parent == parent obj * modname == name of the module to look for * * RETURNS: * pointer to the module or NULL if no child * from module with the specified name was found. *********************************************************************/ ncx_module_t* obj123_find_child_mod_from_name(obj_template_t *parent, const char* modname) { assert ( parent && " param parent is NULL" ); assert ( modname && " param modname is NULL" ); obj_template_t *obj; for( obj = obj_first_child_deep(parent); obj != NULL; obj = obj_next_child_deep(obj)) { if (0==strcmp(modname,obj_get_mod_name(obj))) { return obj_get_mod(obj); } } return NULL; } /* obj123_find_child_mod_from_name */ /******************************************************************** * FUNCTION obj123_find_child_mod_from_prefix * * Looks for a child from a module with specified prefix * and returns pointer to the module. * * * INPUTS: * parent == parent obj * modprefix == prefix of the module to look for * * RETURNS: * pointer to the module or NULL if no child * from module with the specified prefix was found. *********************************************************************/ ncx_module_t* obj123_find_child_mod_from_prefix(obj_template_t *parent, const char* modprefix) { assert ( parent && " param parent is NULL" ); assert ( modprefix && " param modprefix is NULL" ); obj_template_t *obj; for( obj = obj_first_child_deep(parent); obj != NULL; obj = obj_next_child_deep(obj)) { if (0==strcmp(modprefix,obj_get_mod_prefix(obj))) { return obj_get_mod(obj); } } return NULL; } /* obj123_find_child_mod_from_prefix */ /******************************************************************** * FUNCTION val123_parse_idref_ex * * Parse a CLI BASED identityref QName into its various parts * * INPUTS: * idref == typ_idref_t ptr of the target identityref type * mod == module containing the default-stmt (or NULL if N/A) * qname == QName or local-name string to parse * nsid == address of return namespace ID of the module * indicated by the prefix. If mod==NULL then * a prefix MUST be present * name == address of return local name part of QName * id == address of return identity, if found * * OUTPUTS: * if non-NULL: * *nsid == namespace ID for the prefix part of the QName * *name == pointer into the qname string at the start of * the local name part * *id == pointer to ncx_identity_t found (if any, not an error) * * RETURNS: * status *********************************************************************/ status_t val123_parse_idref_ex (ncx_module_t *mod, const xmlChar *qname, const typ_idref_t *idref, ncx_identity_t **id) { status_t res; unsigned int matched_cnt; matched_cnt = ncx123_find_matching_identities(mod,qname,idref,id,1); if(matched_cnt==0) { res=ERR_NCX_INVALID_VALUE; } else if(matched_cnt==1) { res=NO_ERR; } else { unsigned int i; ncx_identity_t **identity_array; identity_array=malloc(matched_cnt*sizeof(ncx_identity_t *)); ncx123_find_matching_identities(mod,qname,idref,identity_array,matched_cnt); log_error("\nError: Multiple identities match identityref value '%s': '%s:%s'",qname, identity_array[0]->mod->name,identity_array[0]->name); for(i=1;imod->name,identity_array[i]->name); } free(identity_array); res=ERR_NCX_MULTIPLE_MATCHES; } return res; } /* val123_parse_idref_ex */ /******************************************************************** * FUNCTION val123_add_virtual_cb * * Special function to add additional callback * to containers that are virtual value nodes * * INPUTS: * val == pointer to the preexisting virual container value * cbfn == get callback function to use *********************************************************************/ void val123_add_virtual_cb (val_value_t *val, void *cbfn) { val_virt_getcb_node_t* getcb_node; assert(val && cbfn); getcb_node=malloc(sizeof(val_virt_getcb_node_t)); assert(getcb_node); memset(getcb_node,0,sizeof(val_virt_getcb_node_t)); getcb_node->getcb = cbfn; dlq_enque(getcb_node, &val->getcbQ); } /* val_add_virtual_cb */ /******************************************************************** * FUNCTION obj123_get_top_uses * * Returns the case obj of value for a specified parent choice object. * * INPUTS: * choicobj == obj template for the choice to be resolved * testval == value * * RETURNS: * pointer to obj_template_t of used case or NULL *********************************************************************/ obj_template_t* val123_get_case_for_choice(obj_template_t* choicobj, val_value_t* testval) { obj_template_t* obj; obj = testval->obj; while(obj) { if(obj->parent == choicobj) { return obj; } obj = obj->parent; } return NULL; } yuma123_2.14/netconf/src/ncx/plock.c0000664000175000017500000002072714770023131017422 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: plock.c Partial lock control block Support for RFC 5717 operations ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 21jun10 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_plock #include "plock.h" #endif #ifndef _H_plock_cb #include "plock_cb.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tstamp #include "tstamp.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xpath1 #include "xpath1.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define PLOCK_DEBUG 1 #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION plock_get_id * * Get the lock ID for this partial lock * * INPUTS: * plcb == partial lock control block to use * * RETURNS: * the lock ID for this lock *********************************************************************/ plock_id_t plock_get_id (plock_cb_t *plcb) { #ifdef DEBUG if (plcb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif return plcb->plock_id; } /* plock_get_id */ /******************************************************************** * FUNCTION plock_get_sid * * Get the session ID holding this partial lock * * INPUTS: * plcb == partial lock control block to use * * RETURNS: * session ID that owns this lock *********************************************************************/ uint32 plock_get_sid (plock_cb_t *plcb) { #ifdef DEBUG if (plcb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif return plcb->plock_sesid; } /* plock_get_sid */ /******************************************************************** * FUNCTION plock_get_timestamp * * Get the timestamp of the lock start time * * INPUTS: * plcb == partial lock control block to use * * RETURNS: * timestamp in date-time format *********************************************************************/ const xmlChar * plock_get_timestamp (plock_cb_t *plcb) { #ifdef DEBUG if (plcb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return plcb->plock_time; } /* plock_get_timestamp */ /******************************************************************** * FUNCTION plock_get_final_result * * Get the final result for this partial lock * * INPUTS: * plcb == partial lock control block to use * * RETURNS: * pointer to final result struct *********************************************************************/ xpath_result_t * plock_get_final_result (plock_cb_t *plcb) { #ifdef DEBUG if (plcb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return plcb->plock_final_result; } /* plock_get_final_result */ /******************************************************************** * FUNCTION plock_get_first_select * * Get the first select XPath control block for the partial lock * * INPUTS: * plcb == partial lock control block to use * * RETURNS: * pointer to first xpath_pcb_t for the lock *********************************************************************/ xpath_pcb_t * plock_get_first_select (plock_cb_t *plcb) { #ifdef DEBUG if (plcb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (xpath_pcb_t *)dlq_firstEntry(&plcb->plock_xpathpcbQ); } /* plock_get_first_select */ /******************************************************************** * FUNCTION plock_get_next_select * * Get the next select XPath control block for the partial lock * * INPUTS: * xpathpcb == current select block to use * * RETURNS: * pointer to first xpath_pcb_t for the lock *********************************************************************/ xpath_pcb_t * plock_get_next_select (xpath_pcb_t *xpathpcb) { #ifdef DEBUG if (xpathpcb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (xpath_pcb_t *)dlq_nextEntry(xpathpcb); } /* plock_get_next_select */ /******************************************************************** * FUNCTION plock_add_select * * Add a select XPath control block to the partial lock * * INPUTS: * plcb == partial lock control block to use * xpathpcb == xpath select block to add * result == result struct to add * *********************************************************************/ void plock_add_select (plock_cb_t *plcb, xpath_pcb_t *xpathpcb, xpath_result_t *result) { #ifdef DEBUG if (plcb == NULL || xpathpcb == NULL || result == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif dlq_enque(xpathpcb, &plcb->plock_xpathpcbQ); dlq_enque(result, &plcb->plock_resultQ); } /* plock_add_select */ /******************************************************************** * FUNCTION plock_make_final_result * * Create a final XPath result for all the partial results * * This does not add the partial lock to the target config! * This is an intermediate step! * * INPUTS: * plcb == partial lock control block to use * * RETURNS: * status; NCX_ERR_INVALID_VALUE if the final nodeset is empty *********************************************************************/ status_t plock_make_final_result (plock_cb_t *plcb) { xpath_result_t *result; xpath_pcb_t *xpathpcb; #ifdef DEBUG if (plcb == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif xpathpcb = (xpath_pcb_t *) dlq_firstEntry(&plcb->plock_xpathpcbQ); if (xpathpcb == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } for (result = (xpath_result_t *) dlq_firstEntry(&plcb->plock_resultQ); result != NULL; result = (xpath_result_t *)dlq_nextEntry(result)) { xpath_move_nodeset(result, plcb->plock_final_result); } xpath1_prune_nodeset(xpathpcb, plcb->plock_final_result); if (xpath_nodeset_empty(plcb->plock_final_result)) { return ERR_NCX_XPATH_NODESET_EMPTY; } else { return NO_ERR; } } /* plock_make_final_result */ /* END file plock.c */ yuma123_2.14/netconf/src/ncx/xml_wr.c0000664000175000017500000017606014770023131017624 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xml_wr.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24may06 abb begun; split out from agt_ncx.c 12feb07 abb split out non-agent specific write fns back to ncx ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "dlq.h" #include "ncx.h" #include "ncx_num.h" #include "ncxconst.h" #include "obj.h" #include "ses.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_msg.h" #include "xml_util.h" #include "xml_wr.h" #include "xpath.h" #include "xpath_wr.h" #include "xpath_yang.h" #include "cli.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define XML_WR_DEBUG 1 #endif #define XML_WR_MAX_LINESTR 34 /******************************************************************** * FUNCTION xml_wr_default_leaf_list * * Check if this value has a leaf-list with defaults in use. * * INPUTS: * val == value to check * * RETURNS: * TRUE if the node should be treated like it has content because * of an empty leaf-list with defaults. FALSE otherwise. *********************************************************************/ static boolean xml_wr_default_leaf_list(const xml_msg_hdr_t *msg, const val_value_t *val) { if ((msg->withdef == NCX_WITHDEF_REPORT_ALL || msg->withdef == NCX_WITHDEF_REPORT_ALL) && val_has_default_leaf_list(val)) return TRUE; return FALSE; } /******************************************************************** * FUNCTION fit_on_line * * Check if the specified value will fit on the current line * or if a newline is needed first * * INPUTS: * scb == session control block * val == value to check * * RETURNS: * TRUE if value will fit on current line, FALSE if not *********************************************************************/ static boolean fit_on_line (ses_cb_t *scb, const val_value_t *val) { /* metavals must be put on 1 line */ if (val_is_metaval(val)) { return TRUE; } /* make sure leafs are printed without leading and * trailing whitespace in a normal session or * output to an XML file */ if (scb->mode == SES_MODE_XML || scb->mode == SES_MODE_XMLDOC) { if (obj_is_leafy(val->obj)) { return TRUE; } } return val_fit_oneline(val, SES_LINESIZE(scb)); } /* fit_on_line */ /******************************************************************** * FUNCTION write_xmlns_decl * * Write an xmlns declaration * * INPUTS: * scb == session control block * pfix == prefix to use * nsid == namespace ID to use for xmlns value * indent == actual indent amount; xml_wr_indent will not be checked * * RETURNS: * none *********************************************************************/ static void write_xmlns_decl (ses_cb_t *scb, const xmlChar *pfix, xmlns_id_t nsid, int32 indent) { const xmlChar *val; if (ses_get_xml_nons(scb)) { return; } val = xmlns_get_ns_name(nsid); if (!val) { SET_ERROR(ERR_INTERNAL_VAL); return; } if (indent < 0) { ses_putchar(scb, ' '); } else { ses_indent(scb, indent); } /* write the xmlns attribute name */ ses_putstr(scb, XMLNS); /* generate a prefix if this attribute has a namespace ID */ if (pfix) { ses_putchar(scb, ':'); ses_putstr(scb, pfix); } ses_putchar(scb, '='); ses_putchar(scb, '\"'); ses_putstr(scb, val); /* write the namespace URI value */ ses_putchar(scb, '\"'); } /* write_xmlns_decl */ /******************************************************************** * FUNCTION handle_xpath_start_tag * * Write a the xmlns attributes needed for the * namespaces (implied or explicit) in an XPath * expression * * Since all XPath content variables are leafs * (no mixed content in YANG) * then it does not matter if any xmlns attributes * override the prefixes in the message header prefix map * * When the XPath expression is generated in xml_wr_check_val * only default prefixes will be used, assuming that * this function was called and worked correctly first * * INPUTS: * scb == session control block * xpathpcb == XPath parser control block to use * indent == number of chars to indent after a newline * == -1 means no newline or indent * == 0 means just newline * retcount == address of return xmlns print count * * OUTPUTS: * *retcount == number of xmlns directives written * * RETURNS: * status *********************************************************************/ static status_t handle_xpath_start_tag (ses_cb_t *scb, const xpath_pcb_t *xpathpcb, int32 indent, uint32 *retcount) { const xmlChar *defpfix; uint32 num_nsids, i; xmlns_id_t cur_nsid; xmlns_id_t nsid_array[XML_WR_MAX_NAMESPACES]; status_t res; *retcount = 0; num_nsids = 0; res = xpath_yang_get_namespaces(xpathpcb, nsid_array, XML_WR_MAX_NAMESPACES, &num_nsids); if (res != NO_ERR) { /* not expecting anything except a buffer overflow * from too many namespaces in the same XPath expr */ return res; } /* else add all the missing xmlns directives */ for (i = 0; i < num_nsids; i++) { cur_nsid = nsid_array[i]; /* get the default and message prefixes */ defpfix = xmlns_get_ns_prefix(cur_nsid); if (defpfix == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } /* force this namespace to have a prefix and * xmlns attribute */ write_xmlns_decl(scb, defpfix, cur_nsid, indent); } *retcount = num_nsids; return NO_ERR; } /* handle_xpath_start_tag */ /******************************************************************** * FUNCTION write_attrs * * Write all the required attributes for this element * * INPUTS: * scb == session control block * msg == header for the rpc_msg_t in progress * attrQ == Q of xml_attr_t or val_value_t to write * isattrq == TRUE for Q of xml_attr_t * == FALSE for Q of val_value_t * curelem == value node for current element, if available * indent == actual indent amount; xml_wr_indent will not be checked * elem_nsid == namespace ID of the parent element * *********************************************************************/ static void write_attrs (ses_cb_t *scb, xml_msg_hdr_t *msg, const dlq_hdr_t *attrQ, boolean isattrq, val_value_t *curelem, int32 indent, xmlns_id_t elem_nsid) { const xml_attr_t *attr; val_value_t *val; dlq_hdr_t *hdr; const xmlChar *pfix, *attr_name, *attr_qname; xmlChar *buffer; boolean xneeded; uint32 len, retcount, bufferlen; xmlns_id_t ns_id, attr_nsid; status_t res; ns_id = xmlns_ns_id(); for (hdr = dlq_firstEntry(attrQ); hdr != NULL; hdr = dlq_nextEntry(hdr)) { attr = NULL; val = NULL; /* set up the data fields; len is not precise, ignores prefix */ if (isattrq) { attr = (const xml_attr_t *)hdr; attr_nsid = attr->attr_ns; attr_name = attr->attr_name; attr_qname = attr->attr_qname; len = xml_strlen(attr->attr_val) + xml_strlen(attr->attr_name); } else { val = (val_value_t *)hdr; attr_nsid = val->nsid; attr_name = val->name; attr_qname = NULL; len = xml_strlen(val->v.str) + xml_strlen(val->name); } /* deal with initial indent */ if (indent < 0) { ses_putchar(scb, ' '); } else if (len + 4 + SES_LINELEN(scb) >= SES_LINESIZE(scb)) { ses_indent(scb, indent); } else { ses_putchar(scb, ' '); } /* generate one attribute name value pair * * generate a prefix if this attribute has a namespace ID * make sure to skip the XMLNS namespace; this is * handled different than all other attributes * */ /* check if this is an XMLNS directive */ if (XMLNS_EQ(attr_nsid, ns_id)) { /* xmlns:prefix format */ if (attr_name != attr_qname) { /* this is a namespace decl with a prefix */ ses_putstr(scb, XMLNS); ses_putchar(scb, ':'); } } else if (attr_nsid) { /* prefix:attribute-name format */ pfix = xml_msg_get_prefix(msg, elem_nsid, attr_nsid, curelem, &xneeded); if (xneeded) { write_xmlns_decl(scb, pfix, attr_nsid, indent); } /* deal with indent again */ if (indent < 0) { ses_putchar(scb, ' '); } else if (len + 4 + SES_LINELEN(scb) >= SES_LINESIZE(scb)) { ses_indent(scb, indent); } else { ses_putchar(scb, ' '); } if (pfix) { ses_putstr(scb, pfix); ses_putchar(scb, ':'); } } else if (val) { /* check if XPath or identityref content */ if (val->xpathpcb) { /* generate all the default xmlns directives needed * for the content following this start tag to be valid */ retcount = 0; res = handle_xpath_start_tag(scb, val->xpathpcb, indent, &retcount); if (res != NO_ERR) { /* not expecting anything except a buffer overflow * from too many namespaces in the same XPath expr */ SET_ERROR(res); } else if (retcount) { ses_indent(scb, indent); } } else if (val->btyp == NCX_BT_IDREF) { xneeded = FALSE; pfix = xml_msg_get_prefix(msg, (val->parent) ? val_get_nsid(val->parent) : 0, VAL_IDREF_NSID(val), val, &xneeded); if (xneeded) { write_xmlns_decl(scb, pfix, VAL_IDREF_NSID(val), indent); } } } ses_putstr(scb, attr_name); ses_putchar(scb, '='); ses_putchar(scb, '\"'); if (isattrq) { ses_putastr(scb, attr->attr_val, -1); } else if (typ_is_string(val->btyp)) { ses_putastr(scb, VAL_STR(val), -1); } else { /* write the simple value meta var the slow way */ bufferlen = 0; res = val_sprintf_simval_nc(NULL, val, &bufferlen); if (res != NO_ERR) { SET_ERROR(res); } else { buffer = m__getMem(bufferlen+1); if (buffer == NULL) { SET_ERROR(ERR_INTERNAL_MEM); } else { res = val_sprintf_simval_nc(buffer, val, &bufferlen); if (res != NO_ERR) { SET_ERROR(res); } else { ses_putastr(scb, buffer, -1); } m__free(buffer); } } } ses_putchar(scb, '\"'); } } /* write_attrs */ /******************************************************************** * FUNCTION begin_elem_val * * Write a start or empty XML tag to the specified session * * INPUTS: * scb == session control block * msg == top header from message in progress * val == value node to use * indent == number of chars to indent after a newline * == -1 means no newline or indent * == 0 means just newline * empty == true if the value has no content == false if it has content or a leaf-list with defaults enabled * RETURNS: * none *********************************************************************/ static void begin_elem_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 indent, boolean empty) { const xmlChar *pfix, *elname; const dlq_hdr_t *attrQ; const xpath_pcb_t *xpathpcb; boolean xneeded, xmlcontent, isdefault; xmlns_id_t nsid, parent_nsid; status_t res; uint32 retcount; elname = val->name; nsid = val->nsid; attrQ = &val->metaQ; xpathpcb = NULL; isdefault = FALSE; if (typ_is_simple(val->btyp)) { isdefault = val_is_default(val); } if (val->parent) { parent_nsid = val->parent->nsid; } else if (!msg->useprefix && (val->nsid == xmlns_nc_id())) { /* hack: using default prefix and the client or server * sometimes sends detached data structures with no * parent. Assume that the NETCONF namespace is * the parent */ parent_nsid = xmlns_nc_id(); } else { parent_nsid = 0; } xmlcontent = (val->btyp == NCX_BT_INSTANCE_ID) || obj_is_xpath_string(val->obj); if (xmlcontent) { xpathpcb = val_get_const_xpathpcb(val); } ses_indent(scb, indent); /* start the element and write the prefix, if any */ ses_putchar(scb, '<'); pfix = xml_msg_get_prefix(msg, parent_nsid, nsid, val, &xneeded); if (pfix && msg->useprefix) { ses_putstr(scb, pfix); ses_putchar(scb, ':'); } /* write the element name */ ses_putstr(scb, elname); /* write the wda:default element if needed * hack: bypass usual checking for xmlns needed because the * xml_msg_build_prefix_map function added the wda * namespace attribute already if it was needed */ if (isdefault && msg->withdef == NCX_WITHDEF_REPORT_ALL_TAGGED) { const xmlChar *wpfix; boolean xneeded2; wpfix = xml_msg_get_prefix(msg, parent_nsid, xmlns_wda_id(), NULL, &xneeded2); if (wpfix) { ses_putchar(scb, ' '); ses_putstr(scb, wpfix); ses_putchar(scb, ':'); ses_putstr(scb, NCX_EL_DEFAULT); ses_putstr(scb, (const xmlChar *)"=\"true\""); } } if (xneeded || xmlcontent || (attrQ && !dlq_empty(attrQ))) { if (indent >= 0) { indent += ses_indent_count(scb); } if (attrQ) { write_attrs(scb, msg, attrQ, FALSE, val, indent, nsid); } if (xneeded) { if (!attrQ || dlq_empty(attrQ)) { indent = -1; } write_xmlns_decl(scb, (msg->useprefix) ? pfix : NULL, nsid, indent); } if (xpathpcb) { /* generate all the default xmlns directives needed * for the content following this start tag to be valid */ res = handle_xpath_start_tag(scb, xpathpcb, indent, &retcount); /* don't care about the retcount value * terminating the start tag here no matter what */ if (res != NO_ERR) { /* not expecting anything except a buffer overflow * from too many namespaces in the same XPath expr */ SET_ERROR(res); } } } /* finish up the element */ if (empty) { ses_putchar(scb, '/'); } ses_putchar(scb, '>'); /* hack in XMLDOC mode to get more readable XSD output */ if (empty && scb->mode==SES_MODE_XMLDOC && indent < (3*ses_indent_count(scb))) { ses_putchar(scb, '\n'); } } /* begin_elem_val */ /******************************************************************************/ /** * Write out an NCX Enum value. * * \param scb the session control block. * \param out the value to write. */ static void write_enum_val( ses_cb_t *scb, const val_value_t* out ) { if (VAL_ENUM_NAME(out)) { ses_putstr(scb, VAL_ENUM_NAME(out)); } else { SET_ERROR(ERR_INTERNAL_VAL); } } /******************************************************************************/ /** * Write out an NCX Numeric value. * * \param scb the session control block. * \param out the value to write. */ static void write_num_val( ses_cb_t *scb, const val_value_t* out ) { xmlChar buff[NCX_MAX_NUMLEN]; uint32 len; status_t res; res = ncx_sprintf_num( buff, &out->v.num, out->btyp, &len ); if (res == NO_ERR) { ses_putstr(scb, buff); } else { SET_ERROR(res); } } /******************************************************************************/ /** * Write out an NCX String or InstanceID value. * * \param scb the session control block. * \param out the value to write. * \param indent the start indent amount if indent is enabled. */ static void write_string_val( ses_cb_t *scb, const val_value_t* out, int32 indent ) { /* if the content has XPath or QName content, then the begin_elem_val * function should have already printed all the required xmlns * attributes so just print the string value with prefixes and all */ if (VAL_STR(out)) { if ( !fit_on_line(scb, out) && (indent > 0) ) { ses_indent(scb, indent); } ses_putcstr(scb, VAL_STR(out), indent); } else { SET_ERROR(ERR_INTERNAL_VAL); } } /******************************************************************************/ /** * Write out an NCX ID Ref value. * * \param scb the session control block. * \param msg the message (xml_msg_hdr_t) being processed. * \param out the value to write. */ static void write_idref_val( ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t* out ) { const xmlChar *pfix; bool xneeded; /* counting on xmlns decl to be in ancestor node because the xneeded node * is being ignored here. */ pfix = xml_msg_get_prefix( msg, ( (out->parent) ? out->parent->nsid : 0 ), out->v.idref.nsid, out, &xneeded ); if (pfix) { /* need the prefix all the time on XML content */ ses_putstr(scb, pfix); ses_putchar(scb, XMLNS_SEPCH); } ses_putstr(scb, out->v.idref.name); } /******************************************************************************/ /** * Write out an NCX Binary value. * * \param scb the session control block. * \param out the value to write. * \param indent the start indent amount if indent is enabled. */ static void write_binary_val( ses_cb_t *scb, const val_value_t* out, int32 indent ) { xmlChar* binStr = val_make_sprintf_string( out ); if ( !binStr ) { SET_ERROR(ERR_INTERNAL_MEM); return; } ses_putcstr(scb, binStr, indent); m__free(binStr); } /******************************************************************************/ /** * Write out an NCX String from a list or bits value. * * \param scb the session control block. * \param listmem the value to write. * \param indent the start indent amount if indent is enabled. * \param first flag indicating if his is the first element in the list. */ static void write_slist_bits_string_val( ses_cb_t *scb, const ncx_lmem_t* listmem, int32 indent, boolean first ) { boolean wspace = FALSE; uint32 len; /* handle indent+double quote or space for non-strings */ /* get len and whitespace flag */ len = xml_strlen_sp( listmem->val.str, &wspace); /* check special case -- empty string handle it here instead of * going through the loop */ if (!len) { if ( !first ) { ses_putstr(scb, (const xmlChar *)" \"\""); } else { ses_putstr(scb, (const xmlChar *)"\"\""); } return; } /* check if double quotes needed */ if (wspace) { ses_putchar(scb, '\"'); } /* print the list member content as a string */ ses_putcstr( scb, listmem->val.str, indent ); /* check finish quoted string */ if (wspace) { ses_putchar(scb, '\"'); } } /******************************************************************************/ /** * Write out an NCX numeric value from a list or bits value. * * \param scb the session control block. * \param listmem the value to write. * \param numtype the type of numeric value to write. * \param indent the start indent amount if indent is enabled. */ static void write_slist_bits_num_val( ses_cb_t *scb, const ncx_lmem_t* listmem, ncx_btype_t numtype, int32 indent ) { // Number type xmlChar buff[NCX_MAX_NUMLEN]; uint32 len; (void)ncx_sprintf_num( buff, &listmem->val.num, numtype, &len ); ses_putcstr(scb, buff, indent); } /******************************************************************************/ /** * Write out an NCX Binary value. * * \param scb the session control block. * \param out the value to write. * \param indent the start indent amount if indent is enabled. */ static void write_slist_bits_val( ses_cb_t *scb, const val_value_t* out, int32 indent ) { boolean first = TRUE; const ncx_lmem_t *listmem; for( listmem = (const ncx_lmem_t *) dlq_firstEntry(&out->v.list.memQ); listmem != NULL; listmem = (const ncx_lmem_t *)dlq_nextEntry(listmem)) { if ( !first ) { if (SES_LINELEN(scb) > SES_LINESIZE(scb)) { ses_indent(scb, indent); } else { ses_putchar(scb, ' '); } } switch ( out->v.list.btyp ) { case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: /* FALL THRU */ // String type write_slist_bits_string_val( scb, listmem, indent, first ); break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: // numeric type write_slist_bits_num_val( scb, listmem, out->v.list.btyp, indent ); break; case NCX_BT_BITS: ses_putstr(scb, listmem->val.bit.name); break; case NCX_BT_ENUM: ses_putcstr(scb, listmem->val.enu.name, -1); break; case NCX_BT_BOOLEAN: ses_putcstr( scb, ( listmem->val.boo ? NCX_EL_TRUE : NCX_EL_FALSE ), indent ); break; default: SET_ERROR(ERR_INTERNAL_VAL); } first = false; } } /******************************************************************************/ /** * Write out an NCX String from a list or InstanceID value. * * \param scb the session control block. * \param msg the message (xml_msg_hdr_t) being processed. * \param out the value to write. * \param indent the start indent amount if indent is enabled. * \param testfn callback function to use, NULL if not used */ static void write_child_values( ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *out, int32 indent, val_nodetest_fn_t testfn ) { val_value_t *chval; for (chval = val_get_first_child(out); chval != NULL; chval = val_get_next_child(chval)) { xml_wr_full_check_val( scb, msg, chval, indent, testfn ); } } static boolean not_non_presence_container (ncx_withdefaults_t withdef, boolean realtest, val_value_t *node) { if (node->obj->objtype != OBJ_TYP_CONTAINER) { return TRUE; } if (obj_get_presence_string(node->obj)) { return TRUE; } return FALSE; } /** * Check a leaf-list node's ancestry to determine if defaults are in use. * See RFC 7950 section 7.7.2. * * \param val leaf-list we have created with default value * \param real_parent value that we searched for empty leaf-lists */ static boolean leaf_list_use_default (val_value_t *val, val_value_t *real_parent) { val_value_t *ancestor; val_value_t *child; obj_template_t *ancestor_obj = NULL; obj_template_t *choice_obj; obj_template_t *defcase; boolean other_case = FALSE; /* "If no such ancestor exists in the schema tree, the default values * MUST be used." */ ancestor = val_find_nearest_ancestor(val, &ancestor_obj); if (ancestor == NULL) { return TRUE; } /* If the nearest ancestor that is not a non-presence container is the * real parent value that we searched for empty leaf-lists, use _that_ * value instead of the dummy value because it may be necessary to look * for children of other CASEs, etc. */ if (ancestor->obj == real_parent->obj) { ancestor = real_parent; } /* "Otherwise, the default values MUST be used if the ancestor node * exists in the data tree." * * Not a case node - we're done. */ if (ancestor_obj->objtype != OBJ_TYP_CASE) { return TRUE; } /* "Otherwise, if this ancestor is a case node, the default values * MUST be used if any node from the case exists in the data tree * or the case node is the choice's default case, and if no nodes * from any other case exist in the data tree." * * * This is a CASE node. Do any nodes from this case exist in the * data tree? */ choice_obj = ancestor_obj->parent; for (child = val_get_first_child(ancestor); child; child = val_get_next_child(child)) { /* Don't look our temporary leaf-list value. */ if (child == val) { continue; /* shouldn't need this any more */ } if (child->obj->parent == ancestor_obj) return TRUE; /* Is this a child of a different CASE in this OPTION? If so, * we can stop since there won't be any children of the case * we're looking for. */ if (child->obj->objtype == OBJ_TYP_CASE && child->obj->parent == choice_obj) { other_case = TRUE; break; } } /* This CASE node has no children. Is it the default? If so, * use the leaf-list defaults IF no other case in the choice * has any children. */ defcase = obj_get_default_case(choice_obj); if (ancestor_obj == defcase && other_case == FALSE) { return TRUE; } /* So, this is a CASE with no child nodes that is not the default. * do _not_ use the leaf-list defaults. */ return FALSE; } /** * Evaluate conditionals for a leaf-list as if it was actually present * in the data tree. * * \param val candidate leaf-list value node to examine */ static boolean leaf_list_check_conditionals (val_value_t *val) { val_value_t *root = val; boolean condres; uint32 count = 0; status_t res; while (root->parent && !obj_is_root(root->obj)) root = root->parent; /* Evaluate when statements */ res = val_check_obj_when(val->parent, root, NULL, val->obj, &condres, &count); if (res != NO_ERR) { log_error("\n%s: failed to check when statements"); return FALSE; } if (count > 0 && condres == FALSE) { return FALSE; } return obj_is_enabled(val->obj); } /** * Check for leaf-lists in the schema tree that do not exist in the data tree * and format the default values. Leaf-list defaults are handled differently * than leaf defaults in that the default values are used only when no leaf-list * values exist in the data tree. See RFC 7950 section 7.7.2. * * \param scb the session control block. * \param msg the message (xml_msg_hdr_t) being processed. * \param out the value to search for empty child leaf-lists. * \param indent the start indent amount if indent is enabled. * \param testfn callback function to use, NULL if not used */ static void write_child_leaf_list_defaults (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *out, int32 indent, val_nodetest_fn_t testfn) { val_value_t *dummy; /* temporary container to hold new leaf-list values */ val_value_t *llval; val_value_t *chval; obj_template_t *chobj; obj_leaflist_defval_t *cur; if (msg->withdef != NCX_WITHDEF_REPORT_ALL_TAGGED && msg->withdef != NCX_WITHDEF_REPORT_ALL) { return; } if (out->obj == NULL) { return; } dummy = val_new_value(); if (dummy == NULL) { SET_ERROR(ERR_INTERNAL_MEM); return; } val_init_from_template(dummy, out->obj); /* search for child schema nodes of objtype leaf-list */ for (chobj = obj_first_child_deep(out->obj); chobj; chobj = obj_next_child_deep(chobj)) { if (chobj->objtype != OBJ_TYP_LEAF_LIST) { continue; } /* make sure there is at least one default value */ cur = obj_get_first_default(chobj); if (cur == NULL) { continue; } chval = val_find_child(out, obj_get_mod_name(chobj), obj_get_name(chobj)); if (chval != NULL) { continue; } /* accumulate one value for each default in the schema */ for (; cur; cur = obj_get_next_default(cur)) { status_t res; res = cli_parse_parm(NULL, dummy, chobj, cur->defval, TRUE); if (res != NO_ERR) { val_free_value(dummy); SET_ERROR(res); return; } } } /* output the accumulated values */ for (llval = val_get_first_child(dummy); llval; llval = val_get_next_child(llval)) { if (!leaf_list_use_default(llval, out)) { continue; } if (leaf_list_check_conditionals(llval) == FALSE) { continue; } /* make sure tag added with with-default=report-all-tagged */ llval->flags |= VAL_FL_DEFSET; xml_wr_full_check_val(scb, msg, llval, indent, testfn); } val_free_value(dummy); } /******************************************************************** * FUNCTION write_check_val * * Write an NCX value in XML encoding * while checking nodes for suppression of output with * the supplied test fn * * !!! NOTE !!! * * This function generates the contents of the val_value_t * but not the top node itself. This function is called * recursively and this is the intended behavior. * * To generate XML for an entire val_value_t, including * the top-level node, use the xml_wr_full_val fn. * * If the acm_cache and acm_cbfn fields are set in * the msg header then access control will be checked * If FALSE, then nothing will be written to the output session * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * indent == start indent amount if indent enabled * testcb == callback function to use, NULL if not used * acmcheck == TRUE if the ACM check should be done * * RETURNS: * none *********************************************************************/ static void write_check_val ( ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 indent, val_nodetest_fn_t testfn, boolean acmcheck ) { val_value_t *out; status_t res = NO_ERR; boolean malloced = FALSE; // Handle virtual values and check access control out = val_get_value(scb, msg, val, testfn, acmcheck, &malloced, &res); if ( !out || res != NO_ERR) { if (out && malloced) { val_free_value(out); } /* FIXME: ignore error return */ return; } switch (out->btyp) { case NCX_BT_EXTERN: val_write_extern(scb, out); break; case NCX_BT_INTERN: val_write_intern(scb, out); break; case NCX_BT_ENUM: write_enum_val( scb, out ); break; case NCX_BT_EMPTY: if (out->v.boo) { xml_wr_empty_elem( scb, msg, val_get_parent_nsid(out), out->nsid, out->name, -1 ); } break; case NCX_BT_BOOLEAN: ses_putcstr( scb, ( out->v.boo ? NCX_EL_TRUE : NCX_EL_FALSE ), indent ); break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: write_num_val( scb, out ); break; case NCX_BT_LEAFREF: case NCX_BT_INSTANCE_ID: case NCX_BT_STRING: write_string_val( scb, out, indent ); break; case NCX_BT_IDREF: write_idref_val( scb, msg, out ); break; case NCX_BT_BINARY: write_binary_val( scb, out , indent ); break; case NCX_BT_BITS: case NCX_BT_SLIST: write_slist_bits_val( scb, out, indent ); break; case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_CONTAINER: case NCX_BT_LIST: case NCX_BT_CHOICE: case NCX_BT_CASE: write_child_values( scb, msg, out, indent, testfn ); write_child_leaf_list_defaults( scb, msg, out, indent, testfn ); break; default: SET_ERROR(ERR_INTERNAL_VAL); } if (malloced && out) { val_free_value(out); } } /* write_check_val */ /******************************************************************** * FUNCTION begin_elem_ex * * Write a start or empty XML tag to the specified session * * INPUTS: * scb == session control block * msg == top header from message in progress * parent_nsid == namespace ID of the parent element, if known * nsid == namespace ID of the element to write * elname == unqualified name of element to write * attrQ == Q of xml_attr_t or val_value_t records to write in * the element; NULL == none * isattrq == TRUE if the qQ contains xml_attr_t nodes * FALSE if the Q contains val_value_t nodes (metadata) * indent == number of chars to indent after a newline * == -1 means no newline or indent * == 0 means just newline * empty == TRUE for empty node * == FALSE for start node * qname_nsid == namespace ID if the content is a QName * and an xmlns with a prefix is needed * isdefault == TRUE if the XML value node represents a default leaf * == FALSE otherwise * RETURNS: * none *********************************************************************/ static void begin_elem_ex (ses_cb_t *scb, xml_msg_hdr_t *msg, xmlns_id_t parent_nsid, xmlns_id_t nsid, const xmlChar *elname, const dlq_hdr_t *attrQ, boolean isattrq, int32 indent, boolean empty, xmlns_id_t qname_nsid, boolean isdefault) { const xmlChar *pfix, *qname_pfix; boolean xneeded; #ifdef DEBUG if (!scb || !msg || !elname) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif ses_indent(scb, indent); /* start the element and write the prefix, if any */ ses_putchar(scb, '<'); pfix = xml_msg_get_prefix(msg, parent_nsid, nsid, NULL, &xneeded); if (pfix && msg->useprefix) { ses_putstr(scb, pfix); ses_putchar(scb, ':'); } /* write the element name */ ses_putstr(scb, elname); /* write the wda:default element if needed * hack: bypass usual checking for xmlns needed because the * xml_msg_build_prefix_map function added the wda * namespace attribute already if it was needed */ if (isdefault && msg->withdef == NCX_WITHDEF_REPORT_ALL_TAGGED) { const xmlChar *wpfix; boolean xneeded2; wpfix = xml_msg_get_prefix(msg, parent_nsid, xmlns_wda_id(), NULL, &xneeded2); if (wpfix) { ses_putchar(scb, ' '); ses_putstr(scb, wpfix); ses_putchar(scb, ':'); ses_putstr(scb, NCX_EL_DEFAULT); ses_putstr(scb, (const xmlChar *)"=\"true\" "); } } if (xneeded || qname_nsid || (attrQ && !dlq_empty(attrQ))) { if (indent >= 0) { indent += ses_indent_count(scb); } if (attrQ) { write_attrs(scb, msg, attrQ, isattrq, NULL, indent, nsid); } if (xneeded) { if (!attrQ || dlq_empty(attrQ)) { indent = -1; } write_xmlns_decl(scb, (msg->useprefix) ? pfix : NULL, nsid, indent); } if (qname_nsid) { qname_pfix = xml_msg_get_prefix_xpath(msg, qname_nsid); if (qname_pfix == NULL) { SET_ERROR(ERR_INTERNAL_VAL); } else { /* force an xmlns attribute with a prefix */ write_xmlns_decl(scb, qname_pfix, qname_nsid, indent); } } } /* finish up the element */ if (empty) { ses_putchar(scb, '/'); } ses_putchar(scb, '>'); /* hack in XMLDOC mode to get more readable XSD output */ if (empty && scb->mode==SES_MODE_XMLDOC && indent < (3*ses_indent_count(scb))) { ses_putchar(scb, '\n'); } } /* begin_elem_ex */ /************ E X T E R N A L F U N C T I O N S **************/ /******************************************************************** * FUNCTION xml_wr_buff * * Write some xmlChars to the specified session * * INPUTS: * scb == session control block to start msg * buff == buffer to write * bufflen == number of bytes to write, not including any * EOS char at the end of the buffer * RETURNS: * none *********************************************************************/ void xml_wr_buff (ses_cb_t *scb, const xmlChar *buff, uint32 bufflen) { uint32 i; assert( scb && "scb is NULL!" ); assert( buff && "buff is NULL!" ); for (i=0; iuseprefix) { pfix = xml_msg_get_prefix(msg, 0, nsid, NULL, &xneeded); if (pfix) { ses_putstr(scb, pfix); ses_putchar(scb, ':'); } } /* write the element name */ ses_putstr(scb, elname); /* finish up the element */ ses_putchar(scb, '>'); } /* xml_wr_end_elem */ /******************************************************************** * FUNCTION xml_wr_string_elem * * Write a start tag, simple string content, and an end tag * to the specified session. A flag element and * ename will vary from this format. * * Simple content nodes are completed on a single line to * prevent introduction of extra whitespace * * INPUTS: * scb == session control block * msg == header from message in progress * str == simple string to write as element content * parent_nsid == namespace ID of the parent element * nsid == namespace ID of the element to write * elname == unqualified name of element to write * attrQ == Q of xml_attr_t records to write in * the element; NULL == none * isattrq == TRUE for Q of xml_attr_t, FALSE for val_value_t * indent == number of chars to indent after a newline * == -1 means no newline or indent * == 0 means just newline * * RETURNS: * none *********************************************************************/ void xml_wr_string_elem (ses_cb_t *scb, xml_msg_hdr_t *msg, const xmlChar *str, xmlns_id_t parent_nsid, xmlns_id_t nsid, const xmlChar *elname, const dlq_hdr_t *attrQ, boolean isattrq, int32 indent) { assert( scb && "scb is NULL" ); assert( msg && "msg is NULL" ); assert( str && "str is NULL" ); assert( elname && "elname is NULL" ); begin_elem_ex(scb, msg, parent_nsid, nsid, elname, attrQ, isattrq, indent, FALSE, 0, FALSE); ses_putcstr(scb, str, -1); xml_wr_end_elem(scb, msg, nsid, elname, -1); } /* xml_wr_string_elem */ /******************************************************************** * FUNCTION xml_wr_qname_elem * * Write a start tag, QName string content, and an end tag * to the specified session. * * The ses_start_msg must be called before this * function, in order for it to allow any writes * * INPUTS: * scb == session control block * msg == header from message in progres * val_nsid == namespace ID of the QName prefix * str == local-name part of the QName * parent_nsid == namespace ID of the parent element * nsid == namespace ID of the element to write * elname == unqualified name of element to write * attrQ == Q of xml_attr_t records to write in * the element; NULL == none * isattrq == TRUE for Q of xml_attr_t, FALSE for val_value_t * indent == number of chars to indent after a newline * == -1 means no newline or indent * == 0 means just newline * isdefault == TRUE if the XML value node represents a default leaf * == FALSE otherwise * RETURNS: * none *********************************************************************/ void xml_wr_qname_elem (ses_cb_t *scb, xml_msg_hdr_t *msg, xmlns_id_t val_nsid, const xmlChar *str, xmlns_id_t parent_nsid, xmlns_id_t nsid, const xmlChar *elname, const dlq_hdr_t *attrQ, boolean isattrq, int32 indent, boolean isdefault) { const xmlChar *pfix; assert( scb && "scb is NULL" ); assert( msg && "msg is NULL" ); assert( str && "str is NULL" ); assert( elname && "elname is NULL" ); begin_elem_ex(scb, msg, parent_nsid, nsid, elname, attrQ, isattrq, indent, FALSE, val_nsid, isdefault); pfix = xml_msg_get_prefix_xpath(msg, val_nsid); if (pfix) { /* should always be non-NULL * should already have the xmlns:pfix * in the start tag * do not check msg->useprefix here! */ ses_putstr(scb, pfix); ses_putchar(scb, XMLNS_SEPCH); } ses_putcstr(scb, str, -1); xml_wr_end_elem(scb, msg, nsid, elname, -1); } /* xml_wr_qname_elem */ /******************************************************************** * FUNCTION xml_wr_check_val * * Write a YANG value in XML encoding * while checking nodes for suppression of output with * the supplied test fn * * !!! NOTE !!! * * This function generates the contents of the val_value_t * but not the top node itself. This function is called * recursively and this is the intended behavior. * * To generate XML for an entire val_value_t, including * the top-level node, use the xml_wr_full_val fn. * * If the acm_cache and acm_cbfn fields are set in * the msg header then access control will be checked * If FALSE, then nothing will be written to the output session * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * indent == start indent amount if indent enabled * testcb == callback function to use, NULL if not used * * RETURNS: * none *********************************************************************/ void xml_wr_check_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 indent, val_nodetest_fn_t testfn) { assert( scb && "scb is NULL" ); assert( msg && "msg is NULL" ); assert( val && "val is NULL" ); write_check_val(scb, msg, val, indent, testfn, TRUE); } /* xml_wr_check_val */ /******************************************************************** * FUNCTION xml_wr_val * * output val_value_t node contents only * Write an NCX value node in XML encoding * See xml_wr_check_write for full details of this fn. * It is the same, except a NULL testfn is supplied. * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * indent == start indent amount if indent enabled * * RETURNS: * none *********************************************************************/ void xml_wr_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 indent) { xml_wr_check_val(scb, msg, val, indent, NULL); } /* xml_wr_val */ /******************************************************************** * FUNCTION xml_wr_full_check_val * * generate entire val_value_t *w/filter) * Write an entire val_value_t out as XML, including the top level * Using an optional testfn to filter output * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * indent == start indent amount if indent enabled * testcb == callback function to use, NULL if not used * * RETURNS: * none *********************************************************************/ void xml_wr_full_check_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 indent, val_nodetest_fn_t testfn) { val_value_t *out; status_t res; boolean isdefault, malloced; assert( scb && "scb is NULL" ); assert( msg && "msg is NULL" ); assert( val && "val is NULL" ); malloced = FALSE; res = NO_ERR; out = val_get_value(scb, msg, val, testfn, TRUE, &malloced, &res); if (!out) return; if (res != NO_ERR) { if (res == ERR_NCX_SKIPPED) { res = NO_ERR; /* retained (see next comment) */ } if (malloced) { val_free_value(out); } /* FIXME: error exit ignored */ return; } isdefault = FALSE; if (typ_is_simple(out->btyp)) { isdefault = val_is_default(out); } if (out->btyp==NCX_BT_EMPTY && !VAL_BOOL(out)) { /* this is a false (not present) flag */ ; } else if (out->btyp == NCX_BT_IDREF) { /* write a complete QName element */ xml_wr_qname_elem(scb, msg, out->v.idref.nsid, out->v.idref.name, (out->parent) ? out->parent->nsid : 0, out->nsid, out->name, &out->metaQ, FALSE, indent, isdefault); } else if (val_has_content(out) || xml_wr_default_leaf_list(msg, out)) { /* write the top-level start node */ begin_elem_val(scb, msg, out, indent, FALSE); /* write the value node contents; skip ACM on this node */ write_check_val(scb, msg, out, indent+ses_indent_count(scb), testfn, FALSE); /* write the top-level end node */ xml_wr_end_elem(scb, msg, out->nsid, out->name, fit_on_line(scb, out) ? -1 : indent); } else { /* write the top-level empty node */ begin_elem_val(scb, msg, out, indent, TRUE); } if (malloced) { val_free_value(out); } } /* xml_wr_full_check_val */ /******************************************************************** * FUNCTION xml_wr_full_val * * generate entire val_value_t * Write an entire val_value_t out as XML, including the top level * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * indent == start indent amount if indent enabled * * RETURNS: * none *********************************************************************/ void xml_wr_full_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 indent) { xml_wr_full_check_val(scb, msg, val, indent, NULL); } /* xml_wr_full_val */ /******************************************************************** * FUNCTION xml_wr_check_open_file * * Write the specified value to an open FILE in XML format * * INPUTS: * fp == open FILE control block * val == value for output * attrs == top-level attributes to generate * docmode == TRUE if XML_DOC output mode should be used * == FALSE if XML output mode should be used * xmlhdr == TRUE if directive should be output * == FALSE if not * withns == TRUE if xmlns attributes should be used * FALSE to leave them out * startindent == starting indent point * indent == indent amount (0..9 spaces) * testfn == callback test function to use * * RETURNS: * status *********************************************************************/ status_t xml_wr_check_open_file (FILE *fp, val_value_t *val, xml_attrs_t *attrs, boolean docmode, boolean xmlhdr, boolean withns, int32 startindent, int32 indent, val_nodetest_fn_t testfn) { assert( fp && "fp is NULL" ); assert( val && "val is NULL" ); indent = min(indent, 9); /* get a dummy session control block */ ses_cb_t *scb = ses_new_dummy_scb(); if (!scb) { return ERR_INTERNAL_MEM; } scb->fp = fp; scb->indent = indent; if (withns == FALSE) { ses_set_xml_nons(scb); } /* get a dummy output message */ rpc_msg_t *msg = rpc_new_out_msg(); if (!msg) { scb->fp = NULL; /* do not close the file */ ses_free_scb(scb); return ERR_INTERNAL_MEM; } /* hack -- need a queue because there is no top * element which this usually shadows */ xml_attrs_t myattrs; xml_init_attrs(&myattrs); msg->rpc_in_attrs = (attrs) ? attrs : &myattrs; if (withns == FALSE) { /* probably already false, but make sure */ msg->mhdr.useprefix = FALSE; } ses_mode_t sesmode = SES_MODE_NONE; /* XML output mode will add more whitespace if this is ncxdump calling */ if (docmode) { sesmode = ses_get_mode(scb); ses_set_mode(scb, SES_MODE_XMLDOC); } status_t res = NO_ERR; boolean anyout = FALSE; /* send the XML declaration */ if (xmlhdr) { res = ses_start_msg(scb); if (res == NO_ERR) { anyout = TRUE; } } /* setup an empty prefix map */ if (res == NO_ERR) { res = xml_msg_build_prefix_map(&msg->mhdr, msg->rpc_in_attrs, FALSE, FALSE); } /* cannot use xml_wr_full_val because that * function assumes the attrQ is val_value_t * but it is really xml_attr_t * * !!! not handling i-i and XPath strings yet !!! */ if (res == NO_ERR) { boolean hascontent = TRUE; if (!val_has_content(val)) { /* print empty element */ xml_wr_begin_elem_ex(scb, &msg->mhdr, 0, val->nsid, val->name, attrs, TRUE, startindent, TRUE); hascontent = FALSE; } else if (val->btyp == NCX_BT_IDREF) { /* print start QName element */ xml_wr_qname_elem(scb, &msg->mhdr, val->v.idref.nsid, val->v.idref.name, 0, val->nsid, val->name, attrs, TRUE, startindent, FALSE); } else { /* print start normal string node element */ xml_wr_begin_elem_ex(scb, &msg->mhdr, 0, val->nsid, val->name, attrs, TRUE, startindent, FALSE); } anyout = TRUE; if (hascontent) { boolean fitoneline = fit_on_line(scb, val); /* output the contents of the value */ xml_wr_check_val(scb, &msg->mhdr, val, (fitoneline) ? -1 : indent, testfn); /* generate the end tag */ xml_wr_end_elem(scb, &msg->mhdr, val->nsid, val->name, (fitoneline) ? -1 : startindent); } } /* finish the message, should be NO-OP */ if (anyout) { ses_finish_msg(scb); } if (docmode) { ses_set_mode(scb, sesmode); } /* clean up and exit */ rpc_free_msg(msg); scb->fp = NULL; /* do not close the file */ ses_free_scb(scb); xml_clean_attrs(&myattrs); return res; } /* xml_wr_check_open_file */ /******************************************************************** * FUNCTION xml_wr_check_file * * Write the specified value to a FILE in XML format * * INPUTS: * filespec == exact path of filename to open * val == value for output * attrs == top-level attributes to generate * docmode == TRUE if XML_DOC output mode should be used * == FALSE if XML output mode should be used * xmlhdr == TRUE if directive should be output * == FALSE if not * withns == TRUE if xmlns attributes should be used * FALSE to leave them out * startindent == starting indent point * indent == indent amount (0..9 spaces) * testfn == callback test function to use * * RETURNS: * status *********************************************************************/ status_t xml_wr_check_file (const xmlChar *filespec, val_value_t *val, xml_attrs_t *attrs, boolean docmode, boolean xmlhdr, boolean withns, int32 startindent, int32 indent, val_nodetest_fn_t testfn) { FILE *fp; status_t res; assert( filespec && "filespec is NULL" ); assert( val && "val is NULL" ); assert( attrs && "attrs is NULL" ); fp = fopen((const char *)filespec, "w"); if (!fp) { log_error("\nError: Cannot open XML file '%s'", filespec); return ERR_FIL_OPEN; } res = xml_wr_check_open_file(fp, val, attrs, docmode, xmlhdr, withns, startindent, indent, testfn); fclose(fp); return res; } /* xml_wr_check_file */ /******************************************************************** * FUNCTION xml_wr_file * * Write the specified value to a FILE in XML format * * INPUTS: * filespec == exact path of filename to open * val == value for output * attrs == top-level attributes to generate * docmode == TRUE if XML_DOC output mode should be used * == FALSE if XML output mode should be used * xmlhdr == TRUE if directive should be output * == FALSE if not * withns == TRUE if xmlns attributes should be used * FALSE to leave them out * startindent == starting indent point * indent == indent amount (0..9 spaces) * * RETURNS: * status *********************************************************************/ status_t xml_wr_file (const xmlChar *filespec, val_value_t *val, xml_attrs_t *attrs, boolean docmode, boolean xmlhdr, boolean withns, int32 startindent, int32 indent) { return xml_wr_check_file(filespec, val, attrs, docmode, xmlhdr, withns, startindent, indent, NULL); } /* xml_wr_file */ /* END file xml_wr.c */ yuma123_2.14/netconf/src/ncx/op.c0000664000175000017500000003211714770023131016724 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: op.c NETCONF Protocol Operations ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 02may05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_op #include "op.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif /******************************************************************** * FUNCTION op_method_name * * Get the keyword for the specified STD RPC method * * INPUTS: * op_id == proto op ID * RETURNS: * string for the operation, or "none" or "illegal" *********************************************************************/ const xmlChar * op_method_name (op_method_t op_id) { switch (op_id) { case OP_NO_METHOD: return NCX_EL_NONE; case OP_GET_CONFIG: return NCX_EL_GET_CONFIG; case OP_EDIT_CONFIG: return NCX_EL_EDIT_CONFIG; case OP_COPY_CONFIG: return NCX_EL_COPY_CONFIG; case OP_DELETE_CONFIG: return NCX_EL_DELETE_CONFIG; case OP_LOCK: return NCX_EL_LOCK; case OP_UNLOCK: return NCX_EL_UNLOCK; case OP_GET: return NCX_EL_GET; case OP_CLOSE_SESSION: return NCX_EL_CLOSE_SESSION; case OP_KILL_SESSION: return NCX_EL_KILL_SESSION; case OP_COMMIT: return NCX_EL_COMMIT; case OP_DISCARD_CHANGES: return NCX_EL_DISCARD_CHANGES; case OP_VALIDATE: return NCX_EL_VALIDATE; case OP_CANCEL_COMMIT: return NCX_EL_CANCEL_COMMIT; default: return NCX_EL_ILLEGAL; } } /* op_method_name */ /******************************************************************** * FUNCTION op_editop_name * * Get the keyword for the specified op_editop_t enumeration * * INPUTS: * ed_id == edit operation ID * RETURNS: * string for the edit operation type, or "none" or "illegal" *********************************************************************/ const xmlChar * op_editop_name (op_editop_t ed_id) { switch (ed_id) { case OP_EDITOP_NONE: return NCX_EL_NONE; case OP_EDITOP_MERGE: return NCX_EL_MERGE; case OP_EDITOP_REPLACE: return NCX_EL_REPLACE; case OP_EDITOP_CREATE: return NCX_EL_CREATE; case OP_EDITOP_DELETE: return NCX_EL_DELETE; case OP_EDITOP_LOAD: return NCX_EL_LOAD; case OP_EDITOP_COMMIT: return NCX_EL_COMMIT; case OP_EDITOP_REMOVE: return NCX_EL_REMOVE; default: return (const xmlChar *) "illegal"; } } /* op_editop_name */ /******************************************************************** * FUNCTION op_editop_id * * Get the ID for the editop from its keyword * * INPUTS: * opstr == string for the edit operation type * RETURNS: * the op_editop_t enumeration value for the string *********************************************************************/ op_editop_t op_editop_id (const xmlChar *opstr) { #ifdef DEBUG if (!opstr) { SET_ERROR(ERR_INTERNAL_PTR); return OP_EDITOP_NONE; } #endif if (!xml_strcmp(opstr, NCX_EL_MERGE)) { return OP_EDITOP_MERGE; } if (!xml_strcmp(opstr, NCX_EL_REPLACE)) { return OP_EDITOP_REPLACE; } if (!xml_strcmp(opstr, NCX_EL_CREATE)) { return OP_EDITOP_CREATE; } if (!xml_strcmp(opstr, NCX_EL_DELETE)) { return OP_EDITOP_DELETE; } if (!xml_strcmp(opstr, NCX_EL_REMOVE)) { return OP_EDITOP_REMOVE; } if (!xml_strcmp(opstr, NCX_EL_NONE)) { return OP_EDITOP_NONE; } /* internal extensions, should not be used in NETCONF PDU */ if (!xml_strcmp(opstr, NCX_EL_LOAD)) { return OP_EDITOP_LOAD; } if (!xml_strcmp(opstr, NCX_EL_COMMIT)) { return OP_EDITOP_COMMIT; } return OP_EDITOP_NONE; } /* op_editop_id */ /******************************************************************** * FUNCTION op_insertop_name * * Get the keyword for the specified op_insertop_t enumeration * * INPUTS: * ed_id == insert operation ID * * RETURNS: * string for the insert operation type, or "none" or "illegal" *********************************************************************/ const xmlChar * op_insertop_name (op_insertop_t ins_id) { switch (ins_id) { case OP_INSOP_NONE: return NCX_EL_NONE; case OP_INSOP_FIRST: return YANG_K_FIRST; case OP_INSOP_LAST: return YANG_K_LAST; case OP_INSOP_BEFORE: return YANG_K_BEFORE; case OP_INSOP_AFTER: return YANG_K_AFTER; default: return (const xmlChar *) "illegal"; } } /* op_insertop_name */ /******************************************************************** * FUNCTION op_insertop_id * * Get the ID for the insert operation from its keyword * * INPUTS: * opstr == string for the insert operation type * RETURNS: * the op_insertop_t enumeration value for the string *********************************************************************/ op_insertop_t op_insertop_id (const xmlChar *opstr) { #ifdef DEBUG if (!opstr) { SET_ERROR(ERR_INTERNAL_PTR); return OP_INSOP_NONE; } #endif if (!xml_strcmp(opstr, YANG_K_FIRST)) { return OP_INSOP_FIRST; } if (!xml_strcmp(opstr, YANG_K_LAST)) { return OP_INSOP_LAST; } if (!xml_strcmp(opstr, YANG_K_BEFORE)) { return OP_INSOP_BEFORE; } if (!xml_strcmp(opstr, YANG_K_AFTER)) { return OP_INSOP_AFTER; } return OP_INSOP_NONE; } /* op_insertop_id */ /******************************************************************** * FUNCTION op_filtertyp_id * * Get the ID for the filter type from its keyword * * INPUTS: * filstr == string for the filter type * RETURNS: * the op_filtertyp_t enumeration value for the string *********************************************************************/ op_filtertyp_t op_filtertyp_id (const xmlChar *filstr) { #ifdef DEBUG if (!filstr) { SET_ERROR(ERR_INTERNAL_PTR); return OP_FILTER_NONE; } #endif if (!xml_strcmp(filstr, NCX_EL_SUBTREE)) { return OP_FILTER_SUBTREE; } if (!xml_strcmp(filstr, NCX_EL_XPATH)) { return OP_FILTER_XPATH; } return OP_FILTER_NONE; } /* op_filtertyp_id */ /******************************************************************** * FUNCTION op_defop_name * * Get the keyword for the specified op_defop_t enumeration * * INPUTS: * def_id == default operation ID * RETURNS: * string for the default operation type, or "none" or "illegal" *********************************************************************/ const xmlChar * op_defop_name (op_defop_t def_id) { switch (def_id) { case OP_DEFOP_NOT_SET: return NCX_EL_NOT_SET; case OP_DEFOP_NONE: return NCX_EL_NONE; case OP_DEFOP_MERGE: return NCX_EL_MERGE; case OP_DEFOP_REPLACE: return NCX_EL_REPLACE; case OP_DEFOP_NOT_USED: return NCX_EL_NOT_USED; default: return NCX_EL_ILLEGAL; } } /* op_defop_name */ /******************************************************************** * FUNCTION op_defop_id * * Get the ID for the default-operation from its keyword * * INPUTS: * defstr == string for the default operation * RETURNS: * the op_editop_t enumeration value for the string *********************************************************************/ op_editop_t op_defop_id (const xmlChar *defstr) { #ifdef DEBUG if (!defstr) { SET_ERROR(ERR_INTERNAL_PTR); return OP_EDITOP_NONE; } #endif if (!xml_strcmp(defstr, NCX_EL_MERGE)) { return OP_EDITOP_MERGE; } if (!xml_strcmp(defstr, NCX_EL_REPLACE)) { return OP_EDITOP_REPLACE; } if (!xml_strcmp(defstr, NCX_EL_NONE)) { return OP_EDITOP_NONE; } return OP_EDITOP_NONE; } /* op_defop_id */ /******************************************************************** * FUNCTION op_testop_name * * Get the keyword for the specified op_testop_t enumeration * * INPUTS: * test_id == test operation ID * RETURNS: * string for the test operation type, or "none" or "illegal" *********************************************************************/ const xmlChar * op_testop_name (op_testop_t test_id) { switch (test_id) { case OP_TESTOP_NONE: return NCX_EL_NONE; case OP_TESTOP_TESTTHENSET: return NCX_EL_TESTTHENSET; case OP_TESTOP_SET: return NCX_EL_SET; case OP_TESTOP_TESTONLY: return NCX_EL_TESTONLY; default: return NCX_EL_ILLEGAL; } } /* op_testop_name */ /******************************************************************** * FUNCTION op_testop_enum * * Get the enum for the specified op_testop_t string * * INPUTS: * teststr == string for the test operation type * RETURNS: * test_id == test operation ID * *********************************************************************/ op_testop_t op_testop_enum (const xmlChar *teststr) { if (!xml_strcmp(teststr, NCX_EL_TESTONLY)) { return OP_TESTOP_TESTONLY; } else if (!xml_strcmp(teststr, NCX_EL_TESTTHENSET)) { return OP_TESTOP_TESTTHENSET; } else if (!xml_strcmp(teststr, NCX_EL_SET)) { return OP_TESTOP_SET; } else { return OP_TESTOP_NONE; } } /* op_testop_enum */ /******************************************************************** * FUNCTION op_errop_name * * Get the keyword for the specified op_errop_t enumeration * * INPUTS: * err_id == error operation ID * RETURNS: * string for the error operation type, or "none" or "illegal" *********************************************************************/ const xmlChar * op_errop_name (op_errop_t err_id) { switch (err_id) { case OP_ERROP_NONE: return NCX_EL_NONE; case OP_ERROP_STOP: return NCX_EL_STOP_ON_ERROR; case OP_ERROP_CONTINUE: return NCX_EL_CONTINUE_ON_ERROR; case OP_ERROP_ROLLBACK: return NCX_EL_ROLLBACK_ON_ERROR; default: return NCX_EL_ILLEGAL; } } /* op_errop_name */ /******************************************************************** * FUNCTION op_errop_id * * Get the ID for the error-option from its keyword * * INPUTS: * errstr == string for the error option * RETURNS: * the op_errop_t enumeration value for the string *********************************************************************/ op_errop_t op_errop_id (const xmlChar *errstr) { #ifdef DEBUG if (!errstr) { SET_ERROR(ERR_INTERNAL_PTR); return OP_ERROP_NONE; } #endif if (!xml_strcmp(errstr, NCX_EL_STOP_ON_ERROR)) { return OP_ERROP_STOP; } if (!xml_strcmp(errstr, NCX_EL_CONTINUE_ON_ERROR)) { return OP_ERROP_CONTINUE; } if (!xml_strcmp(errstr, NCX_EL_ROLLBACK_ON_ERROR)) { return OP_ERROP_ROLLBACK; } return OP_ERROP_NONE; } /* op_errop_id */ /******************************************************************** * FUNCTION op_defop_id2 * * Get the ID for the default-operation from its keyword * Return the op_defop_t, not the op_editop_t conversion * * INPUTS: * defstr == string for the default operation * RETURNS: * the op_defop_t enumeration value for the string *********************************************************************/ op_defop_t op_defop_id2 (const xmlChar *defstr) { #ifdef DEBUG if (!defstr) { SET_ERROR(ERR_INTERNAL_PTR); return OP_DEFOP_NOT_SET; } #endif if (!xml_strcmp(defstr, NCX_EL_NONE)) { return OP_DEFOP_NONE; } if (!xml_strcmp(defstr, NCX_EL_MERGE)) { return OP_DEFOP_MERGE; } if (!xml_strcmp(defstr, NCX_EL_REPLACE)) { return OP_DEFOP_REPLACE; } if (!xml_strcmp(defstr, NCX_EL_NOT_USED)) { return OP_DEFOP_NOT_USED; } return OP_DEFOP_NOT_SET; } /* op_defop_id2 */ /* END file op.c */ yuma123_2.14/netconf/src/ncx/obj.c0000664000175000017500000130061114770023131017056 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: obj.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 09dec07 abb begun 21jul08 abb start obj-based rewrite ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "dlq.h" #include "grp.h" #include "ncxconst.h" #include "ncx.h" #include "ncx_appinfo.h" #include "ncx_feature.h" #include "ncx_list.h" #include "obj.h" #include "tk.h" #include "typ.h" #include "xml_util.h" #include "xmlns.h" #include "xpath.h" #include "yang.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG /* #define OBJ_CLONE_DEBUG 1 */ /* #define OBJ_MEM_DEBUG 1 */ #endif /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static obj_case_t * new_case (boolean isreal); static void free_case (obj_case_t *cas); static obj_template_t* find_template( dlq_hdr_t*que, const xmlChar *modname, const xmlChar *objname, boolean lookdeep, boolean partialmatch, boolean usecase, boolean altnames, boolean dataonly, uint32 *matchcount ); /******************************************************************** * FUNCTION find_type_in_grpchain * * Search for a defined typedef in the typeQ of each grouping * in the chain * * INPUTS: * obj == blank template to free * * RETURNS: * pointer to found type template, NULL if not found *********************************************************************/ static typ_template_t * find_type_in_grpchain (grp_template_t *grp, const xmlChar *typname) { typ_template_t *typ; grp_template_t *testgrp; typ = ncx_find_type_que(&grp->typedefQ, typname); if (typ) { return typ; } testgrp = grp->parentgrp; while (testgrp) { typ = ncx_find_type_que(&testgrp->typedefQ, typname); if (typ) { return typ; } testgrp = testgrp->parentgrp; } return NULL; } /* find_type_in_grpchain */ /******************************************************************** * FUNCTION init_template * * Initialize the fields in an obj_template_t * * INPUTS: * obj == template to init *********************************************************************/ static void init_template (obj_template_t *obj) { (void)memset(obj, 0x0, sizeof(obj_template_t)); dlq_createSQue(&obj->metadataQ); dlq_createSQue(&obj->appinfoQ); dlq_createSQue(&obj->iffeatureQ); dlq_createSQue(&obj->inherited_iffeatureQ); dlq_createSQue(&obj->inherited_whenQ); dlq_createSQue(&obj->cbsetQ); obj->rpc_cbset=NULL; } /* init_template */ /******************************************************************** * FUNCTION new_blank_template * * Malloc and initialize the fields in a an obj_template_t * Do not malloc or initialize any of the def union pointers * * * RETURNS: * pointer to the malloced and partially initialized struct; * NULL if an error *********************************************************************/ static obj_template_t * new_blank_template (void) { obj_template_t *obj; obj = m__getObj(obj_template_t); if (!obj) { return NULL; } init_template(obj); return obj; } /* new_blank_template */ /******************************************************************** * Clean a Q of xpath_pcb_t entries * * \param mustQ Q of ncx_errinfo_t to delete * *********************************************************************/ static void clean_mustQ (dlq_hdr_t *mustQ) { if ( !mustQ ) { return; } while (!dlq_empty(mustQ)) { xpath_pcb_t *must = (xpath_pcb_t *)dlq_deque(mustQ); xpath_free_pcb(must); } } /* clean_mustQ */ /******************************************************************** * FUNCTION clean_metadataQ * * Clean a Q of obj_metadata_t * * INPUTS: * metadataQ == Q of obj_metadata_t to delete * *********************************************************************/ static void clean_metadataQ (dlq_hdr_t *metadataQ) { obj_metadata_t *meta; while (!dlq_empty(metadataQ)) { meta = (obj_metadata_t *)dlq_deque(metadataQ); obj_free_metadata(meta); } } /* clean_metadataQ */ /******************************************************************** * FUNCTION clone_datadefQ * * Clone a Q of obj_template_t * * INPUTS: * mod == module owner of the cloned data * newQ == Q of obj_template_t getting new contents * srcQ == Q of obj_template_t with starting contents * mobjQ == Q of OBJ_TYP_REFINE obj_template_t to * merge into the clone, as refinements * (May be NULL) * parent == parent object containing the srcQ (may be NULL) * * RETURNS: * status *********************************************************************/ static status_t clone_datadefQ (ncx_module_t *mod, dlq_hdr_t *newQ, dlq_hdr_t *srcQ, dlq_hdr_t *mobjQ, obj_template_t *parent) { obj_template_t *newobj, *srcobj, *testobj; status_t res; res = NO_ERR; for (srcobj = (obj_template_t *)dlq_firstEntry(srcQ); srcobj != NULL; srcobj = (obj_template_t *)dlq_nextEntry(srcobj)) { if (!obj_has_name(srcobj)) { continue; } newobj = obj_clone_template(mod, srcobj, mobjQ); if (!newobj) { log_error("\nError: clone of object %s failed", obj_get_name(srcobj)); return ERR_INTERNAL_MEM; } else { testobj = obj_find_template(newQ, obj_get_mod_name(newobj), obj_get_name(newobj)); if (testobj) { log_error("\nError: Object %s on line %u " "already defined at line %u", obj_get_name(newobj), (unsigned int)srcobj->tkerr.linenum, (unsigned int)testobj->tkerr.linenum); obj_free_template(newobj); } else { newobj->parent = parent; dlq_enque(newobj, newQ); } } } return res; } /* clone_datadefQ */ /******************************************************************** * FUNCTION clone_appinfoQ * * Copy the contents of the src appinfoQ to the new appinfoQ * Also add in any merge appinfo that are present * * INPUTS: * newQ == Q of ncx_appinfo_t getting new contents * srcQ == Q of ncx_appinfo_t with starting contents * merQ == Q of ncx_appinfo_t to merge into the clone, * as additions (May be NULL) * * RETURNS: * status *********************************************************************/ static status_t clone_appinfoQ (dlq_hdr_t *newQ, dlq_hdr_t *srcQ, dlq_hdr_t *merQ) { ncx_appinfo_t *newapp, *srcapp; for (srcapp = (ncx_appinfo_t *)dlq_firstEntry(srcQ); srcapp != NULL; srcapp = (ncx_appinfo_t *)dlq_nextEntry(srcapp)) { newapp = ncx_clone_appinfo(srcapp); if (!newapp) { log_error("\nError: clone of appinfo failed"); return ERR_INTERNAL_MEM; } else { dlq_enque(newapp, newQ); } } if (merQ) { for (srcapp = (ncx_appinfo_t *)dlq_firstEntry(merQ); srcapp != NULL; srcapp = (ncx_appinfo_t *)dlq_nextEntry(srcapp)) { newapp = ncx_clone_appinfo(srcapp); if (!newapp) { log_error("\nError: clone of appinfo failed"); return ERR_INTERNAL_MEM; } else { dlq_enque(newapp, newQ); } } } return NO_ERR; } /* clone_appinfoQ */ /******************************************************************** * FUNCTION clone_iffeatureQ * * Copy the contents of the src iffeatureQ to the new iffeatureQ * Also add in any merge if-features that are present * * INPUTS: * newQ == Q of ncx_iffeature_t getting new contents * srcQ == Q of ncx_iffeature_t with starting contents * merQ == Q of ncx_iffeature_t to merge into the clone, * as additions (May be NULL) * * RETURNS: * status *********************************************************************/ static status_t clone_iffeatureQ (dlq_hdr_t *newQ, dlq_hdr_t *srcQ, dlq_hdr_t *merQ) { ncx_iffeature_t *newif, *srcif; for (srcif = (ncx_iffeature_t *)dlq_firstEntry(srcQ); srcif != NULL; srcif = (ncx_iffeature_t *)dlq_nextEntry(srcif)) { newif = ncx_clone_iffeature(srcif); if (!newif) { log_error("\nError: clone of iffeature failed"); return ERR_INTERNAL_MEM; } else { dlq_enque(newif, newQ); } } if (merQ) { for (srcif = (ncx_iffeature_t *)dlq_firstEntry(merQ); srcif != NULL; srcif = (ncx_iffeature_t *)dlq_nextEntry(srcif)) { newif = ncx_clone_iffeature(srcif); if (!newif) { log_error("\nError: clone of iffeature failed"); return ERR_INTERNAL_MEM; } else { dlq_enque(newif, newQ); } } } return NO_ERR; } /* clone_iffeatureQ */ /******************************************************************** * FUNCTION clone_case * * Clone a case struct * * INPUTS: * mod == module owner of the cloned data * cas == obj_case_t data structure to clone * mcas == obj_refine_t data structure to merge * into the clone, as refinements. Only * legal case refinements will be checked * (May be NULL) * obj == new object template getting this cloned case * mobjQ == Q containing OBJ_TYP_REFINE objects to check * * RETURNS: * pointer to malloced case clone * NULL if malloc error or internal error *********************************************************************/ static obj_case_t * clone_case (ncx_module_t *mod, obj_case_t *cas, obj_refine_t *mcas, obj_template_t *obj, dlq_hdr_t *mobjQ) { obj_case_t *newcas; status_t res; res = NO_ERR; newcas = new_case(TRUE); /*** need a real datadefQ ***/ if (!newcas) { return NULL; } /* set the fields that cannot be refined */ newcas->name = cas->name; newcas->nameclone = TRUE; newcas->status = cas->status; if (mcas && mcas->descr) { newcas->descr = xml_strdup(mcas->descr); if (!newcas->descr) { free_case(newcas); return NULL; } } else if (cas->descr) { newcas->descr = xml_strdup(cas->descr); if (!newcas->descr) { free_case(newcas); return NULL; } } if (mcas && mcas->ref) { newcas->ref = xml_strdup(mcas->ref); if (!newcas->ref) { free_case(newcas); return NULL; } } else if (cas->ref) { newcas->ref = xml_strdup(cas->ref); if (!newcas->ref) { free_case(newcas); return NULL; } } res = clone_datadefQ(mod, newcas->datadefQ, cas->datadefQ, mobjQ, obj); if (res != NO_ERR) { free_case(newcas); return NULL; } return newcas; } /* clone_case */ /******************************************************************** * FUNCTION clone_mustQ * * Clone a Q of must clauses (ncx_errinfo_t structs) * * INPUTS: * newQ == Q getting new structs * srcQ == Q with starting contents * mergeQ == Q of refinements (additional must statements) * * RETURNS: * status *********************************************************************/ static status_t clone_mustQ (dlq_hdr_t *newQ, dlq_hdr_t *srcQ, dlq_hdr_t *mergeQ) { xpath_pcb_t *srcmust, *newmust; for (srcmust = (xpath_pcb_t *)dlq_firstEntry(srcQ); srcmust != NULL; srcmust = (xpath_pcb_t *)dlq_nextEntry(srcmust)) { newmust = xpath_clone_pcb(srcmust); if (!newmust) { return ERR_INTERNAL_MEM; } else { dlq_enque(newmust, newQ); } } if (mergeQ) { for (srcmust = (xpath_pcb_t *)dlq_firstEntry(mergeQ); srcmust != NULL; srcmust = (xpath_pcb_t *)dlq_nextEntry(srcmust)) { newmust = xpath_clone_pcb(srcmust); if (!newmust) { return ERR_INTERNAL_MEM; } else { dlq_enque(newmust, newQ); } } } return NO_ERR; } /* clone_mustQ */ /******************************************************************** * Malloc and initialize the fields in a an obj_container_t * * \param isreal TRUE for a real object, FALSE for a cloned object * \return pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static obj_container_t * new_container (boolean isreal) { obj_container_t *con; con = m__getObj(obj_container_t); if (!con) { return NULL; } (void)memset(con, 0x0, sizeof(obj_container_t)); con->datadefQ = dlq_createQue(); if (!con->datadefQ) { m__free(con); return NULL; } if (isreal) { con->typedefQ = dlq_createQue(); if (!con->typedefQ) { dlq_destroyQue(con->datadefQ); m__free(con); return NULL; } con->groupingQ = dlq_createQue(); if (!con->groupingQ) { dlq_destroyQue(con->datadefQ); dlq_destroyQue(con->typedefQ); m__free(con); return NULL; } con->status = NCX_STATUS_CURRENT; } dlq_createSQue(&con->mustQ); return con; } /* new_container */ /******************************************************************** * Scrub the memory in a obj_container_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * \param con obj_container_t data structure to free * \param flags flags field from object freeing this container *********************************************************************/ static void free_container ( obj_container_t *con, uint32 flags ) { if ( !con ) { return; } boolean notclone = (flags & OBJ_FL_CLONE) ? FALSE : TRUE; if ( notclone ) { m__free(con->name); } m__free(con->descr); m__free(con->ref); m__free(con->presence); if (notclone) { typ_clean_typeQ(con->typedefQ); dlq_destroyQue(con->typedefQ); grp_clean_groupingQ(con->groupingQ); dlq_destroyQue(con->groupingQ); } if (!con->datadefclone) { obj_clean_datadefQ(con->datadefQ); dlq_destroyQue(con->datadefQ); } clean_mustQ(&con->mustQ); m__free(con); } /* free_container */ /******************************************************************** * FUNCTION clone_container * * Clone a container struct * * INPUTS: * mod == module owner of the cloned data * parent == new object containing 'con' * con == obj_container_t data structure to clone * mcon == obj_refine_t data structure to merge * into the clone, as refinements. Only * legal container refinements will be checked * (May be NULL) * mobjQ == starting merge object Q, passed through * * RETURNS: * pointer to malloced object clone * NULL if malloc error or internal error *********************************************************************/ static obj_container_t * clone_container (ncx_module_t *mod, obj_template_t *parent, obj_container_t *con, obj_refine_t *mcon, dlq_hdr_t *mobjQ) { obj_container_t *newcon; status_t res; newcon = new_container(FALSE); if (!newcon) { return NULL; } /* set the fields that cannot be refined */ newcon->name = con->name; newcon->typedefQ = con->typedefQ; newcon->groupingQ = con->groupingQ; newcon->status = con->status; if (mcon && mcon->descr) { newcon->descr = xml_strdup(mcon->descr); if (!newcon->descr) { free_container(newcon, OBJ_FL_CLONE); return NULL; } } else if (con->descr) { newcon->descr = xml_strdup(con->descr); if (!newcon->descr) { free_container(newcon, OBJ_FL_CLONE); return NULL; } } if (mcon && mcon->ref) { newcon->ref = xml_strdup(mcon->ref); if (!newcon->ref) { free_container(newcon, OBJ_FL_CLONE); return NULL; } } else if (con->ref) { newcon->ref = xml_strdup(con->ref); if (!newcon->ref) { free_container(newcon, OBJ_FL_CLONE); return NULL; } } if (mcon && mcon->presence) { newcon->presence = xml_strdup(mcon->presence); if (!newcon->presence) { free_container(newcon, OBJ_FL_CLONE); return NULL; } } else if (con->presence) { newcon->presence = xml_strdup(con->presence); if (!newcon->presence) { free_container(newcon, OBJ_FL_CLONE); return NULL; } } res = clone_mustQ(&newcon->mustQ, &con->mustQ, (mcon) ? &mcon->mustQ : NULL); if (res != NO_ERR) { free_container(newcon, OBJ_FL_CLONE); return NULL; } res = clone_datadefQ(mod, newcon->datadefQ, con->datadefQ, mobjQ, parent); if (res != NO_ERR) { free_container(newcon, OBJ_FL_CLONE); return NULL; } return newcon; } /* clone_container */ /******************************************************************** * FUNCTION new_leaf * * Malloc and initialize the fields in a an obj_leaf_t * * INPUTS: * isreal == TRUE if this is for a real object * == FALSE if this is a cloned object * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static obj_leaf_t * new_leaf (boolean isreal) { obj_leaf_t *leaf; leaf = m__getObj(obj_leaf_t); if (!leaf) { return NULL; } (void)memset(leaf, 0x0, sizeof(obj_leaf_t)); if (isreal) { leaf->typdef = typ_new_typdef(); if (!leaf->typdef) { m__free(leaf); return NULL; } leaf->status = NCX_STATUS_CURRENT; } dlq_createSQue(&leaf->mustQ); return leaf; } /* new_leaf */ /******************************************************************** * Scrub the memory in a obj_leaf_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * \param leaf obj_leaf_t data structure to free * \param flags flags field from object freeing this leaf *********************************************************************/ static void free_leaf (obj_leaf_t *leaf, uint32 flags) { if ( !leaf ) { return; } boolean notclone = (flags & OBJ_FL_CLONE) ? FALSE : TRUE; m__free(leaf->defval); m__free(leaf->descr); m__free(leaf->ref); if (notclone ) { m__free(leaf->name); m__free(leaf->units); if ( leaf->typdef && (leaf->typdef->tclass != NCX_CL_BASE)) { typ_free_typdef(leaf->typdef); } } clean_mustQ(&leaf->mustQ); m__free(leaf); } /* free_leaf */ /******************************************************************** * FUNCTION clone_leaf * * Clone a leaf struct * * INPUTS: * leaf == obj_leaf_t data structure to clone * mleaf == obj_refine_t data structure to merge * into the clone, as refinements. Only * legal leaf refinements will be checked * (May be NULL) * * RETURNS: * pointer to malloced object clone * NULL if malloc error or internal error *********************************************************************/ static obj_leaf_t * clone_leaf (obj_leaf_t *leaf, obj_refine_t *mleaf) { obj_leaf_t *newleaf; status_t res; newleaf = new_leaf(FALSE); if (!newleaf) { return NULL; } /* set the fields that cannot be refined */ newleaf->name = leaf->name; newleaf->units = leaf->units; newleaf->typdef = leaf->typdef; newleaf->status = leaf->status; if (mleaf && mleaf->def) { newleaf->defval = xml_strdup(mleaf->def); if (!newleaf->defval) { free_leaf(newleaf, OBJ_FL_CLONE); return NULL; } } else if (leaf->defval) { newleaf->defval = xml_strdup(leaf->defval); if (!newleaf->defval) { free_leaf(newleaf, OBJ_FL_CLONE); return NULL; } } if (mleaf && mleaf->descr) { newleaf->descr = xml_strdup(mleaf->descr); if (!newleaf->descr) { free_leaf(newleaf, OBJ_FL_CLONE); return NULL; } } else if (leaf->descr) { newleaf->descr = xml_strdup(leaf->descr); if (!newleaf->descr) { free_leaf(newleaf, OBJ_FL_CLONE); return NULL; } } if (mleaf && mleaf->ref) { newleaf->ref = xml_strdup(mleaf->ref); if (!newleaf->ref) { free_leaf(newleaf, OBJ_FL_CLONE); return NULL; } } else if (leaf->ref) { newleaf->ref = xml_strdup(leaf->ref); if (!newleaf->ref) { free_leaf(newleaf, OBJ_FL_CLONE); return NULL; } } res = clone_mustQ(&newleaf->mustQ, &leaf->mustQ, (mleaf) ? &mleaf->mustQ : NULL); if (res != NO_ERR) { free_leaf(newleaf, OBJ_FL_CLONE); return NULL; } return newleaf; } /* clone_leaf */ /******************************************************************** * FUNCTION clean_defvalsQ * * Free memory allocated by leaf-list default values queue * * INPUTS: * defvalsQ == dlq_hdr_t pointer to the list of default values * *********************************************************************/ static void clean_defvalsQ (dlq_hdr_t *defvalsQ) { obj_leaflist_defval_t *def; if (defvalsQ == NULL) return; while (!dlq_empty(defvalsQ)) { def = (obj_leaflist_defval_t *)dlq_deque(defvalsQ); if (def->defval) m__free(def->defval); m__free(def); } } /******************************************************************** * FUNCTION clone_defvalsQ * * Create a deep copy of the leaf-list defvals queue. * * INPUTS: * defvalsQ == dlq_hdr_t pointer to the list of default values * * RETURNS: * dlq_hdr_t pointer to the new copy or NULL if an error *********************************************************************/ static dlq_hdr_t * clone_defvalsQ (dlq_hdr_t *defvalsQ) { dlq_hdr_t *cpy; obj_leaflist_defval_t *def; obj_leaflist_defval_t *tmp; if (defvalsQ == NULL) return NULL; cpy = dlq_createQue(); if (cpy == NULL) return NULL; for (def = (obj_leaflist_defval_t *)dlq_firstEntry(defvalsQ); def; def = (obj_leaflist_defval_t *)dlq_nextEntry(def)) { tmp = m__getMem(sizeof(*tmp)); if (tmp == NULL) goto err; tmp->defval = xml_strdup(def->defval); if (tmp->defval == NULL) { m__free(tmp); goto err; } dlq_enque(tmp, cpy); } return cpy; err: clean_defvalsQ(cpy); return NULL; } /******************************************************************** * FUNCTION new_leaflist * * Malloc and initialize the fields in a an obj_leaflist_t * * INPUTS: * isreal == TRUE if this is for a real object * == FALSE if this is a cloned object * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static obj_leaflist_t * new_leaflist (boolean isreal) { obj_leaflist_t *leaflist; leaflist = m__getObj(obj_leaflist_t); if (!leaflist) { return NULL; } (void)memset(leaflist, 0x0, sizeof(obj_leaflist_t)); if (isreal) { leaflist->typdef = typ_new_typdef(); if (!leaflist->typdef) { m__free(leaflist); return NULL; } leaflist->status = NCX_STATUS_CURRENT; leaflist->ordersys = TRUE; leaflist->defvalsQ = dlq_createQue(); if (!leaflist->defvalsQ) { dlq_destroyQue(leaflist->defvalsQ); m__free(leaflist); return NULL; } } dlq_createSQue(&leaflist->mustQ); return leaflist; } /* new_leaflist */ /******************************************************************** * Scrub the memory in a obj_leaflist_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * \param leaflist obj_leaflist_t data structure to free * \param flags flags field from object freeing this leaf-list *********************************************************************/ static void free_leaflist (obj_leaflist_t *leaflist, uint32 flags) { if ( !leaflist ) { return; } boolean notclone = (flags & OBJ_FL_CLONE) ? FALSE : TRUE; if (notclone ) { m__free(leaflist->name); m__free(leaflist->units); typ_free_typdef(leaflist->typdef); } m__free(leaflist->descr); m__free(leaflist->ref); if (leaflist->defvalsQ) clean_defvalsQ(leaflist->defvalsQ); clean_mustQ(&leaflist->mustQ); m__free(leaflist); } /* free_leaflist */ /******************************************************************** * FUNCTION clone_leaflist * * Clone a leaf-list struct * * INPUTS: * leaflist == obj_leaflist_t data structure to clone * mleaflist == obj_refine_t data structure to merge * into the clone, as refinements. Only * legal leaf-list refinements will be checked * (May be NULL) * * RETURNS: * pointer to malloced object clone * NULL if malloc error or internal error *********************************************************************/ static obj_leaflist_t * clone_leaflist (obj_leaflist_t *leaflist, obj_refine_t *mleaflist) { obj_leaflist_t *newleaflist; status_t res; newleaflist = new_leaflist(FALSE); if (!newleaflist) { return NULL; } /* set the fields that cannot be refined */ newleaflist->name = leaflist->name; newleaflist->units = leaflist->units; newleaflist->typdef = leaflist->typdef; newleaflist->ordersys = leaflist->ordersys; newleaflist->status = leaflist->status; if (mleaflist && mleaflist->descr) { newleaflist->descr = xml_strdup(mleaflist->descr); if (!newleaflist->descr) { free_leaflist(newleaflist, OBJ_FL_CLONE); return NULL; } } else if (leaflist->descr) { newleaflist->descr = xml_strdup(leaflist->descr); if (!newleaflist->descr) { free_leaflist(newleaflist, OBJ_FL_CLONE); return NULL; } } if (mleaflist && mleaflist->ref) { newleaflist->ref = xml_strdup(mleaflist->ref); if (!newleaflist->ref) { free_leaflist(newleaflist, OBJ_FL_CLONE); return NULL; } } else if (leaflist->ref) { newleaflist->ref = xml_strdup(leaflist->ref); if (!newleaflist->ref) { free_leaflist(newleaflist, OBJ_FL_CLONE); return NULL; } } res = clone_mustQ(&newleaflist->mustQ, &leaflist->mustQ, (mleaflist) ? &mleaflist->mustQ : NULL); if (res != NO_ERR) { free_leaflist(newleaflist, OBJ_FL_CLONE); return NULL; } if (mleaflist && mleaflist->minelems_tkerr.mod) { newleaflist->minelems = mleaflist->minelems; newleaflist->minset = TRUE; } else { newleaflist->minelems = leaflist->minelems; newleaflist->minset = leaflist->minset; } if (mleaflist && mleaflist->maxelems_tkerr.mod) { newleaflist->maxelems = mleaflist->maxelems; newleaflist->maxset = TRUE; } else { newleaflist->maxelems = leaflist->maxelems; newleaflist->maxset = leaflist->maxset; } newleaflist->defvalsQ = clone_defvalsQ(leaflist->defvalsQ); if (newleaflist->defvalsQ == NULL) { free_leaflist(newleaflist, OBJ_FL_CLONE); return NULL; } return newleaflist; } /* clone_leaflist */ /******************************************************************** * Free a key Q. * * \param keyQ the queue to free. ********************************************************************/ static void free_keyQ( dlq_hdr_t* keyQ ) { if ( !keyQ ) { return; } while (!dlq_empty(keyQ)) { obj_key_t *key = (obj_key_t *)dlq_deque(keyQ); obj_free_key(key); } } /******************************************************************** * Free a unique Q. * * \param keyQ the queue to free. ********************************************************************/ static void free_uniqueQ( dlq_hdr_t* uniqueQ ) { if ( !uniqueQ ) { return; } while (!dlq_empty(uniqueQ)) { obj_unique_t *uni = (obj_unique_t *)dlq_deque(uniqueQ); obj_free_unique(uni); } } /******************************************************************** * Scrub the memory in a obj_list_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * \param list obj_list_t data structure to free * \param flags flags field from object freeing this list *********************************************************************/ static void free_list( obj_list_t *list, uint32 flags) { if ( !list ) { return; } boolean notclone = (flags & OBJ_FL_CLONE) ? FALSE : TRUE; m__free(list->descr); m__free(list->ref); free_keyQ( &list->keyQ ); free_uniqueQ( &list->uniqueQ ); if (notclone) { m__free(list->name); m__free(list->keystr); typ_clean_typeQ(list->typedefQ); dlq_destroyQue(list->typedefQ); grp_clean_groupingQ(list->groupingQ); dlq_destroyQue(list->groupingQ); } if ( !list->datadefclone ) { obj_clean_datadefQ( list->datadefQ ); dlq_destroyQue( list->datadefQ ); } clean_mustQ(&list->mustQ); m__free(list); } /* free_list */ /******************************************************************** * FUNCTION new_list * * Malloc and initialize the fields in a an obj_list_t * * INPUTS: * isreal == TRUE if this is for a real object * == FALSE if this is a cloned object * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static obj_list_t * new_list (boolean isreal) { obj_list_t *list; list = m__getObj(obj_list_t); if (!list) { return NULL; } (void)memset(list, 0x0, sizeof(obj_list_t)); dlq_createSQue(&list->keyQ); dlq_createSQue(&list->uniqueQ); list->status = NCX_STATUS_CURRENT; list->ordersys = TRUE; if (isreal) { list->typedefQ = dlq_createQue(); if (!list->typedefQ) { m__free(list); return NULL; } list->groupingQ = dlq_createQue(); if (!list->groupingQ) { dlq_destroyQue(list->typedefQ); m__free(list); return NULL; } } dlq_createSQue(&list->mustQ); list->datadefQ = dlq_createQue(); if (!list->datadefQ) { free_list(list, (isreal) ? 0U : OBJ_FL_CLONE); return NULL; } return list; } /* new_list */ /******************************************************************** * FUNCTION clone_list * * Clone a leaf-list struct * * INPUTS: * mod == module owner of the cloned data * newparent == new obj containing 'list' * srclist == obj_template_t data structure to clone * mlist == obj_refine_t data structure to merge * into the clone, as refinements. Only * legal list refinements will be checked * (May be NULL) * mobjQ == Q of objects with OBJ_TYP_REFINE to apply * * RETURNS: * pointer to malloced object clone * NULL if malloc error or internal error *********************************************************************/ static obj_list_t * clone_list (ncx_module_t *mod, obj_template_t *newparent, obj_template_t *srclist, obj_refine_t *mlist, dlq_hdr_t *mobjQ) { obj_list_t *list, *newlist; status_t res; newlist = new_list(FALSE); if (!newlist) { return NULL; } list = srclist->def.list; /* set the fields that cannot be refined */ newlist->name = list->name; newlist->keystr = list->keystr; newlist->typedefQ = list->typedefQ; newlist->groupingQ = list->groupingQ; newlist->ordersys = list->ordersys; newlist->status = list->status; if (mlist && mlist->descr) { newlist->descr = xml_strdup(mlist->descr); if (!newlist->descr) { free_list(newlist, OBJ_FL_CLONE); return NULL; } } else if (list->descr) { newlist->descr = xml_strdup(list->descr); if (!newlist->descr) { free_list(newlist, OBJ_FL_CLONE); return NULL; } } if (mlist && mlist->ref) { newlist->ref = xml_strdup(mlist->ref); if (!newlist->ref) { free_list(newlist, OBJ_FL_CLONE); return NULL; } } else if (list->ref) { newlist->ref = xml_strdup(list->ref); if (!newlist->ref) { free_list(newlist, OBJ_FL_CLONE); return NULL; } } res = clone_mustQ(&newlist->mustQ, &list->mustQ, (mlist) ? &mlist->mustQ : NULL); if (res != NO_ERR) { free_list(newlist, OBJ_FL_CLONE); return NULL; } if (mlist && mlist->minelems_tkerr.mod) { newlist->minelems = mlist->minelems; newlist->minset = TRUE; } else { newlist->minelems = list->minelems; newlist->minset = list->minset; } if (mlist && mlist->maxelems_tkerr.mod) { newlist->maxelems = mlist->maxelems; newlist->maxset = TRUE; } else { newlist->maxelems = list->maxelems; newlist->maxset = list->maxset; } res = clone_datadefQ(mod, newlist->datadefQ, list->datadefQ, mobjQ, newparent); if (res != NO_ERR) { free_list(newlist, OBJ_FL_CLONE); return NULL; } /* newlist->keyQ is still empty * newlist->uniqueQ is still empty * these are filled in by the yang_obj_resolve_final function */ return newlist; } /* clone_list */ /******************************************************************** * FUNCTION new_case * * Malloc and initialize the fields in a an obj_case_t * * INPUTS: * isreal == TRUE if this is for a real object * == FALSE if this is a cloned object * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static obj_case_t * new_case (boolean isreal) { obj_case_t *cas; cas = m__getObj(obj_case_t); if (!cas) { return NULL; } (void)memset(cas, 0x0, sizeof(obj_case_t)); cas->status = NCX_STATUS_CURRENT; if (isreal) { cas->datadefQ = dlq_createQue(); if (!cas->datadefQ) { m__free(cas); return NULL; } } return cas; } /* new_case */ /******************************************************************** * Clean and free the fields in a an obj_case_t, then free * the case struct * * \param cas the case struct to free *********************************************************************/ static void free_case (obj_case_t *cas) { if ( !cas ) { return; } if (!cas->nameclone ) { m__free(cas->name); } m__free(cas->descr); m__free(cas->ref); if (!cas->datadefclone) { obj_clean_datadefQ(cas->datadefQ); dlq_destroyQue(cas->datadefQ); } m__free(cas); } /* free_case */ /******************************************************************** * FUNCTION new_choice * * Malloc and initialize the fields in a an obj_choice_t * * INPUTS: * isreal == TRUE if this is for a real object * == FALSE if this is a cloned object * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static obj_choice_t * new_choice (boolean isreal) { obj_choice_t *ch; ch = m__getObj(obj_choice_t); if (!ch) { return NULL; } (void)memset(ch, 0x0, sizeof(obj_choice_t)); ch->caseQ = dlq_createQue(); if (!ch->caseQ) { m__free(ch); return NULL; } if (isreal) { ch->status = NCX_STATUS_CURRENT; } return ch; } /* new_choice */ /******************************************************************** * Scrub the memory in a obj_choice_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * \param choic obj_choice_t data structure to free * \param flags flags field from object freeing this choice *********************************************************************/ static void free_choice (obj_choice_t *choic, uint32 flags) { if ( !choic ) { return; } boolean notclone = (flags & OBJ_FL_CLONE) ? FALSE : TRUE; if (notclone) { m__free(choic->name); } m__free(choic->defval); m__free(choic->descr); m__free(choic->ref); if ( !choic->caseQclone ) { obj_clean_datadefQ(choic->caseQ); dlq_destroyQue(choic->caseQ); } m__free(choic); } /* free_choice */ /******************************************************************** * FUNCTION clone_choice * * Clone a choice struct * * INPUTS: * mod == module owner of the cloned data * choic == obj_choice_t data structure to clone * mchoic == obj_choice_t data structure to merge * into the clone, as refinements. Only * legal choice refinements will be checked * (May be NULL) * obj == parent object containing 'choic' * mobjQ == Q with OBJ_TYP_REFINE nodes to check * * RETURNS: * pointer to malloced object clone * NULL if malloc error or internal error *********************************************************************/ static obj_choice_t * clone_choice (ncx_module_t *mod, obj_choice_t *choic, obj_refine_t *mchoic, obj_template_t *obj, dlq_hdr_t *mobjQ) { obj_choice_t *newchoic; status_t res; newchoic = new_choice(FALSE); if (!newchoic) { return NULL; } /* set the fields that cannot be refined */ newchoic->name = choic->name; newchoic->status = choic->status; if (mchoic && mchoic->def) { newchoic->defval = xml_strdup(mchoic->def); if (!newchoic->defval) { free_choice(newchoic, OBJ_FL_CLONE); return NULL; } } else if (choic->defval) { newchoic->defval = xml_strdup(choic->defval); if (!newchoic->defval) { free_choice(newchoic, OBJ_FL_CLONE); return NULL; } } if (mchoic && mchoic->descr) { newchoic->descr = xml_strdup(mchoic->descr); if (!newchoic->descr) { free_choice(newchoic, OBJ_FL_CLONE); return NULL; } } else if (choic->descr) { newchoic->descr = xml_strdup(choic->descr); if (!newchoic->descr) { free_choice(newchoic, OBJ_FL_CLONE); return NULL; } } if (mchoic && mchoic->ref) { newchoic->ref = xml_strdup(mchoic->ref); if (!newchoic->ref) { free_choice(newchoic, OBJ_FL_CLONE); return NULL; } } else if (choic->ref) { newchoic->ref = xml_strdup(choic->ref); if (!newchoic->ref) { free_choice(newchoic, OBJ_FL_CLONE); return NULL; } } res = clone_datadefQ(mod, newchoic->caseQ, choic->caseQ, mobjQ, obj); if (res != NO_ERR) { free_choice(newchoic, OBJ_FL_CLONE); return NULL; } return newchoic; } /* clone_choice */ /******************************************************************** * FUNCTION new_uses * * Malloc and initialize the fields in a obj_uses_t * * INPUTS: * isreal == TRUE if this is for a real object * == FALSE if this is a cloned object * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static obj_uses_t * new_uses (boolean isreal) { obj_uses_t *us; us = m__getObj(obj_uses_t); if (!us) { return NULL; } (void)memset(us, 0x0, sizeof(obj_uses_t)); if (isreal) { us->status = NCX_STATUS_CURRENT; /* default */ } us->datadefQ = dlq_createQue(); if (!us->datadefQ) { m__free(us); return NULL; } return us; } /* new_uses */ /******************************************************************** * Scrub the memory in a obj_uses_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * \param us the obj_uses_t data structure to free *********************************************************************/ static void free_uses (obj_uses_t *us) { if ( !us ) { return; } m__free(us->prefix); m__free(us->name); m__free(us->descr); m__free(us->ref); obj_clean_datadefQ(us->datadefQ); dlq_destroyQue(us->datadefQ); m__free(us); } /* free_uses */ /******************************************************************** * FUNCTION new_refine * * Malloc and initialize the fields in a obj_refine_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static obj_refine_t * new_refine (void) { obj_refine_t *refi; refi = m__getObj(obj_refine_t); if (!refi) { return NULL; } (void)memset(refi, 0x0, sizeof(obj_refine_t)); dlq_createSQue(&refi->mustQ); return refi; } /* new_refine */ /******************************************************************** * Scrub the memory in a obj_refine_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * \param refi == obj_refine_t data structure to free *********************************************************************/ static void free_refine (obj_refine_t *refi) { if ( !refi ) { return; } m__free(refi->target); m__free(refi->descr); m__free(refi->ref); m__free(refi->presence); m__free(refi->def); clean_mustQ(&refi->mustQ); m__free(refi); } /* free_refine */ /******************************************************************** * FUNCTION new_augment * * Malloc and initialize the fields in a obj_augment_t * * INPUTS: * isreal == TRUE if this is for a real object * == FALSE if this is a cloned object * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static obj_augment_t * new_augment (boolean isreal) { obj_augment_t *aug; aug = m__getObj(obj_augment_t); if (!aug) { return NULL; } (void)memset(aug, 0x0, sizeof(obj_augment_t)); dlq_createSQue(&aug->datadefQ); #ifdef ENABLE_DIRECT_MUST_AUGMENT_EX dlq_createSQue(&aug->mustQ); #endif if (isreal) { aug->status = NCX_STATUS_CURRENT; } return aug; } /* new_augment */ /******************************************************************** * Scrub the memory in a obj_augment_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * \param aug the obj_augment_t data structure to free *********************************************************************/ static void free_augment (obj_augment_t *aug) { if ( !aug ) { return; } m__free(aug->target); m__free(aug->descr); m__free(aug->ref); obj_clean_datadefQ(&aug->datadefQ); m__free(aug); } /* free_augment */ /******************************************************************** * FUNCTION new_rpc * * Malloc and initialize the fields in a an obj_rpc_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static obj_rpc_t * new_rpc (void) { obj_rpc_t *rpc; rpc = m__getObj(obj_rpc_t); if (!rpc) { return NULL; } (void)memset(rpc, 0x0, sizeof(obj_rpc_t)); dlq_createSQue(&rpc->typedefQ); dlq_createSQue(&rpc->groupingQ); dlq_createSQue(&rpc->datadefQ); rpc->status = NCX_STATUS_CURRENT; /* by default: set supported to true for * agent simulation mode; there will not be any * callbacks to load, but RPC message * processing based on the template will be done */ rpc->supported = TRUE; return rpc; } /* new_rpc */ /******************************************************************** * Clean and free the fields in a an obj_rpc_t, then free * the RPC struct * * \param rpc the RPC struct to free *********************************************************************/ static void free_rpc (obj_rpc_t *rpc, uint32 flags) { if ( !rpc ) { return; } m__free(rpc->name); m__free(rpc->descr); m__free(rpc->ref); if (!(flags & OBJ_FL_CLONE)) { typ_clean_typeQ(&rpc->typedefQ); grp_clean_groupingQ(&rpc->groupingQ); } obj_clean_datadefQ(&rpc->datadefQ); m__free(rpc); } /* free_rpc */ /******************************************************************** * FUNCTION clone_rpc * * Clone an rpc struct * * INPUTS: * not == obj_rpc_t data structure to clone * mnot == obj_refine_t data structure to merge * into the clone, as refinements. Only * legal container refinements will be checked * (May be NULL) * * RETURNS: * pointer to malloced object clone * NULL if malloc error or internal error *********************************************************************/ static obj_rpc_t * clone_rpc (ncx_module_t *mod, obj_rpc_t *rpc, obj_refine_t *mrpc) { obj_rpc_t *newrpc; status_t res; newrpc = new_rpc(); if (!newrpc) { return NULL; } /* set the fields that canrpc be refined */ newrpc->name = xml_strdup(rpc->name); newrpc->status = rpc->status; newrpc->typedefQ = rpc->typedefQ; newrpc->groupingQ = rpc->groupingQ; newrpc->nsid = rpc->nsid; newrpc->supported = rpc->supported; if (mrpc && mrpc->descr) { newrpc->descr = xml_strdup(mrpc->descr); if (!newrpc->descr) { free_rpc(newrpc, OBJ_FL_CLONE); return NULL; } } else if (rpc->descr) { newrpc->descr = xml_strdup(rpc->descr); if (!newrpc->descr) { free_rpc(newrpc, OBJ_FL_CLONE); return NULL; } } if (mrpc && mrpc->ref) { newrpc->ref = xml_strdup(mrpc->ref); if (!newrpc->ref) { free_rpc(newrpc, OBJ_FL_CLONE); return NULL; } } else if (rpc->ref) { newrpc->ref = xml_strdup(rpc->ref); if (!newrpc->ref) { free_rpc(newrpc, OBJ_FL_CLONE); return NULL; } } res = clone_datadefQ(mod, &newrpc->datadefQ, &rpc->datadefQ, NULL, NULL); if (res != NO_ERR) { free_rpc(newrpc, OBJ_FL_CLONE); return NULL; } return newrpc; } /* clone_rpc */ /******************************************************************** * FUNCTION new_rpcio * * Malloc and initialize the fields in a an obj_rpcio_t * Fields are setup within the new obj_template_t, based * on the values in rpcobj * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static obj_rpcio_t * new_rpcio (void) { obj_rpcio_t *rpcio; rpcio = m__getObj(obj_rpcio_t); if (!rpcio) { return NULL; } (void)memset(rpcio, 0x0, sizeof(obj_rpcio_t)); dlq_createSQue(&rpcio->typedefQ); dlq_createSQue(&rpcio->groupingQ); dlq_createSQue(&rpcio->datadefQ); return rpcio; } /* new_rpcio */ /******************************************************************** * Clean and free the fields in a an obj_rpcio_t, then free * the RPC IO struct * * \param rpcio the RPC IO struct to free *********************************************************************/ static void free_rpcio (obj_rpcio_t *rpcio, uint32 flags) { if ( !rpcio ) { return; } m__free(rpcio->name); if (!(flags & OBJ_FL_CLONE)) { typ_clean_typeQ(&rpcio->typedefQ); grp_clean_groupingQ(&rpcio->groupingQ); } obj_clean_datadefQ(&rpcio->datadefQ); m__free(rpcio); } /* free_rpcio */ /******************************************************************** * FUNCTION clone_rpcio * * Clone rpcio struct * * INPUTS: * mod == module in progress * rpcio == obj_notif_t data structure to clone * mrpcio == obj_refine_t data structure to merge * into the clone, as refinements. Only * legal container refinements will be checked * (May be NULL) * * RETURNS: * pointer to malloced rpcio clone * NULL if malloc error or internal error *********************************************************************/ static obj_rpcio_t * clone_rpcio (ncx_module_t *mod, obj_rpcio_t *rpcio, obj_refine_t *mrpcio) { obj_rpcio_t *newrpcio = new_rpcio(); status_t res; if (newrpcio == NULL) return NULL; newrpcio->name = xml_strdup(rpcio->name); newrpcio->typedefQ = rpcio->typedefQ; newrpcio->groupingQ = rpcio->groupingQ; newrpcio->defaultparm = rpcio->defaultparm; /* does this need to be cloned? */ res = clone_datadefQ(mod, &newrpcio->datadefQ, &rpcio->datadefQ, NULL, NULL); if (res != NO_ERR) { free_rpcio(newrpcio, OBJ_FL_CLONE); return NULL; } return newrpcio; } /******************************************************************** * FUNCTION new_notif * * Malloc and initialize the fields in a an obj_notif_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static obj_notif_t * new_notif (void) { obj_notif_t *notif; notif = m__getObj(obj_notif_t); if (!notif) { return NULL; } (void)memset(notif, 0x0, sizeof(obj_notif_t)); dlq_createSQue(¬if->typedefQ); dlq_createSQue(¬if->groupingQ); dlq_createSQue(¬if->datadefQ); notif->status = NCX_STATUS_CURRENT; return notif; } /* new_notif */ /******************************************************************** * Clean and free the fields in a an obj_notif_t, then free * the notification struct * * \param notif == notification struct to free *********************************************************************/ static void free_notif (obj_notif_t *notif, uint32 flags) { if ( !notif ) { return; } m__free(notif->name); m__free(notif->descr); m__free(notif->ref); if (!(flags & OBJ_FL_CLONE)) { typ_clean_typeQ(¬if->typedefQ); grp_clean_groupingQ(¬if->groupingQ); } obj_clean_datadefQ(¬if->datadefQ); m__free(notif); } /* free_notif */ /******************************************************************** * FUNCTION clone_notif * * Clone a notification struct * * INPUTS: * not == obj_notif_t data structure to clone * mnot == obj_refine_t data structure to merge * into the clone, as refinements. Only * legal container refinements will be checked * (May be NULL) * * RETURNS: * pointer to malloced object clone * NULL if malloc error or internal error *********************************************************************/ static obj_notif_t * clone_notif (ncx_module_t *mod, obj_notif_t *not, obj_refine_t *mnot) { obj_notif_t *newnot; status_t res; newnot = new_notif(); if (!newnot) { return NULL; } /* set the fields that cannot be refined */ newnot->name = xml_strdup(not->name); newnot->status = not->status; newnot->typedefQ = not->typedefQ; newnot->groupingQ = not->groupingQ; if (mnot && mnot->descr) { newnot->descr = xml_strdup(mnot->descr); if (!newnot->descr) { free_notif(newnot, OBJ_FL_CLONE); return NULL; } } else if (not->descr) { newnot->descr = xml_strdup(not->descr); if (!newnot->descr) { free_notif(newnot, OBJ_FL_CLONE); return NULL; } } if (mnot && mnot->ref) { newnot->ref = xml_strdup(mnot->ref); if (!newnot->ref) { free_notif(newnot, OBJ_FL_CLONE); return NULL; } } else if (not->ref) { newnot->ref = xml_strdup(not->ref); if (!newnot->ref) { free_notif(newnot, OBJ_FL_CLONE); return NULL; } } res = clone_datadefQ(mod, &newnot->datadefQ, ¬->datadefQ, NULL, NULL); if (res != NO_ERR) { free_notif(newnot, OBJ_FL_CLONE); return NULL; } return newnot; } /* clone_notif */ /********************************************************************/ /** * utility function to perform full or partial case sensitive / * insenstive string comparison. * * \param objname object name to find * \param curname object name being matched * \param partialmatch flag idicating if a partial match is allowed. * \param usecase TRUE if case-sensitive FALSE if case-insensitive * \param len_objname the length of the object name. * \return true if the names match */ static bool compare_names( const xmlChar *objname, const xmlChar *curname, boolean partialmatch, boolean usecase, uint32 len_objname ) { int ret = 0; if (partialmatch) { if (usecase) { ret = xml_strncmp(objname, curname, len_objname ); } else { ret = xml_strnicmp(objname, curname, len_objname ); } } else { if (usecase) { ret = xml_strcmp(objname, curname); } else { ret = xml_stricmp(objname, curname); } } return ( ret == 0 ); } /* compare_names */ /********************************************************************/ /** * utility function to search a case object * * \param obj the parent object * \param modname module name that defines the obj_template_t * ( NULL and first match will be done, and the * module ignored (Name instead of QName ) * \param objname object name to find * \param lookdeep TRUE to check objects inside choices/cases and match these * nodes before matching the choice or case * \param partialmatch TRUE if a strncmp (partial match ) is reqquried. * \param usecase == TRUE if case-sensitive * FALSE if case-insensitive * \param altnames == TRUE if altnames allowed * FALSE if normal names only * \param dataonly == TRUE to check just data nodes * FALSE to check all nodes * \param namematch flag indicating if the current objects name is a match * \param matchcount == address of return parameter match count * \return pointer to obj_template_t or NULL if not found in 'que' */ static obj_template_t* search_case( obj_template_t* obj, const xmlChar *modname, const xmlChar *objname, boolean lookdeep, boolean partialmatch, boolean usecase, boolean altnames, boolean dataonly, boolean nameMatch, uint32 *matchcount, obj_template_t** partialMatchedObj ) { obj_case_t* cas = obj->def.cas; obj_template_t* chObj = find_template( cas->datadefQ, modname, objname, lookdeep, partialmatch, usecase, altnames, dataonly, matchcount ); if ( chObj ) { if (partialmatch) { if ( !(*partialMatchedObj ) ) { *partialMatchedObj = chObj; } } else { return chObj; } } /* last try: the choice name itself */ if ( nameMatch && !partialmatch ) { return obj; } return NULL; } /* search_case */ /********************************************************************/ /** * utility function to search a choice object * \param obj the parent object * \param modname module name that defines the obj_template_t * ( NULL and first match will be done, and the * module ignored (Name instead of QName ) * \param objname object name to find * \param lookdeep TRUE to check objects inside choices/cases and match * these nodes before matching the choice or case * \param partialmatch TRUE if a strncmp (partial match ) is reqquried. * \param usecase == TRUE if case-sensitive * FALSE if case-insensitive * \param altnames == TRUE if altnames allowed * FALSE if normal names only * \param dataonly == TRUE to check just data nodes * FALSE to check all nodes * \param namematch flag indicating if the current objects name is a match * \param matchcount == address of return parameter match count * \return pointer to obj_template_t or NULL if not found in 'que' */ static obj_template_t* search_choice( obj_template_t* obj, const xmlChar *modname, const xmlChar *objname, boolean lookdeep, boolean partialmatch, boolean usecase, boolean altnames, boolean dataonly, boolean nameMatch, uint32 *matchcount, obj_template_t** partialMatchedObj ) { obj_template_t* casobj = (obj_template_t*) dlq_firstEntry(obj->def.choic->caseQ); obj_template_t* chObj; for ( ; casobj; casobj = (obj_template_t *)dlq_nextEntry(casobj)) { chObj = search_case( casobj, modname, objname, lookdeep, partialmatch, usecase, altnames, dataonly, nameMatch, matchcount, partialMatchedObj ); if ( chObj ) { if ( partialmatch ) { if ( !(*partialMatchedObj) ) { *partialMatchedObj = chObj; } } else { return chObj; } } } /* last try: the choice name itself */ if ( nameMatch && !partialmatch ) { return obj; } return NULL; } /* search choice */ /******************************************************************** * Check if an object is a CHOICE or CASE. * * \param obj the object to check * \return true of the object is a CHOICE or CASE. */ static boolean obj_is_choice_or_case( obj_template_t* obj ) { return ( obj->objtype == OBJ_TYP_CHOICE || obj->objtype == OBJ_TYP_CASE ); } /********************************************************************/ /** * Utility function for managing partial matches. Increment the count * of partial matches and set the partial match object if it is NULL, * this ensures that the first partial match is used. * * \param obj the object being matched * \param nameMatch flag indicating if the name matched * \param partialmatch flag indicating if a partial match is allowed * \param lookdeep flag indicating if look deep is set * \param matchcount address of return parameter match count * \param partialMatchedObj the partial matched object * \return a matching object or NULL, */ static obj_template_t* handle_partial_match_found( obj_template_t* obj, boolean nameMatch, boolean partialmatch, boolean lookdeep, uint32 *matchcount, obj_template_t** partialMatchedObj ) { if ( nameMatch ) { if ( !partialmatch ) { return obj; } else { if ( lookdeep || !obj_is_choice_or_case( obj ) ) { ++(*matchcount); if ( !(*partialMatchedObj) ) { *partialMatchedObj = obj; } } } } return NULL; } /********************************************************************/ /** * Find an object with the specified name * * \param que Q of obj_template_t to search * \param modname module name that defines the obj_template_t * ( NULL and first match will be done, and the * module ignored (Name instead of QName ) * \param objname object name to find * \param lookdeep TRUE to check objects inside choices/cases and match * these nodes before matching the choice or case * \param partialmatch TRUE if a strncmp (partial match ) is reqquried. * \param usecase == TRUE if case-sensitive * FALSE if case-insensitive * \param altnames == TRUE if altnames allowed * FALSE if normal names only * \param dataonly == TRUE to check just data nodes * FALSE to check all nodes * \param matchcount == address of return parameter match count * \return pointer to obj_template_t or NULL if not found in 'que' *********************************************************************/ static obj_template_t* find_template (dlq_hdr_t *que, const xmlChar *modname, const xmlChar *objname, boolean lookdeep, boolean partialmatch, boolean usecase, boolean altnames, boolean dataonly, uint32 *matchcount ) { obj_template_t *partialMatchedObj = NULL; obj_template_t *matchedObj = NULL; const xmlChar *curname; // the name of the current obj being checked const xmlChar *curmodulename; // the name of the current mod being checked bool nameMatch; uint32 len_objname = ( partialmatch ? xml_strlen(objname) : 0 ); *matchcount = 0; /* check all the objects in this datadefQ */ obj_template_t* obj = (obj_template_t *)dlq_firstEntry(que); for( ; obj; obj = (obj_template_t *)dlq_nextEntry(obj) ) { /* skip augment and uses */ if (!obj_has_name(obj) || !obj_is_enabled(obj)) { continue; } /* skip rpc and notifications for dataonly matching */ if (dataonly && (obj_is_rpc(obj) || obj_is_notif(obj)) ) { continue; } /* skip objects with no name / altname */ curname = (altnames ? obj_get_altname(obj) : obj_get_name(obj) ); if ( !curname ) { continue; } curmodulename = obj_get_mod_name(obj); nameMatch = compare_names( objname, curname, partialmatch, usecase, len_objname ); if (!lookdeep) { /* if lookdeep == FALSE then check the choice name of an * OBJ_TYP_CHOICE or OBJ_TYP_CASE object. */ /* if the module does not match the current module name, skip it */ if ( modname && xml_strcmp(modname, curmodulename) ) { continue; } matchedObj = handle_partial_match_found( obj, nameMatch, partialmatch, lookdeep, matchcount, &partialMatchedObj ); if ( matchedObj ) { return matchedObj; } } switch ( obj->objtype ) { case OBJ_TYP_CHOICE: /* since the choice and case layers disappear, need to check if any * real node names would clash will also check later that all choice * nodes within the same sibling set do not clash either */ matchedObj = search_choice( obj, modname, objname, lookdeep, partialmatch, usecase, altnames, dataonly, nameMatch, matchcount, &partialMatchedObj ); if ( matchedObj ) { return matchedObj; } break; case OBJ_TYP_CASE: matchedObj = search_case( obj, modname, objname, lookdeep, partialmatch, usecase, altnames, dataonly, nameMatch, matchcount, &partialMatchedObj ); if ( matchedObj ) { return matchedObj; } break; default: /* check if a specific module name requested, * and if so, skip any object not from that module */ if (lookdeep) { if (modname && xml_strcmp(modname, curmodulename)) { continue; } matchedObj = handle_partial_match_found( obj, nameMatch, partialmatch, lookdeep, matchcount, &partialMatchedObj ); if ( matchedObj ) { return matchedObj; } } } } if (partialmatch) { return partialMatchedObj; } return NULL; } /* find_template */ /******************************************************************** * FUNCTION get_config_flag * * Get the config flag for an obj_template_t * Return the explicit value or the inherited value * Also return if the config-stmt is really set or not * * INPUTS: * obj == obj_template to check * setflag == address of return config-stmt set flag * * OUTPUTS: * *setflag == TRUE if the config-stmt is set in this * node, or if it is a top-level object * == FALSE if the config-stmt is inherited from its parent * * RETURNS: * TRUE if config set to TRUE * FALSE if config set to FALSE * *********************************************************************/ static boolean get_config_flag (const obj_template_t *obj, boolean *setflag) { switch (obj->objtype) { case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_CONTAINER: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: case OBJ_TYP_LIST: case OBJ_TYP_CHOICE: if (obj_is_root(obj)) { *setflag = TRUE; return TRUE; } else if ((obj->parent && !obj_is_root(obj->parent)) || obj->grp) { *setflag = (obj->flags & OBJ_FL_CONFSET) ? TRUE : FALSE; } else { *setflag = TRUE; } return (obj->flags & OBJ_FL_CONFIG) ? TRUE : FALSE; case OBJ_TYP_CASE: *setflag = FALSE; if (obj->parent) { return (obj->parent->flags & OBJ_FL_CONFIG) ? TRUE : FALSE; } else { /* should not happen */ return FALSE; } /*NOTREACHED*/ case OBJ_TYP_USES: case OBJ_TYP_AUGMENT: case OBJ_TYP_REFINE: /* no real setting -- not applicable */ *setflag = FALSE; return FALSE; case OBJ_TYP_RPC: /* no real setting for this, but has to be true * to allow rpc/input to be true */ *setflag = FALSE; return TRUE; case OBJ_TYP_RPCIO: *setflag = FALSE; if (!xml_strcmp(obj->def.rpcio->name, YANG_K_INPUT)) { return TRUE; } else { return FALSE; } break; case OBJ_TYP_NOTIF: *setflag = FALSE; return FALSE; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* get_config_flag */ /******************************************************************** * FUNCTION get_object_string * * Generate the object identifier string * * INPUTS: * obj == node to generate the instance ID for * stopobj == ancestor node to treat as root (may be NULL) * buff == buffer to use (may be NULL) * bufflen == length of buffer to use (0 to ignore check) * normalmode == TRUE for a real Xpath OID * == FALSE to generate a big element name to use * for augment substitutionGroup name generation * mod == module in progress for C code generation * == NULL for other purposes * retlen == address of return length * withmodname == TRUE if force modname in string * forcexpath == TRUE to generate a value tree absolute path expr * FALSE to use normalnode var to determine mode * OUTPUTS * *retlen == length of object identifier string * if buff non-NULL: * *buff filled in with string * * RETURNS: * status *********************************************************************/ static status_t get_object_string (const obj_template_t *obj, const obj_template_t *stopobj, xmlChar *buff, uint32 bufflen, boolean normalmode, ncx_module_t *mod, uint32 *retlen, boolean withmodname, boolean forcexpath) { *retlen = 0; boolean addmodname = withmodname || forcexpath; boolean topnode = FALSE; if (obj->parent && obj->parent != stopobj && !obj_is_root(obj->parent)) { status_t res = get_object_string(obj->parent, stopobj, buff, bufflen, normalmode, mod, retlen, withmodname, forcexpath); if (res != NO_ERR) { return res; } } else { topnode = TRUE; } if (!obj_has_name(obj)) { /* should not enounter a uses or augment!! */ return NO_ERR; } if (forcexpath && (obj->objtype == OBJ_TYP_CHOICE || obj->objtype == OBJ_TYP_CASE)) { return NO_ERR; } const xmlChar *modname = NULL; uint32 modnamelen = 0; if (forcexpath) { modname = xmlns_get_ns_prefix(obj_get_nsid(obj)); if (!modname) { return SET_ERROR(ERR_INTERNAL_VAL); } modnamelen = xml_strlen(modname); } else { modname = obj_get_mod_name(obj); modnamelen = xml_strlen(modname); } if (!addmodname && mod != NULL && (xml_strcmp(modname, ncx_get_modname(mod)))) { addmodname = TRUE; } /* get the name and check the added length */ const xmlChar *name = obj_get_name(obj); uint32 namelen = xml_strlen(name), seplen = 1; if (topnode && stopobj) { seplen = 0; } if (bufflen && (((*retlen) + namelen + (addmodname?modnamelen:0) + seplen + 1) > bufflen)) { return ERR_BUFF_OVFL; } /* copy the name string recusively, letting the stack * keep track of the next child node to write */ if (buff) { /* node separator char */ if (topnode && stopobj) { ; } else if (normalmode) { buff[*retlen] = '/'; } else { buff[*retlen] = '.'; } if (addmodname) { xml_strcpy(&buff[*retlen + seplen], modname); buff[*retlen + modnamelen + seplen] = (forcexpath || withmodname) ? ':' : '_'; xml_strcpy(&buff[*retlen + modnamelen + seplen + 1], name); } else { xml_strcpy(&buff[*retlen + seplen], name); } } if (addmodname) { *retlen += (namelen + modnamelen + seplen + 1); } else { *retlen += (namelen + seplen); } return NO_ERR; } /* get_object_string */ /******************************************************************** * FUNCTION find_next_child * * Check the instance qualifiers and see if the specified node * is a valid (subsequent) child node. * * Example: * * container foo { * leaf a { type int32; } * leaf b { type int32; } * leaf-list c { type int32; } * * Since a, b, and c are all optional, all of them have to be * checked, even while node 'a' is expected * The caller will save the current child in case the pointer * needs to be backed up. * * INPUTS: * chobj == current child object template * chnode == xml_node_t of start element to match * * RETURNS: * pointer to child that matched or NULL if no valid next child *********************************************************************/ static obj_template_t * find_next_child (obj_template_t *chobj, const xml_node_t *chnode) { obj_template_t *chnext, *foundobj; status_t res; chnext = chobj; for (;;) { switch (obj_get_iqualval(chnext)) { case NCX_IQUAL_ONE: case NCX_IQUAL_1MORE: /* the current child is mandatory; this is an error */ return NULL; /* else fall through to next case */ case NCX_IQUAL_OPT: case NCX_IQUAL_ZMORE: /* the current child is optional; keep trying * try to get the next child in the complex type */ chnext = obj_next_child(chnext); if (!chnext) { return NULL; } else { if ( obj_is_choice_or_case( chnext ) ) { foundobj = obj_find_child(chnext, xmlns_get_module(chnode->nsid), chnode->elname); if (foundobj && obj_is_choice_or_case( foundobj ) ) { foundobj = NULL; } if (foundobj) { return chnext; /* not the nested foundobj! */ } } else { res = xml_node_match(chnode, obj_get_nsid(chnext), obj_get_name(chnext), XML_NT_NONE); if (res == NO_ERR) { return chnext; } } } break; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /*NOTREACHED*/ } /* find_next_child */ /******************************************************************** * FUNCTION process_one_walker_child * * Process one child object node * * INPUTS: * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * obj == object to process * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * childname == name of child node to find * == NULL to match any child name * configonly = TRUE for config=true only * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * fncalled == address of return function called flag * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ static boolean process_one_walker_child (obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *obj, const xmlChar *modname, const xmlChar *childname, boolean configonly, boolean textmode, boolean *fncalled) { boolean fnresult; *fncalled = FALSE; if (!obj_has_name(obj)) { return TRUE; } if (configonly && !childname && !obj_is_config(obj)) { return TRUE; } fnresult = TRUE; if (textmode) { if (obj_is_leafy(obj)) { fnresult = (*walkerfn)(obj, cookie1, cookie2); *fncalled = TRUE; } } else if (modname && childname) { if (!xml_strcmp(modname, obj_get_mod_name(obj)) && !xml_strcmp(childname, obj_get_name(obj))) { fnresult = (*walkerfn)(obj, cookie1, cookie2); *fncalled = TRUE; } } else if (modname) { if (!xml_strcmp(modname, obj_get_mod_name(obj))) { fnresult = (*walkerfn)(obj, cookie1, cookie2); *fncalled = TRUE; } } else if (childname) { if (!xml_strcmp(childname, obj_get_name(obj))) { fnresult = (*walkerfn)(obj, cookie1, cookie2); *fncalled = TRUE; } } else { fnresult = (*walkerfn)(obj, cookie1, cookie2); *fncalled = TRUE; } return fnresult; } /* process_one_walker_child */ /******************************************************************** * FUNCTION test_one_child * * The the specified node * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * This function skips choice and case nodes and * only processes real data nodes * * INPUTS: * exprmod == module containing the XPath object * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * obj == node to check * modname == module name; * only matches in this module namespace * will be returned * == NULL: * namespace matching will be skipped * name == name of node to find * == NULL to match any name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if childname == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ static boolean test_one_child (ncx_module_t *exprmod, obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *obj, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode) { boolean fnresult, fncalled; if ( obj_is_choice_or_case( obj ) ) { fnresult = obj_find_all_children(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, FALSE); } else { fnresult = process_one_walker_child(walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, &fncalled); } if (!fnresult) { return FALSE; } return TRUE; } /* test_one_child */ /******************************************************************** * FUNCTION test_one_ancestor * * The the specified node * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * This function skips choice and case nodes and * only processes real data nodes * * INPUTS: * exprmod == module containing object * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * obj == node to check * modname == module name; * only matches in this module namespace * will be returned * == NULL: * namespace matching will be skipped * name == name of node to find * == NULL to match any name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if childname == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * orself == TRUE if axis ancestor-or-self * fncalled == address of return function called flag * * OUTPUTS: * *fncalled set to TRUE if a callback function was called * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ static boolean test_one_ancestor (ncx_module_t *exprmod, obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *obj, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode, boolean orself, boolean *fncalled) { boolean fnresult; if ( obj_is_choice_or_case( obj ) ) { fnresult = obj_find_all_ancestors(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, FALSE, orself, fncalled); } else { fnresult = process_one_walker_child(walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, fncalled); } if (!fnresult) { return FALSE; } return TRUE; } /* test_one_ancestor */ /******************************************************************** * FUNCTION test_one_descendant * * The the specified node * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * This function skips choice and case nodes and * only processes real data nodes * * INPUTS: * exprmod == module containing object * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startobj == node to check * modname == module name; * only matches in this module namespace * will be returned * == NULL: * namespace matching will be skipped * name == name of node to find * == NULL to match any name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if childname == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * orself == TRUE if descendant-or-self test * fncalled == address of return function called flag * * OUTPUTS: * *fncalled set to TRUE if a callback function was called * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ static boolean test_one_descendant (ncx_module_t *exprmod, obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *startobj, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode, boolean orself, boolean *fncalled) { obj_template_t *obj; dlq_hdr_t *datadefQ; boolean fnresult; if (orself) { fnresult = process_one_walker_child(walkerfn, cookie1, cookie2, startobj, modname, name, configonly, textmode, fncalled); if (!fnresult) { return FALSE; } } datadefQ = obj_get_datadefQ(startobj); if (!datadefQ) { return TRUE; } for (obj = (obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if ( obj_is_choice_or_case( obj ) ) { fnresult = obj_find_all_descendants(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, FALSE, orself, fncalled); } else { fnresult = process_one_walker_child(walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, fncalled); if (fnresult && !*fncalled) { fnresult = obj_find_all_descendants(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, FALSE, orself, fncalled); } } if (!fnresult) { return FALSE; } } return TRUE; } /* test_one_descendant */ /******************************************************************** * FUNCTION test_one_pfnode * * The the specified node * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * This function skips choice and case nodes and * only processes real data nodes * * INPUTS: * exprmod == module containing object * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * obj == node to check * modname == module name; * only matches in this module namespace * will be returned * == NULL: * namespace matching will be skipped * childname == name of child node to find * == NULL to match any child name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if childname == NULL * dblslash == TRUE if all decendents of the preceding * or following nodes should be checked * FALSE only 1 level is checked * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * forward == TRUE: forward axis test * FALSE: reverse axis test * axis == axis enum to use * fncalled == address of return function called flag * * OUTPUTS: * *fncalled set to TRUE if a callback function was called * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ static boolean test_one_pfnode (ncx_module_t *exprmod, obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *obj, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean dblslash, boolean textmode, boolean forward, ncx_xpath_axis_t axis, boolean *fncalled) { obj_template_t *child; boolean fnresult, needcont; /* for objects, need to let same node match, because * if there are multiple instances of the object, * it would match */ if (obj->objtype == OBJ_TYP_LIST || obj->objtype == OBJ_TYP_LEAF_LIST) { ; } else if (forward) { obj = (obj_template_t *)dlq_nextEntry(obj); } else { obj = (obj_template_t *)dlq_prevEntry(obj); } while (obj) { needcont = FALSE; if (!obj_has_name(obj)) { needcont = TRUE; } if (configonly && !name && !obj_is_config(obj)) { needcont = TRUE; } if (needcont) { /* get the next node to process */ if (forward) { obj = (obj_template_t *)dlq_nextEntry(obj); } else { obj = (obj_template_t *)dlq_prevEntry(obj); } continue; } if ( obj_is_choice_or_case( obj ) ) { for (child = (forward) ? obj_first_child(obj) : obj_last_child(obj); child != NULL; child = (forward) ? obj_next_child(child) : obj_previous_child(child)) { fnresult = obj_find_all_pfaxis(exprmod, walkerfn, cookie1, cookie2, child, modname, name, configonly, dblslash, textmode, FALSE, axis, fncalled); if (!fnresult) { return FALSE; } } } else { fnresult = process_one_walker_child(walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, fncalled); if (!fnresult) { return FALSE; } if (!*fncalled && dblslash) { /* if /foo did not get added, than * try /foo/bar, /foo/baz, etc. * check all the child nodes even if * one of them matches, because all * matches are needed with the '//' operator */ for (child = (forward) ? obj_first_child(obj) : obj_last_child(obj); child != NULL; child = (forward) ? obj_next_child(child) : obj_previous_child(child)) { fnresult = obj_find_all_pfaxis(exprmod, walkerfn, cookie1, cookie2, child, modname, name, configonly, dblslash, textmode, FALSE, axis, fncalled); if (!fnresult) { return FALSE; } } } } /* get the next node to process */ if (forward) { obj = (obj_template_t *)dlq_nextEntry(obj); } else { obj = (obj_template_t *)dlq_prevEntry(obj); } } return TRUE; } /* test_one_pfnode */ /********************************************************************/ /** * Check if an obj_template_t in the mod->datadefQ or any * of the include files visible to this module * * Top-level access is not tracked, so the 'lookdeep' variable * is hard-wired to FALSE * * \param mod ncx_module to check * \param modname module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * \param objname name of the object name to find * \param match TRUE to partial-length match node names * FALSE to find exact-length match only * \param altnames TRUE if alt-name should be checked * \param usecase TRUE if case-sensitive, FALSE if allow case-insensitive match * \param onematch TRUE if only 1 match allowed FALSE if first match of * N allowed * \param dataonly TRUE to check just data nodes FALSE to check all nodes * \return pointer to struct if present, NULL otherwise *********************************************************************/ static obj_template_t* find_template_top_in_submodules_includes( ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, boolean match, boolean altnames, boolean usecase, boolean dataonly, uint32 *matchcount ) { obj_template_t *obj; /* Q of all includes this [sub]module has seen */ dlq_hdr_t *que = ncx_get_allincQ(mod); ncx_include_t *inc; yang_node_t *node; /* check all the submodules, visible to this module or submodule */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc; inc = (ncx_include_t *)dlq_nextEntry(inc)) { /* get the real submodule struct */ if ( !inc->submod ) { node = yang_find_node(que, inc->submodule, inc->revision); if( !node || !node->submod ) { /* include not found, skip this one */ continue; } inc->submod = node->submod; } /* check the type Q in this submodule */ obj = find_template( &inc->submod->datadefQ, modname, objname, FALSE, match, usecase, altnames, dataonly, matchcount ); if (obj) { return obj; } } return NULL; } /********************************************************************/ /** * Handle flags for multiple matches, setting the return status and * return value accordingly. * * If the find request specified single full length match and multiple * matches were found, set the return status to * ERR_NCX_MULTIPLE_MATCHES and return NULL, otherwise return the * supplied object_template. */ static obj_template_t* rationalise_multiple_matches( obj_template_t* obj, boolean match, boolean onematch, uint32 matchcount, status_t *retres ) { if (match && onematch && matchcount > 1) { *retres = ERR_NCX_MULTIPLE_MATCHES; return NULL; } return obj; } /********************************************************************/ /** * Check if an obj_template_t in the mod->datadefQ or any * of the include files visible to this module * * Top-level access is not tracked, so the 'lookdeep' variable * is hard-wired to FALSE * * \param mod ncx_module to check * \param modname module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * \param objname name of the object name to find * \param match TRUE to partial-length match node names * FALSE to find exact-length match only * \param altnames TRUE if alt-name should be checked * \param usecase TRUE if case-sensitive, FALSE if allow case-insensitive match * \param dataonly TRUE to check just data nodes FALSE to check all nodes * \return pointer to struct if present, NULL otherwise *********************************************************************/ static obj_template_t* find_template_top_in_paraent_module( ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, boolean match, boolean altnames, boolean usecase, boolean dataonly, uint32 *matchcount ) { if ( !mod->ismod ) { ncx_module_t *mainmod = ncx_get_parent_mod(mod); if ( mainmod ) { /* check the [sub]module datadefQ */ return find_template( &mainmod->datadefQ, modname, objname, FALSE, match, usecase, altnames, dataonly, matchcount ); } } return NULL; } /********************************************************************/ /** * FUNCTION find_template_top * * Check if an obj_template_t in the mod->datadefQ or any * of the include files visible to this module * * Top-level access is not tracked, so the 'lookdeep' variable * is hard-wired to FALSE * * \param mod ncx_module to check * \param modname module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * \param objname name of the object name to find * \param match TRUE to partial-length match node names * FALSE to find exact-length match only * \param altnames TRUE if alt-name should be checked * \param usecase TRUE if case-sensitive, FALSE if allow case-insensitive match * \param onematch TRUE if only 1 match allowed FALSE if first match of * N allowed * \param dataonly TRUE to check just data nodes FALSE to check all nodes * \param retres output return status * \return pointer to struct if present, NULL otherwise *********************************************************************/ static obj_template_t* find_template_top( ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, boolean match, boolean altnames, boolean usecase, boolean onematch, boolean dataonly, status_t *retres ) { obj_template_t *obj; uint32 matchcount = 0; *retres = NO_ERR; /* check the [sub]module datadefQ */ obj = find_template( &mod->datadefQ, modname, objname, FALSE, match, usecase, altnames, dataonly, &matchcount ); if ( obj ) { return rationalise_multiple_matches( obj, match, onematch, matchcount, retres ); } /* Check Submodule Includes */ obj = find_template_top_in_submodules_includes( mod, modname, objname, match, altnames, usecase, dataonly, &matchcount ); if ( obj ) { return rationalise_multiple_matches( obj, match, onematch, matchcount, retres ); } /* if this is a submodule, then still need to check * the datadefQ of the main module */ obj = find_template_top_in_paraent_module( mod, modname, objname, match, altnames, usecase, dataonly, &matchcount ); if ( obj ) { return rationalise_multiple_matches( obj, match, onematch, matchcount, retres ); } *retres = ERR_NCX_DEF_NOT_FOUND; return NULL; } /* find_template_top */ /******************************************************************** * FUNCTION count_keys * * Count the keys; walker function * * INPUTS: * obj == the key object * cookie1 == the running count * cookie2 = not used * RETURNS: * TRUE to keep walking *********************************************************************/ static boolean count_keys (obj_template_t *obj, void *cookie1, void *cookie2) { uint32 *count = (uint32 *)cookie1; (void)obj; (void)cookie2; (*count)++; return TRUE; } /* count_keys */ /********************************************************************/ /** * Try to find an object using an exact match. * * \param mod ncx_module to check * \param modname the module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * \param objname the object name to find * \param dataonly TRUE to check just data nodes FALSE to check all nodes * \param retres the return status * \return * pointer to struct if present, NULL otherwise *********************************************************************/ static obj_template_t* try_exact_match ( ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, boolean dataonly, status_t *retres ) { return find_template_top( mod, modname, objname, FALSE, /* match */ FALSE, /* altnames */ TRUE, /* usecase */ FALSE, /* onematch */ dataonly, retres ); } /********************************************************************/ /** * Try to find an object using a case insensitive exact length match * * \param mod ncx_module to check * \param modname the module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * \param objname the object name to find * \param dataonly TRUE to check just data nodes FALSE to check all nodes * \param retres the return status * \return * pointer to struct if present, NULL otherwise *********************************************************************/ static obj_template_t* try_case_insensitive_exact_length ( ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, boolean dataonly, status_t *retres ) { return find_template_top( mod, modname, objname, FALSE, /* match */ FALSE, /* altnames */ FALSE, /* usecase */ FALSE, /* onematch */ dataonly, retres ); } /********************************************************************/ /** * Try to find an object using a case sensitive partial name match * * \param mod ncx_module to check * \param modname the module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * \param objname the object name to find * \param dataonly TRUE to check just data nodes FALSE to check all nodes * \param onematch TRUE if only 1 match allowed * FALSE if first match of N allowed * \param retres the return status * \return * pointer to struct if present, NULL otherwise *********************************************************************/ static obj_template_t* try_case_sensitive_partial_match ( ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, boolean dataonly, boolean onematch, status_t *retres ) { return find_template_top( mod, modname, objname, TRUE, /* match */ FALSE, /* altnames */ TRUE, /* usecase */ onematch, dataonly, retres ); } /********************************************************************/ /** * Try to find an object using a case insensitive partial name match * * \param mod ncx_module to check * \param modname the module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * \param objname the object name to find * \param dataonly TRUE to check just data nodes FALSE to check all nodes * \param onematch TRUE if only 1 match allowed * FALSE if first match of N allowed * \param retres the return status * \return * pointer to struct if present, NULL otherwise *********************************************************************/ static obj_template_t* try_case_insensitive_partial_match( ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, boolean dataonly, boolean onematch, status_t *retres ) { return find_template_top( mod, modname, objname, TRUE, /* match */ FALSE, /* altnames */ FALSE, /* usecase */ onematch, dataonly, retres ); } /********************************************************************/ /** * Try to find an object using an exact match alt name. * * \param mod ncx_module to check * \param modname the module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * \param objname the object name to find * \param dataonly TRUE to check just data nodes FALSE to check all nodes * \param retres the return status * \return * pointer to struct if present, NULL otherwise *********************************************************************/ static obj_template_t* try_exact_match_alt_name ( ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, boolean dataonly, status_t *retres ) { return find_template_top( mod, modname, objname, FALSE, /* match */ TRUE, /* altnames */ TRUE, /* usecase */ FALSE, /* onematch */ dataonly, retres ); } /********************************************************************/ /** * Try to find an object using a case insensitive exact match alt name. * * \param mod ncx_module to check * \param modname the module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * \param objname the object name to find * \param dataonly TRUE to check just data nodes FALSE to check all nodes * \param retres the return status * \return * pointer to struct if present, NULL otherwise *********************************************************************/ static obj_template_t* try_case_insensitive_exact_alt_name ( ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, boolean dataonly, status_t *retres ) { return find_template_top( mod, modname, objname, FALSE, /* match */ TRUE, /* altnames */ FALSE, /* usecase */ FALSE, /* onematch */ dataonly, retres ); } /********************************************************************/ /** * Try to find an object using a case sensitive partial match alt name. * * \param mod ncx_module to check * \param modname the module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * \param objname the object name to find * \param dataonly TRUE to check just data nodes FALSE to check all nodes * \param onematch TRUE if only 1 match allowed * FALSE if first match of N allowed * \param retres the return status * \return * pointer to struct if present, NULL otherwise *********************************************************************/ static obj_template_t* try_case_sensitive_partial_alt_name ( ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, boolean dataonly, boolean onematch, status_t *retres ) { return find_template_top( mod, modname, objname, TRUE, /* match */ TRUE, /* altnames */ TRUE, /* usecase */ onematch, /* onematch */ dataonly, retres ); } /******************************************************************** * Try to find an object using a case insensitive partial match alt name. * * \param mod ncx_module to check * \param modname the module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * \param objname the object name to find * \param dataonly TRUE to check just data nodes FALSE to check all nodes * \param onematch TRUE if only 1 match allowed * FALSE if first match of N allowed * \param retres the return status * \return * pointer to struct if present, NULL otherwise *********************************************************************/ static obj_template_t* try_case_insensitive_partial_alt_name( ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, boolean dataonly, boolean onematch, status_t *retres ) { return find_template_top( mod, modname, objname, TRUE, /* match */ TRUE, /* altnames */ FALSE, /* usecase */ onematch, /* onematch */ dataonly, retres ); } /******************************************************************** * Check if the RPC object has any real input children * * \param obj the obj_template to check * \param chkParam the name of the parameter to check for * \return TRUE if there are any input children *********************************************************************/ static boolean obj_rpc_has_input_or_output( obj_template_t *obj, const xmlChar* chkParam ) { assert(obj && "obj is NULL" ); if (obj->objtype != OBJ_TYP_RPC) { return FALSE; } obj_template_t* childobj = obj_find_child( obj, obj_get_mod_name(obj), chkParam ); if (childobj) { return obj_has_children(childobj); } else { return FALSE; } } /* obj_rpc_has_input_or_output */ /******************************************************************** * Try to find the module for an object using its nsid. * * \param node the node to use. * \param force_modQ the Q of ncx_module_t to check * \return pointer to the ncx_module or NULL. *********************************************************************/ static ncx_module_t* find_module_from_nsid( const xml_node_t* node, dlq_hdr_t *force_modQ ) { ncx_module_t* foundmod = NULL; if (node->nsid) { if (node->nsid == xmlns_nc_id() || !force_modQ ) { foundmod = xmlns_get_modptr(node->nsid); } else if (force_modQ) { /* try a session module Q */ foundmod = ncx_find_module_que_nsid( force_modQ, node->nsid); if ( !foundmod ) { /* try a client-loaded module */ foundmod = xmlns_get_modptr(node->nsid); } } } return foundmod; } /******************************************************************** * Try to find the module name for an object using its nsid. * * \param node the node to use. * \param force_modQ the Q of ncx_module_t to check * \return pointer to the modulename *********************************************************************/ static const xmlChar* get_module_name_from_nsid( const xml_node_t* node, dlq_hdr_t *force_modQ ) { ncx_module_t *foundmod = find_module_from_nsid( node, force_modQ ); return ( foundmod ? ncx_get_modname(foundmod) : NULL ); } /******************************************************************** * Utility function for validating the type of a founc child node. * Check that the child object is a valid type. If the child node is * of a valid type this function simply returns it, otherwise it * returns NULL. * * \param obj the parent objext template * \param foundobj the child node * *********************************************************************/ static obj_template_t* validated_child_object( obj_template_t* obj, obj_template_t* foundobj ) { if ( foundobj ) { if ( !obj_is_data_db(foundobj) || obj_is_abstract(foundobj) || obj_is_cli(foundobj) || obj_is_choice_or_case( obj ) ) { return NULL; } } return foundobj; } /******************************************************************** * Check if an object is a element * * \param obj the parent objext template * \return True if the NSID of the object is the NCNID and the * object's name is NCX_EL_NOTIFICATION * * @TODO: How does this test differ from calling obj_is_notif *********************************************************************/ static bool obj_is_notification_parent( obj_template_t* obj ) { return ( obj_get_nsid(obj) == xmlns_ncn_id() && !xml_strcmp( obj_get_name( obj ), NCX_EL_NOTIFICATION ) ); } /******************************************************************** * Get the child node for the supplied root node. * * \param obj the parent object template * \param curnode the current XML start or empty node to check * \param force_modQ the Q of ncx_module_t to check * \return the found object or NULL; *********************************************************************/ static obj_template_t* get_child_node_from_root( obj_template_t* obj, const xml_node_t* curnode, dlq_hdr_t *force_modQ ) { ncx_module_t *foundmod = find_module_from_nsid( curnode, force_modQ );; const xmlChar *foundmodname = ( foundmod ? ncx_get_modname(foundmod) : NULL ); obj_template_t* foundobj = NULL; /* the child node can be any top-level object in the configuration */ if (foundmodname) { /* get the name from 1 module */ foundobj = ncx_find_object( foundmod, curnode->elname ); } else if (force_modQ) { /* check this Q of modules for a top-level match */ foundobj = ncx_find_any_object_que(force_modQ, curnode->elname); if ( !foundobj && curnode->nsid == 0) { foundobj = ncx_find_any_object(curnode->elname); } } else { /* NSID not set, get the name from any module */ foundobj = ncx_find_any_object(curnode->elname); } return validated_child_object( obj, foundobj ); } /******************************************************************** * Get the child node for the supplied notification node. * * hack: special case handling of the element the child * node can be or any top-level OBJ_TYP_NOTIF node * * \param obj the parent object template * \param curnode the current XML start or empty node to check * \param force_modQ the Q of ncx_module_t to check, if set to NULL and the * xmlns registry of module pointers will be used instead * \return the found object or NULL; *********************************************************************/ static obj_template_t* get_child_node_for_notif( obj_template_t* obj, const xml_node_t* curnode, dlq_hdr_t *force_modQ ) { ncx_module_t *foundmod = find_module_from_nsid( curnode, force_modQ );; const xmlChar *foundmodname = ( foundmod ? ncx_get_modname(foundmod) : NULL ); obj_template_t* foundobj = NULL; if (foundmodname) { /* try a child of */ foundobj = obj_find_child(obj, foundmodname, curnode->elname); if (!foundobj) { /* try to find an */ foundobj = ncx_find_object( foundmod, curnode->elname); // check the foundobj is a notification type if ( foundobj && !obj_is_notif(foundobj) ) { /* object is the wrong type */ foundobj = NULL; } } } else { /* no namespace ID used try to find any eventType object */ if (force_modQ) { foundobj = ncx_find_any_object_que(force_modQ, curnode->elname); } else { foundobj = ncx_find_any_object(curnode->elname); } if (foundobj) { // check the foundobj is a notification type if ( obj_is_notif( foundobj ) ) { foundobj = NULL; } } else { /* try a child of obj (eventTime), no need to check type, * since any child is a direct descendent of the * notification object */ foundobj = obj_find_child(obj, NULL, curnode->elname ); } } return foundobj; } /******************************************************************** * Get the child node for the curnode based on xml order. * * \param curnode the current XML start or empty node to check * \param chobj the current child node. * \param force_modQ the Q of ncx_module_t to check * \param rettop the address of return topchild object * \param topdone flag indicating if rettop was set * \return the found object or NULL; *********************************************************************/ static obj_template_t* get_xml_ordered_child_node( const xml_node_t* curnode, obj_template_t* chobj, dlq_hdr_t *force_modQ, obj_template_t **rettop, boolean* topdone ) { /* the current node or one of the subsequent child nodes must match */ if (!chobj) { return NULL; } const xmlChar *foundmodname = get_module_name_from_nsid( curnode, force_modQ ); obj_template_t* foundobj = NULL; if ( obj_is_choice_or_case( chobj ) ) { /* these nodes are not really in the XML so check all the child nodes * of the cases. When found, need to remember the current child node * at the choice or case level, so when the lower level child pointer * runs out, the search can continue at the next sibling of 'rettop' */ foundobj = obj_find_child(chobj, foundmodname, curnode->elname); if (foundobj) { /* make sure this matched a real node instead */ if ( obj_is_choice_or_case( foundobj ) ) { foundobj = NULL; } else { *rettop = chobj; *topdone = TRUE; } } } else { /* the YANG node and XML node line up, ergo compare them directly */ if ( NO_ERR == xml_node_match( curnode, obj_get_nsid(chobj), obj_get_name(chobj), XML_NT_NONE ) ) { foundobj = chobj; } else { foundobj = NULL; } } if (!foundobj) { /* check if there are other child nodes that could match, due to * instance qualifiers */ obj_template_t *nextchobj = find_next_child(chobj, curnode); if (nextchobj) { foundobj = nextchobj; } else if (*rettop) { // @TODO: Why is rettop being used as an input parameter here? it is // only set of we have found an object by obj_get_child_node! nextchobj = find_next_child(*rettop, curnode); if (nextchobj) { foundobj = nextchobj; } } } return foundobj; } /******************************************************************** * Get any matching node within the current parent, regardless of xml order. * * \param obj the parent object template * \param curnode the current XML start or empty node to check * \param force_modQ the Q of ncx_module_t to check * \return the found object or NULL; *********************************************************************/ static obj_template_t* get_first_matching_child_node( obj_template_t* obj, const xml_node_t* curnode, dlq_hdr_t *force_modQ ) { const xmlChar *foundmodname = get_module_name_from_nsid( curnode, force_modQ ); obj_template_t* foundobj = obj_find_child( obj, foundmodname, curnode->elname ); return (foundobj && obj_is_choice_or_case( foundobj ) ) ? NULL : foundobj; } /******************************************************************** * Search for a child of the current XML node. * * \param obj the parent object template * \param chobj the current child node * \param xmlorder TRUE if should follow strict XML element order, * \param curnode the current XML start or empty node to check * \param force_modQ the Q of ncx_module_t to check * \param rettop the address of return topchild object * \param topdone flag indicating if rettop was set * \return the found object or NULL; *********************************************************************/ static obj_template_t* search_for_child_node( obj_template_t *obj, obj_template_t *chobj, const xml_node_t *curnode, boolean xmlorder, dlq_hdr_t *force_modQ, obj_template_t **rettop, boolean *topdone ) { if (obj_is_root(obj)) { return get_child_node_from_root( obj, curnode, force_modQ ); } else if ( obj_is_notification_parent( obj ) ) { return get_child_node_for_notif( obj, curnode, force_modQ ); } else if (xmlorder) { return get_xml_ordered_child_node( curnode, chobj, force_modQ, rettop, topdone ); } else { return get_first_matching_child_node( obj, curnode, force_modQ ); } } /******************************************************************** * Clean the inherited iffeature queue * * \param ptrQ the Q of obj_iffeature_ptr_t to clean *********************************************************************/ static void clean_inherited_iffeatureQ( dlq_hdr_t *ptrQ ) { obj_iffeature_ptr_t *iffptr; while (!dlq_empty(ptrQ)) { iffptr = (obj_iffeature_ptr_t *)dlq_deque(ptrQ); obj_free_iffeature_ptr(iffptr); } } /******************************************************************** * Clean the inherited when-stmt queue * * \param ptrQ the Q of obj_xpath_ptr_t to clean *********************************************************************/ static void clean_inherited_whenQ( dlq_hdr_t *ptrQ ) { obj_xpath_ptr_t *xptr; while (!dlq_empty(ptrQ)) { xptr = (obj_xpath_ptr_t *)dlq_deque(ptrQ); obj_free_xpath_ptr(xptr); } } /**************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * Malloc and initialize the fields in a an object template * * \param objtype the specific object type to create * \return pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ obj_template_t* obj_new_template( obj_type_t objtype ) { obj_template_t *obj; if ( objtype == OBJ_TYP_NONE || objtype > OBJ_TYP_RPCIO ) { SET_ERROR( ERR_INTERNAL_VAL ); return NULL; } obj = m__getObj(obj_template_t); if (!obj) { return NULL; } init_template(obj); obj->objtype = objtype; switch (objtype) { case OBJ_TYP_CONTAINER: obj->def.container = new_container(TRUE); break; case OBJ_TYP_LEAF: case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: obj->def.leaf = new_leaf(TRUE); break; case OBJ_TYP_LEAF_LIST: obj->def.leaflist = new_leaflist(TRUE); break; case OBJ_TYP_LIST: obj->def.list = new_list(TRUE); break; case OBJ_TYP_CHOICE: obj->def.choic = new_choice(TRUE); break; case OBJ_TYP_CASE: obj->def.cas = new_case(TRUE); break; case OBJ_TYP_USES: obj->def.uses = new_uses(TRUE); break; case OBJ_TYP_REFINE: obj->def.refine = new_refine(); break; case OBJ_TYP_AUGMENT: obj->def.augment = new_augment(TRUE); break; case OBJ_TYP_RPC: obj->def.rpc = new_rpc(); break; case OBJ_TYP_RPCIO: obj->def.rpcio = new_rpcio(); break; case OBJ_TYP_NOTIF: obj->def.notif = new_notif(); break; default: break; } // any member of the union def can be used to check that the object was // allocated correctly if ( !obj->def.container ) { m__free( obj ); obj = NULL; } return obj; } /* obj_new_template */ /******************************************************************** * Scrub the memory in a obj_template_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * \param obj obj_template_t data structure to free *********************************************************************/ void obj_free_template( obj_template_t *obj ) { if (!obj) { return; } #ifdef OBJ_MEM_DEBUG if (obj_is_cloned(obj)) { log_debug4("\nobj_free: %p (cloned)", obj); } else { log_debug4("\nobj_free: %p (%s)", obj, obj_get_name(obj)); } #endif clean_metadataQ(&obj->metadataQ); ncx_clean_appinfoQ(&obj->appinfoQ); ncx_clean_iffeatureQ(&obj->iffeatureQ); clean_inherited_iffeatureQ(&obj->inherited_iffeatureQ); clean_inherited_whenQ(&obj->inherited_whenQ); xpath_free_pcb(obj->when); switch (obj->objtype) { case OBJ_TYP_CONTAINER: free_container(obj->def.container, obj->flags); break; case OBJ_TYP_LEAF: case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: free_leaf(obj->def.leaf, obj->flags); break; case OBJ_TYP_LEAF_LIST: free_leaflist(obj->def.leaflist, obj->flags); break; case OBJ_TYP_LIST: free_list(obj->def.list, obj->flags); break; case OBJ_TYP_CHOICE: free_choice(obj->def.choic, obj->flags); break; case OBJ_TYP_CASE: free_case(obj->def.cas); break; case OBJ_TYP_USES: free_uses(obj->def.uses); break; case OBJ_TYP_REFINE: free_refine(obj->def.refine); break; case OBJ_TYP_AUGMENT: free_augment(obj->def.augment); break; case OBJ_TYP_RPC: free_rpc(obj->def.rpc, obj->flags); break; case OBJ_TYP_RPCIO: free_rpcio(obj->def.rpcio, obj->flags); break; case OBJ_TYP_NOTIF: free_notif(obj->def.notif, obj->flags); break; case OBJ_TYP_NONE: // no object type specific fields set yet break; default: SET_ERROR(ERR_INTERNAL_VAL); } m__free(obj); } /* obj_free_template */ /******************************************************************** * FUNCTION obj_find_template * * Find an object with the specified name * * INPUTS: * que == Q of obj_template_t to search * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find * * RETURNS: * pointer to obj_template_t or NULL if not found in 'que' *********************************************************************/ obj_template_t * obj_find_template ( dlq_hdr_t *que, const xmlChar *modname, const xmlChar *objname ) { assert( que && "que is NULL" ); assert( objname && "objname is NULL" ); uint32 matchcount=0; return find_template( que, modname, objname, FALSE, /* lookdeep */ FALSE, /* match */ TRUE, /* usecase */ FALSE, /* altnames */ FALSE, /* dataonly */ &matchcount ); } /* obj_find_template */ /******************************************************************** * FUNCTION obj_find_template_con * * Find an object with the specified name * Return a const pointer; used by yangdump * * INPUTS: * que == Q of obj_template_t to search * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find * * RETURNS: * pointer to obj_template_t or NULL if not found in 'que' *********************************************************************/ const obj_template_t * obj_find_template_con (dlq_hdr_t *que, const xmlChar *modname, const xmlChar *objname) { assert( que && "que is NULL" ); assert( objname && "objname is NULL" ); uint32 matchcount=0; return find_template( que, modname, objname, FALSE, /* lookdeep */ FALSE, /* match */ TRUE, /* usecase */ FALSE, /* altnames */ FALSE, /* dataonly */ &matchcount ); } /* obj_find_template_con */ /******************************************************************** * FUNCTION obj_find_template_test * * Find an object with the specified name * * INPUTS: * que == Q of obj_template_t to search * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find * * RETURNS: * pointer to obj_template_t or NULL if not found in 'que' *********************************************************************/ obj_template_t * obj_find_template_test (dlq_hdr_t *que, const xmlChar *modname, const xmlChar *objname) { assert( que && "que is NULL" ); assert( objname && "objname is NULL" ); uint32 matchcount=0; return find_template( que, modname, objname, TRUE, /* lookdeep */ FALSE, /* match */ TRUE, /* usecase */ FALSE, /* altnames */ FALSE, /* dataonly */ &matchcount ); } /* obj_find_template_test */ /******************************************************************** * FUNCTION obj_find_template_top * * Check if an obj_template_t in the mod->datadefQ or any * of the include files visible to this module * * Top-level access is not tracked, so the 'test' variable * is hard-wired to FALSE * * INPUTS: * mod == ncx_module to check * modname == module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * objname == object name to find * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ obj_template_t * obj_find_template_top (ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname) { status_t res; return obj_find_template_top_ex(mod, modname, objname, NCX_MATCH_EXACT, FALSE, FALSE, &res); } /* obj_find_template_top */ /******************************************************************** * FUNCTION obj_find_template_top_ex * * Check if an obj_template_t in the mod->datadefQ or any * of the include files visible to this module * * Top-level access is not tracked, so the 'test' variable * is hard-wired to FALSE * * INPUTS: * mod == ncx_module to check * modname == module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * objname == object name to find * match_names == enum for selected match names mode * alt_names == TRUE if alt-name should be checked in addition * to the YANG node name * == FALSE to check YANG names only * dataonly == TRUE to check just data nodes * FALSE to check all nodes * retres == address of return status * * OUTPUTS: * *retres set to return status * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ obj_template_t * obj_find_template_top_ex ( ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname, ncx_name_match_t match_names, boolean alt_names, boolean dataonly, status_t *retres ) { obj_template_t *obj; boolean multmatches = FALSE; // note: modname may be NULL assert( mod && " mod param is NULL" ); assert( objname && " objname param is NULL" ); assert( retres && " retres param is NULL" ); /* 1) try an exact match */ obj = try_exact_match( mod, modname, objname, dataonly, retres ); if (obj) { return obj; } /* 2) try an case-insensitive exact-length match */ if ( match_names >= NCX_MATCH_EXACT_NOCASE ) { obj = try_case_insensitive_exact_length( mod, modname, objname, dataonly, retres ); if (obj) { return obj; } } else if (!alt_names) { /* NCX_MATCH_EXACT mode */ return NULL; } /* 3) try an case-sensitive partial-name match */ if (match_names >= NCX_MATCH_ONE) { obj = try_case_sensitive_partial_match( mod, modname, objname, (match_names < NCX_MATCH_FIRST), dataonly, retres); if (obj) { return obj; } multmatches = (*retres == ERR_NCX_MULTIPLE_MATCHES ); } else if (!alt_names) { /* NCX_MATCH_EXACT_NOCASE mode */ return NULL; } /* 4) try an case-insensitive partial-name match */ if (match_names == NCX_MATCH_ONE_NOCASE || match_names == NCX_MATCH_FIRST_NOCASE) { obj = try_case_insensitive_partial_match( mod, modname, objname, (match_names < NCX_MATCH_FIRST), dataonly, retres ); if (obj) { return obj; } multmatches = (*retres == ERR_NCX_MULTIPLE_MATCHES ); } else if (!alt_names) { /* NCX_MATCH_ONE mode or NCX_MATCH_FIRST mode */ if (multmatches) { *retres = ERR_NCX_MULTIPLE_MATCHES; } return NULL; } /* 5) try an exact match on alt-name */ obj = try_exact_match_alt_name( mod, modname, objname, dataonly, retres ); if (obj) { *retres = NO_ERR; // FIXME: Why is retres being ignored? return obj; } /* 6) try an case-insensitive exact-length match on alt-name */ if (match_names >= NCX_MATCH_EXACT_NOCASE) { obj = try_case_insensitive_exact_alt_name( mod, modname, objname, dataonly, retres ); if (obj) { return obj; } } else { /* NCX_MATCH_EXACT mode + alt_names */ return NULL; // note: multmatches cannot be ture on this path! } /* 7) try an case-sensitive partial-name match on alt-name */ if (match_names >= NCX_MATCH_ONE) { obj = try_case_sensitive_partial_alt_name( mod, modname, objname, (match_names < NCX_MATCH_FIRST), dataonly, retres ); if (obj) { return obj; } } else { /* NCX_MATCH_EXACT_NOCASE mode + alt_names */ return NULL; } /* 8) try an case-insensitive partial-name match on alt-name */ if (match_names == NCX_MATCH_ONE_NOCASE || match_names == NCX_MATCH_FIRST_NOCASE) { obj = try_case_insensitive_partial_alt_name( mod, modname, objname, (match_names < NCX_MATCH_FIRST), dataonly, retres ); if (obj) { return obj; } } if (multmatches) { *retres = ERR_NCX_MULTIPLE_MATCHES; } return NULL; } /* obj_find_template_top_ex */ /******************************************************************** * FUNCTION obj_find_template_all * * Check if an obj_template_t in the mod->datadefQ or any * of the include files used within the entire main module * * Top-level access is not tracked, so the 'test' variable * is hard-wired to FALSE * * INPUTS: * mod == ncx_module to check * modname == module name for the object (needed for augments) * (may be NULL to match any 'objname' instance) * objname == object name to find * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ obj_template_t * obj_find_template_all (ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname) { assert( mod && "modname is NULL" ); assert( objname && "objname is NULL" ); obj_template_t *obj; yang_node_t *node; dlq_hdr_t *que; /* check the main module */ uint32 matchcount=0; obj = find_template( &mod->datadefQ, modname, objname, FALSE, /* lookdeep */ FALSE, /* match */ TRUE, /* usecase */ FALSE, /* altnames */ FALSE, /* dataonly */ &matchcount ); if (obj) { return obj; } que = ncx_get_allincQ(mod); /* check all the submodules, but only the ones visible * to this module or submodule, YANG only */ for (node = (yang_node_t *)dlq_firstEntry(que); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { /* check the object Q in this submodule */ obj = find_template(&node->submod->datadefQ, modname, objname, FALSE, /* lookdeep */ FALSE, /* match */ TRUE, /* usecase */ FALSE, /* altnames */ FALSE, /* dataonly */ &matchcount ); if (obj) { return obj; } } } return NULL; } /* obj_find_template_all */ /******************************************************************** * Find a child object with the specified Qname * * !!! This function checks for accessible names only!!! * !!! That means child nodes of choice->case will be * !!! present instead of the choice name or case name * * \param obj the obj_template_t to check * \param modname the module name that defines the obj_template_t * if it is NULL and first match will be done, and the * module ignored (Name instead of QName) * \param objname the object name to find * \return pointer to obj_template_t or NULL if not found *********************************************************************/ obj_template_t * obj_find_child ( obj_template_t *obj, const xmlChar *modname, const xmlChar *objname) { assert( obj && "que is NULL" ); assert( objname && "objname is NULL" ); dlq_hdr_t *que; que = obj_get_datadefQ(obj); if (que != NULL) { uint32 matchcount=0; return find_template( que, modname, objname, TRUE, /* lookdeep */ FALSE, /* match */ TRUE, /* usecase */ FALSE, /* altnames */ FALSE, /* dataonly */ &matchcount ); } return NULL; } /* obj_find_child */ /******************************************************************** * FUNCTION obj_find_child_ex * * Find a child object with the specified Qname * extended match modes * * !!! This function checks for accessible names only!!! * !!! That means child nodes of choice->case will be * !!! present instead of the choice name or case name * * INPUTS: * obj == obj_template_t to check * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find * match_names == enum for selected match names mode * alt_names == TRUE if alt-name should be checked in addition * to the YANG node name * == FALSE to check YANG names only * dataonly == TRUE to check just data nodes * FALSE to check all nodes * retres == address of return status * * OUTPUTS: * if retres not NULL, *retres set to return status * * RETURNS: * pointer to obj_template_t or NULL if not found *********************************************************************/ obj_template_t * obj_find_child_ex (obj_template_t *obj, const xmlChar *modname, const xmlChar *objname, ncx_name_match_t match_names, boolean alt_names, boolean dataonly, status_t *retres) { assert( obj && "obj is NULL" ); assert( objname && "objname is NULL" ); dlq_hdr_t *que; uint32 matchcount; if (retres != NULL) { *retres = NO_ERR; } que = obj_get_datadefQ(obj); if ( !que ) { return NULL; } /* 1) try an exact match */ obj = find_template(que, modname, objname, TRUE, /* loopdeep */ FALSE, /* match */ TRUE, /* usecase */ FALSE, /* alt_names */ dataonly, &matchcount); if (obj != NULL) { return obj; } /* 2) try an case-insensitive exact-length match */ if (match_names >= NCX_MATCH_EXACT_NOCASE) { obj = find_template(que, modname, objname, TRUE, /* loopdeep */ FALSE, /* match */ FALSE, /* usecase */ FALSE, /* altnames */ dataonly, &matchcount); if (obj) { return obj; } } else if (!alt_names) { /* NCX_MATCH_EXACT mode */ if (retres != NULL) { *retres = ERR_NCX_DEF_NOT_FOUND; } return NULL; } /* 3) try an case-sensitive partial-name match */ if (match_names >= NCX_MATCH_ONE) { obj = find_template(que, modname, objname, TRUE, /* loopdeep */ TRUE, /* match */ TRUE, /* usecase */ FALSE, /* altnames */ dataonly, &matchcount); if (obj) { if (match_names <= NCX_MATCH_ONE_NOCASE && matchcount > 1) { if (retres != NULL) { *retres = ERR_NCX_MULTIPLE_MATCHES; } return NULL; } return obj; } } else if (!alt_names) { /* NCX_MATCH_EXACT_NOCASE mode */ if (retres != NULL) { *retres = ERR_NCX_DEF_NOT_FOUND; } return NULL; } /* 4) try an case-insensitive partial-name match */ if (match_names == NCX_MATCH_ONE_NOCASE || match_names == NCX_MATCH_FIRST_NOCASE) { obj = find_template(que, modname, objname, TRUE, /* loopdeep */ TRUE, /* match */ FALSE, /* usecase */ FALSE, /* altnames */ dataonly, &matchcount); if (obj) { if (match_names <= NCX_MATCH_ONE_NOCASE && matchcount > 1) { if (retres != NULL) { *retres = ERR_NCX_MULTIPLE_MATCHES; } return NULL; } return obj; } } else if (!alt_names) { /* NCX_MATCH_ONE mode or NCX_MATCH_FIRST mode */ if (retres != NULL) { *retres = ERR_NCX_DEF_NOT_FOUND; } return NULL; } if (!alt_names) { if (retres != NULL) { *retres = ERR_NCX_DEF_NOT_FOUND; } return NULL; } /* 5) try an exact match on alt-name */ obj = find_template(que, modname, objname, TRUE, /* loopdeep */ FALSE, /* match */ TRUE, /* usecase */ TRUE, /* altnames */ dataonly, &matchcount); if (obj) { return obj; } /* 6) try an case-insensitive exact-length match on alt-name */ if (match_names >= NCX_MATCH_EXACT_NOCASE) { obj = find_template(que, modname, objname, TRUE, /* loopdeep */ FALSE, /* match */ FALSE, /* usecase */ TRUE, /* altnames */ dataonly, &matchcount); if (obj) { if (match_names <= NCX_MATCH_ONE_NOCASE && matchcount > 1) { if (retres != NULL) { *retres = ERR_NCX_MULTIPLE_MATCHES; } return NULL; } return obj; } } else { /* NCX_MATCH_EXACT mode + alt_names */ if (retres != NULL) { *retres = ERR_NCX_DEF_NOT_FOUND; } return NULL; } /* 7) try an case-sensitive partial-name match on alt-name */ if (match_names >= NCX_MATCH_ONE) { obj = find_template(que, modname, objname, TRUE, /* loopdeep */ TRUE, /* match */ TRUE, /* usecase */ TRUE, /* altnames */ dataonly, &matchcount); if (obj) { if (match_names <= NCX_MATCH_ONE_NOCASE && matchcount > 1) { if (retres != NULL) { *retres = ERR_NCX_MULTIPLE_MATCHES; } return NULL; } return obj; } } else { /* NCX_MATCH_EXACT_NOCASE mode + alt_names */ if (retres != NULL) { *retres = ERR_NCX_DEF_NOT_FOUND; } return NULL; } /* 8) try an case-insensitive partial-name match on alt-name */ if (match_names == NCX_MATCH_ONE_NOCASE || match_names == NCX_MATCH_FIRST_NOCASE) { obj = find_template(que, modname, objname, TRUE, /* loopdeep */ TRUE, /* match */ FALSE, /* usecase */ TRUE, /* altnames */ dataonly, &matchcount); if (obj) { if (match_names <= NCX_MATCH_ONE_NOCASE && matchcount > 1) { return NULL; } return obj; } } else { /* NCX_MATCH_ONE mode or NCX_MATCH_FIRST mode */ if (retres != NULL) { *retres = ERR_NCX_DEF_NOT_FOUND; } return NULL; } if (retres != NULL) { *retres = ERR_NCX_DEF_NOT_FOUND; } return NULL; } /* obj_find_child_ex */ /******************************************************************** * FUNCTION obj_find_child_str * * Find a child object with the specified Qname * * INPUTS: * obj == obj_template_t to check * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find, not Z-terminated * objnamelen == length of objname string * * RETURNS: * pointer to obj_template_t or NULL if not found *********************************************************************/ obj_template_t * obj_find_child_str (obj_template_t *obj, const xmlChar *modname, const xmlChar *objname, uint32 objnamelen) { assert( obj && "obj is NULL" ); assert( objname && "objname is NULL" ); obj_template_t *template; dlq_hdr_t *que; xmlChar *buff; if (objnamelen > NCX_MAX_NLEN) { return NULL; } que = obj_get_datadefQ(obj); if (que) { buff = m__getMem(objnamelen+1); if (buff) { uint32 matchcount=0; xml_strncpy(buff, objname, objnamelen); template = find_template( que, modname, buff, TRUE, /* lookdeep */ FALSE, /* match */ TRUE, /* usecase */ FALSE, /* altnames */ FALSE, /* dataonly */ &matchcount ); m__free(buff); return template; } } return NULL; } /* obj_find_child_str */ /******************************************************************** * FUNCTION obj_match_child_str * * Match a child object with the specified Qname * Find first command that matches all N chars of objname * * !!! This function checks for accessible names only!!! * !!! That means child nodes of choice->case will be * !!! present instead of the choice name or case name * * INPUTS: * obj == obj_template_t to check * modname == module name that defines the obj_template_t * == NULL and first match will be done, and the * module ignored (Name instead of QName) * objname == object name to find, not Z-terminated * objnamelen == length of objname string * matchcount == address of return parameter match count * (may be NULL) * OUTPUTS: * if non-NULL: * *matchcount == number of parameters that matched * only the first match will be returned * * RETURNS: * pointer to obj_template_t or NULL if not found *********************************************************************/ obj_template_t * obj_match_child_str (obj_template_t *obj, const xmlChar *modname, const xmlChar *objname, uint32 objnamelen, uint32 *matchcount) { assert( obj && "obj is NULL" ); assert( objname && "objname is NULL" ); obj_template_t *template; dlq_hdr_t *que; xmlChar *buff; if (objnamelen > NCX_MAX_NLEN) { return NULL; } que = obj_get_datadefQ(obj); if (que) { buff = m__getMem(objnamelen+1); if (buff) { xml_strncpy(buff, objname, objnamelen); template = find_template(que, modname, buff, TRUE, /* lookdeep */ TRUE, /* match */ TRUE, /* usecase */ FALSE, /* altnames */ FALSE, /* dataonly */ matchcount); m__free(buff); return template; } } return NULL; } /* obj_match_child_str */ /******************************************************************** * FUNCTION obj_first_child * * Get the first child object if the specified object * has any children * * !!!! SKIPS OVER AUGMENT AND USES !!!! * * INPUTS: * obj == obj_template_t to check * RETURNS: * pointer to first child obj_template_t or * NULL if not found *********************************************************************/ obj_template_t * obj_first_child (obj_template_t *obj) { dlq_hdr_t *que; obj_template_t *chobj; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif que = obj_get_datadefQ(obj); if (que != NULL) { for (chobj = (obj_template_t *)dlq_firstEntry(que); chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { if (obj_has_name(chobj) && obj_is_enabled(chobj)) { return chobj; } } } return NULL; } /* obj_first_child */ /******************************************************************** * FUNCTION obj_last_child * * Get the last child object if the specified object * has any children * * !!!! SKIPS OVER AUGMENT AND USES !!!! * * INPUTS: * obj == obj_template_t to check * RETURNS: * pointer to first child obj_template_t or * NULL if not found *********************************************************************/ obj_template_t * obj_last_child (obj_template_t *obj) { dlq_hdr_t *que; obj_template_t *chobj; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif que = obj_get_datadefQ(obj); if (que) { for (chobj = (obj_template_t *)dlq_lastEntry(que); chobj != NULL; chobj = (obj_template_t *)dlq_prevEntry(chobj)) { if (obj_has_name(chobj) && obj_is_enabled(chobj)) { return chobj; } } } return NULL; } /* obj_last_child */ /******************************************************************** * FUNCTION obj_next_child * * Get the next child object if the specified object * has any children * * !!!! SKIPS OVER AUGMENT AND USES !!!! * * INPUTS: * obj == obj_template_t to check * RETURNS: * pointer to next child obj_template_t or * NULL if not found *********************************************************************/ obj_template_t * obj_next_child (obj_template_t *obj) { obj_template_t *next; boolean done; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif next = obj; done = FALSE; while (!done) { next = (obj_template_t *)dlq_nextEntry(next); if (!next) { done = TRUE; } else if (obj_has_name(next) && obj_is_enabled(next)) { return next; } } return NULL; } /* obj_next_child */ /******************************************************************** * FUNCTION obj_previous_child * * Get the previous child object if the specified object * has any children * * !!!! SKIPS OVER AUGMENT AND USES !!!! * * INPUTS: * obj == obj_template_t to check * RETURNS: * pointer to next child obj_template_t or * NULL if not found *********************************************************************/ obj_template_t * obj_previous_child (obj_template_t *obj) { obj_template_t *prev; boolean done; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif prev = obj; done = FALSE; while (!done) { prev = (obj_template_t *)dlq_prevEntry(prev); if (!prev) { done = TRUE; } else if (obj_has_name(prev) && obj_is_enabled(prev)) { return prev; } } return NULL; } /* obj_previous_child */ /******************************************************************** * FUNCTION obj_first_child_deep * * Get the first child object if the specified object * has any children. Look past choices and cases to * the real nodes within them * * !!!! SKIPS OVER AUGMENT AND USES AND CHOICES AND CASES !!!! * * INPUTS: * obj == obj_template_t to check * RETURNS: * pointer to first child obj_template_t or * NULL if not found *********************************************************************/ obj_template_t * obj_first_child_deep (obj_template_t *obj) { dlq_hdr_t *que; obj_template_t *chobj; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif /* go through the child nodes of this object looking * for the first data object; skip over all meta-objects */ que = obj_get_datadefQ(obj); if (que) { for (chobj = (obj_template_t *)dlq_firstEntry(que); chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { if (obj_has_name(chobj) && obj_is_enabled(chobj)) { if ( obj_is_choice_or_case( chobj ) ) { return (obj_first_child_deep(chobj)); } else { return chobj; } } } } return NULL; } /* obj_first_child_deep */ /******************************************************************** * FUNCTION obj_next_child_deep * * Get the next child object if the specified object * has any children. Look past choice and case nodes * to the real nodes within them. * * !!!! SKIPS OVER AUGMENT AND USES !!!! * * INPUTS: * obj == obj_template_t to check * * RETURNS: * pointer to next child obj_template_t or * NULL if not found *********************************************************************/ extern obj_template_t * obj_next_child_deep (obj_template_t *obj) { obj_template_t *next, *last, *child; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif /* start the loop at the current object to set the * 'last' object correctly */ last = obj; next = last; while (next) { last = next; /* try next sibling */ next = obj_next_child(next); if (next) { if(next->objtype == OBJ_TYP_CHOICE || next->objtype == OBJ_TYP_CASE) { child = obj_first_child_deep(next); if (child) { return child; } } else { return next; } } } /* was last sibling, try parent if this is a case */ if(!obj_is_choice_or_case(obj->parent)) { return NULL; } while(last->parent && obj_is_choice_or_case(last->parent)) { next = obj_next_child_deep(last->parent); if(next) { return next; } last = last->parent; } return NULL; } /* obj_next_child_deep */ /******************************************************************** * FUNCTION obj_find_all_children * * Find all occurances of the specified node(s) * within the children of the current node. * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * This function skips choice and case nodes and * only processes real data nodes * * INPUTS: * exprmod == module containing XPath expression * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == start node to check * modname == module name; * only matches in this module namespace * will be returned * == NULL: * namespace matching will be skipped * childname == name of child node to find * == NULL to match any child name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if childname == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * useroot == TRUE is it is safe to use the toproot * FALSE if not, use all moduleQ search instead * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ boolean obj_find_all_children (ncx_module_t *exprmod, obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *startnode, const xmlChar *modname, const xmlChar *childname, boolean configonly, boolean textmode, boolean useroot) { dlq_hdr_t *datadefQ; obj_template_t *obj; ncx_module_t *mod; boolean fnresult; #ifdef DEBUG if (!exprmod || !walkerfn || !startnode) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (exprmod && exprmod->parent) { /* look in the parent module, not a submodule */ exprmod = exprmod->parent; } if (obj_is_root(startnode) && !useroot) { for (obj = ncx_get_first_data_object(exprmod); obj != NULL; obj = ncx_get_next_data_object(exprmod, obj)) { fnresult = test_one_child(exprmod, walkerfn, cookie1, cookie2, obj, modname, childname, configonly, textmode); if (!fnresult) { return FALSE; } } for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { for (obj = ncx_get_first_data_object(mod); obj != NULL; obj = ncx_get_next_data_object(mod, obj)) { fnresult = test_one_child(exprmod, walkerfn, cookie1, cookie2, obj, modname, childname, configonly, textmode); if (!fnresult) { return FALSE; } } } for (mod = ncx_get_first_session_module(); mod != NULL; mod = ncx_get_next_session_module(mod)) { for (obj = ncx_get_first_data_object(mod); obj != NULL; obj = ncx_get_next_data_object(mod, obj)) { fnresult = test_one_child(exprmod, walkerfn, cookie1, cookie2, obj, modname, childname, configonly, textmode); if (!fnresult) { return FALSE; } } } } else { datadefQ = obj_get_datadefQ(startnode); if (!datadefQ) { return TRUE; } for (obj = (obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { fnresult = test_one_child(exprmod, walkerfn, cookie1, cookie2, obj, modname, childname, configonly, textmode); if (!fnresult) { return FALSE; } } } return TRUE; } /* obj_find_all_children */ /******************************************************************** * FUNCTION obj_find_all_ancestors * * Find all occurances of the specified node(s) * within the ancestors of the current node. * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * This function skips choice and case nodes and * only processes real data nodes * * INPUTS: * exprmod == module containing XPath object * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == start node to check * modname == module name; * only matches in this module namespace * will be returned * == NULL: * namespace matching will be skipped * name == name of ancestor node to find * == NULL to match any ancestor name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if name == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * useroot == TRUE is it is safe to use the toproot * FALSE if not, use all moduleQ search instead * orself == TRUE if axis is really ancestor-or-self * FALSE if axis is ancestor * fncalled == address of return function called flag * * OUTPUTS: * *fncalled set to TRUE if a callback function was called * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ boolean obj_find_all_ancestors (ncx_module_t *exprmod, obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode, boolean useroot, boolean orself, boolean *fncalled) { obj_template_t *obj; ncx_module_t *mod; boolean fnresult; #ifdef DEBUG if (!exprmod || !walkerfn || !startnode || !fncalled) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif *fncalled = FALSE; if (orself) { obj = startnode; } else { obj = startnode->parent; } if (exprmod && exprmod->parent) { /* look in the parent module, not a submodule */ exprmod = exprmod->parent; } if (obj && obj_is_root(obj) && !useroot) { for (obj = ncx_get_first_data_object(exprmod); obj != NULL; obj = ncx_get_next_data_object(exprmod, obj)) { fnresult = test_one_ancestor(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, orself, fncalled); if (!fnresult) { return FALSE; } } for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { for (obj = ncx_get_first_data_object(mod); obj != NULL; obj = ncx_get_next_data_object(mod, obj)) { fnresult = test_one_ancestor(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, orself, fncalled); if (!fnresult) { return FALSE; } } } for (mod = ncx_get_first_session_module(); mod != NULL; mod = ncx_get_next_session_module(mod)) { for (obj = ncx_get_first_data_object(mod); obj != NULL; obj = ncx_get_next_data_object(mod, obj)) { fnresult = test_one_ancestor(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, orself, fncalled); if (!fnresult) { return FALSE; } } } } else { while (obj) { if ( obj_is_choice_or_case( obj ) ) { fnresult = TRUE; } else { fnresult = process_one_walker_child(walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, fncalled); } if (!fnresult) { return FALSE; } obj = obj->parent; } } return TRUE; } /* obj_find_all_ancestors */ /******************************************************************** * FUNCTION obj_find_all_descendants * * Find all occurances of the specified node(s) * within the descendants of the current node. * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * This function skips choice and case nodes and * only processes real data nodes * * INPUTS: * exprmod == module containing XPath expression * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == start node to check * modname == module name; * only matches in this module namespace * will be returned * == NULL: * namespace matching will be skipped * name == name of descendant node to find * == NULL to match any descendant name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if name == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * useroot == TRUE is it is safe to use the toproot * FALSE if not, use all moduleQ search instead * orself == TRUE if axis is really ancestor-or-self * FALSE if axis is ancestor * fncalled == address of return function called flag * * OUTPUTS: * *fncalled set to TRUE if a callback function was called * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ boolean obj_find_all_descendants (ncx_module_t *exprmod, obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode, boolean useroot, boolean orself, boolean *fncalled) { obj_template_t *obj; ncx_module_t *mod; boolean fnresult; #ifdef DEBUG if (!exprmod || !walkerfn || !startnode || !fncalled) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif *fncalled = FALSE; if (exprmod && exprmod->parent) { /* look in the parent module, not a submodule */ exprmod = exprmod->parent; } if (obj_is_root(startnode) && !useroot) { for (obj = ncx_get_first_data_object(exprmod); obj != NULL; obj = ncx_get_next_data_object(exprmod, obj)) { fnresult = test_one_descendant(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, orself, fncalled); if (!fnresult) { return FALSE; } } for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { for (obj = ncx_get_first_data_object(mod); obj != NULL; obj = ncx_get_next_data_object(mod, obj)) { fnresult = test_one_descendant(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, orself, fncalled); if (!fnresult) { return FALSE; } } } for (mod = ncx_get_first_session_module(); mod != NULL; mod = ncx_get_next_session_module(mod)) { for (obj = ncx_get_first_data_object(mod); obj != NULL; obj = ncx_get_next_data_object(mod, obj)) { fnresult = test_one_descendant(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, textmode, orself, fncalled); if (!fnresult) { return FALSE; } } } } else { fnresult = test_one_descendant(exprmod, walkerfn, cookie1, cookie2, startnode, modname, name, configonly, textmode, orself, fncalled); if (!fnresult) { return FALSE; } } return TRUE; } /* obj_find_all_descendants */ /******************************************************************** * FUNCTION obj_find_all_pfaxis * * Find all occurances of the specified preceding * or following node(s). Could also be * within the descendants of the current node. * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * This function skips choice and case nodes and * only processes real data nodes * * INPUTS: * exprmod == module containing object * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == starting sibling node to check * modname == module name; * only matches in this module namespace * will be returned * == NULL: * namespace matching will be skipped * * name == name of preceding or following node to find * == NULL to match any name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if name == NULL * dblslash == TRUE if all decendents of the preceding * or following nodes should be checked * FALSE only 1 level is checked * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * axis == axis enum to use * fncalled == address of return function called flag * * OUTPUTS: * *fncalled set to TRUE if a callback function was called * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ boolean obj_find_all_pfaxis (ncx_module_t *exprmod, obj_walker_fn_t walkerfn, void *cookie1, void *cookie2, obj_template_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean dblslash, boolean textmode, boolean useroot, ncx_xpath_axis_t axis, boolean *fncalled) { obj_template_t *obj; ncx_module_t *mod; boolean fnresult, forward; #ifdef DEBUG if (!exprmod || !walkerfn || !startnode || !fncalled) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif *fncalled = FALSE; if (exprmod && exprmod->parent) { /* look in the parent module, not a submodule */ exprmod = exprmod->parent; } /* check the Q containing the startnode * for preceding or following nodes; * could be sibling node check or any node check */ switch (axis) { case XP_AX_PRECEDING: dblslash = TRUE; /* fall through */ case XP_AX_PRECEDING_SIBLING: /* execute the callback for all preceding nodes * that match the filter criteria */ forward = FALSE; break; case XP_AX_FOLLOWING: dblslash = TRUE; /* fall through */ case XP_AX_FOLLOWING_SIBLING: forward = TRUE; break; case XP_AX_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } if (obj_is_root(startnode) && !dblslash) { return TRUE; } if (obj_is_root(startnode) && !useroot) { for (obj = ncx_get_first_data_object(exprmod); obj != NULL; obj = ncx_get_next_data_object(exprmod, obj)) { fnresult = test_one_pfnode(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, dblslash, textmode, forward, axis, fncalled); if (!fnresult) { return FALSE; } } for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { for (obj = ncx_get_first_data_object(mod); obj != NULL; obj = ncx_get_next_data_object(mod, obj)) { fnresult = test_one_pfnode(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, dblslash, textmode, forward, axis, fncalled); if (!fnresult) { return FALSE; } } } for (mod = ncx_get_first_session_module(); mod != NULL; mod = ncx_get_next_session_module(mod)) { for (obj = ncx_get_first_data_object(mod); obj != NULL; obj = ncx_get_next_data_object(mod, obj)) { fnresult = test_one_pfnode(exprmod, walkerfn, cookie1, cookie2, obj, modname, name, configonly, dblslash, textmode, forward, axis, fncalled); if (!fnresult) { return FALSE; } } } } else { fnresult = test_one_pfnode(exprmod, walkerfn, cookie1, cookie2, startnode, modname, name, configonly, dblslash, textmode, forward, axis, fncalled); if (!fnresult) { return FALSE; } } return TRUE; } /* obj_find_all_pfaxis */ /******************************************************************** * FUNCTION obj_find_case * * Find a specified case arm by name * * INPUTS: * choic == choice struct to check * modname == name of the module that added this case (may be NULL) * casname == name of the case to find * * RETURNS: * pointer to obj_case_t for requested case, NULL if not found *********************************************************************/ obj_case_t * obj_find_case (obj_choice_t *choic, const xmlChar *modname, const xmlChar *casname) { obj_template_t *casobj; obj_case_t *cas; #ifdef DEBUG if (!choic || !casname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (casobj = (obj_template_t *)dlq_firstEntry(choic->caseQ); casobj != NULL; casobj = (obj_template_t *)dlq_nextEntry(casobj)) { cas = casobj->def.cas; if (modname && xml_strcmp(obj_get_mod_name(casobj), modname)) { continue; } if (!xml_strcmp(casname, cas->name)) { return cas; } } return NULL; } /* obj_find_case */ /******************************************************************** * FUNCTION obj_new_rpcio * * Malloc and initialize the fields in a an obj_rpcio_t * Fields are setup within the new obj_template_t, based * on the values in rpcobj * * INPUTS: * rpcobj == parent OBJ_TYP_RPC template * name == name string of the node (input or output) * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ obj_template_t * obj_new_rpcio (obj_template_t *rpcobj, const xmlChar *name) { obj_template_t *rpcio; #ifdef DEBUG if (!rpcobj || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif rpcio = obj_new_template(OBJ_TYP_RPCIO); if (!rpcio) { return NULL; } rpcio->def.rpcio->name = xml_strdup(name); if (!rpcio->def.rpcio->name) { obj_free_template(rpcio); return NULL; } ncx_set_error(&rpcio->tkerr, rpcobj->tkerr.mod, rpcobj->tkerr.linenum, rpcobj->tkerr.linepos); rpcio->parent = rpcobj; return rpcio; } /* obj_new_rpcio */ /******************************************************************** * Clean and free all the obj_template_t structs in the specified Q * * \param datadefQ Q of obj_template_t to clean *********************************************************************/ void obj_clean_datadefQ (dlq_hdr_t *que) { if (!que) { return; } while (!dlq_empty(que)) { obj_template_t *obj = (obj_template_t *)dlq_deque(que); obj_free_template(obj); } } /* obj_clean_datadefQ */ /******************************************************************** * FUNCTION obj_find_type * * Check if a typ_template_t in the obj typedefQ hierarchy * * INPUTS: * obj == obj_template using the typedef * typname == type name to find * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ typ_template_t * obj_find_type (obj_template_t *obj, const xmlChar *typname) { dlq_hdr_t *que; typ_template_t *typ; grp_template_t *testgrp; #ifdef DEBUG if (!obj || !typname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif /* if this is a direct child of a grouping, try the tryedefQ * in the grouping first */ if (obj->grp) { que = &obj->grp->typedefQ; typ = ncx_find_type_que(que, typname); if (typ) { return typ; } testgrp = obj->grp->parentgrp; while (testgrp) { typ = ncx_find_type_que(&testgrp->typedefQ, typname); if (typ) { return typ; } testgrp = testgrp->parentgrp; } } /* object not in directly in a group or nothing found * check if this object has a typedefQ */ que = NULL; switch (obj->objtype) { case OBJ_TYP_CONTAINER: que = obj->def.container->typedefQ; break; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: break; case OBJ_TYP_LIST: que = obj->def.list->typedefQ; break; case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: case OBJ_TYP_USES: case OBJ_TYP_REFINE: case OBJ_TYP_AUGMENT: break; case OBJ_TYP_RPC: que = &obj->def.rpc->typedefQ; break; case OBJ_TYP_RPCIO: que = &obj->def.rpcio->typedefQ; break; case OBJ_TYP_NOTIF: que = &obj->def.notif->typedefQ; break; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } if (que) { typ = ncx_find_type_que(que, typname); if (typ) { return typ; } } if (obj->parent && !obj_is_root(obj->parent)) { return obj_find_type(obj->parent, typname); } else { return NULL; } } /* obj_find_type */ /******************************************************************** * FUNCTION obj_first_typedef * * Get the first local typedef for this object, if any * * INPUTS: * obj == obj_template to use * * RETURNS: * pointer to first typ_template_t struct if present, NULL otherwise *********************************************************************/ typ_template_t * obj_first_typedef (obj_template_t *obj) { dlq_hdr_t *que; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif que = NULL; switch (obj->objtype) { case OBJ_TYP_CONTAINER: que = obj->def.container->typedefQ; break; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: break; case OBJ_TYP_LIST: que = obj->def.list->typedefQ; break; case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: case OBJ_TYP_USES: case OBJ_TYP_REFINE: case OBJ_TYP_AUGMENT: break; case OBJ_TYP_RPC: que = &obj->def.rpc->typedefQ; break; case OBJ_TYP_RPCIO: que = &obj->def.rpcio->typedefQ; break; case OBJ_TYP_NOTIF: que = &obj->def.notif->typedefQ; break; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } if (que) { return (typ_template_t *)dlq_firstEntry(que); } return NULL; } /* obj_first_typedef */ /******************************************************************** * FUNCTION obj_find_grouping * * Check if a grp_template_t in the obj groupingQ hierarchy * * INPUTS: * obj == obj_template using the grouping * grpname == grouping name to find * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ grp_template_t * obj_find_grouping (obj_template_t *obj, const xmlChar *grpname) { dlq_hdr_t *que; grp_template_t *grp, *testgrp; #ifdef DEBUG if (!obj || !grpname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif /* check direct nesting within a grouping chain */ if (obj->grp) { grp = ncx_find_grouping_que(&obj->grp->groupingQ, grpname); if (grp) { return grp; } testgrp = obj->grp->parentgrp; while (testgrp) { if (!xml_strcmp(testgrp->name, grpname)) { return testgrp; } else { grp = ncx_find_grouping_que(&testgrp->groupingQ, grpname); if (grp) { return grp; } } testgrp = testgrp->parentgrp; } } /* check the object has a groupingQ within the object chain */ que = NULL; switch (obj->objtype) { case OBJ_TYP_CONTAINER: que = obj->def.container->groupingQ; break; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: break; case OBJ_TYP_LIST: que = obj->def.list->groupingQ; break; case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: case OBJ_TYP_USES: case OBJ_TYP_REFINE: case OBJ_TYP_AUGMENT: break; case OBJ_TYP_RPC: que = &obj->def.rpc->groupingQ; break; case OBJ_TYP_RPCIO: que = &obj->def.rpcio->groupingQ; break; case OBJ_TYP_NOTIF: que = &obj->def.notif->groupingQ; break; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } if (que) { grp = ncx_find_grouping_que(que, grpname); if (grp) { return grp; } } if (obj->parent && !obj_is_root(obj->parent)) { return obj_find_grouping(obj->parent, grpname); } else { return NULL; } } /* obj_find_grouping */ /******************************************************************** * FUNCTION obj_first_grouping * * Get the first local grouping if any * * INPUTS: * obj == obj_template to use * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ grp_template_t * obj_first_grouping (obj_template_t *obj) { dlq_hdr_t *que; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif que = NULL; switch (obj->objtype) { case OBJ_TYP_CONTAINER: que = obj->def.container->groupingQ; break; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: break; case OBJ_TYP_LIST: que = obj->def.list->groupingQ; break; case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: case OBJ_TYP_USES: case OBJ_TYP_REFINE: case OBJ_TYP_AUGMENT: break; case OBJ_TYP_RPC: que = &obj->def.rpc->groupingQ; break; case OBJ_TYP_RPCIO: que = &obj->def.rpcio->groupingQ; break; case OBJ_TYP_NOTIF: que = &obj->def.notif->groupingQ; break; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); } if (que) { return (grp_template_t *)dlq_firstEntry(que); } return NULL; } /* obj_first_grouping */ /******************************************************************** * FUNCTION obj_set_named_type * * Resolve type test * Called during phase 2 of module parsing * * INPUTS: * tkc == token chain * mod == module in progress * typname == name field from typ->name (may be NULL) * typdef == typdef in progress * parent == obj_template containing this typedef * == NULL if this is the top-level, use mod->typeQ * grp == grp_template containing this typedef * == NULL if the typedef is not contained in a grouping * * RETURNS: * status *********************************************************************/ status_t obj_set_named_type (tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *typname, typ_def_t *typdef, obj_template_t *parent, grp_template_t *grp) { typ_template_t *testtyp; if (typdef->tclass == NCX_CL_NAMED && typdef->def.named.typ==NULL) { /* assumed to be a named type from this module * because any named type from another module * would get resolved OK, or fail due to syntax * or dependency loop */ if (typname && !xml_strcmp(typname, typdef->typenamestr)) { log_error("\nError: typedef '%s' cannot use type '%s'", typname, typname); tkc->curerr = &typdef->tkerr; return ERR_NCX_DEF_LOOP; } testtyp = NULL; /* find the type within the specified typedef Q */ if (typdef->typenamestr) { if (grp) { testtyp = find_type_in_grpchain(grp, typdef->typenamestr); } if (!testtyp && parent) { testtyp = obj_find_type(parent, typdef->typenamestr); } if (!testtyp) { testtyp = ncx_find_type(mod, typdef->typenamestr, FALSE); } } if (!testtyp) { log_error("\nError: type '%s' not found", typdef->typenamestr); tkc->curerr = &typdef->tkerr; return ERR_NCX_UNKNOWN_TYPE; } else { typdef->def.named.typ = testtyp; typdef->linenum = testtyp->tkerr.linenum; testtyp->used = TRUE; if (testtyp->typdef.tclass == NCX_CL_NAMED && testtyp->typdef.def.named.typ==NULL) { obj_set_named_type (tkc, mod, typname, &testtyp->typdef, parent, grp); } } } return NO_ERR; } /* obj_set_named_type */ /******************************************************************** * FUNCTION obj_clone_template * * Clone an obj_template_t * Copy the pointers from the srcobj into the new obj * * If the mobj is non-NULL, then the non-NULL revisable * fields in the mobj struct will be merged into the new object * * INPUTS: * mod == module struct that is defining the new cloned data * this may be different than the module that will * contain the cloned data (except top-level objects) * srcobj == obj_template to clone * !!! This struct MUST NOT be deleted!!! * !!! Unless all of its clones are also deleted !!! * mobjQ == merge object Q (may be NULL) * datadefQ to check for OBJ_TYP_REFINE nodes * If the target of the refine node matches the * srcobj (e.g., from same grouping), then the * sub-clauses in that refinement-stmt that * are allowed to be revised will be checked * * RETURNS: * pointer to malloced clone obj_template_t * NULL if malloc failer error or internal error *********************************************************************/ obj_template_t * obj_clone_template (ncx_module_t *mod, obj_template_t *srcobj, dlq_hdr_t *mobjQ) { obj_template_t *newobj, *mobj, *testobj; status_t res; #ifdef DEBUG if (!srcobj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } /* RPCs and Actions share the same object type. Need to clone * actions when found within a grouping. */ if (srcobj->objtype == OBJ_TYP_NONE) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif #ifdef OBJ_CLONE_DEBUG if (LOGDEBUG4) { log_debug4("\nobj_clone: '%s' in mod '%s' on line %u", obj_get_name(srcobj), obj_get_mod_name(srcobj), srcobj->tkerr.linenum); } #endif newobj = new_blank_template(); if (!newobj) { return NULL; } /* set most of the common fields but leave some blank * since the uses or augment calling this fn is going to * re-parent the cloned node under a different part of the tree * maintain struct definition order! */ mobj = NULL; newobj->objtype = srcobj->objtype; newobj->flags = (srcobj->flags | OBJ_FL_CLONE); if (mobjQ) { /* this code assumes all the refine-stmts have been normalized * and there is only one refine-stmt per object at this point */ for (testobj = (obj_template_t *)dlq_firstEntry(mobjQ); testobj != NULL && mobj == NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { if (testobj->objtype != OBJ_TYP_REFINE) { continue; } if (testobj->def.refine->targobj == srcobj) { mobj = testobj; } } } if (mobj) { newobj->flags |= mobj->flags; /* check if special flags need to be cleared */ if ((mobj->flags & OBJ_FL_MANDSET) && !(mobj->flags & OBJ_FL_MANDATORY)) { newobj->flags &= ~OBJ_FL_MANDATORY; } if ((mobj->flags & OBJ_FL_CONFSET) && !(mobj->flags & OBJ_FL_CONFIG)) { newobj->flags &= ~OBJ_FL_CONFIG; } } ncx_set_error(&newobj->tkerr, srcobj->tkerr.mod, srcobj->tkerr.linenum, srcobj->tkerr.linepos); //newobj->grp not set //newobj->parent not set //newobj->usesobj not set //newobj->augobj not set if (srcobj->when) { newobj->when = xpath_clone_pcb(srcobj->when); if (newobj->when == NULL) { obj_free_template(newobj); return NULL; } } //newobj->metadataQ not set res = clone_appinfoQ(&newobj->appinfoQ, &srcobj->appinfoQ, (mobj) ? &mobj->appinfoQ : NULL); if (res != NO_ERR) { obj_free_template(newobj); return NULL; } res = clone_iffeatureQ(&newobj->iffeatureQ, &srcobj->iffeatureQ, (mobj) ? &mobj->iffeatureQ : NULL); if (res != NO_ERR) { obj_free_template(newobj); return NULL; } newobj->mod = mod; newobj->nsid = mod->nsid; /* do not set the group in a clone */ /* newobj->grp = srcobj->grp; */ /* set the specific object definition type */ switch (srcobj->objtype) { case OBJ_TYP_CONTAINER: newobj->def.container = clone_container(mod, newobj, srcobj->def.container, (mobj) ? mobj->def.refine : NULL, mobjQ); if (!newobj->def.container) { res = ERR_INTERNAL_MEM; } break; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: newobj->def.leaf = clone_leaf(srcobj->def.leaf, (mobj) ? mobj->def.refine : NULL); if (!newobj->def.leaf) { res = ERR_INTERNAL_MEM; } break; case OBJ_TYP_LEAF_LIST: newobj->def.leaflist = clone_leaflist(srcobj->def.leaflist, (mobj) ? mobj->def.refine : NULL); if (!newobj->def.leaflist) { res = ERR_INTERNAL_MEM; } break; case OBJ_TYP_LIST: newobj->def.list = clone_list(mod, newobj, srcobj, (mobj) ? mobj->def.refine : NULL, mobjQ); if (!newobj->def.list) { res = ERR_INTERNAL_MEM; } break; case OBJ_TYP_CHOICE: newobj->def.choic = clone_choice(mod, srcobj->def.choic, (mobj) ? mobj->def.refine : NULL, newobj, mobjQ); if (!newobj->def.choic) { res = ERR_INTERNAL_MEM; } break; case OBJ_TYP_CASE: newobj->def.cas = clone_case(mod, srcobj->def.cas, (mobj) ? mobj->def.refine : NULL, newobj, mobjQ); if (!newobj->def.cas) { res = ERR_INTERNAL_MEM; } break; case OBJ_TYP_USES: if (mobj) { res = SET_ERROR(ERR_INTERNAL_VAL); } else { /* set back pointer to uses! do not clone! */ newobj->def.uses = srcobj->def.uses; newobj->flags |= OBJ_FL_DEFCLONE; } break; case OBJ_TYP_NOTIF: newobj->def.notif = clone_notif(mod, srcobj->def.notif, (mobj) ? mobj->def.refine : NULL); if (!newobj->def.notif) { res = ERR_INTERNAL_MEM; } break; case OBJ_TYP_AUGMENT: if (mobj) { res = SET_ERROR(ERR_INTERNAL_VAL); } else { /* set back pointer to augment! do not clone! */ newobj->def.augment = srcobj->def.augment; newobj->flags |= OBJ_FL_DEFCLONE; } break; case OBJ_TYP_RPC: /* action */ newobj->def.rpc = clone_rpc(mod, srcobj->def.rpc, (mobj) ? mobj->def.refine : NULL); if (!newobj->def.rpc) { res = ERR_INTERNAL_MEM; } break; case OBJ_TYP_RPCIO: /* action */ newobj->def.rpcio = clone_rpcio(mod, srcobj->def.rpcio, (mobj) ? mobj->def.refine : NULL); if (!newobj->def.rpcio) { res = ERR_INTERNAL_MEM; } break; case OBJ_TYP_NONE: default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { obj_free_template(newobj); return NULL; } else { return newobj; } } /* obj_clone_template */ /******************************************************************** * FUNCTION obj_clone_template_case * * Clone an obj_template_t but make sure it is wrapped * in a OBJ_TYP_CASE layer * * Copy the pointers from the srcobj into the new obj * * Create an OBJ_TYP_CASE wrapper if needed, * for a short-case-stmt data def * * If the mobj is non-NULL, then the non-NULL revisable * fields in the mobj struct will be merged into the new object * * INPUTS: * mod == module struct that is defining the new cloned data * this may be different than the module that will * contain the cloned data (except top-level objects) * srcobj == obj_template to clone * !!! This struct MUST NOT be deleted!!! * !!! Unless all of its clones are also deleted !!! * mobjQ == Q of obj_refine_t objects to merge (may be NULL) * only fields allowed to be revised will be checked * even if other fields are set in this struct * * RETURNS: * pointer to malloced clone obj_template_t * NULL if malloc failer error or internal error *********************************************************************/ obj_template_t * obj_clone_template_case (ncx_module_t *mod, obj_template_t *srcobj, dlq_hdr_t *mobjQ) { obj_template_t *casobj, *newobj; #ifdef DEBUG if (!srcobj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (srcobj->objtype == OBJ_TYP_NONE || srcobj->objtype > OBJ_TYP_AUGMENT) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif if (srcobj->objtype == OBJ_TYP_CASE) { return obj_clone_template(mod, srcobj, mobjQ); } casobj = new_blank_template(); if (!casobj) { return NULL; } /* set most of the common fields but leave the mod and parent NULL * since the uses or augment calling this fn is going to * re-prent the cloned node under a different part of the tree */ casobj->objtype = OBJ_TYP_CASE; ncx_set_error(&casobj->tkerr, srcobj->tkerr.mod, srcobj->tkerr.linenum, srcobj->tkerr.linepos); casobj->flags = OBJ_FL_CLONE; casobj->def.cas = new_case(TRUE); if (!casobj->def.cas) { obj_free_template(casobj); return NULL; } casobj->def.cas->name = xml_strdup(obj_get_name(srcobj)); if (!casobj->def.cas->name) { obj_free_template(casobj); return NULL; } casobj->def.cas->status = obj_get_status(srcobj); newobj = obj_clone_template(mod, srcobj, mobjQ); if (!newobj) { obj_free_template(casobj); return NULL; } newobj->parent = casobj; dlq_enque(newobj, casobj->def.cas->datadefQ); return casobj; } /* obj_clone_template_case */ /******************************************************************** * FUNCTION obj_new_unique * * Alloc and Init a obj_unique_t struct * * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ obj_unique_t * obj_new_unique (void) { obj_unique_t *un; un = m__getObj(obj_unique_t); if (!un) { return NULL; } obj_init_unique(un); return un; } /* obj_new_unique */ /******************************************************************** * FUNCTION obj_init_unique * * Init a obj_unique_t struct * * INPUTS: * un == obj_unique_t struct to init *********************************************************************/ void obj_init_unique (obj_unique_t *un) { #ifdef DEBUG if (!un) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif memset(un, 0, sizeof(obj_unique_t)); dlq_createSQue(&un->compQ); } /* obj_init_unique */ /******************************************************************** * FUNCTION obj_free_unique * * Free a obj_unique_t struct * * INPUTS: * un == obj_unique_t struct to free *********************************************************************/ void obj_free_unique (obj_unique_t *un) { #ifdef DEBUG if (!un) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif obj_clean_unique(un); m__free(un); } /* obj_free_unique */ /******************************************************************** * FUNCTION obj_clean_unique * * Clean a obj_unique_t struct * * INPUTS: * un == obj_unique_t struct to clean *********************************************************************/ void obj_clean_unique (obj_unique_t *un) { obj_unique_comp_t *unc; #ifdef DEBUG if (!un) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (un->xpath) { m__free(un->xpath); un->xpath = NULL; } while (!dlq_empty(&un->compQ)) { unc = (obj_unique_comp_t *)dlq_deque(&un->compQ); obj_free_unique_comp(unc); } } /* obj_clean_unique */ /******************************************************************** * FUNCTION obj_new_unique_comp * * Alloc and Init a obj_unique_comp_t struct * * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ obj_unique_comp_t * obj_new_unique_comp (void) { obj_unique_comp_t *unc; unc = m__getObj(obj_unique_comp_t); if (!unc) { return NULL; } memset(unc, 0x0, sizeof(obj_unique_comp_t)); return unc; } /* obj_new_unique_comp */ /******************************************************************** * FUNCTION obj_free_unique_comp * * Free a obj_unique_comp_t struct * * INPUTS: * unc == obj_unique_comp_t struct to free *********************************************************************/ void obj_free_unique_comp (obj_unique_comp_t *unc) { #ifdef DEBUG if (!unc) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (unc->xpath) { m__free(unc->xpath); } m__free(unc); } /* obj_free_unique_comp */ /******************************************************************** * FUNCTION obj_find_unique * * Find a specific unique-stmt * * INPUTS: * que == queue of obj_unique_t to check * xpath == relative path expression for the * unique node to find * * RETURNS: * pointer to found entry or NULL if not found *********************************************************************/ obj_unique_t * obj_find_unique (dlq_hdr_t *que, const xmlChar *xpath) { obj_unique_t *un; #ifdef DEBUG if (!que || !xpath) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (un = (obj_unique_t *)dlq_firstEntry(que); un != NULL; un = (obj_unique_t *)dlq_nextEntry(un)) { if (!xml_strcmp(un->xpath, xpath)) { return un; } } return NULL; } /* obj_find_unique */ /******************************************************************** * FUNCTION obj_first_unique * * Get the first unique-stmt for a list * * INPUTS: * listobj == (list) object to check for unique structs * * RETURNS: * pointer to found entry or NULL if not found *********************************************************************/ obj_unique_t * obj_first_unique (obj_template_t *listobj) { #ifdef DEBUG if (!listobj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (listobj->objtype != OBJ_TYP_LIST) { return NULL; } return (obj_unique_t *) dlq_firstEntry(&listobj->def.list->uniqueQ); } /* obj_first_unique */ /******************************************************************** * FUNCTION obj_next_unique * * Get the next unique-stmt for a list * * INPUTS: * un == current unique node * * RETURNS: * pointer to found entry or NULL if not found *********************************************************************/ obj_unique_t * obj_next_unique (obj_unique_t *un) { #ifdef DEBUG if (!un) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (obj_unique_t *)dlq_nextEntry(un); } /* obj_next_unique */ /******************************************************************** * FUNCTION obj_first_unique_comp * * Get the first identifier in a unique-stmt for a list * * INPUTS: * un == unique struct to check * * RETURNS: * pointer to found entry or NULL if not found *********************************************************************/ obj_unique_comp_t * obj_first_unique_comp (obj_unique_t *un) { #ifdef DEBUG if (!un) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (obj_unique_comp_t *)dlq_firstEntry(&un->compQ); } /* obj_first_unique_comp */ /******************************************************************** * FUNCTION obj_next_unique_comp * * Get the next unique-stmt component for a list * * INPUTS: * uncomp == current unique component node * * RETURNS: * pointer to next entry or NULL if none *********************************************************************/ obj_unique_comp_t * obj_next_unique_comp (obj_unique_comp_t *uncomp) { #ifdef DEBUG if (!uncomp) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (obj_unique_comp_t *)dlq_nextEntry(uncomp); } /* obj_next_unique_comp */ /******************************************************************** * FUNCTION obj_new_key * * Alloc and Init a obj_key_t struct * * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ obj_key_t * obj_new_key (void) { obj_key_t *key; key = m__getObj(obj_key_t); if (!key) { return NULL; } memset(key, 0x0, sizeof(obj_key_t)); return key; } /* obj_new_key */ /******************************************************************** * FUNCTION obj_free_key * * Free a obj_key_t struct * * INPUTS: * key == obj_key_t struct to free *********************************************************************/ void obj_free_key (obj_key_t *key) { #ifdef DEBUG if (!key) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif m__free(key); } /* obj_free_key */ /******************************************************************** * FUNCTION obj_find_key * * Find a specific key component by key leaf identifier name * Assumes deep keys are not supported!!! * * INPUTS: * que == Q of obj_key_t to check * keycompname == key component name to find * * RETURNS: * pointer to found key component or NULL if not found *********************************************************************/ obj_key_t * obj_find_key (dlq_hdr_t *que, const xmlChar *keycompname) { obj_key_t *key; #ifdef DEBUG if (!que || !keycompname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (key = (obj_key_t *)dlq_firstEntry(que); key != NULL; key = (obj_key_t *)dlq_nextEntry(key)) { if (!xml_strcmp(obj_get_name(key->keyobj), keycompname)) { return key; } } return NULL; } /* obj_find_key */ /******************************************************************** * FUNCTION obj_find_key2 * * Find a specific key component, check for a specific node * in case deep keys are supported, and to check for duplicates * * INPUTS: * que == Q of obj_key_t to check * keyobj == key component object to find * * RETURNS: * pointer to found key component or NULL if not found *********************************************************************/ obj_key_t * obj_find_key2 (dlq_hdr_t *que, obj_template_t *keyobj) { obj_key_t *key; #ifdef DEBUG if (!que || !keyobj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (key = (obj_key_t *)dlq_firstEntry(que); key != NULL; key = (obj_key_t *)dlq_nextEntry(key)) { if (keyobj == key->keyobj) { return key; } } return NULL; } /* obj_find_key2 */ /******************************************************************** * FUNCTION obj_first_key * * Get the first key record * * INPUTS: * obj == object to check * * RETURNS: * pointer to first key component or NULL if not found *********************************************************************/ obj_key_t * obj_first_key (obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (obj->objtype != OBJ_TYP_LIST) { return NULL; } return (obj_key_t *)dlq_firstEntry(&obj->def.list->keyQ); } /* obj_first_key */ /******************************************************************** * FUNCTION obj_first_ckey * * Get the first key record: Const version * * INPUTS: * obj == object to check * * RETURNS: * pointer to first key component or NULL if not found *********************************************************************/ const obj_key_t * obj_first_ckey (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (obj->objtype != OBJ_TYP_LIST) { return NULL; } return (const obj_key_t *)dlq_firstEntry(&obj->def.list->keyQ); } /* obj_first_ckey */ /******************************************************************** * FUNCTION obj_next_key * * Get the next key record * * INPUTS: * objkey == current key record * * RETURNS: * pointer to next key component or NULL if not found *********************************************************************/ obj_key_t * obj_next_key (obj_key_t *objkey) { #ifdef DEBUG if (!objkey) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (obj_key_t *)dlq_nextEntry(objkey); } /* obj_next_key */ /******************************************************************** * FUNCTION obj_next_ckey * * Get the next key record: Const version * * INPUTS: * objkey == current key record * * RETURNS: * pointer to next key component or NULL if not found *********************************************************************/ const obj_key_t * obj_next_ckey (const obj_key_t *objkey) { #ifdef DEBUG if (!objkey) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (const obj_key_t *)dlq_nextEntry(objkey); } /* obj_next_ckey */ /******************************************************************** * FUNCTION obj_key_count * * Get the number of keys for this object * * INPUTS: * obj == object to check * * RETURNS: * number of keys in the obj_key_t Q *********************************************************************/ uint32 obj_key_count (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif if (obj->objtype != OBJ_TYP_LIST) { return 0; } return dlq_count(&obj->def.list->keyQ); } /* obj_key_count */ /******************************************************************** * FUNCTION obj_key_count_to_root * * Check ancestor-or-self nodes until root reached * Find all lists; Count the number of keys * * INPUTS: * obj == object to start check from * RETURNS: * number of keys in ancestor-or-self nodes *********************************************************************/ uint32 obj_key_count_to_root (obj_template_t *obj) { uint32 count = 0; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif if (obj_is_root(obj)) { return 0; } obj_traverse_keys(obj, (void *)&count, NULL, count_keys); return count; } /* obj_key_count_to_root */ /******************************************************************** * FUNCTION obj_traverse_keys * * Check ancestor-or-self nodes until root reached * Find all lists; For each list, starting with the * closest to root, invoke the callback function * for each of the key objects in order * * INPUTS: * obj == object to start check from * cookie1 == cookie1 to pass to the callback function * cookie2 == cookie2 to pass to the callback function * walkerfn == walker callback function * returns FALSE to terminate traversal * *********************************************************************/ void obj_traverse_keys (obj_template_t *obj, void *cookie1, void *cookie2, obj_walker_fn_t walkerfn) { obj_key_t *objkey; #ifdef DEBUG if (!obj || !walkerfn) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (obj_is_root(obj)) { return; } if (obj->parent != NULL) { obj_traverse_keys(obj->parent, cookie1, cookie2, walkerfn); } if (obj->objtype != OBJ_TYP_LIST) { return; } for (objkey = obj_first_key(obj); objkey != NULL; objkey = obj_next_key(objkey)) { if (objkey->keyobj) { boolean ret = (*walkerfn)(objkey->keyobj, cookie1, cookie2); if (!ret) { return; } } // else some error; skip this key!!! } } /* obj_traverse_keys */ /******************************************************************** * FUNCTION obj_any_rpcs * * Check if there are any RPC methods in the datadefQ * * INPUTS: * que == Q of obj_template_t to check * * RETURNS: * TRUE if any OBJ_TYP_RPC found, FALSE if not *********************************************************************/ boolean obj_any_rpcs (const dlq_hdr_t *datadefQ) { const obj_template_t *obj; #ifdef DEBUG if (!datadefQ) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif for (obj = (const obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (const obj_template_t *)dlq_nextEntry(obj)) { if (obj->objtype == OBJ_TYP_RPC) { return TRUE; } } return FALSE; } /* obj_any_rpcs */ /******************************************************************** * FUNCTION obj_any_notifs * * Check if there are any notifications in the datadefQ * * INPUTS: * que == Q of obj_template_t to check * * RETURNS: * TRUE if any OBJ_TYP_NOTIF found, FALSE if not *********************************************************************/ boolean obj_any_notifs (const dlq_hdr_t *datadefQ) { const obj_template_t *obj; #ifdef DEBUG if (!datadefQ) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif for (obj = (const obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (const obj_template_t *)dlq_nextEntry(obj)) { if (obj->objtype == OBJ_TYP_NOTIF) { return TRUE; } } return FALSE; } /* obj_any_notifs */ /******************************************************************** * FUNCTION obj_new_deviate * * Malloc and initialize the fields in a an object deviate statement * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ obj_deviate_t * obj_new_deviate (void) { obj_deviate_t *deviate; deviate = m__getObj(obj_deviate_t); if (!deviate) { return NULL; } memset(deviate, 0x0, sizeof(obj_deviate_t)); dlq_createSQue(&deviate->mustQ); dlq_createSQue(&deviate->uniqueQ); dlq_createSQue(&deviate->appinfoQ); return deviate; } /* obj_new_deviate */ /******************************************************************** * Clean and free an object deviate statement * \param deviate the pointer to the struct to clean and free *********************************************************************/ void obj_free_deviate (obj_deviate_t *deviate) { if (!deviate) { return; } typ_free_typdef(deviate->typdef); m__free(deviate->units); m__free(deviate->defval); clean_mustQ(&deviate->mustQ); free_uniqueQ( &deviate->uniqueQ ); ncx_clean_appinfoQ(&deviate->appinfoQ); m__free(deviate); } /* obj_free_deviate */ /******************************************************************** * FUNCTION obj_get_deviate_arg * * Get the deviate-arg string from its enumeration * * INPUTS: * devarg == enumeration to convert * RETURNS: * const string version of the enum *********************************************************************/ const xmlChar * obj_get_deviate_arg (obj_deviate_arg_t devarg) { switch (devarg) { case OBJ_DARG_NONE: return NCX_EL_NONE; case OBJ_DARG_ADD: return YANG_K_ADD; case OBJ_DARG_DELETE: return YANG_K_DELETE; case OBJ_DARG_REPLACE: return YANG_K_REPLACE; case OBJ_DARG_NOT_SUPPORTED: return YANG_K_NOT_SUPPORTED; default: SET_ERROR(ERR_INTERNAL_VAL); return (const xmlChar *)"--"; } } /* obj_get_deviate_arg */ /******************************************************************** * FUNCTION obj_new_deviation * * Malloc and initialize the fields in a an object deviation statement * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ obj_deviation_t * obj_new_deviation (void) { obj_deviation_t *deviation; deviation = m__getObj(obj_deviation_t); if (!deviation) { return NULL; } memset(deviation, 0x0, sizeof(obj_deviation_t)); dlq_createSQue(&deviation->deviateQ); dlq_createSQue(&deviation->appinfoQ); return deviation; } /* obj_new_deviation */ /******************************************************************** * Clean and free an object deviation statement * \param deviation the pointer to the struct to clean and free *********************************************************************/ void obj_free_deviation (obj_deviation_t *deviation) { if (!deviation) { return; } m__free(deviation->target); m__free(deviation->targmodname); m__free(deviation->descr); m__free(deviation->ref); m__free(deviation->devmodname); while (!dlq_empty(&deviation->deviateQ)) { obj_deviate_t *deviate = (obj_deviate_t *) dlq_deque(&deviation->deviateQ); obj_free_deviate(deviate); } ncx_clean_appinfoQ(&deviation->appinfoQ); m__free(deviation); } /* obj_free_deviation */ /******************************************************************** * Clean and free an Q of object deviation statements * * \param deviationQ the pointer to Q of the structs to clean and free *********************************************************************/ void obj_clean_deviationQ (dlq_hdr_t *deviationQ) { if (!deviationQ) { return; } while (!dlq_empty(deviationQ)) { obj_deviation_t *deviation = (obj_deviation_t *)dlq_deque(deviationQ); obj_free_deviation(deviation); } } /* obj_clean_deviationQ */ /******************************************************************** * Malloc and Generate the object ID for an object node * * \param obj the node to generate the instance ID for * \param buff the pointer to address of buffer to use * \return status *********************************************************************/ status_t obj_gen_object_id (const obj_template_t *obj, xmlChar **buff) { uint32 len; status_t res; #ifdef DEBUG if (!obj || !buff) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif *buff = NULL; /* figure out the length of the object ID */ res = get_object_string(obj, NULL, NULL, 0, TRUE, NULL, &len, FALSE, FALSE); if (res != NO_ERR) { return res; } /* get a buffer to fit the instance ID string */ *buff = (xmlChar *)m__getMem(len+1); if (!*buff) { return ERR_INTERNAL_MEM; } /* get the object ID for real this time */ res = get_object_string(obj, NULL, *buff, len+1, TRUE, NULL, &len, FALSE, FALSE); if (res != NO_ERR) { m__free(*buff); *buff = NULL; return SET_ERROR(res); } return NO_ERR; } /* obj_gen_object_id */ /******************************************************************** * Malloc and Generate the object ID for an object node * Remove all conceptual OBJ_TYP_CHOICE and OBJ_TYP_CASE nodes * so the resulting string will represent the structure of the * value tree for XPath searching * * \param obj the node to generate the instance ID for * \param buff the pointer to address of buffer to use * \return status *********************************************************************/ status_t obj_gen_object_id_xpath (const obj_template_t *obj, xmlChar **buff) { uint32 len; status_t res; #ifdef DEBUG if (!obj || !buff) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif *buff = NULL; /* figure out the length of the object ID */ res = get_object_string(obj, NULL, NULL, 0, TRUE, NULL, &len, FALSE, TRUE); if (res != NO_ERR) { return res; } /* get a buffer to fit the instance ID string */ *buff = (xmlChar *)m__getMem(len+1); if (!*buff) { return ERR_INTERNAL_MEM; } /* get the object ID for real this time */ res = get_object_string(obj, NULL, *buff, len+1, TRUE, NULL, &len, FALSE, TRUE); if (res != NO_ERR) { m__free(*buff); *buff = NULL; return SET_ERROR(res); } return NO_ERR; } /* obj_gen_object_id_xpath */ /******************************************************************** * Malloc and Generate the object ID for a unique-stmt test * * \param obj the node to generate the instance ID for * \param stopobj the ancestor node to stop at * \param buff the pointer to address of buffer to use * \return status *********************************************************************/ status_t obj_gen_object_id_unique (const obj_template_t *obj, const obj_template_t *stopobj, xmlChar **buff) { uint32 len; status_t res; assert( obj && "obj is NULL!" ); assert( stopobj && "stopobj is NULL!" ); assert( buff && "buff is NULL!" ); *buff = NULL; /* figure out the length of the object ID */ res = get_object_string(obj, stopobj, NULL, 0, TRUE, NULL, &len, FALSE, TRUE); if (res != NO_ERR) { return res; } /* get a buffer to fit the instance ID string */ *buff = (xmlChar *)m__getMem(len+1); if (!*buff) { return ERR_INTERNAL_MEM; } /* get the object ID for real this time */ res = get_object_string(obj, stopobj, *buff, len+1, TRUE, NULL, &len, FALSE, TRUE); if (res != NO_ERR) { m__free(*buff); *buff = NULL; return SET_ERROR(res); } return NO_ERR; } /* obj_gen_object_id_unique */ /******************************************************************** * FUNCTION obj_gen_object_id_code * * Malloc and Generate the object ID for an object node * for C code usage * generate a unique name for C code; handles augments * * INPUTS: * mod == current module in progress * obj == node to generate the instance ID for * buff == pointer to address of buffer to use * * OUTPUTS * *buff == malloced buffer with the instance ID * * RETURNS: * status *********************************************************************/ status_t obj_gen_object_id_code (ncx_module_t *mod, const obj_template_t *obj, xmlChar **buff) { uint32 len; status_t res; #ifdef DEBUG if (!mod || !obj || !buff) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif *buff = NULL; /* figure out the length of the object ID */ res = get_object_string(obj, NULL, NULL, 0, TRUE, mod, &len, FALSE, FALSE); if (res != NO_ERR) { return res; } /* get a buffer to fit the instance ID string */ *buff = (xmlChar *)m__getMem(len+1); if (!*buff) { return ERR_INTERNAL_MEM; } /* get the object ID for real this time */ res = get_object_string(obj, NULL, *buff, len+1, TRUE, mod, &len, FALSE, FALSE); if (res != NO_ERR) { m__free(*buff); *buff = NULL; return SET_ERROR(res); } return NO_ERR; } /* obj_gen_object_id_code */ /******************************************************************** * FUNCTION obj_copy_object_id * * Generate the object ID for an object node and copy to the buffer * copy an object ID to a buffer * * INPUTS: * obj == node to generate the instance ID for * buff == buffer to use * bufflen == size of buff * reallen == address of return length of actual identifier * (may be NULL) * * OUTPUTS * buff == filled in with the object ID * if reallen not NULL: * *reallen == length of identifier, even if error occurred * * RETURNS: * status *********************************************************************/ status_t obj_copy_object_id (const obj_template_t *obj, xmlChar *buff, uint32 bufflen, uint32 *reallen) { #ifdef DEBUG if (!obj || !buff) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return get_object_string(obj, NULL, buff, bufflen, TRUE, NULL, reallen, FALSE, FALSE); } /* obj_copy_object_id */ /******************************************************************** * FUNCTION obj_copy_object_id_mod * * Generate the object ID for an object node and copy to the buffer * copy an object ID to a buffer; Use modname in object identifier * * INPUTS: * obj == node to generate the instance ID for * buff == buffer to use * bufflen == size of buff * reallen == address of return length of actual identifier * (may be NULL) * * OUTPUTS * buff == filled in with the object ID * if reallen not NULL: * *reallen == length of identifier, even if error occurred * * RETURNS: * status *********************************************************************/ status_t obj_copy_object_id_mod (const obj_template_t *obj, xmlChar *buff, uint32 bufflen, uint32 *reallen) { #ifdef DEBUG if (!obj || !buff) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return get_object_string(obj, NULL, buff, bufflen, TRUE, NULL, reallen, TRUE, FALSE); } /* obj_copy_object_id_mod */ /******************************************************************** * FUNCTION obj_gen_aughook_id * * Malloc and Generate the augment hook element name for * the specified object. This will be a child node of the * specified object. * * INPUTS: * obj == node to generate the augment hook ID for * buff == pointer to address of buffer to use * * OUTPUTS * *buff == malloced buffer with the instance ID * * RETURNS: * status *********************************************************************/ status_t obj_gen_aughook_id (const obj_template_t *obj, xmlChar **buff) { xmlChar *p; uint32 len, extra; status_t res; #ifdef DEBUG if (!obj || !buff) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif *buff = NULL; /* figure out the length of the aughook ID */ res = get_object_string(obj, NULL, NULL, 0, FALSE, NULL, &len, FALSE, FALSE); if (res != NO_ERR) { return res; } /* get the length for the aughook prefix and suffix */ extra = (xml_strlen(NCX_AUGHOOK_START) + xml_strlen(NCX_AUGHOOK_END)); /* get a buffer to fit the instance ID string */ *buff = (xmlChar *)m__getMem(len+extra+1); if (!*buff) { return ERR_INTERNAL_MEM; } /* put prefix in buffer */ p = *buff; p += xml_strcpy(p, NCX_AUGHOOK_START); /* add the aughook ID to the buffer */ res = get_object_string(obj, NULL, p, len+1, FALSE, NULL, &len, FALSE, FALSE); if (res != NO_ERR) { m__free(*buff); *buff = NULL; return SET_ERROR(res); } /* add suffix to the buffer */ p += len; xml_strcpy(p, NCX_AUGHOOK_END); return NO_ERR; } /* obj_gen_aughook_id */ /******************************************************************** * FUNCTION obj_get_name * * Get the name field for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * pointer to the name field, NULL if some error or unnamed *********************************************************************/ const xmlChar * obj_get_name (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return (const xmlChar *)""; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: return obj->def.container->name; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: return obj->def.leaf->name; case OBJ_TYP_LEAF_LIST: return obj->def.leaflist->name; case OBJ_TYP_LIST: return obj->def.list->name; case OBJ_TYP_CHOICE: return obj->def.choic->name; case OBJ_TYP_CASE: return obj->def.cas->name; case OBJ_TYP_USES: return YANG_K_USES; case OBJ_TYP_AUGMENT: return YANG_K_AUGMENT; case OBJ_TYP_REFINE: return YANG_K_REFINE; case OBJ_TYP_RPC: return obj->def.rpc->name; case OBJ_TYP_RPCIO: return obj->def.rpcio->name; case OBJ_TYP_NOTIF: return obj->def.notif->name; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_EL_NONE; } /*NOTREACHED*/ } /* obj_get_name */ /******************************************************************** * FUNCTION obj_set_name * * Set the name field for this obj * * INPUTS: * obj == the specific object to set or change the name * objname == new name string to use * * RETURNS: * status *********************************************************************/ status_t obj_set_name (obj_template_t *obj, const xmlChar *objname) { xmlChar **namevar, *newname; boolean *nameclone, defnameclone; #ifdef DEBUG if (obj == NULL || objname == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif namevar = NULL; defnameclone = FALSE; nameclone = &defnameclone; switch (obj->objtype) { case OBJ_TYP_CONTAINER: namevar = &obj->def.container->name; break; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: namevar = &obj->def.leaf->name; break; case OBJ_TYP_LEAF_LIST: namevar = &obj->def.leaflist->name; break; case OBJ_TYP_LIST: namevar = &obj->def.list->name; break; case OBJ_TYP_CHOICE: namevar = &obj->def.choic->name; break; case OBJ_TYP_CASE: namevar = &obj->def.cas->name; nameclone = &obj->def.cas->nameclone; break; case OBJ_TYP_USES: case OBJ_TYP_AUGMENT: case OBJ_TYP_REFINE: return ERR_NCX_SKIPPED; case OBJ_TYP_RPC: namevar = &obj->def.rpc->name; break; case OBJ_TYP_RPCIO: namevar = &obj->def.rpcio->name; break; case OBJ_TYP_NOTIF: namevar = &obj->def.notif->name; break; case OBJ_TYP_NONE: default: return SET_ERROR(ERR_INTERNAL_VAL); } newname = xml_strdup(objname); if (newname == NULL) { return ERR_INTERNAL_MEM; } if (*namevar != NULL && !*nameclone) { m__free(*namevar); *namevar = NULL; } *namevar = newname; *nameclone = TRUE; return NO_ERR; } /* obj_set_name */ /******************************************************************** * FUNCTION obj_has_name * * Check if the specified object type has a name * * this function is used throughout the code to * filter out uses and augment nodes from the * real nodes. Those are the only YANG nodes that * do not have a name assigned to them * * INPUTS: * obj == the specific object to check * * RETURNS: * TRUE if obj has a name * FALSE otherwise *********************************************************************/ boolean obj_has_name (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: case OBJ_TYP_LIST: case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: return TRUE; case OBJ_TYP_USES: case OBJ_TYP_AUGMENT: case OBJ_TYP_REFINE: return FALSE; case OBJ_TYP_RPC: case OBJ_TYP_RPCIO: case OBJ_TYP_NOTIF: return TRUE; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* obj_has_name */ /******************************************************************** * FUNCTION obj_has_text_content * * Check if the specified object type has a text content * for XPath purposes * * INPUTS: * obj == the specific object to check * * RETURNS: * TRUE if obj has text content * FALSE otherwise *********************************************************************/ boolean obj_has_text_content (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (obj->objtype) { case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: return TRUE; default: return FALSE; } /*NOTREACHED*/ } /* obj_has_text_content */ /******************************************************************** * FUNCTION obj_get_status * * Get the status field for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * YANG status clause for this object *********************************************************************/ ncx_status_t obj_get_status (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_STATUS_NONE; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: return obj->def.container->status; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: return obj->def.leaf->status; case OBJ_TYP_LEAF_LIST: return obj->def.leaflist->status; case OBJ_TYP_LIST: return obj->def.list->status; case OBJ_TYP_CHOICE: return obj->def.choic->status; case OBJ_TYP_CASE: case OBJ_TYP_REFINE: return (obj->parent) ? obj_get_status(obj->parent) : NCX_STATUS_CURRENT; case OBJ_TYP_USES: return obj->def.uses->status; case OBJ_TYP_AUGMENT: return obj->def.augment->status; case OBJ_TYP_RPC: return obj->def.rpc->status; case OBJ_TYP_RPCIO: return (obj->parent) ? obj_get_status(obj->parent) : NCX_STATUS_CURRENT; case OBJ_TYP_NOTIF: return obj->def.notif->status; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_STATUS_NONE; } /*NOTREACHED*/ } /* obj_get_status */ /******************************************************************** * FUNCTION obj_get_description * * Get the description field for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * YANG description string for this object *********************************************************************/ const xmlChar * obj_get_description (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: return obj->def.container->descr; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: return obj->def.leaf->descr; case OBJ_TYP_LEAF_LIST: return obj->def.leaflist->descr; case OBJ_TYP_LIST: return obj->def.list->descr; case OBJ_TYP_CHOICE: return obj->def.choic->descr; case OBJ_TYP_CASE: return obj->def.cas->descr; case OBJ_TYP_USES: return obj->def.uses->descr; case OBJ_TYP_REFINE: return obj->def.refine->descr; case OBJ_TYP_AUGMENT: return obj->def.augment->descr; case OBJ_TYP_RPC: return obj->def.rpc->descr; case OBJ_TYP_RPCIO: return NULL; case OBJ_TYP_NOTIF: return obj->def.notif->descr; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* obj_get_description */ /******************************************************************** * FUNCTION obj_get_alt_description * * Get the alternate description field for this obj * Check if any 'info', then 'help' appinfo nodes present * * INPUTS: * obj == the specific object to check * * RETURNS: * YANG description string for this object *********************************************************************/ const xmlChar * obj_get_alt_description (const obj_template_t *obj) { const ncx_appinfo_t *appinfo; const xmlChar *altdescr; #ifdef DEBUG if (obj == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif altdescr = NULL; appinfo = ncx_find_const_appinfo(&obj->appinfoQ, NULL, /* any module */ NCX_EL_INFO); if (appinfo != NULL) { altdescr = ncx_get_appinfo_value(appinfo); } if (altdescr != NULL) { return altdescr; } appinfo = ncx_find_const_appinfo(&obj->appinfoQ, NULL, /* any module */ NCX_EL_HELP); if (appinfo != NULL) { altdescr = ncx_get_appinfo_value(appinfo); } return altdescr; } /* obj_get_alt_description */ /******************************************************************** * FUNCTION obj_get_description_addr * * Get the address of the description field for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * YANG description string for this object *********************************************************************/ const void * obj_get_description_addr (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: return &obj->def.container->descr; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: return &obj->def.leaf->descr; case OBJ_TYP_LEAF_LIST: return &obj->def.leaflist->descr; case OBJ_TYP_LIST: return &obj->def.list->descr; case OBJ_TYP_CHOICE: return &obj->def.choic->descr; case OBJ_TYP_CASE: return &obj->def.cas->descr; case OBJ_TYP_USES: return &obj->def.uses->descr; case OBJ_TYP_REFINE: return &obj->def.refine->descr; case OBJ_TYP_AUGMENT: return &obj->def.augment->descr; case OBJ_TYP_RPC: return &obj->def.rpc->descr; case OBJ_TYP_RPCIO: return NULL; case OBJ_TYP_NOTIF: return &obj->def.notif->descr; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* obj_get_description_addr */ /******************************************************************** * FUNCTION obj_get_reference * * Get the reference field for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * YANG reference string for this object *********************************************************************/ const xmlChar * obj_get_reference (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: return obj->def.container->ref; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: return obj->def.leaf->ref; case OBJ_TYP_LEAF_LIST: return obj->def.leaflist->ref; case OBJ_TYP_LIST: return obj->def.list->ref; case OBJ_TYP_CHOICE: return obj->def.choic->ref; case OBJ_TYP_CASE: return obj->def.cas->ref; case OBJ_TYP_USES: return obj->def.uses->ref; case OBJ_TYP_REFINE: return obj->def.refine->ref; case OBJ_TYP_AUGMENT: return obj->def.augment->ref; case OBJ_TYP_RPC: return obj->def.rpc->ref; case OBJ_TYP_RPCIO: return NULL; case OBJ_TYP_NOTIF: return obj->def.notif->ref; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* obj_get_reference */ /******************************************************************** * FUNCTION obj_get_reference_addr * * Get the reference field for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * YANG reference string for this object *********************************************************************/ const void * obj_get_reference_addr (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: return &obj->def.container->ref; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: return &obj->def.leaf->ref; case OBJ_TYP_LEAF_LIST: return &obj->def.leaflist->ref; case OBJ_TYP_LIST: return &obj->def.list->ref; case OBJ_TYP_CHOICE: return &obj->def.choic->ref; case OBJ_TYP_CASE: return &obj->def.cas->ref; case OBJ_TYP_USES: return &obj->def.uses->ref; case OBJ_TYP_REFINE: return &obj->def.refine->ref; case OBJ_TYP_AUGMENT: return &obj->def.augment->ref; case OBJ_TYP_RPC: return &obj->def.rpc->ref; case OBJ_TYP_RPCIO: return NULL; case OBJ_TYP_NOTIF: return &obj->def.notif->ref; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* obj_get_reference_addr */ /******************************************************************** * FUNCTION obj_get_config_flag * * Get the config flag for an obj_template_t * Return the explicit value or the inherited value * Also return if the config-stmt is really set or not * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if config set to TRUE * FALSE if config set to FALSE * *********************************************************************/ boolean obj_get_config_flag (const obj_template_t *obj) { boolean retval; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif retval = obj_get_config_flag_deep(obj); return retval; } /* obj_get_config_flag */ /******************************************************************** * FUNCTION obj_get_config_flag2 * * Get the config flag for an obj_template_t * Return the explicit value or the inherited value * Also return if the config-stmt is really set or not * * INPUTS: * obj == obj_template to check * setflag == address of return config-stmt set flag * * OUTPUTS: * *setflag == TRUE if the config-stmt is set in this * node, or if it is a top-level object * == FALSE if the config-stmt is inherited from its parent * * RETURNS: * TRUE if config set to TRUE * FALSE if config set to FALSE * *********************************************************************/ boolean obj_get_config_flag2 (const obj_template_t *obj, boolean *setflag) { #ifdef DEBUG if (!obj || !setflag) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return get_config_flag(obj, setflag); } /* obj_get_config_flag2 */ /******************************************************************** * FUNCTION obj_get_max_access * * Get the NCX max-access enum for an obj_template_t * Return the explicit value or the inherited value * * INPUTS: * obj == obj_template to check * * RETURNS: * ncx_access_t enumeration *********************************************************************/ ncx_access_t obj_get_max_access (const obj_template_t *obj) { boolean retval, setflag, done; #ifdef DEBUG if (obj == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_ACCESS_NONE; } #endif done = FALSE; while (!done) { setflag = FALSE; retval = get_config_flag(obj, &setflag); if (setflag) { done = TRUE; } else { obj = obj->parent; if (obj == NULL || obj_is_root(obj)) { done = TRUE; } } } if (setflag) { return (retval) ? NCX_ACCESS_RC : NCX_ACCESS_RO; } else { /* top-level not set defaults to config */ return NCX_ACCESS_RC; } } /* obj_get_max_access */ /******************************************************************** * FUNCTION obj_get_appinfoQ * * Get the appinfoQ for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * pointer to the appinfoQ for this object *********************************************************************/ dlq_hdr_t * obj_get_appinfoQ (obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return &obj->appinfoQ; } /* obj_get_appinfoQ */ /******************************************************************** * FUNCTION obj_get_mustQ * * Get the mustQ for this obj * * INPUTS: * obj == the specific object to check * * RETURNS: * pointer to the mustQ for this object *********************************************************************/ dlq_hdr_t * obj_get_mustQ (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: return &obj->def.container->mustQ; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: return &obj->def.leaf->mustQ; case OBJ_TYP_LEAF_LIST: return &obj->def.leaflist->mustQ; case OBJ_TYP_LIST: return &obj->def.list->mustQ; case OBJ_TYP_REFINE: return &obj->def.refine->mustQ; default: return NULL; } /*NOTREACHED*/ } /* obj_get_mustQ */ /******************************************************************** * FUNCTION obj_get_typestr * * Get the name of the object type * * INPUTS: * obj == obj_template to check * * RETURNS: * name string for this object type *********************************************************************/ const xmlChar * obj_get_typestr (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_EL_NONE; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: return YANG_K_CONTAINER; case OBJ_TYP_ANYXML: return YANG_K_ANYXML; case OBJ_TYP_ANYDATA: return YANG_K_ANYDATA; case OBJ_TYP_LEAF: return YANG_K_LEAF; case OBJ_TYP_LEAF_LIST: return YANG_K_LEAF_LIST; case OBJ_TYP_LIST: return YANG_K_LIST; case OBJ_TYP_CHOICE: return YANG_K_CHOICE; case OBJ_TYP_CASE: return YANG_K_CASE; case OBJ_TYP_USES: return YANG_K_USES; case OBJ_TYP_REFINE: return YANG_K_REFINE; case OBJ_TYP_AUGMENT: return YANG_K_AUGMENT; case OBJ_TYP_RPC: return YANG_K_RPC; case OBJ_TYP_RPCIO: return YANG_K_CONTAINER; case OBJ_TYP_NOTIF: return YANG_K_NOTIFICATION; case OBJ_TYP_NONE: return NCX_EL_NONE; default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_EL_NONE; } /*NOTREACHED*/ } /* obj_get_typestr */ /******************************************************************** * FUNCTION obj_get_datadefQ * * Get the datadefQ (or caseQ) if this object has one * * INPUTS: * obj == object to check * * RETURNS: * pointer to Q of obj_template, or NULL if none *********************************************************************/ dlq_hdr_t * obj_get_datadefQ (obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: return obj->def.container->datadefQ; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: case OBJ_TYP_REFINE: return NULL; case OBJ_TYP_LIST: return obj->def.list->datadefQ; case OBJ_TYP_CHOICE: return obj->def.choic->caseQ; case OBJ_TYP_CASE: return obj->def.cas->datadefQ; case OBJ_TYP_USES: return obj->def.uses->datadefQ; case OBJ_TYP_AUGMENT: return &obj->def.augment->datadefQ; case OBJ_TYP_RPC: return &obj->def.rpc->datadefQ; case OBJ_TYP_RPCIO: return &obj->def.rpcio->datadefQ; case OBJ_TYP_NOTIF: return &obj->def.notif->datadefQ; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* obj_get_datadefQ */ /******************************************************************** * FUNCTION obj_get_cdatadefQ * * Get a const pointer to the datadefQ (or caseQ) if this object has one * * INPUTS: * obj == object to check * * RETURNS: * pointer to Q of obj_template, or NULL if none *********************************************************************/ const dlq_hdr_t * obj_get_cdatadefQ (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: return obj->def.container->datadefQ; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: case OBJ_TYP_REFINE: return NULL; case OBJ_TYP_LIST: return obj->def.list->datadefQ; case OBJ_TYP_CHOICE: return obj->def.choic->caseQ; case OBJ_TYP_CASE: return obj->def.cas->datadefQ; case OBJ_TYP_USES: return obj->def.uses->datadefQ; case OBJ_TYP_AUGMENT: return &obj->def.augment->datadefQ; case OBJ_TYP_RPC: return &obj->def.rpc->datadefQ; case OBJ_TYP_RPCIO: return &obj->def.rpcio->datadefQ; case OBJ_TYP_NOTIF: return &obj->def.notif->datadefQ; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* obj_get_cdatadefQ */ /******************************************************************** * FUNCTION obj_get_default * * Get the default value for the specified object * Only OBJ_TYP_LEAF objtype is supported * If the leaf has nodefault, then the type is checked * Choice defaults are ignored. * * INPUTS: * obj == object to check * * RETURNS: * pointer to default value string or NULL if none *********************************************************************/ const xmlChar * obj_get_default (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (obj->objtype != OBJ_TYP_LEAF) { return NULL; } if (obj->def.leaf->defval) { return obj->def.leaf->defval; } return typ_get_default(obj->def.leaf->typdef); } /* obj_get_default */ /******************************************************************** * FUNCTION obj_get_default_case * * Get the default case for the specified OBJ_TYP_CHOICE object * * INPUTS: * obj == object to check * * RETURNS: * pointer to default case object template OBJ_TYP_CASE *********************************************************************/ obj_template_t * obj_get_default_case (obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (obj->objtype != OBJ_TYP_CHOICE) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif if (obj->def.choic->defval) { return obj_find_child(obj, obj_get_mod_name(obj), obj->def.choic->defval); } return NULL; } /* obj_get_default_case */ /******************************************************************** * FUNCTION obj_get_first_default * * Get the first default value for this object, if any. Only works * for leaf-lists. * * INPUTS: * obj == object to check * * RETURNS: * pointer to first obj_leaflist_defval_t in queue, or NULL if none *********************************************************************/ obj_leaflist_defval_t * obj_get_first_default (const obj_template_t *obj) { if (obj->objtype != OBJ_TYP_LEAF_LIST) { return NULL; } if (dlq_count(obj->def.leaflist->defvalsQ) == 0) { return NULL; } return (obj_leaflist_defval_t *)dlq_firstEntry(obj->def.leaflist->defvalsQ); } /******************************************************************** * FUNCTION obj_get_next_default * * Get the next default value for this object, if any. Only works * for leaf-lists. * * INPUTS: * obj == object to check * * RETURNS: * pointer to the next obj_leaflist_defval_t in queue, or NULL if none *********************************************************************/ obj_leaflist_defval_t * obj_get_next_default (const obj_leaflist_defval_t *def) { return (obj_leaflist_defval_t *)dlq_nextEntry(def); } /******************************************************************** * FUNCTION obj_get_defaults_count * * Get the next number of default values for this leaf-list object. * * INPUTS: * obj == object to check * * RETURNS: * the number of default values in the schema for this object *********************************************************************/ unsigned int obj_get_defaults_count (const obj_template_t *obj) { return dlq_count(obj->def.leaflist->defvalsQ); } /******************************************************************** * FUNCTION obj_get_level * * Get the nest level for the specified object * Top-level is '1' * Does not count groupings as a level * * INPUTS: * obj == object to check * * RETURNS: * level that this object is located, by checking the parent chain *********************************************************************/ uint32 obj_get_level (const obj_template_t *obj) { const obj_template_t *parent; uint32 level; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif level = 1; parent = obj->parent; while (parent && !obj_is_root(parent)) { level++; parent = parent->parent; } return level; } /* obj_get_level */ /******************************************************************** * FUNCTION obj_has_typedefs * * Check if the object has any nested typedefs in it * This will obly be called if the object is defined in a * grouping. * * INPUTS: * obj == object to check * * RETURNS: * TRUE if any nested typedefs, FALSE otherwise *********************************************************************/ boolean obj_has_typedefs (const obj_template_t *obj) { const obj_template_t *chobj; const grp_template_t *grp; const dlq_hdr_t *typedefQ, *groupingQ, *datadefQ; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: typedefQ = obj->def.container->typedefQ; groupingQ = obj->def.container->groupingQ; datadefQ = obj->def.container->datadefQ; break; case OBJ_TYP_LIST: typedefQ = obj->def.list->typedefQ; groupingQ = obj->def.list->groupingQ; datadefQ = obj->def.list->datadefQ; break; case OBJ_TYP_RPC: typedefQ = &obj->def.rpc->typedefQ; groupingQ = &obj->def.rpc->groupingQ; datadefQ = &obj->def.rpc->datadefQ; break; case OBJ_TYP_RPCIO: typedefQ = &obj->def.rpcio->typedefQ; groupingQ = &obj->def.rpcio->groupingQ; datadefQ = &obj->def.rpcio->datadefQ; break; case OBJ_TYP_NOTIF: typedefQ = &obj->def.notif->typedefQ; groupingQ = &obj->def.notif->groupingQ; datadefQ = &obj->def.notif->datadefQ; break; default: return FALSE; } if (!dlq_empty(typedefQ)) { return TRUE; } for (grp = (const grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL; grp = (const grp_template_t *)dlq_nextEntry(grp)) { if (grp_has_typedefs(grp)) { return TRUE; } } for (chobj = (const obj_template_t *)dlq_firstEntry(datadefQ); chobj != NULL; chobj = (const obj_template_t *)dlq_nextEntry(chobj)) { if (obj_has_typedefs(chobj)) { return TRUE; } } return FALSE; } /* obj_has_typedefs */ /******************************************************************** * FUNCTION obj_get_typdef * * Get the typdef for the leaf or leaf-list * * INPUTS: * obj == object to check * * RETURNS: * pointer to the typdef or NULL if this object type does not * have a typdef *********************************************************************/ typ_def_t * obj_get_typdef (obj_template_t *obj) { if (obj->objtype == OBJ_TYP_LEAF || obj->objtype == OBJ_TYP_ANYXML || obj->objtype == OBJ_TYP_ANYDATA) { return obj->def.leaf->typdef; } else if (obj->objtype == OBJ_TYP_LEAF_LIST) { return obj->def.leaflist->typdef; } else { return NULL; } /*NOTREACHED*/ } /* obj_get_typdef */ /******************************************************************** * FUNCTION obj_get_ctypdef * * Get the typdef for the leaf or leaf-list : Const version * * INPUTS: * obj == object to check * * RETURNS: * pointer to the typdef or NULL if this object type does not * have a typdef *********************************************************************/ const typ_def_t * obj_get_ctypdef (const obj_template_t *obj) { if (obj->objtype == OBJ_TYP_LEAF || obj->objtype == OBJ_TYP_ANYXML || obj->objtype == OBJ_TYP_ANYDATA) { return obj->def.leaf->typdef; } else if (obj->objtype == OBJ_TYP_LEAF_LIST) { return obj->def.leaflist->typdef; } else { return NULL; } /*NOTREACHED*/ } /* obj_get_ctypdef */ /******************************************************************** * FUNCTION obj_get_basetype * * Get the NCX base type enum for the object type * * INPUTS: * obj == object to check * * RETURNS: * base type enumeration *********************************************************************/ ncx_btype_t obj_get_basetype (const obj_template_t *obj) { switch (obj->objtype) { case OBJ_TYP_LEAF: return typ_get_basetype(obj->def.leaf->typdef); case OBJ_TYP_LEAF_LIST: return typ_get_basetype(obj->def.leaflist->typdef); case OBJ_TYP_CONTAINER: return NCX_BT_CONTAINER; case OBJ_TYP_LIST: return NCX_BT_LIST; case OBJ_TYP_CHOICE: return NCX_BT_CHOICE; case OBJ_TYP_CASE: return NCX_BT_CASE; case OBJ_TYP_RPC: return NCX_BT_CONTAINER; case OBJ_TYP_RPCIO: return NCX_BT_CONTAINER; case OBJ_TYP_NOTIF: return NCX_BT_CONTAINER; case OBJ_TYP_ANYXML: return NCX_BT_ANYXML; case OBJ_TYP_ANYDATA: return NCX_BT_ANYDATA; default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_BT_NONE; } /*NOTREACHED*/ } /* obj_get_basetype */ /******************************************************************** * FUNCTION obj_get_mod_prefix * * Get the module prefix for this object * * INPUTS: * obj == object to check * * RETURNS: * const pointer to mod prefix *********************************************************************/ const xmlChar * obj_get_mod_prefix (const obj_template_t *obj) { return ncx_get_mod_prefix(obj->tkerr.mod); } /* obj_get_mod_prefix */ /******************************************************************** * FUNCTION obj_get_mod_xmlprefix * * Get the module prefix for this object * * INPUTS: * obj == object to check * * RETURNS: * const pointer to mod XML prefix *********************************************************************/ const xmlChar * obj_get_mod_xmlprefix (const obj_template_t *obj) { return ncx_get_mod_xmlprefix(obj->tkerr.mod); } /* obj_get_mod_xmlprefix */ /******************************************************************** * FUNCTION obj_get_mod_name * * Get the module name for this object * * INPUTS: * obj == object to check * * RETURNS: * const pointer to mod prefix *********************************************************************/ const xmlChar * obj_get_mod_name (const obj_template_t *obj) { ncx_module_t *usemod; #ifdef DEBUG if (obj == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (obj->mod != NULL) { usemod = obj->mod; } else if (obj->tkerr.mod != NULL) { usemod = obj->tkerr.mod; } else { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } if (usemod->ismod) { return usemod->name; } else { return usemod->belongs; } } /* obj_get_mod_name */ /******************************************************************** * FUNCTION obj_get_mod * * Get the module pointer for this object * * INPUTS: * obj == object to check * * RETURNS: * pointer to module *********************************************************************/ ncx_module_t * obj_get_mod (obj_template_t *obj) { ncx_module_t *usemod; #ifdef DEBUG if (obj == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (obj->mod != NULL) { usemod = obj->mod; } else if (obj->tkerr.mod != NULL) { usemod = obj->tkerr.mod; } else { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } return usemod; } /* obj_get_mod */ /******************************************************************** * FUNCTION obj_get_mod_version * * Get the module version for this object * * INPUTS: * obj == object to check * * RETURNS: * const pointer to mod version or NULL if none *********************************************************************/ const xmlChar * obj_get_mod_version (const obj_template_t *obj) { #ifdef DEBUG if (!obj || !obj->tkerr.mod) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return obj->tkerr.mod->version; } /* obj_get_mod_version */ /******************************************************************** * FUNCTION obj_get_type_name * * Get the typename for an object * * INPUTS: * obj == object to check * * RETURNS: * const pointer to type name string *********************************************************************/ const xmlChar * obj_get_type_name (const obj_template_t *obj) { const typ_def_t *typdef; #ifdef DEBUG if (!obj || !obj->tkerr.mod) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif typdef = obj_get_ctypdef(obj); if (typdef) { if (typdef->typenamestr) { return typdef->typenamestr; } else { return (const xmlChar *) tk_get_btype_sym(obj_get_basetype(obj)); } } else { return obj_get_typestr(obj); } } /* obj_get_type_name */ /******************************************************************** * FUNCTION obj_get_nsid * * Get the namespace ID for this object * * INPUTS: * obj == object to check * * RETURNS: * namespace ID *********************************************************************/ xmlns_id_t obj_get_nsid (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif if (obj->nsid != 0) { return obj->nsid; } else if (obj->tkerr.mod) { return ncx_get_mod_nsid(obj->tkerr.mod); } else { return 0; } } /* obj_get_nsid */ /******************************************************************** * FUNCTION obj_get_iqualval * * Get the instance qualifier for this object * * INPUTS: * obj == object to check * * RETURNS: * instance qualifier enumeration *********************************************************************/ ncx_iqual_t obj_get_iqualval (obj_template_t *obj) { boolean required; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_IQUAL_NONE; } #endif required = obj_is_mandatory(obj); return obj_get_iqualval_ex(obj, required); } /* obj_get_iqualval */ /******************************************************************** * FUNCTION obj_get_iqualval_ex * * Get the instance qualifier for this object * * INPUTS: * obj == object to check * required == value to use for 'is_mandatory()' logic * * RETURNS: * instance qualifier enumeration *********************************************************************/ ncx_iqual_t obj_get_iqualval_ex (obj_template_t *obj, boolean required) { ncx_iqual_t ret; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_IQUAL_NONE; } #endif ret = NCX_IQUAL_NONE; switch (obj->objtype) { case OBJ_TYP_CONTAINER: case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: case OBJ_TYP_RPCIO: ret = (required) ? NCX_IQUAL_ONE : NCX_IQUAL_OPT; break; case OBJ_TYP_LEAF_LIST: if (obj->def.leaflist->minset) { if (obj->def.leaflist->maxset && obj->def.leaflist->maxelems==1) { ret = NCX_IQUAL_ONE; } else { ret = NCX_IQUAL_1MORE; } } else { if (obj->def.leaflist->maxset && obj->def.leaflist->maxelems==1) { ret = NCX_IQUAL_OPT; } else { ret = NCX_IQUAL_ZMORE; } } break; case OBJ_TYP_LIST: if (obj->def.list->minset) { if (obj->def.list->maxset && obj->def.list->maxelems==1) { ret = NCX_IQUAL_ONE; } else { ret = NCX_IQUAL_1MORE; } } else { if (obj->def.list->maxset && obj->def.list->maxelems==1) { ret = NCX_IQUAL_OPT; } else { ret = NCX_IQUAL_ZMORE; } } break; case OBJ_TYP_REFINE: ret = NCX_IQUAL_ZMORE; break; case OBJ_TYP_RPC: case OBJ_TYP_NOTIF: ret = NCX_IQUAL_ONE; break; default: SET_ERROR(ERR_INTERNAL_VAL); } return ret; } /* obj_get_iqualval_ex */ /******************************************************************** * FUNCTION obj_get_min_elements * * Get the min-elements clause for this object, if any * * INPUTS: * obj == object to check * minelems == address of return min-elements value * * OUTPUTS: * *minelems == min-elements value if it is set for this object * * RETURNS: * TRUE if min-elements is set, FALSE if not or N/A *********************************************************************/ boolean obj_get_min_elements (obj_template_t *obj, uint32 *minelems) { #ifdef DEBUG if (!obj || !minelems) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (obj->objtype) { case OBJ_TYP_LEAF_LIST: *minelems = obj->def.leaflist->minelems; return obj->def.leaflist->minset; case OBJ_TYP_LIST: *minelems = obj->def.list->minelems; return obj->def.list->minset; case OBJ_TYP_REFINE: *minelems = obj->def.refine->minelems; return (obj->def.refine->minelems_tkerr.mod) ? TRUE : FALSE; default: return FALSE; } /*NOTREACHED*/ } /* obj_get_min_elements */ /******************************************************************** * FUNCTION obj_get_max_elements * * Get the max-elements clause for this object, if any * * INPUTS: * obj == object to check * maxelems == address of return max-elements value * * OUTPUTS: * *maxelems == max-elements value if it is set for this object * * RETURNS: * TRUE if max-elements is set, FALSE if not or N/A *********************************************************************/ boolean obj_get_max_elements (obj_template_t *obj, uint32 *maxelems) { #ifdef DEBUG if (!obj || !maxelems) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (obj->objtype) { case OBJ_TYP_LEAF_LIST: *maxelems = obj->def.leaflist->maxelems; return obj->def.leaflist->maxset; case OBJ_TYP_LIST: *maxelems = obj->def.list->maxelems; return obj->def.list->maxset; case OBJ_TYP_REFINE: *maxelems = obj->def.refine->maxelems; return (obj->def.refine->maxelems_tkerr.mod) ? TRUE : FALSE; default: return FALSE; } /*NOTREACHED*/ } /* obj_get_max_elements */ /******************************************************************** * FUNCTION obj_get_units * * Get the units clause for this object, if any * * INPUTS: * obj == object to check * * RETURNS: * pointer to units clause, or NULL if none *********************************************************************/ const xmlChar * obj_get_units (obj_template_t *obj) { const xmlChar *units; const typ_def_t *typdef; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif units = NULL; switch (obj->objtype) { case OBJ_TYP_LEAF: units = obj->def.leaf->units; break; case OBJ_TYP_LEAF_LIST: units = obj->def.leaflist->units; break; default: return NULL; } if (!units) { typdef = obj_get_ctypdef(obj); if (typdef) { units = typ_get_units_from_typdef(typdef); } } return units; } /* obj_get_units */ /******************************************************************** * FUNCTION obj_get_parent * * Get the parent of the current object * * INPUTS: * obj == object to check * * RETURNS: * pointer to the parent of this object or NULL if none *********************************************************************/ obj_template_t * obj_get_parent (obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return obj->parent; } /* obj_get_parent */ /******************************************************************** * FUNCTION obj_get_cparent * * Get the parent of the current object * CONST POINTER VERSION * * INPUTS: * obj == object to check * * RETURNS: * pointer to the parent of this object or NULL if none *********************************************************************/ const obj_template_t * obj_get_cparent (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return obj->parent; } /* obj_get_cparent */ /******************************************************************** * FUNCTION obj_get_real_parent * * Get the parent of the current object; * skip OBJ_TYP_CHOICE and OBJ_TYP_CASE * * INPUTS: * obj == object to check * * RETURNS: * pointer to the parent of this object or NULL if none *********************************************************************/ obj_template_t * obj_get_real_parent (obj_template_t *obj) { #ifdef DEBUG if (obj == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif obj = obj->parent; if (obj != NULL) { switch (obj->objtype) { case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: return obj_get_real_parent(obj); default: return obj; } } return NULL; } /* obj_get_real_parent */ /******************************************************************** * FUNCTION obj_get_presence_string * * Get the presence-stmt value, if any * * INPUTS: * obj == obj_template to check * * RETURNS: * pointer to string * NULL if none *********************************************************************/ const xmlChar * obj_get_presence_string (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (obj->objtype != OBJ_TYP_CONTAINER) { return NULL; } return obj->def.container->presence; } /* obj_get_presence_string */ /******************************************************************** * FUNCTION obj_get_presence_string_field * * Get the address ot the presence-stmt value, if any * * INPUTS: * obj == obj_template to check * * RETURNS: * pointer to address of presence string * NULL if none *********************************************************************/ void* obj_get_presence_string_field (const obj_template_t *obj) { assert( obj && "obj is NULL" ); if (obj->objtype != OBJ_TYP_CONTAINER) { return NULL; } return &obj->def.container->presence; } /* obj_get_presence_string_field */ /******************************************************************** * Get the correct child node for the specified parent and * current XML node. This function finds the right module namespace * and child node, given the current context * !! Will ignore choice and case nodes !! * !! This function called by agt_val_parse and mgr_val_parse * !! Only YANG data nodes are expected * * \param obj the parent object template * \param chobj the current child node * (may be NULL if the xmlorder param is FALSE). * \param xmlorder TRUE if should follow strict XML element order, * FALSE if sibling node order errors should be * ignored; find child nodes out of order * and check too-many-instances later * \param curnode the current XML start or empty node to check * \param force_modQ the Q of ncx_module_t to check, if set to NULL and the * xmlns registry of module pointers will be used instead * (except netconf.yang) * \param rettop the address of return topchild object * \param retobj the address of return object to use * \return * status *********************************************************************/ status_t obj_get_child_node ( obj_template_t *obj, obj_template_t *chobj, const xml_node_t *curnode, boolean xmlorder, dlq_hdr_t *force_modQ, obj_template_t **rettop, obj_template_t **retobj ) { assert ( obj && "obj is NULL" ); assert ( curnode && "curnode is NULL" ); assert ( rettop && "rettop is NULL" ); assert ( retobj && "retobj is NULL" ); boolean topdone = FALSE; obj_template_t *foundobj = search_for_child_node( obj, chobj, curnode, xmlorder, force_modQ, rettop, &topdone ); if (foundobj) { if (foundobj->objtype == OBJ_TYP_CHOICE) { log_debug("\n***CHOICE %s \n", obj_get_name(foundobj)); } if (foundobj->objtype == OBJ_TYP_CASE) { log_debug("\n***CASE %s \n", obj_get_name(foundobj)); } *retobj = foundobj; if (!topdone) { *rettop = foundobj; } return NO_ERR; } return ERR_NCX_DEF_NOT_FOUND; } /* obj_get_child_node */ /******************************************************************** * FUNCTION obj_get_child_count * * Get the number of child nodes the object has * * INPUTS: * obj == obj_template to check * * RETURNS: * number of child nodes *********************************************************************/ uint32 obj_get_child_count (const obj_template_t *obj) { assert( obj && "obj is NULL" ); const dlq_hdr_t *datadefQ = obj_get_cdatadefQ(obj); if (datadefQ) { return dlq_count(datadefQ); } else { return 0; } } /* obj_get_child_count */ /******************************************************************** * FUNCTION obj_get_default_parm * * Get the ncx:default-parm object for this object * Only supported for OBJ_TYP_CONTAINER and OBJ_TYP_RPCIO (input) * * INPUTS: * obj == the specific object to check * * RETURNS: * pointer to the name field, NULL if some error or unnamed *********************************************************************/ obj_template_t * obj_get_default_parm (obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (obj->objtype) { case OBJ_TYP_CONTAINER: return obj->def.container->defaultparm; case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: case OBJ_TYP_LIST: case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: case OBJ_TYP_USES: case OBJ_TYP_AUGMENT: case OBJ_TYP_REFINE: case OBJ_TYP_RPC: case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: return NULL; case OBJ_TYP_RPCIO: if (obj->def.rpcio->defaultparm != NULL) { return obj->def.rpcio->defaultparm; } if (!xml_strcmp(obj_get_name(obj), YANG_K_INPUT)) { if (obj_get_child_count(obj) == 1) { obj_template_t *childobj = obj_first_child(obj); if (childobj != NULL && obj_is_leafy(childobj)) { return childobj; } } } return NULL; case OBJ_TYP_NOTIF: return NULL; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* obj_get_default_parm */ /******************************************************************** * FUNCTION obj_get_config_flag_deep * * get config flag during augment expand * Get the config flag for an obj_template_t * Go all the way up the tree until an explicit * set node or the root is found * * Used by get_list_key because the config flag * of the parent is not set yet when a key leaf is expanded * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if config set to TRUE * FALSE if config set to FALSE *********************************************************************/ boolean obj_get_config_flag_deep (const obj_template_t *obj) { switch (obj->objtype) { case OBJ_TYP_CONTAINER: case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: case OBJ_TYP_LIST: case OBJ_TYP_CHOICE: if (obj_is_root(obj)) { return TRUE; } /* check if this normal object has a config-stmt */ if (obj->flags & OBJ_FL_CONFSET) { return (obj->flags & OBJ_FL_CONFIG) ? TRUE : FALSE; } if (obj->parent) { return obj_get_config_flag_deep(obj->parent); } /* this should be an object in a grouping */ if (obj->grp) { return TRUE; // !!! this is the old default !!! // return FALSE; } else { return TRUE; } case OBJ_TYP_CASE: if (obj->parent) { return obj_get_config_flag_deep(obj->parent); } else { /* should not happen */ return FALSE; } case OBJ_TYP_AUGMENT: if(obj->def.augment->targobj) { return obj_get_config_flag_deep(obj->def.augment->targobj); } case OBJ_TYP_USES: case OBJ_TYP_REFINE: /* no real setting -- not applicable */ return FALSE; case OBJ_TYP_RPC: /* no real setting for this, but has to be true * to allow rpc/input to be true */ return TRUE; case OBJ_TYP_RPCIO: if (!xml_strcmp(obj->def.rpcio->name, YANG_K_INPUT)) { return TRUE; } else { return FALSE; } case OBJ_TYP_NOTIF: return FALSE; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* obj_get_config_flag_deep */ /******************************************************************** * FUNCTION obj_get_config_flag_check * * get config flag during YANG module checking * Used by yang_obj.c to make sure ncx:root objects * are not treated as 'config', like obj_get_config_deep * * INPUTS: * obj == obj_template to check * ingrp == address if in grouping flag * * OUTPUTS: * *ingrp == TRUE if hit grouping top without finding * a definitive answer * RETURNS: * TRUE if config set to TRUE * FALSE if config set to FALSE * !!! ignore if *ingrp == TRUE *********************************************************************/ boolean obj_get_config_flag_check (const obj_template_t *obj, boolean *ingrp) { #ifdef DEBUG if (obj == NULL || ingrp == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif *ingrp = FALSE; switch (obj->objtype) { case OBJ_TYP_CONTAINER: case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: case OBJ_TYP_LIST: case OBJ_TYP_CHOICE: /* check if this normal object has a config-stmt */ if (obj->flags & OBJ_FL_CONFSET) { return (obj->flags & OBJ_FL_CONFIG) ? TRUE : FALSE; } if (obj->parent) { return obj_get_config_flag_check(obj->parent, ingrp); } /* this should be an object in a grouping */ if (obj->grp) { *ingrp = TRUE; return FALSE; } else { return TRUE; } case OBJ_TYP_CASE: if (obj->parent) { return obj_get_config_flag_check(obj->parent, ingrp); } else { /* should not happen */ return FALSE; } case OBJ_TYP_USES: case OBJ_TYP_AUGMENT: case OBJ_TYP_REFINE: /* no real setting -- not applicable */ return FALSE; case OBJ_TYP_RPC: /* no real setting for this, but has to be true * to allow rpc/input to be true */ return TRUE; case OBJ_TYP_RPCIO: if (!xml_strcmp(obj->def.rpcio->name, YANG_K_INPUT)) { return TRUE; } else { return FALSE; } case OBJ_TYP_NOTIF: return FALSE; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* obj_get_config_flag_check */ /******************************************************************** * FUNCTION obj_get_fraction_digits * * Get the fraction-digits field from the object typdef * * INPUTS: * obj == object template to check * * RETURNS: * number of fixed decimal digits expected (1..18) * 0 if some error *********************************************************************/ uint8 obj_get_fraction_digits (const obj_template_t *obj) { const typ_def_t *typdef; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif typdef = obj_get_ctypdef(obj); if (typdef) { return typ_get_fraction_digits(typdef); } else { return 0; } } /* obj_get_fraction_digits */ /******************************************************************** * FUNCTION obj_get_first_iffeature * * Get the first if-feature clause (if any) for the specified object * * INPUTS: * obj == object template to check * * RETURNS: * pointer to first if-feature struct * NULL if none available *********************************************************************/ const ncx_iffeature_t * obj_get_first_iffeature (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (const ncx_iffeature_t *) dlq_firstEntry(&obj->iffeatureQ); } /* obj_get_first_iffeature */ /******************************************************************** * FUNCTION obj_get_next_iffeature * * Get the next if-feature clause (if any) * * INPUTS: * iffeature == current iffeature struct * * RETURNS: * pointer to next if-feature struct * NULL if none available *********************************************************************/ const ncx_iffeature_t * obj_get_next_iffeature (const ncx_iffeature_t *iffeature) { #ifdef DEBUG if (!iffeature) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (const ncx_iffeature_t *)dlq_nextEntry(iffeature); } /* obj_get_next_iffeature */ /******************************************************************** * Check if object is a proper leaf * * \param obj the obj_template to check * \return TRUE if object is a leaf *********************************************************************/ boolean obj_is_leaf (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->objtype == OBJ_TYP_LEAF); } /* obj_is_leaf */ /******************************************************************** * Check if object is a proper leaf or leaflist * * \param obj the obj_template to check * \return TRUE if object is a leaf or leaflist *********************************************************************/ boolean obj_is_leafy (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->objtype == OBJ_TYP_LEAF || obj->objtype == OBJ_TYP_LEAF_LIST); } /* obj_is_leafy */ /******************************************************************** * Figure out if the obj is YANG mandatory or not * * \param obj the obj_template to check * \return TRUE if object is not mandatory *********************************************************************/ boolean obj_is_mandatory (obj_template_t *obj) { assert(obj && "obj is NULL" ); switch (obj->objtype) { case OBJ_TYP_CASE: return FALSE; case OBJ_TYP_CONTAINER: if (obj->def.container->presence) { return FALSE; } /* else drop through and check children */ case OBJ_TYP_RPCIO: { obj_template_t *chobj = obj_first_child(obj); for ( ; chobj; chobj = obj_next_child(chobj)) { if (obj_is_mandatory(chobj)) { return TRUE; } } } return FALSE; case OBJ_TYP_LEAF: if (obj_is_key(obj)) { return TRUE; } /* else fall through */ case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_CHOICE: return (obj->flags & OBJ_FL_MANDATORY) ? TRUE : FALSE; case OBJ_TYP_LEAF_LIST: return (obj->def.leaflist->minelems) ? TRUE : FALSE; case OBJ_TYP_LIST: return (obj->def.list->minelems) ? TRUE : FALSE; case OBJ_TYP_USES: case OBJ_TYP_AUGMENT: case OBJ_TYP_REFINE: case OBJ_TYP_RPC: case OBJ_TYP_NOTIF: return FALSE; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } } /* obj_is_mandatory */ /******************************************************************** * Figure out if the obj is YANG mandatory or not * Check the when-stmts, not just mandatory-stmt * * \param obj the obj_template to check * \param config_only flag indicating weather to only check the config. * \return TRUE if object is mandatory *********************************************************************/ boolean obj_is_mandatory_when_ex (obj_template_t *obj, boolean config_only) { assert(obj && "obj is NULL" ); if (config_only && !obj_is_config(obj)) { return FALSE; } switch (obj->objtype) { case OBJ_TYP_CONTAINER: if (obj->def.container->presence) { return FALSE; } /* else drop through and check children */ case OBJ_TYP_CASE: case OBJ_TYP_RPCIO: { obj_template_t *chobj = obj_first_child(obj); for ( ; chobj; chobj = obj_next_child(chobj)) { if (obj_is_mandatory_when_ex(chobj, config_only)) { return TRUE; } } } return FALSE; case OBJ_TYP_LEAF: if (obj_is_key(obj)) { return TRUE; } /* else fall through */ case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_CHOICE: if (obj_has_when_stmts(obj)) { return FALSE; } return (obj->flags & OBJ_FL_MANDATORY) ? TRUE : FALSE; case OBJ_TYP_LEAF_LIST: if (obj_has_when_stmts(obj)) { return FALSE; } return (obj->def.leaflist->minelems) ? TRUE : FALSE; case OBJ_TYP_LIST: if (obj_has_when_stmts(obj)) { return FALSE; } return (obj->def.list->minelems) ? TRUE : FALSE; case OBJ_TYP_USES: case OBJ_TYP_AUGMENT: case OBJ_TYP_REFINE: case OBJ_TYP_RPC: case OBJ_TYP_NOTIF: return FALSE; case OBJ_TYP_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } } /* obj_is_mandatory_when_ex */ /******************************************************************** * Figure out if the obj is YANG mandatory or not * Check the when-stmts, not just mandatory-stmt * * \param obj the obj_template to check * \return TRUE if object is mandatory *********************************************************************/ boolean obj_is_mandatory_when (obj_template_t *obj) { return obj_is_mandatory_when_ex(obj, FALSE); } /* obj_is_mandatory_when */ /******************************************************************** * Figure out if the obj is a cloned object, inserted via uses * or augment statements * * \param obj the obj_template to check * \return TRUE if object is cloned *********************************************************************/ boolean obj_is_cloned (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->flags & OBJ_FL_CLONE) ? TRUE : FALSE; } /* obj_is_cloned */ /******************************************************************** * Figure out if the obj is a cloned object, inserted via an * augment statement * * \param obj the obj_template to check * \return TRUE if object is sourced from an augment *********************************************************************/ boolean obj_is_augclone (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->flags & OBJ_FL_AUGCLONE) ? TRUE : FALSE; } /* obj_is_augclone */ /******************************************************************** * Figure out if the obj is a refinement object, within a uses-stmt * * \param obj the obj_template to check * \return TRUE if object is a refinement *********************************************************************/ boolean obj_is_refine (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->objtype == OBJ_TYP_REFINE) ? TRUE : FALSE; } /* obj_is_refine */ /******************************************************************** * Check if the object is defined within data or within a * notification or RPC instead * * \param obj the obj_template to check * \return TRUE if data object (could be in a grouping or real data) *********************************************************************/ boolean obj_is_data (const obj_template_t *obj) { assert(obj && "obj is NULL" ); switch (obj->objtype) { case OBJ_TYP_RPC: case OBJ_TYP_NOTIF: return FALSE; case OBJ_TYP_RPCIO: return TRUE; /* hack for yangdump HTML output */ case OBJ_TYP_REFINE: return FALSE; case OBJ_TYP_AUGMENT: assert(obj->def.augment->targobj); return obj_is_data(obj->def.augment->targobj); default: if (obj->parent && !obj_is_root(obj->parent)) { return obj_is_data(obj->parent); } else { return TRUE; } } } /* obj_is_data */ /******************************************************************** * Check if the object is some sort of data Constrained to only check * the config DB objects, not any notification or RPC objects * * \param obj the obj_template to check * \return TRUE if data object (could be in a grouping or real data) * FALSE if defined within notification or RPC (or some error) *********************************************************************/ boolean obj_is_data_db (const obj_template_t *obj) { assert(obj && "obj is NULL" ); if (obj_is_cli(obj)) { return FALSE; } if (obj_is_abstract(obj) && !obj_is_root(obj)) { return FALSE; } switch (obj->objtype) { case OBJ_TYP_RPC: case OBJ_TYP_NOTIF: return FALSE; case OBJ_TYP_RPCIO: return FALSE; case OBJ_TYP_REFINE: return FALSE; case OBJ_TYP_AUGMENT: assert(obj->def.augment->targobj); return obj_is_data_db(obj->def.augment->targobj); default: if (obj_is_root(obj)) { return TRUE; } else if (obj->parent && !obj_is_root(obj->parent)) { return obj_is_data_db(obj->parent); } else { return TRUE; } } assert(0); /*NOTREACHED*/ } /* obj_is_data_db */ /******************************************************************** * Check if the object is in an rpc/input section * * \param obj the obj_template to check * \return TRUE if /rpc/input object *********************************************************************/ boolean obj_in_rpc (const obj_template_t *obj) { assert(obj && "obj is NULL" ); switch (obj->objtype) { case OBJ_TYP_RPC: case OBJ_TYP_NOTIF: return FALSE; case OBJ_TYP_RPCIO: return (!xml_strcmp(obj_get_name(obj), YANG_K_INPUT)) ? TRUE : FALSE; case OBJ_TYP_REFINE: return FALSE; case OBJ_TYP_AUGMENT: assert(obj->def.augment->targobj); return obj_in_rpc(obj->def.augment->targobj); default: if (obj->parent && !obj_is_root(obj->parent)) { return obj_in_rpc(obj->parent); } else { return FALSE; } } /*NOTREACHED*/ } /* obj_in_rpc */ /******************************************************************** * Check if the object is in an rpc-reply/output section * * \param obj the obj_template to check * \return TRUE if /rpc-reply/output object *********************************************************************/ boolean obj_in_rpc_reply (const obj_template_t *obj) { assert(obj && "obj is NULL" ); switch (obj->objtype) { case OBJ_TYP_RPC: case OBJ_TYP_NOTIF: return FALSE; case OBJ_TYP_RPCIO: return (!xml_strcmp(obj_get_name(obj), YANG_K_OUTPUT)) ? TRUE : FALSE; case OBJ_TYP_REFINE: return FALSE; case OBJ_TYP_AUGMENT: assert(obj->def.augment->targobj); return obj_in_rpc_reply(obj->def.augment->targobj); default: if (obj->parent && !obj_is_root(obj->parent)) { return obj_in_rpc_reply(obj->parent); } else { return FALSE; } } /*NOTREACHED*/ } /* obj_in_rpc_reply */ /******************************************************************** * FUNCTION obj_in_notif * * Check if the object is in a notification * * \param obj the obj_template to check * \return TRUE if /notification object *********************************************************************/ boolean obj_in_notif (const obj_template_t *obj) { assert(obj && "obj is NULL" ); switch (obj->objtype) { case OBJ_TYP_RPC: return FALSE; case OBJ_TYP_NOTIF: return TRUE; case OBJ_TYP_RPCIO: return FALSE; case OBJ_TYP_REFINE: return FALSE; case OBJ_TYP_AUGMENT: assert(obj->def.augment->targobj); return obj_in_notif(obj->def.augment->targobj); default: if (obj->parent && !obj_is_root(obj->parent)) { return obj_in_notif(obj->parent); } else { return FALSE; } } /*NOTREACHED*/ } /* obj_in_notif */ /******************************************************************** * Check if the object is an RPC method * * \param obj the obj_template to check * \return TRUE if RPC method *********************************************************************/ boolean obj_is_rpc (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->objtype == OBJ_TYP_RPC) ? TRUE : FALSE; } /* obj_is_rpc */ /******************************************************************** * Check if the object is a notification * * \param obj the obj_template to check * \return TRUE if notification *********************************************************************/ boolean obj_is_notif (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->objtype == OBJ_TYP_NOTIF); } /* obj_is_notif */ /******************************************************************** * Check if object was entered in empty fashion: * list foo; * uses grpx; * * \param obj the obj_template to check * \return TRUE if object is empty of subclauses * FALSE if object is not empty of subclauses *********************************************************************/ boolean obj_is_empty (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->flags & OBJ_FL_EMPTY) ? TRUE : FALSE; } /* obj_is_empty */ /******************************************************************** * Check if one object is a match in identity with another one * Only used by yangdiff to compare objects. * * \param obj the first obj_template in the match * \param obj the second obj_template in the match * \return TRUE is a match, FALSE otherwise *********************************************************************/ boolean obj_is_match ( const obj_template_t *obj1, const obj_template_t *obj2 ) { if (!xmlns_ids_equal(obj_get_nsid(obj1), obj_get_nsid(obj2))) { return FALSE; } if (obj_has_name(obj1) && obj_has_name(obj2)) { return xml_strcmp(obj_get_name(obj1), obj_get_name(obj2)) ? FALSE : TRUE; } else { return FALSE; } } /* obj_is_match */ /******************************************************************** * Check if object is marked as a hidden object * * \param obj the obj_template to check * \return TRUE if object is marked as OBJ_FL_HIDDEN *********************************************************************/ boolean obj_is_hidden (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->flags & OBJ_FL_HIDDEN) ? TRUE : FALSE; } /* obj_is_hidden */ /******************************************************************** * Check if object is marked as a root object * * \param obj the obj_template to check * \return TRUE if object is marked as OBJ_FL_ROOT *********************************************************************/ boolean obj_is_root (const obj_template_t *obj) { //assert(obj && "obj is NULL" ); return (obj->flags & OBJ_FL_ROOT) ? TRUE : FALSE; } /* obj_is_root */ /******************************************************************** * Check if object is marked as a password object * * \param obj the obj_template to check * \return TRUE if object is marked as OBJ_FL_PASSWD *********************************************************************/ boolean obj_is_password (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->flags & OBJ_FL_PASSWD) ? TRUE : FALSE; } /* obj_is_password */ /******************************************************************** * Check if object is marked as an XSD list * * \param obj the obj_template to check * \return TRUE if object is marked as OBJ_FL_XSDLIST *********************************************************************/ boolean obj_is_xsdlist (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->flags & OBJ_FL_XSDLIST) ? TRUE : FALSE; } /* obj_is_xsdlist */ /******************************************************************** * Check if object is marked as a CLI object * * \param obj the obj_template to check * \return TRUE if object is marked as ncx:cli *********************************************************************/ boolean obj_is_cli (const obj_template_t *obj) { assert(obj && "obj is NULL" ); if (obj->flags & OBJ_FL_CLI) { return TRUE; } else if (obj->parent) { return obj_is_cli(obj->parent); } else { return FALSE; } } /* obj_is_cli */ /******************************************************************** * Check if object is being used as a key leaf within a list * * \param obj the obj_template to check * \return TRUE if object is a key leaf *********************************************************************/ boolean obj_is_key (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->flags & OBJ_FL_KEY) ? TRUE : FALSE; } /* obj_is_key */ /******************************************************************** * Check if object is being used as an object identifier or error-info * * \param obj the obj_template to check * \return TRUE if object is marked as ncx:abstract *********************************************************************/ boolean obj_is_abstract (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->flags & OBJ_FL_ABSTRACT) ? TRUE : FALSE; } /* obj_is_abstract */ /******************************************************************** * Check if object is an XPath string * * \param obj the obj_template to check * \return TRUE if object is marked as ncx:xpath *********************************************************************/ boolean obj_is_xpath_string (const obj_template_t *obj) { assert(obj && "obj is NULL" ); boolean retval = ( (obj->flags & (OBJ_FL_XPATH | OBJ_FL_SCHEMAINST)) || obj_get_basetype(obj)==NCX_BT_INSTANCE_ID) ? TRUE : FALSE; if ( !retval ) { const typ_def_t *typdef = obj_get_ctypdef(obj); if (typdef) { return typ_is_xpath_string(typdef); } } return retval; } /* obj_is_xpath_string */ /******************************************************************** * Check if object is a schema-instance string * * \param obj the obj_template to check * \return TRUE if object is marked as ncx:schema-instance *********************************************************************/ boolean obj_is_schema_instance_string (const obj_template_t *obj) { assert(obj && "obj is NULL" ); if (obj_get_basetype(obj) != NCX_BT_STRING) { return FALSE; } return (obj->flags & OBJ_FL_SCHEMAINST) ? TRUE : FALSE; } /* obj_is_schema_instance_string */ /******************************************************************** * Check if object is tagged ncx:secure * * \param obj the obj_template to check * \return TRUE if object is marked as ncx:secure *********************************************************************/ boolean obj_is_secure (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->flags & OBJ_FL_SECURE) ? TRUE : FALSE; } /* obj_is_secure */ /******************************************************************** * Check if object is tagged ncx:very-secure * * \param obj the obj_template to check * \return TRUE if object is marked as ncx:very-secure *********************************************************************/ boolean obj_is_very_secure (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->flags & OBJ_FL_VERY_SECURE) ? TRUE : FALSE; } /* obj_is_very_secure */ /******************************************************************** * Check if the object is system or user-ordered * * \param obj the obj_template to check * \return TRUE if object is system ordered *********************************************************************/ boolean obj_is_system_ordered (const obj_template_t *obj) { assert(obj && "obj is NULL" ); switch (obj->objtype) { case OBJ_TYP_LEAF_LIST: return obj->def.leaflist->ordersys; case OBJ_TYP_LIST: return obj->def.list->ordersys; default: return TRUE; } /*NOTREACHED*/ } /* obj_is_system_ordered */ /******************************************************************** * Check if the object is an NP-container * * \param obj the obj_template to check * \return TRUE if object is an NP-container *********************************************************************/ boolean obj_is_np_container (const obj_template_t *obj) { assert(obj && "obj is NULL" ); if (obj->objtype != OBJ_TYP_CONTAINER) { return FALSE; } return (obj->def.container->presence) ? FALSE : TRUE; } /* obj_is_np_container */ /******************************************************************** * Check any if-feature statement that may * cause the specified object to be invisible * * \param obj the obj_template to check * \return TRUE if object is enabled *********************************************************************/ boolean obj_is_enabled (const obj_template_t *obj) { assert(obj && "obj is NULL" ); const ncx_iffeature_t *iffeature = obj_get_first_iffeature(obj); for ( ; iffeature ; iffeature = obj_get_next_iffeature(iffeature)) { if (!iffeature->feature || !ncx_feature_enabled(iffeature->feature)) { return FALSE; } } const obj_iffeature_ptr_t *iffptr = (obj_iffeature_ptr_t *) dlq_firstEntry(&obj->inherited_iffeatureQ); for ( ; iffptr ; iffptr = (obj_iffeature_ptr_t *)dlq_nextEntry(iffptr) ) { if (!iffptr->iffeature->feature || !ncx_feature_enabled(iffptr->iffeature->feature)) { return FALSE; } } obj_template_t *testobj = obj->parent; boolean done = FALSE; while (!done) { if (testobj && (testobj->objtype == OBJ_TYP_CHOICE || testobj->objtype == OBJ_TYP_CASE)) { iffeature = obj_get_first_iffeature(testobj); for ( ; iffeature ; iffeature = obj_get_next_iffeature(iffeature)) { if (!iffeature->feature || !ncx_feature_enabled(iffeature->feature)) { return FALSE; } } iffptr = (obj_iffeature_ptr_t *) dlq_firstEntry(&testobj->inherited_iffeatureQ); for ( ; iffptr ; iffptr = (obj_iffeature_ptr_t *) dlq_nextEntry(iffptr) ) { if (!iffptr->iffeature->feature || !ncx_feature_enabled(iffptr->iffeature->feature)) { return FALSE; } } testobj = testobj->parent; } else { done = TRUE; } } return TRUE; } /* obj_is_enabled */ /******************************************************************** * Check if the object is a single instance of if it * allows multiple instances; check all of the * ancestors if needed * * \param obj the obj_template to check * \return TRUE if object is a single instance object *********************************************************************/ boolean obj_is_single_instance (obj_template_t *obj) { assert(obj && "obj is NULL" ); while (obj != NULL) { ncx_iqual_t iqual = obj_get_iqualval(obj); switch (iqual) { case NCX_IQUAL_ZMORE: case NCX_IQUAL_1MORE: return FALSE; default: /* don't bother checking the root and don't go past the root into * the RPC parameters */ obj = obj->parent; if ( obj && obj_is_root(obj)) { obj = NULL; } } } return TRUE; } /* obj_is_single_instance */ /******************************************************************** * Check if the object is a short case statement * * \param obj the obj_template to check * \return TRUE if object is a 1 object case statement *********************************************************************/ boolean obj_is_short_case (obj_template_t *obj) { assert(obj && "obj is NULL" ); const obj_case_t *cas; if (obj->objtype != OBJ_TYP_CASE) { return FALSE; } cas = obj->def.cas; if (dlq_count(cas->datadefQ) != 1) { return FALSE; } if (obj->when && obj->when->exprstr) { return FALSE; } if (obj_get_first_iffeature(obj) != NULL) { return FALSE; } if (obj_get_status(obj) != NCX_STATUS_CURRENT) { return FALSE; } if (obj_get_description(obj) != NULL) { return FALSE; } if (obj_get_reference(obj) != NULL) { return FALSE; } if (dlq_count(obj_get_appinfoQ(obj)) > 0) { return FALSE; } return TRUE; } /* obj_is_short_case */ /******************************************************************** * Check if the object is top-level object within * the YANG module that defines it * * \param obj the obj_template to check * \return TRUE if obj is a top-level object *********************************************************************/ boolean obj_is_top (const obj_template_t *obj) { assert(obj && "obj is NULL" ); return (obj->flags & OBJ_FL_TOP) ? TRUE : FALSE; } /* obj_is_top */ /******************************************************************** * Figure out if the obj is OK for current CLI implementation * Top object must be a container. Child objects must be only choices of leafs, * plain leafs, or leaf lists are allowed * * \param obj the obj_template to check * \return TRUE if object is OK for CLI *********************************************************************/ boolean obj_ok_for_cli (obj_template_t *obj) { assert(obj && "obj is NULL" ); if (obj->objtype != OBJ_TYP_CONTAINER) { return FALSE; } obj_template_t *chobj = obj_first_child(obj); for (; chobj; chobj = obj_next_child(chobj)) { switch (chobj->objtype) { case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: return TRUE; /**** was FALSE ****/ case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: break; case OBJ_TYP_CHOICE: { obj_template_t* casobj = obj_first_child(chobj); for ( ; casobj; casobj = obj_next_child(casobj)) { obj_template_t* caschild = obj_first_child(casobj); for ( ; caschild ; caschild = obj_next_child(caschild)) { switch (caschild->objtype) { case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: return FALSE; case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: break; default: return FALSE; } } } } break; default: return FALSE; } } return TRUE; } /* obj_ok_for_cli */ /******************************************************************** * Check if there are any accessible nodes within the object * * \param obj the obj_template to check * \return TRUE if there are any accessible children *********************************************************************/ boolean obj_has_children (obj_template_t *obj) { assert(obj && "obj is NULL" ); if ( obj_first_child_deep(obj) ) { return TRUE; } else { return FALSE; } } /* obj_has_children */ /******************************************************************** * Check if there are any accessible read-only nodes within the object * * \param obj the obj_template to check * \return TRUE if there are any accessible read-only children *********************************************************************/ boolean obj_has_ro_children (obj_template_t *obj) { assert(obj && "obj is NULL" ); obj_template_t *childobj = obj_first_child(obj); for ( ; childobj ; childobj = obj_next_child(childobj)) { if ( obj_has_name(childobj) && obj_is_enabled(childobj) && !obj_is_abstract(childobj)) { if (!obj_get_config_flag(childobj)) { return TRUE; } } } return FALSE; } /* obj_has_ro_children */ /******************************************************************** * Check if the RPC object has any real input children * * \param obj the obj_template to check * \return TRUE if there are any input children *********************************************************************/ boolean obj_rpc_has_input (obj_template_t *obj) { return obj_rpc_has_input_or_output( obj, YANG_K_INPUT ); } /* obj_rpc_has_input */ /******************************************************************** * Check if the RPC object has any real output children * * \param obj the obj_template to check * \return TRUE if there are any output children *********************************************************************/ boolean obj_rpc_has_output (obj_template_t *obj) { return obj_rpc_has_input_or_output( obj, YANG_K_OUTPUT ); } /* obj_rpc_has_output */ /******************************************************************** * Check if any when-stmts apply to this object * Does not check if they are true, just any when-stmts present * * \param obj the obj_template to check * \return TRUE if object has any when-stmts associated with it *********************************************************************/ boolean obj_has_when_stmts (obj_template_t *obj) { assert(obj && "obj is NULL" ); if (obj->when || !dlq_empty(&obj->inherited_whenQ)) { return TRUE; } obj_template_t *testobj = obj->parent; boolean done = FALSE; while (!done) { if (testobj && (testobj->objtype == OBJ_TYP_CHOICE || testobj->objtype == OBJ_TYP_CASE)) { if (testobj->when || !dlq_empty(&testobj->inherited_whenQ)) { return TRUE; } testobj = testobj->parent; } else { done = TRUE; } } return FALSE; } /* obj_has_when_stmts */ /******************************************************************** * FUNCTION obj_new_metadata * * Malloc and initialize the fields in a an obj_metadata_t * * INPUTS: * isreal == TRUE if this is for a real object * == FALSE if this is a cloned object * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ obj_metadata_t * obj_new_metadata (void) { obj_metadata_t *meta; meta = m__getObj(obj_metadata_t); if (!meta) { return NULL; } (void)memset(meta, 0x0, sizeof(obj_metadata_t)); meta->typdef = typ_new_typdef(); if (!meta->typdef) { m__free(meta); return NULL; } return meta; } /* obj_new_metadata */ /******************************************************************** * FUNCTION obj_free_metadata * * Scrub the memory in a obj_metadata_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * meta == obj_metadata_t data structure to free *********************************************************************/ void obj_free_metadata (obj_metadata_t *meta) { if (!meta) { return; } if (meta->name) { m__free(meta->name); } if (meta->typdef) { typ_free_typdef(meta->typdef); } m__free(meta); } /* obj_free_metadata */ /******************************************************************** * FUNCTION obj_add_metadata * * Add the filled out object metadata definition to the object * * INPUTS: * meta == obj_metadata_t data structure to add * obj == object template to add meta to * * RETURNS: * status *********************************************************************/ status_t obj_add_metadata (obj_metadata_t *meta, obj_template_t *obj) { obj_metadata_t *testmeta; #ifdef DEBUG if (!meta || !obj) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif testmeta = obj_find_metadata(obj, meta->name); if (testmeta) { return ERR_NCX_ENTRY_EXISTS; } meta->parent = obj; meta->nsid = obj_get_nsid(obj); dlq_enque(meta, &obj->metadataQ); return NO_ERR; } /* obj_add_metadata */ /******************************************************************** * FUNCTION obj_find_metadata * * Find the object metadata definition in the object * * INPUTS: * obj == object template to check * name == name of obj_metadata_t data structure to find * * RETURNS: * pointer to found entry, NULL if not found *********************************************************************/ obj_metadata_t * obj_find_metadata (const obj_template_t *obj, const xmlChar *name) { obj_metadata_t *testmeta; #ifdef DEBUG if (!obj || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (testmeta = (obj_metadata_t *) dlq_firstEntry(&obj->metadataQ); testmeta != NULL; testmeta = (obj_metadata_t *) dlq_nextEntry(testmeta)) { if (!xml_strcmp(testmeta->name, name)) { return testmeta; } } return NULL; } /* obj_find_metadata */ /******************************************************************** * FUNCTION obj_first_metadata * * Get the first object metadata definition in the object * * INPUTS: * obj == object template to check * * RETURNS: * pointer to first entry, NULL if none *********************************************************************/ obj_metadata_t * obj_first_metadata (const obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (obj_metadata_t *) dlq_firstEntry(&obj->metadataQ); } /* obj_first_metadata */ /******************************************************************** * FUNCTION obj_next_metadata * * Get the next object metadata definition in the object * * INPUTS: * meta == current meta object template * * RETURNS: * pointer to next entry, NULL if none *********************************************************************/ obj_metadata_t * obj_next_metadata (const obj_metadata_t *meta) { #ifdef DEBUG if (!meta) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (obj_metadata_t *)dlq_nextEntry(meta); } /* obj_next_metadata */ /******************************************************************** * FUNCTION obj_sort_children * * Check all the child nodes of the specified object * and rearrange them into alphabetical order, * based on the element local-name. * * ONLY SAFE TO USE FOR ncx:cli CONTAINERS * YANG DATA CONTENT ORDER NEEDS TO BE PRESERVED * * INPUTS: * obj == object template to reorder *********************************************************************/ void obj_sort_children (obj_template_t *obj) { obj_template_t *newchild, *curchild; dlq_hdr_t *datadefQ, sortQ; boolean done; int retval; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif datadefQ = obj_get_datadefQ(obj); if (datadefQ == NULL) { return; } dlq_createSQue(&sortQ); newchild = (obj_template_t *)dlq_deque(datadefQ); while (newchild != NULL) { if (!obj_has_name(newchild)) { dlq_enque(newchild, &sortQ); } else { obj_sort_children(newchild); done = FALSE; for (curchild = (obj_template_t *) dlq_firstEntry(&sortQ); curchild != NULL && !done; curchild = (obj_template_t *) dlq_nextEntry(curchild)) { if (!obj_has_name(curchild)) { continue; } retval = xml_strcmp(obj_get_name(newchild), obj_get_name(curchild)); if (retval == 0) { if (obj_get_nsid(newchild) < obj_get_nsid(curchild)) { dlq_insertAhead(newchild, curchild); } else { dlq_insertAfter(newchild, curchild); } done = TRUE; } else if (retval < 0) { dlq_insertAhead(newchild, curchild); done = TRUE; } } if (!done) { dlq_enque(newchild, &sortQ); } } newchild = (obj_template_t *)dlq_deque(datadefQ); } dlq_block_enque(&sortQ, datadefQ); } /* obj_sort_children */ /******************************************************************** * FUNCTION obj_set_ncx_flags * * Check the NCX appinfo extensions and set flags as needed * ** INPUTS: * obj == obj_template to check * * OUTPUTS: * may set additional bits in the obj->flags field * *********************************************************************/ void obj_set_ncx_flags (obj_template_t *obj) { assert( obj && "obj is NULL!" ); const dlq_hdr_t *appinfoQ = obj_get_appinfoQ(obj); if (obj_is_leafy(obj)) { if (ncx_find_const_appinfo(appinfoQ, NCX_PREFIX, NCX_EL_PASSWORD)) { obj->flags |= OBJ_FL_PASSWD; } } if (ncx_find_const_appinfo(appinfoQ, NCX_PREFIX, NCX_EL_HIDDEN)) { obj->flags |= OBJ_FL_HIDDEN; } if (obj_is_leafy(obj)) { if (ncx_find_const_appinfo(appinfoQ, NCX_PREFIX, NCX_EL_XSDLIST)) { obj->flags |= OBJ_FL_XSDLIST; } } if (obj->objtype == OBJ_TYP_CONTAINER) { if (ncx_find_const_appinfo(appinfoQ, NCX_PREFIX, NCX_EL_ROOT)) { obj->flags |= OBJ_FL_ROOT; } } if (ncx_find_const_appinfo(appinfoQ, NCX_PREFIX, NCX_EL_CLI)) { obj->flags |= OBJ_FL_CLI; } if (ncx_find_const_appinfo(appinfoQ, NCX_PREFIX, NCX_EL_ABSTRACT)) { obj->flags |= OBJ_FL_ABSTRACT; } if (ncx_find_const_appinfo(appinfoQ, NCX_PREFIX, NCX_EL_DEFAULT_PARM_EQUALS_OK)) { obj->flags |= OBJ_FL_CLI_EQUALS_OK; } if (ncx_find_const_appinfo(appinfoQ, NCX_PREFIX, NCX_EL_SIL_DELETE_CHILDREN_FIRST)) { obj->flags |= OBJ_FL_SIL_DELETE_CHILDREN_FIRST; } if (ncx_find_const_appinfo(appinfoQ, NACM_PREFIX, NCX_EL_SECURE)) { obj->flags |= OBJ_FL_SECURE; } if (ncx_find_const_appinfo(appinfoQ, NACM_PREFIX, NCX_EL_VERY_SECURE)) { obj->flags |= OBJ_FL_VERY_SECURE; } if (obj_is_config(obj)) { const ncx_appinfo_t *appinfo = ncx_find_const_appinfo(appinfoQ, NCX_PREFIX, NCX_EL_USER_WRITE); if (appinfo) { const xmlChar *str = ncx_get_appinfo_value(appinfo); if (str) { ncx_list_t mylist; ncx_init_list(&mylist, NCX_BT_STRING); status_t res = ncx_set_list(NCX_BT_STRING, str, &mylist); if (res != NO_ERR) { /* not setting any user-write flags! */ log_error("\nError: invalid ncx:user-write value '%s' (%s)", str, get_error_string(res)); } else { /* not checking if the list has extra bogus strings!! */ if (!ncx_string_in_list(NCX_EL_CREATE, &mylist)) { obj->flags |= OBJ_FL_BLOCK_CREATE; } if (!ncx_string_in_list(NCX_EL_UPDATE, &mylist)) { obj->flags |= OBJ_FL_BLOCK_UPDATE; } if (!ncx_string_in_list(NCX_EL_DELETE, &mylist)) { obj->flags |= OBJ_FL_BLOCK_DELETE; } } ncx_clean_list(&mylist); } else { /* treat no value the same as an empty string, * which means no user access at all; * YANG parser should complain if the extension usage * has no value proveded */ obj->flags |= OBJ_FL_BLOCK_CREATE; obj->flags |= OBJ_FL_BLOCK_UPDATE; obj->flags |= OBJ_FL_BLOCK_DELETE; } } } if (obj_is_leafy(obj)) { const typ_def_t *typdef = obj_get_ctypdef(obj); /* ncx:xpath extension */ if (typ_is_xpath_string(typdef)) { obj->flags |= OBJ_FL_XPATH; } else if (ncx_find_const_appinfo(appinfoQ, NCX_PREFIX, NCX_EL_XPATH)) { obj->flags |= OBJ_FL_XPATH; } /* ncx:qname extension */ if (typ_is_qname_string(typdef)) { obj->flags |= OBJ_FL_QNAME; } else if (ncx_find_const_appinfo(appinfoQ, NCX_PREFIX, NCX_EL_XPATH)) { obj->flags |= OBJ_FL_QNAME; } /* ncx:schema-instance extension */ if (typ_is_schema_instance_string(typdef)) { obj->flags |= OBJ_FL_SCHEMAINST; } else if (ncx_find_const_appinfo(appinfoQ, NCX_PREFIX, NCX_EL_SCHEMA_INSTANCE)) { obj->flags |= OBJ_FL_SCHEMAINST; } } } /* obj_set_ncx_flags */ /******************************************************************** * FUNCTION obj_enabled_child_count * * Get the count of the number of enabled child nodes * for the object template * * INPUTS: * obj == obj_template to check * * RETURNS: * number of enabled child nodes *********************************************************************/ uint32 obj_enabled_child_count (obj_template_t *obj) { dlq_hdr_t *childQ; obj_template_t *chobj; uint32 count; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif childQ = obj_get_datadefQ(obj); if (childQ == NULL) { return 0; } count = 0; for (chobj = (obj_template_t *)dlq_firstEntry(childQ); chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { if (!obj_has_name(chobj)) { continue; } if (obj_is_enabled(chobj)) { count++; } } return count; } /* obj_enabled_child_count */ /******************************************************************** * FUNCTION obj_dump_child_list * * Dump the object names in a datadefQ -- just child level * uses log_write for output * * INPUTS: * datadefQ == Q of obj_template_t to dump * startindent == start-indent columns * indent == indent amount *********************************************************************/ void obj_dump_child_list (dlq_hdr_t *datadefQ, uint32 startindent, uint32 indent) { obj_template_t *obj; dlq_hdr_t *child_datadefQ; uint32 i; #ifdef DEBUG if (!datadefQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif for (obj = (obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { log_write("\n"); for (i=0; i < startindent; i++) { log_write(" "); } log_write("%s", obj_get_typestr(obj)); if (obj_has_name(obj)) { log_write(" %s", obj_get_name(obj)); } child_datadefQ = obj_get_datadefQ(obj); if (child_datadefQ != NULL) { obj_dump_child_list(child_datadefQ, startindent+indent, indent); } } } /* obj_dump_child_list */ /******************************************************************** * FUNCTION obj_get_keystr * * Get the key string for this list object * * INPUTS: * obj == obj_template to check * * RETURNS: * pointer to key string or NULL if none or not a list *********************************************************************/ const xmlChar * obj_get_keystr (obj_template_t *obj) { #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (obj->objtype != OBJ_TYP_LIST) { return NULL; } return obj->def.list->keystr; } /* obj_get_keystr */ /******************************************************************** * FUNCTION obj_delete_obsolete * * Delete any obsolete child nodes within the specified object subtree * * INPUTS: * objQ == Q of obj_template to check * *********************************************************************/ void obj_delete_obsolete (dlq_hdr_t *objQ) { obj_template_t *childobj, *nextobj; dlq_hdr_t *childdatadefQ; #ifdef DEBUG if (objQ == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif for (childobj = (obj_template_t *)dlq_firstEntry(objQ); childobj != NULL; childobj = nextobj) { nextobj = (obj_template_t *)dlq_nextEntry(childobj); childdatadefQ = obj_get_datadefQ(childobj); if (childdatadefQ != NULL) { obj_delete_obsolete(childdatadefQ); } } } /* obj_delete_obsolete */ /******************************************************************** * Get the alt-name for this object, if any * * \param obj the obj_template to check * \return pointer to alt-name of NULL if none *********************************************************************/ const xmlChar* obj_get_altname (const obj_template_t *obj) { assert( obj && "obj is NULL!" ); const xmlChar *altname = NULL; const ncx_appinfo_t* appinfo = ncx_find_const_appinfo( &obj->appinfoQ, NULL, /* any module */ NCX_EL_ALT_NAME ); if ( appinfo ) { altname = ncx_get_appinfo_value(appinfo); } return altname; } /* obj_get_altname */ /******************************************************************** * Get the target object for a leafref leaf or leaf-list * \param obj the object to check * \return pointer to the target object or NULL if this object type does not * have a leafref target object *********************************************************************/ obj_template_t * obj_get_leafref_targobj (obj_template_t *obj) { assert( obj && "obj is NULL!" ); if (obj->objtype == OBJ_TYP_LEAF) { return obj->def.leaf->leafrefobj; } else if (obj->objtype == OBJ_TYP_LEAF_LIST) { return obj->def.leaflist->leafrefobj; } return NULL; } /* obj_get_leafref_targobj */ /******************************************************************** * Get the target object for an augments object * \param obj the object to check * \return pointer to the augment context target object * or NULL if this object type does not have an augment target object *********************************************************************/ obj_template_t * obj_get_augment_targobj (obj_template_t *obj) { assert( obj && "obj is NULL!" ); if (obj->augobj && obj->augobj->objtype == OBJ_TYP_AUGMENT) { return obj->augobj->def.augment->targobj; } return NULL; } /* obj_get_augment_targobj */ /******************************************************************** * Check if object is marked as ncx:default-parm-equals-ok * * \param obj the obj_template to check * \return TRUE if object is marked as ncx:default-parm-equals-ok *********************************************************************/ boolean obj_is_cli_equals_ok (const obj_template_t *obj) { assert( obj && "obj is NULL!" ); return (obj->flags & OBJ_FL_CLI_EQUALS_OK) ? TRUE : FALSE; } /* obj_is_cli_equals_ok */ /******************************************************************** * Check if object is marked as ncx:sil-delete-children-first * * \param obj the obj_template to check * \return TRUE if object is marked as ncx:sil-delete-children-first. *********************************************************************/ boolean obj_is_sil_delete_children_first (const obj_template_t *obj) { assert( obj && "obj is NULL!" ); return (obj->flags & OBJ_FL_SIL_DELETE_CHILDREN_FIRST) ? TRUE : FALSE; } /* obj_is_sil_delete_children_first */ /******************************************************************** * Add a child object to the specified complex node * * \param child the obj_template to add * \param parent the obj_template of the parent *********************************************************************/ void obj_add_child (obj_template_t *child, obj_template_t *parent) { assert( child && "child is NULL!" ); assert( parent && "parent is NULL!" ); dlq_hdr_t *que = obj_get_datadefQ(parent); if (que) { dlq_enque(child, que); } child->parent = parent; } /* obj_add_child */ /******************************************************************** * FUNCTION obj_is_block_user_create * * Check if object is marked as ncx:user-write with create * access disabled * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked to block user create access * FALSE if not *********************************************************************/ boolean obj_is_block_user_create (const obj_template_t *obj) { assert( obj && "obj is NULL!" ); return (obj->flags & OBJ_FL_BLOCK_CREATE) ? TRUE : FALSE; } /******************************************************************** * FUNCTION obj_is_block_user_update * * Check if object is marked as ncx:user-write with update * access disabled * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked to block user update access * FALSE if not *********************************************************************/ boolean obj_is_block_user_update (const obj_template_t *obj) { assert( obj && "obj is NULL!" ); return (obj->flags & OBJ_FL_BLOCK_UPDATE) ? TRUE : FALSE; } /******************************************************************** * FUNCTION obj_is_block_user_delete * * Check if object is marked as ncx:user-write with delete * access disabled * * INPUTS: * obj == obj_template to check * * RETURNS: * TRUE if object is marked to block user delete access * FALSE if not *********************************************************************/ boolean obj_is_block_user_delete (const obj_template_t *obj) { assert( obj && "obj is NULL!" ); return (obj->flags & OBJ_FL_BLOCK_DELETE) ? TRUE : FALSE; } /******************************************************************** * FUNCTION obj_new_iffeature_ptr * * Malloc and initialize a new obj_iffeature_ptr_t struct * * INPUTS: * iff == iffeature to point at * RETURNS: * malloced struct or NULL if memory error *********************************************************************/ obj_iffeature_ptr_t * obj_new_iffeature_ptr (ncx_iffeature_t *iff) { obj_iffeature_ptr_t *iffptr = m__getObj(obj_iffeature_ptr_t); if (iffptr == NULL) { return NULL; } memset(iffptr, 0x0, sizeof(obj_iffeature_ptr_t)); iffptr->iffeature = iff; return iffptr; } /******************************************************************** * FUNCTION obj_free_iffeature_ptr * * Free an obj_iffeature_ptr_t struct * * INPUTS: * iffptr == struct to free *********************************************************************/ void obj_free_iffeature_ptr (obj_iffeature_ptr_t *iffptr) { if (iffptr == NULL) { return; } m__free(iffptr); } /******************************************************************** * Get first if-feature pointer * * \param obj the obj_template to check * \return pointer to first entry or NULL if none *********************************************************************/ obj_iffeature_ptr_t * obj_first_iffeature_ptr (obj_template_t *obj) { assert(obj && "obj is NULL" ); obj_iffeature_ptr_t *iffptr = (obj_iffeature_ptr_t *) dlq_firstEntry(&obj->inherited_iffeatureQ); return iffptr; } /* obj_first_iffeature_ptr */ /******************************************************************** * Get the next if-feature pointer * * \param iffptr the current iffeature ptr struct * \return pointer to next entry or NULL if none *********************************************************************/ obj_iffeature_ptr_t * obj_next_iffeature_ptr (obj_iffeature_ptr_t *iffptr) { assert(iffptr && "iffptr is NULL" ); obj_iffeature_ptr_t *nextptr = (obj_iffeature_ptr_t *) dlq_nextEntry(iffptr); return nextptr; } /* obj_next_iffeature_ptr */ /******************************************************************** * FUNCTION obj_new_xpath_ptr * * Malloc and initialize a new obj_xpath_ptr_t struct * * INPUTS: * xpath == Xpath PCB to point at * RETURNS: * malloced struct or NULL if memory error *********************************************************************/ obj_xpath_ptr_t * obj_new_xpath_ptr (struct xpath_pcb_t_ *xpath) { obj_xpath_ptr_t *xptr = m__getObj(obj_xpath_ptr_t); if (xptr == NULL) { return NULL; } memset(xptr, 0x0, sizeof(obj_xpath_ptr_t)); xptr->xpath = xpath; return xptr; } /******************************************************************** * FUNCTION obj_free_xpath_ptr * * Free an obj_xpath_ptr_t struct * * INPUTS: * xptr == struct to free *********************************************************************/ void obj_free_xpath_ptr (obj_xpath_ptr_t *xptr) { if (xptr == NULL) { return; } m__free(xptr); } /******************************************************************** * Get first xpath pointer struct * * \param obj the obj_template to check * \return pointer to first entry or NULL if none *********************************************************************/ obj_xpath_ptr_t * obj_first_xpath_ptr (obj_template_t *obj) { assert(obj && "obj is NULL" ); obj_xpath_ptr_t *xptr = (obj_xpath_ptr_t *) dlq_firstEntry(&obj->inherited_whenQ); return xptr; } /* obj_first_xpath_ptr */ /******************************************************************** * Get the next xpath pointer struct * * \param xptr the current xpath ptr struct * \return pointer to next entry or NULL if none *********************************************************************/ obj_xpath_ptr_t * obj_next_xpath_ptr (obj_xpath_ptr_t *xptr) { assert(xptr && "xptr is NULL" ); obj_xpath_ptr_t *nextptr = (obj_xpath_ptr_t *)dlq_nextEntry(xptr); return nextptr; } /* obj_next_iffeature_ptr */ /* END obj.c */ yuma123_2.14/netconf/src/ncx/ncx_feature.c0000664000175000017500000011671514770023131020620 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: ncx_feature.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17feb10 abb begun; split out from ncx.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "ncx_appinfo.h" #include "ncx_feature.h" #include "ncxconst.h" #include "status.h" #include "typ.h" #include "val.h" #include "xml_util.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* feature code entry */ typedef struct feature_entry_t_ { dlq_hdr_t qhdr; xmlChar *modname; xmlChar *feature; ncx_feature_code_t code; // deprecated; not used boolean code_set; // deprecated; not used boolean enable; boolean enable_set; } feature_entry_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /* the default code generation mode for yangdump * related to YANG features */ static ncx_feature_code_t feature_code_default; /* the default mode for enabling or disabling YANG features */ static boolean feature_enable_default; /* Q of feature_entry_t parameters */ static dlq_hdr_t feature_entryQ; static boolean feature_init_done = FALSE; /******************************************************************** * FUNCTION split_feature_string * * Split a feature string into its 2 parts * modname:feature * * INPUTS: * featstr == feature string parm to split * modnamelen == address of return module name length * * OUTPUTS: * *modnamelen == module name length * * RETURNS: * status *********************************************************************/ static status_t split_feature_string (const xmlChar *featstr, uint32 *modnamelen) { const xmlChar *str, *found; found = NULL; str = featstr; *modnamelen = 0; while (*str) { if (*str == ':') { if (found != NULL) { return ERR_NCX_INVALID_VALUE; } else { found = str; } } str++; } if (found) { *modnamelen = (uint32)(found - featstr); return NO_ERR; } else { return ERR_NCX_INVALID_VALUE; } } /* split_feature_string */ /******************************************************************** * FUNCTION free_feature_entry * * Free a feature_entry_t * * INPUTS: * feature_entry == feature entry struct to free *********************************************************************/ static void free_feature_entry (feature_entry_t *feature_entry) { if (feature_entry->modname) { m__free(feature_entry->modname); } if (feature_entry->feature) { m__free(feature_entry->feature); } m__free(feature_entry); } /* free_feature_entry */ /******************************************************************** * FUNCTION new_feature_entry * * Create a feature_entry_t * * INPUTS: * featstr == feature string parm to use *********************************************************************/ static feature_entry_t * new_feature_entry (const xmlChar *featstr) { uint32 len = 0; boolean splitdone = FALSE; status_t res = split_feature_string(featstr, &len); if (res == NO_ERR) { splitdone = TRUE; } feature_entry_t *feature_entry = m__getObj(feature_entry_t); if (feature_entry == NULL) { return NULL; } memset(feature_entry, 0x0, sizeof(feature_entry_t)); if (splitdone) { feature_entry->modname = xml_strndup(featstr, len); if (feature_entry->modname == NULL) { free_feature_entry(feature_entry); return NULL; } feature_entry->feature = xml_strdup(&featstr[len+1]); if (feature_entry->feature == NULL) { free_feature_entry(feature_entry); return NULL; } } else { feature_entry->feature = xml_strdup(featstr); if (feature_entry->feature == NULL) { free_feature_entry(feature_entry); return NULL; } } return feature_entry; } /* new_feature_entry */ /******************************************************************** * FUNCTION new_feature_entry2 * * Create a feature_entry_t * * INPUTS: * modname == module name to use * name == feature name to use *********************************************************************/ static feature_entry_t * new_feature_entry2 (const xmlChar *modname, const xmlChar *name) { feature_entry_t *feature_entry = m__getObj(feature_entry_t); if (feature_entry == NULL) { return NULL; } memset(feature_entry, 0x0, sizeof(feature_entry_t)); feature_entry->modname = xml_strdup(modname); if (feature_entry->modname == NULL) { free_feature_entry(feature_entry); return NULL; } feature_entry->feature = xml_strdup(name); if (feature_entry->feature == NULL) { free_feature_entry(feature_entry); return NULL; } return feature_entry; } /* new_feature_entry2 */ /******************************************************************** * FUNCTION find_feature_entry * * Find a feature_entry_t * * INPUTS: * featstr == feature string parm to use * featQ == Q of feature_entry_t to use * * RETURNS: * pointer to found entry or NULL if not found *********************************************************************/ static feature_entry_t * find_feature_entry (const xmlChar *featstr, dlq_hdr_t *featQ) { uint32 len = 0; boolean splitdone = FALSE; status_t res = split_feature_string(featstr, &len); if (res == NO_ERR) { splitdone = TRUE; } feature_entry_t *feature_entry = (feature_entry_t *)dlq_firstEntry(featQ); for (; feature_entry != NULL; feature_entry = (feature_entry_t *)dlq_nextEntry(feature_entry)) { if (splitdone && feature_entry->modname) { /* match the module name */ uint32 len2 = xml_strlen(feature_entry->modname); if (len != len2) { continue; } if (xml_strncmp(feature_entry->modname, featstr, len)) { continue; } } /* match the feature name */ if (splitdone) { if (xml_strcmp(feature_entry->feature, &featstr[len+1])) { continue; } } else { if (xml_strcmp(feature_entry->feature, featstr)) { continue; } } return feature_entry; } return NULL; } /* find_feature_entry */ /******************************************************************** * FUNCTION find_feature_entry2 * * Find a feature_entry_t * * INPUTS: * modname == module name * feature == feature name * featQ == Q of feature_entry_t to use * * RETURNS: * pointer to found and removed entry or NULL if not found *********************************************************************/ static feature_entry_t * find_feature_entry2 (const xmlChar *modname, const xmlChar *feature, dlq_hdr_t *featQ) { feature_entry_t *feature_entry; for (feature_entry = (feature_entry_t *)dlq_firstEntry(featQ); feature_entry != NULL; feature_entry = (feature_entry_t *)dlq_nextEntry(feature_entry)) { /* match the module name */ if (feature_entry->modname && modname && xml_strcmp(feature_entry->modname, modname)) { continue; } /* match the feature name */ if (xml_strcmp(feature_entry->feature, feature)) { continue; } return feature_entry; } return NULL; } /* find_feature_entry2 */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION ncx_feature_init * * Init the ncx_feature module * * INPUTS: * none * RETURNS: * none *********************************************************************/ void ncx_feature_init (void) { if (feature_init_done) { SET_ERROR(ERR_INTERNAL_INIT_SEQ); return; } feature_code_default = NCX_FEATURE_CODE_DYNAMIC; feature_enable_default = TRUE; dlq_createSQue(&feature_entryQ); feature_init_done = TRUE; } /* ncx_feature_init */ /******************************************************************** * FUNCTION ncx_feature_cleanup * * Cleanup the ncx_feature module * * INPUTS: * none * RETURNS: * none *********************************************************************/ void ncx_feature_cleanup (void) { feature_entry_t *feature_entry; if (!feature_init_done) { return; } while (!dlq_empty(&feature_entryQ)) { feature_entry = (feature_entry_t *)dlq_deque(&feature_entryQ); free_feature_entry(feature_entry); } feature_init_done = FALSE; } /* ncx_feature_cleanup */ /******************************************************************** * FUNCTION ncx_new_iffeature * * Get a new ncx_iffeature_t struct * * INPUTS: * none * RETURNS: * pointer to a malloced ncx_iffeature_t struct, * or NULL if malloc error *********************************************************************/ ncx_iffeature_t * ncx_new_iffeature (void) { ncx_iffeature_t *iff; iff = m__getObj(ncx_iffeature_t); if (!iff) { return NULL; } memset(iff, 0x0, sizeof(ncx_iffeature_t)); return iff; } /* ncx_new_iffeature */ /******************************************************************** * FUNCTION ncx_free_iffeature * * Free a malloced ncx_iffeature_t struct * * INPUTS: * iff == struct to free * *********************************************************************/ void ncx_free_iffeature (ncx_iffeature_t *iff) { #ifdef DEBUG if (!iff) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (iff->prefix) { m__free(iff->prefix); } if (iff->name) { m__free(iff->name); } if (iff->expr) { m__free(iff->expr); } m__free(iff); } /* ncx_free_iffeature */ /******************************************************************** * FUNCTION ncx_clone_iffeature * * Clone a new ncx_iffeature_t struct * * INPUTS: * srciff == ifffeature struct to clone * RETURNS: * pointer to a malloced ncx_iffeature_t struct, * or NULL if malloc error *********************************************************************/ ncx_iffeature_t * ncx_clone_iffeature (ncx_iffeature_t *srciff) { ncx_iffeature_t *iff; iff = m__getObj(ncx_iffeature_t); if (!iff) { return NULL; } memset(iff, 0x0, sizeof(ncx_iffeature_t)); if (srciff->prefix) { iff->prefix = xml_strdup(srciff->prefix); if (iff->prefix == NULL) { ncx_free_iffeature(iff); return NULL; } } if (srciff->name) { iff->name = xml_strdup(srciff->name); if (iff->name == NULL) { ncx_free_iffeature(iff); return NULL; } } if (srciff->expr) { iff->expr = xml_strdup(srciff->expr); if (iff->expr == NULL) { ncx_free_iffeature(iff); return NULL; } } iff->feature = srciff->feature; ncx_set_error(&iff->tkerr, srciff->tkerr.mod, srciff->tkerr.linenum, srciff->tkerr.linepos); //iff->seen not set return iff; } /* ncx_clone_iffeature */ /******************************************************************** * FUNCTION ncx_clean_iffeatureQ * * Clean a Q of malloced ncx_iffeature_t struct * * INPUTS: * iffeatureQ == address of Q to clean * *********************************************************************/ void ncx_clean_iffeatureQ (dlq_hdr_t *iffeatureQ) { ncx_iffeature_t *iff; #ifdef DEBUG if (!iffeatureQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (!dlq_empty(iffeatureQ)) { iff = (ncx_iffeature_t *)dlq_deque(iffeatureQ); ncx_free_iffeature(iff); } } /* ncx_clean_iffeatureQ */ /******************************************************************** * FUNCTION ncx_find_iffeature_1dot1 * * Search a Q of ncx_iffeature_t structs for a match * expr is an alternative introduced in YANG 1.1 * with logical expression with or,and and not * * INPUTS: * iffeatureQ == address of Q to search * prefix == prefix to check for * a NULL value indicates the current module * name == feature name string to find * expr == feature expr string to find * a NULL value indicates simple if-feature without logical expression * when not NULL prefix and name are NULL *********************************************************************/ ncx_iffeature_t * ncx_find_iffeature_1dot1 (dlq_hdr_t *iffeatureQ, const xmlChar *prefix, const xmlChar *name, const xmlChar *expr, const xmlChar *modprefix) { ncx_iffeature_t *iff; #ifdef DEBUG if (!iffeatureQ || (!name && !expr)) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (iff = (ncx_iffeature_t *) dlq_firstEntry(iffeatureQ); iff != NULL; iff = (ncx_iffeature_t *)dlq_nextEntry(iff)) { /* check if expr fields the same */ if (iff->expr && !xml_strcmp(iff->expr, expr)) { return iff; } /* check if name fields the same */ if (iff->name && !xml_strcmp(iff->name, name)) { /* check if prefix fields reference * different modules, if set or implied */ if (!ncx_prefix_different(prefix, iff->prefix, modprefix)) { return iff; } } } return NULL; } /* ncx_find_iffeature_1dot1 */ /******************************************************************** * FUNCTION ncx_find_iffeature * * Search a Q of ncx_iffeature_t structs for a match * * INPUTS: * iffeatureQ == address of Q to search * prefix == prefix to check for * a NULL value indicates the current module * name == feature name string to find *********************************************************************/ ncx_iffeature_t * ncx_find_iffeature (dlq_hdr_t *iffeatureQ, const xmlChar *prefix, const xmlChar *name, const xmlChar *modprefix) { return ncx_find_iffeature_1dot1(iffeatureQ, prefix, name, NULL, modprefix); } /* ncx_find_iffeature */ /******************************************************************** * FUNCTION ncx_resolve_iffeatureQ * * Check the Q of if-feature statements for the specified object * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * obj == object to check * * RETURNS:for if-feature statementfor if-feature statementfor if-feature statementfor if-feature statementfor if-feature statementfor if-feature statementfor if-feature statementfor if-feature statementfor if-feature statementfor if-feature statementfor if-feature statementfor if-feature statement * status of the operation *********************************************************************/ status_t ncx_resolve_iffeatureQ (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *name, dlq_hdr_t *iffeatureQ) { ncx_feature_t *testfeature; ncx_iffeature_t *iff; status_t res, retres; boolean errdone; retres = NO_ERR; /* check if there are any if-feature statements inside * this element that need to be resolved */ for (iff = (ncx_iffeature_t *) dlq_firstEntry(iffeatureQ); iff != NULL; iff = (ncx_iffeature_t *)dlq_nextEntry(iff)) { testfeature = NULL; errdone = FALSE; res = NO_ERR; if (iff->expr) { continue; } if (iff->prefix && xml_strcmp(iff->prefix, mod->prefix)) { /* find the feature in another module */ res = yang_find_imp_feature(pcb, tkc, mod, iff->prefix, iff->name, &iff->tkerr, &testfeature); if (res != NO_ERR) { retres = res; errdone = TRUE; } } else { testfeature = ncx_find_feature(mod, iff->name); } if (!testfeature && !errdone) { log_error("\nError: Feature '%s' not found " "for if-feature statement in %s", iff->name, name); res = retres = set_tkc_error( tkc, mod, &iff->tkerr, ERR_NCX_DEF_NOT_FOUND ); } if (testfeature) { iff->feature = testfeature; } } /* check the feature mismatch corner cases later, * after the OBJ_FL_KEY flags have been set */ return retres; } /* resolve_iffeatureQ */ /******************************************************************** * FUNCTION ncx_new_feature * * Get a new ncx_feature_t struct * * INPUTS: * none * RETURNS: * pointer to a malloced ncx_feature_t struct, * or NULL if malloc error *********************************************************************/ ncx_feature_t * ncx_new_feature (void) { ncx_feature_t *feature; feature = m__getObj(ncx_feature_t); if (!feature) { return NULL; } memset(feature, 0x0, sizeof(ncx_feature_t)); dlq_createSQue(&feature->iffeatureQ); dlq_createSQue(&feature->appinfoQ); /*** setting feature enabled as the default *** the agent code needs to adjust this *** with agt_enable_feature or *** agt_disable_feature() if needed ***/ feature->enabled = feature_enable_default; feature->code = feature_code_default; return feature; } /* ncx_new_feature */ /******************************************************************** * FUNCTION ncx_free_feature * * Free a malloced ncx_feature_t struct * * INPUTS: * feature == struct to free * *********************************************************************/ void ncx_free_feature (ncx_feature_t *feature) { #ifdef DEBUG if (!feature) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (feature->name) { m__free(feature->name); } if (feature->descr) { m__free(feature->descr); } if (feature->ref) { m__free(feature->ref); } ncx_clean_iffeatureQ(&feature->iffeatureQ); ncx_clean_appinfoQ(&feature->appinfoQ); m__free(feature); } /* ncx_free_feature */ /******************************************************************** * FUNCTION ncx_find_feature * * Find a ncx_feature_t struct in the module and perhaps * any of its visible submodules * * INPUTS: * mod == module to search * name == feature name to find * * RETURNS: * pointer to found feature or NULL if not found *********************************************************************/ ncx_feature_t * ncx_find_feature (ncx_module_t *mod, const xmlChar *name) { ncx_feature_t *feature; dlq_hdr_t *que; yang_node_t *node; ncx_include_t *inc; #ifdef DEBUG if (!mod || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif feature = ncx_find_feature_que(&mod->featureQ, name); if (feature) { return feature; } que = ncx_get_allincQ(mod); /* check all the submodules, but only the ones visible * to this module or submodule */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { /* get the real submodule struct */ if (!inc->submod) { node = yang_find_node(que, inc->submodule, inc->revision); if (node) { inc->submod = node->submod; } if (!inc->submod) { /* include not found or errors in it */ continue; } } /* check the feature Q in this submodule */ feature = ncx_find_feature_que(&inc->submod->featureQ, name); if (feature) { return feature; } } return NULL; } /* ncx_find_feature */ /******************************************************************** * FUNCTION ncx_find_feature_que * * Find a ncx_feature_t struct in the specified Q * * INPUTS: * featureQ == Q of ncx_feature_t to search * name == feature name to find * * RETURNS: * pointer to found feature or NULL if not found *********************************************************************/ ncx_feature_t * ncx_find_feature_que (dlq_hdr_t *featureQ, const xmlChar *name) { ncx_feature_t *feature; #ifdef DEBUG if (!featureQ || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (feature = (ncx_feature_t *)dlq_firstEntry(featureQ); feature != NULL; feature = (ncx_feature_t *)dlq_nextEntry(feature)) { if (!xml_strcmp(feature->name, name)) { return feature; } } return NULL; } /* ncx_find_feature_que */ /******************************************************************** * FUNCTION ncx_find_feature_all * * Find a ncx_feature_t struct in the module and perhaps * any of its submodules * * INPUTS: * mod == module to search * name == feature name to find * * RETURNS: * pointer to found feature or NULL if not found *********************************************************************/ ncx_feature_t * ncx_find_feature_all (ncx_module_t *mod, const xmlChar *name) { ncx_feature_t *feature; dlq_hdr_t *que; yang_node_t *node; #ifdef DEBUG if (!mod || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif feature = ncx_find_feature_que(&mod->featureQ, name); if (feature) { return feature; } que = ncx_get_allincQ(mod); /* check all the submodules */ for (node = (yang_node_t *)dlq_firstEntry(que); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { /* check the feature Q in this submodule */ feature = ncx_find_feature_que(&node->submod->featureQ, name); if (feature) { return feature; } } } return NULL; } /* ncx_find_feature_all */ /******************************************************************** * FUNCTION ncx_for_all_features * * Execute a callback function for all features in this module * and any submodules * * INPUTS: * mod == module to search for features * cbfn == feature callback function * cookie == cookie value to pass to each iteration of the callback * enabledonly == TRUE if only callbacks for enabled features * FALSE if all features should invoke callbacks *********************************************************************/ void ncx_for_all_features (const ncx_module_t *mod, ncx_feature_cbfn_t cbfn, void *cookie, boolean enabledonly) { ncx_feature_t *feature; const dlq_hdr_t *que; yang_node_t *node; ncx_include_t *inc; boolean keepgoing; #ifdef DEBUG if (!mod || !cbfn) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif keepgoing = TRUE; for (feature = (ncx_feature_t *)dlq_firstEntry(&mod->featureQ); feature != NULL && keepgoing; feature = (ncx_feature_t *)dlq_nextEntry(feature)) { if (enabledonly && !ncx_feature_enabled(feature)) { continue; } keepgoing = (*cbfn)(mod, feature, cookie); } que = ncx_get_const_allincQ(mod); /* check all the submodules, but only the ones visible * to this module or submodule */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL && keepgoing; inc = (ncx_include_t *)dlq_nextEntry(inc)) { /* get the real submodule struct */ if (!inc->submod) { node = yang_find_node(que, inc->submodule, inc->revision); if (node) { inc->submod = node->submod; } if (!inc->submod) { /* include not found or errors in it */ continue; } } for (feature = (ncx_feature_t *) dlq_firstEntry(&inc->submod->featureQ); feature != NULL && keepgoing; feature = (ncx_feature_t *)dlq_nextEntry(feature)) { if (enabledonly && !ncx_feature_enabled(feature)) { continue; } keepgoing = (*cbfn)(mod, feature, cookie); } } } /* ncx_for_all_features */ /******************************************************************** * FUNCTION ncx_feature_count * * Get the total feature count for this module * and any submodules * * INPUTS: * mod == module to search for features * enabledonly == TRUE to only count enabled features * FALSE to count all features * * RETURNS: * total number of features *********************************************************************/ uint32 ncx_feature_count (const ncx_module_t *mod, boolean enabledonly) { const ncx_feature_t *feature; const yang_node_t *node; const dlq_hdr_t *que; ncx_include_t *inc; uint32 count; #ifdef DEBUG if (!mod) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif count = 0; for (feature = (const ncx_feature_t *)dlq_firstEntry(&mod->featureQ); feature != NULL; feature = (const ncx_feature_t *)dlq_nextEntry(feature)) { if (enabledonly && !ncx_feature_enabled(feature)) { continue; } count++; } que = ncx_get_const_allincQ(mod); /* check all the submodules, but only the ones visible * to this module or submodule */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { /* get the real submodule struct */ if (!inc->submod) { node = yang_find_node(que, inc->submodule, inc->revision); if (node) { inc->submod = node->submod; } if (!inc->submod) { /* include not found or errors in it */ continue; } } for (feature = (const ncx_feature_t *) dlq_firstEntry(&inc->submod->featureQ); feature != NULL; feature = (const ncx_feature_t *)dlq_nextEntry(feature)) { if (enabledonly && !ncx_feature_enabled(feature)) { continue; } count++; } } return count; } /* ncx_feature_count */ /******************************************************************** * FUNCTION ncx_feature_enabled * * Check if the specified feature and any referenced * if-features are enabled * * INPUTS: * feature == feature to check * * RETURNS: * TRUE if feature is completely enabled * FALSE if feature is not enabled, or partially enabled *********************************************************************/ boolean ncx_feature_enabled (const ncx_feature_t *feature) { const ncx_iffeature_t *iffeature; #ifdef DEBUG if (!feature) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (!feature->enabled) { return FALSE; } /* make sure all nested if-features are also enabled */ for (iffeature = (const ncx_iffeature_t *) dlq_firstEntry(&feature->iffeatureQ); iffeature != NULL; iffeature = (const ncx_iffeature_t *) dlq_nextEntry(iffeature)) { if (!iffeature->feature) { /* feature was not found, so call it disabled */ return FALSE; } if (!ncx_feature_enabled(iffeature->feature)) { return FALSE; } } return TRUE; } /* ncx_feature_enabled */ /******************************************************************** * FUNCTION ncx_feature_enabled_str * * Check if the specified feature and any referenced * if-features are enabled * * INPUTS: * modname == name of module to search * revision == module revision string (may be NULL) * name == feature name to find * RETURNS: * TRUE if feature is completely enabled * FALSE if feature is not enabled, or partially enabled *********************************************************************/ boolean ncx_feature_enabled_str (const xmlChar *modname, const xmlChar *revision, const xmlChar *name) { #ifdef DEBUG if (!modname || !name) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif ncx_module_t *mod = ncx_find_module(modname, revision); if (mod == NULL) { return FALSE; } const ncx_feature_t *feature = ncx_find_feature(mod, name); if (feature == NULL) { return FALSE; } return ncx_feature_enabled(feature); } /* ncx_feature_enabled_str */ /******************************************************************** * FUNCTION ncx_set_feature_enable_default * * Set the feature_enable_default flag * * INPUTS: * flag == feature enabled flag value *********************************************************************/ void ncx_set_feature_enable_default (boolean flag) { feature_enable_default = flag; } /* ncx_set_feature_enabled_default */ /******************************************************************** * FUNCTION ncx_set_feature_code_default * * Set the feature_code_default enumeration * * !!! THIS FUNCTION IS DEPRECATED!!! * !!! The --feature-code and --feature-code-default parameters are ignored * !!! Feature code generation is not controlled by this parameter * * INPUTS: * code == feature code value *********************************************************************/ void ncx_set_feature_code_default (ncx_feature_code_t code) { feature_code_default = code; } /* ncx_set_feature_code_default */ /******************************************************************** * FUNCTION ncx_set_feature_code_entry * * Create or set a feature_entry struct for the specified * feature code parameter * * !!! THIS FUNCTION IS DEPRECATED!!! * !!! The --feature-code and --feature-code-default parameters are ignored * !!! Feature code generation is not controlled by this parameter * * INPUTS: * featstr == feature parameter string * featcode == ncx_feature_code_t enumeration to set * * RETURNS: * status *********************************************************************/ status_t ncx_set_feature_code_entry (const xmlChar *featstr, ncx_feature_code_t featcode) { feature_entry_t *fentry; status_t res; uint32 cnt; #ifdef DEBUG if (featstr == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; fentry = find_feature_entry(featstr, &feature_entryQ); if (fentry != NULL) { if (fentry->code_set) { if (fentry->code != featcode) { log_error("\nError: feature '%s' already set with " "conflicting value", featstr); res = ERR_NCX_INVALID_VALUE; } else { log_info("\nFeature '%s' already set with " "same value", featstr); } } else { fentry->code_set = TRUE; fentry->code = featcode; } } else { cnt = 0; res = split_feature_string(featstr, &cnt); if (res == NO_ERR) { fentry = new_feature_entry(featstr); if (fentry == NULL) { res = ERR_INTERNAL_MEM; } else { fentry->code_set = TRUE; fentry->code = featcode; dlq_enque(fentry, &feature_entryQ); } } } return res; } /* ncx_set_feature_code_entry */ /******************************************************************** * FUNCTION ncx_set_feature_enable_entry * * Create or set a feature_entry struct for the specified * feature enabled parameter * * Called from CLI/conf handler code * * INPUTS: * featstr == feature parameter string * flag == enabled flag * * RETURNS: * status *********************************************************************/ status_t ncx_set_feature_enable_entry (const xmlChar *featstr, boolean flag) { #ifdef DEBUG if (featstr == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif status_t res = NO_ERR; feature_entry_t *fentry = find_feature_entry(featstr, &feature_entryQ); if (fentry != NULL) { if (fentry->enable_set) { if (fentry->enable != flag) { log_info("\nFeature '%s' already %s so ignoring new value", (flag) ? "disabled" : "enabled", featstr); res = ERR_NCX_INVALID_VALUE; } } else { fentry->enable_set = TRUE; fentry->enable = flag; } } else { fentry = new_feature_entry(featstr); if (fentry == NULL) { res = ERR_INTERNAL_MEM; } else { fentry->enable_set = TRUE; fentry->enable = flag; dlq_enque(fentry, &feature_entryQ); } } return res; } /* ncx_set_feature_enable_entry */ /******************************************************************** * FUNCTION ncx_set_feature_enable * * Create or set a feature_entry struct for the specified * feature enabled parameter * * Called from SIL init code * * INPUTS: * modname == name of module defining the feature * name == feature name * flag == feature enabled flag * * RETURNS: * status *********************************************************************/ status_t ncx_set_feature_enable (const xmlChar *modname, const xmlChar *name, boolean flag) { assert( modname && "modname is NULL!" ); assert( name && "modname is NULL!" ); status_t res = NO_ERR; feature_entry_t *fentry = find_feature_entry2(modname, name, &feature_entryQ); if (fentry != NULL) { if (fentry->enable_set) { if (fentry->enable != flag) { if (flag) { /* SIL enabled, so previous CLI disable is allowed */ log_debug("\nFeature '%s' already disabled from CLI, " "ignoring SIL disable", name); } else { /* SIL disabled so override CLI enable */ log_info("\nFeature '%s' disabled in SIL, " "overriding CLI enable", name); fentry->enable = FALSE; } } /* else same value so ignore */ } else { fentry->enable_set = TRUE; fentry->enable = flag; } } else { fentry = new_feature_entry2(modname, name); if (fentry == NULL) { res = ERR_INTERNAL_MEM; } else { fentry->enable_set = TRUE; fentry->enable = flag; dlq_enque(fentry, &feature_entryQ); } } return res; } /* ncx_set_feature_enable */ /******************************************************************** * FUNCTION ncx_set_feature_parms * * Check if any feature parameters were set for the specified * feature struct * * INPUTS: * feature == feature struct to check * * OUTPUTS: * feature->code and/or feature->enabled may be set *********************************************************************/ void ncx_set_feature_parms (ncx_feature_t *feature) { feature_entry_t *fentry; #ifdef DEBUG if (feature == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (feature->name == NULL || feature->tkerr.mod == NULL || ncx_get_modname(feature->tkerr.mod) == NULL) { /* feature not filled in properly */ return; } fentry = find_feature_entry2(ncx_get_modname(feature->tkerr.mod), feature->name, &feature_entryQ); if (fentry != NULL) { if (fentry->code_set) { feature->code = fentry->code; } if (fentry->enable_set) { feature->enabled = fentry->enable; } } } /* ncx_set_feature_parms */ /* END file ncx_feature.c */ yuma123_2.14/netconf/src/ncx/ncx_num.c0000664000175000017500000017355714770023131017773 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: ncx_num.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17feb10 abb begun; split out from ncx.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #ifdef HAS_FLOAT #include #include #endif #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_num #include "ncx_num.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ #ifdef HAS_FLOAT /******************************************************************** * FUNCTION remove_trailing_zero_count * * Get the number of trailing zeros and maybe the decimal point too * * INPUTS: * buff == number buffer to check * * RETURNS: * number of chars to remove from the end of string *********************************************************************/ static int32 remove_trailing_zero_count (const xmlChar *buff) { int32 len, newlen; const xmlChar *str; len = strlen((const char *)buff); if (!len) { return 0; } str = &buff[len-1]; while (str >= buff && *str == '0') { str--; } if (*str == '.') { str--; } newlen = (int32)(str - buff) + 1; return len - newlen; } /* remove_trailing_zero_count */ #endif /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION ncx_init_num * * Init a ncx_num_t struct * * INPUTS: * num == number to initialize *********************************************************************/ void ncx_init_num (ncx_num_t *num) { #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif memset(num, 0x0, sizeof(ncx_num_t)); } /* ncx_init_num */ /******************************************************************** * FUNCTION ncx_clean_num * * Scrub the memory in a ncx_num_t by freeing all * the sub-fields. DOES NOT free the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * btyp == base type of number * num == ncx_num_t data structure to clean *********************************************************************/ void ncx_clean_num (ncx_btype_t btyp, ncx_num_t *num) { #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* clean the num->union, depending on base type */ switch (btyp) { case NCX_BT_NONE: case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: memset(num, 0x0, sizeof(ncx_num_t)); break; case NCX_BT_DECIMAL64: num->dec.val = 0; num->dec.digits = 0; num->dec.zeroes = 0; break; case NCX_BT_FLOAT64: num->d = 0; break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* ncx_clean_num */ /******************************************************************** * FUNCTION ncx_compare_nums * * Compare 2 ncx_num_t union contents * * INPUTS: * num1 == first number * num2 == second number * btyp == expected data type (NCX_BT_INT, UINT, REAL) * RETURNS: * -1 if num1 is < num2 * 0 if num1 == num2 * 1 if num1 is > num2 *********************************************************************/ int32 ncx_compare_nums (const ncx_num_t *num1, const ncx_num_t *num2, ncx_btype_t btyp) { int64 temp1, temp2; uint8 diffdigits; #ifdef DEBUG if (!num1 || !num2) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: if (num1->i < num2->i) { return -1; } else if (num1->i == num2->i) { return 0; } else { return 1; } case NCX_BT_INT64: if (num1->l < num2->l) { return -1; } else if (num1->l == num2->l) { return 0; } else { return 1; } case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: if (num1->u < num2->u) { return -1; } else if (num1->u == num2->u) { return 0; } else { return 1; } case NCX_BT_UINT64: if (num1->ul < num2->ul) { return -1; } else if (num1->ul == num2->ul) { return 0; } else { return 1; } case NCX_BT_DECIMAL64: /* check the base parts first */ temp1 = ncx_get_dec64_base(num1); temp2 = ncx_get_dec64_base(num2); if (temp1 < temp2) { return -1; } else if (temp1 == temp2) { /* check if any leading zeroes */ if (temp1 == 0) { if (num1->dec.zeroes < num2->dec.zeroes) { return 1; } else if (num1->dec.zeroes > num2->dec.zeroes) { return -1; } /* else need to compare fraction parts */ } /* check fraction parts next */ temp1 = ncx_get_dec64_fraction(num1); temp2 = ncx_get_dec64_fraction(num2); /* normalize these numbers to compare them */ if (num1->dec.digits > num2->dec.digits) { diffdigits = num1->dec.digits - num2->dec.digits; temp2 *= (10 * diffdigits); } else if (num1->dec.digits < num2->dec.digits) { diffdigits = num2->dec.digits - num1->dec.digits; temp1 *= (10 * diffdigits); } if (temp1 < temp2) { return -1; } else if (temp1 == temp2) { return 0; } else { return 1; } } else { return 1; } case NCX_BT_FLOAT64: if (num1->d < num2->d) { return -1; } else if (num1->d == num2->d) { return 0; } else { return 1; } default: SET_ERROR(ERR_INTERNAL_VAL); return 0; } /*NOTREACHED*/ } /* ncx_compare_nums */ /******************************************************************** * FUNCTION ncx_set_num_min * * Set a number to the minimum value for its type * * INPUTS: * num == number to set * btyp == expected data type * *********************************************************************/ void ncx_set_num_min (ncx_num_t *num, ncx_btype_t btyp) { #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif switch (btyp) { case NCX_BT_INT8: num->i = NCX_MIN_INT8; break; case NCX_BT_INT16: num->i = NCX_MIN_INT16; break; case NCX_BT_INT32: num->i = NCX_MIN_INT; break; case NCX_BT_INT64: num->l = NCX_MIN_LONG; break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: num->u = NCX_MIN_UINT; break; case NCX_BT_UINT64: num->ul = NCX_MIN_ULONG; break; case NCX_BT_DECIMAL64: num->dec.val = NCX_MIN_LONG; num->dec.zeroes = 0; break; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT num->d = -INFINITY; #else num->d = NCX_MIN_LONG; #endif break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* ncx_set_num_min */ /******************************************************************** * FUNCTION ncx_set_num_max * * Set a number to the maximum value for its type * * INPUTS: * num == number to set * btyp == expected data type * *********************************************************************/ void ncx_set_num_max (ncx_num_t *num, ncx_btype_t btyp) { #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif switch (btyp) { case NCX_BT_INT8: num->i = NCX_MAX_INT8; break; case NCX_BT_INT16: num->i = NCX_MAX_INT16; break; case NCX_BT_INT32: num->i = NCX_MAX_INT; break; case NCX_BT_INT64: num->l = NCX_MAX_LONG; break; case NCX_BT_UINT8: num->u = NCX_MAX_UINT8; break; case NCX_BT_UINT16: num->u = NCX_MAX_UINT16; break; case NCX_BT_UINT32: num->u = NCX_MAX_UINT; break; case NCX_BT_UINT64: num->ul = NCX_MAX_ULONG; break; case NCX_BT_DECIMAL64: num->dec.val = NCX_MAX_LONG; num->dec.zeroes = 0; break; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT num->d = INFINITY; #else num->d = NCX_MAX_LONG-1; #endif break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* ncx_set_num_max */ /******************************************************************** * FUNCTION ncx_set_num_one * * Set a number to one * * INPUTS: * num == number to set * btyp == expected data type * *********************************************************************/ void ncx_set_num_one (ncx_num_t *num, ncx_btype_t btyp) { #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: num->i = 1; break; case NCX_BT_INT64: num->l = 1; break; case NCX_BT_UINT8: num->u = 1; break; case NCX_BT_UINT64: num->ul = 1; break; case NCX_BT_DECIMAL64: num->dec.val = 10 * num->dec.digits; num->dec.zeroes = 0; break; case NCX_BT_FLOAT64: num->d = 1; break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* ncx_set_num_one */ /******************************************************************** * FUNCTION ncx_set_num_zero * * Set a number to zero * * INPUTS: * num == number to set * btyp == expected data type * *********************************************************************/ void ncx_set_num_zero (ncx_num_t *num, ncx_btype_t btyp) { #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: num->i = 0; break; case NCX_BT_INT64: num->l = 0; break; case NCX_BT_UINT8: num->u = 0; break; case NCX_BT_UINT64: num->ul = 0; break; case NCX_BT_DECIMAL64: num->dec.val = 0; num->dec.zeroes = 1; break; case NCX_BT_FLOAT64: num->d = 0; break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* ncx_set_num_zero */ /******************************************************************** * FUNCTION ncx_set_num_nan * * Set a FP number to the Not a Number value * * INPUTS: * num == number to set * btyp == expected data type * *********************************************************************/ void ncx_set_num_nan (ncx_num_t *num, ncx_btype_t btyp) { #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (btyp == NCX_BT_FLOAT64) { #ifdef HAS_FLOAT num->d = NAN; #else num->d = NCX_MAX_LONG; #endif } } /* ncx_set_num_nan */ /******************************************************************** * FUNCTION ncx_num_is_nan * * Check if a FP number is set to the Not a Number value * * INPUTS: * num == number to check * btyp == expected data type * * RETURNS: * TRUE if number is not-a-number (NaN) * FALSE otherwise *********************************************************************/ boolean ncx_num_is_nan (ncx_num_t *num, ncx_btype_t btyp) { #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return TRUE; } #endif if (btyp == NCX_BT_FLOAT64) { #ifdef HAS_FLOAT return (num->d == NAN) ? TRUE : FALSE; #else return (num->d == NCX_MAX_LONG) ? TRUE : FALSE; #endif } return FALSE; } /* ncx_num_is_nan */ /******************************************************************** * FUNCTION ncx_num_zero * * Compare a ncx_num_t to zero * * INPUTS: * num == number to check * btyp == expected data type (e.g., NCX_BT_INT32, NCX_BT_UINT64) * * RETURNS: * TRUE if value is equal to zero * FALSE if value is not equal to zero *********************************************************************/ boolean ncx_num_zero (const ncx_num_t *num, ncx_btype_t btyp) { #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: return (num->i) ? FALSE : TRUE; case NCX_BT_INT64: return (num->l) ? FALSE : TRUE; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: return (num->u) ? FALSE : TRUE; case NCX_BT_UINT64: return (num->ul) ? FALSE : TRUE; case NCX_BT_DECIMAL64: return (num->dec.val == 0) ? TRUE : FALSE; case NCX_BT_FLOAT64: return (num->d == 0) ? TRUE : FALSE; default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* ncx_num_zero */ /******************************************************************** * FUNCTION ncx_convert_num * * Convert a number string to a numeric type * * INPUTS: * numstr == number string * numfmt == NCX_NF_OCTAL, NCX_NF_DEC, NCX_NF_HEX, or NCX_NF_REAL * btyp == desired number type * (e.g., NCX_BT_INT32, NCX_BT_UINT32, NCX_BT_FLOAT64) * val == pointer to ncx_num_t to hold result * * OUTPUTS: * *val == converted number value * * RETURNS: * status *********************************************************************/ status_t ncx_convert_num (const xmlChar *numstr, ncx_numfmt_t numfmt, ncx_btype_t btyp, ncx_num_t *val) { char *err; long l; long long ll; unsigned long ul; unsigned long long ull; #ifdef HAS_FLOAT double d; #endif #ifdef DEBUG if (!numstr || !val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (*numstr == '\0') { return ERR_NCX_INVALID_VALUE; } err = NULL; l = 0; ll = 0; ul = 0; ull = 0; /* check the number format set to don't know */ if (numfmt==NCX_NF_NONE) { numfmt = ncx_get_numfmt(numstr); } switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: switch (numfmt) { case NCX_NF_OCTAL: l = strtol((const char *)numstr, &err, 8); break; case NCX_NF_DEC: l = strtol((const char *)numstr, &err, 10); break; case NCX_NF_HEX: l = strtol((const char *)numstr, &err, 16); break; case NCX_NF_REAL: return ERR_NCX_WRONG_NUMTYP; default: return SET_ERROR(ERR_INTERNAL_VAL); } if (err && *err) { return ERR_NCX_INVALID_NUM; } switch (btyp) { case NCX_BT_INT8: if (l < NCX_MIN_INT8 || l > NCX_MAX_INT8) { return ERR_NCX_NOT_IN_RANGE; } break; case NCX_BT_INT16: if (l < NCX_MIN_INT16 || l > NCX_MAX_INT16) { return ERR_NCX_NOT_IN_RANGE; } break; default: ; } val->i = (int32)l; break; case NCX_BT_INT64: switch (numfmt) { case NCX_NF_OCTAL: ll = strtoll((const char *)numstr, &err, 8); break; case NCX_NF_DEC: ll = strtoll((const char *)numstr, &err, 10); break; case NCX_NF_HEX: ll = strtoll((const char *)numstr, &err, 16); break; case NCX_NF_REAL: return ERR_NCX_WRONG_NUMTYP; default: return SET_ERROR(ERR_INTERNAL_VAL); } if (err && *err) { return ERR_NCX_INVALID_NUM; } val->l = (int64)ll; break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: switch (numfmt) { case NCX_NF_OCTAL: ul = strtoul((const char *)numstr, &err, 8); break; case NCX_NF_DEC: ul = strtoul((const char *)numstr, &err, 10); break; case NCX_NF_HEX: ul = strtoul((const char *)numstr, &err, 16); break; case NCX_NF_REAL: return ERR_NCX_WRONG_NUMTYP; default: return SET_ERROR(ERR_INTERNAL_VAL); } if (err && *err) { return ERR_NCX_INVALID_NUM; } switch (btyp) { case NCX_BT_UINT8: if (ul > NCX_MAX_UINT8) { return ERR_NCX_NOT_IN_RANGE; } break; case NCX_BT_UINT16: if (ul > NCX_MAX_UINT16) { return ERR_NCX_NOT_IN_RANGE; } break; default: ; } if (*numstr == '-') { return ERR_NCX_NOT_IN_RANGE; } val->u = (uint32)ul; break; case NCX_BT_UINT64: switch (numfmt) { case NCX_NF_OCTAL: ull = strtoull((const char *)numstr, &err, 8); break; case NCX_NF_DEC: ull = strtoull((const char *)numstr, &err, 10); break; case NCX_NF_HEX: ull = strtoull((const char *)numstr, &err, 16); break; case NCX_NF_REAL: return ERR_NCX_WRONG_TKTYPE; default: return SET_ERROR(ERR_INTERNAL_VAL); } if (err && *err) { return ERR_NCX_INVALID_NUM; } if (*numstr == '-') { return ERR_NCX_NOT_IN_RANGE; } val->ul = (uint64)ull; break; case NCX_BT_DECIMAL64: return SET_ERROR(ERR_INTERNAL_VAL); case NCX_BT_FLOAT64: #ifdef HAS_FLOAT switch (numfmt) { case NCX_NF_OCTAL: case NCX_NF_DEC: case NCX_NF_REAL: errno = 0; d = strtod((const char *)numstr, &err); if (errno) { return ERR_NCX_INVALID_NUM; } val->d = d; break; case NCX_NF_HEX: return ERR_NCX_WRONG_NUMTYP; default: return SET_ERROR(ERR_INTERNAL_VAL); } #else switch (numfmt) { case NCX_NF_OCTAL: ll = strtoll((const char *)numstr, &err, 8); if (err && *err) { return ERR_NCX_INVALID_NUM; } val->d = (int64)ll; break; case NCX_NF_DEC: ll = strtoll((const char *)numstr, &err, 10); if (err && *err) { return ERR_NCX_INVALID_NUM; } val->d = (int64)ll; break; case NCX_NF_HEX: ll = strtoll((const char *)numstr, &err, 16); if (err && *err) { return ERR_NCX_INVALID_HEXNUM; } val->d = (int64)ll; break; case NCX_NF_REAL: return ERR_NCX_WRONG_NUMTYP; default: return SET_ERROR(ERR_INTERNAL_VAL); } #endif break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return NO_ERR; } /* ncx_convert_num */ /******************************************************************** * FUNCTION ncx_convert_dec64 * * Convert a number string to a decimal64 number * * INPUTS: * numstr == number string * numfmt == number format used * digits == number of fixed-point digits expected * val == pointer to ncx_num_t to hold result * * OUTPUTS: * *val == converted number value * * RETURNS: * status *********************************************************************/ status_t ncx_convert_dec64 (const xmlChar *numstr, ncx_numfmt_t numfmt, uint8 digits, ncx_num_t *val) { const xmlChar *point, *str; char *err; int64 basenum, fracnum, testnum; uint32 numdigits; boolean isneg; uint8 i, lzeroes; xmlChar numbuff[NCX_MAX_NUMLEN]; #ifdef DEBUG if (!numstr || !val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (*numstr == '\0') { val->dec.val = 0; val->dec.digits = digits; return NO_ERR; } err = NULL; point = NULL; basenum = 0; fracnum = 0; isneg = FALSE; lzeroes = 0; /* check the number format set to don't know */ if (numfmt==NCX_NF_NONE) { numfmt = ncx_get_numfmt(numstr); } /* check the number string for plus or minus sign */ str = numstr; if (*str == '+') { str++; } else if (*str == '-') { str++; isneg = TRUE; } while (isdigit((int)*str)) { str++; } /* check if stopped on a decimal point */ if (*str == '.') { /* get just the base part now */ point = str; xml_strncpy(numbuff, numstr, (uint32)(point - numstr)); basenum = strtoll((const char *)numbuff, &err, 10); if (basenum == 0) { /* need to count all the zeroes in the number * since they will get lost in the conversion algorithm * 0.00034 --> 34 0.0034 --> 34 */ lzeroes = 1; /* count the leading 0.xxx */ while (point[lzeroes] == '0') { lzeroes++; } } } else { /* assume the entire string is just a base part * the token parser broke up the string * already so a string concat '123foo' should * not happen here */ switch (numfmt) { case NCX_NF_OCTAL: basenum = strtoll((const char *)numstr, &err, 8); break; case NCX_NF_DEC: case NCX_NF_REAL: basenum = strtoll((const char *)numstr, &err, 10); break; case NCX_NF_HEX: basenum = strtoll((const char *)numstr, &err, 16); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } /* check if strtoll accepted the number string */ if (err && *err) { return ERR_NCX_INVALID_NUM; } } /* check that the number is actually in range */ if (isneg) { testnum = NCX_MIN_LONG; } else { testnum = NCX_MAX_LONG; } /* adjust the test number to the maximum for * the specified number of fraction digits */ for (i = 0; i < digits; i++) { testnum /= 10; } /* check if the base number is OK wrt/ testnum */ if (isneg) { if (basenum < testnum) { return ERR_NCX_DEC64_BASEOVFL; } } else { if (basenum > testnum) { return ERR_NCX_DEC64_BASEOVFL; } } /* check if there is a fraction part entered */ if (point) { fracnum = 0; str = point + 1; while (isdigit((int)*str)) { str++; } numdigits = (uint32)(str - point - 1); /* check if fraction part too big */ if (numdigits > (uint32)digits) { return ERR_NCX_DEC64_FRACOVFL; } if (numdigits) { err = NULL; xml_strncpy(numbuff, point+1, numdigits); fracnum = strtoll((const char *)numbuff, &err, 10); /* check if strtoll accepted the number string */ if (err && *err) { return ERR_NCX_INVALID_NUM; } /* adjust the fraction part will trailing zeros * if the user omitted them */ for (i = numdigits; i < digits; i++) { fracnum *= 10; } if (isneg) { fracnum *= -1; } } } /* encode the base part shifted left 10 * fraction-digits */ if (basenum) { for (i= 0; i < digits; i++) { basenum *= 10; } } /* save the number with the fraction-digits value added in */ val->dec.val = basenum + fracnum; val->dec.digits = digits; val->dec.zeroes = lzeroes; return NO_ERR; } /* ncx_convert_dec64 */ /******************************************************************** * FUNCTION ncx_decode_num * * Handle some sort of number string * * INPUTS: * numstr == number string * btyp == desired number type * retnum == pointer to initialized ncx_num_t to hold result * * OUTPUTS: * *retnum == converted number * * RETURNS: * status of the operation *********************************************************************/ status_t ncx_decode_num (const xmlChar *numstr, ncx_btype_t btyp, ncx_num_t *retnum) { const xmlChar *str; #ifdef DEBUG if (!numstr || !retnum) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* check if this is a hex number */ if (*numstr == '0' && NCX_IS_HEX_CH(*(numstr+1))) { return ncx_convert_num(numstr+2, NCX_NF_HEX, btyp, retnum); } /* check if this is a real number */ str = numstr; while (*str && (*str != '.')) { str++; } if (*str) { return ncx_convert_num(numstr, NCX_NF_REAL, btyp, retnum); } /* check octal number */ if (*numstr == '0' && numstr[1] != '.') { return ncx_convert_num(numstr, NCX_NF_OCTAL, btyp, retnum); } /* else assume this is a decimal number */ return ncx_convert_num(numstr, NCX_NF_DEC, btyp, retnum); } /* ncx_decode_num */ /******************************************************************** * FUNCTION ncx_decode_dec64 * * Handle some sort of decimal64 number string (NCX_BT_DECIMAL64) * * INPUTS: * numstr == number string * digits == number of expected digits for this decimal64 * retnum == pointer to initialized ncx_num_t to hold result * * OUTPUTS: * *retnum == converted number * * RETURNS: * status of the operation *********************************************************************/ status_t ncx_decode_dec64 (const xmlChar *numstr, uint8 digits, ncx_num_t *retnum) { const xmlChar *str; #ifdef DEBUG if (!numstr || !retnum) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* check if this is a hex number */ if (*numstr == '0' && NCX_IS_HEX_CH(*(numstr+1))) { return ncx_convert_dec64(numstr+2, NCX_NF_HEX, digits, retnum); } /* check if this is a real number */ str = numstr; while (*str && (*str != '.')) { str++; } if (*str) { return ncx_convert_dec64(numstr, NCX_NF_REAL, digits, retnum); } /* check octal number */ if (*numstr == '0') { return ncx_convert_dec64(numstr, NCX_NF_OCTAL, digits, retnum); } /* else assume this is a decimal number */ return ncx_convert_dec64(numstr, NCX_NF_DEC, digits, retnum); } /* ncx_decode_dec64 */ /******************************************************************** * FUNCTION ncx_copy_num * * Copy the contents of num1 to num2 * * Supports all NCX numeric types: * NCX_BT_INT* * NCX_BT_UINT* * NCX_BT_DECIMAL64 * NCX_BT_FLOAT64 * * INPUTS: * num1 == first number * num2 == second number * btyp == expected data type (NCX_BT_INT, UINT, REAL) * RETURNS: * status *********************************************************************/ status_t ncx_copy_num (const ncx_num_t *num1, ncx_num_t *num2, ncx_btype_t btyp) { #ifdef DEBUG if (!num1 || !num2) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: num2->i = num1->i; break; case NCX_BT_INT64: num2->l = num1->l; break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: num2->u = num1->u; break; case NCX_BT_UINT64: num2->ul = num1->ul; break; case NCX_BT_DECIMAL64: num2->dec.val = num1->dec.val; num2->dec.digits = num1->dec.digits; num2->dec.zeroes = num1->dec.zeroes; break; case NCX_BT_FLOAT64: num2->d = num1->d; break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return NO_ERR; } /* ncx_copy_num */ /******************************************************************** * FUNCTION ncx_cast_num * * Cast a number as another number type * * Supports all NCX numeric types: * NCX_BT_INT* * NCX_BT_UINT* * NCX_BT_FLOAT64 * * INPUTS: * num1 == source number * btyp1 == expected data type of num1 * num2 == target number * btyp2 == desired data type of num2 * * OUTPUTS: * *num2 set to cast value of num1 * * RETURNS: * status *********************************************************************/ status_t ncx_cast_num (const ncx_num_t *num1, ncx_btype_t btyp1, ncx_num_t *num2, ncx_btype_t btyp2) { int64 testbase /*, testfrac */; status_t res; #ifdef DEBUG if (!num1 || !num2) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; switch (btyp1) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: switch (btyp2) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: num2->i = num1->i; break; case NCX_BT_INT64: num2->l = (int64)num1->i; break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: num2->u = (uint32)num1->i; break; case NCX_BT_UINT64: num2->ul = (uint64)num1->i; break; case NCX_BT_DECIMAL64: if (num2->dec.digits == 0) { /* hack: set a default if none set already */ num2->dec.digits = NCX_DEF_FRACTION_DIGITS; } /* this may cause an overflow, but too bad !!! */ num2->dec.val = (int64)(num1->i * (10 * num2->dec.digits)); break; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT num2->d = (double)num1->i; #else num2->d = (int64)num1->i; #endif break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case NCX_BT_INT64: switch (btyp2) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: res = ERR_NCX_INVALID_VALUE; break; case NCX_BT_INT64: num2->l = num1->l; break; case NCX_BT_UINT64: num2->ul = (uint64)num1->l; break; case NCX_BT_DECIMAL64: if (num2->dec.digits == 0) { /* hack: set a default if none set already */ num2->dec.digits = NCX_DEF_FRACTION_DIGITS; } /* this may cause an overflow, but too bad !!! */ num2->dec.val = (int64)(num1->l * (10 * num2->dec.digits)); break; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT num2->d = (double)num1->l; #else num2->d = (int64)num1->l; #endif break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: switch (btyp2) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: num2->i = (int32)num1->u; break; case NCX_BT_INT64: num2->l = (int64)num1->u; break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: num2->u = num1->u; break; case NCX_BT_UINT64: num2->ul = (uint64)num1->u; break; case NCX_BT_DECIMAL64: if (num2->dec.digits == 0) { /* hack: set a default if none set already */ num2->dec.digits = NCX_DEF_FRACTION_DIGITS; } /* this may cause an overflow, but too bad !!! */ num2->dec.val = (int64)(num1->u * (10 * num2->dec.digits)); break; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT num2->d = (double)num1->u; #else num2->d = (int64)num1->u; #endif break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case NCX_BT_UINT64: switch (btyp2) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: res = ERR_NCX_INVALID_VALUE; break; case NCX_BT_INT64: num2->l = (int64)num1->ul; break; case NCX_BT_UINT64: num2->ul = num1->ul; break; case NCX_BT_DECIMAL64: if (num2->dec.digits == 0) { /* hack: set a default if none set already */ num2->dec.digits = NCX_DEF_FRACTION_DIGITS; } /* this may cause an overflow, but too bad !!! */ num2->dec.val = (int64)(num1->ul * (10 * num2->dec.digits)); break; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT num2->d = (double)num1->ul; #else num2->d = (int64)num1->ul; #endif break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case NCX_BT_DECIMAL64: if (num1->dec.digits == 0) { res = ERR_NCX_INVALID_VALUE; } else { /* just use testbase for now; * not sure if this will ever be used */ testbase = num1->dec.val / (10 * num1->dec.digits); /* testfrac = num1->dec.val % (10 * num1->dec.digits); */ switch (btyp2) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: return ERR_NCX_INVALID_VALUE; case NCX_BT_INT64: /* just do a floor() function for now */ num2->l = testbase; break; case NCX_BT_UINT64: num2->ul = (uint64)testbase; break; case NCX_BT_DECIMAL64: num2->dec.val = num1->dec.val; num2->dec.digits = num1->dec.digits; num2->dec.zeroes = num1->dec.zeroes; break; case NCX_BT_FLOAT64: num2->d = (double)testbase; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } } break; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT switch (btyp2) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_DECIMAL64: return ERR_NCX_INVALID_VALUE; case NCX_BT_INT64: { double d = num1->d; num2->l = (int64)lrint(d); } break; case NCX_BT_UINT64: { double d = num1->d; num2->ul = (uint64)lrint(d); } break; case NCX_BT_FLOAT64: num2->d = num1->d; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } #else switch (btyp2) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_DECIMAL64: return ERR_NCX_INVALID_VALUE; case NCX_BT_INT64: num2->l = num1->d; break; case NCX_BT_UINT64: num2->ul = (uint64)num1->d; break; case NCX_BT_FLOAT64: num2->d = num1->d; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } #endif break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* ncx_cast_num */ /******************************************************************** * FUNCTION ncx_num_floor * * Get the floor value of a number * * Supports all NCX numeric types: * NCX_BT_INT* * NCX_BT_UINT* * NCX_BT_DECIMAL64 * NCX_BT_FLOAT64 * * INPUTS: * num1 == source number * num2 == address of target number * btyp == expected data type of numbers * * OUTPUTS: * *num2 set to floor of num1 * * RETURNS: * status *********************************************************************/ status_t ncx_num_floor (const ncx_num_t *num1, ncx_num_t *num2, ncx_btype_t btyp) { status_t res; #ifdef DEBUG if (!num1 || !num2) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: num2->i = num1->i; break; case NCX_BT_INT64: num2->l = num1->l; break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: num2->u = num1->u; break; case NCX_BT_UINT64: num2->ul = num1->ul; break; case NCX_BT_DECIMAL64: num2->dec.digits = num1->dec.digits; num2->dec.val = num1->dec.val % (10 * num1->dec.digits); num2->dec.zeroes = num1->dec.zeroes; break; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT { double d = num1->d; num2->d = floor(d); } #else num2->d = num1->d; #endif break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* ncx_num_floor */ /******************************************************************** * FUNCTION ncx_num_ceiling * * Get the ceiling value of a number * * Supports all NCX numeric types: * NCX_BT_INT* * NCX_BT_UINT* * NCX_BT_DECIMAL64 * NCX_BT_FLOAT64 * * INPUTS: * num1 == source number * num2 == target number * btyp == expected data type of numbers * * OUTPUTS: * *num2 set to ceiling of num1 * * RETURNS: * status *********************************************************************/ status_t ncx_num_ceiling (const ncx_num_t *num1, ncx_num_t *num2, ncx_btype_t btyp) { status_t res; #ifdef DEBUG if (!num1 || !num2) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: num2->i = num1->i; break; case NCX_BT_INT64: num2->l = num1->l; break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: num2->u = num1->u; break; case NCX_BT_UINT64: num2->ul = num1->ul; break; case NCX_BT_DECIMAL64: num2->dec.digits = num1->dec.digits; /*** !!!!! this is not right !!!! ***/ num2->dec.val = num1->dec.val % (10 * num1->dec.digits); num2->dec.zeroes = num1->dec.zeroes; break; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT { double d = num1->d; num2->d = ceil(d); } #else num2->d = num1->d; #endif break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* ncx_num_ceiling */ /******************************************************************** * FUNCTION ncx_round_num * * Get the rounded value of a number * * Supports all NCX numeric types: * NCX_BT_INT* * NCX_BT_UINT* * NCX_BT_DECIMAL64 * NCX_BT_FLOAT64 * * INPUTS: * num1 == source number * num2 == target number * btyp == expected data type of numbers * * OUTPUTS: * *num2 set to round of num1 * * RETURNS: * status *********************************************************************/ status_t ncx_round_num (const ncx_num_t *num1, ncx_num_t *num2, ncx_btype_t btyp) { status_t res; #ifdef DEBUG if (!num1 || !num2) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: num2->i = num1->i; break; case NCX_BT_INT64: num2->l = num1->l; break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: num2->u = num1->u; break; case NCX_BT_UINT64: num2->ul = num1->ul; break; case NCX_BT_DECIMAL64: num2->dec.digits = num1->dec.digits; /*** this is not right !!!! ***/ num2->dec.val = num1->dec.val % (10 * num1->dec.digits); num2->dec.zeroes = num1->dec.zeroes; break; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT { double d = num1->d; num2->d = round(d); } #else num2->d = num1->d; #endif break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* ncx_round_num */ /******************************************************************** * FUNCTION ncx_num_is_integral * * Check if the number is integral or if it has a fractional part * Supports all NCX numeric types: * NCX_BT_INT* * NCX_BT_UINT* * NCX_BT_DECIMAL64 * NCX_BT_FLOAT64 * * INPUTS: * num == number to check * btyp == expected data type * * RETURNS: * TRUE if integral, FALSE if not *********************************************************************/ boolean ncx_num_is_integral (const ncx_num_t *num, ncx_btype_t btyp) { #ifdef HAS_FLOAT double d; #endif #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: return TRUE; case NCX_BT_DECIMAL64: if (num->dec.digits == 0) { SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } if (num->dec.val / (10 * num->dec.digits)) { return TRUE; } else { return FALSE; } case NCX_BT_FLOAT64: #ifdef HAS_FLOAT d = num->d; d = round(d); return (d == num->d) ? TRUE : FALSE; #else return TRUE; #endif default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* ncx_num_is_integral */ /******************************************************************** * FUNCTION ncx_cvt_to_int64 * * Convert a number to an integer64; * Use rounding for float64 * * INPUTS: * num == number to convert * btyp == data type of num * * RETURNS: * int64 representation *********************************************************************/ int64 ncx_cvt_to_int64 (const ncx_num_t *num, ncx_btype_t btyp) { #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: return (int64)num->i; case NCX_BT_INT64: return num->l; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: return (int64)num->u; case NCX_BT_UINT64: return (int64)num->ul; case NCX_BT_DECIMAL64: if (num->dec.digits == 0) { SET_ERROR(ERR_INTERNAL_VAL); return 0; } return (int64)(num->dec.val / (10 * num->dec.digits)); case NCX_BT_FLOAT64: #ifdef HAS_FLOAT { double d = num->d; return lrint(d); } #else return num->d; #endif default: SET_ERROR(ERR_INTERNAL_VAL); return 0; } /*NOTREACHED*/ } /* ncx_cvt_to_int64 */ /******************************************************************** * FUNCTION ncx_get_numfmt * * Get the number format of the specified string * Does not check for valid format * Just figures out which type it must be if it were valid * * INPUTS: * numstr == number string * * RETURNS: * NCX_NF_NONE, NCX_NF_DEC, NCX_NF_HEX, or NCX_NF_REAL *********************************************************************/ ncx_numfmt_t ncx_get_numfmt (const xmlChar *numstr) { #ifdef DEBUG if (!numstr) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_NF_NONE; } #endif if (*numstr == '\0') { return NCX_NF_NONE; } /* check for a HEX string first */ if (*numstr=='0' && (numstr[1]=='x' || numstr[1]=='X')) { return NCX_NF_HEX; } /* check real number next */ while (*numstr && (*numstr != '.')) { numstr++; } if (*numstr) { return NCX_NF_REAL; } /* leading zero means octal, otherwise decimal */ return (*numstr == '0') ? NCX_NF_OCTAL : NCX_NF_DEC; } /* ncx_get_numfmt */ /******************************************************************** * FUNCTION ncx_printf_num * * Printf a ncx_num_t contents * * INPUTS: * num == number to printf * btyp == number base type * *********************************************************************/ void ncx_printf_num (const ncx_num_t *num, ncx_btype_t btyp) { xmlChar numbuff[VAL_MAX_NUMLEN]; uint32 len; status_t res; #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif res = ncx_sprintf_num(numbuff, num, btyp, &len); if (res != NO_ERR) { log_write("invalid num '%s'", get_error_string(res)); } else { log_write("%s", numbuff); } } /* ncx_printf_num */ /******************************************************************** * FUNCTION ncx_alt_printf_num * * Printf a ncx_num_t contents to the alternate log file * * INPUTS: * num == number to printf * btyp == number base type * *********************************************************************/ void ncx_alt_printf_num (const ncx_num_t *num, ncx_btype_t btyp) { xmlChar numbuff[VAL_MAX_NUMLEN]; uint32 len; status_t res; #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif res = ncx_sprintf_num(numbuff, num, btyp, &len); if (res != NO_ERR) { log_alt_write("invalid num '%s'", get_error_string(res)); } else { log_alt_write("%s", numbuff); } } /* ncx_alt_printf_num */ /******************************************************************** * FUNCTION ncx_sprintf_num * * Sprintf a ncx_num_t contents * * INPUTS: * buff == buffer to write; NULL means just get length * num == number to printf * btyp == number base type * len == address of return length * * OUTPUTS:: * *len == number of bytes written (or would have been) to buff * * RETURNS: * status *********************************************************************/ status_t ncx_sprintf_num (xmlChar *buff, const ncx_num_t *num, ncx_btype_t btyp, uint32 *len) { xmlChar *point; int32 ilen, pos, i; uint32 ulen; xmlChar dumbuff[VAL_MAX_NUMLEN]; xmlChar decbuff[VAL_MAX_NUMLEN]; #ifdef HAS_FLOAT int32 tzcount; #endif #ifdef DEBUG if (!num || !len) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (!buff) { buff = dumbuff; } ilen = 0; *len = 0; switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: ilen = sprintf((char *)buff, "%d", num->i); break; case NCX_BT_INT64: ilen = sprintf((char *)buff, "%lld", (long long)num->l); break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: ilen = sprintf((char *)buff, "%u", num->u); break; case NCX_BT_UINT64: ilen = sprintf((char *)buff, "%llu", (unsigned long long)num->ul); break; case NCX_BT_DECIMAL64: if (num->dec.val == 0) { ilen = xml_strcpy(buff, (const xmlChar *)"0.0"); } else if (num->dec.zeroes > 0) { if(num->dec.val>0) { ilen = xml_strcpy(buff, (const xmlChar *)"0."); } else { ilen = xml_strcpy(buff, (const xmlChar *)"-0."); } i = 1; while (i < num->dec.zeroes) { buff[ilen++] = '0'; i++; } ilen += sprintf((char *)&buff[ilen], "%lld", (long long)llabs(num->dec.val)); } else { if (num->dec.digits == 0) { return SET_ERROR(ERR_INTERNAL_VAL); } else { /* get the encoded number in the temp buffer */ pos = sprintf((char *)decbuff, "%lld", (long long)num->dec.val); if (pos <= num->dec.digits) { return SET_ERROR(ERR_INTERNAL_VAL); } else { /* find where the decimal point should go */ point = &decbuff[pos - num->dec.digits]; /* copy the base part to the real buffer */ ulen = xml_strncpy(buff, decbuff, (uint32)(point - decbuff)); buff[ulen] = '.'; xml_strcpy(&buff[ulen+1], point); /* current length is pos+1 * need to check for trailing zeros * and remove them * (!!! need flag to override!!!) * !!! TBD: WAITING FOR WG TO DECIDE * !! RETURN WITH TRAILING ZEROS FOR NOW */ ilen = pos + 1; } } } break; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT ilen = sprintf((char *)buff, "%.14f", num->d); tzcount = remove_trailing_zero_count(buff); if (tzcount) { ilen -= tzcount; if (buff != dumbuff) { buff[ilen] = 0; } } #else ilen = sprintf((char *)buff, "%lld", (long long)num->d); #endif break; default: return SET_ERROR(ERR_INTERNAL_VAL); } /* check the sprintf return value */ if (ilen < 0) { return ERR_NCX_INVALID_NUM; } else { *len = (uint32)ilen; } return NO_ERR; } /* ncx_sprintf_num */ /******************************************************************** * FUNCTION ncx_is_min * * Return TRUE if the specified number is the min value * for its type * * INPUTS: * num == number to check * btyp == data type of num * RETURNS: * TRUE if this is the minimum value * FALSE otherwise *********************************************************************/ boolean ncx_is_min (const ncx_num_t *num, ncx_btype_t btyp) { #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (btyp) { case NCX_BT_INT8: return (num->i == NCX_MIN_INT8) ? TRUE : FALSE; case NCX_BT_INT16: return (num->i == NCX_MIN_INT16) ? TRUE : FALSE; case NCX_BT_INT32: return (num->i == NCX_MIN_INT) ? TRUE : FALSE; case NCX_BT_INT64: return (num->l == NCX_MIN_LONG) ? TRUE : FALSE; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: return (num->u == NCX_MIN_UINT) ? TRUE : FALSE; case NCX_BT_UINT64: return (num->ul == NCX_MIN_ULONG) ? TRUE : FALSE; case NCX_BT_DECIMAL64: return (num->dec.val == NCX_MIN_LONG) ? TRUE : FALSE; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT return (num->d == -INFINITY) ? TRUE : FALSE; #else return (num->d == NCX_MIN_LONG) ? TRUE : FALSE; #endif default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* ncx_is_min */ /******************************************************************** * FUNCTION ncx_is_max * * Return TRUE if the specified number is the max value * for its type * * INPUTS: * num == number to check * btyp == data type of num * RETURNS: * TRUE if this is the maximum value * FALSE otherwise *********************************************************************/ boolean ncx_is_max (const ncx_num_t *num, ncx_btype_t btyp) { #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (btyp) { case NCX_BT_INT8: return (num->i == NCX_MAX_INT8) ? TRUE : FALSE; case NCX_BT_INT16: return (num->i == NCX_MAX_INT16) ? TRUE : FALSE; case NCX_BT_INT32: return (num->i == NCX_MAX_INT) ? TRUE : FALSE; case NCX_BT_INT64: return (num->l == NCX_MAX_LONG) ? TRUE : FALSE; case NCX_BT_UINT8: return (num->u == NCX_MAX_UINT8) ? TRUE : FALSE; case NCX_BT_UINT16: return (num->u == NCX_MAX_UINT16) ? TRUE : FALSE; case NCX_BT_UINT32: return (num->u == NCX_MAX_UINT) ? TRUE : FALSE; case NCX_BT_UINT64: return (num->ul == NCX_MAX_ULONG) ? TRUE : FALSE; case NCX_BT_DECIMAL64: return (num->dec.val == NCX_MAX_LONG) ? TRUE : FALSE; case NCX_BT_FLOAT64: #ifdef HAS_FLOAT return (num->d == INFINITY) ? TRUE : FALSE; #else return (num->d == NCX_MAX_LONG-1) ? TRUE : FALSE; #endif default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* ncx_is_max */ /******************************************************************** * FUNCTION ncx_convert_tkcnum * * Convert the current token in a token chain to * a ncx_num_t struct * * INPUTS: * tkc == token chain; current token will be converted * tkc->typ == TK_TT_DNUM, TK_TT_HNUM, TK_TT_RNUM * btyp == desired number type * (e.g., NCX_BT_INT32, NCX_BT_UINT64, NCX_BT_FLOAT64) * OUTPUTS: * *val == converted number value (0..2G-1), if NO_ERR * RETURNS: * status *********************************************************************/ status_t ncx_convert_tkcnum (tk_chain_t *tkc, ncx_btype_t btyp, ncx_num_t *val) { const xmlChar *numstr; if (btyp == NCX_BT_DECIMAL64) { return SET_ERROR(ERR_INTERNAL_VAL); } switch (TK_CUR_TYP(tkc)) { case TK_TT_DNUM: numstr = TK_CUR_VAL(tkc); if (numstr && *numstr=='0') { return ncx_convert_num(TK_CUR_VAL(tkc), NCX_NF_OCTAL, btyp, val); } else { return ncx_convert_num(TK_CUR_VAL(tkc), NCX_NF_DEC, btyp, val); } case TK_TT_HNUM: return ncx_convert_num(TK_CUR_VAL(tkc), NCX_NF_HEX, btyp, val); case TK_TT_RNUM: return ncx_convert_num(TK_CUR_VAL(tkc), NCX_NF_REAL, btyp, val); default: /* if this is a string, then this might work */ return ncx_decode_num(TK_CUR_VAL(tkc), btyp, val); } } /* ncx_convert_tkcnum */ /******************************************************************** * FUNCTION ncx_convert_tkc_dec64 * * Convert the current token in a token chain to * a ncx_num_t struct, expecting NCX_BT_DECIMAL64 * * INPUTS: * tkc == token chain; current token will be converted * tkc->typ == TK_TT_DNUM, TK_TT_HNUM, TK_TT_RNUM * digits == number of expected digits * * OUTPUTS: * *val == converted number value, if NO_ERR * * RETURNS: * status *********************************************************************/ status_t ncx_convert_tkc_dec64 (tk_chain_t *tkc, uint8 digits, ncx_num_t *val) { const xmlChar *numstr; #ifdef DEBUG if (!tkc || !val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif switch (TK_CUR_TYP(tkc)) { case TK_TT_DNUM: numstr = TK_CUR_VAL(tkc); if (numstr && *numstr=='0' && numstr[1] != '.') { return ncx_convert_dec64(TK_CUR_VAL(tkc), NCX_NF_OCTAL, digits, val); } else { return ncx_convert_dec64(TK_CUR_VAL(tkc), NCX_NF_DEC, digits, val); } case TK_TT_HNUM: return ncx_convert_dec64(TK_CUR_VAL(tkc), NCX_NF_HEX, digits, val); case TK_TT_RNUM: return ncx_convert_dec64(TK_CUR_VAL(tkc), NCX_NF_REAL, digits, val); default: /* if this is a string, then this might work */ return ncx_decode_dec64(TK_CUR_VAL(tkc), digits, val); } } /* ncx_convert_tkc_dec64 */ /******************************************************************** * FUNCTION ncx_get_dec64_base * * Get the base part of a decimal64 number * * INPUT: * num == number to check (expected to be NCX_BT_DECIMAL64) * * RETURNS: * base part of the number *********************************************************************/ int64 ncx_get_dec64_base (const ncx_num_t *num) { int64 temp1; #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif temp1 = num->dec.val; if (num->dec.digits) { temp1 = temp1 / (10 * num->dec.digits); } return temp1; } /* ncx_get_dec64_base */ /******************************************************************** * FUNCTION ncx_get_dec64_fraction * * Get the fraction part of a decimal64 number * * INPUT: * num == number to check (expected to be NCX_BT_DECIMAL64) * * RETURNS: * fraction part of the number *********************************************************************/ int64 ncx_get_dec64_fraction (const ncx_num_t *num) { int64 temp1; #ifdef DEBUG if (!num) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif if (num->dec.digits) { temp1 = num->dec.val % (10 * num->dec.digits); } else { temp1 = 0; } return temp1; } /* ncx_get_dec64_fraction */ /* END file ncx_num.c */ yuma123_2.14/netconf/src/ncx/val_set_cplxval_obj.c0000664000175000017500000000204314770023131022321 0ustar vladimirvladimir#include #include #include #include #include #include #include #include "procdefs.h" #include "b64.h" #include "cfg.h" #include "dlq.h" #include "getcb.h" #include "json_wr.h" #include "log.h" #include "ncx.h" #include "ncx_list.h" #include "ncx_num.h" #include "ncx_str.h" #include "ncxconst.h" #include "obj.h" #include "ses.h" #include "tk.h" #include "typ.h" #include "val.h" #include "val_parse.h" #include "val_util.h" #include "xml_util.h" #include "xml_wr.h" #include "xml_rd.h" #include "xpath.h" #include "xpath1.h" #include "xpath_yang.h" #include "yangconst.h" status_t val_set_cplxval_obj(val_value_t *val, obj_template_t *obj, char* xmlstr) { FILE* fp; status_t res; val_value_t* tmp_val; fp = fmemopen((void*)xmlstr, strlen(xmlstr), "r"); res = xml_rd_open_file (fp, obj, &tmp_val); if(res!=NO_ERR) { return res; } val_move_children(tmp_val,val); val_free_value(tmp_val); fclose(fp); return NO_ERR; } yuma123_2.14/netconf/src/ncx/xpath_wr.h0000664000175000017500000000407314770023131020147 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xpath_wr #define _H_xpath_wr /* FILE: xpath_wr.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Write an XPath expression to a session output in normalized format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24-apr-09 abb Begun */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xpath_wr_expr * * Write the specified XPath expression to the current session * using the default prefixes * * The XPath pcb must be previously parsed and found valid * * INPUTS: * scb == session control block to use * msg == message header to use * xpathval == the value containing the XPath expr to write * * RETURNS: * status *********************************************************************/ extern status_t xpath_wr_expr (ses_cb_t *scb, val_value_t *xpathval); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xpath_wr */ yuma123_2.14/netconf/src/ncx/cli.c0000664000175000017500000016707114770023131017065 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: cli.c Parse an XML representation of a parameter set instance, and create a ps_parmset_t struct that contains the data found. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 21oct05 abb begun 19dec05 abb restart after refining NCX 10feb06 abb change raw xmlTextReader interface to use ses_cbt_t instead 10feb07 abb split common routines from agt_ps_parse so mgr code can use it 21jul08 abb converted to cli.c; removed non-CLI functions ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include "procdefs.h" #include "cfg.h" #include "cli.h" #include "dlq.h" #include "log.h" #include "obj.h" #include "runstack.h" #include "status.h" #include "typ.h" #include "val.h" #include "val_util.h" #include "var.h" #include "xml_util.h" #include "yangconst.h" #include "val123.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define CLI_DEBUG 1 /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION parse_parm_cmn * * Create a val_value_t struct for the specified parm value, * and insert it into the value set * * INPUTS: * rcxt == runstack context to use * new_parm == complex val_value_t to add the parsed parm into * strval == string representation of the object value * (may be NULL if obj btype is NCX_BT_EMPTY * script == TRUE if parsing a script (in the manager) * == FALSE if parsing XML or CLI * * OUTPUTS: * If the specified parm is mandatory w/defval defined, then a * new val_value_t will be inserted in the vnew_parm->v.childQ * as required to fill in the value set. * * RETURNS: * status *********************************************************************/ static status_t parse_parm_cmn (runstack_context_t *rcxt, val_value_t *new_parm, const xmlChar *strval, boolean script) { val_value_t *newchild; typ_def_t *typdef; obj_template_t *obj, *choiceobj, *targobj; ncx_btype_t btyp; status_t res; res = NO_ERR; obj = new_parm->obj; /* check special case where the parm is a container * with 1 child -- a choice of empty; for this * common NETCONF mechanism, try to match the * strval to a child name */ if (obj_is_root(obj)) { if (script) { (void)var_get_script_val(rcxt, obj, new_parm, strval, ISPARM, &res); } else { log_error("\nError: ncx:root object only " "supported in script mode"); res = ERR_NCX_INVALID_VALUE; } } else if (obj->objtype == OBJ_TYP_CONTAINER) { if(!obj_is_np_container(obj) && strval==NULL) { /*presence container without strval is OK*/ return NO_ERR; } /* check if the only child is an OBJ_TYP_CHOICE */ choiceobj = obj_first_child(obj); if (choiceobj == NULL) { log_error("\nError: container %s does not have any child nodes", obj_get_name(obj)); return ERR_NCX_INVALID_VALUE; } /* check that empty string was not entered, for a default * there is no default for a container */ if (strval == NULL || *strval == 0) { log_error("\nError: 'missing value string"); return ERR_NCX_INVALID_VALUE; } /* figure out how to create the child node (newchild) * that will be added to the new_parm container */ if (*strval == '$' || *strval == '@') { /* this is a file or var reference */ if (script) { newchild = var_get_script_val_ex(rcxt, obj, choiceobj, NULL, strval, ISPARM, NULL, &res); if (newchild != NULL) { if (newchild->obj == obj) { val_replace(newchild, new_parm); val_free_value(newchild); } else { val_add_child(newchild, new_parm); } } } else { log_error("\nError: var or file reference only " "supported in script mode"); return ERR_NCX_INVALID_VALUE; } } else { if (choiceobj->objtype != OBJ_TYP_CHOICE) { log_error("\nError: child %s in container %s must be " "a 'choice' object", obj_get_name(choiceobj), obj_get_name(obj)); return ERR_NCX_INVALID_VALUE; } /* check if a child of any case is named 'strval' * this check will look deep and find child nodes * within a choice or case with the same name * as a member of the choice or case node */ targobj = obj_find_child(choiceobj, obj_get_mod_name(choiceobj), strval); if (targobj == NULL) { log_error("\nError: choice %s in container %s" " does not have any child nodes named '%s'", obj_get_name(choiceobj), obj_get_name(obj), strval); return ERR_NCX_INVALID_VALUE; } if (targobj->objtype != OBJ_TYP_LEAF) { log_error("\nError: case %s in choice %s in container %s" " is not a leaf node", obj_get_name(targobj), obj_get_name(choiceobj), obj_get_name(obj)); return ERR_NCX_INVALID_VALUE; } if (obj_get_basetype(targobj) != NCX_BT_EMPTY) { log_error("\nError: leaf %s in choice %s in container %s" " is not type 'empty'", obj_get_name(targobj), obj_get_name(choiceobj), obj_get_name(obj)); return ERR_NCX_INVALID_VALUE; } /* found a match so create a value node */ newchild = val_new_value(); if (!newchild) { res = ERR_INTERNAL_MEM; } else { val_init_from_template(newchild, targobj); val_add_child(newchild, new_parm); } } } else if (obj->objtype == OBJ_TYP_CHOICE && strval != NULL) { if (*strval == '$' || *strval == '@') { if (script) { newchild = var_get_script_val(rcxt, obj, NULL, strval, ISPARM, &res); if (newchild != NULL) { val_replace(newchild, new_parm); val_free_value(newchild); } } else { res = ERR_NCX_INVALID_VALUE; } } else { /* check if a child of any case is named 'strval' */ targobj = obj_find_child(obj, obj_get_mod_name(obj), strval); if (targobj && obj_get_basetype(targobj) == NCX_BT_EMPTY) { /* found a match so set the value node to type empty */ val_init_from_template(new_parm, targobj); val_set_name(new_parm, obj_get_name(targobj), xml_strlen(obj_get_name(targobj))); } else { res = ERR_NCX_INVALID_VALUE; } } } else { if (script) { (void)var_get_script_val(rcxt, obj, new_parm, strval, ISPARM, &res); } else { /* get the base type value */ btyp = obj_get_basetype(obj); if (btyp == NCX_BT_ANYDATA || btyp == NCX_BT_ANYXML || typ_is_simple(btyp)) { typdef = obj_get_typdef(obj); if (btyp != NCX_BT_ANYDATA && btyp != NCX_BT_ANYXML) { res = val_simval_ok(typdef, strval); } if (res == NO_ERR) { res = val_set_simval(new_parm, typdef, obj_get_nsid(obj), obj_get_name(obj), strval); } } else { res = ERR_NCX_WRONG_DATATYP; } } } return res; } /* parse_parm_cmn */ /******************************************************************** * FUNCTION parse_cli_parm * * Create a val_value_t struct for the specified parm value, * and insert it into the value set * * INPUTS: * rcxt == runstack context to use * val == complex val_value_t to add the parsed parm into * obj == obj_template_t descriptor for the missing parm * strval == string representation of the object value * (may be NULL if obj btype is NCX_BT_EMPTY * script == TRUE if parsing a script (in the manager) * == FALSE if parsing XML or CLI * * OUTPUTS: * If the specified parm is mandatory w/defval defined, then a * new val_value_t will be inserted in the val->v.childQ as required * to fill in the value set. * * RETURNS: * status *********************************************************************/ static status_t parse_cli_parm (runstack_context_t *rcxt, val_value_t *val, obj_template_t *obj, const xmlChar *strval, boolean script) { val_value_t *new_parm; status_t res; /* create a new parm and fill it in */ new_parm = val_new_value(); if (!new_parm) { return ERR_INTERNAL_MEM; } val_init_from_template(new_parm, obj); res = parse_parm_cmn(rcxt, new_parm, strval, script); /* save or free the new child node */ if (res != NO_ERR) { val_free_value(new_parm); } else { val_add_child_sorted(new_parm, val); } return res; } /* parse_cli_parm */ /******************************************************************** * FUNCTION parse_parm_ex * * Create a val_value_t struct for the specified parm value, * and insert it into the value set (extended) * * INPUTS: * rcxt == runstack context to use * val == complex val_value_t to add the parsed parm into * obj == obj_template_t descriptor for the missing parm * nsid == namespace ID to really use * name == object name to really use * strval == string representation of the object value * (may be NULL if obj btype is NCX_BT_EMPTY * script == TRUE if parsing a script (in the manager) * == FALSE if parsing XML or CLI * * OUTPUTS: * If the specified parm is mandatory w/defval defined, then a * new val_value_t will be inserted in the val->v.childQ as required * to fill in the value set. * * RETURNS: * status *********************************************************************/ static status_t parse_parm_ex (runstack_context_t *rcxt, val_value_t *val, obj_template_t *obj, xmlns_id_t nsid, const xmlChar *name, const xmlChar *strval, boolean script) { val_value_t *new_parm; status_t res; /* create a new parm and fill it in */ new_parm = val_new_value(); if (!new_parm) { return ERR_INTERNAL_MEM; } val_init_from_template(new_parm, obj); /* adjust namespace ID and name */ val_set_name(new_parm, name, xml_strlen(name)); new_parm->nsid = nsid; res = parse_parm_cmn(rcxt, new_parm, strval, script); if (res != NO_ERR) { val_free_value(new_parm); } else { val_add_child(new_parm, val); } return res; } /* parse_parm_ex */ /******************************************************************** * FUNCTION find_rawparm * * Find the specified raw parm entry * * INPUTS: * parmQ == Q of cli_rawparm_t * name == object name to really use * namelen == length of 'name' * * RETURNS: * raw parm entry if found, NULL if not *********************************************************************/ static cli_rawparm_t * find_rawparm (dlq_hdr_t *parmQ, const char *name, int32 namelen) { cli_rawparm_t *parm; if (!namelen) { return NULL; } /* check exact match only */ for (parm = (cli_rawparm_t *)dlq_firstEntry(parmQ); parm != NULL; parm = (cli_rawparm_t *)dlq_nextEntry(parm)) { if (strlen(parm->name) == (size_t)namelen && !strncmp(parm->name, name, namelen)) { return parm; } } return NULL; } /* find_rawparm */ /******************************************************************** * FUNCTION copy_argv * * Check the argv string and copy it to the CLI buffer * Add quotes as needed to restore command to original form * * INPUTS: * buffer == buffer spot to copy string into * parmstr == parameter string to use * * RETURNS: * number of chars written to the buffer *********************************************************************/ static int32 copy_argv (char *buffer, const char *parmstr) { const char *str; char *str2; str = NULL; str2 = NULL; /* look for any whitespace in the string at all */ str = parmstr; while (*str && !isspace((int)*str)) { str++; } if (*str == '\0') { /* no whitespace, so nothing to worry about */ return (int32)xml_strcpy((xmlChar *)buffer, (const xmlChar *)parmstr); } /* check leading whitespace */ str = parmstr; while (*str && isspace((int)*str)) { str++; } if (*str == '\0') { /* the string is all whitespace, just copy it * and it will be skipped later */ return (int32)xml_strcpy((xmlChar *)buffer, (const xmlChar *)parmstr); } else if (str != parmstr) { /* there was starting whitespace * just guess that the whole string was * quoted, since it is a rare case */ str2 = buffer; *str2++ = '"'; str2 += (int32)xml_strcpy((xmlChar *)str2, (const xmlChar *)parmstr); *str2++ = '"'; *str2 = '\0'; return (str2 - buffer); } /* started with some non-whitespace; go until the * equals sign is seen or the next whitespace */ while (*str && !isspace((int)*str) && (*str != '=')) { str++; } /* check where the search stopped */ if (*str == '=') { str++; /* str == first char to be inside dquotes */ } else if (isspace((int)*str)) { /* see if the equals sign is still out there */ while (*str && isspace((int)*str)) { str++; } if (*str == '=') { str++; /* str == first char to be inside dquotes */ } else { /* hit end of string of the start of some new token */ } } else { /* should not have hit end of string */ SET_ERROR(ERR_INTERNAL_VAL); return 0; } /* have some split point in the command string to * guess where the original quotes were located */ str2 = buffer; str2 += (int32)xml_strncpy((xmlChar *)str2, (const xmlChar *)parmstr, (uint32)(str - parmstr)); *str2++ = '"'; str2 += (int32)xml_strcpy((xmlChar *)str2, (const xmlChar *)str); *str2++ = '"'; *str2 = '\0'; return (str2 - buffer); } /* copy_argv */ /******************************************************************** * FUNCTION copy_argv_to_buffer * * Check the argv string and copy it to the CLI buffer * Add quotes as needed to restore command to original form * * INPUTS: * argc == number of strings passed in 'argv' * argv == array of command line argument strings * mode == CLI parsing mode in progress * (CLI_MODE_PROGRAM or CLI_MODE_COMMAND) * bufflen == address of return buffer length * res == address of return status * * OUTPUTS: * *bufflen contains the number of chars to use in the * return buffer. * Note that the malloced size of the return * buffer may be slightly larger than this. * *res == return status * * RETURNS: * malloced buffer with the argv[] strings copied * as if it were 1 long command line *********************************************************************/ static char * copy_argv_to_buffer (int argc, char *argv[], cli_mode_t mode, int32 *bufflen, status_t *res) { char *buff; int32 padamount; int32 buffpos; int parmnum; *bufflen = 0; *res = NO_ERR; /* gather all the argv strings into one buffer for easier parsing * need to copy because the buffer is written during parsing * and the argv parameter is always a 'const char **' data type * * Unfortunately, glibc will remove any 'outside' quotes * that were found while processing the input * * program --foo --bar=1 2 --baz="1 2 3" "--goo='a b c'" * * argc = 6 * argv[0] = * argv[1] = '--foo' * argv[2] = '--bar=1' * argv[3] = '2' * argv[4] = '--baz=1 2 3' * argv[5] = "--goo='a b c'" * * Note: * argv[2] is an error anytime, no quotes will be added * argv[4] needs quotes put back into the string * argv[5] needs to be left alone and not get any new quotes * * first determine the buffer length * * mode == CLI_MODE_COMMAND: * padamount = 1 == for the space char added * * mode == CLI_MODE_PROGRAM: * padamount = 3 * 1 for the space char added * 2 for the 2 quote chars that may be needed */ switch (mode) { case CLI_MODE_PROGRAM: padamount = 3; break; case CLI_MODE_COMMAND: padamount = 1; break; default: *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } for (parmnum=1; parmnum < argc; parmnum++) { *bufflen += (strlen(argv[parmnum]) + padamount); } buff = m__getMem(*bufflen + 1); if (!buff) { /* non-recoverable error */ *res = ERR_INTERNAL_MEM; return NULL; } /* copy the argv strings into the buffer */ buffpos = 0; for (parmnum=1; parmnum < argc; parmnum++) { if (mode == CLI_MODE_PROGRAM) { buffpos += copy_argv(&buff[buffpos], argv[parmnum]); } else { buffpos += (int32)xml_strcpy((xmlChar *)&buff[buffpos], (const xmlChar *)argv[parmnum]); } if (parmnum+1 < argc) { buff[buffpos++] = ' '; } } buff[buffpos] = 0; /* need to shorten the buffer because the malloc guessed * 2 chars bigger than needed in most cases, on each * separate command line */ *bufflen = buffpos; return buff; } /* copy_argv_to_buffer */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION cli_new_rawparm * * bootstrap CLI support * Malloc and init a raw parm entry * * INPUTS: * name == name of parm (static const string) * * RETURNS: * new parm entry, NULL if malloc failed *********************************************************************/ cli_rawparm_t * cli_new_rawparm (const xmlChar *name) { cli_rawparm_t *parm; #ifdef DEBUG if (!name) { return NULL; } #endif parm = m__getObj(cli_rawparm_t); if (parm) { memset(parm, 0x0, sizeof(cli_rawparm_t)); parm->name = (const char *)name; parm->hasvalue = TRUE; } return parm; } /* cli_new_rawparm */ /******************************************************************** * FUNCTION cli_new_empty_rawparm * * Malloc and init a raw parm entry that has no value (NCX_BT_EMPTY) * * INPUTS: * name == name of parm (static const string) * * RETURNS: * new parm entry, NULL if malloc failed *********************************************************************/ cli_rawparm_t * cli_new_empty_rawparm (const xmlChar *name) { cli_rawparm_t *parm; #ifdef DEBUG if (!name) { return NULL; } #endif parm = m__getObj(cli_rawparm_t); if (parm) { memset(parm, 0x0, sizeof(cli_rawparm_t)); parm->name = (const char *)name; } return parm; } /* cli_new_empty_rawparm */ /******************************************************************** * FUNCTION cli_free_rawparm * * Clean and free a raw parm entry * * INPUTS: * parm == raw parm entry to free *********************************************************************/ void cli_free_rawparm (cli_rawparm_t *parm) { #ifdef DEBUG if (!parm) { return; } #endif if (parm->value) { m__free(parm->value); } m__free(parm); } /* cli_free_rawparm */ /******************************************************************** * FUNCTION cli_clean_rawparmQ * * Clean and free a Q of raw parm entries * * INPUTS: * parmQ == Q of raw parm entry to free *********************************************************************/ void cli_clean_rawparmQ (dlq_hdr_t *parmQ) { cli_rawparm_t *parm; #ifdef DEBUG if (!parmQ) { return; } #endif while (!dlq_empty(parmQ)) { parm = (cli_rawparm_t *)dlq_deque(parmQ); cli_free_rawparm(parm); } } /* cli_clean_rawparmQ */ /******************************************************************** * FUNCTION cli_find_rawparm * * Find the specified raw parm entry * * INPUTS: * name == object name to really use * parmQ == Q of cli_rawparm_t * * RETURNS: * raw parm entry if found, NULL if not *********************************************************************/ cli_rawparm_t * cli_find_rawparm (const xmlChar *name, dlq_hdr_t *parmQ) { #ifdef DEBUG if (!name || !parmQ) { return NULL; } #endif return find_rawparm(parmQ, (const char *)name, strlen((const char *)name)); } /* cli_find_rawparm */ /******************************************************************** * FUNCTION cli_parse_raw * * Generate N sets of variable/value pairs for the * specified boot-strap CLI parameters * * There are no modules loaded yet, and nothing * has been initialized, not even logging * This function is almost the first thing done * by the application * * CLI Syntax Supported * * [prefix] parmname * * [prefix] parmname=value * * prefix == 0 to 2 dashes foo -foo --foo * parmname == any valid NCX identifier string * value == string * * No spaces are allowed after 'parmname' if 'value' is entered * Each parameter is allowed to occur zero or one times. * If multiple instances of a parameter are entered, then * the last one entered will win. * The 'autocomp' parameter is set to TRUE * * The 'value' string cannot be split across multiple argv segments. * Use quotation chars within the CLI shell to pass a string * containing whitespace to the CLI parser: * * --foo="quoted string if whitespace needed" * --foo="quoted string if setting a variable \ * as a top-level assignment" * --foo=unquoted-string-without-whitespace * * - There are no 1-char aliases for CLI parameters. * - Position-dependent, unnamed parameters are not supported * * INPUTS: * argc == number of strings passed in 'argv' * argv == array of command line argument strings * parmQ == Q of cli_rawparm_t entries * that should be used to validate the CLI input * and store the results * * OUTPUTS: * *rawparm (within parmQ): * - 'value' will be recorded if it is present * - count will be set to the number of times this * parameter was entered (set even if no value) * * RETURNS: * status *********************************************************************/ status_t cli_parse_raw (int argc, char *argv[], dlq_hdr_t *rawparmQ) { cli_rawparm_t *rawparm; char *parmname, *parmval, *str, *buff; int32 parmnamelen, buffpos, bufflen; int i; status_t res; #ifdef DEBUG if (!argv || !rawparmQ) { return ERR_INTERNAL_PTR; } if (dlq_empty(rawparmQ)) { return ERR_INTERNAL_VAL; } #endif /* check if there are any parameters at all to parse */ if (argc < 2) { return NO_ERR; } if (LOGDEBUG2) { log_debug("\nCLI bootstrap: input parameters:"); for (i=0; icount++; if (!rawparm->hasvalue) { /* start over, since no value is expected * for this known empty parm */ continue; } } if ((buffpos < bufflen) && ((buff[buffpos] == '=') || isspace((int)buff[buffpos]))) { /* assume that the parm followed by a * space is the termination, try again on * the next keyword match */ if (buff[buffpos] != '=' && !rawparm) { continue; } buffpos++; /* skip past '=' or whitespace */ while (buff[buffpos] && isspace((int)buff[buffpos])) { buffpos++; } /* if any chars left in buffer, get the parmval */ if (buffpos < bufflen) { if (buff[buffpos] == NCX_QUOTE_CH) { /* set the start after quote */ parmval = &buff[++buffpos]; /* find the end of the quoted string */ str = &parmval[1]; while (*str && *str != NCX_QUOTE_CH) { str++; } } else { /* set the start of the parmval */ parmval = &buff[buffpos]; /* find the end of the unquoted string */ str = &parmval[1]; while (*str && !isspace((int)*str)) { str++; } } /* terminate string */ *str = 0; /* skip buffpos past eo-string */ buffpos += (int32)((str - parmval) + 1); } } if (rawparm) { if (rawparm->value) { m__free(rawparm->value); } rawparm->value = (char *) xml_strdup((const xmlChar *)parmval); if (!rawparm->value) { res = ERR_INTERNAL_MEM; } } } m__free(buff); return res; } /* cli_parse_raw */ /******************************************************************** * FUNCTION cli_parse * * schema based CLI support * Generate 1 val_value_t struct from a Unix Command Line, * which should conform to the specified obj_template_t definition. * * For CLI interfaces, only one container object can be specified * at this time. * * CLI Syntax Supported * * [prefix] parmname [separator] [value] * * prefix == 0 to 2 dashes foo -foo --foo * parmname == any valid NCX identifier string * separator == - equals char (=) * - whitespace (sp, ht) * value == any NCX number or NCX string * == 'enum' data type * == not present (only for 'flag' data type) * == extended * This value: string will converted to appropriate * simple type in val_value_t format. * * The format "--foo=bar" must be processed within one argv segment. * If the separator is a whitespace char, then the next string * in the argv array must contain the expected value. * Whitespace can be preserved using single or double quotes * * DESIGN NOTES: * * 1) parse the (argc, argv) input against the specified object * 2) add any missing mandatory and condition-met parameters * to the container. * 3) Add defaults if valonly is false * 4) Validate any 'choice' constructs within the parmset * 5) Validate the proper number of instances (missing or extra) * (unless valonly is TRUE) * * The 'value' string cannot be split across multiple argv segments. * Use quotation chars within the CLI shell to pass a string * containing whitespace to the CLI parser: * * --foo="quoted string if whitespace needed" * --foo="quoted string if setting a variable \ * as a top-level assignment" * --foo=unquoted-string-without-whitespace * * The input is parsed against the specified obj_template_t struct. * A val_value_t tree is built as the input is read. * Each parameter is syntax checked as is is parsed. * * If possible, the parser will skip to next parmameter in the parmset, * in order to support 'continue-on-error' type of operations. * * Any agent callback functions that might be registered for * the specified container (or its sub-nodes) are not called * by this function. This must be done after this function * has been called, and returns NO_ERR. * * INPUTS: * rcxt == runstack context to use * argc == number of strings passed in 'argv' * argv == array of command line argument strings * obj == obj_template_t of the container * that should be used to validate the input * against the child nodes of this container * valonly == TRUE if only the values presented should * be checked, no defaults, missing parms (Step 1 & 2 only) * == FALSE if all the tests and procedures should be done * autocomp == TRUE if parameter auto-completion should be * tried if any specified parameters are not matches * for the specified parmset * == FALSE if exact match only is desired * mode == CLI_MODE_PROGRAM if calling with real (argc, argv) * parameters; these may need some prep work * == CLI_MODE_COMMAND if calling from yangcli or * some other internal command parser. * These strings will not be preped at all * * status == pointer to status_t to get the return value * * OUTPUTS: * *status == the final function return status * * Just as the NETCONF parser does, the CLI parser will not add a parameter * to the val_value_t if any errors occur related to the initial parsing. * * RETURNS: * pointer to the malloced and filled in val_value_t *********************************************************************/ val_value_t * cli_parse (runstack_context_t *rcxt, int argc, char *argv[], obj_template_t *obj, boolean valonly, boolean script, boolean autocomp, cli_mode_t mode, status_t *status) { #define ERRLEN 127 val_value_t *val; obj_template_t *chobj; const char *msg; char *parmname, *parmval; char *str = NULL, *buff, testch; int32 buffpos, bufflen; uint32 parmnamelen; ncx_btype_t btyp; status_t res; xmlChar errbuff[ERRLEN+1], savechar; boolean gotdashes, gotmatch, gotdefaultparm, isdefaultparm; int i; #ifdef DEBUG if (!status) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (!argv || !obj) { *status = SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif res = NO_ERR; *status = NO_ERR; /* check if CLI parmset really OK to use * Must have only choices, leafs, and leaflists * Since the CLI parameters come from an external YANG file * This is a quick check to make sure the CLI code will work * with the provided object template. */ if (!script && !obj_ok_for_cli(obj)) { *status = SET_ERROR(ERR_NCX_OPERATION_FAILED); return NULL; } /* check if there are any parameters at all to parse */ if (argc < 2 && valonly) { *status = NO_ERR; return NULL; } if (LOGDEBUG3) { log_debug3("\nCLI: input parameters:"); for (i=0; idname = xml_strdup((const xmlChar *)argv[0]); val->name = val->dname; } else { /* set the parmset name to the PSD static name */ val->name = obj_get_name(obj); } /* check for a malloc error */ if (!val->name) { *status = ERR_INTERNAL_MEM; val_free_value(val); return NULL; } bufflen = 0; buff = NULL; /* handle special case -- may just need default CLI params * and validation */ if (argc < 2) { /* 2) add any defaults for optional parms that are not set */ if (!valonly) { res = val_add_defaults(val, NULL, NULL, script); } /* 3) CLI Instance Check * Go through all the parameters in the object and check * to see if each child node is present with the * correct number of instances * The CLI object node can be a choice or a simple parameter * The choice test is also performed by this function call */ if (res == NO_ERR && !valonly) { res = val_instance_check(val, val->obj); } *status = res; return val; } /* need to parse some CLI parms * get 1 normalized buffer */ buff = copy_argv_to_buffer(argc, argv, mode, &bufflen, status); if (!buff) { val_free_value(val); return NULL; } /* setup parm loop */ res = NO_ERR; buffpos = 0; gotdefaultparm = FALSE; /* 1) go through all the command line strings * setup parmname and parmval based on strings found * and the PSD for these parameters * save each parm value in a ps_parm_t struct */ while (buffpos < bufflen && res == NO_ERR) { boolean finish_equals_ok = FALSE; gotdashes = FALSE; gotmatch = FALSE; isdefaultparm = FALSE; chobj = NULL; parmval = NULL; parmname = NULL; parmnamelen = 0; /* first skip starting whitespace */ while (buff[buffpos] && isspace((int)buff[buffpos])) { buffpos++; } /* check start of parameter name conventions * allow zero, one, or two dashes before parmname * foo -foo --foo */ if (!buff[buffpos]) { /* got some extra whitespace at the EOLN */ continue; } else if (buff[buffpos] == NCX_CLI_START_CH) { if (!buff[buffpos+1]) { res = ERR_NCX_INVALID_VALUE; } else if (buff[buffpos+1] == NCX_CLI_START_CH) { testch = buff[buffpos+2]; if (testch == '\0' || isspace((int)testch) || testch == NCX_CLI_START_CH) { res = ERR_NCX_INVALID_VALUE; } else { buffpos += 2; /* skip past 2 dashes */ gotdashes = TRUE; } } else { testch = buff[buffpos+1]; if (testch == '\0' || isspace((int)testch)) { res = ERR_NCX_INVALID_VALUE; } else { buffpos++; /* skip past 1 dash */ gotdashes = TRUE; } } } /* else no dashes, leave parmname pointer alone */ /* should be pointing at start of the parm name * check for the end of the parm name, wsp or equal sign * get the parm template, and get ready to parse it */ if (res == NO_ERR) { unsigned int offset=0; unsigned int len; obj_template_t* base_obj = obj; do { res = cli123_parse_next_child_obj_from_path(base_obj, autocomp, &buff[buffpos+offset], &len, &chobj); if(res!=NO_ERR || chobj==NULL) { break; } offset += len; if(chobj->objtype==OBJ_TYP_LIST && buff[buffpos+offset]=='[') { while (buff[buffpos+offset]=='[') { /* key predicates e.g. /a/b[c=1][d=2] */ val_value_t* predicate_val; unsigned int predicate_len; offset++; res = cli123_parse_parm_assignment(chobj, autocomp, &buff[buffpos+offset], &predicate_len, &predicate_val); if(res != NO_ERR) { break; } val_free_value(predicate_val); offset += predicate_len; if(buff[buffpos+offset]!=']') { res = ERR_NCX_INVALID_VALUE; break; } offset++; } } if(res!=NO_ERR) { break; } if((chobj->objtype==OBJ_TYP_CONTAINER || chobj->objtype==OBJ_TYP_LIST) && buff[buffpos+offset]=='/') { /* instance-identifier to parameter in subcontainer e.g. foo/bar[name='123']/leaf=123*/ base_obj=chobj; offset++; continue; } parmname=&buff[buffpos]; parmnamelen=offset; buffpos += offset; break; } while(1); if(res==NO_ERR && chobj!=NULL) { gotmatch=TRUE; } if(res==ERR_NCX_AMBIGUOUS_CMD) { gotmatch=TRUE; } if (res != NO_ERR) { if (res == ERR_NCX_INVALID_VALUE) { log_error("\nError: invalid CLI parameter prefix"); } else { log_error("\nError: invalid CLI syntax (%s)", get_error_string(res)); } continue; } if (!chobj && !gotdashes) { if (parmnamelen) { int32 idx = buffpos + parmnamelen; /* check if next char is an equals sign and * the default-parm-equals_ok flag is set */ if (obj_is_cli_equals_ok(obj) && buff[idx] == '=') { finish_equals_ok = TRUE; } else { /* check for whitespace following value */ while (isspace((int)buff[idx]) && idx < bufflen) { idx++; } /* check for equals sign, indicating an unknown * parameter name, not a value string for the * default parameter */ if (buff[idx] == '=') { /* will prevent chobj from getting set */ res = ERR_NCX_UNKNOWN_PARM; } } } if (res == NO_ERR) { /* try the default parameter * if any defined, then use the unknown parm * name as a parameter value for the default parm */ chobj = obj_get_default_parm(obj); if (chobj) { if (chobj->objtype != OBJ_TYP_LEAF_LIST && gotdefaultparm) { log_error("\nError: default parm '%s' " "already entered", obj_get_name(chobj)); res = ERR_NCX_DUP_ENTRY; continue; } gotdefaultparm = TRUE; isdefaultparm = TRUE; } } } if (chobj == NULL) { res = ERR_NCX_UNKNOWN_PARM; } else { /* do not check parameter order for CLI */ btyp = obj_get_basetype(chobj); parmval = NULL; /* skip past any whitespace after the parm name */ if (!isdefaultparm) { while (isspace((int)buff[buffpos]) && buffpos < bufflen) { buffpos++; } } if (btyp==NCX_BT_EMPTY) { if (buff[buffpos] == '=') { if((buffpos+1)>=bufflen || (buff[buffpos+1] != '$') || (buff[buffpos+1] != '@')) { log_error("\nError: cannot assign value to " "non-leafy obj '%s'", obj_get_name(obj)); res = ERR_NCX_INVALID_VALUE; } } } else if (buffpos < bufflen) { if (!isdefaultparm) { if (buff[buffpos] == '=') { buffpos++; /* skip any whitespace */ while (buff[buffpos] && buffpos < bufflen && isspace((int)buff[buffpos])) { buffpos++; } } /* else whitespace already skipped */ } /* if any chars left in buffer, get the parmval */ if (buffpos < bufflen) { if (finish_equals_ok) { /* treating the entire string as the * parm value; expecting a string to follow */ int32 j, testidx = 0; parmval = &buff[buffpos]; while ((buffpos+testidx) < bufflen && buff[buffpos+testidx] != '=') { testidx++; } j = buffpos+testidx+1; if (buff[buffpos+testidx] != '=') { res = SET_ERROR(ERR_INTERNAL_VAL); } else { if (j < bufflen) { if (buff[j] == NCX_QUOTE_CH || buff[j] == NCX_SQUOTE_CH) { savechar = buff[j++]; while (j < bufflen && buff[j] && buff[j] != savechar) { j++; } if (buff[j]) { if (j < bufflen) { /* OK exit */ str = &buff[j+1]; } else { res = SET_ERROR (ERR_INTERNAL_VAL); } } else { str = &buff[j]; } } else { /* not a quoted string */ while (j < bufflen && buff[j] && !isspace((int)buff[j])) { j++; } str = &buff[j]; // OK exit } } else { str = &buff[j]; } } } else if (buff[buffpos] == NCX_QUOTE_CH || buff[buffpos] == NCX_SQUOTE_CH) { savechar = buff[buffpos]; if (script) { /* set the start at quote */ parmval = &buff[buffpos]; } else { /* set the start after quote */ parmval = &buff[++buffpos]; } /* find the end of the quoted string */ str = &parmval[1]; while (*str && *str != savechar) { str++; } if (*str == savechar) { /* if script mode keep ending quote */ if (script) { str++; } } else { log_error("\nError: unfinished string " "found '%s'", parmval); res = ERR_NCX_UNENDED_QSTRING; } } else if (script && (buffpos+1 < bufflen) && (buff[buffpos] == NCX_XML1a_CH) && (buff[buffpos+1] == NCX_XML1b_CH)) { /* set the start of the XML parmval to the [ */ parmval = &buff[buffpos]; /* find the end of the inline XML */ str = parmval+1; while (*str && !((*str==NCX_XML2a_CH) && (str[1]==NCX_XML2b_CH))) { str++; } if (!*str) { /* did not find end of XML string */ res = ERR_NCX_DATA_MISSING; } else { /* setup after the ] char to be zeroed */ str += 2; } } else { /* set the start of the parmval */ parmval = &buff[buffpos]; /* find the end of the unquoted string */ str = parmval+1; unsigned int quote=0; unsigned int squote=0; unsigned int backslash_seq=0; while (*str) { if(isspace((int)*str)) { if((quote%2)==0 && (squote%2)==0) { //no open quotes break; } } else if((backslash_seq%2)==0 && *str == NCX_QUOTE_CH) { quote++; } else if((backslash_seq%2)==0 && *str == NCX_SQUOTE_CH) { squote++; } if(*str=='\\') { backslash_seq++; } else { backslash_seq==0; } str++; } } /* terminate string */ if (str) { *str = 0; /* skip buffpos past eo-string */ buffpos += (uint32)((str - parmval) + 1); } else { res = SET_ERROR(ERR_INTERNAL_VAL); } } } /* make sure value entered if expected * NCX_BT_EMPTY and NCX_BT_STRING * (if zero-length strings allowed) */ if (res==NO_ERR && !parmval && (obj_is_leafy(chobj) && btyp!=NCX_BT_EMPTY)) { if (!(typ_is_string(btyp) && (val_simval_ok(obj_get_typdef(chobj), EMPTY_STRING) == NO_ERR))) { res = ERR_NCX_EMPTY_VAL; } } } } /* create a new val_value struct and set the value */ if (res == NO_ERR) { char* instance_id_str; instance_id_str = strndup(parmname, parmnamelen); assert(instance_id_str); res = cli123_parse_value_instance(rcxt, val, chobj, instance_id_str, (const xmlChar *)parmval, script); free(instance_id_str); } else if (res == ERR_NCX_EMPTY_VAL && gotmatch && !gotdashes) { /* matched parm did not work out so * check if this is intended to be a * parameter value for the default-parm */ chobj = obj_get_default_parm(obj); if (chobj) { savechar = parmname[parmnamelen]; parmname[parmnamelen] = 0; res = cli123_parse_value_instance(rcxt, val, chobj, obj_get_name(chobj)/*instance_id_str*/, (const xmlChar *)parmname, script); parmname[parmnamelen] = savechar; } } /* check any errors in the parm name or value */ if (res != NO_ERR) { msg = get_error_string(res); errbuff[0] = 0; if (parmname != NULL) { xml_strncpy(errbuff, (const xmlChar *)parmname, min(parmnamelen, ERRLEN)); } else if(isdefaultparm) { xml_strncpy(errbuff, (const xmlChar *)"(in default parm)", min(strlen("(in default parm)"), ERRLEN)); } else { xml_strncpy(errbuff, (const xmlChar *)&buff[buffpos], min(strlen(&buff[buffpos]), ERRLEN)); } switch (res) { case ERR_NCX_UNKNOWN_PARM: log_error("\nError: Unknown parameter (%s)", errbuff); break; case ERR_NCX_AMBIGUOUS_CMD: parmname[parmnamelen] = 0; log_error("\nError: multiple matches for '%s'", parmname); break; default: if (*errbuff) { if (parmval != NULL) { log_error("\nError: %s (%s = %s)", msg, errbuff, parmval); } else if (buffpos < bufflen) { log_error("\nError: %s (%s = %s)", msg, errbuff, &buff[buffpos]); } else { log_error("\nError: %s (%s)", msg, errbuff); } } else { log_error("\nError: %s", msg); } } m__free(buff); *status = res; return val; } } /* cleanup after loop */ m__free(buff); buff = NULL; /* 2) add any defaults for optional parms that are not set */ if (res == NO_ERR && !valonly) { res = val_add_defaults(val, NULL, NULL, script); } /* 3) CLI Instance Check * Go through all the parameters in the object and check * to see if each child node is present with the * correct number of instances * The CLI object node can be a choice or a simple parameter * The choice test is also performed by this function call */ if (res == NO_ERR && !valonly) { res = val_instance_check(val, val->obj); } *status = res; return val; } /* cli_parse */ /******************************************************************** * FUNCTION cli_parse_parm * * Create a val_value_t struct for the specified parm value, * and insert it into the parent container value * * ONLY CALLED FROM CLI PARSING FUNCTIONS IN ncxcli.c * ALLOWS SCRIPT EXTENSIONS TO BE PRESENT * * INPUTS: * rcxt == runstack context to use * val == parent value struct to adjust * parm == obj_template_t descriptor for the missing parm * strval == string representation of the parm value * (may be NULL if parm btype is NCX_BT_EMPTY * script == TRUE if CLI script mode * == FALSE if CLI plain mode * * OUTPUTS: * A new val_value_t will be inserted in the val->v.childQ * as required to fill in the parm. * * RETURNS: * status *********************************************************************/ status_t cli_parse_parm (runstack_context_t *rcxt, val_value_t *val, obj_template_t *obj, const xmlChar *strval, boolean script) { return parse_cli_parm(rcxt, val, obj, strval, script); } /* cli_parse_parm */ /******************************************************************** * FUNCTION cli_parse_parm_ex * * Create a val_value_t struct for the specified parm value, * and insert it into the parent container value * Allow different bad data error handling vioa parameter * * ONLY CALLED FROM CLI PARSING FUNCTIONS IN ncxcli.c * ALLOWS SCRIPT EXTENSIONS TO BE PRESENT * * INPUTS: * rcxt == runstack context to use * val == parent value struct to adjust * parm == obj_template_t descriptor for the missing parm * strval == string representation of the parm value * (may be NULL if parm btype is NCX_BT_EMPTY * script == TRUE if CLI script mode * == FALSE if CLI plain mode * bad_data == enum defining how bad data should be handled * * OUTPUTS: * A new val_value_t will be inserted in the val->v.childQ * as required to fill in the parm. * * RETURNS: * status *********************************************************************/ status_t cli_parse_parm_ex (runstack_context_t *rcxt, val_value_t *val, obj_template_t *obj, const xmlChar *strval, boolean script, ncx_bad_data_t bad_data) { obj_template_t *genstr; status_t res; res = parse_cli_parm(rcxt, val, obj, strval, script); if (res == NO_ERR || NEED_EXIT(res)) { return res; } switch (bad_data) { case NCX_BAD_DATA_WARN: if (ncx_warning_enabled(ERR_NCX_USING_BADDATA)) { log_warn("\nWarning: invalid value " "'%s' used for parm '%s'", (strval) ? strval : EMPTY_STRING, obj_get_name(obj)); } /* drop through */ case NCX_BAD_DATA_IGNORE: genstr = ncx_get_gen_string(); res = parse_parm_ex(rcxt, val, genstr, obj_get_nsid(obj), obj_get_name(obj), strval, script); return res; case NCX_BAD_DATA_CHECK: case NCX_BAD_DATA_ERROR: return res; default: return SET_ERROR(ERR_INTERNAL_VAL); } /*NOTREACHED*/ } /* cli_parse_parm_ex */ /* END file cli.c */ yuma123_2.14/netconf/src/ncx/bobhash.c0000664000175000017500000001101614770023131017707 0ustar vladimirvladimir/* Public domain hash function from the 1997 Dr Dobbs article By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this code any way you wish, private, educational, or commercial. It's free. This function is referenced in - From Packet Sampling Techniques RFC 5475 - implemented from */ #include "bobhash.h" /* -------------------------------------------------------------------- mix -- mix 3 32-bit values reversibly. For every delta with one or two bits set, and the deltas of all three high bits or all three low bits, whether the original value of a,b,c is almost all zero or is uniformly distributed, * If mix() is run forward or backward, at least 32 bits in a,b,c have at least 1/4 probability of changing. * If mix() is run forward, every bit of c will change between 1/3 and 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) mix() was built out of 36 single-cycle latency instructions in a structure that could supported 2x parallelism, like so: a -= b; a -= c; x = (c>>13); b -= c; a ^= x; b -= a; x = (a<<8); c -= a; b ^= x; c -= b; x = (b>>13); ... Unfortunately, superscalar Pentiums and Sparcs can't take advantage of that parallelism. They've also turned some of those single-cycle latency instructions into multi-cycle latency instructions. Still, this is the fastest good hash I could find. There were about 2^^68 to choose from. I only looked at a billion or so. -------------------------------------------------------------------- */ #define mix(a,b,c) \ { \ a -= b; a -= c; a ^= (c>>13); \ b -= c; b -= a; b ^= (a<<8); \ c -= a; c -= b; c ^= (b>>13); \ a -= b; a -= c; a ^= (c>>12); \ b -= c; b -= a; b ^= (a<<16); \ c -= a; c -= b; c ^= (b>>5); \ a -= b; a -= c; a ^= (c>>3); \ b -= c; b -= a; b ^= (a<<10); \ c -= a; c -= b; c ^= (b>>15); \ } /* -------------------------------------------------------------------- bobhash() -- hash a variable-length key into a 32-bit value k : the key (the unaligned variable-length array of bytes) len : the length of the key, counting by bytes initval : can be any 4-byte value Returns a 32-bit value. Every bit of the key affects every bit of the return value. Every 1-bit and 2-bit delta achieves avalanche. About 6*len+35 instructions. The best hash table sizes are powers of 2. There is no need to do mod a prime (mod is sooo slow!). If you need less than 32 bits, use a bitmask. For example, if you need only 10 bits, do h = (h & hashmask(10)); In which case, the hash table should have hashsize(10) elements. If you are hashing n strings (ub1 **)k, do it like this: for (i=0, h=0; i= 12) { a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24)); b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24)); c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24)); mix(a,b,c); k += 12; len -= 12; } /*------------------------------------- handle the last 11 bytes */ c += length; switch(len) /* all the case statements fall through */ { case 11: c+=((ub4)k[10]<<24); case 10: c+=((ub4)k[9]<<16); case 9 : c+=((ub4)k[8]<<8); /* the first byte of c is reserved for the length */ case 8 : b+=((ub4)k[7]<<24); case 7 : b+=((ub4)k[6]<<16); case 6 : b+=((ub4)k[5]<<8); case 5 : b+=k[4]; case 4 : a+=((ub4)k[3]<<24); case 3 : a+=((ub4)k[2]<<16); case 2 : a+=((ub4)k[1]<<8); case 1 : a+=k[0]; /* case 0: nothing left to add */ } mix(a,b,c); /*-------------------------------------------- report the result */ return c; } yuma123_2.14/netconf/src/ncx/xpath1.h0000664000175000017500000004031714770023131017521 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xpath1 #define _H_xpath1 /* FILE: xpath1.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* XPath 1.0 expression support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13-nov-08 abb Begun */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xpath1_parse_expr * * Parse the XPATH 1.0 expression string. * * parse initial expr with YANG prefixes: must/when * the object is left out in case it is in a grouping * * This is just a first pass done when the * XPath string is consumed. If this is a * YANG file source then the prefixes will be * checked against the 'mod' import Q * * The expression is parsed into XPath tokens * and checked for well-formed syntax and function * invocations. Any variable * * If the source is XP_SRC_INSTANCEID, then YANG * instance-identifier syntax is followed, not XPath 1.0 * This is only used by instance-identifiers in * default-stmt, conf file, CLI, etc. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * * INPUTS: * tkc == parent token chain * mod == module in progress * pcb == initialized xpath parser control block * for the expression; use xpath_new_pcb * to initialize before calling this fn * The pcb->exprstr MUST BE SET BEFORE THIS CALL * source == enum indicating source of this expression * * OUTPUTS: * pcb->tkc is filled and then partially validated * pcb->parseres is set * * RETURNS: * status *********************************************************************/ extern status_t xpath1_parse_expr (tk_chain_t *tkc, ncx_module_t *mod, xpath_pcb_t *pcb, xpath_source_t source); /******************************************************************** * FUNCTION xpath1_validate_expr * * Validate the previously parsed expression string * - QName prefixes are valid * - function calls are well-formed and exist in * the pcb->functions array * - variable references exist in the pcb->varbindQ * * parse expr with YANG prefixes: must/when * called from final OBJ xpath check after all * cooked objects are in place * * Called after all 'uses' and 'augment' expansion * so validation against cooked object tree can be done * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * mod == module containing the 'obj' (in progress) * obj == object containing the XPath clause * pcb == the XPath parser control block to process * * OUTPUTS: * pcb->obj and pcb->objmod are set * pcb->validateres is set * * RETURNS: * status *********************************************************************/ extern status_t xpath1_validate_expr (ncx_module_t *mod, obj_template_t *obj, xpath_pcb_t *pcb); /******************************************************************** * FUNCTION xpath1_validate_expr_ex * * Validate the previously parsed expression string * - QName prefixes are valid * - function calls are well-formed and exist in * the pcb->functions array * - variable references exist in the &pcb->varbindQ * * parse expr with YANG prefixes: must/when * called from final OBJ xpath check after all * cooked objects are in place * * Called after all 'uses' and 'augment' expansion * so validation against cooked object tree can be done * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * mod == module containing the 'obj' (in progress) * obj == object containing the XPath clause * pcb == the XPath parser control block to process * missing_is_error == TRUE if a missing node is an error * == FALSE if a warning * OUTPUTS: * pcb->obj and pcb->objmod are set * pcb->validateres is set * * RETURNS: * status *********************************************************************/ extern status_t xpath1_validate_expr_ex (ncx_module_t *mod, obj_template_t *obj, xpath_pcb_t *pcb, boolean missing_is_error); /******************************************************************** * FUNCTION xpath1_eval_expr * * use if the prefixes are YANG: must/when * Evaluate the expression and get the expression nodeset result * * INPUTS: * pcb == XPath parser control block to use * val == start context node for value of current() * docroot == ptr to cfg->root or top of rpc/rpc-replay/notif tree * logerrors == TRUE if log_error and ncx_print_errormsg * should be used to log XPath errors and warnings * FALSE if internal error info should be recorded * in the xpath_result_t struct instead * configonly == * XP_SRC_XML: * TRUE if this is a call * and all config=false nodes should be skipped * FALSE if call and non-config nodes * will not be skipped * XP_SRC_YANG and XP_SRC_LEAFREF: * should be set to false * res == address of return status * * OUTPUTS: * *res is set to the return status * * * RETURNS: * malloced result struct with expr result * NULL if no result produced (see *res for reason) *********************************************************************/ extern xpath_result_t * xpath1_eval_expr (xpath_pcb_t *pcb, val_value_t *val, val_value_t *docroot, boolean logerrors, boolean configonly, status_t *res); /******************************************************************** * FUNCTION xpath1_eval_xmlexpr * * use if the prefixes are XML: select * Evaluate the expression and get the expression nodeset result * Called from inside the XML parser, so the XML reader * must be used to get the XML namespace to prefix mappings * * INPUTS: * reader == XML reader to use * pcb == initialized XPath parser control block * the xpath_new_pcb(exprstr) function is * all that is needed. This function will * call xpath1_parse_expr if it has not * already been called. * val == start context node for value of current() * docroot == ptr to cfg->root or top of rpc/rpc-replay/notif tree * logerrors == TRUE if log_error and ncx_print_errormsg * should be used to log XPath errors and warnings * FALSE if internal error info should be recorded * in the xpath_result_t struct instead * !!! use FALSE unless DEBUG mode !!! * configonly == * XP_SRC_XML: * TRUE if this is a call * and all config=false nodes should be skipped * FALSE if call and non-config nodes * will not be skipped * XP_SRC_YANG and XP_SRC_LEAFREF: * should be set to false * res == address of return status * * OUTPUTS: * *res is set to the return status * * RETURNS: * malloced result struct with expr result * NULL if no result produced (see *res for reason) *********************************************************************/ extern xpath_result_t * xpath1_eval_xmlexpr (xmlTextReaderPtr reader, xpath_pcb_t *pcb, val_value_t *val, val_value_t *docroot, boolean logerrors, boolean configonly, status_t *res); /******************************************************************** * FUNCTION xpath1_get_functions_ptr * * Get the start of the function array for XPath 1.0 plus * the current() function * * RETURNS: * pointer to functions array *********************************************************************/ extern const xpath_fncb_t * xpath1_get_functions_ptr (void); /******************************************************************** * FUNCTION xpath1_prune_nodeset * * Check the current result nodeset and remove * any redundant nodes from a NETCONF POV * Any node that has an ancestor already in * the result will be deleted * * INPUTS: * pcb == XPath parser control block to use * result == XPath result nodeset to prune * * OUTPUTS: * some result->nodeQ contents adjusted or removed * *********************************************************************/ extern void xpath1_prune_nodeset (xpath_pcb_t *pcb, xpath_result_t *result); /******************************************************************** * FUNCTION xpath1_check_node_exists * * Check if any ancestor-ot-self node is already in the specified Q * ONLY FOR VALUE NODES IN THE RESULT * * This is only done after all the nodes have been processed * and the nodeset is complete. For NETCONF purposes, * the entire path to root is added for the context node, * and the entire context node contexts are always returned * * INPUTS: * pcb == parser control block to use * resultQ == Q of xpath_resnode_t structs to check * DOES NOT HAVE TO BE WITHIN A RESULT NODE Q * val == value node pointer value to find * * RETURNS: * TRUE if found, FALSE otherwise *********************************************************************/ extern boolean xpath1_check_node_exists (xpath_pcb_t *pcb, dlq_hdr_t *resultQ, const val_value_t *val); /******************************************************************** * FUNCTION xpath1_check_node_exists_slow * * Check if any ancestor-ot-self node is already in the specified Q * ONLY FOR VALUE NODES IN THE RESULT * * This is only done after all the nodes have been processed * and the nodeset is complete. For NETCONF purposes, * the entire path to root is added for the context node, * and the entire context node contexts are always returned * * INPUTS: * pcb == parser control block to use * resultQ == Q of xpath_resnode_t structs to check * DOES NOT HAVE TO BE WITHIN A RESULT NODE Q * val == value node pointer value to find * * RETURNS: * TRUE if found, FALSE otherwise *********************************************************************/ extern boolean xpath1_check_node_exists_slow (xpath_pcb_t *pcb, dlq_hdr_t *resultQ, const val_value_t *val); /******************************************************************** * FUNCTION xpath1_check_node_child_exists_slow * * Check if any child-or-self node is already in the specified Q * ONLY FOR VALUE NODES IN THE RESULT * * This is only done after all the nodes have been processed * and the nodeset is complete. For NETCONF purposes, * the entire path to root is added for the context node, * and the entire context node contexts are always returned * * INPUTS: * pcb == parser control block to use * resultQ == Q of xpath_resnode_t structs to check * DOES NOT HAVE TO BE WITHIN A RESULT NODE Q * val == value node pointer value to find * * RETURNS: * TRUE if found, FALSE otherwise *********************************************************************/ extern boolean xpath1_check_node_child_exists_slow (xpath_pcb_t *pcb, dlq_hdr_t *resultQ, const val_value_t *val); /******************************************************************** * FUNCTION xpath1_stringify_nodeset * * Convert a value node pointer to a string node * ONLY FOR VALUE NODES IN THE RESULT * * string(nodeset) * * INPUTS: * pcb == parser control block to use * result == result to stringify * str == address of return string * * OUTPUTS: * *str == malloced and filled in string (if NO_ERR) * * RETURNS: * status, NO_ER or ERR_INTERNAL_MEM, etc. *********************************************************************/ extern status_t xpath1_stringify_nodeset (xpath_pcb_t *pcb, const xpath_result_t *result, xmlChar **str); /******************************************************************* * FUNCTION xpath1_stringify_node * * Convert a value node to a string node * * string(node) * * INPUTS: * pcb == parser control block to use * val == value node to stringify * str == address of return string * * OUTPUTS: * *str == malloced and filled in string (if NO_ERR) * * RETURNS: * status, NO_ER or ERR_INTERNAL_MEM, etc. *********************************************************************/ extern status_t xpath1_stringify_node (xpath_pcb_t *pcb, val_value_t *val, xmlChar **str); /******************************************************************** * FUNCTION xpath1_compare_result_to_string * * Compare an XPath result to the specified string * * result = 'string' * * INPUTS: * pcb == parser control block to use * result == result struct to compare * strval == string value to compare to result * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * equality relation result (TRUE or FALSE) *********************************************************************/ extern boolean xpath1_compare_result_to_string (xpath_pcb_t *pcb, xpath_result_t *result, xmlChar *strval, status_t *res); /******************************************************************** * FUNCTION xpath1_compare_result_to_number * * Compare an XPath result to the specified number * * result = number * * INPUTS: * pcb == parser control block to use * result == result struct to compare * numval == number struct to compare to result * MUST BE TYPE NCX_BT_FLOAT64 * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * equality relation result (TRUE or FALSE) *********************************************************************/ extern boolean xpath1_compare_result_to_number (xpath_pcb_t *pcb, xpath_result_t *result, ncx_num_t *numval, status_t *res); /******************************************************************** * FUNCTION xpath1_compare_nodeset_results * * Compare an XPath result to another result * * result1 = node-set * result2 = node-set * * INPUTS: * pcb == parser control block to use * result1 == result struct to compare * result2 == result struct to compare * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * equality relation result (TRUE or FALSE) *********************************************************************/ extern boolean xpath1_compare_nodeset_results (xpath_pcb_t *pcb, xpath_result_t *result1, xpath_result_t *result2, status_t *res); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xpath1 */ yuma123_2.14/netconf/src/ncx/getcb.h0000664000175000017500000000621614770023131017400 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_getcb #define _H_getcb /* FILE: getcb.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Data Model Get Operation callback handler for virtual data that is not stored in a val_value_t tree. These callback functions are used to provide temporary val_value_t structures that MUST BE FREED by the caller after they are used!!! The callback function has several sub-mode functions to support the retrieval of virtual data, mixed with the real data tree of a configuration database. Submode 1: GETCB_GET_METAQ Retrieve a Queue header of val_value_t structs representing the attributes for the specified value node Submode x: GETCB_GET_LEAFVAL Retrieve the simple value contents of a virtual value leaf node ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 16-apr-07 abb Begun; split out from agt_ps.h */ #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* placeholder for expansion modes */ typedef enum getcb_mode_t_ { GETCB_NONE, GETCB_GET_VALUE } getcb_mode_t; /* getcb_fn_t * * Callback function for agent node get handler * * INPUTS: * scb == session that issued the get (may be NULL) * can be used for access control purposes * cbmode == reason for the callback * virval == place-holder node in the data model for * this virtual value node * dstval == pointer to value output struct * * OUTPUTS: * *fil may be adjusted depending on callback reason * *dstval should be filled in, depending on the callback reason * * RETURNS: * status: */ typedef status_t (*getcb_fn_t) (ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_getcb */ yuma123_2.14/netconf/src/ncx/xml_msg.h0000664000175000017500000002663614770023131017772 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xml_msg #define _H_xml_msg /* FILE: xml_msg.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* XML Message send and receive Deals with generic namespace and xmlns optimization and tries to keep changing the default namespace so most nested elements do not have prefixes Deals with the NETCONF requirement that the attributes in are returned in unchanged. Although XML allows the xnmlns prefixes to change, the same prefixes are used in the that the NMS provided in the . ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 14-jan-07 abb Begun; split from agt_rpc.h */ #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* Common XML Message Header */ typedef struct xml_msg_hdr_t_ { /* incoming: * All the namespace decls that were in the * request are used in the , so the same prefixes * will be used, and the XML on the wire will be easier to debug * by examining packet traces * * if useprefix=TRUE then prefixes will be used for * element start and end tags. FALSE then default NS * will be used so no prefixes will be needed except * for XML content */ boolean useprefix; ncx_withdefaults_t withdef; /* with-defaults value */ dlq_hdr_t prefixQ; /* Q of xmlns_pmap_t */ dlq_hdr_t errQ; /* Q of rpc_err_rec_t */ /* agent access control for database reads and writes; * !!! shadow pointer to per-session cache, not malloced */ struct agt_acm_cache_t_ *acm_cache; /* agent access control read authorization * callback function: xml_msg_authfn_t */ void *acm_cbfn; } xml_msg_hdr_t; /* read authorization callback template */ typedef boolean (*xml_msg_authfn_t) (xml_msg_hdr_t *msg, const xmlChar *username, const val_value_t *val); /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xml_msg_init_hdr * * Initialize a new xml_msg_hdr_t struct * * INPUTS: * msg == xml_msg_hdr_t memory to initialize * RETURNS: * none *********************************************************************/ extern void xml_msg_init_hdr (xml_msg_hdr_t *msg); /******************************************************************** * FUNCTION xml_msg_clean_hdr * * Clean all the memory used by the specified xml_msg_hdr_t * but do not free the struct itself * * INPUTS: * msg == xml_msg_hdr_t to clean * RETURNS: * none *********************************************************************/ extern void xml_msg_clean_hdr (xml_msg_hdr_t *msg); /******************************************************************** * FUNCTION xml_msg_get_prefix * * Find the namespace prefix for the specified namespace ID * If it is not there then create one * * INPUTS: * msg == message to search * parent_nsid == parent namespace ID * nsid == namespace ID to find * curelem == value node for current element if available * xneeded == pointer to xmlns needed flag output value * * OUTPUTS: * *xneeded == TRUE if the prefix is new and an xmlns * decl is needed in the element being generated * * RETURNS: * pointer to prefix if found, else NULL if not found *********************************************************************/ extern const xmlChar * xml_msg_get_prefix (xml_msg_hdr_t *msg, xmlns_id_t parent_nsid, xmlns_id_t nsid, val_value_t *curelem, boolean *xneeded); /******************************************************************** * FUNCTION xml_msg_get_prefix_xpath * * Find the namespace prefix for the specified namespace ID * If it is not there then create one in the msg prefix map * Always returns a prefix, instead of using a default * * creates a new pfixmap if needed * * !!! MUST BE CALLED BEFORE THE XML OUTPUT * !!! HAS BEGUN. CANNOT BE CALLED BY OUTPUT FUNCTIONS * !!! DURING THE OR OUTPUT GENERATION * * INPUTS: * msg == message to search * nsid == namespace ID to find * * RETURNS: * pointer to prefix if found, else NULL if not found *********************************************************************/ extern const xmlChar * xml_msg_get_prefix_xpath (xml_msg_hdr_t *msg, xmlns_id_t nsid); /******************************************************************** * FUNCTION xml_msg_get_prefix_start_tag * * Find the namespace prefix for the specified namespace ID * DO NOT CREATE A NEW PREFIX MAP IF IT IS NOT THERE * does not create any pfixmap, just returns NULL if not found * * INPUTS: * msg == message to search * nsid == namespace ID to find * * RETURNS: * pointer to prefix if found, else NULL if not found *********************************************************************/ extern const xmlChar * xml_msg_get_prefix_start_tag (xml_msg_hdr_t *msg, xmlns_id_t nsid); /******************************************************************** * FUNCTION xml_msg_gen_new_prefix * * Generate a new namespace prefix * * INPUTS: * msg == message to search and generate a prefix for * nsid == namespace ID to generate prefix for * retbuff == address of return buffer * buffsize == buffer size * OUTPUTS: * if *retbuff is NULL it will be created * else *retbuff is filled in with the new prefix if NO_ERR * * RETURNS: * status *********************************************************************/ extern status_t xml_msg_gen_new_prefix (xml_msg_hdr_t *msg, xmlns_id_t nsid, xmlChar **retbuff, uint32 buffsize); /******************************************************************** * FUNCTION xml_msg_build_prefix_map * * Build a queue of xmlns_pmap_t records for the current message * * INPUTS: * msg == message in progrss * attrs == the top-level attrs list (e;g, rpc_in_attrs) * addncid == TRUE if a prefix entry for the NC namespace * should be added * == FALSE if the NC nsid should not be added * addncxid == TRUE if a prefix entry for the NCX namespace * should be added * == FALSE if the NCX nsid should not be added * OUTPUTS: * msg->prefixQ will be populated as needed, * could be partially populated if some error returned * * XMLNS Entries for NETCONF and NCX will be added if they * are not present * * RETURNS: * status *********************************************************************/ extern status_t xml_msg_build_prefix_map (xml_msg_hdr_t *msg, xml_attrs_t *attrs, boolean addncid, boolean addncxid); /******************************************************************** * FUNCTION xml_msg_finish_prefix_map * * Finish the queue of xmlns_pmap_t records for the current message * * INPUTS: * msg == message in progrss * attrs == the top-level attrs list (e;g, rpc_in_attrs) * OUTPUTS: * msg->prefixQ will be populated as needed, * could be partially populated if some error returned * * RETURNS: * status *********************************************************************/ extern status_t xml_msg_finish_prefix_map (xml_msg_hdr_t *msg, xml_attrs_t *attrs); /******************************************************************** * FUNCTION xml_msg_check_xmlns_attr * * Check the default NS and the prefix map in the msg; * * INPUTS: * msg == message in progress * nsid == namespace ID to check * badns == namespace URI of the bad namespace * used if the nsid is the INVALID marker * attrs == Q to hold the xml_attr_t, if generated * * OUTPUTS: * msg->prefixQ will be populated as needed, * could be partially populated if some error returned * * XMLNS attr entry may be added to the attrs Q * * RETURNS: * status *********************************************************************/ extern status_t xml_msg_check_xmlns_attr (xml_msg_hdr_t *msg, xmlns_id_t nsid, const xmlChar *badns, xml_attrs_t *attrs); /******************************************************************** * FUNCTION xml_msg_gen_xmlns_attrs * * Generate any xmlns directives in the top-level * attribute Q * * INPUTS: * msg == message in progress * attrs == xmlns_attrs_t Q to process * addncx == TRUE if an xmlns for the NCX prefix (for errors) * should be added to the element * FALSE if not * * OUTPUTS: * *attrs will be populated as needed, * * RETURNS: * status *********************************************************************/ extern status_t xml_msg_gen_xmlns_attrs (xml_msg_hdr_t *msg, xml_attrs_t *attrs, boolean addncx); /******************************************************************** * FUNCTION xml_msg_clean_defns_attr * * Get rid of an xmlns=foo default attribute * * INPUTS: * attrs == xmlns_attrs_t Q to process * * OUTPUTS: * *attrs will be cleaned as needed, * * RETURNS: * status *********************************************************************/ extern status_t xml_msg_clean_defns_attr (xml_attrs_t *attrs); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xml_msg */ yuma123_2.14/netconf/src/ncx/typ.c0000664000175000017500000037074314770023131017134 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: typ.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 12nov05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include "procdefs.h" #include "dlq.h" #include "ncxconst.h" #include "ncx.h" #include "ncx_appinfo.h" #include "ncx_feature.h" #include "ncx_num.h" #include "tk.h" #include "typ.h" #include "xml_util.h" #include "xpath.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static typ_template_t *basetypes[NCX_NUM_BASETYPES+1]; static boolean typ_init_done = FALSE; /******************************************************************** * FUNCTION clean_simple * * Clean a simple type struct contents, but do not delete it * * INPUTS: * sim == pointer to the typ_simple_t struct to clean *********************************************************************/ static void clean_simple (typ_simple_t *sim) { typ_enum_t *en; typ_unionnode_t *un; typ_rangedef_t *rv; typ_sval_t *sv; typ_pattern_t *pat; ncx_btype_t rtyp; /* range base type */ #ifdef DEBUG if (!sim) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (sim->range.rangestr) { m__free(sim->range.rangestr); sim->range.rangestr = NULL; } memset(&sim->range.tkerr, 0x0, sizeof(ncx_error_t)); if (sim->idref.baseprefix) { m__free(sim->idref.baseprefix); sim->idref.baseprefix = NULL; } if (sim->idref.basename) { m__free(sim->idref.basename); sim->idref.basename = NULL; } sim->idref.base = NULL; /* clean the rangeQ only if it is used */ if (!dlq_empty(&sim->range.rangeQ)) { rtyp = typ_get_range_type(sim->btyp); while (!dlq_empty(&sim->range.rangeQ)) { rv = (typ_rangedef_t *)dlq_deque(&sim->range.rangeQ); typ_free_rangedef(rv, rtyp); } } ncx_clean_errinfo(&sim->range.range_errinfo); /* clean the patternQ */ while (!dlq_empty(&sim->patternQ)) { pat = (typ_pattern_t *)dlq_deque(&sim->patternQ); typ_free_pattern(pat); } /* the Qs are used for differnt items, based on the type */ switch (sim->btyp) { case NCX_BT_BITS: case NCX_BT_ENUM: while (!dlq_empty(&sim->valQ)) { en = (typ_enum_t *)dlq_deque(&sim->valQ); typ_free_enum(en); } break; case NCX_BT_UNION: while (!dlq_empty(&sim->unionQ)) { un = (typ_unionnode_t *)dlq_deque(&sim->unionQ); typ_free_unionnode(un); } break; case NCX_BT_SLIST: break; default: /* this will be non-empty for enums and strings */ while (!dlq_empty(&sim->valQ)) { sv = (typ_sval_t *)dlq_deque(&sim->valQ); typ_free_sval(sv); } } if (sim->xleafref) { xpath_free_pcb(sim->xleafref); sim->xleafref = NULL; } sim->btyp = NCX_BT_NONE; sim->strrest = NCX_SR_NONE; sim->listtyp = NULL; } /* clean_simple */ /******************************************************************** * FUNCTION clean_named * * Clean a named type struct contents, but do not delete it * * INPUTS: * nam == pointer to the typ_named_t struct to clean *********************************************************************/ static void clean_named (typ_named_t *nam) { #ifdef DEBUG if (!nam) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif nam->typ = NULL; nam->flags = 0; if (nam->newtyp) { typ_free_typdef(nam->newtyp); nam->newtyp = NULL; } } /* clean_named */ /************* E X T E R N A L F U N C T I O N S *****************/ /******************************************************************** * FUNCTION typ_load_basetypes * * Create typ_template_t structs for the base types * Must be called before any modules are loaded * load the typ_template_t structs for the ncx_btype_t types * MUST be called during ncx_init startup * * RETURNS: * status *********************************************************************/ status_t typ_load_basetypes (void) { typ_template_t *typ; ncx_btype_t btyp; xmlns_id_t xsd_id; if (typ_init_done) { return NO_ERR; } xsd_id = xmlns_xs_id(); basetypes[NCX_BT_NONE] = NULL; for (btyp=NCX_FIRST_DATATYPE; btyp<=NCX_LAST_DATATYPE; btyp++) { basetypes[btyp] = NULL; } for (btyp=NCX_FIRST_DATATYPE; btyp<=NCX_LAST_DATATYPE; btyp++) { /* create a typ_template_t struct */ typ = typ_new_template(); if (!typ) { return SET_ERROR(ERR_INTERNAL_MEM); } /* fill in the essential fields */ typ->name = xml_strdup((const xmlChar *)tk_get_btype_sym(btyp)); if (!typ->name) { m__free(typ); return SET_ERROR(ERR_INTERNAL_MEM); } typ->typdef.iqual = NCX_IQUAL_ONE; typ->typdef.tclass = NCX_CL_BASE; typ->typdef.maxaccess = NCX_ACCESS_NONE; typ->typdef.def.base = btyp; typ->nsid = xsd_id; /* save the struct in the basetype queue */ basetypes[btyp] = typ; } typ_init_done = TRUE; return NO_ERR; } /* typ_load_basetypes */ /******************************************************************** * FUNCTION typ_unload_basetypes * * Unload and destroy the typ_template_t structs for the base types * unload the typ_template_t structs for the ncx_btype_t types * SHOULD be called during ncx_cleanup * *********************************************************************/ void typ_unload_basetypes (void) { typ_template_t *typ; ncx_btype_t btyp; if (!typ_init_done) { return; } for (btyp = NCX_FIRST_DATATYPE; btyp <= NCX_LAST_DATATYPE; btyp++) { typ = basetypes[btyp]; typ_free_template(typ); basetypes[btyp] = NULL; } typ_init_done = FALSE; } /* typ_unload_basetypes */ /******************************************************************** * FUNCTION typ_new_template * * Malloc and initialize the fields in a typ_template_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ typ_template_t * typ_new_template (void) { typ_template_t *typ; typ = m__getObj(typ_template_t); if (!typ) { return NULL; } (void)memset(typ, 0x0, sizeof(typ_template_t)); typ_init_typdef(&typ->typdef); typ->status = NCX_STATUS_CURRENT; dlq_createSQue(&typ->appinfoQ); return typ; } /* typ_new_template */ /******************************************************************** * FUNCTION typ_free_template * * Scrub the memory in a typ_template_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * typ == typ_template_t to delete *********************************************************************/ void typ_free_template (typ_template_t *typ) { #ifdef DEBUG if (!typ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* clean the typ_def_t struct */ typ_clean_typdef(&typ->typdef); if (typ->name) { m__free(typ->name); } if (typ->descr) { m__free(typ->descr); } if (typ->ref) { m__free(typ->ref); } if (typ->defval) { m__free(typ->defval); } if (typ->units) { m__free(typ->units); } ncx_clean_appinfoQ(&typ->appinfoQ); m__free(typ); } /* typ_free_template */ /******************************************************************** * FUNCTION typ_new_typdef * * Malloc and initialize the fields in a typ_def_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ typ_def_t * typ_new_typdef (void) { typ_def_t *typdef; typdef = m__getObj(typ_def_t); if (!typdef) { return NULL; } typ_init_typdef(typdef); return typdef; } /* typ_new_typdef */ /******************************************************************** * FUNCTION typ_init_typdef * * init a pre-allocated typdef (done first) * Initialize the fields in a typ_def_t * !! Still need to call typ_init_simple * !! when the actual builting type is determined * * INPUTS: * typdef == pointer to the struct to initialize *********************************************************************/ void typ_init_typdef (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif (void)memset(typdef, 0x0, sizeof(typ_def_t)); typdef->iqual = NCX_IQUAL_ONE; dlq_createSQue(&typdef->appinfoQ); } /* typ_init_typdef */ /******************************************************************** * FUNCTION typ_init_simple * * Init a typ_simple_t struct inside a typ_def_t * init a simple data type after typ_init_typdef * * INPUTS: * typdef == pointer to the typ_def_t struct to init * as a NCX_CL_SIMPLE variant *********************************************************************/ void typ_init_simple (typ_def_t *tdef, ncx_btype_t btyp) { typ_simple_t *sim; #ifdef DEBUG if (!tdef) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif tdef->iqual = NCX_IQUAL_ONE; tdef->tclass = NCX_CL_SIMPLE; if (btyp == NCX_BT_BITS) { tdef->mergetype = NCX_MERGE_SORT; } sim = &tdef->def.simple; sim->btyp = btyp; dlq_createSQue(&sim->range.rangeQ); ncx_init_errinfo(&sim->range.range_errinfo); dlq_createSQue(&sim->valQ); dlq_createSQue(&sim->metaQ); dlq_createSQue(&sim->unionQ); dlq_createSQue(&sim->patternQ); tdef->def.simple.strrest = NCX_SR_NONE; tdef->def.simple.flags = 0; } /* typ_init_simple */ /******************************************************************** * FUNCTION typ_init_named * * Init a typ_named_t struct inside a typ_def_t * init a named data type after typ_init_typdef * * INPUTS: * typdef == pointer to the typ_def_t struct to init * as a NCX_CL_SIMPLE variant *********************************************************************/ void typ_init_named (typ_def_t *tdef) { #ifdef DEBUG if (!tdef) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif tdef->tclass = NCX_CL_NAMED; tdef->def.named.typ = NULL; tdef->def.named.newtyp = NULL; } /* typ_init_named */ /******************************************************************** * Scrub the memory in a typ_def_t by freeing all Then free the typdef itself * * \param typdef typ_def_t to delete *********************************************************************/ void typ_free_typdef (typ_def_t *typdef) { if (!typdef) { return; } typ_clean_typdef(typdef); m__free(typdef); } /* typ_free_typdef */ /******************************************************************** * FUNCTION typ_clean_typdef * * Clean a typ_def_t struct, but do not delete it * * INPUTS: * typdef == pointer to the typ_def_t struct to clean *********************************************************************/ void typ_clean_typdef (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (typdef->prefix) { m__free(typdef->prefix); typdef->prefix = NULL; } if (typdef->typenamestr) { m__free(typdef->typenamestr); typdef->typenamestr = NULL; } ncx_clean_appinfoQ(&typdef->appinfoQ); switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: case NCX_CL_REF: break; case NCX_CL_SIMPLE: clean_simple(&typdef->def.simple); break; case NCX_CL_NAMED: clean_named(&typdef->def.named); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* typ_clean_typdef */ /******************************************************************** * FUNCTION typ_set_named_typdef * * Set the fields in a named typedef (used by YANG parser) * * INPUTS: * typdef == type def struct to set * imptyp == named type to set within 'typ.typdef' * *********************************************************************/ void typ_set_named_typdef (typ_def_t *typdef, typ_template_t *imptyp) { #ifdef DEBUG if (!typdef || !imptyp) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif typdef->tclass = NCX_CL_NAMED; typdef->def.named.typ = imptyp; typdef->linenum = imptyp->tkerr.linenum; } /* typ_set_named_typdef */ /******************************************************************** * FUNCTION typ_get_named_typename * * Get the type name of the named typ * * INPUTS: * typdef == pointer to the typ_def_t struct to check * * RETURNS: * pointer to type name, NULL if some error *********************************************************************/ const xmlChar * typ_get_named_typename (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typdef->tclass != NCX_CL_NAMED) { return NULL; } return (typdef->def.named.typ) ? typdef->def.named.typ->name : NULL; } /* typ_get_named_typename */ /******************************************************************** * FUNCTION typ_get_named_type_linenum * * Get the line number of the type template of the named type * * INPUTS: * typdef == pointer to the typ_def_t struct to check * * RETURNS: * pointer to type name, NULL if some error *********************************************************************/ uint32 typ_get_named_type_linenum (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif if (typdef->tclass != NCX_CL_NAMED) { return 0; } return (typdef->def.named.typ) ? typ_get_typ_linenum(typdef->def.named.typ) : 0; } /* typ_get_named_type_linenum */ /******************************************************************** * FUNCTION typ_set_new_named * * Create a new typdef inside a typ_named_t struct inside a typ_def_t * * INPUTS: * typdef == pointer to the typ_def_t struct to setup * btyp == builtin type (NCX_BT_NONE if local type extension) * * RETURNS: * status *********************************************************************/ status_t typ_set_new_named (typ_def_t *typdef, ncx_btype_t btyp) { typ_def_t *tdef; #ifdef DEBUG if (!typdef) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif tdef = typdef->def.named.newtyp = typ_new_typdef(); if (!tdef) { return ERR_INTERNAL_MEM; } /* initialize the new typdef with the parent base type */ typ_init_simple(tdef, btyp); return NO_ERR; } /* typ_set_new_named */ /******************************************************************** * FUNCTION typ_get_new_named * * Access the new typdef inside a typ_named_t struct inside a typ_def_t * * INPUTS: * typdef == pointer to the typ_def_t struct to check * * RETURNS: * pointer to new typ_def_t or NULL if none *********************************************************************/ typ_def_t * typ_get_new_named (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typdef->tclass != NCX_CL_NAMED) { return NULL; } return typdef->def.named.newtyp; } /* typ_get_new_named */ /******************************************************************** * FUNCTION typ_cget_new_named * * Access the new typdef inside a typ_named_t struct inside a typ_def_t * * INPUTS: * typdef == pointer to the typ_def_t struct to check * * RETURNS: * pointer to new typ_def_t or NULL if none *********************************************************************/ const typ_def_t * typ_cget_new_named (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typdef->tclass != NCX_CL_NAMED) { return NULL; } return typdef->def.named.newtyp; } /* typ_cget_new_named */ /******************************************************************** * FUNCTION typ_set_simple_typdef * * Set the fields in a simple typedef (used by YANG parser) * * INPUTS: * typ == type template to set * btyp == builtin type to set within 'typ.typdef' * *********************************************************************/ void typ_set_simple_typdef (typ_template_t *typ, ncx_btype_t btyp) { #ifdef DEBUG if (!typ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif typ->typdef.tclass = NCX_CL_SIMPLE; typ->typdef.def.simple.btyp = btyp; } /* typ_set_simple_typdef */ /******************************************************************** * FUNCTION typ_new_enum * * Alloc and Init a typ_enum_t struct * malloc and init an enumeration descriptor, strdup name ptr * * INPUTS: * name == name string for the enumeration * RETURNS: * pointer to malloced struct or NULL if memory error * Note that the enum integer value is initialized to zero *********************************************************************/ typ_enum_t * typ_new_enum (const xmlChar *name) { typ_enum_t *ev; #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif ev = m__getObj(typ_enum_t); if (!ev) { return NULL; } memset(ev, 0, sizeof(typ_enum_t)); ev->name = xml_strdup(name); if (!ev->name) { m__free(ev); return NULL; } dlq_createSQue(&ev->appinfoQ); dlq_createSQue(&ev->iffeatureQ); return ev; } /* typ_new_enum */ /******************************************************************** * FUNCTION typ_new_enum2 * * Alloc and Init a typ_enum_t struct * Use the string value as-=is, instead of mallocing a new one * malloc and init an enumeration descriptor, pass off name ptr * * INPUTS: * name == name string for the enumeration (will get free-ed later!!) * * RETURNS: * pointer to malloced struct or NULL if memory error * Note that the enum integer value is initialized to zero *********************************************************************/ typ_enum_t * typ_new_enum2 (xmlChar *name) { typ_enum_t *ev; #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif ev = m__getObj(typ_enum_t); if (!ev) { return NULL; } memset(ev, 0, sizeof(typ_enum_t)); ev->name = name; dlq_createSQue(&ev->appinfoQ); dlq_createSQue(&ev->iffeatureQ); return ev; } /* typ_new_enum2 */ /******************************************************************** * FUNCTION typ_free_enum * * Free a typ_enum_t struct * free an enumeration descriptor * * INPUTS: * en == enum struct to free *********************************************************************/ void typ_free_enum (typ_enum_t *en) { #ifdef DEBUG if (!en) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (en->name) { m__free(en->name); } if (en->descr) { m__free(en->descr); } if (en->ref) { m__free(en->ref); } ncx_clean_appinfoQ(&en->appinfoQ); ncx_clean_iffeatureQ(&en->iffeatureQ); m__free(en); } /* typ_free_enum */ /******************************************************************** * FUNCTION typ_new_rangedef * * Alloc and Init a typ_rangedef_t struct (range-stmt) * * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ typ_rangedef_t * typ_new_rangedef (void) { typ_rangedef_t *rv; rv = m__getObj(typ_rangedef_t); if (!rv) { return NULL; } memset(rv, 0, sizeof(typ_rangedef_t)); return rv; } /* typ_new_rangedef */ /******************************************************************** * FUNCTION typ_free_rangedef * * Free a typ_rangedef_t struct (range-stmt) * * INPUTS: * rv == rangeval struct to delete * btyp == base type of range (float and double have malloced strings) *********************************************************************/ void typ_free_rangedef (typ_rangedef_t *rv, ncx_btype_t btyp) { #ifdef DEBUG if (!rv) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (rv->rangestr) { m__free(rv->rangestr); } ncx_clean_num(btyp, &rv->lb); ncx_clean_num(btyp, &rv->ub); if (rv->lbstr) { m__free(rv->lbstr); } if (rv->ubstr) { m__free(rv->ubstr); } m__free(rv); } /* typ_free_rangedef */ /******************************************************************** * FUNCTION typ_normalize_rangeQ * * Start with a valid rangedef chain * Combine any consecutive range definitions like * 1..4|5|6|7..9 would break replaced with 1..9 * concat consecutive rangedef sections for integral numbers * * Not done for NCX_BT_FLOAT64 data types * * INPUTS: * rangeQ == Q of typ_rangeval_t structs to normalize * btyp == base type of range *********************************************************************/ void typ_normalize_rangeQ (dlq_hdr_t *rangeQ, ncx_btype_t btyp) { typ_rangedef_t *rv1, *rv2; boolean concat; #ifdef DEBUG if (!rangeQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif concat = FALSE; /* check if this range type can be normalized at all */ switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: /* range type OK, continue on */ break; case NCX_BT_DECIMAL64: /***********/ break; case NCX_BT_FLOAT64: /* cannot concat real numbers (by definition ;-) */ return; default: /* not a number type, internal error */ SET_ERROR(ERR_INTERNAL_VAL); return; } /* check empty rangeQ */ rv1 = (typ_rangedef_t *)dlq_firstEntry(rangeQ); if (!rv1) { return; } /* get next entry to check against first */ rv2 = (typ_rangedef_t *)dlq_nextEntry(rv1); while (rv2) { switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: if (rv1->ub.i+1 == rv2->lb.i) { rv1->ub.i = rv2->ub.i; concat = TRUE; } break; case NCX_BT_INT64: if (rv1->ub.l+1 == rv2->lb.l) { rv1->ub.l = rv2->ub.l; concat = TRUE; } break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: if (rv1->ub.u+1 == rv2->lb.u) { rv1->ub.u = rv2->ub.u; concat = TRUE; } break; case NCX_BT_UINT64: if (rv1->ub.ul+1 == rv2->lb.ul) { rv1->ub.ul = rv2->ub.ul; concat = TRUE; } break; default: ; } if (concat) { /* keep rv1 as the first rangedef to test */ dlq_remove(rv2); typ_free_rangedef(rv2, btyp); rv2 = (typ_rangedef_t *)dlq_nextEntry(rv1); concat = FALSE; } else { /* move along both rangedef pointers */ rv1 = rv2; rv2 = (typ_rangedef_t *)dlq_nextEntry(rv2); } } } /* typ_normalize_rangeQ */ /******************************************************************** * FUNCTION typ_get_rangeQ * * Return the rangeQ for the given typdef * Follow typdef chains if needed until first range found * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first rangedef struct or NULL if none *********************************************************************/ dlq_hdr_t * typ_get_rangeQ (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_BASE: return NULL; case NCX_CL_SIMPLE: return &typdef->def.simple.range.rangeQ; case NCX_CL_COMPLEX: return NULL; case NCX_CL_NAMED: if (typdef->def.named.newtyp && !dlq_empty(&typdef->def.named.newtyp->def.simple.range.rangeQ)) { return &typdef->def.named.newtyp->def.simple.range.rangeQ; } else { return (typdef->def.named.typ) ? typ_get_rangeQ(&typdef->def.named.typ->typdef) : NULL; } case NCX_CL_REF: return (typdef->def.ref.typdef) ? typ_get_rangeQ(typdef->def.ref.typdef) : NULL; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* typ_get_rangeQ */ /******************************************************************** * FUNCTION typ_get_rangeQ_con * * Return the rangeQ for the given typdef * Do not follow typdef chains * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the rangeQ from this typdef, or NULL if none *********************************************************************/ dlq_hdr_t * typ_get_rangeQ_con (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_BASE: return NULL; case NCX_CL_SIMPLE: return &typdef->def.simple.range.rangeQ; case NCX_CL_COMPLEX: return NULL; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { return &typdef->def.named.newtyp->def.simple.range.rangeQ; } else { return NULL; } /*NOTREACHED*/ case NCX_CL_REF: return NULL; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* typ_get_rangeQ_con */ /******************************************************************** * FUNCTION typ_get_crangeQ * * Return the rangeQ for the given typdef * Follow typdef chains if needed until first range found * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first rangedef struct or NULL if none *********************************************************************/ const dlq_hdr_t * typ_get_crangeQ (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_BASE: return NULL; case NCX_CL_SIMPLE: return &typdef->def.simple.range.rangeQ; case NCX_CL_COMPLEX: return NULL; case NCX_CL_NAMED: if (typdef->def.named.newtyp && !dlq_empty(&typdef->def.named.newtyp->def.simple.range.rangeQ)) { return &typdef->def.named.newtyp->def.simple.range.rangeQ; } else { return (typdef->def.named.typ) ? typ_get_crangeQ(&typdef->def.named.typ->typdef) : NULL; } /*NOTREACHED*/ case NCX_CL_REF: return (typdef->def.ref.typdef) ? typ_get_crangeQ(typdef->def.ref.typdef) : NULL; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* typ_get_crangeQ */ /******************************************************************** * FUNCTION typ_get_rangeQ_con * * Return the rangeQ for the given typdef * Do not follow typdef chains * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the rangeQ from this typdef, or NULL if none *********************************************************************/ const dlq_hdr_t * typ_get_crangeQ_con (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_BASE: return NULL; case NCX_CL_SIMPLE: return &typdef->def.simple.range.rangeQ; case NCX_CL_COMPLEX: return NULL; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { return &typdef->def.named.newtyp->def.simple.range.rangeQ; } else { return NULL; } /*NOTREACHED*/ case NCX_CL_REF: return NULL; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* typ_get_crangeQ_con */ /******************************************************************** * FUNCTION typ_get_range_con * * Return the range struct for the given typdef * Do not follow typdef chains * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the range struct for this typdef *********************************************************************/ typ_range_t * typ_get_range_con (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_BASE: return NULL; case NCX_CL_SIMPLE: return &typdef->def.simple.range; case NCX_CL_COMPLEX: return NULL; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { return &typdef->def.named.newtyp->def.simple.range; } else { return NULL; } /*NOTREACHED*/ case NCX_CL_REF: return NULL; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* typ_get_range_con */ /******************************************************************** * FUNCTION typ_get_crange_con * * Return the range struct for the given typdef * Do not follow typdef chains * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the range struct for this typdef *********************************************************************/ const typ_range_t * typ_get_crange_con (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_BASE: return NULL; case NCX_CL_SIMPLE: return &typdef->def.simple.range; case NCX_CL_COMPLEX: return NULL; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { return &typdef->def.named.newtyp->def.simple.range; } else { return NULL; } /*NOTREACHED*/ case NCX_CL_REF: return NULL; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* typ_get_crange_con */ /******************************************************************** * FUNCTION typ_get_rangestr * * Return the range string for the given typdef chain * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the range string for this typdef, NULL if none *********************************************************************/ const xmlChar * typ_get_rangestr (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_BASE: return NULL; case NCX_CL_SIMPLE: return typdef->def.simple.range.rangestr; case NCX_CL_COMPLEX: return NULL; case NCX_CL_NAMED: if (typdef->def.named.newtyp && typdef->def.named.newtyp->def.simple.range.rangestr) { return typdef->def.named.newtyp->def.simple.range.rangestr; } else { return (typdef->def.named.typ) ? typ_get_rangestr(&typdef->def.named.typ->typdef) : NULL; } case NCX_CL_REF: return (typdef->def.ref.typdef) ? typ_get_rangestr(typdef->def.ref.typdef) : NULL; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* typ_get_rangestr */ /******************************************************************** * FUNCTION typ_first_rangedef * * Return the lower bound range definition struct * Follow typdef chains if needed until first range found * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first rangedef struct or NULL if none *********************************************************************/ const typ_rangedef_t * typ_first_rangedef (const typ_def_t *typdef) { const dlq_hdr_t *rangeQ; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif rangeQ = typ_get_crangeQ(typdef); if (rangeQ) { return (const typ_rangedef_t *)dlq_firstEntry(rangeQ); } else { return NULL; } } /* typ_first_rangedef */ /******************************************************************** * FUNCTION typ_first_rangedef_con * * Return the lower bound range definition struct * Constain search to this typdef * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first rangedef struct or NULL if none *********************************************************************/ const typ_rangedef_t * typ_first_rangedef_con (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_BASE: return NULL; case NCX_CL_SIMPLE: return (const typ_rangedef_t *) dlq_firstEntry(&typdef->def.simple.range.rangeQ); case NCX_CL_COMPLEX: return NULL; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { return (const typ_rangedef_t *) dlq_firstEntry( &typdef->def.named.newtyp->def.simple.range.rangeQ); } else { return NULL; } case NCX_CL_REF: return NULL; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* typ_first_rangedef_con */ /******************************************************************** * FUNCTION typ_get_rangebounds_con * * Return the lower and upper bound range number * Constain search to this typdef * deprecated -- does not support multi-part ranges * * INPUTS: * typ_def == typ def struct to check * btyp == pointer to output range number type * lb == pointer to output lower bound number * ub == pointer to output upper bound number * * OUTPUTS: * *btyp == the type of number in the return type, if non-NULL * *lb == lower bound number * *ub == upper bound number * * RETURNS: * status, NO_ERR == something found *********************************************************************/ status_t typ_get_rangebounds_con (const typ_def_t *typdef, ncx_btype_t *btyp, const ncx_num_t **lb, const ncx_num_t **ub) { const typ_rangedef_t *rdef; status_t res; #ifdef DEBUG if (!typdef || !btyp || !lb || !ub) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; switch (typdef->tclass) { case NCX_CL_BASE: res = ERR_NCX_SKIPPED; break; case NCX_CL_SIMPLE: /* get lower bound */ rdef = (const typ_rangedef_t *) dlq_firstEntry(&typdef->def.simple.range.rangeQ); if (rdef ) { *btyp = rdef->btyp; *lb = &rdef->lb; /* get upper bound */ rdef = (const typ_rangedef_t *) dlq_lastEntry(&typdef->def.simple.range.rangeQ); if (rdef) { *ub = &rdef->ub; } else { res = SET_ERROR(ERR_INTERNAL_PTR); } } else { res = ERR_NCX_NOT_FOUND; } break; case NCX_CL_COMPLEX: res = ERR_NCX_WRONG_DATATYP; break; case NCX_CL_NAMED: /* same check as simple type if newtyp exists */ if (typdef->def.named.newtyp) { rdef = (const typ_rangedef_t *)dlq_firstEntry (&typdef->def.named.newtyp->def.simple.range.rangeQ); if (rdef ) { *btyp = rdef->btyp; *lb = &rdef->lb; rdef = (const typ_rangedef_t *)dlq_lastEntry (&typdef->def.named.newtyp->def.simple.range.rangeQ); if (rdef ) { *ub = &rdef->ub; } else { res = SET_ERROR(ERR_INTERNAL_PTR); } } else { res = ERR_NCX_NOT_FOUND; } } else { res = ERR_NCX_NOT_FOUND; } break; case NCX_CL_REF: res = ERR_NCX_NOT_FOUND; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* typ_get_rangebounds_con */ /******************************************************************** * FUNCTION typ_get_strrest * * Get the string restrinvtion type set for this typdef * * INPUTS: * typdef == typdef to check * * RETURNS: * string restrinction enumeration value *********************************************************************/ ncx_strrest_t typ_get_strrest (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_SR_NONE; } #endif switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: return NCX_SR_NONE; case NCX_CL_SIMPLE: return typdef->def.simple.strrest; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { return typdef->def.named.newtyp->def.simple.strrest; } else { return NCX_SR_NONE; } case NCX_CL_REF: if (typdef->def.ref.typdef) { return typ_get_strrest(typdef->def.ref.typdef); } else { return NCX_SR_NONE; } default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_SR_NONE; } /*NOTREACHED*/ } /* typ_get_strrest */ /******************************************************************** * FUNCTION typ_set_strrest * * Set the string restrinvtion type set for this typdef * * INPUTS: * typdef == typdef to check * strrest == string restriction enum value to set * *********************************************************************/ void typ_set_strrest (typ_def_t *typdef, ncx_strrest_t strrest) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: SET_ERROR(ERR_INTERNAL_VAL); break; case NCX_CL_SIMPLE: typdef->def.simple.strrest = strrest; break; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { typdef->def.named.newtyp->def.simple.strrest = strrest; } break; case NCX_CL_REF: default: SET_ERROR(ERR_INTERNAL_VAL); } } /* typ_set_strrest */ /******************************************************************** * FUNCTION typ_new_sval * * Alloc and Init a typ_sval_t struct * malloc and init a string descriptor * * INPUTS: * str == string value inside token to copy * btyp == type of string (NCX_BT_STRING/LIST, OSTRING/OLIST) * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ typ_sval_t * typ_new_sval (const xmlChar *str, ncx_btype_t btyp) { typ_sval_t *sv; #ifdef DEBUG if (!str) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (!typ_is_string(btyp)) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } sv = m__getObj(typ_sval_t); if (!sv) { return NULL; } memset(sv, 0, sizeof(typ_sval_t)); sv->val = xml_strdup(str); if (!sv->val) { m__free(sv); return NULL; } return sv; } /* typ_new_sval */ /******************************************************************** * FUNCTION typ_free_sval * * Free a typ_sval_t struct * free a string descriptor * * INPUTS: * sv == typ_sval_t struct to free *********************************************************************/ void typ_free_sval (typ_sval_t *sv) { #ifdef DEBUG if (!sv) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (sv->val) { m__free(sv->val); } m__free(sv); } /* typ_free_sval */ /******************************************************************** * FUNCTION typ_new_listval * * Alloc and Init a typ_listval_t struct * malloc and init a list descriptor * * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ typ_listval_t * typ_new_listval (void) { typ_listval_t *lv; lv = m__getObj(typ_listval_t); if (!lv) { return NULL; } memset(lv, 0, sizeof(typ_listval_t)); dlq_createSQue(&lv->strQ); return lv; } /* typ_new_listval */ /******************************************************************** * FUNCTION typ_free_listval * * Free a typ_listval_t struct * free a list descriptor * * INPUTS: * lv == typ_listval_t struct to free *********************************************************************/ void typ_free_listval (typ_listval_t *lv) { typ_sval_t *sv; #ifdef DEBUG if (!lv) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (!dlq_empty(&lv->strQ)) { sv = (typ_sval_t *)dlq_deque(&lv->strQ); typ_free_sval(sv); } m__free(lv); } /* typ_free_listval */ /******************************************************************** * FUNCTION typ_get_range_type * * Get the correct typ_rangedef_t data type for the * indicated base type * get the proper range base type to use for a given base type * * INPUTS: * btyp == base type enum * RETURNS: * base type enum of the range data type *********************************************************************/ ncx_btype_t typ_get_range_type (ncx_btype_t btyp) { /* figure out what type of number is in the rangeval Q */ switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: return btyp; case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_SLIST: /* length ranges have to be non-negative uint32 */ return NCX_BT_UINT32; default: /* the rest of the data types don't have ranges, but to simplify * cleanup, a value is returned, even though the rangeQ * will be empty during cleanup * Do not flag an error, but do not return a valid type either */ return NCX_BT_NONE; } /*NOTREACHED*/ } /* typ_get_range_type */ /******************************************************************** * FUNCTION typ_get_basetype * * Get the final base type of the specified typ_def_t * Follow any typdef links and get the actual base type of * the specified typedef * * INPUTS: * typdef == typdef to check * RETURNS: * base type of final typ_def_t *********************************************************************/ ncx_btype_t typ_get_basetype (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_BT_NONE; } #endif switch (typdef->tclass) { case NCX_CL_NONE: return NCX_BT_NONE; case NCX_CL_BASE: return typdef->def.base; case NCX_CL_SIMPLE: return typdef->def.simple.btyp; case NCX_CL_NAMED: if (typdef->def.named.typ) { return typ_get_basetype(&typdef->def.named.typ->typdef); } else { return NCX_BT_NONE; } case NCX_CL_REF: if (typdef->def.ref.typdef) { return typ_get_basetype(typdef->def.ref.typdef); } else { return NCX_BT_NONE; } default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_BT_NONE; } /*NOTREACHED*/ } /* typ_get_basetype */ /******************************************************************** * FUNCTION typ_get_name * * Get the name for the specified typdef * * INPUTS: * typdef == type definition to check * * RETURNS: * type name or empty string if some error *********************************************************************/ const xmlChar * typ_get_name (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NONE: SET_ERROR(ERR_INTERNAL_VAL); return EMPTY_STRING; case NCX_CL_BASE: return (const xmlChar *)tk_get_btype_sym(typdef->def.base); case NCX_CL_SIMPLE: return (const xmlChar *) tk_get_btype_sym(typdef->def.simple.btyp); case NCX_CL_NAMED: return (typdef->def.named.typ) ? typdef->def.named.typ->name : NULL; case NCX_CL_REF: return (typdef->def.ref.typdef) ? typ_get_name(typdef->def.ref.typdef) : NULL; default: SET_ERROR(ERR_INTERNAL_VAL); return EMPTY_STRING; } /*NOTREACHED*/ } /* typ_get_name */ /******************************************************************** * FUNCTION typ_get_basetype_name * * Get the name of the final base type of the specified typ_template_t * * INPUTS: * typ == template containing the typdef to check * * RETURNS: * base type name of final embedded typ_def_t *********************************************************************/ const xmlChar * typ_get_basetype_name (const typ_template_t *typ) { ncx_btype_t btyp; #ifdef DEBUG if (!typ) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif btyp = typ_get_basetype(&typ->typdef); if (btyp != NCX_BT_NONE) { return (const xmlChar *)tk_get_btype_sym(btyp); } else { return EMPTY_STRING; } /*NOTREACHED*/ } /* typ_get_basetype_name */ /******************************************************************** * FUNCTION typ_get_parenttype_name * * Get the final base type of the specified typ_def_t * * INPUTS: * typdef == typdef to check * RETURNS: * base type of final typ_def_t *********************************************************************/ const xmlChar * typ_get_parenttype_name (const typ_template_t *typ) { #ifdef DEBUG if (!typ) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typ->typdef.tclass == NCX_CL_NAMED) { return (typ->typdef.def.named.typ) ? typ->typdef.def.named.typ->name : EMPTY_STRING; } else { return EMPTY_STRING; } /*NOTREACHED*/ } /* typ_get_parenttype_name */ /******************************************************************** * FUNCTION typ_get_base_class * * Follow any typdef links and get the class of the base typdef * for the specified typedef * * INPUTS: * typdef == typdef to check * RETURNS: * base class of final typ_def_t *********************************************************************/ ncx_tclass_t typ_get_base_class (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_CL_NONE; } #endif switch (typdef->tclass) { case NCX_CL_NONE: return NCX_CL_NONE; case NCX_CL_BASE: case NCX_CL_SIMPLE: case NCX_CL_COMPLEX: return typdef->tclass; case NCX_CL_NAMED: if (typdef->def.named.typ) { return typ_get_base_class(&typdef->def.named.typ->typdef); } else { return NCX_CL_NAMED; } case NCX_CL_REF: return typ_get_base_class(typdef->def.ref.typdef); default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_CL_NONE; } /*NOTREACHED*/ } /* typ_get_base_class */ /******************************************************************** * FUNCTION typ_get_basetype_typ * * Get the default typ_template_t for the specified base type * Get the default type template for the specified base type * * INPUTS: * btyp == base type to get * RETURNS: * pointer to the type template for the specified basetype *********************************************************************/ typ_template_t * typ_get_basetype_typ (ncx_btype_t btyp) { if (!typ_init_done) { SET_ERROR(ERR_INTERNAL_INIT_SEQ); return NULL; } if (btypNCX_LAST_DATATYPE) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } return basetypes[btyp]; } /* typ_get_basetype_typ */ /******************************************************************** * FUNCTION typ_get_basetype_typdef * * Get the default typdef for the specified base type * * INPUTS: * btyp == base type to get * RETURNS: * pointer to the typdef for the specified basetype *********************************************************************/ typ_def_t * typ_get_basetype_typdef (ncx_btype_t btyp) { if (!typ_init_done) { SET_ERROR(ERR_INTERNAL_INIT_SEQ); return NULL; } if (btypNCX_LAST_DATATYPE) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } return &basetypes[btyp]->typdef; } /* typ_get_basetype_typdef */ /******************************************************************** * FUNCTION typ_get_parent_typdef * * Get the next typ_def_t in a chain -- for NCX_CL_NAMED chained typed * Also NCX_CL_REF pointer typdefs * Ignores current named type even if if has new restrictions * Get the parent typdef for NCX_CL_NAMED and NCX_CL_REF * Returns NULL for all other classes * * INPUTS: * typdef == typdef to check * RETURNS: * pointer to next non-empty typ_def_t *********************************************************************/ typ_def_t * typ_get_parent_typdef (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: case NCX_CL_SIMPLE: case NCX_CL_COMPLEX: return NULL; case NCX_CL_NAMED: if (typdef->def.named.typ) { return &typdef->def.named.typ->typdef; } else { return NULL; } case NCX_CL_REF: return typdef->def.ref.typdef; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* typ_get_parent_typdef */ /******************************************************************** * FUNCTION typ_get_parent_type * * Get the next typ_template_t in a chain -- for NCX_CL_NAMED only * * INPUTS: * typ == type template to check * RETURNS: * pointer to next non-empty typ_template_t for a named type *********************************************************************/ const typ_template_t * typ_get_parent_type (const typ_template_t *typ) { #ifdef DEBUG if (!typ) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typ->typdef.tclass) { case NCX_CL_NONE: case NCX_CL_BASE: case NCX_CL_SIMPLE: case NCX_CL_COMPLEX: return NULL; case NCX_CL_NAMED: return typ->typdef.def.named.typ; case NCX_CL_REF: return NULL; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* typ_get_parent_type */ /******************************************************************** * FUNCTION typ_get_cparent_typdef * * Get the next typ_def_t in a chain -- for NCX_CL_NAMED chained typed * Also NCX_CL_REF pointer typdefs * Ignores current named type even if if has new restrictions * INPUTS: * typdef == typdef to check * RETURNS: * pointer to next non-empty typ_def_t *********************************************************************/ const typ_def_t * typ_get_cparent_typdef (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: case NCX_CL_SIMPLE: case NCX_CL_COMPLEX: return NULL; case NCX_CL_NAMED: return (typdef->def.named.typ) ? &typdef->def.named.typ->typdef : NULL; case NCX_CL_REF: return typdef->def.ref.typdef; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* typ_get_cparent_typdef */ /******************************************************************** * FUNCTION typ_get_next_typdef * * Get the next typ_def_t in a chain -- for NCX_CL_NAMED chained typed * Also NCX_CL_REF pointer typdefs * Get the next typdef in the chain for NCX_CL_NAMED or NCX_CL_REF * Returns the input typdef for all other typdef classes * * INPUTS: * typdef == typdef to check * RETURNS: * pointer to next non-empty typ_def_t *********************************************************************/ typ_def_t * typ_get_next_typdef (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: case NCX_CL_SIMPLE: case NCX_CL_COMPLEX: return typdef; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { return typdef; } else { return (typdef->def.named.typ) ? typ_get_next_typdef(&typdef->def.named.typ->typdef) : NULL; } case NCX_CL_REF: return typdef->def.ref.typdef; default: SET_ERROR(ERR_INTERNAL_VAL); return typdef; } /*NOTREACHED*/ } /* typ_get_next_typdef */ /******************************************************************** * FUNCTION typ_get_base_typdef * * Get the base typ_def_t in a chain -- for NCX_CL_NAMED chained typed * Also NCX_CL_REF pointer typdefs * get the real typdef that describes the type, if the * input is one of the 'pointer' typdef classes. Otherwise, * just return the input typdef * * INPUTS: * typdef == typdef to check * RETURNS: * pointer to base typ_def_t *********************************************************************/ typ_def_t * typ_get_base_typdef (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: case NCX_CL_SIMPLE: case NCX_CL_COMPLEX: return typdef; case NCX_CL_NAMED: return (typdef->def.named.typ) ? typ_get_base_typdef(&typdef->def.named.typ->typdef) : NULL; case NCX_CL_REF: return typdef->def.ref.typdef; default: SET_ERROR(ERR_INTERNAL_VAL); return typdef; } /*NOTREACHED*/ } /* typ_get_base_typdef */ /******************************************************************** * FUNCTION typ_get_cbase_typdef * * Get the base typ_def_t in a chain -- for NCX_CL_NAMED chained typed * Also NCX_CL_REF pointer typdefs * * INPUTS: * typdef == typdef to check * RETURNS: * pointer to base typ_def_t *********************************************************************/ const typ_def_t * typ_get_cbase_typdef (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: case NCX_CL_SIMPLE: case NCX_CL_COMPLEX: return typdef; case NCX_CL_NAMED: return (typdef->def.named.typ) ? typ_get_cbase_typdef(&typdef->def.named.typ->typdef) : NULL; case NCX_CL_REF: return typdef->def.ref.typdef; default: SET_ERROR(ERR_INTERNAL_VAL); return typdef; } /*NOTREACHED*/ } /* typ_get_cbase_typdef */ /******************************************************************** * FUNCTION typ_get_qual_typdef * * Get the final typ_def_t of the specified typ_def_t * based on the qualifier * Get the next typdef in the chain for NCX_CL_NAMED or NCX_CL_REF * Skip any named types without the specific restriction defined * * Returns the input typdef for simple typdef classes * * INPUTS: * typdef == typdef to check * squal == type of search qualifier desired * NCX_SQUAL_NONE == get first non-empty typdef * NCX_SQUAL_RANGE == find the first w/ range definition * NCX_SQUAL_VAL == find the first w/ stringval/pattern def * NCX_SQUAL_META == find the first typdef w/ meta-data def * * RETURNS: * pointer to found typ_def_t or NULL if none found *********************************************************************/ typ_def_t * typ_get_qual_typdef (typ_def_t *typdef, ncx_squal_t squal) { typ_def_t *ntypdef; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NONE: return NULL; case NCX_CL_BASE: if (squal==NCX_SQUAL_NONE) { return typdef; } else { return NULL; } case NCX_CL_SIMPLE: switch (squal) { case NCX_SQUAL_NONE: return typdef; case NCX_SQUAL_RANGE: return (dlq_empty(&typdef->def.simple.range.rangeQ)) ? NULL : typdef; case NCX_SQUAL_VAL: return (dlq_empty(&typdef->def.simple.valQ)) ? NULL : typdef; case NCX_SQUAL_META: return (dlq_empty(&typdef->def.simple.metaQ)) ? NULL : typdef; case NCX_SQUAL_APPINFO: return (dlq_empty(&typdef->appinfoQ)) ? NULL : typdef; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ case NCX_CL_NAMED: ntypdef = typdef->def.named.newtyp; if (!ntypdef) { return (typdef->def.named.typ) ? typ_get_qual_typdef(&typdef->def.named.typ->typdef, squal) : NULL; } switch (squal) { case NCX_SQUAL_NONE: return typdef; case NCX_SQUAL_RANGE: if (!dlq_empty(&ntypdef->def.simple.range.rangeQ)) { return typdef; } break; case NCX_SQUAL_VAL: if (!dlq_empty(&ntypdef->def.simple.valQ)) { return typdef; } break; case NCX_SQUAL_META: if (!dlq_empty(&ntypdef->def.simple.metaQ)) { return typdef; } break; case NCX_SQUAL_APPINFO: if (!dlq_empty(&typdef->appinfoQ)) { return typdef; } break; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } return (typdef->def.named.typ) ? typ_get_qual_typdef(&typdef->def.named.typ->typdef, squal) : NULL; case NCX_CL_REF: return typ_get_qual_typdef(typdef->def.ref.typdef, squal); default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* typ_get_qual_typdef */ /******************************************************************** * FUNCTION typ_get_cqual_typdef * * Get the final typ_def_t of the specified typ_def_t * based on the qualifier * INPUTS: * typdef == typdef to check * squal == type of search qualifier desired * NCX_SQUAL_NONE == get first non-empty typdef * NCX_SQUAL_RANGE == find the first w/ range definition * NCX_SQUAL_VAL == find the first w/ stringval/pattern def * NCX_SQUAL_META == find the first typdef w/ meta-data def * * RETURNS: * pointer to found typ_def_t or NULL if none found *********************************************************************/ const typ_def_t * typ_get_cqual_typdef (const typ_def_t *typdef, ncx_squal_t squal) { const typ_def_t *ntypdef; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NONE: return NULL; case NCX_CL_BASE: if (squal==NCX_SQUAL_NONE) { return typdef; } else { return NULL; } case NCX_CL_SIMPLE: switch (squal) { case NCX_SQUAL_NONE: return typdef; case NCX_SQUAL_RANGE: return (dlq_empty(&typdef->def.simple.range.rangeQ)) ? NULL : typdef; case NCX_SQUAL_VAL: return (dlq_empty(&typdef->def.simple.valQ)) ? NULL : typdef; case NCX_SQUAL_META: return (dlq_empty(&typdef->def.simple.metaQ)) ? NULL : typdef; case NCX_SQUAL_APPINFO: return (dlq_empty(&typdef->appinfoQ)) ? NULL : typdef; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ case NCX_CL_NAMED: ntypdef = typdef->def.named.newtyp; if (!ntypdef) { return (typdef->def.named.typ) ? typ_get_cqual_typdef(&typdef->def.named.typ->typdef, squal) : NULL; } switch (squal) { case NCX_SQUAL_NONE: return typdef; case NCX_SQUAL_RANGE: if (!dlq_empty(&ntypdef->def.simple.range.rangeQ)) { return typdef; } break; case NCX_SQUAL_VAL: if (!dlq_empty(&ntypdef->def.simple.valQ)) { return typdef; } break; case NCX_SQUAL_META: if (!dlq_empty(&ntypdef->def.simple.metaQ)) { return typdef; } break; case NCX_SQUAL_APPINFO: if (!dlq_empty(&typdef->appinfoQ)) { return typdef; } break; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } return (typdef->def.named.typ) ? typ_get_cqual_typdef(&typdef->def.named.typ->typdef, squal) : NULL; case NCX_CL_REF: return typ_get_cqual_typdef(typdef->def.ref.typdef, squal); default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* typ_get_cqual_typdef */ /******************************************************************** * FUNCTION typ_find_appinfo * * Find the specified appinfo variable by its prefix and name * * INPUTS: * typdef == typedef to check * prefix == module prefix (may be NULL) * name == name of the appinfo var to find * * RETURNS: * pointer to found appinfo struct or NULL if not found *********************************************************************/ const ncx_appinfo_t * typ_find_appinfo (const typ_def_t *typdef, const xmlChar *prefix, const xmlChar *name) { const typ_def_t *appdef; const ncx_appinfo_t *appinfo; boolean done; #ifdef DEBUG if (!typdef || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif done = FALSE; appinfo = NULL; while (!done) { appdef = typ_get_cqual_typdef(typdef, NCX_SQUAL_APPINFO); if (appdef) { appinfo = ncx_find_const_appinfo(&appdef->appinfoQ, prefix, name); if (appinfo) { done = TRUE; } else if (appdef->tclass == NCX_CL_NAMED && appdef->def.named.typ) { typdef = &appdef->def.named.typ->typdef; } else { done = TRUE; } } else { done = TRUE; } } return appinfo; } /* typ_find_appinfo */ /******************************************************************** * FUNCTION typ_find_appinfo_con * * Find the specified appinfo name, constrained to the current typdef * * INPUTS: * typdef == typedef to check * prefix == appinfo module prefix (may be NULL) * name == name of the appinfo var to find * * RETURNS: * pointer to found appinfo struct or NULL if not found *********************************************************************/ const ncx_appinfo_t * typ_find_appinfo_con (const typ_def_t *typdef, const xmlChar *prefix, const xmlChar *name) { #ifdef DEBUG if (!typdef || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return ncx_find_const_appinfo(&typdef->appinfoQ, prefix, name); } /* typ_find_appinfo_con */ /******************************************************************** * FUNCTION typ_is_xpath_string * * Find the ncx:xpath extension within the specified typdef chain * * INPUTS: * typdef == start of typ_def_t chain to check * * RETURNS: * TRUE if ncx:xpath extension found * FALSE otherwise *********************************************************************/ boolean typ_is_xpath_string (const typ_def_t *typdef) { const typ_template_t *test_typ; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (typ_get_basetype(typdef) == NCX_BT_INSTANCE_ID) { return TRUE; } if (ncx_find_const_appinfo(&typdef->appinfoQ, NCX_PREFIX, NCX_EL_XPATH)) { return TRUE; } if (typdef->tclass == NCX_CL_NAMED) { if (typdef->def.named.newtyp && ncx_find_const_appinfo(&typdef->def.named.newtyp->appinfoQ, NCX_PREFIX, NCX_EL_XPATH)) { return TRUE; } if (typdef->def.named.typ) { test_typ = typdef->def.named.typ; /* hardwire the YANG xpath1.0 type */ if (!xml_strcmp(test_typ->tkerr.mod->name, (const xmlChar *)"ietf-yang-types") && !xml_strcmp(test_typ->name, (const xmlChar *)"xpath1.0")) { return TRUE; } return typ_is_xpath_string(&test_typ->typdef); } else { return FALSE; } } else { return FALSE; } } /* typ_is_xpath_string */ /******************************************************************** * FUNCTION typ_is_qname_string * * Find the ncx:qname extension within the specified typdef chain * * INPUTS: * typdef == start of typ_def_t chain to check * * RETURNS: * TRUE if ncx:qname extension found * FALSE otherwise *********************************************************************/ boolean typ_is_qname_string (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (ncx_find_const_appinfo(&typdef->appinfoQ, NCX_PREFIX, NCX_EL_QNAME)) { return TRUE; } if (typdef->tclass == NCX_CL_NAMED) { if (typdef->def.named.newtyp && ncx_find_const_appinfo(&typdef->def.named.newtyp->appinfoQ, NCX_PREFIX, NCX_EL_QNAME)) { return TRUE; } if (typdef->def.named.typ) { return typ_is_qname_string(&typdef->def.named.typ->typdef); } else { return FALSE; } } else { return FALSE; } } /* typ_is_qname_string */ /******************************************************************** * FUNCTION typ_is_schema_instance_string * * Find the ncx:schema-instance extension within * the specified typdef chain * * INPUTS: * typdef == start of typ_def_t chain to check * * RETURNS: * TRUE if ncx:schema-instance extension found * FALSE otherwise *********************************************************************/ boolean typ_is_schema_instance_string (const typ_def_t *typdef) { const typ_template_t *test_typ; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (typ_get_basetype(typdef) != NCX_BT_STRING) { return FALSE; } if (ncx_find_const_appinfo(&typdef->appinfoQ, NCX_PREFIX, NCX_EL_SCHEMA_INSTANCE)) { return TRUE; } if (typdef->tclass == NCX_CL_NAMED) { if (typdef->def.named.newtyp && ncx_find_const_appinfo(&typdef->def.named.newtyp->appinfoQ, NCX_PREFIX, NCX_EL_SCHEMA_INSTANCE)) { return TRUE; } if (typdef->def.named.typ) { test_typ = typdef->def.named.typ; /* hardwire the nacm schema-instance-identifier type */ if (!xml_strcmp(test_typ->name, YANG_SII_STRING)) { return TRUE; } return typ_is_schema_instance_string(&test_typ->typdef); } else { return FALSE; } } else { return FALSE; } } /* typ_is_schema_instance_string */ /******************************************************************** * FUNCTION typ_get_defval * * Find the default value string for the specified type template * get default from template * * INPUTS: * typ == typ_template_t struct to check * RETURNS: * pointer to found defval string or NULL if none *********************************************************************/ const xmlChar * typ_get_defval (const typ_template_t *typ) { #ifdef DEBUG if (!typ) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typ->defval) { return typ->defval; } if (typ->typdef.tclass == NCX_CL_NAMED) { return (typ->typdef.def.named.typ) ? typ_get_defval(typ->typdef.def.named.typ) : NULL; } else { /* no check for NCX_CL_REF because only type templates * have default values, not embedded typdefs */ return NULL; } } /* typ_get_defval */ /******************************************************************** * FUNCTION typ_get_default * * Check if this typdef has a default value defined * get default from typdef * * INPUTS: * typdef == typ_def_t struct to check * RETURNS: * pointer to default or NULL if there is none *********************************************************************/ const xmlChar * typ_get_default (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typdef->tclass == NCX_CL_NAMED) { return (typdef->def.named.typ) ? typ_get_defval(typdef->def.named.typ) : NULL; } else { /* Unless an embedded data node is a named type * with a simple base type, it cannot have a default * !!! Not sure this always applies to NCX_CL_REF !!! */ return NULL; } } /* typ_get_default */ /******************************************************************** * FUNCTION typ_get_iqualval * * Find the instance qualifier value enum for the specified type template * * INPUTS: * typ == typ_template_t struct to check * RETURNS: * iqual value enum *********************************************************************/ ncx_iqual_t typ_get_iqualval (const typ_template_t *typ) { #ifdef DEBUG if (!typ) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_IQUAL_NONE; } #endif return typ_get_iqualval_def(&typ->typdef); } /* typ_get_iqualval */ /******************************************************************** * FUNCTION typ_get_iqualval_def * * Find the instance qualifier value enum for the specified type template * * INPUTS: * typdef == typ_def_t struct to check * RETURNS: * iqual value enum *********************************************************************/ ncx_iqual_t typ_get_iqualval_def (const typ_def_t *typdef) { ncx_btype_t btyp; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_IQUAL_NONE; } #endif switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: case NCX_CL_SIMPLE: case NCX_CL_COMPLEX: btyp = typ_get_basetype(typdef); if (btyp == NCX_BT_LIST && typdef->iqual==NCX_IQUAL_ONE) { return NCX_IQUAL_ZMORE; } else { return typdef->iqual; } case NCX_CL_NAMED: if (typdef->iqual != NCX_IQUAL_ONE) { return typdef->iqual; } else { return (typdef->def.named.typ) ? typ_get_iqualval(typdef->def.named.typ) : NCX_IQUAL_NONE; } case NCX_CL_REF: if (typdef->iqual != NCX_IQUAL_ONE) { return typdef->iqual; } else { return typdef->def.ref.typdef->iqual; } default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_IQUAL_NONE; } } /* typ_get_iqualval_def */ /******************************************************************** * FUNCTION typ_get_units * * Find the units string for the specified type template * * INPUTS: * typ == typ_template_t struct to check * RETURNS: * pointer to found units string or NULL if none *********************************************************************/ const xmlChar * typ_get_units (const typ_template_t *typ) { #ifdef DEBUG if (!typ) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typ->units) { return typ->units; } return typ_get_units_from_typdef(&typ->typdef); } /* typ_get_units */ /******************************************************************** * FUNCTION typ_get_units_from_typdef * * Find the units string for the specified typdef template * Follow any NCX_CL_NAMED typdefs and check for a units * clause in the the nearest ancestor typdef * get units from named type if any * * INPUTS: * typdef == typ_def_t struct to check * * RETURNS: * pointer to found units string or NULL if none *********************************************************************/ const xmlChar * typ_get_units_from_typdef (const typ_def_t *typdef) { const typ_template_t *typ; boolean done; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typdef->tclass != NCX_CL_NAMED) { return NULL; } done = FALSE; while (!done) { typ = typdef->def.named.typ; if (typ->units) { return typ->units; } typdef = &typ->typdef; if (typdef->tclass != NCX_CL_NAMED) { done = TRUE; } } return NULL; } /* typ_get_units_from_typdef */ /******************************************************************** * FUNCTION typ_has_children * * Check if this is a data type that uses the val.v.childQ * * INPUTS: * btyp == base type enum * * RETURNS: * TRUE if the childQ is used, FALSE otherwise *********************************************************************/ boolean typ_has_children (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_CASE: case NCX_BT_LIST: case NCX_BT_ANYDATA: case NCX_BT_ANYXML: return TRUE; default: return FALSE; } } /* typ_has_children */ /******************************************************************** * FUNCTION typ_has_index * * Check if this is a data type that has an index * * INPUTS: * btyp == base type enum * * RETURNS: * TRUE if the indexQ is used, FALSE otherwise *********************************************************************/ boolean typ_has_index (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_LIST: return TRUE; default: return FALSE; } } /* typ_has_index */ /******************************************************************** * FUNCTION typ_is_simple * * Check if this is a simple data type * * INPUTS: * btyp == base type enum * * RETURNS: * TRUE if this is a simple data type, FALSE otherwise *********************************************************************/ boolean typ_is_simple (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_ANYDATA: case NCX_BT_ANYXML: return FALSE; case NCX_BT_BITS: case NCX_BT_ENUM: case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_INSTANCE_ID: case NCX_BT_UNION: case NCX_BT_LEAFREF: case NCX_BT_IDREF: case NCX_BT_SLIST: return TRUE; case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_CASE: case NCX_BT_LIST: case NCX_BT_EXTERN: case NCX_BT_INTERN: return FALSE; default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* typ_is_simple */ /******************************************************************** * FUNCTION typ_is_xsd_simple * * Check if this is a simple data type in XSD encoding * * INPUTS: * btyp == base type enum * * RETURNS: * TRUE if this is a simple data type, FALSE otherwise *********************************************************************/ boolean typ_is_xsd_simple (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_EMPTY: return FALSE; case NCX_BT_BITS: case NCX_BT_BOOLEAN: case NCX_BT_ENUM: case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_UNION: case NCX_BT_SLIST: case NCX_BT_LEAFREF: case NCX_BT_IDREF: case NCX_BT_INSTANCE_ID: return TRUE; case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_CASE: case NCX_BT_LIST: case NCX_BT_EXTERN: case NCX_BT_INTERN: return FALSE; default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* typ_is_xsd_simple */ /******************************************************************** * FUNCTION typ_first_enumdef * * Get the first enum def struct * looks past named typedefs to base typedef * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first enum def of NULL if none *********************************************************************/ typ_enum_t * typ_first_enumdef (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typdef->tclass != NCX_CL_SIMPLE) { return NULL; } return (typ_enum_t *) dlq_firstEntry(&typdef->def.simple.valQ); } /* typ_first_enumdef */ /******************************************************************** * FUNCTION typ_next_enumdef * * Get the next enum def struct * * INPUTS: * enumdef == typ enum struct to check * * RETURNS: * pointer to the first enum def of NULL if none *********************************************************************/ typ_enum_t * typ_next_enumdef (typ_enum_t *enumdef) { #ifdef DEBUG if (!enumdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (typ_enum_t *)dlq_nextEntry(enumdef); } /* typ_next_enumdef */ /******************************************************************** * FUNCTION typ_first_enumdef2 * * Get the first enum def struct * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first enum def of NULL if none *********************************************************************/ typ_enum_t * typ_first_enumdef2 (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typdef->tclass != NCX_CL_SIMPLE) { return NULL; } return (typ_enum_t *) dlq_firstEntry(&typdef->def.simple.valQ); } /* typ_first_enumdef2 */ /******************************************************************** * FUNCTION typ_first_con_enumdef * * Get the first enum def struct * constrained to this typdef * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first enum def of NULL if none *********************************************************************/ const typ_enum_t * typ_first_con_enumdef (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_SIMPLE: break; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { typdef = typdef->def.named.newtyp; } else { return NULL; } break; default: return NULL; } return (const typ_enum_t *) dlq_firstEntry(&typdef->def.simple.valQ); } /* typ_first_con_enumdef */ /******************************************************************** * FUNCTION typ_find_enumdef * * Get the specified enum def struct * * INPUTS: * ebQ == enum/bits Q to check * name == name of the enum to find * RETURNS: * pointer to the specified enum def of NULL if none *********************************************************************/ typ_enum_t * typ_find_enumdef (dlq_hdr_t *ebQ, const xmlChar *name) { typ_enum_t *en; #ifdef DEBUG if (!ebQ || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (en = (typ_enum_t *)dlq_firstEntry(ebQ); en != NULL; en = (typ_enum_t *)dlq_nextEntry(en)) { if (!xml_strcmp(en->name, name)) { return en; } } return NULL; } /* typ_find_enumdef */ /******************************************************************** * FUNCTION typ_enumdef_count * * Get the number of typ_enum_t Q entries * * INPUTS: * typdef == typ def struct to check * * RETURNS: * number of entries *********************************************************************/ uint32 typ_enumdef_count (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif if (typdef->tclass != NCX_CL_SIMPLE) { return 0; } return dlq_count(&typdef->def.simple.valQ); } /* typ_enumdef_count */ /******************************************************************** * FUNCTION typ_first_strdef * * Get the first string def struct * * INPUTS: * typdef == typ def struct to check * * RETURNS: * pointer to the first string def of NULL if none *********************************************************************/ const typ_sval_t * typ_first_strdef (const typ_def_t *typdef) { ncx_btype_t btyp; const typ_sval_t *retval; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif retval = NULL; btyp = typ_get_basetype(typdef); if (!typ_is_string(btyp)) { return NULL; } switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: break; case NCX_CL_SIMPLE: retval = (const typ_sval_t *) dlq_firstEntry(&typdef->def.simple.valQ); break; case NCX_CL_COMPLEX: break; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { retval = typ_first_strdef(typdef->def.named.newtyp); } break; case NCX_CL_REF: /**** !!! SHOULD THIS BE NULL INSTEAD !!! ****/ retval = typ_first_strdef(typdef->def.ref.typdef); break; default: SET_ERROR(ERR_INTERNAL_VAL); } return retval; } /* typ_first_strdef */ /******************************************************************** * FUNCTION typ_get_maxrows * * Get the maxrows value if it exists or zero if not * * INPUTS: * typdef == typdef to check * RETURNS: * max number of rows or zero if not applicable *********************************************************************/ uint32 typ_get_maxrows (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: case NCX_CL_SIMPLE: return 0; case NCX_CL_NAMED: return (typdef->def.named.typ) ? typ_get_maxrows(&typdef->def.named.typ->typdef) : 0; case NCX_CL_REF: return typ_get_maxrows(typdef->def.ref.typdef); default: SET_ERROR(ERR_INTERNAL_VAL); return 0; } /*NOTREACHED*/ } /* typ_get_maxrows */ /******************************************************************** * FUNCTION typ_get_maxaccess * * Find the max-access value for the specified typdef * Follow named types to see if any parent typdef has a * maxaccess clause, if none found in the parameter * * INPUTS: * typdef == typ_def_t struct to check * RETURNS: * maxaccess enumeration *********************************************************************/ ncx_access_t typ_get_maxaccess (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_ACCESS_NONE; } #endif if (typdef->maxaccess != NCX_ACCESS_NONE) { return typdef->maxaccess; } switch (typdef->tclass) { case NCX_CL_BASE: case NCX_CL_SIMPLE: case NCX_CL_COMPLEX: return NCX_ACCESS_NONE; case NCX_CL_NAMED: return (typdef->def.named.typ) ? typ_get_maxaccess(&typdef->def.named.typ->typdef) : NCX_ACCESS_NONE; case NCX_CL_REF: return typ_get_maxaccess(typdef->def.ref.typdef); default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_ACCESS_NONE; } /*NOTREACHED*/ } /* typ_get_maxaccess */ /******************************************************************** * FUNCTION typ_get_dataclass * * Find the data-class value for the specified typdef * Follow named types to see if any parent typdef has a * data-class clause, if none found in the parameter * * INPUTS: * typdef == typ_def_t struct to check * RETURNS: * data class enumeration *********************************************************************/ ncx_data_class_t typ_get_dataclass (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_ACCESS_NONE; } #endif if (typdef->dataclass != NCX_DC_NONE) { return typdef->dataclass; } switch (typdef->tclass) { case NCX_CL_BASE: case NCX_CL_SIMPLE: case NCX_CL_COMPLEX: return NCX_DC_NONE; case NCX_CL_NAMED: return (typdef->def.named.typ) ? typ_get_dataclass(&typdef->def.named.typ->typdef) : NCX_DC_NONE; case NCX_CL_REF: return typ_get_dataclass(typdef->def.ref.typdef); default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_DC_NONE; } /*NOTREACHED*/ } /* typ_get_dataclass */ /******************************************************************** * FUNCTION typ_get_mergetype * * Get the merge type for a specified type def * * INPUTS: * typdef == typ_def_t struct to check * RETURNS: * merge type enumeration *********************************************************************/ ncx_merge_t typ_get_mergetype (const typ_def_t *typdef) { ncx_merge_t mtyp; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_MERGE_NONE; } #endif mtyp = NCX_MERGE_NONE; switch (typdef->tclass) { case NCX_CL_BASE: break; case NCX_CL_SIMPLE: case NCX_CL_COMPLEX: mtyp = typdef->mergetype; break; case NCX_CL_NAMED: if (typdef->def.named.newtyp && typdef->def.named.newtyp->mergetype != NCX_MERGE_NONE) { mtyp = typdef->def.named.newtyp->mergetype; } else { return (typdef->def.named.typ) ? typ_get_mergetype(&typdef->def.named.typ->typdef) : NCX_MERGE_NONE; } break; case NCX_CL_REF: return typ_get_mergetype(typdef->def.ref.typdef); default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_MERGE_NONE; } if (mtyp == NCX_MERGE_NONE) { return NCX_DEF_MERGETYPE; } else { return mtyp; } /*NOTREACHED*/ } /* typ_get_mergetype */ /******************************************************************** * FUNCTION typ_get_nsid * * Return the namespace ID * * INPUTS: * typ == typ_template_t struct to check * RETURNS: * namespace ID of the type *********************************************************************/ xmlns_id_t typ_get_nsid (const typ_template_t *typ) { #ifdef DEBUG if (!typ) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif return typ->nsid; } /* typ_get_nsid */ /******************************************************************** * FUNCTION typ_get_listtyp * * Return the typ_template for the list type, if the supplied * typ_template contains a list typ_def, or named type chain * leads to a NCX_BT_SLIST or NCX_BT_BITS typdef * * INPUTS: * typ == typ_template_t struct to check * RETURNS: * namespace ID of the type *********************************************************************/ typ_template_t * typ_get_listtyp (typ_def_t *typdef) { typ_def_t *ltypdef; ncx_btype_t btyp; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NAMED: return (typdef->def.named.typ) ? typ_get_listtyp(&typdef->def.named.typ->typdef) : NULL; case NCX_CL_REF: ltypdef = typdef->def.ref.typdef; break; default: ltypdef = typdef; } btyp = typ_get_basetype(ltypdef); switch (btyp) { case NCX_BT_SLIST: case NCX_BT_BITS: return ltypdef->def.simple.listtyp; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* typ_get_listtyp */ /******************************************************************** * FUNCTION typ_get_clisttyp * * Return the typ_template for the list type, if the supplied * typ_template contains a list typ_def, or named type chain * leads to a NCX_BT_SLIST typdef * * INPUTS: * typ == typ_template_t struct to check * RETURNS: * namespace ID of the type *********************************************************************/ const typ_template_t * typ_get_clisttyp (const typ_def_t *typdef) { const typ_def_t *ltypdef; ncx_btype_t btyp; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NAMED: return (typdef->def.named.typ) ? typ_get_clisttyp(&typdef->def.named.typ->typdef) : NULL; case NCX_CL_REF: ltypdef = typdef->def.ref.typdef; break; default: ltypdef = typdef; } btyp = typ_get_basetype(ltypdef); switch (btyp) { case NCX_BT_SLIST: case NCX_BT_BITS: return ltypdef->def.simple.listtyp; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* typ_get_clisttyp */ /******************************************************************** * FUNCTION typ_new_unionnode * * Alloc and Init a typ_unionnode_t struct * * INPUTS: * typ == pointer to type template for this union node * * RETURNS: * pointer to malloced struct or NULL if memory error *********************************************************************/ typ_unionnode_t * typ_new_unionnode (typ_template_t *typ) { typ_unionnode_t *un; /* typ is allowed to be NULL */ un = m__getObj(typ_unionnode_t); if (!un) { return NULL; } memset(un, 0, sizeof(typ_unionnode_t)); un->typ = typ; return un; } /* typ_new_unionnode */ /******************************************************************** * FUNCTION typ_free_unionnode * * Free a typ_unionnode_t struct * * INPUTS: * un == union node to free *********************************************************************/ void typ_free_unionnode (typ_unionnode_t *un) { #ifdef DEBUG if (!un) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (un->typdef) { typ_free_typdef(un->typdef); } m__free(un); } /* typ_free_unionnode */ /******************************************************************** * FUNCTION typ_get_unionnode_ptr * * Get the proper typdef pointer from a unionnode * * INPUTS: * un == union node to check * * RETURNS: * pointer to the typ_def_t inside *********************************************************************/ typ_def_t * typ_get_unionnode_ptr (typ_unionnode_t *un) { #ifdef DEBUG if (!un) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (un->typdef) { return un->typdef; } else if (un->typ) { return &un->typ->typdef; } else { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* typ_get_unionnode_ptr */ /******************************************************************** * FUNCTION typ_first_unionnode * * Get the first union node in the queue for a given typdef * * INPUTS: * typdef == pointer to type definition for the union node * * RETURNS: * pointer to first typ_unionnode struct or NULL if none *********************************************************************/ typ_unionnode_t * typ_first_unionnode (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_SIMPLE: if (typdef->def.simple.btyp != NCX_BT_UNION) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } else { return (typ_unionnode_t *) dlq_firstEntry(&typdef->def.simple.unionQ); } case NCX_CL_NAMED: return (typdef->def.named.typ) ? typ_first_unionnode(&typdef->def.named.typ->typdef) : NULL; case NCX_CL_REF: return typ_first_unionnode(typdef->def.ref.typdef); default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* typ_first_unionnode */ /******************************************************************** * FUNCTION typ_first_con_unionnode * * Get the first union node in the queue for a given typdef * * INPUTS: * typdef == pointer to type definition for the union node * * RETURNS: * pointer to first typ_unionnode struct or NULL if none *********************************************************************/ const typ_unionnode_t * typ_first_con_unionnode (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_SIMPLE: if (typdef->def.simple.btyp != NCX_BT_UNION) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } else { return (const typ_unionnode_t *) dlq_firstEntry(&typdef->def.simple.unionQ); } case NCX_CL_NAMED: return (typdef->def.named.typ) ? typ_first_con_unionnode(&typdef->def.named.typ->typdef) : NULL; case NCX_CL_REF: return (typdef->def.ref.typdef) ? typ_first_con_unionnode(typdef->def.ref.typdef) : NULL; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* typ_first_con_unionnode */ /******************************************************************** * FUNCTION typ_is_number * * Check if the base type is numeric * * INPUTS: * btype == basetype enum to check * * RETURNS: * TRUE if base type is numeric * FALSE if some other type *********************************************************************/ boolean typ_is_number (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: return TRUE; default: return FALSE; } } /* typ_is_number */ /******************************************************************** * FUNCTION typ_is_string * * Check if the base type is a simple string (not list) * * INPUTS: * btyp == base type enum to check * * RETURNS: * TRUE if base type is textual * FALSE if some other type *********************************************************************/ boolean typ_is_string (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: return TRUE; case NCX_BT_LEAFREF: /***/ return TRUE; default: return FALSE; } } /* typ_is_string */ /******************************************************************** * FUNCTION typ_is_enum * * Check if the base type is an enumeration * * INPUTS: * btyp == base type enum to check * * RETURNS: * TRUE if base type is an enumeration * FALSE if some other type *********************************************************************/ boolean typ_is_enum (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_ENUM: return TRUE; default: return FALSE; } } /* typ_is_enum */ /******************************************************************** * FUNCTION typ_new_pattern * * Malloc and init a pattern struct * * INPUTS: * pat_str == pattern string to copy and save * * RETURNS: * malloced struct or NULL if memory error *********************************************************************/ typ_pattern_t * typ_new_pattern (const xmlChar *pat_str) { typ_pattern_t *pat; #ifdef DEBUG if (!pat_str) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif pat = m__getObj(typ_pattern_t); if (!pat) { return NULL; } memset(pat, 0x0, sizeof(typ_pattern_t)); pat->pat_str = xml_strdup(pat_str); if (!pat->pat_str) { m__free(pat); return NULL; } ncx_init_errinfo(&pat->pat_errinfo); return pat; } /* typ_new_pattern */ /******************************************************************** * FUNCTION typ_free_pattern * * Free a pattern struct * Must be freed from any Q before calling this function * * INPUTS: * pat == typ_pattern_t struct to free * *********************************************************************/ void typ_free_pattern (typ_pattern_t *pat) { #ifdef DEBUG if (!pat) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (pat->pattern) { xmlRegFreeRegexp(pat->pattern); } if (pat->pat_str) { m__free(pat->pat_str); } ncx_clean_errinfo(&pat->pat_errinfo); m__free(pat); } /* typ_free_pattern */ /******************************************************************** * FUNCTION typ_compile_pattern * * Compile a pattern as into a regex_t struct * * INPUTS: * btyp == base type of the string * sv == ncx_sval_t holding the pattern to compile * * OUTPUTS: * pat->pattern is set if NO_ERR * * RETURNS: * status *********************************************************************/ status_t typ_compile_pattern (typ_pattern_t *pat) { #ifdef DEBUG if (!pat || !pat->pat_str) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif pat->pattern = xmlRegexpCompile(pat->pat_str); if (!pat->pattern) { return ERR_NCX_INVALID_PATTERN; } else { return NO_ERR; } } /* typ_compile_pattern */ /******************************************************************** * FUNCTION typ_get_first_pattern * * Get the first pattern struct for a typdef * * INPUTS: * typdef == typ_def_t to check * * RETURNS: * pointer to pattern string or NULL if none *********************************************************************/ typ_pattern_t * typ_get_first_pattern (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: case NCX_CL_REF: return NULL; case NCX_CL_SIMPLE: return (typ_pattern_t *) dlq_firstEntry(&typdef->def.simple.patternQ); break; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { return typ_get_first_pattern(typdef->def.named.newtyp); } else { /* constrained -- do not go to next typdef */ return NULL; } /*NOTREACHED*/ default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* typ_get_first_pattern */ /******************************************************************** * FUNCTION typ_get_next_pattern * * Get the next pattern struct for a typdef * * INPUTS: * curpat == current typ_pattern_t to check * * RETURNS: * pointer to next pattern struct or NULL if none *********************************************************************/ typ_pattern_t * typ_get_next_pattern (typ_pattern_t *curpat) { #ifdef DEBUG if (!curpat) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (typ_pattern_t *)dlq_nextEntry(curpat); } /* typ_get_next_pattern */ /******************************************************************** * FUNCTION typ_get_first_cpattern * * Get the first pattern struct for a typdef * Const version * * INPUTS: * typdef == typ_def_t to check * * RETURNS: * pointer to pattern string or NULL if none *********************************************************************/ const typ_pattern_t * typ_get_first_cpattern (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NONE: case NCX_CL_BASE: case NCX_CL_REF: return NULL; case NCX_CL_SIMPLE: return (const typ_pattern_t *) dlq_firstEntry(&typdef->def.simple.patternQ); break; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { return typ_get_first_cpattern(typdef->def.named.newtyp); } else { /* constrained -- do not go to next typdef */ return NULL; } /*NOTREACHED*/ default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* typ_get_first_cpattern */ /******************************************************************** * FUNCTION typ_get_next_cpattern * * Get the next pattern struct for a typdef * Const version * * INPUTS: * curpat == current typ_pattern_t to check * * RETURNS: * pointer to next pattern struct or NULL if none *********************************************************************/ const typ_pattern_t * typ_get_next_cpattern (const typ_pattern_t *curpat) { #ifdef DEBUG if (!curpat) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (typ_pattern_t *)dlq_nextEntry(curpat); } /* typ_get_next_cpattern */ /******************************************************************** * FUNCTION typ_get_pattern_count * * Get the number of pattern structs in a typdef * * INPUTS: * typdef == typ_def_t to check * * RETURNS: * count of the typ_pattern_t structs found *********************************************************************/ uint32 typ_get_pattern_count (const typ_def_t *typdef) { const typ_pattern_t *pat; uint32 cnt; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif cnt = 1; pat = typ_get_first_cpattern(typdef); if (!pat) { return 0; } pat = typ_get_next_cpattern(pat); while (pat) { cnt++; pat = typ_get_next_cpattern(pat); } return cnt; } /* typ_get_pattern_count */ /******************************************************************** * FUNCTION typ_get_range_errinfo * * Get the range errinfo for a typdef * * INPUTS: * typdef == typ_def_t to check * * RETURNS: * pointer to pattern string or NULL if none *********************************************************************/ ncx_errinfo_t * typ_get_range_errinfo (typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif switch (typdef->tclass) { case NCX_CL_NAMED: if (typdef->def.named.newtyp) { return &typdef->def.named.newtyp->def.simple.range.range_errinfo; } break; case NCX_CL_SIMPLE: return &typdef->def.simple.range.range_errinfo; case NCX_CL_REF: default: ; } return NULL; } /* typ_get_range_errinfo */ /******************************************************************** * Clean a queue of typ_template_t structs * * \param que Q of typ_template_t to clean * *********************************************************************/ void typ_clean_typeQ (dlq_hdr_t *que) { if ( !que ) { return; } while (!dlq_empty(que)) { typ_template_t *typ = (typ_template_t *)dlq_deque(que); typ_free_template(typ); } } /* typ_clean_typeQ */ /******************************************************************** * FUNCTION typ_ok_for_inline_index * * Check if the base type is okay to use in an inline index decl * * INPUTS: * btyp == base type enum * RETURNS: * TRUE if okay, FALSE if not *********************************************************************/ boolean typ_ok_for_inline_index (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_BITS: case NCX_BT_ENUM: case NCX_BT_BOOLEAN: case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: case NCX_BT_IDREF: case NCX_BT_SLIST: case NCX_BT_UNION: return TRUE; case NCX_BT_EMPTY: case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_CASE: case NCX_BT_NONE: return FALSE; default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } } /* typ_ok_for_inline_index */ /******************************************************************** * FUNCTION typ_ok_for_metadata * * Check if the base type is okay to use in an XML attribute * * INPUTS: * btyp == base type enum * RETURNS: * TRUE if okay, FALSE if not *********************************************************************/ boolean typ_ok_for_metadata (ncx_btype_t btyp) { return typ_ok_for_inline_index(btyp); } /* typ_ok_for_metadata */ /******************************************************************** * FUNCTION typ_ok_for_index * * Check if the base type is okay to use in an index decl * * INPUTS: * typdef == type def struct to check * * RETURNS: * TRUE if okay, FALSE if not *********************************************************************/ boolean typ_ok_for_index (const typ_def_t *typdef) { ncx_btype_t btyp; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (typdef->tclass) { case NCX_CL_BASE: btyp = typ_get_basetype(typdef); return typ_ok_for_inline_index(btyp); case NCX_CL_SIMPLE: btyp = typ_get_basetype(typdef); return typ_ok_for_inline_index(btyp); case NCX_CL_NAMED: if (typdef->def.named.typ) { return typ_ok_for_index(&typdef->def.named.typ->typdef); } else { return FALSE; } case NCX_CL_REF: return typ_ok_for_index(typdef->def.ref.typdef); default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* typ_ok_for_index */ /******************************************************************** * FUNCTION typ_ok_for_union * * Check if the base type is okay to use in an union decl * * INPUTS: * btyp == base type enum * RETURNS: * TRUE if okay, FALSE if not *********************************************************************/ boolean typ_ok_for_union (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_BITS: case NCX_BT_ENUM: case NCX_BT_BOOLEAN: case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_INSTANCE_ID: case NCX_BT_UNION: case NCX_BT_IDREF: return TRUE; case NCX_BT_EMPTY: case NCX_BT_LEAFREF: case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_CASE: case NCX_BT_NONE: return FALSE; default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } } /* typ_ok_for_union */ /******************************************************************** * FUNCTION typ_ok * * Check if the typdef chain has any errors * Checks the named types in the typdef chain to * see if they were already flagged as invalid * * INPUTS: * typdef == starting typdef to check * RETURNS: * TRUE if okay, FALSE if any errors so far *********************************************************************/ boolean typ_ok (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (typdef->tclass) { case NCX_CL_BASE: case NCX_CL_SIMPLE: case NCX_CL_COMPLEX: return TRUE; case NCX_CL_NAMED: if (typdef->def.named.typ) { if (typdef->def.named.typ->res != NO_ERR) { return FALSE; } else { return typ_ok(&typdef->def.named.typ->typdef); } } else { return FALSE; } case NCX_CL_REF: if (typdef->def.ref.typdef) { return typ_ok(typdef->def.ref.typdef); } else { return FALSE; } default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } } /* typ_ok */ /******************************************************************** * FUNCTION typ_ok_for_xsdlist * * Check if the base type is okay to use in an ncx:xsdlist typedef * * INPUTS: * btyp == base type enum * RETURNS: * TRUE if okay, FALSE if not *********************************************************************/ boolean typ_ok_for_xsdlist (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_BITS: return FALSE; case NCX_BT_ENUM: case NCX_BT_BOOLEAN: case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: case NCX_BT_STRING: /*** really NMTOKEN, not string ***/ return TRUE; case NCX_BT_BINARY: case NCX_BT_INSTANCE_ID: return FALSE; case NCX_BT_UNION: case NCX_BT_LEAFREF: case NCX_BT_EMPTY: return FALSE; case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_NONE: return FALSE; default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } } /* typ_ok_for_xsdlist */ /******************************************************************** * FUNCTION typ_get_leafref_path * * Get the path argument for the leafref data type * * INPUTS: * typdef == typdef for the the leafref * * RETURNS: * pointer to the path argument or NULL if some error *********************************************************************/ const xmlChar * typ_get_leafref_path (const typ_def_t *typdef) { const xmlChar *pathstr; const typ_def_t *tdef; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif pathstr = NULL; if (typ_get_basetype(typdef) != NCX_BT_LEAFREF) { return NULL; } tdef = typ_get_cbase_typdef(typdef); if (tdef && tdef->def.simple.xleafref) { pathstr = tdef->def.simple.xleafref->exprstr; } return pathstr; } /* typ_get_leafref_path */ /******************************************************************** * FUNCTION typ_get_leafref_path_addr * * Get the address of the path argument for the leafref data type * * INPUTS: * typdef == typdef for the the leafref * * RETURNS: * pointer to the path argument or NULL if some error *********************************************************************/ const void * typ_get_leafref_path_addr (const typ_def_t *typdef) { const void *pathstr; const typ_def_t *tdef; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif pathstr = NULL; if (typ_get_basetype(typdef) != NCX_BT_LEAFREF) { return NULL; } tdef = typ_get_cbase_typdef(typdef); if (tdef && tdef->def.simple.xleafref) { pathstr = &tdef->def.simple.xleafref->exprstr; } return pathstr; } /* typ_get_leafref_path_addr */ /******************************************************************** * FUNCTION typ_get_leafref_pcb * * Get the XPath parser control block for the leafref data type * returns xpath_pcb_t but cannot import due to H file loop * INPUTS: * typdef == typdef for the the leafref * * RETURNS: * pointer to the PCB struct or NULL if some error *********************************************************************/ struct xpath_pcb_t_ * typ_get_leafref_pcb (typ_def_t *typdef) { typ_def_t *tdef; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typ_get_basetype(typdef) != NCX_BT_LEAFREF) { return NULL; } tdef = typ_get_base_typdef(typdef); if (tdef && tdef->def.simple.xleafref) { return tdef->def.simple.xleafref; } else { return NULL; } /*NOTREACHED*/ } /* typ_get_leafref_pcb */ /******************************************************************** * FUNCTION typ_get_constrained * * Get the constrained true/false field for the data type * leafref or instance-identifier constrained flag * * INPUTS: * typdef == typdef for the the leafref or instance-identifier * * RETURNS: * TRUE if constrained; FALSE if not *********************************************************************/ boolean typ_get_constrained (const typ_def_t *typdef) { const typ_def_t *tdef; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif tdef = typ_get_cbase_typdef(typdef); if (tdef) { return tdef->def.simple.constrained; } else { return FALSE; } /*NOTREACHED*/ } /* typ_get_constrained */ /******************************************************************** * FUNCTION typ_set_xref_typdef * * Set the target typdef for a leafref or instance-identifier * NCX_BT_LEAFREF or NCX_BT_INSTANCE_ID * * INPUTS: * typdef == typdef for the the leafref or instance-identifier * *********************************************************************/ void typ_set_xref_typdef (typ_def_t *typdef, typ_def_t *target) { typ_def_t *tdef; ncx_btype_t btyp; #ifdef DEBUG if (!typdef || !target) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif btyp = typ_get_basetype(typdef); if (!(btyp == NCX_BT_LEAFREF || btyp == NCX_BT_INSTANCE_ID)) { SET_ERROR(ERR_INTERNAL_VAL); return; } tdef = typ_get_base_typdef(typdef); if (tdef && tdef->tclass == NCX_CL_SIMPLE) { tdef->def.simple.xrefdef = target; } else { SET_ERROR(ERR_INTERNAL_VAL); } } /* typ_set_xref_typdef */ /******************************************************************** * FUNCTION typ_get_xref_typdef * * Get the xrefdef target typdef from a leafref * or instance-identifier * NCX_BT_LEAFREF or NCX_BT_INSTANCE_ID * * INPUTS: * typdef == typdef for the the leafref or instance-identifier * * RETURNS: * pointer to the PCB struct or NULL if some error *********************************************************************/ typ_def_t * typ_get_xref_typdef (typ_def_t *typdef) { typ_def_t *tdef; ncx_btype_t btyp; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif btyp = typ_get_basetype(typdef); if (!(btyp == NCX_BT_LEAFREF || btyp == NCX_BT_INSTANCE_ID)) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } tdef = typ_get_base_typdef(typdef); if (tdef && tdef->tclass == NCX_CL_SIMPLE) { btyp = typ_get_basetype(tdef->def.simple.xrefdef); if (btyp == NCX_BT_LEAFREF) { return typ_get_xref_typdef(tdef->def.simple.xrefdef); } else { return tdef->def.simple.xrefdef; } } else { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* typ_get_xref_typdef */ /******************************************************************** * FUNCTION typ_has_subclauses * * Check if the specified typdef has any sub-clauses * Used by yangdump to reverse-engineer the YANG from the typdef * If any appinfo clauses present, then the result will be TRUE * * INPUTS: * typdef == typdef to check * * RETURNS: * TRUE if any sub-clauses, FALSE otherwise *********************************************************************/ boolean typ_has_subclauses (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (!dlq_empty(&typdef->appinfoQ)) { return TRUE; } switch (typdef->tclass) { case NCX_CL_BASE: return FALSE; case NCX_CL_SIMPLE: switch (typdef->def.simple.btyp) { case NCX_BT_UNION: case NCX_BT_BITS: case NCX_BT_ENUM: return TRUE; case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: case NCX_BT_INSTANCE_ID: return FALSE; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_FLOAT64: return !dlq_empty(&typdef->def.simple.range.rangeQ); case NCX_BT_DECIMAL64: return TRUE; case NCX_BT_STRING: case NCX_BT_BINARY: if (!dlq_empty(&typdef->def.simple.range.rangeQ)) { return TRUE; } if (!dlq_empty(&typdef->def.simple.patternQ)) { return TRUE; } return !dlq_empty(&typdef->def.simple.valQ); case NCX_BT_SLIST: case NCX_BT_LEAFREF: case NCX_BT_IDREF: return TRUE; case NCX_BT_NONE: default: return FALSE; } case NCX_CL_COMPLEX: return TRUE; case NCX_CL_NAMED: return (typdef->def.named.newtyp) ? TRUE : FALSE; case NCX_CL_REF: return FALSE; default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } } /* typ_has_subclauses */ /******************************************************************** * FUNCTION typ_get_idref * * Get the idref field if this is an NCX_BT_IDREF typdef * * INPUTS: * typdef == typdef to check * * RETURNS: * pointer to idref field or NULL if wrong type *********************************************************************/ typ_idref_t * typ_get_idref (typ_def_t *typdef) { typ_def_t *basetypdef; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typ_get_basetype(typdef) != NCX_BT_IDREF) { return NULL; } basetypdef = typ_get_base_typdef(typdef); return &basetypdef->def.simple.idref; } /* typ_get_idref */ /******************************************************************** * FUNCTION typ_get_cidref * * Get the idref field if this is an NCX_BT_IDREF typdef * Const version * * INPUTS: * typdef == typdef to check * * RETURNS: * pointer to idref field or NULL if wrong type *********************************************************************/ const typ_idref_t * typ_get_cidref (const typ_def_t *typdef) { const typ_def_t *basetypdef; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (typ_get_basetype(typdef) != NCX_BT_IDREF) { return NULL; } basetypdef = typ_get_cbase_typdef(typdef); return &basetypdef->def.simple.idref; } /* typ_get_cidref */ /******************************************************************** * FUNCTION typ_get_fraction_digits * * Get the fraction-digits field from the typdef chain * typdef must be an NCX_BT_DECIMAL64 or 0 will be returned * valid values are 1..18 * * INPUTS: * typdef == typdef to check * * RETURNS: * number of fixed decimal digits expected (1..18) * 0 if some error *********************************************************************/ uint8 typ_get_fraction_digits (const typ_def_t *typdef) { const typ_def_t *basetypdef; #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif if (typ_get_basetype(typdef) != NCX_BT_DECIMAL64) { return 0; } basetypdef = typ_get_cbase_typdef(typdef); return basetypdef->def.simple.digits; } /* typ_get_fraction_digits */ /******************************************************************** * FUNCTION typ_set_fraction_digits * * Set the fraction-digits field from the typdef chain * * INPUTS: * typdef == typdef to set (must be TYP_CL_SIMPLE) * digits == digits value to set * * RETURNS: * status *********************************************************************/ status_t typ_set_fraction_digits (typ_def_t *typdef, uint8 digits) { #ifdef DEBUG if (!typdef) { return SET_ERROR(ERR_INTERNAL_PTR); } if (typdef->tclass != NCX_CL_SIMPLE) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif if (digits < TYP_DEC64_MIN_DIGITS || digits > TYP_DEC64_MAX_DIGITS) { return ERR_NCX_INVALID_VALUE; } typdef->def.simple.digits = digits; return NO_ERR; } /* typ_set_fraction_digits */ /******************************************************************** * FUNCTION typ_get_typ_linenum * * Get the line number for the typ_template_t * * INPUTS: * typ == type template to check * RETURNS: * line number *********************************************************************/ uint32 typ_get_typ_linenum (const typ_template_t *typ) { #ifdef DEBUG if (!typ) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif return typ->tkerr.linenum; } /* typ_get_typ_linenum */ /******************************************************************** * FUNCTION typ_get_typdef_linenum * * Get the line number for the typ_def_t * * INPUTS: * typ == typdef to check * RETURNS: * line number *********************************************************************/ uint32 typ_get_typdef_linenum (const typ_def_t *typdef) { #ifdef DEBUG if (!typdef) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif return typdef->tkerr.linenum; } /* typ_get_typdef_linenum */ /* END typ.c */ yuma123_2.14/netconf/src/ncx/val_util.h0000664000175000017500000007522214770023131020136 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_val_util #define _H_val_util /* FILE: val_util.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Value Struct Utilities ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 19-dec-05 abb Begun 21jul08 abb start obj-based rewrite 29jul08 abb split out from val.h */ #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_op #include "op.h" #endif #ifndef _H_plock #include "plock.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* user function callback template to test output * of a specified node. * * val_nodetest_fn_t * * Run a user-defined test on the supplied node, and * determine if it should be output or not. * * INPUTS: * withdef == with-defaults value in affect * realtest == FALSE to just check object properties * in the val->obj template * == TRUE if OK to check the other fields * node == pointer to the value struct to check * * RETURNS: * TRUE if the node should be output * FALSE if the node should be skipped */ typedef boolean (*val_nodetest_fn_t) (ncx_withdefaults_t withdef, boolean realtest, val_value_t *node); /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION val_set_canonical_order * * Change the child XML nodes throughout an entire subtree * to the canonical order defined in the object template * * >>> IT IS ASSUMED THAT ONLY VALID NODES ARE PRESENT * >>> AND ALL ERROR NODES HAVE BEEN PURGED ALREADY * * There is no canonical order defined for * the contents of the following nodes: * - anyxml leaf * - ordered-by user leaf-list * * These nodes are not ordered, but their child nodes are ordered * - ncx:root container * - ordered-by user list * * Leaf objects will not be processed, if val is OBJ_TYP_LEAF * Leaf-list objects will not be processed, if val is * OBJ_TYP_LEAF_LIST. These object types must be processed * within the context of the parent object. * * List child key nodes are ordered first among all * of the list's child nodes. * * List nodes with system keys are not kept in sorted order * This is not required by YANG. Instead the user-given * order servers as the canonical order. It is up to * the application setting the config to pick an * order for the list nodes. * * Also, leaf-list order is not changed, regardless of * the order. The default insert order is 'last'. * * INPUTS: * val == value node to change to canonical order * * OUTPUTS: * val->v.childQ may be reordered, for all complex types * in the subtree * *********************************************************************/ extern void val_set_canonical_order (val_value_t *val); /******************************************************************** * FUNCTION val_gen_index_comp * * Create an index component * * INPUTS: * in == obj_key_t in the chain to process * val == the just parsed table row with the childQ containing * nodes to check as index nodes * * OUTPUTS: * val->indexQ will get a val_index_t record added if return NO_ERR * * RETURNS: * status *********************************************************************/ extern status_t val_gen_index_comp (const obj_key_t *in, val_value_t *val); /******************************************************************** * FUNCTION val_gen_key_entry * * Create a key record within an index comp * * INPUTS: * in == obj_key_t in the chain to process * keyval == the just parsed table row with the childQ containing * nodes to check as index nodes * * OUTPUTS: * val->indexQ will get a val_index_t record added if return NO_ERR * * RETURNS: * status *********************************************************************/ extern status_t val_gen_key_entry (val_value_t *keyval); /******************************************************************** * FUNCTION val_gen_index_chain * * Create an index chain for the just-parsed table or container struct * * INPUTS: * obj == list object containing the keyQ * val == the just parsed table row with the childQ containing * nodes to check as index nodes * * OUTPUTS: * *val->indexQ has entries added for each index component, if NO_ERR * * RETURNS: * status *********************************************************************/ extern status_t val_gen_index_chain (const obj_template_t *obj, val_value_t *val); /******************************************************************** * FUNCTION val_add_defaults * * add defaults to an initialized complex value * Go through the specified value struct and add in any defaults * for missing leaf and choice nodes, that have defaults. * * !!! Only the child nodes will be checked for missing defaults * !!! The top-level value passed to this function is assumed to * !!! be already set * * This function does not handle top-level choice object subtrees. * This special case must be handled with the datadefQ * for the module. If a top-level leaf value is passed in, * which is from a top-level choice case-arm, then the * rest of the case-arm objects will not get added by * this function. * * It is assumed that even top-level data-def-stmts will * be handled within a container, so the top-level * object should always a container. * * INPUTS: * val == the value struct to modify * rootval == the root value for XPath purposes * == NULL to skip when-stmt check * cxtval == the context value for XPath purposes * == NULL to use val instead * scriptmode == TRUE if the value is a script object access * == FALSE for normal val_get_simval access instead * * OUTPUTS: * *val and any sub-nodes are set to the default value as requested * * RETURNS: * status *********************************************************************/ extern status_t val_add_defaults (val_value_t *val, val_value_t *valroot, val_value_t *cxtval, boolean scriptmode); /******************************************************************** * FUNCTION val_instance_check * * Check for the proper number of object instances for * the specified value struct. Checks the direct accessible * children of 'val' only!!! * * The 'obj' parameter is usually the val->obj field * except for choice/case processing * * Log errors as needed and mark val->res as needed * * INPUTS: * val == value to check * * RETURNS: * status *********************************************************************/ extern status_t val_instance_check (val_value_t *val, obj_template_t *obj); /******************************************************************** * FUNCTION val_get_choice_first_set * * Check a val_value_t struct against its expected OBJ * to determine if a specific choice has already been set * Get the value struct for the first value set for * the specified choice * * INPUTS: * val == val_value_t to check * obj == choice object to check * * RETURNS: * pointer to first value struct or NULL if choice not set *********************************************************************/ extern val_value_t * val_get_choice_first_set (val_value_t *val, const obj_template_t *obj); /******************************************************************** * FUNCTION val_get_choice_next_set * * Check a val_value_t struct against its expected OBJ * to determine if a specific choice has already been set * Get the value struct for the next value set from the * specified choice, afvter 'curval' * * INPUTS: * obj == choice object to check * curchild == current child selected from this choice (obj) * * RETURNS: * pointer to first value struct or NULL if choice not set *********************************************************************/ extern val_value_t * val_get_choice_next_set (const obj_template_t *obj, val_value_t *curchild); /******************************************************************** * FUNCTION val_choice_is_set * * Check a val_value_t struct against its expected OBJ * to determine if a specific choice has already been set * Check that all the mandatory config fields in the selected * case are set * * INPUTS: * val == parent of the choice object to check * obj == choice object to check * * RETURNS: * pointer to first value struct or NULL if choice not set *********************************************************************/ extern boolean val_choice_is_set (val_value_t *val, obj_template_t *obj); /******************************************************************** * FUNCTION val_purge_errors_from_root * * Remove any error nodes under a root container * that were saved for error recording purposes * * INPUTS: * val == root container to purge * *********************************************************************/ extern void val_purge_errors_from_root (val_value_t *val); /******************************************************************** * FUNCTION val_new_child_val * * INPUTS: * nsid == namespace ID of name * name == name string (direct or strdup, based on copyname) * copyname == TRUE is dname strdup should be used * parent == parent node * editop == requested edit operation * obj == object template to use * * RETURNS: * status *********************************************************************/ extern val_value_t * val_new_child_val (xmlns_id_t nsid, const xmlChar *name, boolean copyname, val_value_t *parent, op_editop_t editop, obj_template_t *obj); /******************************************************************** * FUNCTION val_gen_instance_id * * Malloc and Generate the instance ID string for this value node, * * INPUTS: * mhdr == message hdr w/ prefix map or NULL to just use * the internal prefix mappings * val == node to generate the instance ID for * format == desired output format (NCX or Xpath) * buff == pointer to address of buffer to use * * OUTPUTS * mhdr.pmap may have entries added if prefixes used * in the instance identifier which are not already in the pmap * *buff == malloced buffer with the instance ID * * RETURNS: * status *********************************************************************/ extern status_t val_gen_instance_id (xml_msg_hdr_t *mhdr, const val_value_t *val, ncx_instfmt_t format, xmlChar **buff); /******************************************************************** * FUNCTION val_gen_instance_id_ex * * Malloc and Generate the instance ID string for this value node, * * INPUTS: * mhdr == message hdr w/ prefix map or NULL to just use * the internal prefix mappings * val == node to generate the instance ID for * format == desired output format (NCX or Xpath) * stop_at_root == TRUE to stop if a 'root' node is encountered * == FALSE to keep recursing all the way to * buff == pointer to address of buffer to use * * OUTPUTS * mhdr.pmap may have entries added if prefixes used * in the instance identifier which are not already in the pmap * *buff == malloced buffer with the instance ID * * RETURNS: * status *********************************************************************/ extern status_t val_gen_instance_id_ex (xml_msg_hdr_t *mhdr, const val_value_t *val, ncx_instfmt_t format, boolean stop_at_root, xmlChar **buff); /******************************************************************** * FUNCTION val_gen_split_instance_id * * Malloc and Generate the instance ID string for this value node, * Add the last node from the parameters, not the value node * * INPUTS: * mhdr == message hdr w/ prefix map or NULL to just use * the internal prefix mappings * val == node to generate the instance ID for * format == desired output format (NCX or Xpath) * leaf_pfix == namespace prefix string of the leaf to add * leaf_name == name string of the leaf to add * stop_at_root == TRUE to stop if a 'root' node is encountered * == FALSE to keep recursing all the way to * buff == pointer to address of buffer to use * * OUTPUTS * mhdr.pmap may have entries added if prefixes used * in the instance identifier which are not already in the pmap * *buff == malloced buffer with the instance ID * * RETURNS: * status *********************************************************************/ extern status_t val_gen_split_instance_id (xml_msg_hdr_t *mhdr, const val_value_t *val, ncx_instfmt_t format, xmlns_id_t leaf_nsid, const xmlChar *leaf_name, boolean stop_at_root, xmlChar **buff); /******************************************************************** * FUNCTION val_get_index_string * * Get the index string for the specified table or container entry * * INPUTS: * mhdr == message hdr w/ prefix map or NULL to just use * the internal prefix mappings * format == desired output format * val == val_value_t for table or container * buff == buffer to hold result; == NULL means get length only * * OUTPUTS: * mhdr.pmap may have entries added if prefixes used * in the instance identifier which are not already in the pmap * *len = number of bytes that were (or would have been) written * to buff * * RETURNS: * status *********************************************************************/ extern status_t val_get_index_string (xml_msg_hdr_t *mhdr, ncx_instfmt_t format, const val_value_t *val, xmlChar *buff, uint32 *len); /******************************************************************** * FUNCTION val_check_obj_when * * checks when-stmt only * Check if the specified object node is * conditionally TRUE or FALSE, based on any * when statements attached to the child node * * INPUTS: * val == parent value node of the object node to check * valroot == database root for XPath purposes * objval == database value node to check (may be NULL) * obj == object template of data node object to check * condresult == address of conditional test result * whencount == address of number of when-stmts tested * (may be NULL if caller does not care) * * OUTPUTS: * *condresult == TRUE if conditional is true or there are none * FALSE if conditional test failed * if non-NULL: * *whencount == number of when-stmts tested * this can be 0 if *condresult == TRUE * * RETURNS: * status *********************************************************************/ extern status_t val_check_obj_when (val_value_t *val, val_value_t *valroot, val_value_t *objval, obj_template_t *obj, boolean *condresult, uint32 *whencount); /******************************************************************** * FUNCTION val_get_xpathpcb * * Get the XPath parser control block in the specified value struct * * INPUTS: * val == value struct to check * * RETURNS: * pointer to xpath control block or NULL if none *********************************************************************/ extern xpath_pcb_t * val_get_xpathpcb (val_value_t *val); /******************************************************************** * FUNCTION val_get_const_xpathpcb * * Get the XPath parser control block in the specified value struct * * INPUTS: * val == value struct to check * * RETURNS: * pointer to xpath control block or NULL if none *********************************************************************/ extern const xpath_pcb_t * val_get_const_xpathpcb (const val_value_t *val); /******************************************************************** * FUNCTION val_make_simval_obj * * Create and set a val_value_t as a simple type * from an object template instead of individual fields * Calls val_make_simval with the object settings * * INPUTS: * obj == object template to use * valstr == simple value encoded as a string * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced and filled in val_value_t struct * NULL if some error *********************************************************************/ extern val_value_t * val_make_simval_obj (obj_template_t *obj, const xmlChar *valstr, status_t *res); /******************************************************************** * FUNCTION val_set_simval_obj * * Set an initialized val_value_t as a simple type * Set a pre-initialized val_value_t as a simple type * from an object template instead of individual fields * Calls val_set_simval with the object settings * * INPUTS: * val == value struct to set * obj == object template to use * valstr == simple value encoded as a string to set * * RETURNS: * status *********************************************************************/ extern status_t val_set_simval_obj (val_value_t *val, obj_template_t *obj, const xmlChar *valstr); /******************************************************************** * FUNCTION val_set_warning_parms * * Check the parent value struct (expected to be a container or list) * for the common warning control parameters. * invoke the warning parms that are present * * --warn-idlen * --warn-linelen * --warn-off * * INPUTS: * parentval == parent value struct to check * * OUTPUTS: * prints an error message if a warn-off record cannot be added * * RETURNS: * status *********************************************************************/ extern status_t val_set_warning_parms (val_value_t *parentval); /******************************************************************** * FUNCTION val_set_logging_parms * * Check the parent value struct (expected to be a container or list) * for the common warning control parameters. * invoke the warning parms that are present * * --log=filename * --log-level= * --log-append= * * INPUTS: * parentval == parent value struct to check * * OUTPUTS: * prints an error message if any errors occur * * RETURNS: * status *********************************************************************/ extern status_t val_set_logging_parms (val_value_t *parentval); /******************************************************************** * FUNCTION val_set_path_parms * --datapath * --modpath * --runpath * * Check the specified value set for the 3 path CLI parms * and override the environment variable setting, if any. * * Not all of these parameters are supported in all programs * The object tree is not checked, just the value tree * * INPUTS: * parentval == CLI container to check for the runpath, * modpath, and datapath variables * RETURNS: * status *********************************************************************/ extern status_t val_set_path_parms (val_value_t *parentval); /******************************************************************** * FUNCTION val_set_subdirs_parm * --subdirs= * * Handle the --subdirs parameter * * INPUTS: * parentval == CLI container to check for the subdirs parm * * RETURNS: * status *********************************************************************/ extern status_t val_set_subdirs_parm (val_value_t *parentval); /******************************************************************** * FUNCTION val_set_feature_parms * --feature-code-default * --feature-enable-default * --feature-static * --feature-dynamic * --feature-enable * --feature-disable * * Handle the feature-related CLI parms for the specified value set * * Not all of these parameters are supported in all programs * The object tree is not checked, just the value tree * * INPUTS: * parentval == CLI container to check for the feature parms * * RETURNS: * status *********************************************************************/ extern status_t val_set_feature_parms (val_value_t *parentval); /******************************************************************** * FUNCTION val_set_protocols_parm * * --protocols=bits [netconf1.0, netconf1.1] * * Handle the protocols parameter * * INPUTS: * parentval == CLI container to check for the protocols parm * * RETURNS: * status: at least 1 protocol must be selected *********************************************************************/ extern status_t val_set_protocols_parm (val_value_t *parentval); /******************************************************************** * FUNCTION val_set_ses_protocols_parm * * --protocols=bits [netconf1.0, netconf1.1] * * Handle the protocols parameter * * INPUTS: * scb == session control block to use * parentval == CLI container to check for the protocols parm * * RETURNS: * status: at least 1 protocol must be selected *********************************************************************/ extern status_t val_set_ses_protocols_parm (ses_cb_t *scb, val_value_t *parentval); /******************************************************************** * FUNCTION val_ok_to_partial_lock * * Check if the specified root val could be locked * right now by the specified session * * INPUTS: * val == start value struct to use * sesid == session ID requesting the partial lock * lockowner == address of first lock owner violation * * OUTPUTS: * *lockowner == pointer to first lock owner violation * * RETURNS: * status: if any error, then val_clear_partial_lock * MUST be called with the start root, to back out any * partial operations. This can happen if the max number * of *********************************************************************/ extern status_t val_ok_to_partial_lock (val_value_t *val, ses_id_t sesid, ses_id_t *lockowner); /******************************************************************** * FUNCTION val_set_partial_lock * * Set the partial lock throughout the value tree * * INPUTS: * val == start value struct to use * plcb == partial lock to set on entire subtree * * RETURNS: * status: if any error, then val_clear_partial_lock * MUST be called with the start root, to back out any * partial operations. This can happen if the max number * of locks reached or lock already help by another session *********************************************************************/ extern status_t val_set_partial_lock (val_value_t *val, plock_cb_t *plcb); /******************************************************************** * FUNCTION val_clear_partial_lock * * Clear the partial lock throughout the value tree * * INPUTS: * val == start value struct to use * plcb == partial lock to clear * *********************************************************************/ extern void val_clear_partial_lock (val_value_t *val, plock_cb_t *plcb); /******************************************************************** * FUNCTION val_write_ok * * Check if there are any partial-locks owned by another * session in the node that is going to be written * If the operation is replace or delete, then the * entire target subtree will be checked * * INPUTS: * val == start value struct to use * editop == requested write operation * sesid == session requesting this write operation * checkup == TRUE to check up the tree as well * lockid == address of return partial lock ID * * OUTPUTS: * *lockid == return lock ID if any portion is locked * Only the first lock violation detected will * be reported * error code will be NCX_ERR_IN_USE if *lockid is set * * RETURNS: * status, NO_ERR indicates no partial lock conflicts *********************************************************************/ extern status_t val_write_ok (val_value_t *val, op_editop_t editop, ses_id_t sesid, boolean checkup, uint32 *lockid); /******************************************************************** * FUNCTION val_check_swap_resnode * * Check if the curnode has any partial locks * and if so, transfer them to the new node * and change any resnodes as well * * INPUTS: * curval == current node to check * newval == new value taking its place * *********************************************************************/ extern void val_check_swap_resnode (val_value_t *curval, val_value_t *newval); /******************************************************************** * FUNCTION val_check_delete_resnode * * Check if the curnode has any partial locks * and if so, remove them from the final result * * INPUTS: * curval == current node to check * *********************************************************************/ extern void val_check_delete_resnode (val_value_t *curval); /******************************************************************** * FUNCTION val_write_extern * * Write an external file to the session * * INPUTS: * scb == session control block * val == value to write (NCX_BT_EXTERN) * * RETURNS: * none *********************************************************************/ extern void val_write_extern (ses_cb_t *scb, const val_value_t *val); /******************************************************************** * FUNCTION val_write_intern * * Write an internal buffer to the session * * INPUTS: * scb == session control block * val == value to write (NCX_BT_INTERN) * * RETURNS: * none *********************************************************************/ extern void val_write_intern (ses_cb_t *scb, const val_value_t *val); /******************************************************************** * FUNCTION val_get_value * * Get the value node for output to a session * Checks access control if enabled * Checks filtering via testfn if non-NULL * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write (node from system) * acmcheck == TRUE if NACM should be checked; FALSE to skip * testcb == callback function to use, NULL if not used * malloced == address of return malloced flag * res == address of return status * * OUTPUTS: * *malloced == TRUE if the * *res == return status * * RETURNS: * value node to use; this is malloced if *malloced is TRUE * NULL if some error; check *res; * !!!! check for ERR_NCX_SKIPPED !!! *********************************************************************/ extern val_value_t * val_get_value (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, val_nodetest_fn_t testfn, boolean acmtest, boolean *malloced, status_t *res); /******************************************************************** * FUNCTION val_traverse_keys * * Check ancestor-or-self nodes until root reached * Find all lists; For each list, starting with the * closest to root, invoke the callback function * for each of the key objects in order * * INPUTS: * val == value node to start check from * cookie1 == cookie1 to pass to the callback function * cookie2 == cookie2 to pass to the callback function * walkerfn == walker callback function * returns FALSE to terminate traversal * *********************************************************************/ extern void val_traverse_keys (val_value_t *val, void *cookie1, void *cookie2, val_walker_fn_t walkerfn); /******************************************************************** * FUNCTION val_build_index_chains * * Check descendant-or-self nodes for lists * Check if they have index chains built already * If not, then try to add one * for each of the key objects in order * * INPUTS: * val == value node to start check from * * RETURNS: * status *********************************************************************/ extern status_t val_build_index_chains (val_value_t *val); /******************************************************************** * FUNCTION val_find_nearest_ancestor * * Find a leaf-lists value node's nearest ancestor that meets the * conditions set in RFC 7950 section 7.7.2. * * INPUTS: * val == leaf-list value node from which test starts * * OUTPUTS: * pobj == parent object found from schema tree. If this is a * case node, it will not be the same as the object pointed * to by the returned val_value_t. * * RETURNS: * The *********************************************************************/ val_value_t * val_find_nearest_ancestor(val_value_t *val, obj_template_t **pobj); /******************************************************************** * FUNCTION val_has_default_leaf_list * * * INPUTS: * val == value node to check for potential leaf-list child * * RETURNS: * true if a value has an empty leaf-list with defaults, * false otherwise. *********************************************************************/ boolean val_has_default_leaf_list(const val_value_t *val); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_val_util */ yuma123_2.14/netconf/src/ncx/ext.h0000664000175000017500000001216714770023131017116 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ext #define _H_ext /* FILE: ext.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Extension Handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 05-jan-08 abb Begun */ #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* One YANG 'extension' definition -- language extension template */ typedef struct ext_template_t_ { dlq_hdr_t qhdr; xmlChar *name; xmlChar *descr; xmlChar *ref; xmlChar *arg; xmlns_id_t nsid; ncx_status_t status; boolean argel; boolean used; /* needed by yangdiff */ dlq_hdr_t appinfoQ; /* Q of ncx_appinfo_t */ ncx_error_t tkerr; } ext_template_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION ext_new_template * * Malloc and initialize the fields in a ext_template_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern ext_template_t * ext_new_template (void); /******************************************************************** * FUNCTION ext_free_template * * Scrub the memory in a ext_template_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * ext == ext_template_t data structure to free *********************************************************************/ extern void ext_free_template (ext_template_t *ext); /******************************************************************** * FUNCTION ext_clean_extensionQ * * Clean a queue of ext_template_t structs * * INPUTS: * que == Q of ext_template_t data structures to free *********************************************************************/ extern void ext_clean_extensionQ (dlq_hdr_t *que); /******************************************************************** * FUNCTION ext_find_extension * * Search a queue of ext_template_t structs for a given name * * INPUTS: * que == Q of ext_template_t data structures to search * name == name string to find * * RETURNS: * pointer to found entry, or NULL if not found *********************************************************************/ extern ext_template_t * ext_find_extension (ncx_module_t *mod, const xmlChar *name); /******************************************************************** * FUNCTION ext_find_extension_que * * Find an ext_template_t struct in the specified Q * * INPUTS: * extensionQ == Q of ext_template_t to search * name == extension name to find * * RETURNS: * pointer to found extension or NULL if not found *********************************************************************/ extern ext_template_t * ext_find_extension_que (dlq_hdr_t *extensionQ, const xmlChar *name); /******************************************************************** * FUNCTION ext_find_extension_all * * Search a module of ext_template_t structs for a given name * Check all submodules as well * * INPUTS: * mod == module to check * name == name string to find * * RETURNS: * pointer to found entry, or NULL if not found *********************************************************************/ extern ext_template_t * ext_find_extension_all (ncx_module_t *mod, const xmlChar *name); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_ext */ yuma123_2.14/netconf/src/ncx/xpath.h0000664000175000017500000011136314770023131017440 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xpath #define _H_xpath /* FILE: xpath.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Schema and data model Xpath search support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30-dec-07 abb Begun 06-jul-11 abb add wildcard match support */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_var #include "var.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* max size of the pcb->result_cacheQ */ #define XPATH_RESULT_CACHE_MAX 16 /* max size of the pcb->resnode_cacheQ */ #define XPATH_RESNODE_CACHE_MAX 64 /* XPath 1.0 sec 2.2 AxisName */ #define XP_AXIS_ANCESTOR (const xmlChar *)"ancestor" #define XP_AXIS_ANCESTOR_OR_SELF (const xmlChar *)"ancestor-or-self" #define XP_AXIS_ATTRIBUTE (const xmlChar *)"attribute" #define XP_AXIS_CHILD (const xmlChar *)"child" #define XP_AXIS_DESCENDANT (const xmlChar *)"descendant" #define XP_AXIS_DESCENDANT_OR_SELF (const xmlChar *)"descendant-or-self" #define XP_AXIS_FOLLOWING (const xmlChar *)"following" #define XP_AXIS_FOLLOWING_SIBLING (const xmlChar *)"following-sibling" #define XP_AXIS_NAMESPACE (const xmlChar *)"namespace" #define XP_AXIS_PARENT (const xmlChar *)"parent" #define XP_AXIS_PRECEDING (const xmlChar *)"preceding" #define XP_AXIS_PRECEDING_SIBLING (const xmlChar *)"preceding-sibling" #define XP_AXIS_SELF (const xmlChar *)"self" /* Xpath 1.0 Function library + current() from XPath 2.0 */ #define XP_FN_BOOLEAN (const xmlChar *)"boolean" #define XP_FN_CEILING (const xmlChar *)"ceiling" #define XP_FN_CONCAT (const xmlChar *)"concat" #define XP_FN_CONTAINS (const xmlChar *)"contains" #define XP_FN_COUNT (const xmlChar *)"count" #define XP_FN_CURRENT (const xmlChar *)"current" #define XP_FN_FALSE (const xmlChar *)"false" #define XP_FN_FLOOR (const xmlChar *)"floor" #define XP_FN_ID (const xmlChar *)"id" #define XP_FN_LANG (const xmlChar *)"lang" #define XP_FN_LAST (const xmlChar *)"last" #define XP_FN_LOCAL_NAME (const xmlChar *)"local-name" #define XP_FN_NAME (const xmlChar *)"name" #define XP_FN_NAMESPACE_URI (const xmlChar *)"namespace-uri" #define XP_FN_NORMALIZE_SPACE (const xmlChar *)"normalize-space" #define XP_FN_NOT (const xmlChar *)"not" #define XP_FN_NUMBER (const xmlChar *)"number" #define XP_FN_POSITION (const xmlChar *)"position" #define XP_FN_ROUND (const xmlChar *)"round" #define XP_FN_STARTS_WITH (const xmlChar *)"starts-with" #define XP_FN_STRING (const xmlChar *)"string" #define XP_FN_STRING_LENGTH (const xmlChar *)"string-length" #define XP_FN_SUBSTRING (const xmlChar *)"substring" #define XP_FN_SUBSTRING_AFTER (const xmlChar *)"substring-after" #define XP_FN_SUBSTRING_BEFORE (const xmlChar *)"substring-before" #define XP_FN_SUM (const xmlChar *)"sum" #define XP_FN_TRANSLATE (const xmlChar *)"translate" #define XP_FN_TRUE (const xmlChar *)"true" /* yuma function extensions */ #define XP_FN_MODULE_LOADED (const xmlChar *)"module-loaded" #define XP_FN_FEATURE_ENABLED (const xmlChar *)"feature-enabled" /* YANG 1.1 functions */ #define XP_FN_RE_MATCH (const xmlChar *)"re-match" #define XP_FN_DERIVED_FROM (const xmlChar *)"derived-from" #define XP_FN_DERIVED_FROM_OR_SELF (const xmlChar *)"derived-from-or-self" #define XP_FN_DEREF (const xmlChar *)"deref" #define XP_FN_ENUM_VALUE (const xmlChar *)"enum-value" #define XP_FN_BIT_IS_SET (const xmlChar *)"bit-is-set" /* XPath NodeType values */ #define XP_NT_COMMENT (const xmlChar *)"comment" #define XP_NT_TEXT (const xmlChar *)"text" #define XP_NT_PROCESSING_INSTRUCTION \ (const xmlChar *)"processing-instruction" #define XP_NT_NODE (const xmlChar *)"node" /* XPath 1.0 operator names */ #define XP_OP_AND (const xmlChar *)"and" #define XP_OP_OR (const xmlChar *)"or" #define XP_OP_DIV (const xmlChar *)"div" #define XP_OP_MOD (const xmlChar *)"mod" /* Special URL to XPath translation */ #define XP_URL_ESC_WILDCARD '-' /* XPath control block flag definitions */ /* If dynnode is present, then at least one component * within the entire XPath expression is variable. * (e.g ../parent == 'fred' or $foo + 1 * * If not set, then the entire expression is constant * (e.g., 34 mod 2 or 48 and 'fred') */ #define XP_FL_DYNNODE bit0 /* during XPath evaluation, skipping the rest of a * FALSE AND expression */ #define XP_FL_SKIP_FAND bit1 /* during XPath evaluation, skipping the rest of a * TRUE OR expression */ #define XP_FL_SKIP_TOR bit2 /* used by xpath_leafref.c to keep track of path type */ #define XP_FL_ABSPATH bit3 /* used for YANG/NETCONF to auto-filter any non-config nodes * that are matched by an XPath wildcard mechanism */ #define XP_FL_CONFIGONLY bit4 /* used to indicate the top object node is set * FALSE to indicate that all the ncx_module_t datadefQs * need to be searched instead */ #define XP_FL_USEROOT bit5 /* used to restrict the XPath expression to the YANG * instance-identifier syntax */ #define XP_FL_INSTANCEID bit6 /* used to restrict the XPath expression to an * ncx:schema-instance string syntax */ #define XP_FL_SCHEMA_INSTANCEID bit7 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* XPath expression result type */ typedef enum xpath_restype_t_ { XP_RT_NONE, XP_RT_NODESET, XP_RT_NUMBER, XP_RT_STRING, XP_RT_BOOLEAN } xpath_restype_t; /* XPath dynamic parsing mode for leafref */ typedef enum xpath_curmode_t_ { XP_CM_NONE, XP_CM_TARGET, XP_CM_ALT, XP_CM_KEYVAR } xpath_curmode_t; /* document root type */ typedef enum xpath_document_t_ { XP_DOC_NONE, XP_DOC_DATABASE, XP_DOC_RPC, XP_DOC_RPC_REPLY, XP_DOC_NOTIFICATION } xpath_document_t; /* XPath expression source type */ typedef enum xpath_source_t_ { XP_SRC_NONE, XP_SRC_LEAFREF, XP_SRC_YANG, XP_SRC_INSTANCEID, XP_SRC_SCHEMA_INSTANCEID, XP_SRC_XML } xpath_source_t; /* XPath expression operation type */ typedef enum xpath_exop_t_ { XP_EXOP_NONE, XP_EXOP_AND, /* keyword 'and' */ XP_EXOP_OR, /* keyword 'or' */ XP_EXOP_EQUAL, /* equals '=' */ XP_EXOP_NOTEQUAL, /* bang equals '!=' */ XP_EXOP_LT, /* left angle bracket '<' */ XP_EXOP_GT, /* right angle bracket '>' */ XP_EXOP_LEQUAL, /* l. angle-equals '<= */ XP_EXOP_GEQUAL, /* r. angle-equals '>=' */ XP_EXOP_ADD, /* plus sign '+' */ XP_EXOP_SUBTRACT, /* minus '-' */ XP_EXOP_MULTIPLY, /* asterisk '*' */ XP_EXOP_DIV, /* keyword 'div' */ XP_EXOP_MOD, /* keyword 'mod' */ XP_EXOP_NEGATE, /* unary '-' */ XP_EXOP_UNION, /* vert. bar '|' */ XP_EXOP_FILTER1, /* fwd slash '/' */ XP_EXOP_FILTER2 /* double fwd slash (C++ comment) */ } xpath_exop_t; /* XPath expression node types */ typedef enum xpath_nodetype_t_ { XP_EXNT_NONE, XP_EXNT_COMMENT, XP_EXNT_TEXT, XP_EXNT_PROC_INST, XP_EXNT_NODE } xpath_nodetype_t; /* xpath_getvar_fn_t * * Callback function for retrieval of a variable binding * * INPUTS: * pcb == XPath parser control block in use * varname == variable name requested * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to the ncx_var_t data structure * for the specified varbind (malloced and filled in) */ typedef ncx_var_t * (*xpath_getvar_fn_t) (struct xpath_pcb_t_ *pcb, const xmlChar *varname, status_t *res); /* XPath result node struct */ typedef struct xpath_resnode_t_ { dlq_hdr_t qhdr; boolean dblslash; int64 position; int64 last; /* only set in context node */ union node_ { obj_template_t *objptr; val_value_t *valptr; } node; } xpath_resnode_t; /* XPath expression result */ typedef struct xpath_result_t_ { dlq_hdr_t qhdr; /* in case saved in a Q */ xpath_restype_t restype; boolean isval; /* matters if XP_RT_NODESET */ int64 last; /* used with XP_RT_NODESET */ union r_ { dlq_hdr_t nodeQ; /* Q of xpath_resnode_t */ boolean boo; ncx_num_t num; xmlChar *str; } r; status_t res; } xpath_result_t; /* XPath parser control block */ typedef struct xpath_pcb_t_ { dlq_hdr_t qhdr; /* in case saved in a Q */ tk_chain_t *tkc; /* chain for exprstr */ xmlChar *exprstr; /* YANG XPath string */ xmlTextReaderPtr reader; /* get NS inside XML */ /* the prefixes in the QNames in the exprstr MUST be resolved * in different contexts. * * For must/when/leafref XPath, the prefix is a module prefix * which must match an import statement in the 'mod' import Q * * For XML context (NETCONF PDU 'select' attribute) * the prefix is part of an extended name, representing * XML namespace for the module that defines that node */ xpath_source_t source; ncx_errinfo_t errinfo; /* must error extras */ boolean logerrors; /* T: use log_error F: agt */ boolean missing_errors; /* T: missing node is error */ /* these parms are used to parse leafref path-arg * limited object tree syntax allowed only */ obj_template_t *targobj; /* bptr to result object */ obj_template_t *altobj; /* bptr to pred. RHS object */ obj_template_t *varobj; /* bptr to key-expr LHS object */ xpath_curmode_t curmode; /* select targ/alt/var obj */ /* these parms are used by leafref and XPath1 parsing */ obj_template_t *obj; /* bptr to start object */ ncx_module_t *objmod; /* module containing obj */ obj_template_t *docroot; /* bptr to obj */ val_value_t *val; /* current() node */ val_value_t *val_docroot; /* cfg->root for db */ xpath_document_t doctype; /* these parms are used for XPath1 processing * against a target database */ uint32 flags; xpath_result_t *result; /* additive XPath1 context back- pointer to current * step results; initially NULL and modified until * the expression is done */ xpath_resnode_t context; /* relative context */ xpath_resnode_t orig_context; /* for current() fn */ /* The getvar_fn callback function may be set to allow * user variables to be supported in this XPath expression */ xpath_getvar_fn_t getvar_fn; void *cookie; /* The varbindQ may be used instead of the getvar_fn * to store user variables to be supported in * this XPath expression */ dlq_hdr_t varbindQ; /* The function Q is a copy of the global Q * It is not hardwired in case app-specific extensions * are added later -- array of xpath_fncb_t */ const struct xpath_fncb_t_ *functions; /* Performance Caches * The xpath_result_t and xpath_resnode_t structs * are used in many intermediate operations * * These Qs are used to cache these structs * instead of calling malloc and free constantly * * The XPATH_RESULT_CACHE_MAX and XPATH_RESNODE_CACHE_MAX * constants are used to control the max cache sizes * This is not user-configurable (TBD). */ dlq_hdr_t result_cacheQ; /* Q of xpath_result_t */ dlq_hdr_t resnode_cacheQ; /* Q of xpath_resnode_t */ uint32 result_count; uint32 resnode_count; /* first and second pass parsing results * the next phase will not execute until * all previous phases have a NO_ERR status */ status_t parseres; status_t validateres; status_t valueres; /* saved error info for the agent to process */ ncx_error_t tkerr; boolean seen; /* yangdiff support */ } xpath_pcb_t; /* XPath function prototype */ typedef xpath_result_t * (*xpath_fn_t) (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, /* Q of xpath_result_t */ status_t *res); /* XPath function control block */ typedef struct xpath_fncb_t_ { const xmlChar *name; xpath_restype_t restype; int32 parmcnt; /* -1 == N, 0..N == actual cnt */ xpath_fn_t fn; } xpath_fncb_t; /* Value or object node walker fn callback parameters */ typedef struct xpath_walkerparms_t_ { dlq_hdr_t *resnodeQ; int64 callcount; status_t res; } xpath_walkerparms_t; /* Value node compare walker fn callback parameters */ typedef struct xpath_compwalkerparms_t_ { xpath_result_t *result2; val_value_t *cmpval; xmlChar *cmpstring; ncx_num_t *cmpnum; xmlChar *buffer; uint32 buffsize; xpath_exop_t exop; boolean cmpresult; status_t res; } xpath_compwalkerparms_t; /* Value node stringify walker fn callback parameters */ typedef struct xpath_stringwalkerparms_t_ { xmlChar *buffer; uint32 buffsize; uint32 buffpos; status_t res; } xpath_stringwalkerparms_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xpath_find_schema_target * * find target, save in *targobj * Follow the absolute-path or descendant-node path expression * and return the obj_template_t that it indicates, and the * que that the object is in * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain in progress (may be NULL: errmsg only) * mod == module in progress * obj == augment object initiating search, NULL to start at top * datadefQ == Q of obj_template_t containing 'obj' * target == Xpath expression string to evaluate * targobj == address of return object (may be NULL) * targQ == address of return target queue (may be NULL) * * OUTPUTS: * if non-NULL inputs: * *targobj == target object * *targQ == datadefQ Q header which contains targobj * * RETURNS: * status *********************************************************************/ extern status_t xpath_find_schema_target (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *datadefQ, const xmlChar *target, obj_template_t **targobj, dlq_hdr_t **targQ); /******************************************************************** * FUNCTION xpath_find_schema_target_err * * find target, save in *targobj, use the errtk if error * Same as xpath_find_schema_target except a token struct * is provided to use for the error token, instead of 'obj' * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain in progress (may be NULL: errmsg only) * mod == module in progress * obj == augment object initiating search, NULL to start at top * datadefQ == Q of obj_template_t containing 'obj' * target == Xpath expression string to evaluate * targobj == address of return object (may be NULL) * targQ == address of return target queue (may be NULL) * tkerr == error struct to use if any messages generated * * OUTPUTS: * if non-NULL inputs: * *targobj == target object * *targQ == datadefQ header for targobj * * RETURNS: * status *********************************************************************/ extern status_t xpath_find_schema_target_err (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *datadefQ, const xmlChar *target, obj_template_t **targobj, dlq_hdr_t **targQ, ncx_error_t *tkerr); /******************************************************************** * FUNCTION xpath_find_schema_target_int * * internal find target, without any error reporting * Follow the absolute-path expression * and return the obj_template_t that it indicates * * Internal access version * Error messages are not printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * target == absolute Xpath expression string to evaluate * targobj == address of return object (may be NULL) * * OUTPUTS: * if non-NULL inputs: * *targobj == target object * * RETURNS: * status *********************************************************************/ extern status_t xpath_find_schema_target_int (const xmlChar *target, obj_template_t **targobj); /******************************************************************** * FUNCTION xpath_find_val_target * * used by cfg.c to find parms in the value struct for * a config file (ncx:cli) * * Follow the absolute-path Xpath expression as used * internally to identify a config DB node * and return the val_value_t that it indicates * * Expression must be the node-path from root for * the desired node. * * Error messages are logged by this function * * INPUTS: * startval == top-level start element to search * mod == module to use for the default context * and prefixes will be relative to this module's * import statements. * == NULL and the default registered prefixes * will be used * target == Xpath expression string to evaluate * targval == address of return value (may be NULL) * * OUTPUTS: * if non-NULL inputs and value node found: * *targval == target value node * If non-NULL targval and error exit: * *targval == last good node visited in expression (if any) * * RETURNS: * status *********************************************************************/ extern status_t xpath_find_val_target (val_value_t *startval, ncx_module_t *mod, const xmlChar *target, val_value_t **targval); /******************************************************************** * FUNCTION xpath_find_val_unique * * called by server to find a descendant value node * based on a relative-path sub-clause of a unique-stmt * * Follow the relative-path Xpath expression as used * internally to identify a config DB node * and return the val_value_t that it indicates * * Error messages are logged by this function * only if logerrors is TRUE * * INPUTS: * startval == starting context node (contains unique-stmt) * mod == module to use for the default context * and prefixes will be relative to this module's * import statements. * == NULL and the default registered prefixes * will be used * target == Xpath expression string to evaluate * root = XPath docroot to use * logerrors == TRUE to use log_error, FALSE to skip it * retpcb == address of return value * * OUTPUTS: * if value node found: * *retpcb == malloced XPath PCB with result * * RETURNS: * status *********************************************************************/ extern status_t xpath_find_val_unique (val_value_t *startval, ncx_module_t *mod, const xmlChar *target, val_value_t *root, boolean logerrors, xpath_pcb_t **retpcb); /******************************************************************** * FUNCTION xpath_new_pcb * * malloc a new XPath parser control block * xpathstr is allowed to be NULL, otherwise * a strdup will be made and exprstr will be set * * Create and initialize an XPath parser control block * * INPUTS: * xpathstr == XPath expression string to save (a copy will be made) * == NULL if this step should be skipped * getvar_fn == callback function to retirieve an XPath * variable binding * NULL if no variables are used * * RETURNS: * pointer to malloced struct, NULL if malloc error *********************************************************************/ extern xpath_pcb_t * xpath_new_pcb (const xmlChar *xpathstr, xpath_getvar_fn_t getvar_fn); /******************************************************************** * FUNCTION xpath_new_pcb_ex * * malloc a new XPath parser control block * xpathstr is allowed to be NULL, otherwise * a strdup will be made and exprstr will be set * * Create and initialize an XPath parser control block * * INPUTS: * xpathstr == XPath expression string to save (a copy will be made) * == NULL if this step should be skipped * getvar_fn == callback function to retirieve an XPath * variable binding * NULL if no variables are used * cookie == runstack context pointer to use, cast as a cookie * * RETURNS: * pointer to malloced struct, NULL if malloc error *********************************************************************/ extern xpath_pcb_t * xpath_new_pcb_ex (const xmlChar *xpathstr, xpath_getvar_fn_t getvar_fn, void *cookie); /******************************************************************** * FUNCTION xpath_clone_pcb * * Clone an XPath PCB for a must clause copy * copy from typdef to object for leafref * of object to value for NETCONF PDU processing * * INPUTS: * srcpcb == struct with starting contents * * RETURNS: * new xpatyh_pcb_t clone of the srcmust, NULL if malloc error * It will not be processed or parsed. Only the starter * data will be set *********************************************************************/ extern xpath_pcb_t * xpath_clone_pcb (const xpath_pcb_t *srcpcb); /******************************************************************** * FUNCTION xpath_find_pcb * * Find an XPath PCB * find by exact match of the expressions string * * INPUTS: * pcbQ == Q of xpath_pcb_t structs to check * exprstr == XPath expression string to find * * RETURNS: * pointer to found xpath_pcb_t or NULL if not found *********************************************************************/ extern xpath_pcb_t * xpath_find_pcb (dlq_hdr_t *pcbQ, const xmlChar *exprstr); /******************************************************************** * FUNCTION xpath_free_pcb * * Free a malloced XPath parser control block * * INPUTS: * pcb == pointer to parser control block to free *********************************************************************/ extern void xpath_free_pcb (xpath_pcb_t *pcb); /******************************************************************** * FUNCTION xpath_new_result * * malloc an XPath result * Create and initialize an XPath result struct * * INPUTS: * restype == the desired result type * * RETURNS: * pointer to malloced struct, NULL if malloc error *********************************************************************/ extern xpath_result_t * xpath_new_result (xpath_restype_t restype); /******************************************************************** * FUNCTION xpath_init_result * * Initialize an XPath result struct * malloc an XPath result node * * INPUTS: * result == pointer to result struct to initialize * restype == the desired result type *********************************************************************/ extern void xpath_init_result (xpath_result_t *result, xpath_restype_t restype); /******************************************************************** * FUNCTION xpath_free_result * * Free a malloced XPath result struct * * INPUTS: * result == pointer to result struct to free *********************************************************************/ extern void xpath_free_result (xpath_result_t *result); /******************************************************************** * FUNCTION xpath_clean_result * * Clean an XPath result struct * * INPUTS: * result == pointer to result struct to clean *********************************************************************/ extern void xpath_clean_result (xpath_result_t *result); /******************************************************************** * FUNCTION xpath_new_resnode * * Create and initialize an XPath result node struct * * INPUTS: * restype == the desired result type * * RETURNS: * pointer to malloced struct, NULL if malloc error *********************************************************************/ extern xpath_resnode_t * xpath_new_resnode (void); /******************************************************************** * FUNCTION xpath_init_resnode * * Initialize an XPath result node struct * * INPUTS: * resnode == pointer to result node struct to initialize *********************************************************************/ extern void xpath_init_resnode (xpath_resnode_t *resnode); /******************************************************************** * FUNCTION xpath_free_resnode * * Free a malloced XPath result node struct * * INPUTS: * resnode == pointer to result node struct to free *********************************************************************/ extern void xpath_free_resnode (xpath_resnode_t *resnode); /******************************************************************** * FUNCTION xpath_delete_resnode * * Delete and free a malloced XPath result node struct * * INPUTS: * resnode == pointer to result node struct to free *********************************************************************/ extern void xpath_delete_resnode (xpath_resnode_t *resnode); /******************************************************************** * FUNCTION xpath_clean_resnode * * Clean an XPath result node struct * * INPUTS: * resnode == pointer to result node struct to clean *********************************************************************/ extern void xpath_clean_resnode (xpath_resnode_t *resnode); /******************************************************************** * FUNCTION xpath_get_curmod_from_prefix * * Get the correct module to use for a given prefix * * INPUTS: * prefix == string to check * mod == module to use for the default context * and prefixes will be relative to this module's * import statements. * == NULL and the default registered prefixes * will be used * targmod == address of return module * * OUTPUTS: * *targmod == target moduke to use * * RETURNS: * status *********************************************************************/ extern status_t xpath_get_curmod_from_prefix (const xmlChar *prefix, ncx_module_t *mod, ncx_module_t **targmod); /******************************************************************** * FUNCTION xpath_get_curmod_from_prefix_str * * Get the correct module to use for a given prefix * Unended string version * * INPUTS: * prefix == string to check * prefixlen == length of prefix * mod == module to use for the default context * and prefixes will be relative to this module's * import statements. * == NULL and the default registered prefixes * will be used * targmod == address of return module * * OUTPUTS: * *targmod == target moduke to use * * RETURNS: * status *********************************************************************/ extern status_t xpath_get_curmod_from_prefix_str (const xmlChar *prefix, uint32 prefixlen, ncx_module_t *mod, ncx_module_t **targmod); /******************************************************************** * FUNCTION xpath_parse_token * * Parse the XPath token sequence for a specific token type * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block in progress * tktyp == expected token type * * RETURNS: * status *********************************************************************/ extern status_t xpath_parse_token (xpath_pcb_t *pcb, tk_type_t tktype); /******************************************************************** * FUNCTION xpath_cvt_boolean * * Convert an XPath result to a boolean answer * * INPUTS: * result == result struct to convert to boolean * * RETURNS: * TRUE or FALSE depending on conversion *********************************************************************/ extern boolean xpath_cvt_boolean (const xpath_result_t *result); /******************************************************************** * FUNCTION xpath_cvt_number * * Convert an XPath result to a number answer * * INPUTS: * result == result struct to convert to a number * num == pointer to ncx_num_t to hold the conversion result * * OUTPUTS: * *num == numeric result from conversion * *********************************************************************/ extern void xpath_cvt_number (const xpath_result_t *result, ncx_num_t *num); /******************************************************************** * FUNCTION xpath_cvt_string * * Convert an XPath result to a string answer * * INPUTS: * pcb == parser control block to use * result == result struct to convert to a number * str == pointer to xmlChar * to hold the conversion result * * OUTPUTS: * *str == pointer to malloced string from conversion * * RETURNS: * status; could get an ERR_INTERNAL_MEM error or NO_RER *********************************************************************/ extern status_t xpath_cvt_string (xpath_pcb_t *pcb, const xpath_result_t *result, xmlChar **str); /******************************************************************** * FUNCTION xpath_get_resnodeQ * * Get the renodeQ from a result struct * * INPUTS: * result == result struct to check * * RETURNS: * pointer to resnodeQ or NULL if some error *********************************************************************/ extern dlq_hdr_t * xpath_get_resnodeQ (xpath_result_t *result); /******************************************************************** * FUNCTION xpath_get_first_resnode * * Get the first result in the renodeQ from a result struct * * INPUTS: * result == result struct to check * * RETURNS: * pointer to resnode or NULL if some error *********************************************************************/ extern xpath_resnode_t * xpath_get_first_resnode (xpath_result_t *result); /******************************************************************** * FUNCTION xpath_get_next_resnode * * Get the first result in the renodeQ from a result struct * * INPUTS: * result == result struct to check * * RETURNS: * pointer to resnode or NULL if some error *********************************************************************/ extern xpath_resnode_t * xpath_get_next_resnode (xpath_resnode_t *resnode); /******************************************************************** * FUNCTION xpath_get_resnode_valptr * * Get the first result in the renodeQ from a result struct * * INPUTS: * result == result struct to check * * RETURNS: * pointer to resnode or NULL if some error *********************************************************************/ extern val_value_t * xpath_get_resnode_valptr (xpath_resnode_t *resnode); /******************************************************************** * FUNCTION xpath_get_varbindQ * * Get the varbindQ from a parser control block struct * * INPUTS: * pcb == parser control block to use * * RETURNS: * pointer to varbindQ or NULL if some error *********************************************************************/ extern dlq_hdr_t * xpath_get_varbindQ (xpath_pcb_t *pcb); /******************************************************************** * FUNCTION xpath_move_nodeset * * Move the nodes from a nodeset reult into the * target nodeset result. * This is needed to support partial lock * because multiple select expressions are allowed * for the same partial lock * * INPUTS: * srcresult == XPath result nodeset source * destresult == XPath result nodeset target * * OUTPUTS: * srcresult nodes will be moved to the target result *********************************************************************/ extern void xpath_move_nodeset (xpath_result_t *srcresult, xpath_result_t *destresult); /******************************************************************** * FUNCTION xpath_nodeset_empty * * Check if the result is an empty nodeset * * INPUTS: * result == XPath result to check * * RETURNS: * TRUE if this is an empty nodeset * FALSE if not empty or not a nodeset *********************************************************************/ extern boolean xpath_nodeset_empty (const xpath_result_t *result); /******************************************************************** * FUNCTION xpath_nodeset_swap_valptr * * Check if the result has the oldval ptr and if so, * replace it with the newval ptr * * INPUTS: * result == result struct to check * oldval == value ptr to find * newval == new value replace it with if oldval is found * *********************************************************************/ extern void xpath_nodeset_swap_valptr (xpath_result_t *result, val_value_t *oldval, val_value_t *newval); /******************************************************************** * FUNCTION xpath_nodeset_delete_valptr * * Check if the result has the oldval ptr and if so, * delete it * * INPUTS: * result == result struct to check * oldval == value ptr to find * *********************************************************************/ extern void xpath_nodeset_delete_valptr (xpath_result_t *result, val_value_t *oldval); /******************************************************************** * FUNCTION xpath_convert_url_to_path * * Convert a URL format path to XPath format path * * INPUTS: * urlpath == URL path string to convert to XPath * match_names == enum for selected match names mode * alt_naming == TRUE if alt-name and cli-drop-node-name * containers should be checked * wildcards == TRUE if wildcards allowed instead of key values * FALSE if the '-' wildcard mechanism not allowed * withkeys == TRUE if keys are expected * FALSE if just nodes are expected * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced string containing XPath expression from conversion * NULL if some error *********************************************************************/ extern xmlChar * xpath_convert_url_to_path (const xmlChar *urlpath, ncx_name_match_t match_names, boolean alt_naming, boolean wildcards, boolean withkeys, status_t *res); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xpath */ yuma123_2.14/netconf/src/ncx/runstack.h0000664000175000017500000004320414770023131020144 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_runstack #define _H_runstack /* FILE: runstack.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 22-aug-07 abb Begun; split from ncxcli.c 24-apr-10 abb Redesign to use dynamic memory, allow recursion, and support multiple concurrent contexts 26-apr-10 abb added conditional cmd and loop support */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* this is an arbitrary limit, but it must match the * yangcli:run rpc P1 - Pn variables, currently set to 9 * $1 to $9 parameters passed by yangcli to next script */ #define RUNSTACK_MAX_PARMS 9 /* this is an arbitrary max to limit resources * and run-away scripts that are called recursively */ #define RUNSTACK_MAX_NEST 512 /* this is an arbitrary max to limit * run-away scripts that have bugs */ #define RUNSTACK_MAX_LOOP 65535 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* identify the runstack input source */ typedef enum runstack_src_t_ { RUNSTACK_SRC_NONE, RUNSTACK_SRC_USER, RUNSTACK_SRC_SCRIPT, RUNSTACK_SRC_LOOP } runstack_src_t; /* identify the runstack conditional control block type */ typedef enum runstack_condtype_t_ { RUNSTACK_COND_NONE, RUNSTACK_COND_IF, RUNSTACK_COND_LOOP } runstack_condtype_t; /* keep track of the if,elif,else,end sequence */ typedef enum runstack_ifstate_t_ { RUNSTACK_IF_NONE, RUNSTACK_IF_IF, RUNSTACK_IF_ELIF, RUNSTACK_IF_ELSE } runstack_ifstate_t; /* keep track of the while,end sequence */ typedef enum runstack_loopstate_t_ { RUNSTACK_LOOP_NONE, RUNSTACK_LOOP_COLLECTING, RUNSTACK_LOOP_LOOPING } runstack_loopstate_t; /* control the conditional state for 1 if...end sequence */ typedef struct runstack_ifcb_t_ { runstack_ifstate_t ifstate; boolean startcond; boolean curcond; boolean ifused; /* T already used up */ } runstack_ifcb_t; /* save 1 line for looping purposes */ typedef struct runstack_line_t_ { dlq_hdr_t qhdr; xmlChar *line; } runstack_line_t; /* control the looping for 1 while - end sequence */ typedef struct runstack_loopcb_t_ { uint32 maxloops; uint32 loop_count; runstack_loopstate_t loop_state; boolean startcond; /* condition to evaluate before each loop iteration */ struct xpath_pcb_t_ *xpathpcb; val_value_t *docroot; /* if the collector is non-NULL, then save lines * in this outer while loop */ struct runstack_loopcb_t_ *collector; /* these may point to entries in this lineQ or * the collector's lineQ; only the commands * that are part of the loop contents are saved */ runstack_line_t *first_line; runstack_line_t *cur_line; runstack_line_t *last_line; boolean empty_block; /* this Q will only be used if collector is NULL */ dlq_hdr_t lineQ; /* Q of runstack_line_t */ } runstack_loopcb_t; /* control the looping for 1 while - end sequence */ typedef struct runstack_condcb_t_ { dlq_hdr_t qhdr; runstack_condtype_t cond_type; union u_ { runstack_loopcb_t loopcb; runstack_ifcb_t ifcb; } u; } runstack_condcb_t; /* one script run level context entry * each time a 'run script' command is * encountered, a new stack context is created, * unless max_script_level is reached */ typedef struct runstack_entry_t_ { dlq_hdr_t qhdr; uint32 level; FILE *fp; xmlChar *source; xmlChar *buff; int bufflen; uint32 linenum; dlq_hdr_t parmQ; /* Q of ncx_var_t */ dlq_hdr_t varQ; /* Q of ncx_var_t */ dlq_hdr_t condcbQ; /* Q of runstack_condcb_t */ } runstack_entry_t; typedef struct runstack_context_t_ { boolean cond_state; boolean script_cancel; uint32 script_level; uint32 max_script_level; runstack_src_t cur_src; dlq_hdr_t runstackQ; /* Q of runstack_entry_t */ dlq_hdr_t globalQ; /* Q of ncx_var_t */ dlq_hdr_t zeroQ; /* Q of ncx_var_t */ dlq_hdr_t zero_condcbQ; /* Q of runstack_condcb_t */ } runstack_context_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION runstack_level * * Get the current stack level * * INPUTS: * rcxt == runstack context to use * * RETURNS: * current stack level; 0 --> not in any script *********************************************************************/ extern uint32 runstack_level (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_push * * Add a script nest level context to the stack * Call just after the file is opened and before * the first line has been read * * Still need to add the script parameters (if any) * with the runstack_setparm * * INPUTS: * rcxt == runstack context to use * source == file source * fp == file pointer * * RETURNS: * status *********************************************************************/ extern status_t runstack_push (runstack_context_t *rcxt, const xmlChar *source, FILE *fp); /******************************************************************** * FUNCTION runstack_pop * * Remove a script nest level context from the stack * Call just after script is completed * * INPUTS: * rcxt == runstack context to use *********************************************************************/ extern void runstack_pop (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_get_cmd * * Read the current runstack context and construct * a command string for processing by do_run_script. * - Comment lines will be skipped. * - Extended lines will be concatenated in the * buffer. If a buffer overflow occurs due to this * concatenation, an error will be returned * * INPUTS: * rcxt == runstack context to use * res == address of status result * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to the command line to process (should treat as CONST !!!) * NULL if some error *********************************************************************/ extern xmlChar * runstack_get_cmd (runstack_context_t *rcxt, status_t *res); /******************************************************************** * FUNCTION runstack_cancel * * Cancel all running scripts * * INPUTS: * rcxt == runstack context to use *********************************************************************/ extern void runstack_cancel (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_clear_cancel * * Clear the cancel flags * * INPUTS: * rcxt == runstack context to use *********************************************************************/ extern void runstack_clear_cancel (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_get_que * * Read the current runstack context and figure * out which queue to get * * INPUTS: * rcxt == runstack context to use * isglobal == TRUE if global queue desired * == FALSE if the runstack var que is desired * * RETURNS: * pointer to the requested que *********************************************************************/ extern dlq_hdr_t * runstack_get_que (runstack_context_t *rcxt, boolean isglobal); /******************************************************************** * FUNCTION runstack_get_parm_que * * Get the parameter queue for the current stack level * * INPUTS: * rcxt == runstack context to use * * RETURNS: * que for current stack level, NULL if an error *********************************************************************/ extern dlq_hdr_t * runstack_get_parm_que (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_init * * Must Init this module before using it!!! * *********************************************************************/ extern void runstack_init (void); /******************************************************************** * FUNCTION runstack_cleanup * * Must cleanup this module after using it!!! * *********************************************************************/ extern void runstack_cleanup (void); /******************************************************************** * FUNCTION runstack_clean_context * * INPUTS: * rcxt == runstack context to clean, but not free * *********************************************************************/ extern void runstack_clean_context (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_free_context * * INPUTS: * rcxt == runstack context to free * *********************************************************************/ extern void runstack_free_context (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_init_context * * Initialize a pre-malloced runstack context * * INPUTS: * rcxt == runstack context to free * *********************************************************************/ extern void runstack_init_context (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_new_context * * Malloc a new runstack context * * RETURNS: * malloced and initialized runstack context * *********************************************************************/ extern runstack_context_t * runstack_new_context (void); /******************************************************************** * FUNCTION runstack_session_cleanup * * Cleanup after a yangcli session has ended * * INPUTS: * rcxt == runstack context to use * *********************************************************************/ extern void runstack_session_cleanup (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_get_source * * Get the current input source for the runstack context * * INPUTS: * rcxt == runstack context to use * *********************************************************************/ extern runstack_src_t runstack_get_source (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_save_line * * Save the current line if needed if a loop is active * * INPUTS: * rcxt == runstack context to use * line == line to save, a copy will be made * * RETURNS: * status *********************************************************************/ extern status_t runstack_save_line (runstack_context_t *rcxt, const xmlChar *line); /******************************************************************** * FUNCTION runstack_get_loop_cmd * * Get the next command during loop processing. * This function is called after the while loop end * has been reached, and buffered commands are used * * INPUTS: * rcxt == runstack context to use * res == address of status result * * OUTPUTS: * *res == function result status * == ERR_NCX_LOOP_ENDED if the loop expr was just * evaluated to FALSE; In this case the caller must * use the runstack_get_source function to determine * the source of the next line. If this occurs, * the loopcb (and while context) will be removed from * the curent runstack frame * * RETURNS: * pointer to the command line to process (should treat as CONST !!!) * NULL if some error *********************************************************************/ extern xmlChar * runstack_get_loop_cmd (runstack_context_t *rcxt, status_t *res); /******************************************************************** * FUNCTION runstack_get_cond_state * * Get the current conditional code state for the context * * INPUTS: * rcxt == runstack context to use * * RETURNS: * TRUE if in a TRUE conditional or no conditional * FALSE if in a FALSE conditional statement right now *********************************************************************/ extern boolean runstack_get_cond_state (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_handle_while * * Process the current command, which is a 'while' command. * This must be called before the next command is retrieved * during runstack context processing * * INPUTS: * rcxt == runstack context to use * maxloops == max number of loop iterations * xpathpcb == XPath control block to save which contains * the expression to be processed. * !!! this memory is handed off here; it will * !!! be freed as part of the loopcb context * docroot == docroot var struct that represents * the XML document to run the XPath expr against * !!! this memory is handed off here; it will * !!! be freed as part of the loopcb context * * RETURNS: * status; if status != NO_ERR then the xpathpcb and docroot * parameters need to be freed by the caller *********************************************************************/ extern status_t runstack_handle_while (runstack_context_t *rcxt, uint32 maxloops, struct xpath_pcb_t_ *xpathpcb, val_value_t *docroot); /******************************************************************** * FUNCTION runstack_handle_if * * Handle the if command for the specific runstack context * * INPUTS: * rcxt == runstack context to use * startcond == start condition state for this if block * may be FALSE because the current conditional * state is already FALSE * * RETURNS: * status *********************************************************************/ extern status_t runstack_handle_if (runstack_context_t *rcxt, boolean startcond); /******************************************************************** * FUNCTION runstack_handle_elif * * Handle the elif command for the specific runstack context * * INPUTS: * rcxt == runstack context to use * startcond == start condition state for this if block * may be FALSE because the current conditional * state is already FALSE * * RETURNS: * status *********************************************************************/ extern status_t runstack_handle_elif (runstack_context_t *rcxt, boolean startcond); /******************************************************************** * FUNCTION runstack_handle_else * * Handle the elsecommand for the specific runstack context * * INPUTS: * rcxt == runstack context to use * * RETURNS: * status *********************************************************************/ extern status_t runstack_handle_else (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_handle_end * * Handle the end command for the specific runstack context * * INPUTS: * rcxt == runstack context to use * * RETURNS: * status *********************************************************************/ extern status_t runstack_handle_end (runstack_context_t *rcxt); /******************************************************************** * FUNCTION runstack_get_if_used * * Check if the run context, which should be inside an if-stmt * now, has used the 'true' block already * * INPUTS: * rcxt == runstack context to use * * RETURNS: * TRUE if TRUE block already used * FALSE if FALSE not already used or not in an if-block *********************************************************************/ extern boolean runstack_get_if_used (runstack_context_t *rcxt); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_runstack */ yuma123_2.14/netconf/src/ncx/conf.c0000664000175000017500000006440214770023131017235 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: conf.c This module parses an NCX Text config file The text input format matches the output format of vaL-dump_value and val_stdout_value. In addition to that syntax, comments may also be present. The '#' char starts a comments and all chars up until the next newline char (but not including the newline) end a comment. A comment cannot be inside a string. Commands in a config file are not whitespace sensitive, except within string values. Unless inside a string, a newline will end a command line, so value pairs must appear on the same line. foo 42 bar fred baz fred flintstone goo "multi line value entered for goo must start on the same line as goo" Containers are represented by curly braces myvars { foo 42 bar fred baz fred flintstone } The left brace must appear on the same line as foo The right brace must be after the newline for the preceding (baz) command. Objects with index values are listed sequentially after the list name. Whitespace is significant, so string index values with whitespace or newlines must be in double quotes. The nodes within the array which represent the index will not be repreated inside the curly braces. For example, the array 'myarray' is indexed by 'foo', so it does not appear again within the entry. myarray 42 { bar fred baz fred flintstone } point 22 478 { red 6 green 17 blue 9 } ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 20oct07 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_conf #include "conf.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_val_util #include "val_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define CONF_DEBUG 1 #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * FUNCTION adv_tk * * Advance to the next token * Print error message if EOF found instead * * INPUTS: * tkc == token chain * * RETURNS: * status *********************************************************************/ static status_t adv_tk (tk_chain_t *tkc) { status_t res; res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, NULL, res); } return res; } /* adv_tk */ /******************************************************************** * FUNCTION get_tk * * Get the next token * Skip over TK_TT_NEWLINE until a different type is found * * INPUTS: * tkc == token chain * * RETURNS: * status *********************************************************************/ static status_t get_tk (tk_chain_t *tkc) { boolean done; status_t res; res = NO_ERR; done = FALSE; while (!done) { res = TK_ADV(tkc); if (res != NO_ERR) { done = TRUE; } else if (TK_CUR_TYP(tkc) != TK_TT_NEWLINE) { done = TRUE; } } return res; } /* get_tk */ /******************************************************************** * FUNCTION consume_tk * * Consume a specified token type * Skip over TK_TT_NEWLINE until a different type is found * * INPUTS: * tkc == token chain * ttyp == expected token type * * RETURNS: * status *********************************************************************/ static status_t consume_tk (tk_chain_t *tkc, tk_type_t ttyp) { status_t res; res = get_tk(tkc); if (res != NO_ERR) { return res; } else { return (TK_CUR_TYP(tkc) == ttyp) ? NO_ERR : ERR_NCX_WRONG_TKTYPE; } /*NOTREACHED*/ } /* consume_tk */ /******************************************************************** * FUNCTION match_name * * Get the next token which must be a string which matches name * * INPUTS: * tkc == token chain * name == name to match * * RETURNS: * status of the operation *********************************************************************/ static status_t match_name (tk_chain_t *tkc, const xmlChar *name) { status_t res; /* get the next token */ res = consume_tk(tkc, TK_TT_TSTRING); if (res == NO_ERR) { if (xml_strcmp(TK_CUR_VAL(tkc), name)) { res = ERR_NCX_WRONG_VAL; } } return res; } /* match_name */ /******************************************************************** * FUNCTION skip_object * * Skip until the end of the object * current token is the parmset name * * INPUTS: * tkc == token chain * * RETURNS: * status of the operation *********************************************************************/ static status_t skip_object (tk_chain_t *tkc) { status_t res; uint32 brace_count; boolean done; brace_count = 0; done = FALSE; /* get the next token */ while (!done) { res = TK_ADV(tkc); if (res != NO_ERR) { return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_LBRACE: brace_count++; break; case TK_TT_RBRACE: if (brace_count <= 1) { done = TRUE; } else { brace_count--; } default: ; } } return NO_ERR; } /* skip_object */ /******************************************************************** * FUNCTION parse_index * * Parse, and fill the indexQ for one val_value_t struct during * processing of a text config file * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * The value name is the current token. * Based on the value typdef, the res of the tokens * comprising the value statement will be processed * * INPUTS: * tkc == token chain * obj == the object template to use for filling in 'val' * val == initialized value struct, without any value, * which will be filled in by this function * nsid == namespace ID to use for this value * * OUTPUTS: * indexQ filled in as tokens representing the index components * are parsed. NEWLINE tokens are skipped as needed * * RETURNS: * status of the operation *********************************************************************/ static status_t parse_index (tk_chain_t *tkc, obj_template_t *obj, val_value_t *val, xmlns_id_t nsid) { obj_key_t *indef, *infirst; val_value_t *inval; status_t res; infirst = obj_first_key(obj); /* first make value nodes for all the index values */ for (indef = infirst; indef != NULL; indef = obj_next_key(indef)) { /* advance to the next non-NEWLINE token */ res = get_tk(tkc); if (res != NO_ERR) { ncx_conf_exp_err(tkc, res, "index value"); return res; } /* check if a valid token is given for the index value */ if (TK_CUR_TEXT(tkc)) { inval = val_make_simval_obj(indef->keyobj, TK_CUR_VAL(tkc), &res); if (!inval) { ncx_conf_exp_err(tkc, res, "index value"); return res; } else { val_change_nsid(inval, nsid); val_add_child(inval, val); } } else { res = ERR_NCX_WRONG_TKTYPE; ncx_conf_exp_err(tkc, res, "index value"); return res; } } /* generate the index chain in the indexQ */ res = val_gen_index_chain(obj, val); if (res != NO_ERR) { ncx_print_errormsg(tkc, NULL, res); } return res; } /* parse_index */ /******************************************************************** * FUNCTION parse_val * * Parse, and fill one val_value_t struct during * processing of a text config file * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * The value name is the current token. * Based on the value typdef, the res of the tokens * comprising the value statement will be processed * * INPUTS: * tkc == token chain * obj == the object template struct to use for filling in 'val' * val == initialized value struct, without any value, * which will be filled in by this function * nsid == namespace ID to use for this value * valname == name of the value struct * * RETURNS: * status of the operation *********************************************************************/ static status_t parse_val (tk_chain_t *tkc, obj_template_t *obj, val_value_t *val) { obj_template_t *chobj; val_value_t *chval; const xmlChar *valname, *useval; typ_def_t *typdef; status_t res; ncx_btype_t btyp; boolean done; xmlns_id_t nsid; btyp = obj_get_basetype(obj); nsid = obj_get_nsid(obj); valname = obj_get_name(obj); typdef = obj_get_typdef(obj); /* check if there is an index clause expected */ if (typ_has_index(btyp)) { res = parse_index(tkc, obj, val, nsid); if (res != NO_ERR) { return res; } } /* get next token, NEWLINE is significant at this point */ res = adv_tk(tkc); if (res != NO_ERR) { return res; } /* the current token should be the value for a leaf * or a left brace for the start of a complex type * A NEWLINE is treated as if the user entered a * zero-length string for the value. (Unless the * base type is NCX_BT_EMPTY, in which case the NEWLINE * is the expected token */ if (typ_is_simple(btyp)) { /* form for a leaf is: foo [value] NEWLINE */ if (TK_CUR_TYP(tkc)==TK_TT_NEWLINE) { useval = NULL; } else { useval = TK_CUR_VAL(tkc); } res = val_set_simval(val, typdef, nsid, valname, useval); if (res != NO_ERR) { log_error("\nError: '%s' cannot be set to '%s'", valname, (TK_CUR_VAL(tkc)) ? TK_CUR_VAL(tkc) : EMPTY_STRING); if (btyp == NCX_BT_EMPTY) { ncx_conf_exp_err(tkc, res, "empty"); } else { ncx_conf_exp_err(tkc, res, "simple value string"); } return res; } /* get a NEWLINE unless current token is already a NEWLINE */ if (TK_CUR_TYP(tkc) != TK_TT_NEWLINE) { res = adv_tk(tkc); if (res != NO_ERR) { return res; } if (TK_CUR_TYP(tkc) != TK_TT_NEWLINE) { res = ERR_NCX_WRONG_TKTYPE; ncx_conf_exp_err(tkc, res, "\\n"); } } } else { /* complex type is foo { ... } or * foo index1 index2 { ... } * If there is an index, it was already parsed */ res = consume_tk(tkc, TK_TT_LBRACE); if (res != NO_ERR) { ncx_conf_exp_err(tkc, res, "left brace"); return res; } /* get all the child nodes specified for this complex type */ res = NO_ERR; done = FALSE; while (!done && res==NO_ERR) { /* start out looking for a child node name or a * right brace to end the sub-section */ if (tk_next_typ(tkc)==TK_TT_NEWLINE) { /* skip the NEWLINE token */ (void)adv_tk(tkc); } else if (tk_next_typ(tkc)==TK_TT_RBRACE) { /* found end of sub-section */ done = TRUE; } else { /* get the next token */ res = adv_tk(tkc); if (res != NO_ERR) { continue; } /* make sure cur token is an identifier string * if so, find the child node and call this function * recursively to fill it in and add it to * the parent 'val' */ if (TK_CUR_ID(tkc)) { /* parent 'typdef' must have a child with a name * that matches the current token vale */ chobj = obj_find_child(obj, TK_CUR_MOD(tkc), TK_CUR_VAL(tkc)); if (chobj) { chval = val_new_value(); if (!chval) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, NULL, res); } else { val_init_from_template(chval, chobj); res = parse_val(tkc, chobj, chval); if (res == NO_ERR) { val_add_child(chval, val); } else { val_free_value(chval); } } } else { /* string is not a child name in this typdef */ res = ERR_NCX_DEF_NOT_FOUND; ncx_conf_exp_err(tkc, res, "identifier string"); } } else { /* token is not an identifier string */ res = ERR_NCX_WRONG_TKTYPE; ncx_conf_exp_err(tkc, res, "identifier string"); } } } /* end loop through all the child nodes */ /* expecting a right brace to finish the complex value */ if (res == NO_ERR) { res = consume_tk(tkc, TK_TT_RBRACE); if (res != NO_ERR) { ncx_conf_exp_err(tkc, res, "right brace"); return res; } } } return res; } /* parse_val */ /******************************************************************** * FUNCTION parse_parm * * Parse, and fill one val_value_t struct during * processing of a parmset * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * * INPUTS: * tkc == token chain * val == container val to fill in * keepvals == TRUE to save existing parms in 'ps', as needed * FALSE to overwrite old parms in 'ps', as needed * * RETURNS: * status of the operation *********************************************************************/ static status_t parse_parm (tk_chain_t *tkc, val_value_t *val, boolean keepvals) { obj_template_t *obj; const xmlChar *modname; val_value_t *curparm, *newparm; status_t res; ncx_iqual_t iqual; boolean match, usewarning, isdefault; /* get the next token, which must be a TSTRING * representing the parameter name */ if (TK_CUR_TYP(tkc) != TK_TT_TSTRING) { res = ERR_NCX_WRONG_TKTYPE; ncx_conf_exp_err(tkc, res, "parameter name"); return res; } curparm = NULL; usewarning = ncx_warning_enabled(ERR_NCX_CONF_PARM_EXISTS); /* check if this TSTRING is a parameter in this parmset * make sure to always check for prefix:identifier * This is automatically processed in tk.c */ if (TK_CUR_MOD(tkc)) { modname = xmlns_get_module (xmlns_find_ns_by_prefix(TK_CUR_MOD(tkc))); if (modname) { curparm = val_find_child(val, modname, TK_CUR_VAL(tkc)); } } else { curparm = val_find_child(val, val_get_mod_name(val), TK_CUR_VAL(tkc)); } if (curparm) { obj = curparm->obj; } else { obj = obj_find_child(val->obj, TK_CUR_MOD(tkc), TK_CUR_VAL(tkc)); } if (!obj) { res = ERR_NCX_UNKNOWN_PARM; if (TK_CUR_MOD(tkc)) { log_error("\nError: parameter '%s:%s' not found", TK_CUR_MOD(tkc), TK_CUR_VAL(tkc)); } else { log_error("\nError: parameter '%s' not found", TK_CUR_VAL(tkc)); } ncx_conf_exp_err(tkc, res, "parameter name"); return res; } /* got a valid parameter name, now create a new parm * even if it may not be kept. There are corner-cases * that require the new value be parsed before knowing * if a parm value is a duplicate or not */ newparm = val_new_value(); if (!newparm) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, NULL, res); return res; } val_init_from_template(newparm, obj); /* parse the parameter value */ res = parse_val(tkc, obj, newparm); if (res != NO_ERR) { val_free_value(newparm); return res; } /* check if a potential current value exists, or just * add the newparm to the parmset */ if (curparm) { isdefault = val_set_by_default(curparm); iqual = obj_get_iqualval(obj); if (iqual == NCX_IQUAL_ONE || iqual == NCX_IQUAL_OPT) { /* only one allowed, check really a match */ match = TRUE; if (val_has_index(curparm) && !val_index_match(newparm, curparm)) { match = FALSE; } if (!match) { val_add_child(newparm, val); } else if (isdefault) { dlq_remove(curparm); val_free_value(curparm); val_add_child(newparm, val); } else if (keepvals) { if (usewarning) { /* keep current value and toss new value */ log_warn("\nWarning: Parameter '%s' already exists. " "Not using new value\n", curparm->name); if (LOGDEBUG2) { val_dump_value(newparm, NCX_DEF_INDENT); log_debug2("\n"); } } val_free_value(newparm); } else { if (usewarning) { /* replace current value and warn old value tossed */ log_warn("\nconf: Parameter '%s' already exists. " "Overwriting with new value\n", curparm->name); if (LOGDEBUG2) { val_dump_value(newparm, NCX_DEF_INDENT); log_debug2("\n"); } } dlq_remove(curparm); val_free_value(curparm); val_add_child(newparm, val); } } else { /* mutliple instances allowed */ val_add_child(newparm, val); } } else { val_add_child(newparm, val); } return NO_ERR; } /* parse_parm */ /******************************************************************** * FUNCTION parse_top * * Parse, and fill one val_value_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * * INPUTS: * tkc == token chain * val == value iniitalized and could be already filled in * keepvals == TRUE to save existing values in 'val', as needed * FALSE to overwrite old values in 'val', as needed * * RETURNS: * status of the operation *********************************************************************/ static status_t parse_top (tk_chain_t *tkc, val_value_t *val, boolean keepvals) { status_t res; boolean done; res = NO_ERR; /* get the container name */ done = FALSE; while (!done) { res = match_name(tkc, obj_get_name(val->obj)); if (res == ERR_NCX_EOF) { if (LOGDEBUG) { log_debug("\nconf: object '%s' not found in file '%s'", obj_get_name(val->obj), tkc->filename); } return NO_ERR; } else if (res != NO_ERR) { res = skip_object(tkc); if (res != NO_ERR) { return res; } } else { done = TRUE; } } /* get a left brace */ res = consume_tk(tkc, TK_TT_LBRACE); if (res != NO_ERR) { ncx_conf_exp_err(tkc, res, "left brace to start object"); return res; } done = FALSE; while (!done) { res = get_tk(tkc); if (res == ERR_NCX_EOF) { return NO_ERR; } else if (res != NO_ERR) { return res; } /* allow an empty parmset */ if (TK_CUR_TYP(tkc)==TK_TT_RBRACE) { done = TRUE; } else { res = parse_parm(tkc, val, keepvals); if (res != NO_ERR) { done = TRUE; } } } return res; } /* parse_top */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION conf_parse_from_filespec * * Parse a file as an NCX text config file against * a specific parmset definition. Fill in an * initialized parmset (and could be partially filled in) * * Error messages are printed by this function!! * * If a value is already set, and only one value is allowed * then the 'keepvals' parameter will control whether that * value will be kept or overwitten * * INPUTS: * filespec == absolute path or relative path * This string is used as-is without adjustment. * val == value struct to fill in, must be initialized * already with val_new_value or val_init_value * keepvals == TRUE if old values should always be kept * FALSE if old vals should be overwritten * fileerr == TRUE to generate a missing file error * FALSE to return NO_ERR instead, if file not found * * RETURNS: * status of the operation *********************************************************************/ status_t conf_parse_val_from_filespec (const xmlChar *filespec, val_value_t *val, boolean keepvals, boolean fileerr) { tk_chain_t *tkc; FILE *fp; xmlChar *sourcespec; status_t res; #ifdef DEBUG if (!filespec || !val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (*filespec == 0) { log_error("\nError: no config file name specified"); return ERR_NCX_INVALID_VALUE; } res = NO_ERR; sourcespec = ncx_get_source(filespec, &res); if (!sourcespec) { return res; } fp = fopen((const char *)sourcespec, "r"); if (!fp) { m__free(sourcespec); if (fileerr) { log_error("\nError: config file '%s' could not be opened", filespec); return ERR_FIL_OPEN; } else { return NO_ERR; } } if (LOGINFO) { log_info("\nLoading CLI parameters from '%s'", sourcespec); } m__free(sourcespec); sourcespec = NULL; /* get a new token chain */ res = NO_ERR; tkc = tk_new_chain(); if (!tkc) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(NULL, NULL, res); fclose(fp); return res; } /* else setup the token chain and parse this config file */ tk_setup_chain_conf(tkc, fp, filespec); res = tk_tokenize_input(tkc, NULL); #ifdef CONF_TK_DEBUG if (LOGDEBUG3) { tk_dump_chain(tkc); } #endif if (res == NO_ERR) { res = parse_top(tkc, val, keepvals); } fclose(fp); tkc->fp = NULL; tk_free_chain(tkc); if (res != NO_ERR) { log_error("\nError: invalid .conf file '%s' (%s)", filespec, get_error_string(res)); } return res; } /* conf_parse_val_from_filespec */ /* END file conf.c */ yuma123_2.14/netconf/src/ncx/cfg.c0000664000175000017500000011563214770023131017051 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: cfg.c CFG locking: - if a partial lock exists then a global lock will fail - if a global lock exists then the same session ID can still add partial locks - Only VAL_MAX_PARTIAL_LOCKS con-current partial locks can be held for the same value node; MUST be at least 2 - A partial lock will fail if any part of the subtree is already partial-locked by another session - CFG_ST_READY to CFG_ST_FLOCK is always OK - CFG_ST_FLOCK to/from CFG_ST_PLOCK is not allowed - partial-lock(s) or global lock can be active, but not both - deleting a session will cause all locks for that session to be deleted. Any global locks on the candidate will cause a discard-changes ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 15apr06 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "cfg.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "ncx_list.h" #include "ncx_num.h" #include "ncxconst.h" #include "ncxmod.h" #include "plock.h" #include "plock_cb.h" #include "rpc.h" #include "rpc_err.h" #include "ses.h" #include "status.h" #include "tstamp.h" #include "val.h" #include "xmlns.h" #include "xml_util.h" #include "xpath.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define CFG_NUM_STATIC 3 #define CFG_DATETIME_LEN 64 #define MAX_CFGID NCX_CFGID_STARTUP /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean cfg_init_done = FALSE; static cfg_template_t *cfg_arr[CFG_NUM_STATIC]; /******************************************************************** * FUNCTION get_template * * Get the config template from its name * * INPUTS: * name == config name * RETURNS: * pointer config template or NULL if not found *********************************************************************/ static cfg_template_t * get_template (const xmlChar *cfgname) { ncx_cfg_t id; for (id = NCX_CFGID_RUNNING; id <= MAX_CFGID; id++) { if (!cfg_arr[id]) { continue; } if (!xml_strcmp(cfg_arr[id]->name, cfgname)) { return cfg_arr[id]; } } return NULL; } /* get_template */ /******************************************************************** * FUNCTION free_template * * Clean and free the cfg_template_t struct * * INPUTS: * cfg = cfg_template_t to clean and free * RETURNS: * none *********************************************************************/ static void free_template (cfg_template_t *cfg) { rpc_err_rec_t *err; plock_cb_t *plock; #ifdef DEBUG if (!cfg) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (cfg->root) { val_free_value(cfg->root); } if (cfg->name) { m__free(cfg->name); } if (cfg->src_url) { m__free(cfg->src_url); } while (!dlq_empty(&cfg->load_errQ)) { err = (rpc_err_rec_t *)dlq_deque(&cfg->load_errQ); rpc_err_free_record(err); } while (!dlq_empty(&cfg->plockQ)) { plock = (plock_cb_t *)dlq_deque(&cfg->plockQ); plock_cb_free(plock); } m__free(cfg); } /* free_template */ /******************************************************************** * FUNCTION new_template * * Malloc and initialize a cfg_template_t struct * * INPUTS: * name == cfg name * cfg_id == cfg ID * RETURNS: * malloced struct or NULL if some error * This struct needs to be freed by the caller *********************************************************************/ static cfg_template_t * new_template (const xmlChar *name, ncx_cfg_t cfg_id) { ncx_module_t *mod; cfg_template_t *cfg; obj_template_t *cfgobj; cfgobj = NULL; mod = ncx_find_module(NCXMOD_NETCONF, NULL); if (mod) { cfgobj = ncx_find_object(mod, NCX_EL_CONFIG); } if (!cfgobj) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } cfg = m__getObj(cfg_template_t); if (!cfg) { return NULL; } memset(cfg, 0x0, sizeof(cfg_template_t)); dlq_createSQue(&cfg->load_errQ); dlq_createSQue(&cfg->plockQ); cfg->name = xml_strdup(name); if (!cfg->name) { free_template(cfg); return NULL; } /* give each config a valid last changed time */ cfg_update_last_ch_time(cfg); cfg->cfg_id = cfg_id; cfg->cfg_state = CFG_ST_INIT; if (cfg_id != NCX_CFGID_CANDIDATE) { cfg->root = val_new_value(); if (!cfg->root) { free_template(cfg); return NULL; } /* finish setting up the root value */ val_init_from_template(cfg->root, cfgobj); } /* else root will be set next with val_clone_config_data */ return cfg; } /* new_template */ /***************** E X P O R T E D F U N C T I O N S ***********/ /******************************************************************** * FUNCTION cfg_init * * Initialize the config manager * * INPUTS: * none * RETURNS: * none *********************************************************************/ void cfg_init (void) { uint32 i; if (!cfg_init_done) { for (i=0; i MAX_CFGID) { return SET_ERROR(ERR_INTERNAL_VAL); } if (cfg_arr[cfg_id]) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* get the hard-wired config name */ switch (cfg_id) { case NCX_CFGID_RUNNING: name = NCX_CFG_RUNNING; break; case NCX_CFGID_CANDIDATE: name = NCX_CFG_CANDIDATE; break; case NCX_CFGID_STARTUP: name = NCX_CFG_STARTUP; break; default: return SET_ERROR(ERR_INTERNAL_VAL); } cfg = new_template(name, cfg_id); if (!cfg) { return ERR_INTERNAL_MEM; } cfg_arr[cfg_id] = cfg; return NO_ERR; } /* cfg_init_static_db */ /******************************************************************** * FUNCTION cfg_new_template * * Malloc and initialize a cfg_template_t struct * * INPUTS: * name == cfg name * cfg_id == cfg ID * RETURNS: * malloced struct or NULL if some error * This struct needs to be freed by the caller *********************************************************************/ cfg_template_t * cfg_new_template (const xmlChar *name, ncx_cfg_t cfg_id) { cfg_template_t *cfg; cfg = m__getObj(cfg_template_t); if (!cfg) { return NULL; } memset(cfg, 0x0, sizeof(cfg_template_t)); cfg->name = xml_strdup(name); if (!cfg->name) { m__free(cfg); return NULL; } cfg->cfg_id = cfg_id; cfg->cfg_state = CFG_ST_INIT; dlq_createSQue(&cfg->load_errQ); /* root is still NULL; indicates empty cfg */ return cfg; } /* cfg_new_template */ /******************************************************************** * FUNCTION cfg_free_template * * Clean and free the cfg_template_t struct * * INPUTS: * cfg = cfg_template_t to clean and free * RETURNS: * none *********************************************************************/ void cfg_free_template (cfg_template_t *cfg) { #ifdef DEBUG if (!cfg) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif free_template(cfg); } /* cfg_free_template */ /******************************************************************** * FUNCTION cfg_set_state * * Change the state of the specified static config * * INPUTS: * cfg_id = Config ID to change * new_state == new config state to set * RETURNS: * none *********************************************************************/ void cfg_set_state (ncx_cfg_t cfg_id, cfg_state_t new_state) { #ifdef DEBUG if (cfg_id > MAX_CFGID) { SET_ERROR(ERR_INTERNAL_VAL); return; } if (!cfg_arr[cfg_id]) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif cfg_arr[cfg_id]->cfg_state = new_state; } /* cfg_set_state */ /******************************************************************** * FUNCTION cfg_get_state * * Get the state of the specified static config * * INPUTS: * cfg_id = Config ID * RETURNS: * config state (CFG_ST_NONE if some error) *********************************************************************/ cfg_state_t cfg_get_state (ncx_cfg_t cfg_id) { #ifdef DEBUG if (cfg_id > MAX_CFGID) { SET_ERROR(ERR_INTERNAL_VAL); return CFG_ST_NONE; } #endif if (!cfg_arr[cfg_id]) { return CFG_ST_NONE; } return cfg_arr[cfg_id]->cfg_state; } /* cfg_get_state */ /******************************************************************** * FUNCTION cfg_get_config * * Get the config struct from its name * * INPUTS: * cfgname = Config Name * RETURNS: * pointer to config struct or NULL if not found *********************************************************************/ cfg_template_t * cfg_get_config (const xmlChar *cfgname) { #ifdef DEBUG if (!cfgname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return get_template(cfgname); } /* cfg_get_config */ /******************************************************************** * FUNCTION cfg_get_config_name * * Get the config name from its ID * * INPUTS: * cfgid == config ID * RETURNS: * pointer to config name or NULL if not found *********************************************************************/ const xmlChar * cfg_get_config_name (ncx_cfg_t cfgid) { cfg_template_t *cfg = cfg_get_config_id(cfgid); if (cfg) { return cfg->name; } return NULL; } /* cfg_get_config_name */ /******************************************************************** * FUNCTION cfg_get_config_id * * Get the config struct from its ID * * INPUTS: * cfgid == config ID * RETURNS: * pointer to config struct or NULL if not found *********************************************************************/ cfg_template_t * cfg_get_config_id (ncx_cfg_t cfgid) { if (cfgid <= MAX_CFGID) { return cfg_arr[cfgid]; } return NULL; } /* cfg_get_config_id */ /******************************************************************** * FUNCTION cfg_set_target * * Set the CFG_FL_TARGET flag in the specified config * * INPUTS: * cfg_id = Config ID to set as a valid target * *********************************************************************/ void cfg_set_target (ncx_cfg_t cfg_id) { #ifdef DEBUG if (cfg_id > MAX_CFGID) { SET_ERROR(ERR_INTERNAL_VAL); return; } if (!cfg_arr[cfg_id]) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif cfg_arr[cfg_id]->flags |= CFG_FL_TARGET; } /* cfg_set_target */ /******************************************************************** * FUNCTION cfg_fill_candidate_from_running * * Fill the config with the config contents * of the config * * RETURNS: * status *********************************************************************/ status_t cfg_fill_candidate_from_running (void) { cfg_template_t *running, *candidate; status_t res; #ifdef DEBUG if (!cfg_arr[NCX_CFGID_RUNNING] || !cfg_arr[NCX_CFGID_CANDIDATE]) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif running = cfg_arr[NCX_CFGID_RUNNING]; candidate = cfg_arr[NCX_CFGID_CANDIDATE]; if (!running->root) { return ERR_NCX_DATA_MISSING; } if (candidate->root) { val_free_value(candidate->root); candidate->root = NULL; } res = NO_ERR; candidate->root = val_clone_config_data(running->root, &res); candidate->flags &= ~CFG_FL_DIRTY; candidate->last_txid = running->last_txid; candidate->cur_txid = 0; return res; } /* cfg_fill_candidate_from_running */ /******************************************************************** * FUNCTION cfg_fill_candidate_from_startup * * Fill the config with the config contents * of the config * * RETURNS: * status *********************************************************************/ status_t cfg_fill_candidate_from_startup (void) { cfg_template_t *startup, *candidate; status_t res; #ifdef DEBUG if (!cfg_arr[NCX_CFGID_CANDIDATE] || !cfg_arr[NCX_CFGID_STARTUP]) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif startup = cfg_arr[NCX_CFGID_STARTUP]; candidate = cfg_arr[NCX_CFGID_CANDIDATE]; if (!startup->root) { return ERR_NCX_DATA_MISSING; } if (candidate->root) { val_free_value(candidate->root); candidate->root = NULL; } res = NO_ERR; candidate->root = val_clone2(startup->root); if (candidate->root == NULL) { res = ERR_INTERNAL_MEM; } candidate->flags &= ~CFG_FL_DIRTY; candidate->last_txid = startup->last_txid; candidate->cur_txid = 0; return res; } /* cfg_fill_candidate_from_startup */ /******************************************************************** * FUNCTION cfg_fill_candidate_from_inline * * Fill the config with the config contents * of the inline XML node * * INPUTS: * newroot == new root for the candidate config * * RETURNS: * status *********************************************************************/ status_t cfg_fill_candidate_from_inline (val_value_t *newroot) { cfg_template_t *candidate; status_t res; #ifdef DEBUG if (newroot == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!cfg_arr[NCX_CFGID_CANDIDATE]) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif candidate = cfg_arr[NCX_CFGID_CANDIDATE]; if (candidate->root) { val_free_value(candidate->root); candidate->root = NULL; } res = NO_ERR; candidate->root = val_clone_config_data(newroot, &res); candidate->flags &= ~CFG_FL_DIRTY; return res; } /* cfg_fill_candidate_from_inline */ /******************************************************************** * FUNCTION cfg_set_dirty_flag * * Mark the config as 'changed' * * INPUTS: * cfg == configuration template to set * *********************************************************************/ void cfg_set_dirty_flag (cfg_template_t *cfg) { #ifdef DEBUG if (!cfg) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif cfg->flags |= CFG_FL_DIRTY; } /* cfg_set_dirty_flag */ /******************************************************************** * FUNCTION cfg_get_dirty_flag * * Get the config dirty flag value * * INPUTS: * cfg == configuration template to check * *********************************************************************/ boolean cfg_get_dirty_flag (const cfg_template_t *cfg) { #ifdef DEBUG if (!cfg) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return (cfg->flags & CFG_FL_DIRTY) ? TRUE : FALSE; } /* cfg_get_dirty_flag */ /******************************************************************** * FUNCTION cfg_ok_to_lock * * Check if the specified config can be locked right now * for global lock only * * INPUTS: * cfg = Config template to check * * RETURNS: * status *********************************************************************/ status_t cfg_ok_to_lock (const cfg_template_t *cfg) { status_t res; #ifdef DEBUG if (!cfg) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif switch (cfg->cfg_state) { case CFG_ST_READY: if (cfg->cfg_id == NCX_CFGID_CANDIDATE) { /* lock cannot be granted if any changes * to the candidate are already made */ res = (cfg_get_dirty_flag(cfg)) ? ERR_NCX_CANDIDATE_DIRTY : NO_ERR; } else { /* lock can be granted if state is ready */ res = NO_ERR; } break; case CFG_ST_PLOCK: case CFG_ST_FLOCK: /* full or partial lock already held by a session */ res = ERR_NCX_LOCK_DENIED; break; case CFG_ST_NONE: case CFG_ST_INIT: case CFG_ST_CLEANUP: /* config is in a state where locks cannot be granted */ res = ERR_NCX_NO_ACCESS_STATE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* cfg_ok_to_lock */ /******************************************************************** * FUNCTION cfg_ok_to_unlock * * Check if the specified config can be unlocked right now * by the specified session ID; for global lock only * * INPUTS: * cfg = Config template to check * sesid == session ID requesting to unlock the config * RETURNS: * status *********************************************************************/ status_t cfg_ok_to_unlock (const cfg_template_t *cfg, ses_id_t sesid) { status_t res; #ifdef DEBUG if (!cfg) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif switch (cfg->cfg_state) { case CFG_ST_PLOCK: res = ERR_NCX_NO_ACCESS_STATE; break; case CFG_ST_FLOCK: /* check if global lock is granted to the correct user */ if (cfg->locked_by == sesid) { res = NO_ERR; } else { res = ERR_NCX_NO_ACCESS_LOCK; } break; case CFG_ST_NONE: case CFG_ST_INIT: case CFG_ST_READY: case CFG_ST_CLEANUP: /* config is not in a locked state */ res = ERR_NCX_NO_ACCESS_STATE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* cfg_ok_to_unlock */ /******************************************************************** * FUNCTION cfg_ok_to_read * * Check if the specified config can be read right now * * INPUTS: * cfg = Config template to check * RETURNS: * status *********************************************************************/ status_t cfg_ok_to_read (const cfg_template_t *cfg) { status_t res; #ifdef DEBUG if (!cfg) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif switch (cfg->cfg_state) { case CFG_ST_PLOCK: case CFG_ST_FLOCK: case CFG_ST_INIT: case CFG_ST_READY: res = NO_ERR; break; case CFG_ST_NONE: case CFG_ST_CLEANUP: /* config is not in a writable state */ res = ERR_NCX_NO_ACCESS_STATE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); break; } return res; } /* cfg_ok_to_read */ /******************************************************************** * FUNCTION cfg_ok_to_write * * Check if the specified config can be written right now * by the specified session ID * * This is not an access control check, * only locks and config state will be checked * * INPUTS: * cfg = Config template to check * sesid == session ID requesting to write to the config * RETURNS: * status *********************************************************************/ status_t cfg_ok_to_write (const cfg_template_t *cfg, ses_id_t sesid) { status_t res; #ifdef DEBUG if (!cfg) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; /* check if this is a writable target, * except during agent boot * except for the config */ if (cfg->cfg_state != CFG_ST_INIT) { switch (cfg->cfg_id) { case NCX_CFGID_RUNNING: case NCX_CFGID_CANDIDATE: case NCX_CFGID_STARTUP: break; default: if (!(cfg->flags & CFG_FL_TARGET)) { res = ERR_NCX_NOT_WRITABLE; } } } if (res != NO_ERR) { return res; } /* check the current config state */ switch (cfg->cfg_state) { case CFG_ST_PLOCK: /* partial lock is always OK for root node access */ res = NO_ERR; break; case CFG_ST_FLOCK: if (cfg->locked_by == sesid) { res = NO_ERR; } else { res = ERR_NCX_NO_ACCESS_LOCK; } break; case CFG_ST_INIT: case CFG_ST_READY: res = NO_ERR; break; case CFG_ST_NONE: case CFG_ST_CLEANUP: /* config is not in a writable state */ res = ERR_NCX_NO_ACCESS_STATE; break; default: SET_ERROR(ERR_INTERNAL_VAL); res = ERR_NCX_OPERATION_FAILED; break; } return res; } /* cfg_ok_to_write */ /******************************************************************** * FUNCTION cfg_is_global_locked * * Check if the specified config has an active global lock * * INPUTS: * cfg = Config template to check * * RETURNS: * TRUE if global lock active, FALSE if not *********************************************************************/ boolean cfg_is_global_locked (const cfg_template_t *cfg) { #ifdef DEBUG if (!cfg) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return (cfg->cfg_state == CFG_ST_FLOCK) ? TRUE : FALSE; } /* cfg_is_global_locked */ /******************************************************************** * FUNCTION cfg_is_partial_locked * * Check if the specified config has any active partial locks * * INPUTS: * cfg = Config template to check * * RETURNS: * TRUE if partial lock active, FALSE if not *********************************************************************/ boolean cfg_is_partial_locked (const cfg_template_t *cfg) { #ifdef DEBUG if (!cfg) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return (cfg->cfg_state == CFG_ST_PLOCK) ? TRUE : FALSE; } /* cfg_is_partial_locked */ /******************************************************************** * FUNCTION cfg_get_global_lock_info * * Get the current global lock info * * INPUTS: * cfg = Config template to check * sid == address of return session ID * locktime == address of return locktime pointer * * OUTPUTS: * *sid == session ID of lock holder * *locktime == pointer to lock time string * * RETURNS: * status, NCX_ERR_SKIPPED if not locked *********************************************************************/ status_t cfg_get_global_lock_info (const cfg_template_t *cfg, ses_id_t *sid, const xmlChar **locktime) { #ifdef DEBUG if (!cfg || !sid || !locktime) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif *sid = 0; *locktime = NULL; if (cfg->cfg_state == CFG_ST_FLOCK) { *sid = cfg->locked_by; *locktime = cfg->lock_time; return NO_ERR; } else { return ERR_NCX_SKIPPED; } /*NOTREACHED*/ } /* cfg_get_global_lock_info */ /******************************************************************** * FUNCTION cfg_lock * * Lock the specified config. * This will not really have an effect unless the * CFG_FL_TARGET flag in the specified config is also set * For global lock only * * INPUTS: * cfg = Config template to lock * locked_by == session ID of the lock owner * lock_src == enum classifying the lock source * RETURNS: * status *********************************************************************/ status_t cfg_lock (cfg_template_t *cfg, ses_id_t locked_by, cfg_source_t lock_src) { status_t res; #ifdef DEBUG if (!cfg) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = cfg_ok_to_lock(cfg); if (res == NO_ERR) { cfg->cfg_state = CFG_ST_FLOCK; cfg->locked_by = locked_by; cfg->lock_src = lock_src; tstamp_datetime(cfg->lock_time); } return res; } /* cfg_lock */ /******************************************************************** * FUNCTION cfg_unlock * * Unlock the specified config. * * INPUTS: * cfg = Config template to unlock * locked_by == session ID of the lock owner * RETURNS: * status *********************************************************************/ status_t cfg_unlock (cfg_template_t *cfg, ses_id_t locked_by) { status_t res; #ifdef DEBUG if (!cfg) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = cfg_ok_to_unlock(cfg, locked_by); if (res == NO_ERR) { cfg->cfg_state = CFG_ST_READY; cfg->locked_by = 0; cfg->lock_src = CFG_SRC_NONE; /* sec 8.3.5.2 requires a discard-changes * when a lock is released on the candidate */ if (cfg->cfg_id == NCX_CFGID_CANDIDATE) { res = cfg_fill_candidate_from_running(); } } return res; } /* cfg_unlock */ /******************************************************************** * FUNCTION cfg_release_locks * * Release any configuration locks held by the specified session * * INPUTS: * sesid == session ID to check for * *********************************************************************/ void cfg_release_locks (ses_id_t sesid) { uint32 i; cfg_template_t *cfg; status_t res; if (!cfg_init_done) { return; } if (sesid == 0) { return; } /* check for partial locks */ cfg_release_partial_locks(sesid); /* check for global locks */ for (i=0; ilocked_by == sesid) { cfg->cfg_state = CFG_ST_READY; cfg->locked_by = 0; cfg->lock_src = CFG_SRC_NONE; log_info("\ncfg forced unlock on %s config, " "held by session %d", cfg->name, sesid); /* sec 8.3.5.2 requires a discard-changes * when a lock is released on the candidate */ if (cfg->cfg_id == NCX_CFGID_CANDIDATE) { res = cfg_fill_candidate_from_running(); if (res != NO_ERR) { log_error("\nError: discard-changes failed (%s)", get_error_string(res)); } } } } } /* cfg_release_locks */ /******************************************************************** * FUNCTION cfg_release_partial_locks * * Release any configuration locks held by the specified session * * INPUTS: * sesid == session ID to check for * *********************************************************************/ void cfg_release_partial_locks (ses_id_t sesid) { cfg_template_t *cfg; plock_cb_t *plcb, *nextplcb; ses_id_t plock_sid; if (!cfg_init_done) { return; } cfg = cfg_arr[NCX_CFGID_RUNNING]; if (cfg == NULL) { return; } for (plcb = (plock_cb_t *)dlq_firstEntry(&cfg->plockQ); plcb != NULL; plcb = nextplcb) { nextplcb = (plock_cb_t *)dlq_nextEntry(plcb); plock_sid = plock_get_sid(plcb); if (plock_sid == sesid) { log_info("\ncfg forced partial unlock (id:%u) " "on running config, " "held by session %d", plock_get_id(plcb), plock_sid); dlq_remove(plcb); if (cfg->root != NULL) { val_clear_partial_lock(cfg->root, plcb); } plock_cb_free(plcb); } } } /* cfg_release_partial_locks */ /******************************************************************** * FUNCTION cfg_get_lock_list * * Get a list of all the locks held by a session * * INPUTS: * sesid == session ID to check for any locks * retval == pointer to malloced and initialized NCX_BT_SLIST * * OUTPUTS: * *retval is filled in with any lock entryies or left empty * if none found * *********************************************************************/ void cfg_get_lock_list (ses_id_t sesid, val_value_t *retval) { ncx_lmem_t *lmem; uint32 i; #ifdef DEBUG if (!retval) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* dummy session ID does not bother with locks */ if (!sesid) { return; } for (i=0; ilocked_by == sesid) { lmem = ncx_new_lmem(); if (lmem) { lmem->val.str = xml_strdup(cfg_arr[i]->name); if (lmem->val.str) { ncx_insert_lmem(&retval->v.list, lmem, NCX_MERGE_LAST); } else { ncx_free_lmem(lmem, NCX_BT_STRING); } } } } } /* cfg_get_lock_list */ /******************************************************************** * FUNCTION cfg_apply_load_root * * Apply the AGT_CB_APPLY function for the OP_EDITOP_LOAD operation * * INPUTS: * cfg == config target * newroot == new config tree * *********************************************************************/ void cfg_apply_load_root (cfg_template_t *cfg, val_value_t *newroot) { assert ( cfg && "cfg is NULL!" ); if (cfg->root && val_child_cnt(cfg->root)) { log_warn("\nWarning: config root already has child nodes"); } cfg_update_last_ch_time(cfg); if (cfg->root) { val_free_value(cfg->root); } cfg->root = newroot; } /* cfg_apply_load_root */ /******************************************************************** * FUNCTION cfg_update_last_ch_time * * Update the last-modified timestamp * * INPUTS: * cfg == config target *********************************************************************/ void cfg_update_last_ch_time (cfg_template_t *cfg) { tstamp_datetime(cfg->last_ch_time); } /* cfg_update_last_ch_time */ /******************************************************************** * FUNCTION cfg_update_last_txid * * Update the last good transaction ID * * INPUTS: * cfg == config target * txid == trnasaction ID to use *********************************************************************/ void cfg_update_last_txid (cfg_template_t *cfg, cfg_transaction_id_t txid) { cfg->last_txid = txid; } /* cfg_update_last_txid */ /******************************************************************** * FUNCTION cfg_add_partial_lock * * Add a partial lock the specified config. * This will not really have an effect unless the * CFG_FL_TARGET flag in the specified config is also set * For global lock only * * INPUTS: * cfg = Config template to lock * plcb == partial lock control block, already filled * out, to add to this configuration * * RETURNS: * status; if NO_ERR then the memory in plcb is handed off * and will be released when cfg_remove_partial_lock * is called for 'plcb' *********************************************************************/ status_t cfg_add_partial_lock (cfg_template_t *cfg, plock_cb_t *plcb) { status_t res; #ifdef DEBUG if (cfg == NULL || plcb == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = cfg_ok_to_partial_lock(cfg); if (res == NO_ERR) { cfg->cfg_state = CFG_ST_PLOCK; dlq_enque(plcb, &cfg->plockQ); } return res; } /* cfg_add_partial_lock */ /******************************************************************** * FUNCTION cfg_find_partial_lock * * Find a partial lock in the specified config. * * INPUTS: * cfg = Config template to use * lockid == lock-id for the plcb to find * * RETURNS: * pointer to the partial lock control block found * NULL if not found *********************************************************************/ plock_cb_t * cfg_find_partial_lock (cfg_template_t *cfg, plock_id_t lockid) { plock_cb_t *plock; #ifdef DEBUG if (cfg == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (plock = (plock_cb_t *)dlq_firstEntry(&cfg->plockQ); plock != NULL; plock = (plock_cb_t *)dlq_nextEntry(plock)) { if (plock_get_id(plock) == lockid) { return plock; } } return NULL; } /* cfg_find_partial_lock */ /******************************************************************** * FUNCTION cfg_first_partial_lock * * Get the first partial lock in the specified config. * * INPUTS: * cfg = Config template to use * * RETURNS: * pointer to the first partial lock control block * NULL if none exist at this time *********************************************************************/ plock_cb_t * cfg_first_partial_lock (cfg_template_t *cfg) { #ifdef DEBUG if (cfg == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (plock_cb_t *)dlq_firstEntry(&cfg->plockQ); } /* cfg_first_partial_lock */ /******************************************************************** * FUNCTION cfg_next_partial_lock * * Get the next partial lock in the specified config. * * INPUTS: * curplockcb == current lock control block; get next CB * * RETURNS: * pointer to the next partial lock control block * NULL if none exist at this time *********************************************************************/ plock_cb_t * cfg_next_partial_lock (plock_cb_t *curplockcb) { #ifdef DEBUG if (curplockcb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (plock_cb_t *)dlq_nextEntry(curplockcb); } /* cfg_next_partial_lock */ /******************************************************************** * FUNCTION cfg_delete_partial_lock * * Remove a partial lock from the specified config. * * INPUTS: * cfg = Config template to use * lockid == lock-id for the plcb to remove * *********************************************************************/ void cfg_delete_partial_lock (cfg_template_t *cfg, plock_id_t lockid) { plock_cb_t *plock, *nextplock; #ifdef DEBUG if (cfg == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (cfg->cfg_state != CFG_ST_PLOCK) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif for (plock = (plock_cb_t *)dlq_firstEntry(&cfg->plockQ); plock != NULL; plock = nextplock) { nextplock = (plock_cb_t *)dlq_nextEntry(plock); if (plock_get_id(plock) == lockid) { dlq_remove(plock); if (cfg->root != NULL) { val_clear_partial_lock(cfg->root, plock); } plock_cb_free(plock); if (dlq_empty(&cfg->plockQ)) { cfg->cfg_state = CFG_ST_READY; } else { cfg->cfg_state = CFG_ST_PLOCK; } return; } } } /* cfg_delete_partial_lock */ /******************************************************************** * FUNCTION cfg_ok_to_partial_lock * * Check if the specified config can be locked right now * for partial lock only * * INPUTS: * cfg = Config template to check * * RETURNS: * status *********************************************************************/ status_t cfg_ok_to_partial_lock (const cfg_template_t *cfg) { status_t res; #ifdef DEBUG if (!cfg) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (cfg->cfg_id != NCX_CFGID_RUNNING) { return ERR_NCX_LOCK_DENIED; } switch (cfg->cfg_state) { case CFG_ST_READY: case CFG_ST_PLOCK: res = NO_ERR; break; case CFG_ST_FLOCK: case CFG_ST_NONE: case CFG_ST_INIT: case CFG_ST_CLEANUP: /* config is in a state where locks cannot be granted */ res = ERR_NCX_NO_ACCESS_STATE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* cfg_ok_to_partial_lock */ /******************************************************************** * FUNCTION cfg_get_root * * Get the config root for the specified config * * INPUTS: * cfgid == config ID to get root from * * RETURNS: * config root or NULL if none or error *********************************************************************/ val_value_t * cfg_get_root (ncx_cfg_t cfgid) { cfg_template_t *cfg = NULL; if (cfgid <= MAX_CFGID) { cfg = cfg_arr[cfgid]; } if (cfg != NULL) { return cfg->root; } return NULL; } /* cfg_get_root */ /* END file cfg.c */ yuma123_2.14/netconf/src/ncx/b64.c0000664000175000017500000002102014770023131016670 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2013 - 2016, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "b64.h" #include #include #include #include #include #include "log.h" /** translation Table used for encoding characters */ static const char encode_character_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; /** translation Table used for decoding characters */ static const char decode_character_table[256] = { -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, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -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, -1, -1, -1 }; /** * Determine if a character is a valid base64 value. * * \param c the character to test. * \return true if c is a base64 value. */ static bool is_base64(unsigned char c) { return (isalnum(c) || (c == '+') || (c == '/')); } /** * Count the number of valid base64 characters in the supplied input * buffer. * @desc this function counts the number of valid base64 characters, * skipping CR and LF characters. Invalid characters are treated as * end of buffer markers. * * \param inbuff the buffer to check. * \param inputlen the length of the input buffer. * \return the number of valid base64 characters. */ static uint32_t get_num_valid_characters( const uint8_t* inbuff, size_t inputlen ) { uint32_t valid_char_count=0; uint32_t idx=0; while ( idx < inputlen ) { if ( is_base64( inbuff[idx] ) ) { ++valid_char_count; ++idx; } else if ( inbuff[idx] == '\r' || inbuff[idx] == '\n' ) { ++idx; } else { break; } } return valid_char_count; } /** * Decode the valid_bytes_count+1 byte array pf base64 values into valid_bytes_count bytes. * * \param inbuff the buffer to decode * \param outbuff the output buffer for decoded bytes. * \param valid_data_bytes_count number of expected result bytes to convert */ static void decode_bytes( const uint8_t* b64, uint8_t* data, unsigned int valid_data_bytes_count) { assert(valid_data_bytes_count>0 && valid_data_bytes_count<=3); data[0] = (decode_character_table[ b64[0] ] << 2) + ((decode_character_table[ b64[1] ] & 0x30) >> 4); if(valid_data_bytes_count==1) return; data[1] = ((decode_character_table[ b64[1] ] & 0xf) << 4) + ((decode_character_table[ b64[2] ] & 0x3c) >> 2); if(valid_data_bytes_count==2) return; data[2] = ((decode_character_table[ b64[2] ] & 0x3) << 6) + decode_character_table[ b64[3] ]; } static void encode_3bytes(const uint8_t data[3], uint8_t b64[4]) { b64[0] = encode_character_table[ ( data[0] & 0xfc) >> 2 ]; b64[1] = encode_character_table[ ( ( data[0] & 0x03 ) << 4 ) + ( ( data[1] & 0xf0 ) >> 4 ) ]; b64[2] = encode_character_table[ ( ( data[1] & 0x0f ) << 2 ) + ( ( data[2] & 0xc0 ) >> 6 ) ]; b64[3] = encode_character_table[ data[2] & 0x3f ]; } static void encode_last_2bytes(const uint8_t data[3], uint8_t b64[4]) { b64[0] = encode_character_table[ ( data[0] & 0xfc) >> 2 ]; b64[1] = encode_character_table[ ( ( data[0] & 0x03 ) << 4 ) + ( ( data[1] & 0xf0 ) >> 4 ) ]; b64[2] = encode_character_table[ ( ( data[1] & 0x0f ) << 2 )]; b64[3] = '='; } static void encode_last_1byte(const uint8_t data[3], uint8_t b64[4]) { b64[0] = encode_character_table[ ( data[0] & 0xfc) >> 2 ]; b64[1] = encode_character_table[ ( ( data[0] & 0x03 ) << 4 )]; b64[2] = '='; b64[3] = '='; } /*************** E X T E R N A L F U N C T I O N S *************/ uint32_t b64_get_encoded_str_len( uint32_t inbufflen, uint32_t linesize ) { uint32_t outbufflen = inbufflen%3 ? 4 * ( 1 + inbufflen/3 ) : 4 * ( inbufflen/3 ); if(linesize != 0) { outbufflen += 2 * ( inbufflen/linesize ); /* allow for line breaks*/ } outbufflen += 1; /* NULL termination */ return outbufflen; } uint32_t b64_get_decoded_str_len( const uint8_t* inbuff, size_t inputlen ) { uint32_t valid_char_count= get_num_valid_characters( inbuff, inputlen ); uint32_t required_buf_len = 3*(valid_char_count/4); uint32_t rem=valid_char_count%4; if ( rem ) { required_buf_len += rem-1; } return required_buf_len; } status_t b64_encode ( const uint8_t* inbuff, uint32_t inbufflen, uint8_t* outbuff, uint32_t outbufflen, uint32_t linesize, uint32_t* retlen) { uint32_t i,j,wrapindex; uint8_t b64_4[4]; uint8_t* outptr; assert( inbuff && "b64_decode() inbuff is NULL!" ); assert( outbuff && "b64_decode() outbuff is NULL!" ); if ( b64_get_encoded_str_len( inbufflen, linesize ) > outbufflen ) { return ERR_BUFF_OVFL; } outptr=outbuff; wrapindex=0; for(i=0;i<((inbufflen+2)/3);i++) { if((inbufflen-i*3)==1) { encode_last_1byte(inbuff+i*3,b64_4); } else if((inbufflen-i*3)==2) { encode_last_2bytes(inbuff+i*3,b64_4); } else { encode_3bytes(inbuff+i*3,b64_4); } for(j=0;j<4;j++) { *outptr++=b64_4[j]; if(linesize && ++wrapindex==linesize) { *outptr++='\r'; *outptr++='\n'; wrapindex=0; } } } *retlen=outptr-outbuff; *outptr++='\0'; return NO_ERR; } status_t b64_decode( const uint8_t* inbuff, uint32_t inbufflen, uint8_t* outbuff, uint32_t outbufflen, uint32_t* retlen ) { int i; int b64_byte_index; uint8_t b64_4[4]; int padding; assert( inbuff && "b64_decode() inbuff is NULL!" ); assert( outbuff && "b64_decode() outbuff is NULL!" ); b64_byte_index=0; padding=0; *retlen=0; for(i=0;i=2) { if(padding==0) { padding=4-b64_byte_index; } b64_4[b64_byte_index++]=inbuff[i]; } else { /* encountered a dodgy character */ log_warn( "b64_decode() encountered invalid character(%c), " "output string truncated!", inbuff[i]); return ERR_NCX_WRONG_TKVAL; } if(b64_byte_index==4) { if((*retlen+3-padding)>outbufflen) { return ERR_BUFF_OVFL; } b64_byte_index=0; decode_bytes(b64_4, outbuff+*retlen, 3-padding); *retlen+=3-padding; } } if(b64_byte_index!=0) { /* encountered a dodgy character */ log_warn( "b64_decode() encountered trailing %d bytes data not aligned to 4 bytes!", b64_byte_index); return ERR_NCX_WRONG_TKVAL; } return NO_ERR; } /* END b64.c */ yuma123_2.14/netconf/src/ncx/send_buff.h0000664000175000017500000000420014770023131020236 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_send_buff #define _H_send_buff /* FILE: send_buff.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Send buffer utility ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 20-jan-07 abb begun */ #include #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION send_buff * * Send the buffer to the ncxserver * * This function is used by applications which do not * select for write_fds, and may not block (if fnctl used) * * INPUTS: * fd == the socket to write to * buffer == the buffer to write * cnt == the number of bytes to write * * RETURNS: * status *********************************************************************/ extern status_t send_buff (int fd, const char *buffer, size_t cnt); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_send_buff */ yuma123_2.14/netconf/src/ncx/yangconst.h0000664000175000017500000002165614770023131020326 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yangconst #define _H_yangconst /* FILE: yangconst.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Contains YANG constants separated to prevent H file include loops ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 27-oct-07 abb Begun; started from ncxconst.h */ #include #include #include "xmlns.h" #include "status.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define YANG_URN (const xmlChar *)"urn:ietf:params:xml:ns:yang:1" #define YANG_PREFIX (const xmlChar *)"y" #define YANG_MODULE (const xmlChar *)"yang" /*** no mod ! ***/ #define YANG_SUFFIX (const xmlChar *)"yang" #define YIN_URN (const xmlChar *)"urn:ietf:params:xml:ns:yang:yin:1" #define YIN_PREFIX (const xmlChar *)"yin" #define YIN_MODULE (const xmlChar *)"yin" /*** no mod ! ***/ #define YIN_SUFFIX (const xmlChar *)"yin" #define YANG_VERSION10_STR (const xmlChar *)"1" #define YANG_VERSION11_STR (const xmlChar *)"1.1" #define YANG_FILE_SEPCHAR '@' #define YANG_SII_STRING \ (const xmlChar *)"schema-instance-identifier" /* YANG keyword/YIN element names */ #define YANG_K_ADD (const xmlChar *)"add" #define YANG_K_AFTER (const xmlChar *)"after" #define YANG_K_ARGUMENT (const xmlChar *)"argument" #define YANG_K_AUGMENT (const xmlChar *)"augment" #define YANG_K_ANYDATA (const xmlChar *)"anydata" #define YANG_K_ANYXML (const xmlChar *)"anyxml" #define YANG_K_BASE (const xmlChar *)"base" #define YANG_K_BEFORE (const xmlChar *)"before" #define YANG_K_BELONGS_TO (const xmlChar *)"belongs-to" #define YANG_K_BIT (const xmlChar *)"bit" #define YANG_K_CASE (const xmlChar *)"case" #define YANG_K_CHOICE (const xmlChar *)"choice" #define YANG_K_CONDITION (const xmlChar *)"condition" #define YANG_K_CONFIG (const xmlChar *)"config" #define YANG_K_CONTACT (const xmlChar *)"contact" #define YANG_K_CONTAINER (const xmlChar *)"container" #define YANG_K_DATE (const xmlChar *)"date" #define YANG_K_DEFAULT (const xmlChar *)"default" #define YANG_K_DELETE (const xmlChar *)"delete" #define YANG_K_DESCRIPTION (const xmlChar *)"description" #define YANG_K_DEVIATE (const xmlChar *)"deviate" #define YANG_K_DEVIATION (const xmlChar *)"deviation" #define YANG_K_DEVIATIONS (const xmlChar *)"deviations" #define YANG_K_ENUM (const xmlChar *)"enum" #define YANG_K_ERROR_APP_TAG (const xmlChar *)"error-app-tag" #define YANG_K_ERROR_MESSAGE (const xmlChar *)"error-message" #define YANG_K_EXTENSION (const xmlChar *)"extension" #define YANG_K_FEATURE (const xmlChar *)"feature" #define YANG_K_FEATURES (const xmlChar *)"features" #define YANG_K_FIRST (const xmlChar *)"first" #define YANG_K_FRACTION_DIGITS (const xmlChar *)"fraction-digits" #define YANG_K_GROUPING (const xmlChar *)"grouping" #define YANG_K_IDENTITY (const xmlChar *)"identity" #define YANG_K_IDENTITYREF (const xmlChar *)"identityref" #define YANG_K_IF_FEATURE (const xmlChar *)"if-feature" #define YANG_K_IMPORT (const xmlChar *)"import" #define YANG_K_INCLUDE (const xmlChar *)"include" #define YANG_K_INFO (const xmlChar *)"info" #define YANG_K_INPUT (const xmlChar *)"input" #define YANG_K_INSERT (const xmlChar *)"insert" #define YANG_K_KEY (const xmlChar *)"key" #define YANG_K_LAST (const xmlChar *)"last" #define YANG_K_LEAF (const xmlChar *)"leaf" #define YANG_K_LEAF_LIST (const xmlChar *)"leaf-list" #define YANG_K_LENGTH (const xmlChar *)"length" #define YANG_K_LIST (const xmlChar *)"list" #define YANG_K_MANDATORY (const xmlChar *)"mandatory" #define YANG_K_MAX (const xmlChar *)"max" #define YANG_K_MAX_ELEMENTS (const xmlChar *)"max-elements" #define YANG_K_MIN (const xmlChar *)"min" #define YANG_K_MIN_ELEMENTS (const xmlChar *)"min-elements" #define YANG_K_MODULE (const xmlChar *)"module" #define YANG_K_MUST (const xmlChar *)"must" #define YANG_K_NAME (const xmlChar *)"name" #define YANG_K_NAMESPACE (const xmlChar *)"namespace" #define YANG_K_NAN (const xmlChar *)"NaN" #define YANG_K_NEGINF (const xmlChar *)"-INF" #define YANG_K_NOTIFICATION (const xmlChar *)"notification" #define YANG_K_NOT_SUPPORTED (const xmlChar *)"not-supported" #define YANG_K_ORDERED_BY (const xmlChar *)"ordered-by" #define YANG_K_ORGANIZATION (const xmlChar *)"organization" #define YANG_K_OUTPUT (const xmlChar *)"output" #define YANG_K_PATH (const xmlChar *)"path" #define YANG_K_PATTERN (const xmlChar *)"pattern" #define YANG_K_POSITION (const xmlChar *)"position" #define YANG_K_POSINF (const xmlChar *)"INF" #define YANG_K_PREFIX (const xmlChar *)"prefix" #define YANG_K_PRESENCE (const xmlChar *)"presence" #define YANG_K_RANGE (const xmlChar *)"range" #define YANG_K_REFERENCE (const xmlChar *)"reference" #define YANG_K_REFINE (const xmlChar *)"refine" #define YANG_K_REPLACE (const xmlChar *)"replace" #define YANG_K_REQUIRE_INSTANCE (const xmlChar *)"require-instance" #define YANG_K_REVISION (const xmlChar *)"revision" #define YANG_K_REVISION_DATE (const xmlChar *)"revision-date" #define YANG_K_RPC (const xmlChar *)"rpc" #define YANG_K_STATUS (const xmlChar *)"status" #define YANG_K_SUBMODULE (const xmlChar *)"submodule" #define YANG_K_SYSTEM (const xmlChar *)"system" #define YANG_K_TAG (const xmlChar *)"tag" #define YANG_K_TEXT (const xmlChar *)"text" #define YANG_K_TARGET_NODE (const xmlChar *)"target-node" #define YANG_K_TYPE (const xmlChar *)"type" #define YANG_K_TYPEDEF (const xmlChar *)"typedef" #define YANG_K_UNBOUNDED (const xmlChar *)"unbounded" #define YANG_K_UNION (const xmlChar *)"union" #define YANG_K_UNIQUE (const xmlChar *)"unique" #define YANG_K_UNITS (const xmlChar *)"units" #define YANG_K_URI (const xmlChar *)"uri" #define YANG_K_USER (const xmlChar *)"user" #define YANG_K_USES (const xmlChar *)"uses" #define YANG_K_VALUE (const xmlChar *)"value" #define YANG_K_WHEN (const xmlChar *)"when" #define YANG_K_YANG_VERSION (const xmlChar *)"yang-version" #define YANG_K_YIN_ELEMENT (const xmlChar *)"yin-element" /*YANG 1.1*/ #define YANG_K_ACTION (const xmlChar *)"action" /** * Check if parsing should terminate. * * \param res the current status * \return true if parsing should terminate. */ static inline boolean terminate_parse( status_t res ) { boolean terminate; terminate = ( res != NO_ERR && ( res < ERR_LAST_SYS_ERR || res==ERR_NCX_EOF )); #if 0 if(res!=NO_ERR) { fprintf(stderr, "%s ? res=%d (%s) - %s\n",__FUNCTION__, res, get_error_string(res), terminate?"terminate":"continue"); } #endif } /* used in parser routines to decide if processing can continue * will exit the function if critical error or continue if not */ #define CHK_EXIT(res, retres) \ if (res != NO_ERR) { \ if ( terminate_parse( res ) ) { \ return res; \ } else { \ retres = res; \ } \ } /* used in parser routines to decide if processing can continue * does not return, just evaluates to TRUE if there is * a critical error and false if processing can continue */ #define NEED_EXIT(res) \ ((res == NO_ERR) ? FALSE : \ (res < ERR_LAST_SYS_ERR || res==ERR_NCX_EOF) ? \ TRUE : FALSE) #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yangconst */ yuma123_2.14/netconf/src/ncx/op.h0000664000175000017500000002440214770023131016727 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_op #define _H_op /* FILE: op.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF protocol operations ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 06-apr-05 abb Begun. */ #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* NETCONF protocol operation enumeration is actually * an RPC method in the NECONF namespace */ typedef enum op_method_t_ { OP_NO_METHOD, OP_GET_CONFIG, /* base protocol operations */ OP_EDIT_CONFIG, OP_COPY_CONFIG, OP_DELETE_CONFIG, OP_LOCK, OP_UNLOCK, OP_GET, OP_CLOSE_SESSION, OP_KILL_SESSION, OP_COMMIT, /* #candidate capability */ OP_DISCARD_CHANGES, /* #candidate capability */ OP_VALIDATE, /* #validate capability */ OP_CANCEL_COMMIT /* base:1.1 + conf-commit */ } op_method_t; /* NETCONF protocol operation PDU source types */ typedef enum op_srctyp_t_ { OP_SOURCE_NONE, OP_SOURCE_CONFIG, OP_SOURCE_INLINE, OP_SOURCE_URL } op_srctyp_t; /* NETCONF protocol operation PDU target types */ typedef enum op_targtyp_t_ { OP_TARGET_NONE, OP_TARGET_CONFIG, OP_TARGET_URL } op_targtyp_t; /* NETCONF protocol default edit-config operation types */ typedef enum op_defop_t_ { OP_DEFOP_NOT_SET, OP_DEFOP_NONE, OP_DEFOP_MERGE, OP_DEFOP_REPLACE, OP_DEFOP_NOT_USED } op_defop_t; /* NETCONF protocol operation PDU filter types */ typedef enum op_filtertyp_t_ { OP_FILTER_NONE, OP_FILTER_SUBTREE, OP_FILTER_XPATH } op_filtertyp_t; /* NETCONF edit-config operation types */ typedef enum op_editop_t_ { OP_EDITOP_NONE, OP_EDITOP_MERGE, OP_EDITOP_REPLACE, OP_EDITOP_CREATE, OP_EDITOP_DELETE, OP_EDITOP_LOAD, /* internal enumeration */ OP_EDITOP_COMMIT, OP_EDITOP_REMOVE /* base:1.1 only */ } op_editop_t; /* YANG insert operation types */ typedef enum op_insertop_t_ { OP_INSOP_NONE, OP_INSOP_FIRST, OP_INSOP_LAST, OP_INSOP_BEFORE, OP_INSOP_AFTER } op_insertop_t; /* NETCONF full operation list for access control */ typedef enum op_t_ { OP_NONE, OP_MERGE, OP_REPLACE, OP_CREATE, OP_DELETE, OP_LOAD, OP_NOTIFY, OP_READ } op_t; /* NETCONF edit-config test-option types */ typedef enum op_testop_t_ { OP_TESTOP_NONE, OP_TESTOP_TESTTHENSET, OP_TESTOP_SET, OP_TESTOP_TESTONLY } op_testop_t; /* NETCONF edit-config error-option types */ typedef enum op_errop_t_ { OP_ERROP_NONE, OP_ERROP_STOP, OP_ERROP_CONTINUE, OP_ERROP_ROLLBACK } op_errop_t; /* NETCONF protocol operation filter spec */ typedef struct op_filter_t_ { op_filtertyp_t op_filtyp; struct val_value_t_ *op_filter; /* back-ptr to the filter value */ } op_filter_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION op_method_name * * Get the keyword for the specified STD RPC method * * INPUTS: * op_id == proto op ID * RETURNS: * string for the operation, or "none" or "illegal" *********************************************************************/ extern const xmlChar * op_method_name (op_method_t op_id); /******************************************************************** * FUNCTION op_editop_name * * Get the keyword for the specified op_editop_t enumeration * * INPUTS: * ed_id == edit operation ID * RETURNS: * string for the edit operation type, or "none" or "illegal" *********************************************************************/ extern const xmlChar * op_editop_name (op_editop_t ed_id); /******************************************************************** * FUNCTION op_editop_id * * Get the ID for the editop from its keyword * * INPUTS: * opstr == string for the edit operation type * RETURNS: * the op_editop_t enumeration value for the string *********************************************************************/ extern op_editop_t op_editop_id (const xmlChar *opstr); /******************************************************************** * FUNCTION op_insertop_name * * Get the keyword for the specified op_insertop_t enumeration * * INPUTS: * ed_id == insert operation ID * * RETURNS: * string for the insert operation type, or "none" or "illegal" *********************************************************************/ extern const xmlChar * op_insertop_name (op_insertop_t ins_id); /******************************************************************** * FUNCTION op_insertop_id * * Get the ID for the insert operation from its keyword * * INPUTS: * opstr == string for the insert operation type * RETURNS: * the op_insertop_t enumeration value for the string *********************************************************************/ extern op_insertop_t op_insertop_id (const xmlChar *opstr); /******************************************************************** * FUNCTION op_filtertyp_id * * Get the ID for the filter type from its keyword * * INPUTS: * filstr == string for the filter type * RETURNS: * the op_filtertyp_t enumeration value for the string *********************************************************************/ extern op_filtertyp_t op_filtertyp_id (const xmlChar *filstr); /******************************************************************** * FUNCTION op_defop_name * * Get the keyword for the specified op_defop_t enumeration * * INPUTS: * def_id == default operation ID * RETURNS: * string for the default operation type, or "none" or "illegal" *********************************************************************/ extern const xmlChar * op_defop_name (op_defop_t def_id); /******************************************************************** * FUNCTION op_defop_id * * Get the ID for the default-operation from its keyword * * INPUTS: * defstr == string for the default operation * RETURNS: * the op_editop_t enumeration value for the string *********************************************************************/ extern op_editop_t op_defop_id (const xmlChar *defstr); /******************************************************************** * FUNCTION op_testop_name * * Get the keyword for the specified op_testop_t enumeration * * INPUTS: * test_id == test operation ID * RETURNS: * string for the test operation type, or "none" or "illegal" *********************************************************************/ extern const xmlChar * op_testop_name (op_testop_t test_id); /******************************************************************** * FUNCTION op_testop_enum * * Get the enum for the specified op_testop_t string * * INPUTS: * teststr == string for the test operation type * RETURNS: * test_id == test operation ID * *********************************************************************/ extern op_testop_t op_testop_enum (const xmlChar *teststr); /******************************************************************** * FUNCTION op_errop_name * * Get the keyword for the specified op_errop_t enumeration * * INPUTS: * err_id == error operation ID * RETURNS: * string for the error operation type, or "none" or "illegal" *********************************************************************/ extern const xmlChar * op_errop_name (op_errop_t err_id); /******************************************************************** * FUNCTION op_errop_id * * Get the ID for the error-option from its keyword * * INPUTS: * errstr == string for the error option * RETURNS: * the op_errop_t enumeration value for the string *********************************************************************/ extern op_errop_t op_errop_id (const xmlChar *errstr); /******************************************************************** * FUNCTION op_defop_id2 * * Get the ID for the default-operation from its keyword * Return the op_defop_t, not the op_editop_t conversion * * INPUTS: * defstr == string for the default operation * RETURNS: * the op_defop_t enumeration value for the string *********************************************************************/ extern op_defop_t op_defop_id2 (const xmlChar *defstr); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_op */ yuma123_2.14/netconf/src/ncx/yang.h0000664000175000017500000012461314770023131017254 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yang #define _H_yang /* FILE: yang.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* YANG Module parser utilities ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 15-nov-07 abb Begun; start from yang_parse.c */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* YANG parser mode entry types */ typedef enum yang_parsetype_t_ { YANG_PT_NONE, YANG_PT_TOP, /* called from top level [sub]module */ YANG_PT_INCLUDE, /* called from module include-stmt */ YANG_PT_IMPORT /* called from module import-stmt */ } yang_parsetype_t; /* YANG statement types for yang_stmt_t order struct */ typedef enum yang_stmttype_t_ { YANG_ST_NONE, YANG_ST_TYPEDEF, /* node is typ_template_t */ YANG_ST_GROUPING, /* node is grp_template_t */ YANG_ST_EXTENSION, /* node is ext_template_t */ YANG_ST_OBJECT, /* node is obj_template_t */ YANG_ST_IDENTITY, /* node is ncx_identity_t */ YANG_ST_FEATURE, /* node is ncx_feature_t */ YANG_ST_DEVIATION /* node is ncx_deviation_t */ } yang_stmttype_t; /* YANG statement node to track top-level statement order for doc output */ typedef struct yang_stmt_t_ { dlq_hdr_t qhdr; yang_stmttype_t stmttype; /* these node pointers are back-pointers and not malloced here */ union s_ { typ_template_t *typ; grp_template_t *grp; ext_template_t *ext; obj_template_t *obj; ncx_identity_t *identity; ncx_feature_t *feature; obj_deviation_t *deviation; } s; } yang_stmt_t; /* YANG node entry to track if a module has been used already */ typedef struct yang_node_t_ { dlq_hdr_t qhdr; const xmlChar *name; /* module name in imp/inc/failed */ const xmlChar *revision; /* revision date in imp/inc/failed */ ncx_module_t *mod; /* back-ptr to module w/ imp/inc */ ncx_module_t *submod; /* submod for allincQ */ xmlChar *failed; /* saved name for failed entries */ xmlChar *failedrev; /* saved revision for failed entries */ status_t res; /* saved result for 'failed' */ ncx_error_t tkerr; } yang_node_t; /* YANG import pointer node to track all imports used */ typedef struct yang_import_ptr_t_ { dlq_hdr_t qhdr; xmlChar *modname; xmlChar *modprefix; xmlChar *revision; } yang_import_ptr_t; /* YANG parser control block * * top level parse can be for a module or a submodule * * The allimpQ is a cache of pointers to all the imports that * have been processed. This is used by yangdump for * document processing and error detection. * * The impchainQ is really a stack of imports that * are being processed, used for import loop detection * * The allincQ is a cache of all the includes that have been * processed. Includes can be nested, and are only parsed once, * * The incchainQ is really a stack of includes that * are being processed, used for include loop detection * * The failedQ is a list of all the modules that had fatal * errors, and have already been processed. This is used * to prevent error messages from imports to be printed more * than once, and speeds up validation processing */ typedef struct yang_pcb_t_ { struct ncx_module_t_ *top; /* top-level file */ struct ncx_module_t_ *retmod; /* nested [sub]mod being returned */ struct ncx_module_t_ *parentparm; /* parent passed for submodule */ const xmlChar *revision; /* back-ptr to rev to match */ boolean with_submods; boolean stmtmode; /* save top-level stmt order */ boolean diffmode; /* TRUE = yangdiff old ver */ boolean deviationmode; /* TRUE if keeping deviations only */ boolean searchmode; /* TRUE if just getting ns & version */ boolean parsemode; /* TRUE if full parse but no reg-load */ boolean keepmode; /* TRUE to keep new mod even if loaded */ boolean topfound; /* TRUE if top found, not added */ boolean topadded; /* TRUE if top in registry; F: need free */ boolean savetkc; /* TRUE if tkc should be kept in tkc */ boolean docmode; /* TRUE if saving strings in origtkQ */ dlq_hdr_t allimpQ; /* Q of yang_import_ptr_t */ dlq_hdr_t *savedevQ; /* ptr to Q of ncx_save_deviations_t */ tk_chain_t *tkc; /* live or NULL parse chain */ /* 2 Qs of yang_node_t */ dlq_hdr_t impchainQ; /* cur chain of import used */ dlq_hdr_t failedQ; /* load mod or submod failed */ } yang_pcb_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION yang_consume_semiapp * * consume a stmtsep clause * Consume a semi-colon to end a simple clause, or * consume a vendor extension * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * appinfoQ == queue to receive any vendor extension found * * OUTPUTS: * *appinfoQ has the extesnion added if any * current token is advanced * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_semiapp (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_consume_string * * consume 1 string token * Consume a YANG string token in any of the 3 forms * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * field == address of field to get the string (may be NULL) * * OUTPUTS: * if field not NULL, * *field is set with a malloced string in NO_ERR * current token is advanced * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_string (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **field); /******************************************************************** * FUNCTION yang_consume_keyword * * consume 1 YANG keyword or vendor extension keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * prefix == address of field to get the prefix string (may be NULL) * field == address of field to get the name string (may be NULL) * * OUTPUTS: * if prefix not NULL, * *prefix is set with a malloced string in NO_ERR * if field not NULL, * *field is set with a malloced string in NO_ERR * current token is advanced * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_keyword (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **prefix, xmlChar **field); /******************************************************************** * FUNCTION yang_consume_nowsp_string * * consume 1 string without any whitespace * Consume a YANG string token in any of the 3 forms * Check No whitespace allowed! * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * field == address of field to get the string (may be NULL) * * OUTPUTS: * if field not NULL, * *field is set with a malloced string in NO_ERR * current token is advanced * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_nowsp_string (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **field); /******************************************************************** * FUNCTION yang_consume_id_string * * consume an identifier-str token * Consume a YANG string token in any of the 3 forms * Check that it is a valid YANG identifier string * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * field == address of field to get the string (may be NULL) * * OUTPUTS: * if field not NULL: * *field is set with a malloced string in NO_ERR * current token is advanced * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_id_string (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **field); /******************************************************************** * FUNCTION yang_consume_pid_string * * consume an identifier-ref-str token * Consume a YANG string token in any of the 3 forms * Check that it is a valid identifier-ref-string * Get the prefix if any is set * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * prefix == address of prefix string if any is used (may be NULL) * field == address of field to get the string (may be NULL) * * OUTPUTS: * if field not NULL: * *field is set with a malloced string in NO_ERR * current token is advanced * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_pid_string (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **prefix, xmlChar **field); /******************************************************************** * FUNCTION yang_consume_error_stmts * * consume the range. length. pattern. must error info extensions * Parse the sub-section as a sub-section for error-app-tag * and error-message clauses * * Current token is the starting left brace for the sub-section * that is extending a range, length, pattern, or must statement * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * errinfo == pointer to valid ncx_errinfo_t struct * The struct will be filled in by this fn * appinfoQ == Q to hold any extensions found * * OUTPUTS: * *errinfo filled in with any clauses found * *appinfoQ filled in with any extensions found * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_error_stmts (tk_chain_t *tkc, ncx_module_t *mod, ncx_errinfo_t *errinfo, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_consume_descr * * consume one descriptive string clause * Parse the description or reference statement * * Current token is the starting keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * str == string to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found * * OUTPUTS: * *str set to the value if str not NULL * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_descr (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **str, boolean *dupflag, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_consume_pid * * consume one [prefix:]name clause * Parse the rest of the statement (2 forms): * * keyword [prefix:]name; * * keyword [prefix:]name { appinfo } * * Current token is the starting keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * prefixstr == prefix string to set (may be NULL) * str == string to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found * * OUTPUTS: * *str set to the value if str not NULL * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_pid (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **prefixstr, xmlChar **str, boolean *dupflag, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_consume_strclause * * consume one normative string clause * Parse the string-parameter-based statement * * Current token is the starting keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * str == string to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *str set to the value if str not NULL * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_strclause (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **str, boolean *dupflag, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_consume_status * * consume one status clause * Parse the status statement * * Current token is the 'status' keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * stat == status object to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *stat set to the value if stat not NULL * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_status (tk_chain_t *tkc, ncx_module_t *mod, ncx_status_t *status, boolean *dupflag, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_consume_ordered_by * * consume one ordered-by clause * Parse the ordered-by statement * * Current token is the 'ordered-by' keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * ordsys == ordered-by object to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *ordsys set to the value if not NULL * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_ordered_by (tk_chain_t *tkc, ncx_module_t *mod, boolean *ordsys, boolean *dupflag, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_consume_max_elements * * consume one max-elements clause * Parse the max-elements statement * * Current token is the 'max-elements' keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * maxelems == max-elements object to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *maxelems set to the value if not NULL * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_max_elements (tk_chain_t *tkc, ncx_module_t *mod, uint32 *maxelems, boolean *dupflag, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_consume_must * * consume one must-stmt into mustQ * Parse the must statement * * Current token is the 'must' keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * mustQ == address of Q of xpath_pcb_t structs to store * a new malloced xpath_pcb_t * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * must entry malloced and added to mustQ (if NO_ERR) * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_must (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *mustQ, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_consume_when * * consume one when-stmt into obj->when * Parse the when statement * * Current token is the 'when' keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * obj == obj_template_t of the parent object of this 'when' * whenflag == address of boolean set-once flag for the when-stmt * an error will be generated if the value passed * in is TRUE. Set the initial value to FALSE * before first call, and do not change it after that * OUTPUTS: * obj->when malloced and dilled in * *whenflag set if not set already * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_when (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, boolean *whenflag); /******************************************************************** * FUNCTION yang_consume_iffeature * * consume one if-feature-stmt into iffeatureQ * Parse the if-feature statement * * Current token is the 'if-feature' keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * iffeatureQ == Q of ncx_iffeature_t to hold the new if-feature * appinfoQ == queue to store appinfo (extension usage) * * OUTPUTS: * ncx_iffeature_t malloced and added to iffeatureQ * maybe ncx_appinfo_t structs added to appinfoQ * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_iffeature (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *iffeatureQ, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_consume_boolean * * consume one boolean clause * Parse the boolean parameter based statement * * Current token is the starting keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * boolval == boolean value to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *str set to the value if str not NULL * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_boolean (tk_chain_t *tkc, ncx_module_t *mod, boolean *boolval, boolean *dupflag, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_consume_int32 * * consume one int32 clause * Parse the int32 based parameter statement * * Current token is the starting keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * num == int32 value to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *num set to the value if num not NULL * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_int32 (tk_chain_t *tkc, ncx_module_t *mod, int32 *num, boolean *dupflag, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_consume_uint32 * * consume one uint32 clause * Parse the uint32 based parameter statement * * Current token is the starting keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * num == uint32 value to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *num set to the value if num not NULL * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_consume_uint32 (tk_chain_t *tkc, ncx_module_t *mod, uint32 *num, boolean *dupflag, dlq_hdr_t *appinfoQ); /******************************************************************** * FUNCTION yang_find_imp_typedef * * Find the specified imported typedef * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * prefix == prefix value to use * name == type name to use * tkerr == error record to use in error messages (may be NULL) * typ == address of return typ_template_t pointer * * OUTPUTS: * *typ set to the found typ_template_t, if NO_ERR * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_find_imp_typedef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *prefix, const xmlChar *name, ncx_error_t *tkerr, typ_template_t **typ); /******************************************************************** * FUNCTION yang_find_imp_grouping * * Find the specified imported grouping * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * prefix == prefix value to use * name == grouping name to use * tkerr == error record to use in error messages (may be NULL) * grp == address of return grp_template_t pointer * * OUTPUTS: * *grp set to the found grp_template_t, if NO_ERR * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_find_imp_grouping (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *prefix, const xmlChar *name, ncx_error_t *tkerr, grp_template_t **grp); /******************************************************************** * FUNCTION yang_find_imp_extension * * Find the specified imported extension * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * prefix == prefix value to use * name == extension name to use * tkerr == error struct to use in error messages (may be NULL) * ext == address of return ext_template_t pointer * * OUTPUTS: * *ext set to the found ext_template_t, if NO_ERR * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_find_imp_extension (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *prefix, const xmlChar *name, ncx_error_t *tkerr, ext_template_t **ext); /******************************************************************** * FUNCTION yang_find_imp_feature * * Find the specified imported feature * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * prefix == prefix value to use * name == feature name to use * tkerr == error struct to use in error messages (may be NULL) * feature == address of return ncx_feature_t pointer * * OUTPUTS: * *feature set to the found ncx_feature_t, if NO_ERR * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_find_imp_feature (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *prefix, const xmlChar *name, ncx_error_t *tkerr, ncx_feature_t **feature); /******************************************************************** * FUNCTION yang_find_imp_identity * * Find the specified imported identity * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * prefix == prefix value to use * name == feature name to use * tkerr == error struct to use in error messages (may be NULL) * identity == address of return ncx_identity_t pointer * * OUTPUTS: * *identity set to the found ncx_identity_t, if NO_ERR * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_find_imp_identity (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *prefix, const xmlChar *name, ncx_error_t *tkerr, ncx_identity_t **identity); /******************************************************************** * FUNCTION yang_check_obj_used * * generate warnings if local typedefs/groupings not used * Check if the local typedefs and groupings are actually used * Generate warnings if not used * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain in progress * mod == module in progress * typeQ == typedef Q to check * grpQ == pgrouping Q to check * *********************************************************************/ extern void yang_check_obj_used (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *typeQ, dlq_hdr_t *grpQ); /******************************************************************** * FUNCTION yang_check_imports_used * * generate warnings if imports not used * Check if the imports statements are actually used * Check if the import is newer than the importing module * Generate warnings if so * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain in progress * mod == module in progress * *********************************************************************/ extern void yang_check_imports_used (tk_chain_t *tkc, ncx_module_t *mod); /******************************************************************** * FUNCTION yang_new_node * * Create a new YANG parser node * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ extern yang_node_t * yang_new_node (void); /******************************************************************** * FUNCTION yang_free_node * * Delete a YANG parser node * * INPUTS: * node == parser node to delete * *********************************************************************/ extern void yang_free_node (yang_node_t *node); /******************************************************************** * FUNCTION yang_clean_nodeQ * * Delete all the node entries in a YANG parser node Q * * INPUTS: * que == Q of yang_node_t to clean * *********************************************************************/ extern void yang_clean_nodeQ (dlq_hdr_t *que); /******************************************************************** * FUNCTION yang_find_node * * Find a YANG parser node in the specified Q * * INPUTS: * que == Q of yang_node_t to check * name == module name to find * revision == module revision date (may be NULL) * * RETURNS: * pointer to found node, NULL if not found *********************************************************************/ extern yang_node_t * yang_find_node (const dlq_hdr_t *que, const xmlChar *name, const xmlChar *revision); /******************************************************************** * FUNCTION yang_dump_nodeQ * * log_debug output for contents of the specified nodeQ * * INPUTS: * que == Q of yang_node_t to check * name == Q name (may be NULL) * * RETURNS: * pointer to found node, NULL if not found *********************************************************************/ extern void yang_dump_nodeQ (dlq_hdr_t *que, const char *name); /******************************************************************** * FUNCTION yang_new_pcb * * Create a new YANG parser control block * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ extern yang_pcb_t * yang_new_pcb (void); /******************************************************************** * FUNCTION yang_free_pcb * * Delete a YANG parser control block * * INPUTS: * pcb == parser control block to delete * *********************************************************************/ extern void yang_free_pcb (yang_pcb_t *pcb); /******************************************************************** * FUNCTION yang_new_typ_stmt * * Create a new YANG stmt node for a typedef * * INPUTS: * typ == type template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ extern yang_stmt_t * yang_new_typ_stmt (typ_template_t *typ); /******************************************************************** * FUNCTION yang_new_grp_stmt * * Create a new YANG stmt node for a grouping * * INPUTS: * grp == grouping template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ extern yang_stmt_t * yang_new_grp_stmt (grp_template_t *grp); /******************************************************************** * FUNCTION yang_new_ext_stmt * * Create a new YANG stmt node for an extension * * INPUTS: * ext == extension template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ extern yang_stmt_t * yang_new_ext_stmt (ext_template_t *ext); /******************************************************************** * FUNCTION yang_new_obj_stmt * * Create a new YANG stmt node for an object * * INPUTS: * obj == object template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ extern yang_stmt_t * yang_new_obj_stmt (obj_template_t *obj); /******************************************************************** * FUNCTION yang_new_id_stmt * * Create a new YANG stmt node for an identity * * INPUTS: * identity == identity template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ extern yang_stmt_t * yang_new_id_stmt (ncx_identity_t *identity); /******************************************************************** * FUNCTION yang_new_feature_stmt * * Create a new YANG stmt node for a feature definition * * INPUTS: * feature == feature template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ extern yang_stmt_t * yang_new_feature_stmt (ncx_feature_t *feature); /******************************************************************** * FUNCTION yang_new_deviation_stmt * * Create a new YANG stmt node for a deviation definition * * INPUTS: * deviation == deviation template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ extern yang_stmt_t * yang_new_deviation_stmt (obj_deviation_t *deviation); /******************************************************************** * FUNCTION yang_free_stmt * * Delete a YANG statement node * * INPUTS: * stmt == yang_stmt_t node to delete * *********************************************************************/ extern void yang_free_stmt (yang_stmt_t *stmt); /******************************************************************** * FUNCTION yang_clean_stmtQ * * Delete a Q of YANG statement node * * INPUTS: * que == Q of yang_stmt_t node to delete * *********************************************************************/ extern void yang_clean_stmtQ (dlq_hdr_t *que); /******************************************************************** * FUNCTION yang_validate_date_string * * Validate a YANG date string for a revision entry * * Expected format is: * * YYYY-MM-DD * * Error messages are printed by this function * * INPUTS: * tkc == token chain in progress * mod == module in progress * tkerr == error struct to use (may be NULL to use tkc->cur) * datestr == string to validate * * RETURNS: * status *********************************************************************/ extern status_t yang_validate_date_string (tk_chain_t *tkc, ncx_module_t *mod, ncx_error_t *tkerr, const xmlChar *datestr); /******************************************************************** * FUNCTION yang_skip_statement * * Skip past the current invalid statement, starting at * an invalid keyword * * INPUTS: * tkc == token chain in progress * mod == module in progress *********************************************************************/ extern void yang_skip_statement (tk_chain_t *tkc, ncx_module_t *mod); /******************************************************************** * FUNCTION yang_top_keyword * * Check if the string is a top-level YANG keyword * * INPUTS: * keyword == string to check * * RETURNS: * TRUE if a top-level YANG keyword, FALSE otherwise *********************************************************************/ extern boolean yang_top_keyword (const xmlChar *keyword); /******************************************************************** * FUNCTION yang_new_import_ptr * * Create a new YANG import pointer node * * INPUTS: * modname = module name to set * modprefix = module prefix to set * revision = revision date to set * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ extern yang_import_ptr_t * yang_new_import_ptr (const xmlChar *modname, const xmlChar *modprefix, const xmlChar *revision); /******************************************************************** * FUNCTION yang_free_impptr_ptr * * Delete a YANG import pointer node * * INPUTS: * impptr == import pointer node to delete * *********************************************************************/ extern void yang_free_import_ptr (yang_import_ptr_t *impptr); /******************************************************************** * FUNCTION yang_clean_import_ptrQ * * Delete all the node entries in a YANG import pointer node Q * * INPUTS: * que == Q of yang_import_ptr_t to clean * *********************************************************************/ extern void yang_clean_import_ptrQ (dlq_hdr_t *que); /******************************************************************** * FUNCTION yang_find_import_ptr * * Find a YANG import pointer node in the specified Q * * INPUTS: * que == Q of yang_import_ptr_t to check * name == module name to find * * RETURNS: * pointer to found node, NULL if not found *********************************************************************/ extern yang_import_ptr_t * yang_find_import_ptr (dlq_hdr_t *que, const xmlChar *name); /******************************************************************** * FUNCTION yang_compare_revision_dates * * Compare 2 revision strings, which either may be NULL * * INPUTS: * revstring1 == revision string 1 (may be NULL) * revstring2 == revision string 2 (may be NULL) * * RETURNS: * -1 if revision 1 < revision 2 * 0 of they are the same * +1 if revision1 > revision 2 *********************************************************************/ extern int32 yang_compare_revision_dates (const xmlChar *revstring1, const xmlChar *revstring2); /******************************************************************** * FUNCTION yang_make_filename * * Malloc and construct a YANG filename * * INPUTS: * modname == [sub]module name * revision == [sub]module revision date (may be NULL) * isyang == TRUE for YANG extension * FALSE for YIN extension * * RETURNS: * malloced and filled in string buffer with filename * NULL if any error *********************************************************************/ extern xmlChar * yang_make_filename (const xmlChar *modname, const xmlChar *revision, boolean isyang); /******************************************************************** * FUNCTION yang_copy_filename * * Construct a YANG filename into a provided buffer * * INPUTS: * modname == [sub]module name * revision == [sub]module revision date (may be NULL) * buffer == buffer to copy filename into * bufflen == number of bytes available in buffer * isyang == TRUE for YANG extension * FALSE for YIN extension * * RETURNS: * malloced and filled in string buffer with filename * NULL if any error *********************************************************************/ extern status_t yang_copy_filename (const xmlChar *modname, const xmlChar *revision, xmlChar *buffer, uint32 bufflen, boolean isyang); /******************************************************************** * FUNCTION yang_split_filename * * Split a module parameter into its filename components * * INPUTS: * filename == filename string * modnamelen == address of return modname length * * OUTPUTS: * *modnamelen == module name length * * RETURNS: * TRUE if module@revision form was found * FALSE if not, and ignore the output because a * different form of the module parameter was used *********************************************************************/ extern boolean yang_split_filename (const xmlChar *filename, uint32 *modnamelen); /******************************************************************** * FUNCTION yang_fileext_is_yang * * Check if the filespec ends with the .yang extension * * INPUTS: * filename == filename string * * RETURNS: * TRUE if .yang file extension found * FALSE if not *********************************************************************/ extern boolean yang_fileext_is_yang (const xmlChar *filename); /******************************************************************** * FUNCTION yang_fileext_is_yin * * Check if the filespec ends with the .yin extension * * INPUTS: * filename == filename string * * RETURNS: * TRUE if .yin file extension found * FALSE if not *********************************************************************/ extern boolean yang_fileext_is_yin (const xmlChar *filename); /******************************************************************** * FUNCTION yang_fileext_is_xml * * Check if the filespec ends with the .xml extension * * INPUTS: * filename == filename string * * RETURNS: * TRUE if .xml file extension found * FALSE if not *********************************************************************/ extern boolean yang_fileext_is_xml (const xmlChar *filename); /******************************************************************** * FUNCTION yang_final_memcheck * * Check the node malloc and free counts *********************************************************************/ extern void yang_final_memcheck (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yang */ yuma123_2.14/netconf/src/ncx/runstack.c0000664000175000017500000014723714770023131020152 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: runstack.c Simple stack of script execution contexts to support nested 'run' commands within scripts ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 22-aug-07 abb begun; split from ncxcli.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "log.h" #include "ncxconst.h" #include "ncxtypes.h" #include "runstack.h" #include "status.h" #include "val.h" #include "var.h" #include "xml_util.h" #include "xpath.h" #include "xpath1.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define RUNSTACK_DEBUG 1 #endif #define RUNSTACK_BUFFLEN 32000 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean runstack_init_done = FALSE; static runstack_context_t defcxt; static dlq_hdr_t* select_useQ( runstack_context_t* rcxt ) { runstack_entry_t *se = (runstack_entry_t *)dlq_lastEntry(&rcxt->runstackQ); // TODO reverse this logic... if ( se == NULL) { return &rcxt->zero_condcbQ; } else { return &se->condcbQ; } } /******************************************************************** * FUNCTION make_parmvar * * * Still need to add the script parameters (if any) * with the runstack_setparm * * INPUTS: * source == file source * fp == file pointer * * RETURNS: * pointer to new val struct, NULL if an error *********************************************************************/ static val_value_t * make_parmval (const xmlChar *name, const xmlChar *value) { val_value_t *val; status_t res; /* create the parameter */ val = val_new_value(); if (!val) { return NULL; } /* set the string value */ res = val_set_string(val, name, value); if (res != NO_ERR) { val_free_value(val); return NULL; } return val; } /* make_parmval */ /******************************************************************** * FUNCTION get_parent_cond_state * * Get the parent conditional state * * INPUTS: * condcb == current conditional control block * * RETURNS: * TRUE if parent conditiona; state is TRUE * FALSE otherwise *********************************************************************/ static boolean get_parent_cond_state (runstack_condcb_t *condcb) { runstack_condcb_t *testcb; testcb = (runstack_condcb_t *)dlq_prevEntry(condcb); if (testcb == NULL) { return TRUE; } else if (testcb->cond_type == RUNSTACK_COND_IF) { return testcb->u.ifcb.curcond; } else { return testcb->u.loopcb.startcond; } } /* get_parent_cond_state */ /******************************************************************** * FUNCTION reset_cond_state * * Reset the runstack context condition state * * INPUTS: * rcxt == runstack context to use * *********************************************************************/ static void reset_cond_state (runstack_context_t *rcxt) { runstack_entry_t *se; dlq_hdr_t *useQ; runstack_condcb_t *condcb; se = (runstack_entry_t *)dlq_lastEntry(&rcxt->runstackQ); if (se == NULL) { useQ = &rcxt->zero_condcbQ; } else { useQ = &se->condcbQ; } condcb = (runstack_condcb_t *)dlq_lastEntry(useQ); if (condcb == NULL) { rcxt->cond_state = TRUE; } else if (condcb->cond_type == RUNSTACK_COND_IF) { rcxt->cond_state = condcb->u.ifcb.curcond; } else { rcxt->cond_state = condcb->u.loopcb.startcond; } } /* reset_cond_state */ /******************************************************************** * FUNCTION free_line_entry * * Clean and free a runstack line entry * * INPUTS: * le == line entry to free * *********************************************************************/ static void free_line_entry (runstack_line_t *le) { if (le->line) { m__free(le->line); } m__free(le); } /* free_line_entry */ /******************************************************************** * FUNCTION new_line_entry * * Malloc and init a new runstack line entry * * INPUTS: * line == line to save, a copy will be made * * RETURNS: * pointer to new runstack line entry, NULL if an error *********************************************************************/ static runstack_line_t * new_line_entry (const xmlChar *line) { runstack_line_t *le; le = m__getObj(runstack_line_t); if (le == NULL) { return NULL; } memset(le, 0x0, sizeof(runstack_line_t)); le->line = xml_strdup(line); if (le->line == NULL) { m__free(le); return NULL; } return le; } /* new_line_entry */ /******************************************************************** * FUNCTION get_loopcb * * Get the most recent loop control block * * INPUTS: * rcxt == runstack contect to use * * RETURNS: * pointer to last cond block with loopcb or NULL if none *********************************************************************/ static runstack_condcb_t *get_loopcb (runstack_context_t *rcxt) { dlq_hdr_t *useQ = select_useQ( rcxt ); runstack_condcb_t *condcb = (runstack_condcb_t *)dlq_lastEntry(useQ); for (; condcb; condcb = (runstack_condcb_t *)dlq_prevEntry(condcb)) { if (condcb->cond_type == RUNSTACK_COND_LOOP) { return condcb; } } return NULL; } /* get_loopcb */ /******************************************************************** * FUNCTION get_first_loopcb * * Get the first loop, which has the lineQ * * INPUTS: * rcxt == runstack contect to use * * RETURNS: * pointer to first cond block with loopcb or NULL if none *********************************************************************/ static runstack_condcb_t *get_first_loopcb (runstack_context_t *rcxt) { dlq_hdr_t *useQ = select_useQ( rcxt ); runstack_condcb_t *condcb = (runstack_condcb_t *)dlq_firstEntry(useQ); for (; condcb; condcb = (runstack_condcb_t *)dlq_nextEntry(condcb)) { if (condcb->cond_type == RUNSTACK_COND_LOOP) { return condcb; } } return NULL; } /* get_first_loopcb */ /******************************************************************** * FUNCTION free_condcb * * Clean and free a runstack cond. control block * * INPUTS: * condcb == conditional control block to clean and free * *********************************************************************/ static void free_condcb (runstack_condcb_t *condcb) { runstack_line_t *le; runstack_loopcb_t *loopcb; switch (condcb->cond_type) { case RUNSTACK_COND_NONE: SET_ERROR(ERR_INTERNAL_PTR); break; case RUNSTACK_COND_IF: break; case RUNSTACK_COND_LOOP: loopcb = &condcb->u.loopcb; if (loopcb->xpathpcb != NULL) { xpath_free_pcb(loopcb->xpathpcb); } if (loopcb->docroot != NULL) { val_free_value(loopcb->docroot); } while (!dlq_empty(&loopcb->lineQ)) { le = (runstack_line_t *)dlq_deque(&loopcb->lineQ); free_line_entry(le); } break; default: SET_ERROR(ERR_INTERNAL_PTR); } m__free(condcb); } /* free_condcb */ /******************************************************************** * FUNCTION new_condcb * * Malloc and init a new runstack cond control block * * INPUTS: * cond_type == condition control block type * * RETURNS: * pointer to new runstack if control block entry, NULL if an error *********************************************************************/ static runstack_condcb_t * new_condcb (runstack_condtype_t cond_type) { runstack_condcb_t *condcb; condcb = m__getObj(runstack_condcb_t); if (condcb == NULL) { return NULL; } memset(condcb, 0x0, sizeof(runstack_condcb_t)); condcb->cond_type = cond_type; if (cond_type == RUNSTACK_COND_LOOP) { dlq_createSQue(&condcb->u.loopcb.lineQ); } return condcb; } /* new_condcb */ /******************************************************************** * FUNCTION free_stack_entry * * Clean and free a runstack entry * * INPUTS: * se == stack entry to free * * INPUTS: * se == stack entry to free *********************************************************************/ static void free_stack_entry (runstack_entry_t *se) { ncx_var_t *var; runstack_condcb_t *condcb; if (se->buff) { m__free(se->buff); } if (se->fp) { fclose(se->fp); } if (se->source) { m__free(se->source); } while (!dlq_empty(&se->parmQ)) { var = (ncx_var_t *)dlq_deque(&se->parmQ); var_free(var); } while (!dlq_empty(&se->varQ)) { var = (ncx_var_t *)dlq_deque(&se->varQ); var_free(var); } while (!dlq_empty(&se->condcbQ)) { condcb = (runstack_condcb_t *)dlq_deque(&se->condcbQ); free_condcb(condcb); } m__free(se); } /* free_stack_entry */ /******************************************************************** * FUNCTION new_stack_entry * * Malloc and init a new runstack entry * * INPUTS: * source == file source * * RETURNS: * pointer to new val struct, NULL if an error *********************************************************************/ static runstack_entry_t * new_stack_entry (const xmlChar *source) { runstack_entry_t *se; se = m__getObj(runstack_entry_t); if (se == NULL) { return NULL; } memset(se, 0x0, sizeof(runstack_entry_t)); dlq_createSQue(&se->parmQ); dlq_createSQue(&se->varQ); dlq_createSQue(&se->condcbQ); /* get a new line input buffer */ se->buff = m__getMem(RUNSTACK_BUFFLEN); if (!se->buff) { free_stack_entry(se); return NULL; } /* set the script source */ se->source = xml_strdup(source); if (!se->source) { free_stack_entry(se); return NULL; } return se; } /* new_stack_entry */ /******************************************************************** * Utility function for removing the last and deleting and * entry from a queue. * * \param rcxt the runstack context * \param condcd the runstack control sequence * \return NO_ERR; * ********************************************************************/ static status_t free_condcb_end( runstack_context_t* rcxt, runstack_condcb_t* condcb, dlq_hdr_t* useQ ) { /* this 'end' completes this queue, remove it from the stack and delete it*/ dlq_remove(condcb); free_condcb(condcb); /* figure out the new conditional state */ condcb = (runstack_condcb_t *)dlq_lastEntry(useQ); if (condcb == NULL) { /* no conditionals at all in this frame now */ rcxt->cond_state = TRUE; } else if (condcb->cond_type == RUNSTACK_COND_IF) { rcxt->cond_state = condcb->u.ifcb.curcond; } else { rcxt->cond_state = condcb->u.loopcb.startcond; } return NO_ERR; } /******************************************************************** * Utility function for selecting the line end entry. * * \param rcxt the runstack context * \param loopcb the loop cb entry * \param useQ the useQ (thius will be updated) * \return the line entry to use. * ********************************************************************/ static runstack_line_t* select_end_entry( runstack_context_t* rcxt, runstack_loopcb_t* loopcb, dlq_hdr_t** useQ ) { /* get the 'end' entry in the lineq */ if (loopcb->collector) { if (rcxt->cur_src == RUNSTACK_SRC_LOOP) { /* looping in the outside loop and collecting in the inside loop * the 'end' command may not be last */ return loopcb->collector->cur_line; } else { /* collecting so the 'end' command is last */ *useQ = &loopcb->collector->lineQ; return (runstack_line_t *)dlq_lastEntry(*useQ); } } else { /* only loop collecting so the 'end' command is last */ *useQ = &loopcb->lineQ; return(runstack_line_t *)dlq_lastEntry(*useQ); } } /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION runstack_level * * Get the current stack level * INPUTS: * rcxt == runstack context to use * * RETURNS: * current stack level; 0 --> not in any script *********************************************************************/ extern uint32 runstack_level (runstack_context_t *rcxt) { if (rcxt == NULL) { rcxt = &defcxt; } return rcxt->script_level; } /* runstack_level */ /******************************************************************** * FUNCTION runstack_push * * Add a script nest level context to the stack * Call just after the file is opened and before * the first line has been read * * Still need to add the script parameters (if any) * with the runstack_setparm * * INPUTS: * rcxt == runstack context to use * source == file source * fp == file pointer * * RETURNS: * status *********************************************************************/ status_t runstack_push (runstack_context_t *rcxt, const xmlChar *source, FILE *fp) { runstack_entry_t *se; val_value_t *val; status_t res; #ifdef DEBUG if (source == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (rcxt == NULL) { rcxt = &defcxt; } /* check if this would overflow the runstack */ if (rcxt->script_level+1 == rcxt->max_script_level) { return ERR_NCX_RESOURCE_DENIED; } se = new_stack_entry(source); if (se == NULL) { return ERR_INTERNAL_MEM; } se->fp = fp; se->bufflen = RUNSTACK_BUFFLEN; /* create the P0 parameter */ val = make_parmval((const xmlChar *)"0", source); if (!val) { free_stack_entry(se); return ERR_INTERNAL_MEM; } /* need to increment script level now so var_set_move * will work, and the correct script parameter queue * will be used for the P0 parameter */ dlq_enque(se, &rcxt->runstackQ); rcxt->script_level++; rcxt->cur_src = RUNSTACK_SRC_SCRIPT; /* create a new var entry and add it to the runstack que * pass off 'val' memory here */ res = var_set_move(rcxt, (const xmlChar *)"0", 1, VAR_TYP_LOCAL, val); if (res != NO_ERR) { dlq_remove(se); free_stack_entry(se); rcxt->script_level--; return res; } if (LOGDEBUG) { log_debug("\nrunstack: Starting level %u script %s", rcxt->script_level, source); } return NO_ERR; } /* runstack_push */ /******************************************************************** * FUNCTION runstack_pop * * Remove a script nest level context from the stack * Call just after script is completed * * INPUTS: * rcxt == runstack context to use *********************************************************************/ void runstack_pop (runstack_context_t *rcxt) { runstack_entry_t *se; runstack_condcb_t *condcb; if (rcxt == NULL) { rcxt = &defcxt; } if (rcxt->script_level == 0) { SET_ERROR(ERR_INTERNAL_VAL); return; } se = (runstack_entry_t *)dlq_lastEntry(&rcxt->runstackQ); if (se == NULL) { SET_ERROR(ERR_INTERNAL_VAL); return; } dlq_remove(se); if (se->source && LOGDEBUG) { log_debug("\nrunstack: Ending level %u script %s", rcxt->script_level, se->source); } free_stack_entry(se); rcxt->script_level--; /* reset the current input source */ condcb = get_loopcb(rcxt); if (condcb == NULL) { rcxt->cur_src = (rcxt->script_level) ? RUNSTACK_SRC_SCRIPT : RUNSTACK_SRC_USER; } else if (condcb->u.loopcb.loop_state == RUNSTACK_LOOP_COLLECTING) { rcxt->cur_src = (rcxt->script_level) ? RUNSTACK_SRC_SCRIPT : RUNSTACK_SRC_USER; } else { rcxt->cur_src = RUNSTACK_SRC_LOOP; } } /* runstack_pop */ /******************************************************************** * FUNCTION runstack_get_cmd * * Read the current runstack context and construct * a command string for processing by do_run_script. * - Comment lines will be skipped. * - Extended lines will be concatenated in the * buffer. If a buffer overflow occurs due to this * concatenation, an error will be returned * * INPUTS: * rcxt == runstack context to use * res == address of status result * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to the command line to process (should treat as CONST !!!) * NULL if some error *********************************************************************/ xmlChar * runstack_get_cmd (runstack_context_t *rcxt, status_t *res) { runstack_entry_t *se; xmlChar *retstr, *start, *str; boolean done; int len, total, errint; #ifdef DEBUG if (res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (rcxt == NULL) { rcxt = &defcxt; } if (rcxt->script_level == 0) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /* init locals */ se = (runstack_entry_t *)dlq_lastEntry(&rcxt->runstackQ); if (se == NULL) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } retstr = NULL; /* start of return string */ start = se->buff; total = 0; done = FALSE; if (rcxt->script_cancel) { if (LOGINFO) { log_info("\nScript '%s' canceled", se->source); } done = TRUE; *res = ERR_NCX_CANCELED; runstack_clear_cancel(rcxt); } /* get a command line, handling comment and continuation lines */ while (!done) { /* check overflow error */ if (total==se->bufflen) { *res = ERR_BUFF_OVFL; /* do not allow truncated command to execute */ retstr = NULL; done = TRUE; continue; } /* read the next line from the file */ if (!fgets((char *)start, se->bufflen-total, se->fp)) { /* check if the file ended and that is why * no line was retrieved */ errint = feof(se->fp); if (retstr) { log_warn("\nWarning: script possibly truncated." "\n Ended on a continuation line"); *res = NO_ERR; } else if (errint) { *res = ERR_NCX_EOF; } else { *res = ERR_NCX_READ_FAILED; } done = TRUE; continue; } se->linenum++; /* used for error messages */ len = (int)xml_strlen(start); /* get rid of EOLN if present */ if (len && start[len-1]=='\n') { start[--len] = 0; } /* check blank line */ str = start; while (*str && xml_isspace(*str)) { str++; } if (!*str) { if (retstr) { /* return the string we have so far */ *res = NO_ERR; done = TRUE; } else { /* try again */ continue; } } /* check first line or line continuation in progress */ if (retstr == NULL) { /* retstr not set yet, allowed to have a comment line here */ if (*str == '#') { /* got a comment, try for another line */ *start = 0; continue; } else { /* start the return string and keep going */ str = start; retstr = start; } } /* check line continuation */ if (len && start[len-1]=='\\') { /* get rid of the final backslash */ total += len-1; start[len-1] = 0; start += len-1; /* get another line full */ } else { *res = NO_ERR; done = TRUE; } } if (retstr == NULL) { runstack_pop(rcxt); } else if (LOGDEBUG) { log_debug("\nrunstack: run line %u, %s\n cmd: %s", se->linenum, se->source, retstr); } return retstr; } /* runstack_get_cmd */ /******************************************************************** * FUNCTION runstack_cancel * * Cancel all running scripts * * INPUTS: * rcxt == runstack context to use *********************************************************************/ void runstack_cancel (runstack_context_t *rcxt) { if (rcxt == NULL) { rcxt = &defcxt; } if (rcxt->script_level) { rcxt->script_cancel = TRUE; } } /* runstack_cancel */ /******************************************************************** * FUNCTION runstack_clear_cancel * * Clear the cancel flags * * INPUTS: * rcxt == runstack context to use *********************************************************************/ void runstack_clear_cancel (runstack_context_t *rcxt) { if (rcxt == NULL) { rcxt = &defcxt; } if (rcxt->script_level == 0 && rcxt->script_cancel) { rcxt->cur_src = RUNSTACK_SRC_USER; rcxt->script_cancel = FALSE; } } /* runstack_clear_cancel */ /******************************************************************** * FUNCTION runstack_get_que * * Read the current runstack context and figure * out which queue to get * * INPUTS: * rcxt == runstack context to use * isglobal == TRUE if global queue desired * == FALSE if the runstack var que is desired * * RETURNS: * pointer to the requested que *********************************************************************/ dlq_hdr_t * runstack_get_que (runstack_context_t *rcxt, boolean isglobal) { runstack_entry_t *se; if (rcxt == NULL) { rcxt = &defcxt; } /* check global que */ if (isglobal) { return &rcxt->globalQ; } /* check level zero local que */ if (rcxt->script_level == 0) { return &rcxt->zeroQ; } /* get the current slot */ se = (runstack_entry_t *)dlq_lastEntry(&rcxt->runstackQ); if (se == NULL) { return NULL; } return &se->varQ; } /* runstack_get_que */ /******************************************************************** * FUNCTION runstack_get_parm_que * * Get the parameter queue for the current stack level * * INPUTS: * rcxt == runstack context to use * * RETURNS: * que for current stack level, NULL if an error *********************************************************************/ dlq_hdr_t * runstack_get_parm_que (runstack_context_t *rcxt) { runstack_entry_t *se; if (rcxt == NULL) { rcxt = &defcxt; } if (rcxt->script_level == 0) { return NULL; } /* get the current slot */ se = (runstack_entry_t *)dlq_lastEntry(&rcxt->runstackQ); if (se == NULL) { return NULL; } return &se->parmQ; } /* runstack_get_parm_que */ /******************************************************************** * FUNCTION runstack_init * * Must Init this module before using it!!! * *********************************************************************/ void runstack_init (void) { if (!runstack_init_done) { runstack_init_context(&defcxt); runstack_init_done = TRUE; } } /* runstack_init */ /******************************************************************** * FUNCTION runstack_cleanup * * Must cleanup this module after using it!!! * *********************************************************************/ void runstack_cleanup (void) { if (runstack_init_done) { runstack_clean_context(&defcxt); runstack_init_done = FALSE; } } /* runstack_cleanup */ /******************************************************************** * FUNCTION runstack_clean_context * * INPUTS: * rcxt == runstack context to clean, but not free * *********************************************************************/ void runstack_clean_context (runstack_context_t *rcxt) { ncx_var_t *var; runstack_condcb_t *condcb; #ifdef DEBUG if (rcxt == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (rcxt->script_level > 0) { runstack_pop(rcxt); } while (!dlq_empty(&rcxt->globalQ)) { var = (ncx_var_t *)dlq_deque(&rcxt->globalQ); var_free(var); } while (!dlq_empty(&rcxt->zeroQ)) { var = (ncx_var_t *)dlq_deque(&rcxt->zeroQ); var_free(var); } while (!dlq_empty(&rcxt->zero_condcbQ)) { condcb= (runstack_condcb_t *)dlq_deque(&rcxt->zero_condcbQ); free_condcb(condcb); } } /* runstack_clean_context */ /******************************************************************** * FUNCTION runstack_free_context * * INPUTS: * rcxt == runstack context to free * *********************************************************************/ void runstack_free_context (runstack_context_t *rcxt) { #ifdef DEBUG if (rcxt == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif runstack_clean_context(rcxt); m__free(rcxt); } /* runstack_free_context */ /******************************************************************** * FUNCTION runstack_init_context * * Initialize a pre-malloced runstack context * * INPUTS: * rcxt == runstack context to free * *********************************************************************/ void runstack_init_context (runstack_context_t *rcxt) { #ifdef DEBUG if (rcxt == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif memset(rcxt, 0x0, sizeof(runstack_context_t)); dlq_createSQue(&rcxt->globalQ); dlq_createSQue(&rcxt->zeroQ); dlq_createSQue(&rcxt->runstackQ); dlq_createSQue(&rcxt->zero_condcbQ); rcxt->max_script_level = RUNSTACK_MAX_NEST; rcxt->cond_state = TRUE; rcxt->cur_src = RUNSTACK_SRC_USER; } /* runstack_init_context */ /******************************************************************** * FUNCTION runstack_new_context * * Malloc a new runstack context * * RETURNS: * malloced and initialized runstack context * *********************************************************************/ runstack_context_t * runstack_new_context (void) { runstack_context_t *rcxt; rcxt = m__getObj(runstack_context_t); if (rcxt == NULL) { return NULL; } runstack_init_context(rcxt); return rcxt; } /* runstack_new_context */ /******************************************************************** * FUNCTION runstack_session_cleanup * * Cleanup after a yangcli session has ended * * INPUTS: * rcxt == runstack context to use * *********************************************************************/ void runstack_session_cleanup (runstack_context_t *rcxt) { runstack_entry_t *se; if (rcxt == NULL) { rcxt = &defcxt; } var_cvt_generic(&rcxt->globalQ); var_cvt_generic(&rcxt->zeroQ); for (se = (runstack_entry_t *)dlq_firstEntry(&rcxt->runstackQ); se != NULL; se = (runstack_entry_t *)dlq_nextEntry(se)) { var_cvt_generic(&se->varQ); } } /* runstack_session_cleanup */ /******************************************************************** * FUNCTION runstack_get_source * * Determine which input source is active for * the specified runstack context * * INPUTS: * rcxt == runstack context to use * *********************************************************************/ runstack_src_t runstack_get_source (runstack_context_t *rcxt) { if (rcxt == NULL) { rcxt = &defcxt; } return rcxt->cur_src; } /* runstack_get_source */ /******************************************************************** * FUNCTION runstack_save_line * * Save the current line if needed if a loop is active * * INPUTS: * rcxt == runstack context to use * line == line to save, a copy will be made * * RETURNS: * status *********************************************************************/ status_t runstack_save_line (runstack_context_t *rcxt, const xmlChar *line) { runstack_condcb_t *condcb; runstack_loopcb_t *loopcb; runstack_line_t *le; #ifdef DEBUG if (line == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (rcxt == NULL) { rcxt = &defcxt; } if (rcxt->cur_src == RUNSTACK_SRC_LOOP) { return NO_ERR; } condcb = get_loopcb(rcxt); if (condcb != NULL) { loopcb = &condcb->u.loopcb; if (loopcb->loop_state == RUNSTACK_LOOP_COLLECTING) { /* save this line */ le = new_line_entry(line); if (le == NULL) { return ERR_INTERNAL_MEM; } if (loopcb->collector != NULL) { dlq_enque(le, &loopcb->collector->lineQ); } else { dlq_enque(le, &loopcb->lineQ); } /* adjust any loopcb first_line pointers that * need to be set */ dlq_hdr_t *useQ = select_useQ( rcxt ); for (condcb = (runstack_condcb_t *)dlq_firstEntry(useQ); condcb != NULL; condcb = (runstack_condcb_t *)dlq_nextEntry(condcb)) { if (condcb->cond_type != RUNSTACK_COND_LOOP) { continue; } loopcb = &condcb->u.loopcb; if (!loopcb->empty_block && loopcb->first_line == NULL) { /* the first line needs to be set */ loopcb->first_line = le; } } } } return NO_ERR; } /* runstack_save_line */ /******************************************************************** * FUNCTION runstack_get_loop_cmd * * Get the next command during loop processing. * This function is called after the while loop end * has been reached, and buffered commands are used * * INPUTS: * rcxt == runstack context to use * res == address of status result * * OUTPUTS: * *res == function result status * == ERR_NCX_LOOP_ENDED if the loop expr was just * evaluated to FALSE; In this case the caller must * use the runstack_get_source function to determine * the source of the next line. If this occurs, * the loopcb (and while context) will be removed from * the curent runstack frame * * RETURNS: * pointer to the command line to process (should treat as CONST !!!) * NULL if some error *********************************************************************/ xmlChar * runstack_get_loop_cmd (runstack_context_t *rcxt, status_t *res) { runstack_condcb_t *condcb; runstack_loopcb_t *loopcb, *test_loopcb, *first_loopcb; runstack_line_t *le; xpath_result_t *result; boolean needremove, needeval, needmax, cond; #ifdef DEBUG if (res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif first_loopcb = NULL; loopcb = NULL; le = NULL; result = NULL; needremove = FALSE; needeval = FALSE; needmax = FALSE; cond = FALSE; *res = NO_ERR; if (rcxt == NULL) { rcxt = &defcxt; } if (rcxt->script_cancel) { if (LOGINFO) { log_info("\nScript in loop canceled"); } *res = ERR_NCX_CANCELED; if (rcxt->script_level == 0) { runstack_clear_cancel(rcxt); } else { runstack_pop(rcxt); } return NULL; } condcb = get_loopcb(rcxt); if (condcb == NULL) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } loopcb = &condcb->u.loopcb; if (loopcb->loop_state == RUNSTACK_LOOP_COLLECTING) { /* need to get a previous loopcb that is looping */ first_loopcb = loopcb->collector; if (first_loopcb == NULL) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } test_loopcb = first_loopcb; } else { test_loopcb = loopcb; } if (loopcb->empty_block) { /* corner-case, no statements */ needeval = TRUE; needmax = TRUE; } else if (test_loopcb->cur_line == NULL) { /* the next line is supposed to be the first line */ le = test_loopcb->cur_line = test_loopcb->first_line; if (first_loopcb != NULL) { if (loopcb->first_line == NULL) { loopcb->first_line = le; } } /* the following test may compare cur_line to a * NULL last_line if the collector is different * than the current loop. This is OK; the * outside loop is looping and only the inside * loop is collecting, so the last_line is not * set until runstack_handle_end sets it */ } else if (test_loopcb->cur_line == loopcb->last_line) { /* the next line is supposed to be the first line * set the inside loop cur line not the outside * loop cur line. Since the last line is actually * set on the inside loop, the state is now LOOPING * so the inside loop cur_line pointer will be used * until the loopcb is deleted */ le = loopcb->cur_line = loopcb->first_line; needeval = TRUE; needmax = TRUE; } else { /* in the list somewhere 1 .. N * the outside loop is looping and the inside * loop is collecting; or there is only 1 loop * which is collecting or looping */ le = test_loopcb->cur_line = (runstack_line_t *)dlq_nextEntry(test_loopcb->cur_line); if (first_loopcb != NULL) { if (loopcb->first_line == NULL) { loopcb->first_line = le; } } } if (*res == NO_ERR && needmax) { loopcb->loop_count++; if (loopcb->maxloops) { if (loopcb->loop_count == loopcb->maxloops) { log_debug("\nrunstack: max loop iterations ('%u') reached", loopcb->maxloops); /* need to remove the loopcb and change the * script source */ needremove = TRUE; needeval = FALSE; } } } if (*res == NO_ERR && needeval) { /* get current condition state * correct syntax will not allow the current context * to be inside an if-stmt already, so checking the * current if-stmt status should give the outside * loop context */ cond = runstack_get_cond_state(rcxt); if (cond) { /* try to parse the XPath expression */ result = xpath1_eval_expr(loopcb->xpathpcb, loopcb->docroot, /* context */ loopcb->docroot, TRUE, FALSE, res); if (result != NULL && *res == NO_ERR) { /* get new condition state for this loop */ cond = xpath_cvt_boolean(result); xpath_free_result(result); } else { /* error in the XPath expression */ if (result != NULL) { xpath_free_result(result); } return NULL; } } if (!cond) { needremove = TRUE; } } if (needremove) { /* save a pointer to the last line; * since this was not the first loopcb, * the last_line pointer will be valid * in the previous loopcb on the queue */ le = loopcb->last_line; dlq_remove(condcb); free_condcb(condcb); *res = ERR_NCX_LOOP_ENDED; /* reset the runstack context data structures * check if there are any more active loops */ condcb = get_loopcb(rcxt); if (condcb == NULL) { /* done with all looping */ rcxt->cur_src = (rcxt->script_level) ? RUNSTACK_SRC_SCRIPT : RUNSTACK_SRC_USER; } else { /* set the cur line pointer to the end of this * just deleted loop */ loopcb = &condcb->u.loopcb; if (loopcb->loop_state == RUNSTACK_LOOP_LOOPING) { rcxt->cur_src = RUNSTACK_SRC_LOOP; } else { rcxt->cur_src = (rcxt->script_level) ? RUNSTACK_SRC_SCRIPT : RUNSTACK_SRC_USER; } } reset_cond_state(rcxt); le = NULL; } if (LOGDEBUG2) { if (le) { log_debug2("\nrunstack: loop cmd '%s'", le->line); } else { log_debug2("\nrunstack: loop cmd NULL"); } } return (le) ? le->line : NULL; } /* runstack_get_loop_cmd */ /******************************************************************** * FUNCTION runstack_get_cond_state * * Get the current conditional code state for the context * * INPUTS: * rcxt == runstack context to use * * RETURNS: * TRUE if in a TRUE conditional or no conditional * FALSE if in a FALSE conditional statement right now *********************************************************************/ boolean runstack_get_cond_state (runstack_context_t *rcxt) { if (rcxt == NULL) { rcxt = &defcxt; } return rcxt->cond_state; } /* runstack_get_cond_state */ /******************************************************************** * FUNCTION runstack_handle_while * * Process the current command, which is a 'while' command. * This must be called before the next command is retrieved * during runstack context processing * * INPUTS: * rcxt == runstack context to use * maxloops == max number of loop iterations * xpathpcb == XPath control block to save which contains * the expression to be processed. * !!! this memory is handed off here; it will * !!! be freed as part of the loopcb context * docroot == docroot var struct that represents * the XML document to run the XPath expr against * !!! this memory is handed off here; it will * !!! be freed as part of the loopcb context * * RETURNS: * status; if status != NO_ERR then the xpathpcb and docroot * parameters need to be freed by the caller *********************************************************************/ status_t runstack_handle_while (runstack_context_t *rcxt, uint32 maxloops, xpath_pcb_t *xpathpcb, val_value_t *docroot) { runstack_condcb_t *condcb, *testloopcb; runstack_loopcb_t *loopcb; xpath_result_t *result; status_t res; assert( xpathpcb && "xpathpcb == NULL" ); assert( docroot && "docroot == NULL "); res = NO_ERR; if (rcxt == NULL) { rcxt = &defcxt; } dlq_hdr_t *useQ = select_useQ( rcxt ); condcb = new_condcb(RUNSTACK_COND_LOOP); if (condcb == NULL) { return ERR_INTERNAL_MEM; } loopcb = &condcb->u.loopcb; loopcb->loop_state = RUNSTACK_LOOP_COLLECTING; loopcb->maxloops = maxloops; if (rcxt->cond_state) { /* figure out if the first loop is enabled or not */ result = xpath1_eval_expr(xpathpcb, docroot, /* context */ docroot, TRUE, FALSE, &res); if (result != NULL && res == NO_ERR) { /* get new condition state for this loop */ rcxt->cond_state = loopcb->startcond = xpath_cvt_boolean(result); } if (result != NULL) { xpath_free_result(result); } if (res != NO_ERR) { free_condcb(condcb); return res; } } else { /* do not bother evaluating the loop cond inside * a nested false conditional */ loopcb->startcond = FALSE; } /* check if this is a nested loop or the first loop */ testloopcb = get_first_loopcb(rcxt); if (testloopcb != NULL) { loopcb->collector = &testloopcb->u.loopcb; } dlq_enque(condcb, useQ); /* accept xpathpcb and docroot memory here */ loopcb->xpathpcb = xpathpcb; loopcb->docroot = docroot; return NO_ERR; } /* runstack_handle_while */ /******************************************************************** * FUNCTION runstack_handle_if * * Handle the if command for the specific runstack context * * INPUTS: * rcxt == runstack context to use * startcond == start condition state for this if block * may be FALSE because the current conditional * state is already FALSE * * RETURNS: * status *********************************************************************/ status_t runstack_handle_if (runstack_context_t *rcxt, boolean startcond) { runstack_condcb_t *condcb; runstack_ifcb_t *ifcb; if (rcxt == NULL) { rcxt = &defcxt; } dlq_hdr_t *useQ = select_useQ( rcxt ); condcb = new_condcb(RUNSTACK_COND_IF); if (condcb == NULL) { return ERR_INTERNAL_MEM; } ifcb = &condcb->u.ifcb; ifcb->ifstate = RUNSTACK_IF_IF; ifcb->ifused = ifcb->startcond = startcond; dlq_enque(condcb, useQ); /* only allow TRUE --> TRUE/FALSE transition * because a FALSE conditional is always inherited */ if (rcxt->cond_state) { rcxt->cond_state = startcond; } return NO_ERR; } /* runstack_handle_if */ /******************************************************************** * FUNCTION runstack_handle_elif * * Handle the elif command for the specific runstack context * * INPUTS: * rcxt == runstack context to use * startcond == start condition state for this elif block * may be FALSE because the current conditional * block has already used its TRUE block * * RETURNS: * status *********************************************************************/ status_t runstack_handle_elif (runstack_context_t *rcxt, boolean startcond) { runstack_condcb_t *condcb; runstack_ifcb_t *ifcb; status_t res; res = NO_ERR; if (rcxt == NULL) { rcxt = &defcxt; } dlq_hdr_t *useQ = select_useQ( rcxt ); condcb = (runstack_condcb_t *)dlq_lastEntry(useQ); if (condcb == NULL) { log_error("\nError: unexpected 'elif' command"); return ERR_NCX_INVALID_VALUE; } /* make sure this conditional block is an ifcb */ if (condcb->cond_type != RUNSTACK_COND_IF) { log_error("\nError: unexpected 'elif' command"); return ERR_NCX_INVALID_VALUE; } /* make sure the current state is IF or ELIF */ switch (condcb->u.ifcb.ifstate) { case RUNSTACK_IF_NONE: res = SET_ERROR(ERR_INTERNAL_VAL); break; case RUNSTACK_IF_IF: case RUNSTACK_IF_ELIF: ifcb = &condcb->u.ifcb; /* this is the expected state, update to ELIF state */ ifcb->ifstate = RUNSTACK_IF_ELIF; /* set the elif conditional state depending * on if any of the previous if or elif blocks * were true or not * * figure out the new conditional state */ if (get_parent_cond_state(condcb)) { if (ifcb->ifused) { /* this has to be a false block */ rcxt->cond_state = FALSE; ifcb->curcond = FALSE; } else { ifcb->ifused = startcond; rcxt->cond_state = startcond; ifcb->curcond = startcond; } } break; case RUNSTACK_IF_ELSE: log_error("\nError: unexpected 'elif'; previous 'else' command " "already active"); res = ERR_NCX_INVALID_VALUE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* runstack_handle_elif */ /******************************************************************** * FUNCTION runstack_handle_else * * Handle the elsecommand for the specific runstack context * * INPUTS: * rcxt == runstack context to use * * RETURNS: * status *********************************************************************/ status_t runstack_handle_else (runstack_context_t *rcxt) { runstack_condcb_t *condcb; status_t res; res = NO_ERR; if (rcxt == NULL) { rcxt = &defcxt; } dlq_hdr_t *useQ = select_useQ( rcxt ); condcb = (runstack_condcb_t *)dlq_lastEntry(useQ); if (condcb == NULL) { log_error("\nError: unexpected 'else' command"); return ERR_NCX_INVALID_VALUE; } /* make sure this conditional block is an ifcb */ if (condcb->cond_type != RUNSTACK_COND_IF) { log_error("\nError: unexpected 'else' command"); return ERR_NCX_INVALID_VALUE; } /* make sure the current state is IF or ELIF */ switch (condcb->u.ifcb.ifstate) { case RUNSTACK_IF_NONE: res = SET_ERROR(ERR_INTERNAL_VAL); break; case RUNSTACK_IF_IF: case RUNSTACK_IF_ELIF: /* this is the expected state, update to ELSE state */ condcb->u.ifcb.ifstate = RUNSTACK_IF_ELSE; /* set the else conditional state depending * on if any of the previous if or elif blocks * were true or not * * figure out the new conditional state */ rcxt->cond_state = condcb->u.ifcb.curcond = !condcb->u.ifcb.ifused; break; case RUNSTACK_IF_ELSE: log_error("\nError: unexpected 'else'; previous 'else' command " "already active"); res = ERR_NCX_INVALID_VALUE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* runstack_handle_else */ /******************************************************************** * FUNCTION runstack_handle_end * * Handle the end command for the specific runstack context * * INPUTS: * rcxt == runstack context to use * * RETURNS: * status *********************************************************************/ status_t runstack_handle_end (runstack_context_t *rcxt) { if (rcxt == NULL) { rcxt = &defcxt; } dlq_hdr_t *useQ = select_useQ( rcxt ); runstack_condcb_t *condcb = (runstack_condcb_t *)dlq_lastEntry(useQ); if ( !condcb ) { log_error("\nError: unexpected 'end' command"); return ERR_NCX_INVALID_VALUE; } if (condcb->cond_type == RUNSTACK_COND_IF) { log_debug2("\nrunstack: end for if command"); return free_condcb_end( rcxt, condcb, useQ ); } else { log_debug2("\nrunstack: end for while command"); runstack_loopcb_t *loopcb = &condcb->u.loopcb; if ( ! loopcb || loopcb->loop_state != RUNSTACK_LOOP_COLLECTING) { return SET_ERROR(ERR_INTERNAL_VAL); } if ( !loopcb->startcond) { return free_condcb_end( rcxt, condcb, useQ ); } runstack_line_t *le = select_end_entry( rcxt, loopcb, &useQ ); if ( !le ) { return SET_ERROR(ERR_INTERNAL_VAL); } /* check if the first line is this end line */ if (loopcb->first_line == le) { loopcb->first_line = NULL; } /* get pointer for loopcb.last_line */ if ( !loopcb->collector ) { /* outside loop so need to delete this 'end' line */ dlq_remove(le); free_line_entry(le); /* get the new last while loop line */ le = (runstack_line_t *)dlq_lastEntry(useQ); } else { /* get the next to last line */ le = (runstack_line_t *)dlq_prevEntry(le); } /* set the last_line pointer for this loopcb */ if ( !le ) { /* there are no lines in this while loop */ loopcb->empty_block = TRUE; loopcb->first_line = NULL; loopcb->last_line = NULL; } else { loopcb->last_line = le; } /* need to go back to the start of the while loop and test the eval * condition again */ /* loopcb->cur_line = NULL; */ loopcb->loop_state = RUNSTACK_LOOP_LOOPING; loopcb->loop_count = 1; rcxt->cur_src = RUNSTACK_SRC_LOOP; } return NO_ERR; } /* runstack_handle_end */ /******************************************************************** * FUNCTION runstack_get_if_used * * Check if the run context, which should be inside an if-stmt * now, has used the 'true' block already * * INPUTS: * rcxt == runstack context to use * * RETURNS: * TRUE if TRUE block already used * FALSE if FALSE not already used or not in an if-block *********************************************************************/ boolean runstack_get_if_used (runstack_context_t *rcxt) { runstack_condcb_t *condcb; if (rcxt == NULL) { rcxt = &defcxt; } dlq_hdr_t *useQ = select_useQ( rcxt ); condcb = (runstack_condcb_t *)dlq_lastEntry(useQ); if (condcb == NULL) { /* error -- not in any conditional block */ return FALSE; } else if (condcb->cond_type == RUNSTACK_COND_IF) { return condcb->u.ifcb.ifused; } else { /* error -- in a while loop */ return FALSE; } } /* runstack_get_if_used */ /* END runstack.c */ yuma123_2.14/netconf/src/ncx/ncx.h0000664000175000017500000024423614770023131017112 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ncx #define _H_ncx /* FILE: ncx.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Module Library Utility Functions Container of Definitions +-----------------+ | ncx_module_t | | ncxtypes.h | +-----------------+ Template/Schema +-----------------+ | obj_template_t | | obj.h | +-----------------+ ^ | | | Value Instances | | +-----------------+ | +---->| val_value_t | | | val.h | | +-----------------+ | | Data Types | +--------------------+ +---| typ_template_t | | typ.h | +--------------------+ ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 29-oct-05 abb Begun 20-jul-08 abb Start YANG rewrite; remove PSD and PS 23-aug-09 abb Update diagram in header */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION ncx_init * * Initialize the NCX module * * INPUTS: * savestr == TRUE if parsed description strings that are * not needed by the agent at runtime should * be saved anyway. Converters should use this value. * * == FALSE if uneeded strings should not be saved. * Embedded agents should use this value * * dlevel == desired debug output level * logtstamps == TRUE if log should use timestamps * FALSE if not; not used unless 'log' is present * startmsg == log_debug2 message to print before starting; * NULL if not used; * argc == CLI argument count for bootstrap CLI * argv == array of CLI parms for bootstrap CLI * * RETURNS: * status of the initialization procedure *********************************************************************/ extern status_t ncx_init (boolean savestr, log_debug_t dlevel, boolean logtstamps, const char *startmsg, int argc, char *argv[]); /******************************************************************** * FUNCTION ncx_cleanup * * cleanup NCX module *********************************************************************/ extern void ncx_cleanup (void); /******************************************************************** * FUNCTION ncx_new_module * * Malloc and initialize the fields in a ncx_module_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern ncx_module_t * ncx_new_module (void); /******************************************************************** * FUNCTION ncx_find_module * * Find a ncx_module_t in the ncx_modQ * These are the modules that are already loaded * * INPUTS: * modname == module name * revision == module revision date * * RETURNS: * module pointer if found or NULL if not *********************************************************************/ extern ncx_module_t * ncx_find_module (const xmlChar *modname, const xmlChar *revision); /******************************************************************** * FUNCTION ncx_find_module_que * * Find a ncx_module_t in the specified Q * Check the namespace ID * * INPUTS: * modQ == module Q to search * modname == module name * revision == module revision date * * RETURNS: * module pointer if found or NULL if not *********************************************************************/ extern ncx_module_t * ncx_find_module_que (dlq_hdr_t *modQ, const xmlChar *modname, const xmlChar *revision); /******************************************************************** * FUNCTION ncx_find_module_que_nsid * * Find a ncx_module_t in the specified Q * Check the namespace ID * * INPUTS: * modQ == module Q to search * nsid == xmlns ID to find * * RETURNS: * module pointer if found or NULL if not *********************************************************************/ extern ncx_module_t * ncx_find_module_que_nsid (dlq_hdr_t *modQ, xmlns_id_t nsid); /******************************************************************** * FUNCTION ncx_free_module * * Scrub the memory in a ncx_module_t by freeing all * the sub-fields and then freeing the entire struct itself * use if module was not added to registry * * MUST remove this struct from the ncx_modQ before calling * Does not remove module definitions from the registry * * Use the ncx_remove_module function if the module was * already successfully added to the modQ and definition registry * * INPUTS: * mod == ncx_module_t data structure to free *********************************************************************/ extern void ncx_free_module (ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_any_mod_errors * * Check if any of the loaded modules are loaded with non-fatal errors * * RETURNS: * TRUE if any modules are loaded with non-fatal errors * FALSE if all modules present have a status of NO_ERR *********************************************************************/ extern boolean ncx_any_mod_errors (void); /******************************************************************** * FUNCTION ncx_any_dependency_errors * * Check if any of the imports that this module relies on * were loadeds are loaded with non-fatal errors * * RETURNS: * TRUE if any modules are loaded with non-fatal errors * FALSE if all modules present have a status of NO_ERR *********************************************************************/ extern boolean ncx_any_dependency_errors (const ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_find_type * * Check if a typ_template_t in the mod->typeQ * * INPUTS: * mod == ncx_module to check * typname == type name * useall == TRUE to use all submodules * FALSE to only use the ones in the mod->includeQ * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern typ_template_t * ncx_find_type (ncx_module_t *mod, const xmlChar *typname, boolean useall); /******************************************************************** * FUNCTION ncx_find_type_que * * Check if a typ_template_t in the mod->typeQ * * INPUTS: * que == type Q to check * typname == type name * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern typ_template_t * ncx_find_type_que (const dlq_hdr_t *typeQ, const xmlChar *typname); /******************************************************************** * FUNCTION ncx_find_grouping * * Check if a grp_template_t in the mod->groupingQ * * INPUTS: * mod == ncx_module to check * grpname == group name * useall == TRUE to check all existing nodes * FALSE to only use includes visible to this [sub]mod * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern grp_template_t * ncx_find_grouping (ncx_module_t *mod, const xmlChar *grpname, boolean useall); /******************************************************************** * FUNCTION ncx_find_grouping_que * * Check if a grp_template_t in the specified Q * * INPUTS: * groupingQ == Queue of grp_template_t to check * grpname == group name * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern grp_template_t * ncx_find_grouping_que (const dlq_hdr_t *groupingQ, const xmlChar *grpname); /******************************************************************** * FUNCTION ncx_find_rpc * * Check if a rpc_template_t in the mod->rpcQ * * INPUTS: * mod == ncx_module to check * rpcname == RPC name * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern obj_template_t * ncx_find_rpc (const ncx_module_t *mod, const xmlChar *rpcname); /******************************************************************** * FUNCTION ncx_match_rpc * * Check if a rpc_template_t in the mod->rpcQ * * INPUTS: * mod == ncx_module to check * rpcname == RPC name to match * retcount == address of return match count * * OUTPUTS: * *retcount == number of matches found * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern obj_template_t * ncx_match_rpc (const ncx_module_t *mod, const xmlChar *rpcname, uint32 *retcount); /******************************************************************** * FUNCTION ncx_match_any_rpc * * Check if a rpc_template_t in in any module that * matches the rpc name string and maybe the owner * * INPUTS: * module == module name to check (NULL == check all) * rpcname == RPC name to match * retcount == address of return count of matches * * OUTPUTS: * *retcount == number of matches found * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern obj_template_t * ncx_match_any_rpc (const xmlChar *module, const xmlChar *rpcname, uint32 *retcount); /******************************************************************** * FUNCTION ncx_match_any_rpc_mod * * Check if a rpc_template_t is in the specified module * * INPUTS: * mod == module struct to check * rpcname == RPC name to match * retcount == address of return count of matches * * OUTPUTS: * *retcount == number of matches found * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern obj_template_t * ncx_match_any_rpc_mod (ncx_module_t *mod, const xmlChar *rpcname, uint32 *retcount); /******************************************************************** * FUNCTION ncx_match_rpc_error * * Generate an error for multiple matches * * INPUTS: * mod == module struct to check (may be NULL) * modname == module name if mod not set * == NULL to match all modules * rpcname == RPC name to match * match == TRUE to match partial command names * FALSE for exact match only * firstmsg == TRUE to do the first log_error banner msg * FALSE to skip this step *********************************************************************/ extern void ncx_match_rpc_error (ncx_module_t *mod, const xmlChar *modname, const xmlChar *rpcname, boolean match, boolean firstmsg); /******************************************************************** * FUNCTION ncx_find_any_object * * Check if an obj_template_t in in any module that * matches the object name string * * INPUTS: * objname == object name to match * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern obj_template_t * ncx_find_any_object (const xmlChar *objname); /******************************************************************** * FUNCTION ncx_match_any_object * * Check if an obj_template_t in in any module that * matches the object name string * * INPUTS: * objname == object name to match * name_match == name match mode enumeration * alt_names == TRUE if alternate names should be checked * after regular names; FALSE if not * retres == address of return status * * OUTPUTS: * if retres not NULL, *retres set to return status * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern obj_template_t * ncx_match_any_object (const xmlChar *objname, ncx_name_match_t name_match, boolean alt_names, status_t *retres); /******************************************************************** * FUNCTION ncx_find_any_object_que * * Check if an obj_template_t in in any module that * matches the object name string * * INPUTS: * modQ == Q of modules to check * objname == object name to match * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern obj_template_t * ncx_find_any_object_que (dlq_hdr_t *modQ, const xmlChar *objname); /******************************************************************** * FUNCTION ncx_find_object * * Find a top level module object * * INPUTS: * mod == ncx_module to check * typname == type name * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ extern obj_template_t * ncx_find_object (ncx_module_t *mod, const xmlChar *objname); /******************************************************************** * FUNCTION ncx_add_namespace_to_registry * * Add the namespace and prefix to the registry * or retrieve it if already set * * INPUTS: * mod == module to add to registry * tempmod == TRUE if this is a temporary add mode * FALSE if this is a real registry add * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncx_add_namespace_to_registry (ncx_module_t *mod, boolean tempmod); /******************************************************************** * FUNCTION ncx_add_to_registry * * Add all the definitions stored in an ncx_module_t to the registry * This step is deferred to keep the registry stable as possible * and only add modules in an all-or-none fashion. * * INPUTS: * mod == module to add to registry * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncx_add_to_registry (ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_add_to_modQ * * Add module to the current module Q * Used by yangdiff to bypass add_to_registry to support * N different module trees * * INPUTS: * mod == module to add to current module Q * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncx_add_to_modQ (ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_is_duplicate * * Search the specific module for the specified definition name. * This function is for modules in progress which have not been * added to the registry yet. * * INPUTS: * mod == ncx_module_t to check * defname == name of definition to find * RETURNS: * TRUE if found, FALSE otherwise *********************************************************************/ extern boolean ncx_is_duplicate (ncx_module_t *mod, const xmlChar *defname); /******************************************************************** * FUNCTION ncx_get_first_module * * Get the first module in the ncx_modQ * * RETURNS: * pointer to the first entry or NULL if empty Q *********************************************************************/ extern ncx_module_t * ncx_get_first_module (void); /******************************************************************** * FUNCTION ncx_get_next_module * * Get the next module in the ncx_modQ * * INPUTS: * mod == current module to find next * * RETURNS: * pointer to the first entry or NULL if empty Q *********************************************************************/ extern ncx_module_t * ncx_get_next_module (const ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_first_session_module * * Get the first module in the ncx_sesmodQ * * RETURNS: * pointer to the first entry or NULL if empty Q *********************************************************************/ extern ncx_module_t * ncx_get_first_session_module (void); /******************************************************************** * FUNCTION ncx_get_next_session_module * * Get the next module in the ncx_sesmodQ * * RETURNS: * pointer to the first entry or NULL if empty Q *********************************************************************/ extern ncx_module_t * ncx_get_next_session_module (const ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_modname * * Get the main module name * * INPUTS: * mod == module or submodule to get main module name * * RETURNS: * main module name or NULL if error *********************************************************************/ extern const xmlChar * ncx_get_modname (const ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_mod_nsid * * Get the main module namespace ID * * INPUTS: * mod == module or submodule to get main module namespace ID * * RETURNS: * namespace id number *********************************************************************/ extern xmlns_id_t ncx_get_mod_nsid (const ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_modversion * * Get the [sub]module version * INPUTS: * mod == module or submodule to get module version * * RETURNS: * module version or NULL if error *********************************************************************/ extern const xmlChar * ncx_get_modversion (const ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_modnamespace * * Get the module namespace URI * * INPUTS: * mod == module or submodule to get module namespace * * RETURNS: * module namespace or NULL if error *********************************************************************/ extern const xmlChar * ncx_get_modnamespace (const ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_modsource * * Get the module filespec source string * * INPUTS: * mod == module or submodule to use * * RETURNS: * module filespec source string *********************************************************************/ extern const xmlChar * ncx_get_modsource (const ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_mainmod * * Get the main module * * INPUTS: * mod == submodule to get main module * * RETURNS: * main module NULL if error *********************************************************************/ extern ncx_module_t * ncx_get_mainmod (ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_first_object * * Get the first object in the datadefQs for the specified module * Get any object with a name * * INPUTS: * mod == module to search for the first object * * RETURNS: * pointer to the first object or NULL if empty Q *********************************************************************/ extern obj_template_t * ncx_get_first_object (ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_next_object * * Get the next object in the specified module * Get any object with a name * * INPUTS: * mod == module struct to get the next object from * curobj == pointer to the current object to get the next for * * RETURNS: * pointer to the next object or NULL if none *********************************************************************/ extern obj_template_t * ncx_get_next_object (ncx_module_t *mod, obj_template_t *curobj); /******************************************************************** * FUNCTION ncx_get_first_data_object * * Get the first database object in the datadefQs * for the specified module * * INPUTS: * mod == module to search for the first object * * RETURNS: * pointer to the first object or NULL if empty Q *********************************************************************/ extern obj_template_t * ncx_get_first_data_object (ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_next_data_object * * Get the next database object in the specified module * * INPUTS: * mod == pointer to module to get object from * curobj == pointer to current object to get next from * * RETURNS: * pointer to the next object or NULL if none *********************************************************************/ extern obj_template_t * ncx_get_next_data_object (ncx_module_t *mod, obj_template_t *curobj); /******************************************************************** * FUNCTION ncx_new_import * * Malloc and initialize the fields in a ncx_import_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern ncx_import_t * ncx_new_import (void); /******************************************************************** * FUNCTION ncx_free_import * * Scrub the memory in a ncx_import_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * import == ncx_import_t data structure to free *********************************************************************/ extern void ncx_free_import (ncx_import_t *import); /******************************************************************** * FUNCTION ncx_find_import * * Search the importQ for a specified module name * * INPUTS: * mod == module to search (mod->importQ) * module == module name to find * * RETURNS: * pointer to the node if found, NULL if not found *********************************************************************/ extern ncx_import_t * ncx_find_import (const ncx_module_t *mod, const xmlChar *module); /******************************************************************** * FUNCTION ncx_find_import_que * * Search the specified importQ for a specified module name * * INPUTS: * importQ == Q of ncx_import_t to search * module == module name to find * * RETURNS: * pointer to the node if found, NULL if not found *********************************************************************/ extern ncx_import_t * ncx_find_import_que (const dlq_hdr_t *importQ, const xmlChar *module); /******************************************************************** * FUNCTION ncx_find_import_test * * Search the importQ for a specified module name * Do not set used flag * * INPUTS: * mod == module to search (mod->importQ) * module == module name to find * * RETURNS: * pointer to the node if found, NULL if not found *********************************************************************/ extern ncx_import_t * ncx_find_import_test (const ncx_module_t *mod, const xmlChar *module); /******************************************************************** * FUNCTION ncx_find_pre_import * * Search the importQ for a specified prefix value * * INPUTS: * mod == module to search (mod->importQ) * prefix == prefix string to find * * RETURNS: * pointer to the node if found, NULL if not found *********************************************************************/ extern ncx_import_t * ncx_find_pre_import (const ncx_module_t *mod, const xmlChar *prefix); /******************************************************************** * FUNCTION ncx_find_pre_import_que * * Search the specified importQ for a specified prefix value * * INPUTS: * importQ == Q of ncx_import_t to search * prefix == prefix string to find * * RETURNS: * pointer to the node if found, NULL if not found *********************************************************************/ extern ncx_import_t * ncx_find_pre_import_que (const dlq_hdr_t *importQ, const xmlChar *prefix); /******************************************************************** * FUNCTION ncx_find_pre_import_test * * Search the importQ for a specified prefix value * Test only, do not set used flag * * INPUTS: * mod == module to search (mod->importQ) * prefix == prefix string to find * * RETURNS: * pointer to the node if found, NULL if not found *********************************************************************/ extern ncx_import_t * ncx_find_pre_import_test (const ncx_module_t *mod, const xmlChar *prefix); /******************************************************************** * FUNCTION ncx_locate_modqual_import * * Search the specific module for the specified definition name. * * Okay for YANG or NCX * * - typ_template_t (NCX_NT_TYP) * - grp_template_t (NCX_NT_GRP) * - obj_template_t (NCX_NT_OBJ) * - rpc_template_t (NCX_NT_RPC) * - not_template_t (NCX_NT_NOTIF) * * INPUTS: * pcb == parser control block to use * imp == NCX import struct to use * defname == name of definition to find * *deftyp == specified type or NCX_NT_NONE if any will do * * OUTPUTS: * imp->mod may get set if not already * *deftyp == type retrieved if NO_ERR * * RETURNS: * pointer to the located definition or NULL if not found *********************************************************************/ extern void * ncx_locate_modqual_import (yang_pcb_t *pcb, ncx_import_t *imp, const xmlChar *defname, ncx_node_t *deftyp); /******************************************************************** * FUNCTION ncx_new_include * * Malloc and initialize the fields in a ncx_include_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern ncx_include_t * ncx_new_include (void); /******************************************************************** * FUNCTION ncx_free_include * * Scrub the memory in a ncx_include_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * inc == ncx_include_t data structure to free *********************************************************************/ extern void ncx_free_include (ncx_include_t *inc); /******************************************************************** * FUNCTION ncx_find_include * * Search the includeQ for a specified submodule name * * INPUTS: * mod == module to search (mod->includeQ) * submodule == submodule name to find * * RETURNS: * pointer to the node if found, NULL if not found *********************************************************************/ extern ncx_include_t * ncx_find_include (const ncx_module_t *mod, const xmlChar *submodule); /******************************************************************** * FUNCTION ncx_new_binary * * Malloc and fill in a new ncx_binary_t struct * * INPUTS: * none * RETURNS: * pointer to malloced and initialized ncx_binary_t struct * NULL if malloc error *********************************************************************/ extern ncx_binary_t * ncx_new_binary (void); /******************************************************************** * FUNCTION ncx_init_binary * * Init the memory of a ncx_binary_t struct * * INPUTS: * binary == ncx_binary_t struct to init *********************************************************************/ extern void ncx_init_binary (ncx_binary_t *binary); /******************************************************************** * FUNCTION ncx_clean_binary * * Scrub the memory of a ncx_binary_t but do not delete it * * INPUTS: * binary == ncx_binary_t struct to clean *********************************************************************/ extern void ncx_clean_binary (ncx_binary_t *binary); /******************************************************************** * FUNCTION ncx_free_binary * * Free all the memory in a ncx_binary_t struct * * INPUTS: * binary == struct to clean and free * *********************************************************************/ extern void ncx_free_binary (ncx_binary_t *binary); /******************************************************************** * FUNCTION ncx_new_identity * * Get a new ncx_identity_t struct * * INPUTS: * none * RETURNS: * pointer to a malloced ncx_identity_t struct, * or NULL if malloc error *********************************************************************/ extern ncx_identity_t * ncx_new_identity (void); /******************************************************************** * FUNCTION ncx_free_identity * * Free a malloced ncx_identity_t struct * * INPUTS: * identity == struct to free * *********************************************************************/ extern void ncx_free_identity (ncx_identity_t *identity); /******************************************************************** * FUNCTION ncx_find_identity * * Find a ncx_identity_t struct in the module and perhaps * any of its submodules * * INPUTS: * mod == module to search * name == identity name to find * useall == TRUE if all submodules should be checked * FALSE if only visible included submodules * should be checked * RETURNS: * pointer to found feature or NULL if not found *********************************************************************/ extern ncx_identity_t * ncx_find_identity (ncx_module_t *mod, const xmlChar *name, boolean useall); /******************************************************************** * FUNCTION ncx_find_identity_que * * Find a ncx_identity_t struct in the specified Q * * INPUTS: * identityQ == Q of ncx_identity_t to search * name == identity name to find * * RETURNS: * pointer to found identity or NULL if not found *********************************************************************/ extern ncx_identity_t * ncx_find_identity_que (const dlq_hdr_t *identityQ, const xmlChar *name); /******************************************************************** * FUNCTION ncx_new_filptr * * Get a new ncx_filptr_t struct * * INPUTS: * none * RETURNS: * pointer to a malloced or cached ncx_filptr_t struct, * or NULL if none available *********************************************************************/ extern ncx_filptr_t * ncx_new_filptr (void); /******************************************************************** * FUNCTION ncx_free_filptr * * Free a new ncx_filptr_t struct or add to the cache if room * * INPUTS: * filptr == struct to free * RETURNS: * none *********************************************************************/ extern void ncx_free_filptr (ncx_filptr_t *filptr); /******************************************************************** * FUNCTION ncx_new_revhist * * Create a revision history entry * * RETURNS: * malloced revision history entry or NULL if malloc error *********************************************************************/ extern ncx_revhist_t * ncx_new_revhist (void); /******************************************************************** * FUNCTION ncx_free_revhist * * Free a revision history entry * * INPUTS: * revhist == ncx_revhist_t data structure to free *********************************************************************/ extern void ncx_free_revhist (ncx_revhist_t *revhist); /******************************************************************** * FUNCTION ncx_find_revhist * * Search the revhistQ for a specified revision * * INPUTS: * mod == module to search (mod->importQ) * ver == version string to find * * RETURNS: * pointer to the node if found, NULL if not found *********************************************************************/ extern ncx_revhist_t * ncx_find_revhist (const ncx_module_t *mod, const xmlChar *ver); /******************************************************************** * FUNCTION ncx_init_enum * * Init the memory of a ncx_enum_t * * INPUTS: * enu == ncx_enum_t struct to init *********************************************************************/ extern void ncx_init_enum (ncx_enum_t *enu); /******************************************************************** * FUNCTION ncx_clean_enum * * Scrub the memory of a ncx_enum_t but do not delete it * * INPUTS: * enu == ncx_enum_t struct to clean *********************************************************************/ extern void ncx_clean_enum (ncx_enum_t *enu); /******************************************************************** * FUNCTION ncx_compare_enums * * Compare 2 enum values * * INPUTS: * enu1 == first ncx_enum_t check * enu2 == second ncx_enum_t check * * RETURNS: * -1 if enu1 is < enu2 * 0 if enu1 == enu2 * 1 if enu1 is > enu2 *********************************************************************/ extern int32 ncx_compare_enums (const ncx_enum_t *enu1, const ncx_enum_t *enu2); /******************************************************************** * FUNCTION ncx_set_enum * * Parse an enumerated integer string into an ncx_enum_t * without matching it against any typdef * * Mallocs a copy of the enum name, using the enu->dname field * * INPUTS: * enumval == enum string value to parse * retenu == pointer to return enuym variable to fill in * * OUTPUTS: * *retenu == enum filled in * * RETURNS: * status *********************************************************************/ extern status_t ncx_set_enum (const xmlChar *enumval, ncx_enum_t *retenu); /******************************************************************** * FUNCTION ncx_init_bit * * Init the memory of a ncx_bit_t * * INPUTS: * bit == ncx_bit_t struct to init *********************************************************************/ extern void ncx_init_bit (ncx_bit_t *bit); /******************************************************************** * FUNCTION ncx_clean_bit * * Scrub the memory of a ncx_bit_t but do not delete it * * INPUTS: * bit == ncx_bit_t struct to clean *********************************************************************/ extern void ncx_clean_bit (ncx_bit_t *bit); /******************************************************************** * FUNCTION ncx_compare_bits * * Compare 2 bit values by their schema order position * * INPUTS: * bitone == first ncx_bit_t check * bitone == second ncx_bit_t check * * RETURNS: * -1 if bitone is < bittwo * 0 if bitone == bittwo * 1 if bitone is > bittwo * *********************************************************************/ extern int32 ncx_compare_bits (const ncx_bit_t *bitone, const ncx_bit_t *bittwo); /******************************************************************** * FUNCTION ncx_new_typname * * Malloc and init a typname struct * * RETURNS: * malloced struct or NULL if memory error *********************************************************************/ extern ncx_typname_t * ncx_new_typname (void); /******************************************************************** * FUNCTION ncx_free_typname * * Free a typname struct * * INPUTS: * typnam == ncx_typname_t struct to free * *********************************************************************/ extern void ncx_free_typname (ncx_typname_t *typnam); /******************************************************************** * FUNCTION ncx_find_typname * * Find a typname struct in the specified Q for a typ pointer * * INPUTS: * que == Q of ncx_typname_t struct to check * typ == matching type template to find * * RETURNS: * name assigned to this type template *********************************************************************/ extern const xmlChar * ncx_find_typname (const typ_template_t *typ, const dlq_hdr_t *que); /******************************************************************** * FUNCTION ncx_find_typname_type * * Find a typ_template_t pointer in a typename mapping, * in the specified Q * * INPUTS: * que == Q of ncx_typname_t struct to check * typname == matching type name to find * * RETURNS: * pointer to the stored typstatus *********************************************************************/ extern const typ_template_t * ncx_find_typname_type (const dlq_hdr_t *que, const xmlChar *typname); /******************************************************************** * FUNCTION ncx_clean_typnameQ * * Delete all the Q entries, of typname mapping structs * * INPUTS: * que == Q of ncx_typname_t struct to delete * *********************************************************************/ extern void ncx_clean_typnameQ (dlq_hdr_t *que); /******************************************************************** * FUNCTION ncx_get_gen_anyxml * * Get the object template for the NCX generic anyxml container * * RETURNS: * pointer to generic anyxml object template *********************************************************************/ extern obj_template_t * ncx_get_gen_anyxml (void); /******************************************************************** * FUNCTION ncx_get_gen_container * * Get the object template for the NCX generic container * * RETURNS: * pointer to generic container object template *********************************************************************/ extern obj_template_t * ncx_get_gen_container (void); /******************************************************************** * FUNCTION ncx_get_gen_string * * Get the object template for the NCX generic string leaf * * RETURNS: * pointer to generic string object template *********************************************************************/ extern obj_template_t * ncx_get_gen_string (void); /******************************************************************** * FUNCTION ncx_get_gen_empty * * Get the object template for the NCX generic empty leaf * * RETURNS: * pointer to generic empty object template *********************************************************************/ extern obj_template_t * ncx_get_gen_empty (void); /******************************************************************** * FUNCTION ncx_get_gen_root * * Get the object template for the NCX generic root container * * RETURNS: * pointer to generic root container object template *********************************************************************/ extern obj_template_t * ncx_get_gen_root (void); /******************************************************************** * FUNCTION ncx_get_gen_binary * * Get the object template for the NCX generic binary leaf * * RETURNS: * pointer to generic binary object template *********************************************************************/ extern obj_template_t * ncx_get_gen_binary (void); /******************************************************************** * FUNCTION ncx_get_layer * * translate ncx_layer_t enum to a string * Get the ncx_layer_t string * * INPUTS: * layer == ncx_layer_t to convert to a string * * RETURNS: * const pointer to the string value *********************************************************************/ extern const xmlChar * ncx_get_layer (ncx_layer_t layer); /******************************************************************** * FUNCTION ncx_get_name_segment * * Get the name string between the dots * * INPUTS: * str == scoped string * buff == address of return buffer * buffsize == buffer size * * OUTPUTS: * buff is filled in with the namestring segment * * RETURNS: * current string pointer after operation *********************************************************************/ extern const xmlChar * ncx_get_name_segment (const xmlChar *str, xmlChar *buff, uint32 buffsize); /******************************************************************** * FUNCTION ncx_get_cvttyp_enum * * Get the enum for the string name of a ncx_cvttyp_t enum * * INPUTS: * str == string name of the enum value * * RETURNS: * enum value *********************************************************************/ extern ncx_cvttyp_t ncx_get_cvttyp_enum (const char *str); /******************************************************************** * FUNCTION ncx_get_status_enum * * Get the enum for the string name of a ncx_status_t enum * * INPUTS: * str == string name of the enum value * * RETURNS: * enum value *********************************************************************/ extern ncx_status_t ncx_get_status_enum (const xmlChar *str); /******************************************************************** * FUNCTION ncx_get_status_string * * Get the string for the enum value of a ncx_status_t enum * * INPUTS: * status == enum value * * RETURNS: * string name of the enum value *********************************************************************/ extern const xmlChar * ncx_get_status_string (ncx_status_t status); /******************************************************************** * FUNCTION ncx_check_yang_status * * Check the backward compatibility of the 2 YANG status fields * * INPUTS: * mystatus == enum value for the node to be tested * depstatus == status value of the dependency * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncx_check_yang_status (ncx_status_t mystatus, ncx_status_t depstatus); /******************************************************************** * FUNCTION ncx_save_descr * * Get the value of the save description strings variable * * RETURNS: * TRUE == descriptive strings should be save * FALSE == descriptive strings should not be saved *********************************************************************/ extern boolean ncx_save_descr (void); /******************************************************************** * FUNCTION ncx_print_errormsg * * Print an parse error message to STDOUT * * INPUTS: * tkc == token chain (may be NULL) * mod == module in progress (may be NULL) * res == error status * * RETURNS: * none *********************************************************************/ extern void ncx_print_errormsg (tk_chain_t *tkc, ncx_module_t *mod, status_t res); /******************************************************************** * FUNCTION ncx_print_errormsg_ex * * Print an parse error message to STDOUT (Extended) * * INPUTS: * tkc == token chain (may be NULL) * mod == module in progress (may be NULL) * res == error status * filename == script finespec * linenum == script file number * fineoln == TRUE if finish with a newline, FALSE if not * * RETURNS: * none *********************************************************************/ extern void ncx_print_errormsg_ex (tk_chain_t *tkc, ncx_module_t *mod, status_t res, const char *filename, uint32 linenum, boolean fineoln); /******************************************************************** * FUNCTION ncx_conf_exp_err * * Print an error for wrong token, expected a different token * * INPUTS: * tkc == token chain * result == error code * expstr == expected token description * *********************************************************************/ extern void ncx_conf_exp_err (tk_chain_t *tkc, status_t result, const char *expstr); /******************************************************************** * FUNCTION ncx_mod_exp_err * * Print an error for wrong token, expected a different token * * INPUTS: * tkc == token chain * mod == module in progress * result == error code * expstr == expected token description * *********************************************************************/ extern void ncx_mod_exp_err (tk_chain_t *tkc, ncx_module_t *mod, status_t result, const char *expstr); /******************************************************************** * FUNCTION ncx_mod_missing_err * * Print an error for wrong token, mandatory * sub-statement is missing * * INPUTS: * tkc == token chain * mod == module in progress * stmtstr == parent statement * expstr == expected sub-statement * *********************************************************************/ extern void ncx_mod_missing_err (tk_chain_t *tkc, ncx_module_t *mod, const char *stmtstr, const char *expstr); /******************************************************************** * FUNCTION ncx_free_node * * Delete a node based on its type * * INPUTS: * nodetyp == NCX node type * node == node top free * *********************************************************************/ extern void ncx_free_node (ncx_node_t nodetyp, void *node); /******************************************************************** * FUNCTION ncx_get_data_class_enum * * Get the enum for the string name of a ncx_data_class_t enum * * INPUTS: * str == string name of the enum value * * RETURNS: * enum value *********************************************************************/ extern ncx_data_class_t ncx_get_data_class_enum (const xmlChar *str); /******************************************************************** * FUNCTION ncx_get_data_class_str * * Get the string value for the ncx_data_class_t enum * * INPUTS: * dataclass == enum value to convert * * RETURNS: * striong value for the enum *********************************************************************/ extern const xmlChar * ncx_get_data_class_str (ncx_data_class_t dataclass); /******************************************************************** * FUNCTION ncx_get_access_str * * Get the string name of a ncx_access_t enum * * INPUTS: * access == enum value * * RETURNS: * string value *********************************************************************/ extern const xmlChar * ncx_get_access_str (ncx_access_t max_access); /******************************************************************** * FUNCTION ncx_get_access_enum * * Get the enum for the string name of a ncx_access_t enum * * INPUTS: * str == string name of the enum value * * RETURNS: * enum value *********************************************************************/ extern ncx_access_t ncx_get_access_enum (const xmlChar *str); /******************************************************************** * FUNCTION ncx_get_tclass * * Get the token class * * INPUTS: * btyp == base type enum * RETURNS: * tclass enum *********************************************************************/ extern ncx_tclass_t ncx_get_tclass (ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_get_tclass * * Get the token class * * INPUTS: * btyp == base type enum * RETURNS: * tclass enum *********************************************************************/ extern boolean ncx_valid_name_ch (uint32 ch); /******************************************************************** * FUNCTION ncx_valid_fname_ch * * Check if an xmlChar is a valid NCX name string first char * * INPUTS: * ch == xmlChar to check * RETURNS: * TRUE if a valid first name char, FALSE otherwise *********************************************************************/ extern boolean ncx_valid_fname_ch (uint32 ch); /******************************************************************** * FUNCTION ncx_valid_name * * Check if an xmlChar string is a valid YANG identifier value * * INPUTS: * str == xmlChar string to check * len == length of the string to check (in case of substr) * RETURNS: * TRUE if a valid name string, FALSE otherwise *********************************************************************/ extern boolean ncx_valid_name (const xmlChar *str, uint32 len); /******************************************************************** * FUNCTION ncx_valid_name2 * * Check if an xmlChar string is a valid NCX name * * INPUTS: * str == xmlChar string to check (zero-terminated) * RETURNS: * TRUE if a valid name string, FALSE otherwise *********************************************************************/ extern boolean ncx_valid_name2 (const xmlChar *str); /******************************************************************** * FUNCTION ncx_parse_name * * Check if the next N chars represent a valid NcxName * Will end on the first non-name char * * INPUTS: * str == xmlChar string to check * len == address of name length * * OUTPUTS: * *len == 0 if no valid name parsed * > 0 for the numbers of chars in the NcxName * * RETURNS: * status_t (error if name too long) *********************************************************************/ extern status_t ncx_parse_name (const xmlChar *str, uint32 *len); /******************************************************************** * FUNCTION ncx_is_true * * Check if an xmlChar string is a string OK for XSD boolean * * INPUTS: * str == xmlChar string to check * * RETURNS: * TRUE if a valid boolean value indicating true * FALSE otherwise *********************************************************************/ extern boolean ncx_is_true (const xmlChar *str); /******************************************************************** * FUNCTION ncx_is_false * * Check if an xmlChar string is a string OK for XSD boolean * * INPUTS: * str == xmlChar string to check * * RETURNS: * TRUE if a valid boolean value indicating false * FALSE otherwise *********************************************************************/ extern boolean ncx_is_false (const xmlChar *str); /******************************************************************** * FUNCTION ncx_consume_tstring * * Consume a TK_TT_TSTRING with the specified value * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress (NULL if none) * name == token name * opt == TRUE for optional param * == FALSE for mandatory param * RETURNS: * status of the operation *********************************************************************/ extern status_t ncx_consume_tstring (tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *name, ncx_opt_t opt); /******************************************************************** * FUNCTION ncx_consume_name * * Consume a TK_TSTRING that matches the 'name', then * retrieve the next TK_TSTRING token into the namebuff * If ctk specified, then consume the specified close token * * Store the results in a malloced buffer * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress (NULL if none) * name == first token name * namebuff == ptr to output name string * opt == NCX_OPT for optional param * == NCX_REQ for mandatory param * ctyp == close token (use TK_TT_NONE to skip this part) * * OUTPUTS: * *namebuff points at the malloced name string * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncx_consume_name (tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *name, xmlChar **namebuff, ncx_opt_t opt, tk_type_t ctyp); /******************************************************************** * FUNCTION ncx_consume_token * * Consume the next token which should be a 1 or 2 char token * without any value. However this function does not check the value, * just the token type. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress (NULL if none) * ttyp == token type * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncx_consume_token (tk_chain_t *tkc, ncx_module_t *mod, tk_type_t ttyp); /******************************************************************** * FUNCTION ncx_new_errinfo * * Malloc and init a new ncx_errinfo_t * * RETURNS: * pointer to malloced ncx_errinfo_t, or NULL if memory error *********************************************************************/ extern ncx_errinfo_t * ncx_new_errinfo (void); /******************************************************************** * FUNCTION ncx_init_errinfo * * Init the fields in an ncx_errinfo_t struct * * INPUTS: * err == ncx_errinfo_t data structure to init *********************************************************************/ extern void ncx_init_errinfo (ncx_errinfo_t *err); /******************************************************************** * FUNCTION ncx_clean_errinfo * * Scrub the memory in a ncx_errinfo_t by freeing all * the sub-fields * * INPUTS: * err == ncx_errinfo_t data structure to clean *********************************************************************/ extern void ncx_clean_errinfo (ncx_errinfo_t *err); /******************************************************************** * FUNCTION ncx_free_errinfo * * Scrub the memory in a ncx_errinfo_t by freeing all * the sub-fields, then free the errinfo struct * * INPUTS: * err == ncx_errinfo_t data structure to free *********************************************************************/ extern void ncx_free_errinfo (ncx_errinfo_t *err); /******************************************************************** * FUNCTION ncx_errinfo_set * * Check if error-app-tag or error-message set * Check if the errinfo struct is set or empty * Checks only the error_app_tag and error_message fields * * INPUTS: * errinfo == ncx_errinfo_t struct to check * * RETURNS: * TRUE if at least one field set * FALSE if the errinfo struct is empty *********************************************************************/ extern boolean ncx_errinfo_set (const ncx_errinfo_t *errinfo); /******************************************************************** * FUNCTION ncx_copy_errinfo * * Copy the fields from one errinfo to a blank errinfo * * INPUTS: * src == struct with starting contents * dest == struct to get copy of src contents * * OUTPUTS: * *dest fields set which are set in src * * RETURNS: * status *********************************************************************/ extern status_t ncx_copy_errinfo (const ncx_errinfo_t *src, ncx_errinfo_t *dest); /******************************************************************** * FUNCTION ncx_get_source_ex * * Get a malloced buffer containing the complete filespec * for the given input string. If this is a complete dirspec, * this this will just strdup the value. * * This is just a best effort to get the full spec. * If the full spec is greater than 1500 bytes, * then a NULL value (error) will be returned * * - Change ./ --> cwd/ * - Remove ~/ --> $HOME * - add trailing '/' if not present * * INPUTS: * fspec == input filespec * expand_cwd == TRUE if foo should be expanded to /cur/dir/foo * FALSE if not * res == address of return status * * OUTPUTS: * *res == return status, NO_ERR if return is non-NULL * * RETURNS: * malloced buffer containing possibly expanded full filespec *********************************************************************/ extern xmlChar * ncx_get_source_ex (const xmlChar *fspec, boolean expand_cwd, status_t *res); /******************************************************************** * FUNCTION ncx_get_source * * Get a malloced buffer containing the complete filespec * for the given input string. If this is a complete dirspec, * this this will just strdup the value. * * This is just a best effort to get the full spec. * If the full spec is greater than 1500 bytes, * then a NULL value (error) will be returned * * This will expand the cwd! * * - Change ./ --> cwd/ * - Remove ~/ --> $HOME * - add trailing '/' if not present * * INPUTS: * fspec == input filespec * res == address of return status * * OUTPUTS: * *res == return status, NO_ERR if return is non-NULL * * RETURNS: * malloced buffer containing possibly expanded full filespec *********************************************************************/ extern xmlChar * ncx_get_source (const xmlChar *fspec, status_t *res); /******************************************************************** * FUNCTION ncx_set_cur_modQ * * Set the current module Q to an alternate (for yangdiff) * This will be used for module searches usually in ncx_modQ * * INPUTS: * que == Q of ncx_module_t to use *********************************************************************/ extern void ncx_set_cur_modQ (dlq_hdr_t *que); /******************************************************************** * FUNCTION ncx_get_cur_modQ * * Get the current module Q * This will be used for module searches usually in ncx_modQ * * INPUTS: * que == Q of ncx_module_t to use *********************************************************************/ dlq_hdr_t * ncx_get_cur_modQ (void); /******************************************************************** * FUNCTION ncx_reset_modQ * * Set the current module Q to the original ncx_modQ * *********************************************************************/ extern void ncx_reset_modQ (void); /******************************************************************** * FUNCTION ncx_set_session_modQ * * !!! THIS HACK IS NEEDED BECAUSE val.c * !!! USES ncx_find_module sometimes, and * !!! yangcli sessions are not loaded into the * !!! main database of modules. * !!! THIS DOES NOT WORK FOR MULTIPLE CONCURRENT PROCESSES * * Set the current session module Q to an alternate (for yangdiff) * This will be used for module searches usually in ncx_modQ * * INPUTS: * que == Q of ncx_module_t to use *********************************************************************/ extern void ncx_set_session_modQ (dlq_hdr_t *que); /******************************************************************** * FUNCTION ncx_clear_session_modQ * * !!! THIS HACK IS NEEDED BECAUSE val.c * !!! USES ncx_find_module sometimes, and * !!! yangcli sessions are not loaded into the * !!! main database of modules. * !!! THIS DOES NOT WORK FOR MULTIPLE CONCURRENT PROCESSES * * Clear the current session module Q * *********************************************************************/ extern void ncx_clear_session_modQ (void); /******************************************************************** * FUNCTION ncx_set_load_callback * * Set the callback function for a load-module event * * INPUT: * cbfn == callback function to use * *********************************************************************/ extern void ncx_set_load_callback (ncx_load_cbfn_t cbfn); /******************************************************************** * FUNCTION ncx_prefix_different * * Check if the specified prefix pair reference different modules * * INPUT: * prefix1 == 1st prefix to check (may be NULL) * prefix2 == 2nd prefix to check (may be NULL) * modprefix == module prefix to check (may be NULL) * * RETURNS: * TRUE if prefix1 and prefix2 reference different modules * FALSE if prefix1 and prefix2 reference the same modules *********************************************************************/ extern boolean ncx_prefix_different (const xmlChar *prefix1, const xmlChar *prefix2, const xmlChar *modprefix); /******************************************************************** * FUNCTION ncx_get_baddata_enum * * Check if the specified string matches an ncx_baddata_t enum * * INPUT: * valstr == value string to check * * RETURNS: * enum value if OK * NCX_BAD_DATA_NONE if an error *********************************************************************/ extern ncx_bad_data_t ncx_get_baddata_enum (const xmlChar *valstr); /******************************************************************** * FUNCTION ncx_get_baddata_string * * Get the string for the specified enum value * * INPUT: * baddatar == enum value to check * * RETURNS: * string pointer if OK * NULL if an error *********************************************************************/ extern const xmlChar * ncx_get_baddata_string (ncx_bad_data_t baddata); /******************************************************************** * FUNCTION ncx_get_withdefaults_string * * Get the string for the specified enum value * * INPUT: * withdef == enum value to check * * RETURNS: * string pointer if OK * NULL if an error *********************************************************************/ extern const xmlChar * ncx_get_withdefaults_string (ncx_withdefaults_t withdef); /******************************************************************** * FUNCTION ncx_get_withdefaults_enum * * Get the enum for the specified string value * * INPUT: * withdefstr == string value to check * * RETURNS: * enum value for the string * NCX_WITHDEF_NONE if invalid value *********************************************************************/ extern ncx_withdefaults_t ncx_get_withdefaults_enum (const xmlChar *withdefstr); /******************************************************************** * FUNCTION ncx_get_mod_prefix * * Get the module prefix for the specified module * * INPUT: * mod == module to check * * RETURNS: * pointer to module YANG prefix *********************************************************************/ extern const xmlChar * ncx_get_mod_prefix (const ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_mod_xmlprefix * * Get the module XML prefix for the specified module * * INPUT: * mod == module to check * * RETURNS: * pointer to module XML prefix *********************************************************************/ extern const xmlChar * ncx_get_mod_xmlprefix (const ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_display_mode_enum * * Get the enum for the specified string value * * INPUT: * dmstr == string value to check * * RETURNS: * enum value for the string * NCX_DISPLAY_MODE_NONE if invalid value *********************************************************************/ extern ncx_display_mode_t ncx_get_display_mode_enum (const xmlChar *dmstr); /******************************************************************** * FUNCTION ncx_get_display_mode_str * * Get the string for the specified enum value * * INPUT: * dmode == enum display mode value to check * * RETURNS: * string value for the enum * NULL if none found *********************************************************************/ extern const xmlChar * ncx_get_display_mode_str (ncx_display_mode_t dmode); /******************************************************************** * FUNCTION ncx_set_warn_idlen * * Set the warning length for identifiers * * INPUT: * warnlen == warning length to use * *********************************************************************/ extern void ncx_set_warn_idlen (uint32 warnlen); /******************************************************************** * FUNCTION ncx_get_warn_idlen * * Get the warning length for identifiers * * RETURNS: * warning length to use *********************************************************************/ extern uint32 ncx_get_warn_idlen (void); /******************************************************************** * FUNCTION ncx_set_warn_linelen * * Set the warning length for YANG file lines * * INPUT: * warnlen == warning length to use * *********************************************************************/ extern void ncx_set_warn_linelen (uint32 warnlen); /******************************************************************** * FUNCTION ncx_get_warn_linelen * * Get the warning length for YANG file lines * * RETURNS: * warning length to use * *********************************************************************/ extern uint32 ncx_get_warn_linelen (void); /******************************************************************** * FUNCTION ncx_check_warn_idlen * * Check if the identifier length is greater than * the specified amount. * * INPUTS: * tkc == token chain to use (for warning message only) * mod == module (for warning message only) * id == identifier string to check; must be Z terminated * * OUTPUTS: * may generate log_warn ouput *********************************************************************/ extern void ncx_check_warn_idlen (tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *id); /******************************************************************** * FUNCTION ncx_check_warn_linelen * * Check if the line display length is greater than * the specified amount. * * INPUTS: * tkc == token chain to use * mod == module (used in warning message only * linelen == line length to check * * OUTPUTS: * may generate log_warn ouput *********************************************************************/ extern void ncx_check_warn_linelen (tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *line); /******************************************************************** * FUNCTION ncx_turn_off_warning * * Add ar warning suppression entry * * INPUTS: * res == internal status code to suppress * * RETURNS: * status (duplicates are silently dropped) *********************************************************************/ extern status_t ncx_turn_off_warning (status_t res); /******************************************************************** * FUNCTION ncx_turn_on_warning * * Remove a warning suppression entry if it exists * * INPUTS: * res == internal status code to enable * * RETURNS: * status (duplicates are silently dropped) *********************************************************************/ extern status_t ncx_turn_on_warning (status_t res); /******************************************************************** * FUNCTION ncx_warning_enabled * * Check if a specific status_t code is enabled * * INPUTS: * res == internal status code to check * * RETURNS: * TRUE if warning is enabled * FALSE if warning is suppressed *********************************************************************/ extern boolean ncx_warning_enabled (status_t res); /******************************************************************** * FUNCTION ncx_get_version * * Get the the Yuma version ID string * * INPUT: * buffer == buffer to hold the version string * buffsize == number of bytes in buffer * * RETURNS: * status *********************************************************************/ extern status_t ncx_get_version (xmlChar *buffer, uint32 buffsize); /******************************************************************** * FUNCTION ncx_new_save_deviations * * create a deviation save structure * * INPUTS: * devmodule == deviations module name * devrevision == deviation module revision (optional) * devnamespace == deviation module namespace URI value * devprefix == local module prefix (optional) * * RETURNS: * malloced and initialized save_deviations struct, * or NULL if malloc error *********************************************************************/ extern ncx_save_deviations_t * ncx_new_save_deviations (const xmlChar *devmodule, const xmlChar *devrevision, const xmlChar *devnamespace, const xmlChar *devprefix); /******************************************************************** * FUNCTION ncx_free_save_deviations * * free a deviation save struct * * INPUT: * savedev == struct to clean and delete *********************************************************************/ extern void ncx_free_save_deviations (ncx_save_deviations_t *savedev); /******************************************************************** * FUNCTION ncx_clean_save_deviationsQ * * clean a Q of deviation save structs * * INPUT: * savedevQ == Q of ncx_save_deviations_t to clean *********************************************************************/ extern void ncx_clean_save_deviationsQ (dlq_hdr_t *savedevQ); /******************************************************************** * FUNCTION ncx_set_error * * Set the fields in an ncx_error_t struct * When called from NACM or internally, there is no * module or line number info * * INPUTS: * tkerr== address of ncx_error_t struct to set * mod == [sub]module containing tkerr * linenum == current linenum * linepos == current column position on the current line * * OUTPUTS: * *tkerr is filled in *********************************************************************/ extern void ncx_set_error (ncx_error_t *tkerr, ncx_module_t *mod, uint32 linenum, uint32 linepos); /******************************************************************** * FUNCTION ncx_set_temp_modQ * * Set the temp_modQ for yangcli session-specific module list * * INPUTS: * modQ == new Q pointer to use * *********************************************************************/ extern void ncx_set_temp_modQ (dlq_hdr_t *modQ); /******************************************************************** * FUNCTION ncx_get_temp_modQ * * Get the temp_modQ for yangcli session-specific module list * * RETURNS: * pointer to the temp modQ, if set *********************************************************************/ extern dlq_hdr_t * ncx_get_temp_modQ (void); /******************************************************************** * FUNCTION ncx_clear_temp_modQ * * Clear the temp_modQ for yangcli session-specific module list * *********************************************************************/ extern void ncx_clear_temp_modQ (void); /******************************************************************** * FUNCTION ncx_get_display_mode * * Get the current default display mode * * RETURNS: * the current dispay mode enumeration *********************************************************************/ extern ncx_display_mode_t ncx_get_display_mode (void); /******************************************************************** * FUNCTION ncx_get_confirm_event_str * * Get the string for the specified enum value * * INPUT: * event == enum confirm event value to convert * * RETURNS: * string value for the enum * NULL if none found *********************************************************************/ extern const xmlChar * ncx_get_confirm_event_str (ncx_confirm_event_t event); /******************************************************************** * FUNCTION ncx_mod_revision_count * * Find all the ncx_module_t structs in the ncx_modQ * that have the same module name * * INPUTS: * modname == module name * * RETURNS: * count of modules that have this name (exact match) *********************************************************************/ extern uint32 ncx_mod_revision_count (const xmlChar *modname); /******************************************************************** * FUNCTION ncx_mod_revision_count_que * * Find all the ncx_module_t structs in the specified queue * that have the same module name * * INPUTS: * modQ == queue of ncx_module_t structs to check * modname == module name * * RETURNS: * count of modules that have this name (exact match) *********************************************************************/ extern uint32 ncx_mod_revision_count_que (dlq_hdr_t *modQ, const xmlChar *modname); /******************************************************************** * FUNCTION ncx_get_allincQ * * Find the correct Q of yang_node_t for all include files * that have the same 'belongs-to' value * * INPUTS: * mod == module to check * * RETURNS: * pointer to Q of all include nodes *********************************************************************/ extern dlq_hdr_t * ncx_get_allincQ (ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_const_allincQ * * Find the correct Q of yang_node_t for all include files * that have the same 'belongs-to' value (const version) * * INPUTS: * mod == module to check * * RETURNS: * pointer to Q of all include nodes *********************************************************************/ extern const dlq_hdr_t * ncx_get_const_allincQ (const ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_parent_mod * * Find the correct module by checking mod->parent nodes * * INPUTS: * mod == module to check * * RETURNS: * pointer to parent module (!! not submodule !!) * NULL if none found *********************************************************************/ extern ncx_module_t * ncx_get_parent_mod (ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_vtimeout_value * * Get the virtual node cache timeout value * * RETURNS: * number of seconds for the cache timeout; 0 == disabled *********************************************************************/ extern uint32 ncx_get_vtimeout_value (void); /******************************************************************** * FUNCTION ncx_compare_base_uris * * Compare the base part of 2 URI strings * * INPUTS: * str1 == URI string 1 * str2 == URI string 2 * * RETURNS: * compare of base parts (up to '?') * -1, 0 or 1 *********************************************************************/ extern int32 ncx_compare_base_uris (const xmlChar *str1, const xmlChar *str2); /******************************************************************** * FUNCTION ncx_get_useprefix * * Get the use_prefix value * * RETURNS: * TRUE if XML prefixes should be used * FALSE if XML messages should use default namespace (no prefix) *********************************************************************/ extern boolean ncx_get_useprefix (void); /******************************************************************** * FUNCTION ncx_set_useprefix * * Set the use_prefix value * * INPUTS: * val == * TRUE if XML prefixes should be used * FALSE if XML messages should use default namespace (no prefix) *********************************************************************/ extern void ncx_set_useprefix (boolean val); /******************************************************************** * FUNCTION ncx_get_system_sorted * * Get the system_sorted value * * RETURNS: * TRUE if system ordered objects should be sorted * FALSE if system ordered objects should not be sorted *********************************************************************/ extern boolean ncx_get_system_sorted (void); /******************************************************************** * FUNCTION ncx_set_system_sorted * * Set the system_sorted value * * INPUTS: * val == * TRUE if system ordered objects should be sorted * FALSE if system ordered objects should not be sorted *********************************************************************/ extern void ncx_set_system_sorted (boolean val); /******************************************************************** * FUNCTION ncx_inc_warnings * * Increment the module warning count * * INPUTS: * mod == module being parsed *********************************************************************/ extern void ncx_inc_warnings (ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_cwd_subdirs * * Get the CLI parameter value whether to search for modules * in subdirs of the CWD by default. Does not affect YUMA_MODPATH * or other hard-wired searches * * RETURNS: * TRUE if ncxmod should search for modules in subdirs of the CWD * FALSE if ncxmod should not search for modules in subdirs of the CWD *********************************************************************/ extern boolean ncx_get_cwd_subdirs (void); /******************************************************************** * FUNCTION ncx_protocol_enabled * * Check if the specified protocol version is enabled * * RETURNS: * TRUE if protocol enabled * FALSE if protocol not enabled or error *********************************************************************/ extern boolean ncx_protocol_enabled (ncx_protocol_t proto); /******************************************************************** * FUNCTION ncx_set_protocol_enabled * * Set the specified protocol version to be enabled * * INPUTS: * proto == protocol version to enable *********************************************************************/ extern void ncx_set_protocol_enabled (ncx_protocol_t proto); /******************************************************************** * FUNCTION ncx_set_use_deadmodQ * * Set the usedeadmodQ flag * *********************************************************************/ extern void ncx_set_use_deadmodQ (void); /******************************************************************** * FUNCTION ncx_delete_all_obsolete_objects * * Go through all the modules and delete the obsolete nodes * *********************************************************************/ extern void ncx_delete_all_obsolete_objects (void); /******************************************************************** * FUNCTION ncx_delete_mod_obsolete_objects * * Go through one module and delete the obsolete nodes * * INPUTS: * mod == module to check *********************************************************************/ extern void ncx_delete_mod_obsolete_objects (ncx_module_t *mod); /******************************************************************** * FUNCTION ncx_get_name_match_enum * * Get the enum for the string name of a ncx_name_match_t enum * * INPUTS: * str == string name of the enum value * * RETURNS: * enum value *********************************************************************/ extern ncx_name_match_t ncx_get_name_match_enum (const xmlChar *str); /******************************************************************** * FUNCTION ncx_get_name_match_string * * Get the string for the ncx_name_match_t enum * * INPUTS: * match == enum value * * RETURNS: * string value *********************************************************************/ extern const xmlChar * ncx_get_name_match_string (ncx_name_match_t match); /******************************************************************** * FUNCTION ncx_write_tracefile * * Write a byte to the tracefile * * INPUTS: * buff == buffer to write * count == number of chars to write *********************************************************************/ extern void ncx_write_tracefile (const char *buff, uint32 count); /******************************************************************** * FUNCTION ncx_set_top_mandatory_allowed * * Allow or disallow modules with top-level mandatory object * to be loaded; used by the server when agt_running_error is TRUE * * INPUTS: * allowed == value to set T: to allow; F: to disallow *********************************************************************/ extern void ncx_set_top_mandatory_allowed (boolean allowed); /******************************************************************** * FUNCTION ncx_get_top_mandatory_allowed * * Check if top-level mandatory objects are allowed or not * * RETURNS: * T: allowed; F: disallowed *********************************************************************/ extern boolean ncx_get_top_mandatory_allowed (void); #ifdef __cplusplus } /* end extern 'C' */ #endif /******************************************************************** * FUNCTION identity_get_first_iffeature * * Get the first if-feature clause (if any) for the specified identity * * INPUTS: * identity == identity structure to check * * RETURNS: * pointer to first if-feature struct * NULL if none available *********************************************************************/ const ncx_iffeature_t * identity_get_first_iffeature (const ncx_identity_t *identity); /******************************************************************** * FUNCTION identity_get_next_iffeature * * Get the next if-feature clause (if any) * * INPUTS: * iffeature == current iffeature struct * * RETURNS: * pointer to next if-feature struct * NULL if none available *********************************************************************/ const ncx_iffeature_t * identity_get_next_iffeature (const ncx_iffeature_t *iffeature); /******************************************************************** * FUNCTION identity_is_enabled * * Check any if-feature statement that may * cause the specified identity to be invisible * * INPUTS: * identity == ncx_identity_t to check * RETURNS: * TRUE if identity is enabled * FALSE if any if-features are present and FALSE *********************************************************************/ extern boolean identity_is_enabled (const ncx_identity_t *identity); #endif /* _H_ncx */ yuma123_2.14/netconf/src/ncx/ncx_feature.h0000664000175000017500000003244114770023131020616 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ncx_feature #define _H_ncx_feature /* FILE: ncx_feature.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Module Library YANG Feature Utility Functions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17-feb-10 abb Begun; split out from ncx.c */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION ncx_feature_init * * Init the ncx_feature module * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void ncx_feature_init (void); /******************************************************************** * FUNCTION ncx_feature_cleanup * * Cleanup the ncx_feature module * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void ncx_feature_cleanup (void); /******************************************************************** * FUNCTION ncx_new_iffeature * * Get a new ncx_iffeature_t struct * * INPUTS: * none * RETURNS: * pointer to a malloced ncx_iffeature_t struct, * or NULL if malloc error *********************************************************************/ extern ncx_iffeature_t * ncx_new_iffeature (void); /******************************************************************** * FUNCTION ncx_free_iffeature * * Free a malloced ncx_iffeature_t struct * * INPUTS: * iff == struct to free * *********************************************************************/ extern void ncx_free_iffeature (ncx_iffeature_t *iffeature); /******************************************************************** * FUNCTION ncx_clone_iffeature * * Clone a new ncx_iffeature_t struct * * INPUTS: * srciff == ifffeature struct to clone * RETURNS: * pointer to a malloced ncx_iffeature_t struct, * or NULL if malloc error *********************************************************************/ extern ncx_iffeature_t * ncx_clone_iffeature (ncx_iffeature_t *srciff); /******************************************************************** * FUNCTION ncx_clean_iffeatureQ * * Clean a Q of malloced ncx_iffeature_t struct * * INPUTS: * iffeatureQ == address of Q to clean * *********************************************************************/ extern void ncx_clean_iffeatureQ (dlq_hdr_t *iffeatureQ); /******************************************************************** * FUNCTION ncx_find_iffeature * * Search a Q of ncx_iffeature_t structs for a match * * INPUTS: * iffeatureQ == address of Q to search * prefix == prefix to check for * a NULL value indicates the current module * name == feature name string to find *********************************************************************/ extern ncx_iffeature_t * ncx_find_iffeature (dlq_hdr_t *iffeatureQ, const xmlChar *prefix, const xmlChar *name, const xmlChar *modprefix); /******************************************************************** * FUNCTION ncx_find_iffeature_1dot1 * * Search a Q of ncx_iffeature_t structs for a match * expr is an alternative introduced in YANG 1.1 * with logical expression with or,and and not * * INPUTS: * iffeatureQ == address of Q to search * prefix == prefix to check for * a NULL value indicates the current module * name == feature name string to find * expr == feature expr string to find * a NULL value indicates simple if-feature without logical expression * when not NULL prefix and name are NULL *********************************************************************/ extern ncx_iffeature_t * ncx_find_iffeature_1dot1 (dlq_hdr_t *iffeatureQ, const xmlChar *prefix, const xmlChar *name, const xmlChar *expr, const xmlChar *modprefix); /******************************************************************** * FUNCTION ncx_resolve_iffeatureQ * * Check the Q of if-feature statements for the specified object * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * obj == object to check * * RETURNS: * status of the operation *********************************************************************/ status_t ncx_resolve_iffeatureQ (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *name, dlq_hdr_t *iffeatureQ); /******************************************************************** * FUNCTION ncx_new_feature * * Get a new ncx_feature_t struct * * INPUTS: * none * RETURNS: * pointer to a malloced ncx_feature_t struct, * or NULL if malloc error *********************************************************************/ extern ncx_feature_t * ncx_new_feature (void); /******************************************************************** * FUNCTION ncx_free_feature * * Free a malloced ncx_feature_t struct * * INPUTS: * feature == struct to free * *********************************************************************/ extern void ncx_free_feature (ncx_feature_t *feature); /******************************************************************** * FUNCTION ncx_find_feature * * Find a ncx_feature_t struct in the module and perhaps * any of its submodules * * INPUTS: * mod == module to search * name == feature name to find * * RETURNS: * pointer to found feature or NULL if not found *********************************************************************/ extern ncx_feature_t * ncx_find_feature (ncx_module_t *mod, const xmlChar *name); /******************************************************************** * FUNCTION ncx_find_feature_que * * Find a ncx_feature_t struct in the specified Q * * INPUTS: * featureQ == Q of ncx_feature_t to search * name == feature name to find * * RETURNS: * pointer to found feature or NULL if not found *********************************************************************/ extern ncx_feature_t * ncx_find_feature_que (dlq_hdr_t *featureQ, const xmlChar *name); /******************************************************************** * FUNCTION ncx_find_feature_all * * Find a ncx_feature_t struct in the module and perhaps * any of its submodules * * INPUTS: * mod == module to search * name == feature name to find * * RETURNS: * pointer to found feature or NULL if not found *********************************************************************/ extern ncx_feature_t * ncx_find_feature_all (ncx_module_t *mod, const xmlChar *name); /******************************************************************** * FUNCTION ncx_for_all_features * * Execute a callback function for all features in this module * and any submodules * * INPUTS: * mod == module to search for features * cbfn == feature callback function * cookie == cookie value to pass to each iteration of the callback * enabledonly == TRUE if only callbacks for enabled features * FALSE if all features should invoke callbacks *********************************************************************/ extern void ncx_for_all_features (const ncx_module_t *mod, ncx_feature_cbfn_t cbfn, void *cookie, boolean enabledonly); /******************************************************************** * FUNCTION ncx_feature_count * * Get the total feature count for this module * and any submodules * * INPUTS: * mod == module to search for features * enabledonly == TRUE to only count enabled features * FALSE to count all features * * RETURNS: * total number of features *********************************************************************/ extern uint32 ncx_feature_count (const ncx_module_t *mod, boolean enabledonly); /******************************************************************** * FUNCTION ncx_feature_enabled * * Check if the specified feature and any referenced * if-features are enabled * * INPUTS: * feature == feature to check * * RETURNS: * TRUE if feature is completely enabled * FALSE if feature is not enabled, or partially enabled *********************************************************************/ extern boolean ncx_feature_enabled (const ncx_feature_t *feature); /******************************************************************** * FUNCTION ncx_feature_enabled_str * * Find a ncx_feature_t struct and check if it is enabled * * INPUTS: * modname == name of module to search * revision == module revision string (may be NULL) * name == feature name to find * * RETURNS: * TRUE if feature is enabled *********************************************************************/ extern boolean ncx_feature_enabled_str (const xmlChar *modname, const xmlChar *revision, const xmlChar *name); /******************************************************************** * FUNCTION ncx_set_feature_enable_default * * Set the feature_enable_default flag * * INPUTS: * flag == feature enabled flag value *********************************************************************/ extern void ncx_set_feature_enable_default (boolean flag); /******************************************************************** * FUNCTION ncx_set_feature_code_default * * Set the feature_code_default enumeration * * !!! THIS FUNCTION IS DEPRECATED!!! * !!! The --feature-code and --feature-code-default parameters are ignored * !!! Feature code generation is not controlled by this parameter * * INPUTS: * code == feature code value *********************************************************************/ extern void ncx_set_feature_code_default (ncx_feature_code_t code); /******************************************************************** * FUNCTION ncx_set_feature_code_entry * * Create or set a feature_entry struct for the specified * feature code parameter * * !!! THIS FUNCTION IS DEPRECATED!!! * !!! The --feature-code and --feature-code-default parameters are ignored * !!! Feature code generation is not controlled by this parameter * * INPUTS: * featstr == feature parameter string * featcode == ncx_feature_code_t enumeration to set * * RETURNS: * status *********************************************************************/ extern status_t ncx_set_feature_code_entry (const xmlChar *featstr, ncx_feature_code_t featcode); /******************************************************************** * FUNCTION ncx_set_feature_enable_entry * * Create or set a feature_entry struct for the specified * feature enabled parameter * * Called from CLI/conf handler code * * INPUTS: * featstr == feature parameter string * flag == enabled flag * * RETURNS: * status *********************************************************************/ extern status_t ncx_set_feature_enable_entry (const xmlChar *featstr, boolean flag); /******************************************************************** * FUNCTION ncx_set_feature_enable * * Create or set a feature_entry struct for the specified * feature enabled parameter * * Called from SIL init code * * INPUTS: * modname == name of module defining the feature * name == feature name * flag == feature enabled flag * * RETURNS: * status *********************************************************************/ extern status_t ncx_set_feature_enable (const xmlChar *modname, const xmlChar *name, boolean flag); /******************************************************************** * FUNCTION ncx_set_feature_parms * * Check if any feature parameters were set for the specified * feature struct * * INPUTS: * feature == feature struct to check * * OUTPUTS: * feature->code and/or feature->enabled may be set *********************************************************************/ extern void ncx_set_feature_parms (ncx_feature_t *feature); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_ncx_feature */ yuma123_2.14/netconf/src/ncx/yang_ext.h0000664000175000017500000000452214770023131020130 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yang_ext #define _H_yang_ext /* FILE: yang_ext.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* YANG Module parser extension statement support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 05-jan-08 abb Begun; start from yang_grp.h */ #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION yang_ext_consume_extension * * Parse the next N tokens as an extension-stmt * Create an ext_template_t struct and add it to the specified Q * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'extension' keyword * * INPUTS: * tkc == token chain * mod == module in progress * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_ext_consume_extension (tk_chain_t *tkc, ncx_module_t *mod); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yang_ext */ yuma123_2.14/netconf/src/ncx/top.c0000664000175000017500000002020314770023131017101 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: top.c NCX Common Top Element Handler This module uses a simple queue of top-level entries because there are not likely to be very many of them. Each top-level node is keyed by the owner name and the element name. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30dec05 abb begun 11-feb-07 abb Move some common fns back to ncx/top.h ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_top #include "top.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define TOP_DEBUG 1 #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* callback registry entry for (owner, element-name) pairs */ typedef struct top_entry_t_ { dlq_hdr_t qhdr; /* Q header for topQ */ xmlChar *owner; xmlChar *elname; top_handler_t handler; } top_entry_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean top_init_done = FALSE; static dlq_hdr_t topQ; /******************************************************************** * FUNCTION find_entry * * Find an entry in the topQ * * INPUTS: * owner == owner name * elname == element name * RETURNS: * pointer to top_entry_t if found, NULL if not found *********************************************************************/ static top_entry_t * find_entry (const xmlChar *owner, const xmlChar *elname) { top_entry_t *en; for (en = (top_entry_t *)dlq_firstEntry(&topQ); en != NULL; en = (top_entry_t *)dlq_nextEntry(en)) { if (!xml_strcmp(en->owner, owner) && !xml_strcmp(en->elname, elname)) { return en; } } return NULL; } /* find_entry */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION top_init * * Initialize the Top Elelment Handler * *********************************************************************/ void top_init (void) { if (!top_init_done) { dlq_createSQue(&topQ); top_init_done = TRUE; } } /* top_init */ /******************************************************************** * FUNCTION top_cleanup * * cleanup Top Element Handler *********************************************************************/ void top_cleanup (void) { top_entry_t *en; if (top_init_done) { while (!dlq_empty(&topQ)) { en = (top_entry_t *)dlq_deque(&topQ); m__free(en); } memset(&topQ, 0x0, sizeof(dlq_hdr_t)); top_init_done = FALSE; } } /* top_cleanup */ /******************************************************************** * FUNCTION top_register_node * * Register a top entry handler function * * INPUTS: * owner == owner name (really module name) * elname == element name * handler == callback function for this element * RETURNS: * status *********************************************************************/ status_t top_register_node (const xmlChar *owner, const xmlChar *elname, top_handler_t handler) { top_entry_t *en; /* validate the parameters */ if (!top_init_done) { top_init(); } if (!owner || !elname || !handler) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!ncx_valid_name(owner, xml_strlen(owner)) || !ncx_valid_name(elname, xml_strlen(elname))) { return SET_ERROR(ERR_NCX_INVALID_NAME); } if (find_entry(owner, elname)) { return SET_ERROR(ERR_NCX_DUP_ENTRY); } /* add the new entry */ en = m__getObj(top_entry_t); if (!en) { return SET_ERROR(ERR_INTERNAL_MEM); } memset(en, 0x0, sizeof(top_entry_t)); en->owner = xml_strdup(owner); if (!en->owner) { m__free(en); return SET_ERROR(ERR_INTERNAL_MEM); } en->elname = xml_strdup(elname); if (!en->elname) { m__free(en->owner); m__free(en); return SET_ERROR(ERR_INTERNAL_MEM); } en->handler = handler; dlq_enque(en, &topQ); return NO_ERR; } /* top_register_node */ /******************************************************************** * FUNCTION top_unregister_node * * Remove a top entry handler function * This will not affect calls to the handler currently * in progress, but new calls to top_dispatch_msg * will no longer find this node. * * INPUTS: * owner == owner name * elname == element name * RETURNS: * none *********************************************************************/ void top_unregister_node (const xmlChar *owner, const xmlChar *elname) { top_entry_t *en; if (!top_init_done) { top_init(); SET_ERROR(ERR_NCX_NOT_FOUND); return; } if (!owner || !elname) { SET_ERROR(ERR_INTERNAL_PTR); return; } en = find_entry(owner, elname); if (!en) { SET_ERROR(ERR_NCX_NOT_FOUND); return; } dlq_remove(en); if (en->owner) { m__free(en->owner); } if (en->elname) { m__free(en->elname); } m__free(en); } /* top_unregister_node */ /******************************************************************** * FUNCTION top_find_handler * * Find the top_handler in the topQ * * INPUTS: * owner == owner name * elname == element name * * RETURNS: * pointer to top_handler or NULL if not found *********************************************************************/ top_handler_t top_find_handler (const xmlChar *owner, const xmlChar *elname) { top_entry_t *en; en = find_entry(owner, elname); return (en) ? en-> handler : NULL; } /* top_find_handler */ /* END file top.c */ yuma123_2.14/netconf/src/ncx/grp.c0000664000175000017500000001430114770023131017071 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: grp.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 09dec07 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_appinfo #include "ncx_appinfo.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_typ #include "typ.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION grp_new_template * * Malloc and initialize the fields in a grp_template_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ grp_template_t * grp_new_template (void) { grp_template_t *grp; grp = m__getObj(grp_template_t); if (!grp) { return NULL; } (void)memset(grp, 0x0, sizeof(grp_template_t)); dlq_createSQue(&grp->typedefQ); dlq_createSQue(&grp->groupingQ); dlq_createSQue(&grp->datadefQ); dlq_createSQue(&grp->appinfoQ); grp->status = NCX_STATUS_CURRENT; /* default */ return grp; } /* grp_new_template */ /******************************************************************** * FUNCTION grp_free_template * * Scrub the memory in a grp_template_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * grp == grp_template_t data structure to free *********************************************************************/ void grp_free_template (grp_template_t *grp) { #ifdef DEBUG if (!grp) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (grp->name) { m__free(grp->name); } if (grp->descr) { m__free(grp->descr); } if (grp->ref) { m__free(grp->ref); } typ_clean_typeQ(&grp->typedefQ); grp_clean_groupingQ(&grp->groupingQ); obj_clean_datadefQ(&grp->datadefQ); ncx_clean_appinfoQ(&grp->appinfoQ); m__free(grp); } /* grp_free_template */ /******************************************************************** * Clean a queue of grp_template_t structs * * \param que Q of grp_template_t data structures to free *********************************************************************/ void grp_clean_groupingQ (dlq_hdr_t *que) { if (!que) { return; } while (!dlq_empty(que)) { grp_template_t *grp = (grp_template_t *)dlq_deque(que); grp_free_template(grp); } } /* grp_clean_groupingQ */ /******************************************************************** * FUNCTION grp_has_typedefs * * Check if the grouping contains any typedefs * * INPUTS: * grp == grp_template_t struct to check * * RETURNS: * TRUE if any embedded typedefs * FALSE if no embedded typedefs *********************************************************************/ boolean grp_has_typedefs (const grp_template_t *grp) { const grp_template_t *chgrp; #ifdef DEBUG if (!grp) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (!dlq_empty(&grp->typedefQ)) { return TRUE; } for (chgrp = (const grp_template_t *) dlq_firstEntry(&grp->groupingQ); chgrp != NULL; chgrp = (const grp_template_t *)dlq_nextEntry(chgrp)) { if (grp_has_typedefs(chgrp)) { return TRUE; } } return FALSE; } /* grp_has_typedefs */ /******************************************************************** * FUNCTION grp_get_mod_name * * Get the module name for a grouping * * INPUTS: * grp == grp_template_t struct to check * * RETURNS: * const pointer to module name *********************************************************************/ const xmlChar * grp_get_mod_name (const grp_template_t *grp) { #ifdef DEBUG if (!grp || !grp->tkerr.mod) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (grp->tkerr.mod->ismod) { return grp->tkerr.mod->name; } else { return grp->tkerr.mod->belongs; } /*NOTREACHED*/ } /* grp_get_mod_name */ /* END grp.c */ yuma123_2.14/netconf/src/ncx/yang_grp.h0000664000175000017500000001513214770023131020117 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yang_grp #define _H_yang_grp /* FILE: yang_grp.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* YANG Module parser grouping statement support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 14-dec-07 abb Begun; start from yang_typ.h */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION yang_grp_consume_grouping * * 2nd pass parsing * Parse the next N tokens as a grouping-stmt * Create a grp_template_t struct and add it to the specified Q * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'grouping' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == queue will get the grp_template_t * parent == parent object or NULL if top-level grouping-stmt * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_grp_consume_grouping (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent); /******************************************************************** * FUNCTION yang_grp_resolve_groupings * * 3rd pass parsing * Analyze the entire 'groupingQ' within the module struct * Finish all the clauses within this struct that * may have been defered because of possible forward references * * Any uses or augment within the grouping is deferred * until later passes because of forward references * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * groupingQ == Q of grp_template_t structs to check * parent == obj_template containing this groupingQ * == NULL if this is a module-level groupingQ * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_grp_resolve_groupings (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *groupingQ, obj_template_t *parent); /******************************************************************** * FUNCTION yang_grp_resolve_complete * * 4th pass parsing * Analyze the entire 'groupingQ' within the module struct * Expand any uses and augment statements within the group and * validate as much as possible * * Completes processing for all the groupings * sust that it is safe to expand any uses clauses * within objects, via the yang_obj_resolve_final fn * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * groupingQ == Q of grp_template_t structs to check * parent == parent object contraining groupingQ (may be NULL) * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_grp_resolve_complete (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *groupingQ, obj_template_t *parent); /******************************************************************** * FUNCTION yang_grp_resolve_final * * Analyze the entire 'groupingQ' within the module struct * Check final warnings etc. * * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * groupingQ == Q of grp_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_grp_resolve_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *groupingQ); /******************************************************************** * FUNCTION yang_grp_check_nest_loop * * Check the 'uses' object and determine if it is contained * within the group being used. * * grouping A { * uses A; * } * * grouping B { * container C { * grouping BB { * uses B; * } * } * } * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * obj == 'uses' obj_template containing the ref to 'grp' * grp == grp_template_t that this 'obj' is using * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_grp_check_nest_loop (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, grp_template_t *grp); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yang_grp */ yuma123_2.14/netconf/src/ncx/bobhash.h0000664000175000017500000000141514770023131017716 0ustar vladimirvladimir/* Public domain hash function from the 1997 Dr Dobbs article By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this code any way you wish, private, educational, or commercial. It's free. This function is referenced in - From Packet Sampling Techniques RFC 5475 - implemented from */ typedef unsigned long int ub4; /* unsigned 4-byte quantities */ typedef unsigned char ub1; /* unsigned 1-byte quantities */ #define hashsize(n) ((ub4)1<<(n)) #define hashmask(n) (hashsize(n)-1) extern ub4 bobhash(register const ub1 *k, /* the key */ register ub4 length, /* the length of the key */ register ub4 initval); /* the previous hash, or an arbitrary value */ yuma123_2.14/netconf/src/ncx/def_reg.c0000664000175000017500000003334614770023131017706 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: def_reg.c Definition Lookup Registry Stores Pointers to Data Structures but does not malloc or free those structures. That must be done outside this registry, which justs provides hash table storage of pointers Registry node types DEF_NT_NSNODE : Namespace to module pointer DEF_NT_FDNODE : File Descriptor to session control block DEF_NT_MODNAME : ncx_module_t lookup DEF_NT_OWNNODE Parent node of module-specific applications DEF_NT_DEFNODE : Child node: Module-specific definition DEF_NT_CFGNODE : Child node: Configuration data pointer ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17oct05 abb begun 11-feb-06 abb remove application layer; too complicated 18-aug-07 abb add user variable support for ncxcli ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_bobhash #include "bobhash.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_yang #include "yang.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* 256 row, chained entry top hash table */ #define DR_TOP_HASH_SIZE (hashsize(8)) #define DR_TOP_HASH_MASK (hashmask(8)) /* random number to seed the hash function */ #define DR_HASH_INIT 0x7e456289 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* top-level registry node type */ typedef enum def_nodetyp_t_ { DEF_NT_NONE, DEF_NT_NSNODE, /* topht namespace URI */ DEF_NT_FDNODE /* topht file descriptor integer */ } def_nodetyp_t; /* The def_reg module header that must be the first field in * any record stored in any level hash table */ typedef struct def_hdr_t_ { dlq_hdr_t qhdr; def_nodetyp_t nodetyp; const xmlChar *key; } def_hdr_t; /* DEF_NT_NSNODE * Top tier: Entries that don't have any children */ typedef struct def_topnode_t_ { def_hdr_t hdr; void *dptr; } def_topnode_t; /* DEF_NT_FDNODE * * Top Tier: File Descriptor Mapping Content Struct */ typedef struct def_fdmap_t_ { xmlChar num[NCX_MAX_NUMLEN]; int fd; ses_cb_t *scb; } def_fdmap_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /* first tier: module header hash table */ static dlq_hdr_t topht[DR_TOP_HASH_SIZE]; /* module init flag */ static boolean def_reg_init_done = FALSE; /******************************************************************** * FUNCTION find_top_node_h * * Find a top level node by its type and key * Retrieve the hash table index in case the search fails * * INPUTS: * nodetyp == def_nodetyp_t enum value * key == top level node key * OUTPUTS: * *h == hash table index * RETURNS: * void *to the top level entry or NULL if not found *********************************************************************/ static void * find_top_node_h (def_nodetyp_t nodetyp, const xmlChar *key, uint32 *h) { uint32 len; def_hdr_t *hdr; len = xml_strlen(key); if (!len) { return NULL; } /* get the hash value */ *h = bobhash(key, len, DR_HASH_INIT); /* clear bits to fit the topht array size */ *h &= DR_TOP_HASH_MASK; for (hdr = (def_hdr_t *)dlq_firstEntry(&topht[*h]); hdr != NULL; hdr = (def_hdr_t *)dlq_nextEntry(hdr)) { if (hdr->nodetyp==nodetyp && !xml_strcmp(key, hdr->key)) { return (void *)hdr; } } return NULL; } /* find_top_node_h */ /******************************************************************** * FUNCTION find_top_node * * Find a top level node by its type and key * * INPUTS: * nodetyp == def_nodetyp_t enum value * key == top level node key * RETURNS: * void *to the top level entry or NULL if not found *********************************************************************/ static void * find_top_node (def_nodetyp_t nodetyp, const xmlChar *key) { uint32 h; return find_top_node_h(nodetyp, key, &h); } /* find_top_node */ /******************************************************************** * FUNCTION add_top_node * * add one top-level node to the registry * !!! NOT FOR OWNNODE ENTRIES !!! * * INPUTS: * nodetyp == internal node type enum * key == address of key string inside 'ptr' * ptr == struct pointer to store in the registry * RETURNS: * status of the operation *********************************************************************/ static status_t add_top_node (def_nodetyp_t nodetyp, const xmlChar *key, void *ptr) { uint32 h; def_topnode_t *top; h = 0; /* check if the entry already exists */ top = (def_topnode_t *)find_top_node_h(nodetyp, key, &h); if (top) { return ERR_NCX_DUP_ENTRY; } /* create a new def_topnode_t struct and initialize it */ top = m__getObj(def_topnode_t); if (top == NULL) { return ERR_INTERNAL_MEM; } (void)memset(top, 0x0, sizeof(def_topnode_t)); top->hdr.nodetyp = nodetyp; top->hdr.key = key; top->dptr = ptr; /* add the topnode to the topht */ dlq_enque(top, &topht[h]); return NO_ERR; } /* add_top_node */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION def_reg_init * * Initialize the def_reg module * * RETURNS: * none *********************************************************************/ void def_reg_init (void) { uint32 i; if (!def_reg_init_done) { /* initialize the application hash table */ for (i=0; inodetyp) { case DEF_NT_NSNODE: m__free(hdr); break; case DEF_NT_FDNODE: /* free the def_fdnode_t struct first */ topnode = (def_topnode_t *)hdr; m__free(topnode->dptr); m__free(topnode); break; default: SET_ERROR(ERR_INTERNAL_VAL); m__free(hdr); /* free it anyway */ } } } (void)memset(topht, 0x0, sizeof(dlq_hdr_t)*DR_TOP_HASH_SIZE); def_reg_init_done = FALSE; } /* def_reg_cleanup */ /******************************************************************** * FUNCTION def_reg_add_ns * * add one xmlns_t to the registry * * INPUTS: * ns == namespace record to add * RETURNS: * status of the operation *********************************************************************/ status_t def_reg_add_ns (xmlns_t *ns) { #ifdef DEBUG if (!ns) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return add_top_node(DEF_NT_NSNODE, ns->ns_name, ns); } /* def_reg_add_ns */ /******************************************************************** * FUNCTION def_reg_find_ns * * find one xmlns_t in the registry * find a xmlns_t by its value (name) * * INPUTS: * nsname == namespace ID to find * RETURNS: * pointer to xmlns_t or NULL if not found *********************************************************************/ xmlns_t * def_reg_find_ns (const xmlChar *nsname) { def_topnode_t *nsdef; #ifdef DEBUG if (!nsname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif nsdef = find_top_node(DEF_NT_NSNODE, nsname); return (nsdef) ? (xmlns_t *)nsdef->dptr : NULL; } /* def_reg_find_ns */ /******************************************************************** * FUNCTION def_reg_del_ns * * unregister a xmlns_t * delete one ncx_module from the registry * * INPUTS: * nsname == namespace name to delete * RETURNS: * none *********************************************************************/ void def_reg_del_ns (const xmlChar *nsname) { def_topnode_t *nsdef; #ifdef DEBUG if (!nsname) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif nsdef = find_top_node(DEF_NT_NSNODE, nsname); if (nsdef) { dlq_remove(nsdef); m__free(nsdef); } } /* def_reg_del_ns */ /******************************************************************** * FUNCTION def_reg_add_scb * * add one FD to SCB mapping to the registry * * INPUTS: * fd == file descriptor to add * session == ses_cb_t for the session * RETURNS: * status of the operation *********************************************************************/ status_t def_reg_add_scb (int fd, ses_cb_t *scb) { def_fdmap_t *fdmap; int ret; status_t res; #ifdef DEBUG if (!scb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* create an FD-to-SCB mapping */ fdmap = m__getObj(def_fdmap_t); if (!fdmap) { return ERR_INTERNAL_MEM; } memset(fdmap, 0x0, sizeof(def_fdmap_t)); /* get a string key */ ret = snprintf((char *)fdmap->num, sizeof(fdmap->num), "%d", fd); if (ret <= 0) { m__free(fdmap); return ERR_NCX_INVALID_NUM; } /* set the mapping */ fdmap->fd = fd; fdmap->scb = scb; /* save the string-keyed mapping entry */ res = add_top_node(DEF_NT_FDNODE, fdmap->num, fdmap); if (res != NO_ERR) { m__free(fdmap); } return res; } /* def_reg_add_scb */ /******************************************************************** * FUNCTION def_reg_find_scb * * find one FD-to-SCB mapping in the registry * * INPUTS: * fd == file descriptor ID to find * RETURNS: * pointer to ses_cb_t or NULL if not found *********************************************************************/ ses_cb_t * def_reg_find_scb (int fd) { def_topnode_t *fddef; def_fdmap_t *fdmap; int ret; xmlChar buff[NCX_MAX_NUMLEN]; ret = snprintf((char *)buff, sizeof(buff), "%d", fd); if (ret <= 0) { return NULL; } fddef = find_top_node(DEF_NT_FDNODE, buff); if (!fddef) { return NULL; } fdmap = fddef->dptr; return fdmap->scb; } /* def_reg_find_scb */ /******************************************************************** * FUNCTION def_reg_del_scb * * delete one FD to SCB mapping from the registry * * INPUTS: * fd == file descriptor index to delete * RETURNS: * none *********************************************************************/ void def_reg_del_scb (int fd) { def_topnode_t *fddef; int ret; xmlChar buff[NCX_MAX_NUMLEN]; ret = snprintf((char *)buff, sizeof(buff), "%d", fd); if (ret <= 0) { return; } fddef = find_top_node(DEF_NT_FDNODE, buff); if (fddef) { dlq_remove(fddef); m__free(fddef->dptr); /* free the def_fdmap_t */ m__free(fddef); } } /* def_reg_del_scb */ /* END file def_reg.c */ yuma123_2.14/netconf/src/ncx/dlq.c0000664000175000017500000006074614770023131017077 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* File dlq.c dlq provides general queue and linked list support: QUEUE initialization/cleanup * create queue (create and initialize dynamic queue hdr) * destroy queue (destroy previously created dynamic queue) * create Squeue (initialize static queue hdr--no destroyS function) FIFO queue operators * enque (add node to end of list) * deque (return first node - remove from list) * empty (return TRUE if queue is empty, FALSE otherwise) LINEAR search (linked list) * nextEntry (return node AFTER param node - leave in list) * prevEntry (return node BEFORE param node - leave in list) * insertAhead (add node in list ahead of param node) * insertAfter (add node in list after param node) * remove (remove a node from a linked list) ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 07-jan-89 abb Begun. 24-mar-90 abb Add que_firstEntry routine 18-feb-91 abb Add que_createSQue function 12-mar-91 abb Adapt for DAVID sbee project 13-jun-91 abb change que.c to dlq.c 22-oct-93 abb added critical section hooks 14-nov-93 abb moved to this library from \usr\src\gendep\1011 15-jan-07 abb update for NCX software project add dlq_swap, dlq_hdr_t change err_msg to use log_error function 12-oct-07 abb add dlq_count 17-feb-10 abb fill in function headers */ /******************************************************************** * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_log #include "log.h" #endif #define err_msg(S) log_error("\nerr: %s ",#S) /* add enter and exit critical section hooks here */ #ifndef ENTER_CRIT #define ENTER_CRIT #endif #ifndef EXIT_CRIT #define EXIT_CRIT #endif #ifdef CPP_DEBUG /******************************************************************** * FUNCTION dlq_dumpHdr * * use log_debug to dump a Queue header contents * * INPUTS: * nodeP == Q header cast as a void * *********************************************************************/ void dlq_dumpHdr (const void *nodeP) { const dlq_hdrT *p = (const dlq_hdrT *) nodeP; if (p==NULL) { log_debug("\ndlq: NULL hdr"); return; } log_debug("\ndlq: "); switch(p->hdr_typ) { case DLQ_NULL_NODE: log_debug("unused "); break; case DLQ_SHDR_NODE: log_debug("shdr node"); break; case DLQ_DHDR_NODE: log_debug("dhdr node"); break; case DLQ_DATA_NODE: log_debug("data node"); break; case DLQ_DEL_NODE: log_debug("deleted "); break; default: log_debug("invalid "); } log_debug("(%p) p (%p) n (%p)", p, p->prev, p->next); } /* END dlq_dumpHdr */ #endif /* CPP_DEBUG */ /******************************************************************** * FUNCTION dlq_createQue * * create a dynamic queue header * * RETURNS: * pointer to malloced queue header * NULL if memory error *********************************************************************/ dlq_hdrT * dlq_createQue (void) { REG dlq_hdrT *retP; retP = m__getObj(dlq_hdrT); if (retP==NULL) { return NULL; } /* init empty list */ retP->hdr_typ = DLQ_DHDR_NODE; retP->prev = retP->next = retP; return retP; } /* END function dlq_createQue */ /******************************************************************** * FUNCTION dlq_createSQue * * create a static queue header * * INPUTS: * queAddr == pointer to malloced queue header to initialize *********************************************************************/ void dlq_createSQue (dlq_hdrT *queAddr) { #ifdef CPP_ICHK if (queAddr==NULL) { err_msg(ERR_INTERNAL_PTR); return; } #endif /* init empty list */ ENTER_CRIT; queAddr->hdr_typ = DLQ_SHDR_NODE; queAddr->prev = queAddr->next = queAddr; EXIT_CRIT; } /* END function dlq_createSQue */ /******************************************************************** * FUNCTION dlq_destroyQue * * free a dynamic queue header previously allocated * with dlq_createQue * * INPUTS: * listP == pointer to malloced queue header to free *********************************************************************/ void dlq_destroyQue (dlq_hdrT *listP) { if ( !listP ) { return; } #ifdef CPP_ICHK if (!_hdr_node(listP) || !dlq_empty(listP)) { err_msg(ERR_INTERNAL_QDEL); return; } #endif if (listP->hdr_typ==DLQ_DHDR_NODE) { listP->hdr_typ = DLQ_DEL_DHDR; m__free(listP); } } /* END function dlq_destroyQue */ /******************************************************************** * FUNCTION dlq_enque * * add a queue node to the end of a queue list * * INPUTS: * newP == pointer to queue entry to add * listP == pointer to queue list to put newP *********************************************************************/ void dlq_enque (REG void *newP, REG dlq_hdrT *listP) { #ifdef CPP_ICHK if (newP==NULL || listP==NULL) { err_msg(ERR_INTERNAL_PTR); return; } else if (!_hdr_node(listP)) { err_msg(ERR_QNODE_NOT_HDR); return; } #endif ENTER_CRIT; /* code copied directly form dlq_insertAhead to save a little time */ ((dlq_hdrT *) newP)->hdr_typ = DLQ_DATA_NODE; ((dlq_hdrT *) newP)->next = listP; ((dlq_hdrT *) newP)->prev = ((dlq_hdrT *) listP)->prev; ((dlq_hdrT *) newP)->next->prev = newP; ((dlq_hdrT *) newP)->prev->next = newP; EXIT_CRIT; } /* END function dlq_enque */ /******************************************************************** * FUNCTION dlq_deque * * remove the first queue node from the queue list * * INPUTS: * listP == pointer to queue list remove the first entry * * RETURNS: * pointer to removed first entry * NULL if the queue was empty *********************************************************************/ void *dlq_deque (dlq_hdrT * listP) { REG void *nodeP; #ifdef CPP_ICHK if (listP==NULL) { err_msg(ERR_INTERNAL_PTR); return NULL; } else if (!_hdr_node(listP)) { err_msg(ERR_QNODE_NOT_HDR); return NULL; } #endif ENTER_CRIT; /* check que empty */ if (listP==listP->next) { EXIT_CRIT; return NULL; } /* * return next que element, after removing it from the que * the que link ptrs in 'nodeP' are set to NULL upon return * the dlq_hdr in 'nodeP' is also marked as deleted */ nodeP = listP->next; #ifdef CPP_ICHK if (!_data_node(nodeP)) { EXIT_CRIT; err_msg(ERR_QNODE_NOT_DATA); return NULL; } #endif /*** dlq_remove (listP->next); *** copied inline below ***/ /* relink the queue chain */ ((dlq_hdrT *) nodeP)->prev->next = ((dlq_hdrT *) nodeP)->next; ((dlq_hdrT *) nodeP)->next->prev = ((dlq_hdrT *) nodeP)->prev; /* remove the node from the linked list */ ((dlq_hdrT *) nodeP)->hdr_typ = DLQ_DEL_NODE; ((dlq_hdrT *) nodeP)->prev = NULL; ((dlq_hdrT *) nodeP)->next = NULL; EXIT_CRIT; return nodeP; } /* END function dlq_deque */ #if defined(CPP_NO_MACROS) /******************************************************************** * FUNCTION dlq_nextEntry * * get the next queue entry after the current entry * * INPUTS: * nodeP == pointer to current queue entry to use * * RETURNS: * pointer to next queue entry * NULL if the no next entry was found *********************************************************************/ void *dlq_nextEntry (const void *nodeP) { void *retP; #ifdef CPP_ICHK if (nodeP==NULL) { err_msg(ERR_INTERNAL_PTR); return NULL; /* error */ } #endif ENTER_CRIT; /* get next entry in list -- maybe (hdr_node==end of list) */ nodeP = ((dlq_hdrT *) nodeP)->next; #ifdef CPP_ICHK if (!(_data_node(nodeP) || _hdr_node(nodeP))) { EXIT_CRIT; err_msg(ERR_BAD_QLINK); return NULL; } #endif retP = _data_node( ((dlq_hdrT *) nodeP) ) ? nodeP : NULL; EXIT_CRIT; return retP; } /* END function dlq_nextEntry */ #endif /* CPP_NO_MACROS */ #if defined(CPP_NO_MACROS) /******************************************************************** * FUNCTION dlq_prevEntry * * get the previous queue entry before the current entry * * INPUTS: * nodeP == pointer to current queue entry to use * * RETURNS: * pointer to previous queue entry * NULL if the no previous entry was found *********************************************************************/ void *dlq_prevEntry (const void *nodeP) { void *retP; #ifdef CPP_ICHK if (nodeP==NULL) { err_msg(ERR_INTERNAL_PTR); return NULL; } #endif ENTER_CRIT; /* get prev entry in list -- maybe (hdr_node==start of list) */ nodeP = ((dlq_hdrT *) nodeP)->prev; #ifdef CPP_ICHK if (!(_data_node(nodeP) || _hdr_node(nodeP))) { EXIT_CRIT; err_msg(ERR_BAD_QLINK); return NULL; } #endif retP = _data_node( ((dlq_hdrT *) nodeP) ) ? nodeP : NULL; EXIT_CRIT; return retP; } /* END function dlq_prevEntry */ #endif /* CPP_NO_MACROS */ /******************************************************************** * FUNCTION dlq_insertAhead * * insert the new queue entry before the current entry * * INPUTS: * newP == pointer to new queue entry to insert ahead of nodeP * nodeP == pointer to current queue entry to insert ahead *********************************************************************/ void dlq_insertAhead (void *newP, void *nodeP) { #ifdef CPP_ICHK if (nodeP==NULL || newP==NULL) { err_msg(ERR_INTERNAL_PTR); return; } if (nodeP==newP) { err_msg(ERR_INTERNAL_VAL); return; } #endif ENTER_CRIT; ((dlq_hdrT *) newP)->hdr_typ = DLQ_DATA_NODE; ((dlq_hdrT *) newP)->next = nodeP; ((dlq_hdrT *) newP)->prev = ((dlq_hdrT *) nodeP)->prev; ((dlq_hdrT *) newP)->next->prev = newP; ((dlq_hdrT *) newP)->prev->next = newP; EXIT_CRIT; } /* END function dlq_insertAhead */ /******************************************************************** * FUNCTION dlq_insertAfter * * insert the new queue entry after the current entry * * INPUTS: * newP == pointer to new queue entry to insert after nodeP * nodeP == pointer to current queue entry to insert after *********************************************************************/ void dlq_insertAfter (void *newP, void *nodeP) { #ifdef CPP_ICHK if (nodeP==NULL || newP==NULL) { err_msg(ERR_INTERNAL_PTR); return; } if (nodeP==newP) { err_msg(ERR_INTERNAL_VAL); return; } #endif ENTER_CRIT; ((dlq_hdrT *) newP)->hdr_typ = DLQ_DATA_NODE; ((dlq_hdrT *) newP)->prev = nodeP; ((dlq_hdrT *) newP)->next = ((dlq_hdrT *) nodeP)->next; ((dlq_hdrT *) newP)->next->prev = newP; ((dlq_hdrT *) newP)->prev->next = newP; EXIT_CRIT; } /* END function dlq_insertAfter */ /******************************************************************** * FUNCTION dlq_remove * * remove the queue entry from its queue list * entry MUST have been enqueued somehow before * this function is called * * INPUTS: * nodeP == pointer to queue entry to remove from queue *********************************************************************/ void dlq_remove (void *nodeP) { #ifdef CPP_ICHK if (nodeP==NULL) { err_msg(ERR_INTERNAL_PTR); return; } else if (!_data_node( ((dlq_hdrT *) nodeP) )) { err_msg(ERR_QNODE_NOT_DATA); return; } #endif ENTER_CRIT; /* relink the queue chain */ ((dlq_hdrT *) nodeP)->prev->next = ((dlq_hdrT *) nodeP)->next; ((dlq_hdrT *) nodeP)->next->prev = ((dlq_hdrT *) nodeP)->prev; /* remove the node from the linked list */ ((dlq_hdrT *) nodeP)->hdr_typ = DLQ_DEL_NODE; ((dlq_hdrT *) nodeP)->prev = NULL; ((dlq_hdrT *) nodeP)->next = NULL; EXIT_CRIT; } /* END fuction dlq_remove */ /******************************************************************** * FUNCTION dlq_swap * * remove the cur_node queue entry from its queue list * and replace it with the new_node * cur_node entry MUST have been enqueued somehow before * this function is called. * new_node MUST NOT already be in a queue * * INPUTS: * new_node == pointer to new queue entry to put into queue * cur_node == pointer to current queue entry to remove from queue *********************************************************************/ void dlq_swap (void *new_node, void *cur_node) { #ifdef CPP_ICHK if (new_node==NULL || cur_node==NULL) { err_msg(ERR_INTERNAL_PTR); return; } else if (!_data_node( ((dlq_hdrT *) cur_node) )) { err_msg(ERR_QNODE_NOT_DATA); return; } #endif ENTER_CRIT; /* replace cur_node with new_node in the queue chain */ ((dlq_hdrT *) cur_node)->prev->next = new_node; ((dlq_hdrT *) new_node)->prev = ((dlq_hdrT *) cur_node)->prev; ((dlq_hdrT *) cur_node)->prev = NULL; ((dlq_hdrT *) cur_node)->next->prev = new_node; ((dlq_hdrT *) new_node)->next = ((dlq_hdrT *) cur_node)->next; ((dlq_hdrT *) cur_node)->next = NULL; /* mark the new node as being in a Q */ ((dlq_hdrT *) new_node)->hdr_typ = DLQ_DATA_NODE; /* mark the current node as removed from the Q */ ((dlq_hdrT *) cur_node)->hdr_typ = DLQ_DEL_NODE; EXIT_CRIT; } /* END fuction dlq_swap */ #if defined(CPP_NO_MACROS) /******************************************************************** * FUNCTION dlq_firstEntry * * get the first entry in the queue list * * INPUTS: * listP == pointer to queue list to get the first entry from * * RETURNS: * pointer to first queue entry * NULL if the queue is empty *********************************************************************/ void *dlq_firstEntry (const dlq_hdrT * listP) { void *retP; #ifdef CPP_ICHK if (listP==NULL) { err_msg(ERR_INTERNAL_PTR); return NULL; } else if (!_hdr_node(listP)) { err_msg(ERR_QNODE_NOT_HDR); return NULL; } #endif ENTER_CRIT; retP = (listP != listP->next) ? listP->next : NULL; EXIT_CRIT; return retP; } /* END function dlq_firstEntry */ #endif /* CPP_NO_MACROS */ #if defined(CPP_NO_MACROS) /******************************************************************** * FUNCTION dlq_lastEntry * * get the last entry in the queue list * * INPUTS: * listP == pointer to queue list to get the last entry from * * RETURNS: * pointer to last queue entry * NULL if the queue is empty *********************************************************************/ void *dlq_lastEntry (const dlq_hdrT * listP) { void *retP; #ifdef CPP_ICHK if (listP==NULL) { err_msg(ERR_INTERNAL_PTR); return NULL; } else if (!_hdr_node(listP)) { err_msg(ERR_QNODE_NOT_HDR); return NULL; } #endif ENTER_CRIT; retP = (listP != listP->next) ? listP->prev : NULL; EXIT_CRIT; return retP; } /* END function dlq_lastEntry */ #endif /* CPP_NO_MACROS */ #if defined(CPP_NO_MACROS) /******************************************************************** * FUNCTION dlq_empty * * check if queue list is empty * * INPUTS: * listP == pointer to queue list to check * * RETURNS: * TRUE if queue is empty * FALSE if queue is not empty *********************************************************************/ boolean dlq_empty (const dlq_hdrT * listP) { boolean ret; #ifdef CPP_ICHK if (listP==NULL) { err_msg(ERR_INTERNAL_PTR); return TRUE; } if (!_hdr_node(listP)) { err_msg(ERR_QNODE_NOT_HDR); return TRUE; } #endif ENTER_CRIT; ret = (boolean) (listP==listP->next); EXIT_CRIT; return ret; } /* END function dlq_empty */ #endif /******************************************************************** * FUNCTION dlq_block_enque * * add all the queue entries in the srcP queue list to the * end of the dstP queue list * * INPUTS: * srcP == pointer to queue list entry to add end of dstP list * dstP == pointer to queue list to add all newP entries *********************************************************************/ void dlq_block_enque (dlq_hdrT * srcP, dlq_hdrT * dstP) { dlq_hdrT *sf, *sl, *dl; #ifdef CPP_ICHK if (srcP==NULL || dstP==NULL) return; if (!_hdr_node(srcP) || !_hdr_node(dstP)) { err_msg(ERR_QNODE_NOT_HDR); return; } #endif /* check simple case first */ if (dlq_empty(srcP)) return; /* nothing to add to dst que */ /* check next simple case--dst empty */ if (dlq_empty(dstP)) { ENTER_CRIT; /* copy srcP pointers to dstP */ dstP->next = srcP->next; dstP->prev = srcP->prev; /* relink first and last data nodes */ dstP->next->prev = dstP; dstP->prev->next = dstP; /* make src que empty */ srcP->next = srcP->prev = srcP; EXIT_CRIT; return; } ENTER_CRIT; /* else neither que is empty...move [sf..sl] after dl */ sf = srcP->next; /* source first */ sl = srcP->prev; /* source last */ dl = dstP->prev; /* dst last */ /* extend the dstQ */ dl->next = sf; /* link dl --> sf */ sf->prev = dl; /* link dl <-- sf */ /* relink the new last data node in dstQ */ dstP->prev = sl; /* link hdr.prev --> sl */ sl->next = dstP; /* link hdr.prev <-- sl */ /* make the srcQ empty */ srcP->prev = srcP->next = srcP; EXIT_CRIT; } /* END dlq_block_enque */ /******************************************************************** * FUNCTION dlq_block_insertAhead * * insert all the entries in the srcP queue list before * the dstP queue entry * * INPUTS: * srcP == pointer to new queue list to insert all entries * ahead of dstP * dstP == pointer to current queue entry to insert ahead *********************************************************************/ void dlq_block_insertAhead (dlq_hdrT * srcP, void *dstP) { REG dlq_hdrT *sf, *sl, *d1, *d2; #ifdef CPP_ICHK if (srcP==NULL || dstP==NULL) { err_msg(ERR_INTERNAL_PTR); return; } if (!_hdr_node(srcP)) { err_msg(ERR_QNODE_NOT_HDR); return; } if (!_data_node( ((dlq_hdrT *)dstP) )) { err_msg(ERR_QNODE_NOT_DATA); return; } #endif /* check simple case first */ if (dlq_empty(srcP)) return; /* nothing to add to dst que */ ENTER_CRIT; /* source que is empty... */ sf = srcP->next; /* source-first */ sl = srcP->prev; /* source-last */ d1 = ((dlq_hdrT *) dstP)->prev; /* dest-begin-insert */ d2 = (dlq_hdrT *) dstP; /* dest-end-insert (dstP) */ /* link src-list into the dst-list */ d1->next = sf; sf->prev = d1; d2->prev = sl; sl->next = d2; /* make srcQ empty */ srcP->prev = srcP->next = srcP; EXIT_CRIT; } /* END dlq_block_insertAhead */ /******************************************************************** * FUNCTION dlq_block_insertAfter * * insert all the entries in the srcP queue list after * the dstP queue entry * * INPUTS: * srcP == pointer to new queue list to insert all entries * after dstP * dstP == pointer to current queue entry to insert after *********************************************************************/ void dlq_block_insertAfter (dlq_hdrT * srcP, void *dstP) { REG dlq_hdrT *sf, *sl, *d1, *d2; #ifdef CPP_ICHK if (srcP==NULL || dstP==NULL) { err_msg(ERR_INTERNAL_PTR); return; } if (!_hdr_node(srcP)) { err_msg(ERR_QNODE_NOT_HDR); return; } if (!_data_node( ((dlq_hdrT *)dstP) )) { err_msg(ERR_QNODE_NOT_DATA); return; } #endif /* check simple case first */ if (dlq_empty(srcP)) return; /* nothing to add to dst que */ ENTER_CRIT; /* source que is not empty... */ sf = srcP->next; /* source-first */ sl = srcP->prev; /* source-last */ /* make new chain: d1 + sf [ + ... ] + sl + d2 (+...) */ d1 = (dlq_hdrT *) dstP; /* dest-begin-insert */ d2 = ((dlq_hdrT *) dstP)->next; /* dest-end-insert (dstP) */ /* link src-list into the dst-list */ d1->next = sf; sf->prev = d1; d2->prev = sl; sl->next = d2; /* make srcQ empty */ srcP->prev = srcP->next = srcP; EXIT_CRIT; } /* END dlq_block_insertAfter */ /******************************************************************** * FUNCTION dlq_block_move * * enque from [srcP .. end of srcQ list] to the dstQ * insert all the entries in the srcP queue list after * the dstP queue entry * * INPUTS: * srcQ == pointer to source queue list to move entries from * srcP == pointer to source queue entry in the srcQ * move this entry and all entries to the end of * the srcQ to the end of dstQ * dstQ == pointer to destination queue list to move the * entries from the srcQ to the end of this queue *********************************************************************/ void dlq_block_move (dlq_hdrT * srcQ, void *srcP, dlq_hdrT * dstQ) { REG dlq_hdrT *sf, *sl; dlq_hdrT tmpQ; #ifdef CPP_ICHK if (srcQ==NULL || srcP==NULL || dstQ==NULL) { err_msg(ERR_INTERNAL_PTR); return; } if (!_hdr_node(srcQ) || !_hdr_node(dstQ)) { err_msg(ERR_QNODE_NOT_HDR); return; } if (!_data_node(srcP)) { err_msg(ERR_QNODE_NOT_DATA); return; } #endif /* check simple case first */ if (dlq_empty(srcQ)) return; /* nothing to add to dst que */ ENTER_CRIT; /* unlink the srcQ list from srcP to the end */ sf = (dlq_hdrT *) srcP; sf->prev->next = srcQ; sl = srcQ->prev; srcQ->prev = sf->prev; /* insert new chain into tmpQ */ dlq_createSQue(&tmpQ); tmpQ.next = sf; sf->prev = &tmpQ; tmpQ.prev = sl; sl->next = &tmpQ; EXIT_CRIT; dlq_block_enque(&tmpQ, dstQ); } /* END dlq_block_move */ /******************************************************************** * FUNCTION dlq_count * * get the number of queue entries in the listP queue list * * INPUTS: * listP == pointer to queue list to check * * RETURNS: * number of queue entries found in listP queue *********************************************************************/ unsigned int dlq_count (const dlq_hdrT *listP) { REG const dlq_hdrT *p; REG unsigned int cnt; #ifdef CPP_ICHK if (listP==NULL) { err_msg(ERR_INTERNAL_PTR); return; } if (!_hdr_node(listP)) { err_msg(ERR_QNODE_NOT_HDR); return; } #endif cnt = 0; for (p = dlq_firstEntry(listP); p != NULL; p = dlq_nextEntry(p)) { cnt++; } return cnt; } /* END dlq_count */ /* END file dlq.c */ yuma123_2.14/netconf/src/ncx/top.h0000664000175000017500000001057414770023131017120 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_top #define _H_top /* FILE: top.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Common Top Element module Manage callback registry for received XML messages ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30-dec-05 abb Begun 11-feb-07 abb Move some common fns back to ncx/top.h */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* callback function template for a top handler * * INPUTS: * scb == session control block; source of input * The session manager will only stream * one message to this function and wait * for it to return. * top == very first node of the incoming message * This allows a single handler to support * multiple top nodes, and also contains * the attributes present in the node * RETURNS: * none */ typedef void (*top_handler_t) (ses_cb_t *scb, xml_node_t *top); /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION top_init * * Initialize the Top Elelment Handler * *********************************************************************/ extern void top_init (void); /******************************************************************** * FUNCTION top_cleanup * * cleanup Top Element Handler *********************************************************************/ extern void top_cleanup (void); /******************************************************************** * FUNCTION top_register_node * * Register a top entry handler function * * INPUTS: * owner == owner name (really module name) * elname == element name * handler == callback function for this element * RETURNS: * status *********************************************************************/ extern status_t top_register_node (const xmlChar *owner, const xmlChar *elname, top_handler_t handler); /******************************************************************** * FUNCTION top_unregister_node * * Remove a top entry handler function * This will not affect calls to the handler currently * in progress, but new calls to top_dispatch_msg * will no longer find this node. * * INPUTS: * owner == owner name * elname == element name * RETURNS: * none *********************************************************************/ extern void top_unregister_node (const xmlChar *owner, const xmlChar *elname); /******************************************************************** * FUNCTION top_find_handler * * Find the top_handler in the topQ * * INPUTS: * owner == owner name * elname == element name * * RETURNS: * pointer to top_handler or NULL if not found *********************************************************************/ extern top_handler_t top_find_handler (const xmlChar *owner, const xmlChar *elname); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_top */ yuma123_2.14/netconf/src/ncx/yin.h0000664000175000017500000000442414770023131017112 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yin #define _H_yin /* FILE: yin.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert YANG module to YIN format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 06-dec-09 abb Begun; split out from yangdump dir */ #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ typedef struct yin_mapping_t_ { const xmlChar *keyword; const xmlChar *argname; /* may be NULL */ boolean elem; } yin_mapping_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION find_yin_mapping * * Find a static yin mapping entry * * INPUTS: * name == keyword name to find * * RETURNS: * pointer to found entry, NULL if none found *********************************************************************/ extern const yin_mapping_t * yin_find_mapping (const xmlChar *name); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yin */ yuma123_2.14/netconf/src/ncx/plock_cb.h0000664000175000017500000001046314770023131020067 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_plock_cb #define _H_plock_cb /* FILE: plock_cb.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* RFC 57517 partial lock support Data structure definition ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 25-jun-10 abb Begun; slpit out from plock.h */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tstamp #include "tstamp.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* matches lock-id-type in YANG module */ typedef uint32 plock_id_t; /* struct representing 1 configuration database */ typedef struct plock_cb_t_ { dlq_hdr_t qhdr; plock_id_t plock_id; uint32 plock_sesid; dlq_hdr_t plock_xpathpcbQ; /* Q of xpath_pcb_t */ dlq_hdr_t plock_resultQ; /* Q of xpath_result_t */ struct xpath_result_t_ *plock_final_result; xmlChar plock_time[TSTAMP_MIN_SIZE]; } plock_cb_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION plock_cb_new * * Create a new partial lock control block * * INPUTS: * sid == session ID reqauesting this partial lock * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to initialized PLCB, or NULL if some error * this struct must be freed by the caller *********************************************************************/ extern plock_cb_t * plock_cb_new (uint32 sid, status_t *res); /******************************************************************** * FUNCTION plock_cb_free * * Free a partial lock control block * * INPUTS: * plcb == partial lock control block to free * *********************************************************************/ extern void plock_cb_free (plock_cb_t *plcb); /******************************************************************** * FUNCTION plock_cb_reset_id * * Set the next ID number back to the start * Only the caller maintaining a queue of plcb * can decide if the ID should rollover * *********************************************************************/ extern void plock_cb_reset_id (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_plock_cb */ yuma123_2.14/netconf/src/ncx/val_get_leafref_targval.c0000664000175000017500000000306314770023131023131 0ustar vladimirvladimir#include #include #include #include #include #include #include #include "ncx.h" #include "ncx_list.h" #include "ncx_num.h" #include "ncx_str.h" #include "ncxconst.h" #include "obj.h" #include "val.h" #include "val_parse.h" #include "val_util.h" #include "xml_util.h" #include "xml_wr.h" #include "xml_rd.h" #include "xpath.h" #include "xpath1.h" #include "xpath_yang.h" #include "yangconst.h" val_value_t* val_get_leafref_targval(val_value_t *leafref_val, val_value_t *root_val) { status_t res; val_value_t* target_val =NULL; xpath_resnode_t *resnode; xpath_result_t *result; xpath_pcb_t* xpathpcb; if(leafref_val->xpathpcb == NULL) { typ_def_t *typdef = obj_get_typdef(leafref_val->obj); xpathpcb = typ_get_leafref_pcb(typdef); } else { xpathpcb = leafref_val->xpathpcb; } result = xpath1_eval_expr(xpathpcb, leafref_val, root_val, FALSE /* logerrors */, FALSE /* non-configonly */, &res); assert(result); for (resnode = (xpath_resnode_t *)dlq_firstEntry(&result->r.nodeQ); resnode != NULL; resnode = (xpath_resnode_t *)dlq_nextEntry(resnode)) { char* target_str; val_value_t* val; val = resnode->node.valptr; target_str = val_make_sprintf_string(val); if(0==strcmp(target_str,VAL_STRING(leafref_val))) { free(target_str); target_val = val; break; } free(target_str); } free(result); return target_val; } yuma123_2.14/netconf/src/ncx/yinyang.c0000664000175000017500000003071014770023131017761 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yinyang.c YIN to YANG conversion for input ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 12dec09 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yin #include "yin.h" #endif #ifndef _H_yinyang #include "yinyang.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define GET_NODE(S, N) xml_consume_node((S)->reader, N, FALSE, TRUE) /******************************************************************** * FUNCTION parse_arg * * Parse the XML input as a YIN element argument string * * fred * * INPUTS: * scb == session control block * Input is read from scb->reader. * startnode == top node of the parameter to be parsed * Parser function will attempt to consume all the * nodes until the matching endnode is reached * tkc == token chain to add tokens into * * RETURNS: * status *********************************************************************/ static status_t parse_arg (ses_cb_t *scb, tk_chain_t *tkc, xmlns_id_t nsid, const xmlChar *argname) { xml_node_t startnode, valnode, endnode; status_t res, res2; boolean empty; /* init local vars */ xml_init_node(&startnode); xml_init_node(&valnode); xml_init_node(&endnode); empty = FALSE; res = GET_NODE(scb, &startnode); if (res != NO_ERR) { return res; } /* make sure the startnode is correct */ res = xml_node_match(&startnode, nsid, argname, XML_NT_START); if (res != NO_ERR) { res = xml_node_match(&startnode, nsid, argname, XML_NT_EMPTY); if (res == NO_ERR) { empty = TRUE; } } if (res != NO_ERR) { xml_clean_node(&startnode); return res; } /* check if an empty string was given */ if (empty) { res = tk_add_string_token(tkc, EMPTY_STRING); xml_clean_node(&startnode); return res; } /* else got a start node, get the value string node */ res = GET_NODE(scb, &valnode); if (res == NO_ERR) { if (valnode.nodetyp == XML_NT_END) { /* check correct end-node */ res = xml_endnode_match(&startnode, &valnode); if (res == NO_ERR) { /* got 2-token empty instead of value string */ res = tk_add_string_token(tkc, EMPTY_STRING); } xml_clean_node(&startnode); xml_clean_node(&valnode); return res; } } else { xml_clean_node(&startnode); xml_clean_node(&valnode); return res; } /* record the string content as a token */ if (valnode.nodetyp == XML_NT_STRING) { res = tk_add_string_token(tkc, valnode.simval); } else { res = ERR_NCX_WRONG_NODETYP_CPX; } /* get the matching end node for startnode */ res2 = GET_NODE(scb, &endnode); if (res2 == NO_ERR) { res2 = xml_endnode_match(&startnode, &endnode); } if (res == NO_ERR) { res = res2; } xml_clean_node(&startnode); xml_clean_node(&valnode); xml_clean_node(&endnode); return res; } /* parse_arg */ /******************************************************************** * FUNCTION parse_yin_stmt * * Try to parse a YIN statement from the XML reader * * INPUTS: * * * OUTPUTS: * *res == status of the operation * * RETURNS: * malloced token chain containing the converted YIN file *********************************************************************/ static status_t parse_yin_stmt (ses_cb_t *scb, tk_chain_t *tkc, xml_node_t *startnode) { const yin_mapping_t *mapping; xml_attr_t *attr; status_t res; xmlns_id_t yin_id; boolean empty, done, first; xml_node_t nextnode; res = NO_ERR; yin_id = xmlns_yin_id(); /* the startnode is determined to be the * start of a YIN statement */ switch (startnode->nodetyp) { case XML_NT_START: empty = FALSE; break; case XML_NT_EMPTY: empty = TRUE; break; default: return ERR_NCX_WRONG_NODETYP; } if (startnode->nsid == yin_id) { /* this should be a YANG keyword */ mapping = yin_find_mapping(startnode->elname); if (mapping == NULL) { return ERR_NCX_DEF_NOT_FOUND; } /* generate the YANG keyword token */ res = tk_add_id_token(tkc, mapping->keyword); if (res != NO_ERR) { return res; } /* check for the optional argument string */ if (mapping->argname != NULL) { /* a parameter sring is expected */ if (mapping->elem) { /* a simple element is expected */ if (empty) { /* startnode is empty; * child element parameter is missing */ return ERR_NCX_MISSING_ELEMENT; } /* need to get the argument as an element */ res = parse_arg(scb, tkc, yin_id, mapping->argname); } else { /* get the attribute value for the argument string */ attr = xml_find_attr(startnode, yin_id, mapping->argname); if (attr == NULL) { return ERR_NCX_DEF_NOT_FOUND; } res = tk_add_string_token(tkc, attr->attr_val); } if (res != NO_ERR) { return res; } } } else { /* YANG extension usage found */ res = tk_add_pid_token(tkc, startnode->qname, (uint32) (startnode->elname - startnode->qname - 1), startnode->elname); if (res != NO_ERR) { return res; } /* fish for an argument attribute */ attr = xml_get_first_attr(startnode); done = FALSE; while (!done && attr) { if (attr->attr_xmlns_ns) { attr = xml_next_attr(attr); } else { done = TRUE; } } switch (startnode->nodetyp) { case XML_NT_EMPTY: empty = TRUE; if (attr) { res = tk_add_string_token(tkc, attr->attr_val); } break; case XML_NT_START: empty = FALSE; if (attr) { res = tk_add_string_token(tkc, attr->attr_val); } else { res = parse_arg(scb, tkc, startnode->nsid, NULL); } break; default: res = ERR_NCX_WRONG_NODETYP; } } if (empty) { res = tk_add_semicol_token(tkc); return res; } /* if we get here, the startnode is a start-tag * and all the sibling elements (if namy) are * sub-statements of the startnode statement */ done = FALSE; first = TRUE; xml_init_node(&nextnode); while (!done && res == NO_ERR) { /* get the next XML node */ res = GET_NODE(scb, &nextnode); if (res != NO_ERR) { continue; } /* got a start node and now there could be * another start node or an end node; * a string should not be found because that would * indicate a wrong encoding or mixed content */ switch (nextnode.nodetyp) { case XML_NT_START: case XML_NT_EMPTY: if (first) { res = tk_add_lbrace_token(tkc); } if (res == NO_ERR) { res = parse_yin_stmt(scb, tkc, &nextnode); } break; case XML_NT_END: done = TRUE; res = xml_endnode_match(startnode, &nextnode); if (res == NO_ERR) { if (first) { res = tk_add_semicol_token(tkc); } else { res = tk_add_rbrace_token(tkc); } } break; default: done = TRUE; res = ERR_NCX_WRONG_NODETYP; } xml_clean_node(&nextnode); first = FALSE; } return res; } /* parse_yin_stmt */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION yinyang_convert_token_chain * * Try to open a file as a YIN formatted XML file * and convert it to a token chain for processing * by yang_parse_from_token_chain * * INPUTS: * sourcespec == file spec for the YIN file to read * res == address of return status * * OUTPUTS: * *res == status of the operation * * RETURNS: * malloced token chain containing the converted YIN file *********************************************************************/ tk_chain_t * yinyang_convert_token_chain (const xmlChar *sourcespec, status_t *res) { tk_chain_t *tkc; ses_cb_t *scb; xml_node_t startnode; #ifdef DEBUG if (sourcespec == NULL || res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif scb = ses_new_dummy_scb(); if (scb == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } tkc = tk_new_chain(); if (tkc == NULL) { *res = ERR_INTERNAL_MEM; ses_free_scb(scb); return NULL; } tk_setup_chain_yin(tkc, sourcespec); xml_init_node(&startnode); /* open the XML reader */ *res = xml_get_reader_from_filespec((const char *)sourcespec, &scb->reader); if (*res == NO_ERR) { *res = GET_NODE(scb, &startnode); if (*res == NO_ERR) { *res = parse_yin_stmt(scb, tkc, &startnode); } } /* free the reader and the session control block */ ses_free_scb(scb); xml_clean_node(&startnode); if (LOGDEBUG3) { tk_dump_chain(tkc); } if (*res == NO_ERR) { return tkc; } else { tk_free_chain(tkc); return NULL; } } /* yinyang_convert_token_chain */ /* END file yinyang.c */ yuma123_2.14/netconf/src/ncx/var.c0000664000175000017500000017634414770023131017111 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: var.c User variable utilities ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 23-aug-07 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include "procdefs.h" #include "log.h" #include "ncx.h" #include "ncxconst.h" #include "ncxmod.h" #include "runstack.h" #include "status.h" #include "xml_rd.h" #include "val.h" #include "val_util.h" #include "var.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define VAR_DEBUG 1 #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION new_var * * Malloc and fill in a new var struct * * INPUTS: * name == name of var (not Z-terminated) * namelen == length of name * val == variable value * vartype == specified variable type * res == address of status result if returning NULL * * OUTPUTS: * *res == status * * RETURNS: * malloced struct with direct 'val' string added * to be freed later *********************************************************************/ static ncx_var_t * new_var (const xmlChar *name, uint32 namelen, val_value_t *val, var_type_t vartype, status_t *res) { ncx_var_t *var; var = m__getObj(ncx_var_t); if (!var) { *res = ERR_INTERNAL_MEM; return NULL; } memset(var, 0x0, sizeof(ncx_var_t)); var->vartype = vartype; var->name = xml_strndup(name, namelen); if (!var->name) { m__free(var); *res = ERR_INTERNAL_MEM; return NULL; } var->val = val; *res = NO_ERR; return var; } /* new_var */ /******************************************************************** * FUNCTION free_var * * Free a previously malloced user var struct * * INPUTS: * var == user var struct to free *********************************************************************/ static void free_var (ncx_var_t *var) { if (var->name) { m__free(var->name); } if (var->val) { val_free_value(var->val); } m__free(var); } /* free_var */ /******************************************************************** * FUNCTION get_que * * Get the correct queue for the ncx_var_t struct * * INPUTS: * rcxt == runstack context to use * vartype == variable type * name == name string (only checks first char * does not have to be zero terminated!! * * RETURNS: * correct que header or NULL if internal error *********************************************************************/ static dlq_hdr_t * get_que (runstack_context_t *rcxt, var_type_t vartype, const xmlChar *name) { if (isdigit((int)*name)) { return runstack_get_parm_que(rcxt); } else { switch (vartype) { case VAR_TYP_LOCAL: case VAR_TYP_SESSION: /****/ return runstack_get_que(rcxt, FALSE); case VAR_TYP_CONFIG: case VAR_TYP_GLOBAL: case VAR_TYP_SYSTEM: return runstack_get_que(rcxt, TRUE); default: return NULL; } } } /* get_que */ /******************************************************************** * FUNCTION insert_var * * Insert a user var * * INPUTS: * var == var to insert * que == queue header to contain the var struct * * RETURNS: * status *********************************************************************/ static status_t insert_var (ncx_var_t *var, dlq_hdr_t *que) { ncx_var_t *cur; int ret; for (cur = (ncx_var_t *)dlq_firstEntry(que); cur != NULL; cur = (ncx_var_t *)dlq_nextEntry(cur)) { ret = xml_strcmp(var->name, cur->name); if (ret < 0) { dlq_insertAhead(var, cur); return NO_ERR; } else if (ret == 0) { return SET_ERROR(ERR_NCX_DUP_ENTRY); } /* else keep going */ } /* if we get here, then new first entry */ dlq_enque(var, que); return NO_ERR; } /* insert_var */ /******************************************************************** * FUNCTION remove_var * * Remove a user var * * INPUTS: * rcxt == runstack context to use * varQ == que to use (NULL if not known yet) * name == var name to remove * namelen == length of name * nsid == namespace ID to check if non-zero * vartype == variable type * * RETURNS: * found var struct or NULL if not found *********************************************************************/ static ncx_var_t * remove_var (runstack_context_t *rcxt, dlq_hdr_t *varQ, const xmlChar *name, uint32 namelen, xmlns_id_t nsid, var_type_t vartype) { ncx_var_t *cur; int ret; if (!varQ) { varQ = get_que(rcxt, vartype, name); if (!varQ) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } for (cur = (ncx_var_t *)dlq_firstEntry(varQ); cur != NULL; cur = (ncx_var_t *)dlq_nextEntry(cur)) { if (nsid && cur->nsid && nsid != cur->nsid) { continue; } ret = xml_strncmp(name, cur->name, namelen); if (ret == 0 && xml_strlen(cur->name)==namelen) { dlq_remove(cur); return cur; } /* else keep going */ } return NULL; } /* remove_var */ /******************************************************************** * FUNCTION find_var * * Find a user var * * INPUTS: * rcxt == runstack context to use * varQ == que to use or NULL if not known * name == var name to find * namelen == name length * nsid == namespace ID to check if non-zero * vartype == variable type * * RETURNS: * found var struct or NULL if not found *********************************************************************/ static ncx_var_t * find_var (runstack_context_t *rcxt, dlq_hdr_t *varQ, const xmlChar *name, uint32 namelen, xmlns_id_t nsid, var_type_t vartype) { ncx_var_t *cur; int ret; if (!varQ) { varQ = get_que(rcxt, vartype, name); if (!varQ) { return NULL; } } for (cur = (ncx_var_t *)dlq_firstEntry(varQ); cur != NULL; cur = (ncx_var_t *)dlq_nextEntry(cur)) { if (nsid && cur->nsid && nsid != cur->nsid) { continue; } ret = xml_strncmp(name, cur->name, namelen); if (ret == 0 && xml_strlen(cur->name)==namelen) { return cur; } /* else keep going */ } return NULL; } /* find_var */ /******************************************************************** * FUNCTION modify_str * * helper function to modify an existing stri. * * INPUTS: * val == the new value to set * var == variable to modify * vartype == variable type * * RETURNS: * status *********************************************************************/ static status_t modify_str( val_value_t *val, ncx_var_t* var, var_type_t vartype ) { status_t res = NO_ERR; if (var->vartype == VAR_TYP_SYSTEM) { log_error("\nError: system variables cannot be changed"); val_free_value( val ); return ERR_NCX_VAR_READ_ONLY; } /* only allow user vars to change the data type */ if ( (vartype == VAR_TYP_CONFIG) && (val->btyp != var->val->btyp) ) { log_error("\nError: cannot change the variable data type"); val_free_value( val ); return ERR_NCX_WRONG_TYPE; } if (vartype == VAR_TYP_CONFIG && var->val->typdef != NULL) { uint32 len; xmlChar *buffer; /* do not replace this typdef since it might * not be generic like all the user variables */ res = val_sprintf_simval_nc(NULL, val, &len); if ( NO_ERR == res ) { buffer = m__getMem(len+1); if (buffer == NULL) { res = ERR_INTERNAL_MEM; } else { res = val_sprintf_simval_nc(buffer, val, &len); if (res == NO_ERR) { res = val_set_simval(var->val, var->val->typdef, var->val->nsid, var->val->name, buffer); } m__free(buffer); } } val_free_value( val ); } else { val_value_t *tempval; /* swap out the value structs and free the old one */ tempval = var->val; var->val = val; var->val->nsid = tempval->nsid; /* make sure the name stays the same */ val_set_name(var->val, tempval->name, xml_strlen(tempval->name)); val_free_value(tempval); } return res; } /******************************************************************** * FUNCTION insert_new_str * * helper fucntion to insert a new stri. * * INPUTS: * rcxt == runstack context to use * varQ == queue to use or NULL if not known yet * name == var name to set * namelen == length of name * val == var value to set * vartype == variable type * * RETURNS: * status *********************************************************************/ static status_t insert_new_str( runstack_context_t *rcxt, dlq_hdr_t *varQ, const xmlChar *name, uint32 namelen, val_value_t *val, var_type_t vartype ) { ncx_var_t *var; status_t res; if (!varQ) { varQ = get_que(rcxt, vartype, name); if (!varQ) { val_free_value( val ); return SET_ERROR(ERR_INTERNAL_VAL); } } /* create a new value */ var = new_var(name, namelen, val, vartype, &res); if (!var ) { val_free_value( val ); return ERR_INTERNAL_MEM; } if ( NO_ERR == res ) { res = insert_var(var, varQ); } if (res != NO_ERR) { free_var(var); } return res; } /******************************************************************** * FUNCTION set_str * * Find and set (or create a new) global user variable * Common portions only!!! * * This function takes responsibility for managing 'val'. The parameter * passed into this function as 'val' should not be used or freed after * calling this function * * INPUTS: * rcxt == runstack context to use * varQ == queue to use or NULL if not known yet * name == var name to set * namelen == length of name * val == var value to set * vartype == variable type * * RETURNS: * status *********************************************************************/ static status_t set_str (runstack_context_t *rcxt, dlq_hdr_t *varQ, const xmlChar *name, uint32 namelen, val_value_t *val, var_type_t vartype) { ncx_var_t *var; status_t res; if ( !val ) { return ERR_INTERNAL_PTR; } if (!val->name) { val_set_name(val, name, namelen); } /* try to find this var */ var = find_var(rcxt, varQ, name, namelen, 0, vartype); if (var) { res = modify_str( val, var, vartype ); } else { res = insert_new_str( rcxt, varQ, name, namelen, val, vartype ); } return res; } /* set_str */ /******************************************************************** * FUNCTION set_val_from_external * * set a value struct from a external source (file) * * INPUTS: * obj == expected object template * == NULL and will be set to NCX_BT_STRING for * simple types * varval == var value node that was found * new_parm == value in progress to fill in * * RETURNS: * status *********************************************************************/ static status_t set_val_from_external (obj_template_t *obj, val_value_t *varval, val_value_t *new_parm) { FILE* f; status_t res; val_value_t* tmp_val; f = fopen(VAL_EXTERN(varval),"r"); assert(f!=NULL); res = xml_rd_open_file (f, obj, &tmp_val); if(res==NO_ERR) { val_move_children(tmp_val, new_parm); val_free_value(tmp_val); } fclose(f); } /******************************************************************** * FUNCTION set_val_from_var * * set a value struct from a var parm * * INPUTS: * obj == expected object template * == NULL and will be set to NCX_BT_STRING for * simple types * varval == var value node that was found * new_parm == value in progress to fill in * * RETURNS: * status *********************************************************************/ static status_t set_val_from_var (obj_template_t *obj, val_value_t *varval, val_value_t *new_parm) { obj_template_t *testobj, *targobj; val_value_t *cloneval, *newchild; status_t res; res = NO_ERR; switch (obj->objtype) { case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: if (typ_is_string(varval->btyp) && new_parm->btyp == NCX_BT_CONTAINER) { /* check if a child of any case is named with the * varval value */ targobj = obj_find_child(obj, NULL, VAL_STR(varval)); if (targobj != NULL && obj_get_basetype(targobj) == NCX_BT_EMPTY) { /* found a match so create a value node */ newchild = val_new_value(); if (!newchild) { return ERR_INTERNAL_MEM; } else { val_init_from_template(newchild, targobj); val_add_child(newchild, new_parm); return NO_ERR; } } } targobj = NULL; if (obj_is_root(varval->obj)) { /* look for the first real child that is * a root parameter */ for (testobj = obj_first_child_deep(obj); testobj != NULL && targobj == NULL; testobj = obj_next_child_deep(testobj)) { if (obj_is_root(testobj)) { targobj = testobj; } } } if (targobj == NULL) { /* look for the first real child that matches * the same name, and replace the choice or case value * with a case child node and its contents from varval */ targobj = obj_find_child(obj, NULL, varval->name); } if (targobj != NULL) { cloneval = val_clone(varval); if (!cloneval) { return ERR_INTERNAL_MEM; } else { if (obj_is_root(targobj)) { newchild = val_new_value(); if (!newchild) { val_free_value(cloneval); return ERR_INTERNAL_MEM; } else { val_init_from_template(newchild, targobj); } val_move_children(cloneval, newchild); if (new_parm->btyp == NCX_BT_CONTAINER) { val_add_child(newchild, new_parm); } else { res = val_replace(newchild, new_parm); val_free_value(newchild); } } else { res = val_replace(cloneval, new_parm); } val_free_value(cloneval); return res; } } break; case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: /* check that the var and the useval have * the same basic type */ if (typ_is_simple(varval->btyp) && typ_is_simple(new_parm->btyp)) { cloneval = val_clone(varval); if (!cloneval) { res = ERR_INTERNAL_MEM; } else { res = val_replace(cloneval, new_parm); val_free_value(cloneval); } } else { res = ERR_NCX_WRONG_DATATYP; } break; case OBJ_TYP_LIST: case OBJ_TYP_CONTAINER: if (varval->btyp == new_parm->btyp) { cloneval = val_clone(varval); if (!cloneval) { res = ERR_INTERNAL_MEM; } else { val_move_children(cloneval, new_parm); val_free_value(cloneval); } } else if(varval->btyp==NCX_BT_EXTERN) { set_val_from_external(obj, varval, new_parm); } else { res = ERR_NCX_WRONG_DATATYP; } if (res == NO_ERR && new_parm->btyp == NCX_BT_LIST) { res = val_gen_index_chain(new_parm->obj, new_parm); } break; case OBJ_TYP_ANYXML: if (!typ_is_simple(varval->btyp)) { cloneval = val_clone(varval); if (!cloneval) { res = ERR_INTERNAL_MEM; } else { /* hack: just move the entire node as a child of new_parm * but only for the yangcli:value parameter */ if (!xml_strcmp(obj_get_mod_name(obj), NCXMOD_YANGCLI) && !xml_strcmp(obj_get_name(obj), NCX_EL_VALUE)) { /* adding a container to a complex parm like 'value' */ val_add_child(cloneval, new_parm); } else { if(varval->btyp==NCX_BT_EXTERN) { set_val_from_external(obj, varval, new_parm); } else { /* the old code moved the children to the anyxml container */ val_move_children(cloneval, new_parm); val_free_value(cloneval); } /* change the new_parm->btyp from ANYXML to CONTAINER */ new_parm->btyp = NCX_BT_CONTAINER; } } } else { /* convert the new_parm to a string because * ANYXML is allowed to change from complex * to a simple type */ res = val_replace(varval, new_parm); } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* set_val_from_var */ /************* E X T E R N A L F U N C T I O N S ************/ /******************************************************************** * FUNCTION var_free * * Free a ncx_var_t struct * * INPUTS: * var == var struct to free * *********************************************************************/ void var_free (ncx_var_t *var) { #ifdef DEBUG if (!var) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (var->val) { val_free_value(var->val); } if (var->name) { m__free(var->name); } m__free(var); } /* var_free */ /******************************************************************** * FUNCTION var_clean_varQ * * Clean a Q of ncx_var_t * * INPUTS: * varQ == Q of var structs to free * *********************************************************************/ void var_clean_varQ (dlq_hdr_t *varQ) { ncx_var_t *var; #ifdef DEBUG if (!varQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (!dlq_empty(varQ)) { var = (ncx_var_t *)dlq_deque(varQ); var_free(var); } } /* var_clean_varQ */ /******************************************************************** * FUNCTION var_clean_type_from_varQ * * Clean all entries of one type from a Q of ncx_var_t * * INPUTS: * varQ == Q of var structs to free * vartype == variable type to delete *********************************************************************/ void var_clean_type_from_varQ (dlq_hdr_t *varQ, var_type_t vartype) { ncx_var_t *var, *nextvar; #ifdef DEBUG if (!varQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif for (var = (ncx_var_t *)dlq_firstEntry(varQ); var != NULL; var = nextvar) { nextvar = (ncx_var_t *)dlq_nextEntry(var); if (var->vartype == vartype) { dlq_remove(var); var_free(var); } } } /* var_clean_type_from_varQ */ /******************************************************************** * FUNCTION var_set_str * * Find and set (or create a new) global user variable * * INPUTS: * rcxt == runstack context to use to find the var * name == var name to set * namelen == length of name * value == var value to set * vartype == variable type * * RETURNS: * status *********************************************************************/ status_t var_set_str (runstack_context_t *rcxt, const xmlChar *name, uint32 namelen, const val_value_t *value, var_type_t vartype) { val_value_t *val; #ifdef DEBUG if (!name || !value) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!namelen) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif if (vartype == VAR_TYP_NONE || vartype > VAR_TYP_SYSTEM) { return ERR_NCX_INVALID_VALUE; } val = val_clone(value); if (!val) { return ERR_INTERNAL_MEM; } return set_str(rcxt, NULL, name, namelen, val, vartype); } /* var_set_str */ /******************************************************************** * FUNCTION var_set * * Find and set (or create a new) global user variable * * INPUTS: * rcxt == runstack context to use to find the var * name == var name to set * value == var value to set * vartype == variable type * * RETURNS: * status *********************************************************************/ status_t var_set (runstack_context_t *rcxt, const xmlChar *name, const val_value_t *value, var_type_t vartype) { #ifdef DEBUG if (!name) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return var_set_str(rcxt, name, xml_strlen(name), value, vartype); } /* var_set */ /******************************************************************** * FUNCTION var_set_str_que * * Find and set (or create a new) global user variable * * INPUTS: * varQ == variable binding Q to use instead of runstack * name == var name to set * namelen == length of name * value == var value to set * * RETURNS: * status *********************************************************************/ status_t var_set_str_que (dlq_hdr_t *varQ, const xmlChar *name, uint32 namelen, const val_value_t *value) { val_value_t *val; #ifdef DEBUG if (!varQ || !name || !value) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!namelen) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif val = val_clone(value); if (!val) { return ERR_INTERNAL_MEM; } return set_str(NULL, varQ, name, namelen, val, VAR_TYP_QUEUE); } /* var_set_str_que */ /******************************************************************** * FUNCTION var_set_que * * Find and set (or create a new) Q-based user variable * * INPUTS: * varQ == varbind Q to use * name == var name to set * value == var value to set * * RETURNS: * status *********************************************************************/ status_t var_set_que (dlq_hdr_t *varQ, const xmlChar *name, const val_value_t *value) { #ifdef DEBUG if (!varQ || !name) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return var_set_str_que(varQ, name, xml_strlen(name), value); } /* var_set_que */ /******************************************************************** * FUNCTION var_set_move_que * * Find or create and set a Q-based user variable * * INPUTS: * varQ == varbind Q to use * name == var name to set * value == var value to set (pass off memory, do not clone!) * * RETURNS: * status *********************************************************************/ status_t var_set_move_que (dlq_hdr_t *varQ, const xmlChar *name, val_value_t *value) { if ( !value ) { return ERR_INTERNAL_PTR; } #ifdef DEBUG if (!varQ || !name) { val_free_value( value ); return SET_ERROR(ERR_INTERNAL_PTR); } #endif return set_str(NULL, varQ, name, xml_strlen(name), value, /* pass off value memory here */ VAR_TYP_QUEUE); } /* var_set_move_que */ /******************************************************************** * FUNCTION var_set_move * * Find and set (or create a new) global user variable * Use the provided entry which will be freed later * This function will not clone the value like var_set * * INPUTS: * rcxt == runstack context to use to find the var * name == var name to set * namelen == length of name string * vartype == variable type * value == var value to set * * RETURNS: * status *********************************************************************/ status_t var_set_move (runstack_context_t *rcxt, const xmlChar *name, uint32 namelen, var_type_t vartype, val_value_t *value) { if ( !value ) { return ERR_INTERNAL_PTR; } #ifdef DEBUG if (!name ) { val_free_value( value ); return SET_ERROR(ERR_INTERNAL_PTR); } if (!namelen) { val_free_value( value ); return SET_ERROR(ERR_INTERNAL_VAL); } #endif if (vartype == VAR_TYP_NONE || vartype > VAR_TYP_SYSTEM) { val_free_value( value ); return ERR_NCX_INVALID_VALUE; } return set_str(rcxt, NULL, name, namelen, value, vartype); } /* var_set_move */ /******************************************************************** * FUNCTION var_set_sys * * Find and set (or create a new) global system variable * * INPUTS: * rcxt == runstack context to use * name == var name to set * value == var value to set * * RETURNS: * status *********************************************************************/ status_t var_set_sys (runstack_context_t *rcxt, const xmlChar *name, const val_value_t *value) { val_value_t *val; status_t res; #ifdef DEBUG if (!name || !value) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif val = val_clone(value); if (!val) { return ERR_INTERNAL_MEM; } res = set_str(rcxt, NULL, name, xml_strlen(name), val, VAR_TYP_SYSTEM); if (res != NO_ERR) { val_free_value(val); } return res; } /* var_set_sys */ /******************************************************************** * FUNCTION var_set_from_string * * Find and set (or create a new) global user variable * from a string value instead of a val_value_t struct * * INPUTS: * rcxt == runstack context to use * name == var name to set * valstr == value string to set * vartype == variable type * * RETURNS: * status *********************************************************************/ status_t var_set_from_string (runstack_context_t *rcxt, const xmlChar *name, const xmlChar *valstr, var_type_t vartype) { obj_template_t *genstr; val_value_t *val; status_t res; #ifdef DEBUG if (!name) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (vartype == VAR_TYP_NONE || vartype > VAR_TYP_SYSTEM) { return ERR_NCX_INVALID_VALUE; } genstr = ncx_get_gen_string(); if (!genstr) { return SET_ERROR(ERR_INTERNAL_VAL); } /* create a value struct to store */ val = val_new_value(); if (!val) { return ERR_INTERNAL_MEM; } val_init_from_template(val, genstr); /* create a string value */ res = val_set_string(val, name, valstr); if (res != NO_ERR) { val_free_value(val); return res; } /* change the name of the value to the variable node * instead of the generic 'string' */ val_set_name(val, name, xml_strlen(name)); /* save the variable */ res = set_str(rcxt, NULL, name, xml_strlen(name), val, vartype); return res; } /* var_set_from_string */ /******************************************************************** * FUNCTION var_unset * * Find and remove a local or global user variable * * !!! This function does not try global if local fails !!! * * INPUTS: * rcxt == runstack context to use * name == var name to unset * namelen == length of name string * vartype == variable type * * RETURNS: * status *********************************************************************/ status_t var_unset (runstack_context_t *rcxt, const xmlChar *name, uint32 namelen, var_type_t vartype) { ncx_var_t *var; #ifdef DEBUG if (!name) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!namelen) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif if (vartype == VAR_TYP_NONE || vartype > VAR_TYP_SYSTEM) { log_error("\nError: invalid variable type"); return ERR_NCX_WRONG_TYPE; } var = find_var(rcxt, NULL, name, namelen, 0, vartype); if (var && (var->vartype == VAR_TYP_SYSTEM || var->vartype == VAR_TYP_CONFIG)) { log_error("\nError: variable cannot be removed"); return ERR_NCX_OPERATION_FAILED; } if (var) { dlq_remove(var); free_var(var); return NO_ERR; } else { log_error("\nunset: Variable %s not found", name); return ERR_NCX_VAR_NOT_FOUND; } } /* var_unset */ /******************************************************************** * FUNCTION var_unset_que * * Find and remove a Q-based user variable * * INPUTS: * varQ == Q of ncx_var_t to use * name == var name to unset * namelen == length of name string * nsid == namespace ID to check if non-zero * *********************************************************************/ status_t var_unset_que (dlq_hdr_t *varQ, const xmlChar *name, uint32 namelen, xmlns_id_t nsid) { ncx_var_t *var; #ifdef DEBUG if (!varQ || !name) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!namelen) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif var = remove_var(NULL, varQ, name, namelen, nsid, VAR_TYP_QUEUE); if (var) { free_var(var); return NO_ERR; } else { log_error("\nunset: Variable %s not found", name); return ERR_NCX_VAR_NOT_FOUND; } } /* var_unset_que */ /******************************************************************** * FUNCTION var_get_str * * Find a global user variable * * INPUTS: * rcxt == runstack context to use * name == var name to get * namelen == length of name * vartype == variable type * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ val_value_t * var_get_str (runstack_context_t *rcxt, const xmlChar *name, uint32 namelen, var_type_t vartype) { ncx_var_t *var; #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (!namelen) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif if (vartype == VAR_TYP_NONE || vartype > VAR_TYP_SYSTEM) { return NULL; } var = find_var(rcxt, NULL, name, namelen, 0, vartype); if (var) { return var->val; } else if (vartype == VAR_TYP_LOCAL) { var = find_var(rcxt, NULL, name, namelen, 0, VAR_TYP_GLOBAL); if (var) { return var->val; } } return NULL; } /* var_get_str */ /******************************************************************** * FUNCTION var_get * * Find a local or global user variable * * INPUTS: * rcxt == runstack context to use * name == var name to get * vartype == variable type * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ val_value_t * var_get (runstack_context_t *rcxt, const xmlChar *name, var_type_t vartype) { #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return var_get_str(rcxt, name, xml_strlen(name), vartype); } /* var_get */ /******************************************************************** * FUNCTION var_get_type_str * * Find a user variable; get its var type * * INPUTS: * rcxt == runstack context to use * name == var name to get * namelen == length of name * globalonly == TRUE to check only the global Q * FALSE to check local, then global Q * * RETURNS: * var type if found, or VAR_TYP_NONE *********************************************************************/ var_type_t var_get_type_str (runstack_context_t *rcxt, const xmlChar *name, uint32 namelen, boolean globalonly) { ncx_var_t *var; #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return VAR_TYP_NONE; } if (!namelen) { SET_ERROR(ERR_INTERNAL_VAL); return VAR_TYP_NONE; } #endif if (!globalonly) { var = find_var(rcxt, NULL, name, namelen, 0, VAR_TYP_LOCAL); if (var) { return var->vartype; } } var = find_var(rcxt, NULL, name, namelen, 0, VAR_TYP_GLOBAL); if (var) { return var->vartype; } return VAR_TYP_NONE; } /* var_get_type_str */ /******************************************************************** * FUNCTION var_get_type * * Get the var type of a specified var name * * INPUTS: * rcxt == runstack context to use * name == var name to get * globalonly == TRUE to check only the global Q * FALSE to check local, then global Q * * RETURNS: * var type or VAR_TYP_NONE if not found *********************************************************************/ var_type_t var_get_type (runstack_context_t *rcxt, const xmlChar *name, boolean globalonly) { #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return VAR_TYP_NONE; } #endif return var_get_type_str(rcxt, name, xml_strlen(name), globalonly); } /* var_get_type */ /******************************************************************** * FUNCTION var_get_str_que * * Find a global user variable * * INPUTS: * varQ == queue of ncx_var_t to use * name == var name to get * namelen == length of name * nsid == namespace ID for name (0 if not used) * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ val_value_t * var_get_str_que (dlq_hdr_t *varQ, const xmlChar *name, uint32 namelen, xmlns_id_t nsid) { ncx_var_t *var; #ifdef DEBUG if (!varQ || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (!namelen) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif var = find_var(NULL, varQ, name, namelen, nsid, VAR_TYP_QUEUE); if (var) { return var->val; } else { return NULL; } } /* var_get_str_que */ /******************************************************************** * FUNCTION var_get_que * * Find a Q-based user variable * * INPUTS: * varQ == Q of ncx_var_t to use * name == var name to get * nsid == namespace ID for name (0 if not used) * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ val_value_t * var_get_que (dlq_hdr_t *varQ, const xmlChar *name, xmlns_id_t nsid) { ncx_var_t *var; #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif var = find_var(NULL, varQ, name, xml_strlen(name), nsid, VAR_TYP_QUEUE); if (var) { return var->val; } else { return NULL; } } /* var_get_que */ /******************************************************************** * FUNCTION var_get_que_raw * * Find a Q-based user variable; return the var struct instead * of just the value * * INPUTS: * varQ == Q of ncx_var_t to use * nsid == namespace ID to match (0 if not used) * name == var name to get * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ ncx_var_t * var_get_que_raw (dlq_hdr_t *varQ, xmlns_id_t nsid, const xmlChar *name) { #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return find_var(NULL, varQ, name, xml_strlen(name), nsid, VAR_TYP_QUEUE); } /* var_get_que_raw */ /******************************************************************** * FUNCTION var_get_local * * Find a local user variable * * INPUTS: * rcxt == runstack context to use * name == var name to get * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ val_value_t * var_get_local (runstack_context_t *rcxt, const xmlChar *name) { ncx_var_t *var; #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif var = find_var(rcxt, NULL, name, xml_strlen(name), 0, VAR_TYP_LOCAL); if (var) { return var->val; } return NULL; } /* var_get_local */ /******************************************************************** * FUNCTION var_get_local_str * * Find a local user variable, count-based name string * * INPUTS: * rcxt == runstack context to use * name == var name to get * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ val_value_t * var_get_local_str (runstack_context_t *rcxt, const xmlChar *name, uint32 namelen) { ncx_var_t *var; #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif var = find_var(rcxt, NULL, name, namelen, 0, VAR_TYP_LOCAL); if (var) { return var->val; } return NULL; } /* var_get_local_str */ /******************************************************************** * FUNCTION var_check_ref * * Check if the immediate command sub-string is a variable * reference. If so, return the (vartype, name, namelen) * tuple that identifies the reference. Also return * the total number of chars consumed from the input line. * * E.g., * * $foo = get-config filter=$myfilter * * INPUTS: * rcxt == runstack context to use * line == command line string to expand * isleft == TRUE if left hand side of an expression * == FALSE if right hand side ($1 type vars allowed) * len == address of number chars parsed so far in line * vartype == address of return variable Q type * name == address of string start return val * namelen == address of name length return val * * OUTPUTS: * *len == number chars consumed by this function * *vartype == variable type enum * *name == start of name string * *namelen == length of *name string * * RETURNS: * status *********************************************************************/ status_t var_check_ref (runstack_context_t *rcxt, const xmlChar *line, var_side_t side, uint32 *len, var_type_t *vartype, const xmlChar **name, uint32 *namelen) { const xmlChar *str; ncx_var_t *testvar; int num; status_t res; #ifdef DEBUG if (!line || !len || !vartype || !name || !namelen) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* save start point in line */ str = line; /* skip leading whitespace */ while (*str && isspace(*str)) { str++; } /* check if this is not the start var char */ if (*str != NCX_VAR_CH) { *len = 0; return NO_ERR; } /* is a var, check $$global or $local variable */ if (str[1] == NCX_VAR_CH) { *vartype = VAR_TYP_GLOBAL; str += 2; } else { *vartype = VAR_TYP_LOCAL; str++; } /* check if this is a number variable reference */ if (isdigit((int)*str)) { if (side==ISLEFT || *vartype == VAR_TYP_GLOBAL) { *len = 0; return ERR_NCX_INVALID_VALUE; } num = atoi((const char *)str); if (num < 0 || num > RUNSTACK_MAX_PARMS) { *len = 0; return ERR_NCX_INVALID_VALUE; } *namelen = 1; } else { /* parse the variable name */ res = ncx_parse_name(str, namelen); if (res != NO_ERR) { *len = 0; return res; } } /* return the name string */ *name = str; str += *namelen; *len = (uint32)(str - line); /* check the global var further */ if (*vartype == VAR_TYP_GLOBAL) { /* VAR_TYP_GLOBAL selects anything in the globalQ */ testvar = find_var(rcxt, NULL, *name, *namelen, 0, VAR_TYP_GLOBAL); if (testvar) { /* could be VAR_TYP_SYSTEM, VAR_TYP_CONFIG, * or VAR_TYP_GLOBAL */ *vartype = testvar->vartype; } } return NO_ERR; } /* var_check_ref */ /******************************************************************** * FUNCTION var_get_script_val * * Create or fill in a val_value_t struct for a parameter assignment * within the script processing mode * * See ncxcli.c for details on the script syntax * * INPUTS: * rcxt == runstack context to use * obj == expected type template * == NULL and will be set to NCX_BT_STRING for * simple types * val == value to fill in :: val->obj MUST be set * == NULL to create a new one * strval == string value to check * istop == TRUE (ISTOP) if calling from top level assignment * An unquoted string is the start of a command * == FALSE (ISPARM) if calling from a parameter parse * An unquoted string is just a string * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * If error, then returns NULL; * If no error, then returns pointer to new val or filled in 'val' *********************************************************************/ val_value_t * var_get_script_val (runstack_context_t *rcxt, obj_template_t *obj, val_value_t *val, const xmlChar *strval, boolean istop, status_t *res) { return var_get_script_val_ex (rcxt, NULL, obj, val, strval, istop, NULL, res); } /* var_get_script_val */ /******************************************************************** * FUNCTION var_get_script_val_ex * * Create or fill in a val_value_t struct for a parameter assignment * within the script processing mode * Allow external values * * See ncxcli.c for details on the script syntax * * INPUTS: * rcxt == runstack context to use * parentobj == container or list real node parent of 'obj' * == NULL and will be set to NCX_BT_STRING for * simple types * obj == expected type template * == NULL and will be set to NCX_BT_STRING for * simple types * val == value to fill in :: val->obj MUST be set * == NULL to create a new one * strval == string value to check * istop == TRUE (ISTOP) if calling from top level assignment * An unquoted string is the start of a command * == FALSE (ISPARM) if calling from a parameter parse * An unquoted string is just a string * fillval == value from yangcli, could be NCX_BT_EXTERN; * used instead of strval! * == NULL: not used * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * If error, then returns NULL; * If no error, then returns pointer to new val or filled in 'val' *********************************************************************/ val_value_t * var_get_script_val_ex (runstack_context_t *rcxt, obj_template_t *parentobj, obj_template_t *obj, val_value_t *val, const xmlChar *strval, boolean istop, val_value_t *fillval, status_t *res) { val_value_t *varval; const xmlChar *str, *name; val_value_t *newval, *useval, *fillcopy; xmlChar *fname, *intbuff, *sourcefile; uint32 namelen, len; var_type_t vartype; boolean simtyp; assert( obj && "obj is NULL!" ); assert( res && "res is NULL!" ); newval = NULL; useval = NULL; *res = NO_ERR; simtyp = typ_is_simple(obj_get_basetype(obj)); if (fillval != NULL && simtyp) { useval = NULL; /* must not pre-allocate and replace with fillval */ if (val != NULL) { *res = ERR_NCX_OPERATION_NOT_SUPPORTED; return NULL; } } else if (val != NULL) { /* the obj and val->obj templates may not be the same */ useval = val; } else { newval = val_new_value(); if (!newval) { *res = ERR_INTERNAL_MEM; return NULL; } if (obj->objtype == OBJ_TYP_CHOICE || obj->objtype == OBJ_TYP_CASE) { if (parentobj) { val_init_from_template(newval, parentobj); } else { log_error("\nError: container parent not provided for " "complex type"); val_free_value(newval); *res = ERR_NCX_INVALID_VALUE; return NULL; } } else { val_init_from_template(newval, obj); } useval = newval; } /* check if strval is NULL */ if (strval == NULL) { if (fillval != NULL) { fillcopy = val_clone(fillval); if (fillcopy == NULL) { *res = ERR_INTERNAL_MEM; } else { if (simtyp) { useval = fillcopy; } else { val_add_child(fillcopy, useval); } } } else if (simtyp) { *res = val_set_simval(useval, obj_get_typdef(obj), obj_get_nsid(obj), obj_get_name(obj), NULL); } else if (obj->objtype == OBJ_TYP_ANYXML) { *res = val_replace_str(NULL, 0, useval); } else { *res = ERR_NCX_WRONG_DATATYP; } } else if (*strval == NCX_AT_CH) { /* this is a NCX_BT_EXTERN value * find the file with the raw XML data * treat this as a source file name which * may need to be expanded (e.g., ~/foo.xml) */ sourcefile = ncx_get_source_ex(&strval[1], FALSE, res); if (*res == NO_ERR && sourcefile != NULL) { fname = ncxmod_find_data_file(sourcefile, TRUE, res); if (fname) { /* hand off the malloced 'fname' to be freed later */ val_set_extern(useval, fname); } /* else res already set */ } if (sourcefile != NULL) { m__free(sourcefile); sourcefile = NULL; } } else if (*strval == NCX_VAR_CH) { /* this is a variable reference * get the value and clone it for the new value * flag an error if variable not found */ len = 0; vartype = VAR_TYP_NONE; name = NULL; namelen = 0; *res = var_check_ref(rcxt, strval, ISRIGHT, &len, &vartype, &name, &namelen); if (*res == NO_ERR) { /* this is a var-reference, so get the variable */ varval = var_get_str(rcxt, name, namelen, vartype); if (!varval) { *res = ERR_NCX_VAR_NOT_FOUND; } else { *res = set_val_from_var(obj, varval, useval); } } } else if (*strval == NCX_QUOTE_CH || *strval == NCX_SQUOTE_CH) { /* this is a quoted string literal * set the start after quote */ strval++; if (simtyp) { /* set the counted string and leave off the last char * which is the ending quote */ *res = val_set_string2(useval, obj_get_name(obj), obj_get_typdef(obj), strval, xml_strlen(strval)-1); } else if (obj->objtype == OBJ_TYP_ANYXML) { *res = val_replace_str(strval, xml_strlen(strval)-1, useval); } } else if ((*strval == NCX_XML1a_CH) && (strval[1] == NCX_XML1b_CH)) { /* this is a bracketed inline XML sequence */ str = strval+2; /* find the end of the inline XML */ while (*str && !((*str==NCX_XML2a_CH) && (str[1]==NCX_XML2b_CH))) { str++; } intbuff = xml_strndup(strval+1, (uint32)(str-strval)); if (!intbuff) { *res = ERR_INTERNAL_MEM; } else { val_set_intern(useval, intbuff); } } else if (istop && ncx_valid_fname_ch(*strval)) { /* this is a regular string, treated as a function * call at the top level. If no RPC method is found, * then it will be treated as a string */ *res = NO_ERR; if (newval) { val_free_value(newval); } return NULL; } else if (obj_is_leafy(obj)) { /* this is a regular string, but not a valid NcxName, * so just treat as a string instead of potential RPC method */ *res = val_set_simval(useval, obj_get_typdef(obj), val_get_nsid(useval), useval->name, strval); } else if (obj->objtype == OBJ_TYP_ANYXML) { /* convert the NCX_BT_ANY* value to an NCX_BT_STRING * by reinitializing the template */ if (useval->btyp == NCX_BT_ANYDATA || useval->btyp == NCX_BT_ANYXML) { memset(&useval->v.childQ, 0x0, sizeof(dlq_hdr_t)); useval->btyp = NCX_BT_STRING; } *res = val_set_simval(useval, typ_get_basetype_typdef(NCX_BT_STRING), val_get_nsid(useval), useval->name, strval); } else { *res = ERR_NCX_WRONG_TYPE; } /* clean up and exit */ if (*res != NO_ERR) { if (newval) { val_free_value(newval); } useval = NULL; } return useval; } /* var_get_script_val_ex */ /******************************************************************** * FUNCTION var_check_script_val * * Create a val_value_t struct for a parameter assignment * within the script processing mode, if a var ref is found * * See yangcli documentation for details on the script syntax * * INPUTS: * rcxt == runstack context to use * obj == expected object template * == NULL and will be set to NCX_BT_STRING for * simple types * strval == string value to check * istop == TRUE if calling from top level assignment * An unquoted string is the start of a command * == FALSE if calling from a parameter parse * An unquoted string is just a string * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * If no error, then returns pointer to new malloced val * If error, then returns NULL *********************************************************************/ val_value_t * var_check_script_val (runstack_context_t *rcxt, obj_template_t *obj, const xmlChar *strval, boolean istop, status_t *res) { obj_template_t *useobj; const val_value_t *varval; const xmlChar *str, *name; val_value_t *newval; xmlChar *fname, *intbuff; uint32 namelen, len; var_type_t vartype; #ifdef DEBUG if (!res || !strval) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif useobj = NULL; newval = NULL; *res = NO_ERR; /* get a new value struct if one is not provided */ if (*strval == NCX_VAR_CH) { /* this is a variable reference * get the value and clone it for the new value * flag an error if variable not found */ *res = var_check_ref(rcxt, strval, ISRIGHT, &len, &vartype, &name, &namelen); if (*res == NO_ERR) { varval = var_get_str(rcxt, name, namelen, vartype); if (!varval) { *res = ERR_NCX_DEF_NOT_FOUND; } else { newval = val_clone(varval); if (!newval) { *res = ERR_INTERNAL_MEM; } } } return newval; } /* not a variable reference so check further */ if (obj) { useobj = obj; } else { useobj = ncx_get_gen_string(); if (!useobj) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* malloc and init a new value struct for the result */ newval = val_new_value(); if (!newval) { *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(newval, useobj); /* check the string for the appopriate value assignment */ if (*strval==NCX_AT_CH) { /* this is a NCX_BT_EXTERNAL value * find the file with the raw XML data */ fname = ncxmod_find_data_file(&strval[1], TRUE, res); if (fname) { /* hand off the malloced 'fname' to be freed later */ val_set_extern(newval, fname); } /* else res already set */ } else if (strval && *strval == NCX_QUOTE_CH) { /* this is a double-quoted string literal */ /* set the start after quote */ str = ++strval; /* find the end of the quoted string */ while (*str && *str != NCX_QUOTE_CH) { str++; } *res = val_set_string2(newval, NULL, obj_get_typdef(useobj), strval, (uint32)(str-strval)); } else if (strval && *strval == NCX_SQUOTE_CH) { /* this is a single-quoted string literal */ /* set the start after quote */ str = ++strval; /* find the end of the quoted string */ while (*str && *str != NCX_SQUOTE_CH) { str++; } *res = val_set_string2(newval, NULL, obj_get_typdef(useobj), strval, (uint32)(str-strval)); } else if (strval && (*strval == NCX_XML1a_CH) && (strval[1] == NCX_XML1b_CH)) { /* this is a bracketed inline XML sequence */ str = strval+2; /* find the end of the inline XML */ while (*str && !((*str==NCX_XML2a_CH) && (str[1]==NCX_XML2b_CH))) { str++; } intbuff = xml_strndup(strval+1, (uint32)(str-strval)); if (!intbuff) { *res = ERR_INTERNAL_MEM; } else { val_set_intern(newval, intbuff); } } else if (strval && istop && ncx_valid_fname_ch(*strval)) { /* this is a regular string, treated as a function * call at the top level; return NULL but with * the res status set to NO_ERR to signal that * an RPC method needs to be checked */ *res = NO_ERR; val_free_value(newval); return NULL; } else if (typ_is_simple(obj_get_basetype(useobj))) { /* this is a regular string, treated as a string * when used within an RPC function parameter */ *res = val_set_simval(newval, obj_get_typdef(useobj), obj_get_nsid(useobj), obj_get_name(useobj), strval); } else { /* need to convert the value to a simple value * could just make it an error, but interpreted * scripts usually allow this sort of thing * issue a warning that the old data type is * getting changed to generic string */ if (ncx_warning_enabled(ERR_NCX_USING_STRING)) { log_warn("\nWarning: changing object type from '%s' " "to 'string' for var '%s'", obj_get_typestr(useobj), newval->name); } useobj = ncx_get_gen_string(); *res = val_set_simval(newval, obj_get_typdef(useobj), val_get_nsid(newval), newval->name, strval); } /* tried to get some value set, only return value if NO_ERR * TBD: extended error reporting by returning the value anyways */ if (*res != NO_ERR) { val_free_value(newval); newval = NULL; } return newval; } /* var_check_script_val */ /******************************************************************** * FUNCTION var_cvt_generic * * Cleanup after a yangcli session has ended * * INPUTS: * varQ == Q of ncx_var_t to cleanup and change to generic * object pointers * *********************************************************************/ void var_cvt_generic (dlq_hdr_t *varQ) { ncx_var_t *cur; status_t res; #ifdef DEBUG if (varQ == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif for (cur = (ncx_var_t *)dlq_firstEntry(varQ); cur != NULL; cur = (ncx_var_t *)dlq_nextEntry(cur)) { if (cur->val) { res = val_cvt_generic(cur->val); if (res != NO_ERR) { SET_ERROR(res); } } } } /* var_cvt_generic */ /******************************************************************** * FUNCTION var_find * * Find a complete var struct for use with XPath * * INPUTS: * rcxt == runstack context to use * varname == variable name string * nsid == namespace ID for varname (0 is OK) * * RETURNS: * pointer to ncx_var_t for the first match found (local or global) *********************************************************************/ extern ncx_var_t * var_find (runstack_context_t *rcxt, const xmlChar *varname, xmlns_id_t nsid) { ncx_var_t *retvar; uint32 namelen; #ifdef DEBUG if (varname == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif namelen = xml_strlen(varname); if (namelen == 0) { return NULL; } retvar = find_var(rcxt, NULL, varname, namelen, nsid, VAR_TYP_LOCAL); if (retvar == NULL) { retvar = find_var(rcxt, NULL, varname, namelen, nsid, VAR_TYP_GLOBAL); } return retvar; } /* var_find */ /* END var.c */ yuma123_2.14/netconf/src/ncx/help.c0000664000175000017500000003421414770023131017236 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: help.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 05oct07 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_help #include "help.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_obj_help #include "obj_help.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define HELP_DEBUG 1 #endif /******************************************************************** * FUNCTION get_nestlevel * * Get the appropriate nestlevel for the help mode * * INPUTS: * mode == help mode requested * * RETURNS: * nestlevel to use for obj_dump_template *********************************************************************/ static uint32 get_nestlevel (help_mode_t mode) { /* these are arbitrary numbers; need better plan */ switch (mode) { case HELP_MODE_BRIEF: return 9; case HELP_MODE_NORMAL: return 31; case HELP_MODE_FULL: return 0; case HELP_MODE_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return 1; } /*NOTREACHED*/ } /* get_nestlevel */ /******************************************************************** * FUNCTION dump_appinfoQ (sub-mode of show RPC) * * show module=mod-name * * INPUTS: * hdr == appinfo Q to show (initialized but may be empty) * indent == start indent count *********************************************************************/ static void dump_appinfoQ (const dlq_hdr_t *hdr, uint32 indent) { const ncx_appinfo_t *appinfo; boolean first; first = TRUE; for (appinfo = (const ncx_appinfo_t *)dlq_firstEntry(hdr); appinfo != NULL; appinfo = (const ncx_appinfo_t *)dlq_nextEntry(appinfo)) { if (first) { help_write_lines((const xmlChar *)"Appinfo Queue:\n", indent, TRUE); first = FALSE; } help_write_lines(EMPTY_STRING, indent+2, TRUE); if (appinfo->value) { log_stdout("%s = %s", appinfo->name, appinfo->value); } else { log_stdout("%s", appinfo->name); } } if (!first) { log_stdout("\n"); } } /* dump_appinfoQ */ /******************************************************************** * FUNCTION dump_rpcQ (sub-mode of show RPC) * * List all the RPC methods defined in the specified module * * INPUTS: * hdr == Queue header of RPC methods to show * mode == help mode requisted * HELP_MODE_FULL for full dump of each RPC * HELP_MODE_NORMAL for normal dump of each RPC * HELP_MODE_BRIEF for 1 line report on each RPC * indent == start indent count *********************************************************************/ static void dump_rpcQ (const dlq_hdr_t *hdr, help_mode_t mode, uint32 indent) { obj_template_t *rpc; boolean anyout; uint32 nestlevel; nestlevel = get_nestlevel(mode); anyout = FALSE; for (rpc = (obj_template_t *)dlq_firstEntry(hdr); rpc != NULL; rpc = (obj_template_t *)dlq_nextEntry(rpc)) { if (rpc->objtype == OBJ_TYP_RPC) { anyout = TRUE; obj_dump_template(rpc, mode, nestlevel, indent); } } if (anyout) { log_stdout("\n"); } } /* dump_rpcQ */ /******************************************************************** * FUNCTION dump_mod_hdr (sub-mode of do_show_module(s) * * show module=mod-name header info * * INPUTS: * mod == module to show * *********************************************************************/ static void dump_mod_hdr (const ncx_module_t *mod) { /* dump some header info */ log_stdout("\n\nModule: %s", mod->name); if (mod->version) { log_stdout(" (%s)", mod->version); } log_stdout("\nPrefix: %s", mod->prefix); if (mod->xmlprefix) { log_stdout("\nXML prefix: %s", mod->xmlprefix); } log_stdout("\nNamespace: %s", (mod->ns) ? (const char *)mod->ns : "(none)"); log_stdout("\nSource: %s", mod->source); } /* dump_mod_hdr */ /********** E X T E R N A L F U N C T I O N S ****************/ /******************************************************************** * FUNCTION help_program_module * * Print the full help text for an entire program module to STDOUT * * INPUTS: * modname == module name without file suffix * cliname == name of CLI parmset within the modname module * *********************************************************************/ void help_program_module (const xmlChar *modname, const xmlChar *cliname, help_mode_t mode) { ncx_module_t *mod; obj_template_t *cli; uint32 nestlevel; help_mode_t usemode; #ifdef DEBUG if (!modname) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (mode == HELP_MODE_NONE || mode > HELP_MODE_FULL) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif nestlevel = get_nestlevel(mode); mod = ncx_find_module(modname, NULL); if (!mod) { log_error("\nhelp: Module '%s' not found", modname); SET_ERROR(ERR_NCX_MOD_NOT_FOUND); return; } log_stdout("\n\n Program %s", mod->name); log_stdout("\n\n Usage:"); log_stdout("\n\n %s [parameters]", mod->name); if (mode != HELP_MODE_BRIEF) { log_stdout("\n\n Parameters can be entered in any order, and have "); log_stdout("the form:"); log_stdout("\n\n [start] name separator [value]"); log_stdout("\n\n where:"); log_stdout("\n\n start == 0, 1, or 2 dashes (foo, -foo, --foo)"); log_stdout("\n\n name == parameter name (foo)" "\n\n Parameter name completion " "will be attempted " "\n if a partial name is entered."); log_stdout("\n\n separator == whitespace or equals sign " "(foo=bar, foo bar)"); log_stdout("\n\n value == string value for the parameter"); log_stdout("\n\n Strings with whitespace need to be " "double quoted." "\n (--foo=\"some string\")"); } if (mode == HELP_MODE_FULL && mod->descr) { log_stdout("\n\n Description:"); help_write_lines(mod->descr, 4, TRUE); } if (cliname) { cli = ncx_find_object(mod, cliname); if (!cli) { log_error("\nhelp: CLI Object %s not found", cliname); SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return; } else if (cli->objtype == OBJ_TYP_CONTAINER) { log_stdout("\n\n Command Line Parameters"); log_stdout("\n\n Key: parm-name [built-in-type] [d:default]\n"); if (mode == HELP_MODE_BRIEF) { usemode = HELP_MODE_NORMAL; } else { usemode = HELP_MODE_FULL; } obj_dump_datadefQ(obj_get_datadefQ(cli), usemode, nestlevel, 4); log_stdout("\n"); } } if (obj_any_rpcs(&mod->datadefQ) && mode == HELP_MODE_FULL) { log_stdout("\n\n Local Commands\n"); dump_rpcQ(&mod->datadefQ, mode, 4); } } /* help_program_module */ /******************************************************************** * FUNCTION help_data_module * * Print the full help text for an entire data module to STDOUT * * INPUTS: * mod == data module struct * mode == help mode requested *********************************************************************/ void help_data_module (const ncx_module_t *mod, help_mode_t mode) { #ifdef DEBUG if (mod == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif dump_mod_hdr(mod); if (mode == HELP_MODE_BRIEF) { return; } if (mode == HELP_MODE_FULL && mod->descr) { log_stdout("\nDescription:\n %s", mod->descr); } dump_rpcQ(&mod->datadefQ, mode, 2); if (mode == HELP_MODE_FULL) { dump_appinfoQ(&mod->appinfoQ, 2); } } /* help_data_module */ /******************************************************************** * FUNCTION help_type * * Print the full help text for a data type to STDOUT * * INPUTS: * typ == type template struct * mode == help mode requested *********************************************************************/ void help_type (const typ_template_t *typ, help_mode_t mode) { #ifdef DEBUG if (typ == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif log_stdout("\n Type: %s", typ->name); log_stdout(" (%s)", tk_get_btype_sym(typ_get_basetype ((const typ_def_t *)&typ->typdef))); if (mode > HELP_MODE_BRIEF && typ->descr) { log_stdout("\n Description: %s", typ->descr); } if (typ->defval) { log_stdout("\n Default: %s", typ->defval); } if (typ->units) { log_stdout("\n Units: %s", typ->units); } if (mode == HELP_MODE_FULL) { dump_appinfoQ(&typ->typdef.appinfoQ, 1); } } /* help_type */ /******************************************************************** * FUNCTION help_object * * Print the full help text for a RPC method template to STDOUT * * INPUTS: * obj == object template * mode == help mode requested *********************************************************************/ void help_object (obj_template_t *obj, help_mode_t mode) { #ifdef DEBUG if (obj == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif obj_dump_template(obj, mode, get_nestlevel(mode), 0); } /* help_object */ /******************************************************************** * FUNCTION help_write_lines * * write some indented output to STDOUT * * INPUTS: * str == string to print; 'indent' number of spaces * will be added to each new line * indent == indent count * startnl == TRUE if start with a newline, FALSE otherwise *********************************************************************/ void help_write_lines (const xmlChar *str, uint32 indent, boolean startnl) { uint32 i; if (startnl) { log_stdout("\n"); for (i=0; i maxlen) { log_stdout("..."); return; } for (i=0; i maxlen) { log_stdout("..."); return; } } } if (str) { while (*str) { log_stdout("%c", *str); if (++cnt > maxlen) { log_stdout("..."); return; } if (*str++ == '\n') { for (i=0; i maxlen) { log_stdout("..."); return; } } } } } } /* help_write_lines_max */ /* END file help.c */ yuma123_2.14/netconf/src/ncx/xml_val.c0000664000175000017500000003417214770023131017753 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xml_val.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24nov06 abb begun; split from xsd.c 16jan07 abb spit core functions from ncxdump/xml_val_util.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_num #include "ncx_num.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tstamp #include "tstamp.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define DATETIME_BUFFSIZE 64 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /************ L O C A L F U N C T I O N S ******/ /******************************************************************** * FUNCTION new_string_attr * * Set up a new string val * * INPUTS: * name == attr name * nsid == namespace ID of attr * str == the string value to use * * RETURNS: * A newly created string value or NULL *********************************************************************/ static val_value_t* new_string_attr( const xmlChar *name, const xmlns_id_t nsid, xmlChar *strval ) { val_value_t *newval; newval = val_new_value(); if (!newval) { return NULL; } newval->btyp = NCX_BT_STRING; newval->typdef = typ_get_basetype_typdef(NCX_BT_STRING); newval->name = name; newval->nsid = nsid; newval->v.str = strval; return newval; } /************ E X T E R N A L F U N C T I O N S ******/ /******************************************************************** * FUNCTION xml_val_make_qname * * Build output value: add child node to a struct node * Malloc a string buffer and create a QName string * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * INPUTS: * nsid == namespace ID to use * name == condition clause (may be NULL) * * RETURNS: * malloced value string or NULL if malloc error *********************************************************************/ xmlChar * xml_val_make_qname (xmlns_id_t nsid, const xmlChar *name) { xmlChar *str, *str2; const xmlChar *pfix; uint32 len; pfix = xmlns_get_ns_prefix(nsid); if (!pfix) { SET_ERROR(ERR_INTERNAL_VAL); /* catch no namespace error */ return xml_strdup(name); } len = xml_strlen(name) + xml_strlen(pfix) + 2; str = m__getMem(len); if (!str) { return NULL; } str2 = str; str2 += xml_strcpy(str2, pfix); *str2++ = ':'; str2 += xml_strcpy(str2, name); return str; } /* xml_val_make_qname */ /******************************************************************** * FUNCTION xml_val_qname_len * * Determine the length of the qname string that would be generated * with the xml_val_make_qname function * * INPUTS: * nsid == namespace ID to use * name == condition clause (may be NULL) * * RETURNS: * length of string needed for this QName *********************************************************************/ uint32 xml_val_qname_len (xmlns_id_t nsid, const xmlChar *name) { const xmlChar *pfix; pfix = xmlns_get_ns_prefix(nsid); if (!pfix) { SET_ERROR(ERR_INTERNAL_VAL); /* catch no namespace error */ return xml_strlen(name); } return xml_strlen(name) + xml_strlen(pfix) + 1; } /* xml_val_qname_len */ /******************************************************************** * FUNCTION xml_val_sprintf_qname * * construct a QName into a buffer * * INPUTS: * buff == buffer * bufflen == size of buffer * nsid == namespace ID to use * name == condition clause (may be NULL) * * RETURNS: * number of bytes written to the buffer *********************************************************************/ uint32 xml_val_sprintf_qname (xmlChar *buff, uint32 bufflen, xmlns_id_t nsid, const xmlChar *name) { xmlChar *str; const xmlChar *pfix; uint32 len; pfix = xmlns_get_ns_prefix(nsid); if (!pfix) { SET_ERROR(ERR_INTERNAL_VAL); /* catch no namespace error */ return 0; } len = xml_strlen(name) + xml_strlen(pfix) + 2; if (len > bufflen) { SET_ERROR(ERR_BUFF_OVFL); return 0; } /* construct the QName string */ str = buff; str += xml_strcpy(str, pfix); *str++ = ':'; str += xml_strcpy(str, name); return len-1; } /* xml_val_sprintf_qname */ /******************************************************************** * FUNCTION xml_val_add_attr * * Set up a new attr val and add it to the specified val * hand off a malloced attribute string * * INPUTS: * name == attr name * nsid == namespace ID of attr * attrval == attr val to add (do not use strdup) * val == parent val struct to hold the new attr * * RETURNS: * status *********************************************************************/ status_t xml_val_add_attr (const xmlChar *name, xmlns_id_t nsid, xmlChar *attrval, val_value_t *val) { val_value_t *newval; if (!val) { return ERR_INTERNAL_MEM; } /* create a new value to hold the attribute name value pair */ newval = new_string_attr( name, nsid, attrval ); if (!newval) { return ERR_INTERNAL_MEM; } dlq_enque(newval, &val->metaQ); return NO_ERR; } /* xml_val_add_attr */ /******************************************************************** * FUNCTION xml_val_add_cattr * * Set up a new const attr val and add it to the specified val * copy a const attribute string * * INPUTS: * name == attr name * nsid == namespace ID of attr * cattrval == const attr val to add (use strdup) * val == parent val struct to hold the new attr * * RETURNS: * status *********************************************************************/ status_t xml_val_add_cattr (const xmlChar *name, xmlns_id_t nsid, const xmlChar *cattrval, val_value_t *val) { xmlChar *str; status_t res; str = xml_strdup( cattrval ); if (!str) { return ERR_INTERNAL_MEM; } res = xml_val_add_attr( name, nsid, str, val ); if ( NO_ERR != res ) { m__free( str ); } return res; } /* xml_val_add_cattr */ /******************************************************************** * FUNCTION xml_val_new_struct * * Set up a new struct * * INPUTS: * name == element name * nsid == namespace ID of name * * RETURNS: * new struct or NULL if malloc error *********************************************************************/ val_value_t * xml_val_new_struct (const xmlChar *name, xmlns_id_t nsid) { val_value_t *val; val = val_new_value(); if (!val) { return NULL; } val_init_complex(val, NCX_BT_CONTAINER); val->typdef = typ_get_basetype_typdef(NCX_BT_CONTAINER); val->name = name; val->nsid = nsid; val->obj = ncx_get_gen_container(); return val; } /* xml_val_new_struct */ /******************************************************************** * FUNCTION xml_val_new_string * * Set up a new string element; reuse the value instead of copying it * hand off a malloced string * * INPUTS: * name == element name * nsid == namespace ID of name * strval == malloced string value that will be freed later * * RETURNS: * new string or NULL if malloc error *********************************************************************/ val_value_t * xml_val_new_string (const xmlChar *name, xmlns_id_t nsid, xmlChar *strval) { val_value_t *val; val = new_string_attr( name, nsid, strval ); if (!val) { return NULL; } val->obj = ncx_get_gen_string(); return val; } /* xml_val_new_string */ /******************************************************************** * FUNCTION xml_val_new_cstring * * Set up a new string from a const string * copy a const string * * INPUTS: * name == element name * nsid == namespace ID of name * strval == const string value that will strduped first * * RETURNS: * new string or NULL if malloc error *********************************************************************/ val_value_t * xml_val_new_cstring (const xmlChar *name, xmlns_id_t nsid, const xmlChar *strval) { xmlChar *str; val_value_t *val; str = xml_strdup(strval); if (!str) { return NULL; } val = new_string_attr( name, nsid, str ); if ( !val ) { m__free( str ); return NULL; } val->obj = ncx_get_gen_string(); return val; } /* xml_val_new_cstring */ /******************************************************************** * FUNCTION xml_val_new_flag * * Set up a new flag * * INPUTS: * name == element name * nsid == namespace ID of name * * RETURNS: * new struct or NULL if malloc error *********************************************************************/ val_value_t * xml_val_new_flag (const xmlChar *name, xmlns_id_t nsid) { val_value_t *val; val = val_new_value(); if (!val) { return NULL; } val->btyp = NCX_BT_EMPTY; val->v.boo = TRUE; val->typdef = typ_get_basetype_typdef(NCX_BT_EMPTY); val->name = name; val->nsid = nsid; val->obj = ncx_get_gen_empty(); return val; } /* xml_val_new_flag */ /******************************************************************** * FUNCTION xml_val_new_boolean * * Set up a new boolean * * INPUTS: * name == element name * nsid == namespace ID of name * boo == boolean value to set * * RETURNS: * new struct or NULL if malloc error *********************************************************************/ val_value_t * xml_val_new_boolean (const xmlChar *name, xmlns_id_t nsid, boolean boo) { val_value_t *val; val = xml_val_new_flag( name, nsid ); if (!val) { return NULL; } val->v.boo = boo; return val; } /* xml_val_new_boolean */ /******************************************************************** * FUNCTION xml_val_new_number * * Set up a new number * * INPUTS: * name == element name * nsid == namespace ID of name * num == number value to set * btyp == base type of 'num' * * RETURNS: * new struct or NULL if malloc error *********************************************************************/ val_value_t * xml_val_new_number (const xmlChar *name, xmlns_id_t nsid, ncx_num_t *num, ncx_btype_t btyp) { val_value_t *val; status_t res; val = val_new_value(); if (!val) { return NULL; } val->btyp = btyp; res = ncx_copy_num(num, &val->v.num, btyp); if (res != NO_ERR) { val_free_value(val); return NULL; } val->typdef = typ_get_basetype_typdef(btyp); val->name = name; val->nsid = nsid; /* borrowing the generic string object * not sure it will really matter * since the val(nsid,name) is used instead */ val->obj = ncx_get_gen_string(); return val; } /* xml_val_new_number */ /* END file xml_val.c */ yuma123_2.14/netconf/src/ncx/ncx_appinfo.c0000664000175000017500000005441314770023131020615 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: ncx_appinfo.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17feb10 abb begun; split out from ncx.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_appinfo #include "ncx_appinfo.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION consume_appinfo_entry * * Check if an appinfo sub-clause is present * * foovar "fred"; * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Consume the appinfo clause tokens, and save it in * appinfoQ, if that var is non-NULL * * INPUTS: * tkc == token chain * mod == ncx_module_t in progress * appinfoQ == address of Queue to hold this entry (may be NULL) * bkup == TRUE if not looking for a right brace * FALSE if right brace should be checked * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_appinfo_entry (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *appinfoQ, boolean bkup) { ncx_appinfo_t *appinfo; status_t res, retres; /* right brace means appinfo is done */ if (tkc->source == TK_SOURCE_YANG && !bkup) { if (tk_next_typ(tkc)==TK_TT_RBRACE) { return ERR_NCX_SKIPPED; } } res = NO_ERR; retres = NO_ERR; appinfo = NULL; appinfo = ncx_new_appinfo(FALSE); if (!appinfo) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } /* get the appinfo prefix value and variable name * * Get the first token; should be an unquoted string * if OK then malloc a new appinfo struct and make * a copy of the token value. */ res = yang_consume_pid_string(tkc, mod, &appinfo->prefix, &appinfo->name); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_appinfo(appinfo); return retres; } } ncx_set_error(&appinfo->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); /* at this point, if appinfoQ non-NULL: * appinfo is malloced initialized * appinfo name is set, prefix may be set * * Now get the optional appinfo value string * * move to the 2nd token, either a string or a semicolon * if the value is missing */ switch (tk_next_typ(tkc)) { case TK_TT_SEMICOL: case TK_TT_LBRACE: break; default: res = yang_consume_string(tkc, mod, &appinfo->value); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_appinfo(appinfo); return retres; } } } /* go around and get nested extension statements or semi-colon */ res = yang_consume_semiapp(tkc, mod, appinfo->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_appinfo(appinfo); return retres; } } if (retres != NO_ERR || !appinfoQ) { ncx_free_appinfo(appinfo); } else { dlq_enque(appinfo, appinfoQ); } return retres; } /* consume_appinfo_entry */ /******************************************************************** * FUNCTION consume_appinfo * * Check if an appinfo clause is present * * Save in appinfoQ if non-NULL * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == ncx_module_t in progress (NULL if none) * appinfoQ == queue to use for any found entries (may be NULL) * bkup == TRUE if token should be backed up first * FALSE if not * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_appinfo (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *appinfoQ, boolean bkup) { status_t res; boolean done; if (tkc->source == TK_SOURCE_YANG && bkup) { /* hack: all the YANG fns that call this function * already parsed the MSTRING since extensions * can be spread out throughout the file */ TK_BKUP(tkc); } res = NO_ERR; done = FALSE; while (!done) { res = consume_appinfo_entry(tkc, mod, appinfoQ, bkup); if (res != NO_ERR || tkc->source == TK_SOURCE_YANG) { done = TRUE; } } return res; } /* consume_appinfo */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION ncx_new_appinfo * * Create an appinfo entry * * INPOUTS: * isclone == TRUE if this is for a cloned object * * RETURNS: * malloced appinfo entry or NULL if malloc error *********************************************************************/ ncx_appinfo_t * ncx_new_appinfo (boolean isclone) { ncx_appinfo_t *appinfo; appinfo = m__getObj(ncx_appinfo_t); if (!appinfo) { return NULL; } memset(appinfo, 0x0, sizeof(ncx_appinfo_t)); appinfo->isclone = isclone; if (!isclone) { appinfo->appinfoQ = dlq_createQue(); if (!appinfo->appinfoQ) { m__free(appinfo); appinfo = NULL; } } return appinfo; } /* ncx_new_appinfo */ /******************************************************************** * FUNCTION ncx_free_appinfo * * Free an appinfo entry * * INPUTS: * appinfo == ncx_appinfo_t data structure to free *********************************************************************/ void ncx_free_appinfo (ncx_appinfo_t *appinfo) { #ifdef DEBUG if (!appinfo) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (!appinfo->isclone) { if (appinfo->prefix) { m__free(appinfo->prefix); } if (appinfo->name) { m__free(appinfo->name); } if (appinfo->value) { m__free(appinfo->value); } if (appinfo->appinfoQ) { ncx_clean_appinfoQ(appinfo->appinfoQ); dlq_destroyQue(appinfo->appinfoQ); } } m__free(appinfo); } /* ncx_free_appinfo */ /******************************************************************** * FUNCTION ncx_find_appinfo * * Find an appinfo entry by name (First match is returned) * The entry returned is not removed from the Q * * INPUTS: * appinfoQ == pointer to Q of ncx_appinfo_t data structure to check * prefix == module prefix that defines the extension * == NULL to pick the first match (not expecting * appinfo name collisions) * varname == name string of the appinfo variable to find * * RETURNS: * pointer to the ncx_appinfo_t struct for the entry if found * NULL if the entry is not found *********************************************************************/ ncx_appinfo_t * ncx_find_appinfo (dlq_hdr_t *appinfoQ, const xmlChar *prefix, const xmlChar *varname) { ncx_appinfo_t *appinfo; #ifdef DEBUG if (!appinfoQ || !varname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (appinfo = (ncx_appinfo_t *)dlq_firstEntry(appinfoQ); appinfo != NULL; appinfo = (ncx_appinfo_t *)dlq_nextEntry(appinfo)) { if (prefix && appinfo->prefix && xml_strcmp(prefix, appinfo->prefix)) { continue; } if (!xml_strcmp(varname, appinfo->name)) { return appinfo; } } return NULL; } /* ncx_find_appinfo */ /******************************************************************** * FUNCTION ncx_find_const_appinfo * * Find an appinfo entry by name (First match is returned) * The entry returned is not removed from the Q * * INPUTS: * appinfoQ == pointer to Q of ncx_appinfo_t data structure to check * prefix == module prefix that defines the extension * == NULL to pick the first match (not expecting * appinfo name collisions) * varname == name string of the appinfo variable to find * * RETURNS: * pointer to the ncx_appinfo_t struct for the entry if found * NULL if the entry is not found *********************************************************************/ const ncx_appinfo_t * ncx_find_const_appinfo (const dlq_hdr_t *appinfoQ, const xmlChar *prefix, const xmlChar *varname) { const ncx_appinfo_t *appinfo; #ifdef DEBUG if (!appinfoQ || !varname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (appinfo = (const ncx_appinfo_t *)dlq_firstEntry(appinfoQ); appinfo != NULL; appinfo = (const ncx_appinfo_t *)dlq_nextEntry(appinfo)) { if (prefix && appinfo->prefix && xml_strcmp(prefix, appinfo->prefix)) { continue; } if (!xml_strcmp(varname, appinfo->name)) { return appinfo; } } return NULL; } /* ncx_find_const_appinfo */ /******************************************************************** * FUNCTION ncx_find_next_appinfo * * Find the next instance of an appinfo entry by name * (First match is returned) * The entry returned is not removed from the Q * * INPUTS: * current == pointer to current ncx_appinfo_t data structure to check * prefix == module prefix that defines the extension * == NULL to pick the first match (not expecting * appinfo name collisions) * varname == name string of the appinfo variable to find * * RETURNS: * pointer to the ncx_appinfo_t struct for the entry if found * NULL if the entry is not found *********************************************************************/ const ncx_appinfo_t * ncx_find_next_appinfo (const ncx_appinfo_t *current, const xmlChar *prefix, const xmlChar *varname) { ncx_appinfo_t *appinfo; #ifdef DEBUG if (!current || !varname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (appinfo = (ncx_appinfo_t *)dlq_nextEntry(current); appinfo != NULL; appinfo = (ncx_appinfo_t *)dlq_nextEntry(appinfo)) { if (prefix && appinfo->prefix && xml_strcmp(prefix, appinfo->prefix)) { continue; } if (!xml_strcmp(varname, appinfo->name)) { return appinfo; } } return NULL; } /* ncx_find_next_appinfo */ /******************************************************************** * FUNCTION ncx_find_next_appinfo2 * * Find the next instance of an appinfo entry by name * (First match is returned) * The entry returned is not removed from the Q * * INPUTS: * current == pointer to current ncx_appinfo_t data structure to check * prefix == module prefix that defines the extension * == NULL to pick the first match (not expecting * appinfo name collisions) * varname == name string of the appinfo variable to find * * RETURNS: * pointer to the ncx_appinfo_t struct for the entry if found * NULL if the entry is not found *********************************************************************/ ncx_appinfo_t * ncx_find_next_appinfo2 (ncx_appinfo_t *current, const xmlChar *prefix, const xmlChar *varname) { ncx_appinfo_t *appinfo; #ifdef DEBUG if (!current || !varname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (appinfo = (ncx_appinfo_t *)dlq_nextEntry(current); appinfo != NULL; appinfo = (ncx_appinfo_t *)dlq_nextEntry(appinfo)) { if (prefix && appinfo->prefix && xml_strcmp(prefix, appinfo->prefix)) { continue; } if (!xml_strcmp(varname, appinfo->name)) { return appinfo; } } return NULL; } /* ncx_find_next_appinfo2 */ /******************************************************************** * FUNCTION ncx_clone_appinfo * * Clone an appinfo value * * INPUTS: * appinfo == ncx_appinfo_t data structure to clone * * RETURNS: * pointer to the malloced ncx_appinfo_t struct clone of appinfo * NULL if a malloc error *********************************************************************/ ncx_appinfo_t * ncx_clone_appinfo (ncx_appinfo_t *appinfo) { ncx_appinfo_t *newapp; #ifdef DEBUG if (!appinfo) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif newapp = ncx_new_appinfo(TRUE); if (!newapp) { return NULL; } newapp->prefix = appinfo->prefix; newapp->name = appinfo->name; newapp->value = appinfo->value; newapp->appinfoQ = appinfo->appinfoQ; return newapp; } /* ncx_clone_appinfo */ /******************************************************************** * FUNCTION ncx_clean_appinfoQ * * Check an initialized appinfoQ for any entries * Remove them from the queue and delete them * * INPUTS: * appinfoQ == Q of ncx_appinfo_t data structures to free *********************************************************************/ void ncx_clean_appinfoQ (dlq_hdr_t *appinfoQ) { ncx_appinfo_t *appinfo; #ifdef DEBUG if (!appinfoQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (!dlq_empty(appinfoQ)) { appinfo = (ncx_appinfo_t *)dlq_deque(appinfoQ); ncx_free_appinfo(appinfo); } } /* ncx_clean_appinfoQ */ /******************************************************************** * FUNCTION ncx_consume_appinfo * * Check if an appinfo clause is present * * Save in appinfoQ if non-NULL * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == ncx_module_t in progress (NULL if none) * appinfoQ == queue to use for any found entries (may be NULL) * * RETURNS: * status of the operation *********************************************************************/ status_t ncx_consume_appinfo (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *appinfoQ) { #ifdef DEBUG if (!tkc || !appinfoQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return consume_appinfo(tkc, mod, appinfoQ, TRUE); } /* ncx_consume_appinfo */ /******************************************************************** * FUNCTION ncx_consume_appinfo2 * * Check if an appinfo clause is present * Do not backup the current token * The TK_TT_MSTRING token has not been seen yet * Called from yang_consume_semiapp * * Save in appinfoQ if non-NULL * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == ncx_module_t in progress (NULL if none) * appinfoQ == queue to use for any found entries (may be NULL) * * RETURNS: * status of the operation *********************************************************************/ status_t ncx_consume_appinfo2 (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *appinfoQ) { #ifdef DEBUG if (!tkc || !appinfoQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return consume_appinfo(tkc, mod, appinfoQ, FALSE); } /* ncx_consume_appinfo2 */ /******************************************************************** * FUNCTION ncx_resolve_appinfoQ * * Validate all the appinfo clauses present in the specified Q * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == ncx_module_t in progress * appinfoQ == queue to check * * RETURNS: * status of the operation *********************************************************************/ status_t ncx_resolve_appinfoQ (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *appinfoQ) { ncx_appinfo_t *appinfo; ext_template_t *ext = NULL; status_t res, retres; #ifdef DEBUG if (!tkc || !mod || !appinfoQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif retres = NO_ERR; for (appinfo = (ncx_appinfo_t *)dlq_firstEntry(appinfoQ); appinfo != NULL; appinfo = (ncx_appinfo_t *)dlq_nextEntry(appinfo)) { if (appinfo->isclone) { continue; } if (appinfo->ext) { /* this is a redo validation */ continue; } res = NO_ERR; if (appinfo->prefix && xml_strcmp(appinfo->prefix, mod->prefix)) { res = yang_find_imp_extension(pcb, tkc, mod, appinfo->prefix, appinfo->name, &appinfo->tkerr, &ext); CHK_EXIT(res, retres); } else if (appinfo->prefix != NULL) { ext = ext_find_extension(mod, appinfo->name); if (!ext) { log_error("\nError: Local module extension '%s' not found", appinfo->name); res = retres = ERR_NCX_DEF_NOT_FOUND; tkc->curerr = &appinfo->tkerr; ncx_print_errormsg(tkc, mod, retres); } else { res = NO_ERR; } } /* else skipping stmt assumed to be YANG inside an ext-stmt */ if (res == NO_ERR && appinfo->prefix != NULL) { appinfo->ext = ext; if (ext->arg && !appinfo->value) { retres = ERR_NCX_MISSING_PARM; log_error("\nError: argument missing for extension '%s:%s' ", appinfo->prefix, ext->name); tkc->curerr = &appinfo->tkerr; ncx_print_errormsg(tkc, mod, retres); } else if (!ext->arg && appinfo->value) { retres = ERR_NCX_EXTRA_PARM; log_error("\nError: argument '%s' provided for" " extension '%s:%s' is not allowed", appinfo->value, appinfo->prefix, ext->name); tkc->curerr = &appinfo->tkerr; ncx_print_errormsg(tkc, mod, retres); } } /* recurse through any nested appinfo statements */ res = ncx_resolve_appinfoQ(pcb, tkc, mod, appinfo->appinfoQ); CHK_EXIT(res, retres); } return retres; } /* ncx_resolve_appinfoQ */ /******************************************************************** * FUNCTION ncx_get_appinfo_value * * Get the value string from an appinfo struct * * INPUTS: * appinfo == ncx_appinfo_t data structure to use * * RETURNS: * pointer to the string value if name * NULL if no value *********************************************************************/ const xmlChar * ncx_get_appinfo_value (const ncx_appinfo_t *appinfo) { #ifdef DEBUG if (appinfo == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return appinfo->value; } /* ncx_get_appinfo_value */ /* END file ncx_appinfo.c */ yuma123_2.14/netconf/src/ncx/obj_help.h0000664000175000017500000000527514770023131020102 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_obj_help #define _H_obj_help /* FILE: obj_help.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Help command support for obj_template_t ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17-aug-08 abb Begun; split from obj.h to prevent H file loop */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_help #include "help.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION obj_dump_template * * Dump the contents of an obj_template_t struct for help text * * INPUTS: * obj == obj_template to dump help for * mode == requested help mode * nestlevel == number of levels from the top-level * that should be printed; 0 == all levels * indent == start indent count *********************************************************************/ extern void obj_dump_template (obj_template_t *obj, help_mode_t mode, uint32 nestlevel, uint32 indent); /******************************************************************** * FUNCTION obj_dump_datadefQ * * Dump the contents of a datadefQ for debugging * * INPUTS: * datadefQ == Q of obj_template to dump * mode == desired help mode (brief, normal, full) * nestlevel == number of levels from the top-level * that should be printed; 0 == all levels * indent == start indent count *********************************************************************/ extern void obj_dump_datadefQ (dlq_hdr_t *datadefQ, help_mode_t mode, uint32 nestlevel, uint32 indent); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_obj_help */ yuma123_2.14/netconf/src/ncx/ses_msg.h0000664000175000017500000002374614770023131017763 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ses_msg #define _H_ses_msg /* FILE: ses_msg.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Session Message Common definitions module ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 20-jan-07 abb Begun. */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION ses_msg_init * * Initialize the session message manager module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void ses_msg_init (void); /******************************************************************** * FUNCTION ses_msg_cleanup * * Cleanup the session message manager module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void ses_msg_cleanup (void); /******************************************************************** * FUNCTION ses_msg_new_msg * * Malloc a new session message control header * * INPUTS: * msg == address of ses_msg_t pointer that will be set * * OUTPUTS: * *msg == malloced session message struct (if NO_ERR return) * * RETURNS: * status *********************************************************************/ extern status_t ses_msg_new_msg (ses_msg_t **msg); /******************************************************************** * FUNCTION ses_msg_free_msg * * Free the session message and all its buffer chunks * * INPUTS: * scb == session control block owning the message * msg == message to free (already removed from any Q) * *********************************************************************/ extern void ses_msg_free_msg (ses_cb_t *scb, ses_msg_t *msg); /******************************************************************** * FUNCTION ses_msg_new_buff * * Malloc a new session buffer chuck * * Note that the buffer memory is not cleared after each use * since this is not needed for byte stream IO * * INPUTS: * scb == session control block to malloc a new message for * outbuff == TRUE if this is for outgoing message * FALSE if this is for incoming message * buff == address of ses_msg_buff_t pointer that will be set * * OUTPUTS: * *buff == malloced session buffer chunk (if NO_ERR return) * * RETURNS: * status *********************************************************************/ extern status_t ses_msg_new_buff (ses_cb_t *scb, boolean outbuff, ses_msg_buff_t **buff); /******************************************************************** * FUNCTION ses_msg_free_buff * * Free the session buffer chunk * * INPUTS: * scb == session control block owning the message * buff == buffer to free (already removed from any Q) * * RETURNS: * none *********************************************************************/ extern void ses_msg_free_buff (ses_cb_t *scb, ses_msg_buff_t *buff); /******************************************************************** * FUNCTION ses_msg_write_buff * * Add some text to the message buffer * * INPUTS: * scb == session control block to use * buff == buffer to write to * ch == xmlChar to write * * RETURNS: * status_t * *********************************************************************/ extern status_t ses_msg_write_buff (ses_cb_t *scb, ses_msg_buff_t *buff, uint32 ch); /******************************************************************** * FUNCTION ses_msg_send_buffs * * Send multiple buffers to the session client socket * Tries to send one packet at maximum MTU * * INPUTS: * scb == session control block * * RETURNS: * status *********************************************************************/ extern status_t ses_msg_send_buffs (ses_cb_t *scb); /******************************************************************** * FUNCTION ses_msg_new_output_buff * * Put the current outbuff on the outQ * Put the session on the outreadyQ if it is not already there * Try to allocate a new buffer for the session * * INPUTS: * scb == session control block * * OUTPUTS: * scb->outbuff, scb->outready, and scb->outQ will be changed * * RETURNS: * status, could return malloc or buffers exceeded error *********************************************************************/ extern status_t ses_msg_new_output_buff (ses_cb_t *scb); /******************************************************************** * FUNCTION ses_msg_make_inready * * Put the session on the inreadyQ if it is not already there * * INPUTS: * scb == session control block * * OUTPUTS: * scb->inready will be queued on the inreadyQ *********************************************************************/ extern void ses_msg_make_inready (ses_cb_t *scb); extern void ses_msg_unmake_inready (ses_cb_t *scb); /******************************************************************** * FUNCTION ses_msg_make_outready * * Put the session on the outreadyQ if it is not already there * * INPUTS: * scb == session control block * * OUTPUTS: * scb->outready will be queued on the outreadyQ *********************************************************************/ extern void ses_msg_make_outready (ses_cb_t *scb); extern void ses_msg_unmake_outready (ses_cb_t *scb); /******************************************************************** * FUNCTION ses_msg_finish_outmsg * * Put the outbuff in the outQ if non-empty * Put the session on the outreadyQ if it is not already there * * INPUTS: * scb == session control block * * OUTPUTS: * scb->outready will be queued on the outreadyQ *********************************************************************/ extern void ses_msg_finish_outmsg (ses_cb_t *scb); /******************************************************************** * FUNCTION ses_msg_get_first_inready * * Dequeue the first entry in the inreadyQ, if any * * RETURNS: * first entry in the inreadyQ or NULL if none *********************************************************************/ extern ses_ready_t * ses_msg_get_first_inready (void); /******************************************************************** * FUNCTION ses_msg_get_first_outready * * Dequeue the first entry in the outreadyQ, if any * * RETURNS: * first entry in the outreadyQ or NULL if none *********************************************************************/ extern ses_ready_t * ses_msg_get_first_outready (void); /******************************************************************** * FUNCTION ses_msg_dump * * Dump the message contents * * INPUTS: * msg == message to dump * text == start text before message dump (may be NULL) * *********************************************************************/ extern void ses_msg_dump (const ses_msg_t *msg, const xmlChar *text); /******************************************************************** * FUNCTION ses_msg_add_framing * * Add the base:1.1 framing chars to the buffer and adjust * the buffer size pointers * * INPUTS: * scb == session control block * buff == buffer control block * * OUTPUTS: * framing chars added to buff->buff * *********************************************************************/ extern void ses_msg_add_framing (ses_cb_t *scb, ses_msg_buff_t *buff); /******************************************************************** * FUNCTION ses_msg_init_buff * * Init the buffer fields * * INPUTS: * scb == session control block * outbuff == TRUE if oupput buffer; FALSE if input buffer * buff == buffer to send *********************************************************************/ extern void ses_msg_init_buff (ses_cb_t *scb, boolean outbuff, ses_msg_buff_t *buff); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_ses_msg */ yuma123_2.14/netconf/src/ncx/yang_parse.h0000664000175000017500000000567514770023131020454 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yang_parse #define _H_yang_parse /* FILE: yang_parse.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* YANG Module parser module Data type conversion ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 26-oct-07 abb Begun; start from ncx_parse.h */ #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION yang_parse_from_filespec * * Parse a file as a YANG module * * Error messages are printed by this function!! * * INPUTS: * filespec == absolute path or relative path * This string is used as-is without adjustment. * pcb == parser control block used as very top-level struct * ptyp == parser call type * YANG_PT_TOP == called from top-level file * YANG_PT_INCLUDE == called from an include-stmt in a file * YANG_PT_IMPORT == called from an import-stmt in a file * isyang == TRUE if a YANG file is expected * FALSE if a YIN file is expected * * OUTPUTS: * an ncx_module is filled out and validated as the file * is parsed. If no errors: * TOP, IMPORT: * the module is loaded into the definition registry with * the ncx_add_to_registry function * INCLUDE: * the submodule is loaded into the top-level module, * specified in the pcb * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_parse_from_filespec (const xmlChar *filespec, yang_pcb_t *pcb, yang_parsetype_t ptyp, boolean isyang); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yang_parse */ yuma123_2.14/netconf/src/ncx/ncxmod.h0000664000175000017500000012606014770023131017604 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ncxmod #define _H_ncxmod /* FILE: ncxmod.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Module Load Manager - manages NCX module search path - loads NCX module files by module name ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 10-nov-05 abb Begun 22-jan-08 abb Add support for YANG import and include Unlike NCX, forward references are allowed so import/include loops have to be tracked and prevented 16-feb-08 abb Changed environment variables from NCX to YANG Added YANG_INSTALL envvar as well. 22-jul-08 abb Remove NCX support -- YANG only from now on 06-oct-09 abb Change YANG_ env vars to YUMA_ */ #ifndef _H_cap #include "cap.h" #endif #ifndef _H_help #include "help.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* max user-configurable directories for NCX and YANG modules */ #define NCXMOD_MAX_SEARCHPATH 64 /* maximum abolute filespec */ #define NCXMOD_MAX_FSPEC_LEN 2047 /* path, file separator char */ #define NCXMOD_PSCHAR '/' #define NCXMOD_HMCHAR '~' #define NCXMOD_ENVCHAR '$' #define NCXMOD_DOTCHAR '.' /* name of the NCX module containing agent boot parameters * loaded during startup */ #define NCXMOD_NETCONFD (const xmlChar *)"netconfd" #define NCXMOD_NETCONFD_EX (const xmlChar *)"netconfd-ex" #define NCXMOD_NCX (const xmlChar *)"yuma-ncx" #define NCXMOD_WITH_DEFAULTS (const xmlChar *)"ietf-netconf-with-defaults" #define NCXMOD_YANGCLI (const xmlChar *)"yangcli" /* name of the NETCONF module containing NETCONF protocol definitions, * that is loaded by default during startup */ #define NCXMOD_NETCONF (const xmlChar *)"yuma123-netconf" #define NCXMOD_YUMA_NETCONF (const xmlChar *)"yuma123-netconf" #define NCXMOD_IETF_NETCONF (const xmlChar *)"ietf-netconf" #define NCXMOD_IETF_NETCONF_REVISION (const xmlChar *)"2011-06-01" #define NCXMOD_IETF_NETCONF_ACM (const xmlChar *)"ietf-netconf-acm" #define NCXMOD_IETF_YANG_TYPES (const xmlChar *)"ietf-yang-types" #define NCXMOD_IETF_NETCONF_STATE (const xmlChar *)"ietf-netconf-monitoring" /* name of the NCX modules directory appended when YUMA_HOME or HOME * ENV vars used to construct NCX module filespec */ #define NCXMOD_DIR (const xmlChar *)"modules" /* name of the data direectory when YUMA_HOME or HOME * ENV vars used to construct an NCX filespec */ #define NCXMOD_DATA_DIR (const xmlChar *)"data" /* name of the scripts direectory when YUMA_HOME or HOME * ENV vars used to construct a NCX filespec */ #define NCXMOD_SCRIPT_DIR (const xmlChar *)"scripts" /* STD Environment Variable for user home directory */ #define NCXMOD_PWD "PWD" /* STD Environment Variable for user home directory */ #define USER_HOME "HOME" /* NCX Environment Variable for YANG/NCX user work home directory */ #define NCXMOD_HOME "YUMA_HOME" /* NCX Environment Variable for tools install directory * The default is /usr/share/yuma */ #define NCXMOD_INSTALL "YUMA_INSTALL" #define NCXMOD_DEFAULT_INSTALL (const xmlChar *)YUMA_DATAROOTDIR #define NCXMOD_DEFAULT_YUMALIB (const xmlChar *)NETCONFMODULEDIR #define NCXMOD_ETC_DATA (const xmlChar *)SYSCONFDIR"/yuma" /* NCX Environment Variable for MODULE search path */ #define NCXMOD_MODPATH "YUMA_MODPATH" /* NCX Environment Variable for DATA search path */ #define NCXMOD_DATAPATH "YUMA_DATAPATH" /* NCX Environment Variable for SCRIPTS search path */ #define NCXMOD_RUNPATH "YUMA_RUNPATH" /* per user yangcli internal data home when $HOME defined */ #define NCXMOD_YUMA_DIR (const xmlChar *)"~/.yuma" /* per user yangcli internal data home when $HOME not defined */ #define NCXMOD_TEMP_YUMA_DIR (const xmlChar *)"/tmp/yuma" /* Yuma work directory name */ #define NCXMOD_YUMA_DIRNAME (const xmlChar *)".yuma" /* sub-directory name yangcli uses to store local per-session workdirs * appended to ncxmod_yumadir_path */ #define NCXMOD_TEMP_DIR (const xmlChar *)"/tmp" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* following 3 structs used for providing temporary * work directories for yangcli sessions */ /* program-level temp dir control block */ typedef struct ncxmod_temp_progcb_t_ { dlq_hdr_t qhdr; xmlChar *source; dlq_hdr_t temp_sescbQ; /* Q of ncxmod_temp_sescb_t */ } ncxmod_temp_progcb_t; /* session-level temp-dir control block */ typedef struct ncxmod_temp_sescb_t_ { dlq_hdr_t qhdr; xmlChar *source; uint32 sidnum; dlq_hdr_t temp_filcbQ; /* Q of ncxmod_temp_filcb_t */ } ncxmod_temp_sescb_t; /* temporary file control block */ typedef struct ncxmod_temp_filcb_t_ { dlq_hdr_t qhdr; xmlChar *source; const xmlChar *filename; /* ptr into source */ } ncxmod_temp_filcb_t; /* struct for storing YANG file search results * this is used by yangcli for schema auto-load * also for finding newest version, or all versions * within the module path */ typedef struct ncxmod_search_result_t_ { dlq_hdr_t qhdr; xmlChar *module; /* module or submodule name */ xmlChar *belongsto; /* set if submodule & belongs-to found */ xmlChar *revision; /* set if most recent revision found */ xmlChar *namespacestr; /* set if module & namespace found */ xmlChar *source; /* file location */ ncx_module_t *mod; /* back-ptr to found module if loaded */ status_t res; /* search result, only use if NO_ERR */ uint32 nslen; /* length of base part of namespacestr */ cap_rec_t *cap; /* back-ptr to source capability URI */ val_value_t *module_val; /* back-ptr to yang-library /module-state/module */ val_value_t *submodule_val; /* back-ptr to yang-library /module-state/module/submodule */ ncx_list_t devlist; /* deviation module names from modules-state */ boolean capmatch; /* set by yangcli; internal use only */ boolean ismod; /* TRUE=module; FALSE=submodule */ } ncxmod_search_result_t; /********************************************************************** * ncxmod_callback_fn_t * * user function callback template to process a module * during a subtree traversal * * Used by the ncxmod_process_subtree function * * DESCRIPTION: * Handle the current filename in the subtree traversal * Parse the module and generate. * * INPUTS: * fullspec == absolute or relative path spec, with filename and ext. * this regular file exists, but has not been checked for * read access of * cookie == opaque handle passed from start of callbacks * * RETURNS: * status * * Return fatal error to stop the traversal or NO_ERR to * keep the traversal going. Do not return any warning or * recoverable error, just log and move on *********************************************************************/ typedef status_t (*ncxmod_callback_fn_t) (const char *fullspec, void *cookie); /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION ncxmod_init * * Initialize the ncxmod module * * RETURNS: * status *********************************************************************/ extern status_t ncxmod_init (void); /******************************************************************** * FUNCTION ncxmod_cleanup * * Cleanup the ncxmod module * *********************************************************************/ extern void ncxmod_cleanup (void); /******************************************************************** * FUNCTION ncxmod_load_module * * Determine the location of the specified module * and then load it into the system, if not already loaded * * This is the only load module variant that checks if there * are any errors recorded in the module or any of its dependencies * !!! ONLY RETURNS TRUE IF MODULE AND ALL IMPORTS ARE LOADED OK !!! * * Module Search order: * * 1) YUMA_MODPATH environment var (or set by modpath CLI var) * 2) current dir or absolute path * 3) YUMA_HOME/modules directory * 4) HOME/modules directory * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * savedevQ == Q of ncx_save_deviations_t to use, if any * retmod == address of return module (may be NULL) * * OUTPUTS: * if non-NULL: * *retmod == pointer to requested module version * * RETURNS: * status *********************************************************************/ extern status_t ncxmod_load_module (const xmlChar *modname, const xmlChar *revision, dlq_hdr_t *savedevQ, ncx_module_t **retmod); /******************************************************************** * FUNCTION ncxmod_parse_module * * Determine the location of the specified module * and then parse the file into an ncx_module_t, * but do not load it into the module registry * * Used by yangcli to build per-session view of each * module advertised by the NETCONF server * * Module Search order: * * 1) YUMA_MODPATH environment var (or set by modpath CLI var) * 2) current dir or absolute path * 3) YUMA_HOME/modules directory * 4) HOME/modules directory * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * savedevQ == Q of ncx_save_deviations_t to use, if any * retmod == address of return module * * OUTPUTS: * *retmod == pointer to requested module version * THIS IS A LIVE MALLOCED STRUCT THAT NEEDS * FREED LATER * * RETURNS: * status *********************************************************************/ extern status_t ncxmod_parse_module (const xmlChar *modname, const xmlChar *revision, dlq_hdr_t *savedevQ, ncx_module_t **retmod); /******************************************************************** * FUNCTION ncxmod_find_module * * Determine the location of the specified module * and then fill out a search result struct * DO NOT load it into the system * * Module Search order: * * 1) YUMA_MODPATH environment var (or set by modpath CLI var) * 2) current dir or absolute path * 3) YUMA_HOME/modules directory * 4) HOME/modules directory * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * * RETURNS: * == malloced and filled in search result struct * MUST call ncxmod_free_search_result() if the * return value is non-NULL * CHECK result->res TO SEE IF MODULE WAS FOUND * A SEARCH RESULT WILL BE RETURNED IF A SEARCH WAS * ATTEMPTED, EVEN IF NOTHING FOUND * == NULL if any error preventing a search *********************************************************************/ extern ncxmod_search_result_t * ncxmod_find_module (const xmlChar *modname, const xmlChar *revision); /******************************************************************** * FUNCTION ncxmod_find_all_modules * * Determine the location of all possible YANG modules and submodules * within the configured YUMA_MODPATH and default search path * All files with .yang and .yin file extensions found in the * search directories will be checked. * * Does not cause modules to be fully parsed and registered. * Quick parse only is done, and modules are discarded. * Strings from the module are copied into the searchresult struct * * Module Search order: * * 1) YUMA_MODPATH environment var (or set by modpath CLI var) * 2) HOME/modules directory * 3) YUMA_HOME/modules directory * 4) YUMA_INSTALL/modules directory * * INPUTS: * resultQ == address of Q to stor malloced search results * * OUTPUTS: * resultQ may have malloced ncxmod_zsearch_result_t structs * queued into it representing the modules found in the search * * RETURNS: * status *********************************************************************/ extern status_t ncxmod_find_all_modules (dlq_hdr_t *resultQ); /******************************************************************** * FUNCTION ncxmod_load_deviation * * Determine the location of the specified module * and then parse it in the special deviation mode * and save any deviation statements in the Q that is provided * * Module Search order: * * 1) YUMA_MODPATH environment var (or set by modpath CLI var) * 2) current dir or absolute path * 3) YUMA_HOME/modules directory * 4) HOME/modules directory * * INPUTS: * devname == deviation module name with * no path prefix or file extension * deviationQ == address of Q of ncx_save_deviations_t structs * to add any new entries * * OUTPUTS: * if non-NULL: * deviationQ has malloced ncx_save_deviations_t structs added * * RETURNS: * status *********************************************************************/ extern status_t ncxmod_load_deviation (const xmlChar *deviname, dlq_hdr_t *deviationQ); /******************************************************************** * * FUNCTION ncxmod_process_deviation_imports * * * * Iterate over the deviation module's imports and fill in the * * module structure for each. ncxmod_load_deviation() skips loading * * imported modules. This function should be called after all * * modules have already been loaded. * * * * INPUTS: * * savedev == deviation structure to update * * * * RETURNS: * * status * *********************************************************************/ extern status_t ncxmod_process_deviation_imports (ncx_save_deviations_t *savedev); /******************************************************************** * FUNCTION ncxmod_resolve_deviations * * Iterate over the saved deviations and resolve those deviations * that target the current module. * * INPUTS: * mod == the module in which deviations should be resolved * savedevQ == a queue of deviations to try * * RETURNS: * status *********************************************************************/ status_t ncxmod_resolve_deviations(ncx_module_t *mod, dlq_hdr_t *savedevQ); /******************************************************************** * FUNCTION ncxmod_apply_deviations * * Iterate over the saved deviations and apply those deviations * that target the current module. * * INPUTS: * mod == the module to which deviations should be applied * savedevQ == a queue of deviations to try * * RETURNS: * status *********************************************************************/ status_t ncxmod_apply_deviations(ncx_module_t *mod); /******************************************************************** * FUNCTION ncxmod_load_imodule * * Determine the location of the specified module * and then load it into the system, if not already loaded * * Called from an include or import or submodule * Includes the YANG parser control block and new parser source type * * Module Search order: * * 1) YUMA_MODPATH environment var (or set by modpath CLI var) * 2) current dir or absolute path * 3) YUMA_HOME/modules directory * 4) HOME/modules directory * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * pcb == YANG parser control block * ptyp == YANG parser source type * parent == pointer to module being parsed if this is a * a request to parse a submodule; there is only 1 parent for * all submodules, based on the value of belongs-to * retmod == address of return module * OUTPUTS: * *retmod == pointer to found module (if NO_ERR) * * RETURNS: * status *********************************************************************/ extern status_t ncxmod_load_imodule (const xmlChar *modname, const xmlChar *revision, yang_pcb_t *pcb, yang_parsetype_t ptyp, ncx_module_t *parent, ncx_module_t **retmod); /******************************************************************** * FUNCTION ncxmod_load_module_ex * * Determine the location of the specified module * and then load it into the system, if not already loaded * Return the PCB instead of deleting it * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * with_submods == TRUE if YANG_PT_TOP mode should skip submodules * == FALSE if top-level mode should process sub-modules * savetkc == TRUE if the parse chain should be saved (e.g., YIN) * keepmode == TRUE if pcb->top should be saved even if it * is already loaded; FALSE will allow the mod * to be swapped out and the new copy deleted * yangdump sets this to true * docmode == TRUE if need to preserve strings for --format=html or yang * == FALSE if no need to preserve string token sequences * savedevQ == Q of ncx_save_deviations_t to use * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced parser control block, or NULL of none *********************************************************************/ extern yang_pcb_t * ncxmod_load_module_ex (const xmlChar *modname, const xmlChar *revision, boolean with_submods, boolean savetkc, boolean keepmode, boolean docmode, dlq_hdr_t *savedevQ, status_t *res); /******************************************************************** * FUNCTION ncxmod_load_module_diff * * Determine the location of the specified module * and then load it into the system, if not already loaded * Return the PCB instead of deleting it * !!Do not add definitions to the registry!! * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * with_submods == TRUE if YANG_PT_TOP mode should skip submodules * == FALSE if top-level mode should process sub-modules * modpath == module path to override the modpath CLI var or * the YUMA_MODPATH env var * savedevQ == Q of ncx_save_deviations_t to use * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced parser control block, or NULL of none *********************************************************************/ extern yang_pcb_t * ncxmod_load_module_diff (const xmlChar *modname, const xmlChar *revision, boolean with_submods, const xmlChar *modpath, dlq_hdr_t *savedevQ, status_t *res); /******************************************************************** * FUNCTION ncxmod_find_data_file * * Determine the location of the specified data file * * Search order: * * 1) YUMA_DATAPATH environment var (or set by datapath CLI var) * 2) current directory or absolute path * 3) HOME/data directory * 4) YUMA_HOME/data directory * 5) HOME/.yuma/ directory * 6a) YUMA_INSTALL/data directory OR * 6b) /usr/share/yuma/data directory * 7) /etc/yuma directory * * INPUTS: * fname == file name with extension * if the first char is '.' or '/', then an absolute * path is assumed, and the search path will not be tries * generrors == TRUE if error message should be generated * FALSE if no error message * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * pointer to the malloced and initialized string containing * the complete filespec or NULL if not found * It must be freed after use!!! *********************************************************************/ extern xmlChar * ncxmod_find_data_file (const xmlChar *fname, boolean generrors, status_t *res); /******************************************************************** * FUNCTION ncxmod_find_sil_file * * Determine the location of the specified * server instrumentation library file * * Search order: * * 1) $YUMA_HOME/target/lib directory * 2) $YUMA_RUNPATH environment variable * 3) $YUMA_INSTALL/lib * 4) $YUMA_INSTALL/lib/yuma * 5) /usr/lib64/yuma directory (LIB64 only) * 5 or 6) /usr/lib/yuma directory * * INPUTS: * fname == SIL file name with extension * generrors == TRUE if error message should be generated * FALSE if no error message * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * pointer to the malloced and initialized string containing * the complete filespec or NULL if not found * It must be freed after use!!! *********************************************************************/ extern xmlChar * ncxmod_find_sil_file (const xmlChar *fname, boolean generrors, status_t *res); /******************************************************************** * FUNCTION ncxmod_make_data_filespec * * Determine a suitable path location for the specified data file name * * Search order: * * 1) YUMA_DATAPATH environment var (or set by datapath CLI var) * 2) HOME/data directory * 3) YUMA_HOME/data directory * 4) HOME/.yuma directory * 5) YUMA_INSTALL/data directory * 6) current directory * * INPUTS: * fname == file name with extension * if the first char is '.' or '/', then an absolute * path is assumed, and the search path will not be tries * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * pointer to the malloced and initialized string containing * the complete filespec or NULL if no suitable location * for the datafile is found * It must be freed after use!!! *********************************************************************/ extern xmlChar * ncxmod_make_data_filespec (const xmlChar *fname, status_t *res); /******************************************************************** * FUNCTION ncxmod_make_data_filespec_from_src * * Determine the directory path portion of the specified * source_url and change the filename to the specified filename * in a new copy of the complete filespec * * INPUTS: * srcspec == source filespec to use * fname == file name with extension * if the first char is '.' or '/', then an absolute * path is assumed, and the search path will not be tries * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * pointer to the malloced and initialized string containing * the complete filespec or NULL if some error occurred *********************************************************************/ extern xmlChar * ncxmod_make_data_filespec_from_src (const xmlChar *srcspec, const xmlChar *fname, status_t *res); /******************************************************************** * FUNCTION ncxmod_find_script_file * * Determine the location of the specified script file * * Search order: * * 1) current directory or absolute path * 2) YUMA_RUNPATH environment var (or set by runpath CLI var) * 3) HOME/scripts directory * 4) YUMA_HOME/scripts directory * 5) YUMA_INSTALL/scripts directory * * INPUTS: * fname == file name with extension * if the first char is '.' or '/', then an absolute * path is assumed, and the search path will not be tries * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * pointer to the malloced and initialized string containing * the complete filespec or NULL if not found * It must be freed after use!!! *********************************************************************/ extern xmlChar * ncxmod_find_script_file (const xmlChar *fname, status_t *res); /******************************************************************** * FUNCTION ncxmod_set_home * * Override the HOME env var with the home CLI var * * THIS MAY GET SET DURING BOOTSTRAP SO SET_ERROR NOT CALLED !!! * MALLOC FAILED IGNORED!!! * * INPUTS: * home == new HOME value * == NULL or empty string to disable *********************************************************************/ extern void ncxmod_set_home (const xmlChar *home); /******************************************************************** * FUNCTION ncxmod_get_home * * Get the HOME or --home parameter value, * whichever is in effect, if any * * RETURNS: * const point to the home variable, or NULL if not set *********************************************************************/ extern const xmlChar * ncxmod_get_home (void); /******************************************************************** * FUNCTION ncxmod_set_yuma_home * * Override the YUMA_HOME env var with the yuma-home CLI var * * THIS MAY GET SET DURING BOOTSTRAP SO SET_ERROR NOT CALLED !!! * MALLOC FAILED IGNORED!!! * * INPUTS: * yumahome == new YUMA_HOME value * == NULL or empty string to disable *********************************************************************/ extern void ncxmod_set_yuma_home (const xmlChar *yumahome); /******************************************************************** * FUNCTION ncxmod_get_yuma_home * * Get the YUMA_HOME or --yuma-home parameter value, * whichever is in effect, if any * * RETURNS: * const point to the yuma_home variable, or NULL if not set *********************************************************************/ extern const xmlChar * ncxmod_get_yuma_home (void); /******************************************************************** * FUNCTION ncxmod_get_yuma_install * * Get the YUMA_INSTALL or default install parameter value, * whichever is in effect * * RETURNS: * const point to the YUMA_INSTALL value *********************************************************************/ extern const xmlChar * ncxmod_get_yuma_install (void); /******************************************************************** * FUNCTION ncxmod_set_modpath * * Override the YUMA_MODPATH env var with the modpath CLI var * * THIS MAY GET SET DURING BOOTSTRAP SO SET_ERROR NOT CALLED !!! * MALLOC FAILED IGNORED!!! * * INPUTS: * modpath == new YUMA_MODPATH value * == NULL or empty string to disable *********************************************************************/ extern void ncxmod_set_modpath (const xmlChar *modpath); /******************************************************************** * FUNCTION ncxmod_set_datapath * * Override the YUMA_DATAPATH env var with the datapath CLI var * * INPUTS: * datapath == new YUMA_DATAPATH value * == NULL or empty string to disable * *********************************************************************/ extern void ncxmod_set_datapath (const xmlChar *datapath); /******************************************************************** * FUNCTION ncxmod_set_runpath * * Override the YUMA_RUNPATH env var with the runpath CLI var * * INPUTS: * datapath == new YUMA_RUNPATH value * == NULL or empty string to disable * *********************************************************************/ extern void ncxmod_set_runpath (const xmlChar *runpath); /******************************************************************** * FUNCTION ncxmod_set_subdirs * * Set the subdirs flag to FALSE if the no-subdirs CLI param is set * * INPUTS: * usesubdirs == TRUE if subdirs searchs should be done * == FALSE if subdir searches should not be done *********************************************************************/ extern void ncxmod_set_subdirs (boolean usesubdirs); /******************************************************************** * FUNCTION ncxmod_get_yumadir * * Get the yuma directory being used * * RETURNS: * pointer to the yuma dir string *********************************************************************/ extern const xmlChar * ncxmod_get_yumadir (void); /******************************************************************** * FUNCTION ncxmod_process_subtree * * Search the entire specified subtree, looking for YANG * modules. Invoke the callback function for each module * file found * * INPUTS: * startspec == absolute or relative pathspec to start * the search. If this is not a valid pathname, * processing will exit immediately. * callback == address of the ncxmod_callback_fn_t function * to use for this traveral * cookie == cookie to pass to each invocation of the callback * * OUTPUTS: * *done == TRUE if done processing * FALSE to keep going * RETURNS: * NO_ERR if file found okay, full filespec in the 'buff' variable * OR some error if not found or buffer overflow *********************************************************************/ extern status_t ncxmod_process_subtree (const char *startspec, ncxmod_callback_fn_t callback, void *cookie); /******************************************************************** * FUNCTION ncxmod_test_subdir * * Check if the specified string is a directory * * INPUTS: * dirspec == string to check as a directory spec * * RETURNS: * TRUE if the string is a directory spec that this user * is allowed to open * FALSE otherwise *********************************************************************/ extern boolean ncxmod_test_subdir (const xmlChar *dirspec); /******************************************************************** * FUNCTION ncxmod_get_userhome * * Get the user home dir from the passwd file * * INPUTS: * user == user name string (may not be zero-terminiated) * userlen == length of user * * RETURNS: * const pointer to the user home directory string *********************************************************************/ extern const xmlChar * ncxmod_get_userhome (const xmlChar *user, uint32 userlen); /******************************************************************** * FUNCTION ncxmod_get_envvar * * Get the specified shell environment variable * * INPUTS: * name == name of the environment variable (may not be zero-terminiated) * namelen == length of name string * * RETURNS: * const pointer to the specified environment variable value *********************************************************************/ extern const xmlChar * ncxmod_get_envvar (const xmlChar *name, uint32 namelen); /******************************************************************** * FUNCTION ncxmod_set_altpath * * Set the alternate path that should be used first (for yangdiff) * * INPUTS: * altpath == full path string to use * must be static * a const back-pointer is kept, not a copy * *********************************************************************/ extern void ncxmod_set_altpath (const xmlChar *altpath); /******************************************************************** * FUNCTION ncxmod_clear_altpath * * Clear the alternate path so none is used (for yangdiff) * *********************************************************************/ extern void ncxmod_clear_altpath (void); /******************************************************************** * FUNCTION ncxmod_list_data_files * * List the available data files found in the data search parh * * Search order: * * 1) current directory or absolute path * 2) YUMA_DATAPATH environment var (or set by datapath CLI var) * 3) HOME/data directory * 4) YUMA_HOME/data directory * 5) YUMA_INSTALL/data directory * * INPUTS: * helpmode == BRIEF, NORMAL or FULL * logstdout == TRUE to use log_stdout * FALSE to use log_write * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncxmod_list_data_files (help_mode_t helpmode, boolean logstdout); /******************************************************************** * FUNCTION ncxmod_list_script_files * * List the available script files found in the 'run' search parh * * Search order: * * 1) current directory or absolute path * 2) YUMA_RUNPATH environment var (or set by datapath CLI var) * 3) HOME/scripts directory * 4) YUMA_HOME/scripts directory * 5) YUMA_INSTALL/scripts directory * * INPUTS: * helpmode == BRIEF, NORMAL or FULL * logstdout == TRUE to use log_stdout * FALSE to use log_write * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncxmod_list_script_files (help_mode_t helpmode, boolean logstdout); /******************************************************************** * FUNCTION ncxmod_list_yang_files * * List the available YANG files found in the 'mod' search parh * * Search order: * * 1) current directory or absolute path * 2) YUMA_MODPATH environment var (or set by datapath CLI var) * 3) HOME/modules directory * 4) YUMA_HOME/modules directory * 5) YUMA_INSTALL/modules directory * * INPUTS: * helpmode == BRIEF, NORMAL or FULL * logstdout == TRUE to use log_stdout * FALSE to use log_write * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncxmod_list_yang_files (help_mode_t helpmode, boolean logstdout); /******************************************************************** * FUNCTION ncxmod_setup_yumadir * * Setup the ~/.yuma directory if it does not exist * * RETURNS: * status *********************************************************************/ extern status_t ncxmod_setup_yumadir (void); /******************************************************************** * FUNCTION ncxmod_setup_tempdir * * Setup the ~/.yuma/tmp directory if it does not exist * * RETURNS: * status *********************************************************************/ extern status_t ncxmod_setup_tempdir (void); /******************************************************************** * FUNCTION ncxmod_new_program_tempdir * * Setup a program instance temp files directory * * INPUTS: * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced tempdir_progcb_t record (if NO_ERR) *********************************************************************/ extern ncxmod_temp_progcb_t * ncxmod_new_program_tempdir (status_t *res); /******************************************************************** * FUNCTION ncxmod_free_program_tempdir * * Remove a program instance temp files directory * * INPUTS: * progcb == tempoeray program instance control block to free * *********************************************************************/ extern void ncxmod_free_program_tempdir (ncxmod_temp_progcb_t *progcb); /******************************************************************** * FUNCTION ncxmod_new_session_tempdir * * Setup a session instance temp files directory * * INPUTS: * progcb == program instance control block to use * sid == manager session ID of the new session instance control block * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced tempdir_sescb_t record *********************************************************************/ extern ncxmod_temp_sescb_t * ncxmod_new_session_tempdir (ncxmod_temp_progcb_t *progcb, uint32 sidnum, status_t *res); /******************************************************************** * FUNCTION ncxmod_free_session_tempdir * * Clean and free a session instance temp files directory * * INPUTS: * progcb == program instance control block to use * sidnum == manager session number to delete * *********************************************************************/ extern void ncxmod_free_session_tempdir (ncxmod_temp_progcb_t *progcb, uint32 sidnum); /******************************************************************** * FUNCTION ncxmod_new_session_tempfile * * Setup a session instance temp file for writing * * INPUTS: * sescb == session instance control block to use * filename == filename to create in the temp directory * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced tempdir_sescb_t record *********************************************************************/ extern ncxmod_temp_filcb_t * ncxmod_new_session_tempfile (ncxmod_temp_sescb_t *sescb, const xmlChar *filename, status_t *res); /******************************************************************** * FUNCTION ncxmod_free_session_tempfile * * Clean and free a session instance temp files directory * * INPUTS: * filcb == file control block to delete * *********************************************************************/ extern void ncxmod_free_session_tempfile (ncxmod_temp_filcb_t *filcb); /******************************************************************** * FUNCTION ncxmod_new_search_result * * Malloc and initialize a search result struct * * RETURNS: * malloced and initialized struct, NULL if ERR_INTERNAL_MEM *********************************************************************/ extern ncxmod_search_result_t * ncxmod_new_search_result (void); /******************************************************************** * FUNCTION ncxmod_new_search_result_ex * * Malloc and initialize a search result struct * * INPUTS: * mod == module struct to use * * RETURNS: * malloced and initialized struct, NULL if ERR_INTERNAL_MEM *********************************************************************/ extern ncxmod_search_result_t * ncxmod_new_search_result_ex (const ncx_module_t *mod); /******************************************************************** * FUNCTION ncxmod_new_search_result_str * * Malloc and initialize a search result struct * * INPUTS: * modname == module name string to use * revision == revision date to use (may be NULL) * RETURNS: * malloced and initialized struct, NULL if ERR_INTERNAL_MEM *********************************************************************/ extern ncxmod_search_result_t * ncxmod_new_search_result_str (const xmlChar *modname, const xmlChar *revision); /******************************************************************** * FUNCTION ncxmod_free_search_result * * Clean and free a search result struct * * INPUTS: * searchresult == struct to clean and free *********************************************************************/ extern void ncxmod_free_search_result (ncxmod_search_result_t *searchresult); /******************************************************************** * FUNCTION ncxmod_clean_search_result_queue * * Clean and free all the search result structs * in the specified Q * * INPUTS: * searchQ = Q of ncxmod_search_result_t to clean and free *********************************************************************/ extern void ncxmod_clean_search_result_queue (dlq_hdr_t *searchQ); /******************************************************************** * FUNCTION ncxmod_find_search_result * * Find a search result inthe specified Q * * Either modname or nsuri must be set * If modname is set, then revision will be checked * * INPUTS: * searchQ = Q of ncxmod_search_result_t to check * modname == module or submodule name to find * revision == revision-date to find * nsuri == namespace URI fo find * RETURNS: * pointer to first matching record; NULL if not found *********************************************************************/ extern ncxmod_search_result_t * ncxmod_find_search_result (dlq_hdr_t *searchQ, const xmlChar *modname, const xmlChar *revision, const xmlChar *nsuri); /******************************************************************** * FUNCTION ncxmod_clone_search_result * * Clone a search result * * INPUTS: * sr = searchresult to clone * * RETURNS: * pointer to malloced and filled in clone of sr *********************************************************************/ extern ncxmod_search_result_t * ncxmod_clone_search_result (const ncxmod_search_result_t *sr); /******************************************************************** * FUNCTION ncxmod_test_filespec * * Check the exact filespec to see if it a file * * INPUTS: * filespec == file spec to check * * RETURNS: * TRUE if valid readable file * FALSE otherwise *********************************************************************/ extern boolean ncxmod_test_filespec (const xmlChar *filespec); /******************************************************************** * FUNCTION ncxmod_get_pathlen_from_filespec * * Get the length of th path part of the filespec string * * INPUTS: * filespec == file spec to check * * RETURNS: * number of chars to keep for the path spec *********************************************************************/ extern uint32 ncxmod_get_pathlen_from_filespec (const xmlChar *filespec); /******************************************************************** * FUNCTION ncxmod123_find_module_filespec * * Determine the location of the specified module * * Module Search order: * 1) current directory * 2) YUMA_MODPATH environment var (or set by modpath CLI var) * 3) HOME/modules directory * 4) YUMA_HOME/modules directory * 5) YUMA_INSTALL/modules directory OR * 6) default install module location, which is '/usr/share/yuma/modules' * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find ** * RETURNS: * NULL if no match was found or pointer to allocated string * containing the path * *********************************************************************/ extern xmlChar* ncxmod123_find_module_filespec(const xmlChar *modname, const xmlChar *revision); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_ncxmod */ yuma123_2.14/netconf/src/ncx/blob.h0000664000175000017500000000455414770023131017235 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_blob #define _H_blob /* FILE: blob.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* binary to string conversion for database storage ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 22-apr-05 abb begun */ #ifdef __cplusplus extern "C" { #endif /******************************************************************** * FUNCTION blob2bin * * Convert a mySQL BLOB to a binary string * * INPUTS: * pblob == pointer to BLOB to convert; must be of the * same type as the binary object; this blob will be * bsize * 2 + 1 bytes in length * pbuff == pointer to buffer to fill in; must be at least * bsize+1 bytes in length * bsize == binary object size * OUTPUTS: * pbuff is filled in *********************************************************************/ extern void blob2bin (const char *pblob, unsigned char *pbuff, uint32 bsize); /******************************************************************** * FUNCTION bin2blob * * Convert a binary string to to a mySQL BLOB * * INPUTS: * pbuff == pointer to buffer to convert; must be at least * bsize+1 bytes in length * pblob == pointer to BLOB to fill in; must be at least * bsize * 2 + 1 bytes in length * bsize == binary object size * OUTPUTS: * pblob is filled in, and zero-terminated *********************************************************************/ extern void bin2blob (const unsigned char *pbuff, char *pblob, uint32 bsize); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_blob */ yuma123_2.14/netconf/src/ncx/ncx_list.c0000664000175000017500000007342714770023131020142 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: ncx_list.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17feb10 abb begun; split out from ncx.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_list #include "ncx_list.h" #endif #ifndef _H_ncx_num #include "ncx_num.h" #endif #ifndef _H_ncx_str #include "ncx_str.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION ncx_new_list * * Malloc Initialize an allocated ncx_list_t * * INPUTS: * btyp == type of list desired * * RETURNS: * pointer to new entry, or NULL if memory error *********************************************************************/ ncx_list_t * ncx_new_list (ncx_btype_t btyp) { ncx_list_t *list; list = m__getObj(ncx_list_t); if (list) { ncx_init_list(list, btyp); } return list; } /* ncx_new_list */ /******************************************************************** * FUNCTION ncx_init_list * * Initialize an allocated ncx_list_t * * INPUTS: * list == pointer to ncx_list_t memory * btyp == base type for the list *********************************************************************/ void ncx_init_list (ncx_list_t *list, ncx_btype_t btyp) { #ifdef DEBUG if (!list) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif list->btyp = btyp; dlq_createSQue(&list->memQ); } /* ncx_init_list */ /******************************************************************** * FUNCTION ncx_clean_list * * Scrub the memory of a ncx_list_t but do not delete it * * INPUTS: * list == ncx_list_t struct to clean *********************************************************************/ void ncx_clean_list (ncx_list_t *list) { ncx_lmem_t *lmem; if (!list || list->btyp == NCX_BT_NONE) { return; } while (!dlq_empty(&list->memQ)) { lmem = (ncx_lmem_t *)dlq_deque(&list->memQ); ncx_clean_lmem(lmem, list->btyp); m__free(lmem); } list->btyp = NCX_BT_NONE; /* leave the list->memQ ready to use again */ } /* ncx_clean_list */ /******************************************************************** * FUNCTION ncx_free_list * * Clean and free an allocated ncx_list_t * * INPUTS: * list == pointer to ncx_list_t memory *********************************************************************/ void ncx_free_list (ncx_list_t *list) { #ifdef DEBUG if (!list) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif ncx_clean_list(list); m__free(list); } /* ncx_free_list */ /******************************************************************** * FUNCTION ncx_list_cnt * * Get the number of entries in the list * * INPUTS: * list == pointer to ncx_list_t memory * RETURNS: * number of entries counted *********************************************************************/ uint32 ncx_list_cnt (const ncx_list_t *list) { const ncx_lmem_t *lmem; uint32 cnt; #ifdef DEBUG if (!list) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif cnt = 0; for (lmem = (const ncx_lmem_t *)dlq_firstEntry(&list->memQ); lmem != NULL; lmem = (const ncx_lmem_t *)dlq_nextEntry(lmem)) { cnt++; } return cnt; } /* ncx_list_cnt */ /******************************************************************** * FUNCTION ncx_list_empty * * Check if the list is empty or not * * INPUTS: * list == pointer to ncx_list_t memory * RETURNS: * TRUE if list is empty * FALSE otherwise *********************************************************************/ boolean ncx_list_empty (const ncx_list_t *list) { #ifdef DEBUG if (!list) { SET_ERROR(ERR_INTERNAL_PTR); return TRUE; } #endif return dlq_empty(&list->memQ); } /* ncx_list_empty */ /******************************************************************** * FUNCTION ncx_string_in_list * * Check if the string value is in the list * List type must be string based, or an enum * * INPUTS: * str == string to find in the list * list == slist to check * * RETURNS: * TRUE if string is found; FALSE otherwise *********************************************************************/ boolean ncx_string_in_list (const xmlChar *str, const ncx_list_t *list) { const ncx_lmem_t *lmem; #ifdef DEBUG if (!str || !list) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif /* screen the list base type */ switch (list->btyp) { case NCX_BT_STRING: case NCX_BT_ENUM: case NCX_BT_BITS: break; default: SET_ERROR(ERR_NCX_WRONG_TYPE); return FALSE; } /* search the list for a match */ for (lmem = (const ncx_lmem_t *)dlq_firstEntry(&list->memQ); lmem != NULL; lmem = (const ncx_lmem_t *)dlq_nextEntry(lmem)) { switch (list->btyp) { case NCX_BT_ENUM: if (!xml_strcmp(str, lmem->val.enu.name)) { return TRUE; } break; case NCX_BT_BITS: if (!xml_strcmp(str, lmem->val.bit.name)) { return TRUE; } break; default: if (!xml_strcmp(str, lmem->val.str)) { return TRUE; } } } return FALSE; } /* ncx_string_in_list */ /******************************************************************** * FUNCTION ncx_compare_lists * * Compare 2 ncx_list_t struct contents * * Expected data type (NCX_BT_SLIST) * * INPUTS: * list1 == first number * list2 == second number * RETURNS: * -1 if list1 is < list2 * 0 if list1 == list2 (also for error, after SET_ERROR called) * 1 if list1 is > list2 *********************************************************************/ int32 ncx_compare_lists (const ncx_list_t *list1, const ncx_list_t *list2) { const ncx_lmem_t *s1, *s2; int retval; #ifdef DEBUG if (!list1 || !list2) { SET_ERROR(ERR_INTERNAL_PTR); return -1; } if (list1->btyp != list2->btyp) { SET_ERROR(ERR_INTERNAL_VAL); return -1; } #endif /* get start strings */ s1 = (const ncx_lmem_t *)dlq_firstEntry(&list1->memQ); s2 = (const ncx_lmem_t *)dlq_firstEntry(&list2->memQ); /* have 2 start structs to compare */ for (;;) { if (!s1 && !s2) { return 0; } else if (!s1) { return -1; } else if (!s2) { return 1; } if (typ_is_string(list1->btyp)) { retval = ncx_compare_strs(&s1->val.str, &s2->val.str, NCX_BT_STRING); } else if (typ_is_number(list1->btyp)) { retval = ncx_compare_nums(&s1->val.num, &s2->val.num, list1->btyp); } else { switch (list1->btyp) { case NCX_BT_BITS: retval = ncx_compare_bits(&s1->val.bit, &s2->val.bit); break; case NCX_BT_ENUM: retval = ncx_compare_enums(&s1->val.enu, &s2->val.enu); break; default: SET_ERROR(ERR_INTERNAL_VAL); return 0; } } switch (retval) { case -1: return -1; case 0: break; case 1: return 1; default: SET_ERROR(ERR_INTERNAL_VAL); return 0; } s1 = (const ncx_lmem_t *)dlq_nextEntry(s1); s2 = (const ncx_lmem_t *)dlq_nextEntry(s2); } /*NOTREACHED*/ } /* ncx_compare_lists */ /******************************************************************** * FUNCTION ncx_copy_list * * Copy the contents of list1 to list2 * Supports base type NCX_BT_SLIST * * A partial copy may occur, and list2 should be properly cleaned * and freed, even if an error is returned * * INPUTS: * list1 == first list * list2 == second list * * RETURNS: * status *********************************************************************/ status_t ncx_copy_list (const ncx_list_t *list1, ncx_list_t *list2) { const ncx_lmem_t *lmem; ncx_lmem_t *lcopy; status_t res; #ifdef DEBUG if (!list1 || !list2) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; list2->btyp = list1->btyp; dlq_createSQue(&list2->memQ); /* go through all the list members and copy each one */ for (lmem = (const ncx_lmem_t *)dlq_firstEntry(&list1->memQ); lmem != NULL; lmem = (const ncx_lmem_t *)dlq_nextEntry(lmem)) { lcopy = ncx_new_lmem(); if (!lcopy) { return ERR_INTERNAL_MEM; } /* copy the string or number from lmem to lcopy */ switch (list1->btyp) { case NCX_BT_STRING: res = ncx_copy_str(&lmem->val.str, &lcopy->val.str, list1->btyp); break; case NCX_BT_BITS: lcopy->val.bit.pos = lmem->val.bit.pos; lcopy->val.bit.dname = xml_strdup(lmem->val.bit.name); if (!lcopy->val.bit.dname) { res = ERR_INTERNAL_MEM; } else { lcopy->val.bit.name = lcopy->val.bit.dname; } break; case NCX_BT_ENUM: lcopy->val.enu.val = lmem->val.enu.val; lcopy->val.enu.dname = xml_strdup(lmem->val.enu.name); if (!lcopy->val.enu.dname) { res = ERR_INTERNAL_MEM; } else { lcopy->val.enu.name = lcopy->val.enu.dname; } break; case NCX_BT_BOOLEAN: lcopy->val.boo = lmem->val.boo; break; default: if (typ_is_number(list1->btyp)) { res = ncx_copy_num(&lmem->val.num, &lcopy->val.num, list1->btyp); } else { res = SET_ERROR(ERR_INTERNAL_VAL); } } if (res != NO_ERR) { ncx_free_lmem(lcopy, list1->btyp); return res; } /* save lcopy in list2 */ dlq_enque(lcopy, &list2->memQ); } return NO_ERR; } /* ncx_copy_list */ /******************************************************************** * FUNCTION ncx_merge_list * * The merge function is handled specially for lists. * The contents are not completely replaced like a string. * Instead, only new entries from src are added to the dest list. * * NCX merge algorithm for lists: * * If list types not the same, then error exit; * * If allow_dups == FALSE: * check if entry exists; if so, exit; * * Merge src list member into dest, based on mergetyp enum * } * * INPUTS: * src == ncx_list_t struct to merge from * dest == ncx_list_t struct to merge into * mergetyp == type of merge used for this list * allow_dups == TRUE if this list allows duplicate values * * OUTPUTS: * ncx_lmem_t structs will be moved from the src to dest as needed * * RETURNS: * none *********************************************************************/ void ncx_merge_list (ncx_list_t *src, ncx_list_t *dest, ncx_merge_t mergetyp, boolean allow_dups) { ncx_lmem_t *lmem, *dest_lmem; if (!src || !dest) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (src->btyp != dest->btyp) { SET_ERROR(ERR_INTERNAL_VAL); return; } /* get rid of dups in the src list if duplicates not allowed */ if (!allow_dups) { for (dest_lmem = (ncx_lmem_t *)dlq_firstEntry(&dest->memQ); dest_lmem != NULL; dest_lmem = (ncx_lmem_t *)dlq_nextEntry(dest_lmem)) { lmem = ncx_find_lmem(src, dest_lmem); if (lmem) { dlq_remove(lmem); ncx_free_lmem(lmem, dest->btyp); } } } /* transfer the source members to the dest list */ while (!dlq_empty(&src->memQ)) { /* pick an entry to merge, reverse of the merge type * to preserve the source order in the dest list */ switch (mergetyp) { case NCX_MERGE_FIRST: lmem = (ncx_lmem_t *)dlq_lastEntry(&src->memQ); break; case NCX_MERGE_LAST: case NCX_MERGE_SORT: lmem = (ncx_lmem_t *)dlq_firstEntry(&src->memQ); break; default: SET_ERROR(ERR_INTERNAL_VAL); return; } if (lmem) { dlq_remove(lmem); /* merge lmem into the dest list */ ncx_insert_lmem(dest, lmem, mergetyp); } /* else should not happen since dlq_empty is false */ } } /* ncx_merge_list */ /******************************************************************** * FUNCTION ncx_set_strlist * * consume a generic string list with no type checking * Convert a text line into an ncx_list_t using NCX_BT_STRING * as the list type. Must call ncx_init_list first !!! * * INPUTS: * liststr == list value in string form * list == ncx_list_t that should be initialized and * filled with the values from the string * * RETURNS: * status *********************************************************************/ status_t ncx_set_strlist (const xmlChar *liststr, ncx_list_t *list) { #ifdef DEBUG if (!liststr || !list) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif ncx_init_list(list, NCX_BT_STRING); return ncx_set_list(NCX_BT_STRING, liststr, list); } /* ncx_set_strlist */ /******************************************************************** * FUNCTION ncx_set_list * * consume a generic string list with base type checking * Parse the XML input as an NCX_BT_SLIST * Do not check the individual strings against any restrictions * Just check that the strings parse as the expected type. * Mark list members with errors as needed * * Must call ncx_init_list first!!! * * INPUTS: * btyp == expected basetype for the list member type * strval == cleaned XML string to parse into ncx_str_t or * ncx_num_t values * list == ncx_list_t in progress that will get the ncx_lmem_t * structs added to it, as they are parsed * * OUTPUTS: * list->memQ has 1 or more ncx_lmem_t structs appended to it * * RETURNS: * status *********************************************************************/ status_t ncx_set_list (ncx_btype_t btyp, const xmlChar *strval, ncx_list_t *list) { const xmlChar *str1 = strval; const xmlChar *str2 = NULL; ncx_lmem_t *lmem; uint32 len; boolean done = FALSE; boolean checkexists; if (!strval || !list) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!*strval) { return NO_ERR; } /* probably already set but make sure */ list->btyp = btyp; checkexists = !dlq_empty(&list->memQ); while (!done) { /* skip any leading whitespace */ while( xml_isspace( *str1 ) ) { ++str1; } if (!*str1) { done = TRUE; continue; } /* if str1 starts with a double quote, parse the string as * a whitespace-allowed string */ if (NCX_STR_START == *str1) { ++str1; str2 = str1; while (*str2 && (NCX_STR_END != *str2 )) { str2++; } len = (uint32)( str2 - str1 ); if (*str2) { str2++; } else { log_info("\nncx_set_list: missing EOS marker\n (%s)", str1); } } else { /* consume string until a WS, str-start, or EOS seen */ str2 = str1+1; while (*str2 && !xml_isspace(*str2) && (NCX_STR_START != *str2 )) { str2++; } len = (uint32)(str2-str1); } /* set up a new list string struct */ lmem = ncx_new_lmem(); if (!lmem) { return ERR_INTERNAL_MEM; } /* copy the string just parsed for now just separate into strings and * do not validate or parse into enums or numbers */ lmem->val.str = xml_strndup(str1, len); if (!lmem->val.str) { ncx_free_lmem(lmem, NCX_BT_STRING); return ERR_INTERNAL_MEM; } if (checkexists && ncx_string_in_list(lmem->val.str, list)) { /* The entry is already present, discard it */ ncx_free_lmem(lmem, NCX_BT_STRING); } else { /* save the list member in the Q */ dlq_enque(lmem, &list->memQ); } /* reset the string pointer and loop */ str1 = str2; } return NO_ERR; } /* ncx_set_list */ /******************************************************************** * FUNCTION ncx_finish_list * * 2nd pass of parsing a ncx_list_t * Finish converting the list members to the proper format * * INPUTS: * typdef == typ_def_t for the designated list member type * list == list struct with ncx_lmem_t structs to check * * OUTPUTS: * If return other than NO_ERR: * each list->lmem.flags field may contain bits set * for errors: * NCX_FL_RANGE_ERR: size out of range * NCX_FL_VALUE_ERR value not permitted by value set, * or pattern * RETURNS: * status *********************************************************************/ status_t ncx_finish_list (typ_def_t *typdef, ncx_list_t *list) { ncx_lmem_t *lmem; xmlChar *str; ncx_btype_t btyp; status_t res, retres; dlq_hdr_t tempQ; #ifdef DEBUG if (!typdef || !list) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif btyp = typ_get_basetype(typdef); res = NO_ERR; retres = NO_ERR; /* check if any work to do */ switch (btyp) { case NCX_BT_STRING: case NCX_BT_BOOLEAN: return NO_ERR; default: ; } /* go through all the list members and check them */ for (lmem = (ncx_lmem_t *)dlq_firstEntry(&list->memQ); lmem != NULL; lmem = (ncx_lmem_t *)dlq_nextEntry(lmem)) { str = lmem->val.str; if (btyp == NCX_BT_ENUM) { res = val_enum_ok(typdef, str, &lmem->val.enu.val, &lmem->val.enu.name); } else if (btyp == NCX_BT_BITS) { /* transfer the malloced string from * val.str to val.bit.dname */ lmem->val.bit.dname = str; lmem->val.bit.name = lmem->val.bit.dname; res = val_bit_ok(typdef, str, &lmem->val.bit.pos); } else if (typ_is_number(btyp)){ res = ncx_decode_num(str, btyp, &lmem->val.num); } else { SET_ERROR(ERR_INTERNAL_VAL); } if (btyp != NCX_BT_BITS) { m__free(str); } if (res != NO_ERR) { /* the string did not match this pattern */ CHK_EXIT(res, retres); lmem->flags |= NCX_FL_VALUE_ERR; } } if (retres == NO_ERR && btyp == NCX_BT_BITS) { /* put bits in their canonical order */ dlq_createSQue(&tempQ); dlq_block_enque(&list->memQ, &tempQ); while (!dlq_empty(&tempQ)) { lmem = (ncx_lmem_t *)dlq_deque(&tempQ); ncx_insert_lmem(list, lmem, NCX_MERGE_SORT); } } return retres; } /* ncx_finish_list */ /******************************************************************** * FUNCTION ncx_new_lmem * * Malloc and fill in a new ncx_lmem_t struct * * INPUTS: * none * RETURNS: * pointer to malloced and initialized ncx_lmem_t struct * NULL if malloc error *********************************************************************/ ncx_lmem_t * ncx_new_lmem (void) { ncx_lmem_t *lmem; lmem = m__getObj(ncx_lmem_t); if (!lmem) { return NULL; } memset(lmem, 0x0, sizeof(ncx_lmem_t)); return lmem; } /* ncx_new_lmem */ /******************************************************************** * FUNCTION ncx_clean_lmem * * Scrub the memory of a ncx_lmem_t but do not delete it * * INPUTS: * lmem == ncx_lmem_t struct to clean * btyp == base type of list member (lmem) *********************************************************************/ void ncx_clean_lmem (ncx_lmem_t *lmem, ncx_btype_t btyp) { #ifdef DEBUG if (!lmem) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (typ_is_string(btyp)) { ncx_clean_str(&lmem->val.str); } else if (typ_is_number(btyp)) { ncx_clean_num(btyp, &lmem->val.num); } else { switch (btyp) { case NCX_BT_ENUM: ncx_clean_enum(&lmem->val.enu); break; case NCX_BT_BITS: ncx_clean_bit(&lmem->val.bit); break; case NCX_BT_BOOLEAN: break; default: SET_ERROR(ERR_INTERNAL_VAL); } } } /* ncx_clean_lmem */ /******************************************************************** * FUNCTION ncx_free_lmem * * Free all the memory in a ncx_lmem_t struct * * INPUTS: * lmem == struct to clean and free * btyp == base type of the list member * *********************************************************************/ void ncx_free_lmem (ncx_lmem_t *lmem, ncx_btype_t btyp) { #ifdef DEBUG if (!lmem) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif ncx_clean_lmem(lmem, btyp); m__free(lmem); } /* ncx_free_lmem */ /******************************************************************** * FUNCTION ncx_find_lmem * * Find a the first matching list member with the specified value * * INPUTS: * list == list to check * memval == value to find, based on list->btyp * * RETURNS: * pointer to the first instance of this value, or NULL if none *********************************************************************/ ncx_lmem_t * ncx_find_lmem (ncx_list_t *list, const ncx_lmem_t *memval) { ncx_lmem_t *lmem; const ncx_num_t *num; const ncx_str_t *str; const ncx_enum_t *enu; const ncx_bit_t *bit; int32 cmpval; boolean boo; #ifdef DEBUG if (!list || !memval) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif num = NULL; str = NULL; enu = NULL; bit = NULL; boo = FALSE; if (typ_is_number(list->btyp)) { num = &memval->val.num; } else if (typ_is_string(list->btyp)) { str = &memval->val.str; } else if (list->btyp == NCX_BT_ENUM) { enu = &memval->val.enu; } else if (list->btyp == NCX_BT_BITS) { bit = &memval->val.bit; } else if (list->btyp == NCX_BT_BOOLEAN) { boo = memval->val.boo; } else { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } for (lmem = (ncx_lmem_t *)dlq_firstEntry(&list->memQ); lmem != NULL; lmem = (ncx_lmem_t *)dlq_nextEntry(lmem)) { if (num) { cmpval = ncx_compare_nums(&lmem->val.num, num, list->btyp); } else if (str) { cmpval = ncx_compare_strs(&lmem->val.str, str, list->btyp); } else if (enu) { cmpval = ncx_compare_enums(&lmem->val.enu, enu); } else if (bit) { cmpval = ncx_compare_bits(&lmem->val.bit, bit); } else { cmpval = (lmem->val.boo && boo) ? 0 : 1; } if (!cmpval) { return lmem; } } return NULL; } /* ncx_find_lmem */ /******************************************************************** * FUNCTION ncx_insert_lmem * * Insert a list entry into the specified list * * INPUTS: * list == list to insert into * memval == value to insert, based on list->btyp * mergetyp == requested merge type for the insertion * * RETURNS: * none *********************************************************************/ void ncx_insert_lmem (ncx_list_t *list, ncx_lmem_t *memval, ncx_merge_t mergetyp) { ncx_lmem_t *lmem; const ncx_num_t *num; const ncx_str_t *str; const ncx_enum_t *enu; const ncx_bit_t *bit; int32 cmpval; boolean boo; #ifdef DEBUG if (!list || !memval) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif switch (mergetyp) { case NCX_MERGE_FIRST: lmem = (ncx_lmem_t *)dlq_firstEntry(&list->memQ); if (lmem) { dlq_insertAhead(memval, lmem); } else { dlq_enque(memval, &list->memQ); } break; case NCX_MERGE_LAST: dlq_enque(memval, &list->memQ); break; case NCX_MERGE_SORT: num = NULL; str = NULL; enu = NULL; bit = NULL; boo = FALSE; if (typ_is_number(list->btyp)) { num = &memval->val.num; } else if (typ_is_string(list->btyp)) { str = &memval->val.str; } else if (list->btyp == NCX_BT_ENUM) { enu = &memval->val.enu; } else if (list->btyp == NCX_BT_BITS) { bit = &memval->val.bit; } else if (list->btyp == NCX_BT_BOOLEAN) { boo = memval->val.boo; } else { SET_ERROR(ERR_INTERNAL_VAL); return; } for (lmem = (ncx_lmem_t *)dlq_firstEntry(&list->memQ); lmem != NULL; lmem = (ncx_lmem_t *)dlq_nextEntry(lmem)) { if (num) { cmpval = ncx_compare_nums(&lmem->val.num, num, list->btyp); } else if (str) { cmpval = ncx_compare_strs(&lmem->val.str, str, list->btyp); } else if (enu) { cmpval = ncx_compare_enums(&lmem->val.enu, enu); } else if (bit) { cmpval = ncx_compare_bits(&lmem->val.bit, bit); } else { if (lmem->val.boo) { cmpval = (boo) ? 0 : 1; } else { cmpval = (boo) ? -1 : 0; } } if (cmpval >= 0) { dlq_insertAhead(memval, lmem); return; } } /* make new last entry */ dlq_enque(memval, &list->memQ); break; default: SET_ERROR(ERR_INTERNAL_VAL); return; } } /* ncx_insert_lmem */ /******************************************************************** * FUNCTION ncx_first_lmem * * Return the first list member * * INPUTS: * list == list to check * * RETURNS: * pointer to the first list member or NULL if none *********************************************************************/ ncx_lmem_t * ncx_first_lmem (ncx_list_t *list) { #ifdef DEBUG if (!list) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (ncx_lmem_t *)dlq_firstEntry(&list->memQ); } /* ncx_first_lmem */ /* END file ncx_list.c */ yuma123_2.14/netconf/src/ncx/yin.c0000664000175000017500000001423214770023131017103 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yin.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 23feb08 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yin #include "yin.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static yin_mapping_t yinmap[] = { { YANG_K_ANYXML, YANG_K_NAME, FALSE }, { YANG_K_ARGUMENT, YANG_K_NAME, FALSE }, { YANG_K_AUGMENT, YANG_K_TARGET_NODE, FALSE }, { YANG_K_BASE, YANG_K_NAME, FALSE }, { YANG_K_BELONGS_TO, YANG_K_MODULE, FALSE}, { YANG_K_BIT, YANG_K_NAME, FALSE}, { YANG_K_CASE, YANG_K_NAME, FALSE }, { YANG_K_CHOICE, YANG_K_NAME, FALSE }, { YANG_K_CONFIG, YANG_K_VALUE, FALSE }, { YANG_K_CONTACT, YANG_K_TEXT, TRUE }, { YANG_K_CONTAINER, YANG_K_NAME, FALSE }, { YANG_K_DEFAULT, YANG_K_VALUE, FALSE }, { YANG_K_DESCRIPTION, YANG_K_TEXT, TRUE }, { YANG_K_DEVIATE, YANG_K_VALUE, FALSE }, { YANG_K_DEVIATION, YANG_K_TARGET_NODE, FALSE }, { YANG_K_ENUM, YANG_K_NAME, FALSE }, { YANG_K_ERROR_APP_TAG, YANG_K_VALUE, FALSE }, { YANG_K_ERROR_MESSAGE, YANG_K_VALUE, TRUE }, { YANG_K_EXTENSION, YANG_K_NAME, FALSE }, { YANG_K_FEATURE, YANG_K_NAME, FALSE }, { YANG_K_FRACTION_DIGITS, YANG_K_VALUE, FALSE }, { YANG_K_GROUPING, YANG_K_NAME, FALSE }, { YANG_K_IDENTITY, YANG_K_NAME, FALSE }, { YANG_K_IF_FEATURE, YANG_K_NAME, FALSE }, { YANG_K_IMPORT, YANG_K_MODULE, FALSE }, { YANG_K_INCLUDE, YANG_K_MODULE, FALSE }, { YANG_K_INPUT, NULL, FALSE }, { YANG_K_KEY, YANG_K_VALUE, FALSE }, { YANG_K_LEAF, YANG_K_NAME, FALSE }, { YANG_K_LEAF_LIST, YANG_K_NAME, FALSE }, { YANG_K_LENGTH, YANG_K_VALUE, FALSE }, { YANG_K_LIST, YANG_K_NAME, FALSE }, { YANG_K_MANDATORY, YANG_K_VALUE, FALSE }, { YANG_K_MAX_ELEMENTS, YANG_K_VALUE, FALSE }, { YANG_K_MIN_ELEMENTS, YANG_K_VALUE, FALSE }, { YANG_K_MODULE, YANG_K_NAME, FALSE }, { YANG_K_MUST, YANG_K_CONDITION, FALSE }, { YANG_K_NAMESPACE, YANG_K_URI, FALSE }, { YANG_K_NOTIFICATION, YANG_K_NAME, FALSE }, { YANG_K_ORDERED_BY, YANG_K_VALUE, FALSE }, { YANG_K_ORGANIZATION, YANG_K_TEXT, TRUE }, { YANG_K_OUTPUT, NULL, FALSE }, { YANG_K_PATH, YANG_K_VALUE, FALSE }, { YANG_K_PATTERN, YANG_K_VALUE, FALSE }, { YANG_K_POSITION, YANG_K_VALUE, FALSE }, { YANG_K_PREFIX, YANG_K_VALUE, FALSE }, { YANG_K_PRESENCE, YANG_K_VALUE, FALSE }, { YANG_K_RANGE, YANG_K_VALUE, FALSE }, { YANG_K_REFERENCE, YANG_K_TEXT, TRUE }, { YANG_K_REFINE, YANG_K_TARGET_NODE, FALSE }, { YANG_K_REQUIRE_INSTANCE, YANG_K_VALUE, FALSE }, { YANG_K_REVISION, YANG_K_DATE, FALSE }, { YANG_K_REVISION_DATE, YANG_K_DATE, FALSE }, { YANG_K_RPC, YANG_K_NAME, FALSE }, { YANG_K_STATUS, YANG_K_VALUE, FALSE }, { YANG_K_SUBMODULE, YANG_K_NAME, FALSE }, { YANG_K_TYPE, YANG_K_NAME, FALSE }, { YANG_K_TYPEDEF, YANG_K_NAME, FALSE }, { YANG_K_UNIQUE, YANG_K_TAG, FALSE }, { YANG_K_UNITS, YANG_K_NAME, FALSE }, { YANG_K_USES, YANG_K_NAME, FALSE }, { YANG_K_VALUE, YANG_K_VALUE, FALSE }, { YANG_K_WHEN, YANG_K_CONDITION, FALSE }, { YANG_K_YANG_VERSION, YANG_K_VALUE, FALSE }, { YANG_K_YIN_ELEMENT, YANG_K_VALUE, FALSE }, { NULL, NULL, FALSE } }; /******************************************************************** * FUNCTION find_yin_mapping * * Find a static yin mapping entry * * INPUTS: * name == keyword name to find * * RETURNS: * pointer to found entry, NULL if none found *********************************************************************/ const yin_mapping_t * yin_find_mapping (const xmlChar *name) { const yin_mapping_t *mapping; int i; i = 0; for (mapping = &yinmap[i]; mapping != NULL && mapping->keyword != NULL; mapping = &yinmap[++i]) { if (!xml_strcmp(name, mapping->keyword)) { return mapping; } } return NULL; } /* yin_find_mapping */ /* END file yin.c */ yuma123_2.14/netconf/src/ncx/xmlns.h0000664000175000017500000003515214770023131017456 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xmlns #define _H_xmlns /* FILE: xmlns.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* XML namespace support Applications will register namespaces in order to process XML requests containing elements in different namespaces, as required by the NETCONF protocol and XML 1.0. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30-apr-05 abb Begun. */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define XMLNS_NULL_NS_ID 0 #define XMLNS ((const xmlChar *)"xmlns") #define XMLNS_LEN 5 #define XMLNS_SEPCH ':' /* only compare if both elements have namespaces specified * If either one is 'no namespace', then it is considered a match */ #define XMLNS_EQ(NS1,NS2) (((NS1) && (NS2)) && ((NS1)==(NS2))) /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* integer handle for registered namespaces */ typedef uint32 xmlns_id_t; /* represents one QName data element */ typedef struct xmlns_qname_t_ { xmlns_id_t nsid; const xmlChar *name; } xmlns_qname_t; /* represents one registered namespace */ typedef struct xmlns_t_ { xmlns_id_t ns_id; xmlChar *ns_pfix; xmlChar *ns_name; xmlChar *ns_module; struct ncx_module_t_ *ns_mod; } xmlns_t; /* represents one namespace prefix mapping */ typedef struct xmlns_pmap_t_ { dlq_hdr_t qhdr; xmlns_id_t nm_id; xmlChar *nm_pfix; boolean nm_topattr; } xmlns_pmap_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xmlns_init * * Initialize the module static variables * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void xmlns_init (void); /******************************************************************** * FUNCTION xmlns_cleanup * * Cleanup module static data * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void xmlns_cleanup (void); /******************************************************************** * FUNCTION xmlns_register_ns * * Register the specified namespace. Each namespace or prefix * can only be registered once. An entry must be removed and * added back in order to change it. * * INPUTS: * ns == namespace name * pfix == namespace prefix to use (if none provided but needed) * modname == name string of module associated with this NS * modptr == back-ptr to ncx_module_t struct (may be NULL) * * OUTPUTS: * *ns_id contains the ID assigned to the namespace * * RETURNS: * status, NO_ERR if all okay *********************************************************************/ extern status_t xmlns_register_ns (const xmlChar *ns, const xmlChar *pfix, const xmlChar *modname, void *modptr, xmlns_id_t *ns_id); /******************************************************************** * FUNCTION xmlns_get_ns_prefix * * Get the prefix for the specified namespace * * INPUTS: * ns_id == namespace ID * RETURNS: * pointer to prefix or NULL if bad params *********************************************************************/ extern const xmlChar * xmlns_get_ns_prefix (xmlns_id_t ns_id); /******************************************************************** * FUNCTION xmlns_get_ns_name * * Get the name for the specified namespace * * INPUTS: * ns_id == namespace ID * RETURNS: * pointer to name or NULL if bad params *********************************************************************/ extern const xmlChar * xmlns_get_ns_name (xmlns_id_t ns_id); /******************************************************************** * FUNCTION xmlns_find_ns_by_module * * Find the NS ID from its module name that registered it * * INPUTS: * modname == module name string to find * * RETURNS: * namespace ID or XMLNS_NULL_NS_ID if error *********************************************************************/ extern xmlns_id_t xmlns_find_ns_by_module (const xmlChar *modname); /******************************************************************** * FUNCTION xmlns_find_ns_by_prefix * * Find the NS ID from its prefix * * INPUTS: * pfix == pointer to prefix string * RETURNS: * namespace ID or XMLNS_NULL_NS_ID if error *********************************************************************/ extern xmlns_id_t xmlns_find_ns_by_prefix (const xmlChar *pfix); /******************************************************************** * FUNCTION xmlns_find_ns_by_name * * Find the NS ID from its name * * INPUTS: * name == pointer to name string * RETURNS: * namespace ID or XMLNS_NULL_NS_ID if error *********************************************************************/ extern xmlns_id_t xmlns_find_ns_by_name (const xmlChar *name); /******************************************************************** * FUNCTION xmlns_find_ns_by_name_str * * Find the NS ID from its name (counted string version) * * INPUTS: * name == pointer to name string * namelen == length of name string * * RETURNS: * namespace ID or XMLNS_NULL_NS_ID if error *********************************************************************/ extern xmlns_id_t xmlns_find_ns_by_name_str (const xmlChar *name, uint32 namelen); /******************************************************************** * FUNCTION xmlns_nc_id * * Get the ID for the NETCONF namespace or 0 if it doesn't exist * * INPUTS: * none * RETURNS: * NETCONF NS ID or 0 if not found *********************************************************************/ extern xmlns_id_t xmlns_nc_id (void); /******************************************************************** * FUNCTION xmlns_ncx_id * * Get the ID for the NETCONF Extensions namespace or 0 if it doesn't exist * * INPUTS: * none * RETURNS: * NETCONF-X NS ID or 0 if not found *********************************************************************/ extern xmlns_id_t xmlns_ncx_id (void); /******************************************************************** * FUNCTION xmlns_ns_id * * Get the ID for the XMLNS namespace or 0 if it doesn't exist * * INPUTS: * none * RETURNS: * XMLNS NS ID or 0 if not found *********************************************************************/ extern xmlns_id_t xmlns_ns_id (void); /******************************************************************** * FUNCTION xmlns_inv_id * * Get the INVALID namespace ID * * INPUTS: * none * RETURNS: * INVALID NS ID or 0 if not set yet *********************************************************************/ extern xmlns_id_t xmlns_inv_id (void); /******************************************************************** * FUNCTION xmlns_xs_id * * Get the ID for the XSD namespace or 0 if it doesn't exist * * INPUTS: * none * RETURNS: * XSD NS ID or 0 if not found *********************************************************************/ extern xmlns_id_t xmlns_xs_id (void); /******************************************************************** * FUNCTION xmlns_xsi_id * * Get the ID for the XSD Instance (XSI) namespace or 0 if it doesn't exist * * INPUTS: * none * RETURNS: * XSI ID or 0 if not found *********************************************************************/ extern xmlns_id_t xmlns_xsi_id (void); /******************************************************************** * FUNCTION xmlns_xml_id * * Get the ID for the 1998 XML namespace or 0 if it doesn't exist * * INPUTS: * none * RETURNS: * XML ID or 0 if not found *********************************************************************/ extern xmlns_id_t xmlns_xml_id (void); /******************************************************************** * FUNCTION xmlns_ncn_id * * Get the ID for the NETCONF Notifications namespace or 0 if it * doesn't exist * * INPUTS: * none * RETURNS: * NCN ID or 0 if not found *********************************************************************/ extern xmlns_id_t xmlns_ncn_id (void); /******************************************************************** * FUNCTION xmlns_yang_id * * Get the ID for the YANG namespace or 0 if it * doesn't exist * * INPUTS: * none * RETURNS: * YANG ID or 0 if not found *********************************************************************/ extern xmlns_id_t xmlns_yang_id (void); /******************************************************************** * FUNCTION xmlns_yin_id * * Get the ID for the YIN namespace or 0 if it * doesn't exist * * INPUTS: * none * RETURNS: * YIN ID or 0 if not found *********************************************************************/ extern xmlns_id_t xmlns_yin_id (void); /******************************************************************** * FUNCTION xmlns_wildcard_id * * Get the ID for the base:1.1 wildcard namespace or 0 if it * doesn't exist * * INPUTS: * none * RETURNS: * Wildcard ID or 0 if not found *********************************************************************/ extern xmlns_id_t xmlns_wildcard_id (void); /******************************************************************** * FUNCTION xmlns_wda_id * * Get the ID for the wd:default XML attribute namespace or 0 if it * doesn't exist * * INPUTS: * none * RETURNS: * with-defaults default attribute namespace ID or 0 if not found *********************************************************************/ extern xmlns_id_t xmlns_wda_id (void); /******************************************************************** * FUNCTION xmlns_get_module * * get the module name of the namespace ID * get module name that registered this namespace * * INPUTS: * nsid == namespace ID to check * RETURNS: * none *********************************************************************/ extern const xmlChar * xmlns_get_module (xmlns_id_t nsid); /******************************************************************** * FUNCTION xmlns_get_modptr * * get the module pointer for the namespace ID * * INPUTS: * nsid == namespace ID to check * RETURNS: * void * cast of the module or NULL *********************************************************************/ extern void * xmlns_get_modptr (xmlns_id_t nsid); /******************************************************************** * FUNCTION xmlns_set_modptrs * * get the module pointer for the namespace ID * * INPUTS: * modname == module owner name to find * modptr == ncx_module_t back-ptr to set * *********************************************************************/ extern void xmlns_set_modptrs (const xmlChar *modname, void *modptr); /******************************************************************** * FUNCTION xmlns_new_pmap * * malloc and initialize a new xmlns_pmap_t struct * * INPUTS: * buffsize == size of the prefix buffer to allocate * within this pmap (0 == do not malloc yet) * * RETURNS: * pointer to new struct or NULL if malloc error *********************************************************************/ extern xmlns_pmap_t * xmlns_new_pmap (uint32 buffsize); /******************************************************************** * FUNCTION xmlns_free_pmap * * free a xmlns_pmap_t struct * * INPUTS: * pmap == prefix map struct to free * *********************************************************************/ extern void xmlns_free_pmap (xmlns_pmap_t *pmap); /******************************************************************** * FUNCTION xmlns_new_qname * * malloc and initialize a new xmlns_qname_t struct * * RETURNS: * pointer to new struct or NULL if malloc error *********************************************************************/ extern xmlns_qname_t * xmlns_new_qname (void); /******************************************************************** * FUNCTION xmlns_free_qname * * free a xmlns_qname_t struct * * INPUTS: * qname == QName struct to free * *********************************************************************/ extern void xmlns_free_qname (xmlns_qname_t *qname); /******************************************************************** * FUNCTION xmlns_ids_equal * * compare 2 namespace IDs only if they are both non-zero * and return TRUE if they are equal * * INPUTS: * ns1 == namespace ID 1 * ns2 == namespace ID 2 * * RETURNS: * TRUE if equal or both IDs are not zero * FALSE if both IDs are non-zero and they are different *********************************************************************/ extern boolean xmlns_ids_equal (xmlns_id_t ns1, xmlns_id_t ns2); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xmlns */ yuma123_2.14/netconf/src/ncx/val_util.c0000664000175000017500000031745514770023131020140 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: val_util.c val_value_t struct utilities for object validateion support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 19dec05 abb begun 21jul08 abb start obj-based rewrite ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include "procdefs.h" #include "cfg.h" #include "cli.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "ncx_feature.h" #include "ncx_list.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "status.h" #include "typ.h" #include "val.h" #include "val123.h" #include "val_util.h" #include "xml_util.h" #include "xpath.h" #include "xpath1.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* #define VAL_UTIL_DEBUG_CANONICAL 1 */ /******************************************************************** * FUNCTION new_index * * Malloc and initialize the fields in a val_index_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ static val_index_t * new_index (val_value_t *valnode) { val_index_t *in; in = m__getObj(val_index_t); if (!in) { return NULL; } in->val = valnode; return in; } /* new_index */ /******************************************************************** * FUNCTION choice_check * * Check a val_value_t struct against its expected OBJ * for instance validation: * * - choice validation: * only one case allowed if the data type is choice * Only issue errors based on the instance test qualifiers * * The input is checked against the specified obj_template_t. * * INPUTS: * val == parent val_value_t to check * choicobj == object template for the choice to check * * OUTPUTS: * log any errors encountered * * RETURNS: * status of the operation, NO_ERR if no validation errors found *********************************************************************/ static status_t choice_check (val_value_t *val, obj_template_t *choicobj) { val_value_t *chval, *testval; status_t res, retres; retres = NO_ERR; /* Go through all the child nodes for this object * and look for choices against the value set to see if each * a choice case is present in the correct number of instances. * * The current value could never be a OBJ_TYP_CHOICE since * those nodes are not stored in the val_value_t tree * Instead, it is the parent of the choice object, * and the accessible case nodes will be child nodes * of that complex parent type */ chval = val_get_choice_first_set(val, choicobj); if (!chval) { if (obj_is_mandatory(choicobj)) { /* error missing choice */ retres = ERR_NCX_MISSING_CHOICE; log_error("\nError: Nothing selected for " "mandatory choice '%s'", obj_get_name(choicobj)); ncx_print_errormsg(NULL, NULL, retres); } return retres; } /* else a choice was selected * first make sure all the mandatory case * objects are present */ res = val_instance_check(val, chval->casobj); if (res != NO_ERR) { retres = res; } /* check if any objects from other cases are present */ testval = val_get_choice_next_set(choicobj, chval); while (testval) { if (val123_get_case_for_choice(choicobj, testval) != val123_get_case_for_choice(choicobj, chval)) { /* error: extra case object in this choice */ retres = ERR_NCX_EXTRA_CHOICE; log_error("\nError: Extra object '%s' " "in choice '%s'; Case '%s' already selected", testval->name, obj_get_name(choicobj), obj_get_name(chval->casobj)); ncx_print_errormsg(NULL, NULL, retres); } testval = val_get_choice_next_set(choicobj, testval); } return retres; } /* choice_check */ /******************************************************************** * FUNCTION add_defaults * * Go through the specified value struct and add in any defaults * for missing leaf and choice nodes, that have defaults. * * !!! Only the child nodes will be checked for missing defaults * !!! The top-level value passed to this function is assumed to * !!! be already set * * This function does not handle top-level choice object subtrees. * This special case must be handled with the datadefQ * for the module. If a top-level leaf value is passed in, * which is from a top-level choice case-arm, then the * rest of the case-arm objects will not get added by * this function. * * It is assumed that even top-level data-def-stmts will * be handled within a container, so the top-level * object should always a container. * * INPUTS: * val == the value struct to modify * rootval == the root value for XPath purposes * == NULL to skip when-stmt check * cxtval == the context value for XPath purposes * == NULL to use val instead * scriptmode == TRUE if the value is a script object access * == FALSE for normal val_get_simval access instead * addcas == obj_template for OBJ_TYP_CASE when adding defaults * to a case * * OUTPUTS: * *val and any sub-nodes are set to the default value as requested * * RETURNS: * status *********************************************************************/ static status_t add_defaults (val_value_t *val, val_value_t *rootval, val_value_t *cxtval, boolean scriptmode, obj_template_t *addcas) { #ifdef DEBUG /* test in static fn because it is so recursive */ if (!val || !val->obj) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif obj_template_t *obj, *chobj; if (addcas) { obj = addcas; } else { obj = val->obj; } status_t res = NO_ERR; /* skip any uses or augment nodes */ if (!obj_has_name(obj)) { return NO_ERR; } /* go through each child object node and determine * if a child value node exists for it or not * * If not, then determine if a default is needed * and available * * Objects without children will drop through the loop * All the uses and augment nodes will be skipped, */ for (chobj = obj_first_child(obj); chobj != NULL && res == NO_ERR; chobj = obj_next_child(chobj)) { const xmlChar *defval = NULL; val_value_t *chval = NULL, *testval = NULL, *chcxtval = NULL; obj_template_t *casobj = NULL; uint32 whencount = 0; boolean condresult = FALSE; switch (chobj->objtype) { case OBJ_TYP_ANYXML: break; case OBJ_TYP_LEAF: /* first check if this leaf even has a default */ defval = obj_get_default(chobj); if (!defval) { continue; } /* check if the child leaf is a config node */ if (!obj_is_config(chobj)) { continue; } /* check if this leaf has a false when-stmt associated * with it; skip this leaf if when FALSE */ if (rootval) { res = val_check_obj_when((cxtval) ? cxtval : val, rootval, NULL, chobj, &condresult, &whencount); if (res != NO_ERR) { return res; } if (whencount && !condresult) { if (LOGDEBUG3) { log_debug3("\nadd_default: skipping false when '%s:%s'", obj_get_mod_name(chobj), obj_get_name(chobj)); } continue; } } /* node is not conditional or when=TRUE * check if the node exists alread */ chval = val_find_child(val, obj_get_mod_name(chobj), obj_get_name(chobj)); if (!chval) { res = cli_parse_parm(NULL, val, chobj, defval, scriptmode); if (res==NO_ERR) { chval = val_find_child(val, obj_get_mod_name(chobj), obj_get_name(chobj)); if (!chval) { SET_ERROR(ERR_INTERNAL_VAL); } else { if (LOGDEBUG4) { log_debug4("\nadd default leaf '%s:%s'", val_get_mod_name(chval), chval->name); } chval->flags |= VAL_FL_DEFSET; } } } break; case OBJ_TYP_CHOICE: /* if the choice is not set, and it has a default * case, then add that case to the val struct * If a partial case is present, then try to fill in * any of the nodes with defaults */ if (obj_is_mandatory(chobj)) { break; } /* get the default case for this choice (if any) */ casobj = obj_get_default_case(chobj); /* check if the choice has been set at all */ testval = val_get_choice_first_set(val, chobj); if (testval) { /* use the selected case instead of the default case */ casobj = testval->casobj; if (!casobj) { res = SET_ERROR(ERR_INTERNAL_VAL); } } if (casobj) { /* add all the default nodes in the default case * or selected case */ res = add_defaults(val, rootval, cxtval, scriptmode, casobj); } break; case OBJ_TYP_LEAF_LIST: /* leaf list objects never get default entries */ break; case OBJ_TYP_CONTAINER: if (obj_is_root(chobj)) { break; } /* else fall through */ case OBJ_TYP_RPCIO: case OBJ_TYP_LIST: case OBJ_TYP_CASE: /* add defaults to the subtrees of existing * complex nodes, but do not add any new ones */ chval = val_find_child(val, obj_get_mod_name(chobj), obj_get_name(chobj)); if (chval) { if (cxtval) { chcxtval = val_first_child_match(cxtval, chval); } res = add_defaults(chval, rootval, chcxtval, scriptmode, NULL); } if (chobj->objtype == OBJ_TYP_LIST) { while (res == NO_ERR && chval) { chval = val_find_next_child(val, obj_get_mod_name(chobj), obj_get_name(chobj), chval); if (chval) { if (chcxtval) { chcxtval = val_next_child_match(cxtval, chval, chcxtval); } res = add_defaults(chval, rootval, chcxtval, scriptmode, NULL); } } } break; case OBJ_TYP_RPC: case OBJ_TYP_NOTIF: default: res = SET_ERROR(ERR_INTERNAL_VAL); } } return res; } /* add_defaults */ /******************************************************************** * FUNCTION get_index_comp * * Get the index component string for the specified value * * USAGE: * call 1st time with a NULL buffer to get the length * call the 2nd time with a buffer of the returned length * * !!!! DOES NOT CHECK BUFF OVERRUN IF buff is non-NULL !!!! * * INPUTS: * mhdr == message hdr w/ prefix map or NULL to just use * the internal prefix mappings * format == desired output format * val == val_value_t for table or container * buff = buffer to hold result; NULL == get length only * * OUTPUTS: * mhdr.pmap may have entries added if prefixes used * in the instance identifier which are not already in the pmap * *len = number of bytes that were (or would have been) written * to buff * RETURNS: * status *********************************************************************/ static status_t get_index_comp (xml_msg_hdr_t *mhdr, ncx_instfmt_t format, const val_value_t *val, xmlChar *buff, uint32 *len) { const xmlChar *prefix; uint32 cnt, total; status_t res; boolean quotes; total = 0; /* get the data type to determine if a quoted string is needed */ if (format==NCX_IFMT_XPATH1 || format==NCX_IFMT_XPATH2) { quotes = TRUE; } else if (typ_is_string(val->btyp)) { quotes = (format==NCX_IFMT_CLI) ? val_need_quotes(VAL_STR(val)) : TRUE; } else if (typ_is_number(val->btyp)) { quotes = FALSE; } else { switch (val->btyp) { case NCX_BT_ENUM: quotes = val_need_quotes(VAL_ENUM_NAME(val)); break; case NCX_BT_BOOLEAN: quotes = FALSE; break; case NCX_BT_BINARY: case NCX_BT_BITS: quotes = TRUE; break; case NCX_BT_UNION: quotes = TRUE; break; case NCX_BT_IDREF: quotes = FALSE; break; default: return SET_ERROR(ERR_INTERNAL_VAL); } } /* check if foo:parmname='parmval' format is needed */ if (format==NCX_IFMT_XPATH1 || format==NCX_IFMT_XPATH2) { if (mhdr) { prefix = xml_msg_get_prefix_xpath(mhdr, val->nsid); } else { prefix = xmlns_get_ns_prefix(val->nsid); } if (!prefix) { return ERR_INTERNAL_MEM; } if (buff) { buff += xml_strcpy(buff, prefix); *buff++ = ':'; } total += xml_strlen(prefix); total++; if (buff) { buff += xml_strcpy(buff, val->name); } total += xml_strlen(val->name); if (buff) { *buff++ = VAL_EQUAL_CH; } total++; } if (quotes) { if (buff) { *buff++ = (xmlChar)((format==NCX_IFMT_XPATH1) ? VAL_QUOTE_CH : VAL_DBLQUOTE_CH); } total++; } res = val_sprintf_simval_nc(buff, val, &cnt); if (res != NO_ERR) { return SET_ERROR(res); } if (buff) { buff += cnt; } total += cnt; if (quotes) { if (buff) { *buff++ = (xmlChar)((format==NCX_IFMT_XPATH1) ? VAL_QUOTE_CH : VAL_DBLQUOTE_CH); } total++; } /* end the string */ if (buff) { *buff = 0; } *len = total; return NO_ERR; } /* get_index_comp */ /******************************************************************** * FUNCTION get_instance_string * * Get the instance ID string for the specified value * * USAGE: * call 1st time with a NULL buffer to get the length * call the 2nd time with a buffer of the returned length * * !!!! DOES NOT CHECK BUFF OVERRUN IF buff is non-NULL !!!! * * INPUTS: * mhdr == message hdr w/ prefix map or NULL to just use * the internal prefix mappings * format == desired output format * val == value node to generate instance string for * stop_at_root == TRUE to stop if a 'root' node is encountered * == FALSE to keep recursing all the way to * buff = buffer to hold result; NULL == get length only * len == address of return length * * OUTPUTS: * mhdr.pmap may have entries added if prefixes used * in the instance identifier which are not already in the pmap * *len = number of bytes that were (or would have been) written * to buff * * RETURNS: * status *********************************************************************/ static status_t get_instance_string (xml_msg_hdr_t *mhdr, ncx_instfmt_t format, const val_value_t *val, boolean stop_at_root, xmlChar *buff, uint32 *len) { const xmlChar *name = NULL, *prefix = NULL, *ncprefix = NULL; xmlChar numbuff[NCX_MAX_NUMLEN+1]; uint32 cnt = 0, childcnt = 0, total = 0; status_t res = NO_ERR; boolean root = FALSE, skiproot = FALSE; xmlns_id_t rpcid = 0; /* process the specific node type * Recurively find the top node and start there */ if (stop_at_root && val->obj && obj_is_root(val->obj)) { root = TRUE; skiproot = TRUE; } else if (val->parent && val->parent->obj) { if (stop_at_root && obj_is_root(val->parent->obj)) { root = TRUE; } } else { root = TRUE; } if (!root) { res = get_instance_string(mhdr, format, val->parent, stop_at_root, buff, &cnt); } *len = 0; if (res != NO_ERR) { return res; } /* make sure not to generate an i-i that starts with /nc:config */ if (val->obj && obj_is_root(val->obj) && val->parent == NULL) { return NO_ERR; } /* move the buffer pointer to the end to append */ if (buff) { buff += cnt; } if (val->obj->objtype == OBJ_TYP_RPCIO) { /* get the prefix and name of the RPC method * instead of this node named 'input' */ rpcid = obj_get_nsid(val->obj->parent); if (rpcid) { if (mhdr) { prefix = xml_msg_get_prefix_xpath(mhdr, rpcid); } else { prefix = xmlns_get_ns_prefix(rpcid); } } name = obj_get_name(val->obj->parent); } else { /* make sure the prefix is in the message header so * an xmlns directive will be generated for this prefix */ if (mhdr) { prefix = xml_msg_get_prefix_xpath(mhdr, val->nsid); } else { prefix = xmlns_get_ns_prefix(val->nsid); } name = val->name; } #ifdef DEBUG if (!prefix || !*prefix || !name || !*name) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif /* check if a path sep char is needed */ if (format == NCX_IFMT_C) { if (cnt) { if (buff) { *buff++ = VAL_INST_SEPCH; } cnt++; } } else { if (buff) { *buff++ = VAL_XPATH_SEPCH; /* starting '/' */ } cnt++; } /* check if the 'rpc' root element needs to be added here */ if (root && val->obj->objtype == OBJ_TYP_RPCIO) { /* copy prefix */ if (mhdr) { ncprefix = xml_msg_get_prefix_xpath(mhdr, xmlns_nc_id()); if (!ncprefix) { return ERR_INTERNAL_MEM; } } else { ncprefix = xmlns_get_ns_prefix(xmlns_nc_id()); if (!ncprefix) { return SET_ERROR(ERR_INTERNAL_VAL); } } if (buff) { buff += xml_strcpy(buff, ncprefix); *buff++ = ':'; } cnt += xml_strlen(ncprefix); cnt++; /* copy name */ if (buff) { buff += xml_strcpy(buff, NCX_EL_RPC); } cnt += xml_strlen(NCX_EL_RPC); /* add another path sep char */ if (buff) { *buff++ = (xmlChar)((format==NCX_IFMT_C) ? VAL_INST_SEPCH : VAL_XPATH_SEPCH); } cnt++; } /* add prefix string for this component */ if (buff && !skiproot) { buff += xml_strcpy(buff, prefix); *buff++ = ':'; } if (!skiproot) { cnt += xml_strlen(prefix); cnt++; } /* add name string for this component */ if (buff && !skiproot) { buff += xml_strcpy(buff, name); } if (!skiproot) { cnt += xml_strlen(name); } total = cnt; /* check if this is a value node with an index clause */ if (!dlq_empty(&val->indexQ)) { cnt = 0; res = val_get_index_string(mhdr, format, val, buff, &cnt); if (res == NO_ERR) { if (buff) { buff[cnt] = 0; } total += cnt; } } else if (val->obj->objtype == OBJ_TYP_LEAF_LIST) { /* leaf-list e.g. /ex:system/ex:services/ex:ssh/ex:cipher[.='blowfish-cbc'] */ int sprinted_cnt; xmlChar *val_str; val_str = val_make_sprintf_string(val); assert(val_str); sprinted_cnt=snprintf((char *)buff, buff?(*len-total):0, "[.='%s']", val_str); free(val_str); if(buff) { buff+=sprinted_cnt; } total += sprinted_cnt; } else if (val->parent && val->obj->objtype == OBJ_TYP_LIST) { /* instance-identifier for a list entry without keys */ int sprinted_cnt; assert(dlq_empty(&val->indexQ)); cnt = val_get_child_inst_id(val->parent, val); sprinted_cnt=snprintf((char *)buff, buff?(*len-total):0, "[%u]", cnt); assert(sprinted_cnt>0); if(buff) { buff+=sprinted_cnt; } total += sprinted_cnt; } /* set the length even if index error, and exit */ *len = total; return res; } /* get_instance_string */ /******************************************************************** * FUNCTION purge_errors * * Remove any error nodes recursively * Need to use raw Q access to find nodes marked deleted * INPUTS: * val == node to purge * *********************************************************************/ static void purge_errors (val_value_t *val) { if (!typ_has_children(val->btyp)) { return; } val_value_t *nextval; val_value_t *chval = (val_value_t *)dlq_firstEntry(&val->v.childQ); for (; chval != NULL; chval = nextval) { nextval = (val_value_t *)dlq_nextEntry(chval); if (chval->res != NO_ERR) { log_debug("\nDeleting error node '%s:%s' (%s)", val_get_mod_name(chval), chval->name, get_error_string(chval->res)); if (obj_is_key(chval->obj)) { val_remove_key(chval); } val_remove_child(chval); val_free_value(chval); } else if (typ_has_children(chval->btyp)) { purge_errors(chval); } } } /* purge_errors */ /******************************************************************** * FUNCTION check_when_stmt * * Check if the specified when-stmt for the specified node * * INPUTS: * val == parent value node of the object node to check * valroot == database root for XPath purposes * context == value node to use for context node * whenstmt == XPath PCB to use * condresult == address of conditional test result * * OUTPUTS: * *condresult == TRUE if conditional is true or there are none * FALSE if conditional test failed * * RETURNS: * status *********************************************************************/ static status_t check_when_stmt (val_value_t *val, val_value_t *valroot, val_value_t *context, xpath_pcb_t *whenstmt, boolean *condresult) { status_t res = NO_ERR; xpath_result_t *result = xpath1_eval_expr(whenstmt, context, valroot, FALSE /* logerrors */, TRUE /* configonly */, &res); if (result == NULL || res != NO_ERR) { *condresult = FALSE; } else { boolean whentest = xpath_cvt_boolean(result); *condresult = whentest; if (LOGDEBUG3) { char* pathbuff; status_t res; res = val_gen_instance_id(NULL, val, NCX_IFMT_XPATH1, (xmlChar **) &pathbuff); if (whentest) { log_debug3("\nval: when test '%s' OK for node '%s' with " "context '%s'", whenstmt->exprstr, pathbuff, context->name); } else { log_debug3("\nval: when test '%s' failed for node '%s' " "with context '%s'", whenstmt->exprstr, pathbuff, context->name); } free(pathbuff); } } xpath_free_result(result); return res; } /* check_when_stmt */ /*************** E X T E R N A L F U N C T I O N S *************/ /******************************************************************** * FUNCTION val_set_canonical_order * * Change the child XML nodes throughout an entire subtree * to the canonical order defined in the object template * * >>> IT IS ASSUMED THAT ONLY VALID NODES ARE PRESENT * >>> AND ALL ERROR NODES HAVE BEEN PURGED ALREADY * * There is no canonical order defined for * the contents of the following nodes: * - anyxml leaf * - ordered-by user leaf-list * * These nodes are not ordered, but their child nodes are ordered * - ncx:root container * - ordered-by user list * * Leaf objects will not be processed, if val is OBJ_TYP_LEAF * Leaf-list objects will not be processed, if val is * OBJ_TYP_LEAF_LIST. These object types must be processed * within the context of the parent object. * * List child key nodes are ordered first among all * of the list's child nodes. * * List nodes with system keys are not kept in sorted order * This is not required by YANG. Instead the user-given * order servers as the canonical order. It is up to * the application setting the config to pick an * order for the list nodes. * * Also, leaf-list order is not changed, regardless of * the order. The default insert order is 'last'. * * INPUTS: * val == value node to change to canonical order * * OUTPUTS: * val->v.childQ may be reordered, for all complex types * in the subtree * *********************************************************************/ void val_set_canonical_order (val_value_t *val) { obj_template_t *chobj; const obj_key_t *key; val_value_t *chval; dlq_hdr_t tempQ; assert( val && "val is NULL!" ); assert( val->obj && "val->obj is NULL!" ); if (val_is_virtual(val)) { return; } boolean skip = FALSE; switch (val->obj->objtype) { case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: case OBJ_TYP_USES: case OBJ_TYP_AUGMENT: case OBJ_TYP_REFINE: skip = TRUE; break; case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: case OBJ_TYP_RPC: SET_ERROR(ERR_INTERNAL_VAL); return; case OBJ_TYP_LIST: case OBJ_TYP_CONTAINER: case OBJ_TYP_RPCIO: case OBJ_TYP_NOTIF: break; default: SET_ERROR(ERR_INTERNAL_VAL); return; } if (skip) { #ifdef VAL_UTIL_DEBUG_CANONICAL log_debug3("\nval_canonical skip '%s'", val->name); #endif return; } #ifdef VAL_UTIL_DEBUG_CANONICAL log_debug3("\nval_canonical start '%s'", val->name); if (LOGDEBUG4) { val_dump_value(val, 0); } #endif /* transfer all the val->childQ nodes to the tempQ */ dlq_createSQue(&tempQ); dlq_block_enque(&val->v.childQ, &tempQ); switch (val->obj->objtype) { case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: break; case OBJ_TYP_USES: case OBJ_TYP_AUGMENT: case OBJ_TYP_REFINE: break; case OBJ_TYP_CHOICE: case OBJ_TYP_CASE: case OBJ_TYP_RPC: SET_ERROR(ERR_INTERNAL_VAL); break; case OBJ_TYP_LIST: /* for a list, put all the key leafs first */ for (key = obj_first_ckey(val->obj); key != NULL; key = obj_next_ckey(key)) { chval = val_find_child_que(&tempQ, obj_get_mod_name(key->keyobj), obj_get_name(key->keyobj)); if (chval) { dlq_remove(chval); val_add_child(chval, val); } } /* fall through to do the rest of the child nodes */ case OBJ_TYP_CONTAINER: if (obj_is_root(val->obj)) { while (!dlq_empty(&tempQ)) { chval = (val_value_t *)dlq_deque(&tempQ); val_add_child_sorted(chval, val); val_set_canonical_order(chval); } break; } /* else fall through to normal container or list case */ case OBJ_TYP_RPCIO: case OBJ_TYP_NOTIF: for (chobj = obj_first_child_deep(val->obj); chobj != NULL; chobj = obj_next_child_deep(chobj)) { if (obj_is_key(chobj)) { continue; } chval = val_find_child_que(&tempQ, obj_get_mod_name(chobj), obj_get_name(chobj)); while (chval) { dlq_remove(chval); val_add_child_sorted(chval, val); switch (chval->obj->objtype) { case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: break; case OBJ_TYP_CONTAINER: if (obj_is_root(chval->obj)) { break; } /* else fall through */ case OBJ_TYP_LIST: val_set_canonical_order(chval); break; default: ; } chval = val_find_child_que(&tempQ, obj_get_mod_name(chobj), obj_get_name(chobj)); } } /* check left over nodes */ if (dlq_count(&tempQ)) { /* could be some error nodes left over not * part of the schema definition */ log_debug("\nset_canonical: %d leftover nodes added " " to end of childQ for val %s", dlq_count(&tempQ), val->name); dlq_block_enque(&tempQ, &val->v.childQ); } break; default: SET_ERROR(ERR_INTERNAL_VAL); } #ifdef VAL_UTIL_DEBUG_CANONICAL log_debug3("\nval_canonical end '%s'", val->name); //log_debug4("\n tempQ count is %u", dlq_count(&tempQ)); if (LOGDEBUG4) { val_dump_value(val, 0); } #endif } /* val_set_canonical_order */ /******************************************************************** * FUNCTION val_gen_index_comp * * Create an index component * * INPUTS: * in == obj_key_t in the chain to process * val == the just parsed table row with the childQ containing * nodes to check as index nodes * * OUTPUTS: * val->indexQ will get a val_index_t record added if return NO_ERR * * RETURNS: * status *********************************************************************/ status_t val_gen_index_comp (const obj_key_t *in, val_value_t *val) { val_value_t *chval; status_t res; boolean found; #ifdef DEBUG if (!in || !val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* 1 or more index components expected */ found = FALSE; res = NO_ERR; for (chval = (val_value_t *)dlq_firstEntry(&val->v.childQ); chval != NULL && !found && res==NO_ERR; chval = (val_value_t *)dlq_nextEntry(chval)) { if (chval->index) { continue; } else if (val_get_nsid(chval) != obj_get_nsid(in->keyobj)) { continue; } else if (!xml_strcmp(obj_get_name(in->keyobj), chval->name)) { res = val_gen_key_entry(chval); if (res == NO_ERR) { found = TRUE; } } } if (res == NO_ERR && !found) { res = ERR_NCX_MISSING_INDEX; } return res; } /* val_gen_index_comp */ /******************************************************************** * FUNCTION val_gen_key_entry * * Create a key record within an index comp * * INPUTS: * in == obj_key_t in the chain to process * keyval == the just parsed table row with the childQ containing * nodes to check as index nodes * * OUTPUTS: * val->indexQ will get a val_index_t record added if return NO_ERR * * RETURNS: * status *********************************************************************/ status_t val_gen_key_entry (val_value_t *keyval) { val_index_t *valin; status_t res; #ifdef DEBUG if (!keyval || !keyval->parent) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; valin = new_index(keyval); if (!valin) { res = ERR_INTERNAL_MEM; } else { /* save the index marker record */ keyval->index = valin; dlq_enque(valin, &keyval->parent->indexQ); } return res; } /* val_gen_key_entry */ /******************************************************************** * FUNCTION val_gen_index_chain * * Create an index chain for the just-parsed table or container struct * * INPUTS: * obj == list object containing the keyQ * val == the just parsed table row with the childQ containing * nodes to check as index nodes * * OUTPUTS: * *val->indexQ has entries added for each index component, if NO_ERR * * RETURNS: * status *********************************************************************/ status_t val_gen_index_chain (const obj_template_t *obj, val_value_t *val) { const obj_key_t *key; status_t res; #ifdef DEBUG if (!obj || !val) { return SET_ERROR(ERR_INTERNAL_PTR); } if (obj->objtype != OBJ_TYP_LIST) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif /* 0 or more index components expected */ for (key = obj_first_ckey(obj); key != NULL; key = obj_next_ckey(key)) { res = val_gen_index_comp(key, val); if (res != NO_ERR) { return res; } } return NO_ERR; } /* val_gen_index_chain */ /******************************************************************** * FUNCTION val_add_defaults * * add defaults to an initialized complex value * Go through the specified value struct and add in any defaults * for missing leaf and choice nodes, that have defaults. * * !!! Only the child nodes will be checked for missing defaults * !!! The top-level value passed to this function is assumed to * !!! be already set * * This function does not handle top-level choice object subtrees. * This special case must be handled with the datadefQ * for the module. If a top-level leaf value is passed in, * which is from a top-level choice case-arm, then the * rest of the case-arm objects will not get added by * this function. * * It is assumed that even top-level data-def-stmts will * be handled within a container, so the top-level * object should always a container. * * INPUTS: * val == the value struct to modify * rootval == the root value for XPath purposes * == NULL to skip when-stmt check * cxtval == the context value for XPath purposes * == NULL to use val instead * scriptmode == TRUE if the value is a script object access * == FALSE for normal val_get_simval access instead * * OUTPUTS: * *val and any sub-nodes are set to the default value as requested * * RETURNS: * status *********************************************************************/ status_t val_add_defaults (val_value_t *val, val_value_t *rootval, val_value_t *cxtval, boolean scriptmode) { return add_defaults(val, rootval, cxtval, scriptmode, NULL); } /* val_add_defaults */ /******************************************************************** * FUNCTION val_instance_check * * Check for the proper number of object instances for * the specified value struct. Checks the direct accessible * children of 'val' only!!! * * The 'obj' parameter is usually the val->obj field * except for choice/case processing * * Log errors as needed and mark val->res as needed * * INPUTS: * val == value to check * * RETURNS: * status *********************************************************************/ status_t val_instance_check (val_value_t *val, obj_template_t *obj) { obj_template_t *chobj; ncx_iqual_t iqual; uint32 cnt, minelems, maxelems; boolean minset, maxset, minerr, maxerr; status_t res, retres; retres = NO_ERR; /* check all the child nodes for correct number of instances */ for (chobj = obj_first_child(obj); chobj != NULL; chobj = obj_next_child(chobj)) { iqual = obj_get_iqualval(chobj); minerr = FALSE; maxerr = FALSE; minelems = 0; maxelems = 0; switch (chobj->objtype) { case OBJ_TYP_LEAF_LIST: minset = chobj->def.leaflist->minset; minelems = chobj->def.leaflist->minelems; maxset = chobj->def.leaflist->maxset; maxelems = chobj->def.leaflist->maxelems; break; case OBJ_TYP_LIST: minset = chobj->def.list->minset; minelems = chobj->def.list->minelems; maxset = chobj->def.list->maxset; maxelems = chobj->def.list->maxelems; break; default: minset = FALSE; maxset = FALSE; } switch (chobj->objtype) { case OBJ_TYP_CHOICE: res = choice_check(val, chobj); if (res != NO_ERR) { retres = res; } continue; case OBJ_TYP_CASE: retres = SET_ERROR(ERR_INTERNAL_VAL); continue; default: cnt = val_instance_count(val, obj_get_mod_name(chobj), obj_get_name(chobj)); } if (minset) { if (cnt < minelems) { /* not enough instances error */ minerr = TRUE; retres = ERR_NCX_MISSING_VAL_INST; log_error("\nError: Not enough instances of object '%s' " "Got '%u', needed '%u'", obj_get_name(chobj), cnt, minelems); ncx_print_errormsg(NULL, NULL, retres); } } if (maxset) { if (cnt > maxelems) { /* too many instances error */ maxerr = TRUE; retres = ERR_NCX_EXTRA_VAL_INST; log_error("\nError: Too many instances of object '%s' entered " "Got '%u', allowed '%u'", obj_get_name(chobj), cnt, maxelems); ncx_print_errormsg(NULL, NULL, retres); } } switch (iqual) { case NCX_IQUAL_ONE: if (cnt < 1 && !minerr) { /* missing single parameter */ retres = ERR_NCX_MISSING_VAL_INST; log_error("\nError: Mandatory object '%s' is missing", obj_get_name(chobj)); ncx_print_errormsg(NULL, NULL, retres); } else if (cnt > 1 && !maxerr) { /* too many parameters */ retres = ERR_NCX_EXTRA_VAL_INST; log_error("\nError: Extra instances of object '%s' entered", obj_get_name(chobj)); ncx_print_errormsg(NULL, NULL, retres); } break; case NCX_IQUAL_OPT: if (cnt > 1 && !maxerr) { /* too many parameters */ retres = ERR_NCX_EXTRA_VAL_INST; log_error("\nError: Extra instances of object '%s' entered", obj_get_name(chobj)); ncx_print_errormsg(NULL, NULL, retres); } break; case NCX_IQUAL_1MORE: if (cnt < 1 && !minerr) { /* missing parameter error */ retres = ERR_NCX_MISSING_VAL_INST; log_error("\nError: Mandatory object '%s' is missing", obj_get_name(chobj)); ncx_print_errormsg(NULL, NULL, retres); } break; case NCX_IQUAL_ZMORE: break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); } } return retres; } /* val_instance_check */ /******************************************************************** * FUNCTION val_get_choice_first_set * * Check a val_value_t struct against its expected OBJ * to determine if a specific choice has already been set * Get the value struct for the first value set for * the specified choice * * INPUTS: * val == val_value_t to check * obj == choice object to check * * RETURNS: * pointer to first value struct or NULL if choice not set *********************************************************************/ val_value_t * val_get_choice_first_set (val_value_t *val, const obj_template_t *obj) { val_value_t *chval; #ifdef DEBUG if (!val || !obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (chval = val_get_first_child(val); chval != NULL; chval = val_get_next_child(chval)) { if (chval->casobj != NULL) { boolean done2 = FALSE; const obj_template_t *testobj = chval->casobj->parent; while (!done2) { if (testobj == obj) { return chval; } else if (testobj != NULL && (testobj->objtype == OBJ_TYP_CHOICE || testobj->objtype == OBJ_TYP_CASE)) { testobj = testobj->parent; } else { done2 = TRUE; } } } } return NULL; } /* val_get_choice_first_set */ /******************************************************************** * FUNCTION val_get_choice_next_set * * Check a val_value_t struct against its expected OBJ * to determine if a specific choice has already been set * Get the value struct for the next value set from the * specified choice, afvter 'curval' * * INPUTS: * val == val_value_t to check * obj == choice object to check * curchild == current child selected from this choice (obj) * * RETURNS: * pointer to first value struct or NULL if choice not set *********************************************************************/ val_value_t * val_get_choice_next_set (const obj_template_t *obj, val_value_t *curchild) { val_value_t *chval; #ifdef DEBUG if (!obj || !curchild) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif for (chval = val_get_next_child(curchild); chval != NULL; chval = val_get_next_child(chval)) { if (chval->casobj && chval->casobj->parent==obj) { return chval; } } return NULL; } /* val_get_choice_next_set */ /******************************************************************** * FUNCTION val_choice_is_set * * Check a val_value_t struct against its expected OBJ * to determine if a specific choice has already been set * Check that all the mandatory config fields in the selected * case are set * * INPUTS: * val == parent of the choice object to check * obj == choice object to check * * RETURNS: * pointer to first value struct or NULL if choice not set *********************************************************************/ boolean val_choice_is_set (val_value_t *val, obj_template_t *obj) { obj_template_t *cas, *child; val_value_t *testval, *chval; boolean done; #ifdef DEBUG if (!val || !obj) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif chval = NULL; done = FALSE; for (testval = val_get_first_child(val); testval != NULL && !done; testval = val_get_next_child(testval)) { if (testval->casobj != NULL) { boolean done2 = FALSE; const obj_template_t *testobj = testval->casobj->parent; while (!done2) { if (testobj == obj) { done2 = done = TRUE; } else if (testobj != NULL && (testobj->objtype == OBJ_TYP_CHOICE || testobj->objtype == OBJ_TYP_CASE)) { testobj = testobj->parent; } else { done2 = TRUE; } } if (done) { chval = testval; } } } if (!done) { return FALSE; } cas = chval->casobj; /* check if all the mandatory parms are present in this case */ for (child = obj_first_child(cas); child != NULL; child = obj_next_child(child)) { if (!obj_is_config(child)) { continue; } if (!obj_is_mandatory(child)) { continue; } if (!val_find_child(val, obj_get_mod_name(child), obj_get_name(child))) { return FALSE; } } return TRUE; } /* val_choice_is_set */ /******************************************************************** * FUNCTION val_purge_errors_from_root * * Remove any error nodes under a root container * that were saved for error recording purposes * * INPUTS: * val == root container to purge * *********************************************************************/ void val_purge_errors_from_root (val_value_t *val) { assert( val && "val is NULL!" ); purge_errors(val); val->res = NO_ERR; } /* val_purge_errors_from_root */ /******************************************************************** * FUNCTION val_new_child_val * * INPUTS: * nsid == namespace ID of name * name == name string (direct or strdup, based on copyname) * copyname == TRUE is dname strdup should be used * parent == parent node * editop == requested edit operation * obj == object template to use * * RETURNS: * status *********************************************************************/ val_value_t * val_new_child_val (xmlns_id_t nsid, const xmlChar *name, boolean copyname, val_value_t *parent, op_editop_t editop, obj_template_t *obj) { val_value_t *chval; chval = val_new_value(); if (!chval) { return NULL; } /* save a const pointer to the name of this field */ if (copyname) { chval->dname = xml_strdup(name); if (chval->dname) { chval->name = chval->dname; } else { val_free_value(chval); return NULL; } } else { chval->name = name; } chval->parent = parent; chval->editop = editop; chval->nsid = nsid; chval->obj = obj; return chval; } /* val_new_child_val */ /******************************************************************** * FUNCTION val_gen_instance_id * * Malloc and Generate the instance ID string for this value node, * * INPUTS: * mhdr == message hdr w/ prefix map or NULL to just use * the internal prefix mappings * val == node to generate the instance ID for * format == desired output format (NCX or Xpath) * buff == pointer to address of buffer to use * * OUTPUTS * mhdr.pmap may have entries added if prefixes used * in the instance identifier which are not already in the pmap * *buff == malloced buffer with the instance ID * * RETURNS: * status *********************************************************************/ status_t val_gen_instance_id (xml_msg_hdr_t *mhdr, const val_value_t *val, ncx_instfmt_t format, xmlChar **buff) { return val_gen_instance_id_ex(mhdr, val, format, TRUE, buff); } /* val_gen_instance_id */ /******************************************************************** * FUNCTION val_gen_instance_id_ex * * Malloc and Generate the instance ID string for this value node, * * INPUTS: * mhdr == message hdr w/ prefix map or NULL to just use * the internal prefix mappings * val == node to generate the instance ID for * format == desired output format (NCX or Xpath) * stop_at_root == TRUE to stop if a 'root' node is encountered * == FALSE to keep recursing all the way to * buff == pointer to address of buffer to use * * OUTPUTS * mhdr.pmap may have entries added if prefixes used * in the instance identifier which are not already in the pmap * *buff == malloced buffer with the instance ID * * RETURNS: * status *********************************************************************/ status_t val_gen_instance_id_ex (xml_msg_hdr_t *mhdr, const val_value_t *val, ncx_instfmt_t format, boolean stop_at_root, xmlChar **buff) { assert( val && "val is NULL!" ); assert( buff && "buff is NULL!" ); uint32 len = 0, len2 = 0; status_t res; /* figure out the length of the parmset instance ID */ res = get_instance_string(mhdr, format, val, stop_at_root, NULL, &len); if (res != NO_ERR) { return res; } /* check no instance ID */ if (len==0) { if (obj_is_root(val->obj)) { len = 1; } else { *buff = NULL; return ERR_NCX_NO_INSTANCE; } } /* get a buffer to fit the instance ID string */ *buff = (xmlChar *)m__getMem(len+1); if (!*buff) { return ERR_INTERNAL_MEM; } else { memset(*buff, 0x0, len+1); } if (obj_is_root(val->obj) && len == 1) { xml_strcpy(*buff, (const xmlChar *)"/"); len2 = 1; } else { /* get the instance ID string for real this time */ res = get_instance_string(mhdr, format, val, stop_at_root, *buff, &len2); if (res != NO_ERR) { m__free(*buff); *buff = NULL; } } if (res == NO_ERR && len != len2) { SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* val_gen_instance_id_ex */ /******************************************************************** * FUNCTION val_gen_split_instance_id * * Malloc and Generate the instance ID string for this value node, * Add the last node from the parameters, not the value node * * INPUTS: * mhdr == message hdr w/ prefix map or NULL to just use * the internal prefix mappings * val == node to generate the instance ID for * format == desired output format (NCX or Xpath) * leaf_pfix == namespace prefix string of the leaf to add * leaf_name == name string of the leaf to add * stop_at_root == TRUE to stop if a 'root' node is encountered * == FALSE to keep recursing all the way to * buff == pointer to address of buffer to use * * OUTPUTS * mhdr.pmap may have entries added if prefixes used * in the instance identifier which are not already in the pmap * *buff == malloced buffer with the instance ID * * RETURNS: * status *********************************************************************/ status_t val_gen_split_instance_id (xml_msg_hdr_t *mhdr, const val_value_t *val, ncx_instfmt_t format, xmlns_id_t leaf_nsid, const xmlChar *leaf_name, boolean stop_at_root, xmlChar **buff) { const xmlChar *leaf_pfix = NULL; uint32 len = 0, leaf_len = 0; status_t res = NO_ERR; #ifdef DEBUG if (!val || !leaf_name || !buff) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (leaf_nsid) { leaf_pfix = xmlns_get_ns_prefix(leaf_nsid); } if (!leaf_pfix) { leaf_pfix = (const xmlChar *)"inv"; } if (!(stop_at_root && val->obj && obj_is_root(val->obj))) { /* figure out the length of the parmset instance ID */ res = get_instance_string(mhdr, format, val, stop_at_root, NULL, &len); if (res != NO_ERR) { return res; } /* check no instance ID */ if (len==0) { *buff = NULL; return ERR_NCX_NO_INSTANCE; } } /* get the length of the extra leaf '/pfix:name' */ leaf_len = 1 + xml_strlen(leaf_pfix) + 1 + xml_strlen(leaf_name); /* get a buffer to fit the instance ID string */ *buff = (xmlChar *)m__getMem(len+leaf_len+1); if (!*buff) { return ERR_INTERNAL_MEM; } else { memset(*buff, 0x0, len+1); } /* get the instance ID string for real this time */ if (!(stop_at_root && val->obj && obj_is_root(val->obj))) { res = get_instance_string(mhdr, format, val, stop_at_root, *buff, &len); } if (res != NO_ERR) { m__free(*buff); *buff = NULL; } else { xmlChar *p = *buff; p += len; *p++ = '/'; p += xml_strcpy(p, leaf_pfix); *p++ = ':'; xml_strcpy(p, leaf_name); } return res; } /* val_gen_split_instance_id */ /******************************************************************** * FUNCTION val_get_index_string * * Get the index string for the specified table or container entry * * INPUTS: * mhdr == message hdr w/ prefix map or NULL to just use * the internal prefix mappings * format == desired output format * val == val_value_t for table or container * buff == buffer to hold result; == NULL means get length only * * OUTPUTS: * mhdr.pmap may have entries added if prefixes used * in the instance identifier which are not already in the pmap * *len = number of bytes that were (or would have been) written * to buff * * RETURNS: * status *********************************************************************/ status_t val_get_index_string (xml_msg_hdr_t *mhdr, ncx_instfmt_t format, const val_value_t *val, xmlChar *buff, uint32 *len) { const val_value_t *ival, *nextival; const val_index_t *valin; uint32 cnt, total; status_t res; #ifdef DEBUG if (!val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif cnt = 0; total = 0; *len = 0; /* get the first index node value struct */ valin = (const val_index_t *)dlq_firstEntry(&val->indexQ); if (valin) { ival = valin->val; } else { return SET_ERROR(ERR_INTERNAL_VAL); } /* check that there is at least one index component */ if (format != NCX_IFMT_CLI) { /* start with the left bracket */ if (buff) { *buff++ = (xmlChar)VAL_BINDEX_CH; } total++; } /* at least one real index component; generate entire index */ while (ival) { /* generate one component, or N if it is a struct */ res = get_index_comp(mhdr, format, ival, buff, &cnt); if (res != NO_ERR) { return res; } else { if (buff) { buff += cnt; } total += cnt; } /* setup next index component */ nextival = NULL; valin = (const val_index_t *)dlq_nextEntry(valin); if (valin) { nextival = valin->val; } /* check if an index separator string is needed */ if (nextival) { switch (format) { case NCX_IFMT_C: /* add the index separator char ',' */ if (buff) { *buff++ = VAL_INDEX_SEPCH; } total++; break; case NCX_IFMT_XPATH1: case NCX_IFMT_XPATH2: /* format is one of the Xpath variants * add the index separator string ' and ' */ if (buff) { buff += xml_strcpy(buff, VAL_XPATH_INDEX_SEPSTR); } total += VAL_XPATH_INDEX_SEPLEN; break; case NCX_IFMT_CLI: /* add the CLI index separator char ' ' */ if (buff) { *buff++ = VAL_INDEX_CLI_SEPCH; } total++; break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* setup the next index component */ ival = nextival; } /* add the closing right bracket */ if (format != NCX_IFMT_CLI) { if (buff) { *buff++ = VAL_EINDEX_CH; *buff = 0; } total++; } /* return success */ *len = total; return NO_ERR; } /* val_get_index_string */ /******************************************************************** * FUNCTION val_check_obj_when * * checks when-stmt only * Check if the specified object node is * conditionally TRUE or FALSE, based on any * when statements attached to the child node * * INPUTS: * val == parent value node of the object node to check * valroot == database root for XPath purposes * objval == database value node to check (may be NULL) * obj == object template of data node object to check * condresult == address of conditional test result * whencount == address of number of when-stmts tested * (may be NULL if caller does not care) * * OUTPUTS: * *condresult == TRUE if conditional is true or there are none * FALSE if conditional test failed * if non-NULL: * *whencount == number of when-stmts tested * this can be 0 if *condresult == TRUE * * RETURNS: * status *********************************************************************/ status_t val_check_obj_when (val_value_t *val, val_value_t *valroot, val_value_t *objval, obj_template_t *obj, boolean *condresult, uint32 *whencount) { assert( val && "val is NULL!" ); assert( valroot && "valroot is NULL!" ); assert( obj && "obj is NULL!" ); assert( condresult && "condresult is NULL!" ); status_t res = NO_ERR; uint32 cnt = 0; /* check for any when statements attached to this object * 1) direct + parent choice/case * 2) inherited from uses or augment-when + parent choice/case */ if (obj->when) { cnt++; val_value_t *dummychild = NULL, *usechild = objval; if (objval == NULL) { /* this code demopnstrates a bug in YANG * the dummy node alters tha value tree that the * test is being performed on. It adds a node * with no value, even if the type is not 'empty' * * The when-stmt MUST NOT use descendant-or-self * nodes in the test! This should seem obvious * but it is still allowed in YANG */ dummychild = val_new_value(); if (!dummychild) { return ERR_INTERNAL_MEM; } val_init_from_template(dummychild, obj); val_add_child(dummychild, val); usechild = dummychild; } res = check_when_stmt(val, valroot, usechild, obj->when, condresult); if (dummychild) { val_remove_child(dummychild); val_free_value(dummychild); } if (res != NO_ERR || !*condresult) { if (whencount) { *whencount = cnt; } return res; } } if (val == valroot || val->parent == NULL) { if (whencount) { *whencount = cnt; } *condresult = FALSE; return NO_ERR; } /* !! the parent node is passed in so it is the context node * !! when xptrs are saved in the object */ obj_xpath_ptr_t *xptr = obj_first_xpath_ptr(obj); for (; xptr; xptr = obj_next_xpath_ptr(xptr)) { cnt++; res = check_when_stmt(val, valroot, val, xptr->xpath, condresult); if (res != NO_ERR || !*condresult) { if (whencount) { *whencount = cnt; } return res; } } obj_template_t *testobj = obj->parent; boolean done = FALSE; while (!done) { if (testobj && (testobj->objtype == OBJ_TYP_CHOICE || testobj->objtype == OBJ_TYP_CASE)) { if (testobj->when) { cnt++; res = check_when_stmt(val, valroot, val, testobj->when, condresult); if (res != NO_ERR || !*condresult) { if (whencount) { *whencount = cnt; } return res; } } for (xptr = obj_first_xpath_ptr(testobj); xptr; xptr = obj_next_xpath_ptr(xptr)) { cnt++; res = check_when_stmt(val, valroot, val, xptr->xpath, condresult); if (res != NO_ERR || !*condresult) { if (whencount) { *whencount = cnt; } return res; } } testobj = testobj->parent; } else { done = TRUE; } } if (whencount) { *whencount = cnt; } *condresult = TRUE; return NO_ERR; } /* val_check_obj_when */ /******************************************************************** * FUNCTION val_get_xpathpcb * * Get the XPath parser control block in the specified value struct * * INPUTS: * val == value struct to check * * RETURNS: * pointer to xpath control block or NULL if none *********************************************************************/ xpath_pcb_t * val_get_xpathpcb (val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return val->xpathpcb; } /* val_get_xpathpcb */ /******************************************************************** * FUNCTION val_get_const_xpathpcb * * Get the XPath parser control block in the specified value struct * * INPUTS: * val == value struct to check * * RETURNS: * pointer to xpath control block or NULL if none *********************************************************************/ const xpath_pcb_t * val_get_const_xpathpcb (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return val->xpathpcb; } /* val_get_const_xpathpcb */ /******************************************************************** * FUNCTION val_make_simval_obj * * Create and set a val_value_t as a simple type * from an object template instead of individual fields * Calls val_make_simval with the object settings * * INPUTS: * obj == object template to use * valstr == simple value encoded as a string * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced and filled in val_value_t struct * NULL if some error *********************************************************************/ val_value_t * val_make_simval_obj (obj_template_t *obj, const xmlChar *valstr, status_t *res) { val_value_t *newval; #ifdef DEBUG if (!obj || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif newval = val_new_value(); if (!newval) { *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(newval, obj); *res = val_set_simval(newval, obj_get_typdef(obj), obj_get_nsid(obj), obj_get_name(obj), valstr); if (*res != NO_ERR) { val_free_value(newval); newval = NULL; } return newval; } /* val_make_simval_obj */ /******************************************************************** * FUNCTION val_set_simval_obj * * Set an initialized val_value_t as a simple type * Set a pre-initialized val_value_t as a simple type * from an object template instead of individual fields * Calls val_set_simval with the object settings * * INPUTS: * val == value struct to set * obj == object template to use * valstr == simple value encoded as a string to set * * RETURNS: * status *********************************************************************/ status_t val_set_simval_obj (val_value_t *val, obj_template_t *obj, const xmlChar *valstr) { #ifdef DEBUG if (!val || !obj) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return val_set_simval(val, obj_get_typdef(obj), obj_get_nsid(obj), obj_get_name(obj), valstr); } /* val_set_simval_obj */ /******************************************************************** * FUNCTION val_set_warning_parms * * Check the parent value struct (expected to be a container or list) * for the common warning control parameters. * invoke the warning parms that are present * * --warn-idlen * --warn-linelen * --warn-off * * INPUTS: * parentval == parent value struct to check * * OUTPUTS: * prints an error message if a warn-off record cannot be added * * RETURNS: * status *********************************************************************/ status_t val_set_warning_parms (val_value_t *parentval) { val_value_t *parmval; status_t res = NO_ERR; #ifdef DEBUG if (!parentval) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!(parentval->btyp == NCX_BT_CONTAINER || parentval->btyp == NCX_BT_LIST)) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif /* warn-idlen parameter */ parmval = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_WARN_IDLEN); if (parmval && parmval->res == NO_ERR) { ncx_set_warn_idlen(VAL_UINT(parmval)); } /* warn-linelen parameter */ parmval = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_WARN_LINELEN); if (parmval && parmval->res == NO_ERR) { ncx_set_warn_linelen(VAL_UINT(parmval)); } /* warn-off parameter */ for (parmval = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_WARN_OFF); parmval != NULL; parmval = val_find_next_child(parentval, val_get_mod_name(parentval), NCX_EL_WARN_OFF, parmval)) { if (parmval->res == NO_ERR) { res = ncx_turn_off_warning(VAL_UINT(parmval)); if (res != NO_ERR) { log_error("\nError: disable warning failed (%s)", get_error_string(res)); } } } return res; } /* val_set_warning_parms */ /******************************************************************** * FUNCTION val_set_logging_parms * * Check the parent value struct (expected to be a container or list) * for the common warning control parameters. * invoke the warning parms that are present * * --log=filename * --log-level= * --log-append= * * INPUTS: * parentval == parent value struct to check * * OUTPUTS: * prints an error message if any errors occur * * RETURNS: * status *********************************************************************/ status_t val_set_logging_parms (val_value_t *parentval) { val_value_t *val; char *logfilename; status_t res = NO_ERR; boolean logappend; #ifdef DEBUG if (!parentval) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!(parentval->btyp == NCX_BT_CONTAINER || parentval->btyp == NCX_BT_LIST)) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif logappend = FALSE; /* get the log-level parameter */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_LOGLEVEL); if (val && val->res == NO_ERR) { log_set_debug_level (log_get_debug_level_enum ((const char *)VAL_ENUM_NAME(val))); if (log_get_debug_level() == LOG_DEBUG_NONE) { log_error("\nError: invalid log-level value (%s)", (const char *)VAL_ENUM_NAME(val)); return ERR_NCX_INVALID_VALUE; } } val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_LOGAPPEND); if (val && val->res == NO_ERR) { logappend = TRUE; } /* get the log file name parameter */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_LOG); if (val && val->res == NO_ERR && VAL_STR(val)) { if (!log_is_open()) { res = NO_ERR; logfilename = (char *)ncx_get_source(VAL_STR(val), &res); if (logfilename) { res = log_open(logfilename, logappend, TRUE); if (res != NO_ERR) { log_error("\nError: open logfile '%s' failed (%s)", logfilename, get_error_string(res)); } m__free(logfilename); } } } return res; } /* val_set_logging_parms */ /******************************************************************** * FUNCTION val_set_path_parms * --datapath * --modpath * --runpath * * Check the specified value set for the 3 path CLI parms * and override the environment variable setting, if any. * * Not all of these parameters are supported in all programs * The object tree is not checked, just the value tree * * INPUTS: * parentval == CLI container to check for the runpath, * modpath, and datapath variables * RETURNS: * status *********************************************************************/ status_t val_set_path_parms (val_value_t *parentval) { val_value_t *val; #ifdef DEBUG if (!parentval) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!(parentval->btyp == NCX_BT_CONTAINER || parentval->btyp == NCX_BT_LIST)) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif /* get the modpath parameter */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_MODPATH); if (val && val->res == NO_ERR) { ncxmod_set_modpath(VAL_STR(val)); } /* get the datapath parameter */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_DATAPATH); if (val && val->res == NO_ERR) { ncxmod_set_datapath(VAL_STR(val)); } /* get the runpath parameter */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_RUNPATH); if (val && val->res == NO_ERR) { ncxmod_set_runpath(VAL_STR(val)); } return NO_ERR; } /* val_set_path_parms */ /******************************************************************** * FUNCTION val_set_subdirs_parm * --subdirs= * * Handle the --subdirs parameter * * INPUTS: * parentval == CLI container to check for the subdirs parm * * RETURNS: * status *********************************************************************/ status_t val_set_subdirs_parm (val_value_t *parentval) { val_value_t *val; #ifdef DEBUG if (!parentval) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!(parentval->btyp == NCX_BT_CONTAINER || parentval->btyp == NCX_BT_LIST)) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif /* get the subdirs parameter */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_SUBDIRS); if (val && val->res == NO_ERR) { ncxmod_set_subdirs(VAL_BOOL(val)); } return NO_ERR; } /* val_set_subdirs_parm */ /******************************************************************** * FUNCTION val_set_feature_parms * --feature-code-default * --feature-enable-default * --feature-static * --feature-dynamic * --feature-enable * --feature-disable * * Handle the feature-related CLI parms for the specified value set * * Not all of these parameters are supported in all programs * The object tree is not checked, just the value tree * * INPUTS: * parentval == CLI container to check for the feature parms * * RETURNS: * status *********************************************************************/ status_t val_set_feature_parms (val_value_t *parentval) { val_value_t *val; status_t res = NO_ERR; #ifdef DEBUG if (!parentval) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!(parentval->btyp == NCX_BT_CONTAINER || parentval->btyp == NCX_BT_LIST)) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif /* get the feature-code-default parameter */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_FEATURE_CODE_DEFAULT); if (val && val->res == NO_ERR) { if (!xml_strcmp(VAL_ENUM_NAME(val), NCX_EL_DYNAMIC)) { ncx_set_feature_code_default(NCX_FEATURE_CODE_DYNAMIC); } else if (!xml_strcmp(VAL_ENUM_NAME(val), NCX_EL_STATIC)) { ncx_set_feature_code_default(NCX_FEATURE_CODE_STATIC); } else { return ERR_NCX_INVALID_VALUE; } } /* get the feature-enable-default parameter */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_FEATURE_ENABLE_DEFAULT); if (val && val->res == NO_ERR) { ncx_set_feature_enable_default(VAL_BOOL(val)); } /* process the feature-static leaf-list */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_FEATURE_STATIC); while (val && val->res == NO_ERR) { res = ncx_set_feature_code_entry(VAL_STR(val), NCX_FEATURE_CODE_STATIC); if (res != NO_ERR) { return res; } val = val_find_next_child(parentval, val_get_mod_name(parentval), NCX_EL_FEATURE_STATIC, val); } /* process the feature-dynamic leaf-list */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_FEATURE_DYNAMIC); while (val && val->res == NO_ERR) { res = ncx_set_feature_code_entry(VAL_STR(val), NCX_FEATURE_CODE_DYNAMIC); if (res != NO_ERR) { return res; } val = val_find_next_child(parentval, val_get_mod_name(parentval), NCX_EL_FEATURE_DYNAMIC, val); } /* process the feature-enable leaf-list */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_FEATURE_ENABLE); while (val && val->res == NO_ERR) { res = ncx_set_feature_enable_entry(VAL_STR(val), TRUE); if (res != NO_ERR) { return res; } val = val_find_next_child(parentval, val_get_mod_name(parentval), NCX_EL_FEATURE_ENABLE, val); } /* process the feature-disable leaf-list */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_FEATURE_DISABLE); while (val && val->res == NO_ERR) { res = ncx_set_feature_enable_entry(VAL_STR(val), FALSE); if (res != NO_ERR) { return res; } val = val_find_next_child(parentval, val_get_mod_name(parentval), NCX_EL_FEATURE_DISABLE, val); } return res; } /* val_set_feature_parms */ /******************************************************************** * FUNCTION val_set_protocols_parm * * --protocols=bits [netconf1.0, netconf1.1] * * Handle the protocols parameter * * INPUTS: * parentval == CLI container to check for the protocols parm * * RETURNS: * status: at least 1 protocol must be selected *********************************************************************/ status_t val_set_protocols_parm (val_value_t *parentval) { val_value_t *val; boolean anyset = FALSE; #ifdef DEBUG if (!parentval) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!(parentval->btyp == NCX_BT_CONTAINER || parentval->btyp == NCX_BT_LIST)) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif /* get the protocols parameter */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_PROTOCOLS); if (val && val->res == NO_ERR) { /* set to the values specified */ if (ncx_string_in_list(NCX_EL_NETCONF10, &(VAL_BITS(val)))) { anyset = TRUE; ncx_set_protocol_enabled(NCX_PROTO_NETCONF10); } if (ncx_string_in_list(NCX_EL_NETCONF11, &(VAL_BITS(val)))) { anyset = TRUE; ncx_set_protocol_enabled(NCX_PROTO_NETCONF11); } } else { /* set to the default -- all versions enabled */ anyset = TRUE; ncx_set_protocol_enabled(NCX_PROTO_NETCONF10); ncx_set_protocol_enabled(NCX_PROTO_NETCONF11); } return (anyset) ? NO_ERR : ERR_NCX_MISSING_PARM; } /* val_set_protocols_parm */ /******************************************************************** * FUNCTION val_set_ses_protocols_parm * * --protocols=bits [netconf1.0, netconf1.1] * * Handle the protocols parameter * * INPUTS: * scb == session control block to use * parentval == CLI container to check for the protocols parm * * RETURNS: * status: at least 1 protocol must be selected *********************************************************************/ status_t val_set_ses_protocols_parm (ses_cb_t *scb, val_value_t *parentval) { val_value_t *val; boolean anyset = FALSE; #ifdef DEBUG if (!parentval) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!(parentval->btyp == NCX_BT_CONTAINER || parentval->btyp == NCX_BT_LIST)) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif /* get the protocols parameter */ val = val_find_child(parentval, val_get_mod_name(parentval), NCX_EL_PROTOCOLS); if (val && val->res == NO_ERR) { /* set to the values specified */ if (ncx_string_in_list(NCX_EL_NETCONF10, &(VAL_BITS(val)))) { anyset = TRUE; ses_set_protocols_requested(scb, NCX_PROTO_NETCONF10); } if (ncx_string_in_list(NCX_EL_NETCONF11, &(VAL_BITS(val)))) { anyset = TRUE; ses_set_protocols_requested(scb, NCX_PROTO_NETCONF11); } } else { /* set to the default -- whatever was set globally */ anyset = TRUE; if (ncx_protocol_enabled(NCX_PROTO_NETCONF10)) { ses_set_protocols_requested(scb, NCX_PROTO_NETCONF10); } if (ncx_protocol_enabled(NCX_PROTO_NETCONF11)) { ses_set_protocols_requested(scb, NCX_PROTO_NETCONF11); } } return (anyset) ? NO_ERR : ERR_NCX_MISSING_PARM; } /* val_set_ses_protocols_parm */ /******************************************************************** * FUNCTION val_ok_to_partial_lock * * Check if the specified root val could be locked * right now by the specified session * * INPUTS: * val == start value struct to use * sesid == session ID requesting the partial lock * lockowner == address of first lock owner violation * * OUTPUTS: * *lockowner == pointer to first lock owner violation * * RETURNS: * status: if any error, then val_clear_partial_lock * MUST be called with the start root, to back out any * partial operations. This can happen if the max number * of *********************************************************************/ status_t val_ok_to_partial_lock (val_value_t *val, ses_id_t sesid, ses_id_t *lockowner) { val_value_t *childval; status_t res; uint32 i; boolean anyavail; ses_id_t owner; #ifdef DEBUG if (val == NULL || lockowner == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } if (sesid == 0) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif if (!val_is_config_data(val)) { return ERR_NCX_NOT_CONFIG; } res = NO_ERR; *lockowner = 0; /* check for an empty slot and locked-by-another session */ anyavail = FALSE; for (i = 0; i < VAL_MAX_PLOCKS; i++) { if (val->plock[i] == NULL) { anyavail = TRUE; } else { owner = plock_get_sid(val->plock[i]); if (owner != sesid) { *lockowner = owner; return ERR_NCX_LOCK_DENIED; } } } if (!anyavail) { return ERR_NCX_RESOURCE_DENIED; } for (childval = val_get_first_child(val); childval != NULL; childval = val_get_next_child(childval)) { if (!val_is_config_data(childval)) { continue; } res = val_ok_to_partial_lock(childval, sesid, lockowner); if (res != NO_ERR) { return res; } } return NO_ERR; } /* val_ok_to_partial_lock */ /******************************************************************** * FUNCTION val_set_partial_lock * * Set the partial lock throughout the value tree * * INPUTS: * val == start value struct to use * plcb == partial lock to set on entire subtree * * RETURNS: * status: if any error, then val_clear_partial_lock * MUST be called with the start root, to back out any * partial operations. This can happen if the max number * of locks reached or lock already help by another session *********************************************************************/ status_t val_set_partial_lock (val_value_t *val, plock_cb_t *plcb) { uint32 i; boolean anyavail, done; ses_id_t newsid; #ifdef DEBUG if (val == NULL || plcb == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (!val_is_config_data(val)) { return ERR_NCX_NOT_CONFIG; } newsid = plock_get_sid(plcb); /* check for an empty slot and locked-by-another session */ anyavail = FALSE; for (i = 0; i < VAL_MAX_PLOCKS; i++) { if (val->plock[i] == NULL) { anyavail = TRUE; } else if (plock_get_sid(val->plock[i]) != newsid) { return ERR_NCX_LOCK_DENIED; } } if (!anyavail) { return ERR_NCX_RESOURCE_DENIED; } done = FALSE; for (i = 0; i < VAL_MAX_PLOCKS && !done; i++) { if (val->plock[i] == NULL) { val->plock[i] = plcb; done = TRUE; } } return NO_ERR; } /* val_set_partial_lock */ /******************************************************************** * FUNCTION val_clear_partial_lock * * Clear the partial lock throughout the value tree * * INPUTS: * val == start value struct to use * plcb == partial lock to clear * *********************************************************************/ void val_clear_partial_lock (val_value_t *val, plock_cb_t *plcb) { val_value_t *childval; uint32 i; #ifdef DEBUG if (val == NULL || plcb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (!val_is_config_data(val)) { return; } /* check for the specified plcb */ for (i = 0; i < VAL_MAX_PLOCKS; i++) { if (val->plock[i] == plcb) { val->plock[i] = NULL; return; } } for (childval = val_get_first_child(val); childval != NULL; childval = val_get_next_child(childval)) { if (val_is_config_data(childval)) { val_clear_partial_lock(childval, plcb); } } } /* val_clear_partial_lock */ /******************************************************************** * FUNCTION val_write_ok * * Check if there are any partial-locks owned by another * session in the node that is going to be written * If the operation is replace or delete, then the * entire target subtree will be checked * * INPUTS: * val == start value struct to use * editop == requested write operation * sesid == session requesting this write operation * checkup == TRUE to check up the tree as well * lockid == address of return partial lock ID * * OUTPUTS: * *lockid == return lock ID if any portion is locked * Only the first lock violation detected will * be reported * error code will be NCX_ERR_IN_USE if *lockid is set * * RETURNS: * status, NO_ERR indicates no partial lock conflicts *********************************************************************/ status_t val_write_ok (val_value_t *val, op_editop_t editop, ses_id_t sesid, boolean checkup, uint32 *lockid) { val_value_t *childval, *upval; cfg_template_t *running; uint32 i; status_t res; #ifdef DEBUG if (val == NULL || lockid == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (editop == OP_EDITOP_NONE) { return NO_ERR; } if (!val_is_config_data(val)) { return ERR_NCX_NO_ACCESS_MAX; } /* quick exit check */ running = cfg_get_config_id(NCX_CFGID_RUNNING); if (cfg_first_partial_lock(running) == NULL) { return NO_ERR; } /* check for the specified session ID * this function should not be called for * the create operation, since there must not be * an existing instance that could be locked * in order for the createoperation to be valid */ for (i = 0; i < VAL_MAX_PLOCKS; i++) { if (val->plock[i] == NULL) { continue; } if (plock_get_sid(val->plock[i]) != sesid) { /* this node locked by another session */ *lockid = plock_get_id(val->plock[i]); return ERR_NCX_IN_USE_LOCKED; } } /* see if the path to root needs to be checked * because the agt_val_check_commit_edits waited until * applyhere to check the partial lock */ if (checkup) { upval = val->parent; while (upval != NULL && !obj_is_root(upval->obj)) { for (i = 0; i < VAL_MAX_PLOCKS; i++) { if (upval->plock[i] == NULL) { continue; } if (plock_get_sid(upval->plock[i]) != sesid) { /* this node locked by another session */ *lockid = plock_get_id(upval->plock[i]); return ERR_NCX_IN_USE_LOCKED; } } upval = upval->parent; } } /* only replace and delete need to dive into the subtree * because the config in the PDU does not need to * align with the target data tree; and the request * is all-or-nothing. The merge operation will * dive into the subtree as indicated by the * config in the PDU input, and not all subtrees * may be affected by the merge, so any partial locks * in those subtrees need to be ignored now */ if (editop == OP_EDITOP_REPLACE || editop == OP_EDITOP_DELETE || editop == OP_EDITOP_REMOVE) { for (childval = val_get_first_child(val); childval != NULL; childval = val_get_next_child(childval)) { if (val_is_config_data(childval)) { res = val_write_ok(childval, editop, sesid, FALSE, lockid); if (res != NO_ERR) { return res; } } } } return NO_ERR; } /* val_write_ok */ /******************************************************************** * FUNCTION val_check_swap_resnode * * Check if the curnode has any partial locks * and if so, transfer them to the new node * and change any resnodes as well * * INPUTS: * curval == current node to check * newval == new value taking its place * *********************************************************************/ void val_check_swap_resnode (val_value_t *curval, val_value_t *newval) { if (curval == NULL || newval == NULL) { return; } uint32 i = 0; for (; i < VAL_MAX_PLOCKS; i++) { newval->plock[i] = curval->plock[i]; if (curval->plock[i] != NULL) { xpath_result_t *result = plock_get_final_result(curval->plock[i]); xpath_nodeset_swap_valptr(result, curval, newval); } } } /* val_check_swap_resnode */ /******************************************************************** * FUNCTION val_check_delete_resnode * * Check if the curnode has any partial locks * and if so, remove them from the final result * * INPUTS: * curval == current node to check * *********************************************************************/ void val_check_delete_resnode (val_value_t *curval) { if (curval == NULL) { return; } uint32 i = 0; for (; i < VAL_MAX_PLOCKS; i++) { if (curval->plock[i] != NULL) { xpath_result_t *result = plock_get_final_result((curval->plock[i])); xpath_nodeset_delete_valptr(result, curval); } } } /* val_check_delete_resnode */ /******************************************************************** * FUNCTION val_write_extern * * Write an external file to the session * * INPUTS: * scb == session control block * val == value to write (NCX_BT_EXTERN) * * RETURNS: * none *********************************************************************/ void val_write_extern (ses_cb_t *scb, const val_value_t *val) { FILE *fil; boolean done, inxml, xmldone, firstline; int ch, lastch; if (val->v.fname == NULL) { SET_ERROR(ERR_INTERNAL_VAL); return; } fil = fopen((const char *)val->v.fname, "r"); if (fil == NULL) { log_error("\nError: open extern var " "file '%s' failed", val->v.fname); return; } xmldone = FALSE; inxml = FALSE; done = FALSE; firstline = TRUE; lastch = 0; while (!done) { ch = fgetc(fil); if (ch == EOF) { if (lastch && !inxml) { ses_putchar(scb, (uint32)lastch); } fclose(fil); done = TRUE; continue; } if (firstline) { /* do not match the first char in the file */ if (lastch && !inxml) { if (lastch == '<' && ch == '?') { inxml = TRUE; } else { /* done with xml checking */ xmldone = TRUE; firstline = FALSE; } } else if (lastch && ch == '\n') { /* done with xml checking */ firstline = FALSE; xmldone = TRUE; } else if (!xmldone && inxml) { /* look for xml declaration and remove it */ if (lastch == '?' && ch == '>') { xmldone = TRUE; } } /* first time xmldone is true skip this */ if (xmldone && !inxml) { if (lastch) { ses_putchar(scb, (uint32)lastch); } } /* setup 3rd loop to print char after '?>' */ if (xmldone && inxml) { if (ch != '>') { inxml = FALSE; } } } else { if (lastch) { ses_putchar(scb, (uint32)lastch); } } lastch = ch; } } /* val_write_extern */ /******************************************************************** * FUNCTION val_write_intern * * Write an internal buffer to the session * * INPUTS: * scb == session control block * val == value to write (NCX_BT_INTERN) * * RETURNS: * none *********************************************************************/ void val_write_intern (ses_cb_t *scb, const val_value_t *val) { if (val->v.intbuff) { ses_putstr(scb, val->v.intbuff); } } /* val_write_intern */ /******************************************************************** * FUNCTION val_get_value * * Get the value node for output to a session * Checks access control if enabled * Checks filtering via testfn if non-NULL * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write (node from system) * acmcheck == TRUE if NACM should be checked; FALSE to skip * testcb == callback function to use, NULL if not used * malloced == address of return malloced flag * res == address of return status * * OUTPUTS: * *malloced == TRUE if the * *res == return status * * RETURNS: * value node to use; this is malloced if *malloced is TRUE * NULL if some error; check *res; * !!!! check for ERR_NCX_SKIPPED !!! *********************************************************************/ val_value_t * val_get_value (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, val_nodetest_fn_t testfn, boolean acmcheck, boolean *malloced, status_t *res) { val_value_t *realval = NULL; val_value_t *v_val = NULL; val_value_t *useval; #ifdef DEBUG if (!scb || !msg || !val || !malloced || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif *malloced = FALSE; /* check the user filter callback function */ if (testfn) { if (!(*testfn)(msg->withdef, TRUE, val)) { *res = ERR_NCX_SKIPPED; return NULL; } } if (acmcheck && msg->acm_cbfn) { xml_msg_authfn_t cbfn = (xml_msg_authfn_t)msg->acm_cbfn; boolean acmtest = (*cbfn)(msg, scb->username, val); if (!acmtest) { *res = ERR_NCX_SKIPPED; return NULL; } } if (val_is_virtual(val)) { v_val = val_get_virtual_value(scb, val, res); if (!v_val) { return NULL; } } useval = (v_val) ? v_val : val; if (useval->btyp == NCX_BT_LEAFREF) { typ_def_t *realtypdef = typ_get_xref_typdef(val->typdef); if (realtypdef) { switch (typ_get_basetype(realtypdef)) { case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_BOOLEAN: case NCX_BT_ENUM: break; default: realval = val_make_simval(realtypdef, val_get_nsid(useval), useval->name, VAL_STR(useval), res); if (realval) { *malloced = TRUE; val_move_fields_for_xml(val, realval, msg->acm_cbfn == NULL); return realval; } else { return NULL; } } } else { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } return useval; } /* val_get_value */ /******************************************************************** * FUNCTION val_traverse_keys * * Check ancestor-or-self nodes until root reached * Find all lists; For each list, starting with the * closest to root, invoke the callback function * for each of the key objects in order * * INPUTS: * val == value node to start check from * cookie1 == cookie1 to pass to the callback function * cookie2 == cookie2 to pass to the callback function * walkerfn == walker callback function * returns FALSE to terminate traversal * *********************************************************************/ void val_traverse_keys (val_value_t *val, void *cookie1, void *cookie2, val_walker_fn_t walkerfn) { val_index_t *valkey; #ifdef DEBUG if (!val || !val->obj || !walkerfn) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (obj_is_root(val->obj)) { return; } if (val->parent != NULL) { val_traverse_keys(val->parent, cookie1, cookie2, walkerfn); } if (val->btyp != NCX_BT_LIST) { return; } for (valkey = val_get_first_key(val); valkey != NULL; valkey = val_get_next_key(valkey)) { if (valkey->val) { boolean ret = (*walkerfn)(valkey->val, cookie1, cookie2); if (!ret) { return; } } // else some error; skip this key!!! } } /* val_traverse_keys */ /******************************************************************** * FUNCTION val_build_index_chains * * Check descendant-or-self nodes for lists * Check if they have index chains built already * If not, then try to add one * for each of the key objects in order * * INPUTS: * val == value node to start check from * * RETURNS: * status *********************************************************************/ status_t val_build_index_chains (val_value_t *val) { val_value_t *childval = NULL; status_t res = NO_ERR; #ifdef DEBUG if (!val || !val->obj) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (obj_is_leafy(val->obj)) { return NO_ERR; } for (childval = val_get_first_child(val); childval != NULL; childval = val_get_next_child(childval)) { if (!obj_is_leafy(childval->obj)) { res = val_build_index_chains(childval); if (res != NO_ERR) { return res; } } } if (val->btyp != NCX_BT_LIST) { /* container or maybe anyxml */ return NO_ERR; } if (!dlq_empty(&val->indexQ)) { /* assume index chain already built */ return NO_ERR; } /* 0 or more index components expected */ res = val_gen_index_chain(val->obj, val); return res; } /* val_build_index_chains */ /******************************************************************** * FUNCTION val_find_nearest_ancestor * * Find a leaf-lists value node's nearest ancestor that meets the * conditions set in RFC 7950 section 7.7.2. * * INPUTS: * val == leaf-list value node from which test starts * * OUTPUTS: * pobj == parent object found from schema tree. If this is a * case node, it will not be the same as the object pointed * to by the returned val_value_t. * * RETURNS: * The *********************************************************************/ val_value_t * val_find_nearest_ancestor(val_value_t *val, obj_template_t **pobj) { obj_template_t *curobj; if (val->obj->objtype != OBJ_TYP_LEAF_LIST) return NULL; curobj = val->obj->parent; while (curobj) { if (curobj->objtype == OBJ_TYP_USES || curobj->objtype == OBJ_TYP_AUGMENT) { goto skip; } if (val->parent && val->parent->obj == curobj) val = val->parent; if (curobj->objtype != OBJ_TYP_CONTAINER) { /* found something not a container - can stop */ break; } if (obj_get_presence_string(curobj)) { /* found a container, but it's a presence node - can stop */ break; } skip: curobj = curobj->parent; } if (curobj) { if (val->obj != curobj) { val = val->parent; } *pobj = curobj; return val; } return NULL; } /******************************************************************** * FUNCTION val_has_default_leaf_list * * * INPUTS: * val == value node to check for potential leaf-list child * * RETURNS: * true if a value has an empty leaf-list with defaults, * false otherwise. *********************************************************************/ boolean val_has_default_leaf_list(const val_value_t *val) { obj_template_t *obj; /* Check if each child object is a leaf-list. There could be more * than one */ for (obj = obj_first_child_deep(val->obj); obj; obj = obj_next_child_deep(obj)) { if (obj->objtype == OBJ_TYP_LEAF_LIST) { /* does this leaf list have any values? */ if (val_find_child(val, obj_get_mod_name(obj), obj_get_name(obj))) { continue; } /* no values. does it have one more more default values? */ if (obj_get_first_default(obj)) { return TRUE; } } } return FALSE; } /* END file val_util.c */ yuma123_2.14/netconf/src/ncx/ncx_str.h0000664000175000017500000000736614770023131020003 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ncx_str #define _H_ncx_str /* FILE: ncx_str.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Module Library String Utility Functions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17-feb-10 abb Begun; split out from ncx.c */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION ncx_compare_strs * * Compare 2 ncx_str_t union contents * * INPUTS: * str1 == first string * str2 == second string * btyp == expected data type * (NCX_BT_STRING, NCX_BT_INSTANCE_ID) * RETURNS: * -1 if str1 is < str2 * 0 if str1 == str2 (also for error, after SET_ERROR called) * 1 if str1 is > str2 *********************************************************************/ extern int32 ncx_compare_strs (const ncx_str_t *str1, const ncx_str_t *str2, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_copy_str * * Copy the contents of str1 to str2 * Supports base types: * NCX_BT_STRING * NCX_BT_INSTANCE_ID * NCX_BT_LEAFREF * * INPUTS: * str1 == first string * str2 == second string * btyp == expected data type * * OUTPUTS: * str2 contains copy of str1 * * RETURNS: * status *********************************************************************/ extern status_t ncx_copy_str (const ncx_str_t *str1, ncx_str_t *str2, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_clean_str * * Scrub the memory in a ncx_str_t by freeing all * the sub-fields. DOES NOT free the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * str == ncx_str_t data structure to clean *********************************************************************/ extern void ncx_clean_str (ncx_str_t *str); /******************************************************************** * FUNCTION ncx_copy_c_safe_str * * Copy the string to the buffer, changing legal YANG identifier * chars that cannot be used in C function names to underscore * * !!! DOES NOT CHECK BUFFER OVERRUN !!! * !!! LENGTH OF STRING IS NOT CHANGED WHEN COPIED TO THE BUFFER !!! * * INPUTS: * buffer == buffer to write into * strval == string value to copy * * RETURNS * number of chars copied *********************************************************************/ extern uint32 ncx_copy_c_safe_str (xmlChar *buffer, const xmlChar *strval); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_ncx_str */ yuma123_2.14/netconf/src/ncx/yang_typ.c0000664000175000017500000044330714770023131020147 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yang_typ.c YANG module parser, typedef and type statement support Data type conversion Current NCX base type YANG builtin type NCX_BT_ANYDATA anydata NCX_BT_ANYDATA anyxml NCX_BT_BITS bits NCX_BT_ENUM enumeration NCX_BT_EMPTY empty NCX_BT_INT8 int8 NCX_BT_INT32 int16 NCX_BT_INT32 int32 NCX_BT_INT32 int64 NCX_BT_UINT8 uint8 NCX_BT_UINT32 uint16 NCX_BT_UINT32 uint32 NCX_BT_UINT64 uint64 NCX_BT_DECIMAL64 decimal64 NCX_BT_FLOAT64 float64 NCX_BT_STRING string NCX_BT_USTRING binary NCX_BT_UNION union NCX_BT_SLIST N/A NCX_BT_CONTAINER container ** Not a type ** NCX_BT_CHOICE choice ** Not a type ** NCX_BT_LIST list ** Not a type ** NCX_BT_LEAFREF leafref NCX_BT_INSTANCE_ID instance-identifier NCX_BT_CHOICE meta-type for YANG choice NCX_BT_CASE meta-type for YANG case sim-typ w/ '*' iqual leaf-list ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 26oct07 abb begun; start from ncx_parse.c 15nov07 abb split out from yang_parse.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #define _GNU_SOURCE #include #include #include #include #include #include #include "procdefs.h" #include "def_reg.h" #include "dlq.h" #include "log.h" #include "ncxconst.h" #include "ncxtypes.h" #include "ncx.h" #include "ncx_appinfo.h" #include "ncx_feature.h" #include "ncx_num.h" #include "obj.h" #include "status.h" #include "typ.h" #include "xml_util.h" #include "val123.h" #include "xpath.h" #include "xpath_yang.h" #include "yangconst.h" #include "yang.h" #include "yang_typ.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG /* #define YANG_TYP_DEBUG 1 */ /* #define YANG_TYP_TK_DEBUG 1 */ #endif static status_t resolve_type (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, const xmlChar *name, const xmlChar *defval, obj_template_t *obj, grp_template_t *grp); /******************************************************************** * FUNCTION loop_test * * Check for named type dependency loops * Called during phase 2 of module parsing * * INPUTS: * typdef == typdef in progress * * RETURNS: * status *********************************************************************/ static status_t loop_test (typ_def_t *typdef) { typ_def_t *testdef, *lastdef; status_t res; res = NO_ERR; if (typdef->tclass == NCX_CL_NAMED) { testdef = typ_get_parent_typdef(typdef); lastdef = testdef; while (testdef && res==NO_ERR) { if (testdef == typdef) { res = ERR_NCX_DEF_LOOP; log_error("\nError: named type loops with " "type '%s' on line %u", testdef->typenamestr, lastdef->tkerr.linenum); } else { lastdef = testdef; testdef = typ_get_parent_typdef(testdef); } } } return res; } /* loop_test */ /******************************************************************** * FUNCTION one_restriction_test * * Check 1 restriction Q * * INPUTS: * tkc == token chain * mod == module in progress * newdef == new typdef in progress * * RETURNS: * status *********************************************************************/ static status_t one_restriction_test (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *newdef) { dlq_hdr_t *rangeQ; typ_pattern_t *pat; status_t retres; ncx_btype_t btyp; boolean doerr; ncx_strrest_t strrest; retres = NO_ERR; doerr = FALSE; btyp = typ_get_basetype(newdef); if (btyp == NCX_BT_UNION) { return NO_ERR; } rangeQ = typ_get_rangeQ_con(newdef); strrest = typ_get_strrest(newdef); /* check if proper base type restrictions * are present. Range allowed for numbers * and strings only */ if (rangeQ && !dlq_empty(rangeQ)) { if (!(typ_is_number(btyp) || typ_is_string(btyp) || btyp==NCX_BT_BINARY)) { log_error("\nError: Range or length not " "allowed for the %s builtin type", tk_get_btype_sym(btyp)); retres = ERR_NCX_RESTRICT_NOT_ALLOWED; tkc->curerr = &newdef->tkerr; ncx_print_errormsg(tkc, mod, retres); } } /* Check that a pattern was actually entered * Check Pattern allowed for NCX_BT_STRING only */ pat = typ_get_first_pattern(newdef); if (pat) { if (btyp != NCX_BT_STRING) { log_error("\nError: keyword 'pattern' " "within a restriction for a %s type", tk_get_btype_sym(btyp)); doerr = TRUE; } } else { switch (strrest) { case NCX_SR_ENUM: if (btyp != NCX_BT_ENUM) { log_error("\nError: keyword 'enumeration' " "within a restriction for a %s type", tk_get_btype_sym(btyp)); doerr = TRUE; } break; case NCX_SR_BIT: if (btyp != NCX_BT_BITS) { log_error("\nError: keyword 'bit' " "within a restriction for a %s type", tk_get_btype_sym(btyp)); doerr = TRUE; } break; default: ; } } if (doerr) { retres = ERR_NCX_RESTRICT_NOT_ALLOWED; tkc->curerr = &newdef->tkerr; ncx_print_errormsg(tkc, mod, retres); } return retres; } /* one_restriction_test */ /******************************************************************** * FUNCTION restriction_test * * Check for proper restrictions to a data type definition * Called during phase 2 of module parsing * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typdef in progress * * RETURNS: * status *********************************************************************/ static status_t restriction_test (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef) { typ_def_t *testdef, *newdef; ncx_btype_t btyp, testbtyp; status_t res, retres; if (typdef->tclass == NCX_CL_SIMPLE) { return one_restriction_test(tkc, mod, typdef); } else if (typdef->tclass != NCX_CL_NAMED) { return NO_ERR; } res = NO_ERR; retres = NO_ERR; btyp = typ_get_basetype(typdef); /* if there is a 'newtyp', then restrictions were * added; If NULL, then just a type name was given */ testdef = typdef; while (testdef && testdef->tclass==NCX_CL_NAMED) { newdef = typ_get_new_named(testdef); if (!newdef) { testdef = typ_get_parent_typdef(testdef); continue; } /* validate the 'newdef' typdef */ testbtyp = typ_get_basetype(newdef); if (testbtyp==NCX_BT_NONE) { newdef->def.simple.btyp = btyp; res = one_restriction_test(tkc, mod, newdef); CHK_EXIT(res, retres); } else if (testbtyp != btyp) { log_error("\nError: Derived type '%s' does not match " "the eventual builtin type (%s)", tk_get_btype_sym(testbtyp), tk_get_btype_sym(btyp)); retres = ERR_NCX_WRONG_DATATYP; tkc->curerr = &typdef->tkerr; ncx_print_errormsg(tkc, mod, retres); } /* setup the next (parent) typdef in the chain */ testdef = typ_get_parent_typdef(testdef); } return retres; } /* restriction_test */ /******************************************************************** * FUNCTION consume_yang_rangedef * * Current token is the start of range fragment * Process the token chain and gather the range fragment * in a pre-allocated typ_rangedef_t struct * * Normal exit with current token as the next one to * be processed. * * Cloned from consume_rangedef in ncx_parse.c because * the YANG range clause is different than NCX * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typdef in progress * btyp == base type of range vals * * RETURNS: * status *********************************************************************/ static status_t consume_yang_rangedef (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, ncx_btype_t btyp) { typ_rangedef_t *rv; const char *expstr; dlq_hdr_t *rangeQ; status_t res, retres; boolean done; int32 cmpval; expstr = "number or min, max, -INF, INF keywords"; res = NO_ERR; retres = NO_ERR; done = FALSE; rangeQ = typ_get_rangeQ_con(typdef); if (!rangeQ) { res = SET_ERROR(ERR_NCX_INVALID_VALUE); ncx_print_errormsg(tkc, mod, res); return res; } /* get a new range value struct */ rv = typ_new_rangedef(); if (!rv) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } /* save the range builtin type */ rv->btyp = btyp; /* get the first range-boundary, check if min requested */ if (TK_CUR_STR(tkc)) { if (!xml_strcmp(TK_CUR_VAL(tkc), YANG_K_MIN)) { /* flag lower bound min for later eval */ rv->flags |= TYP_FL_LBMIN; } else if (!xml_strcmp(TK_CUR_VAL(tkc), YANG_K_MAX)) { /* flag lower bound max for later eval */ rv->flags |= TYP_FL_LBMAX; } else if (btyp==NCX_BT_FLOAT64) { /* -INF and INF keywords allowed for real numbers */ if (!xml_strcmp(TK_CUR_VAL(tkc), YANG_K_NEGINF)) { /* flag lower bound -INF for later eval */ rv->flags |= TYP_FL_LBINF; } else if (!xml_strcmp(TK_CUR_VAL(tkc), YANG_K_POSINF)) { /* flag lower bound INF for later eval */ rv->flags |= TYP_FL_LBINF2; } else { res = ERR_NCX_WRONG_TKVAL; } } else { res = ERR_NCX_WRONG_TKVAL; } } else if (TK_CUR_NUM(tkc)) { if (btyp != NCX_BT_NONE) { if (btyp == NCX_BT_DECIMAL64) { res = ncx_convert_tkc_dec64(tkc, typ_get_fraction_digits(typdef), &rv->lb); } else { res = ncx_convert_tkcnum(tkc, btyp, &rv->lb); } } else { rv->lbstr = xml_strdup(TK_CUR_VAL(tkc)); if (!rv->lbstr) { res = ERR_INTERNAL_MEM; } } } else { res = ERR_NCX_WRONG_TKTYPE; } /* record any error so far */ if (res != NO_ERR) { retres = res; ncx_mod_exp_err(tkc, mod, res, expstr); if (NEED_EXIT(res)) { typ_free_rangedef(rv, btyp); return retres; } } /* move past lower range-boundary */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); typ_free_rangedef(rv, btyp); return res; } /* check if done with this range part */ switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: case TK_TT_LBRACE: case TK_TT_BAR: /* normal end of range reached * lower bound is the upper bound also */ if (rv->flags & TYP_FL_LBMIN) { rv->flags |= TYP_FL_UBMIN; } else if (rv->flags & TYP_FL_LBMAX) { rv->flags |= TYP_FL_UBMAX; } else if (rv->flags & TYP_FL_LBINF) { rv->flags |= TYP_FL_UBINF2; } else if (rv->flags & TYP_FL_LBINF2) { rv->flags |= TYP_FL_UBINF; } else { if (btyp != NCX_BT_NONE) { res = ncx_copy_num(&rv->lb, &rv->ub, btyp); } else { rv->ubstr = xml_strdup(rv->lbstr); if (!rv->ubstr) { res = ERR_INTERNAL_MEM; } } } done = TRUE; break; case TK_TT_RANGESEP: /* continue on to upper bound */ res = TK_ADV(tkc); break; default: res = ERR_NCX_WRONG_TKTYPE; } /* record any error in previous section */ if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); retres = res; if (NEED_EXIT(res)) { typ_free_rangedef(rv, btyp); return res; } } /* get the last range-boundary, check if max requested */ if (!done) { if (TK_CUR_STR(tkc)) { if (!xml_strcmp(TK_CUR_VAL(tkc), YANG_K_MIN)) { /* flag upper bound min for later eval */ rv->flags |= TYP_FL_UBMIN; } else if (!xml_strcmp(TK_CUR_VAL(tkc), YANG_K_MAX)) { /* flag upper bound max for later eval */ rv->flags |= TYP_FL_UBMAX; } else if (btyp==NCX_BT_FLOAT64) { if (!xml_strcmp(TK_CUR_VAL(tkc), YANG_K_NEGINF)) { /* flag upper bound -INF for later eval */ rv->flags |= TYP_FL_UBINF2; } else if (!xml_strcmp(TK_CUR_VAL(tkc), YANG_K_POSINF)) { /* flag upper bound INF for later eval */ rv->flags |= TYP_FL_UBINF; } else { res = ERR_NCX_WRONG_TKVAL; } } else { res = ERR_NCX_WRONG_TKVAL; } } else if (TK_CUR_NUM(tkc)) { if (btyp != NCX_BT_NONE) { if (btyp == NCX_BT_DECIMAL64) { res = ncx_convert_tkc_dec64 (tkc, typ_get_fraction_digits(typdef), &rv->ub); } else { res = ncx_convert_tkcnum(tkc, btyp, &rv->ub); } } else { rv->ubstr = xml_strdup(TK_CUR_VAL(tkc)); if (!rv->ubstr) { res = ERR_INTERNAL_MEM; } } } else { res = ERR_NCX_WRONG_TKTYPE; } /* record any error in previous section */ if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); retres = res; if (NEED_EXIT(res)) { typ_free_rangedef(rv, btyp); return res; } } /* move past this keyword or number */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); typ_free_rangedef(rv, btyp); return res; } } /* check basic overlap, still need to evaluate min max later */ if (!(rv->flags & TYP_RANGE_FLAGS)) { /* just numbers entered, no min.max.-INF, INF */ if (btyp != NCX_BT_NONE) { cmpval = ncx_compare_nums(&rv->lb, &rv->ub, btyp); if (cmpval > 0) { retres = ERR_NCX_INVALID_RANGE; ncx_print_errormsg(tkc, mod, retres); } } } else { /* check corner cases with min, max, -INF, INF keywords * check this LB > the last UB (if any lastrv) */ if (rv->flags & (TYP_FL_LBMAX|TYP_FL_LBINF2)) { /* lower bound set to max or INF */ if (!(rv->flags & (TYP_FL_UBMAX|TYP_FL_UBINF))) { /* upper bound not set to max or INF */ retres = ERR_NCX_INVALID_RANGE; ncx_print_errormsg(tkc, mod, retres); } } if (rv->flags & (TYP_FL_UBMIN|TYP_FL_UBINF2)) { /* upper bound set to min or -INF */ if (!(rv->flags & (TYP_FL_LBMIN|TYP_FL_LBINF))) { /* lower bound not set to min or -INF */ retres = ERR_NCX_INVALID_RANGE; ncx_print_errormsg(tkc, mod, retres); } } if ((rv->flags & TYP_FL_LBINF) && !dlq_empty(rangeQ)) { /* LB set to -INF and the rangeQ is not empty */ retres = ERR_NCX_OVERLAP_RANGE; ncx_print_errormsg(tkc, mod, retres); } } /* save and exit if rangedef is valid */ if (retres == NO_ERR) { dlq_enque(rv, rangeQ); } else { typ_free_rangedef(rv, btyp); } return retres; } /* consume_yang_rangedef */ /******************************************************************** * FUNCTION consume_yang_range * * Current token is the 'range' or 'length' keyword * Process the token chain and gather the range definition in typdef * * Cloned from consume_range in ncx_parse.c because * the YANG range clause is different than NCX * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typedef struct in progress (already setup as SIMPLE) * simple.btyp == enum for the specific builtin type * isrange == TRUE if called from range definition * == FALSE if called from a length definition * * RETURNS: * status *********************************************************************/ static status_t consume_yang_range (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, boolean isrange) { typ_range_t *range; status_t res = NO_ERR; boolean done; ncx_btype_t rbtyp; ncx_btype_t btyp; range = typ_get_range_con(typdef); /* save the range token in case needed for error msg */ ncx_set_error( &range->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); if ( !range ) { res = ERR_INTERNAL_PTR; ncx_print_errormsg(tkc, mod, res); return res; } btyp = typ_get_basetype(typdef); if (btyp == NCX_BT_NONE) { if (isrange) { /* signal that the range will be processed in phase 2 */ rbtyp = NCX_BT_NONE; } else { /* length range is always uint32 */ rbtyp = NCX_BT_UINT32; } } else { /* get the correct number type for the range specification */ rbtyp = typ_get_range_type(btyp); } /* move past start-of-range keyword check token type, which may be in a * quoted string * an identifier string, or a series of tokens */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } if (TK_CUR_STR(tkc)) { if (range->rangestr) { m__free(range->rangestr); } range->rangestr = xml_strdup(TK_CUR_VAL(tkc)); if (!range->rangestr) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } /* undo quotes or identifier string into separate tokens. In YANG there * are very few tokens, so all these no-WSP strings are valid. If * quoted, the entire range clause will be a single string. If not, * many combinations of valid range clause tokens might be * mis-classified * E.g.: * range min..max; (TSTRING) * range 1..100; (DNUM, STRING, DNUM) * range -INF..47.8 (STRING, RNUM) */ res = tk_retokenize_cur_string(tkc, mod); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } } /* get all the range-part sections */ done = FALSE; while (!done) { /* get one range spec */ res = consume_yang_rangedef(tkc, mod, typdef, rbtyp); if ( ( NO_ERR != res && res < ERR_LAST_SYS_ERR ) || ERR_NCX_EOF == res ) { return res; } /* Current token is either a BAR or SEMICOL/LBRACE. Move past it if * BAR and keep going * Else exit loop, pointing at this token */ switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: case TK_TT_LBRACE: done = TRUE; break; case TK_TT_BAR: res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } break; default: res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err( tkc, mod, res, "semi-colon, left brace, or vertical bar" ); continue; } } /* check sub-section for error-app-tag and error-message */ if (TK_CUR_TYP(tkc)==TK_TT_LBRACE) { res = yang_consume_error_stmts( tkc, mod, &range->range_errinfo, &typdef->appinfoQ ); } return res; } /* consume_yang_range */ /******************************************************************** * FUNCTION consume_yang_pattern * * Current token is the 'pattern' keyword * Process the token chain and gather the pattern definition in typdef * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typedef struct in progress (already setup as SIMPLE) * simple.btyp == enum for the specific builtin type * * RETURNS: * status *********************************************************************/ static status_t consume_yang_pattern ( tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef ) { typ_pattern_t *pat = NULL; status_t res; typ_set_strrest(typdef, NCX_SR_PATTERN); /* move past pattern keyword to pattern value, get 1 string */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } /* need to accept any token as the pattern string, * even if it was parsed as a non-string token * special case: check for module prefix present, which * is not present in the TK_CUR_VAL() string */ if (TK_TT_MSTRING == TK_CUR_TYP(tkc) || TK_TT_MSSTRING == TK_CUR_TYP(tkc)) { /* need to use the entire prefix:value string */ xmlChar *p, *buff = m__getMem(TK_CUR_MODLEN(tkc) + TK_CUR_LEN(tkc) + 2); if (NULL == buff) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } p = buff; p += xml_strncpy(p, TK_CUR_MOD(tkc), TK_CUR_MODLEN(tkc)); *p++ = ':'; p += xml_strncpy(p, TK_CUR_VAL(tkc), TK_CUR_LEN(tkc)); *p = 0; pat = typ_new_pattern(buff); m__free(buff); } else { pat = typ_new_pattern(TK_CUR_VAL(tkc)); } if (!pat) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } /* make sure the pattern is valid */ res = typ_compile_pattern(pat); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); if (res < ERR_LAST_SYS_ERR || res==ERR_NCX_EOF) { typ_free_pattern( pat ); return res; } } /* save the struct in the patternQ early, even if it didn't compile */ dlq_enque(pat, &typdef->def.simple.patternQ); /* move to the next token, which must be ';' or '{' */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: break; case TK_TT_LBRACE: /* check sub-section for error-app-tag and error-message */ res = yang_consume_error_stmts( tkc, mod, &pat->pat_errinfo, &typdef->appinfoQ); break; default: res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, res, "semicolon or left brace" ); } return res; } /* consume_yang_pattern */ /******************************************************************** * FUNCTION finish_string_type * * Parse the next N tokens as 1 string type definition * sub-section, expecting 'length' or 'pattern' keywords * * Current token is the left brace starting the string * sub-section. Continue until right brace or error. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typ_def_t in progress * btyp == the builtin type already figured out * * RETURNS: * status of the operation *********************************************************************/ static status_t finish_string_type (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, ncx_btype_t btyp) { const xmlChar *val; const char *expstr; tk_type_t tktyp; status_t res, retres; boolean done, lendone; expstr = "length or pattern keyword"; done = FALSE; lendone = FALSE; res = NO_ERR; retres = NO_ERR; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &typdef->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_LENGTH)) { if (lendone) { retres = ERR_NCX_ENTRY_EXISTS; log_error("\nError: length clause already entered"); ncx_print_errormsg(tkc, mod, retres); } lendone = TRUE; res = consume_yang_range(tkc, mod, typdef, FALSE); CHK_EXIT(res, retres); } else if (!xml_strcmp(val, YANG_K_PATTERN)) { /* make sure this is not the 'binary' data type */ if (btyp == NCX_BT_BINARY) { log_error("\nPattern restriction not allowed" " for the binary type"); retres = ERR_NCX_RESTRICT_NOT_ALLOWED; ncx_print_errormsg(tkc, mod, retres); } res = consume_yang_pattern(tkc, mod, typdef); CHK_EXIT(res, retres); } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); } } return retres; } /* finish_string_type */ /******************************************************************** * FUNCTION finish_number_type * * Parse the next N tokens as the sub-section for a number type * Expecting range statement. * * Current token is the left brace starting the string * sub-section. Continue until right brace or error. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * btyp == basetype of the number * typdef == typ_def_t in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t finish_number_type (tk_chain_t *tkc, ncx_module_t *mod, ncx_btype_t btyp, typ_def_t *typdef) { const xmlChar *val; const char *expstr; tk_type_t tktyp; status_t res, retres; uint32 digits; boolean done, rangedone, digitsdone; expstr = "range keyword"; rangedone = FALSE; digitsdone = FALSE; digits = 0; res = NO_ERR; retres = NO_ERR; done = FALSE; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &typdef->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_RANGE)) { if (rangedone) { retres = ERR_NCX_ENTRY_EXISTS; log_error("\nError: range statement already entered"); ncx_print_errormsg(tkc, mod, retres); } rangedone = TRUE; res = consume_yang_range(tkc, mod, typdef, TRUE); CHK_EXIT(res, retres); } else if (!xml_strcmp(val, YANG_K_FRACTION_DIGITS)) { res = yang_consume_uint32(tkc, mod, &digits, &digitsdone, &typdef->appinfoQ); if (res == NO_ERR && btyp != NCX_BT_DECIMAL64) { retres = res = ERR_NCX_RESTRICT_NOT_ALLOWED; log_error("\nError: fraction-digits found for '%s'", tk_get_btype_sym(btyp)); ncx_print_errormsg(tkc, mod, retres); } if (res == NO_ERR) { /* check value is actually in proper range */ if (digits < TYP_DEC64_MIN_DIGITS || digits > TYP_DEC64_MAX_DIGITS) { retres = res = ERR_NCX_INVALID_VALUE; log_error("\nError: fraction-digits '%u' out of range", digits); ncx_print_errormsg(tkc, mod, retres); } } if (res == NO_ERR) { res = typ_set_fraction_digits(typdef, (uint8)digits); if (res != NO_ERR) { retres = res; log_error("\nError: set fraction-digits failed"); ncx_print_errormsg(tkc, mod, retres); } } } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); } } return retres; } /* finish_number_type */ /******************************************************************** * FUNCTION finish_unknown_type * * Parse the next N tokens as the subsection of a * local type that is being refined * * Current token is the left brace starting the string * sub-section. Continue until right brace or error. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typ_def_t in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t finish_unknown_type (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef) { const xmlChar *val; const char *expstr; tk_type_t tktyp; status_t res, retres; boolean done, rangedone, lendone; expstr = "range, length, or pattern keyword"; rangedone = FALSE; lendone = FALSE; res = NO_ERR; retres = NO_ERR; done = FALSE; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &typdef->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_RANGE)) { if (lendone) { retres = ERR_NCX_ENTRY_EXISTS; log_error("\nError: length clause already entered," " range clause not allowed"); ncx_print_errormsg(tkc, mod, retres); } if (rangedone) { retres = ERR_NCX_ENTRY_EXISTS; log_error("\nError: range clause already entered"); ncx_print_errormsg(tkc, mod, retres); } rangedone = TRUE; res = consume_yang_range(tkc, mod, typdef, TRUE); CHK_EXIT(res, retres); } else if (!xml_strcmp(val, YANG_K_LENGTH)) { if (rangedone) { retres = ERR_NCX_ENTRY_EXISTS; log_error("\nError: range clause already entered," " length clause not allowed"); ncx_print_errormsg(tkc, mod, retres); } if (lendone) { retres = ERR_NCX_ENTRY_EXISTS; log_error("\nError: length clause already entered"); ncx_print_errormsg(tkc, mod, retres); } lendone = TRUE; res = consume_yang_range(tkc, mod, typdef, FALSE); CHK_EXIT(res, retres); } else if (!xml_strcmp(val, YANG_K_PATTERN)) { res = consume_yang_pattern(tkc, mod, typdef); CHK_EXIT(res, retres); } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); } } return retres; } /* finish_unknown_type */ /******************************************************************** * FUNCTION insert_yang_enumbit * * Insert an enum or bit in its proper order in the simple->valQ * * * Error messages are printed by this function * * INPUTS: * tkc == token chain in progress * mod == module in progress * en == typ_enum_t struct to insert in the sim->valQ * typdef == simple typdef in progress * btyp == builtin type (NCX_BT_ENUM or NCX_BT_BITS) * valset == TRUE is value or position is set * == FALSE to use auto-numbering * * RETURNS: * status *********************************************************************/ static status_t insert_yang_enumbit (tk_chain_t *tkc, ncx_module_t *mod, typ_enum_t *en, typ_def_t *typdef, ncx_btype_t btyp, boolean valset) { typ_simple_t *sim; typ_enum_t *enl; boolean done; status_t res; sim = &typdef->def.simple; /* check if this is a duplicate entry */ for (enl = (typ_enum_t *)dlq_firstEntry(&sim->valQ); enl != NULL; enl = (typ_enum_t *)dlq_nextEntry(enl)) { if (!xml_strcmp(en->name, enl->name)) { res = ERR_NCX_DUP_ENTRY; log_error("\nError: duplicate enum or bit name (%s)", en->name); ncx_print_errormsg(tkc, mod, res); return res; } else if (valset && btyp==NCX_BT_ENUM && en->val==enl->val) { res = ERR_NCX_DUP_ENTRY; log_error("\nError: duplicate enum value (%d)", en->val); ncx_print_errormsg(tkc, mod, res); return res; } else if (valset && btyp==NCX_BT_BITS && en->pos==enl->pos) { res = ERR_NCX_DUP_ENTRY; log_error("\nError: duplicate bit position (%u)", en->pos); ncx_print_errormsg(tkc, mod, res); return res; } } if (valset) { /* check if this is an out-of-order insert */ done = FALSE; for (enl = (typ_enum_t *)dlq_firstEntry(&sim->valQ); enl != NULL && !done; enl = (typ_enum_t *)dlq_nextEntry(enl)) { if (btyp==NCX_BT_ENUM) { if (en->val < enl->val) { res = ERR_NCX_ENUM_VAL_ORDER; if (ncx_warning_enabled(res)) { log_warn("\nWarning: Out of order enum '%s' = %d", (char *)en->name, en->val); ncx_print_errormsg(tkc, mod, res); } else { ncx_inc_warnings(mod); } done = TRUE; } } else { if (en->pos < enl->pos) { res = ERR_NCX_BIT_POS_ORDER; if (ncx_warning_enabled(res)) { log_warn("\nWarning: Out of order bit '%s' = %u", (char *)en->name, en->pos); ncx_print_errormsg(tkc, mod, res); } else { ncx_inc_warnings(mod); } done = TRUE; } } } } else { /* value not set, use highest value + 1 */ if (dlq_empty(&sim->valQ)) { /* first enum set gets the value zero */ if (btyp==NCX_BT_ENUM) { en->val = 0; sim->maxenum = 0; } else { en->pos = 0; sim->maxbit = 0; } } else { /* assign the highest value in use + 1 */ if (btyp==NCX_BT_ENUM) { if (sim->maxenum < NCX_MAX_INT) { en->val = ++(sim->maxenum); } else { res = ERR_NCX_INVALID_VALUE; log_error("\nError: Cannot auto-increment enum " "value beyond MAXINT"); ncx_print_errormsg(tkc, mod, res); } } else { if (sim->maxbit < NCX_MAX_UINT) { en->pos = ++(sim->maxbit); } else { res = ERR_NCX_INVALID_VALUE; log_error("\nError: Cannot auto-increment bit " "position beyond MAXUINT"); ncx_print_errormsg(tkc, mod, res); } } } } /* always insert in the user-defined order == new last entry */ dlq_enque(en, &sim->valQ); return NO_ERR; } /* insert_yang_enumbit */ /******************************************************************** * FUNCTION consume_yang_bit * * Current token is the 'bit' keyword * Process the token chain and gather the bit definition in typdef * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typedef struct in progress (already setup as SIMPLE) * simple.btyp == enum for the specific builtin type * * RETURNS: * status *********************************************************************/ static status_t consume_yang_bit (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef) { typ_enum_t *enu; xmlChar *val, *str; const char *expstr; tk_type_t tktyp; status_t res, retres; boolean done, desc, ref, stat, pos; enu = NULL; str = NULL; expstr = NULL; retres = NO_ERR; done = FALSE; desc = FALSE; ref = FALSE; stat = FALSE; pos = FALSE; /* move past bit keyword * check token type, which should be an identifier string */ res = yang_consume_id_string(tkc, mod, &str); CHK_EXIT(res, retres); if (str) { enu = typ_new_enum2(str); } else { enu = typ_new_enum((const xmlChar *)"none"); } if (!enu) { res = ERR_INTERNAL_MEM; if (str) { m__free(str); } ncx_print_errormsg(tkc, mod, res); return res; } else { enu->flags |= TYP_FL_ISBITS; } /* move past identifier-str to stmtsep */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); typ_free_enum(enu); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: done = TRUE; break; case TK_TT_LBRACE: done = FALSE; break; default: expstr = "semi-colon or left brace"; res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, res, expstr); typ_free_enum(enu); return res; } /* get all the bit sub-clauses */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); typ_free_enum(enu); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); typ_free_enum(enu); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &enu->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { typ_free_enum(enu); return res; } } continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "bit sub-statement"; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &enu->descr, &desc, &enu->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &enu->ref, &ref, &enu->appinfoQ); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &enu->status, &stat, &enu->appinfoQ); } else if (!xml_strcmp(val, YANG_K_POSITION)) { res = yang_consume_uint32(tkc, mod, &enu->pos, &pos, &enu->appinfoQ); enu->flags |= TYP_FL_ESET; /* mark explicit set val */ } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { typ_free_enum(enu); return res; } } } /* save the bit definition */ if (retres == NO_ERR) { res = insert_yang_enumbit(tkc, mod, enu, typdef, NCX_BT_BITS, pos); if (res != NO_ERR) { typ_free_enum(enu); } } else { typ_free_enum(enu); } return retres; } /* consume_yang_bit */ /******************************************************************** * FUNCTION finish_bits_type * * Parse the next N tokens as the subsection of a * NCX_BT_BITS type that is being refined * * Current token is the left brace starting the string * sub-section. Continue until right brace or error. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typ_def_t in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t finish_bits_type (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef) { const xmlChar *val; const char *expstr; tk_type_t tktyp; status_t res, retres; boolean done, bitdone; expstr = "bit keyword"; bitdone = FALSE; retres = NO_ERR; done = FALSE; typdef->def.simple.strrest = NCX_SR_BIT; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &typdef->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_BIT)) { bitdone = TRUE; res = consume_yang_bit(tkc, mod, typdef); CHK_EXIT(res, retres); } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); } } /* check all the mandatory clauses are present */ if (!bitdone) { retres = ERR_NCX_DATA_MISSING; ncx_mod_missing_err(tkc, mod, "bits", "bit"); } return retres; } /* finish_bits_type */ /******************************************************************** * FUNCTION consume_yang_enum * * Current token is the 'enum' keyword * Process the token chain and gather the enum definition in typdef * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typedef struct in progress (already setup as SIMPLE) * simple.btyp == enum for the specific builtin type * * RETURNS: * status *********************************************************************/ static status_t consume_yang_enum (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef) { typ_enum_t *enu; xmlChar *val, *str; const char *expstr; tk_type_t tktyp; status_t res, retres; boolean done, desc, ref, stat, valdone; enu = NULL; str = NULL; res = NO_ERR; retres = NO_ERR; desc = FALSE; ref = FALSE; stat = FALSE; valdone = FALSE; expstr = "enum value string"; /* move past enum keyword * check token type, which should be a trimmed string */ res = yang_consume_string(tkc, mod, &str); CHK_EXIT(res, retres); /* validate the enum string format */ if (str) { if (!*str) { res = ERR_NCX_WRONG_LEN; log_error("\nError: Zero length enum string"); ncx_mod_exp_err(tkc, mod, res, expstr); m__free(str); str = NULL; } else if (xml_isspace(*str)) { res = ERR_NCX_INVALID_VALUE; log_error("\nError: Leading whitespace in enum string '%s'", str); ncx_mod_exp_err(tkc, mod, res, expstr); m__free(str); str = NULL; } else if (xml_isspace(str[xml_strlen(str)-1])) { res = ERR_NCX_INVALID_VALUE; log_error("\nError: Trailing whitespace in enum string '%s'", str); ncx_mod_exp_err(tkc, mod, res, expstr); m__free(str); str = NULL; } else { /* pass off the malloced string */ enu = typ_new_enum2(str); } } else { /* copy the placeholder string during error processing */ enu = typ_new_enum(NCX_EL_NONE); } /* check enum malloced OK */ if (!enu) { if (res == NO_ERR) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); } if (str) { m__free(str); } return res; } /* have malloced enum struct * move past identifier-str to stmtsep */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); typ_free_enum(enu); return res; } /* check statement exit or sub-statement start */ switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: done = TRUE; break; case TK_TT_LBRACE: done = FALSE; break; default: expstr = "semi-colon or left brace"; res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, res, expstr); typ_free_enum(enu); return res; } /* get all the enum sub-clauses */ while (!done) { res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); typ_free_enum(enu); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); typ_free_enum(enu); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &enu->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { typ_free_enum(enu); return res; } } continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "bit sub-statement"; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &enu->descr, &desc, &enu->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &enu->ref, &ref, &enu->appinfoQ); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &enu->status, &stat, &enu->appinfoQ); } else if (!xml_strcmp(val, YANG_K_VALUE)) { res = yang_consume_int32(tkc, mod, &enu->val, &valdone, &enu->appinfoQ); enu->flags |= TYP_FL_ESET; /* mark explicit set val */ } else if (!xml_strcmp(val, YANG_K_IF_FEATURE) && mod->langver == NCX_YANG_VERSION11) { res = yang_consume_iffeature(tkc, mod, &enu->iffeatureQ, &enu->appinfoQ); } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { typ_free_enum(enu); return res; } } } /* save the enum definition */ if (retres == NO_ERR) { res = insert_yang_enumbit(tkc, mod, enu, typdef, NCX_BT_ENUM, valdone); if (res != NO_ERR) { typ_free_enum(enu); } } else { typ_free_enum(enu); } return retres; } /* consume_yang_enum */ /******************************************************************** * FUNCTION finish_enum_type * * Parse the next N tokens as the subsection of a * NCX_BT_ENUM type that is being refined * * Current token is the left brace starting the string * sub-section. Continue until right brace or error. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typ_def_t in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t finish_enum_type (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef) { const xmlChar *val; const char *expstr; tk_type_t tktyp; status_t res, retres; boolean done, enumdone; expstr = "enum keyword"; enumdone = FALSE; res = NO_ERR; retres = NO_ERR; done = FALSE; typdef->def.simple.strrest = NCX_SR_ENUM; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &typdef->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_ENUM)) { enumdone = TRUE; res = consume_yang_enum(tkc, mod, typdef); CHK_EXIT(res, retres); } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); } } /* check all the mandatory clauses are present */ if (!enumdone) { retres = ERR_NCX_DATA_MISSING; ncx_mod_missing_err(tkc, mod, "enumeration", "enum"); } return retres; } /* finish_enum_type */ /******************************************************************** * FUNCTION finish_union_type * * Parse the next N tokens as the subsection of a * NCX_BT_UNION type that is being defined * * Current token is the left brace starting the string * sub-section. Continue until right brace or error. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * typdef == typ_def_t in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t finish_union_type (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef) { const xmlChar *val; const char *expstr; typ_unionnode_t *un; tk_type_t tktyp; status_t res, retres; boolean done, typedone; expstr = "type keyword"; typedone = FALSE; res = NO_ERR; retres = NO_ERR; done = FALSE; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &typdef->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_TYPE)) { typedone = TRUE; un = typ_new_unionnode(NULL); if (!un) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } else { un->typdef = typ_new_typdef(); if (!un->typdef) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); typ_free_unionnode(un); return res; } } res = yang_typ_consume_type(pcb, tkc, mod, un->typdef); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { typ_free_unionnode(un); return res; } } if (res == NO_ERR) { dlq_enque(un, &typdef->def.simple.unionQ); } else { typ_free_unionnode(un); } } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); } } /* check all the mandatory clauses are present */ if (!typedone) { retres = ERR_NCX_DATA_MISSING; ncx_mod_missing_err(tkc, mod, "union", "type"); } return retres; } /* finish_union_type */ /******************************************************************** * FUNCTION finish_leafref_type * * Parse the next N tokens as the subsection of a * NCX_BT_LEAFREF type that is being defined * * Current token is the left brace starting the string * sub-section. Continue until right brace or error. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typ_def_t in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t finish_leafref_type (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef) { typ_simple_t *sim; const xmlChar *val; const char *expstr; tk_type_t tktyp; status_t res, retres; boolean done, pathdone, constrained; expstr = "path keyword"; pathdone = FALSE; constrained = FALSE; res = NO_ERR; retres = NO_ERR; done = FALSE; sim = &typdef->def.simple; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &typdef->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_PATH)) { if (pathdone) { retres = ERR_NCX_ENTRY_EXISTS; log_error("\nError: path statement already entered"); ncx_print_errormsg(tkc, mod, retres); return res; } /* get the path string value and save it */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } /* store, parse, and validate that the leafref * is well-formed. Do not check the objects * in the path string until later */ if (TK_CUR_STR(tkc)) { /* create an XPath parser control block and * store the path string there */ if (!pathdone) { /* very little validation is done now * because the object using this leafref * data type is needed to set the starting * context node */ sim->xleafref = xpath_new_pcb(TK_CUR_VAL(tkc), NULL); if (!sim->xleafref) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } else { res = tk_check_save_origstr (tkc, TK_CUR(tkc), (const void *)&sim->xleafref->exprstr); if (res == NO_ERR) { ncx_set_error(&sim->xleafref->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); res = xpath_yang_parse_path(tkc, mod, XP_SRC_LEAFREF, sim->xleafref); } if (res != NO_ERR) { /* errors already reported */ retres = res; } } } pathdone = TRUE; res = yang_consume_semiapp(tkc, mod, &typdef->appinfoQ); CHK_EXIT(res, retres); } sim->constrained = TRUE; } else if ((mod->langver>=NCX_YANG_VERSION11) && !xml_strcmp(val, YANG_K_REQUIRE_INSTANCE)) { if (constrained) { retres = ERR_NCX_ENTRY_EXISTS; log_error("\nError: require-instance statement already entered"); ncx_print_errormsg(tkc, mod, retres); return res; } res = yang_consume_boolean(tkc, mod, &sim->constrained, &constrained, &typdef->appinfoQ); CHK_EXIT(res, retres); } else { retres = ERR_NCX_WRONG_TKTYPE; expstr = "path statement"; ncx_mod_exp_err(tkc, mod, retres, expstr); } } if (retres == NO_ERR && !pathdone) { retres = ERR_NCX_MISSING_REFTARGET; log_error("\nError: path missing in leafref type"); ncx_print_errormsg(tkc, mod, retres); } if (retres == NO_ERR && !constrained) { /* set to default */ sim->constrained = TRUE; } return retres; } /* finish_leafref_type */ /******************************************************************** * FUNCTION finish_instanceid_type * * Parse the next N tokens as the subsection of a * NCX_BT_INSTANCE_ID type that is being defined * * Current token is the left brace starting the string * sub-section. Continue until right brace or error. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typ_def_t in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t finish_instanceid_type (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef) { typ_simple_t *sim; const xmlChar *val; const char *expstr; tk_type_t tktyp; status_t res, retres; boolean done, constrained; expstr = "require-instance keyword"; constrained = FALSE; res = NO_ERR; retres = NO_ERR; done = FALSE; sim = &typdef->def.simple; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &typdef->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_REQUIRE_INSTANCE)) { res = yang_consume_boolean(tkc, mod, &sim->constrained, &constrained, &typdef->appinfoQ); CHK_EXIT(res, retres); } else { retres = ERR_NCX_WRONG_TKTYPE; expstr = "require-instance statement"; ncx_mod_exp_err(tkc, mod, retres, expstr); } } if (retres == NO_ERR && !constrained) { /* set to default */ sim->constrained = TRUE; } return retres; } /* finish_instanceid_type */ /******************************************************************** * FUNCTION finish_idref_type * * Parse the next N tokens as the subsection of a * NCX_BT_IDREF type that is being defined * * Current token is the left brace starting the string * sub-section. Continue until right brace or error. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typ_def_t in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t finish_idref_type (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef) { typ_simple_t *sim; const xmlChar *val; const char *expstr; tk_type_t tktyp; status_t res, retres; boolean done, basedone; expstr = "base keyword"; basedone = FALSE; res = NO_ERR; retres = NO_ERR; done = FALSE; sim = &typdef->def.simple; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &typdef->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_BASE)) { sim->idref.modname = ncx_get_modname(mod); res = yang_consume_pid(tkc, mod, &sim->idref.baseprefix, &sim->idref.basename, &basedone, &typdef->appinfoQ); CHK_EXIT(res, retres); } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); } } return retres; } /* finish_idref_type */ /******************************************************************** * FUNCTION resolve_minmax * * Resolve the min and max keywords in the rangeQ if present * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * rangeQ == rangeQ to process * simple == TRUE if this is a NCX_CL_SIMPLE typedef * == FALSE if this is a NCX_CL_NAMED typedef * rbtyp == range builtin type * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_minmax (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, boolean simple, ncx_btype_t rbtyp) { dlq_hdr_t *rangeQ, *parentQ; typ_rangedef_t *rv, *pfirstrv, *plastrv; typ_def_t *parentdef, *rangedef; status_t res; rangeQ = typ_get_rangeQ_con(typdef); if (!rangeQ || dlq_empty(rangeQ)) { return ERR_NCX_SKIPPED; } res = NO_ERR; pfirstrv = NULL; plastrv = NULL; /* get the parent range bounds, if any */ if (!simple) { parentdef = typ_get_parent_typdef(typdef); rangedef = typ_get_qual_typdef(parentdef, NCX_SQUAL_RANGE); if (rangedef) { parentQ = typ_get_rangeQ_con(rangedef); if (!parentQ || dlq_empty(parentQ)) { return SET_ERROR(ERR_INTERNAL_VAL); } pfirstrv = (typ_rangedef_t *)dlq_firstEntry(parentQ); plastrv = (typ_rangedef_t *)dlq_lastEntry(parentQ); } } for (rv = (typ_rangedef_t *)dlq_firstEntry(rangeQ); rv != NULL && res==NO_ERR; rv = (typ_rangedef_t *)dlq_nextEntry(rv)) { if (rv->btyp ==NCX_BT_NONE) { rv->btyp = rbtyp; } else if (rv->btyp != rbtyp) { res = ERR_NCX_WRONG_DATATYP; } /* set the lower bound if set to 'min' or 'max' */ if (rv->flags & TYP_FL_LBMIN) { if (pfirstrv) { if (pfirstrv->flags & TYP_FL_LBINF) { rv->flags |= TYP_FL_LBINF; } else if (pfirstrv->flags & TYP_FL_LBINF2) { rv->flags |= TYP_FL_LBINF2; } else { res = ncx_copy_num(&pfirstrv->lb, &rv->lb, rbtyp); } } else { if (rbtyp==NCX_BT_FLOAT64) { rv->flags |= TYP_FL_LBINF; } else { ncx_set_num_min(&rv->lb, rbtyp); } } } else if (rv->flags & TYP_FL_LBMAX) { if (plastrv) { if (plastrv->flags & TYP_FL_UBINF) { rv->flags |= TYP_FL_LBINF2; } else if (plastrv->flags & TYP_FL_UBINF2) { rv->flags |= TYP_FL_LBINF; } else { res = ncx_copy_num(&plastrv->ub, &rv->lb, rbtyp); } } else { if (rbtyp==NCX_BT_FLOAT64) { rv->flags |= TYP_FL_LBINF2; } else { ncx_set_num_max(&rv->lb, rbtyp); } } } else if (rv->lbstr) { res = ncx_decode_num(rv->lbstr, rbtyp, &rv->lb); } if (res != NO_ERR) { continue; } /* set the upper bound if set to 'min' or 'max' */ if (rv->flags & TYP_FL_UBMAX) { if (plastrv) { if (plastrv->flags & TYP_FL_UBINF) { rv->flags |= TYP_FL_UBINF; } else if (plastrv->flags & TYP_FL_UBINF2) { rv->flags |= TYP_FL_UBINF2; } else { res = ncx_copy_num(&plastrv->ub, &rv->ub, rbtyp); } } else { if (rbtyp==NCX_BT_FLOAT64) { rv->flags |= TYP_FL_UBINF; } else { ncx_set_num_max(&rv->ub, rbtyp); } } } else if (rv->flags & TYP_FL_UBMIN) { if (pfirstrv) { if (pfirstrv->flags & TYP_FL_LBINF) { rv->flags |= TYP_FL_UBINF2; } else if (pfirstrv->flags & TYP_FL_LBINF2) { rv->flags |= TYP_FL_UBINF; } else { res = ncx_copy_num(&pfirstrv->lb, &rv->ub, rbtyp); } } else { if (rbtyp==NCX_BT_FLOAT64) { rv->flags |= TYP_FL_UBINF2; } else { ncx_set_num_min(&rv->ub, rbtyp); } } } else if (rv->ubstr) { res = ncx_decode_num(rv->ubstr, rbtyp, &rv->ub); } } if (res != NO_ERR) { if (simple) { tkc->curerr = &typdef->def.simple.range.tkerr; } else { tkc->curerr = &typdef->tkerr; } ncx_print_errormsg(tkc, mod, res); } else { /* set flags even if boundary set explicitly so * the ncxdump program will suppress these clauses * unless really needed */ rv = (typ_rangedef_t *)dlq_firstEntry(rangeQ); if (rv && ncx_is_min(&rv->lb, rbtyp)) { rv->flags |= TYP_FL_LBMIN; } rv = (typ_rangedef_t *)dlq_lastEntry(rangeQ); if (rv && ncx_is_max(&rv->ub, rbtyp)) { rv->flags |= TYP_FL_UBMAX; } } return res; } /* resolve_minmax */ /******************************************************************** * FUNCTION finish_yang_range * * Validate the typdef chain range definitions * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typ_def_t in progress * rbtyp == range builtin type * * RETURNS: * status of the operation *********************************************************************/ static status_t finish_yang_range (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, ncx_btype_t rbtyp) { dlq_hdr_t *rangeQ; typ_def_t *parentdef; status_t res; res = NO_ERR; switch (typdef->tclass) { case NCX_CL_NAMED: parentdef = typ_get_parent_typdef(typdef); if (parentdef) { res = finish_yang_range(tkc, mod, parentdef, rbtyp); } else { res = ERR_NCX_MISSING_TYPE; } if (res == NO_ERR) { res = resolve_minmax(tkc, mod, typdef, FALSE, rbtyp); if (res == NO_ERR) { rangeQ = typ_get_rangeQ_con(typdef); typ_normalize_rangeQ(rangeQ, rbtyp); } else if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } } break; case NCX_CL_SIMPLE: /* this is the final stop in the type chain * if this typdef has a range, then check it * and convert min/max to real numbers */ res = resolve_minmax(tkc, mod, typdef, TRUE, rbtyp); if (res == NO_ERR) { rangeQ = typ_get_rangeQ_con(typdef); typ_normalize_rangeQ(rangeQ, rbtyp); } else if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } break; default: break; } return res; } /* finish_yang_range */ /******************************************************************** * FUNCTION rv_fits_in_rangedef * * Check if the testrv fits within the checkrv * * INPUTS: * btyp == number base type * testrv == rangedef to test * checkrv == rangedef to test against * * RETURNS: * status, NO_ERR if rangedef is valid and passes the range * defined by the rangedefs in checkQ *********************************************************************/ static status_t rv_fits_in_rangedef (ncx_btype_t btyp, const typ_rangedef_t *testrv, const typ_rangedef_t *checkrv) { int32 cmp; boolean lbok, lbok2, ubok, ubok2; lbok = FALSE; lbok2 = FALSE; ubok = FALSE; ubok2 = FALSE; /* make sure any INF corner cases are met */ if (testrv->flags & TYP_FL_LBINF && !(checkrv->flags & TYP_FL_LBINF)) { return ERR_NCX_NOT_IN_RANGE; } if (testrv->flags & TYP_FL_LBINF2 && !(checkrv->flags & TYP_FL_LBINF2)) { return ERR_NCX_NOT_IN_RANGE; } if (testrv->flags & TYP_FL_UBINF && !(checkrv->flags & TYP_FL_UBINF)) { return ERR_NCX_NOT_IN_RANGE; } if (testrv->flags & TYP_FL_UBINF2 && !(checkrv->flags & TYP_FL_UBINF2)) { return ERR_NCX_NOT_IN_RANGE; } /* check lower bound */ if (!(checkrv->flags & TYP_FL_LBINF)) { cmp = ncx_compare_nums(&testrv->lb, &checkrv->lb, btyp); if (cmp >= 0) { lbok = TRUE; } } else { /* LB == -INF, always passes the test */ lbok = TRUE; } if (!(checkrv->flags & TYP_FL_UBINF)) { cmp = ncx_compare_nums(&testrv->lb, &checkrv->ub, btyp); if (cmp <= 0) { lbok2 = TRUE; } } else { /* UB == INF, always passes the test */ lbok2 = TRUE; } /* check upper bound */ if (!(checkrv->flags & TYP_FL_LBINF)) { cmp = ncx_compare_nums(&testrv->ub, &checkrv->lb, btyp); if (cmp >= 0) { ubok = TRUE; } } else { /* LB == -INF, always passes the test */ ubok = TRUE; } if (!(checkrv->flags & TYP_FL_UBINF)) { cmp = ncx_compare_nums(&testrv->ub, &checkrv->ub, btyp); if (cmp <= 0) { ubok2 = TRUE; } } else { /* UB == INF, always passes the test */ ubok2 = TRUE; } return (lbok && lbok2 && ubok && ubok2) ? NO_ERR : ERR_NCX_NOT_IN_RANGE; } /* rv_fits_in_rangedef */ /******************************************************************** * FUNCTION validate_range_chain * * Final validatation of all range definitions within * the type definition * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * intypdef == typ_def_t in progress to validate * typname == name from typedef (may be NULL for unnamed types) * * RETURNS: * status of the operation *********************************************************************/ static status_t validate_range_chain (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *intypdef, const xmlChar *typname) { dlq_hdr_t *rangeQ, *prangeQ; typ_def_t *typdef, *parentdef; typ_rangedef_t *rv, *checkrv; typ_range_t *range; ncx_num_t *curmax; status_t res, res2; boolean done, errdone, ubinf, first; ncx_btype_t rbtyp; int32 retval; typdef = typ_get_qual_typdef(intypdef, NCX_SQUAL_RANGE); if (!typdef) { return NO_ERR; } res = NO_ERR; errdone = FALSE; rbtyp = typ_get_range_type(typ_get_basetype(typdef)); if (!typname) { typname = (const xmlChar *)"(unnamed type)"; } /* Test 1: Individual tests * validate each individual range definition in each * typdef in the chain for the specified typ_template_t * Check that each rangeQ is well-formed on its own */ while (typdef && res==NO_ERR) { rangeQ = typ_get_rangeQ_con(typdef); if (!rangeQ || dlq_empty(rangeQ)) { return SET_ERROR(ERR_INTERNAL_VAL); } curmax = NULL; ubinf = FALSE; first = TRUE; for (rv = (typ_rangedef_t *)dlq_firstEntry(rangeQ); rv != NULL && res==NO_ERR; rv = (typ_rangedef_t *)dlq_nextEntry(rv)) { /* check range-part after INF */ if (ubinf) { res = ERR_NCX_INVALID_RANGE; log_error("\nError: INF already used and no more range" " part clauses are allowed in type '%s'", typname); continue; } /* check upper-bound not >= lower-bound */ retval = ncx_compare_nums(&rv->lb, &rv->ub, rbtyp); if (retval == 1) { res = ERR_NCX_INVALID_RANGE; log_error("\nError: lower bound is greater than " "upper bound in range part clause in type '%s'", typname); continue; } /* check -INF used in a range-part other than first */ if (rv->flags & TYP_FL_LBINF) { if (!first) { res = ERR_NCX_OVERLAP_RANGE; log_error("\nError: -INF not allowed here in type '%s'", typname); continue; } } /* check this lower-bound > any previous upper bound */ if (rv->flags & TYP_FL_UBINF) { ubinf = TRUE; } else if (!curmax) { curmax = &rv->ub; } else { retval = ncx_compare_nums(&rv->lb, curmax, rbtyp); if (retval != 1) { res = ERR_NCX_OVERLAP_RANGE; } else { curmax = &rv->ub; } } first = FALSE; } if (res == NO_ERR) { typdef = typ_get_parent_typdef(typdef); if (typdef) { typdef = typ_get_qual_typdef(typdef, NCX_SQUAL_RANGE); } } else { tkc->curerr = &typdef->tkerr; } } if (res != NO_ERR && !errdone) { ncx_print_errormsg(tkc, mod, res); return res; } /* Test 2: Typdef Chain tests * validate each individual range definition in each * typdef in the chain against any previously defined * range definitions in the typdef chain */ typdef = typ_get_qual_typdef(intypdef, NCX_SQUAL_RANGE); while (typdef && res==NO_ERR) { /* have a typdef with a range definition rangeQ contains the rangedef * structs to test For each parent typdef with a range clause: * check raneQ contents against prangeQ contents * * rv is the rangedef to test * checkrv is the rangedef to test against */ rangeQ = typ_get_rangeQ_con(typdef); for (rv = (typ_rangedef_t *)dlq_firstEntry(rangeQ); rv != NULL && res==NO_ERR; rv = (typ_rangedef_t *)dlq_nextEntry(rv)) { parentdef = typ_get_parent_typdef(typdef); if (parentdef) { parentdef = typ_get_qual_typdef(parentdef, NCX_SQUAL_RANGE); while (parentdef && res==NO_ERR) { prangeQ = typ_get_rangeQ_con(parentdef); /* rv rangedef just has to fit within one of the checkrv * rangedefs to pass the test */ done = FALSE; for (checkrv = (typ_rangedef_t *)dlq_firstEntry(prangeQ); checkrv != NULL && !done; checkrv = (typ_rangedef_t *) dlq_nextEntry(checkrv)) { res2 = rv_fits_in_rangedef(rbtyp, rv, checkrv); if (res2 == NO_ERR) { done = TRUE; } } if (!done) { res = ERR_NCX_NOT_IN_RANGE; log_error("\nError: Range definition not a " "valid restriction of parent type '%s'", typname); } else { parentdef = typ_get_parent_typdef(parentdef); if (parentdef) { parentdef = typ_get_qual_typdef(parentdef, NCX_SQUAL_RANGE); } } } } } if (res == NO_ERR) { /* setup next loop */ typdef = typ_get_parent_typdef(typdef); if (typdef) { typdef = typ_get_qual_typdef(typdef, NCX_SQUAL_RANGE); } } else { /* set error token */ range = typ_get_range_con(typdef); if (range) { tkc->curerr = &range->tkerr; } else { tkc->curerr = &typdef->tkerr; } } } if (res != NO_ERR && !errdone) { ncx_print_errormsg(tkc, mod, res); } return res; } /* validate_range_chain */ #if 0 /******************************************************************** * FUNCTION check_defval * * Check the defval agtainst the typdef * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typdef to check * obj == obj_template_t containing the union (may be NULL) * btyp == base type for the typdef * badvalok == TRUE if invalid value is OK (called from union) * FALSE if must be OK (called from plain simple type) * defval == default value, if any * hasdefval == address of has default value return value * name == typ_template_t name, if any * * OUTPUTS: * *hasdefval == default value return value * * RETURNS: * status *********************************************************************/ static status_t check_defval (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, obj_template_t *obj, ncx_btype_t btyp, boolean badvalok, const xmlChar *defval, boolean *hasdefval, const xmlChar *name) { status_t res = NO_ERR; *hasdefval = FALSE; if (defval != NULL) { *hasdefval = TRUE; res = val_simval_ok(typdef, defval); if (res != NO_ERR) { if (!badvalok) { if (obj) { log_error("\nError: %s '%s' has invalid " "default value (%s)", (name) ? "Type" : (const char *)obj_get_typestr(obj), (name) ? name : obj_get_name(obj), defval); } else { log_error("\nError: %s '%s' has invalid " "default value (%s)", (name) ? "Type" : "leaf or leaf-list", (name) ? (const char *)name : "--", defval); } tkc->curerr = &typdef->tkerr; ncx_print_errormsg(tkc, mod, res); } } } else if (typdef->tclass == NCX_CL_NAMED) { const xmlChar *typdefval; typdefval = typ_get_defval(typdef->def.named.typ); if (typdefval) { *hasdefval = TRUE; res = val_simval_ok(typdef, typdefval); if (res != NO_ERR) { if (!badvalok) { if (obj) { log_error("\nError: %s '%s' has invalid " "inherited default value (%s)", (name) ? "Type" : (const char *)obj_get_typestr(obj), (name) ? name : obj_get_name(obj), typdefval); } else { log_error("\nError: %s '%s' has invalid " "inherited default value (%s)", (name) ? "Type" : "leaf or leaf-list", (name) ? (const char *)name : "--", typdefval); } tkc->curerr = &typdef->tkerr; ncx_print_errormsg(tkc, mod, res); } } } } return res; } /* check_defval */ #endif /******************************************************************** * FUNCTION check_defval * * Check the defval agtainst the typdef * * INPUTS: * tkc == token chain * mod == module in progress * typdef == typdef to check * obj == obj_template_t containing the union (may be NULL) * btyp == base type for the typdef * badvalok == TRUE if invalid value is OK (called from union) * FALSE if must be OK (called from plain simple type) * defval == default value, if any * name == typ_template_t name, if any * * RETURNS: * status *********************************************************************/ static status_t check_defval (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, obj_template_t *obj, ncx_btype_t btyp, boolean badvalok, const xmlChar *defval, const xmlChar *name) { status_t res = ERR_NCX_SKIPPED; if (defval != NULL) { res = val_simval_ok_max(typdef, defval, NULL, mod, TRUE); if (res != NO_ERR) { if (!badvalok) { if (obj) { log_error("\nError: %s '%s' has invalid " "default value (%s)", (name) ? "Type" : (const char *)obj_get_typestr(obj), (name) ? name : obj_get_name(obj), defval); } else { log_error("\nError: %s '%s' has invalid " "default value (%s)", (name) ? "Type" : "leaf or leaf-list", (name) ? (const char *)name : "--", defval); } tkc->curerr = &typdef->tkerr; ncx_print_errormsg(tkc, mod, res); } } } return res; } /* check_defval */ /******************************************************************** * FUNCTION resolve_union_type * * Check for named type dependency loops * Called during phase 2 of module parsing * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * unionQ == Q of unfinished typ_unionnode_t structs * parent == obj_template_t containing the union (may be NULL) * grp == grp_template_t containing the union (may be NULL) * defval == default value, if any * name == typ_template_t name, if any * * RETURNS: * status *********************************************************************/ static status_t resolve_union_type (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *unionQ, obj_template_t *parent, grp_template_t *grp, const xmlChar *defval, const xmlChar *name) { typ_unionnode_t *un = NULL; status_t res = NO_ERR, retres = NO_ERR; ncx_btype_t btyp = NCX_BT_NONE; boolean defvaldone = FALSE; /* first resolve all the local type names */ for (un = (typ_unionnode_t *)dlq_firstEntry(unionQ); un != NULL && retres == NO_ERR; un = (typ_unionnode_t *)dlq_nextEntry(un)) { res = resolve_type(pcb, tkc, mod, un->typdef, NULL, NULL, parent, grp); CHK_EXIT(res, retres); btyp = typ_get_basetype(un->typdef); if (btyp != NCX_BT_NONE && !typ_ok_for_union(btyp)) { retres = ERR_NCX_WRONG_TYPE; log_error("\nError: builtin type '%s' not allowed" " within a union", tk_get_btype_sym(btyp)); tkc->curerr = &un->typdef->tkerr; ncx_print_errormsg(tkc, mod, retres); } /* check default value if any defined */ if ((retres == NO_ERR) && !defvaldone && defval && (btyp != NCX_BT_NONE) && typ_ok(un->typdef)) { res = check_defval(tkc, mod, un->typdef, parent, btyp, TRUE, defval, name); if (res == NO_ERR) { defvaldone = TRUE; } } if (un->typdef->tclass == NCX_CL_NAMED) { un->typ = un->typdef->def.named.typ; /* keep the typdef around for yangdump */ } } /* only report error if defval passed caused the error * otherwise the resolve_type pass for member types in * the union will print a typdef defualt error */ if (retres == NO_ERR && defval != NULL && !defvaldone) { retres = ERR_NCX_INVALID_VALUE; if (parent != NULL) { log_error("\nError: Union %s '%s' has invalid " "default value (%s)", (name) ? "type" : (const char *)obj_get_typestr(parent), (name) ? name : obj_get_name(parent), defval); } else { log_error("\nError: Union %s '%s' has invalid default value", (name) ? "Type" : "leaf or leaf-list", (name) ? (const char *)name : "--", defval); } if (parent) { tkc->curerr = &parent->tkerr; } else if (grp) { tkc->curerr = &grp->tkerr; } ncx_print_errormsg(tkc, mod, retres); } return retres; } /* resolve_union_type */ /******************************************************************** * FUNCTION resolve_union_type_final * * Check for XPath defaults * * INPUTS: * tkc == token chain * mod == module in progress * unionQ == Q of unfinished typ_unionnode_t structs * parent == obj_template_t containing the union (may be NULL) * defval == default value, if any * name == typ_template_t name, if any * * RETURNS: * status *********************************************************************/ static status_t resolve_union_type_final (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *unionQ, obj_template_t *parent, const xmlChar *defval, const xmlChar *name) { typ_unionnode_t *un; status_t res = NO_ERR; ncx_btype_t btyp = NCX_BT_NONE; boolean needtest = FALSE, defvaldone = FALSE; if (!defval) { return NO_ERR; } /* check if this extra test is needed */ for (un = (typ_unionnode_t *)dlq_firstEntry(unionQ); un != NULL && !needtest; un = (typ_unionnode_t *)dlq_nextEntry(un)) { if (!typ_ok(un->typdef)) { return NO_ERR; } btyp = typ_get_basetype(un->typdef); if (btyp == NCX_BT_LEAFREF || btyp == NCX_BT_UNION || typ_is_xpath_string(un->typdef)) { needtest = TRUE; } } if (!needtest) { return NO_ERR; } /* run the extra test */ for (un = (typ_unionnode_t *)dlq_firstEntry(unionQ); un != NULL; un = (typ_unionnode_t *)dlq_nextEntry(un)) { /* check default value if any defined */ if (defval && !defvaldone && (btyp != NCX_BT_NONE)) { res = check_defval(tkc, mod, un->typdef, parent, btyp, TRUE, defval, name); if (res == NO_ERR) { defvaldone = TRUE; } } } /* only report error if defval passed caused the error * otherwise the resolve_type pass for member types in * the union will print a typdef default error */ if (defval && !defvaldone) { res = ERR_NCX_INVALID_VALUE; if (parent) { log_error("\nError: Union %s '%s' has invalid " "default value (%s)", (name) ? "type" : (const char *)obj_get_typestr(parent), (name) ? name : obj_get_name(parent), defval); } else { log_error("\nError: Union %s '%s' has invalid default value", (name) ? "Type" : "leaf or leaf-list", (name) ? (const char *)name : "--", defval); } if (tkc) { if (parent) { tkc->curerr = &parent->tkerr; } ncx_print_errormsg(tkc, mod, res); } } return res; } /* resolve_union_type_final */ /******************************************************************* * FUNCTION resolve_type * * Analyze the typdef * Finish if a named type, and/or range clauses present, * which were left unfinished due to possible forward references * * Algorithm for checking named types in 4 separate loops: * 1) resolve all open type name references * 2) check for any name loops in all named types * 3) check that all base types and builtin types * in each type chain are correct. Also check that * all restrictions given are correct for that type * 4) Check all range clauses and resolve all min/max * keyword uses to decimal numbers and validate that * each range is well-formed. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typdef == typ_def_t struct to check * name == name of the typ_template that contains 'typdef' * defval == default value string to check (may be NULL) * obj == obj_template containing this typdef, NULL if top-level * grp == grp_template containing this typdef, otherwise NULL * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_type (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, const xmlChar *name, const xmlChar *defval, obj_template_t *obj, grp_template_t *grp) { typ_def_t *testdef; typ_template_t *errtyp; grp_template_t *nextgrp; typ_enum_t *enu; typ_idref_t *idref; ncx_identity_t *testidentity; const xmlChar *errname = NULL; status_t res = NO_ERR, retres = NO_ERR; boolean errdone = FALSE; ncx_btype_t btyp; if (typdef->tclass == NCX_CL_BASE) { return NO_ERR; } btyp = typ_get_basetype(typdef); #ifdef YANG_TYP_DEBUG if (LOGDEBUG4) { log_debug4("\nyang_typ: resolve type '%s' (name %s) on line %u", (typdef->typenamestr) ? typdef->typenamestr : NCX_EL_NONE, (name) ? name : NCX_EL_NONE, typdef->tkerr.linenum); } #endif /* check the appinfoQ */ res = ncx_resolve_appinfoQ(pcb, tkc, mod, &typdef->appinfoQ); if (NEED_EXIT(res)) { return res; } else { res = NO_ERR; } /* first resolve all the local type names */ if (res == NO_ERR && btyp != NCX_BT_UNION) { res = obj_set_named_type(tkc, mod, name, typdef, obj, grp); } /* type name loop check */ if (res == NO_ERR && btyp != NCX_BT_UNION) { res = loop_test(typdef); } /* If no loops then make sure base type is correct */ if (res == NO_ERR && btyp != NCX_BT_UNION) { res = restriction_test(tkc, mod, typdef); if (res != NO_ERR) { errdone = TRUE; } } /* print any errors so far, and exit if the typdef may * not be stable enough to test further */ if (res != NO_ERR) { if (!errdone) { tkc->curerr = &typdef->tkerr; ncx_print_errormsg(tkc, mod, res); } return res; } /* OK so far: safe to process the typdef further * go through again and resolve the range definitions * This is needed if min and max keywords used * Errors printed in the called fn */ if (btyp != NCX_BT_UNION) { res = finish_yang_range(tkc, mod, typdef, typ_get_range_type(typ_get_basetype(typdef))); } /* validate that the ranges are valid now that min/max is set * and all parent ranges are also set. A range must be valid * wrt/ its parent range definition(s) * Errors printed in the called fn */ if (res == NO_ERR && btyp != NCX_BT_UNION) { if (!name && (typdef->tclass == NCX_CL_NAMED)) { /* name field just used for error messages */ errname = typdef->typenamestr; } else { errname = name; } res = validate_range_chain(tkc, mod, typdef, errname); } /* check built-in type specific details */ if (res == NO_ERR) { switch (btyp) { case NCX_BT_ENUM: case NCX_BT_BITS: /* check each enumeration appinfoQ */ if (typdef->tclass == NCX_CL_SIMPLE) { for (enu = (typ_enum_t *) dlq_firstEntry(&typdef->def.simple.valQ); enu != NO_ERR; enu = (typ_enum_t *)dlq_nextEntry(enu)) { xmlChar *namestr; res = ncx_resolve_appinfoQ(pcb, tkc, mod, &enu->appinfoQ); CHK_EXIT(res, retres); if(asprintf((char **)&namestr, "enum '%s'", enu->name) < 0) { res = ERR_INTERNAL_MEM; } else { res = ncx_resolve_iffeatureQ(pcb, tkc, mod, namestr, &enu->iffeatureQ); } CHK_EXIT(res, retres); } res = NO_ERR; } break; case NCX_BT_IDREF: idref = typ_get_idref(typdef); if (idref) { ncx_module_t *referer_mod; testidentity = NULL; if(0==strcmp(mod->name,idref->modname)) { referer_mod=mod; } else { referer_mod = ncx_find_module(idref->modname,NULL); } if (idref->baseprefix && xml_strcmp(idref->baseprefix, mod->prefix)) { /* find the identity referenced locally in another directly imported module */ res = yang_find_imp_identity(pcb, tkc, referer_mod, idref->baseprefix, idref->basename, &typdef->tkerr, &testidentity); } else { testidentity = ncx_find_identity(referer_mod, idref->basename, FALSE); if (!testidentity) { res = ERR_NCX_DEF_NOT_FOUND; } else { idref->base = testidentity; } } idref->base = testidentity; if (res != NO_ERR) { log_error("\nError: identityref '%s' has invalid " "base value (%s)", (name) ? (const char *)name : "--", idref->basename); tkc->curerr = &typdef->tkerr; ncx_print_errormsg(tkc, mod, res); } } else { res = SET_ERROR(ERR_INTERNAL_VAL); } break; default: ; } } /* special check for union typdefs, errors printed by called fn */ if (res == NO_ERR && typdef->tclass == NCX_CL_SIMPLE && btyp == NCX_BT_UNION) { testdef = typ_get_base_typdef(typdef); res = resolve_union_type(pcb, tkc, mod, &testdef->def.simple.unionQ, obj, grp, defval, name); } /* check shadow typedef name error, even if other errors so far */ if (name != NULL) { errtyp = NULL; /* if object directly within a grouping, then need * to make sure that parent groupings do not contain * this type definition */ if (obj && obj->grp && obj->grp->parentgrp) { nextgrp = obj->grp->parentgrp; while (nextgrp) { errtyp = ncx_find_type_que(&nextgrp->typedefQ, name); if (errtyp) { nextgrp = NULL; } else { nextgrp = nextgrp->parentgrp; } } } /* check local typedef shadowed further up the chain */ if (!errtyp && obj && obj->parent && !obj_is_root(obj->parent)) { errtyp = obj_find_type(obj->parent, name); } /* check module-global (exportable) typedef shadowed * only check for nested typedefs */ if (!errtyp && obj ) { errtyp = ncx_find_type(mod, name, TRUE); } if (errtyp) { res = ERR_NCX_DUP_ENTRY; log_error("\nError: local typedef %s shadows " "definition in %s on line %u", name, errtyp->tkerr.mod->name, errtyp->tkerr.linenum); tkc->curerr = &typdef->tkerr; ncx_print_errormsg(tkc, mod, res); } } /* check default value if any defined */ if ((res == NO_ERR) && (btyp != NCX_BT_NONE) && (btyp != NCX_BT_UNION) && typ_ok(typdef)) { if (btyp == NCX_BT_LEAFREF || typ_is_xpath_string(typdef)) { if (LOGDEBUG4) { log_debug4("\nyang_typ: postponing default check " "for type '%s'", (errname) ? errname : (typdef->typenamestr) ? typdef->typenamestr : NCX_EL_NONE); } } else { if (defval) { res = check_defval(tkc, mod, typdef, obj, btyp, FALSE, defval, name); } } } return (retres != NO_ERR) ? retres : res; } /* resolve_type */ /******************************************************************* * FUNCTION resolve_type_final * * Finish checking default values * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * obj == object containing this type (may be NULL) * typdef == typ_def_t struct to check * defval == default value string to check (may be NULL) * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_type_final (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, typ_def_t *typdef, const xmlChar *defval) { const xmlChar *typname; xpath_pcb_t *pcb; obj_template_t *leafobj; status_t res = NO_ERR; ncx_btype_t btyp; if (!defval || !typ_ok(typdef) || typdef->tclass == NCX_CL_BASE ) { /* skip this test if typdef malformed */ return res; } btyp = typ_get_basetype(typdef); if ((btyp != NCX_BT_LEAFREF) && (btyp != NCX_BT_UNION) && !typ_is_xpath_string(typdef) ) { return res; } typname = (typdef->typenamestr) ? typdef->typenamestr : NCX_EL_NONE; log_debug4("\nyang_typ: resolve type final '%s'", typname); /* check default value if any defined */ if (btyp == NCX_BT_LEAFREF || btyp == NCX_BT_INSTANCE_ID) { pcb = typ_get_leafref_pcb(typdef); if (tkc) { tkc->curerr = &pcb->tkerr; } leafobj = NULL; if (!obj) { obj = ncx_get_gen_root(); } res = xpath_yang_validate_path(mod, obj, pcb, FALSE, &leafobj); if (res == NO_ERR && leafobj != NULL) { typ_set_xref_typdef(typdef, obj_get_typdef(leafobj)); if (obj->objtype == OBJ_TYP_LEAF) { obj->def.leaf->leafrefobj = leafobj; } else { obj->def.leaflist->leafrefobj = leafobj; } } } if (res != NO_ERR) { return res; } if (btyp == NCX_BT_UNION) { if (defval) { res = check_defval(tkc, mod, typdef, obj, btyp, FALSE, defval, typname); } } else if (typdef->tclass == NCX_CL_SIMPLE) { /* special check for union typdefs, errors printed by called fn */ res = resolve_union_type_final(tkc, mod, &typdef->def.simple.unionQ, obj, defval, (obj) ? obj_get_name(obj) : NULL); } return res; } /* resolve_type_final */ /******************************************************************** * FUNCTION resolve_typedef * * Analyze the typdef * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typ == typ_template struct to check * obj == obj_template containing this typdef, NULL if top-level * grp == grp_template containing this typdef, NULL if none * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_typedef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, typ_template_t *typ, obj_template_t *obj, grp_template_t *grp) { status_t res, retres = NO_ERR; /* check the appinfoQ */ typ->res = res = ncx_resolve_appinfoQ(pcb, tkc, mod, &typ->appinfoQ); CHK_EXIT(res, retres); typ->res = res = resolve_type(pcb, tkc, mod, &typ->typdef, typ->name, typ->defval, obj, grp); CHK_EXIT(res, retres); return retres; } /* resolve_typedef */ /******************************************************************** * FUNCTION resolve_typedef_final * * Analyze the typdef (check defaults for XPath types) * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typ == typ_template struct to check * obj == obj_template containing this typdef, NULL if top-level * grp == grp_template containing this typdef, NULL if none * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_typedef_final (tk_chain_t *tkc, ncx_module_t *mod, typ_template_t *typ) { status_t res; if (typ->res != NO_ERR) { return NO_ERR; } typ->res = res = resolve_type_final(tkc, mod, NULL, &typ->typdef, typ->defval); return res; } /* resolve_typedef_final */ /******************************************************************** * FUNCTION consume_type * * Parse the next N tokens as a type clause * Add to the typ_template_t struct in progress * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'type' keyword * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * intypdef == struct that will get the type info * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_type (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *intypdef, boolean metamode) { const char *expstr; typ_template_t *imptyp; typ_def_t *typdef; ncx_btype_t btyp; boolean extonly, derived; status_t res, retres; expstr = "type name"; imptyp = NULL; extonly = FALSE; derived = FALSE; res = NO_ERR; retres = NO_ERR; btyp = NCX_BT_NONE; ncx_set_error(&intypdef->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); /* Get the mandatory base type */ res = yang_consume_pid_string(tkc, mod, &intypdef->prefix, &intypdef->typenamestr); CHK_EXIT(res, retres); /* got ID and prefix if there is one */ if (intypdef->prefix && intypdef->typenamestr && xml_strcmp(intypdef->prefix, mod->prefix)) { /* real import - not the same as this module's prefix */ res = yang_find_imp_typedef(pcb, tkc, mod, intypdef->prefix, intypdef->typenamestr, NULL, &imptyp); if (res != NO_ERR) { CHK_EXIT(res, retres); /* type not found but continue processing errors for now */ typ_init_named(intypdef); } else { /* found named type OK, setup typdef */ typ_init_named(intypdef); btyp = typ_get_basetype(&imptyp->typdef); typ_set_named_typdef(intypdef, imptyp); } } else if (intypdef->typenamestr) { /* there was no real prefix, so try to resolve this ID * as a builtin type, else save as a local type * btyp == NCX_BT_NONE indicates a local type */ btyp = tk_get_yang_btype_id(intypdef->typenamestr, xml_strlen(intypdef->typenamestr)); if (btyp != NCX_BT_NONE) { /* base type is builtin type */ typ_init_simple(intypdef, btyp); } else { /* base type local named type */ typ_init_named(intypdef); } } else { /* not even a typename to work with, error already set */ typ_init_named(intypdef); } /* check for ending semi-colon or starting left brace */ if (!metamode) { res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } } if (TK_CUR_TYP(tkc) == TK_TT_SEMICOL || metamode) { /* got the type name and that's all. * Check if a named imported type is given * or a builtin type name, and gen an error * if any subclauses are expected */ if (imptyp) { return NO_ERR; } /* check if builtin type and missing data */ switch (btyp) { case NCX_BT_BITS: retres = ERR_NCX_MISSING_TYPE; expstr = "bit definitions"; break; case NCX_BT_ENUM: retres = ERR_NCX_MISSING_TYPE; expstr = "enum definitions"; break; case NCX_BT_UNION: retres = ERR_NCX_MISSING_TYPE; expstr = "union member definitions"; break; case NCX_BT_LEAFREF: retres = ERR_NCX_MISSING_TYPE; expstr = "path specifier"; break; case NCX_BT_NONE: if (metamode && typ_get_basetype(intypdef) == NCX_BT_NONE) { retres = obj_set_named_type(tkc, mod, NULL, intypdef, NULL, NULL); } return retres; default: if (btyp == NCX_BT_INSTANCE_ID) { intypdef->def.simple.constrained = TRUE; } return NO_ERR; } ncx_mod_exp_err(tkc, mod, retres, expstr); return retres; } else if (TK_CUR_TYP(tkc) != TK_TT_LBRACE) { retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); return retres; } /* got left brace, get the type sub-section clauses * adding restrictions to an imported type or * to a local named type (btyp not set yet) */ if (imptyp || btyp == NCX_BT_NONE) { res = typ_set_new_named(intypdef, btyp); if (res == NO_ERR) { typdef = typ_get_new_named(intypdef); derived = TRUE; typdef->def.simple.flags |= TYP_FL_REPLACE; } else { ncx_print_errormsg(tkc, mod, res); return res; } } else { typdef = intypdef; } /* check for hard-wired builtin type sub-sections */ switch (btyp) { case NCX_BT_NONE: /* extending a local type that has not been resolved yet */ res = finish_unknown_type(tkc, mod, typdef); CHK_EXIT(res, retres); break; case NCX_BT_ANYDATA: case NCX_BT_ANYXML: extonly = TRUE; break; case NCX_BT_BITS: if (derived) { extonly = TRUE; } else { res = finish_bits_type(tkc, mod, typdef); CHK_EXIT(res, retres); } break; case NCX_BT_ENUM: if (derived) { extonly = TRUE; } else { res = finish_enum_type(tkc, mod, typdef); CHK_EXIT(res, retres); } break; case NCX_BT_EMPTY: extonly = TRUE; break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: res = finish_number_type(tkc, mod, btyp, typdef); CHK_EXIT(res, retres); break; case NCX_BT_STRING: case NCX_BT_BINARY: res = finish_string_type(tkc, mod, typdef, btyp); CHK_EXIT(res, retres); break; case NCX_BT_UNION: if (derived) { extonly = TRUE; } else { res = finish_union_type(pcb, tkc, mod, typdef); CHK_EXIT(res, retres); } break; case NCX_BT_LEAFREF: if (derived) { extonly = TRUE; } else { res = finish_leafref_type(tkc, mod, typdef); CHK_EXIT(res, retres); } break; case NCX_BT_IDREF: if (derived) { extonly = TRUE; } else { res = finish_idref_type(tkc, mod, typdef); CHK_EXIT(res, retres); } break; case NCX_BT_INSTANCE_ID: if (derived) { extonly = TRUE; } else { res = finish_instanceid_type(tkc, mod, typdef); CHK_EXIT(res, retres); } break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); ncx_print_errormsg(tkc, mod, retres); } /* check if this sub-section is only allowed * to have extensions in it; no YANG statements */ if (extonly) { /* give back left brace and get the appinfo */ TK_BKUP(tkc); res = yang_consume_semiapp(tkc, mod, &typdef->appinfoQ); CHK_EXIT(res, retres); } return retres; } /* consume_type */ /************** E X T E R N A L F U N C T I O N S **************/ /******************************************************************** * FUNCTION yang_typ_consume_type * * Parse the next N tokens as a type clause * Add to the typ_template_t struct in progress * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'type' keyword * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * intypdef == struct that will get the type info * * RETURNS: * status of the operation *********************************************************************/ status_t yang_typ_consume_type (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *intypdef) { #ifdef DEBUG if (!tkc || !mod || !intypdef) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return consume_type(pcb, tkc, mod, intypdef, FALSE); } /* yang_typ_consume_type */ /******************************************************************** * FUNCTION yang_typ_consume_metadata_type * * Parse the next token as a type declaration * for an ncx:metadata definition * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * intypdef == struct that will get the type info * * RETURNS: * status of the operation *********************************************************************/ status_t yang_typ_consume_metadata_type( yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *intypdef ) { assert ( pcb && "pcb is NULL" ); assert ( tkc && "tkc is NULL" ); assert ( mod && "mod is NULL" ); assert ( intypdef && "intypdef is NULL" ); return consume_type(pcb, tkc, mod, intypdef, TRUE); } /* yang_typ_consume_metadata_type */ /******************************************************************** * FUNCTION yang_typ_consume_typedef * * Parse the next N tokens as a typedef clause * Create a typ_template_t struct and add it to the specified module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'typedef' keyword * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * que == queue will get the typ_template_t * * RETURNS: * status of the operation *********************************************************************/ status_t yang_typ_consume_typedef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que) { typ_template_t *typ, *testtyp; const xmlChar *val; const char *expstr; yang_stmt_t *stmt; tk_type_t tktyp; boolean done, typdone, typeok, unit, def, stat, desc, ref; status_t res, retres; typ = NULL; testtyp = NULL; val = NULL; expstr = "identifier string"; tktyp = TK_TT_NONE; done = FALSE; typdone = FALSE; typeok = FALSE; unit = FALSE; def = FALSE; stat = FALSE; desc = FALSE; ref = FALSE; res = NO_ERR; retres = NO_ERR; /* Get a new typ_template_t to fill in */ typ = typ_new_template(); if (!typ) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } else { ncx_set_error(&typ->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); } /* Get the mandatory type name */ res = yang_consume_id_string(tkc, mod, &typ->name); if ( ( NO_ERR != res && res < ERR_LAST_SYS_ERR ) || res == ERR_NCX_EOF ) { typ_free_template(typ); return res; } /* Get the starting left brace for the sub-clauses */ res = ncx_consume_token(tkc, mod, TK_TT_LBRACE); if ( ( NO_ERR != res && res < ERR_LAST_SYS_ERR ) || res == ERR_NCX_EOF ) { typ_free_template(typ); return res; } /* get the prefix clause and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); typ_free_template(typ); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); typ_free_template(typ); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead * Note that typ appinfo is saved in typ.type appinfo * so leaf processing code can access it in the typdef * instead of needed a back pointer access to the * typ_template_t */ res = ncx_consume_appinfo(tkc, mod, &typ->typdef.appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { typ_free_template(typ); return res; } } continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: done = TRUE; continue; default: expstr = "keyword"; res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, res, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_TYPE)) { if (typdone) { retres = ERR_NCX_ENTRY_EXISTS; typeok = FALSE; log_error("\nError: type-stmt already entered"); ncx_print_errormsg(tkc, mod, res); typ_clean_typdef(&typ->typdef); } typdone = TRUE; res = yang_typ_consume_type(pcb, tkc, mod, &typ->typdef); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { typ_free_template(typ); return res; } } else { typeok = TRUE; } } else if (!xml_strcmp(val, YANG_K_UNITS)) { res = yang_consume_strclause(tkc, mod, &typ->units, &unit, &typ->typdef.appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { typ_free_template(typ); return res; } } } else if (!xml_strcmp(val, YANG_K_DEFAULT)) { res = yang_consume_strclause(tkc, mod, &typ->defval, &def, &typ->typdef.appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { typ_free_template(typ); return res; } } } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &typ->status, &stat, &typ->typdef.appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { typ_free_template(typ); return res; } } } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &typ->descr, &desc, &typ->typdef.appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { typ_free_template(typ); return res; } } } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &typ->ref, &ref, &typ->typdef.appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { typ_free_template(typ); return res; } } } else { expstr = "keyword"; retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); } } /* check all the mandatory clauses are present */ if (!typdone) { retres = ERR_NCX_DATA_MISSING; ncx_mod_missing_err(tkc, mod, "typedef", "type"); } /* check if the type name is already used in this module */ if (typ->name && ncx_valid_name2(typ->name)) { testtyp = ncx_find_type_que(que, typ->name); if (testtyp == NULL) { testtyp = ncx_find_type(mod, typ->name, TRUE); } if (testtyp) { /* fatal error for duplicate type w/ same name */ retres = ERR_NCX_DUP_ENTRY; log_error("\nError: type '%s' is already defined in '%s' " "on line %u", testtyp->name, testtyp->tkerr.mod->name, testtyp->tkerr.linenum); tkc->curerr = &typ->tkerr; ncx_print_errormsg(tkc, mod, retres); typ_free_template(typ); } else if (typeok) { #ifdef YANG_TYP_DEBUG if (LOGDEBUG4) { log_debug4("\nyang_typ: adding type (%s) to mod (%s)", typ->name, mod->name); } #endif dlq_enque(typ, que); if (mod->stmtmode && que==&mod->typeQ) { /* save stmt for top-level typedefs only */ stmt = yang_new_typ_stmt(typ); if (stmt) { dlq_enque(stmt, &mod->stmtQ); } else { log_error("\nError: malloc failure for typ_stmt"); retres = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, retres); } } } else { typ_free_template(typ); } } else { typ_free_template(typ); } return retres; } /* yang_typ_consume_typedef */ /******************************************************************** * FUNCTION yang_typ_resolve_typedefs * * Analyze the entire typeQ within the module struct * Finish all the named types and range clauses, * which were left unfinished due to possible forward references * * Check all the types in the Q * If a type has validation errors, it will be removed * from the typeQ * * Algorithm for checking named types in 4 separate loops: * 1) resolve all open type name references * 2) check for any name loops in all named types * 3) check that all base types and builtin types * in each type chain are correct. Also check that * all restrictions given are correct for that type * 4) Check all range clauses and resolve all min/max * keyword uses to decimal numbers and validate that * each range is well-formed. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typeQ == Q of typ_template_t structs t0o check * parent == obj_template containing this typeQ * == NULL if this is a module-level typeQ * * RETURNS: * status of the operation *********************************************************************/ status_t yang_typ_resolve_typedefs (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *typeQ, obj_template_t *parent) { typ_template_t *typ; status_t res, retres; #ifdef DEBUG if (!pcb || !tkc || !mod || !typeQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif retres = NO_ERR; /* first resolve all the local type names */ for (typ = (typ_template_t *)dlq_firstEntry(typeQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { res = resolve_typedef(pcb, tkc, mod, typ, parent, NULL); CHK_EXIT(res, retres); } return retres; } /* yang_typ_resolve_typedefs */ /******************************************************************** * FUNCTION yang_typ_resolve_typedefs_final * * Analyze the entire typeQ within the module struct * Finish all default value checking that was not done before * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typeQ == Q of typ_template_t structs t0o check * parent == obj_template containing this typeQ * == NULL if this is a module-level typeQ * * RETURNS: * status of the operation *********************************************************************/ status_t yang_typ_resolve_typedefs_final (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *typeQ) { typ_template_t *typ; status_t res, retres; #ifdef DEBUG if (!tkc || !mod || !typeQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif retres = NO_ERR; /* first resolve all the local type names */ for (typ = (typ_template_t *)dlq_firstEntry(typeQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { res = resolve_typedef_final(tkc, mod, typ); CHK_EXIT(res, retres); } return retres; } /* yang_typ_resolve_typedefs_final */ /******************************************************************** * FUNCTION yang_typ_resolve_typedefs_grp * * Analyze the entire typeQ within the module struct * Finish all the named types and range clauses, * which were left unfinished due to possible forward references * * Check all the types in the Q * If a type has validation errors, it will be removed * from the typeQ * * Algorithm for checking named types in 4 separate loops: * 1) resolve all open type name references * 2) check for any name loops in all named types * 3) check that all base types and builtin types * in each type chain are correct. Also check that * all restrictions given are correct for that type * 4) Check all range clauses and resolve all min/max * keyword uses to decimal numbers and validate that * each range is well-formed. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typeQ == Q of typ_template_t structs t0o check * parent == obj_template containing this typeQ * == NULL if this is a module-level typeQ * grp == grp_template containing this typedef * * RETURNS: * status of the operation *********************************************************************/ status_t yang_typ_resolve_typedefs_grp (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *typeQ, obj_template_t *parent, grp_template_t *grp) { typ_template_t *typ; status_t res, retres; #ifdef DEBUG if (!pcb || !tkc || !mod || !typeQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif retres = NO_ERR; /* first resolve all the local type names */ for (typ = (typ_template_t *)dlq_firstEntry(typeQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { res = resolve_typedef(pcb, tkc, mod, typ, parent, grp); CHK_EXIT(res, retres); } return retres; } /* yang_typ_resolve_typedefs_grp */ /******************************************************************** * FUNCTION yang_typ_resolve_type * * Analyze the typdef within a single leaf or leaf-list statement * Finish if a named type, and/or range clauses present, * which were left unfinished due to possible forward references * * Algorithm for checking named types in 4 separate loops: * 1) resolve all open type name references * 2) check for any name loops in all named types * 3) check that all base types and builtin types * in each type chain are correct. Also check that * all restrictions given are correct for that type * 4) Check all range clauses and resolve all min/max * keyword uses to decimal numbers and validate that * each range is well-formed. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typdef == typdef struct from leaf or leaf-list to check * defval == default value string for this leaf (may be NULL) * obj == obj_template containing this typdef * == NULL if this is a top-level union typedef, * checking its nested unnamed type clauses * RETURNS: * status of the operation *********************************************************************/ status_t yang_typ_resolve_type (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, const xmlChar *defval, obj_template_t *obj) { status_t res; #ifdef DEBUG if (!pcb || !tkc || !mod || !typdef) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = resolve_type(pcb, tkc, mod, typdef, NULL, defval, obj, NULL); return res; } /* yang_typ_resolve_type */ /******************************************************************** * FUNCTION yang_typ_resolve_type_final * * Check deferred XPath resolution tests * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * typdef == typdef struct from leaf or leaf-list to check * defval == default value string for this typedef or leaf * obj == obj_template containing this typdef * == NULL if this is a top-level union typedef, * checking its nested unnamed type clauses * RETURNS: * status of the operation *********************************************************************/ status_t yang_typ_resolve_type_final (tk_chain_t *tkc, ncx_module_t *mod, typ_def_t *typdef, const xmlChar *defval, obj_template_t *obj) { status_t res; #ifdef DEBUG if (!tkc || !mod || !typdef) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = resolve_type_final(tkc, mod, obj, typdef, defval); return res; } /* yang_typ_resolve_type_final */ /******************************************************************** * FUNCTION yang_typ_rangenum_ok * * Check a typdef for range definitions * and check if the specified number passes all * the range checks (if any) * * INPUTS: * typdef == typdef to check * num == number to check * * RETURNS: * status of the operation *********************************************************************/ status_t yang_typ_rangenum_ok (typ_def_t *typdef, const ncx_num_t *num) { typ_def_t *rdef; status_t res; ncx_btype_t rbtyp; res = NO_ERR; rbtyp = typ_get_range_type(typ_get_basetype(typdef)); rdef = typ_get_qual_typdef(typdef, NCX_SQUAL_RANGE); while (rdef && res==NO_ERR) { res = val_range_ok(rdef, rbtyp, num); if (res != NO_ERR) { continue; } rdef = typ_get_parent_typdef(rdef); if (rdef) { rdef = typ_get_qual_typdef(rdef, NCX_SQUAL_RANGE); } } return res; } /* yang_typ_rangenum_ok */ /* END file yang_typ.c */ yuma123_2.14/netconf/src/ncx/def_reg.h0000664000175000017500000001266414770023131017713 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_def_reg #define _H_def_reg /* FILE: def_reg.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Definition Registry module Provides fast tiered lookup for data structures used to process NCX messages. The data structures 'pointed to' by these registry entries are not managed in this module. Deleting a registry entry will not delete the 'pointed to' data structure. Entry types NS: Namespace to Module Lookup Key: namespace URI Data: module name and back pointer FD: File Desscriptor ID to Session Control Block Ptr Key: File Descriptor Index Data: Session Ptr attached to that FD ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 14-oct-05 abb Begun 11-nov-05 abb re-design to add OWNER as top-level instead of APP 11-feb-06 abb remove application layer; too complicated 12-jul-07 abb changed owner-based definitions to module-based definitions throughout all code Change OWNER to MODULE 19-feb-09 abb Give up on module/mod_def/cfg_def because too complicated once import-by-revision added Too much memory usage as well. */ #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION def_reg_init * * Initialize the def_reg module * * RETURNS: * none *********************************************************************/ extern void def_reg_init (void); /******************************************************************** * FUNCTION def_reg_cleanup * * Cleanup all the malloced memory in this module * and return the module to an uninitialized state * After this fn, the def_reg_init fn could be called again * * INPUTS: * none * RETURNS: * none *********************************************************************/ extern void def_reg_cleanup (void); /*********************** NS ***************************/ /******************************************************************** * FUNCTION def_reg_add_ns * * add one xmlns_t to the registry * * INPUTS: * ns == namespace record to add * RETURNS: * status of the operation *********************************************************************/ extern status_t def_reg_add_ns (xmlns_t *ns); /******************************************************************** * FUNCTION def_reg_find_ns * * find one xmlns_t in the registry * find a xmlns_t by its value (name) * * INPUTS: * nsname == namespace ID to find * RETURNS: * pointer to xmlns_t or NULL if not found *********************************************************************/ extern xmlns_t * def_reg_find_ns (const xmlChar *nsname); /******************************************************************** * FUNCTION def_reg_del_ns * * unregister a xmlns_t * delete one ncx_module from the registry * * INPUTS: * nsname == namespace name to delete * RETURNS: * none *********************************************************************/ extern void def_reg_del_ns (const xmlChar *nsname); /*********************** SCB ***************************/ /******************************************************************** * FUNCTION def_reg_add_scb * * add one FD to SCB mapping to the registry * * INPUTS: * fd == file descriptor to add * session == ses_cb_t for the session * RETURNS: * status of the operation *********************************************************************/ extern status_t def_reg_add_scb (int fd, ses_cb_t *scb); /******************************************************************** * FUNCTION def_reg_find_scb * * find one FD-to-SCB mapping in the registry * * INPUTS: * fd == file descriptor ID to find * RETURNS: * pointer to ses_cb_t or NULL if not found *********************************************************************/ extern ses_cb_t * def_reg_find_scb (int fd); /******************************************************************** * FUNCTION def_reg_del_scb * * delete one FD to SCB mapping from the registry * * INPUTS: * fd == file descriptor index to delete * RETURNS: * none *********************************************************************/ extern void def_reg_del_scb (int fd); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_def_reg */ yuma123_2.14/netconf/src/ncx/yinyang.h0000664000175000017500000000443614770023131017774 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yinyang #define _H_yinyang /* FILE: yinyang.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Convert YIN format to YANG format for input ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 12-dec-09 abb Begun; */ #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION yinyang_convert_token_chain * * Try to open a file as a YIN formatted XML file * and convert it to a token chain for processing * by yang_parse_from_token_chain * * INPUTS: * sourcespec == file spec for the YIN file to read * res == address of return status * * OUTPUTS: * *res == status of the operation * * RETURNS: * malloced token chain containing the converted YIN file *********************************************************************/ extern tk_chain_t * yinyang_convert_token_chain (const xmlChar *sourcespec, status_t *res); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yinyang */ yuma123_2.14/netconf/src/ncx/ses_msg.c0000664000175000017500000006415314770023131017753 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: ses_msg.c NETCONF Session Message Manager: Common Support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 06jun06 abb begun; 29apr11 abb add support for NETCONF:base:1.1 message framing ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include "procdefs.h" #include "log.h" #include "send_buff.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "tstamp.h" #include "val.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* clear buffers before use */ /* #define SES_MSG_CLEAR_INIT_BUFFERS 1 */ /* #define SES_MSG_DEBUG_CACHE 1 */ /* max number of buffers a session is allowed to cache in its freeQ */ #define MAX_FREE_MSGS 32 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean ses_msg_init_done = FALSE; static uint32 freecnt; static dlq_hdr_t freeQ; static dlq_hdr_t inreadyQ; static dlq_hdr_t outreadyQ; /******************************************************************** * FUNCTION trace_buff * * Log the specified buffer for debugging * * INPUTS: * scb == session control block to use * buff == buffer to send * *********************************************************************/ static void trace_buff (ses_msg_buff_t *buff) { size_t pos; xmlChar buf[2]; if (LOGDEBUG) { buf[1] = 0; for (pos = buff->buffpos; pos < buff->bufflen; pos++) { buf[0] = buff->buff[pos]; log_debug2("%s", buf); } } } /* trace_buff */ /******************************************************************** * FUNCTION do_send_buff * * Send the specified buffer. * Add framing chars if needed for base:1.1 over SSH * * INPUTS: * scb == session control block to use * buff == buffer to send * * RETURNS: * status *********************************************************************/ static status_t do_send_buff (ses_cb_t *scb, ses_msg_buff_t *buff) { status_t res = NO_ERR; if (scb->framing11) { ses_msg_add_framing(scb, buff); /* bufflen has been adjusted for buffstart */ if (LOGDEBUG2) { log_debug2("\nses_msg send 1.1 buff:%u\n", buff->bufflen - buff->buffpos); if (LOGDEBUG3) { trace_buff(buff); } } if (buff->bufflen > 0) { res = send_buff(scb->fd, (const char *)&buff->buff[buff->buffstart], buff->bufflen); } else if (LOGDEBUG2) { log_debug2("\nses: skip sending empty 1.1 buffer on sesion '%d'", scb->sid); } } else { if (buff->bufflen > buff->buffstart) { /* send with base:1.0 framing; EOM markers in the buffer */ if (LOGDEBUG2) { log_debug("\nses_msg send 1.0 buff:%u\n", buff->bufflen - buff->buffpos); if (LOGDEBUG3) { trace_buff(buff); } } res = send_buff(scb->fd, (const char *)buff->buff, buff->bufflen); } else if (LOGDEBUG2) { log_debug2("\nses: skip sending empty 1.0 buffer on sesion '%d'", scb->sid); } } return res; } /* do_send_buff */ /******************************************************************** * FUNCTION ses_msg_init * * Initialize the session message manager module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ void ses_msg_init (void) { if (!ses_msg_init_done) { freecnt = 0; dlq_createSQue(&freeQ); dlq_createSQue(&inreadyQ); dlq_createSQue(&outreadyQ); ses_msg_init_done = TRUE; } } /* ses_msg_init */ /******************************************************************** * FUNCTION ses_msg_cleanup * * Cleanup the session message manager module data structures * * INPUTS: * none * RETURNS: * none *********************************************************************/ void ses_msg_cleanup (void) { ses_msg_t *msg; if (ses_msg_init_done) { while (!dlq_empty(&freeQ)) { msg = (ses_msg_t *)dlq_deque(&freeQ); /* these do not belong to any session and do not have * any buffers, so just toss the memory instead of * using ses_msg_free_msg */ m__free(msg); } /* nothing malloced in these Qs now */ memset(&freeQ, 0x0, sizeof(dlq_hdr_t)); memset(&inreadyQ, 0x0, sizeof(dlq_hdr_t)); memset(&outreadyQ, 0x0, sizeof(dlq_hdr_t)); freecnt = 0; ses_msg_init_done = FALSE; } } /* ses_msg_cleanup */ /******************************************************************** * FUNCTION ses_msg_new_msg * * Malloc a new session message control header * * INPUTS: * msg == address of ses_msg_t pointer that will be set * * OUTPUTS: * *msg == malloced session message struct (if NO_ERR return) * * RETURNS: * status *********************************************************************/ status_t ses_msg_new_msg (ses_msg_t **msg) { ses_msg_t *newmsg; assert( msg && "msg == NULL" ); /* try the freeQ first */ newmsg = (ses_msg_t *)dlq_deque(&freeQ); if (newmsg) { freecnt--; } else { /* freeQ is empty, malloc a msg */ newmsg = m__getObj(ses_msg_t); if (!newmsg) { return ERR_INTERNAL_MEM; } } /* set the fields and exit */ memset(newmsg, 0x0, sizeof(ses_msg_t)); dlq_createSQue(&newmsg->buffQ); *msg = newmsg; return NO_ERR; } /* ses_msg_new_msg */ /******************************************************************** * FUNCTION ses_msg_free_msg * * Free the session message and all its buffer chunks * * INPUTS: * scb == session control block owning the message * msg == message to free (already removed from any Q) * *********************************************************************/ void ses_msg_free_msg (ses_cb_t *scb, ses_msg_t *msg) { ses_msg_buff_t *buff; assert( scb && "scb == NULL" ); assert( msg && "msg == NULL" ); while (!dlq_empty(&msg->buffQ)) { buff = dlq_deque(&msg->buffQ); ses_msg_free_buff(scb, buff); } if (freecnt < MAX_FREE_MSGS) { dlq_enque(msg, &freeQ); freecnt++; } else { m__free(msg); } } /* ses_msg_free_msg */ /******************************************************************** * FUNCTION ses_msg_new_buff * * Malloc a new session buffer chuck * * Note that the buffer memory is not cleared after each use * since this is not needed for byte stream IO * * INPUTS: * scb == session control block to malloc a new message for * outbuff == TRUE if this is for outgoing message * FALSE if this is for incoming message * buff == address of ses_msg_buff_t pointer that will be set * * OUTPUTS: * *buff == malloced session buffer chunk (if NO_ERR return) * * RETURNS: * status *********************************************************************/ status_t ses_msg_new_buff( ses_cb_t *scb, boolean outbuff, ses_msg_buff_t **buff) { ses_msg_buff_t *newbuff; assert( scb && "scb == NULL" ); assert( buff && "buff == NULL" ); /* handle the session freeQ separately */ if (scb->freecnt) { newbuff = (ses_msg_buff_t *)dlq_deque(&scb->freeQ); if (newbuff) { /* use buffer from freeQ */ ses_msg_init_buff(scb, outbuff, newbuff); #ifdef SES_MSG_CLEAR_INIT_BUFFERS memset(newbuff->buff, 0x0, SES_MSG_BUFFSIZE); #endif *buff = newbuff; scb->freecnt--; if (LOGDEBUG4) { log_debug4("\nses_msg: reused %s buff %p for s %u", (outbuff) ? "out" : "in", newbuff, scb->sid); } return NO_ERR; } else { SET_ERROR(ERR_INTERNAL_VAL); scb->freecnt = 0; } } /* check buffers exceeded error */ if (scb->buffcnt+1 >= SES_MAX_BUFFERS) { return ERR_NCX_RESOURCE_DENIED; } /* malloc the buffer */ newbuff = m__getObj(ses_msg_buff_t); if (newbuff == NULL) { return ERR_INTERNAL_MEM; } /* set the fields and exit */ ses_msg_init_buff(scb, outbuff, newbuff); #ifdef DEBUG memset(newbuff->buff, 0x0, SES_MSG_BUFFSIZE); #endif *buff = newbuff; scb->buffcnt++; if (LOGDEBUG4) { log_debug4("\nses_msg: new %s buff %p for s %u", (outbuff) ? "out" : "in", newbuff, scb->sid); } return NO_ERR; } /* ses_msg_new_buff */ /******************************************************************** * FUNCTION ses_msg_free_buff * * Free the session buffer chunk * * INPUTS: * scb == session control block owning the message * buff == buffer to free (already removed from any Q) * * RETURNS: * none *********************************************************************/ void ses_msg_free_buff (ses_cb_t *scb, ses_msg_buff_t *buff) { assert( scb && "scb == NULL" ); if (scb->state < SES_ST_SHUTDOWN_REQ && scb->freecnt < SES_MAX_FREE_BUFFERS) { dlq_enque(buff, &scb->freeQ); scb->freecnt++; #ifdef SES_MSG_DEBUG_CACHE if (LOGDEBUG4) { log_debug4("\nses_msg: cache buff %p for s %u", buff, scb->sid); } #endif } else { #ifdef SES_MSG_DEBUG_CACHE if (LOGDEBUG4) { log_debug4("\nses_msg: free buff %p for s %u", buff, scb->sid); } #endif m__free(buff); scb->buffcnt--; } } /* ses_msg_free_buff */ /******************************************************************** * FUNCTION ses_msg_write_buff * * Add some text to the message buffer * * Upper layer code should never write framing chars to the * output buff -- that is always done in this module. * Use ses_finish_msg to cause framing chars to be written/ * * INPUTS: * scb == session control block to use * buff == buffer to write to * ch == xmlChar to write * * RETURNS: * status_t * *********************************************************************/ status_t ses_msg_write_buff (ses_cb_t *scb, ses_msg_buff_t *buff, uint32 ch) { status_t res; assert( scb && "scb == NULL" ); assert( buff && "buff == NULL" ); res = NO_ERR; if (scb->framing11) { if (buff->bufflen < (SES_MSG_BUFFSIZE - SES_ENDCHUNK_PAD)) { buff->buff[buff->bufflen++] = (xmlChar)ch; } else { res = ERR_BUFF_OVFL; } } else { if (buff->bufflen < SES_MSG_BUFFSIZE) { buff->buff[buff->bufflen++] = (xmlChar)ch; } else { res = ERR_BUFF_OVFL; } } return res; } /* ses_msg_write_buff */ /******************************************************************** * FUNCTION ses_msg_send_buffs * * Send multiple buffers to the session client socket * Tries to send one packet at maximum MTU * * INPUTS: * scb == session control block * * RETURNS: * status *********************************************************************/ status_t ses_msg_send_buffs (ses_cb_t *scb) { ses_msg_buff_t *buff; uint32 buffleft, total; ssize_t retcnt; int i, cnt; boolean done; status_t res; struct iovec iovs[SES_MAX_BUFFSEND]; assert( scb && "scb == NULL" ); if (LOGDEBUG) { log_debug("\nses got send request on session %d", scb->sid); } /* log message trace here only for base 1.0 or mgr */ if (LOGDEBUG2 && (scb->wrfn != NULL || scb->framing11 == FALSE)) { buff = (ses_msg_buff_t *)dlq_firstEntry(&scb->outQ); if (buff) { if (LOGDEBUG3) { log_debug3("\nses_msg_send full msg:\n"); trace_buff(buff); buff = (ses_msg_buff_t *)dlq_nextEntry(buff); while (buff != NULL) { trace_buff(buff); buff = (ses_msg_buff_t *)dlq_nextEntry(buff); } } else { log_debug2("\nses_msg_send first buffer:\n"); trace_buff(buff); } } } /* check if an external write function is used */ if (scb->wrfn) { return (*scb->wrfn)(scb); } memset(iovs, 0x0, sizeof(iovs)); total = 0; cnt = 0; done = FALSE; buff = (ses_msg_buff_t *)dlq_firstEntry(&scb->outQ); /* setup the writev call */ for (i=0; ibufflen - buff->buffpos; if ((total+buffleft) > SES_MAX_BYTESEND) { done = TRUE; } else { total += buffleft; iovs[i].iov_base = &buff->buff[buff->buffpos]; iovs[i].iov_len = buffleft; buff = (ses_msg_buff_t *)dlq_nextEntry(buff); #ifdef SES_MSG_FULL_TRACE if (LOGDEBUG3) { log_debug3("\nses_msg: setup send buff %d\n%s\n", i, iovs[i].iov_base); } #endif cnt++; } } /* make sure there is at least one buffer set */ if (iovs[0].iov_base == NULL) { return SET_ERROR(ERR_NCX_OPERATION_FAILED); } if (scb->framing11) { /* send the 'cnt' number of buffs identified above * do not use the iovs array because this function * may not write the entire amount requested * and that would not match the huge chunksize; * so just send each buffer as a chunk */ for (i=0; i < cnt; i++) { buff = (ses_msg_buff_t *)dlq_deque(&scb->outQ); if (buff == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } res = do_send_buff(scb, buff); ses_msg_free_buff(scb, buff); if (res != NO_ERR) { return res; } } return NO_ERR; } /* else base:1.0 framing * write a packet to the session socket */ retcnt = writev(scb->fd, iovs, cnt); if (retcnt < 0) { /* should not need retries because the select loop * indicated this session was ready for output */ log_info("\nses msg write failed for session %d", scb->sid); return errno_to_status(); } else { if (LOGDEBUG2) { log_debug2("\nses wrote %d of %d bytes on session %d\n", retcnt, total, scb->sid); } } /* clean up the buffers that were written */ buff = (ses_msg_buff_t *)dlq_firstEntry(&scb->outQ); while (retcnt && buff) { /* get the number of bytes written from this buffer */ buffleft = buff->bufflen - buff->buffpos; /* free the buffer if all of it was written or just * bump the buffer pointer if not */ if ((uint32)retcnt >= buffleft) { dlq_remove(buff); ses_msg_free_buff(scb, buff); retcnt -= (ssize_t)buffleft; buff = (ses_msg_buff_t *)dlq_firstEntry(&scb->outQ); } else { buff->buffpos += (uint32)retcnt; retcnt = 0; } } return NO_ERR; } /* ses_msg_send_buffs */ /******************************************************************** * FUNCTION ses_msg_new_output_buff * * Put the current outbuff on the outQ * Put the session on the outreadyQ if it is not already there * Try to allocate a new buffer for the session * * INPUTS: * scb == session control block * * OUTPUTS: * scb->outbuff, scb->outready, and scb->outQ will be changed * !!! buffer will be sent if stream output mode, then buffer reused * * RETURNS: * status, could return malloc or buffers exceeded error *********************************************************************/ status_t ses_msg_new_output_buff (ses_cb_t *scb) { ses_msg_buff_t *buff; status_t res; assert( scb && "scb == NULL" ); buff = scb->outbuff; buff->buffpos = 0; if (scb->stream_output) { /* send this buffer right now * this works because the agt_ncxserver loop and mgr_io * loop are single threaded and a notification cannot * be in the middle of being sent right now * If that code is changed, then make sure a notification * is not being streamed right now */ if (buff->bufflen) { res = do_send_buff(scb, buff); ses_msg_init_buff(scb, TRUE, buff); } else { res = SET_ERROR(ERR_INTERNAL_VAL); } /* reuse the same outbuff again */ } else { /* save the buffer in the message loop do be sent when * the main loop checks if any output pending */ dlq_enque(scb->outbuff, &scb->outQ); ses_msg_make_outready(scb); scb->outbuff = NULL; res = ses_msg_new_buff(scb, TRUE, &scb->outbuff); } return res; } /* ses_msg_new_output_buff */ /******************************************************************** * FUNCTION ses_msg_make_inready * * Put the session on the inreadyQ if it is not already there * * INPUTS: * scb == session control block * * OUTPUTS: * scb->inready will be queued on the inreadyQ *********************************************************************/ void ses_msg_make_inready (ses_cb_t *scb) { assert( scb && "scb is NULL" ); if (!scb->inready.inq) { dlq_enque(&scb->inready, &inreadyQ); scb->inready.inq = TRUE; } } /* ses_msg_make_inready */ void ses_msg_unmake_inready (ses_cb_t *scb) { assert( scb && "scb is NULL" ); if (scb->inready.inq) { dlq_deque(&scb->inready.hdr); scb->inready.inq = FALSE; } } /******************************************************************** * FUNCTION ses_msg_make_outready * * Put the session on the outreadyQ if it is not already there * * INPUTS: * scb == session control block * * OUTPUTS: * scb->outready will be queued on the outreadyQ *********************************************************************/ void ses_msg_make_outready (ses_cb_t *scb) { assert( scb && "scb is NULL" ); if (!scb->outready.inq) { dlq_enque(&scb->outready, &outreadyQ); scb->outready.inq = TRUE; } } /* ses_msg_make_outready */ void ses_msg_unmake_outready (ses_cb_t *scb) { assert( scb && "scb is NULL" ); if (scb->outready.inq) { dlq_deque(&scb->outready.hdr); scb->outready.inq = FALSE; } } /******************************************************************** * FUNCTION ses_msg_finish_outmsg * * Put the outbuff in the outQ if non-empty * Put the session on the outreadyQ if it is not already there * * INPUTS: * scb == session control block * * OUTPUTS: * scb->outready will be queued on the outreadyQ *********************************************************************/ void ses_msg_finish_outmsg (ses_cb_t *scb) { status_t res; assert( scb && "scb is NULL" ); assert( scb->outbuff && "scb->outbuff is NULL" ); if (scb->stream_output) { res = do_send_buff(scb, scb->outbuff); ses_msg_init_buff(scb, TRUE, scb->outbuff); if (res != NO_ERR) { log_error("\nError: IO failed on session '%d' (%s)", scb->sid, get_error_string(res)); } } else { scb->outbuff->buffpos = scb->outbuff->buffstart; dlq_enque(scb->outbuff, &scb->outQ); scb->outbuff = NULL; (void)ses_msg_new_buff(scb, TRUE, &scb->outbuff); ses_msg_make_outready(scb); } } /* ses_msg_finish_outmsg */ /******************************************************************** * FUNCTION ses_msg_get_first_inready * * Dequeue the first entry in the inreadyQ, if any * * RETURNS: * first entry in the inreadyQ or NULL if none *********************************************************************/ ses_ready_t * ses_msg_get_first_inready (void) { ses_ready_t *rdy; rdy = (ses_ready_t *)dlq_deque(&inreadyQ); if (rdy) { rdy->inq = FALSE; } return rdy; } /* ses_msg_get_first_inready */ /******************************************************************** * FUNCTION ses_msg_get_first_outready * * Dequeue the first entry in the outreadyQ, if any * * RETURNS: * first entry in the outreadyQ or NULL if none *********************************************************************/ ses_ready_t * ses_msg_get_first_outready (void) { ses_ready_t *rdy; rdy = (ses_ready_t *)dlq_deque(&outreadyQ); if (rdy) { rdy->inq = FALSE; } return rdy; } /* ses_msg_get_first_outready */ /******************************************************************** * FUNCTION ses_msg_dump * * Dump the message contents * * INPUTS: * msg == message to dump * text == start text before message dump (may be NULL) * *********************************************************************/ void ses_msg_dump (const ses_msg_t *msg, const xmlChar *text) { const ses_msg_buff_t *buff; boolean anytext; uint32 i; assert( msg && "msg is NULL" ); if (text) { log_write("\n%s\n", text); anytext = TRUE; } else { anytext = FALSE; } for (buff = (const ses_msg_buff_t *)dlq_firstEntry(&msg->buffQ); buff != NULL; buff = (const ses_msg_buff_t *)dlq_nextEntry(buff)) { for (i = buff->buffstart; i < buff->bufflen; i++) { log_write("%c", buff->buff[i]); } anytext = TRUE; } if (anytext) { log_write("\n"); } } /* ses_msg_dump */ /******************************************************************** * FUNCTION ses_msg_add_framing * * Add the base:1.1 framing chars to the buffer and adjust * the buffer size pointers * * INPUTS: * scb == session control block * buff == buffer control block * * OUTPUTS: * framing chars added to buff->buff * *********************************************************************/ void ses_msg_add_framing( ses_cb_t *scb, ses_msg_buff_t *buff ) { assert( scb && "scb == NULL" ); assert( buff && "buff == NULL" ); if (!scb->framing11) { return; } /* get the chunk size */ char numbuff[SES_MAX_CHUNKNUM_SIZE]; size_t buffsize = buff->bufflen - SES_STARTCHUNK_PAD; int32 numlen = snprintf(numbuff, sizeof(numbuff), "%zu", buffsize); /* figure out where to put the start chunks within * the beginning pad area; total size is numlen+3 * \n#numlen\n */ buff->buffstart = SES_STARTCHUNK_PAD - (numlen + 3); char *p = (char *)&buff->buff[buff->buffstart]; *p++ = '\n'; *p++ = '#'; memcpy(p, numbuff, numlen); p += numlen; *p = '\n'; if (buff->islast) { memcpy(&buff->buff[buff->bufflen], NC_SSH_END_CHUNKS, NC_SSH_END_CHUNKS_LEN); buff->bufflen += NC_SSH_END_CHUNKS_LEN; } buff->bufflen -= buff->buffstart; } /* ses_msg_add_framing */ /******************************************************************** * FUNCTION ses_msg_init_buff * * Init the buffer fields * * INPUTS: * scb == session control block * outbuff == TRUE if oupput buffer; FALSE if input buffer * buff == buffer to send *********************************************************************/ void ses_msg_init_buff (ses_cb_t *scb, boolean outbuff, ses_msg_buff_t *buff) { buff->buffpos = 0; buff->islast = FALSE; if (outbuff && scb->framing11) { buff->buffstart = SES_STARTCHUNK_PAD; } else { buff->buffstart = 0; } buff->bufflen = buff->buffstart; /* do not clear mem in buffer buff->buff */ } /* ses_msg_init_buff */ /* END file ses_msg.c */ yuma123_2.14/netconf/src/ncx/status.h0000664000175000017500000005226014770023131017637 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_status #define _H_status /* FILE: status.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* global error status code enumerations ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 03-jan-92 abb move statusT from global.h to here 18-apr-96 abb adapted for gr8cgi project */ #ifdef __cplusplus extern "C" { #endif /********************************************************************* * * * C O N S T A N T S * * * *********************************************************************/ /* * error code definitions--be sure to add any err code * definitions to the copy in err.c to register the errcode name, * the error severity, and the error string */ /* group the error messages by severity */ #define ERR_INT_BASE 0 /* really 2, hold 'NO_ERR' here */ #define ERR_FIRST_INT 2 #define ERR_SYS_BASE 100 #define ERR_USR_BASE 200 #define ERR_WARN_BASE 400 #define ERR_INFO_BASE 900 /* for backward compatability */ #define statusT status_t /* macro SET_ERROR * * call the set_error function with hard-wired parameters * Used to flag internal code errors only!!! * Use log_error for normal errors expected during operation * * INPUTS: * E == status_t : error enumeration * * RETURNS: * E */ #define SET_ERROR(E) set_error(__FILE__, __LINE__, E, 0) /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* error type */ typedef enum errtyp_t_ { ERR_TYP_NONE, ERR_TYP_INTERNAL, ERR_TYP_SYSTEM, ERR_TYP_USER, ERR_TYP_WARN, ERR_TYP_INFO } errtyp_t; /* global error return code */ typedef enum status_t_ { NO_ERR, /* 000 */ ERR_END_OF_FILE, /* 001 */ /* internal errors start at 2 */ ERR_INTERNAL_PTR=ERR_FIRST_INT, /* 002 */ ERR_INTERNAL_MEM, /* 003 */ ERR_INTERNAL_VAL, /* 004 */ ERR_INTERNAL_BUFF, /* 005 */ ERR_INTERNAL_QDEL, /* 006 */ ERR_INTERNAL_INIT_SEQ, /* 007 */ ERR_QNODE_NOT_HDR, /* 008 */ ERR_QNODE_NOT_DATA, /* 009 */ ERR_BAD_QLINK, /* 010 */ ERR_Q_ALREADY, /* 011 */ ERR_TOO_MANY_ENTRIES, /* 012 */ ERR_XML2_FAILED, /* 013 */ ERR_LAST_INT_ERR, /* 014 -- not really used */ /* system errors start at 100 */ ERR_FIL_OPEN=ERR_SYS_BASE, /* 100 */ ERR_FIL_READ, /* 101 */ ERR_FIL_CLOSE, /* 102 */ ERR_FIL_WRITE, /* 103 */ ERR_FIL_CHDIR, /* 104 */ ERR_FIL_STAT, /* 105 */ ERR_BUFF_OVFL, /* 106 */ ERR_FIL_DELETE, /* 107 */ ERR_FIL_SETPOS, /* 108 */ ERR_DB_CONNECT_FAILED, /* 109 */ ERR_DB_ENTRY_EXISTS, /* 110 */ ERR_DB_NOT_FOUND, /* 111 */ ERR_DB_QUERY_FAILED, /* 112 */ ERR_DB_DELETE_FAILED, /* 113 */ ERR_DB_WRONG_CKSUM, /* 114 */ ERR_DB_WRONG_TAGTYPE, /* 115 */ ERR_DB_READ_FAILED, /* 116 */ ERR_DB_WRITE_FAILED, /* 117 */ ERR_DB_INIT_FAILED, /* 118 */ ERR_TR_BEEP_INIT, /* 119 */ ERR_TR_BEEP_NC_INIT, /* 120 */ ERR_XML_READER_INTERNAL, /* 121 */ ERR_OPEN_DIR_FAILED, /* 122 */ ERR_READ_DIR_FAILED, /* 123 */ ERR_LAST_SYS_ERR, /* 124 -- not really used */ /* user errors start at 200 */ ERR_NO_CFGFILE=ERR_USR_BASE, /* 200 */ ERR_NO_SRCFILE, /* 201 */ ERR_PARSPOST_RD_INPUT, /* 202 */ ERR_FIL_BAD_DRIVE, /* 203 */ ERR_FIL_BAD_PATH, /* 204 */ ERR_FIL_BAD_FILENAME, /* 205 */ ERR_DUP_VALPAIR, /* 206 */ ERR_PAGE_NOT_HANDLED, /* 207 */ ERR_PAGE_ACCESS_DENIED, /* 208 */ ERR_MISSING_FORM_PARAMS, /* 209 */ ERR_FORM_STATE, /* 210 */ ERR_DUP_NS, /* 211 */ ERR_XML_READER_START_FAILED, /* 212 */ ERR_XML_READER_READ, /* 213 */ ERR_XML_READER_NODETYP, /* 214 */ ERR_XML_READER_NULLNAME, /* 215 */ ERR_XML_READER_NULLVAL, /* 216 */ ERR_XML_READER_WRONGNAME, /* 217 */ ERR_XML_READER_WRONGVAL, /* 218 */ ERR_XML_READER_WRONGEL, /* 219 */ ERR_XML_READER_EXTRANODES, /* 220 */ ERR_XML_READER_EOF, /* 221 */ ERR_NCX_WRONG_LEN, /* 222 */ ERR_NCX_ENTRY_EXISTS, /* 223 */ ERR_NCX_DUP_ENTRY, /* 224 */ ERR_NCX_NOT_FOUND, /* 225 */ ERR_NCX_MISSING_FILE, /* 226 */ ERR_NCX_UNKNOWN_PARM, /* 227 */ ERR_NCX_INVALID_NAME, /* 228 */ ERR_NCX_UNKNOWN_NS, /* 229 */ ERR_NCX_WRONG_NS, /* 230 */ ERR_NCX_WRONG_TYPE, /* 231 */ ERR_NCX_WRONG_VAL, /* 232 */ ERR_NCX_MISSING_PARM, /* 233 */ ERR_NCX_EXTRA_PARM, /* 234 */ ERR_NCX_EMPTY_VAL, /* 235 */ ERR_NCX_MOD_NOT_FOUND, /* 236 */ ERR_NCX_LEN_EXCEEDED, /* 237 */ ERR_NCX_INVALID_TOKEN, /* 238 */ ERR_NCX_UNENDED_QSTRING, /* 239 */ ERR_NCX_READ_FAILED, /* 240 */ ERR_NCX_INVALID_NUM, /* 241 */ ERR_NCX_INVALID_HEXNUM, /* 242 */ ERR_NCX_INVALID_REALNUM, /* 243 */ ERR_NCX_EOF, /* 244 */ ERR_NCX_WRONG_TKTYPE, /* 245 */ ERR_NCX_WRONG_TKVAL, /* 246 */ ERR_NCX_BUFF_SHORT, /* 247 */ ERR_NCX_INVALID_RANGE, /* 248 */ ERR_NCX_OVERLAP_RANGE, /* 249 */ ERR_NCX_DEF_NOT_FOUND, /* 250 */ ERR_NCX_DEFSEG_NOT_FOUND, /* 251 */ ERR_NCX_TYPE_NOT_INDEX, /* 252 */ ERR_NCX_INDEX_TYPE_NOT_FOUND, /* 253 */ ERR_NCX_TYPE_NOT_MDATA, /* 254 */ ERR_NCX_MDATA_NOT_ALLOWED, /* 255 */ ERR_NCX_TOP_NOT_FOUND, /* 256 */ /* match netconf errors here */ ERR_NCX_IN_USE, /* 257 */ ERR_NCX_INVALID_VALUE, /* 258 */ ERR_NCX_TOO_BIG, /* 259 */ ERR_NCX_MISSING_ATTRIBUTE, /* 260 */ ERR_NCX_BAD_ATTRIBUTE, /* 261 */ ERR_NCX_UNKNOWN_ATTRIBUTE, /* 262 */ ERR_NCX_MISSING_ELEMENT, /* 263 */ ERR_NCX_BAD_ELEMENT, /* 264 */ ERR_NCX_UNKNOWN_ELEMENT, /* 265 */ ERR_NCX_UNKNOWN_NAMESPACE, /* 266 */ ERR_NCX_ACCESS_DENIED, /* 267 */ ERR_NCX_LOCK_DENIED, /* 268 */ ERR_NCX_RESOURCE_DENIED, /* 269 */ ERR_NCX_ROLLBACK_FAILED, /* 270 */ ERR_NCX_DATA_EXISTS, /* 271 */ ERR_NCX_DATA_MISSING, /* 272 */ ERR_NCX_OPERATION_NOT_SUPPORTED, /* 273 */ ERR_NCX_OPERATION_FAILED, /* 274 */ ERR_NCX_PARTIAL_OPERATION, /* 275 */ /* netconf error extensions */ ERR_NCX_WRONG_NAMESPACE, /* 276 */ ERR_NCX_WRONG_NODEDEPTH, /* 277 */ ERR_NCX_WRONG_OWNER, /* 278 */ ERR_NCX_WRONG_ELEMENT, /* 279 */ ERR_NCX_WRONG_ORDER, /* 280 */ ERR_NCX_EXTRA_NODE, /* 281 */ ERR_NCX_WRONG_NODETYP, /* 282 */ ERR_NCX_WRONG_NODETYP_SIM, /* 283 */ ERR_NCX_WRONG_NODETYP_CPX, /* 284 */ ERR_NCX_WRONG_DATATYP, /* 285 */ ERR_NCX_WRONG_DATAVAL, /* 286 */ ERR_NCX_NUMLEN_TOOBIG, /* 287 */ ERR_NCX_NOT_IN_RANGE, /* 288 */ ERR_NCX_WRONG_NUMTYP, /* 289 */ ERR_NCX_EXTRA_ENUMCH, /* 290 */ ERR_NCX_VAL_NOTINSET, /* 291 */ ERR_NCX_EXTRA_LISTSTR, /* 292 */ ERR_NCX_UNKNOWN_OBJECT, /* 293 */ ERR_NCX_EXTRA_PARMINST, /* 294 */ ERR_NCX_EXTRA_CHOICE, /* 295 */ ERR_NCX_MISSING_CHOICE, /* 296 */ /* 13.6 */ ERR_NCX_CFG_STATE, /* 297 */ ERR_NCX_UNKNOWN_APP, /* 298 */ ERR_NCX_UNKNOWN_TYPE, /* 299 */ ERR_NCX_NO_ACCESS_ACL, /* 300 */ ERR_NCX_NO_ACCESS_LOCK, /* 301 */ ERR_NCX_NO_ACCESS_STATE, /* 302 */ ERR_NCX_NO_ACCESS_MAX, /* 303 */ ERR_NCX_WRONG_INDEX_TYPE, /* 304 */ ERR_NCX_WRONG_INSTANCE_TYPE, /* 305 */ ERR_NCX_MISSING_INDEX, /* 306 */ ERR_NCX_CFG_NOT_FOUND, /* 307 */ ERR_NCX_EXTRA_ATTR, /* 308 */ ERR_NCX_MISSING_ATTR, /* 309 */ ERR_NCX_MISSING_VAL_INST, /* 310 */ ERR_NCX_EXTRA_VAL_INST, /* 311 */ ERR_NCX_NOT_WRITABLE, /* 312 */ ERR_NCX_INVALID_PATTERN, /* 313 */ ERR_NCX_WRONG_VERSION, /* 314 */ ERR_NCX_CONNECT_FAILED, /* 315 */ ERR_NCX_UNKNOWN_HOST, /* 316 */ ERR_NCX_SESSION_FAILED, /* 317 */ ERR_NCX_AUTH_FAILED, /* 318 */ ERR_NCX_UNENDED_COMMENT, /* 319 */ ERR_NCX_INVALID_CONCAT, /* 320 */ ERR_NCX_IMP_NOT_FOUND, /* 321 */ ERR_NCX_MISSING_TYPE, /* 322 */ ERR_NCX_RESTRICT_NOT_ALLOWED, /* 323 */ ERR_NCX_REFINE_NOT_ALLOWED, /* 324 */ ERR_NCX_DEF_LOOP, /* 325 */ ERR_NCX_DEFCHOICE_NOT_OPTIONAL, /* 326 */ ERR_NCX_IMPORT_LOOP, /* 327 */ ERR_NCX_INCLUDE_LOOP, /* 328 */ ERR_NCX_EXP_MODULE, /* 329 */ ERR_NCX_EXP_SUBMODULE, /* 330 */ ERR_NCX_PREFIX_NOT_FOUND, /* 331 */ ERR_NCX_IMPORT_ERRORS, /* 332 */ ERR_NCX_PATTERN_FAILED, /* 333 */ ERR_NCX_INVALID_TYPE_CHANGE, /* 334 */ ERR_NCX_MANDATORY_NOT_ALLOWED, /* 335 */ ERR_NCX_UNIQUE_TEST_FAILED, /* 336 */ /* 13.1 */ ERR_NCX_MAX_ELEMS_VIOLATION, /* 337 */ /* 13.2 */ ERR_NCX_MIN_ELEMS_VIOLATION, /* 338 */ /* 13.3 */ ERR_NCX_MUST_TEST_FAILED, /* 339 */ /* 13.4 */ ERR_NCX_DATA_REST_VIOLATION, /* 340 */ /* obsolete */ ERR_NCX_INSERT_MISSING_INSTANCE, /* 341 */ /* 13.7 */ ERR_NCX_NOT_CONFIG, /* 342 */ ERR_NCX_INVALID_CONDITIONAL, /* 343 */ ERR_NCX_USING_OBSOLETE, /* 344 */ ERR_NCX_INVALID_AUGTARGET, /* 345 */ ERR_NCX_DUP_REFINE_STMT, /* 346 */ ERR_NCX_INVALID_DEV_STMT, /* 347 */ ERR_NCX_INVALID_XPATH_EXPR, /* 348 */ ERR_NCX_INVALID_INSTANCEID, /* 349 */ ERR_NCX_MISSING_INSTANCE, /* 350 */ /* 13.n */ ERR_NCX_UNEXPECTED_INSERT_ATTRS, /* 351 */ ERR_NCX_INVALID_UNIQUE_NODE, /* 352 */ ERR_NCX_INVALID_DUP_IMPORT, /* 353 */ ERR_NCX_INVALID_DUP_INCLUDE, /* 354 */ ERR_NCX_AMBIGUOUS_CMD, /* 355 */ ERR_NCX_UNKNOWN_MODULE, /* 356 */ ERR_NCX_UNKNOWN_VERSION, /* 357 */ ERR_NCX_VALUE_NOT_SUPPORTED, /* 358 */ ERR_NCX_LEAFREF_LOOP, /* 359 */ ERR_NCX_VAR_NOT_FOUND, /* 360 */ ERR_NCX_VAR_READ_ONLY, /* 361 */ ERR_NCX_DEC64_BASEOVFL, /* 362 */ ERR_NCX_DEC64_FRACOVFL, /* 363 */ ERR_NCX_RPC_WHEN_FAILED, /* 364 */ ERR_NCX_NO_MATCHES, /* 365 */ ERR_NCX_MISSING_REFTARGET, /* 366 */ ERR_NCX_CANDIDATE_DIRTY, /* 367 */ ERR_NCX_TIMEOUT, /* 368 */ ERR_NCX_GET_SCHEMA_DUPLICATES, /* 369 */ ERR_NCX_XPATH_NOT_NODESET, /* 370 */ ERR_NCX_XPATH_NODESET_EMPTY, /* 371 */ ERR_NCX_IN_USE_LOCKED, /* 372 */ ERR_NCX_IN_USE_COMMIT, /* 373 */ ERR_NCX_SUBMOD_NOT_LOADED, /* 374 */ ERR_NCX_ACCESS_READ_ONLY, /* 375 */ ERR_NCX_CONFIG_NOT_TARGET, /* 376 */ ERR_NCX_MISSING_RBRACE, /* 377 */ ERR_NCX_INVALID_FRAMING, /* 378 */ ERR_NCX_PROTO11_NOT_ENABLED, /* 379 */ ERR_NCX_CC_NOT_ACTIVE, /* 380 */ ERR_NCX_MULTIPLE_MATCHES, /* 381 */ ERR_NCX_NO_DEFAULT, /* 382 */ ERR_NCX_MISSING_KEY, /* 383 */ ERR_NCX_TOP_LEVEL_MANDATORY_FAILED, /* 384 */ ERR_LAST_USR_ERR, /* 385 -- not really used */ /* user warnings start at 400 */ ERR_MAKFILE_DUP_SRC=ERR_WARN_BASE, /* 400 */ ERR_INC_NOT_FOUND, /* 401 */ ERR_CMDLINE_VAL, /* 402 */ ERR_CMDLINE_OPT, /* 403 */ ERR_CMDLINE_OPT_UNKNOWN, /* 404 */ ERR_CMDLINE_SYNTAX, /* 405 */ ERR_CMDLINE_VAL_REQUIRED, /* 406 */ ERR_FORM_INPUT, /* 407 */ ERR_FORM_UNKNOWN, /* 408 */ ERR_NCX_NO_INSTANCE, /* 409 */ ERR_NCX_SESSION_CLOSED, /* 410 */ ERR_NCX_DUP_IMPORT, /* 411 */ ERR_NCX_PREFIX_DUP_IMPORT, /* 412 */ ERR_NCX_TYPDEF_NOT_USED, /* 413 */ ERR_NCX_GRPDEF_NOT_USED, /* 414 */ ERR_NCX_IMPORT_NOT_USED, /* 415 */ ERR_NCX_DUP_UNIQUE_COMP, /* 416 */ ERR_NCX_STMT_IGNORED, /* 417 */ ERR_NCX_DUP_INCLUDE, /* 418 */ ERR_NCX_INCLUDE_NOT_USED, /* 419 */ ERR_NCX_DATE_PAST, /* 420 */ ERR_NCX_DATE_FUTURE, /* 421 */ ERR_NCX_ENUM_VAL_ORDER, /* 422 */ ERR_NCX_BIT_POS_ORDER, /* 423 */ ERR_NCX_INVALID_STATUS, /* 424 */ ERR_NCX_DUP_AUGNODE, /* 425 */ ERR_NCX_DUP_IF_FEATURE, /* 426 */ ERR_NCX_USING_DEPRECATED, /* 427 */ ERR_NCX_MAX_KEY_CHECK, /* 428 */ ERR_NCX_EMPTY_XPATH_RESULT, /* 429 */ ERR_NCX_NO_XPATH_ANCESTOR, /* 430 */ ERR_NCX_NO_XPATH_PARENT, /* 431 */ ERR_NCX_NO_XPATH_CHILD, /* 432 */ ERR_NCX_NO_XPATH_DESCENDANT, /* 433 */ ERR_NCX_NO_XPATH_NODES, /* 434 */ ERR_NCX_BAD_REV_ORDER, /* 435 */ ERR_NCX_DUP_PREFIX, /* 436 */ ERR_NCX_IDLEN_EXCEEDED, /* 437 */ ERR_NCX_LINELEN_EXCEEDED, /* 438 */ ERR_NCX_RCV_UNKNOWN_CAP, /* 439 */ ERR_NCX_RCV_INVALID_MODCAP, /* 440 */ ERR_NCX_USING_ANYXML, /* 441 */ ERR_NCX_USING_BADDATA, /* 442 */ ERR_NCX_USING_STRING, /* 443 */ ERR_NCX_USING_RESERVED_NAME, /* 444 */ ERR_NCX_CONF_PARM_EXISTS, /* 445 */ ERR_NCX_NO_REVISION, /* 446 */ ERR_NCX_DEPENDENCY_ERRORS, /* 447 */ ERR_NCX_TOP_LEVEL_MANDATORY, /* 448 */ ERR_NCX_FILE_MOD_MISMATCH, /* 449 */ ERR_NCX_UNIQUE_CONDITIONAL_MISMATCH, /* 450 */ ERR_NCX_DUP_DATE_IN_REV_HISTORY, /* 451 */ ERR_LAST_WARN, /* 452 -- not really used */ /* system info return codes start at 900 */ ERR_PARS_SECDONE=ERR_INFO_BASE, /* 900 */ ERR_NCX_SKIPPED, /* 901 */ ERR_NCX_CANCELED, /* 902 */ ERR_NCX_LOOP_ENDED, /* 903 */ ERR_NCX_FOUND_INLINE, /* 904 */ ERR_NCX_FOUND_URL, /* 905 */ ERR_LAST_INFO /* 906 -- not really used */ } status_t; /******************************************************************** * FUNCTION set_error * * Generate an error stack entry or if log_error is enabled, * then log the error report right now. * * Used to flag internal code errors only!!! * Use log_error for normal errors expected during operation * * DO NOT USE THIS FUNCTION DIRECTLY! * USE THE SET_ERROR MACRO INSTEAD! * * INPUTS: * filename == C filename that caused the error * linenum -- line number in the C file that caused the error * status == internal error code * sqlError = mySQL error code (deprecated) * * RETURNS * the 'status' parameter will be returned *********************************************************************/ extern status_t set_error (const char *filename, int linenum, status_t status, int sqlError); /******************************************************************** * FUNCTION print_errors * * Dump any entries stored in the error_stack. * *********************************************************************/ extern void print_errors (void); /******************************************************************** * FUNCTION clear_errors * * Clear the error_stack if it has any errors stored in it * *********************************************************************/ extern void clear_errors (void); /******************************************************************** * FUNCTION get_errtyp * * Get the error classification for the result code * * INPUTS: * res == result code * * RETURNS: * error type for 'res' parameter *********************************************************************/ extern errtyp_t get_errtyp (status_t res); /******************************************************************** * FUNCTION get_error_string * * Get the error message for a specific internal error * * INPUTS: * res == internal status_t error code * * RETURNS: * const pointer to error message *********************************************************************/ extern const char * get_error_string (status_t res); /******************************************************************** * FUNCTION errno_to_status * * Get the errno variable and convert it to a status_t * * INPUTS: * none; must be called just after error occurred to prevent * the errno variable from being overwritten by a new operation * * RETURNS: * status_t for the errno enum *********************************************************************/ extern status_t errno_to_status (void); /******************************************************************** * FUNCTION status_init * * Init this module * *********************************************************************/ extern void status_init (void); /******************************************************************** * FUNCTION status_cleanup * * Cleanup this module * *********************************************************************/ extern void status_cleanup (void); /******************************************************************** * FUNCTION print_error_count * * Print the error_count field, if it is non-zero * to STDOUT or the logfile * Clears out the error_count afterwards so * the count will start over after printing!!! * *********************************************************************/ extern void print_error_count (void); /******************************************************************** * FUNCTION print_error_messages * * Print the error number and error message for each error * to STDOUT or the logfile * *********************************************************************/ extern void print_error_messages (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_status */ yuma123_2.14/netconf/src/ncx/xpath_yang.h0000664000175000017500000002776514770023131020472 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xpath_yang #define _H_xpath_yang /* FILE: xpath_yang.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* YANG-specific Xpath support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13-nov-08 abb Begun */ #include #include #include #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xpath_yang_parse_path * * Parse the leafref path as a leafref path * * DOES NOT VALIDATE PATH NODES USED IN THIS PHASE * A 2-pass validation is used in case the path expression * is defined within a grouping. This pass is * used on all objects, even in groupings * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * * INPUTS: * tkc == parent token chain (may be NULL) * mod == module in progress * source == context for this expression * XP_SRC_LEAFREF or XP_SRC_INSTANCEID * pcb == initialized xpath parser control block * for the leafref path; use xpath_new_pcb * to initialize before calling this fn. * The pcb->exprstr field must be set * * OUTPUTS: * pcb->tkc is filled and then validated for well-formed * leafref or instance-identifier syntax * * RETURNS: * status *********************************************************************/ extern status_t xpath_yang_parse_path (tk_chain_t *tkc, ncx_module_t *mod, xpath_source_t source, xpath_pcb_t *pcb); /******************************************************************** * FUNCTION xpath_yang_parse_path_ex * * Parse the leafref path as a leafref path * * DOES NOT VALIDATE PATH NODES USED IN THIS PHASE * A 2-pass validation is used in case the path expression * is defined within a grouping. This pass is * used on all objects, even in groupings * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * * INPUTS: * tkc == parent token chain (may be NULL) * mod == module in progress * obj == context node * source == context for this expression * XP_SRC_LEAFREF or XP_SRC_INSTANCEID * pcb == initialized xpath parser control block * for the leafref path; use xpath_new_pcb * to initialize before calling this fn. * The pcb->exprstr field must be set * logerrors == TRUE to log errors; * == FALSE to suppress error messages * OUTPUTS: * pcb->tkc is filled and then validated for well-formed * leafref or instance-identifier syntax * * RETURNS: * status *********************************************************************/ extern status_t xpath_yang_parse_path_ex (tk_chain_t *tkc, ncx_module_t *mod, xpath_source_t source, xpath_pcb_t *pcb, boolean logerrors); /******************************************************************** * FUNCTION xpath_yang_validate_path * * Validate the previously parsed leafref path * - QNames are valid * - object structure referenced is valid * - objects are all 'config true' * - target object is a leaf * - leafref represents a single instance * * A 2-pass validation is used in case the path expression * is defined within a grouping. This pass is * used only on cooked (real) objects * * Called after all 'uses' and 'augment' expansion * so validation against cooked object tree can be done * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * mod == module containing the 'obj' (in progress) * == NULL if no object in progress * obj == object using the leafref data type * pcb == the leafref parser control block, possibly * cloned from from the typdef * schemainst == TRUE if ncx:schema-instance string * == FALSE to use the pcb->source field * to determine the exact parse mode * leafobj == address of the return target object * * OUTPUTS: * *leafobj == the target leaf found by parsing the path (NO_ERR) * * RETURNS: * status *********************************************************************/ extern status_t xpath_yang_validate_path (ncx_module_t *mod, obj_template_t *obj, xpath_pcb_t *pcb, boolean schemainst, obj_template_t **leafobj); /******************************************************************** * FUNCTION xpath_yang_validate_path_ex * * Validate the previously parsed leafref path * - QNames are valid * - object structure referenced is valid * - objects are all 'config true' * - target object is a leaf * - leafref represents a single instance * * A 2-pass validation is used in case the path expression * is defined within a grouping. This pass is * used only on cooked (real) objects * * Called after all 'uses' and 'augment' expansion * so validation against cooked object tree can be done * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * mod == module containing the 'obj' (in progress) * == NULL if no object in progress * obj == object using the leafref data type * pcb == the leafref parser control block, possibly * cloned from from the typdef * schemainst == TRUE if ncx:schema-instance string * == FALSE to use the pcb->source field * to determine the exact parse mode * leafobj == address of the return target object * logerrors == TRUE to log errors, FALSE to suppress errors * val_parse uses FALSE if basetype == NCX_BT_UNION * * OUTPUTS: * *leafobj == the target leaf found by parsing the path (NO_ERR) * * RETURNS: * status *********************************************************************/ extern status_t xpath_yang_validate_path_ex (ncx_module_t *mod, obj_template_t *obj, xpath_pcb_t *pcb, boolean schemainst, obj_template_t **leafobj, boolean logerrors); /******************************************************************** * FUNCTION xpath_yang_validate_xmlpath * * Validate an instance-identifier expression * within an XML PDU context * * INPUTS: * reader == XML reader to use * pcb == initialized XPath parser control block * with a possibly unchecked pcb->exprstr. * This function will call tk_tokenize_xpath_string * if it has not already been called. * logerrors == TRUE if log_error and ncx_print_errormsg * should be used to log XPath errors and warnings * FALSE if internal error info should be recorded * in the xpath_result_t struct instead * !!! use FALSE unless DEBUG mode !!! * targobj == address of return target object * * OUTPUTS: * *targobj is set to the object that this instance-identifier * references, if NO_ERR * RETURNS: * status *********************************************************************/ extern status_t xpath_yang_validate_xmlpath (xmlTextReaderPtr reader, xpath_pcb_t *pcb, obj_template_t *pathobj, boolean logerrors, obj_template_t **targobj); /******************************************************************** * FUNCTION xpath_yang_validate_xmlkey * * Validate a key XML attribute value given in * an operation with an 'insert' attribute * Check that a complete set of predicates is present * for the specified list of leaf-list * * INPUTS: * reader == XML reader to use * pcb == initialized XPath parser control block * with a possibly unchecked pcb->exprstr. * This function will call tk_tokenize_xpath_string * if it has not already been called. * obj == list or leaf-list object associated with * the pcb->exprstr predicate expression * (MAY be NULL if first-pass parsing and * object is not known yet -- parsed in XML attribute) * logerrors == TRUE if log_error and ncx_print_errormsg * should be used to log XPath errors and warnings * FALSE if internal error info should be recorded * in the xpath_result_t struct instead * !!! use FALSE unless DEBUG mode !!! * * RETURNS: * status *********************************************************************/ extern status_t xpath_yang_validate_xmlkey (xmlTextReaderPtr reader, xpath_pcb_t *pcb, obj_template_t *obj, boolean logerrors); /******************************************************************** * FUNCTION xpath_yang_make_instanceid_val * * Make a value subtree out of an instance-identifier * Used by yangcli to send PDUs from CLI target parameters * * The XPath pcb must be previously parsed and found valid * It must be an instance-identifier value, * not a leafref path * * INPUTS: * pcb == the leafref parser control block, possibly * cloned from from the typdef * retres == address of return status (may be NULL) * deepest == address of return deepest node created (may be NULL) * * OUTPUTS: * if (non-NULL) * *retres == return status * *deepest == pointer to end of instance-id chain node * RETURNS: * malloced value subtree representing the instance-identifier * in internal val_value_t data structures *********************************************************************/ extern val_value_t * xpath_yang_make_instanceid_val (xpath_pcb_t *pcb, status_t *retres, val_value_t **deepest); /******************************************************************** * FUNCTION xpath_yang_get_namespaces * * Get the namespace URI IDs used in the specified * XPath expression; * * usually an instance-identifier or schema-instance node * but this function simply reports all the TK_TT_MSTRING * tokens that have an nsid set * * The XPath pcb must be previously parsed and found valid * * INPUTS: * pcb == the XPath parser control block to use * nsid_array == address of return array of xmlns_id_t * max_nsids == number of NSIDs that can be held * num_nsids == address of return number of NSIDs * written to the buffer. No duplicates * will be present in the buffer * * OUTPUTS: * nsid_array[0..*num_nsids] == returned NSIDs used * in the XPath expression * *num_nsids == number of NSIDs written to the array * * RETURNS: * status *********************************************************************/ extern status_t xpath_yang_get_namespaces (const xpath_pcb_t *pcb, xmlns_id_t *nsid_array, uint32 max_nsids, uint32 *num_nsids); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xpath_yang */ yuma123_2.14/netconf/src/ncx/ncx_str.c0000664000175000017500000001466514770023131017776 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: ncx_num.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17feb10 abb begun; split out from ncx.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_str #include "ncx_str.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_typ #include "typ.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION ncx_compare_strs * * Compare 2 ncx_str_t union contents * * INPUTS: * str1 == first string * str2 == second string * btyp == expected data type * (NCX_BT_STRING, NCX_BT_INSTANCE_ID) * RETURNS: * -1 if str1 is < str2 * 0 if str1 == str2 (also for error, after SET_ERROR called) * 1 if str1 is > str2 *********************************************************************/ int32 ncx_compare_strs (const ncx_str_t *str1, const ncx_str_t *str2, ncx_btype_t btyp) { #ifdef DEBUG if (!str1 || !str2) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif if (!typ_is_string(btyp)) { SET_ERROR(ERR_INTERNAL_VAL); return 0; } return xml_strcmp(*str1, *str2); /*NOTREACHED*/ } /* ncx_compare_strs */ /******************************************************************** * FUNCTION ncx_copy_str * * Copy the contents of str1 to str2 * Supports base types: * NCX_BT_STRING * NCX_BT_INSTANCE_ID * NCX_BT_LEAFREF * * INPUTS: * str1 == first string * str2 == second string * btyp == expected data type * * OUTPUTS: * str2 contains copy of str1 * * RETURNS: * status *********************************************************************/ status_t ncx_copy_str (const ncx_str_t *str1, ncx_str_t *str2, ncx_btype_t btyp) { #ifdef DEBUG if (!str1 || !str2) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (!(typ_is_string(btyp) || btyp==NCX_BT_BITS)) { return ERR_NCX_INVALID_VALUE; } if (*str1) { *str2 = xml_strdup(*str1); if (!*str2) { return ERR_INTERNAL_MEM; } } else { *str2 = NULL; } return NO_ERR; } /* ncx_copy_str */ /******************************************************************** * FUNCTION ncx_clean_str * * Scrub the memory in a ncx_str_t by freeing all * the sub-fields. DOES NOT free the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * str == ncx_str_t data structure to clean *********************************************************************/ void ncx_clean_str (ncx_str_t *str) { #ifdef DEBUG if (!str) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* clean the num->union, depending on base type */ if (*str) { m__free(*str); *str = NULL; } } /* ncx_clean_str */ /******************************************************************** * FUNCTION ncx_copy_c_safe_str * * Copy the string to the buffer, changing legal YANG identifier * chars that cannot be used in C function names to underscore * * !!! DOES NOT CHECK BUFFER OVERRUN !!! * !!! LENGTH OF STRING IS NOT CHANGED WHEN COPIED TO THE BUFFER !!! * * INPUTS: * buffer == buffer to write into * strval == string value to copy * * RETURNS * number of chars copied *********************************************************************/ uint32 ncx_copy_c_safe_str (xmlChar *buffer, const xmlChar *strval) { const xmlChar *s; uint32 count; count = 0; s = strval; while (*s) { if (*s == '.' || *s == '-' || *s == NCXMOD_PSCHAR) { *buffer++ = '_'; } else { *buffer++ = *s; } s++; count++; } *buffer = 0; return count; } /* ncx_copy_c_safe_str */ /* END file ncx.c */ yuma123_2.14/netconf/src/ncx/yang_obj.h0000664000175000017500000005025314770023131020104 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_yang_obj #define _H_yang_obj /* FILE: yang_obj.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* YANG Module parser support: - data-def-stmt support - container-stmt - leaf-stmt - leaf-list-stmt - list-stmt - choice-stmt - uses-stmt - augment-stmt YANG module parser, data-def-stmt support /ns1:value/ns2:value/ns3:value/... An obj_template_t is essentially a QName node in the conceptual element, Every leaf/leaf-list definition node has a typ_def_t, and every value instance node has a val_value_t struct. This allows engine callbacks to process arbitrarily complex data structues with the same code. There are 13 types of objects: enum constant has value node ---------------------------------------- OBJ_TYP_ANYXML Y (1) OBJ_TYP_CONTAINER Y OBJ_TYP_LEAF Y OBJ_TYP_LEAF_LIST Y OBJ_TYP_LIST Y OBJ_TYP_CHOICE N OBJ_TYP_CASE N OBJ_TYP_USES N OBJ_TYP_REFINE N OBJ_TYP_AUGMENT N OBJ_TYP_RPC N OBJ_TYP_RPCIO Y (2) OBJ_TYP_NOTIF N (1) ANYXML is not stored in the value tree as type anyxml. It is converted as follows: Complex Node -> NCX_BT_CONTAINER Simple Node -> NCX_BT_STRING Empty Node -> NCX_BT_EMPTY (2) RPCIO nodes are instantiated only within the implementation, to act as a container for collected parameters or results. It is not found under the element. These objects are grouped as follows: * concrete data node objects (anyxml, container - list) * meta grouping constructs (choice, case) and (uses, refine, augment) * RPC method objects (rpc, input, output) * notification objects (notification) 5 Pass Validation Process -------------------------- In pass 1, the source file is parsed into YANG tokens. String concatentation are quoted string adjustment are handled in this pass. In pass 2, the objects are parsed via yang_obj_consume_datadef. Syntax errors and any other static errors are reported In pass 3, the object definitions are validated for correctness, via the yang_obj_resolve_datadefs function. This is mixed with calls to yang_typ_resolve_typedefs and yang_grp_resolve_groupings. Uses and augments are not expanded in pass 3, so some details like key validation for a list cannot be done, since the contents may depend on the expanded uses or descendant form augment statement. In pass 4, groupings are completed with yang_grp_resolve_complete. Then all the uses-based data is cloned and placed into the tree, via yang_obj_resolve_uses In pass 5, all the augment-based data is cloned and placed into the tree, via yang_obj_resolve_augments The uses and augment objects are kept for XSD and other translation, and needed for internal data sharing. In a cloned object, a minimal amount of data is copied, and the rest is shadowed with back-pointers. For the 'uses' statement, refined objects are merged into the cloned tree as specified by the grouping and any refine statements within the uses statement. For the 'augment' statement, one exact clone of each augmenting node is placed in the target, based on the schema node target for the augment clause. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 11-def-07 abb Begun; start from yang_parse.c */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION yang_obj_consume_datadef * * Parse the next N tokens as a data-def-stmt * Create a obj_template_t struct and add it to the specified module * * First pass of a 3 pass compiler * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the first keyword, starting the specific * data definition * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == queue will get the obj_template_t * parent == parent object or NULL if top-level data-def-stmt * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_consume_datadef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent); /******************************************************************** * FUNCTION yang_obj_consume_datadef_grp * * Parse the next N tokens as a data-def-stmt * Create a obj_template_t struct and add it to the specified module * * First pass of a 3 pass compiler * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the first keyword, starting the specific * data definition * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == queue will get the obj_template_t * parent == parent object or NULL if top-level data-def-stmt * grp == grp_template_t containing 'que' * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_consume_datadef_grp (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp); /******************************************************************** * FUNCTION yang_obj_consume_rpc * * Parse the next N tokens as a rpc-stmt * Create a obj_template_t struct and add it to the specified module * * First pass of a 3 pass compiler * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'rpc' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * * OUTPUTS: * new RPC added to module * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_consume_rpc (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod); /******************************************************************** * FUNCTION yang_obj_consume_notififcation * * Parse the next N tokens as a notification-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'notification' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object or NULL if top-level * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_consume_notification (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp); /******************************************************************** * FUNCTION yang_obj_consume_augment * * Parse the next N tokens as a top-level augment-stmt * Create a obj_template_t struct and add it to the specified module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'augment' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * * OUTPUTS: * new augment object added to module * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_consume_augment (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod); /******************************************************************** * FUNCTION yang_obj_consume_deviation * * Parse the next N tokens as a top-level deviation-stmt * Create a obj_deviation_t struct and add it to the * specified module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'deviation' keyword * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * * OUTPUTS: * new deviation struct added to module deviationQ * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_consume_deviation (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod); /******************************************************************** * FUNCTION yang_obj_resolve_datadefs * * First pass object validation * * Analyze the entire datadefQ within the module struct * Finish all the clauses within this struct that * may have been defered because of possible forward references * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_resolve_datadefs (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ); /******************************************************************** * FUNCTION yang_obj_resolve_uses * * Second pass object validation * This calls expand_uses not resolve_uses! * * Refine-stmts have already been patched into objects in phase 1. * Expand and validate any uses clauses within any objects * within the datadefQ. * * Validate and expand any augments within a uses-stmt * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_resolve_uses (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ); /******************************************************************** * FUNCTION yang_obj_resolve_augments * * * Third pass object validation * * Expand and validate any augment clauses within any objects * within the datadefQ * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_resolve_augments (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ); /******************************************************************** * FUNCTION yang_obj_resolve_augments_final * * Fourth pass object expand augments * * Clone any list keys missed in yang_obj_resolve_augments * This only occurs for augments of external objects, so * only top-level augment-stmts need to be checked. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_resolve_augments_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ); /******************************************************************** * FUNCTION yang_obj_resolve_deviations * * * Validate any local deviation statements within the * module deviationQ * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_resolve_deviations (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod); /******************************************************************** * FUNCTION yang_obj_resolve_ext_deviations * * * Validate any external deviation statements that apply to * this module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_resolve_ext_deviations (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod); /******************************************************************** * FUNCTION yang_obj_resolve_final * * Fourth pass object validation * * Check various final stage errors and warnings * within a single file * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * ingrouping == TRUE if this object being resolved * from yang_grp_resolve_final * == FALSE otherwise * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_resolve_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ, boolean ingrouping); /******************************************************************** * FUNCTION yang_obj_top_resolve_final * * Fourth pass object validation; for top-level module with submodules * * Check various final stage errors and warnings * within a single file * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_top_resolve_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ); /******************************************************************** * FUNCTION yang_obj_resolve_xpath * * Fifth (and final) pass object validation * * Check all leafref, must, and when XPath expressions * to make sure they are well-formed * * Checks the cooked objects, and skips all groupings * uses, and augment nodes * * MUST BE CALLED AFTER yang_obj_resolve_final * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_resolve_xpath (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ); /******************************************************************** * FUNCTION yang_obj_resolve_xpath_final * * Fifth pass validate defvals for XPath leafs * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_resolve_xpath_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ); /******************************************************************** * FUNCTION yang_obj_check_leafref_loops * * Check all leafref objects for hard-wired object loops * Must be done after yang_obj_resolve_xpath * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_check_leafref_loops (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ); /******************************************************************** * FUNCTION yang_obj_remove_deleted_nodes * * Find any nodes marked for deletion and remove them * * INPUTS: * pcb == parser control block to use * tkc == token parse chain to use for errors * mod == module in progress; transfer to this deviationQ * datadefQ == current object datadef Q to check * * RETURNS: * status of the operation *********************************************************************/ extern status_t yang_obj_remove_deleted_nodes (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_yang_obj */ yuma123_2.14/netconf/src/ncx/val123.h0000664000175000017500000001022414770023131017316 0ustar vladimirvladimir/* * Copyright (c) 2013 - 2018, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: val123.h Support functions not part of the original libyumancx API /******************************************************************** * * * I N C L U D E F I L E S * * * *********************************************************************/ #include "val.h" #include "obj.h" #include "cli.h" val_value_t* val123_find_match(val_value_t* haystack_root_val, val_value_t* needle_val); status_t val123_clone_instance(val_value_t* root_val, val_value_t* original_val, val_value_t** clone_val); val_value_t* val123_get_first_obj_instance(val_value_t* top_val, obj_template_t* obj); val_value_t* val123_get_next_obj_instance(val_value_t* top_val, val_value_t* cur_val); boolean ncx123_identity_is_derived_from(const ncx_identity_t * identity, const ncx_identity_t *identity_base); ncx_identity_t* ncx123_identity_get_first_base(const ncx_identity_t* identity); ncx_identity_t* ncx123_identity_get_next_base(const ncx_identity_t* identity, const ncx_identity_t *identity_base); val_value_t* val123_deref(val_value_t* leafref_val); bool val123_bit_is_set(val_value_t* bits_val, const char* bit_str); status_t cli123_parse_value_instance(runstack_context_t *rcxt, val_value_t *topval, obj_template_t *obj, const xmlChar * instance_id_str, const xmlChar *strval, boolean script); status_t val123_new_value_from_instance_id(obj_template_t* parent_obj, const xmlChar* instance_id_str, boolean schemainst, val_value_t** childval, obj_template_t** targobj, val_value_t** targval); obj_template_t* obj123_get_first_data_parent(obj_template_t* obj); status_t cli123_parse_next_child_obj_from_path(obj_template_t* obj, boolean autocomp, const char* parmname, unsigned int* len_out, obj_template_t** chobj_out); status_t cli123_parse_parm_assignment(obj_template_t* obj, boolean autocomp, const char* cli_str, unsigned int* len_out, val_value_t** chval_out); status_t cli123_parse_value_string(const char* cli_str, unsigned int* len, char** valstr); status_t val123_merge_cplx(val_value_t* dst, val_value_t* src); val_value_t* val123_select_obj(val_value_t* parent_val, obj_template_t* child_obj); val_value_t* val123_clone_real(val_value_t* val); obj_template_t* obj123_get_top_uses(obj_template_t* obj); typ_def_t* typ123_get_first_named_typdef(typ_def_t* typdef); unsigned int ncx123_find_all_homonym_top_objs(dlq_hdr_t *modQ, const xmlChar *objname, obj_template_t **matched_objs, unsigned int matched_objs_limit); unsigned int ncx123_find_matching_identities(ncx_module_t* mod, const xmlChar * qname, const typ_idref_t *idref, ncx_identity_t **ids, unsigned int matched_ids_limit); status_t val123_parse_idref_ex (ncx_module_t *mod, const xmlChar *qname, const typ_idref_t *idref, ncx_identity_t **id); unsigned int obj123_find_all_homonym_child_objs (obj_template_t *parent, const xmlChar *objname, obj_template_t **matched_objs, unsigned int matched_objs_limit); ncx_module_t* obj123_find_child_mod_from_name(obj_template_t *parent, const char* modname); ncx_module_t* obj123_find_child_mod_from_prefix(obj_template_t *parent, const char* modprefix); void val123_add_virtual_cb (val_value_t *val, void *cbfn); void tstamp123_datetime_nsec (xmlChar *buff); void ncx123_set_vtimeout_value (uint32 sec); obj_template_t* val123_get_case_for_choice(obj_template_t* choicobj, val_value_t* testval); yuma123_2.14/netconf/src/ncx/rpc.h0000664000175000017500000001421714770023131017100 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_rpc #define _H_rpc /* FILE: rpc.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF protocol remote procedure call common definitions This module is only used by the agent at this time ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 01-may-05 abb Begun. */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_op #include "op.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_msg #include "xml_msg.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define RPC_STR_REQ "rpc" #define RPC_STR_RPY "rpc-reply" #define RPC_STR_ERR "rpc-error" #define RPC_STR_MSG_ID "message-id" #define RPC_STR_GRP_ID "group-id" #define RPC_ERR_QUEUE(MSG) &(MSG)->mhdr.errQ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* Type of the data source */ typedef enum rpc_data_t_ { RPC_DATA_NONE, RPC_DATA_STD, RPC_DATA_YANG } rpc_data_t; /* NETCONF Server and Client RPC Request/Reply Message Header */ typedef struct rpc_msg_t_ { dlq_hdr_t qhdr; /* generic XML message header */ xml_msg_hdr_t mhdr; /* incoming: top-level rpc element data */ xml_attrs_t *rpc_in_attrs; /* borrowed from elem */ /* incoming: * 2nd-level method name element data, used in agt_output_filter * to check get or get-config; cannot import obj.h here! */ struct obj_template_t_ *rpc_method; /* incoming: SERVER RPC processing state */ int rpc_agt_state; /* agt_rpc_phase_t */ op_errop_t rpc_err_option; op_editop_t rpc_top_editop; val_value_t *rpc_input; /* incoming: * hooks for method routines to save context or whatever */ void *rpc_user1; void *rpc_user2; uint32 rpc_returncode; /* for nested callbacks */ /* incoming: get method reply handling builtin * If the rpc_datacb is non-NULL then it will be used as a * callback to generate the rpc-reply inline, instead of * buffering the output. * The rpc_data and rpc_filter parameters are optionally used * by the rpc_datacb function to generate a reply. */ rpc_data_t rpc_data_type; /* type of data reply */ void *rpc_datacb; /* agt_rpc_data_cb_t */ dlq_hdr_t rpc_dataQ; /* data reply: Q of val_value_t */ op_filter_t rpc_filter; /* backptrs for get* methods */ /* incoming: agent database edit transaction control block * must be freed by an upper layer if set to malloced data */ struct agt_cfg_transaction_t_ *rpc_txcb; /* load-config parse-error and --startup-error=continue * flag if the val_purge_errors_from_root function is needed */ boolean rpc_parse_errors; } rpc_msg_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION rpc_new_msg * * Malloc and initialize a new rpc_msg_t struct * * INPUTS: * none * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ extern rpc_msg_t * rpc_new_msg (void); /******************************************************************** * FUNCTION rpc_new_out_msg * * Malloc and initialize a new rpc_msg_t struct for output * or for dummy use * * INPUTS: * none * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ extern rpc_msg_t * rpc_new_out_msg (void); /******************************************************************** * FUNCTION rpc_free_msg * * Free all the memory used by the specified rpc_msg_t * * INPUTS: * msg == rpc_msg_t to clean and delete * RETURNS: * none *********************************************************************/ extern void rpc_free_msg (rpc_msg_t *msg); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_rpc */ yuma123_2.14/netconf/src/ncx/ext.c0000664000175000017500000002141514770023131017105 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: ext.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 05jan08 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_appinfo #include "ncx_appinfo.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_typ #include "typ.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION ext_new_template * * Malloc and initialize the fields in a ext_template_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ ext_template_t * ext_new_template (void) { ext_template_t *ext; ext = m__getObj(ext_template_t); if (!ext) { return NULL; } (void)memset(ext, 0x0, sizeof(ext_template_t)); dlq_createSQue(&ext->appinfoQ); ext->status = NCX_STATUS_CURRENT; /* default */ return ext; } /* ext_new_template */ /******************************************************************** * FUNCTION ext_free_template * * Scrub the memory in a ext_template_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * ext == ext_template_t data structure to free *********************************************************************/ void ext_free_template (ext_template_t *ext) { #ifdef DEBUG if (!ext) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (ext->name) { m__free(ext->name); } if (ext->descr) { m__free(ext->descr); } if (ext->ref) { m__free(ext->ref); } if (ext->arg) { m__free(ext->arg); } ncx_clean_appinfoQ(&ext->appinfoQ); m__free(ext); } /* ext_free_template */ /******************************************************************** * FUNCTION ext_clean_extensionQ * * Clean a queue of ext_template_t structs * * INPUTS: * que == Q of ext_template_t data structures to free *********************************************************************/ void ext_clean_extensionQ (dlq_hdr_t *que) { ext_template_t *ext; #ifdef DEBUG if (!que) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (!dlq_empty(que)) { ext = (ext_template_t *)dlq_deque(que); ext_free_template(ext); } } /* ext_clean_groupingQ */ /******************************************************************** * FUNCTION ext_find_extension * * Search a queue of ext_template_t structs for a given name * * INPUTS: * que == Q of ext_template_t data structures to search * name == name string to find * * RETURNS: * pointer to found entry, or NULL if not found *********************************************************************/ ext_template_t * ext_find_extension (ncx_module_t *mod, const xmlChar *name) { ext_template_t *extension; dlq_hdr_t *que; yang_node_t *node; ncx_include_t *inc; #ifdef DEBUG if (!mod || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif extension = ext_find_extension_que(&mod->extensionQ, name); if (extension) { return extension; } que = ncx_get_allincQ(mod); /* check all the submodules, but only the ones visible * to this module or submodule */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { /* get the real submodule struct */ if (!inc->submod) { node = yang_find_node(que, inc->submodule, inc->revision); if (node) { inc->submod = node->submod; } if (!inc->submod) { /* include not found or errors found in it */ continue; } } /* check the extension Q in this submodule */ extension = ext_find_extension_que(&inc->submod->extensionQ, name); if (extension) { return extension; } } return NULL; } /* ext_find_extension */ /******************************************************************** * FUNCTION ext_find_extension_que * * Find an ext_template_t struct in the specified Q * * INPUTS: * extensionQ == Q of ext_template_t to search * name == extension name to find * * RETURNS: * pointer to found extension or NULL if not found *********************************************************************/ ext_template_t * ext_find_extension_que (dlq_hdr_t *extensionQ, const xmlChar *name) { ext_template_t *extension; #ifdef DEBUG if (!extensionQ || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (extension = (ext_template_t *)dlq_firstEntry(extensionQ); extension != NULL; extension = (ext_template_t *)dlq_nextEntry(extension)) { if (!xml_strcmp(extension->name, name)) { return extension; } } return NULL; } /* ext_find_extension_que */ /******************************************************************** * FUNCTION ext_find_extension_all * * Search a module of ext_template_t structs for a given name * Check all submodules as well * * INPUTS: * mod == module to check * name == name string to find * * RETURNS: * pointer to found entry, or NULL if not found *********************************************************************/ ext_template_t * ext_find_extension_all (ncx_module_t *mod, const xmlChar *name) { ext_template_t *extension; dlq_hdr_t *que; yang_node_t *node; #ifdef DEBUG if (!mod || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif extension = ext_find_extension_que(&mod->extensionQ, name); if (extension) { return extension; } que = ncx_get_allincQ(mod); /* check all the submodules, but only the ones visible * to this module or submodule */ for (node = (yang_node_t *)dlq_firstEntry(que); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { /* check the extension Q in this submodule */ extension = ext_find_extension_que(&node->submod->extensionQ, name); if (extension) { return extension; } } } return NULL; } /* ext_find_extension_all */ /* END ext.c */ yuma123_2.14/netconf/src/ncx/yang.c0000664000175000017500000032760014770023131017250 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yang.c YANG module parser utilities ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 15novt07 abb begun; split out from yang_parse.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_appinfo #include "ncx_appinfo.h" #endif #ifndef _H_ncx_feature #include "ncx_feature.h" #endif #ifndef _H_ncx_num #include "ncx_num.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tstamp #include "tstamp.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xpath1 #include "xpath1.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #include "tk.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG /* #define YANG_DEBUG 1 */ #endif /******************************************************************** * * * S T A T I C D A T A * * * *********************************************************************/ static const xmlChar *top_keywords[] = { YANG_K_ANYXML, YANG_K_AUGMENT, YANG_K_BELONGS_TO, YANG_K_CHOICE, YANG_K_CONTACT, YANG_K_CONTAINER, YANG_K_DESCRIPTION, YANG_K_DEVIATION, YANG_K_EXTENSION, YANG_K_FEATURE, YANG_K_GROUPING, YANG_K_IDENTITY, YANG_K_IMPORT, YANG_K_INCLUDE, YANG_K_LEAF, YANG_K_LEAF_LIST, YANG_K_LIST, YANG_K_NAMESPACE, YANG_K_NOTIFICATION, YANG_K_ORGANIZATION, YANG_K_PREFIX, YANG_K_REFERENCE, YANG_K_REVISION, YANG_K_RPC, YANG_K_TYPEDEF, YANG_K_USES, YANG_K_YANG_VERSION, NULL }; #ifdef YANG_DEBUG static uint32 new_node_cnt = 0; static uint32 free_node_cnt = 0; #endif /******************************************************************** * FUNCTION yang_consume_semiapp * * consume a stmtsep clause * Consume a semi-colon to end a simple clause, or * consume a vendor extension * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * appinfoQ == queue to receive any vendor extension found * * OUTPUTS: * *appinfoQ has the extesnion added if any * current token is advanced * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_semiapp (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *appinfoQ) { const char *expstr; status_t res, retres; boolean done; #ifdef DEBUG if (!tkc) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif expstr = "semi-colon or left brace"; retres = NO_ERR; /* get semicolon or left brace */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: return NO_ERR; case TK_TT_LBRACE: break; case TK_TT_NONE: default: res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, res, expstr); switch (TK_CUR_TYP(tkc)) { case TK_TT_TSTRING: case TK_TT_MSTRING: case TK_TT_RBRACE: /* try to recover and keep parsing */ TK_BKUP(tkc); break; default: ; } return res; } /* got a left brace '{' * the start of a vendor extension section */ done = FALSE; while (!done) { res = ncx_consume_appinfo2(tkc, mod, appinfoQ); if (res == ERR_NCX_SKIPPED) { res = NO_ERR; done = TRUE; } else { CHK_EXIT(res, retres); } } res = ncx_consume_token(tkc, mod, TK_TT_RBRACE); CHK_EXIT(res, retres); return retres; } /* yang_consume_semiapp */ /******************************************************************** * FUNCTION yang_consume_string * * consume 1 string token * Consume a YANG string token in any of the 3 forms * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * field == address of field to get the string (may be NULL) * * OUTPUTS: * if field not NULL, * *field is set with a malloced string in NO_ERR * current token is advanced * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_string (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **field) { const char *expstr; xmlChar *str; status_t res; res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } if (TK_CUR_STR(tkc)) { if (field) { if (TK_CUR_MOD(tkc)) { *field = m__getMem(TK_CUR_MODLEN(tkc)+TK_CUR_LEN(tkc)+2); if (*field) { str = *field; str += xml_strncpy(str, TK_CUR_MOD(tkc), TK_CUR_MODLEN(tkc)); *str++ = ':'; if (TK_CUR_VAL(tkc)) { xml_strncpy(str, TK_CUR_VAL(tkc), TK_CUR_LEN(tkc)); } else { *str = 0; } } } else { if (TK_CUR_VAL(tkc)) { *field = xml_strdup(TK_CUR_VAL(tkc)); } else { *field = xml_strdup(EMPTY_STRING); } } if (!*field) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); } if (res == NO_ERR) { res = tk_check_save_origstr(tkc, TK_CUR(tkc), (const void *)field); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); } } } /* else server does not save descr/ref */ } else { switch (TK_CUR_TYP(tkc)) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); break; case TK_TT_LBRACE: case TK_TT_RBRACE: case TK_TT_SEMICOL: res = ERR_NCX_WRONG_TKTYPE; expstr = "string"; ncx_mod_exp_err(tkc, mod, res, expstr); break; default: if (field) { if (TK_CUR_VAL(tkc)) { *field = xml_strdup(TK_CUR_VAL(tkc)); } else { *field = xml_strdup((const xmlChar *) tk_get_token_name(TK_CUR_TYP(tkc))); } if (!*field) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); } } } } return res; } /* yang_consume_string */ /******************************************************************** * FUNCTION yang_consume_keyword * * consume 1 YANG keyword or vendor extension keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * prefix == address of field to get the prefix string (may be NULL) * field == address of field to get the name string (may be NULL) * * OUTPUTS: * if prefix not NULL, * *prefix is set with a malloced string in NO_ERR * if field not NULL, * *field is set with a malloced string in NO_ERR * current token is advanced * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_keyword (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **prefix, xmlChar **field) { tk_type_t tktyp; status_t res, retres; res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } retres = NO_ERR; tktyp = TK_CUR_TYP(tkc); if (tktyp==TK_TT_QSTRING || tktyp==TK_TT_SQSTRING) { res = ERR_NCX_INVALID_VALUE; log_error("\nError: quoted strings not allowed for keywords"); } else if (TK_CUR_ID(tkc)) { if (TK_CUR_VAL(tkc)) { /* right kind of tokens, validate id name string */ if (ncx_valid_name(TK_CUR_VAL(tkc), TK_CUR_LEN(tkc))) { if (field) { *field = xml_strdup(TK_CUR_VAL(tkc)); if (!*field) { res = ERR_INTERNAL_MEM; } } } else { res = ERR_NCX_INVALID_NAME; } if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, "identifier-ref string"); retres = res; res = NO_ERR; } /* validate prefix name string if any */ if (TK_CUR_MOD(tkc)) { if (ncx_valid_name(TK_CUR_MOD(tkc), TK_CUR_MODLEN(tkc))) { if (prefix) { *prefix = xml_strdup(TK_CUR_MOD(tkc)); if (!*prefix) { res = ERR_INTERNAL_MEM; } } } else { res = ERR_NCX_INVALID_NAME; } } } else { res = ERR_NCX_INVALID_NAME; } } else { res = ERR_NCX_WRONG_TKTYPE; } if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); retres = res; } return retres; } /* yang_consume_keyword */ /******************************************************************** * FUNCTION yang_consume_nowsp_string * * consume 1 string without any whitespace * Consume a YANG string token in any of the 3 forms * Check No whitespace allowed! * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * field == address of field to get the string (may be NULL) * * OUTPUTS: * if field not NULL, * *field is set with a malloced string in NO_ERR * current token is advanced * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_nowsp_string (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **field) { xmlChar *str; status_t res; res = TK_ADV(tkc); if (res == NO_ERR) { if (TK_CUR_STR(tkc)) { if (TK_CUR_MOD(tkc)) { if (field) { *field = m__getMem(TK_CUR_MODLEN(tkc)+TK_CUR_LEN(tkc)+2); if (*field) { str = *field; str += xml_strncpy(str, TK_CUR_MOD(tkc), TK_CUR_MODLEN(tkc)); *str++ = ':'; if (TK_CUR_VAL(tkc)) { xml_strncpy(str, TK_CUR_VAL(tkc), TK_CUR_LEN(tkc)); } else { *str = 0; } } } } else if (TK_CUR_VAL(tkc)) { if (!tk_is_wsp_string(TK_CUR(tkc))) { if (field) { *field = xml_strdup(TK_CUR_VAL(tkc)); if (!*field) { res = ERR_INTERNAL_MEM; } } } else { res = ERR_NCX_INVALID_VALUE; } } else { res = ERR_NCX_INVALID_VALUE; } } else { res = ERR_NCX_WRONG_TKTYPE; } } if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, "string w/o whitespace"); } return res; } /* yang_consume_nowsp_string */ /******************************************************************** * FUNCTION yang_consume_id_string * * consume an identifier-str token * Consume a YANG string token in any of the 3 forms * Check that it is a valid YANG identifier string * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * field == address of field to get the string (may be NULL) * * OUTPUTS: * if field not NULL: * *field is set with a malloced string in NO_ERR * current token is advanced * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_id_string (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **field) { status_t res; res = TK_ADV(tkc); if (res == NO_ERR) { if (TK_CUR_ID(tkc) || (TK_CUR_STR(tkc) && !tk_is_wsp_string(TK_CUR(tkc)))) { if (TK_CUR_MOD(tkc)) { log_error("\nError: Prefix '%s' not allowed", TK_CUR_MOD(tkc)); res = ERR_NCX_INVALID_NAME; } else if (TK_CUR_VAL(tkc)) { if (ncx_valid_name(TK_CUR_VAL(tkc), TK_CUR_LEN(tkc))) { if (field) { *field = xml_strdup(TK_CUR_VAL(tkc)); if (!*field) { res = ERR_INTERNAL_MEM; } } } else { res = ERR_NCX_INVALID_NAME; } } else { res = ERR_NCX_INVALID_NAME; } } else { res = ERR_NCX_WRONG_TKTYPE; } } if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, "identifier string"); } else { ncx_check_warn_idlen(tkc, mod, TK_CUR_VAL(tkc)); } return res; } /* yang_consume_id_string */ /******************************************************************** * FUNCTION yang_consume_pid_string * * consume an identifier-ref-str token * Consume a YANG string token in any of the 3 forms * Check that it is a valid identifier-ref-string * Get the prefix if any is set * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * prefix == address of prefix string if any is used (may be NULL) * field == address of field to get the string (may be NULL) * * OUTPUTS: * if field not NULL: * *field is set with a malloced string in NO_ERR * current token is advanced * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_pid_string (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **prefix, xmlChar **field) { const xmlChar *p; tk_type_t tktyp; status_t res; uint32 plen, nlen; res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } if (prefix) { *prefix = NULL; } if (field) { *field = NULL; } tktyp = TK_CUR_TYP(tkc); if (tktyp == TK_TT_QSTRING || tktyp == TK_TT_SQSTRING || tktyp == TK_TT_STRING) { p = TK_CUR_VAL(tkc); while (*p && *p != ':') { p++; } if (*p) { /* found the prefix separator char, * try to parse this quoted string * as a prefix:identifier */ plen = (uint32)(p - TK_CUR_VAL(tkc)); nlen = TK_CUR_LEN(tkc) - (plen+1); if (plen && plen+1 < TK_CUR_LEN(tkc)) { /* have a possible string structure to try */ if (ncx_valid_name(TK_CUR_VAL(tkc), plen) && ncx_valid_name(p+1, nlen)) { /* have valid syntax for prefix:identifier */ if (prefix) { *prefix = xml_strndup(TK_CUR_VAL(tkc), plen); if (*prefix == NULL) { res = ERR_INTERNAL_MEM; } } if (res == NO_ERR && field) { *field = xml_strndup(p+1, nlen); if (*field == NULL) { res = ERR_INTERNAL_MEM; if (prefix) { m__free(*prefix); *prefix = NULL; } } } if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); } else { if (prefix && *prefix) { ncx_check_warn_idlen(tkc, mod, *prefix); } if (field && *field) { ncx_check_warn_idlen(tkc, mod, *field); } } return res; } } } /* else drop through and try again */ } if (TK_CUR_ID(tkc) || (TK_CUR_STR(tkc) && !tk_is_wsp_string(TK_CUR(tkc)))) { if (TK_CUR_VAL(tkc)) { /* right kind of tokens, validate id name string */ if (ncx_valid_name(TK_CUR_VAL(tkc), TK_CUR_LEN(tkc))) { if (field) { *field = xml_strdup(TK_CUR_VAL(tkc)); if (*field == NULL) { res = ERR_INTERNAL_MEM; } else { ncx_check_warn_idlen(tkc, mod, *field); } } } else { res = ERR_NCX_INVALID_NAME; } /* validate prefix name string if any */ if (res == NO_ERR && TK_CUR_MOD(tkc)) { if (ncx_valid_name(TK_CUR_MOD(tkc), TK_CUR_MODLEN(tkc))) { if (prefix) { *prefix = xml_strdup(TK_CUR_MOD(tkc)); if (*prefix == NULL) { res = ERR_INTERNAL_MEM; } } } else { res = ERR_NCX_INVALID_NAME; } } } else { res = ERR_NCX_INVALID_NAME; } if (res == NO_ERR) { if (prefix && *prefix) { ncx_check_warn_idlen(tkc, mod, *prefix); } if (field && *field) { ncx_check_warn_idlen(tkc, mod, *field); } } } else { res = ERR_NCX_WRONG_TKTYPE; } if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, "identifier-ref string"); if (prefix && *prefix) { m__free(*prefix); *prefix = NULL; } if (field && *field) { m__free(*field); *field = NULL; } } return res; } /* yang_consume_pid_string */ /******************************************************************** * FUNCTION yang_consume_iffeature_expr *********************************************************************/ status_t yang_consume_iffeature_expr (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **prefix, xmlChar **field, xmlChar **expr) { const xmlChar *p; tk_type_t tktyp; status_t res; uint32 plen, nlen; res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } if (prefix) { *prefix = NULL; } if (field) { *field = NULL; } if (expr) { *expr = NULL; } tktyp = TK_CUR_TYP(tkc); if (tktyp == TK_TT_QSTRING || tktyp == TK_TT_SQSTRING || tktyp == TK_TT_STRING) { p = TK_CUR_VAL(tkc); if(strpbrk (p, " ")) { *expr = xml_strndup(p+1, nlen); return res; } } TK_BKUP(tkc); return yang_consume_pid_string(tkc, mod, prefix, field); } /* yang_consume_iffeature_expr */ /******************************************************************** * FUNCTION yang_consume_error_stmts * * consume the range. length. pattern. must error info extensions * Parse the sub-section as a sub-section for error-app-tag * and error-message clauses * * Current token is the starting left brace for the sub-section * that is extending a range, length, pattern, or must statement * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * errinfo == pointer to valid ncx_errinfo_t struct * The struct will be filled in by this fn * appinfoQ == Q to hold any extensions found * * OUTPUTS: * *errinfo filled in with any clauses found * *appinfoQ filled in with any extensions found * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_error_stmts (tk_chain_t *tkc, ncx_module_t *mod, ncx_errinfo_t *errinfo, dlq_hdr_t *appinfoQ) { const xmlChar *val; const char *expstr = "description, reference, error-app-tag, or error-message keyword"; ncx_errinfo_t *err = errinfo; tk_type_t tktyp; status_t res = NO_ERR; boolean done = FALSE; boolean desc = FALSE; boolean ref = FALSE; boolean etag = FALSE; boolean emsg = FALSE; #ifdef DEBUG if (!tkc || !errinfo) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, appinfoQ); if ( ( NO_ERR != res && res < ERR_LAST_SYS_ERR ) || res == ERR_NCX_EOF ) { return res; } break; case TK_TT_RBRACE: done = TRUE; break; case TK_TT_TSTRING: /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { /* Optional 'description' field is present */ res = yang_consume_descr( tkc, mod, &err->descr, &desc, appinfoQ ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { /* Optional 'description' field is present */ res = yang_consume_descr( tkc, mod, &err->ref, &ref, appinfoQ ); } else if (!xml_strcmp(val, YANG_K_ERROR_APP_TAG)) { /* Optional 'error-app-tag' field is present */ res = yang_consume_strclause( tkc, mod, &err->error_app_tag, &etag, appinfoQ ); } else if (!xml_strcmp(val, YANG_K_ERROR_MESSAGE)) { /* Optional 'error-app-tag' field is present */ res = yang_consume_strclause( tkc, mod, &err->error_message, &emsg, appinfoQ ); } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } if ( ( NO_ERR != res && res < ERR_LAST_SYS_ERR ) || res == ERR_NCX_EOF ) { return res; } break; /* YANG clause assumed */ default: res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, res, expstr); break; } } return res; } /* yang_consume_error_stmts */ /******************************************************************** * FUNCTION yang_consume_descr * * consume one descriptive string clause * Parse the description or reference statement * * Current token is the starting keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * str == string to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found * * OUTPUTS: * *str set to the value if str not NULL * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_descr (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **str, boolean *dupflag, dlq_hdr_t *appinfoQ) { status_t res, retres; boolean save; #ifdef DEBUG if (!tkc) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif save = TRUE; res = NO_ERR; retres = NO_ERR; if (dupflag) { if (*dupflag) { retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); save = FALSE; } else { *dupflag = TRUE; } } /* get the string value */ if (ncx_save_descr() && str && save) { res = yang_consume_string(tkc, mod, str); } else { res = yang_consume_string(tkc, mod, NULL); } CHK_EXIT(res, retres); /* finish the clause */ if (save) { res = yang_consume_semiapp(tkc, mod, appinfoQ); } else { res = yang_consume_semiapp(tkc, mod, NULL); } CHK_EXIT(res, retres); return retres; } /* yang_consume_descr */ /******************************************************************** * FUNCTION yang_consume_pid * * consume one [prefix:]name clause * Parse the rest of the statement (2 forms): * * keyword [prefix:]name; * * keyword [prefix:]name { appinfo } * * Current token is the starting keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * prefixstr == prefix string to set (may be NULL) * str == string to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found * * OUTPUTS: * *str set to the value if str not NULL * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_pid (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **prefixstr, xmlChar **str, boolean *dupflag, dlq_hdr_t *appinfoQ) { status_t res, retres; boolean save; #ifdef DEBUG if (!tkc) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif save = TRUE; res = NO_ERR; retres = NO_ERR; if (dupflag) { if ((mod->langver == NCX_YANG_VERSION10) && *dupflag) { retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); save = FALSE; } else { *dupflag = TRUE; } } /* get the PID string value */ res = yang_consume_pid_string(tkc, mod, prefixstr, str); CHK_EXIT(res, retres); /* finish the clause */ if (save) { res = yang_consume_semiapp(tkc, mod, appinfoQ); } else { res = yang_consume_semiapp(tkc, mod, NULL); } CHK_EXIT(res, retres); return retres; } /* yang_consume_pid */ /******************************************************************** * FUNCTION yang_consume_strclause * * consume one normative string clause * Parse the string-parameter-based statement * * Current token is the starting keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * str == string to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *str set to the value if str not NULL * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_strclause (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **str, boolean *dupflag, dlq_hdr_t *appinfoQ) { status_t res, retres; boolean save; save = TRUE; res = NO_ERR; retres = NO_ERR; if (dupflag) { if (*dupflag) { retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); save = FALSE; } else { *dupflag = TRUE; } } /* get the string value */ if (str && save) { res = yang_consume_string(tkc, mod, str); } else { res = yang_consume_string(tkc, mod, NULL); } CHK_EXIT(res, retres); /* finish the clause */ if (save) { res = yang_consume_semiapp(tkc, mod, appinfoQ); } else { res = yang_consume_semiapp(tkc, mod, NULL); } CHK_EXIT(res, retres); return retres; } /* yang_consume_strclause */ /******************************************************************** * FUNCTION yang_consume_status * * consume one status clause * Parse the status statement * * Current token is the 'status' keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * stat == status object to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *stat set to the value if stat not NULL * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_status (tk_chain_t *tkc, ncx_module_t *mod, ncx_status_t *status, boolean *dupflag, dlq_hdr_t *appinfoQ) { xmlChar *str; const char *expstr; ncx_status_t stat2; status_t res, retres; boolean save; expstr = "status enumeration string"; retres = NO_ERR; save = TRUE; if (dupflag) { if (*dupflag) { res = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, res); save = FALSE; } else { *dupflag = TRUE; } } /* get the string value */ res = yang_consume_string(tkc, mod, &str); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { if (str) { m__free(str); } return res; } } if (str) { if (status && save) { *status = ncx_get_status_enum(str); stat2 = *status; } else { stat2 = ncx_get_status_enum(str); } if (save && stat2 == NCX_STATUS_NONE) { retres = ERR_NCX_INVALID_VALUE; ncx_mod_exp_err(tkc, mod, retres, expstr); } m__free(str); } /* finish the clause */ if (save) { res = yang_consume_semiapp(tkc, mod, appinfoQ); } else { res = yang_consume_semiapp(tkc, mod, NULL); } CHK_EXIT(res, retres); return retres; } /* yang_consume_status */ /******************************************************************** * FUNCTION yang_consume_ordered_by * * consume one ordered-by clause * Parse the ordered-by statement * * Current token is the 'ordered-by' keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * ordsys == ordered-by object to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *ordsys set to the value if not NULL * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_ordered_by (tk_chain_t *tkc, ncx_module_t *mod, boolean *ordsys, boolean *dupflag, dlq_hdr_t *appinfoQ) { xmlChar *str; const char *expstr; boolean ordsys2; status_t res, retres; boolean save; expstr = "system or user keyword"; retres = NO_ERR; save = TRUE; str = NULL; ordsys2 = FALSE; if (dupflag) { if (*dupflag) { res = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, res); save = FALSE; } else { *dupflag = TRUE; } } /* get the string value */ res = yang_consume_string(tkc, mod, &str); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { if (str) { m__free(str); } return res; } } if (str) { if (!xml_strcmp(str, YANG_K_USER)) { ordsys2 = FALSE; } else if (!xml_strcmp(str, YANG_K_SYSTEM)) { ordsys2 = TRUE; } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); } if (ordsys && save) { *ordsys = ordsys2; } m__free(str); } /* finish the clause */ if (save) { res = yang_consume_semiapp(tkc, mod, appinfoQ); } else { res = yang_consume_semiapp(tkc, mod, NULL); } CHK_EXIT(res, retres); return retres; } /* yang_consume_ordered_by */ /******************************************************************** * FUNCTION yang_consume_max_elements * * consume one max-elements clause * Parse the max-elements statement * * Current token is the 'max-elements' keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * maxelems == max-elements object to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *maxelems set to the value if not NULL * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_max_elements (tk_chain_t *tkc, ncx_module_t *mod, uint32 *maxelems, boolean *dupflag, dlq_hdr_t *appinfoQ) { xmlChar *str; const xmlChar *nextval; tk_type_t nexttk; status_t res; res = NO_ERR; nexttk = tk_next_typ(tkc); nextval = tk_next_val(tkc); if (nexttk==TK_TT_DNUM) { res = yang_consume_uint32(tkc, mod, maxelems, dupflag, appinfoQ); } else if (TK_TYP_STR(nexttk)) { str = NULL; if (!xml_strcmp(nextval, YANG_K_UNBOUNDED)) { res = yang_consume_strclause(tkc, mod, &str, dupflag, appinfoQ); if (str) { m__free(str); str = NULL; } if (maxelems) { *maxelems = 0; } } else { /* may be a quoted number or an error */ res = yang_consume_uint32(tkc, mod, maxelems, dupflag, appinfoQ); } } return res; } /* yang_consume_max_elements */ /******************************************************************** * FUNCTION yang_consume_must * * consume one must-stmt into mustQ * Parse the must statement * * Current token is the 'must' keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * mustQ == address of Q of xpath_pcb_t structs to store * a new malloced xpath_pcb_t * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * must entry malloced and added to mustQ (if NO_ERR) * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_must (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *mustQ, dlq_hdr_t *appinfoQ) { const xmlChar *val; const char *expstr; xpath_pcb_t *must; tk_type_t tktyp; status_t res, retres; boolean done, etag, emsg, desc, ref; #ifdef DEBUG if (!tkc || !mustQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif must = xpath_new_pcb(NULL, NULL); if (!must) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&must->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); retres = NO_ERR; expstr = "Xpath expression string"; done = FALSE; etag = FALSE; emsg = FALSE; desc = FALSE; ref = FALSE; /* get the Xpath string for the must expression */ res = yang_consume_string(tkc, mod, &must->exprstr); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { xpath_free_pcb(must); return retres; } } /* parse the must expression for well-formed XPath */ res = xpath1_parse_expr(tkc, mod, must, XP_SRC_YANG); if (res != NO_ERR) { /* errors already reported */ retres = res; if (NEED_EXIT(res)) { xpath_free_pcb(must); return retres; } } /* move on to the must sub-clauses, if any */ expstr = "error-message, error-app-tag, " "description, or reference keywords"; res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); xpath_free_pcb(must); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: dlq_enque(must, mustQ); return retres; case TK_TT_LBRACE: break; default: res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, res, expstr); xpath_free_pcb(must); return res; } while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); xpath_free_pcb(must); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); xpath_free_pcb(must); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { xpath_free_pcb(must); return retres; } } continue; case TK_TT_RBRACE: dlq_enque(must, mustQ); return retres; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); xpath_free_pcb(must); done = TRUE; continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_ERROR_APP_TAG)) { /* Optional 'error-app-tag' field is present */ res = yang_consume_strclause(tkc, mod, &must->errinfo.error_app_tag, &etag, appinfoQ); } else if (!xml_strcmp(val, YANG_K_ERROR_MESSAGE)) { /* Optional 'error-message' field is present */ res = yang_consume_strclause(tkc, mod, &must->errinfo.error_message, &emsg, appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { /* Optional 'description' field is present */ res = yang_consume_descr(tkc, mod, &must->errinfo.descr, &desc, appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { /* Optional 'reference' field is present */ res = yang_consume_descr(tkc, mod, &must->errinfo.ref, &ref, appinfoQ); } else { expstr = "must sub-statement"; res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { xpath_free_pcb(must); done = TRUE; } } } return retres; } /* yang_consume_must */ /******************************************************************** * FUNCTION yang_consume_when * * consume one when-stmt into obj->when * Parse the when statement * * Current token is the 'when' keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * obj == obj_template_t of the parent object of this 'when' * whenflag == address of boolean set-once flag for the when-stmt * an error will be generated if the value passed * in is TRUE. Set the initial value to FALSE * before first call, and do not change it after that * OUTPUTS: * obj->when malloced and dilled in * *whenflag set if not set already * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_when (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, boolean *whenflag) { const xmlChar *val; const char *expstr; xpath_pcb_t *when; tk_type_t tktyp; status_t res, retres; boolean done, desc, ref; #ifdef DEBUG if (!tkc || !mod || !obj) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif retres = NO_ERR; if (whenflag) { if (*whenflag) { retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); } else { *whenflag = TRUE; } } when = xpath_new_pcb(NULL, NULL); if (!when) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&when->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); expstr = "Xpath expression string"; done = FALSE; desc = FALSE; ref = FALSE; /* pass off 'when' memory here */ obj->when = when; /* get the Xpath string for the when expression */ res = yang_consume_string(tkc, mod, &when->exprstr); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { return retres; } } /* parse the must expression for well-formed XPath */ res = xpath1_parse_expr(tkc, mod, when, XP_SRC_YANG); if (res != NO_ERR) { /* errors already reported */ retres = res; if (NEED_EXIT(res)) { return retres; } } /* move on to the must sub-clauses, if any */ expstr = "description or reference keywords"; res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: return retres; case TK_TT_LBRACE: break; default: res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, res, expstr); return res; } while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { return retres; } } continue; case TK_TT_RBRACE: return retres; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { /* Optional 'description' field is present */ res = yang_consume_descr(tkc, mod, &when->errinfo.descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { /* Optional 'reference' field is present */ res = yang_consume_descr(tkc, mod, &when->errinfo.ref, &ref, &obj->appinfoQ); } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { done = TRUE; } } } return retres; } /* yang_consume_when */ /******************************************************************** * FUNCTION yang_consume_iffeature * * consume one if-feature-stmt into iffeatureQ * Parse the if-feature statement * * Current token is the 'if-feature' keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * iffeatureQ == Q of ncx_iffeature_t to hold the new if-feature * appinfoQ == queue to store appinfo (extension usage) * * OUTPUTS: * ncx_iffeature_t malloced and added to iffeatureQ * maybe ncx_appinfo_t structs added to appinfoQ * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_iffeature (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *iffeatureQ, dlq_hdr_t *appinfoQ) { ncx_iffeature_t *iff; xmlChar *prefix, *name; xmlChar *expr; status_t res, res2; #ifdef DEBUG if (!tkc || !mod || !iffeatureQ || !appinfoQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif prefix = NULL; name = NULL; expr = NULL; if(mod->langver>=NCX_YANG_VERSION11) { res = yang_consume_iffeature_expr(tkc, mod, &prefix, &name, &expr); } else { res = yang_consume_pid_string(tkc, mod, &prefix, &name); } if (res == NO_ERR) { /* check if this if if-feature already entered warning */ iff = ncx_find_iffeature_1dot1(iffeatureQ, prefix, name, expr, mod->prefix); if (iff) { if (ncx_warning_enabled(ERR_NCX_DUP_IF_FEATURE)) { log_warn("\nWarning: if-feature '%s%s%s%s' " "already specified on line %u", (prefix) ? prefix : EMPTY_STRING, (prefix) ? ":" : "", (name) ? name : EMPTY_STRING, (expr) ? expr : EMPTY_STRING, iff->tkerr.linenum); ncx_print_errormsg(tkc, mod, ERR_NCX_DUP_IF_FEATURE); } else { ncx_inc_warnings(mod); } if (prefix) { m__free(prefix); } if (name) { m__free(name); } if (expr) { m__free(expr); } } else { /* not found so add it to the if-feature Q */ iff = ncx_new_iffeature(); if (!iff) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); if (prefix) { m__free(prefix); } if (name) { m__free(name); } if (expr) { m__free(expr); } } else { /* transfer malloced fields */ iff->prefix = prefix; iff->name = name; iff->expr = expr; ncx_set_error(&iff->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); dlq_enque(iff, iffeatureQ); } } } /* get the closing semi-colon even if previous error */ res2 = yang_consume_semiapp(tkc, mod, appinfoQ); if (res == NO_ERR) { res = res2; } return res; } /* yang_consume_iffeature */ /******************************************************************** * FUNCTION yang_consume_boolean * * consume one boolean clause * Parse the boolean parameter based statement * * Current token is the starting keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * boolval == boolean value to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *str set to the value if str not NULL * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_boolean (tk_chain_t *tkc, ncx_module_t *mod, boolean *boolval, boolean *dupflag, dlq_hdr_t *appinfoQ) { xmlChar *str; const char *expstr; status_t res, retres; boolean save; retres = NO_ERR; expstr = "true or false keyword"; str = NULL; save = TRUE; if (dupflag) { if (*dupflag) { retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); save = FALSE; } else { *dupflag = TRUE; } } /* get the string value */ res = yang_consume_string(tkc, mod, &str); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { if (str) { m__free(str); } return res; } } if (str) { if (!xml_strcmp(str, NCX_EL_TRUE)) { if (save) { *boolval = TRUE; } } else if (!xml_strcmp(str, NCX_EL_FALSE)) { if (save) { *boolval = FALSE; } } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); } m__free(str); } /* finish the clause */ if (save) { res = yang_consume_semiapp(tkc, mod, appinfoQ); } else { res = yang_consume_semiapp(tkc, mod, NULL); } CHK_EXIT(res, retres); return retres; } /* yang_consume_boolean */ /******************************************************************** * FUNCTION yang_consume_int32 * * consume one int32 clause * Parse the int32 based parameter statement * * Current token is the starting keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * num == int32 value to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *num set to the value if num not NULL * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_int32 (tk_chain_t *tkc, ncx_module_t *mod, int32 *num, boolean *dupflag, dlq_hdr_t *appinfoQ) { const char *expstr; ncx_num_t numstruct; status_t res, retres; boolean save; save = TRUE; retres = NO_ERR; expstr = "signed integer number"; if (dupflag) { if (*dupflag) { retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); save = FALSE; } else { *dupflag = TRUE; } } res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } if (TK_CUR_NUM(tkc) || TK_CUR_STR(tkc)) { res = ncx_convert_tkcnum(tkc, NCX_BT_INT32, &numstruct); if (res == NO_ERR) { if (num && save) { *num = numstruct.i; } } else { retres = res; ncx_mod_exp_err(tkc, mod, retres, expstr); } } else { retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); } if (save) { res = yang_consume_semiapp(tkc, mod, appinfoQ); } else { res = yang_consume_semiapp(tkc, mod, NULL); } CHK_EXIT(res, retres); return retres; } /* yang_consume_int32 */ /******************************************************************** * FUNCTION yang_consume_uint32 * * consume one uint32 clause * Parse the uint32 based parameter statement * * Current token is the starting keyword * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * num == uint32 value to set (may be NULL) * dupflag == flag to check if entry already found (may be NULL) * appinfoQ == Q to hold any extensions found (may be NULL) * * OUTPUTS: * *num set to the value if num not NULL * * RETURNS: * status of the operation *********************************************************************/ status_t yang_consume_uint32 (tk_chain_t *tkc, ncx_module_t *mod, uint32 *num, boolean *dupflag, dlq_hdr_t *appinfoQ) { const char *expstr; ncx_num_t numstruct; status_t res, retres; boolean save; save = TRUE; retres = NO_ERR; expstr = "non-negative number"; if (dupflag) { if (*dupflag) { retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); save = FALSE; } else { *dupflag = TRUE; } } res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } if (TK_CUR_NUM(tkc) || TK_CUR_STR(tkc)) { res = ncx_convert_tkcnum(tkc, NCX_BT_UINT32, &numstruct); if (res == NO_ERR) { if (num && save) { *num = numstruct.u; } } else { retres = res; ncx_mod_exp_err(tkc, mod, retres, expstr); } } else { retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); } if (save) { res = yang_consume_semiapp(tkc, mod, appinfoQ); } else { res = yang_consume_semiapp(tkc, mod, NULL); } CHK_EXIT(res, retres); return retres; } /* yang_consume_uint32 */ /******************************************************************** * FUNCTION yang_find_imp_typedef * * Find the specified imported typedef * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * prefix == prefix value to use * name == type name to use * tkerr == error record to use in error messages (may be NULL) * typ == address of return typ_template_t pointer * * OUTPUTS: * *typ set to the found typ_template_t, if NO_ERR * * RETURNS: * status of the operation *********************************************************************/ status_t yang_find_imp_typedef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *prefix, const xmlChar *name, ncx_error_t *tkerr, typ_template_t **typ) { ncx_import_t *imp; ncx_node_t dtyp; status_t res; #ifdef DEBUG if (!pcb || !tkc || !mod || !prefix || !name || !typ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif *typ = NULL; imp = ncx_find_pre_import(mod, prefix); if (!imp) { res = ERR_NCX_PREFIX_NOT_FOUND; log_error("\nError: import for prefix '%s' not found", prefix); } else { /* found import OK, look up imported type definition */ dtyp = NCX_NT_TYP; *typ = (typ_template_t *) ncx_locate_modqual_import(pcb, imp, name, &dtyp); if (!*typ) { res = ERR_NCX_DEF_NOT_FOUND; log_error("\nError: typedef definition for '%s:%s' not found" " in module %s", prefix, name, imp->module); } else { return NO_ERR; } } tkc->curerr = tkerr; ncx_print_errormsg(tkc, mod, res); return res; } /* yang_find_imp_typedef */ /******************************************************************** * FUNCTION yang_find_imp_grouping * * Find the specified imported grouping * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * prefix == prefix value to use * name == grouping name to use * tkerr == error record to use in error messages (may be NULL) * grp == address of return grp_template_t pointer * * OUTPUTS: * *grp set to the found grp_template_t, if NO_ERR * * RETURNS: * status of the operation *********************************************************************/ status_t yang_find_imp_grouping (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *prefix, const xmlChar *name, ncx_error_t *tkerr, grp_template_t **grp) { ncx_import_t *imp; ncx_node_t dtyp; status_t res; #ifdef DEBUG if (!pcb || !tkc || !mod || !prefix || !name || !grp) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; *grp = NULL; imp = ncx_find_pre_import(mod, prefix); if (!imp) { res = ERR_NCX_PREFIX_NOT_FOUND; log_error("\nError: import for prefix '%s' not found", prefix); } else { /* found import OK, look up imported type definition */ dtyp = NCX_NT_GRP; *grp = (grp_template_t *) ncx_locate_modqual_import(pcb, imp, name, &dtyp); if (!*grp) { res = ERR_NCX_DEF_NOT_FOUND; log_error("\nError: grouping definition for '%s:%s' not found" " in module %s", prefix, name, imp->module); } else { return NO_ERR; } } tkc->curerr = tkerr; ncx_print_errormsg(tkc, mod, res); return res; } /* yang_find_imp_grouping */ /******************************************************************** * FUNCTION yang_find_imp_extension * * Find the specified imported extension * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * prefix == prefix value to use * name == extension name to use * tkerr == error struct to use in error messages (may be NULL) * ext == address of return ext_template_t pointer * * OUTPUTS: * *ext set to the found ext_template_t, if NO_ERR * * RETURNS: * status of the operation *********************************************************************/ status_t yang_find_imp_extension (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *prefix, const xmlChar *name, ncx_error_t *tkerr, ext_template_t **ext) { ncx_import_t *imp; status_t res; #ifdef DEBUG if (!pcb || !tkc || !mod || !prefix || !name || !ext) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; *ext = NULL; imp = ncx_find_pre_import(mod, prefix); if (imp == NULL) { res = ERR_NCX_PREFIX_NOT_FOUND; log_error("\nError: import for prefix '%s' not found", prefix); } else if (imp->mod == NULL) { res = ncxmod_load_module(imp->module, imp->revision, pcb->savedevQ, &imp->mod); if (res != NO_ERR) { log_error("\nError: failure importing module '%s'", imp->module); } } /* found import OK, look up imported extension definition */ if (imp != NULL && imp->mod != NULL) { *ext = ext_find_extension(imp->mod, name); if (*ext == NULL) { res = ERR_NCX_DEF_NOT_FOUND; log_error("\nError: extension definition for '%s:%s' not found" " in module %s", prefix, name, imp->module); } else { return NO_ERR; } } tkc->curerr = tkerr; ncx_print_errormsg(tkc, mod, res); return res; } /* yang_find_imp_extension */ /******************************************************************** * FUNCTION yang_find_imp_feature * * Find the specified imported feature * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * prefix == prefix value to use * name == feature name to use * tkerr == error struct to use in error messages (may be NULL) * feature == address of return ncx_feature_t pointer * * OUTPUTS: * *feature set to the found ncx_feature_t, if NO_ERR * * RETURNS: * status of the operation *********************************************************************/ status_t yang_find_imp_feature (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *prefix, const xmlChar *name, ncx_error_t *tkerr, ncx_feature_t **feature) { ncx_import_t *imp; status_t res; #ifdef DEBUG if (!pcb || !tkc || !mod || !prefix || !name || !feature) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; *feature = NULL; imp = ncx_find_pre_import(mod, prefix); if (imp == NULL) { res = ERR_NCX_PREFIX_NOT_FOUND; log_error("\nError: import for prefix '%s' not found", prefix); } else if (imp->mod == NULL) { res = ncxmod_load_module(imp->module, imp->revision, pcb->savedevQ, &imp->mod); if (imp->mod == NULL) { log_error("\nError: failure importing module '%s'", imp->module); res = ERR_NCX_DEF_NOT_FOUND; } } /* found import OK, look up imported extension definition */ if (imp != NULL && imp->mod != NULL) { *feature = ncx_find_feature(imp->mod, name); if (*feature == NULL) { res = ERR_NCX_DEF_NOT_FOUND; log_error("\nError: feature definition for '%s:%s' not found" " in module %s", prefix, name, imp->module); } else { return NO_ERR; } } set_tkc_error(tkc, mod, tkerr, res); return res; } /* yang_find_imp_feature */ /******************************************************************** * FUNCTION yang_find_imp_identity * * Find the specified imported identity * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * prefix == prefix value to use * name == feature name to use * tkerr == error struct to use in error messages (may be NULL) * identity == address of return ncx_identity_t pointer * * OUTPUTS: * *identity set to the found ncx_identity_t, if NO_ERR * * RETURNS: * status of the operation *********************************************************************/ status_t yang_find_imp_identity (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *prefix, const xmlChar *name, ncx_error_t *tkerr, ncx_identity_t **identity) { ncx_import_t *imp; status_t res; #ifdef DEBUG if (!pcb || !tkc || !mod || !prefix || !name || !identity) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; *identity = NULL; imp = ncx_find_pre_import(mod, prefix); if (imp == NULL) { res = ERR_NCX_PREFIX_NOT_FOUND; log_error("\nError: import for prefix '%s' not found", prefix); } else if (imp->mod == NULL) { res = ncxmod_load_module(imp->module, imp->revision, pcb->savedevQ, &imp->mod); if (imp->mod == NULL) { log_error("\nError: failure importing module '%s'", imp->module); res = ERR_NCX_DEF_NOT_FOUND; } } if (imp != NULL && imp->mod != NULL) { /* found import OK, look up imported extension definition */ *identity = ncx_find_identity(imp->mod, name, FALSE); if (*identity == NULL) { res = ERR_NCX_DEF_NOT_FOUND; log_error("\nError: identity definition for '%s:%s' not found" " in module %s", prefix, name, imp->module); } else { return NO_ERR; } } tkc->curerr = tkerr; ncx_print_errormsg(tkc, mod, res); return res; } /* yang_find_imp_identity */ /******************************************************************** * FUNCTION yang_check_obj_used * * generate warnings if local typedefs/groupings not used * Check if the local typedefs and groupings are actually used * Generate warnings if not used * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain in progress * mod == module in progress * typeQ == typedef Q to check * grpQ == pgrouping Q to check * *********************************************************************/ void yang_check_obj_used (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *typeQ, dlq_hdr_t *grpQ) { typ_template_t *testtyp; grp_template_t *testgrp; #ifdef DEBUG if (!tkc || !mod || !typeQ || !grpQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif for (testtyp = (typ_template_t *)dlq_firstEntry(typeQ); testtyp != NULL; testtyp = (typ_template_t *)dlq_nextEntry(testtyp)) { if (!testtyp->used) { if (ncx_warning_enabled(ERR_NCX_TYPDEF_NOT_USED)) { log_warn("\nWarning: Local typedef '%s' not used", testtyp->name); tkc->curerr = &testtyp->tkerr; ncx_print_errormsg(tkc, mod, ERR_NCX_TYPDEF_NOT_USED); } else { ncx_inc_warnings(mod); } } } for (testgrp = (grp_template_t *)dlq_firstEntry(grpQ); testgrp != NULL; testgrp = (grp_template_t *)dlq_nextEntry(testgrp)) { if (!testgrp->used) { if (ncx_warning_enabled(ERR_NCX_GRPDEF_NOT_USED)) { log_warn("\nWarning: Local grouping '%s' not used", testgrp->name); tkc->curerr = &testgrp->tkerr; ncx_print_errormsg(tkc, mod, ERR_NCX_GRPDEF_NOT_USED); } else { ncx_inc_warnings(mod); } } } } /* yang_check_obj_used */ /******************************************************************** * FUNCTION yang_check_imports_used * * generate warnings if imports not used * Check if the imports statements are actually used * Check if the import is newer than the importing module * Generate warnings if so * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain in progress * mod == module in progress * *********************************************************************/ void yang_check_imports_used (tk_chain_t *tkc, ncx_module_t *mod) { ncx_import_t *testimp; ncx_module_t *impmod; int ret; #ifdef DEBUG if (!tkc || !mod) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif impmod = NULL; for (testimp = (ncx_import_t *)dlq_firstEntry(&mod->importQ); testimp != NULL; testimp = (ncx_import_t *)dlq_nextEntry(testimp)) { if (!testimp->used) { if (ncx_warning_enabled(ERR_NCX_IMPORT_NOT_USED)) { log_warn("\nWarning: Module '%s' not used", testimp->module); tkc->curerr = &testimp->tkerr; ncx_print_errormsg(tkc, mod, ERR_NCX_IMPORT_NOT_USED); } else { ncx_inc_warnings(mod); } } /* find the imported module */ if (testimp->mod == NULL) { impmod = ncx_find_module(testimp->module, testimp->revision); /* save the import back-ptr since it is not already set */ testimp->mod = impmod; } /* check if the import is newer than this file, * skip yuma-netconf hack */ if (!testimp->force_yuma_nc && impmod && impmod->version && mod->version) { ret = yang_compare_revision_dates(impmod->version, mod->version); if (ret > 0 && LOGDEBUG2) { log_debug2("\nNote: imported module '%s' (%s)" " is newer than '%s' (%s)", impmod->name, (impmod->version) ? impmod->version : EMPTY_STRING, mod->name, (mod->version) ? mod->version : EMPTY_STRING); } } } } /* yang_check_imports_used */ /******************************************************************** * FUNCTION yang_new_node * * Create a new YANG parser node * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ yang_node_t * yang_new_node (void) { yang_node_t *node; node = m__getObj(yang_node_t); if (!node) { return NULL; } #ifdef YANG_DEBUG new_node_cnt++; if (LOGDEBUG4) { log_debug4("\nyang_new_node: %p (%u)", node, new_node_cnt); } #endif memset(node, 0x0, sizeof(yang_node_t)); return node; } /* yang_new_node */ /******************************************************************** * FUNCTION yang_free_node * * Delete a YANG parser node * * INPUTS: * node == parser node to delete * *********************************************************************/ void yang_free_node (yang_node_t *node) { #ifdef DEBUG if (!node) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif #ifdef YANG_DEBUG free_node_cnt++; if (LOGDEBUG4) { log_debug4("\nyang_free_node: %p (%u)", node, free_node_cnt); if (node->submod && node->submod->name) { log_debug4(" %s", node->submod->name); } } #endif if (node->submod) { ncx_free_module(node->submod); } if (node->failed) { m__free(node->failed); } if (node->failedrev) { m__free(node->failedrev); } m__free(node); } /* yang_free_node */ /******************************************************************** * FUNCTION yang_clean_nodeQ * * Delete all the node entries in a YANG parser node Q * * INPUTS: * que == Q of yang_node_t to clean * *********************************************************************/ void yang_clean_nodeQ (dlq_hdr_t *que) { yang_node_t *node; #ifdef DEBUG if (!que) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (!dlq_empty(que)) { node = (yang_node_t *)dlq_deque(que); yang_free_node(node); } } /* yang_clean_nodeQ */ /******************************************************************** * FUNCTION yang_find_node * * Find a YANG parser node in the specified Q * * INPUTS: * que == Q of yang_node_t to check * name == module name to find * revision == module revision date (may be NULL) * * RETURNS: * pointer to found node, NULL if not found *********************************************************************/ yang_node_t * yang_find_node (const dlq_hdr_t *que, const xmlChar *name, const xmlChar *revision) { yang_node_t *node; #ifdef DEBUG if (!que || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (node = (yang_node_t *)dlq_firstEntry(que); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (!xml_strcmp(node->name, name)) { if (!yang_compare_revision_dates(node->revision, revision)) { return node; } } } return NULL; } /* yang_find_node */ /******************************************************************** * FUNCTION yang_dump_nodeQ * * log_debug output for contents of the specified nodeQ * * INPUTS: * que == Q of yang_node_t to check * name == Q name (may be NULL) * * RETURNS: * pointer to found node, NULL if not found *********************************************************************/ void yang_dump_nodeQ (dlq_hdr_t *que, const char *name) { yang_node_t *node; boolean anyout; #ifdef DEBUG if (!que) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (!LOGDEBUG3) { return; } anyout = FALSE; if (name) { anyout = TRUE; log_debug3("\n%s Q:", name); } for (node = (yang_node_t *)dlq_firstEntry(que); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { anyout = TRUE; log_debug3("\nNode %s ", node->name); if (node->res != NO_ERR) { log_debug3("res: %s ", get_error_string(node->res)); } if (node->mod) { log_debug3("%smod:%s", node->mod->ismod ? "" : "sub", node->mod->name); } } if (anyout) { log_debug3("\n"); } } /* yang_dump_nodeQ */ /******************************************************************** * FUNCTION yang_new_pcb * * Create a new YANG parser control block * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ yang_pcb_t * yang_new_pcb (void) { yang_pcb_t *pcb; pcb = m__getObj(yang_pcb_t); if (!pcb) { return NULL; } memset(pcb, 0x0, sizeof(yang_pcb_t)); dlq_createSQue(&pcb->impchainQ); dlq_createSQue(&pcb->allimpQ); dlq_createSQue(&pcb->failedQ); /* needed for init load and imports for yangdump * do not do this for the agent, which will set 'save descr' * mode to FALSE to indicate do not care about module display */ pcb->stmtmode = ncx_save_descr(); return pcb; } /* yang_new_pcb */ /******************************************************************** * FUNCTION yang_free_pcb * * Delete a YANG parser control block * * INPUTS: * pcb == parser control block to delete * *********************************************************************/ void yang_free_pcb (yang_pcb_t *pcb) { #ifdef DEBUG if (!pcb) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (pcb->top && !pcb->topfound) { if (pcb->top->ismod) { if (!pcb->topadded) { if (pcb->searchmode || pcb->keepmode) { /* need to get rid of the module, it is not wanted */ ncx_free_module(pcb->top); } } } else { ncx_free_module(pcb->top); } } yang_clean_import_ptrQ(&pcb->allimpQ); if (pcb->tkc) { tk_free_chain(pcb->tkc); } yang_clean_nodeQ(&pcb->impchainQ); yang_clean_nodeQ(&pcb->failedQ); m__free(pcb); } /* yang_free_pcb */ /******************************************************************** * FUNCTION yang_new_typ_stmt * * Create a new YANG stmt node for a typedef * * INPUTS: * typ == type template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ yang_stmt_t * yang_new_typ_stmt (typ_template_t *typ) { yang_stmt_t *stmt; #ifdef DEBUG if (!typ) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif stmt = m__getObj(yang_stmt_t); if (!stmt) { return NULL; } memset(stmt, 0x0, sizeof(yang_stmt_t)); stmt->stmttype = YANG_ST_TYPEDEF; stmt->s.typ = typ; return stmt; } /* yang_new_typ_stmt */ /******************************************************************** * FUNCTION yang_new_grp_stmt * * Create a new YANG stmt node for a grouping * * INPUTS: * grp == grouping template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ yang_stmt_t * yang_new_grp_stmt (grp_template_t *grp) { yang_stmt_t *stmt; #ifdef DEBUG if (!grp) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif stmt = m__getObj(yang_stmt_t); if (!stmt) { return NULL; } memset(stmt, 0x0, sizeof(yang_stmt_t)); stmt->stmttype = YANG_ST_GROUPING; stmt->s.grp = grp; return stmt; } /* yang_new_grp_stmt */ /******************************************************************** * FUNCTION yang_new_ext_stmt * * Create a new YANG stmt node for an extension * * INPUTS: * ext == extension template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ yang_stmt_t * yang_new_ext_stmt (ext_template_t *ext) { yang_stmt_t *stmt; #ifdef DEBUG if (!ext) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif stmt = m__getObj(yang_stmt_t); if (!stmt) { return NULL; } memset(stmt, 0x0, sizeof(yang_stmt_t)); stmt->stmttype = YANG_ST_EXTENSION; stmt->s.ext = ext; return stmt; } /* yang_new_ext_stmt */ /******************************************************************** * FUNCTION yang_new_obj_stmt * * Create a new YANG stmt node for an object * * INPUTS: * obj == object template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ yang_stmt_t * yang_new_obj_stmt (obj_template_t *obj) { yang_stmt_t *stmt; #ifdef DEBUG if (!obj) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif stmt = m__getObj(yang_stmt_t); if (!stmt) { return NULL; } memset(stmt, 0x0, sizeof(yang_stmt_t)); stmt->stmttype = YANG_ST_OBJECT; stmt->s.obj = obj; return stmt; } /* yang_new_obj_stmt */ /******************************************************************** * FUNCTION yang_new_id_stmt * * Create a new YANG stmt node for an identity * * INPUTS: * identity == identity template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ yang_stmt_t * yang_new_id_stmt (ncx_identity_t *identity) { yang_stmt_t *stmt; #ifdef DEBUG if (!identity) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif stmt = m__getObj(yang_stmt_t); if (!stmt) { return NULL; } memset(stmt, 0x0, sizeof(yang_stmt_t)); stmt->stmttype = YANG_ST_IDENTITY; stmt->s.identity = identity; return stmt; } /* yang_new_id_stmt */ /******************************************************************** * FUNCTION yang_new_feature_stmt * * Create a new YANG stmt node for a feature definition * * INPUTS: * feature == feature template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ yang_stmt_t * yang_new_feature_stmt (ncx_feature_t *feature) { yang_stmt_t *stmt; #ifdef DEBUG if (!feature) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif stmt = m__getObj(yang_stmt_t); if (!stmt) { return NULL; } memset(stmt, 0x0, sizeof(yang_stmt_t)); stmt->stmttype = YANG_ST_FEATURE; stmt->s.feature = feature; return stmt; } /* yang_new_feature_stmt */ /******************************************************************** * FUNCTION yang_new_deviation_stmt * * Create a new YANG stmt node for a deviation definition * * INPUTS: * deviation == deviation template to use for new statement struct * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ yang_stmt_t * yang_new_deviation_stmt (obj_deviation_t *deviation) { yang_stmt_t *stmt; #ifdef DEBUG if (!deviation) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif stmt = m__getObj(yang_stmt_t); if (!stmt) { return NULL; } memset(stmt, 0x0, sizeof(yang_stmt_t)); stmt->stmttype = YANG_ST_DEVIATION; stmt->s.deviation = deviation; return stmt; } /* yang_new_deviation_stmt */ /******************************************************************** * FUNCTION yang_free_stmt * * Delete a YANG statement node * * INPUTS: * stmt == yang_stmt_t node to delete * *********************************************************************/ void yang_free_stmt (yang_stmt_t *stmt) { #ifdef DEBUG if (!stmt) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif m__free(stmt); } /* yang_free_stmt */ /******************************************************************** * FUNCTION yang_clean_stmtQ * * Delete a Q of YANG statement node * * INPUTS: * que == Q of yang_stmt_t node to delete * *********************************************************************/ void yang_clean_stmtQ (dlq_hdr_t *que) { yang_stmt_t *stmt; #ifdef DEBUG if (!que) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (!dlq_empty(que)) { stmt = (yang_stmt_t *)dlq_deque(que); yang_free_stmt(stmt); } } /* yang_clean_stmtQ */ /******************************************************************** * FUNCTION yang_validate_date_string * * Validate a YANG date string for a revision entry * * Expected format is: * * YYYY-MM-DD * * Error messages are printed by this function * * INPUTS: * tkc == token chain in progress * mod == module in progress * tkerr == error struct to use (may be NULL to use tkc->cur) * datestr == string to validate * * RETURNS: * status *********************************************************************/ status_t yang_validate_date_string (tk_chain_t *tkc, ncx_module_t *mod, ncx_error_t *tkerr, const xmlChar *datestr) { #define DATE_STR_LEN 10 status_t res, retres; uint32 len, i; int ret; ncx_num_t num; xmlChar numbuff[NCX_MAX_NUMLEN]; xmlChar curdate[16]; #ifdef DEBUG if (!tkc || !mod || !datestr) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif retres = NO_ERR; len = xml_strlen(datestr); tstamp_date(curdate); /* validate the length */ if (len != DATE_STR_LEN) { retres = ERR_NCX_INVALID_VALUE; log_error("\nError: Invalid date string length (%u)", len); tkc->curerr = tkerr; ncx_print_errormsg(tkc, mod, retres); } /* validate the year field */ if (retres == NO_ERR) { for (i=0; i<4; i++) { numbuff[i] = datestr[i]; } numbuff[4] = 0; res = ncx_decode_num(numbuff, NCX_BT_UINT32, &num); if (res != NO_ERR) { retres = ERR_NCX_INVALID_VALUE; log_error("\nError: Invalid year string (%s)", numbuff); ncx_print_errormsg(tkc, mod, retres); } else if (num.u < 1970) { if (ncx_warning_enabled(ERR_NCX_DATE_PAST)) { log_warn("\nWarning: Invalid revision year (%s)", numbuff); tkc->curerr = tkerr; ncx_print_errormsg(tkc, mod, ERR_NCX_DATE_PAST); } else { ncx_inc_warnings(mod); } } } /* validate the first separator */ if (retres == NO_ERR) { if (datestr[4] != '-') { retres = ERR_NCX_INVALID_VALUE; log_error("\nError: Invalid date string separator (%c)", datestr[4]); tkc->curerr = tkerr; ncx_print_errormsg(tkc, mod, retres); } } /* validate the month field */ if (retres == NO_ERR) { numbuff[0] = datestr[5]; numbuff[1] = datestr[6]; numbuff[2] = 0; res = ncx_convert_num(numbuff, NCX_NF_DEC, NCX_BT_UINT32, &num); if (res != NO_ERR) { retres = ERR_NCX_INVALID_VALUE; log_error("\nError: Invalid month string (%s)", numbuff); ncx_print_errormsg(tkc, mod, retres); } else if (num.u < 1 || num.u > 12) { retres = ERR_NCX_INVALID_VALUE; log_error("\nError: Invalid month string (%s)", numbuff); tkc->curerr = tkerr; ncx_print_errormsg(tkc, mod, retres); } } /* validate the last separator */ if (retres == NO_ERR) { if (datestr[7] != '-') { retres = ERR_NCX_INVALID_VALUE; log_error("\nError: Invalid date string separator (%c)", datestr[7]); tkc->curerr = tkerr; ncx_print_errormsg(tkc, mod, retres); } } /* validate the day field */ if (retres == NO_ERR) { numbuff[0] = datestr[8]; numbuff[1] = datestr[9]; numbuff[2] = 0; res = ncx_convert_num(numbuff, NCX_NF_DEC, NCX_BT_UINT32, &num); if (res != NO_ERR) { retres = ERR_NCX_INVALID_VALUE; log_error("\nError: Invalid day string (%s)", numbuff); tkc->curerr = tkerr; ncx_print_errormsg(tkc, mod, retres); } else if (num.u < 1 || num.u > 31) { retres = ERR_NCX_INVALID_VALUE; log_error("\nError: Invalid day string (%s)", numbuff); tkc->curerr = tkerr; ncx_print_errormsg(tkc, mod, retres); } } /* check the date against the future */ if (retres == NO_ERR) { ret = xml_strcmp(curdate, datestr); if (ret < 0) { if (ncx_warning_enabled(ERR_NCX_DATE_FUTURE)) { log_warn("\nWarning: Revision date in the future (%s)", datestr); tkc->curerr = tkerr; ncx_print_errormsg(tkc, mod, ERR_NCX_DATE_FUTURE); } else { ncx_inc_warnings(mod); } } } return retres; } /* yang_validate_date_string */ /******************************************************************** * FUNCTION yang_skip_statement * * Skip past the current invalid statement, starting at * an invalid keyword * * INPUTS: * tkc == token chain in progress * mod == module in progress *********************************************************************/ void yang_skip_statement (tk_chain_t *tkc, ncx_module_t *mod) { uint32 bracecnt; status_t res; #ifdef DEBUG if (!tkc || !tkc->cur) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif res = NO_ERR; bracecnt = 0; while (res==NO_ERR) { /* current should be value string or stmtend */ switch (TK_CUR_TYP(tkc)) { case TK_TT_NONE: return; case TK_TT_SEMICOL: if (!bracecnt) { return; } break; case TK_TT_LBRACE: bracecnt++; break; case TK_TT_RBRACE: if (bracecnt) { bracecnt--; if (!bracecnt) { return; } } else { return; } default: ; } res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); } } } /* yang_skip_statement */ /******************************************************************** * FUNCTION yang_top_keyword * * Check if the string is a top-level YANG keyword * * INPUTS: * keyword == string to check * * RETURNS: * TRUE if a top-level YANG keyword, FALSE otherwise *********************************************************************/ boolean yang_top_keyword (const xmlChar *keyword) { const xmlChar *topkw; uint32 i; int ret; #ifdef DEBUG if (!keyword) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif for (i=0;;i++) { topkw = top_keywords[i]; if (!topkw) { return FALSE; } ret = xml_strcmp(topkw, keyword); if (ret == 0) { return TRUE; } else if (ret > 0) { return FALSE; } } /*NOTREACHED*/ } /* yang_top_keyword */ /******************************************************************** * FUNCTION yang_new_import_ptr * * Create a new YANG import pointer node * * INPUTS: * modname = module name to set * modprefix = module prefix to set * revision = revision date to set * * RETURNS: * pointer to new and initialized struct, NULL if memory error *********************************************************************/ yang_import_ptr_t * yang_new_import_ptr (const xmlChar *modname, const xmlChar *modprefix, const xmlChar *revision) { yang_import_ptr_t *impptr; impptr = m__getObj(yang_import_ptr_t); if (!impptr) { return NULL; } memset(impptr, 0x0, sizeof(yang_import_ptr_t)); if (modname) { impptr->modname = xml_strdup(modname); if (!impptr->modname) { yang_free_import_ptr(impptr); return NULL; } } if (modprefix) { impptr->modprefix = xml_strdup(modprefix); if (!impptr->modprefix) { yang_free_import_ptr(impptr); return NULL; } } if (revision) { impptr->revision = xml_strdup(revision); if (!impptr->revision) { yang_free_import_ptr(impptr); return NULL; } } return impptr; } /* yang_new_import_ptr */ /******************************************************************** * FUNCTION yang_free_impptr_ptr * * Delete a YANG import pointer node * * INPUTS: * impptr == import pointer node to delete * *********************************************************************/ void yang_free_import_ptr (yang_import_ptr_t *impptr) { #ifdef DEBUG if (!impptr) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (impptr->modname) { m__free(impptr->modname); } if (impptr->modprefix) { m__free(impptr->modprefix); } if (impptr->revision) { m__free(impptr->revision); } m__free(impptr); } /* yang_free_import_ptr */ /******************************************************************** * FUNCTION yang_clean_import_ptrQ * * Delete all the node entries in a YANG import pointer node Q * * INPUTS: * que == Q of yang_import_ptr_t to clean * *********************************************************************/ void yang_clean_import_ptrQ (dlq_hdr_t *que) { yang_import_ptr_t *impptr; #ifdef DEBUG if (!que) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (!dlq_empty(que)) { impptr = (yang_import_ptr_t *)dlq_deque(que); yang_free_import_ptr(impptr); } } /* yang_clean_import_ptrQ */ /******************************************************************** * FUNCTION yang_find_import_ptr * * Find a YANG import pointer node in the specified Q * * INPUTS: * que == Q of yang_import_ptr_t to check * name == module name to find * * RETURNS: * pointer to found node, NULL if not found *********************************************************************/ yang_import_ptr_t * yang_find_import_ptr (dlq_hdr_t *que, const xmlChar *name) { yang_import_ptr_t *impptr; #ifdef DEBUG if (!que || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (impptr = (yang_import_ptr_t *)dlq_firstEntry(que); impptr != NULL; impptr = (yang_import_ptr_t *)dlq_nextEntry(impptr)) { if (!xml_strcmp(impptr->modname, name)) { return impptr; } } return NULL; } /* yang_find_import_ptr */ /******************************************************************** * FUNCTION yang_compare_revision_dates * * Compare 2 revision strings, which either may be NULL * * INPUTS: * revstring1 == revision string 1 (may be NULL) * revstring2 == revision string 2 (may be NULL) * * RETURNS: * -1 if revision 1 < revision 2 * 0 of they are the same * +1 if revision1 > revision 2 *********************************************************************/ int32 yang_compare_revision_dates (const xmlChar *revstring1, const xmlChar *revstring2) { /* if either revision is NULL then call it a match * else actually compare the revision date strings */ if (!revstring1 || !revstring2) { return 0; } else { return xml_strcmp(revstring1, revstring2); } } /* yang_compare_revision_dates */ /******************************************************************** * FUNCTION yang_make_filename * * Malloc and construct a YANG filename * * INPUTS: * modname == [sub]module name * revision == [sub]module revision date (may be NULL) * isyang == TRUE for YANG extension * FALSE for YIN extension * * RETURNS: * malloced and filled in string buffer with filename * NULL if any error *********************************************************************/ xmlChar * yang_make_filename (const xmlChar *modname, const xmlChar *revision, boolean isyang) { xmlChar *buff, *p; uint32 mlen, rlen, slen; #ifdef DEBUG if (!modname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif mlen = xml_strlen(modname); rlen = (revision) ? xml_strlen(revision) : 0; if (rlen) { rlen++; /* file sep char */ } slen = (isyang) ? xml_strlen(YANG_SUFFIX) : xml_strlen(YIN_SUFFIX); buff = m__getMem(mlen + rlen + slen + 2); if (!buff) { return NULL; } p = buff; p += xml_strcpy(p, modname); if (revision && *revision) { *p++ = YANG_FILE_SEPCHAR; p += xml_strcpy(p, revision); } *p++ = '.'; if (isyang) { xml_strcpy(p, YANG_SUFFIX); } else { xml_strcpy(p, YIN_SUFFIX); } return buff; } /* yang_make_filename */ /******************************************************************** * FUNCTION yang_copy_filename * * Construct a YANG filename into a provided buffer * * INPUTS: * modname == [sub]module name * revision == [sub]module revision date (may be NULL) * buffer == buffer to copy filename into * bufflen == number of bytes available in buffer * isyang == TRUE for YANG extension * FALSE for YIN extension * * RETURNS: * malloced and filled in string buffer with filename * NULL if any error *********************************************************************/ status_t yang_copy_filename (const xmlChar *modname, const xmlChar *revision, xmlChar *buffer, uint32 bufflen, boolean isyang) { xmlChar *p; uint32 mlen, rlen, slen; #ifdef DEBUG if (!modname) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif mlen = xml_strlen(modname); rlen = (revision) ? xml_strlen(revision) : 0; if (rlen) { rlen++; /* file sep char */ } slen = (isyang) ? xml_strlen(YANG_SUFFIX) : xml_strlen(YIN_SUFFIX); if ((mlen + rlen + slen + 2) < bufflen) { p = buffer; p += xml_strcpy(p, modname); if (revision && *revision) { *p++ = YANG_FILE_SEPCHAR; p += xml_strcpy(p, revision); } *p++ = '.'; if (isyang) { xml_strcpy(p, YANG_SUFFIX); } else { xml_strcpy(p, YIN_SUFFIX); } return NO_ERR; } else { return ERR_BUFF_OVFL; } } /* yang_copy_filename */ /******************************************************************** * FUNCTION yang_split_filename * * Split a module parameter into its filename components * * INPUTS: * filename == filename string * modnamelen == address of return modname length * * OUTPUTS: * *modnamelen == module name length * * RETURNS: * TRUE if module@revision form was found * FALSE if not, and ignore the output because a * different form of the module parameter was used *********************************************************************/ boolean yang_split_filename (const xmlChar *filename, uint32 *modnamelen) { const xmlChar *str, *sepchar; uint32 len, yangslen, yinslen; #ifdef DEBUG if (filename == NULL || modnamelen == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif *modnamelen = 0; str = filename; sepchar = NULL; /* check if special filespec first chars are present */ if (*str == '$' || *str == '~') { return FALSE; } /* check if the revision separator is present * or if any path sep chars are present */ while (*str) { if (*str == YANG_FILE_SEPCHAR) { sepchar = str; } else if (*str == NCXMOD_PSCHAR) { return FALSE; } str++; } if (sepchar == NULL) { return FALSE; } /* check if a file extension is present */ len = (uint32)(str - filename); yangslen = xml_strlen(YANG_SUFFIX); yinslen = xml_strlen(YIN_SUFFIX); if (len > yangslen + 1) { if ((*(str - yangslen - 1) == '.') && (!xml_strcmp(str - yangslen, YANG_SUFFIX))) { return FALSE; } } else if (len > yinslen + 1) { if ((*(str - yinslen - 1) == '.') && (!xml_strcmp(str - yinslen, YIN_SUFFIX))) { return FALSE; } } *modnamelen = (uint32)(sepchar - filename); return TRUE; } /* yang_split_filename */ /******************************************************************** * FUNCTION yang_fileext_is_yang * * Check if the filespec ends with the .yang extension * * INPUTS: * filename == filename string * * RETURNS: * TRUE if .yang file extension found * FALSE if not *********************************************************************/ boolean yang_fileext_is_yang (const xmlChar *filename) { uint32 len; #ifdef DEBUG if (filename == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif len = xml_strlen(filename); if (len < 6) { return FALSE; } if (filename[len - 5] != '.') { return FALSE; } return !xml_strcmp(&filename[len - 4], YANG_SUFFIX); } /* yang_fileext_is_yang */ /******************************************************************** * FUNCTION yang_fileext_is_yin * * Check if the filespec ends with the .yin extension * * INPUTS: * filename == filename string * * RETURNS: * TRUE if .yin file extension found * FALSE if not *********************************************************************/ boolean yang_fileext_is_yin (const xmlChar *filename) { uint32 len; #ifdef DEBUG if (filename == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif len = xml_strlen(filename); if (len < 5) { return FALSE; } if (filename[len - 4] != '.') { return FALSE; } return !xml_strcmp(&filename[len - 3], YIN_SUFFIX); } /* yang_fileext_is_yin */ /******************************************************************** * FUNCTION yang_fileext_is_xml * * Check if the filespec ends with the .xml extension * * INPUTS: * filename == filename string * * RETURNS: * TRUE if .xml file extension found * FALSE if not *********************************************************************/ boolean yang_fileext_is_xml (const xmlChar *filename) { uint32 len; #ifdef DEBUG if (filename == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif len = xml_strlen(filename); if (len < 5) { return FALSE; } if (filename[len - 4] != '.') { return FALSE; } return !xml_strcmp(&filename[len - 3], (const xmlChar *)"xml"); } /* yang_fileext_is_xml */ /******************************************************************** * FUNCTION yang_final_memcheck * * Check the node malloc and free counts *********************************************************************/ void yang_final_memcheck (void) { #ifdef YANG_DEBUG if (new_node_cnt != free_node_cnt) { log_error("\nError: YANG node count: new(%u) free(%u)", new_node_cnt, free_node_cnt); } #endif } /* yang_final_memcheck */ /* END file yang.c */ yuma123_2.14/netconf/src/ncx/log.h0000664000175000017500000003517014770023131017076 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_log #define _H_log /* FILE: log.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Logging manager ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 08-jan-06 abb begun */ #include #include "procdefs.h" #include "status.h" #include "ncxtypes.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* macros to check the debug level */ #define LOGERROR (log_get_debug_level() >= LOG_DEBUG_ERROR) #define LOGWARN (log_get_debug_level() >= LOG_DEBUG_WARN) #define LOGINFO (log_get_debug_level() >= LOG_DEBUG_INFO) #define LOGDEBUG (log_get_debug_level() >= LOG_DEBUG_DEBUG) #define LOGDEBUG2 (log_get_debug_level() >= LOG_DEBUG_DEBUG2) #define LOGDEBUG3 (log_get_debug_level() >= LOG_DEBUG_DEBUG3) #define LOGDEBUG4 (log_get_debug_level() >= LOG_DEBUG_DEBUG4) #define LOG_DEBUG_STR_OFF (const xmlChar *)"off" #define LOG_DEBUG_STR_ERROR (const xmlChar *)"error" #define LOG_DEBUG_STR_WARN (const xmlChar *)"warn" #define LOG_DEBUG_STR_INFO (const xmlChar *)"info" #define LOG_DEBUG_STR_DEBUG (const xmlChar *)"debug" #define LOG_DEBUG_STR_DEBUG2 (const xmlChar *)"debug2" #define LOG_DEBUG_STR_DEBUG3 (const xmlChar *)"debug3" #define LOG_DEBUG_STR_DEBUG4 (const xmlChar *)"debug4" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* The debug level enumerations used in util/log.c */ typedef enum log_debug_t_ { LOG_DEBUG_NONE, /* value not set or error */ LOG_DEBUG_OFF, /* logging turned off */ LOG_DEBUG_ERROR, /* fatal + internal errors only */ LOG_DEBUG_WARN, /* all errors + warnings */ LOG_DEBUG_INFO, /* all previous + user info trace */ LOG_DEBUG_DEBUG, /* debug level 1 */ LOG_DEBUG_DEBUG2, /* debug level 2 */ LOG_DEBUG_DEBUG3, /* debug level 3 */ LOG_DEBUG_DEBUG4 /* debug level 3 */ } log_debug_t; /* logging function template to switch between * log_stdout and log_write */ typedef void (*logfn_t) (const char *fstr, ...); /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION log_open * * Open a logfile for writing * DO NOT use this function to send log entries to STDOUT * Leave the logfile NULL instead. * * INPUTS: * fname == full filespec string for logfile * append == TRUE if the log should be appended * == FALSE if it should be rewriten * tstamps == TRUE if the datetime stamp should be generated * at log-open and log-close time * == FALSE if no open and close timestamps should be generated * * RETURNS: * status *********************************************************************/ extern status_t log_open (const char *fname, boolean append, boolean tstamps); /******************************************************************** * FUNCTION log_close * * Close the logfile * * RETURNS: * none *********************************************************************/ extern void log_close (void); /******************************************************************** * FUNCTION log_audit_open * * Open the audit logfile for writing * DO NOT use this function to send log entries to STDOUT * Leave the audit_logfile NULL instead. * * INPUTS: * fname == full filespec string for audit logfile * append == TRUE if the log should be appended * == FALSE if it should be rewriten * tstamps == TRUE if the datetime stamp should be generated * at log-open and log-close time * == FALSE if no open and close timestamps should be generated * * RETURNS: * status *********************************************************************/ extern status_t log_audit_open (const char *fname, boolean append, boolean tstamps); /******************************************************************** * FUNCTION log_audit_close * * Close the audit_logfile * * RETURNS: * none *********************************************************************/ extern void log_audit_close (void); /******************************************************************** * FUNCTION log_audit_is_open * * Check if the audit log is open * * RETURNS: * TRUE if audit log is open; FALSE if not *********************************************************************/ extern boolean log_audit_is_open (void); /******************************************************************** * FUNCTION log_alt_open * * Open an alternate logfile for writing * DO NOT use this function to send log entries to STDOUT * Leave the logfile NULL instead. * * INPUTS: * fname == full filespec string for logfile * * RETURNS: * status *********************************************************************/ extern status_t log_alt_open (const char *fname); /******************************************************************** * FUNCTION log_alt_close * * Close the alternate logfile * * RETURNS: * none *********************************************************************/ extern void log_alt_close (void); /******************************************************************** * FUNCTION log_stdout * * Write lines of text to STDOUT, even if the logfile * is open, unless the debug mode is set to NONE * to indicate silent batch mode * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ extern void log_stdout (const char *fstr, ...); /******************************************************************** * FUNCTION log_write * * Generate a log entry, regardless of log level * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ extern void log_write (const char *fstr, ...); /******************************************************************** * FUNCTION log_audit_write * * Generate an audit log entry, regardless of log level * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ extern void log_audit_write (const char *fstr, ...); /******************************************************************** * FUNCTION log_alt_write * * Write to the alternate log file * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for fprintf * *********************************************************************/ extern void log_alt_write (const char *fstr, ...); /******************************************************************** * FUNCTION log_alt_indent * * Printf a newline to the alternate logfile, * then the specified number of space chars * * INPUTS: * indentcnt == number of indent chars, -1 == skip everything * *********************************************************************/ extern void log_alt_indent (int32 indentcnt); /******************************************************************** * FUNCTION log_error * * Generate a LOG_DEBUG_ERROR log entry * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ extern void log_error (const char *fstr, ...); /******************************************************************** * FUNCTION log_error * * Generate a LOG_DEBUG_ERROR log entry * * INPUTS: * fstr == format string in printf format * args == any additional arguments for printf * *********************************************************************/ extern void vlog_error (const char *fstr, va_list args ); /******************************************************************** * FUNCTION log_warn * * Generate LOG_DEBUG_WARN log output * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ extern void log_warn (const char *fstr, ...); /******************************************************************** * FUNCTION log_info * * Generate a LOG_DEBUG_INFO log entry * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ extern void log_info (const char *fstr, ...); /******************************************************************** * FUNCTION log_debug * * Generate a LOG_DEBUG_DEBUG log entry * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ extern void log_debug (const char *fstr, ...); /******************************************************************** * FUNCTION log_debug2 * * Generate LOG_DEBUG_DEBUG2 log trace output * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ extern void log_debug2 (const char *fstr, ...); /******************************************************************** * FUNCTION log_debug3 * * Generate LOG_DEBUG_DEBUG3 log trace output * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ extern void log_debug3 (const char *fstr, ...); /******************************************************************** * FUNCTION log_debug4 * * Generate LOG_DEBUG_DEBUG4 log trace output * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ extern void log_debug4 (const char *fstr, ...); /******************************************************************** * FUNCTION log_noop * * Do not generate any log message NO-OP * Used to set logfn_t to no-loggging option * * INPUTS: * fstr == format string in printf format * ... == any additional arguments for printf * *********************************************************************/ extern void log_noop (const char *fstr, ...); /******************************************************************** * FUNCTION log_set_debug_level * * Set the global debug filter threshold level * * INPUTS: * dlevel == desired debug level * *********************************************************************/ extern void log_set_debug_level (log_debug_t dlevel); /******************************************************************** * FUNCTION log_get_debug_level * * Get the global debug filter threshold level * * RETURNS: * the global debug level *********************************************************************/ extern log_debug_t log_get_debug_level (void); /******************************************************************** * FUNCTION log_get_debug_level_enum * * Get the corresponding debug enum for the specified string * * INPUTS: * str == string value to convert * * RETURNS: * the corresponding enum for the specified debug level *********************************************************************/ extern log_debug_t log_get_debug_level_enum (const char *str); /******************************************************************** * FUNCTION log_get_debug_level_string * * Get the corresponding string for the debug enum * * INPUTS: * level == the enum for the specified debug level * * RETURNS: * the string value for this enum *********************************************************************/ extern const xmlChar * log_get_debug_level_string (log_debug_t level); /******************************************************************** * FUNCTION log_is_open * * Check if the logfile is active * * RETURNS: * TRUE if logfile open, FALSE otherwise *********************************************************************/ extern boolean log_is_open (void); /******************************************************************** * FUNCTION log_indent * * Printf a newline, then the specified number of chars * * INPUTS: * indentcnt == number of indent chars, -1 == skip everything * *********************************************************************/ extern void log_indent (int32 indentcnt); /******************************************************************** * FUNCTION log_stdout_indent * * Printf a newline to stdout, then the specified number of chars * * INPUTS: * indentcnt == number of indent chars, -1 == skip everything * *********************************************************************/ extern void log_stdout_indent (int32 indentcnt); /******************************************************************** * FUNCTION log_get_logfile * * Get the open logfile for direct output * Needed by libtecla to write command line history * * RETURNS: * pointer to open FILE if any * NULL if no open logfile *********************************************************************/ extern FILE * log_get_logfile (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_log */ yuma123_2.14/netconf/src/ncx/ncxtypes.h0000664000175000017500000006472514770023131020202 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ncxtypes #define _H_ncxtypes /* FILE: ncxtypes.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Contains NCX typedefs ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 14-nov-05 abb Begun; split from ncx.h 04-feb-06 abb Move base/nc.h constants into this file 10-nov-07 abb Split out from ncxconst.h */ #ifdef LIBXML2_ENABLED #include #include #else typedef unsigned char xmlChar; typedef void* xmlRegexpPtr; typedef void* xmlTextReaderPtr; #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* NCX Access Control 'max-access' enumeration values * Note that access control is applied to the session * and the max-access applies to the user max-access, * not the agent max-access. Only the agent may create * or write an object which is classified as 'read-only'. * * Access is grouped into the following catagories. * * none * read-only * read-write * read-create * * Merge and Replace are handled as follows: * * Requested operation Effective Access * ------------------------------------------------- * merge empty with no-cur-node --> no-op * merge empty with cur-node --> no-op * merge non-empty with no-cur-node --> create * merge non-empty with cur-node --> write * * replace empty into no-cur-node --> no-op * replace empty into cur-node --> delete * replace non-empty into no-cur-node --> create * replace non-empty into cur-node --> write * Create and Delete are grouped together for access control * purposes, and called 'read-create' * * Obviously, the max-access of the ancestor nodes affect any * given nested node, but the enumerations are not purely hierarchical. * * Access Nested Behavior * ------------------------------- * [none] Check the ancestor chain until an explicit value * is found (or the parmset root is reached, where the * default is read-create) * * read-only User may only read this node and all child nodes. * All nested access is also read-only, overriding any * explicit setting of any child nodes * * read-write User may read and write this node and all child nodes, * depending on the max-access of the child node. * Any type of nested access may appear in any child * nodes. A read-write container may have child nodes * which are read-create objects. This implies the agent * must create the object in order for the user to write it. * * read-create User may have unrestricted access to this node and all * child nodes, depending on the max-access of the child * nodes. Any type of nested access may appear in any * child nodes. */ typedef enum ncx_access_t_ { NCX_ACCESS_NONE, /* enum not explicitly set */ NCX_ACCESS_RO, /* read-only */ NCX_ACCESS_RW, /* read-write (create/delete not allowed) */ NCX_ACCESS_RC /* read-create (all access) */ } ncx_access_t; /* default max-access is all */ #define NCX_DEF_ACCESS NCX_ACCESS_RC /* NCX Persistence Control * * Enum mapping of 'data-class' field in the 'parm' and 'type' clauses * * This data type controls NV-storage and whether a node is returned * in the 'get-config' operation and stored in the startup config. * */ typedef enum ncx_data_class_t_ { NCX_DC_NONE, NCX_DC_CONFIG, /* persistent config */ NCX_DC_STATE /* state or statistics */ } ncx_data_class_t; /* enumeration of the built-in NCX types * These types cannot be overridden and cannot be imported */ typedef enum ncx_btype_t_ { NCX_BT_NONE, NCX_BT_ANYDATA, NCX_BT_ANYXML, NCX_BT_BITS, NCX_BT_ENUM, NCX_BT_EMPTY, NCX_BT_BOOLEAN, NCX_BT_INT8, NCX_BT_INT16, NCX_BT_INT32, NCX_BT_INT64, NCX_BT_UINT8, NCX_BT_UINT16, NCX_BT_UINT32, NCX_BT_UINT64, NCX_BT_DECIMAL64, NCX_BT_FLOAT64, /* hidden: just for XPath */ NCX_BT_STRING, NCX_BT_BINARY, NCX_BT_INSTANCE_ID, NCX_BT_UNION, NCX_BT_LEAFREF, NCX_BT_IDREF, NCX_BT_SLIST, /* ncx:xsdlist extension */ NCX_BT_CONTAINER, NCX_BT_CHOICE, NCX_BT_CASE, /* not a real type */ NCX_BT_LIST, NCX_BT_EXTERN, /* not a real type */ NCX_BT_INTERN /* not a real type */ } ncx_btype_t; #define NCX_FIRST_DATATYPE NCX_BT_ANYDATA #define NCX_LAST_DATATYPE NCX_BT_LIST #define NCX_NUM_BASETYPES (NCX_LAST_DATATYPE-NCX_FIRST_DATATYPE+1) /* Enumeration of the basic value type classifications */ typedef enum ncx_tclass_t_ { NCX_CL_NONE, NCX_CL_BASE, /* a built-in base type */ NCX_CL_SIMPLE, /* a restriction of a base type */ NCX_CL_COMPLEX, /* a complex type */ NCX_CL_NAMED, /* a restriction of a named type */ NCX_CL_REF /* internal reference to another type */ } ncx_tclass_t; /* Enumeration of the different types of index components * YANG ONLY SUPPORTS NCX_IT_LOCAL */ typedef enum ncx_indextyp_t_ { NCX_IT_NONE, NCX_IT_INLINE, /* index simple type declared inline */ NCX_IT_NAMED, /* index named type declared inline */ NCX_IT_LOCAL, /* local member within the table */ NCX_IT_SLOCAL, /* scoped local member within the table */ NCX_IT_REMOTE, /* unscoped remote name */ NCX_IT_SREMOTE /* scoped remote name */ } ncx_indextyp_t; /* NCX Internal Node Types * * nodes in the value trees can be different types * These enums are used to generate instance IDs and * classify data passed to the agt_record_*error functions */ typedef enum ncx_node_t_ { NCX_NT_NONE, NCX_NT_TYP, /* typ_template_t */ NCX_NT_GRP, /* grp_template_t */ NCX_NT_VAL, /* val_value_t */ NCX_NT_OBJ, /* obj_template_t */ NCX_NT_ERRINFO, /* ncx_errinfo_t, error only */ NCX_NT_STRING, /* xmlChar *, error only */ NCX_NT_CFG, /* cfg_template_t *, error only */ NCX_NT_INDEX, /* obj_key_t *, error only */ NCX_NT_QNAME, /* xmlns_qname_t *, error only */ NCX_NT_UINT32_PTR, /* session ID, error only */ NCX_NT_IMPORT_CB, /* ncx_import_t */ NCX_NT_INCLUDE_CB, /* ncx_include_t */ NCX_NT_REVISION_CB, /* ncx_revhist_t */ NCX_NT_EXTENSION_CB, /* ext_template_t */ NCX_NT_FEATURE_CB, /* ncx_feature_t */ NCX_NT_IDENTITY_CB, /* ncx_identity_t */ NCX_NT_TYPDEF_CB /* typ_def_t */ } ncx_node_t; /* The instance qualifier types are borrowed from ABNF and RelaxNG */ typedef enum ncx_iqual_t_ { NCX_IQUAL_NONE, /* value not set */ NCX_IQUAL_ONE, /* no iqual == 1 */ NCX_IQUAL_OPT, /* '?' == 0 or 1 */ NCX_IQUAL_1MORE, /* '+' == 1 or more */ NCX_IQUAL_ZMORE /* '*' == 0 or more */ } ncx_iqual_t; /* The merge type for the NETCONF merge operation */ typedef enum ncx_merge_t_ { NCX_MERGE_NONE, /* value not set */ NCX_MERGE_FIRST, NCX_MERGE_LAST, NCX_MERGE_SORT } ncx_merge_t; /* typdef search qualifier list */ typedef enum ncx_squal_t_ { NCX_SQUAL_NONE, NCX_SQUAL_RANGE, NCX_SQUAL_VAL, NCX_SQUAL_META, NCX_SQUAL_APPINFO } ncx_squal_t; /* Enumeration of string restriction types */ typedef enum ncx_strrest_t_ { NCX_SR_NONE, NCX_SR_PATTERN, NCX_SR_ENUM, NCX_SR_BIT } ncx_strrest_t; /* Enumeration of number format types */ typedef enum ncx_numfmt_t_ { NCX_NF_NONE, NCX_NF_OCTAL, NCX_NF_DEC, NCX_NF_HEX, NCX_NF_REAL } ncx_numfmt_t; /* Enumeration of NETCONF protocol layers */ typedef enum ncx_layer_t_ { NCX_LAYER_NONE, NCX_LAYER_TRANSPORT, NCX_LAYER_RPC, NCX_LAYER_OPERATION, NCX_LAYER_CONTENT } ncx_layer_t; /* enum to identify the agent native target */ typedef enum ncx_agttarg_t_ { NCX_AGT_TARG_NONE, NCX_AGT_TARG_CANDIDATE, NCX_AGT_TARG_RUNNING, NCX_AGT_TARG_LOCAL, /* TBD */ NCX_AGT_TARG_REMOTE, /* TBD */ NCX_AGT_TARG_CAND_RUNNING /* TBD */ } ncx_agttarg_t; /* enum to identify the agent native startup mode */ typedef enum ncx_agtstart_t_ { NCX_AGT_START_NONE, NCX_AGT_START_MIRROR, NCX_AGT_START_DISTINCT } ncx_agtstart_t; /* enumeration of the different program shutdown modes */ typedef enum ncx_shutdowntyp_t_ { NCX_SHUT_NONE, NCX_SHUT_RESET, NCX_SHUT_RELOAD, NCX_SHUT_RESTART, NCX_SHUT_EXIT } ncx_shutdowntyp_t; /* hardwire the 3 standard configs */ typedef enum ncx_cfg_t_ { NCX_CFGID_RUNNING, NCX_CFGID_CANDIDATE, NCX_CFGID_STARTUP } ncx_cfg_t; /* instance string format types */ typedef enum ncx_instfmt_t_ { NCX_IFMT_NONE, NCX_IFMT_C, NCX_IFMT_XPATH1, /* single-quote Xpath for filter */ NCX_IFMT_XPATH2, /* double-quote Xpath for error-path */ NCX_IFMT_CLI } ncx_instfmt_t; /* enumeration for different NETCONF message types */ typedef enum ncx_msgtyp_t_ { NCX_MSGTYP_NONE, NCX_MSGTYP_HELLO, NCX_MSGTYP_RPCREQ, NCX_MSGTYP_RPCRPY, NCX_MSGTYP_NOTIF } ncx_msgtyp_t; /* enumeration for different YANG data-def status values */ typedef enum ncx_status_t_ { NCX_STATUS_NONE, NCX_STATUS_CURRENT, NCX_STATUS_DEPRECATED, NCX_STATUS_OBSOLETE } ncx_status_t; /* enumeration for CLI handling of bad input data * used by yangcli, all others use NCX_BAD_DATA_ERROR * * NCX_BAD_DATA_IGNORE to silently accept invalid input values * NCX_BAD_DATA_WARN to warn and accept invalid input values * NCX_BAD_DATA_CHECK to prompt user to keep or re-enter value * NCX_BAD_DATA_ERROR to prompt user to re-enter value */ typedef enum ncx_bad_data_t_ { NCX_BAD_DATA_NONE, NCX_BAD_DATA_IGNORE, NCX_BAD_DATA_WARN, NCX_BAD_DATA_CHECK, NCX_BAD_DATA_ERROR } ncx_bad_data_t; /* enumeration of val_dump_value display modes */ typedef enum ncx_display_mode_t_ { NCX_DISPLAY_MODE_NONE, NCX_DISPLAY_MODE_PLAIN, NCX_DISPLAY_MODE_PREFIX, NCX_DISPLAY_MODE_MODULE, NCX_DISPLAY_MODE_XML, NCX_DISPLAY_MODE_XML_NONS, NCX_DISPLAY_MODE_JSON } ncx_display_mode_t; /* XPath expression axis types */ typedef enum ncx_xpath_axis_t_ { XP_AX_NONE, XP_AX_ANCESTOR, XP_AX_ANCESTOR_OR_SELF, XP_AX_ATTRIBUTE, XP_AX_CHILD, XP_AX_DESCENDANT, XP_AX_DESCENDANT_OR_SELF, XP_AX_FOLLOWING, XP_AX_FOLLOWING_SIBLING, XP_AX_NAMESPACE, XP_AX_PARENT, XP_AX_PRECEDING, XP_AX_PRECEDING_SIBLING, XP_AX_SELF } ncx_xpath_axis_t; /* Node name match modes */ typedef enum ncx_name_match_t_ { NCX_MATCH_NONE, NCX_MATCH_EXACT, NCX_MATCH_EXACT_NOCASE, NCX_MATCH_ONE, NCX_MATCH_ONE_NOCASE, NCX_MATCH_FIRST, NCX_MATCH_FIRST_NOCASE } ncx_name_match_t; typedef struct ncx_dec64_t_ { int64 val; /* adjusted number to fit in 64 bits */ uint8 digits; /* number of decimal digits 1 .. 18 */ uint8 zeroes; /* number of leading zeroes 0 .. 17 */ } ncx_dec64_t; /* union of all the basic number types * if float not supported, then it is stored as an int64 */ typedef union ncx_num_t_ { int32 i; /* NCX_BT_INT */ int64 l; /* NCX_BT_LONG */ uint32 u; /* NCX_BT_UINT */ uint64 ul; /* NCX_BT_ULONG */ #ifdef HAS_FLOAT /* 'd' used only in XPath */ double d; /* NCX_BT_DOUBLE */ #else int64 d; /* NCX_BT_DOUBLE */ #endif ncx_dec64_t dec; /* NCX_BT_DECIMAL64 */ } ncx_num_t; /* string alias for data types: * NCX_BT_STRING * NCX_BT_OSTRING * NCX_BT_ENAME */ typedef xmlChar * ncx_str_t; typedef const xmlChar * ncx_const_str_t; /* one NCX_BT_ENUM enumeration value (user may enter 1 of 3 forms) */ typedef struct ncx_enum_t_ { const xmlChar *name; /* bptr to typ_enum_t or dname */ xmlChar *dname; /* malloced enum (value not checked) */ int32 val; } ncx_enum_t; /* one NCX_BT_BITS bit value */ typedef struct ncx_bit_t_ { const xmlChar *name; /* bptr to typ_enum_t.name or this.dname */ xmlChar *dname; /* malloced bit name (value not checked) */ uint32 pos; /* position value */ } ncx_bit_t; /* NCX list member: list of string or number */ typedef struct ncx_lmem_t_ { dlq_hdr_t qhdr; union val_ { ncx_num_t num; ncx_str_t str; ncx_enum_t enu; ncx_bit_t bit; boolean boo; } val; uint32 flags; } ncx_lmem_t; /* header for a NCX List */ typedef struct ncx_list_t_ { ncx_btype_t btyp; dlq_hdr_t memQ; /* Q of ncx_lmem_t */ } ncx_list_t; /* NCX base64 string node for YANG 'binary' built-in type */ typedef struct ncx_binary_t_ { unsigned char *ustr; /* binary string */ uint32 ubufflen; /* binary buffer len */ uint32 ustrlen; /* binary buffer used */ } ncx_binary_t; /* struct to remember error info * tkc->cur_err will be checked before tkc->cur * for error information */ typedef struct ncx_error_t_ { struct ncx_module_t_ *mod; uint32 linenum; uint32 linepos; } ncx_error_t; /* YANG extension usage entry */ typedef struct ncx_appinfo_t_ { dlq_hdr_t qhdr; xmlChar *prefix; xmlChar *name; xmlChar *value; struct ext_template_t_ *ext; dlq_hdr_t *appinfoQ; boolean isclone; ncx_error_t tkerr; } ncx_appinfo_t; /* YANG revision entry */ typedef struct ncx_revhist_t_ { dlq_hdr_t qhdr; xmlChar *version; xmlChar *descr; xmlChar *ref; status_t res; ncx_error_t tkerr; } ncx_revhist_t; /* YANG if-feature entry */ typedef struct ncx_iffeature_t_ { dlq_hdr_t qhdr; xmlChar *prefix; xmlChar *name; xmlChar *expr; struct ncx_feature_t_ *feature; ncx_error_t tkerr; boolean seen; /* for yangdiff */ } ncx_iffeature_t; typedef enum ncx_feature_code_t_ { NCX_FEATURE_CODE_NONE, /* enum not explicitly set */ NCX_FEATURE_CODE_STATIC, /* compile-time if-feature code */ NCX_FEATURE_CODE_DYNAMIC /* run-time if-feature code */ } ncx_feature_code_t; /* NCX session protocol versions supported */ typedef enum ncx_protocol_t_ { NCX_PROTO_NONE, NCX_PROTO_NETCONF10, /* RFC 4741 base:1.0 */ NCX_PROTO_NETCONF11 /* RFC xxxx base:1.1 */ } ncx_protocol_t; typedef enum ncx_yang_version_t_ { NCX_YANG_VERSION10, NCX_YANG_VERSION11 } ncx_yang_version_t; /* YANG feature entry */ typedef struct ncx_feature_t_ { dlq_hdr_t qhdr; xmlChar *name; xmlChar *descr; xmlChar *ref; ncx_status_t status; dlq_hdr_t iffeatureQ; /* Q of ncx_iffeature_t */ dlq_hdr_t appinfoQ; /* Q of ncx_appinfo_t */ status_t res; /* may be stored with errors */ boolean enabled; ncx_feature_code_t code; /* dynamic or static code-gen */ ncx_error_t tkerr; boolean seen; /* for yangdiff */ } ncx_feature_t; /* struct for holding r/o pointer to generic internal node * for filtering purposes */ typedef struct ncx_filptr_t_ { dlq_hdr_t qhdr; struct val_value_t_ *node; /* read-only backptr */ struct val_value_t_ *virtualnode; /* virtual val backptr */ dlq_hdr_t childQ; /* Q of ncx_filptr_t */ } ncx_filptr_t; /* representation of one module or submodule during and after parsing */ typedef struct ncx_module_t_ { dlq_hdr_t qhdr; xmlChar *name; xmlChar *version; xmlChar *organization; xmlChar *contact_info; xmlChar *descr; xmlChar *ref; xmlChar *ns; /* malloc:main, copy:submod */ xmlChar *prefix; /* may be empty in a submod */ xmlChar *xmlprefix; /* will be diff. if duplicate */ xmlChar *source; /* full filespec */ xmlChar *belongs; /* set if submod */ const xmlChar *sourcefn; /* ptr to fn in source */ const xmlChar *belongsver; /* back ptr to mod ver */ xmlns_id_t nsid; /* assigned by xmlns */ ncx_yang_version_t langver; boolean ismod; /* module/submodule keyword */ boolean stmtmode; /* T: save yang_stmt_t */ boolean added; /* T: don't free on err */ boolean defaultrev; /* T: use for default ver */ status_t status; /* module parse result */ uint32 errors; /* yangdump results */ uint32 warnings; /* yangdump results */ dlq_hdr_t revhistQ; /* Q of ncx_revhist_t */ dlq_hdr_t importQ; /* Q of ncx_import_t */ dlq_hdr_t includeQ; /* Q of ncx_include_t */ dlq_hdr_t typeQ; /* Q of typ_template_t */ dlq_hdr_t groupingQ; /* Q of grp_template_t */ dlq_hdr_t datadefQ; /* Q of obj_template_t */ dlq_hdr_t extensionQ; /* Q of ext_template_t */ dlq_hdr_t deviationQ; /* Q of obj_deviation_t */ dlq_hdr_t featureQ; /* Q of ncx_feature_t */ dlq_hdr_t identityQ; /* Q of ncx_identity_t */ dlq_hdr_t appinfoQ; /* Q of ncx_appinfo_t */ dlq_hdr_t typnameQ; /* Q of ncx_typname_t */ dlq_hdr_t saveimpQ; /* Q of yang_import_ptr_t */ /* saved from pcb->allimpQ */ dlq_hdr_t stmtQ; /* Q of yang_stmt_t */ /* saved for top, yang, docmode */ struct ncx_module_t_ *parent; /* if this is a submodule */ /* Q of yang_node_t to track all submodules */ dlq_hdr_t allincQ; /* used if parent == NULL */ dlq_hdr_t incchainQ; /* used if parent == NULL */ /* Q of ncx_save_deviations_t to track modules that deviate this * module. */ dlq_hdr_t devmodlist; /* yang-library conformance-type {implement,import} */ boolean implemented; } ncx_module_t; /* back pointer to a YANG identity * used to create an inline tree of valid values * for an identity used as a base * * This inline Q record will be linked in to the * childQ of the base identity when the identity * containing this struct is using it as a base * * This thread is only used for client help, * to easily list all the QName values that are permitted * for a identityref leaf */ typedef struct ncx_idlink_t_ { dlq_hdr_t qhdr; struct ncx_identity_t_ *identity; boolean inq; } ncx_idlink_t; /* YANG identity entry */ typedef struct ncx_identity_t_ { dlq_hdr_t qhdr; dlq_hdr_t baseQ; ncx_module_t *mod; xmlChar *name; xmlChar *descr; xmlChar *ref; ncx_status_t status; dlq_hdr_t iffeatureQ; /* Q of ncx_iffeature_t */ dlq_hdr_t appinfoQ; /* Q of ncx_appinfo_t */ status_t res; /* may be stored with errors */ boolean isroot; /* empty baseQ not an error */ dlq_hdr_t childQ; /* Q of ncx_idlink_t */ ncx_error_t tkerr; boolean seen; /* for yangcli */ } ncx_identity_t; typedef struct ncx_identity_base_t_ { dlq_hdr_t qhdr; xmlChar *prefix; xmlChar *name; ncx_identity_t *identity; ncx_idlink_t idlink; } ncx_identity_base_t; /* enumeration for different NCX module conversion output types */ typedef enum ncx_cvttyp_t_ { NCX_CVTTYP_NONE, NCX_CVTTYP_XSD, NCX_CVTTYP_SQL, NCX_CVTTYP_SQLDB, NCX_CVTTYP_HTML, NCX_CVTTYP_YANG, NCX_CVTTYP_COPY, NCX_CVTTYP_H, NCX_CVTTYP_C, NCX_CVTTYP_CPP_TEST, NCX_CVTTYP_YIN, NCX_CVTTYP_TG2, NCX_CVTTYP_YH, NCX_CVTTYP_YC, NCX_CVTTYP_UH, NCX_CVTTYP_UC, NCX_CVTTYP_TREE } ncx_cvttyp_t; /* enum for with-defaults enum values */ typedef enum ncx_withdefaults_t_ { NCX_WITHDEF_NONE, NCX_WITHDEF_REPORT_ALL, NCX_WITHDEF_REPORT_ALL_TAGGED, NCX_WITHDEF_TRIM, NCX_WITHDEF_EXPLICIT } ncx_withdefaults_t; /* One 'import' clause in YANG */ typedef struct ncx_import_t_ { dlq_hdr_t qhdr; xmlChar *module; xmlChar *prefix; xmlChar *revision; xmlChar *description; xmlChar *reference; ncx_module_t *mod; /* back-ptr */ boolean used; boolean usexsd; /* FALSE if duplicate */ boolean force_yuma_nc; /* HACK: TRUE if yuma-nc replacing ietf-nc */ dlq_hdr_t appinfoQ; ncx_error_t tkerr; status_t res; /* set to NO_ERR if the import was parsed OK */ } ncx_import_t; /* One 'include' clause, YANG only */ typedef struct ncx_include_t_ { dlq_hdr_t qhdr; xmlChar *submodule; xmlChar *revision; struct ncx_module_t_ *submod; /* back-ptr */ boolean usexsd; /* FALSE if duplicate */ dlq_hdr_t appinfoQ; ncx_error_t tkerr; } ncx_include_t; /* enum for REQUIRED vs. OPTIONAL token */ typedef enum ncx_opt_t_ { NCX_REQ, /* clause is required */ NCX_OPT /* clause is optional */ } ncx_opt_t; /* enum for WHITESPACE ALLOWED vs. WHITESPACE NOT ALLOWED string */ typedef enum ncx_strtyp_t_ { NCX_WSP, /* whitespace allowed: quoted string */ NCX_NO_WSP /* whitespace not allowed: unquoted string */ } ncx_strtyp_t; /* YANG must statement struct */ typedef struct ncx_errinfo_t_ { dlq_hdr_t qhdr; xmlChar *descr; xmlChar *ref; xmlChar *error_app_tag; xmlChar *error_message; boolean seen; /* for yangdiff */ } ncx_errinfo_t; /* keep track of the typenames used for local typedefs * only used by ncxdump to generate XSDs */ typedef struct ncx_typname_t_ { dlq_hdr_t qhdr; struct typ_template_t_ *typ; const xmlChar *typname; xmlChar *typname_malloc; } ncx_typname_t; /* user function callback template when a module is * loaded into the system * * ncx_load_cbfn_t * * Run an instrumentation-defined function * for a 'module-loaded' event * * INPUTS: * mod == module that was added to the registry * */ typedef void (*ncx_load_cbfn_t) (ncx_module_t *mod); /* user function callback template to traverse all module * features for a specified module * * ncx_feature_cbfn_t * * Run an instrumentation-defined function * for each feature found in the module (enabled or not) * * INPUTS: * mod == module originally passed to the main * function, which contains the 'feature' * feature == feature being processed in the traversal * cookie == cookie originally passed to the main function * * RETURNS: * TRUE if processing should continue * FALSE if feature traversal should terminate */ typedef boolean (*ncx_feature_cbfn_t) (const ncx_module_t *mod, ncx_feature_t *feature, void *cookie); /* enum for get-schema format type enum values * matches the schema-format identities in RFC 6022 */ typedef enum ncx_modformat_t_ { NCX_MODFORMAT_NONE, NCX_MODFORMAT_XSD, NCX_MODFORMAT_YANG, NCX_MODFORMAT_YIN, NCX_MODFORMAT_RNG, NCX_MODFORMAT_RNC } ncx_modformat_t; /* used with obj_deviation_t to defer object lookups */ typedef struct ncx_save_deviations_t_ { dlq_hdr_t qhdr; dlq_hdr_t importQ; /* Q of ncx_import_t */ dlq_hdr_t deviationQ; /* Q of obj_deviation_t */ xmlChar *devmodule; xmlChar *devrevision; xmlChar *devnamespace; xmlChar *devprefix; } ncx_save_deviations_t; /* type of confirmEvent in the sysConfirmedCommit notification */ typedef enum ncx_confirm_event_t_ { NCX_CC_EVENT_NONE, NCX_CC_EVENT_START, NCX_CC_EVENT_CANCEL, NCX_CC_EVENT_TIMEOUT, NCX_CC_EVENT_EXTEND, NCX_CC_EVENT_COMPLETE } ncx_confirm_event_t; #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_ncxtypes */ yuma123_2.14/netconf/src/ncx/json_wr.c0000664000175000017500000003311414770023131017765 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: json_wr.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 27aug11 abb begun; start from clone of xml_wr.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include "procdefs.h" #include "dlq.h" #include "ncx.h" #include "ncx_num.h" #include "ncxconst.h" #include "obj.h" #include "ses.h" #include "status.h" #include "val.h" #include "val_util.h" #include "xmlns.h" #include "xml_msg.h" #include "json_wr.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define JSON_WR_DEBUG 1 #endif /******************************************************************** * FUNCTION write_json_string_value * * Write a simple value as a JSON string * * INPUTS: * scb == session control block * val == value to write * * RETURNS: * status *********************************************************************/ static status_t write_json_string_value (ses_cb_t *scb, val_value_t *val) { xmlChar *valstr = val_make_sprintf_string(val); if (valstr) { ses_putchar(scb, '"'); ses_putjstr(scb, valstr, -1); ses_putchar(scb, '"'); m__free(valstr); return NO_ERR; } return ERR_INTERNAL_MEM; } /* write_json_string_value */ /******************************************************************** * FUNCTION write_full_check_val * * generate entire val_value_t *w/filter) * Write an entire val_value_t out as XML, including the top level * Using an optional testfn to filter output * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * startindent == start indent amount if indent enabled * testcb == callback function to use, NULL if not used * justone == TRUE if just one of these; FALSE if more than one * isfirst == TRUE if this is the first (top) val printed * isfirstchild == TRUE if this is the first value of an array * == FALSE if this is the 2nd - Nth value of an array * RETURNS: * status *********************************************************************/ static status_t write_full_check_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 startindent, val_nodetest_fn_t testfn, boolean justone, boolean isfirst, boolean isfirstchild) { val_value_t *out; boolean malloced = FALSE; status_t res = NO_ERR; out = val_get_value(scb, msg, val, testfn, TRUE, &malloced, &res); if (!out || res != NO_ERR) { if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } if (out && malloced) { val_free_value(out); } return res; } /* check if this is an external file to send */ if (out->btyp == NCX_BT_EXTERN || out->btyp == NCX_BT_INTERN || typ_is_simple(out->btyp)) { if (isfirst) { write_json_string_value(scb, out); } else { /* write the name of the node */ ses_putchar(scb, '"'); ses_putjstr(scb, out->name, -1); ses_putchar(scb, '"'); ses_putchar(scb, ':'); switch (out->btyp) { case NCX_BT_EXTERN: ses_putchar(scb, '"'); val_write_extern(scb, out); ses_putchar(scb, '"'); break; case NCX_BT_INTERN: ses_putchar(scb, '"'); val_write_intern(scb, out); ses_putchar(scb, '"'); break; case NCX_BT_ENUM: ses_putchar(scb, '"'); if (VAL_ENUM_NAME(out)) { ses_putjstr(scb, VAL_ENUM_NAME(out), -1); } ses_putchar(scb, '"'); break; case NCX_BT_EMPTY: if (out->v.boo) { ses_putjstr(scb, NCX_EL_NULL, -1); } // else skip this value! should already be checked break; case NCX_BT_BOOLEAN: if (out->v.boo) { ses_putjstr(scb, NCX_EL_TRUE, -1); } else { ses_putjstr(scb, NCX_EL_FALSE, -1); } break; default: write_json_string_value(scb, out); } } } else { val_value_t *chval; val_value_t *lastch = NULL; val_value_t *nextch = NULL; int32 indent = (startindent < 0) ? -1 : startindent + ses_indent_count(scb); if (isfirst) { ses_putchar(scb, '{'); } /* render a complex type; either an object or an array */ if (isfirstchild) { ses_putchar(scb, '"'); ses_putjstr(scb, out->name, -1); ses_putchar(scb, '"'); ses_putchar(scb, ':'); if (!justone) { ses_putchar(scb, '['); } } ses_indent(scb, indent); ses_putchar(scb, '{'); for (chval = val_get_first_child(out); chval != NULL; chval = nextch) { /* JSON ignores XML namespaces, so foo:a and bar:a * are both encoded in the same array */ uint32 childcnt = val_instance_count(out, NULL, chval->name); boolean firstchild = (lastch && !xml_strcmp(lastch->name, chval->name)) ? FALSE : TRUE; lastch = chval; nextch = val_get_next_child(chval); res = write_full_check_val(scb, msg, chval, indent, testfn, (childcnt > 1) ? FALSE : TRUE, FALSE, firstchild); if (res == ERR_NCX_SKIPPED) { ; } else if (res != NO_ERR) { /* FIXME: do something about error; * may not always be OK to continue to next child node */ ; } else if (nextch) { ses_putchar(scb, ','); if (indent >= 0 && (typ_is_simple(nextch->btyp) || xml_strcmp(nextch->name, chval->name))) { ses_indent(scb, indent); ses_putchar(scb, ' '); } } } ses_indent(scb, indent); ses_putchar(scb, '}'); if (!justone) { val_value_t *peeknext = val_get_next_child(val); if (!peeknext || xml_strcmp(val->name, peeknext->name)) { ses_putchar(scb, ']'); } } if (isfirst) { ses_indent(scb, startindent); ses_putchar(scb, '}'); } } if (malloced && out) { val_free_value(out); } return res; } /* write_full_check_val */ /************ E X T E R N A L F U N C T I O N S **************/ /******************************************************************** * FUNCTION json_wr_full_check_val * * generate entire val_value_t *w/filter) * Write an entire val_value_t out as XML, including the top level * Using an optional testfn to filter output * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * startindent == start indent amount if indent enabled * testcb == callback function to use, NULL if not used * * RETURNS: * status *********************************************************************/ status_t json_wr_full_check_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 startindent, val_nodetest_fn_t testfn) { #ifdef DEBUG if (!scb || !msg || !val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return write_full_check_val(scb, msg, val, startindent, testfn, TRUE, TRUE, TRUE); } /* json_wr_full_check_val */ /******************************************************************** * FUNCTION json_wr_check_open_file * * Write the specified value to an open FILE in JSON format * * INPUTS: * fp == open FILE control block * val == value for output * startindent == starting indent point * indent == indent amount (0..9 spaces) * testfn == callback test function to use * * RETURNS: * status *********************************************************************/ status_t json_wr_check_open_file (FILE *fp, val_value_t *val, int32 startindent, int32 indent, val_nodetest_fn_t testfn) { ses_cb_t *scb = NULL; rpc_msg_t *msg = NULL; status_t res = NO_ERR; xml_attrs_t myattrs; #ifdef DEBUG if (!fp || !val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif indent = min(indent, 9); xml_init_attrs(&myattrs); /* get a dummy session control block */ scb = ses_new_dummy_scb(); if (!scb) { res = ERR_INTERNAL_MEM; } else { scb->fp = fp; scb->indent = indent; } /* get a dummy output message */ if (res == NO_ERR) { msg = rpc_new_out_msg(); if (!msg) { res = ERR_INTERNAL_MEM; } else { /* hack -- need a queue because there is no top * element which this usually shadows */ msg->rpc_in_attrs = &myattrs; } } if (res == NO_ERR) { /* write the tree in JSON format */ res = json_wr_full_check_val(scb, &msg->mhdr, val, startindent, testfn); if (res != ERR_NCX_SKIPPED) { ses_finish_msg(scb); } else { res = NO_ERR; } } /* clean up and exit */ if (msg) { rpc_free_msg(msg); } if (scb) { scb->fp = NULL; /* do not close the file */ ses_free_scb(scb); } xml_clean_attrs(&myattrs); return res; } /* json_wr_check_open_file */ /******************************************************************** * FUNCTION json_wr_check_file * * Write the specified value to a FILE in JSON format * * INPUTS: * filespec == exact path of filename to open * val == value for output * startindent == starting indent point * indent == indent amount (0..9 spaces) * testfn == callback test function to use * * RETURNS: * status *********************************************************************/ status_t json_wr_check_file (const xmlChar *filespec, val_value_t *val, int32 startindent, int32 indent, val_nodetest_fn_t testfn) { FILE *fp; status_t res; #ifdef DEBUG if (!filespec || !val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif fp = fopen((const char *)filespec, "w"); if (!fp) { log_error("\nError: Cannot open XML file '%s'", filespec); return ERR_FIL_OPEN; } res = json_wr_check_open_file(fp, val, startindent, indent, testfn); fclose(fp); return res; } /* json_wr_check_file */ /******************************************************************** * FUNCTION json_wr_file * * Write the specified value to a FILE in JSON format * * INPUTS: * filespec == exact path of filename to open * val == value for output * startindent == starting indent point * indent == indent amount (0..9 spaces) * * RETURNS: * status *********************************************************************/ status_t json_wr_file (const xmlChar *filespec, val_value_t *val, int32 startindent, int32 indent) { return json_wr_check_file(filespec, val, startindent, indent, NULL); } /* json_wr_file */ /* END file json_wr.c */ yuma123_2.14/netconf/src/ncx/yang_parse.c0000664000175000017500000041310214770023131020433 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yang_parse.c YANG module parser YANG modules are parsed in the following steps: 1) basic tokenization and string processing (tk.c) 2) first pass module processing, reject bad syntax 3) resolve ranges, forward references, etc in typedefs 4) resolve data model definitions (objects, rpcs, notifications) 5) convert/copy obj_template_t data specifications to typ_def_t representation ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 26oct07 abb begun; start from ncx_parse.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #define _GNU_SOURCE #include #undef _GNU_SOURCE #include #include #include #include #include #include "procdefs.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "ncx_appinfo.h" #include "ncx_feature.h" #include "ncxconst.h" #include "ncxmod.h" #include "ncxtypes.h" #include "obj.h" #include "status.h" #include "tstamp.h" #include "typ.h" #include "xml_util.h" #include "yang.h" #include "yangconst.h" #include "yang_ext.h" #include "yang_grp.h" #include "yang_obj.h" #include "yang_parse.h" #include "yang_typ.h" #include "yinyang.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define YANG_PARSE_DEBUG 1 /* #define YANG_PARSE_TK_DEBUG 1 */ /* #define YANG_PARSE_RDLN_DEBUG 1 */ /* #define YANG_PARSE_DEBUG_TRACE 1 */ /* #define YANG_PARSE_DEBUG_MEMORY 1 */ #endif static status_t consume_revision_date (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **revstring); /******************************************************************** * FUNCTION resolve_mod_appinfo * * Validate the ncx_appinfo_t (extension usage) within * the include, import, and feature clauses for this module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_mod_appinfo (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod) { ncx_import_t *imp; ncx_include_t *inc; ncx_feature_t *feature; status_t res, retres; retres = NO_ERR; for (imp = (ncx_import_t *)dlq_firstEntry(&mod->importQ); imp != NULL; imp = (ncx_import_t *)dlq_nextEntry(imp)) { res = ncx_resolve_appinfoQ(pcb, tkc, mod, &imp->appinfoQ); CHK_EXIT(res, retres); } for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { res = ncx_resolve_appinfoQ(pcb, tkc, mod, &inc->appinfoQ); CHK_EXIT(res, retres); } for (feature = (ncx_feature_t *)dlq_firstEntry(&mod->featureQ); feature != NULL; feature = (ncx_feature_t *)dlq_nextEntry(feature)) { res = ncx_resolve_appinfoQ(pcb, tkc, mod, &feature->appinfoQ); CHK_EXIT(res, retres); } return retres; } /* resolve_mod_appinfo */ /******************************************************************** * FUNCTION consume_mod_hdr * * Parse the module header statements * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_mod_hdr (tk_chain_t *tkc, ncx_module_t *mod) { const xmlChar *val; xmlChar *str; const char *expstr; tk_type_t tktyp; status_t res, retres; boolean done, ver, ns, pfix; expstr = "module header statement"; ver = FALSE; ns = FALSE; pfix = FALSE; res = NO_ERR; retres = NO_ERR; done = FALSE; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_mod_exp_err(tkc, mod, res, expstr); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &mod->appinfoQ); if (res != NO_ERR) { retres = res; res = NO_ERR; } continue; case TK_TT_RBRACE: TK_BKUP(tkc); done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_YANG_VERSION)) { /* Optional 'yang-version' field is present */ if (ver) { retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); } ver = TRUE; /* get the version number */ res = yang_consume_string(tkc, mod, &str); if (res != NO_ERR) { retres = res; } else { if(0==xml_strcmp(TK_CUR_VAL(tkc), YANG_VERSION10_STR)) { mod->langver = NCX_YANG_VERSION10; } else if(0==xml_strcmp(TK_CUR_VAL(tkc), YANG_VERSION11_STR)) { mod->langver = NCX_YANG_VERSION11; } else { retres = ERR_NCX_WRONG_VERSION; ncx_print_errormsg(tkc, mod, retres); } } if (str) { m__free(str); } res = yang_consume_semiapp(tkc, mod, &mod->appinfoQ); if (res != NO_ERR) { retres = res; } } else if (!xml_strcmp(val, YANG_K_NAMESPACE)) { res = yang_consume_strclause(tkc, mod, &mod->ns, &ns, &mod->appinfoQ); if (res != NO_ERR) { retres = res; } else { /*** TBD: check valid URI ***/ } } else if (!xml_strcmp(val, YANG_K_PREFIX)) { res = yang_consume_strclause(tkc, mod, &mod->prefix, &pfix, &mod->appinfoQ); if (res != NO_ERR) { retres = res; } else if (!ncx_valid_name2(mod->prefix)) { retres = ERR_NCX_INVALID_NAME; log_error("\nError: invalid prefix value '%s'", mod->prefix); ncx_print_errormsg(tkc, mod, retres); } else { ncx_check_warn_idlen(tkc, mod, mod->prefix); } } else if (!yang_top_keyword(val)) { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } else { /* assume we reached the end of this sub-section * if there are simply clauses out of order, then * the compiler will not detect that. */ TK_BKUP(tkc); done = TRUE; } } /* check missing mandatory sub-clauses */ if (!mod->ns) { retres = ERR_NCX_DATA_MISSING; ncx_mod_missing_err(tkc, mod, "mod-hdr", "namespace"); } if (!mod->prefix) { retres = ERR_NCX_DATA_MISSING; ncx_mod_missing_err(tkc, mod, "mod-hdr", "prefix"); } return retres; } /* consume_mod_hdr */ /******************************************************************** * FUNCTION consume_belongs_to * * Parse the next N tokens as a belongs-to clause * Set the submodule prefix and belongs string * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'belongs-to' keyword * * INPUTS: * tkc == token chain * mod == module struct that will get updated * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_belongs_to (tk_chain_t *tkc, ncx_module_t *mod) { const xmlChar *val; const char *expstr; tk_type_t tktyp; boolean done, pfixdone; status_t res, retres; val = NULL; expstr = "module name"; done = FALSE; pfixdone = FALSE; retres = NO_ERR; /* Get the mandatory module name */ res = yang_consume_id_string(tkc, mod, &mod->belongs); if (res != NO_ERR) { return res; } /* Get the starting left brace for the sub-clauses * or a semi-colon to end the belongs-to statement */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: done = TRUE; break; case TK_TT_LBRACE: break; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; } /* get the prefix clause and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_mod_exp_err(tkc, mod, res, expstr); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &mod->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } /* Got a token string so check the value, should be 'prefix' */ if (!xml_strcmp(val, YANG_K_PREFIX)) { res = yang_consume_strclause(tkc, mod, &mod->prefix, &pfixdone, &mod->appinfoQ); if (res == NO_ERR && !ncx_valid_name2(mod->prefix)) { res = ERR_NCX_INVALID_NAME; log_error("\nError: invalid prefix value '%s'", mod->prefix); ncx_print_errormsg(tkc, mod, res); } else { ncx_check_warn_idlen(tkc, mod, mod->prefix); } CHK_EXIT(res, retres); } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); } } if (!mod->prefix) { retres = ERR_NCX_DATA_MISSING; ncx_mod_missing_err(tkc, mod, "belongs-to", "prefix"); } return retres; } /* consume_belongs_to */ /******************************************************************** * FUNCTION consume_feature * * Parse the next N tokens as a feature statement * Create an ncx_feature_t struct and add it to the * module or submodule featureQ * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'feature' keyword * * INPUTS: * tkc == token chain * mod == module struct that will get updated * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_feature (tk_chain_t *tkc, ncx_module_t *mod) { const xmlChar *val; const char *expstr; ncx_feature_t *feature, *testfeature; yang_stmt_t *stmt; tk_type_t tktyp; boolean done, stat, desc, ref, keep; status_t res, retres; val = NULL; expstr = "feature name"; done = FALSE; stat = FALSE; desc = FALSE; ref = FALSE; keep = TRUE; retres = NO_ERR; feature = ncx_new_feature(); if (!feature) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&feature->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); /* Get the mandatory feature name */ res = yang_consume_id_string(tkc, mod, &feature->name); if (res != NO_ERR) { /* do not keep -- must have a name field */ retres = res; keep = FALSE; } /* Get the starting left brace for the sub-clauses * or a semi-colon to end the feature statement */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); ncx_free_feature(feature); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: done = TRUE; break; case TK_TT_LBRACE: break; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; } expstr = "feature sub-statement"; /* get the prefix clause and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); retres = res; done = TRUE; continue; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: retres = ERR_NCX_EOF; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; continue; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &mod->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { done = TRUE; } } continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } /* Got a token string so check the value, should be 'prefix' */ if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &feature->iffeatureQ, &feature->appinfoQ); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &feature->status, &stat, &feature->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &feature->descr, &desc, &feature->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &feature->ref, &ref, &feature->appinfoQ); } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { done = TRUE; } } } /* check if feature already exists in this module */ if (keep) { testfeature = ncx_find_feature_all(mod, feature->name); if (testfeature) { retres = ERR_NCX_DUP_ENTRY; log_error("\nError: feature '%s' already defined " "in '%s' at line %u", feature->name, testfeature->tkerr.mod->name, testfeature->tkerr.linenum); ncx_print_errormsg(tkc, mod, retres); } feature->res = retres; dlq_enque(feature, &mod->featureQ); if (mod->stmtmode) { stmt = yang_new_feature_stmt(feature); if (stmt) { dlq_enque(stmt, &mod->stmtQ); } else { log_error("\nError: malloc failure for feature_stmt"); retres = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, retres); } } } else { ncx_free_feature(feature); } return retres; } /* consume_feature */ /******************************************************************** * FUNCTION resolve_feature * * Validate all the if-feature clauses present in * the specified feature * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == ncx_module_t in progress * feature == ncx_feature_t to check * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_feature (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, ncx_feature_t *feature) { ncx_feature_t *testfeature; ncx_iffeature_t *iff; status_t res, retres; boolean errdone; retres = NO_ERR; /* check if there are any feature parameters set */ ncx_set_feature_parms(feature); /* check if there are any if-feature statements inside * this feature that need to be resolved */ for (iff = (ncx_iffeature_t *) dlq_firstEntry(&feature->iffeatureQ); iff != NULL; iff = (ncx_iffeature_t *)dlq_nextEntry(iff)) { testfeature = NULL; errdone = FALSE; res = NO_ERR; if (iff->prefix && xml_strcmp(iff->prefix, mod->prefix)) { /* find the feature in another module */ res = yang_find_imp_feature(pcb, tkc, mod, iff->prefix, iff->name, &iff->tkerr, &testfeature); if (res != NO_ERR) { retres = res; errdone = TRUE; } } else if (!xml_strcmp(iff->name, feature->name)) { /* error: if-feature foo inside feature foo */ res = retres = ERR_NCX_DEF_LOOP; log_error("\nError: 'if-feature %s' inside feature '%s'", iff->name, iff->name); tkc->curerr = &iff->tkerr; ncx_print_errormsg(tkc, mod, retres); errdone = TRUE; } else { testfeature = ncx_find_feature(mod, iff->name); } if (!testfeature && !errdone) { log_error("\nError: Feature '%s' not found " "for if-feature statement", iff->name); res = retres = ERR_NCX_DEF_NOT_FOUND; tkc->curerr = &iff->tkerr; ncx_print_errormsg(tkc, mod, retres); } if (testfeature) { iff->feature = testfeature; } } return retres; } /* resolve_feature */ /******************************************************************** * FUNCTION check_feature_loop * * Validate all the if-feature clauses present in * the specified feature, after all if-features have * been resolved (or at least attempted) * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == ncx_module_t in progress * feature == ncx_feature_t to check now * startfeature == feature that started this off, so if this * is reached again, it will trigger an error * * RETURNS: * status of the operation *********************************************************************/ static status_t check_feature_loop (tk_chain_t *tkc, ncx_module_t *mod, ncx_feature_t *feature, ncx_feature_t *startfeature) { ncx_iffeature_t *iff; status_t res, retres; retres = NO_ERR; /* check if there are any if-feature statements inside * this feature that need to be resolved */ for (iff = (ncx_iffeature_t *) dlq_firstEntry(&feature->iffeatureQ); iff != NULL; iff = (ncx_iffeature_t *)dlq_nextEntry(iff)) { if (!iff->feature) { continue; } if (iff->feature == startfeature) { retres = res = ERR_NCX_DEF_LOOP; startfeature->res = res; log_error("\nError: if-feature loop detected for '%s' " "in feature '%s'", startfeature->name, feature->name); tkc->curerr = &startfeature->tkerr; ncx_print_errormsg(tkc, mod, res); } else if (iff->feature->res != ERR_NCX_DEF_LOOP) { res = check_feature_loop(tkc, mod, iff->feature, startfeature); if (res != NO_ERR) { retres = res; } } } return retres; } /* check_feature_loop */ static ncx_identity_base_t* new_identity_base (void) { ncx_identity_base_t *base = m__getObj(ncx_identity_base_t); if (!base) { return NULL; } memset(base, 0x0, sizeof(ncx_identity_base_t)); return base; } /******************************************************************** * FUNCTION consume_identity * * Parse the next N tokens as an identity statement * Create an ncx_identity_t struct and add it to the * module or submodule identityQ * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'identity' keyword * * INPUTS: * tkc == token chain * mod == module struct that will get updated * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_identity (tk_chain_t *tkc, ncx_module_t *mod) { const xmlChar *val; const char *expstr; ncx_identity_t *identity, *testidentity; yang_stmt_t *stmt; tk_type_t tktyp; boolean done, stat, desc, ref, keep; status_t res, retres; val = NULL; expstr = "identity name"; done = FALSE; stat = FALSE; desc = FALSE; ref = FALSE; keep = TRUE; retres = NO_ERR; identity = ncx_new_identity(); if (!identity) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&identity->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); identity->mod=mod; identity->isroot = TRUE; /* Get the mandatory identity name */ res = yang_consume_id_string(tkc, mod, &identity->name); if (res != NO_ERR) { /* do not keep -- must have a name field */ retres = res; keep = FALSE; } /* Get the starting left brace for the sub-clauses * or a semi-colon to end the identity statement */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); ncx_free_identity(identity); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: done = TRUE; break; case TK_TT_LBRACE: break; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; } expstr = "identity sub-statement"; /* get the prefix clause and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); retres = res; done = TRUE; continue; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: retres = ERR_NCX_EOF; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; continue; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &mod->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { done = TRUE; } } continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } /* Got a token string so check the value, should be 'prefix' */ if (!xml_strcmp(val, YANG_K_BASE)) { ncx_identity_base_t* base; boolean dummy_duplication_flag = FALSE; identity->isroot = FALSE; base = (ncx_identity_base_t*) new_identity_base(); assert(base); res = yang_consume_pid(tkc, mod, &base->prefix, &base->name, &dummy_duplication_flag, &identity->appinfoQ); dlq_enque(base, &identity->baseQ); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &identity->status, &stat, &identity->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &identity->descr, &desc, &identity->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &identity->ref, &ref, &identity->appinfoQ); } else if (!xml_strcmp(val, YANG_K_IF_FEATURE) && mod->langver == NCX_YANG_VERSION11) { res = yang_consume_iffeature(tkc, mod, &identity->iffeatureQ, &identity->appinfoQ); } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { done = TRUE; } } } /* check if feature already exists in this module */ if (keep) { testidentity = ncx_find_identity(mod, identity->name, TRUE); if (testidentity) { retres = ERR_NCX_DUP_ENTRY; log_error("\nError: identity '%s' already defined " "in %s at line %u", identity->name, testidentity->tkerr.mod->name, testidentity->tkerr.linenum); ncx_print_errormsg(tkc, mod, retres); } identity->res = retres; dlq_enque(identity, &mod->identityQ); if (mod->stmtmode) { stmt = yang_new_id_stmt(identity); if (stmt) { dlq_enque(stmt, &mod->stmtQ); } else { log_error("\nError: malloc failure for id_stmt"); retres = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, retres); } } } else { ncx_free_identity(identity); } return retres; } /* consume_identity */ /******************************************************************** * FUNCTION identity_resolve_iffeatureQ * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == ncx_module_t in progress * identity == resolve iffeatures in this ncx_identity_t * * RETURNS: * status of the operation *********************************************************************/ static status_t identity_resolve_iffeatureQ (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, ncx_identity_t *identity) { xmlChar *namestr; if (asprintf((char **)&namestr, "identity '%s'", identity->name) < 0) return ERR_INTERNAL_MEM; return ncx_resolve_iffeatureQ(pcb, tkc, mod, namestr, &identity->iffeatureQ); } /******************************************************************** * FUNCTION resolve_identity * * Validate the identity statement base clause, if any * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == ncx_module_t in progress * identity == ncx_identity_t to check * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_identity (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, ncx_identity_t *identity) { ncx_identity_base_t * base; status_t res = NO_ERR; boolean errdone = FALSE; /* Yang 1.1: first, resolve if-feature statements */ res = identity_resolve_iffeatureQ(pcb, tkc, mod, identity); if (identity->isroot) { return NO_ERR; } else { base = (ncx_identity_base_t *)dlq_firstEntry(&identity->baseQ); assert(base); } for(;base!=NULL;base=(ncx_identity_base_t *)dlq_nextEntry(base)) { ncx_identity_t *testidentity = NULL; if (base->prefix && mod->prefix && xml_strcmp(base->prefix, mod->prefix)) { /* find the identity in another module */ res = yang_find_imp_identity(pcb, tkc, mod, base->prefix, base->name, &identity->tkerr, &testidentity); if (res != NO_ERR) { errdone = TRUE; } } else if (identity->name && base->name && !xml_strcmp(identity->name, base->name)) { /* error: 'base foo' inside 'identity foo' */ res = ERR_NCX_DEF_LOOP; log_error("\nError: 'base %s' inside identity '%s'", base->name, identity->name); tkc->curerr = &identity->tkerr; ncx_print_errormsg(tkc, mod, res); errdone = TRUE; } else if (base->name) { testidentity = ncx_find_identity(mod, base->name, FALSE); } if (!testidentity && !errdone) { if (base->prefix || base->name) { log_error("\nError: Base '%s%s%s' not found " "for identity statement '%s'", (base->prefix) ? base->prefix : EMPTY_STRING, (base->prefix) ? ":" : "", (base->name) ? base->name : EMPTY_STRING, (identity->name) ? identity->name : NCX_EL_NONE); res = ERR_NCX_DEF_NOT_FOUND; } else { log_error("\nError: Invalid base name for identity statement '%s'", (identity->name) ? identity->name : NCX_EL_NONE); res = ERR_NCX_INVALID_NAME; } tkc->curerr = &identity->tkerr; ncx_print_errormsg(tkc, mod, res); } if (testidentity) { base->identity = testidentity; } } return res; } /* resolve_identity */ /******************************************************************** * FUNCTION check_identity_loop * * Validate the base identity chain for loops * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == ncx_module_t in progress * identity == ncx_identity_t to check now * startidentity == identity that started this off, so if this * is reached again, it will trigger an error * * RETURNS: * status of the operation *********************************************************************/ static status_t check_identity_loop (tk_chain_t *tkc, ncx_module_t *mod, ncx_identity_t *identity, ncx_identity_t *startidentity) { status_t res; ncx_identity_base_t *base; res = NO_ERR; /* check if there is a base statement, and if it leads * back to startidentity or not */ for(base=(ncx_identity_base_t *)dlq_firstEntry(&identity->baseQ); base!=NULL; base=(ncx_identity_base_t *)dlq_nextEntry(base)) { if (base->identity == startidentity) { res = ERR_NCX_DEF_LOOP; startidentity->res = res; log_error("\nError: identity base loop detected for '%s' " "in identity '%s'", startidentity->name, identity->name); tkc->curerr = &startidentity->tkerr; ncx_print_errormsg(tkc, mod, res); } else if (base->identity->res != ERR_NCX_DEF_LOOP) { res = check_identity_loop(tkc, mod, base->identity, startidentity); if(identity==startidentity) { /* parent base */ #if 1 if (res == NO_ERR) { /* add idlink entry to */ /* thread the idlink into the base identifier */ dlq_enque(&base->idlink, &base->identity->childQ); base->idlink.inq = TRUE; base->idlink.identity = identity; } #endif } } } return res; } /* check_identity_loop */ /******************************************************************** * FUNCTION consume_submod_hdr * * Parse the sub-module header statements * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_submod_hdr (tk_chain_t *tkc, ncx_module_t *mod) { const xmlChar *val; const char *expstr; xmlChar *str; tk_type_t tktyp; status_t res, retres; boolean done, ver; expstr = "submodule header statement"; ver = FALSE; res = NO_ERR; retres = NO_ERR; done = FALSE; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_mod_exp_err(tkc, mod, res, expstr); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &mod->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: TK_BKUP(tkc); done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_YANG_VERSION)) { /* Optional 'yang-version' field is present */ if (ver) { retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); } ver = TRUE; /* get the version number */ res = yang_consume_string(tkc, mod, NULL); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { return res; } } else { if(0==xml_strcmp(TK_CUR_VAL(tkc), YANG_VERSION10_STR)) { mod->langver = NCX_YANG_VERSION10; } else if(0==xml_strcmp(TK_CUR_VAL(tkc), YANG_VERSION11_STR)) { mod->langver = NCX_YANG_VERSION11; } else { retres = ERR_NCX_WRONG_VERSION; ncx_print_errormsg(tkc, mod, retres); } } res = yang_consume_semiapp(tkc, mod, &mod->appinfoQ); CHK_EXIT(res, retres); } else if (!xml_strcmp(val, YANG_K_BELONGS_TO)) { res = consume_belongs_to(tkc, mod); CHK_EXIT(res, retres); } else if (!yang_top_keyword(val)) { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); } else { /* assume we reached the end of this sub-section * if there are simply clauses out of order, then * the compiler will not detect that. */ TK_BKUP(tkc); done = TRUE; } } /* check missing mandatory sub-clause */ if (!mod->belongs) { retres = ERR_NCX_DATA_MISSING; ncx_mod_missing_err(tkc, mod, "submod-hdr", "belongs-to"); } return retres; } /* consume_submod_hdr */ /******************************************************************** * FUNCTION consume_import * * Parse the next N tokens as an import clause * Create a ncx_import struct and add it to the specified module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'import' keyword * * INPUTS: * tkc == token chain * mod == module struct that will get the ncx_import_t * pcb == parser control block * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_import (tk_chain_t *tkc, ncx_module_t *mod, yang_pcb_t *pcb) { ncx_import_t *imp, *testimp; const xmlChar *val; const char *expstr; yang_node_t *node; yang_import_ptr_t *impptr; ncx_module_t *testmod; tk_type_t tktyp; boolean done, pfixdone, revdone, description_done, reference_done; status_t res, retres; ncx_error_t tkerr; val = NULL; expstr = "module name"; done = FALSE; pfixdone = FALSE; revdone = FALSE; description_done = FALSE; reference_done = FALSE; retres = NO_ERR; memset(&tkerr, 0x0, sizeof(tkerr)); /* Get a new ncx_import_t to fill in */ imp = ncx_new_import(); if (!imp) { retres = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, retres); return retres; } else { ncx_set_error(&imp->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); imp->usexsd = TRUE; } /* Get the mandatory module name */ res = yang_consume_id_string(tkc, mod, &imp->module); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_import(imp); return res; } } /* HACK: need to replace when ietf-netconf.yang is supported */ if (res == NO_ERR && !xml_strcmp(imp->module, NCXMOD_IETF_NETCONF)) { /* force the parser to skip loading this import and just * use the yuma-netconf module instead */ testmod = ncx_find_module(NCXMOD_YUMA_NETCONF, NULL); if (testmod != NULL) { imp->force_yuma_nc = TRUE; imp->mod = testmod; } } /* Get the starting left brace for the sub-clauses */ res = ncx_consume_token(tkc, mod, TK_TT_LBRACE); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_import(imp); return res; } } /* get the prefix clause and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); ncx_free_import(imp); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_mod_exp_err(tkc, mod, res, expstr); ncx_free_import(imp); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &imp->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_import(imp); return res; } } continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } /* Got a token string so check the value, should be 'prefix' */ if (!xml_strcmp(val, YANG_K_PREFIX)) { res = yang_consume_strclause(tkc, mod, &imp->prefix, &pfixdone, &imp->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_import(imp); return res; } } } else if ((mod->langver != NCX_YANG_VERSION10) && !xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_strclause(tkc, mod, &imp->description, &description_done, &imp->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_import(imp); return res; } } } else if ((mod->langver != NCX_YANG_VERSION10) && !xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_strclause(tkc, mod, &imp->reference, &reference_done, &imp->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_import(imp); return res; } } } else if (!xml_strcmp(val, YANG_K_REVISION_DATE)) { ncx_set_error(&tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); if (revdone) { res = retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); if (imp->revision) { m__free(imp->revision); imp->revision = NULL; } } else { res = NO_ERR; revdone = TRUE; } res = consume_revision_date(tkc, mod, &imp->revision); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_import(imp); return res; } } res = yang_consume_semiapp(tkc, mod, &imp->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_import(imp); return res; } } } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); } } if (imp->revision) { /* validate the revision date */ res = yang_validate_date_string(tkc, mod, &tkerr, imp->revision); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_import(imp); return res; } } } /* check special deviation or search processing mode */ if (pcb->deviationmode || pcb->searchmode) { /* save the import for prefix translation later * do not need to actually import any of the symbols * now; may be a waste of time unless there are * any deviation statements found * don't really care how valid it is right now */ dlq_enque(imp, &mod->importQ); return NO_ERR; } /* check all the mandatory clauses are present */ if (!imp->prefix) { retres = ERR_NCX_DATA_MISSING; tkc->curerr = &imp->tkerr; ncx_mod_missing_err(tkc, mod, "import", "prefix"); } /* check if the import is already present */ if (imp->module && imp->prefix) { /* check if module already present */ testimp = ncx_find_import_test(mod, imp->module); if (testimp) { if (((testimp->revision && imp->revision) || (!testimp->revision && !imp->revision)) && yang_compare_revision_dates(testimp->revision, imp->revision)) { log_error("\nError: invalid duplicate import found on line %u", testimp->tkerr.linenum); retres = res = ERR_NCX_INVALID_DUP_IMPORT; tkc->curerr = &imp->tkerr; ncx_print_errormsg(tkc, mod, res); } else { imp->usexsd = FALSE; if (!xml_strcmp(testimp->prefix, imp->prefix)) { /* warning for exact duplicate import */ if (ncx_warning_enabled(ERR_NCX_DUP_IMPORT)) { log_warn("\nWarning: duplicate import " "found on line %u", testimp->tkerr.linenum); res = ERR_NCX_DUP_IMPORT; tkc->curerr = &imp->tkerr; ncx_print_errormsg(tkc, mod, res); } else { ncx_inc_warnings(mod); } } else if (ncx_warning_enabled(ERR_NCX_PREFIX_DUP_IMPORT)) { /* warning for dup. import w/ different prefix */ log_warn("\nWarning: same import with different prefix" " found on line %u", testimp->tkerr.linenum); res = ERR_NCX_PREFIX_DUP_IMPORT; tkc->curerr = &imp->tkerr; ncx_print_errormsg(tkc, mod, res); } else { ncx_inc_warnings(mod); } } } /* check simple module loop with itself */ if (imp->usexsd && !xml_strcmp(imp->module, mod->name)) { log_error("\nError: import '%s' for current module", mod->name); retres = ERR_NCX_IMPORT_LOOP; tkc->curerr = &imp->tkerr; ncx_print_errormsg(tkc, mod, retres); } /* check simple module loop with the top-level file */ if (imp->usexsd && xml_strcmp(mod->name, pcb->top->name) && !xml_strcmp(imp->module, pcb->top->name)) { log_error("\nError: import loop for top-level %smodule '%s'", (pcb->top->ismod) ? "" : "sub", imp->module); retres = ERR_NCX_IMPORT_LOOP; tkc->curerr = &imp->tkerr; ncx_print_errormsg(tkc, mod, retres); } /* check simple submodule importing its parent module */ if (imp->usexsd && mod->belongs && !xml_strcmp(mod->belongs, imp->module)) { log_error("\nError: submodule '%s' cannot import its" " parent module '%s'", mod->name, imp->module); retres = ERR_NCX_IMPORT_LOOP; tkc->curerr = &imp->tkerr; ncx_print_errormsg(tkc, mod, retres); } /* check prefix for this module corner-case */ if (mod->ismod && !xml_strcmp(imp->prefix, mod->prefix)) { log_error("\nError: import '%s' using " "prefix for current module (%s)", imp->module, imp->prefix); retres = ERR_NCX_IN_USE; tkc->curerr = &imp->tkerr; ncx_print_errormsg(tkc, mod, retres); } /* check if prefix already used in other imports */ testimp = ncx_find_pre_import_test(mod, imp->prefix); if (testimp) { if (xml_strcmp(testimp->module, imp->module)) { retres = ERR_NCX_IN_USE; log_error("\nImport %s on line %u already using prefix %s", testimp->module, testimp->tkerr.linenum, testimp->prefix); tkc->curerr = &imp->tkerr; ncx_print_errormsg(tkc, mod, retres); } } /* check for import loop */ node = yang_find_node(&pcb->impchainQ, imp->module, imp->revision); if (node) { log_error("\nError: loop created by import '%s'" " from module '%s', line %u", imp->module, node->mod->name, node->tkerr.linenum); retres = ERR_NCX_IMPORT_LOOP; tkc->curerr = &imp->tkerr; ncx_print_errormsg(tkc, mod, retres); } } /* save or delete the import struct, except in search mode */ if (retres == NO_ERR && imp->usexsd && !pcb->searchmode) { node = yang_new_node(); if (!node) { retres = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, retres); ncx_free_import(imp); } else { impptr = yang_new_import_ptr(imp->module, imp->prefix, imp->revision); if (!impptr) { retres = ERR_INTERNAL_MEM; tkc->curerr = &imp->tkerr; ncx_print_errormsg(tkc, mod, retres); ncx_free_import(imp); yang_free_node(node); } else { /* save the import used record and the import */ node->name = imp->module; node->revision = imp->revision; ncx_set_error(&node->tkerr, imp->tkerr.mod, imp->tkerr.linenum, imp->tkerr.linepos); /* save the import on the impchain stack */ dlq_enque(node, &pcb->impchainQ); /* save the import for prefix translation */ dlq_enque(imp, &mod->importQ); /* save the import marker to keep a list * of all the imports with no duplicates * regardless of recursion or submodules */ dlq_enque(impptr, &pcb->allimpQ); /* load the module now instead of later for validation * it may not get used, but assume it will * skip if forcing yuma-netconf override of ietf-netconf * or if searchmode and just finding modname, revision */ if (!(imp->force_yuma_nc || pcb->searchmode)) { ncx_module_t *impmod = NULL; /* in all cases, even diff-mode, it should be OK * to reuse the imported module if it has already * been parsed and loaded into the registry. * All modes that need the token chain are for the * top module, never an imported module */ impmod = ncx_find_module(imp->module, imp->revision); if (impmod != NULL) { res = NO_ERR; } else { res = ncxmod_load_imodule(imp->module, imp->revision, pcb, YANG_PT_IMPORT, NULL, &impmod); } /* save the status to prevent retrying this module */ imp->res = res; imp->mod = impmod; } else if (LOGDEBUG) { log_debug("\nSkipping import of ietf-netconf, " "using yuma-netconf instead"); } if (res != NO_ERR) { /* skip error if module has just warnings */ if (get_errtyp(res) < ERR_TYP_WARN) { retres = ERR_NCX_IMPORT_ERRORS; if (imp->revision) { log_error("\nError: '%s' import of " "module '%s' revision '%s' failed", mod->sourcefn, imp->module, imp->revision); } else { log_error("\nError: '%s' import of " "module '%s' failed", mod->sourcefn, imp->module); } tkc->curerr = &imp->tkerr; ncx_print_errormsg(tkc, mod, res); } } /* else ignore the warnings */ /* remove the node in the import chain that * was added before the module was loaded */ node = (yang_node_t *)dlq_lastEntry(&pcb->impchainQ); if (node) { dlq_remove(node); yang_free_node(node); } else { retres = SET_ERROR(ERR_INTERNAL_VAL); } } } } else { ncx_free_import(imp); } return retres; } /* consume_import */ /******************************************************************** * FUNCTION consume_include * * Parse the next N tokens as an include clause * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'include' keyword * * INPUTS: * tkc == token chain * mod == module struct that will get the ncx_import_t * pcb == parser control block * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_include (tk_chain_t *tkc, ncx_module_t *mod, yang_pcb_t *pcb) { ncx_include_t *inc, *testinc; const char *expstr; const xmlChar *val; yang_node_t *node, *testnode; dlq_hdr_t *allQ, *chainQ; ncx_module_t *foundmod, *realmod; tk_type_t tktyp; status_t res, retres; boolean done, revdone; ncx_error_t tkerr; done = FALSE; expstr = "submodule name"; retres = NO_ERR; revdone = FALSE; memset(&tkerr, 0x0, sizeof(tkerr)); /* Get a new ncx_include_t to fill in */ inc = ncx_new_include(); if (!inc) { retres = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, retres); return retres; } else { ncx_set_error(&inc->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); inc->usexsd = TRUE; } /* Get the mandatory submodule name */ res = yang_consume_id_string(tkc, mod, &inc->submodule); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_include(inc); return res; } } /* Get the starting left brace for the sub-clauses * or a semi-colon to end the include statement */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_free_include(inc); ncx_print_errormsg(tkc, mod, res); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: done = TRUE; break; case TK_TT_LBRACE: break; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; } /* get the revision clause and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); ncx_free_include(inc); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_mod_exp_err(tkc, mod, res, expstr); ncx_free_include(inc); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &mod->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_include(inc); return res; } } continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } /* Got a token string so check the value, should be 'prefix' */ if (!xml_strcmp(val, YANG_K_REVISION_DATE)) { ncx_set_error(&tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); if (revdone) { res = retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); if (inc->revision) { m__free(inc->revision); inc->revision = NULL; } } else { res = NO_ERR; revdone = TRUE; } res = consume_revision_date(tkc, mod, &inc->revision); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_include(inc); return res; } } res = yang_consume_semiapp(tkc, mod, &inc->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_include(inc); return res; } } } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); } } if (inc->revision) { /* validate the revision date */ res = yang_validate_date_string(tkc, mod, &tkerr, inc->revision); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_include(inc); return res; } } } /* get the include Qs to use * keep checking parents until the real module is reached */ realmod = mod; while (realmod->parent != NULL) { realmod = realmod->parent; } allQ = &realmod->allincQ; chainQ = &realmod->incchainQ; /* check special scan mode looking for mod info */ if (pcb->searchmode) { /* hand off malloced 'inc' struct here * do not load the sub-module; not being used */ dlq_enque(inc, &mod->includeQ); return NO_ERR; } /* check if the mandatory submodule name is valid * and if the include is already present */ if (!inc->submodule) { retres = ERR_NCX_DATA_MISSING; tkc->curerr = &inc->tkerr; ncx_mod_exp_err(tkc, mod, retres, expstr); } else if (!ncx_valid_name2(inc->submodule)) { retres = ERR_NCX_INVALID_NAME; tkc->curerr = &inc->tkerr; ncx_mod_exp_err(tkc, mod, retres, expstr); } else { /* check if submodule already present */ testinc = ncx_find_include(mod, inc->submodule); if (testinc) { /* check if there is a revision conflict */ if (((testinc->revision && inc->revision) || (!testinc->revision && !inc->revision)) && yang_compare_revision_dates(testinc->revision, inc->revision)) { log_error("\nError: invalid duplicate " "include found on line %u", testinc->tkerr.linenum); retres = res = ERR_NCX_INVALID_DUP_INCLUDE; tkc->curerr = &inc->tkerr; ncx_print_errormsg(tkc, mod, res); } else { /* warning for same duplicate already in the * include statements */ inc->usexsd = FALSE; if (ncx_warning_enabled(ERR_NCX_DUP_INCLUDE)) { log_warn("\nWarning: duplicate include found " "on line %u", testinc->tkerr.linenum); res = ERR_NCX_DUP_INCLUDE; tkc->curerr = &inc->tkerr; ncx_print_errormsg(tkc, mod, res); } else { ncx_inc_warnings(mod); } } } else { /* check simple submodule loop with itself */ if (!xml_strcmp(inc->submodule, mod->name)) { log_error("\nError: include '%s' for current submodule", mod->name); retres = ERR_NCX_INCLUDE_LOOP; tkc->curerr = &inc->tkerr; ncx_print_errormsg(tkc, mod, retres); } /* check simple submodule loop with the top-level file */ if (retres == NO_ERR && xml_strcmp(mod->name, pcb->top->name) && !xml_strcmp(inc->submodule, pcb->top->name)) { log_error("\nError: include loop for top-level %smodule '%s'", (pcb->top->ismod) ? "" : "sub", inc->submodule); retres = ERR_NCX_INCLUDE_LOOP; tkc->curerr = &inc->tkerr; ncx_print_errormsg(tkc, mod, retres); } /* check simple submodule including its parent module */ if (retres == NO_ERR && mod->belongs && !xml_strcmp(mod->belongs, inc->submodule)) { log_error("\nError: submodule '%s' cannot include its" " parent module '%s'", mod->name, inc->submodule); retres = ERR_NCX_INCLUDE_LOOP; tkc->curerr = &inc->tkerr; ncx_print_errormsg(tkc, mod, retres); } /* check for include loop */ if (retres == NO_ERR) { node = yang_find_node(chainQ, inc->submodule, inc->revision); if (node) { log_error("\nError: loop created by include '%s'" " from %smodule '%s', line %u", inc->submodule, (node->mod->ismod) ? "" : "sub", node->mod->name, node->tkerr.linenum); retres = ERR_NCX_INCLUDE_LOOP; tkc->curerr = &inc->tkerr; ncx_print_errormsg(tkc, mod, retres); } } } } /* save or delete the include struct */ if (retres == NO_ERR && inc->usexsd) { node = yang_new_node(); if (!node) { retres = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, retres); ncx_free_include(inc); } else { /* save the include */ node->name = inc->submodule; node->revision = inc->revision; node->mod = mod; ncx_set_error(&node->tkerr, inc->tkerr.mod, inc->tkerr.linenum, inc->tkerr.linepos); /* hand off malloced 'inc' struct here */ dlq_enque(inc, &mod->includeQ); /* check if already parsed, and in the allincQ */ testnode = yang_find_node(allQ, inc->submodule, inc->revision); if (testnode == NULL) { dlq_enque(node, chainQ); foundmod = NULL; /* load the module now instead of later for validation */ retres = ncxmod_load_imodule(inc->submodule, inc->revision, pcb, YANG_PT_INCLUDE, realmod, &foundmod); if (retres != NO_ERR) { if (retres == ERR_NCX_MOD_NOT_FOUND || get_errtyp(res) < ERR_TYP_WARN) { if (inc->revision) { log_error("\nError: include of " "submodule '%s' revision '%s' failed", inc->submodule, inc->revision); } else { log_error("\nError: include of " "submodule '%s' failed", inc->submodule); } tkc->curerr = &inc->tkerr; ncx_print_errormsg(tkc, mod, retres); } else { /* ignore warnings */ retres = NO_ERR; } } /* remove the node in the include chain that * was added before the submodule was loaded */ node = (yang_node_t *)dlq_lastEntry(chainQ); if (node) { dlq_remove(node); if (foundmod == NULL) { yang_free_node(node); } else { /* save this node in the mod->allincQ now * because it may be needed while body-stmts * are being processed and nodes are searched */ node->submod = foundmod; node->res = retres; dlq_enque(node, allQ); /* save a back-ptr in the include directive as well */ inc->submod = foundmod; if (LOGDEBUG3) { if (node->mod && node->submod) { log_debug3("\nAdd node %p with submod " "%p (%s) to mod (%s) " "in Q %p", node, node->submod, node->submod->name, node->mod->name, allQ); } else if (node->submod) { log_debug3("\nAdd node %p with submod %p (%s) " "in Q %p", node, node->submod, node->submod->name, allQ); } else if (node->mod) { log_debug3("\nAdd node %p with mod %p (%s) " "in Q %p", node, node->mod, node->mod->name, allQ); } } } } else { SET_ERROR(ERR_INTERNAL_VAL); } } else { inc->submod = testnode->submod; yang_free_node(node); } } } else { ncx_free_include(inc); } return retres; } /* consume_include */ /******************************************************************** * FUNCTION consume_linkage_stmts * * Parse the next N tokens as N import or include clauses * Create ncx_import structs and add them to the specified module * * Recusively parse any include submodule statement * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module struct that will get the ncx_import_t * pcb == parser control block * OUTPUTS: * consumed == some statements were consumed if TRUE * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_linkage_stmts (tk_chain_t *tkc, ncx_module_t *mod, yang_pcb_t *pcb, boolean *consumed) { const xmlChar *val; const char *expstr; tk_type_t tktyp; status_t res, retres; boolean done; *consumed = FALSE; expstr = "import or include keyword"; res = NO_ERR; retres = NO_ERR; done = FALSE; while (!done) { if (!pcb->keepmode && retres != NO_ERR) { done = TRUE; continue; } /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_mod_exp_err(tkc, mod, res, expstr); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &mod->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: TK_BKUP(tkc); done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_IMPORT)) { res = consume_import(tkc, mod, pcb); CHK_EXIT(res, retres); *consumed = TRUE; } else if (!xml_strcmp(val, YANG_K_INCLUDE)) { res = consume_include(tkc, mod, pcb); CHK_EXIT(res, retres); *consumed = TRUE; } else if (!yang_top_keyword(val)) { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); } else { /* assume we reached the end of this sub-section * if there are simply clauses out of order, then * the compiler will not detect that. */ TK_BKUP(tkc); done = TRUE; } } return retres; } /* consume_linkage_stmts */ /******************************************************************** * FUNCTION consume_meta_stmts * * Parse the meta-stmts and submodule-meta-stmts constructs * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * OUTPUTS: * consumed == some statements were consumed if TRUE * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_meta_stmts (tk_chain_t *tkc, ncx_module_t *mod, boolean *consumed) { const xmlChar *val; const char *expstr; tk_type_t tktyp; status_t res, retres; boolean done, org, contact, descr, ref; *consumed = FALSE; expstr = "meta-statement"; done = FALSE; org = FALSE; contact = FALSE; descr = FALSE; ref = FALSE; res = NO_ERR; retres = NO_ERR; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_mod_exp_err(tkc, mod, res, expstr); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &mod->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: TK_BKUP(tkc); done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_ORGANIZATION)) { /* 'organization' field is present */ res = yang_consume_strclause(tkc, mod, &mod->organization, &org, &mod->appinfoQ); CHK_EXIT(res, retres); *consumed = TRUE; } else if (!xml_strcmp(val, YANG_K_CONTACT)) { res = yang_consume_descr(tkc, mod, &mod->contact_info, &contact, &mod->appinfoQ); CHK_EXIT(res, retres); *consumed = TRUE; } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &mod->descr, &descr, &mod->appinfoQ); CHK_EXIT(res, retres); *consumed = TRUE; } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &mod->ref, &ref, &mod->appinfoQ); CHK_EXIT(res, retres); *consumed = TRUE; } else if (!yang_top_keyword(val)) { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); } else { /* assume we reached the end of this sub-section * if there are simply clauses out of order, then * the compiler will not detect that. */ TK_BKUP(tkc); done = TRUE; } } return retres; } /* consume_meta_stmts */ /******************************************************************** * FUNCTION consume_revision_date * * Parse the next N tokens as a date-arg-str clause * Adds the string form to the specified string object * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * NEXT token is the start of the 'date-arg-str' clause * * INPUTS: * tkc == token chain * mod == module struct that will get the ncx_import_t * revstring == address of return revision date string * * OUTPUTS: * *revstring malloced and set to date-arg-str value * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_revision_date (tk_chain_t *tkc, ncx_module_t *mod, xmlChar **revstring) { #define REVBUFF_SIZE 128 status_t res = NO_ERR; /* get the mandatory version identifier date string */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } if (TK_CUR_STR(tkc)) { *revstring = xml_strdup(TK_CUR_VAL(tkc)); if (!*revstring) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); } } else if (TK_CUR_TYP(tkc)==TK_TT_DNUM) { xmlChar *str = NULL; xmlChar *p; /* assume this is an unquoted date string this code does not detect * corner-cases like 2007 -11 -20 because an unquoted dateTime string * is the same as 3 integers and if there are spaces between them * this will be parsed ok by tk.c */ str = m__getMem(REVBUFF_SIZE); if (!str) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } p = str; p += xml_strcpy(p, TK_CUR_VAL(tkc)); res = ncx_consume_token(tkc, mod, TK_TT_DNUM); if ( NO_ERR == res ) { p += xml_strcpy(p, TK_CUR_VAL(tkc)); res = ncx_consume_token(tkc, mod, TK_TT_DNUM); if ( NO_ERR == res ) { xml_strcpy(p, TK_CUR_VAL(tkc)); *revstring = str; } } if ( NO_ERR != res ) { m__free(str); } } else { const char *expstr = "date-arg-str"; res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, res, expstr); } return res; } /* consume_revision_date */ /******************************************************************** * FUNCTION consume_revision * * Parse the next N tokens as a revision clause * Create a ncx_revhist entry and add it to the specified module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'revision' keyword * * INPUTS: * tkc == token chain * mod == module struct that will get the ncx_import_t * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_revision (tk_chain_t *tkc, ncx_module_t *mod ) { ncx_revhist_t *rev, *testrev; const xmlChar *val; const char *expstr; tk_type_t tktyp; boolean done, descrdone, refdone; status_t res, retres; val = NULL; expstr = "description or reference"; done = FALSE; descrdone = FALSE; refdone = FALSE; res = NO_ERR; retres = NO_ERR; rev = ncx_new_revhist(); if (!rev) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } else { ncx_set_error(&rev->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); } /* get the mandatory version identifier date string */ res = consume_revision_date(tkc, mod, &rev->version); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_revhist(rev); return res; } } /* Get the starting left brace for the sub-clauses * or a semi-colon to end the revision statement */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); ncx_free_revhist(rev); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: done = TRUE; break; case TK_TT_LBRACE: break; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; } /* get the description clause and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); ncx_free_revhist(rev); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); ncx_free_revhist(rev); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &mod->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_revhist(rev); return res; } } continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } /* Got a token str so check the value, should be 'description' */ if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { /* Mandatory 'description' field is present */ res = yang_consume_descr(tkc, mod, &rev->descr, &descrdone, &mod->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_revhist(rev); return res; } } } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { /* Optional 'reference' field is present */ res = yang_consume_descr(tkc, mod, &rev->ref, &refdone, &mod->appinfoQ); if (res != NO_ERR) { retres = res; if (NEED_EXIT(res)) { ncx_free_revhist(rev); return res; } } } else { retres = ERR_NCX_WRONG_TKVAL; log_error("\nError: invalid reference-stmt"); ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } } if (rev->version) { /* check if the version string is valid */ res = yang_validate_date_string(tkc, mod, &rev->tkerr, rev->version); CHK_EXIT(res, retres); /* check if the revision is already present */ testrev = ncx_find_revhist(mod, rev->version); if (testrev && ncx_warning_enabled(ERR_NCX_DUP_DATE_IN_REV_HISTORY)) { log_warn("\nWarning: revisions with same date (%s) in %s line %u", rev->version, mod->name, testrev->tkerr.linenum); tkc->curerr = &rev->tkerr; ncx_print_errormsg(tkc, mod, ERR_NCX_DUP_DATE_IN_REV_HISTORY); } else { ncx_inc_warnings(mod); } } /* save the revision struct */ rev->res = retres; dlq_enque(rev, &mod->revhistQ); return retres; } /* consume_revision */ /******************************************************************** * FUNCTION consume_revision_stmts * * Parse the next N tokens as N revision statements * Create a ncx_revhist struct and add it to the specified module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module struct that will get the ncx_revhist_t entries * pcb == parser control block to use to check rev-date * OUTPUTS: * consumed == consumed statement if TRUE * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_revision_stmts (tk_chain_t *tkc, ncx_module_t *mod, yang_pcb_t *pcb, boolean *consumed) { const xmlChar *val; const char *expstr; ncx_revhist_t *rev; tk_type_t tktyp; status_t res, retres; boolean done; int ret; *consumed = FALSE; expstr = "description keyword"; res = NO_ERR; retres = NO_ERR; done = FALSE; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &mod->appinfoQ); CHK_EXIT(res, retres); continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ case TK_TT_RBRACE: TK_BKUP(tkc); done = TRUE; continue; default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_REVISION)) { res = consume_revision(tkc, mod); CHK_EXIT(res, retres); *consumed = TRUE; } else if (!yang_top_keyword(val)) { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); } else { /* assume we reached the end of this sub-section * if there are simply clauses out of order, then * the compiler will not detect that. */ TK_BKUP(tkc); done = TRUE; } } /* search the revision date strings, find the * highest value date string */ val = NULL; for (rev = (ncx_revhist_t *)dlq_firstEntry(&mod->revhistQ); rev != NULL; rev = (ncx_revhist_t *)dlq_nextEntry(rev)) { if (rev->res == NO_ERR) { if (!val) { /* first pass through loop */ val = rev->version; } else { ret = yang_compare_revision_dates(rev->version, val); if (ret > 0) { if (ncx_warning_enabled(ERR_NCX_BAD_REV_ORDER)) { log_warn("\nWarning: revision dates not in " "descending order"); tkc->curerr = &rev->tkerr; ncx_print_errormsg(tkc, mod, ERR_NCX_BAD_REV_ORDER); } else { ncx_inc_warnings(mod); } val = rev->version; } } } } /* assign the module version string */ if (val) { /* valid date string found */ mod->version = xml_strdup(val); } /* check if revision warnings should be skipped */ if (pcb->searchmode) { return retres; } /* leave the version NULL if no good revision dates found */ if (!val) { if (ncx_warning_enabled(ERR_NCX_NO_REVISION)) { log_warn("\nWarning: no revision statements " "for %smodule '%s'", (mod->ismod) ? "" : "sub", mod->name); mod->warnings++; } else { ncx_inc_warnings(mod); } } else if (pcb->revision && yang_compare_revision_dates(val, pcb->revision)) { log_error("\nError: found version '%s' instead of " "requested version '%s'", (val) ? val : EMPTY_STRING, pcb->revision); retres = ERR_NCX_WRONG_VERSION; ncx_print_errormsg(tkc, mod, retres); } return retres; } /* consume_revision_stmts */ /******************************************************************** * FUNCTION consume_body_stmts * * Parse the next N tokens as N body statements * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module struct in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_body_stmts (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod) { const char *expstr = "body statement"; status_t res = NO_ERR, retres = NO_ERR; boolean done = FALSE; while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); if (res == ERR_NCX_EOF) { res = ERR_NCX_MISSING_RBRACE; } return res; } tk_type_t tktyp = TK_CUR_TYP(tkc); const xmlChar *val = TK_CUR_VAL(tkc); const xmlChar *objprefix = TK_CUR_MOD(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_mod_exp_err(tkc, mod, res, expstr); return res; case TK_TT_RBRACE: /* found end of module */ TK_BKUP(tkc); return retres; case TK_TT_MSTRING: /* vendor-specific clause found instead */ if(!xml_strcmp(val, YANG_K_AUGMENT) && !xml_strcmp(objprefix, "direct-must-augment-ex")) { /* direct-must-augment-ex:augment parse as normal augment and allow direct must sub-statements */ break; } else { res = ncx_consume_appinfo(tkc, mod, &mod->appinfoQ); CHK_EXIT(res, retres); continue; } case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); yang_skip_statement(tkc, mod); continue; } /* Got a token string so check the keyword value * separate the top-level only statements from * the data-def-stmts, which can appear within * groupings and nested objects */ if (!xml_strcmp(val, YANG_K_EXTENSION)) { res = yang_ext_consume_extension(tkc, mod); } else if (!xml_strcmp(val, YANG_K_FEATURE)) { res = consume_feature(tkc, mod); } else if (!xml_strcmp(val, YANG_K_IDENTITY)) { res = consume_identity(tkc, mod); } else if (!xml_strcmp(val, YANG_K_TYPEDEF)) { res = yang_typ_consume_typedef( pcb, tkc, mod, &mod->typeQ ); } else if (!xml_strcmp(val, YANG_K_GROUPING)) { res = yang_grp_consume_grouping( pcb, tkc, mod, &mod->groupingQ, NULL ); } else if (!xml_strcmp(val, YANG_K_RPC)) { res = yang_obj_consume_rpc( pcb, tkc, mod ); } else if (!xml_strcmp(val, YANG_K_NOTIFICATION)) { res = yang_obj_consume_notification( pcb, tkc, mod, &mod->datadefQ, NULL, NULL ); } else if (!xml_strcmp(val, YANG_K_AUGMENT)) { res = yang_obj_consume_augment( pcb, tkc, mod ); } else if (!xml_strcmp(val, YANG_K_DEVIATION)) { res = yang_obj_consume_deviation( pcb, tkc, mod ); } else { res = yang_obj_consume_datadef( pcb, tkc, mod, &mod->datadefQ, NULL ); } CHK_EXIT(res, retres); } return retres; } /* consume_body_stmts */ /******************************************************************** * FUNCTION check_module_name * * Check if the module name part of the sourcefn is the * same as the mod name * * INPUTS: * mod == ncx_module in progress * * RETURNS: * TRUE if name check OK * FALSE if name check failed *********************************************************************/ static boolean check_module_name (ncx_module_t *mod) { const xmlChar *str; boolean ret = TRUE; uint32 len1, len2; if (mod->sourcefn != NULL && mod->name != NULL) { str = mod->sourcefn; while (*str != '\0' && *str != '@') { str++; } if (*str == '@') { len1 = (uint32)(str - mod->sourcefn); } else { len1 = xml_strlen(mod->sourcefn); if (len1 > 2) { str = &mod->sourcefn[len1-1]; while (str > mod->sourcefn && *str != '.') { str--; } if (str > mod->sourcefn) { len1 = (uint32)(str - mod->sourcefn); } else { return FALSE; } } else { return FALSE; } } len2 = xml_strlen(mod->name); if (len1 != len2) { ret = FALSE; } else if (xml_strncmp(mod->sourcefn, mod->name, len1)) { ret = FALSE; } } return ret; } /* check_module_name */ /******************************************************************** * FUNCTION parse_yang_module * * Parse, generate and register one ncx_module_t struct * from an NCX instance document stream containing one NCX module, * which conforms to the NcxModule ABNF. * * This is just the first pass parser. * Many constructs are not validated or internal data structures * completed yet, after this pass is done. * This allows support for syntax error checking first, * and support for forward references i the DML * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == ncx_module in progress * pcb == YANG parser control block * ptyp == yang parse type * wasadded == pointer to return registry-added flag * * OUTPUTS: * *wasadded == TRUE if the module was addeed to the NCX moduleQ * == FALSE if error-exit, not added * * RETURNS: * status of the operation *********************************************************************/ static status_t parse_yang_module (tk_chain_t *tkc, ncx_module_t *mod, yang_pcb_t *pcb, yang_parsetype_t ptyp, boolean *wasadded) { yang_node_t *node; ncx_feature_t *feature; ncx_identity_t *identity; boolean ismain, loaded, ietfnetconf, done; status_t res, retres; #ifdef YANG_PARSE_DEBUG_TRACE if (LOGDEBUG3) { log_debug3("\nEnter parse_yang_module"); if (mod->sourcefn) { log_debug3(" %s", mod->sourcefn); } yang_dump_nodeQ(&pcb->impchainQ, "impchainQ"); yang_dump_nodeQ(&pcb->incchainQ, "incchainQ"); if (mod->ismod) { yang_dump_nodeQ(&mod->allincQ, "allincQ"); } } #endif loaded = FALSE; ietfnetconf = FALSE; ismain = TRUE; retres = NO_ERR; *wasadded = FALSE; /* could be module or submodule -- get the first keyword */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); } else if (TK_CUR_ID(tkc)) { /* got an ID token, make sure it is correct to continue */ res = NO_ERR; if (!xml_strcmp(TK_CUR_VAL(tkc), YANG_K_MODULE)) { ismain = TRUE; TK_BKUP(tkc); res = ncx_consume_name(tkc, mod, YANG_K_MODULE, &mod->name, NCX_REQ, TK_TT_LBRACE); } else if (!xml_strcmp(TK_CUR_VAL(tkc), YANG_K_SUBMODULE)) { ismain = FALSE; TK_BKUP(tkc); res = ncx_consume_name(tkc, mod, YANG_K_SUBMODULE, &mod->name, NCX_REQ, TK_TT_LBRACE); } else { res = ERR_NCX_WRONG_TKVAL; ncx_print_errormsg(tkc, mod, res); } #ifdef YANG_PARSE_DEBUG_MEMORY if (res == NO_ERR && LOGDEBUG3) { log_debug3(" malloced %p %smodule '%s'", mod, ismain ? "" : "sub", mod->name); } #endif } else { res = ERR_NCX_WRONG_TKTYPE; ncx_print_errormsg(tkc, mod, res); } CHK_EXIT(res, retres); /* exit on all errors, since this is probably not a YANG file */ if (retres != NO_ERR) { return res; } /* make sure the deviation or annotation parameter is pointing at * a module and not a submodule, if this is deviation mode */ if (pcb->deviationmode && !ismain) { res = ERR_NCX_EXP_MODULE; log_error("\nError: deviation or annotation " "cannot be a submodule"); ncx_print_errormsg(tkc, mod, res); return res; } /* got a start of [sub]module OK, check the parse type */ switch (ptyp) { case YANG_PT_TOP: if (pcb->with_submods && !ismain) { return ERR_NCX_SKIPPED; } else { pcb->top = mod; } break; case YANG_PT_INCLUDE: if (ismain) { log_error("\nError: including module '%s', " "should be submodule", mod->name); retres = ERR_NCX_EXP_SUBMODULE; ncx_print_errormsg(tkc, mod, retres); return retres; } break; case YANG_PT_IMPORT: if (!ismain) { log_error("\nError: importing submodule '%s', " "should be module", mod->name); retres = ERR_NCX_EXP_MODULE; ncx_print_errormsg(tkc, mod, retres); return retres; } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } #ifdef YANG_PARSE_DEBUG if (res == NO_ERR && LOGDEBUG2) { log_debug2("\nyang_parse: Start %smodule '%s' OK", ismain ? "" : "sub", mod->name); } #endif mod->ismod = ismain; /* check if the file name matches the [sub]module name given */ if (check_module_name(mod) == FALSE) { if (ncx_warning_enabled(ERR_NCX_FILE_MOD_MISMATCH)) { log_warn("\nWarning: base file name in '%s' should match " "the %smodule name '%s'", mod->sourcefn, ismain ? "" : "sub", mod->name); ncx_print_errormsg(tkc, mod, ERR_NCX_FILE_MOD_MISMATCH); } else { ncx_inc_warnings(mod); } } if (ismain) { /* consume module-header-stmts */ res = consume_mod_hdr(tkc, mod); CHK_EXIT(res, retres); } else { /* consume submodule-header-stmts */ res = consume_submod_hdr(tkc, mod); CHK_EXIT(res, retres); } if (mod->prefix == NULL) { if (retres == NO_ERR) { SET_ERROR(ERR_INTERNAL_VAL); retres = ERR_NCX_INVALID_VALUE; } log_error("\nError: cannot continue without module prefix set"); return retres; } if (mod->ismod && mod->ns == NULL) { if (retres == NO_ERR) { SET_ERROR(ERR_INTERNAL_VAL); retres = ERR_NCX_INVALID_VALUE; } log_error("\nError: cannot continue without module namespace set"); return retres; } /* set the namespace and the XML prefix now, * so all namespace assignments in XPath * expressions will be valid when first parsed */ if (!(pcb->deviationmode || pcb->diffmode || pcb->searchmode)) { /* add real or temp module NS to the registry */ res = ncx_add_namespace_to_registry(mod, pcb->parsemode); CHK_EXIT(res, retres); } /* loop until no more pre-body stmts left * since the canonical order is not mandatory */ done=FALSE; while(!done) { boolean consumed_linkage_stmts = FALSE; boolean consumed_meta_stmts = FALSE; boolean consumed_revision_stmts = FALSE; done = TRUE; /* Get the linkage statements (imports, include) */ res = consume_linkage_stmts(tkc, mod, pcb, &consumed_linkage_stmts); CHK_EXIT(res, retres); if (!pcb->keepmode && retres != NO_ERR) { if (LOGDEBUG) { log_debug("\nStop parsing '%s' due to linkage errors", mod->name); } return retres; } /* Get the meta statements (organization, etc.) */ res = consume_meta_stmts(tkc, mod, &consumed_meta_stmts); CHK_EXIT(res, retres); /* Get the revision statements */ res = consume_revision_stmts(tkc, mod, pcb, &consumed_revision_stmts); CHK_EXIT(res, retres); /* make sure there is at least name and prefix to continue * do not continue if requested version does not match */ if (retres == ERR_NCX_WRONG_VERSION || !mod->name || (mod->ismod && !mod->prefix) || !*mod->name || (mod->ismod && !*mod->prefix)) { return retres; } /* check for early exit if just searching for modules */ if (pcb->searchmode) { return retres; } /* special hack -- check if this is the ietf-netconf * YANG module, in which case do not load it because * the internal netconf.yang needs to be used instead * * Only do this hack in the client and server, * if yuma-netconf.yang is already loaded. */ if (mod->ismod && !xml_strcmp(mod->name, NCXMOD_IETF_NETCONF)) { if (ncx_find_module(NCXMOD_NETCONF, NULL) != NULL) { mod->nsid = xmlns_nc_id(); ietfnetconf = TRUE; } } /* check if this module is already loaded, except in diff mode * this cannot be done until the module revision is known, which * unfortunately is not at the start of the module, but rather * after all the other headers */ if (mod->ismod && !pcb->diffmode && ncx_find_module(mod->name, mod->version)) { switch (ptyp) { case YANG_PT_TOP: loaded = TRUE; break; case YANG_PT_IMPORT: return NO_ERR; default: ; } } if(/*consumed_linkage_stmts || */ consumed_meta_stmts || consumed_revision_stmts) { done=FALSE; } } /* Get the definition statements */ res = consume_body_stmts(pcb, tkc, mod); CHK_EXIT(res, retres); if (res != ERR_NCX_EOF) { if (res != ERR_NCX_MISSING_RBRACE) { /* the next node should be the '(sub)module' end node */ res = ncx_consume_token(tkc, mod, TK_TT_RBRACE); if (res != ERR_NCX_EOF) { CHK_EXIT(res, retres); /* check extra tokens left over */ res = TK_ADV(tkc); if (res == NO_ERR) { retres = ERR_NCX_EXTRA_NODE; log_error("\nError: Extra input after end of module" " starting on line %u", TK_CUR_LNUM(tkc)); ncx_print_errormsg(tkc, mod, retres); } } } } /* else errors already reported; inside body-stmts */ /**************** Module Validation *************************/ if (pcb->deviationmode) { /* Check any deviations, record the module name * and save the deviation in the global * deviationQ within the parser control block */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve deviations"); } res = yang_obj_resolve_deviations(pcb, tkc, mod); return res; } /* check all the module level extension usage */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve appinfoQ"); } res = ncx_resolve_appinfoQ(pcb, tkc, mod, &mod->appinfoQ); CHK_EXIT(res, retres); /* check all the module level extension usage * within the include, import, and feature statements */ res = resolve_mod_appinfo(pcb, tkc, mod); CHK_EXIT(res, retres); /* resolve any if-feature statements within the featureQ */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve features"); } for (feature = (ncx_feature_t *)dlq_firstEntry(&mod->featureQ); feature != NULL; feature = (ncx_feature_t *)dlq_nextEntry(feature)) { res = resolve_feature(pcb, tkc, mod, feature); CHK_EXIT(res, retres); } /* check for any if-feature loops caused by this module */ for (feature = (ncx_feature_t *)dlq_firstEntry(&mod->featureQ); feature != NULL; feature = (ncx_feature_t *)dlq_nextEntry(feature)) { res = check_feature_loop(tkc, mod, feature, feature); CHK_EXIT(res, retres); } /* resolve the base-stmt within any identity statements * within the identityQ */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve identities"); } for (identity = (ncx_identity_t *)dlq_firstEntry(&mod->identityQ); identity != NULL; identity = (ncx_identity_t *)dlq_nextEntry(identity)) { res = resolve_identity(pcb, tkc, mod, identity); CHK_EXIT(res, retres); } /* resolve any identity base loops within the identityQ */ for (identity = (ncx_identity_t *)dlq_firstEntry(&mod->identityQ); identity != NULL; identity = (ncx_identity_t *)dlq_nextEntry(identity)) { res = check_identity_loop(tkc, mod, identity, identity); CHK_EXIT(res, retres); } /* Validate any module-level typedefs */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve typedefs"); } res = yang_typ_resolve_typedefs(pcb, tkc, mod, &mod->typeQ, NULL); CHK_EXIT(res, retres); /* Validate any module-level groupings */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve groupings"); } res = yang_grp_resolve_groupings(pcb, tkc, mod, &mod->groupingQ, NULL); CHK_EXIT(res, retres); /* Validate any module-level data-def-stmts */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve datadefs"); } res = yang_obj_resolve_datadefs(pcb, tkc, mod, &mod->datadefQ); CHK_EXIT(res, retres); /* Expand and validate any uses-stmts within module-level groupings */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve groupings complete"); } res = yang_grp_resolve_complete(pcb, tkc, mod, &mod->groupingQ, NULL); CHK_EXIT(res, retres); /* Expand and validate any uses-stmts within module-level datadefs */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve uses"); } res = yang_obj_resolve_uses(pcb, tkc, mod, &mod->datadefQ); CHK_EXIT(res, retres); /* Expand and validate any augment-stmts within module-level datadefs */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve augments"); } res = yang_obj_resolve_augments(pcb, tkc, mod, &mod->datadefQ); CHK_EXIT(res, retres); /* Expand and validate any deviation-stmts within the module * !!! This must be done before any object pointers such * !!! as leafref paths and must/when XPath expressions * !!! are cached. The xpath_find_schema_target_int * !!! function should be used instead of caching leafref * !!! target object pointers */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve deviations"); } res = yang_obj_resolve_deviations(pcb, tkc, mod); CHK_EXIT(res, retres); /* One final check for grouping integrity */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve grp final"); } res = yang_grp_resolve_final(pcb, tkc, mod, &mod->groupingQ); CHK_EXIT(res, retres); /* Final check for object integrity */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve obj final"); } res = yang_obj_resolve_final(pcb, tkc, mod, &mod->datadefQ, FALSE); CHK_EXIT(res, retres); /* Validate all the XPath expressions within all cooked objects */ if (mod->ismod || pcb->top == mod) { /* this will resolve all the XPath usage within * this module or submodule; * need to wait until all the includes are processed for * the imported module, top module, or top submodule */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve xpath"); } res = yang_obj_resolve_xpath(tkc, mod, &mod->datadefQ); CHK_EXIT(res, retres); /* fill in all the list keys in cross-submodule augments */ for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { res = yang_obj_top_resolve_final(pcb, tkc, node->submod, &node->submod->datadefQ); CHK_EXIT(res, retres); /* resolve XPath in submodules */ res = yang_obj_resolve_xpath(tkc, node->submod, &node->submod->datadefQ); CHK_EXIT(res, retres); } } /* Final augment expand * List keys that were not cloned in yang_obj_resolve_augment * so another check for cloned lists is needed here */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve augments final"); } res = yang_obj_resolve_augments_final(pcb, tkc, mod, &mod->datadefQ); CHK_EXIT(res, retres); /* fill in all the list keys in cross-submodule augments */ for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod == NULL) { continue; } /* check submod augmenting external modules */ res = yang_obj_resolve_augments_final(pcb, tkc, node->submod, &node->submod->datadefQ); CHK_EXIT(res, retres); } /* Final XPath check * defvals for XPath leafs (& leafref) were not checked * in yang_obj_resolve_xpath so another check for * these leafs with defaults is needed */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve XPath final"); } res = yang_obj_resolve_xpath_final(pcb, tkc, mod, &mod->datadefQ); CHK_EXIT(res, retres); /* final XPath check for all sub-modules */ for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod == NULL) { continue; } /* check submod augmenting external modules */ res = yang_obj_resolve_xpath_final(pcb, tkc, node->submod, &node->submod->datadefQ); CHK_EXIT(res, retres); } /* final check of defvals in typedefs */ if (LOGDEBUG4) { log_debug4("\nyang_parse: resolve XPath final"); } res = yang_typ_resolve_typedefs_final(tkc, mod, &mod->typeQ); CHK_EXIT(res, retres); /* final XPath check in typedefs for all sub-modules */ for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod == NULL) { continue; } /* check submod augmenting external modules */ res = yang_typ_resolve_typedefs_final(tkc, node->submod, &node->submod->typeQ); CHK_EXIT(res, retres); } /* !!! TBD!!! * !!! Need to add final XPath check for grouping * !!! only objects expanded from uses will have * !!! this final check; unused groupings will not * !!! be checked */ } /* check for loops in any leafref XPath targets */ res = yang_obj_check_leafref_loops(tkc, mod, &mod->datadefQ); CHK_EXIT(res, retres); /* Check for imports not used warnings */ yang_check_imports_used(tkc, mod); /* save the module parse status */ mod->status = retres; /* make sure there is at least name and prefix to continue */ if (!mod->name || !ncx_valid_name2(mod->name)) { return retres; } if (mod->ismod) { if (!mod->prefix || !ncx_valid_name2(mod->prefix)) { return retres; } } else if (mod->prefix) { if (mod->ismod && !ncx_valid_name2(mod->prefix)) { return retres; } } else { mod->prefix = xml_strdup(EMPTY_STRING); if (!mod->prefix) { return ERR_INTERNAL_MEM; } } /* add the NS definitions to the registry; * check the parse type first */ switch (ptyp) { case YANG_PT_TOP: case YANG_PT_IMPORT: /* finish up the import or top module */ if (mod->ismod || pcb->top == mod) { /* update the result, errors, and warnings */ if (mod->ismod) { res = NO_ERR; done = FALSE; for (node = (yang_node_t *) dlq_firstEntry(&mod->allincQ); node != NULL && !done; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { if (node->submod->status != NO_ERR) { mod->status = retres = node->submod->status; if (NEED_EXIT(node->submod->status)) { done = TRUE; } } mod->errors += node->submod->errors; mod->warnings += node->submod->warnings; } /* else include not found */ } } /* add this regular module to the registry */ if (!loaded && retres == NO_ERR) { if (ietfnetconf) { res = NO_ERR; } else if (pcb->diffmode || pcb->parsemode) { res = ncx_add_to_modQ(mod); } else { res = ncx_add_to_registry(mod); } if (res != NO_ERR) { retres = res; } else { /* if mod==top, and top is a submodule, then * yang_free_pcb will delete the submodule later */ if (!ietfnetconf && mod->ismod) { *wasadded = TRUE; } } } } else if (mod->ns) { mod->nsid = xmlns_find_ns_by_name(mod->ns); } break; case YANG_PT_INCLUDE: /* set to TRUE because module is live in the realmod allincQ */ *wasadded = TRUE; break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); } return retres; } /* parse_yang_module */ /******************************************************************** * FUNCTION set_source * * Set the [sub]module source * * INPUTS: * mod == ncx_module_t struct in progress * str == source string to use *********************************************************************/ static void set_source (ncx_module_t *mod, xmlChar *str) { /* save the source of this ncx-module for monitor / debug * hand off malloced src string here */ mod->source = str; /* find the start of the file name */ mod->sourcefn = &str[xml_strlen(str)]; while (mod->sourcefn > str && *mod->sourcefn != NCX_PATHSEP_CH) { mod->sourcefn--; } if (*mod->sourcefn == NCX_PATHSEP_CH) { mod->sourcefn++; } } /* set_source */ /******************************************************************** * FUNCTION load_yang_module * * Load a Yang Module * * INPUTS: * * OUTPUTS: * res the result of the operation. *********************************************************************/ static status_t load_yang_module ( const xmlChar *filespec, const xmlChar *filename, tk_chain_t **tkc ) { FILE *fp = NULL; log_debug( "\nLoading YANG module from file:\n %s", filespec ); /* open the YANG source file for reading */ fp = fopen( (const char *)filename, "r" ); if (!fp) { return ERR_NCX_MISSING_FILE; } /* setup the token chain to parse this YANG file */ /* get a new token chain */ *tkc = tk_new_chain(); if ( !*tkc ) { log_error("\nyang_parse malloc error"); fclose(fp); return ERR_INTERNAL_MEM; } else { // hand off management of fp tk_setup_chain_yang( *tkc, fp, filename ); } return NO_ERR; } /******************************************************************** * FUNCTION load_yin_module * * Load a Yin Module * * INPUTS: * * OUTPUTS: * res the result of the operation. *********************************************************************/ static status_t load_yin_module ( const xmlChar *filespec, const xmlChar *filename, tk_chain_t **tkc ) { status_t res = NO_ERR; log_debug( "\nLoading YIN module from file:\n %s", filespec ); /* just make sure the file exists for now */ if ( !ncxmod_test_filespec( filename ) ) { return ERR_NCX_MISSING_FILE; } /* setup the token chain to parse this YANG file */ /* get a new token chain */ *tkc = yinyang_convert_token_chain( filename, &res ); if ( !*tkc ) { log_error( "\nyang_parse: Invalid YIN file (%s)", get_error_string(res) ); if ( NO_ERR == res ) { res = ERR_INTERNAL_MEM; } } return res; } /******************************************************************** * FUNCTION configure_module * * Allocate and configre an NCX_Module. * * INPUTS: * * * OUTPUTS: * res the result of the operation. *********************************************************************/ static ncx_module_t* configure_module( xmlChar *filename, yang_pcb_t *pcb ) { ncx_module_t *mod = NULL; mod = ncx_new_module(); if (mod) { set_source(mod, filename); /* hand off 'str' malloced memory here */ /* set the back-ptr to parent of this submodule * or NULL if this is not a submodule */ mod->parent = pcb->parentparm; /* set the stmt-track mode flag to the master flag in the PCB */ mod->stmtmode = pcb->stmtmode; } return mod; } /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION yang_parse_from_filespec * * Parse a file as a YANG module * * Error messages are printed by this function!! * * INPUTS: * filespec == absolute path or relative path * This string is used as-is without adjustment. * pcb == parser control block used as very top-level struct * ptyp == parser call type * YANG_PT_TOP == called from top-level file * YANG_PT_INCLUDE == called from an include-stmt in a file * YANG_PT_IMPORT == called from an import-stmt in a file * isyang == TRUE if a YANG file is expected * FALSE if a YIN file is expected * * OUTPUTS: * an ncx_module is filled out and validated as the file * is parsed. If no errors: * TOP, IMPORT: * the module is loaded into the definition registry with * the ncx_add_to_registry function * INCLUDE: * the submodule is loaded into the top-level module, * specified in the pcb * * RETURNS: * status of the operation *********************************************************************/ status_t yang_parse_from_filespec (const xmlChar *filespec, yang_pcb_t *pcb, yang_parsetype_t ptyp, boolean isyang) { tk_chain_t *tkc = NULL; ncx_module_t *mod = NULL; xmlChar *str; status_t res = NO_ERR ; boolean wasadd = FALSE; boolean keepmod = FALSE; #ifdef DEBUG if (!filespec || !pcb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* expand and copy the filespec */ str = ncx_get_source(filespec, &res); if ( !str || NO_ERR != res ) { if ( str ) { m__free( str ); } else { res = ERR_INTERNAL_MEM; } return res; } res = (isyang) ? load_yang_module( filespec, str, &tkc ) : load_yin_module( filespec, str, &tkc ); if ( !tkc ) { m__free( str ); return ( NO_ERR == res ) ? ERR_INTERNAL_MEM : res; } else if ( NO_ERR != res ) { tk_free_chain(tkc); m__free( str ); return res; } if ( pcb->docmode && (ptyp == YANG_PT_TOP || ptyp == YANG_PT_INCLUDE)) { tk_setup_chain_docmode(tkc); } mod = configure_module( str, pcb ); if ( !mod ) { m__free( str ); if ( tkc->fp ) { fclose( tkc->fp ); } tk_free_chain(tkc); return ERR_INTERNAL_MEM; } if (isyang) { /* serialize the file into language tokens * !!! need to change this later because it may use too * !!! much memory in embedded parsers */ res = tk_tokenize_input(tkc, mod); if ( NO_ERR != res ) { ncx_free_module(mod); if ( tkc->fp ) { fclose( tkc->fp ); } tk_free_chain(tkc); return res; } } #ifdef YANG_PARSE_TK_DEBUG tk_dump_chain(tkc); #endif /* parse the module and validate it only if a token chain * was properly parsed; set this only once for the top module */ if ( pcb->savetkc && !pcb->docmode && pcb->tkc == NULL) { pcb->tkc = tk_clone_chain(tkc); if (pcb->tkc == NULL) { ncx_free_module(mod); if ( tkc->fp ) { fclose( tkc->fp ); } tk_free_chain(tkc); return ERR_INTERNAL_MEM; } } res = parse_yang_module( tkc, mod, pcb, ptyp, &wasadd ); if (pcb->top == mod) { pcb->topadded = wasadd; pcb->retmod = NULL; } else if (pcb->top) { /* just parsed an import or include */ pcb->retmod = mod; } else { /* the module did not start correctly */ pcb->retmod = NULL; } if (res != NO_ERR) { /* cleanup in all modes if there was an error */ if (pcb->retmod != NULL) { /* got a submodule or import module that is not top make sure * this does not end up in the wrong Q and get freed * incorrectly if import/include mismatch */ if ((res == ERR_NCX_SKIPPED) || (ptyp == YANG_PT_IMPORT && !mod->ismod) || (ptyp == YANG_PT_INCLUDE && mod->ismod)) { pcb->retmod = NULL; ncx_free_module(mod); mod = NULL; } } if (mod != NULL && (pcb->top == NULL || (!wasadd && !pcb->keepmode))) { /* module was not added to registry so it is live this will be * skipped for an include file because wasadd is always set to * TRUE for submods; this is an invalid module if top not set */ if (pcb->top == mod) { pcb->top = NULL; } pcb->retmod = NULL; ncx_free_module(mod); mod = NULL; } /* else the module will be freed in ncx_cleanup or ncx_free_module * for a submodule */ } else if (pcb->deviationmode) { /* toss the module even if NO_ERR, not needed since the savedevQ * contains the deviations and annotations */ if ( pcb->top == mod) { pcb->top = NULL; } pcb->retmod = NULL; ncx_free_module(mod); mod = NULL; } else if (!wasadd) { /* module was not added to the registry which means it was already * there or it is a submodule; decide if the caller really wants * the return module in this case */ if (pcb->parsemode) { /* parse mode for yangcli_autoload do not need to keep the * duplicate */ if ( pcb->top == mod) { pcb->top = NULL; } pcb->retmod = NULL; ncx_free_module(mod); mod = NULL; } else if (!(pcb->diffmode || pcb->searchmode)) { /* do not swap out diffmode or searchmode for all other modes, * check if the new module should be returned or a different * module for the yuma-netconf hack */ if (mod->ismod) { if (pcb->top == mod) { /* hack: make sure netconf-ietf does not get swapped * out for netconf.yang; the internal version is used * instead of the standard one to fill in the missing * pieces */ if (pcb->keepmode) { keepmod = TRUE; } else if (xml_strcmp(mod->name, NCXMOD_IETF_NETCONF)) { /* swap with the real module already done */ pcb->top = ncx_find_module(mod->name, mod->version); pcb->retmod = NULL; } else { keepmod = TRUE; } } } else { /* this is a submodule, make sure it was included */ if (ptyp != YANG_PT_IMPORT) { if (pcb->keepmode) { keepmod = TRUE; } } if (!pcb->with_submods) { /* this is a submodule and they are being processed * instead of skipped; sub tree parsing mode can cause * top-level to already be loaded into the registry, * swap out the new dummy module with the real one */ if (!pcb->keepmode && pcb->top == mod) { /* !!!! leave this out now !!! pcb->top = ncx_find_module(mod->belongs, mod->version); pcb->retmod = mod; */ pcb->retmod = NULL; } } else if (pcb->top == mod) { /* don't care about submods in this mode so clear * the top pointer so it won't be used */ pcb->retmod = NULL; pcb->top = NULL; } } if (!pcb->top && !pcb->with_submods) { if (mod->ismod) { res = ERR_NCX_MOD_NOT_FOUND; } else if (!keepmod) { res = ERR_NCX_SUBMOD_NOT_LOADED; } else { res = NO_ERR; } } if (!keepmod) { ncx_free_module(mod); mod = NULL; } } } else { /* was added */ keepmod = (pcb->keepmode && pcb->top == mod) ? TRUE : FALSE; } /* final cleanup */ if ( tkc->fp ) { fclose( tkc->fp ); tkc->fp = NULL; } if (keepmod && pcb->tkc == NULL) { /* this is still NULL because pcb->docmode is TRUE and the altered * token chain is desired, not * the clone like YIN parsing */ pcb->tkc = tkc; } else { tk_free_chain(tkc); } return res; } /* yang_parse_from_filespec */ /* END file yang_parse.c */ yuma123_2.14/netconf/src/ncx/xml_msg.c0000664000175000017500000007551714770023131017767 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xml_msg.c XML Message send and receive Deals with generic namespace and xmlns optimization and tries to keep changing the default namespace so most nested elements do not have prefixes Deals with the NETCONF requirement that the attributes in are returned in unchanged. Although XML allows the xnmlns prefixes to change, the same prefixes are used in the that the NMS provided in the . ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 14jan07 abb begun; split from agt_rpc.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "def_reg.h" #include "dlq.h" #include "ncx.h" #include "ncxconst.h" #include "rpc_err.h" #include "status.h" #include "xmlns.h" #include "xml_msg.h" #include "xml_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* #define XML_MSG_DEBUG 1 */ #define MAX_PREFIX_TRIES 26 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION add_pmap * * Add a prefix mapping entry * * INPUTS: * msg == message to search * newpmap == xmlns_pmap_t struct to add * * RETURNS: * none *********************************************************************/ static void add_pmap (xml_msg_hdr_t *msg, xmlns_pmap_t *newpmap) { xmlns_pmap_t *pmap; /* add the new prefix mapping */ for (pmap = (xmlns_pmap_t *)dlq_firstEntry(&msg->prefixQ); pmap != NULL; pmap = (xmlns_pmap_t *)dlq_nextEntry(pmap)) { if (newpmap->nm_id < pmap->nm_id) { dlq_insertAhead(newpmap, pmap); return; } } dlq_enque(newpmap, &msg->prefixQ); } /* add_pmap */ /******************************************************************** * FUNCTION find_pmap * * Find the pmap entry for the specified namespace ID * * INPUTS: * msg == message to search * nsid == namespace ID to find * * RETURNS: * pointer to prefix if found, else NULL if not found *********************************************************************/ static xmlns_pmap_t * find_pmap (xml_msg_hdr_t *msg, xmlns_id_t nsid) { xmlns_pmap_t *pmap; for (pmap = (xmlns_pmap_t *)dlq_firstEntry(&msg->prefixQ); pmap != NULL; pmap = (xmlns_pmap_t *)dlq_nextEntry(pmap)) { if (pmap->nm_id == nsid) { return pmap; } else if (pmap->nm_id > nsid) { return NULL; } } return NULL; } /* find_pmap */ /******************************************************************** * FUNCTION find_prefix * * Find the namespace prefix for the specified namespace ID * * INPUTS: * msg == message to search * nsid == namespace ID to find * * RETURNS: * pointer to prefix if found, else NULL if not found *********************************************************************/ static const xmlChar * find_prefix (xml_msg_hdr_t *msg, xmlns_id_t nsid) { const xmlns_pmap_t *pmap; for (pmap = (const xmlns_pmap_t *)dlq_firstEntry(&msg->prefixQ); pmap != NULL; pmap = (const xmlns_pmap_t *)dlq_nextEntry(pmap)) { if (pmap->nm_id == nsid) { return (const xmlChar *)pmap->nm_pfix; } else if (pmap->nm_id > nsid) { return NULL; } } return NULL; } /* find_prefix */ /******************************************************************** * FUNCTION find_prefix_val * * Find the namespace prefix for the specified namespace ID * by the prefix value itself * * INPUTS: * msg == message to search * pfix == prefix to check for * * RETURNS: * namespace ID in the mapping or 0 if not found *********************************************************************/ static xmlns_id_t find_prefix_val (xml_msg_hdr_t *msg, const xmlChar *pfix) { const xmlns_pmap_t *pmap; for (pmap = (const xmlns_pmap_t *)dlq_firstEntry(&msg->prefixQ); pmap != NULL; pmap = (const xmlns_pmap_t *)dlq_nextEntry(pmap)) { if (!xml_strcmp(pmap->nm_pfix, pfix)) { return pmap->nm_id; } } return XMLNS_NULL_NS_ID; } /* find_prefix_val */ /******************************************************************** * FUNCTION xmlns_needed * * Check if an xmlns directive is needed for the specified * element node * * INPUTS: * curelem == value node for the current element * nsid == namespace ID to check * * RETURNS: * TRUE if an xmlns directive is needed * FALSE if not (already generated in this XML path to toor) *********************************************************************/ static boolean xmlns_needed (val_value_t *curelem, xmlns_id_t nsid) { val_value_t *metaval; /* check if the XMLNS directive already present * in this node due to an attribute using the NS ID */ for (metaval = val_get_first_meta_val(curelem); metaval != NULL; metaval = val_get_next_meta(metaval)) { if (metaval->nsid == nsid) { return FALSE; } } /* last chance, check the parent */ if (curelem->parent) { if (curelem->parent->nsid == nsid) { return FALSE; } else { return xmlns_needed(curelem->parent, nsid); } } else { /* the NS ID was not found anywhere in the path to root * so an XMLNS directive is needed now */ return TRUE; } } /* xmlns_needed */ /******************************************************************** * build a prefix map using the xmplns directives in a message. * * \param msg the message in progrss * \param attrs the the top-level attrs list (e;g, rpc_in_attrs) * \return status *********************************************************************/ static status_t build_prefix_map_from_xmlns_directives( xml_msg_hdr_t* msg, xml_attrs_t* attrs ) { xmlns_id_t invid = xmlns_inv_id(); xml_attr_t *attr = (xml_attr_t *)xml_first_attr(attrs); for ( ; attr; attr = (xml_attr_t *)xml_next_attr(attr)) { /* make sure this is an XMLNS attribute with or wo a prefix */ if (xml_strncmp(XMLNS, attr->attr_qname, XMLNS_LEN)) { continue; } /* find the namespace associated with the prefix note: it is not an error to have extra xmlns decls in the elem; still need to make sure not to reuse the prefix anyway */ xmlns_t *nsrec = def_reg_find_ns(attr->attr_val); /* check if this attribute has a prefix */ if (attr->attr_qname == attr->attr_name) { /* no prefix in the name so this must be the default namespace */ if ( !nsrec ) { /* the default namespace is not one of ours, so it will not be * used in the reply */ attr->attr_xmlns_ns = invid; } else { attr->attr_xmlns_ns = nsrec->ns_id; } continue; } /* there is a prefix, so get the prefix len. * The entire prefix was saved as the attr_name */ uint32 plen = xml_strlen(attr->attr_name); /* get a new prefix map */ xmlns_pmap_t *newpmap = xmlns_new_pmap(plen+1); if (!newpmap) { return ERR_INTERNAL_MEM; } /* save the prefix and the xmlns ID */ xml_strncpy(newpmap->nm_pfix, attr->attr_name, plen); if ( !nsrec ) { newpmap->nm_id = invid; attr->attr_xmlns_ns = invid; } else { newpmap->nm_id = nsrec->ns_id; attr->attr_xmlns_ns = nsrec->ns_id; } newpmap->nm_topattr = TRUE; add_pmap(msg, newpmap); } return NO_ERR; } /******************************************************************** * Add an ncid or ncxid to a prefix map. * * \param msg the message in progrss * \param attrs the the top-level attrs list (e;g, rpc_in_attrs) * \param ncid the ncid to add. * \return status *********************************************************************/ static status_t add_ncid_to_prefix_map( xml_msg_hdr_t* msg, xml_attrs_t* attrs, xmlns_id_t ncid ) { status_t res = NO_ERR; if (!find_prefix(msg, ncid) ) { /* add a prefix an xmlns attr for NETCONF */ xmlChar *buff = NULL; res = xml_msg_gen_new_prefix(msg, ncid, &buff, 0); if (res != NO_ERR) { m__free( buff ); return res; } res = xml_add_xmlns_attr(attrs, ncid, buff); if (res != NO_ERR) { m__free( buff ); return res; } /* create a new prefix map */ xmlns_pmap_t *newpmap = xmlns_new_pmap(0); if (!newpmap) { m__free( buff ); return ERR_INTERNAL_MEM; } newpmap->nm_id = ncid; newpmap->nm_pfix = buff; newpmap->nm_topattr = TRUE; add_pmap(msg, newpmap); } return res; } /************** E X T E R N A L F U N C T I O N S ***************/ /******************************************************************** * FUNCTION xml_msg_init_hdr * * Initialize a new xml_msg_hdr_t struct * * INPUTS: * msg == xml_msg_hdr_t memory to initialize * RETURNS: * none *********************************************************************/ void xml_msg_init_hdr (xml_msg_hdr_t *msg) { #ifdef DEBUG if (!msg) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif memset(msg, 0x0, sizeof(xml_msg_hdr_t)); dlq_createSQue(&msg->prefixQ); dlq_createSQue(&msg->errQ); msg->withdef = NCX_DEF_WITHDEF; msg->useprefix = ncx_get_useprefix(); } /* xml_msg_init_hdr */ /******************************************************************** * FUNCTION xml_msg_clean_hdr * * Clean all the memory used by the specified xml_msg_hdr_t * but do not free the struct itself * * INPUTS: * msg == xml_msg_hdr_t to clean * RETURNS: * none *********************************************************************/ void xml_msg_clean_hdr (xml_msg_hdr_t *msg) { xmlns_pmap_t *pmap; #ifdef DEBUG if (!msg) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* clean prefix queue */ while (!dlq_empty(&msg->prefixQ)) { pmap = (xmlns_pmap_t *)dlq_deque(&msg->prefixQ); xmlns_free_pmap(pmap); } /* clean error queue */ rpc_err_clean_errQ(&msg->errQ); msg->withdef = NCX_DEF_WITHDEF; } /* xml_msg_clean_hdr */ /******************************************************************** * FUNCTION xml_msg_get_prefix * * Find the namespace prefix for the specified namespace ID * If it is not there then create one * * INPUTS: * msg == message to search * parent_nsid == parent namespace ID * nsid == namespace ID to find * curelem == value node for current element if available * xneeded == pointer to xmlns needed flag output value * * OUTPUTS: * *xneeded == TRUE if the prefix is new and an xmlns * decl is needed in the element being generated * * RETURNS: * pointer to prefix if found, else NULL if not found *********************************************************************/ const xmlChar * xml_msg_get_prefix (xml_msg_hdr_t *msg, xmlns_id_t parent_nsid, xmlns_id_t nsid, val_value_t *curelem, boolean *xneeded) { xmlns_pmap_t *pmap, *newpmap; const xmlChar *pfix; status_t res; #ifdef DEBUG if (!msg || !xneeded) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif newpmap = NULL; *xneeded = FALSE; /* check if the default namespace is requested */ if (nsid == 0) { return NULL; } /* see if a prefix is already present in the rpc-reply element */ pfix = NULL; pmap = find_pmap(msg, nsid); if (!pmap) { /* need to create a new prefix map and save it for real */ newpmap = xmlns_new_pmap(0); if (!newpmap) { SET_ERROR(ERR_INTERNAL_MEM); return NULL; } /* generate a prefix ID */ newpmap->nm_id = nsid; res = xml_msg_gen_new_prefix(msg, nsid, &newpmap->nm_pfix, 0); if (res != NO_ERR) { SET_ERROR(res); xmlns_free_pmap(newpmap); return NULL; } pfix = newpmap->nm_pfix; /* add the new prefix mapping to the prefixQ */ add_pmap(msg, newpmap); } else { pfix = pmap->nm_pfix; } /* xmlns directive will be needed if this is a new prefix */ if (!pmap) { /* this is the first use */ *xneeded = TRUE; } else if (!msg->useprefix && (parent_nsid != nsid)) { /* the default prefix needs to be generated again */ *xneeded = TRUE; } else if (!pmap->nm_topattr) { /* this is not the first use, and the xmlns directive * is not in the top element; need to check if * the NSID has already been used in the current * element or any of its ancestors */ if (curelem) { /* check the actual value tree */ *xneeded = xmlns_needed(curelem, nsid); } else if (parent_nsid != nsid) { /* use hack -- if child and parent not the same, * then generate the xmlns directive */ *xneeded = TRUE; } } return pfix; } /* xml_msg_get_prefix */ /******************************************************************** * FUNCTION xml_msg_get_prefix_xpath * * Find the namespace prefix for the specified namespace ID * If it is not there then create one in the msg prefix map * Always returns a prefix, instead of using a default * * creates a new pfixmap if needed * * !!! MUST BE CALLED BEFORE THE XML OUTPUT * !!! HAS BEGUN. CANNOT BE CALLED BY OUTPUT FUNCTIONS * !!! DURING THE OR OUTPUT GENERATION * * INPUTS: * msg == message to search * nsid == namespace ID to find * * RETURNS: * pointer to prefix if found, else NULL if not found *********************************************************************/ const xmlChar * xml_msg_get_prefix_xpath (xml_msg_hdr_t *msg, xmlns_id_t nsid) { xmlns_pmap_t *newpmap; const xmlChar *pfix; status_t res; #ifdef DEBUG if (!msg) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (!nsid) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif /* see if a prefix is already present in the rpc-reply element */ pfix = find_prefix(msg, nsid); if (pfix) { return pfix; } /* need to create a new prefix map and save it for real */ newpmap = xmlns_new_pmap(0); if (!newpmap) { SET_ERROR(ERR_INTERNAL_MEM); return NULL; } /* generate a prefix ID */ newpmap->nm_id = nsid; res = xml_msg_gen_new_prefix(msg, nsid, &newpmap->nm_pfix, 0); if (res != NO_ERR) { xmlns_free_pmap(newpmap); return NULL; } /* add the new prefix mapping to the prefixQ */ add_pmap(msg, newpmap); return newpmap->nm_pfix; } /* xml_msg_get_prefix_xpath */ /******************************************************************** * FUNCTION xml_msg_get_prefix_start_tag * * Find the namespace prefix for the specified namespace ID * DO NOT CREATE A NEW PREFIX MAP IF IT IS NOT THERE * does not create any pfixmap, just returns NULL if not found * * INPUTS: * msg == message to search * nsid == namespace ID to find * * RETURNS: * pointer to prefix if found, else NULL if not found *********************************************************************/ const xmlChar * xml_msg_get_prefix_start_tag (xml_msg_hdr_t *msg, xmlns_id_t nsid) { const xmlChar *pfix; #ifdef DEBUG if (!msg) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (!nsid) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif /* see if a prefix is already present in the rpc-reply element */ pfix = find_prefix(msg, nsid); if (pfix) { return pfix; } else { return NULL; } } /* xml_msg_get_prefix_start_tag */ /******************************************************************** * FUNCTION xml_msg_gen_new_prefix * * Generate a new namespace prefix * * INPUTS: * msg == message to search and generate a prefix for * nsid == namespace ID to generate prefix for * retbuff == address of return buffer * buffsize == buffer size * OUTPUTS: * if *retbuff is NULL it will be created * else *retbuff is filled in with the new prefix if NO_ERR * * RETURNS: * status *********************************************************************/ status_t xml_msg_gen_new_prefix (xml_msg_hdr_t *msg, xmlns_id_t nsid, xmlChar **retbuff, uint32 buffsize) { const xmlChar *defpfix; xmlChar startch; int32 nlen, i; xmlChar numbuff[NCX_MAX_NUMLEN], *buff; xmlns_id_t testid; #ifdef DEBUG if (!msg || !retbuff) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (*retbuff) { buff = *retbuff; } else { buff = m__getMem(NCX_MAX_NUMLEN+1); if (!buff) { return ERR_INTERNAL_MEM; } else { buffsize = NCX_MAX_NUMLEN+1; *retbuff = buff; } } /* first see if a default prefix was registered with the namespace * and use it if not already in the prefix map */ defpfix = xmlns_get_ns_prefix(nsid); if (defpfix && *defpfix) { testid = find_prefix_val(msg, defpfix); if (testid == 0 || testid==nsid) { if (xml_strlen(defpfix) < buffsize) { xml_strcpy(buff, defpfix); return NO_ERR; } else { return ERR_BUFF_OVFL; } } } /* default already in use for something else so generate a prefix */ nlen = snprintf((char *)numbuff, NCX_MAX_NUMLEN, "%u", (uint32)nsid); if (nlen < 0) { return ERR_NCX_INVALID_NUM; } if ((uint32)(nlen+2) >= buffsize) { return ERR_BUFF_OVFL; } /* copy the number to the prefix buffer w/ trailing zero */ for (i=0; i<=nlen; i++) { buff[i+1] = numbuff[i]; } /* set the start letter in the prefix buffer */ startch = 'n'; /* try to generate a unique prefix */ for (i=0; i<=MAX_PREFIX_TRIES; i++) { /* adjust the label value */ buff[0] = startch++; if (startch > 'z') { startch = 'a'; } /* check if the prefix is in use */ if (!find_prefix_val(msg, buff)) { return NO_ERR; } } return ERR_NCX_OPERATION_FAILED; } /* xml_msg_gen_new_prefix */ /******************************************************************** * Build a queue of xmlns_pmap_t records for the current message * This function will populate msg->prefixQ as needed, * XMLNS Entries for NETCONF and NCX will be added if they * are not present * * \param msg the message in progrss * \param attrs the the top-level attrs list (e;g, rpc_in_attrs) * \param addncid flag indicating if prefix entry for the NC namespace * should be added * \param addncxid flag indicating if a prefix entry for the NCX namespace * should be added * \return status *********************************************************************/ status_t xml_msg_build_prefix_map ( xml_msg_hdr_t *msg, xml_attrs_t *attrs, boolean addncid, boolean addncxid ) { assert( msg && "msg is NULL" ); assert( attrs && "attrs is NULL" ); /* look for any xmlns directives in the attrs list and build a prefix map * entry so they will be reused in the reply. Deal with foreign namespace * decls as well */ status_t res = build_prefix_map_from_xmlns_directives( msg, attrs ); if ( res != NO_ERR ) { return res; } /* make sure XMLNS decl for NETCONF is in the map make sure it is not the * default, by forcing a new xmlns with a prefix -- needed by XPath 1.0 */ if ( addncid ) { res = add_ncid_to_prefix_map( msg, attrs, xmlns_nc_id() ); } /* make sure XMLNS decl for NCX is in the map try even if errors in setting * up the NETCONF pmap */ if (addncxid ) { status_t retres = add_ncid_to_prefix_map( msg, attrs, xmlns_nc_id() ); if (retres != NO_ERR && res == NO_ERR ) { res = retres ; } } return res; } /* xml_msg_build_prefix_map */ /** * Finish the queue of xmlns_pmap_t records for the current message * * /param msg message in progrss * /param attrs the top-level attrs list (e;g, rpc_in_attrs) * * /return status of operation *********************************************************************/ status_t xml_msg_finish_prefix_map (xml_msg_hdr_t *msg, xml_attrs_t *attrs) { xmlns_pmap_t *newpmap; xmlChar *buff = NULL; xmlns_id_t wdaid; status_t res = NO_ERR; assert(msg && "msg is NULL"); assert(attrs && "attrs is NULL"); wdaid = xmlns_wda_id(); /* make sure XMLNS decl for wd:default is in the map * if the report-all-tagged mode is active */ if (msg->withdef == NCX_WITHDEF_REPORT_ALL_TAGGED && !find_prefix(msg, wdaid)) { /* add a prefix an xmlns attr for WD */ res = xml_msg_gen_new_prefix(msg, wdaid, &buff, 0); if (res == NO_ERR) { res = xml_add_xmlns_attr(attrs, wdaid, buff); } if (res == NO_ERR) { /* create a new prefix map */ newpmap = xmlns_new_pmap(0); if (newpmap == NULL) { res = ERR_INTERNAL_MEM; } else { newpmap->nm_id = wdaid; newpmap->nm_pfix = buff; newpmap->nm_topattr = TRUE; add_pmap(msg, newpmap); } } } if (buff && (res != NO_ERR)) { m__free(buff); } return res; } /* xml_msg_finish_prefix_map */ /******************************************************************** * FUNCTION xml_msg_check_xmlns_attr * * Check the default NS and the prefix map in the msg; * * INPUTS: * msg == message in progress * nsid == namespace ID to check * badns == namespace URI of the bad namespace * used if the nsid is the INVALID marker * attrs == Q to hold the xml_attr_t, if generated * * OUTPUTS: * msg->prefixQ will be populated as needed, * could be partially populated if some error returned * * XMLNS attr entry may be added to the attrs Q * * RETURNS: * status *********************************************************************/ status_t xml_msg_check_xmlns_attr (xml_msg_hdr_t *msg, xmlns_id_t nsid, const xmlChar *badns, xml_attrs_t *attrs) { const xmlChar *pfix; xmlChar *buff; xml_attr_t *attr; status_t res; #ifdef DEBUG if (!msg || !attrs) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* no namespace is always ok, and if it is the same as the * current default, then nothing to do */ if (!nsid) { return NO_ERR; } /* not the default, see if prefix already set for this NS */ pfix = find_prefix(msg, nsid); if (pfix) { return NO_ERR; } /* check if this namespace ID already covered because the * xmlns decl is already present in the attrs list parameter */ for (attr = (xml_attr_t *)dlq_firstEntry(attrs); attr != NULL; attr = (xml_attr_t *)dlq_nextEntry(attr)) { if (attr->attr_xmlns_ns == nsid) { return NO_ERR; } } /* not already covered */ buff = NULL; res = xml_msg_gen_new_prefix(msg, nsid, &buff, 0); if (res == NO_ERR) { if (nsid == xmlns_inv_id()) { res = xml_add_inv_xmlns_attr(attrs, nsid, buff, badns); } else { res = xml_add_xmlns_attr(attrs, nsid, buff); } } if (buff) { m__free(buff); } return res; } /* xml_msg_check_xmlns_attr */ /******************************************************************** * FUNCTION xml_msg_gen_xmlns_attrs * * Generate any xmlns directives in the top-level * attribute Q * * INPUTS: * msg == message in progress * attrs == xmlns_attrs_t Q to process * addncx == TRUE if an xmlns for the NCX prefix (for errors) * should be added to the element * FALSE if not * * OUTPUTS: * *attrs will be populated as needed, * * RETURNS: * status *********************************************************************/ status_t xml_msg_gen_xmlns_attrs (xml_msg_hdr_t *msg, xml_attrs_t *attrs, boolean addncx) { xmlns_pmap_t *pmap, *newpmap; xmlChar *buff; status_t res, retres; boolean ncxfound; xmlns_id_t ncx_id; #ifdef DEBUG if (!msg || !attrs) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif ncxfound = FALSE; retres = NO_ERR; ncx_id = xmlns_ncx_id(); for (pmap = (xmlns_pmap_t *)dlq_firstEntry(&msg->prefixQ); pmap != NULL; pmap = (xmlns_pmap_t *)dlq_nextEntry(pmap)) { if (pmap->nm_id == ncx_id) { ncxfound = TRUE; } if (pmap->nm_topattr) { continue; } buff = NULL; res = xml_msg_gen_new_prefix(msg, pmap->nm_id, &buff, 0); if (res == NO_ERR) { res = xml_add_xmlns_attr(attrs, pmap->nm_id, buff); } if (buff) { m__free(buff); } if (res != NO_ERR) { retres = res; } else { pmap->nm_topattr = TRUE; } } if (!ncxfound && addncx && retres == NO_ERR) { /* need to create a new prefix map and save it */ newpmap = xmlns_new_pmap(0); if (!newpmap) { retres = ERR_INTERNAL_MEM; } else { /* generate a prefix ID */ newpmap->nm_id = ncx_id; newpmap->nm_topattr = TRUE; res = xml_msg_gen_new_prefix(msg, ncx_id, &newpmap->nm_pfix, 0); if (res == NO_ERR) { /* add the new prefix mapping to the prefixQ */ res = xml_add_xmlns_attr(attrs, newpmap->nm_id, newpmap->nm_pfix); if (res == NO_ERR) { add_pmap(msg, newpmap); } else { xmlns_free_pmap(newpmap); retres = res; } } else { xmlns_free_pmap(newpmap); retres = res; } } } return retres; } /* xml_msg_gen_xmlns_attrs */ /******************************************************************** * FUNCTION xml_msg_clean_defns_attr * * Get rid of an xmlns=foo default attribute * * INPUTS: * attrs == xmlns_attrs_t Q to process * * OUTPUTS: * *attrs will be cleaned as needed, * * RETURNS: * status *********************************************************************/ status_t xml_msg_clean_defns_attr (xml_attrs_t *attrs) { xml_attr_t *attr, *nextattr; uint32 len, len2; #ifdef DEBUG if (!attrs) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif len = xml_strlen(XMLNS); for (attr = (xml_attr_t *)xml_first_attr(attrs); attr != NULL; attr = nextattr) { nextattr = (xml_attr_t *)xml_next_attr(attr); len2 = xml_strlen(attr->attr_qname); if (len2 >= len) { if (!xml_strncmp(attr->attr_qname, XMLNS, len)) { if (len == len2) { /* this xmlns=foo is getting toosed so * the netconf NSID can be the default */ dlq_remove(attr); xml_free_attr(attr); return NO_ERR; } /* else xmlns:foo=bar found */ } /* else not xmlns */ } /* else not 'xmlns' */ } return NO_ERR; } /* xml_msg_clean_defns_attr */ /* END file xml_msg.c */ yuma123_2.14/netconf/src/ncx/tstamp.c0000664000175000017500000002677414770023131017632 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2013 - 2018, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: tstamp.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17apr06 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #define __USE_XOPEN 1 #include #include #include "procdefs.h" #include "status.h" #include "tstamp.h" #include "xml_util.h" /******************************************************************** * FUNCTION time_to_string * * Convert the tm to a string in YANG canonical format * * INPUTS: * curtime == time struct to use * buff == pointer to buffer to hold output * MUST BE AT LEAST 21 CHARS * OUTPUTS: * buff is filled in *********************************************************************/ static void time_to_string (const struct tm *curtime, xmlChar *buff) { (void)sprintf((char *)buff, "%04u-%02u-%02uT%02u:%02u:%02uZ", (uint32)(curtime->tm_year+1900), (uint32)(curtime->tm_mon+1), (uint32)curtime->tm_mday, (uint32)curtime->tm_hour, (uint32)curtime->tm_min, (uint32)curtime->tm_sec); } /* time_to_string */ /******************************************************************** * FUNCTION time_to_dirname * * Convert the tm to a directory name for yangcli * * INPUTS: * curtime == time struct to use * buff == pointer to buffer to hold output * MUST BE AT LEAST 21 CHARS * OUTPUTS: * buff is filled in *********************************************************************/ static void time_to_dirname (const struct tm *curtime, xmlChar *buff) { (void)sprintf((char *)buff, "%04u%02u%02u%02u%02u%02u", (uint32)(curtime->tm_year+1900), (uint32)(curtime->tm_mon+1), (uint32)curtime->tm_mday, (uint32)curtime->tm_hour, (uint32)curtime->tm_min, (uint32)curtime->tm_sec); } /* time_to_dirname */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION tstamp_datetime * * Set the current date and time in an XML dateTime string format * * INPUTS: * buff == pointer to buffer to hold output * MUST BE AT LEAST 21 CHARS * OUTPUTS: * buff is filled in *********************************************************************/ void tstamp_datetime (xmlChar *buff) { time_t utime; struct tm *curtime; #ifdef DEBUG if (!buff) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif (void)time(&utime); curtime = gmtime(&utime); time_to_string(curtime, buff); } /* tstamp_datetime */ /******************************************************************** * FUNCTION tstamp123_datetime_nsec * * Set the current date and time in ietf-yang-types:date-and-time * format * * INPUTS: * buff == pointer to buffer to hold output * MUST BE AT LEAST 30 CHARS 1970-01-01T00:00:00.999999999Z * OUTPUTS: * buff is filled in *********************************************************************/ void tstamp123_datetime_nsec (xmlChar *buff) { int res; struct timespec ts; struct tm my_tm; assert(buff); res = clock_gettime(CLOCK_REALTIME, &ts); assert(res==0); //localtime_r(&(ts.tv_sec), &my_tm); gmtime_r(&(ts.tv_sec),&my_tm); (void)sprintf((char *)buff, "%04u-%02u-%02uT%02u:%02u:%02u.%09uZ", (uint32)(my_tm.tm_year+1900), (uint32)(my_tm.tm_mon+1), (uint32)my_tm.tm_mday, (uint32)my_tm.tm_hour, (uint32)my_tm.tm_min, (uint32)my_tm.tm_sec, (uint32)ts.tv_nsec); } /* tstamp123_datetime_nsec */ /******************************************************************** * FUNCTION tstamp_date * * Set the current date in an XML dateTime string format * * INPUTS: * buff == pointer to buffer to hold output * MUST BE AT LEAST 11 CHARS * OUTPUTS: * buff is filled in *********************************************************************/ void tstamp_date (xmlChar *buff) { time_t utime; struct tm *curtime; #ifdef DEBUG if (!buff) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif (void)time(&utime); curtime = localtime(&utime); (void)sprintf((char *)buff, "%04u-%02u-%02u", (uint32)(curtime->tm_year+1900), (uint32)(curtime->tm_mon+1), (uint32)curtime->tm_mday); } /* tstamp_date */ /******************************************************************** * FUNCTION tstamp_datetime_sql * * Set the current date and time in an XML dateTime string format * * INPUTS: * buff == pointer to buffer to hold output * MUST BE AT LEAST 20 CHARS * OUTPUTS: * buff is filled in *********************************************************************/ void tstamp_datetime_sql (xmlChar *buff) { time_t utime; struct tm *curtime; #ifdef DEBUG if (!buff) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif (void)time(&utime); curtime = localtime(&utime); /*** milliseconds not returned, hardwired to '00' ***/ (void)sprintf((char *)buff, "%04u-%02u-%02u %02u:%02u:%02u", (uint32)(curtime->tm_year+1900), (uint32)(curtime->tm_mon+1), (uint32)curtime->tm_mday, (uint32)curtime->tm_hour, (uint32)curtime->tm_min, (uint32)curtime->tm_sec); } /* tstamp_datetime_sql */ /******************************************************************** * FUNCTION tstamp_convert_to_utctime * * Check if the specified string is a valid dateTime or * date-and-time string is valid and if so, convert it * to * * INPUTS: * buff == pointer to buffer to check * isNegative == address of return negative date flag * res == address of return status * * OUTPUTS: * *isNegative == TRUE if a negative dateTime string is given * FALSE if no starting '-' sign found * *res == return status * * RETURNS: * malloced pointer to converted date time string * or NULL if some error *********************************************************************/ xmlChar * tstamp_convert_to_utctime (const xmlChar *timestr, boolean *isNegative, status_t *res) { const char *retptr; xmlChar *buffer; time_t utime; struct tm convertedtime, *curtime; uint32 len; #ifdef DEBUG if (!timestr || !isNegative || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif *res = NO_ERR; memset(&convertedtime, 0x0, sizeof(struct tm)); if (*timestr == '-') { *isNegative = TRUE; timestr++; } else { *isNegative = FALSE; } len = xml_strlen(timestr); if (len == 20) { /* could be in canonical form */ retptr = strptime((const char *)timestr, "%FT%TZ", &convertedtime); if (retptr && *retptr == '\0') { buffer = xml_strdup(timestr); if (!buffer) { *res = ERR_INTERNAL_MEM; return NULL; } else { return buffer; } } else { *res = ERR_NCX_INVALID_VALUE; return NULL; } } else if (len > 20) { retptr = strptime((const char *)timestr, "%FT%T", &convertedtime); if (retptr == NULL || *retptr == '\0') { *res = ERR_NCX_INVALID_VALUE; return NULL; } /* check is frac-seconds entered, and skip it */ if (*retptr == '.') { retptr++; if (!isdigit((int)*retptr)) { *res = ERR_NCX_INVALID_VALUE; return NULL; } retptr++; /* got a start digit */ while (isdigit((int)*retptr)) { retptr++; } } /* check if a timezone offset is present */ retptr = strptime(retptr, "%z", &convertedtime); if (retptr == NULL) { *res = ERR_NCX_INVALID_VALUE; return NULL; } /* check where retptr ended up */ if (*retptr == '\0') { /* OK read all the bytes */ ; } else if (*retptr == ':') { if (strcmp(retptr, ":00")) { /* the linux strptime function does * not process the 'time-minute' field in the * time string; since this is so rare * just treat as a special error */ *res = ERR_NCX_OPERATION_NOT_SUPPORTED; return NULL; } /* else time-minute field == '00' and no error */ } else { *res = ERR_NCX_INVALID_VALUE; return NULL; } buffer = m__getMem(TSTAMP_MIN_SIZE); if (!buffer) { *res = ERR_INTERNAL_MEM; return NULL; } utime = mktime(&convertedtime); if (utime == (utime)-1) { *res = ERR_NCX_INVALID_VALUE; m__free(buffer); return NULL; } curtime = gmtime(&utime); time_to_string(curtime, buffer); return buffer; } else { /* improper length */ *res = ERR_NCX_INVALID_VALUE; return NULL; } } /* tstamp_convert_to_utctime */ /******************************************************************** * FUNCTION tstamp_datetime_dirname * * Set the current date and time in an XML dateTime string format * * INPUTS: * buff == pointer to buffer to hold output * MUST BE AT LEAST 21 CHARS * OUTPUTS: * buff is filled in *********************************************************************/ void tstamp_datetime_dirname (xmlChar *buff) { time_t utime; struct tm *curtime; #ifdef DEBUG if (!buff) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif (void)time(&utime); curtime = gmtime(&utime); time_to_dirname(curtime, buff); } /* tstamp_datetime_dirname */ /* END file tstamp.c */ yuma123_2.14/netconf/src/ncx/xml_util.c0000664000175000017500000021003014770023131020133 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xml_util.c General Utilities that simplify usage of the libxml functions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13oct05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xpath #include "xpath.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define XML_UTIL_DEBUG 1 #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* One entry to find a character entity value */ typedef struct xml_chcvt_t_ { const xmlChar *chstr; xmlChar ch; } xml_chcvt_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /* lookup list for the char entities that need to be converted */ static xml_chcvt_t chcvt [] = { {(const xmlChar *)"gt", '>'}, {(const xmlChar *)"lt", '<'}, {(const xmlChar *)"quot", '"'}, {(const xmlChar *)"amp", '&'}, {(const xmlChar *)"lt", '<'}, {NULL, ' '} }; /******************************************************************** * FUNCTION get_attrs * * Copy all the attributes from the current node to * the xml_attrs_t queue * * INPUTS: * reader == XmlReader already initialized from File, Memory, * or whatever * attrs == pointer to output var * nserr == TRUE if unknown namespace should cause the * function to fail and the attr not to be saved * This is the normal mode. * == FALSE and the namespace will be marked INVALID * but an error will not be returned * * OUTPUTS: * attrs Q contains 0 or more entries * *errQ may have rpc-errors added to it * * RETURNS: * status of the operation * returns NO_ERR if all copied okay or even zero copied *********************************************************************/ static status_t get_attrs (xmlTextReaderPtr reader, xml_attrs_t *attrs, boolean nserr) { int i, cnt, ret; xmlChar *value; const xmlChar *badns, *name; xmlns_id_t nsid; status_t res; boolean done; uint32 plen; res = NO_ERR; /* check the attribute count first */ cnt = xmlTextReaderAttributeCount(reader); if (cnt==0) { return NO_ERR; } /* move through the list of attributes */ for (i=0, done=FALSE; iattrs); } /* xml_init_node */ /******************************************************************** * FUNCTION xml_free_node * * Free an xml_node_t struct * * INPUTS: * node == pointer to node to free *********************************************************************/ void xml_free_node (xml_node_t *node) { #ifdef DEBUG if (!node) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif xml_clean_node(node); m__free(node); } /* xml_free_node */ /******************************************************************** * FUNCTION xml_clean_node * * Clean an xml_node_t struct * * INPUTS: * node == pointer to node to clean *********************************************************************/ void xml_clean_node (xml_node_t *node) { #ifdef DEBUG if (!node) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif node->nodetyp = XML_NT_NONE; node->nsid = 0; if (node->qname) { m__free(node->qname); node->qname = NULL; } node->qname = NULL; node->elname = NULL; if (node->simfree) { m__free(node->simfree); node->simfree = NULL; } node->simval = NULL; node->simlen = 0; node->depth = 0; xml_clean_attrs(&node->attrs); } /* xml_clean_node */ /******************************************************************** * FUNCTION xml_get_reader_from_filespec * * Get a new xmlTextReader for parsing a debug test file * * INPUTS: * filespec == full filename including path of the * XML instance document to parse * OUTPUTS: * *reader == pointer to new reader or NULL if some error * * RETURNS: * status of the operation *********************************************************************/ status_t xml_get_reader_from_filespec (const char *filespec, xmlTextReaderPtr *reader) { int options; #ifdef DEBUG if (!filespec || !reader) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* the parser option settings are critical!!! * The parser routines assume that includes will be * expanded in place and #TEXT nodes such as EOLN will * be omitted by the parser */ options = XML_READER_OPTIONS + XML_PARSE_XINCLUDE; *reader = xmlReaderForFile(filespec, NULL, options); if (*reader==NULL) { return ERR_XML_READER_START_FAILED; } return NO_ERR; } /* xml_get_reader_from_filespec */ /******************************************************************** * FUNCTION xml_get_reader_for_session * * Get a new xmlTextReader for parsing the input of a NETCONF session * * INPUTS: * readfn == IO read function to use for this xmlTextReader * closefn == IO close fn to use for this xmlTextReader * context == the ses_cb_t pointer passes as a void * * this will be passed to the read and close functions * OUTPUTS: * *reader == pointer to new reader or NULL if some error * RETURNS: * status of the operation *********************************************************************/ status_t xml_get_reader_for_session (xmlInputReadCallback readfn, xmlInputCloseCallback closefn, void *context, xmlTextReaderPtr *reader) { int options; #ifdef DEBUG if (!readfn || !reader) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* the parser option settings are critical!!! * The parser routines assume that includes will be * expanded in place and #TEXT nodes such as EOLN will * be omitted by the parser */ options = XML_READER_OPTIONS; *reader = xmlReaderForIO(readfn, closefn, context, XML_SES_URL, NULL, options); if (*reader==NULL) { return ERR_XML_READER_START_FAILED; } return NO_ERR; } /* xml_get_reader_for_session */ /******************************************************************** * FUNCTION xml_reset_reader_for_session * * Reset the xmlTextReader for parsing the input of a NETCONF session * * INPUTS: * readfn == IO read function to use for this xmlTextReader * closefn == IO close fn to use for this xmlTextReader * context == the ses_cb_t pointer passes as a void * * this will be passed to the read and close functions * OUTPUTS: * *reader == pointer to new reader or NULL if some error * RETURNS: * status of the operation *********************************************************************/ status_t xml_reset_reader_for_session (xmlInputReadCallback readfn, xmlInputCloseCallback closefn, void *context, xmlTextReaderPtr reader) { int ret; #ifdef DEBUG if (!readfn || !reader) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* the parser option settings are critical!!! * The parser routines assume that includes will be * expanded in place and #TEXT nodes such as EOLN will * be omitted by the parser */ ret = xmlReaderNewIO(reader, readfn, closefn, context, XML_SES_URL, NULL, XML_READER_OPTIONS); if (ret != 0) { return ERR_XML_READER_START_FAILED; } return NO_ERR; } /* xml_reset_reader_for_session */ /******************************************************************** * FUNCTION xml_free_reader * * Free the previously allocated xmlTextReader * * INPUTS: * reader == xmlTextReader to close and deallocate * RETURNS: * none *********************************************************************/ void xml_free_reader (xmlTextReaderPtr reader) { #ifdef DEBUG if (!reader) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif xmlFreeTextReader(reader); } /* xml_free_reader */ /******************************************************************** * FUNCTION xml_get_node_name * * get the node type according to the xmlElementType enum list * in /usr/include/libxml/libxml/tree.h * * INPUTS: * nodeval == integer node type from system * RETURNS: * string corresponding to the integer value *********************************************************************/ const char * xml_get_node_name (int nodeval) { switch (nodeval) { case 1: return "XML_ELEMENT_NODE"; case 2: return "XML_ATTRIBUTE_NODE"; case 3: return "XML_TEXT_NODE"; case 4: return "XML_CDATA_SECTION_NODE"; case 5: return "XML_ENTITY_REF_NODE"; case 6: return "XML_ENTITY_NODE"; case 7: return "XML_PI_NODE"; case 8: return "XML_COMMENT_NODE"; case 9: return "XML_DOCUMENT_NODE"; case 10: return "XML_DOCUMENT_TYPE_NODE"; case 11: return "XML_DOCUMENT_FRAG_NODE"; case 12: return "XML_NOTATION_NODE"; case 13: return "XML_HTML_DOCUMENT_NODE"; case 14: return "XML_DTD_NODE"; case 15: return "XML_ELEMENT_DECL"; case 16: return "XML_ATTRIBUTE_DECL"; case 17: return "XML_ENTITY_DECL"; case 18: return "XML_NAMESPACE_DECL"; case 19: return "XML_XINCLUDE_START"; case 20: return "XML_XINCLUDE_END"; case 21: return "XML_DOCB_DOCUMENT_NODE"; default: return "**unknown**"; } /*NOTREACHED*/ } /* xml_get_node_name */ /******************************************************************** * FUNCTION xml_advance_reader * * Advance to the next node in the specified reader * * INPUTS: * reader == XmlReader already initialized from File, Memory, * or whatever * RETURNS: * FALSE if OEF seen, or TRUE if normal *********************************************************************/ boolean xml_advance_reader (xmlTextReaderPtr reader) { int ret; #ifdef DEBUG if (!reader) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif /* advance the node pointer */ ret = xmlTextReaderRead(reader); if (ret != 1) { return FALSE; } return TRUE; } /* xml_advance_reader */ /******************************************************************** * FUNCTION xml_node_match * * check if a specific node is the proper owner, name, and type * * INPUTS: * node == node to match against * nsid == namespace ID to match (0 == match any) * elname == element name to match (NULL == match any) * nodetyp == node type to match (XML_NT_NONE == match any) * RETURNS: * status *********************************************************************/ status_t xml_node_match (const xml_node_t *node, xmlns_id_t nsid, const xmlChar *elname, xml_nodetyp_t nodetyp) { #ifdef DEBUG if (!node) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (nsid) { /* namespace error only if namespaces are being used */ if (node->nsid && node->nsid != nsid) { return ERR_NCX_WRONG_NAMESPACE; } } if (elname) { if (!node->elname) { return ERR_NCX_UNKNOWN_ELEMENT; } if (xml_strcmp(elname, node->elname)) { return ERR_NCX_WRONG_ELEMENT; } } if (nodetyp != XML_NT_NONE) { if (nodetyp != node->nodetyp) { return ERR_NCX_WRONG_NODETYP; } } return NO_ERR; } /* xml_node_match */ /******************************************************************** * FUNCTION xml_endnode_match * * check if a specific node is the proper endnode match * for a given startnode * * INPUTS: * startnode == start node to match against * endnode == potential end node to test * RETURNS: * status, *********************************************************************/ status_t xml_endnode_match (const xml_node_t *startnode, const xml_node_t *endnode) { #ifdef DEBUG if (!startnode || !endnode) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (endnode->nodetyp != XML_NT_END) { return ERR_NCX_WRONG_NODETYP; } if (startnode->depth != endnode->depth) { return ERR_NCX_WRONG_NODEDEPTH; } if (xml_strcmp(startnode->elname, endnode->elname)) { return ERR_NCX_UNKNOWN_ELEMENT; } if (startnode->nsid && !endnode->nsid) { return ERR_NCX_UNKNOWN_NAMESPACE; } if (startnode->nsid != endnode->nsid) { return ERR_NCX_WRONG_NAMESPACE; } return NO_ERR; } /* xml_endnode_match */ /******************************************************************** * FUNCTION xml_docdone * * check if the input is completed for a given PDU * * INPUTS: * reader == xml text reader * RETURNS: * TRUE if document is done * FALSE if document is not done or some error * If a node is read, the reader will be pointing to that node *********************************************************************/ boolean xml_docdone (xmlTextReaderPtr reader) { int ret; #ifdef DEBUG if (!reader) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif /* advance the node pointer */ ret = xmlTextReaderRead(reader); if (ret != 1) { return TRUE; /* assume ERR_XML_READER_EOF !! */ } else { return FALSE; /* read was successful, doc is not done */ } } /* xml_docdone */ /******************************************************************** * FUNCTION xml_dump_node * * Debug function to printf xml_node_t contents * * INPUTS: * node == node to dump *********************************************************************/ void xml_dump_node (const xml_node_t *node) { const char *typ; boolean nam, ok; const xml_attr_t *attr; #ifdef DEBUG if (!node) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif switch (node->nodetyp) { case XML_NT_NONE: typ = "NONE"; ok = nam = FALSE; break; case XML_NT_EMPTY: typ = "EMPTY"; ok = nam = TRUE; break; case XML_NT_START: typ = "START"; ok = nam = TRUE; break; case XML_NT_END: typ = "END"; ok = nam = TRUE; break; case XML_NT_STRING: typ = "STRING"; nam = FALSE; ok = TRUE; break; default: typ = "ERR"; ok = nam = FALSE; break; } if (ok) { log_write("\nXML node (%d:%d): %s %s", node->nsid, node->depth, typ, (nam) ? (const char *)node->elname : ""); if (node->simval) { log_write("\n val(%u):%s", node->simlen, node->simval); } for (attr = (const xml_attr_t *)dlq_firstEntry(&node->attrs); attr != NULL; attr = (const xml_attr_t *)dlq_nextEntry(attr)) { log_write("\n attr: ns:%d name:%s (%s)", attr->attr_ns, (const char *)attr->attr_name, (const char *)attr->attr_val); } } else { log_write("\nXML node ERR (%s)", typ); } log_write("\n"); } /* xml_dump_node */ /******************************************************************** * FUNCTION xml_init_attrs * * initialize an xml_attrs_t variable * * INPUTS: * attrs == attribute queue to init * RETURNS: * none *********************************************************************/ void xml_init_attrs (xml_attrs_t *attrs) { #ifdef DEBUG if (!attrs) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif dlq_createSQue(attrs); } /* xml_init_attrs */ /******************************************************************** * FUNCTION xml_new_attr * * malloc and init an attribute struct * * INPUTS: * none * RETURNS: * pointer to new xml_attr_t or NULL if malloc error *********************************************************************/ xml_attr_t * xml_new_attr (void) { xml_attr_t *attr; attr = m__getObj(xml_attr_t); if (!attr) { return NULL; } memset(attr, 0x0, sizeof(xml_attr_t)); return attr; } /* xml_new_attr */ /******************************************************************** * FUNCTION xml_free_attr * * free an attribute * * INPUTS: * attr == xml_attr_t to free *********************************************************************/ void xml_free_attr (xml_attr_t *attr) { #ifdef DEBUG if (!attr) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (attr->attr_dname) { m__free(attr->attr_dname); } if (attr->attr_val) { m__free(attr->attr_val); } if (attr->attr_xpcb) { xpath_free_pcb(attr->attr_xpcb); } m__free(attr); } /* xml_free_attr */ /******************************************************************** * FUNCTION xml_add_attr * * add an attribute to an attribute list * * INPUTS: * attrs == attribute queue to init * ns_id == namespace ID (use XMLNS_NONE if no prefix desired) * attr_name == attribute name string * attr_val == attribute value string * RETURNS: * NO_ERR if all okay *********************************************************************/ status_t xml_add_attr (xml_attrs_t *attrs, xmlns_id_t ns_id, const xmlChar *attr_name, const xmlChar *attr_val) { xml_attr_t *attr; #ifdef DEBUG if (!attrs || !attr_name || !attr_val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif attr = xml_new_attr(); if (!attr) { return ERR_INTERNAL_MEM; } attr->attr_dname = xml_strdup(attr_name); attr->attr_name = attr->attr_dname; attr->attr_qname = attr->attr_dname; if (!attr->attr_dname) { xml_free_attr(attr); return SET_ERROR(ERR_INTERNAL_MEM); } attr->attr_val = xml_strdup(attr_val); if (!attr->attr_val) { xml_free_attr(attr); return SET_ERROR(ERR_INTERNAL_MEM); } attr->attr_ns = ns_id; dlq_enque(attr, attrs); return NO_ERR; } /* xml_add_attr */ /******************************************************************** * FUNCTION xml_add_qattr * * add a qualified attribute to an attribute list with a prefix * * INPUTS: * attrs == attribute queue to init * ns_id == namespace ID (use XMLNS_NONE if no prefix desired) * attr_qname == qualified attribute name string * plen == attribute prefix length * attr_val == attribute value string * res == address of return status * * OUTPUTS: * *res == return status * RETURNS: * pointer to attr that was added, NULL if none added *********************************************************************/ xml_attr_t * xml_add_qattr (xml_attrs_t *attrs, xmlns_id_t ns_id, const xmlChar *attr_qname, uint32 plen, const xmlChar *attr_val, status_t *res) { xml_attr_t *attr; #ifdef DEBUG if (!attrs || !attr_qname || !attr_val || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif attr = xml_new_attr(); if (!attr) { *res = ERR_INTERNAL_MEM; return NULL; } attr->attr_dname = xml_strdup(attr_qname); if (!attr->attr_dname) { xml_free_attr(attr); *res = ERR_INTERNAL_MEM; return NULL; } attr->attr_qname = attr->attr_dname; attr->attr_name = attr->attr_dname+plen; attr->attr_val = xml_strdup(attr_val); if (!attr->attr_val) { xml_free_attr(attr); *res = ERR_INTERNAL_MEM; return NULL; } attr->attr_ns = ns_id; dlq_enque(attr, attrs); *res = NO_ERR; return attr; } /* xml_add_qattr */ /******************************************************************** * FUNCTION xml_add_xmlns_attr * * add an xmlns decl to the attribute Queue * * INPUTS: * attrs == attribute queue to add to * ns_id == namespace ID of the xmlns target * pfix == namespace prefix string assigned * == NULL for default namespace * * RETURNS: * NO_ERR if all okay *********************************************************************/ status_t xml_add_xmlns_attr (xml_attrs_t *attrs, xmlns_id_t ns_id, const xmlChar *pfix) { xml_attr_t *attr; xmlChar *s; const xmlChar *nsval; uint32 len; #ifdef DEBUG if (!attrs) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* get a new attr struct */ attr = xml_new_attr(); if (!attr) { return ERR_INTERNAL_MEM; } /* get the namespace URI value */ nsval = xmlns_get_ns_name(ns_id); if (!nsval) { xml_free_attr(attr); return SET_ERROR(ERR_INTERNAL_VAL); } /* copy the namespace URI as the attr value */ attr->attr_val = xml_strdup(nsval); if (!attr->attr_val) { xml_free_attr(attr); return SET_ERROR(ERR_INTERNAL_MEM); } /* get the dname buffer length to malloc */ len = XMLNS_LEN+1; if (pfix) { len += (xml_strlen(pfix) + 1); } /* get a name buffer */ attr->attr_dname = m__getMem(len); if (!attr->attr_dname) { xml_free_attr(attr); return ERR_INTERNAL_MEM; } attr->attr_qname = attr->attr_dname; /* construct an xmlns:prefix string in the name buffer */ s = attr->attr_dname; s += xml_strcpy(attr->attr_dname, XMLNS); /* point the name field at the prefix value if there is one */ if (pfix) { *s++ = XMLNS_SEPCH; attr->attr_name = s; while (*pfix) { *s++ = *pfix++; } } else { attr->attr_name = attr->attr_dname; } *s = 0; attr->attr_ns = xmlns_ns_id(); attr->attr_xmlns_ns = ns_id; dlq_enque(attr, attrs); return NO_ERR; } /* xml_add_xmlns_attr */ /******************************************************************** * FUNCTION xml_add_xmlns_attr_string * * add an xmlns decl to the attribute Queue * * INPUTS: * attrs == attribute queue to add to * ns == namespace URI string of the xmlns target * pfix == namespace prefix string assigned * == NULL for default namespace * * RETURNS: * NO_ERR if all okay *********************************************************************/ status_t xml_add_xmlns_attr_string (xml_attrs_t *attrs, const xmlChar *ns, const xmlChar *pfix) { xml_attr_t *attr; xmlChar *s; uint32 len; #ifdef DEBUG if (!attrs || !ns) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* get a new attr struct */ attr = xml_new_attr(); if (!attr) { return ERR_INTERNAL_MEM; } /* copy the namespace URI as the attr value */ attr->attr_val = xml_strdup(ns); if (!attr->attr_val) { xml_free_attr(attr); return SET_ERROR(ERR_INTERNAL_MEM); } /* get the dname buffer length to malloc */ len = XMLNS_LEN+1; if (pfix) { len += (xml_strlen(pfix) + 1); } /* get a name buffer */ attr->attr_dname = m__getMem(len); if (!attr->attr_dname) { xml_free_attr(attr); return ERR_INTERNAL_MEM; } attr->attr_qname = attr->attr_dname; /* construct an xmlns:prefix string in the name buffer */ s = attr->attr_dname; s += xml_strcpy(attr->attr_dname, XMLNS); /* point the name field at the prefix value if there is one */ if (pfix) { *s++ = XMLNS_SEPCH; attr->attr_name = s; while (*pfix) { *s++ = *pfix++; } } else { attr->attr_name = attr->attr_dname; } *s = 0; attr->attr_ns = xmlns_ns_id(); attr->attr_xmlns_ns = 0; /*****/ dlq_enque(attr, attrs); return NO_ERR; } /* xml_add_xmlns_attr_string */ /******************************************************************** * FUNCTION xml_add_inv_xmlns_attr * * add an xmlns decl to the attribute Queue * for an INVALID namespace. This is needed for the * error-info element within the rpc-error report * * INPUTS: * attrs == attribute queue to add to * ns_id == namespace ID of the xmlns target * pfix == namespace prefix string assigned * == NULL for default namespace * nsval == namespace URI value of invalid namespace * * RETURNS: * NO_ERR if all okay *********************************************************************/ status_t xml_add_inv_xmlns_attr (xml_attrs_t *attrs, xmlns_id_t ns_id, const xmlChar *pfix, const xmlChar *nsval) { xml_attr_t *attr; xmlChar *s; uint32 len; #ifdef DEBUG if (!attrs) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* get a new attr struct */ attr = xml_new_attr(); if (!attr) { return ERR_INTERNAL_MEM; } if (!nsval) { nsval = (const xmlChar *)"INVALID"; } /* copy the namespace URI as the attr value */ attr->attr_val = xml_strdup(nsval); if (!attr->attr_val) { xml_free_attr(attr); return SET_ERROR(ERR_INTERNAL_MEM); } /* get the dname buffer length to malloc */ len = XMLNS_LEN+1; if (pfix) { len += (xml_strlen(pfix) + 1); } /* get a name buffer */ attr->attr_dname = m__getMem(len); if (!attr->attr_dname) { xml_free_attr(attr); return ERR_INTERNAL_MEM; } attr->attr_qname = attr->attr_dname; /* construct an xmlns:prefix string in the name buffer */ s = attr->attr_dname; s += xml_strcpy(attr->attr_dname, XMLNS); /* point the name field at the prefix value if there is one */ if (pfix) { *s++ = XMLNS_SEPCH; attr->attr_name = s; while (*pfix) { *s++ = *pfix++; } } else { attr->attr_name = attr->attr_dname; } *s = 0; attr->attr_ns = xmlns_ns_id(); attr->attr_xmlns_ns = ns_id; dlq_enque(attr, attrs); return NO_ERR; } /* xml_add_inv_xmlns_attr */ /******************************************************************** * FUNCTION xml_first_attr * * get the first attribute in the list * * INPUTS: * attrs == attribute queue to get from * RETURNS: * pointer to first entry or NULL if none *********************************************************************/ xml_attr_t * xml_first_attr (xml_attrs_t *attrs) { #ifdef DEBUG if (!attrs) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (xml_attr_t *)dlq_firstEntry(attrs); } /* xml_first_attr */ /******************************************************************** * FUNCTION xml_get_first_attr * * get the first attribute in the attrs list, from an xml_node_t param * * INPUTS: * node == node with the attrQ to use * RETURNS: * pointer to first entry or NULL if none *********************************************************************/ xml_attr_t * xml_get_first_attr (const xml_node_t *node) { #ifdef DEBUG if (!node) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (xml_attr_t *)dlq_firstEntry(&node->attrs); } /* xml_get_first_attr */ /******************************************************************** * FUNCTION xml_next_attr * * get the next attribute in the list * * INPUTS: * attr == attribute entry to get next for * * RETURNS: * pointer to the next entry or NULL if none *********************************************************************/ xml_attr_t * xml_next_attr (xml_attr_t *attr) { #ifdef DEBUG if (!attr) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (xml_attr_t *)dlq_nextEntry(attr); } /* xml_next_attr */ /******************************************************************** * FUNCTION xml_clean_attrs * * clean an xml_attrs_t variable * * INPUTS: * attrs == attribute queue to clean * RETURNS: * none *********************************************************************/ void xml_clean_attrs (xml_attrs_t *attrs) { xml_attr_t *attr; #ifdef DEBUG if (!attrs) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (!dlq_empty(attrs)) { attr = (xml_attr_t *)dlq_deque(attrs); xml_free_attr(attr); } } /* xml_clean_attrs */ /******************************************************************** * FUNCTION xml_find_attr * * Must be called after xxx_xml_consume_node * Go looking for the specified attribute node * INPUTS: * node == xml_node_t to check * nsid == namespace ID of attribute to find * attrname == attribute name to find * RETURNS: * pointer to found xml_attr_t or NULL if not found *********************************************************************/ xml_attr_t * xml_find_attr (xml_node_t *node, xmlns_id_t nsid, const xmlChar *attrname) { #ifdef DEBUG if (!node || !attrname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return xml_find_attr_q(&node->attrs, nsid, attrname); } /* xml_find_attr */ /******************************************************************** * FUNCTION xml_find_attr_q * * Must be called after xxx_xml_consume_node * Go looking for the specified attribute node * INPUTS: * node == xml_node_t to check * nsid == namespace ID of attribute to find * attrname == attribute name to find * RETURNS: * pointer to found xml_attr_t or NULL if not found *********************************************************************/ xml_attr_t * xml_find_attr_q (xml_attrs_t *attrs, xmlns_id_t nsid, const xmlChar *attrname) { xml_attr_t *attr; #ifdef DEBUG if (!attrs || !attrname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (attr = (xml_attr_t *)dlq_firstEntry(attrs); attr != NULL; attr = (xml_attr_t *)dlq_nextEntry(attr)) { if (nsid && attr->attr_ns) { if (nsid==attr->attr_ns && !xml_strcmp(attr->attr_name, attrname)) { return attr; } } else if (!xml_strcmp(attr->attr_name, attrname)) { return attr; } } return NULL; } /* xml_find_attr_q */ /******************************************************************** * FUNCTION xml_find_ro_attr * * Must be called after xxx_xml_consume_node * Go looking for the specified attribute node * INPUTS: * node == xml_node_t to check * nsid == namespace ID of attribute to find * attrname == attribute name to find * RETURNS: * pointer to found xml_attr_t or NULL if not found *********************************************************************/ const xml_attr_t * xml_find_ro_attr (const xml_node_t *node, xmlns_id_t nsid, const xmlChar *attrname) { const xml_attr_t *attr; #ifdef DEBUG if (!node || !attrname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (attr = (const xml_attr_t *)dlq_firstEntry(&node->attrs); attr != NULL; attr = (const xml_attr_t *)dlq_nextEntry(attr)) { if (nsid && attr->attr_ns) { if (nsid==attr->attr_ns && !xml_strcmp(attr->attr_name, attrname)) { return attr; } } else if (!xml_strcmp(attr->attr_name, attrname)) { return attr; } } return NULL; } /* xml_find_ro_attr */ /******************************************************************** * FUNCTION xml_strlen * * String len for xmlChar -- does not check for buffer overflow * INPUTS: * str == buffer to check * RETURNS: * number of xmlChars before null terminator found *********************************************************************/ uint32 xml_strlen (const xmlChar *str) { uint32 len = 0; #ifdef DEBUG if (!str) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif while (*str++) { len++; } return len; } /* xml_strlen */ /******************************************************************** * FUNCTION xml_strlen_sp * * get length and check if any whitespace at the same time * String len for xmlChar -- does not check for buffer overflow * Check for any whitespace in the string as well * * INPUTS: * str == buffer to check * sp == address of any-spaces-test output * * OUTPUTS: * *sp == TRUE if any whitespace found in the string * * RETURNS: * number of xmlChars before null terminator found *********************************************************************/ uint32 xml_strlen_sp (const xmlChar *str, boolean *sp) { uint32 len = 0; #ifdef DEBUG if (!str || !sp) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif *sp = FALSE; for (;;) { if (!*str) { return len; } else if (!*sp) { if (xml_isspace(*str)) { *sp = TRUE; } } str++; len++; } /*NOTREACHED*/ } /* xml_strlen_sp */ /******************************************************************** * FUNCTION xml_strcpy * * String copy for xmlChar -- does not check for buffer overflow * * Even if return value is zero, the EOS char is copied * * INPUTS: * copyTo == buffer to copy into * copyFrom == zero-terminated xmlChar string to copy from * RETURNS: * number of bytes copied to the result buffer, not including EOS *********************************************************************/ uint32 xml_strcpy (xmlChar *copyTo, const xmlChar *copyFrom) { uint32 cnt; #ifdef DEBUG if (!copyTo || !copyFrom) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif cnt = 0; while ((*copyTo++ = *copyFrom++) != (xmlChar)0x0) { cnt++; } return cnt; } /* xml_strcpy */ /******************************************************************** * FUNCTION xml_strncpy * * String copy for xmlChar -- checks for buffer overflow * INPUTS: * copyTo == buffer to copy into * copyFrom == zero-terminated xmlChar string to copy from * maxLen == max number of xmlChars to copy, but does include * the terminating zero that is added if this max is reached * RETURNS: * number of bytes copied to the result buffer *********************************************************************/ uint32 xml_strncpy (xmlChar *copyTo, const xmlChar *copyFrom, uint32 maxlen) { uint32 i; #ifdef DEBUG if (!copyTo || !copyFrom) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif for (i=0; i *s2) { return 1; } else if (!*s1 && !*s2) { return 0; } s1++; s2++; } /*NOTREACHED*/ } /* xml_strcmp */ /******************************************************************** * FUNCTION xml_stricmp * * Case insensitive string compare for xmlChar * INPUTS: * s1 == zero-terminated xmlChar string to compare * s2 == zero-terminated xmlChar string to compare * * RETURNS: * == -1 : string 1 is less than string 2 * == 0 : strings are equal * == 1 : string 1 is greater than string 2 *********************************************************************/ int xml_stricmp (const xmlChar *s1, const xmlChar *s2) { xmlChar s1char, s2char; #ifdef DEBUG if (s1 == NULL || s2 == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif for (;;) { s1char = (xmlChar)tolower((int)*s1); s2char = (xmlChar)tolower((int)*s2); if (s1char < s2char) { return -1; } else if (s1char > s2char) { return 1; } else if (s1char == 0 && s2char == 0) { return 0; } s1++; s2++; } /*NOTREACHED*/ } /* xml_stricmp */ /******************************************************************** * FUNCTION xml_strncmp * * String compare for xmlChar for at most 'maxlen' xmlChars * INPUTS: * s1 == zero-terminated xmlChar string to compare * s2 == zero-terminated xmlChar string to compare * maxlen == max number of xmlChars to compare * * RETURNS: * == -1 : string 1 is less than string 2 * == 0 : strings are equal * == 1 : string 1 is greater than string 2 *********************************************************************/ int xml_strncmp (const xmlChar *s1, const xmlChar *s2, uint32 maxlen) { uint32 i; #ifdef DEBUG if (!s1 || !s2) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif for (i=0; i *s2) { return 1; } else if (!*s1 && !*s2) { return 0; } s1++; s2++; } return 0; } /* xml_strncmp */ /******************************************************************** * FUNCTION xml_strnicmp * * Case insensitive string compare for xmlChar for at * most 'maxlen' xmlChars * * INPUTS: * s1 == zero-terminated xmlChar string to compare * s2 == zero-terminated xmlChar string to compare * maxlen == max number of xmlChars to compare * * RETURNS: * == -1 : string 1 is less than string 2 * == 0 : strings are equal * == 1 : string 1 is greater than string 2 *********************************************************************/ int xml_strnicmp (const xmlChar *s1, const xmlChar *s2, uint32 maxlen) { uint32 i; xmlChar s1char, s2char; #ifdef DEBUG if (s1 == NULL || s2 == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif for (i=0; i s2char) { return 1; } else if (s1char == 0 && s2char == 0) { return 0; } s1++; s2++; } return 0; } /* xml_strnicmp */ /******************************************************************** * FUNCTION xml_isspace * * Check if an xmlChar is a space char * INPUTS: * ch == xmlChar to check * RETURNS: * TRUE if a space, FALSE otherwise *********************************************************************/ boolean xml_isspace (uint32 ch) { char c; if (ch & bit7) { return FALSE; } else { c = (char)ch; return isspace((int)c) ? TRUE : FALSE; } /*NOTREACHED*/ } /* xml_isspace */ /******************************************************************** * FUNCTION xml_isspace_str * * Check if an xmlChar string is all whitespace chars * INPUTS: * str == xmlChar string to check * RETURNS: * TRUE if all whitespace, FALSE otherwise *********************************************************************/ boolean xml_isspace_str (const xmlChar *str) { #ifdef DEBUG if (!str) { SET_ERROR(ERR_INTERNAL_PTR); return TRUE; } #endif while (*str) { if (xml_isspace(*str)) { str++; } else { return FALSE; } } return TRUE; } /* xml_isspace_str */ /******************************************************************** * FUNCTION xml_strcmp_nosp * * String compare for xmlChar for 2 strings, but ignoring * whitespace differences. All consecutive whitespace is * treated as one space char for comparison purposes * * Needed by yangdiff to compare description clauses which * have been reformated * * INPUTS: * s1 == zero-terminated xmlChar string to compare * s2 == zero-terminated xmlChar string to compare * * RETURNS: * == -1 : string 1 is less than string 2 * == 0 : strings are equal * == 1 : string 1 is greater than string 2 *********************************************************************/ int xml_strcmp_nosp (const xmlChar *s1, const xmlChar *s2) { #ifdef DEBUG if (!s1 || !s2) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif /* skip starting whitespace */ while (*s1 && xml_isspace(*s1)) { s1++; } while (*s2 && xml_isspace(*s2)) { s2++; } /* use the first string as the master, the 2nd must track * to the first, which means all non-whitespace sections * are identical in both strings */ while (*s1 && *s2) { /* both strings point at non whitespace chars */ if (*s1 < *s2) { return -1; } else if (*s1 > *s2) { return 1; } else if (!*s1 && !*s2) { return 0; } /* both strings point at the same non-whitespace char */ s1++; s2++; if (xml_isspace(*s1) && xml_isspace(*s2)) { /* both strings point at some WSP ch, resynch non-WSP */ while (*s1 && xml_isspace(*s1)) { s1++; } while (*s2 && xml_isspace(*s2)) { s2++; } } } if (*s1 == *s2) { return 0; } else if (*s1 < *s2) { return 1; } else { return -1; } } /* xml_strcmp_nosp */ /******************************************************************** * FUNCTION xml_copy_clean_string * * Get a malloced string contained the converted string * from the input * Get rid of the leading and trailing whilespace * * Character entities have already been removed by the xmlTextReader * * INPUTS: * str == xmlChar string to check * RETURNS: * pointer to new malloced string *********************************************************************/ xmlChar * xml_copy_clean_string (const xmlChar *str) { const xmlChar *newstart, *endstr; xmlChar *newstr; uint32 len, newlen; boolean allwhitespace; #ifdef DEBUG if (!str) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif allwhitespace = FALSE; len = xml_strlen(str); newlen = len; newstart = NULL; if (len) { newstart = str; endstr = str + len - 1; while (endstr >= str && xml_isspace(*endstr)) { endstr--; } if (endstr < str) { allwhitespace = TRUE; } else { newstart = str; while (xml_isspace(*newstart)) { newstart++; } } if (!allwhitespace) { newlen = (uint32)(endstr - newstart + 1); } } newstr = (xmlChar *)m__getMem(newlen+1); if (!newstr) { SET_ERROR(ERR_INTERNAL_MEM); return NULL; } if (len == newlen) { xml_strcpy(newstr, str); } else { xml_strncpy(newstr, newstart, newlen); } return newstr; } /* xml_copy_clean_string */ /******************************************************************** * FUNCTION xml_convert_char_entity * * Convert an XML character entity into a single xmlChar * * INPUTS: * str == string pointing to start of char entity * OUTPUTS: * *used == number of chars consumed * RETURNS: * converted xmlChar *********************************************************************/ xmlChar xml_convert_char_entity (const xmlChar *str, uint32 *used) { xmlChar buff[MAX_CHAR_ENT+1]; uint32 i; #ifdef DEBUG if (!str || !used) { SET_ERROR(ERR_INTERNAL_PTR); return (xmlChar)' '; /* error -- return blank */ } #endif /* make sure the start is okay */ if (*str != '&') { *used = 1; return *str; } else { str++; } /* get the char entity name */ i = 0; while (*str && (*str != ';') && (ins_id == element namespace to check * elname == element name to check * * OUTPUTS: * *id == namespace ID found or 0 if none * *pfix_len == filled in > 0 if one found * real element name will start at pfix_len+1 * if pfix is non-NULL * *badns == pointer to unknown namespace if error returned * RETURNS: * status; could be error if namespace specified but not supported *********************************************************************/ status_t xml_check_ns (xmlTextReaderPtr reader, const xmlChar *elname, xmlns_id_t *id, uint32 *pfix_len, const xmlChar **badns) { const xmlChar *str; xmlns_t *ns; #ifdef DEBUG if (!reader || !elname || !id || !pfix_len || !badns) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif *id = 0; *pfix_len = 0; *badns = NULL; /* check for a prefix in the element namestring */ str = elname; while (*str && *str != XMLNS_SEPCH) { str++; } if (*str) { /* found a prefix */ *pfix_len = (uint32)(str-elname+1); } /* check the namespace associated with this node */ str = xmlTextReaderConstNamespaceUri(reader); if (str != NULL) { ns = def_reg_find_ns(str); if (ns) { *id = ns->ns_id; } else { *id = xmlns_inv_id(); *badns = str; return ERR_NCX_UNKNOWN_NS; } } /* else treat as no namespace, no error */ return NO_ERR; } /* xml_check_ns */ /******************************************************************** * FUNCTION xml_check_qname_content * * Check if the string node content is a likely QName * If so, then get the namespace URI for the prefix, * look it up in def_reg, and store the NSID in the node * * INPUTS: * reader == reader to use * node == current string node in progress * * OUTPUTS: * *********************************************************************/ void xml_check_qname_content (xmlTextReaderPtr reader, xml_node_t *node) { xmlChar *str, *ns; str = node->simfree; assert( str && "str is NULL" ); /* find the first colon char */ while (*str && *str != ':') { str++; } if (*str == ':') { *str = 0; ns = xmlTextReaderLookupNamespace(reader, node->simfree); if (ns) { node->contentnsid = xmlns_find_ns_by_name(ns); xmlFree(ns); } *str = ':'; } else { ns = xmlTextReaderLookupNamespace(reader, NULL); if (ns) { node->contentnsid = xmlns_find_ns_by_name(ns); xmlFree(ns); } } } /* xml_check_qname_content */ /******************************************************************** * FUNCTION xml_get_namespace_id * * Get the namespace for the specified prefix (may be NULL) * Use the current XML reader context to resolve the prefix * * INPUTS: * reader == XML reader to use * prefix == prefix string to use (NULL == default namespace) * prefixlen == N if not a Z-terminated string * == 0 if it is a Z-terminated string * retnsid == address of return namespace ID * * OUTPUTS: * *retnsid == XMLNS ID for the namespace, 0 if none found * INVALID ID if unknown * RETURNS: * status *********************************************************************/ status_t xml_get_namespace_id (xmlTextReaderPtr reader, const xmlChar *prefix, uint32 prefixlen, xmlns_id_t *retnsid) { xmlChar *str, *ns; #ifdef DEBUG if (!reader || !retnsid) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif *retnsid = 0; if (prefix && prefixlen) { str = xml_strndup(prefix, prefixlen); if (!str) { return ERR_INTERNAL_MEM; } ns = xmlTextReaderLookupNamespace(reader, str); if (ns) { *retnsid = xmlns_find_ns_by_name(ns); xmlFree(ns); } m__free(str); } else { ns = xmlTextReaderLookupNamespace(reader, NULL); if (ns) { *retnsid = xmlns_find_ns_by_name(ns); xmlFree(ns); } } if (*retnsid) { return NO_ERR; } else { return ERR_NCX_UNKNOWN_NAMESPACE; } } /* xml_get_namespace_id */ /******************************************************************** * FUNCTION xml_consume_node * * parse function for YIN input * * INPUTS: * reader == xmlTextReader to use * node == address of node pointer to use * MUST be an initialized node * with xml_new_node or xml_init_node * nserr == TRUE if bad namespace should be checked * == FALSE if not * adv == TRUE if advance reader * == FALSE if no advance (reget current node) * * OUTPUTS: * *xmlnode == filled in or malloced and filled-in xml node * * RETURNS: * status of the operation *********************************************************************/ status_t xml_consume_node (xmlTextReaderPtr reader, xml_node_t *xmlnode, boolean nserr, boolean adv) { const xmlChar *badns; xmlChar *valstr, *namestr; uint32 len; status_t res, res2; int ret, nodetyp; boolean done; #ifdef DEBUG if (reader == NULL || xmlnode == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* init local vars */ done = FALSE; res = NO_ERR; res2 = NO_ERR; xmlnode->nodetyp = XML_NT_NONE; /* loop past any unused xmlTextReader node types */ while (!done) { if (adv) { /* advance the node pointer */ ret = xmlTextReaderRead(reader); if (ret != 1) { /* do not treat this as an internal error */ return ERR_XML_READER_EOF; } } else { done = TRUE; } /* get the node depth to match the end node correctly */ xmlnode->depth = xmlTextReaderDepth(reader); if (xmlnode->depth == -1) { /* this never actaully happens */ SET_ERROR(ERR_XML_READER_INTERNAL); xmlnode->depth = 0; } /* get the internal nodetype, check it and convert it */ nodetyp = xmlTextReaderNodeType(reader); switch (nodetyp) { case XML_ELEMENT_NODE: /* classify element as empty or start */ if (xmlTextReaderIsEmptyElement(reader)) { xmlnode->nodetyp = XML_NT_EMPTY; } else { xmlnode->nodetyp = XML_NT_START; } done = TRUE; break; case XML_ELEMENT_DECL: xmlnode->nodetyp = XML_NT_END; done = TRUE; break; case XML_CDATA_SECTION_NODE: case XML_TEXT_NODE: /* case XML_DTD_NODE: */ xmlnode->nodetyp = XML_NT_STRING; done = TRUE; break; default: /* unused node type -- keep trying */ #ifdef XML_UTIL_DEBUG log_debug3("\nxml_consume_node: skip unused node (%s)", xml_get_node_name(nodetyp)); #endif if (done) { /* re-get of current node should not fail */ res = ERR_XML_READER_INTERNAL; } } } /* finish the node, depending on its type */ switch (xmlnode->nodetyp) { case XML_NT_START: case XML_NT_END: case XML_NT_EMPTY: /* get the element QName */ namestr = xml_strdup(xmlTextReaderConstName(reader)); if (!namestr) { res = ERR_INTERNAL_MEM; } else { xmlnode->qname = namestr; /* check for namespace prefix in the name * only error returned is unknown-namespace */ len = 0; res = xml_check_ns(reader, namestr, &xmlnode->nsid, &len, &badns); if (!nserr && res != NO_ERR) { xmlnode->nsid = xmlns_inv_id(); res = NO_ERR; } /* set the element name to the char after the prefix, if any */ xmlnode->elname = (const xmlChar *)(namestr+len); /* get all the attributes, except for XML_NT_END */ if (xmlnode->nodetyp != XML_NT_END) { res2 = get_attrs(reader, &xmlnode->attrs, nserr); } /* Set the node module */ if (xmlnode->nsid) { xmlnode->module = xmlns_get_module(xmlnode->nsid); } else { /* no entry, use the default owner (netconf) */ xmlnode->module = NCX_DEF_MODULE; } } break; case XML_NT_STRING: /* get the text value */ xmlnode->simval = NULL; valstr = xmlTextReaderValue(reader); if (valstr) { xmlnode->simfree = xml_copy_clean_string(valstr); if (xmlnode->simfree) { xmlnode->simlen = xml_strlen(xmlnode->simfree); xmlnode->simval = (const xmlChar *)xmlnode->simfree; } /* see if this is a QName string; if so save the NSID */ xml_check_qname_content(reader, xmlnode); xmlFree(valstr); } if (!xmlnode->simval) { /* prevent a NULL ptr reference */ xmlnode->simval = (const xmlChar *)""; xmlnode->simlen = 0; xmlnode->simfree = NULL; } break; default: break; } #ifdef XML_UTIL_DEBUG log_debug3("\nxml_consume_node: return (%d)", (res==NO_ERR) ? res2 : res); if (LOGDEBUG3) { xml_dump_node(xmlnode); } #endif /* return general error first, then attribute error * It doesn't really matter since the caller will * assume all error reports have been queued upon return */ return (res==NO_ERR) ? res2 : res; } /* xml_consume_node */ /******************************************************************** * FUNCTION xml_skip_subtree * * Already encountered an error, so advance nodes until the * matching start-node is reached or a terminating error occurs * - end of input * - start depth level reached * * INPUTS: * reader == XmlReader already initialized from File, Memory, * or whatever * startnode == xml_node_t of the start node of the sub-tree to skip * RETURNS: * status of the operation * SIDE EFFECTS: * the xmlreader state is advanced until the current node is the * end node of the specified start node or a fatal error occurs *********************************************************************/ status_t xml_skip_subtree (xmlTextReaderPtr reader, const xml_node_t *startnode) { xml_node_t node; const xmlChar *qname, *badns; uint32 len; int ret, depth, nodetyp; xmlns_id_t nsid; boolean done, justone; status_t res; #ifdef DEBUG if (!reader || !startnode) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif justone = FALSE; switch (startnode->nodetyp) { case XML_NT_START: break; case XML_NT_EMPTY: return NO_ERR; case XML_NT_STRING: justone = TRUE; break; case XML_NT_END: return NO_ERR; default: return SET_ERROR(ERR_INTERNAL_VAL); } xml_init_node(&node); res = xml_consume_node(reader, &node, TRUE, FALSE); if (res == NO_ERR) { res = xml_endnode_match(startnode, &node); if (res == NO_ERR) { xml_clean_node(&node); return NO_ERR; } } xml_clean_node(&node); if (justone) { return NO_ERR; } done = FALSE; while (!done) { /* advance the node pointer */ ret = xmlTextReaderRead(reader); if (ret != 1) { /* fatal error */ return ERR_XML_READER_EOF; } /* get the node depth to match the end node correctly */ depth = xmlTextReaderDepth(reader); if (depth == -1) { /* not sure if this can happen, treat as fatal error */ return ERR_XML_READER_INTERNAL; } else if (depth <= startnode->depth) { /* this depth override will cause errors to be ignored * - wrong namespace in matching end node * - unknown namespace in matching end node * - wrong name in 'matching' end node */ done = TRUE; } /* get the internal nodetype, check it and convert it */ nodetyp = xmlTextReaderNodeType(reader); /* get the element QName */ qname = xmlTextReaderConstName(reader); if (qname) { /* check for namespace prefix in the name * only error is 'unregistered namespace ID' * which doesn't matter in this case */ nsid = 0; (void)xml_check_ns(reader, qname, &nsid, &len, &badns); } else { qname = (const xmlChar *)""; } /* check the normal case to see if the search is done */ if (depth == startnode->depth && !xml_strcmp(qname, startnode->qname) && nodetyp == XML_ELEMENT_DECL) { done = TRUE; } #ifdef XML_UTIL_DEBUG log_debug3("\nxml_skip: %s L:%d T:%s", qname, depth, xml_get_node_name(nodetyp)); #endif } return NO_ERR; } /* xml_skip_subtree */ /* END xml_util.c */ yuma123_2.14/netconf/src/ncx/Makefile.am0000664000175000017500000000624114770023131020175 0ustar vladimirvladimirlib_LTLIBRARIES = libyumancx.la libyumancx_la_SOURCES = \ $(top_srcdir)/netconf/src/ncx/b64.c \ $(top_srcdir)/netconf/src/ncx/blob.c \ $(top_srcdir)/netconf/src/ncx/bobhash.c \ $(top_srcdir)/netconf/src/ncx/cap.c \ $(top_srcdir)/netconf/src/ncx/cfg.c \ $(top_srcdir)/netconf/src/ncx/cli.c \ $(top_srcdir)/netconf/src/ncx/conf.c \ $(top_srcdir)/netconf/src/ncx/def_reg.c \ $(top_srcdir)/netconf/src/ncx/dlq.c \ $(top_srcdir)/netconf/src/ncx/ext.c \ $(top_srcdir)/netconf/src/ncx/grp.c \ $(top_srcdir)/netconf/src/ncx/help.c \ $(top_srcdir)/netconf/src/ncx/json_wr.c \ $(top_srcdir)/netconf/src/ncx/log.c \ $(top_srcdir)/netconf/src/ncx/ncx_appinfo.c \ $(top_srcdir)/netconf/src/ncx/ncx.c \ $(top_srcdir)/netconf/src/ncx/ncx_feature.c \ $(top_srcdir)/netconf/src/ncx/ncx_list.c \ $(top_srcdir)/netconf/src/ncx/ncxmod.c \ $(top_srcdir)/netconf/src/ncx/ncx_num.c \ $(top_srcdir)/netconf/src/ncx/ncx_str.c \ $(top_srcdir)/netconf/src/ncx/obj.c \ $(top_srcdir)/netconf/src/ncx/obj_help.c \ $(top_srcdir)/netconf/src/ncx/op.c \ $(top_srcdir)/netconf/src/ncx/plock.c \ $(top_srcdir)/netconf/src/ncx/plock_cb.c \ $(top_srcdir)/netconf/src/ncx/rpc.c \ $(top_srcdir)/netconf/src/ncx/rpc_err.c \ $(top_srcdir)/netconf/src/ncx/runstack.c \ $(top_srcdir)/netconf/src/ncx/send_buff.c \ $(top_srcdir)/netconf/src/ncx/ses.c \ $(top_srcdir)/netconf/src/ncx/ses_msg.c \ $(top_srcdir)/netconf/src/ncx/status.c \ $(top_srcdir)/netconf/src/ncx/tk.c \ $(top_srcdir)/netconf/src/ncx/top.c \ $(top_srcdir)/netconf/src/ncx/tstamp.c \ $(top_srcdir)/netconf/src/ncx/typ.c \ $(top_srcdir)/netconf/src/ncx/val.c \ $(top_srcdir)/netconf/src/ncx/val_set_cplxval_obj.c \ $(top_srcdir)/netconf/src/ncx/val_get_leafref_targval.c \ $(top_srcdir)/netconf/src/ncx/val_util.c \ $(top_srcdir)/netconf/src/ncx/var.c \ $(top_srcdir)/netconf/src/ncx/xml_msg.c \ $(top_srcdir)/netconf/src/ncx/xmlns.c \ $(top_srcdir)/netconf/src/ncx/xml_util.c \ $(top_srcdir)/netconf/src/ncx/xml_val.c \ $(top_srcdir)/netconf/src/ncx/xml_wr.c \ $(top_srcdir)/netconf/src/ncx/xml_rd.c \ $(top_srcdir)/netconf/src/ncx/xpath1.c \ $(top_srcdir)/netconf/src/ncx/xpath.c \ $(top_srcdir)/netconf/src/ncx/xpath_wr.c \ $(top_srcdir)/netconf/src/ncx/xpath_yang.c \ $(top_srcdir)/netconf/src/ncx/yang.c \ $(top_srcdir)/netconf/src/ncx/yang_ext.c \ $(top_srcdir)/netconf/src/ncx/yang_grp.c \ $(top_srcdir)/netconf/src/ncx/yang_obj.c \ $(top_srcdir)/netconf/src/ncx/yang_parse.c \ $(top_srcdir)/netconf/src/ncx/yang_typ.c \ $(top_srcdir)/netconf/src/ncx/yin.c \ $(top_srcdir)/netconf/src/ncx/yinyang.c \ $(top_srcdir)/netconf/src/ncx/val_parse.c \ $(top_srcdir)/netconf/src/ncx/uptime.c \ $(top_srcdir)/netconf/src/ncx/val123.c libyumancx_la_CPPFLAGS = -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump $(XML_CPPFLAGS) -DNCXMOD_SIL_INSTALL_PATH=\"${netconfmoduledir}\" libyumancx_la_CPPFLAGS += -DSYSCONFDIR=\"@sysconfdir@\" libyumancx_la_CPPFLAGS += -DNETCONFMODULEDIR=\"@netconfmoduledir@\" libyumancx_la_CPPFLAGS += -DYUMA_DATAROOTDIR=\"@yuma_datarootdir@\" #libyumancx_la_CPPFLAGS += -DSES_DEBUG_TRACE -DSES_DEBUG libyumancx_la_LDFLAGS = -version-info 2:0:0 $(XML_LIBS) $(LIBS) -lrt yuma123_2.14/netconf/src/ncx/ncxmod.c0000664000175000017500000052111414770023131017576 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2012 - 2018, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: ncxmod.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 10nov05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "help.h" #include "log.h" #include "ncx.h" #include "ncxconst.h" #include "ncxtypes.h" #include "ncxmod.h" #include "ncx_list.h" #include "status.h" #include "tstamp.h" #include "xml_util.h" #include "yangconst.h" #include "yang_parse.h" #include "yang_obj.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* Enumeration of the basic value type classifications */ typedef enum ncxmod_mode_t_ { NCXMOD_MODE_NONE, NCXMOD_MODE_YANG, NCXMOD_MODE_YIN, NCXMOD_MODE_FILEYANG, NCXMOD_MODE_FILEYIN } ncxmod_mode_t; /* Enumeration of the file directory list modes */ typedef enum search_type_t_ { SEARCH_TYPE_NONE, SEARCH_TYPE_MODULE, SEARCH_TYPE_DATA, SEARCH_TYPE_SCRIPT } search_type_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static boolean ncxmod_init_done = FALSE; static const xmlChar *ncxmod_yuma_home; static xmlChar *ncxmod_home_cli; static xmlChar *ncxmod_yuma_home_cli; static xmlChar *ncxmod_yumadir_path; static const xmlChar *ncxmod_env_install; static const xmlChar *ncxmod_home; static const xmlChar *ncxmod_mod_path = NULL; static xmlChar *ncxmod_mod_path_cli; static const xmlChar *ncxmod_alt_path; static const xmlChar *ncxmod_data_path; static xmlChar *ncxmod_data_path_cli; static const xmlChar *ncxmod_run_path; static xmlChar *ncxmod_run_path_cli; static boolean ncxmod_subdirs; /******************************************************************** * FUNCTION is_yang_file * * Check the file suffix for .yang * * INPUTS: * buff == buffer to check * * RETURNS: * TRUE if .yang extension * FALSE otherwise *********************************************************************/ static boolean is_yang_file (const xmlChar *buff) { const xmlChar *str; uint32 count; count = xml_strlen(buff); if (count < 6) { return FALSE; } if (buff[count-5] != '.') { return FALSE; } str = &buff[count-4]; return (xml_strcmp(str, YANG_SUFFIX)) ? FALSE : TRUE; } /* is_yang_file */ /******************************************************************** * FUNCTION is_yin_file * * Check the file suffix for .yin * * INPUTS: * buff == buffer to check * * RETURNS: * TRUE if .yang extension * FALSE otherwise *********************************************************************/ static boolean is_yin_file (const xmlChar *buff) { const xmlChar *str; uint32 count; count = xml_strlen(buff); if (count < 5) { return FALSE; } if (buff[count-4] != '.') { return FALSE; } str = &buff[count-3]; return (xml_strcmp(str, YIN_SUFFIX)) ? FALSE : TRUE; } /* is_yin_file */ /******************************************************************** * FUNCTION prep_dirpath * * Setup the directory path in the buffer * Leave it with a trailing path-sep-char so a * file name can be added to the buffer * * INPUTS: * buff == buffer to use for filespec construction * bufflen == length of buffer * path == first piece of path string (may be NULL) * path2 == optional 2nd piece of path string (may be NULL) * cnt == address of return count of bytes added to buffer * * OUTPUTS: * buff filled in with path and path2 if present * *cnt == number of bytes added to buff * * RETURNS: * status *********************************************************************/ static status_t prep_dirpath (xmlChar *buff, uint32 bufflen, const xmlChar *path, const xmlChar *path2, uint32 *cnt) { xmlChar *str; uint32 pathlen, path2len, pathsep, path2sep, total; boolean needslash; *cnt = 0; *buff = 0; needslash = FALSE; if (!path) { return NO_ERR; } pathlen = xml_strlen(path); if (pathlen) { pathsep = (uint32)(path[pathlen-1] == NCXMOD_PSCHAR); } else { pathsep = 0; } if (path2) { path2len = xml_strlen(path2); path2sep = (uint32)(path2len && path2[path2len-1]==NCXMOD_PSCHAR); } else { path2len = 0; path2sep = 0; } total = pathlen + path2len; if (!pathsep) { if (path2 != NULL && *path2 != NCXMOD_PSCHAR) { needslash = TRUE; total++; } else if (path2 == NULL && !(is_yang_file(path) || is_yin_file(path))) { needslash = TRUE; total++; } } if (path2 && path2len && !path2sep) { total++; } if (*path == NCXMOD_HMCHAR && path[1] == NCXMOD_PSCHAR) { if (!ncxmod_home) { return ERR_FIL_BAD_FILENAME; } else { total += (xml_strlen(ncxmod_home) - 1); } } if (total >= bufflen) { log_error("\nncxmod: Path spec too long error. Max: %d Got %u\n", bufflen, total); return ERR_BUFF_OVFL; } str = buff; if (*path == NCXMOD_HMCHAR && path[1] == NCXMOD_PSCHAR) { str += xml_strcpy(str, ncxmod_home); str += xml_strcpy(str, &path[1]); } else { str += xml_strcpy(str, path); } if (needslash) { *str++ = NCXMOD_PSCHAR; } if (path2 && path2len) { str += xml_strcpy(str, path2); if (!path2sep) { *str++ = NCXMOD_PSCHAR; } } *str = 0; *cnt = (uint32)(str-buff); return NO_ERR; } /* prep_dirpath */ /******************************************************************** * FUNCTION make_module_filespec * * For YANG and YIN Modules Only!!! * * Construct a filespec out of a path name and a module name * * Used in several different modes: * Buffer mode: * input is pre-constructed in 'buff' * Path Mode * input is given in pieces, path and path2 are used * Current Dir Mode * Search Mod Path Mode * input is given in modname and revision * * INPUTS: * buff == buffer to use for filespec construction * bufflen == length of buffer * path == first piece of path string (may be NULL) * path2 == optional 2nd piece of path string (may be NULL) * modname == module name without file suffix (may be NULL) * revision == revision date string (may be NULL) * mode == suffix mode * == NCXMOD_MODE_YANG if YANG module and suffix is .yang * == NCXMOD_MODE_FILEYANG if YANG filespec * == NCXMOD_MODE_YIN if YIN module and suffix is .yin * == NCXMOD_MODE_FILEYIN if YIN filespec * usebuff == use buffer as-is, unless modname is present * done == address of return done flag * pcb == YANG parser control block (NULL if not used) * ptyp == YANG parser source type (YANG_PT_TOP if YANG top module) * * OUTPUTS: * *done == TRUE if module loaded and status==NO_ERR * or status != NO_ERR and fatal error occurred * The YANG pcb will also be updated * * == FALSE if file-not-found-error or some non-fatal * error so the search should continue if possible * * buff contains the full filespec of the found file * * RETURNS: * status *********************************************************************/ static status_t make_module_filespec (xmlChar *buff, uint32 bufflen, const xmlChar *path, const xmlChar *path2, const xmlChar *modname, const xmlChar *revision, ncxmod_mode_t mode, boolean usebuff) { xmlChar *p; const xmlChar *suffix; uint32 total, modlen, pathlen; status_t res=NO_ERR; boolean isyang; total = 0; switch (mode) { case NCXMOD_MODE_YANG: suffix = YANG_SUFFIX; isyang = TRUE; break; case NCXMOD_MODE_YIN: suffix = YIN_SUFFIX; isyang = FALSE; break; case NCXMOD_MODE_FILEYANG: suffix = EMPTY_STRING; isyang = TRUE; break; case NCXMOD_MODE_FILEYIN: suffix = EMPTY_STRING; isyang = FALSE; break; default: assert(0); } if (usebuff) { if (modname) { /* add module name and maybe revision to end of buffer, * if no overflow */ pathlen = xml_strlen(buff); total = pathlen + xml_strlen(modname) + ((revision) ? xml_strlen(revision) + 1 : 0) + xml_strlen(suffix) + 1; if (total >= bufflen) { log_error("\nncxmod: Filename too long error. Max: %d Got %u\n", bufflen, total); return ERR_BUFF_OVFL; } p = buff+pathlen; p += xml_strcpy(p, modname); if (revision) { *p++ = YANG_FILE_SEPCHAR; p += xml_strcpy(p, revision); } if (*suffix) { *p++ = '.'; xml_strcpy(p, suffix); } } } else { res = prep_dirpath(buff, bufflen, path, path2, &total); if (res != NO_ERR) { return res; } if (modname) { modlen = xml_strlen(modname); /* construct an complete module filespec * [@]. */ if (*suffix) { pathlen = xml_strlen(suffix) + 1; } else { pathlen = 0; } if (revision) { pathlen += (xml_strlen(revision) + 1); } if (total + pathlen + modlen >= bufflen) { log_info("\nncxmod: Filename too long " "error. Max: %d Got %u", bufflen, total + pathlen + modlen); return ERR_BUFF_OVFL; } /* add module name and suffix */ p = &buff[total]; p += xml_strcpy(p, modname); if (revision) { *p++ = YANG_FILE_SEPCHAR; p += xml_strcpy(p, revision); } if (*suffix) { *p++ = '.'; xml_strcpy(p, suffix); } } } return res; } /* make_module_filespec */ /******************************************************************** * FUNCTION try_module * * For YANG and YIN Modules Only!!! * Construct a filespec with make_module_filespec * and try to load the filespec as a YANG module * * INPUTS: * buff == buffer to use for filespec construction * bufflen == length of buffer * path == first piece of path string (may be NULL) * path2 == optional 2nd piece of path string (may be NULL) * modname == module name without file suffix (may be NULL) * revision == revision date string (may be NULL) * mode == suffix mode * == NCXMOD_MODE_YANG if YANG module and suffix is .yang * == NCXMOD_MODE_FILEYANG if YANG filespec * == NCXMOD_MODE_YIN if YIN module and suffix is .yin * == NCXMOD_MODE_FILEYIN if YIN filespec * usebuff == use buffer as-is, unless modname is present * done == address of return done flag * pcb == YANG parser control block (NULL if not used) * ptyp == YANG parser source type (YANG_PT_TOP if YANG top module) * * OUTPUTS: * *done == TRUE if module loaded and status==NO_ERR * or status != NO_ERR and fatal error occurred * The YANG pcb will also be updated * * == FALSE if file-not-found-error or some non-fatal * error so the search should continue if possible * * buff contains the full filespec of the found file * * RETURNS: * status *********************************************************************/ static status_t try_module (xmlChar *buff, uint32 bufflen, const xmlChar *path, const xmlChar *path2, const xmlChar *modname, const xmlChar *revision, ncxmod_mode_t mode, boolean usebuff, boolean *done, yang_pcb_t *pcb, yang_parsetype_t ptyp) { status_t res; boolean isyang; *done = FALSE; res = make_module_filespec (buff, bufflen, path, path2, modname, revision, mode, usebuff); assert(res==NO_ERR); switch (mode) { case NCXMOD_MODE_YANG: case NCXMOD_MODE_FILEYANG: isyang = TRUE; break; case NCXMOD_MODE_YIN: case NCXMOD_MODE_FILEYIN: isyang = FALSE; break; default: assert(0); } /* attempt to load this filespec as a YANG module */ res = yang_parse_from_filespec(buff, pcb, ptyp, isyang); switch (res) { case ERR_XML_READER_START_FAILED: case ERR_NCX_MISSING_FILE: /* not an error, *done == FALSE */ if (mode==NCXMOD_MODE_YANG || mode==NCXMOD_MODE_YIN) { res = NO_ERR; } break; default: /* NO_ERR or some other error */ *done = TRUE; } return res; } /* try_module */ /******************************************************************** * FUNCTION test_file * * Construct a filespec out of a path name and a file name * and try to find the filespec as an data or script file * * INPUTS: * buff == buffer to use for filespec construction * bufflen == length of buffer * path == first piece of path string * path2 == optional 2nd piece of path string * filename == complete filename with or without suffix * * RETURNS: * TRUE if file found okay * FALSE if file-not-found-error *********************************************************************/ static boolean test_file (xmlChar *buff, uint32 bufflen, const xmlChar *path, const xmlChar *path2, const xmlChar *filename) { xmlChar *p; uint32 flen, total; int ret; status_t res; struct stat statbuf; res = prep_dirpath(buff, bufflen, path, path2, &total); if (res != NO_ERR) { return FALSE; } flen = xml_strlen(filename); if (flen+total >= bufflen) { log_error("\nError: Filename too long error. Max: %d Got %u\n", NCXMOD_MAX_FSPEC_LEN, flen+total); return FALSE; } p = &buff[total]; p += xml_strcpy(p, filename); memset(&statbuf, 0x0, sizeof(statbuf)); ret = stat((const char *)buff, &statbuf); return (ret == 0 && S_ISREG(statbuf.st_mode)) ? TRUE : FALSE; } /* test_file */ /******************************************************************** * FUNCTION test_file_make * * Construct a filespec out of a path name and a file name * if the constructed directory is valid * * INPUTS: * buff == buffer to use for filespec construction * bufflen == length of buffer * path == first piece of path string * path2 == optional 2nd piece of path string * filename == complete filename with or without suffix * * RETURNS: * status: * NO_ERR if directory found OK and buffer constructed OK *********************************************************************/ static status_t test_file_make (xmlChar *buff, uint32 bufflen, const xmlChar *path, const xmlChar *path2, const xmlChar *filename) { uint32 flen, total; int ret; status_t res; struct stat statbuf; res = prep_dirpath(buff, bufflen, path, path2, &total); if (res != NO_ERR) { return res; } flen = xml_strlen(filename); if (flen+total >= bufflen) { log_error("\nError: Filename too long error. Max: %d Got %u\n", NCXMOD_MAX_FSPEC_LEN, flen+total); return ERR_BUFF_OVFL; } if (path) { ret = stat((const char *)buff, &statbuf); if (ret == 0 && S_ISDIR(statbuf.st_mode)) { xml_strcpy(&buff[total], filename); return NO_ERR; } else { return ERR_NCX_DEF_NOT_FOUND; } } else { xml_strcpy(buff, filename); return NO_ERR; } } /* test_file_make */ /******************************************************************** * FUNCTION test_pathlist * * Check the filespec path string for the specified module * and suffix. This function does not load any module * It just finds the specified file. * * Subdirs are not checked. The ENV vars that specify * directories to search needs to add an entry for each * dir to search * * INPUTS: * pathstr == pathstring list to check * buff == buffer to use for filespec construction * bufflen == length of buffer * modname == module name without file suffix * modsuffix == file suffix (no dot) [MAY BE NULL] * * OUTPUTS: * buff contains the complete path to the found file if * the return value is TRUE. Ignore buff contents otherwise * * RETURNS: * TRUE if file found okay * FALSE if file-not-found-error *********************************************************************/ static boolean test_pathlist (const xmlChar *pathlist, xmlChar *buff, uint32 bufflen, const xmlChar *modname, const xmlChar *modsuffix) { const xmlChar *str, *p; uint32 len, mlen, slen, dot; int ret; struct stat statbuf; mlen = xml_strlen(modname); slen = (modsuffix) ? xml_strlen(modsuffix) : 0; dot = (uint32)((slen) ? 1 : 0); /* go through the path list and check each string */ str = pathlist; while (*str) { /* find end of path entry or EOS */ p = str+1; while (*p && *p != ':') { p++; } len = (uint32)(p-str); if (len >= bufflen) { SET_ERROR(ERR_BUFF_OVFL); return FALSE; } /* copy the next string into buff */ xml_strncpy(buff, str, len); /* make sure string ends with path sep char */ if (buff[len-1] != NCXMOD_PSCHAR) { if (len+1 >= bufflen) { SET_ERROR(ERR_BUFF_OVFL); return FALSE; } else { buff[len++] = NCXMOD_PSCHAR; } } /* add the module name and suffix */ if (len+mlen+dot+slen >= bufflen) { SET_ERROR(ERR_BUFF_OVFL); return FALSE; } xml_strcpy(&buff[len], modname); if (modsuffix) { buff[len+mlen] = '.'; xml_strcpy(&buff[len+mlen+1], modsuffix); } /* check if the file exists and is readable */ ret = stat((const char *)buff, &statbuf); if (ret == 0) { /* match in buff */ if (S_ISREG(statbuf.st_mode)) { return TRUE; } else { /* should really be an error */ assert(0); return FALSE; } } /* setup the next path string to try */ if (*p) { str = p+1; /* one past ':' char */ } else { str = p; /* already at EOS */ } } return FALSE; } /* test_pathlist */ /******************************************************************** * FUNCTION test_pathlist_make * * Check the filespec path string to find the first * entry that actually exists. It does not check * if the specified user has permission to write * to this directory. That must be true or the * backup will fail * * INPUTS: * pathstr == pathstring list to check * buff == buffer to use for pathspec construction * bufflen == length of buffer * * OUTPUTS: * buff contains the complete path to the found directory if * the return value is TRUE. Ignore buff contents otherwise * * RETURNS: * TRUE if valid directory found * FALSE otherwise *********************************************************************/ static boolean test_pathlist_make (const xmlChar *pathlist, xmlChar *buff, uint32 bufflen) { const xmlChar *str, *p; uint32 len; int ret; struct stat statbuf; /* go through the path list and check each string */ str = pathlist; while (*str) { /* find end of path entry or EOS */ p = str+1; while (*p && *p != ':') { p++; } len = (uint32)(p-str); if (len >= bufflen) { SET_ERROR(ERR_BUFF_OVFL); return FALSE; } /* copy the next string into buff */ xml_strncpy(buff, str, len); /* check if this is a directory */ ret = stat((const char *)buff, &statbuf); if (ret == 0) { /* match in buff, make sure it is a directory */ if (S_ISDIR(statbuf.st_mode)) { /* make sure string ends with path sep char */ if (buff[len-1] != NCXMOD_PSCHAR) { if (len+1 >= bufflen) { SET_ERROR(ERR_BUFF_OVFL); return FALSE; } else { buff[len++] = NCXMOD_PSCHAR; buff[len] = 0; } } return TRUE; } } /* setup the next path string to try */ if (*p) { str = p+1; /* one past ':' char */ } else { str = p; /* already at EOS */ } } return FALSE; } /* test_pathlist_make */ /******************************************************************** * FUNCTION check_module_in_dir * * Check the directory specified in the buffer * for any possible for of a YANG/YIN module * * INPUTS: * buff == buffer to use for filespec construction * at the start it contains the path string to use; * new directory names will be added to this buffer * as the subdirs are searched, until the 'fname' file * is found or an error occurs * bufflen == size of buff in bytes * pathlen == current end of buffer in use marker * modname == module name * revision == module revision string * done == address of return search done flag * * OUTPUTS: * *done == TRUE if done processing * FALSE to keep going * RETURNS: * NO_ERR if file found okay, full filespec in the 'buff' variable * OR some error if not found or buffer overflow *********************************************************************/ static status_t check_module_in_dir (xmlChar *buff, uint32 bufflen, uint32 pathlen, const xmlChar *modname, const xmlChar *revision, boolean *done) { struct stat statbuf; uint32 buffleft; int ret; status_t res; /* init locals and return done flag */ *done = FALSE; res = NO_ERR; buffleft = bufflen - pathlen; /* try YANG first */ res = yang_copy_filename(modname, revision, &buff[pathlen], buffleft, TRUE); if (res != NO_ERR) { return res; } ret = stat((const char *)buff, &statbuf); if (ret == 0) { *done = TRUE; if (S_ISREG(statbuf.st_mode)) { return NO_ERR; } else { return ERR_FIL_BAD_FILENAME; } } /* make sure the path buffer is restored */ buff[pathlen] = 0; /* try YIN next */ res = yang_copy_filename(modname, revision, &buff[pathlen], buffleft, FALSE); if (res != NO_ERR) { return res; } ret = stat((const char *)buff, &statbuf); if (ret == 0) { *done = TRUE; if (S_ISREG(statbuf.st_mode)) { return NO_ERR; } else { return ERR_FIL_BAD_FILENAME; } } /* not done searching yet */ buff[pathlen] = 0; return NO_ERR; } /* check_module_in_dir */ /******************************************************************** * FUNCTION is_later_revision * * Check if modpath_a is a more recent revision then modpath_b * INPUTS: * modpath_a == modpath e.g. .../...@.... * modpath_b == modpath e.g. .../...@.... * * RETURNS: * TRUE if modpath_a has no revision in the name or is newer * FALSE if modpath_a is older *********************************************************************/ static boolean is_later_revision(xmlChar* modpath_a, xmlChar* modpath_b) { char* a; char* b; //printf("is a=%s later revision then b=%s\n", modpath_a, modpath_b); a=strchr(modpath_a,'@'); b=strchr(modpath_b,'@'); if(a==NULL) { /* modpath without version suffix is always latest */ return TRUE; } else if(b==NULL) { /* modpath without version suffix is always latest */ return FALSE; } else { if(0.yang * 4) modname@.yin * * if revision != NULL * 1) modname.yang * 2) modname.yin * 3) modname@revision.yang * 4) modname@revision.yin * * INPUTS: * buff == buffer to use for filespec construction * at the start it contains the path string to use; * new directory names will be added to this buffer * as the subdirs are searched, until the 'fname' file * is found or an error occurs * bufflen == size of buff in bytes * modname == module name * revision == module revision string * done == address of return search done flag * * OUTPUTS: * *done == TRUE if done processing * FALSE to keep going * RETURNS: * NO_ERR if file found okay, full filespec in the 'buff' variable * OR some error if not found or buffer overflow *********************************************************************/ static status_t search_subdirs (xmlChar *buff, uint32 bufflen, const xmlChar *modname, const xmlChar *revision, boolean *done) { DIR *dp; struct dirent *ep; uint32 pathlen, modnamelen, revisionlen, dentlen; boolean dirdone; status_t res; boolean done_subdir; xmlChar* best_match=NULL; /* init locals and return done flag */ *done = FALSE; pathlen = xml_strlen(buff); modnamelen = xml_strlen(modname); if (revision != NULL) { revisionlen = xml_strlen(revision); } else { revisionlen = 0; } res = NO_ERR; /* check minimal buffer space exists to even start */ if (pathlen + modnamelen + revisionlen + 2 >= bufflen) { *done = TRUE; return ERR_BUFF_OVFL; } /* make sure path ends with a pathsep char */ if (buff[pathlen-1] != NCXMOD_PSCHAR) { buff[pathlen++] = NCXMOD_PSCHAR; buff[pathlen] = 0; } res = check_module_in_dir(buff, bufflen, pathlen, modname, revision, done); if (*done || res != NO_ERR) { return res; } /* try to open the buffer spec as a directory */ dp = opendir((const char *)buff); if (!dp) { return NO_ERR; /* not done yet */ } dirdone = FALSE; while (!dirdone) { ep = readdir(dp); if (!ep) { dirdone = TRUE; continue; } /* this field may not be present on all POSIX systems * according to the glibc 2.7 documentation!! * only using dir file which works on linux. * !!!No support for symbolic links at this time!!! * * Always skip any directory or file that starts with * the dot-char or is named CVS */ dentlen = xml_strlen((const xmlChar *)ep->d_name); /* this dive-first behavior is not really what is desired * but do not have a 'stat' function for partial filenames * so just going through the directory block in order */ //ep->d_type=DT_UNKNOWN; /*simulate filesystem with no d_type*/ if (ep->d_type == DT_DIR || ep->d_type == DT_UNKNOWN) { if (*ep->d_name != '.' && strcmp(ep->d_name, "CVS")) { if ((pathlen + dentlen) >= bufflen) { res = ERR_BUFF_OVFL; *done = TRUE; dirdone = TRUE; } else { xml_strcpy(&buff[pathlen], (const xmlChar *)ep->d_name); done_subdir = FALSE; res = search_subdirs(buff, bufflen, modname, revision, &done_subdir); if (done_subdir) { *done = TRUE; dirdone = TRUE; } else { /* erase the directory name and keep trying */ res = NO_ERR; buff[pathlen] = 0; } } } } if (ep->d_type == DT_REG || ep->d_type == DT_UNKNOWN) { if (!xml_strncmp(modname, (const xmlChar *)ep->d_name, modnamelen)) { /* filename is a partial match so check it out * further to see if it is a pattern match; * check if length matches foo@YYYY-MM-DD.yang */ if ((dentlen == modnamelen + 16) || (dentlen == modnamelen + 15)) { /* check if the at-sign is * present in the filespec */ if (ep->d_name[modnamelen] != '@') { continue; } /* check if the revision matches, if specified */ if (revision != NULL) { if (xml_strncmp((const xmlChar *) &ep->d_name[modnamelen+1], revision, revisionlen)) { continue; } } /* check if the file extension is really *.yang or .yin * search the entire dir for the * highest valued date string * if the revision == NULL */ if (!xml_strcmp((const xmlChar *) &ep->d_name[modnamelen+12], YANG_SUFFIX) || !xml_strcmp((const xmlChar *) &ep->d_name[modnamelen+12], YIN_SUFFIX)) { *done = TRUE; if ((pathlen + dentlen) >= bufflen) { res = ERR_BUFF_OVFL; } else { res = NO_ERR; xml_strcpy(&buff[pathlen], (const xmlChar *)ep->d_name); } } } } } if(*done==TRUE) { if(revision != NULL) { /*strict revision no need to keep going on checking if there is newer revision*/ dirdone = TRUE; } else { if(best_match==NULL) { best_match=strdup(buff); } else { if(is_later_revision(buff,best_match)) { free(best_match); best_match=strdup(buff); } } *done=FALSE; } } } if(best_match) { *done=TRUE; xml_strcpy(buff,best_match); free(best_match); } (void)closedir(dp); return res; } /* search_subdirs */ /******************************************************************** * FUNCTION list_subdirs * * List the relevant files in the subdir * * INPUTS: * buff == buffer to use for filespec construction * at the start it contains the path string to use; * new directory names will be added to this buffer * as the subdirs are traversed * bufflen == size of buff in bytes * searchtype == enum for the search type to use * helpmode == help mode to use (BRIEF, NORMAL, FULL) * logstdout == TRUE for log_stdout, FALSE for log_write * dive == TRUE to list subdirs; FALSE to list just the * specified directory * * RETURNS: * NO_ERR if file found okay, full filespec in the 'buff' variable * OR some error if not found or buffer overflow *********************************************************************/ static status_t list_subdirs (xmlChar *buff, uint32 bufflen, search_type_t searchtype, help_mode_t helpmode, boolean logstdout, boolean dive) { DIR *dp; struct dirent *ep; xmlChar *str; uint32 pathlen, dentlen; boolean dirdone, isyang, isyin; status_t res; res = NO_ERR; dentlen = 0; pathlen = xml_strlen(buff); /* try to open the buffer spec as a directory */ dp = opendir((const char *)buff); if (!dp) { return NO_ERR; /* not done yet */ } /* list top-down: * first all the files first from this directory * then files from any subdirs */ dirdone = FALSE; while (!dirdone) { ep = readdir(dp); if (!ep) { dirdone = TRUE; continue; } if (ep->d_type == DT_REG || ep->d_type == DT_UNKNOWN) { /* skip files that start with a dot char */ if (*ep->d_name == '.') { continue; } isyang = is_yang_file((const xmlChar *)ep->d_name); isyin = is_yin_file((const xmlChar *)ep->d_name); /* check if this file needs to be listed */ switch (searchtype) { case SEARCH_TYPE_MODULE: if (isyang || isyin) { break; } continue; case SEARCH_TYPE_DATA: case SEARCH_TYPE_SCRIPT: if (isyang || isyin) { continue; } break; case SEARCH_TYPE_NONE: default: dirdone = TRUE; SET_ERROR(ERR_INTERNAL_VAL); continue; } /* list the file */ if (logstdout) { log_stdout_indent(NCX_DEF_INDENT); if (helpmode == HELP_MODE_FULL) { log_stdout((const char *)buff); } log_stdout(ep->d_name); if (helpmode != HELP_MODE_BRIEF) { ; } } else { log_indent(NCX_DEF_INDENT); if (helpmode == HELP_MODE_FULL) { log_write((const char *)buff); } log_write(ep->d_name); if (helpmode != HELP_MODE_BRIEF) { ; } } } } (void)closedir(dp); if (!dive) { return NO_ERR; } /* try to open the buffer spec as a directory */ dp = opendir((const char *)buff); if (!dp) { return NO_ERR; } /* go through again but this time just dive into the subdirs */ dirdone = FALSE; while (!dirdone) { ep = readdir(dp); if (!ep) { dirdone = TRUE; continue; } /* this field may not be present on all POSIX systems * according to the glibc 2.7 documentation!! * only using dir file which works on linux. * !!!No support for symbolic links at this time!!! * If d_type == DT_UNKNOWN then guessing this is a file * * Always skip any directory or file that starts with * the dot-char or is named CVS */ dentlen = xml_strlen((const xmlChar *)ep->d_name); /* this dive-first behavior is not really what is desired * but do not have a 'stat' function for partial filenames * so just going through the directory block in order */ if (ep->d_type == DT_DIR || ep->d_type == DT_UNKNOWN) { if (*ep->d_name != '.' && strcmp(ep->d_name, "CVS")) { if ((pathlen + dentlen + 2) >= bufflen) { res = ERR_BUFF_OVFL; dirdone = TRUE; } else { /* make sure last dirspec ends witha path-sep-char */ if (pathlen > 0 && buff[pathlen-1] != NCXMOD_PSCHAR) { buff[pathlen] = NCXMOD_PSCHAR; str = &buff[pathlen+1]; str += xml_strcpy(str, (const xmlChar *)ep->d_name); } else { str = &buff[pathlen]; str += xml_strcpy(str, (const xmlChar *)ep->d_name); } *str++ = NCXMOD_PSCHAR; *str = '\0'; res = list_subdirs(buff, bufflen, searchtype, helpmode, logstdout, TRUE); /* erase the filename and keep trying */ buff[pathlen] = 0; if (res != NO_ERR) { dirdone = TRUE; continue; } } } } } (void)closedir(dp); return res; } /* list_subdirs */ /******************************************************************** * FUNCTION list_pathlist * * Check the filespec path string and list files in each dir * * INPUTS: * pathstr == pathstring list to check * buff == scratch buffer to use * bufflen == length of buff * searchtype == search type to use * helpmode == verbosity mode (BRIEF, NORMAL, FULL) * logstdout == TRUE for log_stdout, FALSE for log_write * * RETURNS: * status *********************************************************************/ static status_t list_pathlist (const xmlChar *pathlist, xmlChar *buff, uint32 bufflen, search_type_t searchtype, help_mode_t helpmode, boolean logstdout) { const xmlChar *str, *p; uint32 len; status_t res; res = NO_ERR; str = pathlist; /* go through the path list and check each string */ while (*str) { /* find end of path entry or EOS */ p = str+1; while (*p && *p != ':') { p++; } len = (uint32)(p-str); if (len >= bufflen) { return ERR_BUFF_OVFL; } /* copy the next string into buff */ xml_strncpy(buff, str, len); /* make sure string ends with path sep char */ if (buff[len-1] != NCXMOD_PSCHAR) { if (len+1 >= bufflen) { return ERR_BUFF_OVFL; } else { buff[len++] = NCXMOD_PSCHAR; } } /* list all the requested files in this path */ res = list_subdirs(buff, bufflen, searchtype, helpmode, logstdout, FALSE); if (res != NO_ERR) { return res; } /* setup the next path string to try */ if (*p) { str = p+1; /* one past ':' char */ } else { str = p; /* already at EOS */ } } return res; } /* list_pathlist */ /******************************************************************** * FUNCTION add_failed * * Add a yang_node_t entry to the pcb->failedQ * * INPUTS: * modname == failed module name * revision == failed revision date (may be NULL) * pcb == parser control block * res == final result status for the failed module * * RETURNS: * status *********************************************************************/ static status_t add_failed (const xmlChar *modname, const xmlChar *revision, yang_pcb_t *pcb, status_t res) { yang_node_t *node; /* do not save failed modules based on a failed search * or if the module is not really being added to the registry */ if (pcb->searchmode || pcb->parsemode) { return NO_ERR; } node = yang_new_node(); if (!node) { return ERR_INTERNAL_MEM; } node->failed = xml_strdup(modname); if (!node->failed) { yang_free_node(node); return ERR_INTERNAL_MEM; } if (revision) { node->failedrev = xml_strdup(revision); if (!node->failed) { yang_free_node(node); return ERR_INTERNAL_MEM; } } node->name = node->failed; node->revision = node->failedrev; node->res = res; dlq_enque(node, &pcb->failedQ); return NO_ERR; } /* add_failed */ /******************************************************************** * FUNCTION check_module_path * * Check the specified path for a YANG module file * * INPUTS: * path == starting path to check * buff == buffer to use for the filespec * bufflen == size of 'buff' in bytes * modname == module name to find (no file suffix) * revision == module revision date (may be NULL) * usepath == TRUE if path should be used directly * FALSE if the path should be appended with the 'modules' dir * done == address of return done flag * * OUTPUTS: * *done == TRUE if file found or fatal error * file completely processed in try_module if file found * * RETURNS: * status *********************************************************************/ static status_t check_module_path (const xmlChar *path, xmlChar *buff, uint32 bufflen, const xmlChar *modname, const xmlChar *revision, boolean usepath, boolean *done) { const xmlChar *path2; uint32 total; status_t res, res2; *done = FALSE; res = NO_ERR; res2 = NO_ERR; /* T == use path directly, leave path2 NULL * F == add 'modules' to end of path by setting path2 */ path2 = (usepath) ? NULL : NCXMOD_DIR; total = xml_strlen(path); if (total >= bufflen) { *done = TRUE; return ERR_BUFF_OVFL; } if (ncxmod_subdirs) { res = prep_dirpath(buff, bufflen, path, path2, &total); if (res != NO_ERR) { *done = TRUE; return res; } /* try YANG or YIN file */ res = search_subdirs(buff, bufflen, modname, revision, done); if (*done ) { } return (res != NO_ERR) ? res : res2; } /* else subdir searches not allowed * check for YANG file in the current path */ res = make_module_filespec(buff, bufflen, path, path2, modname, revision, NCXMOD_MODE_YANG, FALSE); if(ncxmod_test_filespec(buff)) { *done=TRUE; } if (!*done && res == NO_ERR) { /* check for YIN file in the current path */ res = make_module_filespec(buff, bufflen, path, path2, modname, revision, NCXMOD_MODE_YIN, FALSE); if(ncxmod_test_filespec(buff)) { *done=TRUE; } } return (res != NO_ERR) ? res : res2; } /* check_module_path */ /******************************************************************** * FUNCTION check_module_pathlist * * Check a list of pathnames for the specified path of * a YANG module file * * Example: path1:path2:path3 * * INPUTS: * pathlist == formatted string containing list of path strings * buff == buffer to use for the filespec * bufflen == size of 'buff' in bytes * modname == module name to find (no file suffix) * revision == module revision date (may be NULL) * done == address of return done flag * * OUTPUTS: * buff == buffer containing the filespec * *done == TRUE if file found or fatal error * file completely processed in try_module if file found * * RETURNS: * status *********************************************************************/ static status_t check_module_pathlist (const xmlChar *pathlist, xmlChar *buff, uint32 bufflen, const xmlChar *modname, const xmlChar *revision, boolean *done) { const xmlChar *str, *p; xmlChar *pathbuff; uint32 pathbufflen, pathlen; status_t res; pathbufflen = NCXMOD_MAX_FSPEC_LEN+1; pathbuff = m__getMem(pathbufflen); if (!pathbuff) { *done = TRUE; return ERR_INTERNAL_MEM; } /* go through the path list and check each string */ str = pathlist; while (*str) { /* find end of path entry or EOS */ p = str+1; while (*p && *p != ':') { p++; } pathlen = (uint32)(p-str); if (pathlen >= pathbufflen) { *done = TRUE; m__free(pathbuff); return ERR_BUFF_OVFL; } /* copy the next string into the path buffer */ xml_strncpy(pathbuff, str, pathlen); res = check_module_path(pathbuff, buff, bufflen, modname, revision, TRUE, done); if (*done) { m__free(pathbuff); return res; } /* setup the next path string to try */ if (*p) { str = p+1; /* one past ':' char */ } else { str = p; /* already at EOS */ } } m__free(pathbuff); *done = FALSE; return NO_ERR; } /* check_module_pathlist */ /******************************************************************** * FUNCTION search_module_path * * Check the specified path for all YANG files * * INPUTS: * path == starting path to check * buff == buffer to use for the filespec * bufflen == size of 'buff' in bytes * callback == callback function to use for process_subdir * cookie == parameter for the callback * * * RETURNS: * status *********************************************************************/ static status_t search_module_path (const xmlChar *path, xmlChar *buff, uint32 bufflen, ncxmod_callback_fn_t callback, void *cookie) { status_t res; uint32 total; total = 0; res = prep_dirpath(buff, bufflen, path, NCXMOD_DIR, &total); if (res == NO_ERR) { res = ncxmod_process_subtree((const char *)buff, callback, cookie); } return res; } /* search_module_path */ /******************************************************************** * FUNCTION search_module_pathlist * * Check a list of pathnames for all the YANG files in the path * * Example: path1:path2/foo/bar:path3 * * INPUTS: * pathlist == formatted string containing list of path strings * callback == callback function to use for process_subdir * cookie == parameter for the callback * * RETURNS: * status *********************************************************************/ static status_t search_module_pathlist (const xmlChar *pathlist, ncxmod_callback_fn_t callback, void *cookie) { const xmlChar *str, *p; xmlChar *pathbuff; uint32 pathbufflen, pathlen; status_t res; pathbufflen = NCXMOD_MAX_FSPEC_LEN+1; pathbuff = m__getMem(pathbufflen); if (!pathbuff) { return ERR_INTERNAL_MEM; } /* go through the path list and check each string */ str = pathlist; res = NO_ERR; while (*str) { /* find end of path entry or EOS */ p = str+1; while (*p && *p != ':') { p++; } pathlen = (uint32)(p-str); if (pathlen >= pathbufflen) { m__free(pathbuff); return ERR_BUFF_OVFL; } /* copy the next string into the path buffer */ xml_strncpy(pathbuff, str, pathlen); res = ncxmod_process_subtree((const char *)pathbuff, callback, cookie); if (res != NO_ERR) { m__free(pathbuff); return res; } /* setup the next path string to try */ if (*p) { str = p+1; /* one past ':' char */ } else { str = p; /* already at EOS */ } } m__free(pathbuff); return NO_ERR; } /* search_module_pathlist */ /******************************************************************** * FUNCTION is_module_file * * Determine if the module is a file * * INPUTS: * modname == module name with no path prefix or file extension * * OUTPUTS: * RETURNS: * True if the module is a file * *********************************************************************/ static boolean is_module_file( const xmlChar *modname ) { boolean isfile = FALSE; /* check which form of input is present, module name or filespec */ if ((*modname == '.') || (*modname == NCXMOD_PSCHAR)) { return TRUE; } else { /* if a dir sep char is in the string it is automatically treated as an * absolute filespec, even if it doesn't have a file suffix */ const xmlChar *str = modname; while (*str && *str != NCXMOD_PSCHAR) { ++str; } if (*str) { isfile = TRUE; } } return isfile; } /******************************************************************** * FUNCTION determine_mode * * Determine if the module is a YANG or YIN file extension * * INPUTS: * modname == module name with no path prefix or file extension * modlen == length of modname string * * OUTPUTS: * * RETURNS: * True if teh module is a file * *********************************************************************/ static ncxmod_mode_t determine_mode( const xmlChar* modname, const uint32 modlen ) { ncxmod_mode_t mode = NCXMOD_MODE_NONE; const xmlChar *str = modname; // find the start of the file suffix, if any str = &modname[modlen]; while (str > modname && *str != '.') { --str; } /* try to find a .yang file suffix */ if (*str == '.') { /* since the dot-char is allowed in YANG identifier names * only treat this string with a dot in it as a file if * it has a YANG file extension */ if (!xml_strcmp(str+1, YANG_SUFFIX)) { mode = NCXMOD_MODE_FILEYANG; } else if (!xml_strcmp(str+1, YIN_SUFFIX)) { mode = NCXMOD_MODE_FILEYIN; } } if ( NCXMOD_MODE_NONE == mode ) { if ( is_module_file ( modname ) ) { mode = NCXMOD_MODE_FILEYIN; } } return mode; } /******************************************************************** * FUNCTION try_module_filespec * * try to load a module from a file * * INPUTS: * mode the file mode (YANG or YIN) * * OUTPUTS: * RETURNS: * True if teh module is a file * *********************************************************************/ static status_t try_module_filespec( const ncxmod_mode_t mode, const xmlChar *modname, const uint32 modlen, yang_pcb_t *pcb, yang_parsetype_t ptyp, ncx_module_t **retmod) { status_t res = ERR_NCX_MISSING_FILE; xmlChar *buff; boolean done = FALSE; /* the try_module function expects the first parm to be a writable * buffer, even though in this case there will be nothing altered in the * module name string. Need to copy it instead of pass it directly :-( */ buff = xml_strdup(modname); if ( !buff ) { return ERR_INTERNAL_MEM; } res = try_module( buff, modlen, NULL, NULL, NULL, NULL, mode, TRUE, &done, pcb, ptyp); if ( ERR_NCX_MISSING_FILE == res ) { log_error("\nError: file not found (%s)\n", modname); } else if ( res == NO_ERR && retmod ) { *retmod = pcb->top; } m__free(buff); return res; } /******************************************************************** * FUNCTION load_module * * Determine the location of the specified module * and then load it into the system, if not already loaded * * Module Search order: * 1) filespec == try that only and exit * 2) current directory * 3) YUMA_MODPATH environment var (or set by modpath CLI var) * 4) HOME/modules directory * 5) YUMA_HOME/modules directory * 6) YUMA_INSTALL/modules directory OR * 7) default install module location, which is '/usr/share/yuma/modules' * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * pcb == parser control block * ptyp == current parser source type * retmod == address of return module (may be NULL) * * OUTPUTS: * if non-NULL: * *retmod == pointer to requested module version * * RETURNS: * status *********************************************************************/ static status_t load_module (const xmlChar *modname, const xmlChar *revision, yang_pcb_t *pcb, yang_parsetype_t ptyp, ncx_module_t **retmod) { ncxmod_mode_t mode = NCXMOD_MODE_NONE; uint32 modlen; status_t res = NO_ERR; boolean done = FALSE; xmlChar* file_path; unsigned int file_path_len; if ( !modname || !pcb ) { return ERR_INTERNAL_PTR; } if (LOGDEBUG2) { log_debug2("\nAttempting to load module '%s'", modname); if (revision) { log_debug2(" r:%s", revision); } } else if (LOGDEBUG) { log_debug("\nload_module called for '%s'", modname); } modlen = xml_strlen(modname); if (retmod) { *retmod = NULL; } /* find the start of the file name extension, expecting .yang or .yin */ mode = determine_mode( modname, modlen ); if ( NCXMOD_MODE_FILEYANG == mode || NCXMOD_MODE_FILEYIN == mode ) { /* 1) if parameter is a filespec, then try it and exit if it does not * work, instead of trying other directories */ return try_module_filespec( mode, modname, modlen, pcb, ptyp, retmod ); } /* the module name is not a file; the revision may be relevant now; * make sure the module name is valid */ if ( !ncx_valid_name( modname, modlen ) ) { log_error("\nError: Invalid module name (%s)\n", modname); res = add_failed(modname, revision, pcb, res); if ( NO_ERR != res ) { return res; } else { return ERR_NCX_INVALID_NAME; } } /* check if the module is already loaded (in the current ncx_modQ) skip if * this is yangdump converting a YANG file to a YIN file or * yangcli_autoload parsing a server module */ if ((ptyp != YANG_PT_INCLUDE) && !(pcb->parsemode && ptyp == YANG_PT_TOP) && !(pcb->savetkc && pcb->tkc == NULL)) { ncx_module_t *testmod; testmod = ncx_find_module(modname, revision); if (testmod) { log_debug2( "\nncxmod: Using module '%s' already loaded", modname ); if (!pcb->top) { pcb->top = testmod; pcb->topfound = TRUE; } if (retmod) { *retmod = testmod; } return testmod->status; } else { /*** hack for now, check if this is ietf-netconf ***/ if (!xml_strcmp(modname, NCXMOD_IETF_NETCONF)) { /* check if yuma-netconf already loaded */ testmod = ncx_find_module(NCXMOD_YUMA_NETCONF, NULL); if (testmod) { /* use yuma-netconf instead of ietf-netconf */ log_debug( "\nncxmod: cannot load 'ietf-netconf'; " "'yuma-netconf' already loaded" ); if (!pcb->top) { pcb->top = testmod; pcb->topfound = TRUE; } if (retmod) { *retmod = testmod; } return testmod->status; } } } } file_path=ncxmod123_find_module_filespec(modname, revision); if(file_path==NULL) { return ERR_NCX_MOD_NOT_FOUND; } file_path_len = xml_strlen(file_path); /* find the start of the file name extension, expecting .yang or .yin */ mode = determine_mode( file_path, file_path_len ); if ( NCXMOD_MODE_FILEYANG == mode || NCXMOD_MODE_FILEYIN == mode ) { res = try_module_filespec( mode, file_path, file_path_len, pcb, ptyp, retmod ); if (res != NO_ERR) { ( void ) add_failed(modname, revision, pcb, res); } if ( ( res == NO_ERR || ptyp == YANG_PT_INCLUDE ) && retmod ) { if (pcb->retmod != NULL) { *retmod = pcb->retmod; } else if (ptyp == YANG_PT_TOP) { *retmod = pcb->top; } else { SET_ERROR(ERR_INTERNAL_VAL); } } else if (res != NO_ERR && pcb->retmod) { log_debug( "\nFree retmod import %p (%s)", pcb->retmod, pcb->retmod->name); ncx_free_module(pcb->retmod); pcb->retmod = NULL; } /* else pcb->top will get deleted in yang_free_pcb */ } else { assert(0); } free(file_path); return res; } /* load_module */ /******************************************************************** * FUNCTION ncxmod123_find_module_filespec * * Determine the location of the specified module * * Module Search order: * 1) current directory * 2) YUMA_MODPATH environment var (or set by modpath CLI var) * 3) HOME/modules directory * 4) YUMA_HOME/modules directory * 5) YUMA_INSTALL/modules directory OR * 6) default install module location, which is '/usr/share/yuma/modules' * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find ** * RETURNS: * NULL if no match was found or pointer to allocated string * containing the path * *********************************************************************/ xmlChar* ncxmod123_find_module_filespec(const xmlChar *modname, const xmlChar *revision) { xmlChar *buff; uint32 bufflen = 0; boolean done = FALSE; status_t res; /* get a temp buffer to construct filespecs */ bufflen = NCXMOD_MAX_FSPEC_LEN+1; buff = malloc(bufflen); assert(buff); *buff = 0; /* 1) try alt_path variable if set; used by yangdiff */ if ( ncxmod_alt_path) { res = check_module_path( ncxmod_alt_path, buff, bufflen, modname, revision, TRUE, &done ); } if (ncx_get_cwd_subdirs()) { /* CHECK THE CURRENT DIR AND ANY SUBDIRS * 3) try cur working directory and subdirs if the subdirs parameter * is true * check before the modpath, which can cause the wrong version to be * picked, depending * on the CWD used by the application. */ if (!done) { res = check_module_pathlist( (const xmlChar *)".", buff, bufflen, modname, revision, &done ); } } else { /* CHECK THE CURRENT DIR BUT NOT ANY SUBDIRS * 2a) try as module in current dir, YANG format */ if (!done) { res = make_module_filespec( buff, bufflen, NULL, NULL, modname, revision, NCXMOD_MODE_YANG, FALSE); if(ncxmod_test_filespec(buff)) { done=TRUE; } } /* 2b) try as module in current dir, YIN format */ if (!done) { res = make_module_filespec( buff, bufflen, NULL, NULL, modname, revision, NCXMOD_MODE_YIN, FALSE); if(ncxmod_test_filespec(buff)) { done=TRUE; } } } /* 3) try YUMA_MODPATH environment variable if set */ if (!done && ncxmod_mod_path) { res = check_module_pathlist( ncxmod_mod_path, buff, bufflen, modname, revision, &done ); } /* 4) HOME/modules directory */ if (!done && ncxmod_home) { res = check_module_path( ncxmod_home, buff, bufflen, modname, revision, FALSE, &done ); } /* 5) YUMA_HOME/modules directory */ if (!done && ncxmod_yuma_home) { res = check_module_path( ncxmod_yuma_home, buff, bufflen, modname, revision, FALSE, &done ); } /* 6) YUMA_INSTALL/modules directory or default install path * If this envvar is set then the default install path will not * be tried */ if (!done) { if (ncxmod_env_install) { res = check_module_path( ncxmod_env_install, buff, bufflen, modname, revision, FALSE, &done ); } else { res = check_module_path( NCXMOD_DEFAULT_INSTALL, buff, bufflen, modname, revision, FALSE, &done ); } } if(done) { return buff; } else { free(buff); return NULL; } } /* ncxmod123_find_module_filespec */ /******************************************************************** * FUNCTION has_mod_ext * * Check if the filespec ends in '.yang' or '.yin' * * INPUTS: * filespec == file spec string to check * * RETURNS: * TRUE if YANG file extension found * and non-zero filename\ * FALSE otherwise *********************************************************************/ static boolean has_mod_ext (const char *filespec) { const char *p; p = filespec; while (*p) { p++; } while (p>filespec && (*p != '.')) { p--; } if (p==filespec) { return FALSE; } if (!strcmp(p+1, (const char *)YANG_SUFFIX)) { return TRUE; } if (!strcmp(p+1, (const char *)YIN_SUFFIX)) { return TRUE; } return FALSE; } /* has_mod_ext */ /******************************************************************** * FUNCTION process_subtree * * Search the entire specified subtree, looking for YANG * modules. Invoke the callback function for each module * file found * * INPUTS: * buff == working filespec buffer containing the dir spec * to start with * bufflen == maxsize of buff, in bytes * callback == address of the ncxmod_callback_fn_t function * to use for this traveral * cookie == cookie to pass to each invocation of the callback * * RETURNS: * NO_ERR if file found okay, full filespec in the 'buff' variable * OR some error if not found or buffer overflow *********************************************************************/ static status_t process_subtree (char *buff, uint32 bufflen, ncxmod_callback_fn_t callback, void *cookie) { DIR *dp; struct dirent *ep; uint32 pathlen; boolean dirdone; status_t res; res = NO_ERR; pathlen = xml_strlen((const xmlChar *)buff); if (!pathlen) { return NO_ERR; } /* make sure a min-length YANG file can be added (x.yang) */ if ((pathlen + 8) >= bufflen) { log_error("\nError: pathspec too long '%s'\n", buff); return ERR_BUFF_OVFL; } /* make sure dir-sep char is in place */ if (buff[pathlen-1] != NCXMOD_PSCHAR) { buff[pathlen++] = NCXMOD_PSCHAR; buff[pathlen] = 0; } /* try to open the buffer spec as a directory */ dp = opendir(buff); if (!dp) { #if 0 log_error("\nError: open directory '%s' failed\n", buff); return ERR_OPEN_DIR_FAILED; #else return NO_ERR; #endif } dirdone = FALSE; while (!dirdone && res==NO_ERR) { ep = readdir(dp); if (!ep) { dirdone = TRUE; continue; } /* this field may not be present on all POSIX systems * according to the glibc 2.7 documentation!! * only using dir file which works on linux. * !!!No support for symbolic links at this time!!! */ if (ep->d_type == DT_DIR || ep->d_type == DT_UNKNOWN) { if ((*ep->d_name != '.') && strcmp(ep->d_name, "CVS")) { if ((pathlen + xml_strlen((const xmlChar *)ep->d_name)) >= bufflen) { res = ERR_BUFF_OVFL; } else { strncpy(&buff[pathlen], ep->d_name, bufflen-pathlen); res = process_subtree(buff, bufflen, callback, cookie); buff[pathlen] = 0; } } } if (ep->d_type == DT_REG || ep->d_type == DT_UNKNOWN) { if ((*ep->d_name != '.') && has_mod_ext(ep->d_name)) { if ((pathlen + xml_strlen((const xmlChar *)ep->d_name)) >= bufflen) { res = ERR_BUFF_OVFL; } else { strncpy(&buff[pathlen], ep->d_name, bufflen-pathlen); res = (*callback)(buff, cookie); } } } } (void)closedir(dp); return res; } /* process_subtree */ /******************************************************************** * FUNCTION try_load_module * * Try 1 to 3 forms of the YANG filespec to * locate the specified module * * INPUTS: * pcb == parser control block to use * ptyp == parser mode type to use * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * retmod == address of return module (may be NULL) * * OUTPUTS: * if non-NULL: * *retmod == pointer to requested module version * * RETURNS: * status *********************************************************************/ static status_t try_load_module (yang_pcb_t *pcb, yang_parsetype_t ptyp, const xmlChar *modname, const xmlChar *revision, ncx_module_t **retmod) { ncx_module_t *testmod; status_t res; #ifdef DEBUG if (!modname) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif testmod = NULL; /* need to choose the correct match mode from the start * so that foo@rev-date.yang early in the search path * does not get passed over for a generic foo.yang * later on in the search path */ res = load_module(modname, revision, pcb, ptyp, &testmod); if (res == ERR_NCX_MOD_NOT_FOUND) { if (revision && *revision) { /* try filenames without revision dates in them */ res = load_module(modname, NULL, pcb, ptyp, &testmod); if (res == NO_ERR && testmod) { if (testmod->version == NULL) { /* asked for a specific revision; * got a generic revision instead * rejected!; return error */ res = ERR_NCX_WRONG_VERSION; } else if (yang_compare_revision_dates(revision, testmod->version)) { /* error should already be reported */ res = ERR_NCX_WRONG_VERSION; } /* else got correct revision */ } } } if (res == NO_ERR && testmod && testmod->errors) { log_debug("\nParser returned OK but module '%s' has errors", testmod->name); res = ERR_NCX_OPERATION_FAILED; if (!testmod->ismod && (pcb->top != testmod)) { ncx_free_module(testmod); } } else if (retmod) { *retmod = testmod; } return res; } /* try_load_module */ /******************************************************************** * FUNCTION new_temp_filcb * * Malloc a new temporary file control block * * RETURNS: * malloced and initialized struct *********************************************************************/ static ncxmod_temp_filcb_t * new_temp_filcb (void) { ncxmod_temp_filcb_t *newcb; newcb = m__getObj(ncxmod_temp_filcb_t); if (newcb == NULL) { return NULL; } memset(newcb, 0x0, sizeof(ncxmod_temp_filcb_t)); return newcb; } /* new_temp_filcb */ /******************************************************************** * FUNCTION free_temp_filcb * * Clean and free a temporary file control block * * INPUTS: * filcb == temporary file control block to free * *********************************************************************/ static void free_temp_filcb (ncxmod_temp_filcb_t *filcb) { int retval; if (filcb->source) { retval = remove((const char *)filcb->source); if (retval < 0) { log_error("\nError: could not delete temp file '%s' (%s)\n", filcb->source, get_error_string(errno_to_status())); } m__free(filcb->source); } m__free(filcb); } /* free_temp_filcb */ /******************************************************************** * FUNCTION new_temp_sescb * * Malloc a new temporary session files control block * * RETURNS: * malloced and initialized struct *********************************************************************/ static ncxmod_temp_sescb_t * new_temp_sescb (void) { ncxmod_temp_sescb_t *newcb; newcb = m__getObj(ncxmod_temp_sescb_t); if (newcb == NULL) { return NULL; } memset(newcb, 0x0, sizeof(ncxmod_temp_sescb_t)); dlq_createSQue(&newcb->temp_filcbQ); return newcb; } /* new_temp_sescb */ /******************************************************************** * FUNCTION free_temp_sescb * * Clean and free a new temporary session files control block * * INPUTS: * sescb == temp files session control block to free * *********************************************************************/ static void free_temp_sescb (ncxmod_temp_sescb_t *sescb) { ncxmod_temp_filcb_t *filcb; int retval; while (!dlq_empty(&sescb->temp_filcbQ)) { filcb = (ncxmod_temp_filcb_t *) dlq_deque(&sescb->temp_filcbQ); free_temp_filcb(filcb); } if (sescb->source) { retval = rmdir((const char *)sescb->source); if (retval < 0) { log_error("\nError: could not delete temp directory '%s' (%s)\n", sescb->source, get_error_string(errno_to_status())); } m__free(sescb->source); } m__free(sescb); } /* free_temp_sescb */ /******************************************************************** * FUNCTION new_temp_progcb * * Malloc a new temporary program instance control block * * RETURNS: * malloced and initialized struct *********************************************************************/ static ncxmod_temp_progcb_t * new_temp_progcb (void) { ncxmod_temp_progcb_t *newcb; newcb = m__getObj(ncxmod_temp_progcb_t); if (newcb == NULL) { return NULL; } memset(newcb, 0x0, sizeof(ncxmod_temp_progcb_t)); dlq_createSQue(&newcb->temp_sescbQ); return newcb; } /* new_temp_progcb */ /******************************************************************** * FUNCTION free_temp_progcb * * Clean and free a new temporary program instance control block * * INPUTS: * progcb == temp program instance control block to free * *********************************************************************/ static void free_temp_progcb (ncxmod_temp_progcb_t *progcb) { ncxmod_temp_sescb_t *sescb; int retval; while (!dlq_empty(&progcb->temp_sescbQ)) { sescb = (ncxmod_temp_sescb_t *) dlq_deque(&progcb->temp_sescbQ); free_temp_sescb(sescb); } if (progcb->source) { retval = rmdir((const char *)progcb->source); if (retval < 0) { log_error("\nError: could not delete temp directory '%s' (%s)\n", progcb->source, get_error_string(errno_to_status())); } m__free(progcb->source); } m__free(progcb); } /* free_temp_progcb */ /******************************************************************** * FUNCTION make_search_result * * Make a ncxmod_search_result_t struct for the given module * * INPUTS: * mod == module to use * * RETURNS: * == malloced and filled in search result struct * MUST call ncxmod_free_search_result() if the * return value is non-NULL * CHECK result->res TO SEE IF MODULE WAS FOUND * A SEARCH RESULT WILL BE RETURNED IF A SEARCH WAS * ATTEMPTED, EVEN IF NOTHING FOUND * == NULL if any error preventing a search *********************************************************************/ static ncxmod_search_result_t * make_search_result (ncx_module_t *mod) { ncxmod_search_result_t *searchresult; if (LOGDEBUG2) { log_debug2("\nFound %smodule" "\n name: '%s'" "\n revision: '%s':" "\n namespace: '%s'" "\n source: '%s'", (mod->ismod) ? "" : "sub", (mod->name) ? mod->name : EMPTY_STRING, (mod->version) ? mod->version : EMPTY_STRING, (mod->ns) ? mod->ns : EMPTY_STRING, (mod->source) ? mod->source : EMPTY_STRING); } searchresult = ncxmod_new_search_result_ex(mod); if (searchresult == NULL) { return NULL; } searchresult->res = NO_ERR; return searchresult; } /* make_search_result */ /******************************************************************** * FUNCTION search_subtree_callback * * Handle the current filename in the subtree traversal * Quick parse and generate a search result for the module * * INPUTS: * fullspec == absolute or relative path spec, with filename and ext. * this regular file exists, but has not been checked for * read access of * cookie == resultQ to store malloced search result * * RETURNS: * status * * Return fatal error to stop the traversal or NO_ERR to * keep the traversal going. Do not return any warning or * recoverable error, just log and move on *********************************************************************/ static status_t search_subtree_callback (const char *fullspec, void *cookie) { dlq_hdr_t *resultQ; ncx_module_t *retmod; yang_pcb_t *pcb; ncxmod_search_result_t *searchresult; status_t res; resultQ = (dlq_hdr_t *)cookie; if (resultQ == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } pcb = yang_new_pcb(); if (pcb == NULL) { return ERR_INTERNAL_MEM; } pcb->searchmode = TRUE; retmod = NULL; searchresult = NULL; res = try_load_module(pcb, YANG_PT_TOP, (const xmlChar *)fullspec, NULL, &retmod); if (res != NO_ERR || retmod == NULL) { if (LOGDEBUG2) { log_debug2("\nFind module '%s' failed (%s)", fullspec, get_error_string(res)); } } else { searchresult = make_search_result(retmod); if (searchresult != NULL) { /* hand off malloced memory here */ dlq_enque(searchresult, resultQ); } else { res = ERR_INTERNAL_MEM; } } if (pcb) { yang_free_pcb(pcb); } return res; } /* search_subtree_callback */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION ncxmod_init * * Initialize the ncxmod module * * RETURNS: * status *********************************************************************/ status_t ncxmod_init (void) { status_t res = NO_ERR; #ifdef DEBUG if (ncxmod_init_done) { return SET_ERROR(ERR_INTERNAL_INIT_SEQ); } #endif /* try to get the YUMA_HOME environment variable */ ncxmod_yuma_home = (const xmlChar *)getenv(NCXMOD_HOME); ncxmod_yuma_home_cli = NULL; /* try to get the YUMA_INSTALL environment variable */ ncxmod_env_install = (const xmlChar *)getenv(NCXMOD_INSTALL); /* try to get the user HOME environment variable */ ncxmod_home = (const xmlChar *)getenv(USER_HOME); ncxmod_home_cli = NULL; /* try to get the module search path variable */ ncxmod_mod_path = (const xmlChar *)getenv(NCXMOD_MODPATH); if (ncxmod_home != NULL) { ncxmod_yumadir_path = ncx_get_source(NCXMOD_YUMA_DIR, &res); } else { ncxmod_yumadir_path = xml_strdup(NCXMOD_TEMP_YUMA_DIR); if (ncxmod_yumadir_path == NULL) { res = ERR_INTERNAL_MEM; } } ncxmod_mod_path_cli = NULL; ncxmod_alt_path = NULL; /* try to get the data search path variable */ ncxmod_data_path = (const xmlChar *)getenv(NCXMOD_DATAPATH); ncxmod_data_path_cli = NULL; /* try to get the script search path variable */ ncxmod_run_path = (const xmlChar *)getenv(NCXMOD_RUNPATH); ncxmod_run_path_cli = NULL; ncxmod_subdirs = TRUE; ncxmod_init_done = TRUE; return res; } /* ncxmod_init */ /******************************************************************** * FUNCTION ncxmod_cleanup * * Cleanup the ncxmod module * *********************************************************************/ void ncxmod_cleanup (void) { #ifdef DEBUG if (!ncxmod_init_done) { SET_ERROR(ERR_INTERNAL_INIT_SEQ); return; } #endif ncxmod_yuma_home = NULL; ncxmod_env_install = NULL; ncxmod_home = NULL; ncxmod_mod_path = NULL; ncxmod_data_path = NULL; ncxmod_run_path = NULL; if (ncxmod_home_cli) { m__free(ncxmod_home_cli); } if (ncxmod_yuma_home_cli) { m__free(ncxmod_yuma_home_cli); } if (ncxmod_yumadir_path) { m__free(ncxmod_yumadir_path); } if (ncxmod_mod_path_cli) { m__free(ncxmod_mod_path_cli); } if (ncxmod_data_path_cli) { m__free(ncxmod_data_path_cli); } if (ncxmod_run_path_cli) { m__free(ncxmod_run_path_cli); } ncxmod_init_done = FALSE; } /* ncxmod_cleanup */ /******************************************************************** * FUNCTION ncxmod_load_module * * Determine the location of the specified module * and then load it into the system, if not already loaded * * This is the only load module variant that checks if there * are any errors recorded in the module or any of its dependencies * !!! ONLY RETURNS TRUE IF MODULE AND ALL IMPORTS ARE LOADED OK !!! * * Module Search order: * * 1) YUMA_MODPATH environment var (or set by modpath CLI var) * 2) current dir or absolute path * 3) YUMA_HOME/modules directory * 4) HOME/modules directory * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * savedevQ == Q of ncx_save_deviations_t to use, if any * retmod == address of return module (may be NULL) * * OUTPUTS: * if non-NULL: * *retmod == pointer to requested module version * * RETURNS: * status *********************************************************************/ status_t ncxmod_load_module (const xmlChar *modname, const xmlChar *revision, dlq_hdr_t *savedevQ, ncx_module_t **retmod) { assert( modname && "modname is NULL!" ); if (retmod) { *retmod = NULL; } status_t res = NO_ERR; yang_pcb_t *pcb = yang_new_pcb(); if (!pcb) { res = ERR_INTERNAL_MEM; } else { pcb->revision = revision; pcb->savedevQ = savedevQ; res = try_load_module(pcb, YANG_PT_TOP, modname, revision, retmod); if(res==NO_ERR) { ncx_module_t* mod; if(retmod) { mod = *retmod; } else { mod = ncx_find_module(modname, revision); assert(mod); } /* module loaded directly - in yang-library context implemented (not just imported) */ mod->implemented = TRUE; } } if (LOGINFO && res != NO_ERR) { if (revision) { log_info("\nLoad module '%s', revision '%s' failed (%s)", modname, revision, get_error_string(res)); } else { log_info("\nLoad module '%s' failed (%s)", modname, get_error_string(res)); } } if (pcb) { yang_free_pcb(pcb); } return res; } /* ncxmod_load_module */ /******************************************************************** * FUNCTION ncxmod_parse_module * * Determine the location of the specified module * and then parse the file into an ncx_module_t, * but do not load it into the module registry * * Used by yangcli to build per-session view of each * module advertised by the NETCONF server * * Module Search order: * * 1) YUMA_MODPATH environment var (or set by modpath CLI var) * 2) current dir or absolute path * 3) YUMA_HOME/modules directory * 4) HOME/modules directory * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * savedevQ == Q of ncx_save_deviations_t to use, if any * retmod == address of return module * * OUTPUTS: * *retmod == pointer to requested module version * THIS IS A LIVE MALLOCED STRUCT THAT NEEDS * FREED LATER * * RETURNS: * status *********************************************************************/ status_t ncxmod_parse_module (const xmlChar *modname, const xmlChar *revision, dlq_hdr_t *savedevQ, ncx_module_t **retmod) { yang_pcb_t *pcb; status_t res; #ifdef DEBUG if (!modname) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; pcb = yang_new_pcb(); if (!pcb) { res = ERR_INTERNAL_MEM; } else { pcb->revision = revision; pcb->savedevQ = savedevQ; pcb->parsemode = TRUE; res = try_load_module(pcb, YANG_PT_TOP, modname, revision, retmod); } if (LOGINFO && res != NO_ERR) { if (revision) { log_info("\nLoad module '%s', revision '%s' failed (%s)", modname, revision, get_error_string(res)); } else { log_info("\nLoad module '%s' failed (%s)", modname, get_error_string(res)); } } if (pcb) { yang_free_pcb(pcb); } return res; } /* ncxmod_parse_module */ /******************************************************************** * FUNCTION ncxmod_find_module * * Determine the location of the specified module * and then fill out a search result struct * DO NOT load it into the system * * Module Search order: * * 1) YUMA_MODPATH environment var (or set by modpath CLI var) * 2) current dir or absolute path * 3) YUMA_HOME/modules directory * 4) HOME/modules directory * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * * RETURNS: * == malloced and filled in search result struct * MUST call ncxmod_free_search_result() if the * return value is non-NULL * CHECK result->res TO SEE IF MODULE WAS FOUND * A SEARCH RESULT WILL BE RETURNED IF A SEARCH WAS * ATTEMPTED, EVEN IF NOTHING FOUND * == NULL if any error preventing a search *********************************************************************/ ncxmod_search_result_t * ncxmod_find_module (const xmlChar *modname, const xmlChar *revision) { yang_pcb_t *pcb; ncxmod_search_result_t *searchresult; ncx_module_t *retmod; status_t res; #ifdef DEBUG if (!modname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif pcb = yang_new_pcb(); if (pcb == NULL) { return NULL; } pcb->revision = revision; pcb->searchmode = TRUE; retmod = NULL; searchresult = NULL; res = try_load_module(pcb, YANG_PT_TOP, modname, revision, &retmod); if (res != NO_ERR || retmod == NULL) { if (LOGDEBUG2) { if (revision) { log_debug2("\nFind module '%s', revision '%s' failed (%s)", modname, revision, get_error_string(res)); } else { log_debug2("\nFind module '%s' failed (%s)", modname, get_error_string(res)); } } } else { searchresult = make_search_result(retmod); } if (pcb) { yang_free_pcb(pcb); } return searchresult; } /* ncxmod_find_module */ /******************************************************************** * FUNCTION ncxmod_find_all_modules * * Determine the location of all possible YANG modules and submodules * within the configured YUMA_MODPATH and default search path * All files with .yang and .yin file extensions found in the * search directories will be checked. * * Does not cause modules to be fully parsed and registered. * Quick parse only is done, and modules are discarded. * Strings from the module are copied into the searchresult struct * * Module Search order: * * 1) YUMA_MODPATH environment var (or set by modpath CLI var) * 2) HOME/modules directory * 3) YUMA_HOME/modules directory * 4) YUMA_INSTALL/modules directory * * INPUTS: * resultQ == address of Q to stor malloced search results * * OUTPUTS: * resultQ may have malloced ncxmod_zsearch_result_t structs * queued into it representing the modules found in the search * * RETURNS: * status *********************************************************************/ status_t ncxmod_find_all_modules (dlq_hdr_t *resultQ) { xmlChar *buff; uint32 bufflen; status_t res; #ifdef DEBUG if (!resultQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; /* get a temp buffer to construct filespecs */ bufflen = NCXMOD_MAX_FSPEC_LEN+1; buff = m__getMem(bufflen); if (!buff) { return ERR_INTERNAL_MEM; } else { *buff = 0; } /* 1) try YUMA_MODPATH environment variable if set */ if (ncxmod_mod_path) { res = search_module_pathlist(ncxmod_mod_path, search_subtree_callback, resultQ); } /* 2) HOME/modules directory */ if (res == NO_ERR && ncxmod_home) { res = search_module_path(ncxmod_home, buff, bufflen, search_subtree_callback, resultQ); } /* 3) YUMA_HOME/modules directory */ if (res == NO_ERR && ncxmod_yuma_home) { res = search_module_path(ncxmod_yuma_home, buff, bufflen, search_subtree_callback, resultQ); } /* 4) YUMA_INSTALL/modules directory or default install path * If this envvar is set then the default install path will not * be tried */ if (res == NO_ERR) { if (ncxmod_env_install) { res = search_module_path(ncxmod_env_install, buff, bufflen, search_subtree_callback, resultQ); } else { res = search_module_path(NCXMOD_DEFAULT_INSTALL, buff, bufflen, search_subtree_callback, resultQ); } } m__free(buff); return NO_ERR; /* ignore any module errors found */ } /* ncxmod_find_all_modules */ /******************************************************************** * FUNCTION ncxmod_load_deviation * * Determine the location of the specified module * and then parse it in the special deviation mode * and save any deviation statements in the Q that is provided * * Module Search order: * * 1) YUMA_MODPATH environment var (or set by modpath CLI var) * 2) current dir or absolute path * 3) YUMA_HOME/modules directory * 4) HOME/modules directory * * INPUTS: * devname == deviation module name with * no path prefix or file extension * deviationQ == address of Q of ncx_save_deviations_t structs * to add any new entries * * OUTPUTS: * if non-NULL: * deviationQ has malloced ncx_save_deviations_t structs added * * RETURNS: * status *********************************************************************/ status_t ncxmod_load_deviation (const xmlChar *deviname, dlq_hdr_t *deviationQ) { yang_pcb_t *pcb; ncx_module_t *retmod; ncx_save_deviations_t *savedev; status_t res; #ifdef DEBUG if (!deviname || !deviationQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; retmod = NULL; /* first check that this deviation has not aleady * been loaded and the user gave multiple leaf-list * values with the same name (may not get checked) */ for (savedev = (ncx_save_deviations_t *) dlq_firstEntry(deviationQ); savedev != NULL; savedev = (ncx_save_deviations_t *) dlq_nextEntry(savedev)) { if (!xml_strcmp(deviname, savedev->devmodule)) { if (LOGDEBUG) { log_debug("\nSkipping duplicate deviation module '%s'", deviname); } return NO_ERR; } } pcb = yang_new_pcb(); if (!pcb) { res = ERR_INTERNAL_MEM; } else { pcb->deviationmode = TRUE; pcb->savedevQ = deviationQ; res = try_load_module(pcb, YANG_PT_TOP, deviname, NULL, &retmod); } if (res != NO_ERR) { log_error("\nError: Load deviation module '%s' failed (%s)\n", deviname, get_error_string(res)); } else if (LOGDEBUG) { log_debug("\nLoad deviation module '%s' OK", deviname); } if (pcb) { yang_free_pcb(pcb); } return res; } /* ncxmod_load_deviation */ /******************************************************************** * FUNCTION ncxmod_process_deviation_imports * * Iterate over the deviation module's imports and fill in the * module structure for each. ncxmod_load_deviation() skips loading * imported modules. This function should be called after all * modules have already been loaded. * * INPUTS: * savedev == deviation structure to update * * RETURNS: * status *********************************************************************/ status_t ncxmod_process_deviation_imports(ncx_save_deviations_t *savedev) { status_t res = NO_ERR; ncx_import_t *imp; ncx_module_t *impmod; for (imp = (ncx_import_t *)dlq_firstEntry(&savedev->importQ); imp != NULL; imp = (ncx_import_t *)dlq_nextEntry(imp)) { impmod = ncx_find_module(imp->module, imp->revision); if (impmod == NULL) { res = ERR_NCX_OPERATION_FAILED; break; } imp->res = NO_ERR; imp->mod = impmod; } return res; } /* ncxmod_process_deviation_imports */ /******************************************************************** * FUNCTION ncxmod_resolve_deviations * * Iterate over the saved deviations and resolve those deviations * that target the current module. * * INPUTS: * mod == the module in which deviations should be resolved * savedevQ == a queue of deviations to try * * RETURNS: * status *********************************************************************/ status_t ncxmod_resolve_deviations(ncx_module_t *mod, dlq_hdr_t *savedevQ) { status_t res; yang_pcb_t *tmppcb; tk_chain_t *tkc = NULL; tmppcb = yang_new_pcb(); if (tmppcb == NULL) { res = ERR_INTERNAL_MEM; goto out; } tkc = tk_new_chain(); if (tkc == NULL) { res = ERR_INTERNAL_MEM; goto out; } tmppcb->savedevQ = savedevQ; res = yang_obj_resolve_ext_deviations(tmppcb, tkc, mod); if (res != NO_ERR) goto out; out: if (tmppcb) yang_free_pcb(tmppcb); if (tkc) tk_free_chain(tkc); return res; } /******************************************************************** * FUNCTION ncxmod_apply_deviations * * Iterate over the saved deviations and apply those deviations * that target the current module. * * INPUTS: * mod == the module to which deviations should be applied * savedevQ == a queue of deviations to try * * RETURNS: * status *********************************************************************/ status_t ncxmod_apply_deviations(ncx_module_t *mod) { status_t res; yang_pcb_t *tmppcb; tk_chain_t *tmptkc = NULL; tmppcb = yang_new_pcb(); if (tmppcb == NULL) { res = ERR_INTERNAL_MEM; goto out; } tmptkc = tk_new_chain(); if (tmptkc == NULL) { res = ERR_INTERNAL_MEM; goto out; } /* remove any nodes that were marked as deleted by deviations */ if (LOGDEBUG4) { log_debug4("\n%s: remove deleted nodes in module %s", __func__, mod->name); } res = yang_obj_remove_deleted_nodes(tmppcb, tmptkc, mod, &mod->datadefQ); out: if (tmppcb) yang_free_pcb(tmppcb); if (tmptkc) tk_free_chain(tmptkc); return res; } /******************************************************************** * FUNCTION ncxmod_load_imodule * * Determine the location of the specified module * and then load it into the system, if not already loaded * * Called from an include or import or submodule * Includes the YANG parser control block and new parser source type * * Module Search order: * * 1) YUMA_MODPATH environment var (or set by modpath CLI var) * 2) current dir or absolute path * 3) YUMA_HOME/modules directory * 4) HOME/modules directory * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * pcb == YANG parser control block * ptyp == YANG parser source type * parent == pointer to module being parsed if this is a * a request to parse a submodule; there is only 1 parent for * all submodules, based on the value of belongs-to * retmod == address of return module * OUTPUTS: * *retmod == pointer to found module (if NO_ERR) * * RETURNS: * status *********************************************************************/ status_t ncxmod_load_imodule (const xmlChar *modname, const xmlChar *revision, yang_pcb_t *pcb, yang_parsetype_t ptyp, ncx_module_t *parent, ncx_module_t **retmod) { yang_node_t *node; const xmlChar *savedrev; ncx_module_t *savedparentparm; status_t res; #ifdef DEBUG if (!modname || !pcb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* check if return module is requested */ if (retmod != NULL) { *retmod = NULL; } /* see if [sub]module already tried and failed */ node = yang_find_node(&pcb->failedQ, modname, revision); if (node) { return node->res; } savedrev = pcb->revision; pcb->revision = revision; savedparentparm = pcb->parentparm; pcb->parentparm = parent; res = try_load_module(pcb, ptyp, modname, revision, retmod); pcb->revision = savedrev; pcb->parentparm = savedparentparm; return res; } /* ncxmod_load_imodule */ /******************************************************************** * FUNCTION ncxmod_load_module_ex * * Determine the location of the specified module * and then load it into the system, if not already loaded * Return the PCB instead of deleting it * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * with_submods == TRUE if YANG_PT_TOP mode should skip submodules * == FALSE if top-level mode should process sub-modules * savetkc == TRUE if the parse chain should be saved (e.g., YIN) * keepmode == TRUE if pcb->top should be saved even if it * is already loaded; FALSE will allow the mod * to be swapped out and the new copy deleted * yangdump sets this to true * docmode == TRUE if need to preserve strings for --format=html or yang * == FALSE if no need to preserve string token sequences * savedevQ == Q of ncx_save_deviations_t to use * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced parser control block, or NULL of none *********************************************************************/ yang_pcb_t * ncxmod_load_module_ex (const xmlChar *modname, const xmlChar *revision, boolean with_submods, boolean savetkc, boolean keepmode, boolean docmode, dlq_hdr_t *savedevQ, status_t *res) { yang_pcb_t *pcb; #ifdef DEBUG if (!modname || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif pcb = yang_new_pcb(); if (!pcb) { *res = ERR_INTERNAL_MEM; } else { pcb->keepmode = keepmode; pcb->savedevQ = savedevQ; pcb->revision = revision; pcb->with_submods = with_submods; pcb->savetkc = savetkc; pcb->docmode = docmode; *res = try_load_module(pcb, YANG_PT_TOP, modname, revision, NULL); } return pcb; } /* ncxmod_load_module_ex */ /******************************************************************** * FUNCTION ncxmod_load_module_diff * * Determine the location of the specified module * and then load it into the system, if not already loaded * Return the PCB instead of deleting it * !!Do not add definitions to the registry!! * * INPUTS: * modname == module name with no path prefix or file extension * revision == optional revision date of 'modname' to find * with_submods == TRUE if YANG_PT_TOP mode should skip submodules * == FALSE if top-level mode should process sub-modules * modpath == module path to override the modpath CLI var or * the YUMA_MODPATH env var * savedevQ == Q of ncx_save_deviations_t to use * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced parser control block, or NULL of none *********************************************************************/ yang_pcb_t * ncxmod_load_module_diff (const xmlChar *modname, const xmlChar *revision, boolean with_submods, const xmlChar *modpath, dlq_hdr_t *savedevQ, status_t *res) { yang_pcb_t *pcb; #ifdef DEBUG if (!modname || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif pcb = yang_new_pcb(); if (!pcb) { *res = ERR_INTERNAL_MEM; } else { pcb->savedevQ = savedevQ; pcb->with_submods = with_submods; pcb->diffmode = TRUE; if (modpath) { ncxmod_set_altpath(modpath); } *res = try_load_module(pcb, YANG_PT_TOP, modname, revision, NULL); if (modpath) { ncxmod_clear_altpath(); } } return pcb; } /* ncxmod_load_module_diff */ /******************************************************************** * FUNCTION ncxmod_find_data_file * * Determine the location of the specified data file * * Search order: * * 1) YUMA_DATAPATH environment var (or set by datapath CLI var) * 2) current directory or absolute path * 3) HOME/data directory * 4) YUMA_HOME/data directory * 5) HOME/.yuma/ directory * 6a) YUMA_INSTALL/data directory OR * 6b) /usr/share/yuma/data directory * 7) /etc/yuma directory * * INPUTS: * fname == file name with extension * if the first char is '.' or '/', then an absolute * path is assumed, and the search path will not be tries * generrors == TRUE if error message should be generated * FALSE if no error message * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * pointer to the malloced and initialized string containing * the complete filespec or NULL if not found * It must be freed after use!!! *********************************************************************/ xmlChar * ncxmod_find_data_file (const xmlChar *fname, boolean generrors, status_t *res) { xmlChar *buff; uint32 flen, bufflen; int ret; #ifdef DEBUG if (!fname || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif *res = NO_ERR; if (LOGDEBUG2) { log_debug2("\nNcxmod: Finding data file (%s)", fname); } flen = xml_strlen(fname); if (!flen || flen>NCX_MAX_NLEN) { *res = ERR_NCX_WRONG_LEN; return NULL; } /* get a buffer to construct filespacs */ bufflen = NCXMOD_MAX_FSPEC_LEN+1; buff = m__getMem(bufflen); if (!buff) { *res = ERR_INTERNAL_MEM; return NULL; } /* 2) current directory or absolute path */ if (test_file(buff, bufflen, NULL, NULL, fname)) { return buff; } /* 3) HOME/data directory */ if (ncxmod_home) { if (test_file(buff, bufflen, ncxmod_home, NCXMOD_DATA_DIR, fname)) { return buff; } } /* 4) YUMA_HOME/data directory */ if (ncxmod_yuma_home) { if (test_file(buff, bufflen, ncxmod_yuma_home, NCXMOD_DATA_DIR, fname)) { return buff; } } /* 5) HOME/.yuma directory */ if (ncxmod_home) { if (test_file(buff, bufflen, ncxmod_home, NCXMOD_YUMA_DIRNAME, fname)) { return buff; } } /* 6a) YUMA_INSTALL/data directory */ if (ncxmod_env_install) { if (test_file(buff, bufflen, ncxmod_env_install, NCXMOD_DATA_DIR, fname)) { return buff; } } else if (test_file(buff, bufflen, NCXMOD_DEFAULT_INSTALL, NCXMOD_DATA_DIR, fname)) { /* 6b) default YUMA_INSTALL data directory */ return buff; } /* 7) default Yuma data directory */ if (test_file(buff, bufflen, NCXMOD_ETC_DATA, NULL, fname)) { return buff; } if (generrors) { log_error("\nError: data file (%s) not found.\n", fname); } m__free(buff); *res = ERR_NCX_CFG_NOT_FOUND; return NULL; } /* ncxmod_find_data_file */ /******************************************************************** * FUNCTION ncxmod_find_sil_file * * Determine the location of the specified * server instrumentation library file * * Search order: * * 1) $YUMA_HOME/target/lib directory * 2) $YUMA_RUNPATH environment variable * 3) $YUMA_INSTALL/lib * 4) $YUMA_INSTALL/lib/yuma * 5) /usr/lib[64]/yuma directory * * INPUTS: * fname == SIL file name with extension * generrors == TRUE if error message should be generated * FALSE if no error message * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * pointer to the malloced and initialized string containing * the complete filespec or NULL if not found * It must be freed after use!!! *********************************************************************/ xmlChar * ncxmod_find_sil_file (const xmlChar *fname, boolean generrors, status_t *res) { xmlChar *buff; uint32 flen, bufflen; #ifdef DEBUG if (!fname || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif *res = NO_ERR; if (LOGDEBUG2) { log_debug2("\nNcxmod: Finding SIL file (%s)", fname); } flen = xml_strlen(fname); if (!flen || flen>NCX_MAX_NLEN) { *res = ERR_NCX_WRONG_LEN; return NULL; } /* get a buffer to construct filespacs */ bufflen = NCXMOD_MAX_FSPEC_LEN+1; buff = m__getMem(bufflen); if (!buff) { *res = ERR_INTERNAL_MEM; return NULL; } /* 1) YUMA_HOME/target/lib directory */ if (ncxmod_yuma_home) { if (test_file(buff, bufflen, ncxmod_yuma_home, (const xmlChar *)"target/lib/", fname)) { return buff; } } /* 2) try the YUMA_RUNPATH environment variable */ if (ncxmod_run_path) { if (test_pathlist(ncxmod_run_path, buff, bufflen, fname, NULL)) { return buff; } } /* 3) YUMA_INSTALL/lib directory */ if (ncxmod_env_install) { if (test_file(buff, bufflen, ncxmod_env_install, (const xmlChar *)"lib", fname)) { return buff; } } /* 4) YUMA_INSTALL/lib/yuma directory */ if (ncxmod_env_install) { if (test_file(buff, bufflen, ncxmod_env_install, (const xmlChar *)"lib/yuma", fname)) { return buff; } } /* 5 /usr/lib[64]/yuma directory */ if (test_file(buff, bufflen, NCXMOD_DEFAULT_YUMALIB, NULL, fname)) { return buff; } #ifdef NCXMOD_SIL_INSTALL_PATH /* 6 or 7) ${NCXMOD_SIL_INSTALL_PATH} e.g. /usr/lib/x86_64-linux-gnu/yuma directory */ if (test_file(buff, bufflen, NCXMOD_SIL_INSTALL_PATH, NULL, fname)) { return buff; } #endif /* 8) try /usr/lib/yuma */ if (!ncxmod_run_path) { if (test_pathlist("/usr/lib/yuma", buff, bufflen, fname, NULL)) { return buff; } } if (generrors) { log_error("\nError: SIL file (%s) not found.\n", fname); } m__free(buff); *res = ERR_NCX_MOD_NOT_FOUND; return NULL; } /* ncxmod_find_sil_file */ /******************************************************************** * FUNCTION ncxmod_make_data_filespec * * Determine a suitable path location for the specified data file name * * Search order: * * 1) YUMA_DATAPATH environment var (or set by datapath CLI var) * 2) HOME/data directory * 3) YUMA_HOME/data directory * 4) HOME/.yuma directory * 5) YUMA_INSTALL/data directory * 6) current directory * * INPUTS: * fname == file name with extension * if the first char is '.' or '/', then an absolute * path is assumed, and the search path will not be tries * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * pointer to the malloced and initialized string containing * the complete filespec or NULL if no suitable location * for the datafile is found * It must be freed after use!!! *********************************************************************/ xmlChar * ncxmod_make_data_filespec (const xmlChar *fname, status_t *res) { xmlChar *buff; uint32 flen, pathlen, bufflen; #ifdef DEBUG if (!fname || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif *res = NO_ERR; flen = xml_strlen(fname); if (!flen || flen > NCX_MAX_NLEN) { *res = ERR_NCX_WRONG_LEN; return NULL; } /* get a buffer to construct filespecs */ bufflen = NCXMOD_MAX_FSPEC_LEN+1; buff = m__getMem(bufflen); if (!buff) { *res = ERR_INTERNAL_MEM; return NULL; } /* 1) try the YUMA_DATAPATH environment variable */ if (ncxmod_data_path) { if (test_pathlist_make(ncxmod_data_path, buff, bufflen)) { pathlen = xml_strlen(buff); if ((pathlen + flen) < bufflen) { xml_strcat(buff, fname); } else { *res = ERR_BUFF_OVFL; m__free(buff); return NULL; } } } /* 2) HOME/data directory */ if (ncxmod_home) { if (test_file_make(buff, bufflen, ncxmod_home, NCXMOD_DATA_DIR, fname) == NO_ERR) { return buff; } } /* 3) YUMA_HOME/data directory */ if (ncxmod_yuma_home) { if (test_file_make(buff, bufflen, ncxmod_yuma_home, NCXMOD_DATA_DIR, fname) == NO_ERR) { return buff; } } /* 4) HOME/.yuma directory */ if (ncxmod_home) { if (test_file_make(buff, bufflen, ncxmod_home, NCXMOD_YUMA_DIRNAME, fname) == NO_ERR) { return buff; } } /* 5) YUMA_INSTALL/data directory */ if (ncxmod_env_install) { if (test_file_make(buff, bufflen, ncxmod_env_install, NCXMOD_DATA_DIR, fname) == NO_ERR) { return buff; } } else { if (test_file_make(buff, bufflen, NCXMOD_DEFAULT_INSTALL, NCXMOD_DATA_DIR, fname) == NO_ERR) { return buff; } } /* 6) current directory */ if (test_file_make(buff, bufflen, NULL, NULL, fname) == NO_ERR) { return buff; } m__free(buff); *res = ERR_NCX_MOD_NOT_FOUND; return NULL; } /* ncxmod_make_data_filespec */ /******************************************************************** * FUNCTION ncxmod_make_data_filespec_from_src * * Determine the directory path portion of the specified * source_url and change the filename to the specified filename * in a new copy of the complete filespec * * INPUTS: * srcspec == source filespec to use * fname == file name with extension * if the first char is '.' or '/', then an absolute * path is assumed, and the search path will not be tries * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * pointer to the malloced and initialized string containing * the complete filespec or NULL if some error occurred *********************************************************************/ xmlChar * ncxmod_make_data_filespec_from_src (const xmlChar *srcspec, const xmlChar *fname, status_t *res) { const xmlChar *str; xmlChar *buff, *p; uint32 flen, pathlen, copylen, bufflen; #ifdef DEBUG if (!srcspec || !fname || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif *res = NO_ERR; pathlen = xml_strlen(srcspec); if (pathlen == 0) { *res = ERR_NCX_WRONG_LEN; return NULL; } flen = xml_strlen(fname); if (!flen || flen > NCX_MAX_NLEN) { *res = ERR_NCX_WRONG_LEN; return NULL; } bufflen = flen+1; copylen = 0; /* find the end of the path portion of the srcspec */ str = &srcspec[pathlen-1]; while (str >= srcspec && *str != NCXMOD_PSCHAR) { str--; } if (*str == NCXMOD_PSCHAR) { copylen = (uint32)(str - srcspec) + 1; bufflen += copylen; } /* get a buffer to construct the filespec */ buff = m__getMem(bufflen); if (!buff) { *res = ERR_INTERNAL_MEM; return NULL; } p = buff; if (copylen) { p += xml_strncpy(p, srcspec, copylen); } xml_strcpy(p, fname); return buff; } /* ncxmod_make_data_filespec_from_src */ /******************************************************************** * FUNCTION ncxmod_find_script_file * * Determine the location of the specified script file * * Search order: * * 1) current directory or absolute path * 2) YUMA_RUNPATH environment var (or set by runpath CLI var) * 3) HOME/scripts directory * 4) YUMA_HOME/scripts directory * 5) YUMA_INSTALL/scripts directory * * INPUTS: * fname == file name with extension * if the first char is '.' or '/', then an absolute * path is assumed, and the search path will not be tries * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * pointer to the malloced and initialized string containing * the complete filespec or NULL if not found * It must be freed after use!!! *********************************************************************/ xmlChar * ncxmod_find_script_file (const xmlChar *fname, status_t *res) { xmlChar *buff; uint32 flen, bufflen; #ifdef DEBUG if (!fname || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif *res = NO_ERR; if (LOGDEBUG2) { log_debug2("\nNcxmod: Finding script file (%s)", fname); } flen = xml_strlen(fname); if (!flen || flen>NCX_MAX_NLEN) { *res = ERR_NCX_WRONG_LEN; return NULL; } /* get a buffer to construct filespacs */ bufflen = NCXMOD_MAX_FSPEC_LEN+1; buff = m__getMem(bufflen); if (!buff) { *res = ERR_INTERNAL_MEM; return NULL; } /* 1) try 'fname' as a current directory or absolute path */ if (test_file(buff, bufflen, NULL, NULL, fname)) { return buff; } /* look for the script file 'fname' * 2) check YUMA_RUNPATH env-var or runpath CLI param */ if (ncxmod_run_path) { if (test_pathlist(ncxmod_run_path, buff, bufflen, fname, NULL)) { return buff; } } /* 3) try HOME/scripts/fname */ if (ncxmod_home) { if (test_file(buff, bufflen, ncxmod_home, NCXMOD_SCRIPT_DIR, fname)) { return buff; } } /* 4) try YUMA_HOME/scripts/fname */ if (ncxmod_yuma_home) { if (test_file(buff, bufflen, ncxmod_yuma_home, NCXMOD_SCRIPT_DIR, fname)) { return buff; } } /* 5) YUMA_INSTALL/scripts directory or default install path * If this envvar is set then the default install path will not * be tried */ if (ncxmod_env_install) { if (test_file(buff, bufflen, ncxmod_env_install, NCXMOD_SCRIPT_DIR, fname)) { return buff; } } else { if (test_file(buff, bufflen, NCXMOD_DEFAULT_INSTALL, NCXMOD_SCRIPT_DIR, fname)) { return buff; } } log_info("\nError: script file (%s) not found.", fname); m__free(buff); *res = ERR_NCX_MISSING_FILE; return NULL; } /* ncxmod_find_script_file */ /******************************************************************** * FUNCTION ncxmod_set_home * * Override the HOME env var with the home CLI var * * THIS MAY GET SET DURING BOOTSTRAP SO SET_ERROR NOT CALLED !!! * MALLOC FAILED IGNORED!!! * * INPUTS: * home == new HOME value * == NULL or empty string to disable *********************************************************************/ void ncxmod_set_home (const xmlChar *home) { xmlChar *savehome = ncxmod_home_cli; xmlChar *savepath = ncxmod_yumadir_path; status_t res = NO_ERR; if (home && *home) { if (*home == '/') { ncxmod_home_cli = xml_strdup(home); if (ncxmod_home_cli == NULL) { res = ERR_INTERNAL_MEM; } } else { ncxmod_home_cli = ncx_get_source(home, &res); } } else { log_error("\nError: cannot set 'home' to empty string\n"); return; } if (ncxmod_home_cli == NULL) { log_error("\nError: set home to '%s' failed (%s)\n", home, get_error_string(res)); ncxmod_home_cli = savehome; return; } ncxmod_home = ncxmod_home_cli; if (savehome) { m__free(savehome); } /* reset the yumadir path if needed */ if (ncxmod_home != NULL) { ncxmod_yumadir_path = ncx_get_source(NCXMOD_YUMA_DIR, &res); } else { ncxmod_yumadir_path = xml_strdup(NCXMOD_TEMP_YUMA_DIR); if (ncxmod_yumadir_path == NULL) { res = ERR_INTERNAL_MEM; } } if (ncxmod_yumadir_path == NULL) { log_error("\nError: set yumadir_path to '%s' failed (%s)\n", home, get_error_string(res)); ncxmod_yumadir_path = savepath; return; } if (savepath) { m__free(savepath); } } /* ncxmod_set_home */ /******************************************************************** * FUNCTION ncxmod_get_home * * Get the HOME or --home parameter value, * whichever is in effect, if any * * RETURNS: * const point to the home variable, or NULL if not set *********************************************************************/ const xmlChar * ncxmod_get_home (void) { return ncxmod_home; } /* ncxmod_get_home */ /******************************************************************** * FUNCTION ncxmod_set_yuma_home * * Override the YUMA_HOME env var with the yuma-home CLI var * * THIS MAY GET SET DURING BOOTSTRAP SO SET_ERROR NOT CALLED !!! * MALLOC FAILED IGNORED!!! * * INPUTS: * yumahome == new YUMA_HOME value *********************************************************************/ void ncxmod_set_yuma_home (const xmlChar *yumahome) { xmlChar *savehome = ncxmod_yuma_home_cli; status_t res = NO_ERR; if (yumahome && *yumahome) { if (*yumahome == '/') { ncxmod_yuma_home_cli = xml_strdup(yumahome); if (ncxmod_yuma_home_cli == NULL) { res = ERR_INTERNAL_MEM; } } else { ncxmod_yuma_home_cli = ncx_get_source(yumahome, &res); } if (ncxmod_yuma_home_cli == NULL) { log_error("\nError: set yuma home to '%s' failed (%s)", yumahome, get_error_string(res)); return; } ncxmod_yuma_home = ncxmod_yuma_home_cli; if (savehome) { m__free(savehome); } } else { log_error("\nError: cannot set yuma home to empty string\n"); return; } } /* ncxmod_set_yuma_home */ /******************************************************************** * FUNCTION ncxmod_get_yuma_home * * Get the YUMA_HOME or --yuma-home parameter value, * whichever is in effect, if any * * RETURNS: * const point to the yuma_home variable, or NULL if not set *********************************************************************/ const xmlChar * ncxmod_get_yuma_home (void) { return ncxmod_yuma_home; } /* ncxmod_get_yuma_home */ /******************************************************************** * FUNCTION ncxmod_get_yuma_install * * Get the YUMA_INSTALL or default install parameter value, * whichever is in effect * * RETURNS: * const point to the YUMA_INSTALL value *********************************************************************/ const xmlChar * ncxmod_get_yuma_install (void) { if (ncxmod_env_install) { return ncxmod_env_install; } else { return NCXMOD_DEFAULT_INSTALL; } } /* ncxmod_get_yuma_install */ /******************************************************************** * FUNCTION ncxmod_set_modpath * * Override the YUMA_MODPATH env var with the modpath CLI var * * THIS MAY GET SET DURING BOOTSTRAP SO SET_ERROR NOT CALLED !!! * MALLOC FAILED IGNORED!!! * * INPUTS: * modpath == new YUMA_MODPATH value * == NULL or empty string to disable *********************************************************************/ void ncxmod_set_modpath (const xmlChar *modpath) { if (ncxmod_mod_path_cli) { m__free(ncxmod_mod_path_cli); ncxmod_mod_path_cli = NULL; } if (modpath && *modpath) { /* ignoring possible malloc failed!! */ ncxmod_mod_path_cli = xml_strdup(modpath); } ncxmod_mod_path = ncxmod_mod_path_cli; } /* ncxmod_set_modpath */ /******************************************************************** * FUNCTION ncxmod_set_datapath * * Override the YUMA_DATAPATH env var with the datapath CLI var * * INPUTS: * datapath == new YUMA_DATAPATH value * == NULL or empty string to disable * *********************************************************************/ void ncxmod_set_datapath (const xmlChar *datapath) { if (ncxmod_data_path_cli) { m__free(ncxmod_data_path_cli); ncxmod_data_path_cli = NULL; } if (datapath && *datapath) { /* ignoring possible malloc failed!! */ ncxmod_data_path_cli = xml_strdup(datapath); } ncxmod_data_path = ncxmod_data_path_cli; } /* ncxmod_set_datapath */ /******************************************************************** * FUNCTION ncxmod_set_runpath * * Override the YUMA_RUNPATH env var with the runpath CLI var * * INPUTS: * datapath == new YUMA_RUNPATH value * == NULL or empty string to disable * *********************************************************************/ void ncxmod_set_runpath (const xmlChar *runpath) { if (ncxmod_run_path_cli) { m__free(ncxmod_run_path_cli); ncxmod_run_path_cli = NULL; } if (runpath && *runpath) { /* ignoring possible malloc failed!! */ ncxmod_run_path_cli = xml_strdup(runpath); } ncxmod_run_path = ncxmod_run_path_cli; } /* ncxmod_set_runpath */ /******************************************************************** * FUNCTION ncxmod_set_subdirs * * Set the subdirs flag to FALSE if the no-subdirs CLI param is set * * INPUTS: * usesubdirs == TRUE if subdirs searchs should be done * == FALSE if subdir searches should not be done *********************************************************************/ void ncxmod_set_subdirs (boolean usesubdirs) { ncxmod_subdirs = usesubdirs; } /* ncxmod_set_subdirs */ /******************************************************************** * FUNCTION ncxmod_get_yumadir * * Get the yuma directory being used * * RETURNS: * pointer to the yuma dir string *********************************************************************/ const xmlChar * ncxmod_get_yumadir (void) { return ncxmod_yumadir_path; } /* ncxmod_get_yumadir */ /******************************************************************** * FUNCTION ncxmod_process_subtree * * Search the entire specified subtree, looking for YANG * modules. Invoke the callback function for each module * file found * * INPUTS: * startspec == absolute or relative pathspec to start * the search. If this is not a valid pathname, * processing will exit immediately. * callback == address of the ncxmod_callback_fn_t function * to use for this traveral * cookie == cookie to pass to each invocation of the callback * * RETURNS: * NO_ERR if file found okay, full filespec in the 'buff' variable * OR some error if not found or buffer overflow *********************************************************************/ status_t ncxmod_process_subtree (const char *startspec, ncxmod_callback_fn_t callback, void *cookie) { xmlChar *sourcespec; char *buff; DIR *dp; uint32 bufflen; status_t res; #ifdef DEBUG if (!startspec || !callback) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (strlen(startspec) >= NCXMOD_MAX_FSPEC_LEN) { log_error("\nError: startspec too long '%s'\n", startspec); return ERR_BUFF_OVFL; } res = NO_ERR; sourcespec = ncx_get_source((const xmlChar *)startspec, &res); if (!sourcespec) { return res; } dp = opendir((const char *)sourcespec); if (!dp) { if (LOGDEBUG) { log_debug("\nncxmod: could not open directory '%s'\n", startspec); } m__free(sourcespec); return NO_ERR; } else { (void)closedir(dp); } bufflen = NCXMOD_MAX_FSPEC_LEN+1; buff = m__getMem(bufflen); if (!buff) { m__free(sourcespec); return ERR_INTERNAL_MEM; } strncpy(buff, (const char *)sourcespec, bufflen); res = process_subtree(buff, bufflen, callback, cookie); m__free(sourcespec); m__free(buff); return res; } /* ncxmod_process_subtree */ /******************************************************************** * FUNCTION ncxmod_test_subdir * * Check if the specified string is a directory * * INPUTS: * dirspec == string to check as a directory spec * * RETURNS: * TRUE if the string is a directory spec that this user * is allowed to open * FALSE otherwise *********************************************************************/ boolean ncxmod_test_subdir (const xmlChar *dirspec) { DIR *dp; /* try to open the buffer spec as a directory */ dp = opendir((const char *)dirspec); if (!dp) { return FALSE; } else { (void)closedir(dp); return TRUE; } /*NOTREACHED*/ } /* ncxmod_test_subdir */ /******************************************************************** * FUNCTION ncxmod_get_userhome * * Get the user home dir from the passwd file * * INPUTS: * user == user name string (may not be zero-terminiated) * userlen == length of user * * RETURNS: * const pointer to the user home directory string *********************************************************************/ const xmlChar * ncxmod_get_userhome (const xmlChar *user, uint32 userlen) { struct passwd *pw; char buff[NCX_MAX_USERNAME_LEN+1]; /* only support user names up to N chars in length */ if (userlen > NCX_MAX_USERNAME_LEN) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } if (!user) { uid_t self = geteuid(); pw = getpwuid(self); if (!pw) return (const xmlChar *)ncxmod_home; } else { strncpy(buff, (const char *)user, userlen); buff[userlen] = 0; pw = getpwnam(buff); if (!pw) { return NULL; } } return (const xmlChar *)pw->pw_dir; } /* ncxmod_get_userhome */ /******************************************************************** * FUNCTION ncxmod_get_envvar_cached * * Get the specified shell environment variable OR the value that was * provided on the commandline, if present. * * INPUTS: * name == name of the environment variable (may not be zero-terminiated) * namelen == length of name string * * RETURNS: * const pointer to the requested value if found, NULL otherwise *********************************************************************/ static const xmlChar * ncxmod_get_envvar_cached (const xmlChar *name, uint32 namelen) { #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (strncmp(name, NCXMOD_HOME, namelen) == 0) return ncxmod_yuma_home; if (strncmp(name, NCXMOD_MODPATH, namelen) == 0) return ncxmod_yumadir_path; if (strncmp(name, NCXMOD_DATAPATH, namelen) == 0) return ncxmod_data_path; if (strncmp(name, NCXMOD_RUNPATH, namelen) == 0) return ncxmod_run_path; return NULL; } /******************************************************************** * FUNCTION ncxmod_get_envvar * * Get the specified shell environment variable * * INPUTS: * name == name of the environment variable (may not be zero-terminiated) * namelen == length of name string * * RETURNS: * const pointer to the specified environment variable value *********************************************************************/ const xmlChar * ncxmod_get_envvar (const xmlChar *name, uint32 namelen) { char buff[NCX_MAX_USERNAME_LEN+1]; const xmlChar *cached; #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif /* only support user names up to N chars in length */ if (namelen > NCX_MAX_USERNAME_LEN) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /* some commandline parameters take precendence over environment * variables */ cached = ncxmod_get_envvar_cached(name, namelen); if (cached) return cached; strncpy(buff, (const char *)name, namelen); buff[namelen] = 0; return (const xmlChar *)getenv(buff); } /* ncxmod_get_envvar */ /******************************************************************** * FUNCTION ncxmod_set_altpath * * Set the alternate path that should be used first (for yangdiff) * * INPUTS: * altpath == full path string to use * must be static * a const back-pointer is kept, not a copy * *********************************************************************/ void ncxmod_set_altpath (const xmlChar *altpath) { #ifdef DEBUG if (!altpath) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif ncxmod_alt_path = altpath; } /* ncxmod_set_altpath */ /******************************************************************** * FUNCTION ncxmod_clear_altpath * * Clear the alternate path so none is used (for yangdiff) * *********************************************************************/ void ncxmod_clear_altpath (void) { ncxmod_alt_path = NULL; } /* ncxmod_clear_altpath */ /******************************************************************** * FUNCTION ncxmod_list_data_files * * List the available data files found in the data search parh * * Search order: * * 1) current directory or absolute path * 2) YUMA_DATAPATH environment var (or set by datapath CLI var) * 3) HOME/data directory * 4) YUMA_HOME/data directory * 5) YUMA_INSTALL/data directory * * INPUTS: * helpmode == BRIEF, NORMAL or FULL * logstdout == TRUE to use log_stdout * FALSE to use log_write * * RETURNS: * status of the operation *********************************************************************/ status_t ncxmod_list_data_files (help_mode_t helpmode, boolean logstdout) { xmlChar *buff, *p; uint32 bufflen, pathlen; status_t res; /* get a buffer to construct filespacs */ bufflen = NCXMOD_MAX_FSPEC_LEN+1; buff = m__getMem(bufflen); if (!buff) { return ERR_INTERNAL_MEM; } /* 1) current directory */ xml_strcpy(buff, (const xmlChar *)"./"); res = list_subdirs(buff, bufflen, SEARCH_TYPE_DATA, helpmode, logstdout, FALSE); if (res != NO_ERR) { m__free(buff); return res; } /* 2) try the YUMA_DATAPATH environment variable */ if (ncxmod_data_path) { res = list_pathlist(ncxmod_data_path, buff, bufflen, SEARCH_TYPE_DATA, helpmode, logstdout); if (res != NO_ERR) { m__free(buff); return res; } } /* 3) HOME/data directory */ if (ncxmod_home) { pathlen = xml_strlen(ncxmod_home); if (pathlen + 6 < bufflen) { p = buff; p += xml_strcpy(p, ncxmod_home); *p++ = NCXMOD_PSCHAR; p += xml_strcpy(p, (const xmlChar *)"data"); *p++ = NCXMOD_PSCHAR; *p = '\0'; res = list_subdirs(buff, bufflen, SEARCH_TYPE_DATA, helpmode, logstdout, TRUE); if (res != NO_ERR) { m__free(buff); return res; } } } /* 4) YUMA_HOME/data directory */ if (ncxmod_yuma_home) { pathlen = xml_strlen(ncxmod_yuma_home);; if (pathlen + 6 < bufflen) { p = buff; p += xml_strcpy(p, ncxmod_yuma_home); *p++ = NCXMOD_PSCHAR; p += xml_strcpy(p, (const xmlChar *)"data"); *p++ = NCXMOD_PSCHAR; *p = '\0'; res = list_subdirs(buff, bufflen, SEARCH_TYPE_DATA, helpmode, logstdout, TRUE); if (res != NO_ERR) { m__free(buff); return res; } } } /* 5) YUMA_INSTALL/data directory */ if (ncxmod_env_install) { pathlen = xml_strlen(ncxmod_env_install);; if (pathlen + 6 < bufflen) { p = buff; p += xml_strcpy(p, ncxmod_env_install); *p++ = NCXMOD_PSCHAR; p += xml_strcpy(p, (const xmlChar *)"data"); *p++ = NCXMOD_PSCHAR; *p = '\0'; res = list_subdirs(buff, bufflen, SEARCH_TYPE_DATA, helpmode, logstdout, TRUE); if (res != NO_ERR) { m__free(buff); return res; } } } if (logstdout) { log_stdout("\n"); } else { log_write("\n"); } m__free(buff); return NO_ERR; } /* ncxmod_list_data_files */ /******************************************************************** * FUNCTION ncxmod_list_script_files * * List the available script files found in the 'run' search parh * * Search order: * * 1) current directory or absolute path * 2) YUMA_RUNPATH environment var (or set by datapath CLI var) * 3) HOME/scripts directory * 4) YUMA_HOME/scripts directory * 5) YUMA_INSTALL/scripts directory * * INPUTS: * helpmode == BRIEF, NORMAL or FULL * logstdout == TRUE to use log_stdout * FALSE to use log_write * * RETURNS: * status of the operation *********************************************************************/ status_t ncxmod_list_script_files (help_mode_t helpmode, boolean logstdout) { xmlChar *buff, *p; uint32 bufflen, pathlen; status_t res; /* get a buffer to construct filespacs */ bufflen = NCXMOD_MAX_FSPEC_LEN+1; buff = m__getMem(bufflen); if (!buff) { return ERR_INTERNAL_MEM; } /* 1) current directory */ xml_strcpy(buff, (const xmlChar *)"./"); res = list_subdirs(buff, bufflen, SEARCH_TYPE_SCRIPT, helpmode, logstdout, FALSE); if (res != NO_ERR) { m__free(buff); return res; } /* 2) try the NCX_RUNPATH environment variable */ if (ncxmod_run_path) { res = list_pathlist(ncxmod_run_path, buff, bufflen, SEARCH_TYPE_SCRIPT, helpmode, logstdout); if (res != NO_ERR) { m__free(buff); return res; } } /* 3) HOME/scripts directory */ if (ncxmod_home) { pathlen = xml_strlen(ncxmod_home); if (pathlen + 9 < bufflen) { p = buff; p += xml_strcpy(p, ncxmod_home); *p++ = NCXMOD_PSCHAR; p += xml_strcpy(p, (const xmlChar *)"scripts"); *p++ = NCXMOD_PSCHAR; *p = '\0'; res = list_subdirs(buff, bufflen, SEARCH_TYPE_SCRIPT, helpmode, logstdout, TRUE); if (res != NO_ERR) { m__free(buff); return res; } } } /* 4) YUMA_HOME/scripts directory */ if (ncxmod_yuma_home) { pathlen = xml_strlen(ncxmod_yuma_home);; if (pathlen + 9 < bufflen) { p = buff; p += xml_strcpy(p, ncxmod_yuma_home); *p++ = NCXMOD_PSCHAR; p += xml_strcpy(p, (const xmlChar *)"scripts"); *p++ = NCXMOD_PSCHAR; *p = '\0'; res = list_subdirs(buff, bufflen, SEARCH_TYPE_SCRIPT, helpmode, logstdout, TRUE); if (res != NO_ERR) { m__free(buff); return res; } } } /* 5) YUMA_INSTALL/scripts directory */ if (ncxmod_env_install) { pathlen = xml_strlen(ncxmod_env_install);; if (pathlen + 9 < bufflen) { p = buff; p += xml_strcpy(p, ncxmod_env_install); *p++ = NCXMOD_PSCHAR; p += xml_strcpy(p, (const xmlChar *)"scripts"); *p++ = NCXMOD_PSCHAR; *p = '\0'; res = list_subdirs(buff, bufflen, SEARCH_TYPE_SCRIPT, helpmode, logstdout, TRUE); if (res != NO_ERR) { m__free(buff); return res; } } } if (logstdout) { log_stdout("\n"); } else { log_write("\n"); } m__free(buff); return NO_ERR; } /* ncxmod_list_script_files */ /******************************************************************** * FUNCTION ncxmod_list_yang_files * * List the available YANG files found in the 'mod' search parh * * Search order: * * 1) current directory or absolute path * 2) YUMA_MODPATH environment var (or set by datapath CLI var) * 3) HOME/modules directory * 4) YUMA_HOME/modules directory * 5) YUMA_INSTALL/modules directory * * INPUTS: * helpmode == BRIEF, NORMAL or FULL * logstdout == TRUE to use log_stdout * FALSE to use log_write * * RETURNS: * status of the operation *********************************************************************/ status_t ncxmod_list_yang_files (help_mode_t helpmode, boolean logstdout) { xmlChar *buff, *p; uint32 bufflen, pathlen; status_t res; /* get a buffer to construct filespacs */ bufflen = NCXMOD_MAX_FSPEC_LEN+1; buff = m__getMem(bufflen); if (!buff) { return ERR_INTERNAL_MEM; } /* 1) current directory */ xml_strcpy(buff, (const xmlChar *)"./"); res = list_subdirs(buff, bufflen, SEARCH_TYPE_MODULE, helpmode, logstdout, FALSE); if (res != NO_ERR) { m__free(buff); return res; } /* 2) try the NCX_MODPATH environment variable */ if (ncxmod_mod_path) { res = list_pathlist(ncxmod_mod_path, buff, bufflen, SEARCH_TYPE_MODULE, helpmode, logstdout); if (res != NO_ERR) { m__free(buff); return res; } } /* 3) HOME/modules directory */ if (ncxmod_home) { pathlen = xml_strlen(ncxmod_home); if (pathlen + 9 < bufflen) { p = buff; p += xml_strcpy(p, ncxmod_home); *p++ = NCXMOD_PSCHAR; p += xml_strcpy(p, (const xmlChar *)"modules"); *p++ = NCXMOD_PSCHAR; *p = '\0'; res = list_subdirs(buff, bufflen, SEARCH_TYPE_MODULE, helpmode, logstdout, TRUE); if (res != NO_ERR) { m__free(buff); return res; } } } /* 4) YUMA_HOME/modules directory */ if (ncxmod_yuma_home) { pathlen = xml_strlen(ncxmod_yuma_home);; if (pathlen + 9 < bufflen) { p = buff; p += xml_strcpy(p, ncxmod_yuma_home); *p++ = NCXMOD_PSCHAR; p += xml_strcpy(p, (const xmlChar *)"modules"); *p++ = NCXMOD_PSCHAR; *p = '\0'; res = list_subdirs(buff, bufflen, SEARCH_TYPE_MODULE, helpmode, logstdout, TRUE); if (res != NO_ERR) { m__free(buff); return res; } } } /* 5) YUMA_INSTALL/modules directory */ if (ncxmod_env_install) { pathlen = xml_strlen(ncxmod_env_install);; if (pathlen + 9 < bufflen) { p = buff; p += xml_strcpy(p, ncxmod_env_install); *p++ = NCXMOD_PSCHAR; p += xml_strcpy(p, (const xmlChar *)"modules"); *p++ = NCXMOD_PSCHAR; *p = '\0'; res = list_subdirs(buff, bufflen, SEARCH_TYPE_MODULE, helpmode, logstdout, TRUE); if (res != NO_ERR) { m__free(buff); return res; } } } if (logstdout) { log_stdout("\n"); } else { log_write("\n"); } m__free(buff); return NO_ERR; } /* ncxmod_list_yang_files */ /******************************************************************** * FUNCTION ncxmod_setup_yumadir * * Setup the ~/.yuma directory if it does not exist * * RETURNS: * status *********************************************************************/ status_t ncxmod_setup_yumadir (void) { DIR *dp; status_t res; int retcode; /* try to open yuma directory */ res = NO_ERR; dp = opendir((const char *)ncxmod_yumadir_path); if (dp == NULL) { /* create a yuma directory with 700 permissions */ retcode = mkdir((const char *)ncxmod_yumadir_path, S_IRWXU); if (retcode != 0) { res = errno_to_status(); } } else { /* use the existing yuma directory */ (void)closedir(dp); } if (res != NO_ERR) { log_error("\nError: Could not setup Yuma work directory\n"); } return res; } /* ncxmod_setup_yumadir */ /******************************************************************** * FUNCTION ncxmod_setup_tempdir * * Setup the ~/.yuma/tmp directory if it does not exist * * RETURNS: * status *********************************************************************/ status_t ncxmod_setup_tempdir (void) { const xmlChar *tmpdir = NCXMOD_TEMP_DIR; xmlChar *tempdir_path, *str; DIR *dp; status_t res; uint32 len; int retcode; res = NO_ERR; /* make the string to use for the yuma temp directory */ len = xml_strlen(ncxmod_yumadir_path) + xml_strlen(tmpdir) + 1; tempdir_path = m__getMem(len); if (tempdir_path == NULL) { return ERR_INTERNAL_MEM; } str = tempdir_path; str += xml_strcpy(str, ncxmod_yumadir_path); xml_strcpy(str, tmpdir); /* try to open yuma temp directory */ dp = opendir((const char *)tempdir_path); if (dp == NULL) { /* create a yuma temp directory with 700 permissions */ retcode = mkdir((const char *)tempdir_path, S_IRWXU); if (retcode != 0) { res = errno_to_status(); } } else { /* use the existing ~/.yuma/tmp directory */ (void)closedir(dp); } m__free(tempdir_path); return res; } /* ncxmod_setup_tempdir */ /******************************************************************** * FUNCTION ncxmod_new_program_tempdir * * Setup a program instance temp files directory * * INPUTS: * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced tempdir_progcb_t record (if NO_ERR) *********************************************************************/ ncxmod_temp_progcb_t * ncxmod_new_program_tempdir (status_t *res) { xmlChar *buffer, *tempdir_path, *p; DIR *dp; ncxmod_temp_progcb_t *progcb = NULL; xmlChar datebuff[TSTAMP_MIN_SIZE]; xmlChar numbuff[NCX_MAX_NUMLEN]; int retcode; uint32 fixedlen, numlen; if (res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } /* setup */ *res = NO_ERR; tstamp_datetime_dirname(datebuff); fixedlen = xml_strlen(ncxmod_yumadir_path) + xml_strlen(NCXMOD_TEMP_DIR); snprintf((char *)numbuff, NCX_MAX_NUMLEN, "%u", (unsigned int) getpid()); numlen = xml_strlen(numbuff); /* get a buffer for the constructed directory name */ buffer = m__getMem(TSTAMP_MIN_SIZE+fixedlen+numlen+2); if (buffer == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } /* construct the entire dir name string generate the default directory name * based on the current time + pid number */ p = buffer; p += xml_strcpy(p, ncxmod_yumadir_path); p += xml_strcpy(p, NCXMOD_TEMP_DIR); *p++ = NCXMOD_PSCHAR; p += xml_strcpy(p, datebuff); xml_strcpy(p, numbuff); /* covert this string to the expanded path name */ tempdir_path = ncx_get_source(buffer, res); m__free(buffer); if (tempdir_path == NULL) { return NULL; } /* try to open ~/.yuma/tmp/ directory */ dp = opendir((const char *)tempdir_path); if (dp == NULL) { /* create a directory with 700 permissions */ retcode = mkdir((const char *)tempdir_path, S_IRWXU); if (retcode != 0) { *res = errno_to_status(); } } else { /* error! this directory already exists, */ *res = ERR_NCX_ENTRY_EXISTS; closedir( dp ); } if (*res == NO_ERR) { progcb = new_temp_progcb(); if (progcb == NULL) { *res = ERR_INTERNAL_MEM; } else { /* transfer malloced tempdir_path here */ progcb->source = tempdir_path; tempdir_path = NULL; } } if (tempdir_path) { /* error exit */ m__free(tempdir_path); } return progcb; } /* ncxmod_new_program_tempdir */ /******************************************************************** * FUNCTION ncxmod_free_program_tempdir * * Remove a program instance temp files directory * * INPUTS: * progcb == tempoeray program instance control block to free * *********************************************************************/ void ncxmod_free_program_tempdir (ncxmod_temp_progcb_t *progcb) { #ifdef DEBUG if (progcb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif free_temp_progcb(progcb); } /* ncxmod_free_program_tempdir */ /******************************************************************** * FUNCTION ncxmod_new_session_tempdir * * Setup a session instance temp files directory * * INPUTS: * progcb == program instance control block to use * sid == manager session ID of the new session instance control block * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced tempdir_sescb_t record *********************************************************************/ ncxmod_temp_sescb_t * ncxmod_new_session_tempdir (ncxmod_temp_progcb_t *progcb, uint32 sidnum, status_t *res) { xmlChar *buffer, *p; DIR *dp; ncxmod_temp_sescb_t *sescb; xmlChar numbuff[NCX_MAX_NUMLEN]; uint32 fixedlen, numlen; int retcode; #ifdef DEBUG if (progcb == NULL || res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (sidnum == 0 || progcb->source == NULL) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif /* setup */ *res = NO_ERR; sescb = NULL; fixedlen = xml_strlen(progcb->source); snprintf((char *)numbuff, NCX_MAX_NUMLEN, "%u", sidnum); numlen = xml_strlen(numbuff); /* get a buffer for the constructed directory name */ buffer = m__getMem(fixedlen+numlen+2); if (buffer == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } /* construct the entire dir name string * generate the default directory name * based on the session number */ p = buffer; p += xml_strcpy(p, progcb->source); *p++ = NCXMOD_PSCHAR; xml_strcpy(p, numbuff); /* already in expanded path name format * try to open ~/.yuma/tmp// directory */ dp = opendir((const char *)buffer); if (dp == NULL) { /* create a directory with 700 permissions */ retcode = mkdir((const char *)buffer, S_IRWXU); if (retcode != 0) { *res = errno_to_status(); } } else { /* error! this directory already exists, */ (void) closedir( dp ); *res = ERR_NCX_ENTRY_EXISTS; } if (*res == NO_ERR) { sescb = new_temp_sescb(); if (sescb == NULL) { *res = ERR_INTERNAL_MEM; } else { /* transfer malloced buffer here */ sescb->source = buffer; buffer = NULL; sescb->sidnum = sidnum; /* store pointer to the new session for cleanup */ dlq_enque(sescb, &progcb->temp_sescbQ); } } if (buffer) { /* error exit */ m__free(buffer); } return sescb; } /* ncxmod_new_session_tempdir */ /******************************************************************** * FUNCTION ncxmod_free_session_tempdir * * Clean and free a session instance temp files directory * * INPUTS: * progcb == program instance control block to use * sidnum == manager session number to delete * *********************************************************************/ void ncxmod_free_session_tempdir (ncxmod_temp_progcb_t *progcb, uint32 sidnum) { ncxmod_temp_sescb_t *sescb; #ifdef DEBUG if (progcb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (sidnum == 0) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif for (sescb = (ncxmod_temp_sescb_t *) dlq_firstEntry(&progcb->temp_sescbQ); sescb != NULL; sescb = (ncxmod_temp_sescb_t *)dlq_nextEntry(sescb)) { if (sescb->sidnum == sidnum) { dlq_remove(sescb); free_temp_sescb(sescb); return; } } /* sid not found */ SET_ERROR(ERR_INTERNAL_VAL); } /* ncxmod_free_session_tempdir */ /******************************************************************** * FUNCTION ncxmod_new_session_tempfile * * Setup a session instance temp file for writing * * INPUTS: * sescb == session instance control block to use * filename == filename to create in the temp directory * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced tempdir_sescb_t record *********************************************************************/ ncxmod_temp_filcb_t * ncxmod_new_session_tempfile (ncxmod_temp_sescb_t *sescb, const xmlChar *filename, status_t *res) { xmlChar *buffer, *p; ncxmod_temp_filcb_t *filcb; uint32 fixedlen, filenamelen; #ifdef DEBUG if (sescb == NULL || filename == NULL || res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (sescb->source == NULL) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif /* first check if the filename is already used */ for (filcb = (ncxmod_temp_filcb_t *) dlq_firstEntry(&sescb->temp_filcbQ); filcb != NULL; filcb = (ncxmod_temp_filcb_t *)dlq_nextEntry(filcb)) { if (!xml_strcmp(filcb->filename, filename)) { log_error("\nError: cannot create temp file '%s', " "duplicate entry\n"); *res = ERR_NCX_ENTRY_EXISTS; return NULL; } } /* setup */ *res = NO_ERR; fixedlen = xml_strlen(sescb->source); filenamelen = xml_strlen(filename); /* get a buffer for the constructed file name */ buffer = m__getMem(fixedlen+filenamelen+2); if (buffer == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } p = buffer; p += xml_strcpy(p, sescb->source); *p++ = NCXMOD_PSCHAR; xml_strcpy(p, filename); /* get a new file control block to return */ filcb = new_temp_filcb(); if (filcb == NULL) { m__free(buffer); *res = ERR_INTERNAL_MEM; return NULL; } /* transfer malloced buffer here */ filcb->source = buffer; filcb->filename = p; /* store pointer to the new file for cleanup */ dlq_enque(filcb, &sescb->temp_filcbQ); return filcb; } /* ncxmod_new_session_tempfile */ /******************************************************************** * FUNCTION ncxmod_free_session_tempfile * * Clean and free a session instance temp files directory * * INPUTS: * filcb == file control block to delete * *********************************************************************/ void ncxmod_free_session_tempfile (ncxmod_temp_filcb_t *filcb) { #ifdef DEBUG if (filcb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* supposeds to be part of the sescb->temp_filcbQ !!! */ dlq_remove(filcb); free_temp_filcb(filcb); } /* ncxmod_free_session_tempfile */ /******************************************************************** * FUNCTION ncxmod_new_search_result * * Malloc and initialize a search result struct * * RETURNS: * malloced and initialized struct, NULL if ERR_INTERNAL_MEM *********************************************************************/ ncxmod_search_result_t * ncxmod_new_search_result (void) { ncxmod_search_result_t *searchresult; searchresult = m__getObj(ncxmod_search_result_t); if (searchresult == NULL) { return NULL; } memset(searchresult, 0x0, sizeof(ncxmod_search_result_t)); return searchresult; } /* ncxmod_new_search_result */ /******************************************************************** * FUNCTION ncxmod_new_search_result_ex * * Malloc and initialize a search result struct * from a module source * * INPUTS: * mod == module struct to use * * RETURNS: * malloced and initialized struct, NULL if ERR_INTERNAL_MEM *********************************************************************/ ncxmod_search_result_t * ncxmod_new_search_result_ex (const ncx_module_t *mod) { ncxmod_search_result_t *searchresult; const xmlChar *str; #ifdef DEBUG if (mod == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif searchresult = m__getObj(ncxmod_search_result_t); if (searchresult == NULL) { return NULL; } memset(searchresult, 0x0, sizeof(ncxmod_search_result_t)); searchresult->module = xml_strdup(mod->name); if (searchresult->module == NULL) { ncxmod_free_search_result(searchresult); return NULL; } if (mod->version) { searchresult->revision = xml_strdup(mod->version); if (searchresult->revision == NULL) { ncxmod_free_search_result(searchresult); return NULL; } } if (mod->ns) { searchresult->namespacestr = xml_strdup(mod->ns); if (searchresult->namespacestr == NULL) { ncxmod_free_search_result(searchresult); return NULL; } str = searchresult->namespacestr; while (*str && *str != '?') { str++; } searchresult->nslen = (uint32)(str - searchresult->namespacestr); } if (mod->source) { searchresult->source = xml_strdup(mod->source); if (searchresult->source == NULL) { ncxmod_free_search_result(searchresult); return NULL; } } if (mod->belongs) { searchresult->belongsto = xml_strdup(mod->belongs); if (searchresult->belongsto == NULL) { ncxmod_free_search_result(searchresult); return NULL; } } searchresult->ismod = mod->ismod; ncx_init_list(&searchresult->devlist, NCX_BT_STRING); return searchresult; } /* ncxmod_new_search_result_ex */ /******************************************************************** * FUNCTION ncxmod_new_search_result_str * * Malloc and initialize a search result struct * * INPUTS: * modname == module name string to use * revision == revision date to use (may be NULL) * RETURNS: * malloced and initialized struct, NULL if ERR_INTERNAL_MEM *********************************************************************/ ncxmod_search_result_t * ncxmod_new_search_result_str (const xmlChar *modname, const xmlChar *revision) { ncxmod_search_result_t *searchresult; #ifdef DEBUG if (modname == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif searchresult = m__getObj(ncxmod_search_result_t); if (searchresult == NULL) { return NULL; } memset(searchresult, 0x0, sizeof(ncxmod_search_result_t)); searchresult->module = xml_strdup(modname); if (searchresult->module == NULL) { ncxmod_free_search_result(searchresult); return NULL; } if (revision) { searchresult->revision = xml_strdup(revision); if (searchresult->revision == NULL) { ncxmod_free_search_result(searchresult); return NULL; } } searchresult->res = ERR_NCX_MOD_NOT_FOUND; ncx_init_list(&searchresult->devlist, NCX_BT_STRING); return searchresult; } /* ncxmod_new_search_result_str */ /******************************************************************** * FUNCTION ncxmod_free_search_result * * Clean and free a search result struct * * INPUTS: * searchresult == struct to clean and free *********************************************************************/ void ncxmod_free_search_result (ncxmod_search_result_t *searchresult) { #ifdef DEBUG if (searchresult == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (searchresult->module) { m__free(searchresult->module); } if (searchresult->belongsto) { m__free(searchresult->belongsto); } if (searchresult->revision) { m__free(searchresult->revision); } if (searchresult->namespacestr) { m__free(searchresult->namespacestr); } if (searchresult->source) { m__free(searchresult->source); } ncx_clean_list(&searchresult->devlist); m__free(searchresult); } /* ncxmod_free_search_result */ /******************************************************************** * FUNCTION ncxmod_clean_search_result_queue * * Clean and free all the search result structs * in the specified Q * * INPUTS: * searchQ = Q of ncxmod_search_result_t to clean and free *********************************************************************/ void ncxmod_clean_search_result_queue (dlq_hdr_t *searchQ) { ncxmod_search_result_t *searchresult; #ifdef DEBUG if (searchQ == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif while (!dlq_empty(searchQ)) { searchresult = (ncxmod_search_result_t *)dlq_deque(searchQ); ncxmod_free_search_result(searchresult); } } /* ncxmod_clean_search_result_queue */ /******************************************************************** * FUNCTION ncxmod_find_search_result * * Find a search result in the specified Q * * Either modname or nsuri must be set * If modname is set, then revision will be checked * * INPUTS: * searchQ = Q of ncxmod_search_result_t to check * modname == module or submodule name to find * revision == revision-date to find * nsuri == namespace URI fo find * RETURNS: * pointer to first matching record; NULL if not found *********************************************************************/ ncxmod_search_result_t * ncxmod_find_search_result (dlq_hdr_t *searchQ, const xmlChar *modname, const xmlChar *revision, const xmlChar *nsuri) { ncxmod_search_result_t *sr; const xmlChar *str; uint32 nslen; #ifdef DEBUG if (searchQ == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (sr = (ncxmod_search_result_t *)dlq_firstEntry(searchQ); sr != NULL; sr = (ncxmod_search_result_t *)dlq_nextEntry(sr)) { if (modname) { if (sr->module == NULL || xml_strcmp(sr->module, modname)) { continue; } if (revision) { if (sr->revision == NULL || xml_strcmp(sr->revision, revision)) { continue; } } return sr; } else if (nsuri) { str = nsuri; while (*str && *str != '?') { str++; } nslen = (uint32)(str - nsuri); if (nslen == 0) { continue; } if (sr->namespacestr == NULL || sr->nslen == 0) { continue; } if (nslen != sr->nslen) { continue; } if (xml_strncmp(sr->namespacestr, nsuri, nslen)) { continue; } return sr; } else { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } } return NULL; } /* ncxmod_find_search_result */ /******************************************************************** * FUNCTION ncxmod_clone_search_result * * Clone a search result * * INPUTS: * sr = searchresult to clone * * RETURNS: * pointer to malloced and filled in clone of sr *********************************************************************/ ncxmod_search_result_t * ncxmod_clone_search_result (const ncxmod_search_result_t *sr) { ncxmod_search_result_t *newsr; #ifdef DEBUG if (sr == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif newsr = ncxmod_new_search_result(); if (newsr == NULL) { return NULL; } if (sr->module) { newsr->module = xml_strdup(sr->module); if (newsr->module == NULL) { ncxmod_free_search_result(newsr); return NULL; } } if (sr->belongsto) { newsr->belongsto = xml_strdup(sr->belongsto); if (newsr->belongsto == NULL) { ncxmod_free_search_result(newsr); return NULL; } } if (sr->revision) { newsr->revision = xml_strdup(sr->revision); if (newsr->revision == NULL) { ncxmod_free_search_result(newsr); return NULL; } } if (sr->namespacestr) { newsr->namespacestr = xml_strdup(sr->namespacestr); if (newsr->namespacestr == NULL) { ncxmod_free_search_result(newsr); return NULL; } } if (sr->source) { newsr->source = xml_strdup(sr->source); if (newsr->source == NULL) { ncxmod_free_search_result(newsr); return NULL; } } newsr->mod = sr->mod; newsr->res = sr->res; newsr->nslen = sr->nslen; newsr->cap = sr->cap; newsr->module_val = sr->module_val; newsr->capmatch = sr->capmatch; newsr->ismod = sr->ismod; ncx_copy_list(&sr->devlist, &newsr->devlist); return newsr; } /* ncxmod_clone_search_result */ /******************************************************************** * FUNCTION ncxmod_test_filespec * * Check the exact filespec to see if it a file * * INPUTS: * filespec == file spec to check * * RETURNS: * TRUE if valid readable file * FALSE otherwise *********************************************************************/ boolean ncxmod_test_filespec (const xmlChar *filespec) { int ret; struct stat statbuf; #ifdef DEBUG if (filespec == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif memset(&statbuf, 0x0, sizeof(statbuf)); ret = stat((const char *)filespec, &statbuf); return (ret == 0 && S_ISREG(statbuf.st_mode)) ? TRUE : FALSE; } /* ncxmod_test_filespec */ /******************************************************************** * FUNCTION ncxmod_get_pathlen_from_filespec * * Get the length of the path part of the filespec string * * INPUTS: * filespec == file spec to check * * RETURNS: * number of chars to keep for the path spec *********************************************************************/ uint32 ncxmod_get_pathlen_from_filespec (const xmlChar *filespec) { const xmlChar *str; uint32 len; #ifdef DEBUG if (filespec == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif len = xml_strlen(filespec); if (len == 0) { return 0; } str = &filespec[len-1]; while (*str && *str != NCXMOD_PSCHAR) { str--; } if (*str) { return (uint32)((str - filespec) + 1); } else { return 0; } } /* ncxmod_get_pathlen_from_filespec */ /* END file ncxmod.c */ yuma123_2.14/netconf/src/ncx/xml_rd.c0000664000175000017500000000367014770023131017575 0ustar vladimirvladimir#include #include #include #include #include #include "procdefs.h" #include "dlq.h" #include "ncx.h" #include "ncx_num.h" #include "ncxconst.h" #include "obj.h" #include "ses.h" #include "status.h" #include "val.h" #include "val_util.h" #include "val_parse.h" #include "xmlns.h" #include "xml_msg.h" #include "xml_util.h" #include "xml_wr.h" #include "xpath.h" #include "xpath_wr.h" #include "xpath_yang.h" static int my_ses_read_cb (void* context, char* buffer, int len) { ses_cb_t *scb = (ses_cb_t *)context; return fread(buffer, 1, len, scb->fp); } /******************************************************************** * FUNCTION xml_rd_open_file * * Read the value for the specified obj from an open FILE in XML format * * INPUTS: * fp == open FILE control block * obj == object template for the output value * val == address of value for output * * RETURNS: * status *********************************************************************/ status_t xml_rd_open_file (FILE *fp, obj_template_t *obj, val_value_t **val) { xml_node_t top; status_t res; /* get a dummy session control block */ ses_cb_t *scb = ses_new_dummy_scb(); if (!scb) { return ERR_INTERNAL_MEM; } scb->fp = fp; res = xml_get_reader_for_session(my_ses_read_cb, NULL, scb/*context*/, &scb->reader); if(res != NO_ERR) { return res; } /* parse */ *val = val_new_value(); if(*val == NULL) { return ERR_INTERNAL_MEM; } xml_init_node(&top); res = xml_consume_node(scb->reader, &top, TRUE, TRUE); if (res != NO_ERR) { return res; } res = val_parse(scb, obj, &top, *val); scb->fp = NULL; /* skip fclose inside ses_free_scb */ ses_free_scb(scb); xml_clean_node(&top); return res; } /* xml_rd_open_file */ yuma123_2.14/netconf/src/ncx/ncx.c0000664000175000017500000060011114770023131017071 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: ncx.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30oct05 abb begun 30oct07 abb change identifier separator from '.' to '/' and change valid identifier chars to match YANG ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifdef HAS_FLOAT #include #include #endif #include #include #include #include #include #include "procdefs.h" #ifndef RELEASE #include "curversion.h" #endif #include "cfg.h" #include "cli.h" #include "def_reg.h" #include "dlq.h" #include "ext.h" #include "grp.h" #include "log.h" #include "ncx.h" #include "ncx_appinfo.h" #include "ncx_feature.h" #include "ncx_list.h" #include "ncx_num.h" #include "ncxconst.h" #include "ncxmod.h" #include "obj.h" #include "rpc.h" #include "runstack.h" #include "status.h" #include "ses_msg.h" #include "typ.h" #include "top.h" #include "val.h" #include "version.h" #include "xml_util.h" #include "xmlns.h" #include "yang.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define INV_PREFIX ((const xmlChar *)"inv") #define WILDCARD_PREFIX ((const xmlChar *)"___") #ifdef DEBUG /* this flag will cause debug4 trace statements to be printed * to the log during operation for module alloc and free operations */ /* #define NCX_DEBUG_MOD_MEMORY 1 */ /* #define WITH_TRACEFILE 1 */ #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* warning suppression entry */ typedef struct warnoff_t_ { dlq_hdr_t qhdr; status_t res; } warnoff_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /* Q of ncx_module_t * list of active modules */ static dlq_hdr_t ncx_modQ; /* pointer to the current Q of active modules */ static dlq_hdr_t *ncx_curQ; /* pointer to the current session Q of active modules * this is for yangcli to serialize access to the * database of modules */ static dlq_hdr_t *ncx_sesmodQ; /* pointer to dead modules during yangdump subtree processing */ static dlq_hdr_t deadmodQ; static boolean usedeadmodQ; /* Q of ncx_filptr_t * used as a cache of subtree filtering headers */ static dlq_hdr_t ncx_filptrQ; /* maximum number of filter cache entries */ static uint32 ncx_max_filptrs; /* current number of filter cache entries */ static uint32 ncx_cur_filptrs; /* generic anyxml object template */ static obj_template_t *gen_anyxml; /* generic container object template */ static obj_template_t *gen_container; /* generic string object template */ static obj_template_t *gen_string; /* generic empty object template */ static obj_template_t *gen_empty; /* generic root container object template */ static obj_template_t *gen_root; /* generic binary leaf object template */ static obj_template_t *gen_binary; /* module load callback function * TBD: support multiple callbacks * used when a ncxmod loads a module */ static ncx_load_cbfn_t mod_load_callback; /* module init */ static boolean ncx_init_done = FALSE; /* save descriptive clauses like description, reference */ static boolean save_descr; /* system warning length for identifiers */ static uint32 warn_idlen; /* system warning length for YANG file line length */ static uint32 warn_linelen; /* Q of warnoff_t * used to suppress warnings * from being counted in the total * only filters out the ncx_print_errormsg calls * not the log_warn text messages */ static dlq_hdr_t warnoffQ; /* pointer to the current Q of modules to use * for yangcli sessions; will be 1 per thread * later but now this works because access is * serialized and the temp_modQ is set when each * session needs to look for objects (CLI or XPath) */ static dlq_hdr_t *temp_modQ; /* default diplay mode in all programs except yangcli, * which uses a more complex schem for diplay-mode */ static ncx_display_mode_t display_mode; /* memory leak debugging vars */ uint32 malloc_cnt; uint32 free_cnt; /* use XML prefix flag */ boolean use_prefix; /* search subdirs in the CWD for modules, for default search * not YUMA_MODPATH or YUMA_HOME, or YUMA_INSTALL subdirs * this is over-ridden by the --subdirs=false parameter */ boolean cwd_subdirs; /* bitmask of protocols to match ncx_protocol_t enum */ uint32 protocols_enabled; /* flag to indicate whether ordered-by system is sorted or not */ boolean system_sorted; static FILE *tracefile; /* the variable ncx_get_vtimeout_value returns */ static uint32 ncx_vtimeout=NCX_DEF_VTIMEOUT; /* flag to force yang_parse to reject a module that has top-level * mandatory data nodes; applies to server operation */ static boolean allow_top_mandatory; /** * \fn check_moddef * \brief Check if a specified module is loaded; if not, load it. * Search the module for the data struct for the specified definition * name. * \param pcb parser control block to use * \param imp import struct to use, imp->mod may be set if not already * \param defname name of the app-specific definition to find * \param dtyp address of return definition type (for verification) * \param *dtyp NCX_NT_NONE for any match, or a specific type to find * \param dptr addres of return definition pointer * \param (output) pcb parser control block to use * \param (output) *dtyp node type found NCX_NT_OBJ or NCX_NT_TYPE, etc. * \param (output) *dptr pointer to data struct or NULL if not found * \return status */ static status_t check_moddef (yang_pcb_t *pcb, ncx_import_t *imp, const xmlChar *defname, ncx_node_t *dtyp, void **dptr) { status_t res; /* First find or load the module if it has not already been tried */ if (imp->res != NO_ERR) { return imp->res; } if (!imp->mod) { imp->mod = ncx_find_module(imp->module, imp->revision); } if (!imp->mod) { res = ncxmod_load_module(imp->module, imp->revision, pcb->savedevQ, &imp->mod); if (!imp->mod || res != NO_ERR) { return ERR_NCX_MOD_NOT_FOUND; } } /* have a module loaded that might contain this def * look for the defname * the module may be loaded with non-fatal errors */ switch (*dtyp) { case NCX_NT_TYP: *dptr = ncx_find_type(imp->mod, defname, FALSE); break; case NCX_NT_GRP: *dptr = ncx_find_grouping(imp->mod, defname, FALSE); break; case NCX_NT_OBJ: *dptr = obj_find_template(&imp->mod->datadefQ, imp->mod->name, defname); break; case NCX_NT_NONE: *dptr = ncx_find_type(imp->mod, defname, FALSE); if (*dptr) { *dtyp = NCX_NT_TYP; } if (!*dptr) { *dptr = ncx_find_grouping(imp->mod, defname, FALSE); if (*dptr) { *dtyp = NCX_NT_GRP; } } if (!*dptr) { *dptr = obj_find_template(&imp->mod->datadefQ, imp->mod->name, defname); if (*dptr) { *dtyp = NCX_NT_OBJ; } } break; default: SET_ERROR(ERR_INTERNAL_VAL); *dptr = NULL; } return (*dptr) ? NO_ERR : ERR_NCX_DEF_NOT_FOUND; } /* check_moddef */ // ----------------------------------------------------------------------------! /** * \fn set_toplevel_defs * \brief * \param mod module to check * \param nsid namespace ID of the main module being added * \return status */ static status_t set_toplevel_defs (ncx_module_t *mod, xmlns_id_t nsid) { typ_template_t *typ; grp_template_t *grp; obj_template_t *obj; ext_template_t *ext; for (typ = (typ_template_t *)dlq_firstEntry(&mod->typeQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { typ->nsid = nsid; } for (grp = (grp_template_t *)dlq_firstEntry(&mod->groupingQ); grp != NULL; grp = (grp_template_t *)dlq_nextEntry(grp)) { grp->nsid = nsid; } /* the first time this is called (for yuma-ncx) the * gen_root object has not been set and is still NULL * The 'root' object is defined in yuma-ncx. * There are no real data objects in this module. * All other modules that follow will set the parent of * top-level objects to 'gen_root' for XPath usage */ for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (obj_is_data_db(obj) || obj_is_rpc(obj) || obj_is_notif(obj)) { /* set the parent to the gen_root object so XPath expressions * that reference the root via ancestor (../../foo) will work; */ obj->parent = gen_root; } } for (ext = (ext_template_t *)dlq_firstEntry(&mod->extensionQ); ext != NULL; ext = (ext_template_t *)dlq_nextEntry(ext)) { ext->nsid = nsid; } return NO_ERR; } /* set_toplevel_defs */ // ----------------------------------------------------------------------------! /** * \fn free_module * \brief Scrub the memory in a ncx_module_t by freeing all the * sub-fields and then freeing the entire struct itself * \details MUST remove this struct from the ncx_modQ before calling * Do not need to remove module definitions from the registry * Use the ncx_remove_module function if the module was * already successfully added to the modQ and definition registry * \param mod ncx_module_t data structure to free * \return none */ static void free_module (ncx_module_t *mod) { ncx_revhist_t *revhist; ncx_import_t *import; ncx_include_t *incl; ncx_feature_t *feature; ncx_identity_t *identity; yang_stmt_t *stmt; #ifdef NCX_DEBUG_MOD_MEMORY if (LOGDEBUG3) { log_debug3("\n%p:modtrace:freemod:%s:%s", mod, (mod->ismod) ? NCX_EL_MODULE : NCX_EL_SUBMODULE, mod->name); if (usedeadmodQ) { log_debug3(" save in deadmodQ"); } } #endif if (usedeadmodQ) { dlq_enque(mod, &deadmodQ); return; } /* clear the revision Q */ while (!dlq_empty(&mod->revhistQ)) { revhist = (ncx_revhist_t *)dlq_deque(&mod->revhistQ); ncx_free_revhist(revhist); } /* clear the import Que */ while (!dlq_empty(&mod->importQ)) { import = (ncx_import_t *)dlq_deque(&mod->importQ); ncx_free_import(import); } /* clear the include Que */ while (!dlq_empty(&mod->includeQ)) { incl = (ncx_include_t *)dlq_deque(&mod->includeQ); ncx_free_include(incl); } /* clear the allinc Q , empty unless this is a mod w/ submods */ yang_clean_nodeQ(&mod->allincQ); /* clear the incchain Q , empty unless this is a mod w/ submods */ yang_clean_nodeQ(&mod->incchainQ); /* clear the type Que */ typ_clean_typeQ(&mod->typeQ); /* clear the grouping Que */ grp_clean_groupingQ(&mod->groupingQ); /* clear the datadefQ */ obj_clean_datadefQ(&mod->datadefQ); /* clear the extension Que */ ext_clean_extensionQ(&mod->extensionQ); obj_clean_deviationQ(&mod->deviationQ); ncx_clean_appinfoQ(&mod->appinfoQ); ncx_clean_typnameQ(&mod->typnameQ); yang_clean_import_ptrQ(&mod->saveimpQ); /* clear the YANG stmtQ, used for docmode only */ while (!dlq_empty(&mod->stmtQ)) { stmt = (yang_stmt_t *)dlq_deque(&mod->stmtQ); yang_free_stmt(stmt); } /* clear the YANG featureQ */ while (!dlq_empty(&mod->featureQ)) { feature = (ncx_feature_t *)dlq_deque(&mod->featureQ); ncx_free_feature(feature); } /* clear the YANG identityQ */ while (!dlq_empty(&mod->identityQ)) { identity = (ncx_identity_t *)dlq_deque(&mod->identityQ); ncx_free_identity(identity); } /* clear the name and other fields last for easier debugging */ if (mod->name) { m__free(mod->name); } if (mod->version) { m__free(mod->version); } if (mod->organization) { m__free(mod->organization); } if (mod->contact_info) { m__free(mod->contact_info); } if (mod->descr) { m__free(mod->descr); } if (mod->ref) { m__free(mod->ref); } if (mod->ismod && mod->ns) { m__free(mod->ns); } if (mod->prefix) { m__free(mod->prefix); } if (mod->xmlprefix) { m__free(mod->xmlprefix); } if (mod->source) { m__free(mod->source); } if (mod->belongs) { m__free(mod->belongs); } ncx_clean_save_deviationsQ(&mod->devmodlist); m__free(mod); } /* free_module */ // ----------------------------------------------------------------------------! /** * \fn bootstrap_cli * \brief Handle the CLI parms that need to be set even before * any other initialization has been done, or any YANG modules * have been loaded. * \details Hardwired list of bootstrap parameters * DO NOT RESET THEM FROM THE OUTPUT OF THE cli_parse function * THESE CLI PARAMS WILL STILL BE IN THE argv array * * Common CLI parameters handled as bootstrap parameters * * log-level * log-append * log * modpath * \param argc CLI argument count * \param argv array of CLI parms * \param dlevel default debug level * \param logtstamps flag for log_open, if 'log' parameter is present * \return status */ static status_t bootstrap_cli (int argc, char *argv[], log_debug_t dlevel, boolean logtstamps) { dlq_hdr_t parmQ; cli_rawparm_t *parm; char *logfilename; log_debug_t loglevel; boolean logappend; status_t res; dlq_createSQue(&parmQ); res = NO_ERR; logfilename = NULL; logappend = FALSE; loglevel = LOG_DEBUG_NONE; /* create bootstrap parm: home */ parm = cli_new_rawparm(NCX_EL_HOME); if (parm) { dlq_enque(parm, &parmQ); } else { log_error("\nError: malloc failed"); res = ERR_INTERNAL_MEM; } /* create bootstrap parm: log-level */ if (res == NO_ERR) { parm = cli_new_rawparm(NCX_EL_LOGLEVEL); if (parm) { dlq_enque(parm, &parmQ); } else { log_error("\nError: malloc failed"); res = ERR_INTERNAL_MEM; } } /* create bootstrap parm: log */ if (res == NO_ERR) { parm = cli_new_rawparm(NCX_EL_LOG); if (parm) { dlq_enque(parm, &parmQ); } else { log_error("\nError: malloc failed"); res = ERR_INTERNAL_MEM; } } /* create bootstrap parm: log-append */ if (res == NO_ERR) { parm = cli_new_empty_rawparm(NCX_EL_LOGAPPEND); if (parm) { dlq_enque(parm, &parmQ); } else { log_error("\nError: malloc failed"); res = ERR_INTERNAL_MEM; } } /* create bootstrap parm: modpath */ if (res == NO_ERR) { parm = cli_new_rawparm(NCX_EL_MODPATH); if (parm) { dlq_enque(parm, &parmQ); } else { log_error("\nError: malloc failed"); res = ERR_INTERNAL_MEM; } } /* create bootstrap parm: yuma-home */ if (res == NO_ERR) { parm = cli_new_rawparm(NCX_EL_YUMA_HOME); if (parm) { dlq_enque(parm, &parmQ); } else { log_error("\nError: malloc failed"); res = ERR_INTERNAL_MEM; } } /* check if any of these bootstrap parms are present * and process them right away; this is different than * normal CLI where all the parameters are gathered, * validated, and then invoked */ if (res == NO_ERR) { res = cli_parse_raw(argc, argv, &parmQ); if (res != NO_ERR) { log_error("\nError: bootstrap CLI failed (%s)", get_error_string(res)); } } if (res != NO_ERR) { cli_clean_rawparmQ(&parmQ); return res; } /* --home= */ parm = cli_find_rawparm(NCX_EL_HOME, &parmQ); if (parm && parm->count) { if (parm->count > 1) { log_error("\nError: Only one home parameter allowed"); res = ERR_NCX_DUP_ENTRY; } else if (parm->value) { ncxmod_set_home((const xmlChar *)parm->value); } else { log_error("\nError: no value entered for 'home' parameter"); res = ERR_NCX_INVALID_VALUE; } } /* --log-level= */ parm = cli_find_rawparm(NCX_EL_LOGLEVEL, &parmQ); if (parm && parm->count) { if (parm->count > 1) { log_error("\nError: Only one log-level parameter allowed"); res = ERR_NCX_DUP_ENTRY; } else if (parm->value) { loglevel = log_get_debug_level_enum(parm->value); if (loglevel == LOG_DEBUG_NONE) { log_error("\nError: '%s' not valid log-level", parm->value); res = ERR_NCX_INVALID_VALUE; } else { log_set_debug_level(loglevel); } } else { log_error("\nError: no value entered for " "'log-level' parameter"); res = ERR_NCX_INVALID_VALUE; } } else { log_set_debug_level(dlevel); } /* --log-append */ if (res == NO_ERR) { parm = cli_find_rawparm(NCX_EL_LOGAPPEND, &parmQ); logappend = (parm && parm->count) ? TRUE : FALSE; if (parm && parm->value) { log_error("\nError: log-append is empty parameter"); res = ERR_NCX_INVALID_VALUE; } } /* --log= */ if (res == NO_ERR) { parm = cli_find_rawparm(NCX_EL_LOG, &parmQ); if (parm && parm->count) { if (parm->count > 1) { log_error("\nError: Only one 'log' filename allowed"); res = ERR_NCX_DUP_ENTRY; } else if (parm->value) { res = NO_ERR; logfilename = (char *) ncx_get_source((const xmlChar *)parm->value, &res); if (logfilename) { res = log_open(logfilename, logappend, logtstamps); if (res != NO_ERR) { log_error("\nError: open logfile '%s' failed (%s)", logfilename, get_error_string(res)); } m__free(logfilename); } } else { log_error("\nError: no value entered for " "'log' parameter"); res = ERR_NCX_INVALID_VALUE; } } /* else use default log (stdout) */ } /* --modpath= */ if (res == NO_ERR) { parm = cli_find_rawparm(NCX_EL_MODPATH, &parmQ); if (parm && parm->count) { if (parm->count > 1) { log_error("\nError: Only one 'modpath' parameter allowed"); res = ERR_NCX_DUP_ENTRY; } else if (parm->value) { /*** VALIDATE MODPATH FIRST ***/ ncxmod_set_modpath((const xmlChar *)parm->value); } else { log_error("\nError: no value entered for " "'modpath' parameter"); res = ERR_NCX_INVALID_VALUE; } } /* else use default modpath */ } /* --yuma-home=<$YUMA_HOME> */ if (res == NO_ERR) { parm = cli_find_rawparm(NCX_EL_YUMA_HOME, &parmQ); if (parm && parm->count) { if (parm->count > 1) { log_error("\nError: Only one 'yuma-home' parameter allowed"); res = ERR_NCX_DUP_ENTRY; } else { /*** VALIDATE YUMA_HOME ***/ ncxmod_set_yuma_home((const xmlChar *)parm->value); } } /* else use default modpath */ } cli_clean_rawparmQ(&parmQ); return res; } /* bootstrap_cli */ // ----------------------------------------------------------------------------! /** * \fn add_to_modQ * \brief * \param mod == module to add to modQ * \param modQ == Q of ncx_module_t to use * \return none */ static void add_to_modQ (ncx_module_t *mod, dlq_hdr_t *modQ) { ncx_module_t *testmod; boolean done; int32 retval; done = FALSE; for (testmod = (ncx_module_t *)dlq_firstEntry(modQ); testmod != NULL && !done; testmod = (ncx_module_t *)dlq_nextEntry(testmod)) { retval = xml_strcmp(mod->name, testmod->name); if (retval == 0) { retval = yang_compare_revision_dates(mod->version, testmod->version); if (retval == 0) { if ((!mod->version && !testmod->version) || (mod->version && testmod->version)) { /* !!! adding duplicate version !!! */ log_info("\nInfo: Adding duplicate revision '%s' of " "%s module (%s)", (mod->version) ? mod->version : EMPTY_STRING, mod->name, mod->source); } testmod->defaultrev = FALSE; mod->defaultrev = TRUE; dlq_insertAhead(mod, testmod); done = TRUE; } else if (retval > 0) { testmod->defaultrev = FALSE; mod->defaultrev = TRUE; dlq_insertAhead(mod, testmod); done = TRUE; } else { mod->defaultrev = FALSE; dlq_insertAfter(mod, testmod); done = TRUE; } } else if (retval < 0) { mod->defaultrev = TRUE; dlq_insertAhead(mod, testmod); done = TRUE; } /* else keep going */ } if (!done) { mod->defaultrev = TRUE; dlq_enque(mod, modQ); } } /* add_to_modQ */ // ----------------------------------------------------------------------------! /** * \fn do_match_rpc_error * \brief Generate an error for multiple matches for 1 module * \param mod module struct to use * \param rpcname partial name string to match * \param match TRUE if match allowed, FALSE for exact match only * \return none */ static void do_match_rpc_error (ncx_module_t *mod, const xmlChar *rpcname, boolean match) { obj_template_t *rpc; uint32 len; len = 0; if (match) { len = xml_strlen(rpcname); } for (rpc = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); rpc != NULL; rpc = (obj_template_t *)dlq_nextEntry(rpc)) { if (rpc->objtype != OBJ_TYP_RPC) { continue; } if (match) { if (xml_strncmp(obj_get_name(rpc), rpcname, len)) { continue; } } else { if (xml_strcmp(obj_get_name(rpc), rpcname)) { continue; } } if (mod->version) { log_error("\n '%s:%s' from module '%s@%s'", ncx_get_mod_xmlprefix(mod), obj_get_name(rpc), obj_get_mod_name(rpc), mod->version); } else { log_error("\n '%s:%s' from module '%s'", ncx_get_mod_xmlprefix(mod), obj_get_name(rpc), obj_get_mod_name(rpc)); } } } /* do_match_rpc_error */ /************** E X T E R N A L F U N C T I O N S **********/ /** * \fn ncx_init * \brief Initialize the NCX module * \param savestr TRUE if parsed description strings that are * not needed by the agent at runtime should * be saved anyway. Converters should use this value. * * FALSE if uneeded strings should not be saved. * Embedded agents should use this value * \param dlevel desired debug output level * \param logtstamps TRUE if log should use timestamps * FALSE if not; not used unless 'log' is present * \param startmsg log_debug2 message to print before starting; * NULL if not used; * \param argc CLI argument count for bootstrap CLI * \param argv array of CLI parms for bootstrap CLI * (may be NULL to skip bootstrap CLI parameter parsing) * \return status */ status_t ncx_init (boolean savestr, log_debug_t dlevel, boolean logtstamps, const char *startmsg, int argc, char *argv[]) { ncx_module_t *mod; status_t res; xmlns_id_t nsid; if (ncx_init_done) { return NO_ERR; } malloc_cnt = 0; free_cnt = 0; status_init(); save_descr = savestr; warn_idlen = NCX_DEF_WARN_IDLEN; warn_linelen = NCX_DEF_WARN_LINELEN; ncx_feature_init(); mod_load_callback = NULL; log_set_debug_level(dlevel); /* create the module and appnode queues */ dlq_createSQue(&ncx_modQ); dlq_createSQue(&deadmodQ); ncx_curQ = &ncx_modQ; ncx_sesmodQ = NULL; dlq_createSQue(&ncx_filptrQ); dlq_createSQue(&warnoffQ); temp_modQ = NULL; display_mode = NCX_DISPLAY_MODE_PREFIX; ncx_max_filptrs = NCX_DEF_FILPTR_CACHESIZE; ncx_cur_filptrs = 0; /* no CLI parameters for these 2 parms yet */ use_prefix = FALSE; cwd_subdirs = FALSE; system_sorted = FALSE; tracefile = NULL; #ifdef WITH_TRACEFILE tracefile = fopen("tracefile.xml", "w"); #endif allow_top_mandatory = TRUE; /* check that the correct version of libxml2 is installed */ LIBXML_TEST_VERSION; /* init module library handler */ res = ncxmod_init(); if (res != NO_ERR) { return res; } /* deal with bootstrap CLI parms */ if (argv != NULL) { res = bootstrap_cli(argc, argv, dlevel, logtstamps); if (res != NO_ERR) { return res; } } if (startmsg) { log_write(startmsg); } /* init runstack script support */ runstack_init(); /* init top level msg dispatcher */ top_init(); /* initialize the definition resistry */ def_reg_init(); /* initialize the namespace registry */ xmlns_init(); ncx_init_done = TRUE; /* Initialize the INVALID namespace to help filter handling */ res = xmlns_register_ns(INVALID_URN, INV_PREFIX, NCX_MODULE, NULL, &nsid); if (res != NO_ERR) { return res; } /* Initialize the Wildcard namespace for base:1.1 filter handling */ res = xmlns_register_ns(WILDCARD_URN, WILDCARD_PREFIX, NCX_MODULE, NULL, &nsid); if (res != NO_ERR) { return res; } /* Initialize the XML namespace for NETCONF */ res = xmlns_register_ns(NC_URN, NC_PREFIX, NC_MODULE, NULL, &nsid); if (res != NO_ERR) { return res; } /* Initialize the XML namespace for YANG */ res = xmlns_register_ns(YANG_URN, YANG_PREFIX, YANG_MODULE, NULL, &nsid); if (res != NO_ERR) { return res; } /* Initialize the XML namespace for YIN */ res = xmlns_register_ns(YIN_URN, YIN_PREFIX, YIN_MODULE, NULL, &nsid); if (res != NO_ERR) { return res; } /* Initialize the XMLNS namespace for xmlns attributes */ res = xmlns_register_ns(NS_URN, NS_PREFIX, (const xmlChar *)"W3C XML Namespaces", NULL, &nsid); if (res != NO_ERR) { return res; } /* Initialize the XSD namespace for ncxdump program */ res = xmlns_register_ns(XSD_URN, XSD_PREFIX, (const xmlChar *)"W3C XML Schema Definition", NULL, &nsid); if (res != NO_ERR) { return res; } /* Initialize the XSI namespace for ncxdump program */ res = xmlns_register_ns(XSI_URN, XSI_PREFIX, (const xmlChar *)"W3C XML Schema Instance", NULL, &nsid); if (res != NO_ERR) { return res; } /* Initialize the XML namespace for xml:lang attribute support */ res = xmlns_register_ns(XML_URN, XML_PREFIX, (const xmlChar *)"W3C XML Lang Attribute", NULL, &nsid); if (res != NO_ERR) { return res; } /* Initialize the XML namespace for wd:default attribute support */ res = xmlns_register_ns(NC_WD_ATTR_URN, NC_WD_ATTR_PREFIX, (const xmlChar *)"wd:default Attribute", NULL, &nsid); if (res != NO_ERR) { return res; } /* load the basetypes into the definition registry */ res = typ_load_basetypes(); if (res != NO_ERR) { return res; } /* initialize the configuration manager */ cfg_init(); /* initialize the session message manager */ ses_msg_init(); mod = NULL; res = ncxmod_load_module(NCXMOD_NCX, NULL, NULL, &mod); if (mod == NULL) { return res; } gen_anyxml = ncx_find_object(mod, NCX_EL_ANY); if (!gen_anyxml) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } gen_container = ncx_find_object(mod, NCX_EL_STRUCT); if (!gen_container) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } gen_string = ncx_find_object(mod, NCX_EL_STRING); if (!gen_string) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } gen_empty = ncx_find_object(mod, NCX_EL_EMPTY); if (!gen_empty) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } gen_root = ncx_find_object(mod, NCX_EL_ROOT); if (!gen_root) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } gen_binary = ncx_find_object(mod, NCX_EL_BINARY); if (!gen_binary) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } return NO_ERR; } /* ncx_init */ // ----------------------------------------------------------------------------! /** * \fn ncx_cleanup * \brief cleanup NCX module * \return none */ void ncx_cleanup (void) { ncx_module_t *mod; ncx_filptr_t *filptr; warnoff_t *warnoff; if (!ncx_init_done) { return; } while (!dlq_empty(&ncx_modQ)) { mod = (ncx_module_t *)dlq_deque(&ncx_modQ); free_module(mod); } usedeadmodQ = FALSE; while (!dlq_empty(&deadmodQ)) { mod = (ncx_module_t *)dlq_deque(&deadmodQ); free_module(mod); } while (!dlq_empty(&ncx_filptrQ)) { filptr = (ncx_filptr_t *)dlq_deque(&ncx_filptrQ); m__free(filptr); } while (!dlq_empty(&warnoffQ)) { warnoff = (warnoff_t *)dlq_deque(&warnoffQ); m__free(warnoff); } gen_anyxml = NULL; gen_container = NULL; gen_string = NULL; gen_empty = NULL; gen_root = NULL; gen_binary = NULL; ncx_feature_cleanup(); typ_unload_basetypes(); xmlns_cleanup(); def_reg_cleanup(); cfg_cleanup(); ses_msg_cleanup(); top_cleanup(); runstack_cleanup(); ncxmod_cleanup(); xmlCleanupParser(); status_cleanup(); #ifdef NCX_DEBUG_MEMORY if (malloc_cnt > free_cnt) { log_error("\n*** Error: memory leak (m:%u f:%u)\n", malloc_cnt, free_cnt); } else if (malloc_cnt < free_cnt) { log_error("\n*** Error: memory corruption (m:%u f:%u)\n", malloc_cnt, free_cnt); } #endif log_close(); if (tracefile != NULL) { fclose(tracefile); } ncx_init_done = FALSE; } /* ncx_cleanup */ // ----------------------------------------------------------------------------! /** * \fn ncx_new_module * \brief Malloc and initialize the fields in a ncx_module_t * \return pointer to the malloced and initialized struct or NULL if an error */ ncx_module_t * ncx_new_module (void) { ncx_module_t *mod; mod = m__getObj(ncx_module_t); if (!mod) { return NULL; } #ifdef NCX_DEBUG_MOD_MEMORY if (LOGDEBUG3) { log_debug3("\n%p:modtrace:newmod", mod); } #endif (void)memset(mod, 0x0, sizeof(ncx_module_t)); mod->langver = NCX_YANG_VERSION10; mod->defaultrev = TRUE; dlq_createSQue(&mod->revhistQ); dlq_createSQue(&mod->importQ); dlq_createSQue(&mod->includeQ); dlq_createSQue(&mod->allincQ); dlq_createSQue(&mod->incchainQ); dlq_createSQue(&mod->typeQ); dlq_createSQue(&mod->groupingQ); dlq_createSQue(&mod->datadefQ); dlq_createSQue(&mod->extensionQ); dlq_createSQue(&mod->deviationQ); dlq_createSQue(&mod->appinfoQ); dlq_createSQue(&mod->typnameQ); dlq_createSQue(&mod->saveimpQ); dlq_createSQue(&mod->stmtQ); dlq_createSQue(&mod->featureQ); dlq_createSQue(&mod->identityQ); dlq_createSQue(&mod->devmodlist); return mod; } /* ncx_new_module */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_module * \brief Find an ncx_module_t in the ncx_modQ; These are the modules that are * already loaded* * \param modname module name * \param revision module revision date * \return module pointer if found or NULL if not */ ncx_module_t * ncx_find_module (const xmlChar *modname, const xmlChar *revision) { ncx_module_t *mod; assert ( modname && " param modname is NULL" ); /* check the yangcli session module Q * and then the current module Q */ mod = NULL; if (ncx_sesmodQ) { mod = ncx_find_module_que(ncx_sesmodQ, modname, revision); } if (mod == NULL) { mod = ncx_find_module_que(ncx_curQ, modname, revision); } return mod; } /* ncx_find_module */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_module_que * \brief Find a ncx_module_t in the specified Q and check the namespace ID * \param modQ module Q to search * \param modname module name * \param revision module revision date * \return module pointer if found or NULL if not */ ncx_module_t * ncx_find_module_que (dlq_hdr_t *modQ, const xmlChar *modname, const xmlChar *revision) { ncx_module_t *mod; int32 retval; ncx_include_t *inc; assert ( modQ && " param modQ is NULL" ); assert ( modname && " param modname is NULL" ); for (mod = (ncx_module_t *)dlq_firstEntry(modQ); mod != NULL; mod = (ncx_module_t *)dlq_nextEntry(mod)) { retval = xml_strcmp(modname, mod->name); if (retval == 0) { if (!revision || !mod->version) { if (mod->defaultrev) { return mod; } } else { retval = yang_compare_revision_dates(revision, mod->version); if (retval == 0) { return mod; } else if (retval > 0) { return NULL; } } } else if (retval < 0) { return NULL; } /* check for submodule match */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { ncx_module_t *submod = inc->submod; retval = xml_strcmp(modname, submod->name); if (retval == 0) { if (!revision || !submod->version) { if (submod->defaultrev) { return submod; } } else { retval = yang_compare_revision_dates(revision, submod->version); if (retval == 0) { return submod; } else if (retval > 0) { return NULL; } } } } } return NULL; } /* ncx_find_module_que */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_module_que_nsid * \brief Find a ncx_module_t in the specified Q and check the namespace ID * \param modQ module Q to search * \param nsid xmlns ID to find * \return module pointer if found or NULL if not */ ncx_module_t * ncx_find_module_que_nsid (dlq_hdr_t *modQ, xmlns_id_t nsid) { ncx_module_t *mod; assert ( modQ && " param modQ is NULL" ); assert ( nsid && " param nsid is NULL" ); for (mod = (ncx_module_t *)dlq_firstEntry(modQ); mod != NULL; mod = (ncx_module_t *)dlq_nextEntry(mod)) { if (mod->nsid == nsid) { return mod; } } return NULL; } /* ncx_find_module_que_nsid */ // ----------------------------------------------------------------------------! /** * \fn ncx_free_module * \brief Scrub the memory in a ncx_module_t by freeing all the sub-fields and * then freeing the entire struct itself use if module was not added to registry * \details MUST remove this struct from the ncx_modQ before calling Does not * remove module definitions from the registry * Use the ncx_remove_module function if the module was * already successfully added to the modQ and definition registry * \param mod == ncx_module_t data structure to free * \return none */ void ncx_free_module (ncx_module_t *mod) { assert ( mod && " param mod is NULL" ); free_module(mod); } /* ncx_free_module */ // ----------------------------------------------------------------------------! /** * \fn ncx_any_mod_errors * \brief Check if any of the loaded modules are loaded with non-fatal errors * \return TRUE if any modules are loaded with non-fatal errors; FALSE if all * modules present have a status of NO_ERR */ boolean ncx_any_mod_errors (void) { ncx_module_t *mod; for (mod = (ncx_module_t *)dlq_firstEntry(ncx_curQ); mod != NULL; mod = (ncx_module_t *)dlq_nextEntry(mod)) { if (mod->status != NO_ERR) { return TRUE; } } return FALSE; } /* ncx_any_mod_errors */ // ----------------------------------------------------------------------------! /** * \fn ncx_any_dependency_errors * \brief Check if any of the imports that this module relies on were loadeds * are loaded with non-fatal errors * \param mod module to check * \return TRUE if any modules are loaded with non-fatal errors; FALSE if all * modules present have a status of NO_ERR */ boolean ncx_any_dependency_errors (const ncx_module_t *mod) { assert ( mod && " param mod is NULL" ); const yang_import_ptr_t *impptr = (const yang_import_ptr_t *)dlq_firstEntry(&mod->saveimpQ); for (; impptr != NULL; impptr = (const yang_import_ptr_t *) dlq_nextEntry(impptr)) { /*** hack: skip ietf-netconf because it is not stored *** remove when ietf-netconf is supported ***/ if (!xml_strcmp(impptr->modname, NCXMOD_IETF_NETCONF)) { continue; } ncx_module_t *testmod = ncx_find_module(impptr->modname, impptr->revision); if (!testmod) { /* missing import */ return TRUE; } if (testmod->errors) { return TRUE; } } return FALSE; } /* ncx_any_dependency_errors */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_type * \brief Check if a typ_template_t in the mod->typeQ * \param mod == ncx_module to check * \param typname type name * \param useall TRUE to use all submodules * FALSE to only use the ones in the mod->includeQ * \return pointer to struct if present, NULL otherwise */ typ_template_t * ncx_find_type (ncx_module_t *mod, const xmlChar *typname, boolean useall) { assert ( mod && " param mod is NULL" ); assert ( typname && " param typname is NULL" ); typ_template_t *typ; yang_node_t *node; ncx_include_t *inc; dlq_hdr_t *que; typ = ncx_find_type_que(&mod->typeQ, typname); if (typ) { return typ; } que = ncx_get_allincQ(mod); if (useall) { for (node = (yang_node_t *)dlq_firstEntry(que); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { typ = ncx_find_type_que(&node->submod->typeQ, typname); if (typ) { return typ; } } } } else { /* check all the submodules, but only the ones visible * to this module or submodule, YANG only */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { /* get the real submodule struct */ if (!inc->submod) { node = yang_find_node(que, inc->submodule, inc->revision); if (node) { inc->submod = node->submod; } if (!inc->submod) { /* include not found or errors in it */ continue; } } /* check the type Q in this submodule */ typ = ncx_find_type_que(&inc->submod->typeQ, typname); if (typ) { return typ; } } } return NULL; } /* ncx_find_type */ /******************************************************************** * FUNCTION ncx_find_type_que * * Check if a typ_template_t in the mod->typeQ * * INPUTS: * que == type Q to check * typname == type name * * RETURNS: * pointer to struct if present, NULL otherwise *********************************************************************/ typ_template_t * ncx_find_type_que (const dlq_hdr_t *typeQ, const xmlChar *typname) { typ_template_t *typ; assert ( typeQ && " param typeQ is NULL"); assert ( typname && " param typname is NULL"); for (typ = (typ_template_t *)dlq_firstEntry(typeQ); typ != NULL; typ = (typ_template_t *)dlq_nextEntry(typ)) { if (typ->name && !xml_strcmp(typ->name, typname)) { return typ; } } return NULL; } /* ncx_find_type_que */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_grouping * \brief Check if a grp_template_t in the mod->groupingQ * \param mod ncx_module to check * \param grpname group name * \param useall TRUE to check all existing nodes * FALSE to only use includes visible to this [sub]mod * \return pointer to struct if present, NULL otherwise */ grp_template_t * ncx_find_grouping (ncx_module_t *mod, const xmlChar *grpname, boolean useall) { grp_template_t *grp; yang_node_t *node; ncx_include_t *inc; dlq_hdr_t *que; assert ( mod && " param mod is NULL" ); assert ( grpname && " param grpname is NULL" ); /* check the main module */ grp = ncx_find_grouping_que(&mod->groupingQ, grpname); if (grp) { return grp; } que = ncx_get_allincQ(mod); if (useall) { for (node = (yang_node_t *)dlq_firstEntry(que); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { grp = ncx_find_grouping_que(&node->submod->groupingQ, grpname); if (grp) { return grp; } } } } else { /* check all the submodules, but only the ones visible * to this module or submodule, YANG only */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { /* get the real submodule struct */ if (!inc->submod) { node = yang_find_node(que, inc->submodule, inc->revision); if (node) { inc->submod = node->submod; } if (!inc->submod) { /* include not found or errors in it */ continue; } } /* check the grouping Q in this submodule */ grp = ncx_find_grouping_que(&inc->submod->groupingQ, grpname); if (grp) { return grp; } } } return NULL; } /* ncx_find_grouping */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_grouping_que * \brief Check if a grp_template_t in the specified Q * \param groupingQ Queue of grp_template_t to check * \param grpname group name * \return pointer to struct if present, NULL otherwise */ grp_template_t * ncx_find_grouping_que (const dlq_hdr_t *groupingQ, const xmlChar *grpname) { grp_template_t *grp; assert ( groupingQ && " param groupingQ is NULL" ); assert ( grpname && " param grpname is NULL" ); for (grp = (grp_template_t *)dlq_firstEntry(groupingQ); grp != NULL; grp = (grp_template_t *)dlq_nextEntry(grp)) { if (grp->name && !xml_strcmp(grp->name, grpname)) { return grp; } } return NULL; } /* ncx_find_grouping_que */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_rpc * \brief Check if a rpc_template_t in the mod->rpcQ * \param mod ncx_module to check * \param rpcname RPC name * \return pointer to struct if present, NULL otherwise */ obj_template_t * ncx_find_rpc (const ncx_module_t *mod, const xmlChar *rpcname) { obj_template_t *rpc; assert ( mod && " param mod is NULL" ); assert ( rpcname && " param rpcname is NULL" ); for (rpc = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); rpc != NULL; rpc = (obj_template_t *)dlq_nextEntry(rpc)) { if (rpc->objtype == OBJ_TYP_RPC) { if (!xml_strcmp(obj_get_name(rpc), rpcname)) { return rpc; } } } return NULL; } /* ncx_find_rpc */ // ----------------------------------------------------------------------------! /** * \fn ncx_match_rpc * \brief Check if a rpc_template_t is in the mod->rpcQ; Partial match the * commmand name * \param mod ncx_module to check * \param rpcname RPC name to match * \param retcount address of return match count; updated with * number of matches found * \return pointer to struct if present, NULL otherwise */ obj_template_t * ncx_match_rpc (const ncx_module_t *mod, const xmlChar *rpcname, uint32 *retcount) { obj_template_t *rpc, *firstfound; uint32 len, cnt; assert ( mod && " param mod is NULL" ); assert ( rpcname && " param rpcname is NULL" ); assert ( retcount && " param retcount is NULL" ); *retcount = 0; cnt = 0; firstfound = NULL; len = xml_strlen(rpcname); for (rpc = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); rpc != NULL; rpc = (obj_template_t *)dlq_nextEntry(rpc)) { if (rpc->objtype == OBJ_TYP_RPC) { if (!xml_strncmp(obj_get_name(rpc), rpcname, len)) { if (firstfound == NULL) { firstfound = rpc; } cnt++; } } } *retcount = cnt; return firstfound; } /* ncx_match_rpc */ // ----------------------------------------------------------------------------! /** * \fn ncx_match_any_rpc * \brief Check if a rpc_template_t is in any module that matches the rpc name * string * \param module module name to check (NULL == check all) * \param rpcname RPC name to match * \param retcount address of return count of matches; set to * number of matches * \return pointer to struct if present, NULL otherwise */ obj_template_t * ncx_match_any_rpc (const xmlChar *module, const xmlChar *rpcname, uint32 *retcount) { obj_template_t *rpc, *firstfound; ncx_module_t *mod; uint32 cnt, tempcnt; assert ( rpcname && " param rpcname is NULL" ); assert ( retcount && " param retcount is NULL" ); firstfound = NULL; *retcount = 0; if (module) { mod = ncx_find_module(module, NULL); if (mod) { firstfound = ncx_match_rpc(mod, rpcname, retcount); } } else { cnt = 0; for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { tempcnt = 0; rpc = ncx_match_rpc(mod, rpcname, &tempcnt); if (rpc) { if (firstfound == NULL) { firstfound = rpc; } cnt += tempcnt; } } *retcount = cnt; } return firstfound; } /* ncx_match_any_rpc */ // ----------------------------------------------------------------------------! /** * \fn ncx_match_any_rpc_mod * \brief Check if a rpc_template_t is in the specified module * \param mod module struct to check * \param rpcname RPC name to match * \param retcount address of return count of matches * \return pointer to struct if present, NULL otherwise */ obj_template_t * ncx_match_any_rpc_mod (ncx_module_t *mod, const xmlChar *rpcname, uint32 *retcount) { assert ( mod && " param mod is NULL" ); assert ( rpcname && " param rpcname is NULL" ); assert ( retcount && " param retcount is NULL" ); *retcount = 0; obj_template_t *firstfound = ncx_match_rpc(mod, rpcname, retcount); return firstfound; } /* ncx_match_any_rpc_mod */ // ----------------------------------------------------------------------------! /** * \fn ncx_match_rpc_error * \brief Generate an error for multiple matches * \param mod module struct to check (may be NULL) * \param modname module name if mod not set * NULL to match all modules * \param rpcname RPC name to match * \param match TRUE to match partial command names * FALSE for exact match only * \param firstmsg TRUE to do the first log_error banner msg * FALSE to skip this step * \return none */ void ncx_match_rpc_error (ncx_module_t *mod, const xmlChar *modname, const xmlChar *rpcname, boolean match, boolean firstmsg) { assert ( rpcname && " param rpcname is NULL" ); if (firstmsg) { if (match) { log_error("\nError: Ambiguous partial command name: '%s'", rpcname); } else { log_error("\nError: Ambiguous command name: '%s'", rpcname); } } if (mod != NULL) { do_match_rpc_error(mod, rpcname, match); } else if (modname != NULL) { mod = ncx_find_module(modname, NULL); if (mod != NULL) { do_match_rpc_error(mod, rpcname, match); } } else { for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { do_match_rpc_error(mod, rpcname, match); } } } /* ncx_match_rpc_error */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_any_object * \brief Check if an obj_template_t in in any module that * matches the object name string * \param objname object name to match * \return pointer to struct if present, NULL otherwise */ obj_template_t * ncx_find_any_object (const xmlChar *objname) { assert ( objname && " param objname is NULL" ); obj_template_t *obj = NULL; ncx_module_t *mod = NULL; boolean useses = FALSE; if (ncx_sesmodQ != NULL) { mod = (ncx_module_t *)dlq_firstEntry(ncx_sesmodQ); if (mod != NULL) { useses = TRUE; } } if (mod == NULL) { mod = ncx_get_first_module(); } for (; mod != NULL; mod = ncx_get_next_module(mod)) { obj = obj_find_template_top(mod, ncx_get_modname(mod), objname); if (obj) { return obj; } } if (useses) { /* make 1 more loop trying the main moduleQ */ for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { obj = obj_find_template_top(mod, ncx_get_modname(mod), objname); if (obj) { return obj; } } } return NULL; } /* ncx_find_any_object */ // ----------------------------------------------------------------------------! /** * \fn ncx_match_any_object * \brief Check if an obj_template_t in in any module that * matches the object name string * \param objname object name to match * \param name_match name match mode enumeration * \param alt_names TRUE if alternate names should be checked * after regular names; FALSE if not * \param retres address of return status * \return pointer to struct if present, NULL otherwise */ obj_template_t * ncx_match_any_object (const xmlChar *objname, ncx_name_match_t name_match, boolean alt_names, status_t *retres) { assert ( objname && " param objname is NULL" ); obj_template_t *obj = NULL; ncx_module_t *mod = NULL; boolean useses = FALSE; /* find a queue of modules to use; get first entry */ if (ncx_sesmodQ != NULL) { mod = (ncx_module_t *)dlq_firstEntry(ncx_sesmodQ); if (mod != NULL) { useses = TRUE; } } if (mod == NULL) { mod = ncx_get_first_module(); } /* always check exact match first */ for (; mod != NULL; mod = ncx_get_next_module(mod)) { obj = obj_find_template_top_ex(mod, ncx_get_modname(mod), objname, name_match, alt_names, TRUE, /* dataonly */ retres); if (obj) { return obj; } if (*retres == ERR_NCX_MULTIPLE_MATCHES) { return NULL; } } if (useses) { /* make 1 more loop trying the main moduleQ */ for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { obj = obj_find_template_top_ex(mod, ncx_get_modname(mod), objname, name_match, alt_names, TRUE, /* dataonly */ retres); if (obj) { return obj; } if (*retres == ERR_NCX_MULTIPLE_MATCHES) { return NULL; } } } return NULL; } /* ncx_match_any_object */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_any_object_que * \brief Check if an obj_template_t in in any module that * matches the object name string * \param modQ Q of modules to check * \param objname object name to match * \return pointer to struct if present, NULL otherwise */ obj_template_t * ncx_find_any_object_que (dlq_hdr_t *modQ, const xmlChar *objname) { assert ( modQ && " param modQ is NULL" ); assert ( objname && " param objname is NULL" ); obj_template_t *obj = NULL; ncx_module_t *mod; for (mod = (ncx_module_t *)dlq_firstEntry(modQ); mod != NULL; mod = (ncx_module_t *)dlq_nextEntry(mod)) { obj = obj_find_template_top(mod, ncx_get_modname(mod), objname); if (obj) { return obj; } } return NULL; } /* ncx_find_any_object_que */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_object * \brief Find a top level module object * \param mod ncx_module to check * \param typname type name * \return pointer to struct if present, NULL otherwise */ obj_template_t * ncx_find_object (ncx_module_t *mod, const xmlChar *objname) { assert ( mod && " param mod is NULL" ); assert ( objname && " param objname is NULL" ); return obj_find_template_top(mod, mod->name, objname); } /* ncx_find_object */ // ----------------------------------------------------------------------------! /** * \fn ncx_add_namespace_to_registry * \brief Add the namespace and prefix to the registry * or retrieve it if already set * \param mod module to add to registry * \param tempmod TRUE if this is a temporary add mode * FALSE if this is a real registry add * \return status of the operation */ status_t ncx_add_namespace_to_registry (ncx_module_t *mod, boolean tempmod) { assert ( mod && " param mod is NULL" ); xmlns_t *ns; const xmlChar *modname; status_t res; xmlns_id_t nsid; boolean isnetconf; if (!mod->ismod) { return NO_ERR; } if (mod->ns == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } res = NO_ERR; isnetconf = FALSE; /* if this is the XSD module, then use the NS ID already registered */ if (!xml_strcmp(mod->name, NCX_EL_XSD)) { mod->nsid = xmlns_xs_id(); } else if (!xml_strcmp(mod->name, (const xmlChar *)"ietf-netconf")) { mod->nsid = xmlns_nc_id(); isnetconf = TRUE; } else { mod->nsid = xmlns_find_ns_by_module(mod->name); } if (!tempmod) { /* check module prefix collision */ nsid = xmlns_find_ns_by_prefix(mod->prefix); if (nsid) { modname = xmlns_get_module(nsid); if (xml_strcmp(mod->name, modname) && !isnetconf) { if (ncx_warning_enabled(ERR_NCX_DUP_PREFIX)) { log_warn("\nWarning: prefix '%s' already in use " "by module '%s'", mod->prefix, modname); ncx_print_errormsg(NULL, mod, ERR_NCX_DUP_PREFIX); } else { ncx_inc_warnings(mod); } /* redo the module xmlprefix, the length is the length of the * prefix + 6 characters for the suffix value and a NULL */ uint32 buflen = xml_strlen(mod->prefix) + 6; xmlChar *buffer = m__getMem(buflen + 6); if (!buffer) { return ERR_INTERNAL_MEM; } xmlChar* p = buffer; p += xml_strcpy(p, mod->prefix); /* keep adding numbers to end of prefix until * 1 is unused or run out of numbers */ uint32 i = 1; for ( ; i<10000 && nsid; ++i) { snprintf((char *)p, 6, "%u", i); nsid = xmlns_find_ns_by_prefix(buffer); } if (nsid) { log_error("\nError: could not assign module prefix"); res = ERR_NCX_OPERATION_FAILED; ncx_print_errormsg(NULL, mod, res); m__free(buffer); return res; } /* else the current buffer contains an unused prefix */ mod->xmlprefix = buffer; } } } ns = def_reg_find_ns(mod->ns); if (ns) { if (tempmod) { mod->nsid = ns->ns_id; } else if (isnetconf) { /* ignore the hack corner-case for yuma-netconf */ ; } else if (ns->ns_id == xmlns_xs_id() || ns->ns_id == xmlns_xsi_id() || ns->ns_id == xmlns_xml_id()) { /* ignore these special XML duplicates used by yangdump */ ; } else if (xml_strcmp(mod->name, ns->ns_module) && xml_strcmp(ns->ns_module, NCX_MODULE)) { /* this NS string already registered to another module */ log_error("\nError: Module '%s' registering " "duplicate namespace '%s'\n " "registered by module '%s'", mod->name, mod->ns, ns->ns_module); res = ERR_DUP_NS; } else { /* same owner so okay */ mod->nsid = ns->ns_id; } } else if (!isnetconf) { res = xmlns_register_ns(mod->ns, (mod->xmlprefix) ? mod->xmlprefix : mod->prefix, mod->name, (tempmod) ? NULL : mod, &mod->nsid); if (res != NO_ERR) { /* this NS registration failed */ log_error("\nncx reg: Module '%s' registering " "namespace '%s' failed (%s)", mod->name, mod->ns, get_error_string(res)); return res; } } return res; } /* ncx_add_namespace_to_registry */ // ----------------------------------------------------------------------------! /** * \fn ncx_add_to_registry * \brief Add all the definitions stored in an ncx_module_t to the registry * registry This step is deferred to keep the registry stable as possible * and only add modules in an all-or-none fashion. * \param mod module to add to registry * \return status of the operation */ status_t ncx_add_to_registry (ncx_module_t *mod) { assert ( mod && " param mod is NULL" ); if (!mod->ismod) { return NO_ERR; } status_t res = NO_ERR; /* check module parse code */ if (mod->status != NO_ERR) { res = mod->status; if (NEED_EXIT(res)) { /* should not happen */ log_error("\nError: cannot add module '%s' to registry" " with fatal errors", mod->name); ncx_print_errormsg(NULL, mod, res); return SET_ERROR(ERR_INTERNAL_VAL); } else { log_warn("\nWarning: Adding module '%s' to registry" " with errors", mod->name); res = NO_ERR; } } res = set_toplevel_defs(mod, mod->nsid); if (res != NO_ERR) { return res; } /* add all the submodules included in this module */ yang_node_t *node; for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { node->submod->nsid = mod->nsid; res = set_toplevel_defs(node->submod, mod->nsid); if (res != NO_ERR) { return res; } } /* add the module itself for fast lookup in imports * of other modules */ if (mod->ismod) { /* save the module in the module Q */ add_to_modQ(mod, ncx_curQ); mod->added = TRUE; /* !!! hack to cleanup after xmlns init cycle !!! * check for netconf.yang or ncx.yang and back-fill * all the xmlns entries for those modules with the * real module pointer */ if (!xml_strcmp(mod->name, NC_MODULE)) { xmlns_set_modptrs(NC_MODULE, mod); } else if (!xml_strcmp(mod->name, NCX_MODULE)) { xmlns_set_modptrs(NCX_MODULE, mod); } if (mod_load_callback) { (*mod_load_callback)(mod); } } return res; } /* ncx_add_to_registry */ // ----------------------------------------------------------------------------! /** * \fn ncx_add_to_modQ * \brief Add module to the current module Q * Used by yangdiff to bypass add_to_registry to support * N different module trees * \param mod module to add to current module Q * \return status of the operation */ status_t ncx_add_to_modQ (ncx_module_t *mod) { assert ( mod && " param mod is NULL" ); if (mod->ismod) { add_to_modQ(mod, ncx_curQ); mod->added = TRUE; } return NO_ERR; } /* ncx_add_to_modQ */ // ----------------------------------------------------------------------------! /** * \fn ncx_is_duplicate * \brief Search the specific module for the specified definition name. * This function is for modules in progress which have not been * added to the registry yet. * \param mod ncx_module_t to check * \param defname name of definition to find * \return TRUE if found, FALSE otherwise */ boolean ncx_is_duplicate (ncx_module_t *mod, const xmlChar *defname) { assert ( mod && " param mod is NULL" ); assert ( defname && " param defname is NULL" ); if (ncx_find_type(mod, defname, TRUE)) { return TRUE; } if (ncx_find_rpc(mod, defname)) { return TRUE; } return FALSE; } /* ncx_is_duplicate */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_first_module * \brief Get the first module in the ncx_modQ * \return pointer to the first entry or NULL if empty Q */ ncx_module_t * ncx_get_first_module (void) { ncx_module_t *mod = (ncx_module_t *)dlq_firstEntry(ncx_curQ); while (mod) { if (mod->defaultrev) { return mod; } mod = (ncx_module_t *)dlq_nextEntry(mod); } return mod; } /* ncx_get_first_module */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_next_module * \brief Get the next module in the ncx_modQ * \param mod current module to find next * \return pointer to the first entry or NULL if empty Q */ ncx_module_t * ncx_get_next_module (const ncx_module_t *mod) { assert ( mod && " param mod is NULL" ); ncx_module_t *nextmod = (ncx_module_t *)dlq_nextEntry(mod); while (nextmod) { if (nextmod->defaultrev) { return nextmod; } nextmod = (ncx_module_t *)dlq_nextEntry(nextmod); } return nextmod; } /* ncx_get_next_module */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_first_session_module * \brief Get the first module in the ncx_sesmodQ * \return pointer to the first entry or NULL if empty Q */ ncx_module_t * ncx_get_first_session_module (void) { if (ncx_sesmodQ == NULL) { return NULL; } ncx_module_t *mod = (ncx_module_t *)dlq_firstEntry(ncx_sesmodQ); return mod; } /* ncx_get_first_session_module */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_next_session_module * \brief Get the next module in the ncx_sesmodQ * \param mod module to get next session * \return pointer to the first entry or NULL if empty Q */ ncx_module_t * ncx_get_next_session_module (const ncx_module_t *mod) { assert ( mod && " param mod is NULL" ); ncx_module_t *nextmod = (ncx_module_t *)dlq_nextEntry(mod); return nextmod; } /* ncx_get_next_session_module */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_modname * \brief Get the main module name * \param mod module or submodule to get main module name * \return main module name or NULL if error */ const xmlChar * ncx_get_modname (const ncx_module_t *mod) { assert ( mod && " param mod is NULL" ); return (mod->ismod) ? mod->name : mod->belongs; } /* ncx_get_modname */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_mod_nsid * \brief Get the main module namespace ID * \param mod module or submodule to get main module namespace ID * \return namespace id number */ xmlns_id_t ncx_get_mod_nsid (const ncx_module_t *mod) { assert ( mod && " param mod is NULL" ); while (mod->parent != NULL) { mod = mod->parent; } return mod->nsid; } /* ncx_get_mod_nsid */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_modversion * \brief Get the [sub]module version * \param mod module or submodule to get module version * \return module version or NULL if error */ const xmlChar * ncx_get_modversion (const ncx_module_t *mod) { assert ( mod && " param mod is NULL"); return mod->version; } /* ncx_get_modversion */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_modnamespace * \brief Get the module namespace URI * \param mod module or submodule to get module namespace * \return module namespace or NULL if error */ const xmlChar * ncx_get_modnamespace (const ncx_module_t *mod) { assert ( mod && " param mod is NULL"); return mod->ns; } /* ncx_get_modnamespace */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_modsource * \brief Get the module filespec source string * \param mod module or submodule to use * \return module filespec source string */ const xmlChar * ncx_get_modsource (const ncx_module_t *mod) { assert ( mod && " param mod is NULL"); return mod->source; } /* ncx_get_modsource */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_mainmod * \brief Get the main module * \param mod submodule to get main module * \return main module NULL if error */ ncx_module_t * ncx_get_mainmod (ncx_module_t *mod) { assert ( mod && " param mod is NULL"); if (mod->ismod) { return mod; } /**** DO NOT KNOW THE REAL MAIN MODULE REVISION ****/ return ncx_find_module(mod->belongs, NULL); } /* ncx_get_mainmod */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_first_object * \brief Get the first object in the datadefQs for the specified module * Get any object with a name * \param mod module to search for the first object * \return pointer to the first object or NULL if empty Q */ obj_template_t * ncx_get_first_object (ncx_module_t *mod) { assert ( mod && " param mod is NULL"); obj_template_t *obj; for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || !obj_is_enabled(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } return obj; } if (!mod->ismod) { return NULL; } yang_node_t *node; for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (!node->submod) { SET_ERROR(ERR_INTERNAL_PTR); continue; } for (obj = (obj_template_t *) dlq_firstEntry(&node->submod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || !obj_is_enabled(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } return obj; } } return NULL; } /* ncx_get_first_object */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_next_object * \brief Get the next object in the specified module * Get any object with a name * \param mod module struct to get the next object from * \param curobj pointer to the current object to get the next for * \return pointer to the next object or NULL if none */ obj_template_t * ncx_get_next_object (ncx_module_t *mod, obj_template_t *curobj) { assert ( mod && " param mod is NULL" ); assert ( curobj && " param curobj is NULL" ); obj_template_t *obj; for (obj = (obj_template_t *)dlq_nextEntry(curobj); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || !obj_is_enabled(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } return obj; } boolean start = (curobj->tkerr.mod == mod) ? TRUE : FALSE; if (!mod->ismod) { return NULL; } yang_node_t *node; for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (!node->submod) { SET_ERROR(ERR_INTERNAL_PTR); continue; } if (!start) { if (node->submod == curobj->tkerr.mod) { start = TRUE; } continue; } for (obj = (obj_template_t *) dlq_firstEntry(&node->submod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || !obj_is_enabled(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } return obj; } } return NULL; } /* ncx_get_next_object */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_first_data_object * \brief Get the first database object in the datadefQs * for the specified module * \param mod module to search for the first object * \return pointer to the first object or NULL if empty Q */ obj_template_t * ncx_get_first_data_object (ncx_module_t *mod) { assert ( mod && " param mod is NULL"); obj_template_t *obj; for (obj = (obj_template_t *)dlq_firstEntry(&mod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || !obj_is_enabled(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } if (obj_is_data_db(obj)) { return obj; } } if (!mod->ismod) { return NULL; } yang_node_t *node; for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (!node->submod) { SET_ERROR(ERR_INTERNAL_PTR); continue; } for (obj = (obj_template_t *) dlq_firstEntry(&node->submod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || !obj_is_enabled(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } if (obj_is_data_db(obj)) { return obj; } } } return NULL; } /* ncx_get_first_data_object */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_next_data_object * \brief Get the next database object in the specified module * \param mod pointer to module to get object from * \param curobj pointer to current object to get next from * \return pointer to the next object or NULL if none */ obj_template_t * ncx_get_next_data_object (ncx_module_t *mod, obj_template_t *curobj) { assert ( mod && " param mod is NULL"); obj_template_t *obj; for (obj = (obj_template_t *)dlq_nextEntry(curobj); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || !obj_is_enabled(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } if (obj_is_data_db(obj)) { return obj; } } if (!mod->ismod) { return NULL; } boolean start = (curobj->tkerr.mod == mod) ? TRUE : FALSE; yang_node_t *node; for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (!node->submod) { SET_ERROR(ERR_INTERNAL_PTR); continue; } if (!start) { if (node->submod == curobj->tkerr.mod) { start = TRUE; } continue; } for (obj = (obj_template_t *) dlq_firstEntry(&node->submod->datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || !obj_is_enabled(obj) || obj_is_cli(obj) || obj_is_abstract(obj)) { continue; } if (obj_is_data_db(obj)) { return obj; } } } return NULL; } /* ncx_get_next_data_object */ // ----------------------------------------------------------------------------! /** * \fn ncx_new_import * \brief Malloc and initialize the fields in a ncx_import_t * \return pointer to the malloced and initialized struct or NULL if an error */ ncx_import_t * ncx_new_import (void) { ncx_import_t *import = m__getObj(ncx_import_t); if (!import) { return NULL; } (void)memset(import, 0x0, sizeof(ncx_import_t)); dlq_createSQue(&import->appinfoQ); return import; } /* ncx_new_import */ // ----------------------------------------------------------------------------! /** * \fn ncx_free_import * \brief Scrub the memory in a ncx_import_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * \param import ncx_import_t data structure to free * \return none */ void ncx_free_import (ncx_import_t *import) { assert ( import && " param import is NULL"); if (import->module) { m__free(import->module); } if (import->prefix) { m__free(import->prefix); } if (import->revision) { m__free(import->revision); } if (import->description) { m__free(import->description); } if (import->reference) { m__free(import->reference); } /* YANG only */ ncx_clean_appinfoQ(&import->appinfoQ); m__free(import); } /* ncx_free_import */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_import * \brief Search the importQ for a specified module name * \param mod module to search (mod->importQ) * \param module module name to find * \return pointer to the node if found, NULL if not found */ ncx_import_t * ncx_find_import (const ncx_module_t *mod, const xmlChar *module) { assert ( mod && " param mod is NULL"); assert ( module && " param module is NULL"); return ncx_find_import_que(&mod->importQ, module); } /* ncx_find_import */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_import_que * \brief Search the specified importQ for a specified module name * \param importQ Q of ncx_import_t to search * \param module module name to find * \return pointer to the node if found, NULL if not found */ ncx_import_t * ncx_find_import_que (const dlq_hdr_t *importQ, const xmlChar *module) { assert ( importQ && " param importQ is NULL"); assert ( module && " param module is NULL"); ncx_import_t *import; for (import = (ncx_import_t *)dlq_firstEntry(importQ); import != NULL; import = (ncx_import_t *)dlq_nextEntry(import)) { if (!xml_strcmp(import->module, module)) { import->used = TRUE; return import; } } return NULL; } /* ncx_find_import_que */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_import_test * \brief Search the importQ for a specified module name * Do not set used flag * \param mod module to search (mod->importQ) * \param module module name to find * \return pointer to the node if found, NULL if not found */ ncx_import_t * ncx_find_import_test (const ncx_module_t *mod, const xmlChar *module) { assert ( mod && " param mod is NULL"); assert ( module && " param module is NULL"); ncx_import_t *import; for (import = (ncx_import_t *)dlq_firstEntry(&mod->importQ); import != NULL; import = (ncx_import_t *)dlq_nextEntry(import)) { if (!xml_strcmp(import->module, module)) { return import; } } return NULL; } /* ncx_find_import_test */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_pre_import * \brief Search the importQ for a specified prefix value * \param mod module to search (mod->importQ) * \param prefix prefix string to find * \return pointer to the node if found, NULL if not found */ ncx_import_t * ncx_find_pre_import (const ncx_module_t *mod, const xmlChar *prefix) { assert ( mod && " param mod is NULL"); assert ( prefix && " param prefix is NULL"); return ncx_find_pre_import_que(&mod->importQ, prefix); } /* ncx_find_pre_import */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_pre_import_que * \brief Search the specified importQ for a specified prefix value * \param importQ Q of ncx_import_t to search * \param prefix prefix string to find * \return pointer to the node if found, NULL if not found */ ncx_import_t * ncx_find_pre_import_que (const dlq_hdr_t *importQ, const xmlChar *prefix) { assert ( importQ && " param importQ is NULL"); assert ( prefix && " param prefix is NULL"); ncx_import_t *import; for (import = (ncx_import_t *)dlq_firstEntry(importQ); import != NULL; import = (ncx_import_t *)dlq_nextEntry(import)) { if (import->prefix && !xml_strcmp(import->prefix, prefix)) { import->used = TRUE; return import; } } return NULL; } /* ncx_find_pre_import_que */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_pre_import_test * \brief Search the importQ for a specified prefix value * Test only, do not set used flag * \param mod module to search (mod->importQ) * \param prefix prefix string to find * \return pointer to the node if found, NULL if not found */ ncx_import_t * ncx_find_pre_import_test (const ncx_module_t *mod, const xmlChar *prefix) { assert ( mod && " param mod is NULL"); assert ( prefix && " param prefix is NULL"); ncx_import_t *import; for (import = (ncx_import_t *)dlq_firstEntry(&mod->importQ); import != NULL; import = (ncx_import_t *)dlq_nextEntry(import)) { if (import->prefix && !xml_strcmp(import->prefix, prefix)) { return import; } } return NULL; } /* ncx_find_pre_import_test */ // ----------------------------------------------------------------------------! /** * \fn ncx_locate_modqual_import * \brief Search the specific module for the specified definition name. * Okay for YANG or NCX * - typ_template_t (NCX_NT_TYP) * - grp_template_t (NCX_NT_GRP) * - obj_template_t (NCX_NT_OBJ) * - rpc_template_t (NCX_NT_RPC) * - not_template_t (NCX_NT_NOTIF) * \param pcb parser control block to use * \param imp NCX import struct to use; imp->mod may get set if not already * \param defname name of definition to find * \param *deftyp specified type or NCX_NT_NONE if any will do; set to type * retrieved if NO_ERR * \return pointer to the located definition or NULL if not found */ void * ncx_locate_modqual_import (yang_pcb_t *pcb, ncx_import_t *imp, const xmlChar *defname, ncx_node_t *deftyp) { assert ( imp && " param imp is NULL"); assert ( defname && " param defname is NULL"); assert ( deftyp && " param deftyp is NULL"); void *dptr; status_t res = check_moddef(pcb, imp, defname, deftyp, &dptr); return (res==NO_ERR) ? dptr : NULL; /*** error res is lost !!! ***/ } /* ncx_locate_modqual_import */ // ----------------------------------------------------------------------------! /** * \fn ncx_new_include * \brief Malloc and initialize the fields in a ncx_include_t * \return pointer to the malloced and initialized struct or NULL if an error */ ncx_include_t * ncx_new_include (void) { ncx_include_t *inc = m__getObj(ncx_include_t); if (!inc) { return NULL; } (void)memset(inc, 0x0, sizeof(ncx_include_t)); dlq_createSQue(&inc->appinfoQ); return inc; } /* ncx_new_include */ // ----------------------------------------------------------------------------! /** * \fn ncx_free_include * \brief Scrub the memory in a ncx_include_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * \param inc ncx_include_t data structure to free * \return none */ void ncx_free_include (ncx_include_t *inc) { assert ( inc && " param inc is NULL"); if (inc->submodule) { m__free(inc->submodule); } if (inc->revision) { m__free(inc->revision); } ncx_clean_appinfoQ(&inc->appinfoQ); m__free(inc); } /* ncx_free_include */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_include * \brief Search the includeQ for a specified submodule name * \param mod module to search (mod->includeQ) * \param submodule submodule name to find * \return pointer to the node if found, NULL if not found */ ncx_include_t * ncx_find_include (const ncx_module_t *mod, const xmlChar *submodule) { assert ( mod && " param mod is NULL"); assert ( submodule && " param submodule is NULL"); ncx_include_t *inc; for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { if (!xml_strcmp(inc->submodule, submodule)) { return inc; } } return NULL; } /* ncx_find_include */ // ----------------------------------------------------------------------------! /** * \fn ncx_new_binary * \brief Malloc and fill in a new ncx_binary_t struct * \return pointer to malloced and initialized ncx_binary_t struct * NULL if malloc error */ ncx_binary_t * ncx_new_binary (void) { ncx_binary_t *binary = m__getObj(ncx_binary_t); if (!binary) { return NULL; } ncx_init_binary(binary); return binary; } /* ncx_new_binary */ // ----------------------------------------------------------------------------! /** * \fn ncx_init_binary * \brief Init the memory of a ncx_binary_t struct * \param binary ncx_binary_t struct to init * \return none */ void ncx_init_binary (ncx_binary_t *binary) { assert ( binary && " param binary is NULL"); memset(binary, 0x0, sizeof(ncx_binary_t)); } /* ncx_init_binary */ // ----------------------------------------------------------------------------! /** * \fn ncx_clean_binary * \brief Scrub the memory of a ncx_binary_t but do not delete it * \param binary ncx_binary_t struct to clean * \return none */ void ncx_clean_binary (ncx_binary_t *binary) { assert ( binary && " param binary is NULL"); if (binary->ustr) { m__free(binary->ustr); } memset(binary, 0x0, sizeof(ncx_binary_t)); } /* ncx_clean_binary */ // ----------------------------------------------------------------------------! /** * \fn ncx_free_binary * \brief Free all the memory in a ncx_binary_t struct * \param binary struct to clean and free * \return none */ void ncx_free_binary (ncx_binary_t *binary) { assert ( binary && " param binary is NULL"); ncx_clean_binary(binary); m__free(binary); } /* ncx_free_binary */ // ----------------------------------------------------------------------------! /** * \fn ncx_new_identity * \brief Get a new ncx_identity_t struct * \return pointer to a malloced ncx_identity_t struct, * or NULL if malloc error */ ncx_identity_t * ncx_new_identity (void) { ncx_identity_t *identity = m__getObj(ncx_identity_t); if (!identity) { return NULL; } memset(identity, 0x0, sizeof(ncx_identity_t)); dlq_createSQue(&identity->childQ); dlq_createSQue(&identity->baseQ); dlq_createSQue(&identity->iffeatureQ); dlq_createSQue(&identity->appinfoQ); return identity; } /* ncx_new_identity */ // ----------------------------------------------------------------------------! /** * \fn ncx_free_identity * \brief Free a malloced ncx_identity_t struct * \param identity struct to free * \return none */ void ncx_free_identity (ncx_identity_t *identity) { ncx_idlink_t *idlink; assert ( identity && " param identity is NULL"); if (identity->name) { m__free(identity->name); } if (!identity->isroot) { ncx_identity_base_t * base; for(base = (ncx_identity_base_t *)dlq_firstEntry(&identity->baseQ); base!=NULL; base=(ncx_identity_base_t *)dlq_firstEntry(&identity->baseQ)) { /*** !!! ignoring the back-ptr childQ threading the *** !!! idlink headers; do not delete from system *** !!! until some way to clear all or issue an error *** !!! is done. Assume this free is part of the *** !!! system cleanup for now *** *** !!! Clearing out the back-ptrs in case the heap 'free' *** !!! function does not set these fields to garbage ***/ if(base->idlink.inq) { dlq_remove(&base->idlink); base->idlink.inq = FALSE; base->idlink.identity = NULL; } if(base->prefix) { m__free(base->prefix); } if(base->name) { m__free(base->name); } dlq_remove(base); m__free(base); } } else { assert(dlq_firstEntry(&identity->baseQ)==NULL); } for(idlink=(ncx_idlink_t *)dlq_firstEntry(&identity->childQ); idlink!=NULL; idlink=(ncx_idlink_t *)dlq_firstEntry(&identity->childQ)) { assert(idlink->inq==TRUE); dlq_remove(idlink); idlink->inq=FALSE; } if (identity->descr) { m__free(identity->descr); } if (identity->ref) { m__free(identity->ref); } ncx_clean_appinfoQ(&identity->appinfoQ); ncx_clean_iffeatureQ(&identity->iffeatureQ); m__free(identity); } /* ncx_free_identity */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_identity * \brief Find a ncx_identity_t struct in the module and perhaps * any of its submodules * \param mod module to search * \param name identity name to find * \param useall TRUE if all submodules should be checked * FALSE if only visible included submodules * should be checked * \return pointer to found feature or NULL if not found */ ncx_identity_t * ncx_find_identity (ncx_module_t *mod, const xmlChar *name, boolean useall) { assert ( mod && " param mod is NULL"); assert ( name && " param name NULL"); ncx_identity_t *identity = ncx_find_identity_que(&mod->identityQ, name); if (identity) { if (!identity_is_enabled(identity)) return NULL; return identity; } dlq_hdr_t *que = ncx_get_allincQ(mod); yang_node_t *node; ncx_include_t *inc; if (useall) { for (node = (yang_node_t *)dlq_firstEntry(que); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod) { /* check the identity Q in this submodule */ identity = ncx_find_identity_que(&node->submod->identityQ, name); if (identity) { if (!identity_is_enabled(identity)) return NULL; return identity; } } } } else { /* check all the submodules, but only the ones visible * to this module or submodule */ for (inc = (ncx_include_t *)dlq_firstEntry(&mod->includeQ); inc != NULL; inc = (ncx_include_t *)dlq_nextEntry(inc)) { /* get the real submodule struct */ if (!inc->submod) { node = yang_find_node(que, inc->submodule, inc->revision); if (node) { inc->submod = node->submod; } if (!inc->submod) { /* include not found or errors in it */ continue; } } /* check the identity Q in this submodule */ identity = ncx_find_identity_que(&inc->submod->identityQ, name); if (identity) { if (!identity_is_enabled(identity)) return NULL; return identity; } } } return NULL; } /* ncx_find_identity */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_identity_que * \brief Find a ncx_identity_t struct in the specified Q * \param identityQ Q of ncx_identity_t to search * \param name identity name to find * \return pointer to found identity or NULL if not found */ ncx_identity_t * ncx_find_identity_que (const dlq_hdr_t *identityQ, const xmlChar *name) { assert ( identityQ && " param identityQ is NULL"); assert ( name && " param name is NULL"); ncx_identity_t *identity; for (identity = (ncx_identity_t *)dlq_firstEntry(identityQ); identity != NULL; identity = (ncx_identity_t *)dlq_nextEntry(identity)) { if (!xml_strcmp(identity->name, name)) { return identity; } } return NULL; } /* ncx_find_identity_que */ // ----------------------------------------------------------------------------! /** * \fn ncx_new_filptr * \brief Get a new ncx_filptr_t struct * \return pointer to a malloced or cached ncx_filptr_t struct, * or NULL if none available */ ncx_filptr_t * ncx_new_filptr (void) { ncx_filptr_t *filptr; /* check the cache first */ if (ncx_cur_filptrs) { filptr = (ncx_filptr_t *)dlq_deque(&ncx_filptrQ); ncx_cur_filptrs--; return filptr; } /* create a new one */ filptr = m__getObj(ncx_filptr_t); if (!filptr) { return NULL; } memset (filptr, 0x0, sizeof(ncx_filptr_t)); dlq_createSQue(&filptr->childQ); return filptr; } /* ncx_new_filptr */ // ----------------------------------------------------------------------------! /** * \fn ncx_free_filptr * \brief Free a new ncx_filptr_t struct or add to the cache if room * \param filptr struct to free * \return none */ void ncx_free_filptr (ncx_filptr_t *filptr) { assert ( filptr && " param filptr is NULL"); /* recursively clean out the child Queues */ ncx_filptr_t *fp; while (!dlq_empty(&filptr->childQ)) { fp = (ncx_filptr_t *)dlq_deque(&filptr->childQ); ncx_free_filptr(fp); } /* check if this entry should be put in the cache */ if (ncx_cur_filptrs < ncx_max_filptrs) { memset(filptr, 0x0, sizeof(ncx_filptr_t)); dlq_createSQue(&filptr->childQ); dlq_enque(filptr, &ncx_filptrQ); ncx_cur_filptrs++; } else { /* cache full, so just delete this entry */ m__free(filptr); } } /* ncx_free_filptr */ // ----------------------------------------------------------------------------! /** * \fn ncx_new_revhist * \brief Create a revision history entry * \return malloced revision history entry or NULL if malloc error */ ncx_revhist_t * ncx_new_revhist (void) { ncx_revhist_t *revhist = m__getObj(ncx_revhist_t); if (!revhist) { return NULL; } memset(revhist, 0x0, sizeof(ncx_revhist_t)); return revhist; } /* ncx_new_revhist */ // ----------------------------------------------------------------------------! /** * \fn ncx_free_revhist * \brief Free a revision history entry * \param revhist ncx_revhist_t data structure to free * \return none */ void ncx_free_revhist (ncx_revhist_t *revhist) { assert ( revhist && " param revhist is NULL"); if (revhist->version) { m__free(revhist->version); } if (revhist->descr) { m__free(revhist->descr); } if (revhist->ref) { m__free(revhist->ref); } m__free(revhist); } /* ncx_free_revhist */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_revhist * \brief Search the revhistQ for a specified revision * \param mod module to search (mod->importQ) * \param ver version string to find * \return pointer to the node if found, NULL if not found */ ncx_revhist_t * ncx_find_revhist (const ncx_module_t *mod, const xmlChar *ver) { assert ( mod && " param mod is NULL"); assert ( ver && " param ver is NULL"); ncx_revhist_t *revhist; for (revhist = (ncx_revhist_t *)dlq_firstEntry(&mod->revhistQ); revhist != NULL; revhist = (ncx_revhist_t *)dlq_nextEntry(revhist)) { if (!xml_strcmp(revhist->version, ver)) { return revhist; } } return NULL; } /* ncx_find_revhist */ /********************** ncx_enum_t *********************/ // ----------------------------------------------------------------------------! /** * \fn ncx_init_enum * \brief Init the memory of a ncx_enum_t * \param enu ncx_enum_t struct to init * \return none */ void ncx_init_enum (ncx_enum_t *enu) { assert ( enu && " param enu is NULL" ); enu->name = NULL; enu->dname = NULL; enu->val = 0; } /* ncx_init_enum */ // ----------------------------------------------------------------------------! /** * \fn ncx_clean_enum * \brief Scrub the memory of a ncx_enum_t but do not delete it * \param enu ncx_enum_t struct to clean * \return none */ void ncx_clean_enum (ncx_enum_t *enu) { assert ( enu && " param enu is NULL" ); enu->name = NULL; if (enu->dname) { m__free(enu->dname); enu->dname = NULL; } enu->val = 0; } /* ncx_clean_enum */ // ----------------------------------------------------------------------------! /** * \fn ncx_compare_enums * \brief Compare 2 enum values * \param enu1 first ncx_enum_t check * \param enu2 second ncx_enum_t check * \return-1 if enu1 is < enu2 * 0 if enu1 == enu2 * 1 if enu1 is > enu2 */ int32 ncx_compare_enums (const ncx_enum_t *enu1, const ncx_enum_t *enu2) { assert ( enu1 && " param enu1 is NULL" ); assert ( enu2 && " param enu2 is NULL" ); /* just check strings exact match */ return xml_strcmp(enu1->name, enu2->name); } /* ncx_compare_enums */ // ----------------------------------------------------------------------------! /** * \fn ncx_set_enum * \brief Parse an enumerated integer string into an ncx_enum_t * without matching it against any typdef * Mallocs a copy of the enum name, using the enu->dname field * \param enumval enum string value to parse * \param retenu pointer to return enum variable to fill in * \return status */ status_t ncx_set_enum (const xmlChar *enumval, ncx_enum_t *retenu) { assert ( enumval && " param enumval is NULL" ); assert ( retenu && " param retenu is NULL" ); xmlChar *str = xml_strdup(enumval); if (!str) { return ERR_INTERNAL_MEM; } if (retenu->dname != NULL) { m__free(retenu->dname); } retenu->dname = str; retenu->name = str; retenu->val = 0; /*** NOT USED ***/ return NO_ERR; } /* ncx_set_enum */ /********************** ncx_bit_t *********************/ // ----------------------------------------------------------------------------! /** * \fn ncx_init_bit * \brief Init the memory of a ncx_bit_t * \param bit ncx_bit_t struct to init * \return none */ void ncx_init_bit (ncx_bit_t *bit) { assert ( bit && " param bit is NULL" ); bit->name = NULL; bit->dname = NULL; bit->pos = 0; } /* ncx_init_bit */ // ----------------------------------------------------------------------------! /** * \fn ncx_clean_bit * \brief Scrub the memory of a ncx_bit_t but do not delete it * \param bit ncx_bit_t struct to clean * \return none */ void ncx_clean_bit (ncx_bit_t *bit) { assert ( bit && " param bit is NULL" ); if (bit->dname) { m__free(bit->dname); bit->dname = NULL; } bit->pos = 0; bit->name = NULL; } /* ncx_clean_bit */ // ----------------------------------------------------------------------------! /** * \fn ncx_compare_bits * \brief Compare 2 bit values by their schema order position * \param bitone first ncx_bit_t check * \param bittwo second ncx_bit_t check * \return -1 if bitone is < bittwo * 0 if bitone == bittwo * 1 if bitone is > bittwo */ int32 ncx_compare_bits (const ncx_bit_t *bitone, const ncx_bit_t *bittwo) { assert ( bitone && " param bitone is NULL" ); assert ( bittwo && " param bittwo is NULL" ); if (bitone->pos < bittwo->pos) { return -1; } else if (bitone->pos > bittwo->pos) { return 1; } else { return 0; } /*NOTREACHED*/ } /* ncx_compare_bits */ /********************** ncx_typname_t *********************/ // ----------------------------------------------------------------------------! /** * \fn ncx_new_typname * \brief Malloc and init a typname struct * \return malloced struct or NULL if memory error */ ncx_typname_t * ncx_new_typname (void) { ncx_typname_t *tn = m__getObj(ncx_typname_t); if (!tn) { return NULL; } memset(tn, 0x0, sizeof(ncx_typname_t)); return tn; } /* ncx_new_typname */ // ----------------------------------------------------------------------------! /** * \fn ncx_free_typname * \brief Free a typname struct * \param typnam ncx_typname_t struct to free * \return none */ void ncx_free_typname (ncx_typname_t *typnam) { assert ( typnam && " param typnam is NULL" ); if (typnam->typname_malloc) { m__free(typnam->typname_malloc); } m__free(typnam); } /* ncx_free_typname */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_typname * \brief Find a typname struct in the specified Q for a typ pointer * \param que Q of ncx_typname_t struct to check * \param typ matching type template to find * \return name assigned to this type template */ const xmlChar * ncx_find_typname (const typ_template_t *typ, const dlq_hdr_t *que) { assert ( typ && " param typ is NULL" ); assert ( que && " param que is NULL" ); const ncx_typname_t *tn; for (tn = (const ncx_typname_t *)dlq_firstEntry(que); tn != NULL; tn = (const ncx_typname_t *)dlq_nextEntry(tn)) { if (tn->typ == typ) { return tn->typname; } } return NULL; } /* ncx_find_typname */ // ----------------------------------------------------------------------------! /** * \fn ncx_find_typname_type * \brief Find a typ_template_t pointer in a typename mapping, * in the specified Q * \param que Q of ncx_typname_t struct to check * \param typname matching type name to find * \return pointer to the stored typstatus */ const typ_template_t * ncx_find_typname_type (const dlq_hdr_t *que, const xmlChar *typname) { assert ( que && " param que is NULL" ); assert ( typname && " param typname is NULL" ); const ncx_typname_t *tn; for (tn = (const ncx_typname_t *)dlq_firstEntry(que); tn != NULL; tn = (const ncx_typname_t *)dlq_nextEntry(tn)) { if (!xml_strcmp(tn->typname, typname)) { return tn->typ; } } return NULL; } /* ncx_find_typname_type */ // ----------------------------------------------------------------------------! /** * \fn ncx_clean_typnameQ * \brief Delete all the Q entries, of typname mapping structs * \param que Q of ncx_typname_t struct to delete * \return none */ void ncx_clean_typnameQ (dlq_hdr_t *que) { assert ( que && " param que is NULL" ); ncx_typname_t *tn; while (!dlq_empty(que)) { tn = (ncx_typname_t *)dlq_deque(que); ncx_free_typname(tn); } } /* ncx_clean_typnameQ */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_gen_anyxml * \brief Get the object template for the NCX generic anyxml container * \return pointer to generic anyxml object template */ obj_template_t * ncx_get_gen_anyxml (void) { return gen_anyxml; } /* ncx_get_gen_anyxml */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_gen_container * \brief Get the object template for the NCX generic container * \return pointer to generic container object template */ obj_template_t * ncx_get_gen_container (void) { return gen_container; } /* ncx_get_gen_container */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_gen_string * \brief Get the object template for the NCX generic string leaf * \return pointer to generic string object template */ obj_template_t * ncx_get_gen_string (void) { return gen_string; } /* ncx_get_gen_string */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_gen_empty * \brief Get the object template for the NCX generic empty leaf * \return pointer to generic empty object template */ obj_template_t * ncx_get_gen_empty (void) { return gen_empty; } /* ncx_get_gen_empty */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_gen_root * \brief Get the object template for the NCX generic root container * \return pointer to generic root container object template */ obj_template_t * ncx_get_gen_root (void) { return gen_root; } /* ncx_get_gen_root */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_gen_binary * \brief Get the object template for the NCX generic binary leaf * \return pointer to generic binary object template */ obj_template_t * ncx_get_gen_binary (void) { return gen_binary; } /* ncx_get_gen_binary */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_layer * \brief Translate ncx_layer_t enum to a string * Get the ncx_layer_t string * \param layer ncx_layer_t to convert to a string * \return const pointer to the string value */ const xmlChar * ncx_get_layer (ncx_layer_t layer) { switch (layer) { case NCX_LAYER_NONE: return (const xmlChar *)"none"; case NCX_LAYER_TRANSPORT: return (const xmlChar *)"transport"; case NCX_LAYER_RPC: return (const xmlChar *)"rpc"; case NCX_LAYER_OPERATION: return (const xmlChar *)"protocol"; case NCX_LAYER_CONTENT: return (const xmlChar *)"application"; default: SET_ERROR(ERR_INTERNAL_VAL); return (const xmlChar *)"--"; } } /* ncx_get_layer */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_name_segment * \brief Get the name string between the dots * \param str scoped string * \param buff address of return buffer * \param buffsize buffer size * \return current string pointer after operation */ const xmlChar * ncx_get_name_segment (const xmlChar *str, xmlChar *buff, uint32 buffsize) { assert ( str && " param str is NULL" ); assert ( buff && " param buff is NULL" ); const xmlChar *teststr = str; while (*teststr && *teststr != NCX_SCOPE_CH) { teststr++; } if ((uint32)(teststr - str) >= buffsize) { SET_ERROR(ERR_BUFF_OVFL); return NULL; } while (*str && *str != NCX_SCOPE_CH) { *buff++ = *str++; } *buff = 0; return str; } /* ncx_get_name_segment */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_cvttyp_enum * \brief Get the enum for the string name of a ncx_cvttyp_t enum * \param str string name of the enum value * \return enum value */ ncx_cvttyp_t ncx_get_cvttyp_enum (const char *str) { assert ( str && " param str is NULL" ); if (!xml_strcmp(NCX_EL_XSD, (const xmlChar *)str)) { return NCX_CVTTYP_XSD; } else if (!xml_strcmp(NCX_EL_SQL, (const xmlChar *)str)) { return NCX_CVTTYP_SQL; } else if (!xml_strcmp(NCX_EL_SQLDB, (const xmlChar *)str)) { return NCX_CVTTYP_SQLDB; } else if (!xml_strcmp(NCX_EL_HTML, (const xmlChar *)str)) { return NCX_CVTTYP_HTML; } else if (!xml_strcmp(NCX_EL_H, (const xmlChar *)str)) { return NCX_CVTTYP_H; } else if (!xml_strcmp(NCX_EL_C, (const xmlChar *)str)) { return NCX_CVTTYP_C; } else if (!xml_strcmp(NCX_EL_CPP_TEST, (const xmlChar *)str)) { return NCX_CVTTYP_CPP_TEST; } else if (!xml_strcmp(NCX_EL_YANG, (const xmlChar *)str)) { return NCX_CVTTYP_YANG; } else if (!xml_strcmp(NCX_EL_COPY, (const xmlChar *)str)) { return NCX_CVTTYP_COPY; } else if (!xml_strcmp(NCX_EL_YIN, (const xmlChar *)str)) { return NCX_CVTTYP_YIN; } else if (!xml_strcmp(NCX_EL_TG2, (const xmlChar *)str)) { return NCX_CVTTYP_TG2; } else if (!xml_strcmp(NCX_EL_UC, (const xmlChar *)str)) { return NCX_CVTTYP_UC; } else if (!xml_strcmp(NCX_EL_UH, (const xmlChar *)str)) { return NCX_CVTTYP_UH; } else if (!xml_strcmp(NCX_EL_YC, (const xmlChar *)str)) { return NCX_CVTTYP_YC; } else if (!xml_strcmp(NCX_EL_YH, (const xmlChar *)str)) { return NCX_CVTTYP_YH; } else if (!xml_strcmp(NCX_EL_TREE, (const xmlChar *)str)) { return NCX_CVTTYP_TREE; } else { return NCX_CVTTYP_NONE; } /*NOTREACHED*/ } /* ncx_get_cvttype_enum */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_status_enum * \brief Get the enum for the string name of a ncx_status_t enum * \param str string name of the enum value * \return enum value */ ncx_status_t ncx_get_status_enum (const xmlChar *str) { assert ( str && " param str is NULL" ); if (!xml_strcmp(NCX_EL_CURRENT, str)) { return NCX_STATUS_CURRENT; } else if (!xml_strcmp(NCX_EL_DEPRECATED, str)) { return NCX_STATUS_DEPRECATED; } else if (!xml_strcmp(NCX_EL_OBSOLETE, str)) { return NCX_STATUS_OBSOLETE; } else { return NCX_STATUS_NONE; } /*NOTREACHED*/ } /* ncx_get_status_enum */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_status_string * \brief Get the string for the enum value of a ncx_status_t enum * \param status enum value * \return string name of the enum value */ const xmlChar * ncx_get_status_string (ncx_status_t status) { switch (status) { case NCX_STATUS_CURRENT: case NCX_STATUS_NONE: return NCX_EL_CURRENT; case NCX_STATUS_DEPRECATED: return NCX_EL_DEPRECATED; case NCX_STATUS_OBSOLETE: return NCX_EL_OBSOLETE; default: SET_ERROR(ERR_INTERNAL_VAL); return (const xmlChar *)"none"; } /*NOTREACHED*/ } /* ncx_get_status_string */ // ----------------------------------------------------------------------------! /** * \fn ncx_check_yang_status * \brief Check the backward compatibility of the 2 YANG status fields * \param mystatus enum value for the node to be tested * \param depstatus status value of the dependency * \return status of the operation */ status_t ncx_check_yang_status (ncx_status_t mystatus, ncx_status_t depstatus) { switch (mystatus) { case NCX_STATUS_CURRENT: /* current definition can use another * current definition */ switch (depstatus) { case NCX_STATUS_CURRENT: return NO_ERR; case NCX_STATUS_DEPRECATED: return ERR_NCX_USING_DEPRECATED; case NCX_STATUS_OBSOLETE: return ERR_NCX_USING_OBSOLETE; default: return SET_ERROR(ERR_INTERNAL_VAL); } /*NOTRECHED*/ case NCX_STATUS_DEPRECATED: /* deprecated definition can use anything but an * an obsolete definition */ switch (depstatus) { case NCX_STATUS_CURRENT: case NCX_STATUS_DEPRECATED: return NO_ERR; case NCX_STATUS_OBSOLETE: return ERR_NCX_USING_OBSOLETE; default: return SET_ERROR(ERR_INTERNAL_VAL); } /*NOTREACHED*/ case NCX_STATUS_OBSOLETE: /* obsolete definition can use any definition */ return NO_ERR; case NCX_STATUS_NONE: default: return SET_ERROR(ERR_INTERNAL_VAL); } /*NOTREACHED*/ } /* ncx_check_yang_status */ // ----------------------------------------------------------------------------! /** * \fn ncx_save_descr * \brief Get the value of the save description strings variable * \return TRUE descriptive strings should be saved * FALSE descriptive strings should not be saved */ boolean ncx_save_descr (void) { return save_descr; } /* ncx_save_descr */ // ----------------------------------------------------------------------------! /** * \fn ncx_print_errormsg * \brief Print an parse error message to STDOUT * \param tkc token chain (may be NULL) * \param mod module in progress (may be NULL) * \param res error status * \return none */ void ncx_print_errormsg (tk_chain_t *tkc, ncx_module_t *mod, status_t res) { ncx_print_errormsg_ex(tkc, mod, res, NULL, 0, TRUE); } /* ncx_print_errormsg */ // ----------------------------------------------------------------------------! /** * \fn ncx_print_errormsg_ex * \brief Print an parse error message to STDOUT (Extended) * \param tkc token chain (may be NULL) * \param mod module in progress (may be NULL) * \param res error status * \param filename script finespec * \param linenum script file number * \param fineoln TRUE if finish with a newline, FALSE if not * \return none */ void ncx_print_errormsg_ex (tk_chain_t *tkc, ncx_module_t *mod, status_t res, const char *filename, uint32 linenum, boolean fineoln) { if (res == NO_ERR) { SET_ERROR(ERR_INTERNAL_VAL); return; } boolean iserr = (res <= ERR_LAST_USR_ERR) ? TRUE : FALSE; if (!iserr && !ncx_warning_enabled(res)) { log_debug3("\nSuppressed warning %d (%s.%u)", res, get_error_string(res), ( mod ? (const xmlChar*)mod->name : (const xmlChar*)"UNKNOWN" ), linenum); return; } if (mod) { if (iserr) { mod->errors++; } else { mod->warnings++; } } if (iserr) { if (!LOGERROR) { /* errors turned off by the user! */ return; } } else if (!LOGWARN) { /* warnings turned off by the user */ return; } if (tkc && tkc->curerr && tkc->curerr->mod) { log_write("\n%s:", (tkc->curerr->mod->sourcefn) ? (const char *)tkc->curerr->mod->sourcefn : "--"); } else if (mod && mod->sourcefn) { log_write("\n%s:", (mod->sourcefn) ? (const char *)mod->sourcefn : "--"); } else if (tkc && tkc->filename) { log_write("\n%s:", tkc->filename); } else if (filename) { log_write("\n%s:", filename); if (linenum) { log_write("line %u:", linenum); } } else { log_write("\n"); } if (tkc) { if (tkc->curerr && tkc->curerr->mod) { log_write("%u.%u:", tkc->curerr->linenum, tkc->curerr->linepos); } else if (tkc->cur && (tkc->cur != (tk_token_t *)&tkc->tkQ) && TK_CUR_VAL(tkc)) { log_write("%u.%u:", TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); } else { log_write("%u.%u:", tkc->linenum, tkc->linepos); } tkc->curerr = NULL; } if (iserr) { log_write(" error(%u): %s", res, get_error_string(res)); } else { log_write(" warning(%u): %s", res, get_error_string(res)); } if (fineoln) { log_write("\n"); } } /* ncx_print_errormsg_ex */ // ----------------------------------------------------------------------------! /** * \fn ncx_conf_exp_err * \brief Print an error for wrong token, expected a different token * \param tkc token chain * \param result error code * \param expstr expected token description * \return none */ void ncx_conf_exp_err (tk_chain_t *tkc, status_t result, const char *expstr) { ncx_print_errormsg_ex(tkc, NULL, result, NULL, 0, (expstr) ? FALSE : TRUE); if (expstr) { log_write(" Expected: %s\n", expstr); } } /* ncx_conf_exp_err */ // ----------------------------------------------------------------------------! /** * \fn ncx_mod_exp_err * \brief Print an error for wrong token, expected a different token * \param tkc token chain * \param mod module in progress * \param result error code * \param expstr expected token description * \return none */ void ncx_mod_exp_err (tk_chain_t *tkc, ncx_module_t *mod, status_t result, const char *expstr) { const char *gotval; tk_type_t tktyp; boolean skip = FALSE; if (TK_CUR(tkc)) { tktyp = TK_CUR_TYP(tkc); } else { tktyp = TK_TT_NONE; } if (tktyp == TK_TT_NONE) { gotval = NULL; } else if (TK_TYP_STR(tktyp)) { gotval = (const char *)TK_CUR_VAL(tkc); } else if (TK_CUR_TYP(tkc) == TK_TT_LBRACE) { gotval = "left brace, skipping to closing right brace"; skip = TRUE; } else { gotval = tk_get_token_name(tktyp); } if (LOGERROR) { if (gotval && expstr) { log_error("\nError: Got '%s', Expected: %s", gotval, expstr); } else if (expstr) { log_error("\nError: Expected: %s", expstr); } ncx_print_errormsg_ex(tkc, mod, result, NULL, 0, (expstr) ? FALSE : TRUE); log_error("\n"); } if (skip) { /* got an unexpected left brace, so skip to the * end of this unknown section to resynch; * otherwise the first unknown closing right brace * will end the parent section, which causes * a false 'unexpected EOF' error */ uint32 skipcount = 1; boolean done = FALSE; status_t res = NO_ERR; while (!done && res == NO_ERR) { res = TK_ADV(tkc); if (res == NO_ERR) { tktyp = TK_CUR_TYP(tkc); if (tktyp == TK_TT_LBRACE) { skipcount++; } else if (tktyp == TK_TT_RBRACE) { skipcount--; } if (!skipcount) { done = TRUE; } } } } } /* ncx_mod_exp_err */ // ----------------------------------------------------------------------------! /** * \fn ncx_mod_missing_err * \brief Print an error for wrong token, mandatory * sub-statement is missing * \param tkc token chain * \param mod module in progress * \param stmtstr parent statement * \param expstr expected sub-statement * \return none */ void ncx_mod_missing_err (tk_chain_t *tkc, ncx_module_t *mod, const char *stmtstr, const char *expstr) { if (LOGERROR) { if (stmtstr && expstr) { log_error("\nError: '%s' statement missing " "mandatory '%s' sub-statement", stmtstr, expstr); } else { SET_ERROR(ERR_INTERNAL_VAL); } ncx_print_errormsg_ex(tkc, mod, ERR_NCX_DATA_MISSING, NULL, 0, (expstr) ? FALSE : TRUE); log_error("\n"); } } /* ncx_mod_missing_err */ // ----------------------------------------------------------------------------! /** * \fn ncx_free_node * \brief Delete a node based on its type * \param nodetyp NCX node type * \param node node top free * \return none */ void ncx_free_node (ncx_node_t nodetyp, void *node) { assert ( node && " param node is NULL" ); switch (nodetyp) { case NCX_NT_NONE: /* uninitialized */ m__free(node); break; case NCX_NT_TYP: /* typ_template_t */ typ_free_template(node); break; case NCX_NT_GRP: /* grp_template_t */ grp_free_template(node); break; case NCX_NT_VAL: /* val_value_t */ val_free_value(node); break; case NCX_NT_OBJ: /* obj_template_t */ obj_free_template(node); break; case NCX_NT_STRING: /* xmlChar string */ m__free(node); break; case NCX_NT_CFG: /* cfg_template_t */ cfg_free_template(node); break; case NCX_NT_QNAME: /* xmlns_qname_t */ xmlns_free_qname(node); break; default: SET_ERROR(ERR_INTERNAL_VAL); m__free(node); } } /* ncx_free_node */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_data_class_enum * \brief Get the enum for the string name of a ncx_data_class_t enum * \param str string name of the enum value * \return enum value */ ncx_data_class_t ncx_get_data_class_enum (const xmlChar *str) { if (!str) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_DC_NONE; } else if (!xml_strcmp(NCX_EL_CONFIG, str)) { return NCX_DC_CONFIG; } else if (!xml_strcmp(NCX_EL_STATE, str)) { return NCX_DC_STATE; } else { /* SET_ERROR(ERR_INTERNAL_VAL); */ return NCX_DC_NONE; } /*NOTREACHED*/ } /* ncx_get_data_class_enum */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_data_class_str * \brief Get the string value for the ncx_data_class_t enum * \param dataclass enum value to convert * \return string value for the enum */ const xmlChar * ncx_get_data_class_str (ncx_data_class_t dataclass) { switch (dataclass) { case NCX_DC_NONE: return NULL; case NCX_DC_CONFIG: return NCX_EL_CONFIG; case NCX_DC_STATE: return NCX_EL_STATE; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* ncx_get_data_class_str */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_access_str * \brief Get the string name of a ncx_access_t enum * \param access == enum value * \return string value */ const xmlChar * ncx_get_access_str (ncx_access_t max_access) { switch (max_access) { case NCX_ACCESS_NONE: return (const xmlChar *) "not set"; case NCX_ACCESS_RO: return NCX_EL_ACCESS_RO; case NCX_ACCESS_RW: return NCX_EL_ACCESS_RW; case NCX_ACCESS_RC: return NCX_EL_ACCESS_RC; default: return (const xmlChar *) "illegal"; } /*NOTREACHED*/ } /* ncx_get_access_str */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_access_enum * \brief Get the enum for the string name of a ncx_access_t enum * \param str string name of the enum value * \return enum value */ ncx_access_t ncx_get_access_enum (const xmlChar *str) { assert ( str && " param str is NULL" ); if (!xml_strcmp(NCX_EL_ACCESS_RO, str)) { return NCX_ACCESS_RO; } else if (!xml_strcmp(NCX_EL_ACCESS_RW, str)) { return NCX_ACCESS_RW; } else if (!xml_strcmp(NCX_EL_ACCESS_RC, str)) { return NCX_ACCESS_RC; } else { SET_ERROR(ERR_INTERNAL_VAL); return NCX_ACCESS_NONE; } /*NOTREACHED*/ } /* ncx_get_access_enum */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_tclass * \brief Get the token class * \param btyp base type enum * \return tclass enum */ ncx_tclass_t ncx_get_tclass (ncx_btype_t btyp) { switch (btyp) { case NCX_BT_NONE: return NCX_CL_NONE; case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_BOOLEAN: case NCX_BT_BITS: case NCX_BT_ENUM: case NCX_BT_EMPTY: case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_LEAFREF: case NCX_BT_SLIST: case NCX_BT_UNION: return NCX_CL_SIMPLE; case NCX_BT_CONTAINER: case NCX_BT_LIST: case NCX_BT_CHOICE: case NCX_BT_CASE: return NCX_CL_COMPLEX; default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_CL_NONE; } } /* ncx_get_tclass */ // ----------------------------------------------------------------------------! /** * \fn ncx_valid_name_ch * \brief Check if an xmlChar is a valid NCX name string char * \param ch xmlChar to check * \return TRUE if a valid name char, FALSE otherwise */ boolean ncx_valid_name_ch (uint32 ch) { char c; if (ch & bit7) { return FALSE; /* TEMP -- handling ASCII only */ } else { c = (char)ch; return (isalpha((int)c) || isdigit((int)c) || c=='_' || c=='-' || c=='.') ? TRUE : FALSE; } /*NOTREACHED*/ } /* ncx_valid_name_ch */ // ----------------------------------------------------------------------------! /** * \fn ncx_valid_fname_ch * \brief Check if an xmlChar is a valid NCX name string first char * \param ch xmlChar to check * \return TRUE if a valid first name char, FALSE otherwise */ boolean ncx_valid_fname_ch (uint32 ch) { char c; if (ch & bit7) { return FALSE; /* TEMP -- handling ASCII only */ } else { c = (char)ch; return (isalpha((int)c) || (c=='_')) ? TRUE : FALSE; } /*NOTREACHED*/ } /* ncx_valid_fname_ch */ // ----------------------------------------------------------------------------! /** * \fn ncx_valid_name * \brief Check if an xmlChar string is a valid YANG identifier value * \param str xmlChar string to check * \param len length of the string to check (in case of substr) * \return TRUE if a valid name string, FALSE otherwise */ boolean ncx_valid_name (const xmlChar *str, uint32 len) { assert ( str && " param str is NULL" ); if (len == 0 || len > NCX_MAX_NLEN) { return FALSE; } if (!ncx_valid_fname_ch(*str)) { return FALSE; } uint32 i; for (i=1; i= 3 && ((*str == 'X' || *str == 'x') && (str[1] == 'M' || str[1] == 'm') && (str[2] == 'L' || str[2] == 'l'))) { return FALSE; } return TRUE; } /* ncx_valid_name */ // ----------------------------------------------------------------------------! /** * \fn ncx_valid_name2 * \brief Check if an xmlChar string is a valid NCX name * \param str xmlChar string to check (zero-terminated) * \return TRUE if a valid name string, FALSE otherwise */ boolean ncx_valid_name2 (const xmlChar *str) { assert ( str && " param str is NULL" ); return ncx_valid_name(str, xml_strlen(str)); } /* ncx_valid_name2 */ // ----------------------------------------------------------------------------! /** * \fn ncx_parse_name * \brief Check if the next N chars represent a valid NcxName * Will end on the first non-name char * \param str xmlChar string to check * \param len address of name length * set to 0 if no valid name parsed * set to > 0 for the numbers of chars in the NcxName * \return status_t i(error if name too long) */ status_t ncx_parse_name (const xmlChar *str, uint32 *len) { assert ( str && " param str is NULL" ); if (!ncx_valid_fname_ch(*str)) { *len = 0; return ERR_NCX_INVALID_NAME; } const xmlChar *s = str+1; while (ncx_valid_name_ch(*s)) { s++; } *len = (uint32)(s - str); if (*len > NCX_MAX_NLEN) { return ERR_NCX_TOO_BIG; } else { return NO_ERR; } } /* ncx_parse_name */ // ----------------------------------------------------------------------------! /** * \fn ncx_is_true * \brief Check if an xmlChar string is a string OK for XSD boolean * \param str xmlChar string to check * \return TRUE if a valid boolean value indicating true * FALSE otherwise */ boolean ncx_is_true (const xmlChar *str) { assert ( str && " param str is NULL" ); if (!xml_strcmp(str, (const xmlChar *)"true") || !xml_strcmp(str, (const xmlChar *)"1")) { return TRUE; } else { return FALSE; } } /* ncx_is_true */ // ----------------------------------------------------------------------------! /** * \fn ncx_is_false * \brief Check if an xmlChar string is a string OK for XSD boolean * \param str xmlChar string to check * \return TRUE if a valid boolean value indicating false * FALSE otherwise */ boolean ncx_is_false (const xmlChar *str) { assert ( str && " param str is NULL" ); if (!xml_strcmp(str, (const xmlChar *)"false") || !xml_strcmp(str, (const xmlChar *)"0")) { return TRUE; } else { return FALSE; } } /* ncx_is_false */ /********* P A R S E R H E L P E R F U N C T I O N S *******/ // ----------------------------------------------------------------------------! /** * \fn ncx_consume_tstring * \brief Consume a TK_TT_TSTRING with the specified value * Error messages are printed by this function!! * Do not duplicate error messages upon error return * \param tkc token chain * \param mod module in progress (NULL if none) * \param name token name * \param opt TRUE for optional param * FALSE for mandatory param * \return status of the operation */ status_t ncx_consume_tstring (tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *name, ncx_opt_t opt) { assert ( tkc && " param tkc is NULL" ); status_t res = TK_ADV(tkc); if (res == NO_ERR) { if (TK_CUR_TYP(tkc) != TK_TT_TSTRING) { if (opt==NCX_OPT) { TK_BKUP(tkc); return ERR_NCX_SKIPPED; } else { res = ERR_NCX_WRONG_TKTYPE; } } else { if (xml_strcmp(TK_CUR_VAL(tkc), name)) { if (opt==NCX_OPT) { TK_BKUP(tkc); return ERR_NCX_SKIPPED; } else { res = ERR_NCX_WRONG_TKVAL; } } } } if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); } return res; } /* ncx_consume_tstring */ // ----------------------------------------------------------------------------! /** * \fn ncx_consume_name * \brief Consume a TK_TSTRING that matches the 'name', then * retrieve the next TK_TSTRING token into the namebuff * If ctk specified, then consume the specified close token * Store the results in a malloced buffer * Error messages are printed by this function!! * Do not duplicate error messages upon error return * \param tkc token chain * \param mod module in progress (NULL if none) * \param name first token name * \param namebuff ptr to OUTPUT name string * \param opt NCX_OPT for optional param * NCX_REQ for mandatory param * \param ctyp close token (use TK_TT_NONE to skip this part) * \return status of the operation */ status_t ncx_consume_name (tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *name, xmlChar **namebuff, ncx_opt_t opt, tk_type_t ctyp) { assert ( tkc && " param tkc is NULL" ); assert ( name && " param name is NULL" ); assert ( namebuff && " param namebuff is NULL" ); status_t retres = NO_ERR; const char *expstr = "name string"; /* check 'name' token */ status_t res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); return res; } if (TK_CUR_TYP(tkc) != TK_TT_TSTRING) { if (opt==NCX_OPT) { TK_BKUP(tkc); return ERR_NCX_SKIPPED; } else { res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, res, expstr); } } if (res==NO_ERR && xml_strcmp(TK_CUR_VAL(tkc), name)) { if (opt==NCX_OPT) { TK_BKUP(tkc); return ERR_NCX_SKIPPED; } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } } retres = res; expstr = "name string"; /* check string value token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); return res; } else { if (TK_CUR_TYP(tkc) != TK_TT_TSTRING) { res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, res, expstr); } else { *namebuff = xml_strdup(TK_CUR_VAL(tkc)); if (!*namebuff) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } } } retres = res; expstr = "closing token"; /* check for a closing token */ if (ctyp != TK_TT_NONE) { res = TK_ADV(tkc); if (res != NO_ERR) { ncx_mod_exp_err(tkc, mod, res, expstr); } else { if (TK_CUR_TYP(tkc) != ctyp) { res = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, res, expstr); } } CHK_EXIT(res, retres); } return retres; } /* ncx_consume_name */ // ----------------------------------------------------------------------------! /** * \fn ncx_consume_token * \brief Consume the next token which should be a 1 or 2 char token * without any value. However this function does not check the value, * just the token type. * Error messages are printed by this function!! * Do not duplicate error messages upon error return * \param tkc token chain * \param mod module in progress (NULL if none) * \param ttyp token type * \return status of the operation */ status_t ncx_consume_token (tk_chain_t *tkc, ncx_module_t *mod, tk_type_t ttyp) { assert ( tkc && " param tkc is NULL" ); const char *tkname; status_t res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } res = (TK_CUR_TYP(tkc) == ttyp) ? NO_ERR : ERR_NCX_WRONG_TKTYPE; if (res != NO_ERR) { tkname = tk_get_token_name(ttyp); switch (tkc->source) { case TK_SOURCE_YANG: ncx_mod_exp_err(tkc, mod, res, tkname); /* if a token is missing and the token * parsed instead looks like the continuation * of a statement, or the end of a section, * then backup and let parsing continue */ switch (ttyp) { case TK_TT_SEMICOL: case TK_TT_LBRACE: switch (TK_CUR_TYP(tkc)) { case TK_TT_TSTRING: case TK_TT_MSTRING: case TK_TT_RBRACE: TK_BKUP(tkc); break; default: ; } break; case TK_TT_RBRACE: switch (TK_CUR_TYP(tkc)) { case TK_TT_TSTRING: case TK_TT_MSTRING: TK_BKUP(tkc); break; default: ; } break; default: ; } break; default: ; } } return res; } /* ncx_consume_token */ // ----------------------------------------------------------------------------! /** * \fn ncx_new_errinfo * \brief Malloc and init a new ncx_errinfo_t * \return pointer to malloced ncx_errinfo_t, or NULL if memory error */ ncx_errinfo_t * ncx_new_errinfo (void) { ncx_errinfo_t *err = m__getObj(ncx_errinfo_t); if (!err) { return NULL; } ncx_init_errinfo(err); return err; } /* ncx_new_errinfo */ // ----------------------------------------------------------------------------! /** * \fn ncx_init_errinfo * \brief Init the fields in an ncx_errinfo_t struct * \param err ncx_errinfo_t data structure to init * \return none */ void ncx_init_errinfo (ncx_errinfo_t *err) { assert ( err && " param err is NULL" ); memset(err, 0x0, sizeof(ncx_errinfo_t)); } /* ncx_init_errinfo */ // ----------------------------------------------------------------------------! /** * \fn ncx_clean_errinfo * \brief Scrub the memory in a ncx_errinfo_t by freeing all * the sub-fields * \param err ncx_errinfo_t data structure to clean * \return none */ void ncx_clean_errinfo (ncx_errinfo_t *err) { assert ( err && " param err is NULL" ); if (err->descr) { m__free(err->descr); err->descr = NULL; } if (err->ref) { m__free(err->ref); err->ref = NULL; } if (err->error_app_tag) { m__free(err->error_app_tag); err->error_app_tag = NULL; } if (err->error_message) { m__free(err->error_message); err->error_message = NULL; } } /* ncx_clean_errinfo */ // ----------------------------------------------------------------------------! /** * \fn ncx_free_errinfo * \brief Scrub the memory in a ncx_errinfo_t by freeing all * the sub-fields, then free the errinfo struct * \param err ncx_errinfo_t data structure to free * \return none */ void ncx_free_errinfo (ncx_errinfo_t *err) { assert ( err && " param err is NULL" ); ncx_clean_errinfo(err); m__free(err); } /* ncx_free_errinfo */ // ----------------------------------------------------------------------------! /** * \fn ncx_errinfo_set * \brief Check if error-app-tag or error-message set * Check if the errinfo struct is set or empty * Checks only the error_app_tag and error_message fields * \param errinfo ncx_errinfo_t struct to check * \return TRUE if at least one field set * FALSE if the errinfo struct is empty */ boolean ncx_errinfo_set (const ncx_errinfo_t *errinfo) { assert( errinfo && "errinfo is NULL" ); if (errinfo->error_app_tag || errinfo->error_message) { return TRUE; } else { return FALSE; } } /* ncx_errinfo_set */ // ----------------------------------------------------------------------------! /** * \fn ncx_copy_errinfo * \brief Copy the fields from one errinfo to a blank errinfo * \param src struct with starting contents * \param src struct with starting contents * \return status */ status_t ncx_copy_errinfo (const ncx_errinfo_t *src, ncx_errinfo_t *dest) { assert ( src && " param src is NULL" ); assert ( dest && " param dest is NULL" ); if (src->descr) { if (dest->descr) { m__free(dest->descr); } dest->descr = xml_strdup(src->descr); if (!dest->descr) { return ERR_INTERNAL_MEM; } } if (src->ref) { if (dest->ref) { m__free(dest->ref); } dest->ref = xml_strdup(src->ref); if (!dest->ref) { return ERR_INTERNAL_MEM; } } if (src->error_app_tag) { if (dest->error_app_tag) { m__free(dest->error_app_tag); } dest->error_app_tag = xml_strdup(src->error_app_tag); if (!dest->error_app_tag) { return ERR_INTERNAL_MEM; } } if (src->error_message) { if (dest->error_message) { m__free(dest->error_message); } dest->error_message = xml_strdup(src->error_message); if (!dest->error_message) { return ERR_INTERNAL_MEM; } } return NO_ERR; } /* ncx_copy_errinfo */ // ----------------------------------------------------------------------------! /** * \fn ncx_get_source_ex * \brief Get a malloced buffer containing the complete filespec * for the given input string. If this is a complete dirspec, * this this will just strdup the value. * This is just a best effort to get the full spec. * If the full spec is greater than 1500 bytes, * then a NULL value (error) will be returned * - Change ./ --> cwd/ * - Remove ~/ --> $HOME * - add trailing '/' if not present * \param fspec input filespec * \param expand_cwd TRUE if foo should be expanded to /cur/dir/foo * FALSE if not * \param res address of return status OUTPUT * \return malloced buffer containing possibly expanded full filespec */ xmlChar * ncx_get_source_ex (const xmlChar *fspec, boolean expand_cwd, status_t *res) { #define DIRBUFF_SIZE 1500 assert ( fspec && " param fspec is NULL" ); assert ( res && " param res is NULL" ); if (*fspec == 0) { *res = ERR_NCX_INVALID_VALUE; return NULL; } const xmlChar *start; xmlChar *bp; uint32 bufflen, userlen; boolean with_dot; *res = NO_ERR; xmlChar *buff = NULL; const xmlChar *user = NULL; uint32 len = 0; const xmlChar *p = fspec; if (*p == NCXMOD_PSCHAR) { /* absolute path */ buff = xml_strdup(fspec); if (!buff) { *res = ERR_INTERNAL_MEM; } } else if (*p == NCXMOD_HMCHAR) { /* starts with ~[username]/some/path */ if (p[1] && p[1] != NCXMOD_PSCHAR) { /* explicit user name */ start = &p[1]; p = &p[2]; while (*p && *p != NCXMOD_PSCHAR) { p++; } userlen = (uint32)(p-start); user = ncxmod_get_userhome(start, userlen); } else { /* implied current user */ p++; /* skip ~ char */ /* get current user home dir */ user = ncxmod_get_userhome(NULL, 0); } if (user == NULL) { log_error("\nError: invalid user name in path string (%s)", fspec); *res = ERR_NCX_INVALID_VALUE; return NULL; } /* string pointer 'p' stopped on the PSCHAR to start the * rest of the path string */ len = xml_strlen(user) + xml_strlen(p); buff = m__getMem(len+1); if (buff == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } bp = buff; bp += xml_strcpy(bp, user); xml_strcpy(bp, p); } else if (*p == NCXMOD_ENVCHAR) { /* should start with $ENVVAR/some/path */ start = ++p; /* skip dollar sign */ while (*p && *p != NCXMOD_PSCHAR) { p++; } userlen = (uint32)(p-start); if (userlen) { user = ncxmod_get_envvar(start, userlen); } len = 0; if (!user) { /* ignoring this environment variable * could be something like $DESTDIR/usr/share */ if (LOGDEBUG) { log_debug("\nEnvironment variable not " "found in path string (%s)", fspec); } } else { len = xml_strlen(user); } /* string pointer 'p' stopped on the PSCHAR to start the * rest of the path string */ len += xml_strlen(p); buff = m__getMem(len+1); if (buff == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } bp = buff; if (user) { bp += xml_strcpy(bp, user); } xml_strcpy(bp, p); } else if ((*p == NCXMOD_DOTCHAR && p[1] == NCXMOD_PSCHAR) || (*p != NCXMOD_DOTCHAR && expand_cwd)) { /* check for ./some/path or some/path */ with_dot = (*p == NCXMOD_DOTCHAR); if (with_dot) { p++; } /* prepend string with current directory */ buff = m__getMem(DIRBUFF_SIZE); if (buff == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } if (!getcwd((char *)buff, DIRBUFF_SIZE)) { SET_ERROR(ERR_BUFF_OVFL); m__free(buff); return NULL; } bufflen = xml_strlen(buff); if ((bufflen + xml_strlen(p) + 2) >= DIRBUFF_SIZE) { *res = ERR_BUFF_OVFL; m__free(buff); return NULL; } if (!with_dot) { buff[bufflen++] = NCXMOD_PSCHAR; } xml_strcpy(&buff[bufflen], p); } else { /* probably contains ../ and that is not handled yet */ buff = xml_strdup(fspec); if (buff == NULL) { *res = ERR_INTERNAL_MEM; } } return buff; } /* ncx_get_source_ex */ /******************************************************************** * FUNCTION ncx_get_source * * Get a malloced buffer containing the complete filespec * for the given input string. If this is a complete dirspec, * this this will just strdup the value. * * This is just a best effort to get the full spec. * If the full spec is greater than 1500 bytes, * then a NULL value (error) will be returned * * This will expand the cwd! * * - Change ./ --> cwd/ * - Remove ~/ --> $HOME * - add trailing '/' if not present * * INPUTS: * fspec == input filespec * res == address of return status * * OUTPUTS: * *res == return status, NO_ERR if return is non-NULL * * RETURNS: * malloced buffer containing possibly expanded full filespec *********************************************************************/ xmlChar * ncx_get_source (const xmlChar *fspec, status_t *res) { return ncx_get_source_ex(fspec, TRUE, res); } /* ncx_get_source */ /******************************************************************** * FUNCTION ncx_set_cur_modQ * * Set the current module Q to an alternate (for yangdiff) * This will be used for module searches usually in ncx_modQ * * INPUTS: * que == Q of ncx_module_t to use *********************************************************************/ void ncx_set_cur_modQ (dlq_hdr_t *que) { assert ( que && " param que is NULL"); ncx_curQ = que; } /* ncx_set_cur_modQ */ /******************************************************************** * FUNCTION ncx_get_cur_modQ * * Get the current module Q * This will be used for module searches usually in ncx_modQ * * INPUTS: * que == Q of ncx_module_t to use *********************************************************************/ dlq_hdr_t * ncx_get_cur_modQ (void) { return ncx_curQ; } /* ncx_get_cur_modQ */ /******************************************************************** * FUNCTION ncx_reset_modQ * * Set the current module Q to the original ncx_modQ * *********************************************************************/ void ncx_reset_modQ (void) { ncx_curQ = &ncx_modQ; } /* ncx_reset_modQ */ /******************************************************************** * FUNCTION ncx_set_session_modQ * * !!! THIS HACK IS NEEDED BECAUSE val.c * !!! USES ncx_find_module sometimes, and * !!! yangcli sessions are not loaded into the * !!! main database of modules. * !!! THIS DOES NOT WORK FOR MULTIPLE CONCURRENT PROCESSES * * Set the current session module Q to an alternate (for yangdiff) * This will be used for module searches usually in ncx_modQ * * INPUTS: * que == Q of ncx_module_t to use *********************************************************************/ void ncx_set_session_modQ (dlq_hdr_t *que) { assert ( que && " param que is NULL"); ncx_sesmodQ = que; } /* ncx_set_sesion_modQ */ /******************************************************************** * FUNCTION ncx_clear_session_modQ * * !!! THIS HACK IS NEEDED BECAUSE val.c * !!! USES ncx_find_module sometimes, and * !!! yangcli sessions are not loaded into the * !!! main database of modules. * !!! THIS DOES NOT WORK FOR MULTIPLE CONCURRENT PROCESSES * * Clear the current session module Q * *********************************************************************/ void ncx_clear_session_modQ (void) { ncx_sesmodQ = NULL; } /* ncx_clear_sesion_modQ */ /******************************************************************** * FUNCTION ncx_set_load_callback * * Set the callback function for a load-module event * * INPUT: * cbfn == callback function to use * *********************************************************************/ void ncx_set_load_callback (ncx_load_cbfn_t cbfn) { mod_load_callback = cbfn; } /* ncx_set_load_callback */ /******************************************************************** * FUNCTION ncx_prefix_different * * Check if the specified prefix pair reference different modules * * INPUT: * prefix1 == 1st prefix to check (may be NULL) * prefix2 == 2nd prefix to check (may be NULL) * modprefix == module prefix to check (may be NULL) * * RETURNS: * TRUE if prefix1 and prefix2 reference different modules * FALSE if prefix1 and prefix2 reference the same modules *********************************************************************/ boolean ncx_prefix_different (const xmlChar *prefix1, const xmlChar *prefix2, const xmlChar *modprefix) { if (!prefix1) { prefix1 = modprefix; } if (!prefix2) { prefix2 = modprefix; } if (prefix1 == prefix2) { return FALSE; } if (prefix1 == NULL || prefix2 == NULL) { return TRUE; } return (xml_strcmp(prefix1, prefix2)) ? TRUE : FALSE; } /* ncx_prefix_different */ /******************************************************************** * FUNCTION ncx_get_baddata_enum * * Check if the specified string matches an ncx_baddata_t enum * * INPUT: * valstr == value string to check * * RETURNS: * enum value if OK * NCX_BAD_DATA_NONE if an error *********************************************************************/ ncx_bad_data_t ncx_get_baddata_enum (const xmlChar *valstr) { assert ( valstr && " param valstr is NULL"); if (!xml_strcmp(valstr, E_BAD_DATA_IGNORE)) { return NCX_BAD_DATA_IGNORE; } else if (!xml_strcmp(valstr, E_BAD_DATA_WARN)) { return NCX_BAD_DATA_WARN; } else if (!xml_strcmp(valstr, E_BAD_DATA_CHECK)) { return NCX_BAD_DATA_CHECK; } else if (!xml_strcmp(valstr, E_BAD_DATA_ERROR)) { return NCX_BAD_DATA_ERROR; } else { return NCX_BAD_DATA_NONE; } /*NOTREACHED*/ } /* ncx_get_baddata_enum */ /******************************************************************** * FUNCTION ncx_get_baddata_string * * Get the string for the specified enum value * * INPUT: * baddatar == enum value to check * * RETURNS: * string pointer if OK * NULL if an error *********************************************************************/ const xmlChar * ncx_get_baddata_string (ncx_bad_data_t baddata) { switch (baddata) { case NCX_BAD_DATA_NONE: return NCX_EL_NONE; case NCX_BAD_DATA_IGNORE: return E_BAD_DATA_IGNORE; case NCX_BAD_DATA_WARN: return E_BAD_DATA_WARN; case NCX_BAD_DATA_CHECK: return E_BAD_DATA_CHECK; case NCX_BAD_DATA_ERROR: return E_BAD_DATA_ERROR; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* ncx_get_baddata_string */ /******************************************************************** * FUNCTION ncx_get_withdefaults_string * * Get the string for the specified enum value * * INPUT: * withdef == enum value to check * * RETURNS: * string pointer if OK * NULL if an error *********************************************************************/ const xmlChar * ncx_get_withdefaults_string (ncx_withdefaults_t withdef) { switch (withdef) { case NCX_WITHDEF_NONE: return NCX_EL_NONE; case NCX_WITHDEF_REPORT_ALL: return NCX_EL_REPORT_ALL; case NCX_WITHDEF_REPORT_ALL_TAGGED: return NCX_EL_REPORT_ALL_TAGGED; case NCX_WITHDEF_TRIM: return NCX_EL_TRIM; case NCX_WITHDEF_EXPLICIT: return NCX_EL_EXPLICIT; default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } } /* ncx_get_withdefaults_string */ /******************************************************************** * FUNCTION ncx_get_withdefaults_enum * * Get the enum for the specified string value * * INPUT: * withdefstr == string value to check * * RETURNS: * enum value for the string * NCX_WITHDEF_NONE if invalid value *********************************************************************/ ncx_withdefaults_t ncx_get_withdefaults_enum (const xmlChar *withdefstr) { assert ( withdefstr && " param withdefstr is NULL"); if (!xml_strcmp(withdefstr, NCX_EL_REPORT_ALL)) { return NCX_WITHDEF_REPORT_ALL; } else if (!xml_strcmp(withdefstr, NCX_EL_REPORT_ALL_TAGGED)) { return NCX_WITHDEF_REPORT_ALL_TAGGED; } else if (!xml_strcmp(withdefstr, NCX_EL_TRIM)) { return NCX_WITHDEF_TRIM; } else if (!xml_strcmp(withdefstr, NCX_EL_EXPLICIT)) { return NCX_WITHDEF_EXPLICIT; } else { return NCX_WITHDEF_NONE; } } /* ncx_get_withdefaults_enum */ /******************************************************************** * FUNCTION ncx_get_mod_prefix * * Get the module prefix for the specified module * * INPUT: * mod == module to check * * RETURNS: * pointer to module YANG prefix *********************************************************************/ const xmlChar * ncx_get_mod_prefix (const ncx_module_t *mod) { assert ( mod && " param mod is NULL"); return mod->prefix; } /* ncx_get_mod_prefix */ /******************************************************************** * FUNCTION ncx_get_mod_xmlprefix * * Get the module XML prefix for the specified module * * INPUT: * mod == module to check * * RETURNS: * pointer to module XML prefix *********************************************************************/ const xmlChar * ncx_get_mod_xmlprefix (const ncx_module_t *mod) { assert ( mod && " param mod is NULL"); if (mod->xmlprefix) { return mod->xmlprefix; } else { return mod->prefix; } } /* ncx_get_mod_xmlprefix */ /******************************************************************** * FUNCTION ncx_get_display_mode_enum * * Get the enum for the specified string value * * INPUT: * dmstr == string value to check * * RETURNS: * enum value for the string * NCX_DISPLAY_MODE_NONE if invalid value *********************************************************************/ ncx_display_mode_t ncx_get_display_mode_enum (const xmlChar *dmstr) { assert ( dmstr && " param dmstr is NULL" ); if (!xml_strcmp(dmstr, NCX_EL_PLAIN)) { return NCX_DISPLAY_MODE_PLAIN; } else if (!xml_strcmp(dmstr, NCX_EL_PREFIX)) { return NCX_DISPLAY_MODE_PREFIX; } else if (!xml_strcmp(dmstr, NCX_EL_MODULE)) { return NCX_DISPLAY_MODE_MODULE; } else if (!xml_strcmp(dmstr, NCX_EL_XML)) { return NCX_DISPLAY_MODE_XML; } else if (!xml_strcmp(dmstr, NCX_EL_XML_NONS)) { return NCX_DISPLAY_MODE_XML_NONS; } else if (!xml_strcmp(dmstr, NCX_EL_JSON)) { return NCX_DISPLAY_MODE_JSON; } else { return NCX_DISPLAY_MODE_NONE; } } /* ncx_get_display_mode_enum */ /******************************************************************** * FUNCTION ncx_get_display_mode_str * * Get the string for the specified enum value * * INPUT: * dmode == enum display mode value to check * * RETURNS: * string value for the enum * NULL if none found *********************************************************************/ const xmlChar * ncx_get_display_mode_str (ncx_display_mode_t dmode) { switch (dmode) { case NCX_DISPLAY_MODE_NONE: return NULL; case NCX_DISPLAY_MODE_PLAIN: return NCX_EL_PLAIN; case NCX_DISPLAY_MODE_PREFIX: return NCX_EL_PREFIX; case NCX_DISPLAY_MODE_MODULE: return NCX_EL_MODULE; case NCX_DISPLAY_MODE_XML: return NCX_EL_XML; case NCX_DISPLAY_MODE_XML_NONS: return NCX_EL_XML_NONS; case NCX_DISPLAY_MODE_JSON: return NCX_EL_JSON; default: return NULL; } } /* ncx_get_display_mode_str */ /******************************************************************** * FUNCTION ncx_set_warn_idlen * * Set the warning length for identifiers * * INPUT: * warnlen == warning length to use * *********************************************************************/ void ncx_set_warn_idlen (uint32 warnlen) { warn_idlen = warnlen; } /* ncx_set_warn_idlen */ /******************************************************************** * FUNCTION ncx_get_warn_idlen * * Get the warning length for identifiers * * RETURNS: * warning length to use *********************************************************************/ uint32 ncx_get_warn_idlen (void) { return warn_idlen; } /* ncx_get_warn_idlen */ /******************************************************************** * FUNCTION ncx_set_warn_linelen * * Set the warning length for YANG file lines * * INPUT: * warnlen == warning length to use * *********************************************************************/ void ncx_set_warn_linelen (uint32 warnlen) { warn_linelen = warnlen; } /* ncx_set_warn_linelen */ /******************************************************************** * FUNCTION ncx_get_warn_linelen * * Get the warning length for YANG file lines * * RETURNS: * warning length to use * *********************************************************************/ uint32 ncx_get_warn_linelen (void) { return warn_linelen; } /* ncx_get_warn_linelen */ /******************************************************************** * FUNCTION ncx_check_warn_idlen * * Check if the identifier length is greater than * the specified amount. * * INPUTS: * tkc == token chain to use (for warning message only) * mod == module (for warning message only) * id == identifier string to check; must be Z terminated * * OUTPUTS: * may generate log_warn ouput *********************************************************************/ void ncx_check_warn_idlen (tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *id) { assert ( id && " param id is NULL" ); uint32 idlen; if (!warn_idlen) { return; } idlen = xml_strlen(id); if (idlen > warn_idlen) { log_warn("\nWarning: identifier '%s' length is %u chars, " "limit is %u chars", id, idlen, warn_idlen); ncx_print_errormsg(tkc, mod, ERR_NCX_IDLEN_EXCEEDED); } } /* ncx_check_warn_idlen */ /******************************************************************** * FUNCTION ncx_check_warn_linelen * * Check if the line display length is greater than * the specified amount. * * INPUTS: * tkc == token chain to use * mod == module (used in warning message only * linelen == line length to check * * OUTPUTS: * may generate log_warn ouput *********************************************************************/ void ncx_check_warn_linelen (tk_chain_t *tkc, ncx_module_t *mod, const xmlChar *line) { assert ( line && " param line is NULL" ); const xmlChar *str; uint32 len; if (!warn_linelen) { return; } str = line; len = 0; /* check for line starting with newline */ if (*str == '\n') { str++; } /* get the display length */ while (*str && *str != '\n') { if (*str == '\t') { len += 8; } else { len++; } str++; } if (len > warn_linelen) { log_warn("\nWarning: line is %u chars, limit is %u chars", len, warn_linelen); ncx_print_errormsg(tkc, mod, ERR_NCX_LINELEN_EXCEEDED); } } /* ncx_check_warn_linelen */ /******************************************************************** * FUNCTION ncx_turn_off_warning * * Add a warning suppression entry * * INPUTS: * res == internal status code to suppress * * RETURNS: * status (duplicates are silently dropped) *********************************************************************/ status_t ncx_turn_off_warning (status_t res) { warnoff_t *warnoff; if (res == NO_ERR) { return SET_ERROR(ERR_INTERNAL_VAL); } if (res < ERR_WARN_BASE) { return ERR_NCX_INVALID_VALUE; } /* check if 'res' already entered */ for (warnoff = (warnoff_t *)dlq_firstEntry(&warnoffQ); warnoff != NULL; warnoff = (warnoff_t *)dlq_nextEntry(warnoff)) { if (warnoff->res == res) { return NO_ERR; } } warnoff = m__getObj(warnoff_t); if (!warnoff) { return ERR_INTERNAL_MEM; } memset(warnoff, 0x0, sizeof(warnoff_t)); warnoff->res = res; dlq_enque(warnoff, &warnoffQ); return NO_ERR; } /* ncx_turn_off_warning */ /******************************************************************** * FUNCTION ncx_turn_on_warning * * Remove a warning suppression entry if it exists * * INPUTS: * res == internal status code to enable * * RETURNS: * status (duplicates are silently dropped) *********************************************************************/ status_t ncx_turn_on_warning (status_t res) { warnoff_t *warnoff; if (res == NO_ERR) { return SET_ERROR(ERR_INTERNAL_VAL); } if (res < ERR_WARN_BASE) { return ERR_NCX_INVALID_VALUE; } /* check if 'res' exists and remove it if found */ for (warnoff = (warnoff_t *)dlq_firstEntry(&warnoffQ); warnoff != NULL; warnoff = (warnoff_t *)dlq_nextEntry(warnoff)) { if (warnoff->res == res) { dlq_remove(warnoff); m__free(warnoff); return NO_ERR; } } return NO_ERR; } /* ncx_turn_on_warning */ /******************************************************************** * FUNCTION ncx_warning_enabled * * Check if a specific status_t code is enabled * * INPUTS: * res == internal status code to check * * RETURNS: * TRUE if warning is enabled * FALSE if warning is suppressed *********************************************************************/ boolean ncx_warning_enabled (status_t res) { const warnoff_t *warnoff; if (res < ERR_WARN_BASE) { return TRUE; } if (!LOGWARN) { return FALSE; } /* check if 'res' already entered */ for (warnoff = (const warnoff_t *)dlq_firstEntry(&warnoffQ); warnoff != NULL; warnoff = (const warnoff_t *)dlq_nextEntry(warnoff)) { if (warnoff->res == res) { return FALSE; } } return TRUE; } /* ncx_warning_enabled */ /******************************************************************** * FUNCTION ncx_get_version * * Get the the Yuma version ID string * * INPUT: * buffer == buffer to hold the version string * buffsize == number of bytes in buffer * * RETURNS: * status *********************************************************************/ status_t ncx_get_version (xmlChar *buffer, uint32 buffsize) { xmlChar *str; uint32 versionlen; #ifdef RELEASE xmlChar numbuff[NCX_MAX_NUMLEN]; #endif assert ( buffer && " param buffer is NULL" ); #ifdef RELEASE snprintf((char *)numbuff, sizeof(numbuff), "%d", RELEASE); versionlen = xml_strlen(YUMA_VERSION) + xml_strlen(numbuff) + 1; if (versionlen >= buffsize) { return ERR_BUFF_OVFL; } str = buffer; str += xml_strcpy(str, YUMA_VERSION); *str++ = '-'; xml_strcpy(str, numbuff); #else versionlen = xml_strlen(YUMA_VERSION) + xml_strlen((const xmlChar *)SVNVERSION) + 1; if (versionlen >= buffsize) { return ERR_BUFF_OVFL; } str = buffer; str += xml_strcpy(str, YUMA_VERSION); *str++ = '.'; xml_strcpy(str, (const xmlChar *)SVNVERSION); #endif return NO_ERR; } /* ncx_get_version */ /******************************************************************** * FUNCTION ncx_new_save_deviations * * create a deviation save structure * * INPUTS: * devmodule == deviations module name * devrevision == deviation module revision (optional) * devnamespace == deviation module namespace URI value * devprefix == local module prefix (optional) * * RETURNS: * malloced and initialized save_deviations struct, * or NULL if malloc error *********************************************************************/ ncx_save_deviations_t * ncx_new_save_deviations (const xmlChar *devmodule, const xmlChar *devrevision, const xmlChar *devnamespace, const xmlChar *devprefix) { assert ( devmodule && " param devmodule is NULL" ); assert ( devnamespace && " param devnamespace is NULL" ); ncx_save_deviations_t *savedev; savedev = m__getObj(ncx_save_deviations_t); if (savedev == NULL) { return NULL; } memset(savedev, 0x0, sizeof(ncx_save_deviations_t)); dlq_createSQue(&savedev->importQ); dlq_createSQue(&savedev->deviationQ); savedev->devmodule = xml_strdup(devmodule); if (savedev->devmodule == NULL) { ncx_free_save_deviations(savedev); return NULL; } if (devprefix) { savedev->devprefix = xml_strdup(devprefix); if (savedev->devprefix == NULL) { ncx_free_save_deviations(savedev); return NULL; } } if (devrevision) { savedev->devrevision = xml_strdup(devrevision); if (savedev->devrevision == NULL) { ncx_free_save_deviations(savedev); return NULL; } } savedev->devnamespace = xml_strdup(devnamespace); if (savedev->devnamespace == NULL) { ncx_free_save_deviations(savedev); return NULL; } return savedev; } /* ncx_new_save_deviations */ /******************************************************************** * FUNCTION ncx_free_save_deviations * * free a deviation save struct * * INPUT: * savedev == struct to clean and delete *********************************************************************/ void ncx_free_save_deviations (ncx_save_deviations_t *savedev) { assert ( savedev && " param savedev is NULL" ); ncx_import_t *import; obj_deviation_t *deviation; while (!dlq_empty(&savedev->importQ)) { import = (ncx_import_t *) dlq_deque(&savedev->importQ); ncx_free_import(import); } while (!dlq_empty(&savedev->deviationQ)) { deviation = (obj_deviation_t *) dlq_deque(&savedev->deviationQ); obj_free_deviation(deviation); } if (savedev->devmodule) { m__free(savedev->devmodule); } if (savedev->devrevision) { m__free(savedev->devrevision); } if (savedev->devnamespace) { m__free(savedev->devnamespace); } if (savedev->devprefix) { m__free(savedev->devprefix); } m__free(savedev); } /* ncx_free_save_deviations */ /******************************************************************** * FUNCTION ncx_clean_save_deviationsQ * * clean a Q of deviation save structs * * INPUT: * savedevQ == Q of ncx_save_deviations_t to clean *********************************************************************/ void ncx_clean_save_deviationsQ (dlq_hdr_t *savedevQ) { ncx_save_deviations_t *savedev; while (!dlq_empty(savedevQ)) { savedev = (ncx_save_deviations_t *)dlq_deque(savedevQ); ncx_free_save_deviations(savedev); } } /* ncx_clean_save_deviationsQ */ /******************************************************************** * FUNCTION ncx_set_error * * Set the fields in an ncx_error_t struct * When called from NACM or internally, there is no * module or line number info * * INPUTS: * tkerr== address of ncx_error_t struct to set * mod == [sub]module containing tkerr * linenum == current linenum * linepos == current column position on the current line * * OUTPUTS: * *tkerr is filled in *********************************************************************/ void ncx_set_error( ncx_error_t *tkerr, ncx_module_t *mod, uint32 linenum, uint32 linepos ) { assert ( tkerr && " param tkerr is NULL" ); tkerr->mod = mod; tkerr->linenum = linenum; tkerr->linepos = linepos; } /* ncx_set_error */ /******************************************************************** * FUNCTION ncx_set_temp_modQ * * Set the temp_modQ for yangcli session-specific module list * * INPUTS: * modQ == new Q pointer to use * *********************************************************************/ void ncx_set_temp_modQ (dlq_hdr_t *modQ) { assert ( modQ && " param modQ is NULL" ); temp_modQ = modQ; } /* ncx_set_temp_modQ */ /******************************************************************** * FUNCTION ncx_get_temp_modQ * * Get the temp_modQ for yangcli session-specific module list * * RETURNS: * pointer to the temp modQ, if set *********************************************************************/ dlq_hdr_t * ncx_get_temp_modQ (void) { return temp_modQ; } /* ncx_get_temp_modQ */ /******************************************************************** * FUNCTION ncx_clear_temp_modQ * * Clear the temp_modQ for yangcli session-specific module list * *********************************************************************/ void ncx_clear_temp_modQ (void) { temp_modQ = NULL; } /* ncx_clear_temp_modQ */ /******************************************************************** * FUNCTION ncx_get_display_mode * * Get the current default display mode * * RETURNS: * the current dispay mode enumeration *********************************************************************/ ncx_display_mode_t ncx_get_display_mode (void) { return display_mode; } /* ncx_get_display_mode */ /******************************************************************** * FUNCTION ncx_get_confirm_event_str * * Get the string for the specified enum value * * INPUT: * event == enum confirm event value to convert * * RETURNS: * string value for the enum * NULL if none found *********************************************************************/ const xmlChar * ncx_get_confirm_event_str (ncx_confirm_event_t event) { switch (event) { case NCX_CC_EVENT_NONE: return NULL; case NCX_CC_EVENT_START: return NCX_EL_START; case NCX_CC_EVENT_CANCEL: return NCX_EL_CANCEL; case NCX_CC_EVENT_TIMEOUT: return NCX_EL_TIMEOUT; case NCX_CC_EVENT_EXTEND: return NCX_EL_EXTEND; case NCX_CC_EVENT_COMPLETE: return NCX_EL_COMPLETE; default: return NULL; } } /* ncx_get_confirm_event_str */ /******************************************************************** * FUNCTION ncx_mod_revision_count * * Find all the ncx_module_t structs in the ncx_modQ * that have the same module name * * INPUTS: * modname == module name * * RETURNS: * count of modules that have this name (exact match) *********************************************************************/ uint32 ncx_mod_revision_count (const xmlChar *modname) { assert ( modname && " param modname is NULL" ); uint32 count; if (ncx_sesmodQ != NULL) { count = ncx_mod_revision_count_que(ncx_sesmodQ, modname); } else { count = ncx_mod_revision_count_que(ncx_curQ, modname); } return count; } /* ncx_mod_revision_count */ /******************************************************************** * FUNCTION ncx_mod_revision_count_que * * Find all the ncx_module_t structs in the specified queue * that have the same module name * * INPUTS: * modQ == queue of ncx_module_t structs to check * modname == module name * * RETURNS: * count of modules that have this name (exact match) *********************************************************************/ uint32 ncx_mod_revision_count_que (dlq_hdr_t *modQ, const xmlChar *modname) { assert ( modQ && " param modQ is NULL" ); assert ( modname && " param modname is NULL" ); ncx_module_t *mod; uint32 count; int32 retval; count = 0; for (mod = (ncx_module_t *)dlq_firstEntry(modQ); mod != NULL; mod = (ncx_module_t *)dlq_nextEntry(mod)) { retval = xml_strcmp(modname, mod->name); if (retval == 0) { count++; } } return count; } /* ncx_mod_revision_count_que */ /******************************************************************** * FUNCTION ncx_get_allincQ * * Find the correct Q of yang_node_t for all include files * that have the same 'belongs-to' value * * INPUTS: * mod == module to check * * RETURNS: * pointer to Q of all include nodes *********************************************************************/ dlq_hdr_t * ncx_get_allincQ (ncx_module_t *mod) { assert ( mod && " param mod is NULL" ); while (mod->parent != NULL) { mod = mod->parent; } return &mod->allincQ; } /* ncx_get_allincQ */ /******************************************************************** * FUNCTION ncx_get_const_allincQ * * Find the correct Q of yang_node_t for all include files * that have the same 'belongs-to' value (const version) * * INPUTS: * mod == module to check * * RETURNS: * pointer to Q of all include nodes *********************************************************************/ const dlq_hdr_t * ncx_get_const_allincQ (const ncx_module_t *mod) { assert ( mod && " param mod is NULL" ); while (mod->parent != NULL) { mod = mod->parent; } return &mod->allincQ; } /* ncx_get_allincQ */ /******************************************************************** * FUNCTION ncx_get_parent_mod * * Find the correct module by checking mod->parent nodes * * INPUTS: * mod == module to check * * RETURNS: * pointer to parent module (!! not submodule !!) * NULL if none found *********************************************************************/ ncx_module_t * ncx_get_parent_mod (ncx_module_t *mod) { assert ( mod && " param mod is NULL" ); while (mod->parent != NULL) { mod = mod->parent; if (mod != NULL && mod->ismod) { return mod; } } return NULL; } /* ncx_get_parent_mod */ /******************************************************************** * FUNCTION ncx_get_vtimeout_value * * Get the virtual node cache timeout value * * RETURNS: * number of seconds for the cache timeout; 0 == disabled *********************************************************************/ uint32 ncx_get_vtimeout_value (void) { return ncx_vtimeout; } /* ncx_get_vtimeout_value */ /******************************************************************** * FUNCTION ncx123_set_vtimeout_value * * Get the virtual node cache timeout value * * INPUTS: * sec == number of seconds for the cache timeout; 0 == disabled *********************************************************************/ void ncx123_set_vtimeout_value (uint32 sec) { ncx_vtimeout=sec; } /* ncx123_set_vtimeout_value */ /******************************************************************** * FUNCTION ncx_compare_base_uris * * Compare the base part of 2 URI strings * * INPUTS: * str1 == URI string 1 * str2 == URI string 2 * * RETURNS: * compare of base parts (up to '?') * -1, 0 or 1 *********************************************************************/ int32 ncx_compare_base_uris (const xmlChar *str1, const xmlChar *str2) { assert ( str1 && " param str1 is NULL" ); assert ( str2 && " param str2 is NULL" ); const xmlChar *s; uint32 len1, len2; s = str1; while (*s && *s != '?') { s++; } len1 = (uint32)(s - str1); s = str2; while (*s && *s != '?') { s++; } len2 = (uint32)(s - str2); if (len1 != len2) { return (len1 > len2) ? 1 : -1; } if (len1 == 0) { return 0; } return xml_strncmp(str1, str2, len1); } /* ncx_compare_base_uris */ /******************************************************************** * FUNCTION ncx_get_useprefix * * Get the use_prefix value * * RETURNS: * TRUE if XML prefixes should be used * FALSE if XML messages should use default namespace (no prefix) *********************************************************************/ boolean ncx_get_useprefix (void) { return use_prefix; } /* ncx_get_useprefix */ /******************************************************************** * FUNCTION ncx_set_useprefix * * Set the use_prefix value * * INPUTS: * val == * TRUE if XML prefixes should be used * FALSE if XML messages should use default namespace (no prefix) *********************************************************************/ void ncx_set_useprefix (boolean val) { use_prefix = val; } /* ncx_set_useprefix */ /******************************************************************** * FUNCTION ncx_get_system_sorted * * Get the system_sorted value * * RETURNS: * TRUE if system ordered objects should be sorted * FALSE if system ordered objects should not be sorted *********************************************************************/ boolean ncx_get_system_sorted (void) { return system_sorted; } /* ncx_get_system_sorted */ /******************************************************************** * FUNCTION ncx_set_system_sorted * * Set the system_sorted value * * INPUTS: * val == * TRUE if system ordered objects should be sorted * FALSE if system ordered objects should not be sorted *********************************************************************/ void ncx_set_system_sorted (boolean val) { system_sorted = val; } /* ncx_set_system_sorted */ /******************************************************************** * FUNCTION ncx_inc_warnings * * Increment the module warning count * * INPUTS: * mod == module being parsed *********************************************************************/ void ncx_inc_warnings (ncx_module_t *mod) { assert ( mod && " param mod is NULL" ); mod->warnings++; } /* ncx_inc_warnings */ /******************************************************************** * FUNCTION ncx_get_cwd_subdirs * * Get the CLI parameter value whether to search for modules * in subdirs of the CWD by default. Does not affect YUMA_MODPATH * or other hard-wired searches * * RETURNS: * TRUE if ncxmod should search for modules in subdirs of the CWD * FALSE if ncxmod should not search for modules in subdirs of the CWD *********************************************************************/ boolean ncx_get_cwd_subdirs (void) { return cwd_subdirs; } /* ncx_get_cwd_subdirs */ /******************************************************************** * FUNCTION ncx_protocol_enabled * * Check if the specified protocol version is enabled * * RETURNS: * TRUE if protocol enabled * FALSE if protocol not enabled or error *********************************************************************/ boolean ncx_protocol_enabled (ncx_protocol_t proto) { boolean ret = FALSE; switch (proto) { case NCX_PROTO_NETCONF10: ret = (protocols_enabled & NCX_FL_PROTO_NETCONF10) ? TRUE : FALSE; break; case NCX_PROTO_NETCONF11: ret = (protocols_enabled & NCX_FL_PROTO_NETCONF11) ? TRUE : FALSE; break; default: SET_ERROR(ERR_INTERNAL_VAL); } return ret; } /* ncx_protocol_enabled */ /******************************************************************** * FUNCTION ncx_set_protocol_enabled * * Set the specified protocol version to be enabled * * INPUTS: * proto == protocol version to enable *********************************************************************/ void ncx_set_protocol_enabled (ncx_protocol_t proto) { switch (proto) { case NCX_PROTO_NETCONF10: protocols_enabled |= NCX_FL_PROTO_NETCONF10; break; case NCX_PROTO_NETCONF11: protocols_enabled |= NCX_FL_PROTO_NETCONF11; break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* ncx_set_protocol_enabled */ /******************************************************************** * FUNCTION ncx_set_use_deadmodQ * * Set the usedeadmodQ flag * *********************************************************************/ void ncx_set_use_deadmodQ (void) { usedeadmodQ = TRUE; } /* ncx_set_use_deadmodQ */ /******************************************************************** * FUNCTION ncx_delete_all_obsolete_objects * * Go through all the modules and delete the obsolete nodes * *********************************************************************/ void ncx_delete_all_obsolete_objects (void) { ncx_module_t *mod; for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { ncx_delete_mod_obsolete_objects(mod); } } /* ncx_delete_all_obsolete_objects */ /******************************************************************** * FUNCTION ncx_delete_mod_obsolete_objects * * Go through one module and delete the obsolete nodes * * INPUTS: * mod == module to check *********************************************************************/ void ncx_delete_mod_obsolete_objects (ncx_module_t *mod) { yang_node_t *node; obj_delete_obsolete(&mod->datadefQ); if (!mod->ismod) { return; } for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod == NULL) { SET_ERROR(ERR_INTERNAL_PTR); continue; } obj_delete_obsolete(&node->submod->datadefQ); } } /* ncx_delete_mod_obsolete_objects */ /******************************************************************** * FUNCTION ncx_get_name_match_enum * * Get the enum for the string name of a ncx_name_match_t enum * * INPUTS: * str == string name of the enum value * * RETURNS: * enum value *********************************************************************/ ncx_name_match_t ncx_get_name_match_enum (const xmlChar *str) { assert ( str && " param str is NULL" ); if (!xml_strcmp(NCX_EL_EXACT, str)) { return NCX_MATCH_EXACT; } else if (!xml_strcmp(NCX_EL_EXACT_NOCASE, str)) { return NCX_MATCH_EXACT_NOCASE; } else if (!xml_strcmp(NCX_EL_ONE, str)) { return NCX_MATCH_ONE; } else if (!xml_strcmp(NCX_EL_ONE_NOCASE, str)) { return NCX_MATCH_ONE_NOCASE; } else if (!xml_strcmp(NCX_EL_FIRST, str)) { return NCX_MATCH_FIRST; } else if (!xml_strcmp(NCX_EL_FIRST_NOCASE, str)) { return NCX_MATCH_FIRST_NOCASE; } else { return NCX_MATCH_NONE; } /*NOTREACHED*/ } /* ncx_get_name_match_enum */ /******************************************************************** * FUNCTION ncx_get_name_match_string * * Get the string for the ncx_name_match_t enum * * INPUTS: * match == enum value * * RETURNS: * string value *********************************************************************/ const xmlChar * ncx_get_name_match_string (ncx_name_match_t match) { switch (match) { case NCX_MATCH_NONE: return NCX_EL_NONE; case NCX_MATCH_EXACT: return NCX_EL_EXACT; case NCX_MATCH_EXACT_NOCASE: return NCX_EL_EXACT_NOCASE; case NCX_MATCH_ONE: return NCX_EL_ONE; case NCX_MATCH_ONE_NOCASE: return NCX_EL_ONE_NOCASE; case NCX_MATCH_FIRST: return NCX_EL_FIRST; case NCX_MATCH_FIRST_NOCASE: return NCX_EL_FIRST_NOCASE; default: SET_ERROR(ERR_INTERNAL_VAL); return NCX_EL_NONE; } /*NOTREACHED*/ } /* ncx_get_name_match_string */ /******************************************************************** * FUNCTION ncx_write_tracefile * * Write a byte to the tracefile * * INPUTS: * buff == buffer to write * count == number of chars to write *********************************************************************/ void ncx_write_tracefile (const char *buff, uint32 count) { uint32 i; if (tracefile != NULL) { for (i = 0; i < count; i++) { fputc((int)buff[i], tracefile); } } } /* ncx_write_tracefile */ /******************************************************************** * FUNCTION ncx_set_top_mandatory_allowed * * Allow or disallow modules with top-level mandatory object * to be loaded; used by the server when agt_running_error is TRUE * * INPUTS: * allowed == value to set T: to allow; F: to disallow *********************************************************************/ void ncx_set_top_mandatory_allowed (boolean allowed) { allow_top_mandatory = allowed; } /******************************************************************** * FUNCTION ncx_get_top_mandatory_allowed * * Check if top-level mandatory objects are allowed or not * * RETURNS: * T: allowed; F: disallowed *********************************************************************/ boolean ncx_get_top_mandatory_allowed (void) { return allow_top_mandatory; } /******************************************************************** * FUNCTION identity_get_first_iffeature * * Get the first if-feature clause (if any) for the specified identity * * INPUTS: * identity == identity structure to check * * RETURNS: * pointer to first if-feature struct * NULL if none available *********************************************************************/ const ncx_iffeature_t * identity_get_first_iffeature (const ncx_identity_t *identity) { #ifdef DEBUG if (!identity) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (const ncx_iffeature_t *) dlq_firstEntry(&identity->iffeatureQ); } /* identity_get_first_iffeature */ /******************************************************************** * FUNCTION identity_get_next_iffeature * * Get the next if-feature clause (if any) * * INPUTS: * iffeature == current iffeature struct * * RETURNS: * pointer to next if-feature struct * NULL if none available *********************************************************************/ const ncx_iffeature_t * identity_get_next_iffeature (const ncx_iffeature_t *iffeature) { #ifdef DEBUG if (!iffeature) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (const ncx_iffeature_t *)dlq_nextEntry(iffeature); } /* identity_get_next_iffeature */ /******************************************************************** * Check any if-feature statement that may * cause the specified object to be invisible * * \param obj the obj_template to check * \return TRUE if object is enabled *********************************************************************/ boolean identity_is_enabled (const ncx_identity_t *identity) { assert(identity && "identity is NULL" ); const ncx_iffeature_t *iffeature = identity_get_first_iffeature(identity); for ( ; iffeature ; iffeature = identity_get_next_iffeature(iffeature)) { if (!iffeature->feature || !ncx_feature_enabled(iffeature->feature)) { return FALSE; } } return TRUE; } /* END file ncx.c */ yuma123_2.14/netconf/src/ncx/xml_val.h0000664000175000017500000001672114770023131017760 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xml_val #define _H_xml_val /* FILE: xml_val.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Utility functions for creating value structs ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24-nov-06 abb Begun; split from xsd.c 16-jan-07 abb Moved from ncxdump/xml_val_util.h */ #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xml_val_make_qname * * Build output value: add child node to a struct node * Malloc a string buffer and create a QName string * This is complete; The m__free function must be called * with the return value if it is non-NULL; * * INPUTS: * nsid == namespace ID to use * name == condition clause (may be NULL) * * RETURNS: * malloced value string or NULL if malloc error *********************************************************************/ extern xmlChar * xml_val_make_qname (xmlns_id_t nsid, const xmlChar *name); /******************************************************************** * FUNCTION xml_val_qname_len * * Determine the length of the qname string that would be generated * with the xml_val_make_qname function * * INPUTS: * nsid == namespace ID to use * name == condition clause (may be NULL) * * RETURNS: * length of string needed for this QName *********************************************************************/ extern uint32 xml_val_qname_len (xmlns_id_t nsid, const xmlChar *name); /******************************************************************** * FUNCTION xml_val_sprintf_qname * * construct a QName into a buffer * * INPUTS: * buff == buffer * bufflen == size of buffer * nsid == namespace ID to use * name == condition clause (may be NULL) * * RETURNS: * number of bytes written to the buffer *********************************************************************/ extern uint32 xml_val_sprintf_qname (xmlChar *buff, uint32 bufflen, xmlns_id_t nsid, const xmlChar *name); /******************************************************************** * FUNCTION xml_val_add_attr * * Set up a new attr val and add it to the specified val * hand off a malloced attribute string * * INPUTS: * name == attr name * nsid == namespace ID of attr * attrval == attr val to add (do not use strdup) * val == parent val struct to hold the new attr * * RETURNS: * status *********************************************************************/ extern status_t xml_val_add_attr (const xmlChar *name, xmlns_id_t nsid, xmlChar *attrval, val_value_t *val); /******************************************************************** * FUNCTION xml_val_add_cattr * * Set up a new const attr val and add it to the specified val * copy a const attribute string * * INPUTS: * name == attr name * nsid == namespace ID of attr * cattrval == const attr val to add (use strdup) * val == parent val struct to hold the new attr * * RETURNS: * status *********************************************************************/ extern status_t xml_val_add_cattr (const xmlChar *name, xmlns_id_t nsid, const xmlChar *cattrval, val_value_t *val); /******************************************************************** * FUNCTION xml_val_new_struct * * Set up a new struct * * INPUTS: * name == element name * nsid == namespace ID of name * * RETURNS: * new struct or NULL if malloc error *********************************************************************/ extern val_value_t * xml_val_new_struct (const xmlChar *name, xmlns_id_t nsid); /******************************************************************** * FUNCTION xml_val_new_string * * Set up a new string element; reuse the value instead of copying it * hand off a malloced string * * INPUTS: * name == element name * nsid == namespace ID of name * strval == malloced string value that will be freed later * * RETURNS: * new string or NULL if malloc error *********************************************************************/ extern val_value_t * xml_val_new_string (const xmlChar *name, xmlns_id_t nsid, xmlChar *strval); /******************************************************************** * FUNCTION xml_val_new_cstring * * Set up a new string from a const string * copy a const string * * INPUTS: * name == element name * nsid == namespace ID of name * strval == const string value that will strduped first * * RETURNS: * new string or NULL if malloc error *********************************************************************/ extern val_value_t * xml_val_new_cstring (const xmlChar *name, xmlns_id_t nsid, const xmlChar *strval); /******************************************************************** * FUNCTION xml_val_new_flag * * Set up a new flag * This is not complete; more nodes will be added * * INPUTS: * name == element name * nsid == namespace ID of name * * RETURNS: * new struct or NULL if malloc error *********************************************************************/ extern val_value_t * xml_val_new_flag (const xmlChar *name, xmlns_id_t nsid); /******************************************************************** * FUNCTION xml_val_new_boolean * * Set up a new boolean * This is not complete; more nodes will be added * * INPUTS: * name == element name * nsid == namespace ID of name * boo == boolean value to set * * RETURNS: * new struct or NULL if malloc error *********************************************************************/ extern val_value_t * xml_val_new_boolean (const xmlChar *name, xmlns_id_t nsid, boolean boo); /******************************************************************** * FUNCTION xml_val_new_number * * Set up a new number * * INPUTS: * name == element name * nsid == namespace ID of name * num == number value to set * btyp == base type of 'num' * * RETURNS: * new struct or NULL if malloc error *********************************************************************/ extern val_value_t * xml_val_new_number (const xmlChar *name, xmlns_id_t nsid, ncx_num_t *num, ncx_btype_t btyp); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xml_val */ yuma123_2.14/netconf/src/ncx/ncx_num.h0000664000175000017500000004140514770023131017762 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ncx_num #define _H_ncx_num /* FILE: ncx_num.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Module Library Number Utility Functions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17-feb-10 abb Begun; split out from ncx.c */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION ncx_init_num * * Init a ncx_num_t struct * * INPUTS: * num == number to initialize *********************************************************************/ extern void ncx_init_num (ncx_num_t *num); /******************************************************************** * FUNCTION ncx_clean_num * * Scrub the memory in a ncx_num_t by freeing all * the sub-fields. DOES NOT free the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * btyp == base type of number * num == ncx_num_t data structure to clean *********************************************************************/ extern void ncx_clean_num (ncx_btype_t btyp, ncx_num_t *num); /******************************************************************** * FUNCTION ncx_compare_nums * * Compare 2 ncx_num_t union contents * * INPUTS: * num1 == first number * num2 == second number * btyp == expected data type (NCX_BT_INT, UINT, REAL) * RETURNS: * -1 if num1 is < num2 * 0 if num1 == num2 * 1 if num1 is > num2 *********************************************************************/ extern int32 ncx_compare_nums (const ncx_num_t *num1, const ncx_num_t *num2, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_set_num_min * * Set a number to the minimum value for its type * * INPUTS: * num == number to set * btyp == expected data type * *********************************************************************/ extern void ncx_set_num_min (ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_set_num_max * * Set a number to the maximum value for its type * * INPUTS: * num == number to set * btyp == expected data type * *********************************************************************/ extern void ncx_set_num_max (ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_set_num_one * * Set a number to one * * INPUTS: * num == number to set * btyp == expected data type * *********************************************************************/ extern void ncx_set_num_one (ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_set_num_zero * * Set a number to zero * * INPUTS: * num == number to set * btyp == expected data type * *********************************************************************/ extern void ncx_set_num_zero (ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_set_num_nan * * Set a FP number to the Not a Number value * * INPUTS: * num == number to set * btyp == expected data type * *********************************************************************/ extern void ncx_set_num_nan (ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_num_is_nan * * Check if a FP number is set to the Not a Number value * * INPUTS: * num == number to check * btyp == expected data type * * RETURNS: * TRUE if number is not-a-number (NaN) * FALSE otherwise *********************************************************************/ extern boolean ncx_num_is_nan (ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_num_zero * * Compare a ncx_num_t to zero * * INPUTS: * num == number to check * btyp == expected data type (e.g., NCX_BT_INT32, NCX_BT_UINT64) * * RETURNS: * TRUE if value is equal to zero * FALSE if value is not equal to zero *********************************************************************/ extern boolean ncx_num_zero (const ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_convert_num * * Convert a number string to a numeric type * * INPUTS: * numstr == number string * numfmt == NCX_NF_OCTAL, NCX_NF_DEC, NCX_NF_HEX, or NCX_NF_REAL * btyp == desired number type * (e.g., NCX_BT_INT32, NCX_BT_UINT32, NCX_BT_FLOAT64) * val == pointer to ncx_num_t to hold result * * OUTPUTS: * *val == converted number value * * RETURNS: * status *********************************************************************/ extern status_t ncx_convert_num (const xmlChar *numstr, ncx_numfmt_t numfmt, ncx_btype_t btyp, ncx_num_t *val); /******************************************************************** * FUNCTION ncx_convert_dec64 * * Convert a number string to a decimal64 number * * INPUTS: * numstr == number string * numfmt == number format used * digits == number of fixed-point digits expected * val == pointer to ncx_num_t to hold result * * OUTPUTS: * *val == converted number value * * RETURNS: * status *********************************************************************/ extern status_t ncx_convert_dec64 (const xmlChar *numstr, ncx_numfmt_t numfmt, uint8 digits, ncx_num_t *val); /******************************************************************** * FUNCTION ncx_decode_num * * Handle some sort of number string * * INPUTS: * numstr == number string * btyp == desired number type * retnum == pointer to initialized ncx_num_t to hold result * * OUTPUTS: * *retnum == converted number * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncx_decode_num (const xmlChar *numstr, ncx_btype_t btyp, ncx_num_t *retnum); /******************************************************************** * FUNCTION ncx_decode_dec64 * * Handle some sort of decimal64 number string (NCX_BT_DECIMAL64) * * INPUTS: * numstr == number string * digits == number of expected digits for this decimal64 * retnum == pointer to initialized ncx_num_t to hold result * * OUTPUTS: * *retnum == converted number * * RETURNS: * status of the operation *********************************************************************/ extern status_t ncx_decode_dec64 (const xmlChar *numstr, uint8 digits, ncx_num_t *retnum); /******************************************************************** * FUNCTION ncx_copy_num * * Copy the contents of num1 to num2 * * Supports all NCX numeric types: * NCX_BT_INT* * NCX_BT_UINT* * NCX_BT_DECIMAL64 * NCX_BT_FLOAT64 * * INPUTS: * num1 == first number * num2 == second number * btyp == expected data type (NCX_BT_INT, UINT, REAL) * RETURNS: * status *********************************************************************/ extern status_t ncx_copy_num (const ncx_num_t *num1, ncx_num_t *num2, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_cast_num * * Cast a number as another number type * * Supports all NCX numeric types: * NCX_BT_INT* * NCX_BT_UINT* * NCX_BT_FLOAT64 * * INPUTS: * num1 == source number * btyp1 == expected data type of num1 * num2 == target number * btyp2 == desired data type of num2 * * OUTPUTS: * *num2 set to cast value of num1 * * RETURNS: * status *********************************************************************/ extern status_t ncx_cast_num (const ncx_num_t *num1, ncx_btype_t btyp1, ncx_num_t *num2, ncx_btype_t btyp2); /******************************************************************** * FUNCTION ncx_num_floor * * Get the floor value of a number * * Supports all NCX numeric types: * NCX_BT_INT* * NCX_BT_UINT* * NCX_BT_DECIMAL64 * NCX_BT_FLOAT64 * * INPUTS: * num1 == source number * num2 == address of target number * btyp == expected data type of numbers * * OUTPUTS: * *num2 set to floor of num1 * * RETURNS: * status *********************************************************************/ extern status_t ncx_num_floor (const ncx_num_t *num1, ncx_num_t *num2, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_num_ceiling * * Get the ceiling value of a number * * Supports all NCX numeric types: * NCX_BT_INT* * NCX_BT_UINT* * NCX_BT_DECIMAL64 * NCX_BT_FLOAT64 * * INPUTS: * num1 == source number * num2 == target number * btyp == expected data type of numbers * * OUTPUTS: * *num2 set to ceiling of num1 * * RETURNS: * status *********************************************************************/ extern status_t ncx_num_ceiling (const ncx_num_t *num1, ncx_num_t *num2, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_round_num * * Get the rounded value of a number * * Supports all NCX numeric types: * NCX_BT_INT* * NCX_BT_UINT* * NCX_BT_DECIMAL64 * NCX_BT_FLOAT64 * * INPUTS: * num1 == source number * num2 == target number * btyp == expected data type of numbers * * OUTPUTS: * *num2 set to round of num1 * * RETURNS: * status *********************************************************************/ extern status_t ncx_round_num (const ncx_num_t *num1, ncx_num_t *num2, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_num_is_integral * * Check if the number is integral or if it has a fractional part * Supports all NCX numeric types: * NCX_BT_INT* * NCX_BT_UINT* * NCX_BT_DECIMAL64 * NCX_BT_FLOAT64 * * INPUTS: * num == number to check * btyp == expected data type * * RETURNS: * TRUE if integral, FALSE if not *********************************************************************/ extern boolean ncx_num_is_integral (const ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_cvt_to_int64 * * Convert a number to an integer64; * Use rounding for float64 * * INPUTS: * num == number to convert * btyp == data type of num * * RETURNS: * int64 representation *********************************************************************/ extern int64 ncx_cvt_to_int64 (const ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_get_numfmt * * Get the number format of the specified string * Does not check for valid format * Just figures out which type it must be if it were valid * * INPUTS: * numstr == number string * * RETURNS: * NCX_NF_NONE, NCX_NF_DEC, NCX_NF_HEX, or NCX_NF_REAL *********************************************************************/ extern ncx_numfmt_t ncx_get_numfmt (const xmlChar *numstr); /******************************************************************** * FUNCTION ncx_printf_num * * Printf a ncx_num_t contents * * INPUTS: * num == number to printf * btyp == number base type * *********************************************************************/ extern void ncx_printf_num (const ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_alt_printf_num * * Printf a ncx_num_t contents to the alternate log file * * INPUTS: * num == number to printf * btyp == number base type * *********************************************************************/ extern void ncx_alt_printf_num (const ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_sprintf_num * * Sprintf a ncx_num_t contents * * INPUTS: * buff == buffer to write; NULL means just get length * num == number to printf * btyp == number base type * len == address of return length * * OUTPUTS:: * *len == number of bytes written (or would have been) to buff * * RETURNS: * status *********************************************************************/ extern status_t ncx_sprintf_num (xmlChar *buff, const ncx_num_t *num, ncx_btype_t btyp, uint32 *len); /******************************************************************** * FUNCTION ncx_is_min * * Return TRUE if the specified number is the min value * for its type * * INPUTS: * num == number to check * btyp == data type of num * RETURNS: * TRUE if this is the minimum value * FALSE otherwise *********************************************************************/ extern boolean ncx_is_min (const ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_is_max * * Return TRUE if the specified number is the max value * for its type * * INPUTS: * num == number to check * btyp == data type of num * RETURNS: * TRUE if this is the maximum value * FALSE otherwise *********************************************************************/ extern boolean ncx_is_max (const ncx_num_t *num, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_convert_tkcnum * * Convert the current token in a token chain to * a ncx_num_t struct * * INPUTS: * tkc == token chain; current token will be converted * tkc->typ == TK_TT_DNUM, TK_TT_HNUM, TK_TT_RNUM * btyp == desired number type * (e.g., NCX_BT_INT32, NCX_BT_UINT64, NCX_BT_FLOAT64) * OUTPUTS: * *val == converted number value (0..2G-1), if NO_ERR * RETURNS: * status *********************************************************************/ extern status_t ncx_convert_tkcnum (tk_chain_t *tkc, ncx_btype_t btyp, ncx_num_t *val); /******************************************************************** * FUNCTION ncx_convert_tkc_dec64 * * Convert the current token in a token chain to * a ncx_num_t struct, expecting NCX_BT_DECIMAL64 * * INPUTS: * tkc == token chain; current token will be converted * tkc->typ == TK_TT_DNUM, TK_TT_HNUM, TK_TT_RNUM * digits == number of expected digits * * OUTPUTS: * *val == converted number value, if NO_ERR * * RETURNS: * status *********************************************************************/ extern status_t ncx_convert_tkc_dec64 (tk_chain_t *tkc, uint8 digits, ncx_num_t *val); /******************************************************************** * FUNCTION ncx_get_dec64_base * * Get the base part of a decimal64 number * * INPUT: * num == number to check (expected to be NCX_BT_DECIMAL64) * * RETURNS: * base part of the number *********************************************************************/ extern int64 ncx_get_dec64_base (const ncx_num_t *num); /******************************************************************** * FUNCTION ncx_get_dec64_fraction * * Get the fraction part of a decimal64 number * * INPUT: * num == number to check (expected to be NCX_BT_DECIMAL64) * * RETURNS: * fraction part of the number *********************************************************************/ extern int64 ncx_get_dec64_fraction (const ncx_num_t *num); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_ncx_num */ yuma123_2.14/netconf/src/ncx/conf.h0000664000175000017500000000511514770023131017236 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_conf #define _H_conf /* FILE: conf.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Text Config file parser ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 22-oct-07 abb Begun */ #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION conf_parse_from_filespec * * Parse a file as an NCX text config file against * a specific parmset definition. Fill in an * initialized parmset (and could be partially filled in) * * Error messages are printed by this function!! * * If a value is already set, and only one value is allowed * then the 'keepvals' parameter will control whether that * value will be kept or overwitten * * INPUTS: * filespec == absolute path or relative path * This string is used as-is without adjustment. * val == value struct to fill in, must be initialized * already with val_new_value or val_init_value * keepvals == TRUE if old values should always be kept * FALSE if old vals should be overwritten * fileerr == TRUE to generate a missing file error * FALSE to return NO_ERR instead, if file not found * * RETURNS: * status of the operation *********************************************************************/ extern status_t conf_parse_val_from_filespec (const xmlChar *filespec, val_value_t *val, boolean keepvals, boolean fileerr); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_conf */ yuma123_2.14/netconf/src/ncx/obj_help.c0000664000175000017500000005323414770023131020073 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: obj_help.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17aug08 abb begun; split out from obj.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_help #include "help.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif /* #ifndef _H_ncx #include "ncx.h" #endif */ #ifndef _H_obj #include "obj.h" #endif #ifndef _H_obj_help #include "obj_help.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * FUNCTION dump_typdef_data * * Dump some contents from a typ_def_t struct for help text * * INPUTS: * obj == obj_template_t to dump help for * mode == requested help mode * indent == start indent count *********************************************************************/ static void dump_typdef_data (obj_template_t *obj, help_mode_t mode, uint32 indent) { const xmlChar *datastr; typ_def_t *typdef, *basetypdef, *testdef; typ_enum_t *tenum; typ_pattern_t *pattern; xmlChar numbuff[NCX_MAX_NUMLEN]; ncx_btype_t btyp; if (mode == HELP_MODE_BRIEF) { return; } typdef = obj_get_typdef(obj); if (!typdef) { return; } basetypdef = typ_get_base_typdef(typdef); btyp = typ_get_basetype(typdef); datastr = typ_get_rangestr(typdef); if (datastr) { if (typ_is_string(btyp)) { help_write_lines((const xmlChar *)"length: ", indent, TRUE); } else { help_write_lines((const xmlChar *)"range: ", indent, TRUE); } help_write_lines(datastr, 0, FALSE); } testdef = typdef; while (testdef) { for (pattern = typ_get_first_pattern(testdef); pattern != NULL; pattern = typ_get_next_pattern(pattern)) { help_write_lines((const xmlChar *)"pattern: ", indent, TRUE); help_write_lines(pattern->pat_str, 0, FALSE); } testdef = typ_get_parent_typdef(testdef); } switch (btyp) { case NCX_BT_ENUM: case NCX_BT_BITS: if (btyp == NCX_BT_ENUM) { help_write_lines((const xmlChar *)"enum values:", indent, TRUE); } else { help_write_lines((const xmlChar *)"bit values:", indent, TRUE); } for (tenum = typ_first_enumdef(basetypdef); tenum != NULL; tenum = typ_next_enumdef(tenum)) { if (mode == HELP_MODE_NORMAL) { help_write_lines((const xmlChar *)" ", 0, FALSE); help_write_lines(tenum->name, 0, FALSE); } else { help_write_lines(tenum->name, indent+NCX_DEF_INDENT, TRUE); help_write_lines((const xmlChar *)"(", 0, FALSE); if (btyp == NCX_BT_ENUM) { snprintf((char *)numbuff, sizeof(numbuff), "%d", tenum->val); } else { snprintf((char *)numbuff, sizeof(numbuff), "%u", tenum->pos); } help_write_lines(numbuff, 0, FALSE); help_write_lines((const xmlChar *)")", 0, FALSE); if (tenum->descr) { help_write_lines(tenum->descr, indent+(2*NCX_DEF_INDENT), TRUE); } if (tenum->ref) { help_write_lines(tenum->ref, indent+(2*NCX_DEF_INDENT), TRUE); } } } break; case NCX_BT_LEAFREF: datastr = typ_get_leafref_path(basetypdef); if (datastr) { help_write_lines((const xmlChar *)"leafref path:", indent, TRUE); help_write_lines(datastr, indent+NCX_DEF_INDENT, TRUE); } break; default: ; } } /* dump_typdef_data */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION obj_dump_template * * Dump the contents of an obj_template_t struct for help text * * INPUTS: * obj == obj_template to dump help for * mode == requested help mode * nestlevel == number of levels from the top-level * that should be printed; 0 == all levels * indent == start indent count *********************************************************************/ void obj_dump_template (obj_template_t *obj, help_mode_t mode, uint32 nestlevel, uint32 indent) { const xmlChar *val, *keystr; obj_template_t *testobj; uint32 count, objnestlevel; char numbuff[NCX_MAX_NUMLEN]; boolean normalpass, neednewline; #ifdef DEBUG if (mode > HELP_MODE_FULL) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif if (obj == NULL) { /* could be called because disabled first child found */ return; } if (!obj_has_name(obj) || !obj_is_enabled(obj)) { return; } if (mode == HELP_MODE_NONE) { return; } objnestlevel = obj_get_level(obj); if (mode == HELP_MODE_BRIEF && nestlevel > 0 && objnestlevel > nestlevel) { return; } normalpass = (nestlevel && (objnestlevel > nestlevel)) ? FALSE : TRUE; if (obj->objtype == OBJ_TYP_RPCIO || obj->objtype == OBJ_TYP_RPC) { help_write_lines(obj_get_name(obj), indent, TRUE); count = 0; } else if (obj->objtype == OBJ_TYP_CASE) { count = obj_enabled_child_count(obj); } else { count = 2; } if (count > 1) { help_write_lines(obj_get_typestr(obj), indent, TRUE); help_write_lines((const xmlChar *)" ", 0, FALSE); help_write_lines(obj_get_name(obj), 0, FALSE); } neednewline = FALSE; switch (obj->objtype) { case OBJ_TYP_CONTAINER: case OBJ_TYP_LIST: case OBJ_TYP_CASE: case OBJ_TYP_RPC: case OBJ_TYP_RPCIO: case OBJ_TYP_NOTIF: break; default: /* print type and default for leafy and choice objects */ if (obj->objtype != OBJ_TYP_CHOICE) { help_write_lines((const xmlChar *)" [", 0, FALSE); help_write_lines((const xmlChar *) obj_get_type_name(obj), 0, FALSE); help_write_lines((const xmlChar *)"]", 0, FALSE); } if (mode != HELP_MODE_BRIEF) { val = obj_get_default(obj); if (val != NULL) { help_write_lines((const xmlChar *)" [d:", 0, FALSE); help_write_lines(val, 0, FALSE); help_write_lines((const xmlChar *)"]", 0, FALSE); } if (obj_is_mandatory(obj)) { help_write_lines((const xmlChar *)" ", 0, FALSE); } } } if (normalpass) { val = obj_get_description(obj); if (val == NULL) { val = obj_get_alt_description(obj); } if (val != NULL) { switch (mode) { case HELP_MODE_BRIEF: if (obj->objtype == OBJ_TYP_RPC || obj->objtype == OBJ_TYP_NOTIF) { help_write_lines_max(val, indent+NCX_DEF_INDENT, TRUE, HELP_MODE_BRIEF_MAX); } break; case HELP_MODE_NORMAL: help_write_lines_max(val, indent+NCX_DEF_INDENT, TRUE, HELP_MODE_NORMAL_MAX); break; case HELP_MODE_FULL: help_write_lines(val, indent+NCX_DEF_INDENT, TRUE); break; default: SET_ERROR(ERR_INTERNAL_VAL); return; } } } switch (obj->objtype) { case OBJ_TYP_CONTAINER: switch (mode) { case HELP_MODE_BRIEF: neednewline = TRUE; break; case HELP_MODE_NORMAL: if (obj->def.container->presence) { help_write_lines((const xmlChar *)" (P)", 0, FALSE); } else { help_write_lines((const xmlChar *)" (NP)", 0, FALSE); } testobj = obj_get_default_parm(obj); if (testobj) { help_write_lines((const xmlChar *)"default parameter: ", indent+NCX_DEF_INDENT, TRUE); help_write_lines(obj_get_name(testobj), 0, FALSE); } break; case HELP_MODE_FULL: if (obj->def.container->presence) { help_write_lines((const xmlChar *)"presence: ", indent+NCX_DEF_INDENT, TRUE); help_write_lines(obj->def.container->presence, 0, FALSE); } testobj = obj_get_default_parm(obj); if (testobj) { help_write_lines((const xmlChar *)"default parameter: ", indent+NCX_DEF_INDENT, TRUE); help_write_lines(obj_get_name(testobj), 0, FALSE); } if (mode == HELP_MODE_FULL) { /*** add mustQ ***/; } break; default: SET_ERROR(ERR_INTERNAL_VAL); return; } obj_dump_datadefQ(obj->def.container->datadefQ, mode, nestlevel, indent+NCX_DEF_INDENT); break; case OBJ_TYP_ANYXML: /* nothing interesting in the typdef to report */ neednewline = TRUE; break; case OBJ_TYP_LEAF: switch (mode) { case HELP_MODE_BRIEF: break; case HELP_MODE_NORMAL: if (normalpass) { dump_typdef_data(obj, mode, indent+NCX_DEF_INDENT); } break; case HELP_MODE_FULL: dump_typdef_data(obj, mode, indent+NCX_DEF_INDENT); val = obj_get_units(obj); if (val) { help_write_lines((const xmlChar *)"units: ", indent+NCX_DEF_INDENT, TRUE); help_write_lines(val, 0, FALSE); } break; default: ; } break; case OBJ_TYP_LEAF_LIST: switch (mode) { case HELP_MODE_NORMAL: if (normalpass) { dump_typdef_data(obj, mode, indent+NCX_DEF_INDENT); } break; case HELP_MODE_FULL: dump_typdef_data(obj, mode, indent+NCX_DEF_INDENT); val = obj_get_units(obj); if (val) { help_write_lines((const xmlChar *)"units: ", indent+NCX_DEF_INDENT, TRUE); help_write_lines(val, 0, FALSE); } if (!obj->def.leaflist->ordersys) { help_write_lines((const xmlChar *)"ordered-by: user", indent+NCX_DEF_INDENT, TRUE); } else { help_write_lines((const xmlChar *)"ordered-by: system", indent+NCX_DEF_INDENT, TRUE); } if (obj->def.leaflist->minset) { help_write_lines((const xmlChar *)"min-elements: ", indent+NCX_DEF_INDENT, TRUE); snprintf(numbuff, sizeof(numbuff), "%u", obj->def.leaflist->minelems); help_write_lines((const xmlChar *)numbuff, 0, FALSE); } if (obj->def.leaflist->maxset) { help_write_lines((const xmlChar *)"max-elements: ", indent+NCX_DEF_INDENT, TRUE); snprintf(numbuff, sizeof(numbuff), "%u", obj->def.leaflist->maxelems); help_write_lines((const xmlChar *)numbuff, 0, FALSE); } break; default: ; } break; case OBJ_TYP_CHOICE: if (mode == HELP_MODE_BRIEF) { neednewline = TRUE; break; } count = obj_enabled_child_count(obj); if (count) { obj_dump_datadefQ(obj_get_datadefQ(obj), mode, nestlevel, indent+NCX_DEF_INDENT); } break; case OBJ_TYP_CASE: if (mode == HELP_MODE_BRIEF) { neednewline = TRUE; break; } count = obj_enabled_child_count(obj); if (count > 1) { obj_dump_datadefQ(obj_get_datadefQ(obj), mode, nestlevel, indent+NCX_DEF_INDENT); } else if (count == 1) { testobj = obj_first_child(obj); if (testobj) { obj_dump_template(testobj, mode, nestlevel, indent); } } /* else skip this case */ break; case OBJ_TYP_LIST: switch (mode) { case HELP_MODE_BRIEF: break; case HELP_MODE_NORMAL: case HELP_MODE_FULL: keystr = obj_get_keystr(obj); if (keystr != NULL) { help_write_lines((const xmlChar *)"key: ", indent+NCX_DEF_INDENT, TRUE); help_write_lines(keystr, 0, FALSE); } if (!obj->def.list->ordersys) { help_write_lines((const xmlChar *)"ordered-by: user", indent+NCX_DEF_INDENT, TRUE); } if (mode == HELP_MODE_NORMAL) { break; } if (obj->def.list->minset) { help_write_lines((const xmlChar *)"min-elements: ", indent+NCX_DEF_INDENT, TRUE); snprintf(numbuff, sizeof(numbuff), "%u", obj->def.list->minelems); help_write_lines((const xmlChar *)numbuff, 0, FALSE); } if (obj->def.list->maxset) { help_write_lines((const xmlChar *)"max-elements: ", indent+NCX_DEF_INDENT, TRUE); snprintf(numbuff, sizeof(numbuff), "%u", obj->def.list->maxelems); help_write_lines((const xmlChar *)numbuff, 0, FALSE); } break; default: ; } if (mode != HELP_MODE_BRIEF) { obj_dump_datadefQ(obj_get_datadefQ(obj), mode, nestlevel, indent+NCX_DEF_INDENT); } break; case OBJ_TYP_RPC: testobj = obj_find_child(obj, NULL, YANG_K_INPUT); if (testobj && obj_enabled_child_count(testobj)) { obj_dump_template(testobj, mode, nestlevel, indent+NCX_DEF_INDENT); } testobj = obj_find_child(obj, NULL, YANG_K_OUTPUT); if (testobj && obj_enabled_child_count(testobj)) { obj_dump_template(testobj, mode, nestlevel, indent+NCX_DEF_INDENT); } help_write_lines((const xmlChar *)"\n", 0, FALSE); break; case OBJ_TYP_RPCIO: if (mode != HELP_MODE_BRIEF) { testobj = obj_get_default_parm(obj); if (testobj && obj_is_enabled(testobj)) { help_write_lines((const xmlChar *)"default parameter: ", indent+NCX_DEF_INDENT, TRUE); help_write_lines(obj_get_name(testobj), 0, FALSE); } else { neednewline = FALSE; } } obj_dump_datadefQ(obj_get_datadefQ(obj), mode, nestlevel, indent+NCX_DEF_INDENT); break; case OBJ_TYP_NOTIF: obj_dump_datadefQ(obj_get_datadefQ(obj), mode, nestlevel, indent+NCX_DEF_INDENT); break; case OBJ_TYP_AUGMENT: case OBJ_TYP_USES: case OBJ_TYP_REFINE: default: SET_ERROR(ERR_INTERNAL_VAL); } if (neednewline) { help_write_lines(NULL, 0, TRUE); } } /* obj_dump_template */ /******************************************************************** * FUNCTION obj_dump_datadefQ * * Dump the contents of a datadefQ for debugging * * INPUTS: * datadefQ == Q of obj_template to dump * mode == desired help mode (brief, normal, full) * nestlevel == number of levels from the top-level * that should be printed; 0 == all levels * indent == start indent count *********************************************************************/ void obj_dump_datadefQ (dlq_hdr_t *datadefQ, help_mode_t mode, uint32 nestlevel, uint32 indent) { obj_template_t *obj; uint32 objnestlevel; #ifdef DEBUG if (!datadefQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (mode > HELP_MODE_FULL) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif if (mode == HELP_MODE_NONE) { return; } for (obj = (obj_template_t *)dlq_firstEntry(datadefQ); obj != NULL; obj = (obj_template_t *)dlq_nextEntry(obj)) { if (!obj_has_name(obj) || !obj_is_enabled(obj)) { continue; } objnestlevel = obj_get_level(obj); if (mode == HELP_MODE_BRIEF && objnestlevel > nestlevel) { continue; } obj_dump_template(obj, mode, nestlevel, indent); switch (obj->objtype) { case OBJ_TYP_RPCIO: case OBJ_TYP_CHOICE: break; case OBJ_TYP_CASE: switch (obj_enabled_child_count(obj)) { case 0: break; case 1: help_write_lines((const xmlChar *)"\n", 0, FALSE); break; default: break; } break; default: help_write_lines((const xmlChar *)"\n", 0, FALSE); } } } /* obj_dump_datadefQ */ /* END obj_help.c */ yuma123_2.14/netconf/src/ncx/uptime.c0000664000175000017500000000032014770023131017600 0ustar vladimirvladimir#include #include time_t uptime(time_t *t) { int ret; struct timespec tp; ret = clock_gettime(CLOCK_MONOTONIC, &tp); assert(ret==0); *t = tp.tv_sec; return *t; } yuma123_2.14/netconf/src/ncx/status.c0000664000175000017500000007362714770023131017644 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: status.c * ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 22-dec-01 abb begin 22-apr-05 abb updated for netconf project ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_status #include "status.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define MAX_ERR_FILENAME 64 #define MAX_ERR_MSG_LEN 256 #define MAX_ERR_LEVEL 15 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* the array of form IDs and form handlers */ typedef struct error_snapshot_t_ { int linenum; int sqlError; status_t status; char filename[MAX_ERR_FILENAME]; const char *msg; } error_snapshot_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /* used for error_stack, when debug_level == NONE */ static int error_level; static error_snapshot_t error_stack[MAX_ERR_LEVEL]; static uint32 error_count; /******************************************************************** * FUNCTION set_error * * Generate an error stack entry or if log_error is enabled, * then log the error report right now. * * Used to flag internal code errors * * DO NOT USE THIS FUNCTION DIRECTLY! * USE THE SET_ERROR MACRO INSTEAD! * * INPUTS: * filename == C filename that caused the error * linenum -- line number in the C file that caused the error * status == internal error code * sqlError = mySQL error code (deprecated) * * RETURNS * the 'status' parameter will be returned *********************************************************************/ status_t set_error (const char *filename, int linenum, status_t status, int sqlError) { if (log_get_debug_level() > LOG_DEBUG_NONE) { log_error("\nE0:\n %s:%d\n Error %d: %s\n", filename, linenum, status, get_error_string(status)); } else if (error_level < MAX_ERR_LEVEL) { error_stack[error_level].linenum = linenum; error_stack[error_level].sqlError = sqlError; error_stack[error_level].status = status; strncpy(error_stack[error_level].filename, filename, MAX_ERR_FILENAME-1); error_stack[error_level].filename[MAX_ERR_FILENAME-1] = 0; error_stack[error_level].msg = get_error_string(status); error_level++; } error_count++; return status; } /* set_error */ /******************************************************************** * FUNCTION print_errors * * Dump any entries stored in the error_stack. * *********************************************************************/ void print_errors (void) { int i; for (i=0; i < error_level; i++) { log_error("\nE%d:\n %s:%d\n Error %d: %s", i, error_stack[i].filename, error_stack[i].linenum, error_stack[i].status, (error_stack[i].msg) ? error_stack[i].msg : ""); if (i==error_level-1) { log_error("\n"); } } } /* print_errors */ /******************************************************************** * FUNCTION clear_errors * * Clear the error_stack if it has any errors stored in it * *********************************************************************/ void clear_errors (void) { int i; for (i=0; i < error_level; i++) { error_stack[i].linenum = 0; error_stack[i].sqlError = 0; error_stack[i].status = NO_ERR; error_stack[i].filename[0] = 0; error_stack[i].msg = NULL; } error_level = 0; } /* clear_errors */ /******************************************************************** * FUNCTION get_errtyp * * Get the error classification for the result code * * INPUTS: * res == result code * * RETURNS: * error type for 'res' parameter *********************************************************************/ errtyp_t get_errtyp (status_t res) { if (res == NO_ERR) { return ERR_TYP_NONE; } else if (res < ERR_LAST_INT_ERR) { return ERR_TYP_INTERNAL; } else if (res < ERR_LAST_SYS_ERR) { return ERR_TYP_SYSTEM; } else if (res < ERR_LAST_USR_ERR) { return ERR_TYP_USER; } else if (res < ERR_LAST_WARN) { return ERR_TYP_WARN; } else { return ERR_TYP_INFO; } /*NOTREACHED*/ } /* get_errtyp */ /******************************************************************** * FUNCTION get_error_string * * Get the error message for a specific internal error * * INPUTS: * res == internal status_t error code * * RETURNS: * const pointer to error message *********************************************************************/ const char * get_error_string (status_t res) { switch (res) { case NO_ERR: return "ok"; case ERR_END_OF_FILE: return "EOF reached"; case ERR_INTERNAL_PTR: return "NULL pointer"; case ERR_INTERNAL_MEM: return "malloc failed"; case ERR_INTERNAL_VAL: return "invalid internal value"; case ERR_INTERNAL_BUFF: return "internal buffering failed"; case ERR_INTERNAL_QDEL: return "invalid queue deletion"; case ERR_INTERNAL_INIT_SEQ: return "wrong init sequence"; case ERR_QNODE_NOT_HDR: return "queue node not header"; case ERR_QNODE_NOT_DATA: return "queue node not data"; case ERR_BAD_QLINK: return "invalid queue header"; case ERR_Q_ALREADY: return "entry already queued"; case ERR_TOO_MANY_ENTRIES: return "too many entries"; case ERR_XML2_FAILED: return "libxml2 operation failed"; case ERR_FIL_OPEN: return "cannot open file"; case ERR_FIL_READ: return "cannot read file"; case ERR_FIL_CLOSE: return "cannot close file"; case ERR_FIL_WRITE: return "cannot write file"; case ERR_FIL_CHDIR: return "cannot change directory"; case ERR_FIL_STAT: return "cannot stat file"; case ERR_BUFF_OVFL: return "buffer overflow error"; case ERR_FIL_DELETE: return "cannot delete file"; case ERR_FIL_SETPOS: return "cannot access file"; case ERR_DB_CONNECT_FAILED: return "db connect failed"; case ERR_DB_ENTRY_EXISTS: return "db entry exists"; case ERR_DB_NOT_FOUND: return "db not found"; case ERR_DB_QUERY_FAILED: return "db query failed"; case ERR_DB_DELETE_FAILED: return "db delete failed"; case ERR_DB_WRONG_CKSUM: return "wrong checksum"; case ERR_DB_WRONG_TAGTYPE: return "wrong tag type"; case ERR_DB_READ_FAILED: return "db read failed"; case ERR_DB_WRITE_FAILED: return "db write failed"; case ERR_DB_INIT_FAILED: return "db init failed"; case ERR_TR_BEEP_INIT: return "beep init failed"; case ERR_TR_BEEP_NC_INIT: return "beep init nc failed"; case ERR_XML_READER_INTERNAL: return "xml reader internal"; case ERR_OPEN_DIR_FAILED: return "open directory failed"; case ERR_READ_DIR_FAILED: return "read directory failed"; case ERR_NO_CFGFILE: return "no config file"; case ERR_NO_SRCFILE: return "no source file"; case ERR_PARSPOST_RD_INPUT: return "POST read input"; case ERR_FIL_BAD_DRIVE: return "bad drive"; case ERR_FIL_BAD_PATH: return "bad path"; case ERR_FIL_BAD_FILENAME: return "bad filename"; case ERR_DUP_VALPAIR: return "duplicate value pair"; case ERR_PAGE_NOT_HANDLED: return "page not handled"; case ERR_PAGE_ACCESS_DENIED: return "page access denied"; case ERR_MISSING_FORM_PARAMS: return "missing form params"; case ERR_FORM_STATE: return "invalid form state"; case ERR_DUP_NS: return "duplicate namespace"; case ERR_XML_READER_START_FAILED: return "xml reader start failed"; case ERR_XML_READER_READ: return "xml reader read failed"; case ERR_XML_READER_NODETYP: return "wrong XML node type"; case ERR_XML_READER_NULLNAME: return "xml reader null name"; case ERR_XML_READER_NULLVAL: return "xml reader null value"; case ERR_XML_READER_WRONGNAME: return "xml reader wrong name"; case ERR_XML_READER_WRONGVAL: return "xml reader wrong value"; case ERR_XML_READER_WRONGEL: return "xml reader wrong element"; case ERR_XML_READER_EXTRANODES: return "xml reader extra nodes"; case ERR_XML_READER_EOF: return "xml reader EOF"; case ERR_NCX_WRONG_LEN: return "wrong length"; case ERR_NCX_ENTRY_EXISTS: return "entry exists"; case ERR_NCX_DUP_ENTRY: return "duplicate entry"; case ERR_NCX_NOT_FOUND: return "not found"; case ERR_NCX_MISSING_FILE: return "missing file"; case ERR_NCX_UNKNOWN_PARM: return "unknown parameter"; case ERR_NCX_INVALID_NAME: return "invalid name"; case ERR_NCX_UNKNOWN_NS: return "unknown namespace"; case ERR_NCX_WRONG_NS: return "wrong namespace"; case ERR_NCX_WRONG_TYPE: return "wrong data type"; case ERR_NCX_WRONG_VAL: return "wrong value"; case ERR_NCX_MISSING_PARM: return "missing parameter"; case ERR_NCX_EXTRA_PARM: return "extra parameter"; case ERR_NCX_EMPTY_VAL: return "empty value"; case ERR_NCX_MOD_NOT_FOUND: return "module not found"; case ERR_NCX_LEN_EXCEEDED: return "max length exceeded"; case ERR_NCX_INVALID_TOKEN: return "invalid token"; case ERR_NCX_UNENDED_QSTRING: return "unended quoted string"; case ERR_NCX_READ_FAILED: return "read failed"; case ERR_NCX_INVALID_NUM: return "invalid number"; case ERR_NCX_INVALID_HEXNUM: return "invalid hex number"; case ERR_NCX_INVALID_REALNUM: return "invalid real number"; case ERR_NCX_EOF: return "EOF reached"; case ERR_NCX_WRONG_TKTYPE: return "wrong token type"; case ERR_NCX_WRONG_TKVAL: return "wrong token value"; case ERR_NCX_BUFF_SHORT: return "buffer overflow"; case ERR_NCX_INVALID_RANGE: return "invalid range"; case ERR_NCX_OVERLAP_RANGE: return "overlapping range"; case ERR_NCX_DEF_NOT_FOUND: return "definition not found"; case ERR_NCX_DEFSEG_NOT_FOUND: return "definition segment not found"; case ERR_NCX_TYPE_NOT_INDEX: return "type not allowed in index"; case ERR_NCX_INDEX_TYPE_NOT_FOUND: return "index type not found"; case ERR_NCX_TYPE_NOT_MDATA: return "type not mdata"; case ERR_NCX_MDATA_NOT_ALLOWED: return "meta-data not allowed"; case ERR_NCX_TOP_NOT_FOUND: return "top not found"; /*** match netconf errors here ***/ case ERR_NCX_IN_USE: return "resource in use"; case ERR_NCX_INVALID_VALUE: return "invalid value"; case ERR_NCX_TOO_BIG: return "too big"; case ERR_NCX_MISSING_ATTRIBUTE: return "missing attribute"; case ERR_NCX_BAD_ATTRIBUTE: return "bad attribute"; case ERR_NCX_UNKNOWN_ATTRIBUTE: return "unknown attribute"; case ERR_NCX_MISSING_ELEMENT: return "missing element"; case ERR_NCX_BAD_ELEMENT: return "bad element"; case ERR_NCX_UNKNOWN_ELEMENT: return "unknown element"; case ERR_NCX_UNKNOWN_NAMESPACE: return "unknown namespace"; case ERR_NCX_ACCESS_DENIED: return "access denied"; case ERR_NCX_LOCK_DENIED: return "lock denied"; case ERR_NCX_RESOURCE_DENIED: return "resource denied"; case ERR_NCX_ROLLBACK_FAILED: return "rollback failed"; case ERR_NCX_DATA_EXISTS: return "data exists"; case ERR_NCX_DATA_MISSING: return "data missing"; case ERR_NCX_OPERATION_NOT_SUPPORTED: return "operation not supported"; case ERR_NCX_OPERATION_FAILED: return "operation failed"; case ERR_NCX_PARTIAL_OPERATION: return "partial operation"; /* netconf error extensions */ case ERR_NCX_WRONG_NAMESPACE: return "wrong namespace"; case ERR_NCX_WRONG_NODEDEPTH: return "wrong node depth"; case ERR_NCX_WRONG_OWNER: return "wrong owner"; case ERR_NCX_WRONG_ELEMENT: return "wrong element"; case ERR_NCX_WRONG_ORDER: return "wrong order"; case ERR_NCX_EXTRA_NODE: return "extra node"; case ERR_NCX_WRONG_NODETYP: return "wrong node type"; case ERR_NCX_WRONG_NODETYP_SIM: return "expecting complex node type"; case ERR_NCX_WRONG_NODETYP_CPX: return "expecting string node type"; case ERR_NCX_WRONG_DATATYP: return "wrong data type"; case ERR_NCX_WRONG_DATAVAL: return "wrong data value"; case ERR_NCX_NUMLEN_TOOBIG: return "invalid number length"; case ERR_NCX_NOT_IN_RANGE: return "value not in range"; case ERR_NCX_WRONG_NUMTYP: return "wrong number type"; case ERR_NCX_EXTRA_ENUMCH: return "invalid enum value"; case ERR_NCX_VAL_NOTINSET: return "value not in set"; case ERR_NCX_EXTRA_LISTSTR: return "extra list string found"; case ERR_NCX_UNKNOWN_OBJECT: return "unknown object"; case ERR_NCX_EXTRA_PARMINST: return "extra parameter instance"; case ERR_NCX_EXTRA_CHOICE: return "extra case in choice"; case ERR_NCX_MISSING_CHOICE: return "missing mandatory choice"; case ERR_NCX_CFG_STATE: return "wrong config state"; case ERR_NCX_UNKNOWN_APP: return "unknown application"; case ERR_NCX_UNKNOWN_TYPE: return "unknown data type"; case ERR_NCX_NO_ACCESS_ACL: return "access control violation"; case ERR_NCX_NO_ACCESS_LOCK: return "config locked"; case ERR_NCX_NO_ACCESS_STATE: return "wrong config state"; case ERR_NCX_NO_ACCESS_MAX: return "max-access exceeded"; case ERR_NCX_WRONG_INDEX_TYPE: return "wrong index type"; case ERR_NCX_WRONG_INSTANCE_TYPE: return "wrong instance type"; case ERR_NCX_MISSING_INDEX: return "missing index component"; case ERR_NCX_CFG_NOT_FOUND: return "config not found"; case ERR_NCX_EXTRA_ATTR: return "extra attribute instance(s) found"; case ERR_NCX_MISSING_ATTR: return "required attribute not found"; case ERR_NCX_MISSING_VAL_INST: return "required value instance not found"; case ERR_NCX_EXTRA_VAL_INST: return "extra value instance(s) found"; case ERR_NCX_NOT_WRITABLE: return "target is read only"; case ERR_NCX_INVALID_PATTERN: return "invalid pattern"; case ERR_NCX_WRONG_VERSION: return "wrong version"; case ERR_NCX_CONNECT_FAILED: return "connect failed"; case ERR_NCX_UNKNOWN_HOST: return "unknown host"; case ERR_NCX_SESSION_FAILED: return "session failed"; case ERR_NCX_AUTH_FAILED: return "authentication failed"; case ERR_NCX_UNENDED_COMMENT: return "end of comment not found"; case ERR_NCX_INVALID_CONCAT: return "invalid string concatenation"; case ERR_NCX_IMP_NOT_FOUND: return "import not found"; case ERR_NCX_MISSING_TYPE: return "missing typedef sub-section"; case ERR_NCX_RESTRICT_NOT_ALLOWED: return "restriction not allowed for this type"; case ERR_NCX_REFINE_NOT_ALLOWED: return "specified refinement not allowed"; case ERR_NCX_DEF_LOOP: return "definition loop detected"; case ERR_NCX_DEFCHOICE_NOT_OPTIONAL: return "default case contains mandatory object(s)"; case ERR_NCX_IMPORT_LOOP: return "import loop"; case ERR_NCX_INCLUDE_LOOP: return "include loop"; case ERR_NCX_EXP_MODULE: return "expecting module"; case ERR_NCX_EXP_SUBMODULE: return "expecting submodule"; case ERR_NCX_PREFIX_NOT_FOUND: return "undefined prefix"; case ERR_NCX_IMPORT_ERRORS: return "imported module has errors"; case ERR_NCX_PATTERN_FAILED: return "pattern match failed"; case ERR_NCX_INVALID_TYPE_CHANGE: return "invalid data type change"; case ERR_NCX_MANDATORY_NOT_ALLOWED: return "mandatory object not allowed"; case ERR_NCX_UNIQUE_TEST_FAILED: return "unique-stmt test failed"; case ERR_NCX_MAX_ELEMS_VIOLATION: return "max-elements exceeded"; case ERR_NCX_MIN_ELEMS_VIOLATION: return "min-elements not reached"; case ERR_NCX_MUST_TEST_FAILED: return "must-stmt test failed"; case ERR_NCX_DATA_REST_VIOLATION: return "data restriction violation"; case ERR_NCX_INSERT_MISSING_INSTANCE: return "missing instance for insert operation"; case ERR_NCX_NOT_CONFIG: return "object not config"; case ERR_NCX_INVALID_CONDITIONAL: return "invalid conditional object"; case ERR_NCX_USING_OBSOLETE: return "using obsolete definition"; case ERR_NCX_INVALID_AUGTARGET: return "invalid augment target"; case ERR_NCX_DUP_REFINE_STMT: return "duplicate refine sub-clause"; case ERR_NCX_INVALID_DEV_STMT: return "invalid deviate sub-clause"; case ERR_NCX_INVALID_XPATH_EXPR: return "invalid XPath expression syntax"; case ERR_NCX_INVALID_INSTANCEID: return "invalid instance-identifier syntax"; case ERR_NCX_MISSING_INSTANCE: return "require-instance test failed"; case ERR_NCX_UNEXPECTED_INSERT_ATTRS: return "key or select attribute not allowed"; case ERR_NCX_INVALID_UNIQUE_NODE: return "invalid unique-stmt node"; case ERR_NCX_INVALID_DUP_IMPORT: return "invalid duplicate import-stmt"; case ERR_NCX_INVALID_DUP_INCLUDE: return "invalid duplicate include-stmt"; case ERR_NCX_AMBIGUOUS_CMD: return "ambiguous command"; case ERR_NCX_UNKNOWN_MODULE: return "unknown module"; case ERR_NCX_UNKNOWN_VERSION: return "unknown version"; case ERR_NCX_VALUE_NOT_SUPPORTED: return "value not supported"; case ERR_NCX_LEAFREF_LOOP: return "leafref path loop"; case ERR_NCX_VAR_NOT_FOUND: return "variable not found"; case ERR_NCX_VAR_READ_ONLY: return "variable is read-only"; case ERR_NCX_DEC64_BASEOVFL: return "decimal64 base number overflow"; case ERR_NCX_DEC64_FRACOVFL: return "decimal64 fraction precision overflow"; case ERR_NCX_RPC_WHEN_FAILED: return "when-stmt tested false"; case ERR_NCX_NO_MATCHES: return "no matches found"; case ERR_NCX_MISSING_REFTARGET: return "missing refine target"; case ERR_NCX_CANDIDATE_DIRTY: return "candidate cannot be locked, discard-changes needed"; case ERR_NCX_TIMEOUT: return "timeout occurred"; case ERR_NCX_GET_SCHEMA_DUPLICATES: return "multiple module revisions exist"; case ERR_NCX_XPATH_NOT_NODESET: return "XPath result not a nodeset"; case ERR_NCX_XPATH_NODESET_EMPTY: return "XPath node-set result is empty"; case ERR_NCX_IN_USE_LOCKED: return "node is protected by a partial lock"; case ERR_NCX_IN_USE_COMMIT: return "cannot perform the operation with confirmed-commit pending"; case ERR_NCX_SUBMOD_NOT_LOADED: return "cannot directly load a submodule"; case ERR_NCX_ACCESS_READ_ONLY: return "cannot write to a read-only object"; case ERR_NCX_CONFIG_NOT_TARGET: return "cannot write to this configuration directly"; case ERR_NCX_MISSING_RBRACE: return "YANG file missing right brace"; case ERR_NCX_INVALID_FRAMING: return "invalid protocol framing characters received"; case ERR_NCX_PROTO11_NOT_ENABLED: return "base:1.1 protocol not enabled"; case ERR_NCX_CC_NOT_ACTIVE: return "persistent confirmed commit not active"; case ERR_NCX_MULTIPLE_MATCHES: return "multiple matches found"; case ERR_NCX_NO_DEFAULT: return "no schema default for this node"; case ERR_NCX_MISSING_KEY: return "expected key leaf in list"; case ERR_NCX_TOP_LEVEL_MANDATORY_FAILED: return "top-level mandatory objects are not allowed"; /* user warnings start at 400 */ case ERR_MAKFILE_DUP_SRC: return "duplicate source"; case ERR_INC_NOT_FOUND: return "include file not found"; case ERR_CMDLINE_VAL: return "invalid command line value"; case ERR_CMDLINE_OPT: return "invalid command line option"; case ERR_CMDLINE_OPT_UNKNOWN: return "command line option unknown"; case ERR_CMDLINE_SYNTAX: return "invalid command line syntax"; case ERR_CMDLINE_VAL_REQUIRED: return "missing command line value"; case ERR_FORM_INPUT: return "invalid form input"; case ERR_FORM_UNKNOWN: return "invalid form"; case ERR_NCX_NO_INSTANCE: return "no instance found"; case ERR_NCX_SESSION_CLOSED: return "session closed by remote peer"; case ERR_NCX_DUP_IMPORT: return "duplicate import"; case ERR_NCX_PREFIX_DUP_IMPORT: return "duplicate import with different prefix value"; case ERR_NCX_TYPDEF_NOT_USED: return "local typedef not used"; case ERR_NCX_GRPDEF_NOT_USED: return "local grouping not used"; case ERR_NCX_IMPORT_NOT_USED: return "import not used"; case ERR_NCX_DUP_UNIQUE_COMP: return "duplicate unique-stmt argument"; case ERR_NCX_STMT_IGNORED: return "statement ignored"; case ERR_NCX_DUP_INCLUDE: return "duplicate include"; case ERR_NCX_INCLUDE_NOT_USED: return "include not used"; case ERR_NCX_DATE_PAST: return "revision date before 1970"; case ERR_NCX_DATE_FUTURE: return "revision date in the future"; case ERR_NCX_ENUM_VAL_ORDER: return "enum value order"; case ERR_NCX_BIT_POS_ORDER: return "bit position order"; case ERR_NCX_INVALID_STATUS: return "invalid status for child node"; case ERR_NCX_DUP_AUGNODE: return "duplicate sibling node name from external augment"; case ERR_NCX_DUP_IF_FEATURE: return "duplicate if-feature statement"; case ERR_NCX_USING_DEPRECATED: return "using deprecated definition"; case ERR_NCX_MAX_KEY_CHECK: return "XPath object predicate check limit reached"; case ERR_NCX_EMPTY_XPATH_RESULT: return "empty XPath result in must or when expr"; case ERR_NCX_NO_XPATH_ANCESTOR: return "no ancestor node available"; case ERR_NCX_NO_XPATH_PARENT: return "no parent node available"; case ERR_NCX_NO_XPATH_CHILD: return "no child node available"; case ERR_NCX_NO_XPATH_DESCENDANT: return "no descendant node available"; case ERR_NCX_NO_XPATH_NODES: return "no nodes available"; case ERR_NCX_BAD_REV_ORDER: return "bad revision-stmt order"; case ERR_NCX_DUP_PREFIX: return "duplicate prefix"; case ERR_NCX_IDLEN_EXCEEDED: return "identifier length exceeded"; case ERR_NCX_LINELEN_EXCEEDED: return "display line length exceeded"; case ERR_NCX_RCV_UNKNOWN_CAP: return "received unknown capability"; case ERR_NCX_RCV_INVALID_MODCAP: return "invalid module capability URI"; case ERR_NCX_USING_ANYXML: return "unknown child node, using anyxml"; case ERR_NCX_USING_BADDATA: return "invalid value used for parm"; case ERR_NCX_USING_STRING: return "changing object type to string"; case ERR_NCX_USING_RESERVED_NAME: return "using a reserved name"; case ERR_NCX_CONF_PARM_EXISTS: return "conf file parm already exists"; case ERR_NCX_NO_REVISION: return "no valid revision statements found"; case ERR_NCX_DEPENDENCY_ERRORS: return "dependency file has errors"; case ERR_NCX_TOP_LEVEL_MANDATORY: return "top-level object is mandatory"; case ERR_NCX_FILE_MOD_MISMATCH: return "file name does not match [sub]module name"; case ERR_NCX_UNIQUE_CONDITIONAL_MISMATCH: return "unique-stmt component conditions do not match parent list"; case ERR_NCX_DUP_DATE_IN_REV_HISTORY: return "duplicated dates in revision history"; /* system info codes start at 500 */ case ERR_PARS_SECDONE: return "invalid command line value"; case ERR_NCX_SKIPPED: return "operation skipped"; case ERR_NCX_CANCELED: return "operation canceled"; default: return "--"; } /*NOTREACHED*/ } /* get_error_string */ /******************************************************************** * FUNCTION errno_to_status * * Get the errno variable and convert it to a status_t * * INPUTS: * none; must be called just after error occurred to prevent * the errno variable from being overwritten by a new operation * * RETURNS: * status_t for the errno enum *********************************************************************/ status_t errno_to_status (void) { switch (errno) { case EACCES: return ERR_NCX_ACCESS_DENIED; case EAFNOSUPPORT: case EPROTONOSUPPORT: return ERR_NCX_OPERATION_NOT_SUPPORTED; case EINVAL: return ERR_NCX_INVALID_VALUE; case EMFILE: return ERR_NCX_OPERATION_FAILED; case ENFILE: return ERR_NCX_RESOURCE_DENIED; case ENOBUFS: case ENOMEM: return ERR_INTERNAL_MEM; default: return ERR_NCX_OPERATION_FAILED; } } /* errno_to_status */ /******************************************************************** * FUNCTION status_init * * Init this module * *********************************************************************/ void status_init (void) { error_level = 0; memset(&error_stack, 0x0, sizeof(error_stack)); error_count = 0; } /* status_init */ /******************************************************************** * FUNCTION status_cleanup * * Cleanup this module * *********************************************************************/ void status_cleanup (void) { clear_errors(); } /* status_cleanup */ /******************************************************************** * FUNCTION print_error_count * * Print the error_count field, if it is non-zero * to STDOUT or the logfile * Clears out the error_count afterwards so * the count will start over after printing!!! * *********************************************************************/ void print_error_count (void) { if (error_count) { log_error("\n\n*** Total Internal Errors: %u ***\n", error_count); error_count = 0; } } /* print_error_count */ /******************************************************************** * FUNCTION print_error_messages * * Print the error number and error message for each error * to STDOUT or the logfile * *********************************************************************/ void print_error_messages (void) { status_t res; /* internal errors */ for (res = NO_ERR; res < ERR_LAST_INT_ERR; res++) { log_write("\n%3d\t%s", res, get_error_string(res)); } /* system errors */ for (res = ERR_SYS_BASE; res < ERR_LAST_SYS_ERR; res++) { log_write("\n%3d\t%s", res, get_error_string(res)); } /* user errors */ for (res = ERR_USR_BASE; res < ERR_LAST_USR_ERR; res++) { log_write("\n%3d\t%s", res, get_error_string(res)); } /* warnings */ for (res = ERR_WARN_BASE; res < ERR_LAST_WARN; res++) { log_write("\n%3d\t%s", res, get_error_string(res)); } log_write("\n"); } /* print_error_messages */ /* END file status.c */ yuma123_2.14/netconf/src/ncx/xpath_wr.c0000664000175000017500000001512114770023131020136 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xpath_wr.c Write an XPath expression in normalized format ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 24apr09 abb begun; ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xpath_wr #include "xpath_wr.h" #endif /******************************************************************** * FUNCTION wr_expr * * Write the specified XPath expression to the current session * using the default prefixes * * The XPath pcb must be previously parsed and found valid * * INPUTS: * scb == session control block to use * msg == message header to use * xpathval == the value containing the XPath expr to write * * RETURNS: * status *********************************************************************/ static status_t wr_expr (ses_cb_t *scb, xpath_pcb_t *pcb) { const xmlChar *cur_val, *prefix; boolean done, needspace, needprefix; xmlns_id_t cur_nsid; tk_type_t cur_typ; status_t res; uint32 quotes; if (pcb->tkc == NULL || pcb->parseres != NO_ERR) { return pcb->parseres; } tk_reset_chain(pcb->tkc); done = FALSE; while (!done) { /* get the next token */ res = TK_ADV(pcb->tkc); if (res != NO_ERR) { res = NO_ERR; done = TRUE; continue; } needspace = FALSE; needprefix = FALSE; quotes = 0; cur_typ = TK_CUR_TYP(pcb->tkc); cur_nsid = TK_CUR_NSID(pcb->tkc); cur_val = TK_CUR_VAL(pcb->tkc); switch (cur_typ) { case TK_TT_LBRACE: case TK_TT_RBRACE: case TK_TT_LPAREN: case TK_TT_RPAREN: case TK_TT_LBRACK: case TK_TT_RBRACK: case TK_TT_STAR: case TK_TT_ATSIGN: case TK_TT_COLON: case TK_TT_PERIOD: case TK_TT_FSLASH: case TK_TT_DBLCOLON: case TK_TT_DBLFSLASH: needspace = FALSE; break; case TK_TT_SEMICOL: case TK_TT_COMMA: case TK_TT_EQUAL: case TK_TT_BAR: case TK_TT_PLUS: case TK_TT_MINUS: case TK_TT_LT: case TK_TT_GT: case TK_TT_RANGESEP: case TK_TT_NOTEQUAL: case TK_TT_LEQUAL: case TK_TT_GEQUAL: case TK_TT_STRING: case TK_TT_SSTRING: case TK_TT_TSTRING: case TK_TT_VARBIND: case TK_TT_NCNAME_STAR: case TK_TT_DNUM: case TK_TT_HNUM: case TK_TT_RNUM: needspace = TRUE; break; case TK_TT_MSTRING: case TK_TT_MSSTRING: case TK_TT_QVARBIND: needspace = TRUE; needprefix = TRUE; break; case TK_TT_QSTRING: needspace = TRUE; quotes = 2; break; case TK_TT_SQSTRING: needspace = TRUE; quotes = 1; break; default: return SET_ERROR(ERR_INTERNAL_VAL); } if (cur_val == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } switch (cur_typ) { case TK_TT_VARBIND: case TK_TT_QVARBIND: ses_putchar(scb, '$'); break; default: ; } if (needprefix && cur_nsid != 0) { prefix = xmlns_get_ns_prefix(cur_nsid); if (prefix) { ses_putstr(scb, prefix); ses_putchar(scb, ':'); } else { return SET_ERROR(ERR_INTERNAL_VAL); } } if (quotes == 1) { ses_putchar(scb, '\''); } else if (quotes == 2) { ses_putchar(scb, '"'); } ses_putstr(scb, cur_val); if (cur_typ == TK_TT_NCNAME_STAR) { ses_putchar(scb, ':'); ses_putchar(scb, '*'); } if (needspace && quotes == 0) { ses_putchar(scb, ' '); } if (quotes == 1) { ses_putchar(scb, '\''); } else if (quotes == 2) { ses_putchar(scb, '"'); } } return NO_ERR; } /* wr_expr */ /************ E X P O R T E D F U N C T I O N S *************/ /******************************************************************** * FUNCTION xpath_wr_expr * * Write the specified XPath expression to the current session * using the default prefixes * * The XPath pcb must be previously parsed and found valid * * INPUTS: * scb == session control block to use * msg == message header to use * xpathval == the value containing the XPath expr to write * * RETURNS: * status *********************************************************************/ status_t xpath_wr_expr (ses_cb_t *scb, val_value_t *xpathval) { xpath_pcb_t *pcb; status_t res; #ifdef DEBUG if (!scb || !xpathval) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif pcb = val_get_xpathpcb(xpathval); if (pcb == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } res = wr_expr(scb, pcb); return res; } /* xpath_wr_expr */ /* END xpath_wr.c */ yuma123_2.14/netconf/src/ncx/val_parse.h0000664000175000017500000000153314770023131020265 0ustar vladimirvladimir#ifndef _H_val_parse #define _H_val_parse #include "obj.h" #include "ses.h" #include "status.h" #include "val.h" #include "xml_util.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ status_t val_parse_split (ses_cb_t *scb, obj_template_t *obj, obj_template_t *output, const xml_node_t *startnode, val_value_t *retval); status_t val_parse (ses_cb_t *scb, obj_template_t *obj, const xml_node_t *startnode, val_value_t *retval); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_val_parse */ yuma123_2.14/netconf/src/ncx/yang_ext.c0000664000175000017500000003237714770023131020134 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yang_ext.c YANG module parser, extension statement support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 05jan08 abb begun; start from yang_grp.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_appinfo #include "ncx_appinfo.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yang_ext #include "yang_ext.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define YANG_EXT_DEBUG 1 #endif /******************************************************************** * * * M A C R O S * * * *********************************************************************/ /* used in parser routines to decide if processing can continue * will exit the function if critical error or continue if not */ #define CHK_EXT_EXIT \ if (res != NO_ERR) { \ if (res < ERR_LAST_SYS_ERR || res==ERR_NCX_EOF) { \ ext_free_template(ext); \ return res; \ } else { \ retres = res; \ } \ } /******************************************************************** * FUNCTION consume_yang_arg * * Parse the next N tokens as an argument-stmt * Fill in the arg anf argel fields in the ext_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'argument' keyword * * INPUTS: * tkc == token chain * mod == module in progress * ext == extension template in progress * argdone == address of duplicate entry error flag * * OUTPUTS: * *argdone == TRUE upon exit * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_yang_arg (tk_chain_t *tkc, ncx_module_t *mod, ext_template_t *ext, boolean *argdone) { const xmlChar *val; const char *expstr; xmlChar *errstr; dlq_hdr_t errQ; tk_type_t tktyp; boolean done, yinel, save, errsave; status_t res, retres; #ifdef DEBUG if (!tkc || !mod) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif val = NULL; expstr = NULL; done = FALSE; yinel = FALSE; save = TRUE; res = NO_ERR; retres = NO_ERR; dlq_createSQue(&errQ); /* check duplicate entry error first */ if (*argdone) { retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); save = FALSE; } else { *argdone = TRUE; } /* Get the mandatory argument name */ if (save) { res = yang_consume_id_string(tkc, mod, &ext->arg); } else { errstr = NULL; res = yang_consume_id_string(tkc, mod, &errstr); if (errstr) { m__free(errstr); } } CHK_EXIT(res, retres); /* Get the starting left brace for the sub-clauses * or a semi-colon to end the extension-stmt */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: done = TRUE; break; case TK_TT_LBRACE: break; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; } /* get the extension statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ if (save) { res = ncx_consume_appinfo(tkc, mod, &ext->appinfoQ); } else { res = ncx_consume_appinfo(tkc, mod, &errQ); ncx_clean_appinfoQ(&errQ); } CHK_EXIT(res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_YIN_ELEMENT)) { if (save) { res = yang_consume_boolean(tkc, mod, &ext->argel, &yinel, &ext->appinfoQ); } else { res = yang_consume_boolean(tkc, mod, &errsave, NULL, NULL); } } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_EXIT(res, retres); } return retres; } /* consume_yang_arg */ /******************************************************************** * FUNCTION yang_ext_consume_extension * * Parse the next N tokens as an extension-stmt * Create an ext_template_t struct and add it to the specified Q * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'extension' keyword * * INPUTS: * tkc == token chain * mod == module in progress * * RETURNS: * status of the operation *********************************************************************/ status_t yang_ext_consume_extension (tk_chain_t *tkc, ncx_module_t *mod) { ext_template_t *ext, *testext; const xmlChar *val; const char *expstr; yang_stmt_t *stmt; tk_type_t tktyp; boolean done, arg, stat, desc, ref; status_t res, retres; #ifdef DEBUG if (!tkc || !mod) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif val = NULL; expstr = "keyword"; done = FALSE; arg = FALSE; stat = FALSE; desc = FALSE; ref = FALSE; res = NO_ERR; retres = NO_ERR; /* Get a new ext_template_t to fill in */ ext = ext_new_template(); if (!ext) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&ext->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); /* Get the mandatory extension name */ res = yang_consume_id_string(tkc, mod, &ext->name); CHK_EXT_EXIT; /* Get the starting left brace for the sub-clauses * or a semi-colon to end the extension-stmt */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); ext_free_template(ext); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: done = TRUE; break; case TK_TT_LBRACE: break; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; } /* get the extension statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); ext_free_template(ext); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); ext_free_template(ext); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &ext->appinfoQ); CHK_EXT_EXIT; continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_ARGUMENT)) { res = consume_yang_arg(tkc, mod, ext, &arg); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &ext->status, &stat, &ext->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &ext->descr, &desc, &ext->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &ext->ref, &ref, &ext->appinfoQ); } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_EXT_EXIT; } /* save or delete the ext_template_t struct */ if (ext->name && ncx_valid_name2(ext->name)) { testext = ext_find_extension_all(mod, ext->name); if (testext) { log_error("\nError: extension '%s' already defined " "in '%s' at line %u", ext->name, testext->tkerr.mod->name, testext->tkerr.linenum); retres = ERR_NCX_DUP_ENTRY; ncx_print_errormsg(tkc, mod, retres); ext_free_template(ext); } else { dlq_enque(ext, &mod->extensionQ); /* may have some errors */ if (mod->stmtmode) { stmt = yang_new_ext_stmt(ext); if (stmt) { dlq_enque(stmt, &mod->stmtQ); } else { log_error("\nError: malloc failure for ext_stmt"); retres = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, retres); } } } } else { ext_free_template(ext); } return retres; } /* yang_ext_consume_extension */ /* END file yang_ext.c */ yuma123_2.14/netconf/src/ncx/libncx.h0000664000175000017500000001201714770023131017567 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_libncx #define _H_libncx /* FILE: libncx.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* This is a wrapper for all NCX Library functions. Include individual H files instead to save compile time. NCX Module Library Utility Functions Container of Definitions +-----------------+ | ncx_module_t | | ncxtypes.h | +-----------------+ Template/Schema +-----------------+ | obj_template_t | | obj.h | +-----------------+ ^ | | | Value Instances | | +-----------------+ | +---->| val_value_t | | | val.h | | +-----------------+ | | Data Types | +--------------------+ +---| typ_template_t | | typ.h | +--------------------+ ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 29-oct-05 abb Begun 20-jul-08 abb Start YANG rewrite; remove PSD and PS 23-aug-09 abb Update diagram in header 29-may-10 abb Make libncx wrapper for all H files in libncx.so */ #ifdef __cplusplus extern "C" { #endif #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_b64 #include "b64.h" #endif #ifndef _H_blob #include "blob.h" #endif #ifndef _H_bobhash #include "bobhash.h" #endif #ifndef _H_cap #include "cap.h" #endif #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_cli #include "cli.h" #endif #ifndef _H_conf #include "conf.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ext #include "ext.h" #endif #ifndef _H_getcb #include "getcb.h" #endif #ifndef _H_grp #include "grp.h" #endif #ifndef _H_help #include "help.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncx_appinfo #include "ncx_appinfo.h" #endif #ifndef _H_ncx_feature #include "ncx_feature.h" #endif #ifndef _H_ncx_list #include "ncx_list.h" #endif #ifndef _H_ncx_num #include "ncx_num.h" #endif #ifndef _H_ncx_str #include "ncx_str.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_obj_help #include "obj_help.h" #endif #ifndef _H_op #include "op.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_rpc_err #include "rpc_err.h" #endif #ifndef _H_runstack #include "runstack.h" #endif #ifndef _H_send_buff #include "send_buff.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_ses_msg #include "ses_msg.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_top #include "top.h" #endif #ifndef _H_tstamp #include "tstamp.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifndef _H_var #include "var.h" #endif #ifndef _H_version #include "version.h" #endif #ifndef _H_xml_msg #include "xml_msg.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xml_val #include "xml_val.h" #endif #ifndef _H_xml_wr #include "xml_wr.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xpath1 #include "xpath1.h" #endif #ifndef _H_xpath_yang #include "xpath_yang.h" #endif #ifndef _H_xpath_wr #include "xpath_wr.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif #ifndef _H_yang_ext #include "yang_ext.h" #endif #ifndef _H_yang_grp #include "yang_grp.h" #endif #ifndef _H_yang_obj #include "yang_obj.h" #endif #ifndef _H_yang_parse #include "yang_parse.h" #endif #ifndef _H_yang_typ #include "yang_typ.h" #endif #ifndef _H_yin #include "yin.h" #endif #ifndef _H_yinyang #include "yinyang.h" #endif #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_libncx */ yuma123_2.14/netconf/src/ncx/dlq.h0000664000175000017500000003432414770023131017075 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_dlq #define _H_dlq /* FILE: dlq.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* dlq provides general double-linked list and queue support: API Functions ============= QUEUE initialization/cleanup * dlq_createQue - create and initialize dynamic queue hdr * dlq_destroyQue - destroy previously created dynamic queue * dlq_createSQue - initialize static queue hdr, no destroy needed FIFO queue operations * dlq_enque - add node to end of list * dlq_block_enque - add N nodes to end of list * dlq_deque - return first node, remove from list TEST queue operations * dlq_empty - return TRUE if queue is empty, FALSE otherwise LINEAR search (linked list) * dlq_nextEntry - return node AFTER param node - leave in list * dlq_prevEntry - return node BEFORE param node - leave in list * dlq_firstEntry - return first node in the Q * dlq_lastEntry - return last node in the Q Q INSERTION operations * dlq_insertAhead - add node in list ahead of param node * dlq_insertAfter - add node in list after param node * dlq_block_insertAhead - add N nodes in list ahead of param node * dlq_block_insertAfter - add N nodes in list after param node * dlq_block_move - move contents of srcQ to the destintaion Q Q DELETION operations * dlq_remove - remove a node from a linked list Q DEBUG operations * dlq_dumpHdr - printf Q header info (CPP_DEBUG required) CPP Macros Used: ================ CPP_DEBUG - enables debug code CPP_NO_MACROS - forces function calls instead of macros for some functions. Should not be used except in some debug modes CPP_ICHK - this will force function calls instead of macros and enable lots of parameter checking (internal checks). Should only be used for unit test debugging ENTER_CRIT - this macro hook can be set to call an enter critical section function for thread-safe queueing EXIT_CRIT - this macro hook can be set to call an exit critical section function for thread-safe queueing ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 06-jan-89 abb Begun. 18-jan-91 abb adapted for depend project 12-mar-91 abb adapted for DAVID sbee project 14-jun-91 abb changed que.h to dlq.h 27-apr-05 abb update docs and use for netconf project 15-feb-06 abb make DLQ module const compatible get rid of dlq_hdrPT, as this doesn't work for const pointers. 15-sep-06 abb added dlq_swap function 26-jan-07 abb added dlq_hdr_t alias for NCX naming conventions 12-oct-07 abb add dlq_count */ #include "procdefs.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * C O N S T A N T S * *********************************************************************/ /* que header types */ #define DLQ_NULL_NODE 1313 #define DLQ_SHDR_NODE 2727 #define DLQ_DHDR_NODE 3434 #define DLQ_DATA_NODE 5757 #define DLQ_DEL_NODE 8686 #define DLQ_DEL_DHDR 9696 #define dlq_hdr_t dlq_hdrT /******************************************************************** * * T Y P E S * *********************************************************************/ typedef struct TAGdlq_hdrT { unsigned short hdr_typ; struct TAGdlq_hdrT *prev; struct TAGdlq_hdrT *next; } dlq_hdrT; /* shorthand macros */ #define _hdr_node(P) (((const dlq_hdrT *)(P))->hdr_typ==DLQ_SHDR_NODE \ || ((const dlq_hdrT *)(P))->hdr_typ==DLQ_DHDR_NODE) #define _data_node(P) (((const dlq_hdrT *)(P))->hdr_typ==DLQ_DATA_NODE) /******************************************************************** * * F U N C T I O N S * *********************************************************************/ #ifdef CPP_DEBUG /******************************************************************** * FUNCTION dlq_dumpHdr * * use log_debug to dump a Queue header contents * * INPUTS: * nodeP == Q header cast as a void * *********************************************************************/ extern void dlq_dumpHdr (const void *nodeP); #endif /******************************************************************** * FUNCTION dlq_createQue * * create a dynamic queue header * * RETURNS: * pointer to malloced queue header * NULL if memory error *********************************************************************/ extern dlq_hdrT * dlq_createQue (void); /******************************************************************** * FUNCTION dlq_createSQue * * create a static queue header * * INPUTS: * queAddr == pointer to malloced queue header to initialize *********************************************************************/ extern void dlq_createSQue (dlq_hdrT * queAddr); /******************************************************************** * FUNCTION dlq_destroyQue * * free a dynamic queue header previously allocated * with dlq_createQue * * INPUTS: * listP == pointer to malloced queue header to free *********************************************************************/ extern void dlq_destroyQue (dlq_hdrT * listP); /******************************************************************** * FUNCTION dlq_enque * * add a queue node to the end of a queue list * * INPUTS: * newP == pointer to queue entry to add * listP == pointer to queue list to put newP *********************************************************************/ extern void dlq_enque (REG void *newP, REG dlq_hdrT * listP); /******************************************************************** * FUNCTION dlq_deque * * remove the first queue node from the queue list * * INPUTS: * listP == pointer to queue list remove the first entry * * RETURNS: * pointer to removed first entry * NULL if the queue was empty *********************************************************************/ extern void *dlq_deque (dlq_hdrT * listP); /******************************************************************** * FUNCTION dlq_nextEntry * * get the next queue entry after the current entry * * INPUTS: * nodeP == pointer to current queue entry to use * * RETURNS: * pointer to next queue entry * NULL if the no next entry was found *********************************************************************/ #if defined(CPP_NO_MACROS) extern void *dlq_nextEntry (const void *nodeP); #else #define dlq_nextEntry(P) (_data_node(((const dlq_hdrT *) (P))->next) ? \ ((const dlq_hdrT *) (P))->next : NULL) #endif /* END CPP_NO_MACROS */ /******************************************************************** * FUNCTION dlq_prevEntry * * get the previous queue entry before the current entry * * INPUTS: * nodeP == pointer to current queue entry to use * * RETURNS: * pointer to previous queue entry * NULL if the no previous entry was found *********************************************************************/ #if defined(CPP_NO_MACROS) extern void *dlq_prevEntry (const void *nodeP); #else #define dlq_prevEntry(P) (_data_node(((const dlq_hdrT *) (P))->prev ) ? \ ((const dlq_hdrT *) (P))->prev : NULL) #endif /* CPP_NO_MACROS */ /******************************************************************** * FUNCTION dlq_insertAhead * * insert the new queue entry before the current entry * * INPUTS: * newP == pointer to new queue entry to insert ahead of nodeP * nodeP == pointer to current queue entry to insert ahead *********************************************************************/ extern void dlq_insertAhead (void *newP, void *nodeP); /******************************************************************** * FUNCTION dlq_insertAfter * * insert the new queue entry after the current entry * * INPUTS: * newP == pointer to new queue entry to insert after nodeP * nodeP == pointer to current queue entry to insert after *********************************************************************/ extern void dlq_insertAfter (void *newP, void *nodeP); /******************************************************************** * FUNCTION dlq_remove * * remove the queue entry from its queue list * entry MUST have been enqueued somehow before * this function is called * * INPUTS: * nodeP == pointer to queue entry to remove from queue *********************************************************************/ extern void dlq_remove (void *nodeP); /******************************************************************** * FUNCTION dlq_swap * * remove the cur_node queue entry from its queue list * and replace it with the new_node * cur_node entry MUST have been enqueued somehow before * this function is called. * new_node MUST NOT already be in a queue * * INPUTS: * new_node == pointer to new queue entry to put into queue * cur_node == pointer to current queue entry to remove from queue *********************************************************************/ extern void dlq_swap (void *new_node, void *cur_node); /******************************************************************** * FUNCTION dlq_firstEntry * * get the first entry in the queue list * * INPUTS: * listP == pointer to queue list to get the first entry from * * RETURNS: * pointer to first queue entry * NULL if the queue is empty *********************************************************************/ #if defined(CPP_NO_MACROS) extern void *dlq_firstEntry (const dlq_hdrT * listP); #else #define dlq_firstEntry(P) ((P) != ((const dlq_hdrT *)(P))->next ? \ ((const dlq_hdrT *)(P))->next : NULL) #endif /* CPP_NO_MACROS */ /******************************************************************** * FUNCTION dlq_lastEntry * * get the last entry in the queue list * * INPUTS: * listP == pointer to queue list to get the last entry from * * RETURNS: * pointer to last queue entry * NULL if the queue is empty *********************************************************************/ #if defined(CPP_NO_MACROS) extern void *dlq_lastEntry (const dlq_hdrT * listP); #else #define dlq_lastEntry(P) ((P) != ((const dlq_hdrT *)(P))->next ? \ ((const dlq_hdrT *)(P))->prev : NULL) #endif /* CPP_NO_MACROS */ /******************************************************************** * FUNCTION dlq_empty * * check if queue list is empty * * INPUTS: * listP == pointer to queue list to check * * RETURNS: * TRUE if queue is empty * FALSE if queue is not empty *********************************************************************/ #if defined(CPP_NO_MACROS) extern boolean dlq_empty (const dlq_hdrT * listP); #else #define dlq_empty(P) (boolean)((P)==((const dlq_hdrT *)(P))->next) #endif /* CPP_NO_MACROS */ /******************************************************************** * FUNCTION dlq_block_enque * * add all the queue entries in the srcP queue list to the * end of the dstP queue list * * INPUTS: * srcP == pointer to queue list entry to add end of dstP list * dstP == pointer to queue list to add all newP entries *********************************************************************/ extern void dlq_block_enque (dlq_hdrT * srcP, dlq_hdrT * dstP); /******************************************************************** * FUNCTION dlq_block_insertAhead * * insert all the entries in the srcP queue list before * the dstP queue entry * * INPUTS: * srcP == pointer to new queue list to insert all entries * ahead of dstP * dstP == pointer to current queue entry to insert ahead *********************************************************************/ extern void dlq_block_insertAhead (dlq_hdrT *srcP, void *dstP); /******************************************************************** * FUNCTION dlq_block_insertAfter * * insert all the entries in the srcP queue list after * the dstP queue entry * * INPUTS: * srcP == pointer to new queue list to insert all entries * after dstP * dstP == pointer to current queue entry to insert after *********************************************************************/ extern void dlq_block_insertAfter (dlq_hdrT *srcP, void *dstP); /******************************************************************** * FUNCTION dlq_block_move * * enque from [srcP .. end of srcQ list] to the dstQ * insert all the entries in the srcP queue list after * the dstP queue entry * * INPUTS: * srcQ == pointer to source queue list to move entries from * srcP == pointer to source queue entry in the srcQ * move this entry and all entries to the end of * the srcQ to the end of dstQ * dstQ == pointer to destination queue list to move the * entries from the srcQ to the end of this queue *********************************************************************/ extern void dlq_block_move (dlq_hdrT *srcQ, void *srcP, dlq_hdrT * dstQ); /******************************************************************** * FUNCTION dlq_count * * get the number of queue entries in the listP queue list * * INPUTS: * listP == pointer to queue list to check * * RETURNS: * number of queue entries found in listP queue *********************************************************************/ extern unsigned int dlq_count (const dlq_hdrT *listP); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_dlq */ yuma123_2.14/netconf/src/ncx/var.h0000664000175000017500000004762314770023131017113 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_var #define _H_var /* FILE: var.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 23-aug-07 abb Begun 09-mar-09 abb Add more support for yangcli */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_runstack #include "runstack.h" #endif #ifndef _H_val #include "val.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* top or parm values for the istop parameter */ #define ISTOP TRUE #define ISPARM FALSE /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* different types of variables supported */ typedef enum var_type_t_ { VAR_TYP_NONE, VAR_TYP_SESSION, VAR_TYP_LOCAL, VAR_TYP_CONFIG, VAR_TYP_GLOBAL, VAR_TYP_SYSTEM, VAR_TYP_QUEUE } var_type_t; /* struct of NCX user variable mapping for yangcli */ typedef struct ncx_var_t_ { dlq_hdr_t hdr; var_type_t vartype; xmlns_id_t nsid; /* set to zero if not used */ xmlChar *name; val_value_t *val; } ncx_var_t; /* values for isleft parameter in var_check_ref */ typedef enum var_side_t_ { ISRIGHT, ISLEFT } var_side_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION var_free * * Free a ncx_var_t struct * * INPUTS: * var == var struct to free * *********************************************************************/ extern void var_free (ncx_var_t *var); /******************************************************************** * FUNCTION var_clean_varQ * * Clean a Q of ncx_var_t * * INPUTS: * varQ == Q of var structs to free * *********************************************************************/ extern void var_clean_varQ (dlq_hdr_t *varQ); /******************************************************************** * FUNCTION var_clean_type_from_varQ * * Clean all entries of one type from a Q of ncx_var_t * * INPUTS: * varQ == Q of var structs to free * vartype == variable type to delete *********************************************************************/ extern void var_clean_type_from_varQ (dlq_hdr_t *varQ, var_type_t vartype); /******************************************************************** * FUNCTION var_set_str * * Find and set (or create a new) global user variable * * INPUTS: * rcxt == runstack context to use to find the var * name == var name to set * namelen == length of name * value == var value to set * vartype == variable type * * RETURNS: * status *********************************************************************/ extern status_t var_set_str (runstack_context_t *rcxt, const xmlChar *name, uint32 namelen, const val_value_t *value, var_type_t vartype); /******************************************************************** * FUNCTION var_set * * Find and set (or create a new) global user variable * * INPUTS: * rcxt == runstack context to use to find the var * name == var name to set * value == var value to set * vartype == variable type * * RETURNS: * status *********************************************************************/ extern status_t var_set (runstack_context_t *rcxt, const xmlChar *name, const val_value_t *value, var_type_t vartype); /******************************************************************** * FUNCTION var_set_str_que * * Find and set (or create a new) global user variable * * INPUTS: * varQ == variable binding Q to use instead of runstack * name == var name to set * namelen == length of name * value == var value to set * * RETURNS: * status *********************************************************************/ extern status_t var_set_str_que (dlq_hdr_t *varQ, const xmlChar *name, uint32 namelen, const val_value_t *value); /******************************************************************** * FUNCTION var_set_que * * Find and set (or create a new) Q-based user variable * * INPUTS: * varQ == varbind Q to use * name == var name to set * value == var value to set * * RETURNS: * status *********************************************************************/ extern status_t var_set_que (dlq_hdr_t *varQ, const xmlChar *name, const val_value_t *value); /******************************************************************** * FUNCTION var_set_move_que * * Find or create and set a Q-based user variable * * INPUTS: * varQ == varbind Q to use * name == var name to set * value == var value to set (pass off memory, do not clone!) * * RETURNS: * status *********************************************************************/ extern status_t var_set_move_que (dlq_hdr_t *varQ, const xmlChar *name, val_value_t *value); /******************************************************************** * FUNCTION var_set_move * * Find and set (or create a new) global user variable * Use the provided entry which will be freed later * This function will not clone the value like var_set * * INPUTS: * rcxt == runstack context to use to find the var * name == var name to set * namelen == length of name string * vartype == variable type * value == var value to set * * RETURNS: * status *********************************************************************/ extern status_t var_set_move (runstack_context_t *rcxt, const xmlChar *name, uint32 namelen, var_type_t vartype, val_value_t *value); /******************************************************************** * FUNCTION var_set_sys * * Find and set (or create a new) global system variable * * INPUTS: * rcxt == runstack context to use to find the var * name == var name to set * value == var value to set * * RETURNS: * status *********************************************************************/ extern status_t var_set_sys (runstack_context_t *rcxt, const xmlChar *name, const val_value_t *value); /******************************************************************** * FUNCTION var_set_from_string * * Find and set (or create a new) global user variable * from a string value instead of a val_value_t struct * * INPUTS: * rcxt == runstack context to use * name == var name to set * valstr == value string to set * vartype == variable type * * RETURNS: * status *********************************************************************/ extern status_t var_set_from_string (runstack_context_t *rcxt, const xmlChar *name, const xmlChar *valstr, var_type_t vartype); /******************************************************************** * FUNCTION var_unset * * Find and remove a local or global user variable * * !!! This function does not try global if local fails !!! * * INPUTS: * rcxt == runstack context to use * name == var name to unset * namelen == length of name string * vartype == variable type * * RETURNS: * status *********************************************************************/ extern status_t var_unset (runstack_context_t *rcxt, const xmlChar *name, uint32 namelen, var_type_t vartype); /******************************************************************** * FUNCTION var_unset_que * * Find and remove a Q-based user variable * * INPUTS: * varQ == Q of ncx_var_t to use * name == var name to unset * namelen == length of name string * nsid == namespace ID to check if non-zero * *********************************************************************/ extern status_t var_unset_que (dlq_hdr_t *varQ, const xmlChar *name, uint32 namelen, xmlns_id_t nsid); /******************************************************************** * FUNCTION var_get_str * * Find a global user variable * * INPUTS: * rcxt == runstack context to use * name == var name to get * namelen == length of name * vartype == variable type * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ extern val_value_t * var_get_str (runstack_context_t *rcxt, const xmlChar *name, uint32 namelen, var_type_t vartype); /******************************************************************** * FUNCTION var_get * * Find a local or global user variable * * INPUTS: * rcxt == runstack context to use * name == var name to get * vartype == variable type * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ extern val_value_t * var_get (runstack_context_t *rcxt, const xmlChar *name, var_type_t vartype); /******************************************************************** * FUNCTION var_get_type_str * * Find a user variable; get its var type * * INPUTS: * rcxt == runstack context to use * name == var name to get * namelen == length of name * globalonly == TRUE to check only the global Q * FALSE to check local, then global Q * * RETURNS: * var type if found, or VAR_TYP_NONE *********************************************************************/ extern var_type_t var_get_type_str (runstack_context_t *rcxt, const xmlChar *name, uint32 namelen, boolean globalonly); /******************************************************************** * FUNCTION var_get_type * * Get the var type of a specified var name * * INPUTS: * rcxt == runstack context to use * name == var name to get * globalonly == TRUE to check only the global Q * FALSE to check local, then global Q * * RETURNS: * var type or VAR_TYP_NONE if not found *********************************************************************/ extern var_type_t var_get_type (runstack_context_t *rcxt, const xmlChar *name, boolean globalonly); /******************************************************************** * FUNCTION var_get_str_que * * Find a global user variable * * INPUTS: * varQ == queue of ncx_var_t to use * name == var name to get * namelen == length of name * nsid == namespace ID for name (0 if not used) * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ extern val_value_t * var_get_str_que (dlq_hdr_t *varQ, const xmlChar *name, uint32 namelen, xmlns_id_t nsid); /******************************************************************** * FUNCTION var_get_que * * Find a Q-based user variable * * INPUTS: * varQ == Q of ncx_var_t to use * name == var name to get * nsid == namespace ID for name (0 if not used) * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ extern val_value_t * var_get_que (dlq_hdr_t *varQ, const xmlChar *name, xmlns_id_t nsid); /******************************************************************** * FUNCTION var_get_que_raw * * Find a Q-based user variable; return the var struct instead * of just the value * * INPUTS: * varQ == Q of ncx_var_t to use * nsid == namespace ID to match (0 if not used) * name == var name to get * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ extern ncx_var_t * var_get_que_raw (dlq_hdr_t *varQ, xmlns_id_t nsid, const xmlChar *name); /******************************************************************** * FUNCTION var_get_local * * Find a local user variable * * INPUTS: * rcxt == runstack context to use * name == var name to get * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ extern val_value_t * var_get_local (runstack_context_t *rcxt, const xmlChar *name); /******************************************************************** * FUNCTION var_get_local_str * * Find a local user variable, count-based name string * * INPUTS: * rcxt == runstack context to use * name == var name to get * * RETURNS: * pointer to value, or NULL if not found *********************************************************************/ extern val_value_t * var_get_local_str (runstack_context_t *rcxt, const xmlChar *name, uint32 namelen); /******************************************************************** * FUNCTION var_check_ref * * Check if the immediate command sub-string is a variable * reference. If so, return the (vartype, name, namelen) * tuple that identifies the reference. Also return * the total number of chars consumed from the input line. * * E.g., * * $foo = get-config filter=@filter.xml * * INPUTS: * rcxt == runstack context to use * line == command line string to expand * isleft == TRUE if left hand side of an expression * == FALSE if right hand side ($1 type vars allowed) * len == address of number chars parsed so far in line * vartype == address of return variable Q type * name == address of string start return val * namelen == address of name length return val * * OUTPUTS: * *len == number chars consumed by this function * *vartype == variable type enum * *name == start of name string * *namelen == length of *name string * * RETURNS: * status *********************************************************************/ extern status_t var_check_ref (runstack_context_t *rcxt, const xmlChar *line, var_side_t side, uint32 *len, var_type_t *vartype, const xmlChar **name, uint32 *namelen); /******************************************************************** * FUNCTION var_get_script_val * * Create or fill in a val_value_t struct for a parameter assignment * within the script processing mode * * See ncxcli.c for details on the script syntax * * INPUTS: * rcxt == runstack context to use * obj == expected type template * == NULL and will be set to NCX_BT_STRING for * simple types * val == value to fill in :: val->obj MUST be set * == NULL to create a new one * strval == string value to check * istop == TRUE (ISTOP) if calling from top level assignment * An unquoted string is the start of a command * == FALSE (ISPARM) if calling from a parameter parse * An unquoted string is just a string * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * If error, then returns NULL; * If no error, then returns pointer to new val or filled in 'val' *********************************************************************/ extern val_value_t * var_get_script_val (runstack_context_t *rcxt, obj_template_t *obj, val_value_t *val, const xmlChar *strval, boolean istop, status_t *res); /******************************************************************** * FUNCTION var_get_script_val_ex * * Create or fill in a val_value_t struct for a parameter assignment * within the script processing mode * Allow external values * * See ncxcli.c for details on the script syntax * * INPUTS: * rcxt == runstack context to use * parentobj == container or list real node parent of 'obj' * == NULL and will be set to NCX_BT_STRING for * simple types * obj == expected type template * == NULL and will be set to NCX_BT_STRING for * simple types * val == value to fill in :: val->obj MUST be set * == NULL to create a new one * strval == string value to check * istop == TRUE (ISTOP) if calling from top level assignment * An unquoted string is the start of a command * == FALSE (ISPARM) if calling from a parameter parse * An unquoted string is just a string * fillval == value from yangcli, could be NCX_BT_EXTERN; * used instead of strval! * == NULL: not used * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * If error, then returns NULL; * If no error, then returns pointer to new val or filled in 'val' *********************************************************************/ extern val_value_t * var_get_script_val_ex (runstack_context_t *rcxt, obj_template_t *parentobj, obj_template_t *obj, val_value_t *val, const xmlChar *strval, boolean istop, val_value_t *fillval, status_t *res); /******************************************************************** * FUNCTION var_check_script_val * * Create a val_value_t struct for a parameter assignment * within the script processing mode, if a var ref is found * * See yangcli documentation for details on the script syntax * * INPUTS: * rcxt == runstack context to use * obj == expected object template * == NULL and will be set to NCX_BT_STRING for * simple types * strval == string value to check * istop == TRUE if calling from top level assignment * An unquoted string is the start of a command * == FALSE if calling from a parameter parse * An unquoted string is just a string * res == address of status result * * OUTPUTS: * *res == status * * RETURNS: * If no error, then returns pointer to new malloced val * If error, then returns NULL *********************************************************************/ extern val_value_t * var_check_script_val (runstack_context_t *rcxt, obj_template_t *obj, const xmlChar *strval, boolean istop, status_t *res); /******************************************************************** * FUNCTION var_cvt_generic * * Cleanup after a yangcli session has ended * * INPUTS: * varQ == Q of ncx_var_t to cleanup and change to generic * object pointers * *********************************************************************/ extern void var_cvt_generic (dlq_hdr_t *varQ); /******************************************************************** * FUNCTION var_find * * Find a complete var struct for use with XPath * * INPUTS: * rcxt == runstack context to use * varname == variable name string * nsid == namespace ID for varname (0 is OK) * * RETURNS: * pointer to ncx_var_t for the first match found (local or global) *********************************************************************/ extern ncx_var_t * var_find (runstack_context_t *rcxt, const xmlChar *varname, xmlns_id_t nsid); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_var */ yuma123_2.14/netconf/src/ncx/tk.c0000664000175000017500000031516414770023131016732 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: tk.c YANG Token Manager : Low level token management functions - Tokenization of YANG modules - Tokenization of text config files ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 12nov05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include "procdefs.h" #include "dlq.h" #include "log.h" #include "ncx.h" #include "ncxconst.h" #include "status.h" #include "typ.h" #include "tk.h" #include "xml_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* #define TK_DEBUG 1 */ /* #define TK_RDLN_DEBUG 1 */ #define FL_YANG bit0 /* source is TK_SOURCE_YANG */ #define FL_CONF bit1 /* source is TK_SOURCE_CONF */ #define FL_XPATH bit2 /* source is TK_SOURCE_XPATH */ #define FL_REDO bit3 /* source is TK_SOURCE_REDO */ #define FL_NCX bit4 /* place-holder; deprecated */ #define FL_ALL (FL_YANG|FL_CONF|FL_XPATH|FL_REDO) /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* One quick entry token lookup */ typedef struct tk_ent_t_ { tk_type_t ttyp; uint32 tlen; const char *tid; const char *tname; uint32 flags; } tk_ent_t; /* One quick entry built-in type name lookup */ typedef struct tk_btyp_t_ { ncx_btype_t btyp; uint32 blen; const xmlChar *bid; uint32 flags; } tk_btyp_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /* lookup list of all the non-textual/numeric tokens * !! MAKE SURE THIS TABLE ORDER MATCHES THE ORDER OF tk_type_t * The lengths in this table do not include the terminating zero */ static tk_ent_t tlist [] = { /* align array with token enumeration values, skip 0 */ { TK_TT_NONE, 0, NULL, "none", 0 }, /* ONE CHAR TOKENS */ { TK_TT_LBRACE, 1, "{", "left brace", FL_ALL }, { TK_TT_RBRACE, 1, "}", "right brace", FL_ALL }, { TK_TT_SEMICOL, 1, ";", "semicolon", FL_YANG }, { TK_TT_LPAREN, 1, "(", "left paren", FL_XPATH }, { TK_TT_RPAREN, 1, ")", "right paren", FL_XPATH }, { TK_TT_LBRACK, 1, "[", "left bracket", FL_XPATH }, { TK_TT_RBRACK, 1, "]", "right bracket", FL_XPATH }, { TK_TT_COMMA, 1, ",", "comma", FL_XPATH }, { TK_TT_EQUAL, 1, "=", "equals sign", FL_XPATH }, { TK_TT_BAR, 1, "|", "vertical bar", (FL_YANG|FL_XPATH|FL_REDO) }, { TK_TT_STAR, 1, "*", "asterisk", FL_XPATH }, { TK_TT_ATSIGN, 1, "@", "at sign", FL_XPATH }, { TK_TT_PLUS, 1, "+", "plus sign", (FL_YANG|FL_XPATH|FL_REDO) }, { TK_TT_COLON, 1, ":", "colon", FL_XPATH }, { TK_TT_PERIOD, 1, ".", "period", FL_XPATH }, { TK_TT_FSLASH, 1, "/", "forward slash", FL_XPATH }, { TK_TT_MINUS, 1, "-", "minus", FL_XPATH }, { TK_TT_LT, 1, "<", "less than", FL_XPATH }, { TK_TT_GT, 1, ">", "greater than", FL_XPATH }, /* TWO CHAR TOKENS */ { TK_TT_RANGESEP, 2, "..", "range separator", (FL_REDO|FL_XPATH) }, { TK_TT_DBLCOLON, 2, "::", "double colon", FL_XPATH }, { TK_TT_DBLFSLASH, 2, "//", "double forward slash", FL_XPATH }, { TK_TT_NOTEQUAL, 2, "!=", "not equal sign", FL_XPATH }, { TK_TT_LEQUAL, 2, "<=", "less than or equal", FL_XPATH }, { TK_TT_GEQUAL, 2, ">=", "greater than or equal", FL_XPATH }, /* string classification tokens, all here for name and placeholder */ { TK_TT_STRING, 0, "", "unquoted string", FL_ALL}, { TK_TT_SSTRING, 0, "", "scoped ID string", FL_ALL}, { TK_TT_TSTRING, 0, "", "token string", FL_ALL}, { TK_TT_MSTRING, 0, "", "prefix qualified ID string", FL_ALL}, { TK_TT_MSSTRING, 0, "", "prefix qualified scoped ID string", FL_ALL}, { TK_TT_QSTRING, 0, "", "double quoted string", FL_ALL}, { TK_TT_SQSTRING, 0, "", "single quoted string", FL_ALL}, /* variable binding '$NCName' or '$QName' */ { TK_TT_VARBIND, 0, "", "varbind", FL_XPATH }, { TK_TT_QVARBIND, 0, "", "qvarbind", FL_XPATH }, /* XPath NameTest, form 2: 'NCName:*' */ { TK_TT_NCNAME_STAR, 0, "", "NCName:*", FL_XPATH }, /* number classification tokens */ { TK_TT_DNUM, 0, "", "decimal number", FL_ALL}, { TK_TT_HNUM, 0, "", "hex number", FL_ALL}, { TK_TT_RNUM, 0, "", "real number", FL_ALL}, /* newline for conf file parsing only */ { TK_TT_NEWLINE, 0, "", "newline", FL_CONF}, /* EO Array marker */ { TK_TT_NONE, 0, NULL, "none", 0} }; /* lookup list of YANG builtin type names * also includes NCX extensions xsdlist * and placeholder types for YANG data node types * * !!! ORDER IS HARD-WIRED TO PREVENT RUNTIME INIT FUNCTION !!! * !!! NEED TO CHANGE IF YANG DATA TYPE NAMES ARE CHANGED !!! * !!! NEED TO KEEP ARRAY POSITION AND NCX_BT_ VALUE THE SAME !!! * * hack: string lengths are stored so only string compares * with the correct number of chars are actually made * when parsing the YANG type statements * * TBD: change to runtime strlen evaluation * instead of hardwire lengths. */ static tk_btyp_t blist [] = { { NCX_BT_NONE, 4, (const xmlChar *)"NONE", 0 }, { NCX_BT_ANYDATA, 7, NCX_EL_ANYDATA, 0 }, /* anydata */ { NCX_BT_ANYXML, 6, NCX_EL_ANYXML, 0 }, /* anyxml */ { NCX_BT_BITS, 4, NCX_EL_BITS, FL_YANG }, { NCX_BT_ENUM, 11, NCX_EL_ENUMERATION, FL_YANG }, { NCX_BT_EMPTY, 5, NCX_EL_EMPTY, FL_YANG }, { NCX_BT_BOOLEAN, 7, NCX_EL_BOOLEAN, FL_YANG }, { NCX_BT_INT8, 4, NCX_EL_INT8, FL_YANG }, { NCX_BT_INT16, 5, NCX_EL_INT16, FL_YANG }, { NCX_BT_INT32, 5, NCX_EL_INT32, FL_YANG }, { NCX_BT_INT64, 5, NCX_EL_INT64, FL_YANG }, { NCX_BT_UINT8, 5, NCX_EL_UINT8, FL_YANG }, { NCX_BT_UINT16, 6, NCX_EL_UINT16, FL_YANG }, { NCX_BT_UINT32, 6, NCX_EL_UINT32, FL_YANG }, { NCX_BT_UINT64, 6, NCX_EL_UINT64, FL_YANG }, { NCX_BT_DECIMAL64, 9, NCX_EL_DECIMAL64, FL_YANG }, { NCX_BT_FLOAT64, 7, NCX_EL_FLOAT64, 0 }, { NCX_BT_STRING, 6, NCX_EL_STRING, FL_YANG }, { NCX_BT_BINARY, 6, NCX_EL_BINARY, FL_YANG }, { NCX_BT_INSTANCE_ID, 19, NCX_EL_INSTANCE_IDENTIFIER, FL_YANG }, { NCX_BT_UNION, 5, NCX_EL_UNION, FL_YANG }, { NCX_BT_LEAFREF, 7, NCX_EL_LEAFREF, FL_YANG }, { NCX_BT_IDREF, 11, NCX_EL_IDENTITYREF, FL_YANG }, { NCX_BT_SLIST, 5, NCX_EL_SLIST, 0 }, { NCX_BT_CONTAINER, 9, NCX_EL_CONTAINER, 0 }, { NCX_BT_CHOICE, 6, NCX_EL_CHOICE, 0 }, { NCX_BT_CASE, 4, NCX_EL_CASE, 0 }, { NCX_BT_LIST, 4, NCX_EL_LIST, 0 }, { NCX_BT_NONE, 4, (const xmlChar *)"NONE", 0 } }; /******************************************************************** * FUNCTION new_origstr * * Allocatate a new tk_origstr_t * * INPUTS: * ttyp == token type * newline == TRUE if newline added; FALSE if not * strval == malloced string handed over to this data structure * * RETURNS: * new token or NULL if some error *********************************************************************/ static tk_origstr_t * new_origstr (tk_type_t ttyp, boolean newline, xmlChar *strval) { tk_origstr_t *origstr; origstr = m__getObj(tk_origstr_t); if (origstr == NULL) { return NULL; } memset(origstr, 0x0, sizeof(tk_origstr_t)); if (ttyp == TK_TT_QSTRING) { origstr->origtyp = ((newline) ? TK_ORIGSTR_DQUOTE_NL : TK_ORIGSTR_DQUOTE); } else if (ttyp == TK_TT_SQSTRING) { origstr->origtyp = ((newline) ? TK_ORIGSTR_SQUOTE_NL : TK_ORIGSTR_SQUOTE); } else { SET_ERROR(ERR_INTERNAL_VAL); m__free(origstr); return NULL; } origstr->str = strval; /* hand off memory here */ return origstr; } /* new_origstr */ /******************************************************************** * FUNCTION free_origstr * * Deallocatate a tk_origstr_t * * INPUTS: * origstr == original token string struct to delete *********************************************************************/ static void free_origstr (tk_origstr_t *origstr) { if (origstr->str != NULL) { m__free(origstr->str); } m__free(origstr); } /* free_origstr */ /******************************************************************** * FUNCTION new_token_ptr * * Allocatate a new token pointer * * INPUTS: * tk == token to copy * field == field key to save * * RETURNS: * new token pointer struct or NULL if some error *********************************************************************/ static tk_token_ptr_t * new_token_ptr (tk_token_t *tk, const void *field) { tk_token_ptr_t *tkptr; tkptr = m__getObj(tk_token_ptr_t); if (!tkptr) { return NULL; } memset(tkptr, 0x0, sizeof(tk_token_ptr_t)); tkptr->tk = tk; tkptr->field = field; return tkptr; } /* new_token_ptr */ /******************************************************************** * FUNCTION free_token_ptr * * Free a token pointer struct * * INPUTS: * tkptr == token pointer to free *********************************************************************/ static void free_token_ptr (tk_token_ptr_t *tkptr) { m__free(tkptr); } /* free_token_ptr */ /******************************************************************** * FUNCTION new_token * * Allocatate a new token with a value string that will be copied * * INPUTS: * ttyp == token type * tval == token value or NULL if not used * tlen == token value length if used; Ignored if tval == NULL * * RETURNS: * new token or NULL if some error *********************************************************************/ static tk_token_t * new_token (tk_type_t ttyp, const xmlChar *tval, uint32 tlen) { tk_token_t *tk; tk = m__getObj(tk_token_t); if (!tk) { return NULL; } memset(tk, 0x0, sizeof(tk_token_t)); tk->typ = ttyp; if (tval) { tk->len = tlen; tk->val = xml_strndup(tval, tlen); if (!tk->val) { m__free(tk); return NULL; } } dlq_createSQue(&tk->origstrQ); #ifdef TK_DEBUG if (LOGDEBUG4) { log_debug4("\ntk: new token (%s) ", tk_get_token_name(ttyp)); if (tval) { while (tlen--) { log_debug4("%c", *tval++); } } else { log_debug4("%s", tk_get_token_sym(ttyp)); } } #endif return tk; } /* new_token */ /******************************************************************** * FUNCTION new_mtoken * * Allocatate a new token with a value string that will be * consumed and freed later, and not copied * * INPUTS: * ttyp == token type * tval == token value or NULL if not used * * RETURNS: * new token or NULL if some error *********************************************************************/ static tk_token_t * new_mtoken (tk_type_t ttyp, xmlChar *tval) { tk_token_t *tk; tk = m__getObj(tk_token_t); if (!tk) { return NULL; } memset(tk, 0x0, sizeof(tk_token_t)); tk->typ = ttyp; if (tval) { tk->len = xml_strlen(tval); tk->val = tval; } dlq_createSQue(&tk->origstrQ); #ifdef TK_DEBUG if (LOGDEBUG4) { log_debug4("\ntk: new mtoken (%s) ", tk_get_token_name(ttyp)); if (tval) { while (tlen--) { log_debug4("%c", *tval++); } } else { log_debug4("%s", tk_get_token_sym(ttyp)); } } #endif return tk; } /* new_mtoken */ /******************************************************************** * FUNCTION free_token * * Cleanup and deallocate a tk_token_t * * RETURNS: * none *********************************************************************/ static void free_token (tk_token_t *tk) { tk_origstr_t *origstr; #ifdef TK_DEBUG if (LOGDEBUG4) { log_debug4("\ntk: free_token: (%s)", tk_get_token_name(tk->typ)); if (tk->val) { log_debug4(" val=(%s) ", tk->val); } } #endif if (tk->mod) { m__free(tk->mod); } if (tk->val) { m__free(tk->val); } if (tk->origval) { m__free(tk->origval); } while (!dlq_empty(&tk->origstrQ)) { origstr = (tk_origstr_t *)dlq_deque(&tk->origstrQ); free_origstr(origstr); } m__free(tk); } /* free_token */ /******************************************************************** * FUNCTION new_token_wmod * * Allocatate a new identifier token with a qualifier * is a module name in NCX * is a prefix name in YANG * * INPUTS: * ttyp == token type * mod == module name string, not z-terminated * modlen == 'mod' string length * tval == token value * tlen == token value length * * RETURNS: * new token or NULL if some error *********************************************************************/ static tk_token_t * new_token_wmod (tk_type_t ttyp, const xmlChar *mod, uint32 modlen, const xmlChar *tval, uint32 tlen) { tk_token_t *ret; ret = new_token(ttyp, tval, tlen); if (ret) { ret->modlen = modlen; ret->mod = xml_strndup(mod, modlen); if (!ret->mod) { free_token(ret); return NULL; } } #ifdef TK_DEBUG if (LOGDEBUG3) { log_debug3(" mod: "); while (modlen--) { log_debug3("%c", *mod++); } } #endif return ret; } /* new_token_wmod */ /******************************************************************** * FUNCTION add_new_token * * Allocatate a new token with the specified type * Use the token chain directly * * tkc->bptr == first char in the value string to save * * Add the token to the chain if no error * Update the buffer pointer to 'str' after token is added OK * * INPUTS: * tkc == token chain * ttyp == token type to create * str == pointer to the first char after the last char in the value * value includes chars from bptr to str-1 * == NULL if no value for this token * startpos == line position where this token started * * RETURNS: * status *********************************************************************/ static status_t add_new_token (tk_chain_t *tkc, tk_type_t ttyp, const xmlChar *str, uint32 startpos) { tk_token_t *tk; uint32 total; tk = NULL; total = (str) ? (uint32)(str - tkc->bptr) : 0; if (total > NCX_MAX_STRLEN) { return ERR_NCX_LEN_EXCEEDED; } else if (total == 0) { /* zero length value strings are allowed */ tk = new_token(ttyp, NULL, 0); } else { /* normal case string -- non-zero length */ tk = new_token(ttyp, tkc->bptr, total); } if (!tk) { return ERR_INTERNAL_MEM; } tk->linenum = tkc->linenum; tk->linepos = startpos; dlq_enque(tk, &tkc->tkQ); return NO_ERR; } /* add_new_token */ /******************************************************************** * FUNCTION consume_escaped_char * * Consume and format an escaped character encountered by add_new_qtoken. * INPUTS: * dest == the destination buffer * src == the source buffer * endstr == the end of the source buffer * * RETURNS: * TRUE if source type is TK_SOURCE_XPATH *********************************************************************/ static void consume_escaped_char( xmlChar **dest, const xmlChar **src, const xmlChar *endstr ) { const xmlChar* instr = *src; xmlChar* outstr = *dest; if (instr+1 != endstr ) { switch (instr[1]) { case 'n': *outstr++ = '\n'; break; case 't': *outstr++ = '\t'; break; case '"': *outstr++ = '"'; break; case '\\': *outstr++ = '\\'; break; case '\0': /* let the next loop exit on EO-buffer */ break; default: /* pass through the escape sequence */ *outstr++ = '\\'; *outstr++ = instr[1]; } /* adjust the in pointer if not EO-buffer */ if (instr[1]) { instr += 2; } else { ++instr; } } *src = instr; *dest = outstr; } /******************************************************************** * FUNCTION is_xpath_string * * Helper function that helps improve code clarity. It simply returns * true if sourceType is TK_SOURCE_XPATH. * * Note: This function is likely to be inlined by the compiler. * * INPUTS: * sourceType == the source type to test. * * RETURNS: * TRUE if source type is TK_SOURCE_XPATH *********************************************************************/ static boolean is_xpath_string( const tk_source_t sourceType ) { return TK_SOURCE_XPATH == sourceType; } /******************************************************************** * FUNCTION is_newline_char * * Helper function that helps improve code clarity. It simply returns * true if ch is a newline character. * * Note: This function is likely to be inlined by the compiler. * * INPUTS: * ch == the character to test. * * RETURNS: * TRUE if ch is '\n' *********************************************************************/ static boolean is_newline_char( const xmlChar ch ) { return '\n' == ch; } /******************************************************************** * FUNCTION is_space_or_tab * * Helper function that helps improve code clarity. It simply returns * true if ch is a space or tab character. * * Note: This function is likely to be inlined by the compiler. * * INPUTS: * ch == the character to test. * * RETURNS: * TRUE if ch is '\n' *********************************************************************/ static boolean is_space_or_tab( const xmlChar ch ) { return '\t' == ch || ' ' == ch; } /******************************************************************** * FUNCTION trim_trailing_whitespace * * Helper function that removes any whitespace from the end of a buffer. * * INPUTS: * buffer == the destination buffer to trim * endbuffer == the end of the destination buffer, this should be one * past the newline character at the end of the buffer * * RETURNS: * The new end of the destination buffer *********************************************************************/ static xmlChar* trim_trailing_whitespace( xmlChar *buffer, xmlChar *endbuffer ) { --endbuffer; if ( *endbuffer != '\n' ) { return ++endbuffer; // nothing to trim } --endbuffer; // skip back to first character before the newline while ( endbuffer >= buffer && is_space_or_tab( *endbuffer) ) { --endbuffer; } ++endbuffer; // skip non whitespace character *endbuffer++ = '\n'; // add a newline return endbuffer; } /******************************************************************** * FUNCTION skip_leading_whitespace_src * * Skip leading whitespace in the supplied buffer * * INPUTS: * buffer == the buffer to skip space from * endstr == the end of the buffer, this should be one * past the newline character at the end of the buffer * * RETURNS: * The number of characters skipped. *********************************************************************/ static uint32 skip_leading_whitespace_src( const xmlChar **src, const xmlChar *endstr ) { const xmlChar* instr = *src; uint32 count = 0; while ( instr < endstr ) { if ( ' ' == *instr ) { ++count; } else if ( '\t' == *instr ) { count += NCX_TABSIZE; } else { break; } ++instr; } *src = instr; return count; } /******************************************************************** * FUNCTION format_leading_whitespace * * Add leading whitespace to a destination buffer after a newline was * encountered during a copy operation. * * INPUTS: * destptr == the destination buffer * srcptr == the source buffer * endstr == the end of the buffer, this should be one * past the newline character at the end of the buffer * indent == line position where this token started *********************************************************************/ static void format_leading_whitespace( xmlChar **destptr, const xmlChar **srcptr, const xmlChar *endstr, const uint32 indent ) { uint32 numLeadingSpaceChars = 0; const xmlChar* instr = *srcptr; xmlChar* outstr = *destptr; // get the number of leading whitespaces on the next line numLeadingSpaceChars = skip_leading_whitespace_src( &instr, endstr ); // linepos is the indent total for the next line subtract // the start position and indent the rest if ( numLeadingSpaceChars > indent ) { uint32 totReqSpaces = numLeadingSpaceChars - indent; uint32 numTabs = totReqSpaces / NCX_TABSIZE; uint32 numSpaces = totReqSpaces % NCX_TABSIZE; uint32 numConsumedChars = (uint32)(instr - *srcptr); /* make sure not to write more chars than were read E.g: do not replace * 2 tabs with 7 spaces and over flow the buffer */ if ( numTabs + numSpaces > numConsumedChars ) { if ( numTabs > numConsumedChars ) { numTabs = numConsumedChars; numSpaces = 0; } else { numSpaces = numConsumedChars - numTabs; } } while ( numTabs ) { *outstr++ = '\t'; --numTabs; } while ( numSpaces ) { *outstr++ = ' '; --numSpaces; } } *destptr = outstr; *srcptr = instr; } /******************************************************************** * FUNCTION copy_and_format_token_str * * Copy the supplied TK_TT_QSTRING token, converting any escaped chars * in the source buffer. * * Adjust the leading and trailing whitespace around newline characters * * INPUTS: * dest == A buffer large enough to store the copy characters. * This must be at least as long as the source string. * src == The source string to copy * endstr == pointer to the first char after the last char in the value * src string. * is_xpath == flag indicating of this is an xpath * indent == line position where this token started *********************************************************************/ static void copy_and_format_token_str( xmlChar *dest, const xmlChar *src, const xmlChar *endstr, const boolean is_xpath, const uint32 indent ) { xmlChar *outstr = dest; const xmlChar *instr = src; while (instr < endstr) { /* translate escape char or copy regular char */ if (*instr == '\\') { consume_escaped_char( &outstr, &instr, endstr ); } else { *outstr++ = *instr++; } /* check if last char written was a newline, * DO NOT ADJUST XPATH STRINGS */ if ( !is_xpath ) { if ( is_newline_char( *(outstr-1) ) ) { // skip back to the first character before the newline outstr = trim_trailing_whitespace( dest, outstr ); format_leading_whitespace( &outstr, &instr, endstr, indent ); } } } /* finish the string */ *outstr = 0; } /******************************************************************** * FUNCTION add_new_qtoken * * Allocatate a new TK_TT_QSTRING token * Use the token chain directly * Convert any escaped chars in the buffer first * Adjust the leading and trailing whitespace around * newline characters * * tkc->bptr == first char in the value string to save * * Add the token to the chain if no error * Update the buffer pointer to 'str' after token is added OK * * INPUTS: * tkc == token chain to add new token to * isdouble == TRUE for TK_TT_QSTRING * == FALSE for TK_TT_SQSTRING * tkbuff == token input buffer to use * endstr == pointer to the first char after the last char in the value * value includes chars from bptr to str-1 * == NULL if no value for this token * startline == line number where this token started * startpos == line position where this token started * * RETURNS: * status *********************************************************************/ static status_t add_new_qtoken ( tk_chain_t *tkc, boolean isdouble, xmlChar *tkbuff, const xmlChar *endstr, uint32 startline, uint32 startpos ) { tk_token_t *tk = NULL; xmlChar *origbuff = NULL; uint32 total = (endstr) ? (uint32)(endstr - tkbuff) : 0; if (total > NCX_MAX_STRLEN) { return ERR_NCX_LEN_EXCEEDED; } if (total == 0) { /* zero length value strings are allowed */ tk = (isdouble) ? new_token( TK_TT_QSTRING, NULL, 0) : new_token( TK_TT_SQSTRING, NULL, 0); } else if (!isdouble) { /* single quote string */ tk = new_token( TK_TT_SQSTRING, tkbuff, total ); } else { /* double quote normal case -- non-zero length QSTRING fill the buffer, * while converting escaped chars */ xmlChar *buff = (xmlChar *)m__getMem(total+1); if (!buff) { return ERR_INTERNAL_MEM; } copy_and_format_token_str( buff, tkbuff, endstr, is_xpath_string( tkc->source), startpos ); /* if --format=html or --format=yang then a copy of the original double * quoted string needs to be saved unaltered according to the * YANG spec */ if (TK_DOCMODE(tkc)) { origbuff = xml_strndup(tkbuff, total); if ( !origbuff ) { return ERR_INTERNAL_MEM; } } tk = new_mtoken(TK_TT_QSTRING, buff); if ( !tk ) { m__free(buff); m__free(origbuff); } } if (!tk) { return ERR_INTERNAL_MEM; } tk->linenum = startline; tk->linepos = startpos; tk->origval = origbuff; dlq_enque(tk, &tkc->tkQ); return NO_ERR; } /* add_new_qtoken */ /******************************************************************** * FUNCTION get_token_id * * Check if the spceified string is a NCX non-string token * Checks for 1-char and 2-char tokens only!! * * INPUTS: * buff == token string to check -- NOT ZERO-TERMINATED * len == length of string to check * srctyp == parsing source * (TK_SOURCE_CONF, TK_SOURCE_YANG, TK_SOURCE_CONF, TK_SOURCE_REDO) * * RETURNS: * token type found or TK_TT_NONE if no match *********************************************************************/ static tk_type_t get_token_id (const xmlChar *buff, uint32 len, tk_source_t srctyp) { tk_type_t t; uint32 flags; /* determine which subset of the token list will be checked */ switch (srctyp) { case TK_SOURCE_CONF: flags = FL_CONF; break; case TK_SOURCE_YANG: flags = FL_YANG; break; case TK_SOURCE_XPATH: flags = FL_XPATH; break; case TK_SOURCE_REDO: flags = FL_REDO; break; default: SET_ERROR(ERR_INTERNAL_VAL); return TK_TT_NONE; } /* look in the tlist for the specified token * This is an optimized hack to save time, instead * of a complete search through all data types * * !!! MAKE SURE THE FOR LOOPS BELOW ARE UPDATED * !!! IF THE tk_token_t ENUMERATION IS CHANGED * * Only tokens for the specified source language * and the specified length will be returned */ if (len==1) { for (t=TK_TT_LBRACE; t <= TK_TT_GT; t++) { if (*buff == *tlist[t].tid && (tlist[t].flags & flags)) { return tlist[t].ttyp; } } } else if (len==2) { for (t=TK_TT_RANGESEP; t <= TK_TT_GEQUAL; t++) { if (!xml_strncmp(buff, (const xmlChar *)tlist[t].tid, 2) && (tlist[t].flags & flags)) { return tlist[t].ttyp; } } } return TK_TT_NONE; } /* get_token_id */ /******************************************************************** * FUNCTION tokenize_qstring * * Handle a double-quoted string ("string") * * Converts escaped chars during this first pass processing * * Does not realign whitespace in this function * That is done when strings are concatenated * with finish_qstrings * * INPUTS: * tkc == token chain * * RETURNS: * status of the operation *********************************************************************/ static status_t tokenize_qstring (tk_chain_t *tkc) { xmlChar *str, *tempbuff, *outstr; uint32 total, startline, startpos, linelen; boolean done, done2; status_t res; startline = tkc->linenum; startpos = tkc->linepos; /* the bptr is pointing at the quote char which * indicates the start of a quoted string * find the end of the string of end of the line/buffer * start bptr after the quote since that is not saved * as part of the content, and doesn't count againt maxlen * * This first loop does not process chars yet * because most quoted strings do not contain the * escaped chars, and an extra copy is not needed if this is * a simple quoted string on a single line */ str = ++tkc->bptr; /* skip over escaped double quotes */ done = FALSE; while (!done) { if (!*str) { /* End of buffer */ done = TRUE; } else if (*str == NCX_QSTRING_CH && (*(str-1) != '\\')) { /* ending double quote */ done = TRUE; } else { if (*str == '\\') { if (str[1]) { str++; } if (*str == 'n') { tkc->linepos = 1; } else if (*str =='t') { tkc->linepos += NCX_TABSIZE; } else { tkc->linepos++; } } else { if (*str == '\n') { tkc->linepos = 1; } else if (*str =='\t') { tkc->linepos += NCX_TABSIZE; } else { tkc->linepos++; } } str++; } } total = (uint32)(str - tkc->bptr); if (*str == NCX_QSTRING_CH) { /* easy case, a quoted string on 1 line */ res = add_new_qtoken(tkc, TRUE, tkc->bptr, str, startline, startpos); tkc->bptr = str+1; return res; } /* else we reached the end of the buffer without * finding the QSTRING_CH. If input from buffer this is * an error */ if (!(tkc->flags & TK_FL_MALLOC)) { return ERR_NCX_UNENDED_QSTRING; } /* FILE input, keep reading lines looking for QUOTE_CH * need to get a temp buffer to store all the lines */ tempbuff = (xmlChar *)m__getMem(NCX_MAX_Q_STRLEN+1); if (!tempbuff) { return ERR_INTERNAL_MEM; } outstr = tempbuff; /* check total bytes parsed, and copy into temp buff if any */ if (total) { outstr += xml_strcpy(outstr, tkc->bptr); } else { outstr[0] = 0; } /* ELSE real special case, start of QSTRING last char in the line * This should not happen because a newline should at * least follow the QSTRING char */ /* keep saving lines in tempbuff until the QSTRING_CH is found */ done = FALSE; while (!done) { if (!fgets((char *)tkc->buff, TK_BUFF_SIZE, tkc->fp)) { /* read line failed -- assume EOF */ m__free(tempbuff); return ERR_NCX_UNENDED_QSTRING; } else { tkc->linenum++; tkc->linepos = 1; tkc->bptr = tkc->buff; } #ifdef TK_RDLN_DEBUG if (LOGDEBUG3) { if (xml_strlen(tkc->buff) < 128) { log_debug3("\nNCX Parse: read line (%s)", tkc->buff); } else { log_debug3("\nNCX Parse: read line len (%u)", xml_strlen(tkc->buff)); } } #endif /* look for ending quote on this line */ str = tkc->bptr; done2 = FALSE; while (!done2) { if (!*str) { done2 = TRUE; } else if (*str == NCX_QSTRING_CH && (*(str-1) != '\\')) { done2 = TRUE; } else { str++; } } linelen = (uint32)(str-tkc->bptr); if (*str) { tkc->linepos = linelen+1; done = TRUE; /* stopped on a double quote */ } if (linelen + total < NCX_MAX_Q_STRLEN) { /* copy this line to tempbuff */ outstr += xml_strncpy(outstr, tkc->bptr, linelen); total += linelen; tkc->bptr = str+1; } else { /* would be a buffer overflow */ m__free(tempbuff); return ERR_NCX_LEN_EXCEEDED; } } res = add_new_qtoken(tkc, TRUE, tempbuff, outstr, startline, startpos); m__free(tempbuff); return res; } /* tokenize_qstring */ /******************************************************************** * FUNCTION tokenize_sqstring * * Handle a single-quoted string ('string') * * INPUTS: * tkc == token chain * RETURNS: * status of the operation *********************************************************************/ static status_t tokenize_sqstring (tk_chain_t *tkc) { xmlChar *str, *tempbuff, *outstr; uint32 total, startline, startpos, linelen; status_t res; boolean done; startline = tkc->linenum; startpos = tkc->linepos; /* the bptr is pointing at the quote char which * indicates the start of a quoted string * find the end of the string of end of the line/buffer * start bptr after the quote since that is not saved * as part of the content, and doesn't count againt maxlen */ str = ++tkc->bptr; while (*str && (*str != NCX_SQSTRING_CH)) { str++; } total = (uint32)(str - tkc->bptr); if (*str == NCX_SQSTRING_CH) { /* easy case, a quoted string on 1 line; */ res = add_new_token(tkc, TK_TT_SQSTRING, str, startpos); tkc->bptr = str+1; tkc->linepos += total+2; return NO_ERR; } /* else we reached the end of the buffer without * finding the SQSTRING_CH. If input from buffer this is * an error */ if (!(tkc->flags & TK_FL_MALLOC)) { return ERR_NCX_UNENDED_QSTRING; } /* FILE input, keep reading lines looking for QUOTE_CH * need to get a temp buffer to store all the lines */ tempbuff = (xmlChar *)m__getMem(NCX_MAX_Q_STRLEN+1); if (!tempbuff) { return ERR_INTERNAL_MEM; } outstr = tempbuff; /* check total bytes parsed, and copy into temp buff if any */ total = (uint32)(str - tkc->bptr); if (total) { outstr += xml_strcpy(outstr, tkc->bptr); } else { outstr[0] = 0; } /* keep saving lines in tempbuff until the QSTRING_CH is found */ done = FALSE; while (!done) { if (!fgets((char *)tkc->buff, TK_BUFF_SIZE, tkc->fp)) { /* read line failed -- assume EOF */ m__free(tempbuff); return ERR_NCX_UNENDED_QSTRING; } else { tkc->linenum++; tkc->linepos = 1; tkc->bptr = tkc->buff; } #ifdef TK_RDLN_DEBUG if (LOGDEBUG3) { if (xml_strlen(tkc->buff) < 128) { log_debug3("\nNCX Parse: read line (%s)", tkc->buff); } else { log_debug3("\nNCX Parse: read line len (%u)", xml_strlen(tkc->buff)); } } #endif str = tkc->bptr; while (*str && (*str != NCX_SQSTRING_CH)) { str++; } linelen = (uint32)(str-tkc->bptr); if (*str) { tkc->linepos = linelen+1; done = TRUE; /* stopped on a single quote */ } if (linelen + total < NCX_MAX_Q_STRLEN) { outstr += xml_strncpy(outstr, tkc->bptr, linelen); total += linelen; tkc->bptr = str+1; } else { m__free(tempbuff); return ERR_NCX_LEN_EXCEEDED; } } /* get a token and save the SQSTRING */ res = add_new_qtoken(tkc, FALSE, tempbuff, outstr, startline, startpos); m__free(tempbuff); return res; } /* tokenize_sqstring */ /******************************************************************** * FUNCTION skip_yang_cstring * * Handle a YANG multi-line comment string TK_TT_COMMENT * Advance current buffer pointer past the comment * * INPUTS: * tkc == token chain * * RETURNS: * status of the operation *********************************************************************/ static status_t skip_yang_cstring (tk_chain_t *tkc) { xmlChar *str; boolean done; /* the bptr is pointing at the comment char which * indicates the start of a C style comment */ tkc->linepos += 2; tkc->bptr += 2; /* look for star-slash '*' '/' EO comment sequence */ str = tkc->bptr; while (*str && !(*str=='*' && str[1]=='/')) { if (*str == '\t') { tkc->linepos += NCX_TABSIZE; } else { tkc->linepos++; } str++; } if (*str) { /* simple case, stopped at end of 1 line comment */ tkc->bptr = str+2; tkc->linepos += 2; return NO_ERR; } /* else stopped at end of buffer, get rest of multiline comment */ if (!(tkc->flags & TK_FL_MALLOC)) { return ERR_NCX_UNENDED_COMMENT; } /* FILE input, keep reading lines looking for the * end of comment */ done = FALSE; while (!done) { if (!fgets((char *)tkc->buff, TK_BUFF_SIZE, tkc->fp)) { /* read line failed -- assume EOF */ return ERR_NCX_UNENDED_COMMENT; } else { tkc->linenum++; tkc->linepos = 1; tkc->bptr = tkc->buff; } #ifdef TK_RDLN_DEBUG if (LOGDEBUG3) { if (xml_strlen(tkc->buff) < 128) { log_debug3("\nNCX Parse: read line (%s)", tkc->buff); } else { log_debug3("\nNCX Parse: read line len (%u)", xml_strlen(tkc->buff)); } } #endif str = tkc->bptr; while (*str && !(*str=='*' && str[1]=='/')) { if (*str == '\t') { tkc->linepos += NCX_TABSIZE; } else { tkc->linepos++; } str++; } if (*str) { tkc->linepos += 2; tkc->bptr = str+2; done = TRUE; /* stopped on an end of comment */ } } return NO_ERR; } /* skip_yang_cstring */ /******************************************************************** * FUNCTION tokenize_number * * Handle some sort of number string * * INPUTS: * tkc == token chain * RETURNS: * status of the operation *********************************************************************/ static status_t tokenize_number (tk_chain_t *tkc) { xmlChar *str; uint32 startpos, total; status_t res; startpos = tkc->linepos; str = tkc->bptr; /* the bptr is pointing at the first number char which * is a valid (0 - 9) digit, or a + or - char * get the sign first */ if (*str == '+' || *str == '-') { str++; } /* check if this is a hex number */ if (*str == '0' && NCX_IS_HEX_CH(str[1])) { /* move the start of number portion */ str += 2; while (isxdigit(*str)) { str++; } total = (uint32)(str - tkc->bptr); /* make sure we ended on a proper char and have a proper len */ if (isalpha(*str) || str==tkc->bptr || total > NCX_MAX_HEXCHAR) { return ERR_NCX_INVALID_HEXNUM; } res = add_new_token(tkc, TK_TT_HNUM, str, startpos); tkc->bptr = str; tkc->linepos += total; return res; } /* else not a hex number so just find the end or a '.' */ while (isdigit(*str)) { str++; } /* check if we stopped on a dot, indicating a real number * make sure it's not 2 dots, which is the range separator */ if (*str == '.' && str[1] != '.') { /* this is a real number, get the rest of the value */ str++; /* skip dot char */ while (isdigit(*str)) { str++; } total = (uint32)(str - tkc->bptr); /* make sure we ended on a proper char and have a proper len * This function does not support number entry in * scientific notation, just numbers with decimal points */ if (isalpha(*str) || *str=='.' || total > NCX_MAX_RCHAR) { return ERR_NCX_INVALID_REALNUM; } res = add_new_token(tkc, TK_TT_RNUM, str, startpos); tkc->bptr = str; tkc->linepos += total; return res; } /* else this is a decimal number */ total = (uint32)(str - tkc->bptr); /* make sure we ended on a proper char and have a proper len */ if (isalpha(*str) || total > NCX_MAX_DCHAR) { return ERR_NCX_INVALID_NUM; } res = add_new_token(tkc, TK_TT_DNUM, str, startpos); tkc->bptr = str; tkc->linepos += total; return res; } /* tokenize_number */ /******************************************************************** * FUNCTION get_name_comp * * Get a name component * This will stop the identifier name on the first non-name char * whatever it is. * * INPUTS: * str == start of name component * OUTPUTS * *len == length of valid name component, if NO_ERR * RETURNS: * status *********************************************************************/ static status_t get_name_comp (const xmlChar *str, uint32 *len) { const xmlChar *start; start = str; *len = 0; if (!ncx_valid_fname_ch(*str)) { return ERR_NCX_INVALID_NAME; } str++; while (ncx_valid_name_ch(*str)) { str++; } if ((str - start) < NCX_MAX_NLEN) { /* got a valid identifier fragment */ *len = (uint32)(str - start); return NO_ERR; } else { return ERR_NCX_LEN_EXCEEDED; } /*NOTREACHED*/ } /* get_name_comp */ /******************************************************************** * FUNCTION finish_string * * Finish off a suspected identifier as a plain string * * INPUTS: * tkc == token chain * str == rest of string to process * RETURNS: * status *********************************************************************/ static status_t finish_string (tk_chain_t *tkc, xmlChar *str) { boolean done; tk_type_t ttyp; uint32 startpos, total; status_t res; startpos = tkc->linepos; done = FALSE; while (!done) { if (!*str) { done = TRUE; } else if (xml_isspace(*str) || *str=='\n') { done = TRUE; } else if (tkc->source == TK_SOURCE_YANG) { ttyp = get_token_id(str, 1, tkc->source); switch (ttyp) { case TK_TT_SEMICOL: case TK_TT_LBRACE: case TK_TT_RBRACE: done = TRUE; break; default: ; } } else if (get_token_id(str, 1, tkc->source) != TK_TT_NONE) { done = TRUE; } if (!done) { if (*str == '\t') { tkc->linepos += NCX_TABSIZE; } else { tkc->linepos++; } str++; } } total = (uint32)(str - tkc->bptr); if (total > NCX_MAX_Q_STRLEN) { return ERR_NCX_LEN_EXCEEDED; } res = add_new_token(tkc, TK_TT_STRING, str, startpos); tkc->bptr = str; /* advance the buffer pointer */ return res; } /* finish_string */ /******************************************************************** * FUNCTION tokenize_varbind_string * * Handle some sort of $string, which could be a varbind string * in the form $QName * * INPUTS: * tkc == token chain * RETURNS: * status of the operation *********************************************************************/ static status_t tokenize_varbind_string (tk_chain_t *tkc) { xmlChar *str; const xmlChar *prefix, *name, *item; tk_token_t *tk; uint32 len, prelen; status_t res; prefix = NULL; name = NULL; item = NULL; prelen = 0; /* the bptr is pointing at the dollar sign char */ str = tkc->bptr+1; name = str; while (ncx_valid_name_ch(*str)) { str++; } /* check reasonable length for a QName */ len = (uint32)(str - name); if (!len || len > NCX_MAX_NLEN) { return finish_string(tkc, str); } /* else got a string fragment that could be a valid ID format */ if (*str == NCX_MODSCOPE_CH && str[1] != NCX_MODSCOPE_CH) { /* stopped on the module-scope-identifier token * the first identifier component must be a prefix * or possibly a module name */ prefix = tkc->bptr+1; prelen = len; item = ++str; /* str now points at the start of the imported item * There needs to be at least one valid name component * after the module qualifier */ res = get_name_comp(str, &len); if (res != NO_ERR) { return finish_string(tkc, str); } str += len; /* drop through -- either we stopped on a scope char or * the end of the module-scoped identifier string */ } if (prefix) { /* XPath $prefix:identifier */ tk = new_token_wmod(TK_TT_QVARBIND, prefix, prelen, item, (uint32)(str - item)); } else { /* XPath $identifier */ tk = new_token(TK_TT_VARBIND, tkc->bptr+1, len); } if (!tk) { return ERR_INTERNAL_MEM; } tk->linenum = tkc->linenum; tk->linepos = tkc->linepos; dlq_enque(tk, &tkc->tkQ); len = (uint32)(str - tkc->bptr); tkc->bptr = str; /* advance the buffer pointer */ tkc->linepos += len; return NO_ERR; } /* tokenize_varbind_string */ /******************************************************************** * FUNCTION tokenize_id_string * * Handle some sort of non-quoted string, which could be an ID string * * INPUTS: * tkc == token chain * RETURNS: * status of the operation *********************************************************************/ static status_t tokenize_id_string (tk_chain_t *tkc) { xmlChar *str; const xmlChar *prefix, *item; boolean scoped, namestar; tk_token_t *tk; uint32 len, prelen; status_t res; prefix = NULL; item = NULL; scoped = FALSE; namestar = FALSE; tk = NULL; prelen = 0; /* the bptr is pointing at the first string char which * is a valid identifier first char; start at the next char */ str = tkc->bptr+1; if (tkc->source == TK_SOURCE_REDO) { /* partial ID syntax; redo so range clause * components are not included */ while ((*str != '.') && (*str != '-') && ncx_valid_name_ch(*str)) { str++; } } else { /* allow full YANG identifier syntax */ while (ncx_valid_name_ch(*str)) { str++; } } /* check max identifier length */ if ((str - tkc->bptr) > NCX_MAX_NLEN) { /* string is too long to be an identifier so just * look for any valid 1-char token or whitespace to end it. * * This will ignore any multi-char tokens that may be embedded, * but these unquoted strings are a special case * and this code depends on the fact that there are * no valid NCX token sequences in which a string is * followed by a milti-char token without any * whitespace in between. */ return finish_string(tkc, str); } /* check YANG parser stopped on proper end of ID */ if (tkc->source == TK_SOURCE_YANG && !(*str=='{' || *str==';' || *str == '/' || *str==':' || xml_isspace(*str))) { return finish_string(tkc, str); } /* else got a string fragment that could be a valid ID format */ if (*str == NCX_MODSCOPE_CH && str[1] != NCX_MODSCOPE_CH) { /* stopped on the prefix-scope-identifier token * the first identifier component must be a prefix name */ prefix = tkc->bptr; prelen = (uint32)(str - tkc->bptr); item = ++str; if (tkc->source == TK_SOURCE_XPATH && *item == '*') { namestar = TRUE; str++; /* consume the '*' char */ } else { /* str now points at the start of the imported item * There needs to be at least one valid name component * after the prefix qualifier */ res = get_name_comp(str, &len); if (res != NO_ERR) { return finish_string(tkc, str); } /* if we stopped on a colon char then treat this as a URI */ if (str[len] == ':') { return finish_string(tkc, str); } /* drop through -- either we stopped on a scope char or * the end of the prefix-scoped identifier string */ str += len; } } if (tkc->source != TK_SOURCE_XPATH) { /* got some sort of identifier string * keep going until we don't stop on the scope char */ while (*str == NCX_SCOPE_CH) { res = get_name_comp(++str, &len); if (res != NO_ERR) { return finish_string(tkc, str); } scoped = TRUE; str += len; } /* for Xpath purposes in YANG, treat scoped ID as a string now */ if (scoped && tkc->source==TK_SOURCE_YANG) { return finish_string(tkc, str); } /* done with the string; create a token and save it */ if (prefix) { if ((str - item) > NCX_MAX_Q_STRLEN) { return ERR_NCX_LEN_EXCEEDED; } tk = new_token_wmod(scoped ? TK_TT_MSSTRING : TK_TT_MSTRING, prefix, prelen, item, (uint32)(str - item)); } else { if ((str - tkc->bptr) > NCX_MAX_Q_STRLEN) { return ERR_NCX_LEN_EXCEEDED; } tk = new_token(scoped ? TK_TT_SSTRING : TK_TT_TSTRING, tkc->bptr, (uint32)(str - tkc->bptr)); } } else if (prefix) { if (namestar) { /* XPath 'prefix:*' */ tk = new_token(TK_TT_NCNAME_STAR, prefix, prelen); } else { /* XPath prefix:identifier */ tk = new_token_wmod(TK_TT_MSTRING, prefix, prelen, item, (uint32)(str - item)); } } else { /* XPath identifier */ tk = new_token(TK_TT_TSTRING, tkc->bptr, (uint32)(str - tkc->bptr)); } if (!tk) { return ERR_INTERNAL_MEM; } tk->linenum = tkc->linenum; tk->linepos = tkc->linepos; dlq_enque(tk, &tkc->tkQ); len = (uint32)(str - tkc->bptr); tkc->bptr = str; /* advance the buffer pointer */ tkc->linepos += len; return NO_ERR; } /* tokenize_id_string */ /******************************************************************** * FUNCTION tokenize_string * * Handle some sort of non-quoted string, which is not an ID string * * INPUTS: * tkc == token chain * RETURNS: * status of the operation *********************************************************************/ static status_t tokenize_string (tk_chain_t *tkc) { xmlChar *str; /* the bptr is pointing at the first string char * Don't need to check it. * Move past it, because if we got here, this is not * a valid token of any other kind. * * The 2nd pass of the parser may very well barf on this * string token, or it could be something like a description * clause, where the content is irrelevant to the parser. */ str = tkc->bptr+1; return finish_string(tkc, str); } /* tokenize_string */ /******************************************************************** * FUNCTION concat_qstrings * * Go through the entire token chain and handle all the * quoted string concatenation * * INPUTS: * tkc == token chain to adjust; * start token is the first token * * OUTPUTS: * tkc->tkQ will be adjusted as needed: * First string token in a concat chain will be modified * to include all the text from the chain. All the other * tokens in the concat chain will be deleted * * RETURNS: * status of the operation *********************************************************************/ static status_t concat_qstrings (tk_chain_t *tkc) { tk_token_t *first, *plus, *prev, *next, *last; xmlChar *buff, *str; uint32 bufflen; boolean done; /* find the last consecutive quoted string * concatenate the strings together if possible * and redo the first quoted string to contain * the concatenation of the other strings * * "string1" + "string2" + 'string3' ... */ first = (tk_token_t *)dlq_firstEntry(&tkc->tkQ); while (first) { /* check if 'first' is a quoted string */ if (!(first->typ==TK_TT_QSTRING || first->typ==TK_TT_SQSTRING)) { first = (tk_token_t *)dlq_nextEntry(first); continue; } /* check if any string concat is requested * loop through the tokens following the string 'first' * and find the buffer length needed and the last token * in the "str1" + "str2" + "strn" sequence */ last = NULL; next = (tk_token_t *)dlq_nextEntry(first); bufflen = first->len; done = FALSE; while (!done) { if (!next || next->typ != TK_TT_PLUS) { /* no token or not a '+', back to outer loop */ done = TRUE; } else { /* found '+', should find another string next */ plus = next; next = (tk_token_t *)dlq_nextEntry(next); if (!next || !(next->typ==TK_TT_QSTRING || next->typ==TK_TT_SQSTRING)) { /* error, missing or wrong token */ tkc->cur = (next) ? next : plus; return ERR_NCX_INVALID_CONCAT; } else { /* OK, get rid of '+' and setup next loop */ last = next; bufflen += last->len; dlq_remove(plus); free_token(plus); next = (tk_token_t *)dlq_nextEntry(next); } } } if (last) { /* need to concat some strings */ if (bufflen > NCX_MAX_STRLEN) { /* user intended one really big string (2 gig!) * but this cannot done so error exit */ tkc->cur = first; return ERR_NCX_LEN_EXCEEDED; } /* else fixup the consecutive strings * get a buffer to store the result */ buff = (xmlChar *)m__getMem(bufflen+1); if (!buff) { tkc->cur = first; return ERR_INTERNAL_MEM; } /* copy the strings */ str = buff; next = first; done = FALSE; while (!done) { str += xml_strcpy(str, next->val); if (next == last) { done = TRUE; } else { next = (tk_token_t *)dlq_nextEntry(next); } } if (TK_DOCMODE(tkc)) { /* make the Q of tk_origstr_t structs * and add it to the &first->origstrQ */ tk_origstr_t *origstr; boolean newline; prev = first; next = first; done = FALSE; while (!done) { origstr = NULL; if (next != first) { xmlChar *usestr; if (next->origval != NULL) { usestr = next->origval; } else { usestr = next->val; } newline = (prev->linenum != next->linenum); origstr = new_origstr(next->typ, newline, usestr); if (origstr == NULL) { tkc->cur = first; m__free(buff); return ERR_INTERNAL_MEM; } /* transferred 'usestr' memory OK * clear pointer to prevent double free */ if (next->origval != NULL) { next->origval = NULL; } else { next->val = NULL; } dlq_enque(origstr, &first->origstrQ); } if (next == last) { done = TRUE; } else { prev = next; next = (tk_token_t *)dlq_nextEntry(next); } } } /* fixup the first token */ first->len = bufflen; if (first->origval == NULL) { /* save the first SQSTRING since its value * is about to get changed to the entire concat string */ first->origval = first->val; } else { /* the first part of a QSTRING has already been * converted so it cannot be used as 'origval' * like an SQSTRING; just toss it as origval copy is * already set before the conversion was done */ m__free(first->val); } first->val = buff; /* remove the 2nd through the 'last' token */ done = FALSE; while (!done) { next = (tk_token_t *)dlq_nextEntry(first); dlq_remove(next); if (next == last) { done = TRUE; } free_token(next); } /* setup the next search for a 'first' string */ first = (tk_token_t *)dlq_nextEntry(first); } else { /* did not find a string concat, continue search */ first = next; } } return NO_ERR; } /* concat_qstrings */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION tk_new_chain * * Allocatate a new token parse chain * * RETURNS: * new parse chain or NULL if memory error *********************************************************************/ tk_chain_t * tk_new_chain (void) { tk_chain_t *tkc; tkc = m__getObj(tk_chain_t); if (!tkc) { return NULL; } memset(tkc, 0x0, sizeof(tk_chain_t)); dlq_createSQue(&tkc->tkQ); tkc->cur = (tk_token_t *)&tkc->tkQ; dlq_createSQue(&tkc->tkptrQ); return tkc; } /* tk_new_chain */ /******************************************************************** * FUNCTION tk_setup_chain_conf * * Setup a previously allocated chain for a text config file * * INPUTS * tkc == token chain to setup * fp == open file to use for text source * filename == source filespec *********************************************************************/ void tk_setup_chain_conf (tk_chain_t *tkc, FILE *fp, const xmlChar *filename) { #ifdef DEBUG if (!tkc) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif tkc->fp = fp; tkc->source = TK_SOURCE_CONF; tkc->filename = filename; tkc->flags |= TK_FL_MALLOC; } /* tk_setup_chain_conf */ /******************************************************************** * FUNCTION tk_setup_chain_yang * * Setup a previously allocated chain for a YANG file * * INPUTS * tkc == token chain to setup * fp == open file to use for text source * filename == source filespec *********************************************************************/ void tk_setup_chain_yang (tk_chain_t *tkc, FILE *fp, const xmlChar *filename) { #ifdef DEBUG if (!tkc) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif tkc->fp = fp; tkc->source = TK_SOURCE_YANG; tkc->filename = filename; tkc->flags |= TK_FL_MALLOC; } /* tk_setup_chain_yang */ /******************************************************************** * FUNCTION tk_setup_chain_yin * * Setup a previously allocated chain for a YIN file * * INPUTS * tkc == token chain to setup * filename == source filespec *********************************************************************/ void tk_setup_chain_yin (tk_chain_t *tkc, const xmlChar *filename) { #ifdef DEBUG if (!tkc) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif tkc->source = TK_SOURCE_YANG; tkc->filename = filename; tkc->flags |= TK_FL_MALLOC; } /* tk_setup_chain_yin */ /******************************************************************** * FUNCTION tk_setup_chain_docmode * * Setup a previously allocated chain for a yangdump doc output mode * * INPUTS * tkc == token chain to setup *********************************************************************/ void tk_setup_chain_docmode (tk_chain_t *tkc) { #ifdef DEBUG if (!tkc) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif tkc->flags |= TK_FL_DOCMODE; } /* tk_setup_chain_docmode */ /******************************************************************** * FUNCTION tk_free_chain * * Cleanup and deallocate a tk_chain_t * INPUTS: * tkc == TK chain to delete * RETURNS: * none *********************************************************************/ void tk_free_chain (tk_chain_t *tkc) { tk_token_t *tk; tk_token_ptr_t *tkptr; #ifdef DEBUG if (!tkc) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (!tkc) { SET_ERROR(ERR_INTERNAL_PTR); return; } while (!dlq_empty(&tkc->tkQ)) { tk = (tk_token_t *)dlq_deque(&tkc->tkQ); free_token(tk); } while (!dlq_empty(&tkc->tkptrQ)) { tkptr = (tk_token_ptr_t *)dlq_deque(&tkc->tkptrQ); free_token_ptr(tkptr); } if ((tkc->flags & TK_FL_MALLOC) && tkc->buff) { m__free(tkc->buff); } m__free(tkc); } /* tk_free_chain */ /******************************************************************** * FUNCTION tk_get_yang_btype_id * * Check if the specified string is a YANG builtin type name * checks for valid YANG data type name * * INPUTS: * buff == token string to check -- NOT ZERO-TERMINATED * len == length of string to check * RETURNS: * btype found or NCX_BT_NONE if no match *********************************************************************/ ncx_btype_t tk_get_yang_btype_id (const xmlChar *buff, uint32 len) { uint32 i; #ifdef DEBUG if (!buff) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_BT_NONE; } if (!len) { SET_ERROR(ERR_INTERNAL_VAL); return NCX_BT_NONE; } #endif /* hack first because of NCX_BT_ENUM */ if (len==11 && !xml_strncmp(buff, NCX_EL_ENUMERATION, 11)) { return NCX_BT_ENUM; } /* look in the blist for the specified type name */ for (i=1; blist[i].btyp != NCX_BT_NONE; i++) { if ((blist[i].blen == len) && !xml_strncmp(blist[i].bid, buff, len)) { if (blist[i].flags & FL_YANG) { return blist[i].btyp; } else { return NCX_BT_NONE; } } } return NCX_BT_NONE; } /* tk_get_yang_btype_id */ /******************************************************************** * FUNCTION tk_get_token_name * * Get the symbolic token name * * INPUTS: * ttyp == token type * RETURNS: * const string to the name; will not be NULL *********************************************************************/ const char * tk_get_token_name (tk_type_t ttyp) { if (ttyp <= TK_TT_RNUM) { return tlist[ttyp].tname; } else { return "--none--"; } } /* tk_get_token_name */ /******************************************************************** * FUNCTION tk_get_token_sym * * Get the symbolic token symbol * * INPUTS: * ttyp == token type * RETURNS: * const string to the symbol; will not be NULL *********************************************************************/ const char * tk_get_token_sym (tk_type_t ttyp) { if (ttyp <= TK_TT_GEQUAL) { return tlist[ttyp].tid; } else { return "--none--"; } } /* tk_get_token_sym */ /******************************************************************** * FUNCTION tk_get_btype_sym * * Get the symbolic token symbol for one of the base types * * INPUTS: * btyp == base type * RETURNS: * const string to the symbol; will not be NULL *********************************************************************/ const char * tk_get_btype_sym (ncx_btype_t btyp) { if (btyp <= NCX_LAST_DATATYPE) { return (const char *)blist[btyp].bid; } else if (btyp == NCX_BT_EXTERN) { return "extern"; } else if (btyp == NCX_BT_INTERN) { return "intern"; } else { return "none"; } } /* tk_get_btype_sym */ /******************************************************************** * FUNCTION tk_next_typ * * Get the token type of the next token * * INPUTS: * tkc == token chain * RETURNS: * token type *********************************************************************/ tk_type_t tk_next_typ (tk_chain_t *tkc) { tk_token_t *tk; #ifdef DEBUG if (!tkc) { SET_ERROR(ERR_INTERNAL_PTR); return TK_TT_NONE; } #endif if (!tkc->cur) { /* hit EOF in token chain already */ return TK_TT_NONE; } tk = (tk_token_t *)dlq_nextEntry(tkc->cur); return (tk) ? tk->typ : TK_TT_NONE; } /* tk_next_typ */ /******************************************************************** * FUNCTION tk_next_typ2 * * Get the token type of the token after the next token * * INPUTS: * tkc == token chain * RETURNS: * token type *********************************************************************/ tk_type_t tk_next_typ2 (tk_chain_t *tkc) { tk_token_t *tk; #ifdef DEBUG if (!tkc) { SET_ERROR(ERR_INTERNAL_PTR); return TK_TT_NONE; } #endif if (!tkc->cur) { return TK_TT_NONE; } tk = (tk_token_t *)dlq_nextEntry(tkc->cur); if (tk) { tk = (tk_token_t *)dlq_nextEntry(tk); return (tk) ? tk->typ : TK_TT_NONE; } else { return TK_TT_NONE; } } /* tk_next_typ2 */ /******************************************************************** * FUNCTION tk_next_val * * Get the token type of the next token * * INPUTS: * tkc == token chain * RETURNS: * token type *********************************************************************/ const xmlChar * tk_next_val (tk_chain_t *tkc) { tk_token_t *tk; #ifdef DEBUG if (!tkc) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif tk = (tk_token_t *)dlq_nextEntry(tkc->cur); return (tk) ? (const xmlChar *)tk->val : NULL; } /* tk_next_val */ /******************************************************************** * FUNCTION tk_dump_token * * Debug printf the specified token * !!! Very verbose !!! * * INPUTS: * tk == token * *********************************************************************/ void tk_dump_token (const tk_token_t *tk) { #ifdef DEBUG if (!tk) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (!LOGDEBUG2) { return; } log_debug2("\nline(%u.%u), typ(%s)", tk->linenum, tk->linepos, tk_get_token_name(tk->typ)); if (tk->val) { if (xml_strlen(tk->val) > 40) { log_debug2("\n"); } log_debug2(" val(%s)", (const char *)tk->val); } } /* tk_dump_token */ /******************************************************************** * FUNCTION tk_dump_chain * * Debug printf the token chain * !!! Very verbose !!! * * INPUTS: * tkc == token chain * * RETURNS: * none *********************************************************************/ void tk_dump_chain (const tk_chain_t *tkc) { const tk_token_t *tk; int i; #ifdef DEBUG if (!tkc) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (!LOGDEBUG3) { return; } i = 0; for (tk = (tk_token_t *)dlq_firstEntry(&tkc->tkQ); tk != NULL; tk = (tk_token_t *)dlq_nextEntry(tk)) { log_debug3("\n%s line(%u.%u), tk(%d), typ(%s)", (tk==tkc->cur) ? "*cur*" : "", tk->linenum, tk->linepos, ++i, tk_get_token_name(tk->typ)); if (tk->val) { if (xml_strlen(tk->val) > 40) { log_debug3("\n"); } log_debug3(" val(%s)", (const char *)tk->val); } } } /* tk_dump_chain */ /******************************************************************** * FUNCTION tk_is_wsp_string * * Check if the current token is a string with whitespace in it * * INPUTS: * tk == token to check * * RETURNS: * TRUE if a string with whitespace in it * FALSE if not a string or no whitespace in the string *********************************************************************/ boolean tk_is_wsp_string (const tk_token_t *tk) { const xmlChar *str; #ifdef DEBUG if (!tk) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (tk->typ) { case TK_TT_QSTRING: case TK_TT_SQSTRING: str = tk->val; while (*str && (*str != '\n') && !xml_isspace(*str)) { str++; } return (*str) ? TRUE : FALSE; default: return FALSE; } } /* tk_is_wsp_string */ /******************************************************************** * FUNCTION tk_tokenize_input * * Parse the input (FILE or buffer) into tk_token_t structs * * The tkc param must be initialized to use the internal * buffer to read from the specified filespec: * * tkc->filename * tkc->flags * tkc->fp * tkc->source * * External buffer mode: * * If no filename is provided, the the TK_FL_MALLOC * flag will not be set, and the tkc->buff field * must be initialized before this function is called. * This function will not free the buffer, * but just read from it until a '0' char is reached. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress (NULL if not used) * !!! Just used for error messages !!! * * RETURNS: * status of the operation *********************************************************************/ status_t tk_tokenize_input (tk_chain_t *tkc, ncx_module_t *mod) { status_t res; boolean done; tk_token_t *tk; tk_type_t ttyp; #ifdef DEBUG if (!tkc) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* check if a temp buffer is needed */ if (tkc->flags & TK_FL_MALLOC) { tkc->buff = m__getMem(TK_BUFF_SIZE); if (!tkc->buff) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } else { memset(tkc->buff, 0x0, TK_BUFF_SIZE); } } else if (tkc->buff == NULL) { /* tkc->buff expected to be setup already */ return SET_ERROR(ERR_INTERNAL_VAL); } /* setup buffer for parsing */ res = NO_ERR; tkc->bptr = tkc->buff; /* outside loop iterates per buffer full only * if reading input from file */ done = FALSE; while (!done) { /* get one line of input if parsing from FILE in buffer, * or already have the buffer if parsing from memory */ if (tkc->filename) { if (!fgets((char *)tkc->buff, TK_BUFF_SIZE, tkc->fp)) { /* read line failed, treating as not an error */ res = NO_ERR; done = TRUE; continue; } else { /* save newline token for conf file only */ if (tkc->source == TK_SOURCE_CONF) { tk = new_token(TK_TT_NEWLINE, NULL, 0); if (!tk) { res = ERR_INTERNAL_MEM; done = TRUE; continue; } tk->linenum = tkc->linenum; tk->linepos = tkc->linepos; dlq_enque(tk, &tkc->tkQ); } tkc->linenum++; tkc->linepos = 1; } /* set buffer pointer to start of buffer */ tkc->bptr = tkc->buff; #ifdef TK_RDLN_DEBUG if (LOGDEBUG3) { if (xml_strlen(tkc->buff) < 80) { log_debug3("\ntk_tokenize: read line (%s)", tkc->buff); } else { log_debug3("\ntk_tokenize: read line len (%d)", xml_strlen(tkc->buff)); } } #endif ncx_check_warn_linelen(tkc, mod, tkc->buff); } /* Have some sort of input in the buffer (tkc->buff) */ while (*tkc->bptr && res==NO_ERR) { /* skip whitespace */ while (*tkc->bptr && (*tkc->bptr != '\n') && xml_isspace(*tkc->bptr)) { if (*tkc->bptr == '\t') { tkc->linepos += NCX_TABSIZE; } else { tkc->linepos++; } tkc->bptr++; } /* check the first non-whitespace char found or exit */ if (!*tkc->bptr) { continue; /* EOS, exit loop */ } else if (*tkc->bptr == '\n') { /* save newline token for conf file only */ if (tkc->source == TK_SOURCE_CONF) { tk = new_token(TK_TT_NEWLINE, NULL, 0); if (!tk) { res = ERR_INTERNAL_MEM; done = TRUE; continue; } tk->linenum = tkc->linenum; tk->linepos = ++tkc->linepos; dlq_enque(tk, &tkc->tkQ); } tkc->bptr++; } else if ((tkc->source == TK_SOURCE_CONF && *tkc->bptr == NCX_COMMENT_CH) || (tkc->source == TK_SOURCE_YANG && *tkc->bptr == '/' && tkc->bptr[1] == '/')) { /* CONF files use the '# to eoln' comment format * YANG files use the '// to eoln' comment format * skip past the comment, make next char EOLN * * TBD: SAVE COMMENTS IN XMLDOC SESSION MODE */ while (*tkc->bptr && *tkc->bptr != '\n') { tkc->bptr++; } } else if (tkc->source == TK_SOURCE_YANG && *tkc->bptr == '/' && tkc->bptr[1] == '*') { /* found start of a C-style YANG comment */ res = skip_yang_cstring(tkc); } else if (*tkc->bptr == NCX_QSTRING_CH) { /* get a dbl-quoted string which may span multiple lines */ res = tokenize_qstring(tkc); } else if (*tkc->bptr == NCX_SQSTRING_CH) { /* get a single-quoted string which may span multiple lines */ res = tokenize_sqstring(tkc); } else if (tkc->source == TK_SOURCE_XPATH && *tkc->bptr == NCX_VARBIND_CH) { res = tokenize_varbind_string(tkc); } else if (ncx_valid_fname_ch(*tkc->bptr)) { /* get some some of unquoted ID string or regular string */ res = tokenize_id_string(tkc); } else if ((*tkc->bptr=='+' || *tkc->bptr=='-') && isdigit(*(tkc->bptr+1)) && (tkc->source != TK_SOURCE_YANG) && (tkc->source != TK_SOURCE_XPATH)) { /* get some sort of number * YANG does not have +/- number sequences * so they are parsed (first pass) as a string * There are corner cases such as range 1..max * that will be parsed wrong (2nd dot). These * strings use the tk_retokenize_cur_string fn * to break up the string into more tokens */ res = tokenize_number(tkc); } else if (isdigit(*tkc->bptr) && (tkc->source != TK_SOURCE_YANG)) { res = tokenize_number(tkc); } else { /* check for a 2 char token before 1 char token */ ttyp = get_token_id(tkc->bptr, 2, tkc->source); if (ttyp != TK_TT_NONE) { res = add_new_token(tkc, ttyp, tkc->bptr+2, tkc->linepos); tkc->bptr += 2; tkc->linepos += 2; } else { /* not a 2-char, check for a 1-char token */ ttyp = get_token_id(tkc->bptr, 1, tkc->source); if (ttyp != TK_TT_NONE) { /* got a 1 char token */ res = add_new_token(tkc, ttyp, tkc->bptr+1, tkc->linepos); tkc->bptr++; tkc->linepos++; } else { /* ran out of token type choices * call it a string */ res = tokenize_string(tkc); } } } } /* end while non-zero chars left in buff and NO_ERR */ /* finish outer loop, once through for buffer mode */ if (!(tkc->flags & TK_FL_MALLOC)) { done = TRUE; } } if (res == NO_ERR && tkc->source != TK_SOURCE_XPATH) { res = concat_qstrings(tkc); } if (res == NO_ERR) { /* setup the token queue current pointer */ tkc->cur = (tk_token_t *)&tkc->tkQ; } else { ncx_print_errormsg(tkc, mod, res); } return res; } /* tk_tokenize_input */ /******************************************************************** * FUNCTION tk_retokenize_cur_string * * The current token is some sort of a string * Reparse it according to the full NCX token list, as needed * * The current token may be replaced with one or more tokens * * INPUTS: * tkc == token chain * mod == module in progress (NULL if not used) * * RETURNS: * status of the operation *********************************************************************/ status_t tk_retokenize_cur_string (tk_chain_t *tkc, ncx_module_t *mod) { tk_chain_t *tkctest; tk_token_t *p; status_t res; #ifdef DEBUG if (!tkc || !tkc->cur) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (!TK_CUR_STR(tkc)) { return NO_ERR; /* not a string, leave it alone */ } /* create a test chain and parse the string */ tkctest = tk_new_chain(); if (!tkctest) { return ERR_INTERNAL_MEM; } tkctest->source = TK_SOURCE_REDO; tkctest->bptr = tkctest->buff = TK_CUR_VAL(tkc); res = tk_tokenize_input(tkctest, mod); /* check if the token parse was different * if more than 1 token, then it was changed * from a single string to something else */ if (res == NO_ERR) { /* redo token line info for these expanded tokens */ for (p = (tk_token_t *)dlq_firstEntry(&tkctest->tkQ); p != NULL; p = (tk_token_t *)dlq_nextEntry(p)) { p->linenum = tkc->cur->linenum; p->linepos = tkc->cur->linepos; } dlq_block_insertAfter(&tkctest->tkQ, tkc->cur); /* get rid of the original string and reset the cur token */ p = (tk_token_t *)dlq_nextEntry(tkc->cur); dlq_remove(tkc->cur); free_token(tkc->cur); tkc->cur = p; } tk_free_chain(tkctest); return res; } /* tk_retokenize_cur_string */ /******************************************************************** * FUNCTION tk_tokenize_metadata_string * * The specified ncx:metadata string is parsed into tokens * convert the ncx:metadata content to 1 or 2 tokens * * INPUTS: * mod == module in progress for error purposes (may be NULL) * str == string to tokenize * res == address of return status * * OUTPUTS: * *res == error status, if return NULL or non-NULL * * RETURNS: * pointer to malloced and filled in token chain * ready to be traversed; always check *res for valid syntax *********************************************************************/ tk_chain_t * tk_tokenize_metadata_string ( ncx_module_t *mod, xmlChar *str, status_t *res ) { tk_chain_t *tkc; assert( str && " str is NULL" ); assert( res && " res is NULL" ); /* create a new chain and parse the string */ tkc = tk_new_chain(); if (!tkc) { *res = ERR_INTERNAL_MEM; return NULL; } tkc->source = TK_SOURCE_YANG; tkc->bptr = tkc->buff = str; *res = tk_tokenize_input(tkc, mod); return tkc; } /* tk_tokenize_metadata_string */ /******************************************************************** * FUNCTION tk_tokenize_xpath_string * * The specified XPath string is parsed into tokens * * INPUTS: * mod == module in progress for error purposes (may be NULL) * str == string to tokenize * curlinenum == current line number * curlinepos == current line position * res == address of return status * * OUTPUTS: * *res == error status, if return NULL or non-NULL * * RETURNS: * pointer to malloced and filled in token chain * ready to be traversed; always check *res for valid syntax *********************************************************************/ tk_chain_t * tk_tokenize_xpath_string (ncx_module_t *mod, xmlChar *str, uint32 curlinenum, uint32 curlinepos, status_t *res) { tk_chain_t *tkc; #ifdef DEBUG if (!str || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif /* create a new chain and parse the string */ tkc = tk_new_chain(); if (!tkc) { *res = ERR_INTERNAL_MEM; return NULL; } tkc->source = TK_SOURCE_XPATH; tkc->bptr = tkc->buff = str; tkc->linenum = curlinenum; tkc->linepos = curlinepos; *res = tk_tokenize_input(tkc, mod); return tkc; } /* tk_tokenize_xpath_string */ /******************************************************************** * FUNCTION tk_token_count * * Get the number of tokens in the queue * * INPUTS: * tkc == token chain to check * * RETURNS: * number of tokens in the queue *********************************************************************/ uint32 tk_token_count (const tk_chain_t *tkc) { const tk_token_t *tk; uint32 cnt; #ifdef DEBUG if (!tkc) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif cnt = 0; for (tk = (const tk_token_t *)dlq_firstEntry(&tkc->tkQ); tk != NULL; tk = (const tk_token_t *)dlq_nextEntry(tk)) { cnt++; } return cnt; } /* tk_token_count */ /******************************************************************** * FUNCTION tk_reset_chain * * Reset the token chain current pointer to the start * * INPUTS: * tkc == token chain to reset * *********************************************************************/ void tk_reset_chain (tk_chain_t *tkc) { #ifdef DEBUG if (!tkc) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif tkc->cur = (tk_token_t *)&tkc->tkQ; } /* tk_reset_chain */ /******************************************************************** * FUNCTION tk_clone_chain * * Allocatate and a new token parse chain and fill * it with the specified token chain contents * * INPUTS: * oldtkc == token chain to clone * * RETURNS: * new cloned parse chain or NULL if memory error *********************************************************************/ tk_chain_t * tk_clone_chain (tk_chain_t *oldtkc) { tk_chain_t *tkc; tk_token_t *token, *oldtoken; status_t res; #ifdef DEBUG if (oldtkc == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif tkc = tk_new_chain(); if (!tkc) { return NULL; } tkc->filename = oldtkc->filename; tkc->linenum = oldtkc->linenum; tkc->flags = oldtkc->flags; tkc->source = oldtkc->source; res = NO_ERR; for (oldtoken = (tk_token_t *)dlq_firstEntry(&oldtkc->tkQ); oldtoken != NULL && res == NO_ERR; oldtoken = (tk_token_t *)dlq_nextEntry(oldtoken)) { token = new_token(oldtoken->typ, oldtoken->val, oldtoken->len); if (!token) { tk_free_chain(tkc); return NULL; } if (oldtoken->mod) { token->mod = xml_strndup(oldtoken->mod, oldtoken->modlen); if (!token->mod) { free_token(token); tk_free_chain(tkc); return NULL; } token->modlen = oldtoken->modlen; } token->linenum = oldtoken->linenum; token->linepos = oldtoken->linepos; token->nsid = oldtoken->nsid; dlq_enque(token, &tkc->tkQ); } return tkc; } /* tk_clone_chain */ /******************************************************************** * FUNCTION tk_add_id_token * * Allocatate a new ID token and add it to the parse chain * * INPUTS: * tkc == token chain to use * valstr == ID name * * RETURNS: * status *********************************************************************/ status_t tk_add_id_token (tk_chain_t *tkc, const xmlChar *valstr) { tk_token_t *tk; if ( !tkc ) { return SET_ERROR(ERR_INTERNAL_PTR); } /* hack for YIN input, no XML line numbers */ tkc->linenum++; if ( !valstr ) { tk = new_token(TK_TT_TSTRING, NULL, 0); } else { /* normal case string -- non-zero length */ tk = new_token(TK_TT_TSTRING, valstr, xml_strlen(valstr)); } if (!tk) { return ERR_INTERNAL_MEM; } tk->linenum = tkc->linenum; tk->linepos = 1; dlq_enque(tk, &tkc->tkQ); return NO_ERR; } /* tk_add_id_token */ /******************************************************************** * FUNCTION tk_add_pid_token * * Allocatate a new prefixed ID token and add it to * the parse chain * * INPUTS: * tkc == token chain to use * prefix == ID prefix * prefixlen == 'prefix' length in bytes * valstr == ID name * * RETURNS: * status *********************************************************************/ status_t tk_add_pid_token (tk_chain_t *tkc, const xmlChar *prefix, uint32 prefixlen, const xmlChar *valstr) { tk_token_t *tk; #ifdef DEBUG if (tkc == NULL || prefix == NULL || valstr == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* hack for YIN input, no XML line numbers */ tkc->linenum++; tk = new_token_wmod(TK_TT_MSTRING, prefix, prefixlen, valstr, xml_strlen(valstr)); if (tk == NULL) { return ERR_INTERNAL_MEM; } tk->linenum = tkc->linenum; tk->linepos = 1; dlq_enque(tk, &tkc->tkQ); return NO_ERR; } /* tk_add_pid_token */ /******************************************************************** * FUNCTION tk_add_string_token * * Allocatate a new string token and add it to the parse chain * * INPUTS: * tkc == token chain to use * valstr == string value to use * * RETURNS: * status *********************************************************************/ status_t tk_add_string_token (tk_chain_t *tkc, const xmlChar *valstr) { tk_token_t *tk; tk_type_t tktyp; if ( !tkc ) { return SET_ERROR(ERR_INTERNAL_PTR); } /* hack for YIN input, no XML line numbers */ tkc->linenum++; if ( !valstr ) { tk = new_token(TK_TT_QSTRING, NULL, 0); } else { uint32 tklen = xml_strlen(valstr); if (val_need_quotes(valstr)) { tktyp = TK_TT_QSTRING; } else if (ncx_valid_name(valstr, tklen)) { tktyp = TK_TT_TSTRING; } else { tktyp = TK_TT_STRING; } /* normal case string -- non-zero length */ tk = new_token(tktyp, valstr, tklen); } if (!tk) { return ERR_INTERNAL_MEM; } tk->linenum = tkc->linenum; tk->linepos = 1; dlq_enque(tk, &tkc->tkQ); return NO_ERR; } /* tk_add_string_token */ /******************************************************************** * FUNCTION tk_add_lbrace_token * * Allocatate a new left brace token and add it to the parse chain * * INPUTS: * tkc == token chain to use * * RETURNS: * status *********************************************************************/ status_t tk_add_lbrace_token (tk_chain_t *tkc) { tk_token_t *tk; #ifdef DEBUG if (tkc == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* hack for YIN input, no XML line numbers */ tkc->linenum++; tk = new_token(TK_TT_LBRACE, (const xmlChar *)"{", 1); if (!tk) { return ERR_INTERNAL_MEM; } tk->linenum = tkc->linenum; tk->linepos = 1; dlq_enque(tk, &tkc->tkQ); return NO_ERR; } /* tk_add_lbrace_token */ /******************************************************************** * FUNCTION tk_add_rbrace_token * * Allocatate a new right brace token and add it to the parse chain * * INPUTS: * tkc == token chain to use * * RETURNS: * status *********************************************************************/ status_t tk_add_rbrace_token (tk_chain_t *tkc) { tk_token_t *tk; #ifdef DEBUG if (tkc == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* hack for YIN input, no XML line numbers */ tkc->linenum++; tk = new_token(TK_TT_RBRACE, (const xmlChar *)"}", 1); if (!tk) { return ERR_INTERNAL_MEM; } tk->linenum = tkc->linenum; tk->linepos = 1; dlq_enque(tk, &tkc->tkQ); return NO_ERR; } /* tk_add_rbrace_token */ /******************************************************************** * FUNCTION tk_add_semicol_token * * Allocatate a new semi-colon token and add it to the parse chain * * INPUTS: * tkc == token chain to use * * RETURNS: * status *********************************************************************/ status_t tk_add_semicol_token (tk_chain_t *tkc) { tk_token_t *tk; #ifdef DEBUG if (tkc == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* hack for YIN input, no XML line numbers */ tkc->linenum++; tk = new_token(TK_TT_SEMICOL, (const xmlChar *)";", 1); if (!tk) { return ERR_INTERNAL_MEM; } tk->linenum = tkc->linenum; tk->linepos = 1; dlq_enque(tk, &tkc->tkQ); return NO_ERR; } /* tk_add_semicol_token */ /******************************************************************** * FUNCTION tk_check_save_origstr * * Check the docmode and the specified token; * Save a tk_origptr_t if needed with the field address * * INPUTS: * tkc = token chain to use * tk = token to use (usually TK_CUR()) * field == address of string field to save as the key * * RETURNS: * status; ERR_INTERNAL_MEM if entry cannot be malloced *********************************************************************/ status_t tk_check_save_origstr (tk_chain_t *tkc, tk_token_t *tk, const void *field) { tk_token_ptr_t *tkptr; #ifdef DEBUG if (tkc == NULL || tk == NULL || field == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (!TK_DOCMODE(tkc)) { return NO_ERR; } if (!TK_TYP_STR(tk->typ)) { return NO_ERR; } /* save all string tokens, not just the ones that * have multi-part format, needed for original quotes */ tkptr = new_token_ptr(tk, field); if (tkptr == NULL) { return ERR_INTERNAL_MEM; } dlq_enque(tkptr, &tkc->tkptrQ); return NO_ERR; } /* tk_check_save_origstr */ /******************************************************************** * FUNCTION tk_get_first_origstr * * Get the first original string to use * * INPUTS: * tkptr = token pointer to use * dquote = address of return double quote flag * morestr == addres of return more string fragments flag * OUTPUTS: * *dquote = return double quote flag * TRUE == TK_TT_QSTRING * FALSE == TK_TTSQSTRING * *morestr == addres of return more string fragments flag * TRUE == more string fragments after first one * RETURNS: * pointer to the first string fragment *********************************************************************/ const xmlChar * tk_get_first_origstr (const tk_token_ptr_t *tkptr, boolean *dquote, boolean *morestr) { const tk_token_t *tk; #ifdef DEBUG if (tkptr == NULL || tkptr->tk == NULL || dquote == NULL || morestr == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif tk = tkptr->tk; *morestr = !dlq_empty(&tk->origstrQ); *dquote = (tk->typ == TK_TT_QSTRING) ? TRUE : FALSE; if (tk->origval) { return tk->origval; } return tk->val; } /******************************************************************** * FUNCTION tk_first_origstr_rec * * Get the first tk_origstr_t struct (if any) * * INPUTS: * tkptr = token pointer to use * * RETURNS: * pointer to the first original string record *********************************************************************/ const tk_origstr_t * tk_first_origstr_rec (const tk_token_ptr_t *tkptr) { const tk_token_t *tk; #ifdef DEBUG if (tkptr == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif tk = tkptr->tk; return (const tk_origstr_t *)dlq_firstEntry(&tk->origstrQ); } /* tk_first_origstr_rec */ /******************************************************************** * FUNCTION tk_next_origstr_rec * * Get the next tk_origstr_t struct (if any) * * INPUTS: * origstr = origisnal string record pointer to use * * RETURNS: * pointer to the next original string record *********************************************************************/ const tk_origstr_t * tk_next_origstr_rec (const tk_origstr_t *origstr) { #ifdef DEBUG if (origstr == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (const tk_origstr_t *)dlq_nextEntry(origstr); } /* tk_next_origstr_rec */ /******************************************************************** * FUNCTION tk_get_origstr_parts * * Get the fields from the original string record * * INPUTS: * origstr = original string record pointer to use * dquote = address of return double quote flag * newline == addres of return need newline flag * OUTPUTS: * *dquote = return double quote flag * TRUE == TK_TT_QSTRING * FALSE == TK_TTSQSTRING * *newline == return need newline flag * TRUE == need newline + indent before this string * RETURNS: * pointer to the string fragment *********************************************************************/ const xmlChar * tk_get_origstr_parts (const tk_origstr_t *origstr, boolean *dquote, boolean *newline) { #ifdef DEBUG if (origstr == NULL || dquote == NULL || newline == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif *dquote = (origstr->origtyp == TK_ORIGSTR_DQUOTE || origstr->origtyp == TK_ORIGSTR_DQUOTE_NL); *newline = (origstr->origtyp == TK_ORIGSTR_DQUOTE_NL || origstr->origtyp == TK_ORIGSTR_SQUOTE_NL); return origstr->str; } /* tk_get_origstr_parts */ /******************************************************************** * FUNCTION tk_find_tkptr * * Find the specified token pointer record * * INPUTS: * tkc = token chain to use * field == address of field to use as key to find * * RETURNS: * pointer to the token pointer record or NULL if not found *********************************************************************/ const tk_token_ptr_t * tk_find_tkptr (const tk_chain_t *tkc, const void *field) { const tk_token_ptr_t *tkptr; #ifdef DEBUG if (tkc == NULL || field == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (tkptr = (const tk_token_ptr_t *) dlq_firstEntry(&tkc->tkptrQ); tkptr != NULL; tkptr = (const tk_token_ptr_t *) dlq_nextEntry(tkptr)) { if (field == tkptr->field) { return tkptr; } } return NULL; } /* tk_find_tkptr */ /******************************************************************** * FUNCTION tk_tkptr_quotes * * Get the specified token pointer record token ID type * Use the first string or only string * * INPUTS: * tkptr = token pointer to use * * RETURNS: * number of quotes used in first string *********************************************************************/ uint32 tk_tkptr_quotes (const tk_token_ptr_t *tkptr) { #ifdef DEBUG if (tkptr == NULL || tkptr->tk == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif switch (tkptr->tk->typ) { case TK_TT_SQSTRING: return 1; case TK_TT_QSTRING: return 2; default: return 0; } } /* tk_tkptr_quotes */ /******************************************************************** * FUNCTION set_tkc_error * * utility function for setting and reporting tkc errors. * * INPUTS: * tkc the parser token chain (may be NULL) * mod the module * res the error status. * * RETURNS: * the error status ***************************************************************/ status_t set_tkc_error( tk_chain_t *tkc, ncx_module_t *mod, ncx_error_t *err, status_t res ) { if ( tkc ) { tkc->curerr = err; } ncx_print_errormsg( tkc, mod, res ); return res; } /* END file tk.c */ yuma123_2.14/netconf/src/ncx/help.h0000664000175000017500000001164014770023131017241 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_help #define _H_help /* FILE: help.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Print help text for various templates ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 05-oct-07 abb Begun */ #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_obj #include "obj.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define HELP_MODE_BRIEF_MAX 60 #define HELP_MODE_NORMAL_MAX 100 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ typedef enum help_mode_t_ { HELP_MODE_NONE, HELP_MODE_BRIEF, HELP_MODE_NORMAL, HELP_MODE_FULL } help_mode_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION help_program_module * * Print the full help text for an entire program module to STDOUT * * INPUTS: * modname == module name without file suffix * cliname == name of CLI parmset within the modname module * *********************************************************************/ extern void help_program_module (const xmlChar *modname, const xmlChar *cliname, help_mode_t mode); /******************************************************************** * FUNCTION help_data_module * * Print the full help text for an entire data module to STDOUT * * INPUTS: * mod == data module struct * mode == help mode requested *********************************************************************/ extern void help_data_module (const ncx_module_t *mod, help_mode_t mode); /******************************************************************** * FUNCTION help_type * * Print the full help text for a data type to STDOUT * * INPUTS: * typ == type template struct * mode == help mode requested *********************************************************************/ extern void help_type (const typ_template_t *typ, help_mode_t mode); /******************************************************************** * FUNCTION help_object * * Print the full help text for a RPC method template to STDOUT * * INPUTS: * obj == object template * mode == help mode requested *********************************************************************/ extern void help_object (obj_template_t *obj, help_mode_t mode); /******************************************************************** * FUNCTION help_write_lines * * write some indented output to STDOUT * * INPUTS: * str == string to print; 'indent' number of spaces * will be added to each new line * indent == indent count * startnl == TRUE if start with a newline, FALSE otherwise *********************************************************************/ extern void help_write_lines (const xmlChar *str, uint32 indent, boolean startnl); /******************************************************************** * FUNCTION help_write_lines_max * * write some indented output to STDOUT * * INPUTS: * str == string to print; 'indent' number of spaces * will be added to each new line * indent == indent count * startnl == TRUE if start with a newline, FALSE otherwise * maxlen == 0..N max number of chars to output *********************************************************************/ extern void help_write_lines_max (const xmlChar *str, uint32 indent, boolean startnl, uint32 maxlen); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_help */ yuma123_2.14/netconf/src/ncx/rpc_err.c0000664000175000017500000003722714770023131017751 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: rpc_err.c NETCONF Protocol Operations: Common RPC Error Support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 05may05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncx_num #include "ncx_num.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_rpc_err #include "rpc_err.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define RPC_ERR_NUM_ERRORS ((uint32)RPC_ERR_LAST_ERROR) /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* map internal enum to string value for each error code */ typedef struct rpc_err_map_t_ { rpc_err_t errid; const xmlChar *errtag; } rpc_err_map_t; /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /* error-tag names */ static rpc_err_map_t rpc_err_map[] = { { RPC_ERR_NONE, (const xmlChar *)"none" }, { RPC_ERR_IN_USE, (const xmlChar *)"in-use" }, { RPC_ERR_INVALID_VALUE, (const xmlChar *)"invalid-value" }, { RPC_ERR_TOO_BIG, (const xmlChar *)"too-big" }, { RPC_ERR_MISSING_ATTRIBUTE, (const xmlChar *)"missing-attribute" }, { RPC_ERR_BAD_ATTRIBUTE, (const xmlChar *)"bad-attribute" }, { RPC_ERR_UNKNOWN_ATTRIBUTE, (const xmlChar *)"unknown-attribute" }, { RPC_ERR_MISSING_ELEMENT, (const xmlChar *)"missing-element" }, { RPC_ERR_BAD_ELEMENT, (const xmlChar *)"bad-element" }, { RPC_ERR_UNKNOWN_ELEMENT, (const xmlChar *)"unknown-element" }, { RPC_ERR_UNKNOWN_NAMESPACE, (const xmlChar *)"unknown-namespace" }, { RPC_ERR_ACCESS_DENIED, (const xmlChar *)"access-denied" }, { RPC_ERR_LOCK_DENIED, (const xmlChar *)"lock-denied" }, { RPC_ERR_RESOURCE_DENIED, (const xmlChar *)"resource-denied" }, { RPC_ERR_ROLLBACK_FAILED, (const xmlChar *)"rollback-failed" }, { RPC_ERR_DATA_EXISTS, (const xmlChar *)"data-exists" }, { RPC_ERR_DATA_MISSING, (const xmlChar *)"data-missing" }, { RPC_ERR_OPERATION_NOT_SUPPORTED, (const xmlChar *)"operation-not-supported" }, { RPC_ERR_OPERATION_FAILED, (const xmlChar *)"operation-failed" }, { RPC_ERR_PARTIAL_OPERATION, (const xmlChar *)"partial-operation" }, { RPC_ERR_MALFORMED_MESSAGE, (const xmlChar *)"malformed-message" }, { RPC_ERR_NONE, NULL } }; static rpc_err_rec_t staterr; static boolean staterr_inuse = FALSE; /******************************************************************** * FUNCTION dump_error_info * * Dump the error-info struct * * INPUTS: * errinfo == rpc_err_info_t struct to dump * *********************************************************************/ static void dump_error_info (const rpc_err_info_t *errinfo) { const xmlChar *prefix; log_write("\n error-info: %s T:%s = ", (errinfo->name) ? (const char *)errinfo->name : "--", tk_get_btype_sym(errinfo->val_btype)); if (errinfo->isqname) { prefix = xmlns_get_ns_prefix(errinfo->val_nsid); log_write("%s:%s", (prefix) ? prefix : (const xmlChar *)"--", (errinfo->v.strval) ? (const char *)errinfo->v.strval : "--"); return; } switch (errinfo->val_btype) { case NCX_BT_BINARY: case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: log_write("%s", (errinfo->v.strval) ? (const char *)errinfo->v.strval : "--"); break; case NCX_BT_BOOLEAN: SET_ERROR(ERR_NCX_OPERATION_NOT_SUPPORTED); /***/ break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: ncx_printf_num(&errinfo->v.numval, errinfo->val_btype); break; default: val_dump_value((val_value_t *)errinfo->v.cpxval, NCX_DEF_INDENT*2); } } /* dump_error_info */ /******************************************************************** * FUNCTION dump_error * * Dump the error message * * INPUTS: * err == rpc_err_rec_t struct to dump * *********************************************************************/ static void dump_error (const rpc_err_rec_t *err) { const rpc_err_info_t *errinfo; log_write("\nrpc-error: (%u) %s L:%s S:%s ", err->error_res, (err->error_tag) ? (const char *)err->error_tag : "--", ncx_get_layer(err->error_type), rpc_err_get_severity(err->error_severity)); if (err->error_app_tag) { log_write("app-tag:%s ", err->error_app_tag); } if (err->error_path) { log_write("\n path:%s ", err->error_path); } if (err->error_message_lang) { log_write("lang:%s ", err->error_message_lang); } if (err->error_message) { log_write("\n msg:%s ", err->error_message); } for (errinfo = (const rpc_err_info_t *)dlq_firstEntry(&err->error_info); errinfo != NULL; errinfo = (const rpc_err_info_t *)dlq_nextEntry(errinfo)) { dump_error_info(errinfo); } log_write("\n"); } /* dump_error */ /************** E X T E R N A L F U N C T I O N S **********/ /******************************************************************** * FUNCTION rpc_err_get_errtag * * Get the RPC error-tag for an rpc_err_t enumeration * * INPUTS: * errid == rpc error enum to convert to a string * * RETURNS: * string for the specified error-tag enum *********************************************************************/ const xmlChar * rpc_err_get_errtag (rpc_err_t errid) { if (errid > RPC_ERR_LAST_ERROR) { SET_ERROR(ERR_INTERNAL_VAL); errid = RPC_ERR_NONE; } return rpc_err_map[errid].errtag; } /* rpc_err_get_errtag */ /******************************************************************** * FUNCTION rpc_err_get_errtag_enum * * Get the RPC error-tag enum for an error-tag string * * INPUTS: * errtag == error-tag string to check * * RETURNS: * enum for this error-tag *********************************************************************/ rpc_err_t rpc_err_get_errtag_enum (const xmlChar *errtag) { rpc_err_t errcode; #ifdef DEBUG if (!errtag) { SET_ERROR(ERR_INTERNAL_PTR); return RPC_ERR_NONE; } #endif for (errcode = RPC_ERR_IN_USE; errcode <= RPC_ERR_LAST_ERROR; errcode++) { if (!xml_strcmp(errtag, rpc_err_map[errcode].errtag)) { return rpc_err_map[errcode].errid; } } return RPC_ERR_NONE; } /* rpc_err_get_errtag_enum */ /******************************************************************** * FUNCTION rpc_err_new_record * * Malloc and init an rpc_err_rec_t struct * * RETURNS: * malloced error record or NULL if memory error *********************************************************************/ rpc_err_rec_t * rpc_err_new_record (void) { rpc_err_rec_t *err; err = m__getObj(rpc_err_rec_t); if (!err) { if (!staterr_inuse) { err = &staterr; staterr_inuse = TRUE; } else { return NULL; } } rpc_err_init_record(err); return err; } /* rpc_err_new_record */ /******************************************************************** * FUNCTION rpc_err_init_record * * Init an rpc_err_rec_t struct * * INPUTS: * err == rpc_err_rec_t struct to init * RETURNS: * none *********************************************************************/ void rpc_err_init_record (rpc_err_rec_t *err) { #ifdef DEBUG if (!err) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif memset(err, 0x0, sizeof(rpc_err_rec_t)); dlq_createSQue(&err->error_info); } /* rpc_err_init_record */ /******************************************************************** * FUNCTION rpc_err_free_record * * Clean and free an rpc_err_rec_t struct * * INPUTS: * err == rpc_err_rec_t struct to clean and free * RETURNS: * none *********************************************************************/ void rpc_err_free_record (rpc_err_rec_t *err) { #ifdef DEBUG if (!err) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif rpc_err_clean_record(err); if (err == &staterr) { staterr_inuse = FALSE; } else { m__free(err); } } /* rpc_err_free_record */ /******************************************************************** * FUNCTION rpc_err_clean_record * * Clean an rpc_err_rec_t struct * * INPUTS: * err == rpc_err_rec_t struct to clean * RETURNS: * none *********************************************************************/ void rpc_err_clean_record (rpc_err_rec_t *err) { rpc_err_info_t *errinfo; #ifdef DEBUG if (!err) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif err->error_res = NO_ERR; err->error_id = RPC_ERR_NONE; err->error_type = NCX_LAYER_NONE; err->error_severity = RPC_ERR_SEV_NONE; err->error_tag = NULL; err->error_app_tag = NULL; if (err->error_path) { m__free(err->error_path); err->error_path = NULL; } if (err->error_message) { m__free(err->error_message); } err->error_message_lang = NULL; while (!dlq_empty(&err->error_info)) { errinfo = (rpc_err_info_t *)dlq_deque(&err->error_info); rpc_err_free_info(errinfo); } } /* rpc_err_clean_record */ /******************************************************************** * FUNCTION rpc_err_new_info * * Malloc and init an rpc_err_info_t struct * * RETURNS: * malloced error-info record, or NULL if memory error *********************************************************************/ rpc_err_info_t * rpc_err_new_info (void) { rpc_err_info_t *errinfo; errinfo = m__getObj(rpc_err_info_t); if (!errinfo) { return NULL; } memset(errinfo, 0x0, sizeof(rpc_err_info_t)); return errinfo; } /* rpc_err_new_info */ /******************************************************************** * FUNCTION rpc_err_free_info * * Clean and free an rpc_err_info_t struct * * INPUTS: * errinfo == rpc_err_info_t struct to clean and free * RETURNS: * none *********************************************************************/ void rpc_err_free_info (rpc_err_info_t *errinfo) { #ifdef DEBUG if (!errinfo) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (errinfo->badns) { m__free(errinfo->badns); } if (errinfo->dname) { m__free(errinfo->dname); } if (errinfo->dval) { m__free(errinfo->dval); } switch (errinfo->val_btype) { case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_LIST: if (errinfo->v.cpxval) { val_free_value((val_value_t *)errinfo->v.cpxval); } break; default: ; } m__free(errinfo); } /* rpc_err_free_info */ /******************************************************************** * FUNCTION rpc_err_dump_errors * * Dump the error messages in the RPC message error Q * * INPUTS: * msg == rpc_msg_t struct to check for errors * *********************************************************************/ void rpc_err_dump_errors (const rpc_msg_t *msg) { const rpc_err_rec_t *err; #ifdef DEBUG if (!msg) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif for (err = (const rpc_err_rec_t *)dlq_firstEntry(&msg->mhdr.errQ); err != NULL; err = (const rpc_err_rec_t *)dlq_nextEntry(err)) { dump_error(err); } } /* rpc_err_dump_errors */ /******************************************************************** * FUNCTION rpc_err_get_severity * * Translate an rpc_err_sev_t to a string * * INPUTS: * sev == rpc_err_sev_t enum to translate * * RETURNS: * const pointer to the enum string *********************************************************************/ const xmlChar * rpc_err_get_severity (rpc_err_sev_t sev) { switch (sev) { case RPC_ERR_SEV_WARNING: return NCX_EL_WARNING; case RPC_ERR_SEV_ERROR: return NCX_EL_ERROR; default: SET_ERROR(ERR_INTERNAL_VAL); return EMPTY_STRING; } } /* rpc_err_get_severity */ /******************************************************************** * FUNCTION rpc_err_clean_errQ * * Clean all the entries from a Q of rpc_err_rec_t * * INPUTS: * errQ == Q of rpc_err_rec_t to clean * RETURNS: * none *********************************************************************/ void rpc_err_clean_errQ (dlq_hdr_t *errQ) { rpc_err_rec_t *err; #ifdef DEBUG if (!errQ) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* clean error queue */ while (!dlq_empty(errQ)) { err = (rpc_err_rec_t *)dlq_deque(errQ); rpc_err_free_record(err); } } /* rpc_err_clean_errQ */ /******************************************************************** * FUNCTION rpc_err_any_errors * * Check if there are any errors in the RPC message error Q * * INPUTS: * msg == rpc_msg_t struct to check for errors * * RETURNS: * TRUE if any errors recorded; FALSE if none *********************************************************************/ boolean rpc_err_any_errors (const rpc_msg_t *msg) { #ifdef DEBUG if (!msg) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return (dlq_empty(&msg->mhdr.errQ)) ? FALSE : TRUE; } /* rpc_err_any_errors */ /* END file rpc_err.c */ yuma123_2.14/netconf/src/ncx/version.h0000664000175000017500000000474014770023131020001 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_version #define _H_version /* FILE: version.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Contains the current Yuma version ID ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 23-jun-09 abb Begun. 01-jun-10 abb Switched to 1.x instead of 0.x to align with debian packaging standards 09-oct-10 abb Bumped version to 1.14 17-apr-11 abb Bumped version to 1.15 29-may-11 abb Bumped trunk version to 2.0 25-sep-11 abb Bumped trunk version to 2.1 25-jan-12 abb Bumped trunk version to 2.2 21-dec-15 vv Bumped trunk version to 2.5 17-jul-16 vv Bumped trunk version to 2.6 21-jul-16 vv Bumped trunk version to 2.7 29-jul-16 vv Bumped trunk version to 2.8 20-aug-16 vv Bumped trunk version to 2.9 12-apr-17 vv Bumped trunk version to 2.10 */ #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define YUMA_VERSION (const xmlChar *)"2.14" #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_version */ yuma123_2.14/netconf/src/ncx/ses.c0000664000175000017500000020734114770023131017103 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: ses.c NETCONF Session Manager: Common Support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 06jun06 abb begun; cloned from ses_mgr.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #include #include #include "procdefs.h" #include "log.h" #include "ncx.h" #include "ncx_num.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "tstamp.h" #include "val.h" #include "xml_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define SES_DEBUG 1 /* #define SES_SSH_DEBUG 1 */ /* #define SES_DEBUG_TRACE 1 */ #define SES_DEBUG_XML_TRACE 1 #endif #define LTSTR (const xmlChar *)"<" #define GTSTR (const xmlChar *)">" #define AMPSTR (const xmlChar *)"&" #define QSTR (const xmlChar *)""" /* used by yangcli to read in between stdin polling */ #define MAX_READ_TRIES 500 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static ses_total_stats_t totals; /******************************************************************** * FUNCTION accept_buffer_ssh_v10 * * Handle one input buffer within the ses_accept_input function * transport is SSH or TCP; protocol is NETCONF:base:1.0 * * Need to separate the input stream into separate XML instance * documents and reset the xmlTextReader each time a new document * is encountered. For SSH, also need to detect the EOM flag * and remove it + control input to the reader. * * This function breaks the byte stream into ses_msg_t structs * that get queued on the session's msgQ * * INPUTS: * scb == session control block to accept input for * len == number of bytes in scb->readbuff just read * * RETURNS: * status *********************************************************************/ static status_t accept_buffer_ssh_v10 (ses_cb_t *scb, size_t len) { ses_msg_t *msg; ses_msg_buff_t *buff; const char *endmatch; status_t res; boolean done; xmlChar ch; uint32 count; #ifdef SES_DEBUG if (LOGDEBUG3 && scb->state != SES_ST_INIT) { scb->readbuff[len] = 0; log_debug3("\nses: accept buffer (%u):\n%s\n", len, scb->readbuff); } else if (LOGDEBUG2) { log_debug2("\nses: accept buffer (%u)", len); } #endif /* make sure there is a current message */ msg = (ses_msg_t *)dlq_lastEntry(&scb->msgQ); if (msg == NULL || msg->ready) { /* need a new message */ res = ses_msg_new_msg(&msg); if (res != NO_ERR) { return res; } /* add early */ dlq_enque(msg, &scb->msgQ); } /* init local vars */ endmatch = NC_SSH_END; done = FALSE; count = 0; res = NO_ERR; /* make sure there is a current buffer to use */ buff = (ses_msg_buff_t *)dlq_lastEntry(&msg->buffQ); if (buff == NULL || buff->bufflen == SES_MSG_BUFFSIZE) { /* need a new buffer */ res = ses_msg_new_buff(scb, FALSE, /* outbuff */ &buff); if (res != NO_ERR) { return res; } dlq_enque(buff, &msg->buffQ); } /* check the chars in the buffer for the * the NETCONF EOM if this is an SSH session * * TBD: ses how xmlReader handles non-well-formed XML * and junk text. May need some sort of error state * to dump chars until the EOM if SSH, or force the * session closed, or force the transport to reset somehow */ while (!done) { /* check if the read buffer length has been reached */ if (count == (uint32)len) { done = TRUE; continue; } /* check if a new message is needed */ if (msg == NULL) { res = ses_msg_new_msg(&msg); if (res == NO_ERR) { /* put msg in the msg Q early */ dlq_enque(msg, &scb->msgQ); } else { return res; } } /* check if a buffer is needed */ if (buff == NULL) { /* need new first buff for a message */ res = ses_msg_new_buff(scb, FALSE, /* outbuff */ &buff); if (res != NO_ERR) { return res; } dlq_enque(buff, &msg->buffQ); } else if (buff->buffpos == SES_MSG_BUFFSIZE) { /* current buffer is full; get a new one */ buff->buffpos = 0; buff->bufflen = SES_MSG_BUFFSIZE; res = ses_msg_new_buff(scb, FALSE, /* outbuff */ &buff); if (res != NO_ERR) { return res; } dlq_enque(buff, &msg->buffQ); } /* get the next char in the input buffer and advance the pointer */ ch = scb->readbuff[count++]; buff->buff[buff->buffpos++] = ch; /* handle the char in the buffer based on the input state */ switch (scb->instate) { case SES_INST_IDLE: /* check for EOM if SSH or just copy the char */ if (ch==*endmatch) { scb->instate = SES_INST_INEND; scb->inendpos = 1; } else { scb->instate = SES_INST_INMSG; } break; case SES_INST_INMSG: /* check for EOM if SSH or just copy the char */ if (ch==*endmatch) { scb->instate = SES_INST_INEND; scb->inendpos = 1; } break; case SES_INST_INEND: /* already matched at least 1 EOM char * try to match the rest of the SSH EOM string */ if (ch == endmatch[scb->inendpos]) { /* check message complete */ if (++scb->inendpos == NC_SSH_END_LEN) { /* completely matched the SSH EOM marker * finish the current message and put it in the inreadyQ * * buff->buffpos points to the first char after * the EOM string, check any left over bytes * to start a new message * * handle any bytes left at the end of 'buff' * if this is a base:1.1 session then there * is a possibility the new framing will start * right away, because the peer sent a * and an back-to-back * * save the buffer and make the message ready to parse * don't let the xmlreader see the EOM string */ if(buff->buffpos > NC_SSH_END_LEN) { buff->bufflen = buff->buffpos - NC_SSH_END_LEN; } else { /* the SSH EOM string is split accross 2 buffers */ unsigned int rem_len; rem_len = NC_SSH_END_LEN - buff->buffpos; dlq_remove(buff); ses_msg_free_buff(scb, buff); buff = (ses_msg_buff_t *)dlq_lastEntry(&msg->buffQ); buff->bufflen = buff->bufflen - rem_len; } buff->buffpos = 0; buff->islast = TRUE; msg->curbuff = NULL; msg->ready = TRUE; ses_msg_make_inready(scb); /* reset reader state */ scb->instate = SES_INST_IDLE; scb->inendpos = 0; /* force a new message and buffer */ msg = NULL; buff = NULL; /* check corner-case: do not try to handle * base:1.1 back 2 back messages * if protocol not set yet by hello * an empty message is the last one right now * error out if a 3rd real message is found */ if (scb->protocol == NCX_PROTO_NONE && ncx_protocol_enabled(NCX_PROTO_NETCONF11)) { assert(dlq_count(&scb->msgQ) == 1); scb->indefer_len=len-count; memmove(scb->readbuff,&scb->readbuff[count], len-count); count=len; log_debug3("\nses: Deferring trailing input data on ses:%d (%d of %d bytes)" "until the preceding message is handled.", scb->indefer_len, len, scb->sid); } } /* else still more chars in EOM string to match */ } else { /* char did not match the expected position in the * EOM string, go back to MSG state */ scb->instate = SES_INST_INMSG; scb->inendpos = 0; } break; default: /* should not happen */ return SET_ERROR(ERR_INTERNAL_VAL); } } return NO_ERR; } /* accept_buffer_ssh_v10 */ /******************************************************************** * FUNCTION accept_buffer_ssh_v11 * * Handle one input buffer within the ses_accept_input function * Transport is SSH. Protocol is NETCONF:base:1.1 * Use SSH base:1.1 framing, not old EOM sequence * * Need to separate the input stream into separate XML instance * documents and reset the xmlTextReader each time a new document * is encountered. Handle NETCONF over SSH base:1.1 protocol framing * * This function breaks the byte stream into ses_msg_t structs * that get queued on the session's msgQ. * * The chunks encoded into each incoming buffer will be * be mapped and stored in 1 or more ses_buffer_t structs. * There will be wasted buffer space if the chunks * are very small and more than SES_MAX_BUFF_CHUNKS * per buffer are received * * RFC 6242, sec 4.2. Chunked Framing Mechanism This mechanism encodes all NETCONF messages with a chunked framing. Specifically, the message follows the ABNF [RFC5234] rule Chunked- Message: Chunked-Message = 1*chunk end-of-chunks chunk = LF HASH chunk-size LF chunk-data chunk-size = 1*DIGIT1 0*DIGIT chunk-data = 1*OCTET end-of-chunks = LF HASH HASH LF DIGIT1 = %x31-39 DIGIT = %x30-39 HASH = %x23 LF = %x0A OCTET = %x00-FF The chunk-size field is a string of decimal digits indicating the number of octets in chunk-data. Leading zeros are prohibited, and the maximum allowed chunk-size value is 4294967295. * INPUTS: * scb == session control block to accept input for * len == number of bytes in scb->readbuff just read * * RETURNS: * status *********************************************************************/ static status_t accept_buffer_ssh_v11 (ses_cb_t *scb, size_t len) { ses_msg_t *msg; ses_msg_buff_t *buff; status_t res; uint32 count; boolean done; xmlChar ch; size_t chunkleft, inbuffleft, outbuffleft, copylen; ncx_num_t num; #ifdef SES_DEBUG if (LOGDEBUG3 && scb->state != SES_ST_INIT) { scb->readbuff[len] = 0; log_debug3("\nses: accept base:1.1 buffer (%u):\n%s\n", len, scb->readbuff); } else if (LOGDEBUG2) { log_debug2("\nses: accept base:1.1 buffer (%u)", len); } #endif /* make sure there is a current message */ msg = (ses_msg_t *)dlq_lastEntry(&scb->msgQ); if (msg == NULL || msg->ready) { /* need a new message */ res = ses_msg_new_msg(&msg); if (res != NO_ERR) { return res; } /* add early */ dlq_enque(msg, &scb->msgQ); } /* init local vars */ done = FALSE; res = NO_ERR; ch = 0; count = 0; /* make sure there is a current buffer to use */ buff = (ses_msg_buff_t *)dlq_lastEntry(&msg->buffQ); if (buff == NULL || buff->bufflen == SES_MSG_BUFFSIZE) { /* need a new buffer */ res = ses_msg_new_buff(scb, FALSE, /* outbuff */ &buff); if (res != NO_ERR) { return res; } dlq_enque(buff, &msg->buffQ); } /* check the chars in the buffer for the * frame markers, based on session instate value * the framing breaks will be mixed in with the XML */ while (!done) { /* check if the read buffer length has been reached */ if (count == (uint32)len) { done = TRUE; continue; } /* check if a buffer is needed */ if (buff == NULL) { /* need new first buff for a message */ res = ses_msg_new_buff(scb, FALSE, /* outbuff */ &buff); if (res != NO_ERR) { return res; } dlq_enque(buff, &msg->buffQ); } else if (buff->buffpos == SES_MSG_BUFFSIZE) { /* current buffer is full; get a new one */ buff->buffpos = 0; buff->bufflen = SES_MSG_BUFFSIZE; res = ses_msg_new_buff(scb, FALSE, /* outbuff */ &buff); if (res != NO_ERR) { return res; } dlq_enque(buff, &msg->buffQ); } /* get the next char in the input buffer and advance the pointer */ ch = scb->readbuff[count++]; /* handle the char in the buffer based on the input state */ switch (scb->instate) { case SES_INST_IDLE: /* expecting start of the first chunk of a message */ if (ch == '\n') { /* matched the \n to start a chunk */ scb->instate = SES_INST_INSTART; scb->inendpos = 1; } else { /* return framing error */ if (LOGDEBUG) { log_debug("\nses: invalid base:1;1 framing " "(idle: expect first newline)"); } done = TRUE; res = ERR_NCX_INVALID_FRAMING; } break; case SES_INST_INSTART: if (scb->inendpos == 1) { if (ch == '#') { /* matched first hash mark # */ scb->inendpos++; } else { /* return framing error; * save buff for garbage collection */ if (LOGDEBUG) { log_debug("\nses: invalid base:1;1 framing " "(instart: expect '#')"); } done = TRUE; res = ERR_NCX_INVALID_FRAMING; } } else if (scb->inendpos == 2) { /* expecting at least 1 starting digit */ if (ch >= '1' && ch <= '9') { scb->startchunk[0] = ch; /* save first num char */ scb->inendpos++; /* == 3 now */ } else { /* return framing error */ if (LOGDEBUG) { log_debug("\nses: invalid base:1;1 framing " "(instart: expect digit)"); } done = TRUE; res = ERR_NCX_INVALID_FRAMING; } } else { /* looking for end of a number * expecting an ending digit or \n */ if (scb->inendpos == SES_MAX_STARTCHUNK_SIZE) { /* invalid number -- too long error */ if (LOGDEBUG) { log_debug("\nses: invalid base:1;1 framing " "(instart: number too long)"); } done = TRUE; res = ERR_NCX_INVALID_FRAMING; } else if (ch == '\n') { /* have the complete number now * done with chunk start tag * get a binary number from this number */ ncx_init_num(&num); scb->startchunk[scb->inendpos - 2] = 0; scb->inendpos = 0; res = ncx_convert_num(scb->startchunk, NCX_NF_DEC, NCX_BT_UINT32, &num); if (res == NO_ERR) { msg->expchunksize = num.u; msg->curchunksize = 0; scb->instate = SES_INST_INMSG; } else { if (LOGDEBUG) { log_debug("\nses: invalid base:1;1 framing " "(instart: invalid number)"); } done = TRUE; res = ERR_NCX_INVALID_FRAMING; } ncx_clean_num(NCX_BT_UINT32, &num); } else if (ch >= '0' && ch <= '9') { /* continue collecting digits */ scb->startchunk[scb->inendpos - 2] = ch; scb->inendpos++; } else { /* return framing error; invalid char */ if (LOGDEBUG) { log_debug("\nses: invalid base:1;1 framing " "(instart: expect digit char)"); } done = TRUE; res = ERR_NCX_INVALID_FRAMING; } } break; case SES_INST_INMSG: /* expecting first part or Nth part of a chunk */ count--; /* back up count */ chunkleft = msg->expchunksize - msg->curchunksize; inbuffleft = len - count; outbuffleft = SES_MSG_BUFFSIZE - buff->buffpos; copylen = min(inbuffleft, chunkleft); /* account for the amount copied above */ msg->curchunksize += copylen; if (msg->curchunksize == msg->expchunksize) { /* finished this chunk */ scb->inendpos = 0; scb->instate = SES_INST_INBETWEEN; } /* else finished the input buffer */ /* copy the required input bytes to 1 or more buffers */ if (outbuffleft >= copylen) { /* rest of chunk or input fits in rest of current buffer */ xml_strncpy(&buff->buff[buff->buffpos], &scb->readbuff[count], copylen); buff->buffpos += copylen; count += copylen; } else { /* rest of the current buffer not big enough; * split input across N buffers; * first finish current buffer */ xml_strncpy(&buff->buff[buff->buffpos], &scb->readbuff[count], outbuffleft); buff->buffpos = 0; buff->bufflen = SES_MSG_BUFFSIZE; copylen -= outbuffleft; count += outbuffleft; /* fill N-2 full buffers and possibly * 1 final partial buffer */ while (copylen > 0) { size_t copy2len; /* get new buffer */ res = ses_msg_new_buff(scb, FALSE, /* outbuff */ &buff); if (res != NO_ERR) { return res; } dlq_enque(buff, &msg->buffQ); copy2len = min(SES_MSG_BUFFSIZE, copylen); xml_strncpy(&buff->buff[buff->buffpos], &scb->readbuff[count], copy2len); buff->buffpos += copy2len; buff->bufflen = copy2len; copylen -= copy2len; count += copy2len; } } break; case SES_INST_INBETWEEN: if (scb->inendpos == 0) { if (ch == '\n') { scb->inendpos++; } else { if (LOGDEBUG) { log_debug("\nses: invalid base:1;1 framing " "(inbetween: expect newline)"); } done = TRUE; res = ERR_NCX_INVALID_FRAMING; } } else if (scb->inendpos == 1) { if (ch == '#') { scb->inendpos++; } else { if (LOGDEBUG) { log_debug("\nses: invalid base:1;1 framing " "(inbetween: expect '#')"); } done = TRUE; res = ERR_NCX_INVALID_FRAMING; } } else { if (ch == '#') { scb->inendpos++; scb->instate = SES_INST_INEND; } else if (ch >= '1' && ch <= '9') { /* back up and process this char in start state * account for first 2 chars \n# */ count--; scb->instate = SES_INST_INSTART; } else { if (LOGDEBUG) { log_debug("\nses: invalid base:1;1 framing " "(inbetween: expect digit or '#')"); } done = TRUE; res = ERR_NCX_INVALID_FRAMING; } } break; case SES_INST_INEND: /* expect to match 4 char \n##\n sequence */ if (scb->inendpos == 2) { if (ch == '#') { scb->inendpos++; } else { if (LOGDEBUG) { log_debug("\nses: invalid base:1;1 framing " "(inend: expect '#')"); } done = TRUE; res = ERR_NCX_INVALID_FRAMING; } } else if (scb->inendpos == 3) { if (ch == '\n') { /* completely matched the SSH End of Chunks marker * finish the current message and put it in the inreadyQ */ msg->curbuff = NULL; msg->ready = TRUE; ses_msg_make_inready(scb); /* reset reader state */ scb->instate = SES_INST_IDLE; scb->inendpos = 0; if(buff->buffpos == 0) { /* remove an empty trailing buffer */ dlq_remove(buff); ses_msg_free_buff (scb,buff); } else { buff->bufflen = buff->buffpos; buff->buffpos = 0; } if (count < len) { /* need a new message */ res = ses_msg_new_msg(&msg); if (res != NO_ERR) { return res; } /* add early */ dlq_enque(msg, &scb->msgQ); /* get a new buffer */ res = ses_msg_new_buff(scb, FALSE, /* outbuff */ &buff); if (res != NO_ERR) { return res; } dlq_enque(buff, &msg->buffQ); } else { buff = NULL; } } else { if (LOGDEBUG) { log_debug("\nses: invalid base:1;1 framing " "(inend: expect newline)"); } done = TRUE; res = ERR_NCX_INVALID_FRAMING; } } else { /* should not happen */ done = TRUE; res = SET_ERROR(ERR_INTERNAL_VAL); } break; default: /* should not happen */ done = TRUE; res = SET_ERROR(ERR_INTERNAL_VAL); } } return res; } /* accept_buffer_ssh_v11 */ /******************************************************************** * FUNCTION put_char_entity * * Write a character entity for the specified character * * INPUTS: * scb == session control block to start msg * ch == character to write as a character entity * *********************************************************************/ static void put_char_entity (ses_cb_t *scb, xmlChar ch) { xmlChar numbuff[NCX_MAX_NUMLEN]; snprintf((char *)numbuff, NCX_MAX_NUMLEN, "%u", (uint32)ch); ses_putchar(scb, '&'); ses_putchar(scb, '#'); ses_putstr(scb, numbuff); ses_putchar(scb, ';'); } /* put_char_entity */ /******************************************************************** * FUNCTION handle_prolog_state * * Deal with the first few characters of an incoming message * hack: xmlTextReaderRead wants to start off * with a newline for some reason, so always * start the first buffer with a newline, even if * none was sent by the NETCONF peer. * Only the first 0xa char seems to matter * Trailing newlines do not seem to affect the problem * * Also, the first line needs to be the * prolog directive, or the libxml2 parser refuses * to use the incoming message. * It quits and returns EOF instead. * * Only the current buffer is processed within the message * INPUTS: * msg == current message to process * buffer == buffer to fill in * bufflen == max buffer size * buff == current buffer about to be read * endpos == max end pos of buff to use * retlen == address of running return length * * OUTPUTS: * buffer is filled in with the prolog if needed * msg->prolog_state is updated as needed * *retlen may be increased if prolog or newline added *********************************************************************/ static void handle_prolog_state (ses_msg_t *msg, char *buffer, int bufflen, ses_msg_buff_t *buff, size_t endpos, int *retlen) { boolean needprolog = FALSE; boolean needfirstnl = FALSE; char tempbuff[4]; int i, j, k; /* save the first 3 chars in the temp buffer */ memset(tempbuff, 0x0, 4); switch (msg->prolog_state) { case SES_PRST_NONE: if ((endpos - buff->buffpos) < 3) { msg->prolog_state = SES_PRST_WAITING; return; } else if (!strncmp((const char *)&buff->buff[buff->buffpos], "\nprolog_state = SES_PRST_DONE; return; } else if (!strncmp((const char *)&buff->buff[buff->buffpos], "prolog_state = SES_PRST_DONE; } else { needprolog = TRUE; msg->prolog_state = SES_PRST_DONE; } break; case SES_PRST_WAITING: if ((*retlen + (endpos - buff->buffpos)) < 3) { /* keep waiting */ return; } else { msg->prolog_state = SES_PRST_DONE; strncpy(tempbuff, buffer, *retlen); for (i = *retlen, j=buff->buffpos; i <= 3; i++, j++) { tempbuff[i] = (char)buff->buff[j]; } if (!strncmp(tempbuff, "\nreadbuffsize = SES_READBUFF_SIZE; /* make sure the debug log trace code never writes a zero byte * past the end of a full read buffer by adding 2 pad bytes */ scb->readbuff = m__getMem(scb->readbuffsize + 2); if (scb->readbuff == NULL) { m__free(now); m__free(scb); return NULL; } scb->start_time = now; dlq_createSQue(&scb->msgQ); dlq_createSQue(&scb->freeQ); dlq_createSQue(&scb->outQ); scb->linesize = SES_DEF_LINESIZE; scb->withdef = NCX_DEF_WITHDEF; scb->indent = NCX_DEF_INDENT; scb->cache_timeout = NCX_DEF_VTIMEOUT; return scb; } /* ses_new_scb */ /******************************************************************** * FUNCTION ses_new_dummy_scb * * Create a new dummy session control block * * INPUTS: * none * RETURNS: * pointer to initialized SCB, or NULL if malloc error *********************************************************************/ ses_cb_t * ses_new_dummy_scb (void) { ses_cb_t *scb; scb = ses_new_scb(); if (!scb) { return NULL; } scb->type = SES_TYP_DUMMY; scb->mode = SES_MODE_XML; scb->state = SES_ST_IDLE; scb->sid = 0; scb->username = xml_strdup(NCX_DEF_SUPERUSER); return scb; } /* ses_new_dummy_scb */ /******************************************************************** * FUNCTION ses_free_scb * * Free a session control block * * INPUTS: * scb == session control block to free * RETURNS: * none *********************************************************************/ void ses_free_scb (ses_cb_t *scb) { ses_msg_t *msg; ses_msg_buff_t *buff; assert( scb && "scb is NULL" ); if (scb->start_time) { m__free(scb->start_time); } if (scb->username) { m__free(scb->username); } if (scb->peeraddr) { m__free(scb->peeraddr); } if (scb->reader) { xml_free_reader(scb->reader); } if (scb->fd) { close(scb->fd); } if (scb->fp) { fclose(scb->fp); } while (!dlq_empty(&scb->msgQ)) { msg = (ses_msg_t *)dlq_deque(&scb->msgQ); ses_msg_free_msg(scb, msg); } if (scb->outbuff) { ses_msg_free_buff(scb, scb->outbuff); } while (!dlq_empty(&scb->outQ)) { buff = (ses_msg_buff_t *)dlq_deque(&scb->outQ); ses_msg_free_buff(scb, buff); } /* the freeQ must be cleared after the outQ because * the normal free buffer action is to move it to * the scb->freeQ */ while (!dlq_empty(&scb->freeQ)) { buff = (ses_msg_buff_t *)dlq_deque(&scb->freeQ); ses_msg_free_buff(scb, buff); } if (scb->readbuff != NULL) { m__free(scb->readbuff); } if (scb->buffcnt) { log_error("\nsession %d terminated with %d buffers", scb->sid, scb->buffcnt); } /* the mgrcb must be cleaned before this function is called */ m__free(scb); } /* ses_free_scb */ /******************************************************************** * FUNCTION ses_putchar * * Write one char to the session, without any translation * * THIS FUNCTION DOES NOT CHECK ANY PARAMETERS TO SAVE TIME * * NO CHARS ARE ACTUALLY WRITTEN TO A REAL SESSION!!! * The 'output ready' indicator will be set and the session * queued in the outreadyQ. Non-blocking IO functions * will send the data when the connection allows. * * INPUTS: * scb == session control block to start msg * ch = xmlChar to write, cast as uint32 to avoid compiler warnings * *********************************************************************/ void ses_putchar (ses_cb_t *scb, uint32 ch) { ses_msg_buff_t *buff; status_t res; if (scb->fd) { /* Normal NETCONF session mode: */ res = NO_ERR; if (scb->outbuff == NULL) { res = ses_msg_new_buff(scb, TRUE, &scb->outbuff); } if (scb->outbuff != NULL) { buff = scb->outbuff; res = ses_msg_write_buff(scb, buff, ch); if (res == ERR_BUFF_OVFL) { res = ses_msg_new_output_buff(scb); if (res == NO_ERR) { buff = scb->outbuff; res = ses_msg_write_buff(scb, buff, ch); } } } else { res = ERR_NCX_OPERATION_FAILED; } if (res == NO_ERR) { scb->stats.out_bytes++; totals.stats.out_bytes++; } } else if (scb->fp) { /* debug session, sending output to a file */ fputc((int)ch, scb->fp); } else { /* debug session, sending output to the screen */ putchar((int)ch); } if (ch=='\n') { scb->stats.out_line = 0; } else { scb->stats.out_line++; } } /* ses_putchar */ /******************************************************************** * FUNCTION ses_putstr * * Write a zero-terminated string to the session * * THIS FUNCTION DOES NOT CHECK ANY PARAMTERS TO SAVE TIME * * INPUTS: * scb == session control block to start msg * str == string to write * *********************************************************************/ void ses_putstr (ses_cb_t *scb, const xmlChar *str) { while (*str) { ses_putchar(scb, *str++); } } /* ses_putstr */ /******************************************************************** * FUNCTION ses_putstr_indent * * Write a zero-terminated content string to the session * with indentation * * THIS FUNCTION DOES NOT CHECK ANY PARAMTERS TO SAVE TIME * EXCEPT THAT ILLEGAL XML CHARS ARE CONVERTED TO CHAR ENTITIES * * INPUTS: * scb == session control block to start msg * str == string to write * indent == current indent amount * *********************************************************************/ void ses_putstr_indent (ses_cb_t *scb, const xmlChar *str, int32 indent) { ses_indent(scb, indent); while (*str) { if (*str == '\n') { if (indent < 0) { ses_putchar(scb, *str++); } else { ses_indent(scb, indent); str++; } } else { ses_putchar(scb, *str++); } } } /* ses_putstr_indent */ /******************************************************************** * FUNCTION ses_putcstr * * write XML element safe content string * Write a zero-terminated element content string to the session * * THIS FUNCTION DOES NOT CHECK ANY PARAMTERS TO SAVE TIME * EXCEPT THAT ILLEGAL XML CHARS ARE CONVERTED TO CHAR ENTITIES * * INPUTS: * scb == session control block to start msg * str == string to write * indent == current indent amount * *********************************************************************/ void ses_putcstr (ses_cb_t *scb, const xmlChar *str, int32 indent) { while (*str) { if (*str == '<') { ses_putstr(scb, LTSTR); str++; } else if (*str == '>') { ses_putstr(scb, GTSTR); str++; } else if (*str == '&') { ses_putstr(scb, AMPSTR); str++; } else if ((scb->mode == SES_MODE_XMLDOC || scb->mode == SES_MODE_TEXT) && *str == '\n') { if (indent < 0) { ses_putchar(scb, *str++); } else { ses_indent(scb, indent); str++; } } else { ses_putchar(scb, *str++); } } } /* ses_putcstr */ /******************************************************************** * FUNCTION ses_puhcstr * * write HTML element safe content string * Write a zero-terminated element content string to the session * * THIS FUNCTION DOES NOT CHECK ANY PARAMTERS TO SAVE TIME * EXCEPT THAT ILLEGAL XML CHARS ARE CONVERTED TO CHAR ENTITIES * * INPUTS: * scb == session control block to start msg * str == string to write * *********************************************************************/ void ses_puthstr (ses_cb_t *scb, const xmlChar *str) { while (*str) { if (*str == '<') { ses_putstr(scb, LTSTR); str++; } else if (*str == '>') { ses_putstr(scb, GTSTR); str++; } else if (*str == '&') { ses_putstr(scb, AMPSTR); str++; } else { ses_putchar(scb, *str++); } } } /* ses_puthstr */ /******************************************************************** * FUNCTION ses_putcchar * * Write one content char to the session, with translation as needed * * THIS FUNCTION DOES NOT CHECK ANY PARAMETERS TO SAVE TIME * * NO CHARS ARE ACTUALLY WRITTEN TO A REAL SESSION!!! * The 'output ready' indicator will be set and the session * queued in the outreadyQ. Non-blocking IO functions * will send the data when the connection allows. * * INPUTS: * scb == session control block to write * ch = xmlChar to write, cast as uint32 to avoid compiler warnings * *********************************************************************/ void ses_putcchar (ses_cb_t *scb, uint32 ch) { if (ch) { if (ch == '<') { ses_putstr(scb, LTSTR); } else if (ch == '>') { ses_putstr(scb, GTSTR); } else if (ch == '&') { ses_putstr(scb, AMPSTR); } else if ((scb->mode == SES_MODE_XMLDOC || scb->mode == SES_MODE_TEXT) && ch == '\n') { int32 indent = ses_indent_count(scb); if (indent < 0) { ses_putchar(scb, ch); } else { ses_indent(scb, indent); } } else { ses_putchar(scb, ch); } } } /* ses_putcchar */ /******************************************************************** * FUNCTION ses_putastr * * write XML attribute safe content string * Write a zero-terminated attribute content string to the session * * THIS FUNCTION DOES NOT CHECK ANY PARAMTERS TO SAVE TIME * EXCEPT THAT ILLEGAL XML CHARS ARE CONVERTED TO CHAR ENTITIES * * INPUTS: * scb == session control block to start msg * str == string to write * indent == current indent amount * *********************************************************************/ void ses_putastr (ses_cb_t *scb, const xmlChar *str, int32 indent) { while (*str) { if (*str == '<') { ses_putstr(scb, LTSTR); str++; } else if (*str == '>') { ses_putstr(scb, GTSTR); str++; } else if (*str == '&') { ses_putstr(scb, AMPSTR); str++; } else if (*str == '"') { ses_putstr(scb, QSTR); str++; } else if (*str == '\n') { if (scb->mode == SES_MODE_XMLDOC || scb->mode == SES_MODE_TEXT) { if (indent < 0) { ses_putchar(scb, *str++); } else { ses_indent(scb, indent); str++; } } else { put_char_entity(scb, *str++); } } else if (isspace(*str)) { put_char_entity(scb, *str++); } else { ses_putchar(scb, *str++); } } } /* ses_putastr */ /******************************************************************** * FUNCTION ses_putjstr * * write JSON safe content string * Write a zero-terminated element content string to the session * * THIS FUNCTION DOES NOT CHECK ANY PARAMTERS TO SAVE TIME * EXCEPT THAT ILLEGAL JSON CHARS ARE CONVERTED TO ESCAPED CHARS * * INPUTS: * scb == session control block to start msg * str == string to write * indent == current indent amount * *********************************************************************/ void ses_putjstr (ses_cb_t *scb, const xmlChar *str, int32 indent) { ses_indent(scb, indent); while (*str) { switch (*str) { case '"': ses_putchar(scb, '\\'); ses_putchar(scb, '"'); break; case '\\': ses_putchar(scb, '\\'); ses_putchar(scb, '\\'); break; case '/': ses_putchar(scb, '\\'); ses_putchar(scb, '/'); break; case '\b': ses_putchar(scb, '\\'); ses_putchar(scb, 'b'); break; case '\f': ses_putchar(scb, '\\'); ses_putchar(scb, 'f'); break; case '\n': ses_putchar(scb, '\\'); ses_putchar(scb, 'n'); break; case '\r': ses_putchar(scb, '\\'); ses_putchar(scb, 'r'); break; case '\t': ses_putchar(scb, '\\'); ses_putchar(scb, 't'); break; default: ses_putchar(scb, *str); } ++str; } } /* ses_putjstr */ /******************************************************************** * FUNCTION ses_indent * * Write the proper newline + indentation to the specified session * * THIS FUNCTION DOES NOT CHECK ANY PARAMETERS TO SAVE TIME * * INPUTS: * scb == session control block to start msg * indent == number of chars to indent after a newline * will be ignored if indent is turned off * in the agent profile * == -1 means no newline or indent * == 0 means just newline * *********************************************************************/ void ses_indent (ses_cb_t *scb, int32 indent) { int32 i; if (indent < 0) { return; } /* set limit on indentation in case of bug */ indent = min(indent, 255); ses_putchar(scb, '\n'); for (i=0; iindent; } /* ses_indent_count */ /******************************************************************** * FUNCTION ses_set_indent * * Set the indent count for this session * * INPUTS: * scb == session control block to check * indent == value to use (may get adjusted) * *********************************************************************/ void ses_set_indent (ses_cb_t *scb, int32 indent) { #ifdef DEBUG if (scb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (indent < 0) { indent = 0; } else if (indent > 9) { indent = 9; } scb->indent = indent; } /* ses_set_indent */ /******************************************************************** * FUNCTION ses_set_mode * * Set the output mode for the specified session * * INPUTS: * scb == session control block to set * mode == new mode value * RETURNS: * none *********************************************************************/ void ses_set_mode (ses_cb_t *scb, ses_mode_t mode) { assert( scb && "scb is NULL" ); scb->mode = mode; } /* ses_set_mode */ /******************************************************************** * FUNCTION ses_get_mode * * Get the output mode for the specified session * * INPUTS: * scb == session control block to get * * RETURNS: * session mode value *********************************************************************/ ses_mode_t ses_get_mode (ses_cb_t *scb) { assert( scb && "scb is NULL" ); return scb->mode; } /* ses_get_mode */ /******************************************************************** * FUNCTION ses_start_msg * * Start a new outbound message on the specified session * * INPUTS: * scb == session control block to start msg * * RETURNS: * status *********************************************************************/ status_t ses_start_msg (ses_cb_t *scb) { assert( scb && "scb is NULL" ); /* check if this session will allow a msg to start now */ if (scb->state >= SES_ST_SHUTDOWN) { return ERR_NCX_OPERATION_FAILED; } /* Generate Start of XML Message Directive */ ses_putstr(scb, XML_START_MSG); return NO_ERR; } /* ses_start_msg */ /******************************************************************** * FUNCTION ses_finish_msg * * Finish an outbound message on the specified session * * INPUTS: * scb == session control block to finish msg * RETURNS: * none *********************************************************************/ void ses_finish_msg (ses_cb_t *scb) { assert( scb && "scb is NULL" ); /* add the NETCONF EOM marker */ if (scb->transport==SES_TRANSPORT_SSH || scb->transport==SES_TRANSPORT_TCP) { if (scb->framing11) { scb->outbuff->islast = TRUE; } else { ses_putstr(scb, (const xmlChar *)NC_SSH_END); } } /* add a final newline when writing to a file * but never if writing to a socket or STDOUT */ if (scb->fd == 0 && scb->fp != stdout) { ses_putchar(scb, '\n'); } /* queue for output if not done so already */ if (scb->type != SES_TYP_DUMMY) { ses_msg_finish_outmsg(scb); } } /* ses_finish_msg */ /******************************************************************** * FUNCTION ses_read_cb * * The IO input front-end for the xmlTextReader parser read fn * * Need to separate the input stream into separate XML instance * documents and reset the xmlTextReader each time a new document * is encountered. * * The underlying transport (accept_buffer*) has already stripped * all the framing characters from the incoming stream * * Uses a complex state machine which does not assume that the * input from the network is going to arrive in well-formed chunks. * It has to be treated as a byte stream (SOCK_STREAM). * * Does not remove char entities or any XML, just the SSH framing chars * * INPUTS: * context == scb pointer for the session to read * buffer == char buffer to fill * len == length of the buffer * * RETURNS: * number of bytes read into the buffer * -1 indicates error and EOF *********************************************************************/ int ses_read_cb (void *context, char *buffer, int len) { ses_cb_t *scb; ses_msg_t *msg; ses_msg_buff_t *buff; int retlen; boolean done; if (len == 0) { return 0; } scb = (ses_cb_t *)context; if (scb->state >= SES_ST_SHUTDOWN_REQ) { return -1; } msg = (ses_msg_t *)dlq_firstEntry(&scb->msgQ); if (msg == NULL) { return 0; } /* hack: the xmlTextReader parser will request 4 bytes * to test the message and a buffer size of 4096 * if the request is for real. The 4096 constant * is not hard-wired into the code, though. */ if (len == 4) { strncpy(buffer, "\ncurbuff; if (buff == NULL) { buff = (ses_msg_buff_t *)dlq_firstEntry(&msg->buffQ); if (buff == NULL) { return 0; } else { buff->buffpos = buff->buffstart; msg->curbuff = buff; } } /* check current buffer end has been reached */ if (buff->buffpos == buff->bufflen) { buff = (ses_msg_buff_t *)dlq_nextEntry(buff); if (buff == NULL) { return 0; } else { buff->buffpos = buff->buffstart; msg->curbuff = buff; } } handle_prolog_state(msg, buffer, len, buff, buff->bufflen, &retlen); /* start transferring bytes to the return buffer */ done = FALSE; while (!done) { /* check current buffer end has been reached */ if (buff->buffpos == buff->bufflen) { buff = (ses_msg_buff_t *)dlq_nextEntry(buff); if (buff == NULL) { done = TRUE; continue; } else { buff->buffpos = buff->buffstart; msg->curbuff = buff; handle_prolog_state(msg, buffer, len, buff, buff->bufflen, &retlen); } } if (buff->buffpos == buff->bufflen) { continue; /* an empty buffer! */ } buffer[retlen++] = (char)buff->buff[buff->buffpos++]; /* check xmlreader buffer full */ if (retlen == len) { done = TRUE; continue; } } #ifdef SES_DEBUG_XML_TRACE ncx_write_tracefile(buffer, retlen); #endif return retlen; } /* ses_read_cb */ /******************************************************************** * FUNCTION ses_accept_input * * The IO input handler for the ncxserver loop * * Need to separate the input stream into separate XML instance * documents and reset the xmlTextReader each time a new document * is encountered. For SSH, also need to detect the EOM flag * and remove it + control input to the reader. * * This function breaks the byte stream into ses_msg_t structs * that get queued on the session's msgQ * * INPUTS: * scb == session control block to accept input for * * RETURNS: * status *********************************************************************/ status_t ses_accept_input (ses_cb_t *scb) { status_t res; ssize_t ret; boolean done, readdone, erragain; uint32 readtries; #ifdef DEBUG if (scb == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif #ifdef SES_DEBUG if (LOGDEBUG3) { log_debug3("\nses_accept_input on session %d", scb->sid); } #endif res = NO_ERR; done = FALSE; readdone = FALSE; ret = 0; while (!done) { if (scb->state >= SES_ST_SHUTDOWN_REQ) { return ERR_NCX_SESSION_CLOSED; } /* loop until 1 buffer is read OK or retry count hit */ readtries = 0; readdone = FALSE; while (0==scb->indefer_len && !readdone && res == NO_ERR) { /* check read retry count */ if (readtries++ == MAX_READ_TRIES) { readdone = TRUE; res = ERR_NCX_SKIPPED; continue; } /* read data into the new buffer */ if (scb->rdfn) { erragain = FALSE; ret = (*scb->rdfn)(scb, (char *)scb->readbuff, scb->readbuffsize, &erragain); } else { ret = read(scb->fd, scb->readbuff, scb->readbuffsize); } if (ret < 0) { /* some error or EAGAIN */ res = NO_ERR; if (scb->rdfn) { if (!erragain) { res = ERR_NCX_READ_FAILED; } } else { if (errno != EAGAIN) { res = ERR_NCX_READ_FAILED; } } if (res == ERR_NCX_READ_FAILED) { log_error("\nError: ses read failed on session %d (%s)", scb->sid, strerror(errno)); } } else { /* channel closed or returned byte count */ res = NO_ERR; readdone = TRUE; } } if (res != NO_ERR) { if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } return res; } #ifdef SES_DEBUG if (LOGDEBUG4) { char* buf; buf = malloc(scb->readbuffsize+1); buf[scb->readbuffsize]=0; memcpy(buf, scb->readbuff, scb->readbuffsize); log_debug4("\nses: read (%s)", buf); free(buf); } #endif if(scb->indefer_len>0) { ret = scb->indefer_len; scb->indefer_len=0; } else if (ret == 0) { /* session closed by remote peer */ if (LOGINFO) { log_info("\nses: session %d shut by remote peer", scb->sid); } return ERR_NCX_SESSION_CLOSED; } else { /* read was done if we reach here */ if (LOGDEBUG2) { log_debug2("\nses read OK (%d) on session %d", ret, scb->sid); } /* increment session byte counters */ scb->stats.in_bytes += (uint32)ret; totals.stats.in_bytes += (uint32)ret; /* adjust length if read was z-terminated * should not be needed; zero not a valid * char to appear in an XML document */ if (scb->readbuff[ret - 1] == '\0') { /* don't barf if the client sends a * zero-terminated string instead of * just the contents of the string */ if (LOGDEBUG3) { log_debug3("\nses: dropping zero byte at EObuff"); } ret--; } } /* pass the read buffer in 1 of these functions * to handle the buffer framing */ if (ses_get_protocol(scb) == NCX_PROTO_NETCONF11) { res = accept_buffer_ssh_v11(scb, ret); } else { res = accept_buffer_ssh_v10(scb, ret); } if (res != NO_ERR || ((uint32)ret < scb->readbuffsize) || scb->rdfn == NULL || scb->indefer_len) { #ifdef SES_DEBUG_TRACE if (LOGDEBUG3) { log_debug3("\nses: bail exit %u:%u (%s)", ret, scb->readbuffsize, get_error_string(res)); } #endif done = TRUE; } /* else the SSH2 channel probably has more bytes to read */ } return res; } /* ses_accept_input */ /******************************************************************** * FUNCTION ses_state_name * * Get the name of a session state from the enum value * * INPUTS: * state == session state enum value * * RETURNS: * staing corresponding to the state name *********************************************************************/ const xmlChar * ses_state_name (ses_state_t state) { switch (state) { case SES_ST_NONE: return (const xmlChar *)"none"; case SES_ST_INIT: return (const xmlChar *)"init"; case SES_ST_HELLO_WAIT: return (const xmlChar *)"hello-wait"; case SES_ST_IDLE: return (const xmlChar *)"idle"; case SES_ST_IN_MSG: return (const xmlChar *)"in-msg"; case SES_ST_SHUTDOWN_REQ: return (const xmlChar *)"shutdown-requested"; case SES_ST_SHUTDOWN: return (const xmlChar *)"shutdown"; default: SET_ERROR(ERR_INTERNAL_VAL); return (const xmlChar *)"--"; } /*NOTREACHED*/ } /* ses_state_name */ /******************************************************************** * FUNCTION ses_withdef * * Get the with-defaults value for this session * * INPUTS: * scb == session control block to check * * RETURNS: * with-defaults value for the session *********************************************************************/ ncx_withdefaults_t ses_withdef (const ses_cb_t *scb) { #ifdef DEBUG if (scb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_WITHDEF_NONE; } #endif return scb->withdef; } /* ses_withdef */ /******************************************************************** * FUNCTION ses_line_left * * Get the number of bytes that can be added to the current line * before the session linesize limit is reached * * INPUTS: * scb == session control block to check * * RETURNS: * number of bytes left, or zero if limit already reached *********************************************************************/ uint32 ses_line_left (const ses_cb_t *scb) { if (scb->stats.out_line >= scb->linesize) { return 0; } else { return scb->linesize - scb->stats.out_line; } } /* ses_line_left */ /******************************************************************** * FUNCTION ses_put_extern * * write the contents of a file to the session * * INPUTS: * scb == session to write * fspec == filespec to write * *********************************************************************/ void ses_put_extern (ses_cb_t *scb, const xmlChar *fname) { FILE *fil; boolean done; int ch; fil = fopen((const char *)fname, "r"); if (!fil) { SET_ERROR(ERR_INTERNAL_VAL); return; } done = FALSE; while (!done) { ch = fgetc(fil); if (ch == EOF) { fclose(fil); done = TRUE; } else { ses_putchar(scb, (uint32)ch); } } } /* ses_put_extern */ /******************************************************************** * FUNCTION ses_get_total_stats * * Get a r/w pointer to the the session totals stats * * RETURNS: * pointer to the global session stats struct *********************************************************************/ ses_total_stats_t * ses_get_total_stats (void) { return &totals; } /* ses_get_total_stats */ /******************************************************************** * FUNCTION ses_get_transport_name * * Get the name of the transport for a given enum value * * INPUTS: * transport == ses_transport_t enum value * * RETURNS: * pointer to the string value for the specified enum *********************************************************************/ const xmlChar * ses_get_transport_name (ses_transport_t transport) { /* needs to match netconf-state DM values */ switch (transport) { case SES_TRANSPORT_NONE: return (const xmlChar *)"none"; case SES_TRANSPORT_SSH: return (const xmlChar *)"netconf-ssh"; case SES_TRANSPORT_BEEP: return (const xmlChar *)"netconf-beep"; case SES_TRANSPORT_SOAP: return (const xmlChar *)"netconf-soap-over-https"; case SES_TRANSPORT_SOAPBEEP: return (const xmlChar *)"netconf-soap-over-beep"; case SES_TRANSPORT_TLS: return (const xmlChar *)"netconf-tls"; case SES_TRANSPORT_TCP: return (const xmlChar *)"netconf-tcp"; default: SET_ERROR(ERR_INTERNAL_VAL); return (const xmlChar *)"none"; } } /* ses_get_transport_name */ /******************************************************************** * FUNCTION ses_set_xml_nons * * force xmlns attributes to be skipped in XML mode * * INPUTS: * scb == session to set * *********************************************************************/ void ses_set_xml_nons (ses_cb_t *scb) { scb->noxmlns = TRUE; } /* ses_set_xml_nons */ /******************************************************************** * FUNCTION ses_get_xml_nons * * force xmlns attributes to be skipped in XML mode * * INPUTS: * scb == session to get * * RETURNS: * TRUE if no xmlns attributes set * FALSE if OK to use xmlns attributes *********************************************************************/ boolean ses_get_xml_nons (const ses_cb_t *scb) { return scb->noxmlns; } /* ses_get_xml_nons */ /******************************************************************** * FUNCTION ses_set_protocol * * set the NETCONF protocol version in use * * INPUTS: * scb == session to set * proto == protocol to set * RETURNS: * status *********************************************************************/ status_t ses_set_protocol (ses_cb_t *scb, ncx_protocol_t proto) { #ifdef DEBUG if (scb == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (proto == NCX_PROTO_NONE) { return ERR_NCX_INVALID_VALUE; } if (scb->protocol != NCX_PROTO_NONE) { return ERR_NCX_DUP_ENTRY; } scb->protocol = proto; if (scb->transport == SES_TRANSPORT_SSH && proto == NCX_PROTO_NETCONF11) { scb->framing11 = TRUE; } if (scb->outbuff != NULL && scb->framing11) { if (scb->outbuff->bufflen != 0) { SET_ERROR(ERR_INTERNAL_VAL); } ses_msg_init_buff(scb, TRUE, scb->outbuff); } return NO_ERR; } /* ses_set_protocol */ /******************************************************************** * FUNCTION ses_get_protocol * * Get the NETCONF protocol set (or unset) for this session * * INPUTS: * scb == session to get * * RETURNS: * protocol enumeration in use *********************************************************************/ ncx_protocol_t ses_get_protocol (const ses_cb_t *scb) { #ifdef DEBUG if (scb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_PROTO_NONE; } #endif return scb->protocol; } /* ses_get_protocol */ /******************************************************************** * FUNCTION ses_set_protocols_requested * * set the NETCONF protocol versions requested * * INPUTS: * scb == session to set * proto == protocol to set * RETURNS: * status *********************************************************************/ void ses_set_protocols_requested (ses_cb_t *scb, ncx_protocol_t proto) { #ifdef DEBUG if (scb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif switch (proto) { case NCX_PROTO_NETCONF10: scb->protocols_requested |= NCX_FL_PROTO_NETCONF10; break; case NCX_PROTO_NETCONF11: scb->protocols_requested |= NCX_FL_PROTO_NETCONF11; break; default: SET_ERROR(ERR_INTERNAL_VAL); } } /* ses_set_protocols_requested */ /******************************************************************** * FUNCTION ses_protocol_requested * * check if the NETCONF protocol version was requested * * INPUTS: * scb == session to check * proto == protocol to check * RETURNS: * TRUE is requested; FALSE otherwise *********************************************************************/ boolean ses_protocol_requested (ses_cb_t *scb, ncx_protocol_t proto) { boolean ret = FALSE; #ifdef DEBUG if (scb == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif switch (proto) { case NCX_PROTO_NETCONF10: if (scb->protocols_requested & NCX_FL_PROTO_NETCONF10) { ret = TRUE; } break; case NCX_PROTO_NETCONF11: if (scb->protocols_requested & NCX_FL_PROTO_NETCONF11) { ret = TRUE; } break; default: SET_ERROR(ERR_INTERNAL_VAL); } return ret; } /* ses_protocol_requested */ /* END file ses.c */ yuma123_2.14/netconf/src/ncx/plock.h0000664000175000017500000001371014770023131017421 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_plock #define _H_plock /* FILE: plock.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* RFC 57517 partial lock support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 21-jun-10 abb Begun. */ #ifndef _H_plock_cb #include "plock_cb.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION plock_get_id * * Get the lock ID for this partial lock * * INPUTS: * plcb == partial lock control block to use * * RETURNS: * the lock ID for this lock *********************************************************************/ extern plock_id_t plock_get_id (plock_cb_t *plcb); /******************************************************************** * FUNCTION plock_get_sid * * Get the session ID holding this partial lock * * INPUTS: * plcb == partial lock control block to use * * RETURNS: * session ID that owns this lock *********************************************************************/ extern uint32 plock_get_sid (plock_cb_t *plcb); /******************************************************************** * FUNCTION plock_get_timestamp * * Get the timestamp of the lock start time * * INPUTS: * plcb == partial lock control block to use * * RETURNS: * timestamp in date-time format *********************************************************************/ extern const xmlChar * plock_get_timestamp (plock_cb_t *plcb); /******************************************************************** * FUNCTION plock_get_final_result * * Get the session ID holding this partial lock * * INPUTS: * plcb == partial lock control block to use * * RETURNS: * session ID that owns this lock *********************************************************************/ extern xpath_result_t * plock_get_final_result (plock_cb_t *plcb); /******************************************************************** * FUNCTION plock_get_first_select * * Get the first select XPath control block for the partial lock * * INPUTS: * plcb == partial lock control block to use * * RETURNS: * pointer to first xpath_pcb_t for the lock *********************************************************************/ extern xpath_pcb_t * plock_get_first_select (plock_cb_t *plcb); /******************************************************************** * FUNCTION plock_get_next_select * * Get the next select XPath control block for the partial lock * * INPUTS: * xpathpcb == current select block to use * * RETURNS: * pointer to first xpath_pcb_t for the lock *********************************************************************/ extern xpath_pcb_t * plock_get_next_select (xpath_pcb_t *xpathpcb); /******************************************************************** * FUNCTION plock_add_select * * Add a select XPath control block to the partial lock * * INPUTS: * plcb == partial lock control block to use * xpathpcb == xpath select block to add * result == result struct to add * *********************************************************************/ extern void plock_add_select (plock_cb_t *plcb, xpath_pcb_t *xpathpcb, xpath_result_t *result); /******************************************************************** * FUNCTION plock_make_final_result * * Create a final XPath result for all the partial results * * This does not add the partial lock to the target config! * This is an intermediate step! * * INPUTS: * plcb == partial lock control block to use * * RETURNS: * status; NCX_ERR_INVALID_VALUE if the final nodeset is empty *********************************************************************/ extern status_t plock_make_final_result (plock_cb_t *plcb); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_plock */ yuma123_2.14/netconf/src/ncx/rpc.c0000664000175000017500000001163214770023131017071 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: rpc.c NETCONF RPC Operations ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 09nov05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "cfg.h" #include "ncx.h" #include "obj.h" #include "op.h" #include "rpc.h" #include "rpc_err.h" #include "xmlns.h" #include "xml_msg.h" #include "xml_util.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************* E X T E R N F U N C T I O N S ***************/ /******************************************************************** * FUNCTION rpc_new_msg * * Malloc and initialize a new rpc_msg_t struct * * INPUTS: * none * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ rpc_msg_t * rpc_new_msg (void) { rpc_msg_t *msg; msg = m__getObj(rpc_msg_t); if (!msg) { return NULL; } memset(msg, 0x0, sizeof(rpc_msg_t)); xml_msg_init_hdr(&msg->mhdr); dlq_createSQue(&msg->rpc_dataQ); msg->rpc_input = val_new_value(); if (!msg->rpc_input) { rpc_free_msg(msg); return NULL; } msg->rpc_top_editop = OP_EDITOP_MERGE; return msg; } /* rpc_new_msg */ /******************************************************************** * FUNCTION rpc_new_out_msg * * Malloc and initialize a new rpc_msg_t struct for output * or for dummy use * * INPUTS: * none * RETURNS: * pointer to struct or NULL or memory error *********************************************************************/ rpc_msg_t * rpc_new_out_msg (void) { rpc_msg_t *msg; msg = rpc_new_msg(); if (!msg) { return NULL; } msg->rpc_in_attrs = NULL; return msg; } /* rpc_new_out_msg */ /******************************************************************** * FUNCTION rpc_free_msg * * Free all the memory used by the specified rpc_msg_t * * INPUTS: * msg == rpc_msg_t to clean and delete * RETURNS: * none *********************************************************************/ void rpc_free_msg (rpc_msg_t *msg) { if (!msg) { return; } xml_msg_clean_hdr(&msg->mhdr); //msg->rpc_in_attrs = NULL; //msg->rpc_method = NULL; //msg->rpc_agt_state = 0; /* clean input parameter set */ if (msg->rpc_input) { val_free_value(msg->rpc_input); } //msg->rpc_user1 = NULL; //msg->rpc_user2 = NULL; //msg->rpc_filter.op_filtyp = OP_FILTER_NONE; //msg->rpc_filter.op_filter = NULL; //msg->rpc_datacb = NULL; /* clean data queue */ while (!dlq_empty(&msg->rpc_dataQ)) { val_value_t *val = (val_value_t *)dlq_deque(&msg->rpc_dataQ); val_free_value(val); } m__free(msg); } /* rpc_free_msg */ /* END file rpc.c */ yuma123_2.14/netconf/src/ncx/grp.h0000664000175000017500000001137314770023131017104 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_grp #define _H_grp /* FILE: grp.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Grouping Handler ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 09-dec-07 abb Begun */ #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* One YANG 'grouping' definition -- sibling set template */ typedef struct grp_template_t_ { dlq_hdr_t qhdr; xmlChar *name; xmlChar *descr; xmlChar *ref; ncx_error_t tkerr; void *parent; /* const back-ptr to parent obj */ struct grp_template_t_ *parentgrp; /* direct parent is grp */ xmlns_id_t nsid; boolean used; boolean istop; boolean expand_done; ncx_status_t status; uint32 grpindex; /* used for XSD generation */ dlq_hdr_t typedefQ; /* Q of typ_template_t */ dlq_hdr_t groupingQ; /* Q of grp_template_t */ dlq_hdr_t datadefQ; /* Q of obj_template_t */ dlq_hdr_t appinfoQ; /* Q of ncx_appinfo_t */ } grp_template_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION grp_new_template * * Malloc and initialize the fields in a grp_template_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern grp_template_t * grp_new_template (void); /******************************************************************** * FUNCTION grp_free_template * * Scrub the memory in a grp_template_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * grp == grp_template_t data structure to free *********************************************************************/ extern void grp_free_template (grp_template_t *grp); /******************************************************************** * FUNCTION grp_clean_groupingQ * * Clean a queue of grp_template_t structs * * INPUTS: * que == Q of grp_template_t data structures to free *********************************************************************/ extern void grp_clean_groupingQ (dlq_hdr_t *que); /******************************************************************** * FUNCTION grp_has_typedefs * * Check if the grouping contains any typedefs * * INPUTS: * grp == grp_template_t struct to check * * RETURNS: * TRUE if any embedded typedefs * FALSE if no embedded typedefs *********************************************************************/ extern boolean grp_has_typedefs (const grp_template_t *grp); /******************************************************************** * FUNCTION grp_get_mod_name * * Get the module name for a grouping * * INPUTS: * grp == grp_template_t struct to check * * RETURNS: * const pointer to module name *********************************************************************/ extern const xmlChar * grp_get_mod_name (const grp_template_t *grp); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_grp */ yuma123_2.14/netconf/src/ncx/ses.h0000664000175000017500000006543314770023131017114 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ses #define _H_ses /* FILE: ses.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NETCONF Session Common definitions module ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30-dec-05 abb Begun. */ /* used by applications to generate FILE output */ #include /* used for timestamps and time deltas */ #include #ifdef LIBXML2_ENABLED /* used by the agent for the xmlTextReader interface */ #include #else typedef void* xmlTextReaderPtr; #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tstamp #include "tstamp.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define SES_MY_SID(S) ((S)->sid) #define SES_MY_USERNAME(S) ((S)->username) #define SES_KILLREQ_SET(S) ((S)->state >= SES_ST_SHUTDOWN_REQ) #define SES_ACK_KILLREQ(S) ((S)->state = SES_ST_SHUTDOWN) #define SES_OUT_BYTES(S) (S)->stats.out_bytes #define SES_LINELEN(S) (S)->stats.out_line #define SES_LINESIZE(S) (S)->linesize #define SES_NULL_SID 0 /* controls the size of each buffer chuck */ #define SES_MSG_BUFFSIZE 2000 // 1024 /* max number of buffer chunks a session can have allocated at once */ #define SES_MAX_BUFFERS 4096 /* max number of buffers a session is allowed to cache in its freeQ */ #define SES_MAX_FREE_BUFFERS 32 /* max number of buffers to try to send in one call to the write fn */ #define SES_MAX_BUFFSEND 32 /* max number of bytes to try to send in one call to the write_fn */ #define SES_MAX_BYTESEND 0xffff /* max desired lines size; not a hard limit */ #define SES_DEF_LINESIZE 72 /* max size of a valid base:1.1 chunk header start tag */ #define SES_MAX_STARTCHUNK_SIZE 13 /* max size of the chunk size number in the chunk start tag */ #define SES_MAX_CHUNKNUM_SIZE 10 /* padding at start of buffer for chunk tagging * Max: \n#xxxxxxx\n --> 7 digit chunk size */ #define SES_STARTCHUNK_PAD 10 /* leave enough room at the end for EOChunks */ #define SES_ENDCHUNK_PAD 4 /* default read buffer size */ #define SES_READBUFF_SIZE 1000 /* port number for NETCONF over TCP */ #define SES_DEF_TCP_PORT 2023 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* Session ID */ typedef uint32 ses_id_t; /* Session Types */ typedef enum ses_type_t_ { SES_TYP_NONE, SES_TYP_NETCONF, SES_TYP_NCX, SES_TYP_DUMMY } ses_type_t; /* NETCONF Transport Types */ typedef enum ses_transport_t_ { SES_TRANSPORT_NONE, SES_TRANSPORT_SSH, /* only enum supported */ SES_TRANSPORT_BEEP, SES_TRANSPORT_SOAP, SES_TRANSPORT_SOAPBEEP, SES_TRANSPORT_TLS, SES_TRANSPORT_TCP, /* tail-f NETCONF over TCP */ SES_TRANSPORT_TCP_DIRECT } ses_transport_t; /* Session States */ typedef enum ses_state_t_ { SES_ST_NONE, SES_ST_INIT, SES_ST_HELLO_WAIT, SES_ST_IDLE, SES_ST_IN_MSG, SES_ST_SHUTDOWN_REQ, SES_ST_SHUTDOWN } ses_state_t; /* Session Input Handler States */ typedef enum ses_instate_t_ { SES_INST_NONE, SES_INST_IDLE, SES_INST_INMSG, SES_INST_INSTART, SES_INST_INBETWEEN, SES_INST_INEND } ses_instate_t; /* Session Output Mode */ typedef enum ses_mode_t_ { SES_MODE_NONE, SES_MODE_XML, SES_MODE_XMLDOC, SES_MODE_HTML, SES_MODE_TEXT } ses_mode_t; /* Session Termination reason */ typedef enum ses_term_reason_t_ { SES_TR_NONE, SES_TR_CLOSED, SES_TR_KILLED, SES_TR_DROPPED, SES_TR_TIMEOUT, SES_TR_OTHER, SES_TR_BAD_START, SES_TR_BAD_HELLO } ses_term_reason_t; /* prolog parsing state */ typedef enum ses_prolog_state_t_ { SES_PRST_NONE, SES_PRST_WAITING, SES_PRST_DONE } ses_prolog_state_t; /*** using uint32 instead of uint64 because the netconf-state *** data model is specified that way ***/ /* Per Session Statistics */ typedef struct ses_stats_t_ { /* extra original internal byte counters */ uint32 in_bytes; uint32 out_bytes; /* hack: bytes since '\n', pretty-print */ uint32 out_line; /* netconf-state counters */ uint32 inRpcs; uint32 inBadRpcs; uint32 outRpcErrors; uint32 outNotifications; } ses_stats_t; /* Session Total Statistics */ typedef struct ses_total_stats_t_ { uint32 active_sessions; uint32 closed_sessions; uint32 failed_sessions; uint32 inBadHellos; uint32 inSessions; uint32 droppedSessions; ses_stats_t stats; xmlChar startTime[TSTAMP_MIN_SIZE]; } ses_total_stats_t; /* Session Message Buffer */ typedef struct ses_msg_buff_t_ { dlq_hdr_t qhdr; size_t buffstart; /* buff start pos */ size_t bufflen; /* buff actual size */ size_t buffpos; /* buff cur position */ boolean islast; /* T: last buff in msg */ xmlChar buff[SES_MSG_BUFFSIZE]; } ses_msg_buff_t; /* embedded Q header for the message ready Q */ typedef struct ses_ready_t_ { dlq_hdr_t hdr; ses_id_t sid; boolean inq; } ses_ready_t; /* Session Message */ typedef struct ses_msg_t_ { dlq_hdr_t qhdr; /* Q header for buffcb->msgQ */ boolean ready; /* ready for parsing */ ses_msg_buff_t *curbuff; /* cur position in buffQ */ dlq_hdr_t buffQ; /* Q of ses_msg_buff_t */ ses_prolog_state_t prolog_state; /* for insert prolog */ size_t curchunksize; /* cur chunk rcvd */ size_t expchunksize; /* expected chunk size */ } ses_msg_t; /* optional read function for the session */ typedef ssize_t (*ses_read_fn_t) (void *s, char *buff, size_t bufflen, boolean *erragain); /* optional write function for the session */ typedef status_t (*ses_write_fn_t) (void *s); /* Session Control Block */ typedef struct ses_cb_t_ { dlq_hdr_t qhdr; /* queued by manager only */ ses_type_t type; /* session type */ uint32 protocols_requested; /* bitmask */ ncx_protocol_t protocol; /* protocol version in use */ ses_transport_t transport; /* transport type */ ses_state_t state; /* session state */ ses_mode_t mode; /* session mode */ ses_id_t sid; /* session ID */ ses_id_t killedbysid; /* killed-by session ID */ ses_id_t rollback_sid; /* session ID for rollback */ ses_term_reason_t termreason; time_t hello_time; /* used for hello timeout */ time_t last_rpc_time; /* used for idle timeout */ xmlChar *start_time; /* dateTime start time */ xmlChar *username; /* user ID */ xmlChar *peeraddr; /* Inet address string */ boolean active; /* completed ok */ boolean notif_active; /* subscription active */ boolean stream_output; /* buffer/stream svr */ boolean noxmlns; /* xml-nons display-mode */ boolean framing11; /* T: base:1.1, F: base:1.0 */ xmlTextReaderPtr reader; /* input stream reader */ FILE *fp; /* set if output to a file */ int fd; /* set if output to a socket */ ses_read_fn_t rdfn; /* set if external write fn */ ses_write_fn_t wrfn; /* set if external read fn */ uint32 inendpos; /* inside framing directive */ ses_instate_t instate; /* input state enum */ uint32 buffcnt; /* current buffer count */ uint32 freecnt; /* current freeQ count */ dlq_hdr_t msgQ; /* Q of ses_msg_t input */ dlq_hdr_t freeQ; /* Q of ses_msg_buff_t */ dlq_hdr_t outQ; /* Q of ses_msg_buff_t */ ses_msg_buff_t *outbuff; /* current output buffer */ ses_ready_t inready; /* header for inreadyQ */ ses_ready_t outready; /* header for outreadyQ */ ses_stats_t stats; /* per-session statistics */ void *mgrcb; /* if manager session, mgr_scb_t */ uint32 indefer_len; /* pending defered input data */ /* base:1.1 chunk state handling; * need to store number part of incoming chunk markers * in the scb in case they are split across buffers */ xmlChar startchunk[SES_MAX_STARTCHUNK_SIZE+1]; /* input buffer for session */ xmlChar *readbuff; uint32 readbuffsize; /*** user preferences ***/ int32 indent; /* indent N spaces (0..9) */ uint32 linesize; /* TERM line length */ ncx_withdefaults_t withdef; /* with-defaults default */ uint32 cache_timeout; /* vir-val cache tmr in sec */ /* agent access control for database reads and writes; * for incoming agent requests, the access control * cache is used to minimize data structure processing * during authorization procedures in agt/agt_acm.c * there is a back-ptr embedded in the XML header so it can * be easily passed to the agt_val and xml_wr functions */ struct agt_acm_cache_t_ *acm_cache; /* session dump files */ FILE* dump_output_data; FILE* dump_output_timestamps; FILE* dump_input_data; FILE* dump_input_timestamps; } ses_cb_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION ses_new_scb * * Create a new session control block * * INPUTS: * none * RETURNS: * pointer to initialized SCB, or NULL if malloc error *********************************************************************/ extern ses_cb_t * ses_new_scb (void); /******************************************************************** * FUNCTION ses_new_dummy_scb * * Create a new dummy session control block * * INPUTS: * none * RETURNS: * pointer to initialized SCB, or NULL if malloc error *********************************************************************/ extern ses_cb_t * ses_new_dummy_scb (void); /******************************************************************** * FUNCTION ses_free_scb * * Free a session control block * * INPUTS: * scb == session control block to free * RETURNS: * none *********************************************************************/ extern void ses_free_scb (ses_cb_t *scb); /******************************************************************** * FUNCTION ses_putchar * * Write one char to the session, without any translation * * THIS FUNCTION DOES NOT CHECK ANY PARAMETERS TO SAVE TIME * * NO CHARS ARE ACTUALLY WRITTEN TO A REAL SESSION!!! * The 'output ready' indicator will be set and the session * queued in the outreadyQ. Non-blocking IO functions * will send the data when the connection allows. * * INPUTS: * scb == session control block to start msg * ch = xmlChar to write, cast as uint32 to avoid compiler warnings * *********************************************************************/ extern void ses_putchar (ses_cb_t *scb, uint32 ch); /******************************************************************** * FUNCTION ses_putstr * * Write a zero-terminated string to the session * * THIS FUNCTION DOES NOT CHECK ANY PARAMTERS TO SAVE TIME * * INPUTS: * scb == session control block to start msg * str == string to write * *********************************************************************/ extern void ses_putstr (ses_cb_t *scb, const xmlChar *str); /******************************************************************** * FUNCTION ses_putstr_indent * * Write a zero-terminated content string to the session * with indentation * * THIS FUNCTION DOES NOT CHECK ANY PARAMTERS TO SAVE TIME * EXCEPT THAT ILLEGAL XML CHARS ARE CONVERTED TO CHAR ENTITIES * * INPUTS: * scb == session control block to start msg * str == string to write * indent == current indent amount * *********************************************************************/ extern void ses_putstr_indent (ses_cb_t *scb, const xmlChar *str, int32 indent); /******************************************************************** * FUNCTION ses_putcstr * * write XML element safe content string * Write a zero-terminated element content string to the session * * THIS FUNCTION DOES NOT CHECK ANY PARAMTERS TO SAVE TIME * EXCEPT THAT ILLEGAL XML CHARS ARE CONVERTED TO CHAR ENTITIES * * INPUTS: * scb == session control block to start msg * str == string to write * indent == current indent amount * *********************************************************************/ extern void ses_putcstr (ses_cb_t *scb, const xmlChar *str, int32 indent); /******************************************************************** * FUNCTION ses_puthstr * * write HTML element safe content string * Write a zero-terminated element content string to the session * * THIS FUNCTION DOES NOT CHECK ANY PARAMTERS TO SAVE TIME * EXCEPT THAT ILLEGAL XML CHARS ARE CONVERTED TO CHAR ENTITIES * * INPUTS: * scb == session control block to start msg * str == string to write * *********************************************************************/ extern void ses_puthstr (ses_cb_t *scb, const xmlChar *str); /******************************************************************** * FUNCTION ses_putcchar * * Write one content char to the session, with translation as needed * * THIS FUNCTION DOES NOT CHECK ANY PARAMETERS TO SAVE TIME * * NO CHARS ARE ACTUALLY WRITTEN TO A REAL SESSION!!! * The 'output ready' indicator will be set and the session * queued in the outreadyQ. Non-blocking IO functions * will send the data when the connection allows. * * INPUTS: * scb == session control block to write * ch = xmlChar to write, cast as uint32 to avoid compiler warnings * *********************************************************************/ extern void ses_putcchar (ses_cb_t *scb, uint32 ch); /******************************************************************** * FUNCTION ses_putastr * * write XML attribute safe content string * Write a zero-terminated attribute content string to the session * * THIS FUNCTION DOES NOT CHECK ANY PARAMTERS TO SAVE TIME * EXCEPT THAT ILLEGAL XML CHARS ARE CONVERTED TO CHAR ENTITIES * * INPUTS: * scb == session control block to start msg * str == string to write * indent == current indent amount * *********************************************************************/ extern void ses_putastr (ses_cb_t *scb, const xmlChar *str, int32 indent); /******************************************************************** * FUNCTION ses_putjstr * * write JSON safe content string * Write a zero-terminated element content string to the session * * THIS FUNCTION DOES NOT CHECK ANY PARAMTERS TO SAVE TIME * EXCEPT THAT ILLEGAL JSON CHARS ARE CONVERTED TO ESCAPED CHARS * * INPUTS: * scb == session control block to start msg * str == string to write * indent == current indent amount * *********************************************************************/ extern void ses_putjstr (ses_cb_t *scb, const xmlChar *str, int32 indent); /******************************************************************** * FUNCTION ses_indent * * Write the proper newline + indentation to the specified session * * THIS FUNCTION DOES NOT CHECK ANY PARAMETERS TO SAVE TIME * * INPUTS: * scb == session control block to start msg * indent == number of chars to indent after a newline * will be ignored if indent is turned off * in the agent profile * == -1 means no newline or indent * == 0 means just newline * *********************************************************************/ extern void ses_indent (ses_cb_t *scb, int32 indent); /******************************************************************** * FUNCTION ses_indent_count * * Get the indent count for this session * * THIS FUNCTION DOES NOT CHECK ANY PARAMETERS TO SAVE TIME * * INPUTS: * scb == session control block to check * * RETURNS: * indent value for the session *********************************************************************/ extern int32 ses_indent_count (const ses_cb_t *scb); /******************************************************************** * FUNCTION ses_set_indent * * Set the indent count for this session * * INPUTS: * scb == session control block to check * indent == value to use (may get adjusted) * *********************************************************************/ extern void ses_set_indent (ses_cb_t *scb, int32 indent); /******************************************************************** * FUNCTION ses_set_mode * * Set the output mode for the specified session * * INPUTS: * scb == session control block to set * mode == new mode value * RETURNS: * none *********************************************************************/ extern void ses_set_mode (ses_cb_t *scb, ses_mode_t mode); /******************************************************************** * FUNCTION ses_get_mode * * Get the output mode for the specified session * * INPUTS: * scb == session control block to get * * RETURNS: * session mode value *********************************************************************/ extern ses_mode_t ses_get_mode (ses_cb_t *scb); /******************************************************************** * FUNCTION ses_start_msg * * Start a new outbound message on the specified session * * INPUTS: * scb == session control block to start msg * * RETURNS: * status *********************************************************************/ extern status_t ses_start_msg (ses_cb_t *scb); /******************************************************************** * FUNCTION ses_finish_msg * * Finish an outbound message on the specified session * * INPUTS: * scb == session control block to finish msg * RETURNS: * none *********************************************************************/ extern void ses_finish_msg (ses_cb_t *scb); /******************************************************************** * FUNCTION ses_read_cb * * The IO input front-end for the xmlTextReader parser read fn * * Need to separate the input stream into separate XML instance * documents and reset the xmlTextReader each time a new document * is encountered. For SSH, also need to detect the EOM flag * and remove it + control input to the reader. * * Uses a complex state machine which does not assume that the * input from the network is going to arrive in well-formed chunks. * It has to be treated as a byte stream (SOCK_STREAM). * * Does not remove char entities or any XML, just the SSH EOM directive * * INPUTS: * context == scb pointer for the session to read * buffer == char buffer to fill * len == length of the buffer * * RETURNS: * number of bytes read into the buffer * -1 indicates error and EOF *********************************************************************/ extern int ses_read_cb (void *context, char *buffer, int len); /******************************************************************** * FUNCTION ses_accept_input * * The IO input handler for the ncxserver loop * * Need to separate the input stream into separate XML instance * documents and reset the xmlTextReader each time a new document * is encountered. For SSH, also need to detect the EOM flag * and remove it + control input to the reader. * * This function breaks the byte stream into ses_msg_t structs * that get queued on the session's msgQ * * INPUTS: * scb == session control block to accept input for * * RETURNS: * status *********************************************************************/ extern status_t ses_accept_input (ses_cb_t *scb); /******************************************************************** * FUNCTION ses_state_name * * Get the name of a session state from the enum value * * INPUTS: * state == session state enum value * * RETURNS: * staing corresponding to the state name *********************************************************************/ extern const xmlChar * ses_state_name (ses_state_t state); /******************************************************************** * FUNCTION ses_withdef * * Get the with-defaults value for this session * * INPUTS: * scb == session control block to check * * RETURNS: * with-defaults value for the session *********************************************************************/ extern ncx_withdefaults_t ses_withdef (const ses_cb_t *scb); /******************************************************************** * FUNCTION ses_line_left * * Get the number of bytes that can be added to the current line * before the session linesize limit is reached * * INPUTS: * scb == session control block to check * * RETURNS: * number of bytes left, or zero if limit already reached *********************************************************************/ extern uint32 ses_line_left (const ses_cb_t *scb); /******************************************************************** * FUNCTION ses_put_extern * * write the contents of a file to the session * * INPUTS: * scb == session to write * fspec == filespec to write * *********************************************************************/ extern void ses_put_extern (ses_cb_t *scb, const xmlChar *fname); /******************************************************************** * FUNCTION ses_get_total_stats * * Get a r/w pointer to the the session totals stats * * RETURNS: * pointer to the global session stats struct *********************************************************************/ extern ses_total_stats_t * ses_get_total_stats (void); /******************************************************************** * FUNCTION ses_get_transport_name * * Get the name of the transport for a given enum value * * INPUTS: * transport == ses_transport_t enum value * * RETURNS: * pointer to the string value for the specified enum *********************************************************************/ extern const xmlChar * ses_get_transport_name (ses_transport_t transport); /******************************************************************** * FUNCTION ses_set_xml_nons * * force xmlns attributes to be skipped in XML mode * * INPUTS: * scb == session to set * *********************************************************************/ extern void ses_set_xml_nons (ses_cb_t *scb); /******************************************************************** * FUNCTION ses_get_xml_nons * * force xmlns attributes to be skipped in XML mode * * INPUTS: * scb == session to get * * RETURNS: * TRUE if no xmlns attributes set * FALSE if OK to use xmlns attributes *********************************************************************/ extern boolean ses_get_xml_nons (const ses_cb_t *scb); /******************************************************************** * FUNCTION ses_set_protocol * * set the NETCONF protocol version in use * * INPUTS: * scb == session to set * proto == protocol to set * RETURNS: * status *********************************************************************/ extern status_t ses_set_protocol (ses_cb_t *scb, ncx_protocol_t proto); /******************************************************************** * FUNCTION ses_get_protocol * * Get the NETCONF protocol set (or unset) for this session * * INPUTS: * scb == session to get * * RETURNS: * protocol enumeration in use *********************************************************************/ extern ncx_protocol_t ses_get_protocol (const ses_cb_t *scb); /******************************************************************** * FUNCTION ses_set_protocols_requested * * set the NETCONF protocol versions requested * * INPUTS: * scb == session to set * proto == protocol to set * RETURNS: * status *********************************************************************/ extern void ses_set_protocols_requested (ses_cb_t *scb, ncx_protocol_t proto); /******************************************************************** * FUNCTION ses_protocol_requested * * check if the NETCONF protocol version was requested * * INPUTS: * scb == session to check * proto == protocol to check * RETURNS: * TRUE is requested; FALSE otherwise *********************************************************************/ extern boolean ses_protocol_requested (ses_cb_t *scb, ncx_protocol_t proto); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_ses */ yuma123_2.14/netconf/src/ncx/b64.h0000664000175000017500000000711614770023131016707 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_b64 #define _H_b64 /* FILE: b64.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* RFC 4648 base64 support, from b64.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 25-oct-08 abb Begun */ #include "procdefs.h" #include "status.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /** * base64 encode a stream adding padding and line breaks as per spec. * * \param inbuff pointer to buffer of binary chars * \param inbufflen number of binary chars in inbuff * \param outbuff pointer to the output buffer to use. * \param outbufflen max number of chars to write to outbuff * \param linesize the output line length to use or 0 for no wrapping * \param retlen address of return length * \returns NO_ERR if all OK, ERR_BUFF_OVFL if outbuff not big enough */ status_t b64_encode ( const unsigned char *inbuff, unsigned int inbufflen, unsigned char *outbuff, unsigned int outbufflen, unsigned int linesize, unsigned int *retlen ); /** * Decode a base64 string. * This function decodes the supplied base 64 string. It has the * following constraints: * 1 - The length of the supplied base64 string must be divisible by 4 * (any other length strings are NOT valid base 64) * 2 - If any non base64 characters are encountered the length of * decoded string will be truncated. * 3 - CR and LF characters in the encoded string will be skipped. * * \param inbuff pointer to buffer of base64 chars * \param inbufflen number of chars in inbuff * \param outbuff pointer to the output buffer to use * \param outbufflen the length of outbuff. * \param retlen the number of decoded bytes * \return NO_ERR if all OK * ERR_BUFF_OVFL if outbuff not big enough */ status_t b64_decode ( const uint8_t* inbuff, uint32_t inbufflen, uint8_t* outbuff, uint32_t outbufflen, uint32_t* retlen ); /** * Calculate the length of the buffer required to decode the * base64 string. * * @param inbuff the base64 string to decode. * @param inputlen the length of inbuff. * @return the length of the required buffer. */ uint32_t b64_get_decoded_str_len( const uint8_t* inbuff, size_t inputlen ); /** * Get the output buffer size required for encoding the string. * * \param inbufflen the size of the input buffer. * \param linesize the length of each line * \return the size required for encoding the string. */ uint32_t b64_get_encoded_str_len( uint32_t inbufflen, uint32_t linesize ); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_b64 */ yuma123_2.14/netconf/src/ncx/xmlns.c0000664000175000017500000006245014770023131017452 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xmlns.c ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 30apr05 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_def_reg #include "def_reg.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_yangconst #include "yangconst.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* max registered namespaces/modules in the database * increase to allow more modules loaded at once * (this is an arbitrary limit) */ #define XMLNS_MAX_NS 4096 #ifdef DEBUG #define XMLNS_DEBUG 1 #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /* INVALID namespace ID */ static xmlns_id_t xmlns_invid; /* NETCONF namespace ID */ static xmlns_id_t xmlns_ncid; /* XMLNS namespace ID */ static xmlns_id_t xmlns_nsid; /* XSD namespace ID */ static xmlns_id_t xmlns_xsid; /* NETCONF Extensions namespace ID */ static xmlns_id_t xmlns_ncxid; /* XSD Instance (XSI) namespace ID */ static xmlns_id_t xmlns_xsiid; /* 1998 XML Namespace ID */ static xmlns_id_t xmlns_xmlid; /* NETCONF Notifications namespace ID */ static xmlns_id_t xmlns_ncnid; /* YANG namespace ID */ static xmlns_id_t xmlns_yangid; /* YIN namespace ID */ static xmlns_id_t xmlns_yinid; /* Wildcard namespace ID */ static xmlns_id_t xmlns_wildcardid; /* with-defaults wd:default attribute namespace ID */ static xmlns_id_t xmlns_wdaid; /* next ID to allocate */ static xmlns_id_t xmlns_next_id; /* array of xmlns_t pointers */ static xmlns_t *xmlns[XMLNS_MAX_NS]; /* module init done flag */ static boolean xmlns_init_done = FALSE; /******************************************************************** * * * M A C R O S * * * *********************************************************************/ /* MACRO valid_id * * check if the ns_id (I) indicates a valid entry * * INPUTS: * I == NS ID to check * RETURN * zero if not valid, non-zero if valid */ #define valid_id(I) ((I)&&((I)<=XMLNS_MAX_NS)&&(xmlns[(I)-1]) && \ (xmlns[(I)-1]->ns_id==(I))) /******************************************************************** * FUNCTION new_xmlns * * Malloc and init an xmlns_t struct * * RETURNS: * pointer to new struct, or NULL if memorty error *********************************************************************/ static xmlns_t * new_xmlns (void) { xmlns_t *rec; rec = m__getObj(xmlns_t); if (!rec) { return NULL; } memset(rec, 0x0, sizeof(xmlns_t)); return rec; } /* new_xmlns */ /******************************************************************** * FUNCTION free_xmlns * * Free an xmlns_t struct * * INPUTS: * rec == xmlns_t struct to free *********************************************************************/ static void free_xmlns (xmlns_t *rec) { if (rec->ns_pfix) { m__free(rec->ns_pfix); } if (rec->ns_name) { m__free(rec->ns_name); } if (rec->ns_module) { m__free(rec->ns_module); } m__free(rec); } /* free_xmlns */ /******************************************************************** * FUNCTION init_xmlns_static_vars * * Initialze the module static variables * * INPUTS: * none * RETURNS: * none *********************************************************************/ static void init_xmlns_static_vars (void) { uint32 i; for (i=0; i < XMLNS_MAX_NS; i++) { xmlns[i] = NULL; } xmlns_invid = 0; xmlns_ncid = 0; xmlns_nsid = 0; xmlns_xsid = 0; xmlns_ncxid = 0; xmlns_xsiid = 0; xmlns_xmlid = 0; xmlns_ncnid = 0; xmlns_yangid = 0; xmlns_yinid = 0; xmlns_wildcardid = 0; xmlns_wdaid = 0; xmlns_next_id = 1; xmlns_init_done = FALSE; } /* init_xmlns_static_vars */ /******************************************************************** * * * E X P O R T E D F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xmlns_init * * Initialize the module static variables * * INPUTS: * none * RETURNS: * none *********************************************************************/ void xmlns_init (void) { if (!xmlns_init_done) { init_xmlns_static_vars(); xmlns_init_done = TRUE; } } /* xmlns_init */ /******************************************************************** * FUNCTION xmlns_cleanup * * Cleanup module static data * * INPUTS: * none * RETURNS: * none *********************************************************************/ void xmlns_cleanup (void) { uint32 i; if (xmlns_init_done) { for (i=0; i XMLNS_MAX_NS) { return ERR_TOO_MANY_ENTRIES; } /* check duplicate entry */ if (xmlns_find_ns_by_name(ns)) { return ERR_DUP_NS; } /* all ok - try to malloc the new entry at the end of the list */ rec = new_xmlns(); if (!rec) { return ERR_INTERNAL_MEM; } /* copy the prefix string */ if (pfix) { rec->ns_pfix = xml_strdup(pfix); if (!rec->ns_pfix) { free_xmlns(rec); return ERR_INTERNAL_MEM; } } /* copy the name namespace URI string */ rec->ns_name = xml_strdup(ns); if (!rec->ns_name) { free_xmlns(rec); return ERR_INTERNAL_MEM; } /* copy the name namespace URI string */ rec->ns_module = xml_strdup(modname); if (!rec->ns_module) { free_xmlns(rec); return ERR_INTERNAL_MEM; } /* copy the module back-ptr */ rec->ns_mod = (ncx_module_t *)modptr; /* assign the next XMLNS ID */ rec->ns_id = xmlns_next_id; /* register the entry in the def_reg hash table */ res = def_reg_add_ns(rec); if (res != NO_ERR) { free_xmlns(rec); return res; } xmlns[xmlns_next_id-1] = rec; /* hack: check if this is one of the cached NS IDs */ if (!xml_strcmp(ns, NC_URN)) { xmlns_ncid = xmlns_next_id; } else if (!xml_strcmp(ns, NCX_URN)) { xmlns_ncxid = xmlns_next_id; } else if (!xml_strcmp(ns, NS_URN)) { xmlns_nsid = xmlns_next_id; } else if (!xml_strcmp(ns, XSD_URN)) { xmlns_xsid = xmlns_next_id; } else if (!xml_strcmp(ns, INVALID_URN)) { xmlns_invid = xmlns_next_id; } else if (!xml_strcmp(ns, XSI_URN)) { xmlns_xsiid = xmlns_next_id; } else if (!xml_strcmp(ns, XML_URN)) { xmlns_xmlid = xmlns_next_id; } else if (!xml_strcmp(ns, NCN_URN)) { xmlns_ncnid = xmlns_next_id; } else if (!xml_strcmp(ns, YANG_URN)) { xmlns_yangid = xmlns_next_id; } else if (!xml_strcmp(ns, YIN_URN)) { xmlns_yinid = xmlns_next_id; } else if (!xml_strcmp(ns, WILDCARD_URN)) { xmlns_wildcardid = xmlns_next_id; } else if (!xml_strcmp(ns, NC_WD_ATTR_URN)) { xmlns_wdaid = xmlns_next_id; } if (LOGDEBUG2) { log_debug2("\nxmlns_reg: id:%2d mod:%s\turi: %s", rec->ns_id, rec->ns_module, rec->ns_name); } /* bump the next_id after returning the value used */ *ns_id = xmlns_next_id++; return NO_ERR; } /* xmlns_register_ns */ /******************************************************************** * FUNCTION xmlns_get_ns_prefix * * Get the prefix for the specified namespace * * INPUTS: * ns_id == namespace ID * RETURNS: * pointer to prefix or NULL if bad params *********************************************************************/ const xmlChar * xmlns_get_ns_prefix (xmlns_id_t ns_id) { if (!valid_id(ns_id)) { return (const xmlChar *)"--"; } else { return (const xmlChar *)xmlns[ns_id-1]->ns_pfix; } } /* xmlns_get_ns_prefix */ /******************************************************************** * FUNCTION xmlns_get_ns_name * * Get the name for the specified namespace * * INPUTS: * ns_id == namespace ID * RETURNS: * pointer to name or NULL if bad params *********************************************************************/ const xmlChar * xmlns_get_ns_name (xmlns_id_t ns_id) { if (!valid_id(ns_id)) { return NULL; } else { return (const xmlChar *)xmlns[ns_id-1]->ns_name; } } /* xmlns_get_ns_name */ /******************************************************************** * FUNCTION xmlns_find_ns_by_module * * Find the NS ID from its module name that registered it * * INPUTS: * modname == module name string to find * * RETURNS: * namespace ID or XMLNS_NULL_NS_ID if error *********************************************************************/ xmlns_id_t xmlns_find_ns_by_module (const xmlChar *modname) { uint32 i; xmlns_t *rec; #ifdef DEBUG if (!modname) { SET_ERROR(ERR_INTERNAL_PTR); return XMLNS_NULL_NS_ID; } #endif for (i=0; ins_module) { if (!xml_strcmp(rec->ns_module, modname)) { return rec->ns_id; } } } return XMLNS_NULL_NS_ID; } /* xmlns_find_ns_by_module */ /******************************************************************** * FUNCTION xmlns_find_ns_by_prefix * * Find the NS ID from its prefix * * INPUTS: * pfix == pointer to prefix string * RETURNS: * namespace ID or XMLNS_NULL_NS_ID if error *********************************************************************/ xmlns_id_t xmlns_find_ns_by_prefix (const xmlChar *pfix) { uint32 i; xmlns_t *rec; #ifdef DEBUG if (!pfix) { return XMLNS_NULL_NS_ID; } #endif for (i=0; ins_pfix[0]) { if (!xml_strcmp(rec->ns_pfix, pfix)) { return rec->ns_id; } } } return XMLNS_NULL_NS_ID; } /* xmlns_find_ns_by_prefix */ /******************************************************************** * FUNCTION xmlns_find_ns_by_name * * Find the NS ID from its name * * INPUTS: * name == pointer to name string * RETURNS: * namespace ID or XMLNS_NULL_NS_ID if error *********************************************************************/ xmlns_id_t xmlns_find_ns_by_name (const xmlChar *name) { xmlns_t *ns; #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return XMLNS_NULL_NS_ID; } #endif ns = def_reg_find_ns(name); if (ns) { return ns->ns_id; } return XMLNS_NULL_NS_ID; } /* xmlns_find_ns_by_name */ /******************************************************************** * FUNCTION xmlns_find_ns_by_name_str * * Find the NS ID from its name (counted string version) * * INPUTS: * name == pointer to name string * namelen == length of name string * * RETURNS: * namespace ID or XMLNS_NULL_NS_ID if error *********************************************************************/ xmlns_id_t xmlns_find_ns_by_name_str (const xmlChar *name, uint32 namelen) { xmlns_t *ns; uint32 i; #ifdef DEBUG if (!name) { SET_ERROR(ERR_INTERNAL_PTR); return XMLNS_NULL_NS_ID; } if (!namelen) { SET_ERROR(ERR_INTERNAL_VAL); return XMLNS_NULL_NS_ID; } #endif for (i=0; ins_name) { if (!xml_strncmp(ns->ns_name, name, namelen)) { return ns->ns_id; } } } return XMLNS_NULL_NS_ID; } /* xmlns_find_ns_by_name_str */ /******************************************************************** * FUNCTION xmlns_nc_id * * Get the ID for the NETCONF namespace or 0 if it doesn't exist * * INPUTS: * none * RETURNS: * NETCONF NS ID or 0 if not found *********************************************************************/ xmlns_id_t xmlns_nc_id (void) { return xmlns_ncid; } /* xmlns_nc_id */ /******************************************************************** * FUNCTION xmlns_ncx_id * * Get the ID for the NETCONF Extensions namespace or 0 if it doesn't exist * * INPUTS: * none * RETURNS: * NETCONF-X NS ID or 0 if not found *********************************************************************/ xmlns_id_t xmlns_ncx_id (void) { return xmlns_ncxid; } /* xmlns_ncx_id */ /******************************************************************** * FUNCTION xmlns_ns_id * * Get the ID for the XMLNS namespace or 0 if it doesn't exist * * INPUTS: * none * RETURNS: * XMLNS NS ID or 0 if not found *********************************************************************/ xmlns_id_t xmlns_ns_id (void) { return xmlns_nsid; } /* xmlns_ns_id */ /******************************************************************** * FUNCTION xmlns_inv_id * * Get the INVALID namespace ID * * INPUTS: * none * RETURNS: * INVALID NS ID or 0 if not set yet *********************************************************************/ xmlns_id_t xmlns_inv_id (void) { return xmlns_invid; } /* xmlns_inv_id */ /******************************************************************** * FUNCTION xmlns_xs_id * * Get the ID for the XSD namespace or 0 if it doesn't exist * * INPUTS: * none * RETURNS: * XSD NS ID or 0 if not found *********************************************************************/ xmlns_id_t xmlns_xs_id (void) { return xmlns_xsid; } /* xmlns_xs_id */ /******************************************************************** * FUNCTION xmlns_xsi_id * * Get the ID for the XSD Instance (XSI) namespace or 0 if it doesn't exist * * INPUTS: * none * RETURNS: * XSI ID or 0 if not found *********************************************************************/ xmlns_id_t xmlns_xsi_id (void) { return xmlns_xsiid; } /* xmlns_xsi_id */ /******************************************************************** * FUNCTION xmlns_xml_id * * Get the ID for the 1998 XML namespace or 0 if it doesn't exist * * INPUTS: * none * RETURNS: * XML ID or 0 if not found *********************************************************************/ xmlns_id_t xmlns_xml_id (void) { return xmlns_xmlid; } /* xmlns_xml_id */ /******************************************************************** * FUNCTION xmlns_ncn_id * * Get the ID for the NETCONF Notifications namespace or 0 if it * doesn't exist * * INPUTS: * none * RETURNS: * NCN ID or 0 if not found *********************************************************************/ xmlns_id_t xmlns_ncn_id (void) { return xmlns_ncnid; } /* xmlns_ncn_id */ /******************************************************************** * FUNCTION xmlns_yang_id * * Get the ID for the YANG namespace or 0 if it * doesn't exist * * INPUTS: * none * RETURNS: * YANG ID or 0 if not found *********************************************************************/ xmlns_id_t xmlns_yang_id (void) { return xmlns_yangid; } /* xmlns_yang_id */ /******************************************************************** * FUNCTION xmlns_yin_id * * Get the ID for the YIN namespace or 0 if it * doesn't exist * * INPUTS: * none * RETURNS: * YIN ID or 0 if not found *********************************************************************/ xmlns_id_t xmlns_yin_id (void) { return xmlns_yinid; } /* xmlns_yin_id */ /******************************************************************** * FUNCTION xmlns_wildcard_id * * Get the ID for the base:1.1 wildcard namespace or 0 if it * doesn't exist * * INPUTS: * none * RETURNS: * Wildcard ID or 0 if not found *********************************************************************/ xmlns_id_t xmlns_wildcard_id (void) { return xmlns_wildcardid; } /* xmlns_wildcard_id */ /******************************************************************** * FUNCTION xmlns_wda_id * * Get the ID for the wd:default XML attribute namespace or 0 if it * doesn't exist * * INPUTS: * none * RETURNS: * with-defaults default attribute namespace ID or 0 if not found *********************************************************************/ xmlns_id_t xmlns_wda_id (void) { return xmlns_wdaid; } /* xmlns_wda_id */ /******************************************************************** * FUNCTION xmlns_get_module * * get the module name of the namespace ID * get module name that registered this namespace * * INPUTS: * nsid == namespace ID to check * RETURNS: * none *********************************************************************/ const xmlChar * xmlns_get_module (xmlns_id_t nsid) { if (!xmlns_init_done) { xmlns_init(); return NULL; } if (!valid_id(nsid)) { return NULL; } return xmlns[nsid-1]->ns_module; } /* xmlns_get_module */ /******************************************************************** * FUNCTION xmlns_get_modptr * * get the module pointer for the namespace ID * * INPUTS: * nsid == namespace ID to check * RETURNS: * void * cast of the module or NULL *********************************************************************/ void * xmlns_get_modptr (xmlns_id_t nsid) { if (!xmlns_init_done) { xmlns_init(); return NULL; } if (!valid_id(nsid)) { return NULL; } return xmlns[nsid-1]->ns_mod; } /* xmlns_get_modptr */ /******************************************************************** * FUNCTION xmlns_set_modptrs * * get the module pointer for the namespace ID * * INPUTS: * modname == module owner name to find * modptr == ncx_module_t back-ptr to set * *********************************************************************/ void xmlns_set_modptrs (const xmlChar *modname, void *modptr) { uint32 i; xmlns_t *rec; #ifdef DEBUG if (!modname) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (!xmlns_init_done) { xmlns_init(); return; } for (i=0; ins_module) { if (!xml_strcmp(rec->ns_module, modname)) { rec->ns_mod = modptr; } } } } /* xmlns_set_modptrs */ /******************************************************************** * FUNCTION xmlns_new_pmap * * malloc and initialize a new xmlns_pmap_t struct * * INPUTS: * buffsize == size of the prefix buffer to allocate * within this pmap (0 == do not malloc yet) * * RETURNS: * pointer to new struct or NULL if malloc error *********************************************************************/ xmlns_pmap_t * xmlns_new_pmap (uint32 buffsize) { xmlns_pmap_t *pmap; pmap = m__getObj(xmlns_pmap_t); if (!pmap) { return NULL; } memset(pmap, 0x0, sizeof(xmlns_pmap_t)); if (buffsize) { pmap->nm_pfix = m__getMem(buffsize); if (!pmap->nm_pfix) { m__free(pmap); return NULL; } else { memset(pmap->nm_pfix, 0x0, buffsize); } } return pmap; } /* xmlns_new_pmap */ /******************************************************************** * FUNCTION xmlns_free_pmap * * free a xmlns_pmap_t struct * * INPUTS: * pmap == prefix map struct to free * *********************************************************************/ void xmlns_free_pmap (xmlns_pmap_t *pmap) { #ifdef DEBUG if (!pmap) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (pmap->nm_pfix) { m__free(pmap->nm_pfix); } m__free(pmap); } /* xmlns_free_pmap */ /******************************************************************** * FUNCTION xmlns_new_qname * * malloc and initialize a new xmlns_qname_t struct * * RETURNS: * pointer to new struct or NULL if malloc error *********************************************************************/ xmlns_qname_t * xmlns_new_qname (void) { xmlns_qname_t *qname; qname = m__getObj(xmlns_qname_t); if (!qname) { return NULL; } memset(qname, 0x0, sizeof(xmlns_qname_t)); return qname; } /* xmlns_new_qname */ /******************************************************************** * FUNCTION xmlns_free_qname * * free a xmlns_qname_t struct * * INPUTS: * qname == QName struct to free * *********************************************************************/ void xmlns_free_qname (xmlns_qname_t *qname) { #ifdef DEBUG if (!qname) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif m__free(qname); } /* xmlns_free_qname */ /******************************************************************** * FUNCTION xmlns_ids_equal * * compare 2 namespace IDs only if they are both non-zero * and return TRUE if they are equal * * INPUTS: * ns1 == namespace ID 1 * ns2 == namespace ID 2 * * RETURNS: * TRUE if equal or both IDs are not zero * FALSE if both IDs are non-zero and they are different *********************************************************************/ boolean xmlns_ids_equal (xmlns_id_t ns1, xmlns_id_t ns2) { if (ns1 && ns2) { return (ns1==ns2) ? TRUE : FALSE; } else { return TRUE; } } /* xmlns_ids_equal */ /* END file xmlns.c */ yuma123_2.14/netconf/src/ncx/yang_obj.c0000664000175000017500000134725114770023131020107 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: yang_obj.c YANG module parser, data-def-stmt support /ns1:value/ns2:value/ns3:value/... An obj_template_t is essentially a QName node in the conceptual element, Every leaf/leaf-list definition node has a typ_def_t, and every value instance node has a val_value_t struct. This allows engine callbacks to process arbitrarily complex data structues with the same code. There are 13 types of objects: enum constant has value node ---------------------------------------- OBJ_TYP_ANYDATA Y (1) OBJ_TYP_ANYXML Y (1) OBJ_TYP_CONTAINER Y OBJ_TYP_LEAF Y OBJ_TYP_LEAF_LIST Y OBJ_TYP_LIST Y OBJ_TYP_CHOICE N OBJ_TYP_CASE N OBJ_TYP_USES N OBJ_TYP_REFINE N OBJ_TYP_AUGMENT N OBJ_TYP_RPC N OBJ_TYP_RPCIO Y (2) OBJ_TYP_NOTIF N (1) ANYXML and ANYDATA are not stored in the value tree as type anyxml/anydata. They are converted as follows: Complex Node -> NCX_BT_CONTAINER Simple Node -> NCX_BT_STRING Empty Node -> NCX_BT_EMPTY (2) RPCIO nodes are instantiated only within the implementation, to act as a container for collected parameters or results. It is not found under the element. These objects are grouped as follows: * concrete data node objects (anyxml, anydata, container - list) * meta grouping constructs (choice, case) and (uses, refine, augment) * RPC method objects (rpc, input, output) * notification objects (notification) 5 Pass Validation Process -------------------------- In pass 1, the source file is parsed into YANG tokens. String concatentation and quoted string adjustment are handled in this pass. In pass 2, the objects are parsed via yang_obj_consume_datadef. Syntax errors and any other static errors are reported. In pass 3, the object definitions are validated for correctness, via the yang_obj_resolve_datadefs function. This is mixed with calls to yang_typ_resolve_typedefs and yang_grp_resolve_groupings. Uses and augments are not expanded in pass 3, so some details like key validation for a list cannot be done, since the contents may depend on the expanded uses or descendant form augment statement. In pass 4, groupings are completed with yang_grp_resolve_complete. Then all the uses-based data is cloned and placed into the tree, via yang_obj_resolve_uses In pass 5, all the augment-based data is cloned and placed into the tree, via yang_obj_resolve_augments The uses and augment objects are kept for XSD and other translation, and needed for internal data sharing. In a cloned object, a minimal amount of data is copied, and the rest is shadowed with back-pointers. For the 'uses' statement, refined objects are merged into the cloned tree as specified by the grouping and any refine statements within the uses statement. For the 'augment' statement, one exact clone of each augmenting node is placed in the target, based on the schema node target for the augment clause. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 09dec07 abb begun; start from yang_typ.c 29nov08 abb added when-stmt support as per yang-02 05def11 abb update docs; fix skipped resolve_xpath bug ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #define _GNU_SOURCE #include #undef _GNU_SOURCE #include #include #include #include #include #include "procdefs.h" #include "def_reg.h" #include "dlq.h" #include "grp.h" #include "log.h" #include "ncxconst.h" #include "ncxtypes.h" #include "ncx.h" #include "ncx_appinfo.h" #include "ncx_feature.h" #include "ncx_list.h" #include "obj.h" #include "status.h" #include "typ.h" #include "val123.h" #include "xml_util.h" #include "xpath.h" #include "xpath1.h" #include "xpath_yang.h" #include "yangconst.h" #include "yang.h" #include "yang_grp.h" #include "yang_obj.h" #include "yang_typ.h" #include "tk.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define YANG_OBJ_DEBUG( fmtstr, ... ) log_debug( fmtstr, ##__VA_ARGS__ ) #define YANG_OBJ_DEBUG2( fmtstr, ... ) log_debug2( fmtstr, ##__VA_ARGS__ ) #define YANG_OBJ_DEBUG3( fmtstr, ... ) log_debug3( fmtstr, ##__VA_ARGS__ ) #define YANG_OBJ_DEBUG4( fmtstr, ... ) log_debug4( fmtstr, ##__VA_ARGS__ ) #define YANG_OBJ_DEBUG_USES3( fmtstr, ... ) log_debug3( fmtstr, ##__VA_ARGS__ ) #define YANG_OBJ_DEBUG_USES4( fmtstr, ... ) log_debug4( fmtstr, ##__VA_ARGS__ ) #define YANG_OBJ_DEBUG_MEMORY( fmtstr, ... ) \ log_debug3( fmtstr, ##__VA_ARGS__ ) #else #define YANG_OBJ_DEBUG( fmtstr, ... ) #define YANG_OBJ_DEBUG2( fmtstr, ... ) #define YANG_OBJ_DEBUG3( fmtstr, ... ) #define YANG_OBJ_DEBUG4( fmtstr, ... ) #define YANG_OBJ_DEBUG_USES3( fmtstr, ... ) #define YANG_OBJ_DEBUG_USES4( fmtstr, ... ) #define YANG_OBJ_DEBUG_MEMORY( fmtstr, ... ) #endif /******************************************************************** * * * M A C R O S * * * *********************************************************************/ /* used in parser routines to decide if processing can continue * will exit the function if critical error or continue if not * In all uses, there is a new object being constructed, * called 'obj', which must be freed before exit * * Unless the error is considered fatal, processing * continues in order to validate as much of the input * module as possible */ #define CHK_OBJ_EXIT(obj, res, retres)\ if (res != NO_ERR) {\ if (res < ERR_LAST_SYS_ERR || res==ERR_NCX_EOF) {\ obj_free_template(obj);\ return res;\ } else {\ retres = res;\ }\ } #define CHK_DEV_EXIT(dev, res, retres)\ if (res != NO_ERR) {\ if (res < ERR_LAST_SYS_ERR || res==ERR_NCX_EOF) {\ obj_free_deviation(dev);\ return res;\ } else {\ retres = res;\ }\ } #define CHK_DEVI_EXIT(devi, res, retres)\ if (res != NO_ERR) {\ if (res < ERR_LAST_SYS_ERR || res==ERR_NCX_EOF) {\ obj_free_deviate(devi);\ return res;\ } else {\ retres = res;\ }\ } #define SET_OBJ_CURERR(tkc, obj) \ if (obj->usesobj) {\ tkc->curerr = &obj->usesobj->tkerr;\ } else {\ tkc->curerr = &obj->tkerr;\ } /******************************************************************** * * * F O R W A R D D E C L A R A T I O N S * * * *********************************************************************/ /* local functions called recursively */ static status_t consume_datadef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp); static status_t consume_case_datadef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent); static status_t consume_refine (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent); static status_t consume_augment (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp); static status_t expand_augment (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *datadefQ); static status_t resolve_datadef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *testobj, boolean redo); static status_t resolve_datadefs (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ, boolean redo); /************* U T I L I T Y F U N C T I O N S **********/ /* * Handle the TOP_LEVEL_MANDATORY warning or error * * \param tkc the parser token chain (may be NULL) * \param mod the module being parsed * \param obj the top-level mandatory object * \return the error status (treat as error or warning ***************************************************************/ static status_t handle_top_mandatory ( tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, const xmlChar *objstr ) { const xmlChar *errstr = (obj_has_when_stmts(obj)) ? (const xmlChar *)"conditional " : EMPTY_STRING; if (ncx_get_top_mandatory_allowed()) { if (ncx_warning_enabled(ERR_NCX_TOP_LEVEL_MANDATORY)) { log_warn("\nWarning: top-level %s%s '%s' is mandatory", errstr, objstr, obj_get_name(obj)); SET_OBJ_CURERR(tkc, obj); ncx_print_errormsg(tkc, mod, ERR_NCX_TOP_LEVEL_MANDATORY); } else if (mod != NULL) { ncx_inc_warnings(mod); } return NO_ERR; } else { log_error("\nError: top-level %s%s '%s' is mandatory", errstr, obj_get_name(obj)); SET_OBJ_CURERR(tkc, obj); ncx_print_errormsg(tkc, mod, ERR_NCX_TOP_LEVEL_MANDATORY_FAILED); return ERR_NCX_TOP_LEVEL_MANDATORY_FAILED; } } /* * Adds deviation module name to targets module devmodlist * * \param target_mod module defining the target of the deviation * \param deviation_mod module defining the deviation ***************************************************************/ static void devmodlist_update(ncx_module_t * target_mod, const ncx_save_deviations_t *savedev) { status_t res; ncx_save_deviations_t *devmod; /* skip if reference already present */ for (devmod = (ncx_save_deviations_t *) dlq_firstEntry(&target_mod->devmodlist); devmod != NULL; devmod = (ncx_save_deviations_t *)dlq_nextEntry(devmod)) { if(0==strcmp(devmod->devmodule, savedev->devmodule)) { return; } } devmod = ncx_new_save_deviations (savedev->devmodule, savedev->devrevision, savedev->devnamespace, savedev->devprefix); assert(devmod != NULL); dlq_enque(devmod, &target_mod->devmodlist); } /* * Add a string to the default value(s) for a leaf-list * * \param defvalsQ list to which value should be appended * \param defval value to append ***************************************************************/ static status_t append_defvalsQ (dlq_hdr_t *defvalsQ, xmlChar *defval) { obj_leaflist_defval_t *tmp; tmp = m__getMem(sizeof(*tmp)); if (tmp == NULL) return ERR_INTERNAL_MEM; tmp->defval = defval; dlq_enque(tmp, defvalsQ); return NO_ERR; } /************* P A R S E F U N C T I O N S *************/ /******************************************************************** * FUNCTION finish_config_flag * * Finish the internal settings for the config-stmt * * INPUTS: * obj == object to process * *********************************************************************/ static void finish_config_flag (obj_template_t *obj) { boolean flag; if (!(obj->flags & OBJ_FL_CONFSET)) { if (obj->parent && !obj_is_root(obj->parent)) { flag = obj_get_config_flag_deep(obj->parent); if (flag) { obj->flags |= OBJ_FL_CONFIG; } else { obj->flags &= ~OBJ_FL_CONFIG; } } else if (OBJ_DEF_CONFIG) { obj->flags |= OBJ_FL_CONFIG; } } } /* finish_config_flag */ /******************************************************************** * FUNCTION add_object * * Check if an object already exists, and add it if not * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * 'obj' is either deleted or added at the end of this fn * * INPUTS: * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * obj == object to add * * RETURNS: * status of the operation *********************************************************************/ static status_t add_object (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *obj) { obj_template_t *testobj; const xmlChar *name; yang_stmt_t *stmt; status_t res; res = NO_ERR; name = obj_get_name(obj); if (que == &mod->datadefQ) { testobj = obj_find_template_top(mod, obj_get_mod_name(obj), name); } else { testobj = obj_find_template_test(que, obj_get_mod_name(obj), name); } if (testobj == NULL && obj_is_top(obj) && obj_is_data_db(obj)) { testobj = obj_find_template_all(mod, obj_get_mod_name(obj), name); } if (testobj) { if (testobj->tkerr.mod != mod) { log_error("\nError: object '%s' already defined " "in [sub]module '%s' at line %u", name, testobj->tkerr.mod->name, testobj->tkerr.linenum); } else { log_error("\nError: object '%s' already defined at line %u", name, testobj->tkerr.linenum); } res = ERR_NCX_DUP_ENTRY; SET_OBJ_CURERR(tkc, obj); ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); } else { obj_set_ncx_flags(obj); dlq_enque(obj, que); /* may have some errors */ if (mod->stmtmode && que==&mod->datadefQ) { /* save top-level object order only */ stmt = yang_new_obj_stmt(obj); if (stmt) { dlq_enque(stmt, &mod->stmtQ); } else { log_error("\nError: malloc failure for obj_stmt"); res = ERR_INTERNAL_MEM; SET_OBJ_CURERR(tkc, obj); ncx_print_errormsg(tkc, mod, res); } } } return res; } /* add_object */ /******************************************************************** * FUNCTION consume_semi_lbrace * * Parse the next token as a semi-colon or a left brace * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'anyxml' keyword * * INPUTS: * tkc == token chain * mod == module in progress * obj == object in progress * done == address of boolean done flag * * OUTPUTS: * *done will be set on exit to TRUE or FALSE * * RETURNS: * status of the operation; *********************************************************************/ static status_t consume_semi_lbrace (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, boolean *done) { const char *expstr; status_t res; /* Get the starting left brace for the sub-clauses * or a semi-colon to end the case-stmt */ res = TK_ADV(tkc); if (res != NO_ERR) { *done = TRUE; ncx_print_errormsg(tkc, mod, res); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: obj->flags |= OBJ_FL_EMPTY; *done = TRUE; break; case TK_TT_LBRACE: *done = FALSE; break; default: res = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, res, expstr); *done = TRUE; } return res; } /* consume_semi_lbrace */ /******************************************************************** * FUNCTION consume_any * * Parse the next N tokens as an anyxml-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'anyxml' keyword * * INPUTS: * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object or NULL if top-level anyxml-stmt * grp == parent grp_template_t or NULL if not child of grp * any_typ == either OBJ_TYP_ANYXML or OBJ_TYP_ANYDATA * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_any(tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp, obj_type_t any_typ) { obj_template_t *obj = NULL; obj_leaf_t *leaf = NULL; const xmlChar *val = NULL; const char *expstr = "keyword"; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, when = FALSE, conf = FALSE; boolean flagset = FALSE, mand = FALSE, stat = FALSE; boolean desc = FALSE, ref = FALSE; status_t res = NO_ERR, retres = NO_ERR; assert(any_typ == OBJ_TYP_ANYXML || any_typ == OBJ_TYP_ANYDATA); /* Get a new obj_template_t to fill in */ obj = obj_new_template(any_typ); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->grp = grp; obj->nsid = mod->nsid; leaf = obj->def.leaf; if (que == &mod->datadefQ) { obj->flags |= (OBJ_FL_TOP | OBJ_FL_CONFSET | OBJ_FL_CONFIG); } if (leaf->typdef) { typ_free_typdef(leaf->typdef); } leaf->typdef = typ_get_basetype_typdef(any_typ==OBJ_TYP_ANYDATA?NCX_BT_ANYDATA:NCX_BT_ANYXML); /* Get the mandatory anyxml name */ res = yang_consume_id_string(tkc, mod, &leaf->name); CHK_OBJ_EXIT(obj, res, retres); res = consume_semi_lbrace(tkc, mod, obj, &done); CHK_OBJ_EXIT(obj, res, retres); /* get the anyxml statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); flagset = FALSE; /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a keyword token string so check the value */ if (!xml_strcmp(val, YANG_K_WHEN)) { res = yang_consume_when(tkc, mod, obj, &when); } else if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &obj->iffeatureQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_MUST)) { res = yang_consume_must(tkc, mod, &obj->def.leaf->mustQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_CONFIG)) { res = yang_consume_boolean(tkc, mod, &flagset, &conf, &obj->appinfoQ); obj->flags |= OBJ_FL_CONFSET; if (flagset) { obj->flags |= OBJ_FL_CONFIG; } else { obj->flags &= ~OBJ_FL_CONFIG; } } else if (!xml_strcmp(val, YANG_K_MANDATORY)) { res = yang_consume_boolean(tkc, mod, &flagset, &mand, &obj->appinfoQ); obj->flags |= OBJ_FL_MANDSET; if (flagset) { obj->flags |= OBJ_FL_MANDATORY; } } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &leaf->status, &stat, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &leaf->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &leaf->ref, &ref, &obj->appinfoQ); } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_OBJ_EXIT(obj, res, retres); } /* save or delete the obj_template_t struct */ if (leaf->name && ncx_valid_name2(leaf->name)) { res = add_object(tkc, mod, que, obj); CHK_EXIT(res, retres); } else { obj_free_template(obj); } return retres; } /* consume_any */ /******************************************************************** * FUNCTION consume_anydata * * Parse the next N tokens as an anydata-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'anydata' keyword * * INPUTS: * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object or NULL if top-level anydata-stmt * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_anydata (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { return consume_any(tkc, mod, que, parent, grp, OBJ_TYP_ANYDATA); } /* consume_anydata */ /******************************************************************** * FUNCTION consume_anyxml * * Parse the next N tokens as an anydata-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'anydata' keyword * * INPUTS: * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object or NULL if top-level anydata-stmt * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_anyxml (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { return consume_any(tkc, mod, que, parent, grp, OBJ_TYP_ANYXML); } /* consume_anyxml */ /******************************************************************** * FUNCTION consume_container * * Parse the next N tokens as a container-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'container' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object or NULL if top-level * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_container (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { obj_template_t *obj = NULL; obj_container_t *con = NULL; const xmlChar *val = NULL; const char *expstr = "keyword"; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, when = FALSE, pres = FALSE; boolean conf = FALSE, flagset = FALSE, stat = FALSE; boolean desc = FALSE, ref = FALSE; status_t res = NO_ERR, retres = NO_ERR; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_CONTAINER); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->grp = grp; obj->nsid = mod->nsid; con = obj->def.container; if (que == &mod->datadefQ) { obj->flags |= (OBJ_FL_TOP | OBJ_FL_CONFSET | OBJ_FL_CONFIG); } /* Get the mandatory container name */ res = yang_consume_id_string(tkc, mod, &con->name); CHK_OBJ_EXIT(obj, res, retres); res = consume_semi_lbrace(tkc, mod, obj, &done); CHK_OBJ_EXIT(obj, res, retres); /* get the container statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); flagset = FALSE; /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_WHEN)) { res = yang_consume_when(tkc, mod, obj, &when); } else if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &obj->iffeatureQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_TYPEDEF)) { res = yang_typ_consume_typedef(pcb, tkc, mod, con->typedefQ); } else if (!xml_strcmp(val, YANG_K_GROUPING)) { res = yang_grp_consume_grouping(pcb, tkc, mod, con->groupingQ, obj); } else if (!xml_strcmp(val, YANG_K_MUST)) { res = yang_consume_must(tkc, mod, &con->mustQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_PRESENCE)) { res = yang_consume_strclause(tkc, mod, &con->presence, &pres, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_CONFIG)) { res = yang_consume_boolean(tkc, mod, &flagset, &conf, &obj->appinfoQ); obj->flags |= OBJ_FL_CONFSET; if (flagset) { obj->flags |= OBJ_FL_CONFIG; } else { obj->flags &= ~OBJ_FL_CONFIG; } } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &con->status, &stat, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &con->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &con->ref, &ref, &obj->appinfoQ); } else { res = yang_obj_consume_datadef(pcb, tkc, mod, con->datadefQ, obj); } CHK_OBJ_EXIT(obj, res, retres); } /* save or delete the obj_template_t struct */ if (con->name && ncx_valid_name2(con->name)) { res = add_object(tkc, mod, que, obj); CHK_EXIT(res, retres); } else { obj_free_template(obj); } return retres; } /* consume_container */ #ifdef ENABLE_DIRECT_MUST_AUGMENT_EX static status_t consume_must (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { status_t res; res = yang_consume_must(tkc, mod, que, &parent->appinfoQ); return res; } #endif /******************************************************************** * FUNCTION consume_leaf * * Parse the next N tokens as a leaf-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'leaf' keyword * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object or NULL if top-level data-def-stmt * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_leaf (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { obj_template_t *obj = NULL; obj_leaf_t *leaf = NULL; const xmlChar *val = NULL; const char *expstr = "keyword"; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, when = FALSE, typ = FALSE; boolean units = FALSE, def = FALSE, conf = FALSE; boolean mand = FALSE, stat = FALSE, desc = FALSE; boolean ref = FALSE, typeok = FALSE, flagset = FALSE; status_t res = NO_ERR, retres = NO_ERR; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_LEAF); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->grp = grp; obj->nsid = mod->nsid; leaf = obj->def.leaf; if (que == &mod->datadefQ) { obj->flags |= (OBJ_FL_TOP | OBJ_FL_CONFSET | OBJ_FL_CONFIG); } /* Get the mandatory leaf name */ res = yang_consume_id_string(tkc, mod, &leaf->name); CHK_OBJ_EXIT(obj, res, retres); /* Get the mandatory left brace */ res = ncx_consume_token(tkc, mod, TK_TT_LBRACE); CHK_OBJ_EXIT(obj, res, retres); /* get the leaf statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); flagset = FALSE; /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a keyword token string so check the value */ if (!xml_strcmp(val, YANG_K_WHEN)) { res = yang_consume_when(tkc, mod, obj, &when); } else if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &obj->iffeatureQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_TYPE)) { if (typ) { retres = ERR_NCX_DUP_ENTRY; typeok = FALSE; ncx_print_errormsg(tkc, mod, retres); /* toss the old typdef because this is a fatal * error anyway, and need to skip past the new * typedef; replace the old typedef! */ typ_clean_typdef(leaf->typdef); res = yang_typ_consume_type(pcb, tkc, mod, leaf->typdef); } else { typ = TRUE; res = yang_typ_consume_type(pcb, tkc, mod, leaf->typdef); if (res == NO_ERR) { typeok = TRUE; } } } else if (!xml_strcmp(val, YANG_K_UNITS)) { res = yang_consume_strclause(tkc, mod, &leaf->units, &units, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_MUST)) { res = yang_consume_must(tkc, mod, &leaf->mustQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DEFAULT)) { res = yang_consume_strclause(tkc, mod, &leaf->defval, &def, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_CONFIG)) { res = yang_consume_boolean(tkc, mod, &flagset, &conf, &obj->appinfoQ); obj->flags |= OBJ_FL_CONFSET; if (flagset) { obj->flags |= OBJ_FL_CONFIG; } else { obj->flags &= ~OBJ_FL_CONFIG; } } else if (!xml_strcmp(val, YANG_K_MANDATORY)) { res = yang_consume_boolean(tkc, mod, &flagset, &mand, &obj->appinfoQ); obj->flags |= OBJ_FL_MANDSET; if (flagset) { obj->flags |= OBJ_FL_MANDATORY; } } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &leaf->status, &stat, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &leaf->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &leaf->ref, &ref, &obj->appinfoQ); } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_OBJ_EXIT(obj, res, retres); } /* check mandatory params */ if (!typ) { retres = ERR_NCX_DATA_MISSING; ncx_mod_missing_err(tkc, mod, "leaf", "type"); } /* save or delete the obj_template_t struct */ if (leaf->name && ncx_valid_name2(leaf->name) && typeok) { res = add_object(tkc, mod, que, obj); CHK_EXIT(res, retres); } else { obj_free_template(obj); } return retres; } /* consume_leaf */ /******************************************************************** * FUNCTION consume_leaflist * * Parse the next N tokens as a leaf-list-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'leaf-list' keyword * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object or NULL if top-level data-def-stmt * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_leaflist (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { obj_template_t *obj = NULL; obj_leaflist_t *llist = NULL; const xmlChar *val = NULL; const char *expstr = "keyword"; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, when = FALSE, typ = FALSE; boolean units = FALSE, conf = FALSE; boolean minel = FALSE, maxel = FALSE, ord = FALSE; boolean stat = FALSE, desc = FALSE, ref = FALSE; boolean typeok = FALSE, flagset = FALSE; status_t res = NO_ERR, retres = NO_ERR; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_LEAF_LIST); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->grp = grp; obj->nsid = mod->nsid; llist = obj->def.leaflist; if (que == &mod->datadefQ) { obj->flags |= (OBJ_FL_TOP | OBJ_FL_CONFSET | OBJ_FL_CONFIG); } /* Get the mandatory leaf-list name */ res = yang_consume_id_string(tkc, mod, &llist->name); CHK_OBJ_EXIT(obj, res, retres); /* Get the mandatory left brace */ res = ncx_consume_token(tkc, mod, TK_TT_LBRACE); CHK_OBJ_EXIT(obj, res, retres); /* get the leaf-list statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a keyword token string so check the value */ if (!xml_strcmp(val, YANG_K_WHEN)) { res = yang_consume_when(tkc, mod, obj, &when); } else if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &obj->iffeatureQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_TYPE)) { if (typ) { retres = ERR_NCX_DUP_ENTRY; typeok = FALSE; ncx_print_errormsg(tkc, mod, retres); typ_clean_typdef(llist->typdef); res = yang_typ_consume_type(pcb, tkc, mod, llist->typdef); } else { typ = TRUE; res = yang_typ_consume_type(pcb, tkc, mod, llist->typdef); if (res == NO_ERR) { typeok = TRUE; } } } else if (!xml_strcmp(val, YANG_K_UNITS)) { res = yang_consume_strclause(tkc, mod, &llist->units, &units, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_MUST)) { res = yang_consume_must(tkc, mod, &llist->mustQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_CONFIG)) { flagset = FALSE; res = yang_consume_boolean(tkc, mod, &flagset, &conf, &obj->appinfoQ); obj->flags |= OBJ_FL_CONFSET; if (flagset) { obj->flags |= OBJ_FL_CONFIG; } else { obj->flags &= ~OBJ_FL_CONFIG; } } else if (!xml_strcmp(val, YANG_K_MIN_ELEMENTS)) { res = yang_consume_uint32(tkc, mod, &llist->minelems, &minel, &obj->appinfoQ); llist->minset = TRUE; } else if (!xml_strcmp(val, YANG_K_MAX_ELEMENTS)) { res = yang_consume_max_elements(tkc, mod, &llist->maxelems, &maxel, &obj->appinfoQ); llist->maxset = TRUE; } else if (!xml_strcmp(val, YANG_K_ORDERED_BY)) { res = yang_consume_ordered_by(tkc, mod, &llist->ordersys, &ord, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &llist->status, &stat, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &llist->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &llist->ref, &ref, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DEFAULT)) { xmlChar *defval = NULL; res = yang_consume_strclause(tkc, mod, &defval, NULL, /* don't check for duplicate */ &obj->appinfoQ); if (res == NO_ERR) { res = append_defvalsQ (obj->def.leaflist->defvalsQ, defval); if (res != NO_ERR) m__free(defval); } } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_OBJ_EXIT(obj, res, retres); } /* check mandatory params */ if (!typ) { retres = ERR_NCX_DATA_MISSING; ncx_mod_missing_err(tkc, mod, "leaf-list", "type"); } /* save or delete the obj_template_t struct */ if (llist->name && ncx_valid_name2(llist->name) && typeok) { res = add_object(tkc, mod, que, obj); CHK_EXIT(res, retres); } else { obj_free_template(obj); } return retres; } /* consume_leaflist */ /******************************************************************** * FUNCTION consume_list * * Parse the next N tokens as a list-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'list' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object or NULL if top-level data-def-stmt * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_list (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { obj_template_t *obj = NULL; obj_list_t *list = NULL; obj_unique_t *objuniq = NULL; const xmlChar *val = NULL; const char *expstr = "keyword"; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, when = FALSE, key = FALSE, conf = FALSE; boolean minel = FALSE, maxel = FALSE, ord = FALSE, stat = FALSE; boolean desc = FALSE, ref = FALSE, flagset = FALSE, ingrp = FALSE; status_t res = NO_ERR, retres = NO_ERR; ncx_error_t savetkerr; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_LIST); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->grp = grp; obj->nsid = mod->nsid; list = obj->def.list; if (que == &mod->datadefQ) { obj->flags |= (OBJ_FL_TOP | OBJ_FL_CONFSET | OBJ_FL_CONFIG); } /* Get the mandatory list name */ res = yang_consume_id_string(tkc, mod, &list->name); CHK_OBJ_EXIT(obj, res, retres); /* Get the mandatory left brace */ res = ncx_consume_token(tkc, mod, TK_TT_LBRACE); CHK_OBJ_EXIT(obj, res, retres); /* get the list statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a keyword token string so check the value */ if (!xml_strcmp(val, YANG_K_WHEN)) { res = yang_consume_when(tkc, mod, obj, &when); } else if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &obj->iffeatureQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_TYPEDEF)) { res = yang_typ_consume_typedef(pcb, tkc, mod, list->typedefQ); } else if (!xml_strcmp(val, YANG_K_GROUPING)) { res = yang_grp_consume_grouping(pcb, tkc, mod, list->groupingQ, obj); } else if (!xml_strcmp(val, YANG_K_MUST)) { res = yang_consume_must(tkc, mod, &list->mustQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_KEY)) { ncx_set_error(&savetkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); res = yang_consume_strclause(tkc, mod, &list->keystr, &key, &obj->appinfoQ); if (res == NO_ERR) { ncx_set_error(&list->keytkerr, savetkerr.mod, savetkerr.linenum, savetkerr.linepos); } } else if (!xml_strcmp(val, YANG_K_UNIQUE)) { objuniq = obj_new_unique(); if (!objuniq) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } ncx_set_error(&objuniq->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); res = yang_consume_strclause(tkc, mod, &objuniq->xpath, NULL, &obj->appinfoQ); if (res == NO_ERR) { dlq_enque(objuniq, &list->uniqueQ); } else { obj_free_unique(objuniq); } } else if (!xml_strcmp(val, YANG_K_CONFIG)) { flagset = FALSE; res = yang_consume_boolean(tkc, mod, &flagset, &conf, &obj->appinfoQ); obj->flags |= OBJ_FL_CONFSET; if (flagset) { obj->flags |= OBJ_FL_CONFIG; } else { obj->flags &= ~OBJ_FL_CONFIG; } } else if (!xml_strcmp(val, YANG_K_MIN_ELEMENTS)) { res = yang_consume_uint32(tkc, mod, &list->minelems, &minel, &obj->appinfoQ); list->minset = TRUE; } else if (!xml_strcmp(val, YANG_K_MAX_ELEMENTS)) { res = yang_consume_max_elements(tkc, mod, &list->maxelems, &maxel, &obj->appinfoQ); list->maxset = TRUE; } else if (!xml_strcmp(val, YANG_K_ORDERED_BY)) { res = yang_consume_ordered_by(tkc, mod, &list->ordersys, &ord, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &list->status, &stat, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &list->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &list->ref, &ref, &obj->appinfoQ); } else { res = yang_obj_consume_datadef(pcb, tkc, mod, list->datadefQ, obj); } CHK_OBJ_EXIT(obj, res, retres); } ingrp = FALSE; if (!list->keystr && obj_get_config_flag_check(obj, &ingrp)) { if (!ingrp && !obj_in_notif(obj) && !obj_in_rpc(obj)) { log_error("\nError: No key present for list '%s' on line %u", list->name, obj->tkerr.linenum); retres = ERR_NCX_DATA_MISSING; ncx_print_errormsg(tkc, mod, retres); } } /* save or delete the obj_template_t struct */ if (list->name && ncx_valid_name2(list->name)) { res = add_object(tkc, mod, que, obj); CHK_EXIT(res, retres); } else { obj_free_template(obj); } return retres; } /* consume_list */ /******************************************************************** * FUNCTION consume_case * * Parse the next N tokens as a case-stmt * Create and fill in an obj_template_t struct * and add it to the caseQ for a choice, in progress * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'case' keyword (if withcase==TRUE) * Current token is a data-def keyword (if withcase==FALSE) * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * choic == obj_choice_t in progress, add case arm to this caseQ * caseQ == Que to store the obj_template_t generated * parent == the obj_template_t containing the 'choic' param * In YANG, a top-level object cannot be a 'choice', * so this param should not be NULL * withcase == TRUE if a case arm was entered and the normal * (full) syntax for a case arm is used * == FALSE if the case arm is the shorthand implied * kind. The start token is the limited data-def-stmt * keyword and only one data-def-stmt will be parsed * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_case (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *caseQ, obj_template_t *parent, boolean withcase) { obj_case_t *cas = NULL, *testcas = NULL; obj_template_t *obj = NULL, *testobj = NULL; obj_template_t *test2obj = NULL, *casobj = NULL; const xmlChar *val = NULL, *namestr = NULL; const char *expstr = "keyword"; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, when = FALSE, stat = FALSE; boolean desc = FALSE, ref = FALSE; status_t res = NO_ERR, retres = NO_ERR; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_CASE); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->nsid = mod->nsid; cas = obj->def.cas; /* Get the mandatory case name */ if (withcase) { res = yang_consume_id_string(tkc, mod, &cas->name); CHK_OBJ_EXIT(obj, res, retres); res = consume_semi_lbrace(tkc, mod, obj, &done); CHK_OBJ_EXIT(obj, res, retres); } else { /* shorthand version, just 1 data-def-stmt per case */ res = consume_case_datadef(pcb, tkc, mod, cas->datadefQ, obj); CHK_OBJ_EXIT(obj, res, retres); done = TRUE; } /* get the case statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a keyword token string so check the value */ if (!xml_strcmp(val, YANG_K_WHEN)) { res = yang_consume_when(tkc, mod, obj, &when); } else if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &obj->iffeatureQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &cas->status, &stat, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &cas->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &cas->ref, &ref, &obj->appinfoQ); } else { res = consume_case_datadef(pcb, tkc, mod, cas->datadefQ, obj); } CHK_OBJ_EXIT(obj, res, retres); } /* if shorthand version, copy leaf name to case name */ if (!withcase && retres==NO_ERR) { res = NO_ERR; testobj = (obj_template_t *)dlq_firstEntry(cas->datadefQ); if (testobj) { val = obj_get_name(testobj); if (val) { cas->name = xml_strdup(val); if (!cas->name) { res = ERR_INTERNAL_MEM; } } else { res = SET_ERROR(ERR_INTERNAL_VAL); } } else { res = SET_ERROR(ERR_INTERNAL_VAL); } if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } } /* check case arm already defined * if not, check if any objects in the new case * are already defined in a different case */ if (retres == NO_ERR) { res = NO_ERR; for (casobj = (obj_template_t *)dlq_firstEntry(caseQ); casobj != NULL && res==NO_ERR; casobj = (obj_template_t *)dlq_nextEntry(casobj)) { testcas = casobj->def.cas; if (!xml_strcmp(cas->name, testcas->name)) { /* case arm name already used error */ res = retres = ERR_NCX_DUP_ENTRY; log_error("\nError: case name '%s' already used" " on line %u", testcas->name, casobj->tkerr.linenum); ncx_print_errormsg(tkc, mod, retres); } else { /* check object named within case arm already used */ for (testobj = (obj_template_t *) dlq_firstEntry(cas->datadefQ); testobj != NULL; testobj = (obj_template_t *) dlq_nextEntry(testobj)) { namestr = obj_get_name(testobj); test2obj = obj_find_template_test(testcas->datadefQ, obj_get_mod_name(testobj), namestr); if (test2obj) { /* duplicate in another case arm error */ res = retres = ERR_NCX_DUP_ENTRY; log_error("\nError: object name '%s' already used" " in case '%s', on line %u", namestr, testcas->name, test2obj->tkerr.linenum); ncx_print_errormsg(tkc, mod, retres); } } } } } /* save or delete the obj_template_t struct */ if (res==NO_ERR && cas->name && ncx_valid_name2(cas->name)) { obj_set_ncx_flags(obj); dlq_enque(obj, caseQ); } else { obj_free_template(obj); } return retres; } /* consume_case */ /******************************************************************** * FUNCTION consume_choice * * Parse the next N tokens as a choice-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'choice' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_choice (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { obj_template_t *obj = NULL, *testobj = NULL; obj_template_t *test2obj = NULL, *casobj = NULL; obj_choice_t *choic = NULL; obj_case_t *testcas = NULL; const xmlChar *val = NULL, *namestr = NULL; const char *expstr = "keyword"; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, when = FALSE, def = FALSE; boolean mand = FALSE, conf = FALSE, stat = FALSE; boolean desc = FALSE, ref = FALSE, flagset = FALSE; status_t res = NO_ERR, retres = NO_ERR; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_CHOICE); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->grp = grp; obj->nsid = mod->nsid; choic = obj->def.choic; if (que == &mod->datadefQ) { obj->flags |= (OBJ_FL_TOP | OBJ_FL_CONFSET | OBJ_FL_CONFIG); } /* Get the mandatory choice name */ res = yang_consume_id_string(tkc, mod, &choic->name); CHK_OBJ_EXIT(obj, res, retres); res = consume_semi_lbrace(tkc, mod, obj, &done); CHK_OBJ_EXIT(obj, res, retres); /* get the sub-section statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); flagset = FALSE; /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a keyword token string so check the value */ if (!xml_strcmp(val, YANG_K_WHEN)) { res = yang_consume_when(tkc, mod, obj, &when); } else if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &obj->iffeatureQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DEFAULT)) { res = yang_consume_strclause(tkc, mod, &choic->defval, &def, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_MANDATORY)) { res = yang_consume_boolean(tkc, mod, &flagset, &mand, &obj->appinfoQ); obj->flags |= OBJ_FL_MANDSET; if (flagset) { obj->flags |= OBJ_FL_MANDATORY; } } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &choic->status, &stat, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &choic->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &choic->ref, &ref, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_CONFIG)) { res = yang_consume_boolean(tkc, mod, &flagset, &conf, &obj->appinfoQ); obj->flags |= OBJ_FL_CONFSET; if (flagset) { obj->flags |= OBJ_FL_CONFIG; } else { obj->flags &= OBJ_FL_CONFIG; } } else if (!xml_strcmp(val, YANG_K_CASE)) { res = consume_case(pcb, tkc, mod, choic->caseQ, obj, TRUE); } else if (!xml_strcmp(val, YANG_K_ANYXML) || !xml_strcmp(val, YANG_K_ANYDATA) || !xml_strcmp(val, YANG_K_CONTAINER) || !xml_strcmp(val, YANG_K_LEAF) || !xml_strcmp(val, YANG_K_LEAF_LIST) || !xml_strcmp(val, YANG_K_LIST)) { /* create an inline 1-obj case statement */ res = consume_case(pcb, tkc, mod, choic->caseQ, obj, FALSE); } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_OBJ_EXIT(obj, res, retres); } /* save or delete the obj_template_t struct */ if (choic->name && ncx_valid_name2(choic->name)) { res = NO_ERR; /* make sure sibling choice is not already defined with same name */ if (que == &mod->datadefQ) { testobj = obj_find_template_top(mod, obj_get_mod_name(obj), choic->name); } else { testobj = obj_find_template_test(que, obj_get_mod_name(obj), choic->name); } if (testobj) { if (testobj->tkerr.mod != mod) { log_error("\nError: object '%s' already defined " "in [sub]module '%s' at line %u", choic->name, testobj->tkerr.mod->name, testobj->tkerr.linenum); } else { log_error("\nError: choice '%s' already defined at line %u", choic->name, testobj->tkerr.linenum); } res = retres = ERR_NCX_DUP_ENTRY; ncx_print_errormsg(tkc, mod, retres); } /* since the choice and case nodes do not really exist, * the objects within each case datadefQ must not conflict * with any sibling nodes of the choice itself */ for (casobj = (obj_template_t *)dlq_firstEntry(choic->caseQ); casobj != NULL; casobj = (obj_template_t *)dlq_nextEntry(casobj)) { testcas = casobj->def.cas; /* check object named within choice sibling objects */ for (testobj = (obj_template_t *) dlq_firstEntry(testcas->datadefQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { namestr = obj_get_name(testobj); test2obj = obj_find_template_test(que, obj_get_mod_name(testobj), namestr); if (test2obj) { /* duplicate in the same Q as the choice */ res = retres = ERR_NCX_DUP_ENTRY; log_error("\nError: object name '%s' in case '%s'" " already used in sibling node, on line %u", namestr, obj_get_name(casobj), test2obj->tkerr.linenum); ncx_print_errormsg(tkc, mod, retres); } } } if (res==NO_ERR) { obj_set_ncx_flags(obj); dlq_enque(obj, que); /* may have some errors */ } else { obj_free_template(obj); } } else { /* choice name was not valid */ obj_free_template(obj); } return retres; } /* consume_choice */ /******************************************************************** * FUNCTION consume_refine * * Parse the next N tokens as a refinement-stmt * Create and fill in an obj_template_t struct * and add it to the datadefQ for a uses in progress * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'refine' keyword * * INPUTS: * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t struct that get created * parent == the obj_template_t containing the 'uses' param * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_refine (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent) { obj_template_t *obj = NULL; obj_refine_t *refine = NULL; const xmlChar *val = NULL; const char *expstr = "refine target"; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, desc = FALSE, ref = FALSE; boolean pres = FALSE, def = FALSE, conf = FALSE; boolean mand = FALSE, minel = FALSE, maxel = FALSE; boolean flagset = FALSE; status_t res = NO_ERR, retres = NO_ERR; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_REFINE); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } refine = obj->def.refine; ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->nsid = mod->nsid; /* Get the mandatory refine target */ res = yang_consume_string(tkc, mod, &refine->target); CHK_OBJ_EXIT(obj, res, retres); res = consume_semi_lbrace(tkc, mod, obj, &done); CHK_OBJ_EXIT(obj, res, retres); /* get the container statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); flagset = FALSE; /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { ncx_set_error(&refine->descr_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); res = yang_consume_descr(tkc, mod, &refine->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { ncx_set_error(&refine->ref_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); res = yang_consume_descr(tkc, mod, &refine->ref, &ref, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_PRESENCE)) { ncx_set_error(&refine->presence_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); res = yang_consume_strclause(tkc, mod, &refine->presence, &pres, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DEFAULT)) { ncx_set_error(&refine->def_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); res = yang_consume_strclause(tkc, mod, &refine->def, &def, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_CONFIG)) { flagset = FALSE; ncx_set_error(&refine->config_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); res = yang_consume_boolean(tkc, mod, &flagset, &conf, &obj->appinfoQ); obj->flags |= OBJ_FL_CONFSET; if (flagset) { obj->flags |= OBJ_FL_CONFIG; } else { obj->flags &= ~OBJ_FL_CONFIG; } } else if (!xml_strcmp(val, YANG_K_MANDATORY)) { flagset = FALSE; ncx_set_error(&refine->mandatory_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); res = yang_consume_boolean(tkc, mod, &flagset, &mand, &obj->appinfoQ); obj->flags |= OBJ_FL_MANDSET; if (flagset) { obj->flags |= OBJ_FL_MANDATORY; } else { obj->flags &= ~OBJ_FL_MANDATORY; } } else if (!xml_strcmp(val, YANG_K_MIN_ELEMENTS)) { ncx_set_error(&refine->minelems_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); res = yang_consume_uint32(tkc, mod, &refine->minelems, &minel, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_MAX_ELEMENTS)) { ncx_set_error(&refine->maxelems_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); res = yang_consume_max_elements(tkc, mod, &refine->maxelems, &maxel, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_MUST)) { res = yang_consume_must(tkc, mod, &refine->mustQ, &obj->appinfoQ); } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_OBJ_EXIT(obj, res, retres); } /* save or delete the obj_template_t struct */ if (refine->target) { res = add_object(tkc, mod, que, obj); CHK_EXIT(res, retres); } else { obj_free_template(obj); } return res; } /* consume_refine */ /******************************************************************** * FUNCTION consume_uses * * Parse the next N tokens as a uses-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'uses' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_uses (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { obj_template_t *obj = NULL, *testobj = NULL; obj_uses_t *uses = NULL; grp_template_t *impgrp = NULL; const xmlChar *val = NULL; const char *expstr = "keyword"; yang_stmt_t *stmt = NULL; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, when = FALSE, stat = FALSE; boolean desc = FALSE, ref = FALSE; status_t res = NO_ERR, retres = NO_ERR; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_USES); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->nsid = mod->nsid; obj->grp = grp; if (que == &mod->datadefQ) { obj->flags |= (OBJ_FL_TOP | OBJ_FL_CONFSET | OBJ_FL_CONFIG); } uses = obj->def.uses; /* Get the mandatory uses target [prefix:]name */ res = yang_consume_pid_string(tkc, mod, &uses->prefix, &uses->name); CHK_OBJ_EXIT(obj, res, retres); /* attempt to find grouping only if it is from another module */ if (uses->prefix && xml_strcmp(uses->prefix, mod->prefix)) { impgrp = NULL; res = yang_find_imp_grouping(pcb, tkc, mod, uses->prefix, uses->name, &obj->tkerr, &impgrp); CHK_OBJ_EXIT(obj, res, retres); uses->grp = impgrp; } res = consume_semi_lbrace(tkc, mod, obj, &done); CHK_OBJ_EXIT(obj, res, retres); expstr = "uses sub-statement"; /* get the sub-section statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a keyword token string so check the value */ if (!xml_strcmp(val, YANG_K_WHEN)) { res = yang_consume_when(tkc, mod, obj, &when); } else if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &obj->iffeatureQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &uses->status, &stat, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &uses->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &uses->ref, &ref, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_AUGMENT)) { res = consume_augment(pcb, tkc, mod, uses->datadefQ, obj, NULL); } else if (!xml_strcmp(val, YANG_K_REFINE)) { res = consume_refine(tkc, mod, uses->datadefQ, obj); } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_OBJ_EXIT(obj, res, retres); } /* save or delete the obj_template_t struct */ if (uses->name && ncx_valid_name2(uses->name)) { testobj = obj_find_template_test(que, obj_get_mod_name(obj), uses->name); if (testobj) { log_error("\nError: object '%s' already defined at line %u", uses->name, testobj->tkerr.linenum); retres = ERR_NCX_DUP_ENTRY; ncx_print_errormsg(tkc, mod, retres); obj_free_template(obj); } else { obj_set_ncx_flags(obj); dlq_enque(obj, que); /* may have some errors */ if (mod->stmtmode && que==&mod->datadefQ) { /* save top-level object order only */ stmt = yang_new_obj_stmt(obj); if (stmt) { dlq_enque(stmt, &mod->stmtQ); } else { log_error("\nError: malloc failure for obj_stmt"); res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); } } } } else { obj_free_template(obj); } return retres; } /* consume_uses */ /******************************************************************** * FUNCTION consume_rpcio * * Parse the next N tokens as an input-stmt or output-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'input' or 'output' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent RPC object * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_rpcio (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent) { obj_template_t *obj = NULL, *testobj = NULL; obj_rpcio_t *rpcio = NULL; const xmlChar *val = NULL; const char *expstr = "typedef, grouping, or data-def keyword"; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE; status_t res = NO_ERR, retres = NO_ERR; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_RPCIO); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->nsid = mod->nsid; rpcio = obj->def.rpcio; /* Get the mandatory RPC method name */ rpcio->name = xml_strdup(TK_CUR_VAL(tkc)); if (!rpcio->name) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } /* Get the starting left brace for the sub-clauses */ res = ncx_consume_token(tkc, mod, TK_TT_LBRACE); CHK_OBJ_EXIT(obj, res, retres); /* get the container statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_TYPEDEF)) { res = yang_typ_consume_typedef(pcb, tkc, mod, &rpcio->typedefQ); } else if (!xml_strcmp(val, YANG_K_GROUPING)) { res = yang_grp_consume_grouping(pcb, tkc, mod, &rpcio->groupingQ, obj); } else { res = yang_obj_consume_datadef(pcb, tkc, mod, &rpcio->datadefQ, obj); } CHK_OBJ_EXIT(obj, res, retres); } /* save or delete the obj_template_t struct */ testobj = obj_find_template_test(que, obj_get_mod_name(obj), rpcio->name); if (testobj) { log_error("\nError: '%s' statement already defined at line %u", rpcio->name, testobj->tkerr.linenum); retres = ERR_NCX_DUP_ENTRY; ncx_print_errormsg(tkc, mod, retres); obj_free_template(obj); } else { obj_set_ncx_flags(obj); dlq_enque(obj, que); /* may have some errors */ } return retres; } /* consume_rpcio */ /******************************************************************** * FUNCTION consume_augdata * * Parse the next N tokens as a case-stmt * Create and fill in an obj_template_t struct * and add it to the datadefQ for the augment in progress * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the starting keyword of an object * that can be refined in a uses statement: * * container * leaf * leaf-list * list * choice * uses * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t struct that get created * parent == the obj_template_t containing the 'augment' param * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_augdata (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent) { const xmlChar *val = TK_CUR_VAL(tkc); tk_type_t tktyp = TK_CUR_TYP(tkc); status_t res = NO_ERR; boolean errdone = TRUE; /* check the current token type */ if (tktyp != TK_TT_TSTRING) { errdone = FALSE; res = ERR_NCX_WRONG_TKTYPE; } else { /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_ANYXML)) { res = consume_anyxml(tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_ANYDATA)) { res = consume_anydata(tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_CONTAINER)) { res = consume_container(pcb, tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_LEAF)) { res = consume_leaf(pcb, tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_LEAF_LIST)) { res = consume_leaflist(pcb, tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_LIST)) { res = consume_list(pcb, tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_CHOICE)) { res = consume_choice(pcb, tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_USES)) { res = consume_uses(pcb, tkc, mod, que, parent, NULL); } #ifdef ENABLE_DIRECT_MUST_AUGMENT_EX else if (!xml_strcmp(val, YANG_K_MUST) && parent->def.augment->direct_must_augment_ex) { res = consume_must(pcb, tkc, mod, &parent->def.augment->mustQ, parent /*parent->def.augment->targobj -- uninitialized here*/, NULL); } #endif else { errdone = FALSE; res = ERR_NCX_WRONG_TKVAL; } } if (res != NO_ERR && !errdone) { ncx_mod_exp_err(tkc, mod, res, "container, leaf, leaf-list, list, " "choice, or uses keyword"); } return res; } /* consume_augdata */ /******************************************************************** * FUNCTION consume_augment * * Parse the next N tokens as an augment-stmt * Create a obj_template_t struct and add it to the specified module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'augment' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == queue will get the obj_template_t * parent == parent object or NULL if top-level augment * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_augment (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { obj_template_t *obj = NULL; obj_augment_t *aug = NULL; const xmlChar *val = NULL; const char *expstr = "keyword"; yang_stmt_t *stmt = NULL; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, when = FALSE, stat = FALSE; boolean desc = FALSE, ref = FALSE; status_t res = NO_ERR, retres = NO_ERR; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_AUGMENT); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->nsid = mod->nsid; obj->grp = grp; if (que == &mod->datadefQ) { obj->flags |= (OBJ_FL_TOP | OBJ_FL_CONFSET | OBJ_FL_CONFIG); } aug = obj->def.augment; #ifdef ENABLE_DIRECT_MUST_AUGMENT_EX const xmlChar *objprefix = TK_CUR_MOD(tkc); if(objprefix!=NULL && !xml_strcmp(objprefix, "direct-must-augment-ex")) { aug->direct_must_augment_ex = TRUE; } else { aug->direct_must_augment_ex = FALSE; } #endif /* Get the mandatory augment target */ res = yang_consume_string(tkc, mod, &aug->target); CHK_OBJ_EXIT(obj, res, retres); /* Get the semi-colon or starting left brace for the sub-clauses */ res = consume_semi_lbrace(tkc, mod, obj, &done); CHK_OBJ_EXIT(obj, res, retres); /* get the sub-section statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a keyword token string so check the value */ if (!xml_strcmp(val, YANG_K_WHEN)) { res = yang_consume_when(tkc, mod, obj, &when); } else if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &obj->iffeatureQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &aug->status, &stat, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &aug->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &aug->ref, &ref, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_CASE)) { res = consume_case(pcb, tkc, mod, &aug->datadefQ, obj, TRUE); } else { res = consume_augdata(pcb, tkc, mod, &aug->datadefQ, obj); } CHK_OBJ_EXIT(obj, res, retres); } /* save or delete the obj_template_t struct */ if (aug->target) { obj_set_ncx_flags(obj); dlq_enque(obj, que); if (mod->stmtmode && que==&mod->datadefQ) { /* save top-level object order only */ stmt = yang_new_obj_stmt(obj); if (stmt) { dlq_enque(stmt, &mod->stmtQ); } else { log_error("\nError: malloc failure for obj_stmt"); res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); } } } else { obj_free_template(obj); } return retres; } /* consume_augment */ /******************************************************************** * FUNCTION consume_case_datadef * * Parse the next N tokens as a case-data-def-stmt * Create a obj_template_t struct and add it to the specified module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the first keyword, starting the specific * data definition * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == queue will get the obj_template_t * parent == parent object or NULL if top-level data-def-stmt * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_case_datadef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent) { const xmlChar *val = TK_CUR_VAL(tkc); const char *expstr = "container, leaf, leaf-list, list, uses," "or augment keyword"; tk_type_t tktyp = TK_CUR_TYP(tkc); boolean errdone = TRUE; status_t res = NO_ERR; /* check the current token type */ if (tktyp != TK_TT_TSTRING) { res = ERR_NCX_WRONG_TKTYPE; errdone = FALSE; } else { /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_ANYXML)) { res = consume_anyxml(tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_ANYDATA)) { res = consume_anydata(tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_CONTAINER)) { res = consume_container(pcb, tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_LEAF)) { res = consume_leaf(pcb, tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_LEAF_LIST)) { res = consume_leaflist(pcb, tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_LIST)) { res = consume_list(pcb, tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_CHOICE)) { res = consume_choice(pcb, tkc, mod, que, parent, NULL); } else if (!xml_strcmp(val, YANG_K_USES)) { res = consume_uses(pcb, tkc, mod, que, parent, NULL); } else { res = ERR_NCX_WRONG_TKVAL; errdone = FALSE; } } if (res != NO_ERR && !errdone) { ncx_mod_exp_err(tkc, mod, res, expstr); } return res; } /* consume_case_datadef */ /******************************************************************** * FUNCTION consume_rpc * * Parse the next N tokens as an rpc-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'rpc' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object or NULL if top-level * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_rpc (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { obj_template_t *obj = NULL, *chobj = NULL; const obj_template_t *testobj = NULL; obj_rpc_t *rpc = NULL; const xmlChar *val = NULL; const char *expstr = "keyword"; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, stat = FALSE; boolean desc = FALSE, ref = FALSE; status_t res = NO_ERR, retres = NO_ERR; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_RPC); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->nsid = mod->nsid; obj->grp = grp; if (que == &mod->datadefQ) { obj->flags |= (OBJ_FL_TOP | OBJ_FL_CONFSET | OBJ_FL_CONFIG); } rpc = obj->def.rpc; /* Get the mandatory RPC method name */ res = yang_consume_id_string(tkc, mod, &rpc->name); CHK_OBJ_EXIT(obj, res, retres); res = consume_semi_lbrace(tkc, mod, obj, &done); CHK_OBJ_EXIT(obj, res, retres); /* get the container statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &obj->iffeatureQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_TYPEDEF)) { res = yang_typ_consume_typedef(pcb, tkc, mod, &rpc->typedefQ); } else if (!xml_strcmp(val, YANG_K_GROUPING)) { res = yang_grp_consume_grouping(pcb, tkc, mod, &rpc->groupingQ, obj); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &rpc->status, &stat, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &rpc->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &rpc->ref, &ref, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_INPUT) || !xml_strcmp(val, YANG_K_OUTPUT)) { res = consume_rpcio(pcb, tkc, mod, &rpc->datadefQ, obj); } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_OBJ_EXIT(obj, res, retres); } /* save or delete the obj_template_t struct */ if (rpc->name && ncx_valid_name2(rpc->name)) { /* make sure the rpc node has an input and output node * for augment purposes */ testobj = obj_find_child(obj, NULL, YANG_K_INPUT); if (!testobj) { chobj = obj_new_template(OBJ_TYP_RPCIO); if (!chobj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } ncx_set_error(&chobj->tkerr, mod, obj->tkerr.linenum, obj->tkerr.linepos); chobj->parent = obj; chobj->def.rpcio->name = xml_strdup(YANG_K_INPUT); if (!chobj->def.rpcio->name) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_template(chobj); obj_free_template(obj); return res; } obj_set_ncx_flags(chobj); dlq_enque(chobj, &obj->def.rpc->datadefQ); } testobj = obj_find_child(obj, NULL, YANG_K_OUTPUT); if (!testobj) { chobj = obj_new_template(OBJ_TYP_RPCIO); if (!chobj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } ncx_set_error(&chobj->tkerr, mod, obj->tkerr.linenum, obj->tkerr.linepos); chobj->parent = obj; chobj->def.rpcio->name = xml_strdup(YANG_K_OUTPUT); if (!chobj->def.rpcio->name) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_template(chobj); obj_free_template(obj); return res; } obj_set_ncx_flags(chobj); dlq_enque(chobj, &obj->def.rpc->datadefQ); } res = add_object(tkc, mod, que, obj); CHK_EXIT(res, retres); } else { obj_free_template(obj); } return retres; } /* consume_rpc */ /******************************************************************** * FUNCTION consume_action * * Parse the next N tokens as an action-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'action' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object or NULL if top-level * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_action (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { obj_template_t *obj = NULL, *chobj = NULL; const obj_template_t *testobj = NULL; obj_rpc_t *rpc = NULL; const xmlChar *val = NULL; const char *expstr = "keyword"; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, stat = FALSE; boolean desc = FALSE, ref = FALSE; status_t res = NO_ERR, retres = NO_ERR; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_RPC); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->nsid = mod->nsid; obj->grp = grp; if (que == &mod->datadefQ) { obj->flags |= (OBJ_FL_TOP | OBJ_FL_CONFSET | OBJ_FL_CONFIG); } rpc = obj->def.rpc; /* Get the mandatory RPC method name */ res = yang_consume_id_string(tkc, mod, &rpc->name); CHK_OBJ_EXIT(obj, res, retres); res = consume_semi_lbrace(tkc, mod, obj, &done); CHK_OBJ_EXIT(obj, res, retres); /* get the container statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &obj->iffeatureQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_TYPEDEF)) { res = yang_typ_consume_typedef(pcb, tkc, mod, &rpc->typedefQ); } else if (!xml_strcmp(val, YANG_K_GROUPING)) { res = yang_grp_consume_grouping(pcb, tkc, mod, &rpc->groupingQ, obj); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &rpc->status, &stat, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &rpc->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &rpc->ref, &ref, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_INPUT) || !xml_strcmp(val, YANG_K_OUTPUT)) { res = consume_rpcio(pcb, tkc, mod, &rpc->datadefQ, obj); } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_OBJ_EXIT(obj, res, retres); } /* save or delete the obj_template_t struct */ if (rpc->name && ncx_valid_name2(rpc->name)) { /* make sure the rpc node has an input and output node * for augment purposes */ testobj = obj_find_child(obj, NULL, YANG_K_INPUT); if (!testobj) { chobj = obj_new_template(OBJ_TYP_RPCIO); if (!chobj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } ncx_set_error(&chobj->tkerr, mod, obj->tkerr.linenum, obj->tkerr.linepos); chobj->parent = obj; chobj->def.rpcio->name = xml_strdup(YANG_K_INPUT); if (!chobj->def.rpcio->name) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_template(chobj); obj_free_template(obj); return res; } obj_set_ncx_flags(chobj); dlq_enque(chobj, &obj->def.rpc->datadefQ); } testobj = obj_find_child(obj, NULL, YANG_K_OUTPUT); if (!testobj) { chobj = obj_new_template(OBJ_TYP_RPCIO); if (!chobj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } ncx_set_error(&chobj->tkerr, mod, obj->tkerr.linenum, obj->tkerr.linepos); chobj->parent = obj; chobj->def.rpcio->name = xml_strdup(YANG_K_OUTPUT); if (!chobj->def.rpcio->name) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_template(chobj); obj_free_template(obj); return res; } obj_set_ncx_flags(chobj); dlq_enque(chobj, &obj->def.rpc->datadefQ); } res = add_object(tkc, mod, que, obj); CHK_EXIT(res, retres); } else { obj_free_template(obj); } return retres; } /* consume_action */ /******************************************************************** * Handle an unsupported deviate operation * * \note Error messages are printed by this function!! * Do not duplicate error messages upon error return * * \param tkc the token chain * \param mod the module in progress * \param targobj the target object to apply deviations to * \param devi the 'deviate' operation to apply * \return status of the operation *********************************************************************/ static status_t apply_object_deviate_not_supported( tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *targobj, obj_deviate_t *devi ) { /* make sure not deleting a key leaf */ if (obj_is_key(targobj)) { log_error( "\nError: cannot remove key leaf %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); return set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT); } /* remove the node and toss it out */ YANG_OBJ_DEBUG2( "\napply_dev: mark target obj %s:%s for removal", obj_get_mod_name(targobj), obj_get_name(targobj) ); targobj->flags |= OBJ_FL_DELETED; return NO_ERR; } /******************************************************************** * Apply a obj_deviate_t type statement * * \param targobj the target object to apply deviations to * \param deviation the deviation to apply * \return status of the operation *********************************************************************/ static status_t apply_object_deviate_type( obj_template_t *targobj, obj_deviate_t *devi ) { /* only allowed arg is replace, so replace the typdef; */ /* FIXME: What if arg is not a replace operation? Should this be * handled? */ YANG_OBJ_DEBUG3( "\napply_dev: replacing type in target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj) ); switch (targobj->objtype) { case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: if (targobj->flags & OBJ_FL_CLONE == 0) typ_free_typdef(targobj->def.leaf->typdef); targobj->def.leaf->typdef = devi->typdef; devi->typdef = NULL; break; case OBJ_TYP_LEAF_LIST: if (targobj->flags & OBJ_FL_CLONE == 0) typ_free_typdef(targobj->def.leaflist->typdef); targobj->def.leaflist->typdef = devi->typdef; devi->typdef = NULL; break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return NO_ERR; } /******************************************************************** * Selecte the apporpriate strargs for a units deviate * * \param targobj the target object to apply deviations to * \return the strargs type; ********************************************************************/ static xmlChar** select_units_strarg( obj_template_t* targobj ) { switch (targobj->objtype) { case OBJ_TYP_LEAF: return &targobj->def.leaf->units; break; case OBJ_TYP_LEAF_LIST: return &targobj->def.leaflist->units; break; default: break; } return NULL; } /******************************************************************** * apply a obj_deviate_t units statement * * \param targobj the target object to apply deviations to * \param deviation the deviation to apply * \return status of the operation ********************************************************************/ static status_t apply_object_deviate_units( obj_template_t *targobj, obj_deviate_t *devi ) { xmlChar **strarg = select_units_strarg( targobj ); if ( !strarg ) { return SET_ERROR(ERR_INTERNAL_VAL); } switch (devi->arg) { case OBJ_DARG_ADD: YANG_OBJ_DEBUG3( "\napply_dev: adding units to target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); if (*strarg ) { SET_ERROR(ERR_INTERNAL_VAL); m__free(*strarg); } *strarg = devi->units; devi->units = NULL; break; case OBJ_DARG_DELETE: YANG_OBJ_DEBUG3( "\napply_dev: deleting units in target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj) ); if ( *strarg == NULL ) { SET_ERROR(ERR_INTERNAL_VAL); } m__free(*strarg); *strarg = NULL; break; case OBJ_DARG_REPLACE: if ( *strarg == NULL) { SET_ERROR(ERR_INTERNAL_VAL); } else { YANG_OBJ_DEBUG3( "\napply_dev: replacing units in target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); m__free(*strarg); *strarg = devi->units; devi->units = NULL; } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return NO_ERR; } /******************************************************************** * Selecte the apporpriate strargs for a units deviate * * \param targobj the target object to apply deviations to * \return the strargs type; ********************************************************************/ static xmlChar** select_default_strarg( obj_template_t* targobj ) { switch (targobj->objtype) { case OBJ_TYP_LEAF: return &targobj->def.leaf->defval; break; case OBJ_TYP_CHOICE: return &targobj->def.choic->defval; break; default: break; } return NULL; } /******************************************************************** * Apply a obj_deviate_t default statement * * \param targobj the target object to apply deviations to * \param deviation the deviation to apply * \return status of the operation *********************************************************************/ static status_t apply_object_deviate_default( obj_template_t *targobj, obj_deviate_t *devi ) { xmlChar **strarg = select_default_strarg( targobj ); if ( !strarg ) { return SET_ERROR(ERR_INTERNAL_VAL); } switch (devi->arg) { case OBJ_DARG_ADD: if (*strarg) { SET_ERROR(ERR_INTERNAL_VAL); } else { YANG_OBJ_DEBUG3( "\napply_dev: adding default to target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); *strarg = devi->defval; devi->defval = NULL; } break; case OBJ_DARG_DELETE: if (*strarg == NULL) { SET_ERROR(ERR_INTERNAL_VAL); } else { YANG_OBJ_DEBUG3( "\napply_dev: deleting default in target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); m__free(*strarg); *strarg = NULL; } break; case OBJ_DARG_REPLACE: if (*strarg == NULL) { SET_ERROR(ERR_INTERNAL_VAL); } else { YANG_OBJ_DEBUG3( "\napply_dev: replacing default in target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); m__free(*strarg); *strarg = devi->units; devi->units = NULL; } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return NO_ERR; } /******************************************************************** * Apply a obj_deviate_t config statement * * \param targobj the target object to apply deviations to * \param deviation the deviation to apply * \return status of the operation *********************************************************************/ static void apply_object_deviate_config( obj_template_t *targobj, obj_deviate_t *devi ) { YANG_OBJ_DEBUG3( "\napply_dev: replacing config-stmt in target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj) ); targobj->flags |= OBJ_FL_CONFSET; if (devi->config) { targobj->flags |= OBJ_FL_CONFIG; } else { targobj->flags &= ~OBJ_FL_CONFIG; } } /******************************************************************** * Apply a obj_deviate_t mandatory statement * * \param targobj the target object to apply deviations to * \param deviation the deviation to apply * \return status of the operation *********************************************************************/ static void apply_object_deviate_mandatory( obj_template_t *targobj, obj_deviate_t *devi ) { YANG_OBJ_DEBUG3( "\napply_dev: replacing mandatory-stmt in target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); targobj->flags |= OBJ_FL_MANDSET; if (devi->mandatory) { targobj->flags |= OBJ_FL_MANDATORY; } else { targobj->flags &= ~OBJ_FL_MANDATORY; } } /******************************************************************** * Apply a obj_deviate_t min elements statement * * \param targobj the target object to apply deviations to * \param deviation the deviation to apply * \return status of the operation *********************************************************************/ static status_t apply_object_deviate_minelems( obj_template_t *targobj, obj_deviate_t *devi ) { YANG_OBJ_DEBUG3( "\napply_dev: replacing min-elements in target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); switch (targobj->objtype) { case OBJ_TYP_LIST: targobj->def.list->minelems = devi->minelems; break; case OBJ_TYP_LEAF_LIST: targobj->def.leaflist->minelems = devi->minelems; break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return NO_ERR; } /******************************************************************** * Apply a obj_deviate_t max elements statement * * \param targobj the target object to apply deviations to * \param deviation the deviation to apply * \return status of the operation *********************************************************************/ static status_t apply_object_deviate_maxelems( obj_template_t *targobj, obj_deviate_t *devi ) { YANG_OBJ_DEBUG3( "\napply_dev: replacing max-elements in target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); switch (targobj->objtype) { case OBJ_TYP_LIST: targobj->def.list->maxelems = devi->maxelems; break; case OBJ_TYP_LEAF_LIST: targobj->def.leaflist->maxelems = devi->maxelems; break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return NO_ERR; } /******************************************************************** * Apply a obj_deviate_t mustQ statement * * \param targobj the target object to apply deviations to * \param deviation the deviation to apply * \return status of the operation *********************************************************************/ static status_t apply_object_deviate_mustQ( obj_template_t *targobj, obj_deviate_t *devi ) { status_t res = NO_ERR; dlq_hdr_t *targQ = obj_get_mustQ(targobj); if (!targQ) { return SET_ERROR(ERR_INTERNAL_VAL); } switch (devi->arg) { case OBJ_DARG_ADD: YANG_OBJ_DEBUG3( "\napply_dev: adding must-stmt(s) to target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); dlq_block_enque(&devi->mustQ, targQ); break; case OBJ_DARG_DELETE: YANG_OBJ_DEBUG3( "\napply_dev: removing must-stmt(s) from target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); { xpath_pcb_t* must = (xpath_pcb_t *)dlq_firstEntry(&devi->mustQ); for ( ; must ; must = (xpath_pcb_t *)dlq_nextEntry(must)) { xpath_pcb_t* targmust = xpath_find_pcb( targQ, must->exprstr ); if (targmust) { dlq_remove(targmust); xpath_free_pcb(targmust); } else { // Note: loop does not exit in first error res = SET_ERROR(ERR_INTERNAL_VAL); } } } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /******************************************************************** * Apply a obj_deviate_t unique statement * * \param targobj the target object to apply deviations to * \param deviation the deviation to apply * \return status of the operation *********************************************************************/ static status_t apply_object_deviate_unique( obj_template_t *targobj, obj_deviate_t *devi ) { if (targobj->objtype != OBJ_TYP_LIST) { return SET_ERROR(ERR_INTERNAL_VAL); } dlq_hdr_t *targQ = &targobj->def.list->uniqueQ; if (!targQ) { return SET_ERROR(ERR_INTERNAL_VAL); } status_t res = NO_ERR; switch (devi->arg) { case OBJ_DARG_ADD: YANG_OBJ_DEBUG3( "\napply_dev: adding unique-stmt(s) to target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); dlq_block_enque(&devi->uniqueQ, targQ); break; case OBJ_DARG_DELETE: YANG_OBJ_DEBUG3( "\napply_dev: removing unique-stmt(s) from target obj %s:%s", obj_get_mod_name(targobj), obj_get_name(targobj)); { obj_unique_t* unique=(obj_unique_t *) dlq_firstEntry(&devi->uniqueQ); for ( ; unique ; unique = (obj_unique_t *)dlq_nextEntry(unique)) { obj_unique_t* targunique = obj_find_unique(targQ, unique->xpath); if (targunique) { dlq_remove(targunique); obj_free_unique(targunique); } else { res = SET_ERROR(ERR_INTERNAL_VAL); } } } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); break; } return res; } /******************************************************************** * Apply the Q of deviations to the specified object * \note Error messages are printed by this function!! * Do not duplicate error messages upon error return * * \param pcb the parser control block to use * \param tkc the token chain * \param mod the module in progress * \param targobj the target object to check for deviations pending * \param deviation the deviation to apply * \return status of the operation *********************************************************************/ static status_t apply_object_deviations( yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *targobj, obj_deviation_t *deviation ) { status_t res = NO_ERR; boolean retest = FALSE; obj_deviate_t *devi = (obj_deviate_t*) dlq_firstEntry(&deviation->deviateQ); for ( ; devi && res == NO_ERR ; devi = (obj_deviate_t *)dlq_nextEntry(devi)) { if (devi->arg == OBJ_DARG_NOT_SUPPORTED ) { return apply_object_deviate_not_supported(tkc, mod, targobj, devi); } if( devi->arg == OBJ_DARG_NONE ) { return SET_ERROR(ERR_INTERNAL_VAL); } /* type-stmt */ if ( devi->typdef) { retest = TRUE; res = apply_object_deviate_type( targobj, devi ); if ( NO_ERR != res ) { return res; } } /* units-stmt */ if ( devi->units ) { // FIXME: Is retest required? res = apply_object_deviate_units( targobj, devi ); if ( NO_ERR != res ) { return res; } } /* default-stmt */ if (devi->defval) { retest = TRUE; res = apply_object_deviate_default( targobj, devi ); if ( NO_ERR != res ) { return res; } } /* config-stmt */ if (devi->config_tkerr.mod) { retest = TRUE; apply_object_deviate_config( targobj, devi ); } /* mandatory-stmt */ if (devi->mandatory_tkerr.mod) { retest = TRUE; apply_object_deviate_mandatory( targobj, devi ); } /* min-elements-stmt */ if (devi->minelems_tkerr.mod) { retest = TRUE; res = apply_object_deviate_minelems( targobj, devi ); if ( NO_ERR != res ) { return res; } } /* max-elements-stmt */ if (devi->minelems_tkerr.mod) { retest = TRUE; res = apply_object_deviate_minelems( targobj, devi ); if ( NO_ERR != res ) { return res; } } /* max-elements-stmt */ if (devi->maxelems_tkerr.mod) { retest = TRUE; res = apply_object_deviate_maxelems( targobj, devi ); if ( NO_ERR != res ) { return res; } } /* must-stmt */ if (!dlq_empty(&devi->mustQ)) { // FIXME: Is retest required? res = apply_object_deviate_mustQ( targobj, devi ); if ( NO_ERR != res ) { return res; } } /* unique-stmt */ if (!dlq_empty(&devi->uniqueQ)) { // FIXME: Is retest required? res = apply_object_deviate_unique( targobj, devi ); if ( NO_ERR != res ) { return res; } } } if ( retest ) { YANG_OBJ_DEBUG3( "\nRechecking %s:%s after applying deviation(s)", obj_get_mod_name(targobj), obj_get_name(targobj) ); res = resolve_datadef(pcb, tkc, mod, targobj, TRUE ); } return res; } /* apply_object_deviations */ /******************************************************************** * Apply all the deviations to the specified object * \note Error messages are printed by this function!! * Do not duplicate error messages upon error return * * \param pcb the parser control block to use * \param tkc the token chain * \param mod the module in progress * \return status of the operation *********************************************************************/ static status_t apply_all_object_deviations( yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod ) { status_t res = NO_ERR; obj_deviation_t *deviation = (obj_deviation_t *) dlq_firstEntry(&mod->deviationQ); for ( ; deviation && res == NO_ERR; deviation = (obj_deviation_t *) dlq_nextEntry(deviation)) { assert(deviation->targobj); /* make sure not already processed all the deviate structs from * this deviation have been moved to the object deviate Q */ res = apply_object_deviations( pcb, tkc, mod, deviation->targobj, deviation ); if ( terminate_parse( res ) ) { return res; } } if (!pcb->stmtmode) { /* don't need these anymore for agent */ while (!dlq_empty(&mod->deviationQ)) { deviation = (obj_deviation_t *) dlq_deque(&mod->deviationQ); obj_free_deviation(deviation); } } return res; } /* apply_all_object_deviations */ /******************************************************************** * FUNCTION check_deviate_collision * * Check if the specified obj_deviate_t stmt would * overlap with any of the existing deviate-stmts * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * devi == current obj_deviate_t to validate against the rest * deviateQ == Q of obj_deviate_t structs to check against * * RETURNS: * status of the operation *********************************************************************/ static status_t check_deviate_collision (tk_chain_t *tkc, ncx_module_t *mod, obj_deviate_t *devi, dlq_hdr_t *deviateQ) { obj_deviate_t *testdevi; status_t res; res = NO_ERR; /* check valid deviation-stmt syntax */ if (devi->arg != OBJ_DARG_NOT_SUPPORTED) { for (testdevi = (obj_deviate_t *)dlq_firstEntry(deviateQ); testdevi != NULL; testdevi = (obj_deviate_t *)dlq_nextEntry(testdevi)) { /* check the deviateQ to see if a 'not-supported' clause * already entered; if so, call it a fatal error */ if (testdevi->arg == OBJ_DARG_NOT_SUPPORTED) { log_error("\nError: not-supported deviate-stmt " "already entered on line %u", testdevi->tkerr.linenum); res = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } else { /* make sure none of the same sub-stmts are * touched in these 2 deviate structs */ if (devi->typdef && testdevi->typdef) { log_error("\nError: 'type' deviate-stmt already entered " "on line %u", testdevi->tkerr.linenum); res = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } if (devi->units && testdevi->units) { log_error("\nError: 'units' deviate-stmt " "already entered on line %u", testdevi->tkerr.linenum); res = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } if (devi->defval && testdevi->defval) { log_error("\nError: 'default' deviate-stmt " "already entered on line %u", testdevi->tkerr.linenum); res = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } if (devi->config_tkerr.mod && testdevi->config_tkerr.mod) { log_error("\nError: 'config' deviate-stmt " "already entered on line %u", testdevi->tkerr.linenum); res = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } if (devi->mandatory_tkerr.mod && testdevi->mandatory_tkerr.mod){ log_error("\nError: 'mandatory' deviate-stmt " "already entered on line %u", testdevi->tkerr.linenum); res = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } if (devi->minelems_tkerr.mod && testdevi->minelems_tkerr.mod) { log_error("\nError: 'min-elements' deviate-stmt " "already entered on line %u", testdevi->tkerr.linenum); res = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } if (devi->maxelems_tkerr.mod && testdevi->maxelems_tkerr.mod) { log_error("\nError: 'max-elements' deviate-stmt " "already entered on line %u", testdevi->tkerr.linenum); res = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } /**** check devi->mustQ against testdevi->mustQ **** just ignore them for now ****/ /**** check devi->uniqueQ against testdevi->uniqueQ **** just ignore them for now ****/ } } } else { /* adding a not-supported, so make sure the Q is empty */ if (!dlq_empty(deviateQ)) { log_error("\nError: 'not-supported' deviate-stmt " "not allowed"); res = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } return res; } /* check_deviate_collision */ /******************************************************************** * FUNCTION normalize_deviationQ * * Check for overlapping deviation statements * combine any deviation statements for the same target * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * * RETURNS: * status of the operation *********************************************************************/ static status_t normalize_deviationQ (tk_chain_t *tkc, ncx_module_t *mod) { obj_deviation_t *curdev, *checkdev, *nextdev; obj_deviate_t *deviate; status_t res, retres; if (dlq_count(&mod->deviationQ) < 2) { return NO_ERR; } res = NO_ERR; retres = NO_ERR; /* there are at least 2 entries, so check the whole Q */ curdev = (obj_deviation_t *)dlq_firstEntry(&mod->deviationQ); while (curdev != NULL) { if (curdev->targobj == NULL || curdev->targobj->tkerr.mod != mod) { curdev = (obj_deviation_t *)dlq_nextEntry(curdev); continue; } for (checkdev = (obj_deviation_t *)dlq_nextEntry(curdev); checkdev != NULL; checkdev = nextdev) { nextdev = (obj_deviation_t *)dlq_nextEntry(checkdev); if (checkdev->targobj == curdev->targobj) { /* have a match; remove this entry * and combine it with the current deviation */ dlq_remove(checkdev); dlq_block_enque(&checkdev->appinfoQ, &curdev->appinfoQ); while (!dlq_empty(&checkdev->deviateQ)) { deviate = (obj_deviate_t *) dlq_deque(&checkdev->deviateQ); res = check_deviate_collision(tkc, mod, deviate, &curdev->deviateQ); if (res != NO_ERR) { retres = res; obj_free_deviate(deviate); } else { dlq_enque(deviate, &curdev->deviateQ); } } obj_free_deviation(checkdev); } } /* move through the Q and keep checking for duplicates */ curdev = (obj_deviation_t *)dlq_nextEntry(curdev); } return retres; } /* normalize_deviationQ */ /******************************************************************** * FUNCTION consume_deviate * * Parse the next N tokens as a deviate-stmt * Create a obj_deviate_t struct and add it to the * specified deviation->deviateQ * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'deviate' keyword * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * deviation == parent obj_deviation_t to hold the new obj_deviate_t * created by this function * * OUTPUTS: * new deviate struct added to deviation deviateQ * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_deviate (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_deviation_t *deviation) { obj_deviate_t *devi; obj_unique_t *uniq; typ_def_t *dummy; const xmlChar *val; const char *expstr; xmlChar *str; tk_type_t tktyp; boolean done, type, units, def, conf; boolean mand, minel, maxel; status_t res, retres; /* Get a new obj_deviation_t to fill in */ devi = obj_new_deviate(); if (!devi) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } val = NULL; expstr = "add, replace, delete, or not-supported"; str = NULL; done = FALSE; type = FALSE; units = FALSE; def = FALSE; conf = FALSE; mand = FALSE; minel = FALSE; maxel = FALSE; retres = NO_ERR; ncx_set_error(&devi->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); /* Get the mandatory deviation argument */ res = yang_consume_string(tkc, mod, &str); CHK_DEVI_EXIT(devi, res, retres); if (res == NO_ERR) { /* check the value */ if (!xml_strcmp(str, YANG_K_ADD)) { devi->arg = OBJ_DARG_ADD; } else if (!xml_strcmp(str, YANG_K_DELETE)) { devi->arg = OBJ_DARG_DELETE; } else if (!xml_strcmp(str, YANG_K_REPLACE)) { devi->arg = OBJ_DARG_REPLACE; } else if (!xml_strcmp(str, YANG_K_NOT_SUPPORTED)) { devi->arg = OBJ_DARG_NOT_SUPPORTED; } else { log_error("\nError: invalid deviate-stmt " "argument '%s'", str); retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); } } if (str) { m__free(str); str = NULL; } /* Get the starting left brace or semi-colon */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_deviate(devi); return res; } /* check for semi-colon or left brace */ switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: /* only 1 arg type allowed to be empty */ if (devi->arg == OBJ_DARG_NOT_SUPPORTED) { devi->empty = TRUE; } else { retres = ERR_NCX_WRONG_TKTYPE; expstr = "left brace"; ncx_mod_exp_err(tkc, mod, retres, expstr); } done = TRUE; break; case TK_TT_LBRACE: done = FALSE; break; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; } expstr = "deviate-stmt sub-clause"; /* get the sub-section statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_deviate(devi); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_deviate(devi); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &devi->appinfoQ); CHK_DEVI_EXIT(devi, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a keyword token string so check the value */ if (!xml_strcmp(val, YANG_K_TYPE)) { ncx_set_error(&devi->type_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); switch (devi->arg) { case OBJ_DARG_NONE: /* argument was invalid so cannot check it further */ break; case OBJ_DARG_ADD: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: type-stmt cannot be added"); ncx_print_errormsg(tkc, mod, retres); break; case OBJ_DARG_DELETE: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: type-stmt cannot be deleted"); ncx_print_errormsg(tkc, mod, retres); break; case OBJ_DARG_REPLACE: /* only allowed verb is 'replace' for type-stmt */ break; case OBJ_DARG_NOT_SUPPORTED: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: sub-clauses not allowed " "for 'not-supported'"); ncx_print_errormsg(tkc, mod, retres); break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); } if (type) { log_error("\nError: type-stmt already entered"); retres = ERR_NCX_ENTRY_EXISTS; dummy = typ_new_typdef(); if (!dummy) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_deviate(devi); return res; } else { res = yang_typ_consume_type(pcb, tkc, mod, dummy); typ_free_typdef(dummy); } } else { type = TRUE; devi->typdef = typ_new_typdef(); if (!devi->typdef) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_deviate(devi); return res; } else { res = yang_typ_consume_type(pcb, tkc, mod, devi->typdef); } } } else if (!xml_strcmp(val, YANG_K_UNITS)) { ncx_set_error(&devi->units_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); switch (devi->arg) { case OBJ_DARG_NONE: /* argument was invalid so cannot check it further */ break; case OBJ_DARG_ADD: break; case OBJ_DARG_DELETE: break; case OBJ_DARG_REPLACE: break; case OBJ_DARG_NOT_SUPPORTED: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: sub-clauses not allowed " "for 'not-supported'"); ncx_print_errormsg(tkc, mod, retres); break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); } res = yang_consume_strclause(tkc, mod, &devi->units, &units, &devi->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DEFAULT)) { ncx_set_error(&devi->default_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); switch (devi->arg) { case OBJ_DARG_NONE: /* argument was invalid so cannot check it further */ break; case OBJ_DARG_ADD: break; case OBJ_DARG_DELETE: break; case OBJ_DARG_REPLACE: break; case OBJ_DARG_NOT_SUPPORTED: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: sub-clauses not allowed " "for 'not-supported'"); ncx_print_errormsg(tkc, mod, retres); break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); } res = yang_consume_strclause(tkc, mod, &devi->defval, &def, &devi->appinfoQ); } else if (!xml_strcmp(val, YANG_K_CONFIG)) { ncx_set_error(&devi->config_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); switch (devi->arg) { case OBJ_DARG_NONE: /* argument was invalid so cannot check it further */ break; case OBJ_DARG_ADD: break; case OBJ_DARG_DELETE: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: config-stmt cannot be deleted"); ncx_print_errormsg(tkc, mod, retres); break; case OBJ_DARG_REPLACE: break; case OBJ_DARG_NOT_SUPPORTED: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: sub-clauses not allowed " "for 'not-supported'"); ncx_print_errormsg(tkc, mod, retres); break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); } res = yang_consume_boolean(tkc, mod, &devi->config, &conf, &devi->appinfoQ); } else if (!xml_strcmp(val, YANG_K_MANDATORY)) { ncx_set_error(&devi->mandatory_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); switch (devi->arg) { case OBJ_DARG_NONE: /* argument was invalid so cannot check it further */ break; case OBJ_DARG_ADD: break; case OBJ_DARG_DELETE: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: mandatory-stmt cannot be deleted"); ncx_print_errormsg(tkc, mod, retres); break; case OBJ_DARG_REPLACE: break; case OBJ_DARG_NOT_SUPPORTED: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: sub-clauses not allowed " "for 'not-supported'"); ncx_print_errormsg(tkc, mod, retres); break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); } res = yang_consume_boolean(tkc, mod, &devi->mandatory, &mand, &devi->appinfoQ); } else if (!xml_strcmp(val, YANG_K_MIN_ELEMENTS)) { ncx_set_error(&devi->minelems_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); switch (devi->arg) { case OBJ_DARG_NONE: /* argument was invalid so cannot check it further */ break; case OBJ_DARG_ADD: break; case OBJ_DARG_DELETE: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: min-elements-stmt cannot be deleted"); ncx_print_errormsg(tkc, mod, retres); break; case OBJ_DARG_REPLACE: break; case OBJ_DARG_NOT_SUPPORTED: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: sub-clauses not allowed " "for 'not-supported'"); ncx_print_errormsg(tkc, mod, retres); break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); } res = yang_consume_uint32(tkc, mod, &devi->minelems, &minel, &devi->appinfoQ); } else if (!xml_strcmp(val, YANG_K_MAX_ELEMENTS)) { ncx_set_error(&devi->maxelems_tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); switch (devi->arg) { case OBJ_DARG_NONE: /* argument was invalid so cannot check it further */ break; case OBJ_DARG_ADD: break; case OBJ_DARG_DELETE: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: max-elements-stmt cannot be deleted"); ncx_print_errormsg(tkc, mod, retres); break; case OBJ_DARG_REPLACE: break; case OBJ_DARG_NOT_SUPPORTED: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: sub-clauses not allowed " "for 'not-supported'"); ncx_print_errormsg(tkc, mod, retres); break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); } res = yang_consume_max_elements(tkc, mod, &devi->maxelems, &maxel, &devi->appinfoQ); } else if (!xml_strcmp(val, YANG_K_MUST)) { switch (devi->arg) { case OBJ_DARG_NONE: /* argument was invalid so cannot check it further */ break; case OBJ_DARG_ADD: break; case OBJ_DARG_DELETE: break; case OBJ_DARG_REPLACE: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: must-stmt cannot be replaced"); ncx_print_errormsg(tkc, mod, retres); break; case OBJ_DARG_NOT_SUPPORTED: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: sub-clauses not allowed " "for 'not-supported'"); ncx_print_errormsg(tkc, mod, retres); break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); } res = yang_consume_must(tkc, mod, &devi->mustQ, &devi->appinfoQ); } else if (!xml_strcmp(val, YANG_K_UNIQUE)) { uniq = obj_new_unique(); if (!uniq) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); obj_free_deviate(devi); return res; } ncx_set_error(&uniq->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); switch (devi->arg) { case OBJ_DARG_NONE: /* argument was invalid so cannot check it further */ break; case OBJ_DARG_ADD: break; case OBJ_DARG_DELETE: break; case OBJ_DARG_REPLACE: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: unique-stmt cannot be replaced"); ncx_print_errormsg(tkc, mod, retres); break; case OBJ_DARG_NOT_SUPPORTED: retres = ERR_NCX_INVALID_DEV_STMT; log_error("\nError: sub-clauses not allowed " "for 'not-supported'"); ncx_print_errormsg(tkc, mod, retres); break; default: retres = SET_ERROR(ERR_INTERNAL_VAL); } res = yang_consume_strclause(tkc, mod, &uniq->xpath, NULL, &devi->appinfoQ); CHK_DEVI_EXIT(devi, res, retres); if (res == NO_ERR) { dlq_enque(uniq, &devi->uniqueQ); } else { obj_free_unique(uniq); } } else { retres = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, retres, expstr); } CHK_DEVI_EXIT(devi, res, retres); } /* check valid deviation-stmt syntax */ if (retres == NO_ERR) { retres = check_deviate_collision(tkc, mod, devi, &deviation->deviateQ); } /* not going to resolve this deviate-stmt if it has errors */ if (retres != NO_ERR) { obj_free_deviate(devi); } else { dlq_enque(devi, &deviation->deviateQ); } return retres; } /* consume_deviate */ /************ R E S O L V E F U N C T I O N S ***************/ /******************************************************************** * FUNCTION check_parent * * Check the node against its parent * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * obj == object to check, only if obj != NULL * and obj->parent != NULL * * RETURNS: * status of the operation *********************************************************************/ static status_t check_parent (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj) { status_t res; ncx_status_t stat, parentstat; boolean conf, parentconf, ingrp1, ingrp2; res = NO_ERR; /* check status stmt against the parent, if any */ if (obj && obj->parent && !obj_is_root(obj->parent)) { if (!obj_is_refine(obj)) { stat = obj_get_status(obj); parentstat = obj_get_status(obj->parent); /* check invalid status warning */ if (stat < parentstat) { if (ncx_warning_enabled(ERR_NCX_INVALID_STATUS)) { log_warn("\nWarning: Invalid status: " "child node '%s' = '%s' and" " parent node '%s' = '%s'", obj_get_name(obj), ncx_get_status_string(stat), obj_get_name(obj->parent), ncx_get_status_string(parentstat)); SET_OBJ_CURERR(tkc, obj); ncx_print_errormsg(tkc, mod, ERR_NCX_INVALID_STATUS); } else if (mod != NULL) { ncx_inc_warnings(mod); } } } /* check invalid config flag error for real object only */ if (obj->objtype <= OBJ_TYP_CASE && obj->parent->objtype <= OBJ_TYP_CASE) { ingrp1 = ingrp2 = FALSE; conf = obj_get_config_flag_check(obj, &ingrp1); parentconf = obj_get_config_flag_deep(obj->parent); if ((!parentconf && conf) && (!ingrp1 && !ingrp2)) { if (obj_is_data(obj)) { log_error("\nError: Node '%s' is marked as configuration, " "but parent node '%s' is not", obj_get_name(obj), obj_get_name(obj->parent)); SET_OBJ_CURERR(tkc, obj); res = ERR_NCX_INVALID_VALUE; ncx_print_errormsg(tkc, mod, res); } else { log_info("\nInfo: Non-data node '%s' " "is marked as configuration : statement ignored", obj_get_name(obj)); SET_OBJ_CURERR(tkc, obj); res = ERR_NCX_STMT_IGNORED; ncx_print_errormsg(tkc, mod, res); } } } } return res; } /* check_parent */ /******************************************************************** * FUNCTION resolve_default_parm * * Check the rpc input or container object type * to see if a CLI default-parm was defined. If so, * find the target object. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * obj == parent object for 'rpcio' or 'list' * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_default_parm (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj) { obj_template_t *targobj; ncx_appinfo_t *appinfo; status_t res; res = NO_ERR; if (obj->objtype == OBJ_TYP_CONTAINER || (obj->objtype == OBJ_TYP_RPCIO && !xml_strcmp(obj_get_name(obj), YANG_K_INPUT))) { appinfo = ncx_find_appinfo(&obj->appinfoQ, NCX_PREFIX, NCX_EL_DEFAULT_PARM); if (appinfo) { if (appinfo->value) { targobj = obj_find_child(obj, obj_get_mod_name(obj), appinfo->value); if (targobj) { if (obj->objtype == OBJ_TYP_CONTAINER) { obj->def.container->defaultparm = targobj; } else { obj->def.rpcio->defaultparm = targobj; } } else { res = ERR_NCX_UNKNOWN_OBJECT; } } else { res = ERR_NCX_MISSING_PARM; } if (res != NO_ERR) { log_error("\nError: invalid 'default-parm' extension"); ncx_print_errormsg(tkc, mod, res); } } } return res; } /* resolve_default_parm */ /******************************************************************** * FUNCTION resolve_mustQ * * Check any must-stmts for this node * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain in progress * mod == module in progress containing obj * obj == object to check (from the cooked module, * not from any grouping or augment) * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_mustQ (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj) { xpath_pcb_t *must; dlq_hdr_t *mustQ; status_t res, retres; mustQ = obj_get_mustQ(obj); if (!mustQ) { return NO_ERR; } retres = NO_ERR; for (must = (xpath_pcb_t *)dlq_firstEntry(mustQ); must != NULL; must = (xpath_pcb_t *)dlq_nextEntry(must)) { if (must->tkc == NULL) { /* this is a clone object and the xpath PCB * is a bare-minimum copy; need to parse * the expression again, */ /* if the must is from a grouping in a different * module, then the must->tk value will be * garbage at this point !!!! */ tkc->curerr = &must->tkerr; res = xpath1_parse_expr(tkc, mod, must, XP_SRC_YANG); } if (must->parseres != NO_ERR) { /* some errors already reported so do not * duplicate messages; just skip 2nd pass */ continue; } res = xpath1_validate_expr_ex(mod, obj, must, FALSE); CHK_EXIT(res, retres); } return retres; } /* resolve_mustQ */ /******************************************************************** * FUNCTION resolve_when * * Check any when-stmt for this node * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * mod == module in progress containing obj * when == XPath control block to use * obj == object to check (from the cooked module, * not from any grouping or augment) * * OUTPUTS: * pcb->validateres is set * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_when (ncx_module_t *mod, xpath_pcb_t *when, obj_template_t *obj) { if (when->parseres != NO_ERR) { /* some errors already reported so do not * duplicate messages; just skip 2nd pass */ return NO_ERR; } obj_template_t *context = obj; if (obj->objtype == OBJ_TYP_AUGMENT) { context = obj->def.augment->targobj; } if (context == NULL) { return ERR_NCX_DEF_NOT_FOUND; } return xpath1_validate_expr_ex(mod, context, when, FALSE); } /* resolve_when */ /******************************************************************** * Cleanup errors encountered by resolve_metadata_appinfo. * * \param newchain the tkc chain to free * \param meta the metadata object to free * \param the result. ********************************************************************/ static status_t cleanup_metadata_appinfo_err( tk_chain_t *newchain, obj_metadata_t* meta, status_t err ) { tk_free_chain( newchain ); obj_free_metadata( meta ); return err; } /******************************************************************** * Cleanup errors encountered by resolve_metadata_appinfo. * * \param newchain the tkc chain to free * \param meta the metadata object to free * \param the result. ********************************************************************/ static status_t resolve_metadata_appinfo( yang_pcb_t *pcb, ncx_module_t* mod, ncx_appinfo_t* appinfo, obj_template_t *obj, boolean usewarning ) { status_t res = NO_ERR; /* turn the string into a Q of tokens */ tk_chain_t* newchain = tk_tokenize_metadata_string( mod, appinfo->value, &res ); if (res != NO_ERR) { log_error("\nError: Invalid metadata value string"); return res; } if ( !newchain ) { log_error("\nError: Failed to allocate newchain!" ); return ERR_INTERNAL_MEM; } /* parse the value string into 2 or 3 fields */ obj_metadata_t *meta = obj_new_metadata(); if (!meta) { tk_free_chain( newchain ); log_error("\nError: Failed to allocate meta!" ); return ERR_INTERNAL_MEM; } /* check the tokens that are in the chain for a YANG QName for the datatype * and a YANG identifier for the XML attribute * name */ res = yang_typ_consume_metadata_type(pcb, newchain, mod, meta->typdef); if ( res != NO_ERR ) { return cleanup_metadata_appinfo_err( newchain, meta, res ); } /* make sure type OK for XML attribute */ if ( !typ_ok_for_metadata(typ_get_basetype(meta->typdef))) { log_error("\nError: Builtin type %s not allowed for metadata in obj %s", tk_get_btype_sym(typ_get_basetype(meta->typdef)), obj_get_name(obj)); return cleanup_metadata_appinfo_err(newchain, meta, ERR_NCX_WRONG_TYPE); } /* got a type for the attribute now need to get a valid name */ res = yang_consume_id_string(newchain, mod, &meta->name); if (res != NO_ERR) { return cleanup_metadata_appinfo_err(newchain, meta, res); } /* check if the name clashes with any standard attributes */ if (!xml_strcmp(meta->name, NC_OPERATION_ATTR_NAME)) { if (usewarning) { log_warn( "\nWarning: metadata using reserved name 'operation' " "for object %s", obj_get_name(obj)); } else { ncx_inc_warnings(mod); } } else if (!xml_strcmp(meta->name, YANG_K_KEY)) { if (usewarning) { log_warn( "\nWarning: metadata using reserved name 'key' for " "object %s", obj_get_name(obj)); } else { ncx_inc_warnings(mod); } } else if (!xml_strcmp(meta->name, YANG_K_INSERT)) { if (usewarning) { log_warn( "\nWarning: metadata using reserved name 'insert' " "for object %s", obj_get_name(obj)); } else { ncx_inc_warnings(mod); } } else if (!xml_strcmp(meta->name, YANG_K_VALUE)) { if (usewarning) { log_warn( "\nWarning: metadata using reserved name 'value' " "for object %s", obj_get_name(obj)); } else { ncx_inc_warnings(mod); } } /* save the metadata even if the name clashes because it is supposed to be * used with a namespace; However, the standard attribbutes are often used * without any prefix */ tk_free_chain( newchain ); return obj_add_metadata(meta, obj); } /******************************************************************** * Check the object for ncx:metadata definitions Convert any clauses to * metadata nodes within the the object struct * * \note Error messages are printed by this function!! * Do not duplicate error messages upon error return * * \param pcb the parser control block * \param tkc the token chain * \param mod the module in progress * \param obj the object to check for ncx:metadata clauses * \return status of the operation *********************************************************************/ static status_t resolve_metadata( yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj ) { status_t firsterr = NO_ERR; dlq_hdr_t *que = obj_get_appinfoQ(obj); if (!que) { return NO_ERR; } boolean usewarning = ncx_warning_enabled(ERR_NCX_USING_RESERVED_NAME); ncx_appinfo_t *appinfo = ncx_find_appinfo(que, NCX_PREFIX, NCX_EL_METADATA); for ( ; appinfo; appinfo = ncx_find_next_appinfo2( appinfo, NCX_PREFIX, NCX_EL_METADATA )) { status_t res = resolve_metadata_appinfo( pcb, mod, appinfo, obj, usewarning ); if (res != NO_ERR) { log_error("\nError: Invalid ncx:metadata string"); res = set_tkc_error( tkc, mod, &appinfo->tkerr, ERR_NCX_INVALID_VALUE); firsterr = ( firsterr==NO_ERR ? res : firsterr ); } } return firsterr; } /* resolve_metadata */ /******************************************************************** * FUNCTION resolve_container * * Check the container object type * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * con == obj_container_t to check * obj == parent object for 'con' * redo == TRUE if this is a 2nd pass due to deviations added * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_container (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_container_t *con, obj_template_t *obj, boolean redo) { status_t res, retres; retres = NO_ERR; if (!redo) { res = resolve_metadata(pcb, tkc, mod, obj); CHK_EXIT(res, retres); } if (!obj_is_refine(obj) && !redo) { res = yang_typ_resolve_typedefs(pcb, tkc, mod, con->typedefQ, obj); CHK_EXIT(res, retres); res = yang_grp_resolve_groupings(pcb, tkc, mod, con->groupingQ, obj); CHK_EXIT(res, retres); } res = resolve_datadefs(pcb, tkc, mod, con->datadefQ, redo); CHK_EXIT(res, retres); return retres; } /* resolve_container */ /******************************************************************** * FUNCTION resolve_container_final * * Check the final container placement for any mandatory * top-level NP containers * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * obj == container object to check * ingrouping == TRUE if this object being resolved * from yang_grp_resolve_final * == FALSE otherwise * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_container_final (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, boolean ingrouping) { status_t res, retres = NO_ERR; boolean ingrp = FALSE; finish_config_flag(obj); res = check_parent(tkc, mod, obj); CHK_EXIT(res, retres); if (!ingrouping && !obj_is_abstract(obj) && (obj->def.container->presence == NULL) && obj_get_config_flag_check(obj, &ingrp) && ((obj->parent != NULL && obj_is_root(obj->parent)) || (obj->parent == NULL && obj->grp == NULL)) && obj_is_mandatory_when_ex(obj, TRUE)) { res = handle_top_mandatory(tkc, mod, obj, (const xmlChar *)"NP container"); CHK_EXIT(res, retres); } if (obj_is_cli(obj)) { obj_sort_children(obj); } return retres; } /* resolve_container_final */ /******************************************************************** * FUNCTION resolve_leaf * * Check the leaf object type * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * leaf == obj_leaf_t to check * obj == parent object for 'leaf' * redo == TRUE if this is a 2nd pass due to deviations added * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_leaf (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_leaf_t *leaf, obj_template_t *obj, boolean redo) { status_t res, retres; retres = NO_ERR; if (!redo) { res = resolve_metadata(pcb, tkc, mod, obj); CHK_EXIT(res, retres); } /* check for leaf only since anyxml has no type */ if (obj->objtype == OBJ_TYP_LEAF && (!obj_is_refine(obj) || !redo)) { res = yang_typ_resolve_type(pcb, tkc, mod, leaf->typdef, leaf->defval, obj); CHK_EXIT(res, retres); } return retres; } /* resolve_leaf */ /******************************************************************** * FUNCTION resolve_leaf_final * * Check the final leaf placements for any mandatory * top-level leafs * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * obj == leaf object to check * ingrouping == TRUE if this object being resolved * from yang_grp_resolve_final * == FALSE otherwise * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_leaf_final (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, boolean ingrouping) { status_t res, retres = NO_ERR; boolean ingrp = FALSE; finish_config_flag(obj); res = check_parent(tkc, mod, obj); CHK_EXIT(res, retres); if (obj->flags & OBJ_FL_MANDATORY) { /* if this is an ANYXML defval will not be set */ if (obj->def.leaf->defval) { log_error("\nError: default-stmt not allowed for mandatory leaf " "'%s'", obj_get_name(obj)); retres = ERR_NCX_INVALID_VALUE; SET_OBJ_CURERR(tkc, obj); ncx_print_errormsg(tkc, mod, retres); } } if (!ingrouping && !obj_is_abstract(obj) && obj_is_mandatory_when(obj) && obj_get_config_flag_check(obj, &ingrp) && ((obj->parent && obj_is_root(obj->parent)) || (obj->parent == NULL && obj->grp == NULL))) { res = handle_top_mandatory(tkc, mod, obj, YANG_K_LEAF); CHK_EXIT(res, retres); } return retres; } /* resolve_leaf_final */ /******************************************************************** * FUNCTION resolve_leaflist * * Check the leaf-list object type * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * llist == obj_leaflist_t to check * obj == parent object for 'llist' * redo == TRUE if this is a 2nd pass due to deviations added * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_leaflist (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_leaflist_t *llist, obj_template_t *obj, boolean redo) { status_t res, retres; retres = NO_ERR; if (!redo) { res = resolve_metadata(pcb, tkc, mod, obj); CHK_EXIT(res, retres); } if (!obj_is_refine(obj) && !redo) { res = yang_typ_resolve_type(pcb, tkc, mod, llist->typdef, NULL, obj); CHK_EXIT(res, retres); } /* mark default as zero or more entries * the min-elements and max-elements will override * this property at runtime */ llist->typdef->iqual = NCX_IQUAL_ZMORE; return retres; } /* resolve_leaflist */ /******************************************************************** * FUNCTION resolve_leaflist_final * * Check the leaf-list object type after all modificatins done * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * obj == parent object for 'llist' * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_leaflist_final (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj) { obj_leaflist_t *llist = obj->def.leaflist; status_t res; finish_config_flag(obj); res = check_parent(tkc, mod, obj); /* check if minelems and maxelems are valid */ if (llist->minelems && llist->maxelems) { if (llist->minelems > llist->maxelems) { log_error("\nError: leaf-list '%s' min-elements > max-elements", obj_get_name(obj)); res = ERR_NCX_INVALID_VALUE; SET_OBJ_CURERR(tkc, obj); ncx_print_errormsg(tkc, mod, res); } } return res; } /* resolve_leaflist_final */ /******************************************************************** * FUNCTION get_list_key * * Get the key components and validate, save them * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * list == obj_list_t to check * obj == parent object for 'list' * * RETURNS: * status of the operation *********************************************************************/ static status_t get_list_key (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_list_t *list, obj_template_t *obj) { obj_template_t *keyobj; xmlChar *str, *p, savech; ncx_error_t *tkerr; obj_key_t *objkey; status_t retres; ncx_btype_t btyp; boolean keyconfig, listconfig, ingrp1, ingrp2; retres = NO_ERR; tkerr = (list->keytkerr.mod) ? &list->keytkerr : &obj->tkerr; /* skip all leading whitespace */ p = list->keystr; while (*p && xml_isspace(*p)) { p++; } /* check whitespace only string */ if (!*p) { log_error("\nError: no identifiers entered in key '%s'", list->keystr); return set_tkc_error( tkc, mod, tkerr, ERR_NCX_INVALID_VALUE ); } /* keep parsing Xpath strings until EOS reached * they will be checked to make sure only proper * child nodes are used as keys */ while (*p) { /* save start of identifier */ str = p; /* find end of the identifier string */ while (*p && !xml_isspace(*p)) { p++; } /* make Zstring for 1 key identifier */ savech = *p; *p = 0; /* check for a valid descendant-schema-nodeid string */ retres = xpath_find_schema_target_err(pcb, tkc, mod, obj, list->datadefQ, str, &keyobj, NULL, tkerr); /* check identifier is bogus, nothing found */ if (retres != NO_ERR) { log_error("\nError: invalid identifier in key" " for list '%s' (%s)", list->name, str); retres = set_tkc_error( tkc, mod, tkerr, retres ); /* waited to restore string so it could be used * in the log_error msg above */ *p = savech; while (*p && xml_isspace(*p)) { p++; } continue; } /* mark the object as a key leaf for obj_is_key() fn */ keyobj->flags |= OBJ_FL_KEY; /* restore string and skip any whitespace between key components */ *p = savech; while (*p && xml_isspace(*p)) { p++; } /* get the base type of the object */ btyp = obj_get_basetype(keyobj); /* make sure the key is a leaf */ if (keyobj->objtype != OBJ_TYP_LEAF) { /* found the key node, but it is not a leaf */ log_error("\nError: node '%s' on line %u not a leaf in key" " for list '%s' (%s)", obj_get_name(keyobj), keyobj->tkerr.linenum, list->name, obj_get_typestr(keyobj)); retres = set_tkc_error( tkc, mod, tkerr, ERR_NCX_TYPE_NOT_INDEX ); continue; } /* make sure the leaf is a child of the list object * and not a deep key; this is a CLR in YANG but it * is supported by Yuma */ if (keyobj->parent != obj) { log_error("\nError: leaf node '%s' on line %u not child " "of list '%s'", obj_get_name(keyobj), keyobj->tkerr.linenum, list->name); retres = set_tkc_error( tkc, mod, tkerr, ERR_NCX_WRONG_INDEX_TYPE ); } /* make sure the base type is OK for an index */ if (!typ_ok_for_inline_index(btyp)) { log_error("\nError: leaf node '%s' on line %u not valid type " "in key, for list '%s' (%s)", obj_get_name(keyobj), keyobj->tkerr.linenum, list->name, tk_get_btype_sym(btyp)); retres = set_tkc_error( tkc, mod, tkerr, ERR_NCX_TYPE_NOT_INDEX ); } /* make sure madatory=false is not set for the key leaf */ if ((keyobj->flags & OBJ_FL_MANDSET) && !(keyobj->flags & OBJ_FL_MANDATORY)) { if (ncx_warning_enabled(ERR_NCX_STMT_IGNORED)) { log_warn("\nWarning: 'mandatory false;' " "ignored in leaf '%s' " "on line %u for list '%s'", obj_get_name(keyobj), keyobj->tkerr.linenum, list->name); // FIXME: Why is the retres not set? (void)set_tkc_error( tkc, mod, tkerr, ERR_NCX_STMT_IGNORED ); } else if (mod != NULL) { ncx_inc_warnings(mod); } } /* make sure config has same setting as the list parent */ ingrp1 = ingrp2 = FALSE; keyconfig = obj_get_config_flag_check(keyobj, &ingrp1); listconfig = obj_get_config_flag_check(obj, &ingrp2); if ((keyconfig != listconfig) && (!ingrp1 && !ingrp2)) { log_error("\nError: 'config-stmt for key leaf '%s' " "on line %u must match list '%s'", obj_get_name(keyobj), keyobj->tkerr.linenum, list->name); retres = set_tkc_error( tkc, mod, tkerr, ERR_NCX_WRONG_INDEX_TYPE ); } /* make sure key component not already used */ objkey = obj_find_key2(&list->keyQ, keyobj); if (objkey) { log_error("\nError: duplicate key node '%s' on line %u " "for list '%s'", obj_get_name(keyobj), keyobj->tkerr.linenum, list->name); retres = set_tkc_error( tkc, mod, tkerr, ERR_NCX_DUP_ENTRY ); continue; } /* get a new key record struct */ objkey = obj_new_key(); if (!objkey) { return set_tkc_error( tkc, mod, tkerr, ERR_INTERNAL_MEM ); } /* everything OK so save the key * a backptr to 'objkey' in 'key' cannot be maintained * because 'key' may be inside a grouping, and a simple * uses foo; will cause the groupingQ to be used directly */ objkey->keyobj = keyobj; dlq_enque(objkey, &list->keyQ); } return retres; } /* get_list_key */ /******************************************************************** * FUNCTION resolve_list * * Check the list object type * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * list == obj_list_t to check * obj == parent object for 'list' * redo == TRUE if this is a 2nd pass due to deviations added * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_list (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_list_t *list, obj_template_t *obj, boolean redo) { status_t res, retres; retres = NO_ERR; if (!redo) { res = resolve_metadata(pcb, tkc, mod, obj); CHK_EXIT(res, retres); } if (!obj_is_refine(obj) && !redo) { res = yang_typ_resolve_typedefs(pcb, tkc, mod, list->typedefQ, obj); CHK_EXIT(res, retres); res = yang_grp_resolve_groupings(pcb, tkc, mod, list->groupingQ, obj); CHK_EXIT(res, retres); } res = resolve_datadefs(pcb, tkc, mod, list->datadefQ, redo); CHK_EXIT(res, retres); return retres; } /* resolve_list */ /******************************************************************** * FUNCTION get_unique_comps * * Get the unique-stmt components and validate, save them * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * list == obj_list_t to check * obj == parent object for 'list' * uni == unique statement collected in this struct * needs to be validated and finalized * * OUTPUTS: * uni->compQ is filled with obj_unique_comp_t structs * each one represents one leaf in the unique tuple * * RETURNS: * status of the operation *********************************************************************/ static status_t get_unique_comps (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_list_t *list, obj_template_t *obj, obj_unique_t *uni) { xmlChar *savestr = NULL; status_t res = NO_ERR, retres = NO_ERR; boolean firstset = FALSE; ncx_error_t *tkerr = (uni->tkerr.mod) ? &uni->tkerr : &obj->tkerr; /* skip all leading whitespace */ xmlChar *p = uni->xpath; while (*p && xml_isspace(*p)) { p++; } if (!*p) { log_error("\nError: no identifiers entered in unique statement '%s'", uni->xpath); return set_tkc_error( tkc, mod, tkerr, ERR_NCX_INVALID_VALUE ); } /* keep parsing Xpath strings until EOS reached */ while (*p) { /* find end of non-whitespace string */ xmlChar *str = p; while (*p && !xml_isspace(*p)) { p++; } xmlChar savech = *p; *p = 0; /* check for a valid descendant-schema-nodeid string */ obj_template_t *uniobj = NULL; res = xpath_find_schema_target_err(pcb, tkc, mod, obj, list->datadefQ, str, &uniobj, NULL, tkerr); CHK_EXIT(res, retres); if (res == NO_ERR) { savestr = xml_strdup(str); if (!savestr) { return set_tkc_error( tkc, mod, tkerr, ERR_INTERNAL_MEM ); } } *p = savech; while (*p && xml_isspace(*p)) { p++; /* skip whitespace between strings */ } if (res != NO_ERR) { continue; } /* got a valid Xpath expression which points to a * child node in the obj_list_t datadefQ * make sure the unique target is a leaf */ if (uniobj->objtype != OBJ_TYP_LEAF) { log_error("\nError: node '%s' on line %u not leaf in " "list '%s' unique-stmt", obj_get_name(uniobj), uniobj->tkerr.linenum, list->name); retres = set_tkc_error( tkc, mod, tkerr, ERR_NCX_INVALID_UNIQUE_NODE ); m__free(savestr); continue; } /* make sure there is a no config mismatch */ if (firstset) { boolean ingrp = FALSE; if (obj_get_config_flag_check(obj, &ingrp) && !uni->isconfig) { /* mix of config and non-config leafs * in the unique-stmt */ if (!ingrp) { log_error("\nError: leaf '%s' on line " "%u; unique-stmt config mismatch in " "list '%s'", obj_get_name(uniobj), uniobj->tkerr.linenum, list->name); retres = set_tkc_error( tkc, mod, tkerr, ERR_NCX_INVALID_UNIQUE_NODE ); m__free(savestr); continue; } } } else { /* unique-stmt can be for all config leafs or * all non-config leafs, but no mix */ uni->isconfig = obj_get_config_flag_deep(obj); firstset = TRUE; } /* the final target seems to be a valid leaf * so check that its path back to the original * object is all static object types * container, leaf, choice, case */ /******************* * this code is removed because NETMOD WG decided that list * is allowed to be within the unique node path. * the eval result will contain a node-set, not 1 val_value_t * *obj_template_t *testobj = uniobj->parent; *res = NO_ERR; *while (testobj && (testobj != obj) && (res == NO_ERR)) { * if (testobj->objtype == OBJ_TYP_LIST) { * log_error("\nError: list node (%s) " * "within unique stmt '%s' for node '%s'", * obj_get_name(testobj), uni->xpath, savestr); * res = set_tkc_error( tkc, mod, tkerr, * ERR_NCX_INVALID_UNIQUE_NODE ); * } * * testobj = testobj->parent; *} *if (res != NO_ERR) { * m__free(savestr); * CHK_EXIT(res, retres); * continue; *} */ uniobj->flags |= OBJ_FL_UNIQUE; /* make sure this leaf component not already used */ boolean isduplicate = FALSE; obj_unique_comp_t *testcomp = (obj_unique_comp_t *)dlq_firstEntry(&uni->compQ); for (; testcomp != NULL && res==NO_ERR; testcomp = (obj_unique_comp_t *)dlq_nextEntry(testcomp)) { if (testcomp->unobj == uniobj) { isduplicate = TRUE; if (ncx_warning_enabled(ERR_NCX_DUP_UNIQUE_COMP)) { log_warn("\nWarning: duplicate unique " "node '%s' on line %u for list '%s'", obj_get_name(uniobj), uniobj->tkerr.linenum, list->name); // The retres is not set because this is a warning // not an error (416) (void)set_tkc_error( tkc, mod, tkerr, ERR_NCX_INVALID_UNIQUE_NODE ); } else if (mod != NULL) { ncx_inc_warnings(mod); } } } /* try to save the info in a new unicomp struct */ if (retres == NO_ERR) { /* get a new unique component struct */ obj_unique_comp_t *unicomp = obj_new_unique_comp(); if (!unicomp) { m__free(savestr); return set_tkc_error( tkc, mod, tkerr, ERR_INTERNAL_MEM ); } else { /* everything OK so save the unique component * pass off the malloced savestr to the * unicomp record */ unicomp->unobj = uniobj; unicomp->xpath = savestr; unicomp->isduplicate = isduplicate; dlq_enque(unicomp, &uni->compQ); } } else { m__free(savestr); } } return retres; } /* get_unique_comps */ /******************************************************************** * FUNCTION resolve_list_final * * Check the list object type after all uses and augments are expanded * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * list == obj_list_t to check * obj == parent object for 'list' * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_list_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_list_t *list, obj_template_t *obj) { obj_unique_t *uni; status_t res, retres; retres = NO_ERR; /* augment is processed before resolve_list_final and the list keys * are filled into the orginal list (under augment, not in the * expanded list under the augment target * THIS DOES NOT ALWAYS WORK FOR augment /obj-in-submod-a with * obj-in-submod-b if submod-a processed before submod-b, * this step will get skipped * * The function yang_obj_top_resolve_final will be called for the * main module and all submodules will attempt this code again * in case the submod datadefQ was filled in by another submod * after the first submod called yang_obj_resolve_final * * For modules augmenting other modules, this step could * be skipped, if the augmenting module is compiled after the * augmented module, which is always the case, since the * augmented module has to be imported, then resolve_list_final * for the augmenting list was not done for the cloned list */ finish_config_flag(obj); res = check_parent(tkc, mod, obj); CHK_EXIT(res, retres); /* check if minelems and maxelems are valid */ if (list->minelems && list->maxelems) { if (list->minelems > list->maxelems) { log_error("\nError: list '%s' min-elements > max-elements", obj_get_name(obj)); retres = ERR_NCX_INVALID_VALUE; SET_OBJ_CURERR(tkc, obj); ncx_print_errormsg(tkc, mod, retres); } } /* validate key clause only if this has probably not * been attempted yet */ if (list->keystr && dlq_empty(&list->keyQ)) { res = get_list_key(pcb, tkc, mod, list, obj); CHK_EXIT(res, retres); } /* validate Q of unique clauses only if probably not attempted yet */ for (uni = (obj_unique_t *)dlq_firstEntry(&list->uniqueQ); uni != NULL; uni = (obj_unique_t *)dlq_nextEntry(uni)) { if (!dlq_empty(&uni->compQ)) { /* this list was processed already and some or all * unique components were found already */ continue; } res = get_unique_comps(pcb, tkc, mod, list, obj, uni); CHK_EXIT(res, retres); } // TBD: add warning to check for rare duplicate unique-stmts // so server will ignore them for faster root-check return retres; } /* resolve_list_final */ /******************************************************************** * FUNCTION resolve_case * * Check the case object type * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * cas == obj_case_t to check * redo == TRUE if this is a 2nd pass due to deviations added * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_case (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_case_t *cas, boolean redo) { status_t res; res = resolve_datadefs(pcb, tkc, mod, cas->datadefQ, redo); return res; } /* resolve_case */ /******************************************************************** * FUNCTION resolve_case_final * * Check the case object type * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * obj == parent object for 'cas' * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_case_final (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj) { status_t res; res = check_parent(tkc, mod, obj); return res; } /* resolve_case_final */ /******************************************************************** * FUNCTION resolve_choice * * Check the choice object type * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * choic == obj_choice_t to check * redo == TRUE if this is a 2nd pass due to deviations added * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_choice (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_choice_t *choic, boolean redo) { status_t res; /* finish up the data-def-stmts in each case arm */ res = resolve_datadefs(pcb, tkc, mod, choic->caseQ, redo); return res; } /* resolve_choice */ /******************************************************************** * FUNCTION resolve_choice_final * * Check the final choice placement for any mandatory * top-level choices * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * obj == choice object to check * ingrouping == TRUE if this object being resolved * from yang_grp_resolve_final * == FALSE otherwise * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_choice_final (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, boolean ingrouping) { obj_choice_t *choic = obj->def.choic; status_t res, retres = NO_ERR; boolean ingrp = FALSE; finish_config_flag(obj); res = check_parent(tkc, mod, obj); CHK_EXIT(res, retres); if ((obj->flags & OBJ_FL_MANDATORY) && choic->defval) { log_error("\nError: both mandatory and default statements present" "'%s'", obj_get_name(obj)); retres = ERR_NCX_INVALID_VALUE; SET_OBJ_CURERR(tkc, obj); ncx_print_errormsg(tkc, mod, retres); } /* check defval is valid case name */ if (choic->defval) { obj_case_t *cas = obj_find_case(choic, obj_get_mod_name(obj), choic->defval); if (!cas) { /* default is not a valid case name */ SET_OBJ_CURERR(tkc, obj); retres = ERR_NCX_INVALID_VALUE; log_error("\nError: Choice default '%s' " "not a valid case name", choic->defval); ncx_print_errormsg(tkc, mod, retres); } else { obj_template_t *cobj; /* valid case name, * make sure 'cas' contains only optional data nodes */ for (cobj = (obj_template_t *)dlq_firstEntry(cas->datadefQ); cobj != NULL; cobj = (obj_template_t *)dlq_nextEntry(cobj)) { if (obj_is_mandatory(cobj)) { retres = set_tkc_error( tkc, mod, &cobj->tkerr, ERR_NCX_DEFCHOICE_NOT_OPTIONAL ); } } } } if (!ingrouping && !obj_is_abstract(obj) && obj_is_mandatory_when(obj) && obj_get_config_flag_check(obj, &ingrp) && ((obj->parent && obj_is_root(obj->parent)) || (obj->parent == NULL && obj->grp == NULL))) { res = handle_top_mandatory(tkc, mod, obj, YANG_K_CHOICE); CHK_EXIT(res, retres); } return retres; } /* resolve_choice_final */ /******************************************************************** * FUNCTION add_inherited_conditionals * * Check the uses or augment object for if-feature and when * statements and set the inherited if-feature and when backptrs * for the specified object * * INPUTS: * srcobj == uses or augment object to check for conditionals * that need to be inherited * targobj == target object to add interied conditionals to * * RETURNS: * status of the operation *********************************************************************/ static status_t add_inherited_conditionals (obj_template_t *srcobj, obj_template_t *targobj) { obj_iffeature_ptr_t *iffptr, *iffptr2; obj_xpath_ptr_t *xptr, *xptr2; ncx_iffeature_t *iff; for (iff = (ncx_iffeature_t *)dlq_firstEntry(&srcobj->iffeatureQ); iff != NULL; iff = (ncx_iffeature_t *)dlq_nextEntry(iff)) { iffptr = obj_new_iffeature_ptr(iff); if (iffptr) { dlq_enque(iffptr, &targobj->inherited_iffeatureQ); } else { return ERR_INTERNAL_MEM; } } for (iffptr2 = (obj_iffeature_ptr_t *) dlq_firstEntry(&srcobj->inherited_iffeatureQ); iffptr2 != NULL; iffptr2 = (obj_iffeature_ptr_t *)dlq_nextEntry(iffptr2)) { iffptr = obj_new_iffeature_ptr(iffptr2->iffeature); if (iffptr) { dlq_enque(iffptr, &targobj->inherited_iffeatureQ); } else { return ERR_INTERNAL_MEM; } } if (srcobj->when) { xptr = obj_new_xpath_ptr(srcobj->when); if (xptr) { dlq_enque(xptr, &targobj->inherited_whenQ); } else { return ERR_INTERNAL_MEM; } } for (xptr2 = (obj_xpath_ptr_t *)dlq_firstEntry(&srcobj->inherited_whenQ); xptr2 != NULL; xptr2 = (obj_xpath_ptr_t *)dlq_nextEntry(xptr2)) { xptr = obj_new_xpath_ptr(xptr2->xpath); if (xptr) { dlq_enque(xptr, &targobj->inherited_whenQ); } else { return ERR_INTERNAL_MEM; } } if (srcobj->parent && (srcobj->parent->objtype == OBJ_TYP_CHOICE || srcobj->parent->objtype == OBJ_TYP_CASE)) { return add_inherited_conditionals(srcobj->parent, targobj); } return NO_ERR; } /* add_inherited_conditionals */ /******************************************************************** * FUNCTION check_refine_allowed * * Check the uses object type against the target node found * * Only checks if extra refine clauses are present * which are not allowed for that * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * refineobj == refine object to check * targobj == target object to check against * * RETURNS: * status of the operation *********************************************************************/ static status_t check_refine_allowed (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *refineobj, obj_template_t *targobj) { obj_refine_t *refine; xpath_pcb_t *must; boolean pres, def, conf, mand, minel, maxel, bmust; status_t res; refine = refineobj->def.refine; pres = FALSE; def = FALSE; conf = FALSE; mand = FALSE; minel = FALSE; maxel = FALSE; bmust = FALSE; res = NO_ERR; switch (targobj->objtype) { case OBJ_TYP_LEAF: conf = TRUE; mand = TRUE; bmust = TRUE; def = TRUE; break; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: mand = TRUE; break; case OBJ_TYP_LEAF_LIST: conf = TRUE; bmust = TRUE; minel = TRUE; maxel = TRUE; break; case OBJ_TYP_CONTAINER: bmust = TRUE; pres = TRUE; conf = TRUE; break; case OBJ_TYP_LIST: bmust = TRUE; conf = TRUE; minel = TRUE; maxel = TRUE; break; case OBJ_TYP_CHOICE: def = TRUE; mand = TRUE; break; case OBJ_TYP_CASE: break; default: return NO_ERR; /* error: should already be reported */ } /* check all the fields except description and reference * since they are allowed to appear in every variant */ if (refine->presence && !pres) { log_error("\nError: 'presence' refinement on %s '%s'", obj_get_typestr(targobj), obj_get_name(targobj)); res = set_tkc_error(tkc, mod, &refine->presence_tkerr, ERR_NCX_REFINE_NOT_ALLOWED); } if (refine->def && !def) { log_error("\nError: 'default' refinement on %s '%s'", obj_get_typestr(targobj), obj_get_name(targobj)); res = set_tkc_error(tkc, mod, &refine->def_tkerr, ERR_NCX_REFINE_NOT_ALLOWED); } if (refine->config_tkerr.mod && !conf) { log_error("\nError: 'config' refinement on %s '%s'", obj_get_typestr(targobj), obj_get_name(targobj)); res = set_tkc_error(tkc, mod, &refine->config_tkerr, ERR_NCX_REFINE_NOT_ALLOWED); } if (refine->mandatory_tkerr.mod && !mand) { log_error("\nError: 'mandatory' refinement on %s '%s'", obj_get_typestr(targobj), obj_get_name(targobj)); res = set_tkc_error(tkc, mod, &refine->mandatory_tkerr, ERR_NCX_REFINE_NOT_ALLOWED); } if (refine->minelems_tkerr.mod && !minel) { log_error("\nError: 'min-elements' refinement on %s '%s'", obj_get_typestr(targobj), obj_get_name(targobj)); res = set_tkc_error(tkc, mod, &refine->minelems_tkerr, ERR_NCX_REFINE_NOT_ALLOWED); } if (refine->maxelems_tkerr.mod && !maxel) { log_error("\nError: 'max-elements' refinement on %s '%s'", obj_get_typestr(targobj), obj_get_name(targobj)); res = set_tkc_error(tkc, mod, &refine->maxelems_tkerr, ERR_NCX_REFINE_NOT_ALLOWED); } if (!dlq_empty(&refine->mustQ) && !bmust) { res = ERR_NCX_REFINE_NOT_ALLOWED; for (must = (xpath_pcb_t *)dlq_firstEntry(&refine->mustQ); must != NULL; must = (xpath_pcb_t *)dlq_nextEntry(must)) { log_error("\nError: 'must' refinement on %s '%s'", obj_get_typestr(targobj), obj_get_name(targobj)); // FIXME: Why is the res not set? (void) set_tkc_error(tkc, mod, &must->tkerr, res ); } } return res; } /* check_refine_allowed */ /******************************************************************** * FUNCTION combine_refine_objects * * Combine two refine objects with the same target * Add fields from the mergeobj into the keepobj. * * The mergeobj should be freed after this call * * Only checks if extra refine clauses are present * which are not allowed for that * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * keepobj == refine object to keep * mergeobj == refine object to merge into 'keepobj' * targobj == refine object target to check type * * RETURNS: * status of the operation *********************************************************************/ static status_t combine_refine_objects (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *keepobj, obj_template_t *mergeobj, obj_template_t *targobj) { obj_refine_t *krefine, *mrefine; boolean pres, def, conf, mand, minel, maxel, bmust; status_t res; krefine = keepobj->def.refine; mrefine = mergeobj->def.refine; pres = FALSE; def = FALSE; conf = FALSE; mand = FALSE; minel = FALSE; maxel = FALSE; bmust = FALSE; res = NO_ERR; switch (targobj->objtype) { case OBJ_TYP_LEAF: conf = TRUE; mand = TRUE; bmust = TRUE; def = TRUE; break; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: mand = TRUE; break; case OBJ_TYP_LEAF_LIST: conf = TRUE; bmust = TRUE; minel = TRUE; maxel = TRUE; break; case OBJ_TYP_CONTAINER: bmust = TRUE; pres = TRUE; conf = TRUE; break; case OBJ_TYP_LIST: bmust = TRUE; conf = TRUE; minel = TRUE; maxel = TRUE; break; case OBJ_TYP_CHOICE: def = TRUE; mand = TRUE; break; case OBJ_TYP_CASE: break; default: return NO_ERR; /* error: should already be reported */ } if (mrefine->descr) { if (krefine->descr) { log_error("\nError: description-stmt set in refine on line %u", krefine->descr_tkerr.linenum); res = set_tkc_error( tkc, mod, &mrefine->descr_tkerr, ERR_NCX_DUP_REFINE_STMT ); } else { krefine->descr = mrefine->descr; ncx_set_error(&krefine->descr_tkerr, mrefine->descr_tkerr.mod, mrefine->descr_tkerr.linenum, mrefine->descr_tkerr.linepos); mrefine->descr = NULL; } } if (mrefine->ref) { if (krefine->ref) { log_error("\nError: reference-stmt set in refine on line %u", krefine->ref_tkerr.linenum); res = set_tkc_error( tkc, mod, &mrefine->ref_tkerr, ERR_NCX_DUP_REFINE_STMT ); ncx_print_errormsg(tkc, mod, res); } else { krefine->ref = mrefine->ref; ncx_set_error(&krefine->ref_tkerr, mrefine->ref_tkerr.mod, mrefine->ref_tkerr.linenum, mrefine->ref_tkerr.linepos); mrefine->ref = NULL; } } if (mrefine->presence && pres) { if (krefine->presence) { log_error("\nError: presence-stmt set in refine on line %u", krefine->presence_tkerr.linenum); res = set_tkc_error( tkc, mod, &mrefine->presence_tkerr, ERR_NCX_DUP_REFINE_STMT ); } else { krefine->presence = mrefine->presence; ncx_set_error(&krefine->presence_tkerr, mrefine->presence_tkerr.mod, mrefine->presence_tkerr.linenum, mrefine->presence_tkerr.linepos); mrefine->presence = NULL; } } if (mrefine->def && def) { if (krefine->def) { log_error("\nError: default-stmt set in refine on line %u", krefine->def_tkerr.linenum); res = set_tkc_error( tkc, mod, &mrefine->def_tkerr, ERR_NCX_DUP_REFINE_STMT ); } else { krefine->def = mrefine->def; ncx_set_error(&krefine->def_tkerr, mrefine->def_tkerr.mod, mrefine->def_tkerr.linenum, mrefine->def_tkerr.linepos); mrefine->def = NULL; } } if (mrefine->config_tkerr.mod && conf) { if (krefine->config_tkerr.mod) { log_error("\nError: config-stmt set in refine on line %u", krefine->config_tkerr.linenum); res = set_tkc_error( tkc, mod, &mrefine->config_tkerr, ERR_NCX_DUP_REFINE_STMT ); } else { ncx_set_error(&krefine->config_tkerr, mrefine->config_tkerr.mod, mrefine->config_tkerr.linenum, mrefine->config_tkerr.linepos); keepobj->flags |= OBJ_FL_CONFSET; if (mergeobj->flags & OBJ_FL_CONFIG) { keepobj->flags |= OBJ_FL_CONFIG; } else { keepobj->flags &= ~OBJ_FL_CONFIG; } } } if (mrefine->mandatory_tkerr.mod && mand) { if (krefine->mandatory_tkerr.mod) { log_error("\nError: mandatory-stmt set in refine on line %u", krefine->mandatory_tkerr.linenum); res = set_tkc_error( tkc, mod, &mrefine->mandatory_tkerr, ERR_NCX_DUP_REFINE_STMT ); } else { ncx_set_error(&krefine->mandatory_tkerr, mrefine->mandatory_tkerr.mod, mrefine->mandatory_tkerr.linenum, mrefine->mandatory_tkerr.linepos); keepobj->flags |= OBJ_FL_MANDSET; if (mergeobj->flags & OBJ_FL_MANDATORY) { keepobj->flags |= OBJ_FL_MANDATORY; } } } if (mrefine->minelems_tkerr.mod && minel) { if (krefine->minelems_tkerr.mod) { log_error("\nError: min-elements-stmt set in refine on line %u", krefine->minelems_tkerr.linenum); res = set_tkc_error( tkc, mod, &mrefine->minelems_tkerr, ERR_NCX_DUP_REFINE_STMT ); } else { krefine->minelems = mrefine->minelems; ncx_set_error(&krefine->minelems_tkerr, mrefine->minelems_tkerr.mod, mrefine->minelems_tkerr.linenum, mrefine->minelems_tkerr.linepos); } } if (mrefine->maxelems_tkerr.mod && maxel) { if (krefine->maxelems_tkerr.mod) { log_error("\nError: max-elements-stmt set in refine on line %u", krefine->maxelems_tkerr.linenum); res = set_tkc_error( tkc, mod, &mrefine->maxelems_tkerr, ERR_NCX_DUP_REFINE_STMT ); } else { krefine->maxelems = mrefine->maxelems; ncx_set_error(&krefine->maxelems_tkerr, mrefine->maxelems_tkerr.mod, mrefine->maxelems_tkerr.linenum, mrefine->maxelems_tkerr.linepos); } } if (!dlq_empty(&mrefine->mustQ) && bmust) { dlq_block_enque(&mrefine->mustQ, &krefine->mustQ); } if (!dlq_empty(&mergeobj->appinfoQ)) { dlq_block_enque(&mergeobj->appinfoQ, &keepobj->appinfoQ); } return res; } /* combine_refine_objects */ /******************************************************************** * Resolve the groping that is referenced by the uses clause. * * \param tkc the current parser token chain * \param mod the current module * \param uses the uses clause * \param obj parent object that owns the uses clause * \return the result of the operation. ********************************************************************/ static status_t resolve_uses_grp( tk_chain_t *tkc, ncx_module_t *mod, obj_uses_t *uses, obj_template_t *obj ) { status_t res = NO_ERR; /* find the grouping that this uses references if this is a local grouping, * and grp has not been set yet */ if (!uses->grp) { uses->grp = obj_find_grouping(obj, uses->name); } if (!uses->grp) { uses->grp = ncx_find_grouping(mod, uses->name, FALSE); } if (!uses->grp) { log_error("\nError: grouping '%s' not found", uses->name); res = ERR_NCX_DEF_NOT_FOUND; SET_OBJ_CURERR(tkc, obj); ncx_print_errormsg(tkc, mod, res); return res; } else { /* check for nested uses -- a uses AA within grouping AA */ uses->grp->used = TRUE; res = yang_grp_check_nest_loop(tkc, mod, obj, uses->grp); if (res != NO_ERR) { uses->grp = NULL; /* prevent recursive crash later */ } } return res; } /******************************************************************** * Resolve uses refine clause for leaf object * * \param tkc the current parser token chain * \param mod the current module * \param refine the refine clause * \param targobj the target object that owns the uses clause * \return the result of the operation. ********************************************************************/ static status_t resolve_uses_refine_leaf( tk_chain_t *tkc, ncx_module_t *mod, obj_refine_t *refine, obj_template_t* targobj ) { status_t res = val_simval_ok_ex( targobj->def.leaf->typdef, refine->def, NULL, mod ); if (res != NO_ERR) { log_error( "\nError: Leaf refinement '%s' has invalid default value (%s)", obj_get_name(targobj), refine->def ); (void) set_tkc_error( tkc, mod, &refine->def_tkerr, res ); } return res; } /******************************************************************** * Resolve uses refine clause for a choice object * * \param tkc the current parser token chain * \param mod the current module * \param refine the refine clause * \param targobj the target object that owns the uses clause * \return the result of the operation. ********************************************************************/ static status_t resolve_uses_refine_choice( tk_chain_t *tkc, ncx_module_t *mod, obj_refine_t *refine, obj_template_t* targobj ) { status_t res = NO_ERR; obj_case_t *cas = obj_find_case( targobj->def.choic, obj_get_mod_name(targobj), refine->def ); if (!cas) { /* default is not a valid case name */ log_error( "\nError: Refined choice default '%s' is not a valid case name", refine->def ); res= set_tkc_error(tkc, mod, &refine->def_tkerr, ERR_NCX_INVALID_VALUE); } else { /* valid case name, make sure 'cas' contains only optional data nodes */ obj_template_t *cobj = (obj_template_t *) dlq_firstEntry(cas->datadefQ); for (; cobj; cobj = (obj_template_t *)dlq_nextEntry(cobj)) { if ( obj_has_name(cobj) && obj_is_mandatory(cobj)) { res = set_tkc_error( tkc, mod, &cobj->tkerr, ERR_NCX_DEFCHOICE_NOT_OPTIONAL ); } } } return res; } /******************************************************************** * Resolve uses refine clause * * \param pcb the parser control block * \param tkc the current parser token chain * \param mod the current module * \param uses the uses clause * \param obj parent object that owns the uses clause * \return the result of the operation. ********************************************************************/ static status_t resolve_uses_refine( yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_uses_t *uses, obj_template_t *obj ) { status_t firsterr = NO_ERR; /* make sure all the refinements really match a child in the grouping */ obj_template_t* targobj = NULL; obj_template_t *chobj = (obj_template_t *)dlq_firstEntry(uses->datadefQ); for ( ; chobj ; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { if (chobj->objtype != OBJ_TYP_REFINE) { continue; } obj_refine_t *refine = chobj->def.refine; /* find schema-nodeid target the node being refined, * this MUST exist in the grouping */ status_t res = xpath_find_schema_target( pcb, tkc, uses->grp->tkerr.mod, obj, &uses->grp->datadefQ, refine->target, &targobj, NULL) ; if (res != NO_ERR || !targobj) { /* error: refined obj not in the grouping */ log_error( "\nError: refinement node '%s' not found in grouping '%s'", obj_get_name(chobj), uses->grp->name ); res = set_tkc_error( tkc, mod, &chobj->tkerr, ERR_NCX_MISSING_REFTARGET ); firsterr = ( firsterr == NO_ERR ? res : firsterr ); continue; } /* refine target is valid, so save it */ refine->targobj = targobj; /* check any extra refinements not allowed for the target object type */ res = check_refine_allowed(tkc, mod, chobj, targobj); if ( terminate_parse( res ) ) { return res; } /* check if any default statements are present, and if they are OK for * the target data type */ if ( targobj->objtype == OBJ_TYP_LEAF && refine->def ) { res = resolve_uses_refine_leaf( tkc, mod, refine, targobj ); } else if (targobj->objtype == OBJ_TYP_CHOICE && refine->def) { res = resolve_uses_refine_choice( tkc, mod, refine, targobj ); } firsterr = ( firsterr == NO_ERR ? res : firsterr ); } return firsterr; } /******************************************************************** * Combine any duplicate refine statements. * * \param pcb the parser control block * \param tkc the current parser token chain * \param mod the current module * \param uses the uses clause * \param obj parent object that owns the uses clause * \return the result of the operation. ********************************************************************/ static status_t resolve_uses_combine_duplicate_refines( tk_chain_t *tkc, ncx_module_t *mod, obj_uses_t *uses ) { status_t res = NO_ERR; /* go through the refinement objects one more time and combine the ones * with the same target (if any) Generate an error for duplicate * sub-clauses entered */ obj_template_t* chobj = (obj_template_t *)dlq_firstEntry(uses->datadefQ); for ( ; chobj ; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { if (chobj->objtype != OBJ_TYP_REFINE) { continue; } obj_template_t* cobj = chobj->def.refine->targobj; if (!cobj) { continue; } /* look through rest of Q for any refine w/ same target */ obj_template_t* testobj = (obj_template_t *)dlq_nextEntry(chobj); obj_template_t* nextobj = NULL; for ( ; testobj; testobj = nextobj) { nextobj = (obj_template_t *)dlq_nextEntry(testobj); if ( testobj->objtype == OBJ_TYP_REFINE && testobj->def.refine->targobj == cobj) { /* duplicate refine found, remove and combine */ dlq_remove(testobj); res = combine_refine_objects(tkc, mod, chobj, testobj, cobj); obj_free_template(testobj); if ( terminate_parse( res ) ) { return res; } } } } return res; } /******************************************************************** * FUNCTION resolve_uses * * Check the uses object type * This is done before the groupings are expanded * * - Find the grouping being used * - Check for uses loop errors * - check all the local augment statements * - check all the refine statements * - patch the objects with the refinements * - change refine-stmts to canonical form; no duplicate targets * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * uses == obj_uses_t to check * obj == parent object for 'uses' * * RETURNS: * The error that terminated the parse, or the first error that occurred *********************************************************************/ static status_t resolve_uses( yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_uses_t *uses, obj_template_t *obj ) { status_t res = resolve_uses_grp( tkc, mod, uses, obj ); if( terminate_parse( res ) ) { return res; } status_t firsterr = res; /* resolve all the grouping augments, skip the refines */ res = yang_obj_resolve_datadefs(pcb, tkc, mod, uses->datadefQ); if (res != NO_ERR) { firsterr = ( firsterr == NO_ERR ? res : firsterr ); } /* make sure all the refinements really match a child in the grouping */ res = resolve_uses_refine( pcb, tkc, mod, uses, obj ); if ( terminate_parse( res ) ) { return res; } firsterr = ( firsterr == NO_ERR ? res : firsterr ); res = resolve_uses_combine_duplicate_refines( tkc, mod, uses ); if ( terminate_parse( res ) ) { return res; } firsterr = ( firsterr == NO_ERR ? res : firsterr ); /* at this point there should be one refine object for* each target * specified, and no duplicates, unless fatal error so will not * continue anyways */ return firsterr; } /* resolve_uses */ /******************************************************************** * FUNCTION resolve_uses_final * * Check the uses object type * This is done after refines are applied * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * obj == parent object for 'uses' * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_uses_final (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj) { status_t res; res = check_parent(tkc, mod, obj); return res; } /* resolve_uses_final */ /******************************************************************** * FUNCTION expand_uses * * Expand the indicated grouping inline, inserted into * datadefQ just before the uses object node * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * obj == obj_template_t that contains the obj_uses_t to check * datadefQ == Q that obj is stored in (needed to check for dup. err) * * RETURNS: * status of the operation *********************************************************************/ static status_t expand_uses (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *datadefQ) { obj_uses_t *uses = obj->def.uses; if (!uses->grp) { /* this node has errors, currently proccessing for * errors only, so just keep going */ if (LOGDEBUG) { log_debug("\nSkipping uses w/errors in mod %s on line %u", mod->name, obj->tkerr.linenum); } return NO_ERR; } #if 0 if (obj_get_status(obj) == NCX_STATUS_OBSOLETE) { if (LOGDEBUG) { log_debug("\nSkip expand of obsolete uses '%s' " "in %smodule '%s'", uses->grp, mod->ismod ? "" : "sub", mod->name); } return NO_ERR; } #endif YANG_OBJ_DEBUG_USES4( "\nexpand_uses: uses '%s' in mod '%s' on line %u", uses->grp->name, mod->name, obj->tkerr.linenum); status_t res = NO_ERR, retres = NO_ERR; if (!uses->grp->expand_done) { /* go through the grouping and make sure all the * nested uses-stmts are expanded first */ YANG_OBJ_DEBUG_USES4( "\nexpand_uses: need expand of grouping %s", uses->grp->name); res = yang_obj_resolve_uses(pcb, tkc, mod, &uses->grp->datadefQ); CHK_EXIT(res, retres); uses->grp->expand_done = TRUE; } /* go through each node in the grouping * make sure it is not already in the same datadefQ * as the uses; don't check the module name since * augments has not been expanded yet * clone the object and add it inline to the datadefQ */ obj_template_t *chobj = (obj_template_t *) dlq_firstEntry(&uses->grp->datadefQ); for (; chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { YANG_OBJ_DEBUG_USES4( "\nexpand_uses: object %s in mod %s on line %u", obj_get_name(chobj), mod->name, chobj->tkerr.linenum ); obj_template_t *testobj = NULL; const xmlChar *name = NULL; switch (chobj->objtype) { case OBJ_TYP_USES: /* expand should already be done */ case OBJ_TYP_AUGMENT: case OBJ_TYP_REFINE: break; default: name = obj_get_name(chobj); testobj = obj_find_template_test(datadefQ, NULL, name); if (testobj) { log_error("\nError: object '%s' already defined at line %u", name, testobj->tkerr.linenum); retres = set_tkc_error( tkc, mod, &chobj->tkerr, ERR_NCX_DUP_ENTRY ); } else { obj_template_t *newobj = obj_clone_template(mod, chobj, uses->datadefQ); if (!newobj) { return set_tkc_error( tkc, mod, &chobj->tkerr, ERR_INTERNAL_MEM ); } else { /* set the object module (and namespace) * to the target, not the module w/ grouping * !!! this does not work -- it just sets * !!! the top-level node being expanded; * !!! all the children get the old module name * * !!! newobj->tkerr.mod = obj->tkerr.mod; */ newobj->parent = obj->parent; newobj->usesobj = obj; if (!(newobj->flags & OBJ_FL_CONFSET)) { boolean config = obj_get_config_flag_deep(newobj); if (config) { newobj->flags |= OBJ_FL_CONFIG; } else { newobj->flags &= ~OBJ_FL_CONFIG; } } /* need to add backptrs to any if-feature and when * statements within this object or inherited by * this object !! TBD: make sure seuqnce is correct !! */ res = add_inherited_conditionals(obj, newobj); if (res != NO_ERR) { obj_free_template(newobj); return set_tkc_error(tkc, mod, &chobj->tkerr, res); } dlq_insertAhead(newobj, obj); YANG_OBJ_DEBUG_USES4( "\nexpand_uses: " "add new obj '%s' to parent '%s', uses.%u", obj_get_name(newobj), (obj->grp) ? obj->grp->name : ((obj->parent) ? obj_get_name(obj->parent) : NCX_EL_NONE), obj->tkerr.linenum); } } } } /* go through each node in the uses datadefQ * looking for augments within the uses * expand the augment within the same Q * as the uses */ for (chobj = (obj_template_t *) dlq_firstEntry(uses->datadefQ); chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { if (chobj->objtype != OBJ_TYP_AUGMENT) { continue; } YANG_OBJ_DEBUG_USES3( "\nexpand_uses_augment: mod %s, augment on line %u", mod->name, chobj->tkerr.linenum); res = expand_augment(pcb, tkc, mod, chobj, datadefQ); CHK_EXIT(res, retres); } YANG_OBJ_DEBUG_USES4( "\nyang_obj: uses '%s'; datadefQ after expand", uses->grp->name); if (LOGDEBUG4) { obj_dump_child_list(datadefQ, NCX_DEF_INDENT, NCX_DEF_INDENT); } return retres; } /* expand_uses */ /******************************************************************** * FUNCTION resolve_augment * * Check the augment object type * Error messages are printed by this function!! Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * aug == obj_augment_t to check * obj == parent object for 'aug' * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_augment (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_augment_t *aug, obj_template_t *obj) { status_t res, retres; retres = NO_ERR; res = check_parent(tkc, mod, obj); CHK_EXIT(res, retres); /* figure out augment target later */ /* check if correct target Xpath string form is present */ if (obj->parent && !obj_is_root(obj->parent) && aug->target && *aug->target == '/') { /* absolute-schema-nodeid target not allowed */ log_error("\nError: absolute schema-nodeid form" " not allowed in nested augment statement"); retres = ERR_NCX_INVALID_VALUE; SET_OBJ_CURERR(tkc, obj); ncx_print_errormsg(tkc, mod, retres); } /* check if correct target Xpath string form is present */ if ((!obj->parent || obj_is_root(obj->parent)) && (aug->target && *aug->target != '/')) { /* absolute-schema-nodeid target must be used */ log_error("\nError: descendant schema-nodeid form" " not allowed in top-level augment statement"); retres = ERR_NCX_INVALID_AUGTARGET; SET_OBJ_CURERR(tkc, obj); ncx_print_errormsg(tkc, mod, retres); } /* resolve augment contents */ res = yang_obj_resolve_datadefs(pcb, tkc, mod, &aug->datadefQ); CHK_EXIT(res, retres); return retres; } /* resolve_augment */ /******************************************************************** * FUNCTION expand_augment * * Expand the indicated top-level or nested augment inline, * inserted into the tree at the specified node * * Note that nested augment clauses are only allowed to * use the descendant-schema-nodeid form of Xpath expression, * and the target must therefore be within the sibling sub-trees * contained in the datadefQ * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * obj == obj_template_t that contains the obj_augment_t to check * datadefQ == Q of obj_template_t that contains 'obj' * * RETURNS: * status of the operation *********************************************************************/ static status_t expand_augment (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj, dlq_hdr_t *datadefQ) { obj_augment_t *aug = obj->def.augment; if (!aug->target) { /* this node has errors, currently proccessing for * errors only, so just keep going */ return NO_ERR; } #if 0 if (obj_get_status(obj) == NCX_STATUS_OBSOLETE) { if (LOGDEBUG) { log_debug("\nSkip expand of obsolete augment '%s' " "in %smodule '%s'", aug->target, mod->ismod ? "" : "sub", mod->name); } return NO_ERR; } #endif obj_template_t *targobj = NULL, *testobj = NULL; dlq_hdr_t *augQ = &aug->datadefQ; #ifdef ENABLE_DIRECT_MUST_AUGMENT_EX dlq_hdr_t *aug_mustQ = &aug->mustQ; #endif status_t retres = NO_ERR; /* find schema-nodeid target * the node being augmented MUST exist to be valid */ status_t res = xpath_find_schema_target(pcb, tkc, mod, obj, datadefQ, aug->target, &targobj, NULL); if (res != NO_ERR) { return res; } aug->targobj = targobj; boolean augextern = xml_strcmp(obj_get_mod_name(obj), obj_get_mod_name(targobj)); boolean augdefcase = (targobj->objtype == OBJ_TYP_CASE && targobj->parent && targobj->parent->def.choic->defval && !xml_strcmp(obj_get_name(targobj), targobj->parent->def.choic->defval)) ? TRUE : FALSE; /* check external augment for mandatory nodes */ if (augextern || augdefcase) { /* check that all the augment nodes are optional */ for (testobj = (obj_template_t *)dlq_firstEntry(augQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { if (!obj_has_name(testobj)) { continue; } if (testobj->tkerr.mod->langver == NCX_YANG_VERSION10 && obj_is_mandatory(testobj)) { if (augextern) { log_error("\nError: Mandatory object '%s' not allowed " "in external augment statement", obj_get_name(testobj)); } else { log_error("\nError: Mandatory object '%s' not allowed " "in default case '%s'", obj_get_name(testobj), obj_get_name(targobj)); } retres = set_tkc_error( tkc, mod, &testobj->tkerr, ERR_NCX_MANDATORY_NOT_ALLOWED ); } } } /* make sure the objects augmented the target node * are OK for that object type */ switch (targobj->objtype) { case OBJ_TYP_RPC: log_error("\nError: cannot augment rpc node '%s'; use 'input' " "or 'output' instead", obj_get_name(targobj)); retres = set_tkc_error( tkc, mod, &obj->tkerr, ERR_NCX_INVALID_AUGTARGET ); break; case OBJ_TYP_CHOICE: for (testobj = (obj_template_t *)dlq_firstEntry(augQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { switch (testobj->objtype) { case OBJ_TYP_RPC: case OBJ_TYP_RPCIO: case OBJ_TYP_NOTIF: case OBJ_TYP_CHOICE: log_error("\nError: invalid %s '%s' augmenting choice node", obj_get_typestr(testobj), obj_get_name(testobj)); retres = set_tkc_error( tkc, mod, &obj->tkerr, ERR_NCX_INVALID_AUGTARGET ); break; case OBJ_TYP_NONE: case OBJ_TYP_USES: case OBJ_TYP_AUGMENT: case OBJ_TYP_REFINE: retres = SET_ERROR(ERR_INTERNAL_VAL); break; default: ; } } break; case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: break; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: log_error("\nError: cannot augment anyxml node '%s'", obj_get_name(targobj)); retres = set_tkc_error( tkc, mod, &obj->tkerr, ERR_NCX_INVALID_AUGTARGET ); break; default: for (testobj = (obj_template_t *)dlq_firstEntry(augQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { switch (testobj->objtype) { case OBJ_TYP_RPC: case OBJ_TYP_RPCIO: case OBJ_TYP_NOTIF: case OBJ_TYP_CASE: log_error("\nError: invalid %s '%s' augmenting data node", obj_get_typestr(testobj), obj_get_name(testobj)); retres = set_tkc_error( tkc, mod, &obj->tkerr, ERR_NCX_INVALID_AUGTARGET ); break; case OBJ_TYP_NONE: case OBJ_TYP_AUGMENT: case OBJ_TYP_REFINE: retres = SET_ERROR(ERR_INTERNAL_VAL); break; default: ; } } } /* get the augment target datadefQ */ dlq_hdr_t *targQ = obj_get_datadefQ(targobj); #ifdef ENABLE_DIRECT_MUST_AUGMENT_EX dlq_hdr_t *targ_mustQ = obj_get_mustQ(targobj); if (!targQ && !targ_mustQ) #else if (!targQ) #endif { log_error("\nError: %s '%s' cannot be augmented", obj_get_typestr(targobj), obj_get_name(targobj)); retres = set_tkc_error( tkc, mod, &targobj->tkerr, ERR_NCX_INVALID_AUGTARGET ); return retres; } #ifdef ENABLE_DIRECT_MUST_AUGMENT_EX if(targ_mustQ && aug_mustQ) { /* Move the must statements */ xpath_pcb_t *must; xpath_pcb_t *must_clone; must = (xpath_pcb_t *)dlq_firstEntry(aug_mustQ); for (; must != NULL; must = (xpath_pcb_t*)dlq_nextEntry(must)) { must_clone = xpath_clone_pcb(must); dlq_enque(must_clone, targ_mustQ); } } if(!targQ) { return retres; } #endif /* go through each node in the augment * make sure it is not already in the same datadefQ * if not, then clone the grouping object and add it * to the augment target */ const xmlChar *name = NULL; obj_template_t *chobj = (obj_template_t *)dlq_firstEntry(augQ); for (; chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { YANG_OBJ_DEBUG4( "\nexpand_aug: mod %s, object %s, on line %u", mod->name, obj_get_name(chobj), chobj->tkerr.linenum); switch (chobj->objtype) { case OBJ_TYP_USES: /* expand should already be done */ break; case OBJ_TYP_AUGMENT: /* recurse and expand the child node augment first * !!! YANG does not allow child nodes of augment-stmt * !!! to be another augment-stmt; there must be a * !!! data-def-stmt, which does not contain augment-stmt * !!! THIS CODE SHOULD NOT BE INVOKED */ res = expand_augment(pcb, tkc, mod, chobj, &aug->datadefQ); CHK_EXIT(res, retres); break; default: name = obj_get_name(chobj); /* try to find the node in any namespace (warning) */ testobj = obj_find_template_test(targQ, NULL, name); if (testobj && xml_strcmp(obj_get_mod_name(testobj), obj_get_mod_name(chobj))) { if (ncx_warning_enabled(ERR_NCX_DUP_AUGNODE)) { log_warn("\nWarning: sibling object '%s' " "already defined " "in %smodule '%s' at line %u", name, (testobj->tkerr.mod->ismod) ? "" : "sub", testobj->tkerr.mod->name, testobj->tkerr.linenum); res = set_tkc_error( tkc, mod, &chobj->tkerr, ERR_NCX_DUP_AUGNODE ); } else { ncx_inc_warnings(mod); } } /* try to find the node in the target namespace (error) */ testobj = obj_find_template_test(targQ, obj_get_mod_name(targobj), name); if (testobj) { log_error("\nError: object '%s' already defined " "in %smodule '%s' at line %u", name, (testobj->tkerr.mod->ismod) ? "" : "sub", testobj->tkerr.mod->name, testobj->tkerr.linenum); retres = set_tkc_error( tkc, mod, &chobj->tkerr, ERR_NCX_DUP_ENTRY ); } else { /* OK to create the new name * make a clone of the augment object in * case this augment is inside a grouping */ obj_template_t *newobj = NULL; if (targobj->objtype == OBJ_TYP_CHOICE) { /* make sure all the child nodes are wrapped * in a OBJ_TYP_CASE node -- this has not * been checked yet */ newobj = obj_clone_template_case(mod, chobj, NULL); } else { /* create a cloned object with the namespace of the * module defining the augment */ newobj = obj_clone_template(mod, chobj, NULL); } if (!newobj) { return set_tkc_error( tkc, mod, &chobj->tkerr, ERR_INTERNAL_MEM ); } else { newobj->parent = targobj; newobj->flags |= OBJ_FL_AUGCLONE; newobj->augobj = obj; obj_set_ncx_flags(newobj); /* need to add backptrs to any if-feature and when * statements within this object or inherited by * this object !! TBD: make sure seuqnce is correct !! */ res = add_inherited_conditionals(obj, newobj); if (res != NO_ERR) { obj_free_template(newobj); return set_tkc_error(tkc, mod, &chobj->tkerr, res); } dlq_enque(newobj, targQ); /* may need to set the config flag now, under the context * of the actual target, not within the grouping */ if (!(newobj->flags & OBJ_FL_CONFSET)) { boolean config = obj_get_config_flag_deep(newobj); if (config) { newobj->flags |= OBJ_FL_CONFIG; } else { newobj->flags &= ~OBJ_FL_CONFIG; } } YANG_OBJ_DEBUG4( "\nexpand_aug: " "add new obj '%s' to target %s.%u, aug.%u", obj_get_name(newobj), obj_get_name(targobj), targobj->tkerr.linenum, obj->tkerr.linenum); } } } } return retres; } /* expand_augment */ /******************************************************************** * FUNCTION resolve_augextern_final * * Check for cloned lists that need final internal data structure * modifications * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * datadefQ == Q of obj_template_t to check * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_augextern_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ) { obj_template_t *chobj; dlq_hdr_t *child_datadefQ; status_t res, retres; res = NO_ERR; retres = NO_ERR; for (chobj = (obj_template_t *)dlq_firstEntry(datadefQ); chobj != NULL; chobj = (obj_template_t *)dlq_nextEntry(chobj)) { if (!obj_has_name(chobj) /*|| obj_get_status(chobj) == NCX_STATUS_OBSOLETE*/) { continue; } if (chobj->objtype == OBJ_TYP_LIST) { res = resolve_list_final(pcb, tkc, mod, chobj->def.list, chobj); CHK_EXIT(res, retres); } child_datadefQ = obj_get_datadefQ(chobj); if (child_datadefQ != NULL) { res = resolve_augextern_final(pcb, tkc, mod, child_datadefQ); CHK_EXIT(res, retres); } } return retres; } /* resolve_augextern_final */ /******************************************************************** * FUNCTION resolve_augment_final * * Check for cloned lists that need final internal data structure * modifications * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * obj == obj_template_t of the augment target (augmented obj) * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_augment_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *obj) { obj_augment_t *aug; dlq_hdr_t *targQ; status_t res; boolean augextern; aug = obj->def.augment; if (aug->target == NULL) { /* this node has errors, currently proccessing for * errors only, so just keep going */ return NO_ERR; } #if 0 if (obj_get_status(obj) == NCX_STATUS_OBSOLETE) { /* already reported in expand_augment */ return NO_ERR; } #endif if (aug->targobj == NULL) { return ERR_NCX_OPERATION_FAILED; } augextern = xml_strcmp(obj_get_mod_name(obj), obj_get_mod_name(aug->targobj)); if (!augextern) { return NO_ERR; } /* get the augment target datadefQ */ targQ = obj_get_datadefQ(aug->targobj); #ifdef ENABLE_DIRECT_MUST_AUGMENT_EX if(targQ == NULL) { if (!aug->direct_must_augment_ex) { return ERR_NCX_OPERATION_FAILED; } else { /* direct_must_augment_ex can only have must statements */ return NO_ERR; } } #else if (targQ == NULL) { return ERR_NCX_OPERATION_FAILED; } #endif /* go through each node in the augment * make sure it is not already in the same datadefQ * if not, then clone the grouping object and add it * to the augment target */ res = resolve_augextern_final(pcb, tkc, mod, targQ); return res; } /* resolve_augment_final */ /******************************************************************** * FUNCTION resolve_deviation * * Resolve and possibly expand the indicated top-level deviation-stmt * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * deviation == obj_deviation_t to validate * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_deviation (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_deviation_t *deviation) { obj_deviate_t *devi, *nextdevi; obj_template_t *targobj; const xmlChar *curval; xpath_pcb_t *must, *targmust; obj_unique_t *unique, *targunique; dlq_hdr_t *targQ; status_t res, retres; boolean instancetest, curexists, ingrp; retres = NO_ERR; targobj = NULL; instancetest = FALSE; /* find schema-nodeid target * the node being augmented MUST exist to be valid */ res = xpath_find_schema_target(pcb, tkc, mod, NULL, &mod->datadefQ, deviation->target, &targobj, NULL); if (res != NO_ERR) { return res; } deviation->targobj = targobj; deviation->targmodname = xml_strdup(obj_get_mod_name(targobj)); if (deviation->targmodname == NULL) { return ERR_INTERNAL_MEM; } /* make sure all the deviate statements are * are OK for that object type */ for (devi = (obj_deviate_t *) dlq_firstEntry(&deviation->deviateQ); devi != NULL; devi = nextdevi) { nextdevi = (obj_deviate_t *)dlq_nextEntry(devi); res = NO_ERR; /* check if no sub-clauses are expected */ if (devi->arg == OBJ_DARG_NOT_SUPPORTED) { continue; } else if (devi->arg == OBJ_DARG_ADD) { /* current clause must not exist */ instancetest = FALSE; } else { /* current clause must exist */ instancetest = TRUE; } /* check the object type against the clauses * in this deviate statement; * check type-stmt first */ if (devi->typdef) { /* replace type statement entered */ if (!obj_is_leafy(targobj)) { log_error("\nError: target '%s' is a '%s': " "type-stmt not allowed", deviation->target, obj_get_typestr(targobj)); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } else { res = yang_typ_resolve_type(pcb, tkc, mod, devi->typdef, obj_get_default(targobj), targobj); CHK_EXIT(res, retres); } } /* check if units-stmt was entered */ if (devi->units) { if (!obj_is_leafy(targobj)) { log_error("\nError: target '%s' is a '%s': " "type-stmt not allowed", deviation->target, obj_get_typestr(targobj)); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } else { switch (targobj->objtype) { case OBJ_TYP_LEAF: curexists = (targobj->def.leaf->units) ? TRUE : FALSE; break; case OBJ_TYP_LEAF_LIST: curexists = (targobj->def.leaflist->units) ? TRUE : FALSE; break; default: curexists = FALSE; res = retres = SET_ERROR(ERR_INTERNAL_VAL); } if (instancetest && !curexists) { log_error("\nError: 'units' must exist in " "deviate target '%s'", deviation->target); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } else if (!instancetest && curexists) { log_error("\nError: 'units' must not exist in " "deviate target '%s'", deviation->target); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } else if (devi->arg == OBJ_DARG_DELETE && xml_strcmp(devi->units, obj_get_units(targobj))) { log_error("\nError: 'units' value '%s' must match in " "deviate target '%s'", devi->units, deviation->target); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } } /* check if default-stmt entered */ if (devi->defval) { if ((targobj->objtype == OBJ_TYP_LEAF) || targobj->objtype == OBJ_TYP_CHOICE) { if (targobj->objtype == OBJ_TYP_CHOICE) { curval = targobj->def.choic->defval; } else { curval = targobj->def.leaf->defval; } curexists = (curval) ? TRUE : FALSE; if (instancetest && !curexists) { log_error("\nError: 'default' must exist in " "deviate target '%s'", deviation->target); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } else if (!instancetest && curexists) { log_error("\nError: 'default' must not exist in " "deviate target '%s'", deviation->target); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } else if (devi->arg == OBJ_DARG_DELETE && xml_strcmp(devi->defval, curval)) { log_error("\nError: 'default' value '%s' must match in " "deviate target '%s'", devi->defval, deviation->target); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } else { log_error("\nError: target '%s' is a '%s': " "default-stmt not allowed", deviation->target, obj_get_typestr(targobj)); ncx_print_errormsg(tkc, mod, retres); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } /* check if config-stmt entered */ if (devi->config_tkerr.mod) { switch (targobj->objtype) { case OBJ_TYP_LEAF: ingrp = FALSE; if (!devi->config && obj_is_key(targobj) && obj_get_config_flag_check(targobj, &ingrp)) { if (!ingrp) { log_error("\nError: leaf %s:%s is a key; " "cannot change config to false", obj_get_mod_name(targobj), obj_get_name(targobj)); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } break; case OBJ_TYP_CONTAINER: case OBJ_TYP_LEAF_LIST: case OBJ_TYP_CHOICE: break; case OBJ_TYP_LIST: if (!obj_first_key(targobj) && devi->config) { log_error("\nError: list %s:%s has no key; " "cannot change config to true", obj_get_mod_name(targobj), obj_get_name(targobj)); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } break; default: log_error("\nError: target '%s' is a '%s': " "config-stmt not allowed", deviation->target, obj_get_typestr(targobj)); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } /* check if mandatory-stmt entered */ if (devi->mandatory_tkerr.mod) { switch (targobj->objtype) { case OBJ_TYP_CHOICE: case OBJ_TYP_LEAF: case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: break; default: log_error("\nError: target '%s' is a '%s': " "mandatory-stmt not allowed", deviation->target, obj_get_typestr(targobj)); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } /* check if min-elements stmt entered */ if (devi->minelems_tkerr.mod) { switch (targobj->objtype) { case OBJ_TYP_LEAF_LIST: case OBJ_TYP_LIST: break; default: log_error("\nError: target '%s' is a '%s': " "min-elements-stmt not allowed", deviation->target, obj_get_typestr(targobj)); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } /* check if max-elements stmt entered */ if (devi->maxelems_tkerr.mod) { switch (targobj->objtype) { case OBJ_TYP_LEAF_LIST: case OBJ_TYP_LIST: break; default: log_error("\nError: target '%s' is a '%s': " "max-elements-stmt not allowed", deviation->target, obj_get_typestr(targobj)); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } /* check if any must-stmts entered */ if (!dlq_empty(&devi->mustQ)) { switch (targobj->objtype) { case OBJ_TYP_CONTAINER: case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: case OBJ_TYP_LIST: targQ = obj_get_mustQ(targobj); if (!targQ) { res = SET_ERROR(ERR_INTERNAL_VAL); continue; } for (must = (xpath_pcb_t *)dlq_firstEntry(&devi->mustQ); must != NULL; must = (xpath_pcb_t *)dlq_nextEntry(must)) { targmust = xpath_find_pcb(targQ, must->exprstr); curexists = (targmust) ? TRUE : FALSE; if (instancetest && !curexists) { log_error("\nError: 'must %s' must exist in " "deviate target '%s'", must->exprstr, deviation->target); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } else if (!instancetest && curexists) { log_error("\nError: 'must %s' must not exist in " "deviate target '%s'", must->exprstr, deviation->target); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } break; default: log_error("\nError: target '%s' is a '%s': " "must-stmt not allowed", deviation->target, obj_get_typestr(targobj)); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } /* check if any unique-stmts entered */ if (!dlq_empty(&devi->uniqueQ)) { switch (targobj->objtype) { case OBJ_TYP_LIST: targQ = &targobj->def.list->uniqueQ; if (!targQ) { res = SET_ERROR(ERR_INTERNAL_VAL); continue; } for (unique = (obj_unique_t *) dlq_firstEntry(&devi->uniqueQ); unique != NULL; unique = (obj_unique_t *)dlq_nextEntry(unique)) { targunique = obj_find_unique(targQ, unique->xpath); curexists = (targunique) ? TRUE : FALSE; if (instancetest && !curexists) { log_error("\nError: 'unique %s' must exist in " "deviate target '%s'", unique->xpath, deviation->target); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } else if (!instancetest && curexists) { log_error("\nError: 'unique %s' must not exist in " "deviate target '%s'", unique->xpath, deviation->target); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } break; default: log_error("\nError: target '%s' is a '%s': " "unique-stmt not allowed", deviation->target, obj_get_typestr(targobj)); res = retres = set_tkc_error( tkc, mod, &devi->tkerr, ERR_NCX_INVALID_DEV_STMT ); } } /* finally, if entire deviate-stmt is OK save it * or else toss it */ if (res != NO_ERR) { /* toss this deviate-stmt; it is no good */ dlq_remove(devi); obj_free_deviate(devi); } /* else leave in this Q for HTML or YANG output */ } return retres; } /* resolve_deviation */ /******************************************************************** * FUNCTION transfer_my_deviations * * Find any deviations for the specified module * Resolve them, and stored the resolved obj_deviation_t * in the module deviationQ * * INPUTS: * pcb == parser control block to use * tkc == token parse chain to use for errors * savedev == save deviations struct to check * mod == module in progress; transfer to this deviationQ * runningtotal == address of return total deviation count * this will not be zeroed before using!!! * * OUTPUTS: * *runningtotal == running total of deviations added * * RETURNS: * status of the operation *********************************************************************/ static status_t transfer_my_deviations (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_save_deviations_t *savedev, ncx_module_t *mod, uint32 *runningtotal) { ncx_import_t *myimport; obj_deviation_t *deviation, *nextdeviation; ncx_module_t *dummymod; status_t res; boolean anydone; /* if 'mod->name' import does not show up, then this deviation * module cannot possibly contain and deviations for 'mod' */ myimport = ncx_find_import_que(&savedev->importQ, mod->name); if (myimport == NULL) { return NO_ERR; } /* check the revision if it is present to make sure * it is not for a different version of 'mod' */ if (myimport->revision) { if (mod->version == NULL || xml_strcmp(myimport->revision, mod->version)) { return NO_ERR; } } /* mock-up a dummy module for the deviations * so the resolve_deviation function can be * used directly */ dummymod = ncx_new_module(); if (dummymod == NULL) { return ERR_INTERNAL_MEM; } /* borrow some pointers from the savedev */ dummymod->name = savedev->devmodule; YANG_OBJ_DEBUG_MEMORY( "\n malloced %p module '%s'", dummymod, dummymod->name ); dummymod->ismod = TRUE; dummymod->prefix = savedev->devprefix; myimport->mod = mod; anydone = FALSE; /* temp move the imports from 1 Q to another */ dlq_block_enque(&savedev->importQ, &dummymod->importQ); /* the deviations file imported 'mod'; * check all the deviation targets to find * any for 'mod'; remove and resolve if found */ res = NO_ERR; for (deviation = (obj_deviation_t *) dlq_firstEntry(&savedev->deviationQ); deviation != NULL && res == NO_ERR; deviation = nextdeviation) { nextdeviation = (obj_deviation_t *) dlq_nextEntry(deviation); if (deviation->targobj == NULL) { /* deviation has not been resolved yet; * or the target was never found * and this fn call will fail */ res = resolve_deviation(pcb, tkc, dummymod, deviation); } if (res == NO_ERR) { if (deviation->targobj->tkerr.mod == mod) { if (LOGDEBUG) { log_debug("\nAdding external deviation " "to '%s', from '%s' to '%s'", obj_get_name(deviation->targobj), savedev->devmodule, mod->name); } /* transfer the deviation to the target module */ dlq_remove(deviation); dlq_enque(deviation, &mod->deviationQ); anydone = TRUE; (*runningtotal)++; } } /* check if any devmodlist entry needed */ if (anydone) { devmodlist_update(obj_get_mod(deviation->targobj), savedev); } } /* restore the savedev structure */ myimport->mod = NULL; dlq_block_enque(&dummymod->importQ, &savedev->importQ); dummymod->name = NULL; dummymod->prefix = NULL; ncx_free_module(dummymod); return res; } /* transfer_my_deviations */ /******************************************************************** * FUNCTION resolve_rpc * * Check the rpc method object type * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * rpc == obj_rpc_t to check * obj == parent object for 'rpc' * redo == TRUE if this is a 2nd pass due to deviations added * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_rpc (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_rpc_t *rpc, obj_template_t *obj, boolean redo) { status_t res, retres; retres = NO_ERR; if (!redo) { res = yang_typ_resolve_typedefs(pcb, tkc, mod, &rpc->typedefQ, obj); CHK_EXIT(res, retres); res = yang_grp_resolve_groupings(pcb, tkc, mod, &rpc->groupingQ, obj); CHK_EXIT(res, retres); } res = resolve_datadefs(pcb, tkc, mod, &rpc->datadefQ, redo); CHK_EXIT(res, retres); return retres; } /* resolve_rpc */ /******************************************************************** * FUNCTION resolve_rpcio * * Check the rpc input or output object type * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * rpcio == obj_rpcio_t to check * obj == parent object for 'rpcio' * redo == TRUE if this is a 2nd pass due to deviations added * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_rpcio (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_rpcio_t *rpcio, obj_template_t *obj, boolean redo) { status_t res, retres; retres = NO_ERR; if (!redo) { res = yang_typ_resolve_typedefs(pcb, tkc, mod, &rpcio->typedefQ, obj); CHK_EXIT(res, retres); res = yang_grp_resolve_groupings(pcb, tkc, mod, &rpcio->groupingQ, obj); CHK_EXIT(res, retres); } res = resolve_datadefs(pcb, tkc, mod, &rpcio->datadefQ, redo); CHK_EXIT(res, retres); return retres; } /* resolve_rpcio */ /******************************************************************** * FUNCTION resolve_notif * * Check the notification object type * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * notif == obj_notif_t to check * obj == parent object for 'notif' * redo == TRUE if this is a 2nd pass due to deviations added * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_notif (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_notif_t *notif, obj_template_t *obj, boolean redo) { status_t res, retres; retres = NO_ERR; if (!redo) { res = yang_typ_resolve_typedefs(pcb, tkc, mod, ¬if->typedefQ, obj); CHK_EXIT(res, retres); res = yang_grp_resolve_groupings(pcb, tkc, mod, ¬if->groupingQ, obj); CHK_EXIT(res, retres); } /* resolve notification contents */ res = resolve_datadefs(pcb, tkc, mod, ¬if->datadefQ, redo); CHK_EXIT(res, retres); return retres; } /* resolve_notif */ /******************************************************************** * FUNCTION resolve_datadef * * First pass object validation * * Analyze the entire datadefQ within the module struct * Finish all the clauses within this struct that * may have been defered because of possible forward references * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * testobj == obj_template_t to check * redo == TRUE if this is a 2nd pass due to deviations added * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_datadef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *testobj, boolean redo) { status_t res, retres; res = NO_ERR; retres = NO_ERR; YANG_OBJ_DEBUG4( "\nyang_obj_resolve: %s", obj_get_name(testobj)); if (!redo) { xmlChar *namestr; res = ncx_resolve_appinfoQ(pcb, tkc, mod, &testobj->appinfoQ); CHK_EXIT(res, retres); if (asprintf((char **)&namestr, "object '%s'", obj_get_name(testobj)) < 0) { res = ERR_INTERNAL_MEM; } else { res = ncx_resolve_iffeatureQ(pcb, tkc, mod, namestr, &testobj->iffeatureQ); } CHK_EXIT(res, retres); } switch (testobj->objtype) { case OBJ_TYP_CONTAINER: res = resolve_container(pcb, tkc, mod, testobj->def.container, testobj, redo); break; case OBJ_TYP_LEAF: case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: res = resolve_leaf(pcb, tkc, mod, testobj->def.leaf, testobj, redo); break; case OBJ_TYP_LEAF_LIST: res = resolve_leaflist(pcb, tkc, mod, testobj->def.leaflist, testobj, redo); break; case OBJ_TYP_LIST: res = resolve_list(pcb, tkc, mod, testobj->def.list, testobj, redo); break; case OBJ_TYP_CHOICE: res = resolve_choice(pcb, tkc, mod, testobj->def.choic, redo); break; case OBJ_TYP_CASE: res = resolve_case(pcb,tkc, mod, testobj->def.cas, redo); break; case OBJ_TYP_USES: if (!redo) { res = resolve_uses(pcb, tkc, mod, testobj->def.uses, testobj); } break; case OBJ_TYP_AUGMENT: if (!redo) { res = resolve_augment(pcb, tkc, mod, testobj->def.augment, testobj); } break; case OBJ_TYP_RPC: res = resolve_rpc(pcb, tkc, mod, testobj->def.rpc, testobj, redo); break; case OBJ_TYP_RPCIO: res = resolve_rpcio(pcb, tkc, mod, testobj->def.rpcio, testobj, redo); break; case OBJ_TYP_NOTIF: res = resolve_notif(pcb, tkc, mod, testobj->def.notif, testobj, redo); break; case OBJ_TYP_REFINE: res = NO_ERR; break; case OBJ_TYP_NONE: default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* set the flags again; they were already * set once during the consume_foo function * which means that named typedefs and other * references are not resolved yet; need to * go through again and check some flags */ obj_set_ncx_flags(testobj); CHK_EXIT(res, retres); return retres; } /* resolve_datadef */ /******************************************************************** * FUNCTION resolve_datadefs * * First pass object validation * * Analyze the entire datadefQ within the module struct * Finish all the clauses within this struct that * may have been defered because of possible forward references * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_datadefs (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ, boolean redo) { obj_template_t *testobj; status_t res, retres; retres = NO_ERR; /* first resolve all the local type names */ for (testobj = (obj_template_t *)dlq_firstEntry(datadefQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { res = resolve_datadef(pcb, tkc, mod, testobj, redo); CHK_EXIT(res, retres); } return retres; } /* resolve_datadefs */ /******************************************************************** * FUNCTION consume_datadef * * Parse the next N tokens as a data-def-stmt * Create a obj_template_t struct and add it to the specified module * * First pass of a 3 pass compiler * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the first keyword, starting the specific * data definition * * * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == queue will get the obj_template_t * parent == parent object or NULL if top-level data-def-stmt * grp == grp_template_t parent or NULL if parent is not a grouping * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_datadef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { const xmlChar *val; const char *expstr; tk_type_t tktyp; boolean errdone; status_t res; expstr = "anyxml, anydata, container, leaf, leaf-list, list, choice, uses," "augment, notification keyword"; errdone = TRUE; res = NO_ERR; tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ if (tktyp != TK_TT_TSTRING) { res = ERR_NCX_WRONG_TKTYPE; errdone = FALSE; } else { /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_ANYXML)) { res = consume_anyxml(tkc, mod, que, parent, grp); } else if (!xml_strcmp(val, YANG_K_ANYDATA)) { res = consume_anydata(tkc, mod, que, parent, grp); } else if (!xml_strcmp(val, YANG_K_CONTAINER)) { res = consume_container(pcb, tkc, mod, que, parent, grp); } else if (!xml_strcmp(val, YANG_K_LEAF)) { res = consume_leaf(pcb, tkc, mod, que, parent, grp); } else if (!xml_strcmp(val, YANG_K_LEAF_LIST)) { res = consume_leaflist(pcb, tkc, mod, que, parent, grp); } else if (!xml_strcmp(val, YANG_K_LIST)) { res = consume_list(pcb, tkc, mod, que, parent, grp); } else if (!xml_strcmp(val, YANG_K_CHOICE)) { res = consume_choice(pcb, tkc, mod, que, parent, grp); } else if (!xml_strcmp(val, YANG_K_USES)) { res = consume_uses(pcb, tkc, mod, que, parent, grp); } else if ((mod->langver>=NCX_YANG_VERSION11) && !xml_strcmp(val, YANG_K_ACTION)) { res = consume_action(pcb, tkc, mod, que, parent, grp); } else if ((mod->langver>=NCX_YANG_VERSION11) && !xml_strcmp(val, YANG_K_NOTIFICATION)) { res = yang_obj_consume_notification(pcb, tkc, mod, que, parent, grp); } else { res = ERR_NCX_WRONG_TKVAL; errdone = FALSE; } } if (res != NO_ERR && !errdone) { log_error("\nError: '%s' token not allowed here", val); ncx_mod_exp_err(tkc, mod, res, expstr); yang_skip_statement(tkc, mod); } return res; } /* consume_datadef */ /******************************************************************** * FUNCTION check_iffeature_mismatch * * Check the child object against the ancestor node for 1 if-feature * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * ancestor == ancestor node of child to compare against * and stop the check * testobj == current object being checked * iff == current if-feature record within the testobj to check * iserror == TRUE if checking for key-stmt and mismatch is an error * * RETURNS: * status of the operation *********************************************************************/ static status_t check_iffeature_mismatch (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *ancestor, obj_template_t *testobj, ncx_iffeature_t *iff, boolean iserror) { status_t res; res = NO_ERR; if (!ncx_find_iffeature_1dot1(&ancestor->iffeatureQ, iff->prefix, iff->name, iff->expr, mod->prefix)) { if (iserror) { res = ERR_NCX_INVALID_CONDITIONAL; log_error("\nError: if-feature '%s' present for " "%s '%s', but not in list '%s'", iff->name, obj_get_typestr(testobj), obj_get_name(testobj), obj_get_name(ancestor)); tkc->curerr = &iff->tkerr; ncx_print_errormsg(tkc, mod, res); } else if (ncx_warning_enabled(ERR_NCX_UNIQUE_CONDITIONAL_MISMATCH)) { log_warn("\nWarning: if-feature '%s' present for %s '%s', " "but not in list '%s'", iff->name, obj_get_typestr(testobj), obj_get_name(testobj), obj_get_name(ancestor)); tkc->curerr = &iff->tkerr; ncx_print_errormsg(tkc, mod, ERR_NCX_UNIQUE_CONDITIONAL_MISMATCH); } } return res; } /* check_iffeature_mismatch */ /******************************************************************** * FUNCTION check_one_when_mismatch * * Check one object when clause(s) against another * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * test1 == node to check * test2 == node to check * iserror == TRUE if checking for key-stmt and mismatch is an error * RETURNS: * status of the operation *********************************************************************/ static status_t check_one_when_mismatch (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *test1, obj_template_t *test2, boolean iserror ) { if (!test1->when) { return NO_ERR; } if (test2->when) { if (!xml_strcmp(test1->when->exprstr, test2->when->exprstr)) { return NO_ERR; } } if (test2->usesobj && test2->usesobj->when) { if (!xml_strcmp(test1->when->exprstr, test2->usesobj->when->exprstr)) { return NO_ERR; } } if (test2->augobj && test2->augobj->when) { if (!xml_strcmp(test1->when->exprstr, test2->augobj->when->exprstr)) { return NO_ERR; } } if (iserror) { log_error("\nError: when-stmt for key object '%s' " "not applied to list %s", obj_get_name(test1), obj_get_name(test2)); tkc->curerr = &test1->when->tkerr; ncx_print_errormsg(tkc, mod, ERR_NCX_INVALID_CONDITIONAL); return ERR_NCX_INVALID_CONDITIONAL; } else if (ncx_warning_enabled(ERR_NCX_UNIQUE_CONDITIONAL_MISMATCH)) { log_warn("\nWarning: when-stmt for object '%s' not applied " "to parent list '%s' with unique-stmt", obj_get_name(test1), obj_get_name(test2)); tkc->curerr = &test1->when->tkerr; ncx_print_errormsg(tkc, mod, ERR_NCX_UNIQUE_CONDITIONAL_MISMATCH); } return NO_ERR; } /* check_one_when_mismatch */ /******************************************************************** * FUNCTION check_conditional_mismatch * * Check the child object against the ancestor node to see * if any child conditionals are present that are not * present in the path to the ancestor. Treat this * as an error * * Do not call for every node! * - (parent-list, key-leaf) * - (unique-list, unique-node) * - (parent-choice, default-case) * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain * mod == module in progress * ancestor == ancestor node of child to compare against * and stop the check * child == child object to check * iserror == TRUE if checking for key-stmt and mismatch is an error * RETURNS: * status of the operation *********************************************************************/ static status_t check_conditional_mismatch (tk_chain_t *tkc, ncx_module_t *mod, obj_template_t *ancestor, obj_template_t *child, boolean iserror ) { obj_template_t *testobj; ncx_iffeature_t *iff; status_t res, retres; boolean done; retres = NO_ERR; testobj = child->parent; if (!testobj) { return SET_ERROR(ERR_INTERNAL_VAL); } if (ancestor->objtype==OBJ_TYP_CHOICE && child->objtype==OBJ_TYP_CASE) { } else { /* make sure that the child node does not introduce * any if-features that are not in the ancestor node */ testobj = child; done = FALSE; /* go up the tree from the child to the level before * the ancestor */ while (!done) { /* check all possible when clauses in the testobj * against the ancestor node */ res = check_one_when_mismatch(tkc, mod, testobj, ancestor, iserror); CHK_EXIT(res, retres); if (testobj->usesobj) { res = check_one_when_mismatch(tkc, mod, testobj->usesobj, ancestor, iserror ); CHK_EXIT(res, retres); } if (testobj->augobj) { res = check_one_when_mismatch(tkc, mod, testobj->augobj, ancestor, iserror ); CHK_EXIT(res, retres); } /* check all the if-features in the testnode * against the ones in the ancestor; * a missing if-feature in the ancestor is an error */ for (iff = (ncx_iffeature_t *) dlq_firstEntry(&testobj->iffeatureQ); iff != NULL; iff = (ncx_iffeature_t *)dlq_nextEntry(iff)) { res = check_iffeature_mismatch(tkc, mod, ancestor, testobj, iff, iserror ); CHK_EXIT(res, retres); } /* check any extra if-features from the uses object */ if (testobj->usesobj) { for (iff = (ncx_iffeature_t *) dlq_firstEntry(&testobj->usesobj->iffeatureQ); iff != NULL; iff = (ncx_iffeature_t *)dlq_nextEntry(iff)) { if (!ncx_find_iffeature_1dot1(&testobj->iffeatureQ, iff->prefix, iff->name, iff->expr, mod->prefix)) { res = check_iffeature_mismatch(tkc, mod, ancestor, testobj, iff, iserror); CHK_EXIT(res, retres); } } } /* check any extra if-features from the augment object */ if (testobj->augobj) { for (iff = (ncx_iffeature_t *) dlq_firstEntry(&testobj->augobj->iffeatureQ); iff != NULL; iff = (ncx_iffeature_t *)dlq_nextEntry(iff)) { if (!ncx_find_iffeature_1dot1(&testobj->iffeatureQ, iff->prefix, iff->name, iff->expr, mod->prefix)) { res = check_iffeature_mismatch(tkc, mod, ancestor, testobj, iff, iserror ); CHK_EXIT(res, retres); } } } testobj = testobj->parent; if (!testobj) { return SET_ERROR(ERR_INTERNAL_VAL); } if (testobj == ancestor) { done = TRUE; } } } return retres; } /* check_conditional_mismatch */ /******************************************************************** * FUNCTION resolve_xpath * * Fifth (and final) pass object validation * * Check all leafref, must, and when XPath expressions * to make sure they are well-formed * * Checks the cooked objects, and skips all groupings * uses, and augment nodes * * MUST BE CALLED AFTER yang_obj_resolve_final * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == [sub]module in progress within the tree walking * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ static status_t resolve_xpath (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ) { obj_template_t *testobj; obj_template_t *leafobj; obj_key_t *key; obj_unique_t *uniq; obj_unique_comp_t *uncomp; typ_def_t *typdef; xpath_pcb_t *pcb; xpath_pcb_t *pcbclone; status_t res, retres; boolean is_targetmod; res = NO_ERR; retres = NO_ERR; /* check the must and when stmts in the entire subtree */ for (testobj = (obj_template_t *)dlq_firstEntry(datadefQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { /* check corner case -- submodules augmenting other * submodules; only evaluate in the context of the * submodule that defined the objects (augmentor, * not augmentee) */ if((testobj->tkerr.mod == mod) || (testobj->tkerr.mod == mod->parent)) { is_targetmod = TRUE; } else { /* handle case of used groupings from external modules */ obj_template_t *top_uses_obj; top_uses_obj=obj123_get_top_uses(testobj); if(top_uses_obj==NULL || top_uses_obj->tkerr.mod!=mod) { is_targetmod=FALSE; } else { is_targetmod=TRUE; } } if(is_targetmod==FALSE) { log_debug4("\nresolve_xpath: skipping obj %s %s not native to current module %s", obj_get_name(testobj), obj_get_typestr(testobj), mod->name); } if (LOGDEBUG4) { if (!obj_has_name(testobj)) { log_debug4("\nresolve_xpath: %s", obj_get_typestr(testobj)); } else { xmlChar *mybuff = NULL; res = obj_gen_object_id(testobj, &mybuff); if (res == NO_ERR) { log_debug4("\nresolve_xpath: %s", mybuff); } else { log_debug4("\nresolve_xpath: %s", obj_get_name(testobj)); } if (mybuff) { m__free(mybuff); } } if (is_targetmod) { log_debug4(" (targmod)"); } } /* check the when-stmt in the object itself * check uses and augment since they can have * their own when statements */ if (testobj->when && is_targetmod) { res = resolve_when(mod, testobj->when, testobj); CHK_EXIT(res, retres); } #if 0 if (!obj_has_name(testobj)) { /* skip augment and uses for the rest of the tests */ continue; } #endif /* validate correct Xpath in must clauses */ if (is_targetmod) { res = resolve_mustQ(tkc, mod, testobj); CHK_EXIT(res, retres); } switch (testobj->objtype) { case OBJ_TYP_CONTAINER: /* check container children */ res = resolve_xpath(tkc, mod, testobj->def.container->datadefQ); break; case OBJ_TYP_AUGMENT: /* check augment children */ res = resolve_xpath(tkc, mod, &testobj->def.augment->datadefQ); break; case OBJ_TYP_USES: /* check USES children */ res = resolve_xpath(tkc, mod, testobj->def.uses->datadefQ); break; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: break; case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: if (!is_targetmod) { break; } if (obj_get_basetype(testobj) == NCX_BT_LEAFREF) { YANG_OBJ_DEBUG3( "\nresolve_xpath: " "leafref in mod %s, object %s, on line %u", mod->name, obj_get_name(testobj), testobj->tkerr.linenum); /* need to make a copy of the XPath PCB because * typedefs are treated as read-only when referenced * from a val_value_t node */ typdef = obj_get_typdef(testobj); pcb = typ_get_leafref_pcb(typdef); YANG_OBJ_DEBUG3( "expr: %s", pcb->exprstr); pcbclone = xpath_clone_pcb(pcb); if (!pcbclone) { res = ERR_INTERNAL_MEM; } else { leafobj = NULL; res = xpath_yang_validate_path(mod, testobj, pcbclone, FALSE, &leafobj); if (res == NO_ERR && leafobj) { typ_set_xref_typdef(typdef, obj_get_typdef(leafobj)); if (testobj->objtype == OBJ_TYP_LEAF) { testobj->def.leaf->leafrefobj = leafobj; } else { testobj->def.leaflist->leafrefobj = leafobj; } } xpath_free_pcb(pcbclone); } } if ( res != NO_ERR ) { YANG_OBJ_DEBUG3( "\nresolve_xpath: FAILED (%s)", get_error_string(res)); } break; case OBJ_TYP_LIST: /* check that none of the key leafs have more * conditionals than their list parent */ if (is_targetmod) { for (key = obj_first_key(testobj); key != NULL; key = obj_next_key(key)) { if (key->keyobj) { res = check_conditional_mismatch(tkc, mod, testobj, key->keyobj, TRUE); CHK_EXIT(res, retres); } } } /* check that none of the unique set leafs have more * conditionals than their list parent */ if (is_targetmod) { for (uniq = obj_first_unique(testobj); uniq != NULL; uniq = obj_next_unique(uniq)) { for (uncomp = obj_first_unique_comp(uniq); uncomp != NULL; uncomp = obj_next_unique_comp(uncomp)) { if (uncomp->unobj) { res = check_conditional_mismatch(tkc, mod, testobj, uncomp->unobj, FALSE ); CHK_EXIT(res, retres); } } } } /* check list children */ res = resolve_xpath(tkc, mod, testobj->def.list->datadefQ); CHK_EXIT(res, retres); break; case OBJ_TYP_CHOICE: res = resolve_xpath(tkc, mod, testobj->def.choic->caseQ); break; case OBJ_TYP_CASE: res = resolve_xpath(tkc, mod, testobj->def.cas->datadefQ); break; case OBJ_TYP_RPC: res = resolve_xpath(tkc, mod, &testobj->def.rpc->datadefQ); break; case OBJ_TYP_RPCIO: res = resolve_xpath(tkc, mod, &testobj->def.rpcio->datadefQ); break; case OBJ_TYP_NOTIF: res = resolve_xpath(tkc, mod, &testobj->def.notif->datadefQ); break; case OBJ_TYP_REFINE: break; case OBJ_TYP_NONE: default: res = SET_ERROR(ERR_INTERNAL_VAL); } CHK_EXIT(res, retres); } return retres; } /* resolve_xpath */ /************ E X T E R N A L F U N C T I O N S ***************/ /******************************************************************** * FUNCTION yang_obj_consume_datadef * * Parse the next N tokens as a data-def-stmt * Create a obj_template_t struct and add it to the specified module * * First pass of a 3 pass compiler * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the first keyword, starting the specific * data definition * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == queue will get the obj_template_t * parent == parent object or NULL if top-level data-def-stmt * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_consume_datadef (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent) { status_t res; #ifdef DEBUG if (!pcb || !tkc || !mod || !que) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = consume_datadef(pcb, tkc, mod, que, parent, NULL); return res; } /* yang_obj_consume_datadef */ /******************************************************************** * FUNCTION yang_obj_consume_datadef_grp * * Parse the next N tokens as a data-def-stmt * Create a obj_template_t struct and add it to the specified module * * First pass of a 3 pass compiler * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the first keyword, starting the specific * data definition * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == queue will get the obj_template_t * parent == parent object or NULL if top-level data-def-stmt * grp == grp_template_t containing 'que' * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_consume_datadef_grp (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { status_t res; #ifdef DEBUG if (!pcb || !tkc || !mod || !que || !grp) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = consume_datadef(pcb, tkc, mod, que, parent, grp); return res; } /* yang_obj_consume_datadef_grp */ /******************************************************************** * FUNCTION yang_obj_consume_rpc * * Parse the next N tokens as a rpc-stmt * Create a obj_template_t struct and add it to the specified module * * First pass of a 3 pass compiler * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'rpc' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * * OUTPUTS: * new RPC added to module * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_consume_rpc (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod) { status_t res; #ifdef DEBUG if (!pcb || !tkc || !mod) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = consume_rpc(pcb, tkc, mod, &mod->datadefQ, NULL, NULL); return res; } /* yang_obj_consume_rpc */ /******************************************************************** * FUNCTION yang_obj_consume_notififcation * * Parse the next N tokens as a notification-stmt * Create and fill in an obj_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'notification' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * que == Q to hold the obj_template_t that gets created * parent == parent object or NULL if top-level * grp == parent grp_template_t or NULL if not child of grp * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_consume_notification (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *que, obj_template_t *parent, grp_template_t *grp) { obj_template_t *obj = NULL; obj_notif_t *notif = NULL; const xmlChar *val = NULL; const char *expstr = "keyword"; tk_type_t tktyp = TK_TT_NONE; boolean done = FALSE, stat = FALSE; boolean desc = FALSE, ref = FALSE; status_t res = NO_ERR, retres = NO_ERR; /* Get a new obj_template_t to fill in */ obj = obj_new_template(OBJ_TYP_NOTIF); if (!obj) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&obj->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); obj->parent = parent; obj->grp = grp; obj->nsid = mod->nsid; if (que == &mod->datadefQ) { obj->flags |= (OBJ_FL_TOP | OBJ_FL_CONFSET | OBJ_FL_CONFIG); } notif = obj->def.notif; /* Get the mandatory RPC method name */ res = yang_consume_id_string(tkc, mod, ¬if->name); CHK_OBJ_EXIT(obj, res, retres); res = consume_semi_lbrace(tkc, mod, obj, &done); CHK_OBJ_EXIT(obj, res, retres); /* get the container statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_template(obj); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &obj->appinfoQ); CHK_OBJ_EXIT(obj, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_TYPEDEF)) { res = yang_typ_consume_typedef(pcb, tkc, mod, ¬if->typedefQ); } else if (!xml_strcmp(val, YANG_K_GROUPING)) { res = yang_grp_consume_grouping(pcb, tkc, mod, ¬if->groupingQ, obj); } else if (!xml_strcmp(val, YANG_K_IF_FEATURE)) { res = yang_consume_iffeature(tkc, mod, &obj->iffeatureQ, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, ¬if->status, &stat, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, ¬if->descr, &desc, &obj->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, ¬if->ref, &ref, &obj->appinfoQ); } else { res = consume_datadef(pcb, tkc, mod, ¬if->datadefQ, obj, NULL); } CHK_OBJ_EXIT(obj, res, retres); } /* save or delete the obj_template_t struct */ if (notif->name && ncx_valid_name2(notif->name)) { res = add_object(tkc, mod, que, obj); CHK_EXIT(res, retres); } else { obj_free_template(obj); } return retres; } /* consume_notif */ /******************************************************************** * FUNCTION yang_obj_consume_augment * * Parse the next N tokens as a top-level augment-stmt * Create a obj_template_t struct and add it to the specified module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'augment' keyword * * INPUTS: * pcb == parser control block to use * tkc == token chain * mod == module in progress * * OUTPUTS: * new augment object added to module * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_consume_augment (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod) { status_t res; #ifdef DEBUG if (!pcb || !tkc || !mod) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = consume_augment(pcb, tkc, mod, &mod->datadefQ, NULL, NULL); return res; } /* yang_obj_consume_augment */ /******************************************************************** * FUNCTION yang_obj_consume_deviation * * Parse the next N tokens as a top-level deviation-stmt * Create a obj_deviation_t struct and add it to the * specified module * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'deviation' keyword * * INPUTS: * pcb == parser control block * tkc == token chain * mod == module in progress * * OUTPUTS: * new deviation struct added to module deviationQ * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_consume_deviation (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod) { obj_deviation_t *dev; const xmlChar *val; const char *expstr; yang_stmt_t *stmt; tk_type_t tktyp; boolean done, desc, ref; status_t res, retres; #ifdef DEBUG if (!pcb || !tkc || !mod) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* Get a new obj_deviation_t to fill in */ dev = obj_new_deviation(); if (!dev) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } val = NULL; expstr = "absolute schema node"; done = FALSE; desc = FALSE; ref = FALSE; retres = NO_ERR; ncx_set_error(&dev->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); /* Get the mandatory deviation target */ res = yang_consume_string(tkc, mod, &dev->target); CHK_DEV_EXIT(dev, res, retres); /* Get the starting left brace or semi-colon */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_deviation(dev); return res; } /* check for semi-colon or left brace */ switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: dev->empty = TRUE; done = TRUE; break; case TK_TT_LBRACE: done = FALSE; break; default: res = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, res, expstr); done = TRUE; } /* get the sub-section statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); obj_free_deviation(dev); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); obj_free_deviation(dev); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &dev->appinfoQ); CHK_DEV_EXIT(dev, res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a keyword token string so check the value */ if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &dev->descr, &desc, &dev->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &dev->ref, &ref, &dev->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DEVIATE)) { res = consume_deviate(pcb, tkc, mod, dev); } else { res = ERR_NCX_WRONG_TKVAL; expstr = "description, reference, or deviate"; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_DEV_EXIT(dev, res, retres); } /* save or delete the obj_deviation_t struct */ if (dev->target) { dev->res = retres; dlq_enque(dev, &mod->deviationQ); if (mod->stmtmode) { /* save top-level deviation order */ stmt = yang_new_deviation_stmt(dev); if (stmt) { dlq_enque(stmt, &mod->stmtQ); } else { log_error("\nError: malloc failure for obj_stmt"); res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); } } } else { obj_free_deviation(dev); } return retres; } /* yang_obj_consume_deviation */ /******************************************************************** * FUNCTION yang_obj_resolve_datadefs * * First pass object validation * * Analyze the entire datadefQ within the module struct * Finish all the clauses within this struct that * may have been defered because of possible forward references * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block to use * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_resolve_datadefs (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ) { status_t retres; #ifdef DEBUG if (!pcb || !tkc || !mod || !datadefQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif retres = resolve_datadefs(pcb, tkc, mod, datadefQ, FALSE); return retres; } /* yang_obj_resolve_datadefs */ /******************************************************************** * FUNCTION yang_obj_resolve_uses * * Second pass object validation * This calls expand_uses not resolve_uses! * * Refine-stmts have already been patched into objects in phase 1. * Expand and validate any uses clauses within any objects * within the datadefQ. * * Validate and expand any augments within a uses-stmt * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_resolve_uses (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ) { obj_template_t *testobj, *casobj; obj_case_t *cas; obj_augment_t *aug; status_t res, retres; assert( pcb && "pcb is NULL" ); assert( mod && "mod is NULL" ); assert( tkc && "tkc is NULL" ); assert( datadefQ && "datadefQ is NULL" ); res = NO_ERR; retres = NO_ERR; /* first resolve all the local type names */ for (testobj = (obj_template_t *)dlq_firstEntry(datadefQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { YANG_OBJ_DEBUG4( "\nresolve_uses: mod %s, object %s, on line %u", mod->name, obj_get_name(testobj), testobj->tkerr.linenum); /* all node types are just traversing the object tree * except OBJ_TYP_USES, which is expanded with expand_uses */ switch (testobj->objtype) { case OBJ_TYP_CONTAINER: res = yang_grp_resolve_complete(pcb, tkc, mod, testobj->def.container->groupingQ, testobj); CHK_EXIT(res, retres); res = yang_obj_resolve_uses(pcb, tkc, mod, testobj->def.container->datadefQ); CHK_EXIT(res, retres); break; case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: break; case OBJ_TYP_LIST: res = yang_grp_resolve_complete(pcb, tkc, mod, testobj->def.list->groupingQ, testobj); CHK_EXIT(res, retres); res = yang_obj_resolve_uses(pcb, tkc, mod, testobj->def.list->datadefQ); CHK_EXIT(res, retres); break; case OBJ_TYP_CHOICE: for (casobj = (obj_template_t *) dlq_firstEntry(testobj->def.choic->caseQ); casobj != NULL; casobj = (obj_template_t *)dlq_nextEntry(casobj)) { cas = casobj->def.cas; res = yang_obj_resolve_uses(pcb, tkc, mod, cas->datadefQ); CHK_EXIT(res, retres); } break; case OBJ_TYP_CASE: cas = testobj->def.cas; res = yang_obj_resolve_uses(pcb, tkc, mod, cas->datadefQ); CHK_EXIT(res, retres); break; case OBJ_TYP_USES: res = expand_uses(pcb, tkc, mod, testobj, datadefQ); CHK_EXIT(res, retres); break; case OBJ_TYP_AUGMENT: aug = testobj->def.augment; res = yang_obj_resolve_uses(pcb, tkc, mod, &aug->datadefQ); CHK_EXIT(res, retres); break; case OBJ_TYP_RPC: res = yang_grp_resolve_complete(pcb, tkc, mod, &testobj->def.rpc->groupingQ, testobj); CHK_EXIT(res, retres); res = yang_obj_resolve_uses(pcb, tkc, mod, &testobj->def.rpc->datadefQ); CHK_EXIT(res, retres); break; case OBJ_TYP_RPCIO: res = yang_grp_resolve_complete(pcb, tkc, mod, &testobj->def.rpcio->groupingQ, testobj); CHK_EXIT(res, retres); res = yang_obj_resolve_uses(pcb, tkc, mod, &testobj->def.rpcio->datadefQ); CHK_EXIT(res, retres); break; case OBJ_TYP_NOTIF: res = yang_grp_resolve_complete(pcb, tkc, mod, &testobj->def.notif->groupingQ, testobj); CHK_EXIT(res, retres); res = yang_obj_resolve_uses(pcb, tkc, mod, &testobj->def.notif->datadefQ); CHK_EXIT(res, retres); break; case OBJ_TYP_NONE: default: return SET_ERROR(ERR_INTERNAL_VAL); } CHK_EXIT(res, retres); } return retres; } /* yang_obj_resolve_uses */ /******************************************************************** * FUNCTION yang_obj_resolve_augments * * * Third pass object validation * * Expand and validate any augment clauses within any objects * within the datadefQ * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_resolve_augments (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ) { obj_template_t *testobj; status_t res, retres; assert( pcb && "pcb is NULL" ); assert( mod && "mod is NULL" ); assert( tkc && "tkc is NULL" ); assert( datadefQ && "datadefQ is NULL" ); res = NO_ERR; retres = NO_ERR; /* go through all the object trees and check for augments */ for (testobj = (obj_template_t *)dlq_firstEntry(datadefQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { if (testobj->objtype == OBJ_TYP_AUGMENT) { res = expand_augment(pcb, tkc, mod, testobj, datadefQ); CHK_EXIT(res, retres); } } return retres; } /* yang_obj_resolve_augments */ /******************************************************************** * FUNCTION yang_obj_resolve_augments_final * * Fourth pass object expand augments * * Clone any list keys missed in yang_obj_resolve_augments * This only occurs for augments of external objects, so * only top-level augment-stmts need to be checked. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_resolve_augments_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ) { obj_template_t *testobj; status_t res, retres; #ifdef DEBUG if (!pcb || !tkc || !mod || !datadefQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; retres = NO_ERR; /* only need to check the top-level objects because only * top-level augments can be for other modules */ for (testobj = (obj_template_t *)dlq_firstEntry(datadefQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { if (testobj->objtype == OBJ_TYP_AUGMENT) { res = resolve_augment_final(pcb, tkc, mod, testobj); CHK_EXIT(res, retres); } } return retres; } /* yang_obj_resolve_augments_final */ /******************************************************************** * FUNCTION yang_obj_resolve_deviations * * * Validate any local deviation statements within the * module deviationQ * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_resolve_deviations (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod) { obj_deviation_t *deviation; ncx_save_deviations_t *savedev; status_t res, retres; boolean anydevs; #ifdef DEBUG if (!tkc || !mod) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (pcb->deviationmode) { /* save any deviations until later * grab all the imports also to resolve * the deviation statements later */ if (!dlq_empty(&mod->deviationQ)) { savedev = ncx_new_save_deviations(mod->name, mod->version, mod->ns, mod->prefix); if (savedev == NULL) { return ERR_INTERNAL_MEM; } if (LOGDEBUG) { log_debug("\nSaving %u deviations from deviation module '%s'", dlq_count(&mod->deviationQ), mod->name); } dlq_block_enque(&mod->importQ, &savedev->importQ); dlq_block_enque(&mod->deviationQ, &savedev->deviationQ); dlq_enque(savedev, pcb->savedevQ); } else if (LOGDEBUG) { log_debug("\nNo deviations found in deviation module '%s'", mod->name); } return NO_ERR; } res = NO_ERR; retres = NO_ERR; anydevs = FALSE; /* first resolve all the local deviations */ for (deviation = (obj_deviation_t *) dlq_firstEntry(&mod->deviationQ); deviation != NULL; deviation = (obj_deviation_t *) dlq_nextEntry(deviation)) { if (deviation->res != NO_ERR) { continue; } anydevs = TRUE; res = resolve_deviation(pcb, tkc, mod, deviation); deviation->res = res; CHK_EXIT(res, retres); } if (retres == NO_ERR && anydevs) { retres = normalize_deviationQ(tkc, mod); } return retres; } /* yang_obj_resolve_deviations */ /******************************************************************** * FUNCTION yang_obj_resolve_ext_deviations * * * Validate any external deviation statements for the * module and then apply all deviations. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_resolve_ext_deviations (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod) { obj_deviation_t *deviation; ncx_save_deviations_t *savedev, *nextdev; status_t res, retres; uint32 extdevcount; res = NO_ERR; retres = NO_ERR; extdevcount = 0; /* next gather all the external deviations that apply * to this module; they will be moved from the global * pcb->savedevQ to the mod->deviationQ * Only do this for main modules, not submodules */ if (pcb->savedevQ) { for (savedev = (ncx_save_deviations_t *) dlq_firstEntry(pcb->savedevQ); savedev != NULL && mod->ismod; savedev = nextdev) { nextdev = (ncx_save_deviations_t *)dlq_nextEntry(savedev); if (dlq_empty(&savedev->deviationQ)) { continue; } /* check if the deviation module is this module; skip */ if (!xml_strcmp(savedev->devmodule, mod->name)) { continue; } /* check if there are any deviations for this module * in the savedev->deviationQ; if so, resolve them * and put them in the mod->deviationQ */ res = transfer_my_deviations(pcb, tkc, savedev, mod, &extdevcount); CHK_EXIT(res, retres); } } /* normalize the deviationQ so there are no duplicate * target objects; combine any deviations for the * same targobj; check errors deferred from resolve_module */ if (retres == NO_ERR && extdevcount) { retres = normalize_deviationQ(tkc, mod); } /* pick out any deviations for this module and * patch them into the object tree */ if (retres == NO_ERR) { retres = apply_all_object_deviations(pcb, tkc, mod); } return retres; } /* yang_obj_resolve_ext_deviations */ /******************************************************************** * FUNCTION yang_obj_resolve_final * * Fourth pass object validation * * Check various final stage errors and warnings * within a single file * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * ingrouping == TRUE if this object being resolved * from yang_grp_resolve_final * == FALSE otherwise * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_resolve_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ, boolean ingrouping) { obj_template_t *testobj; status_t res, retres; boolean notclone; #ifdef DEBUG if (!tkc || !mod || !datadefQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; retres = NO_ERR; /* first resolve all the local object names */ for (testobj = (obj_template_t *)dlq_firstEntry(datadefQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { /* go through all the cloned nodes to find any lists * so their keyQ and uniqueQ structures can be * set with pointers to the cloned objects * instead of pointers to the original objects */ notclone = !obj_is_cloned(testobj); YANG_OBJ_DEBUG4( "\nresolve_final: mod %s, object %s, on line %u", mod->name, obj_get_name(testobj), testobj->tkerr.linenum); switch (testobj->objtype) { case OBJ_TYP_CONTAINER: res = resolve_container_final(tkc, mod, testobj, ingrouping); CHK_EXIT(res, retres); if (notclone) { res = yang_grp_resolve_final(pcb, tkc, mod, testobj->def.container->groupingQ); CHK_EXIT(res, retres); } res = yang_obj_resolve_final(pcb, tkc, mod, testobj->def.container->datadefQ, ingrouping); CHK_EXIT(res, retres); if (notclone) { res = resolve_default_parm(tkc, mod, testobj); yang_check_obj_used(tkc, mod, testobj->def.container->typedefQ, testobj->def.container->groupingQ); } break; case OBJ_TYP_LEAF: case OBJ_TYP_ANYXML: case OBJ_TYP_ANYDATA: res = resolve_leaf_final(tkc, mod, testobj, ingrouping); break; case OBJ_TYP_LEAF_LIST: res = resolve_leaflist_final(tkc, mod, testobj); break; case OBJ_TYP_LIST: if (notclone) { res = yang_grp_resolve_final(pcb, tkc, mod, testobj->def.list->groupingQ); CHK_EXIT(res, retres); } res = yang_obj_resolve_final(pcb, tkc, mod, testobj->def.list->datadefQ, ingrouping); CHK_EXIT(res, retres); if (notclone) { yang_check_obj_used(tkc, mod, testobj->def.list->typedefQ, testobj->def.list->groupingQ); } res = resolve_list_final(pcb, tkc, mod, testobj->def.list, testobj); break; case OBJ_TYP_CHOICE: res = resolve_choice_final(tkc, mod, testobj, ingrouping); CHK_EXIT(res, retres); res = yang_obj_resolve_final(pcb, tkc, mod, testobj->def.choic->caseQ, ingrouping); break; case OBJ_TYP_CASE: res = resolve_case_final(tkc, mod, testobj); CHK_EXIT(res, retres); res = yang_obj_resolve_final(pcb, tkc, mod, testobj->def.cas->datadefQ, ingrouping); break; case OBJ_TYP_USES: if (notclone) { res = yang_obj_resolve_final(pcb, tkc, mod, testobj->def.uses->datadefQ, ingrouping); CHK_EXIT(res, retres); } res = resolve_uses_final(tkc, mod, testobj); break; case OBJ_TYP_AUGMENT: if (notclone) { res = yang_obj_resolve_final(pcb, tkc, mod, &testobj->def.augment->datadefQ, ingrouping); } break; case OBJ_TYP_RPC: if (notclone) { res = yang_grp_resolve_final(pcb, tkc, mod, &testobj->def.rpc->groupingQ); CHK_EXIT(res, retres); } res = yang_obj_resolve_final(pcb, tkc, mod, &testobj->def.rpc->datadefQ, ingrouping); if (notclone) { yang_check_obj_used(tkc, mod, &testobj->def.rpc->typedefQ, &testobj->def.rpc->groupingQ); } break; case OBJ_TYP_RPCIO: if (notclone) { res = yang_grp_resolve_final(pcb, tkc, mod, &testobj->def.rpcio->groupingQ); CHK_EXIT(res, retres); } res = yang_obj_resolve_final(pcb, tkc, mod, &testobj->def.rpcio->datadefQ, ingrouping); CHK_EXIT(res, retres); if (notclone) { res = resolve_default_parm(tkc, mod, testobj); yang_check_obj_used(tkc, mod, &testobj->def.rpcio->typedefQ, &testobj->def.rpcio->groupingQ); } break; case OBJ_TYP_NOTIF: if (notclone) { res = yang_grp_resolve_final(pcb, tkc, mod, &testobj->def.notif->groupingQ); CHK_EXIT(res, retres); } res = yang_obj_resolve_final(pcb, tkc, mod, &testobj->def.notif->datadefQ, ingrouping); if (notclone) { yang_check_obj_used(tkc, mod, &testobj->def.notif->typedefQ, &testobj->def.notif->groupingQ); } break; case OBJ_TYP_REFINE: break; case OBJ_TYP_NONE: default: res = SET_ERROR(ERR_INTERNAL_VAL); } CHK_EXIT(res, retres); } return retres; } /* yang_obj_resolve_final */ /******************************************************************** * FUNCTION yang_obj_top_resolve_final * * Fourth pass object validation; for top-level module with submodules * * Check various final stage errors and warnings * within a single file * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_top_resolve_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ) { obj_template_t *testobj; dlq_hdr_t *childdefQ; status_t res, retres; #ifdef DEBUG if (!tkc || !mod || !datadefQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; retres = NO_ERR; /* first resolve all the local object names */ for (testobj = (obj_template_t *)dlq_firstEntry(datadefQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { YANG_OBJ_DEBUG4( "\nresolve_top_final: mod %s, object %s, on line %u", mod->name, obj_get_name(testobj), testobj->tkerr.linenum); if (testobj->objtype == OBJ_TYP_LIST) { res = resolve_list_final(pcb, tkc, mod, testobj->def.list, testobj); CHK_EXIT(res, retres); } childdefQ = obj_get_datadefQ(testobj); if (childdefQ != NULL) { res = yang_obj_top_resolve_final(pcb, tkc, mod, childdefQ); CHK_EXIT(res, retres); } } return retres; } /* yang_obj_top_resolve_final */ /******************************************************************** * FUNCTION yang_obj_resolve_xpath * * Fifth (and final) pass object validation * * Check all leafref, must, and when XPath expressions * to make sure they are well-formed * * Checks the cooked objects, and skips all groupings * uses, and augment nodes * * MUST BE CALLED AFTER yang_obj_resolve_final * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_resolve_xpath (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ) { //ncx_include_t *inc; yang_node_t *node, *node2; status_t res, retres; #ifdef DEBUG if (!tkc || !mod || !datadefQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; retres = NO_ERR; if (LOGDEBUG3) { log_debug3("\nyang_obj_resolve_xpath for %smodule '%s'", (mod->ismod) ? EMPTY_STRING : (const xmlChar *)"sub", mod->name); } /* first resolve the main module or top submodule */ res = resolve_xpath(tkc, mod, datadefQ); CHK_EXIT(res, retres); for (node = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node != NULL; node = (yang_node_t *)dlq_nextEntry(node)) { if (node->submod == NULL) { /* error in processing this submod */ continue; } if (LOGDEBUG3) { log_debug3("\nyang_obj_resolve_xpath " "for submodule '%s' against main mod '%s'", node->submod->name, mod->name); } /* check node submod against main module */ res = resolve_xpath(tkc, node->submod, datadefQ); CHK_EXIT(res, retres); for (node2 = (yang_node_t *)dlq_firstEntry(&mod->allincQ); node2 != NULL; node2 = (yang_node_t *)dlq_nextEntry(node2)) { if (node2->submod == NULL) { continue; } if (LOGDEBUG3) { log_debug3("\nyang_obj_resolve_xpath " "for submodule '%s' against sub mod '%s'", node->submod->name, node2->submod->name); } res = resolve_xpath(tkc, node->submod, &node2->submod->datadefQ); CHK_EXIT(res, retres); } } return retres; } /* yang_obj_resolve_xpath */ /******************************************************************** * FUNCTION yang_obj_resolve_xpath_final * * Fifth pass validate defvals for XPath leafs * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_resolve_xpath_final (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ) { obj_template_t *testobj; status_t res, retres; #ifdef DEBUG if (!pcb || !tkc || !mod || !datadefQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; retres = NO_ERR; /* only need to check the top-level obkects because only * top-level augments can be for other modules */ for (testobj = (obj_template_t *)dlq_firstEntry(datadefQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { if (obj_has_name(testobj) /*&& obj_get_status(testobj) != NCX_STATUS_OBSOLETE*/) { if (obj_is_leafy(testobj) && obj_get_default(testobj) != NULL) { res = yang_typ_resolve_type_final(tkc, mod, obj_get_typdef(testobj), obj_get_default(testobj), testobj); CHK_EXIT(res, retres); } else { /* get the complex type datadefQ */ dlq_hdr_t *childQ = obj_get_datadefQ(testobj); if (childQ) { res = yang_obj_resolve_xpath_final(pcb, tkc, mod, childQ); CHK_EXIT(res, retres); } } } } return retres; } /* yang_obj_resolve_xpath_final */ /******************************************************************** * FUNCTION yang_obj_check_leafref_loops * * Check all leafref objects for hard-wired object loops * Must be done after yang_obj_resolve_xpath * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * tkc == token chain from parsing (needed for error msgs) * mod == module in progress * datadefQ == Q of obj_template_t structs to check * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_check_leafref_loops (tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ) { obj_template_t *testobj, *nextobj, *lastobj; dlq_hdr_t *childdatadefQ; status_t res, retres; #ifdef DEBUG if (!tkc || !mod || !datadefQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; retres = NO_ERR; /* first resolve all the local object names */ for (testobj = (obj_template_t *)dlq_firstEntry(datadefQ); testobj != NULL; testobj = (obj_template_t *)dlq_nextEntry(testobj)) { switch (testobj->objtype) { case OBJ_TYP_LEAF: case OBJ_TYP_LEAF_LIST: if (obj_get_basetype(testobj) == NCX_BT_LEAFREF) { YANG_OBJ_DEBUG4( "\n%s: mod %s, object %s, on line %u", __FUNCTION__, mod->name, obj_get_name(testobj), testobj->tkerr.linenum); if (testobj->objtype == OBJ_TYP_LEAF) { nextobj = testobj->def.leaf->leafrefobj; } else { nextobj = testobj->def.leaflist->leafrefobj; } if (nextobj == testobj) { res = ERR_NCX_LEAFREF_LOOP; log_error("\nError: leafref path in " "%s %s loops with self", obj_get_typestr(testobj), obj_get_name(testobj)); res = set_tkc_error( tkc, mod, &testobj->tkerr, ERR_NCX_LEAFREF_LOOP ); } else { while (nextobj) { if (obj_is_leafy(nextobj) && obj_get_basetype(nextobj) == NCX_BT_LEAFREF) { lastobj = nextobj; if (nextobj->objtype == OBJ_TYP_LEAF) { nextobj = nextobj->def.leaf->leafrefobj; } else { nextobj = nextobj->def.leaflist->leafrefobj; } if (nextobj == testobj) { log_error("\nError: leafref path in " "%s %s loops with %s %s", obj_get_typestr(testobj), obj_get_name(testobj), obj_get_typestr(lastobj), obj_get_name(lastobj)); res = set_tkc_error( tkc, mod, &testobj->tkerr, ERR_NCX_LEAFREF_LOOP ); nextobj = NULL; } } else { nextobj = NULL; } } } CHK_EXIT(res, retres); } break; default: childdatadefQ = obj_get_datadefQ(testobj); if (childdatadefQ) { res = yang_obj_check_leafref_loops(tkc, mod, childdatadefQ); CHK_EXIT(res, retres); } } } return retres; } /* yang_obj_check_leafref_loops */ /******************************************************************** * FUNCTION yang_obj_remove_deleted_nodes * * Find any nodes marked for deletion and remove them * * INPUTS: * pcb == parser control block to use * tkc == token parse chain to use for errors * mod == module in progress; transfer to this deviationQ * datadefQ == current object datadef Q to check * * RETURNS: * status of the operation *********************************************************************/ status_t yang_obj_remove_deleted_nodes (yang_pcb_t *pcb, tk_chain_t *tkc, ncx_module_t *mod, dlq_hdr_t *datadefQ) { obj_template_t *testobj, *nextobj, *parentobj; dlq_hdr_t *child_datadefQ; status_t res, retres; #ifdef DEBUG if (!pcb || !tkc || !mod || !datadefQ) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif retres = NO_ERR; for (testobj = (obj_template_t *) dlq_firstEntry(datadefQ); testobj != NULL; testobj = nextobj) { nextobj = (obj_template_t *)dlq_nextEntry(testobj); parentobj = NULL; if (testobj->flags & OBJ_FL_DELETED) { dlq_remove(testobj); YANG_OBJ_DEBUG2( "\nDeviation caused deletion of object %s:%s", obj_get_mod_name(testobj), obj_get_name(testobj)); parentobj = testobj->parent; obj_free_template(testobj); if (parentobj) { /* need to retest the parent to see if it * is still OK; there should not be any other * deviations with the same target object */ YANG_OBJ_DEBUG2( "\nRechecking %s:%s after applying deviation(s) to child", obj_get_mod_name(parentobj), obj_get_name(parentobj)); res = resolve_datadef(pcb, tkc, mod, parentobj, TRUE); CHK_EXIT(res, retres); } } else { /* this object was not deleted */ child_datadefQ = obj_get_datadefQ(testobj); if (child_datadefQ != NULL) { res = yang_obj_remove_deleted_nodes(pcb, tkc, mod, child_datadefQ); if (res != NO_ERR) { retres = res; } } } } return retres; } /* yang_obj_remove_deleted_nodes */ /* END file yang_obj.c */ yuma123_2.14/netconf/src/ncx/val.c0000664000175000017500000107713314770023131017100 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2013 - 2016, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: val.c support for the val_value_t data structure Note: NCX_BT_LEAFREF code marked with a **** comment is not completed yet. The string value version of the leafref is always returned, instead of the canonical form of the data type of the leafref path target ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 19dec05 abb begun 21jul08 abb start obj-based rewrite 28dec11 abb add editvars only if XML attrs present ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "procdefs.h" #include "b64.h" #include "cfg.h" #include "dlq.h" #include "getcb.h" #include "json_wr.h" #include "log.h" #include "ncx.h" #include "ncx_list.h" #include "ncx_num.h" #include "ncx_str.h" #include "ncxconst.h" #include "obj.h" #include "ses.h" #include "tk.h" #include "typ.h" #include "val.h" #include "val123.h" #include "val_util.h" #include "xml_util.h" #include "xml_wr.h" #include "xpath.h" #include "xpath1.h" #include "xpath_yang.h" #include "yangconst.h" #include "uptime.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* #define VAL_DEBUG 1 */ /* #define VAL_EDITVARS_DEBUG */ /* #define VAL_FREE_DEBUG 1 */ /******************************************************************** * * * T Y P E S * * * *********************************************************************/ typedef struct finderparms_t_ { val_value_t *findval; val_value_t *foundval; int64 findpos; int64 foundpos; } finderparms_t; /* pick a log output function for dump_value */ typedef void (*dumpfn_t) (const char *fstr, ...); /* pick a log indent function for dump_value */ typedef void (*indentfn_t) (int32 indentcnt); #ifdef VAL_EDITVARS_DEBUG static uint32 editvars_malloc = 0; static uint32 editvars_free = 0; #endif /******************************************************************** * FUNCTION stdout_num * * Printf the specified ncx_num_t to stdout * * INPUTS: * btyp == base type of the number * num == number to printf * *********************************************************************/ static void stdout_num (ncx_btype_t btyp, const ncx_num_t *num) { xmlChar numbuff[VAL_MAX_NUMLEN]; uint32 len; status_t res; res = ncx_sprintf_num(numbuff, num, btyp, &len); if (res != NO_ERR) { log_stdout("invalid num '%s'", get_error_string(res)); } else { log_stdout("%s", numbuff); } } /* stdout_num */ /******************************************************************** * FUNCTION dump_extern * * Printf the specified external file * * INPUTS: * fspec == filespec to printf * *********************************************************************/ static void dump_extern (const xmlChar *fname) { FILE *fil; boolean done; int ch; if (!fname) { log_error("\nval: No extern fname"); return; } fil = fopen((const char *)fname, "r"); if (!fil) { log_error("\nError: Open extern failed (%s)", fname); return; } done = FALSE; while (!done) { ch = fgetc(fil); if (ch == EOF) { fclose(fil); done = TRUE; } else { log_write("%c", ch); } } } /* dump_extern */ /******************************************************************** * FUNCTION dump_alt_extern * * Printf the specified external file to the alternate logfile * * INPUTS: * fspec == filespec to printf * *********************************************************************/ static void dump_alt_extern (const xmlChar *fname) { FILE *fil; boolean done; int ch; if (!fname) { log_error("\nval: No extern fname"); return; } fil = fopen((const char *)fname, "r"); if (!fil) { log_error("\nval: Open extern failed (%s)", fname); return; } done = FALSE; while (!done) { ch = fgetc(fil); if (ch == EOF) { fclose(fil); done = TRUE; } else { log_alt_write("%c", ch); } } } /* dump_alt_extern */ /******************************************************************** * FUNCTION stdout_extern * * Printf the specified external file to stdout * * INPUTS: * fspec == filespec to printf * *********************************************************************/ static void stdout_extern (const xmlChar *fname) { FILE *fil; boolean done; int ch; if (!fname) { log_stdout("\nval: No extern fname"); return; } fil = fopen((const char *)fname, "r"); if (!fil) { log_stdout("\nval: Open extern failed (%s)", fname); return; } done = FALSE; while (!done) { ch = fgetc(fil); if (ch == EOF) { fclose(fil); done = TRUE; } else { log_stdout("%c", ch); } } } /* stdout_extern */ /******************************************************************** * FUNCTION dump_intern * * Printf the specified internal XML buffer * * INPUTS: * intbuff == internal buffer to printf * *********************************************************************/ static void dump_intern (const xmlChar *intbuff) { const xmlChar *ch; if (!intbuff) { log_error("\nval: No internal buffer"); return; } ch = intbuff; while (*ch) { log_write("%c", *ch++); } } /* dump_intern */ /******************************************************************** * FUNCTION dump_alt_intern * * Printf the specified internal XML buffer to the alternate logfile * * INPUTS: * intbuff == internal buffer to printf * *********************************************************************/ static void dump_alt_intern (const xmlChar *intbuff) { const xmlChar *ch; if (!intbuff) { log_error("\nval: No internal buffer"); return; } ch = intbuff; while (*ch) { log_alt_write("%c", *ch++); } } /* dump_alt_intern */ /******************************************************************** * FUNCTION stdout_intern * * Printf the specified internal XML buffer to stdout * * INPUTS: * intbuff == internal buffer to printf * *********************************************************************/ static void stdout_intern (const xmlChar *intbuff) { const xmlChar *ch; if (!intbuff) { log_stdout("\nval: No internal buffer"); return; } ch = intbuff; while (*ch) { log_stdout("%c", *ch++); } } /* stdout_intern */ /******************************************************************** * FUNCTION pattern_match * * Check the specified string against the specified pattern * * INPUTS: * pattern == compiled pattern to use * strval == string to check * * RETURNS: * TRUE is string matches pattern; FALSE otherwise *********************************************************************/ static boolean pattern_match (const xmlRegexpPtr pattern, const xmlChar *strval) { int ret; ret = xmlRegexpExec(pattern, strval); if (ret==1) { return TRUE; } else if (ret==0) { return FALSE; } else if (ret < 0) { /* pattern match execution error, but compiled ok */ SET_ERROR(ERR_NCX_INVALID_PATTERN); return FALSE; } else { SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /*NOTREACHED*/ } /* pattern_match */ /******************************************************************** * FUNCTION check_svalQ_enum * * Check all the ncx_enum_t structs in a queue of typ_sval_t * * INPUTS: * name == enum string * checkQ == pointer to Q of typ_sval_t to check * reten == address of return typ_enum_t * * OUTPUTS: * *reten == typ_enum_t found if matched (res == NO_ERR) * RETURNS: * status, NO_ERR if string value is in the set *********************************************************************/ static status_t check_svalQ_enum (const xmlChar *name, const dlq_hdr_t *checkQ, typ_enum_t **reten) { typ_enum_t *en; /* check this typdef for restrictions */ for (en = (typ_enum_t *)dlq_firstEntry(checkQ); en != NULL; en = (typ_enum_t *)dlq_nextEntry(en)) { if (!xml_strcmp(name, en->name)) { *reten = en; return NO_ERR; } } return ERR_NCX_NOT_FOUND; } /* check_svalQ_enum */ /******************************************************************** * FUNCTION free_editvars * * Clean and free the val->editvars field * * INPUTS: * val == val_value_t data structure to use * * OUTPUTS: * val->editvars is cleaned, freed, and set to NULL *********************************************************************/ static void free_editvars (val_value_t *val) { if (val->editvars) { #ifdef VAL_EDITVARS_DEBUG log_debug3("\n\nfree_editvars: %s: %d = %p\n", (val->name) ? val->name : NCX_EL_NONE, ++editvars_free, val->editvars); #endif if (val->editvars->insertstr) { m__free(val->editvars->insertstr); } if (val->editvars->insertxpcb) { xpath_free_pcb(val->editvars->insertxpcb); } m__free(val->editvars); val->editvars = NULL; } else { #ifdef VAL_EDITVARS_DEBUG log_debug3("\nval_free_editvars skipped (%u, %s)", editvars_free, val->name); #endif } } /* free_editvars */ /******************************************************************** * FUNCTION clean_value * * Scrub the memory in a ncx_value_t by freeing all * the sub-fields. DOES NOT free the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * val == val_value_t data structure to clean * full == TRUE if full clean * FALSE if value only *********************************************************************/ static void clean_value (val_value_t *val, boolean full) { val_value_t *cur; val_index_t *in; ncx_btype_t btyp; if (full && val->virtualval) { /* check if any cached entry of self needs to be cleared */ val_free_value(val->virtualval); val->virtualval = NULL; } btyp = val->btyp; if (full && val->editvars) { free_editvars(val); } /* clean the val->v union, depending on base type */ switch (btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: ncx_clean_num(btyp, &val->v.num); break; case NCX_BT_ENUM: ncx_clean_enum(&val->v.enu); break; case NCX_BT_BINARY: ncx_clean_binary(&val->v.binary); break; case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: ncx_clean_str(&val->v.str); break; case NCX_BT_IDREF: if (val->v.idref.name) { m__free(val->v.idref.name); val->v.idref.name = NULL; } break; case NCX_BT_SLIST: case NCX_BT_BITS: ncx_clean_list(&val->v.list); break; case NCX_BT_LIST: case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_CASE: while (!dlq_empty(&val->v.childQ)) { cur = (val_value_t *)dlq_deque(&val->v.childQ); val_free_value(cur); } break; case NCX_BT_EXTERN: if (val->v.fname) { m__free(val->v.fname); val->v.fname = NULL; } break; case NCX_BT_INTERN: if (val->v.intbuff) { m__free(val->v.intbuff); val->v.intbuff = NULL; } break; case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: case NCX_BT_NONE: case NCX_BT_UNION: break; default: SET_ERROR(ERR_INTERNAL_VAL); } if (full) { if (val->dname) { m__free(val->dname); val->dname = NULL; } val->nsid = 0; while (!dlq_empty(&val->metaQ)) { cur = (val_value_t *)dlq_deque(&val->metaQ); val_free_value(cur); } } while (!dlq_empty(&val->indexQ)) { in = (val_index_t *)dlq_deque(&val->indexQ); m__free(in); } if (val->xpathpcb) { xpath_free_pcb(val->xpathpcb); val->xpathpcb = NULL; } } /* clean_value */ /******************************************************************** * FUNCTION merge_simple * * Merge simple src val into dest val (! MUST be same type !) * Instance qualifiers have already been checked, * and only zero or 1 instance is allowed, so replace * the current dest value * * INPUTS: * src == val to merge from * dest == val to merge into * * RETURNS: * status *********************************************************************/ static status_t merge_simple (ncx_btype_t btyp, const val_value_t *src, val_value_t *dest) { status_t res = NO_ERR; xmlChar *val_copy = NULL; /* need to replace the current value or merge a list, etc. */ switch (btyp) { case NCX_BT_ENUM: dest->v.enu.name = src->v.enu.name; dest->v.enu.val = src->v.enu.val; break; case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: dest->v.boo = src->v.boo; break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: dest->v.num.i = src->v.num.i; break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: dest->v.num.u = src->v.num.u; break; case NCX_BT_INT64: dest->v.num.l = src->v.num.l; break; case NCX_BT_UINT64: dest->v.num.ul = src->v.num.ul; break; case NCX_BT_DECIMAL64: ncx_clean_num(btyp, &dest->v.num); dest->v.num.dec.val = src->v.num.dec.val; dest->v.num.dec.digits = src->v.num.dec.digits; dest->v.num.dec.zeroes = src->v.num.dec.zeroes; break; case NCX_BT_FLOAT64: ncx_clean_num(btyp, &dest->v.num); dest->v.num.d = src->v.num.d; break; case NCX_BT_BINARY: val_copy = m__getMem( src->v.binary.ustrlen ); if (val_copy) { ncx_clean_binary(&dest->v.binary); dest->v.binary.ubufflen = src->v.binary.ubufflen; dest->v.binary.ustrlen = src->v.binary.ustrlen; dest->v.binary.ustr = val_copy; memcpy( dest->v.binary.ustr, src->v.binary.ustr, src->v.binary.ustrlen ); } else { res = ERR_INTERNAL_MEM; } break; case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: /*** !!! not sure LEAFREF will ever get here */ val_copy = xml_strdup( src->v.str ); if (val_copy) { ncx_clean_str(&dest->v.str); dest->v.str = val_copy; } else { res = ERR_INTERNAL_MEM; } break; case NCX_BT_IDREF: val_copy = xml_strdup( src->v.idref.name ); if (val_copy) { dest->v.idref.nsid = src->v.idref.nsid; dest->v.idref.identity = src->v.idref.identity; if (dest->v.idref.name) { m__free(dest->v.idref.name); } dest->v.idref.name = val_copy; } else { res = ERR_INTERNAL_MEM; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* merge_simple */ /******************************************************************** * FUNCTION index_match * * Check 2 val_value structs for the same instance ID * * The node data types must match, and must be * NCX_BT_LIST * * All index components must exactly match. * * INPUTS: * val1 == first value to index match * val2 == second value to index match * * RETURNS: * -2 if some error * -1 if val1 < val2 index * 0 if val1 == val2 index * 1 if val1 > val2 index *********************************************************************/ static int32 index_match (const val_value_t *val1, const val_value_t *val2) { const val_index_t *c1, *c2; int32 cmp; status_t res; /* only lists have index chains */ if (val1->obj->objtype != OBJ_TYP_LIST) { SET_ERROR(ERR_INTERNAL_VAL); return -2; } /* object templates must exactly match */ if (val1->obj != val2->obj) { SET_ERROR(ERR_INTERNAL_VAL); return -2; } /* get the first pair of index nodes to check */ c1 = (val_index_t *)dlq_firstEntry(&val1->indexQ); c2 = (val_index_t *)dlq_firstEntry(&val2->indexQ); /* match index values, 1 for 1, left to right */ for (;;) { /* check if 1 table has index values but not the other */ if (!c1 && !c2) { return 0; } else if (!c1) { return -1; } else if (!c2) { return 1; } if (xml_strcmp(c1->val->name, c2->val->name)) { /* could be an invalid key not checked yet * not calling SET_ERROR() */ return -2; } res = NO_ERR; /* same name in the same namespace */ if (c1->val->btyp == c2->val->btyp) { cmp = val_compare(c1->val, c2->val); } else if (typ_is_string(c1->val->btyp)) { /* this might be anyxml parse */ cmp = val_compare_to_string(c2->val, VAL_STR(c1->val), &res); cmp *= -1; } else if (typ_is_string(c2->val->btyp)) { /* this might be anyxml parse */ cmp = val_compare_to_string(c1->val, VAL_STR(c2->val), &res); } else { SET_ERROR(ERR_INTERNAL_VAL); return -2; } if (res != NO_ERR) { SET_ERROR(res); return -2; } if (cmp) { return cmp; } /* node matched, get next node */ c1 = (val_index_t *)dlq_nextEntry(c1); c2 = (val_index_t *)dlq_nextEntry(c2); } /*NOTREACHED*/ } /* index_match */ /******************************************************************** * FUNCTION init_from_template * * Initialize a value node from its object template * * MUST CALL val_new_value FIRST * * INPUTS: * val == pointer to the malloced struct to initialize * obj == object template to use * btyp == builtin type to use *********************************************************************/ static void init_from_template (val_value_t *val, obj_template_t *obj, ncx_btype_t btyp) { typ_template_t *listtyp; ncx_btype_t listbtyp; val->obj = obj; val->typdef = obj_get_typdef(obj); val->btyp = btyp; val->nsid = obj_get_nsid(obj); /* the agent new_child_val function may have already * set the name field in agt_val_parse.c */ if (!val->name) { val->name = obj_get_name(obj); } /* set the case object field if this is a node from a * choice. This will be used to validate and manipulate * the choice that is not actually taking up a node * in the value tree */ val->dataclass = obj_get_config_flag(obj) ? NCX_DC_CONFIG : NCX_DC_STATE; if (obj->parent && obj->parent->objtype==OBJ_TYP_CASE) { val->casobj = obj->parent; } if (!typ_is_simple(val->btyp)) { val_init_complex(val, btyp); } else if (val->btyp == NCX_BT_SLIST) { listtyp = typ_get_listtyp(val->typdef); if (!listtyp) { SET_ERROR(ERR_INTERNAL_VAL); listbtyp = NCX_BT_STRING; } else { listbtyp = typ_get_basetype(&listtyp->typdef); } ncx_init_list(&val->v.list, listbtyp); } else if (val->btyp == NCX_BT_BITS) { ncx_init_list(&val->v.list, NCX_BT_BITS); } else if (val->btyp == NCX_BT_EMPTY) { val->v.boo = TRUE; } } /* init_from_template */ /******************************************************************** * FUNCTION check_rangeQ * * Check all the typ_rangedef_t structs in the queue * For search qualifier NCX_SQUAL_RANGE * * INPUTS: * btyp == number base type * num == number to check * checkQ == pointer to Q of typ_rangedef_t to check * * RETURNS: * status, NO_ERR if string value is in the set *********************************************************************/ static status_t check_rangeQ (ncx_btype_t btyp, const ncx_num_t *num, dlq_hdr_t *checkQ) { typ_rangedef_t *rv; int32 cmp; boolean lbok, ubok; for (rv = (typ_rangedef_t *)dlq_firstEntry(checkQ); rv != NULL; rv = (typ_rangedef_t *)dlq_nextEntry(rv)) { lbok = FALSE; ubok = FALSE; /* make sure the range numbers are really this type */ if (rv->btyp != btyp) { return SET_ERROR(ERR_NCX_WRONG_NUMTYP); } /* check lower bound first, only if there is one */ if (!(rv->flags & TYP_FL_LBINF)) { cmp = ncx_compare_nums(num, &rv->lb, btyp); if (cmp >= 0) { lbok = TRUE; } } else { /* LB == -INF, always passes the test */ lbok = TRUE; } /* check upper bound last, only if there is one */ if (!(rv->flags & TYP_FL_UBINF)) { cmp = ncx_compare_nums(num, &rv->ub, btyp); if (cmp <= 0) { ubok = TRUE; } } else { /* UB == INF, always passes the test */ ubok = TRUE; } if (lbok && ubok) { /* num is >= LB and <= UB */ return NO_ERR; } } /* ran out of rangedef segments to check */ return ERR_NCX_NOT_IN_RANGE; } /* check_rangeQ */ /******************************************************************** * FUNCTION position_walker * * Position finder val_walker_fn for XPath support * Follows val_walker_fn_t template * * INPUTS: * val == value node being processed in the tree walk * cookie1 == cookie1 value passed to start fn * cookie2 == cookie2 value passed to start fn * * RETURNS: * TRUE if walk should continue, FALSE if not *********************************************************************/ static boolean position_walker (val_value_t *val, void *cookie1, void *cookie2) { finderparms_t *finderparms; finderparms = (finderparms_t *)cookie1; (void)cookie2; finderparms->foundval = val; finderparms->foundpos++; if (finderparms->findval && (finderparms->findval == val)) { return FALSE; } if (finderparms->findpos && (finderparms->findpos == finderparms->foundpos)) { return FALSE; } return TRUE; } /* position_walker */ /******************************************************************** * FUNCTION process_one_valwalker * * Process one child object node for * the obj_find_all_* functions * * INPUTS: * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * obj == object to process * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of node to filter * == NULL to match any node name * configonly = TRUE for config=true only * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * fncalled == address of return function called flag * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ static boolean process_one_valwalker (val_walker_fn_t walkerfn, void *cookie1, void *cookie2, val_value_t *val, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode, boolean *fncalled) { boolean fnresult; *fncalled = FALSE; if (configonly && !obj_is_config(val->obj)) { return TRUE; } fnresult = TRUE; if (textmode) { if (obj_is_leafy(val->obj)) { if (walkerfn) { fnresult = (*walkerfn)(val, cookie1, cookie2); } *fncalled = TRUE; } } else if (modname && name) { if (!xml_strcmp(modname, val_get_mod_name(val)) && !xml_strcmp(name, val->name)) { if (walkerfn) { fnresult = (*walkerfn)(val, cookie1, cookie2); } *fncalled = TRUE; } } else if (modname) { if (!xml_strcmp(modname, val_get_mod_name(val))) { if (walkerfn) { fnresult = (*walkerfn)(val, cookie1, cookie2); } *fncalled = TRUE; } } else if (name) { if (!xml_strcmp(name, val->name)) { if (walkerfn) { fnresult = (*walkerfn)(val, cookie1, cookie2); } *fncalled = TRUE; } } else { if (walkerfn) { fnresult = (*walkerfn)(val, cookie1, cookie2); } *fncalled = TRUE; } return fnresult; } /* process_one_valwalker */ /******************************************************************** * FUNCTION setup_virtual_retval * * Set an initialized val_value_t as a virtual return val * * INPUTS: * virval == virtual value to set from * realval == value to set to * * OUTPUTS: * *realval is setup for return if NO_ERR *********************************************************************/ static void setup_virtual_retval (const val_value_t *virval, val_value_t *realval) { typ_template_t *listtyp; ncx_btype_t btyp; realval->name = virval->name; realval->nsid = virval->nsid; realval->obj = virval->obj; realval->typdef = virval->typdef; realval->flags = virval->flags; realval->btyp = virval->btyp; realval->dataclass = virval->dataclass; realval->parent = virval->parent; if (!typ_is_simple(virval->btyp)) { val_init_complex(realval, virval->btyp); } else if (virval->btyp == NCX_BT_SLIST || virval->btyp == NCX_BT_BITS) { listtyp = typ_get_listtyp(realval->typdef); btyp = typ_get_basetype(&listtyp->typdef); ncx_init_list(&realval->v.list, btyp); } } /* setup_virtual_retval */ /******************************************************************** * FUNCTION copy_editvars * * Copy the editvars struct contents * * INPUTS: * val == value to copy from * copy == value to copy to * * RETURNS: * status *********************************************************************/ static status_t copy_editvars (const val_value_t *val, val_value_t *copy) { status_t res; res = NO_ERR; /* set the copy->editvars */ if (val->editvars) { if (!copy->editvars) { res = val_new_editvars(copy); if (res != NO_ERR || !copy->editvars ) { if ( NO_ERR == res ) { res = ERR_INTERNAL_PTR; } if ( copy->editvars ) { free_editvars( copy ); } return res; } } copy->editvars->curparent = val->editvars->curparent; copy->editop = val->editop; copy->editvars->insertop = val->editvars->insertop; copy->editvars->iskey = val->editvars->iskey; copy->editvars->operset = val->editvars->operset; if (val->editvars->insertstr) { copy->editvars->insertstr = xml_strdup(val->editvars->insertstr); if (!copy->editvars->insertstr) { res = ERR_INTERNAL_MEM; } } if (val->editvars->insertxpcb) { copy->editvars->insertxpcb = xpath_clone_pcb(val->editvars->insertxpcb); if (!copy->editvars->insertxpcb) { res = ERR_INTERNAL_MEM; } } copy->editvars->insertval = val->editvars->insertval; } return res; } /* copy_editvars */ /******************************************************************** * FUNCTION cache_virtual_value * * get + cache as val->virtualval; DO NOT FREE the return val * Get the value of a value node and store the malloced * pointer in the virtualval cache * * If the val->getcb is NULL, then an error will be returned * * Caller should check for *res == ERR_NCX_SKIPPED * This will be returned if virtual value has no * instance at this time. * * INPUTS: * scb == session control block getting the virtual value * the scb->cache_timeout value will be used * id scb is not NULL * val == virtual value to get value for * res == pointer to output function return status value * * OUTPUTS: * val->virtualval set to the malloced val; will be cleared * if already set * val->cachetime set to the current time if the getcb is used * *res == the function return status * * RETURNS: * A pointer to the malloced val; * This pointer can not be stored; it is free as part of virtualval * *********************************************************************/ static val_value_t * cache_virtual_value (ses_cb_t *scb, val_value_t *val, status_t *res) { val_value_t *retval; getcb_fn_t getcb; time_t timenow; double timediff, timerval; uint32 deftimeout; boolean disable_cache; val_virt_getcb_node_t * getcb_node; *res = NO_ERR; if (!val->getcb) { *res = ERR_NCX_OPERATION_FAILED; return NULL; } getcb = (getcb_fn_t)val->getcb; if (val->virtualval != NULL) { /* already have a value; check if it is fresh enough */ (void)uptime(&timenow); timediff = difftime(timenow, val->cachetime); disable_cache = FALSE; if (scb != NULL) { timerval = (double)scb->cache_timeout; if (scb->cache_timeout == 0) { disable_cache = TRUE; } } else { deftimeout = ncx_get_vtimeout_value(); timerval = (double)deftimeout; } if (LOGDEBUG4) { log_debug4("\nval: virtual val timer %e", timediff); } if (disable_cache || (timediff >= timerval)) { if (LOGDEBUG4) { log_debug4("\nval: refresh virtual val %s", val->name); } val_free_value(val->virtualval); val->virtualval = NULL; } else { return val->virtualval; } } /* first get or stale and need a refresh */ retval = val_new_value(); if (!retval) { *res = ERR_INTERNAL_MEM; return NULL; } setup_virtual_retval(val, retval); (void)uptime(&val->cachetime); *res = (*getcb)(NULL, GETCB_GET_VALUE, val, retval); if (*res != NO_ERR && ((*res != ERR_NCX_SKIPPED) || dlq_empty(&val->getcbQ))) { val_free_value(retval); retval = NULL; return retval; } for ( getcb_node = (val_virt_getcb_node_t *)dlq_firstEntry(&val->getcbQ); getcb_node != NULL; getcb_node = (val_virt_getcb_node_t *)dlq_nextEntry(getcb_node)) { status_t prev_res = *res; getcb = (getcb_fn_t)getcb_node->getcb; *res = (*getcb)(NULL, GETCB_GET_VALUE, val, retval); if(*res==ERR_NCX_SKIPPED && prev_res==NO_ERR) { *res=prev_res; continue; } } if (*res != NO_ERR) { val_free_value(retval); retval = NULL; } else { val->virtualval = retval; val->virtualval->parent = val->parent; } return retval; } /* cache_virtual_value */ /******************************************************************** * FUNCTION clone_test * * Clone a specified val_value_t struct and sub-trees * Only clone the nodes that pass the test function callback * * INPUTS: * val == value to clone * testcb == filter test callback function to use * NULL means no filter * with_editvars == TRUE if the new editvars should be cloned; * FALSE if the new editvars should be NULL * res == address of return status * * OUTPUTS: * *res == resturn status * * RETURNS: * clone of val, or NULL if a malloc failure or entire node filtered *********************************************************************/ static val_value_t * clone_test (const val_value_t *val, val_test_fn_t testfn, boolean with_editvars, status_t *res) { const val_value_t *ch; val_value_t *copy, *copych; boolean testres; uint32 i; #ifdef DEBUG if (!val || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (testfn) { testres = (*testfn)(val); if (!testres) { *res = ERR_NCX_SKIPPED; return NULL; } } if (val->res != NO_ERR) { *res = val->res; return NULL; } copy = val_new_value(); if (!copy) { *res = ERR_INTERNAL_MEM; return NULL; } /* copy all the fields */ copy->obj = val->obj; copy->typdef = val->typdef; if (val->dname) { copy->dname = xml_strdup(val->dname); if (!copy->dname) { *res = ERR_INTERNAL_MEM; val_free_value(copy); return NULL; } copy->name = copy->dname; } else { copy->dname = NULL; copy->name = val->name; } copy->parent = val->parent; copy->nsid = val->nsid; copy->btyp = val->btyp; copy->flags = val->flags; copy->dataclass = val->dataclass; /* copy any active partial locks; * this should be empty for candidate or PDU source * vals, but in case a copy of running is made, this * array of partial locks needs to be transferred */ for (i=0; iplock[i] = val->plock[i]; } /* copy meta-data */ for (ch = (const val_value_t *)dlq_firstEntry(&val->metaQ); ch != NULL; ch = (const val_value_t *)dlq_nextEntry(ch)) { copych = clone_test(ch, testfn, with_editvars, res); if (!copych) { if (*res == ERR_NCX_SKIPPED) { *res = NO_ERR; } else { val_free_value(copy); return NULL; } } else { dlq_enque(copych, ©->metaQ); } } /* set the copy->editvars */ if (with_editvars) { *res = copy_editvars(val, copy); if (*res != NO_ERR) { val_free_value(copy); return NULL; } } copy->res = val->res; copy->getcb = val->getcb; /* clone the XPath control block if there is one */ if (val->xpathpcb) { copy->xpathpcb = xpath_clone_pcb(val->xpathpcb); if (copy->xpathpcb == NULL) { *res = ERR_INTERNAL_MEM; val_free_value(copy); return NULL; } } /* DO NOT COPY copy->index = val->index; */ /* set copy->indexQ after cloning child nodes is done */ copy->casobj = val->casobj; /* assume OK return for now */ *res = NO_ERR; /* v_ union: copy the actual value or children for complex types */ switch (val->btyp) { case NCX_BT_ENUM: if (val->v.enu.dname != NULL) { /* make sure clone does not point at malloced name * that will get deleted, so this pointer will * point at garbage */ copy->v.enu.dname = xml_strdup(val->v.enu.dname); copy->v.enu.name = copy->v.enu.dname; if (copy->v.enu.dname == NULL) { *res = ERR_INTERNAL_MEM; } } else { copy->v.enu.name = val->v.enu.name; } VAL_ENUM(copy) = VAL_ENUM(val); break; case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: copy->v.boo = val->v.boo; break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: *res = ncx_copy_num(&val->v.num, ©->v.num, val->btyp); break; case NCX_BT_BINARY: ncx_init_binary(©->v.binary); if (val->v.binary.ustr) { copy->v.binary.ustr = m__getMem(val->v.binary.ustrlen); if (!copy->v.binary.ustr) { *res = ERR_INTERNAL_MEM; } else { memcpy(copy->v.binary.ustr, val->v.binary.ustr, val->v.binary.ustrlen); copy->v.binary.ustrlen = val->v.binary.ustrlen; copy->v.binary.ubufflen = val->v.binary.ustrlen; } } break; case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: *res = ncx_copy_str(&val->v.str, ©->v.str, val->btyp); break; case NCX_BT_IDREF: copy->v.idref.name = xml_strdup(val->v.idref.name); if (!copy->v.idref.name) { *res = ERR_INTERNAL_MEM; } else { *res = NO_ERR; } copy->v.idref.nsid = val->v.idref.nsid; copy->v.idref.identity = val->v.idref.identity; break; case NCX_BT_BITS: case NCX_BT_SLIST: *res = ncx_copy_list(&val->v.list, ©->v.list); break; case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_LIST: case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_CASE: val_init_complex(copy, val->btyp); for (ch = (const val_value_t *)dlq_firstEntry(&val->v.childQ); ch != NULL && *res==NO_ERR; ch = (const val_value_t *)dlq_nextEntry(ch)) { if (ch->res == NO_ERR) { copych = clone_test(ch, testfn, with_editvars, res); if (!copych) { if (*res == ERR_NCX_SKIPPED) { *res = NO_ERR; } } else { copych->parent = copy; dlq_enque(copych, ©->v.childQ); } } else { log_warn("\nWarning: Skipping invalid value node '%s' (%s)", ch->name, get_error_string(ch->res)); } } break; case NCX_BT_EXTERN: if (val->v.fname) { copy->v.fname = xml_strdup(val->v.fname); if (!copy->v.fname) { *res = ERR_INTERNAL_MEM; } } break; case NCX_BT_INTERN: if (val->v.intbuff) { copy->v.intbuff = xml_strdup(val->v.intbuff); if (!copy->v.intbuff) { *res = ERR_INTERNAL_MEM; } } break; default: *res = SET_ERROR(ERR_INTERNAL_VAL); } /* reconstruct index records if needed */ if (*res==NO_ERR && !dlq_empty(&val->indexQ)) { *res = val_gen_index_chain(val->obj, copy); } if (*res != NO_ERR) { val_free_value(copy); copy = NULL; } return copy; } /* clone_test */ /*************** E X T E R N A L F U N C T I O N S *************/ /******************************************************************** * FUNCTION val_new_value * * Malloc and initialize the fields in a val_value_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ val_value_t * val_new_value (void) { val_value_t *val = m__getObj(val_value_t); if (!val) { return NULL; } (void)memset(val, 0x0, sizeof(val_value_t)); dlq_createSQue(&val->metaQ); dlq_createSQue(&val->indexQ); dlq_createSQue(&val->getcbQ); return val; } /* val_new_value */ /******************************************************************** * FUNCTION val_init_complex * * Initialize the fields in a complex val_value_t * this is deprecated and should only be called * by val_init_from_template * * MUST CALL val_new_value FIRST * * INPUTS: * val == pointer to the malloced struct to initialize *********************************************************************/ void val_init_complex (val_value_t *val, ncx_btype_t btyp) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif val->btyp = btyp; dlq_createSQue(&val->v.childQ); } /* val_init_complex */ /******************************************************************** * FUNCTION val_init_virtual * * Special function to initialize a virtual value node * * MUST CALL val_new_value FIRST * * INPUTS: * val == pointer to the malloced struct to initialize * cbfn == get callback function to use * obj == object template to use *********************************************************************/ void val_init_virtual (val_value_t *val, void *cbfn, obj_template_t *obj) { #ifdef DEBUG if (!val || !cbfn || !obj) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif val_init_from_template(val, obj); val->getcb = cbfn; } /* val_init_virtual */ /******************************************************************** * FUNCTION val_init_from_template * * Initialize a value node from its object template * * MUST CALL val_new_value FIRST * * INPUTS: * val == pointer to the initialized value struct to bind * obj == object template to use *********************************************************************/ void val_init_from_template (val_value_t *val, obj_template_t *obj) { #ifdef DEBUG if (!val || !obj) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif init_from_template(val, obj, obj_get_basetype(obj)); } /* val_init_from_template */ /******************************************************************** * FUNCTION val_free_value * * Scrub the memory in a val_value_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * val == val_value_t to delete *********************************************************************/ void val_free_value (val_value_t *val) { if (!val) { return; } #ifdef VAL_FREE_DEBUG if (LOGDEBUG4) { log_debug4("\nval_free_value '%s' %p", val->dname ? val->dname : NCX_EL_NONE, val); } #endif clean_value(val, TRUE); m__free(val); } /* val_free_value */ /******************************************************************** * FUNCTION val_set_name * * Set (or reset) the name of a value struct * * INPUTS: * val == val_value_t data structure to check * name == name string to set * namelen == length of name string *********************************************************************/ void val_set_name (val_value_t *val, const xmlChar *name, uint32 namelen) { #ifdef DEBUG if (!val || !name) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* check no change to name */ if (val->name && (xml_strlen(val->name) == namelen) && !xml_strncmp(val->name, name, namelen)) { return; } /* replace the name field */ if (val->dname) { m__free(val->dname); } val->dname = xml_strndup(name, namelen); if (!val->dname) { SET_ERROR(ERR_INTERNAL_MEM); } val->name = val->dname; } /* val_set_name */ /******************************************************************** * FUNCTION val_force_dname * * Set (or reset) the name of a value struct * Set all descendant nodes as well * Force dname to be used, not object name backptr * * INPUTS: * val == val_value_t data structure to check * name == name string to set * namelen == length of name string * * RETURNS: * status *********************************************************************/ status_t val_force_dname (val_value_t *val) { val_value_t *chval; status_t res = NO_ERR; #ifdef DEBUG if (val == NULL || val->name == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (val->dname == NULL) { val->dname = xml_strdup(val->name); if (val->dname == NULL) { return ERR_INTERNAL_MEM; } val->name = val->dname; } for (chval = val_get_first_child(val); chval != NULL; chval = val_get_next_child(chval)) { res = val_force_dname(chval); if (res != NO_ERR) { return res; } } return res; } /* val_force_dname */ /******************************************************************** * FUNCTION val_set_qname * * Set (or reset) the name and namespace ID of a value struct * * INPUTS: * val == val_value_t data structure to check * nsid == namespace ID to set * name == name string to set * namelen == length of name string *********************************************************************/ void val_set_qname (val_value_t *val, xmlns_id_t nsid, const xmlChar *name, uint32 namelen) { #ifdef DEBUG if (!val || !name) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif val->nsid = nsid; /* check no change to name */ if (val->name && (xml_strlen(val->name) == namelen) && !xml_strncmp(val->name, name, namelen)) { return; } /* replace the name field */ if (val->dname) { m__free(val->dname); } val->dname = xml_strndup(name, namelen); if (!val->dname) { SET_ERROR(ERR_INTERNAL_MEM); } val->name = val->dname; } /* val_set_qname */ /******************************************************************** * FUNCTION val_string_ok * * Check a string to make sure the value is valid based * on the restrictions in the specified typdef * * INPUTS: * typdef == typ_def_t for the designated string type * btyp == basetype of the string * strval == string value to check * * RETURNS: * status *********************************************************************/ status_t val_string_ok (typ_def_t *typdef, ncx_btype_t btyp, const xmlChar *strval) { return val_string_ok_ex(typdef, btyp, strval, NULL, TRUE); } /* val_string_ok */ /******************************************************************** * FUNCTION val_string_ok_errinfo * * retrieve the YANG custom error info if any * Check a string to make sure the value is valid based * on the restrictions in the specified typdef * Retrieve the configured error info struct if any error * * INPUTS: * typdef == typ_def_t for the designated string type * btyp == basetype of the string * strval == string value to check * errinfo == address of return errinfo block (may be NULL) * * OUTPUTS: * if non-NULL: * *errinfo == error record to use if return error * * RETURNS: * status *********************************************************************/ status_t val_string_ok_errinfo (typ_def_t *typdef, ncx_btype_t btyp, const xmlChar *strval, ncx_errinfo_t **errinfo) { return val_string_ok_ex(typdef, btyp, strval, errinfo, TRUE); } /******************************************************************** * FUNCTION val_string_ok_ex * * retrieve the YANG custom error info if any * Check a string to make sure the value is valid based * on the restrictions in the specified typdef * Retrieve the configured error info struct if any error * * INPUTS: * typdef == typ_def_t for the designated string type * btyp == basetype of the string * strval == string value to check * errinfo == address of return errinfo block (may be NULL) * logerrors == TRUE to log errors * == FALSE to not log errors (use for NCX_BT_UNION) * OUTPUTS: * if non-NULL: * *errinfo == error record to use if return error * * RETURNS: * status *********************************************************************/ status_t val_string_ok_ex (typ_def_t *typdef, ncx_btype_t btyp, const xmlChar *strval, ncx_errinfo_t **errinfo, boolean logerrors) { xpath_pcb_t *xpathpcb; obj_template_t *leafobj, *objroot; xpath_source_t xpath_source; status_t res; ncx_num_t len; #ifdef DEBUG if (!typdef || !strval) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; xpath_source = XP_SRC_INSTANCEID; if (errinfo) { *errinfo = NULL; } /* make sure the data type is correct */ switch (btyp) { case NCX_BT_BINARY: /* just get the length of the decoded binary string */ len.u = b64_get_decoded_str_len( strval, xml_strlen(strval) ); res = val_range_ok_errinfo( typdef, NCX_BT_UINT32, &len, errinfo); break; case NCX_BT_STRING: if (!(typ_is_xpath_string(typdef) || typ_is_schema_instance_string(typdef))) { len.u = xml_strlen(strval); res = val_range_ok_errinfo(typdef, NCX_BT_UINT32, &len, errinfo); if (res == NO_ERR) { res = val_pattern_ok_errinfo(typdef, strval, errinfo); } break; } /* else fall through and treat XPath string */ case NCX_BT_INSTANCE_ID: /* instance-identifier is handled with xpath.c xpath1.c */ if (typ_is_schema_instance_string(typdef)) { xpath_source = XP_SRC_SCHEMA_INSTANCEID; } xpathpcb = xpath_new_pcb(strval, NULL); if (xpathpcb == NULL) { res = ERR_INTERNAL_MEM; } else if (btyp == NCX_BT_INSTANCE_ID || typ_is_schema_instance_string(typdef)) { /* do a first pass parsing to resolve all * the prefixes and check well-formed XPath */ res = xpath_yang_parse_path_ex(NULL, NULL, xpath_source, xpathpcb, logerrors); if (res == NO_ERR) { leafobj = NULL; res = xpath_yang_validate_path_ex(NULL, ncx_get_gen_root(), xpathpcb, (xpath_source == XP_SRC_INSTANCEID) ? FALSE : TRUE, &leafobj, logerrors); } xpath_free_pcb(xpathpcb); } else { xpathpcb->logerrors = logerrors; res = xpath1_parse_expr(NULL, NULL, xpathpcb, XP_SRC_YANG); if (res == NO_ERR) { objroot = ncx_get_gen_root(); res = xpath1_validate_expr(objroot->tkerr.mod, objroot, xpathpcb); } xpath_free_pcb(xpathpcb); } break; case NCX_BT_LEAFREF: /*** FIXME: MISSING LEAFREF VALIDATION ***/ return NO_ERR; default: return ERR_NCX_WRONG_DATATYP; } return res; } /* val_string_ok_errinfo */ /******************************************************************** * FUNCTION val_list_ok * * Check a list to make sure the all the strings are valid based * on the specified typdef * * validate all the ncx_lmem_t entries in the list * against the specified typdef. Mark any errors * in the ncx_lmem_t flags field of each member * in the list with an error * * INPUTS: * typdef == typ_def_t for the designated list type * btyp == base type (NCX_BT_SLIST or NCX_BT_BITS) * list == list struct with ncx_lmem_t structs to check * * OUTPUTS: * If return other than NO_ERR: * each list->lmem.flags field may contain bits set * for errors: * NCX_FL_RANGE_ERR: size out of range * NCX_FL_VALUE_ERR value not permitted by value set, * or pattern * RETURNS: * status *********************************************************************/ status_t val_list_ok (typ_def_t *typdef, ncx_btype_t btyp, ncx_list_t *list) { #ifdef DEBUG if (!typdef || !list) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return val_list_ok_errinfo(typdef, btyp, list, NULL); } /* val_list_ok */ /******************************************************************** * FUNCTION val_list_ok_errinfo * * Check a list to make sure the all the strings are valid based * on the specified typdef * * INPUTS: * typdef == typ_def_t for the designated list type * btyp == base type (NCX_BT_SLIST or NCX_BT_BITS) * list == list struct with ncx_lmem_t structs to check * errinfo == address of return rpc-error info struct * * OUTPUTS: * If return other than NO_ERR: * *errinfo contains the YANG specified error info, if any* * each list->lmem.flags field may contain bits set * for errors: * NCX_FL_RANGE_ERR: size out of range * NCX_FL_VALUE_ERR value not permitted by value set, * or pattern * RETURNS: * status *********************************************************************/ status_t val_list_ok_errinfo (typ_def_t *typdef, ncx_btype_t btyp, ncx_list_t *list, ncx_errinfo_t **errinfo) { typ_template_t *listtyp; typ_def_t *listdef; ncx_lmem_t *lmem; status_t res; #ifdef DEBUG if (!typdef || !list) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif listdef = NULL; if (errinfo) { *errinfo = NULL; } /* listtyp is for the list members, not the list itself */ if (btyp == NCX_BT_SLIST) { listtyp = typ_get_listtyp(typdef); listdef = &listtyp->typdef; } /* go through all the list members and check them */ for (lmem = (ncx_lmem_t *)dlq_firstEntry(&list->memQ); lmem != NULL; lmem = (ncx_lmem_t *)dlq_nextEntry(lmem)) { /* stop on first error -- if 1 list member is invalid * then the entire list is invalid */ if (btyp == NCX_BT_SLIST) { res = val_simval_ok_errinfo(listdef, lmem->val.str, errinfo); } else { res = val_bit_ok(typdef, lmem->val.str, NULL); } if (res != NO_ERR) { return res; } } return NO_ERR; } /* val_list_ok_errinfo */ /******************************************************************** * FUNCTION val_enum_ok * * Check an enumerated integer string to make sure the value * is valid based on the specified typdef * * INPUTS: * typdef == typ_def_t for the designated enum type * enumval == enum string value to check * retval == pointer to return integer variable * retstr == pointer to return string name variable * * OUTPUTS: * *retval == integer value of enum * *retstr == pointer to return string name variable * * RETURNS: * status *********************************************************************/ status_t val_enum_ok (typ_def_t *typdef, const xmlChar *enumval, int32 *retval, const xmlChar **retstr) { dlq_hdr_t *checkQ; typ_enum_t *en; status_t res; boolean last; ncx_btype_t btyp; #ifdef DEBUG if (!typdef || !enumval ||!retval ||!retstr) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* make sure the data type is correct */ btyp = typ_get_basetype(typdef); if (btyp != NCX_BT_ENUM) { return ERR_NCX_WRONG_DATATYP; } /* check which string Q to use for further processing */ switch (typdef->tclass) { case NCX_CL_SIMPLE: checkQ = &typdef->def.simple.valQ; last = TRUE; break; case NCX_CL_NAMED: /* the restrictions in the newtyp override anything * in the parent typedef(s) */ last = FALSE; if (typdef->def.named.newtyp) { checkQ = &typdef->def.named.newtyp->def.simple.valQ; } else { checkQ = NULL; } break; default: return ERR_NCX_WRONG_DATATYP; /* should not happen */ } /* check typdefs until the final one in the chain is reached */ for (;;) { if (checkQ) { res = check_svalQ_enum(enumval, checkQ, &en); if (res == NO_ERR) { /* return both the name and number of the found enum */ *retval = en->val; *retstr = en->name; return NO_ERR; } } /* check if any more typdefs to search */ if (last) { return ERR_NCX_VAL_NOTINSET; } /* setup the typdef and checkQ for the next loop */ typdef = typ_get_next_typdef(&typdef->def.named.typ->typdef); if (!typdef) { return SET_ERROR(ERR_INTERNAL_VAL); } switch (typdef->tclass) { case NCX_CL_SIMPLE: checkQ = &typdef->def.simple.valQ; last = TRUE; break; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { checkQ = &typdef->def.named.newtyp->def.simple.valQ; } else { checkQ = NULL; } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } } /*NOTREACHED*/ } /* val_enum_ok */ /******************************************************************** * FUNCTION val_bit_ok * * Check a bit name is valid for the typedef * * INPUTS: * typdef == typ_def_t for the designated bits type * bitname == bit name value to check * position == address of return bit struct position value * * OUTPUTS: * if non-NULL: * *position == bit position value * * RETURNS: * status *********************************************************************/ status_t val_bit_ok (const typ_def_t *typdef, const xmlChar *bitname, uint32 *position) { const dlq_hdr_t *checkQ; status_t res; boolean last; typ_enum_t *en; #ifdef DEBUG if (!typdef || !bitname) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif /* check which string Q to use for further processing */ switch (typdef->tclass) { case NCX_CL_SIMPLE: checkQ = &typdef->def.simple.valQ; last = TRUE; break; case NCX_CL_NAMED: /* the restrictions in the newtyp override anything * in the parent typedef(s) */ last = FALSE; if (typdef->def.named.newtyp) { checkQ = &typdef->def.named.newtyp->def.simple.valQ; } else { checkQ = NULL; } break; default: return ERR_NCX_WRONG_DATATYP; /* should not happen */ } /* check typdefs until the final one in the chain is reached */ for (;;) { if (checkQ) { res = check_svalQ_enum(bitname, checkQ, &en); if (res == NO_ERR) { if (position) { *position = en->pos; } return NO_ERR; } } /* check if any more typdefs to search */ if (last) { return ERR_NCX_VAL_NOTINSET; } /* setup the typdef and checkQ for the next loop */ typdef = typ_get_next_typdef(&typdef->def.named.typ->typdef); if (!typdef) { return SET_ERROR(ERR_INTERNAL_VAL); } switch (typdef->tclass) { case NCX_CL_SIMPLE: checkQ = &typdef->def.simple.valQ; last = TRUE; break; case NCX_CL_NAMED: if (typdef->def.named.newtyp) { checkQ = &typdef->def.named.newtyp->def.simple.valQ; } else { checkQ = NULL; } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } } /*NOTREACHED*/ } /* val_bit_ok */ /******************************************************************** * FUNCTION val_idref_ok * * Check if an identityref QName is valid for the typedef * The QName must match an identity that has the same base * as specified in the typdef * * INPUTS: * typdef == typ_def_t for the designated identityref type * qname == QName or local-name string to check * nsid == namespace ID from XML node for NS of QName * this NSID will be used and not the prefix in qname * it was parsed in the XML node and is not a module prefix * name == address of return local name part of QName * id == address of return identity, if found * * OUTPUTS: * if non-NULL: * *name == pointer into the qname string at the start of * the local name part * *id == pointer to ncx_identity_t found * * RETURNS: * status *********************************************************************/ status_t val_idref_ok (typ_def_t *typdef, const xmlChar *qname, xmlns_id_t nsid, const xmlChar **name, const ncx_identity_t **id) { const typ_idref_t *idref; const xmlChar *str, *modname; ncx_module_t *mod; ncx_identity_t *identity; boolean found; #ifdef DEBUG if (!typdef || !qname) { return SET_ERROR(ERR_INTERNAL_PTR); } if (typ_get_basetype(typdef) != NCX_BT_IDREF) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif found = FALSE; idref = typ_get_cidref(typdef); if (!idref) { return SET_ERROR(ERR_INTERNAL_VAL); } /* find the local-name in the prefix:local-name combo */ str = qname; while (*str && *str != ':') { str++; } if (*str == ':') { str++; } else { str = qname; } if (nsid) { /* str should point to the local name now */ modname = xmlns_get_module(nsid); if (!modname) { return ERR_NCX_INVALID_VALUE; } mod = ncx_find_module(modname, NULL); if (!mod) { return ERR_NCX_INVALID_VALUE; } /* the namespace produced a module which should have * the identity-stmt with this name */ identity = ncx_find_identity(mod, str, FALSE); if(identity) { /* got some identity match; make sure this identity * has an ancestor node that is derived * form the base specified in the typdef */ if(ncx123_identity_is_derived_from(identity, idref->base)) { found = TRUE; } } } else { /* no default namespace, so be liberal and * try to find any matching identity from the idref * valid values. */ unsigned int matched_cnt; matched_cnt = ncx123_find_matching_identities(NULL, qname, idref, &identity, 1); if ( matched_cnt==1 ) { found = TRUE; } if ( matched_cnt>1 ) { log_warn("\nWarning: val_idref_ok found %u matches\n",matched_cnt); } } if (found) { if (name) { *name = identity->name; } if (id) { *id = identity; } return NO_ERR; } return ERR_NCX_INVALID_VALUE; } /* val_idref_ok */ /******************************************************************** * FUNCTION val_parse_idref * * Parse a CLI BASED identityref QName into its various parts * * INPUTS: * mod == module containing the default-stmt (or NULL if N/A) * qname == QName or local-name string to parse * nsid == address of return namespace ID of the module * indicated by the prefix. If mod==NULL then * a prefix MUST be present * name == address of return local name part of QName * id == address of return identity, if found * * OUTPUTS: * if non-NULL: * *nsid == namespace ID for the prefix part of the QName * *name == pointer into the qname string at the start of * the local name part * *id == pointer to ncx_identity_t found (if any, not an error) * * RETURNS: * status *********************************************************************/ status_t val_parse_idref (ncx_module_t *mod, const xmlChar *qname, xmlns_id_t *nsid, const xmlChar **name, ncx_identity_t **id) { status_t res; res = val123_parse_idref_ex (mod, qname, NULL/*idref*/, id); if(res==NO_ERR) { *name = (*id)->name; *nsid = (*id)->mod->nsid; } return res; } /******************************************************************** * FUNCTION val_range_ok * * Check a number to see if it is in range or not * Could be a number or size range * * INPUTS: * typdef == typ_def_t for the simple type to check * btyp == base type of num * num == number to check * * RETURNS: * status *********************************************************************/ status_t val_range_ok (typ_def_t *typdef, ncx_btype_t btyp, const ncx_num_t *num) { #ifdef DEBUG if (!typdef || !num) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return val_range_ok_errinfo(typdef, btyp, num, NULL); } /* val_range_ok */ /******************************************************************** * FUNCTION val_range_ok_errinfo * * Check a number to see if it is in range or not * Could be a number or size range * * INPUTS: * typdef == typ_def_t for the simple type to check * btyp == base type of num * num == number to check * errinfo == address of return error struct * * OUTPUTS: * if non-NULL: * *errinfo == errinfo record on error exit * * RETURNS: * status *********************************************************************/ status_t val_range_ok_errinfo (typ_def_t *typdef, ncx_btype_t btyp, const ncx_num_t *num, ncx_errinfo_t **errinfo) { typ_def_t *testdef; dlq_hdr_t *checkQ; ncx_errinfo_t *range_errinfo; status_t res; #ifdef DEBUG if (!typdef || !num) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (errinfo) { *errinfo = NULL; } /* find the real typdef to check */ testdef = typ_get_qual_typdef(typdef, NCX_SQUAL_RANGE); if (!testdef) { /* assume this means no range specified and * not an internal PTR or VAL error */ return NO_ERR; } /* there can only be one active range, which is * the most derived typdef that declares a range */ range_errinfo = typ_get_range_errinfo(testdef); checkQ = typ_get_rangeQ_con(testdef); res = check_rangeQ(btyp, num, checkQ); if (res != NO_ERR && errinfo && range_errinfo && ncx_errinfo_set(range_errinfo)) { *errinfo = range_errinfo; } return res; } /* val_range_ok_errinfo */ /******************************************************************** * FUNCTION val_pattern_ok * * Check a string against all the patterns in a big AND expression * * INPUTS: * typdef == typ_def_t for the designated enum type * strval == string value to check * * RETURNS: * NO_ERR if pattern OK or no patterns found to check; error otherwise *********************************************************************/ status_t val_pattern_ok (typ_def_t *typdef, const xmlChar *strval) { #ifdef DEBUG if (!typdef) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return val_pattern_ok_errinfo(typdef, strval, NULL); } /* val_pattern_ok */ /******************************************************************** * FUNCTION val_pattern_ok_errinfo * * Check a string against all the patterns in a big AND expression * * INPUTS: * typdef == typ_def_t for the designated enum type * strval == string value to check * errinfo == address of return errinfo struct for err-pattern * * OUTPUTS: * *errinfo set to error info struct if any, and if error exit * * RETURNS: * NO_ERR if pattern OK or no patterns found to check; * error otherwise, and *errinfo will be set if the pattern * that failed has any errinfo defined in it *********************************************************************/ status_t val_pattern_ok_errinfo (typ_def_t *typdef, const xmlChar *strval, ncx_errinfo_t **errinfo) { typ_pattern_t *pat; #ifdef DEBUG if (!typdef) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (!strval) { strval = EMPTY_STRING; } if (typ_get_basetype(typdef) != NCX_BT_STRING) { return ERR_NCX_WRONG_DATATYP; } if (errinfo) { *errinfo = NULL; } while (typdef) { for (pat = typ_get_first_pattern(typdef); pat != NULL; pat = typ_get_next_pattern(pat)) { if (!pattern_match(pat->pattern, strval)) { if (errinfo && ncx_errinfo_set(&pat->pat_errinfo)) { *errinfo = &pat->pat_errinfo; } return ERR_NCX_PATTERN_FAILED; } /* else matched -- keep trying more patterns */ } typdef = typ_get_parent_typdef(typdef); } return NO_ERR; } /* val_pattern_ok_errinfo */ /******************************************************************** * FUNCTION val_simval_ok * * check any simple type to see if it is valid, * but do not retrieve the value; used to check the * default parameter for example * * INPUTS: * typdef == typ_def_t for the simple type to check * simval == value string to check (NULL means empty string) * * RETURNS: * status *********************************************************************/ status_t val_simval_ok (typ_def_t *typdef, const xmlChar *simval) { #ifdef DEBUG if (!typdef) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return val_simval_ok_max(typdef, simval, NULL, NULL, TRUE); } /* val_simval_ok */ /******************************************************************** * FUNCTION val_simval_ok_errinfo * * check any simple type to see if it is valid, * but do not retrieve the value; used to check the * default parameter for example * * INPUTS: * typdef == typ_def_t for the simple type to check * simval == value string to check (NULL means empty string) * errinfo == address of return error struct * * OUTPUTS: * if non-NULL: * *errinfo == error struct on error exit * * RETURNS: * status *********************************************************************/ status_t val_simval_ok_errinfo (typ_def_t *typdef, const xmlChar *simval, ncx_errinfo_t **errinfo) { return val_simval_ok_max(typdef, simval, errinfo, NULL, TRUE); } /* val_simval_ok_errinfo */ /******************************************************************** * FUNCTION val_simval_ok_ex * * check any simple type to see if it is valid, * but do not retrieve the value; used to check the * default parameter for example * * INPUTS: * typdef == typ_def_t for the simple type to check * simval == value string to check (NULL means empty string) * errinfo == address of return error struct * mod == module in progress to use for idref and other * strings with prefixes in them * OUTPUTS: * if non-NULL: * *errinfo == error struct on error exit * * RETURNS: * status *********************************************************************/ status_t val_simval_ok_ex (typ_def_t *typdef, const xmlChar *simval, ncx_errinfo_t **errinfo, ncx_module_t *mod) { return val_simval_ok_max(typdef, simval, errinfo, mod, TRUE); } /* val_simval_ok_ex */ /******************************************************************** * FUNCTION val_simval_ok_max * * check any simple type to see if it is valid, * but do not retrieve the value; used to check the * default parameter for example * * INPUTS: * typdef == typ_def_t for the simple type to check * simval == value string to check (NULL means empty string) * errinfo == address of return error struct * mod == module in progress to use for idref and other * strings with prefixes in them * logerrors == TRUE to log errors; FALSE to not log errors * (use FALSE for NCX_BT_UNION) * OUTPUTS: * if non-NULL: * *errinfo == error struct on error exit * * RETURNS: * status *********************************************************************/ status_t val_simval_ok_max (typ_def_t *typdef, const xmlChar *simval, ncx_errinfo_t **errinfo, ncx_module_t *mod, boolean logerrors) { const xmlChar *retstr, *name; val_value_t *unval; typ_template_t *listtyp; typ_def_t *realtypdef; ncx_identity_t *identity; ncx_num_t num; ncx_list_t list; status_t res; ncx_btype_t btyp, listbtyp; int32 retval; boolean forced; xmlns_id_t nsid; #ifdef DEBUG if (!typdef) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (errinfo) { *errinfo = NULL; } if (!simval) { forced = TRUE; simval = EMPTY_STRING; } else { forced = FALSE; } btyp = typ_get_basetype(typdef); switch (btyp) { case NCX_BT_NONE: res = ERR_NCX_DEF_NOT_FOUND; break; case NCX_BT_ANYDATA: case NCX_BT_ANYXML: res = ERR_NCX_INVALID_VALUE; break; case NCX_BT_ENUM: res = val_enum_ok(typdef, simval, &retval, &retstr); break; case NCX_BT_EMPTY: if (forced || !*simval) { res = NO_ERR; } else { res = ERR_NCX_INVALID_VALUE; } break; case NCX_BT_BOOLEAN: if (ncx_is_true(simval) || ncx_is_false(simval)) { res = NO_ERR; } else { res = ERR_NCX_INVALID_VALUE; } break; case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: if (*simval == '-') { res = ERR_NCX_INVALID_VALUE; break; } /* fall through */ case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_FLOAT64: res = ncx_decode_num(simval, btyp, &num); if (res == NO_ERR) { res = val_range_ok_errinfo(typdef, btyp, &num, errinfo); } ncx_clean_num(btyp, &num); break; case NCX_BT_DECIMAL64: res = ncx_decode_dec64(simval, typ_get_fraction_digits(typdef), &num); if (res == NO_ERR) { res = val_range_ok(typdef, btyp, &num); } ncx_clean_num(btyp, &num); break; case NCX_BT_STRING: case NCX_BT_BINARY: res = val_string_ok_ex(typdef, btyp, simval, errinfo, logerrors); break; case NCX_BT_INSTANCE_ID: res = val_string_ok_ex(typdef, btyp, simval, errinfo, logerrors); break; case NCX_BT_UNION: unval = val_new_value(); if (!unval) { res = ERR_INTERNAL_MEM; } else { unval->btyp = NCX_BT_UNION; unval->typdef = typdef; res = val_union_ok_ex(typdef, simval, unval, errinfo, mod); unval->btyp = NCX_BT_NONE; val_free_value(unval); } break; case NCX_BT_LEAFREF: /* cannot check instances for default or * manager-side set function, so just check * if the pointed-at typedef validates correctly */ realtypdef = typ_get_xref_typdef(typdef); if (realtypdef) { res = val_simval_ok_max(realtypdef, simval, errinfo, mod, logerrors); } else { res = SET_ERROR(ERR_INTERNAL_VAL); } break; case NCX_BT_IDREF: { const typ_idref_t *idref; idref = typ_get_cidref(typdef); assert(idref); identity = NULL; res = val123_parse_idref_ex(mod, simval, idref, &identity); } break; case NCX_BT_BITS: ncx_init_list(&list, NCX_BT_BITS); res = ncx_set_list(NCX_BT_BITS, simval, &list); if (res == NO_ERR) { res = ncx_finish_list(typdef, &list); if (res == NO_ERR) { res = val_list_ok_errinfo(typdef, btyp, &list, errinfo); } } ncx_clean_list(&list); break; case NCX_BT_SLIST: listtyp = typ_get_listtyp(typdef); listbtyp = typ_get_basetype(&listtyp->typdef); ncx_init_list(&list, listbtyp); res = ncx_set_list(listbtyp, simval, &list); if (res == NO_ERR) { res = ncx_finish_list(&listtyp->typdef, &list); if (res == NO_ERR) { res = val_list_ok_errinfo(typdef, btyp, &list, errinfo); } } ncx_clean_list(&list); break; case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_LIST: case NCX_BT_EXTERN: case NCX_BT_INTERN: res = ERR_NCX_INVALID_VALUE; break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* val_simval_ok_max */ /******************************************************************** * FUNCTION val_union_ok * * Check a union to make sure the string is valid based * on the specified typdef, and convert the string to * an NCX internal format * * INPUTS: * typdef == typ_def_t for the designated union type * strval == the value to check against the member typ defs * retval == pointer to output struct for converted value * * OUTPUTS: * If return NO_ERR: * retval->str or retval->num will be set with the converted value * * RETURNS: * status *********************************************************************/ status_t val_union_ok (typ_def_t *typdef, const xmlChar *strval, val_value_t *retval) { #ifdef DEBUG if (!typdef || !retval) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return val_union_ok_ex(typdef, strval, retval, NULL, NULL); } /* val_union_ok */ /******************************************************************** * FUNCTION val_union_ok_errinfo * * Check a union to make sure the string is valid based * on the specified typdef, and convert the string to * an NCX internal format * * INPUTS: * typdef == typ_def_t for the designated union type * strval == the value to check against the member typ defs * retval == pointer to output struct for converted value * errinfo == address of error struct * * OUTPUTS: * If return NO_ERR: * retval->str or retval->num will be set with the converted value * *errinfo == error struct on error exit * * RETURNS: * status *********************************************************************/ status_t val_union_ok_errinfo (typ_def_t *typdef, const xmlChar *strval, val_value_t *retval, ncx_errinfo_t **errinfo) { return val_union_ok_ex(typdef, strval, retval, errinfo, NULL); } /* val_union_ok_errinfo */ /******************************************************************** * FUNCTION val_union_ok_ex * * Check a union to make sure the string is valid based * on the specified typdef, and convert the string to * an NCX internal format * * INPUTS: * typdef == typ_def_t for the designated union type * strval == the value to check against the member typ defs * retval == pointer to output struct for converted value * errinfo == address of error struct * mod == module in progress, if any * * OUTPUTS: * If return NO_ERR: * retval->str or retval->num will be set with the converted value * *errinfo == error struct on error exit * * RETURNS: * status *********************************************************************/ status_t val_union_ok_ex (typ_def_t *typdef, const xmlChar *strval, val_value_t *retval, ncx_errinfo_t **errinfo, ncx_module_t *mod) { typ_def_t *undef; typ_unionnode_t *un; status_t res; boolean done; ncx_btype_t testbtyp; #ifdef DEBUG if (!typdef || !retval) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; if (errinfo) { *errinfo = NULL; } /* get the first union member type */ un = typ_first_unionnode(typdef); #ifdef DEBUG if (!un || (!un->typ && !un->typdef)) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif done = FALSE; /* go through all the union member typdefs until * the first match (decodes as a valid value for that typdef) */ while (!done) { /* get type info for this member typedef */ if (un->typ) { undef = &un->typ->typdef; } else if (un->typdef) { undef = un->typdef; } else { res = SET_ERROR(ERR_INTERNAL_VAL); done = TRUE; continue; } testbtyp = typ_get_basetype(undef); if (testbtyp == NCX_BT_UNION) { res = val_union_ok_ex(undef, strval, retval, errinfo, mod); } else { res = val_simval_ok_max(undef, strval, errinfo, mod, FALSE); } if (res == NO_ERR) { /* the un->typ field may be NULL for YANG unions in progress * When the default is checked this ptr may be NULL, but the * retval is not used by that fn. After the module is * registered, the un->typ field should be set */ if (testbtyp != NCX_BT_UNION) { retval->btyp = typ_get_basetype(undef); } done = TRUE; } else if (res != ERR_INTERNAL_MEM) { un = (typ_unionnode_t *)dlq_nextEntry(un); if (!un) { res = ERR_NCX_WRONG_NODETYP; done = TRUE; } } else { done = TRUE; } } return res; } /* val_union_ok_ex */ /******************************************************************** * FUNCTION val_get_metaQ * * Get the meta Q header for the value * * INPUTS: * val == value node to check * * RETURNS: * pointer to the metaQ for this value *********************************************************************/ dlq_hdr_t * val_get_metaQ (val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (val->getcb) { /* the virtual value will not have any attributes * present; only the PDU value nodes will have * any XML attributes present */ return NULL; } else { return &val->metaQ; } } /* val_get_metaQ */ /******************************************************************** * FUNCTION val_get_first_meta * * Get the first metaQ entry from the specified Queue * * INPUTS: * queue == queue of meta-vals to check * * RETURNS: * pointer to the first meta-var in the Queue if found, * or NULL if none *********************************************************************/ val_value_t * val_get_first_meta (dlq_hdr_t *queue) { #ifdef DEBUG if (!queue) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (val_value_t *)dlq_firstEntry(queue); } /* val_get_first_meta */ /******************************************************************** * FUNCTION val_get_first_meta_val * * Get the first metaQ entry from the specified Queue * * INPUTS: * value node to get the metaQ from * * RETURNS: * pointer to the first meta-var in the Queue if found, * or NULL if none *********************************************************************/ val_value_t * val_get_first_meta_val (val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (val_value_t *)dlq_firstEntry(&val->metaQ); } /* val_get_first_meta_val */ /******************************************************************** * FUNCTION val_get_next_meta * * Get the next metaQ entry from the specified entry * * INPUTS: * curnode == current meta-var node * * RETURNS: * pointer to the next meta-var in the Queue if found, * or NULL if none *********************************************************************/ val_value_t * val_get_next_meta (val_value_t *curnode) { #ifdef DEBUG if (!curnode) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (val_value_t *)dlq_nextEntry(curnode); } /* val_get_next_meta */ /******************************************************************** * FUNCTION val_meta_empty * * Check if the metaQ is empty for the value node * * INPUTS: * val == value to check * * RETURNS: * TRUE if the metaQ for the value is empty * FALSE otherwise *********************************************************************/ boolean val_meta_empty (val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return TRUE; } #endif if (val->getcb) { /* only the real values (not virtual values) will * have any XML attributes present */ return TRUE; } else { return dlq_empty(&val->metaQ); } } /* val_meta_empty */ /******************************************************************** * FUNCTION val_find_meta * * Get the corresponding meta data node * * INPUTS: * val == value to check for metadata * nsid == namespace ID of 'name'; 0 == don't use * name == name of metadata variable name * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ val_value_t * val_find_meta (val_value_t *val, xmlns_id_t nsid, const xmlChar *name) { val_value_t *metaval; #ifdef DEBUG if (!val || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (metaval = (val_value_t *)dlq_firstEntry(&val->metaQ); metaval != NULL; metaval = (val_value_t *)dlq_nextEntry(metaval)) { /* check the node if the name matches and * check for position instance match */ if (!xml_strcmp(metaval->name, name)) { if (xmlns_ids_equal(nsid, metaval->nsid)) { return metaval; } } } return NULL; } /* val_find_meta */ /******************************************************************** * FUNCTION val_meta_match * * Return true if the corresponding attribute exists and has * the same value * * INPUTS: * val == value to check for metadata * metaval == value to match in the val->metaQ * * RETURNS: * TRUE if the specified attr if found and has the same value * FALSE otherwise *********************************************************************/ boolean val_meta_match (val_value_t *val, val_value_t *metaval) { val_value_t *m1; dlq_hdr_t *queue; boolean ret, done; #ifdef DEBUG if (!val || !metaval) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif queue = val_get_metaQ(val); if (!queue) { return FALSE; } ret = FALSE; done = FALSE; /* check all the metavars in the val->metaQ until * the specified entry is found or list ends */ for (m1 = val_get_first_meta(queue); m1 != NULL && !done; m1 = val_get_next_meta(m1)) { /* check the node if the name matches and * then if the namespace matches */ if (!xml_strcmp(metaval->name, m1->name)) { if (!xmlns_ids_equal(metaval->nsid, m1->nsid)) { continue; } ret = (val_compare(metaval, m1)) ? FALSE : TRUE; done = TRUE; } } return ret; } /* val_meta_match */ /******************************************************************** * FUNCTION val_metadata_inst_count * * Get the number of instances of the specified attribute * * INPUTS: * val == value to check for metadata instance count * nsid == namespace ID of the meta data variable * name == name of the meta data variable * * RETURNS: * number of instances found in val->metaQ *********************************************************************/ uint32 val_metadata_inst_count (val_value_t *val, xmlns_id_t nsid, const xmlChar *name) { val_value_t *metaval; uint32 cnt; #ifdef DEBUG if (!val || !name) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif cnt = 0; for (metaval = (val_value_t *)dlq_firstEntry(&val->metaQ); metaval != NULL; metaval = (val_value_t *)dlq_nextEntry(metaval)) { if (xml_strcmp(metaval->name, name)) { continue; } if (nsid && metaval->nsid) { if (metaval->nsid == nsid) { cnt++; } } else { cnt++; } } return cnt; } /* val_metadata_inst_count */ /******************************************************************** * FUNCTION val_dump_value * * Printf the specified val_value_t struct to * the logfile, or stdout if none set * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to printf * startindent == start indent char count * *********************************************************************/ void val_dump_value (val_value_t *val, int32 startindent) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif val_dump_value_max(val, startindent, NCX_DEF_INDENT, DUMP_VAL_LOG, ncx_get_display_mode(), FALSE, FALSE); } /* val_dump_value */ /******************************************************************** * FUNCTION val_dump_value_ex * * Printf the specified val_value_t struct to * the logfile, or stdout if none set * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to printf * startindent == start indent char count * display_mode == display mode to use *********************************************************************/ void val_dump_value_ex (val_value_t *val, int32 startindent, ncx_display_mode_t display_mode) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif val_dump_value_max(val, startindent, NCX_DEF_INDENT, DUMP_VAL_LOG, display_mode, FALSE, FALSE); } /* val_dump_value_ex */ /******************************************************************** * FUNCTION val_dump_alt_value * * Printf the specified val_value_t struct to * the alternate logfile * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to printf * startindent == start indent char count * *********************************************************************/ void val_dump_alt_value (val_value_t *val, int32 startindent) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif val_dump_value_max(val, startindent, NCX_DEF_INDENT, DUMP_VAL_ALT_LOG, ncx_get_display_mode(), FALSE, FALSE); } /* val_dump_alt_value */ /******************************************************************** * FUNCTION val_stdout_value * * Printf the specified val_value_t struct to stdout * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to printf * startindent == start indent char count * *********************************************************************/ void val_stdout_value (val_value_t *val, int32 startindent) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif val_dump_value_max(val, startindent, NCX_DEF_INDENT, DUMP_VAL_STDOUT, ncx_get_display_mode(), FALSE, FALSE); } /* val_stdout_value */ /******************************************************************** * FUNCTION val_stdout_value_ex * * Printf the specified val_value_t struct to stdout * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to printf * startindent == start indent char count * display_mode == display mode to use *********************************************************************/ void val_stdout_value_ex (val_value_t *val, int32 startindent, ncx_display_mode_t display_mode) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif val_dump_value_max(val, startindent, NCX_DEF_INDENT, DUMP_VAL_STDOUT, display_mode, FALSE, FALSE); } /* val_stdout_value_ex */ /******************************************************************** * FUNCTION val_dump_value_max_w_file * * Printf the specified val_value_t struct to * the logfile, or stdout if none set * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to dump * startindent == start indent char count * indent_amount == number of spaces for each indent * display_mode == formatting mode for display * with_meta == TRUE if metaQ should be printed * FALSE to skip meta data * configonly == TRUE if config only nodes should be displayed * FALSE if all nodes should be displayed * outputfile == FILE* destination for the serialized data *********************************************************************/ status_t val_dump_value_max_w_file (val_value_t *val, int32 startindent, int32 indent_amount, ncx_display_mode_t display_mode, boolean with_meta, boolean configonly, FILE* outputfile) { dumpfn_t dumpfn, errorfn; indentfn_t indentfn; val_value_t *chval; ncx_lmem_t *listmem; val_idref_t *idref; const xmlChar *prefix; xmlChar *buff; dlq_hdr_t *metaQ; val_value_t *metaval; ncx_btype_t btyp, lbtyp; uint32 len; status_t res; boolean quotes; int32 bump_amount; #ifdef DEBUG if (!val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif bump_amount = max(0, indent_amount); if (configonly && !obj_is_config(val->obj)) { return SET_ERROR(ERR_INTERNAL_PTR); } if (display_mode == NCX_DISPLAY_MODE_XML || display_mode == NCX_DISPLAY_MODE_XML_NONS) { res = xml_wr_check_open_file(outputfile, val, NULL, FALSE, FALSE, (display_mode == NCX_DISPLAY_MODE_XML), startindent, indent_amount, NULL); if (res != NO_ERR) { log_error("\nError: dump value '%s' to XML file failed (%s)", val->name, get_error_string(res)); } } else if (display_mode == NCX_DISPLAY_MODE_JSON) { res = json_wr_check_open_file(outputfile, val, startindent, indent_amount, NULL); if (res != NO_ERR) { log_error("\nError: dump value '%s' to JSON file failed (%s)", val->name, get_error_string(res)); } } return res; } /* val_dump_value_max_w_file */ /* val_make_serialized_string helper functions and definitions */ typedef struct serializer_node_t_ { char* buf; char* cur; size_t buf_len; } serializer_node_t; static ssize_t writer_fn(void *cookie, const void *buffer, size_t size) { serializer_node_t* ser = (serializer_node_t *)cookie; if(ser->buf==NULL) { ser->buf_len+=size; } else { assert((ser->cur+size) <= (ser->buf+ser->buf_len)); memcpy(ser->cur,buffer,size); ser->cur+=size; } return size; } /******************************************************************** * FUNCTION val_make_serialized_string * * Serializes val_value_t struct to selected formatting mode (xml,json) * * INPUTS: * val == value to serialize * mode == formatting mode for display * FALSE if all nodes should be displayed * RETURNS: * Allocated buffer with the serialized string *********************************************************************/ status_t val_make_serialized_string (val_value_t *val, ncx_display_mode_t mode, xmlChar** str) { serializer_node_t ser; status_t res; FILE* fp; cookie_io_functions_t io_functions; val_value_t* copy_val; io_functions.read=NULL; io_functions.write=(cookie_write_function_t *)writer_fn; io_functions.seek=NULL; io_functions.close=NULL; fp=fopencookie (&ser, "w", io_functions); assert(fp != NULL); ser.buf=NULL; ser.cur=NULL; ser.buf_len=0; copy_val = val123_clone_real(val); /* dry-run figure the required buffer length */ res = val_dump_value_max_w_file(copy_val, 0/*startident*/, NCX_DEF_INDENT/*indent_amount*/, mode, TRUE/*with_meta*/, FALSE/*configonly*/, fp); if(res!=NO_ERR) { return res; } fclose(fp); fp=fopencookie (&ser, "w", io_functions); assert(fp != NULL); ser.buf=(char*)malloc(ser.buf_len+1); ser.cur=ser.buf; ser.buf[ser.buf_len]=0; res = val_dump_value_max_w_file(copy_val, 0/*startident*/, NCX_DEF_INDENT/*indent_amount*/, mode, TRUE/*with_meta*/, FALSE/*configonly*/, fp); fclose(fp); val_free_value(copy_val); *str = ser.buf; return NO_ERR; } /******************************************************************** * FUNCTION val_dump_value_max * * Printf the specified val_value_t struct to * the logfile, or stdout if none set * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to dump * startindent == start indent char count * indent_amount == number of spaces for each indent * dumpmode == logging mode to use * display_mode == formatting mode for display * with_meta == TRUE if metaQ should be printed * FALSE to skip meta data * configonly == TRUE if config only nodes should be displayed * FALSE if all nodes should be displayed *********************************************************************/ void val_dump_value_max (val_value_t *val, int32 startindent, int32 indent_amount, val_dumpvalue_mode_t dumpmode, ncx_display_mode_t display_mode, boolean with_meta, boolean configonly) { FILE *outputfile; unsigned int i; #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif outputfile = log_get_logfile(); if (!outputfile) { outputfile = stdout; } if(display_mode == NCX_DISPLAY_MODE_XML || display_mode == NCX_DISPLAY_MODE_XML_NONS || display_mode == NCX_DISPLAY_MODE_JSON) { val_dump_value_max_w_file(val, startindent, indent_amount, display_mode, with_meta, configonly, outputfile); return; } { dumpfn_t dumpfn, errorfn; indentfn_t indentfn; val_value_t *chval; ncx_lmem_t *listmem; val_idref_t *idref; const xmlChar *prefix; xmlChar *buff; FILE *outputfile; dlq_hdr_t *metaQ; val_value_t *metaval; ncx_btype_t btyp, lbtyp; uint32 len; status_t res; boolean quotes; int32 bump_amount; #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif bump_amount = max(0, indent_amount); if (configonly && !obj_is_config(val->obj)) { return; } switch (dumpmode) { case DUMP_VAL_NONE: return; case DUMP_VAL_STDOUT: dumpfn = log_stdout; errorfn = log_stdout; indentfn = log_stdout_indent; break; case DUMP_VAL_LOG: dumpfn = log_write; errorfn = log_error; indentfn = log_indent; break; case DUMP_VAL_ALT_LOG: dumpfn = log_alt_write; errorfn = log_error; indentfn = log_alt_indent; break; default: SET_ERROR(ERR_INTERNAL_VAL); return; } /* indent and print the val name */ (*indentfn)(startindent); if (display_mode == NCX_DISPLAY_MODE_PLAIN) { if (val->btyp == NCX_BT_EXTERN) { (*dumpfn)("%s (extern=%s) ", (val->name) ? (const char *)val->name : "--", (val->v.fname) ? (const char *)val->v.fname : "--"); } else if (val->btyp == NCX_BT_INTERN) { (*dumpfn)("%s (intern) ", (val->name) ? (const char *)val->name : "--"); } else if (dumpmode == DUMP_VAL_ALT_LOG && !xml_strcmp(val->name, NCX_EL_DATA)) { ; /* skip the name */ } else { (*dumpfn)("%s ", (val->name) ? (const char *)val->name : "--"); } } else { if (display_mode == NCX_DISPLAY_MODE_PREFIX) { prefix = xmlns_get_ns_prefix(val_get_nsid(val)); } else { /* assume the mode is NCX_DISPLAY_MODE_MODULE */ prefix = val_get_mod_name(val); } if (!prefix) { prefix = (const xmlChar *)"invalid"; } if (val->btyp == NCX_BT_EXTERN) { (*dumpfn)("%s:%s (extern=%s) ", prefix, (val->name) ? (const char *)val->name : "--", (val->v.fname) ? (const char *)val->v.fname : "--"); } else if (val->btyp == NCX_BT_INTERN) { (*dumpfn)("%s:%s (intern) ", prefix, (val->name) ? (const char *)val->name : "--"); } else if (dumpmode == DUMP_VAL_ALT_LOG && !xml_strcmp(val->name, NCX_EL_DATA)) { ; /* skip the name */ } else { (*dumpfn)("%s:%s ", prefix, (val->name) ? (const char *)val->name : "--"); } } btyp = val->btyp; /* check if an index clause needs to be printed next */ if (!dlq_empty(&val->indexQ)) { res = val_get_index_string(NULL, NCX_IFMT_CLI, val, NULL, &len); if (res == NO_ERR) { buff = m__getMem(len+1); if (buff) { res = val_get_index_string(NULL, NCX_IFMT_CLI, val, buff, &len); if (res == NO_ERR) { (dumpfn)("%s ", buff); } else { SET_ERROR(res); } m__free(buff); } else { (*errorfn)("\nval: malloc failed for %u bytes", len+1); } } } /* dump the value, depending on the base type */ switch (btyp) { case NCX_BT_NONE: SET_ERROR(ERR_INTERNAL_VAL); break; case NCX_BT_ANYDATA: case NCX_BT_ANYXML: (*dumpfn)("(any)"); break; case NCX_BT_ENUM: if (val->v.enu.name) { if (val_need_quotes(val->v.enu.name)) { (*dumpfn)("\'%s\'", (const char *)val->v.enu.name); } else { (*dumpfn)("%s", (const char *)val->v.enu.name); } } break; case NCX_BT_EMPTY: if (!val->v.boo) { (*dumpfn)("(not set)"); /* should not happen */ } break; case NCX_BT_BOOLEAN: if (val->v.boo) { (*dumpfn)("true"); } else { (*dumpfn)("false"); } break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: switch (dumpmode) { case DUMP_VAL_STDOUT: stdout_num(btyp, &val->v.num); break; case DUMP_VAL_LOG: ncx_printf_num(&val->v.num, btyp); break; case DUMP_VAL_ALT_LOG: ncx_alt_printf_num(&val->v.num, btyp); break; default: SET_ERROR(ERR_INTERNAL_VAL); } break; case NCX_BT_BINARY: (*dumpfn)("binary string, length '%u': ", val->v.binary.ustrlen); for(i=0;((iv.binary.ustrlen) && (i<16));i++) { (*dumpfn)("%02X",(unsigned int)(val->v.binary.ustr[i])); } if(iv.binary.ustrlen) { (*dumpfn)("..."); } break; case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: case NCX_BT_UNION: /* leafref is not dumped in canonical form */ if (VAL_STR(val)) { quotes = val_need_quotes(VAL_STR(val)); if (dumpmode == DUMP_VAL_ALT_LOG && !xml_strcmp(val->name, NCX_EL_DATA)) { quotes = FALSE; } if (quotes) { (*dumpfn)("%c", VAL_QUOTE_CH); } if (val->obj && obj_is_password(val->obj)) { (*dumpfn)("%s", VAL_PASSWORD_STRING); } else { (*dumpfn)("%s", (const char *)VAL_STR(val)); } if (quotes) { (*dumpfn)("%c", VAL_QUOTE_CH); } } break; case NCX_BT_IDREF: idref = VAL_IDREF(val); if (idref->nsid && idref->name) { (*dumpfn)("%s:%s", xmlns_get_ns_prefix(idref->nsid), idref->name); } else if (idref->name) { (*dumpfn)("%s", idref->name); } break; case NCX_BT_SLIST: case NCX_BT_BITS: if (dlq_empty(&val->v.list.memQ)) { (*dumpfn)("{ }"); } else { lbtyp = val->v.list.btyp; (*dumpfn)("{"); for (listmem = (ncx_lmem_t *) dlq_firstEntry(&val->v.list.memQ); listmem != NULL; listmem = (ncx_lmem_t *)dlq_nextEntry(listmem)) { if (startindent >= 0) { (*indentfn)(startindent+bump_amount); } if (typ_is_string(lbtyp)) { if (listmem->val.str) { quotes = val_need_quotes(listmem->val.str); if (quotes) { (*dumpfn)("%c", VAL_QUOTE_CH); } (*dumpfn)("%s ", (const char *)listmem->val.str); if (quotes) { (*dumpfn)("%c", VAL_QUOTE_CH); } } } else if (typ_is_number(lbtyp)) { switch (dumpmode) { case DUMP_VAL_STDOUT: stdout_num(lbtyp, &listmem->val.num); break; case DUMP_VAL_LOG: ncx_printf_num(&listmem->val.num, lbtyp); break; case DUMP_VAL_ALT_LOG: ncx_alt_printf_num(&listmem->val.num, lbtyp); break; default: SET_ERROR(ERR_INTERNAL_VAL); } (*dumpfn)(" "); } else { switch (lbtyp) { case NCX_BT_ENUM: if (listmem->val.enu.name) { (*dumpfn)("%s ", (const char *)listmem->val.enu.name); } break; case NCX_BT_BITS: (*dumpfn)("%s ", (const char *)listmem->val.str); break; case NCX_BT_BOOLEAN: (*dumpfn)("%s ", (listmem->val.boo) ? NCX_EL_TRUE : NCX_EL_FALSE); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } } (*indentfn)(startindent); (*dumpfn)("}"); } break; case NCX_BT_LIST: case NCX_BT_CONTAINER: case NCX_BT_CHOICE: // should not happen case NCX_BT_CASE: // should not happen (*dumpfn)("{"); for (chval = (val_value_t *)dlq_firstEntry(&val->v.childQ); chval != NULL; chval = (val_value_t *)dlq_nextEntry(chval)) { val_dump_value_max(chval, startindent+bump_amount, indent_amount, dumpmode, display_mode, with_meta, configonly); } (*indentfn)(startindent); (*dumpfn)("}"); break; case NCX_BT_EXTERN: (*dumpfn)("{"); (*indentfn)(startindent); switch (dumpmode) { case DUMP_VAL_STDOUT: stdout_extern(val->v.fname); break; case DUMP_VAL_LOG: dump_extern(val->v.fname); break; case DUMP_VAL_ALT_LOG: dump_alt_extern(val->v.fname); break; default: SET_ERROR(ERR_INTERNAL_VAL); } (*indentfn)(startindent); (*dumpfn)("}"); break; case NCX_BT_INTERN: (*dumpfn)("{"); (*indentfn)(startindent); switch (dumpmode) { case DUMP_VAL_STDOUT: stdout_intern(val->v.intbuff); break; case DUMP_VAL_LOG: dump_intern(val->v.intbuff); break; case DUMP_VAL_ALT_LOG: dump_alt_intern(val->v.intbuff); break; default: SET_ERROR(ERR_INTERNAL_VAL); } (*indentfn)(startindent); (*dumpfn)("}"); break; default: (*errorfn)("\nval: illegal btype (%d)", btyp); } /* dump the metadata queue if non-empty */ if (with_meta) { metaQ = val_get_metaQ(val); if (metaQ && !dlq_empty(metaQ)) { if (startindent >= 0) { (*indentfn)(startindent+bump_amount); } (*dumpfn)("%s.metaQ ", val->name); for (metaval = val_get_first_meta(metaQ); metaval != NULL; metaval = val_get_next_meta(metaval)) { val_dump_value_max(metaval, startindent+(2*bump_amount), indent_amount, dumpmode, display_mode, with_meta, configonly); } } } } } /* val_dump_value_max */ /******************************************************************** * FUNCTION val_set_string * * set a generic string using the builtin string typdef * Set an initialized val_value_t as a simple type * namespace set to 0 !!! * use after calling val_new_value * * INPUTS: * val == value to set * valname == name of simple value * valstr == simple value encoded as a string * * OUTPUTS: * *val is filled in if return NO_ERR * RETURNS: * status *********************************************************************/ status_t val_set_string (val_value_t *val, const xmlChar *valname, const xmlChar *valstr) { #ifdef DEBUG if (!val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (valname) { return val_set_simval_str(val, typ_get_basetype_typdef(NCX_BT_STRING), 0, valname, xml_strlen(valname), valstr); } else { return val_set_simval_str(val, typ_get_basetype_typdef(NCX_BT_STRING), 0, NULL, 0, valstr); } } /* val_set_string */ /******************************************************************** * FUNCTION val_set_string2 * * set a string with any typdef * Set an initialized val_value_t as a simple type * * Will check if the string is OK for the typdef! * * INPUTS: * val == value to set * valname == name of simple value * typdef == parm typdef (may be NULL) * valstr == simple value encoded as a string * valstrlen == length of valstr to use * * OUTPUTS: * *val is filled in if return NO_ERR * * RETURNS: * status *********************************************************************/ status_t val_set_string2 (val_value_t *val, const xmlChar *valname, typ_def_t *typdef, const xmlChar *valstr, uint32 valstrlen) { status_t res; xmlChar *temp; #ifdef DEBUG if (!val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (typdef) { val->typdef = typdef; val->btyp = typ_get_basetype(typdef); } else { val->typdef = typ_get_basetype_typdef(NCX_BT_STRING); val->btyp = NCX_BT_STRING; } //val->nsid = 0; switch (val->btyp) { case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: if (valname && !val->name) { if (val->dname) { SET_ERROR(ERR_INTERNAL_VAL); m__free(val->dname); } val->dname = xml_strdup(valname); if (!val->dname) { return ERR_INTERNAL_MEM; } val->name = val->dname; } if (valstr) { VAL_STR(val) = xml_strndup(valstr, valstrlen); } else { VAL_STR(val) = xml_strdup(EMPTY_STRING); } if (!VAL_STR(val)) { res = ERR_INTERNAL_MEM; } else { res = NO_ERR; } break; case NCX_BT_LEAFREF: default: if (valstr) { temp = xml_strndup(valstr, valstrlen); } else { temp = xml_strdup(EMPTY_STRING); } if (temp) { res = val_set_simval(val, typdef, val->nsid, NULL, temp); m__free(temp); } else { res = ERR_INTERNAL_MEM; } } return res; } /* val_set_string2 */ /******************************************************************** * FUNCTION val_reset_empty * * Recast an already initialized value as an NCX_BT_EMPTY * clean a value and set it to empty type * used by yangcli to delete leafs * * INPUTS: * val == value to set * * OUTPUTS: * *val is filled in if return NO_ERR * RETURNS: * status *********************************************************************/ status_t val_reset_empty (val_value_t *val) { #ifdef DEBUG if (!val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif return val_set_simval(val, typ_get_basetype_typdef(NCX_BT_EMPTY), val_get_nsid(val), val->name, NULL); } /* val_reset_empty */ /******************************************************************** * FUNCTION val_set_simval * * set any simple value with any typdef * Set an initialized val_value_t as a simple type * * INPUTS: * val == value to set * typdef == typdef of expected type * nsid == namespace ID of this field * valname == name of simple value * valstr == simple value encoded as a string * * OUTPUTS: * *val is filled in if return NO_ERR * RETURNS: * status *********************************************************************/ status_t val_set_simval (val_value_t *val, typ_def_t *typdef, xmlns_id_t nsid, const xmlChar *valname, const xmlChar *valstr) { #ifdef DEBUG if (!val || !typdef) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (valname) { return val_set_simval_str(val, typdef, nsid, valname, xml_strlen(valname), valstr); } else { return val_set_simval_str(val, typdef, nsid, NULL, 0, valstr); } } /* val_set_simval */ /******************************************************************** * FUNCTION val_set_simval_str * * set any simple value with any typdef, and a counted string * Set an initialized val_value_t as a simple type * * The string value will be converted to a value * struct format and checked against the provided typedef * * Handles the following data types: * * NCX_BT_INT8 * NCX_BT_INT16 * NCX_BT_INT32 * NCX_BT_INT64 * NCX_BT_UINT8 * NCX_BT_UINT16 * NCX_BT_UINT32 * NCX_BT_UINT64 * NCX_BT_DECIMAL64 * NCX_BT_FLOAT64 * NCX_BT_BINARY * NCX_BT_STRING * NCX_BT_INSTANCE_ID * NCX_BT_ENUM * NCX_BT_EMPTY * NCX_BT_BOOLEAN * NCX_BT_SLIST * NCX_BT_BITS * NCX_BT_UNION * NCX_BT_LEAFREF * NCX_BT_IDREF * * INPUTS: * val == value to set * typdef == typdef of expected type * nsid == namespace ID of this field * valname == name of simple value * valnamelen == length of name string to compare * valstr == simple value encoded as a string * * OUTPUTS: * *val is filled in if return NO_ERR * RETURNS: * status *********************************************************************/ status_t val_set_simval_str (val_value_t *val, typ_def_t *typdef, xmlns_id_t nsid, const xmlChar *valname, uint32 valnamelen, const xmlChar *valstr) { const xmlChar *localname; ncx_identity_t *identity; obj_template_t *leafobj, *objroot; xpath_pcb_t *xpathpcb; status_t res; uint32 ulen; xmlns_id_t qname_nsid; boolean startsimple; xpath_source_t xpath_source; #ifdef DEBUG if (!val || !typdef) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (val->btyp != NCX_BT_NONE) { startsimple = typ_is_simple(val->btyp); } else { startsimple = TRUE; } /* clean the old value even if it was ANY */ clean_value(val, FALSE); res = NO_ERR; val->btyp = typ_get_basetype(typdef); if (!startsimple) { /* clear out the childQ so it does not appear * to be a bogus string that needs to be cleaned */ memset(&val->v.childQ, 0x0, sizeof(dlq_hdr_t)); } if (!(val->btyp == NCX_BT_INSTANCE_ID || typ_is_schema_instance_string(typdef) || typ_is_xpath_string(typdef))) { res = val_simval_ok(typdef, valstr); if (res != NO_ERR) { return res; } } /* only set name if it is not already set */ if (!val->name && valname) { val->dname = xml_strndup(valname, valnamelen); if (!val->dname) { return ERR_INTERNAL_MEM; } val->name = val->dname; } /* set the object to the generic string if not set */ if (!val->obj) { val->obj = ncx_get_gen_string(); } /* set these fields even if already set by * val_init_from_template */ val->nsid = nsid; val->typdef = typdef; xpath_source = XP_SRC_INSTANCEID; /* convert the value string, if any */ switch (val->btyp) { case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_FLOAT64: if (valstr && *valstr) { res = ncx_convert_num(valstr, NCX_NF_NONE, val->btyp, &val->v.num); } else { res = ERR_NCX_EMPTY_VAL; } break; case NCX_BT_DECIMAL64: if (valstr && *valstr) { res = ncx_convert_dec64(valstr, NCX_NF_NONE, typ_get_fraction_digits(val->typdef), &val->v.num); } else { res = ERR_NCX_EMPTY_VAL; } break; case NCX_BT_BINARY: ulen = 0; if (valstr && *valstr) { ulen = xml_strlen(valstr); } if (!ulen) { break; } /* allocate a binary string as big as the input * text string, even though it will be about 25% too big */ val->v.binary.ustr = m__getMem(ulen+1); if (!val->v.binary.ustr) { res = ERR_INTERNAL_MEM; } else { /* really a test tool only for binary strings * entered at the CLI; use @foo.jpg for * raw input of binary files in var_get_script_val */ #if 0 memcpy(val->v.binary.ustr, valstr, ulen); val->v.binary.ubufflen = ulen+1; val->v.binary.ustrlen = ulen; #else val->v.binary.ubufflen = ulen+1; res = b64_decode(valstr, ulen, val->v.binary.ustr, val->v.binary.ubufflen, &val->v.binary.ustrlen); #endif } break; case NCX_BT_ANYDATA: case NCX_BT_ANYXML: val->btyp = NCX_BT_STRING; val->typdef = typ_get_basetype_typdef(NCX_BT_STRING); if (valstr) { VAL_STR(val) = xml_strdup(valstr); } else { VAL_STR(val) = xml_strdup(EMPTY_STRING); } if (!VAL_STR(val)) { res = ERR_INTERNAL_MEM; } break; case NCX_BT_LEAFREF: if (valstr) { VAL_STR(val) = xml_strdup(valstr); } else { VAL_STR(val) = xml_strdup(EMPTY_STRING); } if (!VAL_STR(val)) { res = ERR_INTERNAL_MEM; } break; case NCX_BT_STRING: if (val->obj && obj_is_schema_instance_string(val->obj)) { xpath_source = XP_SRC_SCHEMA_INSTANCEID; } else { if (valstr) { VAL_STR(val) = xml_strdup(valstr); } else { VAL_STR(val) = xml_strdup(EMPTY_STRING); } if (!VAL_STR(val)) { res = ERR_INTERNAL_MEM; } else if (typ_is_xpath_string(val->typdef) || (val->obj && obj_is_xpath_string(val->obj))) { xpathpcb = xpath_new_pcb(VAL_STR(val), NULL); if (!xpathpcb) { res = ERR_INTERNAL_MEM; } else { res = xpath1_parse_expr(NULL, NULL, xpathpcb, XP_SRC_YANG); if (res == NO_ERR) { objroot = ncx_get_gen_root(); res = xpath1_validate_expr(objroot->tkerr.mod, objroot, xpathpcb); } if (val->xpathpcb) { xpath_free_pcb(val->xpathpcb); } val->xpathpcb = xpathpcb; } } break; } /* fall through if this is an ncx:schema-instance string */ case NCX_BT_INSTANCE_ID: if (valstr) { VAL_STR(val) = xml_strdup(valstr); } else { VAL_STR(val) = xml_strdup(EMPTY_STRING); } if (!VAL_STR(val)) { res = ERR_INTERNAL_MEM; } else { xpathpcb = xpath_new_pcb(VAL_STR(val), NULL); if (!xpathpcb) { res = ERR_INTERNAL_MEM; } else { res = xpath_yang_parse_path(NULL, NULL, xpath_source, xpathpcb); if (res == NO_ERR) { leafobj = NULL; res = xpath_yang_validate_path(NULL, ncx_get_gen_root(), xpathpcb, (xpath_source == XP_SRC_INSTANCEID) ? FALSE : TRUE, &leafobj); } if (val->xpathpcb) { xpath_free_pcb(val->xpathpcb); } val->xpathpcb = xpathpcb; } } break; case NCX_BT_IDREF: { const typ_idref_t *idref; idref = typ_get_cidref(typdef); assert(idref); res=val123_parse_idref_ex (NULL/*mod*/, valstr, idref, &identity); if (res == NO_ERR) { val->v.idref.identity = identity; val->v.idref.nsid = identity->mod->nsid; val->v.idref.name = xml_strdup(identity->name); if (!val->v.idref.name) { res = ERR_INTERNAL_MEM; } } } break; case NCX_BT_ENUM: res = ncx_set_enum(valstr, &val->v.enu); break; case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: /* if supplied, match the flag name against the supplied value */ if (valstr) { if (!xml_strcmp(NCX_EL_TRUE, valstr)) { val->v.boo = TRUE; } else if (!xml_strcmp(NCX_EL_FALSE, valstr)) { val->v.boo = FALSE; } else if (!xml_strncmp(valstr, valname, valnamelen)) { val->v.boo = TRUE; } else { res = ERR_NCX_INVALID_VALUE; } } else { /* name indicates presence, so set val to TRUE */ val->v.boo = TRUE; } break; case NCX_BT_SLIST: case NCX_BT_BITS: res = ncx_set_strlist(valstr, &val->v.list); break; case NCX_BT_UNION: /* set as generic string -- parser as other end will * match against actual union types */ val->btyp = NCX_BT_STRING; VAL_STR(val) = xml_strdup(valstr); if (!VAL_STR(val)) { res = ERR_INTERNAL_MEM; } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); val->btyp = NCX_BT_NONE; } return res; } /* val_set_simval_str */ /******************************************************************** * FUNCTION val_make_simval * * Create and set a val_value_t as a simple type * same as val_set_simval, but malloc the value first * * INPUTS: * typdef == typdef of expected type * nsid == namespace ID of this field * valname == name of simple value * valstr == simple value encoded as a string * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced and filled in val_value_t struct * NULL if some error *********************************************************************/ val_value_t * val_make_simval (typ_def_t *typdef, xmlns_id_t nsid, const xmlChar *valname, const xmlChar *valstr, status_t *res) { val_value_t *val; if (!typdef || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } val = val_new_value(); if (!val) { *res = ERR_INTERNAL_MEM; return NULL; } if (valname) { *res = val_set_simval_str(val, typdef, nsid, valname, xml_strlen(valname), valstr); } else { *res = val_set_simval_str(val, typdef, nsid, NULL, 0, valstr); } return val; } /* val_make_simval */ /******************************************************************** * FUNCTION val_make_string * * Malloc and set a val_value_t as a generic NCX_BT_STRING * namespace set to 0 !!! * * INPUTS: * nsid == namespace ID to use * valname == name of simple value * valstr == simple value encoded as a string * * RETURNS: * malloced val struct filled in; NULL if malloc or strdup failed *********************************************************************/ val_value_t * val_make_string (xmlns_id_t nsid, const xmlChar *valname, const xmlChar *valstr) { val_value_t *val; status_t res; val = val_new_value(); if (!val) { return NULL; } res = val_set_string(val, valname, valstr); if (res != NO_ERR) { val_free_value(val); val = NULL; } else { val->nsid = nsid; } return val; } /* val_make_string */ /******************************************************************** * FUNCTION val_merge * * Merge src val into dest val (! MUST be same type !) * Any meta vars in src are NOT merged into dest!!! * * This function is not used to merge complex objects * !!! For typ_is_simple() only !!! * * The source may need to be copied in order to merge, for some basetypes * The dest is not altered unless the source value can be merged completed. * * INPUTS: * src == val to merge from * dest == val to merge into !!! old value is not saved !!! * * RETURNS: * status *********************************************************************/ status_t val_merge (const val_value_t *src, val_value_t *dest) { #ifdef DEBUG if (!src || !dest) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!typ_is_simple(src->btyp) || !typ_is_simple(dest->btyp)) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif status_t res = NO_ERR; ncx_btype_t btyp = dest->btyp; ncx_iqual_t iqual = typ_get_iqualval_def(dest->typdef); /* the mergetype is only set to NCX_MERGE_SORT for type bits * it is NCX_MERGE_NONE for all other data types * the value NCX_MERGE_FIRST is never used */ ncx_merge_t mergetyp = typ_get_mergetype(dest->typdef); if (mergetyp == NCX_MERGE_NONE) { mergetyp = NCX_MERGE_LAST; } boolean dupsok = FALSE; ncx_list_t *list_copy = NULL; switch (iqual) { case NCX_IQUAL_1MORE: case NCX_IQUAL_ZMORE: /* duplicates not allowed in leaf lists * leave the current value in place */ res = SET_ERROR(ERR_NCX_DUP_ENTRY); break; case NCX_IQUAL_ONE: case NCX_IQUAL_OPT: /* need to replace the current value or merge a list, etc. */ switch (btyp) { case NCX_BT_ENUM: case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: case NCX_BT_IDREF: res = merge_simple(btyp, src, dest); break; case NCX_BT_UNION: res = SET_ERROR(ERR_INTERNAL_VAL); break; case NCX_BT_SLIST: case NCX_BT_BITS: if (btyp == NCX_BT_SLIST) { dupsok = val_duplicates_allowed(dest); } list_copy = ncx_new_list(dest->btyp); if (list_copy) { res = ncx_copy_list(&src->v.list, list_copy); if (res != NO_ERR) { ncx_free_list(list_copy); } } else { res = ERR_INTERNAL_MEM; } if (res == NO_ERR) { ncx_merge_list(list_copy, &dest->v.list, mergetyp, dupsok); ncx_free_list(list_copy); } break; case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_CONTAINER: case NCX_BT_LIST: case NCX_BT_CHOICE: case NCX_BT_CASE: res = SET_ERROR(ERR_INTERNAL_VAL); break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* copy the editvars struct to the leaf */ /*** REMOVING SINCE SOURCE IS SAVED ***/ //if (res == NO_ERR) { // res = copy_editvars(src, dest); //} break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* set or clear the set-by-default flag; * normalize VAL_FL_WITHDEF by converting to VAL_FL_DEFSET here */ if (res == NO_ERR) { if (val_set_by_default(src)) { dest->flags |= VAL_FL_DEFSET; } else { dest->flags &= ~VAL_FL_DEFSET; } dest->flags &= ~VAL_FL_WITHDEF; } return res; } /* val_merge */ /******************************************************************** * FUNCTION val_clone * * Clone a specified val_value_t struct and sub-trees * * INPUTS: * val == value to clone * * RETURNS: * clone of val, or NULL if a malloc failure *********************************************************************/ val_value_t * val_clone (const val_value_t *val) { status_t res; #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return clone_test(val, NULL, TRUE, &res); } /* val_clone */ /******************************************************************** * FUNCTION val_clone2 * * Clone a specified val_value_t struct and sub-trees * but not the editvars * * INPUTS: * val == value to clone * * RETURNS: * clone of val, or NULL if a malloc failure *********************************************************************/ val_value_t * val_clone2 (const val_value_t *val) { status_t res; #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return clone_test(val, NULL, FALSE, &res); } /* val_clone2 */ /******************************************************************** * FUNCTION val_clone_config_data * * Clone a specified val_value_t struct and sub-trees * Filter with the val_is_config_data callback function * pass in a config node, such as root * will call clone_test with the val_is_config_data * callbacck function * * DOES NOT CLONE THE EDITVARS!!!! * * INPUTS: * val == config data value to clone * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * clone of val, or NULL if a malloc failure *********************************************************************/ val_value_t * val_clone_config_data (const val_value_t *val, status_t *res) { #ifdef DEBUG if (!val || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return clone_test(val, val_is_config_data, FALSE, res); } /* val_clone_config_data */ /******************************************************************** * FUNCTION val_replace * * Replace a specified val_value_t struct and sub-trees * !!! this can be destructive to the source 'val' parameter !!!! * * INPUTS: * val == value to clone from * copy == address of value to replace * * OUTPUTS: * *copy has been deleted and reforms with the contents of 'val' * * RETURNS: * status *********************************************************************/ status_t val_replace (val_value_t *val, val_value_t *copy) { xmlChar *buffer; uint32 len; status_t res; #ifdef DEBUG if (!val || !copy) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; if (val->btyp == NCX_BT_EXTERN) { clean_value(copy, TRUE); val_init_from_template(copy, val->obj); copy->btyp = NCX_BT_EXTERN; copy->v.fname = val->v.fname; val->v.fname = NULL; res = copy_editvars(val, copy); return res; } else if (!typ_is_simple(val->btyp)) { clean_value(copy, TRUE); val_init_from_template(copy, val->obj); val_move_children(val, copy); val_set_name(copy, val->name, xml_strlen(val->name)); copy->nsid = val->nsid; if (copy->btyp == NCX_BT_LIST) { res = val_gen_index_chain(copy->obj, copy); } if (res == NO_ERR) { res = copy_editvars(val, copy); } return res; } buffer = NULL; if (typ_is_string(val->btyp) && typ_is_string(copy->btyp)) { if (copy->v.str) { ncx_clean_str(©->v.str); } copy->v.str = val->v.str; val->v.str = NULL; copy->btyp = val->btyp; } else { res = val_sprintf_simval_nc(NULL, val, &len); if (res == NO_ERR) { buffer = m__getMem(len+1); if (!buffer) { return ERR_INTERNAL_MEM; } res = val_sprintf_simval_nc(buffer, val, &len); if (res == NO_ERR) { res = val_set_simval(copy, val->typdef, val->nsid, val->name, buffer); } } if (buffer) { m__free(buffer); } } if (res == NO_ERR) { res = copy_editvars(val, copy); } copy->flags &= ~VAL_FL_DEFSET; return res; } /* val_replace */ /******************************************************************** * FUNCTION val_replace_str * * Replace a specified val_value_t struct with a string type * * INPUTS: * str == value to clone from; may be NULL * stringlen == number of chars to use from str, if not NULL * copy == address of value to replace * * OUTPUTS: * *copy has been deleted and reforms with the contents of 'val' * * RETURNS: * status *********************************************************************/ status_t val_replace_str (const xmlChar *str, uint32 stringlen, val_value_t *copy) { val_value_t *strval; xmlChar *dumstr; status_t res; #ifdef DEBUG if (copy == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif strval = val_make_string(copy->nsid, copy->name, (const xmlChar *)"dummy"); if (strval == NULL) { return ERR_INTERNAL_MEM; } dumstr = xml_strndup(str, stringlen); if (dumstr == NULL) { val_free_value(strval); return ERR_INTERNAL_MEM; } m__free(strval->v.str); strval->v.str = dumstr; res = val_replace(strval, copy); val_free_value(strval); return res; } /* val_replace_str */ /******************************************************************** * FUNCTION val_add_meta * * Add a meta value node to a parent value node * Simply makes a new last meta!!! * * INPUTS: * child == node to store in the parent * parent == complex value node with a childQ * *********************************************************************/ void val_add_meta (val_value_t *meta, val_value_t *parent) { #ifdef DEBUG if (meta == NULL || parent == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif dlq_enque(meta, &parent->metaQ); } /* val_add_meta */ /******************************************************************** * FUNCTION val_add_child * * Add a child value node to a parent value node * Simply makes a new last child!!! * Does not check siblings!!! * Relies on val_set_canonical_order * * To modify existing extries, use val_add_child_sorted instead!! * * INPUTS: * child == node to store in the parent * parent == complex value node with a childQ * *********************************************************************/ void val_add_child (val_value_t *child, val_value_t *parent) { #ifdef DEBUG if (child == NULL || parent == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif child->parent = parent; dlq_enque(child, &parent->v.childQ); } /* val_add_child */ /******************************************************************** * FUNCTION val_add_child_sorted * * Add a child value node to a parent value node * in the proper place * * INPUTS: * child == node to store in the parent * parent == complex value node with a childQ * *********************************************************************/ void val_add_child_sorted (val_value_t *child, val_value_t *parent) { assert( child && "child is NULL!" ); assert( parent && "parent is NULL!" ); child->parent = parent; dlq_hdr_t *childQ = &parent->v.childQ; /* check new first entry */ if (dlq_empty(childQ)) { dlq_enque(child, childQ); return; } val_value_t *curval = NULL; obj_template_t *newobj = child->obj; xmlns_id_t parentid = val_get_nsid(parent); xmlns_id_t childid = val_get_nsid(child); boolean sysorder = obj_is_system_ordered(newobj); int ret = 0; /* The current set of sibling nodes needs to * be searched to determine where to insert this child */ if (obj_is_root(parent->obj)) { /* adding objects to the root is different; need * to use alphabetical order, not schema order * since submodules blur the top-level object * order within a module namespace */ for (curval = val_get_first_child(parent); curval != NULL; curval = val_get_next_child(curval)) { /* check same type of sibling cornercase * should only happen if the child is * type list or leaf-list */ if (newobj == curval->obj) { /* make a new sorted or last one of these entries */ boolean syssorted = ncx_get_system_sorted(); boolean done = FALSE; while (!done) { if (sysorder && syssorted) { if (newobj->objtype == OBJ_TYP_LIST) { ret = val_index_compare(child, curval); } else { ret = val_compare(child, curval); } if (ret < 0) { dlq_insertAhead(child, curval); return; } } val_value_t *nextchild = val_get_next_child(curval); if (nextchild == NULL || nextchild->obj != child->obj) { done = TRUE; } else { curval = nextchild; } } dlq_insertAfter(child, curval); return; } ret = xml_strcmp(child->name, curval->name); if (ret < 0) { dlq_insertAhead(child, curval); return; } else if (ret == 0) { ret = xml_strcmp(val_get_mod_name(child), val_get_mod_name(curval)); if (ret < 0) { dlq_insertAhead(child, curval); return; /* same name, insert in module alphabetical order */ } } } /* make new last entry */ dlq_enque(child, childQ); } else if (parent->obj->objtype == OBJ_TYP_ANYXML) { /* there is no schema order to check, so see if this * child already exists */ curval = val_find_child(parent, val_get_mod_name(child), child->name); if (curval != NULL) { /* make new last instance of this child node */ val_value_t *saveval = NULL; while (curval != NULL) { saveval = curval; curval = val_find_next_child(parent, val_get_mod_name(child), child->name, curval); } dlq_insertAfter(child, saveval); } else { /* make new last child; first one of these */ dlq_enque(child, childQ); } } else { /* normal container or list */ for (curval = val_get_first_child(parent); curval != NULL; curval = val_get_next_child(curval)) { /* check same type of sibling cornercase * should only happen if the child is * type list or leaf-list */ if (newobj == curval->obj) { /* make a new last one of these entries */ boolean syssorted = ncx_get_system_sorted(); boolean done = FALSE; while (!done) { if (sysorder && syssorted) { if (newobj->objtype == OBJ_TYP_LIST) { ret = val_index_compare(child, curval); } else { ret = val_compare(child, curval); } if (ret < 0) { dlq_insertAhead(child, curval); return; } } val_value_t *nextchild = val_get_next_child(curval); if (nextchild == NULL || nextchild->obj != child->obj) { done = TRUE; } else { curval = nextchild; } } /* make a new last instance of this node type */ dlq_insertAfter(child, curval); return; } /* simple test; since native children * will be before external augmented children; * any native node will insert ahead of such * an augment node */ if (val_get_nsid(curval) != parentid && childid == parentid) { dlq_insertAhead(child, curval); return; } /* new node and current node are different so * check if the current object is after the * new object in the schema order. If so, * then insert ahead of this node * * the object siblings are not numbered, so * a linear search is used here */ obj_template_t *testobj = obj_next_child_deep(child->obj); for (; testobj != NULL; testobj = obj_next_child_deep(testobj)) { if (testobj == curval->obj) { /* insert child ahead of this node * which occurs after it in schema order */ dlq_insertAhead(child, curval); return; } } } /* make a new last entry */ dlq_enque(child, childQ); } } /* val_add_child_sorted */ /******************************************************************** * FUNCTION val_insert_child * * Insert a child value node to a parent value node * * INPUTS: * child == node to store in the parent * current == current node to insert after; * NULL to make new first entry * parent == complex value node with a childQ * *********************************************************************/ void val_insert_child (val_value_t *child, val_value_t *current, val_value_t *parent) { #ifdef DEBUG if (child == NULL || parent == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif child->parent = parent; if (current) { dlq_insertAfter(child, current); } else { val_add_child_sorted(child, parent); } } /* val_insert_child */ /******************************************************************** * FUNCTION val_remove_child * * Remove a child value node from its parent value node * * INPUTS: * child == node to store in the parent * *********************************************************************/ void val_remove_child (val_value_t *child) { #ifdef DEBUG if (!child) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif dlq_remove(child); child->parent = NULL; } /* val_remove_child */ /******************************************************************** * FUNCTION val_swap_child * * Swap a child value node with a current value node * * INPUTS: * newchild == node to store in the place of the curchild * curchild == node to remove from the parent * *********************************************************************/ void val_swap_child (val_value_t *newchild, val_value_t *curchild) { #ifdef DEBUG if (!newchild || !curchild) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif newchild->parent = curchild->parent; newchild->getcb = curchild->getcb; dlq_swap(newchild, curchild); curchild->parent = NULL; } /* val_swap_child */ /******************************************************************** * FUNCTION val_first_child_match * * Get the first instance of the corresponding child node * * INPUTS: * parent == parent value to check * child == child value to find (e.g., from a NETCONF PDU) * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ val_value_t * val_first_child_match (val_value_t *parent, val_value_t *child) { val_value_t *val; #ifdef DEBUG if (!parent || !child) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (!typ_has_children(parent->btyp)) { return NULL; } for (val = (val_value_t *)dlq_firstEntry(&parent->v.childQ); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } /* check the node if the QName matches */ if (val->nsid == child->nsid && !xml_strcmp(val->name, child->name)) { if (val->btyp == NCX_BT_LIST) { /* match the instance identifiers, if any */ if (val_index_match(child, val)) { return val; } } else if (val->obj->objtype == OBJ_TYP_LEAF_LIST) { if (val->btyp == child->btyp) { /* find the leaf-list with the same value */ if (!val_compare(val, child)) { return val; } } else { /* match any value; if this is a subtree * filter test, it is not for a content match * node */ return val; } } else { /* can only be this one instance */ return val; } } } return NULL; } /* val_first_child_match */ /******************************************************************** * FUNCTION val_next_child_match * * Get the next instance of the corresponding child node * * INPUTS: * parent == parent value to check * child == child value to find (e.g., from a NETCONF PDU) * curmatch == current child of parent that matched * * RETURNS: * pointer to the next child match if found or NULL if not found *********************************************************************/ val_value_t * val_next_child_match (val_value_t *parent, val_value_t *child, val_value_t *curmatch) { val_value_t *val; #ifdef DEBUG if (!parent || !child || !curmatch) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (!typ_has_children(parent->btyp)) { return NULL; } for (val = (val_value_t *)dlq_nextEntry(curmatch); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } /* check the node if the QName matches */ if (val->nsid == child->nsid && !xml_strcmp(val->name, child->name)) { if (val->btyp == NCX_BT_LIST) { /* match the instance identifiers, if any */ if (val_index_match(child, val)) { return val; } } else if (val->obj->objtype == OBJ_TYP_LEAF_LIST) { if (val->btyp == child->btyp) { /* find the leaf-list with the same value */ if (!val_compare(val, child)) { return val; } } else { /* match any value; if this is a subtree * filter test, it is not for a content match * node */ return val; } } else { /* can only be this one instance */ return val; } } } return NULL; } /* val_next_child_match */ /******************************************************************** * FUNCTION val_get_first_child * * Get the child node * * INPUTS: * parent == parent complex type to check * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ val_value_t * val_get_first_child (const val_value_t *parent) { #ifdef DEBUG if (!parent) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (!typ_has_children(parent->btyp)) { return NULL; } val_value_t *val = (val_value_t *)dlq_firstEntry(&parent->v.childQ); for (; val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } return val; } return NULL; } /* val_get_first_child */ /******************************************************************** * FUNCTION val_get_next_child * * Get the next child node * * INPUTS: * curchild == current child node * * RETURNS: * pointer to the next child if found or NULL if not found *********************************************************************/ val_value_t * val_get_next_child (const val_value_t *curchild) { #ifdef DEBUG if (!curchild) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif val_value_t *val = (val_value_t *)dlq_nextEntry(curchild); for (; val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } return val; } return NULL; } /* val_get_next_child */ /******************************************************************** * FUNCTION val_find_child * * Find the first instance of the specified child node * * INPUTS: * parent == parent complex type to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * childname == name of child node to find * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ val_value_t * val_find_child (const val_value_t *parent, const xmlChar *modname, const xmlChar *childname) { val_value_t *val; #ifdef DEBUG if (!parent || !childname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (!typ_has_children(parent->btyp)) { return NULL; } for (val = (val_value_t *)dlq_firstEntry(&parent->v.childQ); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } if (modname && xml_strcmp(modname, val_get_mod_name(val))) { continue; } if (!xml_strcmp(val->name, childname)) { return val; } } return NULL; } /* val_find_child */ /******************************************************************** * FUNCTION val_find_child_que * * Find the first instance of the specified child node in the * specified child Q * * INPUTS: * parent == parent complex type to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * childname == name of child node to find * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ val_value_t * val_find_child_que (const dlq_hdr_t *childQ, const xmlChar *modname, const xmlChar *childname) { val_value_t *val; #ifdef DEBUG if (!childQ || !childname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif for (val = (val_value_t *)dlq_firstEntry(childQ); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } if (modname && xml_strcmp(modname, val_get_mod_name(val))) { continue; } if (!xml_strcmp(val->name, childname)) { return val; } } return NULL; } /* val_find_child_que */ /******************************************************************** * FUNCTION val_match_child * * Match the first instance of the specified child node * * INPUTS: * parent == parent complex type to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * childname == name of child node to find * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ val_value_t * val_match_child (const val_value_t *parent, const xmlChar *modname, const xmlChar *childname) { val_value_t *val; #ifdef DEBUG if (!parent || !childname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (!typ_has_children(parent->btyp)) { return NULL; } for (val = (val_value_t *)dlq_firstEntry(&parent->v.childQ); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } if (modname && xml_strcmp(modname, val_get_mod_name(val))) { continue; } if (!xml_strncmp(val->name, childname, xml_strlen(childname))) { return val; } } return NULL; } /* val_match_child */ /******************************************************************** * FUNCTION val_find_next_child * * Find the next instance of the specified child node * * INPUTS: * parent == parent complex type to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * childname == name of child node to find * curchild == current child of this object type to start search * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ val_value_t * val_find_next_child (const val_value_t *parent, const xmlChar *modname, const xmlChar *childname, const val_value_t *curchild) { val_value_t *val; #ifdef DEBUG if (!parent || !childname) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (!typ_has_children(parent->btyp)) { return NULL; } for (val = (val_value_t *)dlq_nextEntry(curchild); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } if (modname && xml_strcmp(modname, val_get_mod_name(val))) { continue; } if (!xml_strcmp(val->name, childname)) { return val; } } return NULL; } /* val_find_next_child */ /******************************************************************** * FUNCTION val_first_child_name * * Get the first corresponding child node instance, by name * find first -- really for resolve index function * * INPUTS: * parent == parent complex type to check * name == child name to find * * RETURNS: * pointer to the FIRST match if found, or NULL if not found *********************************************************************/ val_value_t * val_first_child_name (val_value_t *parent, const xmlChar *name) { val_value_t *val; #ifdef DEBUG if (!parent || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (!typ_has_children(parent->btyp)) { return NULL; } for (val = (val_value_t *)dlq_firstEntry(&parent->v.childQ); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } /* check the node if the name matches */ if (!xml_strcmp(val->name, name)) { return val; } } return NULL; } /* val_first_child_name */ /******************************************************************** * FUNCTION val_first_child_qname * * Get the first corresponding child node instance, by QName * * INPUTS: * parent == parent complex type to check * nsid == namespace ID to use, 0 for any * name == child name to find * * RETURNS: * pointer to the first match if found, or NULL if not found *********************************************************************/ val_value_t * val_first_child_qname (val_value_t *parent, xmlns_id_t nsid, const xmlChar *name) { val_value_t *val; #ifdef DEBUG if (!parent || !name) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (!typ_has_children(parent->btyp)) { return NULL; } for (val = (val_value_t *)dlq_firstEntry(&parent->v.childQ); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } if (!xmlns_ids_equal(nsid, val->nsid)) { continue; } /* check the node if the name matches */ if (!xml_strcmp(val->name, name)) { return val; } } return NULL; } /* val_first_child_qname */ /******************************************************************** * FUNCTION val_next_child_qname * * Get the next corresponding child node instance, by QName * * INPUTS: * parent == parent complex type to check * nsid == namespace ID to use, 0 for any * name == child name to find * curchild == current child match to start from * * RETURNS: * pointer to the next match if found, or NULL if not found *********************************************************************/ val_value_t * val_next_child_qname (val_value_t *parent, xmlns_id_t nsid, const xmlChar *name, val_value_t *curchild) { val_value_t *val; #ifdef DEBUG if (!parent || !name || !curchild) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (!typ_has_children(parent->btyp)) { return NULL; } for (val = (val_value_t *)dlq_nextEntry(curchild); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } if (!xmlns_ids_equal(nsid, val->nsid)) { continue; } /* check the node if the name matches */ if (!xml_strcmp(val->name, name)) { return val; } } return NULL; } /* val_next_child_qname */ /******************************************************************** * FUNCTION val_first_child_string * * find first name value pair * Get the first corresponding child node instance, by name * and by string value. * Child node must be a base type of * NCX_BT_STRING * NCX_BT_INSTANCE_ID * NCX_BT_LEAFREF * * INPUTS: * parent == parent complex type to check * name == child name to find * strval == string value to find * RETURNS: * pointer to the FIRST match if found, or NULL if not found *********************************************************************/ val_value_t * val_first_child_string (val_value_t *parent, const xmlChar *name, const xmlChar *strval) { val_value_t *val; #ifdef DEBUG if (!parent || !name || !strval) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (!typ_has_children(parent->btyp)) { return NULL; } for (val = (val_value_t *)dlq_firstEntry(&parent->v.childQ); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } /* check the node if the name matches */ if (!xml_strcmp(val->name, name)) { if (typ_is_string(val->btyp)) { if (!xml_strcmp(val->v.str, strval)) { return val; } } else { /* requested child node is wrong type */ return NULL; } } } return NULL; } /* val_first_child_string */ /******************************************************************** * FUNCTION val_child_cnt * * Get the number of child nodes present * get number of child nodes present -- for choice checking * * INPUTS: * parent == parent complex type to check * * RETURNS: * the number of child nodes found *********************************************************************/ uint32 val_child_cnt (val_value_t *parent) { val_value_t *val; uint32 cnt; #ifdef DEBUG if (!parent) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif if (!typ_has_children(parent->btyp)) { return 0; } cnt = 0; for (val = (val_value_t *)dlq_firstEntry(&parent->v.childQ); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } cnt++; } return cnt; } /* val_child_cnt */ /******************************************************************** * FUNCTION val_child_inst_cnt * * Get the corresponding child instance count by name * get instance count -- for instance qualifer checking * * INPUTS: * parent == parent complex type to check * modname == module name defining the child (may be NULL) * name == child name to find and count * * RETURNS: * the instance count *********************************************************************/ uint32 val_child_inst_cnt (const val_value_t *parent, const xmlChar *modname, const xmlChar *name) { const val_value_t *val; uint32 cnt; #ifdef DEBUG if (!parent || !name) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif if (!typ_has_children(parent->btyp)) { return 0; } cnt = 0; for (val = (const val_value_t *)dlq_firstEntry(&parent->v.childQ); val != NULL; val = (const val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } if (modname && xml_strcmp(modname, val_get_mod_name(val))) { continue; } /* check the node if the name matches */ if (!xml_strcmp(val->name, name)) { cnt++; } } return cnt; } /* val_child_inst_cnt */ /******************************************************************** * FUNCTION val_find_all_children * * Find all occurances of the specified node(s) * within the children of the current node. * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * INPUTS: * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == node to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of child node to find * == NULL to match any child name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if childname == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ boolean val_find_all_children (val_walker_fn_t walkerfn, void *cookie1, void *cookie2, val_value_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode) { val_value_t *val, *useval; boolean fnresult, fncalled; status_t res; #ifdef DEBUG if (!startnode) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (!typ_has_children(startnode->btyp)) { return FALSE; } if (val_is_virtual(startnode)) { res = NO_ERR; useval = cache_virtual_value(NULL, startnode, &res); if (useval == NULL) { return FALSE; } } else { useval = startnode; } for (val = (val_value_t *)dlq_firstEntry(&useval->v.childQ); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } fnresult = process_one_valwalker(walkerfn, cookie1, cookie2, val, modname, name, configonly, textmode, &fncalled); if (!fnresult) { return FALSE; } } return TRUE; } /* val_find_all_children */ /******************************************************************** * FUNCTION val_find_all_ancestors * * Find all the ancestor instances of the specified node * within the path to root from the current node; * use the filter criteria provided * * INPUTS: * * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == node to start search at * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of ancestor node to find * == NULL to match any node name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if name == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * orself == TRUE if axis is really ancestor-or-self * FALSE if axis is ancestor * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ boolean val_find_all_ancestors (val_walker_fn_t walkerfn, void *cookie1, void *cookie2, val_value_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode, boolean orself) { val_value_t *val; boolean fnresult, fncalled; #ifdef DEBUG if (!startnode) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (orself) { val = startnode; } else { val = startnode->parent; } while (val) { fnresult = process_one_valwalker(walkerfn, cookie1, cookie2, val, modname, name, configonly, textmode, &fncalled); if (!fnresult) { return FALSE; } val = val->parent; } return TRUE; } /* val_find_all_ancestors */ /******************************************************************** * FUNCTION val_find_all_descendants * * Find all occurances of the specified node * within the current subtree. The walker fn will * be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * INPUTS: * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == startnode complex type to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of descendant node to find * == NULL to match any node name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if decname == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * orself == TRUE if axis is really ancestor-or-self * FALSE if axis is ancestor * forceall == TRUE to invoke the descendant callbacks * even if fncalled was true from the current * (parent) node; FALSE to skip descendants * if fncalled was TRUE * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ boolean val_find_all_descendants (val_walker_fn_t walkerfn, void *cookie1, void *cookie2, val_value_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode, boolean orself, boolean forceall) { val_value_t *val, *useval; boolean fncalled, fnresult; status_t res; #ifdef DEBUG if (!startnode) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (val_is_virtual(startnode)) { res = NO_ERR; useval = cache_virtual_value(NULL, startnode, &res); if (useval == NULL) { return res; } } else { useval = startnode; } if (orself) { fnresult = process_one_valwalker(walkerfn, cookie1, cookie2, useval, modname, name, configonly, textmode, &fncalled); if (!fnresult) { return FALSE; } } if (!typ_has_children(startnode->btyp)) { return TRUE; } for (val = (val_value_t *)dlq_firstEntry(&useval->v.childQ); val != NULL; val = (val_value_t *)dlq_nextEntry(val)) { if (VAL_IS_DELETED(val)) { continue; } fncalled = FALSE; fnresult = process_one_valwalker(walkerfn, cookie1, cookie2, val, modname, name, configonly, textmode, &fncalled); if (!fnresult) { return FALSE; } if (!fncalled || forceall) { fnresult = val_find_all_descendants(walkerfn, cookie1, cookie2, val, modname, name, configonly, textmode, FALSE, forceall); if (!fnresult) { return FALSE; } } } return TRUE; } /* val_find_all_descendants */ /******************************************************************** * FUNCTION val_find_all_pfaxis * * Find all occurances of the specified node * for the specified preceding or following axis * * preceding::* * following::* * * within the current subtree. The walker fn will * be called for each match. Because the callbacks * will be done in sequential order, starting from * the * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * INPUTS: * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * topnode == topnode used as the relative root for * calculating node position * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of preceding or following node to find * == NULL to match any node name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if decname == NULL * dblslash == TRUE if all decendents of the preceding * or following nodes should be checked * FALSE only 1 level is checked, not their descendants * textmode == TRUE if just testing for text() nodes * name modname will be ignored in this mode * FALSE if using name and modname to filter * axis == axis enum to use * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ boolean val_find_all_pfaxis (val_walker_fn_t walkerfn, void *cookie1, void *cookie2, val_value_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean dblslash, boolean textmode, ncx_xpath_axis_t axis) { val_value_t *val, *child, *useval; boolean fncalled, fnresult, forward; status_t res; #ifdef DEBUG if (!startnode) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif /* check the Q containing the startnode * for preceding or following nodes; * could be sibling node check or any node check */ switch (axis) { case XP_AX_PRECEDING: forward = FALSE; val = (val_value_t *)dlq_prevEntry(startnode); break; case XP_AX_FOLLOWING: forward = TRUE; val = (val_value_t *)dlq_nextEntry(startnode); break; case XP_AX_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /* for virtual nodes, it is assumed that the set of * siblings represents real values, but each * value node checked and expanded if a virtual node */ while (val) { if (VAL_IS_DELETED(val) || (configonly && !obj_is_config(val->obj))) { /* skip this entry */ if (forward) { val = (val_value_t *)dlq_nextEntry(val); } else { val = (val_value_t *)dlq_prevEntry(val); } continue; } if (val_is_virtual(val)) { res = NO_ERR; useval = cache_virtual_value(NULL, val, &res); if (useval == NULL) { return res; } } else { useval = val; } fnresult = process_one_valwalker(walkerfn, cookie1, cookie2, useval, modname, name, configonly, textmode, &fncalled); if (!fnresult) { return FALSE; } if (!fncalled && dblslash) { /* if /foo did not get added, than * try /foo/bar, /foo/baz, etc. * check all the child nodes even if * one of them matches, because all * matches are needed with the '//' operator */ for (child = val_get_first_child(useval); child != NULL; child = val_get_next_child(child)) { fnresult = val_find_all_pfaxis(walkerfn, cookie1, cookie2, child, modname, name, configonly, dblslash, textmode, axis); if (!fnresult) { return FALSE; } } } if (forward) { val = (val_value_t *)dlq_nextEntry(val); } else { val = (val_value_t *)dlq_prevEntry(val); } } return TRUE; } /* val_find_all_pfaxis */ /******************************************************************** * FUNCTION val_find_all_pfsibling_axis * * Find all occurances of the specified node * for the specified axis * * preceding-sibling::* * following-sibling::* * * within the current subtree. The walker fn will * be called for each match. Because the callbacks * will be done in sequential order, starting from * the * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * INPUTS: * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == starting sibling node to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of preceding or following node to find * == NULL to match any node name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if decname == NULL * dblslash == TRUE if all decendents of the preceding * or following nodes should be checked * FALSE only * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * axis == axis enum to use * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ boolean val_find_all_pfsibling_axis (val_walker_fn_t walkerfn, void *cookie1, void *cookie2, val_value_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean dblslash, boolean textmode, ncx_xpath_axis_t axis) { val_value_t *val, *useval, *child; boolean fncalled, fnresult, forward; status_t res; #ifdef DEBUG if (!startnode) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif /* check the Q containing the startnode * for preceding or following nodes; */ switch (axis) { case XP_AX_PRECEDING_SIBLING: /* execute the callback for all preceding nodes * that match the filter criteria */ val = (val_value_t *)dlq_prevEntry(startnode); forward = FALSE; break; case XP_AX_FOLLOWING_SIBLING: val = (val_value_t *)dlq_nextEntry(startnode); forward = TRUE; break; case XP_AX_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } while (val) { if (VAL_IS_DELETED(val) || (configonly && !obj_is_config(val->obj))) { if (forward) { val = (val_value_t *)dlq_nextEntry(val); } else { val = (val_value_t *)dlq_prevEntry(val); } continue; } if (val_is_virtual(val)) { res = NO_ERR; useval = cache_virtual_value(NULL, val, &res); if (useval == NULL) { return FALSE; } } else { useval = val; } fnresult = process_one_valwalker(walkerfn, cookie1, cookie2, useval, modname, name, configonly, textmode, &fncalled); if (!fnresult) { return FALSE; } if (!fncalled && dblslash) { /* if /foo did not get added, than * try /foo/bar, /foo/baz, etc. * check all the child nodes even if * one of them matches, because all * matches are needed with the '//' operator */ for (child = val_get_first_child(useval); child != NULL; child = val_get_next_child(child)) { fnresult = val_find_all_pfsibling_axis(walkerfn, cookie1, cookie2, child, modname, name, configonly, dblslash, textmode, axis); if (!fnresult) { return FALSE; } } } if (forward) { val = (val_value_t *)dlq_nextEntry(val); } else { val = (val_value_t *)dlq_prevEntry(val); } } return TRUE; } /* val_find_all_pfsibling_axis */ /******************************************************************** * FUNCTION val_get_axisnode * * Find the specified node based on the context node, * a context position and an axis * * ancestor::* * ancestor-or-self::* * child::* * descendant::* * descendant-or-self::* * following::* * following-sibling::* * preceding::* * preceding-sibling::* * self::* * * INPUTS: * startnode == context node to run tests from * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of preceding or following node to find * == NULL to match any node name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if decname == NULL * dblslash == TRUE if all decendents of the preceding * or following nodes should be checked * FALSE only * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * axis == axis enum to use * position == position to find in the specified axis * * RETURNS: * pointer to found value or NULL if none *********************************************************************/ val_value_t * val_get_axisnode (val_value_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean dblslash, boolean textmode, ncx_xpath_axis_t axis, int64 position) { finderparms_t finderparms; boolean fnresult, fncalled, orself; #ifdef DEBUG if (!startnode) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (position <= 0) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif orself = FALSE; memset(&finderparms, 0x0, sizeof(finderparms_t)); finderparms.findpos = position; /* check the Q containing the startnode * for preceding or following nodes; * could be sibling node check or any node check */ switch (axis) { case XP_AX_ANCESTOR_OR_SELF: orself = TRUE; /* fall through */ case XP_AX_ANCESTOR: fnresult = val_find_all_ancestors(position_walker, &finderparms, NULL, startnode, modname, name, configonly, textmode, orself); if (fnresult) { return NULL; } else { return finderparms.foundval; } case XP_AX_ATTRIBUTE: /* TBD: attributes not supported */ return NULL; case XP_AX_CHILD: fnresult = val_find_all_children(position_walker, &finderparms, NULL, startnode, modname, name, configonly, textmode); if (fnresult) { return NULL; } else { return finderparms.foundval; } case XP_AX_DESCENDANT_OR_SELF: orself = TRUE; /* fall through */ case XP_AX_DESCENDANT: fnresult = val_find_all_descendants(position_walker, &finderparms, NULL, startnode, modname, name, configonly, textmode, orself, FALSE); if (fnresult) { return NULL; } else { return finderparms.foundval; } case XP_AX_PRECEDING: case XP_AX_FOLLOWING: fnresult = val_find_all_pfaxis(position_walker, &finderparms, NULL, startnode, modname, name, configonly, dblslash, textmode, axis); if (fnresult) { return NULL; } else { return finderparms.foundval; } case XP_AX_PRECEDING_SIBLING: case XP_AX_FOLLOWING_SIBLING: fnresult = val_find_all_pfsibling_axis(position_walker, &finderparms, NULL, startnode, modname, name, configonly, dblslash, textmode, axis); if (fnresult) { return NULL; } else { return finderparms.foundval; } case XP_AX_NAMESPACE: return NULL; case XP_AX_PARENT: if (!startnode->parent) { return NULL; } /* there can only be one node in this axis, * so if the startnode isn't it, then return NULL */ fnresult = process_one_valwalker(position_walker, &finderparms, NULL, startnode->parent, modname, name, configonly, textmode, &fncalled); if (fnresult) { return NULL; } else { return finderparms.foundval; } case XP_AX_NONE: default: SET_ERROR(ERR_INTERNAL_VAL); return NULL; } /*NOTREACHED*/ } /* val_get_axisnode */ /******************************************************************** * FUNCTION val_get_child_inst_id * * Get the instance ID for this child node within the parent context * * INPUTS: * parent == parent complex type to check * child == child node to find ID for * * RETURNS: * the instance ID num (1 .. N), or 0 if some error *********************************************************************/ uint32 val_get_child_inst_id (const val_value_t *parent, const val_value_t *child) { const val_value_t *val; uint32 cnt; #ifdef DEBUG if (!parent || !child) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } if (!typ_has_children(parent->btyp)) { SET_ERROR(ERR_INTERNAL_VAL); return 0; } #endif cnt = 0; for (val = (const val_value_t *)dlq_firstEntry(&parent->v.childQ); val != NULL; val = (const val_value_t *)dlq_nextEntry(val)) { /* do not skip over deleted nodes in case the reason * this function is called is to print the instance ID * of the node being deleted */ if (xml_strcmp(val_get_mod_name(child), val_get_mod_name(val))) { continue; } /* check the node if the name matches */ if (!xml_strcmp(val->name, child->name)) { cnt++; if (val == child) { return cnt; } } } SET_ERROR(ERR_INTERNAL_VAL); return 0; } /* val_get_child_inst_id */ /******************************************************************** * FUNCTION val_liststr_count * * Get the number of strings in the list type * * INPUTS: * val == value to check * * RETURNS: * number of list entries; also zero for error *********************************************************************/ uint32 val_liststr_count (const val_value_t *val) { uint32 cnt; #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif cnt = 0; switch (val->btyp) { case NCX_BT_SLIST: case NCX_BT_BITS: cnt = ncx_list_cnt(&val->v.list); break; default: SET_ERROR(ERR_NCX_WRONG_TYPE); } return cnt; } /* val_liststr_count */ /******************************************************************** * FUNCTION val_index_match * * Check 2 val_value structs for the same instance ID * * The node data types must match, and must be * NCX_BT_LIST * * All index components must exactly match. * * INPUTS: * val1 == first value to index match * val2 == second value to index match * * RETURNS: * TRUE if the index chains match *********************************************************************/ boolean val_index_match (const val_value_t *val1, const val_value_t *val2) { assert(val1 && "val1 is NULL!" ); assert(val2 && "val2 is NULL!" ); int32 ret = index_match(val1, val2); return (ret) ? FALSE : TRUE; } /* val_index_match */ /******************************************************************** * FUNCTION val_index_compare * * Check 2 val_value structs for the same instance ID * * The node data types must match, and must be * NCX_BT_LIST * * INPUTS: * val1 == first value to index match * val2 == second value to index match * * RETURNS: * -1 , - or 1 for compare value *********************************************************************/ int val_index_compare (const val_value_t *val1, const val_value_t *val2) { assert(val1 && "val1 is NULL!" ); assert(val2 && "val2 is NULL!" ); int32 ret = index_match(val1, val2); return ret; } /* val_index_compare */ /******************************************************************** * FUNCTION val_compare_max * * Compare 2 val_value_t struct value contents * Check all or config only * Check just child nodes or all descendant nodes * Handles NCX_CL_BASE and NCX_CL_SIMPLE data classes * by comparing the simple value. * * Handle NCX_CL_COMPLEX by checking the index if needed * and then checking all the child nodes recursively * * !!!! Meta-value contents are ignored for this test !!!! * * INPUTS: * val1 == first value to check * val2 == second value to check * configonly == TRUE to compare config=true nodes only * FALSE to compare all nodes * childonly == TRUE to look just 1 level for comparison * FALSE to compare all descendant nodes of complex types * editing == TRUE to compare for editing * FALSE to compare just the values, so a set by * default and value=default are the same value * * RETURNS: * compare result * -1: val1 is less than val2 (if complex just different or error) * 0: val1 is the same as val2 * 1: val1 is greater than val2 *********************************************************************/ int32 val_compare_max (const val_value_t *val1, const val_value_t *val2, boolean configonly, boolean childonly, boolean editing) { ncx_btype_t btyp; const val_value_t *ch1, *ch2; int32 ret; xmlns_id_t nsid1, nsid2; assert( val1 && "val1 is NULL!"); assert( val2 && "val2 is NULL!"); if (val1->btyp != val2->btyp) { /* this might happen if a new config tree * has a delete node with no value */ return -1; } /* normally ignore all meta-data, except when checking * for nested operations */ if (configonly && editing) { /* if there was an nc:operation or YANG attribute in the * node, then do not treat the nodes as equal */ if (val1->editvars && val1->editvars->operset) { return -1; } if (val2->editvars && val2->editvars->operset) { return 1; } /* if the set-by-default property is different than * do not treat the values as equal */ if (val_set_by_default(val1) != val_set_by_default(val2)) { return 1; } } btyp = val1->btyp; switch (btyp) { case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: if (val1->v.boo == val2->v.boo) { ret = 0; } else if (val1->v.boo) { ret = 1; } else { ret = -1; } break; case NCX_BT_ENUM: if (VAL_ENUM(val1) == VAL_ENUM(val2)) { ret = 0; } else if (VAL_ENUM(val1) < VAL_ENUM(val2)) { ret = -1; } else { ret = 1; } break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_DECIMAL64: case NCX_BT_FLOAT64: ret = ncx_compare_nums(&val1->v.num, &val2->v.num, btyp); break; case NCX_BT_BINARY: if (!val1->v.binary.ustr) { ret = -1; } else if (!val2->v.binary.ustr) { ret = 1; } else if (val1->v.binary.ustrlen < val2->v.binary.ustrlen) { ret = -1; } else if (val1->v.binary.ustrlen > val2->v.binary.ustrlen) { ret = 1; } else { ret = memcmp(val1->v.binary.ustr, val2->v.binary.ustr, val1->v.binary.ustrlen); } break; case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: ret = ncx_compare_strs(&val1->v.str, &val2->v.str, btyp); break; case NCX_BT_SLIST: case NCX_BT_BITS: ret = ncx_compare_lists(&val1->v.list, &val2->v.list); break; case NCX_BT_IDREF: /* note that this sort order based on NSID number may not * be stable across reboots, so the order of system-ordered * identityref leaf-lists can change */ if (val1->v.idref.nsid == val2->v.idref.nsid) { if (val1->v.idref.name == NULL) { ret = 1; } else if (val2->v.idref.name == NULL) { ret = -1; } else { ret = xml_strcmp(val1->v.idref.name, val2->v.idref.name); } } else if (val1->v.idref.nsid < val2->v.idref.nsid) { ret = -1; } else { ret = 1; } break; case NCX_BT_LIST: ret = index_match(val1, val2); if (ret) { break; } /* else drop though and check values */ case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_CASE: ch1 = (val_value_t *)dlq_firstEntry(&val1->v.childQ); ch2 = (val_value_t *)dlq_firstEntry(&val2->v.childQ); for (;;) { if ((ch1 && VAL_IS_DELETED(ch1)) || (ch2 && VAL_IS_DELETED(ch2)) || configonly) { while (ch1 && (VAL_IS_DELETED(ch1) || !obj_get_config_flag(ch1->obj))) { ch1 = (val_value_t *)dlq_nextEntry(ch1); } while (ch2 && (VAL_IS_DELETED(ch2) || !obj_get_config_flag(ch2->obj))) { ch2 = (val_value_t *)dlq_nextEntry(ch2); } } /* check if both child nodes exist */ if (!ch1 && !ch2) { return 0; } else if (!ch1) { return -1; } else if (!ch2) { return 1; } /* check if the namespaces are the same */ nsid1 = val_get_nsid(ch1); nsid2 = val_get_nsid(ch1); if (nsid1 < nsid2) { return -1; } else if (nsid1 > nsid2) { return 1; } /* check if both child nodes have the same name */ ret = xml_strcmp(ch1->name, ch2->name); if (ret) { return ret; } if (!childonly || typ_is_simple(ch1->btyp)) { /* check if they have same value */ ret = val_compare_max(ch1, ch2, configonly, childonly, editing); if (ret) { return ret; } } // else childonly and complex node; treat as same /* get the next pair of child nodes to check */ ch1 = (val_value_t *)dlq_nextEntry(ch1); ch2 = (val_value_t *)dlq_nextEntry(ch2); } /*NOTREACHED*/ case NCX_BT_EXTERN: SET_ERROR(ERR_INTERNAL_VAL); ret = -1; break; case NCX_BT_INTERN: SET_ERROR(ERR_INTERNAL_VAL); ret = -1; break; default: SET_ERROR(ERR_INTERNAL_VAL); ret = -1; } return ret; } /* val_compare_max */ /******************************************************************** * FUNCTION val_compare_ex * * Compare 2 val_value_t struct value contents * Check all or config only * * Handles NCX_CL_BASE and NCX_CL_SIMPLE data classes * by comparing the simple value. * * Handle NCX_CL_COMPLEX by checking the index if needed * and then checking all the child nodes recursively * * !!!! Meta-value contents are ignored for this test !!!! * * INPUTS: * val1 == first value to check * val2 == second value to check * configonly == TRUE to compare config=true nodes only * FALSE to compare all nodes * * RETURNS: * compare result * -1: val1 is less than val2 (if complex just different or error) * 0: val1 is the same as val2 * 1: val1 is greater than val2 *********************************************************************/ int32 val_compare_ex (const val_value_t *val1, const val_value_t *val2, boolean configonly) { return val_compare_max(val1, val2, configonly, FALSE, TRUE); } /******************************************************************** * FUNCTION val_compare * * Compare 2 val_value_t struct value contents * * Handles NCX_CL_BASE and NCX_CL_SIMPLE data classes * by comparing the simple value. * * Handle NCX_CL_COMPLEX by checking the index if needed * and then checking all the child nodes recursively * * !!!! Meta-value contents are ignored for this test !!!! * * INPUTS: * val1 == first value to check * val2 == second value to check * * RETURNS: * compare result * -1: val1 is less than val2 (if complex just different or error) * 0: val1 is the same as val2 * 1: val1 is greater than val2 *********************************************************************/ int32 val_compare (const val_value_t *val1, const val_value_t *val2) { return val_compare_max(val1, val2, FALSE, FALSE, TRUE); } /* val_compare */ /******************************************************************** * FUNCTION val_compare_to_string * * Compare a val_value_t struct value contents to a string * * Handles NCX_CL_BASE and NCX_CL_SIMPLE data classes * by comparing the simple value. * * !!!! Meta-value contents are ignored for this test !!!! * * INPUTS: * val1 == first value to check * strval2 == second value to check * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * compare result * -1: val1 is less than val2 (if complex just different or error) * 0: val1 is the same as val2 * 1: val1 is greater than val2 *********************************************************************/ int32 val_compare_to_string (const val_value_t *val1, const xmlChar *strval2, status_t *res) { #define MYBUFFSIZE 64 assert( val1 && "val1 is NULL!"); assert( strval2 && "strval2 is NULL!"); assert( res && "res is NULL!"); xmlChar buff[MYBUFFSIZE]; xmlChar *mbuff = NULL; uint32 len = 0; int32 retval = 0; status_t myres = val_sprintf_simval_nc(NULL, val1, &len); if (myres != NO_ERR) { *res = myres; return -2; } if (len < MYBUFFSIZE) { myres = val_sprintf_simval_nc(buff, val1, &len); } else { mbuff =m__getMem(len+1); if (!mbuff) { *res = ERR_INTERNAL_MEM; return -2; } myres = val_sprintf_simval_nc(mbuff, val1, &len); } if (myres != NO_ERR) { *res = myres; retval = -2; } else if (mbuff) { retval = xml_strcmp(mbuff, strval2); } else { retval = xml_strcmp(buff, strval2); } if ( mbuff ) { m__free(mbuff); } *res = NO_ERR; return retval; } /* val_compare_to_string */ /******************************************************************** * FUNCTION val_compare_for_replace * * Compare 2 val_value_t struct value contents * for the nc:operation=replace procedures * Only check the child nodes to see if the * config nodes are the same * * !!!! Meta-value contents are ignored for this test !!!! * * INPUTS: * val1 == new value to print * val2 == current value to check * * RETURNS: * compare result * -1: val1 is less than val2 (if complex just different or error) * 0: val1 is the same as val2 * 1: val1 is greater than val2 *********************************************************************/ int32 val_compare_for_replace (const val_value_t *val1, const val_value_t *val2) { assert( val1 && "val1 is NULL!"); assert( val2 && "val2 is NULL!"); const val_value_t *ch1, *ch2; int32 ret = 0; xmlns_id_t nsid1, nsid2; switch (val1->btyp) { case NCX_BT_LIST: ret = index_match(val1, val2); if (ret) { break; } /* else drop though and check values */ case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_CASE: ch1 = (val_value_t *)dlq_firstEntry(&val1->v.childQ); ch2 = (val_value_t *)dlq_firstEntry(&val2->v.childQ); for (;;) { while (ch1 && (VAL_IS_DELETED(ch1) || !obj_get_config_flag(ch1->obj))) { ch1 = (val_value_t *)dlq_nextEntry(ch1); } while (ch2 && (VAL_IS_DELETED(ch2) || !obj_get_config_flag(ch2->obj))) { ch2 = (val_value_t *)dlq_nextEntry(ch2); } /* check if both child nodes exist */ if (!ch1 && !ch2) { return 0; } else if (!ch1) { return -1; } else if (!ch2) { return 1; } /* check if the namespaces are the same */ nsid1 = val_get_nsid(ch1); nsid2 = val_get_nsid(ch1); if (nsid1 < nsid2) { return -1; } else if (nsid1 > nsid2) { return 1; } /* check if both child nodes have the same name */ ret = xml_strcmp(ch1->name, ch2->name); if (ret) { return ret; } /* check if they have same value * need to compare complex types as well * as leafs because the replace operation * could already be set in the parent * so skipping a complex node can result * in the merge of child nodes, instead of replace */ ret = val_compare_ex(ch1, ch2, TRUE); if (ret) { return ret; } /* get the next pair of child nodes to check */ ch1 = (val_value_t *)dlq_nextEntry(ch1); ch2 = (val_value_t *)dlq_nextEntry(ch2); } break; default: ret = val_compare_ex(val1, val2, TRUE); } return ret; } /* val_compare_for_replace */ /******************************************************************** * FUNCTION val_sprintf_simval_nc * * Sprintf the xmlChar string NETCONF representation of a simple value * * buff is allowed to be NULL; if so, then this fn will * just return the length of the string (w/o EOS ch) * * USAGE: * call 1st time with a NULL buffer to get the length * call the 2nd time with a buffer of the returned length * * !!!! DOES NOT CHECK BUFF OVERRUN IF buff is non-NULL !!!! * * INPUTS: * buff == buffer to write (NULL means get length only) * val == value to check * len == address of return length * * OUTPUTS: * *len == number of bytes written (or just length if buff == NULL) * * RETURNS: * status *********************************************************************/ status_t val_sprintf_simval_nc (xmlChar *buff, const val_value_t *val, uint32 *len) { const ncx_lmem_t *lmem, *nextlmem; const xmlChar *s, *prefix; xmlChar *str; ncx_btype_t btyp; status_t res; int32 icnt; uint32 mylen; char numbuff[VAL_MAX_NUMLEN]; #ifdef DEBUG if (!val || !len) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; btyp = val->btyp; switch (btyp) { case NCX_BT_EMPTY: /* flag is element name : */ if (val->v.boo) { if (buff) { icnt = sprintf((char *)buff, "<%s/>", val->name); if (icnt < 0) { return SET_ERROR(ERR_INTERNAL_VAL); } else { *len = (uint32)icnt; } } else { *len = xml_strlen(val->name) + 3; } } else { if (buff) { *buff = 0; } *len = 0; } break; case NCX_BT_BOOLEAN: if (val->v.boo) { if (buff) { sprintf((char *)buff, "true"); } *len = 4; } else { if (buff) { sprintf((char *)buff, "false"); } *len = 5; } break; case NCX_BT_ENUM: if (buff) { if (val->v.enu.name) { icnt = sprintf((char *)buff, "%s", val->v.enu.name); if (icnt < 0 ) { return SET_ERROR(ERR_INTERNAL_VAL); } else { *len = (uint32)icnt; } } else { *len = 0; } } else if (val->v.enu.name) { *len = xml_strlen(val->v.enu.name); } else { *len = 0; } break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_FLOAT64: res = ncx_sprintf_num(buff, &val->v.num, btyp, len); if (res != NO_ERR) { return SET_ERROR(res); } break; case NCX_BT_DECIMAL64: if (val->v.num.dec.val == 0) { if (buff) { *len = xml_strcpy(buff, (const xmlChar *)"0.0"); } else { *len = xml_strlen((const xmlChar *)"0.0"); } } else { /* need to generate the value string */ res = ncx_sprintf_num(buff, &val->v.num, btyp, len); if (res != NO_ERR) { return SET_ERROR(res); } } break; case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: if (val->obj && obj_is_password(val->obj)) { s = VAL_PASSWORD_STRING; } else { s = VAL_STR(val); } if (buff) { if (s) { *len = xml_strcpy(buff, s); } else { *len = 0; } } else { *len = (s) ? xml_strlen(s) : 0; } break; case NCX_BT_BINARY: s = val->v.binary.ustr; if (buff) { if (s) { /* !!! do not know the real buffer length * !!! to send; assume call to this fn * !!! to retrieve the length was done OK */ res = b64_encode(s, val->v.binary.ustrlen, buff, NCX_MAX_UINT, 0 /*no newline split*/, len); } else { *len = 0; } } else if (s) { *len = b64_get_encoded_str_len( val->v.binary.ustrlen, 0/*no newline split*/ ); } else { *len = 0; } break; case NCX_BT_BITS: *len = 0; for (lmem = (const ncx_lmem_t *)dlq_firstEntry(&val->v.list.memQ); lmem != NULL; lmem = nextlmem) { nextlmem = (const ncx_lmem_t *)dlq_nextEntry(lmem); s = lmem->val.str; if (buff) { /* hardwire double quotes to wrapper list strings */ icnt = sprintf((char *)buff, "%s", (s) ? (const char *)s : ""); if (icnt < 0) { return SET_ERROR(ERR_INTERNAL_VAL); } else { buff += icnt; *len += (uint32)icnt; } if (nextlmem) { *buff++ = ' '; *len += 1; } } else { *len += ((s) ? xml_strlen(s) : 0); if (nextlmem) { *len += 1; } } } break; case NCX_BT_SLIST: *len = 0; for (lmem = (const ncx_lmem_t *)dlq_firstEntry(&val->v.list.memQ); lmem != NULL; lmem = (const ncx_lmem_t *)dlq_nextEntry(lmem)) { if (typ_is_string(val->v.list.btyp)) { s = lmem->val.str; } else if (typ_is_number(val->v.list.btyp)) { res = ncx_sprintf_num((xmlChar *)numbuff, &lmem->val.num, val->v.list.btyp, len); if (res != NO_ERR) { return SET_ERROR(res); } s = (const xmlChar *)numbuff; } else { switch (val->v.list.btyp) { case NCX_BT_ENUM: s = VAL_ENUM_NAME(val); break; case NCX_BT_BOOLEAN: if (val->v.boo) { s = NCX_EL_TRUE; } else { s = NCX_EL_FALSE; } break; default: SET_ERROR(ERR_INTERNAL_VAL); s = NULL; } } if (buff) { icnt = sprintf((char *)buff, "%s ", (s) ? (const char *)s : ""); if (icnt < 0) { return SET_ERROR(ERR_INTERNAL_VAL); } else { buff += icnt; *len += (uint32)icnt; } } else { *len += (1 + ((s) ? xml_strlen(s) : 0)); } } break; case NCX_BT_IDREF: /* use the xmlprefix assigned to the NS ID for the * identityref, plus ':', plus the identity name */ prefix = NULL; if (val->v.idref.nsid) { prefix = xmlns_get_ns_prefix(val->v.idref.nsid); } mylen = 0; if (buff) { str = buff; if (prefix) { mylen = xml_strcpy(str, prefix); str += mylen; mylen++; *str++ = ':'; } if(val->v.idref.name) { mylen += xml_strcpy(str, val->v.idref.name); } else { mylen += xml_strcpy(str, NCX_EL_NONE); } } else { mylen = (prefix) ? xml_strlen(prefix)+1 : 0; if(val->v.idref.name) { mylen += xml_strlen(val->v.idref.name); } else { mylen += xml_strlen(NCX_EL_NONE); } } *len = mylen; break; case NCX_BT_LIST: case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_CASE: return SET_ERROR(ERR_NCX_OPERATION_NOT_SUPPORTED); default: return SET_ERROR(ERR_INTERNAL_VAL); } return NO_ERR; } /* val_sprintf_simval_nc */ /******************************************************************** * FUNCTION val_make_sprintf_string * * Malloc a buffer and then sprintf the xmlChar string * NETCONF representation of a simple value * * INPUTS: * val == value to print * * RETURNS: * malloced buffer with string represetation of the * 'val' value node * NULL if some error *********************************************************************/ xmlChar * val_make_sprintf_string (const val_value_t *val) { xmlChar *buff; uint32 len; status_t res; len = 0; res = val_sprintf_simval_nc(NULL, val, &len); if (res != NO_ERR) { return NULL; } buff = m__getMem(len+1); if (buff == NULL) { return NULL; } res = val_sprintf_simval_nc(buff, val, &len); if (res != NO_ERR) { m__free(buff); return NULL; } return buff; } /* val_make_sprintf_string */ /******************************************************************** * FUNCTION val_resolve_scoped_name * * Find the scoped identifier in the specified complex value * * E.g.: foo.bar.baz * * INPUTS: * val == complex type to check * name == scoped name string of a nested node to find * chval == address of return child val * * OUTPUTS: * *chval is set to the value of the found local scoped * child member, if NO_ERR * * RETURNS: * status *********************************************************************/ status_t val_resolve_scoped_name (val_value_t *val, const xmlChar *name, val_value_t **chval) { #define BUFFLEN 0xfffe xmlChar *buff; const xmlChar *next; val_value_t *ch, *nextch; #ifdef DEBUG if (!val || !name || !chval) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif buff = m__getMem(BUFFLEN+1); if (!buff) { return SET_ERROR(ERR_INTERNAL_MEM); } /* get the top-level definition name and look for it * in the child queue. This is going to work because * the token was already parsed as a scoped token string */ next = ncx_get_name_segment(name, buff, BUFFLEN); /* the first segment is the start value */ if (!next || xml_strcmp(buff, val->name)) { m__free(buff); return SET_ERROR(ERR_NCX_NOT_FOUND); } /* Each time get_name_segment is called, the next pointer * will be a dot or end of string. * * Keep looping until there are no more name segments left * or an error occurs. The first time the loop is entered * the *next char should be non-zero. */ ch = val; while (next && *next) { /* there is a next child, this better be a complex value */ nextch = NULL; if (typ_has_children(ch->btyp)) { next = ncx_get_name_segment(++next, buff, BUFFLEN); nextch = val_first_child_name(ch, buff); } if (!nextch) { m__free(buff); return SET_ERROR(ERR_NCX_DEFSEG_NOT_FOUND); } ch = nextch; } m__free(buff); *chval = ch; return NO_ERR; } /* val_resolve_scoped_name */ /******************************************************************** * FUNCTION val_get_iqualval * * Get the effective instance qualifier value for this value * * INPUTS: * val == value construct to check * * RETURNS: * iqual value *********************************************************************/ ncx_iqual_t val_get_iqualval (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return NCX_IQUAL_NONE; } #endif return obj_get_iqualval(val->obj); } /* val_get_iqualval */ /******************************************************************** * FUNCTION val_duplicates_allowed * * Determine if duplicates are allowed for the given val type * The entire definition chain is checked to see if a 'no-duplicates' * * The default is config, so some sort of named type or parameter * must be declared to create a non-config data element * * Fishing order: * 1) typdef chain * 2) parm definition * 3) parmset definition * * INPUTS: * val == value node to check * * RETURNS: * TRUE if the value is classified as configuration * FALSE if the value is not classified as configuration *********************************************************************/ boolean val_duplicates_allowed (val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif /* see if info already cached */ if (val->flags & VAL_FL_DUPDONE) { return (val->flags & VAL_FL_DUPOK) ? TRUE : FALSE; } /* check for no-duplicates in the type appinfo */ if (val->typdef) { if (typ_find_appinfo(val->typdef, NCX_PREFIX, NCX_EL_NODUPLICATES)) { val->flags |= VAL_FL_DUPDONE; return FALSE; } } else { val->flags |= VAL_FL_DUPDONE; return FALSE; } /* default is to allow duplicates */ val->flags |= (VAL_FL_DUPDONE | VAL_FL_DUPOK); return TRUE; } /* val_duplicates_allowed */ /******************************************************************** * FUNCTION val_has_content * * Determine if there is a value or any child nodes for this val * * INPUTS: * val == value node to check * * RETURNS: * TRUE if the value has some content * FALSE if the value does not have any content *********************************************************************/ boolean val_has_content (const val_value_t *val) { ncx_btype_t btyp; #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (!val_is_real(val)) { return TRUE; } btyp = val->btyp; if (typ_has_children(btyp)) { return !dlq_empty(&val->v.childQ); } else if (btyp == NCX_BT_EMPTY) { return FALSE; } else if ((btyp == NCX_BT_SLIST || btyp==NCX_BT_BITS) && ncx_list_empty(&val->v.list)) { return FALSE; } else if (typ_is_string(btyp)) { return (VAL_STR(val) && *(VAL_STR(val))) ? TRUE : FALSE; } else { return TRUE; } } /* val_has_content */ /******************************************************************** * FUNCTION val_has_index * * Determine if this value has an index * * INPUTS: * val == value node to check * * RETURNS: * TRUE if the value has an index * FALSE if the value does not have an index *********************************************************************/ boolean val_has_index (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return (dlq_empty(&val->indexQ)) ? FALSE : TRUE; } /* val_has_index */ /******************************************************************** * FUNCTION val_get_first_index * * Get the first index entry, if any for this value node * * INPUTS: * val == value node to check * * RETURNS: * pointer to first val_index_t node, NULL if none *********************************************************************/ val_index_t * val_get_first_index (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (val_index_t *)dlq_firstEntry(&val->indexQ); } /* val_get_first_index */ /******************************************************************** * FUNCTION val_get_next_index * * Get the next index entry, if any for this value node * * INPUTS: * val == value node to check * * RETURNS: * pointer to next val_index_t node, NULL if none *********************************************************************/ val_index_t * val_get_next_index (const val_index_t *valindex) { #ifdef DEBUG if (!valindex) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (val_index_t *)dlq_nextEntry(valindex); } /* val_get_next_index */ /******************************************************************** * FUNCTION val_parse_meta * * Parse the metadata descriptor against the typdef * Check only that the value is ok, not instance count * * INPUTS: * typdef == typdef to check * attr == XML attribute to check * retval == initialized val_value_t to fill in * * OUTPUTS: * *retval == filled in if return is NO_ERR * RETURNS: * status of the operation *********************************************************************/ status_t val_parse_meta (typ_def_t *typdef, xml_attr_t *attr, val_value_t *retval) { const xmlChar *enustr, *attrval; ncx_btype_t btyp; int32 enuval; status_t res; #ifdef DEBUG if (!typdef || !attr || !retval) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif res = NO_ERR; btyp = typ_get_basetype(typdef); attrval = attr->attr_val; /* setup the return value */ retval->flags |= VAL_FL_META; /* obj field is NULL in meta */ retval->btyp = btyp; retval->typdef = typdef; retval->dname = xml_strdup(attr->attr_name); if (!retval->dname) { return ERR_INTERNAL_MEM; } retval->name = retval->dname; retval->nsid = attr->attr_ns; retval->xpathpcb = attr->attr_xpcb; attr->attr_xpcb = NULL; /* handle the attr string according to its base type */ switch (btyp) { case NCX_BT_BOOLEAN: if (attrval && !xml_strcmp(attrval, NCX_EL_TRUE)) { retval->v.boo = TRUE; } else if (attrval && !xml_strcmp(attrval, (const xmlChar *)"1")) { retval->v.boo = TRUE; } else if (attrval && !xml_strcmp(attrval, NCX_EL_FALSE)) { retval->v.boo = FALSE; } else if (attrval && !xml_strcmp(attrval, (const xmlChar *)"0")) { retval->v.boo = FALSE; } else { res = ERR_NCX_INVALID_VALUE; } break; case NCX_BT_ENUM: res = val_enum_ok(typdef, attrval, &enuval, &enustr); if (res == NO_ERR) { retval->v.enu.name = enustr; retval->v.enu.val = enuval; } break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_FLOAT64: res = ncx_decode_num(attrval, btyp, &retval->v.num); if (res == NO_ERR) { res = val_range_ok(typdef, btyp, &retval->v.num); } break; case NCX_BT_DECIMAL64: res = ncx_decode_dec64(attrval, typ_get_fraction_digits(typdef), &retval->v.num); if (res == NO_ERR) { res = val_range_ok(typdef, btyp, &retval->v.num); } break; case NCX_BT_STRING: case NCX_BT_BINARY: case NCX_BT_INSTANCE_ID: res = val_string_ok(typdef, btyp, attrval); if (res == NO_ERR) { retval->v.str = xml_strdup(attrval); if (!retval->v.str) { res = ERR_INTERNAL_MEM; } } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* val_parse_meta */ /******************************************************************** * FUNCTION val_set_extern * * Setup an NCX_BT_EXTERN value * * INPUTS: * val == value to setup * fname == filespec string to set as the value *********************************************************************/ void val_set_extern (val_value_t *val, xmlChar *fname) { #ifdef DEBUG if (!val || !fname) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif val->btyp = NCX_BT_EXTERN; val->v.fname = fname; } /* val_set_extern */ /******************************************************************** * FUNCTION val_set_intern * * Setup an NCX_BT_INTERN value * * INPUTS: * val == value to setup * intbuff == internal buffer to set as the value *********************************************************************/ void val_set_intern (val_value_t *val, xmlChar *intbuff) { #ifdef DEBUG if (!val || !intbuff) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif val->btyp = NCX_BT_INTERN; val->v.intbuff = intbuff; } /* val_set_intern */ /******************************************************************** * FUNCTION val_fit_oneline * * Check if the XML encoding for the specified val_value_t * should take one line or more than one line * * Simple types should not use more than one line or introduce * any extra whitespace in any simple content element * * !!!The calculation includes the XML start and end tags!!! * * totalsize: value == 26 * * INPUTS: * val == value to check * linelen == length of line to check against * * RETURNS: * TRUE if the val is a type that should or must fit on one line * FALSE otherwise *********************************************************************/ boolean val_fit_oneline (const val_value_t *val, uint32 linesize) { ncx_btype_t btyp; const xmlChar *str; uint32 cnt, valsize, valnamesize, totalsize; xmlns_id_t nsid; #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return TRUE; } #endif btyp = val->btyp; if (btyp == NCX_BT_EMPTY) { return TRUE; } valsize = 0; switch (btyp) { case NCX_BT_ENUM: valsize = (VAL_ENUM_NAME(val)) ? xml_strlen(VAL_ENUM_NAME(val)) : 0; break; case NCX_BT_BOOLEAN: valsize = (VAL_BOOL(val)) ? 4 : 5; break; case NCX_BT_INT8: valsize = 4; break; case NCX_BT_INT16: valsize = 6; break; case NCX_BT_INT32: valsize = 11; break; case NCX_BT_UINT8: valsize = 3; break; case NCX_BT_UINT16: valsize = 5; break; case NCX_BT_UINT32: valsize = 10; break; case NCX_BT_UINT64: case NCX_BT_INT64: case NCX_BT_DECIMAL64: valsize = 21; break; case NCX_BT_FLOAT64: /* guess an average amount, since most numbers are * never going to be this many digits */ valsize = 32; break; case NCX_BT_BINARY: valsize = val->v.binary.ustrlen; break; case NCX_BT_INSTANCE_ID: /*** TEMP !!! fall through !!!! ****/ /*** TBD: XPath check ***/ /* return FALSE; */ case NCX_BT_STRING: case NCX_BT_LEAFREF: if (VAL_STR(val)) { valsize = xml_strlen(VAL_STR(val)); /* check if multiple new-lines are entered */ str = VAL_STR(val); cnt = 0; while (*str && cnt < 2) { if (*str++ == '\n') { cnt++; } } if (cnt >= 2) { return FALSE; } } break; case NCX_BT_SLIST: case NCX_BT_BITS: /* these are printed 1 per line right now */ return TRUE; case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_CONTAINER: case NCX_BT_LIST: case NCX_BT_CHOICE: case NCX_BT_CASE: return dlq_empty(&val->v.childQ); case NCX_BT_EXTERN: case NCX_BT_INTERN: /* just put these on a new line; rare usage at this time */ return FALSE; default: SET_ERROR(ERR_INTERNAL_VAL); return TRUE; } if (valsize >= linesize) { return FALSE; } valnamesize = xml_strlen(val->name); nsid = val_get_nsid(val); if (val->nsid) { /* account for 'foo:' */ valnamesize += (xml_strlen(xmlns_get_ns_prefix(nsid)) + 1); } /* val */ totalsize = valsize + 5 + (2 * valnamesize); return (totalsize <= linesize) ? TRUE : FALSE; } /* val_fit_oneline */ /******************************************************************** * FUNCTION val_create_allowed * * Check if the specified value is allowed to have a * create edit-config operation attribute * * INPUTS: * val == value to check * * RETURNS: * TRUE if the val is allowed to have the edit-op * FALSE otherwise *********************************************************************/ boolean val_create_allowed (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return (val->index) ? FALSE : TRUE; } /* val_create_allowed */ /******************************************************************** * FUNCTION val_delete_allowed * * Check if the specified value is allowed to have a * delete edit-config operation attribute * * INPUTS: * val == value to check * * RETURNS: * TRUE if the val is allowed to have the edit-op * FALSE otherwise *********************************************************************/ boolean val_delete_allowed (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return (val->index) ? FALSE : TRUE; } /* val_delete_allowed */ /******************************************************************** * FUNCTION val_is_config_data * * Check if the specified value is a config DB object instance * * INPUTS: * val == value to check * * RETURNS: * TRUE if the val is a config DB object instance * FALSE otherwise *********************************************************************/ boolean val_is_config_data (const val_value_t *val) { #ifdef DEBUG if (val == NULL || val->obj == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (obj_is_root(val->obj)) { return TRUE; } else if (obj_is_data_db(val->obj) && obj_get_config_flag(val->obj)) { return TRUE; } else if ((val->obj==ncx_get_gen_container() || val->obj==ncx_get_gen_string() || val->obj==ncx_get_gen_empty()) && val->parent!=NULL) { /* data based on generic types is config data if the parent is */ return val_is_config_data(val->parent); } else { return FALSE; } } /* val_is_config_data */ /******************************************************************** * FUNCTION val_is_virtual * * Check if the specified value is a virtual value * such that a 'get' callback function is required * to access the real value contents * * INPUTS: * val == value to check * * RETURNS: * TRUE if the val is a virtual value * FALSE otherwise *********************************************************************/ boolean val_is_virtual (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return (val->getcb) ? TRUE : FALSE; } /* val_is_virtual */ /******************************************************************** * FUNCTION val_get_virtual_value * * Get the value of a value node * The top-level value is provided by the caller * and must be malloced with val_new_value * before calling this function * * must free the return val; not cached * * If the val->getcb is NULL, then an error will be returned * * Caller should check for *res == ERR_NCX_SKIPPED * This will be returned if virtual value has no * instance at this time. * * !!! DO NOT SAVE THE RETURN VALUE LONGER THAN THE * !!! VIRTUAL VALUE CACHE TIMEOUT VALUE * * INPUTS: * session == session CB ptr cast as void * * that is getting the virtual value * val == virtual value to get value for * res == pointer to output function return status value * * OUTPUTS: * val->virtualval will be set with the cached return value * *res == the function return status * * RETURNS: * A malloced and filled in val_value_t struct * This value is cached in the val->virtualval pointer * and will be freed when the cache is replaced or when * val is freed *********************************************************************/ val_value_t * val_get_virtual_value (void *session, val_value_t *val, status_t *res) { ses_cb_t *scb; val_value_t *retval; #ifdef DEBUG if (!val || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } if (!val->getcb) { *res = SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif scb = (ses_cb_t *)session; retval = cache_virtual_value(scb, val, res); return retval; } /* val_get_virtual_value */ /******************************************************************** * FUNCTION val_is_default * * Check if the specified value is set to the YANG default value * * INPUTS: * val == value to check * * SIDE EFFECTS: * val->flags may be adjusted * VAL_FL_DEFVALSET will be set if not set already * VAL_FL_DEFVAL will be set or cleared if * VAL_FL_DEFSETVAL is not already set, * after determining if the value == its default * * RETURNS: * TRUE if the val is set to the default value * FALSE otherwise *********************************************************************/ boolean val_is_default (val_value_t *val) { const xmlChar *def; xmlChar *binbuff; val_value_t *testval; ncx_enum_t enu; ncx_num_t num; boolean ret; ncx_btype_t btyp; status_t res; uint32 len, deflen; #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif /* complex types do not have defaults */ if (val->typdef == NULL) { return FALSE; } /* check added by default */ if (val->flags & VAL_FL_DEFSET) { return TRUE; } /* check the cached response for a normal leaf */ if (val->flags & VAL_FL_DEFVALSET) { return (val->flags & VAL_FL_DEFVAL) ? TRUE : FALSE; } /* check general corner-case: if the value is part of * an index, then return FALSE, even if the data type * for the index node has a default */ if (val->index) { val->flags |= VAL_FL_DEFVALSET; val->flags &= ~VAL_FL_DEFVAL; return FALSE; } /* check if the data type has a default */ def = obj_get_default(val->obj); if (def == NULL) { val->flags |= VAL_FL_DEFVALSET; val->flags &= ~VAL_FL_DEFVAL; return FALSE; } /* check if this is a virtual value, return FALSE instead * of retrieving the value!!! Used for monitoring only!!! */ if (val->getcb != NULL) { val->flags |= VAL_FL_DEFVALSET; val->flags &= ~VAL_FL_DEFVAL; return FALSE; } ret = FALSE; btyp = val->btyp; switch (btyp) { case NCX_BT_EMPTY: case NCX_BT_BOOLEAN: if (ncx_is_true(def)) { /* default is true */ if (val->v.boo) { ret = TRUE; } } else if (ncx_is_false(def)) { /* default is false */ if (!val->v.boo) { ret = TRUE; } } break; case NCX_BT_ENUM: ncx_init_enum(&enu); res = ncx_set_enum(def, &enu); if (res == NO_ERR && !ncx_compare_enums(&enu, &val->v.enu)) { ret = TRUE; } ncx_clean_enum(&enu); break; case NCX_BT_INT8: case NCX_BT_INT16: case NCX_BT_INT32: case NCX_BT_INT64: case NCX_BT_UINT8: case NCX_BT_UINT16: case NCX_BT_UINT32: case NCX_BT_UINT64: case NCX_BT_FLOAT64: ncx_init_num(&num); res = ncx_decode_num(def, btyp, &num); if (res == NO_ERR && !ncx_compare_nums(&num, &val->v.num, btyp)) { ret = TRUE; } ncx_clean_num(btyp, &num); break; case NCX_BT_DECIMAL64: ncx_init_num(&num); res = ncx_decode_dec64(def, typ_get_fraction_digits(val->typdef), &num); if (res == NO_ERR && !ncx_compare_nums(&num, &val->v.num, btyp)) { ret = TRUE; } ncx_clean_num(btyp, &num); break; case NCX_BT_BINARY: deflen = xml_strlen(def); len = b64_get_decoded_str_len( def, deflen ); if ( len ) { binbuff = m__getMem(len); if (!binbuff) { SET_ERROR(ERR_INTERNAL_MEM); return FALSE; } res = b64_decode(def, deflen, binbuff, len, &len); if (res == NO_ERR) { ret = memcmp(binbuff, val->v.binary.ustr, len) ? FALSE : TRUE; } m__free(binbuff); } break; case NCX_BT_STRING: case NCX_BT_INSTANCE_ID: case NCX_BT_LEAFREF: if (!xml_strcmp(def, val->v.str)) { ret = TRUE; } break; case NCX_BT_SLIST: case NCX_BT_BITS: case NCX_BT_IDREF: // treating possible malloc failure as if the value is // not set to its YANG default value testval = val_make_simval(val->typdef, val->nsid, val->name, def, &res); if (testval && res == NO_ERR) { ret = val_compare(val, testval); } val_free_value(testval); break; case NCX_BT_LIST: case NCX_BT_ANYDATA: case NCX_BT_ANYXML: case NCX_BT_CONTAINER: case NCX_BT_CHOICE: case NCX_BT_CASE: case NCX_BT_EXTERN: case NCX_BT_INTERN: /* not supported for default value */ break; default: SET_ERROR(ERR_INTERNAL_VAL); } val->flags |= VAL_FL_DEFVALSET; if (ret) { val->flags |= VAL_FL_DEFVAL; } else { val->flags &= ~VAL_FL_DEFVAL; } return ret; } /* val_is_default */ /******************************************************************** * FUNCTION val_is_real * * Check if the specified value is a real value * * return TRUE if not virtual or NCX_BT_EXTERN or NCX_BT_INTERN) * * INPUTS: * val == value to check * * RETURNS: * TRUE if the val is a real value * FALSE otherwise *********************************************************************/ boolean val_is_real (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return (val->getcb || val->btyp==NCX_BT_EXTERN || val->btyp==NCX_BT_INTERN) ? FALSE : TRUE; } /* val_is_real */ /******************************************************************** * FUNCTION val_get_parent_nsid * * Try to get the parent namespace ID * * INPUTS: * val == value to check * * RETURNS: * namespace ID of parent, or 0 if not found or not a value parent *********************************************************************/ xmlns_id_t val_get_parent_nsid (const val_value_t *val) { const val_value_t *v; #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif if (!val->parent) { return 0; } v = (const val_value_t *)val->parent; return v->nsid; } /* val_get_parent_nsid */ /******************************************************************** * FUNCTION val_instance_count * * Count the number of instances of the specified object name * in the parent value struct. This only checks the first * level under the parent, not the entire subtree * * * INPUTS: * val == value to check * modname == name of module which defines the object to count * NULL (do not check module names) * objname == name of object to count * * RETURNS: * number of instances found *********************************************************************/ uint32 val_instance_count (val_value_t *val, const xmlChar *modname, const xmlChar *objname) { val_value_t *chval; uint32 cnt; #ifdef DEBUG if (!val || !objname) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif cnt = 0; for (chval = val_get_first_child(val); chval != NULL; chval = val_get_next_child(chval)) { if (modname && xml_strcmp(modname, val_get_mod_name(chval))) { continue; } if (!xml_strcmp(objname, chval->name)) { cnt++; } } return cnt; } /* val_instance_count */ /******************************************************************** * FUNCTION val_set_extra_instance_errors * * mark ERR_NCX_EXTRA_VAL_INST errors for nodes > 'maxelems' * Count the number of instances of the specified object name * in the parent value struct. This only checks the first * level under the parent, not the entire subtree * Set the val-res status for all instances beyond the * specified 'maxelems' count to ERR_NCX_EXTRA_VAL_INST * * INPUTS: * val == value to check * modname == name of module which defines the object to count * NULL (do not check module names) * objname == name of object to count * maxelems == number of allowed instances * *********************************************************************/ void val_set_extra_instance_errors (val_value_t *val, const xmlChar *modname, const xmlChar *objname, uint32 maxelems) { val_value_t *chval; uint32 cnt; #ifdef DEBUG if (!val || !objname) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (maxelems == 0) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif cnt = 0; for (chval = val_get_first_child(val); chval != NULL; chval = val_get_next_child(chval)) { if (modname && xml_strcmp(modname, val_get_mod_name(chval))) { continue; } if (!xml_strcmp(objname, chval->name)) { if (++cnt > maxelems) { chval->res = ERR_NCX_EXTRA_VAL_INST; } } } } /* val_set_extra_instance_errors */ /******************************************************************** * FUNCTION val_need_quotes * * Check if a string needs to be quoted to be output * within a conf file or ncxcli stdout output * * INPUTS: * str == string to check * * RETURNS: * TRUE if double quoted string is needed * FALSE if not needed *********************************************************************/ boolean val_need_quotes (const xmlChar *str) { #ifdef DEBUG if (!str) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif /* any whitespace or newline needs quotes */ while (*str) { if (isspace(*str) || *str == '\n') { return TRUE; } str++; } return FALSE; } /* val_need_quotes */ /******************************************************************** * FUNCTION val_all_whitespace * * Check if a string is all whitespace * * INPUTS: * str == string to check * * RETURNS: * TRUE if string is all whitespace or empty length * FALSE if non-whitespace char found *********************************************************************/ boolean val_all_whitespace (const xmlChar *str) { #ifdef DEBUG if (!str) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif /* any whitespace or newline needs quotes */ while (*str) { if (!(isspace(*str) || *str == '\n')) { return FALSE; } str++; } return TRUE; } /* val_all_whitespace */ /******************************************************************** * FUNCTION val_match_metaval * * Match the specific attribute value and namespace ID * * INPUTS: * attr == attr to check * nsid == mamespace ID to match against * name == attribute name to match against * * RETURNS: * TRUE if attr is a match; FALSE otherwise *********************************************************************/ boolean val_match_metaval (const xml_attr_t *attr, xmlns_id_t nsid, const xmlChar *name) { #ifdef DEBUG if (!attr || !name) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif if (xml_strcmp(attr->attr_name, name)) { return FALSE; } if (attr->attr_ns) { return (attr->attr_ns==nsid); } else { /* unqualified match */ return TRUE; } } /* val_match_metaval */ /******************************************************************** * FUNCTION val_get_dirty_flag * * Get the dirty flag for this value node * * INPUTS: * val == value node to check * * RETURNS: * TRUE if value is dirty, false otherwise *********************************************************************/ boolean val_get_dirty_flag (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } #endif return (val->flags & VAL_FL_DIRTY) ? TRUE : FALSE; } /* val_get_dirty_flag */ /******************************************************************** * FUNCTION val_get_subtree_dirty_flag * * Get the subtree dirty flag for this value node * * INPUTS: * val == value node to check * * RETURNS: * TRUE if value is subtree dirty, false otherwise *********************************************************************/ boolean val_get_subtree_dirty_flag (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } #endif return (val->flags & VAL_FL_SUBTREE_DIRTY) ? TRUE : FALSE; } /* val_get_subtree_dirty_flag */ /******************************************************************** * FUNCTION val_set_dirty_flag * * Set the dirty flag for this value node * * INPUTS: * val == value node to check * * RETURNS: * TRUE if value is dirty, false otherwise *********************************************************************/ void val_set_dirty_flag (val_value_t *val) { if (!val) { return; } val->flags |= VAL_FL_DIRTY; val_value_t *parent = val->parent; while (parent && !obj_is_root(parent->obj)) { parent->flags |= VAL_FL_SUBTREE_DIRTY; parent = parent->parent; } } /* val_set_dirty_flag */ /******************************************************************** * FUNCTION val_clear_dirty_flag * * Clear the dirty flag for this value node * * INPUTS: * val == value node to check * * RETURNS: * TRUE if value is dirty, false otherwise *********************************************************************/ void val_clear_dirty_flag (val_value_t *val) { if (!val) { return; } val->flags &= ~VAL_FL_DIRTY; val_value_t *parent = val->parent; while (parent && !obj_is_root(parent->obj)) { parent->flags &= ~VAL_FL_SUBTREE_DIRTY; parent = parent->parent; } } /* val_clear_dirty_flag */ /******************************************************************** * FUNCTION val_dirty_subtree * * Check the dirty or subtree_dirty flag * * INPUTS: * val == value node to check * * RETURNS: * TRUE if value is dirty or any subtree may be dirty, false otherwise *********************************************************************/ boolean val_dirty_subtree (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } #endif return (val->flags & (VAL_FL_DIRTY | VAL_FL_SUBTREE_DIRTY)) ? TRUE : FALSE; } /* val_dirty_subtree */ /******************************************************************** * FUNCTION val_clean_tree * * Clear the dirty flag and the operation for all * nodes within a value struct * * INPUTS: * val == value node to clean * * OUTPUTS: * val and all its child nodes (if any) are cleaned * val->flags: VAL_FL_DIRTY bit cleared to 0 * val->editvars deleted * val->curparent: cleared to NULL *********************************************************************/ void val_clean_tree (val_value_t *val) { val_value_t *chval; #ifdef DEBUG if (!val || !val->obj) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (obj_is_data_db(val->obj)) { for (chval = val_get_first_child(val); chval != NULL; chval = val_get_next_child(chval)) { val_clean_tree(chval); } val->flags &= ~ (VAL_FL_DIRTY | VAL_FL_SUBTREE_DIRTY); val->editop = OP_EDITOP_NONE; free_editvars(val); } } /* val_clean_tree */ /******************************************************************** * FUNCTION val_get_nest_level * * Get the next level of the value * * INPUTS: * val == value node to check * * RETURNS: * nest level from the root *********************************************************************/ uint32 val_get_nest_level (val_value_t *val) { uint32 level; #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_VAL); return 0; } #endif level = 1; while (val->parent) { level++; val = val->parent; } return level; } /* val_get_nest_level */ /******************************************************************** * FUNCTION val_get_first_leaf * * Get the first leaf or leaflist node in the * specified value tree * * INPUTS: * val == value node to check * * RETURNS: * pointer to first leaf found within this val struct * pointer to val if val is a leaf *********************************************************************/ val_value_t * val_get_first_leaf (val_value_t *val) { val_value_t *child, *found; #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif if (obj_is_leafy(val->obj)) { return val; } else if (typ_has_children(val->btyp)) { for (child = (val_value_t *) dlq_firstEntry(&val->v.childQ); child != NULL; child = (val_value_t *)dlq_nextEntry(val)) { found = val_get_first_leaf(child); if (found) { return found; } } } return NULL; } /* val_get_first_leaf */ /******************************************************************** * FUNCTION val_get_mod_name * * Get the module name associated with this value node * * INPUTS: * val == value node to check * * RETURNS: * const pointer to module name string * NULL if not found *********************************************************************/ const xmlChar * val_get_mod_name (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif if (val->nsid) { return xmlns_get_module(val->nsid); } else if (val->obj) { return obj_get_mod_name(val->obj); } else { return NULL; } } /* val_get_mod_name */ /******************************************************************** * FUNCTION val_get_mod_prefix * * Get the module prefix associated with this value node * * INPUTS: * val == value node to check * * RETURNS: * const pointer to module name string * NULL if not found *********************************************************************/ const xmlChar * val_get_mod_prefix (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_VAL); return NULL; } #endif if (val->nsid) { return xmlns_get_ns_prefix(val->nsid); } else if (val->obj) { return obj_get_mod_prefix(val->obj); } else { return NULL; } } /* val_get_mod_prefix */ /******************************************************************** * FUNCTION val_get_nsid * * Get the namespace ID for the specified value node * * INPUTS: * val == value node to check * * RETURNS: * const pointer to module name string * NULL if not found *********************************************************************/ xmlns_id_t val_get_nsid (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_VAL); return 0; } #endif if (val->nsid) { return val->nsid; } else if (val->obj) { return obj_get_nsid(val->obj); } else { return 0; } } /* val_get_nsid */ /******************************************************************** * FUNCTION val_change_nsid * * Change the namespace ID fora value node and all its descendants * * INPUTS: * val == value node to change * nsid == new namespace ID to use * *********************************************************************/ void val_change_nsid (val_value_t *val, xmlns_id_t nsid) { val_value_t *child; #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_VAL); return; } #endif val->nsid = nsid; for (child = val_get_first_child(val); child != NULL; child = val_get_next_child(child)) { val_change_nsid(child, nsid); } } /* val_change_nsid */ /******************************************************************** * FUNCTION val_make_from_insertxpcb * * Make a val_value_t for a list, with the * child nodes for key leafs, specified in the * key attribute string given to the insert operation * * INPUTS: * sourceval == list val_value_t from the PDU with the insertxpcb * to process * status == address of return status (may be NULL, ignored) * * OUTPUTS: * if non-NULL: * *status == return status * * RETURNS: * malloced list val_value_t struct with converted value *********************************************************************/ val_value_t * val_make_from_insertxpcb (val_value_t *sourceval, status_t *res) { val_value_t *listval, *keyval; xpath_pcb_t *xpcb; const xmlChar *keyname, *keystring; boolean done; status_t myres; #ifdef DEBUG if (!sourceval) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif myres = NO_ERR; if (res) { *res = NO_ERR; } listval = val_new_value(); if (!listval) { if (res) { *res = ERR_INTERNAL_MEM; } return NULL; } val_init_from_template(listval, sourceval->obj); myres = val_new_editvars(sourceval); if (myres != NO_ERR) { val_free_value(listval); if (res) { *res = myres; } return NULL; } xpcb = sourceval->editvars->insertxpcb; if (!xpcb || !xpcb->tkc || xpcb->validateres != NO_ERR) { if (res) { *res = SET_ERROR(ERR_INTERNAL_VAL); } val_free_value(listval); return NULL; } tk_reset_chain(xpcb->tkc); done = FALSE; while (!done && myres == NO_ERR) { keyname = NULL; keystring = NULL; keyval = NULL; myres = xpath_parse_token(xpcb, TK_TT_LBRACK); if (myres != NO_ERR) { continue; } myres = TK_ADV(xpcb->tkc); if (myres != NO_ERR) { continue; } keyname = TK_CUR_VAL(xpcb->tkc); myres = xpath_parse_token(xpcb, TK_TT_EQUAL); if (myres != NO_ERR) { continue; } myres = TK_ADV(xpcb->tkc); if (myres != NO_ERR) { continue; } keystring = TK_CUR_VAL(xpcb->tkc); myres = xpath_parse_token(xpcb, TK_TT_RBRACK); if (myres != NO_ERR) { continue; } if (!keyname || !keystring) { myres = SET_ERROR(ERR_INTERNAL_VAL); continue; } keyval = val_make_string(val_get_nsid(sourceval), keyname, keystring); if (!keyval) { myres = ERR_INTERNAL_MEM; continue; } else { val_add_child(keyval, listval); } if (tk_next_typ(xpcb->tkc) != TK_TT_LBRACK) { done = TRUE; } } if (myres == NO_ERR) { myres = val_gen_index_chain(listval->obj, listval); } if (res) { *res = myres; } if (myres != NO_ERR) { val_free_value(listval); listval = NULL; } return listval; } /* val_make_from_insertxpcb */ /******************************************************************** * FUNCTION val_new_unique * * Malloc and initialize the fields in a val_unique_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ val_unique_t * val_new_unique (void) { val_unique_t *valuni; valuni = m__getObj(val_unique_t); if (!valuni) { return NULL; } (void)memset(valuni, 0x0, sizeof(val_unique_t)); return valuni; } /* val_new_unique */ /******************************************************************** * FUNCTION val_free_unique * * CLean and free a val_unique_t struct * * INPUTS: * valuni == val_unique struct to free *********************************************************************/ void val_free_unique (val_unique_t *valuni) { if (!valuni) { return; } if (valuni->pcb) { xpath_free_pcb(valuni->pcb); } m__free(valuni); } /* val_free_unique */ /******************************************************************** * FUNCTION val_get_typdef * * Get the typdef field for a value struct * * INPUTS: * val == val_value_t struct to use * * RETURNS: * pointer to the typdef or NULL if none *********************************************************************/ const typ_def_t * val_get_typdef (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return val->typdef; } /* val_get_typdef */ /******************************************************************** * FUNCTION val_set_by_default * * Check if the value was set by val_add_defaults * * INPUTS: * val == val_value_t struct to check * * RETURNS: * TRUE if set by default * FALSE if set explicitly by some user or the startup config *********************************************************************/ boolean val_set_by_default (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return (val->flags & (VAL_FL_DEFSET | VAL_FL_WITHDEF)) ? TRUE : FALSE; } /* val_set_by_default */ /******************************************************************** * FUNCTION val_has_withdef_default * * Check if the value contained the wd:default attribute * * INPUTS: * val == val_value_t struct to check * * RETURNS: * TRUE if wd:default was set to true * FALSE if wd:default attribute was not set to true *********************************************************************/ boolean val_has_withdef_default (const val_value_t *val) { #ifdef DEBUG if (val == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return (val->flags & VAL_FL_WITHDEF) ? TRUE : FALSE; } /* val_has_withdef_default */ /******************************************************************** * FUNCTION val_set_withdef_default * * Set the value flags as having the wd:default attribute * * INPUTS: * val == val_value_t struct to set * *********************************************************************/ void val_set_withdef_default (val_value_t *val) { #ifdef DEBUG if (val == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif val->flags |= VAL_FL_WITHDEF; } /* val_set_withdef_default */ /******************************************************************** * FUNCTION val_is_metaval * * Check if the value is a meta-val (XML attribute) * * INPUTS: * val == val_value_t struct to check * * RETURNS: * TRUE if val is a meta-val * FALSE if val is not a meta-val *********************************************************************/ boolean val_is_metaval (const val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif return (val->flags & VAL_FL_META) ? TRUE : FALSE; } /* val_is_metaval */ /******************************************************************** * FUNCTION val_move_chidren * * Move all the child nodes from src to dest * Source and dest must both be containers! * * INPUTS: * srcval == source val_value_t struct to move * destval == destination value struct ot use * *********************************************************************/ void val_move_children (val_value_t *srcval, val_value_t *destval) { val_value_t *childval; #ifdef DEBUG if (!srcval || !destval) { SET_ERROR(ERR_INTERNAL_PTR); return; } if (typ_is_simple(srcval->btyp) || typ_is_simple(destval->btyp)) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif /* set new parent */ for (childval = (val_value_t *) dlq_firstEntry(&srcval->v.childQ); childval != NULL; childval = (val_value_t *)dlq_nextEntry(childval)) { childval->parent = destval; } /* move all the entries at once */ dlq_block_enque(&srcval->v.childQ, &destval->v.childQ); } /* val_move_children */ /******************************************************************** * FUNCTION val_cvt_generic * * Convert all the database object pointers to * generic object pointers to decouple a user * variable in yangcli from the server-specific * object definition (which goes away when the * session is terminated) * * !!! Need to assume the val->obj pointer is already * !!! invalid. This can happen to yangcli when a * !!! session is dropped and there are vars that * !!! reference YANG objects from the session * * INPUTS: * val == val_value_t struct to convert to generic * * RETURNS: * status *********************************************************************/ status_t val_cvt_generic (val_value_t *val) { val_value_t *childval; val_index_t *in; xmlChar *buffer; status_t res; uint32 len; boolean haschildren; #ifdef DEBUG if (val == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (val->obj == NULL) { /* leave this object-less value alone */ return NO_ERR; } res = NO_ERR; haschildren = typ_has_children(val->btyp); if (typ_is_string(val->btyp)) { val->obj = ncx_get_gen_string(); if (val->xpathpcb) { xpath_free_pcb(val->xpathpcb); val->xpathpcb = NULL; } } else if (val->btyp == NCX_BT_ANYDATA || val->btyp == NCX_BT_ANYXML) { /* !!! this should not happen if agt/mgr_val_parse used * !!! parse_any will set the val->btyp to container */ val->obj = ncx_get_gen_anyxml(); } else { switch (val->btyp) { case NCX_BT_BINARY: val->obj = ncx_get_gen_binary(); break; case NCX_BT_EMPTY: val->obj = ncx_get_gen_empty(); break; case NCX_BT_CONTAINER: val->obj = ncx_get_gen_container(); break; case NCX_BT_LIST: while (!dlq_empty(&val->indexQ)) { in = (val_index_t *)dlq_deque(&val->indexQ); m__free(in); } val->obj = ncx_get_gen_container(); break; default: len = 0; res = val_sprintf_simval_nc(NULL, val, &len); if (res != NO_ERR) { return res; } buffer = m__getMem(len+1); if (!buffer) { return ERR_INTERNAL_MEM; } res = val_sprintf_simval_nc(buffer, val, &len); if (res == NO_ERR) { clean_value(val, FALSE); val->v.str = buffer; val->btyp = NCX_BT_STRING; val->obj = ncx_get_gen_string(); } else { m__free(buffer); return res; } } } /* dive down into all the child nodest */ if (haschildren) { for (childval = (val_value_t *) dlq_firstEntry(&val->v.childQ); childval != NULL; childval = (val_value_t *)dlq_nextEntry(childval)) { res = val_cvt_generic(childval); } } return res; } /* val_cvt_generic */ /******************************************************************** * FUNCTION val_set_pcookie * * Set the SIL pointer cookie in the editvars for * the specified value node * * INPUTS: * val == val_value_t struct to set * pcookie == pointer cookie value to set * * RETURNS: * status *********************************************************************/ status_t val_set_pcookie (val_value_t *val, void *pcookie) { #ifdef DEBUG if (val == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (val->editvars == NULL) { status_t res = val_new_editvars(val); if (res != NO_ERR) { return res; } } val->editvars->pcookie = pcookie; return NO_ERR; } /* val_set_pcookie */ /******************************************************************** * FUNCTION val_set_icookie * * Set the SIL integer cookie in the editvars for * the specified value node * * INPUTS: * val == val_value_t struct to set * icookie == integer cookie value to set * * RETURNS: * status *********************************************************************/ status_t val_set_icookie (val_value_t *val, int icookie) { #ifdef DEBUG if (val == NULL) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (val->editvars == NULL) { status_t res = val_new_editvars(val); if (res != NO_ERR) { return res; } } val->editvars->icookie = icookie; return NO_ERR; } /* val_set_icookie */ /******************************************************************** * FUNCTION val_get_pcookie * * Get the SIL pointer cookie in the editvars for * the specified value node * * INPUTS: * val == val_value_t struct to set * * RETURNS: * pointer cookie value or NULL if none *********************************************************************/ void * val_get_pcookie (val_value_t *val) { #ifdef DEBUG if (val == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (val->editvars == NULL) { return NULL; } return val->editvars->pcookie; } /* val_get_pcookie */ /******************************************************************** * FUNCTION val_get_icookie * * Get the SIL integer cookie in the editvars for * the specified value node * * INPUTS: * val == val_value_t struct to set * * RETURNS: * integer cookie value or 0 if none *********************************************************************/ int val_get_icookie (val_value_t *val) { #ifdef DEBUG if (val == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return 0; } #endif if (val->editvars == NULL) { return 0; } return val->editvars->icookie; } /* val_get_icookie */ /******************************************************************** * FUNCTION val_delete_default_leaf * * Do the internal work to convert a leaf to its YANG default value * * INPUTS: * val == val_value_t struct to use * * RETURNS: * status *********************************************************************/ status_t val_delete_default_leaf (val_value_t *val) { if ( !val || !val->obj ) { return SET_ERROR(ERR_INTERNAL_PTR); } const xmlChar *defval = obj_get_default(val->obj); if ( !defval ) { return SET_ERROR(ERR_INTERNAL_VAL); } clean_value(val, FALSE); status_t res = val_set_simval_str(val, val->typdef, val->nsid, val->name, xml_strlen(val->name), defval); val->flags |= VAL_FL_DEFSET; return res; } /* val_delete_default_leaf */ /******************************************************************** * FUNCTION val_force_empty * * Convert a simple node to an empty type * * INPUTS: * val == val_value_t struct to use * *********************************************************************/ void val_force_empty (val_value_t *val) { #ifdef DEBUG if (val == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (!typ_is_simple(val->btyp)) { SET_ERROR(ERR_NCX_WRONG_TYPE); return; } clean_value(val, FALSE); val->btyp = NCX_BT_EMPTY; val->v.boo = TRUE; } /* val_force_empty */ /******************************************************************** * FUNCTION val_move_fields_for_xml * * Move or copy the internal fields from one val to another * for xml_wr purposes * * INPUTS: * srcval == source val_value_t struct to move from * destval == destination val to move to * movemeta == TRUE if metaQ should be transferred *********************************************************************/ void val_move_fields_for_xml (val_value_t *srcval, val_value_t *destval, boolean movemeta) { #ifdef DEBUG if (srcval == NULL || destval == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif destval->parent = srcval->parent; destval->dataclass = srcval->dataclass; if (movemeta) { dlq_block_enque(&srcval->metaQ, &destval->metaQ); } } /* val_move_fields_for_xml */ /******************************************************************** * FUNCTION val_get_first_key * * Get the first key record if this is a list with a key-stmt * * INPUTS: * val == value node to check * *********************************************************************/ val_index_t * val_get_first_key (val_value_t *val) { #ifdef DEBUG if (!val) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif if (val->btyp != NCX_BT_LIST) { return NULL; } return (val_index_t *)dlq_firstEntry(&val->indexQ); } /* val_get_first_key */ /******************************************************************** * FUNCTION val_get_next_key * * Get the next key record if this is a list with a key-stmt * * INPUTS: * curkey == current key node * *********************************************************************/ val_index_t * val_get_next_key (val_index_t *curkey) { #ifdef DEBUG if (!curkey) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif return (val_index_t *)dlq_nextEntry(curkey); } /* val_get_next_key */ /******************************************************************** * FUNCTION val_remove_key * * Remove a key pointer because the key is invalid * Free the key pointer * * INPUTS: * keyval == value node to find, remove and free * *********************************************************************/ void val_remove_key (val_value_t *keyval) { assert(keyval && "keyval is NULL!" ); val_value_t *parent = keyval->parent; val_index_t *valin = (val_index_t *)dlq_firstEntry(&parent->indexQ); val_index_t *nextvalin = NULL; for (; valin != NULL; valin = nextvalin) { nextvalin = (val_index_t *)dlq_nextEntry(valin); if (valin->val == keyval) { dlq_remove(valin); m__free(valin); return; } } } /* val_remove_key */ /******************************************************************** * FUNCTION val_new_deleted_value * * Malloc and initialize the fields in a val_value_t to be used * as a deleted node marker * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ val_value_t * val_new_deleted_value (void) { val_value_t *val; val = m__getObj(val_value_t); if (!val) { return NULL; } (void)memset(val, 0x0, sizeof(val_value_t)); dlq_createSQue(&val->metaQ); dlq_createSQue(&val->indexQ); val->flags |= VAL_FL_DELETED; return val; } /* val_new_deleted_value */ /******************************************************************** * FUNCTION val_new_editvars * * Malloc and initialize the val->editvars field * * INPUTS: * val == val_value_t data structure to use * * OUTPUTS: * val->editvars is malloced and initialized * * RETURNS: * status *********************************************************************/ status_t val_new_editvars (val_value_t *val) { val_editvars_t *editvars; if (val->editvars) { return SET_ERROR(ERR_NCX_DATA_EXISTS); } editvars = m__getObj(val_editvars_t); if (!editvars) { return ERR_INTERNAL_MEM; } memset(editvars, 0x0, sizeof(val_editvars_t)); val->editvars = editvars; #ifdef VAL_EDITVARS_DEBUG log_debug3("\n\nval_new_editvars: %u = %p\n", ++editvars_malloc, editvars); #endif return NO_ERR; } /* val_new_editvars */ /******************************************************************** * FUNCTION val_free_editvars * * Free the editing variables for the value node * * INPUTS: * val == val_value_t data structure to use * * OUTPUTS: * val->editvars is freed if set * val->editop set to OP_EDITOP_NONE *********************************************************************/ void val_free_editvars (val_value_t *val) { if (val == NULL) { return; } free_editvars(val); val->editop = OP_EDITOP_NONE; } /* val_free_editvars */ /* END file val.c */ yuma123_2.14/netconf/src/ncx/val.h0000664000175000017500000033047614770023131017106 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2013 - 2016, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_val #define _H_val /* FILE: val.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Value Node Basic Support Value nodes used in thoughout the system are complex 'automation depositories', which contain all the glue to automate the NETCONF functions. Almost all value node variants provide user callback hooks for CRUD operations on the node. The read operations are usually driven from centrally stored data, unless the value node is a 'virtual' value. Basic Value Node Usage: ----------------------- 1a) Malloc a new value with val_new_value() or 1b) Initialize a static val_value_t with val_init_value 2) Bind the value to an object template: val_init_from_template or use val_make_simval to combine steps 1a and 2 3) set simple values with various functions, such as val_set_simval 4) When constructing complex values, use val_add_child to add them to the parent 5a) Use val_free_value to free the memory for a value 5b) Use val_clean_value to clean and reuse a value struct Internal Value Nodes -------------------- A special developer-level feature to assign arbitrary internal values, not in the encoded format. Not used within the configuration database. (More TBD) External Value Nodes -------------------- The yangcli program allows the user to assign values from user and system defined script variables to a value node. Not used within the configuration database. (More TBD) Virtual Value Nodes ------------------- If a value node does not store its data locally, then it is called a virtual node. Callback functions are used for almost all protocol operation support. ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 19-dec-05 abb Begun 21jul08 abb start obj-based rewrite */ #include #include #include "dlq.h" #include "ncxconst.h" #include "ncxtypes.h" #include "op.h" #include "plock_cb.h" #include "status.h" #include "typ.h" #include "xml_util.h" #include "xmlns.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* max number of concurrent partial locks by the same session */ #define VAL_MAX_PLOCKS 4 /* maximum number of bytes in a number string */ #define VAL_MAX_NUMLEN NCX_MAX_NUMLEN /* constants used in generating C and Xpath instance ID strings */ #define VAL_BINDEX_CH '[' #define VAL_EINDEX_CH ']' #define VAL_BENUM_CH '(' #define VAL_EENUM_CH ')' #define VAL_INST_SEPCH '.' #define VAL_INDEX_SEPCH ',' #define VAL_INDEX_CLI_SEPCH ' ' #define VAL_QUOTE_CH '\'' #define VAL_DBLQUOTE_CH '\"' #define VAL_EQUAL_CH '=' #define VAL_XPATH_SEPCH '/' #define VAL_XPATH_INDEX_SEPSTR (const xmlChar *)"][" #define VAL_XPATH_INDEX_SEPLEN 2 /* display instead of readl password contents */ #define VAL_PASSWORD_STRING (const xmlChar *)"****" /* val_value_t flags field */ /* if set the duplicates-ok test has been done */ #define VAL_FL_DUPDONE bit0 /* if set the duplicates-ok test was OK */ #define VAL_FL_DUPOK bit1 /* if set, this value was added by val_add_defaults */ #define VAL_FL_DEFSET bit2 /* if set, value is actually for an XML attribute */ #define VAL_FL_META bit3 /* if set, value has been edited or added */ #define VAL_FL_DIRTY bit4 /* if set, value is a list which has unique-stmt already failed */ #define VAL_FL_UNIDONE bit5 /* if set, value has been checked to see if it is the default value */ #define VAL_FL_DEFVALSET bit6 /* if set, value is set to the YANG default value; * only use if VAL_FL_DEFVALSET is 1 */ #define VAL_FL_DEFVAL bit7 /* if set, PDU value had the with-defaults wd:attribute * set to true */ #define VAL_FL_WITHDEF bit8 /* if set, value has been deleted or moved and awaiting commit or rollback */ #define VAL_FL_DELETED bit9 /* if set, there was an edit operation in a descendant node; * Used by agt_val_root_check to prune trees for faster processing */ #define VAL_FL_SUBTREE_DIRTY bit10 /* set the virtualval lifetime to 3 seconds */ #define VAL_VIRTUAL_CACHE_TIME 3 /* macros to access simple value types */ #define VAL_BOOL(V) ((V)->v.boo) #define VAL_EMPTY(V) ((V)->v.boo) #define VAL_DOUBLE(V) ((V)->v.num.d) #define VAL_STRING(V) ((V)->v.str) #define VAL_BINARY(V) ((V)->v.str) #define VAL_ENU(V) (&(V)->v.enu) #define VAL_ENUM(V) ((V)->v.enu.val) #define VAL_ENUM_NAME(V) ((V)->v.enu.name) #define VAL_FLAG(V) ((V)->v.boo) #define VAL_LONG(V) ((V)->v.num.l) #define VAL_INT(V) ((V)->v.num.i) #define VAL_INT8(V) ((int8)((V)->v.num.i)) #define VAL_INT16(V) ((int16)((V)->v.num.i)) #define VAL_INT32(V) ((V)->v.num.i) #define VAL_INT64(V) ((V)->v.num.l) #define VAL_STR(V) ((V)->v.str) #define VAL_INSTANCE_ID(V) ((V)->v.str) #define VAL_IDREF(V) (&(V)->v.idref) #define VAL_IDREF_NSID(V) ((V)->v.idref.nsid) #define VAL_IDREF_NAME(V) ((V)->v.idref.name) #define VAL_UINT(V) ((V)->v.num.u) #define VAL_UINT8(V) ((uint8)((V)->v.num.u)) #define VAL_UINT16(V) ((uint16)((V)->v.num.u)) #define VAL_UINT32(V) ((V)->v.num.u) #define VAL_UINT64(V) ((V)->v.num.ul) #define VAL_ULONG(V) ((V)->v.num.ul) #define VAL_DEC64(V) ((V)->v.num.dec.val) #define VAL_LIST(V) ((V)->v.list) #define VAL_BITS VAL_LIST #define VAL_EXTERN(V) ((V)->v.fname) #define VAL_IS_DELETED(V) ((V)->flags & VAL_FL_DELETED) #define VAL_MARK_DELETED(V) (V)->flags |= VAL_FL_DELETED #define VAL_UNMARK_DELETED(V) (V)->flags &= ~VAL_FL_DELETED /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* one QName for the NCX_BT_IDREF value */ typedef struct val_idref_t_ { xmlns_id_t nsid; /* if nsid == INV_ID then this is entire QName */ xmlChar *name; const ncx_identity_t *identity; /* ID back-ptr if found */ } val_idref_t; /* one set of edit-in-progress variables for one value node */ typedef struct val_editvars_t_ { /* these fields are only used in modified values before they are * actually added to the config database (TBD: move into struct) * curparent == parent of curnode for merge */ struct val_value_t_ *curparent; //op_editop_t editop; /* effective edit operation */ op_insertop_t insertop; /* YANG insert operation */ xmlChar *insertstr; /* saved value or key attr */ struct xpath_pcb_t_ *insertxpcb; /* key attr for insert */ struct val_value_t_ *insertval; /* back-ptr */ boolean iskey; /* T: key, F: value */ boolean operset; /* nc:operation here */ void *pcookie; /* user pointer cookie */ int icookie; /* user integer cookie */ } val_editvars_t; typedef struct val_virtual_cb_node_t_ { dlq_hdr_t qhdr; void* getcb; } val_virt_getcb_node_t; /* one value to match one type */ typedef struct val_value_t_ { dlq_hdr_t qhdr; /* common fields */ struct obj_template_t_ *obj; /* bptr to object def */ typ_def_t *typdef; /* bptr to typdef if leaf */ const xmlChar *name; /* back pointer to elname */ xmlChar *dname; /* AND malloced name if needed */ struct val_value_t_ *parent; /* back-ptr to parent if any */ xmlns_id_t nsid; /* namespace ID for this node */ ncx_btype_t btyp; /* base type of this value */ uint32 flags; /* internal status flags */ ncx_data_class_t dataclass; /* config or state data */ /* YANG does not support user-defined meta-data but NCX does. * The , and operations * use attributes in the RPC parameters, the metaQ is still used * * The ncx:metadata extension allows optional attributes * to be added to object nodes for anyxml, leaf, leaf-list, * list, and container nodes. The config property will * be inherited from the object that contains the metadata * * This is used mostly for RPC input parameters * and is strongly discouraged. Full edit-config * support is not provided for metdata */ dlq_hdr_t metaQ; /* Q of val_value_t */ /* value editing variables */ val_editvars_t *editvars; /* edit-vars from attrs */ op_editop_t editop; /* needed for all edits */ status_t res; /* validation result */ /* Used by Agent only: * if this field is non-NULL, then the entire value node * is actually a placeholder for a dynamic read-only object * and all read access is done via this callback function; * the real data type is getcb_fn_t * */ void *getcb; /* if this field is non-NULL, then a malloced value struct * representing the real value retrieved by * val_get_virtual_value, is cached here for / */ struct val_value_t_ *virtualval; time_t cachetime; /* these fields are used for NCX_BT_LIST */ struct val_index_t_ *index; /* back-ptr/flag in use as index */ dlq_hdr_t indexQ; /* Q of val_index_t or ncx_filptr_t */ /* this field is used for NCX_BT_CHOICE * If set, the object path for this node is really: * $this --> casobj --> casobj.parent --> $this.parent * the OBJ_TYP_CASE and OBJ_TYP_CHOICE nodes are skipped * inside an XML instance document */ struct obj_template_t_ *casobj; /* these fields are for NCX_BT_LEAFREF * NCX_BT_INSTANCE_ID, or tagged ncx:xpath * value stored in v union as a string */ struct xpath_pcb_t_ *xpathpcb; /* back-ptr to the partial locks that are held * against this node */ plock_cb_t *plock[VAL_MAX_PLOCKS]; /* union of all the NCX-specific sub-types * note that the following invisible constructs should * never show up in this struct: * NCX_BT_CHOICE * NCX_BT_CASE * NCX_BT_UNION */ union v_ { /* complex types have a Q of val_value_t representing * the child nodes with values * NCX_BT_CONTAINER * NCX_BT_LIST */ dlq_hdr_t childQ; /* Numeric data types: * NCX_BT_INT8, NCX_BT_INT16, * NCX_BT_INT32, NCX_BT_INT64 * NCX_BT_UINT8, NCX_BT_UINT16 * NCX_BT_UINT32, NCX_BT_UINT64 * NCX_BT_DECIMAL64, NCX_BT_FLOAT64 */ ncx_num_t num; /* String data types: * NCX_BT_STRING * NCX_BT_INSTANCE_ID */ ncx_str_t str; val_idref_t idref; ncx_binary_t binary; /* NCX_BT_BINARY */ ncx_list_t list; /* NCX_BT_BITS, NCX_BT_SLIST */ boolean boo; /* NCX_BT_EMPTY, NCX_BT_BOOLEAN */ ncx_enum_t enu; /* NCX_BT_UNION, NCX_BT_ENUM */ xmlChar *fname; /* NCX_BT_EXTERN */ xmlChar *intbuff; /* NCX_BT_INTERN */ } v; /* Used by Agent only: * Support for virtual containers with multiple callbacks filling different * subsets of the children. Usefull in case of e.g. * /interfaces-state/interface/statistics being filled from different SILs */ dlq_hdr_t getcbQ; /* Q of val_value_t */ } val_value_t; /* Struct marking the parsing of an instance identifier * The position of this record in the val_value_t indexQ * represents the order the identifers were parsed * Since table and container data structures must always * appear in the specified field order, this will be the * same order for all well-formed entries. * * Each of these records will point to one nested val_value_t * record in the val_value_t childQ */ typedef struct val_index_t_ { dlq_hdr_t qhdr; val_value_t *val; /* points to a child node */ } val_index_t; /* one unique-stmt component test value node */ typedef struct val_unique_t_ { dlq_hdr_t qhdr; struct xpath_pcb_t_ *pcb; // live XPath CB w/ result } val_unique_t; /* test callback function to check if a value node * should be cloned * * INPUTS: * val == value node to check * * RETURNS: * TRUE if OK to be cloned * FALSE if not OK to be cloned (skipped instead) */ typedef boolean (*val_test_fn_t) (const val_value_t *val); /* child or descendant node search walker function * * INPUTS: * val == value node found in descendant search * cookie1 == cookie1 value passed to start of walk * cookie2 == cookie2 value passed to start of walk * * RETURNS: * TRUE if walk should continue * FALSE if walk should terminate */ typedef boolean (*val_walker_fn_t) (val_value_t *val, void *cookie1, void *cookie2); typedef enum val_dumpvalue_mode_t_ { DUMP_VAL_NONE, DUMP_VAL_STDOUT, DUMP_VAL_LOG, DUMP_VAL_ALT_LOG } val_dumpvalue_mode_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION val_new_value * * Malloc and initialize the fields in a val_value_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern val_value_t * val_new_value (void); /******************************************************************** * FUNCTION val_init_complex * * Initialize the fields in a complex val_value_t * this is deprecated and should only be called * by val_init_from_template * * MUST CALL val_new_value FIRST * * INPUTS: * val == pointer to the malloced struct to initialize *********************************************************************/ extern void val_init_complex (val_value_t *val, ncx_btype_t btyp); /******************************************************************** * FUNCTION val_init_virtual * * Special function to initialize a virtual value node * * MUST CALL val_new_value FIRST * * INPUTS: * val == pointer to the malloced struct to initialize * cbfn == get callback function to use * obj == object template to use *********************************************************************/ extern void val_init_virtual (val_value_t *val, void *cbfn, struct obj_template_t_ *obj); /******************************************************************** * FUNCTION val_init_from_template * * Initialize a value node from its object template * * MUST CALL val_new_value FIRST * * INPUTS: * val == pointer to the initialized value struct to bind * obj == object template to use *********************************************************************/ extern void val_init_from_template (val_value_t *val, struct obj_template_t_ *obj); /******************************************************************** * FUNCTION val_free_value * * Scrub the memory in a val_value_t by freeing all * the sub-fields and then freeing the entire struct itself * The struct must be removed from any queue it is in before * this function is called. * * INPUTS: * val == val_value_t to delete *********************************************************************/ extern void val_free_value (val_value_t *val); /******************************************************************** * FUNCTION val_set_name * * Set (or reset) the name of a value struct * * INPUTS: * val == val_value_t data structure to check * name == name string to set * namelen == length of name string *********************************************************************/ extern void val_set_name (val_value_t *val, const xmlChar *name, uint32 namelen); /******************************************************************** * FUNCTION val_force_dname * * Set (or reset) the name of a value struct * Set all descendant nodes as well * Force dname to be used, not object name backptr * * INPUTS: * val == val_value_t data structure to check * name == name string to set * namelen == length of name string * * RETURNS: * status *********************************************************************/ extern status_t val_force_dname (val_value_t *val); /******************************************************************** * FUNCTION val_set_qname * * Set (or reset) the name and namespace ID of a value struct * * INPUTS: * val == val_value_t data structure to check * nsid == namespace ID to set * name == name string to set * namelen == length of name string *********************************************************************/ extern void val_set_qname (val_value_t *val, xmlns_id_t nsid, const xmlChar *name, uint32 namelen); /******************************************************************** * FUNCTION val_string_ok * * Check a string to make sure the value is valid based * on the restrictions in the specified typdef * * INPUTS: * typdef == typ_def_t for the designated string type * btyp == basetype of the string * strval == string value to check * * RETURNS: * status *********************************************************************/ extern status_t val_string_ok (typ_def_t *typdef, ncx_btype_t btyp, const xmlChar *strval); /******************************************************************** * FUNCTION val_string_ok_errinfo * * retrieve the YANG custom error info if any * Check a string to make sure the value is valid based * on the restrictions in the specified typdef * Retrieve the configured error info struct if any error * * INPUTS: * typdef == typ_def_t for the designated string type * btyp == basetype of the string * strval == string value to check * errinfo == address of return errinfo block (may be NULL) * * OUTPUTS: * if non-NULL: * *errinfo == error record to use if return error * * RETURNS: * status *********************************************************************/ extern status_t val_string_ok_errinfo (typ_def_t *typdef, ncx_btype_t btyp, const xmlChar *strval, ncx_errinfo_t **errinfo); /******************************************************************** * FUNCTION val_string_ok_ex * * retrieve the YANG custom error info if any * Check a string to make sure the value is valid based * on the restrictions in the specified typdef * Retrieve the configured error info struct if any error * * INPUTS: * typdef == typ_def_t for the designated string type * btyp == basetype of the string * strval == string value to check * errinfo == address of return errinfo block (may be NULL) * logerrors == TRUE to log errors * == FALSE to not log errors (use for NCX_BT_UNION) * OUTPUTS: * if non-NULL: * *errinfo == error record to use if return error * * RETURNS: * status *********************************************************************/ extern status_t val_string_ok_ex (typ_def_t *typdef, ncx_btype_t btyp, const xmlChar *strval, ncx_errinfo_t **errinfo, boolean logerrors); /******************************************************************** * FUNCTION val_list_ok * * Check a list to make sure the all the strings are valid based * on the specified typdef * * validate all the ncx_lmem_t entries in the list * against the specified typdef. Mark any errors * in the ncx_lmem_t flags field of each member * in the list with an error * * INPUTS: * typdef == typ_def_t for the designated list type * btyp == base type (NCX_BT_SLIST or NCX_BT_BITS) * list == list struct with ncx_lmem_t structs to check * * OUTPUTS: * If return other than NO_ERR: * each list->lmem.flags field may contain bits set * for errors: * NCX_FL_RANGE_ERR: size out of range * NCX_FL_VALUE_ERR value not permitted by value set, * or pattern * RETURNS: * status *********************************************************************/ extern status_t val_list_ok (typ_def_t *typdef, ncx_btype_t btyp, ncx_list_t *list); /******************************************************************** * FUNCTION val_list_ok_errinfo * * Check a list to make sure the all the strings are valid based * on the specified typdef * * INPUTS: * typdef == typ_def_t for the designated list type * btyp == base type (NCX_BT_SLIST or NCX_BT_BITS) * list == list struct with ncx_lmem_t structs to check * errinfo == address of return rpc-error info struct * * OUTPUTS: * If return other than NO_ERR: * *errinfo contains the YANG specified error info, if any* * each list->lmem.flags field may contain bits set * for errors: * NCX_FL_RANGE_ERR: size out of range * NCX_FL_VALUE_ERR value not permitted by value set, * or pattern * RETURNS: * status *********************************************************************/ extern status_t val_list_ok_errinfo (typ_def_t *typdef, ncx_btype_t btyp, ncx_list_t *list, ncx_errinfo_t **errinfo); /******************************************************************** * FUNCTION val_enum_ok * * Check an enumerated integer string to make sure the value * is valid based on the specified typdef * * INPUTS: * typdef == typ_def_t for the designated enum type * enumval == enum string value to check * retval == pointer to return integer variable * retstr == pointer to return string name variable * * OUTPUTS: * *retval == integer value of enum * *retstr == pointer to return string name variable * * RETURNS: * status *********************************************************************/ extern status_t val_enum_ok (typ_def_t *typdef, const xmlChar *enumval, int32 *retval, const xmlChar **retstr); /******************************************************************** * FUNCTION val_bit_ok * * Check a bit name is valid for the typedef * * INPUTS: * typdef == typ_def_t for the designated bits type * bitname == bit name value to check * position == address of return bit struct position value * * OUTPUTS: * if non-NULL: * *position == bit position value * * RETURNS: * status *********************************************************************/ extern status_t val_bit_ok (const typ_def_t *typdef, const xmlChar *bitname, uint32 *position); /******************************************************************** * FUNCTION val_idref_ok * * Check if an identityref QName is valid for the typedef * The QName must match an identity that has the same base * as specified in the typdef * * INPUTS: * typdef == typ_def_t for the designated identityref type * qname == QName or local-name string to check * nsid == namespace ID from XML node for NS of QName * this NSID will be used and not the prefix in qname * it was parsed in the XML node and is not a module prefix * name == address of return local name part of QName * id == address of return identity, if found * * OUTPUTS: * if non-NULL: * *name == pointer into the qname string at the start of * the local name part * *id == pointer to ncx_identity_t found * * RETURNS: * status *********************************************************************/ extern status_t val_idref_ok (typ_def_t *typdef, const xmlChar *qname, xmlns_id_t nsid, const xmlChar **name, const ncx_identity_t **id); /******************************************************************** * FUNCTION val_parse_idref * * Parse a CLI BASED identityref QName into its various parts * * INPUTS: * mod == module containing the default-stmt (or NULL if N/A) * qname == QName or local-name string to parse * nsid == address of return namespace ID of the module * indicated by the prefix. If mod==NULL then * a prefix MUST be present * name == address of return local name part of QName * id == address of return identity, if found * * OUTPUTS: * if non-NULL: * *nsid == namespace ID for the prefix part of the QName * *name == pointer into the qname string at the start of * the local name part * *id == pointer to ncx_identity_t found (if any, not an error) * * RETURNS: * status *********************************************************************/ extern status_t val_parse_idref (ncx_module_t *mod, const xmlChar *qname, xmlns_id_t *nsid, const xmlChar **name, ncx_identity_t **id); /******************************************************************** * FUNCTION val_range_ok * * Check a number to see if it is in range or not * Could be a number or size range * * INPUTS: * typdef == typ_def_t for the simple type to check * btyp == base type of num * num == number to check * * RETURNS: * status *********************************************************************/ extern status_t val_range_ok (typ_def_t *typdef, ncx_btype_t btyp, const ncx_num_t *num); /******************************************************************** * FUNCTION val_range_ok_errinfo * * Check a number to see if it is in range or not * Could be a number or size range * * INPUTS: * typdef == typ_def_t for the simple type to check * btyp == base type of num * num == number to check * errinfo == address of return error struct * * OUTPUTS: * if non-NULL: * *errinfo == errinfo record on error exit * * RETURNS: * status *********************************************************************/ extern status_t val_range_ok_errinfo (typ_def_t *typdef, ncx_btype_t btyp, const ncx_num_t *num, ncx_errinfo_t **errinfo); /******************************************************************** * FUNCTION val_pattern_ok * * Check a string against all the patterns in a big AND expression * * INPUTS: * typdef == typ_def_t for the designated enum type * strval == string value to check * * RETURNS: * NO_ERR if pattern OK or no patterns found to check; error otherwise *********************************************************************/ extern status_t val_pattern_ok (typ_def_t *typdef, const xmlChar *strval); /******************************************************************** * FUNCTION val_pattern_ok_errinfo * * Check a string against all the patterns in a big AND expression * * INPUTS: * typdef == typ_def_t for the designated enum type * strval == string value to check * errinfo == address of return errinfo struct for err-pattern * * OUTPUTS: * *errinfo set to error info struct if any, and if error exit * * RETURNS: * NO_ERR if pattern OK or no patterns found to check; * error otherwise, and *errinfo will be set if the pattern * that failed has any errinfo defined in it *********************************************************************/ extern status_t val_pattern_ok_errinfo (typ_def_t *typdef, const xmlChar *strval, ncx_errinfo_t **errinfo); /******************************************************************** * FUNCTION val_simval_ok * * check any simple type to see if it is valid, * but do not retrieve the value; used to check the * default parameter for example * * INPUTS: * typdef == typ_def_t for the simple type to check * simval == value string to check (NULL means empty string) * * RETURNS: * status *********************************************************************/ extern status_t val_simval_ok (typ_def_t *typdef, const xmlChar *simval); /******************************************************************** * FUNCTION val_simval_ok_errinfo * * check any simple type to see if it is valid, * but do not retrieve the value; used to check the * default parameter for example * * INPUTS: * typdef == typ_def_t for the simple type to check * simval == value string to check (NULL means empty string) * errinfo == address of return error struct * * OUTPUTS: * if non-NULL: * *errinfo == error struct on error exit * * RETURNS: * status *********************************************************************/ extern status_t val_simval_ok_errinfo (typ_def_t *typdef, const xmlChar *simval, ncx_errinfo_t **errinfo); /******************************************************************** * FUNCTION val_simval_ok_ex * * check any simple type to see if it is valid, * but do not retrieve the value; used to check the * default parameter for example * * INPUTS: * typdef == typ_def_t for the simple type to check * simval == value string to check (NULL means empty string) * errinfo == address of return error struct * mod == module in progress to use for idref and other * strings with prefixes in them * OUTPUTS: * if non-NULL: * *errinfo == error struct on error exit * * RETURNS: * status *********************************************************************/ extern status_t val_simval_ok_ex (typ_def_t *typdef, const xmlChar *simval, ncx_errinfo_t **errinfo, ncx_module_t *mod); /******************************************************************** * FUNCTION val_simval_ok_max * * check any simple type to see if it is valid, * but do not retrieve the value; used to check the * default parameter for example * * INPUTS: * typdef == typ_def_t for the simple type to check * simval == value string to check (NULL means empty string) * errinfo == address of return error struct * mod == module in progress to use for idref and other * strings with prefixes in them * logerrors == TRUE to log errors; FALSE to not log errors * (use FALSE for NCX_BT_UNION) * OUTPUTS: * if non-NULL: * *errinfo == error struct on error exit * * RETURNS: * status *********************************************************************/ extern status_t val_simval_ok_max (typ_def_t *typdef, const xmlChar *simval, ncx_errinfo_t **errinfo, ncx_module_t *mod, boolean logerrors); /******************************************************************** * FUNCTION val_union_ok * * Check a union to make sure the string is valid based * on the specified typdef, and convert the string to * an NCX internal format * * INPUTS: * typdef == typ_def_t for the designated union type * strval == the value to check against the member typ defs * retval == pointer to output struct for converted value * * OUTPUTS: * If return NO_ERR: * retval->str or retval->num will be set with the converted value * * RETURNS: * status *********************************************************************/ extern status_t val_union_ok (typ_def_t *typdef, const xmlChar *strval, val_value_t *retval); /******************************************************************** * FUNCTION val_union_ok_errinfo * * Check a union to make sure the string is valid based * on the specified typdef, and convert the string to * an NCX internal format * * INPUTS: * typdef == typ_def_t for the designated union type * strval == the value to check against the member typ defs * retval == pointer to output struct for converted value * errinfo == address of error struct * * OUTPUTS: * If return NO_ERR: * retval->str or retval->num will be set with the converted value * *errinfo == error struct on error exit * * RETURNS: * status *********************************************************************/ extern status_t val_union_ok_errinfo (typ_def_t *typdef, const xmlChar *strval, val_value_t *retval, ncx_errinfo_t **errinfo); /******************************************************************** * FUNCTION val_union_ok_ex * * Check a union to make sure the string is valid based * on the specified typdef, and convert the string to * an NCX internal format * * INPUTS: * typdef == typ_def_t for the designated union type * strval == the value to check against the member typ defs * retval == pointer to output struct for converted value * errinfo == address of error struct * mod == module in progress, if any * * OUTPUTS: * If return NO_ERR: * retval->str or retval->num will be set with the converted value * *errinfo == error struct on error exit * * RETURNS: * status *********************************************************************/ extern status_t val_union_ok_ex (typ_def_t *typdef, const xmlChar *strval, val_value_t *retval, ncx_errinfo_t **errinfo, ncx_module_t *mod); /******************************************************************** * FUNCTION val_get_metaQ * * Get the meta Q header for the value * * INPUTS: * val == value node to check * * RETURNS: * pointer to the metaQ for this value *********************************************************************/ extern dlq_hdr_t * val_get_metaQ (val_value_t *val); /******************************************************************** * FUNCTION val_get_first_meta * * Get the first metaQ entry from the specified Queue * * INPUTS: * queue == queue of meta-vals to check * * RETURNS: * pointer to the first meta-var in the Queue if found, * or NULL if none *********************************************************************/ extern val_value_t * val_get_first_meta (dlq_hdr_t *queue); /******************************************************************** * FUNCTION val_get_first_meta_val * * Get the first metaQ entry from the specified Queue * * INPUTS: * value node to get the metaQ from * * RETURNS: * pointer to the first meta-var in the Queue if found, * or NULL if none *********************************************************************/ extern val_value_t * val_get_first_meta_val (val_value_t *val); /******************************************************************** * FUNCTION val_get_next_meta * * Get the next metaQ entry from the specified entry * * INPUTS: * curnode == current meta-var node * * RETURNS: * pointer to the next meta-var in the Queue if found, * or NULL if none *********************************************************************/ extern val_value_t * val_get_next_meta (val_value_t *curmeta); /******************************************************************** * FUNCTION val_meta_empty * * Check if the metaQ is empty for the value node * * INPUTS: * val == value to check * * RETURNS: * TRUE if the metaQ for the value is empty * FALSE otherwise *********************************************************************/ extern boolean val_meta_empty (val_value_t *val); /******************************************************************** * FUNCTION val_find_meta * * Get the corresponding meta data node * * INPUTS: * val == value to check for metadata * nsid == namespace ID of 'name'; 0 == don't use * name == name of metadata variable name * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ extern val_value_t * val_find_meta (val_value_t *val, xmlns_id_t nsid, const xmlChar *name); /******************************************************************** * FUNCTION val_meta_match * * Return true if the corresponding attribute exists and has * the same value * * INPUTS: * val == value to check for metadata * metaval == value to match in the val->metaQ * * RETURNS: * TRUE if the specified attr if found and has the same value * FALSE otherwise *********************************************************************/ extern boolean val_meta_match (val_value_t *val, val_value_t *metaval); /******************************************************************** * FUNCTION val_metadata_inst_count * * Get the number of instances of the specified attribute * * INPUTS: * val == value to check for metadata instance count * nsid == namespace ID of the meta data variable * name == name of the meta data variable * * RETURNS: * number of instances found in val->metaQ *********************************************************************/ extern uint32 val_metadata_inst_count (val_value_t *val, xmlns_id_t nsid, const xmlChar *name); /******************************************************************** * FUNCTION val_dump_value * * Printf the specified val_value_t struct to * the logfile, or stdout if none set * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to printf * startindent == start indent char count * *********************************************************************/ extern void val_dump_value (val_value_t *val, int32 startindent); /******************************************************************** * FUNCTION val_dump_value_ex * * Printf the specified val_value_t struct to * the logfile, or stdout if none set * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to printf * startindent == start indent char count * display_mode == display mode to use *********************************************************************/ extern void val_dump_value_ex (val_value_t *val, int32 startindent, ncx_display_mode_t display_mode); /******************************************************************** * FUNCTION val_dump_alt_value * * Printf the specified val_value_t struct to * the alternate logfile * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to printf * startindent == start indent char count * *********************************************************************/ extern void val_dump_alt_value (val_value_t *val, int32 startindent); /******************************************************************** * FUNCTION val_stdout_value * * Printf the specified val_value_t struct to stdout * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to printf * startindent == start indent char count * *********************************************************************/ extern void val_stdout_value (val_value_t *val, int32 startindent); /******************************************************************** * FUNCTION val_stdout_value_ex * * Printf the specified val_value_t struct to stdout * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to printf * startindent == start indent char count * display_mode == display mode to use *********************************************************************/ extern void val_stdout_value_ex (val_value_t *val, int32 startindent, ncx_display_mode_t display_mode); /******************************************************************** * FUNCTION val_dump_value_max * * Printf the specified val_value_t struct to * the logfile, or stdout if none set * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to dump * startindent == start indent char count * indent_amount == number of spaces for each indent * dumpmode == logging mode to use * display_mode == formatting mode for display * with_meta == TRUE if metaQ should be printed * FALSE to skip meta data * configonly == TRUE if config only nodes should be displayed * FALSE if all nodes should be displayed *********************************************************************/ extern void val_dump_value_max (val_value_t *val, int32 startindent, int32 indent_amount, val_dumpvalue_mode_t dumpmode, ncx_display_mode_t display_mode, boolean with_meta, boolean configonly); /******************************************************************** * FUNCTION val_set_string * * set a generic string using the builtin string typdef * Set an initialized val_value_t as a simple type * namespace set to 0 !!! * use after calling val_new_value * * INPUTS: * val == value to set * valname == name of simple value * valstr == simple value encoded as a string * * OUTPUTS: * *val is filled in if return NO_ERR * RETURNS: * status *********************************************************************/ extern status_t val_set_string (val_value_t *val, const xmlChar *valname, const xmlChar *valstr); /******************************************************************** * FUNCTION val_set_string2 * * set a string with any typdef * Set an initialized val_value_t as a simple type * namespace set to 0 !!! * * Will check if the string is OK for the typdef! * * INPUTS: * val == value to set * valname == name of simple value * typdef == parm typdef (may be NULL) * valstr == simple value encoded as a string * valstrlen == length of valstr to use * * OUTPUTS: * *val is filled in if return NO_ERR * * RETURNS: * status *********************************************************************/ extern status_t val_set_string2 (val_value_t *val, const xmlChar *valname, typ_def_t *typdef, const xmlChar *valstr, uint32 valstrlen); /******************************************************************** * FUNCTION val_reset_empty * * Recast an already initialized value as an NCX_BT_EMPTY * clean a value and set it to empty type * used by yangcli to delete leafs * * INPUTS: * val == value to set * * OUTPUTS: * *val is filled in if return NO_ERR * RETURNS: * status *********************************************************************/ extern status_t val_reset_empty (val_value_t *val); /******************************************************************** * FUNCTION val_set_simval * * set any simple value with any typdef * Set an initialized val_value_t as a simple type * * INPUTS: * val == value to set * typdef == typdef of expected type * nsid == namespace ID of this field * valname == name of simple value * valstr == simple value encoded as a string * * OUTPUTS: * *val is filled in if return NO_ERR * RETURNS: * status *********************************************************************/ extern status_t val_set_simval (val_value_t *val, typ_def_t *typdef, xmlns_id_t nsid, const xmlChar *valname, const xmlChar *valstr); /******************************************************************** * FUNCTION val_set_simval_str * * set any simple value with any typdef, and a counted string * Set an initialized val_value_t as a simple type * * The string value will be converted to a value * struct format and checked against the provided typedef * * Handles the following data types: * * NCX_BT_INT8 * NCX_BT_INT16 * NCX_BT_INT32 * NCX_BT_INT64 * NCX_BT_UINT8 * NCX_BT_UINT16 * NCX_BT_UINT32 * NCX_BT_UINT64 * NCX_BT_DECIMAL64 * NCX_BT_FLOAT64 * NCX_BT_BINARY * NCX_BT_STRING * NCX_BT_INSTANCE_ID * NCX_BT_ENUM * NCX_BT_EMPTY * NCX_BT_BOOLEAN * NCX_BT_SLIST * NCX_BT_BITS * NCX_BT_UNION * NCX_BT_LEAFREF * NCX_BT_IDREF * * INPUTS: * val == value to set * typdef == typdef of expected type * nsid == namespace ID of this field * valname == name of simple value * valnamelen == length of name string to compare * valstr == simple value encoded as a string * * OUTPUTS: * *val is filled in if return NO_ERR * RETURNS: * status *********************************************************************/ extern status_t val_set_simval_str (val_value_t *val, typ_def_t *typdef, xmlns_id_t nsid, const xmlChar *valname, uint32 valnamelen, const xmlChar *valstr); /******************************************************************** * FUNCTION val_make_simval * * Create and set a val_value_t as a simple type * same as val_set_simval, but malloc the value first * * INPUTS: * typdef == typdef of expected type * nsid == namespace ID of this field * valname == name of simple value * valstr == simple value encoded as a string * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to malloced and filled in val_value_t struct * NULL if some error *********************************************************************/ extern val_value_t * val_make_simval (typ_def_t *typdef, xmlns_id_t nsid, const xmlChar *valname, const xmlChar *valstr, status_t *res); /******************************************************************** * FUNCTION val_make_string * * Malloc and set a val_value_t as a generic NCX_BT_STRING * namespace set to 0 !!! * * INPUTS: * nsid == namespace ID to use * valname == name of simple value * valstr == simple value encoded as a string * * RETURNS: * malloced val struct filled in; NULL if malloc or strdup failed *********************************************************************/ extern val_value_t * val_make_string (xmlns_id_t nsid, const xmlChar *valname, const xmlChar *valstr); /******************************************************************** * FUNCTION val_merge * * Merge src val into dest val (! MUST be same type !) * Any meta vars in src are also merged into dest * * This function is not used to merge complex objects * !!! For typ_is_simple() only !!! * * INPUTS: * src == val to merge from * dest == val to merge into * * RETURNS: * status *********************************************************************/ extern status_t val_merge (const val_value_t *src, val_value_t *dest); /******************************************************************** * FUNCTION val_clone * * Clone a specified val_value_t struct and sub-trees * * INPUTS: * val == value to clone * * RETURNS: * clone of val, or NULL if a malloc failure *********************************************************************/ extern val_value_t * val_clone (const val_value_t *val); /******************************************************************** * FUNCTION val_clone2 * * Clone a specified val_value_t struct and sub-trees * but not the editvars * * INPUTS: * val == value to clone * * RETURNS: * clone of val, or NULL if a malloc failure *********************************************************************/ extern val_value_t * val_clone2 (const val_value_t *val); /******************************************************************** * FUNCTION val_clone_config_data * * Clone a specified val_value_t struct and sub-trees * Filter with the val_is_config_data callback function * pass in a config node, such as root * will call val_clone_test with the val_is_config_data * callbacck function * * INPUTS: * val == config data value to clone * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * clone of val, or NULL if a malloc failure *********************************************************************/ extern val_value_t * val_clone_config_data (const val_value_t *val, status_t *res); /******************************************************************** * FUNCTION val_replace * * Replace a specified val_value_t struct and sub-trees * !!! this can be destructive to the source 'val' parameter !!!! * * INPUTS: * val == value to clone from * copy == address of value to replace * * OUTPUTS: * *copy has been deleted and reforms with the contents of 'val' * * RETURNS: * status *********************************************************************/ extern status_t val_replace (val_value_t *val, val_value_t *copy); /******************************************************************** * FUNCTION val_replace_str * * Replace a specified val_value_t struct with a string type * * INPUTS: * str == value to clone from; may be NULL * stringlen == number of chars to use from str, if not NULL * copy == address of value to replace * * OUTPUTS: * *copy has been deleted and reforms with the contents of 'val' * * RETURNS: * status *********************************************************************/ extern status_t val_replace_str (const xmlChar *str, uint32 stringlen, val_value_t *copy); /******************************************************************** * FUNCTION val_add_meta * * Add a meta value node to a parent value node * Simply makes a new last meta!!! * * INPUTS: * child == node to store in the parent * parent == complex value node with a childQ * *********************************************************************/ extern void val_add_meta (val_value_t *meta, val_value_t *parent); /******************************************************************** * FUNCTION val_add_child * * Add a child value node to a parent value node * Simply makes a new last child!!! * Does not check siblings!!! * Relies on val_set_canonical_order * * To modify existing extries, use val_add_child_sorted instead!! * * INPUTS: * child == node to store in the parent * parent == complex value node with a childQ * *********************************************************************/ extern void val_add_child (val_value_t *child, val_value_t *parent); /******************************************************************** * FUNCTION val_add_child_sorted * * Add a child value node to a parent value node * in the proper place * * INPUTS: * child == node to store in the parent * parent == complex value node with a childQ * *********************************************************************/ extern void val_add_child_sorted (val_value_t *child, val_value_t *parent); /******************************************************************** * FUNCTION val_insert_child * * Insert a child value node to a parent value node * * INPUTS: * child == node to store in the parent * current == current node to insert after; * NULL to make new first entry * parent == complex value node with a childQ * *********************************************************************/ extern void val_insert_child (val_value_t *child, val_value_t *current, val_value_t *parent); /******************************************************************** * FUNCTION val_remove_child * * Remove a child value node from its parent value node * * INPUTS: * child == node to store in the parent * *********************************************************************/ extern void val_remove_child (val_value_t *child); /******************************************************************** * FUNCTION val_swap_child * * Swap a child value node with a current value node * * INPUTS: * newchild == node to store in the place of the curchild * curchild == node to remove from the parent * *********************************************************************/ extern void val_swap_child (val_value_t *newchild, val_value_t *curchild); /******************************************************************** * FUNCTION val_first_child_match * * Get the first instance of the corresponding child node * * INPUTS: * parent == parent value to check * child == child value to find (e.g., from a NETCONF PDU) * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ extern val_value_t * val_first_child_match (val_value_t *parent, val_value_t *child); /******************************************************************** * FUNCTION val_next_child_match * * Get the next instance of the corresponding child node * * INPUTS: * parent == parent value to check * child == child value to find (e.g., from a NETCONF PDU) * curmatch == current child of parent that matched * * RETURNS: * pointer to the next child match if found or NULL if not found *********************************************************************/ extern val_value_t * val_next_child_match (val_value_t *parent, val_value_t *child, val_value_t *curmatch); /******************************************************************** * FUNCTION val_get_first_child * * Get the child node * * INPUTS: * parent == parent complex type to check * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ extern val_value_t * val_get_first_child (const val_value_t *parent); /******************************************************************** * FUNCTION val_get_next_child * * Get the next child node * * INPUTS: * curchild == current child node * * RETURNS: * pointer to the next child if found or NULL if not found *********************************************************************/ extern val_value_t * val_get_next_child (const val_value_t *curchild); /******************************************************************** * FUNCTION val_find_child * * Find the first instance of the specified child node * * INPUTS: * parent == parent complex type to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * childname == name of child node to find * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ extern val_value_t * val_find_child (const val_value_t *parent, const xmlChar *modname, const xmlChar *childname); /******************************************************************** * FUNCTION val_find_child_que * * Find the first instance of the specified child node in the * specified child Q * * INPUTS: * parent == parent complex type to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * childname == name of child node to find * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ extern val_value_t * val_find_child_que (const dlq_hdr_t *childQ, const xmlChar *modname, const xmlChar *childname); /******************************************************************** * FUNCTION val_match_child * * Match the first instance of the specified child node * * INPUTS: * parent == parent complex type to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * childname == name of child node to find * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ extern val_value_t * val_match_child (const val_value_t *parent, const xmlChar *modname, const xmlChar *childname); /******************************************************************** * FUNCTION val_find_next_child * * Find the next instance of the specified child node * * INPUTS: * parent == parent complex type to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * childname == name of child node to find * curchild == current child of this object type to start search * * RETURNS: * pointer to the child if found or NULL if not found *********************************************************************/ extern val_value_t * val_find_next_child (const val_value_t *parent, const xmlChar *modname, const xmlChar *childname, const val_value_t *curchild); /******************************************************************** * FUNCTION val_first_child_name * * Get the first corresponding child node instance, by name * find first -- really for resolve index function * * INPUTS: * parent == parent complex type to check * name == child name to find * * RETURNS: * pointer to the FIRST match if found, or NULL if not found *********************************************************************/ extern val_value_t * val_first_child_name (val_value_t *parent, const xmlChar *name); /******************************************************************** * FUNCTION val_first_child_qname * * Get the first corresponding child node instance, by QName * * INPUTS: * parent == parent complex type to check * nsid == namespace ID to use, 0 for any * name == child name to find * * RETURNS: * pointer to the first match if found, or NULL if not found *********************************************************************/ extern val_value_t * val_first_child_qname (val_value_t *parent, xmlns_id_t nsid, const xmlChar *name); /******************************************************************** * FUNCTION val_next_child_qname * * Get the next corresponding child node instance, by QName * * INPUTS: * parent == parent complex type to check * nsid == namespace ID to use, 0 for any * name == child name to find * curchild == current child match to start from * * RETURNS: * pointer to the next match if found, or NULL if not found *********************************************************************/ extern val_value_t * val_next_child_qname (val_value_t *parent, xmlns_id_t nsid, const xmlChar *name, val_value_t *curchild); /******************************************************************** * FUNCTION val_first_child_string * * find first name value pair * Get the first corresponding child node instance, by name * and by string value. * Child node must be a base type of * NCX_BT_STRING * NCX_BT_INSTANCE_ID * NCX_BT_LEAFREF * * INPUTS: * parent == parent complex type to check * name == child name to find * strval == string value to find * RETURNS: * pointer to the FIRST match if found, or NULL if not found *********************************************************************/ extern val_value_t * val_first_child_string (val_value_t *parent, const xmlChar *name, const xmlChar *strval); /******************************************************************** * FUNCTION val_child_cnt * * Get the number of child nodes present * get number of child nodes present -- for choice checking * * INPUTS: * parent == parent complex type to check * * RETURNS: * the number of child nodes found *********************************************************************/ extern uint32 val_child_cnt (val_value_t *parent); /******************************************************************** * FUNCTION val_child_inst_cnt * * Get the corresponding child instance count by name * get instance count -- for instance qualifer checking * * INPUTS: * parent == parent complex type to check * modname == module name defining the child (may be NULL) * name == child name to find and count * * RETURNS: * the instance count *********************************************************************/ extern uint32 val_child_inst_cnt (const val_value_t *parent, const xmlChar *modname, const xmlChar *name); /******************************************************************** * FUNCTION val_find_all_children * * Find all occurances of the specified node(s) * within the children of the current node. * The walker fn will be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * INPUTS: * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == node to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of child node to find * == NULL to match any child name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if childname == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ extern boolean val_find_all_children (val_walker_fn_t walkerfn, void *cookie1, void *cookie2, val_value_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode); /******************************************************************** * FUNCTION val_find_all_ancestors * * Find all the ancestor instances of the specified node * within the path to root from the current node; * use the filter criteria provided * * INPUTS: * * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == node to start search at * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of ancestor node to find * == NULL to match any node name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if name == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * orself == TRUE if axis is really ancestor-or-self * FALSE if axis is ancestor * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ extern boolean val_find_all_ancestors (val_walker_fn_t walkerfn, void *cookie1, void *cookie2, val_value_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode, boolean orself); /******************************************************************** * FUNCTION val_find_all_descendants * * Find all occurances of the specified node * within the current subtree. The walker fn will * be called for each match. * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * INPUTS: * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == startnode complex type to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of descendant node to find * == NULL to match any node name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if decname == NULL * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * orself == TRUE if axis is really ancestor-or-self * FALSE if axis is ancestor * forceall == TRUE to invoke the descendant callbacks * even if fncalled was true from the current * (parent) node; FALSE to skip descendants * if fncalled was TRUE * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ extern boolean val_find_all_descendants (val_walker_fn_t walkerfn, void *cookie1, void *cookie2, val_value_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean textmode, boolean orself, boolean forceall); /******************************************************************** * FUNCTION val_find_all_pfaxis * * Find all occurances of the specified node * for the specified preceding or following axis * * preceding::* * following::* * * within the current subtree. The walker fn will * be called for each match. Because the callbacks * will be done in sequential order, starting from * the * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * INPUTS: * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * topnode == topnode used as the relative root for * calculating node position * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of preceding or following node to find * == NULL to match any node name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if decname == NULL * dblslash == TRUE if all decendents of the preceding * or following nodes should be checked * FALSE only 1 level is checked, not their descendants * textmode == TRUE if just testing for text() nodes * name modname will be ignored in this mode * FALSE if using name and modname to filter * axis == axis enum to use * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ extern boolean val_find_all_pfaxis (val_walker_fn_t walkerfn, void *cookie1, void *cookie2, val_value_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean dblslash, boolean textmode, ncx_xpath_axis_t axis); /******************************************************************** * FUNCTION val_find_all_pfsibling_axis * * Find all occurances of the specified node * for the specified axis * * preceding-sibling::* * following-sibling::* * * within the current subtree. The walker fn will * be called for each match. Because the callbacks * will be done in sequential order, starting from * the * * If the walker function returns TRUE, then the * walk will continue; If FALSE it will terminate right away * * INPUTS: * walkerfn == callback function to use * cookie1 == cookie1 value to pass to walker fn * cookie2 == cookie2 value to pass to walker fn * startnode == starting sibling node to check * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of preceding or following node to find * == NULL to match any node name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if decname == NULL * dblslash == TRUE if all decendents of the preceding * or following nodes should be checked * FALSE only * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * axis == axis enum to use * * RETURNS: * TRUE if normal termination occurred * FALSE if walker fn requested early termination *********************************************************************/ extern boolean val_find_all_pfsibling_axis (val_walker_fn_t walkerfn, void *cookie1, void *cookie2, val_value_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean dblslash, boolean textmode, ncx_xpath_axis_t axis); /******************************************************************** * FUNCTION val_get_axisnode * * Find the specified node based on the context node, * a context position and an axis * * ancestor::* * ancestor-or-self::* * child::* * descendant::* * descendant-or-self::* * following::* * following-sibling::* * preceding::* * preceding-sibling::* * self::* * * INPUTS: * startnode == context node to run tests from * modname == module name; * the first match in this module namespace * will be returned * == NULL: * the first match in any namespace will * be returned; * name == name of preceding or following node to find * == NULL to match any node name * configonly == TRUE to skip over non-config nodes * FALSE to check all nodes * Only used if decname == NULL * dblslash == TRUE if all decendents of the preceding * or following nodes should be checked * FALSE only * textmode == TRUE if just testing for text() nodes * name and modname will be ignored in this mode * FALSE if using name and modname to filter * axis == axis enum to use * position == position to find in the specified axis * * RETURNS: * pointer to found value or NULL if none *********************************************************************/ extern val_value_t * val_get_axisnode (val_value_t *startnode, const xmlChar *modname, const xmlChar *name, boolean configonly, boolean dblslash, boolean textmode, ncx_xpath_axis_t axis, int64 position); /******************************************************************** * FUNCTION val_get_child_inst_id * * Get the instance ID for this child node within the parent context * * INPUTS: * parent == parent complex type to check * child == child node to find ID for * * RETURNS: * the instance ID num (1 .. N), or 0 if some error *********************************************************************/ extern uint32 val_get_child_inst_id (const val_value_t *parent, const val_value_t *child); /******************************************************************** * FUNCTION val_liststr_count * * Get the number of strings in the list type * * INPUTS: * val == value to check * * RETURNS: * number of list entries; also zero for error *********************************************************************/ extern uint32 val_liststr_count (const val_value_t *val); /******************************************************************** * FUNCTION val_index_match * * Check 2 val_value structs for the same instance ID * * The node data types must match, and must be * NCX_BT_LIST * * All index components must exactly match. * * INPUTS: * val1 == first value to index match * val2 == second value to index match * * RETURNS: * TRUE if the index chains match *********************************************************************/ extern boolean val_index_match (const val_value_t *val1, const val_value_t *val2); /******************************************************************** * FUNCTION val_index_compare * * Check 2 val_value structs for the same instance ID * * The node data types must match, and must be * NCX_BT_LIST * * INPUTS: * val1 == first value to index match * val2 == second value to index match * * RETURNS: * -1 , - or 1 for compare value *********************************************************************/ extern int val_index_compare (const val_value_t *val1, const val_value_t *val2); /******************************************************************** * FUNCTION val_compare_max * * Compare 2 val_value_t struct value contents * Check all or config only * Check just child nodes or all descendant nodes * Handles NCX_CL_BASE and NCX_CL_SIMPLE data classes * by comparing the simple value. * * Handle NCX_CL_COMPLEX by checking the index if needed * and then checking all the child nodes recursively * * !!!! Meta-value contents are ignored for this test !!!! * * INPUTS: * val1 == first value to check * val2 == second value to check * configonly == TRUE to compare config=true nodes only * FALSE to compare all nodes * childonly == TRUE to look just 1 level for comparison * FALSE to compare all descendant nodes of complex types * editing == TRUE to compare for editing * FALSE to compare just the values, so a set by * default and value=default are the same value * * RETURNS: * compare result * -1: val1 is less than val2 (if complex just different or error) * 0: val1 is the same as val2 * 1: val1 is greater than val2 *********************************************************************/ extern int32 val_compare_max (const val_value_t *val1, const val_value_t *val2, boolean configonly, boolean childonly, boolean editing); /******************************************************************** * FUNCTION val_compare_ex * * Compare 2 val_value_t struct value contents * Check all or config only * * Handles NCX_CL_BASE and NCX_CL_SIMPLE data classes * by comparing the simple value. * * Handle NCX_CL_COMPLEX by checking the index if needed * and then checking all the child nodes recursively * * !!!! Meta-value contents are ignored for this test !!!! * * INPUTS: * val1 == first value to check * val2 == second value to check * configonly == TRUE to compare config=true nodes only * FALSE to compare all nodes * * RETURNS: * compare result * -1: val1 is less than val2 (if complex just different or error) * 0: val1 is the same as val2 * 1: val1 is greater than val2 *********************************************************************/ extern int32 val_compare_ex (const val_value_t *val1, const val_value_t *val2, boolean configonly); /******************************************************************** * FUNCTION val_compare * * Compare 2 val_value_t struct value contents * * Handles NCX_CL_BASE and NCX_CL_SIMPLE data classes * by comparing the simple value. * * Handle NCX_CL_COMPLEX by checking the index if needed * and then checking all the child nodes recursively * * !!!! Meta-value contents are ignored for this test !!!! * * INPUTS: * val1 == first value to check * val2 == second value to check * * RETURNS: * compare result * -1: val1 is less than val2 (if complex just different or error) * 0: val1 is the same as val2 * 1: val1 is greater than val2 *********************************************************************/ extern int32 val_compare (const val_value_t *val1, const val_value_t *val2); /******************************************************************** * FUNCTION val_compare_to_string * * Compare a val_value_t struct value contents to a string * * Handles NCX_CL_BASE and NCX_CL_SIMPLE data classes * by comparing the simple value. * * !!!! Meta-value contents are ignored for this test !!!! * * INPUTS: * val1 == first value to check * strval2 == second value to check * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * compare result * -1: val1 is less than val2 (if complex just different or error) * 0: val1 is the same as val2 * 1: val1 is greater than val2 *********************************************************************/ extern int32 val_compare_to_string (const val_value_t *val1, const xmlChar *strval2, status_t *res); /******************************************************************** * FUNCTION val_compare_for_replace * * Compare 2 val_value_t struct value contents * for the nc:operation=replace procedures * Only check the child nodes to see if the * config nodes are the same * * !!!! Meta-value contents are ignored for this test !!!! * * INPUTS: * val1 == new value to check * val2 == current value to check * * RETURNS: * compare result * -1: val1 is less than val2 (if complex just different or error) * 0: val1 is the same as val2 * 1: val1 is greater than val2 *********************************************************************/ extern int32 val_compare_for_replace (const val_value_t *val1, const val_value_t *val2); /******************************************************************** * FUNCTION val_sprintf_simval_nc * * Sprintf the xmlChar string NETCONF representation of a simple value * * buff is allowed to be NULL; if so, then this fn will * just return the length of the string (w/o EOS ch) * * USAGE: * call 1st time with a NULL buffer to get the length * call the 2nd time with a buffer of the returned length * * !!!! DOES NOT CHECK BUFF OVERRUN IF buff is non-NULL !!!! * * INPUTS: * buff == buffer to write (NULL means get length only) * val == value to print * len == address of return length * * OUTPUTS: * *len == number of bytes written (or just length if buff == NULL) * * RETURNS: * status *********************************************************************/ extern status_t val_sprintf_simval_nc (xmlChar *buff, const val_value_t *val, uint32 *len); /******************************************************************** * FUNCTION val_make_sprintf_string * * Malloc a buffer and then sprintf the xmlChar string * NETCONF representation of a simple value * * INPUTS: * val == value to print * * RETURNS: * malloced buffer with string represetation of the * 'val' value node * NULL if some error *********************************************************************/ extern xmlChar * val_make_sprintf_string (const val_value_t *val); /******************************************************************** * FUNCTION val_resolve_scoped_name * * Find the scoped identifier in the specified complex value * * E.g.: foo.bar.baz * * INPUTS: * val == complex type to check * name == scoped name string of a nested node to find * chval == address of return child val * * OUTPUTS: * *chval is set to the value of the found local scoped * child member, if NO_ERR * * RETURNS: * status *********************************************************************/ extern status_t val_resolve_scoped_name (val_value_t *val, const xmlChar *name, val_value_t **chval); /******************************************************************** * FUNCTION val_get_iqualval * * Get the effective instance qualifier value for this value * * INPUTS: * val == value construct to check * * RETURNS: * iqual value *********************************************************************/ extern ncx_iqual_t val_get_iqualval (const val_value_t *val); /******************************************************************** * FUNCTION val_duplicates_allowed * * Determine if duplicates are allowed for the given val type * The entire definition chain is checked to see if a 'no-duplicates' * * The default is config, so some sort of named type or parameter * must be declared to create a non-config data element * * Fishing order: * 1) typdef chain * 2) parm definition * 3) parmset definition * * INPUTS: * val == value node to check * * RETURNS: * TRUE if the value is classified as configuration * FALSE if the value is not classified as configuration *********************************************************************/ extern boolean val_duplicates_allowed (val_value_t *val); /******************************************************************** * FUNCTION val_has_content * * Determine if there is a value or any child nodes for this val * * INPUTS: * val == value node to check * * RETURNS: * TRUE if the value has some content * FALSE if the value does not have any content *********************************************************************/ extern boolean val_has_content (const val_value_t *val); /******************************************************************** * FUNCTION val_has_index * * Determine if this value has an index * * INPUTS: * val == value node to check * * RETURNS: * TRUE if the value has an index * FALSE if the value does not have an index *********************************************************************/ extern boolean val_has_index (const val_value_t *val); /******************************************************************** * FUNCTION val_get_first_index * * Get the first index entry, if any for this value node * * INPUTS: * val == value node to check * * RETURNS: * pointer to first val_index_t node, NULL if none *********************************************************************/ extern val_index_t * val_get_first_index (const val_value_t *val); /******************************************************************** * FUNCTION val_get_next_index * * Get the next index entry, if any for this value node * * INPUTS: * val == value node to check * * RETURNS: * pointer to next val_index_t node, NULL if none *********************************************************************/ extern val_index_t * val_get_next_index (const val_index_t *valindex); /******************************************************************** * FUNCTION val_parse_meta * * Parse the metadata descriptor against the typdef * Check only that the value is ok, not instance count * * INPUTS: * typdef == typdef to check * attr == XML attribute to check * retval == initialized val_value_t to fill in * * OUTPUTS: * *retval == filled in if return is NO_ERR * RETURNS: * status of the operation *********************************************************************/ extern status_t val_parse_meta (typ_def_t *typdef, xml_attr_t *attr, val_value_t *retval); /******************************************************************** * FUNCTION val_set_extern * * Setup an NCX_BT_EXTERN value * * INPUTS: * val == value to setup * fname == filespec string to set as the value *********************************************************************/ extern void val_set_extern (val_value_t *val, xmlChar *fname); /******************************************************************** * FUNCTION val_set_intern * * Setup an NCX_BT_INTERN value * * INPUTS: * val == value to setup * intbuff == internal buffer to set as the value *********************************************************************/ extern void val_set_intern (val_value_t *val, xmlChar *intbuff); /******************************************************************** * FUNCTION val_fit_oneline * * Check if the XML encoding for the specified val_value_t * should take one line or more than one line * * Simple types should not use more than one line or introduce * any extra whitespace in any simple content element * * !!!The calculation includes the XML start and end tags!!! * * totalsize: value == 26 * * INPUTS: * val == value to check * linelen == length of line to check against * * RETURNS: * TRUE if the val is a type that should or must fit on one line * FALSE otherwise *********************************************************************/ extern boolean val_fit_oneline (const val_value_t *val, uint32 linesize); /******************************************************************** * FUNCTION val_create_allowed * * Check if the specified value is allowed to have a * create edit-config operation attribute * * INPUTS: * val == value to check * * RETURNS: * TRUE if the val is allowed to have the edit-op * FALSE otherwise *********************************************************************/ extern boolean val_create_allowed (const val_value_t *val); /******************************************************************** * FUNCTION val_delete_allowed * * Check if the specified value is allowed to have a * delete edit-config operation attribute * * INPUTS: * val == value to check * * RETURNS: * TRUE if the val is allowed to have the edit-op * FALSE otherwise *********************************************************************/ extern boolean val_delete_allowed (const val_value_t *val); /******************************************************************** * FUNCTION val_is_config_data * * Check if the specified value is a config DB object instance * * INPUTS: * val == value to check * * RETURNS: * TRUE if the val is a config DB object instance * FALSE otherwise *********************************************************************/ extern boolean val_is_config_data (const val_value_t *val); /******************************************************************** * FUNCTION val_is_virtual * * Check if the specified value is a virtual value * such that a 'get' callback function is required * to access the real value contents * * INPUTS: * val == value to check * * RETURNS: * TRUE if the val is a virtual value * FALSE otherwise *********************************************************************/ extern boolean val_is_virtual (const val_value_t *val); /******************************************************************** * FUNCTION val_get_virtual_value * * Get the value of a value node * The top-level value is provided by the caller * and must be malloced with val_new_value * before calling this function * * must free the return val; not cached * * If the val->getcb is NULL, then an error will be returned * * Caller should check for *res == ERR_NCX_SKIPPED * This will be returned if virtual value has no * instance at this time. * * !!! DO NOT SAVE THE RETURN VALUE LONGER THAN THE * !!! VIRTUAL VALUE CACHE TIMEOUT VALUE * * INPUTS: * session == session CB ptr cast as void * * that is getting the virtual value * val == virtual value to get value for * res == pointer to output function return status value * * OUTPUTS: * val->virtualval will be set with the cached return value * *res == the function return status * * RETURNS: * A malloced and filled in val_value_t struct * This value is cached in the val->virtualval pointer * and will be freed when the cache is replaced or when * val is freed *********************************************************************/ extern val_value_t * val_get_virtual_value (void *session, /* really ses_cb_t * */ val_value_t *val, status_t *res); /******************************************************************** * FUNCTION val_is_default * * Check if the specified value is set to the YANG default value * * INPUTS: * val == value to check * * SIDE EFFECTS: * val->flags may be adjusted * VAL_FL_DEFVALSET will be set if not set already * VAL_FL_DEFVAL will be set or cleared if * VAL_FL_DEFSETVAL is not already set, * after determining if the value == its default * * RETURNS: * TRUE if the val is set to the default value * FALSE otherwise *********************************************************************/ extern boolean val_is_default (val_value_t *val); /******************************************************************** * FUNCTION val_is_real * * Check if the specified value is a real value * * return TRUE if not virtual or NCX_BT_EXTERN or NCX_BT_INTERN) * * INPUTS: * val == value to check * * RETURNS: * TRUE if the val is a real value * FALSE otherwise *********************************************************************/ extern boolean val_is_real (const val_value_t *val); /******************************************************************** * FUNCTION val_get_parent_nsid * * Try to get the parent namespace ID * * INPUTS: * val == value to check * * RETURNS: * namespace ID of parent, or 0 if not found or not a value parent *********************************************************************/ extern xmlns_id_t val_get_parent_nsid (const val_value_t *val); /******************************************************************** * FUNCTION val_instance_count * * Count the number of instances of the specified object name * in the parent value struct. This only checks the first * level under the parent, not the entire subtree * * * INPUTS: * val == value to check * modname == name of module which defines the object to count * NULL (do not check module names) * objname == name of object to count * * RETURNS: * number of instances found *********************************************************************/ extern uint32 val_instance_count (val_value_t *val, const xmlChar *modname, const xmlChar *objname); /******************************************************************** * FUNCTION val_set_extra_instance_errors * * mark ERR_NCX_EXTRA_VAL_INST errors for nodes > 'maxelems' * Count the number of instances of the specified object name * in the parent value struct. This only checks the first * level under the parent, not the entire subtree * Set the val-res status for all instances beyond the * specified 'maxelems' count to ERR_NCX_EXTRA_VAL_INST * * INPUTS: * val == value to check * modname == name of module which defines the object to count * NULL (do not check module names) * objname == name of object to count * maxelems == number of allowed instances * *********************************************************************/ extern void val_set_extra_instance_errors (val_value_t *val, const xmlChar *modname, const xmlChar *objname, uint32 maxelems); /******************************************************************** * FUNCTION val_need_quotes * * Check if a string needs to be quoted to be output * within a conf file or ncxcli stdout output * * INPUTS: * str == string to check * * RETURNS: * TRUE if double quoted string is needed * FALSE if not needed *********************************************************************/ extern boolean val_need_quotes (const xmlChar *str); /******************************************************************** * FUNCTION val_all_whitespace * * Check if a string is all whitespace * * INPUTS: * str == string to check * * RETURNS: * TRUE if string is all whitespace or empty length * FALSE if non-whitespace char found *********************************************************************/ extern boolean val_all_whitespace (const xmlChar *str); /******************************************************************** * FUNCTION val_match_metaval * * Match the specific attribute value and namespace ID * * INPUTS: * attr == attr to check * nsid == mamespace ID to match against * name == attribute name to match against * * RETURNS: * TRUE if attr is a match; FALSE otherwise *********************************************************************/ extern boolean val_match_metaval (const xml_attr_t *attr, xmlns_id_t nsid, const xmlChar *name); /******************************************************************** * FUNCTION val_get_dirty_flag * * Get the dirty flag for this value node * * INPUTS: * val == value node to check * * RETURNS: * TRUE if value is dirty, false otherwise *********************************************************************/ extern boolean val_get_dirty_flag (const val_value_t *val); /******************************************************************** * FUNCTION val_get_subtree_dirty_flag * * Get the subtree dirty flag for this value node * * INPUTS: * val == value node to check * * RETURNS: * TRUE if value is subtree dirty, false otherwise *********************************************************************/ extern boolean val_get_subtree_dirty_flag (const val_value_t *val); /******************************************************************** * FUNCTION val_set_dirty_flag * * Set the dirty flag for this value node * * INPUTS: * val == value node to check * * RETURNS: * TRUE if value is dirty, false otherwise *********************************************************************/ extern void val_set_dirty_flag (val_value_t *val); /******************************************************************** * FUNCTION val_clear_dirty_flag * * Clear the dirty flag for this value node * * INPUTS: * val == value node to check * * RETURNS: * TRUE if value is dirty, false otherwise *********************************************************************/ extern void val_clear_dirty_flag (val_value_t *val); /******************************************************************** * FUNCTION val_dirty_subtree * * Check the dirty or subtree_dirty flag * * INPUTS: * val == value node to check * * RETURNS: * TRUE if value is dirty or any subtree may be dirty, false otherwise *********************************************************************/ extern boolean val_dirty_subtree (const val_value_t *val); /******************************************************************** * FUNCTION val_clean_tree * * Clear the dirty flag and the operation for all * nodes within a value struct * * INPUTS: * val == value node to clean * * OUTPUTS: * val and all its child nodes (if any) are cleaned * val->flags: VAL_FL_DIRTY bit cleared to 0 * val->editop: cleared to OP_EDITOP_NONE * val->curparent: cleared to NULL *********************************************************************/ extern void val_clean_tree (val_value_t *val); /******************************************************************** * FUNCTION val_get_nest_level * * Get the next level of the value * * INPUTS: * val == value node to check * * RETURNS: * nest level from the root *********************************************************************/ extern uint32 val_get_nest_level (val_value_t *val); /******************************************************************** * FUNCTION val_get_first_leaf * * Get the first leaf or leaflist node in the * specified value tree * * INPUTS: * val == value node to check * * RETURNS: * pointer to first leaf found within this val struct * pointer to val if val is a leaf *********************************************************************/ extern val_value_t * val_get_first_leaf (val_value_t *val); /******************************************************************** * FUNCTION val_get_mod_name * * Get the module name associated with this value node * * INPUTS: * val == value node to check * * RETURNS: * const pointer to module name string * NULL if not found *********************************************************************/ extern const xmlChar * val_get_mod_name (const val_value_t *val); /******************************************************************** * FUNCTION val_get_mod_prefix * * Get the module prefix associated with this value node * * INPUTS: * val == value node to check * * RETURNS: * const pointer to module name string * NULL if not found *********************************************************************/ extern const xmlChar * val_get_mod_prefix (const val_value_t *val); /******************************************************************** * FUNCTION val_get_nsid * * Get the namespace ID for the specified value node * * INPUTS: * val == value node to check * * RETURNS: * const pointer to module name string * NULL if not found *********************************************************************/ extern xmlns_id_t val_get_nsid (const val_value_t *val); /******************************************************************** * FUNCTION val_change_nsid * * Change the namespace ID fora value node and all its descendants * * INPUTS: * val == value node to change * nsid == new namespace ID to use * *********************************************************************/ extern void val_change_nsid (val_value_t *val, xmlns_id_t nsid); /******************************************************************** * FUNCTION val_make_from_insertxpcb * * Make a val_value_t for a list, with the * child nodes for key leafs, specified in the * key attribute string given to the insert operation * * INPUTS: * sourceval == list val_value_t from the PDU with the insertxpcb * to process * status == address of return status (may be NULL, ignored) * * OUTPUTS: * if non-NULL: * *status == return status * * RETURNS: * malloced list val_value_t struct with converted value *********************************************************************/ extern val_value_t * val_make_from_insertxpcb (val_value_t *sourceval, status_t *res); /******************************************************************** * FUNCTION val_new_unique * * Malloc and initialize the fields in a val_unique_t * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern val_unique_t * val_new_unique (void); /******************************************************************** * FUNCTION val_free_unique * * CLean and free a val_unique_t struct * * INPUTS: * valuni == val_unique struct to free *********************************************************************/ extern void val_free_unique (val_unique_t *valuni); /******************************************************************** * FUNCTION val_get_typdef * * Get the typdef field for a value struct * * INPUTS: * val == val_value_t struct to use * * RETURNS: * pointer to the typdef or NULL if none *********************************************************************/ extern const typ_def_t * val_get_typdef (const val_value_t *val); /******************************************************************** * FUNCTION val_set_by_default * * Check if the value was set by val_add_defaults * * INPUTS: * val == val_value_t struct to check * * RETURNS: * TRUE if set by default * FALSE if set explicitly by some user or the ctartup config *********************************************************************/ extern boolean val_set_by_default (const val_value_t *val); /******************************************************************** * FUNCTION val_has_withdef_default * * Check if the value contained the wd:default attribute * * INPUTS: * val == val_value_t struct to check * * RETURNS: * TRUE if wd:default was set to true * FALSE if wd:default attribute was not set to true *********************************************************************/ extern boolean val_has_withdef_default (const val_value_t *val); /******************************************************************** * FUNCTION val_set_withdef_default * * Set the value flags as having the wd:default attribute * * INPUTS: * val == val_value_t struct to set * *********************************************************************/ extern void val_set_withdef_default (val_value_t *val); /******************************************************************** * FUNCTION val_is_metaval * * Check if the value is a meta-val (XML attribute) * * INPUTS: * val == val_value_t struct to check * * RETURNS: * TRUE if val is a meta-val * FALSE if val is not a meta-val *********************************************************************/ extern boolean val_is_metaval (const val_value_t *val); /******************************************************************** * FUNCTION val_move_chidren * * Move all the child nodes from src to dest * Source and dest must both be containers! * * INPUTS: * srcval == source val_value_t struct to move * destval == destination value struct ot use * *********************************************************************/ extern void val_move_children (val_value_t *srcval, val_value_t *destval); /******************************************************************** * FUNCTION val_cvt_generic * * Convert all the database object pointers to * generic object pointers to decouple a user * variable in yangcli from the server-specific * object definition (which goes away when the * session is terminated) * * !!! Need to assume the val->obj pointer is already * !!! invalid. This can happen to yangcli when a * !!! session is dropped and there are vars that * !!! reference YANG objects from the session * * INPUTS: * val == val_value_t struct to convert to generic * * RETURNS: * status *********************************************************************/ extern status_t val_cvt_generic (val_value_t *val); /******************************************************************** * FUNCTION val_set_pcookie * * Set the SIL pointer cookie in the editvars for * the specified value node * * INPUTS: * val == val_value_t struct to set * pcookie == pointer cookie value to set * * RETURNS: * status *********************************************************************/ extern status_t val_set_pcookie (val_value_t *val, void *pcookie); /******************************************************************** * FUNCTION val_set_icookie * * Set the SIL integer cookie in the editvars for * the specified value node * * INPUTS: * val == val_value_t struct to set * icookie == integer cookie value to set * * RETURNS: * status *********************************************************************/ extern status_t val_set_icookie (val_value_t *val, int icookie); /******************************************************************** * FUNCTION val_get_pcookie * * Get the SIL pointer cookie in the editvars for * the specified value node * * INPUTS: * val == val_value_t struct to set * * RETURNS: * pointer cookie value or NULL if none *********************************************************************/ extern void * val_get_pcookie (val_value_t *val); /******************************************************************** * FUNCTION val_get_icookie * * Get the SIL integer cookie in the editvars for * the specified value node * * INPUTS: * val == val_value_t struct to set * * RETURNS: * integer cookie value or 0 if none *********************************************************************/ extern int val_get_icookie (val_value_t *val); /******************************************************************** * FUNCTION val_delete_default_leaf * * Do the internal work to setup a delete of * a default leaf * * INPUTS: * val == val_value_t struct to use * * RETURNS: * status *********************************************************************/ extern status_t val_delete_default_leaf (val_value_t *val); /******************************************************************** * FUNCTION val_force_empty * * Convert a simple node to an empty type * * INPUTS: * val == val_value_t struct to use * *********************************************************************/ extern void val_force_empty (val_value_t *val); /******************************************************************** * FUNCTION val_move_fields_for_xml * * Move or copy the internal fields from one val to another * for xml_wr purposes * * INPUTS: * srcval == source val_value_t struct to move from * destval == destination val to move to * movemeta == TRUE if metaQ should be transferred *********************************************************************/ extern void val_move_fields_for_xml (val_value_t *srcval, val_value_t *destval, boolean movemeta); /******************************************************************** * FUNCTION val_get_first_key * * Get the first key record if this is a list with a key-stmt * * INPUTS: * val == value node to check * *********************************************************************/ extern val_index_t * val_get_first_key (val_value_t *val); /******************************************************************** * FUNCTION val_get_next_key * * Get the next key record if this is a list with a key-stmt * * INPUTS: * curkey == current key node * *********************************************************************/ extern val_index_t * val_get_next_key (val_index_t *curkey); /******************************************************************** * FUNCTION val_remove_key * * Remove a key pointer because the key is invalid * Free the key pointer * * INPUTS: * keyval == value node to find, remove and free * *********************************************************************/ extern void val_remove_key (val_value_t *keyval); /******************************************************************** * FUNCTION val_new_deleted_value * * Malloc and initialize the fields in a val_value_t to be used * as a deleted node marker * * RETURNS: * pointer to the malloced and initialized struct or NULL if an error *********************************************************************/ extern val_value_t * val_new_deleted_value (void); /******************************************************************** * FUNCTION val_new_editvars * * Malloc and initialize the val->editvars field * * INPUTS: * val == val_value_t data structure to use * * OUTPUTS: * val->editvars is malloced and initialized * * RETURNS: * status *********************************************************************/ extern status_t val_new_editvars (val_value_t *val); /******************************************************************** * FUNCTION val_free_editvars * * Free the editing variables for the value node * * INPUTS: * val == val_value_t data structure to use * * OUTPUTS: * val->editvars is freed if set * val->editop set to OP_EDITOP_NONE *********************************************************************/ extern void val_free_editvars (val_value_t *val); #if 1 /*def YUMA123_API_EXTENSIONS*/ /******************************************************************** * FUNCTION val_dump_value_max_w_file * * Printf the specified val_value_t struct to * the logfile, or stdout if none set * Uses conf file format (see ncx/conf.h) * * INPUTS: * val == value to dump * startindent == start indent char count * indent_amount == number of spaces for each indent * display_mode == formatting mode for display * with_meta == TRUE if metaQ should be printed * FALSE to skip meta data * configonly == TRUE if config only nodes should be displayed * FALSE if all nodes should be displayed * outputfile == FILE* destination for the serialized data *********************************************************************/ extern status_t val_dump_value_max_w_file (val_value_t *val, int32 startindent, int32 indent_amount, ncx_display_mode_t display_mode, boolean with_meta, boolean configonly, FILE* outputfile); /******************************************************************** * FUNCTION val_make_serialized_string * * Serializes val_value_t struct to selected formatting mode (xml,json) * * INPUTS: * val == value to serialize * mode == formatting mode for display * FALSE if all nodes should be displayed * RETURNS: * Allocated buffer with the serialized string *********************************************************************/ extern status_t val_make_serialized_string (val_value_t *val, ncx_display_mode_t mode, xmlChar** str); #endif /* YUMA123_API_EXTENSIONS */ #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_val */ yuma123_2.14/netconf/src/ncx/ncxconst.h0000664000175000017500000007773214770023131020166 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ncxconst #define _H_ncxconst /* FILE: ncxconst.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* Contains NCX constants separated to prevent H file include loops ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 14-nov-05 abb Begun; split from ncx.h 04-feb-06 abb Move base/nc.h constants into this file 10-nov-07 abb Moved types typ ncxtypes.h */ #include #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* NETCONF Base URN */ #define NC_VER "1.0" #define NC_PREFIX (const xmlChar *)"nc" #define NC_MODULE (const xmlChar *)"yuma123-netconf" #define NCN_MODULE (const xmlChar *)"notifications" #define NCX_MODULE (const xmlChar *)"yuma-ncx" #define NCX_PREFIX (const xmlChar *)"ncx" /* source of secure and very-secure extensions! */ #define NACM_PREFIX (const xmlChar *)"nacm" #define NC_OK_REPLY (const xmlChar *)"RpcOkReplyType" /* NETCONF namespace used for base:10 and base:1.1 */ #define NC_URN (const xmlChar *)"urn:ietf:params:xml:ns:netconf:base:1.0" /* NETCONF SSH End of Message Marker */ #define NC_SSH_END "]]>]]>" #define NC_SSH_END_LEN 6 /* NETCONF SSH End of Chunks Marker */ #define NC_SSH_END_CHUNKS "\n##\n" #define NC_SSH_END_CHUNKS_LEN 4 /* NETCONF Module Owner */ #define NC_OWNER (const xmlChar *)"ietf" /* NETCONF edit-config operation attribute name */ #define NC_OPERATION_ATTR_NAME (const xmlChar *)"operation" #define NC_RPC_REPLY_TYPE (const xmlChar *)"rpc-reply" #define NCX_SSH_PORT 22 #define NCX_NCSSH_PORT 830 #define INVALID_URN (const xmlChar *)"INVALID" /* base:1.1 subtree wildcard URN */ #define WILDCARD_URN (const xmlChar *)"" /* max number len to use for static buffer allocation only */ #define NCX_MAX_NUMLEN 47 /* all name fields in YANG can be 1 to N bytes * set a limit based on likely malloc failure */ #define NCX_MAX_NLEN 0xffffffe #define NCX_MAX_USERNAME_LEN 127 /* ncxserver server transport */ #define NCX_SERVER_TRANSPORT "ssh" /* ncxserver server transport for local connections*/ #define NCX_SERVER_TRANSPORT_LOCAL "local" /* ncxserver version */ #define NCX_SERVER_VERSION 1 /* URN for NETCONF standard modules */ #define NC_URN1 (const xmlChar *)"urn:netconf:params:xml:ns:" /* URN for with-defaults 'default' XML attribute */ #define NC_WD_ATTR_URN (const xmlChar *)\ "urn:ietf:params:xml:ns:netconf:default:1.0" #define NC_WD_ATTR_PREFIX (const xmlChar *)"wda" /* URN for NCX extensions */ #define NCX_URN (const xmlChar *)"http://netconfcentral.org/ns/yuma-ncx" #define NCX_MOD (const xmlChar *)"yuma-ncx" /* URN for XSD */ #define XSD_URN (const xmlChar *)"http://www.w3.org/2001/XMLSchema" #define XSD_PREFIX (const xmlChar *)"xs" /* URN for XSI */ #define XSI_URN (const xmlChar *)\ "http://www.w3.org/2001/XMLSchema-instance" #define XSI_PREFIX (const xmlChar *)"xsi" /* URN for XML */ #define XML_URN (const xmlChar *)\ "http://www.w3.org/XML/1998/namespace" #define XML_PREFIX (const xmlChar *)"xml" /* URN for NETCONF Notifications */ #define NCN_URN (const xmlChar *)\ "urn:ietf:params:xml:ns:netconf:notification:1.0" #define NCN_PREFIX (const xmlChar *)"ncn" /* schemaLocation string */ #define NCX_SCHEMA_LOC (const xmlChar *)"http://www.netconfcentral.org/xsd/" #define NCX_XSD_EXT (const xmlChar *)"xsd" #define NCX_URNP1 (const xmlChar *)"http://" /* default prefix to use for NCX namespace */ #define NCX_PREFIX (const xmlChar *)"ncx" #define NCX_PREFIX_LEN 3 /* default prefix for the xmlns namespace (not really used */ #define NS_PREFIX (const xmlChar *)"namespace" #define NS_PREFIX_LEN 2 #define NS_URN (const xmlChar *)"http://www.w3.org/2000/xmlns/" /* needed for xml:lang attribute */ #define NCX_XML_URN (const xmlChar *)"http://www.w3.org/XML/1998/namespace" #define NCX_XML_SLOC (const xmlChar *)\ "http://www.w3.org/XML/1998/namespace"\ "\n http://www.w3.org/2001/xml.xsd" #define EMPTY_STRING (const xmlChar *)"" #define NCX_DEF_MODULE (const xmlChar *)"yuma123-netconf" #define NCX_DEF_LANG (const xmlChar *)"en" #define NCX_DEF_MERGETYPE NCX_MERGE_LAST #define NCX_DEF_FRACTION_DIGITS 2 #define NCX_DEF_LINELEN 72 #define NCX_MAX_LINELEN 4095 #define NCX_DEF_WARN_IDLEN 64 #define NCX_DEF_WARN_LINELEN 72 #define NCX_CONF_SUFFIX (const xmlChar *)"conf" #define NCX_CLI_START_CH '-' #define NCX_SQUOTE_CH '\'' #define NCX_QUOTE_CH '\"' #define NCX_VAR_CH '$' /* inline XML entered as [...] */ #define NCX_XML_CH '<' #define NCX_XML1a_CH '[' #define NCX_XML1b_CH '<' #define NCX_XML2a_CH '>' #define NCX_XML2b_CH ']' #define NCX_ASSIGN_CH '=' #define NCX_AT_CH '@' #define NCX_TABSIZE 8 #define NCX_VERSION_BUFFSIZE 24 /* start of comment token in all NCX text */ #define NCX_COMMENT_CH '#' /* scoped identifier field separator token */ #define NCX_SCOPE_CH '/' /* filespec identifier field separator token */ #define NCX_PATHSEP_CH '/' /* prefix scoped identifier field separator token */ #define NCX_MODSCOPE_CH ':' /* start of a double quoted string in NCX text */ #define NCX_QSTRING_CH '"' /* start of a single quoted string in NCX text */ #define NCX_SQSTRING_CH '\'' /* start of an NCX or XPath varbind */ #define NCX_VARBIND_CH '$' /* Standard 0x0 syntax to indicate a HEX number is specified */ #define NCX_IS_HEX_CH(c) ((c)=='x' || (c)=='X') /* max hex digits in the value part of a hex number */ #define NCX_MAX_HEXCHAR 16 /* max attributes that will be printed per line */ #define NCX_ATTR_PER_LINE 3 #define NCX_BIG_ATTR_SIZE 24 /* max decimal digits in a plain int */ #define NCX_MAX_DCHAR 20 /* max digits in a real number */ #define NCX_MAX_RCHAR 255 /* Maximum string length (2^^31 - 1) */ #define NCX_MAX_STRLEN 0x7fffffff /* Maximum string of a single quoted string (64K - 1) */ #define NCX_MAX_Q_STRLEN 0xffff /* max number of cached ncx_filptr_t records */ #define NCX_DEF_FILPTR_CACHESIZE 300 /* default indent amount for nesting XML output */ #define NCX_DEF_INDENT 2 /* default virtual value cache timeout value in seconds * use 0 to disable the cache and refresh a virtual value * every time is is retrieved (1 second default) */ #define NCX_DEF_VTIMEOUT 1 /* Default startup config transaction ID file name */ #define NCX_DEF_STARTUP_TXID_FILE (const xmlChar *)"startup-cfg-txid.txt" /* Default startup config data file name */ #define NCX_DEF_STARTUP_FILE (const xmlChar *)"startup-cfg.xml" #define NCX_YUMA_HOME_STARTUP_FILE (const xmlChar *)\ "$YUMA_HOME/data/startup-cfg.xml" #define NCX_YUMA_HOME_STARTUP_DIR (const xmlChar *)\ "$YUMA_HOME/data/" #define NCX_DOT_YUMA_STARTUP_FILE (const xmlChar *)\ "~/.yuma/startup-cfg.xml" #define NCX_DOT_YUMA_STARTUP_DIR (const xmlChar *)"~/.yuma/" #define NCX_YUMA_INSTALL_STARTUP_FILE (const xmlChar *)\ "$YUMA_INSTALL/data/startup-cfg.xml" #define NCX_DEF_INSTALL_STARTUP_FILE (const xmlChar *)\ "/etc/yuma/startup-cfg.xml" /* Default backup config data file name */ #define NCX_DEF_BACKUP_FILE (const xmlChar *)"backup-cfg.xml" /* default conrm-tmieout value in seconds */ #define NCX_DEF_CONFIRM_TIMEOUT 600 /* default value for the with-defaults option */ #define NCX_DEF_WITHDEF NCX_WITHDEF_EXPLICIT /* default value for the with-metadata option */ #define NCX_DEF_WITHMETA FALSE /* default value for the agent superuser */ #define NCX_DEF_SUPERUSER NCX_EL_SUPERUSER #define NCX_DEF_STREAM_NAME (const xmlChar *)"NETCONF" #define NCX_DEF_STREAM_DESCR (const xmlChar *)\ "default NETCONF event stream" /* String start char: * if the XML string starts with a double quote * then it will be interpreted as whitespace-allowed */ #define NCX_STR_START (xmlChar)'"' #define NCX_STR_END (xmlChar)'"' /* Enumeration number start and end chars */ #define NCX_ENU_START (xmlChar)'(' #define NCX_ENU_END (xmlChar)')' /* String names matching psd_pstype_t enums */ #define NCX_PSTYP_CLI (const xmlChar *)"cli" #define NCX_PSTYP_DATA (const xmlChar *)"data" #define NCX_PSTYP_RPC (const xmlChar *)"rpc" /* file name prefix to use for split SIL files */ #define NCX_USER_SIL_PREFIX (const xmlChar *)"u_" #define NCX_YUMA_SIL_PREFIX (const xmlChar *)"y_" /* Min and max int */ #define NCX_MIN_INT INT_MIN #define NCX_MAX_INT INT_MAX #define NCX_MIN_INT8 -128 #define NCX_MAX_INT8 127 #define NCX_MIN_INT16 -32768 #define NCX_MAX_INT16 32767 /* Min and max long */ #define NCX_MAX_LONG 9223372036854775807LL #define NCX_MIN_LONG (-NCX_MAX_LONG - 1LL) /* Min and max uint */ #define NCX_MIN_UINT 0 #define NCX_MAX_UINT UINT_MAX #define NCX_MAX_UINT8 255 #define NCX_MAX_UINT16 65535 /* Min and max uint64 */ #define NCX_MIN_ULONG 0 #define NCX_MAX_ULONG 18446744073709551615ULL /* Min and max float */ #define NCX_MIN_FLOAT "-inf" #define NCX_MAX_FLOAT "inf" /* Min and max double */ #define NCX_MIN_DOUBLE "-inf" #define NCX_MAX_DOUBLE "inf" /* NETCONF built-in config names */ #define NCX_CFG_RUNNING (const xmlChar *)"running" #define NCX_CFG_CANDIDATE (const xmlChar *)"candidate" #define NCX_CFG_STARTUP (const xmlChar *)"startup" #define NCX_NUM_CFGS 3 /* NCX Extension configuration names */ #define NCX_CFG_ROLLBACK (const xmlChar *)"rollback" /* NCX and NETCONF element and attribute names * * NETCONF common parameter names * NETCONF built-in error-info element names * name of Xpath index for position-only table index * NCX token and element names * NCX token names of the builtin types used in the clause * */ #define NCX_EL_ACCESS_RC (const xmlChar *)"read-create" #define NCX_EL_ACCESS_RO (const xmlChar *)"read-only" #define NCX_EL_ACCESS_RW (const xmlChar *)"read-write" #define NCX_EL_AUDIT_LOG (const xmlChar *)"audit-log" #define NCX_EL_AUDIT_LOG_APPEND (const xmlChar *)"audit-log-append" #define NCX_AUGHOOK_START (const xmlChar *)"__" #define NCX_AUGHOOK_END (const xmlChar *)".A__" #define NCX_EL_ABSTRACT (const xmlChar *)"abstract" #define NCX_EL_ACCESS_CONTROL (const xmlChar *)"access-control" #define NCX_EL_ADDRESS (const xmlChar *)"address" #define NCX_EL_ALT_NAME (const xmlChar *)"alt-name" #define NCX_EL_ANY (const xmlChar *)"any" #define NCX_EL_ANYDATA (const xmlChar *)"anydata" #define NCX_EL_ANYXML (const xmlChar *)"anyxml" #define NCX_EL_APPINFO (const xmlChar *)"appinfo" #define NCX_EL_APPLICATION (const xmlChar *)"application" #define NCX_EL_BAD_ATTRIBUTE (const xmlChar *)"bad-attribute" #define NCX_EL_BAD_ELEMENT (const xmlChar *)"bad-element" #define NCX_EL_BAD_NAMESPACE (const xmlChar *)"bad-namespace" #define NCX_EL_BAD_VALUE (const xmlChar *)"bad-value" #define NCX_EL_BINARY (const xmlChar *)"binary" #define NCX_EL_BITS (const xmlChar *)"bits" #define NCX_EL_BOOLEAN (const xmlChar *)"boolean" #define NCX_EL_BRIEF (const xmlChar *)"brief" #define NCX_EL_BYTE (const xmlChar *)"byte" #define NCX_EL_C (const xmlChar *)"c" #define NCX_EL_CPP_TEST (const xmlChar *)"cpp_test" #define NCX_EL_CANCEL (const xmlChar *)"cancel" #define NCX_EL_CANCEL_COMMIT (const xmlChar *)"cancel-commit" #define NCX_EL_CANDIDATE (const xmlChar *)"candidate" #define NCX_EL_CAPABILITIES (const xmlChar *)"capabilities" #define NCX_EL_CAPABILITY (const xmlChar *)"capability" #define NCX_EL_CASE (const xmlChar *)"case" #define NCX_EL_CASE_NAME (const xmlChar *)"case-name" #define NCX_EL_CHOICE (const xmlChar *)"choice" #define NCX_EL_CHOICE_NAME (const xmlChar *)"choice-name" #define NCX_EL_CLASS (const xmlChar *)"class" #define NCX_EL_CLI (const xmlChar *)"cli" #define NCX_EL_CLOSE_SESSION (const xmlChar *)"close-session" #define NCX_EL_COMMIT (const xmlChar *)"commit" #define NCX_EL_COMPLETE (const xmlChar *)"complete" #define NCX_EL_CONDITION (const xmlChar *)"condition" #define NCX_EL_CONFIG (const xmlChar *)"config" #define NCX_EL_CONFIRMED (const xmlChar *)"confirmed" #define NCX_EL_CONFIRMED_COMMIT (const xmlChar *)"confirmed-commit" #define NCX_EL_CONFIRM_TIMEOUT (const xmlChar *)"confirm-timeout" #define NCX_EL_CONTAINER (const xmlChar *)"container" #define NCX_EL_CONTACT_INFO (const xmlChar *)"contact-info" #define NCX_EL_CONTINUE_ON_ERROR (const xmlChar *)"continue-on-error" #define NCX_EL_COPY (const xmlChar *)"copy" #define NCX_EL_COPYRIGHT (const xmlChar *)"copyright" #define NCX_EL_COPY_CONFIG (const xmlChar *)"copy-config" #define NCX_EL_CREATE (const xmlChar *)"create" #define NCX_EL_CURRENT (const xmlChar *)"current" #define NCX_EL_DATA (const xmlChar *)"data" #define NCX_EL_DATAPATH (const xmlChar *)"datapath" #define NCX_EL_DATA_CLASS (const xmlChar *)"data-class" #define NCX_EL_DEBUG (const xmlChar *)"debug" #define NCX_EL_DECIMAL64 (const xmlChar *)"decimal64" #define NCX_EL_DEF (const xmlChar *)"def" #define NCX_EL_DEFAULT (const xmlChar *)"default" #define NCX_EL_DEFAULT_OPERATION (const xmlChar *)"default-operation" #define NCX_EL_DEFAULT_PARM (const xmlChar *)"default-parm" #define NCX_EL_DEFAULT_PARM_EQUALS_OK (const xmlChar *)"default-parm-equals-ok" #define NCX_EL_DEFAULT_STYLE (const xmlChar *)"default-style" #define NCX_EL_DEFINITIONS (const xmlChar *)"definitions" #define NCX_EL_DEFOP (const xmlChar *)"default-operation" #define NCX_EL_DELETE (const xmlChar *)"delete" #define NCX_EL_DELETE_CONFIG (const xmlChar *)"delete-config" #define NCX_EL_DEPRECATED (const xmlChar *)"deprecated" #define NCX_EL_DESCRIPTION (const xmlChar *)"description" #define NCX_EL_DEVIATION (const xmlChar *)"deviation" #define NCX_EL_DISABLED (const xmlChar *)"disabled" #define NCX_EL_DISCARD_CHANGES (const xmlChar *)"discard-changes" #define NCX_EL_DOUBLE (const xmlChar *)"double" #define NCX_EL_DYNAMIC (const xmlChar *)"dynamic" #define NCX_EL_EDIT_CONFIG (const xmlChar *)"edit-config" #define NCX_EL_EMPTY (const xmlChar *)"empty" #define NCX_EL_ENFORCING (const xmlChar *)"enforcing" #define NCX_EL_ENUM (const xmlChar *)"enum" #define NCX_EL_ENUMERATION (const xmlChar *)"enumeration" #define NCX_EL_ERROR (const xmlChar *)"error" #define NCX_EL_ERROR_APP_TAG (const xmlChar *)"error-app-tag" #define NCX_EL_ERROR_INFO (const xmlChar *)"error-info" #define NCX_EL_ERROR_LEVEL (const xmlChar *)"error-level" #define NCX_EL_ERROR_NUMBER (const xmlChar *)"error-number" #define NCX_EL_ERROR_OPTION (const xmlChar *)"error-option" #define NCX_EL_ERROR_MESSAGE (const xmlChar *)"error-message" #define NCX_EL_ERROR_PATH (const xmlChar *)"error-path" #define NCX_EL_ERROR_SEVERITY (const xmlChar *)"error-severity" #define NCX_EL_ERROR_TAG (const xmlChar *)"error-tag" #define NCX_EL_ERROR_TYPE (const xmlChar *)"error-type" #define NCX_EL_EVENTLOG_SIZE (const xmlChar *)"eventlog-size" #define NCX_EL_EVENTTIME (const xmlChar *)"eventTime" #define NCX_EL_EXACT (const xmlChar *)"exact" #define NCX_EL_EXACT_NOCASE (const xmlChar *)"exact-nocase" #define NCX_EL_EXEC (const xmlChar *)"exec" #define NCX_EL_EXPLICIT (const xmlChar *)"explicit" #define NCX_EL_EXTEND (const xmlChar *)"extend" #define NCX_EL_EXTERN (const xmlChar *)"extern" #define NCX_EL_FALSE (const xmlChar *)"false" #define NCX_EL_FEATURE_CODE_DEFAULT \ (const xmlChar *)"feature-code-default" #define NCX_EL_FEATURE_ENABLE_DEFAULT \ (const xmlChar *)"feature-enable-default" #define NCX_EL_FEATURE_STATIC (const xmlChar *)"feature-static" #define NCX_EL_FEATURE_DYNAMIC (const xmlChar *)"feature-dynamic" #define NCX_EL_FEATURE_ENABLE (const xmlChar *)"feature-enable" #define NCX_EL_FEATURE_DISABLE (const xmlChar *)"feature-disable" #define NCX_EL_FILTER (const xmlChar *)"filter" #define NCX_EL_FIRST (const xmlChar *)"first" #define NCX_EL_FIRST_NOCASE (const xmlChar *)"first-nocase" #define NCX_EL_FLAG (const xmlChar *)"flag" #define NCX_EL_FLOAT (const xmlChar *)"float" #define NCX_EL_FLOAT64 (const xmlChar *)"float64" #define NCX_EL_FORMAT (const xmlChar *)"format" #define NCX_EL_FULL (const xmlChar *)"full" #define NCX_EL_GET (const xmlChar *)"get" #define NCX_EL_GET_CONFIG (const xmlChar *)"get-config" #define NCX_EL_GET_SCHEMA (const xmlChar *)"get-schema" #define NCX_EL_GROUP_ID (const xmlChar *)"group-id" #define NCX_EL_H (const xmlChar *)"h" #define NCX_EL_HEADER (const xmlChar *)"header" #define NCX_EL_HELLO (const xmlChar *)"hello" #define NCX_EL_HELLO_TIMEOUT (const xmlChar *)"hello-timeout" #define NCX_EL_HELP (const xmlChar *)"help" #define NCX_EL_HIDDEN (const xmlChar *)"hidden" #define NCX_EL_HOME (const xmlChar *)"home" #define NCX_EL_HTML (const xmlChar *)"html" #define NCX_EL_IDENTIFIER (const xmlChar *)"identifier" #define NCX_EL_IDENTITYREF (const xmlChar *)"identityref" #define NCX_EL_IDLE_TIMEOUT (const xmlChar *)"idle-timeout" #define NCX_EL_ILLEGAL (const xmlChar *)"illegal" #define NCX_EL_IMPORT (const xmlChar *)"import" #define NCX_EL_IMPORTS (const xmlChar *)"imports" #define NCX_EL_INCLUDE (const xmlChar *)"include" #define NCX_EL_INDENT (const xmlChar *)"indent" #define NCX_EL_INFO (const xmlChar *)"info" #define NCX_EL_INPUT (const xmlChar *)"input" #define NCX_EL_INSTANCE_IDENTIFIER \ (const xmlChar *)"instance-identifier" #define NCX_EL_INT (const xmlChar *)"int" #define NCX_EL_INT8 (const xmlChar *)"int8" #define NCX_EL_INT16 (const xmlChar *)"int16" #define NCX_EL_INT32 (const xmlChar *)"int32" #define NCX_EL_INT64 (const xmlChar *)"int64" #define NCX_EL_JSON (const xmlChar *)"json" #define NCX_EL_KEY (const xmlChar *)"key" #define NCX_EL_KILL_SESSION (const xmlChar *)"kill-session" #define NCX_EL_LANG (const xmlChar *)"xml:lang" #define NCX_EL_LAST_MODIFIED (const xmlChar *)"last-modified" #define NCX_EL_LEAFREF (const xmlChar *)"leafref" #define NCX_EL_LINESIZE (const xmlChar *)"linesize" #define NCX_EL_LIST (const xmlChar *)"list" #define NCX_EL_TCP_DIRECT_ADDRESS (const xmlChar *)"tcp-direct-address" #define NCX_EL_TCP_DIRECT_PORT (const xmlChar *)"tcp-direct-port" #define NCX_EL_NCXSERVER_SOCKNAME (const xmlChar *)"ncxserver-sockname" #define NCX_EL_LOAD (const xmlChar *)"load" #define NCX_EL_LOAD_CONFIG (const xmlChar *)"load-config" #define NCX_EL_LOCK (const xmlChar *)"lock" #define NCX_EL_LOCK_SOURCE (const xmlChar *)"lock-source" #define NCX_EL_LOG (const xmlChar *)"log" #define NCX_EL_LOGAPPEND (const xmlChar *)"log-append" #define NCX_EL_LOGLEVEL (const xmlChar *)"log-level" #define NCX_EL_LONG (const xmlChar *)"long" #define NCX_EL_MAGIC (const xmlChar *)"magic" #define NCX_EL_MAX_ACCESS (const xmlChar *)"max-access" #define NCX_EL_MAX_BURST (const xmlChar *)"max-burst" #define NCX_EL_MERGE (const xmlChar *)"merge" #define NCX_EL_MESSAGE_ID (const xmlChar *)"message-id" #define NCX_EL_METADATA (const xmlChar *)"metadata" #define NCX_EL_MISSING_CHOICE (const xmlChar *)"missing-choice" #define NCX_EL_MODPATH (const xmlChar *)"modpath" #define NCX_EL_MODULE (const xmlChar *)"module" #define NCX_EL_MODULES (const xmlChar *)"modules" #define NCX_EL_MOD_REVISION (const xmlChar *)"mod-revision" #define NCX_EL_MONITOR (const xmlChar *)"monitor" #define NCX_EL_NAME (const xmlChar *)"name" #define NCX_EL_NCX (const xmlChar *)"ncx" #define NCX_EL_NCXCONNECT (const xmlChar *)"ncx-connect" #define NCX_EL_NAMESPACE (const xmlChar *)"namespace" #define NCX_EL_NETCONF (const xmlChar *)"netconf" #define NCX_EL_NETCONF10 (const xmlChar *)"netconf1.0" #define NCX_EL_NETCONF11 (const xmlChar *)"netconf1.1" #define NCX_EL_NO (const xmlChar *)"no" #define NCX_EL_NODEFAULT (const xmlChar *)"no default" #define NCX_EL_NODUPLICATES (const xmlChar *)"no-duplicates" #define NCX_EL_NONE (const xmlChar *)"none" #define NCX_EL_NON_UNIQUE (const xmlChar *)"non-unique" #define NCX_EL_NON_ADVERTISED_MODULE (const xmlChar *)"non-advertised-module" #define NCX_EL_NO_OP (const xmlChar *)"no-op" #define NCX_EL_NOOP_ELEMENT (const xmlChar *)"noop-element" #define NCX_EL_NOTIFICATION (const xmlChar *)"notification" #define NCX_EL_NOT_USED (const xmlChar *)"not-used" #define NCX_EL_NOT_SET (const xmlChar *)"not-set" #define NCX_EL_NULL (const xmlChar *)"null" #define NCX_EL_OBJECT (const xmlChar *)"object" #define NCX_EL_OBJECTS (const xmlChar *)"objects" #define NCX_EL_OBSOLETE (const xmlChar *)"obsolete" #define NCX_EL_OFF (const xmlChar *)"off" #define NCX_EL_OK (const xmlChar *)"ok" #define NCX_EL_OK_ELEMENT (const xmlChar *)"ok-element" #define NCX_EL_ONE (const xmlChar *)"one" #define NCX_EL_ONE_NOCASE (const xmlChar *)"one-nocase" #define NCX_EL_ORDER (const xmlChar *)"order" #define NCX_EL_ORDER_L (const xmlChar *)"loose" #define NCX_EL_ORDER_S (const xmlChar *)"strict" #define NCX_EL_OTHER (const xmlChar *)"other" #define NCX_EL_OUTPUT (const xmlChar *)"output" #define NCX_EL_OWNER (const xmlChar *)"owner" #define NCX_EL_PARM (const xmlChar *)"parm" #define NCX_EL_PARMSET (const xmlChar *)"parmset" #define NCX_EL_PARMS (const xmlChar *)"parms" #define NCX_EL_PASSWORD (const xmlChar *)"password" #define NCX_EL_PATH (const xmlChar *)"path" #define NCX_EL_PATTERN (const xmlChar *)"pattern" #define NCX_EL_PERMISSIVE (const xmlChar *)"permissive" #define NCX_EL_PERSIST (const xmlChar *)"persist" #define NCX_EL_PERSIST_ID (const xmlChar *)"persist-id" #define NCX_EL_PLAIN (const xmlChar *)"plain" #define NCX_EL_PORT (const xmlChar *)"port" #define NCX_EL_POS (const xmlChar *)"pos" #define NCX_EL_POSITION (const xmlChar *)"position" #define NCX_EL_PREFIX (const xmlChar *)"prefix" #define NCX_EL_PROTOCOL (const xmlChar *)"protocol" #define NCX_EL_PROTOCOLS (const xmlChar *)"protocols" #define NCX_EL_QNAME (const xmlChar *)"qname" #define NCX_EL_REMOVE (const xmlChar *)"remove" #define NCX_EL_REPLACE (const xmlChar *)"replace" #define NCX_EL_REPORT_ALL (const xmlChar *)"report-all" #define NCX_EL_REPORT_ALL_TAGGED (const xmlChar *)"report-all-tagged" #define NCX_EL_RESTART (const xmlChar *)"restart" #define NCX_EL_REVISION (const xmlChar *)"revision" #define NCX_EL_REVISION_HISTORY (const xmlChar *)"revision-history" #define NCX_EL_ROLLBACK_ON_ERROR (const xmlChar *)"rollback-on-error" #define NCX_EL_ROOT (const xmlChar *)"root" #define NCX_EL_RPC (const xmlChar *)"rpc" #define NCX_EL_RPC_ERROR (const xmlChar *)"rpc-error" #define NCX_EL_RPC_OUTPUT (const xmlChar *)"rpc-output" #define NCX_EL_RPC_REPLY (const xmlChar *)"rpc-reply" #define NCX_EL_RPC_TYPE (const xmlChar *)"rpc-type" #define NCX_EL_RUNNING (const xmlChar *)"running" #define NCX_EL_RUNPATH (const xmlChar *)"runpath" #define NCX_EL_SCHEMA_INSTANCE (const xmlChar *)"schema-instance" #define NCX_EL_SCRIPT (const xmlChar *)"script" #define NCX_EL_SECURE (const xmlChar *)"secure" #define NCX_EL_SELECT (const xmlChar *)"select" #define NCX_EL_SEQUENCE_ID (const xmlChar *)"sequence-id" #define NCX_EL_SERVER (const xmlChar *)"server" #define NCX_EL_SESSION_ID (const xmlChar *)"session-id" #define NCX_EL_SET (const xmlChar *)"set" #define NCX_EL_SHORT (const xmlChar *)"short" #define NCX_EL_SHOW_ERRORS (const xmlChar *)"show-errors" #define NCX_EL_SHUTDOWN (const xmlChar *)"shutdown" #define NCX_EL_SIL_DELETE_CHILDREN_FIRST (const xmlChar *)\ "sil-delete-children-first" #define NCX_EL_SLIST (const xmlChar *)"slist" #define NCX_EL_SOURCE (const xmlChar *)"source" #define NCX_EL_SQL (const xmlChar *)"sql" #define NCX_EL_SQLDB (const xmlChar *)"sqldb" #define NCX_EL_START (const xmlChar *)"start" #define NCX_EL_STARTUP (const xmlChar *)"startup" #define NCX_EL_STATE (const xmlChar *)"state" #define NCX_EL_STATIC (const xmlChar *)"static" #define NCX_EL_STATUS (const xmlChar *)"status" #define NCX_EL_STOP_ON_ERROR (const xmlChar *)"stop-on-error" #define NCX_EL_STRING (const xmlChar *)"string" #define NCX_EL_STRUCT (const xmlChar *)"struct" #define NCX_EL_SUBDIRS (const xmlChar *)"subdirs" #define NCX_EL_SUBMODULE (const xmlChar *)"submodule" #define NCX_EL_SUBTREE (const xmlChar *)"subtree" #define NCX_EL_SUPERUSER (const xmlChar *)"superuser" #define NCX_EL_SYNTAX (const xmlChar *)"syntax" #define NCX_EL_SYSTEM_SORTED (const xmlChar *)"system-sorted" #define NCX_EL_TABLE (const xmlChar *)"table" #define NCX_EL_TARGET (const xmlChar *)"target" #define NCX_EL_TESTONLY (const xmlChar *)"test-only" #define NCX_EL_TEST_OPTION (const xmlChar *)"test-option" #define NCX_EL_TESTTHENSET (const xmlChar *)"test-then-set" #define NCX_EL_TEXT (const xmlChar *)"text" #define NCX_EL_TG2 (const xmlChar *)"tg2" #define NCX_EL_TIMEOUT (const xmlChar *)"timeout" #define NCX_EL_TREE (const xmlChar *)"tree" #define NCX_EL_TXT (const xmlChar *)"txt" #define NCX_EL_TRANSPORT (const xmlChar *)"transport" #define NCX_EL_TRIM (const xmlChar *)"trim" #define NCX_EL_TRUE (const xmlChar *)"true" #define NCX_EL_TYPE (const xmlChar *)"type" #define NCX_EL_UC (const xmlChar *)"uc" #define NCX_EL_UH (const xmlChar *)"uh" #define NCX_EL_UINT8 (const xmlChar *)"uint8" #define NCX_EL_UINT16 (const xmlChar *)"uint16" #define NCX_EL_UINT32 (const xmlChar *)"uint32" #define NCX_EL_UINT64 (const xmlChar *)"uint64" #define NCX_EL_UNION (const xmlChar *)"union" #define NCX_EL_UNITS (const xmlChar *)"units" #define NCX_EL_UNLOCK (const xmlChar *)"unlock" #define NCX_EL_UNSIGNED_BYTE (const xmlChar *)"unsignedByte" #define NCX_EL_UNSIGNED_INT (const xmlChar *)"unsignedInt" #define NCX_EL_UNSIGNED_LONG (const xmlChar *)"unsignedLong" #define NCX_EL_UNSIGNED_SHORT (const xmlChar *)"unsignedShort" #define NCX_EL_ULONG (const xmlChar *)"ulong" #define NCX_EL_UPDATE (const xmlChar *)"update" #define NCX_EL_URL (const xmlChar *)"url" #define NCX_EL_URLTARGET (const xmlChar *)"urltarget" #define NCX_EL_USAGE (const xmlChar *)"usage" #define NCX_EL_USAGE_C (const xmlChar *)"conditional" #define NCX_EL_USAGE_M (const xmlChar *)"mandatory" #define NCX_EL_USAGE_O (const xmlChar *)"optional" #define NCX_EL_USER (const xmlChar *)"user" #define NCX_EL_USER_WRITE (const xmlChar *)"user-write" #define NCX_EL_USEXMLORDER (const xmlChar *)"usexmlorder" #define NCX_EL_USTRING (const xmlChar *)"ustring" #define NCX_EL_VALIDATE (const xmlChar *)"validate" #define NCX_EL_VALUE (const xmlChar *)"value" #define NCX_EL_VAR (const xmlChar *)"var" #define NCX_EL_VARS (const xmlChar *)"vars" #define NCX_EL_VERSION (const xmlChar *)"version" #define NCX_EL_VERY_SECURE (const xmlChar *)"very-secure" #define NCX_EL_WARNING (const xmlChar *)"warning" #define NCX_EL_WARN_IDLEN (const xmlChar *)"warn-idlen" #define NCX_EL_WARN_LINELEN (const xmlChar *)"warn-linelen" #define NCX_EL_WARN_OFF (const xmlChar *)"warn-off" #define NCX_EL_WHEN (const xmlChar *)"when" #define NCX_EL_WITH_DEFAULTS (const xmlChar *)"with-defaults" #define NCX_EL_WITH_METADATA (const xmlChar *)"with-metadata" #define NCX_EL_WITH_NMDA (const xmlChar *)"with-nmda" #define NCX_EL_WITH_STARTUP (const xmlChar *)"with-startup" #define NCX_EL_WITH_URL (const xmlChar *)"with-url" #define NCX_EL_WITH_VALIDATE (const xmlChar *)"with-validate" #define NCX_EL_WRITABLE_RUNNING (const xmlChar *)"writable-running" #define NCX_EL_XCONTAINER (const xmlChar *)"xcontainer" #define NCX_EL_XLIST (const xmlChar *)"xlist" #define NCX_EL_XML (const xmlChar *)"xml" #define NCX_EL_XML_NONS (const xmlChar *)"xml-nons" #define NCX_EL_XPATH (const xmlChar *)"xpath" #define NCX_EL_XSD (const xmlChar *)"xsd" #define NCX_EL_XSDLIST (const xmlChar *)"xsdlist" #define NCX_EL_YANG (const xmlChar *)"yang" #define NCX_EL_YC (const xmlChar *)"yc" #define NCX_EL_YES (const xmlChar *)"yes" #define NCX_EL_YH (const xmlChar *)"yh" #define NCX_EL_YIN (const xmlChar *)"yin" #define NCX_EL_YUMA_HOME (const xmlChar *)"yuma-home" #define NCX_EL_MAX_SESSIONS (const xmlChar *)"max-sessions" /* bit definitions for ncx_lstr_t flags field */ #define NCX_FL_RANGE_ERR bit0 #define NCX_FL_VALUE_ERR bit1 /* bit definitions for NETCONF session protocol versions */ #define NCX_FL_PROTO_NETCONF10 bit0 #define NCX_FL_PROTO_NETCONF11 bit1 /* textual parameter tags for various NCX functions * that use boolean parameters */ #define NCX_SAVESTR TRUE #define NCX_NO_SAVESTR FALSE #define NCX_LMEM_ENUM(L) ((L)->val.enu) #define NCX_LMEM_STR(L) ((L)->val.str) #define NCX_LMEM_STRVAL(L) ((L)->val.str) #define NCX_LMEM_NUM(L) ((L)->val.num) /* constants for isglobal function parameter */ #define ISGLOBAL TRUE #define ISLOCAL FALSE /* bad-data enumeration values */ #define E_BAD_DATA_IGNORE (const xmlChar *)"ignore" #define E_BAD_DATA_WARN (const xmlChar *)"warn" #define E_BAD_DATA_CHECK (const xmlChar *)"check" #define E_BAD_DATA_ERROR (const xmlChar *)"error" #define COPYRIGHT_STRING_LINE0 "Copyright (c) 2008-2012, Andy Bierman, All Rights Reserved.\n" #define COPYRIGHT_STRING_LINE1 "Copyright (c) 2013-2025, Vladimir Vassilev, All Rights Reserved.\n" #define COPYRIGHT_STRING COPYRIGHT_STRING_LINE0 COPYRIGHT_STRING_LINE1 #define Y_PREFIX (const xmlChar *)"y_" #define U_PREFIX (const xmlChar *)"u_" #define EDIT_SUFFIX (const xmlChar *)"_edit" #define GET_SUFFIX (const xmlChar *)"_get" #define MRO_SUFFIX (const xmlChar *)"_mro" #define INIT_SUFFIX (const xmlChar *)"_init" #define INIT2_SUFFIX (const xmlChar *)"_init2" #define CLEANUP_SUFFIX (const xmlChar *)"_cleanup" #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_ncxconst */ yuma123_2.14/netconf/src/ncx/xml_util.h0000664000175000017500000007731314770023131020157 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_xml_util #define _H_xml_util /* FILE: xml_util.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* General Utilities that simplify usage of the libxml2 functions - xml_node_t allocation - xml_new_node - xml_init_node - xml_free_node - xml_clean_node - XmlReader utilities - xml_get_reader_from_filespec (parse debug test documents) - xml_get_reader_for_session - xml_reset_reader_for_session - xml_free_reader - xml_get_node_name (xmlparser enumeration for node type) - xml_advance_reader - xml_consume_node - xml_consume_start_node - xml_consume_end_node - xml_node_match - xml_endnode_match - xml_docdone - xml_dump_node (debug printf) - XML Attribute utilities - xml_init_attrs - xml_add_attr - xml_first_attr - xml_next_attr - xml_find_attr - xml_clean_attrs - XmlChar string utilites - xml_strlen - xml_strcpy - xml_strncpy - xml_strdup - xml_strcat - xml_strncat - xml_strndup - xml_ch_strndup - xml_strcmp - xml_strncmp - xml_isspace - xml_isspace_str - xml_copy_clean_string - xml_convert_char_entity ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 14-oct-05 abb begun 2-jan-06 abb rewrite xml_consume_* API to use simpler xml_node_t */ #ifndef _H_ncxconst #include "ncxconst.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifdef LIBXML2_ENABLED #include #include #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define MAX_CHAR_ENT 8 #define XML_START_MSG ((const xmlChar *)\ "") #define XML_START_MSG_SIZE 38 #define XML_READER_OPTIONS XML_PARSE_RECOVER+XML_PARSE_NOERROR+\ XML_PARSE_NOWARNING+XML_PARSE_NOBLANKS+XML_PARSE_NONET+ \ XML_PARSE_XINCLUDE #define XML_SES_URL "netconf://pdu" /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* queue of xml_attr_t */ typedef dlq_hdr_t xml_attrs_t; /* represents one attribute */ typedef struct xml_attr_t_ { dlq_hdr_t attr_qhdr; xmlns_id_t attr_ns; xmlns_id_t attr_xmlns_ns; const xmlChar *attr_qname; const xmlChar *attr_name; xmlChar *attr_dname; /* full qualified name if any */ xmlChar *attr_val; struct xpath_pcb_t_ *attr_xpcb; } xml_attr_t; /* only 4 types of nodes returned */ typedef enum xml_nodetyp_t_ { XML_NT_NONE, XML_NT_EMPTY, /* standalone empty node */ XML_NT_START, /* start-tag of an element */ XML_NT_END, /* end-tag of an element */ XML_NT_STRING /* string content node */ } xml_nodetyp_t; /* gather node data into a simple struct * If a simple value is present, the the simval pointer will * be non-NULL and point at the value string after it has * been trimmed and any character entities translated */ typedef struct xml_node_t_ { xml_nodetyp_t nodetyp; xmlns_id_t nsid; xmlns_id_t contentnsid; const xmlChar *module; xmlChar *qname; const xmlChar *elname; const xmlChar *simval; /* may be cleaned val */ uint32 simlen; xmlChar *simfree; /* non-NULL if simval is freed */ int depth; xml_attrs_t attrs; } xml_node_t; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION xml_new_node * * Malloc and init a new xml_node_t struct * * RETURNS: * pointer to new node or NULL if malloc error *********************************************************************/ extern xml_node_t * xml_new_node (void); /******************************************************************** * FUNCTION xml_init_node * * Init an xml_node_t struct * * INPUTS: * node == pointer to node to init *********************************************************************/ extern void xml_init_node (xml_node_t *node); /******************************************************************** * FUNCTION xml_free_node * * Free an xml_node_t struct * * INPUTS: * node == pointer to node to free *********************************************************************/ extern void xml_free_node (xml_node_t *node); /******************************************************************** * FUNCTION xml_clean_node * * Clean an xml_node_t struct * * INPUTS: * node == pointer to node to clean *********************************************************************/ extern void xml_clean_node (xml_node_t *node); #ifdef LIBXML2_ENABLED /******************************************************************** * FUNCTION xml_get_reader_from_filespec * * Get a new xmlTextReader for parsing a debug test file * * INPUTS: * filespec == full filename including path of the * XML instance document to parse * OUTPUTS: * *reader == pointer to new reader or NULL if some error * * RETURNS: * status of the operation *********************************************************************/ extern status_t xml_get_reader_from_filespec (const char *filespec, xmlTextReaderPtr *reader); /******************************************************************** * FUNCTION xml_get_reader_for_session * * Get a new xmlTextReader for parsing the input of a NETCONF session * * INPUTS: * readfn == IO read function to use for this xmlTextReader * closefn == IO close fn to use for this xmlTextReader * context == the ses_cb_t pointer passes as a void * * this will be passed to the read and close functions * OUTPUTS: * *reader == pointer to new reader or NULL if some error * RETURNS: * status of the operation *********************************************************************/ extern status_t xml_get_reader_for_session (xmlInputReadCallback readfn, xmlInputCloseCallback closefn, void *context, xmlTextReaderPtr *reader); /******************************************************************** * FUNCTION xml_reset_reader_for_session * * Reset the xmlTextReader for parsing the input of a NETCONF session * * INPUTS: * readfn == IO read function to use for this xmlTextReader * closefn == IO close fn to use for this xmlTextReader * context == the ses_cb_t pointer passes as a void * * this will be passed to the read and close functions * OUTPUTS: * *reader == pointer to new reader or NULL if some error * RETURNS: * status of the operation *********************************************************************/ extern status_t xml_reset_reader_for_session (xmlInputReadCallback readfn, xmlInputCloseCallback closefn, void *context, xmlTextReaderPtr reader); /******************************************************************** * FUNCTION xml_free_reader * * Free the previously allocated xmlTextReader * * INPUTS: * reader == xmlTextReader to close and deallocate * RETURNS: * none *********************************************************************/ extern void xml_free_reader (xmlTextReaderPtr reader); /******************************************************************** * FUNCTION xml_advance_reader * * Advance to the next node in the specified reader * * INPUTS: * reader == XmlReader already initialized from File, Memory, * or whatever * RETURNS: * FALSE if OEF seen, or TRUE if normal *********************************************************************/ extern boolean xml_advance_reader (xmlTextReaderPtr reader); /******************************************************************** * FUNCTION xml_docdone * * check if the input is completed for a given PDU * * INPUTS: * reader == xml text reader * RETURNS: * TRUE if document is done * FALSE if document is not done or some error * If a node is read, the reader will be pointing to that node *********************************************************************/ extern boolean xml_docdone (xmlTextReaderPtr reader); /******************************************************************** * FUNCTION xml_check_ns * * INPUTS: * reader == XmlReader already initialized from File, Memory, * or whatever * pfs->ns_id == element namespace to check * elname == element name to check * * OUTPUTS: * *id == namespace ID found or 0 if none * *pfix_len == filled in > 0 if one found * real element name will start at pfix_len+1 * if pfix is non-NULL * *badns == pointer to unknown namespace if error returned * RETURNS: * status; could be error if namespace specified but not supported *********************************************************************/ extern status_t xml_check_ns (xmlTextReaderPtr reader, const xmlChar *elname, xmlns_id_t *id, uint32 *pfix_len, const xmlChar **badns); #endif /* LIBXML2_ENABLED */ /******************************************************************** * FUNCTION xml_get_node_name * * get the node type according to the xmlElementType enum list * in /usr/include/libxml/libxml/tree.h * * INPUTS: * nodeval == integer node type from system * RETURNS: * string corresponding to the integer value *********************************************************************/ extern const char * xml_get_node_name (int nodeval); /******************************************************************** * FUNCTION xml_node_match * * check if a specific node is the proper owner, name, and type * * INPUTS: * node == node to match against * nsid == namespace ID to match (0 == match any) * elname == element name to match (NULL == match any) * nodetyp == node type to match (XML_NT_NONE == match any) * RETURNS: * status *********************************************************************/ extern status_t xml_node_match (const xml_node_t *node, xmlns_id_t nsid, const xmlChar *elname, xml_nodetyp_t nodetyp); /******************************************************************** * FUNCTION xml_endnode_match * * check if a specific node is the proper endnode match * for a given startnode * * INPUTS: * startnode == start node to match against * endnode == potential end node to test * RETURNS: * status, *********************************************************************/ extern status_t xml_endnode_match (const xml_node_t *startnode, const xml_node_t *endnode); /******************************************************************** * FUNCTION xml_dump_node * * Debug function to printf xml_node_t contents * * INPUTS: * node == node to dump *********************************************************************/ extern void xml_dump_node (const xml_node_t *node); /******************************************************************** * FUNCTION xml_init_attrs * * initialize an xml_attrs_t variable * * INPUTS: * attrs == attribute queue to init * RETURNS: * none *********************************************************************/ extern void xml_init_attrs (xml_attrs_t *attrs); /******************************************************************** * FUNCTION xml_new_attr * * malloc and init an attribute struct * * INPUTS: * none * RETURNS: * pointer to new xml_attr_t or NULL if malloc error *********************************************************************/ extern xml_attr_t * xml_new_attr (void); /******************************************************************** * FUNCTION xml_free_attr * * free an attribute * * INPUTS: * attr == xml_attr_t to free *********************************************************************/ extern void xml_free_attr (xml_attr_t *attr); /******************************************************************** * FUNCTION xml_add_attr * * add an attribute to an attribute list * * INPUTS: * attrs == attribute queue to init * ns_id == namespace ID (use XMLNS_NONE if no prefix desired) * attr_name == attribute name string * attr_val == attribute value string * RETURNS: * NO_ERR if all okay *********************************************************************/ extern status_t xml_add_attr (xml_attrs_t *attrs, xmlns_id_t ns_id, const xmlChar *attr_name, const xmlChar *attr_val); /******************************************************************** * FUNCTION xml_add_qattr * * add a qualified attribute to an attribute list with a prefix * * INPUTS: * attrs == attribute queue to init * ns_id == namespace ID (use XMLNS_NONE if no prefix desired) * attr_qname == qualified attribute name string * plen == attribute prefix length * attr_val == attribute value string * res == address of return status * * OUTPUTS: * *res == return status * RETURNS: * pointer to attr that was added, NULL if none added *********************************************************************/ extern xml_attr_t * xml_add_qattr (xml_attrs_t *attrs, xmlns_id_t ns_id, const xmlChar *attr_qname, uint32 plen, const xmlChar *attr_val, status_t *res); /******************************************************************** * FUNCTION xml_add_xmlns_attr * * add an xmlns decl to the attribute Queue * * INPUTS: * attrs == attribute queue to add to * ns_id == namespace ID of the xmlns target * pfix == namespace prefix string assigned * == NULL for default namespace * * RETURNS: * NO_ERR if all okay *********************************************************************/ extern status_t xml_add_xmlns_attr (xml_attrs_t *attrs, xmlns_id_t ns_id, const xmlChar *pfix); /******************************************************************** * FUNCTION xml_add_xmlns_attr_string * * add an xmlns decl to the attribute Queue * * INPUTS: * attrs == attribute queue to add to * ns == namespace URI string of the xmlns target * pfix == namespace prefix string assigned * == NULL for default namespace * * RETURNS: * NO_ERR if all okay *********************************************************************/ extern status_t xml_add_xmlns_attr_string (xml_attrs_t *attrs, const xmlChar *ns, const xmlChar *pfix); /******************************************************************** * FUNCTION xml_add_inv_xmlns_attr * * add an xmlns decl to the attribute Queue * for an INVALID namespace. This is needed for the * error-info element within the rpc-error report * * INPUTS: * attrs == attribute queue to add to * ns_id == namespace ID of the xmlns target * pfix == namespace prefix string assigned * == NULL for default namespace * nsval == namespace URI value of invalid namespace * * RETURNS: * NO_ERR if all okay *********************************************************************/ extern status_t xml_add_inv_xmlns_attr (xml_attrs_t *attrs, xmlns_id_t ns_id, const xmlChar *pfix, const xmlChar *nsval); /******************************************************************** * FUNCTION xml_first_attr * * get the first attribute in the list * * INPUTS: * attrs == attribute queue to get from * RETURNS: * pointer to first entry or NULL if none *********************************************************************/ extern xml_attr_t * xml_first_attr (xml_attrs_t *attrs); /******************************************************************** * FUNCTION xml_get_first_attr * * get the first attribute in the attrs list, from an xml_node_t param * * INPUTS: * node == node with the attrQ to use * RETURNS: * pointer to first entry or NULL if none *********************************************************************/ extern xml_attr_t * xml_get_first_attr (const xml_node_t *node); /******************************************************************** * FUNCTION xml_next_attr * * get the next attribute in the list * * INPUTS: * attr == attribute entry to get next for * RETURNS: * pointer to the next entry or NULL if none *********************************************************************/ extern xml_attr_t * xml_next_attr (xml_attr_t *attr); /******************************************************************** * FUNCTION xml_clean_attrs * * clean an xml_attrs_t variable * * INPUTS: * attrs == attribute queue to clean * RETURNS: * none *********************************************************************/ extern void xml_clean_attrs (xml_attrs_t *attrs); /******************************************************************** * FUNCTION xml_find_attr * * Must be called after xxx_xml_consume_node * Go looking for the specified attribute node * INPUTS: * node == xml_node_t to check * nsid == namespace ID of attribute to find * attrname == attribute name to find * RETURNS: * pointer to found xml_attr_t or NULL if not found *********************************************************************/ extern xml_attr_t * xml_find_attr (xml_node_t *node, xmlns_id_t nsid, const xmlChar *attrname); /******************************************************************** * FUNCTION xml_find_attr_q * * Must be called after xxx_xml_consume_node * Go looking for the specified attribute node * INPUTS: * node == xml_node_t to check * nsid == namespace ID of attribute to find * attrname == attribute name to find * RETURNS: * pointer to found xml_attr_t or NULL if not found *********************************************************************/ extern xml_attr_t * xml_find_attr_q (xml_attrs_t *attrs, xmlns_id_t nsid, const xmlChar *attrname); /******************************************************************** * FUNCTION xml_find_ro_attr * * Must be called after xxx_xml_consume_node * Go looking for the specified attribute node * INPUTS: * node == xml_node_t to check * nsid == namespace ID of attribute to find * attrname == attribute name to find * RETURNS: * pointer to found xml_attr_t or NULL if not found *********************************************************************/ extern const xml_attr_t * xml_find_ro_attr (const xml_node_t *node, xmlns_id_t nsid, const xmlChar *attrname); /******************************************************************** * FUNCTION xml_strlen * * String len for xmlChar -- does not check for buffer overflow * INPUTS: * str == buffer to check * RETURNS: * number of xmlChars before null terminator found *********************************************************************/ extern uint32 xml_strlen (const xmlChar *str); /******************************************************************** * FUNCTION xml_strlen_sp * * get length and check if any whitespace at the same time * String len for xmlChar -- does not check for buffer overflow * Check for any whitespace in the string as well * * INPUTS: * str == buffer to check * sp == address of any-spaces-test output * * OUTPUTS: * *sp == TRUE if any whitespace found in the string * * RETURNS: * number of xmlChars before null terminator found *********************************************************************/ extern uint32 xml_strlen_sp (const xmlChar *str, boolean *sp); /******************************************************************** * FUNCTION xml_strcpy * * String copy for xmlChar -- does not check for buffer overflow * * Even if return value is zero, the EOS char is copied * * INPUTS: * copyTo == buffer to copy into * copyFrom == zero-terminated xmlChar string to copy from * RETURNS: * number of bytes copied to the result buffer, not including EOS *********************************************************************/ extern uint32 xml_strcpy (xmlChar *copyTo, const xmlChar *copyFrom); /******************************************************************** * FUNCTION xml_strncpy * * String copy for xmlChar -- checks for buffer overflow * INPUTS: * copyTo == buffer to copy into * copyFrom == zero-terminated xmlChar string to copy from * maxLen == max number of xmlChars to copy, but does include * the terminating zero that is added if this max is reached * RETURNS: * number of bytes copied to the result buffer *********************************************************************/ extern uint32 xml_strncpy (xmlChar *copyTo, const xmlChar *copyFrom, uint32 maxlen); /******************************************************************** * FUNCTION xml_strdup * * String duplicate for xmlChar * INPUTS: * copyFrom == zero-terminated xmlChar string to copy from * RETURNS: * pointer to new string or NULL if some error *********************************************************************/ extern xmlChar * xml_strdup (const xmlChar *copyFrom); /******************************************************************** * FUNCTION xml_strcat * * String concatenate for xmlChar * INPUTS: * appendTo == zero-terminated xmlChar string to append to * appendFrom == zero-terminated xmlChar string to append from * RETURNS: * appendTo if no error or NULL if some error *********************************************************************/ extern xmlChar * xml_strcat (xmlChar *appendTo, const xmlChar *appendFrom); /******************************************************************** * FUNCTION xml_strncat * * String concatenate for at most maxlen xmlChars * INPUTS: * appendTo == zero-terminated xmlChar string to append to * appendFrom == zero-terminated xmlChar string to append from * maxlen == max number of chars to append * RETURNS: * appendTo if no error or NULL if some error *********************************************************************/ extern xmlChar * xml_strncat (xmlChar *appendTo, const xmlChar *appendFrom, uint32 maxlen); /******************************************************************** * FUNCTION xml_strndup * * String duplicate for max N xmlChars * INPUTS: * copyFrom == zero-terminated xmlChar string to copy from * maxlen == max number of non-zero chars to copy * RETURNS: * pointer to new string or NULL if some error *********************************************************************/ extern xmlChar * xml_strndup (const xmlChar *copyFrom, uint32 maxlen); /******************************************************************** * FUNCTION xml_ch_strndup * * String duplicate for max N chars * INPUTS: * copyFrom == zero-terminated char string to copy from * maxlen == max number of non-zero chars to copy * RETURNS: * pointer to new string or NULL if some error *********************************************************************/ extern char * xml_ch_strndup (const char *copyFrom, uint32 maxlen); /******************************************************************** * FUNCTION xml_strcmp * * String compare for xmlChar * INPUTS: * s1 == zero-terminated xmlChar string to compare * s2 == zero-terminated xmlChar string to compare * * RETURNS: * == -1 : string 1 is less than string 2 * == 0 : strings are equal * == 1 : string 1 is greater than string 2 *********************************************************************/ extern int xml_strcmp (const xmlChar *s1, const xmlChar *s2); /******************************************************************** * FUNCTION xml_stricmp * * Case insensitive string compare for xmlChar * INPUTS: * s1 == zero-terminated xmlChar string to compare * s2 == zero-terminated xmlChar string to compare * * RETURNS: * == -1 : string 1 is less than string 2 * == 0 : strings are equal * == 1 : string 1 is greater than string 2 *********************************************************************/ extern int xml_stricmp (const xmlChar *s1, const xmlChar *s2); /******************************************************************** * FUNCTION xml_strncmp * * String compare for xmlChar for at most 'maxlen' xmlChars * INPUTS: * s1 == zero-terminated xmlChar string to compare * s2 == zero-terminated xmlChar string to compare * maxlen == max number of xmlChars to compare * * RETURNS: * == -1 : string 1 is less than string 2 * == 0 : strings are equal * == 1 : string 1 is greater than string 2 *********************************************************************/ extern int xml_strncmp (const xmlChar *s1, const xmlChar *s2, uint32 maxlen); /******************************************************************** * FUNCTION xml_strnicmp * * Case insensitive string compare for xmlChar for at * most 'maxlen' xmlChars * * INPUTS: * s1 == zero-terminated xmlChar string to compare * s2 == zero-terminated xmlChar string to compare * maxlen == max number of xmlChars to compare * * RETURNS: * == -1 : string 1 is less than string 2 * == 0 : strings are equal * == 1 : string 1 is greater than string 2 *********************************************************************/ extern int xml_strnicmp (const xmlChar *s1, const xmlChar *s2, uint32 maxlen); /******************************************************************** * FUNCTION xml_isspace * * Check if an xmlChar is a space char * INPUTS: * ch == xmlChar to check * RETURNS: * TRUE if a space, FALSE otherwise *********************************************************************/ extern boolean xml_isspace (uint32 ch); /******************************************************************** * FUNCTION xml_isspace_str * * Check if an xmlChar string is all whitespace chars * INPUTS: * str == xmlChar string to check * RETURNS: * TRUE if all whitespace, FALSE otherwise *********************************************************************/ extern boolean xml_isspace_str (const xmlChar *str); /******************************************************************** * FUNCTION xml_strcmp_nosp * * String compare for xmlChar for 2 strings, but ignoring * whitespace differences. All consecutive whitespace is * treated as one space char for comparison purposes * * Needed by yangdiff to compare description clauses which * have been reformated * * INPUTS: * s1 == zero-terminated xmlChar string to compare * s2 == zero-terminated xmlChar string to compare * * RETURNS: * == -1 : string 1 is less than string 2 * == 0 : strings are equal * == 1 : string 1 is greater than string 2 *********************************************************************/ extern int xml_strcmp_nosp (const xmlChar *s1, const xmlChar *s2); /******************************************************************** * FUNCTION xml_copy_clean_string * * Get a malloced string contained the converted string * from the input * Get rid of the leading and trailing whilespace * * Character entities have already been removed by the xmlTextReader * * INPUTS: * str == xmlChar string to check * RETURNS: * pointer to new malloced string *********************************************************************/ extern xmlChar * xml_copy_clean_string (const xmlChar *str); /******************************************************************** * FUNCTION xml_convert_char_entity * * Convert an XML character entity into a single xmlChar * * INPUTS: * str == string pointing to start of char entity * OUTPUTS: * *used == number of chars consumed * RETURNS: * converted xmlChar *********************************************************************/ extern xmlChar xml_convert_char_entity (const xmlChar *str, uint32 *used); #ifdef LIBXML2_ENABLED /******************************************************************** * FUNCTION xml_check_qname_content * * Check if the string node content is a likely QName * If so, then get the namespace URI for the prefix, * look it up in def_reg, and store the NSID in the node * * INPUTS: * reader == reader to use * node == current string node in progress * * OUTPUTS: * *********************************************************************/ extern void xml_check_qname_content (xmlTextReaderPtr reader, xml_node_t *node); /******************************************************************** * FUNCTION xml_get_namespace_id * * Get the namespace for the specified prefix (may be NULL) * Use the current XML reader context to resolve the prefix * * INPUTS: * reader == XML reader to use * prefix == prefix string to use (NULL == default namespace) * prefixlen == N if not a Z-terminated string * == 0 if it is a Z-terminated string * retnsid == address of return namespace ID * * OUTPUTS: * *retnsid == XMLNS ID for the namespace, 0 if none found * INVALID ID if unknown * RETURNS: * status *********************************************************************/ extern status_t xml_get_namespace_id (xmlTextReaderPtr reader, const xmlChar *prefix, uint32 prefixlen, xmlns_id_t *retnsid); /******************************************************************** * FUNCTION xml_consume_node * * parse function for YIN input * * INPUTS: * reader == xmlTextReader to use * node == address of node pointer to use * MUST be an initialized node * with xml_new_node or xml_init_node * nserr == TRUE if bad namespace should be checked * == FALSE if not * adv == TRUE if advance reader * == FALSE if no advance (reget current node) * * OUTPUTS: * *xmlnode == filled in or malloced and filled-in xml node * * RETURNS: * status of the operation *********************************************************************/ extern status_t xml_consume_node (xmlTextReaderPtr reader, xml_node_t *xmlnode, boolean nserr, boolean adv); /******************************************************************** * FUNCTION xml_skip_subtree * * Already encountered an error, so advance nodes until the * matching start-node is reached or a terminating error occurs * - end of input * - start depth level reached * * INPUTS: * reader == XmlReader already initialized from File, Memory, * or whatever * startnode == xml_node_t of the start node of the sub-tree to skip * RETURNS: * status of the operation * SIDE EFFECTS: * the xmlreader state is advanced until the current node is the * end node of the specified start node or a fatal error occurs *********************************************************************/ status_t xml_skip_subtree (xmlTextReaderPtr reader, const xml_node_t *startnode); #endif #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_xml_util */ yuma123_2.14/netconf/src/ncx/xpath1.c0000664000175000017500000113010014770023131017503 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2013 - 2017, Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: xpath1.c Xpath 1.0 search support ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 13nov08 abb begun ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include "procdefs.h" #include "def_reg.h" #include "dlq.h" #include "grp.h" #include "ncxconst.h" #include "ncx.h" #include "ncx_feature.h" #include "ncx_num.h" #include "obj.h" #include "val123.h" #include "tk.h" #include "typ.h" #include "xpath.h" #include "xpath1.h" #include "yangconst.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ /* #define XPATH1_PARSE_DEBUG 1 */ /* #define EXTRA_DEBUG 1 */ #define TEMP_BUFFSIZE 1024 /******************************************************************** * * * F O R W A R D D E C L A R A T I O N S * * * *********************************************************************/ static xpath_result_t* parse_expr( xpath_pcb_t *pcb, status_t *res); static xpath_result_t* boolean_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res ); static xpath_result_t* ceiling_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* concat_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* contains_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* count_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* current_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* false_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* floor_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* id_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* lang_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* last_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* local_name_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* namespace_uri_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* name_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* normalize_space_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* not_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* number_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* position_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* round_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* starts_with_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* string_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* string_length_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* substring_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* substring_after_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* substring_before_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* sum_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* translate_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* true_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* module_loaded_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* feature_enabled_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); /*YANG 1.1*/ static xpath_result_t* re_match_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* derived_from_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* derived_from_or_self_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* deref_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* enum_value_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); static xpath_result_t* bit_is_set_fn( xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res); /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ static xpath_fncb_t functions [] = { { XP_FN_BOOLEAN, XP_RT_BOOLEAN, 1, boolean_fn }, { XP_FN_CEILING, XP_RT_NUMBER, 1, ceiling_fn }, { XP_FN_CONCAT, XP_RT_STRING, -1, concat_fn }, { XP_FN_CONTAINS, XP_RT_BOOLEAN, 2, contains_fn }, { XP_FN_COUNT, XP_RT_NUMBER, 1, count_fn }, { XP_FN_CURRENT, XP_RT_NODESET, 0, current_fn }, { XP_FN_FALSE, XP_RT_BOOLEAN, 0, false_fn }, { XP_FN_FLOOR, XP_RT_NUMBER, 1, floor_fn }, { XP_FN_ID, XP_RT_NODESET, 1, id_fn }, { XP_FN_LANG, XP_RT_BOOLEAN, 1, lang_fn }, { XP_FN_LAST, XP_RT_NUMBER, 0, last_fn }, { XP_FN_LOCAL_NAME, XP_RT_STRING, -1, local_name_fn }, { XP_FN_NAME, XP_RT_STRING, -1, name_fn }, { XP_FN_NAMESPACE_URI, XP_RT_STRING, -1, namespace_uri_fn }, { XP_FN_NORMALIZE_SPACE, XP_RT_STRING, -1, normalize_space_fn }, { XP_FN_NOT, XP_RT_BOOLEAN, 1, not_fn }, { XP_FN_NUMBER, XP_RT_NUMBER, -1, number_fn }, { XP_FN_POSITION, XP_RT_NUMBER, 0, position_fn }, { XP_FN_ROUND, XP_RT_NUMBER, 1, round_fn }, { XP_FN_STARTS_WITH, XP_RT_BOOLEAN, 2, starts_with_fn }, { XP_FN_STRING, XP_RT_STRING, -1, string_fn }, { XP_FN_STRING_LENGTH, XP_RT_NUMBER, -1, string_length_fn }, { XP_FN_SUBSTRING, XP_RT_STRING, -1, substring_fn }, { XP_FN_SUBSTRING_AFTER, XP_RT_STRING, 2, substring_after_fn }, { XP_FN_SUBSTRING_BEFORE, XP_RT_STRING, 2, substring_before_fn }, { XP_FN_SUM, XP_RT_NUMBER, 1, sum_fn }, { XP_FN_TRANSLATE, XP_RT_STRING, 3, translate_fn }, { XP_FN_TRUE, XP_RT_BOOLEAN, 0, true_fn }, { XP_FN_MODULE_LOADED, XP_RT_BOOLEAN, -1, module_loaded_fn }, { XP_FN_FEATURE_ENABLED, XP_RT_BOOLEAN, 2, feature_enabled_fn }, { NULL, XP_RT_NONE, 0, NULL } /* last entry marker */ }; static xpath_fncb_t functions11 [] = { { XP_FN_RE_MATCH, XP_RT_BOOLEAN, 2, re_match_fn }, { XP_FN_DERIVED_FROM, XP_RT_BOOLEAN, 2, derived_from_fn }, { XP_FN_DERIVED_FROM_OR_SELF, XP_RT_BOOLEAN, 2, derived_from_or_self_fn }, { XP_FN_DEREF, XP_RT_NODESET, 1, deref_fn }, { XP_FN_ENUM_VALUE, XP_RT_NUMBER, 1, enum_value_fn }, { XP_FN_BIT_IS_SET, XP_RT_BOOLEAN, 2, bit_is_set_fn }, { NULL, XP_RT_NONE, 0, NULL } /* last entry marker */ }; /******************************************************************** * FUNCTION set_uint32_num * * Set an ncx_num_t with a uint32 number * * INPUTS: * innum == number value to use * outnum == address ofoutput number * * OUTPUTS: * *outnum is set with the converted value * *********************************************************************/ static void set_uint32_num (uint32 innum, ncx_num_t *outnum) { #ifdef HAS_FLOAT outnum->d = (double)innum; #else outnum->d = (int64)innum; #endif } /* set_uint32_num */ /******************************************************************** * FUNCTION malloc_failed_error * * Generate a malloc failed error if OK * * INPUTS: * pcb == parser control block to use *********************************************************************/ static void malloc_failed_error (xpath_pcb_t *pcb) { if (pcb->logerrors) { if (pcb->exprstr) { log_error("\nError: malloc failed in " "Xpath expression '%s'.", pcb->exprstr); } else { log_error("\nError: malloc failed in " "Xpath expression"); } ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, ERR_INTERNAL_MEM); } } /* malloc_failed_error */ /******************************************************************** * FUNCTION no_parent_warning * * Generate a no parent available error if OK * * INPUTS: * pcb == parser control block to use *********************************************************************/ static void no_parent_warning (xpath_pcb_t *pcb) { if (pcb->logerrors && ncx_warning_enabled(ERR_NCX_NO_XPATH_PARENT)) { log_warn("\nWarning: no parent found " "in XPath expr '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->objmod, ERR_NCX_NO_XPATH_PARENT); } else if (pcb->objmod != NULL) { ncx_inc_warnings(pcb->objmod); } } /* no_parent_warning */ /******************************************************************** * FUNCTION unexpected_error * * Generate an unexpected token error if OK * * INPUTS: * pcb == parser control block to use *********************************************************************/ static void unexpected_error (xpath_pcb_t *pcb) { if (pcb->logerrors) { if (TK_CUR(pcb->tkc)) { log_error("\nError: Unexpected token '%s' in " "XPath expression '%s'", tk_get_token_name(TK_CUR_TYP(pcb->tkc)), pcb->exprstr); } else { log_error("\nError: End reached in " "XPath expression '%s'", pcb->exprstr); } ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, ERR_NCX_WRONG_TKTYPE); } } /* unexpected_error */ /******************************************************************** * FUNCTION wrong_parmcnt_error * * Generate a wrong function parameter count error if OK * * INPUTS: * pcb == parser control block to use * parmcnt == the number of parameters received * res == error result to use *********************************************************************/ static void wrong_parmcnt_error (xpath_pcb_t *pcb, uint32 parmcnt, status_t res) { if (pcb->logerrors) { log_error("\nError: wrong function arg count '%u' in " "Xpath expression '%s'", parmcnt, pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, res); } } /* wrong_parmcnt_error */ /******************************************************************** * FUNCTION invalid_instanceid_error * * Generate a invalid instance identifier error * * INPUTS: * pcb == parser control block to use *********************************************************************/ static void invalid_instanceid_error (xpath_pcb_t *pcb) { if (pcb->logerrors) { log_error("\nError: XPath found in instance-identifier '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, ERR_NCX_INVALID_INSTANCEID); } } /* invalid_instanceid_error */ /******************************************************************** * FUNCTION invalid_identityid_error * * Generate a invalid identity identifier error * * INPUTS: * pcb == parser control block to use *********************************************************************/ static void invalid_identityid_error (xpath_pcb_t *pcb, const xmlChar* qname) { if (pcb->logerrors) { log_error("\nError: XPath expression '%s' with invalid identity param '%s'", pcb->exprstr, qname); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, /*ERR_NCX_INVALID_IDENTITYID*/ERR_NCX_INVALID_XPATH_EXPR); } } /* invalid_identityid_error */ /******************************************************************** * FUNCTION check_instanceid_expr * * Check the form of an instance-identifier expression * The operation has been checked already and the * expression is: * * leftval = rightval * * Make sure the CLRs in the YANG spec are followed * DOES NOT check the actual result value, just * tries to keep track of the expression syntax * * Use check_instanceid_result to finish the * instance-identifier tests * * INPUTS: * pcb == parser control block to use * leftval == LHS result * rightval == RHS result * * RETURNS: * status *********************************************************************/ static status_t check_instanceid_expr (xpath_pcb_t *pcb, xpath_result_t *leftval, xpath_result_t *rightval) { val_value_t *contextval; const char *msg; status_t res; boolean haserror; /* skip unless this is a real value tree eval */ if (!pcb->val || !pcb->val_docroot) { return NO_ERR; } haserror = FALSE; res = ERR_NCX_INVALID_INSTANCEID; msg = NULL; /* check LHS which is supposed to be a nodeset. * it can only be an empty nodeset if the * instance-identifier is constrained to * existing values by 'require-instance true' */ if (leftval) { if (leftval->restype != XP_RT_NODESET) { msg = "LHS has wrong data type"; haserror = TRUE; } else { /* check out the nodeset contents */ contextval = pcb->context.node.valptr; if (!contextval) { return SET_ERROR(ERR_INTERNAL_VAL); } if (contextval->obj->objtype == OBJ_TYP_LIST || contextval->obj->objtype == OBJ_TYP_LEAF_LIST) { /* the '.=result' must be the format, and be * the same as the context for a leaf-list * For a list, the name='fred' expr is used, * 'name' must be a key leaf * of the context node * check the actual result later */ ; } else { msg = "wrong context node type"; haserror = TRUE; } } } else { haserror = TRUE; msg = "missing LHS value in predicate"; } if (!haserror) { if (rightval) { if (!(rightval->restype == XP_RT_STRING || rightval->restype == XP_RT_NUMBER)) { msg = "RHS has wrong data type"; haserror = TRUE; } } else { haserror = TRUE; msg = "missing RHS value in predicate"; } } if (haserror) { if (pcb->logerrors) { log_error("\nError: %s in instance-identifier '%s'", msg, pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, res); } return res; } else { return NO_ERR; } } /* check_instanceid_expr */ /******************************************************************** * FUNCTION check_instance_result * * Check the results of a leafref or instance-identifier expression * * Make sure the require-instance condition is met * if it is set to 'true' * * INPUTS: * pcb == parser control block to use * resultl == complete instance-identifier result to check * * RETURNS: * status *********************************************************************/ static status_t check_instance_result (xpath_pcb_t *pcb, xpath_result_t *result) { val_value_t *contextval; const char *msg; status_t res; boolean haserror, constrained; uint32 nodecount; ncx_btype_t btyp; /* skip unless this is a real value tree eval */ if (!pcb->val || !pcb->val_docroot) { return NO_ERR; } haserror = FALSE; res = ERR_NCX_INVALID_INSTANCEID; msg = NULL; if (result->restype != XP_RT_NODESET) { msg = "wrong result type"; haserror = TRUE; } else { /* check the node count */ nodecount = dlq_count(&result->r.nodeQ); if (nodecount > 1) { if (pcb->val->btyp == NCX_BT_INSTANCE_ID) { msg = "too many instances"; haserror = TRUE; } } else { /* check out the nodeset contents */ contextval = pcb->val; if (!contextval) { return SET_ERROR(ERR_INTERNAL_VAL); } btyp = contextval->btyp; if (btyp == NCX_BT_INSTANCE_ID || btyp == NCX_BT_LEAFREF) { constrained = typ_get_constrained (obj_get_ctypdef(contextval->obj)); } else { constrained = TRUE; } if (constrained && !nodecount) { /* should have matched exactly one instance */ msg = "missing instance"; haserror = TRUE; res = ERR_NCX_MISSING_INSTANCE; } } } if (haserror) { if (pcb->logerrors) { if (pcb->val->btyp == NCX_BT_LEAFREF) { log_error("\nError: %s in leafref path '%s'", msg, pcb->exprstr); } else { log_error("\nError: %s in instance-identifier '%s'", msg, pcb->exprstr); } ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, res); } return res; } else { return NO_ERR; } } /* check_instance_result */ /******************************************************************** * FUNCTION new_result * * Get a new result from the cache or malloc if none available * * INPUTS: * pcb == parser control block to use * restype == desired result type * * RETURNS: * result from the cache or malloced; NULL if malloc fails *********************************************************************/ static xpath_result_t * new_result (xpath_pcb_t *pcb, xpath_restype_t restype) { xpath_result_t *result; result = (xpath_result_t *)dlq_deque(&pcb->result_cacheQ); if (result) { pcb->result_count--; xpath_init_result(result, restype); } else { result = xpath_new_result(restype); } if (!result) { malloc_failed_error(pcb); } return result; } /* new_result */ /******************************************************************** * FUNCTION free_result * * Free a result struct: put in cache or free if cache maxed out * * INPUTS: * pcb == parser control block to use * result == result struct to free * *********************************************************************/ static void free_result (xpath_pcb_t *pcb, xpath_result_t *result) { xpath_resnode_t *resnode; if (result->restype == XP_RT_NODESET) { while (!dlq_empty(&result->r.nodeQ) && pcb->resnode_count < XPATH_RESNODE_CACHE_MAX) { resnode = (xpath_resnode_t *)dlq_deque(&result->r.nodeQ); xpath_clean_resnode(resnode); dlq_enque(resnode, &pcb->resnode_cacheQ); pcb->resnode_count++; } } if (pcb->result_count < XPATH_RESULT_CACHE_MAX) { xpath_clean_result(result); dlq_enque(result, &pcb->result_cacheQ); pcb->result_count++; } else { xpath_free_result(result); } } /* free_result */ /******************************************************************** * FUNCTION new_obj_resnode * * Get a new result node from the cache or malloc if none available * * INPUTS: * pcb == parser control block to use * if pcb->val set then node.valptr will be used * else node.objptr will be used instead * position == position within the current search * dblslash == TRUE if // present on this result node * and has not been converted to separate * nodes for all descendants instead * == FALSE if '//' is not in effect for the * current step * objptr == object pointer value to use * * RETURNS: * result from the cache or malloced; NULL if malloc fails *********************************************************************/ static xpath_resnode_t * new_obj_resnode (xpath_pcb_t *pcb, int64 position, boolean dblslash, obj_template_t *objptr) { xpath_resnode_t *resnode; resnode = (xpath_resnode_t *)dlq_deque(&pcb->resnode_cacheQ); if (resnode) { pcb->resnode_count--; } else { resnode = xpath_new_resnode(); } if (!resnode) { malloc_failed_error(pcb); } else { resnode->position = position; resnode->dblslash = dblslash; resnode->node.objptr = objptr; } return resnode; } /* new_obj_resnode */ /******************************************************************** * FUNCTION new_val_resnode * * Get a new result node from the cache or malloc if none available * * INPUTS: * pcb == parser control block to use * if pcb->val set then node.valptr will be used * else node.objptr will be used instead * position == position within the search context * dblslash == TRUE if // present on this result node * and has not been converted to separate * nodes for all descendants instead * == FALSE if '//' is not in effect for the * current step * valptr == variable pointer value to use * * RETURNS: * result from the cache or malloced; NULL if malloc fails *********************************************************************/ static xpath_resnode_t * new_val_resnode (xpath_pcb_t *pcb, int64 position, boolean dblslash, val_value_t *valptr) { xpath_resnode_t *resnode; resnode = (xpath_resnode_t *)dlq_deque(&pcb->resnode_cacheQ); if (resnode) { pcb->resnode_count--; } else { resnode = xpath_new_resnode(); } if (!resnode) { malloc_failed_error(pcb); } else { resnode->position = position; resnode->dblslash = dblslash; resnode->node.valptr = valptr; } return resnode; } /* new_val_resnode */ /******************************************************************** * FUNCTION free_resnode * * Free a result node struct: put in cache or free if cache maxed out * * INPUTS: * pcb == parser control block to use * resnode == result node struct to free * *********************************************************************/ static void free_resnode (xpath_pcb_t *pcb, xpath_resnode_t *resnode) { if (pcb->resnode_count < XPATH_RESNODE_CACHE_MAX) { xpath_clean_resnode(resnode); dlq_enque(resnode, &pcb->resnode_cacheQ); pcb->resnode_count++; } else { xpath_free_resnode(resnode); } } /* free_resnode */ /******************************************************************** * FUNCTION new_nodeset * * Start a nodeset result with a specified object or value ptr * Only one of obj or val will really be stored, * depending on the current parsing mode * * INPUTS: * pcb == parser control block to use * obj == object ptr to store as the first resnode * val == value ptr to store as the first resnode * dblslash == TRUE if unprocessed dblslash in effect * FALSE if not * * RETURNS: * malloced data structure or NULL if out-of-memory error *********************************************************************/ static xpath_result_t * new_nodeset (xpath_pcb_t *pcb, obj_template_t *obj, val_value_t *val, int64 position, boolean dblslash) { xpath_result_t *result; xpath_resnode_t *resnode; result = new_result(pcb, XP_RT_NODESET); if (!result) { return NULL; } if (obj || val) { if (pcb->val) { resnode = new_val_resnode(pcb, position, dblslash, val); result->isval = TRUE; } else { resnode = new_obj_resnode(pcb, position, dblslash, obj); result->isval = FALSE; } if (!resnode) { xpath_free_result(result); return NULL; } dlq_enque(resnode, &result->r.nodeQ); } result->last = 1; return result; } /* new_nodeset */ /******************************************************************** * FUNCTION convert_compare_result * * Convert an int32 compare result to a boolean * based on the XPath relation op * * INPUTS: * cmpresult == compare result * exop == XPath relational or equality expression OP * * RETURNS: * TRUE if relation is TRUE * FALSE if relation is FALSE *********************************************************************/ static boolean convert_compare_result (int32 cmpresult, xpath_exop_t exop) { switch (exop) { case XP_EXOP_EQUAL: return (cmpresult) ? FALSE : TRUE; case XP_EXOP_NOTEQUAL: return (cmpresult) ? TRUE : FALSE; case XP_EXOP_LT: return (cmpresult < 0) ? TRUE : FALSE; case XP_EXOP_GT: return (cmpresult > 0) ? TRUE : FALSE; case XP_EXOP_LEQUAL: return (cmpresult <= 0) ? TRUE : FALSE; case XP_EXOP_GEQUAL: return (cmpresult >= 0) ? TRUE : FALSE; default: SET_ERROR(ERR_INTERNAL_VAL); return TRUE; } /*NOTREACHED*/ } /* convert_compare_result */ /******************************************************************** * FUNCTION compare_strings * * Compare str1 to str2 * Use the specified operation * * str1 str2 * * INPUTS: * str1 == left hand side string * str2 == right hand side string * exop == XPath expression op to use * * RETURNS: * TRUE if relation is TRUE * FALSE if relation is FALSE *********************************************************************/ static boolean compare_strings (const xmlChar *str1, const xmlChar *str2, xpath_exop_t exop) { int32 cmpresult; cmpresult = xml_strcmp(str1, str2); return convert_compare_result(cmpresult, exop); } /* compare_strings */ /******************************************************************** * FUNCTION compare_numbers * * Compare str1 to str2 * Use the specified operation * * num1 num2 * * INPUTS: * num1 == left hand side number * numstr2 == right hand side string to convert to num2 * and then compare to num1 * == NULL or empty string to compare to NAN * exop == XPath expression op to use * * RETURNS: * TRUE if relation is TRUE * FALSE if relation is FALSE *********************************************************************/ static boolean compare_numbers (const ncx_num_t *num1, const xmlChar *numstr2, xpath_exop_t exop) { int32 cmpresult; ncx_num_t num2; status_t res; ncx_numfmt_t numfmt; cmpresult = 0; numfmt = NCX_NF_DEC; res = NO_ERR; if (numstr2 && *numstr2) { numfmt = ncx_get_numfmt(numstr2); if (numfmt == NCX_NF_OCTAL) { numfmt = NCX_NF_DEC; } } if (numfmt == NCX_NF_DEC || numfmt == NCX_NF_REAL) { ncx_init_num(&num2); if (numstr2 && *numstr2) { res = ncx_convert_num(numstr2, numfmt, NCX_BT_FLOAT64, &num2); } else { ncx_set_num_nan(&num2, NCX_BT_FLOAT64); } if (res == NO_ERR) { cmpresult = ncx_compare_nums(num1, &num2, NCX_BT_FLOAT64); } ncx_clean_num(NCX_BT_FLOAT64, &num2); } else if (numfmt == NCX_NF_NONE) { res = ERR_NCX_INVALID_VALUE; } else { res = ERR_NCX_WRONG_NUMTYP; } if (res != NO_ERR) { return FALSE; } return convert_compare_result(cmpresult, exop); } /* compare_numbers */ /******************************************************************** * FUNCTION compare_booleans * * Compare bool1 to bool2 * Use the specified operation * * bool1 bool2 * * INPUTS: * num1 == left hand side number * str2 == right hand side string to convert to a number * and then compare to num1 * exop == XPath expression op to use * * RETURNS: * TRUE if relation is TRUE * FALSE if relation is FALSE *********************************************************************/ static boolean compare_booleans (boolean bool1, boolean bool2, xpath_exop_t exop) { int32 cmpresult; if ((bool1 && bool2) || (!bool1 && !bool2)) { cmpresult = 0; } else if (bool1) { cmpresult = 1; } else { cmpresult = -1; } return convert_compare_result(cmpresult, exop); } /* compare_booleans */ /******************************************************************** * FUNCTION val2buf * * Helper function allocating new parms->buffer or allocating larger * and freeing the existing one. * * INPUTS: * val == value to be stringified * parms == ptr to xpath_compwalkerparms_t with buffsize and buffer fields * * RETURNS: * NO_ERR in case operation is completed * or error code if operation failed to complete *********************************************************************/ static status_t val2buf(val_value_t* val, xpath_compwalkerparms_t *parms) { uint32 cnt; status_t res; /* get value sprintf size */ res = val_sprintf_simval_nc(NULL, val, &cnt); if (res != NO_ERR) { return res; } if ((cnt+1) > parms->buffsize) { if(parms->buffer) { m__free(parms->buffer); } parms->buffsize=max(cnt+1,TEMP_BUFFSIZE); parms->buffer = m__getMem(parms->buffsize); if (!parms->buffer) { return ERR_INTERNAL_MEM; } } res = val_sprintf_simval_nc(parms->buffer, val, &cnt); return res; } /******************************************************************** * FUNCTION val2cmpstring * * Helper function allocating new parms->cmpstring buffer and * serializing the value there * * INPUTS: * val == value to be stringified * parms == ptr to xpath_compwalkerparms_t with cmpstring field * * RETURNS: * NO_ERR in case operation is completed * or error code if operation failed to complete *********************************************************************/ static status_t val2cmpstring(val_value_t* val, xpath_compwalkerparms_t *parms) { uint32 cnt; status_t res; /* get value sprintf size */ res = val_sprintf_simval_nc(NULL, val, &cnt); if (res != NO_ERR) { return res; } parms->cmpstring = m__getMem(cnt+1); if (!parms->cmpstring) { return ERR_INTERNAL_MEM; } res = val_sprintf_simval_nc(parms->cmpstring, val, &cnt); return res; } /******************************************************************** * FUNCTION val2cmpnum * * Helper function allocating new parms->cmpnum buffer and * setting it to the number type stored in val casted to NCX_BT_FLOAT64 * * INPUTS: * val == value of number type * parms == ptr to xpath_compwalkerparms_t with cmpnum field * * RETURNS: * NO_ERR in case operation is completed * or error code if operation failed to complete *********************************************************************/ static status_t val2cmpnum(val_value_t* val, xpath_compwalkerparms_t *parms) { status_t res; parms->cmpnum = m__getMem(sizeof(ncx_num_t)); if (!parms->cmpnum) { return ERR_INTERNAL_MEM; } ncx_init_num(parms->cmpnum); res = ncx_cast_num(&val->v.num,val->btyp,parms->cmpnum,NCX_BT_FLOAT64); return res; } /******************************************************************** * FUNCTION compare_walker_fn * * Compare the parm value to the current value * Stop the walk on the first TRUE comparison * * val1 val2 * * val1 == parms.val and for optimization cached parms.cmpstring or parms.cmpnum * val2 == value of node passed by val walker * op == parms.exop * * Matches val_walker_fn_t template in val.h * * INPUTS: * val == value node found in the search, used as 'val2' * cookie1 == xpath_pcb_t * : parser control block to use * currently not used!!! * cookie2 == xpath_compwalkerparms_t *: walker parms to use * cmpstring or cmpnum is used, not result2 * OUTPUTS: * *cookie2 contents adjusted (parms.cmpresult and parms.res) * * RETURNS: * TRUE to keep walk going * FALSE to terminate walk *********************************************************************/ static boolean compare_walker_fn (val_value_t *val, void *cookie1, void *cookie2) { val_value_t *useval, *v_val; xpath_compwalkerparms_t *parms; xmlChar *buffer; status_t res; (void)cookie1; parms = (xpath_compwalkerparms_t *)cookie2; /* skip all complex nodes */ if (!typ_is_simple(val->btyp)) { return TRUE; } v_val = NULL; res = NO_ERR; if (val_is_virtual(val)) { v_val = val_get_virtual_value(NULL, val, &res); if (v_val == NULL) { parms->res = res; parms->cmpresult = FALSE; return FALSE; } else { useval = v_val; } } else { useval = val; } if (obj_is_password(val->obj)) { parms->cmpresult = FALSE; } else if (typ_is_string(val->btyp) && typ_is_string(parms->cmpval->btyp)) { parms->cmpresult = compare_strings(VAL_STR(parms->cmpval),VAL_STR(useval),parms->exop); } else if (typ_is_number(val->btyp) && typ_is_number(parms->cmpval->btyp)) { if(val->btyp==parms->cmpval->btyp) { parms->cmpresult = convert_compare_result(ncx_compare_nums(&parms->cmpval->v.num,&useval->v.num,val->btyp),parms->exop); } else { if(parms->cmpnum) { parms->res=val2cmpnum(useval,parms); } if(parms->res == NO_ERR) { parms->res = val2buf(useval,parms); if (parms->res == NO_ERR) { parms->cmpresult = compare_numbers(parms->cmpnum, parms->buffer, parms->exop); } } } } else { parms->res=val2buf(useval,parms); if (parms->res == NO_ERR) { if (!parms->cmpstring) { parms->res=val2cmpstring(useval,parms); } if(parms->res == NO_ERR) { parms->cmpresult = compare_strings(parms->cmpstring, parms->buffer, parms->exop); } } } return !parms->cmpresult; } /* compare_walker_fn */ /******************************************************************** * FUNCTION top_compare_walker_fn * * Compare the current value in the 1st nodeset * to the entire 2nd node-set * * This callback should get called once for every * node in the first parmset. Each time a simple * type is passed into the callback, the entire * result2 is processed with new callbacks to * compare the value against each simple * type in the 2nd node-set. * * Stop the walk on the first TRUE comparison * * Matches val_walker_fn_t template in val.h * * INPUTS: * val == value node found in the search * cookie1 == xpath_pcb_t * : parser control block to use * currently not used!!! * cookie2 == xpath_compwalkerparms_t *: walker parms to use * result2 is used, not cmpstrings * OUTPUTS: * *cookie2 contents adjusted (parms.cmpresult and parms.res) * * RETURNS: * TRUE to keep walk going * FALSE to terminate walk *********************************************************************/ static boolean top_compare_walker_fn (val_value_t *val, void *cookie1, void *cookie2) { xpath_compwalkerparms_t *parms, newparms; xpath_pcb_t *pcb; xpath_resnode_t *resnode; val_value_t *testval, *useval, *newval; status_t res; uint32 cnt; boolean fnresult, cfgonly, ret; pcb = (xpath_pcb_t *)cookie1; parms = (xpath_compwalkerparms_t *)cookie2; /* skip all complex nodes */ if (!typ_is_simple(val->btyp)) { return TRUE; } res = NO_ERR; newval = NULL; cfgonly = (pcb->flags & XP_FL_CONFIGONLY) ? TRUE : FALSE; if (val_is_virtual(val)) { newval = val_get_virtual_value(NULL, val, &res); if (newval == NULL) { parms->res = res; parms->cmpresult = FALSE; return FALSE; } else { useval = newval; } } else { useval = val; } /* setup 2nd walker parms */ memset(&newparms, 0x0, sizeof(xpath_compwalkerparms_t)); newparms.cmpval = useval; newparms.exop = parms->exop; newparms.cmpresult = FALSE; newparms.res = NO_ERR; ret=TRUE; /* go through all the nodes in the first node-set * and compare each leaf against all the leafs * in the other node-set; stop when condition * is met or both node-sets completely searched */ for (resnode = (xpath_resnode_t *) dlq_firstEntry(&parms->result2->r.nodeQ); resnode != NULL && res == NO_ERR; resnode = (xpath_resnode_t *)dlq_nextEntry(resnode)) { testval = resnode->node.valptr; fnresult = val_find_all_descendants(compare_walker_fn, pcb, &newparms, testval, NULL, NULL, cfgonly, FALSE, TRUE, TRUE); if (newparms.res != NO_ERR) { res = newparms.res; parms->res = res; } else if (!fnresult) { /* condition was met if return FALSE and * walkerparms.res == NO_ERR */ parms->res = NO_ERR; ret=FALSE; } } if (newparms.buffer != NULL) { m__free(newparms.buffer); } if (newparms.cmpstring != NULL) { m__free(newparms.cmpstring); } if (newparms.cmpnum != NULL) { m__free(newparms.cmpnum); } return ret; } /* top_compare_walker_fn */ /******************************************************************** * FUNCTION compare_nodeset_to_other * * Compare 2 results, 1 of them is a node-set * * INPUTS: * pcb == parser control block to use * val1 == first result struct to compare * val2 == second result struct to compare * exop == XPath expression operator to use * res == address of resturn status * * OUTPUTS: * *res == return status * * RETURNS: * TRUE if relation is TRUE * FALSE if relation is FALSE or some error *********************************************************************/ static boolean compare_nodeset_to_other (xpath_pcb_t *pcb, xpath_result_t *val1, xpath_result_t *val2, xpath_exop_t exop, status_t *res) { xpath_resnode_t *resnode; xpath_result_t *tempval; val_value_t *testval, *newval; xmlChar *cmpstring; ncx_num_t cmpnum; int32 cmpresult; boolean bool1, bool2, fnresult; status_t myres; *res = NO_ERR; myres = NO_ERR; /* only compare real results, not objects */ if (!pcb->val) { return TRUE; } if (val2->restype == XP_RT_NODESET) { /* invert the exop; use val2 as val1 */ switch (exop) { case XP_EXOP_EQUAL: case XP_EXOP_NOTEQUAL: break; case XP_EXOP_LT: exop = XP_EXOP_GT; break; case XP_EXOP_GT: exop = XP_EXOP_LT; break; case XP_EXOP_LEQUAL: exop = XP_EXOP_GEQUAL; break; case XP_EXOP_GEQUAL: exop = XP_EXOP_LEQUAL; break; default: *res = SET_ERROR(ERR_INTERNAL_VAL); return TRUE; } /* swap the parameters so the parmset is on the LHS */ tempval = val1; val1 = val2; val2 = tempval; } if (dlq_empty(&val1->r.nodeQ)) { return FALSE; } if (val2->restype == XP_RT_BOOLEAN) { bool1 = xpath_cvt_boolean(val1); bool2 = val2->r.boo; return compare_booleans(bool1, bool2, exop); } /* compare the LHS node-set to the cmpstring or cmpnum * first match will end the loop */ fnresult = FALSE; for (resnode = (xpath_resnode_t *)dlq_firstEntry(&val1->r.nodeQ); resnode != NULL && !fnresult && *res == NO_ERR; resnode = (xpath_resnode_t *)dlq_nextEntry(resnode)) { testval = resnode->node.valptr; newval = NULL; myres = NO_ERR; if (val_is_virtual(testval)) { newval = val_get_virtual_value(NULL, testval, &myres); if (newval == NULL) { *res = myres; continue; } else { testval = newval; } } switch (val2->restype) { case XP_RT_STRING: cmpstring = NULL; *res = xpath1_stringify_node(pcb, testval, &cmpstring); if (*res == NO_ERR) { fnresult = compare_strings(cmpstring, val2->r.str, exop); } if (cmpstring) { m__free(cmpstring); } break; case XP_RT_NUMBER: ncx_init_num(&cmpnum); if (typ_is_number(testval->btyp)) { myres = ncx_cast_num(&testval->v.num, testval->btyp, &cmpnum, NCX_BT_FLOAT64); if (myres != NO_ERR) { ncx_set_num_nan(&cmpnum, NCX_BT_FLOAT64); } } else if (testval->btyp == NCX_BT_STRING) { myres = ncx_convert_num(VAL_STR(testval), NCX_NF_NONE, NCX_BT_FLOAT64, &cmpnum); if (myres != NO_ERR) { ncx_set_num_nan(&cmpnum, NCX_BT_FLOAT64); } } else { ncx_set_num_nan(&cmpnum, NCX_BT_FLOAT64); } cmpresult = ncx_compare_nums(&cmpnum, &val2->r.num, NCX_BT_FLOAT64); fnresult = convert_compare_result(cmpresult, exop); ncx_clean_num(NCX_BT_FLOAT64, &cmpnum); break; default: SET_ERROR(ERR_INTERNAL_VAL); } } return fnresult; } /* compare_nodeset_to_other */ /******************************************************************** * FUNCTION compare_nodesets * * Compare 2 nodeset results * * INPUTS: * pcb == parser control block to use * val1 == first result struct to compare * val2 == second result struct to compare * exop == XPath expression op to use * res == address of resturn status * * OUTPUTS: * *res == return status * * RETURNS: * TRUE if relation is TRUE FALSE if relation is FALSE or some error (check *res) *********************************************************************/ static boolean compare_nodesets (xpath_pcb_t *pcb, xpath_result_t *val1, xpath_result_t *val2, xpath_exop_t exop, status_t *res) { xpath_resnode_t *resnode; val_value_t *testval; boolean fnresult, cfgonly; xpath_compwalkerparms_t walkerparms; *res = NO_ERR; if (!pcb->val) { return FALSE; } if ((val1->restype != val2->restype) || (val1->restype != XP_RT_NODESET)) { *res = SET_ERROR(ERR_INTERNAL_VAL); return FALSE; } /* make sure both node sets are non-empty */ if (dlq_empty(&val1->r.nodeQ) || dlq_empty(&val2->r.nodeQ)) { /* cannot be a matching node in both node-sets * if 1 or both node-sets are empty */ return FALSE; } /* both node-sets have at least 1 node */ cfgonly = (pcb->flags & XP_FL_CONFIGONLY) ? TRUE : FALSE; walkerparms.result2 = val2; walkerparms.cmpstring = NULL; walkerparms.cmpnum = NULL; walkerparms.buffer = m__getMem(TEMP_BUFFSIZE); if (!walkerparms.buffer) { *res = ERR_INTERNAL_MEM; return FALSE; } walkerparms.buffsize = TEMP_BUFFSIZE; walkerparms.exop = exop; walkerparms.cmpresult = FALSE; walkerparms.res = NO_ERR; /* go through all the nodes in the first node-set * and compare each leaf against all the leafs * in the other node-set; stop when condition * is met or both node-sets completely searched */ for (resnode = (xpath_resnode_t *)dlq_firstEntry(&val1->r.nodeQ); resnode != NULL && *res == NO_ERR; resnode = (xpath_resnode_t *)dlq_nextEntry(resnode)) { testval = resnode->node.valptr; fnresult = val_find_all_descendants(top_compare_walker_fn, pcb, &walkerparms, testval, NULL, NULL, cfgonly, FALSE, TRUE, TRUE); if (walkerparms.res != NO_ERR) { *res = walkerparms.res; } else if (!fnresult) { /* condition was met if return FALSE and * walkerparms.res == NO_ERR */ m__free(walkerparms.buffer); return TRUE; } } m__free(walkerparms.buffer); return FALSE; } /* compare_nodesets */ /******************************************************************** * FUNCTION compare_results * * Compare 2 results, using the specified logic operator * * INPUTS: * pcb == parser control block to use * val1 == first result struct to compare * val2 == second result struct to compare * exop == XPath exression operator to use * res == address of resturn status * * OUTPUTS: * *res == return status * * RETURNS: * relation result (TRUE or FALSE) *********************************************************************/ static boolean compare_results (xpath_pcb_t *pcb, xpath_result_t *val1, xpath_result_t *val2, xpath_exop_t exop, status_t *res) { xmlChar *str1, *str2; ncx_num_t num1, num2; boolean retval, bool1, bool2; int32 cmpval; *res = NO_ERR; /* only compare real results, not objects */ if (!pcb->val) { return TRUE; } cmpval = 0; /* compare directly if the vals are the same result type */ if (val1->restype == val2->restype) { switch (val1->restype) { case XP_RT_NODESET: return compare_nodesets(pcb, val1, val2, exop, res); case XP_RT_NUMBER: cmpval = ncx_compare_nums(&val1->r.num, &val2->r.num, NCX_BT_FLOAT64); return convert_compare_result(cmpval, exop); case XP_RT_STRING: return compare_strings(val1->r.str, val2->r.str, exop); case XP_RT_BOOLEAN: return compare_booleans(val1->r.boo, val2->r.boo, exop); break; default: *res = SET_ERROR(ERR_INTERNAL_VAL); return TRUE; } /*NOTREACHED*/ } /* if 1 nodeset is involved, then compare each node * to the other value until the relation is TRUE */ if (val1->restype == XP_RT_NODESET || val2->restype == XP_RT_NODESET) { return compare_nodeset_to_other(pcb, val1, val2, exop, res); } /* no nodesets involved, so the specific exop matters */ if (exop == XP_EXOP_EQUAL || exop == XP_EXOP_NOTEQUAL) { /* no nodesets involved * not the same result types, so figure out the * correct comparision to make. Priority defined * by XPath is * * 1) boolean * 2) number * 3) string */ if (val1->restype == XP_RT_BOOLEAN) { bool2 = xpath_cvt_boolean(val2); return compare_booleans(val1->r.boo, bool2, exop); } else if (val2->restype == XP_RT_BOOLEAN) { bool1 = xpath_cvt_boolean(val1); return compare_booleans(bool1, val2->r.boo, exop); } else if (val1->restype == XP_RT_NUMBER) { ncx_init_num(&num2); xpath_cvt_number(val2, &num2); cmpval = ncx_compare_nums(&val1->r.num, &num2, NCX_BT_FLOAT64); ncx_clean_num(NCX_BT_FLOAT64, &num2); return convert_compare_result(cmpval, exop); } else if (val2->restype == XP_RT_NUMBER) { ncx_init_num(&num1); xpath_cvt_number(val1, &num1); cmpval = ncx_compare_nums(&num1, &val2->r.num, NCX_BT_FLOAT64); ncx_clean_num(NCX_BT_FLOAT64, &num1); return convert_compare_result(cmpval, exop); } else if (val1->restype == XP_RT_STRING) { str2 = NULL; *res = xpath_cvt_string(pcb, val2, &str2); if (*res == NO_ERR) { cmpval = xml_strcmp(val1->r.str, str2); } if (str2) { m__free(str2); } if (*res == NO_ERR) { return convert_compare_result(cmpval, exop); } else { return TRUE; } } else if (val2->restype == XP_RT_STRING) { str1 = NULL; *res = xpath_cvt_string(pcb, val1, &str1); if (*res == NO_ERR) { cmpval = xml_strcmp(str1, val2->r.str); } if (str1) { m__free(str1); } if (*res == NO_ERR) { return convert_compare_result(cmpval, exop); } else { return TRUE; } } else { *res = ERR_NCX_INVALID_VALUE; return TRUE; } } /* no nodesets involved * expression op is a relational operator * not the same result types, so convert to numbers and compare */ ncx_init_num(&num1); ncx_init_num(&num2); xpath_cvt_number(val1, &num1); xpath_cvt_number(val2, &num2); cmpval = ncx_compare_nums(&num1,&num2, NCX_BT_FLOAT64); retval = convert_compare_result(cmpval, exop); ncx_clean_num(NCX_BT_FLOAT64, &num1); ncx_clean_num(NCX_BT_FLOAT64, &num2); return retval; } /* compare_results */ /******************************************************************** * FUNCTION dump_result * * Generate log output displaying the contents of a result * * INPUTS: * pcb == parser control block to use * result == result to dump * banner == optional first banner string to use *********************************************************************/ static void dump_result (xpath_pcb_t *pcb, xpath_result_t *result, const char *banner) { xpath_resnode_t *resnode; val_value_t *val; const obj_template_t *obj; if (banner) { log_write("\n%s", banner); } if (pcb->tkerr.mod) { log_write(" mod:%s, line:%u", ncx_get_modname(pcb->tkerr.mod), pcb->tkerr.linenum); } log_write("\nxpath result for '%s'", pcb->exprstr); switch (result->restype) { case XP_RT_NONE: log_write("\n typ: none"); break; case XP_RT_NODESET: log_write("\n typ: nodeset = "); for (resnode = (xpath_resnode_t *) dlq_firstEntry(&result->r.nodeQ); resnode != NULL; resnode = (xpath_resnode_t *)dlq_nextEntry(resnode)) { log_write("\n node "); if (result->isval) { val = resnode->node.valptr; if (val) { log_write("%s:%s [L:%u]", xmlns_get_ns_prefix(val->nsid), val->name, val_get_nest_level(val)); } else { log_write("NULL"); } } else { obj = resnode->node.objptr; if (obj) { log_write("%s:%s [L:%u]", obj_get_mod_prefix(obj), obj_get_name(obj), obj_get_level(obj)); } else { log_write("NULL"); } } } break; case XP_RT_NUMBER: if (result->isval) { log_write("\n typ: number = "); ncx_printf_num(&result->r.num, NCX_BT_FLOAT64); } else { log_write("\n typ: number"); } break; case XP_RT_STRING: if (result->isval) { log_write("\n typ: string = %s", result->r.str); } else { log_write("\n typ: string", result->r.str); } break; case XP_RT_BOOLEAN: if (result->isval) { log_write("\n typ: boolean = %s", (result->r.boo) ? "true" : "false"); } else { log_write("\n typ: boolean"); } break; default: log_write("\n typ: INVALID (%d)", result->restype); SET_ERROR(ERR_INTERNAL_VAL); } log_write("\n"); } /* dump_result */ /******************************************************************** * FUNCTION get_axis_id * * Check a string token tfor a match of an AxisName * * INPUTS: * name == name string to match * * RETURNS: * enum of axis name or XP_AX_NONE (0) if not an axis name *********************************************************************/ static ncx_xpath_axis_t get_axis_id (const xmlChar *name) { if (!name || !*name) { return XP_AX_NONE; } if (!xml_strcmp(name, XP_AXIS_ANCESTOR)) { return XP_AX_ANCESTOR; } if (!xml_strcmp(name, XP_AXIS_ANCESTOR_OR_SELF)) { return XP_AX_ANCESTOR_OR_SELF; } if (!xml_strcmp(name, XP_AXIS_ATTRIBUTE)) { return XP_AX_ATTRIBUTE; } if (!xml_strcmp(name, XP_AXIS_CHILD)) { return XP_AX_CHILD; } if (!xml_strcmp(name, XP_AXIS_DESCENDANT)) { return XP_AX_DESCENDANT; } if (!xml_strcmp(name, XP_AXIS_DESCENDANT_OR_SELF)) { return XP_AX_DESCENDANT_OR_SELF; } if (!xml_strcmp(name, XP_AXIS_FOLLOWING)) { return XP_AX_FOLLOWING; } if (!xml_strcmp(name, XP_AXIS_FOLLOWING_SIBLING)) { return XP_AX_FOLLOWING_SIBLING; } if (!xml_strcmp(name, XP_AXIS_NAMESPACE)) { return XP_AX_NAMESPACE; } if (!xml_strcmp(name, XP_AXIS_PARENT)) { return XP_AX_PARENT; } if (!xml_strcmp(name, XP_AXIS_PRECEDING)) { return XP_AX_PRECEDING; } if (!xml_strcmp(name, XP_AXIS_PRECEDING_SIBLING)) { return XP_AX_PRECEDING_SIBLING; } if (!xml_strcmp(name, XP_AXIS_SELF)) { return XP_AX_SELF; } return XP_AX_NONE; } /* get_axis_id */ /******************************************************************** * FUNCTION get_nodetype_id * * Check a string token tfor a match of a NodeType * * INPUTS: * name == name string to match * * RETURNS: * enum of node type or XP_EXNT_NONE (0) if not a node type name *********************************************************************/ static xpath_nodetype_t get_nodetype_id (const xmlChar *name) { if (!name || !*name) { return XP_EXNT_NONE; } if (!xml_strcmp(name, XP_NT_COMMENT)) { return XP_EXNT_COMMENT; } if (!xml_strcmp(name, XP_NT_TEXT)) { return XP_EXNT_TEXT; } if (!xml_strcmp(name, XP_NT_PROCESSING_INSTRUCTION)) { return XP_EXNT_PROC_INST; } if (!xml_strcmp(name, XP_NT_NODE)) { return XP_EXNT_NODE; } return XP_EXNT_NONE; } /* get_nodetype_id */ /******************************************************************** * FUNCTION location_path_end * * Check if the current location path is ending * by checking if the next token is going to start some * sub-expression (or end the expression) * * INPUTS: * pcb == parser control block to check * * RETURNS: * TRUE if the location path is ended * FALSE if the next token is part of the continuing * location path *********************************************************************/ static boolean location_path_end (xpath_pcb_t *pcb) { tk_type_t nexttyp; /* check corner-case path '/' */ nexttyp = tk_next_typ(pcb->tkc); if (nexttyp == TK_TT_NONE || nexttyp == TK_TT_EQUAL || nexttyp == TK_TT_BAR || nexttyp == TK_TT_PLUS || nexttyp == TK_TT_MINUS || nexttyp == TK_TT_LT || nexttyp == TK_TT_GT || nexttyp == TK_TT_NOTEQUAL || nexttyp == TK_TT_LEQUAL || nexttyp == TK_TT_GEQUAL) { return TRUE; } else { return FALSE; } } /* location_path_end */ /******************************************************************** * FUNCTION stringify_walker_fn * * Stringify the current node in a nodeset * * Matches val_walker_fn_t template in val.h * * INPUTS: * val == value node found to get the string value for * cookie1 == xpath_pcb_t * : parser control block to use * currently not used!!! * cookie2 == xpath_stringwalkerparms_t *: walker parms to use * buffer is filled unless NULL, then len is * calculated * OUTPUTS: * *cookie2 contents adjusted * * RETURNS: * TRUE to keep walk going * FALSE to terminate walk *********************************************************************/ static boolean stringify_walker_fn (val_value_t *val, void *cookie1, void *cookie2) { xpath_stringwalkerparms_t *parms; val_value_t *newval, *useval; status_t res; uint32 cnt; (void)cookie1; parms = (xpath_stringwalkerparms_t *)cookie2; /* skip all complex nodes */ if (!typ_is_simple(val->btyp)) { if (parms->buffer) { if (parms->buffpos+1 < parms->buffsize) { parms->buffer[parms->buffpos++] = '\n'; parms->buffer[parms->buffpos] = '\0'; } else { parms->res = ERR_BUFF_OVFL; return FALSE; } } else { parms->buffpos++; } return TRUE; } newval = NULL; res = NO_ERR; if (val_is_virtual(val)) { newval = val_get_virtual_value(NULL, val, &res); if (newval == NULL) { parms->res = res; return FALSE; } else { useval = newval; } } else { useval = val; } if (typ_is_string(useval->btyp)) { cnt = xml_strlen(VAL_STR(useval)); if (parms->buffer) { if (parms->buffpos+cnt+2 < parms->buffsize) { parms->buffer[parms->buffpos++] = '\n'; xml_strcpy(&parms->buffer[parms->buffpos], VAL_STR(useval)); } else { parms->res = ERR_BUFF_OVFL; return FALSE; } parms->buffpos += cnt; } else { parms->buffpos += (cnt+1); } } else { /* get value sprintf size */ res = val_sprintf_simval_nc(NULL, useval, &cnt); if (res != NO_ERR) { parms->res = res; return FALSE; } if (parms->buffer) { if (parms->buffpos+cnt+2 < parms->buffsize) { parms->buffer[parms->buffpos++] = '\n'; res = val_sprintf_simval_nc (&parms->buffer[parms->buffpos], useval, &cnt); if (res != NO_ERR) { parms->res = res; return FALSE; } } else { parms->res = ERR_BUFF_OVFL; return FALSE; } parms->buffpos += cnt; } else { parms->buffpos += (cnt+1); } } return TRUE; } /* stringify_walker_fn */ /********** X P A T H F U N C T I O N S ************/ /******************************************************************** * FUNCTION boolean_fn * * boolean boolean(object) function [4.3] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 object to convert to boolean * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * boolean_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result; if (!pcb->val && !pcb->obj) { return NULL; } parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (!parm) { /* should already be reported in obj mode */ *res = ERR_NCX_MISSING_PARM; return NULL; } result = new_result(pcb, XP_RT_BOOLEAN); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } result->r.boo = xpath_cvt_boolean(parm); *res = NO_ERR; return result; } /* boolean_fn */ /******************************************************************** * FUNCTION ceiling_fn * * number ceiling(number) function [4.4] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 number to convert to ceiling(number) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * ceiling_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result; ncx_num_t num; if (!pcb->val && !pcb->obj) { return NULL; } parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (!parm) { *res = ERR_NCX_MISSING_PARM; return NULL; } result = new_result(pcb, XP_RT_NUMBER); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } if (parm->restype == XP_RT_NUMBER) { *res = ncx_num_ceiling(&parm->r.num, &result->r.num, NCX_BT_FLOAT64); } else { ncx_init_num(&num); xpath_cvt_number(parm, &num); *res = ncx_num_ceiling(&num, &result->r.num, NCX_BT_FLOAT64); ncx_clean_num(NCX_BT_FLOAT64, &num); } if (*res != NO_ERR) { free_result(pcb, result); result = NULL; } return result; } /* ceiling_fn */ /******************************************************************** * FUNCTION concat_fn * * string concat(string, string, string*) function [4.2] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 2 or more strings to concatenate * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * concat_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result; xmlChar *str, *returnstr; uint32 parmcnt, len; //1 if (!pcb->val && !pcb->obj) { return NULL; } *res = NO_ERR; len = 0; /* check at least 2 strings to concat */ parmcnt = dlq_count(parmQ); if (parmcnt < 2) { *res = ERR_NCX_MISSING_PARM; wrong_parmcnt_error(pcb, parmcnt, *res); return NULL; } /* check all parms are strings and get total len */ for (parm = (xpath_result_t *)dlq_firstEntry(parmQ); parm != NULL && *res == NO_ERR; parm = (xpath_result_t *)dlq_nextEntry(parm)) { if (parm->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm, &str); if (*res == NO_ERR) { len += xml_strlen(str); m__free(str); } } else if (parm->r.str) { len += xml_strlen(parm->r.str); } } if (*res != NO_ERR) { return NULL; } result = new_result(pcb, XP_RT_STRING); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } result->r.str = m__getMem(len+1); if (!result->r.str) { free_result(pcb, result); *res = ERR_INTERNAL_MEM; return NULL; } returnstr = result->r.str; for (parm = (xpath_result_t *)dlq_firstEntry(parmQ); parm != NULL && *res == NO_ERR; parm = (xpath_result_t *)dlq_nextEntry(parm)) { if (parm->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm, &str); if (*res == NO_ERR) { returnstr += xml_strcpy(returnstr, str); m__free(str); } } else if (parm->r.str) { returnstr += xml_strcpy(returnstr, parm->r.str); } } if (*res != NO_ERR) { xpath_free_result(result); result = NULL; } return result; } /* concat_fn */ /******************************************************************** * FUNCTION contains_fn * * boolean contains(string, string) function [4.2] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 2 strings * returns true if the 1st string contains the 2nd string * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * contains_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm1, *parm2, *result; xmlChar *str1, *str2; boolean malloc1, malloc2; if (!pcb->val && !pcb->obj) { return NULL; } *res = NO_ERR; str1 = NULL; str2 = NULL; malloc1 = FALSE; malloc2 = FALSE; parm1 = (xpath_result_t *)dlq_firstEntry(parmQ); parm2 = (xpath_result_t *)dlq_nextEntry(parm1); if (parm1->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm1, &str1); malloc1 = TRUE; } else { str1 = parm1->r.str; } if (*res != NO_ERR) { return NULL; } if (parm2->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm2, &str2); malloc2 = TRUE; } else { str2 = parm2->r.str; } if (*res != NO_ERR) { if (malloc1) { m__free(str1); } return NULL; } result = new_result(pcb, XP_RT_BOOLEAN); if (!result) { *res = ERR_INTERNAL_MEM; } else { if (strstr((const char *)str1, (const char *)str2)) { result->r.boo = TRUE; } else { result->r.boo = FALSE; } *res = NO_ERR; } if (malloc1) { m__free(str1); } if (malloc2) { m__free(str2); } return result; } /* contains_fn */ /******************************************************************** * FUNCTION count_fn * * number count(nodeset) function [4.1] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 parm (nodeset to get node count for) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * count_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result; ncx_num_t tempnum; if (!pcb->val && !pcb->obj) { return NULL; } parm = (xpath_result_t *)dlq_firstEntry(parmQ); *res = NO_ERR; result = new_result(pcb, XP_RT_NUMBER); if (!result) { *res = ERR_INTERNAL_MEM; } else { if (parm->restype != XP_RT_NODESET) { ncx_set_num_zero(&result->r.num, NCX_BT_FLOAT64); } else { if (pcb->val) { ncx_init_num(&tempnum); tempnum.u = dlq_count(&parm->r.nodeQ); *res = ncx_cast_num(&tempnum, NCX_BT_UINT32, &result->r.num, NCX_BT_FLOAT64); ncx_clean_num(NCX_BT_UINT32, &tempnum); } else { ncx_set_num_zero(&result->r.num, NCX_BT_FLOAT64); } if (*res != NO_ERR) { free_result(pcb, result); result = NULL; } } } return result; } /* count_fn */ /******************************************************************** * FUNCTION current_fn * * number current() function [XPATH 2.0 used in YANG] * * INPUTS: * pcb == parser control block to use * parmQ == empty parmQ * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * current_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *result; (void)parmQ; result = new_nodeset(pcb, pcb->orig_context.node.objptr, pcb->orig_context.node.valptr, 1, FALSE); if (!result) { *res = ERR_INTERNAL_MEM; } else { *res = NO_ERR; } return result; } /* current_fn */ /******************************************************************** * FUNCTION false_fn * * boolean false() function [4.3] * * INPUTS: * pcb == parser control block to use * parmQ == empty parmQ * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * false_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *result; if (!pcb->val && !pcb->obj) { return NULL; } (void)parmQ; result = new_result(pcb, XP_RT_BOOLEAN); if (!result) { *res = ERR_INTERNAL_MEM; } else { result->r.boo = FALSE; *res = NO_ERR; } return result; } /* false_fn */ /******************************************************************** * FUNCTION floor_fn * * number floor(number) function [4.4] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 number to convert to floor(number) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * floor_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result; ncx_num_t num; if (!pcb->val && !pcb->obj) { return NULL; } parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (!parm) { *res = ERR_NCX_MISSING_PARM; return NULL; } result = new_result(pcb, XP_RT_NUMBER); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } if (parm->restype == XP_RT_NUMBER) { *res = ncx_num_floor(&parm->r.num, &result->r.num, NCX_BT_FLOAT64); } else { ncx_init_num(&num); xpath_cvt_number(parm, &num); *res = ncx_num_floor(&num, &result->r.num, NCX_BT_FLOAT64); ncx_clean_num(NCX_BT_FLOAT64, &num); } if (*res != NO_ERR) { free_result(pcb, result); result = NULL; } return result; } /* floor_fn */ /******************************************************************** * FUNCTION id_fn * * nodeset id(object) function [4.1] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 parm, which is the object to match * against the current result in progress * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * id_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *result; if (!pcb->val && !pcb->obj) { return NULL; } (void)parmQ; /*****/ result = new_result(pcb, XP_RT_NODESET); if (!result) { *res = ERR_INTERNAL_MEM; } else { *res = NO_ERR; } /**** get all nodes with the specified ID ****/ /**** No IDs in YANG so return empty nodeset ****/ /**** Change this later if there are standard IDs ****/ return result; } /* id_fn */ /******************************************************************** * FUNCTION lang_fn * * boolean lang(string) function [4.3] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 parm; lang string to match * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * lang_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *result; (void)parmQ; /* no attributes in YANG, so no lang attr either */ result = new_result(pcb, XP_RT_NODESET); if (!result) { *res = ERR_INTERNAL_MEM; } else { *res = NO_ERR; } return result; } /* lang_fn */ /******************************************************************** * FUNCTION last_fn * * number last() function [4.1] * * INPUTS: * pcb == parser control block to use * parmQ == empty parmQ * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * last_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *result; ncx_num_t tempnum; if (!pcb->val && !pcb->obj) { return NULL; } (void)parmQ; *res = NO_ERR; result = new_result(pcb, XP_RT_NUMBER); if (!result) { *res = ERR_INTERNAL_MEM; } else { ncx_init_num(&tempnum); tempnum.l = pcb->context.last; *res = ncx_cast_num(&tempnum, NCX_BT_INT64, &result->r.num, NCX_BT_FLOAT64); ncx_clean_num(NCX_BT_INT64, &tempnum); if (*res != NO_ERR) { free_result(pcb, result); result = NULL; } } return result; } /* last_fn */ /******************************************************************** * FUNCTION local_name_fn * * string local-name(nodeset?) function [4.1] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with optional node-set parm * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * local_name_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result; xpath_resnode_t *resnode; const xmlChar *str; uint32 parmcnt; if (!pcb->val && !pcb->obj) { return NULL; } parmcnt = dlq_count(parmQ); if (parmcnt > 1) { *res = ERR_NCX_EXTRA_PARM; wrong_parmcnt_error(pcb, parmcnt, *res); return NULL; } parm = (xpath_result_t *)dlq_firstEntry(parmQ); str = NULL; if (parm && parm->restype != XP_RT_NODESET) { ; } else if (parm) { resnode = (xpath_resnode_t *) dlq_firstEntry(&parm->r.nodeQ); if (resnode) { if (pcb->val) { str = resnode->node.valptr->name; } else { str = obj_get_name(resnode->node.objptr); } } } else { if (pcb->val) { str = pcb->context.node.valptr->name; } else { str = obj_get_name(pcb->context.node.objptr); } } result = new_result(pcb, XP_RT_STRING); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } if (str) { result->r.str = xml_strdup(str); } else { result->r.str = xml_strdup(EMPTY_STRING); } if (!result->r.str) { *res = ERR_INTERNAL_MEM; free_result(pcb, result); return NULL; } return result; } /* local_name_fn */ /******************************************************************** * FUNCTION namespace_uri_fn * * string namespace-uri(nodeset?) function [4.1] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with optional node-set parm * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * namespace_uri_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result; xpath_resnode_t *resnode; const xmlChar *str; uint32 parmcnt; xmlns_id_t nsid; if (!pcb->val && !pcb->obj) { return NULL; } parmcnt = dlq_count(parmQ); if (parmcnt > 1) { *res = ERR_NCX_EXTRA_PARM; wrong_parmcnt_error(pcb, parmcnt, *res); return NULL; } nsid = 0; parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (parm && parm->restype != XP_RT_NODESET) { ; } else if (parm) { resnode = (xpath_resnode_t *) dlq_firstEntry(&parm->r.nodeQ); if (resnode) { if (pcb->val) { nsid = obj_get_nsid(resnode->node.valptr->obj); } else { nsid = obj_get_nsid(resnode->node.objptr); } } } else { if (pcb->val) { nsid = obj_get_nsid(pcb->context.node.valptr->obj); } else { nsid = obj_get_nsid(pcb->context.node.objptr); } } result = new_result(pcb, XP_RT_STRING); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } if (nsid) { str = xmlns_get_ns_name(nsid); result->r.str = xml_strdup(str); } else { result->r.str = xml_strdup(EMPTY_STRING); } if (!result->r.str) { *res = ERR_INTERNAL_MEM; free_result(pcb, result); return NULL; } return result; } /* namespace_uri_fn */ /******************************************************************** * FUNCTION name_fn * * string name(nodeset?) function [4.1] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with optional node-set parm * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * name_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result; xpath_resnode_t *resnode; const xmlChar *prefix, *name; xmlChar *str; uint32 len, parmcnt; xmlns_id_t nsid; if (!pcb->val && !pcb->obj) { return NULL; } parmcnt = dlq_count(parmQ); if (parmcnt > 1) { *res = ERR_NCX_EXTRA_PARM; wrong_parmcnt_error(pcb, parmcnt, *res); return NULL; } nsid = 0; name = NULL; prefix = NULL; parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (parm && parm->restype != XP_RT_NODESET) { ; } else if (parm) { resnode = (xpath_resnode_t *) dlq_firstEntry(&parm->r.nodeQ); if (resnode) { if (pcb->val) { nsid = obj_get_nsid(resnode->node.valptr->obj); name = resnode->node.valptr->name; } else { nsid = obj_get_nsid(resnode->node.objptr); name = obj_get_name(resnode->node.objptr); } } } else { if (pcb->val) { nsid = obj_get_nsid(pcb->context.node.valptr->obj); name = pcb->context.node.valptr->name; } else { nsid = obj_get_nsid(pcb->context.node.objptr); name = obj_get_name(pcb->context.node.objptr); } } result = new_result(pcb, XP_RT_STRING); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } if (nsid) { prefix = xmlns_get_ns_prefix(nsid); } len = 0; if (prefix) { len += xml_strlen(prefix); len++; } if (name) { len += xml_strlen(name); } else { name = EMPTY_STRING; } result->r.str = m__getMem(len+1); if (!result->r.str) { *res = ERR_INTERNAL_MEM; free_result(pcb, result); return NULL; } str = result->r.str; if (prefix) { str += xml_strcpy(str, prefix); *str++ = ':'; } xml_strcpy(str, name); return result; } /* name_fn */ /******************************************************************** * FUNCTION normalize_space_fn * * string normalize-space(string?) function [4.2] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with optional string to convert to normalized * string * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * normalize_space_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result, *tempresult; const xmlChar *teststr; xmlChar *mallocstr, *str; uint32 parmcnt; if (!pcb->val && !pcb->obj) { return NULL; } parmcnt = dlq_count(parmQ); if (parmcnt > 1) { *res = ERR_NCX_EXTRA_PARM; wrong_parmcnt_error(pcb, parmcnt, *res); return NULL; } teststr = NULL; str = NULL; mallocstr = NULL; parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (parm && parm->restype != XP_RT_STRING) { ; } else if (parm) { teststr = parm->r.str; } else { tempresult= new_nodeset(pcb, pcb->context.node.objptr, pcb->context.node.valptr, 1, FALSE); if (!tempresult) { *res = ERR_INTERNAL_MEM; return NULL; } else { *res = xpath_cvt_string(pcb, tempresult, &mallocstr); if (*res == NO_ERR) { teststr = mallocstr; } free_result(pcb, tempresult); } } if (*res != NO_ERR) { return NULL; } result = new_result(pcb, XP_RT_STRING); if (!result) { *res = ERR_INTERNAL_MEM; if (mallocstr) { m__free(mallocstr); } return NULL; } if (!teststr) { teststr = EMPTY_STRING; } result->r.str = m__getMem(xml_strlen(teststr)+1); if (!result->r.str) { *res = ERR_INTERNAL_MEM; free_result(pcb, result); if (mallocstr) { m__free(mallocstr); } return NULL; } str = result->r.str; while (*teststr && xml_isspace(*teststr)) { teststr++; } while (*teststr) { if (xml_isspace(*teststr)) { *str++ = ' '; teststr++; while (*teststr && xml_isspace(*teststr)) { teststr++; } } else { *str++ = *teststr++; } } if (str > result->r.str && *(str-1) == ' ') { str--; } *str = 0; if (mallocstr) { m__free(mallocstr); } return result; } /* normalize_space_fn */ /******************************************************************** * FUNCTION not_fn * * boolean not(object) function [4.3] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 object to perform NOT boolean conversion * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * not_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result; boolean boolresult; if (!pcb->val && !pcb->obj) { return NULL; } parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (parm->restype != XP_RT_BOOLEAN) { boolresult = xpath_cvt_boolean(parm); } else { boolresult = parm->r.boo; } result = new_result(pcb, XP_RT_BOOLEAN); if (!result) { *res = ERR_INTERNAL_MEM; } else { result->r.boo = !boolresult; *res = NO_ERR; } return result; } /* not_fn */ /******************************************************************** * FUNCTION number_fn * * number number(object?) function [4.4] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 optional object to convert to a number * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * number_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result, *tempresult; uint32 parmcnt; if (!pcb->val && !pcb->obj) { return NULL; } parmcnt = dlq_count(parmQ); if (parmcnt > 1) { *res = ERR_NCX_EXTRA_PARM; wrong_parmcnt_error(pcb, parmcnt, *res); return NULL; } result = new_result(pcb, XP_RT_NUMBER); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } *res = NO_ERR; parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (parm) { xpath_cvt_number(parm, &result->r.num); } else { /* convert the context node value */ if (pcb->val) { /* get the value of the context node * and convert it to a string first */ tempresult= new_nodeset(pcb, pcb->context.node.objptr, pcb->context.node.valptr, 1, FALSE); if (!tempresult) { *res = ERR_INTERNAL_MEM; } else { xpath_cvt_number(tempresult, &result->r.num); free_result(pcb, tempresult); } } else { /* nothing to check; conversion to NaN is * not an XPath error */ ncx_set_num_zero(&result->r.num, NCX_BT_FLOAT64); } } if (*res != NO_ERR) { free_result(pcb, result); result = NULL; } return result; } /* number_fn */ /******************************************************************** * FUNCTION position_fn * * number position() function [4.1] * * INPUTS: * pcb == parser control block to use * parmQ == empty parmQ * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * position_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *result; ncx_num_t tempnum; if (!pcb->val && !pcb->obj) { return NULL; } (void)parmQ; *res = NO_ERR; result = new_result(pcb, XP_RT_NUMBER); if (!result) { *res = ERR_INTERNAL_MEM; } else { ncx_init_num(&tempnum); tempnum.l = pcb->context.position; *res = ncx_cast_num(&tempnum, NCX_BT_INT64, &result->r.num, NCX_BT_FLOAT64); ncx_clean_num(NCX_BT_INT64, &tempnum); if (*res != NO_ERR) { free_result(pcb, result); result = NULL; } } return result; } /* position_fn */ /******************************************************************** * FUNCTION round_fn * * number round(number) function [4.4] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 number to convert to round(number) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * round_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result; ncx_num_t num; if (!pcb->val && !pcb->obj) { return NULL; } parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (!parm) { *res = ERR_NCX_MISSING_PARM; return NULL; } result = new_result(pcb, XP_RT_NUMBER); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } if (parm->restype == XP_RT_NUMBER) { *res = ncx_round_num(&parm->r.num, &result->r.num, NCX_BT_FLOAT64); } else { ncx_init_num(&num); xpath_cvt_number(parm, &num); *res = ncx_round_num(&num, &result->r.num, NCX_BT_FLOAT64); ncx_clean_num(NCX_BT_FLOAT64, &num); } if (*res != NO_ERR) { free_result(pcb, result); result = NULL; } return result; } /* round_fn */ /******************************************************************** * FUNCTION starts_with_fn * * boolean starts-with(string, string) function [4.2] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 2 strings * returns true if the 1st string starts with the 2nd string * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * starts_with_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm1, *parm2, *result; xmlChar *str1, *str2; uint32 len1, len2; boolean malloc1, malloc2; if (!pcb->val && !pcb->obj) { return NULL; } *res = NO_ERR; str1 = NULL; str2 = NULL; malloc1 = FALSE; malloc2 = FALSE; len1 = 0; len2= 0; parm1 = (xpath_result_t *)dlq_firstEntry(parmQ); parm2 = (xpath_result_t *)dlq_nextEntry(parm1); if (parm1->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm1, &str1); malloc1 = TRUE; } else { str1 = parm1->r.str; } if (*res != NO_ERR) { return NULL; } if (parm2->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm2, &str2); malloc2 = TRUE; } else { str2 = parm2->r.str; } if (*res != NO_ERR) { if (malloc1) { m__free(str1); } return NULL; } result = new_result(pcb, XP_RT_BOOLEAN); if (!result) { *res = ERR_INTERNAL_MEM; } else { len1 = xml_strlen(str1); len2 = xml_strlen(str2); if (len2 > len1) { result->r.boo = FALSE; } else if (!xml_strncmp(str1, str2, len2)) { result->r.boo = TRUE; } else { result->r.boo = FALSE; } *res = NO_ERR; } if (malloc1) { m__free(str1); } if (malloc2) { m__free(str2); } return result; } /* starts_with_fn */ /******************************************************************** * FUNCTION string_fn * * string string(object?) function [4.2] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with optional object to convert to string * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * string_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result, *tempresult; uint32 parmcnt; if (!pcb->val && !pcb->obj) { return NULL; } parmcnt = dlq_count(parmQ); if (parmcnt > 1) { *res = ERR_NCX_EXTRA_PARM; wrong_parmcnt_error(pcb, parmcnt, *res); return NULL; } result = new_result(pcb, XP_RT_STRING); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } *res = NO_ERR; parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (parm) { xpath_cvt_string(pcb, parm, &result->r.str); } else { /* convert the context node value */ if (pcb->val) { /* get the value of the context node * and convert it to a string first */ tempresult= new_nodeset(pcb, pcb->context.node.objptr, pcb->context.node.valptr, 1, FALSE); if (!tempresult) { *res = ERR_INTERNAL_MEM; } else { *res = xpath_cvt_string(pcb, tempresult, &result->r.str); free_result(pcb, tempresult); } } else { result->r.str = xml_strdup(EMPTY_STRING); if (!result->r.str) { *res = ERR_INTERNAL_MEM; } } } if (*res != NO_ERR) { free_result(pcb, result); result = NULL; } return result; } /* string_fn */ /******************************************************************** * FUNCTION string_length_fn * * number string-length(string?) function [4.2] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with optional string to check length * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * string_length_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result, *tempresult; xmlChar *tempstr; uint32 parmcnt, len; if (!pcb->val && !pcb->obj) { return NULL; } parmcnt = dlq_count(parmQ); if (parmcnt > 1) { *res = ERR_NCX_EXTRA_PARM; wrong_parmcnt_error(pcb, parmcnt, *res); return NULL; } result = new_result(pcb, XP_RT_NUMBER); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } *res = NO_ERR; len = 0; tempstr = NULL; parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (parm && parm->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm, &tempstr); if (*res == NO_ERR) { len = xml_strlen(tempstr); m__free(tempstr); } } else if (parm) { len = xml_strlen(parm->r.str); } else { /* convert the context node value */ if (pcb->val) { /* get the value of the context node * and convert it to a string first */ tempresult= new_nodeset(pcb, pcb->context.node.objptr, pcb->context.node.valptr, 1, FALSE); if (!tempresult) { *res = ERR_INTERNAL_MEM; } else { tempstr = NULL; *res = xpath_cvt_string(pcb, tempresult, &tempstr); free_result(pcb, tempresult); if (*res == NO_ERR) { len = xml_strlen(tempstr); m__free(tempstr); } } } } if (*res == NO_ERR) { set_uint32_num(len, &result->r.num); } else { free_result(pcb, result); result = NULL; } return result; } /* string_length_fn */ /******************************************************************** * FUNCTION substring_fn * * string substring(string, number, number?) function [4.2] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 2 or 3 parms * returns substring of 1st string starting at the * position indicated by the first number; copies * only N chars if the 2nd number is present * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * substring_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm1, *parm2, *parm3, *result; xmlChar *str1; ncx_num_t tempnum, num2, num3; uint32 parmcnt, maxlen; int64 startpos, copylen, slen; status_t myres; boolean copylenset, malloc1; if (!pcb->val && !pcb->obj) { return NULL; } *res = NO_ERR; str1 = NULL; malloc1 = FALSE; ncx_init_num(&num2); ncx_init_num(&num3); /* check at least 2 strings to concat */ parmcnt = dlq_count(parmQ); if (parmcnt < 2) { *res = ERR_NCX_MISSING_PARM; wrong_parmcnt_error(pcb, parmcnt, *res); return NULL; } else if (parmcnt > 3) { *res = ERR_NCX_EXTRA_PARM; wrong_parmcnt_error(pcb, parmcnt, *res); return NULL; } parm1 = (xpath_result_t *)dlq_firstEntry(parmQ); parm2 = (xpath_result_t *)dlq_nextEntry(parm1); parm3 = (xpath_result_t *)dlq_nextEntry(parm2); if (parm1->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm1, &str1); if (*res != NO_ERR) { return NULL; } else { malloc1 = TRUE; } } else { str1 = parm1->r.str; } xpath_cvt_number(parm2, &num2); if (parm3) { xpath_cvt_number(parm3, &num3); } startpos = 0; ncx_init_num(&tempnum); myres = ncx_round_num(&num2, &tempnum, NCX_BT_FLOAT64); if (myres == NO_ERR) { startpos = ncx_cvt_to_int64(&tempnum, NCX_BT_FLOAT64); } ncx_clean_num(NCX_BT_FLOAT64, &tempnum); copylenset = FALSE; copylen = 0; if (parm3) { copylenset = TRUE; myres = ncx_round_num(&num3, &tempnum, NCX_BT_FLOAT64); if (myres == NO_ERR) { copylen = ncx_cvt_to_int64(&tempnum, NCX_BT_FLOAT64); } ncx_clean_num(NCX_BT_FLOAT64, &tempnum); } slen = (int64)xml_strlen(str1); result = new_result(pcb, XP_RT_STRING); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } if (startpos > (slen+1) || (copylenset && copylen <= 0)) { /* starting after EOS */ result->r.str = xml_strdup(EMPTY_STRING); } else if (copylenset) { if (startpos < 1) { /* adjust startpos, then copy N chars */ copylen += (startpos-1); startpos = 1; } if (copylen >= 1) { if (copylen >= NCX_MAX_UINT) { /* internal max of strings way less than 4G * so copy the whole string */ result->r.str = xml_strdup(&str1[startpos-1]); } else { /* copy at most N chars of whole string */ maxlen = (uint32)copylen; result->r.str = xml_strndup(&str1[startpos-1], maxlen); } } else { result->r.str = xml_strdup(EMPTY_STRING); } } else if (startpos <= 1) { /* copying whole string */ result->r.str = xml_strdup(str1); } else { /* copying rest of string */ result->r.str = xml_strdup(&str1[startpos-1]); } if (!result->r.str) { *res = ERR_INTERNAL_MEM; free_result(pcb, result); result = NULL; } if (malloc1) { m__free(str1); } ncx_clean_num(NCX_BT_FLOAT64, &num2); ncx_clean_num(NCX_BT_FLOAT64, &num3); return result; } /* substring_fn */ /******************************************************************** * FUNCTION substring_after_fn * * string substring-after(string, string) function [4.2] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 2 strings * returns substring of 1st string after * the occurance of the 2nd string * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * substring_after_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm1, *parm2, *result; const xmlChar *str, *retstr; xmlChar *str1, *str2; boolean malloc1, malloc2; if (!pcb->val && !pcb->obj) { return NULL; } str1 = NULL; str2 = NULL; malloc1 = FALSE; malloc2 = FALSE; parm1 = (xpath_result_t *)dlq_firstEntry(parmQ); parm2 = (xpath_result_t *)dlq_nextEntry(parm1); if (parm1->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm1, &str1); if (*res != NO_ERR) { return NULL; } malloc1 = TRUE; } else { str1 = parm1->r.str; } if (parm2->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm2, &str2); if (*res != NO_ERR) { if (malloc1) { m__free(str1); } return NULL; } malloc2 = TRUE; } else { str2 = parm2->r.str; } result = new_result(pcb, XP_RT_STRING); if (result) { str = (const xmlChar *) strstr((const char *)str1, (const char *)str2); if (str) { retstr = str + xml_strlen(str2); result->r.str = m__getMem(xml_strlen(retstr)+1); if (result->r.str) { xml_strcpy(result->r.str, retstr); } } else { result->r.str = xml_strdup(EMPTY_STRING); } if (!result->r.str) { *res = ERR_INTERNAL_MEM; } if (*res != NO_ERR) { free_result(pcb, result); result = NULL; } } else { *res = ERR_INTERNAL_MEM; } if (malloc1) { m__free(str1); } if (malloc2) { m__free(str2); } return result; } /* substring_after_fn */ /******************************************************************** * FUNCTION substring_before_fn * * string substring-before(string, string) function [4.2] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 2 strings * returns substring of 1st string that precedes * the occurance of the 2nd string * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * substring_before_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm1, *parm2, *result; const xmlChar *str; xmlChar *str1, *str2; uint32 retlen; boolean malloc1, malloc2; if (!pcb->val && !pcb->obj) { return NULL; } malloc1 = FALSE; malloc2 = FALSE; parm1 = (xpath_result_t *)dlq_firstEntry(parmQ); parm2 = (xpath_result_t *)dlq_nextEntry(parm1); if (parm1->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm1, &str1); if (*res != NO_ERR) { return NULL; } malloc1 = TRUE; } else { str1 = parm1->r.str; } if (parm2->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm2, &str2); if (*res != NO_ERR) { if (malloc1) { m__free(str1); } return NULL; } malloc2 = TRUE; } else { str2 = parm2->r.str; } result = new_result(pcb, XP_RT_STRING); if (result) { str = (const xmlChar *) strstr((const char *)str1, (const char *)str2); if (str) { retlen = (uint32)(str - str1); result->r.str = m__getMem(retlen+1); if (result->r.str) { xml_strncpy(result->r.str, str1, retlen); } } else { result->r.str = xml_strdup(EMPTY_STRING); } if (!result->r.str) { *res = ERR_INTERNAL_MEM; } if (*res != NO_ERR) { free_result(pcb, result); result = NULL; } } else { *res = ERR_INTERNAL_MEM; } if (malloc1) { m__free(str1); } if (malloc2) { m__free(str2); } return result; } /* substring_before_fn */ /******************************************************************** * FUNCTION sum_fn * * number sum(nodeset) function [4.4] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 nodeset to convert to numbers * and add together to resurn the total * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * sum_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *result; xpath_resnode_t *resnode; val_value_t *val; ncx_num_t tempnum, mysum; status_t myres; if (!pcb->val && !pcb->obj) { return NULL; } result = new_result(pcb, XP_RT_NUMBER); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (parm->restype != XP_RT_NODESET || !pcb->val) { ncx_set_num_zero(&result->r.num, NCX_BT_FLOAT64); } else { ncx_init_num(&tempnum); ncx_set_num_zero(&tempnum, NCX_BT_FLOAT64); ncx_init_num(&mysum); ncx_set_num_zero(&mysum, NCX_BT_FLOAT64); myres = NO_ERR; for (resnode = (xpath_resnode_t *) dlq_firstEntry(&parm->r.nodeQ); resnode != NULL && myres == NO_ERR; resnode = (xpath_resnode_t *) dlq_nextEntry(resnode)) { val = val_get_first_leaf(resnode->node.valptr); if (val && typ_is_number(val->btyp)) { myres = ncx_cast_num(&val->v.num, val->btyp, &tempnum, NCX_BT_FLOAT64); if (myres == NO_ERR) { mysum.d += tempnum.d; } } else { myres = ERR_NCX_INVALID_VALUE; } } if (myres == NO_ERR) { result->r.num.d = mysum.d; } else { ncx_set_num_nan(&result->r.num, NCX_BT_FLOAT64); } } return result; } /* sum_fn */ /******************************************************************** * FUNCTION translate_fn * * string translate(string, string, string) function [4.2] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 3 strings to translate * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * translate_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm1, *parm2, *parm3, *result; xmlChar *p1str, *p2str, *p3str, *writestr; uint32 parmcnt, p1len, p2len, p3len, curpos; boolean done, malloc1, malloc2, malloc3; if (!pcb->val && !pcb->obj) { return NULL; } malloc1 = FALSE; malloc2 = FALSE; malloc3 = FALSE; *res = NO_ERR; parm1 = (xpath_result_t *)dlq_firstEntry(parmQ); parm2 = (xpath_result_t *)dlq_nextEntry(parm1); parm3 = (xpath_result_t *)dlq_nextEntry(parm2); /* check at least 3 parameters */ parmcnt = dlq_count(parmQ); if (parmcnt < 3) { *res = ERR_NCX_MISSING_PARM; return NULL; } if (parm1->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm1, &p1str); if (*res != NO_ERR) { return NULL; } malloc1 = TRUE; } else { p1str = parm1->r.str; } p1len = xml_strlen(p1str); if (parm2->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm2, &p2str); if (*res != NO_ERR) { if (malloc1) { m__free(p1str); } return NULL; } malloc2 = TRUE; } else { p2str = parm2->r.str; } p2len = xml_strlen(p2str); if (parm3->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm3, &p3str); if (*res != NO_ERR) { if (malloc1) { m__free(p1str); } if (malloc2) { m__free(p2str); } return NULL; } malloc3 = TRUE; } else { p3str = parm3->r.str; } p3len = xml_strlen(p3str); result = new_result(pcb, XP_RT_STRING); if (!result) { *res = ERR_INTERNAL_MEM; } else { result->r.str = m__getMem(p1len+1); if (!result->r.str) { *res = ERR_INTERNAL_MEM; free_result(pcb, result); result = NULL; } else { writestr = result->r.str; /* translate p1str into the result string */ while (*p1str) { curpos = 0; done = FALSE; /* look for a match char in p2str */ while (!done && curpos < p2len) { if (p2str[curpos] == *p1str) { done = TRUE; } else { curpos++; } } if (done) { if (curpos < p3len) { /* replace p1char with p3str translation char */ *writestr++ = p3str[curpos]; } /* else drop p1char from result */ } else { /* copy p1char to result */ *writestr++ = *p1str; } p1str++; } *writestr = 0; } } if (malloc1) { m__free(p1str); } if (malloc2) { m__free(p2str); } if (malloc3) { m__free(p3str); } return result; } /* translate_fn */ /******************************************************************** * FUNCTION true_fn * * boolean true() function [4.3] * * INPUTS: * pcb == parser control block to use * parmQ == empty parmQ * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * true_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *result; if (!pcb->val && !pcb->obj) { return NULL; } (void)parmQ; result = new_result(pcb, XP_RT_BOOLEAN); if (!result) { *res = ERR_INTERNAL_MEM; } else { result->r.boo = TRUE; *res = NO_ERR; } return result; } /* true_fn */ /******************************************************************** * FUNCTION module_loaded_fn * * boolean module-loaded(string [,string]) * parm1 == module name * parm2 == optional revision-date * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 object to convert to boolean * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * module_loaded_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *parm2, *result; xmlChar *modstr, *revstr, *str1, *str2; if (!pcb->val && !pcb->obj) { return NULL; } *res = NO_ERR; modstr = NULL; revstr = NULL; str1 = NULL; str2 = NULL; if (dlq_count(parmQ) > 2) { /* should already be reported in obj mode */ *res = ERR_NCX_EXTRA_PARM; return NULL; } parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (parm == NULL) { /* should already be reported in obj mode */ *res = ERR_NCX_MISSING_PARM; return NULL; } parm2 = (xpath_result_t *)dlq_nextEntry(parm); result = new_result(pcb, XP_RT_BOOLEAN); if (result == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } result->r.boo = FALSE; if (parm->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm, &str1); if (*res == NO_ERR) { modstr = str1; } } else { modstr = parm->r.str; } if (*res == NO_ERR && parm2 != NULL) { if (parm2->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm2, &str2); if (*res == NO_ERR) { revstr = str2; } } else { revstr = parm2->r.str; } } if (*res == NO_ERR) { if (ncx_valid_name2(modstr)) { if (ncx_find_module(modstr, revstr)) { result->r.boo = TRUE; } } } if (str1 != NULL) { m__free(str1); } if (str2 != NULL) { m__free(str2); } return result; } /* module_loaded_fn */ /******************************************************************** * FUNCTION feature_enabled_fn * * boolean feature-enabled(string ,string) * parm1 == module name * parm2 == feature name * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 object to convert to boolean * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * feature_enabled_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { xpath_result_t *parm, *parm2, *result; xmlChar *modstr, *featstr, *str1, *str2; ncx_module_t *mod; ncx_feature_t *feat; if (!pcb->val && !pcb->obj) { return NULL; } *res = NO_ERR; modstr = NULL; featstr = NULL; str1 = NULL; str2 = NULL; parm = (xpath_result_t *)dlq_firstEntry(parmQ); if (parm == NULL) { /* should already be reported in obj mode */ *res = ERR_NCX_MISSING_PARM; return NULL; } parm2 = (xpath_result_t *)dlq_nextEntry(parm); result = new_result(pcb, XP_RT_BOOLEAN); if (result == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } result->r.boo = FALSE; if (parm->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm, &str1); if (*res == NO_ERR) { modstr = str1; } } else { modstr = parm->r.str; } if (*res == NO_ERR) { if (parm2->restype != XP_RT_STRING) { *res = xpath_cvt_string(pcb, parm2, &str2); if (*res == NO_ERR) { featstr = str2; } } else { featstr = parm2->r.str; } } if (*res == NO_ERR) { if (ncx_valid_name2(modstr)) { mod = ncx_find_module(modstr, NULL); if (mod != NULL) { feat = ncx_find_feature(mod, featstr); if (feat != NULL && ncx_feature_enabled(feat)) { result->r.boo = TRUE; } } } } if (str1 != NULL) { m__free(str1); } if (str2 != NULL) { m__free(str2); } return result; } /* feature_enabled_fn */ /******************************************************************** * FUNCTION re_match_fn * * re_match(srting subject,string pattern) function [10.2.1] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 2 parms * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * re_match_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { int ret; xpath_result_t *result; xpath_result_t *parm1, *parm2; xmlRegexpPtr regex; xmlns_id_t nsid; const xmlChar *name; xpath_resnode_t *resnode; parm1 = (xpath_result_t *)dlq_firstEntry(parmQ); parm2 = (xpath_result_t *)dlq_nextEntry(parm1); assert(parm1->restype==XP_RT_STRING || parm1->restype==XP_RT_NODESET); assert(parm2->restype==XP_RT_STRING); regex=xmlRegexpCompile(parm2->r.str); result = new_result(pcb, XP_RT_BOOLEAN); assert(result); result->r.boo = FALSE; if (pcb->val==NULL) { /*No data context. Just make sure the regex is valid.*/ if(regex) { result->r.boo = TRUE; } else { *res = ERR_NCX_INVALID_PATTERN; } } else if(parm1->restype==XP_RT_NODESET) { for (resnode = (xpath_resnode_t *) dlq_firstEntry(&parm1->r.nodeQ); resnode != NULL; resnode = (xpath_resnode_t *) dlq_nextEntry(resnode)) { val_value_t* val; val = val_get_first_leaf(resnode->node.valptr); if (val && val->btyp==NCX_BT_STRING) { /* all nodes in the nodeset must be leafs of type string */ ret = xmlRegexpExec(regex, VAL_STRING(val)); if (ret==1) { /*at least one match in the set*/ result->r.boo = TRUE; break; } else if (ret==0) { /*no match*/ continue; } else if (ret < 0) { /* pattern match execution error, but compiled ok */ assert(0); } else { /*the pattern should have been verified already*/ assert(0); } } else { *res = ERR_NCX_INVALID_VALUE; break; } } } else if(parm1->restype==XP_RT_STRING) { ret = xmlRegexpExec(regex, parm1->r.str); if (ret==1) { /*at least one match in the set*/ result->r.boo = TRUE; } else if (ret==0) { /*no match*/ result->r.boo = FALSE; } else if (ret < 0) { /* pattern match execution error, but compiled ok */ assert(0); } else { /*the pattern should have been verified already*/ assert(0); } } if(regex) { xmlRegFreeRegexp(regex); } if(*res!=NO_ERR) { xpath_free_result(result); return NULL; } return result; } /* re_match_fn */ static xpath_result_t * derived_from_common (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res, bool or_self_flag) { xpath_result_t *result; xpath_result_t *parm1, *parm2; const xmlChar *qname; ncx_identity_t *identity_base; xmlns_id_t nsid; const xmlChar *name; xpath_resnode_t *resnode; parm1 = (xpath_result_t *)dlq_firstEntry(parmQ); parm2 = (xpath_result_t *)dlq_nextEntry(parm1); assert(parm1->restype==XP_RT_NODESET); assert(parm2->restype==XP_RT_STRING); qname = parm2->r.str; *res = val_parse_idref(pcb->tkerr.mod, qname, &nsid, &name, &identity_base); if(*res != NO_ERR) { invalid_identityid_error(pcb,qname); return NULL; } result = new_result(pcb, XP_RT_BOOLEAN); if (result == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } if (pcb->val==NULL) { result->r.boo = TRUE; return result; } for (resnode = (xpath_resnode_t *) dlq_firstEntry(&parm1->r.nodeQ); resnode != NULL; resnode = (xpath_resnode_t *) dlq_nextEntry(resnode)) { val_value_t* val; val = val_get_first_leaf(resnode->node.valptr); if (val && val->btyp==NCX_BT_IDREF) { /* all nodes in the nodeset must be leafs of type identityref with bases parents of identity */ if((or_self_flag && val->v.idref.identity==identity_base) || ncx123_identity_is_derived_from(val->v.idref.identity, identity_base)) { continue; } else { result->r.boo = FALSE; return result; } } else { *res = ERR_NCX_INVALID_VALUE; break; } } if(*res!=NO_ERR) { return NULL; } result->r.boo = TRUE; return result; } /* derived_from_common */ /******************************************************************** * FUNCTION derived_from_fn * * derived_from(val,identityref) function [10.4.2] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 parm (nodeset to get node count for) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * derived_from_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { return derived_from_common(pcb, parmQ, res, FALSE); } /* derived_from_fn */ /******************************************************************** * FUNCTION derived_from_or_self_fn * * derived_from_or_self(val,identityref) function [10.4.2] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 parm (nodeset to get node count for) * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * derived_from_or_self_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { return derived_from_common(pcb, parmQ, res, TRUE); } /* derived_from_or_self_fn */ /******************************************************************** * FUNCTION deref_fn * * node-set deref(node-set nodes) function [10.3.1] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 parm * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * deref_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { int ret; xpath_result_t *result; xpath_result_t *parm1; xmlRegexpPtr regex; xmlns_id_t nsid; const xmlChar *name; xpath_resnode_t *resnode; parm1 = (xpath_result_t *)dlq_firstEntry(parmQ); assert(parm1->restype==XP_RT_NODESET); //result = new_nodeset(pcb,obj,val,0/*position*/,false /*boolean dblslash*/); result = new_result(pcb, XP_RT_NODESET); assert(result); for (resnode = (xpath_resnode_t *) dlq_firstEntry(&parm1->r.nodeQ); resnode != NULL; resnode = (xpath_resnode_t *) dlq_nextEntry(resnode)) { val_value_t* val; val=val123_deref(resnode->node.valptr); if(val==NULL) { *res = ERR_NCX_INVALID_VALUE; break; } resnode = new_val_resnode(pcb, 0/*position*/, FALSE/*dblslash*/, val); assert(resnode); result->isval = TRUE; dlq_enque(resnode, &result->r.nodeQ); } if(*res!=NO_ERR) { xpath_free_result(result); return NULL; } return result; } /* deref_fn */ /******************************************************************** * FUNCTION enum_value_fn * * number enum-value(node-set nodes) function [10.5.1] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 parm * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * enum_value_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { int ret; xpath_result_t *result; xpath_result_t *parm1; xmlRegexpPtr regex; xmlns_id_t nsid; const xmlChar *name; xpath_resnode_t *resnode; parm1 = (xpath_result_t *)dlq_firstEntry(parmQ); assert(parm1->restype==XP_RT_NODESET); result = new_result(pcb, XP_RT_NUMBER); assert(result); for (resnode = (xpath_resnode_t *) dlq_firstEntry(&parm1->r.nodeQ); resnode != NULL; resnode = (xpath_resnode_t *) dlq_nextEntry(resnode)) { if(pcb->val) { set_uint32_num(resnode->node.valptr->v.enu.val, &result->r.num); } break; } if(*res!=NO_ERR) { xpath_free_result(result); return NULL; } return result; } /* enum_value_fn */ /******************************************************************** * FUNCTION bit_is_set_fn * * boolean bit-is-set(node-set nodes, string bit-name) function [10.6.1] * * INPUTS: * pcb == parser control block to use * parmQ == parmQ with 1 parm * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * malloced xpath_result_t if no error and results being processed * NULL if error *********************************************************************/ static xpath_result_t * bit_is_set_fn (xpath_pcb_t *pcb, dlq_hdr_t *parmQ, status_t *res) { int ret; xpath_result_t *result; xpath_result_t *parm1; xpath_result_t *parm2; xmlRegexpPtr regex; xmlns_id_t nsid; const xmlChar *name; xpath_resnode_t *resnode; parm1 = (xpath_result_t *)dlq_firstEntry(parmQ); assert(parm1->restype==XP_RT_NODESET); parm2 = (xpath_result_t *)dlq_nextEntry(parm1); assert(parm2->restype==XP_RT_STRING); result = new_result(pcb, XP_RT_BOOLEAN); assert(result); result->r.boo = TRUE; for (resnode = (xpath_resnode_t *) dlq_firstEntry(&parm1->r.nodeQ); resnode != NULL; resnode = (xpath_resnode_t *) dlq_nextEntry(resnode)) { uint32 position; obj_template_t* obj; if(pcb->val) { obj = resnode->node.valptr->obj; } else { obj = resnode->node.objptr; } const typ_def_t *typdef = obj_get_ctypdef(obj); *res = val_bit_ok (typdef, parm2->r.str, &position); if(*res != NO_ERR) { break; } if(pcb->val) { if(!val123_bit_is_set(resnode->node.valptr, parm2->r.str)) { result->r.boo = FALSE; } } break; } if(*res!=NO_ERR) { xpath_free_result(result); return NULL; } return result; } /* bit_is_set_fn */ /**************** U T I L I T Y F U N C T I O N S ***********/ /******************************************************************** * FUNCTION find_fncb * * Find an XPath function control block * * INPUTS: * pcb == parser control block to check * name == name string to check * * RETURNS: * pointer to found control block * NULL if not found *********************************************************************/ static const xpath_fncb_t * find_fncb (xpath_pcb_t *pcb, const xmlChar *name) { const xpath_fncb_t *fncb; uint32 i; i = 0; fncb = &pcb->functions[i]; while (fncb && fncb->name) { if (!xml_strcmp(name, fncb->name)) { return fncb; } else { fncb = &pcb->functions[++i]; } } if(pcb->tkerr.mod->langver == NCX_YANG_VERSION11) { i = 0; fncb = &functions11[i]; while (fncb && fncb->name) { if (!xml_strcmp(name, fncb->name)) { return fncb; } else { fncb = &functions11[++i]; } } } return NULL; } /* find_fncb */ /******************************************************************** * FUNCTION get_varbind * * Get the specified variable binding * * INPUTS: * pcb == parser control block in progress * prefix == prefix string of module with the varbind * == NULL for current module (pcb->tkerr.mod) * prefixlen == length of prefix string * name == variable name string * res == address of return status * * OUTPUTS: * *res == resturn status * * RETURNS: * pointer to found varbind, NULL if not found *********************************************************************/ static ncx_var_t * get_varbind (xpath_pcb_t *pcb, const xmlChar *prefix, uint32 prefixlen, const xmlChar *name, status_t *res) { ncx_var_t *var; var = NULL; *res = NO_ERR; /* * varbinds with prefixes are not supported in NETCONF * there is no way to define a varbind in a YANG module * so they do not correlate to modules. * * They are name to value node bindings in yuma * and the pcb needs to be pre-configured with * a queue of varbinds or a callback function to get the * requested variable binding */ if (prefix && prefixlen) { /* no variables with prefixes allowed !!! */ *res = ERR_NCX_DEF_NOT_FOUND; } else { /* try to find the variable */ if (pcb->getvar_fn) { var = (*pcb->getvar_fn)(pcb, name, res); } else { var = var_get_que_raw(&pcb->varbindQ, 0, name); if (!var) { *res = ERR_NCX_DEF_NOT_FOUND; } } } return var; } /* get_varbind */ /******************************************************************** * FUNCTION match_next_token * * Match the next token in the chain with a type and possibly value * * INPUTS: * pcb == parser control block in progress * tktyp == token type to match * tkval == string val to match (or NULL to just match tktyp) * * RETURNS: * TRUE if the type and value (if non-NULL) match * FALSE if next token is not a match *********************************************************************/ static boolean match_next_token (xpath_pcb_t *pcb, tk_type_t tktyp, const xmlChar *tkval) { const xmlChar *nextval; tk_type_t nexttyp; nexttyp = tk_next_typ(pcb->tkc); if (nexttyp == tktyp) { if (tkval) { nextval = tk_next_val(pcb->tkc); if (nextval && !xml_strcmp(tkval, nextval)) { return TRUE; } else { return FALSE; } } else { return TRUE; } } else { return FALSE; } /*NOTREACHED*/ } /* match_next_token */ /******************************************************************** * FUNCTION check_qname_prefix * * Check the prefix for the current node against the proper context * and report any unresolved prefix errors * * Do not check the local-name part of the QName because * the module is still being parsed and out of order * nodes will not be found yet * * INPUTS: * pcb == parser control block in progress * prefix == prefix string to use (may be NULL) * prefixlen == length of prefix * nsid == address of return NS ID (may be NULL) * * OUTPUTS: * if non-NULL: * *nsid == namespace ID for the prefix, if found * * RETURNS: * status *********************************************************************/ static status_t check_qname_prefix (xpath_pcb_t *pcb, const xmlChar *prefix, uint32 prefixlen, xmlns_id_t *nsid) { ncx_module_t *targmod; status_t res; res = NO_ERR; if (pcb->reader) { res = xml_get_namespace_id(pcb->reader, prefix, prefixlen, nsid); } else { res = xpath_get_curmod_from_prefix_str(prefix, prefixlen, pcb->tkerr.mod, &targmod); if (res == NO_ERR) { *nsid = targmod->nsid; } } if (res != NO_ERR) { if (pcb->logerrors) { log_error("\nError: Module for prefix '%s' not found", (prefix) ? prefix : EMPTY_STRING); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, res); } } return res; } /* check_qname_prefix */ /******************************************************************** * FUNCTION cvt_from_value * * Convert a val_value_t into an XPath result * * INPUTS: * pcb == parser control block to use * val == value struct to convert * * RETURNS: * malloced and filled in xpath_result_t or NULL if malloc error *********************************************************************/ static xpath_result_t * cvt_from_value (xpath_pcb_t *pcb, val_value_t *val) { xpath_result_t *result; val_value_t *newval; const val_value_t *useval; status_t res; uint32 len; result = NULL; newval = NULL; res = NO_ERR; if (val_is_virtual(val)) { newval = val_get_virtual_value(NULL, val, &res); if (newval == NULL) { return NULL; } else { useval = newval; } } else { useval = val; } if (typ_is_string(useval->btyp)) { result = new_result(pcb, XP_RT_STRING); if (result) { if (VAL_STR(useval)) { result->r.str = xml_strdup(VAL_STR(useval)); } else { result->r.str = xml_strdup(EMPTY_STRING); } if (!result->r.str) { free_result(pcb, result); result = NULL; } } } else if (typ_is_number(useval->btyp)) { result = new_result(pcb, XP_RT_NUMBER); if (result) { res = ncx_cast_num(&useval->v.num, useval->btyp, &result->r.num, NCX_BT_FLOAT64); if (res != NO_ERR) { free_result(pcb, result); result = NULL; } } } else if (typ_is_simple(useval->btyp)) { len = 0; res = val_sprintf_simval_nc(NULL, useval, &len); if (res == NO_ERR) { result = new_result(pcb, XP_RT_STRING); if (result) { result->r.str = m__getMem(len+1); if (!result->r.str) { free_result(pcb, result); result = NULL; } else { res = val_sprintf_simval_nc(result->r.str, useval, &len); if (res != NO_ERR) { free_result(pcb, result); result = NULL; } } } } } return result; } /* cvt_from_value */ /******************************************************************** * FUNCTION find_resnode * * Check if the specified resnode ptr is already in the Q * * INPUTS: * pcb == parser control block to use * resultQ == Q of xpath_resnode_t structs to check * DOES NOT HAVE TO BE WITHIN A RESULT NODE Q * ptr == pointer value to find * * RETURNS: * found resnode or NULL if not found *********************************************************************/ static xpath_resnode_t * find_resnode (xpath_pcb_t *pcb, dlq_hdr_t *resultQ, const void *ptr) { xpath_resnode_t *resnode; for (resnode = (xpath_resnode_t *)dlq_firstEntry(resultQ); resnode != NULL; resnode = (xpath_resnode_t *)dlq_nextEntry(resnode)) { if (pcb->val) { if (resnode->node.valptr == ptr) { return resnode; } } else { if (resnode->node.objptr == ptr) { return resnode; } } } return NULL; } /* find_resnode */ /******************************************************************** * FUNCTION find_resnode_slow * * Check if the specified resnode ptr is already in the Q * * INPUTS: * pcb == parser control block to use * resultQ == Q of xpath_resnode_t structs to check * DOES NOT HAVE TO BE WITHIN A RESULT NODE Q * nsid == namespace ID of node to find; 0 to skip NS test * name == local-name of node to find * * RETURNS: * found resnode or NULL if not found *********************************************************************/ static xpath_resnode_t * find_resnode_slow (xpath_pcb_t *pcb, dlq_hdr_t *resultQ, xmlns_id_t nsid, const xmlChar *name) { xpath_resnode_t *resnode; for (resnode = (xpath_resnode_t *)dlq_firstEntry(resultQ); resnode != NULL; resnode = (xpath_resnode_t *)dlq_nextEntry(resnode)) { if (pcb->val) { if (nsid && (nsid != val_get_nsid(resnode->node.valptr))) { continue; } if (!xml_strcmp(name, resnode->node.valptr->name)) { return resnode; } } else { if (nsid && (nsid != obj_get_nsid(resnode->node.objptr))) { continue; } if (!xml_strcmp(name, obj_get_name(resnode->node.objptr))) { return resnode; } } } return NULL; } /* find_resnode_slow */ /******************************************************************** * FUNCTION merge_nodeset * * Add the nodes from val1 into val2 * that are not already there * * INPUTS: * pcb == parser control block to use * result == address of return XPath result nodeset * * OUTPUTS: * result->nodeQ contents adjusted * *********************************************************************/ static void merge_nodeset (xpath_pcb_t *pcb, xpath_result_t *val1, xpath_result_t *val2) { xpath_resnode_t *resnode, *findnode; if (!pcb->val && !pcb->obj) { return; } if (val1->restype != XP_RT_NODESET || val2->restype != XP_RT_NODESET) { SET_ERROR(ERR_INTERNAL_VAL); return; } while (!dlq_empty(&val1->r.nodeQ)) { resnode = (xpath_resnode_t *) dlq_deque(&val1->r.nodeQ); if (pcb->val) { findnode = find_resnode(pcb, &val2->r.nodeQ, resnode->node.valptr); } else { findnode = find_resnode(pcb, &val2->r.nodeQ, resnode->node.objptr); } if (findnode) { if (resnode->dblslash) { findnode->dblslash = TRUE; } findnode->position = resnode->position; free_resnode(pcb, resnode); } else { dlq_enque(resnode, &val2->r.nodeQ); } } } /* merge_nodeset */ /******************************************************************** * FUNCTION set_nodeset_dblslash * * Set the current nodes dblslash flag * to indicate that all unchecked descendants * are also represented by this node, for * further node-test or predicate testing * * INPUTS: * pcb == parser control block to use * result == address of return XPath result nodeset * * OUTPUTS: * result->nodeQ contents adjusted * *********************************************************************/ static void set_nodeset_dblslash (xpath_pcb_t *pcb, xpath_result_t *result) { xpath_resnode_t *resnode; if (!pcb->val && !pcb->obj) { return; } for (resnode = (xpath_resnode_t *) dlq_firstEntry(&result->r.nodeQ); resnode != NULL; resnode = (xpath_resnode_t *)dlq_nextEntry(resnode)) { resnode->dblslash = TRUE; } } /* set_nodeset_dblslash */ /******************************************************************** * FUNCTION value_walker_fn * * Check the current found value node, based on the * criteria passed to the search function * * Matches val_walker_fn_t template in val.h * * INPUTS: * val == value node found in the search * cookie1 == xpath_pcb_t * : parser control block to use * cookie2 == xpath_walkerparms_t *: walker parms to use * * OUTPUTS: * result->nodeQ contents adjusted or replaced * * RETURNS: * TRUE to keep walk going * FALSE to terminate walk *********************************************************************/ static boolean value_walker_fn (val_value_t *val, void *cookie1, void *cookie2) { xpath_pcb_t *pcb; xpath_walkerparms_t *parms; xpath_resnode_t *newresnode; val_value_t *child; int64 position; boolean done; pcb = (xpath_pcb_t *)cookie1; parms = (xpath_walkerparms_t *)cookie2; /* check if this node is already in the result */ if (find_resnode(pcb, parms->resnodeQ, val)) { return TRUE; } if (obj_is_root(val->obj) || val->parent==NULL) { position = 1; } else { position = 0; done = FALSE; for (child = val_get_first_child(val->parent); child != NULL && !done; child = val_get_next_child(child)) { position++; if (child == val) { done = TRUE; } } } ++parms->callcount; /* need to add this node */ newresnode = new_val_resnode(pcb, position, FALSE, val); if (!newresnode) { parms->res = ERR_INTERNAL_MEM; return FALSE; } dlq_enque(newresnode, parms->resnodeQ); return TRUE; } /* value_walker_fn */ /******************************************************************** * FUNCTION object_walker_fn * * Check the current found object node, based on the * criteria passed to the search function * * Matches obj_walker_fn_t template in obj.h * * INPUTS: * val == value node found in the search * cookie1 == xpath_pcb_t * : parser control block to use * cookie2 == xpath_walkerparms_t *: walker parms to use * * OUTPUTS: * result->nodeQ contents adjusted or replaced * * RETURNS: * TRUE to keep walk going * FALSE to terminate walk *********************************************************************/ static boolean object_walker_fn (obj_template_t *obj, void *cookie1, void *cookie2) { xpath_pcb_t *pcb; xpath_walkerparms_t *parms; xpath_resnode_t *newresnode; pcb = (xpath_pcb_t *)cookie1; parms = (xpath_walkerparms_t *)cookie2; /* check if this node is already in the result */ if (find_resnode(pcb, parms->resnodeQ, obj)) { return TRUE; } /* need to add this child node * the position is wrong but it will not really matter */ newresnode = new_obj_resnode(pcb, ++parms->callcount, FALSE, obj); if (!newresnode) { parms->res = ERR_INTERNAL_MEM; return FALSE; } dlq_enque(newresnode, parms->resnodeQ); return TRUE; } /* object_walker_fn */ /******************************************************************** * FUNCTION set_nodeset_self * * Check the current result nodeset and keep each * node, or remove it if filter tests fail * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block in progress * result == address of return XPath result nodeset * nsid == 0 if any namespace is OK * else only this namespace will be checked * name == name of parent to find * == NULL to find any parent name * textmode == TRUE if just selecting text() nodes * FALSE if ignored * * OUTPUTS: * result->nodeQ contents adjusted or removed * * RETURNS: * status *********************************************************************/ static status_t set_nodeset_self (xpath_pcb_t *pcb, xpath_result_t *result, xmlns_id_t nsid, const xmlChar *name, boolean textmode) { xpath_resnode_t *resnode, *findnode; obj_template_t *testobj; const xmlChar *modname; val_value_t *testval; boolean keep, cfgonly, fnresult, fncalled, useroot; dlq_hdr_t resnodeQ; status_t res; xpath_walkerparms_t walkerparms; if (!pcb->val && !pcb->obj) { return NO_ERR; } if (dlq_empty(&result->r.nodeQ)) { return NO_ERR; } dlq_createSQue(&resnodeQ); walkerparms.resnodeQ = &resnodeQ; walkerparms.res = NO_ERR; walkerparms.callcount = 0; modname = (nsid) ? xmlns_get_module(nsid) : NULL; useroot = (pcb->flags & XP_FL_USEROOT) ? TRUE : FALSE; res = NO_ERR; /* the resnodes need to be deleted or moved to a tempQ * to correctly track duplicates and remove them */ while (!dlq_empty(&result->r.nodeQ) && res == NO_ERR) { resnode = (xpath_resnode_t *)dlq_deque(&result->r.nodeQ); if (resnode->dblslash) { if (pcb->val) { testval = resnode->node.valptr; cfgonly = obj_is_config(testval->obj); fnresult = val_find_all_descendants(value_walker_fn, pcb, &walkerparms, testval, modname, name, cfgonly, textmode, TRUE, FALSE); } else { testobj = resnode->node.objptr; cfgonly = obj_is_config(testobj); fnresult = obj_find_all_descendants(pcb->objmod, object_walker_fn, pcb, &walkerparms, testobj, modname, name, cfgonly, textmode, useroot, TRUE, &fncalled); } if (!fnresult || walkerparms.res != NO_ERR) { res = walkerparms.res; } free_resnode(pcb, resnode); } else { keep = FALSE; if (pcb->val) { testval = resnode->node.valptr; if (textmode) { if (obj_has_text_content(testval->obj)) { keep = TRUE; } } else if (modname && name) { if (!xml_strcmp(modname, val_get_mod_name(testval)) && !xml_strcmp(name, testval->name)) { keep = TRUE; } } else if (modname) { if (!xml_strcmp(modname, val_get_mod_name(testval))) { keep = TRUE; } } else if (name) { if (!xml_strcmp(name, testval->name)) { keep = TRUE; } } else { keep = TRUE; } if (keep) { findnode = find_resnode(pcb, &resnodeQ, testval); if (findnode) { if (resnode->dblslash) { findnode->dblslash = TRUE; findnode->position = ++walkerparms.callcount; } free_resnode(pcb, resnode); } else { /* set the resnode to its parent */ resnode->node.valptr = testval; resnode->position = ++walkerparms.callcount; dlq_enque(resnode, &resnodeQ); } } else { free_resnode(pcb, resnode); } } else { testobj = resnode->node.objptr; if (textmode) { if (obj_has_text_content(testobj)) { keep = TRUE; } } else if (modname && name) { if (!xml_strcmp(modname, obj_get_mod_name(testobj)) && !xml_strcmp(name, obj_get_name(testobj))) { keep = TRUE; } } else if (modname) { if (!xml_strcmp(modname, obj_get_mod_name(testobj))) { keep = TRUE; } } else if (name) { if (!xml_strcmp(name, obj_get_name(testobj))) { keep = TRUE; } } else { keep = TRUE; } if (keep) { findnode = find_resnode(pcb, &resnodeQ, testobj); if (findnode) { if (resnode->dblslash) { findnode->dblslash = TRUE; findnode->position = ++walkerparms.callcount; } free_resnode(pcb, resnode); } else { /* set the resnode to its parent */ resnode->node.objptr = testobj; resnode->position = ++walkerparms.callcount; dlq_enque(resnode, &resnodeQ); } } else { if (pcb->logerrors && ncx_warning_enabled(ERR_NCX_NO_XPATH_NODES)) { log_warn("\nWarning: no self node found " "in XPath expr '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->objmod, ERR_NCX_NO_XPATH_NODES); } else if (pcb->objmod != NULL) { ncx_inc_warnings(pcb->objmod); } free_resnode(pcb, resnode); } } } } /* put the resnode entries back where they belong */ if (!dlq_empty(&resnodeQ)) { dlq_block_enque(&resnodeQ, &result->r.nodeQ); } result->last = walkerparms.callcount; return res; } /* set_nodeset_self */ /******************************************************************** * FUNCTION set_nodeset_parent * * Check the current result nodeset and move each * node to its parent, or remove it if no parent * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block in progress * result == address of return XPath result nodeset * nsid == 0 if any namespace is OK * else only this namespace will be checked * name == name of parent to find * == NULL to find any parent name * * OUTPUTS: * result->nodeQ contents adjusted or removed * * RETURNS: * status *********************************************************************/ static status_t set_nodeset_parent (xpath_pcb_t *pcb, xpath_result_t *result, xmlns_id_t nsid, const xmlChar *name) { xpath_resnode_t *resnode, *findnode; obj_template_t *testobj, *useobj; const xmlChar *modname; val_value_t *testval; boolean keep, cfgonly, fnresult, fncalled, useroot; dlq_hdr_t resnodeQ; status_t res; xpath_walkerparms_t walkerparms; int64 position; if (!pcb->val && !pcb->obj) { return NO_ERR; } if (dlq_empty(&result->r.nodeQ)) { return NO_ERR; } dlq_createSQue(&resnodeQ); walkerparms.resnodeQ = &resnodeQ; walkerparms.res = NO_ERR; walkerparms.callcount = 0; modname = (nsid) ? xmlns_get_module(nsid) : NULL; useroot = (pcb->flags & XP_FL_USEROOT) ? TRUE : FALSE; res = NO_ERR; position = 0; /* the resnodes need to be deleted or moved to a tempQ * to correctly track duplicates and remove them */ while (!dlq_empty(&result->r.nodeQ) && res == NO_ERR) { resnode = (xpath_resnode_t *)dlq_deque(&result->r.nodeQ); if (resnode->dblslash) { if (pcb->val) { testval = resnode->node.valptr; cfgonly = obj_is_config(testval->obj); fnresult = val_find_all_descendants(value_walker_fn, pcb, &walkerparms, testval, modname, name, cfgonly, FALSE, TRUE, FALSE); } else { testobj = resnode->node.objptr; cfgonly = obj_is_config(testobj); fnresult = obj_find_all_descendants(pcb->objmod, object_walker_fn, pcb, &walkerparms, testobj, modname, name, cfgonly, FALSE, useroot, TRUE, &fncalled); } if (!fnresult || walkerparms.res != NO_ERR) { res = walkerparms.res; } free_resnode(pcb, resnode); } else { keep = FALSE; if (pcb->val) { testval = resnode->node.valptr; if (testval == pcb->val_docroot) { if (resnode->dblslash) { /* just move this node to the result */ resnode->position = ++position; dlq_enque(resnode, &resnodeQ); } else { /* no parent available error * remove node from result */ no_parent_warning(pcb); free_resnode(pcb, resnode); } } else { testval = testval->parent; if (modname && name) { if (!xml_strcmp(modname, val_get_mod_name(testval)) && !xml_strcmp(name, testval->name)) { keep = TRUE; } } else if (modname) { if (!xml_strcmp(modname, val_get_mod_name(testval))) { keep = TRUE; } } else if (name) { if (!xml_strcmp(name, testval->name)) { keep = TRUE; } } else { keep = TRUE; } if (keep) { findnode = find_resnode(pcb, &resnodeQ, testval); if (findnode) { /* parent already in the Q * remove node from result */ if (resnode->dblslash) { findnode->position = ++position; findnode->dblslash = TRUE; } free_resnode(pcb, resnode); } else { /* set the resnode to its parent */ resnode->position = ++position; resnode->node.valptr = testval; dlq_enque(resnode, &resnodeQ); } } else { /* no parent available error * remove node from result */ no_parent_warning(pcb); free_resnode(pcb, resnode); } } } else { testobj = resnode->node.objptr; if (testobj == pcb->docroot) { resnode->position = ++position; dlq_enque(resnode, &resnodeQ); } else if (!testobj->parent) { if (!resnode->dblslash && (modname || name)) { no_parent_warning(pcb); free_resnode(pcb, resnode); } else { /* this is a databd node */ findnode = find_resnode(pcb, &resnodeQ, pcb->docroot); if (findnode) { if (resnode->dblslash) { findnode->position = ++position; findnode->dblslash = TRUE; } free_resnode(pcb, resnode); } else { resnode->position = ++position; resnode->node.objptr = pcb->docroot; dlq_enque(resnode, &resnodeQ); } } } else { /* find a parent but not a case or choice */ testobj = testobj->parent; while (testobj && testobj->parent && !obj_is_root(testobj->parent) && (testobj->objtype==OBJ_TYP_CHOICE || testobj->objtype==OBJ_TYP_CASE)) { testobj = testobj->parent; } /* check if stopped on top-level choice * a top-level case should not happen * but check it anyway */ if (testobj->objtype==OBJ_TYP_CHOICE || testobj->objtype==OBJ_TYP_CASE) { useobj = pcb->docroot; } else { useobj = testobj; } if (modname && name) { if (!xml_strcmp(modname, obj_get_mod_name(useobj)) && !xml_strcmp(name, obj_get_name(useobj))) { keep = TRUE; } } else if (modname) { if (!xml_strcmp(modname, obj_get_mod_name(useobj))) { keep = TRUE; } } else if (name) { if (!xml_strcmp(name, obj_get_name(useobj))) { keep = TRUE; } } else { keep = TRUE; } if (keep) { /* replace this node with the useobj */ findnode = find_resnode(pcb, &resnodeQ, useobj); if (findnode) { if (resnode->dblslash) { findnode->position = ++position; findnode->dblslash = TRUE; } free_resnode(pcb, resnode); } else { resnode->node.objptr = useobj; resnode->position = ++position; dlq_enque(resnode, &resnodeQ); } } else { no_parent_warning(pcb); free_resnode(pcb, resnode); } } } } } /* put the resnode entries back where they belong */ if (!dlq_empty(&resnodeQ)) { dlq_block_enque(&resnodeQ, &result->r.nodeQ); } result->last = (position) ? position : walkerparms.callcount; return res; } /* set_nodeset_parent */ /******************************************************************** * FUNCTION set_nodeset_child * * Check the current result nodeset and replace * each node with a node for every child instead * * Handles child and descendant nodes * * If a child is specified then any node not * containing this child will be removed * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block in progress * result == address of return XPath result nodeset * childnsid == 0 if any namespace is OK * else only this namespace will be checked * childname == name of child to find * == NULL to find all child nodes * In this mode, if the context object * is config=true, then config=false * children will be skipped * textmode == TRUE if just selecting text() nodes * FALSE if ignored * axis == actual axis used * * OUTPUTS: * result->nodeQ contents adjusted or replaced * * RETURNS: * status *********************************************************************/ static status_t set_nodeset_child (xpath_pcb_t *pcb, xpath_result_t *result, xmlns_id_t childnsid, const xmlChar *childname, boolean textmode, ncx_xpath_axis_t axis) { xpath_resnode_t *resnode; obj_template_t *testobj; val_value_t *testval; const xmlChar *modname; status_t res; boolean fnresult, fncalled, cfgonly; boolean orself, myorself, useroot; dlq_hdr_t resnodeQ; xpath_walkerparms_t walkerparms; if (!pcb->val && !pcb->obj) { return NO_ERR; } if (dlq_empty(&result->r.nodeQ)) { return NO_ERR; } dlq_createSQue(&resnodeQ); res = NO_ERR; cfgonly = (pcb->flags & XP_FL_CONFIGONLY) ? TRUE : FALSE; orself = (axis == XP_AX_DESCENDANT_OR_SELF) ? TRUE : FALSE; useroot = (pcb->flags & XP_FL_USEROOT) ? TRUE : FALSE; if (childnsid) { modname = xmlns_get_module(childnsid); } else { modname = NULL; } walkerparms.resnodeQ = &resnodeQ; walkerparms.res = NO_ERR; walkerparms.callcount = 0; /* the resnodes need to be deleted or moved to a tempQ * to correctly track duplicates and remove them */ while (!dlq_empty(&result->r.nodeQ) && res == NO_ERR) { resnode = (xpath_resnode_t *)dlq_deque(&result->r.nodeQ); myorself = (orself || resnode->dblslash) ? TRUE : FALSE; /* select 1 or all children of the resnode * special YANG support; skip over nodes that * are not config nodes if they were selected * by the wildcard '*' operator */ if (pcb->val) { testval = resnode->node.valptr; if (axis != XP_AX_CHILD || resnode->dblslash) { fnresult = val_find_all_descendants(value_walker_fn, pcb, &walkerparms, testval, modname, childname, cfgonly, textmode, myorself, resnode->dblslash); if (!fnresult || walkerparms.res != NO_ERR) { res = walkerparms.res; } } else { fnresult = val_find_all_children(value_walker_fn, pcb, &walkerparms, testval, modname, childname, cfgonly, textmode); if (!fnresult || walkerparms.res != NO_ERR) { res = walkerparms.res; } } } else { testobj = resnode->node.objptr; if (axis != XP_AX_CHILD || resnode->dblslash) { fnresult = obj_find_all_descendants(pcb->objmod, object_walker_fn, pcb, &walkerparms, testobj, modname, childname, cfgonly, textmode, useroot, myorself, &fncalled); if (!fnresult || walkerparms.res != NO_ERR) { res = walkerparms.res; } } else { fnresult = obj_find_all_children(pcb->objmod, object_walker_fn, pcb, &walkerparms, testobj, modname, childname, cfgonly, textmode, useroot); if (!fnresult || walkerparms.res != NO_ERR) { res = walkerparms.res; } } } free_resnode(pcb, resnode); } /* put the resnode entries back where they belong */ if (!dlq_empty(&resnodeQ)) { dlq_block_enque(&resnodeQ, &result->r.nodeQ); } else if (!pcb->val && pcb->obj) { if (pcb->logerrors) { /* hack: the val_gen_instance_id code will generate * instance IDs that start with /nc:rpc QName. * The node is currently unsupported in * node searches since all YANG content starts * from the RPC operation node; * check for the node and suppress the warning * if this is the node that is not found */ if ((childnsid == 0 || childnsid == xmlns_nc_id()) && !xml_strcmp(childname, NCX_EL_RPC)) { ; // assume this is an error-path XPath string } else if (axis != XP_AX_CHILD) { res = ERR_NCX_NO_XPATH_DESCENDANT; if (ncx_warning_enabled(res)) { log_warn("\nWarning: no descendant nodes found " "in XPath expr '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } else if (pcb->objmod) { ncx_inc_warnings(pcb->objmod); } } else { res = ERR_NCX_NO_XPATH_CHILD; if (ncx_warning_enabled(res)) { log_warn("\nWarning: no child nodes found " "in XPath expr '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } else if (pcb->objmod) { ncx_inc_warnings(pcb->objmod); } } res = NO_ERR; } } result->last = walkerparms.callcount; return res; } /* set_nodeset_child */ /******************************************************************** * FUNCTION set_nodeset_pfaxis * * Handles these axis node tests * * preceding::* * preceding-sibling::* * following::* * following-sibling::* * * Combinations with '//' are handled as well * * Check the current result nodeset and replace * each node with all the requested nodes instead, * based on the axis used * * The result set is changed to the selected axis * Since the current node is no longer going to * be part of the result set * * After this initial step, the desired filtering * is performed on each of the result nodes (if any) * * At this point, if a 'nsid' and/or 'name' is * specified then any selected node not * containing this namespace and/or node name * will be removed * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block in progress * result == address of return XPath result nodeset * nsid == 0 if any namespace is OK * else only this namespace will be checked * name == name of preceding node to find * == NULL to find all child nodes * In this mode, if the context object * is config=true, then config=false * preceding nodes will be skipped * textmode == TRUE if just selecting text() nodes * FALSE if ignored * axis == axis in use for this current step * * OUTPUTS: * result->nodeQ contents adjusted or replaced * * RETURNS: * status *********************************************************************/ static status_t set_nodeset_pfaxis (xpath_pcb_t *pcb, xpath_result_t *result, xmlns_id_t nsid, const xmlChar *name, boolean textmode, ncx_xpath_axis_t axis) { xpath_resnode_t *resnode; obj_template_t *testobj; val_value_t *testval; const xmlChar *modname; status_t res; boolean fnresult, fncalled, cfgonly, useroot; dlq_hdr_t resnodeQ; xpath_walkerparms_t walkerparms; if (!pcb->val && !pcb->obj) { return NO_ERR; } if (dlq_empty(&result->r.nodeQ)) { return NO_ERR; } dlq_createSQue(&resnodeQ); res = NO_ERR; cfgonly = (pcb->flags & XP_FL_CONFIGONLY) ? TRUE : FALSE; modname = (nsid) ? xmlns_get_module(nsid) : NULL; useroot = (pcb->flags & XP_FL_USEROOT) ? TRUE : FALSE; walkerparms.resnodeQ = &resnodeQ; walkerparms.res = NO_ERR; walkerparms.callcount = 0; /* the resnodes need to be deleted or moved to a tempQ * to correctly track duplicates and remove them */ while (!dlq_empty(&result->r.nodeQ) && res == NO_ERR) { resnode = (xpath_resnode_t *)dlq_deque(&result->r.nodeQ); /* select 1 or all children of the resnode * special YANG support; skip over nodes that * are not config nodes if they were selected * by the wildcard '*' operator */ if (pcb->val) { testval = resnode->node.valptr; if (axis==XP_AX_PRECEDING || axis==XP_AX_FOLLOWING) { fnresult = val_find_all_pfaxis(value_walker_fn, pcb, &walkerparms, testval, modname, name, cfgonly, resnode->dblslash, textmode, axis); } else { fnresult = val_find_all_pfsibling_axis(value_walker_fn, pcb, &walkerparms, testval, modname, name, cfgonly, resnode->dblslash, textmode, axis); } if (!fnresult || walkerparms.res != NO_ERR) { res = walkerparms.res; } } else { testobj = resnode->node.objptr; fnresult = obj_find_all_pfaxis(pcb->objmod, object_walker_fn, pcb, &walkerparms, testobj, modname, name, cfgonly, resnode->dblslash, textmode, useroot, axis, &fncalled); if (!fnresult || walkerparms.res != NO_ERR) { res = walkerparms.res; } } free_resnode(pcb, resnode); } /* put the resnode entries back where they belong */ if (!dlq_empty(&resnodeQ)) { dlq_block_enque(&resnodeQ, &result->r.nodeQ); } else if (!pcb->val && pcb->obj) { res = ERR_NCX_NO_XPATH_NODES; if (pcb->logerrors && ncx_warning_enabled(res)) { log_warn("\nWarning: no axis nodes found " "in XPath expr '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } else if (pcb->objmod != NULL) { ncx_inc_warnings(pcb->objmod); } res = NO_ERR; } result->last = walkerparms.callcount; return res; } /* set_nodeset_pfaxis */ /******************************************************************** * FUNCTION set_nodeset_ancestor * * Check the current result nodeset and move each * node to the specified ancestor, or remove it * if none found that matches the filter criteria * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * pcb == parser control block in progress * result == address of return XPath result nodeset * nsid == 0 if any namespace is OK * else only this namespace will be checked * name == name of ancestor to find * == NULL to find any ancestor nodes * In this mode, if the context object * is config=true, then config=false * children will be skipped * textmode == TRUE if just selecting text() nodes * FALSE if ignored * axis == axis in use for this current step * XP_AX_ANCESTOR or XP_AX_ANCESTOR_OR_SELF * * OUTPUTS: * result->nodeQ contents adjusted or removed * * RETURNS: * status *********************************************************************/ static status_t set_nodeset_ancestor (xpath_pcb_t *pcb, xpath_result_t *result, xmlns_id_t nsid, const xmlChar *name, boolean textmode, ncx_xpath_axis_t axis) { xpath_resnode_t *resnode, *testnode; obj_template_t *testobj; val_value_t *testval; const xmlChar *modname; xpath_result_t *dummy; status_t res; boolean cfgonly, fnresult, fncalled, orself, useroot; dlq_hdr_t resnodeQ; xpath_walkerparms_t walkerparms; if (!pcb->val && !pcb->obj) { return NO_ERR; } if (dlq_empty(&result->r.nodeQ)) { return NO_ERR; } dlq_createSQue(&resnodeQ); res = NO_ERR; testval = NULL; testobj = NULL; modname = (nsid) ? xmlns_get_module(nsid) : NULL; cfgonly = (pcb->flags & XP_FL_CONFIGONLY) ? TRUE : FALSE; useroot = (pcb->flags & XP_FL_USEROOT) ? TRUE : FALSE; orself = (axis == XP_AX_ANCESTOR_OR_SELF) ? TRUE : FALSE; walkerparms.resnodeQ = &resnodeQ; walkerparms.res = NO_ERR; /* the resnodes need to be deleted or moved to a tempQ * to correctly track duplicates and remove them */ while (!dlq_empty(&result->r.nodeQ) && res == NO_ERR) { resnode = (xpath_resnode_t *)dlq_deque(&result->r.nodeQ); walkerparms.callcount = 0; if (pcb->val) { testval = resnode->node.valptr; fnresult = val_find_all_ancestors(value_walker_fn, pcb, &walkerparms, testval, modname, name, cfgonly, orself, textmode); } else { testobj = resnode->node.objptr; fnresult = obj_find_all_ancestors(pcb->objmod, object_walker_fn, pcb, &walkerparms, testobj, modname, name, cfgonly, textmode, useroot, orself, &fncalled); } if (walkerparms.res != NO_ERR) { res = walkerparms.res; } else if (!fnresult) { res = ERR_NCX_OPERATION_FAILED; } else if (!walkerparms.callcount && resnode->dblslash) { dummy = new_result(pcb, XP_RT_NODESET); if (!dummy) { res = ERR_INTERNAL_MEM; continue; } walkerparms.resnodeQ = &dummy->r.nodeQ; if (pcb->val) { fnresult = val_find_all_descendants(value_walker_fn, pcb, &walkerparms, testval, modname, name, cfgonly, textmode, TRUE, FALSE); } else { fnresult = obj_find_all_descendants(pcb->objmod, object_walker_fn, pcb, &walkerparms, testobj, modname, name, cfgonly, textmode, useroot, TRUE, &fncalled); } walkerparms.resnodeQ = &resnodeQ; if (walkerparms.res != NO_ERR) { res = walkerparms.res; } else if (!fnresult) { res = ERR_NCX_OPERATION_FAILED; } else { res = set_nodeset_ancestor(pcb, dummy, nsid, name, textmode, axis); while (!dlq_empty(&dummy->r.nodeQ)) { testnode = (xpath_resnode_t *) dlq_deque(&dummy->r.nodeQ); /* It is assumed that testnode cannot NULL because the call * to dlq_empty returned false. */ if (find_resnode(pcb, &resnodeQ, (const void *)testnode->node.valptr)) { free_resnode(pcb, testnode); } else { dlq_enque(testnode, &resnodeQ); } } free_result(pcb, dummy); } } free_resnode(pcb, resnode); } /* put the resnode entries back where they belong */ if (!dlq_empty(&resnodeQ)) { dlq_block_enque(&resnodeQ, &result->r.nodeQ); } else { res = ERR_NCX_NO_XPATH_ANCESTOR; if (pcb->logerrors && ncx_warning_enabled(res)) { if (orself) { log_warn("\nWarning: no ancestor-or-self nodes found " "in XPath expr '%s'", pcb->exprstr); } else { log_warn("\nWarning: no ancestor nodes found " "in XPath expr '%s'", pcb->exprstr); } ncx_print_errormsg(pcb->tkc, pcb->objmod, res); } else if (pcb->objmod != NULL) { ncx_inc_warnings(pcb->objmod); } res = NO_ERR; } result->last = walkerparms.callcount; return res; } /* set_nodeset_ancestor */ /*********** B E G I N E B N F F U N C T I O N S *************/ /******************************************************************** * FUNCTION parse_node_test * * Parse the XPath NodeTest sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [7] NodeTest ::= NameTest * | NodeType '(' ')' * | 'processing-instruction' '(' Literal ')' * * [37] NameTest ::= '*' * | NCName ':' '*' * | QName * * [38] NodeType ::= 'comment' * | 'text' * | 'processing-instruction' * | 'node' * * INPUTS: * pcb == parser control block in progress * axis == current axis from first part of Step * result == address of pointer to result struct in progress * * OUTPUTS: * *result is modified * * RETURNS: * status *********************************************************************/ static status_t parse_node_test (xpath_pcb_t *pcb, ncx_xpath_axis_t axis, xpath_result_t **result) { const xmlChar *name; tk_type_t nexttyp; xpath_nodetype_t nodetyp; status_t res; xmlns_id_t nsid; boolean emptyresult, textmode; nsid = 0; name = NULL; textmode = FALSE; res = TK_ADV(pcb->tkc); if (res != NO_ERR) { res = ERR_NCX_INVALID_XPATH_EXPR; if (pcb->logerrors) { log_error("\nError: token expected in XPath " "expression '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, res); } else { /*** handle agent error ***/ } return res; } /* process the tokens but not the result yet */ switch (TK_CUR_TYP(pcb->tkc)) { case TK_TT_STAR: if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); return ERR_NCX_INVALID_INSTANCEID; } break; case TK_TT_NCNAME_STAR: if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); return ERR_NCX_INVALID_INSTANCEID; } /* match all nodes in the namespace w/ specified prefix */ if (!pcb->tkc->cur->nsid) { res = check_qname_prefix(pcb, TK_CUR_VAL(pcb->tkc), xml_strlen(TK_CUR_VAL(pcb->tkc)), &pcb->tkc->cur->nsid); } if (res == NO_ERR) { nsid = pcb->tkc->cur->nsid; } break; case TK_TT_MSTRING: /* match all nodes in the namespace w/ specified prefix */ if (!pcb->tkc->cur->nsid) { res = check_qname_prefix(pcb, TK_CUR_MOD(pcb->tkc), TK_CUR_MODLEN(pcb->tkc), &pcb->tkc->cur->nsid); } if (res == NO_ERR) { nsid = pcb->tkc->cur->nsid; name = TK_CUR_VAL(pcb->tkc); } break; case TK_TT_TSTRING: /* check the ID token for a NodeType name */ nodetyp = get_nodetype_id(TK_CUR_VAL(pcb->tkc)); if (nodetyp == XP_EXNT_NONE || (tk_next_typ(pcb->tkc) != TK_TT_LPAREN)) { name = TK_CUR_VAL(pcb->tkc); break; } /* get the node test left paren */ res = xpath_parse_token(pcb, TK_TT_LPAREN); if (res != NO_ERR) { return res; } /* check if a literal param can be present */ if (nodetyp == XP_EXNT_PROC_INST) { /* check if a literal param is present */ nexttyp = tk_next_typ(pcb->tkc); if (nexttyp==TK_TT_QSTRING || nexttyp==TK_TT_SQSTRING) { /* temp save the literal string */ res = xpath_parse_token(pcb, nexttyp); if (res != NO_ERR) { return res; } } } /* get the node test right paren */ res = xpath_parse_token(pcb, TK_TT_RPAREN); if (res != NO_ERR) { return res; } if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); return ERR_NCX_INVALID_INSTANCEID; } /* process the result based on the node type test */ switch (nodetyp) { case XP_EXNT_COMMENT: /* no comments to match */ emptyresult = TRUE; if (pcb->obj && pcb->logerrors && ncx_warning_enabled(ERR_NCX_EMPTY_XPATH_RESULT)) { log_warn("\nWarning: no comment nodes available in " "XPath expr '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, ERR_NCX_EMPTY_XPATH_RESULT); } else if (pcb->objmod != NULL) { ncx_inc_warnings(pcb->objmod); } break; case XP_EXNT_TEXT: /* match all leaf of leaf-list content */ emptyresult = FALSE; textmode = TRUE; break; case XP_EXNT_PROC_INST: /* no processing instructions to match */ emptyresult = TRUE; if (pcb->obj && pcb->logerrors && ncx_warning_enabled(ERR_NCX_EMPTY_XPATH_RESULT)) { log_warn("\nWarning: no processing instruction " "nodes available in " "XPath expr '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, ERR_NCX_EMPTY_XPATH_RESULT); } else if (pcb->objmod != NULL) { ncx_inc_warnings(pcb->objmod); } break; case XP_EXNT_NODE: /* match any node */ emptyresult = FALSE; break; default: emptyresult = TRUE; res = SET_ERROR(ERR_INTERNAL_VAL); } if (emptyresult) { if (*result) { free_result(pcb, *result); } *result = new_result(pcb, XP_RT_NODESET); if (!*result) { res = ERR_INTERNAL_MEM; } return res; } /* else go on to the text() or node() test */ break; default: /* wrong token type found */ res = ERR_NCX_WRONG_TKTYPE; unexpected_error(pcb); } /* do not care about result if fatal error occurred */ if (res != NO_ERR) { return res; } else if (!pcb->val && !pcb->obj) { /* nothing to do in first pass except create * dummy result to flag that a location step * has already started */ if (!*result) { *result = new_result(pcb, XP_RT_NODESET); if (!*result) { res = ERR_INTERNAL_MEM; } } return res; } /* if we get here, then the axis and name fields are set * or a texttest is needed */ switch (axis) { case XP_AX_ANCESTOR: case XP_AX_ANCESTOR_OR_SELF: if (!*result) { *result = new_nodeset(pcb, pcb->context.node.objptr, pcb->context.node.valptr, 1, FALSE); if (!*result) { res = ERR_INTERNAL_MEM; } } if (res == NO_ERR) { res = set_nodeset_ancestor(pcb, *result, nsid, name, textmode, axis); } break; case XP_AX_ATTRIBUTE: /* Attribute support in XPath is TBD * YANG does not define them and the ncx:metadata * extension is not fully supported within the * the edit-config code yet anyway * * just set the result to the empty nodeset */ if (pcb->obj && pcb->logerrors && ncx_warning_enabled(ERR_NCX_EMPTY_XPATH_RESULT)) { log_warn("\nWarning: attribute axis is empty in " "XPath expr '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, ERR_NCX_EMPTY_XPATH_RESULT); } else if (pcb->objmod != NULL) { ncx_inc_warnings(pcb->objmod); } if (*result) { free_result(pcb, *result); } *result = new_result(pcb, XP_RT_NODESET); if (!*result) { res = ERR_INTERNAL_MEM; } break; case XP_AX_DESCENDANT: case XP_AX_DESCENDANT_OR_SELF: case XP_AX_CHILD: /* select all the child nodes of each node in * the result node set. * ALSO select all the descendant nodes of each node in * the result node set. (they are the same in NETCONF) */ if (!*result) { /* first step is child::* or descendant::* */ *result = new_nodeset(pcb, pcb->context.node.objptr, pcb->context.node.valptr, 1, FALSE); if (!*result) { res = ERR_INTERNAL_MEM; } } if (res == NO_ERR) { res = set_nodeset_child(pcb, *result, nsid, name, textmode, axis); } break; case XP_AX_FOLLOWING: case XP_AX_PRECEDING: case XP_AX_FOLLOWING_SIBLING: case XP_AX_PRECEDING_SIBLING: /* need to set the result to all the objects * or all instances of all value nodes * preceding or following the context node */ if (!*result) { /* first step is following::* or preceding::* */ *result = new_nodeset(pcb, pcb->context.node.objptr, pcb->context.node.valptr, 1, FALSE); if (!*result) { res = ERR_INTERNAL_MEM; } } if (res == NO_ERR) { res = set_nodeset_pfaxis(pcb, *result, nsid, name, textmode, axis); } break; case XP_AX_NAMESPACE: /* for NETCONF purposes, there is no need to * provide access to namespace xmlns declarations * within the object or value tree * This can be added later! * * * For now, just turn the result into the empty set */ if (pcb->obj && pcb->logerrors && ncx_warning_enabled(ERR_NCX_EMPTY_XPATH_RESULT)) { log_warn("Warning: namespace axis is empty in " "XPath expr '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, ERR_NCX_EMPTY_XPATH_RESULT); } else if (pcb->objmod != NULL) { ncx_inc_warnings(pcb->objmod); } if (*result) { free_result(pcb, *result); } *result = new_result(pcb, XP_RT_NODESET); if (!*result) { res = ERR_INTERNAL_MEM; } break; case XP_AX_PARENT: /* step is parent::* -- same as .. for nodes */ if (textmode) { if (pcb->obj && pcb->logerrors && ncx_warning_enabled(ERR_NCX_EMPTY_XPATH_RESULT)) { log_warn("Warning: parent axis contains no text nodes in " "XPath expr '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, ERR_NCX_EMPTY_XPATH_RESULT); } else if (pcb->objmod != NULL) { ncx_inc_warnings(pcb->objmod); } if (*result) { free_result(pcb, *result); } *result = new_result(pcb, XP_RT_NODESET); if (!*result) { res = ERR_INTERNAL_MEM; } break; } if (!*result) { *result = new_nodeset(pcb, pcb->context.node.objptr, pcb->context.node.valptr, 1, FALSE); if (!*result) { res = ERR_INTERNAL_MEM; } } if (res == NO_ERR) { res = set_nodeset_parent(pcb, *result, nsid, name); } break; case XP_AX_SELF: /* keep the same context node */ if (!*result) { /* first step is self::* */ *result = new_nodeset(pcb, pcb->context.node.objptr, pcb->context.node.valptr, 1, FALSE); if (!*result) { res = ERR_INTERNAL_MEM; } } if (res == NO_ERR) { res = set_nodeset_self(pcb, *result, nsid, name, textmode); } break; case XP_AX_NONE: default: res = SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* parse_node_test */ /******************************************************************** * FUNCTION parse_predicate * * Parse an XPath Predicate sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [8] Predicate ::= '[' PredicateExpr ']' * [9] PredicateExpr ::= Expr * * INPUTS: * pcb == parser control block in progress * result == address of result in progress to filter * * OUTPUTS: * *result may be pruned based on filter matches * nodes may be updated if descendants are * checked and matches are found * * RETURNS: * malloced result of predicate expression *********************************************************************/ static status_t parse_predicate (xpath_pcb_t *pcb, xpath_result_t **result) { xpath_result_t *val1, *contextset; xpath_resnode_t lastcontext, *resnode, *nextnode; tk_token_t *leftbrack; boolean boo; status_t res; int64 position; res = xpath_parse_token(pcb, TK_TT_LBRACK); if (res != NO_ERR) { return res; } boo = FALSE; if (pcb->val || pcb->obj) { leftbrack = TK_CUR(pcb->tkc); contextset = *result; if (!contextset) { return SET_ERROR(ERR_INTERNAL_VAL); } else if (contextset->restype == XP_RT_NODESET) { if (dlq_empty(&contextset->r.nodeQ)) { /* always one pass; do not care about result */ val1 = parse_expr(pcb, &res); if (res == NO_ERR) { res = xpath_parse_token(pcb, TK_TT_RBRACK); } if (val1) { free_result(pcb, val1); } return res; } lastcontext.node.valptr = pcb->context.node.valptr; lastcontext.position = pcb->context.position; lastcontext.last = pcb->context.last; lastcontext.dblslash = pcb->context.dblslash; for (resnode = (xpath_resnode_t *) dlq_firstEntry(&contextset->r.nodeQ); resnode != NULL; resnode = nextnode) { /* go over and over the predicate expression * with the resnode as the current context node */ TK_CUR(pcb->tkc) = leftbrack; boo = FALSE; nextnode = (xpath_resnode_t *) dlq_nextEntry(resnode); pcb->context.node.valptr = resnode->node.valptr; pcb->context.position = resnode->position; pcb->context.last = contextset->last; pcb->context.dblslash = resnode->dblslash; #ifdef EXTRA_DEBUG if (LOGDEBUG3 && pcb->val) { log_debug3("\nXPath setting context node: %s", pcb->context.node.valptr->name); } #endif val1 = parse_expr(pcb, &res); if (res != NO_ERR) { if (val1) { free_result(pcb, val1); } return res; } res = xpath_parse_token(pcb, TK_TT_RBRACK); if (res != NO_ERR) { if (val1) { free_result(pcb, val1); } return res; } if (val1->restype == XP_RT_NUMBER) { /* the predicate specifies a context * position and this resnode is * only selected if it is the Nth * instance within the current context */ if (ncx_num_is_integral(&val1->r.num, NCX_BT_FLOAT64)) { position = ncx_cvt_to_int64(&val1->r.num, NCX_BT_FLOAT64); /* check if the proximity position * of this node matches the position * value from this expression */ boo = (position == resnode->position) ? TRUE : FALSE; } else { boo = FALSE; } } else { boo = xpath_cvt_boolean(val1); } free_result(pcb, val1); if (!boo) { /* predicate expression evaluated to false * so delete this resnode from the result */ dlq_remove(resnode); free_resnode(pcb, resnode); } } pcb->context.node.valptr = lastcontext.node.valptr; pcb->context.position = lastcontext.position; pcb->context.last = lastcontext.last; pcb->context.dblslash = lastcontext.dblslash; #ifdef EXTRA_DEBUG if (LOGDEBUG3 && pcb->val) { log_debug3("\nXPath set context node to last: %s", pcb->context.node.valptr->name); } #endif } else { /* result is from a primary expression and * is not a nodeset. It will get cleared * if the predicate evaluates to false */ val1 = parse_expr(pcb, &res); if (res != NO_ERR) { if (val1) { free_result(pcb, val1); } return res; } res = xpath_parse_token(pcb, TK_TT_RBRACK); if (res != NO_ERR) { if (val1) { free_result(pcb, val1); } return res; } if (val1 && pcb->val) { boo = xpath_cvt_boolean(val1); } if (val1) { free_result(pcb, val1); } if (pcb->val && !boo && *result) { xpath_clean_result(*result); xpath_init_result(*result, XP_RT_NONE); } } } else { /* always one pass; do not care about result */ val1 = parse_expr(pcb, &res); if (res == NO_ERR) { res = xpath_parse_token(pcb, TK_TT_RBRACK); } if (val1) { free_result(pcb, val1); } } return res; } /* parse_predicate */ /******************************************************************** * FUNCTION parse_step * * Parse the XPath Step sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [4] Step ::= AxisSpecifier NodeTest Predicate* * | AbbreviatedStep * * [12] AbbreviatedStep ::= '.' | '..' * * [5] AxisSpecifier ::= AxisName '::' * | AbbreviatedAxisSpecifier * * [13] AbbreviatedAxisSpecifier ::= '@'? * * INPUTS: * pcb == parser control block in progress * result == address of erturn XPath result nodeset * * OUTPUTS: * *result pointer is set to malloced result struct * if it is NULL, or used if it is non-NULL; * MUST be a XP_RT_NODESET result * * RETURNS: * status *********************************************************************/ static status_t parse_step (xpath_pcb_t *pcb, xpath_result_t **result) { const xmlChar *nextval; tk_type_t nexttyp, nexttyp2; ncx_xpath_axis_t axis; status_t res; nexttyp = tk_next_typ(pcb->tkc); axis = XP_AX_CHILD; /* check start token '/' or '//' */ if (nexttyp == TK_TT_DBLFSLASH) { res = xpath_parse_token(pcb, TK_TT_DBLFSLASH); if (res != NO_ERR) { return res; } if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); return ERR_NCX_INVALID_INSTANCEID; } if (!*result) { *result = new_nodeset(pcb, pcb->context.node.objptr, pcb->context.node.valptr, 1, TRUE); if (!*result) { return ERR_INTERNAL_MEM; } } set_nodeset_dblslash(pcb, *result); } else if (nexttyp == TK_TT_FSLASH) { res = xpath_parse_token(pcb, TK_TT_FSLASH); if (res != NO_ERR) { return res; } if (!*result) { /* this is the first call */ *result = new_nodeset(pcb, pcb->docroot, pcb->val_docroot, 1, FALSE); if (!*result) { return ERR_INTERNAL_MEM; } /* check corner-case path '/' */ if (location_path_end(pcb)) { /* exprstr is simply docroot '/' */ return NO_ERR; } } } else if (*result) { /* should not happen */ SET_ERROR(ERR_INTERNAL_VAL); return ERR_NCX_INVALID_XPATH_EXPR; } /* handle an abbreviated step (. or ..) or * handle the axis-specifier for the full form step */ nexttyp = tk_next_typ(pcb->tkc); switch (nexttyp) { case TK_TT_PERIOD: /* abbreviated step '.': * current target node stays the same unless * this is the first token in the location path, * then the context result needs to be initialized * * first consume the period token */ res = xpath_parse_token(pcb, TK_TT_PERIOD); if (res != NO_ERR) { return res; } /* first step is simply . */ if (!*result) { *result = new_nodeset(pcb, pcb->context.node.objptr, pcb->context.node.valptr, 1, FALSE); if (!*result) { return ERR_INTERNAL_MEM; } } /* else leave current result alone */ return NO_ERR; case TK_TT_RANGESEP: /* abbrev step '..': * matches parent of current context */ res = xpath_parse_token(pcb, TK_TT_RANGESEP); if (res != NO_ERR) { return res; } if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); return ERR_NCX_INVALID_INSTANCEID; } /* step is .. or //.. */ if (!*result) { /* first step is .. or //.. */ *result = new_nodeset(pcb, pcb->context.node.objptr, pcb->context.node.valptr, 1, FALSE); if (!*result) { res = ERR_INTERNAL_MEM; } } if (res == NO_ERR) { res = set_nodeset_parent(pcb, *result, 0, NULL); } return res; case TK_TT_ATSIGN: axis = XP_AX_ATTRIBUTE; res = xpath_parse_token(pcb, TK_TT_ATSIGN); if (res != NO_ERR) { return res; } if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); return ERR_NCX_INVALID_INSTANCEID; } break; case TK_TT_STAR: case TK_TT_NCNAME_STAR: case TK_TT_MSTRING: /* set the axis to default child, hit node test */ axis = XP_AX_CHILD; break; case TK_TT_TSTRING: /* check the ID token for an axis name */ nexttyp2 = tk_next_typ2(pcb->tkc); nextval = tk_next_val(pcb->tkc); axis = get_axis_id(nextval); if (axis != XP_AX_NONE && nexttyp2==TK_TT_DBLCOLON) { /* correct axis-name :: sequence */ res = xpath_parse_token(pcb, TK_TT_TSTRING); if (res != NO_ERR) { return res; } res = xpath_parse_token(pcb, TK_TT_DBLCOLON); if (res != NO_ERR) { return res; } if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); return ERR_NCX_INVALID_INSTANCEID; } } else if (axis == XP_AX_NONE && nexttyp2==TK_TT_DBLCOLON) { /* incorrect axis-name :: sequence */ (void)TK_ADV(pcb->tkc); res = ERR_NCX_INVALID_XPATH_EXPR; if (pcb->logerrors) { log_error("\nError: invalid axis name '%s' in " "XPath expression '%s'", (TK_CUR_VAL(pcb->tkc) != NULL) ? TK_CUR_VAL(pcb->tkc) : EMPTY_STRING, pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, res); } else { /*** log agent error ***/ } (void)TK_ADV(pcb->tkc); return res; } else { axis = XP_AX_CHILD; } break; default: /* wrong token type found */ (void)TK_ADV(pcb->tkc); res = ERR_NCX_WRONG_TKTYPE; unexpected_error(pcb); return res; } /* axis or default child parsed OK, get node test */ res = parse_node_test(pcb, axis, result); if (res == NO_ERR) { nexttyp = tk_next_typ(pcb->tkc); while (nexttyp == TK_TT_LBRACK && res==NO_ERR) { res = parse_predicate(pcb, result); nexttyp = tk_next_typ(pcb->tkc); } } return res; } /* parse_step */ /******************************************************************** * FUNCTION parse_location_path * * Parse the Location-Path sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [1] LocationPath ::= RelativeLocationPath * | AbsoluteLocationPath * * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath? * | AbbreviatedAbsoluteLocationPath * * [10] AbbreviatedAbsoluteLocationPath ::= '//' RelativeLocationPath * * [3] RelativeLocationPath ::= Step * | RelativeLocationPath '/' Step * | AbbreviatedRelativeLocationPath * * [11] AbbreviatedRelativeLocationPath ::= * RelativeLocationPath '//' Step * * INPUTS: * pcb == parser control block in progress * result == result struct in progress or NULL if none * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_location_path (xpath_pcb_t *pcb, xpath_result_t *result, status_t *res) { xpath_result_t *val1; tk_type_t nexttyp; boolean done; val1 = result; done = FALSE; while (!done && *res == NO_ERR) { *res = parse_step(pcb, &val1); if (*res == NO_ERR) { nexttyp = tk_next_typ(pcb->tkc); if (!(nexttyp == TK_TT_FSLASH || nexttyp == TK_TT_DBLFSLASH)) { done = TRUE; } } } return val1; } /* parse_location_path */ /******************************************************************** * FUNCTION parse_function_call * * Parse an XPath FunctionCall sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [16] FunctionCall ::= FunctionName * '(' ( Argument ( ',' Argument )* )? ')' * [17] Argument ::= Expr * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_function_call (xpath_pcb_t *pcb, status_t *res) { xpath_result_t *val1, *val2; const xpath_fncb_t *fncb; dlq_hdr_t parmQ; tk_type_t nexttyp; int32 parmcnt; boolean done; boolean has_null_arg; val1 = NULL; parmcnt = 0; dlq_createSQue(&parmQ); /* get the function name */ *res = xpath_parse_token(pcb, TK_TT_TSTRING); if (*res != NO_ERR) { return NULL; } if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); *res = ERR_NCX_INVALID_INSTANCEID; return NULL; } /* find the function in the library */ fncb = NULL; if (TK_CUR_VAL(pcb->tkc) != NULL) { fncb = find_fncb(pcb, TK_CUR_VAL(pcb->tkc)); } if (fncb) { /* get the mandatory left paren */ *res = xpath_parse_token(pcb, TK_TT_LPAREN); if (*res != NO_ERR) { return NULL; } /* get parms until a matching right paren is reached */ nexttyp = tk_next_typ(pcb->tkc); done = (nexttyp == TK_TT_RPAREN) ? TRUE : FALSE; has_null_arg=FALSE; while (!done && *res == NO_ERR) { val1 = parse_expr(pcb, res); if (*res == NO_ERR) { parmcnt++; if (val1) { dlq_enque(val1, &parmQ); val1 = NULL; } else { has_null_arg=TRUE; } /* check for right paren or else should be comma */ nexttyp = tk_next_typ(pcb->tkc); if (nexttyp == TK_TT_RPAREN) { done = TRUE; } else { *res = xpath_parse_token(pcb, TK_TT_COMMA); } } } /* get closing right paren */ if (*res == NO_ERR) { *res = xpath_parse_token(pcb, TK_TT_RPAREN); } /* check parameter count */ if (fncb->parmcnt >= 0 && fncb->parmcnt != parmcnt) { *res = (parmcnt > fncb->parmcnt) ? ERR_NCX_EXTRA_PARM : ERR_NCX_MISSING_PARM; if (pcb->logerrors) { log_error("\nError: wrong number of " "parameters got %d, need %d" " for function '%s'", parmcnt, fncb->parmcnt, fncb->name); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, *res); } else { /*** log agent error ***/ } } else { if(has_null_arg==FALSE) { /* make the function call */ val1 = (*fncb->fn)(pcb, &parmQ, res); } else { val1 = NULL; } if (LOGDEBUG3) { if (val1) { log_debug3("\nXPath fn %s result:", fncb->name); dump_result(pcb, val1, NULL); if (pcb->val && pcb->context.node.valptr->name) { log_debug3("\nXPath context val name: %s", pcb->context.node.valptr->name); } } } } } else { *res = ERR_NCX_UNKNOWN_PARM; if (pcb->logerrors) { log_error("\nError: Invalid XPath function name '%s'", (TK_CUR_VAL(pcb->tkc) != NULL) ? TK_CUR_VAL(pcb->tkc) : EMPTY_STRING); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, *res); } else { /*** log agent error ***/ } } /* clean up any function parameters */ while (!dlq_empty(&parmQ)) { val2 = (xpath_result_t *)dlq_deque(&parmQ); free_result(pcb, val2); } return val1; } /* parse_function_call */ /******************************************************************** * FUNCTION parse_primary_expr * * Parse an XPath PrimaryExpr sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [15] PrimaryExpr ::= VariableReference * | '(' Expr ')' * | Literal * | Number * | FunctionCall * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_primary_expr (xpath_pcb_t *pcb, status_t *res) { xpath_result_t *val1; ncx_var_t *varbind; const xmlChar *errstr; tk_type_t nexttyp; ncx_numfmt_t numfmt; val1 = NULL; nexttyp = tk_next_typ(pcb->tkc); switch (nexttyp) { case TK_TT_VARBIND: case TK_TT_QVARBIND: *res = xpath_parse_token(pcb, nexttyp); if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); *res = ERR_NCX_INVALID_INSTANCEID; return NULL; } /* get QName or NCName variable reference * but only if this get is a real one */ if (*res == NO_ERR && pcb->val) { if (TK_CUR_TYP(pcb->tkc) == TK_TT_VARBIND) { varbind = get_varbind(pcb, NULL, 0, TK_CUR_VAL(pcb->tkc), res); errstr = TK_CUR_VAL(pcb->tkc); } else { varbind = get_varbind(pcb, TK_CUR_MOD(pcb->tkc), TK_CUR_MODLEN(pcb->tkc), TK_CUR_VAL(pcb->tkc), res); errstr = TK_CUR_MOD(pcb->tkc); } if (!varbind || *res != NO_ERR) { if (pcb->logerrors) { if (*res == ERR_NCX_DEF_NOT_FOUND) { log_error("\nError: unknown variable binding '%s'", errstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, *res); } else { log_error("\nError: error in variable binding '%s'", errstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, *res); } } } else { /* OK: found the variable binding */ val1 = cvt_from_value(pcb, varbind->val); if (!val1) { *res = ERR_INTERNAL_MEM; } } } break; case TK_TT_LPAREN: /* get ( expr ) */ *res = xpath_parse_token(pcb, TK_TT_LPAREN); if (*res == NO_ERR) { if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); *res = ERR_NCX_INVALID_INSTANCEID; return NULL; } val1 = parse_expr(pcb, res); if (*res == NO_ERR) { *res = xpath_parse_token(pcb, TK_TT_RPAREN); } } break; case TK_TT_DNUM: case TK_TT_RNUM: *res = xpath_parse_token(pcb, nexttyp); if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); *res = ERR_NCX_INVALID_INSTANCEID; return NULL; } /* get the Number token */ if (*res == NO_ERR) { val1 = new_result(pcb, XP_RT_NUMBER); if (!val1) { *res = ERR_INTERNAL_MEM; return NULL; } numfmt = ncx_get_numfmt(TK_CUR_VAL(pcb->tkc)); if (numfmt == NCX_NF_OCTAL) { numfmt = NCX_NF_DEC; } if (numfmt == NCX_NF_DEC || numfmt == NCX_NF_REAL) { *res = ncx_convert_num(TK_CUR_VAL(pcb->tkc), numfmt, NCX_BT_FLOAT64, &val1->r.num); } else if (numfmt == NCX_NF_NONE) { *res = ERR_NCX_INVALID_VALUE; } else { *res = ERR_NCX_WRONG_NUMTYP; } } break; case TK_TT_QSTRING: /* double quoted string */ case TK_TT_SQSTRING: /* single quoted string */ /* get the literal token */ *res = xpath_parse_token(pcb, nexttyp); if (*res == NO_ERR) { val1 = new_result(pcb, XP_RT_STRING); if (!val1) { *res = ERR_INTERNAL_MEM; return NULL; } if (TK_CUR_VAL(pcb->tkc) != NULL) { val1->r.str = xml_strdup(TK_CUR_VAL(pcb->tkc)); } else { val1->r.str = xml_strdup(EMPTY_STRING); } if (!val1->r.str) { *res = ERR_INTERNAL_MEM; malloc_failed_error(pcb); xpath_free_result(val1); val1 = NULL; } } break; case TK_TT_TSTRING: /* NCName string */ /* get the string ID token */ nexttyp = tk_next_typ2(pcb->tkc); if (nexttyp == TK_TT_LPAREN) { val1 = parse_function_call(pcb, res); } else { *res = SET_ERROR(ERR_INTERNAL_VAL); } break; case TK_TT_NONE: /* unexpected end of token chain */ (void)TK_ADV(pcb->tkc); *res = ERR_NCX_INVALID_XPATH_EXPR; if (pcb->logerrors) { log_error("\nError: token expected in XPath expression '%s'", pcb->exprstr); /* hack to get correct error token to print */ pcb->tkc->curerr = &pcb->tkerr; ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, *res); } else { ; /**** log agent error ****/ } break; default: /* unexpected token error */ (void)TK_ADV(pcb->tkc); *res = ERR_NCX_WRONG_TKTYPE; unexpected_error(pcb); } return val1; } /* parse_primary_expr */ /******************************************************************** * FUNCTION parse_filter_expr * * Parse an XPath FilterExpr sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [20] FilterExpr ::= PrimaryExpr * | FilterExpr Predicate * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_filter_expr (xpath_pcb_t *pcb, status_t *res) { xpath_result_t *val1, *dummy; tk_type_t nexttyp; val1 = parse_primary_expr(pcb, res); if (*res != NO_ERR) { return val1; } /* peek ahead to check the possible next chars */ nexttyp = tk_next_typ(pcb->tkc); if (val1) { while (nexttyp == TK_TT_LBRACK && *res==NO_ERR) { *res = parse_predicate(pcb, &val1); nexttyp = tk_next_typ(pcb->tkc); } } else if (nexttyp==TK_TT_LBRACK) { dummy = new_result(pcb, XP_RT_NODESET); if (dummy) { while (nexttyp == TK_TT_LBRACK && *res==NO_ERR) { *res = parse_predicate(pcb, &dummy); nexttyp = tk_next_typ(pcb->tkc); } free_result(pcb, dummy); } else { *res = ERR_INTERNAL_MEM; return NULL; } } return val1; } /* parse_filter_expr */ /******************************************************************** * FUNCTION parse_path_expr * * Parse an XPath PathExpr sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [19] PathExpr ::= LocationPath * | FilterExpr * | FilterExpr '/' RelativeLocationPath * | FilterExpr '//' RelativeLocationPath * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_path_expr (xpath_pcb_t *pcb, status_t *res) { xpath_result_t *val1, *val2; const xmlChar *nextval; tk_type_t nexttyp, nexttyp2; xpath_exop_t curop; val1 = NULL; val2 = NULL; /* peek ahead to check the possible next sequence */ nexttyp = tk_next_typ(pcb->tkc); switch (nexttyp) { case TK_TT_FSLASH: /* abs location path */ case TK_TT_DBLFSLASH: /* abbrev. abs location path */ case TK_TT_PERIOD: /* abbrev step */ case TK_TT_RANGESEP: /* abbrev step */ case TK_TT_ATSIGN: /* abbrev axis name */ case TK_TT_STAR: /* rel, step, node, name */ case TK_TT_NCNAME_STAR: /* rel, step, node, name */ case TK_TT_MSTRING: /* rel, step, node, QName */ return parse_location_path(pcb, NULL, res); case TK_TT_TSTRING: /* some sort of identifier string to check * get the value of the string and the following token type */ nexttyp2 = tk_next_typ2(pcb->tkc); nextval = tk_next_val(pcb->tkc); /* check 'axis-name ::' sequence */ if (nexttyp2==TK_TT_DBLCOLON && get_axis_id(nextval)) { /* this is an axis name */ return parse_location_path(pcb, NULL, res); } /* check 'NodeType (' sequence */ if (nexttyp2==TK_TT_LPAREN && get_nodetype_id(nextval)) { /* this is an nodetype name */ return parse_location_path(pcb, NULL, res); } /* check not a function call, so must be a QName */ if (nexttyp2 != TK_TT_LPAREN) { /* this is an NameTest QName w/o a prefix */ return parse_location_path(pcb, NULL, res); } break; default: ; } /* if we get here, then a filter expression is expected */ val1 = parse_filter_expr(pcb, res); if (*res == NO_ERR) { nexttyp = tk_next_typ(pcb->tkc); switch (nexttyp) { case TK_TT_FSLASH: curop = XP_EXOP_FILTER1; break; case TK_TT_DBLFSLASH: curop = XP_EXOP_FILTER2; break; default: curop = XP_EXOP_NONE; } if (curop != XP_EXOP_NONE) { val2 = parse_location_path(pcb, val1, res); } else { val2 = val1; } val1 = NULL; } if (val1) { free_result(pcb, val1); } return val2; } /* parse_path_expr */ /******************************************************************** * FUNCTION parse_union_expr * * Parse an XPath UnionExpr sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [18] UnionExpr ::= PathExpr * | UnionExpr '|' PathExpr * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_union_expr (xpath_pcb_t *pcb, status_t *res) { xpath_result_t *val1, *val2; boolean done; tk_type_t nexttyp; val1 = NULL; val2 = NULL; done = FALSE; while (!done && *res == NO_ERR) { val1 = parse_path_expr(pcb, res); if (*res == NO_ERR) { if (val2) { if (pcb->val || pcb->obj) { /* add all the nodes from val1 into val2 * that are not already present */ merge_nodeset(pcb, val1, val2); if (val1) { free_result(pcb, val1); val1 = NULL; } } } else { val2 = val1; val1 = NULL; } if (*res != NO_ERR) { continue; } nexttyp = tk_next_typ(pcb->tkc); if (nexttyp != TK_TT_BAR) { done = TRUE; } else { *res = xpath_parse_token(pcb, TK_TT_BAR); if (*res == NO_ERR) { if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); *res = ERR_NCX_INVALID_INSTANCEID; } } } } } if (val1) { free_result(pcb, val1); } return val2; } /* parse_union_expr */ /******************************************************************** * FUNCTION parse_unary_expr * * Parse an XPath UnaryExpr sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [27] UnaryExpr ::= UnionExpr * | '-' UnaryExpr * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_unary_expr (xpath_pcb_t *pcb, status_t *res) { xpath_result_t *val1, *result; tk_type_t nexttyp; uint32 minuscnt; val1 = NULL; minuscnt = 0; nexttyp = tk_next_typ(pcb->tkc); while (nexttyp == TK_TT_MINUS) { *res = xpath_parse_token(pcb, TK_TT_MINUS); if (*res != NO_ERR) { return NULL; } else { nexttyp = tk_next_typ(pcb->tkc); minuscnt++; } } val1 = parse_union_expr(pcb, res); if (*res == NO_ERR && (minuscnt & 1)) { if (pcb->val || pcb->obj) { /* odd number of negate ops requested */ if (val1->restype == XP_RT_NUMBER) { val1->r.num.d *= -1; return val1; } else { result = new_result(pcb, XP_RT_NUMBER); if (!result) { *res = ERR_INTERNAL_MEM; return NULL; } xpath_cvt_number(val1, &result->r.num); result->r.num.d *= -1; free_result(pcb, val1); return result; } } } return val1; } /* parse_unary_expr */ /******************************************************************** * FUNCTION parse_multiplicative_expr * * Parse an XPath MultiplicativeExpr sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [26] MultiplicativeExpr ::= UnaryExpr * | MultiplicativeExpr MultiplyOperator UnaryExpr * | MultiplicativeExpr 'div' UnaryExpr * | MultiplicativeExpr 'mod' UnaryExpr * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_multiplicative_expr (xpath_pcb_t *pcb, status_t *res) { xpath_result_t *val1, *val2, *result; ncx_num_t num1, num2; xpath_exop_t curop; boolean done; tk_type_t nexttyp; val1 = NULL; val2 = NULL; result = NULL; curop = XP_EXOP_NONE; done = FALSE; ncx_init_num(&num1); ncx_init_num(&num2); while (!done && *res == NO_ERR) { val1 = parse_unary_expr(pcb, res); if (*res == NO_ERR) { if (val2) { if (pcb->val || pcb->obj) { /* val2 holds the 1st operand * val1 holds the 2nd operand */ if (val1->restype != XP_RT_NUMBER) { xpath_cvt_number(val1, &num1); } else { *res = ncx_copy_num(&val1->r.num, &num1, NCX_BT_FLOAT64); } if (val2->restype != XP_RT_NUMBER) { xpath_cvt_number(val2, &num2); } else { *res = ncx_copy_num(&val2->r.num, &num2, NCX_BT_FLOAT64); } if (*res == NO_ERR) { result = new_result(pcb, XP_RT_NUMBER); if (!result) { *res = ERR_INTERNAL_MEM; } else { switch (curop) { case XP_EXOP_MULTIPLY: result->r.num.d = num2.d * num1.d; break; case XP_EXOP_DIV: if (ncx_num_zero(&num2, NCX_BT_FLOAT64)) { ncx_set_num_max(&result->r.num, NCX_BT_FLOAT64); } else { result->r.num.d = num2.d / num1.d; } break; case XP_EXOP_MOD: result->r.num.d = num2.d / num1.d; #ifdef HAS_FLOAT result->r.num.d = trunc(result->r.num.d); #endif break; default: *res = SET_ERROR(ERR_INTERNAL_VAL); } } } } if (val1) { free_result(pcb, val1); val1 = NULL; } if (val2) { free_result(pcb, val2); val2 = NULL; } if (result) { val2 = result; result = NULL; } } else { val2 = val1; val1 = NULL; } if (*res != NO_ERR) { continue; } nexttyp = tk_next_typ(pcb->tkc); switch (nexttyp) { case TK_TT_STAR: curop = XP_EXOP_MULTIPLY; break; case TK_TT_TSTRING: if (match_next_token(pcb, TK_TT_TSTRING, XP_OP_DIV)) { curop = XP_EXOP_DIV; } else if (match_next_token(pcb, TK_TT_TSTRING, XP_OP_MOD)) { curop = XP_EXOP_MOD; } else { done = TRUE; } break; default: done = TRUE; } if (!done) { *res = xpath_parse_token(pcb, nexttyp); if (*res == NO_ERR) { if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); *res = ERR_NCX_INVALID_INSTANCEID; } } } } } if (val1) { free_result(pcb, val1); } ncx_clean_num(NCX_BT_FLOAT64, &num1); ncx_clean_num(NCX_BT_FLOAT64, &num2); return val2; } /* parse_multiplicative_expr */ /******************************************************************** * FUNCTION parse_additive_expr * * Parse an XPath AdditiveExpr sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [25] AdditiveExpr ::= MultiplicativeExpr * | AdditiveExpr '+' MultiplicativeExpr * | AdditiveExpr '-' MultiplicativeExpr * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_additive_expr (xpath_pcb_t *pcb, status_t *res) { xpath_result_t *val1, *val2, *result; ncx_num_t num1, num2; xpath_exop_t curop; boolean done; tk_type_t nexttyp; val1 = NULL; val2 = NULL; result = NULL; curop = XP_EXOP_NONE; done = FALSE; while (!done && *res == NO_ERR) { val1 = parse_multiplicative_expr(pcb, res); if (*res == NO_ERR) { if (val2) { /* val2 holds the 1st operand * val1 holds the 2nd operand */ if (pcb->val || pcb->val) { if (val1->restype != XP_RT_NUMBER) { xpath_cvt_number(val1, &num1); } else { *res = ncx_copy_num(&val1->r.num, &num1, NCX_BT_FLOAT64); } if (val2->restype != XP_RT_NUMBER) { xpath_cvt_number(val2, &num2); } else { *res = ncx_copy_num(&val2->r.num, &num2, NCX_BT_FLOAT64); } if (*res == NO_ERR) { result = new_result(pcb, XP_RT_NUMBER); if (!result) { *res = ERR_INTERNAL_MEM; } else { switch (curop) { case XP_EXOP_ADD: result->r.num.d = num2.d + num1.d; break; case XP_EXOP_SUBTRACT: result->r.num.d = num2.d - num1.d; break; default: *res = SET_ERROR(ERR_INTERNAL_VAL); } } } } if (val1) { free_result(pcb, val1); val1 = NULL; } if (val2) { free_result(pcb, val2); val2 = NULL; } if (result) { val2 = result; result = NULL; } } else { val2 = val1; val1 = NULL; } if (*res != NO_ERR) { continue; } nexttyp = tk_next_typ(pcb->tkc); switch (nexttyp) { case TK_TT_PLUS: curop = XP_EXOP_ADD; break; case TK_TT_MINUS: curop = XP_EXOP_SUBTRACT; break; default: done = TRUE; } if (!done) { *res = xpath_parse_token(pcb, nexttyp); if (*res == NO_ERR) { if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); *res = ERR_NCX_INVALID_INSTANCEID; } } } } } if (val1) { free_result(pcb, val1); } ncx_clean_num(NCX_BT_FLOAT64, &num1); ncx_clean_num(NCX_BT_FLOAT64, &num2); return val2; } /* parse_additive_expr */ /******************************************************************** * FUNCTION parse_relational_expr * * Parse an XPath RelationalExpr sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [24] RelationalExpr ::= AdditiveExpr * | RelationalExpr '<' AdditiveExpr * | RelationalExpr '>' AdditiveExpr * | RelationalExpr '<=' AdditiveExpr * | RelationalExpr '>=' AdditiveExpr * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_relational_expr (xpath_pcb_t *pcb, status_t *res) { xpath_result_t *val1, *val2, *result; xpath_exop_t curop; boolean done, cmpresult; tk_type_t nexttyp; val1 = NULL; val2 = NULL; result = NULL; curop = XP_EXOP_NONE; done = FALSE; while (!done && *res == NO_ERR) { val1 = parse_additive_expr(pcb, res); if (*res == NO_ERR) { if (val2) { if (pcb->val || pcb->obj) { /* val2 holds the 1st operand * val1 holds the 2nd operand */ cmpresult = compare_results(pcb, val2, val1, curop, res); if (*res == NO_ERR) { result = new_result(pcb, XP_RT_BOOLEAN); if (!result) { *res = ERR_INTERNAL_MEM; } else { result->r.boo = cmpresult; } } } if (val1) { free_result(pcb, val1); val1 = NULL; } if (val2) { free_result(pcb, val2); val2 = NULL; } if (result) { val2 = result; result = NULL; } } else { val2 = val1; val1 = NULL; } if (*res != NO_ERR) { continue; } nexttyp = tk_next_typ(pcb->tkc); switch (nexttyp) { case TK_TT_LT: curop = XP_EXOP_LT; break; case TK_TT_GT: curop = XP_EXOP_GT; break; case TK_TT_LEQUAL: curop = XP_EXOP_LEQUAL; break; case TK_TT_GEQUAL: curop = XP_EXOP_GEQUAL; break; default: done = TRUE; } if (!done) { *res = xpath_parse_token(pcb, nexttyp); if (*res == NO_ERR) { if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); *res = ERR_NCX_INVALID_INSTANCEID; } } } } } if (val1) { free_result(pcb, val1); } return val2; } /* parse_relational_expr */ /******************************************************************** * FUNCTION parse_equality_expr * * Parse an XPath EqualityExpr sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [23] EqualityExpr ::= RelationalExpr * | EqualityExpr '=' RelationalExpr * | EqualityExpr '!=' RelationalExpr * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_equality_expr (xpath_pcb_t *pcb, status_t *res) { xpath_result_t *val1, *val2, *result; xpath_exop_t curop; boolean done, cmpresult, equalsdone; val1 = NULL; val2 = NULL; result = NULL; curop = XP_EXOP_NONE; equalsdone = FALSE; done = FALSE; while (!done && *res == NO_ERR) { val1 = parse_relational_expr(pcb, res); if (*res == NO_ERR) { if (val2) { if (pcb->val || pcb->obj) { /* val2 holds the 1st operand * val1 holds the 2nd operand */ if (pcb->flags & XP_FL_INSTANCEID) { *res = check_instanceid_expr(pcb, val2, val1); } if (*res == NO_ERR) { cmpresult = compare_results(pcb, val2, val1, curop, res); if (*res == NO_ERR) { result = new_result(pcb, XP_RT_BOOLEAN); if (!result) { *res = ERR_INTERNAL_MEM; } else { result->r.boo = cmpresult; } } } } if (val1) { free_result(pcb, val1); val1 = NULL; } if (val2) { free_result(pcb, val2); val2 = NULL; } if (result) { val2 = result; result = NULL; } } else { val2 = val1; val1 = NULL; } if (*res != NO_ERR) { continue; } if (match_next_token(pcb, TK_TT_EQUAL, NULL)) { *res = xpath_parse_token(pcb, TK_TT_EQUAL); if (*res == NO_ERR) { if (equalsdone) { if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); *res = ERR_NCX_INVALID_INSTANCEID; } } else { equalsdone = TRUE; } } curop = XP_EXOP_EQUAL; } else if (match_next_token(pcb, TK_TT_NOTEQUAL, NULL)) { *res = xpath_parse_token(pcb, TK_TT_NOTEQUAL); if (*res == NO_ERR) { if (pcb->flags & XP_FL_INSTANCEID) { invalid_instanceid_error(pcb); *res = ERR_NCX_INVALID_INSTANCEID; } } curop = XP_EXOP_NOTEQUAL; } else { done = TRUE; } } } if (val1) { free_result(pcb, val1); } return val2; } /* parse_equality_expr */ /******************************************************************** * FUNCTION parse_and_expr * * Parse an XPath AndExpr sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [22] AndExpr ::= EqualityExpr * | AndExpr 'and' EqualityExpr * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_and_expr (xpath_pcb_t *pcb, status_t *res) { xpath_result_t *val1, *val2, *result; boolean done, bool1, bool2; val1 = NULL; val2 = NULL; result = NULL; done = FALSE; while (!done && *res == NO_ERR) { val1 = parse_equality_expr(pcb, res); if (*res == NO_ERR) { if (val2) { if (pcb->val || pcb->obj) { /* val2 holds the 1st operand * val1 holds the 2nd operand */ bool1 = xpath_cvt_boolean(val1); bool2 = xpath_cvt_boolean(val2); result = new_result(pcb, XP_RT_BOOLEAN); if (!result) { *res = ERR_INTERNAL_MEM; } else { result->r.boo = (bool1 && bool2) ? TRUE : FALSE; } } if (val1) { free_result(pcb, val1); val1 = NULL; } if (val2) { free_result(pcb, val2); val2 = NULL; } if (result) { val2 = result; result = NULL; } } else { val2 = val1; val1 = NULL; } if (*res != NO_ERR) { continue; } if (match_next_token(pcb, TK_TT_TSTRING, XP_OP_AND)) { *res = xpath_parse_token(pcb, TK_TT_TSTRING); } else { done = TRUE; } } } if (val1) { free_result(pcb, val1); } return val2; } /* parse_and_expr */ /******************************************************************** * FUNCTION parse_or_expr * * Parse an XPath OrExpr sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [21] OrExpr ::= AndExpr * | OrExpr 'or' AndExpr * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_or_expr (xpath_pcb_t *pcb, status_t *res) { xpath_result_t *val1, *val2, *result; boolean done, bool1, bool2; val1 = NULL; val2 = NULL; result = NULL; done = FALSE; while (!done && *res == NO_ERR) { val1 = parse_and_expr(pcb, res); if (*res == NO_ERR) { if (val2) { if (pcb->val || pcb->obj) { /* val2 holds the 1st operand * val1 holds the 2nd operand */ bool1 = xpath_cvt_boolean(val1); bool2 = xpath_cvt_boolean(val2); result = new_result(pcb, XP_RT_BOOLEAN); if (!result) { *res = ERR_INTERNAL_MEM; } else { result->r.boo = (bool1 || bool2) ? TRUE : FALSE; } } if (val1) { free_result(pcb, val1); val1 = NULL; } if (val2) { free_result(pcb, val2); val2 = NULL; } if (result) { val2 = result; result = NULL; } } else { val2 = val1; val1 = NULL; } if (*res != NO_ERR) { continue; } if (match_next_token(pcb, TK_TT_TSTRING, XP_OP_OR)) { *res = xpath_parse_token(pcb, TK_TT_TSTRING); } else { done = TRUE; } } } if (val1) { free_result(pcb, val1); } return val2; } /* parse_or_expr */ /******************************************************************** * FUNCTION parse_expr * * Parse an XPath Expr sequence * It has already been tokenized * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * [14] Expr ::= OrExpr * * INPUTS: * pcb == parser control block in progress * res == address of result status * * OUTPUTS: * *res == function result status * * RETURNS: * pointer to malloced result struct or NULL if no * result processing in effect *********************************************************************/ static xpath_result_t * parse_expr (xpath_pcb_t *pcb, status_t *res) { return parse_or_expr(pcb, res); } /* parse_expr */ /******************************************************************** * FUNCTION get_context_objnode * * Get the correct context node according to YANG rules * * INPUTS: * obj == object to check * * RETURNS: * pointer to context object to use (probably obj) *********************************************************************/ static obj_template_t * get_context_objnode (obj_template_t *obj) { boolean done; /* get the correct context node to use */ done = FALSE; while (!done) { if (obj->objtype == OBJ_TYP_CHOICE || obj->objtype == OBJ_TYP_CASE || obj->objtype == OBJ_TYP_USES) { if (obj->parent) { obj = obj->parent; } else { SET_ERROR(ERR_INTERNAL_VAL); return obj; } } else { done = TRUE; } } return obj; } /* get_context_objnode */ /************ E X T E R N A L F U N C T I O N S ************/ /******************************************************************** * FUNCTION xpath1_parse_expr * * Parse the XPATH 1.0 expression string. * * parse initial expr with YANG prefixes: must/when * the object is left out in case it is in a grouping * * This is just a first pass done when the * XPath string is consumed. If this is a * YANG file source then the prefixes will be * checked against the 'mod' import Q * * The expression is parsed into XPath tokens * and checked for well-formed syntax and function * invocations. Any variable * * If the source is XP_SRC_INSTANCEID, then YANG * instance-identifier syntax is followed, not XPath 1.0 * This is only used by instance-identifiers in * default-stmt, conf file, CLI, etc. * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * * INPUTS: * tkc == parent token chain * mod == module in progress * pcb == initialized xpath parser control block * for the expression; use xpath_new_pcb * to initialize before calling this fn * The pcb->exprstr MUST BE SET BEFORE THIS CALL * source == enum indicating source of this expression * * OUTPUTS: * pcb->tkc is filled and then partially validated * pcb->parseres is set * * RETURNS: * status *********************************************************************/ status_t xpath1_parse_expr (tk_chain_t *tkc, ncx_module_t *mod, xpath_pcb_t *pcb, xpath_source_t source) { xpath_result_t *result; status_t res; uint32 linenum, linepos; #ifdef DEBUG if (!pcb) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (tkc && tkc->cur) { linenum = TK_CUR_LNUM(tkc); linepos = TK_CUR_LPOS(tkc); } else { linenum = 1; linepos = 1; } if (pcb->tkc) { tk_reset_chain(pcb->tkc); } else { pcb->tkc = tk_tokenize_xpath_string(mod, pcb->exprstr, linenum, linepos, &res); if (!pcb->tkc || res != NO_ERR) { log_error("\nError: Invalid XPath string '%s'", pcb->exprstr); ncx_print_errormsg(tkc, mod, res); return res; } } /* the module that contains the XPath expr is the one * that will always be used to resolve prefixes */ pcb->tkerr.mod = mod; pcb->source = source; pcb->logerrors = TRUE; pcb->obj = NULL; pcb->objmod = NULL; pcb->docroot = NULL; pcb->doctype = XP_DOC_NONE; pcb->val = NULL; pcb->val_docroot = NULL; pcb->context.node.objptr = NULL; pcb->orig_context.node.objptr = NULL; pcb->context.node.valptr = NULL; pcb->orig_context.node.valptr = NULL; pcb->parseres = NO_ERR; if (pcb->source == XP_SRC_INSTANCEID) { pcb->flags |= XP_FL_INSTANCEID; result = parse_location_path(pcb, NULL, &pcb->parseres); } else { result = parse_expr(pcb, &pcb->parseres); } /* since the pcb->obj is not set, this validation * phase will skip identifier tests, predicate tests * and completeness tests */ if (result) { free_result(pcb, result); } if (pcb->parseres == NO_ERR && pcb->tkc->cur) { res = TK_ADV(pcb->tkc); if (res == NO_ERR) { /* should not get more tokens at this point * have something like '7 + 4 4 + 7' * and the parser stopped after the first complete * expression */ if (pcb->source == XP_SRC_INSTANCEID) { pcb->parseres = ERR_NCX_INVALID_INSTANCEID; if (pcb->logerrors) { log_error("\nError: extra tokens in " "instance-identifier '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, pcb->parseres); } } else { pcb->parseres = ERR_NCX_INVALID_XPATH_EXPR; if (pcb->logerrors) { log_error("\nError: extra tokens in " "XPath expression '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, pcb->parseres); } } } } #ifdef XPATH1_PARSE_DEBUG if (LOGDEBUG3 && pcb->tkc) { log_debug3("\n\nParse chain for XPath '%s':\n", pcb->exprstr); tk_dump_chain(pcb->tkc); log_debug3("\n"); } #endif /* the expression will not be processed further if the * parseres is other than NO_ERR */ return pcb->parseres; } /* xpath1_parse_expr */ /******************************************************************** * FUNCTION xpath1_validate_expr_ex * * Validate the previously parsed expression string * - QName prefixes are valid * - function calls are well-formed and exist in * the pcb->functions array * - variable references exist in the &pcb->varbindQ * * parse expr with YANG prefixes: must/when * called from final OBJ xpath check after all * cooked objects are in place * * Called after all 'uses' and 'augment' expansion * so validation against cooked object tree can be done * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * mod == module containing the 'obj' (in progress) * obj == object containing the XPath clause * pcb == the XPath parser control block to process * missing_is_error == TRUE if a missing node is an error * == FALSE if a warning * OUTPUTS: * pcb->obj and pcb->objmod are set * pcb->validateres is set * * RETURNS: * status *********************************************************************/ status_t xpath1_validate_expr_ex (ncx_module_t *mod, obj_template_t *obj, xpath_pcb_t *pcb, boolean missing_is_error) { xpath_result_t *result; obj_template_t *rootobj; boolean rootdone; #ifdef DEBUG if (!mod || !obj || !pcb) { return SET_ERROR(ERR_INTERNAL_PTR); } if (!pcb->tkc) { return SET_ERROR(ERR_INTERNAL_VAL); } #endif pcb->objmod = mod; pcb->obj = obj; pcb->logerrors = TRUE; pcb->val = NULL; pcb->val_docroot = NULL; /* this is not used yet; a missing child node is always * a warning at this time */ pcb->missing_errors = missing_is_error; if (pcb->source == XP_SRC_YANG && obj_is_config(obj)) { pcb->flags |= XP_FL_CONFIGONLY; } if (pcb->parseres != NO_ERR) { /* errors already reported, skip this one */ return NO_ERR; } if (pcb->tkc) { tk_reset_chain(pcb->tkc); } else { return SET_ERROR(ERR_INTERNAL_VAL); } pcb->context.node.objptr = get_context_objnode(obj); pcb->orig_context.node.objptr = pcb->context.node.objptr; pcb->context.node.valptr = NULL; pcb->orig_context.node.valptr = NULL; rootdone = FALSE; if (obj_is_root(obj) || obj_is_data_db(obj) || obj_is_cli(obj)) { rootdone = TRUE; pcb->doctype = XP_DOC_DATABASE; pcb->docroot = ncx_get_gen_root(); if (!pcb->docroot) { return SET_ERROR(ERR_INTERNAL_VAL); } } else if (obj_in_notif(obj)) { pcb->doctype = XP_DOC_NOTIFICATION; } else if (obj_in_rpc(obj)) { pcb->doctype = XP_DOC_RPC; } else if (obj_in_rpc_reply(obj)) { pcb->doctype = XP_DOC_RPC_REPLY; } else { return SET_ERROR(ERR_INTERNAL_VAL); } if (!rootdone) { /* get the rpc/input, rpc/output, or /notif node */ rootobj = obj; while (rootobj->parent && !obj_is_root(rootobj->parent) && rootobj->objtype != OBJ_TYP_RPCIO) { rootobj = rootobj->parent; } pcb->docroot = rootobj; } /* validate the XPath expression against the * full cooked object tree */ if (pcb->source == XP_SRC_INSTANCEID) { result = parse_location_path(pcb, NULL, &pcb->validateres); } else { result = parse_expr(pcb, &pcb->validateres); } if (result) { if (LOGDEBUG3) { dump_result(pcb, result, "validate_expr"); } free_result(pcb, result); } return pcb->validateres; } /* xpath1_validate_expr_ex */ /******************************************************************** * FUNCTION xpath1_validate_expr * * Validate the previously parsed expression string * - QName prefixes are valid * - function calls are well-formed and exist in * the pcb->functions array * - variable references exist in the &pcb->varbindQ * * parse expr with YANG prefixes: must/when * called from final OBJ xpath check after all * cooked objects are in place * * Called after all 'uses' and 'augment' expansion * so validation against cooked object tree can be done * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * INPUTS: * mod == module containing the 'obj' (in progress) * obj == object containing the XPath clause * pcb == the XPath parser control block to process * * OUTPUTS: * pcb->obj and pcb->objmod are set * pcb->validateres is set * * RETURNS: * status *********************************************************************/ status_t xpath1_validate_expr (ncx_module_t *mod, obj_template_t *obj, xpath_pcb_t *pcb) { return xpath1_validate_expr_ex(mod, obj, pcb, TRUE); } /******************************************************************** * FUNCTION xpath1_eval_expr * * use if the prefixes are YANG: must/when * Evaluate the expression and get the expression nodeset result * * INPUTS: * pcb == XPath parser control block to use * val == start context node for value of current() * docroot == ptr to cfg->root or top of rpc/rpc-reply/notif tree * logerrors == TRUE if log_error and ncx_print_errormsg * should be used to log XPath errors and warnings * FALSE if internal error info should be recorded * in the xpath_result_t struct instead * configonly == * XP_SRC_XML: * TRUE if this is a call * and all config=false nodes should be skipped * FALSE if call and non-config nodes * will not be skipped * XP_SRC_YANG and XP_SRC_LEAFREF: * should be set to false * res == address of return status * * OUTPUTS: * *res is set to the return status * * * RETURNS: * malloced result struct with expr result * NULL if no result produced (see *res for reason) *********************************************************************/ xpath_result_t * xpath1_eval_expr (xpath_pcb_t *pcb, val_value_t *val, val_value_t *docroot, boolean logerrors, boolean configonly, status_t *res) { xpath_result_t *result; assert( pcb && "pcb is NULL" ); assert( val && "val is NULL" ); //assert( docroot && "docroot is NULL" ); assert( res && "res is NULL" ); if (pcb->tkc) { tk_reset_chain(pcb->tkc); } else { pcb->tkc = tk_tokenize_xpath_string(NULL, pcb->exprstr, 1, 1, res); if (!pcb->tkc || *res != NO_ERR) { if (logerrors) { log_error("\nError: Invalid XPath string '%s'", pcb->exprstr); } return NULL; } } if (pcb->parseres != NO_ERR) { *res = pcb->parseres; return NULL; } if (pcb->validateres != NO_ERR) { *res = pcb->validateres; return NULL; } pcb->val = val; pcb->val_docroot = docroot; pcb->logerrors = logerrors; pcb->context.node.valptr = val; pcb->orig_context.node.valptr = val; #ifdef EXTRA_DEBUG log_debug3("\nXPath setting context node to val: %s", pcb->context.node.valptr->name); #endif if (configonly) { #if 0 || (pcb->source == XP_SRC_YANG && obj_is_config(val->obj) && !obj_is_root(val->obj))) { #endif pcb->flags |= XP_FL_CONFIGONLY; } pcb->flags |= XP_FL_USEROOT; if (pcb->source == XP_SRC_INSTANCEID) { result = parse_location_path(pcb, NULL, &pcb->valueres); } else { result = parse_expr(pcb, &pcb->valueres); } if (pcb->valueres != NO_ERR) { *res = pcb->valueres; } if (LOGDEBUG3 && result) { dump_result(pcb, result, "eval_expr"); } return result; } /* xpath1_eval_expr */ /******************************************************************** * FUNCTION xpath1_eval_xmlexpr * * use if the prefixes are XML: select * Evaluate the expression and get the expression nodeset result * Called from inside the XML parser, so the XML reader * must be used to get the XML namespace to prefix mappings * * INPUTS: * reader == XML reader to use * pcb == initialized XPath parser control block * the xpath_new_pcb(exprstr) function is * all that is needed. This function will * call xpath1_parse_expr if it has not * already been called. * val == start context node for value of current() * docroot == ptr to cfg->root or top of rpc/rpc-replay/notif tree * logerrors == TRUE if log_error and ncx_print_errormsg * should be used to log XPath errors and warnings * FALSE if internal error info should be recorded * in the xpath_result_t struct instead * !!! use FALSE unless DEBUG mode !!! * configonly == * XP_SRC_XML: * TRUE if this is a call * and all config=false nodes should be skipped * FALSE if call and non-config nodes * will not be skipped * XP_SRC_YANG and XP_SRC_LEAFREF: * should be set to false * res == address of return status * * OUTPUTS: * *res is set to the return status * * RETURNS: * malloced result struct with expr result * NULL if no result produced (see *res for reason) *********************************************************************/ xpath_result_t * xpath1_eval_xmlexpr (xmlTextReaderPtr reader, xpath_pcb_t *pcb, val_value_t *val, val_value_t *docroot, boolean logerrors, boolean configonly, status_t *res) { xpath_result_t *result; status_t myres; #ifdef DEBUG if (!pcb || !res) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif *res = NO_ERR; if (pcb->tkc) { tk_reset_chain(pcb->tkc); } else { pcb->tkc = tk_tokenize_xpath_string(NULL, pcb->exprstr, 1, 1, res); if (!pcb->tkc || *res != NO_ERR) { if (logerrors) { log_error("\nError: Invalid XPath string '%s'", pcb->exprstr); } return NULL; } } pcb->obj = NULL; pcb->tkerr.mod = NULL; pcb->val = val; pcb->val_docroot = docroot; pcb->logerrors = logerrors; pcb->reader = reader; pcb->valueres = NO_ERR; if (val) { pcb->context.node.valptr = val; pcb->orig_context.node.valptr = val; #ifdef EXTRA_DEBUG if (LOGDEBUG3 && pcb->val) { log_debug3("\nXPath setting context node to val: %s", pcb->context.node.valptr->name); } #endif } else { pcb->context.node.valptr = docroot; pcb->orig_context.node.valptr = docroot; #ifdef EXTRA_DEBUG if (LOGDEBUG3 && pcb->val) { log_debug3("\nXPath setting context node to docroot: %s", pcb->context.node.valptr->name); } #endif } if (configonly || (pcb->source == XP_SRC_YANG && val && obj_is_config(val->obj))) { pcb->flags |= XP_FL_CONFIGONLY; } pcb->flags |= XP_FL_USEROOT; result = parse_expr(pcb, &pcb->valueres); if (pcb->valueres == NO_ERR && pcb->tkc->cur) { myres = TK_ADV(pcb->tkc); if (myres == NO_ERR) { pcb->valueres = ERR_NCX_INVALID_XPATH_EXPR; if (pcb->logerrors) { log_error("\nError: extra tokens in XPath expression '%s'", pcb->exprstr); ncx_print_errormsg(pcb->tkc, pcb->tkerr.mod, pcb->valueres); } } } if (val && pcb->valueres == NO_ERR && result && val->btyp == NCX_BT_LEAFREF) { pcb->valueres = check_instance_result(pcb, result); } *res = pcb->valueres; if (LOGDEBUG3 && result) { dump_result(pcb, result, "eval_xmlexpr"); } return result; } /* xpath1_eval_xmlexpr */ /******************************************************************** * FUNCTION xpath1_get_functions_ptr * * Get the start of the function array for XPath 1.0 plus * the current() function * * RETURNS: * pointer to functions array *********************************************************************/ const xpath_fncb_t * xpath1_get_functions_ptr (void) { return &functions[0]; } /* xpath1_get_functions_ptr */ /******************************************************************** * FUNCTION xpath1_prune_nodeset * * Check the current result nodeset and remove * any redundant nodes from a NETCONF POV * Any node that has an ancestor already in * the result will be deleted * * INPUTS: * pcb == XPath parser control block to use * result == XPath result nodeset to prune * * OUTPUTS: * some result->nodeQ contents adjusted or removed * *********************************************************************/ void xpath1_prune_nodeset (xpath_pcb_t *pcb, xpath_result_t *result) { xpath_resnode_t *resnode, *nextnode; #ifdef DEBUG if (!result) { SET_ERROR(ERR_INTERNAL_PTR); return; } #endif if (result->restype != XP_RT_NODESET) { return; } if (!result->isval || !pcb->val_docroot) { return; } if (dlq_empty(&result->r.nodeQ)) { return; } /* the resnodes need to be deleted or moved to a tempQ * to correctly track duplicates and remove them */ for (resnode = (xpath_resnode_t *) dlq_firstEntry(&result->r.nodeQ); resnode != NULL; resnode = nextnode) { nextnode = (xpath_resnode_t *)dlq_nextEntry(resnode); dlq_remove(resnode); if (xpath1_check_node_exists(pcb, &result->r.nodeQ, resnode->node.valptr)) { log_debug2("\nxpath1: prune node '%s:%s'", val_get_mod_name(resnode->node.valptr), resnode->node.valptr->name); free_resnode(pcb, resnode); } else { if (nextnode) { dlq_insertAhead(resnode, nextnode); } else { dlq_enque(resnode, &result->r.nodeQ); } } } } /* xpath1_prune_nodeset */ /******************************************************************** * FUNCTION xpath1_check_node_exists * * Check if any ancestor-ot-self node is already in the specified Q * ONLY FOR VALUE NODES IN THE RESULT * * This is only done after all the nodes have been processed * and the nodeset is complete. For NETCONF purposes, * the entire path to root is added for the context node, * and the entire context node contexts are always returned * * INPUTS: * pcb == parser control block to use * resultQ == Q of xpath_resnode_t structs to check * DOES NOT HAVE TO BE WITHIN A RESULT NODE Q * val == value node pointer value to find * * RETURNS: * TRUE if found, FALSE otherwise *********************************************************************/ boolean xpath1_check_node_exists (xpath_pcb_t *pcb, dlq_hdr_t *resultQ, const val_value_t *val) { #ifdef DEBUG if (!pcb || !resultQ || !val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif /* quick test -- see if docroot is already in the Q * which means nothing else is needed */ if (find_resnode(pcb, resultQ, pcb->val_docroot)) { return TRUE; } /* no docroot in the Q so check the node itself */ if (val == pcb->val_docroot) { return FALSE; } while (val) { if (find_resnode(pcb, resultQ, val)) { return TRUE; } if (val->parent && !obj_is_root(val->parent->obj)) { val = val->parent; } else { return FALSE; } } return FALSE; } /* xpath1_check_node_exists */ /******************************************************************** * FUNCTION xpath1_check_node_exists_slow * * Check if any ancestor-ot-self node is already in the specified Q * ONLY FOR VALUE NODES IN THE RESULT * * This is only done after all the nodes have been processed * and the nodeset is complete. For NETCONF purposes, * the entire path to root is added for the context node, * and the entire context node contexts are always returned * * INPUTS: * pcb == parser control block to use * resultQ == Q of xpath_resnode_t structs to check * DOES NOT HAVE TO BE WITHIN A RESULT NODE Q * val == value node pointer value to find * * RETURNS: * TRUE if found, FALSE otherwise *********************************************************************/ boolean xpath1_check_node_exists_slow (xpath_pcb_t *pcb, dlq_hdr_t *resultQ, const val_value_t *val) { #ifdef DEBUG if (!pcb || !resultQ || !val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif /* quick test -- see if docroot is already in the Q * which means nothing else is needed */ if (find_resnode_slow(pcb, resultQ, 0, pcb->val_docroot->name)) { return TRUE; } /* no docroot in the Q so check the node itself */ if (val == pcb->val_docroot) { return FALSE; } while (val) { if (find_resnode_slow(pcb, resultQ, val_get_nsid(val), val->name)) { return TRUE; } if (val->parent && !obj_is_root(val->parent->obj)) { val = val->parent; } else { return FALSE; } } return FALSE; } /* xpath1_check_node_exists_slow */ /******************************************************************** * FUNCTION xpath1_check_node_child_exists_slow * * Check if any child-or-self node is already in the specified Q * ONLY FOR VALUE NODES IN THE RESULT * * This is only done after all the nodes have been processed * and the nodeset is complete. For NETCONF purposes, * the entire path to root is added for the context node, * and the entire context node contexts are always returned * * INPUTS: * pcb == parser control block to use * resultQ == Q of xpath_resnode_t structs to check * DOES NOT HAVE TO BE WITHIN A RESULT NODE Q * val == value node pointer value to find * * RETURNS: * TRUE if found, FALSE otherwise *********************************************************************/ boolean xpath1_check_node_child_exists_slow (xpath_pcb_t *pcb, dlq_hdr_t *resultQ, const val_value_t *val) { xpath_resnode_t *resnode; xmlns_id_t nsid; const xmlChar *name; #ifdef DEBUG if (!pcb || !resultQ || !val) { SET_ERROR(ERR_INTERNAL_PTR); return FALSE; } #endif name = val->name; nsid = val_get_nsid(val); for (resnode = (xpath_resnode_t *)dlq_firstEntry(resultQ); resnode != NULL; resnode = (xpath_resnode_t *)dlq_nextEntry(resnode)) { if (pcb->val) { val_value_t *testval; for( testval=resnode->node.valptr; (testval && !obj_is_root(testval->obj)); testval=testval->parent) { if (nsid && (nsid != val_get_nsid(testval))) { continue; } if (!xml_strcmp(name, testval->name)) { return TRUE; } } } else { obj_template_t *testobj; for( testobj=resnode->node.objptr; (testobj && !obj_is_root(testobj)); testobj=testobj->parent) { if (nsid && (nsid != obj_get_nsid(testobj))) { continue; } if (!xml_strcmp(name, obj_get_name(testobj))) { return TRUE; } } } } return FALSE; } /* xpath1_check_node_child_exists_slow */ /******************************************************************** * FUNCTION xpath1_stringify_nodeset * * Convert a value node pointer to a string node * ONLY FOR VALUE NODES IN THE RESULT * * string(nodeset) * * INPUTS: * pcb == parser control block to use * result == result to stringify * str == address of return string * * OUTPUTS: * *str == malloced and filled in string (if NO_ERR) * * RETURNS: * status, NO_ER or ERR_INTERNAL_MEM, etc. *********************************************************************/ status_t xpath1_stringify_nodeset (xpath_pcb_t *pcb, const xpath_result_t *result, xmlChar **str) { xpath_resnode_t *resnode, *bestnode; val_value_t *bestval; uint32 reslevel, bestlevel; #ifdef DEBUG if (!pcb || !result || !str) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (result->restype != XP_RT_NODESET) { return SET_ERROR(ERR_INTERNAL_VAL); } if (!pcb->val_docroot || !result->isval) { return SET_ERROR(ERR_INTERNAL_VAL); } /* find the best node to use in the result * which is the first node at the lowest value nest level */ reslevel = 0; bestlevel = NCX_MAX_UINT; bestnode = NULL; for (resnode = (xpath_resnode_t *)dlq_firstEntry(&result->r.nodeQ); resnode != NULL; resnode = (xpath_resnode_t *)dlq_nextEntry(resnode)) { if (resnode->node.valptr == pcb->val_docroot) { bestlevel = 0; bestnode = resnode; } else { reslevel = val_get_nest_level(resnode->node.valptr); if (reslevel < bestlevel) { bestlevel = reslevel; bestnode = resnode; } } } if (!bestnode) { *str = xml_strdup(EMPTY_STRING); if (!*str) { return ERR_INTERNAL_MEM; } else { return NO_ERR; } } /* If the node == val_docroot * DO NOT use the entire contents of the * database in the string!!! * Since multiple documents are supported * under one root in NETCONF, just grab the * first child no matter what node it is */ bestval = bestnode->node.valptr; return xpath1_stringify_node(pcb, bestval, str); } /* xpath1_stringify_nodeset */ /******************************************************************* * FUNCTION xpath1_stringify_node * * Convert a value node to a string node * * string(node) * * INPUTS: * pcb == parser control block to use * val == value node to stringify * str == address of return string * * OUTPUTS: * *str == malloced and filled in string (if NO_ERR) * * RETURNS: * status, NO_ER or ERR_INTERNAL_MEM, etc. *********************************************************************/ status_t xpath1_stringify_node (xpath_pcb_t *pcb, val_value_t *val, xmlChar **str) { status_t res; uint32 cnt; boolean cfgonly; xpath_stringwalkerparms_t walkerparms; #ifdef DEBUG if (!pcb || !val || !str) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (val == pcb->val_docroot) { val = val_get_first_child(val); } if (!val) { *str = xml_strdup(EMPTY_STRING); if (!*str) { return ERR_INTERNAL_MEM; } else { return NO_ERR; } } if (typ_is_simple(val->btyp)) { res = val_sprintf_simval_nc(NULL, val, &cnt); if (res != NO_ERR) { return res; } *str = m__getMem(cnt+1); if (!*str) { return ERR_INTERNAL_MEM; } res = val_sprintf_simval_nc(*str, val, &cnt); if (res != NO_ERR) { m__free(*str); *str = NULL; return res; } } else { cfgonly = (pcb->flags & XP_FL_CONFIGONLY) ? TRUE : FALSE; walkerparms.buffer = NULL; walkerparms.buffsize = 0; walkerparms.buffpos = 0; walkerparms.res = NO_ERR; /* first walk to get the buffer size * don't care about result; just need to check * walkerparms.res */ (void)val_find_all_descendants(stringify_walker_fn, pcb, &walkerparms, val, NULL, /* modname */ NULL, /* name */ cfgonly, FALSE, /* textmode */ TRUE, /* orself */ TRUE); /* forceall */ if (walkerparms.res != NO_ERR) { return walkerparms.res; } walkerparms.buffer = m__getMem(walkerparms.buffpos+2); if (!walkerparms.buffer) { return ERR_INTERNAL_MEM; } walkerparms.buffsize = walkerparms.buffpos+2; walkerparms.buffpos = 0; /* second walk to fill in the buffer * don't care about result; just need to check * walkerparms.res */ (void)val_find_all_descendants(stringify_walker_fn, pcb, &walkerparms, val, NULL, /* modname */ NULL, /* name */ cfgonly, FALSE, /* textmode */ TRUE, /* orself */ TRUE); /* forceall */ if (walkerparms.res != NO_ERR) { m__free(walkerparms.buffer); return walkerparms.res; } *str = walkerparms.buffer; } return NO_ERR; } /* xpath1_stringify_node */ /******************************************************************** * FUNCTION xpath1_compare_result_to_string * * Compare an XPath result to the specified string * * result = 'string' * * INPUTS: * pcb == parser control block to use * result == result struct to compare * strval == string value to compare to result * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * equality relation result (TRUE or FALSE) *********************************************************************/ boolean xpath1_compare_result_to_string (xpath_pcb_t *pcb, xpath_result_t *result, xmlChar *strval, status_t *res) { xpath_result_t sresult; boolean retval; /* only compare real results, not objects */ if (!pcb->val) { return TRUE; } *res = NO_ERR; xpath_init_result(&sresult, XP_RT_STRING); sresult.r.str = strval; retval = compare_results(pcb, result, &sresult, XP_EXOP_EQUAL, res); sresult.r.str = NULL; xpath_clean_result(&sresult); return retval; } /* xpath1_compare_result_to_string */ /******************************************************************** * FUNCTION xpath1_compare_result_to_number * * Compare an XPath result to the specified number * * result = number * * INPUTS: * pcb == parser control block to use * result == result struct to compare * numval == number struct to compare to result * MUST BE TYPE NCX_BT_FLOAT64 * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * equality relation result (TRUE or FALSE) *********************************************************************/ boolean xpath1_compare_result_to_number (xpath_pcb_t *pcb, xpath_result_t *result, ncx_num_t *numval, status_t *res) { xpath_result_t sresult; boolean retval; /* only compare real results, not objects */ if (!pcb->val) { return TRUE; } *res = NO_ERR; xpath_init_result(&sresult, XP_RT_NUMBER); ncx_copy_num(numval, &sresult.r.num, NCX_BT_FLOAT64); retval = compare_results(pcb, result, &sresult, XP_EXOP_EQUAL, res); xpath_clean_result(&sresult); return retval; } /* xpath1_compare_result_to_number */ /******************************************************************** * FUNCTION xpath1_compare_nodeset_results * * Compare an XPath result to another result * * result1 = node-set * result2 = node-set * * INPUTS: * pcb == parser control block to use * result1 == result struct to compare * result2 == result struct to compare * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * equality relation result (TRUE or FALSE) *********************************************************************/ boolean xpath1_compare_nodeset_results (xpath_pcb_t *pcb, xpath_result_t *result1, xpath_result_t *result2, status_t *res) { /* only compare real results, not objects */ if (!pcb->val) { return TRUE; } *res = NO_ERR; boolean retval = compare_results(pcb, result1, result2, XP_EXOP_EQUAL, res); return retval; } /* xpath1_compare_nodeset_results */ /* END xpath1.c */ yuma123_2.14/netconf/src/ncx/ncx_list.h0000664000175000017500000002601214770023131020133 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _H_ncx_list #define _H_ncx_list /* FILE: ncx_list.h ********************************************************************* * * * P U R P O S E * * * ********************************************************************* NCX Module Library List Utility Functions ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 17-feb-10 abb Begun; split out from ncx.c */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_tk #include "tk.h" #endif #ifndef _H_typ #include "typ.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_yang #include "yang.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /******************************************************************** * FUNCTION ncx_new_list * * Malloc Initialize an allocated ncx_list_t * * INPUTS: * btyp == type of list desired * * RETURNS: * pointer to new entry, or NULL if memory error *********************************************************************/ extern ncx_list_t * ncx_new_list (ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_init_list * * Initialize an allocated ncx_list_t * * INPUTS: * list == pointer to ncx_list_t memory * btyp == base type for the list *********************************************************************/ extern void ncx_init_list (ncx_list_t *list, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_clean_list * * Scrub the memory of a ncx_list_t but do not delete it * * INPUTS: * list == ncx_list_t struct to clean *********************************************************************/ extern void ncx_clean_list (ncx_list_t *list); /******************************************************************** * FUNCTION ncx_free_list * * Clean and free an allocated ncx_list_t * * INPUTS: * list == pointer to ncx_list_t memory *********************************************************************/ extern void ncx_free_list (ncx_list_t *list); /******************************************************************** * FUNCTION ncx_list_cnt * * Get the number of entries in the list * * INPUTS: * list == pointer to ncx_list_t memory * RETURNS: * number of entries counted *********************************************************************/ extern uint32 ncx_list_cnt (const ncx_list_t *list); /******************************************************************** * FUNCTION ncx_list_empty * * Check if the list is empty or not * * INPUTS: * list == pointer to ncx_list_t memory * RETURNS: * TRUE if list is empty * FALSE otherwise *********************************************************************/ extern boolean ncx_list_empty (const ncx_list_t *list); /******************************************************************** * FUNCTION ncx_string_in_list * * Check if the string value is in the list * List type must be string based, or an enum * * INPUTS: * str == string to find in the list * list == slist to check * * RETURNS: * TRUE if string is found; FALSE otherwise *********************************************************************/ extern boolean ncx_string_in_list (const xmlChar *str, const ncx_list_t *list); /******************************************************************** * FUNCTION ncx_compare_lists * * Compare 2 ncx_list_t struct contents * * Expected data type (NCX_BT_SLIST) * * INPUTS: * list1 == first number * list2 == second number * RETURNS: * -1 if list1 is < list2 * 0 if list1 == list2 (also for error, after SET_ERROR called) * 1 if list1 is > list2 *********************************************************************/ extern int32 ncx_compare_lists (const ncx_list_t *list1, const ncx_list_t *list2); /******************************************************************** * FUNCTION ncx_copy_list * * Copy the contents of list1 to list2 * Supports base type NCX_BT_SLIST * * A partial copy may occur, and list2 should be properly cleaned * and freed, even if an error is returned * * INPUTS: * list1 == first list * list2 == second list * * RETURNS: * status *********************************************************************/ extern status_t ncx_copy_list (const ncx_list_t *list1, ncx_list_t *list2); /******************************************************************** * FUNCTION ncx_merge_list * * The merge function is handled specially for lists. * The contents are not completely replaced like a string. * Instead, only new entries from src are added to the dest list. * * NCX merge algorithm for lists: * * If list types not the same, then error exit; * * If allow_dups == FALSE: * check if entry exists; if so, exit; * * Merge src list member into dest, based on mergetyp enum * } * * INPUTS: * src == ncx_list_t struct to merge from * dest == ncx_list_t struct to merge into * mergetyp == type of merge used for this list * allow_dups == TRUE if this list allows duplicate values * * OUTPUTS: * ncx_lmem_t structs will be moved from the src to dest as needed * * RETURNS: * none *********************************************************************/ extern void ncx_merge_list (ncx_list_t *src, ncx_list_t *dest, ncx_merge_t mergetyp, boolean allow_dups); /******************************************************************** * FUNCTION ncx_set_strlist * * consume a generic string list with no type checking * Convert a text line into an ncx_list_t using NCX_BT_STRING * as the list type. Must call ncx_init_list first !!! * * INPUTS: * liststr == list value in string form * list == ncx_list_t that should be initialized and * filled with the values from the string * * RETURNS: * status *********************************************************************/ extern status_t ncx_set_strlist (const xmlChar *liststr, ncx_list_t *list); /******************************************************************** * FUNCTION ncx_set_list * * consume a generic string list with base type checking * Parse the XML input as an NCX_BT_SLIST * Do not check the individual strings against any restrictions * Just check that the strings parse as the expected type. * Mark list members with errors as needed * * Must call ncx_init_list first!!! * * INPUTS: * btyp == expected basetype for the list member type * strval == cleaned XML string to parse into ncx_str_t or * ncx_num_t values * list == ncx_list_t in progress that will get the ncx_lmem_t * structs added to it, as they are parsed * * OUTPUTS: * list->memQ has 1 or more ncx_lmem_t structs appended to it * * RETURNS: * status *********************************************************************/ extern status_t ncx_set_list (ncx_btype_t btyp, const xmlChar *strval, ncx_list_t *list); /******************************************************************** * FUNCTION ncx_finish_list * * 2nd pass of parsing a ncx_list_t * Finish converting the list members to the proper format * * INPUTS: * typdef == typ_def_t for the designated list member type * list == list struct with ncx_lmem_t structs to check * * OUTPUTS: * If return other than NO_ERR: * each list->lmem.flags field may contain bits set * for errors: * NCX_FL_RANGE_ERR: size out of range * NCX_FL_VALUE_ERR value not permitted by value set, * or pattern * RETURNS: * status *********************************************************************/ extern status_t ncx_finish_list (typ_def_t *typdef, ncx_list_t *list); /******************************************************************** * FUNCTION ncx_new_lmem * * Malloc and fill in a new ncx_lmem_t struct * * INPUTS: * none * RETURNS: * pointer to malloced and initialized ncx_lmem_t struct * NULL if malloc error *********************************************************************/ extern ncx_lmem_t * ncx_new_lmem (void); /******************************************************************** * FUNCTION ncx_clean_lmem * * Scrub the memory of a ncx_lmem_t but do not delete it * * INPUTS: * lmem == ncx_lmem_t struct to clean * btyp == base type of list member (lmem) *********************************************************************/ extern void ncx_clean_lmem (ncx_lmem_t *lmem, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_free_lmem * * Free all the memory in a ncx_lmem_t struct * * INPUTS: * lmem == struct to clean and free * btyp == base type of the list member * *********************************************************************/ extern void ncx_free_lmem (ncx_lmem_t *lmem, ncx_btype_t btyp); /******************************************************************** * FUNCTION ncx_find_lmem * * Find a the first matching list member with the specified value * * INPUTS: * list == list to check * memval == value to find, based on list->btyp * * RETURNS: * pointer to the first instance of this value, or NULL if none *********************************************************************/ extern ncx_lmem_t * ncx_find_lmem (ncx_list_t *list, const ncx_lmem_t *memval); /******************************************************************** * FUNCTION ncx_insert_lmem * * Insert a list entry into the specified list * * INPUTS: * list == list to insert into * memval == value to insert, based on list->btyp * mergetyp == requested merge type for the insertion * * RETURNS: * none *********************************************************************/ extern void ncx_insert_lmem (ncx_list_t *list, ncx_lmem_t *memval, ncx_merge_t mergetyp); /******************************************************************** * FUNCTION ncx_first_lmem * * Return the first list member * * INPUTS: * list == list to check * * RETURNS: * pointer to the first list member or NULL if none *********************************************************************/ extern ncx_lmem_t * ncx_first_lmem (ncx_list_t *list); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif /* _H_ncx_list */ yuma123_2.14/netconf/src/ncx/plock_cb.c0000664000175000017500000001332314770023131020060 0ustar vladimirvladimir/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE: plock.c Partial lock control block Support for RFC 5717 operations Data structure definition ********************************************************************* * * * C H A N G E H I S T O R Y * * * ********************************************************************* date init comment ---------------------------------------------------------------------- 25jun10 abb begun; split out from plock.c ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include #include #include #include #include #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_cfg #include "cfg.h" #endif #ifndef _H_log #include "log.h" #endif #ifndef _H_plock_cb #include "plock_cb.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_tstamp #include "tstamp.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_xmlns #include "xmlns.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_xpath #include "xpath.h" #endif #ifndef _H_xpath1 #include "xpath1.h" #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG #define PLOCK_CB_DEBUG 1 #endif /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /* the ID zero will not get used */ static uint32 last_id = 0; /******************************************************************** * FUNCTION plock_cb_new * * Create a new partial lock control block * * INPUTS: * sid == session ID reqauesting this partial lock * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to initialized PLCB, or NULL if some error * this struct must be freed by the caller *********************************************************************/ plock_cb_t * plock_cb_new (uint32 sid, status_t *res) { plock_cb_t *plcb; #ifdef DEBUG if (res == NULL) { SET_ERROR(ERR_INTERNAL_PTR); return NULL; } #endif /* temp design: only 4G partial locks supported * until server stops giving out partial locks */ if (last_id == NCX_MAX_UINT) { *res = ERR_NCX_RESOURCE_DENIED; return NULL; } plcb = m__getObj(plock_cb_t); if (plcb == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } memset(plcb, 0x0, sizeof(plock_cb_t)); plcb->plock_final_result = xpath_new_result(XP_RT_NODESET); if (plcb->plock_final_result == NULL) { *res = ERR_INTERNAL_MEM; m__free(plcb); return NULL; } plcb->plock_id = ++last_id; dlq_createSQue(&plcb->plock_xpathpcbQ); dlq_createSQue(&plcb->plock_resultQ); tstamp_datetime(plcb->plock_time); plcb->plock_sesid = sid; return plcb; } /* plock_cb_new */ /******************************************************************** * FUNCTION plock_cb_free * * Free a partial lock control block * * INPUTS: * plcb == partial lock control block to free * *********************************************************************/ void plock_cb_free (plock_cb_t *plcb) { if ( !plcb ) { return; } while (!dlq_empty(&plcb->plock_xpathpcbQ)) { xpath_free_pcb( (xpath_pcb_t *) dlq_deque(&plcb->plock_xpathpcbQ) ); } while (!dlq_empty(&plcb->plock_resultQ)) { xpath_free_result( (xpath_result_t *) dlq_deque(&plcb->plock_resultQ) ); } xpath_free_result(plcb->plock_final_result); m__free(plcb); } /* plock_cb_free */ /******************************************************************** * FUNCTION plock_cb_reset_id * * Set the next ID number back to the start * Only the caller maintaining a queue of plcb * can decide if the ID should rollover * *********************************************************************/ void plock_cb_reset_id (void) { if (last_id == NCX_MAX_UINT) { last_id = 0; } } /* plock_cb_reset_id */ /* END file plock_cb.c */ yuma123_2.14/netconf/src/check-len.py0000664000175000017500000000730514770023131017556 0ustar vladimirvladimir#!/usr/bin/env python import sys import os import subprocess # ----------------------------------------------------------------------------| class NullFilter: def apply( self, filename ): """Apply a 'null' filter to the filename supplied.""" return True # ----------------------------------------------------------------------------| class EndswithFilenameFilter: def __init__( self, filters = [ '.c', '.h', '.cpp', '.inl' ] ): self.filters_ = filters def apply( self, filename ): """Apply an 'endswith' filter to the filename supplied.""" for f in self.filters_: if filename.endswith( f ): return True return False # ----------------------------------------------------------------------------| class StartswithFilenameFilter: def __init__( self, filters = [ 'M ', 'A ' ] ): self.filters_ = filters def apply( self, filename ): """Apply an 'startswith' filter to the filename supplied.""" for f in self.filters_: if filename.startswith( f ): return True return False # ----------------------------------------------------------------------------| class SVNModifiedFilenameFilter: def __init__( self ): self.startFilter = StartswithFilenameFilter() self.endFilter = EndswithFilenameFilter() def apply( self, filename ): """Apply the filter""" if self.startFilter.apply( filename ) and self.endFilter.apply( filename ): return True return False # ----------------------------------------------------------------------------| def DisplayUsage(): print ( """Usage: check-len.py [-a] Check the line length of modified netconf source files. [-a] : Check all source files.""" ) sys.exit(-1) # ----------------------------------------------------------------------------| def GetAllFilenames( rootDir = "./", filenameFilter = NullFilter() ): """Get the list of all filenames matching the supplied filter""" filenames = [] for root, dirs, files in os.walk( rootDir ): filtered = [ n for n in files if filenameFilter.apply( n ) ] filenames += [ root + "/" + n for n in filtered ] return filenames # ----------------------------------------------------------------------------| def GetModifiedFiles(): svnOp = subprocess.getoutput( "svn status" ) svnOp = svnOp.split( '\n' ) filenameFilter = SVNModifiedFilenameFilter() filenames = [] for entry in svnOp: if filenameFilter.apply( entry ): filenames.append( entry.lstrip( "MA ") ) return filenames # ----------------------------------------------------------------------------| def CheckFile( filename ): """Check the length of every line in the file""" print("Checking %s...." %filename) f = open( filename, 'r' ) lines = f.readlines() lineNo = 1 for line in lines: # Note allow 1 character for CR if len( line ) > 81: print(( "\tLine %d exceeds 80 characters: %d" % ( lineNo, len( line ) ) )) lineNo += 1 # ----------------------------------------------------------------------------| def CheckFiles( filenames ): """Check each of the files""" for filename in filenames: CheckFile( filename ) # ----------------------------------------------------------------------------| if __name__ == '__main__': if len ( sys.argv ) >1 : if sys.argv[1] == "-a": filenames = GetAllFilenames( filenameFilter = EndswithFilenameFilter() ) else: DisplayUsage() else: filenames = GetModifiedFiles() CheckFiles( filenames ) yuma123_2.14/netconf/python/0000775000175000017500000000000014770023131016100 5ustar vladimirvladimiryuma123_2.14/netconf/python/example-yangrpc/0000775000175000017500000000000014770023131021174 5ustar vladimirvladimiryuma123_2.14/netconf/python/example-yangrpc/python-yangrpc-example.py0000664000175000017500000000107214770023131026161 0ustar vladimirvladimirimport yuma import yangrpc import sys conn = yangrpc.connect("127.0.0.1", 830, "root", "mysecretpass","/root/.ssh/id_rsa","/root/.ssh/id_rsa.pub") if(conn==None): print("Error: yangrpc failed to connect!") sys.exit(1) (res, rpc_val) = yangrpc.parse_cli(conn, "xget /interfaces-state") if(res!=0): print("Error: yangrpc failed to parse cli command!") sys.exit(1) yuma.val_dump_value(rpc_val,1) (res, reply_val) = yangrpc.rpc(conn, rpc_val) if(res!=0): print("Error: yangrpc failed to execute rpc!") sys.exit(1) yuma.val_dump_value(reply_val,1) print("Done.") yuma123_2.14/netconf/python/README0000664000175000017500000000012514770023131016756 0ustar vladimirvladimirTo install this module use sudo apt-get install python-dev python setup.py install yuma123_2.14/netconf/python/yangcli/0000775000175000017500000000000014770023131017526 5ustar vladimirvladimiryuma123_2.14/netconf/python/yangcli/__init__.py0000664000175000017500000000023614770023131021640 0ustar vladimirvladimirimport sys from .yangcli import yangcli if sys.version_info < (2, 6): raise RuntimeError('You need Python 2.6+ for this module.') __all__ = [ 'yangcli' ] yuma123_2.14/netconf/python/yangcli/yangcli.py0000664000175000017500000000150414770023131021526 0ustar vladimirvladimirimport yangrpc import yuma import traceback from lxml import etree def yangcli(yangrpc_cb,cmd_line,strip_namespaces=True): (res, rpc_val) = yangrpc.parse_cli(yangrpc_cb, cmd_line) if(res!=0): raise NameError("Error: yangrpc failed to parse cli command!") return None (res, reply_val) = yangrpc.rpc(yangrpc_cb, rpc_val) if(res!=0): raise NameError("Error: yangrpc failed to execute rpc!") return None if(strip_namespaces): display_mode=yuma.NCX_DISPLAY_MODE_XML_NONS else: display_mode=yuma.NCX_DISPLAY_MODE_XML (res, reply_xml_str) = yuma.val_make_serialized_string(reply_val, display_mode) if(res!=0): raise NameError("Error: yuma.val_make_serialized_string failed!") return None yuma.val_free_value(rpc_val); yuma.val_free_value(reply_val); myetree = etree.fromstring(reply_xml_str) return myetree yuma123_2.14/netconf/python/debian/0000775000175000017500000000000014770023131017322 5ustar vladimirvladimiryuma123_2.14/netconf/python/debian/source/0000775000175000017500000000000014770023131020622 5ustar vladimirvladimiryuma123_2.14/netconf/python/debian/source/format0000664000175000017500000000001414770023131022030 0ustar vladimirvladimir3.0 (quilt) yuma123_2.14/netconf/python/debian/control0000664000175000017500000000170114770023131020724 0ustar vladimirvladimirSource: yuma123-python Section: net Priority: optional Maintainer: Vladimir Vassilev Build-Depends: debhelper (>= 10), autoconf, libxml2-dev, libyangrpc-dev (>= 2.14), libyuma-dev (>= 2.14), pkg-config, dh-python, python3-all-dev, libpython3-all-dev, python3-setuptools Standards-Version: 4.4.1 Homepage: https://yuma123.org Package: python3-yuma Section: python Architecture: any Depends: ${python3:Depends}, ${shlibs:Depends}, ${misc:Depends}, python3-lxml, libyuma2 (>=2.14), libyangrpc2 (>=2.14) X-Python3-Versions: ${python3:Versions} Provides: libyuma-python, ${python3:Provides} Description: NETCONF/YANG Python support The NETCONF protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yuma123 Python support files provide Python functions for integration of NETCONF/YANG into applications written in Python. yuma123_2.14/netconf/python/debian/rules0000775000175000017500000000101114770023131020373 0ustar vladimirvladimir#!/usr/bin/make -f #export DH_VERBOSE=1 export PYBUILD_NAME=yuma export DEB_BUILD_OPTIONS=nocheck # TODO # without -pie build fails during perl module build somehow... export DEB_BUILD_MAINT_OPTIONS := hardening=+all,-pie DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) LIB_VERSION = UPSTREAM_VERSION = $(shell dpkg-parsechangelog | egrep '^Version:' | cut -f 2 -d ':' | sed 's/ //' | sed 's/~dfsg.*$$//') COMPAT_VERSION = $(UPSTREAM_VERSION)~dfsg %: dh $@ --with python3 --buildsystem=pybuild yuma123_2.14/netconf/python/debian/copyright0000664000175000017500000000326514770023131021263 0ustar vladimirvladimirFiles: * Copyright: (C) 2016-2025 Vladimir Vassilev Copyright: (C) 2016-2018 Transpacket AS Copyright: (C) 2019-2025 Lightside Instruments AS License: BSD License: BSD Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andy Bierman BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. yuma123_2.14/netconf/python/debian/python-yuma.install0000664000175000017500000000000414770023131023176 0ustar vladimirvladimirusr yuma123_2.14/netconf/python/debian/compat0000664000175000017500000000000314770023131020521 0ustar vladimirvladimir10 yuma123_2.14/netconf/python/debian/changelog0000664000175000017500000000024414770023131021174 0ustar vladimirvladimiryuma123-python (2.14-0) stable; urgency=medium * Unofficial package -- Vladimir Vassilev Sun, 23 Mar 2025 15:25:36 +0100 yuma123_2.14/netconf/python/setup.py0000664000175000017500000000176614770023131017624 0ustar vladimirvladimirimport sys if sys.version_info < (3,10): from distutils.core import setup,Extension else: from setuptools import setup,Extension yuma = Extension('yuma', sources = ['yuma.c'], extra_compile_args=['-I/usr/include/yuma/platform', '-I/usr/include/yuma/ncx' , '-I/usr/include/yuma/agt'], extra_link_args=['-lyumancx', '-lyumaagt']) yangrpc = Extension('yangrpc', sources = ['yangrpc.c'], extra_compile_args=['-I/usr/include/yuma/platform', '-I/usr/include/yuma/ncx' , '-I/usr/include/yuma/yangrpc'], extra_link_args=['-lyumancx', '-lyangrpc']) setup(name='yangcli', version='0.0.1', description="Python yangcli", author="Vladimir Vassilev", author_email="vladimir@lightside-instruments.com", url="http://yuma123.org/wiki", packages=["yangcli"], license="Apache License 2.0", platforms=["Posix; OS X; Windows"], #classifiers=[] #scripts=['scripts/myscript'] ext_modules=[yuma, yangrpc], ) yuma123_2.14/netconf/python/yangrpc.c0000664000175000017500000001020414770023131017704 0ustar vladimirvladimir#include #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "procdefs.h" #include "dlq.h" #include "rpc.h" #include "yangrpc.h" static int yangrpc_init_done = 0; static PyObject * py_yangrpc_connect(PyObject *self, PyObject *args) { status_t res; char* server; int port; char* user; char* password; char* private_key; char* public_key; char* other_args=NULL; yangrpc_cb_ptr_t yangrpc_cb_ptr; if (!PyArg_ParseTuple(args, (char *) "siszzz|z:yangrpc_yangrpc_connect", &server,&port,&user,&password,&public_key,&private_key,&other_args)) { return (NULL); } if(yangrpc_init_done==0) { res=yangrpc_init(NULL); assert(res==NO_ERR); yangrpc_init_done=1; } res = yangrpc_connect(server, port, user, password, public_key, private_key, other_args, &yangrpc_cb_ptr); if(res!=NO_ERR) { Py_RETURN_NONE; /*returning*/ } return Py_BuildValue("O", PyCapsule_New(yangrpc_cb_ptr, "yangrpc_cb_ptr_t", NULL)); } static PyObject * py_yangrpc_exec(PyObject *self, PyObject *args) { PyObject *py_retval; PyObject *py_yangrpc_cb_ptr; PyObject *py_rpc_val; int res; yangrpc_cb_ptr_t yangrpc_cb_ptr; val_value_t *rpc_val; val_value_t *reply_val; if (!PyArg_ParseTuple(args, (char *) "OO:yangrpc_yangrpc_exec", &py_yangrpc_cb_ptr, &py_rpc_val)) { return (NULL); } yangrpc_cb_ptr = (val_value_t*)PyCapsule_GetPointer(py_yangrpc_cb_ptr, "yangrpc_cb_ptr_t"); rpc_val = (val_value_t*)PyCapsule_GetPointer(py_rpc_val, "val_value_t_ptr"); res = yangrpc_exec(yangrpc_cb_ptr, rpc_val, &reply_val); py_retval = PyTuple_New(2); PyTuple_SetItem(py_retval, 0, Py_BuildValue("i", (int)res)); PyTuple_SetItem(py_retval, 1, reply_val?Py_BuildValue("O", PyCapsule_New(reply_val, "val_value_t_ptr", NULL)):Py_None); return py_retval; } static PyObject * py_yangrpc_parse_cli(PyObject *self, PyObject *args) { PyObject *py_retval; PyObject *py_yangrpc_cb_ptr; PyObject *py_rpc_val; int res; yangrpc_cb_ptr_t *yangrpc_cb_ptr; val_value_t *rpc_val; char* cmd; if (!PyArg_ParseTuple(args, (char *) "Os:yangrpc_yangrpc_parse_cli", &py_yangrpc_cb_ptr, &cmd)) { return (NULL); } yangrpc_cb_ptr = (yangrpc_cb_ptr_t *)PyCapsule_GetPointer(py_yangrpc_cb_ptr, "yangrpc_cb_ptr_t"); res = yangrpc_parse_cli(yangrpc_cb_ptr, cmd, &rpc_val); py_retval = PyTuple_New(2); PyTuple_SetItem(py_retval, 0, Py_BuildValue("i", (int)res)); PyTuple_SetItem(py_retval, 1, rpc_val?Py_BuildValue("O", PyCapsule_New(rpc_val, "val_value_t_ptr", NULL)):Py_None); return py_retval; } static PyObject * py_yangrpc_close(PyObject *self, PyObject *args) { PyObject *py_retval; PyObject *py_yangrpc_cb_ptr; int res; yangrpc_cb_ptr_t yangrpc_cb_ptr; val_value_t *rpc_val; val_value_t *reply_val; if (!PyArg_ParseTuple(args, (char *) "O:yangrpc_yangrpc_close", &py_yangrpc_cb_ptr)) { return (NULL); } yangrpc_cb_ptr = (yangrpc_cb_ptr_t *)PyCapsule_GetPointer(py_yangrpc_cb_ptr, "yangrpc_cb_ptr_t"); yangrpc_close(yangrpc_cb_ptr); Py_RETURN_NONE; } /* define functions in module */ static PyMethodDef yang_rpc_methods[] = { {"connect", py_yangrpc_connect, METH_VARARGS, "connects to client"}, {"rpc", py_yangrpc_exec, METH_VARARGS, "executes rpc by sending the rpc data contents and blocking until reply is received"}, {"parse_cli", py_yangrpc_parse_cli, METH_VARARGS, "converts cli string to rpc data contents"}, {"close", py_yangrpc_close, METH_VARARGS, "close session"}, {NULL, NULL, 0, NULL} }; /* module initialization */ static struct PyModuleDef py_mod_def = { PyModuleDef_HEAD_INIT, "yangrpc", /* name of module */ "", /* module documentation, may be NULL */ -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ yang_rpc_methods }; PyMODINIT_FUNC PyInit_yangrpc(void) { PyObject *m; //(void) Py_InitModule("yangrpc", yang_rpc_methods); m = PyModule_Create(&py_mod_def); return m; } yuma123_2.14/netconf/python/yangcli-python0000775000175000017500000000172714770023131021002 0ustar vladimirvladimir#!/usr/bin/python from yangcli import yangcli from lxml import etree import yangrpc import sys import argparse parser = argparse.ArgumentParser() parser.add_argument("--server", help="Address of the server") parser.add_argument("--ncport", help="Netconf port") parser.add_argument("--user", help="Username") parser.add_argument("--password", help="Password") parser.add_argument("--public-key", help="Public key path") parser.add_argument("--private-key", help="Private key path") parser.add_argument("--run-command", help="Run this command in yangcli synthax") parser.add_argument("--batch-mode", help="Terminate after run-command or run-script", action="store_true") args = parser.parse_args() conn = yangrpc.connect(args.server, int(args.ncport), args.user, args.password, args.private_key, args.public_key) if(conn==None): print("Error: yangrpc failed to connect!") sys.exit(1) result = yangcli(conn, args.run_command) print etree.tostring(result, pretty_print=True) yuma123_2.14/netconf/python/example-yangcli/0000775000175000017500000000000014770023131021157 5ustar vladimirvladimiryuma123_2.14/netconf/python/example-yangcli/python-yangcli-example.py0000664000175000017500000000064414770023131026133 0ustar vladimirvladimirfrom yangcli.yangcli import yangcli from lxml import etree import yangrpc import sys conn = yangrpc.connect("127.0.0.1", 830, "root", "mysecretpass","/root/.ssh/id_rsa","/root/.ssh/id_rsa.pub") if(conn==None): print("Error: yangrpc failed to connect!") sys.exit(1) names = yangcli(conn, "xget /interfaces-state").xpath('./data/interfaces-state/interface/name') for name in names: print(name.text) print("Done.") yuma123_2.14/netconf/python/example/0000775000175000017500000000000014770023131017533 5ustar vladimirvladimiryuma123_2.14/netconf/python/example/python-yuma-example.yang0000664000175000017500000000073614770023131024344 0ustar vladimirvladimirmodule python-yuma-example { namespace "http://yuma123.org/ns/python-yuma-example"; prefix "python-yuma-example"; organization "yuma123.org"; description "python-yuma-example module"; revision 2016-04-19 { description "Initial revision"; } container python-yuma-example { description "Top level leaf python-yuma-example container"; leaf message { type string; } } } yuma123_2.14/netconf/python/example/python-yuma-example.py0000664000175000017500000000152214770023131024030 0ustar vladimirvladimirimport yuma import sys yuma.init() print("Loading schema module python-yuma-example.yang") (res, mod) = yuma.schema_module_load("python-yuma-example.yang") if(res!=0): print("Error: python-yuma-example.yang failed to load!") sys.exit(1) (res, root_val) = yuma.cfg_load("python-yuma-example.xml") if(res!=0): print("Error: python-yuma-example.xml failed to load!") sys.exit(1) (res, python_yuma_example_val) = yuma.val_find_child(root_val, "python-yuma-example", "python-yuma-example") if(res!=0): print("Error: Missing /python-yuma-example container!") sys.exit(1) (res, message_val) = yuma.val_find_child(python_yuma_example_val, "python-yuma-example", "message") if(res!=0): print("Error: Missing /python-yuma-example/message leaf!") sys.exit(1) print((yuma.val_string(message_val))) yuma.val_dump_value(root_val,1) print("Done.") yuma123_2.14/netconf/python/example/python-yuma-example.xml0000664000175000017500000000037114770023131024201 0ustar vladimirvladimir Hello Python Yuma! yuma123_2.14/netconf/python/example/python-yuma-example-transcoding.py0000664000175000017500000000133414770023131026342 0ustar vladimirvladimirimport yuma import sys yuma.init() print("Loading schema module python-yuma-example.yang") (res, mod) = yuma.schema_module_load("python-yuma-example.yang") if(res!=0): print("Error: python-yuma-example.yang failed to load!") sys.exit(1) obj = yuma.ncx_find_object(mod,"python-yuma-example") assert(obj!=None) val = yuma.val_new_value() res = yuma.val_set_cplxval_obj(val, obj,""" { "python-yuma-example:python-yuma-example": { "message": "hello world" } } """) val = yuma.val_new_value() res = yuma.val_set_cplxval_obj(val, obj,""" hello world """) yuma.val_dump_value(val,1) print("Done.") yuma123_2.14/netconf/python/yuma.c0000664000175000017500000001535514770023131017230 0ustar vladimirvladimir#include #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_rpc.h" #include "dlq.h" #include "rpc.h" static PyObject * yuma_init(PyObject *self, PyObject *args) { status_t res; int argc=1; char* argv[] = {"hello"}; char buff[] = "yuma for python"; res = ncx_init(FALSE, LOG_DEBUG_INFO, TRUE, buff, argc, argv); res = ncxmod_load_module( NCXMOD_YUMA_NETCONF, NULL, NULL, NULL ); assert(res == 0); res = ncxmod_load_module( NCXMOD_NETCONFD, NULL, NULL, NULL ); assert(res == 0); return Py_BuildValue("i", (int)res); } static PyObject * yuma_schema_module_load(PyObject *self, PyObject *args) { status_t res; PyObject *py_retval; ncx_module_t* mod; const char* mod_name_str; if (!PyArg_ParseTuple(args, (char *) "s:yuma_schema_module_load", &mod_name_str)) { return (NULL); } res = ncxmod_load_module( (const xmlChar*)mod_name_str, NULL, NULL, &mod); py_retval = PyTuple_New(2); PyTuple_SetItem(py_retval, 0, Py_BuildValue("i", (int)res)); PyTuple_SetItem(py_retval, 1, Py_BuildValue("O", PyCapsule_New(mod, "ncx_module_t_ptr", NULL))); return py_retval; } static PyObject * yuma_cfg_load(PyObject *self, PyObject *args) { status_t res; PyObject *py_retval; int argc=1; char* argv[] = {"hello"}; char* cfg_filename_str; char* startup_arg; boolean showver; help_mode_t showhelpmode; agt_profile_t *profile; cfg_template_t *runningcfg; if (!PyArg_ParseTuple(args, (char *) "s:yuma_cfg_load", &cfg_filename_str)) { return (NULL); } startup_arg = malloc(strlen(cfg_filename_str)+1); sprintf(startup_arg,"%s",cfg_filename_str); res = agt_init1(argc, argv, &showver, &showhelpmode); assert(res == 0); if (showver || showhelpmode != HELP_MODE_NONE) { printf("ver 1.0\n"); } profile = agt_get_profile(); res = ncxmod_load_module( NCXMOD_WITH_DEFAULTS, NULL, &profile->agt_savedevQ, NULL ); assert(res == 0); profile->agt_has_startup=TRUE; profile->agt_startup=startup_arg; res = agt_init2(); assert(res == 0); runningcfg = cfg_get_config(NCX_CFG_RUNNING); assert(runningcfg!=NULL); py_retval = PyTuple_New(2); PyTuple_SetItem(py_retval, 0, Py_BuildValue("i", (int)res)); PyTuple_SetItem(py_retval, 1, Py_BuildValue("O", PyCapsule_New(runningcfg->root, "val_value_t_ptr", NULL))); return py_retval; } static PyObject * yuma_val_find_child(PyObject *self, PyObject *args) { PyObject *py_retval; PyObject *py_parent_val; int res; val_value_t* parent_val; val_value_t* child_val; char* namespace; char* name; if (!PyArg_ParseTuple(args, (char *) "Oss:yuma_val_find_child", &py_parent_val,&namespace,&name)) { return (NULL); } parent_val = (val_value_t*)PyCapsule_GetPointer(py_parent_val, "val_value_t_ptr"); child_val=val_find_child(parent_val, namespace, name); if(child_val==NULL) { res=-1; } else { res=0; } py_retval = PyTuple_New(2); PyTuple_SetItem(py_retval, 0, Py_BuildValue("i", (int)res)); PyTuple_SetItem(py_retval, 1, Py_BuildValue("O", PyCapsule_New(child_val, "val_value_t_ptr", NULL))); return py_retval; } static PyObject * yuma_val_string(PyObject *self, PyObject *args) { PyObject *py_val; val_value_t* val; if (!PyArg_ParseTuple(args, (char *) "O:yuma_val_string", &py_val)) { return (NULL); } val = (val_value_t*)PyCapsule_GetPointer(py_val, "val_value_t_ptr"); return Py_BuildValue("s", VAL_STRING(val)); } static PyObject * yuma_val_dump_value(PyObject *self, PyObject *args) { PyObject *py_val; int res; val_value_t* val; int flag; if (!PyArg_ParseTuple(args, (char *) "Oi:yuma_val_dump_value", &py_val,&flag)) { return (NULL); } val = (val_value_t*)PyCapsule_GetPointer(py_val, "val_value_t_ptr"); val_dump_value(val,flag); Py_RETURN_NONE; } static PyObject * yuma_val_make_serialized_string(PyObject *self, PyObject *args) { PyObject *py_retval; PyObject *py_val; status_t res; val_value_t* val; ncx_display_mode_t mode; char* str; if (!PyArg_ParseTuple(args, (char *) "Oi:yuma_val_find_child", &py_val, &mode)) { return (NULL); } val = (val_value_t*)PyCapsule_GetPointer(py_val, "val_value_t_ptr"); res=val_make_serialized_string(val, mode, (xmlChar **)&str); py_retval = PyTuple_New(2); PyTuple_SetItem(py_retval, 0, Py_BuildValue("i", (int)res)); PyTuple_SetItem(py_retval, 1, Py_BuildValue("s", str)); free(str); return py_retval; } static PyObject * yuma_val_free_value(PyObject *self, PyObject *args) { PyObject *py_val; int res; val_value_t* val; if (!PyArg_ParseTuple(args, (char *) "O:yuma_val_dump_value", &py_val)) { return (NULL); } val = (val_value_t*)PyCapsule_GetPointer(py_val, "val_value_t_ptr"); val_free_value(val); Py_RETURN_NONE; } /* define functions in module */ static PyMethodDef yuma_methods[] = { {"init", yuma_init, METH_VARARGS, "initialization"}, {"schema_module_load", yuma_schema_module_load, METH_VARARGS, "load schema module"}, {"cfg_load", yuma_cfg_load, METH_VARARGS, "load configuration"}, {"val_find_child", yuma_val_find_child, METH_VARARGS, "find child of parent val"}, {"val_string", yuma_val_string, METH_VARARGS, "get value of val represented as string"}, {"val_dump_value", yuma_val_dump_value, METH_VARARGS, "dump the value of the provided variable to stdout"}, {"val_make_serialized_string", yuma_val_make_serialized_string, METH_VARARGS, "serialize val variable to new dynamic memory buffer string"}, {"val_free_value", yuma_val_free_value, METH_VARARGS, "free the provided val"}, {NULL, NULL, 0, NULL} }; /* module initialization */ static struct PyModuleDef py_mod_def = { PyModuleDef_HEAD_INIT, "yuma", /* name of module */ "", /* module documentation, may be NULL */ -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ yuma_methods }; PyMODINIT_FUNC PyInit_yuma(void) { PyObject *m; int res; //m = Py_InitModule("yuma", YumaMethods); m = PyModule_Create(&py_mod_def); res = PyModule_AddIntConstant(m, "NCX_DISPLAY_MODE_XML", NCX_DISPLAY_MODE_XML); assert(res==0); res = PyModule_AddIntConstant(m, "NCX_DISPLAY_MODE_XML_NONS", NCX_DISPLAY_MODE_XML_NONS); assert(res==0); res = PyModule_AddIntConstant(m, "NCX_DISPLAY_MODE_JSON", NCX_DISPLAY_MODE_JSON); assert(res==0); return m; } yuma123_2.14/netconf/perl/0000775000175000017500000000000014770023131015521 5ustar vladimirvladimiryuma123_2.14/netconf/perl/yangrpc/0000775000175000017500000000000014770023131017164 5ustar vladimirvladimiryuma123_2.14/netconf/perl/yangrpc/t/0000775000175000017500000000000014770023131017427 5ustar vladimirvladimiryuma123_2.14/netconf/perl/yangrpc/t/yangrpc.t0000664000175000017500000000075314770023131021264 0ustar vladimirvladimir# Before 'make install' is performed this script should be runnable with # 'make test'. After 'make install' it should work as 'perl yangrpc.t' ######################### # change 'tests => 1' to 'tests => last_test_to_print'; use strict; use warnings; use Test::More tests => 1; BEGIN { use_ok('yangrpc') }; ######################### # Insert your test code below, the Test::More module is use()ed here so read # its man page ( perldoc Test::More ) for help writing this test script. yuma123_2.14/netconf/perl/yangrpc/lib/0000775000175000017500000000000014770023131017732 5ustar vladimirvladimiryuma123_2.14/netconf/perl/yangrpc/lib/yangrpc.pm0000664000175000017500000000337414770023131021742 0ustar vladimirvladimirpackage yangrpc; #use 5.020002; use 5.018002; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use yangrpc ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( ); our $VERSION = '0.01'; require XSLoader; XSLoader::load('yangrpc', $VERSION); # Preloaded methods go here. 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME yangrpc - Perl extension for blah blah blah =head1 SYNOPSIS use yangrpc; blah blah blah =head1 DESCRIPTION Stub documentation for yangrpc, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR vladimir, Evladimir@E =head1 COPYRIGHT AND LICENSE Copyright (C) 2016 by vladimir This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.20.2 or, at your option, any later version of Perl 5 you may have available. =cut yuma123_2.14/netconf/perl/yangrpc/yangrpc.xs0000664000175000017500000000153314770023131021205 0ustar vladimirvladimir#define PERL_NO_GET_CONTEXT #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" static int yangrpc_init_done = 0; MODULE = yangrpc PACKAGE = yangrpc void* connect(char* server, U16 port, char* user, char* password, char* public_key, char* private_key) CODE: void* yangrpc_cb_ptr; if(yangrpc_init_done==0) { yangrpc_init(NULL); yangrpc_init_done=1; } yangrpc_connect(server, port, user, password, public_key, private_key, (char*)NULL, &yangrpc_cb_ptr); RETVAL = yangrpc_cb_ptr; OUTPUT: RETVAL int parse_cli(void* yangrpc_cb_ptr, char* cli_cmd, void* &rpc_val) CODE: RETVAL=yangrpc_parse_cli(yangrpc_cb_ptr, cli_cmd, &rpc_val); OUTPUT: rpc_val RETVAL int rpc(void* yangrpc_cb_ptr, void* rpc_val, void* &reply_val) CODE: RETVAL = yangrpc_exec(yangrpc_cb_ptr, rpc_val, &reply_val); OUTPUT: reply_val RETVAL yuma123_2.14/netconf/perl/yangrpc/README0000664000175000017500000000221514770023131020044 0ustar vladimirvladimiryangrpc version 0.01 ==================== The README is used to introduce the module and provide instructions on how to install the module, any machine dependencies it may have (for example C compilers and installed libraries) and any other information that should be provided before the module is installed. A README file is required for CPAN modules since CPAN extracts the README file from a module distribution so that people browsing the archive can use it get an idea of the modules uses. It is usually a good idea to provide version information here so that people can decide whether fixes for the module are worth downloading. INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES This module requires these other modules and libraries: blah blah blah COPYRIGHT AND LICENCE Put the correct copyright and licence information here. Copyright (C) 2016 by vladimir This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.20.2 or, at your option, any later version of Perl 5 you may have available. yuma123_2.14/netconf/perl/yangrpc/Changes0000664000175000017500000000022514770023131020456 0ustar vladimirvladimirRevision history for Perl extension yangrpc. 0.01 Fri Jul 8 13:58:45 2016 - original version; created by h2xs 1.23 with options -A -n yangrpc yuma123_2.14/netconf/perl/yangrpc/Makefile.PL0000664000175000017500000000164514770023131021144 0ustar vladimirvladimiruse 5.018002; use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( NAME => 'yangrpc', VERSION_FROM => 'lib/yangrpc.pm', # finds $VERSION, requires EU::MM from perl >= 5.5 PREREQ_PM => {}, # e.g., Module::Name => 1.1 ABSTRACT_FROM => 'lib/yangrpc.pm', # retrieve abstract from module AUTHOR => 'vladimir ', #LICENSE => 'perl', #Value must be from legacy list of licenses here #http://search.cpan.org/perldoc?Module%3A%3ABuild%3A%3AAPI LIBS => ['-lyangrpc'], # e.g., '-lm' DEFINE => '', # e.g., '-DHAVE_SOMETHING' INC => '-I.', # e.g., '-I. -I/usr/include/other' # Un-comment this if you add C files to link with later: # OBJECT => '$(O_FILES)', # link all the C files too ); yuma123_2.14/netconf/perl/yangrpc/ppport.h0000664000175000017500000055600214770023131020671 0ustar vladimirvladimir#if 0 <<'SKIP'; #endif /* ---------------------------------------------------------------------- ppport.h -- Perl/Pollution/Portability Version 3.21 Automatically created by Devel::PPPort running under perl 5.020002. Do NOT edit this file directly! -- Edit PPPort_pm.PL and the includes in parts/inc/ instead. Use 'perldoc ppport.h' to view the documentation below. ---------------------------------------------------------------------- SKIP =pod =head1 NAME ppport.h - Perl/Pollution/Portability version 3.21 =head1 SYNOPSIS perl ppport.h [options] [source files] Searches current directory for files if no [source files] are given --help show short help --version show version --patch=file write one patch file with changes --copy=suffix write changed copies with suffix --diff=program use diff program and options --compat-version=version provide compatibility with Perl version --cplusplus accept C++ comments --quiet don't output anything except fatal errors --nodiag don't show diagnostics --nohints don't show hints --nochanges don't suggest changes --nofilter don't filter input files --strip strip all script and doc functionality from ppport.h --list-provided list provided API --list-unsupported list unsupported API --api-info=name show Perl API portability information =head1 COMPATIBILITY This version of F is designed to support operation with Perl installations back to 5.003, and has been tested up to 5.11.5. =head1 OPTIONS =head2 --help Display a brief usage summary. =head2 --version Display the version of F. =head2 --patch=I If this option is given, a single patch file will be created if any changes are suggested. This requires a working diff program to be installed on your system. =head2 --copy=I If this option is given, a copy of each file will be saved with the given suffix that contains the suggested changes. This does not require any external programs. Note that this does not automagially add a dot between the original filename and the suffix. If you want the dot, you have to include it in the option argument. If neither C<--patch> or C<--copy> are given, the default is to simply print the diffs for each file. This requires either C or a C program to be installed. =head2 --diff=I Manually set the diff program and options to use. The default is to use C, when installed, and output unified context diffs. =head2 --compat-version=I Tell F to check for compatibility with the given Perl version. The default is to check for compatibility with Perl version 5.003. You can use this option to reduce the output of F if you intend to be backward compatible only down to a certain Perl version. =head2 --cplusplus Usually, F will detect C++ style comments and replace them with C style comments for portability reasons. Using this option instructs F to leave C++ comments untouched. =head2 --quiet Be quiet. Don't print anything except fatal errors. =head2 --nodiag Don't output any diagnostic messages. Only portability alerts will be printed. =head2 --nohints Don't output any hints. Hints often contain useful portability notes. Warnings will still be displayed. =head2 --nochanges Don't suggest any changes. Only give diagnostic output and hints unless these are also deactivated. =head2 --nofilter Don't filter the list of input files. By default, files not looking like source code (i.e. not *.xs, *.c, *.cc, *.cpp or *.h) are skipped. =head2 --strip Strip all script and documentation functionality from F. This reduces the size of F dramatically and may be useful if you want to include F in smaller modules without increasing their distribution size too much. The stripped F will have a C<--unstrip> option that allows you to undo the stripping, but only if an appropriate C module is installed. =head2 --list-provided Lists the API elements for which compatibility is provided by F. Also lists if it must be explicitly requested, if it has dependencies, and if there are hints or warnings for it. =head2 --list-unsupported Lists the API elements that are known not to be supported by F and below which version of Perl they probably won't be available or work. =head2 --api-info=I Show portability information for API elements matching I. If I is surrounded by slashes, it is interpreted as a regular expression. =head1 DESCRIPTION In order for a Perl extension (XS) module to be as portable as possible across differing versions of Perl itself, certain steps need to be taken. =over 4 =item * Including this header is the first major one. This alone will give you access to a large part of the Perl API that hasn't been available in earlier Perl releases. Use perl ppport.h --list-provided to see which API elements are provided by ppport.h. =item * You should avoid using deprecated parts of the API. For example, using global Perl variables without the C prefix is deprecated. Also, some API functions used to have a C prefix. Using this form is also deprecated. You can safely use the supported API, as F will provide wrappers for older Perl versions. =item * If you use one of a few functions or variables that were not present in earlier versions of Perl, and that can't be provided using a macro, you have to explicitly request support for these functions by adding one or more C<#define>s in your source code before the inclusion of F. These functions or variables will be marked C in the list shown by C<--list-provided>. Depending on whether you module has a single or multiple files that use such functions or variables, you want either C or global variants. For a C function or variable (used only in a single source file), use: #define NEED_function #define NEED_variable For a global function or variable (used in multiple source files), use: #define NEED_function_GLOBAL #define NEED_variable_GLOBAL Note that you mustn't have more than one global request for the same function or variable in your project. Function / Variable Static Request Global Request ----------------------------------------------------------------------------------------- PL_parser NEED_PL_parser NEED_PL_parser_GLOBAL PL_signals NEED_PL_signals NEED_PL_signals_GLOBAL eval_pv() NEED_eval_pv NEED_eval_pv_GLOBAL grok_bin() NEED_grok_bin NEED_grok_bin_GLOBAL grok_hex() NEED_grok_hex NEED_grok_hex_GLOBAL grok_number() NEED_grok_number NEED_grok_number_GLOBAL grok_numeric_radix() NEED_grok_numeric_radix NEED_grok_numeric_radix_GLOBAL grok_oct() NEED_grok_oct NEED_grok_oct_GLOBAL load_module() NEED_load_module NEED_load_module_GLOBAL my_snprintf() NEED_my_snprintf NEED_my_snprintf_GLOBAL my_sprintf() NEED_my_sprintf NEED_my_sprintf_GLOBAL my_strlcat() NEED_my_strlcat NEED_my_strlcat_GLOBAL my_strlcpy() NEED_my_strlcpy NEED_my_strlcpy_GLOBAL newCONSTSUB() NEED_newCONSTSUB NEED_newCONSTSUB_GLOBAL newRV_noinc() NEED_newRV_noinc NEED_newRV_noinc_GLOBAL newSV_type() NEED_newSV_type NEED_newSV_type_GLOBAL newSVpvn_flags() NEED_newSVpvn_flags NEED_newSVpvn_flags_GLOBAL newSVpvn_share() NEED_newSVpvn_share NEED_newSVpvn_share_GLOBAL pv_display() NEED_pv_display NEED_pv_display_GLOBAL pv_escape() NEED_pv_escape NEED_pv_escape_GLOBAL pv_pretty() NEED_pv_pretty NEED_pv_pretty_GLOBAL sv_2pv_flags() NEED_sv_2pv_flags NEED_sv_2pv_flags_GLOBAL sv_2pvbyte() NEED_sv_2pvbyte NEED_sv_2pvbyte_GLOBAL sv_catpvf_mg() NEED_sv_catpvf_mg NEED_sv_catpvf_mg_GLOBAL sv_catpvf_mg_nocontext() NEED_sv_catpvf_mg_nocontext NEED_sv_catpvf_mg_nocontext_GLOBAL sv_pvn_force_flags() NEED_sv_pvn_force_flags NEED_sv_pvn_force_flags_GLOBAL sv_setpvf_mg() NEED_sv_setpvf_mg NEED_sv_setpvf_mg_GLOBAL sv_setpvf_mg_nocontext() NEED_sv_setpvf_mg_nocontext NEED_sv_setpvf_mg_nocontext_GLOBAL vload_module() NEED_vload_module NEED_vload_module_GLOBAL vnewSVpvf() NEED_vnewSVpvf NEED_vnewSVpvf_GLOBAL warner() NEED_warner NEED_warner_GLOBAL To avoid namespace conflicts, you can change the namespace of the explicitly exported functions / variables using the C macro. Just C<#define> the macro before including C: #define DPPP_NAMESPACE MyOwnNamespace_ #include "ppport.h" The default namespace is C. =back The good thing is that most of the above can be checked by running F on your source code. See the next section for details. =head1 EXAMPLES To verify whether F is needed for your module, whether you should make any changes to your code, and whether any special defines should be used, F can be run as a Perl script to check your source code. Simply say: perl ppport.h The result will usually be a list of patches suggesting changes that should at least be acceptable, if not necessarily the most efficient solution, or a fix for all possible problems. If you know that your XS module uses features only available in newer Perl releases, if you're aware that it uses C++ comments, and if you want all suggestions as a single patch file, you could use something like this: perl ppport.h --compat-version=5.6.0 --cplusplus --patch=test.diff If you only want your code to be scanned without any suggestions for changes, use: perl ppport.h --nochanges You can specify a different C program or options, using the C<--diff> option: perl ppport.h --diff='diff -C 10' This would output context diffs with 10 lines of context. If you want to create patched copies of your files instead, use: perl ppport.h --copy=.new To display portability information for the C function, use: perl ppport.h --api-info=newSVpvn Since the argument to C<--api-info> can be a regular expression, you can use perl ppport.h --api-info=/_nomg$/ to display portability information for all C<_nomg> functions or perl ppport.h --api-info=/./ to display information for all known API elements. =head1 BUGS If this version of F is causing failure during the compilation of this module, please check if newer versions of either this module or C are available on CPAN before sending a bug report. If F was generated using the latest version of C and is causing failure of this module, please file a bug report using the CPAN Request Tracker at L. Please include the following information: =over 4 =item 1. The complete output from running "perl -V" =item 2. This file. =item 3. The name and version of the module you were trying to build. =item 4. A full log of the build that failed. =item 5. Any other information that you think could be relevant. =back For the latest version of this code, please get the C module from CPAN. =head1 COPYRIGHT Version 3.x, Copyright (c) 2004-2013, Marcus Holland-Moritz. Version 2.x, Copyright (C) 2001, Paul Marquess. Version 1.x, Copyright (C) 1999, Kenneth Albanowski. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO See L. =cut use strict; # Disable broken TRIE-optimization BEGIN { eval '${^RE_TRIE_MAXBUF} = -1' if $] >= 5.009004 && $] <= 5.009005 } my $VERSION = 3.21; my %opt = ( quiet => 0, diag => 1, hints => 1, changes => 1, cplusplus => 0, filter => 1, strip => 0, version => 0, ); my($ppport) = $0 =~ /([\w.]+)$/; my $LF = '(?:\r\n|[\r\n])'; # line feed my $HS = "[ \t]"; # horizontal whitespace # Never use C comments in this file! my $ccs = '/'.'*'; my $cce = '*'.'/'; my $rccs = quotemeta $ccs; my $rcce = quotemeta $cce; eval { require Getopt::Long; Getopt::Long::GetOptions(\%opt, qw( help quiet diag! filter! hints! changes! cplusplus strip version patch=s copy=s diff=s compat-version=s list-provided list-unsupported api-info=s )) or usage(); }; if ($@ and grep /^-/, @ARGV) { usage() if "@ARGV" =~ /^--?h(?:elp)?$/; die "Getopt::Long not found. Please don't use any options.\n"; } if ($opt{version}) { print "This is $0 $VERSION.\n"; exit 0; } usage() if $opt{help}; strip() if $opt{strip}; if (exists $opt{'compat-version'}) { my($r,$v,$s) = eval { parse_version($opt{'compat-version'}) }; if ($@) { die "Invalid version number format: '$opt{'compat-version'}'\n"; } die "Only Perl 5 is supported\n" if $r != 5; die "Invalid version number: $opt{'compat-version'}\n" if $v >= 1000 || $s >= 1000; $opt{'compat-version'} = sprintf "%d.%03d%03d", $r, $v, $s; } else { $opt{'compat-version'} = 5; } my %API = map { /^(\w+)\|([^|]*)\|([^|]*)\|(\w*)$/ ? ( $1 => { ($2 ? ( base => $2 ) : ()), ($3 ? ( todo => $3 ) : ()), (index($4, 'v') >= 0 ? ( varargs => 1 ) : ()), (index($4, 'p') >= 0 ? ( provided => 1 ) : ()), (index($4, 'n') >= 0 ? ( nothxarg => 1 ) : ()), } ) : die "invalid spec: $_" } qw( AvFILLp|5.004050||p AvFILL||| BhkDISABLE||5.019003| BhkENABLE||5.019003| BhkENTRY_set||5.019003| BhkENTRY||| BhkFLAGS||| CALL_BLOCK_HOOKS||| CLASS|||n CPERLscope|5.005000||p CX_CURPAD_SAVE||| CX_CURPAD_SV||| CopFILEAV|5.006000||p CopFILEGV_set|5.006000||p CopFILEGV|5.006000||p CopFILESV|5.006000||p CopFILE_set|5.006000||p CopFILE|5.006000||p CopSTASHPV_set|5.006000||p CopSTASHPV|5.006000||p CopSTASH_eq|5.006000||p CopSTASH_set|5.006000||p CopSTASH|5.006000||p CopyD|5.009002|5.004050|p Copy||5.004050| CvPADLIST||5.008001| CvSTASH||| CvWEAKOUTSIDE||| DEFSV_set|5.010001||p DEFSV|5.004050||p END_EXTERN_C|5.005000||p ENTER||| ERRSV|5.004050||p EXTEND||| EXTERN_C|5.005000||p F0convert|||n FREETMPS||| GIMME_V||5.004000|n GIMME|||n GROK_NUMERIC_RADIX|5.007002||p G_ARRAY||| G_DISCARD||| G_EVAL||| G_METHOD|5.006001||p G_NOARGS||| G_SCALAR||| G_VOID||5.004000| GetVars||| GvAV||| GvCV||| GvHV||| GvSVn|5.009003||p GvSV||| Gv_AMupdate||5.011000| HEf_SVKEY||5.004000| HeHASH||5.004000| HeKEY||5.004000| HeKLEN||5.004000| HePV||5.004000| HeSVKEY_force||5.004000| HeSVKEY_set||5.004000| HeSVKEY||5.004000| HeUTF8||5.010001| HeVAL||5.004000| HvENAMELEN||5.015004| HvENAMEUTF8||5.015004| HvENAME||5.013007| HvNAMELEN_get|5.009003||p HvNAMELEN||5.015004| HvNAMEUTF8||5.015004| HvNAME_get|5.009003||p HvNAME||| INT2PTR|5.006000||p IN_LOCALE_COMPILETIME|5.007002||p IN_LOCALE_RUNTIME|5.007002||p IN_LOCALE|5.007002||p IN_PERL_COMPILETIME|5.008001||p IS_NUMBER_GREATER_THAN_UV_MAX|5.007002||p IS_NUMBER_INFINITY|5.007002||p IS_NUMBER_IN_UV|5.007002||p IS_NUMBER_NAN|5.007003||p IS_NUMBER_NEG|5.007002||p IS_NUMBER_NOT_INT|5.007002||p IVSIZE|5.006000||p IVTYPE|5.006000||p IVdf|5.006000||p LEAVE||| LINKLIST||5.013006| LVRET||| MARK||| MULTICALL||5.019003| MY_CXT_CLONE|5.009002||p MY_CXT_INIT|5.007003||p MY_CXT|5.007003||p MoveD|5.009002|5.004050|p Move||5.004050| NOOP|5.005000||p NUM2PTR|5.006000||p NVTYPE|5.006000||p NVef|5.006001||p NVff|5.006001||p NVgf|5.006001||p Newxc|5.009003||p Newxz|5.009003||p Newx|5.009003||p Nullav||| Nullch||| Nullcv||| Nullhv||| Nullsv||| OP_CLASS||5.013007| OP_DESC||5.007003| OP_NAME||5.007003| ORIGMARK||| PAD_BASE_SV||| PAD_CLONE_VARS||| PAD_COMPNAME_FLAGS||| PAD_COMPNAME_GEN_set||| PAD_COMPNAME_GEN||| PAD_COMPNAME_OURSTASH||| PAD_COMPNAME_PV||| PAD_COMPNAME_TYPE||| PAD_RESTORE_LOCAL||| PAD_SAVE_LOCAL||| PAD_SAVE_SETNULLPAD||| PAD_SETSV||| PAD_SET_CUR_NOSAVE||| PAD_SET_CUR||| PAD_SVl||| PAD_SV||| PERLIO_FUNCS_CAST|5.009003||p PERLIO_FUNCS_DECL|5.009003||p PERL_ABS|5.008001||p PERL_BCDVERSION|5.019002||p PERL_GCC_BRACE_GROUPS_FORBIDDEN|5.008001||p PERL_HASH|5.004000||p PERL_INT_MAX|5.004000||p PERL_INT_MIN|5.004000||p PERL_LONG_MAX|5.004000||p PERL_LONG_MIN|5.004000||p PERL_MAGIC_arylen|5.007002||p PERL_MAGIC_backref|5.007002||p PERL_MAGIC_bm|5.007002||p PERL_MAGIC_collxfrm|5.007002||p PERL_MAGIC_dbfile|5.007002||p PERL_MAGIC_dbline|5.007002||p PERL_MAGIC_defelem|5.007002||p PERL_MAGIC_envelem|5.007002||p PERL_MAGIC_env|5.007002||p PERL_MAGIC_ext|5.007002||p PERL_MAGIC_fm|5.007002||p PERL_MAGIC_glob|5.019002||p PERL_MAGIC_isaelem|5.007002||p PERL_MAGIC_isa|5.007002||p PERL_MAGIC_mutex|5.019002||p PERL_MAGIC_nkeys|5.007002||p PERL_MAGIC_overload_elem|5.019002||p PERL_MAGIC_overload_table|5.007002||p PERL_MAGIC_overload|5.019002||p PERL_MAGIC_pos|5.007002||p PERL_MAGIC_qr|5.007002||p PERL_MAGIC_regdata|5.007002||p PERL_MAGIC_regdatum|5.007002||p PERL_MAGIC_regex_global|5.007002||p PERL_MAGIC_shared_scalar|5.007003||p PERL_MAGIC_shared|5.007003||p PERL_MAGIC_sigelem|5.007002||p PERL_MAGIC_sig|5.007002||p PERL_MAGIC_substr|5.007002||p PERL_MAGIC_sv|5.007002||p PERL_MAGIC_taint|5.007002||p PERL_MAGIC_tiedelem|5.007002||p PERL_MAGIC_tiedscalar|5.007002||p PERL_MAGIC_tied|5.007002||p PERL_MAGIC_utf8|5.008001||p PERL_MAGIC_uvar_elem|5.007003||p PERL_MAGIC_uvar|5.007002||p PERL_MAGIC_vec|5.007002||p PERL_MAGIC_vstring|5.008001||p PERL_PV_ESCAPE_ALL|5.009004||p PERL_PV_ESCAPE_FIRSTCHAR|5.009004||p PERL_PV_ESCAPE_NOBACKSLASH|5.009004||p PERL_PV_ESCAPE_NOCLEAR|5.009004||p PERL_PV_ESCAPE_QUOTE|5.009004||p PERL_PV_ESCAPE_RE|5.009005||p PERL_PV_ESCAPE_UNI_DETECT|5.009004||p PERL_PV_ESCAPE_UNI|5.009004||p PERL_PV_PRETTY_DUMP|5.009004||p PERL_PV_PRETTY_ELLIPSES|5.010000||p PERL_PV_PRETTY_LTGT|5.009004||p PERL_PV_PRETTY_NOCLEAR|5.010000||p PERL_PV_PRETTY_QUOTE|5.009004||p PERL_PV_PRETTY_REGPROP|5.009004||p PERL_QUAD_MAX|5.004000||p PERL_QUAD_MIN|5.004000||p PERL_REVISION|5.006000||p PERL_SCAN_ALLOW_UNDERSCORES|5.007003||p PERL_SCAN_DISALLOW_PREFIX|5.007003||p PERL_SCAN_GREATER_THAN_UV_MAX|5.007003||p PERL_SCAN_SILENT_ILLDIGIT|5.008001||p PERL_SHORT_MAX|5.004000||p PERL_SHORT_MIN|5.004000||p PERL_SIGNALS_UNSAFE_FLAG|5.008001||p PERL_SUBVERSION|5.006000||p PERL_SYS_INIT3||5.010000| PERL_SYS_INIT||5.010000| PERL_SYS_TERM||5.019003| PERL_UCHAR_MAX|5.004000||p PERL_UCHAR_MIN|5.004000||p PERL_UINT_MAX|5.004000||p PERL_UINT_MIN|5.004000||p PERL_ULONG_MAX|5.004000||p PERL_ULONG_MIN|5.004000||p PERL_UNUSED_ARG|5.009003||p PERL_UNUSED_CONTEXT|5.009004||p PERL_UNUSED_DECL|5.007002||p PERL_UNUSED_VAR|5.007002||p PERL_UQUAD_MAX|5.004000||p PERL_UQUAD_MIN|5.004000||p PERL_USE_GCC_BRACE_GROUPS|5.009004||p PERL_USHORT_MAX|5.004000||p PERL_USHORT_MIN|5.004000||p PERL_VERSION|5.006000||p PL_DBsignal|5.005000||p PL_DBsingle|||pn PL_DBsub|||pn PL_DBtrace|||pn PL_Sv|5.005000||p PL_bufend|5.019002||p PL_bufptr|5.019002||p PL_check||5.006000| PL_compiling|5.004050||p PL_comppad_name||5.017004| PL_comppad||5.008001| PL_copline|5.019002||p PL_curcop|5.004050||p PL_curpad||5.005000| PL_curstash|5.004050||p PL_debstash|5.004050||p PL_defgv|5.004050||p PL_diehook|5.004050||p PL_dirty|5.004050||p PL_dowarn|||pn PL_errgv|5.004050||p PL_error_count|5.019002||p PL_expect|5.019002||p PL_hexdigit|5.005000||p PL_hints|5.005000||p PL_in_my_stash|5.019002||p PL_in_my|5.019002||p PL_keyword_plugin||5.011002| PL_last_in_gv|||n PL_laststatval|5.005000||p PL_lex_state|5.019002||p PL_lex_stuff|5.019002||p PL_linestr|5.019002||p PL_modglobal||5.005000|n PL_na|5.004050||pn PL_no_modify|5.006000||p PL_ofsgv|||n PL_opfreehook||5.011000|n PL_parser|5.009005|5.009005|p PL_peepp||5.007003|n PL_perl_destruct_level|5.004050||p PL_perldb|5.004050||p PL_ppaddr|5.006000||p PL_rpeepp||5.013005|n PL_rsfp_filters|5.019002||p PL_rsfp|5.019002||p PL_rs|||n PL_signals|5.008001||p PL_stack_base|5.004050||p PL_stack_sp|5.004050||p PL_statcache|5.005000||p PL_stdingv|5.004050||p PL_sv_arenaroot|5.004050||p PL_sv_no|5.004050||pn PL_sv_undef|5.004050||pn PL_sv_yes|5.004050||pn PL_tainted|5.004050||p PL_tainting|5.004050||p PL_tokenbuf|5.019002||p POP_MULTICALL||5.019003| POPi|||n POPl|||n POPn|||n POPpbytex||5.007001|n POPpx||5.005030|n POPp|||n POPs|||n PTR2IV|5.006000||p PTR2NV|5.006000||p PTR2UV|5.006000||p PTR2nat|5.009003||p PTR2ul|5.007001||p PTRV|5.006000||p PUSHMARK||| PUSH_MULTICALL||5.019003| PUSHi||| PUSHmortal|5.009002||p PUSHn||| PUSHp||| PUSHs||| PUSHu|5.004000||p PUTBACK||| PadARRAY||5.019003| PadMAX||5.019003| PadlistARRAY||5.019003| PadlistMAX||5.019003| PadlistNAMESARRAY||5.019003| PadlistNAMESMAX||5.019003| PadlistNAMES||5.019003| PadlistREFCNT||5.017004| PadnameIsOUR||| PadnameIsSTATE||| PadnameLEN||5.019003| PadnameOURSTASH||| PadnameOUTER||| PadnamePV||5.019003| PadnameSV||5.019003| PadnameTYPE||| PadnameUTF8||5.019003| PadnamelistARRAY||5.019003| PadnamelistMAX||5.019003| PerlIO_clearerr||5.007003| PerlIO_close||5.007003| PerlIO_context_layers||5.009004| PerlIO_eof||5.007003| PerlIO_error||5.007003| PerlIO_fileno||5.007003| PerlIO_fill||5.007003| PerlIO_flush||5.007003| PerlIO_get_base||5.007003| PerlIO_get_bufsiz||5.007003| PerlIO_get_cnt||5.007003| PerlIO_get_ptr||5.007003| PerlIO_read||5.007003| PerlIO_seek||5.007003| PerlIO_set_cnt||5.007003| PerlIO_set_ptrcnt||5.007003| PerlIO_setlinebuf||5.007003| PerlIO_stderr||5.007003| PerlIO_stdin||5.007003| PerlIO_stdout||5.007003| PerlIO_tell||5.007003| PerlIO_unread||5.007003| PerlIO_write||5.007003| Perl_signbit||5.009005|n PoisonFree|5.009004||p PoisonNew|5.009004||p PoisonWith|5.009004||p Poison|5.008000||p READ_XDIGIT||5.017006| RETVAL|||n Renewc||| Renew||| SAVECLEARSV||| SAVECOMPPAD||| SAVEPADSV||| SAVETMPS||| SAVE_DEFSV|5.004050||p SPAGAIN||| SP||| START_EXTERN_C|5.005000||p START_MY_CXT|5.007003||p STMT_END|||p STMT_START|||p STR_WITH_LEN|5.009003||p ST||| SV_CONST_RETURN|5.009003||p SV_COW_DROP_PV|5.008001||p SV_COW_SHARED_HASH_KEYS|5.009005||p SV_GMAGIC|5.007002||p SV_HAS_TRAILING_NUL|5.009004||p SV_IMMEDIATE_UNREF|5.007001||p SV_MUTABLE_RETURN|5.009003||p SV_NOSTEAL|5.009002||p SV_SMAGIC|5.009003||p SV_UTF8_NO_ENCODING|5.008001||p SVfARG|5.009005||p SVf_UTF8|5.006000||p SVf|5.006000||p SVt_INVLIST||5.019002| SVt_IV||| SVt_NULL||| SVt_NV||| SVt_PVAV||| SVt_PVCV||| SVt_PVFM||| SVt_PVGV||| SVt_PVHV||| SVt_PVIO||| SVt_PVIV||| SVt_PVLV||| SVt_PVMG||| SVt_PVNV||| SVt_PV||| SVt_REGEXP||5.011000| Safefree||| Slab_Alloc||| Slab_Free||| Slab_to_ro||| Slab_to_rw||| StructCopy||| SvCUR_set||| SvCUR||| SvEND||| SvGAMAGIC||5.006001| SvGETMAGIC|5.004050||p SvGROW||| SvIOK_UV||5.006000| SvIOK_notUV||5.006000| SvIOK_off||| SvIOK_only_UV||5.006000| SvIOK_only||| SvIOK_on||| SvIOKp||| SvIOK||| SvIVX||| SvIV_nomg|5.009001||p SvIV_set||| SvIVx||| SvIV||| SvIsCOW_shared_hash||5.008003| SvIsCOW||5.008003| SvLEN_set||| SvLEN||| SvLOCK||5.007003| SvMAGIC_set|5.009003||p SvNIOK_off||| SvNIOKp||| SvNIOK||| SvNOK_off||| SvNOK_only||| SvNOK_on||| SvNOKp||| SvNOK||| SvNVX||| SvNV_nomg||5.013002| SvNV_set||| SvNVx||| SvNV||| SvOK||| SvOOK_offset||5.011000| SvOOK||| SvPOK_off||| SvPOK_only_UTF8||5.006000| SvPOK_only||| SvPOK_on||| SvPOKp||| SvPOK||| SvPVX_const|5.009003||p SvPVX_mutable|5.009003||p SvPVX||| SvPV_const|5.009003||p SvPV_flags_const_nolen|5.009003||p SvPV_flags_const|5.009003||p SvPV_flags_mutable|5.009003||p SvPV_flags|5.007002||p SvPV_force_flags_mutable|5.009003||p SvPV_force_flags_nolen|5.009003||p SvPV_force_flags|5.007002||p SvPV_force_mutable|5.009003||p SvPV_force_nolen|5.009003||p SvPV_force_nomg_nolen|5.009003||p SvPV_force_nomg|5.007002||p SvPV_force|||p SvPV_mutable|5.009003||p SvPV_nolen_const|5.009003||p SvPV_nolen|5.006000||p SvPV_nomg_const_nolen|5.009003||p SvPV_nomg_const|5.009003||p SvPV_nomg_nolen|5.013007||p SvPV_nomg|5.007002||p SvPV_renew|5.009003||p SvPV_set||| SvPVbyte_force||5.009002| SvPVbyte_nolen||5.006000| SvPVbytex_force||5.006000| SvPVbytex||5.006000| SvPVbyte|5.006000||p SvPVutf8_force||5.006000| SvPVutf8_nolen||5.006000| SvPVutf8x_force||5.006000| SvPVutf8x||5.006000| SvPVutf8||5.006000| SvPVx||| SvPV||| SvREFCNT_dec_NN||5.017007| SvREFCNT_dec||| SvREFCNT_inc_NN|5.009004||p SvREFCNT_inc_simple_NN|5.009004||p SvREFCNT_inc_simple_void_NN|5.009004||p SvREFCNT_inc_simple_void|5.009004||p SvREFCNT_inc_simple|5.009004||p SvREFCNT_inc_void_NN|5.009004||p SvREFCNT_inc_void|5.009004||p SvREFCNT_inc|||p SvREFCNT||| SvROK_off||| SvROK_on||| SvROK||| SvRV_set|5.009003||p SvRV||| SvRXOK||5.009005| SvRX||5.009005| SvSETMAGIC||| SvSHARED_HASH|5.009003||p SvSHARE||5.007003| SvSTASH_set|5.009003||p SvSTASH||| SvSetMagicSV_nosteal||5.004000| SvSetMagicSV||5.004000| SvSetSV_nosteal||5.004000| SvSetSV||| SvTAINTED_off||5.004000| SvTAINTED_on||5.004000| SvTAINTED||5.004000| SvTAINT||| SvTHINKFIRST||| SvTRUE_nomg||5.013006| SvTRUE||| SvTYPE||| SvUNLOCK||5.007003| SvUOK|5.007001|5.006000|p SvUPGRADE||| SvUTF8_off||5.006000| SvUTF8_on||5.006000| SvUTF8||5.006000| SvUVXx|5.004000||p SvUVX|5.004000||p SvUV_nomg|5.009001||p SvUV_set|5.009003||p SvUVx|5.004000||p SvUV|5.004000||p SvVOK||5.008001| SvVSTRING_mg|5.009004||p THIS|||n UNDERBAR|5.009002||p UTF8_MAXBYTES|5.009002||p UVSIZE|5.006000||p UVTYPE|5.006000||p UVXf|5.007001||p UVof|5.006000||p UVuf|5.006000||p UVxf|5.006000||p WARN_ALL|5.006000||p WARN_AMBIGUOUS|5.006000||p WARN_ASSERTIONS|5.019002||p WARN_BAREWORD|5.006000||p WARN_CLOSED|5.006000||p WARN_CLOSURE|5.006000||p WARN_DEBUGGING|5.006000||p WARN_DEPRECATED|5.006000||p WARN_DIGIT|5.006000||p WARN_EXEC|5.006000||p WARN_EXITING|5.006000||p WARN_GLOB|5.006000||p WARN_INPLACE|5.006000||p WARN_INTERNAL|5.006000||p WARN_IO|5.006000||p WARN_LAYER|5.008000||p WARN_MALLOC|5.006000||p WARN_MISC|5.006000||p WARN_NEWLINE|5.006000||p WARN_NUMERIC|5.006000||p WARN_ONCE|5.006000||p WARN_OVERFLOW|5.006000||p WARN_PACK|5.006000||p WARN_PARENTHESIS|5.006000||p WARN_PIPE|5.006000||p WARN_PORTABLE|5.006000||p WARN_PRECEDENCE|5.006000||p WARN_PRINTF|5.006000||p WARN_PROTOTYPE|5.006000||p WARN_QW|5.006000||p WARN_RECURSION|5.006000||p WARN_REDEFINE|5.006000||p WARN_REGEXP|5.006000||p WARN_RESERVED|5.006000||p WARN_SEMICOLON|5.006000||p WARN_SEVERE|5.006000||p WARN_SIGNAL|5.006000||p WARN_SUBSTR|5.006000||p WARN_SYNTAX|5.006000||p WARN_TAINT|5.006000||p WARN_THREADS|5.008000||p WARN_UNINITIALIZED|5.006000||p WARN_UNOPENED|5.006000||p WARN_UNPACK|5.006000||p WARN_UNTIE|5.006000||p WARN_UTF8|5.006000||p WARN_VOID|5.006000||p WIDEST_UTYPE|5.015004||p XCPT_CATCH|5.009002||p XCPT_RETHROW|5.009002|5.007001|p XCPT_TRY_END|5.009002|5.004000|p XCPT_TRY_START|5.009002|5.004000|p XPUSHi||| XPUSHmortal|5.009002||p XPUSHn||| XPUSHp||| XPUSHs||| XPUSHu|5.004000||p XSPROTO|5.010000||p XSRETURN_EMPTY||| XSRETURN_IV||| XSRETURN_NO||| XSRETURN_NV||| XSRETURN_PV||| XSRETURN_UNDEF||| XSRETURN_UV|5.008001||p XSRETURN_YES||| XSRETURN|||p XST_mIV||| XST_mNO||| XST_mNV||| XST_mPV||| XST_mUNDEF||| XST_mUV|5.008001||p XST_mYES||| XS_APIVERSION_BOOTCHECK||5.013004| XS_EXTERNAL||5.019003| XS_INTERNAL||5.019003| XS_VERSION_BOOTCHECK||| XS_VERSION||| XSprePUSH|5.006000||p XS||| XopDISABLE||5.019003| XopENABLE||5.019003| XopENTRY_set||5.019003| XopENTRY||5.019003| XopFLAGS||5.013007| ZeroD|5.009002||p Zero||| _aMY_CXT|5.007003||p _add_range_to_invlist||| _append_range_to_invlist||| _core_swash_init||| _get_swash_invlist||| _invlist_array_init||| _invlist_contains_cp||| _invlist_contents||| _invlist_dump||| _invlist_intersection_maybe_complement_2nd||| _invlist_intersection||| _invlist_invert_prop||| _invlist_invert||| _invlist_len||| _invlist_populate_swatch||| _invlist_search||| _invlist_subtract||| _invlist_union_maybe_complement_2nd||| _invlist_union||| _is_uni_FOO||5.017008| _is_uni_perl_idcont||5.017008| _is_uni_perl_idstart||5.017007| _is_utf8_FOO||5.017008| _is_utf8_mark||5.017008| _is_utf8_perl_idcont||5.017008| _is_utf8_perl_idstart||5.017007| _new_invlist_C_array||| _new_invlist||| _pMY_CXT|5.007003||p _swash_inversion_hash||| _swash_to_invlist||| _to_fold_latin1||| _to_uni_fold_flags||5.013011| _to_upper_title_latin1||| _to_utf8_fold_flags||5.015006| _to_utf8_lower_flags||5.015006| _to_utf8_title_flags||5.015006| _to_utf8_upper_flags||5.015006| aMY_CXT_|5.007003||p aMY_CXT|5.007003||p aTHXR_|5.019002||p aTHXR|5.019002||p aTHX_|5.006000||p aTHX|5.006000||p aassign_common_vars||| add_cp_to_invlist||| add_data|||n add_utf16_textfilter||| addmad||| adjust_size_and_find_bucket|||n adjust_stack_on_leave||| alloc_maybe_populate_EXACT||| alloccopstash||| allocmy||| amagic_call||| amagic_cmp_locale||| amagic_cmp||| amagic_deref_call||5.013007| amagic_i_ncmp||| amagic_is_enabled||| amagic_ncmp||| anonymise_cv_maybe||| any_dup||| ao||| append_madprops||| apply_attrs_my||| apply_attrs_string||5.006001| apply_attrs||| apply||| assert_uft8_cache_coherent||| atfork_lock||5.007003|n atfork_unlock||5.007003|n av_arylen_p||5.009003| av_clear||| av_create_and_push||5.009005| av_create_and_unshift_one||5.009005| av_delete||5.006000| av_exists||5.006000| av_extend_guts||| av_extend||| av_fetch||| av_fill||| av_iter_p||5.011000| av_len||| av_make||| av_pop||| av_push||| av_reify||| av_shift||| av_store||| av_tindex||5.017009| av_top_index||5.017009| av_undef||| av_unshift||| ax|||n bad_type_gv||| bad_type_pv||| bind_match||| block_end||| block_gimme||5.004000| block_start||| blockhook_register||5.013003| boolSV|5.004000||p boot_core_PerlIO||| boot_core_UNIVERSAL||| boot_core_mro||| bytes_cmp_utf8||5.013007| bytes_from_utf8||5.007001| bytes_to_uni|||n bytes_to_utf8||5.006001| call_argv|5.006000||p call_atexit||5.006000| call_list||5.004000| call_method|5.006000||p call_pv|5.006000||p call_sv|5.006000||p caller_cx||5.013005| calloc||5.007002|n cando||| cast_i32||5.006000| cast_iv||5.006000| cast_ulong||5.006000| cast_uv||5.006000| check_locale_boundary_crossing||| check_type_and_open||| check_uni||| check_utf8_print||| checkcomma||| ckWARN|5.006000||p ck_entersub_args_core||| ck_entersub_args_list||5.013006| ck_entersub_args_proto_or_list||5.013006| ck_entersub_args_proto||5.013006| ck_warner_d||5.011001|v ck_warner||5.011001|v ckwarn_common||| ckwarn_d||5.009003| ckwarn||5.009003| cl_and|||n cl_anything|||n cl_init|||n cl_is_anything|||n cl_or|||n clear_placeholders||| clone_params_del|||n clone_params_new|||n closest_cop||| compute_EXACTish||| convert||| cop_fetch_label||5.015001| cop_free||| cop_hints_2hv||5.013007| cop_hints_fetch_pvn||5.013007| cop_hints_fetch_pvs||5.013007| cop_hints_fetch_pv||5.013007| cop_hints_fetch_sv||5.013007| cop_store_label||5.015001| cophh_2hv||5.013007| cophh_copy||5.013007| cophh_delete_pvn||5.013007| cophh_delete_pvs||5.013007| cophh_delete_pv||5.013007| cophh_delete_sv||5.013007| cophh_fetch_pvn||5.013007| cophh_fetch_pvs||5.013007| cophh_fetch_pv||5.013007| cophh_fetch_sv||5.013007| cophh_free||5.013007| cophh_new_empty||5.019003| cophh_store_pvn||5.013007| cophh_store_pvs||5.013007| cophh_store_pv||5.013007| cophh_store_sv||5.013007| core_prototype||| core_regclass_swash||| coresub_op||| could_it_be_a_POSIX_class||| cr_textfilter||| create_eval_scope||| croak_memory_wrap||5.019003|n croak_no_mem|||n croak_no_modify||5.013003|n croak_nocontext|||vn croak_popstack|||n croak_sv||5.013001| croak_xs_usage||5.010001|n croak|||v csighandler||5.009003|n curmad||| current_re_engine||| curse||| custom_op_desc||5.007003| custom_op_name||5.007003| custom_op_register||5.013007| custom_op_xop||5.013007| cv_ckproto_len_flags||| cv_clone_into||| cv_clone||| cv_const_sv_or_av||| cv_const_sv||5.004000| cv_dump||| cv_forget_slab||| cv_get_call_checker||5.013006| cv_set_call_checker||5.013006| cv_undef||| cvgv_set||| cvstash_set||| cx_dump||5.005000| cx_dup||| cxinc||| dAXMARK|5.009003||p dAX|5.007002||p dITEMS|5.007002||p dMARK||| dMULTICALL||5.009003| dMY_CXT_SV|5.007003||p dMY_CXT|5.007003||p dNOOP|5.006000||p dORIGMARK||| dSP||| dTHR|5.004050||p dTHXR|5.019002||p dTHXa|5.006000||p dTHXoa|5.006000||p dTHX|5.006000||p dUNDERBAR|5.009002||p dVAR|5.009003||p dXCPT|5.009002||p dXSARGS||| dXSI32||| dXSTARG|5.006000||p deb_curcv||| deb_nocontext|||vn deb_stack_all||| deb_stack_n||| debop||5.005000| debprofdump||5.005000| debprof||| debstackptrs||5.007003| debstack||5.007003| debug_start_match||| deb||5.007003|v defelem_target||| del_sv||| delete_eval_scope||| delimcpy||5.004000|n deprecate_commaless_var_list||| despatch_signals||5.007001| destroy_matcher||| die_nocontext|||vn die_sv||5.013001| die_unwind||| die|||v dirp_dup||| div128||| djSP||| do_aexec5||| do_aexec||| do_aspawn||| do_binmode||5.004050| do_chomp||| do_close||| do_delete_local||| do_dump_pad||| do_eof||| do_exec3||| do_execfree||| do_exec||| do_gv_dump||5.006000| do_gvgv_dump||5.006000| do_hv_dump||5.006000| do_ipcctl||| do_ipcget||| do_join||| do_magic_dump||5.006000| do_msgrcv||| do_msgsnd||| do_ncmp||| do_oddball||| do_op_dump||5.006000| do_op_xmldump||| do_open9||5.006000| do_openn||5.007001| do_open||5.004000| do_pmop_dump||5.006000| do_pmop_xmldump||| do_print||| do_readline||| do_seek||| do_semop||| do_shmio||| do_smartmatch||| do_spawn_nowait||| do_spawn||| do_sprintf||| do_sv_dump||5.006000| do_sysseek||| do_tell||| do_trans_complex_utf8||| do_trans_complex||| do_trans_count_utf8||| do_trans_count||| do_trans_simple_utf8||| do_trans_simple||| do_trans||| do_vecget||| do_vecset||| do_vop||| docatch||| doeval||| dofile||| dofindlabel||| doform||| doing_taint||5.008001|n dooneliner||| doopen_pm||| doparseform||| dopoptoeval||| dopoptogiven||| dopoptolabel||| dopoptoloop||| dopoptosub_at||| dopoptowhen||| doref||5.009003| dounwind||| dowantarray||| dump_all_perl||| dump_all||5.006000| dump_eval||5.006000| dump_exec_pos||| dump_fds||| dump_form||5.006000| dump_indent||5.006000|v dump_mstats||| dump_packsubs_perl||| dump_packsubs||5.006000| dump_sub_perl||| dump_sub||5.006000| dump_sv_child||| dump_trie_interim_list||| dump_trie_interim_table||| dump_trie||| dump_vindent||5.006000| dumpuntil||| dup_attrlist||| emulate_cop_io||| eval_pv|5.006000||p eval_sv|5.006000||p exec_failed||| expect_number||| fbm_compile||5.005000| fbm_instr||5.005000| feature_is_enabled||| filter_add||| filter_del||| filter_gets||| filter_read||| finalize_optree||| finalize_op||| find_and_forget_pmops||| find_array_subscript||| find_beginning||| find_byclass||| find_hash_subscript||| find_in_my_stash||| find_lexical_cv||| find_runcv_where||| find_runcv||5.008001| find_rundefsv2||| find_rundefsvoffset||5.009002| find_rundefsv||5.013002| find_script||| find_uninit_var||| first_symbol|||n foldEQ_latin1||5.013008|n foldEQ_locale||5.013002|n foldEQ_utf8_flags||5.013010| foldEQ_utf8||5.013002| foldEQ||5.013002|n fold_constants||| forbid_setid||| force_ident_maybe_lex||| force_ident||| force_list||| force_next||| force_strict_version||| force_version||| force_word||| forget_pmop||| form_nocontext|||vn form_short_octal_warning||| form||5.004000|v fp_dup||| fprintf_nocontext|||vn free_global_struct||| free_tied_hv_pool||| free_tmps||| gen_constant_list||| get_and_check_backslash_N_name||| get_aux_mg||| get_av|5.006000||p get_context||5.006000|n get_cvn_flags|5.009005||p get_cvs|5.011000||p get_cv|5.006000||p get_db_sub||| get_debug_opts||| get_hash_seed||| get_hv|5.006000||p get_invlist_iter_addr||| get_invlist_offset_addr||| get_invlist_previous_index_addr||| get_mstats||| get_no_modify||| get_num||| get_op_descs||5.005000| get_op_names||5.005000| get_opargs||| get_ppaddr||5.006000| get_re_arg||| get_sv|5.006000||p get_vtbl||5.005030| getcwd_sv||5.007002| getenv_len||| glob_2number||| glob_assign_glob||| glob_assign_ref||| gp_dup||| gp_free||| gp_ref||| grok_bin|5.007003||p grok_bslash_N||| grok_bslash_c||| grok_bslash_o||| grok_bslash_x||| grok_hex|5.007003||p grok_number|5.007002||p grok_numeric_radix|5.007002||p grok_oct|5.007003||p group_end||| gv_AVadd||| gv_HVadd||| gv_IOadd||| gv_SVadd||| gv_add_by_type||5.011000| gv_autoload4||5.004000| gv_autoload_pvn||5.015004| gv_autoload_pv||5.015004| gv_autoload_sv||5.015004| gv_check||| gv_const_sv||5.009003| gv_dump||5.006000| gv_efullname3||5.004000| gv_efullname4||5.006001| gv_efullname||| gv_ename||| gv_fetchfile_flags||5.009005| gv_fetchfile||| gv_fetchmeth_autoload||5.007003| gv_fetchmeth_pv_autoload||5.015004| gv_fetchmeth_pvn_autoload||5.015004| gv_fetchmeth_pvn||5.015004| gv_fetchmeth_pv||5.015004| gv_fetchmeth_sv_autoload||5.015004| gv_fetchmeth_sv||5.015004| gv_fetchmethod_autoload||5.004000| gv_fetchmethod_pv_flags||5.015004| gv_fetchmethod_pvn_flags||5.015004| gv_fetchmethod_sv_flags||5.015004| gv_fetchmethod||| gv_fetchmeth||| gv_fetchpvn_flags|5.009002||p gv_fetchpvs|5.009004||p gv_fetchpv||| gv_fetchsv|5.009002||p gv_fullname3||5.004000| gv_fullname4||5.006001| gv_fullname||| gv_handler||5.007001| gv_init_pvn||5.015004| gv_init_pv||5.015004| gv_init_svtype||| gv_init_sv||5.015004| gv_init||| gv_magicalize_isa||| gv_name_set||5.009004| gv_stashpvn|5.004000||p gv_stashpvs|5.009003||p gv_stashpv||| gv_stashsv||| gv_try_downgrade||| handle_regex_sets||| he_dup||| hek_dup||| hfree_next_entry||| hfreeentries||| hsplit||| hv_assert||| hv_auxinit||| hv_backreferences_p||| hv_clear_placeholders||5.009001| hv_clear||| hv_common_key_len||5.010000| hv_common||5.010000| hv_copy_hints_hv||5.009004| hv_delayfree_ent||5.004000| hv_delete_common||| hv_delete_ent||5.004000| hv_delete||| hv_eiter_p||5.009003| hv_eiter_set||5.009003| hv_ename_add||| hv_ename_delete||| hv_exists_ent||5.004000| hv_exists||| hv_fetch_ent||5.004000| hv_fetchs|5.009003||p hv_fetch||| hv_fill||5.013002| hv_free_ent_ret||| hv_free_ent||5.004000| hv_iterinit||| hv_iterkeysv||5.004000| hv_iterkey||| hv_iternext_flags||5.008000| hv_iternextsv||| hv_iternext||| hv_iterval||| hv_kill_backrefs||| hv_ksplit||5.004000| hv_magic_check|||n hv_magic||| hv_name_set||5.009003| hv_notallowed||| hv_placeholders_get||5.009003| hv_placeholders_p||| hv_placeholders_set||5.009003| hv_rand_set||5.017011| hv_riter_p||5.009003| hv_riter_set||5.009003| hv_scalar||5.009001| hv_store_ent||5.004000| hv_store_flags||5.008000| hv_stores|5.009004||p hv_store||| hv_undef_flags||| hv_undef||| ibcmp_locale||5.004000| ibcmp_utf8||5.007003| ibcmp||| incline||| incpush_if_exists||| incpush_use_sep||| incpush||| ingroup||| init_argv_symbols||| init_constants||| init_dbargs||| init_debugger||| init_global_struct||| init_i18nl10n||5.006000| init_i18nl14n||5.006000| init_ids||| init_interp||| init_main_stash||| init_perllib||| init_postdump_symbols||| init_predump_symbols||| init_stacks||5.005000| init_tm||5.007002| inplace_aassign||| instr|||n intro_my||| intuit_method||| intuit_more||| invert||| invlist_array||| invlist_clone||| invlist_extend||| invlist_highest||| invlist_is_iterating||| invlist_iterfinish||| invlist_iterinit||| invlist_iternext||| invlist_max||| invlist_previous_index||| invlist_set_len||| invlist_set_previous_index||| invlist_trim||| invoke_exception_hook||| io_close||| isALNUMC|5.006000||p isALNUM_lazy||| isALPHANUMERIC||5.017008| isALPHA||| isASCII|5.006000|5.006000|p isBLANK|5.006001||p isCNTRL|5.006000|5.006000|p isDIGIT||| isFOO_lc||| isFOO_utf8_lc||| isGRAPH|5.006000||p isGV_with_GP|5.009004||p isIDCONT||5.017008| isIDFIRST_lazy||| isIDFIRST||| isLOWER||| isOCTAL||5.013005| isPRINT|5.004000||p isPSXSPC|5.006001||p isPUNCT|5.006000||p isSPACE||| isUPPER||| isWORDCHAR||5.013006| isXDIGIT|5.006000||p is_an_int||| is_ascii_string||5.011000|n is_cur_LC_category_utf8||| is_handle_constructor|||n is_list_assignment||| is_lvalue_sub||5.007001| is_uni_alnum_lc||5.006000| is_uni_alnumc_lc||5.017007| is_uni_alnumc||5.017007| is_uni_alnum||5.006000| is_uni_alpha_lc||5.006000| is_uni_alpha||5.006000| is_uni_ascii_lc||5.006000| is_uni_ascii||5.006000| is_uni_blank_lc||5.017002| is_uni_blank||5.017002| is_uni_cntrl_lc||5.006000| is_uni_cntrl||5.006000| is_uni_digit_lc||5.006000| is_uni_digit||5.006000| is_uni_graph_lc||5.006000| is_uni_graph||5.006000| is_uni_idfirst_lc||5.006000| is_uni_idfirst||5.006000| is_uni_lower_lc||5.006000| is_uni_lower||5.006000| is_uni_print_lc||5.006000| is_uni_print||5.006000| is_uni_punct_lc||5.006000| is_uni_punct||5.006000| is_uni_space_lc||5.006000| is_uni_space||5.006000| is_uni_upper_lc||5.006000| is_uni_upper||5.006000| is_uni_xdigit_lc||5.006000| is_uni_xdigit||5.006000| is_utf8_alnumc||5.017007| is_utf8_alnum||5.006000| is_utf8_alpha||5.006000| is_utf8_ascii||5.006000| is_utf8_blank||5.017002| is_utf8_char_buf||5.015008|n is_utf8_char_slow|||n is_utf8_char||5.006000|n is_utf8_cntrl||5.006000| is_utf8_common||| is_utf8_digit||5.006000| is_utf8_graph||5.006000| is_utf8_idcont||5.008000| is_utf8_idfirst||5.006000| is_utf8_lower||5.006000| is_utf8_mark||5.006000| is_utf8_perl_space||5.011001| is_utf8_perl_word||5.011001| is_utf8_posix_digit||5.011001| is_utf8_print||5.006000| is_utf8_punct||5.006000| is_utf8_space||5.006000| is_utf8_string_loclen||5.009003|n is_utf8_string_loc||5.008001|n is_utf8_string||5.006001|n is_utf8_upper||5.006000| is_utf8_xdigit||5.006000| is_utf8_xidcont||5.013010| is_utf8_xidfirst||5.013010| isa_lookup||| items|||n ix|||n jmaybe||| join_exact||| keyword_plugin_standard||| keyword||| leave_scope||| lex_bufutf8||5.011002| lex_discard_to||5.011002| lex_grow_linestr||5.011002| lex_next_chunk||5.011002| lex_peek_unichar||5.011002| lex_read_space||5.011002| lex_read_to||5.011002| lex_read_unichar||5.011002| lex_start||5.009005| lex_stuff_pvn||5.011002| lex_stuff_pvs||5.013005| lex_stuff_pv||5.013006| lex_stuff_sv||5.011002| lex_unstuff||5.011002| listkids||| list||| load_module_nocontext|||vn load_module|5.006000||pv localize||| looks_like_bool||| looks_like_number||| lop||| mPUSHi|5.009002||p mPUSHn|5.009002||p mPUSHp|5.009002||p mPUSHs|5.010001||p mPUSHu|5.009002||p mXPUSHi|5.009002||p mXPUSHn|5.009002||p mXPUSHp|5.009002||p mXPUSHs|5.010001||p mXPUSHu|5.009002||p mad_free||| madlex||| madparse||| magic_clear_all_env||| magic_cleararylen_p||| magic_clearenv||| magic_clearhints||| magic_clearhint||| magic_clearisa||| magic_clearpack||| magic_clearsig||| magic_copycallchecker||| magic_dump||5.006000| magic_existspack||| magic_freearylen_p||| magic_freeovrld||| magic_getarylen||| magic_getdefelem||| magic_getnkeys||| magic_getpack||| magic_getpos||| magic_getsig||| magic_getsubstr||| magic_gettaint||| magic_getuvar||| magic_getvec||| magic_get||| magic_killbackrefs||| magic_methcall1||| magic_methcall|||v magic_methpack||| magic_nextpack||| magic_regdata_cnt||| magic_regdatum_get||| magic_regdatum_set||| magic_scalarpack||| magic_set_all_env||| magic_setarylen||| magic_setcollxfrm||| magic_setdbline||| magic_setdefelem||| magic_setenv||| magic_sethint||| magic_setisa||| magic_setmglob||| magic_setnkeys||| magic_setpack||| magic_setpos||| magic_setregexp||| magic_setsig||| magic_setsubstr||| magic_settaint||| magic_setutf8||| magic_setuvar||| magic_setvec||| magic_set||| magic_sizepack||| magic_wipepack||| make_matcher||| make_trie_failtable||| make_trie||| malloc_good_size|||n malloced_size|||n malloc||5.007002|n markstack_grow||| matcher_matches_sv||| mayberelocate||| measure_struct||| memEQs|5.009005||p memEQ|5.004000||p memNEs|5.009005||p memNE|5.004000||p mem_collxfrm||| mem_log_common|||n mess_alloc||| mess_nocontext|||vn mess_sv||5.013001| mess||5.006000|v method_common||| mfree||5.007002|n mg_clear||| mg_copy||| mg_dup||| mg_find_mglob||| mg_findext||5.013008| mg_find||| mg_free_type||5.013006| mg_free||| mg_get||| mg_length||5.005000| mg_localize||| mg_magical||| mg_set||| mg_size||5.005000| mini_mktime||5.007002| minus_v||| missingterm||| mode_from_discipline||| modkids||| more_bodies||| more_sv||| moreswitches||| mro_clean_isarev||| mro_gather_and_rename||| mro_get_from_name||5.010001| mro_get_linear_isa_dfs||| mro_get_linear_isa||5.009005| mro_get_private_data||5.010001| mro_isa_changed_in||| mro_meta_dup||| mro_meta_init||| mro_method_changed_in||5.009005| mro_package_moved||| mro_register||5.010001| mro_set_mro||5.010001| mro_set_private_data||5.010001| mul128||| mulexp10|||n my_atof2||5.007002| my_atof||5.006000| my_attrs||| my_bcopy|||n my_bzero|||n my_chsize||| my_clearenv||| my_cxt_index||| my_cxt_init||| my_dirfd||5.009005| my_exit_jump||| my_exit||| my_failure_exit||5.004000| my_fflush_all||5.006000| my_fork||5.007003|n my_kid||| my_lstat_flags||| my_lstat||5.019003| my_memcmp|||n my_memset||5.004000|n my_pclose||5.004000| my_popen_list||5.007001| my_popen||5.004000| my_setenv||| my_snprintf|5.009004||pvn my_socketpair||5.007003|n my_sprintf|5.009003||pvn my_stat_flags||| my_stat||5.019003| my_strftime||5.007002| my_strlcat|5.009004||pn my_strlcpy|5.009004||pn my_unexec||| my_vsnprintf||5.009004|n need_utf8|||n newANONATTRSUB||5.006000| newANONHASH||| newANONLIST||| newANONSUB||| newASSIGNOP||| newATTRSUB_flags||| newATTRSUB||5.006000| newAVREF||| newAV||| newBINOP||| newCONDOP||| newCONSTSUB_flags||5.015006| newCONSTSUB|5.004050||p newCVREF||| newDEFSVOP||| newFORM||| newFOROP||5.013007| newGIVENOP||5.009003| newGIVWHENOP||| newGP||| newGVOP||| newGVREF||| newGVgen_flags||5.015004| newGVgen||| newHVREF||| newHVhv||5.005000| newHV||| newIO||| newLISTOP||| newLOGOP||| newLOOPEX||| newLOOPOP||| newMADPROP||| newMADsv||| newMYSUB||5.017004| newNULLLIST||| newOP||| newPADOP||| newPMOP||| newPROG||| newPVOP||| newRANGE||| newRV_inc|5.004000||p newRV_noinc|5.004000||p newRV||| newSLICEOP||| newSTATEOP||| newSTUB||| newSUB||| newSVOP||| newSVREF||| newSV_type|5.009005||p newSVhek||5.009003| newSViv||| newSVnv||| newSVpadname||5.017004| newSVpv_share||5.013006| newSVpvf_nocontext|||vn newSVpvf||5.004000|v newSVpvn_flags|5.010001||p newSVpvn_share|5.007001||p newSVpvn_utf8|5.010001||p newSVpvn|5.004050||p newSVpvs_flags|5.010001||p newSVpvs_share|5.009003||p newSVpvs|5.009003||p newSVpv||| newSVrv||| newSVsv||| newSVuv|5.006000||p newSV||| newTOKEN||| newUNOP||| newWHENOP||5.009003| newWHILEOP||5.013007| newXS_flags||5.009004| newXS_len_flags||| newXSproto||5.006000| newXS||5.006000| new_collate||5.006000| new_constant||| new_ctype||5.006000| new_he||| new_logop||| new_numeric||5.006000| new_stackinfo||5.005000| new_version||5.009000| new_warnings_bitfield||| next_symbol||| nextargv||| nextchar||| ninstr|||n no_bareword_allowed||| no_fh_allowed||| no_op||| not_a_number||| not_incrementable||| nothreadhook||5.008000| nuke_stacks||| num_overflow|||n oopsAV||| oopsHV||| op_append_elem||5.013006| op_append_list||5.013006| op_clear||| op_const_sv||| op_contextualize||5.013006| op_dump||5.006000| op_free||| op_getmad_weak||| op_getmad||| op_integerize||| op_linklist||5.013006| op_lvalue_flags||| op_lvalue||5.013007| op_null||5.007002| op_prepend_elem||5.013006| op_refcnt_dec||| op_refcnt_inc||| op_refcnt_lock||5.009002| op_refcnt_unlock||5.009002| op_scope||5.013007| op_std_init||| op_unscope||| op_xmldump||| open_script||| opslab_force_free||| opslab_free_nopad||| opslab_free||| pMY_CXT_|5.007003||p pMY_CXT|5.007003||p pTHX_|5.006000||p pTHX|5.006000||p packWARN|5.007003||p pack_cat||5.007003| pack_rec||| package_version||| package||| packlist||5.008001| pad_add_anon||5.008001| pad_add_name_pvn||5.015001| pad_add_name_pvs||5.015001| pad_add_name_pv||5.015001| pad_add_name_sv||5.015001| pad_alloc_name||| pad_alloc||| pad_block_start||| pad_check_dup||| pad_compname_type||5.009003| pad_findlex||| pad_findmy_pvn||5.015001| pad_findmy_pvs||5.015001| pad_findmy_pv||5.015001| pad_findmy_sv||5.015001| pad_fixup_inner_anons||| pad_free||| pad_leavemy||| pad_new||5.008001| pad_peg|||n pad_push||| pad_reset||| pad_setsv||| pad_sv||| pad_swipe||| pad_tidy||5.008001| padlist_dup||| padlist_store||| parse_arithexpr||5.013008| parse_barestmt||5.013007| parse_block||5.013007| parse_body||| parse_fullexpr||5.013008| parse_fullstmt||5.013005| parse_ident||| parse_label||5.013007| parse_listexpr||5.013008| parse_lparen_question_flags||| parse_stmtseq||5.013006| parse_termexpr||5.013008| parse_unicode_opts||| parser_dup||| parser_free_nexttoke_ops||| parser_free||| path_is_searchable|||n peep||| pending_ident||| perl_alloc_using|||n perl_alloc|||n perl_clone_using|||n perl_clone|||n perl_construct|||n perl_destruct||5.007003|n perl_free|||n perl_parse||5.006000|n perl_run|||n pidgone||| pm_description||| pmop_dump||5.006000| pmop_xmldump||| pmruntime||| pmtrans||| pop_scope||| populate_isa|||v pregcomp||5.009005| pregexec||| pregfree2||5.011000| pregfree||| prepend_madprops||| prescan_version||5.011004| printbuf||| printf_nocontext|||vn process_special_blocks||| ptr_hash|||n ptr_table_clear||5.009005| ptr_table_fetch||5.009005| ptr_table_find|||n ptr_table_free||5.009005| ptr_table_new||5.009005| ptr_table_split||5.009005| ptr_table_store||5.009005| push_scope||| put_byte||| put_latin1_charclass_innards||| pv_display|5.006000||p pv_escape|5.009004||p pv_pretty|5.009004||p pv_uni_display||5.007003| qerror||| qsortsvu||| re_compile||5.009005| re_croak2||| re_dup_guts||| re_intuit_start||5.019001| re_intuit_string||5.006000| re_op_compile||| readpipe_override||| realloc||5.007002|n reentrant_free||5.019003| reentrant_init||5.019003| reentrant_retry||5.019003|vn reentrant_size||5.019003| ref_array_or_hash||| refcounted_he_chain_2hv||| refcounted_he_fetch_pvn||| refcounted_he_fetch_pvs||| refcounted_he_fetch_pv||| refcounted_he_fetch_sv||| refcounted_he_free||| refcounted_he_inc||| refcounted_he_new_pvn||| refcounted_he_new_pvs||| refcounted_he_new_pv||| refcounted_he_new_sv||| refcounted_he_value||| refkids||| refto||| ref||5.019003| reg_check_named_buff_matched||| reg_named_buff_all||5.009005| reg_named_buff_exists||5.009005| reg_named_buff_fetch||5.009005| reg_named_buff_firstkey||5.009005| reg_named_buff_iter||| reg_named_buff_nextkey||5.009005| reg_named_buff_scalar||5.009005| reg_named_buff||| reg_node||| reg_numbered_buff_fetch||| reg_numbered_buff_length||| reg_numbered_buff_store||| reg_qr_package||| reg_recode||| reg_scan_name||| reg_skipcomment||| reg_temp_copy||| reganode||| regatom||| regbranch||| regclass_swash||5.009004| regclass||| regcppop||| regcppush||| regcurly||| regdump_extflags||| regdump_intflags||| regdump||5.005000| regdupe_internal||| regexec_flags||5.005000| regfree_internal||5.009005| reghop3|||n reghop4|||n reghopmaybe3|||n reginclass||| reginitcolors||5.006000| reginsert||| regmatch||| regnext||5.005000| regpatws|||n regpiece||| regpposixcc||| regprop||| regrepeat||| regtail_study||| regtail||| regtry||| reguni||| regwhite|||n reg||| repeatcpy|||n report_evil_fh||| report_redefined_cv||| report_uninit||| report_wrongway_fh||| require_pv||5.006000| require_tie_mod||| restore_magic||| rninstr|||n rpeep||| rsignal_restore||| rsignal_save||| rsignal_state||5.004000| rsignal||5.004000| run_body||| run_user_filter||| runops_debug||5.005000| runops_standard||5.005000| rv2cv_op_cv||5.013006| rvpv_dup||| rxres_free||| rxres_restore||| rxres_save||| safesyscalloc||5.006000|n safesysfree||5.006000|n safesysmalloc||5.006000|n safesysrealloc||5.006000|n same_dirent||| save_I16||5.004000| save_I32||| save_I8||5.006000| save_adelete||5.011000| save_aelem_flags||5.011000| save_aelem||5.004050| save_alloc||5.006000| save_aptr||| save_ary||| save_bool||5.008001| save_clearsv||| save_delete||| save_destructor_x||5.006000| save_destructor||5.006000| save_freeop||| save_freepv||| save_freesv||| save_generic_pvref||5.006001| save_generic_svref||5.005030| save_gp||5.004000| save_hash||| save_hdelete||5.011000| save_hek_flags|||n save_helem_flags||5.011000| save_helem||5.004050| save_hints||5.010001| save_hptr||| save_int||| save_item||| save_iv||5.005000| save_lines||| save_list||| save_long||| save_magic_flags||| save_mortalizesv||5.007001| save_nogv||| save_op||5.005000| save_padsv_and_mortalize||5.010001| save_pptr||| save_pushi32ptr||5.010001| save_pushptri32ptr||| save_pushptrptr||5.010001| save_pushptr||5.010001| save_re_context||5.006000| save_scalar_at||| save_scalar||| save_set_svflags||5.009000| save_shared_pvref||5.007003| save_sptr||| save_svref||| save_vptr||5.006000| savepvn||| savepvs||5.009003| savepv||| savesharedpvn||5.009005| savesharedpvs||5.013006| savesharedpv||5.007003| savesharedsvpv||5.013006| savestack_grow_cnt||5.008001| savestack_grow||| savesvpv||5.009002| sawparens||| scalar_mod_type|||n scalarboolean||| scalarkids||| scalarseq||| scalarvoid||| scalar||| scan_bin||5.006000| scan_commit||| scan_const||| scan_formline||| scan_heredoc||| scan_hex||| scan_ident||| scan_inputsymbol||| scan_num||5.007001| scan_oct||| scan_pat||| scan_str||| scan_subst||| scan_trans||| scan_version||5.009001| scan_vstring||5.009005| scan_word||| screaminstr||5.005000| search_const||| seed||5.008001| sequence_num||| set_context||5.006000|n set_numeric_local||5.006000| set_numeric_radix||5.006000| set_numeric_standard||5.006000| setdefout||| share_hek_flags||| share_hek||5.004000| si_dup||| sighandler|||n simplify_sort||| skipspace0||| skipspace1||| skipspace2||| skipspace_flags||| softref2xv||| sortcv_stacked||| sortcv_xsub||| sortcv||| sortsv_flags||5.009003| sortsv||5.007003| space_join_names_mortal||| ss_dup||| stack_grow||| start_force||| start_glob||| start_subparse||5.004000| stdize_locale||| strEQ||| strGE||| strGT||| strLE||| strLT||| strNE||| str_to_version||5.006000| strip_return||| strnEQ||| strnNE||| study_chunk||| sub_crush_depth||| sublex_done||| sublex_push||| sublex_start||| sv_2bool_flags||5.013006| sv_2bool||| sv_2cv||| sv_2io||| sv_2iuv_common||| sv_2iuv_non_preserve||| sv_2iv_flags||5.009001| sv_2iv||| sv_2mortal||| sv_2num||| sv_2nv_flags||5.013001| sv_2pv_flags|5.007002||p sv_2pv_nolen|5.006000||p sv_2pvbyte_nolen|5.006000||p sv_2pvbyte|5.006000||p sv_2pvutf8_nolen||5.006000| sv_2pvutf8||5.006000| sv_2pv||| sv_2uv_flags||5.009001| sv_2uv|5.004000||p sv_add_arena||| sv_add_backref||| sv_backoff||| sv_bless||| sv_cat_decode||5.008001| sv_catpv_flags||5.013006| sv_catpv_mg|5.004050||p sv_catpv_nomg||5.013006| sv_catpvf_mg_nocontext|||pvn sv_catpvf_mg|5.006000|5.004000|pv sv_catpvf_nocontext|||vn sv_catpvf||5.004000|v sv_catpvn_flags||5.007002| sv_catpvn_mg|5.004050||p sv_catpvn_nomg|5.007002||p sv_catpvn||| sv_catpvs_flags||5.013006| sv_catpvs_mg||5.013006| sv_catpvs_nomg||5.013006| sv_catpvs|5.009003||p sv_catpv||| sv_catsv_flags||5.007002| sv_catsv_mg|5.004050||p sv_catsv_nomg|5.007002||p sv_catsv||| sv_catxmlpvn||| sv_catxmlpv||| sv_catxmlsv||| sv_chop||| sv_clean_all||| sv_clean_objs||| sv_clear||| sv_cmp_flags||5.013006| sv_cmp_locale_flags||5.013006| sv_cmp_locale||5.004000| sv_cmp||| sv_collxfrm_flags||5.013006| sv_collxfrm||| sv_copypv_flags||5.017002| sv_copypv_nomg||5.017002| sv_copypv||| sv_dec_nomg||5.013002| sv_dec||| sv_del_backref||| sv_derived_from_pvn||5.015004| sv_derived_from_pv||5.015004| sv_derived_from_sv||5.015004| sv_derived_from||5.004000| sv_destroyable||5.010000| sv_display||| sv_does_pvn||5.015004| sv_does_pv||5.015004| sv_does_sv||5.015004| sv_does||5.009004| sv_dump||| sv_dup_common||| sv_dup_inc_multiple||| sv_dup_inc||| sv_dup||| sv_eq_flags||5.013006| sv_eq||| sv_exp_grow||| sv_force_normal_flags||5.007001| sv_force_normal||5.006000| sv_free2||| sv_free_arenas||| sv_free||| sv_gets||5.004000| sv_grow||| sv_i_ncmp||| sv_inc_nomg||5.013002| sv_inc||| sv_insert_flags||5.010001| sv_insert||| sv_isa||| sv_isobject||| sv_iv||5.005000| sv_kill_backrefs||| sv_len_utf8_nomg||| sv_len_utf8||5.006000| sv_len||| sv_magic_portable|5.019003|5.004000|p sv_magicext_mglob||| sv_magicext||5.007003| sv_magic||| sv_mortalcopy_flags||| sv_mortalcopy||| sv_ncmp||| sv_newmortal||| sv_newref||| sv_nolocking||5.007003| sv_nosharing||5.007003| sv_nounlocking||| sv_nv||5.005000| sv_peek||5.005000| sv_pos_b2u_flags||5.019003| sv_pos_b2u_midway||| sv_pos_b2u||5.006000| sv_pos_u2b_cached||| sv_pos_u2b_flags||5.011005| sv_pos_u2b_forwards|||n sv_pos_u2b_midway|||n sv_pos_u2b||5.006000| sv_pvbyten_force||5.006000| sv_pvbyten||5.006000| sv_pvbyte||5.006000| sv_pvn_force_flags|5.007002||p sv_pvn_force||| sv_pvn_nomg|5.007003|5.005000|p sv_pvn||5.005000| sv_pvutf8n_force||5.006000| sv_pvutf8n||5.006000| sv_pvutf8||5.006000| sv_pv||5.006000| sv_recode_to_utf8||5.007003| sv_reftype||| sv_ref||| sv_release_COW||| sv_replace||| sv_report_used||| sv_resetpvn||| sv_reset||| sv_rvweaken||5.006000| sv_sethek||| sv_setiv_mg|5.004050||p sv_setiv||| sv_setnv_mg|5.006000||p sv_setnv||| sv_setpv_mg|5.004050||p sv_setpvf_mg_nocontext|||pvn sv_setpvf_mg|5.006000|5.004000|pv sv_setpvf_nocontext|||vn sv_setpvf||5.004000|v sv_setpviv_mg||5.008001| sv_setpviv||5.008001| sv_setpvn_mg|5.004050||p sv_setpvn||| sv_setpvs_mg||5.013006| sv_setpvs|5.009004||p sv_setpv||| sv_setref_iv||| sv_setref_nv||| sv_setref_pvn||| sv_setref_pvs||5.019003| sv_setref_pv||| sv_setref_uv||5.007001| sv_setsv_cow||| sv_setsv_flags||5.007002| sv_setsv_mg|5.004050||p sv_setsv_nomg|5.007002||p sv_setsv||| sv_setuv_mg|5.004050||p sv_setuv|5.004000||p sv_tainted||5.004000| sv_taint||5.004000| sv_true||5.005000| sv_unglob||| sv_uni_display||5.007003| sv_unmagicext||5.013008| sv_unmagic||| sv_unref_flags||5.007001| sv_unref||| sv_untaint||5.004000| sv_upgrade||| sv_usepvn_flags||5.009004| sv_usepvn_mg|5.004050||p sv_usepvn||| sv_utf8_decode||5.006000| sv_utf8_downgrade||5.006000| sv_utf8_encode||5.006000| sv_utf8_upgrade_flags_grow||5.011000| sv_utf8_upgrade_flags||5.007002| sv_utf8_upgrade_nomg||5.007002| sv_utf8_upgrade||5.007001| sv_uv|5.005000||p sv_vcatpvf_mg|5.006000|5.004000|p sv_vcatpvfn_flags||5.017002| sv_vcatpvfn||5.004000| sv_vcatpvf|5.006000|5.004000|p sv_vsetpvf_mg|5.006000|5.004000|p sv_vsetpvfn||5.004000| sv_vsetpvf|5.006000|5.004000|p sv_xmlpeek||| svtype||| swallow_bom||| swash_fetch||5.007002| swash_init||5.006000| swatch_get||| sys_init3||5.010000|n sys_init||5.010000|n sys_intern_clear||| sys_intern_dup||| sys_intern_init||| sys_term||5.010000|n taint_env||| taint_proper||| tied_method|||v tmps_grow||5.006000| toFOLD_uni||5.007003| toFOLD_utf8||5.019001| toFOLD||5.019001| toLOWER_L1||5.019001| toLOWER_LC||5.004000| toLOWER_uni||5.007003| toLOWER_utf8||5.015007| toLOWER||| toTITLE_uni||5.007003| toTITLE_utf8||5.015007| toTITLE||5.019001| toUPPER_uni||5.007003| toUPPER_utf8||5.015007| toUPPER||5.004000| to_byte_substr||| to_lower_latin1||| to_uni_fold||5.007003| to_uni_lower_lc||5.006000| to_uni_lower||5.007003| to_uni_title_lc||5.006000| to_uni_title||5.007003| to_uni_upper_lc||5.006000| to_uni_upper||5.007003| to_utf8_case||5.007003| to_utf8_fold||5.015007| to_utf8_lower||5.015007| to_utf8_substr||| to_utf8_title||5.015007| to_utf8_upper||5.015007| token_free||| token_getmad||| tokenize_use||| tokeq||| tokereport||| too_few_arguments_pv||| too_few_arguments_sv||| too_many_arguments_pv||| too_many_arguments_sv||| translate_substr_offsets||| try_amagic_bin||| try_amagic_un||| uiv_2buf|||n unlnk||| unpack_rec||| unpack_str||5.007003| unpackstring||5.008001| unreferenced_to_tmp_stack||| unshare_hek_or_pvn||| unshare_hek||| unsharepvn||5.004000| unwind_handler_stack||| update_debugger_info||| upg_version||5.009005| usage||| utf16_textfilter||| utf16_to_utf8_reversed||5.006001| utf16_to_utf8||5.006001| utf8_distance||5.006000| utf8_hop||5.006000| utf8_length||5.007001| utf8_mg_len_cache_update||| utf8_mg_pos_cache_update||| utf8_to_bytes||5.006001| utf8_to_uvchr_buf||5.015009| utf8_to_uvchr||5.007001| utf8_to_uvuni_buf||5.015009| utf8_to_uvuni||5.007001| utf8n_to_uvchr||| utf8n_to_uvuni||5.007001| utilize||| uvchr_to_utf8_flags||5.007003| uvchr_to_utf8||| uvuni_to_utf8_flags||5.007003| uvuni_to_utf8||5.007001| valid_utf8_to_uvchr||| valid_utf8_to_uvuni||5.015009| validate_proto||| validate_suid||| varname||| vcmp||5.009000| vcroak||5.006000| vdeb||5.007003| vform||5.006000| visit||| vivify_defelem||| vivify_ref||| vload_module|5.006000||p vmess||5.006000| vnewSVpvf|5.006000|5.004000|p vnormal||5.009002| vnumify||5.009000| vstringify||5.009000| vverify||5.009003| vwarner||5.006000| vwarn||5.006000| wait4pid||| warn_nocontext|||vn warn_sv||5.013001| warner_nocontext|||vn warner|5.006000|5.004000|pv warn|||v was_lvalue_sub||| watch||| whichsig_pvn||5.015004| whichsig_pv||5.015004| whichsig_sv||5.015004| whichsig||| win32_croak_not_implemented|||n with_queued_errors||| wrap_op_checker||5.015008| write_to_stderr||| xmldump_all_perl||| xmldump_all||| xmldump_attr||| xmldump_eval||| xmldump_form||| xmldump_indent|||v xmldump_packsubs_perl||| xmldump_packsubs||| xmldump_sub_perl||| xmldump_sub||| xmldump_vindent||| xs_apiversion_bootcheck||| xs_version_bootcheck||| yyerror_pvn||| yyerror_pv||| yyerror||| yylex||| yyparse||| yyunlex||| yywarn||| ); if (exists $opt{'list-unsupported'}) { my $f; for $f (sort { lc $a cmp lc $b } keys %API) { next unless $API{$f}{todo}; print "$f ", '.'x(40-length($f)), " ", format_version($API{$f}{todo}), "\n"; } exit 0; } # Scan for possible replacement candidates my(%replace, %need, %hints, %warnings, %depends); my $replace = 0; my($hint, $define, $function); sub find_api { my $code = shift; $code =~ s{ / (?: \*[^*]*\*+(?:[^$ccs][^*]*\*+)* / | /[^\r\n]*) | "[^"\\]*(?:\\.[^"\\]*)*" | '[^'\\]*(?:\\.[^'\\]*)*' }{}egsx; grep { exists $API{$_} } $code =~ /(\w+)/mg; } while () { if ($hint) { my $h = $hint->[0] eq 'Hint' ? \%hints : \%warnings; if (m{^\s*\*\s(.*?)\s*$}) { for (@{$hint->[1]}) { $h->{$_} ||= ''; # suppress warning with older perls $h->{$_} .= "$1\n"; } } else { undef $hint } } $hint = [$1, [split /,?\s+/, $2]] if m{^\s*$rccs\s+(Hint|Warning):\s+(\w+(?:,?\s+\w+)*)\s*$}; if ($define) { if ($define->[1] =~ /\\$/) { $define->[1] .= $_; } else { if (exists $API{$define->[0]} && $define->[1] !~ /^DPPP_\(/) { my @n = find_api($define->[1]); push @{$depends{$define->[0]}}, @n if @n } undef $define; } } $define = [$1, $2] if m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(.*)}; if ($function) { if (/^}/) { if (exists $API{$function->[0]}) { my @n = find_api($function->[1]); push @{$depends{$function->[0]}}, @n if @n } undef $function; } else { $function->[1] .= $_; } } $function = [$1, ''] if m{^DPPP_\(my_(\w+)\)}; $replace = $1 if m{^\s*$rccs\s+Replace:\s+(\d+)\s+$rcce\s*$}; $replace{$2} = $1 if $replace and m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(\w+)}; $replace{$2} = $1 if m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(\w+).*$rccs\s+Replace\s+$rcce}; $replace{$1} = $2 if m{^\s*$rccs\s+Replace (\w+) with (\w+)\s+$rcce\s*$}; if (m{^\s*$rccs\s+(\w+(\s*,\s*\w+)*)\s+depends\s+on\s+(\w+(\s*,\s*\w+)*)\s+$rcce\s*$}) { my @deps = map { s/\s+//g; $_ } split /,/, $3; my $d; for $d (map { s/\s+//g; $_ } split /,/, $1) { push @{$depends{$d}}, @deps; } } $need{$1} = 1 if m{^#if\s+defined\(NEED_(\w+)(?:_GLOBAL)?\)}; } for (values %depends) { my %s; $_ = [sort grep !$s{$_}++, @$_]; } if (exists $opt{'api-info'}) { my $f; my $count = 0; my $match = $opt{'api-info'} =~ m!^/(.*)/$! ? $1 : "^\Q$opt{'api-info'}\E\$"; for $f (sort { lc $a cmp lc $b } keys %API) { next unless $f =~ /$match/; print "\n=== $f ===\n\n"; my $info = 0; if ($API{$f}{base} || $API{$f}{todo}) { my $base = format_version($API{$f}{base} || $API{$f}{todo}); print "Supported at least starting from perl-$base.\n"; $info++; } if ($API{$f}{provided}) { my $todo = $API{$f}{todo} ? format_version($API{$f}{todo}) : "5.003"; print "Support by $ppport provided back to perl-$todo.\n"; print "Support needs to be explicitly requested by NEED_$f.\n" if exists $need{$f}; print "Depends on: ", join(', ', @{$depends{$f}}), ".\n" if exists $depends{$f}; print "\n$hints{$f}" if exists $hints{$f}; print "\nWARNING:\n$warnings{$f}" if exists $warnings{$f}; $info++; } print "No portability information available.\n" unless $info; $count++; } $count or print "Found no API matching '$opt{'api-info'}'."; print "\n"; exit 0; } if (exists $opt{'list-provided'}) { my $f; for $f (sort { lc $a cmp lc $b } keys %API) { next unless $API{$f}{provided}; my @flags; push @flags, 'explicit' if exists $need{$f}; push @flags, 'depend' if exists $depends{$f}; push @flags, 'hint' if exists $hints{$f}; push @flags, 'warning' if exists $warnings{$f}; my $flags = @flags ? ' ['.join(', ', @flags).']' : ''; print "$f$flags\n"; } exit 0; } my @files; my @srcext = qw( .xs .c .h .cc .cpp -c.inc -xs.inc ); my $srcext = join '|', map { quotemeta $_ } @srcext; if (@ARGV) { my %seen; for (@ARGV) { if (-e) { if (-f) { push @files, $_ unless $seen{$_}++; } else { warn "'$_' is not a file.\n" } } else { my @new = grep { -f } glob $_ or warn "'$_' does not exist.\n"; push @files, grep { !$seen{$_}++ } @new; } } } else { eval { require File::Find; File::Find::find(sub { $File::Find::name =~ /($srcext)$/i and push @files, $File::Find::name; }, '.'); }; if ($@) { @files = map { glob "*$_" } @srcext; } } if (!@ARGV || $opt{filter}) { my(@in, @out); my %xsc = map { /(.*)\.xs$/ ? ("$1.c" => 1, "$1.cc" => 1) : () } @files; for (@files) { my $out = exists $xsc{$_} || /\b\Q$ppport\E$/i || !/($srcext)$/i; push @{ $out ? \@out : \@in }, $_; } if (@ARGV && @out) { warning("Skipping the following files (use --nofilter to avoid this):\n| ", join "\n| ", @out); } @files = @in; } die "No input files given!\n" unless @files; my(%files, %global, %revreplace); %revreplace = reverse %replace; my $filename; my $patch_opened = 0; for $filename (@files) { unless (open IN, "<$filename") { warn "Unable to read from $filename: $!\n"; next; } info("Scanning $filename ..."); my $c = do { local $/; }; close IN; my %file = (orig => $c, changes => 0); # Temporarily remove C/XS comments and strings from the code my @ccom; $c =~ s{ ( ^$HS*\#$HS*include\b[^\r\n]+\b(?:\Q$ppport\E|XSUB\.h)\b[^\r\n]* | ^$HS*\#$HS*(?:define|elif|if(?:def)?)\b[^\r\n]* ) | ( ^$HS*\#[^\r\n]* | "[^"\\]*(?:\\.[^"\\]*)*" | '[^'\\]*(?:\\.[^'\\]*)*' | / (?: \*[^*]*\*+(?:[^$ccs][^*]*\*+)* / | /[^\r\n]* ) ) }{ defined $2 and push @ccom, $2; defined $1 ? $1 : "$ccs$#ccom$cce" }mgsex; $file{ccom} = \@ccom; $file{code} = $c; $file{has_inc_ppport} = $c =~ /^$HS*#$HS*include[^\r\n]+\b\Q$ppport\E\b/m; my $func; for $func (keys %API) { my $match = $func; $match .= "|$revreplace{$func}" if exists $revreplace{$func}; if ($c =~ /\b(?:Perl_)?($match)\b/) { $file{uses_replace}{$1}++ if exists $revreplace{$func} && $1 eq $revreplace{$func}; $file{uses_Perl}{$func}++ if $c =~ /\bPerl_$func\b/; if (exists $API{$func}{provided}) { $file{uses_provided}{$func}++; if (!exists $API{$func}{base} || $API{$func}{base} > $opt{'compat-version'}) { $file{uses}{$func}++; my @deps = rec_depend($func); if (@deps) { $file{uses_deps}{$func} = \@deps; for (@deps) { $file{uses}{$_} = 0 unless exists $file{uses}{$_}; } } for ($func, @deps) { $file{needs}{$_} = 'static' if exists $need{$_}; } } } if (exists $API{$func}{todo} && $API{$func}{todo} > $opt{'compat-version'}) { if ($c =~ /\b$func\b/) { $file{uses_todo}{$func}++; } } } } while ($c =~ /^$HS*#$HS*define$HS+(NEED_(\w+?)(_GLOBAL)?)\b/mg) { if (exists $need{$2}) { $file{defined $3 ? 'needed_global' : 'needed_static'}{$2}++; } else { warning("Possibly wrong #define $1 in $filename") } } for (qw(uses needs uses_todo needed_global needed_static)) { for $func (keys %{$file{$_}}) { push @{$global{$_}{$func}}, $filename; } } $files{$filename} = \%file; } # Globally resolve NEED_'s my $need; for $need (keys %{$global{needs}}) { if (@{$global{needs}{$need}} > 1) { my @targets = @{$global{needs}{$need}}; my @t = grep $files{$_}{needed_global}{$need}, @targets; @targets = @t if @t; @t = grep /\.xs$/i, @targets; @targets = @t if @t; my $target = shift @targets; $files{$target}{needs}{$need} = 'global'; for (@{$global{needs}{$need}}) { $files{$_}{needs}{$need} = 'extern' if $_ ne $target; } } } for $filename (@files) { exists $files{$filename} or next; info("=== Analyzing $filename ==="); my %file = %{$files{$filename}}; my $func; my $c = $file{code}; my $warnings = 0; for $func (sort keys %{$file{uses_Perl}}) { if ($API{$func}{varargs}) { unless ($API{$func}{nothxarg}) { my $changes = ($c =~ s{\b(Perl_$func\s*\(\s*)(?!aTHX_?)(\)|[^\s)]*\))} { $1 . ($2 eq ')' ? 'aTHX' : 'aTHX_ ') . $2 }ge); if ($changes) { warning("Doesn't pass interpreter argument aTHX to Perl_$func"); $file{changes} += $changes; } } } else { warning("Uses Perl_$func instead of $func"); $file{changes} += ($c =~ s{\bPerl_$func(\s*)\((\s*aTHX_?)?\s*} {$func$1(}g); } } for $func (sort keys %{$file{uses_replace}}) { warning("Uses $func instead of $replace{$func}"); $file{changes} += ($c =~ s/\b$func\b/$replace{$func}/g); } for $func (sort keys %{$file{uses_provided}}) { if ($file{uses}{$func}) { if (exists $file{uses_deps}{$func}) { diag("Uses $func, which depends on ", join(', ', @{$file{uses_deps}{$func}})); } else { diag("Uses $func"); } } $warnings += hint($func); } unless ($opt{quiet}) { for $func (sort keys %{$file{uses_todo}}) { print "*** WARNING: Uses $func, which may not be portable below perl ", format_version($API{$func}{todo}), ", even with '$ppport'\n"; $warnings++; } } for $func (sort keys %{$file{needed_static}}) { my $message = ''; if (not exists $file{uses}{$func}) { $message = "No need to define NEED_$func if $func is never used"; } elsif (exists $file{needs}{$func} && $file{needs}{$func} ne 'static') { $message = "No need to define NEED_$func when already needed globally"; } if ($message) { diag($message); $file{changes} += ($c =~ s/^$HS*#$HS*define$HS+NEED_$func\b.*$LF//mg); } } for $func (sort keys %{$file{needed_global}}) { my $message = ''; if (not exists $global{uses}{$func}) { $message = "No need to define NEED_${func}_GLOBAL if $func is never used"; } elsif (exists $file{needs}{$func}) { if ($file{needs}{$func} eq 'extern') { $message = "No need to define NEED_${func}_GLOBAL when already needed globally"; } elsif ($file{needs}{$func} eq 'static') { $message = "No need to define NEED_${func}_GLOBAL when only used in this file"; } } if ($message) { diag($message); $file{changes} += ($c =~ s/^$HS*#$HS*define$HS+NEED_${func}_GLOBAL\b.*$LF//mg); } } $file{needs_inc_ppport} = keys %{$file{uses}}; if ($file{needs_inc_ppport}) { my $pp = ''; for $func (sort keys %{$file{needs}}) { my $type = $file{needs}{$func}; next if $type eq 'extern'; my $suffix = $type eq 'global' ? '_GLOBAL' : ''; unless (exists $file{"needed_$type"}{$func}) { if ($type eq 'global') { diag("Files [@{$global{needs}{$func}}] need $func, adding global request"); } else { diag("File needs $func, adding static request"); } $pp .= "#define NEED_$func$suffix\n"; } } if ($pp && ($c =~ s/^(?=$HS*#$HS*define$HS+NEED_\w+)/$pp/m)) { $pp = ''; $file{changes}++; } unless ($file{has_inc_ppport}) { diag("Needs to include '$ppport'"); $pp .= qq(#include "$ppport"\n) } if ($pp) { $file{changes} += ($c =~ s/^($HS*#$HS*define$HS+NEED_\w+.*?)^/$1$pp/ms) || ($c =~ s/^(?=$HS*#$HS*include.*\Q$ppport\E)/$pp/m) || ($c =~ s/^($HS*#$HS*include.*XSUB.*\s*?)^/$1$pp/m) || ($c =~ s/^/$pp/); } } else { if ($file{has_inc_ppport}) { diag("No need to include '$ppport'"); $file{changes} += ($c =~ s/^$HS*?#$HS*include.*\Q$ppport\E.*?$LF//m); } } # put back in our C comments my $ix; my $cppc = 0; my @ccom = @{$file{ccom}}; for $ix (0 .. $#ccom) { if (!$opt{cplusplus} && $ccom[$ix] =~ s!^//!!) { $cppc++; $file{changes} += $c =~ s/$rccs$ix$rcce/$ccs$ccom[$ix] $cce/; } else { $c =~ s/$rccs$ix$rcce/$ccom[$ix]/; } } if ($cppc) { my $s = $cppc != 1 ? 's' : ''; warning("Uses $cppc C++ style comment$s, which is not portable"); } my $s = $warnings != 1 ? 's' : ''; my $warn = $warnings ? " ($warnings warning$s)" : ''; info("Analysis completed$warn"); if ($file{changes}) { if (exists $opt{copy}) { my $newfile = "$filename$opt{copy}"; if (-e $newfile) { error("'$newfile' already exists, refusing to write copy of '$filename'"); } else { local *F; if (open F, ">$newfile") { info("Writing copy of '$filename' with changes to '$newfile'"); print F $c; close F; } else { error("Cannot open '$newfile' for writing: $!"); } } } elsif (exists $opt{patch} || $opt{changes}) { if (exists $opt{patch}) { unless ($patch_opened) { if (open PATCH, ">$opt{patch}") { $patch_opened = 1; } else { error("Cannot open '$opt{patch}' for writing: $!"); delete $opt{patch}; $opt{changes} = 1; goto fallback; } } mydiff(\*PATCH, $filename, $c); } else { fallback: info("Suggested changes:"); mydiff(\*STDOUT, $filename, $c); } } else { my $s = $file{changes} == 1 ? '' : 's'; info("$file{changes} potentially required change$s detected"); } } else { info("Looks good"); } } close PATCH if $patch_opened; exit 0; sub try_use { eval "use @_;"; return $@ eq '' } sub mydiff { local *F = shift; my($file, $str) = @_; my $diff; if (exists $opt{diff}) { $diff = run_diff($opt{diff}, $file, $str); } if (!defined $diff and try_use('Text::Diff')) { $diff = Text::Diff::diff($file, \$str, { STYLE => 'Unified' }); $diff = <
$tmp") { print F $str; close F; if (open F, "$prog $file $tmp |") { while () { s/\Q$tmp\E/$file.patched/; $diff .= $_; } close F; unlink $tmp; return $diff; } unlink $tmp; } else { error("Cannot open '$tmp' for writing: $!"); } return undef; } sub rec_depend { my($func, $seen) = @_; return () unless exists $depends{$func}; $seen = {%{$seen||{}}}; return () if $seen->{$func}++; my %s; grep !$s{$_}++, map { ($_, rec_depend($_, $seen)) } @{$depends{$func}}; } sub parse_version { my $ver = shift; if ($ver =~ /^(\d+)\.(\d+)\.(\d+)$/) { return ($1, $2, $3); } elsif ($ver !~ /^\d+\.[\d_]+$/) { die "cannot parse version '$ver'\n"; } $ver =~ s/_//g; $ver =~ s/$/000000/; my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/; $v = int $v; $s = int $s; if ($r < 5 || ($r == 5 && $v < 6)) { if ($s % 10) { die "cannot parse version '$ver'\n"; } } return ($r, $v, $s); } sub format_version { my $ver = shift; $ver =~ s/$/000000/; my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/; $v = int $v; $s = int $s; if ($r < 5 || ($r == 5 && $v < 6)) { if ($s % 10) { die "invalid version '$ver'\n"; } $s /= 10; $ver = sprintf "%d.%03d", $r, $v; $s > 0 and $ver .= sprintf "_%02d", $s; return $ver; } return sprintf "%d.%d.%d", $r, $v, $s; } sub info { $opt{quiet} and return; print @_, "\n"; } sub diag { $opt{quiet} and return; $opt{diag} and print @_, "\n"; } sub warning { $opt{quiet} and return; print "*** ", @_, "\n"; } sub error { print "*** ERROR: ", @_, "\n"; } my %given_hints; my %given_warnings; sub hint { $opt{quiet} and return; my $func = shift; my $rv = 0; if (exists $warnings{$func} && !$given_warnings{$func}++) { my $warn = $warnings{$func}; $warn =~ s!^!*** !mg; print "*** WARNING: $func\n", $warn; $rv++; } if ($opt{hints} && exists $hints{$func} && !$given_hints{$func}++) { my $hint = $hints{$func}; $hint =~ s/^/ /mg; print " --- hint for $func ---\n", $hint; } $rv; } sub usage { my($usage) = do { local(@ARGV,$/)=($0); <> } =~ /^=head\d$HS+SYNOPSIS\s*^(.*?)\s*^=/ms; my %M = ( 'I' => '*' ); $usage =~ s/^\s*perl\s+\S+/$^X $0/; $usage =~ s/([A-Z])<([^>]+)>/$M{$1}$2$M{$1}/g; print < }; my($copy) = $self =~ /^=head\d\s+COPYRIGHT\s*^(.*?)^=\w+/ms; $copy =~ s/^(?=\S+)/ /gms; $self =~ s/^$HS+Do NOT edit.*?(?=^-)/$copy/ms; $self =~ s/^SKIP.*(?=^__DATA__)/SKIP if (\@ARGV && \$ARGV[0] eq '--unstrip') { eval { require Devel::PPPort }; \$@ and die "Cannot require Devel::PPPort, please install.\\n"; if (eval \$Devel::PPPort::VERSION < $VERSION) { die "$0 was originally generated with Devel::PPPort $VERSION.\\n" . "Your Devel::PPPort is only version \$Devel::PPPort::VERSION.\\n" . "Please install a newer version, or --unstrip will not work.\\n"; } Devel::PPPort::WriteFile(\$0); exit 0; } print <$0" or die "cannot strip $0: $!\n"; print OUT "$pl$c\n"; exit 0; } __DATA__ */ #ifndef _P_P_PORTABILITY_H_ #define _P_P_PORTABILITY_H_ #ifndef DPPP_NAMESPACE # define DPPP_NAMESPACE DPPP_ #endif #define DPPP_CAT2(x,y) CAT2(x,y) #define DPPP_(name) DPPP_CAT2(DPPP_NAMESPACE, name) #ifndef PERL_REVISION # if !defined(__PATCHLEVEL_H_INCLUDED__) && !(defined(PATCHLEVEL) && defined(SUBVERSION)) # define PERL_PATCHLEVEL_H_IMPLICIT # include # endif # if !(defined(PERL_VERSION) || (defined(SUBVERSION) && defined(PATCHLEVEL))) # include # endif # ifndef PERL_REVISION # define PERL_REVISION (5) /* Replace: 1 */ # define PERL_VERSION PATCHLEVEL # define PERL_SUBVERSION SUBVERSION /* Replace PERL_PATCHLEVEL with PERL_VERSION */ /* Replace: 0 */ # endif #endif #define _dpppDEC2BCD(dec) ((((dec)/100)<<8)|((((dec)%100)/10)<<4)|((dec)%10)) #define PERL_BCDVERSION ((_dpppDEC2BCD(PERL_REVISION)<<24)|(_dpppDEC2BCD(PERL_VERSION)<<12)|_dpppDEC2BCD(PERL_SUBVERSION)) /* It is very unlikely that anyone will try to use this with Perl 6 (or greater), but who knows. */ #if PERL_REVISION != 5 # error ppport.h only works with Perl version 5 #endif /* PERL_REVISION != 5 */ #ifndef dTHR # define dTHR dNOOP #endif #ifndef dTHX # define dTHX dNOOP #endif #ifndef dTHXa # define dTHXa(x) dNOOP #endif #ifndef pTHX # define pTHX void #endif #ifndef pTHX_ # define pTHX_ #endif #ifndef aTHX # define aTHX #endif #ifndef aTHX_ # define aTHX_ #endif #if (PERL_BCDVERSION < 0x5006000) # ifdef USE_THREADS # define aTHXR thr # define aTHXR_ thr, # else # define aTHXR # define aTHXR_ # endif # define dTHXR dTHR #else # define aTHXR aTHX # define aTHXR_ aTHX_ # define dTHXR dTHX #endif #ifndef dTHXoa # define dTHXoa(x) dTHXa(x) #endif #ifdef I_LIMITS # include #endif #ifndef PERL_UCHAR_MIN # define PERL_UCHAR_MIN ((unsigned char)0) #endif #ifndef PERL_UCHAR_MAX # ifdef UCHAR_MAX # define PERL_UCHAR_MAX ((unsigned char)UCHAR_MAX) # else # ifdef MAXUCHAR # define PERL_UCHAR_MAX ((unsigned char)MAXUCHAR) # else # define PERL_UCHAR_MAX ((unsigned char)~(unsigned)0) # endif # endif #endif #ifndef PERL_USHORT_MIN # define PERL_USHORT_MIN ((unsigned short)0) #endif #ifndef PERL_USHORT_MAX # ifdef USHORT_MAX # define PERL_USHORT_MAX ((unsigned short)USHORT_MAX) # else # ifdef MAXUSHORT # define PERL_USHORT_MAX ((unsigned short)MAXUSHORT) # else # ifdef USHRT_MAX # define PERL_USHORT_MAX ((unsigned short)USHRT_MAX) # else # define PERL_USHORT_MAX ((unsigned short)~(unsigned)0) # endif # endif # endif #endif #ifndef PERL_SHORT_MAX # ifdef SHORT_MAX # define PERL_SHORT_MAX ((short)SHORT_MAX) # else # ifdef MAXSHORT /* Often used in */ # define PERL_SHORT_MAX ((short)MAXSHORT) # else # ifdef SHRT_MAX # define PERL_SHORT_MAX ((short)SHRT_MAX) # else # define PERL_SHORT_MAX ((short) (PERL_USHORT_MAX >> 1)) # endif # endif # endif #endif #ifndef PERL_SHORT_MIN # ifdef SHORT_MIN # define PERL_SHORT_MIN ((short)SHORT_MIN) # else # ifdef MINSHORT # define PERL_SHORT_MIN ((short)MINSHORT) # else # ifdef SHRT_MIN # define PERL_SHORT_MIN ((short)SHRT_MIN) # else # define PERL_SHORT_MIN (-PERL_SHORT_MAX - ((3 & -1) == 3)) # endif # endif # endif #endif #ifndef PERL_UINT_MAX # ifdef UINT_MAX # define PERL_UINT_MAX ((unsigned int)UINT_MAX) # else # ifdef MAXUINT # define PERL_UINT_MAX ((unsigned int)MAXUINT) # else # define PERL_UINT_MAX (~(unsigned int)0) # endif # endif #endif #ifndef PERL_UINT_MIN # define PERL_UINT_MIN ((unsigned int)0) #endif #ifndef PERL_INT_MAX # ifdef INT_MAX # define PERL_INT_MAX ((int)INT_MAX) # else # ifdef MAXINT /* Often used in */ # define PERL_INT_MAX ((int)MAXINT) # else # define PERL_INT_MAX ((int)(PERL_UINT_MAX >> 1)) # endif # endif #endif #ifndef PERL_INT_MIN # ifdef INT_MIN # define PERL_INT_MIN ((int)INT_MIN) # else # ifdef MININT # define PERL_INT_MIN ((int)MININT) # else # define PERL_INT_MIN (-PERL_INT_MAX - ((3 & -1) == 3)) # endif # endif #endif #ifndef PERL_ULONG_MAX # ifdef ULONG_MAX # define PERL_ULONG_MAX ((unsigned long)ULONG_MAX) # else # ifdef MAXULONG # define PERL_ULONG_MAX ((unsigned long)MAXULONG) # else # define PERL_ULONG_MAX (~(unsigned long)0) # endif # endif #endif #ifndef PERL_ULONG_MIN # define PERL_ULONG_MIN ((unsigned long)0L) #endif #ifndef PERL_LONG_MAX # ifdef LONG_MAX # define PERL_LONG_MAX ((long)LONG_MAX) # else # ifdef MAXLONG # define PERL_LONG_MAX ((long)MAXLONG) # else # define PERL_LONG_MAX ((long) (PERL_ULONG_MAX >> 1)) # endif # endif #endif #ifndef PERL_LONG_MIN # ifdef LONG_MIN # define PERL_LONG_MIN ((long)LONG_MIN) # else # ifdef MINLONG # define PERL_LONG_MIN ((long)MINLONG) # else # define PERL_LONG_MIN (-PERL_LONG_MAX - ((3 & -1) == 3)) # endif # endif #endif #if defined(HAS_QUAD) && (defined(convex) || defined(uts)) # ifndef PERL_UQUAD_MAX # ifdef ULONGLONG_MAX # define PERL_UQUAD_MAX ((unsigned long long)ULONGLONG_MAX) # else # ifdef MAXULONGLONG # define PERL_UQUAD_MAX ((unsigned long long)MAXULONGLONG) # else # define PERL_UQUAD_MAX (~(unsigned long long)0) # endif # endif # endif # ifndef PERL_UQUAD_MIN # define PERL_UQUAD_MIN ((unsigned long long)0L) # endif # ifndef PERL_QUAD_MAX # ifdef LONGLONG_MAX # define PERL_QUAD_MAX ((long long)LONGLONG_MAX) # else # ifdef MAXLONGLONG # define PERL_QUAD_MAX ((long long)MAXLONGLONG) # else # define PERL_QUAD_MAX ((long long) (PERL_UQUAD_MAX >> 1)) # endif # endif # endif # ifndef PERL_QUAD_MIN # ifdef LONGLONG_MIN # define PERL_QUAD_MIN ((long long)LONGLONG_MIN) # else # ifdef MINLONGLONG # define PERL_QUAD_MIN ((long long)MINLONGLONG) # else # define PERL_QUAD_MIN (-PERL_QUAD_MAX - ((3 & -1) == 3)) # endif # endif # endif #endif /* This is based on code from 5.003 perl.h */ #ifdef HAS_QUAD # ifdef cray #ifndef IVTYPE # define IVTYPE int #endif #ifndef IV_MIN # define IV_MIN PERL_INT_MIN #endif #ifndef IV_MAX # define IV_MAX PERL_INT_MAX #endif #ifndef UV_MIN # define UV_MIN PERL_UINT_MIN #endif #ifndef UV_MAX # define UV_MAX PERL_UINT_MAX #endif # ifdef INTSIZE #ifndef IVSIZE # define IVSIZE INTSIZE #endif # endif # else # if defined(convex) || defined(uts) #ifndef IVTYPE # define IVTYPE long long #endif #ifndef IV_MIN # define IV_MIN PERL_QUAD_MIN #endif #ifndef IV_MAX # define IV_MAX PERL_QUAD_MAX #endif #ifndef UV_MIN # define UV_MIN PERL_UQUAD_MIN #endif #ifndef UV_MAX # define UV_MAX PERL_UQUAD_MAX #endif # ifdef LONGLONGSIZE #ifndef IVSIZE # define IVSIZE LONGLONGSIZE #endif # endif # else #ifndef IVTYPE # define IVTYPE long #endif #ifndef IV_MIN # define IV_MIN PERL_LONG_MIN #endif #ifndef IV_MAX # define IV_MAX PERL_LONG_MAX #endif #ifndef UV_MIN # define UV_MIN PERL_ULONG_MIN #endif #ifndef UV_MAX # define UV_MAX PERL_ULONG_MAX #endif # ifdef LONGSIZE #ifndef IVSIZE # define IVSIZE LONGSIZE #endif # endif # endif # endif #ifndef IVSIZE # define IVSIZE 8 #endif #ifndef LONGSIZE # define LONGSIZE 8 #endif #ifndef PERL_QUAD_MIN # define PERL_QUAD_MIN IV_MIN #endif #ifndef PERL_QUAD_MAX # define PERL_QUAD_MAX IV_MAX #endif #ifndef PERL_UQUAD_MIN # define PERL_UQUAD_MIN UV_MIN #endif #ifndef PERL_UQUAD_MAX # define PERL_UQUAD_MAX UV_MAX #endif #else #ifndef IVTYPE # define IVTYPE long #endif #ifndef LONGSIZE # define LONGSIZE 4 #endif #ifndef IV_MIN # define IV_MIN PERL_LONG_MIN #endif #ifndef IV_MAX # define IV_MAX PERL_LONG_MAX #endif #ifndef UV_MIN # define UV_MIN PERL_ULONG_MIN #endif #ifndef UV_MAX # define UV_MAX PERL_ULONG_MAX #endif #endif #ifndef IVSIZE # ifdef LONGSIZE # define IVSIZE LONGSIZE # else # define IVSIZE 4 /* A bold guess, but the best we can make. */ # endif #endif #ifndef UVTYPE # define UVTYPE unsigned IVTYPE #endif #ifndef UVSIZE # define UVSIZE IVSIZE #endif #ifndef sv_setuv # define sv_setuv(sv, uv) \ STMT_START { \ UV TeMpUv = uv; \ if (TeMpUv <= IV_MAX) \ sv_setiv(sv, TeMpUv); \ else \ sv_setnv(sv, (double)TeMpUv); \ } STMT_END #endif #ifndef newSVuv # define newSVuv(uv) ((uv) <= IV_MAX ? newSViv((IV)uv) : newSVnv((NV)uv)) #endif #ifndef sv_2uv # define sv_2uv(sv) ((PL_Sv = (sv)), (UV) (SvNOK(PL_Sv) ? SvNV(PL_Sv) : sv_2nv(PL_Sv))) #endif #ifndef SvUVX # define SvUVX(sv) ((UV)SvIVX(sv)) #endif #ifndef SvUVXx # define SvUVXx(sv) SvUVX(sv) #endif #ifndef SvUV # define SvUV(sv) (SvIOK(sv) ? SvUVX(sv) : sv_2uv(sv)) #endif #ifndef SvUVx # define SvUVx(sv) ((PL_Sv = (sv)), SvUV(PL_Sv)) #endif /* Hint: sv_uv * Always use the SvUVx() macro instead of sv_uv(). */ #ifndef sv_uv # define sv_uv(sv) SvUVx(sv) #endif #if !defined(SvUOK) && defined(SvIOK_UV) # define SvUOK(sv) SvIOK_UV(sv) #endif #ifndef XST_mUV # define XST_mUV(i,v) (ST(i) = sv_2mortal(newSVuv(v)) ) #endif #ifndef XSRETURN_UV # define XSRETURN_UV(v) STMT_START { XST_mUV(0,v); XSRETURN(1); } STMT_END #endif #ifndef PUSHu # define PUSHu(u) STMT_START { sv_setuv(TARG, (UV)(u)); PUSHTARG; } STMT_END #endif #ifndef XPUSHu # define XPUSHu(u) STMT_START { sv_setuv(TARG, (UV)(u)); XPUSHTARG; } STMT_END #endif #ifdef HAS_MEMCMP #ifndef memNE # define memNE(s1,s2,l) (memcmp(s1,s2,l)) #endif #ifndef memEQ # define memEQ(s1,s2,l) (!memcmp(s1,s2,l)) #endif #else #ifndef memNE # define memNE(s1,s2,l) (bcmp(s1,s2,l)) #endif #ifndef memEQ # define memEQ(s1,s2,l) (!bcmp(s1,s2,l)) #endif #endif #ifndef memEQs # define memEQs(s1, l, s2) \ (sizeof(s2)-1 == l && memEQ(s1, (s2 ""), (sizeof(s2)-1))) #endif #ifndef memNEs # define memNEs(s1, l, s2) !memEQs(s1, l, s2) #endif #ifndef MoveD # define MoveD(s,d,n,t) memmove((char*)(d),(char*)(s), (n) * sizeof(t)) #endif #ifndef CopyD # define CopyD(s,d,n,t) memcpy((char*)(d),(char*)(s), (n) * sizeof(t)) #endif #ifdef HAS_MEMSET #ifndef ZeroD # define ZeroD(d,n,t) memzero((char*)(d), (n) * sizeof(t)) #endif #else #ifndef ZeroD # define ZeroD(d,n,t) ((void)memzero((char*)(d), (n) * sizeof(t)), d) #endif #endif #ifndef PoisonWith # define PoisonWith(d,n,t,b) (void)memset((char*)(d), (U8)(b), (n) * sizeof(t)) #endif #ifndef PoisonNew # define PoisonNew(d,n,t) PoisonWith(d,n,t,0xAB) #endif #ifndef PoisonFree # define PoisonFree(d,n,t) PoisonWith(d,n,t,0xEF) #endif #ifndef Poison # define Poison(d,n,t) PoisonFree(d,n,t) #endif #ifndef Newx # define Newx(v,n,t) New(0,v,n,t) #endif #ifndef Newxc # define Newxc(v,n,t,c) Newc(0,v,n,t,c) #endif #ifndef Newxz # define Newxz(v,n,t) Newz(0,v,n,t) #endif #ifndef PERL_UNUSED_DECL # ifdef HASATTRIBUTE # if (defined(__GNUC__) && defined(__cplusplus)) || defined(__INTEL_COMPILER) # define PERL_UNUSED_DECL # else # define PERL_UNUSED_DECL __attribute__((unused)) # endif # else # define PERL_UNUSED_DECL # endif #endif #ifndef PERL_UNUSED_ARG # if defined(lint) && defined(S_SPLINT_S) /* www.splint.org */ # include # define PERL_UNUSED_ARG(x) NOTE(ARGUNUSED(x)) # else # define PERL_UNUSED_ARG(x) ((void)x) # endif #endif #ifndef PERL_UNUSED_VAR # define PERL_UNUSED_VAR(x) ((void)x) #endif #ifndef PERL_UNUSED_CONTEXT # ifdef USE_ITHREADS # define PERL_UNUSED_CONTEXT PERL_UNUSED_ARG(my_perl) # else # define PERL_UNUSED_CONTEXT # endif #endif #ifndef NOOP # define NOOP /*EMPTY*/(void)0 #endif #ifndef dNOOP # define dNOOP extern int /*@unused@*/ Perl___notused PERL_UNUSED_DECL #endif #ifndef NVTYPE # if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE) # define NVTYPE long double # else # define NVTYPE double # endif typedef NVTYPE NV; #endif #ifndef INT2PTR # if (IVSIZE == PTRSIZE) && (UVSIZE == PTRSIZE) # define PTRV UV # define INT2PTR(any,d) (any)(d) # else # if PTRSIZE == LONGSIZE # define PTRV unsigned long # else # define PTRV unsigned # endif # define INT2PTR(any,d) (any)(PTRV)(d) # endif #endif #ifndef PTR2ul # if PTRSIZE == LONGSIZE # define PTR2ul(p) (unsigned long)(p) # else # define PTR2ul(p) INT2PTR(unsigned long,p) # endif #endif #ifndef PTR2nat # define PTR2nat(p) (PTRV)(p) #endif #ifndef NUM2PTR # define NUM2PTR(any,d) (any)PTR2nat(d) #endif #ifndef PTR2IV # define PTR2IV(p) INT2PTR(IV,p) #endif #ifndef PTR2UV # define PTR2UV(p) INT2PTR(UV,p) #endif #ifndef PTR2NV # define PTR2NV(p) NUM2PTR(NV,p) #endif #undef START_EXTERN_C #undef END_EXTERN_C #undef EXTERN_C #ifdef __cplusplus # define START_EXTERN_C extern "C" { # define END_EXTERN_C } # define EXTERN_C extern "C" #else # define START_EXTERN_C # define END_EXTERN_C # define EXTERN_C extern #endif #if defined(PERL_GCC_PEDANTIC) # ifndef PERL_GCC_BRACE_GROUPS_FORBIDDEN # define PERL_GCC_BRACE_GROUPS_FORBIDDEN # endif #endif #if defined(__GNUC__) && !defined(PERL_GCC_BRACE_GROUPS_FORBIDDEN) && !defined(__cplusplus) # ifndef PERL_USE_GCC_BRACE_GROUPS # define PERL_USE_GCC_BRACE_GROUPS # endif #endif #undef STMT_START #undef STMT_END #ifdef PERL_USE_GCC_BRACE_GROUPS # define STMT_START (void)( /* gcc supports ``({ STATEMENTS; })'' */ # define STMT_END ) #else # if defined(VOIDFLAGS) && (VOIDFLAGS) && (defined(sun) || defined(__sun__)) && !defined(__GNUC__) # define STMT_START if (1) # define STMT_END else (void)0 # else # define STMT_START do # define STMT_END while (0) # endif #endif #ifndef boolSV # define boolSV(b) ((b) ? &PL_sv_yes : &PL_sv_no) #endif /* DEFSV appears first in 5.004_56 */ #ifndef DEFSV # define DEFSV GvSV(PL_defgv) #endif #ifndef SAVE_DEFSV # define SAVE_DEFSV SAVESPTR(GvSV(PL_defgv)) #endif #ifndef DEFSV_set # define DEFSV_set(sv) (DEFSV = (sv)) #endif /* Older perls (<=5.003) lack AvFILLp */ #ifndef AvFILLp # define AvFILLp AvFILL #endif #ifndef ERRSV # define ERRSV get_sv("@",FALSE) #endif /* Hint: gv_stashpvn * This function's backport doesn't support the length parameter, but * rather ignores it. Portability can only be ensured if the length * parameter is used for speed reasons, but the length can always be * correctly computed from the string argument. */ #ifndef gv_stashpvn # define gv_stashpvn(str,len,create) gv_stashpv(str,create) #endif /* Replace: 1 */ #ifndef get_cv # define get_cv perl_get_cv #endif #ifndef get_sv # define get_sv perl_get_sv #endif #ifndef get_av # define get_av perl_get_av #endif #ifndef get_hv # define get_hv perl_get_hv #endif /* Replace: 0 */ #ifndef dUNDERBAR # define dUNDERBAR dNOOP #endif #ifndef UNDERBAR # define UNDERBAR DEFSV #endif #ifndef dAX # define dAX I32 ax = MARK - PL_stack_base + 1 #endif #ifndef dITEMS # define dITEMS I32 items = SP - MARK #endif #ifndef dXSTARG # define dXSTARG SV * targ = sv_newmortal() #endif #ifndef dAXMARK # define dAXMARK I32 ax = POPMARK; \ register SV ** const mark = PL_stack_base + ax++ #endif #ifndef XSprePUSH # define XSprePUSH (sp = PL_stack_base + ax - 1) #endif #if (PERL_BCDVERSION < 0x5005000) # undef XSRETURN # define XSRETURN(off) \ STMT_START { \ PL_stack_sp = PL_stack_base + ax + ((off) - 1); \ return; \ } STMT_END #endif #ifndef XSPROTO # define XSPROTO(name) void name(pTHX_ CV* cv) #endif #ifndef SVfARG # define SVfARG(p) ((void*)(p)) #endif #ifndef PERL_ABS # define PERL_ABS(x) ((x) < 0 ? -(x) : (x)) #endif #ifndef dVAR # define dVAR dNOOP #endif #ifndef SVf # define SVf "_" #endif #ifndef UTF8_MAXBYTES # define UTF8_MAXBYTES UTF8_MAXLEN #endif #ifndef CPERLscope # define CPERLscope(x) x #endif #ifndef PERL_HASH # define PERL_HASH(hash,str,len) \ STMT_START { \ const char *s_PeRlHaSh = str; \ I32 i_PeRlHaSh = len; \ U32 hash_PeRlHaSh = 0; \ while (i_PeRlHaSh--) \ hash_PeRlHaSh = hash_PeRlHaSh * 33 + *s_PeRlHaSh++; \ (hash) = hash_PeRlHaSh; \ } STMT_END #endif #ifndef PERLIO_FUNCS_DECL # ifdef PERLIO_FUNCS_CONST # define PERLIO_FUNCS_DECL(funcs) const PerlIO_funcs funcs # define PERLIO_FUNCS_CAST(funcs) (PerlIO_funcs*)(funcs) # else # define PERLIO_FUNCS_DECL(funcs) PerlIO_funcs funcs # define PERLIO_FUNCS_CAST(funcs) (funcs) # endif #endif /* provide these typedefs for older perls */ #if (PERL_BCDVERSION < 0x5009003) # ifdef ARGSproto typedef OP* (CPERLscope(*Perl_ppaddr_t))(ARGSproto); # else typedef OP* (CPERLscope(*Perl_ppaddr_t))(pTHX); # endif typedef OP* (CPERLscope(*Perl_check_t)) (pTHX_ OP*); #endif #ifndef isPSXSPC # define isPSXSPC(c) (isSPACE(c) || (c) == '\v') #endif #ifndef isBLANK # define isBLANK(c) ((c) == ' ' || (c) == '\t') #endif #ifdef EBCDIC #ifndef isALNUMC # define isALNUMC(c) isalnum(c) #endif #ifndef isASCII # define isASCII(c) isascii(c) #endif #ifndef isCNTRL # define isCNTRL(c) iscntrl(c) #endif #ifndef isGRAPH # define isGRAPH(c) isgraph(c) #endif #ifndef isPRINT # define isPRINT(c) isprint(c) #endif #ifndef isPUNCT # define isPUNCT(c) ispunct(c) #endif #ifndef isXDIGIT # define isXDIGIT(c) isxdigit(c) #endif #else # if (PERL_BCDVERSION < 0x5010000) /* Hint: isPRINT * The implementation in older perl versions includes all of the * isSPACE() characters, which is wrong. The version provided by * Devel::PPPort always overrides a present buggy version. */ # undef isPRINT # endif #ifdef HAS_QUAD # define WIDEST_UTYPE U64TYPE #else # define WIDEST_UTYPE U32 #endif #ifndef isALNUMC # define isALNUMC(c) (isALPHA(c) || isDIGIT(c)) #endif #ifndef isASCII # define isASCII(c) ((WIDEST_UTYPE) (c) <= 127) #endif #ifndef isCNTRL # define isCNTRL(c) ((WIDEST_UTYPE) (c) < ' ' || (c) == 127) #endif #ifndef isGRAPH # define isGRAPH(c) (isALNUM(c) || isPUNCT(c)) #endif #ifndef isPRINT # define isPRINT(c) (((c) >= 32 && (c) < 127)) #endif #ifndef isPUNCT # define isPUNCT(c) (((c) >= 33 && (c) <= 47) || ((c) >= 58 && (c) <= 64) || ((c) >= 91 && (c) <= 96) || ((c) >= 123 && (c) <= 126)) #endif #ifndef isXDIGIT # define isXDIGIT(c) (isDIGIT(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) #endif #endif #ifndef PERL_SIGNALS_UNSAFE_FLAG #define PERL_SIGNALS_UNSAFE_FLAG 0x0001 #if (PERL_BCDVERSION < 0x5008000) # define D_PPP_PERL_SIGNALS_INIT PERL_SIGNALS_UNSAFE_FLAG #else # define D_PPP_PERL_SIGNALS_INIT 0 #endif #if defined(NEED_PL_signals) static U32 DPPP_(my_PL_signals) = D_PPP_PERL_SIGNALS_INIT; #elif defined(NEED_PL_signals_GLOBAL) U32 DPPP_(my_PL_signals) = D_PPP_PERL_SIGNALS_INIT; #else extern U32 DPPP_(my_PL_signals); #endif #define PL_signals DPPP_(my_PL_signals) #endif /* Hint: PL_ppaddr * Calling an op via PL_ppaddr requires passing a context argument * for threaded builds. Since the context argument is different for * 5.005 perls, you can use aTHXR (supplied by ppport.h), which will * automatically be defined as the correct argument. */ #if (PERL_BCDVERSION <= 0x5005005) /* Replace: 1 */ # define PL_ppaddr ppaddr # define PL_no_modify no_modify /* Replace: 0 */ #endif #if (PERL_BCDVERSION <= 0x5004005) /* Replace: 1 */ # define PL_DBsignal DBsignal # define PL_DBsingle DBsingle # define PL_DBsub DBsub # define PL_DBtrace DBtrace # define PL_Sv Sv # define PL_bufend bufend # define PL_bufptr bufptr # define PL_compiling compiling # define PL_copline copline # define PL_curcop curcop # define PL_curstash curstash # define PL_debstash debstash # define PL_defgv defgv # define PL_diehook diehook # define PL_dirty dirty # define PL_dowarn dowarn # define PL_errgv errgv # define PL_error_count error_count # define PL_expect expect # define PL_hexdigit hexdigit # define PL_hints hints # define PL_in_my in_my # define PL_laststatval laststatval # define PL_lex_state lex_state # define PL_lex_stuff lex_stuff # define PL_linestr linestr # define PL_na na # define PL_perl_destruct_level perl_destruct_level # define PL_perldb perldb # define PL_rsfp_filters rsfp_filters # define PL_rsfp rsfp # define PL_stack_base stack_base # define PL_stack_sp stack_sp # define PL_statcache statcache # define PL_stdingv stdingv # define PL_sv_arenaroot sv_arenaroot # define PL_sv_no sv_no # define PL_sv_undef sv_undef # define PL_sv_yes sv_yes # define PL_tainted tainted # define PL_tainting tainting # define PL_tokenbuf tokenbuf /* Replace: 0 */ #endif /* Warning: PL_parser * For perl versions earlier than 5.9.5, this is an always * non-NULL dummy. Also, it cannot be dereferenced. Don't * use it if you can avoid is and unless you absolutely know * what you're doing. * If you always check that PL_parser is non-NULL, you can * define DPPP_PL_parser_NO_DUMMY to avoid the creation of * a dummy parser structure. */ #if (PERL_BCDVERSION >= 0x5009005) # ifdef DPPP_PL_parser_NO_DUMMY # define D_PPP_my_PL_parser_var(var) ((PL_parser ? PL_parser : \ (croak("panic: PL_parser == NULL in %s:%d", \ __FILE__, __LINE__), (yy_parser *) NULL))->var) # else # ifdef DPPP_PL_parser_NO_DUMMY_WARNING # define D_PPP_parser_dummy_warning(var) # else # define D_PPP_parser_dummy_warning(var) \ warn("warning: dummy PL_" #var " used in %s:%d", __FILE__, __LINE__), # endif # define D_PPP_my_PL_parser_var(var) ((PL_parser ? PL_parser : \ (D_PPP_parser_dummy_warning(var) &DPPP_(dummy_PL_parser)))->var) #if defined(NEED_PL_parser) static yy_parser DPPP_(dummy_PL_parser); #elif defined(NEED_PL_parser_GLOBAL) yy_parser DPPP_(dummy_PL_parser); #else extern yy_parser DPPP_(dummy_PL_parser); #endif # endif /* PL_expect, PL_copline, PL_rsfp, PL_rsfp_filters, PL_linestr, PL_bufptr, PL_bufend, PL_lex_state, PL_lex_stuff, PL_tokenbuf depends on PL_parser */ /* Warning: PL_expect, PL_copline, PL_rsfp, PL_rsfp_filters, PL_linestr, PL_bufptr, PL_bufend, PL_lex_state, PL_lex_stuff, PL_tokenbuf * Do not use this variable unless you know exactly what you're * doint. It is internal to the perl parser and may change or even * be removed in the future. As of perl 5.9.5, you have to check * for (PL_parser != NULL) for this variable to have any effect. * An always non-NULL PL_parser dummy is provided for earlier * perl versions. * If PL_parser is NULL when you try to access this variable, a * dummy is being accessed instead and a warning is issued unless * you define DPPP_PL_parser_NO_DUMMY_WARNING. * If DPPP_PL_parser_NO_DUMMY is defined, the code trying to access * this variable will croak with a panic message. */ # define PL_expect D_PPP_my_PL_parser_var(expect) # define PL_copline D_PPP_my_PL_parser_var(copline) # define PL_rsfp D_PPP_my_PL_parser_var(rsfp) # define PL_rsfp_filters D_PPP_my_PL_parser_var(rsfp_filters) # define PL_linestr D_PPP_my_PL_parser_var(linestr) # define PL_bufptr D_PPP_my_PL_parser_var(bufptr) # define PL_bufend D_PPP_my_PL_parser_var(bufend) # define PL_lex_state D_PPP_my_PL_parser_var(lex_state) # define PL_lex_stuff D_PPP_my_PL_parser_var(lex_stuff) # define PL_tokenbuf D_PPP_my_PL_parser_var(tokenbuf) # define PL_in_my D_PPP_my_PL_parser_var(in_my) # define PL_in_my_stash D_PPP_my_PL_parser_var(in_my_stash) # define PL_error_count D_PPP_my_PL_parser_var(error_count) #else /* ensure that PL_parser != NULL and cannot be dereferenced */ # define PL_parser ((void *) 1) #endif #ifndef mPUSHs # define mPUSHs(s) PUSHs(sv_2mortal(s)) #endif #ifndef PUSHmortal # define PUSHmortal PUSHs(sv_newmortal()) #endif #ifndef mPUSHp # define mPUSHp(p,l) sv_setpvn(PUSHmortal, (p), (l)) #endif #ifndef mPUSHn # define mPUSHn(n) sv_setnv(PUSHmortal, (NV)(n)) #endif #ifndef mPUSHi # define mPUSHi(i) sv_setiv(PUSHmortal, (IV)(i)) #endif #ifndef mPUSHu # define mPUSHu(u) sv_setuv(PUSHmortal, (UV)(u)) #endif #ifndef mXPUSHs # define mXPUSHs(s) XPUSHs(sv_2mortal(s)) #endif #ifndef XPUSHmortal # define XPUSHmortal XPUSHs(sv_newmortal()) #endif #ifndef mXPUSHp # define mXPUSHp(p,l) STMT_START { EXTEND(sp,1); sv_setpvn(PUSHmortal, (p), (l)); } STMT_END #endif #ifndef mXPUSHn # define mXPUSHn(n) STMT_START { EXTEND(sp,1); sv_setnv(PUSHmortal, (NV)(n)); } STMT_END #endif #ifndef mXPUSHi # define mXPUSHi(i) STMT_START { EXTEND(sp,1); sv_setiv(PUSHmortal, (IV)(i)); } STMT_END #endif #ifndef mXPUSHu # define mXPUSHu(u) STMT_START { EXTEND(sp,1); sv_setuv(PUSHmortal, (UV)(u)); } STMT_END #endif /* Replace: 1 */ #ifndef call_sv # define call_sv perl_call_sv #endif #ifndef call_pv # define call_pv perl_call_pv #endif #ifndef call_argv # define call_argv perl_call_argv #endif #ifndef call_method # define call_method perl_call_method #endif #ifndef eval_sv # define eval_sv perl_eval_sv #endif /* Replace: 0 */ #ifndef PERL_LOADMOD_DENY # define PERL_LOADMOD_DENY 0x1 #endif #ifndef PERL_LOADMOD_NOIMPORT # define PERL_LOADMOD_NOIMPORT 0x2 #endif #ifndef PERL_LOADMOD_IMPORT_OPS # define PERL_LOADMOD_IMPORT_OPS 0x4 #endif #ifndef G_METHOD # define G_METHOD 64 # ifdef call_sv # undef call_sv # endif # if (PERL_BCDVERSION < 0x5006000) # define call_sv(sv, flags) ((flags) & G_METHOD ? perl_call_method((char *) SvPV_nolen_const(sv), \ (flags) & ~G_METHOD) : perl_call_sv(sv, flags)) # else # define call_sv(sv, flags) ((flags) & G_METHOD ? Perl_call_method(aTHX_ (char *) SvPV_nolen_const(sv), \ (flags) & ~G_METHOD) : Perl_call_sv(aTHX_ sv, flags)) # endif #endif /* Replace perl_eval_pv with eval_pv */ #ifndef eval_pv #if defined(NEED_eval_pv) static SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error); static #else extern SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error); #endif #ifdef eval_pv # undef eval_pv #endif #define eval_pv(a,b) DPPP_(my_eval_pv)(aTHX_ a,b) #define Perl_eval_pv DPPP_(my_eval_pv) #if defined(NEED_eval_pv) || defined(NEED_eval_pv_GLOBAL) SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error) { dSP; SV* sv = newSVpv(p, 0); PUSHMARK(sp); eval_sv(sv, G_SCALAR); SvREFCNT_dec(sv); SPAGAIN; sv = POPs; PUTBACK; if (croak_on_error && SvTRUE(GvSV(errgv))) croak(SvPVx(GvSV(errgv), na)); return sv; } #endif #endif #ifndef vload_module #if defined(NEED_vload_module) static void DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args); static #else extern void DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args); #endif #ifdef vload_module # undef vload_module #endif #define vload_module(a,b,c,d) DPPP_(my_vload_module)(aTHX_ a,b,c,d) #define Perl_vload_module DPPP_(my_vload_module) #if defined(NEED_vload_module) || defined(NEED_vload_module_GLOBAL) void DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args) { dTHR; dVAR; OP *veop, *imop; OP * const modname = newSVOP(OP_CONST, 0, name); /* 5.005 has a somewhat hacky force_normal that doesn't croak on SvREADONLY() if PL_compling is true. Current perls take care in ck_require() to correctly turn off SvREADONLY before calling force_normal_flags(). This seems a better fix than fudging PL_compling */ SvREADONLY_off(((SVOP*)modname)->op_sv); modname->op_private |= OPpCONST_BARE; if (ver) { veop = newSVOP(OP_CONST, 0, ver); } else veop = NULL; if (flags & PERL_LOADMOD_NOIMPORT) { imop = sawparens(newNULLLIST()); } else if (flags & PERL_LOADMOD_IMPORT_OPS) { imop = va_arg(*args, OP*); } else { SV *sv; imop = NULL; sv = va_arg(*args, SV*); while (sv) { imop = append_elem(OP_LIST, imop, newSVOP(OP_CONST, 0, sv)); sv = va_arg(*args, SV*); } } { const line_t ocopline = PL_copline; COP * const ocurcop = PL_curcop; const int oexpect = PL_expect; #if (PERL_BCDVERSION >= 0x5004000) utilize(!(flags & PERL_LOADMOD_DENY), start_subparse(FALSE, 0), veop, modname, imop); #else utilize(!(flags & PERL_LOADMOD_DENY), start_subparse(), modname, imop); #endif PL_expect = oexpect; PL_copline = ocopline; PL_curcop = ocurcop; } } #endif #endif #ifndef load_module #if defined(NEED_load_module) static void DPPP_(my_load_module)(U32 flags, SV *name, SV *ver, ...); static #else extern void DPPP_(my_load_module)(U32 flags, SV *name, SV *ver, ...); #endif #ifdef load_module # undef load_module #endif #define load_module DPPP_(my_load_module) #define Perl_load_module DPPP_(my_load_module) #if defined(NEED_load_module) || defined(NEED_load_module_GLOBAL) void DPPP_(my_load_module)(U32 flags, SV *name, SV *ver, ...) { va_list args; va_start(args, ver); vload_module(flags, name, ver, &args); va_end(args); } #endif #endif #ifndef newRV_inc # define newRV_inc(sv) newRV(sv) /* Replace */ #endif #ifndef newRV_noinc #if defined(NEED_newRV_noinc) static SV * DPPP_(my_newRV_noinc)(SV *sv); static #else extern SV * DPPP_(my_newRV_noinc)(SV *sv); #endif #ifdef newRV_noinc # undef newRV_noinc #endif #define newRV_noinc(a) DPPP_(my_newRV_noinc)(aTHX_ a) #define Perl_newRV_noinc DPPP_(my_newRV_noinc) #if defined(NEED_newRV_noinc) || defined(NEED_newRV_noinc_GLOBAL) SV * DPPP_(my_newRV_noinc)(SV *sv) { SV *rv = (SV *)newRV(sv); SvREFCNT_dec(sv); return rv; } #endif #endif /* Hint: newCONSTSUB * Returns a CV* as of perl-5.7.1. This return value is not supported * by Devel::PPPort. */ /* newCONSTSUB from IO.xs is in the core starting with 5.004_63 */ #if (PERL_BCDVERSION < 0x5004063) && (PERL_BCDVERSION != 0x5004005) #if defined(NEED_newCONSTSUB) static void DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv); static #else extern void DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv); #endif #ifdef newCONSTSUB # undef newCONSTSUB #endif #define newCONSTSUB(a,b,c) DPPP_(my_newCONSTSUB)(aTHX_ a,b,c) #define Perl_newCONSTSUB DPPP_(my_newCONSTSUB) #if defined(NEED_newCONSTSUB) || defined(NEED_newCONSTSUB_GLOBAL) /* This is just a trick to avoid a dependency of newCONSTSUB on PL_parser */ /* (There's no PL_parser in perl < 5.005, so this is completely safe) */ #define D_PPP_PL_copline PL_copline void DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv) { U32 oldhints = PL_hints; HV *old_cop_stash = PL_curcop->cop_stash; HV *old_curstash = PL_curstash; line_t oldline = PL_curcop->cop_line; PL_curcop->cop_line = D_PPP_PL_copline; PL_hints &= ~HINT_BLOCK_SCOPE; if (stash) PL_curstash = PL_curcop->cop_stash = stash; newSUB( #if (PERL_BCDVERSION < 0x5003022) start_subparse(), #elif (PERL_BCDVERSION == 0x5003022) start_subparse(0), #else /* 5.003_23 onwards */ start_subparse(FALSE, 0), #endif newSVOP(OP_CONST, 0, newSVpv((char *) name, 0)), newSVOP(OP_CONST, 0, &PL_sv_no), /* SvPV(&PL_sv_no) == "" -- GMB */ newSTATEOP(0, Nullch, newSVOP(OP_CONST, 0, sv)) ); PL_hints = oldhints; PL_curcop->cop_stash = old_cop_stash; PL_curstash = old_curstash; PL_curcop->cop_line = oldline; } #endif #endif /* * Boilerplate macros for initializing and accessing interpreter-local * data from C. All statics in extensions should be reworked to use * this, if you want to make the extension thread-safe. See ext/re/re.xs * for an example of the use of these macros. * * Code that uses these macros is responsible for the following: * 1. #define MY_CXT_KEY to a unique string, e.g. "DynaLoader_guts" * 2. Declare a typedef named my_cxt_t that is a structure that contains * all the data that needs to be interpreter-local. * 3. Use the START_MY_CXT macro after the declaration of my_cxt_t. * 4. Use the MY_CXT_INIT macro such that it is called exactly once * (typically put in the BOOT: section). * 5. Use the members of the my_cxt_t structure everywhere as * MY_CXT.member. * 6. Use the dMY_CXT macro (a declaration) in all the functions that * access MY_CXT. */ #if defined(MULTIPLICITY) || defined(PERL_OBJECT) || \ defined(PERL_CAPI) || defined(PERL_IMPLICIT_CONTEXT) #ifndef START_MY_CXT /* This must appear in all extensions that define a my_cxt_t structure, * right after the definition (i.e. at file scope). The non-threads * case below uses it to declare the data as static. */ #define START_MY_CXT #if (PERL_BCDVERSION < 0x5004068) /* Fetches the SV that keeps the per-interpreter data. */ #define dMY_CXT_SV \ SV *my_cxt_sv = get_sv(MY_CXT_KEY, FALSE) #else /* >= perl5.004_68 */ #define dMY_CXT_SV \ SV *my_cxt_sv = *hv_fetch(PL_modglobal, MY_CXT_KEY, \ sizeof(MY_CXT_KEY)-1, TRUE) #endif /* < perl5.004_68 */ /* This declaration should be used within all functions that use the * interpreter-local data. */ #define dMY_CXT \ dMY_CXT_SV; \ my_cxt_t *my_cxtp = INT2PTR(my_cxt_t*,SvUV(my_cxt_sv)) /* Creates and zeroes the per-interpreter data. * (We allocate my_cxtp in a Perl SV so that it will be released when * the interpreter goes away.) */ #define MY_CXT_INIT \ dMY_CXT_SV; \ /* newSV() allocates one more than needed */ \ my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1));\ Zero(my_cxtp, 1, my_cxt_t); \ sv_setuv(my_cxt_sv, PTR2UV(my_cxtp)) /* This macro must be used to access members of the my_cxt_t structure. * e.g. MYCXT.some_data */ #define MY_CXT (*my_cxtp) /* Judicious use of these macros can reduce the number of times dMY_CXT * is used. Use is similar to pTHX, aTHX etc. */ #define pMY_CXT my_cxt_t *my_cxtp #define pMY_CXT_ pMY_CXT, #define _pMY_CXT ,pMY_CXT #define aMY_CXT my_cxtp #define aMY_CXT_ aMY_CXT, #define _aMY_CXT ,aMY_CXT #endif /* START_MY_CXT */ #ifndef MY_CXT_CLONE /* Clones the per-interpreter data. */ #define MY_CXT_CLONE \ dMY_CXT_SV; \ my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1));\ Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t);\ sv_setuv(my_cxt_sv, PTR2UV(my_cxtp)) #endif #else /* single interpreter */ #ifndef START_MY_CXT #define START_MY_CXT static my_cxt_t my_cxt; #define dMY_CXT_SV dNOOP #define dMY_CXT dNOOP #define MY_CXT_INIT NOOP #define MY_CXT my_cxt #define pMY_CXT void #define pMY_CXT_ #define _pMY_CXT #define aMY_CXT #define aMY_CXT_ #define _aMY_CXT #endif /* START_MY_CXT */ #ifndef MY_CXT_CLONE #define MY_CXT_CLONE NOOP #endif #endif #ifndef IVdf # if IVSIZE == LONGSIZE # define IVdf "ld" # define UVuf "lu" # define UVof "lo" # define UVxf "lx" # define UVXf "lX" # elif IVSIZE == INTSIZE # define IVdf "d" # define UVuf "u" # define UVof "o" # define UVxf "x" # define UVXf "X" # else # error "cannot define IV/UV formats" # endif #endif #ifndef NVef # if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE) && \ defined(PERL_PRIfldbl) && (PERL_BCDVERSION != 0x5006000) /* Not very likely, but let's try anyway. */ # define NVef PERL_PRIeldbl # define NVff PERL_PRIfldbl # define NVgf PERL_PRIgldbl # else # define NVef "e" # define NVff "f" # define NVgf "g" # endif #endif #ifndef SvREFCNT_inc # ifdef PERL_USE_GCC_BRACE_GROUPS # define SvREFCNT_inc(sv) \ ({ \ SV * const _sv = (SV*)(sv); \ if (_sv) \ (SvREFCNT(_sv))++; \ _sv; \ }) # else # define SvREFCNT_inc(sv) \ ((PL_Sv=(SV*)(sv)) ? (++(SvREFCNT(PL_Sv)),PL_Sv) : NULL) # endif #endif #ifndef SvREFCNT_inc_simple # ifdef PERL_USE_GCC_BRACE_GROUPS # define SvREFCNT_inc_simple(sv) \ ({ \ if (sv) \ (SvREFCNT(sv))++; \ (SV *)(sv); \ }) # else # define SvREFCNT_inc_simple(sv) \ ((sv) ? (SvREFCNT(sv)++,(SV*)(sv)) : NULL) # endif #endif #ifndef SvREFCNT_inc_NN # ifdef PERL_USE_GCC_BRACE_GROUPS # define SvREFCNT_inc_NN(sv) \ ({ \ SV * const _sv = (SV*)(sv); \ SvREFCNT(_sv)++; \ _sv; \ }) # else # define SvREFCNT_inc_NN(sv) \ (PL_Sv=(SV*)(sv),++(SvREFCNT(PL_Sv)),PL_Sv) # endif #endif #ifndef SvREFCNT_inc_void # ifdef PERL_USE_GCC_BRACE_GROUPS # define SvREFCNT_inc_void(sv) \ ({ \ SV * const _sv = (SV*)(sv); \ if (_sv) \ (void)(SvREFCNT(_sv)++); \ }) # else # define SvREFCNT_inc_void(sv) \ (void)((PL_Sv=(SV*)(sv)) ? ++(SvREFCNT(PL_Sv)) : 0) # endif #endif #ifndef SvREFCNT_inc_simple_void # define SvREFCNT_inc_simple_void(sv) STMT_START { if (sv) SvREFCNT(sv)++; } STMT_END #endif #ifndef SvREFCNT_inc_simple_NN # define SvREFCNT_inc_simple_NN(sv) (++SvREFCNT(sv), (SV*)(sv)) #endif #ifndef SvREFCNT_inc_void_NN # define SvREFCNT_inc_void_NN(sv) (void)(++SvREFCNT((SV*)(sv))) #endif #ifndef SvREFCNT_inc_simple_void_NN # define SvREFCNT_inc_simple_void_NN(sv) (void)(++SvREFCNT((SV*)(sv))) #endif #ifndef newSV_type #if defined(NEED_newSV_type) static SV* DPPP_(my_newSV_type)(pTHX_ svtype const t); static #else extern SV* DPPP_(my_newSV_type)(pTHX_ svtype const t); #endif #ifdef newSV_type # undef newSV_type #endif #define newSV_type(a) DPPP_(my_newSV_type)(aTHX_ a) #define Perl_newSV_type DPPP_(my_newSV_type) #if defined(NEED_newSV_type) || defined(NEED_newSV_type_GLOBAL) SV* DPPP_(my_newSV_type)(pTHX_ svtype const t) { SV* const sv = newSV(0); sv_upgrade(sv, t); return sv; } #endif #endif #if (PERL_BCDVERSION < 0x5006000) # define D_PPP_CONSTPV_ARG(x) ((char *) (x)) #else # define D_PPP_CONSTPV_ARG(x) (x) #endif #ifndef newSVpvn # define newSVpvn(data,len) ((data) \ ? ((len) ? newSVpv((data), (len)) : newSVpv("", 0)) \ : newSV(0)) #endif #ifndef newSVpvn_utf8 # define newSVpvn_utf8(s, len, u) newSVpvn_flags((s), (len), (u) ? SVf_UTF8 : 0) #endif #ifndef SVf_UTF8 # define SVf_UTF8 0 #endif #ifndef newSVpvn_flags #if defined(NEED_newSVpvn_flags) static SV * DPPP_(my_newSVpvn_flags)(pTHX_ const char *s, STRLEN len, U32 flags); static #else extern SV * DPPP_(my_newSVpvn_flags)(pTHX_ const char *s, STRLEN len, U32 flags); #endif #ifdef newSVpvn_flags # undef newSVpvn_flags #endif #define newSVpvn_flags(a,b,c) DPPP_(my_newSVpvn_flags)(aTHX_ a,b,c) #define Perl_newSVpvn_flags DPPP_(my_newSVpvn_flags) #if defined(NEED_newSVpvn_flags) || defined(NEED_newSVpvn_flags_GLOBAL) SV * DPPP_(my_newSVpvn_flags)(pTHX_ const char *s, STRLEN len, U32 flags) { SV *sv = newSVpvn(D_PPP_CONSTPV_ARG(s), len); SvFLAGS(sv) |= (flags & SVf_UTF8); return (flags & SVs_TEMP) ? sv_2mortal(sv) : sv; } #endif #endif /* Backwards compatibility stuff... :-( */ #if !defined(NEED_sv_2pv_flags) && defined(NEED_sv_2pv_nolen) # define NEED_sv_2pv_flags #endif #if !defined(NEED_sv_2pv_flags_GLOBAL) && defined(NEED_sv_2pv_nolen_GLOBAL) # define NEED_sv_2pv_flags_GLOBAL #endif /* Hint: sv_2pv_nolen * Use the SvPV_nolen() or SvPV_nolen_const() macros instead of sv_2pv_nolen(). */ #ifndef sv_2pv_nolen # define sv_2pv_nolen(sv) SvPV_nolen(sv) #endif #ifdef SvPVbyte /* Hint: SvPVbyte * Does not work in perl-5.6.1, ppport.h implements a version * borrowed from perl-5.7.3. */ #if (PERL_BCDVERSION < 0x5007000) #if defined(NEED_sv_2pvbyte) static char * DPPP_(my_sv_2pvbyte)(pTHX_ SV *sv, STRLEN *lp); static #else extern char * DPPP_(my_sv_2pvbyte)(pTHX_ SV *sv, STRLEN *lp); #endif #ifdef sv_2pvbyte # undef sv_2pvbyte #endif #define sv_2pvbyte(a,b) DPPP_(my_sv_2pvbyte)(aTHX_ a,b) #define Perl_sv_2pvbyte DPPP_(my_sv_2pvbyte) #if defined(NEED_sv_2pvbyte) || defined(NEED_sv_2pvbyte_GLOBAL) char * DPPP_(my_sv_2pvbyte)(pTHX_ SV *sv, STRLEN *lp) { sv_utf8_downgrade(sv,0); return SvPV(sv,*lp); } #endif /* Hint: sv_2pvbyte * Use the SvPVbyte() macro instead of sv_2pvbyte(). */ #undef SvPVbyte #define SvPVbyte(sv, lp) \ ((SvFLAGS(sv) & (SVf_POK|SVf_UTF8)) == (SVf_POK) \ ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_2pvbyte(sv, &lp)) #endif #else # define SvPVbyte SvPV # define sv_2pvbyte sv_2pv #endif #ifndef sv_2pvbyte_nolen # define sv_2pvbyte_nolen(sv) sv_2pv_nolen(sv) #endif /* Hint: sv_pvn * Always use the SvPV() macro instead of sv_pvn(). */ /* Hint: sv_pvn_force * Always use the SvPV_force() macro instead of sv_pvn_force(). */ /* If these are undefined, they're not handled by the core anyway */ #ifndef SV_IMMEDIATE_UNREF # define SV_IMMEDIATE_UNREF 0 #endif #ifndef SV_GMAGIC # define SV_GMAGIC 0 #endif #ifndef SV_COW_DROP_PV # define SV_COW_DROP_PV 0 #endif #ifndef SV_UTF8_NO_ENCODING # define SV_UTF8_NO_ENCODING 0 #endif #ifndef SV_NOSTEAL # define SV_NOSTEAL 0 #endif #ifndef SV_CONST_RETURN # define SV_CONST_RETURN 0 #endif #ifndef SV_MUTABLE_RETURN # define SV_MUTABLE_RETURN 0 #endif #ifndef SV_SMAGIC # define SV_SMAGIC 0 #endif #ifndef SV_HAS_TRAILING_NUL # define SV_HAS_TRAILING_NUL 0 #endif #ifndef SV_COW_SHARED_HASH_KEYS # define SV_COW_SHARED_HASH_KEYS 0 #endif #if (PERL_BCDVERSION < 0x5007002) #if defined(NEED_sv_2pv_flags) static char * DPPP_(my_sv_2pv_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags); static #else extern char * DPPP_(my_sv_2pv_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags); #endif #ifdef sv_2pv_flags # undef sv_2pv_flags #endif #define sv_2pv_flags(a,b,c) DPPP_(my_sv_2pv_flags)(aTHX_ a,b,c) #define Perl_sv_2pv_flags DPPP_(my_sv_2pv_flags) #if defined(NEED_sv_2pv_flags) || defined(NEED_sv_2pv_flags_GLOBAL) char * DPPP_(my_sv_2pv_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags) { STRLEN n_a = (STRLEN) flags; return sv_2pv(sv, lp ? lp : &n_a); } #endif #if defined(NEED_sv_pvn_force_flags) static char * DPPP_(my_sv_pvn_force_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags); static #else extern char * DPPP_(my_sv_pvn_force_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags); #endif #ifdef sv_pvn_force_flags # undef sv_pvn_force_flags #endif #define sv_pvn_force_flags(a,b,c) DPPP_(my_sv_pvn_force_flags)(aTHX_ a,b,c) #define Perl_sv_pvn_force_flags DPPP_(my_sv_pvn_force_flags) #if defined(NEED_sv_pvn_force_flags) || defined(NEED_sv_pvn_force_flags_GLOBAL) char * DPPP_(my_sv_pvn_force_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags) { STRLEN n_a = (STRLEN) flags; return sv_pvn_force(sv, lp ? lp : &n_a); } #endif #endif #if (PERL_BCDVERSION < 0x5008008) || ( (PERL_BCDVERSION >= 0x5009000) && (PERL_BCDVERSION < 0x5009003) ) # define DPPP_SVPV_NOLEN_LP_ARG &PL_na #else # define DPPP_SVPV_NOLEN_LP_ARG 0 #endif #ifndef SvPV_const # define SvPV_const(sv, lp) SvPV_flags_const(sv, lp, SV_GMAGIC) #endif #ifndef SvPV_mutable # define SvPV_mutable(sv, lp) SvPV_flags_mutable(sv, lp, SV_GMAGIC) #endif #ifndef SvPV_flags # define SvPV_flags(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_2pv_flags(sv, &lp, flags)) #endif #ifndef SvPV_flags_const # define SvPV_flags_const(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX_const(sv)) : \ (const char*) sv_2pv_flags(sv, &lp, flags|SV_CONST_RETURN)) #endif #ifndef SvPV_flags_const_nolen # define SvPV_flags_const_nolen(sv, flags) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? SvPVX_const(sv) : \ (const char*) sv_2pv_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, flags|SV_CONST_RETURN)) #endif #ifndef SvPV_flags_mutable # define SvPV_flags_mutable(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX_mutable(sv)) : \ sv_2pv_flags(sv, &lp, flags|SV_MUTABLE_RETURN)) #endif #ifndef SvPV_force # define SvPV_force(sv, lp) SvPV_force_flags(sv, lp, SV_GMAGIC) #endif #ifndef SvPV_force_nolen # define SvPV_force_nolen(sv) SvPV_force_flags_nolen(sv, SV_GMAGIC) #endif #ifndef SvPV_force_mutable # define SvPV_force_mutable(sv, lp) SvPV_force_flags_mutable(sv, lp, SV_GMAGIC) #endif #ifndef SvPV_force_nomg # define SvPV_force_nomg(sv, lp) SvPV_force_flags(sv, lp, 0) #endif #ifndef SvPV_force_nomg_nolen # define SvPV_force_nomg_nolen(sv) SvPV_force_flags_nolen(sv, 0) #endif #ifndef SvPV_force_flags # define SvPV_force_flags(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK|SVf_THINKFIRST)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_pvn_force_flags(sv, &lp, flags)) #endif #ifndef SvPV_force_flags_nolen # define SvPV_force_flags_nolen(sv, flags) \ ((SvFLAGS(sv) & (SVf_POK|SVf_THINKFIRST)) == SVf_POK \ ? SvPVX(sv) : sv_pvn_force_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, flags)) #endif #ifndef SvPV_force_flags_mutable # define SvPV_force_flags_mutable(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK|SVf_THINKFIRST)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX_mutable(sv)) \ : sv_pvn_force_flags(sv, &lp, flags|SV_MUTABLE_RETURN)) #endif #ifndef SvPV_nolen # define SvPV_nolen(sv) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? SvPVX(sv) : sv_2pv_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, SV_GMAGIC)) #endif #ifndef SvPV_nolen_const # define SvPV_nolen_const(sv) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? SvPVX_const(sv) : sv_2pv_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, SV_GMAGIC|SV_CONST_RETURN)) #endif #ifndef SvPV_nomg # define SvPV_nomg(sv, lp) SvPV_flags(sv, lp, 0) #endif #ifndef SvPV_nomg_const # define SvPV_nomg_const(sv, lp) SvPV_flags_const(sv, lp, 0) #endif #ifndef SvPV_nomg_const_nolen # define SvPV_nomg_const_nolen(sv) SvPV_flags_const_nolen(sv, 0) #endif #ifndef SvPV_nomg_nolen # define SvPV_nomg_nolen(sv) ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? SvPVX(sv) : sv_2pv_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, 0)) #endif #ifndef SvPV_renew # define SvPV_renew(sv,n) STMT_START { SvLEN_set(sv, n); \ SvPV_set((sv), (char *) saferealloc( \ (Malloc_t)SvPVX(sv), (MEM_SIZE)((n)))); \ } STMT_END #endif #ifndef SvMAGIC_set # define SvMAGIC_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_PVMG); \ (((XPVMG*) SvANY(sv))->xmg_magic = (val)); } STMT_END #endif #if (PERL_BCDVERSION < 0x5009003) #ifndef SvPVX_const # define SvPVX_const(sv) ((const char*) (0 + SvPVX(sv))) #endif #ifndef SvPVX_mutable # define SvPVX_mutable(sv) (0 + SvPVX(sv)) #endif #ifndef SvRV_set # define SvRV_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_RV); \ (((XRV*) SvANY(sv))->xrv_rv = (val)); } STMT_END #endif #else #ifndef SvPVX_const # define SvPVX_const(sv) ((const char*)((sv)->sv_u.svu_pv)) #endif #ifndef SvPVX_mutable # define SvPVX_mutable(sv) ((sv)->sv_u.svu_pv) #endif #ifndef SvRV_set # define SvRV_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_RV); \ ((sv)->sv_u.svu_rv = (val)); } STMT_END #endif #endif #ifndef SvSTASH_set # define SvSTASH_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_PVMG); \ (((XPVMG*) SvANY(sv))->xmg_stash = (val)); } STMT_END #endif #if (PERL_BCDVERSION < 0x5004000) #ifndef SvUV_set # define SvUV_set(sv, val) \ STMT_START { assert(SvTYPE(sv) == SVt_IV || SvTYPE(sv) >= SVt_PVIV); \ (((XPVIV*) SvANY(sv))->xiv_iv = (IV) (val)); } STMT_END #endif #else #ifndef SvUV_set # define SvUV_set(sv, val) \ STMT_START { assert(SvTYPE(sv) == SVt_IV || SvTYPE(sv) >= SVt_PVIV); \ (((XPVUV*) SvANY(sv))->xuv_uv = (val)); } STMT_END #endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(vnewSVpvf) #if defined(NEED_vnewSVpvf) static SV * DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args); static #else extern SV * DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args); #endif #ifdef vnewSVpvf # undef vnewSVpvf #endif #define vnewSVpvf(a,b) DPPP_(my_vnewSVpvf)(aTHX_ a,b) #define Perl_vnewSVpvf DPPP_(my_vnewSVpvf) #if defined(NEED_vnewSVpvf) || defined(NEED_vnewSVpvf_GLOBAL) SV * DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args) { register SV *sv = newSV(0); sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)); return sv; } #endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vcatpvf) # define sv_vcatpvf(sv, pat, args) sv_vcatpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)) #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vsetpvf) # define sv_vsetpvf(sv, pat, args) sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)) #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_catpvf_mg) #if defined(NEED_sv_catpvf_mg) static void DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...); static #else extern void DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...); #endif #define Perl_sv_catpvf_mg DPPP_(my_sv_catpvf_mg) #if defined(NEED_sv_catpvf_mg) || defined(NEED_sv_catpvf_mg_GLOBAL) void DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...) { va_list args; va_start(args, pat); sv_vcatpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*)); SvSETMAGIC(sv); va_end(args); } #endif #endif #ifdef PERL_IMPLICIT_CONTEXT #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_catpvf_mg_nocontext) #if defined(NEED_sv_catpvf_mg_nocontext) static void DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...); static #else extern void DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...); #endif #define sv_catpvf_mg_nocontext DPPP_(my_sv_catpvf_mg_nocontext) #define Perl_sv_catpvf_mg_nocontext DPPP_(my_sv_catpvf_mg_nocontext) #if defined(NEED_sv_catpvf_mg_nocontext) || defined(NEED_sv_catpvf_mg_nocontext_GLOBAL) void DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...) { dTHX; va_list args; va_start(args, pat); sv_vcatpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*)); SvSETMAGIC(sv); va_end(args); } #endif #endif #endif /* sv_catpvf_mg depends on sv_catpvf_mg_nocontext */ #ifndef sv_catpvf_mg # ifdef PERL_IMPLICIT_CONTEXT # define sv_catpvf_mg Perl_sv_catpvf_mg_nocontext # else # define sv_catpvf_mg Perl_sv_catpvf_mg # endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vcatpvf_mg) # define sv_vcatpvf_mg(sv, pat, args) \ STMT_START { \ sv_vcatpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)); \ SvSETMAGIC(sv); \ } STMT_END #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_setpvf_mg) #if defined(NEED_sv_setpvf_mg) static void DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...); static #else extern void DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...); #endif #define Perl_sv_setpvf_mg DPPP_(my_sv_setpvf_mg) #if defined(NEED_sv_setpvf_mg) || defined(NEED_sv_setpvf_mg_GLOBAL) void DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...) { va_list args; va_start(args, pat); sv_vsetpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*)); SvSETMAGIC(sv); va_end(args); } #endif #endif #ifdef PERL_IMPLICIT_CONTEXT #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_setpvf_mg_nocontext) #if defined(NEED_sv_setpvf_mg_nocontext) static void DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...); static #else extern void DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...); #endif #define sv_setpvf_mg_nocontext DPPP_(my_sv_setpvf_mg_nocontext) #define Perl_sv_setpvf_mg_nocontext DPPP_(my_sv_setpvf_mg_nocontext) #if defined(NEED_sv_setpvf_mg_nocontext) || defined(NEED_sv_setpvf_mg_nocontext_GLOBAL) void DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...) { dTHX; va_list args; va_start(args, pat); sv_vsetpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*)); SvSETMAGIC(sv); va_end(args); } #endif #endif #endif /* sv_setpvf_mg depends on sv_setpvf_mg_nocontext */ #ifndef sv_setpvf_mg # ifdef PERL_IMPLICIT_CONTEXT # define sv_setpvf_mg Perl_sv_setpvf_mg_nocontext # else # define sv_setpvf_mg Perl_sv_setpvf_mg # endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vsetpvf_mg) # define sv_vsetpvf_mg(sv, pat, args) \ STMT_START { \ sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)); \ SvSETMAGIC(sv); \ } STMT_END #endif /* Hint: newSVpvn_share * The SVs created by this function only mimic the behaviour of * shared PVs without really being shared. Only use if you know * what you're doing. */ #ifndef newSVpvn_share #if defined(NEED_newSVpvn_share) static SV * DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash); static #else extern SV * DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash); #endif #ifdef newSVpvn_share # undef newSVpvn_share #endif #define newSVpvn_share(a,b,c) DPPP_(my_newSVpvn_share)(aTHX_ a,b,c) #define Perl_newSVpvn_share DPPP_(my_newSVpvn_share) #if defined(NEED_newSVpvn_share) || defined(NEED_newSVpvn_share_GLOBAL) SV * DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash) { SV *sv; if (len < 0) len = -len; if (!hash) PERL_HASH(hash, (char*) src, len); sv = newSVpvn((char *) src, len); sv_upgrade(sv, SVt_PVIV); SvIVX(sv) = hash; SvREADONLY_on(sv); SvPOK_on(sv); return sv; } #endif #endif #ifndef SvSHARED_HASH # define SvSHARED_HASH(sv) (0 + SvUVX(sv)) #endif #ifndef HvNAME_get # define HvNAME_get(hv) HvNAME(hv) #endif #ifndef HvNAMELEN_get # define HvNAMELEN_get(hv) (HvNAME_get(hv) ? (I32)strlen(HvNAME_get(hv)) : 0) #endif #ifndef GvSVn # define GvSVn(gv) GvSV(gv) #endif #ifndef isGV_with_GP # define isGV_with_GP(gv) isGV(gv) #endif #ifndef gv_fetchpvn_flags # define gv_fetchpvn_flags(name, len, flags, svt) gv_fetchpv(name, flags, svt) #endif #ifndef gv_fetchsv # define gv_fetchsv(name, flags, svt) gv_fetchpv(SvPV_nolen_const(name), flags, svt) #endif #ifndef get_cvn_flags # define get_cvn_flags(name, namelen, flags) get_cv(name, flags) #endif #ifndef WARN_ALL # define WARN_ALL 0 #endif #ifndef WARN_CLOSURE # define WARN_CLOSURE 1 #endif #ifndef WARN_DEPRECATED # define WARN_DEPRECATED 2 #endif #ifndef WARN_EXITING # define WARN_EXITING 3 #endif #ifndef WARN_GLOB # define WARN_GLOB 4 #endif #ifndef WARN_IO # define WARN_IO 5 #endif #ifndef WARN_CLOSED # define WARN_CLOSED 6 #endif #ifndef WARN_EXEC # define WARN_EXEC 7 #endif #ifndef WARN_LAYER # define WARN_LAYER 8 #endif #ifndef WARN_NEWLINE # define WARN_NEWLINE 9 #endif #ifndef WARN_PIPE # define WARN_PIPE 10 #endif #ifndef WARN_UNOPENED # define WARN_UNOPENED 11 #endif #ifndef WARN_MISC # define WARN_MISC 12 #endif #ifndef WARN_NUMERIC # define WARN_NUMERIC 13 #endif #ifndef WARN_ONCE # define WARN_ONCE 14 #endif #ifndef WARN_OVERFLOW # define WARN_OVERFLOW 15 #endif #ifndef WARN_PACK # define WARN_PACK 16 #endif #ifndef WARN_PORTABLE # define WARN_PORTABLE 17 #endif #ifndef WARN_RECURSION # define WARN_RECURSION 18 #endif #ifndef WARN_REDEFINE # define WARN_REDEFINE 19 #endif #ifndef WARN_REGEXP # define WARN_REGEXP 20 #endif #ifndef WARN_SEVERE # define WARN_SEVERE 21 #endif #ifndef WARN_DEBUGGING # define WARN_DEBUGGING 22 #endif #ifndef WARN_INPLACE # define WARN_INPLACE 23 #endif #ifndef WARN_INTERNAL # define WARN_INTERNAL 24 #endif #ifndef WARN_MALLOC # define WARN_MALLOC 25 #endif #ifndef WARN_SIGNAL # define WARN_SIGNAL 26 #endif #ifndef WARN_SUBSTR # define WARN_SUBSTR 27 #endif #ifndef WARN_SYNTAX # define WARN_SYNTAX 28 #endif #ifndef WARN_AMBIGUOUS # define WARN_AMBIGUOUS 29 #endif #ifndef WARN_BAREWORD # define WARN_BAREWORD 30 #endif #ifndef WARN_DIGIT # define WARN_DIGIT 31 #endif #ifndef WARN_PARENTHESIS # define WARN_PARENTHESIS 32 #endif #ifndef WARN_PRECEDENCE # define WARN_PRECEDENCE 33 #endif #ifndef WARN_PRINTF # define WARN_PRINTF 34 #endif #ifndef WARN_PROTOTYPE # define WARN_PROTOTYPE 35 #endif #ifndef WARN_QW # define WARN_QW 36 #endif #ifndef WARN_RESERVED # define WARN_RESERVED 37 #endif #ifndef WARN_SEMICOLON # define WARN_SEMICOLON 38 #endif #ifndef WARN_TAINT # define WARN_TAINT 39 #endif #ifndef WARN_THREADS # define WARN_THREADS 40 #endif #ifndef WARN_UNINITIALIZED # define WARN_UNINITIALIZED 41 #endif #ifndef WARN_UNPACK # define WARN_UNPACK 42 #endif #ifndef WARN_UNTIE # define WARN_UNTIE 43 #endif #ifndef WARN_UTF8 # define WARN_UTF8 44 #endif #ifndef WARN_VOID # define WARN_VOID 45 #endif #ifndef WARN_ASSERTIONS # define WARN_ASSERTIONS 46 #endif #ifndef packWARN # define packWARN(a) (a) #endif #ifndef ckWARN # ifdef G_WARN_ON # define ckWARN(a) (PL_dowarn & G_WARN_ON) # else # define ckWARN(a) PL_dowarn # endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(warner) #if defined(NEED_warner) static void DPPP_(my_warner)(U32 err, const char *pat, ...); static #else extern void DPPP_(my_warner)(U32 err, const char *pat, ...); #endif #define Perl_warner DPPP_(my_warner) #if defined(NEED_warner) || defined(NEED_warner_GLOBAL) void DPPP_(my_warner)(U32 err, const char *pat, ...) { SV *sv; va_list args; PERL_UNUSED_ARG(err); va_start(args, pat); sv = vnewSVpvf(pat, &args); va_end(args); sv_2mortal(sv); warn("%s", SvPV_nolen(sv)); } #define warner Perl_warner #define Perl_warner_nocontext Perl_warner #endif #endif /* concatenating with "" ensures that only literal strings are accepted as argument * note that STR_WITH_LEN() can't be used as argument to macros or functions that * under some configurations might be macros */ #ifndef STR_WITH_LEN # define STR_WITH_LEN(s) (s ""), (sizeof(s)-1) #endif #ifndef newSVpvs # define newSVpvs(str) newSVpvn(str "", sizeof(str) - 1) #endif #ifndef newSVpvs_flags # define newSVpvs_flags(str, flags) newSVpvn_flags(str "", sizeof(str) - 1, flags) #endif #ifndef newSVpvs_share # define newSVpvs_share(str) newSVpvn_share(str "", sizeof(str) - 1, 0) #endif #ifndef sv_catpvs # define sv_catpvs(sv, str) sv_catpvn(sv, str "", sizeof(str) - 1) #endif #ifndef sv_setpvs # define sv_setpvs(sv, str) sv_setpvn(sv, str "", sizeof(str) - 1) #endif #ifndef hv_fetchs # define hv_fetchs(hv, key, lval) hv_fetch(hv, key "", sizeof(key) - 1, lval) #endif #ifndef hv_stores # define hv_stores(hv, key, val) hv_store(hv, key "", sizeof(key) - 1, val, 0) #endif #ifndef gv_fetchpvs # define gv_fetchpvs(name, flags, svt) gv_fetchpvn_flags(name "", sizeof(name) - 1, flags, svt) #endif #ifndef gv_stashpvs # define gv_stashpvs(name, flags) gv_stashpvn(name "", sizeof(name) - 1, flags) #endif #ifndef get_cvs # define get_cvs(name, flags) get_cvn_flags(name "", sizeof(name)-1, flags) #endif #ifndef SvGETMAGIC # define SvGETMAGIC(x) STMT_START { if (SvGMAGICAL(x)) mg_get(x); } STMT_END #endif #ifndef PERL_MAGIC_sv # define PERL_MAGIC_sv '\0' #endif #ifndef PERL_MAGIC_overload # define PERL_MAGIC_overload 'A' #endif #ifndef PERL_MAGIC_overload_elem # define PERL_MAGIC_overload_elem 'a' #endif #ifndef PERL_MAGIC_overload_table # define PERL_MAGIC_overload_table 'c' #endif #ifndef PERL_MAGIC_bm # define PERL_MAGIC_bm 'B' #endif #ifndef PERL_MAGIC_regdata # define PERL_MAGIC_regdata 'D' #endif #ifndef PERL_MAGIC_regdatum # define PERL_MAGIC_regdatum 'd' #endif #ifndef PERL_MAGIC_env # define PERL_MAGIC_env 'E' #endif #ifndef PERL_MAGIC_envelem # define PERL_MAGIC_envelem 'e' #endif #ifndef PERL_MAGIC_fm # define PERL_MAGIC_fm 'f' #endif #ifndef PERL_MAGIC_regex_global # define PERL_MAGIC_regex_global 'g' #endif #ifndef PERL_MAGIC_isa # define PERL_MAGIC_isa 'I' #endif #ifndef PERL_MAGIC_isaelem # define PERL_MAGIC_isaelem 'i' #endif #ifndef PERL_MAGIC_nkeys # define PERL_MAGIC_nkeys 'k' #endif #ifndef PERL_MAGIC_dbfile # define PERL_MAGIC_dbfile 'L' #endif #ifndef PERL_MAGIC_dbline # define PERL_MAGIC_dbline 'l' #endif #ifndef PERL_MAGIC_mutex # define PERL_MAGIC_mutex 'm' #endif #ifndef PERL_MAGIC_shared # define PERL_MAGIC_shared 'N' #endif #ifndef PERL_MAGIC_shared_scalar # define PERL_MAGIC_shared_scalar 'n' #endif #ifndef PERL_MAGIC_collxfrm # define PERL_MAGIC_collxfrm 'o' #endif #ifndef PERL_MAGIC_tied # define PERL_MAGIC_tied 'P' #endif #ifndef PERL_MAGIC_tiedelem # define PERL_MAGIC_tiedelem 'p' #endif #ifndef PERL_MAGIC_tiedscalar # define PERL_MAGIC_tiedscalar 'q' #endif #ifndef PERL_MAGIC_qr # define PERL_MAGIC_qr 'r' #endif #ifndef PERL_MAGIC_sig # define PERL_MAGIC_sig 'S' #endif #ifndef PERL_MAGIC_sigelem # define PERL_MAGIC_sigelem 's' #endif #ifndef PERL_MAGIC_taint # define PERL_MAGIC_taint 't' #endif #ifndef PERL_MAGIC_uvar # define PERL_MAGIC_uvar 'U' #endif #ifndef PERL_MAGIC_uvar_elem # define PERL_MAGIC_uvar_elem 'u' #endif #ifndef PERL_MAGIC_vstring # define PERL_MAGIC_vstring 'V' #endif #ifndef PERL_MAGIC_vec # define PERL_MAGIC_vec 'v' #endif #ifndef PERL_MAGIC_utf8 # define PERL_MAGIC_utf8 'w' #endif #ifndef PERL_MAGIC_substr # define PERL_MAGIC_substr 'x' #endif #ifndef PERL_MAGIC_defelem # define PERL_MAGIC_defelem 'y' #endif #ifndef PERL_MAGIC_glob # define PERL_MAGIC_glob '*' #endif #ifndef PERL_MAGIC_arylen # define PERL_MAGIC_arylen '#' #endif #ifndef PERL_MAGIC_pos # define PERL_MAGIC_pos '.' #endif #ifndef PERL_MAGIC_backref # define PERL_MAGIC_backref '<' #endif #ifndef PERL_MAGIC_ext # define PERL_MAGIC_ext '~' #endif /* That's the best we can do... */ #ifndef sv_catpvn_nomg # define sv_catpvn_nomg sv_catpvn #endif #ifndef sv_catsv_nomg # define sv_catsv_nomg sv_catsv #endif #ifndef sv_setsv_nomg # define sv_setsv_nomg sv_setsv #endif #ifndef sv_pvn_nomg # define sv_pvn_nomg sv_pvn #endif #ifndef SvIV_nomg # define SvIV_nomg SvIV #endif #ifndef SvUV_nomg # define SvUV_nomg SvUV #endif #ifndef sv_catpv_mg # define sv_catpv_mg(sv, ptr) \ STMT_START { \ SV *TeMpSv = sv; \ sv_catpv(TeMpSv,ptr); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_catpvn_mg # define sv_catpvn_mg(sv, ptr, len) \ STMT_START { \ SV *TeMpSv = sv; \ sv_catpvn(TeMpSv,ptr,len); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_catsv_mg # define sv_catsv_mg(dsv, ssv) \ STMT_START { \ SV *TeMpSv = dsv; \ sv_catsv(TeMpSv,ssv); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setiv_mg # define sv_setiv_mg(sv, i) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setiv(TeMpSv,i); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setnv_mg # define sv_setnv_mg(sv, num) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setnv(TeMpSv,num); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setpv_mg # define sv_setpv_mg(sv, ptr) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setpv(TeMpSv,ptr); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setpvn_mg # define sv_setpvn_mg(sv, ptr, len) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setpvn(TeMpSv,ptr,len); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setsv_mg # define sv_setsv_mg(dsv, ssv) \ STMT_START { \ SV *TeMpSv = dsv; \ sv_setsv(TeMpSv,ssv); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setuv_mg # define sv_setuv_mg(sv, i) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setuv(TeMpSv,i); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_usepvn_mg # define sv_usepvn_mg(sv, ptr, len) \ STMT_START { \ SV *TeMpSv = sv; \ sv_usepvn(TeMpSv,ptr,len); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef SvVSTRING_mg # define SvVSTRING_mg(sv) (SvMAGICAL(sv) ? mg_find(sv, PERL_MAGIC_vstring) : NULL) #endif /* Hint: sv_magic_portable * This is a compatibility function that is only available with * Devel::PPPort. It is NOT in the perl core. * Its purpose is to mimic the 5.8.0 behaviour of sv_magic() when * it is being passed a name pointer with namlen == 0. In that * case, perl 5.8.0 and later store the pointer, not a copy of it. * The compatibility can be provided back to perl 5.004. With * earlier versions, the code will not compile. */ #if (PERL_BCDVERSION < 0x5004000) /* code that uses sv_magic_portable will not compile */ #elif (PERL_BCDVERSION < 0x5008000) # define sv_magic_portable(sv, obj, how, name, namlen) \ STMT_START { \ SV *SvMp_sv = (sv); \ char *SvMp_name = (char *) (name); \ I32 SvMp_namlen = (namlen); \ if (SvMp_name && SvMp_namlen == 0) \ { \ MAGIC *mg; \ sv_magic(SvMp_sv, obj, how, 0, 0); \ mg = SvMAGIC(SvMp_sv); \ mg->mg_len = -42; /* XXX: this is the tricky part */ \ mg->mg_ptr = SvMp_name; \ } \ else \ { \ sv_magic(SvMp_sv, obj, how, SvMp_name, SvMp_namlen); \ } \ } STMT_END #else # define sv_magic_portable(a, b, c, d, e) sv_magic(a, b, c, d, e) #endif #ifdef USE_ITHREADS #ifndef CopFILE # define CopFILE(c) ((c)->cop_file) #endif #ifndef CopFILEGV # define CopFILEGV(c) (CopFILE(c) ? gv_fetchfile(CopFILE(c)) : Nullgv) #endif #ifndef CopFILE_set # define CopFILE_set(c,pv) ((c)->cop_file = savepv(pv)) #endif #ifndef CopFILESV # define CopFILESV(c) (CopFILE(c) ? GvSV(gv_fetchfile(CopFILE(c))) : Nullsv) #endif #ifndef CopFILEAV # define CopFILEAV(c) (CopFILE(c) ? GvAV(gv_fetchfile(CopFILE(c))) : Nullav) #endif #ifndef CopSTASHPV # define CopSTASHPV(c) ((c)->cop_stashpv) #endif #ifndef CopSTASHPV_set # define CopSTASHPV_set(c,pv) ((c)->cop_stashpv = ((pv) ? savepv(pv) : Nullch)) #endif #ifndef CopSTASH # define CopSTASH(c) (CopSTASHPV(c) ? gv_stashpv(CopSTASHPV(c),GV_ADD) : Nullhv) #endif #ifndef CopSTASH_set # define CopSTASH_set(c,hv) CopSTASHPV_set(c, (hv) ? HvNAME(hv) : Nullch) #endif #ifndef CopSTASH_eq # define CopSTASH_eq(c,hv) ((hv) && (CopSTASHPV(c) == HvNAME(hv) \ || (CopSTASHPV(c) && HvNAME(hv) \ && strEQ(CopSTASHPV(c), HvNAME(hv))))) #endif #else #ifndef CopFILEGV # define CopFILEGV(c) ((c)->cop_filegv) #endif #ifndef CopFILEGV_set # define CopFILEGV_set(c,gv) ((c)->cop_filegv = (GV*)SvREFCNT_inc(gv)) #endif #ifndef CopFILE_set # define CopFILE_set(c,pv) CopFILEGV_set((c), gv_fetchfile(pv)) #endif #ifndef CopFILESV # define CopFILESV(c) (CopFILEGV(c) ? GvSV(CopFILEGV(c)) : Nullsv) #endif #ifndef CopFILEAV # define CopFILEAV(c) (CopFILEGV(c) ? GvAV(CopFILEGV(c)) : Nullav) #endif #ifndef CopFILE # define CopFILE(c) (CopFILESV(c) ? SvPVX(CopFILESV(c)) : Nullch) #endif #ifndef CopSTASH # define CopSTASH(c) ((c)->cop_stash) #endif #ifndef CopSTASH_set # define CopSTASH_set(c,hv) ((c)->cop_stash = (hv)) #endif #ifndef CopSTASHPV # define CopSTASHPV(c) (CopSTASH(c) ? HvNAME(CopSTASH(c)) : Nullch) #endif #ifndef CopSTASHPV_set # define CopSTASHPV_set(c,pv) CopSTASH_set((c), gv_stashpv(pv,GV_ADD)) #endif #ifndef CopSTASH_eq # define CopSTASH_eq(c,hv) (CopSTASH(c) == (hv)) #endif #endif /* USE_ITHREADS */ #ifndef IN_PERL_COMPILETIME # define IN_PERL_COMPILETIME (PL_curcop == &PL_compiling) #endif #ifndef IN_LOCALE_RUNTIME # define IN_LOCALE_RUNTIME (PL_curcop->op_private & HINT_LOCALE) #endif #ifndef IN_LOCALE_COMPILETIME # define IN_LOCALE_COMPILETIME (PL_hints & HINT_LOCALE) #endif #ifndef IN_LOCALE # define IN_LOCALE (IN_PERL_COMPILETIME ? IN_LOCALE_COMPILETIME : IN_LOCALE_RUNTIME) #endif #ifndef IS_NUMBER_IN_UV # define IS_NUMBER_IN_UV 0x01 #endif #ifndef IS_NUMBER_GREATER_THAN_UV_MAX # define IS_NUMBER_GREATER_THAN_UV_MAX 0x02 #endif #ifndef IS_NUMBER_NOT_INT # define IS_NUMBER_NOT_INT 0x04 #endif #ifndef IS_NUMBER_NEG # define IS_NUMBER_NEG 0x08 #endif #ifndef IS_NUMBER_INFINITY # define IS_NUMBER_INFINITY 0x10 #endif #ifndef IS_NUMBER_NAN # define IS_NUMBER_NAN 0x20 #endif #ifndef GROK_NUMERIC_RADIX # define GROK_NUMERIC_RADIX(sp, send) grok_numeric_radix(sp, send) #endif #ifndef PERL_SCAN_GREATER_THAN_UV_MAX # define PERL_SCAN_GREATER_THAN_UV_MAX 0x02 #endif #ifndef PERL_SCAN_SILENT_ILLDIGIT # define PERL_SCAN_SILENT_ILLDIGIT 0x04 #endif #ifndef PERL_SCAN_ALLOW_UNDERSCORES # define PERL_SCAN_ALLOW_UNDERSCORES 0x01 #endif #ifndef PERL_SCAN_DISALLOW_PREFIX # define PERL_SCAN_DISALLOW_PREFIX 0x02 #endif #ifndef grok_numeric_radix #if defined(NEED_grok_numeric_radix) static bool DPPP_(my_grok_numeric_radix)(pTHX_ const char ** sp, const char * send); static #else extern bool DPPP_(my_grok_numeric_radix)(pTHX_ const char ** sp, const char * send); #endif #ifdef grok_numeric_radix # undef grok_numeric_radix #endif #define grok_numeric_radix(a,b) DPPP_(my_grok_numeric_radix)(aTHX_ a,b) #define Perl_grok_numeric_radix DPPP_(my_grok_numeric_radix) #if defined(NEED_grok_numeric_radix) || defined(NEED_grok_numeric_radix_GLOBAL) bool DPPP_(my_grok_numeric_radix)(pTHX_ const char **sp, const char *send) { #ifdef USE_LOCALE_NUMERIC #ifdef PL_numeric_radix_sv if (PL_numeric_radix_sv && IN_LOCALE) { STRLEN len; char* radix = SvPV(PL_numeric_radix_sv, len); if (*sp + len <= send && memEQ(*sp, radix, len)) { *sp += len; return TRUE; } } #else /* older perls don't have PL_numeric_radix_sv so the radix * must manually be requested from locale.h */ #include dTHR; /* needed for older threaded perls */ struct lconv *lc = localeconv(); char *radix = lc->decimal_point; if (radix && IN_LOCALE) { STRLEN len = strlen(radix); if (*sp + len <= send && memEQ(*sp, radix, len)) { *sp += len; return TRUE; } } #endif #endif /* USE_LOCALE_NUMERIC */ /* always try "." if numeric radix didn't match because * we may have data from different locales mixed */ if (*sp < send && **sp == '.') { ++*sp; return TRUE; } return FALSE; } #endif #endif #ifndef grok_number #if defined(NEED_grok_number) static int DPPP_(my_grok_number)(pTHX_ const char * pv, STRLEN len, UV * valuep); static #else extern int DPPP_(my_grok_number)(pTHX_ const char * pv, STRLEN len, UV * valuep); #endif #ifdef grok_number # undef grok_number #endif #define grok_number(a,b,c) DPPP_(my_grok_number)(aTHX_ a,b,c) #define Perl_grok_number DPPP_(my_grok_number) #if defined(NEED_grok_number) || defined(NEED_grok_number_GLOBAL) int DPPP_(my_grok_number)(pTHX_ const char *pv, STRLEN len, UV *valuep) { const char *s = pv; const char *send = pv + len; const UV max_div_10 = UV_MAX / 10; const char max_mod_10 = UV_MAX % 10; int numtype = 0; int sawinf = 0; int sawnan = 0; while (s < send && isSPACE(*s)) s++; if (s == send) { return 0; } else if (*s == '-') { s++; numtype = IS_NUMBER_NEG; } else if (*s == '+') s++; if (s == send) return 0; /* next must be digit or the radix separator or beginning of infinity */ if (isDIGIT(*s)) { /* UVs are at least 32 bits, so the first 9 decimal digits cannot overflow. */ UV value = *s - '0'; /* This construction seems to be more optimiser friendly. (without it gcc does the isDIGIT test and the *s - '0' separately) With it gcc on arm is managing 6 instructions (6 cycles) per digit. In theory the optimiser could deduce how far to unroll the loop before checking for overflow. */ if (++s < send) { int digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { /* Now got 9 digits, so need to check each time for overflow. */ digit = *s - '0'; while (digit >= 0 && digit <= 9 && (value < max_div_10 || (value == max_div_10 && digit <= max_mod_10))) { value = value * 10 + digit; if (++s < send) digit = *s - '0'; else break; } if (digit >= 0 && digit <= 9 && (s < send)) { /* value overflowed. skip the remaining digits, don't worry about setting *valuep. */ do { s++; } while (s < send && isDIGIT(*s)); numtype |= IS_NUMBER_GREATER_THAN_UV_MAX; goto skip_value; } } } } } } } } } } } } } } } } } } numtype |= IS_NUMBER_IN_UV; if (valuep) *valuep = value; skip_value: if (GROK_NUMERIC_RADIX(&s, send)) { numtype |= IS_NUMBER_NOT_INT; while (s < send && isDIGIT(*s)) /* optional digits after the radix */ s++; } } else if (GROK_NUMERIC_RADIX(&s, send)) { numtype |= IS_NUMBER_NOT_INT | IS_NUMBER_IN_UV; /* valuep assigned below */ /* no digits before the radix means we need digits after it */ if (s < send && isDIGIT(*s)) { do { s++; } while (s < send && isDIGIT(*s)); if (valuep) { /* integer approximation is valid - it's 0. */ *valuep = 0; } } else return 0; } else if (*s == 'I' || *s == 'i') { s++; if (s == send || (*s != 'N' && *s != 'n')) return 0; s++; if (s == send || (*s != 'F' && *s != 'f')) return 0; s++; if (s < send && (*s == 'I' || *s == 'i')) { s++; if (s == send || (*s != 'N' && *s != 'n')) return 0; s++; if (s == send || (*s != 'I' && *s != 'i')) return 0; s++; if (s == send || (*s != 'T' && *s != 't')) return 0; s++; if (s == send || (*s != 'Y' && *s != 'y')) return 0; s++; } sawinf = 1; } else if (*s == 'N' || *s == 'n') { /* XXX TODO: There are signaling NaNs and quiet NaNs. */ s++; if (s == send || (*s != 'A' && *s != 'a')) return 0; s++; if (s == send || (*s != 'N' && *s != 'n')) return 0; s++; sawnan = 1; } else return 0; if (sawinf) { numtype &= IS_NUMBER_NEG; /* Keep track of sign */ numtype |= IS_NUMBER_INFINITY | IS_NUMBER_NOT_INT; } else if (sawnan) { numtype &= IS_NUMBER_NEG; /* Keep track of sign */ numtype |= IS_NUMBER_NAN | IS_NUMBER_NOT_INT; } else if (s < send) { /* we can have an optional exponent part */ if (*s == 'e' || *s == 'E') { /* The only flag we keep is sign. Blow away any "it's UV" */ numtype &= IS_NUMBER_NEG; numtype |= IS_NUMBER_NOT_INT; s++; if (s < send && (*s == '-' || *s == '+')) s++; if (s < send && isDIGIT(*s)) { do { s++; } while (s < send && isDIGIT(*s)); } else return 0; } } while (s < send && isSPACE(*s)) s++; if (s >= send) return numtype; if (len == 10 && memEQ(pv, "0 but true", 10)) { if (valuep) *valuep = 0; return IS_NUMBER_IN_UV; } return 0; } #endif #endif /* * The grok_* routines have been modified to use warn() instead of * Perl_warner(). Also, 'hexdigit' was the former name of PL_hexdigit, * which is why the stack variable has been renamed to 'xdigit'. */ #ifndef grok_bin #if defined(NEED_grok_bin) static UV DPPP_(my_grok_bin)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); static #else extern UV DPPP_(my_grok_bin)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); #endif #ifdef grok_bin # undef grok_bin #endif #define grok_bin(a,b,c,d) DPPP_(my_grok_bin)(aTHX_ a,b,c,d) #define Perl_grok_bin DPPP_(my_grok_bin) #if defined(NEED_grok_bin) || defined(NEED_grok_bin_GLOBAL) UV DPPP_(my_grok_bin)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result) { const char *s = start; STRLEN len = *len_p; UV value = 0; NV value_nv = 0; const UV max_div_2 = UV_MAX / 2; bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES; bool overflowed = FALSE; if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) { /* strip off leading b or 0b. for compatibility silently suffer "b" and "0b" as valid binary numbers. */ if (len >= 1) { if (s[0] == 'b') { s++; len--; } else if (len >= 2 && s[0] == '0' && s[1] == 'b') { s+=2; len-=2; } } } for (; len-- && *s; s++) { char bit = *s; if (bit == '0' || bit == '1') { /* Write it in this wonky order with a goto to attempt to get the compiler to make the common case integer-only loop pretty tight. With gcc seems to be much straighter code than old scan_bin. */ redo: if (!overflowed) { if (value <= max_div_2) { value = (value << 1) | (bit - '0'); continue; } /* Bah. We're just overflowed. */ warn("Integer overflow in binary number"); overflowed = TRUE; value_nv = (NV) value; } value_nv *= 2.0; /* If an NV has not enough bits in its mantissa to * represent a UV this summing of small low-order numbers * is a waste of time (because the NV cannot preserve * the low-order bits anyway): we could just remember when * did we overflow and in the end just multiply value_nv by the * right amount. */ value_nv += (NV)(bit - '0'); continue; } if (bit == '_' && len && allow_underscores && (bit = s[1]) && (bit == '0' || bit == '1')) { --len; ++s; goto redo; } if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT)) warn("Illegal binary digit '%c' ignored", *s); break; } if ( ( overflowed && value_nv > 4294967295.0) #if UVSIZE > 4 || (!overflowed && value > 0xffffffff ) #endif ) { warn("Binary number > 0b11111111111111111111111111111111 non-portable"); } *len_p = s - start; if (!overflowed) { *flags = 0; return value; } *flags = PERL_SCAN_GREATER_THAN_UV_MAX; if (result) *result = value_nv; return UV_MAX; } #endif #endif #ifndef grok_hex #if defined(NEED_grok_hex) static UV DPPP_(my_grok_hex)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); static #else extern UV DPPP_(my_grok_hex)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); #endif #ifdef grok_hex # undef grok_hex #endif #define grok_hex(a,b,c,d) DPPP_(my_grok_hex)(aTHX_ a,b,c,d) #define Perl_grok_hex DPPP_(my_grok_hex) #if defined(NEED_grok_hex) || defined(NEED_grok_hex_GLOBAL) UV DPPP_(my_grok_hex)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result) { const char *s = start; STRLEN len = *len_p; UV value = 0; NV value_nv = 0; const UV max_div_16 = UV_MAX / 16; bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES; bool overflowed = FALSE; const char *xdigit; if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) { /* strip off leading x or 0x. for compatibility silently suffer "x" and "0x" as valid hex numbers. */ if (len >= 1) { if (s[0] == 'x') { s++; len--; } else if (len >= 2 && s[0] == '0' && s[1] == 'x') { s+=2; len-=2; } } } for (; len-- && *s; s++) { xdigit = strchr((char *) PL_hexdigit, *s); if (xdigit) { /* Write it in this wonky order with a goto to attempt to get the compiler to make the common case integer-only loop pretty tight. With gcc seems to be much straighter code than old scan_hex. */ redo: if (!overflowed) { if (value <= max_div_16) { value = (value << 4) | ((xdigit - PL_hexdigit) & 15); continue; } warn("Integer overflow in hexadecimal number"); overflowed = TRUE; value_nv = (NV) value; } value_nv *= 16.0; /* If an NV has not enough bits in its mantissa to * represent a UV this summing of small low-order numbers * is a waste of time (because the NV cannot preserve * the low-order bits anyway): we could just remember when * did we overflow and in the end just multiply value_nv by the * right amount of 16-tuples. */ value_nv += (NV)((xdigit - PL_hexdigit) & 15); continue; } if (*s == '_' && len && allow_underscores && s[1] && (xdigit = strchr((char *) PL_hexdigit, s[1]))) { --len; ++s; goto redo; } if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT)) warn("Illegal hexadecimal digit '%c' ignored", *s); break; } if ( ( overflowed && value_nv > 4294967295.0) #if UVSIZE > 4 || (!overflowed && value > 0xffffffff ) #endif ) { warn("Hexadecimal number > 0xffffffff non-portable"); } *len_p = s - start; if (!overflowed) { *flags = 0; return value; } *flags = PERL_SCAN_GREATER_THAN_UV_MAX; if (result) *result = value_nv; return UV_MAX; } #endif #endif #ifndef grok_oct #if defined(NEED_grok_oct) static UV DPPP_(my_grok_oct)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); static #else extern UV DPPP_(my_grok_oct)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); #endif #ifdef grok_oct # undef grok_oct #endif #define grok_oct(a,b,c,d) DPPP_(my_grok_oct)(aTHX_ a,b,c,d) #define Perl_grok_oct DPPP_(my_grok_oct) #if defined(NEED_grok_oct) || defined(NEED_grok_oct_GLOBAL) UV DPPP_(my_grok_oct)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result) { const char *s = start; STRLEN len = *len_p; UV value = 0; NV value_nv = 0; const UV max_div_8 = UV_MAX / 8; bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES; bool overflowed = FALSE; for (; len-- && *s; s++) { /* gcc 2.95 optimiser not smart enough to figure that this subtraction out front allows slicker code. */ int digit = *s - '0'; if (digit >= 0 && digit <= 7) { /* Write it in this wonky order with a goto to attempt to get the compiler to make the common case integer-only loop pretty tight. */ redo: if (!overflowed) { if (value <= max_div_8) { value = (value << 3) | digit; continue; } /* Bah. We're just overflowed. */ warn("Integer overflow in octal number"); overflowed = TRUE; value_nv = (NV) value; } value_nv *= 8.0; /* If an NV has not enough bits in its mantissa to * represent a UV this summing of small low-order numbers * is a waste of time (because the NV cannot preserve * the low-order bits anyway): we could just remember when * did we overflow and in the end just multiply value_nv by the * right amount of 8-tuples. */ value_nv += (NV)digit; continue; } if (digit == ('_' - '0') && len && allow_underscores && (digit = s[1] - '0') && (digit >= 0 && digit <= 7)) { --len; ++s; goto redo; } /* Allow \octal to work the DWIM way (that is, stop scanning * as soon as non-octal characters are seen, complain only iff * someone seems to want to use the digits eight and nine). */ if (digit == 8 || digit == 9) { if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT)) warn("Illegal octal digit '%c' ignored", *s); } break; } if ( ( overflowed && value_nv > 4294967295.0) #if UVSIZE > 4 || (!overflowed && value > 0xffffffff ) #endif ) { warn("Octal number > 037777777777 non-portable"); } *len_p = s - start; if (!overflowed) { *flags = 0; return value; } *flags = PERL_SCAN_GREATER_THAN_UV_MAX; if (result) *result = value_nv; return UV_MAX; } #endif #endif #if !defined(my_snprintf) #if defined(NEED_my_snprintf) static int DPPP_(my_my_snprintf)(char * buffer, const Size_t len, const char * format, ...); static #else extern int DPPP_(my_my_snprintf)(char * buffer, const Size_t len, const char * format, ...); #endif #define my_snprintf DPPP_(my_my_snprintf) #define Perl_my_snprintf DPPP_(my_my_snprintf) #if defined(NEED_my_snprintf) || defined(NEED_my_snprintf_GLOBAL) int DPPP_(my_my_snprintf)(char *buffer, const Size_t len, const char *format, ...) { dTHX; int retval; va_list ap; va_start(ap, format); #ifdef HAS_VSNPRINTF retval = vsnprintf(buffer, len, format, ap); #else retval = vsprintf(buffer, format, ap); #endif va_end(ap); if (retval < 0 || (len > 0 && (Size_t)retval >= len)) Perl_croak(aTHX_ "panic: my_snprintf buffer overflow"); return retval; } #endif #endif #if !defined(my_sprintf) #if defined(NEED_my_sprintf) static int DPPP_(my_my_sprintf)(char * buffer, const char * pat, ...); static #else extern int DPPP_(my_my_sprintf)(char * buffer, const char * pat, ...); #endif #define my_sprintf DPPP_(my_my_sprintf) #define Perl_my_sprintf DPPP_(my_my_sprintf) #if defined(NEED_my_sprintf) || defined(NEED_my_sprintf_GLOBAL) int DPPP_(my_my_sprintf)(char *buffer, const char* pat, ...) { va_list args; va_start(args, pat); vsprintf(buffer, pat, args); va_end(args); return strlen(buffer); } #endif #endif #ifdef NO_XSLOCKS # ifdef dJMPENV # define dXCPT dJMPENV; int rEtV = 0 # define XCPT_TRY_START JMPENV_PUSH(rEtV); if (rEtV == 0) # define XCPT_TRY_END JMPENV_POP; # define XCPT_CATCH if (rEtV != 0) # define XCPT_RETHROW JMPENV_JUMP(rEtV) # else # define dXCPT Sigjmp_buf oldTOP; int rEtV = 0 # define XCPT_TRY_START Copy(top_env, oldTOP, 1, Sigjmp_buf); rEtV = Sigsetjmp(top_env, 1); if (rEtV == 0) # define XCPT_TRY_END Copy(oldTOP, top_env, 1, Sigjmp_buf); # define XCPT_CATCH if (rEtV != 0) # define XCPT_RETHROW Siglongjmp(top_env, rEtV) # endif #endif #if !defined(my_strlcat) #if defined(NEED_my_strlcat) static Size_t DPPP_(my_my_strlcat)(char * dst, const char * src, Size_t size); static #else extern Size_t DPPP_(my_my_strlcat)(char * dst, const char * src, Size_t size); #endif #define my_strlcat DPPP_(my_my_strlcat) #define Perl_my_strlcat DPPP_(my_my_strlcat) #if defined(NEED_my_strlcat) || defined(NEED_my_strlcat_GLOBAL) Size_t DPPP_(my_my_strlcat)(char *dst, const char *src, Size_t size) { Size_t used, length, copy; used = strlen(dst); length = strlen(src); if (size > 0 && used < size - 1) { copy = (length >= size - used) ? size - used - 1 : length; memcpy(dst + used, src, copy); dst[used + copy] = '\0'; } return used + length; } #endif #endif #if !defined(my_strlcpy) #if defined(NEED_my_strlcpy) static Size_t DPPP_(my_my_strlcpy)(char * dst, const char * src, Size_t size); static #else extern Size_t DPPP_(my_my_strlcpy)(char * dst, const char * src, Size_t size); #endif #define my_strlcpy DPPP_(my_my_strlcpy) #define Perl_my_strlcpy DPPP_(my_my_strlcpy) #if defined(NEED_my_strlcpy) || defined(NEED_my_strlcpy_GLOBAL) Size_t DPPP_(my_my_strlcpy)(char *dst, const char *src, Size_t size) { Size_t length, copy; length = strlen(src); if (size > 0) { copy = (length >= size) ? size - 1 : length; memcpy(dst, src, copy); dst[copy] = '\0'; } return length; } #endif #endif #ifndef PERL_PV_ESCAPE_QUOTE # define PERL_PV_ESCAPE_QUOTE 0x0001 #endif #ifndef PERL_PV_PRETTY_QUOTE # define PERL_PV_PRETTY_QUOTE PERL_PV_ESCAPE_QUOTE #endif #ifndef PERL_PV_PRETTY_ELLIPSES # define PERL_PV_PRETTY_ELLIPSES 0x0002 #endif #ifndef PERL_PV_PRETTY_LTGT # define PERL_PV_PRETTY_LTGT 0x0004 #endif #ifndef PERL_PV_ESCAPE_FIRSTCHAR # define PERL_PV_ESCAPE_FIRSTCHAR 0x0008 #endif #ifndef PERL_PV_ESCAPE_UNI # define PERL_PV_ESCAPE_UNI 0x0100 #endif #ifndef PERL_PV_ESCAPE_UNI_DETECT # define PERL_PV_ESCAPE_UNI_DETECT 0x0200 #endif #ifndef PERL_PV_ESCAPE_ALL # define PERL_PV_ESCAPE_ALL 0x1000 #endif #ifndef PERL_PV_ESCAPE_NOBACKSLASH # define PERL_PV_ESCAPE_NOBACKSLASH 0x2000 #endif #ifndef PERL_PV_ESCAPE_NOCLEAR # define PERL_PV_ESCAPE_NOCLEAR 0x4000 #endif #ifndef PERL_PV_ESCAPE_RE # define PERL_PV_ESCAPE_RE 0x8000 #endif #ifndef PERL_PV_PRETTY_NOCLEAR # define PERL_PV_PRETTY_NOCLEAR PERL_PV_ESCAPE_NOCLEAR #endif #ifndef PERL_PV_PRETTY_DUMP # define PERL_PV_PRETTY_DUMP PERL_PV_PRETTY_ELLIPSES|PERL_PV_PRETTY_QUOTE #endif #ifndef PERL_PV_PRETTY_REGPROP # define PERL_PV_PRETTY_REGPROP PERL_PV_PRETTY_ELLIPSES|PERL_PV_PRETTY_LTGT|PERL_PV_ESCAPE_RE #endif /* Hint: pv_escape * Note that unicode functionality is only backported to * those perl versions that support it. For older perl * versions, the implementation will fall back to bytes. */ #ifndef pv_escape #if defined(NEED_pv_escape) static char * DPPP_(my_pv_escape)(pTHX_ SV * dsv, char const * const str, const STRLEN count, const STRLEN max, STRLEN * const escaped, const U32 flags); static #else extern char * DPPP_(my_pv_escape)(pTHX_ SV * dsv, char const * const str, const STRLEN count, const STRLEN max, STRLEN * const escaped, const U32 flags); #endif #ifdef pv_escape # undef pv_escape #endif #define pv_escape(a,b,c,d,e,f) DPPP_(my_pv_escape)(aTHX_ a,b,c,d,e,f) #define Perl_pv_escape DPPP_(my_pv_escape) #if defined(NEED_pv_escape) || defined(NEED_pv_escape_GLOBAL) char * DPPP_(my_pv_escape)(pTHX_ SV *dsv, char const * const str, const STRLEN count, const STRLEN max, STRLEN * const escaped, const U32 flags) { const char esc = flags & PERL_PV_ESCAPE_RE ? '%' : '\\'; const char dq = flags & PERL_PV_ESCAPE_QUOTE ? '"' : esc; char octbuf[32] = "%123456789ABCDF"; STRLEN wrote = 0; STRLEN chsize = 0; STRLEN readsize = 1; #if defined(is_utf8_string) && defined(utf8_to_uvchr) bool isuni = flags & PERL_PV_ESCAPE_UNI ? 1 : 0; #endif const char *pv = str; const char * const end = pv + count; octbuf[0] = esc; if (!(flags & PERL_PV_ESCAPE_NOCLEAR)) sv_setpvs(dsv, ""); #if defined(is_utf8_string) && defined(utf8_to_uvchr) if ((flags & PERL_PV_ESCAPE_UNI_DETECT) && is_utf8_string((U8*)pv, count)) isuni = 1; #endif for (; pv < end && (!max || wrote < max) ; pv += readsize) { const UV u = #if defined(is_utf8_string) && defined(utf8_to_uvchr) isuni ? utf8_to_uvchr((U8*)pv, &readsize) : #endif (U8)*pv; const U8 c = (U8)u & 0xFF; if (u > 255 || (flags & PERL_PV_ESCAPE_ALL)) { if (flags & PERL_PV_ESCAPE_FIRSTCHAR) chsize = my_snprintf(octbuf, sizeof octbuf, "%"UVxf, u); else chsize = my_snprintf(octbuf, sizeof octbuf, "%cx{%"UVxf"}", esc, u); } else if (flags & PERL_PV_ESCAPE_NOBACKSLASH) { chsize = 1; } else { if (c == dq || c == esc || !isPRINT(c)) { chsize = 2; switch (c) { case '\\' : /* fallthrough */ case '%' : if (c == esc) octbuf[1] = esc; else chsize = 1; break; case '\v' : octbuf[1] = 'v'; break; case '\t' : octbuf[1] = 't'; break; case '\r' : octbuf[1] = 'r'; break; case '\n' : octbuf[1] = 'n'; break; case '\f' : octbuf[1] = 'f'; break; case '"' : if (dq == '"') octbuf[1] = '"'; else chsize = 1; break; default: chsize = my_snprintf(octbuf, sizeof octbuf, pv < end && isDIGIT((U8)*(pv+readsize)) ? "%c%03o" : "%c%o", esc, c); } } else { chsize = 1; } } if (max && wrote + chsize > max) { break; } else if (chsize > 1) { sv_catpvn(dsv, octbuf, chsize); wrote += chsize; } else { char tmp[2]; my_snprintf(tmp, sizeof tmp, "%c", c); sv_catpvn(dsv, tmp, 1); wrote++; } if (flags & PERL_PV_ESCAPE_FIRSTCHAR) break; } if (escaped != NULL) *escaped= pv - str; return SvPVX(dsv); } #endif #endif #ifndef pv_pretty #if defined(NEED_pv_pretty) static char * DPPP_(my_pv_pretty)(pTHX_ SV * dsv, char const * const str, const STRLEN count, const STRLEN max, char const * const start_color, char const * const end_color, const U32 flags); static #else extern char * DPPP_(my_pv_pretty)(pTHX_ SV * dsv, char const * const str, const STRLEN count, const STRLEN max, char const * const start_color, char const * const end_color, const U32 flags); #endif #ifdef pv_pretty # undef pv_pretty #endif #define pv_pretty(a,b,c,d,e,f,g) DPPP_(my_pv_pretty)(aTHX_ a,b,c,d,e,f,g) #define Perl_pv_pretty DPPP_(my_pv_pretty) #if defined(NEED_pv_pretty) || defined(NEED_pv_pretty_GLOBAL) char * DPPP_(my_pv_pretty)(pTHX_ SV *dsv, char const * const str, const STRLEN count, const STRLEN max, char const * const start_color, char const * const end_color, const U32 flags) { const U8 dq = (flags & PERL_PV_PRETTY_QUOTE) ? '"' : '%'; STRLEN escaped; if (!(flags & PERL_PV_PRETTY_NOCLEAR)) sv_setpvs(dsv, ""); if (dq == '"') sv_catpvs(dsv, "\""); else if (flags & PERL_PV_PRETTY_LTGT) sv_catpvs(dsv, "<"); if (start_color != NULL) sv_catpv(dsv, D_PPP_CONSTPV_ARG(start_color)); pv_escape(dsv, str, count, max, &escaped, flags | PERL_PV_ESCAPE_NOCLEAR); if (end_color != NULL) sv_catpv(dsv, D_PPP_CONSTPV_ARG(end_color)); if (dq == '"') sv_catpvs(dsv, "\""); else if (flags & PERL_PV_PRETTY_LTGT) sv_catpvs(dsv, ">"); if ((flags & PERL_PV_PRETTY_ELLIPSES) && escaped < count) sv_catpvs(dsv, "..."); return SvPVX(dsv); } #endif #endif #ifndef pv_display #if defined(NEED_pv_display) static char * DPPP_(my_pv_display)(pTHX_ SV * dsv, const char * pv, STRLEN cur, STRLEN len, STRLEN pvlim); static #else extern char * DPPP_(my_pv_display)(pTHX_ SV * dsv, const char * pv, STRLEN cur, STRLEN len, STRLEN pvlim); #endif #ifdef pv_display # undef pv_display #endif #define pv_display(a,b,c,d,e) DPPP_(my_pv_display)(aTHX_ a,b,c,d,e) #define Perl_pv_display DPPP_(my_pv_display) #if defined(NEED_pv_display) || defined(NEED_pv_display_GLOBAL) char * DPPP_(my_pv_display)(pTHX_ SV *dsv, const char *pv, STRLEN cur, STRLEN len, STRLEN pvlim) { pv_pretty(dsv, pv, cur, pvlim, NULL, NULL, PERL_PV_PRETTY_DUMP); if (len > cur && pv[cur] == '\0') sv_catpvs(dsv, "\\0"); return SvPVX(dsv); } #endif #endif #endif /* _P_P_PORTABILITY_H_ */ /* End of File ppport.h */ yuma123_2.14/netconf/perl/yangrpc/MANIFEST0000664000175000017500000000012314770023131020311 0ustar vladimirvladimirChanges Makefile.PL MANIFEST ppport.h README yangrpc.xs t/yangrpc.t lib/yangrpc.pm yuma123_2.14/netconf/perl/yangcli/0000775000175000017500000000000014770023131017147 5ustar vladimirvladimiryuma123_2.14/netconf/perl/yangcli/t/0000775000175000017500000000000014770023131017412 5ustar vladimirvladimiryuma123_2.14/netconf/perl/yangcli/t/yangcli.t0000664000175000017500000000075314770023131021232 0ustar vladimirvladimir# Before 'make install' is performed this script should be runnable with # 'make test'. After 'make install' it should work as 'perl yangcli.t' ######################### # change 'tests => 1' to 'tests => last_test_to_print'; use strict; use warnings; use Test::More tests => 1; BEGIN { use_ok('yangcli') }; ######################### # Insert your test code below, the Test::More module is use()ed here so read # its man page ( perldoc Test::More ) for help writing this test script. yuma123_2.14/netconf/perl/yangcli/lib/0000775000175000017500000000000014770023131017715 5ustar vladimirvladimiryuma123_2.14/netconf/perl/yangcli/lib/yangcli.pm0000664000175000017500000000426614770023131021711 0ustar vladimirvladimirpackage yangcli; #use 5.020002; use 5.018002; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use yangcli ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( ); our $VERSION = '0.01'; require XSLoader; XSLoader::load('yangcli', $VERSION); # Preloaded methods go here. sub yangcli { my $conn = $_[0]; my $cli_cmd = $_[1]; my $rpc_val=0; my $reply_val=0; my $reply_xml_str=0; my $res; $res = yangrpc::parse_cli($conn,$cli_cmd, $rpc_val); $res = yangrpc::rpc($conn,$rpc_val,$reply_val); $res = yuma::val_make_serialized_string($reply_val, 5, $reply_xml_str); my $parser = XML::LibXML->new(); my $doc = $parser->parse_string( $reply_xml_str ); return $doc; } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME yangcli - Perl extension for blah blah blah =head1 SYNOPSIS use yangcli; blah blah blah =head1 DESCRIPTION Stub documentation for yangcli, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR vladimir, Evladimir@E =head1 COPYRIGHT AND LICENSE Copyright (C) 2016 by vladimir This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.20.2 or, at your option, any later version of Perl 5 you may have available. =cut yuma123_2.14/netconf/perl/yangcli/README0000664000175000017500000000221514770023131020027 0ustar vladimirvladimiryangcli version 0.01 ==================== The README is used to introduce the module and provide instructions on how to install the module, any machine dependencies it may have (for example C compilers and installed libraries) and any other information that should be provided before the module is installed. A README file is required for CPAN modules since CPAN extracts the README file from a module distribution so that people browsing the archive can use it get an idea of the modules uses. It is usually a good idea to provide version information here so that people can decide whether fixes for the module are worth downloading. INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES This module requires these other modules and libraries: blah blah blah COPYRIGHT AND LICENCE Put the correct copyright and licence information here. Copyright (C) 2016 by vladimir This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.20.2 or, at your option, any later version of Perl 5 you may have available. yuma123_2.14/netconf/perl/yangcli/Changes0000664000175000017500000000022514770023131020441 0ustar vladimirvladimirRevision history for Perl extension yangcli. 0.01 Fri Jul 8 17:21:04 2016 - original version; created by h2xs 1.23 with options -A -n yangcli yuma123_2.14/netconf/perl/yangcli/yangcli.xs0000664000175000017500000000022214770023131021145 0ustar vladimirvladimir#define PERL_NO_GET_CONTEXT #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" MODULE = yangcli PACKAGE = yangcli yuma123_2.14/netconf/perl/yangcli/Makefile.PL0000664000175000017500000000163414770023131021125 0ustar vladimirvladimiruse 5.018002; use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( NAME => 'yangcli', VERSION_FROM => 'lib/yangcli.pm', # finds $VERSION, requires EU::MM from perl >= 5.5 PREREQ_PM => {}, # e.g., Module::Name => 1.1 ABSTRACT_FROM => 'lib/yangcli.pm', # retrieve abstract from module AUTHOR => 'vladimir ', #LICENSE => 'perl', #Value must be from legacy list of licenses here #http://search.cpan.org/perldoc?Module%3A%3ABuild%3A%3AAPI LIBS => [''], # e.g., '-lm' DEFINE => '', # e.g., '-DHAVE_SOMETHING' INC => '-I.', # e.g., '-I. -I/usr/include/other' # Un-comment this if you add C files to link with later: # OBJECT => '$(O_FILES)', # link all the C files too ); yuma123_2.14/netconf/perl/yangcli/ppport.h0000664000175000017500000055600214770023131020654 0ustar vladimirvladimir#if 0 <<'SKIP'; #endif /* ---------------------------------------------------------------------- ppport.h -- Perl/Pollution/Portability Version 3.21 Automatically created by Devel::PPPort running under perl 5.020002. Do NOT edit this file directly! -- Edit PPPort_pm.PL and the includes in parts/inc/ instead. Use 'perldoc ppport.h' to view the documentation below. ---------------------------------------------------------------------- SKIP =pod =head1 NAME ppport.h - Perl/Pollution/Portability version 3.21 =head1 SYNOPSIS perl ppport.h [options] [source files] Searches current directory for files if no [source files] are given --help show short help --version show version --patch=file write one patch file with changes --copy=suffix write changed copies with suffix --diff=program use diff program and options --compat-version=version provide compatibility with Perl version --cplusplus accept C++ comments --quiet don't output anything except fatal errors --nodiag don't show diagnostics --nohints don't show hints --nochanges don't suggest changes --nofilter don't filter input files --strip strip all script and doc functionality from ppport.h --list-provided list provided API --list-unsupported list unsupported API --api-info=name show Perl API portability information =head1 COMPATIBILITY This version of F is designed to support operation with Perl installations back to 5.003, and has been tested up to 5.11.5. =head1 OPTIONS =head2 --help Display a brief usage summary. =head2 --version Display the version of F. =head2 --patch=I If this option is given, a single patch file will be created if any changes are suggested. This requires a working diff program to be installed on your system. =head2 --copy=I If this option is given, a copy of each file will be saved with the given suffix that contains the suggested changes. This does not require any external programs. Note that this does not automagially add a dot between the original filename and the suffix. If you want the dot, you have to include it in the option argument. If neither C<--patch> or C<--copy> are given, the default is to simply print the diffs for each file. This requires either C or a C program to be installed. =head2 --diff=I Manually set the diff program and options to use. The default is to use C, when installed, and output unified context diffs. =head2 --compat-version=I Tell F to check for compatibility with the given Perl version. The default is to check for compatibility with Perl version 5.003. You can use this option to reduce the output of F if you intend to be backward compatible only down to a certain Perl version. =head2 --cplusplus Usually, F will detect C++ style comments and replace them with C style comments for portability reasons. Using this option instructs F to leave C++ comments untouched. =head2 --quiet Be quiet. Don't print anything except fatal errors. =head2 --nodiag Don't output any diagnostic messages. Only portability alerts will be printed. =head2 --nohints Don't output any hints. Hints often contain useful portability notes. Warnings will still be displayed. =head2 --nochanges Don't suggest any changes. Only give diagnostic output and hints unless these are also deactivated. =head2 --nofilter Don't filter the list of input files. By default, files not looking like source code (i.e. not *.xs, *.c, *.cc, *.cpp or *.h) are skipped. =head2 --strip Strip all script and documentation functionality from F. This reduces the size of F dramatically and may be useful if you want to include F in smaller modules without increasing their distribution size too much. The stripped F will have a C<--unstrip> option that allows you to undo the stripping, but only if an appropriate C module is installed. =head2 --list-provided Lists the API elements for which compatibility is provided by F. Also lists if it must be explicitly requested, if it has dependencies, and if there are hints or warnings for it. =head2 --list-unsupported Lists the API elements that are known not to be supported by F and below which version of Perl they probably won't be available or work. =head2 --api-info=I Show portability information for API elements matching I. If I is surrounded by slashes, it is interpreted as a regular expression. =head1 DESCRIPTION In order for a Perl extension (XS) module to be as portable as possible across differing versions of Perl itself, certain steps need to be taken. =over 4 =item * Including this header is the first major one. This alone will give you access to a large part of the Perl API that hasn't been available in earlier Perl releases. Use perl ppport.h --list-provided to see which API elements are provided by ppport.h. =item * You should avoid using deprecated parts of the API. For example, using global Perl variables without the C prefix is deprecated. Also, some API functions used to have a C prefix. Using this form is also deprecated. You can safely use the supported API, as F will provide wrappers for older Perl versions. =item * If you use one of a few functions or variables that were not present in earlier versions of Perl, and that can't be provided using a macro, you have to explicitly request support for these functions by adding one or more C<#define>s in your source code before the inclusion of F. These functions or variables will be marked C in the list shown by C<--list-provided>. Depending on whether you module has a single or multiple files that use such functions or variables, you want either C or global variants. For a C function or variable (used only in a single source file), use: #define NEED_function #define NEED_variable For a global function or variable (used in multiple source files), use: #define NEED_function_GLOBAL #define NEED_variable_GLOBAL Note that you mustn't have more than one global request for the same function or variable in your project. Function / Variable Static Request Global Request ----------------------------------------------------------------------------------------- PL_parser NEED_PL_parser NEED_PL_parser_GLOBAL PL_signals NEED_PL_signals NEED_PL_signals_GLOBAL eval_pv() NEED_eval_pv NEED_eval_pv_GLOBAL grok_bin() NEED_grok_bin NEED_grok_bin_GLOBAL grok_hex() NEED_grok_hex NEED_grok_hex_GLOBAL grok_number() NEED_grok_number NEED_grok_number_GLOBAL grok_numeric_radix() NEED_grok_numeric_radix NEED_grok_numeric_radix_GLOBAL grok_oct() NEED_grok_oct NEED_grok_oct_GLOBAL load_module() NEED_load_module NEED_load_module_GLOBAL my_snprintf() NEED_my_snprintf NEED_my_snprintf_GLOBAL my_sprintf() NEED_my_sprintf NEED_my_sprintf_GLOBAL my_strlcat() NEED_my_strlcat NEED_my_strlcat_GLOBAL my_strlcpy() NEED_my_strlcpy NEED_my_strlcpy_GLOBAL newCONSTSUB() NEED_newCONSTSUB NEED_newCONSTSUB_GLOBAL newRV_noinc() NEED_newRV_noinc NEED_newRV_noinc_GLOBAL newSV_type() NEED_newSV_type NEED_newSV_type_GLOBAL newSVpvn_flags() NEED_newSVpvn_flags NEED_newSVpvn_flags_GLOBAL newSVpvn_share() NEED_newSVpvn_share NEED_newSVpvn_share_GLOBAL pv_display() NEED_pv_display NEED_pv_display_GLOBAL pv_escape() NEED_pv_escape NEED_pv_escape_GLOBAL pv_pretty() NEED_pv_pretty NEED_pv_pretty_GLOBAL sv_2pv_flags() NEED_sv_2pv_flags NEED_sv_2pv_flags_GLOBAL sv_2pvbyte() NEED_sv_2pvbyte NEED_sv_2pvbyte_GLOBAL sv_catpvf_mg() NEED_sv_catpvf_mg NEED_sv_catpvf_mg_GLOBAL sv_catpvf_mg_nocontext() NEED_sv_catpvf_mg_nocontext NEED_sv_catpvf_mg_nocontext_GLOBAL sv_pvn_force_flags() NEED_sv_pvn_force_flags NEED_sv_pvn_force_flags_GLOBAL sv_setpvf_mg() NEED_sv_setpvf_mg NEED_sv_setpvf_mg_GLOBAL sv_setpvf_mg_nocontext() NEED_sv_setpvf_mg_nocontext NEED_sv_setpvf_mg_nocontext_GLOBAL vload_module() NEED_vload_module NEED_vload_module_GLOBAL vnewSVpvf() NEED_vnewSVpvf NEED_vnewSVpvf_GLOBAL warner() NEED_warner NEED_warner_GLOBAL To avoid namespace conflicts, you can change the namespace of the explicitly exported functions / variables using the C macro. Just C<#define> the macro before including C: #define DPPP_NAMESPACE MyOwnNamespace_ #include "ppport.h" The default namespace is C. =back The good thing is that most of the above can be checked by running F on your source code. See the next section for details. =head1 EXAMPLES To verify whether F is needed for your module, whether you should make any changes to your code, and whether any special defines should be used, F can be run as a Perl script to check your source code. Simply say: perl ppport.h The result will usually be a list of patches suggesting changes that should at least be acceptable, if not necessarily the most efficient solution, or a fix for all possible problems. If you know that your XS module uses features only available in newer Perl releases, if you're aware that it uses C++ comments, and if you want all suggestions as a single patch file, you could use something like this: perl ppport.h --compat-version=5.6.0 --cplusplus --patch=test.diff If you only want your code to be scanned without any suggestions for changes, use: perl ppport.h --nochanges You can specify a different C program or options, using the C<--diff> option: perl ppport.h --diff='diff -C 10' This would output context diffs with 10 lines of context. If you want to create patched copies of your files instead, use: perl ppport.h --copy=.new To display portability information for the C function, use: perl ppport.h --api-info=newSVpvn Since the argument to C<--api-info> can be a regular expression, you can use perl ppport.h --api-info=/_nomg$/ to display portability information for all C<_nomg> functions or perl ppport.h --api-info=/./ to display information for all known API elements. =head1 BUGS If this version of F is causing failure during the compilation of this module, please check if newer versions of either this module or C are available on CPAN before sending a bug report. If F was generated using the latest version of C and is causing failure of this module, please file a bug report using the CPAN Request Tracker at L. Please include the following information: =over 4 =item 1. The complete output from running "perl -V" =item 2. This file. =item 3. The name and version of the module you were trying to build. =item 4. A full log of the build that failed. =item 5. Any other information that you think could be relevant. =back For the latest version of this code, please get the C module from CPAN. =head1 COPYRIGHT Version 3.x, Copyright (c) 2004-2013, Marcus Holland-Moritz. Version 2.x, Copyright (C) 2001, Paul Marquess. Version 1.x, Copyright (C) 1999, Kenneth Albanowski. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO See L. =cut use strict; # Disable broken TRIE-optimization BEGIN { eval '${^RE_TRIE_MAXBUF} = -1' if $] >= 5.009004 && $] <= 5.009005 } my $VERSION = 3.21; my %opt = ( quiet => 0, diag => 1, hints => 1, changes => 1, cplusplus => 0, filter => 1, strip => 0, version => 0, ); my($ppport) = $0 =~ /([\w.]+)$/; my $LF = '(?:\r\n|[\r\n])'; # line feed my $HS = "[ \t]"; # horizontal whitespace # Never use C comments in this file! my $ccs = '/'.'*'; my $cce = '*'.'/'; my $rccs = quotemeta $ccs; my $rcce = quotemeta $cce; eval { require Getopt::Long; Getopt::Long::GetOptions(\%opt, qw( help quiet diag! filter! hints! changes! cplusplus strip version patch=s copy=s diff=s compat-version=s list-provided list-unsupported api-info=s )) or usage(); }; if ($@ and grep /^-/, @ARGV) { usage() if "@ARGV" =~ /^--?h(?:elp)?$/; die "Getopt::Long not found. Please don't use any options.\n"; } if ($opt{version}) { print "This is $0 $VERSION.\n"; exit 0; } usage() if $opt{help}; strip() if $opt{strip}; if (exists $opt{'compat-version'}) { my($r,$v,$s) = eval { parse_version($opt{'compat-version'}) }; if ($@) { die "Invalid version number format: '$opt{'compat-version'}'\n"; } die "Only Perl 5 is supported\n" if $r != 5; die "Invalid version number: $opt{'compat-version'}\n" if $v >= 1000 || $s >= 1000; $opt{'compat-version'} = sprintf "%d.%03d%03d", $r, $v, $s; } else { $opt{'compat-version'} = 5; } my %API = map { /^(\w+)\|([^|]*)\|([^|]*)\|(\w*)$/ ? ( $1 => { ($2 ? ( base => $2 ) : ()), ($3 ? ( todo => $3 ) : ()), (index($4, 'v') >= 0 ? ( varargs => 1 ) : ()), (index($4, 'p') >= 0 ? ( provided => 1 ) : ()), (index($4, 'n') >= 0 ? ( nothxarg => 1 ) : ()), } ) : die "invalid spec: $_" } qw( AvFILLp|5.004050||p AvFILL||| BhkDISABLE||5.019003| BhkENABLE||5.019003| BhkENTRY_set||5.019003| BhkENTRY||| BhkFLAGS||| CALL_BLOCK_HOOKS||| CLASS|||n CPERLscope|5.005000||p CX_CURPAD_SAVE||| CX_CURPAD_SV||| CopFILEAV|5.006000||p CopFILEGV_set|5.006000||p CopFILEGV|5.006000||p CopFILESV|5.006000||p CopFILE_set|5.006000||p CopFILE|5.006000||p CopSTASHPV_set|5.006000||p CopSTASHPV|5.006000||p CopSTASH_eq|5.006000||p CopSTASH_set|5.006000||p CopSTASH|5.006000||p CopyD|5.009002|5.004050|p Copy||5.004050| CvPADLIST||5.008001| CvSTASH||| CvWEAKOUTSIDE||| DEFSV_set|5.010001||p DEFSV|5.004050||p END_EXTERN_C|5.005000||p ENTER||| ERRSV|5.004050||p EXTEND||| EXTERN_C|5.005000||p F0convert|||n FREETMPS||| GIMME_V||5.004000|n GIMME|||n GROK_NUMERIC_RADIX|5.007002||p G_ARRAY||| G_DISCARD||| G_EVAL||| G_METHOD|5.006001||p G_NOARGS||| G_SCALAR||| G_VOID||5.004000| GetVars||| GvAV||| GvCV||| GvHV||| GvSVn|5.009003||p GvSV||| Gv_AMupdate||5.011000| HEf_SVKEY||5.004000| HeHASH||5.004000| HeKEY||5.004000| HeKLEN||5.004000| HePV||5.004000| HeSVKEY_force||5.004000| HeSVKEY_set||5.004000| HeSVKEY||5.004000| HeUTF8||5.010001| HeVAL||5.004000| HvENAMELEN||5.015004| HvENAMEUTF8||5.015004| HvENAME||5.013007| HvNAMELEN_get|5.009003||p HvNAMELEN||5.015004| HvNAMEUTF8||5.015004| HvNAME_get|5.009003||p HvNAME||| INT2PTR|5.006000||p IN_LOCALE_COMPILETIME|5.007002||p IN_LOCALE_RUNTIME|5.007002||p IN_LOCALE|5.007002||p IN_PERL_COMPILETIME|5.008001||p IS_NUMBER_GREATER_THAN_UV_MAX|5.007002||p IS_NUMBER_INFINITY|5.007002||p IS_NUMBER_IN_UV|5.007002||p IS_NUMBER_NAN|5.007003||p IS_NUMBER_NEG|5.007002||p IS_NUMBER_NOT_INT|5.007002||p IVSIZE|5.006000||p IVTYPE|5.006000||p IVdf|5.006000||p LEAVE||| LINKLIST||5.013006| LVRET||| MARK||| MULTICALL||5.019003| MY_CXT_CLONE|5.009002||p MY_CXT_INIT|5.007003||p MY_CXT|5.007003||p MoveD|5.009002|5.004050|p Move||5.004050| NOOP|5.005000||p NUM2PTR|5.006000||p NVTYPE|5.006000||p NVef|5.006001||p NVff|5.006001||p NVgf|5.006001||p Newxc|5.009003||p Newxz|5.009003||p Newx|5.009003||p Nullav||| Nullch||| Nullcv||| Nullhv||| Nullsv||| OP_CLASS||5.013007| OP_DESC||5.007003| OP_NAME||5.007003| ORIGMARK||| PAD_BASE_SV||| PAD_CLONE_VARS||| PAD_COMPNAME_FLAGS||| PAD_COMPNAME_GEN_set||| PAD_COMPNAME_GEN||| PAD_COMPNAME_OURSTASH||| PAD_COMPNAME_PV||| PAD_COMPNAME_TYPE||| PAD_RESTORE_LOCAL||| PAD_SAVE_LOCAL||| PAD_SAVE_SETNULLPAD||| PAD_SETSV||| PAD_SET_CUR_NOSAVE||| PAD_SET_CUR||| PAD_SVl||| PAD_SV||| PERLIO_FUNCS_CAST|5.009003||p PERLIO_FUNCS_DECL|5.009003||p PERL_ABS|5.008001||p PERL_BCDVERSION|5.019002||p PERL_GCC_BRACE_GROUPS_FORBIDDEN|5.008001||p PERL_HASH|5.004000||p PERL_INT_MAX|5.004000||p PERL_INT_MIN|5.004000||p PERL_LONG_MAX|5.004000||p PERL_LONG_MIN|5.004000||p PERL_MAGIC_arylen|5.007002||p PERL_MAGIC_backref|5.007002||p PERL_MAGIC_bm|5.007002||p PERL_MAGIC_collxfrm|5.007002||p PERL_MAGIC_dbfile|5.007002||p PERL_MAGIC_dbline|5.007002||p PERL_MAGIC_defelem|5.007002||p PERL_MAGIC_envelem|5.007002||p PERL_MAGIC_env|5.007002||p PERL_MAGIC_ext|5.007002||p PERL_MAGIC_fm|5.007002||p PERL_MAGIC_glob|5.019002||p PERL_MAGIC_isaelem|5.007002||p PERL_MAGIC_isa|5.007002||p PERL_MAGIC_mutex|5.019002||p PERL_MAGIC_nkeys|5.007002||p PERL_MAGIC_overload_elem|5.019002||p PERL_MAGIC_overload_table|5.007002||p PERL_MAGIC_overload|5.019002||p PERL_MAGIC_pos|5.007002||p PERL_MAGIC_qr|5.007002||p PERL_MAGIC_regdata|5.007002||p PERL_MAGIC_regdatum|5.007002||p PERL_MAGIC_regex_global|5.007002||p PERL_MAGIC_shared_scalar|5.007003||p PERL_MAGIC_shared|5.007003||p PERL_MAGIC_sigelem|5.007002||p PERL_MAGIC_sig|5.007002||p PERL_MAGIC_substr|5.007002||p PERL_MAGIC_sv|5.007002||p PERL_MAGIC_taint|5.007002||p PERL_MAGIC_tiedelem|5.007002||p PERL_MAGIC_tiedscalar|5.007002||p PERL_MAGIC_tied|5.007002||p PERL_MAGIC_utf8|5.008001||p PERL_MAGIC_uvar_elem|5.007003||p PERL_MAGIC_uvar|5.007002||p PERL_MAGIC_vec|5.007002||p PERL_MAGIC_vstring|5.008001||p PERL_PV_ESCAPE_ALL|5.009004||p PERL_PV_ESCAPE_FIRSTCHAR|5.009004||p PERL_PV_ESCAPE_NOBACKSLASH|5.009004||p PERL_PV_ESCAPE_NOCLEAR|5.009004||p PERL_PV_ESCAPE_QUOTE|5.009004||p PERL_PV_ESCAPE_RE|5.009005||p PERL_PV_ESCAPE_UNI_DETECT|5.009004||p PERL_PV_ESCAPE_UNI|5.009004||p PERL_PV_PRETTY_DUMP|5.009004||p PERL_PV_PRETTY_ELLIPSES|5.010000||p PERL_PV_PRETTY_LTGT|5.009004||p PERL_PV_PRETTY_NOCLEAR|5.010000||p PERL_PV_PRETTY_QUOTE|5.009004||p PERL_PV_PRETTY_REGPROP|5.009004||p PERL_QUAD_MAX|5.004000||p PERL_QUAD_MIN|5.004000||p PERL_REVISION|5.006000||p PERL_SCAN_ALLOW_UNDERSCORES|5.007003||p PERL_SCAN_DISALLOW_PREFIX|5.007003||p PERL_SCAN_GREATER_THAN_UV_MAX|5.007003||p PERL_SCAN_SILENT_ILLDIGIT|5.008001||p PERL_SHORT_MAX|5.004000||p PERL_SHORT_MIN|5.004000||p PERL_SIGNALS_UNSAFE_FLAG|5.008001||p PERL_SUBVERSION|5.006000||p PERL_SYS_INIT3||5.010000| PERL_SYS_INIT||5.010000| PERL_SYS_TERM||5.019003| PERL_UCHAR_MAX|5.004000||p PERL_UCHAR_MIN|5.004000||p PERL_UINT_MAX|5.004000||p PERL_UINT_MIN|5.004000||p PERL_ULONG_MAX|5.004000||p PERL_ULONG_MIN|5.004000||p PERL_UNUSED_ARG|5.009003||p PERL_UNUSED_CONTEXT|5.009004||p PERL_UNUSED_DECL|5.007002||p PERL_UNUSED_VAR|5.007002||p PERL_UQUAD_MAX|5.004000||p PERL_UQUAD_MIN|5.004000||p PERL_USE_GCC_BRACE_GROUPS|5.009004||p PERL_USHORT_MAX|5.004000||p PERL_USHORT_MIN|5.004000||p PERL_VERSION|5.006000||p PL_DBsignal|5.005000||p PL_DBsingle|||pn PL_DBsub|||pn PL_DBtrace|||pn PL_Sv|5.005000||p PL_bufend|5.019002||p PL_bufptr|5.019002||p PL_check||5.006000| PL_compiling|5.004050||p PL_comppad_name||5.017004| PL_comppad||5.008001| PL_copline|5.019002||p PL_curcop|5.004050||p PL_curpad||5.005000| PL_curstash|5.004050||p PL_debstash|5.004050||p PL_defgv|5.004050||p PL_diehook|5.004050||p PL_dirty|5.004050||p PL_dowarn|||pn PL_errgv|5.004050||p PL_error_count|5.019002||p PL_expect|5.019002||p PL_hexdigit|5.005000||p PL_hints|5.005000||p PL_in_my_stash|5.019002||p PL_in_my|5.019002||p PL_keyword_plugin||5.011002| PL_last_in_gv|||n PL_laststatval|5.005000||p PL_lex_state|5.019002||p PL_lex_stuff|5.019002||p PL_linestr|5.019002||p PL_modglobal||5.005000|n PL_na|5.004050||pn PL_no_modify|5.006000||p PL_ofsgv|||n PL_opfreehook||5.011000|n PL_parser|5.009005|5.009005|p PL_peepp||5.007003|n PL_perl_destruct_level|5.004050||p PL_perldb|5.004050||p PL_ppaddr|5.006000||p PL_rpeepp||5.013005|n PL_rsfp_filters|5.019002||p PL_rsfp|5.019002||p PL_rs|||n PL_signals|5.008001||p PL_stack_base|5.004050||p PL_stack_sp|5.004050||p PL_statcache|5.005000||p PL_stdingv|5.004050||p PL_sv_arenaroot|5.004050||p PL_sv_no|5.004050||pn PL_sv_undef|5.004050||pn PL_sv_yes|5.004050||pn PL_tainted|5.004050||p PL_tainting|5.004050||p PL_tokenbuf|5.019002||p POP_MULTICALL||5.019003| POPi|||n POPl|||n POPn|||n POPpbytex||5.007001|n POPpx||5.005030|n POPp|||n POPs|||n PTR2IV|5.006000||p PTR2NV|5.006000||p PTR2UV|5.006000||p PTR2nat|5.009003||p PTR2ul|5.007001||p PTRV|5.006000||p PUSHMARK||| PUSH_MULTICALL||5.019003| PUSHi||| PUSHmortal|5.009002||p PUSHn||| PUSHp||| PUSHs||| PUSHu|5.004000||p PUTBACK||| PadARRAY||5.019003| PadMAX||5.019003| PadlistARRAY||5.019003| PadlistMAX||5.019003| PadlistNAMESARRAY||5.019003| PadlistNAMESMAX||5.019003| PadlistNAMES||5.019003| PadlistREFCNT||5.017004| PadnameIsOUR||| PadnameIsSTATE||| PadnameLEN||5.019003| PadnameOURSTASH||| PadnameOUTER||| PadnamePV||5.019003| PadnameSV||5.019003| PadnameTYPE||| PadnameUTF8||5.019003| PadnamelistARRAY||5.019003| PadnamelistMAX||5.019003| PerlIO_clearerr||5.007003| PerlIO_close||5.007003| PerlIO_context_layers||5.009004| PerlIO_eof||5.007003| PerlIO_error||5.007003| PerlIO_fileno||5.007003| PerlIO_fill||5.007003| PerlIO_flush||5.007003| PerlIO_get_base||5.007003| PerlIO_get_bufsiz||5.007003| PerlIO_get_cnt||5.007003| PerlIO_get_ptr||5.007003| PerlIO_read||5.007003| PerlIO_seek||5.007003| PerlIO_set_cnt||5.007003| PerlIO_set_ptrcnt||5.007003| PerlIO_setlinebuf||5.007003| PerlIO_stderr||5.007003| PerlIO_stdin||5.007003| PerlIO_stdout||5.007003| PerlIO_tell||5.007003| PerlIO_unread||5.007003| PerlIO_write||5.007003| Perl_signbit||5.009005|n PoisonFree|5.009004||p PoisonNew|5.009004||p PoisonWith|5.009004||p Poison|5.008000||p READ_XDIGIT||5.017006| RETVAL|||n Renewc||| Renew||| SAVECLEARSV||| SAVECOMPPAD||| SAVEPADSV||| SAVETMPS||| SAVE_DEFSV|5.004050||p SPAGAIN||| SP||| START_EXTERN_C|5.005000||p START_MY_CXT|5.007003||p STMT_END|||p STMT_START|||p STR_WITH_LEN|5.009003||p ST||| SV_CONST_RETURN|5.009003||p SV_COW_DROP_PV|5.008001||p SV_COW_SHARED_HASH_KEYS|5.009005||p SV_GMAGIC|5.007002||p SV_HAS_TRAILING_NUL|5.009004||p SV_IMMEDIATE_UNREF|5.007001||p SV_MUTABLE_RETURN|5.009003||p SV_NOSTEAL|5.009002||p SV_SMAGIC|5.009003||p SV_UTF8_NO_ENCODING|5.008001||p SVfARG|5.009005||p SVf_UTF8|5.006000||p SVf|5.006000||p SVt_INVLIST||5.019002| SVt_IV||| SVt_NULL||| SVt_NV||| SVt_PVAV||| SVt_PVCV||| SVt_PVFM||| SVt_PVGV||| SVt_PVHV||| SVt_PVIO||| SVt_PVIV||| SVt_PVLV||| SVt_PVMG||| SVt_PVNV||| SVt_PV||| SVt_REGEXP||5.011000| Safefree||| Slab_Alloc||| Slab_Free||| Slab_to_ro||| Slab_to_rw||| StructCopy||| SvCUR_set||| SvCUR||| SvEND||| SvGAMAGIC||5.006001| SvGETMAGIC|5.004050||p SvGROW||| SvIOK_UV||5.006000| SvIOK_notUV||5.006000| SvIOK_off||| SvIOK_only_UV||5.006000| SvIOK_only||| SvIOK_on||| SvIOKp||| SvIOK||| SvIVX||| SvIV_nomg|5.009001||p SvIV_set||| SvIVx||| SvIV||| SvIsCOW_shared_hash||5.008003| SvIsCOW||5.008003| SvLEN_set||| SvLEN||| SvLOCK||5.007003| SvMAGIC_set|5.009003||p SvNIOK_off||| SvNIOKp||| SvNIOK||| SvNOK_off||| SvNOK_only||| SvNOK_on||| SvNOKp||| SvNOK||| SvNVX||| SvNV_nomg||5.013002| SvNV_set||| SvNVx||| SvNV||| SvOK||| SvOOK_offset||5.011000| SvOOK||| SvPOK_off||| SvPOK_only_UTF8||5.006000| SvPOK_only||| SvPOK_on||| SvPOKp||| SvPOK||| SvPVX_const|5.009003||p SvPVX_mutable|5.009003||p SvPVX||| SvPV_const|5.009003||p SvPV_flags_const_nolen|5.009003||p SvPV_flags_const|5.009003||p SvPV_flags_mutable|5.009003||p SvPV_flags|5.007002||p SvPV_force_flags_mutable|5.009003||p SvPV_force_flags_nolen|5.009003||p SvPV_force_flags|5.007002||p SvPV_force_mutable|5.009003||p SvPV_force_nolen|5.009003||p SvPV_force_nomg_nolen|5.009003||p SvPV_force_nomg|5.007002||p SvPV_force|||p SvPV_mutable|5.009003||p SvPV_nolen_const|5.009003||p SvPV_nolen|5.006000||p SvPV_nomg_const_nolen|5.009003||p SvPV_nomg_const|5.009003||p SvPV_nomg_nolen|5.013007||p SvPV_nomg|5.007002||p SvPV_renew|5.009003||p SvPV_set||| SvPVbyte_force||5.009002| SvPVbyte_nolen||5.006000| SvPVbytex_force||5.006000| SvPVbytex||5.006000| SvPVbyte|5.006000||p SvPVutf8_force||5.006000| SvPVutf8_nolen||5.006000| SvPVutf8x_force||5.006000| SvPVutf8x||5.006000| SvPVutf8||5.006000| SvPVx||| SvPV||| SvREFCNT_dec_NN||5.017007| SvREFCNT_dec||| SvREFCNT_inc_NN|5.009004||p SvREFCNT_inc_simple_NN|5.009004||p SvREFCNT_inc_simple_void_NN|5.009004||p SvREFCNT_inc_simple_void|5.009004||p SvREFCNT_inc_simple|5.009004||p SvREFCNT_inc_void_NN|5.009004||p SvREFCNT_inc_void|5.009004||p SvREFCNT_inc|||p SvREFCNT||| SvROK_off||| SvROK_on||| SvROK||| SvRV_set|5.009003||p SvRV||| SvRXOK||5.009005| SvRX||5.009005| SvSETMAGIC||| SvSHARED_HASH|5.009003||p SvSHARE||5.007003| SvSTASH_set|5.009003||p SvSTASH||| SvSetMagicSV_nosteal||5.004000| SvSetMagicSV||5.004000| SvSetSV_nosteal||5.004000| SvSetSV||| SvTAINTED_off||5.004000| SvTAINTED_on||5.004000| SvTAINTED||5.004000| SvTAINT||| SvTHINKFIRST||| SvTRUE_nomg||5.013006| SvTRUE||| SvTYPE||| SvUNLOCK||5.007003| SvUOK|5.007001|5.006000|p SvUPGRADE||| SvUTF8_off||5.006000| SvUTF8_on||5.006000| SvUTF8||5.006000| SvUVXx|5.004000||p SvUVX|5.004000||p SvUV_nomg|5.009001||p SvUV_set|5.009003||p SvUVx|5.004000||p SvUV|5.004000||p SvVOK||5.008001| SvVSTRING_mg|5.009004||p THIS|||n UNDERBAR|5.009002||p UTF8_MAXBYTES|5.009002||p UVSIZE|5.006000||p UVTYPE|5.006000||p UVXf|5.007001||p UVof|5.006000||p UVuf|5.006000||p UVxf|5.006000||p WARN_ALL|5.006000||p WARN_AMBIGUOUS|5.006000||p WARN_ASSERTIONS|5.019002||p WARN_BAREWORD|5.006000||p WARN_CLOSED|5.006000||p WARN_CLOSURE|5.006000||p WARN_DEBUGGING|5.006000||p WARN_DEPRECATED|5.006000||p WARN_DIGIT|5.006000||p WARN_EXEC|5.006000||p WARN_EXITING|5.006000||p WARN_GLOB|5.006000||p WARN_INPLACE|5.006000||p WARN_INTERNAL|5.006000||p WARN_IO|5.006000||p WARN_LAYER|5.008000||p WARN_MALLOC|5.006000||p WARN_MISC|5.006000||p WARN_NEWLINE|5.006000||p WARN_NUMERIC|5.006000||p WARN_ONCE|5.006000||p WARN_OVERFLOW|5.006000||p WARN_PACK|5.006000||p WARN_PARENTHESIS|5.006000||p WARN_PIPE|5.006000||p WARN_PORTABLE|5.006000||p WARN_PRECEDENCE|5.006000||p WARN_PRINTF|5.006000||p WARN_PROTOTYPE|5.006000||p WARN_QW|5.006000||p WARN_RECURSION|5.006000||p WARN_REDEFINE|5.006000||p WARN_REGEXP|5.006000||p WARN_RESERVED|5.006000||p WARN_SEMICOLON|5.006000||p WARN_SEVERE|5.006000||p WARN_SIGNAL|5.006000||p WARN_SUBSTR|5.006000||p WARN_SYNTAX|5.006000||p WARN_TAINT|5.006000||p WARN_THREADS|5.008000||p WARN_UNINITIALIZED|5.006000||p WARN_UNOPENED|5.006000||p WARN_UNPACK|5.006000||p WARN_UNTIE|5.006000||p WARN_UTF8|5.006000||p WARN_VOID|5.006000||p WIDEST_UTYPE|5.015004||p XCPT_CATCH|5.009002||p XCPT_RETHROW|5.009002|5.007001|p XCPT_TRY_END|5.009002|5.004000|p XCPT_TRY_START|5.009002|5.004000|p XPUSHi||| XPUSHmortal|5.009002||p XPUSHn||| XPUSHp||| XPUSHs||| XPUSHu|5.004000||p XSPROTO|5.010000||p XSRETURN_EMPTY||| XSRETURN_IV||| XSRETURN_NO||| XSRETURN_NV||| XSRETURN_PV||| XSRETURN_UNDEF||| XSRETURN_UV|5.008001||p XSRETURN_YES||| XSRETURN|||p XST_mIV||| XST_mNO||| XST_mNV||| XST_mPV||| XST_mUNDEF||| XST_mUV|5.008001||p XST_mYES||| XS_APIVERSION_BOOTCHECK||5.013004| XS_EXTERNAL||5.019003| XS_INTERNAL||5.019003| XS_VERSION_BOOTCHECK||| XS_VERSION||| XSprePUSH|5.006000||p XS||| XopDISABLE||5.019003| XopENABLE||5.019003| XopENTRY_set||5.019003| XopENTRY||5.019003| XopFLAGS||5.013007| ZeroD|5.009002||p Zero||| _aMY_CXT|5.007003||p _add_range_to_invlist||| _append_range_to_invlist||| _core_swash_init||| _get_swash_invlist||| _invlist_array_init||| _invlist_contains_cp||| _invlist_contents||| _invlist_dump||| _invlist_intersection_maybe_complement_2nd||| _invlist_intersection||| _invlist_invert_prop||| _invlist_invert||| _invlist_len||| _invlist_populate_swatch||| _invlist_search||| _invlist_subtract||| _invlist_union_maybe_complement_2nd||| _invlist_union||| _is_uni_FOO||5.017008| _is_uni_perl_idcont||5.017008| _is_uni_perl_idstart||5.017007| _is_utf8_FOO||5.017008| _is_utf8_mark||5.017008| _is_utf8_perl_idcont||5.017008| _is_utf8_perl_idstart||5.017007| _new_invlist_C_array||| _new_invlist||| _pMY_CXT|5.007003||p _swash_inversion_hash||| _swash_to_invlist||| _to_fold_latin1||| _to_uni_fold_flags||5.013011| _to_upper_title_latin1||| _to_utf8_fold_flags||5.015006| _to_utf8_lower_flags||5.015006| _to_utf8_title_flags||5.015006| _to_utf8_upper_flags||5.015006| aMY_CXT_|5.007003||p aMY_CXT|5.007003||p aTHXR_|5.019002||p aTHXR|5.019002||p aTHX_|5.006000||p aTHX|5.006000||p aassign_common_vars||| add_cp_to_invlist||| add_data|||n add_utf16_textfilter||| addmad||| adjust_size_and_find_bucket|||n adjust_stack_on_leave||| alloc_maybe_populate_EXACT||| alloccopstash||| allocmy||| amagic_call||| amagic_cmp_locale||| amagic_cmp||| amagic_deref_call||5.013007| amagic_i_ncmp||| amagic_is_enabled||| amagic_ncmp||| anonymise_cv_maybe||| any_dup||| ao||| append_madprops||| apply_attrs_my||| apply_attrs_string||5.006001| apply_attrs||| apply||| assert_uft8_cache_coherent||| atfork_lock||5.007003|n atfork_unlock||5.007003|n av_arylen_p||5.009003| av_clear||| av_create_and_push||5.009005| av_create_and_unshift_one||5.009005| av_delete||5.006000| av_exists||5.006000| av_extend_guts||| av_extend||| av_fetch||| av_fill||| av_iter_p||5.011000| av_len||| av_make||| av_pop||| av_push||| av_reify||| av_shift||| av_store||| av_tindex||5.017009| av_top_index||5.017009| av_undef||| av_unshift||| ax|||n bad_type_gv||| bad_type_pv||| bind_match||| block_end||| block_gimme||5.004000| block_start||| blockhook_register||5.013003| boolSV|5.004000||p boot_core_PerlIO||| boot_core_UNIVERSAL||| boot_core_mro||| bytes_cmp_utf8||5.013007| bytes_from_utf8||5.007001| bytes_to_uni|||n bytes_to_utf8||5.006001| call_argv|5.006000||p call_atexit||5.006000| call_list||5.004000| call_method|5.006000||p call_pv|5.006000||p call_sv|5.006000||p caller_cx||5.013005| calloc||5.007002|n cando||| cast_i32||5.006000| cast_iv||5.006000| cast_ulong||5.006000| cast_uv||5.006000| check_locale_boundary_crossing||| check_type_and_open||| check_uni||| check_utf8_print||| checkcomma||| ckWARN|5.006000||p ck_entersub_args_core||| ck_entersub_args_list||5.013006| ck_entersub_args_proto_or_list||5.013006| ck_entersub_args_proto||5.013006| ck_warner_d||5.011001|v ck_warner||5.011001|v ckwarn_common||| ckwarn_d||5.009003| ckwarn||5.009003| cl_and|||n cl_anything|||n cl_init|||n cl_is_anything|||n cl_or|||n clear_placeholders||| clone_params_del|||n clone_params_new|||n closest_cop||| compute_EXACTish||| convert||| cop_fetch_label||5.015001| cop_free||| cop_hints_2hv||5.013007| cop_hints_fetch_pvn||5.013007| cop_hints_fetch_pvs||5.013007| cop_hints_fetch_pv||5.013007| cop_hints_fetch_sv||5.013007| cop_store_label||5.015001| cophh_2hv||5.013007| cophh_copy||5.013007| cophh_delete_pvn||5.013007| cophh_delete_pvs||5.013007| cophh_delete_pv||5.013007| cophh_delete_sv||5.013007| cophh_fetch_pvn||5.013007| cophh_fetch_pvs||5.013007| cophh_fetch_pv||5.013007| cophh_fetch_sv||5.013007| cophh_free||5.013007| cophh_new_empty||5.019003| cophh_store_pvn||5.013007| cophh_store_pvs||5.013007| cophh_store_pv||5.013007| cophh_store_sv||5.013007| core_prototype||| core_regclass_swash||| coresub_op||| could_it_be_a_POSIX_class||| cr_textfilter||| create_eval_scope||| croak_memory_wrap||5.019003|n croak_no_mem|||n croak_no_modify||5.013003|n croak_nocontext|||vn croak_popstack|||n croak_sv||5.013001| croak_xs_usage||5.010001|n croak|||v csighandler||5.009003|n curmad||| current_re_engine||| curse||| custom_op_desc||5.007003| custom_op_name||5.007003| custom_op_register||5.013007| custom_op_xop||5.013007| cv_ckproto_len_flags||| cv_clone_into||| cv_clone||| cv_const_sv_or_av||| cv_const_sv||5.004000| cv_dump||| cv_forget_slab||| cv_get_call_checker||5.013006| cv_set_call_checker||5.013006| cv_undef||| cvgv_set||| cvstash_set||| cx_dump||5.005000| cx_dup||| cxinc||| dAXMARK|5.009003||p dAX|5.007002||p dITEMS|5.007002||p dMARK||| dMULTICALL||5.009003| dMY_CXT_SV|5.007003||p dMY_CXT|5.007003||p dNOOP|5.006000||p dORIGMARK||| dSP||| dTHR|5.004050||p dTHXR|5.019002||p dTHXa|5.006000||p dTHXoa|5.006000||p dTHX|5.006000||p dUNDERBAR|5.009002||p dVAR|5.009003||p dXCPT|5.009002||p dXSARGS||| dXSI32||| dXSTARG|5.006000||p deb_curcv||| deb_nocontext|||vn deb_stack_all||| deb_stack_n||| debop||5.005000| debprofdump||5.005000| debprof||| debstackptrs||5.007003| debstack||5.007003| debug_start_match||| deb||5.007003|v defelem_target||| del_sv||| delete_eval_scope||| delimcpy||5.004000|n deprecate_commaless_var_list||| despatch_signals||5.007001| destroy_matcher||| die_nocontext|||vn die_sv||5.013001| die_unwind||| die|||v dirp_dup||| div128||| djSP||| do_aexec5||| do_aexec||| do_aspawn||| do_binmode||5.004050| do_chomp||| do_close||| do_delete_local||| do_dump_pad||| do_eof||| do_exec3||| do_execfree||| do_exec||| do_gv_dump||5.006000| do_gvgv_dump||5.006000| do_hv_dump||5.006000| do_ipcctl||| do_ipcget||| do_join||| do_magic_dump||5.006000| do_msgrcv||| do_msgsnd||| do_ncmp||| do_oddball||| do_op_dump||5.006000| do_op_xmldump||| do_open9||5.006000| do_openn||5.007001| do_open||5.004000| do_pmop_dump||5.006000| do_pmop_xmldump||| do_print||| do_readline||| do_seek||| do_semop||| do_shmio||| do_smartmatch||| do_spawn_nowait||| do_spawn||| do_sprintf||| do_sv_dump||5.006000| do_sysseek||| do_tell||| do_trans_complex_utf8||| do_trans_complex||| do_trans_count_utf8||| do_trans_count||| do_trans_simple_utf8||| do_trans_simple||| do_trans||| do_vecget||| do_vecset||| do_vop||| docatch||| doeval||| dofile||| dofindlabel||| doform||| doing_taint||5.008001|n dooneliner||| doopen_pm||| doparseform||| dopoptoeval||| dopoptogiven||| dopoptolabel||| dopoptoloop||| dopoptosub_at||| dopoptowhen||| doref||5.009003| dounwind||| dowantarray||| dump_all_perl||| dump_all||5.006000| dump_eval||5.006000| dump_exec_pos||| dump_fds||| dump_form||5.006000| dump_indent||5.006000|v dump_mstats||| dump_packsubs_perl||| dump_packsubs||5.006000| dump_sub_perl||| dump_sub||5.006000| dump_sv_child||| dump_trie_interim_list||| dump_trie_interim_table||| dump_trie||| dump_vindent||5.006000| dumpuntil||| dup_attrlist||| emulate_cop_io||| eval_pv|5.006000||p eval_sv|5.006000||p exec_failed||| expect_number||| fbm_compile||5.005000| fbm_instr||5.005000| feature_is_enabled||| filter_add||| filter_del||| filter_gets||| filter_read||| finalize_optree||| finalize_op||| find_and_forget_pmops||| find_array_subscript||| find_beginning||| find_byclass||| find_hash_subscript||| find_in_my_stash||| find_lexical_cv||| find_runcv_where||| find_runcv||5.008001| find_rundefsv2||| find_rundefsvoffset||5.009002| find_rundefsv||5.013002| find_script||| find_uninit_var||| first_symbol|||n foldEQ_latin1||5.013008|n foldEQ_locale||5.013002|n foldEQ_utf8_flags||5.013010| foldEQ_utf8||5.013002| foldEQ||5.013002|n fold_constants||| forbid_setid||| force_ident_maybe_lex||| force_ident||| force_list||| force_next||| force_strict_version||| force_version||| force_word||| forget_pmop||| form_nocontext|||vn form_short_octal_warning||| form||5.004000|v fp_dup||| fprintf_nocontext|||vn free_global_struct||| free_tied_hv_pool||| free_tmps||| gen_constant_list||| get_and_check_backslash_N_name||| get_aux_mg||| get_av|5.006000||p get_context||5.006000|n get_cvn_flags|5.009005||p get_cvs|5.011000||p get_cv|5.006000||p get_db_sub||| get_debug_opts||| get_hash_seed||| get_hv|5.006000||p get_invlist_iter_addr||| get_invlist_offset_addr||| get_invlist_previous_index_addr||| get_mstats||| get_no_modify||| get_num||| get_op_descs||5.005000| get_op_names||5.005000| get_opargs||| get_ppaddr||5.006000| get_re_arg||| get_sv|5.006000||p get_vtbl||5.005030| getcwd_sv||5.007002| getenv_len||| glob_2number||| glob_assign_glob||| glob_assign_ref||| gp_dup||| gp_free||| gp_ref||| grok_bin|5.007003||p grok_bslash_N||| grok_bslash_c||| grok_bslash_o||| grok_bslash_x||| grok_hex|5.007003||p grok_number|5.007002||p grok_numeric_radix|5.007002||p grok_oct|5.007003||p group_end||| gv_AVadd||| gv_HVadd||| gv_IOadd||| gv_SVadd||| gv_add_by_type||5.011000| gv_autoload4||5.004000| gv_autoload_pvn||5.015004| gv_autoload_pv||5.015004| gv_autoload_sv||5.015004| gv_check||| gv_const_sv||5.009003| gv_dump||5.006000| gv_efullname3||5.004000| gv_efullname4||5.006001| gv_efullname||| gv_ename||| gv_fetchfile_flags||5.009005| gv_fetchfile||| gv_fetchmeth_autoload||5.007003| gv_fetchmeth_pv_autoload||5.015004| gv_fetchmeth_pvn_autoload||5.015004| gv_fetchmeth_pvn||5.015004| gv_fetchmeth_pv||5.015004| gv_fetchmeth_sv_autoload||5.015004| gv_fetchmeth_sv||5.015004| gv_fetchmethod_autoload||5.004000| gv_fetchmethod_pv_flags||5.015004| gv_fetchmethod_pvn_flags||5.015004| gv_fetchmethod_sv_flags||5.015004| gv_fetchmethod||| gv_fetchmeth||| gv_fetchpvn_flags|5.009002||p gv_fetchpvs|5.009004||p gv_fetchpv||| gv_fetchsv|5.009002||p gv_fullname3||5.004000| gv_fullname4||5.006001| gv_fullname||| gv_handler||5.007001| gv_init_pvn||5.015004| gv_init_pv||5.015004| gv_init_svtype||| gv_init_sv||5.015004| gv_init||| gv_magicalize_isa||| gv_name_set||5.009004| gv_stashpvn|5.004000||p gv_stashpvs|5.009003||p gv_stashpv||| gv_stashsv||| gv_try_downgrade||| handle_regex_sets||| he_dup||| hek_dup||| hfree_next_entry||| hfreeentries||| hsplit||| hv_assert||| hv_auxinit||| hv_backreferences_p||| hv_clear_placeholders||5.009001| hv_clear||| hv_common_key_len||5.010000| hv_common||5.010000| hv_copy_hints_hv||5.009004| hv_delayfree_ent||5.004000| hv_delete_common||| hv_delete_ent||5.004000| hv_delete||| hv_eiter_p||5.009003| hv_eiter_set||5.009003| hv_ename_add||| hv_ename_delete||| hv_exists_ent||5.004000| hv_exists||| hv_fetch_ent||5.004000| hv_fetchs|5.009003||p hv_fetch||| hv_fill||5.013002| hv_free_ent_ret||| hv_free_ent||5.004000| hv_iterinit||| hv_iterkeysv||5.004000| hv_iterkey||| hv_iternext_flags||5.008000| hv_iternextsv||| hv_iternext||| hv_iterval||| hv_kill_backrefs||| hv_ksplit||5.004000| hv_magic_check|||n hv_magic||| hv_name_set||5.009003| hv_notallowed||| hv_placeholders_get||5.009003| hv_placeholders_p||| hv_placeholders_set||5.009003| hv_rand_set||5.017011| hv_riter_p||5.009003| hv_riter_set||5.009003| hv_scalar||5.009001| hv_store_ent||5.004000| hv_store_flags||5.008000| hv_stores|5.009004||p hv_store||| hv_undef_flags||| hv_undef||| ibcmp_locale||5.004000| ibcmp_utf8||5.007003| ibcmp||| incline||| incpush_if_exists||| incpush_use_sep||| incpush||| ingroup||| init_argv_symbols||| init_constants||| init_dbargs||| init_debugger||| init_global_struct||| init_i18nl10n||5.006000| init_i18nl14n||5.006000| init_ids||| init_interp||| init_main_stash||| init_perllib||| init_postdump_symbols||| init_predump_symbols||| init_stacks||5.005000| init_tm||5.007002| inplace_aassign||| instr|||n intro_my||| intuit_method||| intuit_more||| invert||| invlist_array||| invlist_clone||| invlist_extend||| invlist_highest||| invlist_is_iterating||| invlist_iterfinish||| invlist_iterinit||| invlist_iternext||| invlist_max||| invlist_previous_index||| invlist_set_len||| invlist_set_previous_index||| invlist_trim||| invoke_exception_hook||| io_close||| isALNUMC|5.006000||p isALNUM_lazy||| isALPHANUMERIC||5.017008| isALPHA||| isASCII|5.006000|5.006000|p isBLANK|5.006001||p isCNTRL|5.006000|5.006000|p isDIGIT||| isFOO_lc||| isFOO_utf8_lc||| isGRAPH|5.006000||p isGV_with_GP|5.009004||p isIDCONT||5.017008| isIDFIRST_lazy||| isIDFIRST||| isLOWER||| isOCTAL||5.013005| isPRINT|5.004000||p isPSXSPC|5.006001||p isPUNCT|5.006000||p isSPACE||| isUPPER||| isWORDCHAR||5.013006| isXDIGIT|5.006000||p is_an_int||| is_ascii_string||5.011000|n is_cur_LC_category_utf8||| is_handle_constructor|||n is_list_assignment||| is_lvalue_sub||5.007001| is_uni_alnum_lc||5.006000| is_uni_alnumc_lc||5.017007| is_uni_alnumc||5.017007| is_uni_alnum||5.006000| is_uni_alpha_lc||5.006000| is_uni_alpha||5.006000| is_uni_ascii_lc||5.006000| is_uni_ascii||5.006000| is_uni_blank_lc||5.017002| is_uni_blank||5.017002| is_uni_cntrl_lc||5.006000| is_uni_cntrl||5.006000| is_uni_digit_lc||5.006000| is_uni_digit||5.006000| is_uni_graph_lc||5.006000| is_uni_graph||5.006000| is_uni_idfirst_lc||5.006000| is_uni_idfirst||5.006000| is_uni_lower_lc||5.006000| is_uni_lower||5.006000| is_uni_print_lc||5.006000| is_uni_print||5.006000| is_uni_punct_lc||5.006000| is_uni_punct||5.006000| is_uni_space_lc||5.006000| is_uni_space||5.006000| is_uni_upper_lc||5.006000| is_uni_upper||5.006000| is_uni_xdigit_lc||5.006000| is_uni_xdigit||5.006000| is_utf8_alnumc||5.017007| is_utf8_alnum||5.006000| is_utf8_alpha||5.006000| is_utf8_ascii||5.006000| is_utf8_blank||5.017002| is_utf8_char_buf||5.015008|n is_utf8_char_slow|||n is_utf8_char||5.006000|n is_utf8_cntrl||5.006000| is_utf8_common||| is_utf8_digit||5.006000| is_utf8_graph||5.006000| is_utf8_idcont||5.008000| is_utf8_idfirst||5.006000| is_utf8_lower||5.006000| is_utf8_mark||5.006000| is_utf8_perl_space||5.011001| is_utf8_perl_word||5.011001| is_utf8_posix_digit||5.011001| is_utf8_print||5.006000| is_utf8_punct||5.006000| is_utf8_space||5.006000| is_utf8_string_loclen||5.009003|n is_utf8_string_loc||5.008001|n is_utf8_string||5.006001|n is_utf8_upper||5.006000| is_utf8_xdigit||5.006000| is_utf8_xidcont||5.013010| is_utf8_xidfirst||5.013010| isa_lookup||| items|||n ix|||n jmaybe||| join_exact||| keyword_plugin_standard||| keyword||| leave_scope||| lex_bufutf8||5.011002| lex_discard_to||5.011002| lex_grow_linestr||5.011002| lex_next_chunk||5.011002| lex_peek_unichar||5.011002| lex_read_space||5.011002| lex_read_to||5.011002| lex_read_unichar||5.011002| lex_start||5.009005| lex_stuff_pvn||5.011002| lex_stuff_pvs||5.013005| lex_stuff_pv||5.013006| lex_stuff_sv||5.011002| lex_unstuff||5.011002| listkids||| list||| load_module_nocontext|||vn load_module|5.006000||pv localize||| looks_like_bool||| looks_like_number||| lop||| mPUSHi|5.009002||p mPUSHn|5.009002||p mPUSHp|5.009002||p mPUSHs|5.010001||p mPUSHu|5.009002||p mXPUSHi|5.009002||p mXPUSHn|5.009002||p mXPUSHp|5.009002||p mXPUSHs|5.010001||p mXPUSHu|5.009002||p mad_free||| madlex||| madparse||| magic_clear_all_env||| magic_cleararylen_p||| magic_clearenv||| magic_clearhints||| magic_clearhint||| magic_clearisa||| magic_clearpack||| magic_clearsig||| magic_copycallchecker||| magic_dump||5.006000| magic_existspack||| magic_freearylen_p||| magic_freeovrld||| magic_getarylen||| magic_getdefelem||| magic_getnkeys||| magic_getpack||| magic_getpos||| magic_getsig||| magic_getsubstr||| magic_gettaint||| magic_getuvar||| magic_getvec||| magic_get||| magic_killbackrefs||| magic_methcall1||| magic_methcall|||v magic_methpack||| magic_nextpack||| magic_regdata_cnt||| magic_regdatum_get||| magic_regdatum_set||| magic_scalarpack||| magic_set_all_env||| magic_setarylen||| magic_setcollxfrm||| magic_setdbline||| magic_setdefelem||| magic_setenv||| magic_sethint||| magic_setisa||| magic_setmglob||| magic_setnkeys||| magic_setpack||| magic_setpos||| magic_setregexp||| magic_setsig||| magic_setsubstr||| magic_settaint||| magic_setutf8||| magic_setuvar||| magic_setvec||| magic_set||| magic_sizepack||| magic_wipepack||| make_matcher||| make_trie_failtable||| make_trie||| malloc_good_size|||n malloced_size|||n malloc||5.007002|n markstack_grow||| matcher_matches_sv||| mayberelocate||| measure_struct||| memEQs|5.009005||p memEQ|5.004000||p memNEs|5.009005||p memNE|5.004000||p mem_collxfrm||| mem_log_common|||n mess_alloc||| mess_nocontext|||vn mess_sv||5.013001| mess||5.006000|v method_common||| mfree||5.007002|n mg_clear||| mg_copy||| mg_dup||| mg_find_mglob||| mg_findext||5.013008| mg_find||| mg_free_type||5.013006| mg_free||| mg_get||| mg_length||5.005000| mg_localize||| mg_magical||| mg_set||| mg_size||5.005000| mini_mktime||5.007002| minus_v||| missingterm||| mode_from_discipline||| modkids||| more_bodies||| more_sv||| moreswitches||| mro_clean_isarev||| mro_gather_and_rename||| mro_get_from_name||5.010001| mro_get_linear_isa_dfs||| mro_get_linear_isa||5.009005| mro_get_private_data||5.010001| mro_isa_changed_in||| mro_meta_dup||| mro_meta_init||| mro_method_changed_in||5.009005| mro_package_moved||| mro_register||5.010001| mro_set_mro||5.010001| mro_set_private_data||5.010001| mul128||| mulexp10|||n my_atof2||5.007002| my_atof||5.006000| my_attrs||| my_bcopy|||n my_bzero|||n my_chsize||| my_clearenv||| my_cxt_index||| my_cxt_init||| my_dirfd||5.009005| my_exit_jump||| my_exit||| my_failure_exit||5.004000| my_fflush_all||5.006000| my_fork||5.007003|n my_kid||| my_lstat_flags||| my_lstat||5.019003| my_memcmp|||n my_memset||5.004000|n my_pclose||5.004000| my_popen_list||5.007001| my_popen||5.004000| my_setenv||| my_snprintf|5.009004||pvn my_socketpair||5.007003|n my_sprintf|5.009003||pvn my_stat_flags||| my_stat||5.019003| my_strftime||5.007002| my_strlcat|5.009004||pn my_strlcpy|5.009004||pn my_unexec||| my_vsnprintf||5.009004|n need_utf8|||n newANONATTRSUB||5.006000| newANONHASH||| newANONLIST||| newANONSUB||| newASSIGNOP||| newATTRSUB_flags||| newATTRSUB||5.006000| newAVREF||| newAV||| newBINOP||| newCONDOP||| newCONSTSUB_flags||5.015006| newCONSTSUB|5.004050||p newCVREF||| newDEFSVOP||| newFORM||| newFOROP||5.013007| newGIVENOP||5.009003| newGIVWHENOP||| newGP||| newGVOP||| newGVREF||| newGVgen_flags||5.015004| newGVgen||| newHVREF||| newHVhv||5.005000| newHV||| newIO||| newLISTOP||| newLOGOP||| newLOOPEX||| newLOOPOP||| newMADPROP||| newMADsv||| newMYSUB||5.017004| newNULLLIST||| newOP||| newPADOP||| newPMOP||| newPROG||| newPVOP||| newRANGE||| newRV_inc|5.004000||p newRV_noinc|5.004000||p newRV||| newSLICEOP||| newSTATEOP||| newSTUB||| newSUB||| newSVOP||| newSVREF||| newSV_type|5.009005||p newSVhek||5.009003| newSViv||| newSVnv||| newSVpadname||5.017004| newSVpv_share||5.013006| newSVpvf_nocontext|||vn newSVpvf||5.004000|v newSVpvn_flags|5.010001||p newSVpvn_share|5.007001||p newSVpvn_utf8|5.010001||p newSVpvn|5.004050||p newSVpvs_flags|5.010001||p newSVpvs_share|5.009003||p newSVpvs|5.009003||p newSVpv||| newSVrv||| newSVsv||| newSVuv|5.006000||p newSV||| newTOKEN||| newUNOP||| newWHENOP||5.009003| newWHILEOP||5.013007| newXS_flags||5.009004| newXS_len_flags||| newXSproto||5.006000| newXS||5.006000| new_collate||5.006000| new_constant||| new_ctype||5.006000| new_he||| new_logop||| new_numeric||5.006000| new_stackinfo||5.005000| new_version||5.009000| new_warnings_bitfield||| next_symbol||| nextargv||| nextchar||| ninstr|||n no_bareword_allowed||| no_fh_allowed||| no_op||| not_a_number||| not_incrementable||| nothreadhook||5.008000| nuke_stacks||| num_overflow|||n oopsAV||| oopsHV||| op_append_elem||5.013006| op_append_list||5.013006| op_clear||| op_const_sv||| op_contextualize||5.013006| op_dump||5.006000| op_free||| op_getmad_weak||| op_getmad||| op_integerize||| op_linklist||5.013006| op_lvalue_flags||| op_lvalue||5.013007| op_null||5.007002| op_prepend_elem||5.013006| op_refcnt_dec||| op_refcnt_inc||| op_refcnt_lock||5.009002| op_refcnt_unlock||5.009002| op_scope||5.013007| op_std_init||| op_unscope||| op_xmldump||| open_script||| opslab_force_free||| opslab_free_nopad||| opslab_free||| pMY_CXT_|5.007003||p pMY_CXT|5.007003||p pTHX_|5.006000||p pTHX|5.006000||p packWARN|5.007003||p pack_cat||5.007003| pack_rec||| package_version||| package||| packlist||5.008001| pad_add_anon||5.008001| pad_add_name_pvn||5.015001| pad_add_name_pvs||5.015001| pad_add_name_pv||5.015001| pad_add_name_sv||5.015001| pad_alloc_name||| pad_alloc||| pad_block_start||| pad_check_dup||| pad_compname_type||5.009003| pad_findlex||| pad_findmy_pvn||5.015001| pad_findmy_pvs||5.015001| pad_findmy_pv||5.015001| pad_findmy_sv||5.015001| pad_fixup_inner_anons||| pad_free||| pad_leavemy||| pad_new||5.008001| pad_peg|||n pad_push||| pad_reset||| pad_setsv||| pad_sv||| pad_swipe||| pad_tidy||5.008001| padlist_dup||| padlist_store||| parse_arithexpr||5.013008| parse_barestmt||5.013007| parse_block||5.013007| parse_body||| parse_fullexpr||5.013008| parse_fullstmt||5.013005| parse_ident||| parse_label||5.013007| parse_listexpr||5.013008| parse_lparen_question_flags||| parse_stmtseq||5.013006| parse_termexpr||5.013008| parse_unicode_opts||| parser_dup||| parser_free_nexttoke_ops||| parser_free||| path_is_searchable|||n peep||| pending_ident||| perl_alloc_using|||n perl_alloc|||n perl_clone_using|||n perl_clone|||n perl_construct|||n perl_destruct||5.007003|n perl_free|||n perl_parse||5.006000|n perl_run|||n pidgone||| pm_description||| pmop_dump||5.006000| pmop_xmldump||| pmruntime||| pmtrans||| pop_scope||| populate_isa|||v pregcomp||5.009005| pregexec||| pregfree2||5.011000| pregfree||| prepend_madprops||| prescan_version||5.011004| printbuf||| printf_nocontext|||vn process_special_blocks||| ptr_hash|||n ptr_table_clear||5.009005| ptr_table_fetch||5.009005| ptr_table_find|||n ptr_table_free||5.009005| ptr_table_new||5.009005| ptr_table_split||5.009005| ptr_table_store||5.009005| push_scope||| put_byte||| put_latin1_charclass_innards||| pv_display|5.006000||p pv_escape|5.009004||p pv_pretty|5.009004||p pv_uni_display||5.007003| qerror||| qsortsvu||| re_compile||5.009005| re_croak2||| re_dup_guts||| re_intuit_start||5.019001| re_intuit_string||5.006000| re_op_compile||| readpipe_override||| realloc||5.007002|n reentrant_free||5.019003| reentrant_init||5.019003| reentrant_retry||5.019003|vn reentrant_size||5.019003| ref_array_or_hash||| refcounted_he_chain_2hv||| refcounted_he_fetch_pvn||| refcounted_he_fetch_pvs||| refcounted_he_fetch_pv||| refcounted_he_fetch_sv||| refcounted_he_free||| refcounted_he_inc||| refcounted_he_new_pvn||| refcounted_he_new_pvs||| refcounted_he_new_pv||| refcounted_he_new_sv||| refcounted_he_value||| refkids||| refto||| ref||5.019003| reg_check_named_buff_matched||| reg_named_buff_all||5.009005| reg_named_buff_exists||5.009005| reg_named_buff_fetch||5.009005| reg_named_buff_firstkey||5.009005| reg_named_buff_iter||| reg_named_buff_nextkey||5.009005| reg_named_buff_scalar||5.009005| reg_named_buff||| reg_node||| reg_numbered_buff_fetch||| reg_numbered_buff_length||| reg_numbered_buff_store||| reg_qr_package||| reg_recode||| reg_scan_name||| reg_skipcomment||| reg_temp_copy||| reganode||| regatom||| regbranch||| regclass_swash||5.009004| regclass||| regcppop||| regcppush||| regcurly||| regdump_extflags||| regdump_intflags||| regdump||5.005000| regdupe_internal||| regexec_flags||5.005000| regfree_internal||5.009005| reghop3|||n reghop4|||n reghopmaybe3|||n reginclass||| reginitcolors||5.006000| reginsert||| regmatch||| regnext||5.005000| regpatws|||n regpiece||| regpposixcc||| regprop||| regrepeat||| regtail_study||| regtail||| regtry||| reguni||| regwhite|||n reg||| repeatcpy|||n report_evil_fh||| report_redefined_cv||| report_uninit||| report_wrongway_fh||| require_pv||5.006000| require_tie_mod||| restore_magic||| rninstr|||n rpeep||| rsignal_restore||| rsignal_save||| rsignal_state||5.004000| rsignal||5.004000| run_body||| run_user_filter||| runops_debug||5.005000| runops_standard||5.005000| rv2cv_op_cv||5.013006| rvpv_dup||| rxres_free||| rxres_restore||| rxres_save||| safesyscalloc||5.006000|n safesysfree||5.006000|n safesysmalloc||5.006000|n safesysrealloc||5.006000|n same_dirent||| save_I16||5.004000| save_I32||| save_I8||5.006000| save_adelete||5.011000| save_aelem_flags||5.011000| save_aelem||5.004050| save_alloc||5.006000| save_aptr||| save_ary||| save_bool||5.008001| save_clearsv||| save_delete||| save_destructor_x||5.006000| save_destructor||5.006000| save_freeop||| save_freepv||| save_freesv||| save_generic_pvref||5.006001| save_generic_svref||5.005030| save_gp||5.004000| save_hash||| save_hdelete||5.011000| save_hek_flags|||n save_helem_flags||5.011000| save_helem||5.004050| save_hints||5.010001| save_hptr||| save_int||| save_item||| save_iv||5.005000| save_lines||| save_list||| save_long||| save_magic_flags||| save_mortalizesv||5.007001| save_nogv||| save_op||5.005000| save_padsv_and_mortalize||5.010001| save_pptr||| save_pushi32ptr||5.010001| save_pushptri32ptr||| save_pushptrptr||5.010001| save_pushptr||5.010001| save_re_context||5.006000| save_scalar_at||| save_scalar||| save_set_svflags||5.009000| save_shared_pvref||5.007003| save_sptr||| save_svref||| save_vptr||5.006000| savepvn||| savepvs||5.009003| savepv||| savesharedpvn||5.009005| savesharedpvs||5.013006| savesharedpv||5.007003| savesharedsvpv||5.013006| savestack_grow_cnt||5.008001| savestack_grow||| savesvpv||5.009002| sawparens||| scalar_mod_type|||n scalarboolean||| scalarkids||| scalarseq||| scalarvoid||| scalar||| scan_bin||5.006000| scan_commit||| scan_const||| scan_formline||| scan_heredoc||| scan_hex||| scan_ident||| scan_inputsymbol||| scan_num||5.007001| scan_oct||| scan_pat||| scan_str||| scan_subst||| scan_trans||| scan_version||5.009001| scan_vstring||5.009005| scan_word||| screaminstr||5.005000| search_const||| seed||5.008001| sequence_num||| set_context||5.006000|n set_numeric_local||5.006000| set_numeric_radix||5.006000| set_numeric_standard||5.006000| setdefout||| share_hek_flags||| share_hek||5.004000| si_dup||| sighandler|||n simplify_sort||| skipspace0||| skipspace1||| skipspace2||| skipspace_flags||| softref2xv||| sortcv_stacked||| sortcv_xsub||| sortcv||| sortsv_flags||5.009003| sortsv||5.007003| space_join_names_mortal||| ss_dup||| stack_grow||| start_force||| start_glob||| start_subparse||5.004000| stdize_locale||| strEQ||| strGE||| strGT||| strLE||| strLT||| strNE||| str_to_version||5.006000| strip_return||| strnEQ||| strnNE||| study_chunk||| sub_crush_depth||| sublex_done||| sublex_push||| sublex_start||| sv_2bool_flags||5.013006| sv_2bool||| sv_2cv||| sv_2io||| sv_2iuv_common||| sv_2iuv_non_preserve||| sv_2iv_flags||5.009001| sv_2iv||| sv_2mortal||| sv_2num||| sv_2nv_flags||5.013001| sv_2pv_flags|5.007002||p sv_2pv_nolen|5.006000||p sv_2pvbyte_nolen|5.006000||p sv_2pvbyte|5.006000||p sv_2pvutf8_nolen||5.006000| sv_2pvutf8||5.006000| sv_2pv||| sv_2uv_flags||5.009001| sv_2uv|5.004000||p sv_add_arena||| sv_add_backref||| sv_backoff||| sv_bless||| sv_cat_decode||5.008001| sv_catpv_flags||5.013006| sv_catpv_mg|5.004050||p sv_catpv_nomg||5.013006| sv_catpvf_mg_nocontext|||pvn sv_catpvf_mg|5.006000|5.004000|pv sv_catpvf_nocontext|||vn sv_catpvf||5.004000|v sv_catpvn_flags||5.007002| sv_catpvn_mg|5.004050||p sv_catpvn_nomg|5.007002||p sv_catpvn||| sv_catpvs_flags||5.013006| sv_catpvs_mg||5.013006| sv_catpvs_nomg||5.013006| sv_catpvs|5.009003||p sv_catpv||| sv_catsv_flags||5.007002| sv_catsv_mg|5.004050||p sv_catsv_nomg|5.007002||p sv_catsv||| sv_catxmlpvn||| sv_catxmlpv||| sv_catxmlsv||| sv_chop||| sv_clean_all||| sv_clean_objs||| sv_clear||| sv_cmp_flags||5.013006| sv_cmp_locale_flags||5.013006| sv_cmp_locale||5.004000| sv_cmp||| sv_collxfrm_flags||5.013006| sv_collxfrm||| sv_copypv_flags||5.017002| sv_copypv_nomg||5.017002| sv_copypv||| sv_dec_nomg||5.013002| sv_dec||| sv_del_backref||| sv_derived_from_pvn||5.015004| sv_derived_from_pv||5.015004| sv_derived_from_sv||5.015004| sv_derived_from||5.004000| sv_destroyable||5.010000| sv_display||| sv_does_pvn||5.015004| sv_does_pv||5.015004| sv_does_sv||5.015004| sv_does||5.009004| sv_dump||| sv_dup_common||| sv_dup_inc_multiple||| sv_dup_inc||| sv_dup||| sv_eq_flags||5.013006| sv_eq||| sv_exp_grow||| sv_force_normal_flags||5.007001| sv_force_normal||5.006000| sv_free2||| sv_free_arenas||| sv_free||| sv_gets||5.004000| sv_grow||| sv_i_ncmp||| sv_inc_nomg||5.013002| sv_inc||| sv_insert_flags||5.010001| sv_insert||| sv_isa||| sv_isobject||| sv_iv||5.005000| sv_kill_backrefs||| sv_len_utf8_nomg||| sv_len_utf8||5.006000| sv_len||| sv_magic_portable|5.019003|5.004000|p sv_magicext_mglob||| sv_magicext||5.007003| sv_magic||| sv_mortalcopy_flags||| sv_mortalcopy||| sv_ncmp||| sv_newmortal||| sv_newref||| sv_nolocking||5.007003| sv_nosharing||5.007003| sv_nounlocking||| sv_nv||5.005000| sv_peek||5.005000| sv_pos_b2u_flags||5.019003| sv_pos_b2u_midway||| sv_pos_b2u||5.006000| sv_pos_u2b_cached||| sv_pos_u2b_flags||5.011005| sv_pos_u2b_forwards|||n sv_pos_u2b_midway|||n sv_pos_u2b||5.006000| sv_pvbyten_force||5.006000| sv_pvbyten||5.006000| sv_pvbyte||5.006000| sv_pvn_force_flags|5.007002||p sv_pvn_force||| sv_pvn_nomg|5.007003|5.005000|p sv_pvn||5.005000| sv_pvutf8n_force||5.006000| sv_pvutf8n||5.006000| sv_pvutf8||5.006000| sv_pv||5.006000| sv_recode_to_utf8||5.007003| sv_reftype||| sv_ref||| sv_release_COW||| sv_replace||| sv_report_used||| sv_resetpvn||| sv_reset||| sv_rvweaken||5.006000| sv_sethek||| sv_setiv_mg|5.004050||p sv_setiv||| sv_setnv_mg|5.006000||p sv_setnv||| sv_setpv_mg|5.004050||p sv_setpvf_mg_nocontext|||pvn sv_setpvf_mg|5.006000|5.004000|pv sv_setpvf_nocontext|||vn sv_setpvf||5.004000|v sv_setpviv_mg||5.008001| sv_setpviv||5.008001| sv_setpvn_mg|5.004050||p sv_setpvn||| sv_setpvs_mg||5.013006| sv_setpvs|5.009004||p sv_setpv||| sv_setref_iv||| sv_setref_nv||| sv_setref_pvn||| sv_setref_pvs||5.019003| sv_setref_pv||| sv_setref_uv||5.007001| sv_setsv_cow||| sv_setsv_flags||5.007002| sv_setsv_mg|5.004050||p sv_setsv_nomg|5.007002||p sv_setsv||| sv_setuv_mg|5.004050||p sv_setuv|5.004000||p sv_tainted||5.004000| sv_taint||5.004000| sv_true||5.005000| sv_unglob||| sv_uni_display||5.007003| sv_unmagicext||5.013008| sv_unmagic||| sv_unref_flags||5.007001| sv_unref||| sv_untaint||5.004000| sv_upgrade||| sv_usepvn_flags||5.009004| sv_usepvn_mg|5.004050||p sv_usepvn||| sv_utf8_decode||5.006000| sv_utf8_downgrade||5.006000| sv_utf8_encode||5.006000| sv_utf8_upgrade_flags_grow||5.011000| sv_utf8_upgrade_flags||5.007002| sv_utf8_upgrade_nomg||5.007002| sv_utf8_upgrade||5.007001| sv_uv|5.005000||p sv_vcatpvf_mg|5.006000|5.004000|p sv_vcatpvfn_flags||5.017002| sv_vcatpvfn||5.004000| sv_vcatpvf|5.006000|5.004000|p sv_vsetpvf_mg|5.006000|5.004000|p sv_vsetpvfn||5.004000| sv_vsetpvf|5.006000|5.004000|p sv_xmlpeek||| svtype||| swallow_bom||| swash_fetch||5.007002| swash_init||5.006000| swatch_get||| sys_init3||5.010000|n sys_init||5.010000|n sys_intern_clear||| sys_intern_dup||| sys_intern_init||| sys_term||5.010000|n taint_env||| taint_proper||| tied_method|||v tmps_grow||5.006000| toFOLD_uni||5.007003| toFOLD_utf8||5.019001| toFOLD||5.019001| toLOWER_L1||5.019001| toLOWER_LC||5.004000| toLOWER_uni||5.007003| toLOWER_utf8||5.015007| toLOWER||| toTITLE_uni||5.007003| toTITLE_utf8||5.015007| toTITLE||5.019001| toUPPER_uni||5.007003| toUPPER_utf8||5.015007| toUPPER||5.004000| to_byte_substr||| to_lower_latin1||| to_uni_fold||5.007003| to_uni_lower_lc||5.006000| to_uni_lower||5.007003| to_uni_title_lc||5.006000| to_uni_title||5.007003| to_uni_upper_lc||5.006000| to_uni_upper||5.007003| to_utf8_case||5.007003| to_utf8_fold||5.015007| to_utf8_lower||5.015007| to_utf8_substr||| to_utf8_title||5.015007| to_utf8_upper||5.015007| token_free||| token_getmad||| tokenize_use||| tokeq||| tokereport||| too_few_arguments_pv||| too_few_arguments_sv||| too_many_arguments_pv||| too_many_arguments_sv||| translate_substr_offsets||| try_amagic_bin||| try_amagic_un||| uiv_2buf|||n unlnk||| unpack_rec||| unpack_str||5.007003| unpackstring||5.008001| unreferenced_to_tmp_stack||| unshare_hek_or_pvn||| unshare_hek||| unsharepvn||5.004000| unwind_handler_stack||| update_debugger_info||| upg_version||5.009005| usage||| utf16_textfilter||| utf16_to_utf8_reversed||5.006001| utf16_to_utf8||5.006001| utf8_distance||5.006000| utf8_hop||5.006000| utf8_length||5.007001| utf8_mg_len_cache_update||| utf8_mg_pos_cache_update||| utf8_to_bytes||5.006001| utf8_to_uvchr_buf||5.015009| utf8_to_uvchr||5.007001| utf8_to_uvuni_buf||5.015009| utf8_to_uvuni||5.007001| utf8n_to_uvchr||| utf8n_to_uvuni||5.007001| utilize||| uvchr_to_utf8_flags||5.007003| uvchr_to_utf8||| uvuni_to_utf8_flags||5.007003| uvuni_to_utf8||5.007001| valid_utf8_to_uvchr||| valid_utf8_to_uvuni||5.015009| validate_proto||| validate_suid||| varname||| vcmp||5.009000| vcroak||5.006000| vdeb||5.007003| vform||5.006000| visit||| vivify_defelem||| vivify_ref||| vload_module|5.006000||p vmess||5.006000| vnewSVpvf|5.006000|5.004000|p vnormal||5.009002| vnumify||5.009000| vstringify||5.009000| vverify||5.009003| vwarner||5.006000| vwarn||5.006000| wait4pid||| warn_nocontext|||vn warn_sv||5.013001| warner_nocontext|||vn warner|5.006000|5.004000|pv warn|||v was_lvalue_sub||| watch||| whichsig_pvn||5.015004| whichsig_pv||5.015004| whichsig_sv||5.015004| whichsig||| win32_croak_not_implemented|||n with_queued_errors||| wrap_op_checker||5.015008| write_to_stderr||| xmldump_all_perl||| xmldump_all||| xmldump_attr||| xmldump_eval||| xmldump_form||| xmldump_indent|||v xmldump_packsubs_perl||| xmldump_packsubs||| xmldump_sub_perl||| xmldump_sub||| xmldump_vindent||| xs_apiversion_bootcheck||| xs_version_bootcheck||| yyerror_pvn||| yyerror_pv||| yyerror||| yylex||| yyparse||| yyunlex||| yywarn||| ); if (exists $opt{'list-unsupported'}) { my $f; for $f (sort { lc $a cmp lc $b } keys %API) { next unless $API{$f}{todo}; print "$f ", '.'x(40-length($f)), " ", format_version($API{$f}{todo}), "\n"; } exit 0; } # Scan for possible replacement candidates my(%replace, %need, %hints, %warnings, %depends); my $replace = 0; my($hint, $define, $function); sub find_api { my $code = shift; $code =~ s{ / (?: \*[^*]*\*+(?:[^$ccs][^*]*\*+)* / | /[^\r\n]*) | "[^"\\]*(?:\\.[^"\\]*)*" | '[^'\\]*(?:\\.[^'\\]*)*' }{}egsx; grep { exists $API{$_} } $code =~ /(\w+)/mg; } while () { if ($hint) { my $h = $hint->[0] eq 'Hint' ? \%hints : \%warnings; if (m{^\s*\*\s(.*?)\s*$}) { for (@{$hint->[1]}) { $h->{$_} ||= ''; # suppress warning with older perls $h->{$_} .= "$1\n"; } } else { undef $hint } } $hint = [$1, [split /,?\s+/, $2]] if m{^\s*$rccs\s+(Hint|Warning):\s+(\w+(?:,?\s+\w+)*)\s*$}; if ($define) { if ($define->[1] =~ /\\$/) { $define->[1] .= $_; } else { if (exists $API{$define->[0]} && $define->[1] !~ /^DPPP_\(/) { my @n = find_api($define->[1]); push @{$depends{$define->[0]}}, @n if @n } undef $define; } } $define = [$1, $2] if m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(.*)}; if ($function) { if (/^}/) { if (exists $API{$function->[0]}) { my @n = find_api($function->[1]); push @{$depends{$function->[0]}}, @n if @n } undef $function; } else { $function->[1] .= $_; } } $function = [$1, ''] if m{^DPPP_\(my_(\w+)\)}; $replace = $1 if m{^\s*$rccs\s+Replace:\s+(\d+)\s+$rcce\s*$}; $replace{$2} = $1 if $replace and m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(\w+)}; $replace{$2} = $1 if m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(\w+).*$rccs\s+Replace\s+$rcce}; $replace{$1} = $2 if m{^\s*$rccs\s+Replace (\w+) with (\w+)\s+$rcce\s*$}; if (m{^\s*$rccs\s+(\w+(\s*,\s*\w+)*)\s+depends\s+on\s+(\w+(\s*,\s*\w+)*)\s+$rcce\s*$}) { my @deps = map { s/\s+//g; $_ } split /,/, $3; my $d; for $d (map { s/\s+//g; $_ } split /,/, $1) { push @{$depends{$d}}, @deps; } } $need{$1} = 1 if m{^#if\s+defined\(NEED_(\w+)(?:_GLOBAL)?\)}; } for (values %depends) { my %s; $_ = [sort grep !$s{$_}++, @$_]; } if (exists $opt{'api-info'}) { my $f; my $count = 0; my $match = $opt{'api-info'} =~ m!^/(.*)/$! ? $1 : "^\Q$opt{'api-info'}\E\$"; for $f (sort { lc $a cmp lc $b } keys %API) { next unless $f =~ /$match/; print "\n=== $f ===\n\n"; my $info = 0; if ($API{$f}{base} || $API{$f}{todo}) { my $base = format_version($API{$f}{base} || $API{$f}{todo}); print "Supported at least starting from perl-$base.\n"; $info++; } if ($API{$f}{provided}) { my $todo = $API{$f}{todo} ? format_version($API{$f}{todo}) : "5.003"; print "Support by $ppport provided back to perl-$todo.\n"; print "Support needs to be explicitly requested by NEED_$f.\n" if exists $need{$f}; print "Depends on: ", join(', ', @{$depends{$f}}), ".\n" if exists $depends{$f}; print "\n$hints{$f}" if exists $hints{$f}; print "\nWARNING:\n$warnings{$f}" if exists $warnings{$f}; $info++; } print "No portability information available.\n" unless $info; $count++; } $count or print "Found no API matching '$opt{'api-info'}'."; print "\n"; exit 0; } if (exists $opt{'list-provided'}) { my $f; for $f (sort { lc $a cmp lc $b } keys %API) { next unless $API{$f}{provided}; my @flags; push @flags, 'explicit' if exists $need{$f}; push @flags, 'depend' if exists $depends{$f}; push @flags, 'hint' if exists $hints{$f}; push @flags, 'warning' if exists $warnings{$f}; my $flags = @flags ? ' ['.join(', ', @flags).']' : ''; print "$f$flags\n"; } exit 0; } my @files; my @srcext = qw( .xs .c .h .cc .cpp -c.inc -xs.inc ); my $srcext = join '|', map { quotemeta $_ } @srcext; if (@ARGV) { my %seen; for (@ARGV) { if (-e) { if (-f) { push @files, $_ unless $seen{$_}++; } else { warn "'$_' is not a file.\n" } } else { my @new = grep { -f } glob $_ or warn "'$_' does not exist.\n"; push @files, grep { !$seen{$_}++ } @new; } } } else { eval { require File::Find; File::Find::find(sub { $File::Find::name =~ /($srcext)$/i and push @files, $File::Find::name; }, '.'); }; if ($@) { @files = map { glob "*$_" } @srcext; } } if (!@ARGV || $opt{filter}) { my(@in, @out); my %xsc = map { /(.*)\.xs$/ ? ("$1.c" => 1, "$1.cc" => 1) : () } @files; for (@files) { my $out = exists $xsc{$_} || /\b\Q$ppport\E$/i || !/($srcext)$/i; push @{ $out ? \@out : \@in }, $_; } if (@ARGV && @out) { warning("Skipping the following files (use --nofilter to avoid this):\n| ", join "\n| ", @out); } @files = @in; } die "No input files given!\n" unless @files; my(%files, %global, %revreplace); %revreplace = reverse %replace; my $filename; my $patch_opened = 0; for $filename (@files) { unless (open IN, "<$filename") { warn "Unable to read from $filename: $!\n"; next; } info("Scanning $filename ..."); my $c = do { local $/; }; close IN; my %file = (orig => $c, changes => 0); # Temporarily remove C/XS comments and strings from the code my @ccom; $c =~ s{ ( ^$HS*\#$HS*include\b[^\r\n]+\b(?:\Q$ppport\E|XSUB\.h)\b[^\r\n]* | ^$HS*\#$HS*(?:define|elif|if(?:def)?)\b[^\r\n]* ) | ( ^$HS*\#[^\r\n]* | "[^"\\]*(?:\\.[^"\\]*)*" | '[^'\\]*(?:\\.[^'\\]*)*' | / (?: \*[^*]*\*+(?:[^$ccs][^*]*\*+)* / | /[^\r\n]* ) ) }{ defined $2 and push @ccom, $2; defined $1 ? $1 : "$ccs$#ccom$cce" }mgsex; $file{ccom} = \@ccom; $file{code} = $c; $file{has_inc_ppport} = $c =~ /^$HS*#$HS*include[^\r\n]+\b\Q$ppport\E\b/m; my $func; for $func (keys %API) { my $match = $func; $match .= "|$revreplace{$func}" if exists $revreplace{$func}; if ($c =~ /\b(?:Perl_)?($match)\b/) { $file{uses_replace}{$1}++ if exists $revreplace{$func} && $1 eq $revreplace{$func}; $file{uses_Perl}{$func}++ if $c =~ /\bPerl_$func\b/; if (exists $API{$func}{provided}) { $file{uses_provided}{$func}++; if (!exists $API{$func}{base} || $API{$func}{base} > $opt{'compat-version'}) { $file{uses}{$func}++; my @deps = rec_depend($func); if (@deps) { $file{uses_deps}{$func} = \@deps; for (@deps) { $file{uses}{$_} = 0 unless exists $file{uses}{$_}; } } for ($func, @deps) { $file{needs}{$_} = 'static' if exists $need{$_}; } } } if (exists $API{$func}{todo} && $API{$func}{todo} > $opt{'compat-version'}) { if ($c =~ /\b$func\b/) { $file{uses_todo}{$func}++; } } } } while ($c =~ /^$HS*#$HS*define$HS+(NEED_(\w+?)(_GLOBAL)?)\b/mg) { if (exists $need{$2}) { $file{defined $3 ? 'needed_global' : 'needed_static'}{$2}++; } else { warning("Possibly wrong #define $1 in $filename") } } for (qw(uses needs uses_todo needed_global needed_static)) { for $func (keys %{$file{$_}}) { push @{$global{$_}{$func}}, $filename; } } $files{$filename} = \%file; } # Globally resolve NEED_'s my $need; for $need (keys %{$global{needs}}) { if (@{$global{needs}{$need}} > 1) { my @targets = @{$global{needs}{$need}}; my @t = grep $files{$_}{needed_global}{$need}, @targets; @targets = @t if @t; @t = grep /\.xs$/i, @targets; @targets = @t if @t; my $target = shift @targets; $files{$target}{needs}{$need} = 'global'; for (@{$global{needs}{$need}}) { $files{$_}{needs}{$need} = 'extern' if $_ ne $target; } } } for $filename (@files) { exists $files{$filename} or next; info("=== Analyzing $filename ==="); my %file = %{$files{$filename}}; my $func; my $c = $file{code}; my $warnings = 0; for $func (sort keys %{$file{uses_Perl}}) { if ($API{$func}{varargs}) { unless ($API{$func}{nothxarg}) { my $changes = ($c =~ s{\b(Perl_$func\s*\(\s*)(?!aTHX_?)(\)|[^\s)]*\))} { $1 . ($2 eq ')' ? 'aTHX' : 'aTHX_ ') . $2 }ge); if ($changes) { warning("Doesn't pass interpreter argument aTHX to Perl_$func"); $file{changes} += $changes; } } } else { warning("Uses Perl_$func instead of $func"); $file{changes} += ($c =~ s{\bPerl_$func(\s*)\((\s*aTHX_?)?\s*} {$func$1(}g); } } for $func (sort keys %{$file{uses_replace}}) { warning("Uses $func instead of $replace{$func}"); $file{changes} += ($c =~ s/\b$func\b/$replace{$func}/g); } for $func (sort keys %{$file{uses_provided}}) { if ($file{uses}{$func}) { if (exists $file{uses_deps}{$func}) { diag("Uses $func, which depends on ", join(', ', @{$file{uses_deps}{$func}})); } else { diag("Uses $func"); } } $warnings += hint($func); } unless ($opt{quiet}) { for $func (sort keys %{$file{uses_todo}}) { print "*** WARNING: Uses $func, which may not be portable below perl ", format_version($API{$func}{todo}), ", even with '$ppport'\n"; $warnings++; } } for $func (sort keys %{$file{needed_static}}) { my $message = ''; if (not exists $file{uses}{$func}) { $message = "No need to define NEED_$func if $func is never used"; } elsif (exists $file{needs}{$func} && $file{needs}{$func} ne 'static') { $message = "No need to define NEED_$func when already needed globally"; } if ($message) { diag($message); $file{changes} += ($c =~ s/^$HS*#$HS*define$HS+NEED_$func\b.*$LF//mg); } } for $func (sort keys %{$file{needed_global}}) { my $message = ''; if (not exists $global{uses}{$func}) { $message = "No need to define NEED_${func}_GLOBAL if $func is never used"; } elsif (exists $file{needs}{$func}) { if ($file{needs}{$func} eq 'extern') { $message = "No need to define NEED_${func}_GLOBAL when already needed globally"; } elsif ($file{needs}{$func} eq 'static') { $message = "No need to define NEED_${func}_GLOBAL when only used in this file"; } } if ($message) { diag($message); $file{changes} += ($c =~ s/^$HS*#$HS*define$HS+NEED_${func}_GLOBAL\b.*$LF//mg); } } $file{needs_inc_ppport} = keys %{$file{uses}}; if ($file{needs_inc_ppport}) { my $pp = ''; for $func (sort keys %{$file{needs}}) { my $type = $file{needs}{$func}; next if $type eq 'extern'; my $suffix = $type eq 'global' ? '_GLOBAL' : ''; unless (exists $file{"needed_$type"}{$func}) { if ($type eq 'global') { diag("Files [@{$global{needs}{$func}}] need $func, adding global request"); } else { diag("File needs $func, adding static request"); } $pp .= "#define NEED_$func$suffix\n"; } } if ($pp && ($c =~ s/^(?=$HS*#$HS*define$HS+NEED_\w+)/$pp/m)) { $pp = ''; $file{changes}++; } unless ($file{has_inc_ppport}) { diag("Needs to include '$ppport'"); $pp .= qq(#include "$ppport"\n) } if ($pp) { $file{changes} += ($c =~ s/^($HS*#$HS*define$HS+NEED_\w+.*?)^/$1$pp/ms) || ($c =~ s/^(?=$HS*#$HS*include.*\Q$ppport\E)/$pp/m) || ($c =~ s/^($HS*#$HS*include.*XSUB.*\s*?)^/$1$pp/m) || ($c =~ s/^/$pp/); } } else { if ($file{has_inc_ppport}) { diag("No need to include '$ppport'"); $file{changes} += ($c =~ s/^$HS*?#$HS*include.*\Q$ppport\E.*?$LF//m); } } # put back in our C comments my $ix; my $cppc = 0; my @ccom = @{$file{ccom}}; for $ix (0 .. $#ccom) { if (!$opt{cplusplus} && $ccom[$ix] =~ s!^//!!) { $cppc++; $file{changes} += $c =~ s/$rccs$ix$rcce/$ccs$ccom[$ix] $cce/; } else { $c =~ s/$rccs$ix$rcce/$ccom[$ix]/; } } if ($cppc) { my $s = $cppc != 1 ? 's' : ''; warning("Uses $cppc C++ style comment$s, which is not portable"); } my $s = $warnings != 1 ? 's' : ''; my $warn = $warnings ? " ($warnings warning$s)" : ''; info("Analysis completed$warn"); if ($file{changes}) { if (exists $opt{copy}) { my $newfile = "$filename$opt{copy}"; if (-e $newfile) { error("'$newfile' already exists, refusing to write copy of '$filename'"); } else { local *F; if (open F, ">$newfile") { info("Writing copy of '$filename' with changes to '$newfile'"); print F $c; close F; } else { error("Cannot open '$newfile' for writing: $!"); } } } elsif (exists $opt{patch} || $opt{changes}) { if (exists $opt{patch}) { unless ($patch_opened) { if (open PATCH, ">$opt{patch}") { $patch_opened = 1; } else { error("Cannot open '$opt{patch}' for writing: $!"); delete $opt{patch}; $opt{changes} = 1; goto fallback; } } mydiff(\*PATCH, $filename, $c); } else { fallback: info("Suggested changes:"); mydiff(\*STDOUT, $filename, $c); } } else { my $s = $file{changes} == 1 ? '' : 's'; info("$file{changes} potentially required change$s detected"); } } else { info("Looks good"); } } close PATCH if $patch_opened; exit 0; sub try_use { eval "use @_;"; return $@ eq '' } sub mydiff { local *F = shift; my($file, $str) = @_; my $diff; if (exists $opt{diff}) { $diff = run_diff($opt{diff}, $file, $str); } if (!defined $diff and try_use('Text::Diff')) { $diff = Text::Diff::diff($file, \$str, { STYLE => 'Unified' }); $diff = <
$tmp") { print F $str; close F; if (open F, "$prog $file $tmp |") { while () { s/\Q$tmp\E/$file.patched/; $diff .= $_; } close F; unlink $tmp; return $diff; } unlink $tmp; } else { error("Cannot open '$tmp' for writing: $!"); } return undef; } sub rec_depend { my($func, $seen) = @_; return () unless exists $depends{$func}; $seen = {%{$seen||{}}}; return () if $seen->{$func}++; my %s; grep !$s{$_}++, map { ($_, rec_depend($_, $seen)) } @{$depends{$func}}; } sub parse_version { my $ver = shift; if ($ver =~ /^(\d+)\.(\d+)\.(\d+)$/) { return ($1, $2, $3); } elsif ($ver !~ /^\d+\.[\d_]+$/) { die "cannot parse version '$ver'\n"; } $ver =~ s/_//g; $ver =~ s/$/000000/; my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/; $v = int $v; $s = int $s; if ($r < 5 || ($r == 5 && $v < 6)) { if ($s % 10) { die "cannot parse version '$ver'\n"; } } return ($r, $v, $s); } sub format_version { my $ver = shift; $ver =~ s/$/000000/; my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/; $v = int $v; $s = int $s; if ($r < 5 || ($r == 5 && $v < 6)) { if ($s % 10) { die "invalid version '$ver'\n"; } $s /= 10; $ver = sprintf "%d.%03d", $r, $v; $s > 0 and $ver .= sprintf "_%02d", $s; return $ver; } return sprintf "%d.%d.%d", $r, $v, $s; } sub info { $opt{quiet} and return; print @_, "\n"; } sub diag { $opt{quiet} and return; $opt{diag} and print @_, "\n"; } sub warning { $opt{quiet} and return; print "*** ", @_, "\n"; } sub error { print "*** ERROR: ", @_, "\n"; } my %given_hints; my %given_warnings; sub hint { $opt{quiet} and return; my $func = shift; my $rv = 0; if (exists $warnings{$func} && !$given_warnings{$func}++) { my $warn = $warnings{$func}; $warn =~ s!^!*** !mg; print "*** WARNING: $func\n", $warn; $rv++; } if ($opt{hints} && exists $hints{$func} && !$given_hints{$func}++) { my $hint = $hints{$func}; $hint =~ s/^/ /mg; print " --- hint for $func ---\n", $hint; } $rv; } sub usage { my($usage) = do { local(@ARGV,$/)=($0); <> } =~ /^=head\d$HS+SYNOPSIS\s*^(.*?)\s*^=/ms; my %M = ( 'I' => '*' ); $usage =~ s/^\s*perl\s+\S+/$^X $0/; $usage =~ s/([A-Z])<([^>]+)>/$M{$1}$2$M{$1}/g; print < }; my($copy) = $self =~ /^=head\d\s+COPYRIGHT\s*^(.*?)^=\w+/ms; $copy =~ s/^(?=\S+)/ /gms; $self =~ s/^$HS+Do NOT edit.*?(?=^-)/$copy/ms; $self =~ s/^SKIP.*(?=^__DATA__)/SKIP if (\@ARGV && \$ARGV[0] eq '--unstrip') { eval { require Devel::PPPort }; \$@ and die "Cannot require Devel::PPPort, please install.\\n"; if (eval \$Devel::PPPort::VERSION < $VERSION) { die "$0 was originally generated with Devel::PPPort $VERSION.\\n" . "Your Devel::PPPort is only version \$Devel::PPPort::VERSION.\\n" . "Please install a newer version, or --unstrip will not work.\\n"; } Devel::PPPort::WriteFile(\$0); exit 0; } print <$0" or die "cannot strip $0: $!\n"; print OUT "$pl$c\n"; exit 0; } __DATA__ */ #ifndef _P_P_PORTABILITY_H_ #define _P_P_PORTABILITY_H_ #ifndef DPPP_NAMESPACE # define DPPP_NAMESPACE DPPP_ #endif #define DPPP_CAT2(x,y) CAT2(x,y) #define DPPP_(name) DPPP_CAT2(DPPP_NAMESPACE, name) #ifndef PERL_REVISION # if !defined(__PATCHLEVEL_H_INCLUDED__) && !(defined(PATCHLEVEL) && defined(SUBVERSION)) # define PERL_PATCHLEVEL_H_IMPLICIT # include # endif # if !(defined(PERL_VERSION) || (defined(SUBVERSION) && defined(PATCHLEVEL))) # include # endif # ifndef PERL_REVISION # define PERL_REVISION (5) /* Replace: 1 */ # define PERL_VERSION PATCHLEVEL # define PERL_SUBVERSION SUBVERSION /* Replace PERL_PATCHLEVEL with PERL_VERSION */ /* Replace: 0 */ # endif #endif #define _dpppDEC2BCD(dec) ((((dec)/100)<<8)|((((dec)%100)/10)<<4)|((dec)%10)) #define PERL_BCDVERSION ((_dpppDEC2BCD(PERL_REVISION)<<24)|(_dpppDEC2BCD(PERL_VERSION)<<12)|_dpppDEC2BCD(PERL_SUBVERSION)) /* It is very unlikely that anyone will try to use this with Perl 6 (or greater), but who knows. */ #if PERL_REVISION != 5 # error ppport.h only works with Perl version 5 #endif /* PERL_REVISION != 5 */ #ifndef dTHR # define dTHR dNOOP #endif #ifndef dTHX # define dTHX dNOOP #endif #ifndef dTHXa # define dTHXa(x) dNOOP #endif #ifndef pTHX # define pTHX void #endif #ifndef pTHX_ # define pTHX_ #endif #ifndef aTHX # define aTHX #endif #ifndef aTHX_ # define aTHX_ #endif #if (PERL_BCDVERSION < 0x5006000) # ifdef USE_THREADS # define aTHXR thr # define aTHXR_ thr, # else # define aTHXR # define aTHXR_ # endif # define dTHXR dTHR #else # define aTHXR aTHX # define aTHXR_ aTHX_ # define dTHXR dTHX #endif #ifndef dTHXoa # define dTHXoa(x) dTHXa(x) #endif #ifdef I_LIMITS # include #endif #ifndef PERL_UCHAR_MIN # define PERL_UCHAR_MIN ((unsigned char)0) #endif #ifndef PERL_UCHAR_MAX # ifdef UCHAR_MAX # define PERL_UCHAR_MAX ((unsigned char)UCHAR_MAX) # else # ifdef MAXUCHAR # define PERL_UCHAR_MAX ((unsigned char)MAXUCHAR) # else # define PERL_UCHAR_MAX ((unsigned char)~(unsigned)0) # endif # endif #endif #ifndef PERL_USHORT_MIN # define PERL_USHORT_MIN ((unsigned short)0) #endif #ifndef PERL_USHORT_MAX # ifdef USHORT_MAX # define PERL_USHORT_MAX ((unsigned short)USHORT_MAX) # else # ifdef MAXUSHORT # define PERL_USHORT_MAX ((unsigned short)MAXUSHORT) # else # ifdef USHRT_MAX # define PERL_USHORT_MAX ((unsigned short)USHRT_MAX) # else # define PERL_USHORT_MAX ((unsigned short)~(unsigned)0) # endif # endif # endif #endif #ifndef PERL_SHORT_MAX # ifdef SHORT_MAX # define PERL_SHORT_MAX ((short)SHORT_MAX) # else # ifdef MAXSHORT /* Often used in */ # define PERL_SHORT_MAX ((short)MAXSHORT) # else # ifdef SHRT_MAX # define PERL_SHORT_MAX ((short)SHRT_MAX) # else # define PERL_SHORT_MAX ((short) (PERL_USHORT_MAX >> 1)) # endif # endif # endif #endif #ifndef PERL_SHORT_MIN # ifdef SHORT_MIN # define PERL_SHORT_MIN ((short)SHORT_MIN) # else # ifdef MINSHORT # define PERL_SHORT_MIN ((short)MINSHORT) # else # ifdef SHRT_MIN # define PERL_SHORT_MIN ((short)SHRT_MIN) # else # define PERL_SHORT_MIN (-PERL_SHORT_MAX - ((3 & -1) == 3)) # endif # endif # endif #endif #ifndef PERL_UINT_MAX # ifdef UINT_MAX # define PERL_UINT_MAX ((unsigned int)UINT_MAX) # else # ifdef MAXUINT # define PERL_UINT_MAX ((unsigned int)MAXUINT) # else # define PERL_UINT_MAX (~(unsigned int)0) # endif # endif #endif #ifndef PERL_UINT_MIN # define PERL_UINT_MIN ((unsigned int)0) #endif #ifndef PERL_INT_MAX # ifdef INT_MAX # define PERL_INT_MAX ((int)INT_MAX) # else # ifdef MAXINT /* Often used in */ # define PERL_INT_MAX ((int)MAXINT) # else # define PERL_INT_MAX ((int)(PERL_UINT_MAX >> 1)) # endif # endif #endif #ifndef PERL_INT_MIN # ifdef INT_MIN # define PERL_INT_MIN ((int)INT_MIN) # else # ifdef MININT # define PERL_INT_MIN ((int)MININT) # else # define PERL_INT_MIN (-PERL_INT_MAX - ((3 & -1) == 3)) # endif # endif #endif #ifndef PERL_ULONG_MAX # ifdef ULONG_MAX # define PERL_ULONG_MAX ((unsigned long)ULONG_MAX) # else # ifdef MAXULONG # define PERL_ULONG_MAX ((unsigned long)MAXULONG) # else # define PERL_ULONG_MAX (~(unsigned long)0) # endif # endif #endif #ifndef PERL_ULONG_MIN # define PERL_ULONG_MIN ((unsigned long)0L) #endif #ifndef PERL_LONG_MAX # ifdef LONG_MAX # define PERL_LONG_MAX ((long)LONG_MAX) # else # ifdef MAXLONG # define PERL_LONG_MAX ((long)MAXLONG) # else # define PERL_LONG_MAX ((long) (PERL_ULONG_MAX >> 1)) # endif # endif #endif #ifndef PERL_LONG_MIN # ifdef LONG_MIN # define PERL_LONG_MIN ((long)LONG_MIN) # else # ifdef MINLONG # define PERL_LONG_MIN ((long)MINLONG) # else # define PERL_LONG_MIN (-PERL_LONG_MAX - ((3 & -1) == 3)) # endif # endif #endif #if defined(HAS_QUAD) && (defined(convex) || defined(uts)) # ifndef PERL_UQUAD_MAX # ifdef ULONGLONG_MAX # define PERL_UQUAD_MAX ((unsigned long long)ULONGLONG_MAX) # else # ifdef MAXULONGLONG # define PERL_UQUAD_MAX ((unsigned long long)MAXULONGLONG) # else # define PERL_UQUAD_MAX (~(unsigned long long)0) # endif # endif # endif # ifndef PERL_UQUAD_MIN # define PERL_UQUAD_MIN ((unsigned long long)0L) # endif # ifndef PERL_QUAD_MAX # ifdef LONGLONG_MAX # define PERL_QUAD_MAX ((long long)LONGLONG_MAX) # else # ifdef MAXLONGLONG # define PERL_QUAD_MAX ((long long)MAXLONGLONG) # else # define PERL_QUAD_MAX ((long long) (PERL_UQUAD_MAX >> 1)) # endif # endif # endif # ifndef PERL_QUAD_MIN # ifdef LONGLONG_MIN # define PERL_QUAD_MIN ((long long)LONGLONG_MIN) # else # ifdef MINLONGLONG # define PERL_QUAD_MIN ((long long)MINLONGLONG) # else # define PERL_QUAD_MIN (-PERL_QUAD_MAX - ((3 & -1) == 3)) # endif # endif # endif #endif /* This is based on code from 5.003 perl.h */ #ifdef HAS_QUAD # ifdef cray #ifndef IVTYPE # define IVTYPE int #endif #ifndef IV_MIN # define IV_MIN PERL_INT_MIN #endif #ifndef IV_MAX # define IV_MAX PERL_INT_MAX #endif #ifndef UV_MIN # define UV_MIN PERL_UINT_MIN #endif #ifndef UV_MAX # define UV_MAX PERL_UINT_MAX #endif # ifdef INTSIZE #ifndef IVSIZE # define IVSIZE INTSIZE #endif # endif # else # if defined(convex) || defined(uts) #ifndef IVTYPE # define IVTYPE long long #endif #ifndef IV_MIN # define IV_MIN PERL_QUAD_MIN #endif #ifndef IV_MAX # define IV_MAX PERL_QUAD_MAX #endif #ifndef UV_MIN # define UV_MIN PERL_UQUAD_MIN #endif #ifndef UV_MAX # define UV_MAX PERL_UQUAD_MAX #endif # ifdef LONGLONGSIZE #ifndef IVSIZE # define IVSIZE LONGLONGSIZE #endif # endif # else #ifndef IVTYPE # define IVTYPE long #endif #ifndef IV_MIN # define IV_MIN PERL_LONG_MIN #endif #ifndef IV_MAX # define IV_MAX PERL_LONG_MAX #endif #ifndef UV_MIN # define UV_MIN PERL_ULONG_MIN #endif #ifndef UV_MAX # define UV_MAX PERL_ULONG_MAX #endif # ifdef LONGSIZE #ifndef IVSIZE # define IVSIZE LONGSIZE #endif # endif # endif # endif #ifndef IVSIZE # define IVSIZE 8 #endif #ifndef LONGSIZE # define LONGSIZE 8 #endif #ifndef PERL_QUAD_MIN # define PERL_QUAD_MIN IV_MIN #endif #ifndef PERL_QUAD_MAX # define PERL_QUAD_MAX IV_MAX #endif #ifndef PERL_UQUAD_MIN # define PERL_UQUAD_MIN UV_MIN #endif #ifndef PERL_UQUAD_MAX # define PERL_UQUAD_MAX UV_MAX #endif #else #ifndef IVTYPE # define IVTYPE long #endif #ifndef LONGSIZE # define LONGSIZE 4 #endif #ifndef IV_MIN # define IV_MIN PERL_LONG_MIN #endif #ifndef IV_MAX # define IV_MAX PERL_LONG_MAX #endif #ifndef UV_MIN # define UV_MIN PERL_ULONG_MIN #endif #ifndef UV_MAX # define UV_MAX PERL_ULONG_MAX #endif #endif #ifndef IVSIZE # ifdef LONGSIZE # define IVSIZE LONGSIZE # else # define IVSIZE 4 /* A bold guess, but the best we can make. */ # endif #endif #ifndef UVTYPE # define UVTYPE unsigned IVTYPE #endif #ifndef UVSIZE # define UVSIZE IVSIZE #endif #ifndef sv_setuv # define sv_setuv(sv, uv) \ STMT_START { \ UV TeMpUv = uv; \ if (TeMpUv <= IV_MAX) \ sv_setiv(sv, TeMpUv); \ else \ sv_setnv(sv, (double)TeMpUv); \ } STMT_END #endif #ifndef newSVuv # define newSVuv(uv) ((uv) <= IV_MAX ? newSViv((IV)uv) : newSVnv((NV)uv)) #endif #ifndef sv_2uv # define sv_2uv(sv) ((PL_Sv = (sv)), (UV) (SvNOK(PL_Sv) ? SvNV(PL_Sv) : sv_2nv(PL_Sv))) #endif #ifndef SvUVX # define SvUVX(sv) ((UV)SvIVX(sv)) #endif #ifndef SvUVXx # define SvUVXx(sv) SvUVX(sv) #endif #ifndef SvUV # define SvUV(sv) (SvIOK(sv) ? SvUVX(sv) : sv_2uv(sv)) #endif #ifndef SvUVx # define SvUVx(sv) ((PL_Sv = (sv)), SvUV(PL_Sv)) #endif /* Hint: sv_uv * Always use the SvUVx() macro instead of sv_uv(). */ #ifndef sv_uv # define sv_uv(sv) SvUVx(sv) #endif #if !defined(SvUOK) && defined(SvIOK_UV) # define SvUOK(sv) SvIOK_UV(sv) #endif #ifndef XST_mUV # define XST_mUV(i,v) (ST(i) = sv_2mortal(newSVuv(v)) ) #endif #ifndef XSRETURN_UV # define XSRETURN_UV(v) STMT_START { XST_mUV(0,v); XSRETURN(1); } STMT_END #endif #ifndef PUSHu # define PUSHu(u) STMT_START { sv_setuv(TARG, (UV)(u)); PUSHTARG; } STMT_END #endif #ifndef XPUSHu # define XPUSHu(u) STMT_START { sv_setuv(TARG, (UV)(u)); XPUSHTARG; } STMT_END #endif #ifdef HAS_MEMCMP #ifndef memNE # define memNE(s1,s2,l) (memcmp(s1,s2,l)) #endif #ifndef memEQ # define memEQ(s1,s2,l) (!memcmp(s1,s2,l)) #endif #else #ifndef memNE # define memNE(s1,s2,l) (bcmp(s1,s2,l)) #endif #ifndef memEQ # define memEQ(s1,s2,l) (!bcmp(s1,s2,l)) #endif #endif #ifndef memEQs # define memEQs(s1, l, s2) \ (sizeof(s2)-1 == l && memEQ(s1, (s2 ""), (sizeof(s2)-1))) #endif #ifndef memNEs # define memNEs(s1, l, s2) !memEQs(s1, l, s2) #endif #ifndef MoveD # define MoveD(s,d,n,t) memmove((char*)(d),(char*)(s), (n) * sizeof(t)) #endif #ifndef CopyD # define CopyD(s,d,n,t) memcpy((char*)(d),(char*)(s), (n) * sizeof(t)) #endif #ifdef HAS_MEMSET #ifndef ZeroD # define ZeroD(d,n,t) memzero((char*)(d), (n) * sizeof(t)) #endif #else #ifndef ZeroD # define ZeroD(d,n,t) ((void)memzero((char*)(d), (n) * sizeof(t)), d) #endif #endif #ifndef PoisonWith # define PoisonWith(d,n,t,b) (void)memset((char*)(d), (U8)(b), (n) * sizeof(t)) #endif #ifndef PoisonNew # define PoisonNew(d,n,t) PoisonWith(d,n,t,0xAB) #endif #ifndef PoisonFree # define PoisonFree(d,n,t) PoisonWith(d,n,t,0xEF) #endif #ifndef Poison # define Poison(d,n,t) PoisonFree(d,n,t) #endif #ifndef Newx # define Newx(v,n,t) New(0,v,n,t) #endif #ifndef Newxc # define Newxc(v,n,t,c) Newc(0,v,n,t,c) #endif #ifndef Newxz # define Newxz(v,n,t) Newz(0,v,n,t) #endif #ifndef PERL_UNUSED_DECL # ifdef HASATTRIBUTE # if (defined(__GNUC__) && defined(__cplusplus)) || defined(__INTEL_COMPILER) # define PERL_UNUSED_DECL # else # define PERL_UNUSED_DECL __attribute__((unused)) # endif # else # define PERL_UNUSED_DECL # endif #endif #ifndef PERL_UNUSED_ARG # if defined(lint) && defined(S_SPLINT_S) /* www.splint.org */ # include # define PERL_UNUSED_ARG(x) NOTE(ARGUNUSED(x)) # else # define PERL_UNUSED_ARG(x) ((void)x) # endif #endif #ifndef PERL_UNUSED_VAR # define PERL_UNUSED_VAR(x) ((void)x) #endif #ifndef PERL_UNUSED_CONTEXT # ifdef USE_ITHREADS # define PERL_UNUSED_CONTEXT PERL_UNUSED_ARG(my_perl) # else # define PERL_UNUSED_CONTEXT # endif #endif #ifndef NOOP # define NOOP /*EMPTY*/(void)0 #endif #ifndef dNOOP # define dNOOP extern int /*@unused@*/ Perl___notused PERL_UNUSED_DECL #endif #ifndef NVTYPE # if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE) # define NVTYPE long double # else # define NVTYPE double # endif typedef NVTYPE NV; #endif #ifndef INT2PTR # if (IVSIZE == PTRSIZE) && (UVSIZE == PTRSIZE) # define PTRV UV # define INT2PTR(any,d) (any)(d) # else # if PTRSIZE == LONGSIZE # define PTRV unsigned long # else # define PTRV unsigned # endif # define INT2PTR(any,d) (any)(PTRV)(d) # endif #endif #ifndef PTR2ul # if PTRSIZE == LONGSIZE # define PTR2ul(p) (unsigned long)(p) # else # define PTR2ul(p) INT2PTR(unsigned long,p) # endif #endif #ifndef PTR2nat # define PTR2nat(p) (PTRV)(p) #endif #ifndef NUM2PTR # define NUM2PTR(any,d) (any)PTR2nat(d) #endif #ifndef PTR2IV # define PTR2IV(p) INT2PTR(IV,p) #endif #ifndef PTR2UV # define PTR2UV(p) INT2PTR(UV,p) #endif #ifndef PTR2NV # define PTR2NV(p) NUM2PTR(NV,p) #endif #undef START_EXTERN_C #undef END_EXTERN_C #undef EXTERN_C #ifdef __cplusplus # define START_EXTERN_C extern "C" { # define END_EXTERN_C } # define EXTERN_C extern "C" #else # define START_EXTERN_C # define END_EXTERN_C # define EXTERN_C extern #endif #if defined(PERL_GCC_PEDANTIC) # ifndef PERL_GCC_BRACE_GROUPS_FORBIDDEN # define PERL_GCC_BRACE_GROUPS_FORBIDDEN # endif #endif #if defined(__GNUC__) && !defined(PERL_GCC_BRACE_GROUPS_FORBIDDEN) && !defined(__cplusplus) # ifndef PERL_USE_GCC_BRACE_GROUPS # define PERL_USE_GCC_BRACE_GROUPS # endif #endif #undef STMT_START #undef STMT_END #ifdef PERL_USE_GCC_BRACE_GROUPS # define STMT_START (void)( /* gcc supports ``({ STATEMENTS; })'' */ # define STMT_END ) #else # if defined(VOIDFLAGS) && (VOIDFLAGS) && (defined(sun) || defined(__sun__)) && !defined(__GNUC__) # define STMT_START if (1) # define STMT_END else (void)0 # else # define STMT_START do # define STMT_END while (0) # endif #endif #ifndef boolSV # define boolSV(b) ((b) ? &PL_sv_yes : &PL_sv_no) #endif /* DEFSV appears first in 5.004_56 */ #ifndef DEFSV # define DEFSV GvSV(PL_defgv) #endif #ifndef SAVE_DEFSV # define SAVE_DEFSV SAVESPTR(GvSV(PL_defgv)) #endif #ifndef DEFSV_set # define DEFSV_set(sv) (DEFSV = (sv)) #endif /* Older perls (<=5.003) lack AvFILLp */ #ifndef AvFILLp # define AvFILLp AvFILL #endif #ifndef ERRSV # define ERRSV get_sv("@",FALSE) #endif /* Hint: gv_stashpvn * This function's backport doesn't support the length parameter, but * rather ignores it. Portability can only be ensured if the length * parameter is used for speed reasons, but the length can always be * correctly computed from the string argument. */ #ifndef gv_stashpvn # define gv_stashpvn(str,len,create) gv_stashpv(str,create) #endif /* Replace: 1 */ #ifndef get_cv # define get_cv perl_get_cv #endif #ifndef get_sv # define get_sv perl_get_sv #endif #ifndef get_av # define get_av perl_get_av #endif #ifndef get_hv # define get_hv perl_get_hv #endif /* Replace: 0 */ #ifndef dUNDERBAR # define dUNDERBAR dNOOP #endif #ifndef UNDERBAR # define UNDERBAR DEFSV #endif #ifndef dAX # define dAX I32 ax = MARK - PL_stack_base + 1 #endif #ifndef dITEMS # define dITEMS I32 items = SP - MARK #endif #ifndef dXSTARG # define dXSTARG SV * targ = sv_newmortal() #endif #ifndef dAXMARK # define dAXMARK I32 ax = POPMARK; \ register SV ** const mark = PL_stack_base + ax++ #endif #ifndef XSprePUSH # define XSprePUSH (sp = PL_stack_base + ax - 1) #endif #if (PERL_BCDVERSION < 0x5005000) # undef XSRETURN # define XSRETURN(off) \ STMT_START { \ PL_stack_sp = PL_stack_base + ax + ((off) - 1); \ return; \ } STMT_END #endif #ifndef XSPROTO # define XSPROTO(name) void name(pTHX_ CV* cv) #endif #ifndef SVfARG # define SVfARG(p) ((void*)(p)) #endif #ifndef PERL_ABS # define PERL_ABS(x) ((x) < 0 ? -(x) : (x)) #endif #ifndef dVAR # define dVAR dNOOP #endif #ifndef SVf # define SVf "_" #endif #ifndef UTF8_MAXBYTES # define UTF8_MAXBYTES UTF8_MAXLEN #endif #ifndef CPERLscope # define CPERLscope(x) x #endif #ifndef PERL_HASH # define PERL_HASH(hash,str,len) \ STMT_START { \ const char *s_PeRlHaSh = str; \ I32 i_PeRlHaSh = len; \ U32 hash_PeRlHaSh = 0; \ while (i_PeRlHaSh--) \ hash_PeRlHaSh = hash_PeRlHaSh * 33 + *s_PeRlHaSh++; \ (hash) = hash_PeRlHaSh; \ } STMT_END #endif #ifndef PERLIO_FUNCS_DECL # ifdef PERLIO_FUNCS_CONST # define PERLIO_FUNCS_DECL(funcs) const PerlIO_funcs funcs # define PERLIO_FUNCS_CAST(funcs) (PerlIO_funcs*)(funcs) # else # define PERLIO_FUNCS_DECL(funcs) PerlIO_funcs funcs # define PERLIO_FUNCS_CAST(funcs) (funcs) # endif #endif /* provide these typedefs for older perls */ #if (PERL_BCDVERSION < 0x5009003) # ifdef ARGSproto typedef OP* (CPERLscope(*Perl_ppaddr_t))(ARGSproto); # else typedef OP* (CPERLscope(*Perl_ppaddr_t))(pTHX); # endif typedef OP* (CPERLscope(*Perl_check_t)) (pTHX_ OP*); #endif #ifndef isPSXSPC # define isPSXSPC(c) (isSPACE(c) || (c) == '\v') #endif #ifndef isBLANK # define isBLANK(c) ((c) == ' ' || (c) == '\t') #endif #ifdef EBCDIC #ifndef isALNUMC # define isALNUMC(c) isalnum(c) #endif #ifndef isASCII # define isASCII(c) isascii(c) #endif #ifndef isCNTRL # define isCNTRL(c) iscntrl(c) #endif #ifndef isGRAPH # define isGRAPH(c) isgraph(c) #endif #ifndef isPRINT # define isPRINT(c) isprint(c) #endif #ifndef isPUNCT # define isPUNCT(c) ispunct(c) #endif #ifndef isXDIGIT # define isXDIGIT(c) isxdigit(c) #endif #else # if (PERL_BCDVERSION < 0x5010000) /* Hint: isPRINT * The implementation in older perl versions includes all of the * isSPACE() characters, which is wrong. The version provided by * Devel::PPPort always overrides a present buggy version. */ # undef isPRINT # endif #ifdef HAS_QUAD # define WIDEST_UTYPE U64TYPE #else # define WIDEST_UTYPE U32 #endif #ifndef isALNUMC # define isALNUMC(c) (isALPHA(c) || isDIGIT(c)) #endif #ifndef isASCII # define isASCII(c) ((WIDEST_UTYPE) (c) <= 127) #endif #ifndef isCNTRL # define isCNTRL(c) ((WIDEST_UTYPE) (c) < ' ' || (c) == 127) #endif #ifndef isGRAPH # define isGRAPH(c) (isALNUM(c) || isPUNCT(c)) #endif #ifndef isPRINT # define isPRINT(c) (((c) >= 32 && (c) < 127)) #endif #ifndef isPUNCT # define isPUNCT(c) (((c) >= 33 && (c) <= 47) || ((c) >= 58 && (c) <= 64) || ((c) >= 91 && (c) <= 96) || ((c) >= 123 && (c) <= 126)) #endif #ifndef isXDIGIT # define isXDIGIT(c) (isDIGIT(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) #endif #endif #ifndef PERL_SIGNALS_UNSAFE_FLAG #define PERL_SIGNALS_UNSAFE_FLAG 0x0001 #if (PERL_BCDVERSION < 0x5008000) # define D_PPP_PERL_SIGNALS_INIT PERL_SIGNALS_UNSAFE_FLAG #else # define D_PPP_PERL_SIGNALS_INIT 0 #endif #if defined(NEED_PL_signals) static U32 DPPP_(my_PL_signals) = D_PPP_PERL_SIGNALS_INIT; #elif defined(NEED_PL_signals_GLOBAL) U32 DPPP_(my_PL_signals) = D_PPP_PERL_SIGNALS_INIT; #else extern U32 DPPP_(my_PL_signals); #endif #define PL_signals DPPP_(my_PL_signals) #endif /* Hint: PL_ppaddr * Calling an op via PL_ppaddr requires passing a context argument * for threaded builds. Since the context argument is different for * 5.005 perls, you can use aTHXR (supplied by ppport.h), which will * automatically be defined as the correct argument. */ #if (PERL_BCDVERSION <= 0x5005005) /* Replace: 1 */ # define PL_ppaddr ppaddr # define PL_no_modify no_modify /* Replace: 0 */ #endif #if (PERL_BCDVERSION <= 0x5004005) /* Replace: 1 */ # define PL_DBsignal DBsignal # define PL_DBsingle DBsingle # define PL_DBsub DBsub # define PL_DBtrace DBtrace # define PL_Sv Sv # define PL_bufend bufend # define PL_bufptr bufptr # define PL_compiling compiling # define PL_copline copline # define PL_curcop curcop # define PL_curstash curstash # define PL_debstash debstash # define PL_defgv defgv # define PL_diehook diehook # define PL_dirty dirty # define PL_dowarn dowarn # define PL_errgv errgv # define PL_error_count error_count # define PL_expect expect # define PL_hexdigit hexdigit # define PL_hints hints # define PL_in_my in_my # define PL_laststatval laststatval # define PL_lex_state lex_state # define PL_lex_stuff lex_stuff # define PL_linestr linestr # define PL_na na # define PL_perl_destruct_level perl_destruct_level # define PL_perldb perldb # define PL_rsfp_filters rsfp_filters # define PL_rsfp rsfp # define PL_stack_base stack_base # define PL_stack_sp stack_sp # define PL_statcache statcache # define PL_stdingv stdingv # define PL_sv_arenaroot sv_arenaroot # define PL_sv_no sv_no # define PL_sv_undef sv_undef # define PL_sv_yes sv_yes # define PL_tainted tainted # define PL_tainting tainting # define PL_tokenbuf tokenbuf /* Replace: 0 */ #endif /* Warning: PL_parser * For perl versions earlier than 5.9.5, this is an always * non-NULL dummy. Also, it cannot be dereferenced. Don't * use it if you can avoid is and unless you absolutely know * what you're doing. * If you always check that PL_parser is non-NULL, you can * define DPPP_PL_parser_NO_DUMMY to avoid the creation of * a dummy parser structure. */ #if (PERL_BCDVERSION >= 0x5009005) # ifdef DPPP_PL_parser_NO_DUMMY # define D_PPP_my_PL_parser_var(var) ((PL_parser ? PL_parser : \ (croak("panic: PL_parser == NULL in %s:%d", \ __FILE__, __LINE__), (yy_parser *) NULL))->var) # else # ifdef DPPP_PL_parser_NO_DUMMY_WARNING # define D_PPP_parser_dummy_warning(var) # else # define D_PPP_parser_dummy_warning(var) \ warn("warning: dummy PL_" #var " used in %s:%d", __FILE__, __LINE__), # endif # define D_PPP_my_PL_parser_var(var) ((PL_parser ? PL_parser : \ (D_PPP_parser_dummy_warning(var) &DPPP_(dummy_PL_parser)))->var) #if defined(NEED_PL_parser) static yy_parser DPPP_(dummy_PL_parser); #elif defined(NEED_PL_parser_GLOBAL) yy_parser DPPP_(dummy_PL_parser); #else extern yy_parser DPPP_(dummy_PL_parser); #endif # endif /* PL_expect, PL_copline, PL_rsfp, PL_rsfp_filters, PL_linestr, PL_bufptr, PL_bufend, PL_lex_state, PL_lex_stuff, PL_tokenbuf depends on PL_parser */ /* Warning: PL_expect, PL_copline, PL_rsfp, PL_rsfp_filters, PL_linestr, PL_bufptr, PL_bufend, PL_lex_state, PL_lex_stuff, PL_tokenbuf * Do not use this variable unless you know exactly what you're * doint. It is internal to the perl parser and may change or even * be removed in the future. As of perl 5.9.5, you have to check * for (PL_parser != NULL) for this variable to have any effect. * An always non-NULL PL_parser dummy is provided for earlier * perl versions. * If PL_parser is NULL when you try to access this variable, a * dummy is being accessed instead and a warning is issued unless * you define DPPP_PL_parser_NO_DUMMY_WARNING. * If DPPP_PL_parser_NO_DUMMY is defined, the code trying to access * this variable will croak with a panic message. */ # define PL_expect D_PPP_my_PL_parser_var(expect) # define PL_copline D_PPP_my_PL_parser_var(copline) # define PL_rsfp D_PPP_my_PL_parser_var(rsfp) # define PL_rsfp_filters D_PPP_my_PL_parser_var(rsfp_filters) # define PL_linestr D_PPP_my_PL_parser_var(linestr) # define PL_bufptr D_PPP_my_PL_parser_var(bufptr) # define PL_bufend D_PPP_my_PL_parser_var(bufend) # define PL_lex_state D_PPP_my_PL_parser_var(lex_state) # define PL_lex_stuff D_PPP_my_PL_parser_var(lex_stuff) # define PL_tokenbuf D_PPP_my_PL_parser_var(tokenbuf) # define PL_in_my D_PPP_my_PL_parser_var(in_my) # define PL_in_my_stash D_PPP_my_PL_parser_var(in_my_stash) # define PL_error_count D_PPP_my_PL_parser_var(error_count) #else /* ensure that PL_parser != NULL and cannot be dereferenced */ # define PL_parser ((void *) 1) #endif #ifndef mPUSHs # define mPUSHs(s) PUSHs(sv_2mortal(s)) #endif #ifndef PUSHmortal # define PUSHmortal PUSHs(sv_newmortal()) #endif #ifndef mPUSHp # define mPUSHp(p,l) sv_setpvn(PUSHmortal, (p), (l)) #endif #ifndef mPUSHn # define mPUSHn(n) sv_setnv(PUSHmortal, (NV)(n)) #endif #ifndef mPUSHi # define mPUSHi(i) sv_setiv(PUSHmortal, (IV)(i)) #endif #ifndef mPUSHu # define mPUSHu(u) sv_setuv(PUSHmortal, (UV)(u)) #endif #ifndef mXPUSHs # define mXPUSHs(s) XPUSHs(sv_2mortal(s)) #endif #ifndef XPUSHmortal # define XPUSHmortal XPUSHs(sv_newmortal()) #endif #ifndef mXPUSHp # define mXPUSHp(p,l) STMT_START { EXTEND(sp,1); sv_setpvn(PUSHmortal, (p), (l)); } STMT_END #endif #ifndef mXPUSHn # define mXPUSHn(n) STMT_START { EXTEND(sp,1); sv_setnv(PUSHmortal, (NV)(n)); } STMT_END #endif #ifndef mXPUSHi # define mXPUSHi(i) STMT_START { EXTEND(sp,1); sv_setiv(PUSHmortal, (IV)(i)); } STMT_END #endif #ifndef mXPUSHu # define mXPUSHu(u) STMT_START { EXTEND(sp,1); sv_setuv(PUSHmortal, (UV)(u)); } STMT_END #endif /* Replace: 1 */ #ifndef call_sv # define call_sv perl_call_sv #endif #ifndef call_pv # define call_pv perl_call_pv #endif #ifndef call_argv # define call_argv perl_call_argv #endif #ifndef call_method # define call_method perl_call_method #endif #ifndef eval_sv # define eval_sv perl_eval_sv #endif /* Replace: 0 */ #ifndef PERL_LOADMOD_DENY # define PERL_LOADMOD_DENY 0x1 #endif #ifndef PERL_LOADMOD_NOIMPORT # define PERL_LOADMOD_NOIMPORT 0x2 #endif #ifndef PERL_LOADMOD_IMPORT_OPS # define PERL_LOADMOD_IMPORT_OPS 0x4 #endif #ifndef G_METHOD # define G_METHOD 64 # ifdef call_sv # undef call_sv # endif # if (PERL_BCDVERSION < 0x5006000) # define call_sv(sv, flags) ((flags) & G_METHOD ? perl_call_method((char *) SvPV_nolen_const(sv), \ (flags) & ~G_METHOD) : perl_call_sv(sv, flags)) # else # define call_sv(sv, flags) ((flags) & G_METHOD ? Perl_call_method(aTHX_ (char *) SvPV_nolen_const(sv), \ (flags) & ~G_METHOD) : Perl_call_sv(aTHX_ sv, flags)) # endif #endif /* Replace perl_eval_pv with eval_pv */ #ifndef eval_pv #if defined(NEED_eval_pv) static SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error); static #else extern SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error); #endif #ifdef eval_pv # undef eval_pv #endif #define eval_pv(a,b) DPPP_(my_eval_pv)(aTHX_ a,b) #define Perl_eval_pv DPPP_(my_eval_pv) #if defined(NEED_eval_pv) || defined(NEED_eval_pv_GLOBAL) SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error) { dSP; SV* sv = newSVpv(p, 0); PUSHMARK(sp); eval_sv(sv, G_SCALAR); SvREFCNT_dec(sv); SPAGAIN; sv = POPs; PUTBACK; if (croak_on_error && SvTRUE(GvSV(errgv))) croak(SvPVx(GvSV(errgv), na)); return sv; } #endif #endif #ifndef vload_module #if defined(NEED_vload_module) static void DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args); static #else extern void DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args); #endif #ifdef vload_module # undef vload_module #endif #define vload_module(a,b,c,d) DPPP_(my_vload_module)(aTHX_ a,b,c,d) #define Perl_vload_module DPPP_(my_vload_module) #if defined(NEED_vload_module) || defined(NEED_vload_module_GLOBAL) void DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args) { dTHR; dVAR; OP *veop, *imop; OP * const modname = newSVOP(OP_CONST, 0, name); /* 5.005 has a somewhat hacky force_normal that doesn't croak on SvREADONLY() if PL_compling is true. Current perls take care in ck_require() to correctly turn off SvREADONLY before calling force_normal_flags(). This seems a better fix than fudging PL_compling */ SvREADONLY_off(((SVOP*)modname)->op_sv); modname->op_private |= OPpCONST_BARE; if (ver) { veop = newSVOP(OP_CONST, 0, ver); } else veop = NULL; if (flags & PERL_LOADMOD_NOIMPORT) { imop = sawparens(newNULLLIST()); } else if (flags & PERL_LOADMOD_IMPORT_OPS) { imop = va_arg(*args, OP*); } else { SV *sv; imop = NULL; sv = va_arg(*args, SV*); while (sv) { imop = append_elem(OP_LIST, imop, newSVOP(OP_CONST, 0, sv)); sv = va_arg(*args, SV*); } } { const line_t ocopline = PL_copline; COP * const ocurcop = PL_curcop; const int oexpect = PL_expect; #if (PERL_BCDVERSION >= 0x5004000) utilize(!(flags & PERL_LOADMOD_DENY), start_subparse(FALSE, 0), veop, modname, imop); #else utilize(!(flags & PERL_LOADMOD_DENY), start_subparse(), modname, imop); #endif PL_expect = oexpect; PL_copline = ocopline; PL_curcop = ocurcop; } } #endif #endif #ifndef load_module #if defined(NEED_load_module) static void DPPP_(my_load_module)(U32 flags, SV *name, SV *ver, ...); static #else extern void DPPP_(my_load_module)(U32 flags, SV *name, SV *ver, ...); #endif #ifdef load_module # undef load_module #endif #define load_module DPPP_(my_load_module) #define Perl_load_module DPPP_(my_load_module) #if defined(NEED_load_module) || defined(NEED_load_module_GLOBAL) void DPPP_(my_load_module)(U32 flags, SV *name, SV *ver, ...) { va_list args; va_start(args, ver); vload_module(flags, name, ver, &args); va_end(args); } #endif #endif #ifndef newRV_inc # define newRV_inc(sv) newRV(sv) /* Replace */ #endif #ifndef newRV_noinc #if defined(NEED_newRV_noinc) static SV * DPPP_(my_newRV_noinc)(SV *sv); static #else extern SV * DPPP_(my_newRV_noinc)(SV *sv); #endif #ifdef newRV_noinc # undef newRV_noinc #endif #define newRV_noinc(a) DPPP_(my_newRV_noinc)(aTHX_ a) #define Perl_newRV_noinc DPPP_(my_newRV_noinc) #if defined(NEED_newRV_noinc) || defined(NEED_newRV_noinc_GLOBAL) SV * DPPP_(my_newRV_noinc)(SV *sv) { SV *rv = (SV *)newRV(sv); SvREFCNT_dec(sv); return rv; } #endif #endif /* Hint: newCONSTSUB * Returns a CV* as of perl-5.7.1. This return value is not supported * by Devel::PPPort. */ /* newCONSTSUB from IO.xs is in the core starting with 5.004_63 */ #if (PERL_BCDVERSION < 0x5004063) && (PERL_BCDVERSION != 0x5004005) #if defined(NEED_newCONSTSUB) static void DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv); static #else extern void DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv); #endif #ifdef newCONSTSUB # undef newCONSTSUB #endif #define newCONSTSUB(a,b,c) DPPP_(my_newCONSTSUB)(aTHX_ a,b,c) #define Perl_newCONSTSUB DPPP_(my_newCONSTSUB) #if defined(NEED_newCONSTSUB) || defined(NEED_newCONSTSUB_GLOBAL) /* This is just a trick to avoid a dependency of newCONSTSUB on PL_parser */ /* (There's no PL_parser in perl < 5.005, so this is completely safe) */ #define D_PPP_PL_copline PL_copline void DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv) { U32 oldhints = PL_hints; HV *old_cop_stash = PL_curcop->cop_stash; HV *old_curstash = PL_curstash; line_t oldline = PL_curcop->cop_line; PL_curcop->cop_line = D_PPP_PL_copline; PL_hints &= ~HINT_BLOCK_SCOPE; if (stash) PL_curstash = PL_curcop->cop_stash = stash; newSUB( #if (PERL_BCDVERSION < 0x5003022) start_subparse(), #elif (PERL_BCDVERSION == 0x5003022) start_subparse(0), #else /* 5.003_23 onwards */ start_subparse(FALSE, 0), #endif newSVOP(OP_CONST, 0, newSVpv((char *) name, 0)), newSVOP(OP_CONST, 0, &PL_sv_no), /* SvPV(&PL_sv_no) == "" -- GMB */ newSTATEOP(0, Nullch, newSVOP(OP_CONST, 0, sv)) ); PL_hints = oldhints; PL_curcop->cop_stash = old_cop_stash; PL_curstash = old_curstash; PL_curcop->cop_line = oldline; } #endif #endif /* * Boilerplate macros for initializing and accessing interpreter-local * data from C. All statics in extensions should be reworked to use * this, if you want to make the extension thread-safe. See ext/re/re.xs * for an example of the use of these macros. * * Code that uses these macros is responsible for the following: * 1. #define MY_CXT_KEY to a unique string, e.g. "DynaLoader_guts" * 2. Declare a typedef named my_cxt_t that is a structure that contains * all the data that needs to be interpreter-local. * 3. Use the START_MY_CXT macro after the declaration of my_cxt_t. * 4. Use the MY_CXT_INIT macro such that it is called exactly once * (typically put in the BOOT: section). * 5. Use the members of the my_cxt_t structure everywhere as * MY_CXT.member. * 6. Use the dMY_CXT macro (a declaration) in all the functions that * access MY_CXT. */ #if defined(MULTIPLICITY) || defined(PERL_OBJECT) || \ defined(PERL_CAPI) || defined(PERL_IMPLICIT_CONTEXT) #ifndef START_MY_CXT /* This must appear in all extensions that define a my_cxt_t structure, * right after the definition (i.e. at file scope). The non-threads * case below uses it to declare the data as static. */ #define START_MY_CXT #if (PERL_BCDVERSION < 0x5004068) /* Fetches the SV that keeps the per-interpreter data. */ #define dMY_CXT_SV \ SV *my_cxt_sv = get_sv(MY_CXT_KEY, FALSE) #else /* >= perl5.004_68 */ #define dMY_CXT_SV \ SV *my_cxt_sv = *hv_fetch(PL_modglobal, MY_CXT_KEY, \ sizeof(MY_CXT_KEY)-1, TRUE) #endif /* < perl5.004_68 */ /* This declaration should be used within all functions that use the * interpreter-local data. */ #define dMY_CXT \ dMY_CXT_SV; \ my_cxt_t *my_cxtp = INT2PTR(my_cxt_t*,SvUV(my_cxt_sv)) /* Creates and zeroes the per-interpreter data. * (We allocate my_cxtp in a Perl SV so that it will be released when * the interpreter goes away.) */ #define MY_CXT_INIT \ dMY_CXT_SV; \ /* newSV() allocates one more than needed */ \ my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1));\ Zero(my_cxtp, 1, my_cxt_t); \ sv_setuv(my_cxt_sv, PTR2UV(my_cxtp)) /* This macro must be used to access members of the my_cxt_t structure. * e.g. MYCXT.some_data */ #define MY_CXT (*my_cxtp) /* Judicious use of these macros can reduce the number of times dMY_CXT * is used. Use is similar to pTHX, aTHX etc. */ #define pMY_CXT my_cxt_t *my_cxtp #define pMY_CXT_ pMY_CXT, #define _pMY_CXT ,pMY_CXT #define aMY_CXT my_cxtp #define aMY_CXT_ aMY_CXT, #define _aMY_CXT ,aMY_CXT #endif /* START_MY_CXT */ #ifndef MY_CXT_CLONE /* Clones the per-interpreter data. */ #define MY_CXT_CLONE \ dMY_CXT_SV; \ my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1));\ Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t);\ sv_setuv(my_cxt_sv, PTR2UV(my_cxtp)) #endif #else /* single interpreter */ #ifndef START_MY_CXT #define START_MY_CXT static my_cxt_t my_cxt; #define dMY_CXT_SV dNOOP #define dMY_CXT dNOOP #define MY_CXT_INIT NOOP #define MY_CXT my_cxt #define pMY_CXT void #define pMY_CXT_ #define _pMY_CXT #define aMY_CXT #define aMY_CXT_ #define _aMY_CXT #endif /* START_MY_CXT */ #ifndef MY_CXT_CLONE #define MY_CXT_CLONE NOOP #endif #endif #ifndef IVdf # if IVSIZE == LONGSIZE # define IVdf "ld" # define UVuf "lu" # define UVof "lo" # define UVxf "lx" # define UVXf "lX" # elif IVSIZE == INTSIZE # define IVdf "d" # define UVuf "u" # define UVof "o" # define UVxf "x" # define UVXf "X" # else # error "cannot define IV/UV formats" # endif #endif #ifndef NVef # if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE) && \ defined(PERL_PRIfldbl) && (PERL_BCDVERSION != 0x5006000) /* Not very likely, but let's try anyway. */ # define NVef PERL_PRIeldbl # define NVff PERL_PRIfldbl # define NVgf PERL_PRIgldbl # else # define NVef "e" # define NVff "f" # define NVgf "g" # endif #endif #ifndef SvREFCNT_inc # ifdef PERL_USE_GCC_BRACE_GROUPS # define SvREFCNT_inc(sv) \ ({ \ SV * const _sv = (SV*)(sv); \ if (_sv) \ (SvREFCNT(_sv))++; \ _sv; \ }) # else # define SvREFCNT_inc(sv) \ ((PL_Sv=(SV*)(sv)) ? (++(SvREFCNT(PL_Sv)),PL_Sv) : NULL) # endif #endif #ifndef SvREFCNT_inc_simple # ifdef PERL_USE_GCC_BRACE_GROUPS # define SvREFCNT_inc_simple(sv) \ ({ \ if (sv) \ (SvREFCNT(sv))++; \ (SV *)(sv); \ }) # else # define SvREFCNT_inc_simple(sv) \ ((sv) ? (SvREFCNT(sv)++,(SV*)(sv)) : NULL) # endif #endif #ifndef SvREFCNT_inc_NN # ifdef PERL_USE_GCC_BRACE_GROUPS # define SvREFCNT_inc_NN(sv) \ ({ \ SV * const _sv = (SV*)(sv); \ SvREFCNT(_sv)++; \ _sv; \ }) # else # define SvREFCNT_inc_NN(sv) \ (PL_Sv=(SV*)(sv),++(SvREFCNT(PL_Sv)),PL_Sv) # endif #endif #ifndef SvREFCNT_inc_void # ifdef PERL_USE_GCC_BRACE_GROUPS # define SvREFCNT_inc_void(sv) \ ({ \ SV * const _sv = (SV*)(sv); \ if (_sv) \ (void)(SvREFCNT(_sv)++); \ }) # else # define SvREFCNT_inc_void(sv) \ (void)((PL_Sv=(SV*)(sv)) ? ++(SvREFCNT(PL_Sv)) : 0) # endif #endif #ifndef SvREFCNT_inc_simple_void # define SvREFCNT_inc_simple_void(sv) STMT_START { if (sv) SvREFCNT(sv)++; } STMT_END #endif #ifndef SvREFCNT_inc_simple_NN # define SvREFCNT_inc_simple_NN(sv) (++SvREFCNT(sv), (SV*)(sv)) #endif #ifndef SvREFCNT_inc_void_NN # define SvREFCNT_inc_void_NN(sv) (void)(++SvREFCNT((SV*)(sv))) #endif #ifndef SvREFCNT_inc_simple_void_NN # define SvREFCNT_inc_simple_void_NN(sv) (void)(++SvREFCNT((SV*)(sv))) #endif #ifndef newSV_type #if defined(NEED_newSV_type) static SV* DPPP_(my_newSV_type)(pTHX_ svtype const t); static #else extern SV* DPPP_(my_newSV_type)(pTHX_ svtype const t); #endif #ifdef newSV_type # undef newSV_type #endif #define newSV_type(a) DPPP_(my_newSV_type)(aTHX_ a) #define Perl_newSV_type DPPP_(my_newSV_type) #if defined(NEED_newSV_type) || defined(NEED_newSV_type_GLOBAL) SV* DPPP_(my_newSV_type)(pTHX_ svtype const t) { SV* const sv = newSV(0); sv_upgrade(sv, t); return sv; } #endif #endif #if (PERL_BCDVERSION < 0x5006000) # define D_PPP_CONSTPV_ARG(x) ((char *) (x)) #else # define D_PPP_CONSTPV_ARG(x) (x) #endif #ifndef newSVpvn # define newSVpvn(data,len) ((data) \ ? ((len) ? newSVpv((data), (len)) : newSVpv("", 0)) \ : newSV(0)) #endif #ifndef newSVpvn_utf8 # define newSVpvn_utf8(s, len, u) newSVpvn_flags((s), (len), (u) ? SVf_UTF8 : 0) #endif #ifndef SVf_UTF8 # define SVf_UTF8 0 #endif #ifndef newSVpvn_flags #if defined(NEED_newSVpvn_flags) static SV * DPPP_(my_newSVpvn_flags)(pTHX_ const char *s, STRLEN len, U32 flags); static #else extern SV * DPPP_(my_newSVpvn_flags)(pTHX_ const char *s, STRLEN len, U32 flags); #endif #ifdef newSVpvn_flags # undef newSVpvn_flags #endif #define newSVpvn_flags(a,b,c) DPPP_(my_newSVpvn_flags)(aTHX_ a,b,c) #define Perl_newSVpvn_flags DPPP_(my_newSVpvn_flags) #if defined(NEED_newSVpvn_flags) || defined(NEED_newSVpvn_flags_GLOBAL) SV * DPPP_(my_newSVpvn_flags)(pTHX_ const char *s, STRLEN len, U32 flags) { SV *sv = newSVpvn(D_PPP_CONSTPV_ARG(s), len); SvFLAGS(sv) |= (flags & SVf_UTF8); return (flags & SVs_TEMP) ? sv_2mortal(sv) : sv; } #endif #endif /* Backwards compatibility stuff... :-( */ #if !defined(NEED_sv_2pv_flags) && defined(NEED_sv_2pv_nolen) # define NEED_sv_2pv_flags #endif #if !defined(NEED_sv_2pv_flags_GLOBAL) && defined(NEED_sv_2pv_nolen_GLOBAL) # define NEED_sv_2pv_flags_GLOBAL #endif /* Hint: sv_2pv_nolen * Use the SvPV_nolen() or SvPV_nolen_const() macros instead of sv_2pv_nolen(). */ #ifndef sv_2pv_nolen # define sv_2pv_nolen(sv) SvPV_nolen(sv) #endif #ifdef SvPVbyte /* Hint: SvPVbyte * Does not work in perl-5.6.1, ppport.h implements a version * borrowed from perl-5.7.3. */ #if (PERL_BCDVERSION < 0x5007000) #if defined(NEED_sv_2pvbyte) static char * DPPP_(my_sv_2pvbyte)(pTHX_ SV *sv, STRLEN *lp); static #else extern char * DPPP_(my_sv_2pvbyte)(pTHX_ SV *sv, STRLEN *lp); #endif #ifdef sv_2pvbyte # undef sv_2pvbyte #endif #define sv_2pvbyte(a,b) DPPP_(my_sv_2pvbyte)(aTHX_ a,b) #define Perl_sv_2pvbyte DPPP_(my_sv_2pvbyte) #if defined(NEED_sv_2pvbyte) || defined(NEED_sv_2pvbyte_GLOBAL) char * DPPP_(my_sv_2pvbyte)(pTHX_ SV *sv, STRLEN *lp) { sv_utf8_downgrade(sv,0); return SvPV(sv,*lp); } #endif /* Hint: sv_2pvbyte * Use the SvPVbyte() macro instead of sv_2pvbyte(). */ #undef SvPVbyte #define SvPVbyte(sv, lp) \ ((SvFLAGS(sv) & (SVf_POK|SVf_UTF8)) == (SVf_POK) \ ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_2pvbyte(sv, &lp)) #endif #else # define SvPVbyte SvPV # define sv_2pvbyte sv_2pv #endif #ifndef sv_2pvbyte_nolen # define sv_2pvbyte_nolen(sv) sv_2pv_nolen(sv) #endif /* Hint: sv_pvn * Always use the SvPV() macro instead of sv_pvn(). */ /* Hint: sv_pvn_force * Always use the SvPV_force() macro instead of sv_pvn_force(). */ /* If these are undefined, they're not handled by the core anyway */ #ifndef SV_IMMEDIATE_UNREF # define SV_IMMEDIATE_UNREF 0 #endif #ifndef SV_GMAGIC # define SV_GMAGIC 0 #endif #ifndef SV_COW_DROP_PV # define SV_COW_DROP_PV 0 #endif #ifndef SV_UTF8_NO_ENCODING # define SV_UTF8_NO_ENCODING 0 #endif #ifndef SV_NOSTEAL # define SV_NOSTEAL 0 #endif #ifndef SV_CONST_RETURN # define SV_CONST_RETURN 0 #endif #ifndef SV_MUTABLE_RETURN # define SV_MUTABLE_RETURN 0 #endif #ifndef SV_SMAGIC # define SV_SMAGIC 0 #endif #ifndef SV_HAS_TRAILING_NUL # define SV_HAS_TRAILING_NUL 0 #endif #ifndef SV_COW_SHARED_HASH_KEYS # define SV_COW_SHARED_HASH_KEYS 0 #endif #if (PERL_BCDVERSION < 0x5007002) #if defined(NEED_sv_2pv_flags) static char * DPPP_(my_sv_2pv_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags); static #else extern char * DPPP_(my_sv_2pv_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags); #endif #ifdef sv_2pv_flags # undef sv_2pv_flags #endif #define sv_2pv_flags(a,b,c) DPPP_(my_sv_2pv_flags)(aTHX_ a,b,c) #define Perl_sv_2pv_flags DPPP_(my_sv_2pv_flags) #if defined(NEED_sv_2pv_flags) || defined(NEED_sv_2pv_flags_GLOBAL) char * DPPP_(my_sv_2pv_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags) { STRLEN n_a = (STRLEN) flags; return sv_2pv(sv, lp ? lp : &n_a); } #endif #if defined(NEED_sv_pvn_force_flags) static char * DPPP_(my_sv_pvn_force_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags); static #else extern char * DPPP_(my_sv_pvn_force_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags); #endif #ifdef sv_pvn_force_flags # undef sv_pvn_force_flags #endif #define sv_pvn_force_flags(a,b,c) DPPP_(my_sv_pvn_force_flags)(aTHX_ a,b,c) #define Perl_sv_pvn_force_flags DPPP_(my_sv_pvn_force_flags) #if defined(NEED_sv_pvn_force_flags) || defined(NEED_sv_pvn_force_flags_GLOBAL) char * DPPP_(my_sv_pvn_force_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags) { STRLEN n_a = (STRLEN) flags; return sv_pvn_force(sv, lp ? lp : &n_a); } #endif #endif #if (PERL_BCDVERSION < 0x5008008) || ( (PERL_BCDVERSION >= 0x5009000) && (PERL_BCDVERSION < 0x5009003) ) # define DPPP_SVPV_NOLEN_LP_ARG &PL_na #else # define DPPP_SVPV_NOLEN_LP_ARG 0 #endif #ifndef SvPV_const # define SvPV_const(sv, lp) SvPV_flags_const(sv, lp, SV_GMAGIC) #endif #ifndef SvPV_mutable # define SvPV_mutable(sv, lp) SvPV_flags_mutable(sv, lp, SV_GMAGIC) #endif #ifndef SvPV_flags # define SvPV_flags(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_2pv_flags(sv, &lp, flags)) #endif #ifndef SvPV_flags_const # define SvPV_flags_const(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX_const(sv)) : \ (const char*) sv_2pv_flags(sv, &lp, flags|SV_CONST_RETURN)) #endif #ifndef SvPV_flags_const_nolen # define SvPV_flags_const_nolen(sv, flags) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? SvPVX_const(sv) : \ (const char*) sv_2pv_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, flags|SV_CONST_RETURN)) #endif #ifndef SvPV_flags_mutable # define SvPV_flags_mutable(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX_mutable(sv)) : \ sv_2pv_flags(sv, &lp, flags|SV_MUTABLE_RETURN)) #endif #ifndef SvPV_force # define SvPV_force(sv, lp) SvPV_force_flags(sv, lp, SV_GMAGIC) #endif #ifndef SvPV_force_nolen # define SvPV_force_nolen(sv) SvPV_force_flags_nolen(sv, SV_GMAGIC) #endif #ifndef SvPV_force_mutable # define SvPV_force_mutable(sv, lp) SvPV_force_flags_mutable(sv, lp, SV_GMAGIC) #endif #ifndef SvPV_force_nomg # define SvPV_force_nomg(sv, lp) SvPV_force_flags(sv, lp, 0) #endif #ifndef SvPV_force_nomg_nolen # define SvPV_force_nomg_nolen(sv) SvPV_force_flags_nolen(sv, 0) #endif #ifndef SvPV_force_flags # define SvPV_force_flags(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK|SVf_THINKFIRST)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_pvn_force_flags(sv, &lp, flags)) #endif #ifndef SvPV_force_flags_nolen # define SvPV_force_flags_nolen(sv, flags) \ ((SvFLAGS(sv) & (SVf_POK|SVf_THINKFIRST)) == SVf_POK \ ? SvPVX(sv) : sv_pvn_force_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, flags)) #endif #ifndef SvPV_force_flags_mutable # define SvPV_force_flags_mutable(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK|SVf_THINKFIRST)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX_mutable(sv)) \ : sv_pvn_force_flags(sv, &lp, flags|SV_MUTABLE_RETURN)) #endif #ifndef SvPV_nolen # define SvPV_nolen(sv) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? SvPVX(sv) : sv_2pv_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, SV_GMAGIC)) #endif #ifndef SvPV_nolen_const # define SvPV_nolen_const(sv) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? SvPVX_const(sv) : sv_2pv_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, SV_GMAGIC|SV_CONST_RETURN)) #endif #ifndef SvPV_nomg # define SvPV_nomg(sv, lp) SvPV_flags(sv, lp, 0) #endif #ifndef SvPV_nomg_const # define SvPV_nomg_const(sv, lp) SvPV_flags_const(sv, lp, 0) #endif #ifndef SvPV_nomg_const_nolen # define SvPV_nomg_const_nolen(sv) SvPV_flags_const_nolen(sv, 0) #endif #ifndef SvPV_nomg_nolen # define SvPV_nomg_nolen(sv) ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? SvPVX(sv) : sv_2pv_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, 0)) #endif #ifndef SvPV_renew # define SvPV_renew(sv,n) STMT_START { SvLEN_set(sv, n); \ SvPV_set((sv), (char *) saferealloc( \ (Malloc_t)SvPVX(sv), (MEM_SIZE)((n)))); \ } STMT_END #endif #ifndef SvMAGIC_set # define SvMAGIC_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_PVMG); \ (((XPVMG*) SvANY(sv))->xmg_magic = (val)); } STMT_END #endif #if (PERL_BCDVERSION < 0x5009003) #ifndef SvPVX_const # define SvPVX_const(sv) ((const char*) (0 + SvPVX(sv))) #endif #ifndef SvPVX_mutable # define SvPVX_mutable(sv) (0 + SvPVX(sv)) #endif #ifndef SvRV_set # define SvRV_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_RV); \ (((XRV*) SvANY(sv))->xrv_rv = (val)); } STMT_END #endif #else #ifndef SvPVX_const # define SvPVX_const(sv) ((const char*)((sv)->sv_u.svu_pv)) #endif #ifndef SvPVX_mutable # define SvPVX_mutable(sv) ((sv)->sv_u.svu_pv) #endif #ifndef SvRV_set # define SvRV_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_RV); \ ((sv)->sv_u.svu_rv = (val)); } STMT_END #endif #endif #ifndef SvSTASH_set # define SvSTASH_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_PVMG); \ (((XPVMG*) SvANY(sv))->xmg_stash = (val)); } STMT_END #endif #if (PERL_BCDVERSION < 0x5004000) #ifndef SvUV_set # define SvUV_set(sv, val) \ STMT_START { assert(SvTYPE(sv) == SVt_IV || SvTYPE(sv) >= SVt_PVIV); \ (((XPVIV*) SvANY(sv))->xiv_iv = (IV) (val)); } STMT_END #endif #else #ifndef SvUV_set # define SvUV_set(sv, val) \ STMT_START { assert(SvTYPE(sv) == SVt_IV || SvTYPE(sv) >= SVt_PVIV); \ (((XPVUV*) SvANY(sv))->xuv_uv = (val)); } STMT_END #endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(vnewSVpvf) #if defined(NEED_vnewSVpvf) static SV * DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args); static #else extern SV * DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args); #endif #ifdef vnewSVpvf # undef vnewSVpvf #endif #define vnewSVpvf(a,b) DPPP_(my_vnewSVpvf)(aTHX_ a,b) #define Perl_vnewSVpvf DPPP_(my_vnewSVpvf) #if defined(NEED_vnewSVpvf) || defined(NEED_vnewSVpvf_GLOBAL) SV * DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args) { register SV *sv = newSV(0); sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)); return sv; } #endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vcatpvf) # define sv_vcatpvf(sv, pat, args) sv_vcatpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)) #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vsetpvf) # define sv_vsetpvf(sv, pat, args) sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)) #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_catpvf_mg) #if defined(NEED_sv_catpvf_mg) static void DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...); static #else extern void DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...); #endif #define Perl_sv_catpvf_mg DPPP_(my_sv_catpvf_mg) #if defined(NEED_sv_catpvf_mg) || defined(NEED_sv_catpvf_mg_GLOBAL) void DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...) { va_list args; va_start(args, pat); sv_vcatpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*)); SvSETMAGIC(sv); va_end(args); } #endif #endif #ifdef PERL_IMPLICIT_CONTEXT #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_catpvf_mg_nocontext) #if defined(NEED_sv_catpvf_mg_nocontext) static void DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...); static #else extern void DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...); #endif #define sv_catpvf_mg_nocontext DPPP_(my_sv_catpvf_mg_nocontext) #define Perl_sv_catpvf_mg_nocontext DPPP_(my_sv_catpvf_mg_nocontext) #if defined(NEED_sv_catpvf_mg_nocontext) || defined(NEED_sv_catpvf_mg_nocontext_GLOBAL) void DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...) { dTHX; va_list args; va_start(args, pat); sv_vcatpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*)); SvSETMAGIC(sv); va_end(args); } #endif #endif #endif /* sv_catpvf_mg depends on sv_catpvf_mg_nocontext */ #ifndef sv_catpvf_mg # ifdef PERL_IMPLICIT_CONTEXT # define sv_catpvf_mg Perl_sv_catpvf_mg_nocontext # else # define sv_catpvf_mg Perl_sv_catpvf_mg # endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vcatpvf_mg) # define sv_vcatpvf_mg(sv, pat, args) \ STMT_START { \ sv_vcatpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)); \ SvSETMAGIC(sv); \ } STMT_END #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_setpvf_mg) #if defined(NEED_sv_setpvf_mg) static void DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...); static #else extern void DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...); #endif #define Perl_sv_setpvf_mg DPPP_(my_sv_setpvf_mg) #if defined(NEED_sv_setpvf_mg) || defined(NEED_sv_setpvf_mg_GLOBAL) void DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...) { va_list args; va_start(args, pat); sv_vsetpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*)); SvSETMAGIC(sv); va_end(args); } #endif #endif #ifdef PERL_IMPLICIT_CONTEXT #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_setpvf_mg_nocontext) #if defined(NEED_sv_setpvf_mg_nocontext) static void DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...); static #else extern void DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...); #endif #define sv_setpvf_mg_nocontext DPPP_(my_sv_setpvf_mg_nocontext) #define Perl_sv_setpvf_mg_nocontext DPPP_(my_sv_setpvf_mg_nocontext) #if defined(NEED_sv_setpvf_mg_nocontext) || defined(NEED_sv_setpvf_mg_nocontext_GLOBAL) void DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...) { dTHX; va_list args; va_start(args, pat); sv_vsetpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*)); SvSETMAGIC(sv); va_end(args); } #endif #endif #endif /* sv_setpvf_mg depends on sv_setpvf_mg_nocontext */ #ifndef sv_setpvf_mg # ifdef PERL_IMPLICIT_CONTEXT # define sv_setpvf_mg Perl_sv_setpvf_mg_nocontext # else # define sv_setpvf_mg Perl_sv_setpvf_mg # endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vsetpvf_mg) # define sv_vsetpvf_mg(sv, pat, args) \ STMT_START { \ sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)); \ SvSETMAGIC(sv); \ } STMT_END #endif /* Hint: newSVpvn_share * The SVs created by this function only mimic the behaviour of * shared PVs without really being shared. Only use if you know * what you're doing. */ #ifndef newSVpvn_share #if defined(NEED_newSVpvn_share) static SV * DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash); static #else extern SV * DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash); #endif #ifdef newSVpvn_share # undef newSVpvn_share #endif #define newSVpvn_share(a,b,c) DPPP_(my_newSVpvn_share)(aTHX_ a,b,c) #define Perl_newSVpvn_share DPPP_(my_newSVpvn_share) #if defined(NEED_newSVpvn_share) || defined(NEED_newSVpvn_share_GLOBAL) SV * DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash) { SV *sv; if (len < 0) len = -len; if (!hash) PERL_HASH(hash, (char*) src, len); sv = newSVpvn((char *) src, len); sv_upgrade(sv, SVt_PVIV); SvIVX(sv) = hash; SvREADONLY_on(sv); SvPOK_on(sv); return sv; } #endif #endif #ifndef SvSHARED_HASH # define SvSHARED_HASH(sv) (0 + SvUVX(sv)) #endif #ifndef HvNAME_get # define HvNAME_get(hv) HvNAME(hv) #endif #ifndef HvNAMELEN_get # define HvNAMELEN_get(hv) (HvNAME_get(hv) ? (I32)strlen(HvNAME_get(hv)) : 0) #endif #ifndef GvSVn # define GvSVn(gv) GvSV(gv) #endif #ifndef isGV_with_GP # define isGV_with_GP(gv) isGV(gv) #endif #ifndef gv_fetchpvn_flags # define gv_fetchpvn_flags(name, len, flags, svt) gv_fetchpv(name, flags, svt) #endif #ifndef gv_fetchsv # define gv_fetchsv(name, flags, svt) gv_fetchpv(SvPV_nolen_const(name), flags, svt) #endif #ifndef get_cvn_flags # define get_cvn_flags(name, namelen, flags) get_cv(name, flags) #endif #ifndef WARN_ALL # define WARN_ALL 0 #endif #ifndef WARN_CLOSURE # define WARN_CLOSURE 1 #endif #ifndef WARN_DEPRECATED # define WARN_DEPRECATED 2 #endif #ifndef WARN_EXITING # define WARN_EXITING 3 #endif #ifndef WARN_GLOB # define WARN_GLOB 4 #endif #ifndef WARN_IO # define WARN_IO 5 #endif #ifndef WARN_CLOSED # define WARN_CLOSED 6 #endif #ifndef WARN_EXEC # define WARN_EXEC 7 #endif #ifndef WARN_LAYER # define WARN_LAYER 8 #endif #ifndef WARN_NEWLINE # define WARN_NEWLINE 9 #endif #ifndef WARN_PIPE # define WARN_PIPE 10 #endif #ifndef WARN_UNOPENED # define WARN_UNOPENED 11 #endif #ifndef WARN_MISC # define WARN_MISC 12 #endif #ifndef WARN_NUMERIC # define WARN_NUMERIC 13 #endif #ifndef WARN_ONCE # define WARN_ONCE 14 #endif #ifndef WARN_OVERFLOW # define WARN_OVERFLOW 15 #endif #ifndef WARN_PACK # define WARN_PACK 16 #endif #ifndef WARN_PORTABLE # define WARN_PORTABLE 17 #endif #ifndef WARN_RECURSION # define WARN_RECURSION 18 #endif #ifndef WARN_REDEFINE # define WARN_REDEFINE 19 #endif #ifndef WARN_REGEXP # define WARN_REGEXP 20 #endif #ifndef WARN_SEVERE # define WARN_SEVERE 21 #endif #ifndef WARN_DEBUGGING # define WARN_DEBUGGING 22 #endif #ifndef WARN_INPLACE # define WARN_INPLACE 23 #endif #ifndef WARN_INTERNAL # define WARN_INTERNAL 24 #endif #ifndef WARN_MALLOC # define WARN_MALLOC 25 #endif #ifndef WARN_SIGNAL # define WARN_SIGNAL 26 #endif #ifndef WARN_SUBSTR # define WARN_SUBSTR 27 #endif #ifndef WARN_SYNTAX # define WARN_SYNTAX 28 #endif #ifndef WARN_AMBIGUOUS # define WARN_AMBIGUOUS 29 #endif #ifndef WARN_BAREWORD # define WARN_BAREWORD 30 #endif #ifndef WARN_DIGIT # define WARN_DIGIT 31 #endif #ifndef WARN_PARENTHESIS # define WARN_PARENTHESIS 32 #endif #ifndef WARN_PRECEDENCE # define WARN_PRECEDENCE 33 #endif #ifndef WARN_PRINTF # define WARN_PRINTF 34 #endif #ifndef WARN_PROTOTYPE # define WARN_PROTOTYPE 35 #endif #ifndef WARN_QW # define WARN_QW 36 #endif #ifndef WARN_RESERVED # define WARN_RESERVED 37 #endif #ifndef WARN_SEMICOLON # define WARN_SEMICOLON 38 #endif #ifndef WARN_TAINT # define WARN_TAINT 39 #endif #ifndef WARN_THREADS # define WARN_THREADS 40 #endif #ifndef WARN_UNINITIALIZED # define WARN_UNINITIALIZED 41 #endif #ifndef WARN_UNPACK # define WARN_UNPACK 42 #endif #ifndef WARN_UNTIE # define WARN_UNTIE 43 #endif #ifndef WARN_UTF8 # define WARN_UTF8 44 #endif #ifndef WARN_VOID # define WARN_VOID 45 #endif #ifndef WARN_ASSERTIONS # define WARN_ASSERTIONS 46 #endif #ifndef packWARN # define packWARN(a) (a) #endif #ifndef ckWARN # ifdef G_WARN_ON # define ckWARN(a) (PL_dowarn & G_WARN_ON) # else # define ckWARN(a) PL_dowarn # endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(warner) #if defined(NEED_warner) static void DPPP_(my_warner)(U32 err, const char *pat, ...); static #else extern void DPPP_(my_warner)(U32 err, const char *pat, ...); #endif #define Perl_warner DPPP_(my_warner) #if defined(NEED_warner) || defined(NEED_warner_GLOBAL) void DPPP_(my_warner)(U32 err, const char *pat, ...) { SV *sv; va_list args; PERL_UNUSED_ARG(err); va_start(args, pat); sv = vnewSVpvf(pat, &args); va_end(args); sv_2mortal(sv); warn("%s", SvPV_nolen(sv)); } #define warner Perl_warner #define Perl_warner_nocontext Perl_warner #endif #endif /* concatenating with "" ensures that only literal strings are accepted as argument * note that STR_WITH_LEN() can't be used as argument to macros or functions that * under some configurations might be macros */ #ifndef STR_WITH_LEN # define STR_WITH_LEN(s) (s ""), (sizeof(s)-1) #endif #ifndef newSVpvs # define newSVpvs(str) newSVpvn(str "", sizeof(str) - 1) #endif #ifndef newSVpvs_flags # define newSVpvs_flags(str, flags) newSVpvn_flags(str "", sizeof(str) - 1, flags) #endif #ifndef newSVpvs_share # define newSVpvs_share(str) newSVpvn_share(str "", sizeof(str) - 1, 0) #endif #ifndef sv_catpvs # define sv_catpvs(sv, str) sv_catpvn(sv, str "", sizeof(str) - 1) #endif #ifndef sv_setpvs # define sv_setpvs(sv, str) sv_setpvn(sv, str "", sizeof(str) - 1) #endif #ifndef hv_fetchs # define hv_fetchs(hv, key, lval) hv_fetch(hv, key "", sizeof(key) - 1, lval) #endif #ifndef hv_stores # define hv_stores(hv, key, val) hv_store(hv, key "", sizeof(key) - 1, val, 0) #endif #ifndef gv_fetchpvs # define gv_fetchpvs(name, flags, svt) gv_fetchpvn_flags(name "", sizeof(name) - 1, flags, svt) #endif #ifndef gv_stashpvs # define gv_stashpvs(name, flags) gv_stashpvn(name "", sizeof(name) - 1, flags) #endif #ifndef get_cvs # define get_cvs(name, flags) get_cvn_flags(name "", sizeof(name)-1, flags) #endif #ifndef SvGETMAGIC # define SvGETMAGIC(x) STMT_START { if (SvGMAGICAL(x)) mg_get(x); } STMT_END #endif #ifndef PERL_MAGIC_sv # define PERL_MAGIC_sv '\0' #endif #ifndef PERL_MAGIC_overload # define PERL_MAGIC_overload 'A' #endif #ifndef PERL_MAGIC_overload_elem # define PERL_MAGIC_overload_elem 'a' #endif #ifndef PERL_MAGIC_overload_table # define PERL_MAGIC_overload_table 'c' #endif #ifndef PERL_MAGIC_bm # define PERL_MAGIC_bm 'B' #endif #ifndef PERL_MAGIC_regdata # define PERL_MAGIC_regdata 'D' #endif #ifndef PERL_MAGIC_regdatum # define PERL_MAGIC_regdatum 'd' #endif #ifndef PERL_MAGIC_env # define PERL_MAGIC_env 'E' #endif #ifndef PERL_MAGIC_envelem # define PERL_MAGIC_envelem 'e' #endif #ifndef PERL_MAGIC_fm # define PERL_MAGIC_fm 'f' #endif #ifndef PERL_MAGIC_regex_global # define PERL_MAGIC_regex_global 'g' #endif #ifndef PERL_MAGIC_isa # define PERL_MAGIC_isa 'I' #endif #ifndef PERL_MAGIC_isaelem # define PERL_MAGIC_isaelem 'i' #endif #ifndef PERL_MAGIC_nkeys # define PERL_MAGIC_nkeys 'k' #endif #ifndef PERL_MAGIC_dbfile # define PERL_MAGIC_dbfile 'L' #endif #ifndef PERL_MAGIC_dbline # define PERL_MAGIC_dbline 'l' #endif #ifndef PERL_MAGIC_mutex # define PERL_MAGIC_mutex 'm' #endif #ifndef PERL_MAGIC_shared # define PERL_MAGIC_shared 'N' #endif #ifndef PERL_MAGIC_shared_scalar # define PERL_MAGIC_shared_scalar 'n' #endif #ifndef PERL_MAGIC_collxfrm # define PERL_MAGIC_collxfrm 'o' #endif #ifndef PERL_MAGIC_tied # define PERL_MAGIC_tied 'P' #endif #ifndef PERL_MAGIC_tiedelem # define PERL_MAGIC_tiedelem 'p' #endif #ifndef PERL_MAGIC_tiedscalar # define PERL_MAGIC_tiedscalar 'q' #endif #ifndef PERL_MAGIC_qr # define PERL_MAGIC_qr 'r' #endif #ifndef PERL_MAGIC_sig # define PERL_MAGIC_sig 'S' #endif #ifndef PERL_MAGIC_sigelem # define PERL_MAGIC_sigelem 's' #endif #ifndef PERL_MAGIC_taint # define PERL_MAGIC_taint 't' #endif #ifndef PERL_MAGIC_uvar # define PERL_MAGIC_uvar 'U' #endif #ifndef PERL_MAGIC_uvar_elem # define PERL_MAGIC_uvar_elem 'u' #endif #ifndef PERL_MAGIC_vstring # define PERL_MAGIC_vstring 'V' #endif #ifndef PERL_MAGIC_vec # define PERL_MAGIC_vec 'v' #endif #ifndef PERL_MAGIC_utf8 # define PERL_MAGIC_utf8 'w' #endif #ifndef PERL_MAGIC_substr # define PERL_MAGIC_substr 'x' #endif #ifndef PERL_MAGIC_defelem # define PERL_MAGIC_defelem 'y' #endif #ifndef PERL_MAGIC_glob # define PERL_MAGIC_glob '*' #endif #ifndef PERL_MAGIC_arylen # define PERL_MAGIC_arylen '#' #endif #ifndef PERL_MAGIC_pos # define PERL_MAGIC_pos '.' #endif #ifndef PERL_MAGIC_backref # define PERL_MAGIC_backref '<' #endif #ifndef PERL_MAGIC_ext # define PERL_MAGIC_ext '~' #endif /* That's the best we can do... */ #ifndef sv_catpvn_nomg # define sv_catpvn_nomg sv_catpvn #endif #ifndef sv_catsv_nomg # define sv_catsv_nomg sv_catsv #endif #ifndef sv_setsv_nomg # define sv_setsv_nomg sv_setsv #endif #ifndef sv_pvn_nomg # define sv_pvn_nomg sv_pvn #endif #ifndef SvIV_nomg # define SvIV_nomg SvIV #endif #ifndef SvUV_nomg # define SvUV_nomg SvUV #endif #ifndef sv_catpv_mg # define sv_catpv_mg(sv, ptr) \ STMT_START { \ SV *TeMpSv = sv; \ sv_catpv(TeMpSv,ptr); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_catpvn_mg # define sv_catpvn_mg(sv, ptr, len) \ STMT_START { \ SV *TeMpSv = sv; \ sv_catpvn(TeMpSv,ptr,len); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_catsv_mg # define sv_catsv_mg(dsv, ssv) \ STMT_START { \ SV *TeMpSv = dsv; \ sv_catsv(TeMpSv,ssv); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setiv_mg # define sv_setiv_mg(sv, i) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setiv(TeMpSv,i); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setnv_mg # define sv_setnv_mg(sv, num) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setnv(TeMpSv,num); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setpv_mg # define sv_setpv_mg(sv, ptr) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setpv(TeMpSv,ptr); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setpvn_mg # define sv_setpvn_mg(sv, ptr, len) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setpvn(TeMpSv,ptr,len); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setsv_mg # define sv_setsv_mg(dsv, ssv) \ STMT_START { \ SV *TeMpSv = dsv; \ sv_setsv(TeMpSv,ssv); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setuv_mg # define sv_setuv_mg(sv, i) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setuv(TeMpSv,i); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_usepvn_mg # define sv_usepvn_mg(sv, ptr, len) \ STMT_START { \ SV *TeMpSv = sv; \ sv_usepvn(TeMpSv,ptr,len); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef SvVSTRING_mg # define SvVSTRING_mg(sv) (SvMAGICAL(sv) ? mg_find(sv, PERL_MAGIC_vstring) : NULL) #endif /* Hint: sv_magic_portable * This is a compatibility function that is only available with * Devel::PPPort. It is NOT in the perl core. * Its purpose is to mimic the 5.8.0 behaviour of sv_magic() when * it is being passed a name pointer with namlen == 0. In that * case, perl 5.8.0 and later store the pointer, not a copy of it. * The compatibility can be provided back to perl 5.004. With * earlier versions, the code will not compile. */ #if (PERL_BCDVERSION < 0x5004000) /* code that uses sv_magic_portable will not compile */ #elif (PERL_BCDVERSION < 0x5008000) # define sv_magic_portable(sv, obj, how, name, namlen) \ STMT_START { \ SV *SvMp_sv = (sv); \ char *SvMp_name = (char *) (name); \ I32 SvMp_namlen = (namlen); \ if (SvMp_name && SvMp_namlen == 0) \ { \ MAGIC *mg; \ sv_magic(SvMp_sv, obj, how, 0, 0); \ mg = SvMAGIC(SvMp_sv); \ mg->mg_len = -42; /* XXX: this is the tricky part */ \ mg->mg_ptr = SvMp_name; \ } \ else \ { \ sv_magic(SvMp_sv, obj, how, SvMp_name, SvMp_namlen); \ } \ } STMT_END #else # define sv_magic_portable(a, b, c, d, e) sv_magic(a, b, c, d, e) #endif #ifdef USE_ITHREADS #ifndef CopFILE # define CopFILE(c) ((c)->cop_file) #endif #ifndef CopFILEGV # define CopFILEGV(c) (CopFILE(c) ? gv_fetchfile(CopFILE(c)) : Nullgv) #endif #ifndef CopFILE_set # define CopFILE_set(c,pv) ((c)->cop_file = savepv(pv)) #endif #ifndef CopFILESV # define CopFILESV(c) (CopFILE(c) ? GvSV(gv_fetchfile(CopFILE(c))) : Nullsv) #endif #ifndef CopFILEAV # define CopFILEAV(c) (CopFILE(c) ? GvAV(gv_fetchfile(CopFILE(c))) : Nullav) #endif #ifndef CopSTASHPV # define CopSTASHPV(c) ((c)->cop_stashpv) #endif #ifndef CopSTASHPV_set # define CopSTASHPV_set(c,pv) ((c)->cop_stashpv = ((pv) ? savepv(pv) : Nullch)) #endif #ifndef CopSTASH # define CopSTASH(c) (CopSTASHPV(c) ? gv_stashpv(CopSTASHPV(c),GV_ADD) : Nullhv) #endif #ifndef CopSTASH_set # define CopSTASH_set(c,hv) CopSTASHPV_set(c, (hv) ? HvNAME(hv) : Nullch) #endif #ifndef CopSTASH_eq # define CopSTASH_eq(c,hv) ((hv) && (CopSTASHPV(c) == HvNAME(hv) \ || (CopSTASHPV(c) && HvNAME(hv) \ && strEQ(CopSTASHPV(c), HvNAME(hv))))) #endif #else #ifndef CopFILEGV # define CopFILEGV(c) ((c)->cop_filegv) #endif #ifndef CopFILEGV_set # define CopFILEGV_set(c,gv) ((c)->cop_filegv = (GV*)SvREFCNT_inc(gv)) #endif #ifndef CopFILE_set # define CopFILE_set(c,pv) CopFILEGV_set((c), gv_fetchfile(pv)) #endif #ifndef CopFILESV # define CopFILESV(c) (CopFILEGV(c) ? GvSV(CopFILEGV(c)) : Nullsv) #endif #ifndef CopFILEAV # define CopFILEAV(c) (CopFILEGV(c) ? GvAV(CopFILEGV(c)) : Nullav) #endif #ifndef CopFILE # define CopFILE(c) (CopFILESV(c) ? SvPVX(CopFILESV(c)) : Nullch) #endif #ifndef CopSTASH # define CopSTASH(c) ((c)->cop_stash) #endif #ifndef CopSTASH_set # define CopSTASH_set(c,hv) ((c)->cop_stash = (hv)) #endif #ifndef CopSTASHPV # define CopSTASHPV(c) (CopSTASH(c) ? HvNAME(CopSTASH(c)) : Nullch) #endif #ifndef CopSTASHPV_set # define CopSTASHPV_set(c,pv) CopSTASH_set((c), gv_stashpv(pv,GV_ADD)) #endif #ifndef CopSTASH_eq # define CopSTASH_eq(c,hv) (CopSTASH(c) == (hv)) #endif #endif /* USE_ITHREADS */ #ifndef IN_PERL_COMPILETIME # define IN_PERL_COMPILETIME (PL_curcop == &PL_compiling) #endif #ifndef IN_LOCALE_RUNTIME # define IN_LOCALE_RUNTIME (PL_curcop->op_private & HINT_LOCALE) #endif #ifndef IN_LOCALE_COMPILETIME # define IN_LOCALE_COMPILETIME (PL_hints & HINT_LOCALE) #endif #ifndef IN_LOCALE # define IN_LOCALE (IN_PERL_COMPILETIME ? IN_LOCALE_COMPILETIME : IN_LOCALE_RUNTIME) #endif #ifndef IS_NUMBER_IN_UV # define IS_NUMBER_IN_UV 0x01 #endif #ifndef IS_NUMBER_GREATER_THAN_UV_MAX # define IS_NUMBER_GREATER_THAN_UV_MAX 0x02 #endif #ifndef IS_NUMBER_NOT_INT # define IS_NUMBER_NOT_INT 0x04 #endif #ifndef IS_NUMBER_NEG # define IS_NUMBER_NEG 0x08 #endif #ifndef IS_NUMBER_INFINITY # define IS_NUMBER_INFINITY 0x10 #endif #ifndef IS_NUMBER_NAN # define IS_NUMBER_NAN 0x20 #endif #ifndef GROK_NUMERIC_RADIX # define GROK_NUMERIC_RADIX(sp, send) grok_numeric_radix(sp, send) #endif #ifndef PERL_SCAN_GREATER_THAN_UV_MAX # define PERL_SCAN_GREATER_THAN_UV_MAX 0x02 #endif #ifndef PERL_SCAN_SILENT_ILLDIGIT # define PERL_SCAN_SILENT_ILLDIGIT 0x04 #endif #ifndef PERL_SCAN_ALLOW_UNDERSCORES # define PERL_SCAN_ALLOW_UNDERSCORES 0x01 #endif #ifndef PERL_SCAN_DISALLOW_PREFIX # define PERL_SCAN_DISALLOW_PREFIX 0x02 #endif #ifndef grok_numeric_radix #if defined(NEED_grok_numeric_radix) static bool DPPP_(my_grok_numeric_radix)(pTHX_ const char ** sp, const char * send); static #else extern bool DPPP_(my_grok_numeric_radix)(pTHX_ const char ** sp, const char * send); #endif #ifdef grok_numeric_radix # undef grok_numeric_radix #endif #define grok_numeric_radix(a,b) DPPP_(my_grok_numeric_radix)(aTHX_ a,b) #define Perl_grok_numeric_radix DPPP_(my_grok_numeric_radix) #if defined(NEED_grok_numeric_radix) || defined(NEED_grok_numeric_radix_GLOBAL) bool DPPP_(my_grok_numeric_radix)(pTHX_ const char **sp, const char *send) { #ifdef USE_LOCALE_NUMERIC #ifdef PL_numeric_radix_sv if (PL_numeric_radix_sv && IN_LOCALE) { STRLEN len; char* radix = SvPV(PL_numeric_radix_sv, len); if (*sp + len <= send && memEQ(*sp, radix, len)) { *sp += len; return TRUE; } } #else /* older perls don't have PL_numeric_radix_sv so the radix * must manually be requested from locale.h */ #include dTHR; /* needed for older threaded perls */ struct lconv *lc = localeconv(); char *radix = lc->decimal_point; if (radix && IN_LOCALE) { STRLEN len = strlen(radix); if (*sp + len <= send && memEQ(*sp, radix, len)) { *sp += len; return TRUE; } } #endif #endif /* USE_LOCALE_NUMERIC */ /* always try "." if numeric radix didn't match because * we may have data from different locales mixed */ if (*sp < send && **sp == '.') { ++*sp; return TRUE; } return FALSE; } #endif #endif #ifndef grok_number #if defined(NEED_grok_number) static int DPPP_(my_grok_number)(pTHX_ const char * pv, STRLEN len, UV * valuep); static #else extern int DPPP_(my_grok_number)(pTHX_ const char * pv, STRLEN len, UV * valuep); #endif #ifdef grok_number # undef grok_number #endif #define grok_number(a,b,c) DPPP_(my_grok_number)(aTHX_ a,b,c) #define Perl_grok_number DPPP_(my_grok_number) #if defined(NEED_grok_number) || defined(NEED_grok_number_GLOBAL) int DPPP_(my_grok_number)(pTHX_ const char *pv, STRLEN len, UV *valuep) { const char *s = pv; const char *send = pv + len; const UV max_div_10 = UV_MAX / 10; const char max_mod_10 = UV_MAX % 10; int numtype = 0; int sawinf = 0; int sawnan = 0; while (s < send && isSPACE(*s)) s++; if (s == send) { return 0; } else if (*s == '-') { s++; numtype = IS_NUMBER_NEG; } else if (*s == '+') s++; if (s == send) return 0; /* next must be digit or the radix separator or beginning of infinity */ if (isDIGIT(*s)) { /* UVs are at least 32 bits, so the first 9 decimal digits cannot overflow. */ UV value = *s - '0'; /* This construction seems to be more optimiser friendly. (without it gcc does the isDIGIT test and the *s - '0' separately) With it gcc on arm is managing 6 instructions (6 cycles) per digit. In theory the optimiser could deduce how far to unroll the loop before checking for overflow. */ if (++s < send) { int digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { /* Now got 9 digits, so need to check each time for overflow. */ digit = *s - '0'; while (digit >= 0 && digit <= 9 && (value < max_div_10 || (value == max_div_10 && digit <= max_mod_10))) { value = value * 10 + digit; if (++s < send) digit = *s - '0'; else break; } if (digit >= 0 && digit <= 9 && (s < send)) { /* value overflowed. skip the remaining digits, don't worry about setting *valuep. */ do { s++; } while (s < send && isDIGIT(*s)); numtype |= IS_NUMBER_GREATER_THAN_UV_MAX; goto skip_value; } } } } } } } } } } } } } } } } } } numtype |= IS_NUMBER_IN_UV; if (valuep) *valuep = value; skip_value: if (GROK_NUMERIC_RADIX(&s, send)) { numtype |= IS_NUMBER_NOT_INT; while (s < send && isDIGIT(*s)) /* optional digits after the radix */ s++; } } else if (GROK_NUMERIC_RADIX(&s, send)) { numtype |= IS_NUMBER_NOT_INT | IS_NUMBER_IN_UV; /* valuep assigned below */ /* no digits before the radix means we need digits after it */ if (s < send && isDIGIT(*s)) { do { s++; } while (s < send && isDIGIT(*s)); if (valuep) { /* integer approximation is valid - it's 0. */ *valuep = 0; } } else return 0; } else if (*s == 'I' || *s == 'i') { s++; if (s == send || (*s != 'N' && *s != 'n')) return 0; s++; if (s == send || (*s != 'F' && *s != 'f')) return 0; s++; if (s < send && (*s == 'I' || *s == 'i')) { s++; if (s == send || (*s != 'N' && *s != 'n')) return 0; s++; if (s == send || (*s != 'I' && *s != 'i')) return 0; s++; if (s == send || (*s != 'T' && *s != 't')) return 0; s++; if (s == send || (*s != 'Y' && *s != 'y')) return 0; s++; } sawinf = 1; } else if (*s == 'N' || *s == 'n') { /* XXX TODO: There are signaling NaNs and quiet NaNs. */ s++; if (s == send || (*s != 'A' && *s != 'a')) return 0; s++; if (s == send || (*s != 'N' && *s != 'n')) return 0; s++; sawnan = 1; } else return 0; if (sawinf) { numtype &= IS_NUMBER_NEG; /* Keep track of sign */ numtype |= IS_NUMBER_INFINITY | IS_NUMBER_NOT_INT; } else if (sawnan) { numtype &= IS_NUMBER_NEG; /* Keep track of sign */ numtype |= IS_NUMBER_NAN | IS_NUMBER_NOT_INT; } else if (s < send) { /* we can have an optional exponent part */ if (*s == 'e' || *s == 'E') { /* The only flag we keep is sign. Blow away any "it's UV" */ numtype &= IS_NUMBER_NEG; numtype |= IS_NUMBER_NOT_INT; s++; if (s < send && (*s == '-' || *s == '+')) s++; if (s < send && isDIGIT(*s)) { do { s++; } while (s < send && isDIGIT(*s)); } else return 0; } } while (s < send && isSPACE(*s)) s++; if (s >= send) return numtype; if (len == 10 && memEQ(pv, "0 but true", 10)) { if (valuep) *valuep = 0; return IS_NUMBER_IN_UV; } return 0; } #endif #endif /* * The grok_* routines have been modified to use warn() instead of * Perl_warner(). Also, 'hexdigit' was the former name of PL_hexdigit, * which is why the stack variable has been renamed to 'xdigit'. */ #ifndef grok_bin #if defined(NEED_grok_bin) static UV DPPP_(my_grok_bin)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); static #else extern UV DPPP_(my_grok_bin)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); #endif #ifdef grok_bin # undef grok_bin #endif #define grok_bin(a,b,c,d) DPPP_(my_grok_bin)(aTHX_ a,b,c,d) #define Perl_grok_bin DPPP_(my_grok_bin) #if defined(NEED_grok_bin) || defined(NEED_grok_bin_GLOBAL) UV DPPP_(my_grok_bin)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result) { const char *s = start; STRLEN len = *len_p; UV value = 0; NV value_nv = 0; const UV max_div_2 = UV_MAX / 2; bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES; bool overflowed = FALSE; if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) { /* strip off leading b or 0b. for compatibility silently suffer "b" and "0b" as valid binary numbers. */ if (len >= 1) { if (s[0] == 'b') { s++; len--; } else if (len >= 2 && s[0] == '0' && s[1] == 'b') { s+=2; len-=2; } } } for (; len-- && *s; s++) { char bit = *s; if (bit == '0' || bit == '1') { /* Write it in this wonky order with a goto to attempt to get the compiler to make the common case integer-only loop pretty tight. With gcc seems to be much straighter code than old scan_bin. */ redo: if (!overflowed) { if (value <= max_div_2) { value = (value << 1) | (bit - '0'); continue; } /* Bah. We're just overflowed. */ warn("Integer overflow in binary number"); overflowed = TRUE; value_nv = (NV) value; } value_nv *= 2.0; /* If an NV has not enough bits in its mantissa to * represent a UV this summing of small low-order numbers * is a waste of time (because the NV cannot preserve * the low-order bits anyway): we could just remember when * did we overflow and in the end just multiply value_nv by the * right amount. */ value_nv += (NV)(bit - '0'); continue; } if (bit == '_' && len && allow_underscores && (bit = s[1]) && (bit == '0' || bit == '1')) { --len; ++s; goto redo; } if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT)) warn("Illegal binary digit '%c' ignored", *s); break; } if ( ( overflowed && value_nv > 4294967295.0) #if UVSIZE > 4 || (!overflowed && value > 0xffffffff ) #endif ) { warn("Binary number > 0b11111111111111111111111111111111 non-portable"); } *len_p = s - start; if (!overflowed) { *flags = 0; return value; } *flags = PERL_SCAN_GREATER_THAN_UV_MAX; if (result) *result = value_nv; return UV_MAX; } #endif #endif #ifndef grok_hex #if defined(NEED_grok_hex) static UV DPPP_(my_grok_hex)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); static #else extern UV DPPP_(my_grok_hex)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); #endif #ifdef grok_hex # undef grok_hex #endif #define grok_hex(a,b,c,d) DPPP_(my_grok_hex)(aTHX_ a,b,c,d) #define Perl_grok_hex DPPP_(my_grok_hex) #if defined(NEED_grok_hex) || defined(NEED_grok_hex_GLOBAL) UV DPPP_(my_grok_hex)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result) { const char *s = start; STRLEN len = *len_p; UV value = 0; NV value_nv = 0; const UV max_div_16 = UV_MAX / 16; bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES; bool overflowed = FALSE; const char *xdigit; if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) { /* strip off leading x or 0x. for compatibility silently suffer "x" and "0x" as valid hex numbers. */ if (len >= 1) { if (s[0] == 'x') { s++; len--; } else if (len >= 2 && s[0] == '0' && s[1] == 'x') { s+=2; len-=2; } } } for (; len-- && *s; s++) { xdigit = strchr((char *) PL_hexdigit, *s); if (xdigit) { /* Write it in this wonky order with a goto to attempt to get the compiler to make the common case integer-only loop pretty tight. With gcc seems to be much straighter code than old scan_hex. */ redo: if (!overflowed) { if (value <= max_div_16) { value = (value << 4) | ((xdigit - PL_hexdigit) & 15); continue; } warn("Integer overflow in hexadecimal number"); overflowed = TRUE; value_nv = (NV) value; } value_nv *= 16.0; /* If an NV has not enough bits in its mantissa to * represent a UV this summing of small low-order numbers * is a waste of time (because the NV cannot preserve * the low-order bits anyway): we could just remember when * did we overflow and in the end just multiply value_nv by the * right amount of 16-tuples. */ value_nv += (NV)((xdigit - PL_hexdigit) & 15); continue; } if (*s == '_' && len && allow_underscores && s[1] && (xdigit = strchr((char *) PL_hexdigit, s[1]))) { --len; ++s; goto redo; } if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT)) warn("Illegal hexadecimal digit '%c' ignored", *s); break; } if ( ( overflowed && value_nv > 4294967295.0) #if UVSIZE > 4 || (!overflowed && value > 0xffffffff ) #endif ) { warn("Hexadecimal number > 0xffffffff non-portable"); } *len_p = s - start; if (!overflowed) { *flags = 0; return value; } *flags = PERL_SCAN_GREATER_THAN_UV_MAX; if (result) *result = value_nv; return UV_MAX; } #endif #endif #ifndef grok_oct #if defined(NEED_grok_oct) static UV DPPP_(my_grok_oct)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); static #else extern UV DPPP_(my_grok_oct)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); #endif #ifdef grok_oct # undef grok_oct #endif #define grok_oct(a,b,c,d) DPPP_(my_grok_oct)(aTHX_ a,b,c,d) #define Perl_grok_oct DPPP_(my_grok_oct) #if defined(NEED_grok_oct) || defined(NEED_grok_oct_GLOBAL) UV DPPP_(my_grok_oct)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result) { const char *s = start; STRLEN len = *len_p; UV value = 0; NV value_nv = 0; const UV max_div_8 = UV_MAX / 8; bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES; bool overflowed = FALSE; for (; len-- && *s; s++) { /* gcc 2.95 optimiser not smart enough to figure that this subtraction out front allows slicker code. */ int digit = *s - '0'; if (digit >= 0 && digit <= 7) { /* Write it in this wonky order with a goto to attempt to get the compiler to make the common case integer-only loop pretty tight. */ redo: if (!overflowed) { if (value <= max_div_8) { value = (value << 3) | digit; continue; } /* Bah. We're just overflowed. */ warn("Integer overflow in octal number"); overflowed = TRUE; value_nv = (NV) value; } value_nv *= 8.0; /* If an NV has not enough bits in its mantissa to * represent a UV this summing of small low-order numbers * is a waste of time (because the NV cannot preserve * the low-order bits anyway): we could just remember when * did we overflow and in the end just multiply value_nv by the * right amount of 8-tuples. */ value_nv += (NV)digit; continue; } if (digit == ('_' - '0') && len && allow_underscores && (digit = s[1] - '0') && (digit >= 0 && digit <= 7)) { --len; ++s; goto redo; } /* Allow \octal to work the DWIM way (that is, stop scanning * as soon as non-octal characters are seen, complain only iff * someone seems to want to use the digits eight and nine). */ if (digit == 8 || digit == 9) { if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT)) warn("Illegal octal digit '%c' ignored", *s); } break; } if ( ( overflowed && value_nv > 4294967295.0) #if UVSIZE > 4 || (!overflowed && value > 0xffffffff ) #endif ) { warn("Octal number > 037777777777 non-portable"); } *len_p = s - start; if (!overflowed) { *flags = 0; return value; } *flags = PERL_SCAN_GREATER_THAN_UV_MAX; if (result) *result = value_nv; return UV_MAX; } #endif #endif #if !defined(my_snprintf) #if defined(NEED_my_snprintf) static int DPPP_(my_my_snprintf)(char * buffer, const Size_t len, const char * format, ...); static #else extern int DPPP_(my_my_snprintf)(char * buffer, const Size_t len, const char * format, ...); #endif #define my_snprintf DPPP_(my_my_snprintf) #define Perl_my_snprintf DPPP_(my_my_snprintf) #if defined(NEED_my_snprintf) || defined(NEED_my_snprintf_GLOBAL) int DPPP_(my_my_snprintf)(char *buffer, const Size_t len, const char *format, ...) { dTHX; int retval; va_list ap; va_start(ap, format); #ifdef HAS_VSNPRINTF retval = vsnprintf(buffer, len, format, ap); #else retval = vsprintf(buffer, format, ap); #endif va_end(ap); if (retval < 0 || (len > 0 && (Size_t)retval >= len)) Perl_croak(aTHX_ "panic: my_snprintf buffer overflow"); return retval; } #endif #endif #if !defined(my_sprintf) #if defined(NEED_my_sprintf) static int DPPP_(my_my_sprintf)(char * buffer, const char * pat, ...); static #else extern int DPPP_(my_my_sprintf)(char * buffer, const char * pat, ...); #endif #define my_sprintf DPPP_(my_my_sprintf) #define Perl_my_sprintf DPPP_(my_my_sprintf) #if defined(NEED_my_sprintf) || defined(NEED_my_sprintf_GLOBAL) int DPPP_(my_my_sprintf)(char *buffer, const char* pat, ...) { va_list args; va_start(args, pat); vsprintf(buffer, pat, args); va_end(args); return strlen(buffer); } #endif #endif #ifdef NO_XSLOCKS # ifdef dJMPENV # define dXCPT dJMPENV; int rEtV = 0 # define XCPT_TRY_START JMPENV_PUSH(rEtV); if (rEtV == 0) # define XCPT_TRY_END JMPENV_POP; # define XCPT_CATCH if (rEtV != 0) # define XCPT_RETHROW JMPENV_JUMP(rEtV) # else # define dXCPT Sigjmp_buf oldTOP; int rEtV = 0 # define XCPT_TRY_START Copy(top_env, oldTOP, 1, Sigjmp_buf); rEtV = Sigsetjmp(top_env, 1); if (rEtV == 0) # define XCPT_TRY_END Copy(oldTOP, top_env, 1, Sigjmp_buf); # define XCPT_CATCH if (rEtV != 0) # define XCPT_RETHROW Siglongjmp(top_env, rEtV) # endif #endif #if !defined(my_strlcat) #if defined(NEED_my_strlcat) static Size_t DPPP_(my_my_strlcat)(char * dst, const char * src, Size_t size); static #else extern Size_t DPPP_(my_my_strlcat)(char * dst, const char * src, Size_t size); #endif #define my_strlcat DPPP_(my_my_strlcat) #define Perl_my_strlcat DPPP_(my_my_strlcat) #if defined(NEED_my_strlcat) || defined(NEED_my_strlcat_GLOBAL) Size_t DPPP_(my_my_strlcat)(char *dst, const char *src, Size_t size) { Size_t used, length, copy; used = strlen(dst); length = strlen(src); if (size > 0 && used < size - 1) { copy = (length >= size - used) ? size - used - 1 : length; memcpy(dst + used, src, copy); dst[used + copy] = '\0'; } return used + length; } #endif #endif #if !defined(my_strlcpy) #if defined(NEED_my_strlcpy) static Size_t DPPP_(my_my_strlcpy)(char * dst, const char * src, Size_t size); static #else extern Size_t DPPP_(my_my_strlcpy)(char * dst, const char * src, Size_t size); #endif #define my_strlcpy DPPP_(my_my_strlcpy) #define Perl_my_strlcpy DPPP_(my_my_strlcpy) #if defined(NEED_my_strlcpy) || defined(NEED_my_strlcpy_GLOBAL) Size_t DPPP_(my_my_strlcpy)(char *dst, const char *src, Size_t size) { Size_t length, copy; length = strlen(src); if (size > 0) { copy = (length >= size) ? size - 1 : length; memcpy(dst, src, copy); dst[copy] = '\0'; } return length; } #endif #endif #ifndef PERL_PV_ESCAPE_QUOTE # define PERL_PV_ESCAPE_QUOTE 0x0001 #endif #ifndef PERL_PV_PRETTY_QUOTE # define PERL_PV_PRETTY_QUOTE PERL_PV_ESCAPE_QUOTE #endif #ifndef PERL_PV_PRETTY_ELLIPSES # define PERL_PV_PRETTY_ELLIPSES 0x0002 #endif #ifndef PERL_PV_PRETTY_LTGT # define PERL_PV_PRETTY_LTGT 0x0004 #endif #ifndef PERL_PV_ESCAPE_FIRSTCHAR # define PERL_PV_ESCAPE_FIRSTCHAR 0x0008 #endif #ifndef PERL_PV_ESCAPE_UNI # define PERL_PV_ESCAPE_UNI 0x0100 #endif #ifndef PERL_PV_ESCAPE_UNI_DETECT # define PERL_PV_ESCAPE_UNI_DETECT 0x0200 #endif #ifndef PERL_PV_ESCAPE_ALL # define PERL_PV_ESCAPE_ALL 0x1000 #endif #ifndef PERL_PV_ESCAPE_NOBACKSLASH # define PERL_PV_ESCAPE_NOBACKSLASH 0x2000 #endif #ifndef PERL_PV_ESCAPE_NOCLEAR # define PERL_PV_ESCAPE_NOCLEAR 0x4000 #endif #ifndef PERL_PV_ESCAPE_RE # define PERL_PV_ESCAPE_RE 0x8000 #endif #ifndef PERL_PV_PRETTY_NOCLEAR # define PERL_PV_PRETTY_NOCLEAR PERL_PV_ESCAPE_NOCLEAR #endif #ifndef PERL_PV_PRETTY_DUMP # define PERL_PV_PRETTY_DUMP PERL_PV_PRETTY_ELLIPSES|PERL_PV_PRETTY_QUOTE #endif #ifndef PERL_PV_PRETTY_REGPROP # define PERL_PV_PRETTY_REGPROP PERL_PV_PRETTY_ELLIPSES|PERL_PV_PRETTY_LTGT|PERL_PV_ESCAPE_RE #endif /* Hint: pv_escape * Note that unicode functionality is only backported to * those perl versions that support it. For older perl * versions, the implementation will fall back to bytes. */ #ifndef pv_escape #if defined(NEED_pv_escape) static char * DPPP_(my_pv_escape)(pTHX_ SV * dsv, char const * const str, const STRLEN count, const STRLEN max, STRLEN * const escaped, const U32 flags); static #else extern char * DPPP_(my_pv_escape)(pTHX_ SV * dsv, char const * const str, const STRLEN count, const STRLEN max, STRLEN * const escaped, const U32 flags); #endif #ifdef pv_escape # undef pv_escape #endif #define pv_escape(a,b,c,d,e,f) DPPP_(my_pv_escape)(aTHX_ a,b,c,d,e,f) #define Perl_pv_escape DPPP_(my_pv_escape) #if defined(NEED_pv_escape) || defined(NEED_pv_escape_GLOBAL) char * DPPP_(my_pv_escape)(pTHX_ SV *dsv, char const * const str, const STRLEN count, const STRLEN max, STRLEN * const escaped, const U32 flags) { const char esc = flags & PERL_PV_ESCAPE_RE ? '%' : '\\'; const char dq = flags & PERL_PV_ESCAPE_QUOTE ? '"' : esc; char octbuf[32] = "%123456789ABCDF"; STRLEN wrote = 0; STRLEN chsize = 0; STRLEN readsize = 1; #if defined(is_utf8_string) && defined(utf8_to_uvchr) bool isuni = flags & PERL_PV_ESCAPE_UNI ? 1 : 0; #endif const char *pv = str; const char * const end = pv + count; octbuf[0] = esc; if (!(flags & PERL_PV_ESCAPE_NOCLEAR)) sv_setpvs(dsv, ""); #if defined(is_utf8_string) && defined(utf8_to_uvchr) if ((flags & PERL_PV_ESCAPE_UNI_DETECT) && is_utf8_string((U8*)pv, count)) isuni = 1; #endif for (; pv < end && (!max || wrote < max) ; pv += readsize) { const UV u = #if defined(is_utf8_string) && defined(utf8_to_uvchr) isuni ? utf8_to_uvchr((U8*)pv, &readsize) : #endif (U8)*pv; const U8 c = (U8)u & 0xFF; if (u > 255 || (flags & PERL_PV_ESCAPE_ALL)) { if (flags & PERL_PV_ESCAPE_FIRSTCHAR) chsize = my_snprintf(octbuf, sizeof octbuf, "%"UVxf, u); else chsize = my_snprintf(octbuf, sizeof octbuf, "%cx{%"UVxf"}", esc, u); } else if (flags & PERL_PV_ESCAPE_NOBACKSLASH) { chsize = 1; } else { if (c == dq || c == esc || !isPRINT(c)) { chsize = 2; switch (c) { case '\\' : /* fallthrough */ case '%' : if (c == esc) octbuf[1] = esc; else chsize = 1; break; case '\v' : octbuf[1] = 'v'; break; case '\t' : octbuf[1] = 't'; break; case '\r' : octbuf[1] = 'r'; break; case '\n' : octbuf[1] = 'n'; break; case '\f' : octbuf[1] = 'f'; break; case '"' : if (dq == '"') octbuf[1] = '"'; else chsize = 1; break; default: chsize = my_snprintf(octbuf, sizeof octbuf, pv < end && isDIGIT((U8)*(pv+readsize)) ? "%c%03o" : "%c%o", esc, c); } } else { chsize = 1; } } if (max && wrote + chsize > max) { break; } else if (chsize > 1) { sv_catpvn(dsv, octbuf, chsize); wrote += chsize; } else { char tmp[2]; my_snprintf(tmp, sizeof tmp, "%c", c); sv_catpvn(dsv, tmp, 1); wrote++; } if (flags & PERL_PV_ESCAPE_FIRSTCHAR) break; } if (escaped != NULL) *escaped= pv - str; return SvPVX(dsv); } #endif #endif #ifndef pv_pretty #if defined(NEED_pv_pretty) static char * DPPP_(my_pv_pretty)(pTHX_ SV * dsv, char const * const str, const STRLEN count, const STRLEN max, char const * const start_color, char const * const end_color, const U32 flags); static #else extern char * DPPP_(my_pv_pretty)(pTHX_ SV * dsv, char const * const str, const STRLEN count, const STRLEN max, char const * const start_color, char const * const end_color, const U32 flags); #endif #ifdef pv_pretty # undef pv_pretty #endif #define pv_pretty(a,b,c,d,e,f,g) DPPP_(my_pv_pretty)(aTHX_ a,b,c,d,e,f,g) #define Perl_pv_pretty DPPP_(my_pv_pretty) #if defined(NEED_pv_pretty) || defined(NEED_pv_pretty_GLOBAL) char * DPPP_(my_pv_pretty)(pTHX_ SV *dsv, char const * const str, const STRLEN count, const STRLEN max, char const * const start_color, char const * const end_color, const U32 flags) { const U8 dq = (flags & PERL_PV_PRETTY_QUOTE) ? '"' : '%'; STRLEN escaped; if (!(flags & PERL_PV_PRETTY_NOCLEAR)) sv_setpvs(dsv, ""); if (dq == '"') sv_catpvs(dsv, "\""); else if (flags & PERL_PV_PRETTY_LTGT) sv_catpvs(dsv, "<"); if (start_color != NULL) sv_catpv(dsv, D_PPP_CONSTPV_ARG(start_color)); pv_escape(dsv, str, count, max, &escaped, flags | PERL_PV_ESCAPE_NOCLEAR); if (end_color != NULL) sv_catpv(dsv, D_PPP_CONSTPV_ARG(end_color)); if (dq == '"') sv_catpvs(dsv, "\""); else if (flags & PERL_PV_PRETTY_LTGT) sv_catpvs(dsv, ">"); if ((flags & PERL_PV_PRETTY_ELLIPSES) && escaped < count) sv_catpvs(dsv, "..."); return SvPVX(dsv); } #endif #endif #ifndef pv_display #if defined(NEED_pv_display) static char * DPPP_(my_pv_display)(pTHX_ SV * dsv, const char * pv, STRLEN cur, STRLEN len, STRLEN pvlim); static #else extern char * DPPP_(my_pv_display)(pTHX_ SV * dsv, const char * pv, STRLEN cur, STRLEN len, STRLEN pvlim); #endif #ifdef pv_display # undef pv_display #endif #define pv_display(a,b,c,d,e) DPPP_(my_pv_display)(aTHX_ a,b,c,d,e) #define Perl_pv_display DPPP_(my_pv_display) #if defined(NEED_pv_display) || defined(NEED_pv_display_GLOBAL) char * DPPP_(my_pv_display)(pTHX_ SV *dsv, const char *pv, STRLEN cur, STRLEN len, STRLEN pvlim) { pv_pretty(dsv, pv, cur, pvlim, NULL, NULL, PERL_PV_PRETTY_DUMP); if (len > cur && pv[cur] == '\0') sv_catpvs(dsv, "\\0"); return SvPVX(dsv); } #endif #endif #endif /* _P_P_PORTABILITY_H_ */ /* End of File ppport.h */ yuma123_2.14/netconf/perl/yangcli/MANIFEST0000664000175000017500000000012314770023131020274 0ustar vladimirvladimirChanges Makefile.PL MANIFEST ppport.h README yangcli.xs t/yangcli.t lib/yangcli.pm yuma123_2.14/netconf/perl/debian/0000775000175000017500000000000014770023131016743 5ustar vladimirvladimiryuma123_2.14/netconf/perl/debian/source/0000775000175000017500000000000014770023131020243 5ustar vladimirvladimiryuma123_2.14/netconf/perl/debian/source/format0000664000175000017500000000001414770023131021451 0ustar vladimirvladimir3.0 (quilt) yuma123_2.14/netconf/perl/debian/control0000664000175000017500000000137514770023131020354 0ustar vladimirvladimirSource: yuma123-perl Section: net Priority: optional Maintainer: Vladimir Vassilev Build-Depends: debhelper (>= 10), autoconf, libyuma-dev (>=2.14), libyangrpc-dev (>=2.14) Standards-Version: 4.1.4 Homepage: https://yuma123.org Package: libyuma-perl Section: perl Architecture: any Depends: ${perl:Depends}, ${shlibs:Depends}, ${misc:Depends}, libxml-libxml-perl, libyuma2 (>=2.2), libyangrpc2 (>=2.12) Description: NETCONF/YANG Perl5 support The NETCONF protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yuma123 Perl5 support files provide the Perl functions for integration of NETCONF/YANG into applications written in Perl. yuma123_2.14/netconf/perl/debian/rules0000775000175000017500000000144114770023131020023 0ustar vladimirvladimir#!/usr/bin/make -f #export DH_VERBOSE=1 # TODO # without -pie build fails during perl module build somehow... export DEB_BUILD_MAINT_OPTIONS := hardening=+all,-pie DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) LIB_VERSION = UPSTREAM_VERSION = $(shell dpkg-parsechangelog | egrep '^Version:' | cut -f 2 -d ':' | sed 's/ //' | sed 's/~dfsg.*$$//') COMPAT_VERSION = $(UPSTREAM_VERSION)~dfsg %: dh $@ override_dh_install: cd yuma; perl Makefile.PL PREFIX=$(CURDIR)/debian/tmp/usr installdirs=vendor; make; make install ; make clean cd yangrpc; perl Makefile.PL PREFIX=$(CURDIR)/debian/tmp/usr installdirs=vendor; make; make install ; make clean cd yangcli; perl Makefile.PL PREFIX=$(CURDIR)/debian/tmp/usr installdirs=vendor; make; make install ; make clean dh_install yuma123_2.14/netconf/perl/debian/copyright0000664000175000017500000000326514770023131020704 0ustar vladimirvladimirFiles: * Copyright: (C) 2016-2025 Vladimir Vassilev Copyright: (C) 2016-2018 Transpacket AS Copyright: (C) 2019-2025 Lightside Instruments AS License: BSD License: BSD Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andy Bierman BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. yuma123_2.14/netconf/perl/debian/compat0000664000175000017500000000000314770023131020142 0ustar vladimirvladimir10 yuma123_2.14/netconf/perl/debian/changelog0000664000175000017500000000024414770023131020615 0ustar vladimirvladimiryuma123-perl (2.14-0) stable; urgency=medium * Unofficial package -- Vladimir Vassilev Sun, 23 Mar 2025 15:25:36 +0100 yuma123_2.14/netconf/perl/yuma/0000775000175000017500000000000014770023131016474 5ustar vladimirvladimiryuma123_2.14/netconf/perl/yuma/t/0000775000175000017500000000000014770023131016737 5ustar vladimirvladimiryuma123_2.14/netconf/perl/yuma/t/yuma.t0000664000175000017500000000074514770023131020105 0ustar vladimirvladimir# Before 'make install' is performed this script should be runnable with # 'make test'. After 'make install' it should work as 'perl yuma.t' ######################### # change 'tests => 1' to 'tests => last_test_to_print'; use strict; use warnings; use Test::More tests => 1; BEGIN { use_ok('yuma') }; ######################### # Insert your test code below, the Test::More module is use()ed here so read # its man page ( perldoc Test::More ) for help writing this test script. yuma123_2.14/netconf/perl/yuma/lib/0000775000175000017500000000000014770023131017242 5ustar vladimirvladimiryuma123_2.14/netconf/perl/yuma/lib/yuma.pm0000664000175000017500000000335214770023131020556 0ustar vladimirvladimirpackage yuma; #use 5.020002; use 5.018002; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use yuma ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( ); our $VERSION = '0.01'; require XSLoader; XSLoader::load('yuma', $VERSION); # Preloaded methods go here. 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME yuma - Perl extension for blah blah blah =head1 SYNOPSIS use yuma; blah blah blah =head1 DESCRIPTION Stub documentation for yuma, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR vladimir, Evladimir@E =head1 COPYRIGHT AND LICENSE Copyright (C) 2016 by vladimir This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.20.2 or, at your option, any later version of Perl 5 you may have available. =cut yuma123_2.14/netconf/perl/yuma/README0000664000175000017500000000220714770023131017355 0ustar vladimirvladimiryuma version 0.01 ================= The README is used to introduce the module and provide instructions on how to install the module, any machine dependencies it may have (for example C compilers and installed libraries) and any other information that should be provided before the module is installed. A README file is required for CPAN modules since CPAN extracts the README file from a module distribution so that people browsing the archive can use it get an idea of the modules uses. It is usually a good idea to provide version information here so that people can decide whether fixes for the module are worth downloading. INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES This module requires these other modules and libraries: blah blah blah COPYRIGHT AND LICENCE Put the correct copyright and licence information here. Copyright (C) 2016 by vladimir This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.20.2 or, at your option, any later version of Perl 5 you may have available. yuma123_2.14/netconf/perl/yuma/Changes0000664000175000017500000000021714770023131017767 0ustar vladimirvladimirRevision history for Perl extension yuma. 0.01 Fri Jul 8 17:21:10 2016 - original version; created by h2xs 1.23 with options -A -n yuma yuma123_2.14/netconf/perl/yuma/Makefile.PL0000664000175000017500000000163414770023131020452 0ustar vladimirvladimiruse 5.018002; use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( NAME => 'yuma', VERSION_FROM => 'lib/yuma.pm', # finds $VERSION, requires EU::MM from perl >= 5.5 PREREQ_PM => {}, # e.g., Module::Name => 1.1 ABSTRACT_FROM => 'lib/yuma.pm', # retrieve abstract from module AUTHOR => 'vladimir ', #LICENSE => 'perl', #Value must be from legacy list of licenses here #http://search.cpan.org/perldoc?Module%3A%3ABuild%3A%3AAPI LIBS => ['-lyumancx'], # e.g., '-lm' DEFINE => '', # e.g., '-DHAVE_SOMETHING' INC => '-I.', # e.g., '-I. -I/usr/include/other' # Un-comment this if you add C files to link with later: # OBJECT => '$(O_FILES)', # link all the C files too ); yuma123_2.14/netconf/perl/yuma/ppport.h0000664000175000017500000055600214770023131020201 0ustar vladimirvladimir#if 0 <<'SKIP'; #endif /* ---------------------------------------------------------------------- ppport.h -- Perl/Pollution/Portability Version 3.21 Automatically created by Devel::PPPort running under perl 5.020002. Do NOT edit this file directly! -- Edit PPPort_pm.PL and the includes in parts/inc/ instead. Use 'perldoc ppport.h' to view the documentation below. ---------------------------------------------------------------------- SKIP =pod =head1 NAME ppport.h - Perl/Pollution/Portability version 3.21 =head1 SYNOPSIS perl ppport.h [options] [source files] Searches current directory for files if no [source files] are given --help show short help --version show version --patch=file write one patch file with changes --copy=suffix write changed copies with suffix --diff=program use diff program and options --compat-version=version provide compatibility with Perl version --cplusplus accept C++ comments --quiet don't output anything except fatal errors --nodiag don't show diagnostics --nohints don't show hints --nochanges don't suggest changes --nofilter don't filter input files --strip strip all script and doc functionality from ppport.h --list-provided list provided API --list-unsupported list unsupported API --api-info=name show Perl API portability information =head1 COMPATIBILITY This version of F is designed to support operation with Perl installations back to 5.003, and has been tested up to 5.11.5. =head1 OPTIONS =head2 --help Display a brief usage summary. =head2 --version Display the version of F. =head2 --patch=I If this option is given, a single patch file will be created if any changes are suggested. This requires a working diff program to be installed on your system. =head2 --copy=I If this option is given, a copy of each file will be saved with the given suffix that contains the suggested changes. This does not require any external programs. Note that this does not automagially add a dot between the original filename and the suffix. If you want the dot, you have to include it in the option argument. If neither C<--patch> or C<--copy> are given, the default is to simply print the diffs for each file. This requires either C or a C program to be installed. =head2 --diff=I Manually set the diff program and options to use. The default is to use C, when installed, and output unified context diffs. =head2 --compat-version=I Tell F to check for compatibility with the given Perl version. The default is to check for compatibility with Perl version 5.003. You can use this option to reduce the output of F if you intend to be backward compatible only down to a certain Perl version. =head2 --cplusplus Usually, F will detect C++ style comments and replace them with C style comments for portability reasons. Using this option instructs F to leave C++ comments untouched. =head2 --quiet Be quiet. Don't print anything except fatal errors. =head2 --nodiag Don't output any diagnostic messages. Only portability alerts will be printed. =head2 --nohints Don't output any hints. Hints often contain useful portability notes. Warnings will still be displayed. =head2 --nochanges Don't suggest any changes. Only give diagnostic output and hints unless these are also deactivated. =head2 --nofilter Don't filter the list of input files. By default, files not looking like source code (i.e. not *.xs, *.c, *.cc, *.cpp or *.h) are skipped. =head2 --strip Strip all script and documentation functionality from F. This reduces the size of F dramatically and may be useful if you want to include F in smaller modules without increasing their distribution size too much. The stripped F will have a C<--unstrip> option that allows you to undo the stripping, but only if an appropriate C module is installed. =head2 --list-provided Lists the API elements for which compatibility is provided by F. Also lists if it must be explicitly requested, if it has dependencies, and if there are hints or warnings for it. =head2 --list-unsupported Lists the API elements that are known not to be supported by F and below which version of Perl they probably won't be available or work. =head2 --api-info=I Show portability information for API elements matching I. If I is surrounded by slashes, it is interpreted as a regular expression. =head1 DESCRIPTION In order for a Perl extension (XS) module to be as portable as possible across differing versions of Perl itself, certain steps need to be taken. =over 4 =item * Including this header is the first major one. This alone will give you access to a large part of the Perl API that hasn't been available in earlier Perl releases. Use perl ppport.h --list-provided to see which API elements are provided by ppport.h. =item * You should avoid using deprecated parts of the API. For example, using global Perl variables without the C prefix is deprecated. Also, some API functions used to have a C prefix. Using this form is also deprecated. You can safely use the supported API, as F will provide wrappers for older Perl versions. =item * If you use one of a few functions or variables that were not present in earlier versions of Perl, and that can't be provided using a macro, you have to explicitly request support for these functions by adding one or more C<#define>s in your source code before the inclusion of F. These functions or variables will be marked C in the list shown by C<--list-provided>. Depending on whether you module has a single or multiple files that use such functions or variables, you want either C or global variants. For a C function or variable (used only in a single source file), use: #define NEED_function #define NEED_variable For a global function or variable (used in multiple source files), use: #define NEED_function_GLOBAL #define NEED_variable_GLOBAL Note that you mustn't have more than one global request for the same function or variable in your project. Function / Variable Static Request Global Request ----------------------------------------------------------------------------------------- PL_parser NEED_PL_parser NEED_PL_parser_GLOBAL PL_signals NEED_PL_signals NEED_PL_signals_GLOBAL eval_pv() NEED_eval_pv NEED_eval_pv_GLOBAL grok_bin() NEED_grok_bin NEED_grok_bin_GLOBAL grok_hex() NEED_grok_hex NEED_grok_hex_GLOBAL grok_number() NEED_grok_number NEED_grok_number_GLOBAL grok_numeric_radix() NEED_grok_numeric_radix NEED_grok_numeric_radix_GLOBAL grok_oct() NEED_grok_oct NEED_grok_oct_GLOBAL load_module() NEED_load_module NEED_load_module_GLOBAL my_snprintf() NEED_my_snprintf NEED_my_snprintf_GLOBAL my_sprintf() NEED_my_sprintf NEED_my_sprintf_GLOBAL my_strlcat() NEED_my_strlcat NEED_my_strlcat_GLOBAL my_strlcpy() NEED_my_strlcpy NEED_my_strlcpy_GLOBAL newCONSTSUB() NEED_newCONSTSUB NEED_newCONSTSUB_GLOBAL newRV_noinc() NEED_newRV_noinc NEED_newRV_noinc_GLOBAL newSV_type() NEED_newSV_type NEED_newSV_type_GLOBAL newSVpvn_flags() NEED_newSVpvn_flags NEED_newSVpvn_flags_GLOBAL newSVpvn_share() NEED_newSVpvn_share NEED_newSVpvn_share_GLOBAL pv_display() NEED_pv_display NEED_pv_display_GLOBAL pv_escape() NEED_pv_escape NEED_pv_escape_GLOBAL pv_pretty() NEED_pv_pretty NEED_pv_pretty_GLOBAL sv_2pv_flags() NEED_sv_2pv_flags NEED_sv_2pv_flags_GLOBAL sv_2pvbyte() NEED_sv_2pvbyte NEED_sv_2pvbyte_GLOBAL sv_catpvf_mg() NEED_sv_catpvf_mg NEED_sv_catpvf_mg_GLOBAL sv_catpvf_mg_nocontext() NEED_sv_catpvf_mg_nocontext NEED_sv_catpvf_mg_nocontext_GLOBAL sv_pvn_force_flags() NEED_sv_pvn_force_flags NEED_sv_pvn_force_flags_GLOBAL sv_setpvf_mg() NEED_sv_setpvf_mg NEED_sv_setpvf_mg_GLOBAL sv_setpvf_mg_nocontext() NEED_sv_setpvf_mg_nocontext NEED_sv_setpvf_mg_nocontext_GLOBAL vload_module() NEED_vload_module NEED_vload_module_GLOBAL vnewSVpvf() NEED_vnewSVpvf NEED_vnewSVpvf_GLOBAL warner() NEED_warner NEED_warner_GLOBAL To avoid namespace conflicts, you can change the namespace of the explicitly exported functions / variables using the C macro. Just C<#define> the macro before including C: #define DPPP_NAMESPACE MyOwnNamespace_ #include "ppport.h" The default namespace is C. =back The good thing is that most of the above can be checked by running F on your source code. See the next section for details. =head1 EXAMPLES To verify whether F is needed for your module, whether you should make any changes to your code, and whether any special defines should be used, F can be run as a Perl script to check your source code. Simply say: perl ppport.h The result will usually be a list of patches suggesting changes that should at least be acceptable, if not necessarily the most efficient solution, or a fix for all possible problems. If you know that your XS module uses features only available in newer Perl releases, if you're aware that it uses C++ comments, and if you want all suggestions as a single patch file, you could use something like this: perl ppport.h --compat-version=5.6.0 --cplusplus --patch=test.diff If you only want your code to be scanned without any suggestions for changes, use: perl ppport.h --nochanges You can specify a different C program or options, using the C<--diff> option: perl ppport.h --diff='diff -C 10' This would output context diffs with 10 lines of context. If you want to create patched copies of your files instead, use: perl ppport.h --copy=.new To display portability information for the C function, use: perl ppport.h --api-info=newSVpvn Since the argument to C<--api-info> can be a regular expression, you can use perl ppport.h --api-info=/_nomg$/ to display portability information for all C<_nomg> functions or perl ppport.h --api-info=/./ to display information for all known API elements. =head1 BUGS If this version of F is causing failure during the compilation of this module, please check if newer versions of either this module or C are available on CPAN before sending a bug report. If F was generated using the latest version of C and is causing failure of this module, please file a bug report using the CPAN Request Tracker at L. Please include the following information: =over 4 =item 1. The complete output from running "perl -V" =item 2. This file. =item 3. The name and version of the module you were trying to build. =item 4. A full log of the build that failed. =item 5. Any other information that you think could be relevant. =back For the latest version of this code, please get the C module from CPAN. =head1 COPYRIGHT Version 3.x, Copyright (c) 2004-2013, Marcus Holland-Moritz. Version 2.x, Copyright (C) 2001, Paul Marquess. Version 1.x, Copyright (C) 1999, Kenneth Albanowski. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO See L. =cut use strict; # Disable broken TRIE-optimization BEGIN { eval '${^RE_TRIE_MAXBUF} = -1' if $] >= 5.009004 && $] <= 5.009005 } my $VERSION = 3.21; my %opt = ( quiet => 0, diag => 1, hints => 1, changes => 1, cplusplus => 0, filter => 1, strip => 0, version => 0, ); my($ppport) = $0 =~ /([\w.]+)$/; my $LF = '(?:\r\n|[\r\n])'; # line feed my $HS = "[ \t]"; # horizontal whitespace # Never use C comments in this file! my $ccs = '/'.'*'; my $cce = '*'.'/'; my $rccs = quotemeta $ccs; my $rcce = quotemeta $cce; eval { require Getopt::Long; Getopt::Long::GetOptions(\%opt, qw( help quiet diag! filter! hints! changes! cplusplus strip version patch=s copy=s diff=s compat-version=s list-provided list-unsupported api-info=s )) or usage(); }; if ($@ and grep /^-/, @ARGV) { usage() if "@ARGV" =~ /^--?h(?:elp)?$/; die "Getopt::Long not found. Please don't use any options.\n"; } if ($opt{version}) { print "This is $0 $VERSION.\n"; exit 0; } usage() if $opt{help}; strip() if $opt{strip}; if (exists $opt{'compat-version'}) { my($r,$v,$s) = eval { parse_version($opt{'compat-version'}) }; if ($@) { die "Invalid version number format: '$opt{'compat-version'}'\n"; } die "Only Perl 5 is supported\n" if $r != 5; die "Invalid version number: $opt{'compat-version'}\n" if $v >= 1000 || $s >= 1000; $opt{'compat-version'} = sprintf "%d.%03d%03d", $r, $v, $s; } else { $opt{'compat-version'} = 5; } my %API = map { /^(\w+)\|([^|]*)\|([^|]*)\|(\w*)$/ ? ( $1 => { ($2 ? ( base => $2 ) : ()), ($3 ? ( todo => $3 ) : ()), (index($4, 'v') >= 0 ? ( varargs => 1 ) : ()), (index($4, 'p') >= 0 ? ( provided => 1 ) : ()), (index($4, 'n') >= 0 ? ( nothxarg => 1 ) : ()), } ) : die "invalid spec: $_" } qw( AvFILLp|5.004050||p AvFILL||| BhkDISABLE||5.019003| BhkENABLE||5.019003| BhkENTRY_set||5.019003| BhkENTRY||| BhkFLAGS||| CALL_BLOCK_HOOKS||| CLASS|||n CPERLscope|5.005000||p CX_CURPAD_SAVE||| CX_CURPAD_SV||| CopFILEAV|5.006000||p CopFILEGV_set|5.006000||p CopFILEGV|5.006000||p CopFILESV|5.006000||p CopFILE_set|5.006000||p CopFILE|5.006000||p CopSTASHPV_set|5.006000||p CopSTASHPV|5.006000||p CopSTASH_eq|5.006000||p CopSTASH_set|5.006000||p CopSTASH|5.006000||p CopyD|5.009002|5.004050|p Copy||5.004050| CvPADLIST||5.008001| CvSTASH||| CvWEAKOUTSIDE||| DEFSV_set|5.010001||p DEFSV|5.004050||p END_EXTERN_C|5.005000||p ENTER||| ERRSV|5.004050||p EXTEND||| EXTERN_C|5.005000||p F0convert|||n FREETMPS||| GIMME_V||5.004000|n GIMME|||n GROK_NUMERIC_RADIX|5.007002||p G_ARRAY||| G_DISCARD||| G_EVAL||| G_METHOD|5.006001||p G_NOARGS||| G_SCALAR||| G_VOID||5.004000| GetVars||| GvAV||| GvCV||| GvHV||| GvSVn|5.009003||p GvSV||| Gv_AMupdate||5.011000| HEf_SVKEY||5.004000| HeHASH||5.004000| HeKEY||5.004000| HeKLEN||5.004000| HePV||5.004000| HeSVKEY_force||5.004000| HeSVKEY_set||5.004000| HeSVKEY||5.004000| HeUTF8||5.010001| HeVAL||5.004000| HvENAMELEN||5.015004| HvENAMEUTF8||5.015004| HvENAME||5.013007| HvNAMELEN_get|5.009003||p HvNAMELEN||5.015004| HvNAMEUTF8||5.015004| HvNAME_get|5.009003||p HvNAME||| INT2PTR|5.006000||p IN_LOCALE_COMPILETIME|5.007002||p IN_LOCALE_RUNTIME|5.007002||p IN_LOCALE|5.007002||p IN_PERL_COMPILETIME|5.008001||p IS_NUMBER_GREATER_THAN_UV_MAX|5.007002||p IS_NUMBER_INFINITY|5.007002||p IS_NUMBER_IN_UV|5.007002||p IS_NUMBER_NAN|5.007003||p IS_NUMBER_NEG|5.007002||p IS_NUMBER_NOT_INT|5.007002||p IVSIZE|5.006000||p IVTYPE|5.006000||p IVdf|5.006000||p LEAVE||| LINKLIST||5.013006| LVRET||| MARK||| MULTICALL||5.019003| MY_CXT_CLONE|5.009002||p MY_CXT_INIT|5.007003||p MY_CXT|5.007003||p MoveD|5.009002|5.004050|p Move||5.004050| NOOP|5.005000||p NUM2PTR|5.006000||p NVTYPE|5.006000||p NVef|5.006001||p NVff|5.006001||p NVgf|5.006001||p Newxc|5.009003||p Newxz|5.009003||p Newx|5.009003||p Nullav||| Nullch||| Nullcv||| Nullhv||| Nullsv||| OP_CLASS||5.013007| OP_DESC||5.007003| OP_NAME||5.007003| ORIGMARK||| PAD_BASE_SV||| PAD_CLONE_VARS||| PAD_COMPNAME_FLAGS||| PAD_COMPNAME_GEN_set||| PAD_COMPNAME_GEN||| PAD_COMPNAME_OURSTASH||| PAD_COMPNAME_PV||| PAD_COMPNAME_TYPE||| PAD_RESTORE_LOCAL||| PAD_SAVE_LOCAL||| PAD_SAVE_SETNULLPAD||| PAD_SETSV||| PAD_SET_CUR_NOSAVE||| PAD_SET_CUR||| PAD_SVl||| PAD_SV||| PERLIO_FUNCS_CAST|5.009003||p PERLIO_FUNCS_DECL|5.009003||p PERL_ABS|5.008001||p PERL_BCDVERSION|5.019002||p PERL_GCC_BRACE_GROUPS_FORBIDDEN|5.008001||p PERL_HASH|5.004000||p PERL_INT_MAX|5.004000||p PERL_INT_MIN|5.004000||p PERL_LONG_MAX|5.004000||p PERL_LONG_MIN|5.004000||p PERL_MAGIC_arylen|5.007002||p PERL_MAGIC_backref|5.007002||p PERL_MAGIC_bm|5.007002||p PERL_MAGIC_collxfrm|5.007002||p PERL_MAGIC_dbfile|5.007002||p PERL_MAGIC_dbline|5.007002||p PERL_MAGIC_defelem|5.007002||p PERL_MAGIC_envelem|5.007002||p PERL_MAGIC_env|5.007002||p PERL_MAGIC_ext|5.007002||p PERL_MAGIC_fm|5.007002||p PERL_MAGIC_glob|5.019002||p PERL_MAGIC_isaelem|5.007002||p PERL_MAGIC_isa|5.007002||p PERL_MAGIC_mutex|5.019002||p PERL_MAGIC_nkeys|5.007002||p PERL_MAGIC_overload_elem|5.019002||p PERL_MAGIC_overload_table|5.007002||p PERL_MAGIC_overload|5.019002||p PERL_MAGIC_pos|5.007002||p PERL_MAGIC_qr|5.007002||p PERL_MAGIC_regdata|5.007002||p PERL_MAGIC_regdatum|5.007002||p PERL_MAGIC_regex_global|5.007002||p PERL_MAGIC_shared_scalar|5.007003||p PERL_MAGIC_shared|5.007003||p PERL_MAGIC_sigelem|5.007002||p PERL_MAGIC_sig|5.007002||p PERL_MAGIC_substr|5.007002||p PERL_MAGIC_sv|5.007002||p PERL_MAGIC_taint|5.007002||p PERL_MAGIC_tiedelem|5.007002||p PERL_MAGIC_tiedscalar|5.007002||p PERL_MAGIC_tied|5.007002||p PERL_MAGIC_utf8|5.008001||p PERL_MAGIC_uvar_elem|5.007003||p PERL_MAGIC_uvar|5.007002||p PERL_MAGIC_vec|5.007002||p PERL_MAGIC_vstring|5.008001||p PERL_PV_ESCAPE_ALL|5.009004||p PERL_PV_ESCAPE_FIRSTCHAR|5.009004||p PERL_PV_ESCAPE_NOBACKSLASH|5.009004||p PERL_PV_ESCAPE_NOCLEAR|5.009004||p PERL_PV_ESCAPE_QUOTE|5.009004||p PERL_PV_ESCAPE_RE|5.009005||p PERL_PV_ESCAPE_UNI_DETECT|5.009004||p PERL_PV_ESCAPE_UNI|5.009004||p PERL_PV_PRETTY_DUMP|5.009004||p PERL_PV_PRETTY_ELLIPSES|5.010000||p PERL_PV_PRETTY_LTGT|5.009004||p PERL_PV_PRETTY_NOCLEAR|5.010000||p PERL_PV_PRETTY_QUOTE|5.009004||p PERL_PV_PRETTY_REGPROP|5.009004||p PERL_QUAD_MAX|5.004000||p PERL_QUAD_MIN|5.004000||p PERL_REVISION|5.006000||p PERL_SCAN_ALLOW_UNDERSCORES|5.007003||p PERL_SCAN_DISALLOW_PREFIX|5.007003||p PERL_SCAN_GREATER_THAN_UV_MAX|5.007003||p PERL_SCAN_SILENT_ILLDIGIT|5.008001||p PERL_SHORT_MAX|5.004000||p PERL_SHORT_MIN|5.004000||p PERL_SIGNALS_UNSAFE_FLAG|5.008001||p PERL_SUBVERSION|5.006000||p PERL_SYS_INIT3||5.010000| PERL_SYS_INIT||5.010000| PERL_SYS_TERM||5.019003| PERL_UCHAR_MAX|5.004000||p PERL_UCHAR_MIN|5.004000||p PERL_UINT_MAX|5.004000||p PERL_UINT_MIN|5.004000||p PERL_ULONG_MAX|5.004000||p PERL_ULONG_MIN|5.004000||p PERL_UNUSED_ARG|5.009003||p PERL_UNUSED_CONTEXT|5.009004||p PERL_UNUSED_DECL|5.007002||p PERL_UNUSED_VAR|5.007002||p PERL_UQUAD_MAX|5.004000||p PERL_UQUAD_MIN|5.004000||p PERL_USE_GCC_BRACE_GROUPS|5.009004||p PERL_USHORT_MAX|5.004000||p PERL_USHORT_MIN|5.004000||p PERL_VERSION|5.006000||p PL_DBsignal|5.005000||p PL_DBsingle|||pn PL_DBsub|||pn PL_DBtrace|||pn PL_Sv|5.005000||p PL_bufend|5.019002||p PL_bufptr|5.019002||p PL_check||5.006000| PL_compiling|5.004050||p PL_comppad_name||5.017004| PL_comppad||5.008001| PL_copline|5.019002||p PL_curcop|5.004050||p PL_curpad||5.005000| PL_curstash|5.004050||p PL_debstash|5.004050||p PL_defgv|5.004050||p PL_diehook|5.004050||p PL_dirty|5.004050||p PL_dowarn|||pn PL_errgv|5.004050||p PL_error_count|5.019002||p PL_expect|5.019002||p PL_hexdigit|5.005000||p PL_hints|5.005000||p PL_in_my_stash|5.019002||p PL_in_my|5.019002||p PL_keyword_plugin||5.011002| PL_last_in_gv|||n PL_laststatval|5.005000||p PL_lex_state|5.019002||p PL_lex_stuff|5.019002||p PL_linestr|5.019002||p PL_modglobal||5.005000|n PL_na|5.004050||pn PL_no_modify|5.006000||p PL_ofsgv|||n PL_opfreehook||5.011000|n PL_parser|5.009005|5.009005|p PL_peepp||5.007003|n PL_perl_destruct_level|5.004050||p PL_perldb|5.004050||p PL_ppaddr|5.006000||p PL_rpeepp||5.013005|n PL_rsfp_filters|5.019002||p PL_rsfp|5.019002||p PL_rs|||n PL_signals|5.008001||p PL_stack_base|5.004050||p PL_stack_sp|5.004050||p PL_statcache|5.005000||p PL_stdingv|5.004050||p PL_sv_arenaroot|5.004050||p PL_sv_no|5.004050||pn PL_sv_undef|5.004050||pn PL_sv_yes|5.004050||pn PL_tainted|5.004050||p PL_tainting|5.004050||p PL_tokenbuf|5.019002||p POP_MULTICALL||5.019003| POPi|||n POPl|||n POPn|||n POPpbytex||5.007001|n POPpx||5.005030|n POPp|||n POPs|||n PTR2IV|5.006000||p PTR2NV|5.006000||p PTR2UV|5.006000||p PTR2nat|5.009003||p PTR2ul|5.007001||p PTRV|5.006000||p PUSHMARK||| PUSH_MULTICALL||5.019003| PUSHi||| PUSHmortal|5.009002||p PUSHn||| PUSHp||| PUSHs||| PUSHu|5.004000||p PUTBACK||| PadARRAY||5.019003| PadMAX||5.019003| PadlistARRAY||5.019003| PadlistMAX||5.019003| PadlistNAMESARRAY||5.019003| PadlistNAMESMAX||5.019003| PadlistNAMES||5.019003| PadlistREFCNT||5.017004| PadnameIsOUR||| PadnameIsSTATE||| PadnameLEN||5.019003| PadnameOURSTASH||| PadnameOUTER||| PadnamePV||5.019003| PadnameSV||5.019003| PadnameTYPE||| PadnameUTF8||5.019003| PadnamelistARRAY||5.019003| PadnamelistMAX||5.019003| PerlIO_clearerr||5.007003| PerlIO_close||5.007003| PerlIO_context_layers||5.009004| PerlIO_eof||5.007003| PerlIO_error||5.007003| PerlIO_fileno||5.007003| PerlIO_fill||5.007003| PerlIO_flush||5.007003| PerlIO_get_base||5.007003| PerlIO_get_bufsiz||5.007003| PerlIO_get_cnt||5.007003| PerlIO_get_ptr||5.007003| PerlIO_read||5.007003| PerlIO_seek||5.007003| PerlIO_set_cnt||5.007003| PerlIO_set_ptrcnt||5.007003| PerlIO_setlinebuf||5.007003| PerlIO_stderr||5.007003| PerlIO_stdin||5.007003| PerlIO_stdout||5.007003| PerlIO_tell||5.007003| PerlIO_unread||5.007003| PerlIO_write||5.007003| Perl_signbit||5.009005|n PoisonFree|5.009004||p PoisonNew|5.009004||p PoisonWith|5.009004||p Poison|5.008000||p READ_XDIGIT||5.017006| RETVAL|||n Renewc||| Renew||| SAVECLEARSV||| SAVECOMPPAD||| SAVEPADSV||| SAVETMPS||| SAVE_DEFSV|5.004050||p SPAGAIN||| SP||| START_EXTERN_C|5.005000||p START_MY_CXT|5.007003||p STMT_END|||p STMT_START|||p STR_WITH_LEN|5.009003||p ST||| SV_CONST_RETURN|5.009003||p SV_COW_DROP_PV|5.008001||p SV_COW_SHARED_HASH_KEYS|5.009005||p SV_GMAGIC|5.007002||p SV_HAS_TRAILING_NUL|5.009004||p SV_IMMEDIATE_UNREF|5.007001||p SV_MUTABLE_RETURN|5.009003||p SV_NOSTEAL|5.009002||p SV_SMAGIC|5.009003||p SV_UTF8_NO_ENCODING|5.008001||p SVfARG|5.009005||p SVf_UTF8|5.006000||p SVf|5.006000||p SVt_INVLIST||5.019002| SVt_IV||| SVt_NULL||| SVt_NV||| SVt_PVAV||| SVt_PVCV||| SVt_PVFM||| SVt_PVGV||| SVt_PVHV||| SVt_PVIO||| SVt_PVIV||| SVt_PVLV||| SVt_PVMG||| SVt_PVNV||| SVt_PV||| SVt_REGEXP||5.011000| Safefree||| Slab_Alloc||| Slab_Free||| Slab_to_ro||| Slab_to_rw||| StructCopy||| SvCUR_set||| SvCUR||| SvEND||| SvGAMAGIC||5.006001| SvGETMAGIC|5.004050||p SvGROW||| SvIOK_UV||5.006000| SvIOK_notUV||5.006000| SvIOK_off||| SvIOK_only_UV||5.006000| SvIOK_only||| SvIOK_on||| SvIOKp||| SvIOK||| SvIVX||| SvIV_nomg|5.009001||p SvIV_set||| SvIVx||| SvIV||| SvIsCOW_shared_hash||5.008003| SvIsCOW||5.008003| SvLEN_set||| SvLEN||| SvLOCK||5.007003| SvMAGIC_set|5.009003||p SvNIOK_off||| SvNIOKp||| SvNIOK||| SvNOK_off||| SvNOK_only||| SvNOK_on||| SvNOKp||| SvNOK||| SvNVX||| SvNV_nomg||5.013002| SvNV_set||| SvNVx||| SvNV||| SvOK||| SvOOK_offset||5.011000| SvOOK||| SvPOK_off||| SvPOK_only_UTF8||5.006000| SvPOK_only||| SvPOK_on||| SvPOKp||| SvPOK||| SvPVX_const|5.009003||p SvPVX_mutable|5.009003||p SvPVX||| SvPV_const|5.009003||p SvPV_flags_const_nolen|5.009003||p SvPV_flags_const|5.009003||p SvPV_flags_mutable|5.009003||p SvPV_flags|5.007002||p SvPV_force_flags_mutable|5.009003||p SvPV_force_flags_nolen|5.009003||p SvPV_force_flags|5.007002||p SvPV_force_mutable|5.009003||p SvPV_force_nolen|5.009003||p SvPV_force_nomg_nolen|5.009003||p SvPV_force_nomg|5.007002||p SvPV_force|||p SvPV_mutable|5.009003||p SvPV_nolen_const|5.009003||p SvPV_nolen|5.006000||p SvPV_nomg_const_nolen|5.009003||p SvPV_nomg_const|5.009003||p SvPV_nomg_nolen|5.013007||p SvPV_nomg|5.007002||p SvPV_renew|5.009003||p SvPV_set||| SvPVbyte_force||5.009002| SvPVbyte_nolen||5.006000| SvPVbytex_force||5.006000| SvPVbytex||5.006000| SvPVbyte|5.006000||p SvPVutf8_force||5.006000| SvPVutf8_nolen||5.006000| SvPVutf8x_force||5.006000| SvPVutf8x||5.006000| SvPVutf8||5.006000| SvPVx||| SvPV||| SvREFCNT_dec_NN||5.017007| SvREFCNT_dec||| SvREFCNT_inc_NN|5.009004||p SvREFCNT_inc_simple_NN|5.009004||p SvREFCNT_inc_simple_void_NN|5.009004||p SvREFCNT_inc_simple_void|5.009004||p SvREFCNT_inc_simple|5.009004||p SvREFCNT_inc_void_NN|5.009004||p SvREFCNT_inc_void|5.009004||p SvREFCNT_inc|||p SvREFCNT||| SvROK_off||| SvROK_on||| SvROK||| SvRV_set|5.009003||p SvRV||| SvRXOK||5.009005| SvRX||5.009005| SvSETMAGIC||| SvSHARED_HASH|5.009003||p SvSHARE||5.007003| SvSTASH_set|5.009003||p SvSTASH||| SvSetMagicSV_nosteal||5.004000| SvSetMagicSV||5.004000| SvSetSV_nosteal||5.004000| SvSetSV||| SvTAINTED_off||5.004000| SvTAINTED_on||5.004000| SvTAINTED||5.004000| SvTAINT||| SvTHINKFIRST||| SvTRUE_nomg||5.013006| SvTRUE||| SvTYPE||| SvUNLOCK||5.007003| SvUOK|5.007001|5.006000|p SvUPGRADE||| SvUTF8_off||5.006000| SvUTF8_on||5.006000| SvUTF8||5.006000| SvUVXx|5.004000||p SvUVX|5.004000||p SvUV_nomg|5.009001||p SvUV_set|5.009003||p SvUVx|5.004000||p SvUV|5.004000||p SvVOK||5.008001| SvVSTRING_mg|5.009004||p THIS|||n UNDERBAR|5.009002||p UTF8_MAXBYTES|5.009002||p UVSIZE|5.006000||p UVTYPE|5.006000||p UVXf|5.007001||p UVof|5.006000||p UVuf|5.006000||p UVxf|5.006000||p WARN_ALL|5.006000||p WARN_AMBIGUOUS|5.006000||p WARN_ASSERTIONS|5.019002||p WARN_BAREWORD|5.006000||p WARN_CLOSED|5.006000||p WARN_CLOSURE|5.006000||p WARN_DEBUGGING|5.006000||p WARN_DEPRECATED|5.006000||p WARN_DIGIT|5.006000||p WARN_EXEC|5.006000||p WARN_EXITING|5.006000||p WARN_GLOB|5.006000||p WARN_INPLACE|5.006000||p WARN_INTERNAL|5.006000||p WARN_IO|5.006000||p WARN_LAYER|5.008000||p WARN_MALLOC|5.006000||p WARN_MISC|5.006000||p WARN_NEWLINE|5.006000||p WARN_NUMERIC|5.006000||p WARN_ONCE|5.006000||p WARN_OVERFLOW|5.006000||p WARN_PACK|5.006000||p WARN_PARENTHESIS|5.006000||p WARN_PIPE|5.006000||p WARN_PORTABLE|5.006000||p WARN_PRECEDENCE|5.006000||p WARN_PRINTF|5.006000||p WARN_PROTOTYPE|5.006000||p WARN_QW|5.006000||p WARN_RECURSION|5.006000||p WARN_REDEFINE|5.006000||p WARN_REGEXP|5.006000||p WARN_RESERVED|5.006000||p WARN_SEMICOLON|5.006000||p WARN_SEVERE|5.006000||p WARN_SIGNAL|5.006000||p WARN_SUBSTR|5.006000||p WARN_SYNTAX|5.006000||p WARN_TAINT|5.006000||p WARN_THREADS|5.008000||p WARN_UNINITIALIZED|5.006000||p WARN_UNOPENED|5.006000||p WARN_UNPACK|5.006000||p WARN_UNTIE|5.006000||p WARN_UTF8|5.006000||p WARN_VOID|5.006000||p WIDEST_UTYPE|5.015004||p XCPT_CATCH|5.009002||p XCPT_RETHROW|5.009002|5.007001|p XCPT_TRY_END|5.009002|5.004000|p XCPT_TRY_START|5.009002|5.004000|p XPUSHi||| XPUSHmortal|5.009002||p XPUSHn||| XPUSHp||| XPUSHs||| XPUSHu|5.004000||p XSPROTO|5.010000||p XSRETURN_EMPTY||| XSRETURN_IV||| XSRETURN_NO||| XSRETURN_NV||| XSRETURN_PV||| XSRETURN_UNDEF||| XSRETURN_UV|5.008001||p XSRETURN_YES||| XSRETURN|||p XST_mIV||| XST_mNO||| XST_mNV||| XST_mPV||| XST_mUNDEF||| XST_mUV|5.008001||p XST_mYES||| XS_APIVERSION_BOOTCHECK||5.013004| XS_EXTERNAL||5.019003| XS_INTERNAL||5.019003| XS_VERSION_BOOTCHECK||| XS_VERSION||| XSprePUSH|5.006000||p XS||| XopDISABLE||5.019003| XopENABLE||5.019003| XopENTRY_set||5.019003| XopENTRY||5.019003| XopFLAGS||5.013007| ZeroD|5.009002||p Zero||| _aMY_CXT|5.007003||p _add_range_to_invlist||| _append_range_to_invlist||| _core_swash_init||| _get_swash_invlist||| _invlist_array_init||| _invlist_contains_cp||| _invlist_contents||| _invlist_dump||| _invlist_intersection_maybe_complement_2nd||| _invlist_intersection||| _invlist_invert_prop||| _invlist_invert||| _invlist_len||| _invlist_populate_swatch||| _invlist_search||| _invlist_subtract||| _invlist_union_maybe_complement_2nd||| _invlist_union||| _is_uni_FOO||5.017008| _is_uni_perl_idcont||5.017008| _is_uni_perl_idstart||5.017007| _is_utf8_FOO||5.017008| _is_utf8_mark||5.017008| _is_utf8_perl_idcont||5.017008| _is_utf8_perl_idstart||5.017007| _new_invlist_C_array||| _new_invlist||| _pMY_CXT|5.007003||p _swash_inversion_hash||| _swash_to_invlist||| _to_fold_latin1||| _to_uni_fold_flags||5.013011| _to_upper_title_latin1||| _to_utf8_fold_flags||5.015006| _to_utf8_lower_flags||5.015006| _to_utf8_title_flags||5.015006| _to_utf8_upper_flags||5.015006| aMY_CXT_|5.007003||p aMY_CXT|5.007003||p aTHXR_|5.019002||p aTHXR|5.019002||p aTHX_|5.006000||p aTHX|5.006000||p aassign_common_vars||| add_cp_to_invlist||| add_data|||n add_utf16_textfilter||| addmad||| adjust_size_and_find_bucket|||n adjust_stack_on_leave||| alloc_maybe_populate_EXACT||| alloccopstash||| allocmy||| amagic_call||| amagic_cmp_locale||| amagic_cmp||| amagic_deref_call||5.013007| amagic_i_ncmp||| amagic_is_enabled||| amagic_ncmp||| anonymise_cv_maybe||| any_dup||| ao||| append_madprops||| apply_attrs_my||| apply_attrs_string||5.006001| apply_attrs||| apply||| assert_uft8_cache_coherent||| atfork_lock||5.007003|n atfork_unlock||5.007003|n av_arylen_p||5.009003| av_clear||| av_create_and_push||5.009005| av_create_and_unshift_one||5.009005| av_delete||5.006000| av_exists||5.006000| av_extend_guts||| av_extend||| av_fetch||| av_fill||| av_iter_p||5.011000| av_len||| av_make||| av_pop||| av_push||| av_reify||| av_shift||| av_store||| av_tindex||5.017009| av_top_index||5.017009| av_undef||| av_unshift||| ax|||n bad_type_gv||| bad_type_pv||| bind_match||| block_end||| block_gimme||5.004000| block_start||| blockhook_register||5.013003| boolSV|5.004000||p boot_core_PerlIO||| boot_core_UNIVERSAL||| boot_core_mro||| bytes_cmp_utf8||5.013007| bytes_from_utf8||5.007001| bytes_to_uni|||n bytes_to_utf8||5.006001| call_argv|5.006000||p call_atexit||5.006000| call_list||5.004000| call_method|5.006000||p call_pv|5.006000||p call_sv|5.006000||p caller_cx||5.013005| calloc||5.007002|n cando||| cast_i32||5.006000| cast_iv||5.006000| cast_ulong||5.006000| cast_uv||5.006000| check_locale_boundary_crossing||| check_type_and_open||| check_uni||| check_utf8_print||| checkcomma||| ckWARN|5.006000||p ck_entersub_args_core||| ck_entersub_args_list||5.013006| ck_entersub_args_proto_or_list||5.013006| ck_entersub_args_proto||5.013006| ck_warner_d||5.011001|v ck_warner||5.011001|v ckwarn_common||| ckwarn_d||5.009003| ckwarn||5.009003| cl_and|||n cl_anything|||n cl_init|||n cl_is_anything|||n cl_or|||n clear_placeholders||| clone_params_del|||n clone_params_new|||n closest_cop||| compute_EXACTish||| convert||| cop_fetch_label||5.015001| cop_free||| cop_hints_2hv||5.013007| cop_hints_fetch_pvn||5.013007| cop_hints_fetch_pvs||5.013007| cop_hints_fetch_pv||5.013007| cop_hints_fetch_sv||5.013007| cop_store_label||5.015001| cophh_2hv||5.013007| cophh_copy||5.013007| cophh_delete_pvn||5.013007| cophh_delete_pvs||5.013007| cophh_delete_pv||5.013007| cophh_delete_sv||5.013007| cophh_fetch_pvn||5.013007| cophh_fetch_pvs||5.013007| cophh_fetch_pv||5.013007| cophh_fetch_sv||5.013007| cophh_free||5.013007| cophh_new_empty||5.019003| cophh_store_pvn||5.013007| cophh_store_pvs||5.013007| cophh_store_pv||5.013007| cophh_store_sv||5.013007| core_prototype||| core_regclass_swash||| coresub_op||| could_it_be_a_POSIX_class||| cr_textfilter||| create_eval_scope||| croak_memory_wrap||5.019003|n croak_no_mem|||n croak_no_modify||5.013003|n croak_nocontext|||vn croak_popstack|||n croak_sv||5.013001| croak_xs_usage||5.010001|n croak|||v csighandler||5.009003|n curmad||| current_re_engine||| curse||| custom_op_desc||5.007003| custom_op_name||5.007003| custom_op_register||5.013007| custom_op_xop||5.013007| cv_ckproto_len_flags||| cv_clone_into||| cv_clone||| cv_const_sv_or_av||| cv_const_sv||5.004000| cv_dump||| cv_forget_slab||| cv_get_call_checker||5.013006| cv_set_call_checker||5.013006| cv_undef||| cvgv_set||| cvstash_set||| cx_dump||5.005000| cx_dup||| cxinc||| dAXMARK|5.009003||p dAX|5.007002||p dITEMS|5.007002||p dMARK||| dMULTICALL||5.009003| dMY_CXT_SV|5.007003||p dMY_CXT|5.007003||p dNOOP|5.006000||p dORIGMARK||| dSP||| dTHR|5.004050||p dTHXR|5.019002||p dTHXa|5.006000||p dTHXoa|5.006000||p dTHX|5.006000||p dUNDERBAR|5.009002||p dVAR|5.009003||p dXCPT|5.009002||p dXSARGS||| dXSI32||| dXSTARG|5.006000||p deb_curcv||| deb_nocontext|||vn deb_stack_all||| deb_stack_n||| debop||5.005000| debprofdump||5.005000| debprof||| debstackptrs||5.007003| debstack||5.007003| debug_start_match||| deb||5.007003|v defelem_target||| del_sv||| delete_eval_scope||| delimcpy||5.004000|n deprecate_commaless_var_list||| despatch_signals||5.007001| destroy_matcher||| die_nocontext|||vn die_sv||5.013001| die_unwind||| die|||v dirp_dup||| div128||| djSP||| do_aexec5||| do_aexec||| do_aspawn||| do_binmode||5.004050| do_chomp||| do_close||| do_delete_local||| do_dump_pad||| do_eof||| do_exec3||| do_execfree||| do_exec||| do_gv_dump||5.006000| do_gvgv_dump||5.006000| do_hv_dump||5.006000| do_ipcctl||| do_ipcget||| do_join||| do_magic_dump||5.006000| do_msgrcv||| do_msgsnd||| do_ncmp||| do_oddball||| do_op_dump||5.006000| do_op_xmldump||| do_open9||5.006000| do_openn||5.007001| do_open||5.004000| do_pmop_dump||5.006000| do_pmop_xmldump||| do_print||| do_readline||| do_seek||| do_semop||| do_shmio||| do_smartmatch||| do_spawn_nowait||| do_spawn||| do_sprintf||| do_sv_dump||5.006000| do_sysseek||| do_tell||| do_trans_complex_utf8||| do_trans_complex||| do_trans_count_utf8||| do_trans_count||| do_trans_simple_utf8||| do_trans_simple||| do_trans||| do_vecget||| do_vecset||| do_vop||| docatch||| doeval||| dofile||| dofindlabel||| doform||| doing_taint||5.008001|n dooneliner||| doopen_pm||| doparseform||| dopoptoeval||| dopoptogiven||| dopoptolabel||| dopoptoloop||| dopoptosub_at||| dopoptowhen||| doref||5.009003| dounwind||| dowantarray||| dump_all_perl||| dump_all||5.006000| dump_eval||5.006000| dump_exec_pos||| dump_fds||| dump_form||5.006000| dump_indent||5.006000|v dump_mstats||| dump_packsubs_perl||| dump_packsubs||5.006000| dump_sub_perl||| dump_sub||5.006000| dump_sv_child||| dump_trie_interim_list||| dump_trie_interim_table||| dump_trie||| dump_vindent||5.006000| dumpuntil||| dup_attrlist||| emulate_cop_io||| eval_pv|5.006000||p eval_sv|5.006000||p exec_failed||| expect_number||| fbm_compile||5.005000| fbm_instr||5.005000| feature_is_enabled||| filter_add||| filter_del||| filter_gets||| filter_read||| finalize_optree||| finalize_op||| find_and_forget_pmops||| find_array_subscript||| find_beginning||| find_byclass||| find_hash_subscript||| find_in_my_stash||| find_lexical_cv||| find_runcv_where||| find_runcv||5.008001| find_rundefsv2||| find_rundefsvoffset||5.009002| find_rundefsv||5.013002| find_script||| find_uninit_var||| first_symbol|||n foldEQ_latin1||5.013008|n foldEQ_locale||5.013002|n foldEQ_utf8_flags||5.013010| foldEQ_utf8||5.013002| foldEQ||5.013002|n fold_constants||| forbid_setid||| force_ident_maybe_lex||| force_ident||| force_list||| force_next||| force_strict_version||| force_version||| force_word||| forget_pmop||| form_nocontext|||vn form_short_octal_warning||| form||5.004000|v fp_dup||| fprintf_nocontext|||vn free_global_struct||| free_tied_hv_pool||| free_tmps||| gen_constant_list||| get_and_check_backslash_N_name||| get_aux_mg||| get_av|5.006000||p get_context||5.006000|n get_cvn_flags|5.009005||p get_cvs|5.011000||p get_cv|5.006000||p get_db_sub||| get_debug_opts||| get_hash_seed||| get_hv|5.006000||p get_invlist_iter_addr||| get_invlist_offset_addr||| get_invlist_previous_index_addr||| get_mstats||| get_no_modify||| get_num||| get_op_descs||5.005000| get_op_names||5.005000| get_opargs||| get_ppaddr||5.006000| get_re_arg||| get_sv|5.006000||p get_vtbl||5.005030| getcwd_sv||5.007002| getenv_len||| glob_2number||| glob_assign_glob||| glob_assign_ref||| gp_dup||| gp_free||| gp_ref||| grok_bin|5.007003||p grok_bslash_N||| grok_bslash_c||| grok_bslash_o||| grok_bslash_x||| grok_hex|5.007003||p grok_number|5.007002||p grok_numeric_radix|5.007002||p grok_oct|5.007003||p group_end||| gv_AVadd||| gv_HVadd||| gv_IOadd||| gv_SVadd||| gv_add_by_type||5.011000| gv_autoload4||5.004000| gv_autoload_pvn||5.015004| gv_autoload_pv||5.015004| gv_autoload_sv||5.015004| gv_check||| gv_const_sv||5.009003| gv_dump||5.006000| gv_efullname3||5.004000| gv_efullname4||5.006001| gv_efullname||| gv_ename||| gv_fetchfile_flags||5.009005| gv_fetchfile||| gv_fetchmeth_autoload||5.007003| gv_fetchmeth_pv_autoload||5.015004| gv_fetchmeth_pvn_autoload||5.015004| gv_fetchmeth_pvn||5.015004| gv_fetchmeth_pv||5.015004| gv_fetchmeth_sv_autoload||5.015004| gv_fetchmeth_sv||5.015004| gv_fetchmethod_autoload||5.004000| gv_fetchmethod_pv_flags||5.015004| gv_fetchmethod_pvn_flags||5.015004| gv_fetchmethod_sv_flags||5.015004| gv_fetchmethod||| gv_fetchmeth||| gv_fetchpvn_flags|5.009002||p gv_fetchpvs|5.009004||p gv_fetchpv||| gv_fetchsv|5.009002||p gv_fullname3||5.004000| gv_fullname4||5.006001| gv_fullname||| gv_handler||5.007001| gv_init_pvn||5.015004| gv_init_pv||5.015004| gv_init_svtype||| gv_init_sv||5.015004| gv_init||| gv_magicalize_isa||| gv_name_set||5.009004| gv_stashpvn|5.004000||p gv_stashpvs|5.009003||p gv_stashpv||| gv_stashsv||| gv_try_downgrade||| handle_regex_sets||| he_dup||| hek_dup||| hfree_next_entry||| hfreeentries||| hsplit||| hv_assert||| hv_auxinit||| hv_backreferences_p||| hv_clear_placeholders||5.009001| hv_clear||| hv_common_key_len||5.010000| hv_common||5.010000| hv_copy_hints_hv||5.009004| hv_delayfree_ent||5.004000| hv_delete_common||| hv_delete_ent||5.004000| hv_delete||| hv_eiter_p||5.009003| hv_eiter_set||5.009003| hv_ename_add||| hv_ename_delete||| hv_exists_ent||5.004000| hv_exists||| hv_fetch_ent||5.004000| hv_fetchs|5.009003||p hv_fetch||| hv_fill||5.013002| hv_free_ent_ret||| hv_free_ent||5.004000| hv_iterinit||| hv_iterkeysv||5.004000| hv_iterkey||| hv_iternext_flags||5.008000| hv_iternextsv||| hv_iternext||| hv_iterval||| hv_kill_backrefs||| hv_ksplit||5.004000| hv_magic_check|||n hv_magic||| hv_name_set||5.009003| hv_notallowed||| hv_placeholders_get||5.009003| hv_placeholders_p||| hv_placeholders_set||5.009003| hv_rand_set||5.017011| hv_riter_p||5.009003| hv_riter_set||5.009003| hv_scalar||5.009001| hv_store_ent||5.004000| hv_store_flags||5.008000| hv_stores|5.009004||p hv_store||| hv_undef_flags||| hv_undef||| ibcmp_locale||5.004000| ibcmp_utf8||5.007003| ibcmp||| incline||| incpush_if_exists||| incpush_use_sep||| incpush||| ingroup||| init_argv_symbols||| init_constants||| init_dbargs||| init_debugger||| init_global_struct||| init_i18nl10n||5.006000| init_i18nl14n||5.006000| init_ids||| init_interp||| init_main_stash||| init_perllib||| init_postdump_symbols||| init_predump_symbols||| init_stacks||5.005000| init_tm||5.007002| inplace_aassign||| instr|||n intro_my||| intuit_method||| intuit_more||| invert||| invlist_array||| invlist_clone||| invlist_extend||| invlist_highest||| invlist_is_iterating||| invlist_iterfinish||| invlist_iterinit||| invlist_iternext||| invlist_max||| invlist_previous_index||| invlist_set_len||| invlist_set_previous_index||| invlist_trim||| invoke_exception_hook||| io_close||| isALNUMC|5.006000||p isALNUM_lazy||| isALPHANUMERIC||5.017008| isALPHA||| isASCII|5.006000|5.006000|p isBLANK|5.006001||p isCNTRL|5.006000|5.006000|p isDIGIT||| isFOO_lc||| isFOO_utf8_lc||| isGRAPH|5.006000||p isGV_with_GP|5.009004||p isIDCONT||5.017008| isIDFIRST_lazy||| isIDFIRST||| isLOWER||| isOCTAL||5.013005| isPRINT|5.004000||p isPSXSPC|5.006001||p isPUNCT|5.006000||p isSPACE||| isUPPER||| isWORDCHAR||5.013006| isXDIGIT|5.006000||p is_an_int||| is_ascii_string||5.011000|n is_cur_LC_category_utf8||| is_handle_constructor|||n is_list_assignment||| is_lvalue_sub||5.007001| is_uni_alnum_lc||5.006000| is_uni_alnumc_lc||5.017007| is_uni_alnumc||5.017007| is_uni_alnum||5.006000| is_uni_alpha_lc||5.006000| is_uni_alpha||5.006000| is_uni_ascii_lc||5.006000| is_uni_ascii||5.006000| is_uni_blank_lc||5.017002| is_uni_blank||5.017002| is_uni_cntrl_lc||5.006000| is_uni_cntrl||5.006000| is_uni_digit_lc||5.006000| is_uni_digit||5.006000| is_uni_graph_lc||5.006000| is_uni_graph||5.006000| is_uni_idfirst_lc||5.006000| is_uni_idfirst||5.006000| is_uni_lower_lc||5.006000| is_uni_lower||5.006000| is_uni_print_lc||5.006000| is_uni_print||5.006000| is_uni_punct_lc||5.006000| is_uni_punct||5.006000| is_uni_space_lc||5.006000| is_uni_space||5.006000| is_uni_upper_lc||5.006000| is_uni_upper||5.006000| is_uni_xdigit_lc||5.006000| is_uni_xdigit||5.006000| is_utf8_alnumc||5.017007| is_utf8_alnum||5.006000| is_utf8_alpha||5.006000| is_utf8_ascii||5.006000| is_utf8_blank||5.017002| is_utf8_char_buf||5.015008|n is_utf8_char_slow|||n is_utf8_char||5.006000|n is_utf8_cntrl||5.006000| is_utf8_common||| is_utf8_digit||5.006000| is_utf8_graph||5.006000| is_utf8_idcont||5.008000| is_utf8_idfirst||5.006000| is_utf8_lower||5.006000| is_utf8_mark||5.006000| is_utf8_perl_space||5.011001| is_utf8_perl_word||5.011001| is_utf8_posix_digit||5.011001| is_utf8_print||5.006000| is_utf8_punct||5.006000| is_utf8_space||5.006000| is_utf8_string_loclen||5.009003|n is_utf8_string_loc||5.008001|n is_utf8_string||5.006001|n is_utf8_upper||5.006000| is_utf8_xdigit||5.006000| is_utf8_xidcont||5.013010| is_utf8_xidfirst||5.013010| isa_lookup||| items|||n ix|||n jmaybe||| join_exact||| keyword_plugin_standard||| keyword||| leave_scope||| lex_bufutf8||5.011002| lex_discard_to||5.011002| lex_grow_linestr||5.011002| lex_next_chunk||5.011002| lex_peek_unichar||5.011002| lex_read_space||5.011002| lex_read_to||5.011002| lex_read_unichar||5.011002| lex_start||5.009005| lex_stuff_pvn||5.011002| lex_stuff_pvs||5.013005| lex_stuff_pv||5.013006| lex_stuff_sv||5.011002| lex_unstuff||5.011002| listkids||| list||| load_module_nocontext|||vn load_module|5.006000||pv localize||| looks_like_bool||| looks_like_number||| lop||| mPUSHi|5.009002||p mPUSHn|5.009002||p mPUSHp|5.009002||p mPUSHs|5.010001||p mPUSHu|5.009002||p mXPUSHi|5.009002||p mXPUSHn|5.009002||p mXPUSHp|5.009002||p mXPUSHs|5.010001||p mXPUSHu|5.009002||p mad_free||| madlex||| madparse||| magic_clear_all_env||| magic_cleararylen_p||| magic_clearenv||| magic_clearhints||| magic_clearhint||| magic_clearisa||| magic_clearpack||| magic_clearsig||| magic_copycallchecker||| magic_dump||5.006000| magic_existspack||| magic_freearylen_p||| magic_freeovrld||| magic_getarylen||| magic_getdefelem||| magic_getnkeys||| magic_getpack||| magic_getpos||| magic_getsig||| magic_getsubstr||| magic_gettaint||| magic_getuvar||| magic_getvec||| magic_get||| magic_killbackrefs||| magic_methcall1||| magic_methcall|||v magic_methpack||| magic_nextpack||| magic_regdata_cnt||| magic_regdatum_get||| magic_regdatum_set||| magic_scalarpack||| magic_set_all_env||| magic_setarylen||| magic_setcollxfrm||| magic_setdbline||| magic_setdefelem||| magic_setenv||| magic_sethint||| magic_setisa||| magic_setmglob||| magic_setnkeys||| magic_setpack||| magic_setpos||| magic_setregexp||| magic_setsig||| magic_setsubstr||| magic_settaint||| magic_setutf8||| magic_setuvar||| magic_setvec||| magic_set||| magic_sizepack||| magic_wipepack||| make_matcher||| make_trie_failtable||| make_trie||| malloc_good_size|||n malloced_size|||n malloc||5.007002|n markstack_grow||| matcher_matches_sv||| mayberelocate||| measure_struct||| memEQs|5.009005||p memEQ|5.004000||p memNEs|5.009005||p memNE|5.004000||p mem_collxfrm||| mem_log_common|||n mess_alloc||| mess_nocontext|||vn mess_sv||5.013001| mess||5.006000|v method_common||| mfree||5.007002|n mg_clear||| mg_copy||| mg_dup||| mg_find_mglob||| mg_findext||5.013008| mg_find||| mg_free_type||5.013006| mg_free||| mg_get||| mg_length||5.005000| mg_localize||| mg_magical||| mg_set||| mg_size||5.005000| mini_mktime||5.007002| minus_v||| missingterm||| mode_from_discipline||| modkids||| more_bodies||| more_sv||| moreswitches||| mro_clean_isarev||| mro_gather_and_rename||| mro_get_from_name||5.010001| mro_get_linear_isa_dfs||| mro_get_linear_isa||5.009005| mro_get_private_data||5.010001| mro_isa_changed_in||| mro_meta_dup||| mro_meta_init||| mro_method_changed_in||5.009005| mro_package_moved||| mro_register||5.010001| mro_set_mro||5.010001| mro_set_private_data||5.010001| mul128||| mulexp10|||n my_atof2||5.007002| my_atof||5.006000| my_attrs||| my_bcopy|||n my_bzero|||n my_chsize||| my_clearenv||| my_cxt_index||| my_cxt_init||| my_dirfd||5.009005| my_exit_jump||| my_exit||| my_failure_exit||5.004000| my_fflush_all||5.006000| my_fork||5.007003|n my_kid||| my_lstat_flags||| my_lstat||5.019003| my_memcmp|||n my_memset||5.004000|n my_pclose||5.004000| my_popen_list||5.007001| my_popen||5.004000| my_setenv||| my_snprintf|5.009004||pvn my_socketpair||5.007003|n my_sprintf|5.009003||pvn my_stat_flags||| my_stat||5.019003| my_strftime||5.007002| my_strlcat|5.009004||pn my_strlcpy|5.009004||pn my_unexec||| my_vsnprintf||5.009004|n need_utf8|||n newANONATTRSUB||5.006000| newANONHASH||| newANONLIST||| newANONSUB||| newASSIGNOP||| newATTRSUB_flags||| newATTRSUB||5.006000| newAVREF||| newAV||| newBINOP||| newCONDOP||| newCONSTSUB_flags||5.015006| newCONSTSUB|5.004050||p newCVREF||| newDEFSVOP||| newFORM||| newFOROP||5.013007| newGIVENOP||5.009003| newGIVWHENOP||| newGP||| newGVOP||| newGVREF||| newGVgen_flags||5.015004| newGVgen||| newHVREF||| newHVhv||5.005000| newHV||| newIO||| newLISTOP||| newLOGOP||| newLOOPEX||| newLOOPOP||| newMADPROP||| newMADsv||| newMYSUB||5.017004| newNULLLIST||| newOP||| newPADOP||| newPMOP||| newPROG||| newPVOP||| newRANGE||| newRV_inc|5.004000||p newRV_noinc|5.004000||p newRV||| newSLICEOP||| newSTATEOP||| newSTUB||| newSUB||| newSVOP||| newSVREF||| newSV_type|5.009005||p newSVhek||5.009003| newSViv||| newSVnv||| newSVpadname||5.017004| newSVpv_share||5.013006| newSVpvf_nocontext|||vn newSVpvf||5.004000|v newSVpvn_flags|5.010001||p newSVpvn_share|5.007001||p newSVpvn_utf8|5.010001||p newSVpvn|5.004050||p newSVpvs_flags|5.010001||p newSVpvs_share|5.009003||p newSVpvs|5.009003||p newSVpv||| newSVrv||| newSVsv||| newSVuv|5.006000||p newSV||| newTOKEN||| newUNOP||| newWHENOP||5.009003| newWHILEOP||5.013007| newXS_flags||5.009004| newXS_len_flags||| newXSproto||5.006000| newXS||5.006000| new_collate||5.006000| new_constant||| new_ctype||5.006000| new_he||| new_logop||| new_numeric||5.006000| new_stackinfo||5.005000| new_version||5.009000| new_warnings_bitfield||| next_symbol||| nextargv||| nextchar||| ninstr|||n no_bareword_allowed||| no_fh_allowed||| no_op||| not_a_number||| not_incrementable||| nothreadhook||5.008000| nuke_stacks||| num_overflow|||n oopsAV||| oopsHV||| op_append_elem||5.013006| op_append_list||5.013006| op_clear||| op_const_sv||| op_contextualize||5.013006| op_dump||5.006000| op_free||| op_getmad_weak||| op_getmad||| op_integerize||| op_linklist||5.013006| op_lvalue_flags||| op_lvalue||5.013007| op_null||5.007002| op_prepend_elem||5.013006| op_refcnt_dec||| op_refcnt_inc||| op_refcnt_lock||5.009002| op_refcnt_unlock||5.009002| op_scope||5.013007| op_std_init||| op_unscope||| op_xmldump||| open_script||| opslab_force_free||| opslab_free_nopad||| opslab_free||| pMY_CXT_|5.007003||p pMY_CXT|5.007003||p pTHX_|5.006000||p pTHX|5.006000||p packWARN|5.007003||p pack_cat||5.007003| pack_rec||| package_version||| package||| packlist||5.008001| pad_add_anon||5.008001| pad_add_name_pvn||5.015001| pad_add_name_pvs||5.015001| pad_add_name_pv||5.015001| pad_add_name_sv||5.015001| pad_alloc_name||| pad_alloc||| pad_block_start||| pad_check_dup||| pad_compname_type||5.009003| pad_findlex||| pad_findmy_pvn||5.015001| pad_findmy_pvs||5.015001| pad_findmy_pv||5.015001| pad_findmy_sv||5.015001| pad_fixup_inner_anons||| pad_free||| pad_leavemy||| pad_new||5.008001| pad_peg|||n pad_push||| pad_reset||| pad_setsv||| pad_sv||| pad_swipe||| pad_tidy||5.008001| padlist_dup||| padlist_store||| parse_arithexpr||5.013008| parse_barestmt||5.013007| parse_block||5.013007| parse_body||| parse_fullexpr||5.013008| parse_fullstmt||5.013005| parse_ident||| parse_label||5.013007| parse_listexpr||5.013008| parse_lparen_question_flags||| parse_stmtseq||5.013006| parse_termexpr||5.013008| parse_unicode_opts||| parser_dup||| parser_free_nexttoke_ops||| parser_free||| path_is_searchable|||n peep||| pending_ident||| perl_alloc_using|||n perl_alloc|||n perl_clone_using|||n perl_clone|||n perl_construct|||n perl_destruct||5.007003|n perl_free|||n perl_parse||5.006000|n perl_run|||n pidgone||| pm_description||| pmop_dump||5.006000| pmop_xmldump||| pmruntime||| pmtrans||| pop_scope||| populate_isa|||v pregcomp||5.009005| pregexec||| pregfree2||5.011000| pregfree||| prepend_madprops||| prescan_version||5.011004| printbuf||| printf_nocontext|||vn process_special_blocks||| ptr_hash|||n ptr_table_clear||5.009005| ptr_table_fetch||5.009005| ptr_table_find|||n ptr_table_free||5.009005| ptr_table_new||5.009005| ptr_table_split||5.009005| ptr_table_store||5.009005| push_scope||| put_byte||| put_latin1_charclass_innards||| pv_display|5.006000||p pv_escape|5.009004||p pv_pretty|5.009004||p pv_uni_display||5.007003| qerror||| qsortsvu||| re_compile||5.009005| re_croak2||| re_dup_guts||| re_intuit_start||5.019001| re_intuit_string||5.006000| re_op_compile||| readpipe_override||| realloc||5.007002|n reentrant_free||5.019003| reentrant_init||5.019003| reentrant_retry||5.019003|vn reentrant_size||5.019003| ref_array_or_hash||| refcounted_he_chain_2hv||| refcounted_he_fetch_pvn||| refcounted_he_fetch_pvs||| refcounted_he_fetch_pv||| refcounted_he_fetch_sv||| refcounted_he_free||| refcounted_he_inc||| refcounted_he_new_pvn||| refcounted_he_new_pvs||| refcounted_he_new_pv||| refcounted_he_new_sv||| refcounted_he_value||| refkids||| refto||| ref||5.019003| reg_check_named_buff_matched||| reg_named_buff_all||5.009005| reg_named_buff_exists||5.009005| reg_named_buff_fetch||5.009005| reg_named_buff_firstkey||5.009005| reg_named_buff_iter||| reg_named_buff_nextkey||5.009005| reg_named_buff_scalar||5.009005| reg_named_buff||| reg_node||| reg_numbered_buff_fetch||| reg_numbered_buff_length||| reg_numbered_buff_store||| reg_qr_package||| reg_recode||| reg_scan_name||| reg_skipcomment||| reg_temp_copy||| reganode||| regatom||| regbranch||| regclass_swash||5.009004| regclass||| regcppop||| regcppush||| regcurly||| regdump_extflags||| regdump_intflags||| regdump||5.005000| regdupe_internal||| regexec_flags||5.005000| regfree_internal||5.009005| reghop3|||n reghop4|||n reghopmaybe3|||n reginclass||| reginitcolors||5.006000| reginsert||| regmatch||| regnext||5.005000| regpatws|||n regpiece||| regpposixcc||| regprop||| regrepeat||| regtail_study||| regtail||| regtry||| reguni||| regwhite|||n reg||| repeatcpy|||n report_evil_fh||| report_redefined_cv||| report_uninit||| report_wrongway_fh||| require_pv||5.006000| require_tie_mod||| restore_magic||| rninstr|||n rpeep||| rsignal_restore||| rsignal_save||| rsignal_state||5.004000| rsignal||5.004000| run_body||| run_user_filter||| runops_debug||5.005000| runops_standard||5.005000| rv2cv_op_cv||5.013006| rvpv_dup||| rxres_free||| rxres_restore||| rxres_save||| safesyscalloc||5.006000|n safesysfree||5.006000|n safesysmalloc||5.006000|n safesysrealloc||5.006000|n same_dirent||| save_I16||5.004000| save_I32||| save_I8||5.006000| save_adelete||5.011000| save_aelem_flags||5.011000| save_aelem||5.004050| save_alloc||5.006000| save_aptr||| save_ary||| save_bool||5.008001| save_clearsv||| save_delete||| save_destructor_x||5.006000| save_destructor||5.006000| save_freeop||| save_freepv||| save_freesv||| save_generic_pvref||5.006001| save_generic_svref||5.005030| save_gp||5.004000| save_hash||| save_hdelete||5.011000| save_hek_flags|||n save_helem_flags||5.011000| save_helem||5.004050| save_hints||5.010001| save_hptr||| save_int||| save_item||| save_iv||5.005000| save_lines||| save_list||| save_long||| save_magic_flags||| save_mortalizesv||5.007001| save_nogv||| save_op||5.005000| save_padsv_and_mortalize||5.010001| save_pptr||| save_pushi32ptr||5.010001| save_pushptri32ptr||| save_pushptrptr||5.010001| save_pushptr||5.010001| save_re_context||5.006000| save_scalar_at||| save_scalar||| save_set_svflags||5.009000| save_shared_pvref||5.007003| save_sptr||| save_svref||| save_vptr||5.006000| savepvn||| savepvs||5.009003| savepv||| savesharedpvn||5.009005| savesharedpvs||5.013006| savesharedpv||5.007003| savesharedsvpv||5.013006| savestack_grow_cnt||5.008001| savestack_grow||| savesvpv||5.009002| sawparens||| scalar_mod_type|||n scalarboolean||| scalarkids||| scalarseq||| scalarvoid||| scalar||| scan_bin||5.006000| scan_commit||| scan_const||| scan_formline||| scan_heredoc||| scan_hex||| scan_ident||| scan_inputsymbol||| scan_num||5.007001| scan_oct||| scan_pat||| scan_str||| scan_subst||| scan_trans||| scan_version||5.009001| scan_vstring||5.009005| scan_word||| screaminstr||5.005000| search_const||| seed||5.008001| sequence_num||| set_context||5.006000|n set_numeric_local||5.006000| set_numeric_radix||5.006000| set_numeric_standard||5.006000| setdefout||| share_hek_flags||| share_hek||5.004000| si_dup||| sighandler|||n simplify_sort||| skipspace0||| skipspace1||| skipspace2||| skipspace_flags||| softref2xv||| sortcv_stacked||| sortcv_xsub||| sortcv||| sortsv_flags||5.009003| sortsv||5.007003| space_join_names_mortal||| ss_dup||| stack_grow||| start_force||| start_glob||| start_subparse||5.004000| stdize_locale||| strEQ||| strGE||| strGT||| strLE||| strLT||| strNE||| str_to_version||5.006000| strip_return||| strnEQ||| strnNE||| study_chunk||| sub_crush_depth||| sublex_done||| sublex_push||| sublex_start||| sv_2bool_flags||5.013006| sv_2bool||| sv_2cv||| sv_2io||| sv_2iuv_common||| sv_2iuv_non_preserve||| sv_2iv_flags||5.009001| sv_2iv||| sv_2mortal||| sv_2num||| sv_2nv_flags||5.013001| sv_2pv_flags|5.007002||p sv_2pv_nolen|5.006000||p sv_2pvbyte_nolen|5.006000||p sv_2pvbyte|5.006000||p sv_2pvutf8_nolen||5.006000| sv_2pvutf8||5.006000| sv_2pv||| sv_2uv_flags||5.009001| sv_2uv|5.004000||p sv_add_arena||| sv_add_backref||| sv_backoff||| sv_bless||| sv_cat_decode||5.008001| sv_catpv_flags||5.013006| sv_catpv_mg|5.004050||p sv_catpv_nomg||5.013006| sv_catpvf_mg_nocontext|||pvn sv_catpvf_mg|5.006000|5.004000|pv sv_catpvf_nocontext|||vn sv_catpvf||5.004000|v sv_catpvn_flags||5.007002| sv_catpvn_mg|5.004050||p sv_catpvn_nomg|5.007002||p sv_catpvn||| sv_catpvs_flags||5.013006| sv_catpvs_mg||5.013006| sv_catpvs_nomg||5.013006| sv_catpvs|5.009003||p sv_catpv||| sv_catsv_flags||5.007002| sv_catsv_mg|5.004050||p sv_catsv_nomg|5.007002||p sv_catsv||| sv_catxmlpvn||| sv_catxmlpv||| sv_catxmlsv||| sv_chop||| sv_clean_all||| sv_clean_objs||| sv_clear||| sv_cmp_flags||5.013006| sv_cmp_locale_flags||5.013006| sv_cmp_locale||5.004000| sv_cmp||| sv_collxfrm_flags||5.013006| sv_collxfrm||| sv_copypv_flags||5.017002| sv_copypv_nomg||5.017002| sv_copypv||| sv_dec_nomg||5.013002| sv_dec||| sv_del_backref||| sv_derived_from_pvn||5.015004| sv_derived_from_pv||5.015004| sv_derived_from_sv||5.015004| sv_derived_from||5.004000| sv_destroyable||5.010000| sv_display||| sv_does_pvn||5.015004| sv_does_pv||5.015004| sv_does_sv||5.015004| sv_does||5.009004| sv_dump||| sv_dup_common||| sv_dup_inc_multiple||| sv_dup_inc||| sv_dup||| sv_eq_flags||5.013006| sv_eq||| sv_exp_grow||| sv_force_normal_flags||5.007001| sv_force_normal||5.006000| sv_free2||| sv_free_arenas||| sv_free||| sv_gets||5.004000| sv_grow||| sv_i_ncmp||| sv_inc_nomg||5.013002| sv_inc||| sv_insert_flags||5.010001| sv_insert||| sv_isa||| sv_isobject||| sv_iv||5.005000| sv_kill_backrefs||| sv_len_utf8_nomg||| sv_len_utf8||5.006000| sv_len||| sv_magic_portable|5.019003|5.004000|p sv_magicext_mglob||| sv_magicext||5.007003| sv_magic||| sv_mortalcopy_flags||| sv_mortalcopy||| sv_ncmp||| sv_newmortal||| sv_newref||| sv_nolocking||5.007003| sv_nosharing||5.007003| sv_nounlocking||| sv_nv||5.005000| sv_peek||5.005000| sv_pos_b2u_flags||5.019003| sv_pos_b2u_midway||| sv_pos_b2u||5.006000| sv_pos_u2b_cached||| sv_pos_u2b_flags||5.011005| sv_pos_u2b_forwards|||n sv_pos_u2b_midway|||n sv_pos_u2b||5.006000| sv_pvbyten_force||5.006000| sv_pvbyten||5.006000| sv_pvbyte||5.006000| sv_pvn_force_flags|5.007002||p sv_pvn_force||| sv_pvn_nomg|5.007003|5.005000|p sv_pvn||5.005000| sv_pvutf8n_force||5.006000| sv_pvutf8n||5.006000| sv_pvutf8||5.006000| sv_pv||5.006000| sv_recode_to_utf8||5.007003| sv_reftype||| sv_ref||| sv_release_COW||| sv_replace||| sv_report_used||| sv_resetpvn||| sv_reset||| sv_rvweaken||5.006000| sv_sethek||| sv_setiv_mg|5.004050||p sv_setiv||| sv_setnv_mg|5.006000||p sv_setnv||| sv_setpv_mg|5.004050||p sv_setpvf_mg_nocontext|||pvn sv_setpvf_mg|5.006000|5.004000|pv sv_setpvf_nocontext|||vn sv_setpvf||5.004000|v sv_setpviv_mg||5.008001| sv_setpviv||5.008001| sv_setpvn_mg|5.004050||p sv_setpvn||| sv_setpvs_mg||5.013006| sv_setpvs|5.009004||p sv_setpv||| sv_setref_iv||| sv_setref_nv||| sv_setref_pvn||| sv_setref_pvs||5.019003| sv_setref_pv||| sv_setref_uv||5.007001| sv_setsv_cow||| sv_setsv_flags||5.007002| sv_setsv_mg|5.004050||p sv_setsv_nomg|5.007002||p sv_setsv||| sv_setuv_mg|5.004050||p sv_setuv|5.004000||p sv_tainted||5.004000| sv_taint||5.004000| sv_true||5.005000| sv_unglob||| sv_uni_display||5.007003| sv_unmagicext||5.013008| sv_unmagic||| sv_unref_flags||5.007001| sv_unref||| sv_untaint||5.004000| sv_upgrade||| sv_usepvn_flags||5.009004| sv_usepvn_mg|5.004050||p sv_usepvn||| sv_utf8_decode||5.006000| sv_utf8_downgrade||5.006000| sv_utf8_encode||5.006000| sv_utf8_upgrade_flags_grow||5.011000| sv_utf8_upgrade_flags||5.007002| sv_utf8_upgrade_nomg||5.007002| sv_utf8_upgrade||5.007001| sv_uv|5.005000||p sv_vcatpvf_mg|5.006000|5.004000|p sv_vcatpvfn_flags||5.017002| sv_vcatpvfn||5.004000| sv_vcatpvf|5.006000|5.004000|p sv_vsetpvf_mg|5.006000|5.004000|p sv_vsetpvfn||5.004000| sv_vsetpvf|5.006000|5.004000|p sv_xmlpeek||| svtype||| swallow_bom||| swash_fetch||5.007002| swash_init||5.006000| swatch_get||| sys_init3||5.010000|n sys_init||5.010000|n sys_intern_clear||| sys_intern_dup||| sys_intern_init||| sys_term||5.010000|n taint_env||| taint_proper||| tied_method|||v tmps_grow||5.006000| toFOLD_uni||5.007003| toFOLD_utf8||5.019001| toFOLD||5.019001| toLOWER_L1||5.019001| toLOWER_LC||5.004000| toLOWER_uni||5.007003| toLOWER_utf8||5.015007| toLOWER||| toTITLE_uni||5.007003| toTITLE_utf8||5.015007| toTITLE||5.019001| toUPPER_uni||5.007003| toUPPER_utf8||5.015007| toUPPER||5.004000| to_byte_substr||| to_lower_latin1||| to_uni_fold||5.007003| to_uni_lower_lc||5.006000| to_uni_lower||5.007003| to_uni_title_lc||5.006000| to_uni_title||5.007003| to_uni_upper_lc||5.006000| to_uni_upper||5.007003| to_utf8_case||5.007003| to_utf8_fold||5.015007| to_utf8_lower||5.015007| to_utf8_substr||| to_utf8_title||5.015007| to_utf8_upper||5.015007| token_free||| token_getmad||| tokenize_use||| tokeq||| tokereport||| too_few_arguments_pv||| too_few_arguments_sv||| too_many_arguments_pv||| too_many_arguments_sv||| translate_substr_offsets||| try_amagic_bin||| try_amagic_un||| uiv_2buf|||n unlnk||| unpack_rec||| unpack_str||5.007003| unpackstring||5.008001| unreferenced_to_tmp_stack||| unshare_hek_or_pvn||| unshare_hek||| unsharepvn||5.004000| unwind_handler_stack||| update_debugger_info||| upg_version||5.009005| usage||| utf16_textfilter||| utf16_to_utf8_reversed||5.006001| utf16_to_utf8||5.006001| utf8_distance||5.006000| utf8_hop||5.006000| utf8_length||5.007001| utf8_mg_len_cache_update||| utf8_mg_pos_cache_update||| utf8_to_bytes||5.006001| utf8_to_uvchr_buf||5.015009| utf8_to_uvchr||5.007001| utf8_to_uvuni_buf||5.015009| utf8_to_uvuni||5.007001| utf8n_to_uvchr||| utf8n_to_uvuni||5.007001| utilize||| uvchr_to_utf8_flags||5.007003| uvchr_to_utf8||| uvuni_to_utf8_flags||5.007003| uvuni_to_utf8||5.007001| valid_utf8_to_uvchr||| valid_utf8_to_uvuni||5.015009| validate_proto||| validate_suid||| varname||| vcmp||5.009000| vcroak||5.006000| vdeb||5.007003| vform||5.006000| visit||| vivify_defelem||| vivify_ref||| vload_module|5.006000||p vmess||5.006000| vnewSVpvf|5.006000|5.004000|p vnormal||5.009002| vnumify||5.009000| vstringify||5.009000| vverify||5.009003| vwarner||5.006000| vwarn||5.006000| wait4pid||| warn_nocontext|||vn warn_sv||5.013001| warner_nocontext|||vn warner|5.006000|5.004000|pv warn|||v was_lvalue_sub||| watch||| whichsig_pvn||5.015004| whichsig_pv||5.015004| whichsig_sv||5.015004| whichsig||| win32_croak_not_implemented|||n with_queued_errors||| wrap_op_checker||5.015008| write_to_stderr||| xmldump_all_perl||| xmldump_all||| xmldump_attr||| xmldump_eval||| xmldump_form||| xmldump_indent|||v xmldump_packsubs_perl||| xmldump_packsubs||| xmldump_sub_perl||| xmldump_sub||| xmldump_vindent||| xs_apiversion_bootcheck||| xs_version_bootcheck||| yyerror_pvn||| yyerror_pv||| yyerror||| yylex||| yyparse||| yyunlex||| yywarn||| ); if (exists $opt{'list-unsupported'}) { my $f; for $f (sort { lc $a cmp lc $b } keys %API) { next unless $API{$f}{todo}; print "$f ", '.'x(40-length($f)), " ", format_version($API{$f}{todo}), "\n"; } exit 0; } # Scan for possible replacement candidates my(%replace, %need, %hints, %warnings, %depends); my $replace = 0; my($hint, $define, $function); sub find_api { my $code = shift; $code =~ s{ / (?: \*[^*]*\*+(?:[^$ccs][^*]*\*+)* / | /[^\r\n]*) | "[^"\\]*(?:\\.[^"\\]*)*" | '[^'\\]*(?:\\.[^'\\]*)*' }{}egsx; grep { exists $API{$_} } $code =~ /(\w+)/mg; } while () { if ($hint) { my $h = $hint->[0] eq 'Hint' ? \%hints : \%warnings; if (m{^\s*\*\s(.*?)\s*$}) { for (@{$hint->[1]}) { $h->{$_} ||= ''; # suppress warning with older perls $h->{$_} .= "$1\n"; } } else { undef $hint } } $hint = [$1, [split /,?\s+/, $2]] if m{^\s*$rccs\s+(Hint|Warning):\s+(\w+(?:,?\s+\w+)*)\s*$}; if ($define) { if ($define->[1] =~ /\\$/) { $define->[1] .= $_; } else { if (exists $API{$define->[0]} && $define->[1] !~ /^DPPP_\(/) { my @n = find_api($define->[1]); push @{$depends{$define->[0]}}, @n if @n } undef $define; } } $define = [$1, $2] if m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(.*)}; if ($function) { if (/^}/) { if (exists $API{$function->[0]}) { my @n = find_api($function->[1]); push @{$depends{$function->[0]}}, @n if @n } undef $function; } else { $function->[1] .= $_; } } $function = [$1, ''] if m{^DPPP_\(my_(\w+)\)}; $replace = $1 if m{^\s*$rccs\s+Replace:\s+(\d+)\s+$rcce\s*$}; $replace{$2} = $1 if $replace and m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(\w+)}; $replace{$2} = $1 if m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(\w+).*$rccs\s+Replace\s+$rcce}; $replace{$1} = $2 if m{^\s*$rccs\s+Replace (\w+) with (\w+)\s+$rcce\s*$}; if (m{^\s*$rccs\s+(\w+(\s*,\s*\w+)*)\s+depends\s+on\s+(\w+(\s*,\s*\w+)*)\s+$rcce\s*$}) { my @deps = map { s/\s+//g; $_ } split /,/, $3; my $d; for $d (map { s/\s+//g; $_ } split /,/, $1) { push @{$depends{$d}}, @deps; } } $need{$1} = 1 if m{^#if\s+defined\(NEED_(\w+)(?:_GLOBAL)?\)}; } for (values %depends) { my %s; $_ = [sort grep !$s{$_}++, @$_]; } if (exists $opt{'api-info'}) { my $f; my $count = 0; my $match = $opt{'api-info'} =~ m!^/(.*)/$! ? $1 : "^\Q$opt{'api-info'}\E\$"; for $f (sort { lc $a cmp lc $b } keys %API) { next unless $f =~ /$match/; print "\n=== $f ===\n\n"; my $info = 0; if ($API{$f}{base} || $API{$f}{todo}) { my $base = format_version($API{$f}{base} || $API{$f}{todo}); print "Supported at least starting from perl-$base.\n"; $info++; } if ($API{$f}{provided}) { my $todo = $API{$f}{todo} ? format_version($API{$f}{todo}) : "5.003"; print "Support by $ppport provided back to perl-$todo.\n"; print "Support needs to be explicitly requested by NEED_$f.\n" if exists $need{$f}; print "Depends on: ", join(', ', @{$depends{$f}}), ".\n" if exists $depends{$f}; print "\n$hints{$f}" if exists $hints{$f}; print "\nWARNING:\n$warnings{$f}" if exists $warnings{$f}; $info++; } print "No portability information available.\n" unless $info; $count++; } $count or print "Found no API matching '$opt{'api-info'}'."; print "\n"; exit 0; } if (exists $opt{'list-provided'}) { my $f; for $f (sort { lc $a cmp lc $b } keys %API) { next unless $API{$f}{provided}; my @flags; push @flags, 'explicit' if exists $need{$f}; push @flags, 'depend' if exists $depends{$f}; push @flags, 'hint' if exists $hints{$f}; push @flags, 'warning' if exists $warnings{$f}; my $flags = @flags ? ' ['.join(', ', @flags).']' : ''; print "$f$flags\n"; } exit 0; } my @files; my @srcext = qw( .xs .c .h .cc .cpp -c.inc -xs.inc ); my $srcext = join '|', map { quotemeta $_ } @srcext; if (@ARGV) { my %seen; for (@ARGV) { if (-e) { if (-f) { push @files, $_ unless $seen{$_}++; } else { warn "'$_' is not a file.\n" } } else { my @new = grep { -f } glob $_ or warn "'$_' does not exist.\n"; push @files, grep { !$seen{$_}++ } @new; } } } else { eval { require File::Find; File::Find::find(sub { $File::Find::name =~ /($srcext)$/i and push @files, $File::Find::name; }, '.'); }; if ($@) { @files = map { glob "*$_" } @srcext; } } if (!@ARGV || $opt{filter}) { my(@in, @out); my %xsc = map { /(.*)\.xs$/ ? ("$1.c" => 1, "$1.cc" => 1) : () } @files; for (@files) { my $out = exists $xsc{$_} || /\b\Q$ppport\E$/i || !/($srcext)$/i; push @{ $out ? \@out : \@in }, $_; } if (@ARGV && @out) { warning("Skipping the following files (use --nofilter to avoid this):\n| ", join "\n| ", @out); } @files = @in; } die "No input files given!\n" unless @files; my(%files, %global, %revreplace); %revreplace = reverse %replace; my $filename; my $patch_opened = 0; for $filename (@files) { unless (open IN, "<$filename") { warn "Unable to read from $filename: $!\n"; next; } info("Scanning $filename ..."); my $c = do { local $/; }; close IN; my %file = (orig => $c, changes => 0); # Temporarily remove C/XS comments and strings from the code my @ccom; $c =~ s{ ( ^$HS*\#$HS*include\b[^\r\n]+\b(?:\Q$ppport\E|XSUB\.h)\b[^\r\n]* | ^$HS*\#$HS*(?:define|elif|if(?:def)?)\b[^\r\n]* ) | ( ^$HS*\#[^\r\n]* | "[^"\\]*(?:\\.[^"\\]*)*" | '[^'\\]*(?:\\.[^'\\]*)*' | / (?: \*[^*]*\*+(?:[^$ccs][^*]*\*+)* / | /[^\r\n]* ) ) }{ defined $2 and push @ccom, $2; defined $1 ? $1 : "$ccs$#ccom$cce" }mgsex; $file{ccom} = \@ccom; $file{code} = $c; $file{has_inc_ppport} = $c =~ /^$HS*#$HS*include[^\r\n]+\b\Q$ppport\E\b/m; my $func; for $func (keys %API) { my $match = $func; $match .= "|$revreplace{$func}" if exists $revreplace{$func}; if ($c =~ /\b(?:Perl_)?($match)\b/) { $file{uses_replace}{$1}++ if exists $revreplace{$func} && $1 eq $revreplace{$func}; $file{uses_Perl}{$func}++ if $c =~ /\bPerl_$func\b/; if (exists $API{$func}{provided}) { $file{uses_provided}{$func}++; if (!exists $API{$func}{base} || $API{$func}{base} > $opt{'compat-version'}) { $file{uses}{$func}++; my @deps = rec_depend($func); if (@deps) { $file{uses_deps}{$func} = \@deps; for (@deps) { $file{uses}{$_} = 0 unless exists $file{uses}{$_}; } } for ($func, @deps) { $file{needs}{$_} = 'static' if exists $need{$_}; } } } if (exists $API{$func}{todo} && $API{$func}{todo} > $opt{'compat-version'}) { if ($c =~ /\b$func\b/) { $file{uses_todo}{$func}++; } } } } while ($c =~ /^$HS*#$HS*define$HS+(NEED_(\w+?)(_GLOBAL)?)\b/mg) { if (exists $need{$2}) { $file{defined $3 ? 'needed_global' : 'needed_static'}{$2}++; } else { warning("Possibly wrong #define $1 in $filename") } } for (qw(uses needs uses_todo needed_global needed_static)) { for $func (keys %{$file{$_}}) { push @{$global{$_}{$func}}, $filename; } } $files{$filename} = \%file; } # Globally resolve NEED_'s my $need; for $need (keys %{$global{needs}}) { if (@{$global{needs}{$need}} > 1) { my @targets = @{$global{needs}{$need}}; my @t = grep $files{$_}{needed_global}{$need}, @targets; @targets = @t if @t; @t = grep /\.xs$/i, @targets; @targets = @t if @t; my $target = shift @targets; $files{$target}{needs}{$need} = 'global'; for (@{$global{needs}{$need}}) { $files{$_}{needs}{$need} = 'extern' if $_ ne $target; } } } for $filename (@files) { exists $files{$filename} or next; info("=== Analyzing $filename ==="); my %file = %{$files{$filename}}; my $func; my $c = $file{code}; my $warnings = 0; for $func (sort keys %{$file{uses_Perl}}) { if ($API{$func}{varargs}) { unless ($API{$func}{nothxarg}) { my $changes = ($c =~ s{\b(Perl_$func\s*\(\s*)(?!aTHX_?)(\)|[^\s)]*\))} { $1 . ($2 eq ')' ? 'aTHX' : 'aTHX_ ') . $2 }ge); if ($changes) { warning("Doesn't pass interpreter argument aTHX to Perl_$func"); $file{changes} += $changes; } } } else { warning("Uses Perl_$func instead of $func"); $file{changes} += ($c =~ s{\bPerl_$func(\s*)\((\s*aTHX_?)?\s*} {$func$1(}g); } } for $func (sort keys %{$file{uses_replace}}) { warning("Uses $func instead of $replace{$func}"); $file{changes} += ($c =~ s/\b$func\b/$replace{$func}/g); } for $func (sort keys %{$file{uses_provided}}) { if ($file{uses}{$func}) { if (exists $file{uses_deps}{$func}) { diag("Uses $func, which depends on ", join(', ', @{$file{uses_deps}{$func}})); } else { diag("Uses $func"); } } $warnings += hint($func); } unless ($opt{quiet}) { for $func (sort keys %{$file{uses_todo}}) { print "*** WARNING: Uses $func, which may not be portable below perl ", format_version($API{$func}{todo}), ", even with '$ppport'\n"; $warnings++; } } for $func (sort keys %{$file{needed_static}}) { my $message = ''; if (not exists $file{uses}{$func}) { $message = "No need to define NEED_$func if $func is never used"; } elsif (exists $file{needs}{$func} && $file{needs}{$func} ne 'static') { $message = "No need to define NEED_$func when already needed globally"; } if ($message) { diag($message); $file{changes} += ($c =~ s/^$HS*#$HS*define$HS+NEED_$func\b.*$LF//mg); } } for $func (sort keys %{$file{needed_global}}) { my $message = ''; if (not exists $global{uses}{$func}) { $message = "No need to define NEED_${func}_GLOBAL if $func is never used"; } elsif (exists $file{needs}{$func}) { if ($file{needs}{$func} eq 'extern') { $message = "No need to define NEED_${func}_GLOBAL when already needed globally"; } elsif ($file{needs}{$func} eq 'static') { $message = "No need to define NEED_${func}_GLOBAL when only used in this file"; } } if ($message) { diag($message); $file{changes} += ($c =~ s/^$HS*#$HS*define$HS+NEED_${func}_GLOBAL\b.*$LF//mg); } } $file{needs_inc_ppport} = keys %{$file{uses}}; if ($file{needs_inc_ppport}) { my $pp = ''; for $func (sort keys %{$file{needs}}) { my $type = $file{needs}{$func}; next if $type eq 'extern'; my $suffix = $type eq 'global' ? '_GLOBAL' : ''; unless (exists $file{"needed_$type"}{$func}) { if ($type eq 'global') { diag("Files [@{$global{needs}{$func}}] need $func, adding global request"); } else { diag("File needs $func, adding static request"); } $pp .= "#define NEED_$func$suffix\n"; } } if ($pp && ($c =~ s/^(?=$HS*#$HS*define$HS+NEED_\w+)/$pp/m)) { $pp = ''; $file{changes}++; } unless ($file{has_inc_ppport}) { diag("Needs to include '$ppport'"); $pp .= qq(#include "$ppport"\n) } if ($pp) { $file{changes} += ($c =~ s/^($HS*#$HS*define$HS+NEED_\w+.*?)^/$1$pp/ms) || ($c =~ s/^(?=$HS*#$HS*include.*\Q$ppport\E)/$pp/m) || ($c =~ s/^($HS*#$HS*include.*XSUB.*\s*?)^/$1$pp/m) || ($c =~ s/^/$pp/); } } else { if ($file{has_inc_ppport}) { diag("No need to include '$ppport'"); $file{changes} += ($c =~ s/^$HS*?#$HS*include.*\Q$ppport\E.*?$LF//m); } } # put back in our C comments my $ix; my $cppc = 0; my @ccom = @{$file{ccom}}; for $ix (0 .. $#ccom) { if (!$opt{cplusplus} && $ccom[$ix] =~ s!^//!!) { $cppc++; $file{changes} += $c =~ s/$rccs$ix$rcce/$ccs$ccom[$ix] $cce/; } else { $c =~ s/$rccs$ix$rcce/$ccom[$ix]/; } } if ($cppc) { my $s = $cppc != 1 ? 's' : ''; warning("Uses $cppc C++ style comment$s, which is not portable"); } my $s = $warnings != 1 ? 's' : ''; my $warn = $warnings ? " ($warnings warning$s)" : ''; info("Analysis completed$warn"); if ($file{changes}) { if (exists $opt{copy}) { my $newfile = "$filename$opt{copy}"; if (-e $newfile) { error("'$newfile' already exists, refusing to write copy of '$filename'"); } else { local *F; if (open F, ">$newfile") { info("Writing copy of '$filename' with changes to '$newfile'"); print F $c; close F; } else { error("Cannot open '$newfile' for writing: $!"); } } } elsif (exists $opt{patch} || $opt{changes}) { if (exists $opt{patch}) { unless ($patch_opened) { if (open PATCH, ">$opt{patch}") { $patch_opened = 1; } else { error("Cannot open '$opt{patch}' for writing: $!"); delete $opt{patch}; $opt{changes} = 1; goto fallback; } } mydiff(\*PATCH, $filename, $c); } else { fallback: info("Suggested changes:"); mydiff(\*STDOUT, $filename, $c); } } else { my $s = $file{changes} == 1 ? '' : 's'; info("$file{changes} potentially required change$s detected"); } } else { info("Looks good"); } } close PATCH if $patch_opened; exit 0; sub try_use { eval "use @_;"; return $@ eq '' } sub mydiff { local *F = shift; my($file, $str) = @_; my $diff; if (exists $opt{diff}) { $diff = run_diff($opt{diff}, $file, $str); } if (!defined $diff and try_use('Text::Diff')) { $diff = Text::Diff::diff($file, \$str, { STYLE => 'Unified' }); $diff = <
$tmp") { print F $str; close F; if (open F, "$prog $file $tmp |") { while () { s/\Q$tmp\E/$file.patched/; $diff .= $_; } close F; unlink $tmp; return $diff; } unlink $tmp; } else { error("Cannot open '$tmp' for writing: $!"); } return undef; } sub rec_depend { my($func, $seen) = @_; return () unless exists $depends{$func}; $seen = {%{$seen||{}}}; return () if $seen->{$func}++; my %s; grep !$s{$_}++, map { ($_, rec_depend($_, $seen)) } @{$depends{$func}}; } sub parse_version { my $ver = shift; if ($ver =~ /^(\d+)\.(\d+)\.(\d+)$/) { return ($1, $2, $3); } elsif ($ver !~ /^\d+\.[\d_]+$/) { die "cannot parse version '$ver'\n"; } $ver =~ s/_//g; $ver =~ s/$/000000/; my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/; $v = int $v; $s = int $s; if ($r < 5 || ($r == 5 && $v < 6)) { if ($s % 10) { die "cannot parse version '$ver'\n"; } } return ($r, $v, $s); } sub format_version { my $ver = shift; $ver =~ s/$/000000/; my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/; $v = int $v; $s = int $s; if ($r < 5 || ($r == 5 && $v < 6)) { if ($s % 10) { die "invalid version '$ver'\n"; } $s /= 10; $ver = sprintf "%d.%03d", $r, $v; $s > 0 and $ver .= sprintf "_%02d", $s; return $ver; } return sprintf "%d.%d.%d", $r, $v, $s; } sub info { $opt{quiet} and return; print @_, "\n"; } sub diag { $opt{quiet} and return; $opt{diag} and print @_, "\n"; } sub warning { $opt{quiet} and return; print "*** ", @_, "\n"; } sub error { print "*** ERROR: ", @_, "\n"; } my %given_hints; my %given_warnings; sub hint { $opt{quiet} and return; my $func = shift; my $rv = 0; if (exists $warnings{$func} && !$given_warnings{$func}++) { my $warn = $warnings{$func}; $warn =~ s!^!*** !mg; print "*** WARNING: $func\n", $warn; $rv++; } if ($opt{hints} && exists $hints{$func} && !$given_hints{$func}++) { my $hint = $hints{$func}; $hint =~ s/^/ /mg; print " --- hint for $func ---\n", $hint; } $rv; } sub usage { my($usage) = do { local(@ARGV,$/)=($0); <> } =~ /^=head\d$HS+SYNOPSIS\s*^(.*?)\s*^=/ms; my %M = ( 'I' => '*' ); $usage =~ s/^\s*perl\s+\S+/$^X $0/; $usage =~ s/([A-Z])<([^>]+)>/$M{$1}$2$M{$1}/g; print < }; my($copy) = $self =~ /^=head\d\s+COPYRIGHT\s*^(.*?)^=\w+/ms; $copy =~ s/^(?=\S+)/ /gms; $self =~ s/^$HS+Do NOT edit.*?(?=^-)/$copy/ms; $self =~ s/^SKIP.*(?=^__DATA__)/SKIP if (\@ARGV && \$ARGV[0] eq '--unstrip') { eval { require Devel::PPPort }; \$@ and die "Cannot require Devel::PPPort, please install.\\n"; if (eval \$Devel::PPPort::VERSION < $VERSION) { die "$0 was originally generated with Devel::PPPort $VERSION.\\n" . "Your Devel::PPPort is only version \$Devel::PPPort::VERSION.\\n" . "Please install a newer version, or --unstrip will not work.\\n"; } Devel::PPPort::WriteFile(\$0); exit 0; } print <$0" or die "cannot strip $0: $!\n"; print OUT "$pl$c\n"; exit 0; } __DATA__ */ #ifndef _P_P_PORTABILITY_H_ #define _P_P_PORTABILITY_H_ #ifndef DPPP_NAMESPACE # define DPPP_NAMESPACE DPPP_ #endif #define DPPP_CAT2(x,y) CAT2(x,y) #define DPPP_(name) DPPP_CAT2(DPPP_NAMESPACE, name) #ifndef PERL_REVISION # if !defined(__PATCHLEVEL_H_INCLUDED__) && !(defined(PATCHLEVEL) && defined(SUBVERSION)) # define PERL_PATCHLEVEL_H_IMPLICIT # include # endif # if !(defined(PERL_VERSION) || (defined(SUBVERSION) && defined(PATCHLEVEL))) # include # endif # ifndef PERL_REVISION # define PERL_REVISION (5) /* Replace: 1 */ # define PERL_VERSION PATCHLEVEL # define PERL_SUBVERSION SUBVERSION /* Replace PERL_PATCHLEVEL with PERL_VERSION */ /* Replace: 0 */ # endif #endif #define _dpppDEC2BCD(dec) ((((dec)/100)<<8)|((((dec)%100)/10)<<4)|((dec)%10)) #define PERL_BCDVERSION ((_dpppDEC2BCD(PERL_REVISION)<<24)|(_dpppDEC2BCD(PERL_VERSION)<<12)|_dpppDEC2BCD(PERL_SUBVERSION)) /* It is very unlikely that anyone will try to use this with Perl 6 (or greater), but who knows. */ #if PERL_REVISION != 5 # error ppport.h only works with Perl version 5 #endif /* PERL_REVISION != 5 */ #ifndef dTHR # define dTHR dNOOP #endif #ifndef dTHX # define dTHX dNOOP #endif #ifndef dTHXa # define dTHXa(x) dNOOP #endif #ifndef pTHX # define pTHX void #endif #ifndef pTHX_ # define pTHX_ #endif #ifndef aTHX # define aTHX #endif #ifndef aTHX_ # define aTHX_ #endif #if (PERL_BCDVERSION < 0x5006000) # ifdef USE_THREADS # define aTHXR thr # define aTHXR_ thr, # else # define aTHXR # define aTHXR_ # endif # define dTHXR dTHR #else # define aTHXR aTHX # define aTHXR_ aTHX_ # define dTHXR dTHX #endif #ifndef dTHXoa # define dTHXoa(x) dTHXa(x) #endif #ifdef I_LIMITS # include #endif #ifndef PERL_UCHAR_MIN # define PERL_UCHAR_MIN ((unsigned char)0) #endif #ifndef PERL_UCHAR_MAX # ifdef UCHAR_MAX # define PERL_UCHAR_MAX ((unsigned char)UCHAR_MAX) # else # ifdef MAXUCHAR # define PERL_UCHAR_MAX ((unsigned char)MAXUCHAR) # else # define PERL_UCHAR_MAX ((unsigned char)~(unsigned)0) # endif # endif #endif #ifndef PERL_USHORT_MIN # define PERL_USHORT_MIN ((unsigned short)0) #endif #ifndef PERL_USHORT_MAX # ifdef USHORT_MAX # define PERL_USHORT_MAX ((unsigned short)USHORT_MAX) # else # ifdef MAXUSHORT # define PERL_USHORT_MAX ((unsigned short)MAXUSHORT) # else # ifdef USHRT_MAX # define PERL_USHORT_MAX ((unsigned short)USHRT_MAX) # else # define PERL_USHORT_MAX ((unsigned short)~(unsigned)0) # endif # endif # endif #endif #ifndef PERL_SHORT_MAX # ifdef SHORT_MAX # define PERL_SHORT_MAX ((short)SHORT_MAX) # else # ifdef MAXSHORT /* Often used in */ # define PERL_SHORT_MAX ((short)MAXSHORT) # else # ifdef SHRT_MAX # define PERL_SHORT_MAX ((short)SHRT_MAX) # else # define PERL_SHORT_MAX ((short) (PERL_USHORT_MAX >> 1)) # endif # endif # endif #endif #ifndef PERL_SHORT_MIN # ifdef SHORT_MIN # define PERL_SHORT_MIN ((short)SHORT_MIN) # else # ifdef MINSHORT # define PERL_SHORT_MIN ((short)MINSHORT) # else # ifdef SHRT_MIN # define PERL_SHORT_MIN ((short)SHRT_MIN) # else # define PERL_SHORT_MIN (-PERL_SHORT_MAX - ((3 & -1) == 3)) # endif # endif # endif #endif #ifndef PERL_UINT_MAX # ifdef UINT_MAX # define PERL_UINT_MAX ((unsigned int)UINT_MAX) # else # ifdef MAXUINT # define PERL_UINT_MAX ((unsigned int)MAXUINT) # else # define PERL_UINT_MAX (~(unsigned int)0) # endif # endif #endif #ifndef PERL_UINT_MIN # define PERL_UINT_MIN ((unsigned int)0) #endif #ifndef PERL_INT_MAX # ifdef INT_MAX # define PERL_INT_MAX ((int)INT_MAX) # else # ifdef MAXINT /* Often used in */ # define PERL_INT_MAX ((int)MAXINT) # else # define PERL_INT_MAX ((int)(PERL_UINT_MAX >> 1)) # endif # endif #endif #ifndef PERL_INT_MIN # ifdef INT_MIN # define PERL_INT_MIN ((int)INT_MIN) # else # ifdef MININT # define PERL_INT_MIN ((int)MININT) # else # define PERL_INT_MIN (-PERL_INT_MAX - ((3 & -1) == 3)) # endif # endif #endif #ifndef PERL_ULONG_MAX # ifdef ULONG_MAX # define PERL_ULONG_MAX ((unsigned long)ULONG_MAX) # else # ifdef MAXULONG # define PERL_ULONG_MAX ((unsigned long)MAXULONG) # else # define PERL_ULONG_MAX (~(unsigned long)0) # endif # endif #endif #ifndef PERL_ULONG_MIN # define PERL_ULONG_MIN ((unsigned long)0L) #endif #ifndef PERL_LONG_MAX # ifdef LONG_MAX # define PERL_LONG_MAX ((long)LONG_MAX) # else # ifdef MAXLONG # define PERL_LONG_MAX ((long)MAXLONG) # else # define PERL_LONG_MAX ((long) (PERL_ULONG_MAX >> 1)) # endif # endif #endif #ifndef PERL_LONG_MIN # ifdef LONG_MIN # define PERL_LONG_MIN ((long)LONG_MIN) # else # ifdef MINLONG # define PERL_LONG_MIN ((long)MINLONG) # else # define PERL_LONG_MIN (-PERL_LONG_MAX - ((3 & -1) == 3)) # endif # endif #endif #if defined(HAS_QUAD) && (defined(convex) || defined(uts)) # ifndef PERL_UQUAD_MAX # ifdef ULONGLONG_MAX # define PERL_UQUAD_MAX ((unsigned long long)ULONGLONG_MAX) # else # ifdef MAXULONGLONG # define PERL_UQUAD_MAX ((unsigned long long)MAXULONGLONG) # else # define PERL_UQUAD_MAX (~(unsigned long long)0) # endif # endif # endif # ifndef PERL_UQUAD_MIN # define PERL_UQUAD_MIN ((unsigned long long)0L) # endif # ifndef PERL_QUAD_MAX # ifdef LONGLONG_MAX # define PERL_QUAD_MAX ((long long)LONGLONG_MAX) # else # ifdef MAXLONGLONG # define PERL_QUAD_MAX ((long long)MAXLONGLONG) # else # define PERL_QUAD_MAX ((long long) (PERL_UQUAD_MAX >> 1)) # endif # endif # endif # ifndef PERL_QUAD_MIN # ifdef LONGLONG_MIN # define PERL_QUAD_MIN ((long long)LONGLONG_MIN) # else # ifdef MINLONGLONG # define PERL_QUAD_MIN ((long long)MINLONGLONG) # else # define PERL_QUAD_MIN (-PERL_QUAD_MAX - ((3 & -1) == 3)) # endif # endif # endif #endif /* This is based on code from 5.003 perl.h */ #ifdef HAS_QUAD # ifdef cray #ifndef IVTYPE # define IVTYPE int #endif #ifndef IV_MIN # define IV_MIN PERL_INT_MIN #endif #ifndef IV_MAX # define IV_MAX PERL_INT_MAX #endif #ifndef UV_MIN # define UV_MIN PERL_UINT_MIN #endif #ifndef UV_MAX # define UV_MAX PERL_UINT_MAX #endif # ifdef INTSIZE #ifndef IVSIZE # define IVSIZE INTSIZE #endif # endif # else # if defined(convex) || defined(uts) #ifndef IVTYPE # define IVTYPE long long #endif #ifndef IV_MIN # define IV_MIN PERL_QUAD_MIN #endif #ifndef IV_MAX # define IV_MAX PERL_QUAD_MAX #endif #ifndef UV_MIN # define UV_MIN PERL_UQUAD_MIN #endif #ifndef UV_MAX # define UV_MAX PERL_UQUAD_MAX #endif # ifdef LONGLONGSIZE #ifndef IVSIZE # define IVSIZE LONGLONGSIZE #endif # endif # else #ifndef IVTYPE # define IVTYPE long #endif #ifndef IV_MIN # define IV_MIN PERL_LONG_MIN #endif #ifndef IV_MAX # define IV_MAX PERL_LONG_MAX #endif #ifndef UV_MIN # define UV_MIN PERL_ULONG_MIN #endif #ifndef UV_MAX # define UV_MAX PERL_ULONG_MAX #endif # ifdef LONGSIZE #ifndef IVSIZE # define IVSIZE LONGSIZE #endif # endif # endif # endif #ifndef IVSIZE # define IVSIZE 8 #endif #ifndef LONGSIZE # define LONGSIZE 8 #endif #ifndef PERL_QUAD_MIN # define PERL_QUAD_MIN IV_MIN #endif #ifndef PERL_QUAD_MAX # define PERL_QUAD_MAX IV_MAX #endif #ifndef PERL_UQUAD_MIN # define PERL_UQUAD_MIN UV_MIN #endif #ifndef PERL_UQUAD_MAX # define PERL_UQUAD_MAX UV_MAX #endif #else #ifndef IVTYPE # define IVTYPE long #endif #ifndef LONGSIZE # define LONGSIZE 4 #endif #ifndef IV_MIN # define IV_MIN PERL_LONG_MIN #endif #ifndef IV_MAX # define IV_MAX PERL_LONG_MAX #endif #ifndef UV_MIN # define UV_MIN PERL_ULONG_MIN #endif #ifndef UV_MAX # define UV_MAX PERL_ULONG_MAX #endif #endif #ifndef IVSIZE # ifdef LONGSIZE # define IVSIZE LONGSIZE # else # define IVSIZE 4 /* A bold guess, but the best we can make. */ # endif #endif #ifndef UVTYPE # define UVTYPE unsigned IVTYPE #endif #ifndef UVSIZE # define UVSIZE IVSIZE #endif #ifndef sv_setuv # define sv_setuv(sv, uv) \ STMT_START { \ UV TeMpUv = uv; \ if (TeMpUv <= IV_MAX) \ sv_setiv(sv, TeMpUv); \ else \ sv_setnv(sv, (double)TeMpUv); \ } STMT_END #endif #ifndef newSVuv # define newSVuv(uv) ((uv) <= IV_MAX ? newSViv((IV)uv) : newSVnv((NV)uv)) #endif #ifndef sv_2uv # define sv_2uv(sv) ((PL_Sv = (sv)), (UV) (SvNOK(PL_Sv) ? SvNV(PL_Sv) : sv_2nv(PL_Sv))) #endif #ifndef SvUVX # define SvUVX(sv) ((UV)SvIVX(sv)) #endif #ifndef SvUVXx # define SvUVXx(sv) SvUVX(sv) #endif #ifndef SvUV # define SvUV(sv) (SvIOK(sv) ? SvUVX(sv) : sv_2uv(sv)) #endif #ifndef SvUVx # define SvUVx(sv) ((PL_Sv = (sv)), SvUV(PL_Sv)) #endif /* Hint: sv_uv * Always use the SvUVx() macro instead of sv_uv(). */ #ifndef sv_uv # define sv_uv(sv) SvUVx(sv) #endif #if !defined(SvUOK) && defined(SvIOK_UV) # define SvUOK(sv) SvIOK_UV(sv) #endif #ifndef XST_mUV # define XST_mUV(i,v) (ST(i) = sv_2mortal(newSVuv(v)) ) #endif #ifndef XSRETURN_UV # define XSRETURN_UV(v) STMT_START { XST_mUV(0,v); XSRETURN(1); } STMT_END #endif #ifndef PUSHu # define PUSHu(u) STMT_START { sv_setuv(TARG, (UV)(u)); PUSHTARG; } STMT_END #endif #ifndef XPUSHu # define XPUSHu(u) STMT_START { sv_setuv(TARG, (UV)(u)); XPUSHTARG; } STMT_END #endif #ifdef HAS_MEMCMP #ifndef memNE # define memNE(s1,s2,l) (memcmp(s1,s2,l)) #endif #ifndef memEQ # define memEQ(s1,s2,l) (!memcmp(s1,s2,l)) #endif #else #ifndef memNE # define memNE(s1,s2,l) (bcmp(s1,s2,l)) #endif #ifndef memEQ # define memEQ(s1,s2,l) (!bcmp(s1,s2,l)) #endif #endif #ifndef memEQs # define memEQs(s1, l, s2) \ (sizeof(s2)-1 == l && memEQ(s1, (s2 ""), (sizeof(s2)-1))) #endif #ifndef memNEs # define memNEs(s1, l, s2) !memEQs(s1, l, s2) #endif #ifndef MoveD # define MoveD(s,d,n,t) memmove((char*)(d),(char*)(s), (n) * sizeof(t)) #endif #ifndef CopyD # define CopyD(s,d,n,t) memcpy((char*)(d),(char*)(s), (n) * sizeof(t)) #endif #ifdef HAS_MEMSET #ifndef ZeroD # define ZeroD(d,n,t) memzero((char*)(d), (n) * sizeof(t)) #endif #else #ifndef ZeroD # define ZeroD(d,n,t) ((void)memzero((char*)(d), (n) * sizeof(t)), d) #endif #endif #ifndef PoisonWith # define PoisonWith(d,n,t,b) (void)memset((char*)(d), (U8)(b), (n) * sizeof(t)) #endif #ifndef PoisonNew # define PoisonNew(d,n,t) PoisonWith(d,n,t,0xAB) #endif #ifndef PoisonFree # define PoisonFree(d,n,t) PoisonWith(d,n,t,0xEF) #endif #ifndef Poison # define Poison(d,n,t) PoisonFree(d,n,t) #endif #ifndef Newx # define Newx(v,n,t) New(0,v,n,t) #endif #ifndef Newxc # define Newxc(v,n,t,c) Newc(0,v,n,t,c) #endif #ifndef Newxz # define Newxz(v,n,t) Newz(0,v,n,t) #endif #ifndef PERL_UNUSED_DECL # ifdef HASATTRIBUTE # if (defined(__GNUC__) && defined(__cplusplus)) || defined(__INTEL_COMPILER) # define PERL_UNUSED_DECL # else # define PERL_UNUSED_DECL __attribute__((unused)) # endif # else # define PERL_UNUSED_DECL # endif #endif #ifndef PERL_UNUSED_ARG # if defined(lint) && defined(S_SPLINT_S) /* www.splint.org */ # include # define PERL_UNUSED_ARG(x) NOTE(ARGUNUSED(x)) # else # define PERL_UNUSED_ARG(x) ((void)x) # endif #endif #ifndef PERL_UNUSED_VAR # define PERL_UNUSED_VAR(x) ((void)x) #endif #ifndef PERL_UNUSED_CONTEXT # ifdef USE_ITHREADS # define PERL_UNUSED_CONTEXT PERL_UNUSED_ARG(my_perl) # else # define PERL_UNUSED_CONTEXT # endif #endif #ifndef NOOP # define NOOP /*EMPTY*/(void)0 #endif #ifndef dNOOP # define dNOOP extern int /*@unused@*/ Perl___notused PERL_UNUSED_DECL #endif #ifndef NVTYPE # if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE) # define NVTYPE long double # else # define NVTYPE double # endif typedef NVTYPE NV; #endif #ifndef INT2PTR # if (IVSIZE == PTRSIZE) && (UVSIZE == PTRSIZE) # define PTRV UV # define INT2PTR(any,d) (any)(d) # else # if PTRSIZE == LONGSIZE # define PTRV unsigned long # else # define PTRV unsigned # endif # define INT2PTR(any,d) (any)(PTRV)(d) # endif #endif #ifndef PTR2ul # if PTRSIZE == LONGSIZE # define PTR2ul(p) (unsigned long)(p) # else # define PTR2ul(p) INT2PTR(unsigned long,p) # endif #endif #ifndef PTR2nat # define PTR2nat(p) (PTRV)(p) #endif #ifndef NUM2PTR # define NUM2PTR(any,d) (any)PTR2nat(d) #endif #ifndef PTR2IV # define PTR2IV(p) INT2PTR(IV,p) #endif #ifndef PTR2UV # define PTR2UV(p) INT2PTR(UV,p) #endif #ifndef PTR2NV # define PTR2NV(p) NUM2PTR(NV,p) #endif #undef START_EXTERN_C #undef END_EXTERN_C #undef EXTERN_C #ifdef __cplusplus # define START_EXTERN_C extern "C" { # define END_EXTERN_C } # define EXTERN_C extern "C" #else # define START_EXTERN_C # define END_EXTERN_C # define EXTERN_C extern #endif #if defined(PERL_GCC_PEDANTIC) # ifndef PERL_GCC_BRACE_GROUPS_FORBIDDEN # define PERL_GCC_BRACE_GROUPS_FORBIDDEN # endif #endif #if defined(__GNUC__) && !defined(PERL_GCC_BRACE_GROUPS_FORBIDDEN) && !defined(__cplusplus) # ifndef PERL_USE_GCC_BRACE_GROUPS # define PERL_USE_GCC_BRACE_GROUPS # endif #endif #undef STMT_START #undef STMT_END #ifdef PERL_USE_GCC_BRACE_GROUPS # define STMT_START (void)( /* gcc supports ``({ STATEMENTS; })'' */ # define STMT_END ) #else # if defined(VOIDFLAGS) && (VOIDFLAGS) && (defined(sun) || defined(__sun__)) && !defined(__GNUC__) # define STMT_START if (1) # define STMT_END else (void)0 # else # define STMT_START do # define STMT_END while (0) # endif #endif #ifndef boolSV # define boolSV(b) ((b) ? &PL_sv_yes : &PL_sv_no) #endif /* DEFSV appears first in 5.004_56 */ #ifndef DEFSV # define DEFSV GvSV(PL_defgv) #endif #ifndef SAVE_DEFSV # define SAVE_DEFSV SAVESPTR(GvSV(PL_defgv)) #endif #ifndef DEFSV_set # define DEFSV_set(sv) (DEFSV = (sv)) #endif /* Older perls (<=5.003) lack AvFILLp */ #ifndef AvFILLp # define AvFILLp AvFILL #endif #ifndef ERRSV # define ERRSV get_sv("@",FALSE) #endif /* Hint: gv_stashpvn * This function's backport doesn't support the length parameter, but * rather ignores it. Portability can only be ensured if the length * parameter is used for speed reasons, but the length can always be * correctly computed from the string argument. */ #ifndef gv_stashpvn # define gv_stashpvn(str,len,create) gv_stashpv(str,create) #endif /* Replace: 1 */ #ifndef get_cv # define get_cv perl_get_cv #endif #ifndef get_sv # define get_sv perl_get_sv #endif #ifndef get_av # define get_av perl_get_av #endif #ifndef get_hv # define get_hv perl_get_hv #endif /* Replace: 0 */ #ifndef dUNDERBAR # define dUNDERBAR dNOOP #endif #ifndef UNDERBAR # define UNDERBAR DEFSV #endif #ifndef dAX # define dAX I32 ax = MARK - PL_stack_base + 1 #endif #ifndef dITEMS # define dITEMS I32 items = SP - MARK #endif #ifndef dXSTARG # define dXSTARG SV * targ = sv_newmortal() #endif #ifndef dAXMARK # define dAXMARK I32 ax = POPMARK; \ register SV ** const mark = PL_stack_base + ax++ #endif #ifndef XSprePUSH # define XSprePUSH (sp = PL_stack_base + ax - 1) #endif #if (PERL_BCDVERSION < 0x5005000) # undef XSRETURN # define XSRETURN(off) \ STMT_START { \ PL_stack_sp = PL_stack_base + ax + ((off) - 1); \ return; \ } STMT_END #endif #ifndef XSPROTO # define XSPROTO(name) void name(pTHX_ CV* cv) #endif #ifndef SVfARG # define SVfARG(p) ((void*)(p)) #endif #ifndef PERL_ABS # define PERL_ABS(x) ((x) < 0 ? -(x) : (x)) #endif #ifndef dVAR # define dVAR dNOOP #endif #ifndef SVf # define SVf "_" #endif #ifndef UTF8_MAXBYTES # define UTF8_MAXBYTES UTF8_MAXLEN #endif #ifndef CPERLscope # define CPERLscope(x) x #endif #ifndef PERL_HASH # define PERL_HASH(hash,str,len) \ STMT_START { \ const char *s_PeRlHaSh = str; \ I32 i_PeRlHaSh = len; \ U32 hash_PeRlHaSh = 0; \ while (i_PeRlHaSh--) \ hash_PeRlHaSh = hash_PeRlHaSh * 33 + *s_PeRlHaSh++; \ (hash) = hash_PeRlHaSh; \ } STMT_END #endif #ifndef PERLIO_FUNCS_DECL # ifdef PERLIO_FUNCS_CONST # define PERLIO_FUNCS_DECL(funcs) const PerlIO_funcs funcs # define PERLIO_FUNCS_CAST(funcs) (PerlIO_funcs*)(funcs) # else # define PERLIO_FUNCS_DECL(funcs) PerlIO_funcs funcs # define PERLIO_FUNCS_CAST(funcs) (funcs) # endif #endif /* provide these typedefs for older perls */ #if (PERL_BCDVERSION < 0x5009003) # ifdef ARGSproto typedef OP* (CPERLscope(*Perl_ppaddr_t))(ARGSproto); # else typedef OP* (CPERLscope(*Perl_ppaddr_t))(pTHX); # endif typedef OP* (CPERLscope(*Perl_check_t)) (pTHX_ OP*); #endif #ifndef isPSXSPC # define isPSXSPC(c) (isSPACE(c) || (c) == '\v') #endif #ifndef isBLANK # define isBLANK(c) ((c) == ' ' || (c) == '\t') #endif #ifdef EBCDIC #ifndef isALNUMC # define isALNUMC(c) isalnum(c) #endif #ifndef isASCII # define isASCII(c) isascii(c) #endif #ifndef isCNTRL # define isCNTRL(c) iscntrl(c) #endif #ifndef isGRAPH # define isGRAPH(c) isgraph(c) #endif #ifndef isPRINT # define isPRINT(c) isprint(c) #endif #ifndef isPUNCT # define isPUNCT(c) ispunct(c) #endif #ifndef isXDIGIT # define isXDIGIT(c) isxdigit(c) #endif #else # if (PERL_BCDVERSION < 0x5010000) /* Hint: isPRINT * The implementation in older perl versions includes all of the * isSPACE() characters, which is wrong. The version provided by * Devel::PPPort always overrides a present buggy version. */ # undef isPRINT # endif #ifdef HAS_QUAD # define WIDEST_UTYPE U64TYPE #else # define WIDEST_UTYPE U32 #endif #ifndef isALNUMC # define isALNUMC(c) (isALPHA(c) || isDIGIT(c)) #endif #ifndef isASCII # define isASCII(c) ((WIDEST_UTYPE) (c) <= 127) #endif #ifndef isCNTRL # define isCNTRL(c) ((WIDEST_UTYPE) (c) < ' ' || (c) == 127) #endif #ifndef isGRAPH # define isGRAPH(c) (isALNUM(c) || isPUNCT(c)) #endif #ifndef isPRINT # define isPRINT(c) (((c) >= 32 && (c) < 127)) #endif #ifndef isPUNCT # define isPUNCT(c) (((c) >= 33 && (c) <= 47) || ((c) >= 58 && (c) <= 64) || ((c) >= 91 && (c) <= 96) || ((c) >= 123 && (c) <= 126)) #endif #ifndef isXDIGIT # define isXDIGIT(c) (isDIGIT(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) #endif #endif #ifndef PERL_SIGNALS_UNSAFE_FLAG #define PERL_SIGNALS_UNSAFE_FLAG 0x0001 #if (PERL_BCDVERSION < 0x5008000) # define D_PPP_PERL_SIGNALS_INIT PERL_SIGNALS_UNSAFE_FLAG #else # define D_PPP_PERL_SIGNALS_INIT 0 #endif #if defined(NEED_PL_signals) static U32 DPPP_(my_PL_signals) = D_PPP_PERL_SIGNALS_INIT; #elif defined(NEED_PL_signals_GLOBAL) U32 DPPP_(my_PL_signals) = D_PPP_PERL_SIGNALS_INIT; #else extern U32 DPPP_(my_PL_signals); #endif #define PL_signals DPPP_(my_PL_signals) #endif /* Hint: PL_ppaddr * Calling an op via PL_ppaddr requires passing a context argument * for threaded builds. Since the context argument is different for * 5.005 perls, you can use aTHXR (supplied by ppport.h), which will * automatically be defined as the correct argument. */ #if (PERL_BCDVERSION <= 0x5005005) /* Replace: 1 */ # define PL_ppaddr ppaddr # define PL_no_modify no_modify /* Replace: 0 */ #endif #if (PERL_BCDVERSION <= 0x5004005) /* Replace: 1 */ # define PL_DBsignal DBsignal # define PL_DBsingle DBsingle # define PL_DBsub DBsub # define PL_DBtrace DBtrace # define PL_Sv Sv # define PL_bufend bufend # define PL_bufptr bufptr # define PL_compiling compiling # define PL_copline copline # define PL_curcop curcop # define PL_curstash curstash # define PL_debstash debstash # define PL_defgv defgv # define PL_diehook diehook # define PL_dirty dirty # define PL_dowarn dowarn # define PL_errgv errgv # define PL_error_count error_count # define PL_expect expect # define PL_hexdigit hexdigit # define PL_hints hints # define PL_in_my in_my # define PL_laststatval laststatval # define PL_lex_state lex_state # define PL_lex_stuff lex_stuff # define PL_linestr linestr # define PL_na na # define PL_perl_destruct_level perl_destruct_level # define PL_perldb perldb # define PL_rsfp_filters rsfp_filters # define PL_rsfp rsfp # define PL_stack_base stack_base # define PL_stack_sp stack_sp # define PL_statcache statcache # define PL_stdingv stdingv # define PL_sv_arenaroot sv_arenaroot # define PL_sv_no sv_no # define PL_sv_undef sv_undef # define PL_sv_yes sv_yes # define PL_tainted tainted # define PL_tainting tainting # define PL_tokenbuf tokenbuf /* Replace: 0 */ #endif /* Warning: PL_parser * For perl versions earlier than 5.9.5, this is an always * non-NULL dummy. Also, it cannot be dereferenced. Don't * use it if you can avoid is and unless you absolutely know * what you're doing. * If you always check that PL_parser is non-NULL, you can * define DPPP_PL_parser_NO_DUMMY to avoid the creation of * a dummy parser structure. */ #if (PERL_BCDVERSION >= 0x5009005) # ifdef DPPP_PL_parser_NO_DUMMY # define D_PPP_my_PL_parser_var(var) ((PL_parser ? PL_parser : \ (croak("panic: PL_parser == NULL in %s:%d", \ __FILE__, __LINE__), (yy_parser *) NULL))->var) # else # ifdef DPPP_PL_parser_NO_DUMMY_WARNING # define D_PPP_parser_dummy_warning(var) # else # define D_PPP_parser_dummy_warning(var) \ warn("warning: dummy PL_" #var " used in %s:%d", __FILE__, __LINE__), # endif # define D_PPP_my_PL_parser_var(var) ((PL_parser ? PL_parser : \ (D_PPP_parser_dummy_warning(var) &DPPP_(dummy_PL_parser)))->var) #if defined(NEED_PL_parser) static yy_parser DPPP_(dummy_PL_parser); #elif defined(NEED_PL_parser_GLOBAL) yy_parser DPPP_(dummy_PL_parser); #else extern yy_parser DPPP_(dummy_PL_parser); #endif # endif /* PL_expect, PL_copline, PL_rsfp, PL_rsfp_filters, PL_linestr, PL_bufptr, PL_bufend, PL_lex_state, PL_lex_stuff, PL_tokenbuf depends on PL_parser */ /* Warning: PL_expect, PL_copline, PL_rsfp, PL_rsfp_filters, PL_linestr, PL_bufptr, PL_bufend, PL_lex_state, PL_lex_stuff, PL_tokenbuf * Do not use this variable unless you know exactly what you're * doint. It is internal to the perl parser and may change or even * be removed in the future. As of perl 5.9.5, you have to check * for (PL_parser != NULL) for this variable to have any effect. * An always non-NULL PL_parser dummy is provided for earlier * perl versions. * If PL_parser is NULL when you try to access this variable, a * dummy is being accessed instead and a warning is issued unless * you define DPPP_PL_parser_NO_DUMMY_WARNING. * If DPPP_PL_parser_NO_DUMMY is defined, the code trying to access * this variable will croak with a panic message. */ # define PL_expect D_PPP_my_PL_parser_var(expect) # define PL_copline D_PPP_my_PL_parser_var(copline) # define PL_rsfp D_PPP_my_PL_parser_var(rsfp) # define PL_rsfp_filters D_PPP_my_PL_parser_var(rsfp_filters) # define PL_linestr D_PPP_my_PL_parser_var(linestr) # define PL_bufptr D_PPP_my_PL_parser_var(bufptr) # define PL_bufend D_PPP_my_PL_parser_var(bufend) # define PL_lex_state D_PPP_my_PL_parser_var(lex_state) # define PL_lex_stuff D_PPP_my_PL_parser_var(lex_stuff) # define PL_tokenbuf D_PPP_my_PL_parser_var(tokenbuf) # define PL_in_my D_PPP_my_PL_parser_var(in_my) # define PL_in_my_stash D_PPP_my_PL_parser_var(in_my_stash) # define PL_error_count D_PPP_my_PL_parser_var(error_count) #else /* ensure that PL_parser != NULL and cannot be dereferenced */ # define PL_parser ((void *) 1) #endif #ifndef mPUSHs # define mPUSHs(s) PUSHs(sv_2mortal(s)) #endif #ifndef PUSHmortal # define PUSHmortal PUSHs(sv_newmortal()) #endif #ifndef mPUSHp # define mPUSHp(p,l) sv_setpvn(PUSHmortal, (p), (l)) #endif #ifndef mPUSHn # define mPUSHn(n) sv_setnv(PUSHmortal, (NV)(n)) #endif #ifndef mPUSHi # define mPUSHi(i) sv_setiv(PUSHmortal, (IV)(i)) #endif #ifndef mPUSHu # define mPUSHu(u) sv_setuv(PUSHmortal, (UV)(u)) #endif #ifndef mXPUSHs # define mXPUSHs(s) XPUSHs(sv_2mortal(s)) #endif #ifndef XPUSHmortal # define XPUSHmortal XPUSHs(sv_newmortal()) #endif #ifndef mXPUSHp # define mXPUSHp(p,l) STMT_START { EXTEND(sp,1); sv_setpvn(PUSHmortal, (p), (l)); } STMT_END #endif #ifndef mXPUSHn # define mXPUSHn(n) STMT_START { EXTEND(sp,1); sv_setnv(PUSHmortal, (NV)(n)); } STMT_END #endif #ifndef mXPUSHi # define mXPUSHi(i) STMT_START { EXTEND(sp,1); sv_setiv(PUSHmortal, (IV)(i)); } STMT_END #endif #ifndef mXPUSHu # define mXPUSHu(u) STMT_START { EXTEND(sp,1); sv_setuv(PUSHmortal, (UV)(u)); } STMT_END #endif /* Replace: 1 */ #ifndef call_sv # define call_sv perl_call_sv #endif #ifndef call_pv # define call_pv perl_call_pv #endif #ifndef call_argv # define call_argv perl_call_argv #endif #ifndef call_method # define call_method perl_call_method #endif #ifndef eval_sv # define eval_sv perl_eval_sv #endif /* Replace: 0 */ #ifndef PERL_LOADMOD_DENY # define PERL_LOADMOD_DENY 0x1 #endif #ifndef PERL_LOADMOD_NOIMPORT # define PERL_LOADMOD_NOIMPORT 0x2 #endif #ifndef PERL_LOADMOD_IMPORT_OPS # define PERL_LOADMOD_IMPORT_OPS 0x4 #endif #ifndef G_METHOD # define G_METHOD 64 # ifdef call_sv # undef call_sv # endif # if (PERL_BCDVERSION < 0x5006000) # define call_sv(sv, flags) ((flags) & G_METHOD ? perl_call_method((char *) SvPV_nolen_const(sv), \ (flags) & ~G_METHOD) : perl_call_sv(sv, flags)) # else # define call_sv(sv, flags) ((flags) & G_METHOD ? Perl_call_method(aTHX_ (char *) SvPV_nolen_const(sv), \ (flags) & ~G_METHOD) : Perl_call_sv(aTHX_ sv, flags)) # endif #endif /* Replace perl_eval_pv with eval_pv */ #ifndef eval_pv #if defined(NEED_eval_pv) static SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error); static #else extern SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error); #endif #ifdef eval_pv # undef eval_pv #endif #define eval_pv(a,b) DPPP_(my_eval_pv)(aTHX_ a,b) #define Perl_eval_pv DPPP_(my_eval_pv) #if defined(NEED_eval_pv) || defined(NEED_eval_pv_GLOBAL) SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error) { dSP; SV* sv = newSVpv(p, 0); PUSHMARK(sp); eval_sv(sv, G_SCALAR); SvREFCNT_dec(sv); SPAGAIN; sv = POPs; PUTBACK; if (croak_on_error && SvTRUE(GvSV(errgv))) croak(SvPVx(GvSV(errgv), na)); return sv; } #endif #endif #ifndef vload_module #if defined(NEED_vload_module) static void DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args); static #else extern void DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args); #endif #ifdef vload_module # undef vload_module #endif #define vload_module(a,b,c,d) DPPP_(my_vload_module)(aTHX_ a,b,c,d) #define Perl_vload_module DPPP_(my_vload_module) #if defined(NEED_vload_module) || defined(NEED_vload_module_GLOBAL) void DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args) { dTHR; dVAR; OP *veop, *imop; OP * const modname = newSVOP(OP_CONST, 0, name); /* 5.005 has a somewhat hacky force_normal that doesn't croak on SvREADONLY() if PL_compling is true. Current perls take care in ck_require() to correctly turn off SvREADONLY before calling force_normal_flags(). This seems a better fix than fudging PL_compling */ SvREADONLY_off(((SVOP*)modname)->op_sv); modname->op_private |= OPpCONST_BARE; if (ver) { veop = newSVOP(OP_CONST, 0, ver); } else veop = NULL; if (flags & PERL_LOADMOD_NOIMPORT) { imop = sawparens(newNULLLIST()); } else if (flags & PERL_LOADMOD_IMPORT_OPS) { imop = va_arg(*args, OP*); } else { SV *sv; imop = NULL; sv = va_arg(*args, SV*); while (sv) { imop = append_elem(OP_LIST, imop, newSVOP(OP_CONST, 0, sv)); sv = va_arg(*args, SV*); } } { const line_t ocopline = PL_copline; COP * const ocurcop = PL_curcop; const int oexpect = PL_expect; #if (PERL_BCDVERSION >= 0x5004000) utilize(!(flags & PERL_LOADMOD_DENY), start_subparse(FALSE, 0), veop, modname, imop); #else utilize(!(flags & PERL_LOADMOD_DENY), start_subparse(), modname, imop); #endif PL_expect = oexpect; PL_copline = ocopline; PL_curcop = ocurcop; } } #endif #endif #ifndef load_module #if defined(NEED_load_module) static void DPPP_(my_load_module)(U32 flags, SV *name, SV *ver, ...); static #else extern void DPPP_(my_load_module)(U32 flags, SV *name, SV *ver, ...); #endif #ifdef load_module # undef load_module #endif #define load_module DPPP_(my_load_module) #define Perl_load_module DPPP_(my_load_module) #if defined(NEED_load_module) || defined(NEED_load_module_GLOBAL) void DPPP_(my_load_module)(U32 flags, SV *name, SV *ver, ...) { va_list args; va_start(args, ver); vload_module(flags, name, ver, &args); va_end(args); } #endif #endif #ifndef newRV_inc # define newRV_inc(sv) newRV(sv) /* Replace */ #endif #ifndef newRV_noinc #if defined(NEED_newRV_noinc) static SV * DPPP_(my_newRV_noinc)(SV *sv); static #else extern SV * DPPP_(my_newRV_noinc)(SV *sv); #endif #ifdef newRV_noinc # undef newRV_noinc #endif #define newRV_noinc(a) DPPP_(my_newRV_noinc)(aTHX_ a) #define Perl_newRV_noinc DPPP_(my_newRV_noinc) #if defined(NEED_newRV_noinc) || defined(NEED_newRV_noinc_GLOBAL) SV * DPPP_(my_newRV_noinc)(SV *sv) { SV *rv = (SV *)newRV(sv); SvREFCNT_dec(sv); return rv; } #endif #endif /* Hint: newCONSTSUB * Returns a CV* as of perl-5.7.1. This return value is not supported * by Devel::PPPort. */ /* newCONSTSUB from IO.xs is in the core starting with 5.004_63 */ #if (PERL_BCDVERSION < 0x5004063) && (PERL_BCDVERSION != 0x5004005) #if defined(NEED_newCONSTSUB) static void DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv); static #else extern void DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv); #endif #ifdef newCONSTSUB # undef newCONSTSUB #endif #define newCONSTSUB(a,b,c) DPPP_(my_newCONSTSUB)(aTHX_ a,b,c) #define Perl_newCONSTSUB DPPP_(my_newCONSTSUB) #if defined(NEED_newCONSTSUB) || defined(NEED_newCONSTSUB_GLOBAL) /* This is just a trick to avoid a dependency of newCONSTSUB on PL_parser */ /* (There's no PL_parser in perl < 5.005, so this is completely safe) */ #define D_PPP_PL_copline PL_copline void DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv) { U32 oldhints = PL_hints; HV *old_cop_stash = PL_curcop->cop_stash; HV *old_curstash = PL_curstash; line_t oldline = PL_curcop->cop_line; PL_curcop->cop_line = D_PPP_PL_copline; PL_hints &= ~HINT_BLOCK_SCOPE; if (stash) PL_curstash = PL_curcop->cop_stash = stash; newSUB( #if (PERL_BCDVERSION < 0x5003022) start_subparse(), #elif (PERL_BCDVERSION == 0x5003022) start_subparse(0), #else /* 5.003_23 onwards */ start_subparse(FALSE, 0), #endif newSVOP(OP_CONST, 0, newSVpv((char *) name, 0)), newSVOP(OP_CONST, 0, &PL_sv_no), /* SvPV(&PL_sv_no) == "" -- GMB */ newSTATEOP(0, Nullch, newSVOP(OP_CONST, 0, sv)) ); PL_hints = oldhints; PL_curcop->cop_stash = old_cop_stash; PL_curstash = old_curstash; PL_curcop->cop_line = oldline; } #endif #endif /* * Boilerplate macros for initializing and accessing interpreter-local * data from C. All statics in extensions should be reworked to use * this, if you want to make the extension thread-safe. See ext/re/re.xs * for an example of the use of these macros. * * Code that uses these macros is responsible for the following: * 1. #define MY_CXT_KEY to a unique string, e.g. "DynaLoader_guts" * 2. Declare a typedef named my_cxt_t that is a structure that contains * all the data that needs to be interpreter-local. * 3. Use the START_MY_CXT macro after the declaration of my_cxt_t. * 4. Use the MY_CXT_INIT macro such that it is called exactly once * (typically put in the BOOT: section). * 5. Use the members of the my_cxt_t structure everywhere as * MY_CXT.member. * 6. Use the dMY_CXT macro (a declaration) in all the functions that * access MY_CXT. */ #if defined(MULTIPLICITY) || defined(PERL_OBJECT) || \ defined(PERL_CAPI) || defined(PERL_IMPLICIT_CONTEXT) #ifndef START_MY_CXT /* This must appear in all extensions that define a my_cxt_t structure, * right after the definition (i.e. at file scope). The non-threads * case below uses it to declare the data as static. */ #define START_MY_CXT #if (PERL_BCDVERSION < 0x5004068) /* Fetches the SV that keeps the per-interpreter data. */ #define dMY_CXT_SV \ SV *my_cxt_sv = get_sv(MY_CXT_KEY, FALSE) #else /* >= perl5.004_68 */ #define dMY_CXT_SV \ SV *my_cxt_sv = *hv_fetch(PL_modglobal, MY_CXT_KEY, \ sizeof(MY_CXT_KEY)-1, TRUE) #endif /* < perl5.004_68 */ /* This declaration should be used within all functions that use the * interpreter-local data. */ #define dMY_CXT \ dMY_CXT_SV; \ my_cxt_t *my_cxtp = INT2PTR(my_cxt_t*,SvUV(my_cxt_sv)) /* Creates and zeroes the per-interpreter data. * (We allocate my_cxtp in a Perl SV so that it will be released when * the interpreter goes away.) */ #define MY_CXT_INIT \ dMY_CXT_SV; \ /* newSV() allocates one more than needed */ \ my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1));\ Zero(my_cxtp, 1, my_cxt_t); \ sv_setuv(my_cxt_sv, PTR2UV(my_cxtp)) /* This macro must be used to access members of the my_cxt_t structure. * e.g. MYCXT.some_data */ #define MY_CXT (*my_cxtp) /* Judicious use of these macros can reduce the number of times dMY_CXT * is used. Use is similar to pTHX, aTHX etc. */ #define pMY_CXT my_cxt_t *my_cxtp #define pMY_CXT_ pMY_CXT, #define _pMY_CXT ,pMY_CXT #define aMY_CXT my_cxtp #define aMY_CXT_ aMY_CXT, #define _aMY_CXT ,aMY_CXT #endif /* START_MY_CXT */ #ifndef MY_CXT_CLONE /* Clones the per-interpreter data. */ #define MY_CXT_CLONE \ dMY_CXT_SV; \ my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1));\ Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t);\ sv_setuv(my_cxt_sv, PTR2UV(my_cxtp)) #endif #else /* single interpreter */ #ifndef START_MY_CXT #define START_MY_CXT static my_cxt_t my_cxt; #define dMY_CXT_SV dNOOP #define dMY_CXT dNOOP #define MY_CXT_INIT NOOP #define MY_CXT my_cxt #define pMY_CXT void #define pMY_CXT_ #define _pMY_CXT #define aMY_CXT #define aMY_CXT_ #define _aMY_CXT #endif /* START_MY_CXT */ #ifndef MY_CXT_CLONE #define MY_CXT_CLONE NOOP #endif #endif #ifndef IVdf # if IVSIZE == LONGSIZE # define IVdf "ld" # define UVuf "lu" # define UVof "lo" # define UVxf "lx" # define UVXf "lX" # elif IVSIZE == INTSIZE # define IVdf "d" # define UVuf "u" # define UVof "o" # define UVxf "x" # define UVXf "X" # else # error "cannot define IV/UV formats" # endif #endif #ifndef NVef # if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE) && \ defined(PERL_PRIfldbl) && (PERL_BCDVERSION != 0x5006000) /* Not very likely, but let's try anyway. */ # define NVef PERL_PRIeldbl # define NVff PERL_PRIfldbl # define NVgf PERL_PRIgldbl # else # define NVef "e" # define NVff "f" # define NVgf "g" # endif #endif #ifndef SvREFCNT_inc # ifdef PERL_USE_GCC_BRACE_GROUPS # define SvREFCNT_inc(sv) \ ({ \ SV * const _sv = (SV*)(sv); \ if (_sv) \ (SvREFCNT(_sv))++; \ _sv; \ }) # else # define SvREFCNT_inc(sv) \ ((PL_Sv=(SV*)(sv)) ? (++(SvREFCNT(PL_Sv)),PL_Sv) : NULL) # endif #endif #ifndef SvREFCNT_inc_simple # ifdef PERL_USE_GCC_BRACE_GROUPS # define SvREFCNT_inc_simple(sv) \ ({ \ if (sv) \ (SvREFCNT(sv))++; \ (SV *)(sv); \ }) # else # define SvREFCNT_inc_simple(sv) \ ((sv) ? (SvREFCNT(sv)++,(SV*)(sv)) : NULL) # endif #endif #ifndef SvREFCNT_inc_NN # ifdef PERL_USE_GCC_BRACE_GROUPS # define SvREFCNT_inc_NN(sv) \ ({ \ SV * const _sv = (SV*)(sv); \ SvREFCNT(_sv)++; \ _sv; \ }) # else # define SvREFCNT_inc_NN(sv) \ (PL_Sv=(SV*)(sv),++(SvREFCNT(PL_Sv)),PL_Sv) # endif #endif #ifndef SvREFCNT_inc_void # ifdef PERL_USE_GCC_BRACE_GROUPS # define SvREFCNT_inc_void(sv) \ ({ \ SV * const _sv = (SV*)(sv); \ if (_sv) \ (void)(SvREFCNT(_sv)++); \ }) # else # define SvREFCNT_inc_void(sv) \ (void)((PL_Sv=(SV*)(sv)) ? ++(SvREFCNT(PL_Sv)) : 0) # endif #endif #ifndef SvREFCNT_inc_simple_void # define SvREFCNT_inc_simple_void(sv) STMT_START { if (sv) SvREFCNT(sv)++; } STMT_END #endif #ifndef SvREFCNT_inc_simple_NN # define SvREFCNT_inc_simple_NN(sv) (++SvREFCNT(sv), (SV*)(sv)) #endif #ifndef SvREFCNT_inc_void_NN # define SvREFCNT_inc_void_NN(sv) (void)(++SvREFCNT((SV*)(sv))) #endif #ifndef SvREFCNT_inc_simple_void_NN # define SvREFCNT_inc_simple_void_NN(sv) (void)(++SvREFCNT((SV*)(sv))) #endif #ifndef newSV_type #if defined(NEED_newSV_type) static SV* DPPP_(my_newSV_type)(pTHX_ svtype const t); static #else extern SV* DPPP_(my_newSV_type)(pTHX_ svtype const t); #endif #ifdef newSV_type # undef newSV_type #endif #define newSV_type(a) DPPP_(my_newSV_type)(aTHX_ a) #define Perl_newSV_type DPPP_(my_newSV_type) #if defined(NEED_newSV_type) || defined(NEED_newSV_type_GLOBAL) SV* DPPP_(my_newSV_type)(pTHX_ svtype const t) { SV* const sv = newSV(0); sv_upgrade(sv, t); return sv; } #endif #endif #if (PERL_BCDVERSION < 0x5006000) # define D_PPP_CONSTPV_ARG(x) ((char *) (x)) #else # define D_PPP_CONSTPV_ARG(x) (x) #endif #ifndef newSVpvn # define newSVpvn(data,len) ((data) \ ? ((len) ? newSVpv((data), (len)) : newSVpv("", 0)) \ : newSV(0)) #endif #ifndef newSVpvn_utf8 # define newSVpvn_utf8(s, len, u) newSVpvn_flags((s), (len), (u) ? SVf_UTF8 : 0) #endif #ifndef SVf_UTF8 # define SVf_UTF8 0 #endif #ifndef newSVpvn_flags #if defined(NEED_newSVpvn_flags) static SV * DPPP_(my_newSVpvn_flags)(pTHX_ const char *s, STRLEN len, U32 flags); static #else extern SV * DPPP_(my_newSVpvn_flags)(pTHX_ const char *s, STRLEN len, U32 flags); #endif #ifdef newSVpvn_flags # undef newSVpvn_flags #endif #define newSVpvn_flags(a,b,c) DPPP_(my_newSVpvn_flags)(aTHX_ a,b,c) #define Perl_newSVpvn_flags DPPP_(my_newSVpvn_flags) #if defined(NEED_newSVpvn_flags) || defined(NEED_newSVpvn_flags_GLOBAL) SV * DPPP_(my_newSVpvn_flags)(pTHX_ const char *s, STRLEN len, U32 flags) { SV *sv = newSVpvn(D_PPP_CONSTPV_ARG(s), len); SvFLAGS(sv) |= (flags & SVf_UTF8); return (flags & SVs_TEMP) ? sv_2mortal(sv) : sv; } #endif #endif /* Backwards compatibility stuff... :-( */ #if !defined(NEED_sv_2pv_flags) && defined(NEED_sv_2pv_nolen) # define NEED_sv_2pv_flags #endif #if !defined(NEED_sv_2pv_flags_GLOBAL) && defined(NEED_sv_2pv_nolen_GLOBAL) # define NEED_sv_2pv_flags_GLOBAL #endif /* Hint: sv_2pv_nolen * Use the SvPV_nolen() or SvPV_nolen_const() macros instead of sv_2pv_nolen(). */ #ifndef sv_2pv_nolen # define sv_2pv_nolen(sv) SvPV_nolen(sv) #endif #ifdef SvPVbyte /* Hint: SvPVbyte * Does not work in perl-5.6.1, ppport.h implements a version * borrowed from perl-5.7.3. */ #if (PERL_BCDVERSION < 0x5007000) #if defined(NEED_sv_2pvbyte) static char * DPPP_(my_sv_2pvbyte)(pTHX_ SV *sv, STRLEN *lp); static #else extern char * DPPP_(my_sv_2pvbyte)(pTHX_ SV *sv, STRLEN *lp); #endif #ifdef sv_2pvbyte # undef sv_2pvbyte #endif #define sv_2pvbyte(a,b) DPPP_(my_sv_2pvbyte)(aTHX_ a,b) #define Perl_sv_2pvbyte DPPP_(my_sv_2pvbyte) #if defined(NEED_sv_2pvbyte) || defined(NEED_sv_2pvbyte_GLOBAL) char * DPPP_(my_sv_2pvbyte)(pTHX_ SV *sv, STRLEN *lp) { sv_utf8_downgrade(sv,0); return SvPV(sv,*lp); } #endif /* Hint: sv_2pvbyte * Use the SvPVbyte() macro instead of sv_2pvbyte(). */ #undef SvPVbyte #define SvPVbyte(sv, lp) \ ((SvFLAGS(sv) & (SVf_POK|SVf_UTF8)) == (SVf_POK) \ ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_2pvbyte(sv, &lp)) #endif #else # define SvPVbyte SvPV # define sv_2pvbyte sv_2pv #endif #ifndef sv_2pvbyte_nolen # define sv_2pvbyte_nolen(sv) sv_2pv_nolen(sv) #endif /* Hint: sv_pvn * Always use the SvPV() macro instead of sv_pvn(). */ /* Hint: sv_pvn_force * Always use the SvPV_force() macro instead of sv_pvn_force(). */ /* If these are undefined, they're not handled by the core anyway */ #ifndef SV_IMMEDIATE_UNREF # define SV_IMMEDIATE_UNREF 0 #endif #ifndef SV_GMAGIC # define SV_GMAGIC 0 #endif #ifndef SV_COW_DROP_PV # define SV_COW_DROP_PV 0 #endif #ifndef SV_UTF8_NO_ENCODING # define SV_UTF8_NO_ENCODING 0 #endif #ifndef SV_NOSTEAL # define SV_NOSTEAL 0 #endif #ifndef SV_CONST_RETURN # define SV_CONST_RETURN 0 #endif #ifndef SV_MUTABLE_RETURN # define SV_MUTABLE_RETURN 0 #endif #ifndef SV_SMAGIC # define SV_SMAGIC 0 #endif #ifndef SV_HAS_TRAILING_NUL # define SV_HAS_TRAILING_NUL 0 #endif #ifndef SV_COW_SHARED_HASH_KEYS # define SV_COW_SHARED_HASH_KEYS 0 #endif #if (PERL_BCDVERSION < 0x5007002) #if defined(NEED_sv_2pv_flags) static char * DPPP_(my_sv_2pv_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags); static #else extern char * DPPP_(my_sv_2pv_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags); #endif #ifdef sv_2pv_flags # undef sv_2pv_flags #endif #define sv_2pv_flags(a,b,c) DPPP_(my_sv_2pv_flags)(aTHX_ a,b,c) #define Perl_sv_2pv_flags DPPP_(my_sv_2pv_flags) #if defined(NEED_sv_2pv_flags) || defined(NEED_sv_2pv_flags_GLOBAL) char * DPPP_(my_sv_2pv_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags) { STRLEN n_a = (STRLEN) flags; return sv_2pv(sv, lp ? lp : &n_a); } #endif #if defined(NEED_sv_pvn_force_flags) static char * DPPP_(my_sv_pvn_force_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags); static #else extern char * DPPP_(my_sv_pvn_force_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags); #endif #ifdef sv_pvn_force_flags # undef sv_pvn_force_flags #endif #define sv_pvn_force_flags(a,b,c) DPPP_(my_sv_pvn_force_flags)(aTHX_ a,b,c) #define Perl_sv_pvn_force_flags DPPP_(my_sv_pvn_force_flags) #if defined(NEED_sv_pvn_force_flags) || defined(NEED_sv_pvn_force_flags_GLOBAL) char * DPPP_(my_sv_pvn_force_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags) { STRLEN n_a = (STRLEN) flags; return sv_pvn_force(sv, lp ? lp : &n_a); } #endif #endif #if (PERL_BCDVERSION < 0x5008008) || ( (PERL_BCDVERSION >= 0x5009000) && (PERL_BCDVERSION < 0x5009003) ) # define DPPP_SVPV_NOLEN_LP_ARG &PL_na #else # define DPPP_SVPV_NOLEN_LP_ARG 0 #endif #ifndef SvPV_const # define SvPV_const(sv, lp) SvPV_flags_const(sv, lp, SV_GMAGIC) #endif #ifndef SvPV_mutable # define SvPV_mutable(sv, lp) SvPV_flags_mutable(sv, lp, SV_GMAGIC) #endif #ifndef SvPV_flags # define SvPV_flags(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_2pv_flags(sv, &lp, flags)) #endif #ifndef SvPV_flags_const # define SvPV_flags_const(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX_const(sv)) : \ (const char*) sv_2pv_flags(sv, &lp, flags|SV_CONST_RETURN)) #endif #ifndef SvPV_flags_const_nolen # define SvPV_flags_const_nolen(sv, flags) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? SvPVX_const(sv) : \ (const char*) sv_2pv_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, flags|SV_CONST_RETURN)) #endif #ifndef SvPV_flags_mutable # define SvPV_flags_mutable(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX_mutable(sv)) : \ sv_2pv_flags(sv, &lp, flags|SV_MUTABLE_RETURN)) #endif #ifndef SvPV_force # define SvPV_force(sv, lp) SvPV_force_flags(sv, lp, SV_GMAGIC) #endif #ifndef SvPV_force_nolen # define SvPV_force_nolen(sv) SvPV_force_flags_nolen(sv, SV_GMAGIC) #endif #ifndef SvPV_force_mutable # define SvPV_force_mutable(sv, lp) SvPV_force_flags_mutable(sv, lp, SV_GMAGIC) #endif #ifndef SvPV_force_nomg # define SvPV_force_nomg(sv, lp) SvPV_force_flags(sv, lp, 0) #endif #ifndef SvPV_force_nomg_nolen # define SvPV_force_nomg_nolen(sv) SvPV_force_flags_nolen(sv, 0) #endif #ifndef SvPV_force_flags # define SvPV_force_flags(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK|SVf_THINKFIRST)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_pvn_force_flags(sv, &lp, flags)) #endif #ifndef SvPV_force_flags_nolen # define SvPV_force_flags_nolen(sv, flags) \ ((SvFLAGS(sv) & (SVf_POK|SVf_THINKFIRST)) == SVf_POK \ ? SvPVX(sv) : sv_pvn_force_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, flags)) #endif #ifndef SvPV_force_flags_mutable # define SvPV_force_flags_mutable(sv, lp, flags) \ ((SvFLAGS(sv) & (SVf_POK|SVf_THINKFIRST)) == SVf_POK \ ? ((lp = SvCUR(sv)), SvPVX_mutable(sv)) \ : sv_pvn_force_flags(sv, &lp, flags|SV_MUTABLE_RETURN)) #endif #ifndef SvPV_nolen # define SvPV_nolen(sv) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? SvPVX(sv) : sv_2pv_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, SV_GMAGIC)) #endif #ifndef SvPV_nolen_const # define SvPV_nolen_const(sv) \ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? SvPVX_const(sv) : sv_2pv_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, SV_GMAGIC|SV_CONST_RETURN)) #endif #ifndef SvPV_nomg # define SvPV_nomg(sv, lp) SvPV_flags(sv, lp, 0) #endif #ifndef SvPV_nomg_const # define SvPV_nomg_const(sv, lp) SvPV_flags_const(sv, lp, 0) #endif #ifndef SvPV_nomg_const_nolen # define SvPV_nomg_const_nolen(sv) SvPV_flags_const_nolen(sv, 0) #endif #ifndef SvPV_nomg_nolen # define SvPV_nomg_nolen(sv) ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \ ? SvPVX(sv) : sv_2pv_flags(sv, DPPP_SVPV_NOLEN_LP_ARG, 0)) #endif #ifndef SvPV_renew # define SvPV_renew(sv,n) STMT_START { SvLEN_set(sv, n); \ SvPV_set((sv), (char *) saferealloc( \ (Malloc_t)SvPVX(sv), (MEM_SIZE)((n)))); \ } STMT_END #endif #ifndef SvMAGIC_set # define SvMAGIC_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_PVMG); \ (((XPVMG*) SvANY(sv))->xmg_magic = (val)); } STMT_END #endif #if (PERL_BCDVERSION < 0x5009003) #ifndef SvPVX_const # define SvPVX_const(sv) ((const char*) (0 + SvPVX(sv))) #endif #ifndef SvPVX_mutable # define SvPVX_mutable(sv) (0 + SvPVX(sv)) #endif #ifndef SvRV_set # define SvRV_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_RV); \ (((XRV*) SvANY(sv))->xrv_rv = (val)); } STMT_END #endif #else #ifndef SvPVX_const # define SvPVX_const(sv) ((const char*)((sv)->sv_u.svu_pv)) #endif #ifndef SvPVX_mutable # define SvPVX_mutable(sv) ((sv)->sv_u.svu_pv) #endif #ifndef SvRV_set # define SvRV_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_RV); \ ((sv)->sv_u.svu_rv = (val)); } STMT_END #endif #endif #ifndef SvSTASH_set # define SvSTASH_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_PVMG); \ (((XPVMG*) SvANY(sv))->xmg_stash = (val)); } STMT_END #endif #if (PERL_BCDVERSION < 0x5004000) #ifndef SvUV_set # define SvUV_set(sv, val) \ STMT_START { assert(SvTYPE(sv) == SVt_IV || SvTYPE(sv) >= SVt_PVIV); \ (((XPVIV*) SvANY(sv))->xiv_iv = (IV) (val)); } STMT_END #endif #else #ifndef SvUV_set # define SvUV_set(sv, val) \ STMT_START { assert(SvTYPE(sv) == SVt_IV || SvTYPE(sv) >= SVt_PVIV); \ (((XPVUV*) SvANY(sv))->xuv_uv = (val)); } STMT_END #endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(vnewSVpvf) #if defined(NEED_vnewSVpvf) static SV * DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args); static #else extern SV * DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args); #endif #ifdef vnewSVpvf # undef vnewSVpvf #endif #define vnewSVpvf(a,b) DPPP_(my_vnewSVpvf)(aTHX_ a,b) #define Perl_vnewSVpvf DPPP_(my_vnewSVpvf) #if defined(NEED_vnewSVpvf) || defined(NEED_vnewSVpvf_GLOBAL) SV * DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args) { register SV *sv = newSV(0); sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)); return sv; } #endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vcatpvf) # define sv_vcatpvf(sv, pat, args) sv_vcatpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)) #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vsetpvf) # define sv_vsetpvf(sv, pat, args) sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)) #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_catpvf_mg) #if defined(NEED_sv_catpvf_mg) static void DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...); static #else extern void DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...); #endif #define Perl_sv_catpvf_mg DPPP_(my_sv_catpvf_mg) #if defined(NEED_sv_catpvf_mg) || defined(NEED_sv_catpvf_mg_GLOBAL) void DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...) { va_list args; va_start(args, pat); sv_vcatpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*)); SvSETMAGIC(sv); va_end(args); } #endif #endif #ifdef PERL_IMPLICIT_CONTEXT #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_catpvf_mg_nocontext) #if defined(NEED_sv_catpvf_mg_nocontext) static void DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...); static #else extern void DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...); #endif #define sv_catpvf_mg_nocontext DPPP_(my_sv_catpvf_mg_nocontext) #define Perl_sv_catpvf_mg_nocontext DPPP_(my_sv_catpvf_mg_nocontext) #if defined(NEED_sv_catpvf_mg_nocontext) || defined(NEED_sv_catpvf_mg_nocontext_GLOBAL) void DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...) { dTHX; va_list args; va_start(args, pat); sv_vcatpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*)); SvSETMAGIC(sv); va_end(args); } #endif #endif #endif /* sv_catpvf_mg depends on sv_catpvf_mg_nocontext */ #ifndef sv_catpvf_mg # ifdef PERL_IMPLICIT_CONTEXT # define sv_catpvf_mg Perl_sv_catpvf_mg_nocontext # else # define sv_catpvf_mg Perl_sv_catpvf_mg # endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vcatpvf_mg) # define sv_vcatpvf_mg(sv, pat, args) \ STMT_START { \ sv_vcatpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)); \ SvSETMAGIC(sv); \ } STMT_END #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_setpvf_mg) #if defined(NEED_sv_setpvf_mg) static void DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...); static #else extern void DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...); #endif #define Perl_sv_setpvf_mg DPPP_(my_sv_setpvf_mg) #if defined(NEED_sv_setpvf_mg) || defined(NEED_sv_setpvf_mg_GLOBAL) void DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...) { va_list args; va_start(args, pat); sv_vsetpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*)); SvSETMAGIC(sv); va_end(args); } #endif #endif #ifdef PERL_IMPLICIT_CONTEXT #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_setpvf_mg_nocontext) #if defined(NEED_sv_setpvf_mg_nocontext) static void DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...); static #else extern void DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...); #endif #define sv_setpvf_mg_nocontext DPPP_(my_sv_setpvf_mg_nocontext) #define Perl_sv_setpvf_mg_nocontext DPPP_(my_sv_setpvf_mg_nocontext) #if defined(NEED_sv_setpvf_mg_nocontext) || defined(NEED_sv_setpvf_mg_nocontext_GLOBAL) void DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...) { dTHX; va_list args; va_start(args, pat); sv_vsetpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*)); SvSETMAGIC(sv); va_end(args); } #endif #endif #endif /* sv_setpvf_mg depends on sv_setpvf_mg_nocontext */ #ifndef sv_setpvf_mg # ifdef PERL_IMPLICIT_CONTEXT # define sv_setpvf_mg Perl_sv_setpvf_mg_nocontext # else # define sv_setpvf_mg Perl_sv_setpvf_mg # endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vsetpvf_mg) # define sv_vsetpvf_mg(sv, pat, args) \ STMT_START { \ sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)); \ SvSETMAGIC(sv); \ } STMT_END #endif /* Hint: newSVpvn_share * The SVs created by this function only mimic the behaviour of * shared PVs without really being shared. Only use if you know * what you're doing. */ #ifndef newSVpvn_share #if defined(NEED_newSVpvn_share) static SV * DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash); static #else extern SV * DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash); #endif #ifdef newSVpvn_share # undef newSVpvn_share #endif #define newSVpvn_share(a,b,c) DPPP_(my_newSVpvn_share)(aTHX_ a,b,c) #define Perl_newSVpvn_share DPPP_(my_newSVpvn_share) #if defined(NEED_newSVpvn_share) || defined(NEED_newSVpvn_share_GLOBAL) SV * DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash) { SV *sv; if (len < 0) len = -len; if (!hash) PERL_HASH(hash, (char*) src, len); sv = newSVpvn((char *) src, len); sv_upgrade(sv, SVt_PVIV); SvIVX(sv) = hash; SvREADONLY_on(sv); SvPOK_on(sv); return sv; } #endif #endif #ifndef SvSHARED_HASH # define SvSHARED_HASH(sv) (0 + SvUVX(sv)) #endif #ifndef HvNAME_get # define HvNAME_get(hv) HvNAME(hv) #endif #ifndef HvNAMELEN_get # define HvNAMELEN_get(hv) (HvNAME_get(hv) ? (I32)strlen(HvNAME_get(hv)) : 0) #endif #ifndef GvSVn # define GvSVn(gv) GvSV(gv) #endif #ifndef isGV_with_GP # define isGV_with_GP(gv) isGV(gv) #endif #ifndef gv_fetchpvn_flags # define gv_fetchpvn_flags(name, len, flags, svt) gv_fetchpv(name, flags, svt) #endif #ifndef gv_fetchsv # define gv_fetchsv(name, flags, svt) gv_fetchpv(SvPV_nolen_const(name), flags, svt) #endif #ifndef get_cvn_flags # define get_cvn_flags(name, namelen, flags) get_cv(name, flags) #endif #ifndef WARN_ALL # define WARN_ALL 0 #endif #ifndef WARN_CLOSURE # define WARN_CLOSURE 1 #endif #ifndef WARN_DEPRECATED # define WARN_DEPRECATED 2 #endif #ifndef WARN_EXITING # define WARN_EXITING 3 #endif #ifndef WARN_GLOB # define WARN_GLOB 4 #endif #ifndef WARN_IO # define WARN_IO 5 #endif #ifndef WARN_CLOSED # define WARN_CLOSED 6 #endif #ifndef WARN_EXEC # define WARN_EXEC 7 #endif #ifndef WARN_LAYER # define WARN_LAYER 8 #endif #ifndef WARN_NEWLINE # define WARN_NEWLINE 9 #endif #ifndef WARN_PIPE # define WARN_PIPE 10 #endif #ifndef WARN_UNOPENED # define WARN_UNOPENED 11 #endif #ifndef WARN_MISC # define WARN_MISC 12 #endif #ifndef WARN_NUMERIC # define WARN_NUMERIC 13 #endif #ifndef WARN_ONCE # define WARN_ONCE 14 #endif #ifndef WARN_OVERFLOW # define WARN_OVERFLOW 15 #endif #ifndef WARN_PACK # define WARN_PACK 16 #endif #ifndef WARN_PORTABLE # define WARN_PORTABLE 17 #endif #ifndef WARN_RECURSION # define WARN_RECURSION 18 #endif #ifndef WARN_REDEFINE # define WARN_REDEFINE 19 #endif #ifndef WARN_REGEXP # define WARN_REGEXP 20 #endif #ifndef WARN_SEVERE # define WARN_SEVERE 21 #endif #ifndef WARN_DEBUGGING # define WARN_DEBUGGING 22 #endif #ifndef WARN_INPLACE # define WARN_INPLACE 23 #endif #ifndef WARN_INTERNAL # define WARN_INTERNAL 24 #endif #ifndef WARN_MALLOC # define WARN_MALLOC 25 #endif #ifndef WARN_SIGNAL # define WARN_SIGNAL 26 #endif #ifndef WARN_SUBSTR # define WARN_SUBSTR 27 #endif #ifndef WARN_SYNTAX # define WARN_SYNTAX 28 #endif #ifndef WARN_AMBIGUOUS # define WARN_AMBIGUOUS 29 #endif #ifndef WARN_BAREWORD # define WARN_BAREWORD 30 #endif #ifndef WARN_DIGIT # define WARN_DIGIT 31 #endif #ifndef WARN_PARENTHESIS # define WARN_PARENTHESIS 32 #endif #ifndef WARN_PRECEDENCE # define WARN_PRECEDENCE 33 #endif #ifndef WARN_PRINTF # define WARN_PRINTF 34 #endif #ifndef WARN_PROTOTYPE # define WARN_PROTOTYPE 35 #endif #ifndef WARN_QW # define WARN_QW 36 #endif #ifndef WARN_RESERVED # define WARN_RESERVED 37 #endif #ifndef WARN_SEMICOLON # define WARN_SEMICOLON 38 #endif #ifndef WARN_TAINT # define WARN_TAINT 39 #endif #ifndef WARN_THREADS # define WARN_THREADS 40 #endif #ifndef WARN_UNINITIALIZED # define WARN_UNINITIALIZED 41 #endif #ifndef WARN_UNPACK # define WARN_UNPACK 42 #endif #ifndef WARN_UNTIE # define WARN_UNTIE 43 #endif #ifndef WARN_UTF8 # define WARN_UTF8 44 #endif #ifndef WARN_VOID # define WARN_VOID 45 #endif #ifndef WARN_ASSERTIONS # define WARN_ASSERTIONS 46 #endif #ifndef packWARN # define packWARN(a) (a) #endif #ifndef ckWARN # ifdef G_WARN_ON # define ckWARN(a) (PL_dowarn & G_WARN_ON) # else # define ckWARN(a) PL_dowarn # endif #endif #if (PERL_BCDVERSION >= 0x5004000) && !defined(warner) #if defined(NEED_warner) static void DPPP_(my_warner)(U32 err, const char *pat, ...); static #else extern void DPPP_(my_warner)(U32 err, const char *pat, ...); #endif #define Perl_warner DPPP_(my_warner) #if defined(NEED_warner) || defined(NEED_warner_GLOBAL) void DPPP_(my_warner)(U32 err, const char *pat, ...) { SV *sv; va_list args; PERL_UNUSED_ARG(err); va_start(args, pat); sv = vnewSVpvf(pat, &args); va_end(args); sv_2mortal(sv); warn("%s", SvPV_nolen(sv)); } #define warner Perl_warner #define Perl_warner_nocontext Perl_warner #endif #endif /* concatenating with "" ensures that only literal strings are accepted as argument * note that STR_WITH_LEN() can't be used as argument to macros or functions that * under some configurations might be macros */ #ifndef STR_WITH_LEN # define STR_WITH_LEN(s) (s ""), (sizeof(s)-1) #endif #ifndef newSVpvs # define newSVpvs(str) newSVpvn(str "", sizeof(str) - 1) #endif #ifndef newSVpvs_flags # define newSVpvs_flags(str, flags) newSVpvn_flags(str "", sizeof(str) - 1, flags) #endif #ifndef newSVpvs_share # define newSVpvs_share(str) newSVpvn_share(str "", sizeof(str) - 1, 0) #endif #ifndef sv_catpvs # define sv_catpvs(sv, str) sv_catpvn(sv, str "", sizeof(str) - 1) #endif #ifndef sv_setpvs # define sv_setpvs(sv, str) sv_setpvn(sv, str "", sizeof(str) - 1) #endif #ifndef hv_fetchs # define hv_fetchs(hv, key, lval) hv_fetch(hv, key "", sizeof(key) - 1, lval) #endif #ifndef hv_stores # define hv_stores(hv, key, val) hv_store(hv, key "", sizeof(key) - 1, val, 0) #endif #ifndef gv_fetchpvs # define gv_fetchpvs(name, flags, svt) gv_fetchpvn_flags(name "", sizeof(name) - 1, flags, svt) #endif #ifndef gv_stashpvs # define gv_stashpvs(name, flags) gv_stashpvn(name "", sizeof(name) - 1, flags) #endif #ifndef get_cvs # define get_cvs(name, flags) get_cvn_flags(name "", sizeof(name)-1, flags) #endif #ifndef SvGETMAGIC # define SvGETMAGIC(x) STMT_START { if (SvGMAGICAL(x)) mg_get(x); } STMT_END #endif #ifndef PERL_MAGIC_sv # define PERL_MAGIC_sv '\0' #endif #ifndef PERL_MAGIC_overload # define PERL_MAGIC_overload 'A' #endif #ifndef PERL_MAGIC_overload_elem # define PERL_MAGIC_overload_elem 'a' #endif #ifndef PERL_MAGIC_overload_table # define PERL_MAGIC_overload_table 'c' #endif #ifndef PERL_MAGIC_bm # define PERL_MAGIC_bm 'B' #endif #ifndef PERL_MAGIC_regdata # define PERL_MAGIC_regdata 'D' #endif #ifndef PERL_MAGIC_regdatum # define PERL_MAGIC_regdatum 'd' #endif #ifndef PERL_MAGIC_env # define PERL_MAGIC_env 'E' #endif #ifndef PERL_MAGIC_envelem # define PERL_MAGIC_envelem 'e' #endif #ifndef PERL_MAGIC_fm # define PERL_MAGIC_fm 'f' #endif #ifndef PERL_MAGIC_regex_global # define PERL_MAGIC_regex_global 'g' #endif #ifndef PERL_MAGIC_isa # define PERL_MAGIC_isa 'I' #endif #ifndef PERL_MAGIC_isaelem # define PERL_MAGIC_isaelem 'i' #endif #ifndef PERL_MAGIC_nkeys # define PERL_MAGIC_nkeys 'k' #endif #ifndef PERL_MAGIC_dbfile # define PERL_MAGIC_dbfile 'L' #endif #ifndef PERL_MAGIC_dbline # define PERL_MAGIC_dbline 'l' #endif #ifndef PERL_MAGIC_mutex # define PERL_MAGIC_mutex 'm' #endif #ifndef PERL_MAGIC_shared # define PERL_MAGIC_shared 'N' #endif #ifndef PERL_MAGIC_shared_scalar # define PERL_MAGIC_shared_scalar 'n' #endif #ifndef PERL_MAGIC_collxfrm # define PERL_MAGIC_collxfrm 'o' #endif #ifndef PERL_MAGIC_tied # define PERL_MAGIC_tied 'P' #endif #ifndef PERL_MAGIC_tiedelem # define PERL_MAGIC_tiedelem 'p' #endif #ifndef PERL_MAGIC_tiedscalar # define PERL_MAGIC_tiedscalar 'q' #endif #ifndef PERL_MAGIC_qr # define PERL_MAGIC_qr 'r' #endif #ifndef PERL_MAGIC_sig # define PERL_MAGIC_sig 'S' #endif #ifndef PERL_MAGIC_sigelem # define PERL_MAGIC_sigelem 's' #endif #ifndef PERL_MAGIC_taint # define PERL_MAGIC_taint 't' #endif #ifndef PERL_MAGIC_uvar # define PERL_MAGIC_uvar 'U' #endif #ifndef PERL_MAGIC_uvar_elem # define PERL_MAGIC_uvar_elem 'u' #endif #ifndef PERL_MAGIC_vstring # define PERL_MAGIC_vstring 'V' #endif #ifndef PERL_MAGIC_vec # define PERL_MAGIC_vec 'v' #endif #ifndef PERL_MAGIC_utf8 # define PERL_MAGIC_utf8 'w' #endif #ifndef PERL_MAGIC_substr # define PERL_MAGIC_substr 'x' #endif #ifndef PERL_MAGIC_defelem # define PERL_MAGIC_defelem 'y' #endif #ifndef PERL_MAGIC_glob # define PERL_MAGIC_glob '*' #endif #ifndef PERL_MAGIC_arylen # define PERL_MAGIC_arylen '#' #endif #ifndef PERL_MAGIC_pos # define PERL_MAGIC_pos '.' #endif #ifndef PERL_MAGIC_backref # define PERL_MAGIC_backref '<' #endif #ifndef PERL_MAGIC_ext # define PERL_MAGIC_ext '~' #endif /* That's the best we can do... */ #ifndef sv_catpvn_nomg # define sv_catpvn_nomg sv_catpvn #endif #ifndef sv_catsv_nomg # define sv_catsv_nomg sv_catsv #endif #ifndef sv_setsv_nomg # define sv_setsv_nomg sv_setsv #endif #ifndef sv_pvn_nomg # define sv_pvn_nomg sv_pvn #endif #ifndef SvIV_nomg # define SvIV_nomg SvIV #endif #ifndef SvUV_nomg # define SvUV_nomg SvUV #endif #ifndef sv_catpv_mg # define sv_catpv_mg(sv, ptr) \ STMT_START { \ SV *TeMpSv = sv; \ sv_catpv(TeMpSv,ptr); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_catpvn_mg # define sv_catpvn_mg(sv, ptr, len) \ STMT_START { \ SV *TeMpSv = sv; \ sv_catpvn(TeMpSv,ptr,len); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_catsv_mg # define sv_catsv_mg(dsv, ssv) \ STMT_START { \ SV *TeMpSv = dsv; \ sv_catsv(TeMpSv,ssv); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setiv_mg # define sv_setiv_mg(sv, i) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setiv(TeMpSv,i); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setnv_mg # define sv_setnv_mg(sv, num) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setnv(TeMpSv,num); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setpv_mg # define sv_setpv_mg(sv, ptr) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setpv(TeMpSv,ptr); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setpvn_mg # define sv_setpvn_mg(sv, ptr, len) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setpvn(TeMpSv,ptr,len); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setsv_mg # define sv_setsv_mg(dsv, ssv) \ STMT_START { \ SV *TeMpSv = dsv; \ sv_setsv(TeMpSv,ssv); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_setuv_mg # define sv_setuv_mg(sv, i) \ STMT_START { \ SV *TeMpSv = sv; \ sv_setuv(TeMpSv,i); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef sv_usepvn_mg # define sv_usepvn_mg(sv, ptr, len) \ STMT_START { \ SV *TeMpSv = sv; \ sv_usepvn(TeMpSv,ptr,len); \ SvSETMAGIC(TeMpSv); \ } STMT_END #endif #ifndef SvVSTRING_mg # define SvVSTRING_mg(sv) (SvMAGICAL(sv) ? mg_find(sv, PERL_MAGIC_vstring) : NULL) #endif /* Hint: sv_magic_portable * This is a compatibility function that is only available with * Devel::PPPort. It is NOT in the perl core. * Its purpose is to mimic the 5.8.0 behaviour of sv_magic() when * it is being passed a name pointer with namlen == 0. In that * case, perl 5.8.0 and later store the pointer, not a copy of it. * The compatibility can be provided back to perl 5.004. With * earlier versions, the code will not compile. */ #if (PERL_BCDVERSION < 0x5004000) /* code that uses sv_magic_portable will not compile */ #elif (PERL_BCDVERSION < 0x5008000) # define sv_magic_portable(sv, obj, how, name, namlen) \ STMT_START { \ SV *SvMp_sv = (sv); \ char *SvMp_name = (char *) (name); \ I32 SvMp_namlen = (namlen); \ if (SvMp_name && SvMp_namlen == 0) \ { \ MAGIC *mg; \ sv_magic(SvMp_sv, obj, how, 0, 0); \ mg = SvMAGIC(SvMp_sv); \ mg->mg_len = -42; /* XXX: this is the tricky part */ \ mg->mg_ptr = SvMp_name; \ } \ else \ { \ sv_magic(SvMp_sv, obj, how, SvMp_name, SvMp_namlen); \ } \ } STMT_END #else # define sv_magic_portable(a, b, c, d, e) sv_magic(a, b, c, d, e) #endif #ifdef USE_ITHREADS #ifndef CopFILE # define CopFILE(c) ((c)->cop_file) #endif #ifndef CopFILEGV # define CopFILEGV(c) (CopFILE(c) ? gv_fetchfile(CopFILE(c)) : Nullgv) #endif #ifndef CopFILE_set # define CopFILE_set(c,pv) ((c)->cop_file = savepv(pv)) #endif #ifndef CopFILESV # define CopFILESV(c) (CopFILE(c) ? GvSV(gv_fetchfile(CopFILE(c))) : Nullsv) #endif #ifndef CopFILEAV # define CopFILEAV(c) (CopFILE(c) ? GvAV(gv_fetchfile(CopFILE(c))) : Nullav) #endif #ifndef CopSTASHPV # define CopSTASHPV(c) ((c)->cop_stashpv) #endif #ifndef CopSTASHPV_set # define CopSTASHPV_set(c,pv) ((c)->cop_stashpv = ((pv) ? savepv(pv) : Nullch)) #endif #ifndef CopSTASH # define CopSTASH(c) (CopSTASHPV(c) ? gv_stashpv(CopSTASHPV(c),GV_ADD) : Nullhv) #endif #ifndef CopSTASH_set # define CopSTASH_set(c,hv) CopSTASHPV_set(c, (hv) ? HvNAME(hv) : Nullch) #endif #ifndef CopSTASH_eq # define CopSTASH_eq(c,hv) ((hv) && (CopSTASHPV(c) == HvNAME(hv) \ || (CopSTASHPV(c) && HvNAME(hv) \ && strEQ(CopSTASHPV(c), HvNAME(hv))))) #endif #else #ifndef CopFILEGV # define CopFILEGV(c) ((c)->cop_filegv) #endif #ifndef CopFILEGV_set # define CopFILEGV_set(c,gv) ((c)->cop_filegv = (GV*)SvREFCNT_inc(gv)) #endif #ifndef CopFILE_set # define CopFILE_set(c,pv) CopFILEGV_set((c), gv_fetchfile(pv)) #endif #ifndef CopFILESV # define CopFILESV(c) (CopFILEGV(c) ? GvSV(CopFILEGV(c)) : Nullsv) #endif #ifndef CopFILEAV # define CopFILEAV(c) (CopFILEGV(c) ? GvAV(CopFILEGV(c)) : Nullav) #endif #ifndef CopFILE # define CopFILE(c) (CopFILESV(c) ? SvPVX(CopFILESV(c)) : Nullch) #endif #ifndef CopSTASH # define CopSTASH(c) ((c)->cop_stash) #endif #ifndef CopSTASH_set # define CopSTASH_set(c,hv) ((c)->cop_stash = (hv)) #endif #ifndef CopSTASHPV # define CopSTASHPV(c) (CopSTASH(c) ? HvNAME(CopSTASH(c)) : Nullch) #endif #ifndef CopSTASHPV_set # define CopSTASHPV_set(c,pv) CopSTASH_set((c), gv_stashpv(pv,GV_ADD)) #endif #ifndef CopSTASH_eq # define CopSTASH_eq(c,hv) (CopSTASH(c) == (hv)) #endif #endif /* USE_ITHREADS */ #ifndef IN_PERL_COMPILETIME # define IN_PERL_COMPILETIME (PL_curcop == &PL_compiling) #endif #ifndef IN_LOCALE_RUNTIME # define IN_LOCALE_RUNTIME (PL_curcop->op_private & HINT_LOCALE) #endif #ifndef IN_LOCALE_COMPILETIME # define IN_LOCALE_COMPILETIME (PL_hints & HINT_LOCALE) #endif #ifndef IN_LOCALE # define IN_LOCALE (IN_PERL_COMPILETIME ? IN_LOCALE_COMPILETIME : IN_LOCALE_RUNTIME) #endif #ifndef IS_NUMBER_IN_UV # define IS_NUMBER_IN_UV 0x01 #endif #ifndef IS_NUMBER_GREATER_THAN_UV_MAX # define IS_NUMBER_GREATER_THAN_UV_MAX 0x02 #endif #ifndef IS_NUMBER_NOT_INT # define IS_NUMBER_NOT_INT 0x04 #endif #ifndef IS_NUMBER_NEG # define IS_NUMBER_NEG 0x08 #endif #ifndef IS_NUMBER_INFINITY # define IS_NUMBER_INFINITY 0x10 #endif #ifndef IS_NUMBER_NAN # define IS_NUMBER_NAN 0x20 #endif #ifndef GROK_NUMERIC_RADIX # define GROK_NUMERIC_RADIX(sp, send) grok_numeric_radix(sp, send) #endif #ifndef PERL_SCAN_GREATER_THAN_UV_MAX # define PERL_SCAN_GREATER_THAN_UV_MAX 0x02 #endif #ifndef PERL_SCAN_SILENT_ILLDIGIT # define PERL_SCAN_SILENT_ILLDIGIT 0x04 #endif #ifndef PERL_SCAN_ALLOW_UNDERSCORES # define PERL_SCAN_ALLOW_UNDERSCORES 0x01 #endif #ifndef PERL_SCAN_DISALLOW_PREFIX # define PERL_SCAN_DISALLOW_PREFIX 0x02 #endif #ifndef grok_numeric_radix #if defined(NEED_grok_numeric_radix) static bool DPPP_(my_grok_numeric_radix)(pTHX_ const char ** sp, const char * send); static #else extern bool DPPP_(my_grok_numeric_radix)(pTHX_ const char ** sp, const char * send); #endif #ifdef grok_numeric_radix # undef grok_numeric_radix #endif #define grok_numeric_radix(a,b) DPPP_(my_grok_numeric_radix)(aTHX_ a,b) #define Perl_grok_numeric_radix DPPP_(my_grok_numeric_radix) #if defined(NEED_grok_numeric_radix) || defined(NEED_grok_numeric_radix_GLOBAL) bool DPPP_(my_grok_numeric_radix)(pTHX_ const char **sp, const char *send) { #ifdef USE_LOCALE_NUMERIC #ifdef PL_numeric_radix_sv if (PL_numeric_radix_sv && IN_LOCALE) { STRLEN len; char* radix = SvPV(PL_numeric_radix_sv, len); if (*sp + len <= send && memEQ(*sp, radix, len)) { *sp += len; return TRUE; } } #else /* older perls don't have PL_numeric_radix_sv so the radix * must manually be requested from locale.h */ #include dTHR; /* needed for older threaded perls */ struct lconv *lc = localeconv(); char *radix = lc->decimal_point; if (radix && IN_LOCALE) { STRLEN len = strlen(radix); if (*sp + len <= send && memEQ(*sp, radix, len)) { *sp += len; return TRUE; } } #endif #endif /* USE_LOCALE_NUMERIC */ /* always try "." if numeric radix didn't match because * we may have data from different locales mixed */ if (*sp < send && **sp == '.') { ++*sp; return TRUE; } return FALSE; } #endif #endif #ifndef grok_number #if defined(NEED_grok_number) static int DPPP_(my_grok_number)(pTHX_ const char * pv, STRLEN len, UV * valuep); static #else extern int DPPP_(my_grok_number)(pTHX_ const char * pv, STRLEN len, UV * valuep); #endif #ifdef grok_number # undef grok_number #endif #define grok_number(a,b,c) DPPP_(my_grok_number)(aTHX_ a,b,c) #define Perl_grok_number DPPP_(my_grok_number) #if defined(NEED_grok_number) || defined(NEED_grok_number_GLOBAL) int DPPP_(my_grok_number)(pTHX_ const char *pv, STRLEN len, UV *valuep) { const char *s = pv; const char *send = pv + len; const UV max_div_10 = UV_MAX / 10; const char max_mod_10 = UV_MAX % 10; int numtype = 0; int sawinf = 0; int sawnan = 0; while (s < send && isSPACE(*s)) s++; if (s == send) { return 0; } else if (*s == '-') { s++; numtype = IS_NUMBER_NEG; } else if (*s == '+') s++; if (s == send) return 0; /* next must be digit or the radix separator or beginning of infinity */ if (isDIGIT(*s)) { /* UVs are at least 32 bits, so the first 9 decimal digits cannot overflow. */ UV value = *s - '0'; /* This construction seems to be more optimiser friendly. (without it gcc does the isDIGIT test and the *s - '0' separately) With it gcc on arm is managing 6 instructions (6 cycles) per digit. In theory the optimiser could deduce how far to unroll the loop before checking for overflow. */ if (++s < send) { int digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { digit = *s - '0'; if (digit >= 0 && digit <= 9) { value = value * 10 + digit; if (++s < send) { /* Now got 9 digits, so need to check each time for overflow. */ digit = *s - '0'; while (digit >= 0 && digit <= 9 && (value < max_div_10 || (value == max_div_10 && digit <= max_mod_10))) { value = value * 10 + digit; if (++s < send) digit = *s - '0'; else break; } if (digit >= 0 && digit <= 9 && (s < send)) { /* value overflowed. skip the remaining digits, don't worry about setting *valuep. */ do { s++; } while (s < send && isDIGIT(*s)); numtype |= IS_NUMBER_GREATER_THAN_UV_MAX; goto skip_value; } } } } } } } } } } } } } } } } } } numtype |= IS_NUMBER_IN_UV; if (valuep) *valuep = value; skip_value: if (GROK_NUMERIC_RADIX(&s, send)) { numtype |= IS_NUMBER_NOT_INT; while (s < send && isDIGIT(*s)) /* optional digits after the radix */ s++; } } else if (GROK_NUMERIC_RADIX(&s, send)) { numtype |= IS_NUMBER_NOT_INT | IS_NUMBER_IN_UV; /* valuep assigned below */ /* no digits before the radix means we need digits after it */ if (s < send && isDIGIT(*s)) { do { s++; } while (s < send && isDIGIT(*s)); if (valuep) { /* integer approximation is valid - it's 0. */ *valuep = 0; } } else return 0; } else if (*s == 'I' || *s == 'i') { s++; if (s == send || (*s != 'N' && *s != 'n')) return 0; s++; if (s == send || (*s != 'F' && *s != 'f')) return 0; s++; if (s < send && (*s == 'I' || *s == 'i')) { s++; if (s == send || (*s != 'N' && *s != 'n')) return 0; s++; if (s == send || (*s != 'I' && *s != 'i')) return 0; s++; if (s == send || (*s != 'T' && *s != 't')) return 0; s++; if (s == send || (*s != 'Y' && *s != 'y')) return 0; s++; } sawinf = 1; } else if (*s == 'N' || *s == 'n') { /* XXX TODO: There are signaling NaNs and quiet NaNs. */ s++; if (s == send || (*s != 'A' && *s != 'a')) return 0; s++; if (s == send || (*s != 'N' && *s != 'n')) return 0; s++; sawnan = 1; } else return 0; if (sawinf) { numtype &= IS_NUMBER_NEG; /* Keep track of sign */ numtype |= IS_NUMBER_INFINITY | IS_NUMBER_NOT_INT; } else if (sawnan) { numtype &= IS_NUMBER_NEG; /* Keep track of sign */ numtype |= IS_NUMBER_NAN | IS_NUMBER_NOT_INT; } else if (s < send) { /* we can have an optional exponent part */ if (*s == 'e' || *s == 'E') { /* The only flag we keep is sign. Blow away any "it's UV" */ numtype &= IS_NUMBER_NEG; numtype |= IS_NUMBER_NOT_INT; s++; if (s < send && (*s == '-' || *s == '+')) s++; if (s < send && isDIGIT(*s)) { do { s++; } while (s < send && isDIGIT(*s)); } else return 0; } } while (s < send && isSPACE(*s)) s++; if (s >= send) return numtype; if (len == 10 && memEQ(pv, "0 but true", 10)) { if (valuep) *valuep = 0; return IS_NUMBER_IN_UV; } return 0; } #endif #endif /* * The grok_* routines have been modified to use warn() instead of * Perl_warner(). Also, 'hexdigit' was the former name of PL_hexdigit, * which is why the stack variable has been renamed to 'xdigit'. */ #ifndef grok_bin #if defined(NEED_grok_bin) static UV DPPP_(my_grok_bin)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); static #else extern UV DPPP_(my_grok_bin)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); #endif #ifdef grok_bin # undef grok_bin #endif #define grok_bin(a,b,c,d) DPPP_(my_grok_bin)(aTHX_ a,b,c,d) #define Perl_grok_bin DPPP_(my_grok_bin) #if defined(NEED_grok_bin) || defined(NEED_grok_bin_GLOBAL) UV DPPP_(my_grok_bin)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result) { const char *s = start; STRLEN len = *len_p; UV value = 0; NV value_nv = 0; const UV max_div_2 = UV_MAX / 2; bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES; bool overflowed = FALSE; if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) { /* strip off leading b or 0b. for compatibility silently suffer "b" and "0b" as valid binary numbers. */ if (len >= 1) { if (s[0] == 'b') { s++; len--; } else if (len >= 2 && s[0] == '0' && s[1] == 'b') { s+=2; len-=2; } } } for (; len-- && *s; s++) { char bit = *s; if (bit == '0' || bit == '1') { /* Write it in this wonky order with a goto to attempt to get the compiler to make the common case integer-only loop pretty tight. With gcc seems to be much straighter code than old scan_bin. */ redo: if (!overflowed) { if (value <= max_div_2) { value = (value << 1) | (bit - '0'); continue; } /* Bah. We're just overflowed. */ warn("Integer overflow in binary number"); overflowed = TRUE; value_nv = (NV) value; } value_nv *= 2.0; /* If an NV has not enough bits in its mantissa to * represent a UV this summing of small low-order numbers * is a waste of time (because the NV cannot preserve * the low-order bits anyway): we could just remember when * did we overflow and in the end just multiply value_nv by the * right amount. */ value_nv += (NV)(bit - '0'); continue; } if (bit == '_' && len && allow_underscores && (bit = s[1]) && (bit == '0' || bit == '1')) { --len; ++s; goto redo; } if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT)) warn("Illegal binary digit '%c' ignored", *s); break; } if ( ( overflowed && value_nv > 4294967295.0) #if UVSIZE > 4 || (!overflowed && value > 0xffffffff ) #endif ) { warn("Binary number > 0b11111111111111111111111111111111 non-portable"); } *len_p = s - start; if (!overflowed) { *flags = 0; return value; } *flags = PERL_SCAN_GREATER_THAN_UV_MAX; if (result) *result = value_nv; return UV_MAX; } #endif #endif #ifndef grok_hex #if defined(NEED_grok_hex) static UV DPPP_(my_grok_hex)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); static #else extern UV DPPP_(my_grok_hex)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); #endif #ifdef grok_hex # undef grok_hex #endif #define grok_hex(a,b,c,d) DPPP_(my_grok_hex)(aTHX_ a,b,c,d) #define Perl_grok_hex DPPP_(my_grok_hex) #if defined(NEED_grok_hex) || defined(NEED_grok_hex_GLOBAL) UV DPPP_(my_grok_hex)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result) { const char *s = start; STRLEN len = *len_p; UV value = 0; NV value_nv = 0; const UV max_div_16 = UV_MAX / 16; bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES; bool overflowed = FALSE; const char *xdigit; if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) { /* strip off leading x or 0x. for compatibility silently suffer "x" and "0x" as valid hex numbers. */ if (len >= 1) { if (s[0] == 'x') { s++; len--; } else if (len >= 2 && s[0] == '0' && s[1] == 'x') { s+=2; len-=2; } } } for (; len-- && *s; s++) { xdigit = strchr((char *) PL_hexdigit, *s); if (xdigit) { /* Write it in this wonky order with a goto to attempt to get the compiler to make the common case integer-only loop pretty tight. With gcc seems to be much straighter code than old scan_hex. */ redo: if (!overflowed) { if (value <= max_div_16) { value = (value << 4) | ((xdigit - PL_hexdigit) & 15); continue; } warn("Integer overflow in hexadecimal number"); overflowed = TRUE; value_nv = (NV) value; } value_nv *= 16.0; /* If an NV has not enough bits in its mantissa to * represent a UV this summing of small low-order numbers * is a waste of time (because the NV cannot preserve * the low-order bits anyway): we could just remember when * did we overflow and in the end just multiply value_nv by the * right amount of 16-tuples. */ value_nv += (NV)((xdigit - PL_hexdigit) & 15); continue; } if (*s == '_' && len && allow_underscores && s[1] && (xdigit = strchr((char *) PL_hexdigit, s[1]))) { --len; ++s; goto redo; } if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT)) warn("Illegal hexadecimal digit '%c' ignored", *s); break; } if ( ( overflowed && value_nv > 4294967295.0) #if UVSIZE > 4 || (!overflowed && value > 0xffffffff ) #endif ) { warn("Hexadecimal number > 0xffffffff non-portable"); } *len_p = s - start; if (!overflowed) { *flags = 0; return value; } *flags = PERL_SCAN_GREATER_THAN_UV_MAX; if (result) *result = value_nv; return UV_MAX; } #endif #endif #ifndef grok_oct #if defined(NEED_grok_oct) static UV DPPP_(my_grok_oct)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); static #else extern UV DPPP_(my_grok_oct)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result); #endif #ifdef grok_oct # undef grok_oct #endif #define grok_oct(a,b,c,d) DPPP_(my_grok_oct)(aTHX_ a,b,c,d) #define Perl_grok_oct DPPP_(my_grok_oct) #if defined(NEED_grok_oct) || defined(NEED_grok_oct_GLOBAL) UV DPPP_(my_grok_oct)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result) { const char *s = start; STRLEN len = *len_p; UV value = 0; NV value_nv = 0; const UV max_div_8 = UV_MAX / 8; bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES; bool overflowed = FALSE; for (; len-- && *s; s++) { /* gcc 2.95 optimiser not smart enough to figure that this subtraction out front allows slicker code. */ int digit = *s - '0'; if (digit >= 0 && digit <= 7) { /* Write it in this wonky order with a goto to attempt to get the compiler to make the common case integer-only loop pretty tight. */ redo: if (!overflowed) { if (value <= max_div_8) { value = (value << 3) | digit; continue; } /* Bah. We're just overflowed. */ warn("Integer overflow in octal number"); overflowed = TRUE; value_nv = (NV) value; } value_nv *= 8.0; /* If an NV has not enough bits in its mantissa to * represent a UV this summing of small low-order numbers * is a waste of time (because the NV cannot preserve * the low-order bits anyway): we could just remember when * did we overflow and in the end just multiply value_nv by the * right amount of 8-tuples. */ value_nv += (NV)digit; continue; } if (digit == ('_' - '0') && len && allow_underscores && (digit = s[1] - '0') && (digit >= 0 && digit <= 7)) { --len; ++s; goto redo; } /* Allow \octal to work the DWIM way (that is, stop scanning * as soon as non-octal characters are seen, complain only iff * someone seems to want to use the digits eight and nine). */ if (digit == 8 || digit == 9) { if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT)) warn("Illegal octal digit '%c' ignored", *s); } break; } if ( ( overflowed && value_nv > 4294967295.0) #if UVSIZE > 4 || (!overflowed && value > 0xffffffff ) #endif ) { warn("Octal number > 037777777777 non-portable"); } *len_p = s - start; if (!overflowed) { *flags = 0; return value; } *flags = PERL_SCAN_GREATER_THAN_UV_MAX; if (result) *result = value_nv; return UV_MAX; } #endif #endif #if !defined(my_snprintf) #if defined(NEED_my_snprintf) static int DPPP_(my_my_snprintf)(char * buffer, const Size_t len, const char * format, ...); static #else extern int DPPP_(my_my_snprintf)(char * buffer, const Size_t len, const char * format, ...); #endif #define my_snprintf DPPP_(my_my_snprintf) #define Perl_my_snprintf DPPP_(my_my_snprintf) #if defined(NEED_my_snprintf) || defined(NEED_my_snprintf_GLOBAL) int DPPP_(my_my_snprintf)(char *buffer, const Size_t len, const char *format, ...) { dTHX; int retval; va_list ap; va_start(ap, format); #ifdef HAS_VSNPRINTF retval = vsnprintf(buffer, len, format, ap); #else retval = vsprintf(buffer, format, ap); #endif va_end(ap); if (retval < 0 || (len > 0 && (Size_t)retval >= len)) Perl_croak(aTHX_ "panic: my_snprintf buffer overflow"); return retval; } #endif #endif #if !defined(my_sprintf) #if defined(NEED_my_sprintf) static int DPPP_(my_my_sprintf)(char * buffer, const char * pat, ...); static #else extern int DPPP_(my_my_sprintf)(char * buffer, const char * pat, ...); #endif #define my_sprintf DPPP_(my_my_sprintf) #define Perl_my_sprintf DPPP_(my_my_sprintf) #if defined(NEED_my_sprintf) || defined(NEED_my_sprintf_GLOBAL) int DPPP_(my_my_sprintf)(char *buffer, const char* pat, ...) { va_list args; va_start(args, pat); vsprintf(buffer, pat, args); va_end(args); return strlen(buffer); } #endif #endif #ifdef NO_XSLOCKS # ifdef dJMPENV # define dXCPT dJMPENV; int rEtV = 0 # define XCPT_TRY_START JMPENV_PUSH(rEtV); if (rEtV == 0) # define XCPT_TRY_END JMPENV_POP; # define XCPT_CATCH if (rEtV != 0) # define XCPT_RETHROW JMPENV_JUMP(rEtV) # else # define dXCPT Sigjmp_buf oldTOP; int rEtV = 0 # define XCPT_TRY_START Copy(top_env, oldTOP, 1, Sigjmp_buf); rEtV = Sigsetjmp(top_env, 1); if (rEtV == 0) # define XCPT_TRY_END Copy(oldTOP, top_env, 1, Sigjmp_buf); # define XCPT_CATCH if (rEtV != 0) # define XCPT_RETHROW Siglongjmp(top_env, rEtV) # endif #endif #if !defined(my_strlcat) #if defined(NEED_my_strlcat) static Size_t DPPP_(my_my_strlcat)(char * dst, const char * src, Size_t size); static #else extern Size_t DPPP_(my_my_strlcat)(char * dst, const char * src, Size_t size); #endif #define my_strlcat DPPP_(my_my_strlcat) #define Perl_my_strlcat DPPP_(my_my_strlcat) #if defined(NEED_my_strlcat) || defined(NEED_my_strlcat_GLOBAL) Size_t DPPP_(my_my_strlcat)(char *dst, const char *src, Size_t size) { Size_t used, length, copy; used = strlen(dst); length = strlen(src); if (size > 0 && used < size - 1) { copy = (length >= size - used) ? size - used - 1 : length; memcpy(dst + used, src, copy); dst[used + copy] = '\0'; } return used + length; } #endif #endif #if !defined(my_strlcpy) #if defined(NEED_my_strlcpy) static Size_t DPPP_(my_my_strlcpy)(char * dst, const char * src, Size_t size); static #else extern Size_t DPPP_(my_my_strlcpy)(char * dst, const char * src, Size_t size); #endif #define my_strlcpy DPPP_(my_my_strlcpy) #define Perl_my_strlcpy DPPP_(my_my_strlcpy) #if defined(NEED_my_strlcpy) || defined(NEED_my_strlcpy_GLOBAL) Size_t DPPP_(my_my_strlcpy)(char *dst, const char *src, Size_t size) { Size_t length, copy; length = strlen(src); if (size > 0) { copy = (length >= size) ? size - 1 : length; memcpy(dst, src, copy); dst[copy] = '\0'; } return length; } #endif #endif #ifndef PERL_PV_ESCAPE_QUOTE # define PERL_PV_ESCAPE_QUOTE 0x0001 #endif #ifndef PERL_PV_PRETTY_QUOTE # define PERL_PV_PRETTY_QUOTE PERL_PV_ESCAPE_QUOTE #endif #ifndef PERL_PV_PRETTY_ELLIPSES # define PERL_PV_PRETTY_ELLIPSES 0x0002 #endif #ifndef PERL_PV_PRETTY_LTGT # define PERL_PV_PRETTY_LTGT 0x0004 #endif #ifndef PERL_PV_ESCAPE_FIRSTCHAR # define PERL_PV_ESCAPE_FIRSTCHAR 0x0008 #endif #ifndef PERL_PV_ESCAPE_UNI # define PERL_PV_ESCAPE_UNI 0x0100 #endif #ifndef PERL_PV_ESCAPE_UNI_DETECT # define PERL_PV_ESCAPE_UNI_DETECT 0x0200 #endif #ifndef PERL_PV_ESCAPE_ALL # define PERL_PV_ESCAPE_ALL 0x1000 #endif #ifndef PERL_PV_ESCAPE_NOBACKSLASH # define PERL_PV_ESCAPE_NOBACKSLASH 0x2000 #endif #ifndef PERL_PV_ESCAPE_NOCLEAR # define PERL_PV_ESCAPE_NOCLEAR 0x4000 #endif #ifndef PERL_PV_ESCAPE_RE # define PERL_PV_ESCAPE_RE 0x8000 #endif #ifndef PERL_PV_PRETTY_NOCLEAR # define PERL_PV_PRETTY_NOCLEAR PERL_PV_ESCAPE_NOCLEAR #endif #ifndef PERL_PV_PRETTY_DUMP # define PERL_PV_PRETTY_DUMP PERL_PV_PRETTY_ELLIPSES|PERL_PV_PRETTY_QUOTE #endif #ifndef PERL_PV_PRETTY_REGPROP # define PERL_PV_PRETTY_REGPROP PERL_PV_PRETTY_ELLIPSES|PERL_PV_PRETTY_LTGT|PERL_PV_ESCAPE_RE #endif /* Hint: pv_escape * Note that unicode functionality is only backported to * those perl versions that support it. For older perl * versions, the implementation will fall back to bytes. */ #ifndef pv_escape #if defined(NEED_pv_escape) static char * DPPP_(my_pv_escape)(pTHX_ SV * dsv, char const * const str, const STRLEN count, const STRLEN max, STRLEN * const escaped, const U32 flags); static #else extern char * DPPP_(my_pv_escape)(pTHX_ SV * dsv, char const * const str, const STRLEN count, const STRLEN max, STRLEN * const escaped, const U32 flags); #endif #ifdef pv_escape # undef pv_escape #endif #define pv_escape(a,b,c,d,e,f) DPPP_(my_pv_escape)(aTHX_ a,b,c,d,e,f) #define Perl_pv_escape DPPP_(my_pv_escape) #if defined(NEED_pv_escape) || defined(NEED_pv_escape_GLOBAL) char * DPPP_(my_pv_escape)(pTHX_ SV *dsv, char const * const str, const STRLEN count, const STRLEN max, STRLEN * const escaped, const U32 flags) { const char esc = flags & PERL_PV_ESCAPE_RE ? '%' : '\\'; const char dq = flags & PERL_PV_ESCAPE_QUOTE ? '"' : esc; char octbuf[32] = "%123456789ABCDF"; STRLEN wrote = 0; STRLEN chsize = 0; STRLEN readsize = 1; #if defined(is_utf8_string) && defined(utf8_to_uvchr) bool isuni = flags & PERL_PV_ESCAPE_UNI ? 1 : 0; #endif const char *pv = str; const char * const end = pv + count; octbuf[0] = esc; if (!(flags & PERL_PV_ESCAPE_NOCLEAR)) sv_setpvs(dsv, ""); #if defined(is_utf8_string) && defined(utf8_to_uvchr) if ((flags & PERL_PV_ESCAPE_UNI_DETECT) && is_utf8_string((U8*)pv, count)) isuni = 1; #endif for (; pv < end && (!max || wrote < max) ; pv += readsize) { const UV u = #if defined(is_utf8_string) && defined(utf8_to_uvchr) isuni ? utf8_to_uvchr((U8*)pv, &readsize) : #endif (U8)*pv; const U8 c = (U8)u & 0xFF; if (u > 255 || (flags & PERL_PV_ESCAPE_ALL)) { if (flags & PERL_PV_ESCAPE_FIRSTCHAR) chsize = my_snprintf(octbuf, sizeof octbuf, "%"UVxf, u); else chsize = my_snprintf(octbuf, sizeof octbuf, "%cx{%"UVxf"}", esc, u); } else if (flags & PERL_PV_ESCAPE_NOBACKSLASH) { chsize = 1; } else { if (c == dq || c == esc || !isPRINT(c)) { chsize = 2; switch (c) { case '\\' : /* fallthrough */ case '%' : if (c == esc) octbuf[1] = esc; else chsize = 1; break; case '\v' : octbuf[1] = 'v'; break; case '\t' : octbuf[1] = 't'; break; case '\r' : octbuf[1] = 'r'; break; case '\n' : octbuf[1] = 'n'; break; case '\f' : octbuf[1] = 'f'; break; case '"' : if (dq == '"') octbuf[1] = '"'; else chsize = 1; break; default: chsize = my_snprintf(octbuf, sizeof octbuf, pv < end && isDIGIT((U8)*(pv+readsize)) ? "%c%03o" : "%c%o", esc, c); } } else { chsize = 1; } } if (max && wrote + chsize > max) { break; } else if (chsize > 1) { sv_catpvn(dsv, octbuf, chsize); wrote += chsize; } else { char tmp[2]; my_snprintf(tmp, sizeof tmp, "%c", c); sv_catpvn(dsv, tmp, 1); wrote++; } if (flags & PERL_PV_ESCAPE_FIRSTCHAR) break; } if (escaped != NULL) *escaped= pv - str; return SvPVX(dsv); } #endif #endif #ifndef pv_pretty #if defined(NEED_pv_pretty) static char * DPPP_(my_pv_pretty)(pTHX_ SV * dsv, char const * const str, const STRLEN count, const STRLEN max, char const * const start_color, char const * const end_color, const U32 flags); static #else extern char * DPPP_(my_pv_pretty)(pTHX_ SV * dsv, char const * const str, const STRLEN count, const STRLEN max, char const * const start_color, char const * const end_color, const U32 flags); #endif #ifdef pv_pretty # undef pv_pretty #endif #define pv_pretty(a,b,c,d,e,f,g) DPPP_(my_pv_pretty)(aTHX_ a,b,c,d,e,f,g) #define Perl_pv_pretty DPPP_(my_pv_pretty) #if defined(NEED_pv_pretty) || defined(NEED_pv_pretty_GLOBAL) char * DPPP_(my_pv_pretty)(pTHX_ SV *dsv, char const * const str, const STRLEN count, const STRLEN max, char const * const start_color, char const * const end_color, const U32 flags) { const U8 dq = (flags & PERL_PV_PRETTY_QUOTE) ? '"' : '%'; STRLEN escaped; if (!(flags & PERL_PV_PRETTY_NOCLEAR)) sv_setpvs(dsv, ""); if (dq == '"') sv_catpvs(dsv, "\""); else if (flags & PERL_PV_PRETTY_LTGT) sv_catpvs(dsv, "<"); if (start_color != NULL) sv_catpv(dsv, D_PPP_CONSTPV_ARG(start_color)); pv_escape(dsv, str, count, max, &escaped, flags | PERL_PV_ESCAPE_NOCLEAR); if (end_color != NULL) sv_catpv(dsv, D_PPP_CONSTPV_ARG(end_color)); if (dq == '"') sv_catpvs(dsv, "\""); else if (flags & PERL_PV_PRETTY_LTGT) sv_catpvs(dsv, ">"); if ((flags & PERL_PV_PRETTY_ELLIPSES) && escaped < count) sv_catpvs(dsv, "..."); return SvPVX(dsv); } #endif #endif #ifndef pv_display #if defined(NEED_pv_display) static char * DPPP_(my_pv_display)(pTHX_ SV * dsv, const char * pv, STRLEN cur, STRLEN len, STRLEN pvlim); static #else extern char * DPPP_(my_pv_display)(pTHX_ SV * dsv, const char * pv, STRLEN cur, STRLEN len, STRLEN pvlim); #endif #ifdef pv_display # undef pv_display #endif #define pv_display(a,b,c,d,e) DPPP_(my_pv_display)(aTHX_ a,b,c,d,e) #define Perl_pv_display DPPP_(my_pv_display) #if defined(NEED_pv_display) || defined(NEED_pv_display_GLOBAL) char * DPPP_(my_pv_display)(pTHX_ SV *dsv, const char *pv, STRLEN cur, STRLEN len, STRLEN pvlim) { pv_pretty(dsv, pv, cur, pvlim, NULL, NULL, PERL_PV_PRETTY_DUMP); if (len > cur && pv[cur] == '\0') sv_catpvs(dsv, "\\0"); return SvPVX(dsv); } #endif #endif #endif /* _P_P_PORTABILITY_H_ */ /* End of File ppport.h */ yuma123_2.14/netconf/perl/yuma/MANIFEST0000664000175000017500000000011214770023131017617 0ustar vladimirvladimirChanges Makefile.PL MANIFEST ppport.h README yuma.xs t/yuma.t lib/yuma.pm yuma123_2.14/netconf/perl/yuma/yuma.xs0000664000175000017500000000045214770023131020024 0ustar vladimirvladimir#define PERL_NO_GET_CONTEXT #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" MODULE = yuma PACKAGE = yuma int val_make_serialized_string(void* val, int mode, char* xml_str) CODE: RETVAL = val_make_serialized_string(val, mode, &xml_str); OUTPUT: xml_str RETVAL yuma123_2.14/netconf/perl/example-yangcli/0000775000175000017500000000000014770023131020600 5ustar vladimirvladimiryuma123_2.14/netconf/perl/example-yangcli/perl-yangcli-example.pl0000664000175000017500000000066514770023131025163 0ustar vladimirvladimiruse yuma; use yangrpc; use yangcli; use XML::LibXML; my $conn = yangrpc::connect("127.0.0.1", 830, "root", "mysecretpass","/root/.ssh/id_rsa.pub","/root/.ssh/id_rsa"); defined($conn) || die "Error: yangrpc failed to connect!"; my @names = yangcli::yangcli($conn, "xget /interfaces-state")->findnodes("./rpc-reply/data/interfaces-state/interface/name"); for my $name (@names) { print $name->textContent()."\n"; } print("Done.\n"); yuma123_2.14/netconf/man/0000775000175000017500000000000014770023131015332 5ustar vladimirvladimiryuma123_2.14/netconf/man/netconf-subsystem.10000664000175000017500000000261614770023131021111 0ustar vladimirvladimir.\" Process this file with .\" nroff -e -mandoc foo.1 .\" .TH netconf-subsystem 1 "March 23, 2025" Linux "netconf-subsystem 2.14" .SH NAME netconf-subsystem \- thin client to access netconfd server .SH SYNOPSIS .nf netconf-subsystem .fi .SH DESCRIPTION .B netconf-subsystem is a thin-client application that is called by the OpenSSH server to access the netconfd server, when the 'netconf' subsystem is requested. .SH USAGE The location of this program needs to be configured in the /etc/ssh/sshd_config file. The following configuration needs to be present: .nf Port 22 Port 830 Subsystem netconf /usr/sbin/netconf-subsystem .fi The actual filespec in the last line will depend on the location that this program is installed. The default value is shown in the example above. .SH OPTIONS .IP --\fBncxserver-sockname\fP=port@filespec Specifies the corresponding pairs of SSH ports and UNIX socket names. This option allows multiple netconfd instances to run on the same host. /etc/ssh/sshd_config: .nf \&... Port 830 Port 1830 Port 2830 Subsystem netconf "/usr/sbin/netconf-subsystem \ --ncxserver-sockname=830@/tmp/ncxserver.sock \ --ncxserver-sockname=1830@/tmp/ncxserver-right.sock \ --ncxserver-sockname=2830@/tmp/ncxserver-middle.sock" \&... .fi .SH AUTHORS Andy Bierman, Vladimir Vassilev, .SH SEE ALSO .BR netconfd (1) yuma123_2.14/netconf/man/netconfd.10000664000175000017500000004546214770023131017227 0ustar vladimirvladimir.\" Process this file with .\" nroff -e -mandoc foo.1 .\" .TH netconfd 1 "March 23, 2025" Linux "netconfd 2.14" .SH NAME netconfd \- YANG-based NETCONF-over-SSH server .SH SYNOPSIS .nf netconfd [parameter=value...] netconfd --help [brief | normal | full] netconfd --version .fi .SH DESCRIPTION .B netconfd is a YANG-based NETCONF server, which can be used with an SSH server such as OpenSSH. This version of netconfd supports the YANG data modeling language defined in \fBRFC 6020\fP. .SH USAGE Parameters can be entered in any order, and have the form: \fB[start] name [separator [value]]\fP where: \fBstart\fP == 0, 1, or 2 dashes (foo, -foo, --foo) \fBname\fP == parameter name .nf Parameter name completion will be attempted if a partial name is entered. .fi \fBseparator\fP == whitespace or equals sign (foo=bar, foo bar) \fBvalue\fP == string value for the parameter. .nf Strings with whitespace need to be double quoted (--foo="some string") .fi Some examples of valid command line parameters: .nf foo=3 -foo=3 --foo=3 foo 3 foo=fred --foo "fred flintstone" .fi Partial parameter names can be entered if they are unique. .SH OPTIONS .IP --\fBaccess-control\fP=enum Controls how the ietf-netconf-acm access control model will be enforced during server operation. .nf Enum values: enforcing: All configured access control rules will be enforced. permissive: All configured access control rules will be enforced for write and execute requests. All read requests will be allowed, unless the requested object contains the 'nacm:very-secure' extension. In that case, all configured access control rules will be enforced. disabled: All read, write, and execute requests will be allowed, unless the object contains the 'nacm:secure' or 'nacm:very-secure' extension. If the 'nacm:secure' extension is in effect, then all configured access control rules will be enforced for write and execute requests. If the 'nacm:very-secure' extension is in effect, then all configured access control rules will be enforced for all requests. Use this mode with caution. off: All access control enforcement is disabled. Use this mode with extreme caution. .fi .IP --\fBaudit-log\fP=filespec Filespec for the server audit log file to use in addition to the normal log file or STDOUT. .IP --\fBaudit-log-append\fP If present, the audit log will be appended not over-written. If not, the audit log will be over-written. Only meaningful if the 'audit-log' parameter is also present. .IP --\fBconfig\fP=filespec The name of the configuration file to use. Any parameter except this one can be set in the config file. The default config file .I /etc/yuma/netconfd.conf will not be checked if this parameter is present. .IP --\fBdatapath\fP=list Internal file search path for configuration data files. Overrides the YUMA_DATAPATH environment variable. This parameter affects the search for the startup configuration file (default: startup-cfg.xml). .IP --\fBdefault-style\fP=enum Selects the type of filtering behavior the server will advertise as the 'basic' behavior in the 'with-defaults' capability. The server will use this default handling behavior if the 'with-defaults' parameter is not explicitly set. Also, when saving a configuration to NV-storage, this value will be used for filtering defaults from the saved configuration. .nf Enum values: report-all: report all values trim: remove leafs containing the YANG default value explicit: report only the nodes that have been created by the client or the server. This is the default value. .fi .IP --\fBdelete-empty-npcontainers\fP=boolean Selects whether the server will keep or delete empty non-presence containers in the running and startup configurations. Set to true to delete these containers, and false to keep them. Default: false. This parameter is deprecated! It is ignored by the server! .IP --\fBdeviation\fP=string This parameter identifies a YANG module that should only be checked for deviation statements for external modules. These will be collected and applied to the real module(s) being processed. Deviations are applied as patches to the target module. Since they are not identified in the target module at all (ala imports), they have to be specified explicitly, so they will be correctly processed. Zero or more instances of this parameter are allowed. .IP --\fBeventlog-size\fP=number Specifies the maximum number of notification events that will be saved in the notification replay buffer. The oldest entries will be deleted first. The default value is 1000. .IP --\fBfeature-disable\fP=module:feature Identifies a feature which should be considered disabled. Zero or more entries are allowed. .IP --\fBfeature-enable-default\fP=boolean If true (the default), then features will be enabled by default. If false, then features will be disabled by default. .IP --\fBfeature-enable\fP=module:feature Identifies a feature which should be considered enabled. Zero or more entries are allowed. .IP --\fBhello-timeout\fP=number Specifies the number of seconds that a session may exist before the hello PDU is received. A session will be dropped if no hello PDU is received before this number of seconds elapses. If this parameter is set to zero, then the server will wait forever for a hello message, and not drop any sessions stuck in 'hello-wait' state. Setting this parameter to zero may permit denial of service attacks, since only a limited number of concurrent sessions are supported by the server. (range 0 | 10 .. 3600). The default value is 600 seconds (10 minutes). .IP --\fBhelp\fP Print this help text and exit. The help-mode choice (--brief, --normal, or --full) may also be present to control the amount of help text printed. .IP --\fBhome\fP=dirspec Directory specification for the home directory to use instead of HOME. .IP --\fBidle-timeout\fP=number Specifies the number of seconds that a session may remain idle without issuing any RPC requests. A session will be dropped if it is idle for an interval longer than this number of seconds. Sessions that have a notification subscription active are never dropped. If this parameter is set to zero, then the server will never drop a session because it is idle. (range 0 | 10 .. 360000). The default value is 3600 seconds (1 hour). .IP --\fBindent\fP=number Number of spaces to indent (0..9) in formatted output. The default is 2 spaces. .IP --\fBlog\fP=filespec Filespec for the log file to use instead of STDOUT. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. .IP --\fBlog-append\fP If present, the log will be appended not over-written. If not, the log will be over-written. Only meaningful if the \fBlog\fP parameter is also present. .IP --\fBlog-level\fP=enum Sets the debug logging level for the program. .IP --\fBmax-burst\fP=number Specifies the maximum number of notifications that should be sent to one session, within a one second time interval. The value 0 indicates that the server should not limit notification bursts at all. The default value is 10. .IP --\fBmodpath\fP=list Directory search path for YANG and YIN files. Overrides the YUMA_MODPATH environment variable. .IP --\fBmodule\fP=string YANG or YIN source module name to load at startup. The server will attempt to load the specified module and its corresponding server instrumentation library (SIL) . If this string represents a filespec, ending with the \fB.yang\fP or \fB.yin\fP extension, then only that file location will be checked. If this string represents a module name, then the module search path will be checked for a file the \fB.yang\fP or \fB.yin\fP extension. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. .nf ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path .fi .IP --\fBncxserver-sockname\fP=path Overrides the default /tmp/ncxserver.sock UNIX socket name netconfd listens on for incoming connections. You have to add corresponding entry to /etc/ssh/sshd_config e.g.: .nf \&... Port 1830 Subsystem netconf \"/usr/sbin/netconf-subsystem --ncxserver-sockname=/tmp/ncxserver1.sock\" \&... .fi .IP --\fBport\fP=number Specifies the TCP ports that the server will accept connections from. These ports must also be configured in the /etc/ssh/sshd_config file for the SSH master server to accept the connection and invoke the netconf subsystem. Up to 4 port numbers can be configured. If any ports are configured, then only those values will be accepted by the server. If no ports are configured, then the server will accept connections on the netconf-ssh port (tcp/830). .IP --\fBprotocols\fP=bits Specifies which NETCONF protocol versions the server will attempt to use. The empty set is not allowed. The values 'netconf1.0' and 'netconf1.1' are supported. The default is to enable both NETCONF protocol versions. .IP --\fBrunpath\fP=pathlist Internal file search path for executable modules. Overrides the YUMA_RUNPATH environment variable. .IP --\fBrunning-error\fP=enum If 'stop', then errors in the running configuration will be treated as fatal errors. If 'continue', the server will attempt to continue if any validataion errors are found in the running configuration at startup. The default is 'stop'. .IP --\fBstartup\fP=filespec The full or relative filespec of the startup config file to use. If present, overrides the default startup config file name 'startup-cfg.xml', This will also override the YUMA_DATAPATH environment variable and the datapath CLI parameter, if the first character is the forward slash '/', indicating an absolute file path. If this parameter is present, then the --no-startup and --factory-startup parameters cannot be present. This is the default, which will cause startup-cfg.xml to be used if not present. .IP --\fBno-startup\fP If present, do not load the startup config file. Use only factory default values instead. Does not affect the startup.cfg file, if present. If this parameter is present, then the --startup or --factory-startup parameter cannot be present. .IP --\fBfactory-startup\fP Force the system to use the factory configuration and delete the startup config file if it exists. Force the NV-storage startup to contain the factory default configuration. If this parameter is present, then the --no-startup and --startup parameters cannot be present. .IP --\fBstartup-error\fP=enum If 'stop', then any errors in the startup configuration will be treated as fatal errors. If 'continue', the server will attempt to continue if any errors are found in the database loaded from NV-storage to running at boot-time. The default is 'stop'. .IP --\fBsubdirs\fP=boolean If false, the file search paths for modules, scripts, and data files will not include sub-directories if they exist in the specified path. If true, then these file search paths will include sub-directories, if present. Any directory name beginning with a dot (\fB.\fP) character, or named \fBCVS\fP, will be ignored. This is the default mode. .IP --\fBsuperuser\fP=string The user name to use as the superuser account. Any session associated with this user name will bypass all access control enforcement. See ietf-netconf-acm.yang for more details. There is no default value. .IP --\fBsystem-sorted\fP=boolean Indicates whether ordered-by system leaf-lists and lists will be kept in sorted order. The default is true. .IP --\fBtarget\fP=enum Specifies the database to use as the target of edit-config operations. .nf Enum values: running: Write to the running config and support the :writable-running capability. candidate: Write to the candidate config and support the :candidate and :confirmed-commit capabilities. .fi .IP --\fBusexmlorder\fP If present, then XML element order will be enforced. Otherwise, XML element order errors will not be generated if possible. Default is no enforcement of strict XML order. .IP --\fBversion\fP Print the program version string and exit. .IP --\fBvalidate-config-only\fP If present, netconfd acts as command line YANG configuration validator. Load the YANG schema modules, validate the startup configuration and exit without opening socket and listening for incoming sessions. .IP --\fBwarn-idlen\fP=number Control whether identifier length warnings will be generated. The value zero disables all identifier length checking. If non-zero, then a warning will be generated if an identifier is defined which has a length is greater than this amount. range: 0 | 8 .. 1023. The default value is 64. .IP --\fBwarn-linelen\fP=number Control whether line length warnings will be generated. The value zero disables all line length checking. If non-zero, then a warning will be generated if the line length is greater than this amount. Tab characters are counted as 8 spaces. range: 0 | 40 .. 4095. The default value is 72. .IP --\fBwarn-off\fP=number Control whether the specified warning number will be generated and counted in the warning total for the module being parsed. range: 400 .. 899. This parameter may be entered zero or more times. .IP --\fBwith-startup\fP=boolean If set to 'true', then the :startup capability will be enabled. Otherwise, the :startup capability will not be enabled. This capability makes the NV-save operation an explicit operation instead of an automatic save. The default value is false. .IP --\fBwith-url\fP=boolean If set to 'false', then the :url capability will be disabled. Otherwise, the :url capability will be enabled. This capability allows local files to be stored as backups on the server. The default value is true. .IP --\fBwith-validate\fP=boolean If set to 'true', then the :validate capability will be enabled. Otherwise, the :validate capability will not be enabled. This capability requires extensive memory resources. The default value is true. .IP --\fByuma-home\fP=string Directory for the yuma project root to use. If present, this directory location will override the YUMA_HOME environment variable, if it is present. If a zero-length string is entered, then the YUMA_HOME environment variable will be ignored. .SH INPUT FILES YANG modules can be loaded at startup with the '--module' command, or loaded at run-time with the 'load' operation. .SH SEARCH PATH When a module name is entered as input, or when a module or submodule name is specified in an import or include statement within the file, the following search algorithm is used to find the file: .nf 1) file is in the current directory 2) YUMA_MODPATH environment var (or set by modpath parameter) 3) $HOME/modules directory 4) $YUMA_HOME/modules directory 5) $YUMA_INSTALL/modules directory OR default install module location, '/usr/share/yuma/modules' .fi By default, the entire directory tree for all locations (except step 1) will be searched, not just the specified directory. The \fBsubdirs\fP parameter can be used to prevent sub-directories from being searched. Any directory name beginning with a dot character (\fB.\fP) will be skipped. Also, any directory named \fBCVS\fP will be skipped in directory searches. .SH ERROR LOGGING By default, warnings and errors are sent to STDOUT. A log file can be specified instead with the \fBlog\fP' parameter. Existing log files can be reused with the 'logappend' parameter, otherwise log files are overwritten. The logging level can be controlled with the \fBlog-level\fP parameter. The default log level is 'info'. The log-levels are additive: .nf off: suppress all errors (not recommended!) A program return code of '1' indicates some error. error: print errors warn: print warnings info: print generally interesting trace info debug: print general debugging trace info debug2: print verbose debugging trace info debug3: print very verbose debugging trace info debug4: print maximum debugging trace info .fi .SH ENVIRONMENT The following optional environment variables can be used to control module search behavior: .IP \fBHOME\fP The user's home directory (e.g., /home/andy) .IP \fBYUMA_HOME\fP The root of the user's Yuma work directory (e.g., /home/andy/swdev/netconf) .IP \fBYUMA_INSTALL\fP The root of the directory that yangdump is installed on this system (default is, /usr/share/yuma) .IP \fBYUMA_DATAPATH\fP Colon-separated list of directories to search for data files. (e.g.: './workdir/data-files:/home/andy/data') The \fBdatapath\fP parameter will override this environment variable, if both are present. .IP \fBYUMA_MODPATH\fP Colon-separated list of directories to search for modules and submodules. (e.g.: './workdir/modules:/home/andy/test-modules') The \fBmodpath\fP parameter will override this environment variable, if both are present. .SH CONFIGURATION FILES .IP \fBnetconfd.conf\fP YANG config file The default is: \fB/etc/yuma/netconfd.conf\fP An ASCII configuration file format is supported to store command line parameters. The \fBconfig\fP parameter is used to specify a specific config file, otherwise the default config file will be checked. .nf - A hash mark until EOLN is treated as a comment - All text is case-sensitive - Whitespace within a line is not significant - Whitespace to end a line is significant/ Unless the line starts a multi-line string, an escaped EOLN (backslash EOLN) is needed to enter a leaf on multiple lines. - For parameters that define lists, the key components are listed just after the parameter name, without any name, e.g., interface eth0 { # name = eth0 is not listed inside the braces ifMtu 1500 ifName mySystem } .fi A config file can contain any number of parameter sets for different programs. Each program must have its own section, identifies by its name: .nf # this is a comment yangdump { log-level debug output "~/swdev/testfiles" } netconfd { ... } .fi .SH FILES The following data files must be present in the module search path in order for this program to function: * \fBYANG module library\fP default: /usr/share/yuma/modules/ .SH DIAGNOSTICS Internal diagnostics may generate the following type of message if any bugs are detected at runtime: .nf [E0] filename.c:linenum error-number (error-msg) .fi .SH AUTHORS Andy Bierman, Vladimir Vassilev, .SH SEE ALSO .BR netconf-subsystem (1) .BR pyang (1) .BR yangcli (1) yuma123_2.14/netconf/man/yangdiff.10000664000175000017500000003414314770023131017210 0ustar vladimirvladimir.\" Process this file with .\" nroff -e -mandoc foo.1 .\" .TH yangdiff 1 "February 6, 2012" Linux "yangdiff 2.2" .SH NAME yangdiff \-report semantic and syntactic changes between two revisions of a YANG module .SH SYNOPSIS .nf yangdiff old=value new=value [parameter=value...] yangdiff --help [brief | normal | full] yangdiff --version .SH DESCRIPTION .B yangdiff compares the semantics and syntax between two revisions of the same YANG module. The conceptual data model is compared, not the individual files. This version of yangdiff supports the YANG data modeling language defined in \fBdraft-ietf-netmod-yang-12.txt\fP. Only semantic changes to the YANG data model are reported. For example, unless statement order is significant, changing the order is not considered a change, and is not reported. Reformatted test (whitespace changes) are also not reported. If a data type definition is changed in form, but not content, then a 'modify type' message will be generated, but no additional sub-fields will be reported. .SH USAGE Parameters can be entered in any order, and have the form: \fB[start] name [separator [value]]\fP where: \fBstart\fP == 0, 1, or 2 dashes (foo, -foo, --foo) \fBname\fP == parameter name .nf Parameter name completion will be attempted if a partial name is entered. .fi \fBseparator\fP == whitespace or equals sign (foo=bar, foo bar) \fBvalue\fP == string value for the parameter. .nf Strings with whitespace need to be double quoted (--foo="some string") .fi Some examples of valid command line parameters: .nf foo=3 -foo=3 --foo=3 foo 3 foo=fred --foo "fred flintstone" .fi Partial parameter names can be entered if they are unique. .SH OPTIONS .IP --\fBconfig\fP=filespec The name of the configuration file to use. Any parameter except this one can be set in the config file. The default config file .I /etc/yuma/yangdiff.conf will be not be checked if this parameter is present. .IP --\fBdifftype\fP=string The type of comparison output requested. Allowed values are 'terse', 'normal', and 'revision'. The basic format is: .nf [add/delete/modify] field-name [field-value] .fi The 'terse' option will include the names of the top-level fields that are different. .nf A foo --> Added foo in new revision D foo --> Deleted foo in new revision M foo --> Modified foo in new revision (value too long) M foo from '0' to '1' --> Modified foo in new revision .fi The 'normal' option will also include any changes for any nested fields or objects. This is the default option. The 'revision' option will generate the differences report in YANG revision-stmt format. For example: .nf revision { description \" - Added import baxtypes - Changed contact from 'tech@acme.com' to 'support@acme.com' - Modified container myobjects - Added list first-list\"; } .fi If missing, the the default value 'normal' is used. .IP --\fBfeature-disable\fP=module:feature Identifies a feature which should be considered disabled. Zero or more entries are allowed. .IP --\fBfeature-enable-default\fP=boolean If true (the default), then features will be enabled by default. If false, then features will be disabled by default. .IP --\fBfeature-enable\fP=module:feature Identifies a feature which should be considered enabled. Zero or more entries are allowed. .IP --\fBheader\fP=boolean If false, the header clauses will be skipped, and any differences between the module headers will not be reported. Only object definitions will be compared. If true (the default), then header clauses will be compared, along will all the object definitions. .IP --\fBhelp\fP Print this help text and exit. The help-mode choice (--brief, --normal, or --full) may also be present to control the amount of help text printed. .fi .IP --\fBhome\fP=dirspec Directory specification for the home directory to use instead of HOME. .IP --\fBindent\fP=number Number of spaces to indent (0..9) in formatted output. The default is 2 spaces. .IP --\fBlog\fP=filespec Filespec for the log file to use instead of STDOUT. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. .IP --\fBlog-append\fP If present, the log will be appended not over-written. If not, the log will be over-written. Only meaningful if the \fBlog\fP parameter is also present. .IP --\fBlog-level\fP=enum Sets the debug logging level for the program. .IP --\fBmodpath\fP=list Directory search path for YANG and YIN files. Overrides the YUMA_MODPATH environment variable. .IP --\fBnew\fP=string If this parameter indicates a filename, then it represents the YANG source module name to compare as the newer of the two revisions. If this parameter indicates a directory (and the 'old' parameter indicates a filename), then it will be used to to search for a file with the same name as the 'new' parameter. If the 'old' parameter identifies a directory as well (and the 'subdirs' parameter is true), then the modules within the 'new' directory will be compared to files with the same name in the 'old' directory. If the 'subdirs' parameter is false, then all sub-directories within the 'src' directory will also be checked. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. .nf ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path .fi This parameter must be present unless the 'help' or 'version' parameters are used. .IP --\fBold\fP=string The older of the two revisions to compare. If this parameter indicates a filename, then it represents the YANG source module name to compare as the older of the two revisions. If this parameter indicates a directory, then it will be used to to search for a file with the same name as identified by the 'new' parameter. If this string represents a filespec, ending with the \fB.yang\fP or \fB.yin\fP extension, then only that file location will be checked. If this string represents a module name, then the module search path will be checked for a file the \fB.yang\fP or \fB.yin\fP extension. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. .nf ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path .fi This parameter must be present unless the 'help' or 'version' parameters are used. .IP --\fBoutput\fP=filespec Output directory or file name to use. The default is STDOUT if none is specified. If this parameter represents an existing directory, then the default comparison output file (yangdiff.log) will be generated in the specified directory. If this parameter represents a file name, then all comparison output will be directed to the specified file. If the file already exists, it will be overwritten. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. .nf ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path .fi .IP --\fBsubdirs\fP=boolean If false, the file search paths for modules, scripts, and data files will not include sub-directories if they exist in the specified path. If true, then these file search paths will include sub-directories, if present. Any directory name beginning with a dot (\fB.\fP) character, or named \fBCVS\fP, will be ignored. This is the default mode. .IP --\fBversion\fP Print the yangdiff version string and exit. .IP --\fBwarn-idlen\fP=number Control whether identifier length warnings will be generated. The value zero disables all identifier length checking. If non-zero, then a warning will be generated if an identifier is defined which has a length is greater than this amount. range: 0 | 8 .. 1023. The default value is 64. .IP --\fBwarn-linelen\fP=number Control whether line length warnings will be generated. The value zero disables all line length checking. If non-zero, then a warning will be generated if the line length is greater than this amount. Tab characters are counted as 8 spaces. range: 0 | 40 .. 4095. The default value is 72. .IP --\fBwarn-off\fP=number Control whether the specified warning number will be generated and counted in the warning total for the module being parsed. range: 400 .. 899. This parameter may be entered zero or more times. .IP --\fByuma-home\fP=string Directory for the yuma project root to use. If present, this directory location will override the YUMA_HOME environment variable, if it is present. If a zero-length string is entered, then the YUMA_HOME environment variable will be ignored. .SH INPUT FILES To compare one module, use the 'old' and 'new' parameters to specify YANG module files, each with a filespec string ending with the '.yang' or '.yin' file extension. The filespecs must represent different files. If the 'old' parameter represents a directory, then this directory will be searched for the 'new' filename. To compare all the modules in a subtree, use the 'old' and 'new' parameters to specify a directory to be searched for YANG modules to be processed. In this mode, each new module is compared to a corresponding file within the 'old' subtree. Also, dependency and include files will be kept separate, for each subtree. Unless the 'help' or 'version' parameters is entered, the 'old' and 'new' parameters must be present. .SH SEARCH PATH When a module name is entered as input, or when a module or submodule name is specified in an import or include statement within the file, the following search algorithm is used to find the file: .nf 1) if the parameter for the file that generated the search request represents a subtree, search that subtree first. 2) file is in the current directory 3) YUMA_MODPATH environment var (or set by modpath parameter) 4) $HOME/modules directory 5) $YUMA_HOME/modules directory 6) $YUMA_INSTALL/modules directory OR default install module location, '/usr/share/yuma/modules' .fi By default, the entire directory tree for all locations (except step 1) will be searched, not just the specified directory. The \fBsubdirs\fP parameter can be used to prevent sub-directories from being searched. Any directory name beginning with a dot character (\fB.\fP) will be skipped. Also, any directory named \fBCVS\fP will be skipped in directory searches. .SH OUTPUT MODES By default, any translation output will be sent to \fBSTDOUT\fP. The \fBoutput\fP parameter can be used to specify the full filespec of the output file, or a complete directory specification to be combined with the default filename (yangdiff.log). .SH ERROR LOGGING By default, warnings and errors are sent to STDOUT. A log file can be specified instead with the \fBlog\fP' parameter. Existing log files can be reused with the 'logappend' parameter, otherwise log files are overwritten. The logging level can be controlled with the \fBlog-level\fP parameter. The default log level is 'info'. The log-levels are additive: .nf off: suppress all errors (not recommended!) A program return code of '1' indicates some error. error: print errors warn: print warnings info: print generally interesting trace info debug: print general debugging trace info debug2: print verbose debugging trace info debug3: print very verbose debugging trace info debug4: print maximum debugging trace info .fi .SH ENVIRONMENT The following optional environment variables can be used to control module search behavior: .IP \fBHOME\fP The user's home directory (e.g., /home/andy) .IP \fBYUMA_HOME\fP The root of the user's YANG work directory (e.g., /home/andy/swdev/netconf) .IP \fBYUMA_INSTALL\fP The root of the directory that yangdiff data files are installed on this system (default is, /usr/share/yuma) .IP \fBYUMA_MODPATH\fP Colon-separated list of directories to search for modules and submodules. (e.g.: './workdir/modules:/home/andy/test-modules') The \fBmodpath\fP parameter will override this environment variable, if both are present. .SH CONFIGURATION FILES .IP \fByangdiff.conf\fP YANG config file The default is: \fB/etc/yuma/yangdiff.conf\fP An ASCII configuration file format is supported to store command line parameters. The \fBconfig\fP parameter is used to specify a specific config file, otherwise the default config file will be checked. .nf - A hash mark until EOLN is treated as a comment - All text is case-sensitive - Whitespace within a line is not significant - Whitespace to end a line is significant/ Unless the line starts a multi-line string, an escaped EOLN (backslash EOLN) is needed to enter a leaf on multiple lines. - For parameters that define lists, the key components are listed just after the parameter name, without any name, e.g., interface eth0 { # name = eth0 is not listed inside the braces ifMtu 1500 ifName mySystem } .fi A config file can contain any number of parameter sets for different programs. .SH FILES The following data files must be present in the module search path in order for this program to function: * \fBYANG module library\fP default: /usr/share/yuma/modules/ .SH DIAGNOSTICS Internal diagnostics may generate the following type of message if any bugs are detected at runtime: .nf [E0] filename.c:linenum error-number (error-msg) .fi .SH AUTHOR Andy Bierman, .SH SEE ALSO .BR yangdump (1) yuma123_2.14/netconf/man/yangcli.10000664000175000017500000005317314770023131017053 0ustar vladimirvladimir.\" Process this file with .\" nroff -e -mandoc foo.1 .\" .TH yangcli 1 "March 23, 2025" Linux "yangcli 2.14" .SH NAME yangcli \- YANG-based NETCONF-over-SSH client application .SH SYNOPSIS .nf yangcli [parameter=value...] yangcli --help [brief | normal | full] yangcli --version .fi .SH DESCRIPTION .B yangcli is a Command Line Interface for the NETCONF protocol. It uses the YANG data modeling language to manage the NETCONF content layer. This version of yangcli supports the YANG data modeling language defined in \fBRFC 6020\fP. .nf Normal Mode: An interactive CLI shell with command line history. Autostart-mode: If the 'server' parameter is present, then yangcli will attempt to connect to that server upon startup. If the 'user' and 'password' parameters are also present, then the user will not be prompted before the connection is attempted. This parameter will be processed first, if script-mode or batch-mode is used. Script-mode: If the 'run-script' or 'run-command' parameter is present, then the specified script or command will be run automatically upon startup. Batch-mode: If the 'batch-mode' parameter is present, then either the 'run-script' or 'run-command' parameter will be invoked, if present, and then the program will exit. .fi .SH USAGE Parameters can be entered in any order, and have the form: \fB[start] name [separator [value]]\fP where: \fBstart\fP == 0, 1, or 2 dashes (foo, -foo, --foo) \fBname\fP == parameter name .nf Parameter name completion will be attempted if a partial name is entered. .fi \fBseparator\fP == whitespace or equals sign (foo=bar, foo bar) \fBvalue\fP == string value for the parameter. .nf Strings with whitespace need to be double quoted (--foo="some string") .fi Some examples of valid command line parameters: .nf foo=3 -foo=3 --foo=3 foo 3 foo=fred --foo "fred flintstone" .fi Partial parameter names can be entered if they are unique. .SH OPTIONS .IP --\fBaliases-file\fP[=filespec] Specifies the yangcli command aliases file to use. The default is ~/.yuma/.yangcli_aliases. .IP --\fBalt-names\fP=boolean Altername name match mode to use for UrlPath name searches. The default is true (allow matches on alt-name extension). .IP --\fBautoaliases\fP=boolean Controls whether the yangcli command aliases will be saved at exit and loaded at startup. If true, the 'aliases-file' parameter will be used if it is set, or else the default aliases file will be used (~/.yuma/.yangcli_aliases), for loading and saving the yangcli command aliases. If false, the yangcli command aliases will only be stored and loaded manually with the aliases command. The default is true. .IP --\fBautocomp\fP=boolean Controls whether partial keywords will be checked for interactive or script commands. If true (the default), the first match for a partial keyword will be used if no definition is found for a command name or parameter name. If false, then exact command and parameter name values must be given. .IP --\fBautohistory\fP=boolean Controls whether the command line history buffer will be saved at exit and loaded at startup. If true, the default history file will be used (~/.yuma/.yangcli_history) for loading and saving the history buffer. This is the default value. If false, the history buffer will only be stored and loaded manually with the history command. .IP --\fBautoload\fP=boolean Controls whether any YANG content modules will be automatically loaded upon startup or upon session startup with a server. This is the default value. If false, the 'load-module' command must be used to explicitly load all the desired definition modules. .IP --\fBautouservars\fP=boolean Controls whether the yangcli user variables will be saved at exit and loaded at startup. If true, the 'uservars-file' parameter will be used if set, or else the default user variables file will be used (~/.yuma/yangcli_uservars.xml), for loading and saving the yangcli user variables. If false, the yangcli user variables will only be stored and loaded manually with the uservars command. The default is true. .IP --\fBbad-data\fP=enum Specifies how invalid user input from the CLI will be handled when filling PDUs for remote operations. .nf enum values: ignore Silently accept invalid PDU and data model parameters. Intended for advanced server testing mode only. warn Warn, but accept invalid PDU and data model parameters. check Prompt the user to keep the invalid value or re-enter the value. error Prompt the user to re-enter the invalid value. .fi .IP --\fBbatch-mode\fP If present, the interactive CLI will not be used. A script should be provided with the 'run-script' parameter, or a command provided with the 'run-command' parameter, or else the program will simply exit. If the auto-connect mode is enabled, then this will mode simply test if a NETCONF session can be established, then exit. .IP --\fBconfig\fP=filespec The name of the configuration file to use. Any parameter except this one can be set in the config file. The default config file .I /etc/yuma/yangcli.conf will be not be checked if this parameter is present. .IP --\fBdatapath\fP=list Internal file search path for configuration data files. Overrides the YUMA_DATAPATH environment variable. .IP --\fBdefault-module\fP=string Default module name string to use before 'netconf' and 'yangcli' are tried. The module prefix may need to be used for other modules. .IP --\fBdeviation\fP=string This parameter identifies a YANG module that should only be checked for deviation statements for external modules. These will be collected and applied to the real module(s) being processed. Deviations are applied as patches to the target module. Since they are not identified in the target module at all (ala imports), they have to be specified explicitly, so they will be correctly processed. Zero or more instances of this parameter are allowed. .IP --\fBdisplay-mode\fP=enum Controls how values are displayed during output to STDOUT or a log file. .nf enum values: plain Plain identifier without any prefix format. prefix Plain text with XML prefix added format. module Plain text with module name as prefix added format. xml XML format. xml-nons XML format, but without any namespace (xmlns) attributes. json JSON format. .fi .IP --\fBdump-session\fP= Filespec base for dumping the raw netconf traffic data and timestamp information. Example: .nf --dump-session=/tmp/ses- The following files are created: * /tmp/ses-out - session data sent to server * /tmp/ses-in - session data received from server * /tmp/ses-out.ts - size and timestamp for data transmitted * /tmp/ses-in.ts - size and timestamp for data received .fi .IP --\fBecho-replies\fP=boolean Allow RPC replies to be echoed to the log or STDOUT. If true, messages containing data will be output to the log, if log-level is 'info' or higher. If false, messages containing data will not be output to the log, regardless of the value of log-level. .IP --\fBecho-requests\fP=boolean Allow RPC requests to be echoed to the log or STDOUT. If true, messages will be output to the log, if log-level is 'info' or higher. If false, messages will not be output to the log, regardless of the value of log-level. .IP --\fBfeature-disable\fP=module:feature Identifies a feature which should be considered disabled. Zero or more entries are allowed. .IP --\fBfeature-enable-default\fP=boolean If true (the default), then features will be enabled by default. If false, then features will be disabled by default. .IP --\fBfeature-enable\fP=module:feature Identifies a feature which should be considered enabled. Zero or more entries are allowed. .IP --\fBfixorder\fP=boolean Controls whether PDU parameters will be automatically sent to the server in the correct order. If true, then canonical order will be used. This is the default value. If false, the specified order will be used. .IP --\fBforce-target\fP=enum Controls whether the candidate or running configuration datastore will be used as the default edit target, when both are supported by the server. .nf enum values: candidate Force default edit target to be candidate. running Force default edit target to be running. .fi .IP --\fBhelp\fP Print this help text and exit. The help-mode choice (--brief, --normal, or --full) may also be present to control the amount of help text printed. .IP --\fBhome\fP=dirspec Directory specification for the home directory to use instead of HOME. .IP --\fBindent\fP=number Number of spaces to indent (0..9) in formatted output. The default is 2 spaces. .IP --\fBlog\fP=filespec Filespec for the log file to use instead of STDOUT. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. .IP --\fBlog-append\fP If present, the log will be appended not over-written. If not, the log will be over-written. Only meaningful if the \fBlog\fP parameter is also present. .IP --\fBlog-level\fP=enum Sets the debug logging level for the program. .nf enum values: off error warn info debug debug2 debug3 debug4 .fi .IP --\fBmatch-names\fP=enum Match mode to use for UrlPath name searches. .nf enum values: exact The name must exactly match the node name for all characters in both name strings. exact-nocase The name must match the node name for all characters in both name strings. Strings are not case-sensitive. one The name must exactly match the first N characters of just one node name, which must be the only partial name match found. one-nocase The name must exactly match the first N characters of just one node name, which must be the only partial name match found. Strings are not case-sensitive. first The name must exactly match the first N characters of any node name. The first one found will be used. first-nocase The name must exactly match the first N characters of any node name. The first one found will be used. Strings are not case-sensitive. .fi .IP --\fBmodpath\fP=list Directory search path for YANG and YIN files. Overrides the YUMA_MODPATH environment variable. .IP --\fBmodule\fP=string YANG or YIN source module name to load upon startup. If this string represents a filespec, ending with the \fB.yang\fP or \fB.yin\fP extension, then only that file location will be checked. If this string represents a module name, then the module search path will be checked for a file the \fB.yang\fP or \fB.yin\fP extension. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. .nf ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path .fi .IP --\fBncport\fP=number The NETCONF port number to use for starting sessions. If not present, then port 830, followed by port 22, will be tried. .IP --\fBpassword\fP=string User password to use for NETCONF sessions. If none, then user will be prompted before connecting. .IP --\fBprivate-key\fP=string Contains the file path specification for the file containing the client-side private key. If both 'public-key' and 'private-key' files are present, the client will attempt to connect to the server using these keys. If this fails, or not done, then password authentication will be attempted. .IP --\fBprotocols\fP=bits Specifies which protocol versions the program or session will attempt to use. Empty set is not allowed. Default is to enable all protocols. .nf bit values: netconf1.0 RFC 4741 base:1.0 netconf1.1 RFC xxxx base:1.1 .fi .IP --\fBpublic-key\fP=string Contains the file path specification for the file containing the client-side public key. If both 'public-key' and 'private-key' files are present, the client will attempt to connect to the server using these keys. If this fails, or not done, then password authentication will be attempted. .IP --\fBrun-command\fP=string The specified command will be invoked upon startup. If the auto-connect parameters are provided, then a session will be established before running the command. .IP --\fBrun-script\fP=string The specified script will be invoked upon startup. If the auto-connect parameters are provided, then a session will be established before running the script. If a quoted string is used, then any parameters after the script name will be passed to the script. .IP --\fBrunpath\fP=list Internal file search path for script files. Overrides the YUMA_RUNPATH environment variable. .IP --\fBserver\fP=string IP address or DNS name of the NETCONF server target to use for the auto-startup mode, or as the default value when starting a new session. .IP --\fBsubdirs\fP=boolean If false, the file search paths for modules, scripts, and data files will not include sub-directories if they exist in the specified path. If true, then these file search paths will include sub-directories, if present. Any directory name beginning with a dot (\fB.\fP) character, or named \fBCVS\fP, will be ignored. This is the default mode. .IP --\fBtime-rpcs\fP=boolean Measure the round-trip time of each request and at the session level. Echo the elapsed time value to screen if in interactive mode, as well as the log if the log is a file instead of stdout. .IP --\fBtimeout\fP=number The number of seconds to wait for a response from the server before declaring a timeout. Zero means do not timeout at all. .IP --\fBtransport\fP=enum Identifies the transport protocol that should be used. This is the default that will be used or the value used in auto-connect mode. The value can also be provided when invoking the 'connect' command. .nf enum values: ssh NETCONF over SSH. RFC 4742; RFC 6242 tcp NETCONF over TCP. If this enum is selected, then the default --ncport value is set to 2023, and the --protocols value is set to netconf1.0. The --password value will be ignored. .fi .IP --\fBuse-xmlheader\fP= Specifies how file result variables will be written for XML files. Controls whether the XML preamble header will be written or not. .IP --\fBuser\fP=string User name to use for NETCONF sessions. This value will be used in auto-startup mode, or as the default value when starting a new session. .IP --\fBuservars-file\fP=filespec Specifies the yangcli user variables file to use. The default is ~/.yuma/yangcli_uservars.xml. .IP --\fBversion\fP Print the program version string and exit. .IP --\fBwarn-idlen\fP=number Control whether identifier length warnings will be generated. The value zero disables all identifier length checking. If non-zero, then a warning will be generated if an identifier is defined which has a length is greater than this amount. range: 0 | 8 .. 1023. The default value is 64. .IP --\fBwarn-linelen\fP=number Control whether line length warnings will be generated. The value zero disables all line length checking. If non-zero, then a warning will be generated if the line length is greater than this amount. Tab characters are counted as 8 spaces. range: 0 | 40 .. 4095. The default value is 72. .IP --\fBwarn-off\fP=number Control whether the specified warning number will be generated and counted in the warning total for the module being parsed. range: 400 .. 899. This parameter may be entered zero or more times. .IP --\fByuma-home\fP=string Directory for the yuma project root to use. If present, this directory location will override the YUMA_HOME environment variable, if it is present. If a zero-length string is entered, then the YUMA_HOME environment variable will be ignored. .SH SEARCH PATH When a module name is entered as input, or when a module or submodule name is specified in an import or include statement within the file, the following search algorithm is used to find the file: .nf 1) file is in the current directory 2) YUMA_MODPATH environment var (or set by modpath parameter) 3) $HOME/modules directory 4) $YUMA_HOME/modules directory 5) $YUMA_INSTALL/modules directory OR default install module location, '/usr/share/yuma/modules' .fi By default, the entire directory tree for all locations (except step 1) will be searched, not just the specified directory. The \fBsubdirs\fP parameter can be used to prevent sub-directories from being searched. Any directory name beginning with a dot character (\fB.\fP) will be skipped. Also, any directory named \fBCVS\fP will be skipped in directory searches. .SH ERROR LOGGING By default, warnings and errors are sent to STDOUT. A log file can be specified instead with the \fBlog\fP' parameter. Existing log files can be reused with the 'logappend' parameter, otherwise log files are overwritten. The logging level can be controlled with the \fBlog-level\fP parameter. The default log level is 'info'. The log-levels are additive: .nf off: suppress all errors (not recommended!) A program return code of '1' indicates some error. error: print errors warn: print warnings info: print generally interesting trace info debug: print general debugging trace info debug2: print verbose debugging trace info debug3: print very verbose debugging trace info debug4: print maximum debugging trace info .fi .SH ENVIRONMENT The following optional environment variables can be used to control module search behavior: .IP \fBHOME\fP The user's home directory (e.g., /home/andy) .IP \fBYUMA_HOME\fP The root of the user's Yuma work directory (e.g., /home/andy/swdev/netconf) .IP \fBYUMA_INSTALL\fP The root of the directory that yangdump is installed on this system (default is, /usr/share/yuma) .IP \fBYUMA_DATAPATH\fP Colon-separated list of directories to search for data files. (e.g.: './workdir/data-files:/home/andy/test-data') The \fBdatapath\fP parameter will override this environment variable, if both are present. .IP \fBYUMA_MODPATH\fP Colon-separated list of directories to search for modules and submodules. (e.g.: './workdir/modules:/home/andy/test-modules') The \fBmodpath\fP parameter will override this environment variable, if both are present. .IP \fBYUMA_RUNPATH\fP Colon-separated list of directories to search for script files. (e.g.: './workdir/scripts:/home/andy/scripts') The \fBrunpath\fP parameter will override this environment variable, if both are present. .SH CONFIGURATION FILES .IP \fByangcli.conf\fP YANG config file The default is: \fB/etc/yuma/yangcli.conf\fP An ASCII configuration file format is supported to store command line parameters. The \fBconfig\fP parameter is used to specify a specific config file, otherwise the default config file will be checked. .nf - A hash mark until EOLN is treated as a comment - All text is case-sensitive - Whitespace within a line is not significant - Whitespace to end a line is significant/ Unless the line starts a multi-line string, an escaped EOLN (backslash EOLN) is needed to enter a leaf on multiple lines. - For parameters that define lists, the key components are listed just after the parameter name, without any name, e.g., interface eth0 { # name = eth0 is not listed inside the braces ifMtu 1500 ifName mySystem } .fi A config file can contain any number of parameter sets for different programs. Each program must have its own section, identifies by its name: .nf # this is a comment yangcli { log-level debug default-module yuma-interfaces } .fi .SH FILES The following data files must be present in the module search path in order for this program to function: * \fBYANG module library\fP default: /usr/share/yuma/modules/ * \fByangcli command aliases\fP default: ~/.yuma/.yangcli_aliases CLI parameters: --autoaliases, --aliases-file See the 'aliases' and 'alias' command for details. The format of the aliases file is text. A comment is a line that begins with a '#' character, and will be ignored. Comments are not saved if the --autoaliases=true parameter value is used. Single or double quotes can be used. .nf Example aliases file: --------------------- g=get gc=get-config gcnacm='sget-config --source=running /nacm' geteth0="xget /interfaces/interface[name='eth0']" .fi * \fByangcli user variables\fP default: ~/.yuma/yangcli_uservars.xml CLI parameters: --autouservars, --uservars-file See the 'uservars' command for details. The format of the uservars file is XML. Refer to the 'vars' container in yangcli.yang for a definition of the XML contents. .SH DIAGNOSTICS Internal diagnostics may generate the following type of message if any bugs are detected at runtime: .nf [E0] filename.c:linenum error-number (error-msg) .fi .SH AUTHORS Andy Bierman, Vladimir Vassilev, .SH SEE ALSO .BR netconf-subsystem (1) .BR netconfd (1) yuma123_2.14/netconf/man/make_sil_dir.10000664000175000017500000000514314770023131020041 0ustar vladimirvladimir.\" Process this file with .\" nroff -e -mandoc foo.1 .\" .TH make_sil_dir 1 "February 6, 2012" Linux "make_sil_dir 2.2" .SH NAME make_sil_dir \- create a server instrumentation library working directory .SH SYNOPSIS .nf make_sil_dir [--split] module-name .fi .SH DESCRIPTION .B make_sil_dir is a script to create a server instrumentation library (SIL) root work directory, for a specific YANG module. If the \fB--split\fP parameter is present, then the SIL code generated will be split in 'Yuma' files and 'User' files. A Yuma SIL file or external definition begins with the prefix 'y_'. A User SIL file or external definition begins with the prefix 'u_'. The SIL code can be regenerated without calling this script by invoking 'make code' from the 'src' directory. The following directories and files would be created if the command 'make_sil_dir test' was called from the directory ~/work (without the --split option): .nf ~/work/test/ -+ | + bin/ | + lib/ | + Makefile | + src/ -+ | + Makefile | + test.c (*** Developer edits this file ***) | + test.h .fi In --split mode, the Yuma files never need to be edited. All the code that needs device instrumentation is contained in the User SIL C file. The SIL code can be regenerated without calling this script by invoking 'make splitcode' from the 'src' directory. The following directories and files would be created if the command 'make_sil_dir --split test' was called from the directory ~/work: .nf ~/work/test/ -+ | + bin/ | + lib/ | + Makefile | + src/ -+ | + Makefile | + u_test.c (*** Developer edits this file ***) | + u_test.h | + y_test.c | + y_test.h .fi .SH USAGE This script must be called from the parent directory where the SIL root work directory should be created. The 'module-name' parameter will be used for the name of the directory created. It must match a module in the normal module search path for the SIL code to be generated. .SH AUTHOR Andy Bierman, .SH SEE ALSO .BR netconfd (1) .BR yangcli (1) .BR yangdump (1) yuma123_2.14/netconf/man/Makefile.am0000664000175000017500000000010214770023131017357 0ustar vladimirvladimirdist_man_MANS=yangcli.1 netconfd.1 netconf-subsystem.1 yangdump.1 yuma123_2.14/netconf/man/yangdump.10000664000175000017500000005427714770023131017257 0ustar vladimirvladimir.\" Process this file with .\" nroff -e -mandoc foo.1 .\" .TH yangdump 1 "February 6, 2012" Linux "yangdump 2.2" .SH NAME yangdump \- validate YANG modules and convert them to different formats .SH SYNOPSIS .nf yangdump (module=value | subtree=value)+ [parameter=value...] yangdump --help [brief | normal | full] yangdump --version yangdump --show-errors .fi .SH DESCRIPTION .B yangdump provides validation and translation of YANG data models. Information about a module or submodule can be generated as well. This version of yangdump supports the YANG data modeling language defined in \fBRFC 6020\fP. The \fBformat\fP parameter is used to select a translation output mode. If it is missing, then no translation will be done. This parameter can be used with the module reports parameters, but the translation output should be directed to a file instead of STDOUT to keep them separated. For XSD 1.0 translation, use the \fBformat=xsd\fP parameter. For XHTML 1.0 translation, use the \fBformat=html\fP parameter. For a 1 line output of the module name and version, use the \fBmodversion\fP parameter. For a listing of all the symbols that the file exports to other files, use the \fBexports\fP parameter. For a listing of all the files that the file depends on, to compile, use the \fBdependencies\fP parameter. For a listing of all the accessible object identifiers that the file contains, use the \fBidentifiers\fP parameter. For a listing of all the accessible object identifiers that the file contains, in tree format, use the \fBtree-identifiers\fP parameter. .SH USAGE Parameters can be entered in any order, and have the form: \fB[start] name [separator [value]]\fP where: \fBstart\fP == 0, 1, or 2 dashes (foo, -foo, --foo) \fBname\fP == parameter name .nf Parameter name completion will be attempted if a partial name is entered. .fi \fBseparator\fP == whitespace or equals sign (foo=bar, foo bar) \fBvalue\fP == string value for the parameter. .nf Strings with whitespace need to be double quoted (--foo="some string") .fi Some examples of valid command line parameters: .nf foo=3 -foo=3 --foo=3 foo 3 foo=fred --foo "fred flintstone" .fi Partial parameter names can be entered if they are unique. .SH OPTIONS .IP --\fBconfig\fP=filespec The name of the configuration file to use. Any parameter except this one can be set in the config file. The default config file .I /etc/yuma/yangdump.conf will be not be checked if this parameter is present. .IP --\fBdefnames\fP=boolean If true, output to a file with the default name for the format, in the current directory. The default value is 'false'. If the \fBoutput\fP parameter is present and represents an existing directory, then the default filename will be created in that directory, instead of the current directory. .IP --\fBdependencies\fP Validate the file, write the module name, version and module source for each file that this [sub]module imports and includes, then exit. Each dependency type, name, version, and source is listed once. If the dependency version and source are missing, then that import or include file was not found. .IP --\fBdeviation\fP=string This parameter identifies a YANG module that should only be checked for deviation statements for external modules. These will be collected and applied to the real module(s) being processed. Deviations are applied as patches to the target module. Since they are not identified in the target module at all (ala imports), they have to be specified explicitly, so they will be correctly processed. Zero or more instances of this parameter are allowed. .IP --\fBexports\fP Validate the file, write information for the symbols that this [sub]module exports, then exit. Report includes the following info for the specific file, not the entire module, if submodules are used: .nf - [sub]module name - version - source filespec - namespace (module only) - prefix (module only) - belongs-to (submodule only) - typedefs - groupings - objects, rpcs, notifications - extensions. .fi .IP --\fBfeature-code-default\fP=enum If 'dynamic' (the default), then dynamic SIL feature code will be generated by default. If 'static', then static SIL feature code will be generated by default. If false, then features will be disabled by default. .IP --\fBfeature-disable\fP=module:feature Identifies a feature which should be considered disabled. .IP --\fBfeature-dynamic\fP=module:feature Identifies a dynamic feature for SIL code generation purposes. Zero or more entries are allowed. .IP --\fBfeature-enable-default\fP=boolean If true (the default), then features will be enabled by default. If false, then features will be disabled by default. .IP --\fBfeature-enable\fP=module:feature Identifies a feature which should be considered enabled. Zero or more entries are allowed. .IP --\fBfeature-static\fP=module:feature Identifies a static feature for SIL code generation purposes. Zero or more entries are allowed. .IP --\fBformat\fP=string Type of conversion desired, if any. If this parameter is missing, then no translation will be done, but the module will be validated, and any requested reports will be generated. The following translation formats are available: .nf xsd: XSD 1.0 html: XHTML 1.0 yang: Canonical YANG (in progress) copy: Validate, and copy with a new name yin: YIN format c: Combined Yuma and User SIL C module h: Combined Yuma and User SIL H file uc: User part of a split SIL C module uh: User part of a split SIL H file yc: Yuma part of a split SIL C module yh: Yuma part of a split SIL H file .fi .IP --\fBhelp\fP Print this help text and exit. The help-mode choice (--brief, --normal, or --full) may also be present to control the amount of help text printed. .IP --\fBhome\fP=dirspec Directory specification for the home directory to use instead of HOME. .IP --\fBhtml-div\fP If HTML translation is requested, then this parameter will cause the output to be a single
element, instead of an entire HTML file. This allows the HTML translation to be easily integrated within more complex WEB pages, but the proper CSS definitions need to be present for the HTML to render properly. The default filename extension will be '.div' instead of '.html' if this parameter is present. The contents will be well-formed XHTML 1.0, but without any namespace declarations. .IP --\fBhtml-toc\fP=string The HTML Table of Contents output mode. Ignored unless the \fBformat\fP parameter is set to \fBhtml\fP. Default is \fBmenu\fP. Values: .nf - none: no ToC generated - plain: plain list ToC generated - menu: drop-down menu ToC generated. .fi .IP --\fBidentifiers\fP Validate the file, write the list of object identifiers, that this [sub]module contains, then exit. Each accessible object node is listed once, including all child nodes. Notifications and RPC methods are considered top-level objects, and have object identifiers as well as configuration and state data.. .IP --\fBindent\fP=number Number of spaces to indent (0..9) in formatted output. The default is 2 spaces. .IP --\fBlog\fP=filespec Filespec for the log file to use instead of STDOUT. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. .IP --\fBlog-append\fP If present, the log will be appended not over-written. If not, the log will be over-written. Only meaningful if the \fBlog\fP parameter is also present. .IP --\fBlog-level\fP=enum Sets the debug logging level for the program. .IP --\fBmodpath\fP=list Directory search path for YANG and YIN files. Overrides the YUMA_MODPATH environment variable. .IP --\fBmodule\fP=string YANG or YIN source module name to validate and convert. If this string represents a filespec, ending with the \fB.yang\fP or \fB.yin\fP extension, then only that file location will be checked. If this string represents a module name, then the module search path will be checked for a file the \fB.yang\fP or \fB.yin\fP extension. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. .nf ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path .fi .IP --\fBmodversion\fP Validate the file, write the [sub]module name, version and source filespec, then exit. .IP --\fBobjview\fP=string Determines how objects are generated in HTML and YANG outputs. The default mode is the \fBraw\fP view. XSD output is always \fBcooked\fP, since refined groupings and locally-scoped definitions are not supported in XSD. Values: .nf raw -- output includes augment and uses clauses, not the expanded results of those clauses. cooked -- output does not include augment or uses clauses, just the objects generated from those clauses. .fi .IP --\fBoutput\fP=filespec Output file name to use. Default is STDOUT if none specified and the \fBdefname\fP parameter is also missing. If this parameter represents an existing directory, then the \fBdefnames\fP parameter will be assumed by default, and the translation output file(s) will be generated in the specified directory. If this parameter represents a file name, then the \fBdefnames\fP parameter will be ignored, and all translation output will be directed to the specified file. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. .nf ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path .fi .IP --\fBshow-errors\fP If present, list each error or warning number and its default message string. The program will exit after this is done. .IP --\fBsimurls\fP=boolean If true, and if HTML translation is requested, then this parameter will cause the format of URLs within links to be generated in simplified form, for WEB development engines such as CherryPy, which support this format. The default is false. .nf Normal URL format: example.html?parm1=foo&parm2=bar#frag Simplified URL format: example/foo/bar#frag .fi .IP --\fBstats\fP=enumeration Controls YANG usage statistics report generation. .nf enum values: none: (default) No statistics reporting will be done. brief: Brief statistics reporting will be done: - Complexity score - Total nodes basic: Basic statistics reporting will be done. advanced: Advanced statistics reporting will be done. all: All possible statistics reporting will be done. .fi .IP --\fBsubdirs\fP=boolean If false, the file search paths for modules, scripts, and data files will not include sub-directories if they exist in the specified path. If true, then these file search paths will include sub-directories, if present. Any directory name beginning with a dot (\fB.\fP) character, or named \fBCVS\fP, will be ignored. This is the default mode. .IP --\fBsubtree\fP=string Path specification of the directory subtree to convert. All of the YANG and YIN source modules contained in the specified directory sub-tree will be processed. If the \fBformat\fP parameter is present, then one file with the default name will be generated for each YANG or YIN file found in the sub-tree. Note that symbolic links are not followed during the directory traversal. Only real directories will be searched and regular files will be checked as modules. Processing will continue to the next file if a module contains errors. If this string begins with a '~' character, then a username is expected to follow or a directory separator character. If it begins with a '$' character, then an environment variable name is expected to follow. This parameter may be present zero or more times. .nf ~/some/path ==> /some/path ~fred/some/path ==> /some/path $workdir/some/path ==> /some/path .fi .IP --\fBtotals\fP=enumeration Controls summary YANG usage statistics report generation. Must be used with the '--stats' parameter. .nf enum values: none: (default) No summary statistics reporting will be done. summary: Summary statistics totals will be reported, based on the stats mode that is requested. summary-only Only the summary statistics totals will be reported, based on the stats mode that is requested. This mode will cause all individual module statistics reports to be generated, and a summary for all input modules will be generated instead. .fi .IP --\fBtree-identifiers\fP Validate the file, write the hierarchy of node names in tree format, that this [sub]module contains, then exit. Each accessible object node is listed once, including all child nodes. Notifications and RPC methods are considered top-level objects, and have object identifiers as well as configuration and state data.. .IP --\fBunified\fP=boolean If true, then submodules will be processed within the main module, in a unified report, instead of separately, one report for each file. For translation purposes, this parameter will cause any sub-modules to be treated as if they were defined in the main module. Actual definitions will be generated instead of an 'include' directive, for each submodule. If false (the default), then a separate output file is generated for each input file, so that XSD output and other reports for a main module will not include information for submodules. If this parameter is set to true, then submodules entered with the \fBmodule\fP parameter will be ignored. .IP --\fBurlstart\fP=string If present, then this string will be used to prepend to HREF links and URLs generated for SQL and HTML translation. It is expected to be a URL ending with a directory path. The trailing separator '/' will be added if it is missing. If not present (the default), then relative URLs, starting with the file name will be generated instead. For example, if this parameter is set to .nf 'http://acme.com/public' .fi then the URL generated for the 'bar' type on line 53, in the module FOO (version 2008-01-01) would be: .nf if no-versionnames set: 'http://acme.com/public/FOO.html#bar.53' OR if no-versionnames not set (default): 'http://acme.com/public/FOO_2008-01-01.html#bar.53' .fi .IP --\fBversion\fP Print yangdump version string and exit. .IP --\fBversionnames\fP=boolean If false, the default filenames will not contain the module version string. If true, the [sub]module name and version string are both used to generate a default file name, when the \fBdefnames\fP output parameter is used. This flag will cause filenames and links to be generated which do not contain the version string. The default value is true. .IP --\fBwarn-idlen\fP=number Control whether identifier length warnings will be generated. The value zero disables all identifier length checking. If non-zero, then a warning will be generated if an identifier is defined which has a length is greater than this amount. range: 0 | 8 .. 1023. The default value is 64. .IP --\fBwarn-linelen\fP=number Control whether line length warnings will be generated. The value zero disables all line length checking. If non-zero, then a warning will be generated if the line length is greater than this amount. Tab characters are counted as 8 spaces. range: 0 | 40 .. 4095. The default value is 72. .IP --\fBwarn-off\fP=number Control whether the specified warning number will be generated and counted in the warning total for the module being parsed. range: 400 .. 899. This parameter may be entered zero or more times. .IP --\fBxsd-schemaloc\fP=string If present, then this string will be used to prepend to output XSD filenames, when generating schemaLocation clauses. It is expected to be a URL ending with a directory path. The trailing separator '/' will be added if it is missing. This parameter is also prepended to URLs generated fpr include and import directives within the XSD. If not present (the default), then the schemaLocation element is not generated during XSD translation. Relative URLs for include and import directives will be generated, starting with the file name. For example, if this parameter is set to .nf 'http://acme.com/public' .fi then the schemaLocation XSD for the module FOO (version 01-01-2008) would be: .nf if no-versionnames set: 'http://acme.com/public/FOO.xsd' OR if no-versionnames not set (default): 'http://acme.com/public/FOO_2008-01-01.xsd' .fi .IP --\fByuma-home\fP=string Directory for the yuma project root to use. If present, this directory location will override the YUMA_HOME environment variable, if it is present. If a zero-length string is entered, then the YUMA_HOME environment variable will be ignored. .SH INPUT FILES Operations can be performed on one or more files with the \fBmodule\fP parameter, or an entire directory tree with the \fBsubtree\fP parameter. Unless the \fBhelp\fP, \fBversion\fP, or \fBshow-errors\fP parameters is entered, one of these input file parameters is mandatory. Each of these parameters may be entered multiple times. The default parameter for yangdump is 'module', so these commands are wquivalent: .nf yangdump --module=foo yangdump foo .fi Note that 'foo' must not match another parameter name. If it does, the module parameter name must be used for that module. For example, .nf yangdump --module=help .fi .SH SEARCH PATH When a module name is entered as input, or when a module or submodule name is specified in an import or include statement within the file, the following search algorithm is used to find the file: .nf 1) file is in the current directory 2) YUMA_MODPATH environment var (or set by modpath parameter) 3) $HOME/modules directory 4) $YUMA_HOME/modules directory 5) $YUMA_INSTALL/modules directory OR default install module location, '/usr/share/yuma/modules' .fi By default, the entire directory tree for all locations (except step 1) will be searched, not just the specified directory. The \fBsubdirs\fP parameter can be used to prevent sub-directories from being searched. Any directory name beginning with a dot character (\fB.\fP) will be skipped. Also, any directory named \fBCVS\fP will be skipped in directory searches. .SH OUTPUT MODES By default, any translation output will be sent to \fBSTDOUT\fP. The \fBoutput\fP parameter can be used to specify the full filespec of the output file to use instead. The \fBdefname\fP parameter can be used to generate a default filename in the current directory for the output. E.g., the default XSD filename is \fB_.xsd\fP. This is the default mode when \fBsubtree\fP input mode is selected. .SH ERROR LOGGING By default, warnings and errors are sent to STDOUT. A log file can be specified instead with the \fBlog\fP' parameter. Existing log files can be reused with the 'logappend' parameter, otherwise log files are overwritten. The logging level can be controlled with the \fBlog-level\fP parameter. The default log level is 'info'. The log-levels are additive: .nf off: suppress all errors (not recommended!) A program return code of '1' indicates some error. error: print errors warn: print warnings info: print generally interesting trace info debug: print general debugging trace info debug2: print verbose debugging trace info debug3: print very verbose debugging trace info debug4: print maximum debugging trace info .fi .SH ENVIRONMENT The following optional environment variables can be used to control module search behavior: .IP \fBHOME\fP The user's home directory (e.g., /home/andy) .IP \fBYUMA_HOME\fP The root of the user's Yuma work directory (e.g., /home/andy/swdev/netconf) .IP \fBYUMA_INSTALL\fP The root of the directory that yangdump is installed on this system (default is, /usr/share/yuma) .IP \fBYUMA_MODPATH\fP Colon-separated list of directories to search for modules and submodules. (e.g.: './workdir/modules:/home/andy/test-modules') The \fBmodpath\fP parameter will override this environment variable, if both are present. .SH CONFIGURATION FILES .IP \fByangdump.conf\fP YANG config file The default is: \fB/etc/yuma/yangdump.conf\fP An ASCII configuration file format is supported to store command line parameters. The \fBconfig\fP parameter is used to specify a specific config file, otherwise the default config file will be checked. .nf - A hash mark until EOLN is treated as a comment - All text is case-sensitive - Whitespace within a line is not significant - Whitespace to end a line is significant/ Unless the line starts a multi-line string, an escaped EOLN (backslash EOLN) is needed to enter a leaf on multiple lines. - For parameters that define lists, the key components are listed just after the parameter name, without any name, e.g., interface eth0 { # name = eth0 is not listed inside the braces ifMtu 1500 ifName mySystem } .fi A config file can contain any number of parameter sets for different programs. Each program must have its own section, identifies by its name: .nf # this is a comment yangdump { log-level debug output "~/swdev/testfiles" } netconfd { ... } .fi .SH FILES The following data files must be present in the module search path in order for this program to function: * \fBYANG module library\fP default: /usr/share/yuma/modules/ .SH DIAGNOSTICS Internal diagnostics may generate the following type of message if any bugs are detected at runtime: .nf [E0] filename.c:linenum error-number (error-msg) .fi .SH AUTHOR Andy Bierman, .SH SEE ALSO .BR netconfd (1) .BR yangcli (1) .BR yangdiff (1) yuma123_2.14/debian/0000775000175000017500000000000014770056173014361 5ustar vladimirvladimiryuma123_2.14/debian/libyuma2.lintian-overrides0000664000175000017500000000026714770023131021456 0ustar vladimirvladimir#The three libraries share the same source and API/ABI versioning and don't #need 3 different packages libyuma2: package-name-doesnt-match-sonames libyumaagt2 libyumamgr2 libyumancx2 yuma123_2.14/debian/source/0000775000175000017500000000000014770023131015645 5ustar vladimirvladimiryuma123_2.14/debian/source/format0000664000175000017500000000001414770023131017053 0ustar vladimirvladimir3.0 (quilt) yuma123_2.14/debian/control0000664000175000017500000001247514770056173015775 0ustar vladimirvladimirSource: yuma123 Section: net Priority: optional Maintainer: Vladimir Vassilev Build-Depends: debhelper (>= 10), autoconf, libncurses5-dev, libreadline-dev, libssh2-1-dev, libssl-dev, libtool, libxml2-dev, pkg-config, zlib1g-dev Standards-Version: 4.1.4 Homepage: https://yuma123.org Package: libyuma-base Section: libs Architecture: all Depends: ${misc:Depends} Multi-Arch: foreign Description: Configuration script, YANG modules and documentation The NETCONF protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . This package includes configuration scripts, documentation and YANG modules for the NETCONF an YANG libraries, agents and applications. YANG modules contain a formal description of the data that can be managed using NETCONF and applications. Package: libyuma2 Section: libs Architecture: any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends} Depends: ${shlibs:Depends}, ${misc:Depends}, libyuma-base Description: NETCONF/YANG library The NETCONF protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yuma123 libyuma library contains common functions for the construction, sending, receiving, decoding, and manipulation of the NETCONF requests and responses. Package: libyuma-dev Section: libdevel Architecture: any Multi-Arch: same Provides: libyuma-dev Depends: ${misc:Depends} Description: NETCONF/YANG application development files The NETCONF protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yuma123 libyuma development files include the library headers, static libraries, and documentation needed for development of custom NETCONF/YANG applications. Package: netconfd Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, debconf, lsb-base, libyuma-base, libyuma2 Description: NETCONF (RFC6241) agent NETCONF provides a framework for the exchange of management information between agents (servers) and clients. . The yuma123 agent is a daemon which listens for incoming NETCONF requests from clients and provides responses. Package: yangcli Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, libyuma-base, libyuma2 Description: NETCONF/YANG command line client application The NETCONF protocol and the YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yuma123 yangcli application allows the user to issue NETCONF requests to agents according to the specific YANG modules of the agents. Package: yangdump Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, libyuma-base, libyuma2 Description: Validate YANG modules and convert them to different formats The NETCONF protocol and the YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yangdump application allows the user to validate YANG modules and convert YANG to different formats. Package: netconfd-module-ietf-interfaces Architecture: any Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends}, debconf, lsb-base, libyuma2 (>= 2.8), iproute2, net-tools Description: SIL module for netconfd implementing ietf-interfaces.yang The NETCONF protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yuma123 netconfd module ietf-interfaces implements the functionality modeled in ietf-interfaces.yang using some common command line tools. Package: netconfd-module-ietf-system Architecture: any Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends}, debconf, lsb-base, libyuma2 (>= 2.8) Description: SIL module for netconfd implementing ietf-system.yang The NETCONF protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yuma123 netconfd module ietf-interfaces implements the functionality modeled in ietf-system.yang using some common command line tools. Package: libyangrpc2 Section: libs Architecture: any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends} Depends: ${shlibs:Depends}, ${misc:Depends}, libyuma-base, libyuma2 Description: NETCONF/YANG library for simple client applications The NETCONF protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yuma123 yangrpc library contains common functions for the construction, sending, receiving, decoding, and manipulation of the NETCONF requests and responses used on simple manager clients. Package: libyangrpc-dev Section: libdevel Architecture: any Multi-Arch: same Provides: libyangrpc-dev Depends: libc6-dev, libyangrpc2 (=${binary:Version}),${misc:Depends} Description: NETCONF/YANG simple client applications development files The NETCONF protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yuma123 libyangrpc development files include the library headers, static libraries, and documentation needed for development of custom NETCONF/YANG applications. yuma123_2.14/debian/rules0000775000175000017500000000145114770023131015426 0ustar vladimirvladimir#!/usr/bin/make -f #export DH_VERBOSE=1 LIB_VERSION = 2 %: dh $@ --with autoreconf override_dh_auto_configure: dh_auto_configure -- --prefix=/usr --sysconfdir=/etc --mandir=/usr/share/man \ --with-persistent-directory=/var/lib/yuma \ --enable-shared --with-cflags="$(CFLAGS)" \ --with-ldflags="$(LDFLAGS)" override_dh_installdocs: dh_installdocs -plibyuma-base dh_installdocs -plibyuma$(LIB_VERSION) dh_installdocs --link-doc=libyuma$(LIB_VERSION) \ -plibyuma-dev \ -pnetconfd \ -pyangcli \ -pyangdump \ -plibyangrpc$(LIB_VERSION) \ -plibyangrpc-dev \ -pnetconfd-module-ietf-interfaces \ -pnetconfd-module-ietf-system override_dh_makeshlibs: dh_makeshlibs -Xhelloworld -Xtoaster -Xietf-interfaces -Xietf-system override_dh_clean: dh_autoreconf_clean dh_clean yuma123_2.14/debian/yangdump.install0000664000175000017500000000005714770023131017563 0ustar vladimirvladimirusr/bin/yangdump usr/share/man/man1/yangdump.1 yuma123_2.14/debian/libyangrpc2.install0000664000175000017500000000003314770023131020145 0ustar vladimirvladimirusr/lib/*/libyangrpc*.so.* yuma123_2.14/debian/libyuma-base.install0000664000175000017500000000037614770023131020315 0ustar vladimirvladimirusr/share/yuma/modules/examples/helloworld.yang usr/share/yuma/modules/ietf/ usr/share/yuma/modules/ietf-derived/ usr/share/yuma/modules/ietf-draft/ usr/share/yuma/modules/netconfcentral/ usr/share/yuma/modules/yuma123/ usr/share/yuma/nmda-modules/ietf/ yuma123_2.14/debian/libyuma2.install0000664000175000017500000000003014770023131017452 0ustar vladimirvladimirusr/lib/*/libyuma*.so.* yuma123_2.14/debian/netconfd.install0000664000175000017500000000026114770023131017534 0ustar vladimirvladimirusr/sbin/netconfd usr/sbin/netconf-subsystem usr/share/man/man1/netconfd.1 usr/share/man/man1/netconf-subsystem.1 usr/lib/*/yuma/libhelloworld.so* usr/lib/*/yuma/libtoaster.so* yuma123_2.14/debian/libyangrpc-dev.install0000664000175000017500000000006414770023131020643 0ustar vladimirvladimirusr/include/yuma/yangrpc/* usr/lib/*/libyangrpc*.so yuma123_2.14/debian/libyuma-dev.install0000664000175000017500000000016714770023131020157 0ustar vladimirvladimirusr/include/yuma/ncx/* usr/include/yuma/agt/* usr/include/yuma/mgr/* usr/include/yuma/platform/* usr/lib/*/libyuma*.so yuma123_2.14/debian/copyright0000664000175000017500000001313614770023131016304 0ustar vladimirvladimirFormat: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: yuma123 Upstream-Contact: Vladimir Vassilev Source: https://sourceforge.net/projects/yuma123 Files: * Copyright: 2008-2012 Andy Bierman 2009-2010 Netconf Central, Inc. 2012 YumaWorks, Inc. 2013-2025 Vladimir Vassilev 2013-2018 Transpacket AS 2019-2025 Lightside Instruments AS License: BSD-3-clause Files: debian/* Copyright: 2013-2023 Vladimir Vassilev License: BSD-3-clause Files: netconf/modules/ietf/* Copyright: 2011-2025 IETF Trust 2011-2025 Persons identified as the document authors License: BSD-3-clause Files: libtecla/* Copyright: 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. License: MIT Files: libtecla/install-sh Copyright: 1991, Massachusetts Institute of Technology License: MIT Files: netconf/src/ncx/b64.c Copyright: 2001, Bob Trower 2001, Trantor Standard Systems Inc. License: MIT Files: netconf/src/ncx/bobhash.c Copyright: 1996, Bob Jenkins License: public-domain The bobhash function implements the BOB hash algorithm that is originally published as Public domain code in the 1997 Dr Dobbs article By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this code any way you wish, private, educational, or commercial. It's free. Files: example-modules/yuma-arp/yuma-arp.* netconf/src/agt/agt_time_filter.* Copyright: 2008 - 2012, Andy Bierman License: BSD-3-clause Comment: The skeleton of those files were generated by the yangdump tool which generates the boilerplate to build new modules. They are now maintained manually and constitute the preferred form of modification. Files: netconf/perl/*/ppport.h Copyright: 2004-2013, Marcus Holland-Moritz. 2001, Paul Marquess. 1999, Kenneth Albanowski. License: Artistic or GPL-1+ License: BSD-3-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andy Bierman BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. License: MIT All rights reserved. . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. . Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. License: Artistic This program is free software; you can redistribute it and/or modify it under the terms of the Artistic License, which comes with Perl. . On Debian systems, the complete text of the Artistic License can be found in `/usr/share/common-licenses/Artistic'. License: GPL-1+ 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 1, or (at your option) any later version. . On Debian systems, the complete text of version 1 of the GNU General Public License can be found in `/usr/share/common-licenses/GPL-1'. yuma123_2.14/debian/yangcli.install0000664000175000017500000000005514770023131017363 0ustar vladimirvladimirusr/bin/yangcli usr/share/man/man1/yangcli.1 yuma123_2.14/debian/watch0000664000175000017500000000010014770023131015365 0ustar vladimirvladimirversion=3 http://sf.net/yuma123/yuma123_([\d+\.]+|\d+)\.tar\.gz yuma123_2.14/debian/netconfd-module-ietf-system.install0000664000175000017500000000004214770023131023263 0ustar vladimirvladimirusr/lib/*/yuma/libietf-system.so* yuma123_2.14/debian/compat0000664000175000017500000000000314770023131015544 0ustar vladimirvladimir10 yuma123_2.14/debian/changelog0000664000175000017500000000023614770023131016220 0ustar vladimirvladimiryuma123 (2.14-0) stable; urgency=medium * Unofficial package -- Vladimir Vassilev Sun, 23 Mar 2025 15:25:36 +0100 yuma123_2.14/debian/netconfd-module-ietf-interfaces.install0000664000175000017500000000017714770023131024073 0ustar vladimirvladimirusr/lib/*/yuma/libietf-interfaces.so* usr/share/yuma/modules/examples/interfaces-notifications.yang usr/bin/get-interface-ipv4 yuma123_2.14/LICENSE0000777000175000017500000000000014770023131017300 2debian/copyrightustar vladimirvladimiryuma123_2.14/CHANGELOG0000664000175000017500000012445314770023131014346 0ustar vladimirvladimiryuma123 (2.14) stable; urgency=medium * Added support for notifications defined inside list * Bug in cli parsing list keys containing spaces fixed * Added agt_fd_event_cb_register(fd, callback) function for file descriptor event interrupts * Fixed bug in --dump-session always reporting of received chunk size 0 in *-in.ts * Fixed bug in val_set_cplxval_obj function always returning NO_ERR even if validation fails -- Vladimir Vassilev Sun, 23 Mar 2025 15:25:36 +0100 yuma123 (2.13) stable; urgency=medium * ncx - Added if-feature parsing support for YANG 1.1 logical expressions - Fixed bug in obj_next_child_deep processing of models with multiple levels of choice/case statements - Fixed bug in ncx_sprintf_num for decimal64 e.g. -0.000068623 was represented as 0.0000-68623 * netconfd - Fixed bug causing netconfd to not handle --running-error=continue * yangcli - Fill for rpc issue solved by not serializing the virtual - Fixed socket connection timeout and signal handlng e.g. ctrl-c now terminates stall connection attempt * autoconf - pkg-config is now used to find libxml2 * python/test - transition to python3 * examples - Added initial ietf-ip support as part of the ietf-interfaces example -- Vladimir Vassilev Wed, 30 Nov 2022 17:20:00 +0100 yuma123 (2.12) stable; urgency=medium * Added val123_add_virtual_cb to support multiple calbacks "filling" common virtual container. * Fixed anyxml support for config=true data * Improved /system-state/clock/current-datetime resolution to nanoseconds and disabled value caching * Augmented set-my-session and get-my-session RPCs with cache-timeout param allowing cache to be disabled when cache-timeout=0 * Fixed Xpath compare of nodeset to nodeset implementation for numeric nodesets * Updated IETF draft YANG modules -- Vladimir Vassilev Wed, 18 Aug 2021 00:08:23 +0200 yuma123 (2.11) stable; urgency=medium * Improved stability and interoperability * Added more validation testcases * Preliminary NMDA support rfc8342 * RPM build scripts (from ekinzie) * yangcli - Added --use-agent parm for use of ssh-agent authentication (from ekinzie) * yangdump - YANG tree format support rfc8340 -- Vladimir Vassilev Fri, 17 Aug 2018 03:02:54 +0200 yuma123 (2.10) stable; urgency=medium * ncx - YANG 1.1 Xpath functions implementation. - Fixed bug in handling of deviations. - Fixed bug in apply_commit_deletes. - Fixed bug in val_make_serialized_string. - Refactored the base64 implementation. Fixed bugs and inconsistencies. - Added support for multiple obj callback registrations. - Fixed CDATA handling. - Splitted yuma-netconf.yang to yuma123-netconf.yang and yuma123-netconf-types.yang - Various ietf draft models updated. Erratas applied. - Removed circular libyumancx dependencies on libyumamgr * agt - Implementation of ietf-yang-library - Replaced proprietary notifications with the ietf-netconf-notifications.yang defined. - Moved agt_arp.c integrated module as standalone example-modules/yuma-arp * test - Added netconfd testsuite netconf/test/netconfd. - Added litenc client side session scripting python module. * yangcli - Implemented ietf-yang-library client side support. - Fixed bug caused by attempted reload of already loaded module. - Added yes/no container fill check promp. -- Vladimir Vassilev Fri, 29 Sep 2017 13:53:28 +0200 yuma123 (2.9) stable; urgency=medium * Removed IETF copyrighted MIBs to avoid the complications related to +dfsg releases * yangrpc API refactored for stability allowing new options passed as yangcli parameter string * Added dump-session yangcli parameter -- Vladimir Vassilev Sat, 20 Aug 2016 11:46:34 +0200 yuma123 (2.8+dfsg) stable; urgency=medium * Removed IETF copyrighted MIBs and YANG models derived from them -- Vladimir Vassilev Sun, 07 Aug 2016 15:17:40 +0200 yuma123 (2.8) stable; urgency=medium * Resolved bobhash license-problem-non-free-RFC-BCP78 * Fixed unescaped dots in man pages * Fixed debian lintian warning out-of-date-standards-version * Separated libyangrpc from the libyuma package -- Vladimir Vassilev Thu, 28 Jul 2016 23:19:24 +0200 yuma123 (2.7-1) stable; urgency=medium * Cleaned up debian * Renamed yuma libraries libncx->libyumancx, libagt->libyumaagt, libmgr->libyumamgr -- Vladimir Vassilev Thu, 21 Jul 2016 18:16:16 +0200 yuma123 (2.6-1) stable; urgency=medium * ncx - Added val_make_serialized_string to the API * netconfd - Fixed a bug caused by system time adjustments and the use of nonmonotonic time function for timer callback implementation - Fixed bugs both in netconf 1.0 and netconf 1.1 message parsers - Added readline as default cli interface library with option to build with tecla instead - Fixed bugs in must statement validation * yangcli - Added readline as default cli interface library with option to build with tecla instead * examples - Added standalone automake based example projects building ietf-interfaces.yang and ietf-system.yang implementations for linux * yangrpc - Added yangrpc_parse_cli API parsing yangcli style command lines based on the schema context of the connection * python/perl - Initial perl and python bindings -- Sun, 17 Jul 2016 10:10:00 +0200 yuma123 (2.5-1) stable; urgency=low * Implemented direct-must-augment-ex:augment extension -- Thu, 18 Jun 2015 14:48:12 +0200 yuma123 (2.4-1) stable; urgency=low * netconfd - NACM implemented - Debian build system cleanup - Memory leak fixed in xml_rd_open_file -- Mon, 02 Feb 2015 13:37:12 +0100 yuma123 (2.3-1) stable; urgency=medium * Added autotools files to build yuma in standard and portable way. * netconfd - functionality allowing direct connection form yangcli to netconfd over unencrypted tcp socket (--tcp-direct-address="0.0.0.0" --tcp-direct-port=12024 netconfd parameters). - introduced agt_not_queue_notification_cb API adding functionality to allow registering global notification callback - bug in commit_complete code causing problem when more then two different modules register calbacks - changed AGT_TIMER_SKIP_COUNT from 10 to 1 for better resolution - decreased NCX_DEF_VTIMEOUT from 2 to 1 second - introduced val_set_cplxval_obj which initializes a container and populates it with subcontainers and leafs according to xml string - fixed memory leaks caused by un xmlFree -ed buffers - added example-modules directory and a very simple helloworld module to be used as template - fix for the b64_encode which generated \r\n for every fourth byte after > linesize bytes are generated - fixed bug causing memory leak when notification_count of queued notifications exceeded the limit set by agt_profile->agt_eventlog_size - bug causing segmentation fault when session scb is agt_ses_kill_session-ed twice incase of bad - added agt_check_feature function for getting the state of given feature - added command line parameter --non-advertised-module= allowing to "hide" certain modules by taking them out of the hello capabilities list - fixed bug in get_object_string. The function was returning wrong retlen value when called with buf==NULL - added max-sessions netconfd argument to replace the constant (1024) used - fixed bug in log_error format string causing segmentation fault when the error happened - fixed bug caused by unresolved/uninitialized tdef->def.simple.xrefdef members of leafref objects defined within augment sections - added support for 'refine' objects in resolve_xpath - added val_add_meta API function - added autoloading of the server modules based on the received capabilities list from the - fixed memory leak - session and reader in xml_rd_open_file were not freed - added new API function val_get_leafref_targval - added ncxserver-sockname parameter to netconfd and netconf-subsystem. Now multiple instances of netconfd can be started on the same system - replaced the makeshif timplementation of val_find_node with call to xpath1_eval_expr - fixed bug relevant to presistent commit validation check result variables not reinitialized before a new commit validation attempt was made. For example unresolved leafref causes a commit to fail then the missing leaf is added but the commit keeps failing - fixed ancient bug in check_prune_obj causing pruning/skiping of the leafref validation. The bug can be reproduced when the leaf referenced by the leafref is deleted without making changes in the toplevel container containing the leafref. - moved /yuma-system:system to /ieft-system:system-state/yuma-system:yuma * yangcli - patch allowing leaf objects which are not keys to be initialized in the predicate of create target Xpath - taken the config=false objects out of the Xpath tab completion choices for create,replace and delete RPCs - introduced new method for entry of container leafs: create /interfaces/interface -- name='ge0' mtu=1500 - added basic predicate tab completion and seamless tab completion of xpaths after predicate blocks - fixed bug in yangcli tab completion in the cases when the cursor was stending under (not after) the initial '/' - fixed tab completion for paths defined in augment modules - fixed bug in parsing top augment leafs in var_get_script_val. val->nsid was overwritten by 0 and no xmlns meta was added to the generated xml causing netconfd to not accept the data * YANG modules: - replaced yuma-interfaces.yang with ietf-interfaces.yang (rfc7223) - replaced yuma-system.yang with ietf-system.yang (rfc7317) - replaced yuma-nacm.yang with ietf-netconf-acm.yang (rfc6536) * yangrpc: - added experimental blocking (non-callback) API for clients called yangrpc_* (yangrpc_connect, yangrpc_exec, yangrpc_close). An alternative to the mgr_* API suited better for certain applications - added simple apache2 module as yangrpc usage example. Reading state and configuration root as xml and submit form for configurations -- Wed, 10 Dec 2014 17:08:11 +0100 yuma123 (2.2-5.1) stable; urgency=high * Integrated last official yuma release 2.2-5 -- Vladimir Vassilev Thu, 7 Feb 2013 18:22:00 +0100 yuma (2.2-5) stable; urgency=high * netconfd: - fix error-path bugs - extra 'input' node removed from error-path - suppress error-path for RPC access-denied - yuma-proc.yang: add 2 missing leafs to yuma-proc that have been added recently to /proc/cpuinfo - fix sourceforge bug 3572696 Double free memory issue related to subtree filtering and virtual nodes - change server so by default it does not allow port 22 on MACOSX * yangcli: - fix SSH key bug reported on yuma-developers list The filenames were not expanded with ncx_get_source * CLI parse: - fix error message in raw CLI handler code - fix bug where cli_parse_raw can write past the end of the malloced buffer * yangdump: - fix bug in code generation for union; parameter should be string, not val_value_t - fix bug with --identifiers parameter where container with no children caused output to terminate - put correct ietf-netconf-notifications module in ietf module dir - converge yuma-app-common with yumapro version * YANG modules: - update 3 IETF modules - fix test.yang so the default NP containers do not cause errors - refactor yuma-app-common to support --no-config in YumaPro - update metconfcentral YANG modules so they align with Yuma -- Andy Bierman Mon, 29 Oct 2012 11:28:00 -0700 yuma (2.2-4) stable; urgency=medium * netconfd * fixed bug where invalid YANG identifier strings are allowed in NACM module. Now strings such as module-name or rpc-name must be legal YANG identifier strings * fixed bug where error nodes were not getting properly pruned from the element if --startup-error=continue Instead the entire subtree from root would be deleted. Now just the error nodes are deleted during load_running_config * fixed bug where validation errors were not detected by the agt_init function so it reported the startup loaded OK * added agt_load_top_rootcheck_errors flag to profile state. If --running-error=stop is set, the server would reject the config if any errors occurred in agt_val_root_check. Now only root-check failure of top-level nodes cause an error in this case. * fixed bug where the name field for identityref value nodes is not getting set if the value is invalid; now saving name for logging and error purposes (netconfd and yangcli) * fixed bug NACM not checking always nacm:very-secure on default data response * fixed missing unregister-callback (benign) * fixed bug where delete of nested node from candidate does not always get applied to running during a commit (it gets ignored instead) * fixed bug where YANG bits data type (ncx_list_t) function did not check it the embedded Q had been initializing before accessing it * fixed bug where check simval for union containing YANG bits type could cause a segfault when called due to btyp getting set to NCX_BT_BITS but the ncx_list_t was never initialized. * fix bug in log_debug stmt that causes intermittent segfault if log-level >= debug and build is optimized (e.g. -O2) wrong number of printf args passed, not detected by gcc! * fix bug 'netconfd --config=' crash due to assert-fail now checking for empty string before trying to call ncx_get_source updating IETF YANG modules with latest versions * fix bug where session counters were not being returned in the /netconf-state/sessions/session list entry * yangcli: * fixed bug where XPath tab completion did not find objects from new modules loaded with the 'mgrload' command * fixed bug in yangcli where notification nodes were not getting found correctly. * Added more checks in setting NCX extension flags so they are ignored unless they appear in the proper node type. * Fix SF bug 3459707 YangCLI crashes when replacing container * Now allowing value=$var to be a complex type like list or container Now checking for NULL parm val (due to type ANY corner-case) instead of segfault in this case * YANG Parse: * fixed bug in error message printing and resolve_identity that caused SET_ERROR messages to be generated because not all NULL pointers were checked * fixed bug in ncx_mod_exp_err where not all types of string tokens were not being handled properly * fix logging bugs reported by Graham Phillips * YANG parse: fix bug where YANG modules where not getting found sometimes if they are in the form acme-foo@2012-07-12.yang * fixed XPath bug where object testing of the must or when expression uses a top-level object from a different submodule. Caused 'no child node' warning; Value node testing during real XPath is not affected by this bug. * fixed bug must-stmt was not accepted in anyxml-stmt * Build: * fix make distclean in makefiles Do not use 'make superclean' from the top-dir; Use 'make distclean' instead * removed YUMA_HOME from toaster Makefile build dependency * Add gitversion to build to help identify builds during debugging. The last commit ID is used as the ID tag. Derived from 'git describe --always' yangdump 2.2.4f701be If the current branch has been modified, a -M will be added at the end: yangdump 2.2.4f701be-M * Make HTML docs generation part of the build Removed the html files. Now generating xhtml files Need /usr/binwriter2latex12 installed Use 'make htmls' to build out of date HTML files Use 'make DOC=1' to update all PDF and xHTML files update Makefile for better HTML output -- Andy Bierman Wed, 08 Aug 2012 09:12:00 -0800 yuma-transpacket (2.2-3.1-2012-07-24-27096e8e1b4ef9540489a435d058169958b641a41) stable; urgency=low * Added non-interactive filling of created containers -- Vladimir Vassilev Tue, 24 Jul 2012 23:20:31 +0000 yuma123 (2.2-3.1) stable; urgency=high * Fixed bug deleting list nodes from the configuration * Added non-interactive filling of created nodes * Added cross module dependencies validation mechanism with commit_validate * Added rpc-aware xpath tab completion. Ignores status nodes for RPCs dealin$ * Added autoconf/automake build scripts * Fixed printout bug in ncx.c -- Vladimir Vassilev Fri, 20 Jul 2012 18:48:00 +0100 yuma (2.2-3) stable; urgency=medium * netconfd * fix bug in agt_val_root_check where a 'missing-instance' error is incorrectly generated sometimes for an NP-container. This can happen if the NP container has children (or nested NP-container children) which are mandatory (or min-elements > 0) but there are also when-stmts that affect the node. Do not generate an error for NP-container here; if child nodes exist then instance_check for those nodes will check must/when nodes * add 'editing' parameter to val_compare_max because it assumed config=true meant test for editing, so any change will cause the 2 val_value_t nodes to be different (meta-data such as set-by-default vs. client-set-to-default). To simply compare the values and just the config=true nodes, use val_compare_max(val1, val2, TRUE, TRUE, FALSE) * fix bug where edits to candidate are not applied to running during the commit if validate is done on the candidate, e.g.: 1) create /foo 2) validate source=candidate 3) commit * change yuma_arp so it does not build on MACOSX * fix yuma_arp so it does not include any system files on MAC * change SIL makefile for MacOSX to make bundle instead of dynamiclib * change agt.c to load .so file for Mac instead of .dylib for a SIL library * add check to prevent false SET_ERROR trace from occurring in when-check * YANG Parse * fixed bug 3517498 memory leak occurs when the file parsed starts with an invalid token (not a valid module name string) * added NULL pointer checks in typ.c to prevent SET_ERROR and referencing a non-existent typedef in a named type. This can happen sometimes if the YANG module has errors like a leaf that uses a type that does not exist -- Andy Bierman Fri, 18 May 2012 15:00:00 -0800 yuma (2.2-2) stable; urgency=medium * netconfd * enhanced unique-stmt checking to support embedded lists change val_unique_t to hold XPath struct instead of value back-ptr Note: value in error reporting for unique-error may not be correct if list with multiple instances has error. Will indicate the first node in the node-set with an error, which may not be the instance that caused a non-unique error within a nested list * fix bug in yuma-arp: SIL callbacks not getting loaded properly because revision date was wrong * fix bug in new instance_check code where false when-stmt may get ignored and falsely flag a missing mandatory node error * yangdump * fix bug where xpath is checked for an external augment even if context node is NULL because of some error in the external module, so target node not available * removed the list-in-unique-path error check * add auto-generated code for YANG features Conditional code allows features to be enabled at compile-time, boot-time, and/or module-load time Usage: 1) Compile-time The H file will contain a #define for _F_ The default is to enable features at compile-time. To disable, comment out this #define. All code related to the feature will be #ifdef removed from the image. 2) Boot-time If --feature-enable-default=true (d), then --feature-disable parameters should be added to turn features off. If --feature-enable-default=false, then --feature-enable parameters should be added to turn features on. 3) Module Load time During the module SIL init callback, the module features will be enabled or disabled according to the #define constancts in step 1. However these settings will not override any CLI/conf settings in step 2 (at this time) * fixed bug where --feature-enable-default=false would cause the server to shutdown if any modules with features were loaded * now allowing just feature name in --feature-enable and --feature-disable parameters instead of only module-name:feature-name * --format=uc or --format=uh now cause the notification send functions to be generated in the user SIL files, not the yuma SIL files. All code which may be edited by the user is now in the user SIL files if make_sil_dir --split is used * deprecated --feature-code-default parameter. This is ignored by yangdump. Same init sequence is always generated. * deprecated --feature-static and feature-dynamic parameters. These ares ignored by yangdump. See Usage section above for new YANG feature management procedure. * YANG modules * update latest NETMOD WG modules * add 2 new test modules used to test recent code additions * Documentation * update developer manual * update utility scripts and man pages -- Andy Bierman Fri, 09 Mar 2012 15:00:00 -0800 yuma (2.2-1) stable; urgency=high * netconfd * Added server regression testing and Coverity static code cleanup by Marc Pashley, James Parkin, and Joe Handford * fix bug where RPC SIL validate or invoke callback returns an error but does not call agt_record_error; server returns and ignores the error return status; now adding an if none, when RPC SIL validate or invoke callback returns an error. * fixed bug in load module where a module with errors could sometimes be loaded anyway. Now server will exit if initial modules loaded have errors, even if YANG parse returns NO_ERR for a module with a non-zero error count * fixed bug where unknown namespace error caused server to incorrectly skip the entire rest of the XML message. During load_running_config it is possible the server is configured to remove bad nodes and continue to the next XML sibling node. * implemented recoverable edits in agt_val.c; * add transactions to cfg.c; now saving an auto-incrementing transaction ID across reboots so new ID always used any time a config edit request is processed; * val_merge is now always non-destructive to the source value * newval and curval are always rooted in a source XML tree * add VAL_FL_DELETED to mark curval as deleted and not remove until commit finalized * update undo record handling so it is always used and supports recoverable edits * refactor edit code and move some ncx code to new module agt/agt_cfg.c * agt_val_root_check rewrite: Commit tests (see RFC 6020, Sec. 8.3.3) are separated out from agt_val_instance_check), instead of searching the target config for nodes that need commit tests. Started undo_rec based test pruning. * update commit procedure to use VAL_FL_SUBTREE_DIRTY flag to prune unchanged subtrees in the candidate config instead of expensive subtree-compare. * now cleaning all edit records from candidate so commit will not get fooled by delete x, then create x * remove agt_val_split_root_check code * callback states AGT_CB_COMMIT_CHECK and AGT_CB_TEST_APPLY have been removed and the agt_val code simplified * val_clone removed; changed applied to real data tree and undone if needed; no special test phase, just recoverable apply phase * changed user SID to 0 (for superuser) when a commit is rolled back; the old user id should not be used; must force all edits to be reverted. * now only restoring backup from disk if rollback failed, not if commit failed * add reverse_edit to send SIL callbacks for a reverse edit during a rollback; needed when the SIL already returned NO_ERR for a COMMIT callback * optimized 'applyhere' compares to test just child nodes and not all descendent nodes to speed up agt_val processing * optimize unique-stmt checking to minimize data retrieval and test duplication * add code to prune commit tests for objects do not need new tests because they have not changed value; * optimized instance_check so if-feature and when-stmts do not need to be evaluated again (done pre-root-check) * fixed bug in delete_config_validate where error path and error info parms are reversed; set errval for if needed * fix object ID for XPath so choices and cases are removed from the path extression; also add module prefix to prevent external augment with same local-name from matching expression * fix bug where deleting a default leaf did not re-mark the leaf as set-by-default * fix bug in commit code where newval was not checked for NULL before accessing a field in it * change output buffer logging from debug4 to debug3 * updated agt_acm debug logging * remove all #ifdefs around log_debug code * added agt_log_acm_reads and agt_log_acm_writes to the agt_profile to control log output for NACM access * fixed bug in error-path generation where /nc:config node was incorrectly added as the starting node, instead of the top-level YANG object from the database * fixed bug where error-path is not getting set if the error node is the parameter * fixed bug where error-path = '/' was not generated correctly so that field would be missing for and other rpc-error responses * fix error-path generation so it conforms to RFC 6241 * fix memory leak in generating unique-error * updated error message for list within the path of a unique statement component; clarified with YANG author that lists not allowed since nodes from different lists cannot be in the same unique test tuple * added a element to the for a missing value instance error (310), containing the name of the missing node. * fix bug where no namespace ID is set for an where the 'select' attribute in the parameter is invalid; set to NETCONF instead of 0 * fixed bug in XML generation where XML-safe string was not generated in string node content * fixed bug in copy-config where copy from inline to candidate was not getting fully validated or applied correctly * fix bug in val_set_canonical_order where list sometimes not inserted in sorted order * fixed bug in load_config where invoke could be called even if validate phase failed * add ncx:user-write extension see extension 'user-write' in yuma-ncx.yang for details Server will block user access to specific edit operations if this extension is present in a YANG database node definition * add /system/sysNetconfServerCLI monitoring data to inspect the CLI parameters used at boot-time * add boolean flags to agt_profile to track load-config error progress so startup-error and running-error parameters can be processed correctly: - agt_load_validate_errors (OK if --startup-error=continue) - agt_load_rootcheck_errors (OK if --running-error=continue) - agt_load_apply_errors (fatal error if SIL apply/commit fails) * fixed bug 3476123; leafrefs not getting written to XML correctly * fixed bug where inherited when-stmt and if-feature statements (from choice or case nodes) were not checked when deleting dead nodes * fixed bug in check_editop where create on duplicate leaf-list was not properly rejected with a data-exists error * added support to make sure modules with top-level mandatory nodes are rejected by the server if the --running-error parameter is set to 'stop'. This prevents a user from loading such a module and causing the server to shutdown. * added agt_validate_all (d:T) to agt_profile to control op behavior. Set to false to have only call SILs for the nodes that are changed in the candidate, which is how validate works. * fix bug in NACM where read or write access was wrongly denied when read-default=deny and write-default=deny * fixed bug where the server would terminate the op if parse or rpc-instance-check errors occurred, even though --startup-error=continue and the nodes with errors were optional so they could be removed without making the running config invalid * yangcli: * fixed bug in filling database content for a OBJ_TYP_CASE when the 1 and only case member was a complex type * fix bug deleting containers or lists where mandatory child nodes were incorrectly filled in, instead of skipped * enhance CLI parsing so container can be something other than a choice of empty leafs. e.g: validate source=@myconfig.xml myconfig.xml: ... This does not work if a 'source' parameter is given by selecting a case number when filling in a choice. * fix bug where user is prompted for a case number even if there is only 1 enabled case in the choice * fix bug where user is prompted if flag should be set y/n when editing a leaf of type 'empty'; leaf value already implicitly entered by selecting this leaf as the edit target * fix bug in val_set_canonical_order where list sometimes not inserted in sorted order * fixed bug 3476123; leafrefs not getting written to XML correctly * added XPath tab completion provided by Zesi Cai (thanks!) When a '/' is entered instead of the start of a command, the tab key will show all top-level objects available. After each '/', the tab shows the next level of child nodes. Does not support index (predicate) insertion. * yangdump: * fix segfault bug where stale backptr was accessed that contained heap garbage * fixed memory leak where submodule with errors was not freed correctly after it was processed * added '--full' parameter to yangdump --identifiers to show identifiers with module name of each node expanded * YANG Parse: * fixed bug where conditional descendant nodes specified in a unique-stmt are treated as an error. This is only an error is a key-stmt test fails; For unique-stmt, missing nodes in a unique test tuple cause the test to be skipped * fix bug where nested leafref types within unions were not always getting checked in final resolve steps * fix off-by-1 bug in object-id generation when module names are added * Remove memory leak from consume_revision * fix bug where default value for union data type sometimes incorrectly flagged as invalid value; can cause segfault * fix bug where checking if a parameter is set to its default for an identityref, bits, or leafref always returned FALSE * fixed bug where XPath context node for when-stmt in an augment-stmt was not set correctly * fixed bug where errors in consume_body_stmts were not always rippled all the way back to ncxmod.c, causing mod->res == NO_ERR but mod->errors > 0 * Build * Fixed makefile.sil so that yuma symbols are always checked first before standard library module names Also removed extra libraries libagt and libncx from the SIL link command. This is not needed and will cause an error if these libraries are not found. * Fixed bug in several Makefiles where libm is not explicitly declared in the link command. This causes STATIC=1 builds to fail on Ubuntu 11.10 (symbol 'round' not found from libm) * Cleaned up static build of yangcli -- Andy Bierman Fri, 27 Jan 2012 19:55:00 -0800 yuma (2.1-2) unstable; urgency=medium * Build * fix bug added recently that breaks build in libtoaster in a plain build (YUMA_HOME not set) and breaks CYGWIN build as well -- Andy Bierman Sun, 27 Sep 2011 19:00:00 -0700 yuma (2.1-1) unstable; urgency=medium * netconfd * add --runpath to netconfd.yang * fix bug reported by Sara Dickinson where leafref was not getting validated during commit; turned out leafrefs and instance-identifiers were not getting validated for target=running or during commit * fix bug where parameters were not handled correctly and was returned without validating the target config. * add --factory-startup CLI parameter. Currently there is no way to rewrite the invisible startup-cfg.xml if --with-startup=false, except by modifying the factory settings and saving the config. This parameter forces the startup config and the running config to contain the factory default settings during initialization. * fixed bug (reported by Sara Dickinson) where must-stmt tests for sibling nodes were getting skipped as soon as 1 must-test failed. This could result in nodes that should be invalid left in the running config during load_running_config if --startup-error=continue (the default) * fix bug in agt_proc.c where CPU cores were not causing a new val entry to be created. Introduced with vi-cov commit * add yuma-arp module, implemented by Igor Smolyar * fixed bug 3404233 The client and server both incorrectly accepted an XML node for a YANG choice or case node. These nodes do not exist in a YANG data tree, just in the YANG object tree * fix bug in COMMIT phase where SIL callback functions for nested nodes were not getting invoked for create and merge edit operations. * fixed bug introduced in last release where SIL validation callbacks are being called multiple times * fixed bug where commit callback for editop=OP_EDITOP_DELETE the curnode is a detached node -- the parent node is NULL. * fixed bug 3395740 * source tree specified by YUMA_HOME environment variable no longer required to be present * added libagt.so to shared library install location (d: /usr/lib) * using shared libraries in default location for libagt and libncx for ubuntu and RPM packaging. Not static libraries anymore! * building and installing libncx.a and libagt.a if STATIC=1 present in make cmd * updated SIL makefile so YUMA_HOME is not used unless FORCE_YUMA_HOME=1 is present in the make cmd; /usr/lib/yuma is used by default * make /arp node present by default, using refactored code from agt_acm.c * yangcli * add JSON output support for --display-mode=json and saving data with @foo.json * fix bug where manager session control block not checked for NULL * add external parameter support for RPC commands yangcli> some-command @filespec.xml filespec.xml (in YUMA_DATAPATH) == RPC element 10 fred * fixed bug where an edit command (e.g., create) on a choice or case node would generate an XML node for the choice or case. Now being removed from the XML payload before an is sent * yangdump: * added support for split SIL files or combined (old way): * --format=yc : Yuma SIL C file * --format=yh : Yuma SIL H file * --format=uc : User SIL C file * --format=uc : User SIL H file * --format=c : Combined Yuma/User SIL C file * --format=h : Combined Yuma/User SIL H file * added support for code generation for automatic retrieval of ancestor-or-self key values in user SIL callbacks * Remove #ifdef around #include directives for generated files. * fixed bug 3404234 --format=c output (SIL code) was not handling nested config=false containers (lists and leaf-lists still not handled!). Now nested config=false containers will automatically be created * fixed bug for --format=c where edit callbacks were being generated for OBJ_TYP_CHOICE and OBJ_TYP_CASE nodes. Since these nodes never exist in a database, these callbacks get registered but never invoked * fixed bug where SIL code generated for boolean, union, and identityref datatypes is incorrect -- the data type will be whatever the union was parsed will be treated as a string; causes segfault; !!! not fixed in v1 * union now passed to User SIL function as val_value_t instead of string * affected edit callback functions and notification send functions * added __cplusplus 'extern C' wrappers to H file generation for --format=h|uh|yh * Build: * added --split parameter to make_sil_dir * make_sil_dir --split foo : makes files in foo/src/ * y_foo.c : Yuma SIL C file * y_foo.h : Yuma SIL H file * u_foo.c : User SIL C file * u_foo.h : User SIL H file * building and installing libagt as a dynamic library * building Ubuntu package will mostly dynamic libraries instead of STATIC=1 and FULL_STATIC=1 * bumped version to 2.1 * updated Makefiles to allow debian debuild of 3 packages when DEBIAN=1 set: no flag: build yuma package DEVELOPER=1: build yuma-dev package DOC=1: build yuma-doc package * add HTML versions of manuals to install process * move PDFs from /usr/share/doc/yuma to /usr/share/doc/yuma/pdf * updated user manuals * YANG parse: * fix error message for leafref-stmt * fix bug 3404231 * incorrect handling of object type when checking leaf/leaf-list leafref loops, which could cause the wrong struct in a union to be used * fixed bug 3404239 All YANG data-def constructs were being checked for config, default, mandatory, min/max-elements before refine-stmts were applied. Moved all relevant tests from 'resolve' phase to 'resolve_final' phase. * Added modules/test/fail/t13.yang test case for compiler to check mandatory+default combo after refine applied. * fixed bug: anyxml objects were not getting checked during resolve_final phase (mandatory-stmt warnings) * suppress error messages when invalid XPath detected during validation of a union datatype. Unions are only required to be valid for 1 of N union types, not any particular type. -- Andy Bierman Sun, 25 Sep 2011 11:50:00 -0700 yuma (2.0-2) unstable; urgency=medium * yangcli * fixed bug where --batchmode is ignored if --run-command is also used * added support to connect to tailf confd servers over TCP; added --transport=ssh|tcp parameter to connect command and CLI parameter for startup connecting via TCP * Fix potential double calls to free and memory leaks resulting from calls to set_str(). In some paths the function set_str() * fixed bugs in autoload procedure * netconfd * fixed 2 framing bugs in base:1.1 mode * rewrote buffer code to pack incoming message buffers instead of using client buffer size as-is * fixed memory leak in new support code for malformed-message only occurred when malformed-message error generated * Improve logging for debug purposes from netconf-subsys.c (by Mark Pashley) * Many bugfixes and dead code removal detected by Coverity static analysis (from vi-cov branch by Mark Pashley) * Removed potential memory leak in cache_data_rules in NACM * Summary of bugfixes to copy_config_validate(): Coverity reported the following issues: DEAD CODE Code with no effect. Use after free Null pointer dereference Resource Leaks * sprintf changed to snprintf and strcpy changed to strncpy in some cases, to make sure no buffer overrun can occur * add module yuma-time-filter.yang * add last-modified XML attribute to for and replies * add if-modified-since parameter to and protocol operations * make logging from netconf-subsystem configurable via command line options * updated netconfd user manual * yangdump * fix bug in format=html or format=yang where pattern may not get generated in the output * add support for path links in leafrefs in --format=html * YANG parse: * fixed bug where val_clone of enum sometimes had static enu.name pointing at old.enu.dname so if old was freed, new.enu.name would point at garbage in the heap * fixed some memory leaks in error corner-cases * fixed bug where valid patterns parsed as non-strings were not correctly processed and no compiled pattern was created * fixed bug where unquoted prefixed string (foo:bar) would not be saved correctly in the compiled pattern (bar) * XML parse: * add tracefile support to debug input fed to XML textReader * CLI: * Change the signature of all instances of main to meet the 'c' standard. -- Andy Bierman Sun, 21 Aug 2011 12:02:00 -0700 yuma (2.0-1) unstable; urgency=low * initial 2.0 release; contains all yuma 1.15 features, plus major features * NETCONF base:1.1 support (RFC 6241 and RFC 6242) * with-defaults 'report-all-tagged' mode (RFC 6243) * --urltarget path selection mechanism (UrlPath) -- Andy Bierman Wed, 20 Jul 2011 19:00:00 -0700 yuma123_2.14/example-modules/0000775000175000017500000000000014770023131016224 5ustar vladimirvladimiryuma123_2.14/example-modules/yuma-arp/0000775000175000017500000000000014770023131017757 5ustar vladimirvladimiryuma123_2.14/example-modules/yuma-arp/yuma-arp.c0000664000175000017500000011404714770023131021665 0ustar vladimirvladimir /* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * * Instrumentation Written by Igor Smolyar *** Generated by yangdump 1.15.1351 module yuma-arp revision 2011-08-25 namespace http://netconfcentral.org/ns/yuma-arp organization Netconf Central */ //#include #if !defined(CYGWIN) && !defined(MACOSX) #define BUILD_ARP 1 #endif #include #ifdef BUILD_ARP #include #include #include #include #include #include #include #endif #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_yuma_arp.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #ifdef BUILD_ARP /* module static variables */ static ncx_module_t *yuma_arp_mod; static obj_template_t *arp_obj; static val_value_t *arp_val; /* put your static variables here */ static int counter; static int proc_net_arp_ok; static void write_file(const char *name, uint32 value) { FILE *fp; if ((fp = fopen(name, "w")) == NULL) { log_debug("\nCould not open the file: %s\n", name); return; } fprintf(fp, "%u", value); fclose(fp); return; } static int add_leaf (val_value_t * parent, const xmlChar * valName, xmlChar * valValue, status_t *res) { val_value_t * newVal; obj_template_t * newObj; newObj = obj_find_child(parent->obj, y_yuma_arp_M_yuma_arp, valName); if (newObj == NULL) { *res = SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return -1; } newVal = val_make_simval_obj(newObj, valValue, res); if (newVal == NULL) { return -1; } val_add_child(newVal, parent); return 0; } /******************************************************************** * FUNCTION make_arp_entry * * Make the starter arp entry for the specified name * * INPUTS: * entryobj == object template to use * nameptr == name string, zero-terminated * res == address of return status * * OUTPUTS: * *res == return status * * RETURNS: * pointer to the new entry or NULL if malloc failed *********************************************************************/ static val_value_t * make_arp_entry (val_value_t *dynamic_arp_val, xmlChar *ipptr, xmlChar *macptr, status_t *res) { obj_template_t *entryobj; obj_template_t *dynamic_arp_obj; val_value_t *entryval; *res = NO_ERR; if((ipptr == NULL) || (macptr == NULL)) { log_debug("\n IP or MAC can not be NULL!"); *res = SET_ERROR(ERR_NCX_INVALID_VALUE); return NULL; } log_debug2("\nMake dynamic ARP entry ip:%s mac:%s", ipptr, macptr); dynamic_arp_obj = dynamic_arp_val->obj; entryobj = obj_find_child(dynamic_arp_obj, y_yuma_arp_M_yuma_arp, y_yuma_arp_N_dynamic_arp); if (entryobj == NULL) { *res = SET_ERROR(ERR_NCX_DEF_NOT_FOUND); return NULL; } entryval = val_new_value(); if (entryval == NULL) { *res = ERR_INTERNAL_MEM; return NULL; } val_init_from_template(entryval, entryobj); /* pass off entryval memory here */ val_add_child(entryval, dynamic_arp_val); if (add_leaf (entryval, y_yuma_arp_N_ip_address, ipptr, res) == -1) { return NULL; } *res = val_gen_index_chain(entryval->obj, entryval); if (*res != NO_ERR) { return NULL; } if (add_leaf (entryval, y_yuma_arp_N_mac_address, macptr, res) == -1) { return NULL; } return entryval; } /* make_arp_entry */ /******************************************************************** * FUNCTION parse_buffer * * Parse single line from /proc/net/arp and fetch mac and ip addresses * * INPUTS: * currChar = beginning of line to parse * ip_address = buffer to fill with ip address from line * mac_address = buffer to fill with mac address from line * * RETURNS: * error status ********************************************************************/ static status_t parse_buffer( xmlChar *currChar, xmlChar *ip_address, xmlChar *mac_address) { status_t res; xmlChar *startIP, *endIP, *startMAC, *endMAC, *startFlag; int i; if((ip_address == NULL) || (mac_address == NULL)) { log_debug("\n IP or MAC can not be NULL!"); return ERR_NCX_INVALID_VALUE; } res = NO_ERR; /* skip white spaces */ while (*currChar && xml_isspace(*currChar)) { currChar++; } if (*currChar == '\0') { /* not expecting a line with just whitespace on it */ return ERR_NCX_SKIPPED; } else { startIP = currChar; } /* get the end of the interface name */ while (*currChar && !xml_isspace(*currChar)) { currChar++; if (*currChar == '\0') { /* not expecting a line with just foo on it */ return ERR_NCX_SKIPPED; } } endIP = currChar++; /* skipping three parts of line, in following order * 1 - spaces and HW type * 2 - spaces and Flags * 3 - spaces before MAC */ for (i = 0; i < 3; i++) { while (*currChar && xml_isspace(*currChar)) { currChar++; } if (*currChar == '\0') { return ERR_NCX_SKIPPED; } if (i == 2) { /* MAC is found */ break; } startFlag = currChar; /* skipping non-spaces */ while (*currChar && !xml_isspace(*currChar)) { currChar++; if (*currChar == '\0') { return ERR_NCX_SKIPPED; } } if (i == 1) { /* FLag is found - flag defines type of arp entry */ /* We interested only in dynamic entries */ if (xml_strncmp((const xmlChar *) startFlag, MAC_DYNAMIC, currChar - startFlag) != 0) { /* if arp entry is static - no point to parse further */ return ERR_NCX_SKIPPED; } } } startMAC = currChar; /* find the end of MAC str */ while (*currChar && !xml_isspace(*currChar)) { currChar++; if (*currChar == '\0') { return ERR_NCX_SKIPPED; } } endMAC = currChar; if( ip_address && ((endIP - startIP) < ADDRESS_SIZE)) { xml_strncpy (ip_address, startIP, endIP - startIP); ip_address [endIP - startIP] = '\0'; } else { log_debug("\nLine parsing problem - ip address is to large\n"); } if( mac_address && ((endMAC - startMAC) < ADDRESS_SIZE)) { xml_strncpy (mac_address, startMAC, endMAC - startMAC); mac_address [endMAC - startMAC] = '\0'; } else { log_debug("\nLine parsing problem - mac address is too large\n"); } return res; } /* end of parse_buffer */ /******************************************************************** * FUNCTION modify_arp * * Modifies static arp entries * * INPUTS: * ipval = ip-address data node * macval = mac-address data node * action = action to perform * res = store result of the action * ********************************************************************/ static void modify_arp ( val_value_t *ipval, val_value_t *macval, int action, status_t *res) { struct arpreq req ; struct sockaddr_in sa_in; int sockfd = 0, i, tmp[HW_OCTETS]; memset((char *) &req, 0, sizeof(req)); memset(&sa_in, 0, sizeof(sa_in)); if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { log_debug("\nFailed to open a socket for ioctl.\n"); *res = SET_ERROR(ERR_NCX_OPERATION_FAILED); return; } req.arp_pa.sa_family = AF_INET ; sa_in.sin_family = AF_INET; /* set ip */ sa_in.sin_port = 0; if(inet_pton(AF_INET, (const char *)(VAL_STR(ipval)) , &sa_in.sin_addr) != 1) { log_debug("\nINET_PTON error: %s\n", strerror(errno)); *res = SET_ERROR(ERR_NCX_OPERATION_FAILED); return; } memcpy(&req.arp_pa , &sa_in, sizeof(sa_in)); /* set hw address */ if(sscanf((const char *)VAL_STR(macval), "%2x:%2x:%2x:%2x:%2x:%2x", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]) != HW_OCTETS) { log_debug("\nWrong HW mac in data node: mac is %s\n", VAL_STR(macval)); *res = SET_ERROR(ERR_NCX_OPERATION_FAILED); return; } for(i=0; imhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_yuma_arp_arp_arp_settings_maximum_entries_edit */ /******************************************************************** * FUNCTION y_yuma_arp_arp_arp_settings_validity_timeout_edit * * Edit database object callback * Path: /arp/arp-settings/validity-timeout * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_yuma_arp_arp_arp_settings_validity_timeout_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; if (LOGDEBUG) { log_debug("\nEnter y_yuma_arp_arp_arp_settings_validity_timeout_edit " "callback for %s phase", agt_cbtype_name(cbtyp)); } /* remove the next line if newval is used */ (void)newval; /* remove the next line if curval is used */ (void)curval; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ if(editop != OP_EDITOP_DELETE) { DIR *dip; struct dirent *dit; char tmp[255]; if ((dip = opendir(NEIGH_DIR)) == NULL) { log_debug("\nCan not open dir %s\n", NEIGH_DIR); return ERR_NCX_OPERATION_FAILED; } /* now for each interface update stale time */ while ((dit = readdir(dip)) != NULL) { /* skip "." and ".." entries */ if ((strncmp(dit->d_name, ".", strlen(dit->d_name)) == 0) || strncmp(dit->d_name, "..", strlen(dit->d_name)) == 0) { continue; } snprintf(tmp, 255, "/proc/sys/net/ipv4/neigh/%s/gc_stale_time", dit->d_name); write_file(tmp, VAL_UINT(newval)); } closedir(dip); } switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_yuma_arp_arp_arp_settings_validity_timeout_edit */ /******************************************************************** * FUNCTION y_yuma_arp_arp_arp_settings_edit * * Edit database object callback * Path: /arp/arp-settings * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_yuma_arp_arp_arp_settings_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; if (LOGDEBUG) { log_debug("\nEnter y_yuma_arp_arp_arp_settings_edit callback " "for %s phase", agt_cbtype_name(cbtyp)); } /* remove the next line if newval is used */ (void)newval; /* remove the next line if curval is used */ (void)curval; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_yuma_arp_arp_arp_settings_edit */ /******************************************************************** * FUNCTION y_yuma_arp_arp_static_arps_static_arp_ip_address_edit * * Edit database object callback * Path: /arp/static-arps/static-arp/ip-address * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_yuma_arp_arp_static_arps_static_arp_ip_address_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; if (LOGDEBUG) { log_debug( "\nEnter y_yuma_arp_arp_static_arps_static_arp_ip_address_edit " "callback for %s phase", agt_cbtype_name(cbtyp)); } /* remove the next line if newval is used */ (void)newval; /* remove the next line if curval is used */ (void)curval; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_yuma_arp_arp_static_arps_static_arp_ip_address_edit */ /******************************************************************** * FUNCTION y_yuma_arp_arp_static_arps_static_arp_mac_address_edit * * Edit database object callback * Path: /arp/static-arps/static-arp/mac-address * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_yuma_arp_arp_static_arps_static_arp_mac_address_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; if (LOGDEBUG) { log_debug( "\nEnter y_yuma_arp_arp_static_arps_static_arp_mac_address_edit " "callback for %s phase", agt_cbtype_name(cbtyp)); } /* remove the next line if newval is used */ (void)newval; /* remove the next line if curval is used */ (void)curval; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_yuma_arp_arp_static_arps_static_arp_mac_address_edit */ /******************************************************************** * FUNCTION y_yuma_arp_arp_static_arps_static_arp_edit * * Edit database object callback * Path: /arp/static-arps/static-arp * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_yuma_arp_arp_static_arps_static_arp_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval, *ipval, *macval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; if (LOGDEBUG) { log_debug( "\nEnter y_yuma_arp_arp_static_arps_static_arp_edit callback " "for %s phase", agt_cbtype_name(cbtyp)); } /* remove the next line if newval is used */ /*(void)newval; */ /* remove the next line if curval is used */ /* (void)curval; */ switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ if(editop != OP_EDITOP_DELETE) { ipval = val_find_child(newval, y_yuma_arp_M_yuma_arp, y_yuma_arp_N_ip_address); macval = val_find_child(newval, y_yuma_arp_M_yuma_arp, y_yuma_arp_N_mac_address); if(ipval && macval) { modify_arp(ipval, macval, ARP_ADD, &res); } } switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: ipval = val_find_child(curval, y_yuma_arp_M_yuma_arp, y_yuma_arp_N_ip_address); macval = val_find_child(curval, y_yuma_arp_M_yuma_arp, y_yuma_arp_N_mac_address); if(ipval && macval) { modify_arp(ipval, macval, ARP_DEL, &res); } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_yuma_arp_arp_static_arps_static_arp_edit */ /******************************************************************** * FUNCTION y_yuma_arp_arp_static_arps_edit * * Edit database object callback * Path: /arp/static-arps * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_yuma_arp_arp_static_arps_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; if (LOGDEBUG) { log_debug( "\nEnter y_yuma_arp_arp_static_arps_edit callback for %s phase", agt_cbtype_name(cbtyp)); } /* remove the next line if newval is used */ (void)newval; /* remove the next line if curval is used */ (void)curval; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_yuma_arp_arp_static_arps_edit */ /******************************************************************** * FUNCTION y_yuma_arp_arp_dynamic_arps_get * * Get database object callback * Path: /yuma-arp/dynamic-arps * Fill in 'dstval' contents * TBD: automatic get-callback registration * FOR NOW: use agt_make_virtual_leaf to * register this get callback fn * * INPUTS: * see ncx/getcb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_yuma_arp_arp_dynamic_arps_get ( ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { status_t res; FILE * file; xmlChar * buffer, *currChar, *ip_address, *mac_address; int linecount; boolean done; res = NO_ERR; buffer = NULL; counter++; if (LOGDEBUG) { log_debug("\nEnter y_yuma_arp_arp_dynamic_arps_get callback"); } /* remove the next line if scb is used */ (void)scb; /* remove the next line if virval is used */ (void)virval; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } /* open the /proc/net/arp file for reading */ file = fopen("/proc/net/arp", "r"); if (file == NULL) { return errno_to_status(); } /* get a file read line buffer */ buffer = m__getMem(NCX_MAX_LINELEN); if (buffer == NULL) { fclose(file); return ERR_INTERNAL_MEM; } ip_address = m__getMem(ADDRESS_SIZE); if (ip_address == NULL) { fclose(file); m__free(buffer); return ERR_INTERNAL_MEM; } mac_address = m__getMem(ADDRESS_SIZE); if (mac_address == NULL) { fclose(file); m__free(buffer); m__free(ip_address); return ERR_INTERNAL_MEM; } done = FALSE; linecount = 0; /* loop through the file until done */ while (!done) { currChar = (xmlChar *) fgets((char *)buffer, NCX_MAX_LINELEN, file); if (currChar == NULL) { done = TRUE; continue; } else { linecount++; } if (linecount < 2) { /* skip first line */ continue; } /* get IP and MAC from the line */ res = parse_buffer(currChar, ip_address, mac_address); /* inserting the new entry values to the list */ if (res == NO_ERR) { (void)make_arp_entry(dstval, ip_address, mac_address, &res); } } m__free(mac_address); m__free(ip_address); m__free(buffer); fclose(file); return NO_ERR; } /* y_yuma_arp_arp_dynamic_arps_get */ /******************************************************************** * FUNCTION y_yuma_arp_arp_mro * * Make read-only child nodes * Path: /arp * * INPUTS: * parentval == the parent struct to use for new child nodes * * RETURNS: * error status ********************************************************************/ static status_t y_yuma_arp_arp_mro (val_value_t *parentval) { status_t res; val_value_t *dynamic_arp_val; obj_template_t * dynamic_arp_obj; res = NO_ERR; /* container arp not handled!!! */ dynamic_arp_obj = obj_find_template(obj_get_datadefQ(parentval->obj), y_yuma_arp_M_yuma_arp, y_yuma_arp_N_dynamic_arps); if (!dynamic_arp_obj) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } dynamic_arp_val = val_new_value(); if (!dynamic_arp_val) { return ERR_INTERNAL_MEM; } val_init_virtual(dynamic_arp_val, y_yuma_arp_arp_dynamic_arps_get, dynamic_arp_obj); val_add_child(dynamic_arp_val, parentval); return res; } /* y_yuma_arp_arp_mro */ /******************************************************************** * FUNCTION y_yuma_arp_arp_edit * * Edit database object callback * Path: /arp * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_yuma_arp_arp_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; if (LOGDEBUG) { log_debug("\nEnter y_yuma_arp_arp_edit callback for %s phase", agt_cbtype_name(cbtyp)); } switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res == NO_ERR) { res = agt_check_cache( &arp_val, newval, curval, editop); } if (res == NO_ERR && curval == NULL) { res = y_yuma_arp_arp_mro(newval); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_yuma_arp_arp_edit */ /******************************************************************** * FUNCTION y_yuma_arp_init_static_vars * * initialize module static variables * ********************************************************************/ static void y_yuma_arp_init_static_vars (void) { yuma_arp_mod = NULL; arp_obj = NULL; arp_val = NULL; /* init your static variables here */ proc_net_arp_ok = 0; } /* y_yuma_arp_init_static_vars */ #endif /* END ifdef BUILD_ARP */ /******************************************************************** * FUNCTION y_yuma_arp_init * * initialize the yuma-arp server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ status_t y_yuma_arp_init ( const xmlChar *modname, const xmlChar *revision) { #ifdef BUILD_ARP agt_profile_t *agt_profile; status_t res; FILE *testfile; y_yuma_arp_init_static_vars(); /* change if custom handling done */ if (xml_strcmp(modname, y_yuma_arp_M_yuma_arp)) { return ERR_NCX_UNKNOWN_MODULE; } if (revision && xml_strcmp(revision, y_yuma_arp_R_yuma_arp)) { return ERR_NCX_WRONG_VERSION; } agt_profile = agt_get_profile(); /* open the /proc/net/arp file for reading */ testfile = fopen("/proc/net/arp", "r"); if (testfile == NULL) { log_info("\nSkipping yuma-arp module: Cannot open /proc/net/arp file"); return NO_ERR; } else { fclose(testfile); proc_net_arp_ok = 1; } res = ncxmod_load_module( y_yuma_arp_M_yuma_arp, y_yuma_arp_R_yuma_arp, &agt_profile->agt_savedevQ, &yuma_arp_mod); if (res != NO_ERR) { return res; } arp_obj = ncx_find_object( yuma_arp_mod, y_yuma_arp_N_arp); if (yuma_arp_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } res = agt_cb_register_callback( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp", y_yuma_arp_R_yuma_arp, y_yuma_arp_arp_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/arp-settings", y_yuma_arp_R_yuma_arp, y_yuma_arp_arp_arp_settings_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/arp-settings/maximum-entries", y_yuma_arp_R_yuma_arp, y_yuma_arp_arp_arp_settings_maximum_entries_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/arp-settings/validity-timeout", y_yuma_arp_R_yuma_arp, y_yuma_arp_arp_arp_settings_validity_timeout_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/static-arps", y_yuma_arp_R_yuma_arp, y_yuma_arp_arp_static_arps_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/static-arps/static-arp", y_yuma_arp_R_yuma_arp, y_yuma_arp_arp_static_arps_static_arp_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/static-arps/static-arp/ip-address", y_yuma_arp_R_yuma_arp, y_yuma_arp_arp_static_arps_static_arp_ip_address_edit); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/static-arps/static-arp/mac-address", y_yuma_arp_R_yuma_arp, y_yuma_arp_arp_static_arps_static_arp_mac_address_edit); if (res != NO_ERR) { return res; } /* put your module initialization code here */ return res; #else (void)modname; (void)revision; return NO_ERR; #endif } /* y_yuma_arp_init */ /******************************************************************** * FUNCTION y_yuma_arp_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ status_t y_yuma_arp_init2 (void) { #ifdef BUILD_ARP status_t res = NO_ERR; boolean added = FALSE; if (!proc_net_arp_ok) { return res; } arp_val = agt_add_top_node_if_missing(yuma_arp_mod, y_yuma_arp_N_arp, &added, &res); if (res != NO_ERR || arp_val == NULL) { return res; } if (added) { /* just the top node was created, instead of going through * the SIL edit callback, so make-read-only was not called */ res = y_yuma_arp_arp_mro(arp_val); } return res; #else return NO_ERR; #endif } /* y_yuma_arp_init2 */ /******************************************************************** * FUNCTION y_yuma_arp_cleanup * cleanup the server instrumentation library * ********************************************************************/ void y_yuma_arp_cleanup (void) { #ifdef BUILD_ARP if (!proc_net_arp_ok) { return; } agt_cb_unregister_callbacks( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp"); agt_cb_unregister_callbacks( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/arp-settings"); agt_cb_unregister_callbacks( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/arp-settings/maximum-entries"); agt_cb_unregister_callbacks( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/arp-settings/validity-timeout"); agt_cb_unregister_callbacks( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/static-arps"); agt_cb_unregister_callbacks( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/static-arps/static-arp"); agt_cb_unregister_callbacks( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/static-arps/static-arp/ip-address"); agt_cb_unregister_callbacks( y_yuma_arp_M_yuma_arp, (const xmlChar *)"/arp/static-arps/static-arp/mac-address"); /* put your cleanup code here */ #endif } /* y_yuma_arp_cleanup */ /* END yuma_arp.c */ yuma123_2.14/example-modules/yuma-arp/README0000664000175000017500000000130514770023131020636 0ustar vladimirvladimir==Standalone project for netconfd module implementing ietf-system.yang== Dependency: installed netconfd run-time binaries and development shared libraries and headers. ===Build and install=== autoreconf -i -f ./configure CFLAGS="-g -O0" CXXFLAGS="-g -O0" --prefix=/usr make sudo make install sudo netconfd --module=ietf-system ===Functionality=== yangcli vladimir@localhost> xget /arp rpc-reply { data { arp { dynamic-arps { dynamic-arp 31.133.141.143 { ip-address 31.133.141.143 mac-address 4c:66:41:10:9c:62 } dynamic-arp 31.133.128.1 { ip-address 31.133.128.1 mac-address 00:00:5e:00:01:80 } } } } } yuma123_2.14/example-modules/yuma-arp/yuma-arp.h0000664000175000017500000001001114770023131021654 0ustar vladimirvladimir #ifndef _H_agt_yuma_arp #define _H_agt_yuma_arp /* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * * Instrumentation Written by Igor Smolyar *** Generated by yangdump 1.15.1351 module yuma-arp revision 2011-08-25 namespace http://netconfcentral.org/ns/yuma-arp organization Netconf Central */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #ifdef __cplusplus extern "C" { #endif /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #define y_yuma_arp_M_yuma_arp (const xmlChar *)"yuma-arp" #define y_yuma_arp_R_yuma_arp (const xmlChar *)"2012-01-13" #define y_yuma_arp_N_arp (const xmlChar *)"arp" #define y_yuma_arp_N_arp_settings (const xmlChar *)"arp-settings" #define y_yuma_arp_N_dynamic_arp (const xmlChar *)"dynamic-arp" #define y_yuma_arp_N_dynamic_arps (const xmlChar *)"dynamic-arps" #define y_yuma_arp_N_ip_address (const xmlChar *)"ip-address" #define y_yuma_arp_N_mac_address (const xmlChar *)"mac-address" #define y_yuma_arp_N_maximum_entries (const xmlChar *)"maximum-entries" #define y_yuma_arp_N_static_arp (const xmlChar *)"static-arp" #define y_yuma_arp_N_static_arps (const xmlChar *)"static-arps" #define y_yuma_arp_N_validity_timeout (const xmlChar *)"validity-timeout" #define MAC_DYNAMIC (const xmlChar *)"0x2" #define ADDRESS_SIZE 32 #define HW_OCTETS 6 #define ARP_MAX_ENTRIES "cache_size" #define ARP_TIMEOUT "validity_timeout" #define TRESH1 "/proc/sys/net/ipv4/neigh/default/gc_thresh1" #define TRESH2 "/proc/sys/net/ipv4/neigh/default/gc_thresh2" #define TRESH3 "/proc/sys/net/ipv4/neigh/default/gc_thresh3" #define NEIGH_DIR "/proc/sys/net/ipv4/neigh/" enum { ARP_ADD, ARP_DEL }; /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /* container /arp/arp-settings */ typedef struct y_yuma_arp_T_arp_arp_settings_ { uint32 maximum_entries; uint32 validity_timeout; } y_yuma_arp_T_arp_arp_settings; /* list /arp/static-arps/static-arp */ typedef struct y_yuma_arp_T_arp_static_arps_static_arp_ { dlq_hdr_t qhdr; xmlChar *ip_address; xmlChar *mac_address; } y_yuma_arp_T_arp_static_arps_static_arp; /* container /arp/static-arps */ typedef struct y_yuma_arp_T_arp_static_arps_ { dlq_hdr_t static_arp; } y_yuma_arp_T_arp_static_arps; /* list /arp/dynamic-arps/dynamic-arp */ typedef struct y_yuma_arp_T_arp_dynamic_arps_dynamic_arp_ { dlq_hdr_t qhdr; xmlChar *ip_address; xmlChar *mac_address; } y_yuma_arp_T_arp_dynamic_arps_dynamic_arp; /* container /arp/dynamic-arps */ typedef struct y_yuma_arp_T_arp_dynamic_arps_ { dlq_hdr_t dynamic_arp; } y_yuma_arp_T_arp_dynamic_arps; /* container /arp */ typedef struct y_yuma_arp_T_arp_ { y_yuma_arp_T_arp_arp_settings arp_settings; y_yuma_arp_T_arp_static_arps static_arps; y_yuma_arp_T_arp_dynamic_arps dynamic_arps; } y_yuma_arp_T_arp; /******************************************************************** * * * F U N C T I O N S * * * *********************************************************************/ /* yuma-arp module init 1 */ extern status_t y_yuma_arp_init ( const xmlChar *modname, const xmlChar *revision); /* yuma-arp module init 2 */ extern status_t y_yuma_arp_init2 (void); /* yuma-arp module cleanup */ extern void y_yuma_arp_cleanup (void); #ifdef __cplusplus } /* end extern 'C' */ #endif #endif yuma123_2.14/example-modules/yuma-arp/yuma-arp.yang0000664000175000017500000000512414770023131022374 0ustar vladimirvladimirmodule yuma-arp { namespace "http://netconfcentral.org/ns/yuma-arp"; prefix "arp"; import yuma-ncx { prefix ncx; } import ietf-inet-types { prefix inet; } import ietf-yang-types { prefix yang; } organization "Netconf Central"; contact "Andy Bierman ."; description "This module contains a collection of YANG definitions for configuring and monitoring ARP."; revision 2012-01-13 { description "Add ncx:user-write restriction to prevent user deletion."; } revision 2011-08-25 { description "Initial revision; From yuma submission by Igor Smolyar and Zohar M."; } container arp { ncx:user-write update; description "System ARP settings."; /* leaf collection-enabled { type boolean; default true; description "Enable dynamic arp collection"; } */ grouping arp-entry { leaf ip-address { type inet:ipv4-address; description "The IPv4 address for the ARP table entry."; } leaf mac-address { type yang:mac-address; description "The MAC address for the ARP table entry."; } } container arp-settings { description "System ARP Table Settings."; leaf maximum-entries { type uint32 { range "1024 .. 16384"; } description "The maximum entries for the ARP table."; } leaf validity-timeout { type uint32 { range "60 .. 86400"; } units seconds; description "The validity timeout for the ARP table."; } } container static-arps { description "System static ARP table entries."; list static-arp { key "ip-address"; description "One static-ARP entry."; uses arp-entry; } } container dynamic-arps { config false; description "System dynamic ARP table entries."; list dynamic-arp { key "ip-address"; description "One dynamic-ARP entry."; uses arp-entry; } } } } yuma123_2.14/example-modules/yuma-arp/Makefile.am0000775000175000017500000000047214770023131022021 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libyuma-arp.la libyuma_arp_la_SOURCES = \ yuma-arp.c libyuma_arp_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libyuma_arp_la_LDFLAGS = -module -lyumaagt -lyumancx dist_netconfcentral_yang_DATA = yuma-arp.yang yuma123_2.14/example-modules/yuma-arp/configure.ac0000664000175000017500000000123414770023131022245 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-yuma-arp], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) netconfcentral_yangdir="$prefix/share/yuma/modules/netconfcentral" AC_SUBST(netconfcentral_yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/ietf-network-bridge/0000775000175000017500000000000014770023131022074 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-network-bridge/example-bridge.yang0000664000175000017500000000473014770023131025645 0ustar vladimirvladimirmodule example-bridge { yang-version 1.1; namespace "http://example.com/ns/example-bridge"; prefix example; import ietf-network-bridge { prefix netbr; } import ietf-network-bridge-scheduler { prefix sched; } organization "example.com"; description "Example of bridge."; revision 2018-07-17 { description "For the sake of portability commented out double bases for identities since this is not supported by ODL Berillium."; } identity video0 { base sched:traffic-class; } identity video1 { base sched:traffic-class; } identity signaling { base sched:traffic-class; } identity best-effort { base sched:traffic-class; } identity default-port { base sched:port-class; } //Strict priority aggregator with 3 classes: identity strict-priority-aggregator { base sched:aggregator; } identity pri0 { base sched:shared-queue-aggregator-input; //base strict-priority-aggregator; } identity pri1 { base sched:shared-queue-aggregator-input; //base strict-priority-aggregator; } identity pri2 { base sched:shared-queue-aggregator-input; //base strict-priority-aggregator; } //Cyclic timeslot schedule aggregator with 2 timeslots: identity cyclic-timeslot-schedule-aggregator { base sched:aggregator; } identity timeslot0 { base sched:shared-queue-aggregator-input; // base cyclic-timeslot-schedule-aggregator; } identity timeslot1 { base sched:shared-queue-aggregator-input; //base cyclic-timeslot-schedule-aggregator; } augment "/netbr:bridge/sched:scheduler-classes/sched:scheduler-class" + "/sched:gate-controllers/sched:gate-controller" { when "./sched:type = 'example:cyclic-timeslot-schedule-aggregator'"; leaf period { type uint32; units "nanoseconds"; } leaf time-slot0-interval { type uint32; units "nanoseconds"; } leaf time-slot1-interval { type uint32; units "nanoseconds"; } } //Rate limiter - filter: identity rate-limiter { base sched:filter; } identity in { base sched:filter-input; base rate-limiter; } augment "/netbr:bridge/sched:scheduler-classes/sched:scheduler-class" + "/sched:gate-controllers/sched:gate-controller" { when "./sched:type = 'example:rate-limiter'"; leaf interval { type uint32; units "nanoseconds"; } leaf limit { type uint32; units "octets"; } } } yuma123_2.14/example-modules/ietf-network-bridge/README0000664000175000017500000000310014770023131022746 0ustar vladimirvladimir==Standalone project for netconfd module implementing ietf-network-bridge*.yang== Dependency: installed netconfd run-time binaries and development shared libraries and headers and openvswitch-dev>=2-10. ===Build and install=== sudo apt-get install openvswitch-dev autoreconf -i -f ./configure CFLAGS="-g -O0" CXXFLAGS="-g -O0" --prefix=/usr make sudo make install Dry run: /usr/sbin/netconfd --module=/usr/share/yuma/modules/ietf/ietf-interfaces@2014-05-08.yang \ --module=/usr/share/yuma/modules/ietf/iana-if-type@2014-05-08.yang \ --module=/usr/share/yuma/modules/ietf-draft/ietf-network-bridge.yang \ --module=/usr/share/yuma/modules/ietf-draft/ietf-network-bridge-flows.yang \ --module=/usr/share/yuma/modules/ietf-draft/ietf-network-bridge-scheduler.yang \ --module=/usr/share/yuma/modules/examples/example-bridge.yang \ --startup=./example-bridge-cfg.xml --log-level=debug4 --superuser=${USER} with SIL implementation loded run: /usr/sbin/netconfd --module=ietf-network-bridge --startup=./example-bridge-cfg.xml --log-level=debug4 --superuser=${USER} ===Functionality=== yangcli root@localhost> delete /flows yangcli root@localhost> commit yangcli root@localhost> create /flows/flow[id='best-effort-to-host2'] -- \ match/ethernet-match/ethernet-destination\ /address=00:01:02:03:00:02 \ actions/action[order='0']/output-action/out-port=p2 yangcli root@localhost> merge /flows/flow[id='best-effort-to-host2'] -- \ traffic-class=best-effort yuma123_2.14/example-modules/ietf-network-bridge/debian/0000775000175000017500000000000014770023131023316 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-network-bridge/debian/source/0000775000175000017500000000000014770023131024616 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-network-bridge/debian/source/format0000664000175000017500000000001414770023131026024 0ustar vladimirvladimir3.0 (quilt) yuma123_2.14/example-modules/ietf-network-bridge/debian/control0000664000175000017500000000167714770023131024734 0ustar vladimirvladimirSource: yuma123-netconfd-module-ietf-network-bridge Section: net Priority: optional Maintainer: Vladimir Vassilev Build-Depends: debhelper (>= 10), autoconf, libncurses5-dev, libreadline-dev, libssh2-1-dev, libssl-dev, libtool, libxml2-dev, libyuma-dev (>= 2.14), openvswitch-dev, zlib1g-dev Standards-Version: 4.1.4 Homepage: http://yuma123.org Package: netconfd-module-ietf-network-bridge Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, debconf, lsb-base (>= 3.2-13), libyuma2 (>= 2.14), openvswitch-common Description: SIL module for netconfd implementing ietf-network-bridge*.yang The NETCONF protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yuma123 netconfd module ietf-network-bridge implements the functionality modeled in ietf-network-bridge*.yang using some common command line tools. yuma123_2.14/example-modules/ietf-network-bridge/debian/rules0000775000175000017500000000071514770023131024401 0ustar vladimirvladimir#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ --with autoreconf yuma123_2.14/example-modules/ietf-network-bridge/debian/copyright0000664000175000017500000000326514770023131025257 0ustar vladimirvladimirFiles: * Copyright: (C) 2016-2025 Vladimir Vassilev Copyright: (C) 2016-2018 Transpacket AS Copyright: (C) 2019-2025 Lightside Instruments AS License: BSD License: BSD Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andy Bierman BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. yuma123_2.14/example-modules/ietf-network-bridge/debian/compat0000664000175000017500000000000314770023131024515 0ustar vladimirvladimir10 yuma123_2.14/example-modules/ietf-network-bridge/debian/changelog0000664000175000017500000000030214770023131025163 0ustar vladimirvladimiryuma123-netconfd-module-ietf-network-bridge (2.14-0) stable; urgency=medium * Unofficial package -- Vladimir Vassilev Sun, 23 Mar 2025 15:25:36 +0100 yuma123_2.14/example-modules/ietf-network-bridge/ovs-ofctl-utils.h0000664000175000017500000001272314770023131025324 0ustar vladimirvladimir/* This file contains a copy of the port-iterator helper * functions part of ovs-ofctl tool part of the openvswitch * project with minor modifications. * They are used in the ietf-network-bridge-flows * module implementation for OpenFlow southbound interface based on * the openvswitch conntroller API. The original License text follows. */ /* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ struct port_iterator { struct vconn *vconn; enum { PI_FEATURES, PI_PORT_DESC } variant; struct ofpbuf *reply; ovs_be32 send_xid; bool more; }; static void port_iterator_fetch_port_desc(struct port_iterator *pi) { int retval; pi->variant = PI_PORT_DESC; pi->more = true; struct ofpbuf *rq = ofputil_encode_port_desc_stats_request( vconn_get_version(pi->vconn), OFPP_ANY); pi->send_xid = ((struct ofp_header *) rq->data)->xid; retval=vconn_send_block(pi->vconn, rq); assert(retval==0); } static void port_iterator_fetch_features(struct port_iterator *pi) { int retval; pi->variant = PI_FEATURES; /* Fetch the switch's ofp_switch_features. */ enum ofp_version version = vconn_get_version(pi->vconn); struct ofpbuf *rq = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, version, 0); retval=vconn_transact(pi->vconn, rq, &pi->reply); assert(retval==0); enum ofptype type; if (ofptype_decode(&type, pi->reply->data) || type != OFPTYPE_FEATURES_REPLY) { ovs_fatal(0, "%s: received bad features reply", vconn_get_name(pi->vconn)); } if (!ofputil_switch_features_has_ports(pi->reply)) { /* The switch features reply does not contain a complete list of ports. * Probably, there are more ports than will fit into a single 64 kB * OpenFlow message. Use OFPST_PORT_DESC to get a complete list of * ports. */ ofpbuf_delete(pi->reply); pi->reply = NULL; port_iterator_fetch_port_desc(pi); return; } struct ofputil_switch_features features; enum ofperr error = ofputil_pull_switch_features(pi->reply, &features); if (error) { printf("%s: failed to decode features reply (%s)", vconn_get_name(pi->vconn), ofperr_to_string(error)); assert(0); } } /* Initializes 'pi' to prepare for iterating through all of the ports on the * OpenFlow switch to which 'vconn' is connected. * * During iteration, the client should not make other use of 'vconn', because * that can cause other messages to be interleaved with the replies used by the * iterator and thus some ports may be missed or a hang can occur. */ static void port_iterator_init(struct port_iterator *pi, struct vconn *vconn) { memset(pi, 0, sizeof *pi); pi->vconn = vconn; if (vconn_get_version(vconn) < OFP13_VERSION) { port_iterator_fetch_features(pi); } else { port_iterator_fetch_port_desc(pi); } } /* Obtains the next port from 'pi'. On success, initializes '*pp' with the * port's details and returns true, otherwise (if all the ports have already * been seen), returns false. */ static bool port_iterator_next(struct port_iterator *pi, struct ofputil_phy_port *pp) { int retval; for (;;) { if (pi->reply) { int retval = ofputil_pull_phy_port(vconn_get_version(pi->vconn), pi->reply, pp); if (!retval) { return true; } else if (retval != EOF) { #if 0 ovs_fatal(0, "received bad reply: %s", ofp_to_string(pi->reply->data, pi->reply->size, verbosity + 1)); #else assert(0); #endif } } if (pi->variant == PI_FEATURES || !pi->more) { return false; } ovs_be32 recv_xid; do { ofpbuf_delete(pi->reply); retval = vconn_recv_block(pi->vconn, &pi->reply); assert(retval==0); recv_xid = ((struct ofp_header *) pi->reply->data)->xid; } while (pi->send_xid != recv_xid); struct ofp_header *oh = pi->reply->data; enum ofptype type; if (ofptype_pull(&type, pi->reply) || type != OFPTYPE_PORT_DESC_STATS_REPLY) { #if 0 ovs_fatal(0, "received bad reply: %s", ofp_to_string(pi->reply->data, pi->reply->size, verbosity + 1)); #else assert(0); #endif } pi->more = (ofpmp_flags(oh) & OFPSF_REPLY_MORE) != 0; } } /* Destroys iterator 'pi'. */ static void port_iterator_destroy(struct port_iterator *pi) { if (pi) { while (pi->variant == PI_PORT_DESC && pi->more) { /* Drain vconn's queue of any other replies for this request. */ struct ofputil_phy_port pp; port_iterator_next(pi, &pp); } ofpbuf_delete(pi->reply); } } yuma123_2.14/example-modules/ietf-network-bridge/Makefile.am0000775000175000017500000000313614770023131024136 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libietf-network-bridge.la libietf_network_bridge_la_SOURCES = \ ietf-network-bridge.c if STANDALONE # Standalone mode. Depends on installed libyuma-dev libietf_network_bridge_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libietf_network_bridge_la_LDFLAGS = -module -lyumaagt -lyumancx else # Integrated yuma123 build libietf_network_bridge_la_CPPFLAGS = -I $(top_srcdir)/netconf/src/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform libietf_network_bridge_la_LDFLAGS = -module $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la endif netconfmodule_LTLIBRARIES += libietf-network-bridge-openflow.la libietf_network_bridge_openflow_la_SOURCES = \ ietf-network-bridge-openflow.c if STANDALONE # Standalone mode. Depends on installed libyuma-dev libietf_network_bridge_openflow_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/ncx -I${includedir}/yuma/platform -I${includedir}/openvswitch libietf_network_bridge_openflow_la_LDFLAGS = -module -lyumaagt -lyumancx -lopenvswitch else # Integrated yuma123 build libietf_network_bridge_openflow_la_CPPFLAGS = -I $(top_srcdir)/netconf/src/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I${includedir}/openvswitch libietf_network_bridge_openflow_la_LDFLAGS = -module $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la -lopenvswitch endif dist_examples_yang_DATA = example-bridge.yang yuma123_2.14/example-modules/ietf-network-bridge/example-bridge-cfg.xml0000664000175000017500000003760714770023131026255 0ustar vladimirvladimir p0 0 example:default-port 0 p1 1 example:default-port 1 p2 2 example:default-port 2 example:best-effort example:best-effort example:best-effort example:signaling example:video0 example:video1 example:default-port example:default-port example:best-effort example:default-port p example:pri2 0 example:signaling example:default-port r1 example:in 0 example:video0 example:default-port t example:timeslot0 0 example:video1 example:default-port t example:timeslot1 0 a example:strict-priority-aggregator example:pri0 3 2048 r2 example:in 0 p example:strict-priority-aggregator example:pri0 1 2048 example:pri1 1 32768 example:pri2 3 1048576 r1 example:rate-limiter example:in 3 a example:pri0 0 10000000 12500 r2 example:rate-limiter example:in 1 p example:pri0 0 10000000 125000 t example:cyclic-timeslot-schedule-aggregator example:timeslot0 3 1048576 example:timeslot1 3 1048576 p example:pri0 2 10000000 5000000 5000000 best-effort-to-host0
00:01:02:03:00:00
0 p0 example:best-effort
best-effort-to-host1
00:01:02:03:00:01
0 p1 example:best-effort
best-effort-to-host2
00:01:02:03:00:02
0 p2 example:best-effort
ptp-to-host0
00:01:02:03:00:00
0 p0 example:signaling
ptp-to-host1
00:01:02:03:00:01
0 p1 example:signaling
ptp-to-host2
00:01:02:03:00:02
0 p2 example:signaling
video0 10 0 p2 example:video0 video1 11 0 p2 example:video1
if0 ianaift:ethernetCsmacd p0 if1 ianaift:ethernetCsmacd p1 if2 ianaift:ethernetCsmacd p2
yuma123_2.14/example-modules/ietf-network-bridge/ietf-network-bridge-openflow.c0000664000175000017500000010561614770023131027750 0ustar vladimirvladimir/* module ietf-network-bridge-flows implementation for OpenFlow device management namespace urn:ietf:params:xml:ns:yang:ietf-network-bridge-flows */ #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_commit_complete.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "val123.h" /* openvswitch */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define DSCP_DEFAULT (IPTOS_PREC_INTERNETCONTROL >> 2) #define SCHED_STATE_MOD "ietf-network-bridge-scheduler-state" #define FLOWS_MOD "ietf-network-bridge-flows" /* module static variables */ static ncx_module_t *ietf_interfaces_mod; static ncx_module_t *iana_if_type_mod; static ncx_module_t *ietf_network_bridge_mod; static ncx_module_t *ietf_network_bridge_scheduler_mod; static ncx_module_t *ietf_network_bridge_flows_mod; static ncx_module_t *example_bridge_mod; static obj_template_t* bridge_obj; static obj_template_t* packet_received_notification_obj; static obj_template_t* packet_received_notification_ingress_obj; static obj_template_t* packet_received_notification_payload_obj; static struct vconn *vconn; static int ofp_version; void transmit_packet(char* if_name, uint8_t* packet_data, unsigned int len); /* Helper functions */ #include "ovs-ofctl-utils.h" typedef struct dict_node_t_ { dlq_hdr_t qhdr; unsigned int portnum; char name[256]; } dict_node_t; static void dict_init(dlq_hdr_t *que) { dlq_createSQue(que); } static void dict_clear(dlq_hdr_t *que) { dict_node_t *dn; while (!dlq_empty(que)) { dn = (dict_node_t *)dlq_deque(que); free(dn); } dlq_createSQue(que); } static char* dict_get(dlq_hdr_t *que, unsigned int portnum) { dict_node_t *dn; for (dn = (dict_node_t *)dlq_firstEntry(que); dn != NULL; dn = (dict_node_t *)dlq_nextEntry(dn)) { if(dn->portnum==portnum) { return dn->name; } } return NULL; } static int dict_get_num(dlq_hdr_t *que, const char* name) { dict_node_t *dn; for (dn = (dict_node_t *)dlq_firstEntry(que); dn != NULL; dn = (dict_node_t *)dlq_nextEntry(dn)) { if(0==strcmp(dn->name,name)) { return dn->portnum; } } return -1; } static void dict_add(dlq_hdr_t *que, unsigned int portnum, const char* name) { dict_node_t *dn = malloc(sizeof(dict_node_t)); assert(dn); dn->portnum=portnum; strcpy(dn->name,name); dlq_enque (dn, que); } /* Registered callback functions: get_interfaces */ static dlq_hdr_t port_to_name_dict; static status_t y_transmit_packet_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; int ret; val_value_t *egress_val; val_value_t *payload_val; egress_val = val_find_child( msg->rpc_input, FLOWS_MOD, "egress"); payload_val = val_find_child( msg->rpc_input, FLOWS_MOD, "payload"); printf("transmit-packet egress=%s\n",VAL_STRING(egress_val)); transmit_packet(VAL_STRING(egress_val), payload_val->v.binary.ustr, payload_val->v.binary.ustrlen); return NO_ERR; } /* y_transmit_packet_invoke */ static char* make_flow_spec_str(val_value_t* flow_val, int delete) { status_t res; val_value_t* id_val; val_value_t* match_val; val_value_t* in_port_val; val_value_t* traffic_class_val; val_value_t* match_ethernet_source_address_val; val_value_t* match_ethernet_destination_address_val; val_value_t* match_ethernet_type_val; val_value_t* match_vlan_id_val; val_value_t* actions_val; val_value_t* action_val; boolean add_comma; char flow_spec_str[512]=""; /* flow spec str compatible with the ovs-ofctl FLOW format e.g. "in_port=2,actions=output:1" */ id_val=val_find_child(flow_val,FLOWS_MOD,"id"); assert(id_val); res = xpath_find_val_target(flow_val, NULL/*mod*/, "./match/in-port", &in_port_val); assert(res==NO_ERR); if(in_port_val) { sprintf(flow_spec_str+strlen(flow_spec_str),"in_port=%d",dict_get_num(&port_to_name_dict, VAL_STRING(in_port_val))); } res = xpath_find_val_target(flow_val, NULL/*mod*/, "./match/ethernet-match/ethernet-destination/address", &match_ethernet_destination_address_val); assert(res==NO_ERR); if(match_ethernet_destination_address_val) { val_value_t* mask_val; mask_val = val_find_child(match_ethernet_destination_address_val->parent,"ietf-network-bridge-flows","mask"); sprintf(flow_spec_str+strlen(flow_spec_str),",dl_dst=%s",VAL_STRING(match_ethernet_destination_address_val)); if(mask_val) { sprintf(flow_spec_str+strlen(flow_spec_str),",/%s",VAL_STRING(mask_val)); } } res = xpath_find_val_target(flow_val, NULL/*mod*/, "./match/ethernet-match/ethernet-source/address", &match_ethernet_source_address_val); assert(res==NO_ERR); if(match_ethernet_source_address_val) { val_value_t* mask_val; mask_val = val_find_child(match_ethernet_source_address_val->parent,"ietf-network-bridge-flows","mask"); sprintf(flow_spec_str+strlen(flow_spec_str),",dl_src=%s",VAL_STRING(match_ethernet_source_address_val)); if(mask_val) { sprintf(flow_spec_str+strlen(flow_spec_str),",/%s",VAL_STRING(mask_val)); } } res = xpath_find_val_target(flow_val, NULL/*mod*/, "./match/ethernet-match/ethernet-type/type", &match_ethernet_type_val); assert(res==NO_ERR); if(match_ethernet_type_val) { sprintf(flow_spec_str+strlen(flow_spec_str),",dl_type=%u",VAL_UINT32(match_ethernet_type_val)); } res = xpath_find_val_target(flow_val, NULL/*mod*/, "./match/vlan-match/vlan-id/vlan-id", &match_vlan_id_val); if(match_vlan_id_val) { sprintf(flow_spec_str+strlen(flow_spec_str),",dl_vlan=%u",VAL_UINT32(match_vlan_id_val)); } if(!delete) { sprintf(flow_spec_str+strlen(flow_spec_str),",actions="); actions_val = val_find_child(flow_val, FLOWS_MOD, "actions"); add_comma=FALSE; for(action_val = val_find_child(actions_val, FLOWS_MOD, "action"); action_val != NULL; action_val = val_find_next_child(actions_val, FLOWS_MOD, "action", action_val)) { val_value_t* order_val; val_value_t* output_action_val; val_value_t* push_vlan_action_val; val_value_t* pop_vlan_action_val; if(add_comma) { sprintf(flow_spec_str+strlen(flow_spec_str),","); } else { add_comma=TRUE; } order_val = val_find_child(action_val, FLOWS_MOD, "order"); assert(order_val); output_action_val = val_find_child(action_val, FLOWS_MOD, "output-action"); push_vlan_action_val = val_find_child(action_val, FLOWS_MOD, "push-vlan-action"); pop_vlan_action_val = val_find_child(action_val, FLOWS_MOD, "pop-vlan-action"); if(output_action_val!=NULL) { val_value_t* out_port_val; out_port_val=val_find_child(output_action_val, FLOWS_MOD, "out-port"); sprintf(flow_spec_str+strlen(flow_spec_str),"output:%d",dict_get_num(&port_to_name_dict, VAL_STRING(out_port_val))); } else if(push_vlan_action_val!=NULL) { val_value_t* val; val=val_find_child(push_vlan_action_val,FLOWS_MOD,"vlan-id"); if(val) { sprintf(flow_spec_str+strlen(flow_spec_str),"mod_vlan_vid:%u", VAL_UINT16(val)); } val=val_find_child(push_vlan_action_val,FLOWS_MOD,"ethernet-type"); if(val) { sprintf(flow_spec_str+strlen(flow_spec_str),"push_vlan:%u", VAL_UINT32(val)); } val=val_find_child(push_vlan_action_val,FLOWS_MOD,"pcp"); if(val) { sprintf(flow_spec_str+strlen(flow_spec_str),"mod_vlan_pcp:%u", VAL_UINT32(val)); } val=val_find_child(push_vlan_action_val,FLOWS_MOD,"vlan-cfi"); if(val) { /*TODO VAL_UINT32(val)*/ assert(0); } } else if(pop_vlan_action_val!=NULL) { sprintf(flow_spec_str+strlen(flow_spec_str),"strip_vlan"); } else { assert(0); } } } return strdup(flow_spec_str); } static void flow_common(val_value_t* flow_val, int delete) { printf("flow_common: %s\n", delete?"delete":"add"); val_dump_value(flow_val,1); struct ofputil_flow_mod fm; struct ofpbuf *reply; char *error; enum ofputil_protocol usable_protocols; enum ofputil_protocol protocol; char* flow_spec_str; protocol = ofputil_protocol_from_ofp_version(ofp_version); flow_spec_str = make_flow_spec_str(flow_val, delete); error = parse_ofp_flow_mod_str(&fm, flow_spec_str /*for example "in_port=2,actions=output:1" */, NULL, NULL, delete?OFPFC_DELETE:OFPFC_ADD, &usable_protocols); assert(error==NULL); vconn_transact_noreply(vconn, ofputil_encode_flow_mod(&fm, protocol), &reply); free(CONST_CAST(struct ofpact *, fm.ofpacts)); free(flow_spec_str); } static void flow_add(val_value_t* flow_val) { flow_common(flow_val, 0 /*delete*/); } static void flow_delete(val_value_t* flow_val) { flow_common(flow_val, 1 /*delete*/); } static unsigned int get_port_out_action_count(char* if_name, val_value_t* root_val) { status_t res; unsigned int match_count=0; obj_template_t* out_port_obj; val_value_t* out_port_val; res = xpath_find_schema_target_int("/flow:flows/flow/actions/action/output-action/out-port",&out_port_obj); assert(res==NO_ERR && out_port_obj!=NULL); for(out_port_val=val123_get_first_obj_instance(root_val, out_port_obj); out_port_val!=NULL; out_port_val=val123_get_next_obj_instance(root_val, out_port_val)) { if(0==strcmp(VAL_STRING(out_port_val),if_name)) { match_count++; } } return match_count; } static status_t get_flow_statistics(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val); static int update_config(val_value_t* config_cur_val, val_value_t* config_new_val) { status_t res; val_value_t *flows_cur_val, *flow_cur_val; val_value_t *flows_new_val, *flow_new_val; if(config_new_val == NULL) { flows_new_val = NULL; } else { flows_new_val = val_find_child(config_new_val, FLOWS_MOD, "flows"); } if(config_cur_val == NULL) { flows_cur_val = NULL; } else { flows_cur_val = val_find_child(config_cur_val, FLOWS_MOD, "flows"); } /* 2 step (delete/add) flow configuration */ /* 1. deactivation loop - deletes all deleted or modified flows */ if(flows_cur_val!=NULL) { for (flow_cur_val = val_get_first_child(flows_cur_val); flow_cur_val != NULL; flow_cur_val = val_get_next_child(flow_cur_val)) { flow_new_val = val123_find_match(config_new_val, flow_cur_val); if(flow_new_val==NULL || 0!=val_compare_ex(flow_cur_val,flow_new_val,TRUE)) { flow_delete(flow_cur_val); } } } /* 2. activation loop - adds all new or modified flows */ if(flows_new_val!=NULL) { for (flow_new_val = val_get_first_child(flows_new_val); flow_new_val != NULL; flow_new_val = val_get_next_child(flow_new_val)) { flow_cur_val = val123_find_match(config_cur_val, flow_new_val); if(flow_cur_val==NULL || 0!=val_compare_ex(flow_new_val,flow_cur_val,TRUE)) { flow_add(flow_new_val); /* register flow-statistics */ obj_template_t* flow_statistics_obj; val_value_t* flow_statistics_val; flow_statistics_obj = obj_find_child(flow_new_val->obj, "ietf-network-bridge-flows", "flow-statistics"); assert(flow_statistics_obj); flow_statistics_val = val_new_value(); assert(flow_statistics_val); val_init_virtual(flow_statistics_val, get_flow_statistics, flow_statistics_obj); val_add_child(flow_statistics_val, flow_new_val); } } } return NO_ERR; } static val_value_t* prev_root_val = NULL; static int update_config_wrapper() { cfg_template_t *runningcfg; status_t res; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg!=NULL && runningcfg->root!=NULL); if(prev_root_val!=NULL) { val_value_t* cur_root_val; cur_root_val = val_clone_config_data(runningcfg->root, &res); if(0==val_compare(cur_root_val,prev_root_val)) { /*no change*/ val_free_value(cur_root_val); return 0; } val_free_value(cur_root_val); } update_config(prev_root_val, runningcfg->root); if(prev_root_val!=NULL) { val_free_value(prev_root_val); } prev_root_val = val_clone_config_data(runningcfg->root, &res); return 0; } static status_t y_commit_complete(void) { update_config_wrapper(); return NO_ERR; } static status_t get_interfaces(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res; res = NO_ERR; int retval; struct ofpbuf *request; struct ofpbuf *reply; int port; obj_template_t* interface_obj; obj_template_t* name_obj; obj_template_t* oper_status_obj; obj_template_t* speed_obj; obj_template_t* statistics_obj; obj_template_t* out_octets_obj; obj_template_t* out_discards_obj; obj_template_t* in_octets_obj; obj_template_t* in_discards_obj; val_value_t* interfaces_val; val_value_t* interface_val; val_value_t* name_val; val_value_t* oper_status_val; val_value_t* speed_val; val_value_t* statistics_val; interfaces_val = dst_val; res = NO_ERR; printf("Called get_interfaces.\n"); interface_obj = obj_find_child(interfaces_val->obj, "ietf-interfaces", "interface"); assert(interface_obj); name_obj = obj_find_child(interface_obj, "ietf-interfaces", "name"); assert(name_obj); oper_status_obj = obj_find_child(interface_obj, "ietf-interfaces", "oper-status"); assert(oper_status_obj); speed_obj = obj_find_child(interface_obj, "ietf-interfaces", "speed"); assert(speed_obj); statistics_obj = obj_find_child(interface_obj, "ietf-interfaces", "statistics"); assert(statistics_obj); out_octets_obj = obj_find_child(statistics_obj, "ietf-interfaces", "out-octets"); assert(out_octets_obj); in_octets_obj = obj_find_child(statistics_obj, "ietf-interfaces", "in-octets"); assert(in_octets_obj); out_discards_obj = obj_find_child(statistics_obj, "ietf-interfaces", "out-discards"); assert(out_discards_obj); in_discards_obj = obj_find_child(statistics_obj, "ietf-interfaces", "in-discards"); assert(in_discards_obj); struct port_iterator pi; struct ofputil_phy_port p; for (port_iterator_init(&pi, vconn); port_iterator_next(&pi, &p); ) { struct ofputil_port_stats ps; printf("[%d] %s\n", p.port_no, p.name); //dict_add(&port_to_name_dict, p.port_no, p.name); assert(p.port_no==dict_get_num(&port_to_name_dict, p.name)); interface_val=val_new_value(); assert(interface_val); val_init_from_template(interface_val, interface_obj); val_add_child(interface_val, interfaces_val); /* name */ name_val=val_new_value(); assert(name_val); res = val_set_simval_obj(name_val, name_obj, p.name); assert(res==NO_ERR); val_add_child(name_val, interface_val); res = val_gen_index_chain(interface_obj, interface_val); assert(res == NO_ERR); /* oper-status */ oper_status_val = val_new_value(); assert(oper_status_val); res = val_set_simval_obj(oper_status_val, oper_status_obj, p.state==OFPUTIL_PS_LINK_DOWN?"down":"up"); assert(res==NO_ERR); val_add_child(oper_status_val, interface_val); /* speed */ if(p.curr_speed>0 || (p.max_speed>0 && p.state==OFPUTIL_PS_LINK_DOWN)) { speed_val=val_new_value(); assert(speed_val); val_init_from_template(speed_val, speed_obj); VAL_UINT32(speed_val)=(p.state==OFPUTIL_PS_LINK_DOWN?p.max_speed:p.curr_speed)*1000; val_add_child(speed_val, interface_val); } } port_iterator_destroy(&pi); request = ofputil_encode_dump_ports_request(vconn_get_version(vconn), /*port*/ OFPP_ANY); retval=vconn_transact(vconn, request, &reply); assert(retval==0); for (;;) { char* port_name; struct ofputil_port_stats ps; retval = ofputil_decode_port_stats(&ps, reply); if(retval==EOF) { break; } assert(retval==0); printf("[%u]", (uint32_t)ps.port_no); printf("\n"); printf("rx:\n"); printf("pkts=%llu\n", ps.stats.rx_packets); printf("bytes=%llu\n", ps.stats.rx_bytes); printf("drop=%llu\n", ps.stats.rx_dropped); printf("errs=%llu\n", ps.stats.rx_errors); printf("frame=%llu\n", ps.stats.rx_frame_errors); printf("over=%llu\n", ps.stats.rx_over_errors); printf("crc=%llu\n", ps.stats.rx_crc_errors); printf("\n"); printf("tx:\n"); printf("pkts=%llu\n", ps.stats.tx_packets); printf("bytes=%llu\n", ps.stats.tx_bytes); printf("drop=%llu\n", ps.stats.tx_dropped); printf("errs=%llu\n", ps.stats.tx_errors); printf("coll=%llu\n", ps.stats.collisions); printf("\n"); if (ps.duration_sec != UINT32_MAX) { printf("%10u.%09u\n", ps.duration_sec, ps.duration_nsec); } port_name=dict_get(&port_to_name_dict, ps.port_no); assert(port_name); for (interface_val = val_get_first_child(interfaces_val); interface_val != NULL; interface_val = val_get_next_child(interface_val)) { val_value_t* val; name_val = val_find_child(interface_val, "ietf-interfaces", "name"); if(0!=strcmp(VAL_STRING(name_val),port_name)) { continue; } /* statistics */ statistics_val=val_new_value(); assert(statistics_val); val_init_from_template(statistics_val, statistics_obj); val_add_child(statistics_val, interface_val); /* out-octets */ val=val_new_value(); assert(val); val_init_from_template(val, out_octets_obj); VAL_UINT64(val)=ps.stats.tx_bytes; val_add_child(val, statistics_val); /* out-discards */ val=val_new_value(); assert(val); val_init_from_template(val, out_discards_obj); VAL_UINT64(val)=ps.stats.tx_dropped; val_add_child(val, statistics_val); /* in-octets */ val=val_new_value(); assert(val); val_init_from_template(val, in_octets_obj); VAL_UINT64(val)=ps.stats.rx_bytes; val_add_child(val, statistics_val); /* in-discards */ val=val_new_value(); assert(val); val_init_from_template(val, in_discards_obj); VAL_UINT64(val)=ps.stats.rx_dropped; val_add_child(val, statistics_val); } } ofpbuf_delete(reply); return res; } static status_t get_flow_statistics(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { int ret; status_t res; res = NO_ERR; int retval; struct ofpbuf *request; struct ofpbuf *reply; int port; enum ofputil_protocol protocol; obj_template_t* obj; val_value_t* val; val_value_t* flow_statistics_val; flow_statistics_val = dst_val; res = NO_ERR; printf("Called get_flow_statistics.\n"); struct ofputil_flow_stats fs; struct ofpbuf ofpacts; ovs_be32 send_xid; ovs_be32 recv_xid; struct ofputil_flow_stats_request fsr; char* flow_spec_str; enum ofputil_protocol usable_protocols; char* error; /* fill the protocol independent flow stats request struct */ flow_spec_str = make_flow_spec_str(flow_statistics_val->parent, 1); #if 0 struct ofputil_flow_mod fm; error = parse_ofp_flow_mod_str(&fm, flow_spec_str /*for example "in_port=2,actions=output:1" */, NULL, NULL, OFPFC_ADD /* not important */, &usable_protocols); assert(error==NULL); free(flow_spec_str); fsr.aggregate = false; //match_init_catchall(&fsr.match); fsr.match=fm.match; fsr.out_port = OFPP_ANY; fsr.out_group = OFPG_ANY; fsr.table_id = 0xff; fsr.cookie = fsr.cookie_mask = htonll(0); #else error = parse_ofp_flow_stats_request_str(&fsr,false /*aggregate*/,flow_spec_str,NULL,NULL,&usable_protocols); #endif /* generate protocol specific request */ protocol = ofputil_protocol_from_ofp_version(ofp_version); request = ofputil_encode_flow_stats_request(&fsr, protocol); send_xid = ((struct ofp_header *) request->data)->xid; vconn_send_block(vconn, request); ret = vconn_recv_xid(vconn, send_xid, &reply); assert(ret==0); /* Pull an individual flow stats reply out of the message. */ ofpbuf_init(&ofpacts, 0); retval = ofputil_decode_flow_stats_reply(&fs, reply, false, &ofpacts); assert(retval==0); ofpbuf_uninit(&ofpacts); ofpbuf_delete(reply); /* packet-count */ obj = obj_find_child(flow_statistics_val->obj, "ietf-network-bridge-flows", "packet-count"); assert(obj); val=val_new_value(); assert(val); val_init_from_template(val, obj); VAL_UINT64(val)=fs.packet_count; val_add_child(val, flow_statistics_val); /* byte-count */ obj = obj_find_child(flow_statistics_val->obj, "ietf-network-bridge-flows", "byte-count"); assert(obj); val=val_new_value(); assert(val); val_init_from_template(val, obj); VAL_UINT64(val)=fs.byte_count; val_add_child(val, flow_statistics_val); return res; } static void process_echo_request(const struct ofp_header *rq) { struct ofpbuf *reply; reply=ofputil_encode_echo_reply(rq); printf("process_echo_request send reply len=%d\n", reply->size); vconn_send(vconn, reply); } static void send_packet_received_notification(char* ingress, uint8_t* payload_buf, uint32_t payload_len) { status_t res; agt_not_msg_t *notif; val_value_t* ingress_val; val_value_t* payload_val; notif = agt_not_new_notification(packet_received_notification_obj); assert (notif != NULL); /* add params to payload */ /* ingress */ ingress_val = val_new_value(); assert(ingress_val); res = val_set_simval_obj(ingress_val, packet_received_notification_ingress_obj, ingress); agt_not_add_to_payload(notif, ingress_val); /* payload */ payload_val = val_new_value(); assert(payload_val); val_init_from_template(payload_val, packet_received_notification_payload_obj); payload_val->v.binary.ustr = malloc(payload_len); assert(payload_val->v.binary.ustr); memcpy(payload_val->v.binary.ustr,payload_buf,payload_len); payload_val->v.binary.ustrlen = payload_len; agt_not_add_to_payload(notif, payload_val); agt_not_queue_notification(notif); } static void process_packet_in(const struct ofp_header *msg) { struct ofputil_packet_in pin; struct ofpbuf continuation; enum ofperr error = ofputil_decode_packet_in(msg, true, NULL, NULL, &pin, NULL, NULL, &continuation); assert(error==0); //assert(pin.reason==OFPR_ACTION); struct ofpbuf userdata = ofpbuf_const_initializer(pin.userdata, pin.userdata_len); //const struct action_header *ah = ofpbuf_pull(&userdata, sizeof(*ah)); //assert(ah); char* ingress; uint8_t* payload_buf; uint32_t payload_len; send_packet_received_notification(dict_get(&port_to_name_dict, pin.flow_metadata.flow.in_port.ofp_port), pin.packet, pin.packet_len); } void transmit_packet(char* if_name, uint8_t* packet_data, unsigned int len) { struct ofputil_packet_out po; struct ofpbuf ofpacts; struct dp_packet *packet; struct ofpbuf *opo; const char *error_msg; struct ofpbuf *reply; enum ofputil_protocol usable_protocols; enum ofputil_protocol protocol; char action_str[]="output:2147483648"; protocol = ofputil_protocol_from_ofp_version(ofp_version); ofpbuf_init(&ofpacts, 64); sprintf(action_str,"output:%u",dict_get_num(&port_to_name_dict, if_name)); struct ofpact_parse_params pp = { .port_map = NULL, .table_map = NULL, .ofpacts = &ofpacts, .usable_protocols = &usable_protocols }; error_msg = ofpacts_parse_actions(action_str, &pp); assert(error_msg==NULL); assert(usable_protocols&protocol); po.buffer_id = UINT32_MAX; po.ofpacts = ofpacts.data; po.ofpacts_len = ofpacts.size; #if 0 error_msg = eth_from_hex("6CA96F0000026CA96F00000108004500002ED4A500000A115816C0000201C0000202C0200007001A00000102030405060708090A0B0C0D0E0F101112", &packet); assert(error_msg==0); //#else packet=dp_packet_new(len); dp_packet_put(packet, (const void *) packet_data, len); po.packet = dp_packet_data(packet); po.packet_len = dp_packet_size(packet); #else void* buf = malloc(len); memcpy(buf, packet_data, len); po.packet = buf; po.packet_len = len; #endif ofp_version = vconn_get_version(vconn); opo = ofputil_encode_packet_out(&po, protocol); //vconn_transact_noreply(vconn, opo, &reply); vconn_send_block(vconn, opo); #if 0 dp_packet_delete(packet); #endif ofpbuf_uninit(&ofpacts); } int network_bridge_timer(uint32 timer_id, void *cookie) { unsigned int i; int retval=0; struct ofpbuf *msg; struct ofpbuf *request; struct ofpbuf *reply; enum ofptype type; printf("In network_bridge_timer.\n"); for(i=0;retval==0;i++) { retval = vconn_recv(vconn, &msg); if(retval == EAGAIN) { break; } else if(retval!=0) { assert(0); } if(i<10) { ofp_print(stdout, msg->data, msg->size, NULL, NULL, 10 + 1); } if(ofptype_pull(&type, msg)) { assert(0); } if (type == OFPTYPE_ECHO_REQUEST) { process_echo_request(msg->header); } else if (type == OFPTYPE_PACKET_IN) { if(i<10) { process_packet_in(msg->header); } } else if (type == OFPTYPE_FLOW_REMOVED) { /* Nothing to do. */ assert(0); } ofpbuf_delete(msg); } if(i>1) { printf("OpenFlow notifications processed: %u\n", i); } printf("Out network_bridge_timer.\n"); return 0; } /* The 3 mandatory callback functions: y_ietf_network_bridge_openflow_init, y_ietf_network_bridge_openflow_init2, y_ietf_network_bridge_openflow_cleanup */ status_t y_ietf_network_bridge_openflow_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t* agt_profile; obj_template_t* flows_obj; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-interfaces", NULL, &agt_profile->agt_savedevQ, &ietf_interfaces_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "iana-if-type", NULL, &agt_profile->agt_savedevQ, &iana_if_type_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "ietf-network-bridge", NULL, &agt_profile->agt_savedevQ, &ietf_network_bridge_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "ietf-network-bridge", NULL, &agt_profile->agt_savedevQ, &ietf_network_bridge_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "ietf-network-bridge-flows", NULL, &agt_profile->agt_savedevQ, &ietf_network_bridge_flows_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "ietf-network-bridge-scheduler", NULL, &agt_profile->agt_savedevQ, &ietf_network_bridge_scheduler_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "example-bridge", NULL, &agt_profile->agt_savedevQ, &example_bridge_mod); if (res != NO_ERR) { return res; } /* packet-received notification */ packet_received_notification_obj = ncx_find_object( ietf_network_bridge_flows_mod, "packet-received"); assert(packet_received_notification_obj); packet_received_notification_ingress_obj = obj_find_child(packet_received_notification_obj, FLOWS_MOD, "ingress"); assert(packet_received_notification_ingress_obj); packet_received_notification_payload_obj = obj_find_child(packet_received_notification_obj, FLOWS_MOD, "payload"); assert(packet_received_notification_payload_obj); flows_obj = ncx_find_object( ietf_network_bridge_flows_mod, "flows"); assert(flows_obj != NULL); res=agt_commit_complete_register("ietf-network-bridge-openflow" /*SIL id string*/, y_commit_complete); assert(res == NO_ERR); res = agt_rpc_register_method( "ietf-network-bridge-flows", "transmit-packet", AGT_RPC_PH_INVOKE, y_transmit_packet_invoke); assert(res == NO_ERR); /* Wait for connection from OpenFlow node */ { int retval; struct pvconn *pvconn; char* vconn_arg; vconn_arg=getenv("VCONN_ARG"); if(vconn_arg==NULL) { vconn_arg="ptcp:16635"; } printf("Listen on %s ...\n", vconn_arg); retval = pvconn_open(vconn_arg, /*get_allowed_ofp_versions()*/(1u << OFP10_VERSION), DSCP_DEFAULT, &pvconn); printf("Waiting for connection"); while(1) { retval = pvconn_accept(pvconn, &vconn); if(retval == EAGAIN) { printf("."); fflush(stdout); sleep(1); continue; } else if (retval==0) { printf("Connected retval=%d.\n", retval); //new_switch(&switches[n_switches++], vconn); break; } else { assert(0); } } pvconn_close(pvconn); retval = vconn_connect_block(vconn); assert(retval==0); struct ofpbuf *request; struct ofpbuf *reply; ofp_version = vconn_get_version(vconn); assert(ofp_version > 0 && ofp_version < 0xff); /* Send OFPT_FEATURES_REQUEST. */ request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, ofp_version, 0); retval=vconn_transact(vconn, request, &reply); assert(retval==0); ofp_print(stdout, reply->data, reply->size, NULL, NULL, 10 + 1); ofpbuf_delete(reply); dict_init(&port_to_name_dict); struct port_iterator pi; struct ofputil_phy_port p; for (port_iterator_init(&pi, vconn); port_iterator_next(&pi, &p); ) { struct ofputil_port_stats ps; printf("[%d] %s\n", p.port_no, p.name); dict_add(&port_to_name_dict, p.port_no, p.name); } port_iterator_destroy(&pi); } return res; } status_t y_ietf_network_bridge_openflow_init2(void) { status_t res; cfg_template_t* runningcfg; ncx_module_t* mod; obj_template_t* interfaces_obj; val_value_t* root_val; val_value_t* interfaces_val; uint32 timer_id; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg && runningcfg->root); root_val = runningcfg->root; res = NO_ERR; mod = ncx_find_module("ietf-interfaces", NULL); assert(mod); interfaces_obj = ncx_find_object( mod, "interfaces-state"); assert(interfaces_obj); interfaces_val = val_find_child(root_val, "ietf-interfaces", "interfaces-state"); /* not designed to coexist with other implementations */ assert(interfaces_val==NULL); interfaces_val = val_new_value(); assert(interfaces_val); val_init_virtual(interfaces_val, get_interfaces, interfaces_obj); val_add_child(interfaces_val, root_val); res = agt_timer_create(1/* 1 sec period */, TRUE/*periodic*/, network_bridge_timer, interfaces_val/*cookie*/, &timer_id); /* Apply initial config - as if y_commit_complete was called */ y_commit_complete(); return res; } void y_ietf_network_bridge_openflow_cleanup (void) { } yuma123_2.14/example-modules/ietf-network-bridge/ietf-network-bridge.c0000664000175000017500000002062014770023131026110 0ustar vladimirvladimir/* module ietf-network-bridge namespace urn:ietf:params:xml:ns:yang:ietf-network-bridge implementation of common functions e.g. deriving out-discards form all scheduler discard counters */ #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "val123.h" #define SCHED_STATE_MOD "ietf-network-bridge-scheduler-state" /* module static variables */ static ncx_module_t *ietf_interfaces_mod; static ncx_module_t *iana_if_type_mod; static ncx_module_t *ietf_network_bridge_mod; static ncx_module_t *ietf_network_bridge_scheduler_mod; static ncx_module_t *ietf_network_bridge_flows_mod; static ncx_module_t *example_bridge_mod; static obj_template_t* bridge_obj; /* * This implementation independent function sums all discard counter instances with schema id * /interfaces-state/interface/scheduler/gate-controllers/gate-controller/inputs/input/discards * and adds the sum as /interfaces-state/interface/statistics/out-discards value */ status_t get_if_out_discards_cbfn(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res; uint64_t discards_sum=0; val_value_t* scheduler_val; val_value_t* gate_controllers_val; val_value_t* gate_controller_val; obj_template_t* out_discards_obj; val_value_t* out_discards_val; out_discards_obj = obj_find_child(vir_val->obj, "ietf-interfaces", "out-discards"); assert(out_discards_obj); scheduler_val = val_find_child(vir_val->parent, SCHED_STATE_MOD, "scheduler"); if(scheduler_val==NULL) { return ERR_NCX_SKIPPED; } if (val_is_virtual(scheduler_val)) { scheduler_val = val_get_virtual_value(NULL, scheduler_val, &res); if(res==ERR_NCX_SKIPPED) { return res; } assert(res==NO_ERR); assert(scheduler_val); } /* gate-controllers */ gate_controllers_val = val_find_child(scheduler_val, SCHED_STATE_MOD, "gate-controllers"); assert(gate_controllers_val); /* gate-controller */ for(gate_controller_val = val_get_first_child(gate_controllers_val); gate_controller_val != NULL; gate_controller_val = val_get_next_child(gate_controller_val)) { val_value_t* inputs_val; val_value_t* input_val; val_value_t* input_classes_val; val_value_t* input_class_val; /* inputs */ inputs_val = val_find_child(gate_controller_val, SCHED_STATE_MOD, "inputs"); assert(inputs_val); /* input */ for(input_val = val_get_first_child(inputs_val); input_val != NULL; input_val = val_get_next_child(input_val)) { val_value_t* discards_val; /* discards */ discards_val = val_find_child(input_val, SCHED_STATE_MOD, "discards"); if(discards_val==NULL) { /* some gate controllers do not have individual input discards counter */ continue; } discards_sum+=VAL_UINT64(discards_val); } /* input-classes */ input_classes_val = val_find_child(gate_controller_val, SCHED_STATE_MOD, "input-classes"); if(input_classes_val) { /* input-class */ for (input_class_val = val_get_first_child(input_classes_val); input_class_val != NULL; input_class_val = val_get_next_child(input_class_val)) { val_value_t* discards_val; /* discards */ discards_val = val_find_child(input_class_val, SCHED_STATE_MOD, "discards"); if(discards_val==NULL) { continue; } discards_sum+=VAL_UINT64(discards_val); } } } out_discards_val = val_new_value(); assert(out_discards_val); val_init_from_template(out_discards_val, out_discards_obj); VAL_UINT32(out_discards_val)=(uint32_t)(discards_sum&0xFFFFFFFF); val_add_child(out_discards_val, dst_val); return NO_ERR; } /* The 3 mandatory callback functions: y_ietf_network_bridge_init, y_ietf_network_bridge_init2, y_ietf_network_bridge_cleanup */ status_t y_ietf_network_bridge_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t* agt_profile; obj_template_t* flows_obj; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-interfaces", NULL, &agt_profile->agt_savedevQ, &ietf_interfaces_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "iana-if-type", NULL, &agt_profile->agt_savedevQ, &iana_if_type_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "ietf-network-bridge", NULL, &agt_profile->agt_savedevQ, &ietf_network_bridge_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "ietf-network-bridge", NULL, &agt_profile->agt_savedevQ, &ietf_network_bridge_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "ietf-network-bridge-flows", NULL, &agt_profile->agt_savedevQ, &ietf_network_bridge_flows_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "ietf-network-bridge-scheduler", NULL, &agt_profile->agt_savedevQ, &ietf_network_bridge_scheduler_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "example-bridge", NULL, &agt_profile->agt_savedevQ, &example_bridge_mod); if (res != NO_ERR) { return res; } flows_obj = ncx_find_object( ietf_network_bridge_flows_mod, "flows"); assert(flows_obj != NULL); return res; } status_t y_ietf_network_bridge_init2(void) { status_t res; cfg_template_t* runningcfg; val_value_t* interfaces_state_val; val_value_t* interface_val; obj_template_t* interface_obj; obj_template_t* statistics_obj; res = NO_ERR; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg && runningcfg->root); interfaces_state_val = val_find_child(runningcfg->root, "ietf-interfaces", "interfaces-state"); assert(interfaces_state_val); interface_obj = obj_find_child(interfaces_state_val->obj, "ietf-interfaces", "interface"); assert(interface_obj); statistics_obj = obj_find_child(interface_obj, "ietf-interfaces", "statistics"); assert(statistics_obj); /* register /interfaces-state/interface/statistics/out-discards virtual values callbacks */ for(interface_val = val_get_first_child(interfaces_state_val); interface_val != NULL; interface_val = val_get_next_child(interface_val)) { val_value_t* statistics_val; statistics_val = val_find_child(interface_val, "ietf-interfaces", "statistics"); if(statistics_val==NULL) { statistics_val = val_new_value(); assert(statistics_val); val_init_virtual(statistics_val, get_if_out_discards_cbfn, statistics_obj); val_add_child(statistics_val, interface_val); } else { val123_add_virtual_cb(statistics_val, get_if_out_discards_cbfn); } } return res; } void y_ietf_network_bridge_cleanup (void) { } yuma123_2.14/example-modules/ietf-network-bridge/configure.ac0000664000175000017500000000171614770023131024367 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-ietf-network-bridge], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL AC_ARG_ENABLE([standalone], [ --enable-standalone Turn on standalone build], [case "${enableval}" in yes) standalone=true ;; no) standalone=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-standalone]) ;; esac],[standalone=true]) AM_CONDITIONAL([STANDALONE], [test x$standalone = xtrue]) netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) examples_yangdir="$prefix/share/yuma/modules/examples" AC_SUBST(examples_yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/ietf-interfaces/0000775000175000017500000000000014770023131021274 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-interfaces/README0000664000175000017500000000646414770023131022166 0ustar vladimirvladimir==Standalone project for netconfd module implementing ietf-interfaces.yang== Dependency: installed netconfd run-time binaries and development shared libraries and headers. ===Build and install=== autoreconf -i -f ./configure CFLAGS="-g -O0" CXXFLAGS="-g -O0" --prefix=/usr make sudo make install ===Running=== Terminal 1: $ /usr/sbin/netconfd --module=ietf-interfaces --no-startup Terminal 2: $ yangcli --server=localhot --user=${USER} yangcli vladimir@localhost> xget /interfaces-state RPC Data Reply 2 for session 1: rpc-reply { data { interfaces-state { interface lo { name lo statistics { in-octets 4571644 in-unicast-pkts 50712 in-errors 0 in-discards 0 in-multicast-pkts 0 out-octets 4571644 out-unicast-pkts 50712 out-errors 0 out-discards 0 } } interface wlan0 { name wlan0 statistics { in-octets 0 in-unicast-pkts 0 in-errors 0 in-discards 0 in-multicast-pkts 0 out-octets 0 out-unicast-pkts 0 out-errors 0 out-discards 0 } } interface eth0 { name eth0 speed 1000000000 statistics { in-octets 633943896 in-unicast-pkts 23723378 in-errors 0 in-discards 0 in-multicast-pkts 359508 out-octets 702403431 out-unicast-pkts 6941930 out-errors 0 out-discards 0 } } } } } ===Running with NMDA=== Terminal 1: $ /usr/sbin/netconfd --with-nmda=true --modpath=/usr/share/yuma/nmda-modules:/usr/share/yuma/modules --module=ietf-interfaces --no-startup Terminal 2: $ yangcli --server=localhot --user=${USER} yangcli vladimir@localhost> get-data with-origin xpath-filter="/interfaces" datastore=operational RPC Data Reply 2 for session 1: rpc-reply { data { interfaces { interface lo { name lo statistics { in-octets 4571644 in-unicast-pkts 50712 in-errors 0 in-discards 0 in-multicast-pkts 0 out-octets 4571644 out-unicast-pkts 50712 out-errors 0 out-discards 0 } } interface wlan0 { name wlan0 statistics { in-octets 0 in-unicast-pkts 0 in-errors 0 in-discards 0 in-multicast-pkts 0 out-octets 0 out-unicast-pkts 0 out-errors 0 out-discards 0 } } interface eth0 { name eth0 speed 1000000000 statistics { in-octets 633943896 in-unicast-pkts 23723378 in-errors 0 in-discards 0 in-multicast-pkts 359508 out-octets 702403431 out-unicast-pkts 6941930 out-errors 0 out-discards 0 } } } } } ==Environment variables== ===INTERFACE_NAME_PREFIX=== Controls the prefix of the interface names. Allows to start multiple netconfd instances handling different interface sets. Example: INTERFACE_NAME_PREFIX='br0-eth' netconfd ... INTERFACE_NAME_PREFIX='br1-eth' netconfd ... yuma123_2.14/example-modules/ietf-interfaces/debian/0000775000175000017500000000000014770023131022516 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-interfaces/debian/source/0000775000175000017500000000000014770023131024016 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-interfaces/debian/source/format0000664000175000017500000000001414770023131025224 0ustar vladimirvladimir3.0 (quilt) yuma123_2.14/example-modules/ietf-interfaces/debian/control0000664000175000017500000000163514770023131024126 0ustar vladimirvladimirSource: yuma123-netconfd-module-ietf-interfaces Section: net Priority: optional Maintainer: Vladimir Vassilev Build-Depends: debhelper (>= 10), autoconf, libncurses5-dev, libreadline-dev, libssh2-1-dev, libssl-dev, libtool, libxml2-dev, libyuma-dev (>= 2.14), zlib1g-dev Standards-Version: 4.1.4 Homepage: http://yuma123.org Package: netconfd-module-ietf-interfaces Architecture: linux-any Depends: ${shlibs:Depends}, ${misc:Depends}, debconf, lsb-base (>= 3.2-13), libyuma2 (>= 2.14), iproute2, net-tools Description: SIL module for netconfd implementing ietf-interfaces.yang The NETCONF protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yuma123 netconfd module ietf-interfaces implements the functionality modeled in ietf-interfaces.yang using some common command line tools. yuma123_2.14/example-modules/ietf-interfaces/debian/rules0000775000175000017500000000071514770023131023601 0ustar vladimirvladimir#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ --with autoreconf yuma123_2.14/example-modules/ietf-interfaces/debian/copyright0000664000175000017500000000326514770023131024457 0ustar vladimirvladimirFiles: * Copyright: (C) 2016-2025 Vladimir Vassilev Copyright: (C) 2016-2018 Transpacket AS Copyright: (C) 2019-2025 Lightside Instruments AS License: BSD License: BSD Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andy Bierman BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. yuma123_2.14/example-modules/ietf-interfaces/debian/compat0000664000175000017500000000000314770023131023715 0ustar vladimirvladimir10 yuma123_2.14/example-modules/ietf-interfaces/debian/changelog0000664000175000017500000000027514770023131024374 0ustar vladimirvladimiryuma123-netconfd-module-ietf-interfaces (2.14-0) stable; urgency=medium * Unofficial package -- Vladimir Vassilev Sun, 23 Mar 2025 15:25:36 +0100 yuma123_2.14/example-modules/ietf-interfaces/get-interface-ipv40000775000175000017500000000035014770023131024615 0ustar vladimirvladimir#!/bin/sh ip=`ip -4 addr show $1 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'` if [ "$ip" = "" ] ; then exit 0 fi echo -n '
' echo -n "$ip" echo '
' yuma123_2.14/example-modules/ietf-interfaces/interfaces-notifications.yang0000664000175000017500000000140614770023131027147 0ustar vladimirvladimirmodule interfaces-notifications { namespace "http://yuma123.org/ns/interfaces-notifications"; prefix if-notif; import ietf-interfaces { prefix if; } organization "yuma123.org"; contact "Editor: Vladimir Vassilev "; description "This module contains a collection of YANG definitions for notifications for managing network interfaces."; revision 2016-11-12 { description "Initial revision."; } notification link-up { leaf if-name { type leafref { path "/if:interfaces/if:interface/if:name"; } } } notification link-down { leaf if-name { type leafref { path "/if:interfaces/if:interface/if:name"; } } } } yuma123_2.14/example-modules/ietf-interfaces/Makefile.am0000775000175000017500000000141614770023131023335 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libietf-interfaces.la libietf_interfaces_la_SOURCES = \ ietf-interfaces.c if STANDALONE # Standalone mode. Depends on installed libyuma-dev libietf_interfaces_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libietf_interfaces_la_LDFLAGS = -module -lyumaagt -lyumancx else # Integrated yuma123 build libietf_interfaces_la_CPPFLAGS = -I $(top_srcdir)/netconf/src/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform libietf_interfaces_la_LDFLAGS = -module $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la endif dist_examples_yang_DATA = \ interfaces-notifications.yang bin_SCRIPTS = get-interface-ipv4 yuma123_2.14/example-modules/ietf-interfaces/ietf-interfaces.c0000664000175000017500000006365014770023131024522 0ustar vladimirvladimir/* module ietf-interfaces namespace urn:ietf:params:xml:ns:yang:ietf-interfaces */ #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_cli.h" #include "agt_commit_complete.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_nmda.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "val.h" #include "val123.h" #include "val_set_cplxval_obj.h" #include #include #include #include #include /* module static variables */ static val_value_t* root_prev_val; static val_value_t* with_nmda_param_val; static uint32 timer_id; #define BUFSIZE 1024 static void my_send_link_state_notification(char* new_state, char* if_name) { status_t res; obj_template_t* notification_obj; obj_template_t* link_up_obj; obj_template_t* link_down_obj; obj_template_t* if_name_obj; val_value_t* if_name_val; agt_not_msg_t *notif; ncx_module_t *mod; mod = ncx_find_module("interfaces-notifications", NULL); if(mod==NULL) { /* only send notification if the model is loaded */ return; } link_down_obj = ncx_find_object(mod,"link-down"); assert(link_down_obj); link_up_obj = ncx_find_object(mod,"link-up"); assert(link_up_obj); if(0==strcmp(new_state, "down")) { notification_obj = link_down_obj; } else if(0==strcmp(new_state, "up")) { notification_obj = link_up_obj; } else { notification_obj = link_up_obj; /* work around */ } notif = agt_not_new_notification(notification_obj); assert (notif != NULL); /* add params to payload */ if_name_obj = obj_find_child(notification_obj, "interfaces-notifications", "if-name"); assert(if_name_obj != NULL); if_name_val = val_new_value(); assert(if_name_val != NULL); val_init_from_template(if_name_val, if_name_obj); res = val_set_simval_obj(if_name_val, if_name_val->obj, if_name); agt_not_add_to_payload(notif, if_name_val); agt_not_queue_notification(notif); } void oper_status_update(val_value_t* cur_val) { status_t res; val_value_t* prev_val; val_value_t* val; val_value_t* last_change_prev_val; val_value_t* dummy_val; val_value_t* name_val; /* compare the oper-status with the corresponding value in the prev root */ prev_val = val123_find_match(root_prev_val, cur_val); if(prev_val==NULL) { res=val123_clone_instance(root_prev_val, cur_val, &prev_val); assert(res==NO_ERR); } if(0!=strcmp(VAL_STRING(cur_val),VAL_STRING(prev_val))) { obj_template_t* last_change_obj; val_value_t* last_change_val; char tstamp_buf[32]; tstamp_datetime(tstamp_buf); last_change_val = val_new_value(); assert(last_change_val); last_change_obj = obj_find_child(cur_val->parent->obj,"ietf-interfaces","last-change"); assert(last_change_obj); val_init_from_template(last_change_val, last_change_obj); val_set_simval_obj(last_change_val, last_change_obj, tstamp_buf); last_change_prev_val = val_find_child(prev_val->parent, "ietf-interfaces", "last-change"); if(last_change_prev_val) { val_remove_child(last_change_prev_val); val_free_value(last_change_prev_val); } val_add_child(last_change_val, prev_val->parent); /* notify */ name_val=val_find_child(cur_val->parent,"ietf-interfaces","name"); assert(name_val); printf("Notification /interfaces/interface[name=%s]: oper-status changes from %s to %s at %s\n", VAL_STRING(name_val), VAL_STRING(prev_val),VAL_STRING(cur_val), VAL_STRING(last_change_val)); my_send_link_state_notification(VAL_STRING(cur_val), VAL_STRING(name_val)); val_set_simval_obj(prev_val, prev_val->obj, VAL_STRING(cur_val)); } } static status_t get_last_change(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res; val_value_t* last_change_val; last_change_val = val123_find_match(root_prev_val, dst_val); if(last_change_val==NULL) { return ERR_NCX_SKIPPED; } val_set_simval_obj(dst_val, dst_val->obj, VAL_STRING(last_change_val)); return NO_ERR; } static status_t get_oper_status(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res; int oper_status = 0; val_value_t *interface_val; val_value_t *name_val; ncx_btype_t btyp; typ_def_t *typdef, *basetypdef; typ_enum_t *typenum; FILE* f; char cmd_buf[NCX_MAX_LINELEN]; char status_buf[NCX_MAX_LINELEN]; char* fgets_ret; interface_val = vir_val->parent; assert(interface_val); name_val = val_find_child(interface_val, "ietf-interfaces", "name"); assert(name_val); /* open the /proc/net/dev file for reading */ sprintf(cmd_buf, "cat /sys/class/net/%s/operstate", VAL_STRING(name_val)); f = popen(cmd_buf, "r"); if (f == NULL) { return errno_to_status(); } fgets_ret = fgets((char *)status_buf, NCX_MAX_LINELEN, f); assert(fgets_ret!=NULL); fclose(f); strtok(status_buf,"\n"); /* check if we have corresponding entry in the oper-status enum */ btyp = obj_get_basetype(dst_val->obj); typdef = obj_get_typdef(dst_val->obj); basetypdef = typ_get_base_typdef(typdef); assert(btyp==NCX_BT_ENUM); for (typenum = typ_first_enumdef(basetypdef); typenum != NULL; typenum = typ_next_enumdef(typenum)) { if(0==strcmp((const char *)typenum->name, status_buf)) { break; } } if(typenum==NULL) { printf("Warning: unknown oper-status %s, reporting \"unknown\" instead.\n", status_buf); strcpy(status_buf, "unknown"); } res = val_set_simval_obj(dst_val, dst_val->obj, (const char *)status_buf); oper_status_update(dst_val); return res; } static status_t get_speed(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res; val_value_t *interface_val; val_value_t *name_val; FILE* f; char filename[NCX_MAX_LINELEN]; char status_buf[NCX_MAX_LINELEN]; char* fgets_ret; interface_val = vir_val->parent; assert(interface_val); name_val = val_find_child(interface_val, "ietf-interfaces", "name"); assert(name_val); /* open the /proc/net/dev file for reading */ sprintf(filename, "/sys/class/net/%s/speed", VAL_STRING(name_val)); f = fopen(filename, "r"); if (f == NULL) { return ERR_NCX_SKIPPED; } fgets_ret = fgets((char *)status_buf, NCX_MAX_LINELEN, f); fclose(f); if(fgets_ret==NULL) { return ERR_NCX_SKIPPED; } strtok(status_buf,"\n"); VAL_UINT64(dst_val) = (uint64_t)1000000*atoi(status_buf); return NO_ERR; } static status_t get_ipv4(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res; obj_template_t* obj; val_value_t* val; val_value_t* name_val; char* state_xml = "
123.123.123.123
"; ssize_t n; name_val = val_find_child(dst_val->parent, "ietf-interfaces", "name"); assert(name_val); char* ptr; res = NO_ERR; /* .../ipv4 */ char cmd[BUFSIZE]=""; char buf[BUFSIZE]=""; FILE *fp; sprintf(cmd, "get-interface-ipv4 %s", VAL_STRING(name_val)); #if 1 if ((fp = popen(cmd, "r")) == NULL) { printf("Error opening pipe!\n"); assert(0); } do { ptr = fgets(buf+strlen(buf), BUFSIZE, fp); } while(ptr); if(pclose(fp)) { printf("Command not found or exited with error status\n"); assert(0); } #else strcpy(buf, state_xml); #endif if(0==strlen(buf)) { return ERR_NCX_SKIPPED; } res = val_set_cplxval_obj(dst_val, vir_val->obj, buf); return res; } static status_t get_phys_address(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res; val_value_t* name_val; name_val = val_find_child(dst_val->parent, "ietf-interfaces", "name"); assert(name_val); char* ptr; res = NO_ERR; /* .../phys-address */ char buf[]="01:23:45:67:89:AB"; struct ifreq s; int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); strcpy(s.ifr_name, VAL_STRING(name_val)); if (0 == ioctl(fd, SIOCGIFHWADDR, &s)) { sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", (unsigned int) (s.ifr_addr.sa_data[0]&0xFF), (unsigned int) (s.ifr_addr.sa_data[1]&0xFF), (unsigned int) (s.ifr_addr.sa_data[2]&0xFF), (unsigned int) (s.ifr_addr.sa_data[3]&0xFF), (unsigned int) (s.ifr_addr.sa_data[4]&0xFF), (unsigned int) (s.ifr_addr.sa_data[5]&0xFF)) ; res = val_set_simval_obj(dst_val, vir_val->obj, buf); } else { res = ERR_NCX_SKIPPED; } close(fd); return res; } /* Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed lo: 4307500 49739 0 0 0 0 0 0 4307500 49739 0 0 0 0 0 0 wlan0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 eth0: 604474858 23680030 0 0 0 0 0 357073 701580994 6935958 0 0 0 0 1 0 */ static status_t add_interface_entry(char* buf, val_value_t* interfaces_val) { /*objs*/ obj_template_t* interface_obj; obj_template_t* name_obj; obj_template_t* oper_status_obj; obj_template_t* last_change_obj; obj_template_t* statistics_obj; obj_template_t* ipv4_obj; obj_template_t* obj; /*vals*/ val_value_t* interface_val; val_value_t* name_val; val_value_t* oper_status_val; val_value_t* last_change_val; val_value_t* statistics_val; val_value_t* ipv4_val; val_value_t* val; status_t res=NO_ERR; boolean done; char* name; char* str; char* endptr; unsigned int i; uint64_t counter; int ret; char* counter_names_array[] = { "in-octets", "in-unicast-pkts", "in-errors", "in-discards", NULL/*"in-fifo"*/, NULL/*"in-frames"*/, NULL/*in-compressed*/, "in-multicast-pkts", "out-octets", "out-unicast-pkts", "out-errors", "out-discards", NULL/*"out-fifo"*/, NULL/*out-collisions*/, NULL/*out-carrier*/, NULL/*out-compressed*/ }; /* get the start of the interface name */ str = buf; while (*str && isspace(*str)) { str++; } if (*str == '\0') { /* not expecting a line with just whitespace on it */ return ERR_NCX_SKIPPED; } else { name = str++; } /* get the end of the interface name */ while (*str && *str != ':') { str++; } if (*str != ':') { /* expected e.g. eth0: ...*/ return ERR_NCX_SKIPPED; } else { *str=0; str++; } if(NULL!=getenv("INTERFACE_NAME_PREFIX")) { char* prefix=getenv("INTERFACE_NAME_PREFIX"); if(strlen(name)obj, "ietf-interfaces", "interface"); assert(interface_obj != NULL); interface_val = val_new_value(); if (interface_val == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(interface_val, interface_obj); val_add_child(interface_val, interfaces_val); /* interface/name */ name_obj = obj_find_child(interface_obj, "ietf-interfaces", "name"); assert(name_obj != NULL); name_val = val_new_value(); if (name_val == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(name_val, name_obj); res = val_set_simval_obj(name_val, name_obj, name); val_add_child(name_val, interface_val); res = val_gen_index_chain(interface_obj, interface_val); assert(res == NO_ERR); /* interface/oper-state */ oper_status_obj = obj_find_child(interface_obj, "ietf-interfaces", "oper-status"); oper_status_val = val_new_value(); assert(oper_status_val); val_init_virtual(oper_status_val, get_oper_status, oper_status_obj); val_add_child(oper_status_val, interface_val); /* interface/last-change */ last_change_obj = obj_find_child(interface_obj, "ietf-interfaces", "last-change"); last_change_val = val_new_value(); assert(last_change_val); val_init_virtual(last_change_val, get_last_change, last_change_obj); val_add_child(last_change_val, interface_val); /* interface/speed */ obj = obj_find_child(interface_obj, "ietf-interfaces", "speed"); val = val_new_value(); assert(val); val_init_virtual(val, get_speed, obj); val_add_child(val, interface_val); /* interface/statistics */ statistics_obj = obj_find_child(interface_obj, "ietf-interfaces", "statistics"); assert(statistics_obj != NULL); statistics_val = val_new_value(); if (statistics_val == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(statistics_val, statistics_obj); val_add_child(statistics_val, interface_val); done = FALSE; for(i=0;i<(sizeof(counter_names_array)/sizeof(char*));i++) { endptr = NULL; counter = strtoull((const char *)str, &endptr, 10); if (counter == 0 && str == endptr) { /* number conversion failed */ log_error("Error: /proc/net/dev number conversion failed."); return ERR_NCX_OPERATION_FAILED; } if(counter_names_array[i]!=NULL) { obj = obj_find_child(statistics_obj, "ietf-interfaces", counter_names_array[i]); assert(obj != NULL); val = val_new_value(); if (val == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(val, obj); VAL_UINT64(val) = counter; val_add_child(val, statistics_val); } str = (xmlChar *)endptr; if (*str == '\0' || *str == '\n') { break; } } /* Add ipv4 container */ ipv4_obj = obj_find_child(interface_obj, "ietf-ip", "ipv4"); assert(ipv4_obj != NULL); ipv4_val = val_new_value(); if (ipv4_val == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(ipv4_val, ipv4_obj); val_init_virtual(ipv4_val, get_ipv4, ipv4_obj); val_add_child(ipv4_val, interface_val); /* Add phys-address leaf */ obj = obj_find_child(interface_obj, "ietf-interfaces", "phys-address"); assert(obj != NULL); val = val_new_value(); if (val == NULL) { return ERR_INTERNAL_MEM; } val_init_from_template(val, obj); val_init_virtual(val, get_phys_address, obj); val_add_child(val, interface_val); return res; } /* Registered callback functions: get_interfaces */ static status_t get_interfaces(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { FILE* f; status_t res; res = NO_ERR; boolean done; char* buf; unsigned int line; /* open /proc/net/dev for reading */ f = fopen("/proc/net/dev", "r"); if (f == NULL) { return ERR_INTERNAL_VAL; } /* get a file read line buffer */ buf = (char*)malloc(NCX_MAX_LINELEN); if (buf == NULL) { fclose(f); return ERR_INTERNAL_MEM; } done = FALSE; line = 0; while (!done) { if (NULL == fgets((char *)buf, NCX_MAX_LINELEN, f)) { done = TRUE; continue; } else { line++; } if (line < 3) { /* skip the first 2 lines */ continue; } res = add_interface_entry(buf, dst_val); if (res != NO_ERR) { done = TRUE; } } fclose(f); free(buf); return res; } int my_timer_fn(uint32 timer_id, void *cookie) { /* * Brute force method for polling for connection state changes * without this link-up and link-down notifications will be * generated only when someone reads oper-state */ val_value_t* root_system_val; val_value_t* interfaces_val = cookie; xmlChar* dummy_serialized_data_str; status_t res; res = NO_ERR; /* by serializing the value all virtual node callbacks are periodically executed */ res = val_make_serialized_string(interfaces_val, NCX_DISPLAY_MODE_JSON, &dummy_serialized_data_str); free(dummy_serialized_data_str); return 0; } static status_t init2_w_nmda(void) { } static void interface_delete(val_value_t* interface_val) { int ret; int n; char* cmd_buf; val_value_t* name_val; name_val = val_find_child(interface_val,"ietf-interfaces","name"); assert(name_val); n = snprintf(NULL, 0, "ifconfig %s down", VAL_STRING(name_val)); assert(n>0); cmd_buf=malloc(n+1); snprintf(cmd_buf, n+1, "ifconfig %s down", VAL_STRING(name_val)); log_info("Interface down: %s\n", cmd_buf); ret=system(cmd_buf); //assert(ret==0); if(ret!=0) { perror(cmd_buf); } free(cmd_buf); } static void interface_create(val_value_t* interface_val) { int ret; int n; char* cmd_buf; val_value_t* name_val; name_val = val_find_child(interface_val,"ietf-interfaces","name"); assert(name_val); n = snprintf(NULL, 0, "ifconfig %s up", VAL_STRING(name_val)); assert(n>0); cmd_buf=malloc(n+1); snprintf(cmd_buf, n+1, "ifconfig %s up", VAL_STRING(name_val)); log_info("Interface up: %s\n", cmd_buf); ret=system(cmd_buf); //assert(ret==0); if(ret!=0) { perror(cmd_buf); } free(cmd_buf); } static int update_config(val_value_t* config_cur_val, val_value_t* config_new_val) { status_t res; val_value_t *interfaces_cur_val, *interface_cur_val; val_value_t *interfaces_new_val, *interface_new_val; if(config_new_val == NULL) { interfaces_new_val = NULL; } else { interfaces_new_val = val_find_child(config_new_val, "ietf-interfaces", "interfaces"); } if(config_cur_val == NULL) { interfaces_cur_val = NULL; } else { interfaces_cur_val = val_find_child(config_cur_val, "ietf-interfaces", "interfaces"); } /* 2 step (delete/add) interface configuration */ /* 1. deactivation loop - deletes all deleted interface -s */ if(interfaces_cur_val!=NULL) { for (interface_cur_val = val_get_first_child(interfaces_cur_val); interface_cur_val != NULL; interface_cur_val = val_get_next_child(interface_cur_val)) { interface_new_val = val123_find_match(config_new_val, interface_cur_val); if(interface_new_val==NULL) { interface_delete(interface_cur_val); } } } /* 2. activation loop - creates all new interface -s */ if(interfaces_new_val!=NULL) { for (interface_new_val = val_get_first_child(interfaces_new_val); interface_new_val != NULL; interface_new_val = val_get_next_child(interface_new_val)) { interface_cur_val = val123_find_match(config_cur_val, interface_new_val); if(interface_cur_val==NULL) { interface_create(interface_new_val); } } } return NO_ERR; } static val_value_t* prev_root_val = NULL; static int update_config_wrapper() { cfg_template_t *runningcfg; status_t res; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg!=NULL && runningcfg->root!=NULL); if(prev_root_val!=NULL) { val_value_t* cur_root_val; cur_root_val = val_clone_config_data(runningcfg->root, &res); if(0==val_compare(cur_root_val,prev_root_val)) { /*no change*/ val_free_value(cur_root_val); return 0; } val_free_value(cur_root_val); } update_config(prev_root_val, runningcfg->root); if(prev_root_val!=NULL) { val_free_value(prev_root_val); } prev_root_val = val_clone_config_data(runningcfg->root, &res); return 0; } static status_t y_commit_complete(void) { update_config_wrapper(); return NO_ERR; } /* The 3 mandatory callback functions: y_ietf_interfaces_init, y_ietf_interfaces_init2, y_ietf_interfaces_cleanup */ status_t y_ietf_interfaces_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; ncx_module_t *mod; status_t res; val_value_t *clivalset; /* check for --with-nmda=true (param defined in netconfd-ex.yang) */ clivalset = agt_cli_get_valset(); with_nmda_param_val = val_find_child(clivalset, "netconfd-ex", "with-nmda"); agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-interfaces", NULL, &agt_profile->agt_savedevQ, &mod); assert(res == NO_ERR); if(with_nmda_param_val && VAL_BOOL(with_nmda_param_val)) { assert(0==strcmp(mod->version,"2018-02-20")); } else { assert(0==strcmp(mod->version,"2014-05-08")); } res = ncxmod_load_module( "iana-if-type", NULL, &agt_profile->agt_savedevQ, &mod); assert(res == NO_ERR); res = ncxmod_load_module( "ietf-ip", NULL, &agt_profile->agt_savedevQ, &mod); assert(res == NO_ERR); res = ncxmod_load_module( "interfaces-notifications", NULL, &agt_profile->agt_savedevQ, &mod); assert(res == NO_ERR); res=agt_commit_complete_register("ietf-interfaces" /*SIL id string*/, y_commit_complete); assert(res == NO_ERR); return res; } status_t y_ietf_interfaces_init2(void) { status_t res; ncx_module_t* mod; obj_template_t* interfaces_obj; val_value_t* interfaces_val; val_value_t* root_val; res = NO_ERR; mod = ncx_find_module("ietf-interfaces", NULL); assert(mod); if(with_nmda_param_val && VAL_BOOL(with_nmda_param_val)) { root_val = agt_nmda_get_root_system(); assert(root_val); interfaces_obj = ncx_find_object( mod, "interfaces"); assert(interfaces_obj); interfaces_val = val_find_child(root_val, "ietf-interfaces", "interfaces"); } else { cfg_template_t* runningcfg; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg && runningcfg->root); root_val = runningcfg->root; interfaces_obj = ncx_find_object( mod, "interfaces-state"); assert(interfaces_obj); interfaces_val = val_find_child(root_val, "ietf-interfaces", "interfaces-state"); } /* not designed to coexist with other implementations */ assert(interfaces_val==NULL); interfaces_val = val_new_value(); assert(interfaces_val); val_init_virtual(interfaces_val, get_interfaces, interfaces_obj); val_add_child(interfaces_val, root_val); /* init a root value to store copies of prev state data values */ root_prev_val = val_new_value(); val_init_from_template(root_prev_val, root_val->obj); res = agt_timer_create(1/* 1 sec period */, TRUE/*periodic*/, my_timer_fn, interfaces_val/*cookie*/, &timer_id); y_commit_complete(); return res; } void y_ietf_interfaces_cleanup (void) { #if 0 agt_cb_unregister_callbacks( "ietf-interfaces", (const xmlChar *)"/interfaces/interface"); agt_cb_unregister_callbacks( "ietf-interfaces", (const xmlChar *)"/interfaces/interface/enable"); #endif agt_timer_delete(timer_id); } yuma123_2.14/example-modules/ietf-interfaces/configure.ac0000664000175000017500000000171214770023131023563 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-ietf-interfaces], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL AC_ARG_ENABLE([standalone], [ --enable-standalone Turn on standalone build], [case "${enableval}" in yes) standalone=true ;; no) standalone=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-standalone]) ;; esac],[standalone=true]) AM_CONDITIONAL([STANDALONE], [test x$standalone = xtrue]) netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) examples_yangdir="$prefix/share/yuma/modules/examples" AC_SUBST(examples_yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/ietf-ip/0000775000175000017500000000000014770023131017561 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-ip/configure.ac0000664000175000017500000000115614770023131022052 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-ietf-ip], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules" AC_SUBST(yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/helloworld/0000775000175000017500000000000014770023131020377 5ustar vladimirvladimiryuma123_2.14/example-modules/helloworld/README0000664000175000017500000000143514770023131021262 0ustar vladimirvladimir==Standalone project for netconfd module implementing helloworld.yang== Dependency: installed netconfd run-time binaries and development shared libraries and headers. ===Build and install=== autoreconf -i -f ./configure CFLAGS="-g -O0" CXXFLAGS="-g -O0" --prefix=/usr make sudo make install ===Running=== Terminal 1: $ /usr/sbin/netconfd --module=helloworld --no-startup Terminal 2: $ yangcli --server=localhot --user=${USER} yangcli vladimir@localhost> create /helloworld/message value="Hello World!" RPC OK Reply 2 for session 1: yangcli vladimir@localhost> commit RPC OK Reply 3 for session 1: yangcli vladimir@localhost> xget /helloworld/message RPC Data Reply 4 for session 1: rpc-reply { data { helloworld { message 'Hello World!' } } } yuma123_2.14/example-modules/helloworld/helloworld.c0000664000175000017500000000465314770023131022726 0ustar vladimirvladimir/* module helloworld namespace http://helloworld.com/ns/helloworld */ #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include #include /* module static variables */ static ncx_module_t *helloworld_mod; status_t y_helloworld_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "helloworld", NULL, &agt_profile->agt_savedevQ, &helloworld_mod); if (res != NO_ERR) { return res; } /* put your module initialization code here */ return res; } /* y_helloworld_init */ status_t y_helloworld_init2 (void) { status_t res; cfg_template_t* runningcfg; obj_template_t* helloworld_state_obj; obj_template_t* message_obj; val_value_t* helloworld_state_val; val_value_t* message_val; res = NO_ERR; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (!runningcfg || !runningcfg->root) { return SET_ERROR(ERR_INTERNAL_VAL); } helloworld_state_obj = ncx_find_object( helloworld_mod, "helloworld-state"); if (helloworld_state_obj == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } helloworld_state_val = val_new_value(); if (helloworld_state_val == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } val_init_from_template(helloworld_state_val, helloworld_state_obj); val_add_child(helloworld_state_val, runningcfg->root); message_obj = obj_find_child(helloworld_state_obj, "helloworld", "message"); if (message_obj == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } message_val = val_new_value(); if (message_val == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } val_init_from_template(message_val,message_obj); res = val_set_simval_obj(message_val,message_val->obj, "Hello World!"); val_add_child(message_val, helloworld_state_val); return res; } void y_helloworld_cleanup (void) { /* put your cleanup code here */ } yuma123_2.14/example-modules/helloworld/Makefile.am0000775000175000017500000000113414770023131022435 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libhelloworld.la libhelloworld_la_SOURCES = helloworld.c if STANDALONE libhelloworld_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libhelloworld_la_LDFLAGS = -module -lyumaagt -lyumancx else libhelloworld_la_CPPFLAGS = -I $(top_srcdir)/netconf/src/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform libhelloworld_la_LDFLAGS = -module $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la endif dist_examples_yang_DATA = helloworld.yang yuma123_2.14/example-modules/helloworld/helloworld.yang0000664000175000017500000000141314770023131023431 0ustar vladimirvladimirmodule helloworld { namespace "http://helloworld.com/ns/helloworld"; prefix "helloworld"; organization "helloworld organization"; description "helloworld module"; revision 2016-09-15 { description "Added config false; /helloworld-state"; } revision 2013-04-02 { description "Initial revision"; } container helloworld-state { config false; description "Helloworld example for creating YANG-netconfd SIL modules"; leaf message { type string; } } container helloworld { description "Helloworld example for creating YANG-netconfd SIL modules"; leaf message { type string; } } } yuma123_2.14/example-modules/helloworld/configure.ac0000664000175000017500000000177514770023131022677 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-helloworld], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL AC_ARG_ENABLE([standalone], [ --enable-standalone Turn on standalone build], [case "${enableval}" in yes) standalone=true ;; no) standalone=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-standalone]) ;; esac],[standalone=true]) AM_CONDITIONAL([STANDALONE], [test x$standalone = xtrue]) netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules" AC_SUBST(yangdir) examples_yangdir="$prefix/share/yuma/modules/examples" AC_SUBST(examples_yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/netconfd-instance-manager/0000775000175000017500000000000014770023131023236 5ustar vladimirvladimiryuma123_2.14/example-modules/netconfd-instance-manager/yuma123-services-netconfd.yang0000664000175000017500000000215214770023131030736 0ustar vladimirvladimirmodule yuma123-services-netconfd { yang-version 1.1; namespace "urn:yuma123:params:xml:ns:yang:services-netconfd"; prefix srvc-netconfd; import yuma-ncx { prefix ncx; } import yuma-types { prefix nt; } import yuma-app-common { prefix ncxapp; } import ietf-inet-types { prefix inet; } import yuma123-services { prefix srvc; } revision 2019-12-28 { description "Initial revision."; } /* * Identities */ identity netconf { base srvc:service-type; } identity netconfd { base netconf; } augment "/srvc:services/srvc:service" { when "derived-from-or-self(../srvc:type, 'srvc-netconfd:netconfd')"; container parameters { leaf port { description "Specifies the TCP ports that the server will accept connections from."; type inet:port-number; mandatory true; } choice model-spec { case cli { uses ncxapp:ModuleParm; uses ncxapp:DeviationParm; uses ncxapp:CommonFeatureParms; } } } } } yuma123_2.14/example-modules/netconfd-instance-manager/netconfd-instance-manager.c0000664000175000017500000002721414770023131030422 0ustar vladimirvladimir/* module netconfd-instance-manager */ #define __USE_XOPEN 1 //#define _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_commit_complete.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "val.h" #include "val123.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #define SERVICES_MOD "yuma123-services" #define SERVICES_NETCONFD_MOD "yuma123-services-netconfd" void sshd_for_netconfd_start(unsigned int port) { FILE* f; int ret; char filename[]="/var/run/netconfd-ssh/config-65536"; char cmd_buf[]="/usr/sbin/sshd -f /var/run/netconfd-ssh/config-65536 &"; sprintf(filename, "/var/run/netconfd-ssh/config-%u",port); f=fopen(filename,"w"); assert(f!=NULL); fprintf(f,"PidFile /var/run/netconfd-ssh/%u.pid\n" "ChallengeResponseAuthentication no\n" "UsePAM yes\n" "AcceptEnv LANG LC_*\n" "PermitRootLogin yes\n" "Port %u\n" "Subsystem netconf \"/usr/sbin/netconf-subsystem --ncxserver-sockname=%u@/var/run/netconfd/ncxserver-%u.sock\"\n" "#ForceCommand /var/run/netconfd/netconf-subsystem-%u.sh\n" "#LogLevel DEBUG3\n" "#SyslogFacility\n" ,port,port,port,port,port); fclose(f); sprintf(cmd_buf, "/usr/sbin/sshd -f %s &",filename); ret=system(cmd_buf); assert(ret==0); } /* The returned value is the strlen of string. If cli_args_str is NULL the function only calculates the length. */ static unsigned int container_to_cli_args_str(val_value_t* container_val, char* cli_args_str, unsigned int* str_len) { val_value_t* val; char* val_str; unsigned int len=0; for (val = val_get_first_child(container_val); val != NULL; val = val_get_next_child(val)) { val_str = val_make_sprintf_string(val); len += snprintf(cli_args_str?cli_args_str+len:NULL, cli_args_str?(*str_len-len):0, "--%s=%s ", obj_get_name(val->obj), val_str); free(val_str); } *str_len=len; return len; } static void setup_env_vars(val_value_t* service_val) { val_value_t* environment_variables_val; val_value_t* val; int ret; environment_variables_val = val_find_child(service_val,SERVICES_MOD,"environment-variables"); if(environment_variables_val==NULL) { return; } for (val = val_get_first_child(environment_variables_val); val != NULL; val = val_get_next_child(val)) { val_value_t* name_val; val_value_t* value_val; name_val = val_find_child(val,SERVICES_MOD,"name"); assert(name_val!=NULL); value_val = val_find_child(val,SERVICES_MOD,"value"); assert(value_val!=NULL); ret=setenv(VAL_STRING(name_val), VAL_STRING(value_val), TRUE /*overwrite*/); assert(ret==0); } } /* free the malloc-ed buffer when done using it */ char* generate_netconfd_cmd(val_value_t* service_val) { char* buf; unsigned int header_len; unsigned int len; val_value_t* parameters_val; val_value_t* port_val; parameters_val = val_find_child(service_val,SERVICES_NETCONFD_MOD,"parameters"); port_val = val_find_child(parameters_val,SERVICES_NETCONFD_MOD,"port"); header_len = snprintf(NULL, 0, "/usr/sbin/netconfd --startup=/var/lib/netconfd/startup-cfg-%u.xml --ncxserver-sockname=/var/run/netconfd/ncxserver-%u.sock ", (unsigned int)VAL_UINT16(port_val), (unsigned int)VAL_UINT16(port_val)); container_to_cli_args_str(parameters_val, NULL, &len); buf=malloc(header_len+len+1); header_len = snprintf(buf, header_len+1, "/usr/sbin/netconfd --startup=/var/lib/netconfd/startup-cfg-%u.xml --ncxserver-sockname=/var/run/netconfd/ncxserver-%u.sock ", (unsigned int)VAL_UINT16(port_val), (unsigned int)VAL_UINT16(port_val)); container_to_cli_args_str(parameters_val, buf+header_len, &len); return buf; } void service_add(val_value_t* service_new_val) { char cmd_buf[1024]; char* buf; char* background_cmd_buf; val_value_t* parameters_val; val_value_t* port_val; int res; unsigned int len; parameters_val = val_find_child(service_new_val,SERVICES_NETCONFD_MOD,"parameters"); port_val = val_find_child(parameters_val,SERVICES_NETCONFD_MOD,"port"); sprintf(cmd_buf, "rm /var/run/netconfd/ncxserver-%u.pid",(unsigned int)VAL_UINT16(port_val)); system(cmd_buf); sprintf(cmd_buf, "rm /var/run/netconfd/ncxserver-%u.sock",(unsigned int)VAL_UINT16(port_val)); system(cmd_buf); // sprintf(cmd_buf, "rm /var/lib/netconfd/startup-cfg-%u.xml", (unsigned int)VAL_UINT16(port_val)); // system(cmd_buf); val_dump_value(service_new_val,NCX_DEF_INDENT); buf = generate_netconfd_cmd(service_new_val); len = snprintf(NULL, 0, "%s &", buf); background_cmd_buf=malloc(len+1); snprintf(background_cmd_buf, len+1, "%s &", buf); { int status; pid_t child_pid; if((child_pid = fork()) == 0) { /* child */ setup_env_vars(service_new_val); res=system(background_cmd_buf); exit(res); } else { /* parent */ while (child_pid != wait(&status)) { if(child_pid==-1) { assert(0); } } assert(status==0); } } free(background_cmd_buf); free(buf); sshd_for_netconfd_start(VAL_UINT16(port_val)); } void service_delete(val_value_t* service_cur_val) { char* buf; char* pkill_cmd_buf; unsigned int len; int res; buf = generate_netconfd_cmd(service_cur_val); len = snprintf(NULL, 0, "pkill -x -f '%s'", buf); pkill_cmd_buf=malloc(len+1); snprintf(pkill_cmd_buf, len+1, "pkill -x -f '%s'", buf); res=system(pkill_cmd_buf); free(pkill_cmd_buf); free(buf); } static status_t get_service_state(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { int ret; status_t res; res = NO_ERR; int retval; obj_template_t* is_running_obj; val_value_t* is_running_val; unsigned int len; char* pgrep_cmd_buf; char* buf; res = NO_ERR; is_running_obj = obj_find_child(dst_val->obj, "yuma123-services", "is-running"); assert(is_running_obj); is_running_val=val_new_value(); assert(is_running_val); val_init_from_template(is_running_val, is_running_obj); buf = generate_netconfd_cmd(dst_val->parent); len = snprintf(NULL, 0, "pgrep -x -f '%s'", buf); pgrep_cmd_buf=malloc(len+1); snprintf(pgrep_cmd_buf, len+1, "pgrep -x -f '%s'", buf); retval=system(pgrep_cmd_buf); free(pgrep_cmd_buf); free(buf); VAL_BOOL(is_running_val)=(retval==0)?TRUE:FALSE; val_add_child(is_running_val, dst_val); return res; } static val_value_t* prev_root_config_val=NULL; static int update_config(val_value_t* config_cur_val, val_value_t* config_new_val) { status_t res; val_value_t *services_cur_val, *service_cur_val; val_value_t *services_new_val, *service_new_val; if(config_new_val == NULL) { services_new_val = NULL; } else { services_new_val = val_find_child(config_new_val, SERVICES_MOD, "services"); } if(config_cur_val == NULL) { services_cur_val = NULL; } else { services_cur_val = val_find_child(config_cur_val, SERVICES_MOD, "services"); } /* 2 step (delete/add) service configuration */ /* 1. deactivation loop - deletes all deleted or modified services */ if(services_cur_val!=NULL) { for (service_cur_val = val_get_first_child(services_cur_val); service_cur_val != NULL; service_cur_val = val_get_next_child(service_cur_val)) { service_new_val = val123_find_match(config_new_val, service_cur_val); if(service_new_val==NULL || 0!=val_compare_ex(service_cur_val,service_new_val,TRUE)) { service_delete(service_cur_val); } } } /* 2. activation loop - adds all new or modified services */ if(services_new_val!=NULL) { for (service_new_val = val_get_first_child(services_new_val); service_new_val != NULL; service_new_val = val_get_next_child(service_new_val)) { service_cur_val = val123_find_match(config_cur_val, service_new_val); if(service_cur_val==NULL || 0!=val_compare_ex(service_new_val,service_cur_val,TRUE)) { service_add(service_new_val); /* register state */ obj_template_t* service_state_obj; val_value_t* service_state_val; service_state_obj = obj_find_child(service_new_val->obj, "yuma123-services", "state"); assert(service_state_obj); service_state_val = val_new_value(); assert(service_state_val); val_init_virtual(service_state_val, get_service_state, service_state_obj); val_add_child(service_state_val, service_new_val); } } } return NO_ERR; } static val_value_t* prev_root_val = NULL; static int update_config_wrapper() { cfg_template_t *runningcfg; status_t res; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg!=NULL && runningcfg->root!=NULL); if(prev_root_val!=NULL) { val_value_t* cur_root_val; cur_root_val = val_clone_config_data(runningcfg->root, &res); if(0==val_compare(cur_root_val,prev_root_val)) { /*no change*/ val_free_value(cur_root_val); return 0; } val_free_value(cur_root_val); } update_config(prev_root_val, runningcfg->root); if(prev_root_val!=NULL) { val_free_value(prev_root_val); } prev_root_val = val_clone_config_data(runningcfg->root, &res); return 0; } status_t netconfd_instance_manager_commit_complete_cb(void) { update_config_wrapper(); return NO_ERR; } /* The 3 mandatory callback functions: y_netconfd_instance_manager_init, y_neconfd_instance_manager_init2, y_neconfd_instance_manager_cleanup */ status_t y_netconfd_instance_manager_init ( const xmlChar *modname, const xmlChar *revision) { status_t res; agt_profile_t* agt_profile; ncx_module_t * mod; int ret; agt_profile = agt_get_profile(); res = ncxmod_load_module( SERVICES_MOD, NULL, &agt_profile->agt_savedevQ, &mod); assert(res == NO_ERR); res = ncxmod_load_module( SERVICES_NETCONFD_MOD, NULL, &agt_profile->agt_savedevQ, &mod); assert(res == NO_ERR); res=agt_commit_complete_register("external-handler", netconfd_instance_manager_commit_complete_cb); assert(res == NO_ERR); ret=system("mkdir -p /var/run/netconfd"); assert(ret==0); system("mkdir -p /var/run/netconfd-ssh"); assert(ret==0); return NO_ERR; } status_t y_netconfd_instance_manager_init2(void) { netconfd_instance_manager_commit_complete_cb(); return NO_ERR; } void y_netconfd_instance_manager_cleanup(void) { } yuma123_2.14/example-modules/netconfd-instance-manager/Makefile.am0000775000175000017500000000063114770023131025275 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libnetconfd-instance-manager.la libnetconfd_instance_manager_la_SOURCES = \ netconfd-instance-manager.c libnetconfd_instance_manager_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libnetconfd_instance_manager_la_LDFLAGS = -module -lyumaagt -lyumancx dist_yuma123_yang_DATA = yuma123-services.yang \ yuma123-services-netconfd.yang yuma123_2.14/example-modules/netconfd-instance-manager/configure.ac0000664000175000017500000000130014770023131025516 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-instance-manager], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules" AC_SUBST(yangdir) yuma123_yangdir="$prefix/share/yuma/modules/yuma123" AC_SUBST(yuma123_yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/netconfd-instance-manager/yuma123-services.yang0000664000175000017500000000403114770023131027136 0ustar vladimirvladimirmodule yuma123-services { namespace "urn:yuma123:params:xml:ns:yang:services"; prefix srvc; revision 2019-12-30 { description "Initial revision."; } /* * Identities */ identity service-type { description "Base identity from which specific service types are derived."; } container services { description "Service parameters."; list service { key "name"; description "The list of bridge ports on the device."; leaf name { type string; } leaf type { type identityref { base service-type; } mandatory true; description "The type of the service. When a service entry is created, a server MAY initialize the type leaf with a valid value, e.g., if it is possible to derive the type from the name of the service. If a client tries to set the type of a service to a value that can never be used by the system, e.g., if the type is not supported or if the type does not match the name of the service, the server MUST reject the request. A NETCONF server MUST reply with an rpc-error with the error-tag 'invalid-value' in this case."; } container environment-variables { description "Some SIL modules require configuration through environment variables in addition to the configuration data of the YANG modules implemented."; list var { key "name"; leaf name { type string; } leaf value { type string; } } } container state { config false; leaf is-running { type boolean; } } } } typedef sevice-ref { type leafref { path "/srvc:services/srvc:service/srvc:name"; } description "This type is used by data models that need to reference configured services."; } } yuma123_2.14/example-modules/external-handler/0000775000175000017500000000000014770023131021461 5ustar vladimirvladimiryuma123_2.14/example-modules/external-handler/my_get_prog0000775000175000017500000000017414770023131023724 0ustar vladimirvladimir#!/bin/sh echo my_get_prog $@ >&2 echo 'up' yuma123_2.14/example-modules/external-handler/README0000664000175000017500000000165314770023131022346 0ustar vladimirvladimirThis is a module that allows external programs to handle: - transactional configuration changes - status container read requests Usage: export COMMIT_PROG=./my_commit_prog.py export GET_PROG=./my_get_prog export GET_CB_SCHEMA_MAP=my_get_cb_schema_map.txt netconfd --module=external-handler --no-startup --superuser=${USER} Instead of a custom callback a general interface based on external command invocation is implemented. Configuration transactions: commit-prog --before=.xml --before=.xml Operational data read transaction: get-prog --instance-identifier=/interface-state/interface[name='ge0'] The registration schema points for the program-stat are specified in a text file. Each line is an unique absolute-schema-nodeid. get-cb-schema-map.txt: ... /interfaces-state/interface/oper-status ... The two programs and the text file are specified as environment variables. yuma123_2.14/example-modules/external-handler/my_commit_prog.py0000775000175000017500000000062114770023131025061 0ustar vladimirvladimir#!/usr/bin/python import argparse parser = argparse.ArgumentParser() parser.add_argument("--before", help="Configuration before the transaction.", required=True) parser.add_argument("--after", help="Configuration after the transaction.", required=True) args = parser.parse_args() print("Before:") f=open(args.before,"r") print((f.read())) print("After:") f=open(args.after,"r") print((f.read())) yuma123_2.14/example-modules/external-handler/external-handler.c0000664000175000017500000000751114770023131025066 0ustar vladimirvladimir/* module external-handler */ #define __USE_XOPEN 1 //#define _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_commit_complete.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" static val_value_t* prev_root_config_val=NULL; static unsigned int transaction_id=0; static char* commit_prog; static char* get_prog; static char* get_cb_schema_map; void string_to_file(char* str, char* filename) { FILE* fp; int res; fp = fopen(filename, "w"); assert(fp!=NULL); res=fprintf(fp,str); assert(res==strlen(str)); fclose(fp); } #define EMPTY_CONFIG "\n" void my_transaction_handler(unsigned int transaction_id, val_value_t* prev_root_config_val, val_value_t* cur_root_config_val) { status_t res; xmlChar* before; xmlChar* after; char* cmd_buf; size_t needed; if(prev_root_config_val!=NULL) { res=val_make_serialized_string(prev_root_config_val, NCX_DISPLAY_MODE_XML, (xmlChar **)&before); assert(res=NO_ERR); } else { before=malloc(strlen(EMPTY_CONFIG)+1); strcpy(before,EMPTY_CONFIG); } res=val_make_serialized_string(cur_root_config_val, NCX_DISPLAY_MODE_XML, (xmlChar **)&after); assert(res==NO_ERR); string_to_file(before,"/tmp/before.xml"); string_to_file(after,"/tmp/after.xml"); needed=snprintf(NULL,0,"%s --before=/tmp/before.xml --after=/tmp/after.xml", commit_prog); cmd_buf = malloc(needed+1); snprintf(cmd_buf, needed+1, "%s --before=/tmp/before.xml --after=/tmp/after.xml", commit_prog); res=system(cmd_buf); assert(res==0); free(before); free(after); } status_t external_handler_commit_complete_cb(void) { cfg_template_t* runningcfg; status_t res; val_value_t* cur_root_config_val; printf("in external_handler_commit_complete_cb\n"); runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg!=NULL && runningcfg->root!=NULL); cur_root_config_val = val_clone_config_data(runningcfg->root, &res); assert(res==NO_ERR); val_dump_value(cur_root_config_val, NCX_DEF_INDENT); printf("\nTransaction id=%u", transaction_id); printf("\nBefore:"); if(prev_root_config_val==NULL) { printf("\nNone."); } else { val_dump_value(prev_root_config_val, NCX_DEF_INDENT); } printf("\nAfter:"); val_dump_value(cur_root_config_val, NCX_DEF_INDENT); my_transaction_handler(transaction_id, prev_root_config_val, cur_root_config_val); if(prev_root_config_val!=NULL) { val_free_value(prev_root_config_val); } prev_root_config_val = cur_root_config_val; transaction_id++; return NO_ERR; } /* The 3 mandatory callback functions: y_external_handler_init, y_external_handler_init2, y_external_handler_cleanup */ status_t y_external_handler_init ( const xmlChar *modname, const xmlChar *revision) { status_t res; res=agt_commit_complete_register("external-handler", external_handler_commit_complete_cb); assert(res == NO_ERR); commit_prog=getenv("COMMIT_PROG"); assert(commit_prog); get_prog=getenv("GET_PROG"); assert(get_prog); get_cb_schema_map=getenv("GET_CB_SCHEMA_MAP"); assert(get_cb_schema_map); return NO_ERR; } status_t y_external_handler_init2(void) { //TODO: loop through get callback registrations and add virtual nodes in the config false only parent ancestors. return NO_ERR; } void y_external_handler_cleanup(void) { } yuma123_2.14/example-modules/external-handler/Makefile.am0000775000175000017500000000046314770023131023523 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libexternal-handler.la libexternal_handler_la_SOURCES = \ external-handler.c libexternal_handler_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libexternal_handler_la_LDFLAGS = -module -lyumaagt -lyumancx yuma123_2.14/example-modules/external-handler/configure.ac0000664000175000017500000000116714770023131023754 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-external-handler], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules" AC_SUBST(yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/external-handler/my_get_cb_schema_map.txt0000664000175000017500000000005014770023131026322 0ustar vladimirvladimir/interfaces-state/interface/oper-status yuma123_2.14/example-modules/geo-location/0000775000175000017500000000000014770023131020604 5ustar vladimirvladimiryuma123_2.14/example-modules/geo-location/geo-location.yang0000664000175000017500000000065514770023131024052 0ustar vladimirvladimirmodule geo-location { yang-version 1.1; namespace "http://example.com/ns/geo-location"; prefix geol; import ietf-geo-location { prefix geo; } organization "example.com"; description "Container with geo location data."; revision 2021-07-23 { description "Added refine to set config false in /geo-location."; } uses geo:geo-location { refine geo-location { config false; } } } yuma123_2.14/example-modules/geo-location/Makefile.am0000775000175000017500000000057114770023131022646 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libgeo-location.la libgeo_location_la_SOURCES = \ geo-location.c # Standalone mode. Depends on installed libyuma-dev libgeo_location_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libgeo_location_la_LDFLAGS = -module -lyumaagt -lyumancx yang_DATA = geo-location.yang bin_SCRIPTS = get-geo-location yuma123_2.14/example-modules/geo-location/get-geo-location0000775000175000017500000000062414770023131023671 0ustar vladimirvladimir#!/bin/bash #latitude="40.73297" #longitude="-74.007696" latitude=$(gpspipe -w -n 10 |grep lon|tail -n1|cut -d":" -f9|cut -d"," -f1) longitude=$(gpspipe -w -n 10 |grep lon|tail -n1|cut -d":" -f10|cut -d"," -f1) echo -n '' echo -n "$latitude" echo -n '' echo -n "$longitude" echo -n '' yuma123_2.14/example-modules/geo-location/geo-location.c0000664000175000017500000000627414770023131023341 0ustar vladimirvladimir/* module geo-location */ #define _DEFAULT_SOURCE #define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "val.h" #include "val123.h" #include "val_set_cplxval_obj.h" /* module static variables */ static ncx_module_t *geo_location_mod; static obj_template_t* geo_location_obj; #define BUFSIZE 1024 /* Registered callback functions */ static status_t get_geo_location(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res; char* ptr; res = NO_ERR; /* /geo-location */ char *cmd = "get-geo-location"; char buf[BUFSIZE]; FILE *fp; if ((fp = popen(cmd, "r")) == NULL) { printf("Error opening pipe!\n"); assert(0); } ptr = fgets(buf, BUFSIZE, fp); assert(ptr!=NULL); printf("get-geo-location: %s", buf); assert(strlen(buf)); if(pclose(fp)) { printf("Command not found or exited with error status\n"); assert(0); } #if 1 res = val_set_cplxval_obj(dst_val, vir_val->obj, buf); #else res = val_set_cplxval_obj(dst_val, vir_val->obj, "40.73297-74.007696"); #endif /* disable cache */ vir_val->cachetime = 0; return res; } /* The 3 mandatory callback functions: y_geo_location_init, y_geo_location_init2, y_geo_location_cleanup */ status_t y_geo_location_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "geo-location", NULL, &agt_profile->agt_savedevQ, &geo_location_mod); if (res != NO_ERR) { return res; } geo_location_obj = ncx_find_object( geo_location_mod, "geo-location"); if (geo_location_obj == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } return res; } status_t y_geo_location_init2(void) { status_t res; cfg_template_t* runningcfg; val_value_t* geo_location_val; obj_template_t* obj; res = NO_ERR; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (!runningcfg || !runningcfg->root) { return SET_ERROR(ERR_INTERNAL_VAL); } geo_location_val = val_new_value(); assert(geo_location_val != NULL); val_init_virtual(geo_location_val, get_geo_location, geo_location_obj); val_add_child(geo_location_val, runningcfg->root); return res; } void y_geo_location_cleanup (void) { } yuma123_2.14/example-modules/geo-location/configure.ac0000664000175000017500000000117414770023131023075 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-geo-location], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules/examples" AC_SUBST(yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/yangcli-to-rpc/0000775000175000017500000000000014770023131021054 5ustar vladimirvladimiryuma123_2.14/example-modules/yangcli-to-rpc/README0000664000175000017500000000140114770023131021730 0ustar vladimirvladimir==Module returning XML of RPC request specified as yangcli command== Dependency: installed netconfd run-time binaries and development shared libraries and headers. ===Build and install=== autoreconf -i -f ./configure CFLAGS="-g -O0" CXXFLAGS="-g -O0" --prefix=/usr make sudo make install Start: /usr/sbin/netconfd --module=yangcli-to-rpc --no-startup --log-level=debug4 --superuser=${USER} ===Functionality=== yangcli vladimir@localhost> yangcli-to-rpc cmd="xget /system" blah yuma123_2.14/example-modules/yangcli-to-rpc/yangcli-to-rpc.c0000664000175000017500000001535214770023131024056 0ustar vladimirvladimir/* * Copyright (c) 2018 Vladimir Vassilev, All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cli.h" #include "agt_nmda.h" #include "agt_rpc.h" #include "agt_util.h" #include "cfg.h" #include "cli.h" #include "getcb.h" #include "log.h" #include "ncxmod.h" #include "ncxtypes.h" #include "ncx_feature.h" #include "ncx_list.h" #include "rpc.h" #include "rpc_err.h" #include "ses.h" #include "ses_msg.h" #include "status.h" #include "tstamp.h" #include "val.h" #include "val_set_cplxval_obj.h" #include "val123.h" #include "val_util.h" #include "xmlns.h" #include "xml_util.h" #include "xml_val.h" #include "xml_wr.h" #include "yangconst.h" obj_template_t* find_rpc_template(char* rpc_name) { ncx_module_t * mod; obj_template_t* rpc; /* add all modules */ for (mod = ncx_get_first_module(); mod != NULL; mod = ncx_get_next_module(mod)) { if(!mod->implemented) { continue; } rpc = ncx_find_object(mod, rpc_name); if(rpc!=NULL && rpc->objtype==OBJ_TYP_RPC) { break; } } return rpc; } /******************************************************************** * FUNCTION yangcli_to_rpc * * INPUTS: * see agt/agt_rpc.h * RETURNS: * status *********************************************************************/ static status_t yangcli_to_rpc(ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { val_value_t *cmd_val; obj_template_t *output_obj; obj_template_t *output_rpc_obj; val_value_t *output_rpc_val; val_value_t *reqdata; val_value_t *valset; val_value_t *chval; obj_template_t *rpc_obj; obj_template_t *input_obj; val_value_t *rpc_val; char* first_space; char* rpc_name_str; status_t res; char* argv[2]; unsigned int argc; cmd_val = val_find_child(msg->rpc_input, "yuma123-yangcli-to-rpc", "cmd"); assert(cmd_val); printf("yangcli-to-rpc: %s\n",VAL_STRING(cmd_val)); output_obj = obj_find_child( msg->rpc_input->obj->parent, "yuma123-yangcli-to-rpc", "output"); assert(output_obj); output_rpc_obj = obj_find_child( output_obj, "yuma123-yangcli-to-rpc", "rpc"); assert(output_rpc_obj); output_rpc_val = val_new_value(); assert(output_rpc_val); val_init_from_template(output_rpc_val, output_rpc_obj); #if 0 res = val_set_cplxval_obj(output_rpc_val, output_rpc_val->obj, " "); #else first_space=strchr(VAL_STRING(cmd_val), ' '); if(first_space==NULL || first_space==(char*)VAL_STRING(cmd_val)) { rpc_name_str=strdup(VAL_STRING(cmd_val)); argv[0]=rpc_name_str; argv[1]=NULL; argc=1; } else { unsigned int rpc_name_len; rpc_name_len = first_space - (char*)VAL_STRING(cmd_val); rpc_name_str=malloc(rpc_name_len+1); memcpy(rpc_name_str, VAL_STRING(cmd_val), rpc_name_len); rpc_name_str[rpc_name_len]=0; argv[0]=rpc_name_str; argv[1]=first_space+1; argc=2; } rpc_obj=find_rpc_template(argv[0]); if(rpc_obj==NULL) { res = ERR_NCX_INVALID_VALUE; free(argv[0]); return res; } input_obj = obj_find_child(rpc_obj, NULL, YANG_K_INPUT); assert(input_obj); valset = cli_parse (NULL, argc, argv, input_obj, FULLTEST, TRUE/*script*/, TRUE, CLI_MODE_PROGRAM, &res); free(rpc_name_str); if(res!=NO_ERR) { return res; } val_dump_value(valset,1); rpc_val = xml_val_new_struct(obj_get_name(rpc_obj), obj_get_nsid(rpc_obj)); if(rpc_val==NULL) { res = ERR_NCX_INVALID_VALUE; free(rpc_name_str); return res; } for(chval=val_get_first_child(valset); chval!=NULL; chval=val_get_next_child(chval)) { val_value_t *newval; newval = val_clone(chval); val_add_child(newval, rpc_val); } val_free_value(valset); val_add_child(rpc_val, output_rpc_val); #endif dlq_enque(output_rpc_val, &msg->rpc_dataQ); msg->rpc_data_type = RPC_DATA_YANG; return NO_ERR; } /* yangcli_to_rpc */ /******************************************************************** * FUNCTION y_yangcli_to_rpc_init * * INIT 1: * Initialize the module data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t y_yangcli_to_rpc_init (void) { agt_profile_t *agt_profile; status_t res; ncx_module_t *mod; obj_template_t *root_obj; val_value_t* clivalset; val_value_t* val; /* load in the RPC methods */ res = ncxmod_load_module( "yuma123-yangcli-to-rpc", NULL, NULL, NULL ); assert(res == NO_ERR); /* yangcli-to-rpc */ res = agt_rpc_register_method("yuma123-yangcli-to-rpc", "yangcli-to-rpc", AGT_RPC_PH_INVOKE, yangcli_to_rpc); assert(res == NO_ERR); return NO_ERR; } /* y_yangcli_to_rpc_init */ /******************************************************************** * FUNCTION y_yangcli_to_rpc_init2 * * INIT 2: * Initialize the data structures * * INPUTS: * none * RETURNS: * status *********************************************************************/ status_t y_yangcli_to_rpc_init2 (void) { return NO_ERR; } /* y_yangcli_to_rpc_init2 */ /******************************************************************** * FUNCTION y_yangcli_to_rpc_cleanup * * Cleanup the module data structures * * INPUTS: * * RETURNS: * none *********************************************************************/ void y_yangcli_to_rpc_cleanup (void) { agt_rpc_unregister_method("yuma123-yangcli-to-rpc", "yangcli-to-rpc"); } /* y_yangcli_to_rpc_cleanup */ yuma123_2.14/example-modules/yangcli-to-rpc/Makefile.am0000775000175000017500000000134014770023131023111 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libyangcli-to-rpc.la libyangcli_to_rpc_la_SOURCES = \ yangcli-to-rpc.c if STANDALONE # Standalone mode. Depends on installed libyuma-dev libyangcli_to_rpc_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libyangcli_to_rpc_la_LDFLAGS = -module -lyumaagt -lyumancx else # Integrated yuma123 build libyangcli_to_rpc_la_CPPFLAGS = -I $(top_srcdir)/netconf/src/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform libyangcli_to_rpc_la_LDFLAGS = -module $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la endif dist_yuma123_yang_DATA = yuma123-yangcli-to-rpc.yang yuma123_2.14/example-modules/yangcli-to-rpc/configure.ac0000664000175000017500000000170614770023131023346 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-yangcli-to-rpc], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL AC_ARG_ENABLE([standalone], [ --enable-standalone Turn on standalone build], [case "${enableval}" in yes) standalone=true ;; no) standalone=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-standalone]) ;; esac],[standalone=true]) AM_CONDITIONAL([STANDALONE], [test x$standalone = xtrue]) netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yuma123_yangdir="$prefix/share/yuma/modules/yuma123" AC_SUBST(yuma123_yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/yangcli-to-rpc/yuma123-yangcli-to-rpc.yang0000664000175000017500000000156714770023131025774 0ustar vladimirvladimirmodule yuma123-yangcli-to-rpc { namespace "http://yuma123.org/ns/yangcli-to-rpc"; prefix "ycli2rpc"; organization "Yuma123"; contact "Vladimir Vassilev "; description "This module defines rpc that takes yangcli command string as input and returns corresponding RPC request PDU. It enables clients without YANG support to map yangcli commands to RPC PDUs"; revision 2018-10-30 { description "Initial version."; } rpc yangcli-to-rpc { description "This RPC converts yangcli command line to RPC PDU."; input { leaf cmd { description "yangcli command line. Example: create /system -- hostname=foo location=bar"; type string; } } output { anyxml rpc { description "RPC PDU."; } } } } yuma123_2.14/example-modules/ietf-traffic-generator/0000775000175000017500000000000014770023131022553 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-traffic-generator/raw-socket.c0000664000175000017500000000504314770023131025000 0ustar vladimirvladimir#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "raw-socket.h" int raw_socket_init(char* if_name, raw_socket_t* raw_socket) { int rc; int sock; struct sockaddr_ll addr = {0}; struct ifreq ifr = {0}; sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if ( socket < 0 ) { return sock; } raw_socket->socket=sock; strncpy(ifr.ifr_name, if_name, IFNAMSIZ-1); rc = ioctl(sock, SIOCGIFINDEX, &ifr); if ( rc < 0 ) { return rc; } raw_socket->ifindex=ifr.ifr_ifindex; addr.sll_family = AF_PACKET; addr.sll_ifindex = ifr.ifr_ifindex; addr.sll_protocol = htons(ETH_P_ALL); if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) { perror("bind"); assert(0); } memset((void*)&ifr,sizeof(struct ifreq),0); /* promiscuous mode */ rc = ioctl(sock, SIOCGIFFLAGS, &ifr); assert(rc==0); ifr.ifr_flags |= IFF_PROMISC; rc = ioctl(sock, SIOCSIFFLAGS, &ifr); assert(rc==0); return 0; } int raw_socket_send(raw_socket_t* raw_socket, uint8_t* raw_frame, uint32_t raw_frame_len) { struct sockaddr_ll ta; ta.sll_family = PF_PACKET; /* RAW communications */ ta.sll_protocol = 0; /* Not used for sending */ ta.sll_ifindex = raw_socket->ifindex; /* Interface index */ ta.sll_hatype = 0; /* Not used for sending */ ta.sll_pkttype = 0; /* Not used for sending */ ta.sll_halen = 6; /* All MAC addresses are 6 octets in size */ memset(ta.sll_addr, 0, sizeof(ta.sll_addr)); memcpy(ta.sll_addr, &raw_frame[0]/*destination mac*/, 6); int rc = sendto(raw_socket->socket, raw_frame, raw_frame_len - 4 /* without crc */, 0,(struct sockaddr*)&ta, sizeof(ta)); if ( rc < 0 ) { return rc; } return 0; } int raw_socket_receive(raw_socket_t* raw_socket, uint8_t* raw_frame, uint32_t raw_frame_max_len, uint32_t* raw_frame_received_len) { ssize_t res; struct sockaddr_ll saddr; int saddr_len = sizeof (saddr); do { res = recvfrom(raw_socket->socket, raw_frame, raw_frame_max_len, 0, (struct sockaddr *)&saddr, &saddr_len); if(res<=0) { perror("recvfrom"); assert(0); } } while(saddr.sll_pkttype == PACKET_OUTGOING); *raw_frame_received_len = res; return 0; } yuma123_2.14/example-modules/ietf-traffic-generator/dict.c0000664000175000017500000000265214770023131023647 0ustar vladimirvladimir#include #include #include "dlq.h" #include "dict.h" void dict_init(dlq_hdr_t *que) { dlq_createSQue(que); } void dict_clear(dlq_hdr_t *que) { dict_node_t *dn; while (!dlq_empty(que)) { dn = (dict_node_t *)dlq_deque(que); free(dn); } dlq_createSQue(que); } char* dict_get_name(dlq_hdr_t *que, void* data) { dict_node_t *dn; for (dn = (dict_node_t *)dlq_firstEntry(que); dn != NULL; dn = (dict_node_t *)dlq_nextEntry(dn)) { if(dn->data==data) { return dn->name; } } return NULL; } void* dict_get_data(dlq_hdr_t *que, const char* name) { dict_node_t *dn; for (dn = (dict_node_t *)dlq_firstEntry(que); dn != NULL; dn = (dict_node_t *)dlq_nextEntry(dn)) { if(0==strcmp(dn->name, name)) { return dn->data; } } assert(0); } void dict_add(dlq_hdr_t *que, const char* name, void* data) { dict_node_t *dn = malloc(sizeof(dict_node_t)); assert(dn); dn->data=data; dn->name=(char*)name; dlq_enque (dn, que); } void dict_remove(dlq_hdr_t *que, const char* name) { dict_node_t *dn; for (dn = (dict_node_t *)dlq_firstEntry(que); dn != NULL; dn = (dict_node_t *)dlq_nextEntry(dn)) { if(0==strcmp(dn->name,name)) { dlq_remove (dn); free(dn); return; } } assert(0); } yuma123_2.14/example-modules/ietf-traffic-generator/README0000664000175000017500000001313214770023131023433 0ustar vladimirvladimir==Standalone project for netconfd module implementing ietf-traffic-generator.yang== Dependency: installed netconfd run-time binaries and development shared libraries and headers. ===Build and install=== autoreconf -i -f ./configure CFLAGS="-g -O0" CXXFLAGS="-g -O0" --prefix=/usr make sudo make install sudo /usr/sbin/netconfd --module=ietf-interfaces --module=ietf-traffic-generator --module=ietf-traffic-analyzer --module=traffic-generator-testframe --no-startup --log-level=debug4 --superuser=root ===Functionality=== yangcli root@localhost> create /interfaces/interface[name='eth0'] -- type=ethernetCsmacd yangcli root@localhost> create /interfaces/interface[name='eth0']/traffic-generator -- frame-size=64 interframe-gap=67000000 testframe-type=dynamic frame-data=\ 6CA96F0000026CA96F00000108004500\ 002ED4A500000A115816C0000201C000\ 0202C0200007001A0000010203040506\ 0708090A0B0C0D0E0F101112 yangcli root@localhost> create /interfaces/interface[name='eth1'] -- type=ethernetCsmacd yangcli root@localhost> create /interfaces/interface[name='eth1']/traffic-analyzer yangcli root@localhost> commit yangcli root@192.168.0.17> xget /interfaces RPC Data Reply 51 for session 1: rpc-reply { data { interfaces { interface eth0 { name eth0 type ianaift:ethernetCsmacd traffic-generator { frame-size 64 frame-data 6CA96F0000026CA96F00000108004500002ED4A500000A115816C0000201C0000202C0200007001A00000102030405060708090A0B0C0D0E0F101112 interframe-gap 67000000 testframe-type dynamic } } interface eth1 { name eth1 type ianaift:ethernetCsmacd traffic-analyzer { state { pkts 15687 testframe-stats { testframe-pkts 15223 latency { min 54190 max 2084628 latest 218892 } } } } } } } } === realtime-epoch === Generation can be synchronized in realtime using the realtime-epoch leaf. The configured epoch should be in future. Restarting traffic generation on two interfaces with 1s phase difference. Assuming time is 2020-09-29T13:43:59.000000000Z or earlier when you commit. yangcli root@localhost> merge /interfaces/interface[name='eth1']/traffic-generator -- realtime-epoch="2020-09-29T13:44:00.000000000Z" yangcli root@localhost> merge /interfaces/interface[name='eth0']/traffic-generator -- realtime-epoch="2020-09-29T13:44:01.000000000Z" yangcli root@localhost> commit ==Design notes== The libtraffic_generator library creates traffic generator instance context from the ietf-traffic-generator data upon initialization. Then each call fills frame buffer and returns it timestamp and length until after the last frame is generated. Then zero length is returned. The traffic-generator tool is started for each interface with configured /interfaces/interface/traffic-generator container. There is a --stdout-mode option that can be used for a dry run. Usage: $ ./traffic-generator --stdout-mode --interface=eth0 --frame-size=64 --interframe-gap=20 --interburst-gap=124999852 \ --frames-per-burst=2 --total-frames=4 --realtime-epoch="1970-01-01T00:00:00.000000000Z" --interface-speed=1000000000 --frame-data="123456789ABCDEF01234567808004500002E000000000A112CBCC0000201C0000202C0200007001A0000000102030405060708090A0B0C0D0E0F10119CD50E0F" 0 000000000000000:000000000 64 123456789ABCDEF01234567808004500002E000000000A112CBCC0000201C0000202C0200007001A0000000102030405060708090A0B0C0D0E0F10119CD50E0F 1 000000000000000:000000672 64 123456789ABCDEF01234567808004500002E000000000A112CBCC0000201C0000202C0200007001A0000000102030405060708090A0B0C0D0E0F10119CD50E0F 2 000000000000001:000000000 64 123456789ABCDEF01234567808004500002E000000000A112CBCC0000201C0000202C0200007001A0000000102030405060708090A0B0C0D0E0F10119CD50E0F 3 000000000000001:000000672 64 123456789ABCDEF01234567808004500002E000000000A112CBCC0000201C0000202C0200007001A0000000102030405060708090A0B0C0D0E0F10119CD50E0F ==Dynamic testframe - stamped with sequence number and time== Adding the --testframe-type=dynamc according to the model testframes are dynamically stamped with sequence number (8 octets) and IEEE 1588 timestamp (10 octets). These dynamic fields are added at the back of the payload within the specified --frame-size argument: ... 0 000000000000000:000000000 64 123456789ABCDEF01234567808004500002E000000000A112CBCC0000201C0000202C0200007001A00000000000000000000000000000000000000005465E0B4 1 000000000000000:000000672 64 123456789ABCDEF01234567808004500002E000000000A112CBCC0000201C0000202C0200007001A00000000000000000001000000000000000002A0FE7B8E91 2 000000000000001:000000000 64 123456789ABCDEF01234567808004500002E000000000A112CBCC0000201C0000202C0200007001A000000000000000000020000000000010000000025F5ECD1 3 000000000000001:000000672 64 123456789ABCDEF01234567808004500002E000000000A112CBCC0000201C0000202C0200007001A00000000000000000003000000000001000002A08FEB82F4 ===Testframe generation=== While you could use any ethernet frame specified with the --frame-data parameter there is a tool that generates RFC2544 sec C.2.6.4 testframe. Usage: $ ./traffic-generator-make-testframe --frame-size=64 --dst-mac-address="12:34:56:78:9A:BC" --src-mac-address="DE:F0:12:34:56:78" --src-ipv4-address="192.0.2.1" --ipv4-ttl=10 --src-ipv4-udp-port=49184 --dst-ipv4-address="192.0.2.2" --dst-ipv4-udp-port=7 123456789ABCDEF01234567808004500002E000000000A112CBCC0000201C0000202C0200007001A0000000102030405060708090A0B0C0D0E0F10119CD50E0F yuma123_2.14/example-modules/ietf-traffic-generator/libtraffic-analyzer.c0000664000175000017500000000776414770023131026665 0ustar vladimirvladimir#include #include #include #include #include #include "libtraffic-analyzer.h" #include "timespec-math.h" #define DST_IPV4_UDP_PORT_OFFSET 36 static unsigned char hexchar2byte(char hexchar) { char byte; if(hexchar>='0' && hexchar<='9') { byte = hexchar - '0'; } else if (hexchar>='A' && hexchar<='F') { byte = hexchar - 'A' + 10; } else if (hexchar>='a' && hexchar<='f') { byte = hexchar - 'a' + 10; } else { assert(0); } return byte; } static void hexstr2bin(char* hexstr, uint8_t* data) { unsigned int i; unsigned int len; len = strlen(hexstr)/2; for(i=0;itotalframes++; if(!ta->testframe.filter.enabled) { /* no filter specification * default testframe is any IPV4 UDP dstport 7 frame */ if(!(frame_data[DST_IPV4_UDP_PORT_OFFSET]==0 && frame_data[DST_IPV4_UDP_PORT_OFFSET+1]==7)) { return; } } else { /* TODO */ assert(0); } ta->testframes++; seq_num = ((uint64_t)seq_num_ptr[0]<<56) + ((uint64_t)seq_num_ptr[1]<<48) + ((uint64_t)seq_num_ptr[2]<<40) + ((uint64_t)seq_num_ptr[3]<<32) + ((uint64_t)seq_num_ptr[4]<<24) + ((uint64_t)seq_num_ptr[5]<<16) + ((uint64_t)seq_num_ptr[6]<<8) + ((uint64_t)seq_num_ptr[7]<<0); if(ta->testframe.expected_seq_num == seq_num) { ta->testframe.expected_seq_num++; } else { ta->testframe.expected_seq_num = seq_num+1; ta->testframe.sequence_errors++; } memcpy(&ta->testframe.last_rx_time, &ta->last_rx_time, sizeof(struct timespec)); /* TODO - add support for timestamped testframes in the draft for now just expect 10 byte PTP timestamp at the end of the frame */ check_last_tx_time.tv_sec = ((uint64_t)timestamp[0]<<40) + ((uint64_t)timestamp[1]<<32) + ((uint64_t)timestamp[2]<<24) + ((uint64_t)timestamp[3]<<16) + ((uint64_t)timestamp[4]<<8) + ((uint64_t)timestamp[5]); check_last_tx_time.tv_nsec = ((uint32_t)timestamp[6]<<24) + ((uint32_t)timestamp[7]<<16) + ((uint32_t)timestamp[8]<<8) + ((uint32_t)timestamp[9]); ta->last_rx_time.tv_sec = rx_sec; ta->last_rx_time.tv_nsec = rx_nsec; timespec_sub(&ta->last_rx_time, &check_last_tx_time, &check_last_latency); if(check_last_latency.tv_sec!=0) { /* ignore latencies > 1 sec for now */ return; } ta->testframe.latency.samples++; memcpy(&ta->testframe.latency.last, &check_last_latency, sizeof(struct timespec)); memcpy(&ta->testframe.latency.last_tx_time, &check_last_tx_time, sizeof(struct timespec)); memcpy(&ta->testframe.latency.last_rx_time, &ta->last_rx_time, sizeof(struct timespec)); if(ta->testframe.latency.samples<=1 || ta->testframe.latency.last.tv_sec>ta->testframe.latency.max.tv_sec || (ta->testframe.latency.last.tv_sec==ta->testframe.latency.max.tv_sec && ta->testframe.latency.last.tv_nsec>ta->testframe.latency.max.tv_nsec)) { ta->testframe.latency.max.tv_nsec = ta->testframe.latency.last.tv_nsec; ta->testframe.latency.max.tv_sec = ta->testframe.latency.last.tv_sec; } if(ta->testframe.latency.samples<=1 || ta->testframe.latency.last.tv_sectestframe.latency.min.tv_sec || (ta->testframe.latency.last.tv_sec==ta->testframe.latency.min.tv_sec && ta->testframe.latency.last.tv_nsectestframe.latency.min.tv_nsec)) { ta->testframe.latency.min.tv_nsec = ta->testframe.latency.last.tv_nsec; ta->testframe.latency.min.tv_sec = ta->testframe.latency.last.tv_sec; } } yuma123_2.14/example-modules/ietf-traffic-generator/libtraffic-generator.h0000664000175000017500000000300714770023131027015 0ustar vladimirvladimir#include typedef struct burst_t_ { } burst_t; typedef struct stream_t_ { uint32_t frame_size; uint8_t* frame_data; uint32_t interframe_gap; uint32_t frames_per_burst; uint32_t interburst_gap; unsigned int bursts_per_stream; unsigned int burst_index; uint32_t interstream_gap; int testframe_type; int testframe_type_dynamic; } stream_t; typedef struct traffic_generator_t_ { uint64_t total_frames; uint64_t total_frame_index; uint64_t sec; uint32_t nsec; double nsec_fraction; double nsec_per_octet; uint64_t octets_per_sec; stream_t* streams; unsigned int streams_num; unsigned int stream_index; unsigned int burst_index; unsigned int frame_index; } traffic_generator_t; traffic_generator_t* traffic_generator_init(uint64_t interface_speed, char* realtime_epoch, uint32_t frame_size, char* frame_data_hexstr, uint32_t interframe_gap, uint32_t interburst_gap, uint32_t frames_per_burst, uint32_t bursts_per_stream, uint64_t total_frames, char* testframe_type); int traffic_generator_get_frame(traffic_generator_t* tg, uint32_t* frame_length, uint8_t** frame, uint64_t* tx_time_sec, uint32_t* tx_time_nsec); void traffic_generator_set_epoch(traffic_generator_t* tg, uint64_t sec, uint32_t nsec); char* traffic_generator_make_testframe(uint32_t frame_size, char* frame_data_hexstr, char* src_mac_address, char* dst_mac_address, char* src_ipv4_address, char* dst_ipv4_address, char* ipv4_ttl, char* src_ipv4_udp_port, char* dst_ipv4_udp_port); yuma123_2.14/example-modules/ietf-traffic-generator/debian/0000775000175000017500000000000014770023131023775 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-traffic-generator/debian/source/0000775000175000017500000000000014770023131025275 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-traffic-generator/debian/source/format0000664000175000017500000000001414770023131026503 0ustar vladimirvladimir3.0 (quilt) yuma123_2.14/example-modules/ietf-traffic-generator/debian/control0000664000175000017500000000474214770023131025407 0ustar vladimirvladimirSource: yuma123-netconfd-module-ietf-traffic-generator Section: net Priority: optional Maintainer: Vladimir Vassilev Build-Depends: debhelper (>= 10), autoconf, libncurses5-dev, libreadline-dev, libssh2-1-dev, libssl-dev, libtool, libxml2-dev, libyuma-dev (>= 2.14), zlib1g-dev Standards-Version: 4.1.4 Homepage: https://lightside-instruments.com Package: netconfd-module-ietf-traffic-generator Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, debconf, lsb-base (>= 3.2-13), libyuma2 (>= 2.14), traffic-generator-cli Description: SIL module for netconfd implementing ietf-traffic-generator.yang . Implements the functionality modeled in ietf-traffic-generator.yang using the available traffic-generator CLI tool. Package: netconfd-module-ietf-traffic-analyzer Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, debconf, lsb-base (>= 3.2-13), libyuma2 (>= 2.14), traffic-generator-cli Description: SIL module for netconfd implementing ietf-traffic-analyzer.yang . Implements the functionality modeled in ietf-traffic-analyzer.yang using the available traffic-analyzer CLI tool. Package: traffic-generator-cli-rawsocket Architecture: any Provides: traffic-generator-cli Depends: ${shlibs:Depends}, ${misc:Depends}, debconf, lsb-base (>= 3.2-13), traffic-generator-make-testframe Description: CLI tools starting traffic generator stream and a traffic analyzer on local network interface. . This package contains a rawsocket implementation of the traffic-generator-cli interface. It is not as deterministic as alternative hardware based implementations. It also serves as a template and is universally supported on all Linux machines with generic interfaces supporting SOCK_RAW. Package: traffic-analyzer-cli-rawsocket Architecture: any Provides: traffic-analyzer-cli Depends: ${shlibs:Depends}, ${misc:Depends}, debconf, lsb-base (>= 3.2-13) Description: CLI tools starting and monitoring traffic analyzer on local network interface. . This package contains a rawsocket implementation of the traffic-analyzer-cli interface. It is not as deterministic as alternative hardware based implementations. It also serves as a template and is universally supported on all Linux machines with generic interfaces supporting SOCK_RAW. Package: traffic-generator-make-testframe Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: CLI tools printing the hexadecimal data of a traffic generator testframe according to parameters. yuma123_2.14/example-modules/ietf-traffic-generator/debian/traffic-generator-cli-rawsocket.install0000664000175000017500000000003414770023131033531 0ustar vladimirvladimirusr/bin/traffic-generator yuma123_2.14/example-modules/ietf-traffic-generator/debian/traffic-generator-make-testframe.install0000664000175000017500000000005114770023131033666 0ustar vladimirvladimirusr/bin/traffic-generator-make-testframe yuma123_2.14/example-modules/ietf-traffic-generator/debian/rules0000775000175000017500000000071514770023131025060 0ustar vladimirvladimir#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ --with autoreconf ././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootrootyuma123_2.14/example-modules/ietf-traffic-generator/debian/netconfd-module-ietf-traffic-generator.installyuma123_2.14/example-modules/ietf-traffic-generator/debian/netconfd-module-ietf-traffic-generator.in0000664000175000017500000000005614770023131033736 0ustar vladimirvladimirusr/lib/*/yuma/libietf-traffic-generator*.so* ././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootrootyuma123_2.14/example-modules/ietf-traffic-generator/debian/netconfd-module-ietf-traffic-analyzer.installyuma123_2.14/example-modules/ietf-traffic-generator/debian/netconfd-module-ietf-traffic-analyzer.ins0000664000175000017500000000005514770023131033757 0ustar vladimirvladimirusr/lib/*/yuma/libietf-traffic-analyzer*.so* yuma123_2.14/example-modules/ietf-traffic-generator/debian/traffic-analyzer-cli-rawsocket.install0000664000175000017500000000003514770023131033371 0ustar vladimirvladimirusr/bin/traffic-analyzer yuma123_2.14/example-modules/ietf-traffic-generator/debian/copyright0000664000175000017500000000303714770023131025733 0ustar vladimirvladimirFiles: * Copyright: (C) 2019-2025 Vladimir Vassilev License: BSD License: BSD Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andy Bierman BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. yuma123_2.14/example-modules/ietf-traffic-generator/debian/compat0000664000175000017500000000000314770023131025174 0ustar vladimirvladimir10 yuma123_2.14/example-modules/ietf-traffic-generator/debian/changelog0000664000175000017500000000030514770023131025645 0ustar vladimirvladimiryuma123-netconfd-module-ietf-traffic-generator (2.14-0) stable; urgency=medium * Unofficial package -- Vladimir Vassilev Sun, 23 Mar 2025 15:25:36 +0100 yuma123_2.14/example-modules/ietf-traffic-generator/libtraffic-analyzer.h0000664000175000017500000000221114770023131026650 0ustar vladimirvladimir#include typedef struct traffic_analyzer_t_ { uint64_t totalframes; uint64_t testframes; struct timespec last_rx_time; /* testframe stats */ struct testframe_ { uint64_t sequence_errors; uint64_t expected_seq_num; struct filter_ { int enabled; } filter; struct timespec last_rx_time; struct latency_ { uint64_t samples; struct timespec last_tx_time; struct timespec last_rx_time; struct timespec last; struct timespec min; struct timespec max; } latency; } testframe; } traffic_analyzer_t; traffic_analyzer_t* traffic_analyzer_init(uint32_t frame_size, char* frame_data_hexstr, uint32_t interframe_gap, uint32_t interburst_gap, uint32_t frames_per_burst, uint32_t bursts_per_frame, uint64_t total_frames); int traffic_analyzer_get_frame(traffic_analyzer_t* tg, uint32_t* frame_length, uint8_t** frame, uint64_t* tx_time_sec, uint32_t* tx_time_nsec); void traffic_analyzer_put_frame(traffic_analyzer_t* ta, uint8_t* frame_data, uint32_t frame_len, uint64_t rx_sec, uint32_t rx_nsec); yuma123_2.14/example-modules/ietf-traffic-generator/ietf-traffic-analyzer.c0000664000175000017500000002571614770023131027120 0ustar vladimirvladimir/* module ietf-traffic-analyzer implementation for Linux namespace urn:ietf:params:xml:ns:yang:ietf-traffic-analyzer */ #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_commit_complete.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "val.h" #include "val123.h" #include "val_set_cplxval_obj.h" #include "dict.h" #define IF_MOD "ietf-interfaces" #define TA_MOD "ietf-traffic-analyzer" /* module static variables */ static ncx_module_t *ietf_interfaces_mod; static ncx_module_t *iana_if_type_mod; static ncx_module_t *ietf_traffic_analyzer_mod; static dlq_hdr_t io_dict; typedef struct io_t_ { FILE* in; FILE* out; } io_t; static status_t get_if_traffic_analyzer_state(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res; obj_template_t* obj; val_value_t* val; #if 1 val_value_t* name_val; char* state_xml; // = "34"; ssize_t n; io_t* io; name_val = val_find_child(dst_val->parent->parent, "ietf-interfaces", "name"); assert(name_val); io = dict_get_data(&io_dict, VAL_STRING(name_val)); assert(io); fputs("\n",io->out); fflush(io->out); n=0; state_xml=NULL; n = getline(&state_xml, &n, io->in); assert(n > 0 && state_xml != NULL); res = val_set_cplxval_obj(dst_val,dst_val->obj,state_xml); if(res != NO_ERR) { return res; } free(state_xml); #else uint64_t pkts = 33; /* pkts */ obj=obj_find_child(vir_val->obj, TA_MOD, "pkts"); val=val_new_value(); assert(val); val_init_from_template(val, obj); VAL_UINT64(val) = pkts; val_add_child(val, dst_val); #endif return NO_ERR; } static void serialize_params(val_value_t* traffic_analyzer_val, char* cli_args_str) { val_value_t* val; unsigned int i; val = val_find_child(traffic_analyzer_val->parent,"ietf-interfaces","name"); sprintf(cli_args_str,"--interface-name=%s",VAL_STRING(val)); //val = val_find_child(traffic_analyzer_val,"ietf-traffic-analyzer","frame-size"); //sprintf(cli_args_str+strlen(cli_args_str)," --frame-size=%u",VAL_UINT32(val)); } static void traffic_analyzer_delete(val_value_t* traffic_analyzer_val) { char cmd_buf[4096]; static char cmd_args_buf[4096]; val_value_t* name_val; io_t* io; char* name_buf; printf("traffic_analyzer_io_dictdelete:\n"); val_dump_value(traffic_analyzer_val,NCX_DEF_INDENT); name_val = val_find_child(traffic_analyzer_val->parent,"ietf-interfaces","name"); assert(name_val); serialize_params(traffic_analyzer_val, cmd_args_buf); sprintf(cmd_buf, "pkill -f '/bin/sh -c traffic-analyzer %s'", cmd_args_buf); system(cmd_buf); io = dict_get_data(&io_dict, VAL_STRING(name_val)); assert(io); fclose(io->in); fclose(io->out); name_buf = dict_get_name(&io_dict, io); dict_remove(&io_dict, VAL_STRING(name_val)); free(name_buf); free(io); } static void traffic_analyzer_create(val_value_t* traffic_analyzer_val) { char cmd_buf[4096]; static char cmd_args_buf[4096]; val_value_t* name_val; printf("traffic_analyzer_create:\n"); val_dump_value(traffic_analyzer_val,NCX_DEF_INDENT); name_val = val_find_child(traffic_analyzer_val->parent,"ietf-interfaces","name"); assert(name_val); serialize_params(traffic_analyzer_val, cmd_args_buf); sprintf(cmd_buf, "traffic-analyzer %s", cmd_args_buf); //system(cmd_buf); { int fd_in[2]; int fd_out[2]; pid_t childpid; pipe(fd_in); pipe(fd_out); childpid = fork(); if(childpid == -1) { perror("fork"); assert(0); } else if(childpid == 0) { dup2(fd_out[0], 0); close(fd_out[0]); close(fd_out[1]); dup2(fd_in[1], 1); close(fd_in[0]); close(fd_in[1]); execl("/bin/sh", "sh", "-c", cmd_buf, (char *) 0); } else { int nbytes; char readbuffer[1024]; char* lineptr; ssize_t n; ssize_t ret; io_t* io; io = malloc(sizeof(io_t)); assert(io); close(fd_out[0]); close(fd_in[1]); io->out = fdopen(fd_out[1], "w"); assert(io->out != NULL); io->in = fdopen(fd_in[0], "r"); assert(io->in != NULL); dict_add(&io_dict, strdup(VAL_STRING(name_val)), (void *)io); #if 1 fputs("\n",io->out); fflush(io->out); n=0; lineptr=NULL; n = getline(&lineptr, &n, io->in); assert(n > 0 && lineptr != NULL); printf("Received string: %s", lineptr); free(lineptr); #endif } } } static int update_config(val_value_t* config_cur_val, val_value_t* config_new_val) { status_t res; val_value_t *interfaces_cur_val, *interface_cur_val , *traffic_analyzer_cur_val; val_value_t *interfaces_new_val, *interface_new_val , *traffic_analyzer_new_val; if(config_new_val == NULL) { interfaces_new_val = NULL; } else { interfaces_new_val = val_find_child(config_new_val, IF_MOD, "interfaces"); } if(config_cur_val == NULL) { interfaces_cur_val = NULL; } else { interfaces_cur_val = val_find_child(config_cur_val, IF_MOD, "interfaces"); } /* 2 step (delete/add) interface configuration */ /* 1. deactivation loop - deletes all deleted or modified interface/traffic-analyzer -s */ if(interfaces_cur_val!=NULL) { for (interface_cur_val = val_get_first_child(interfaces_cur_val); interface_cur_val != NULL; interface_cur_val = val_get_next_child(interface_cur_val)) { traffic_analyzer_cur_val = val_find_child(interface_cur_val, TA_MOD, "traffic-analyzer"); if(traffic_analyzer_cur_val==NULL) { continue; } traffic_analyzer_new_val = val123_find_match(config_new_val, traffic_analyzer_cur_val); if(traffic_analyzer_new_val==NULL || 0!=val_compare_ex(traffic_analyzer_cur_val,traffic_analyzer_new_val,TRUE)) { traffic_analyzer_delete(traffic_analyzer_cur_val); } } } /* 2. activation loop - adds all new or modified interface/traffic-analyzer -s */ if(interfaces_new_val!=NULL) { for (interface_new_val = val_get_first_child(interfaces_new_val); interface_new_val != NULL; interface_new_val = val_get_next_child(interface_new_val)) { traffic_analyzer_new_val = val_find_child(interface_new_val, TA_MOD, "traffic-analyzer"); if(traffic_analyzer_new_val==NULL) { continue; } traffic_analyzer_cur_val = val123_find_match(config_cur_val, traffic_analyzer_new_val); if(traffic_analyzer_cur_val==NULL || 0!=val_compare_ex(traffic_analyzer_new_val,traffic_analyzer_cur_val,TRUE)) { traffic_analyzer_create(traffic_analyzer_new_val); } if(traffic_analyzer_new_val!=NULL) { val_value_t * state_val; state_val = val_find_child(traffic_analyzer_new_val,TA_MOD,"state"); if(state_val==NULL) { /* add state container */ obj_template_t* state_obj; state_obj = obj_find_child(traffic_analyzer_new_val->obj,TA_MOD,"state"); assert(state_obj); state_val = val_new_value(); assert(state_val!=NULL); val_init_virtual(state_val, get_if_traffic_analyzer_state, state_obj); val_add_child(state_val, traffic_analyzer_new_val); } } } } return NO_ERR; } static val_value_t* prev_root_val = NULL; static int update_config_wrapper() { cfg_template_t *runningcfg; status_t res; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg!=NULL && runningcfg->root!=NULL); if(prev_root_val!=NULL) { val_value_t* cur_root_val; cur_root_val = val_clone_config_data(runningcfg->root, &res); if(0==val_compare(cur_root_val,prev_root_val)) { /*no change*/ val_free_value(cur_root_val); return 0; } val_free_value(cur_root_val); } update_config(prev_root_val, runningcfg->root); if(prev_root_val!=NULL) { val_free_value(prev_root_val); } prev_root_val = val_clone_config_data(runningcfg->root, &res); return 0; } static status_t y_commit_complete(void) { update_config_wrapper(); return NO_ERR; } /* The 3 mandatory callback functions: y_ietf_traffic_analyzer_init, y_ietf_traffic_analyzer_init2, y_ietf_traffic_analyzer_cleanup */ status_t y_ietf_traffic_analyzer_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t* agt_profile; obj_template_t* flows_obj; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-interfaces", NULL, &agt_profile->agt_savedevQ, &ietf_interfaces_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "iana-if-type", NULL, &agt_profile->agt_savedevQ, &iana_if_type_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "ietf-traffic-analyzer", NULL, &agt_profile->agt_savedevQ, &ietf_traffic_analyzer_mod); if (res != NO_ERR) { return res; } agt_disable_feature ("ietf-traffic-analyzer", "multi-stream"); res=agt_commit_complete_register("ietf-traffic-analyzer" /*SIL id string*/, y_commit_complete); assert(res == NO_ERR); dict_init(&io_dict); return res; } status_t y_ietf_traffic_analyzer_init2(void) { status_t res=NO_ERR; cfg_template_t* runningcfg; ncx_module_t* mod; obj_template_t* interfaces_obj; val_value_t* root_val; val_value_t* interfaces_val; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg && runningcfg->root); root_val = runningcfg->root; y_commit_complete(); return res; } void y_ietf_traffic_analyzer_cleanup (void) { } yuma123_2.14/example-modules/ietf-traffic-generator/ietf-traffic-generator.c0000664000175000017500000002402714770023131027253 0ustar vladimirvladimir/* module ietf-traffic-generator implementation for Linux namespace urn:ietf:params:xml:ns:yang:ietf-traffic-generator */ #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_commit_complete.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "val.h" #include "val123.h" #define IF_MOD "ietf-interfaces" #define TG_MOD "ietf-traffic-generator" /* module static variables */ static ncx_module_t *ietf_interfaces_mod; static ncx_module_t *iana_if_type_mod; static ncx_module_t *ietf_traffic_generator_mod; static status_t get_traffic_generator_statistics(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val); /* {"interface-name", required_argument, NULL, 'i'}, {"frame-size", required_argument, NULL, 's'}, {"frame-data", required_argument, NULL, 'd'}, {"interframe-gap", required_argument, NULL, 'f'}, {"interburst-gap", required_argument, NULL, 'b'}, {"frames-per-burst", required_argument, NULL, 'n'}, {"bursts-per-stream", required_argument, NULL, 'p'}, {"total-frames", required_argument, NULL, 't'}, {"testframe-type", required_argument, NULL, 'T'}, */ static void serialize_params(val_value_t* traffic_generator_val, char* cli_args_str) { val_value_t* val; unsigned int i; val = val_find_child(traffic_generator_val->parent,"ietf-interfaces","name"); sprintf(cli_args_str,"--interface-name=%s",VAL_STRING(val)); val = val_find_child(traffic_generator_val,"ietf-traffic-generator","frame-size"); sprintf(cli_args_str+strlen(cli_args_str)," --frame-size=%u",VAL_UINT32(val)); val = val_find_child(traffic_generator_val,"ietf-traffic-generator","frame-data"); if(val!=NULL) { sprintf(cli_args_str+strlen(cli_args_str)," --frame-data="); #if 0 for(i=0;iv.binary.ustrlen;i++) { sprintf(cli_args_str+strlen(cli_args_str),"%02X",(unsigned int)(val->v.binary.ustr[i])); } #else sprintf(cli_args_str+strlen(cli_args_str),"%s",VAL_STRING(val)); #endif } val = val_find_child(traffic_generator_val,"ietf-traffic-generator","interframe-gap"); sprintf(cli_args_str+strlen(cli_args_str)," --interframe-gap=%u",VAL_UINT32(val)); val = val_find_child(traffic_generator_val,"ietf-traffic-generator","interburst-gap"); if(val!=NULL) { sprintf(cli_args_str+strlen(cli_args_str)," --interburst-gap=%u",VAL_UINT32(val)); } val = val_find_child(traffic_generator_val,"ietf-traffic-generator","frames-per-burst"); if(val!=NULL) { sprintf(cli_args_str+strlen(cli_args_str)," --frames-per-burst=%u",VAL_UINT32(val)); } val = val_find_child(traffic_generator_val,"ietf-traffic-generator","bursts-per-stream"); if(val!=NULL) { sprintf(cli_args_str+strlen(cli_args_str)," --bursts-per-stream=%u",VAL_UINT32(val)); } val = val_find_child(traffic_generator_val,"ietf-traffic-generator","total-frames"); if(val!=NULL) { sprintf(cli_args_str+strlen(cli_args_str)," --total-frames=%lu",VAL_UINT64(val)); } val = val_find_child(traffic_generator_val,"ietf-traffic-generator","testframe-type"); if(val!=NULL) { sprintf(cli_args_str+strlen(cli_args_str)," --testframe-type=%s", val->v.idref.name); } val = val_find_child(traffic_generator_val,"ietf-traffic-generator","realtime-epoch"); if(val!=NULL) { sprintf(cli_args_str+strlen(cli_args_str)," --realtime-epoch=%s",VAL_STRING(val)); } } static void traffic_generator_delete(val_value_t* traffic_generator_val) { char cmd_buf[5000]; static char cmd_args_buf[4096]; val_value_t* name_val; printf("traffic_generator_delete:\n"); val_dump_value(traffic_generator_val,NCX_DEF_INDENT); name_val = val_find_child(traffic_generator_val->parent,"ietf-interfaces","name"); assert(name_val); serialize_params(traffic_generator_val, cmd_args_buf); sprintf(cmd_buf, "pkill -f 'traffic-generator %s'", cmd_args_buf); log_info(cmd_buf); system(cmd_buf); sprintf(cmd_buf, "traffic-generator %s --disable &", cmd_args_buf); log_info(cmd_buf); system(cmd_buf); } static void traffic_generator_create(val_value_t* traffic_generator_val) { char cmd_buf[5000]; static char cmd_args_buf[4096]; val_value_t* name_val; printf("traffic_generator_create:\n"); val_dump_value(traffic_generator_val,NCX_DEF_INDENT); name_val = val_find_child(traffic_generator_val->parent,"ietf-interfaces","name"); assert(name_val); serialize_params(traffic_generator_val, cmd_args_buf); sprintf(cmd_buf, "traffic-generator %s &", cmd_args_buf); log_info(cmd_buf); system(cmd_buf); } static int update_config(val_value_t* config_cur_val, val_value_t* config_new_val) { status_t res; val_value_t *interfaces_cur_val, *interface_cur_val , *traffic_generator_cur_val; val_value_t *interfaces_new_val, *interface_new_val , *traffic_generator_new_val; if(config_new_val == NULL) { interfaces_new_val = NULL; } else { interfaces_new_val = val_find_child(config_new_val, IF_MOD, "interfaces"); } if(config_cur_val == NULL) { interfaces_cur_val = NULL; } else { interfaces_cur_val = val_find_child(config_cur_val, IF_MOD, "interfaces"); } /* 2 step (delete/add) interface configuration */ /* 1. deactivation loop - deletes all deleted or modified interface/traffic-generator -s */ if(interfaces_cur_val!=NULL) { for (interface_cur_val = val_get_first_child(interfaces_cur_val); interface_cur_val != NULL; interface_cur_val = val_get_next_child(interface_cur_val)) { traffic_generator_cur_val = val_find_child(interface_cur_val, TG_MOD, "traffic-generator"); if(traffic_generator_cur_val==NULL) { continue; } traffic_generator_new_val = val123_find_match(config_new_val, traffic_generator_cur_val); if(traffic_generator_new_val==NULL || 0!=val_compare_ex(traffic_generator_cur_val,traffic_generator_new_val,TRUE)) { traffic_generator_delete(traffic_generator_cur_val); } } } /* 2. activation loop - adds all new or modified interface/traffic-generator -s */ if(interfaces_new_val!=NULL) { for (interface_new_val = val_get_first_child(interfaces_new_val); interface_new_val != NULL; interface_new_val = val_get_next_child(interface_new_val)) { traffic_generator_new_val = val_find_child(interface_new_val, TG_MOD, "traffic-generator"); if(traffic_generator_new_val==NULL) { continue; } traffic_generator_cur_val = val123_find_match(config_cur_val, traffic_generator_new_val); if(traffic_generator_cur_val==NULL || 0!=val_compare_ex(traffic_generator_new_val,traffic_generator_cur_val,TRUE)) { traffic_generator_create(traffic_generator_new_val); } } } return NO_ERR; } static val_value_t* prev_root_val = NULL; static int update_config_wrapper() { cfg_template_t *runningcfg; status_t res; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg!=NULL && runningcfg->root!=NULL); if(prev_root_val!=NULL) { val_value_t* cur_root_val; cur_root_val = val_clone_config_data(runningcfg->root, &res); if(0==val_compare(cur_root_val,prev_root_val)) { /*no change*/ val_free_value(cur_root_val); return 0; } val_free_value(cur_root_val); } update_config(prev_root_val, runningcfg->root); if(prev_root_val!=NULL) { val_free_value(prev_root_val); } prev_root_val = val_clone_config_data(runningcfg->root, &res); return 0; } static status_t y_commit_complete(void) { update_config_wrapper(); return NO_ERR; } /* The 3 mandatory callback functions: y_ietf_traffic_generator_init, y_ietf_traffic_generator_init2, y_ietf_traffic_generator_cleanup */ status_t y_ietf_traffic_generator_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t* agt_profile; obj_template_t* flows_obj; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-interfaces", NULL, &agt_profile->agt_savedevQ, &ietf_interfaces_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "iana-if-type", NULL, &agt_profile->agt_savedevQ, &iana_if_type_mod); if (res != NO_ERR) { return res; } res = ncxmod_load_module( "ietf-traffic-generator", NULL, &agt_profile->agt_savedevQ, &ietf_traffic_generator_mod); if (res != NO_ERR) { return res; } agt_disable_feature ("ietf-traffic-generator", "multi-stream"); agt_disable_feature("ietf-traffic-generator", "ethernet-vlan"); agt_disable_feature("ietf-traffic-generator", "ingress-direction"); res=agt_commit_complete_register("ietf-traffic-generator" /*SIL id string*/, y_commit_complete); assert(res == NO_ERR); return res; } status_t y_ietf_traffic_generator_init2(void) { status_t res=NO_ERR; cfg_template_t* runningcfg; ncx_module_t* mod; obj_template_t* interfaces_obj; val_value_t* root_val; val_value_t* interfaces_val; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg && runningcfg->root); root_val = runningcfg->root; y_commit_complete(); return res; } void y_ietf_traffic_generator_cleanup (void) { } yuma123_2.14/example-modules/ietf-traffic-generator/timespec-math.c0000664000175000017500000000142614770023131025462 0ustar vladimirvladimir#include void timespec_add(struct timespec* a, struct timespec* b, struct timespec* sum) { if ((a->tv_nsec + b->tv_nsec) >= 1000000000) { sum->tv_sec = a->tv_sec + b->tv_sec + 1; sum->tv_nsec = a->tv_nsec + b->tv_nsec - 1000000000; } else { sum->tv_sec = a->tv_sec + b->tv_sec; sum->tv_nsec = a->tv_nsec + b->tv_nsec; } } void timespec_sub(struct timespec* after, struct timespec* before, struct timespec* diff) { if (after->tv_nsec < before->tv_nsec) { diff->tv_sec = after->tv_sec - before->tv_sec - 1; diff->tv_nsec = after->tv_nsec + 1000000000 - before->tv_nsec; } else { diff->tv_sec = after->tv_sec - before->tv_sec; diff->tv_nsec = after->tv_nsec - before->tv_nsec; } return; } yuma123_2.14/example-modules/ietf-traffic-generator/dict.h0000664000175000017500000000062414770023131023651 0ustar vladimirvladimir#include "dlq.h" typedef struct dict_node_t_ { dlq_hdr_t qhdr; char* name; void* data; } dict_node_t; void dict_init(dlq_hdr_t *que); void dict_clear(dlq_hdr_t *que); char* dict_get_name(dlq_hdr_t *que, void* data); void* dict_get_data(dlq_hdr_t *que, const char* name); void dict_add(dlq_hdr_t *que, const char* name, void* data); void dict_remove(dlq_hdr_t *que, const char* name); yuma123_2.14/example-modules/ietf-traffic-generator/timespec-math.h0000664000175000017500000000027714770023131025472 0ustar vladimirvladimir#include void timespec_add(struct timespec* a, struct timespec* b, struct timespec* sum); void timespec_sub(struct timespec* after, struct timespec* before, struct timespec* diff); yuma123_2.14/example-modules/ietf-traffic-generator/traffic-generator.c0000664000175000017500000001165414770023131026330 0ustar vladimirvladimir#include #include #include #include #include #include #include #include #include "libtraffic-generator.h" #include "timespec-math.h" #include "raw-socket.h" static struct option const long_options[] = { {"interface-name", required_argument, NULL, 'i'}, {"frame-size", required_argument, NULL, 's'}, {"frame-data", required_argument, NULL, 'd'}, {"interframe-gap", required_argument, NULL, 'f'}, {"interburst-gap", required_argument, NULL, 'b'}, {"frames-per-burst", required_argument, NULL, 'n'}, {"bursts-per-stream", required_argument, NULL, 'p'}, {"total-frames", required_argument, NULL, 't'}, {"testframe-type", required_argument, NULL, 'T'}, {"realtime-epoch", required_argument, NULL, 'e'}, {"interface-speed", required_argument, NULL, 'S'}, {"stdout-mode", no_argument, NULL, 'm'}, {NULL, 0, NULL, 0} }; void print_frame(uint64_t frame_index, uint32_t frame_size, uint8_t* frame_data, uint64_t tx_time_sec, uint32_t tx_time_nsec) { int i; printf("%9llu %015llu:%09u %4u ", frame_index, tx_time_sec, tx_time_nsec, frame_size); for(i=0;i typedef struct raw_socket_t_ { int socket; unsigned int ifindex; } raw_socket_t; int raw_socket_init(char* if_name, raw_socket_t* raw_socket); int raw_socket_send(raw_socket_t* raw_socket, uint8_t* raw_frame, uint32_t raw_frame_len); int raw_socket_receive(raw_socket_t* raw_socket, uint8_t* raw_frame, uint32_t raw_frame_max_len, uint32_t* raw_frame_received_len); yuma123_2.14/example-modules/ietf-traffic-generator/libtraffic-generator.c0000664000175000017500000003361114770023131027014 0ustar vladimirvladimir#include #include #include #include #include #include #include //#include #include #include "libtraffic-generator.h" #include "timespec-math.h" #include "yang-date-and-time.h" static unsigned char hexchar2byte(char hexchar) { char byte; if(hexchar>='0' && hexchar<='9') { byte = hexchar - '0'; } else if (hexchar>='A' && hexchar<='F') { byte = hexchar - 'A' + 10; } else if (hexchar>='a' && hexchar<='f') { byte = hexchar - 'a' + 10; } else { assert(0); } return byte; } static char byte2hexchar(unsigned char byte) { char hexchar; if(byte>=0 && byte<=9) { hexchar = byte + '0'; } else if (byte>=0xA && byte<=0xF) { hexchar = byte + 'A'-0xA; } else { assert(0); } return hexchar; } static void hexstr2bin(char* hexstr, uint8_t* data) { unsigned int i; unsigned int len; len = strlen(hexstr)/2; for(i=0;i>4); hexstr[2*i+1] = byte2hexchar(data[i]&0xF); } hexstr[2*len]=0; } /* Frame definitions */ #define DST_MAC_OFFSET 0 #define SRC_MAC_OFFSET 6 #define IPV4_PDU_OFFSET 14 #define IPV4_LEN_OFFSET 16 #define IPV4_HEADER_LEN 20 #define IPV4_TTL_OFFSET 22 #define IPV4_HEADER_CHECKSUM_OFFSET 24 #define SRC_IPV4_ADDRESS_OFFSET 26 #define DST_IPV4_ADDRESS_OFFSET 30 #define SRC_IPV4_UDP_PORT_OFFSET 34 #define DST_IPV4_UDP_PORT_OFFSET 36 #define IPV4_UDP_LEN_OFFSET 38 #define IPV4_UDP_CHECKSUM_OFFSET 40 #define IPV4_UDP_PAYLOAD_OFFSET 42 static void update_dst_mac_address(uint8_t* frame_data, uint32_t frame_size, char* mac_address) { unsigned int mac[6]; uint8_t* ptr; int i; int ret; ret = sscanf(mac_address, "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); assert(ret==6); ptr = frame_data+DST_MAC_OFFSET; for(i=0;i<6;i++) { ptr[i] = mac[i]; } } static void update_src_mac_address(uint8_t* frame_data, uint32_t frame_size, char* mac_address) { unsigned int mac[6]; uint8_t* ptr; int i; int ret; ret = sscanf(mac_address, "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); assert(ret==6); ptr = frame_data+SRC_MAC_OFFSET; for(i=0;i<6;i++) { ptr[i] = mac[i]; } } static void update_src_ipv4_address(uint8_t* frame_data, uint32_t frame_size, char* ipv4_address) { uint8_t* ptr; struct in_addr addr = {}; if(!inet_aton(ipv4_address, &addr)) { fprintf(stderr, "Invalid IP address: %s\n", ipv4_address); assert(0); } ptr = frame_data+SRC_IPV4_ADDRESS_OFFSET; memcpy(ptr,&addr.s_addr,4); } static void update_dst_ipv4_address(uint8_t* frame_data, uint32_t frame_size, char* ipv4_address) { uint8_t* ptr; struct in_addr addr = {}; if(!inet_aton(ipv4_address, &addr)) { fprintf(stderr, "Invalid IP address: %s\n", ipv4_address); assert(0); } ptr = frame_data+DST_IPV4_ADDRESS_OFFSET; memcpy(ptr,&addr.s_addr,4); } static void update_dst_ipv4_udp_port(uint8_t* frame_data, uint32_t frame_size, char* ipv4_udp_port) { uint8_t* ptr; uint16_t port; port = atoi(ipv4_udp_port); ptr = frame_data+DST_IPV4_UDP_PORT_OFFSET; ptr[0] = port >>8; ptr[1] = port & 0xFF; } static void update_src_ipv4_udp_port(uint8_t* frame_data, uint32_t frame_size, char* ipv4_udp_port) { uint8_t* ptr; uint16_t port; port = atoi(ipv4_udp_port); ptr = frame_data+SRC_IPV4_UDP_PORT_OFFSET; ptr[0] = port >>8; ptr[1] = port & 0xFF; } static void update_ipv4_ttl(uint8_t* frame_data, uint32_t frame_size, char* ipv4_ttl) { uint8_t* ptr; ptr = frame_data+IPV4_TTL_OFFSET; ptr[0] = atoi(ipv4_ttl); } static void update_ipv4_len(uint8_t* frame_data, uint32_t frame_size) { uint8_t* ptr; uint32_t len; len = frame_size - 14 - 4; ptr = frame_data+IPV4_LEN_OFFSET; ptr[0] = len >> 8; ptr[1] = len & 0xFF; } static void update_ipv4_header_checksum(uint8_t* frame_data, uint32_t frame_size) { unsigned int i; uint32_t w; uint8_t* ptr; uint32_t sum; ptr = frame_data+IPV4_HEADER_CHECKSUM_OFFSET; ptr[0] = 0; ptr[1] = 0; sum = 0; for(i=0;i<(IPV4_HEADER_LEN/2);i++) { w = (((uint32_t)frame_data[IPV4_PDU_OFFSET+i*2])<<8) + frame_data[IPV4_PDU_OFFSET+i*2+1]; sum += w; if(sum>0xFFFF) { sum = sum - 0xFFFF; } } ptr[0] = ((~sum) >> 8) & 0xFF; ptr[1] = (~sum) & 0xFF; } static void update_ipv4_udp_len(uint8_t* frame_data, uint32_t frame_size) { uint8_t* ptr; uint32_t len; len = frame_size - 14 - 4 - IPV4_HEADER_LEN; ptr = frame_data+IPV4_UDP_LEN_OFFSET; ptr[0] = len >> 8; ptr[1] = len & 0xFF; } static void update_ipv4_udp_checksum(uint8_t* frame_data, uint32_t frame_size) { /* Checksum of 0 is what rfc2544 specifies */ uint8_t* ptr; ptr = frame_data+IPV4_UDP_CHECKSUM_OFFSET; ptr[0] = 0; ptr[1] = 0; } static void update_crc(uint8_t* frame_data, uint32_t frame_size) { uint32_t crc_final; uint32_t crc_initial=0; // linux API: crc_final = crc32(crc_initial ^ 0xffffffff, frame_data, frame_size-4) ^ 0xffffffff; crc_final = crc32(crc_initial, frame_data, frame_size-4); frame_data[frame_size-4]=(crc_final>>0)&0xFF; frame_data[frame_size-3]=(crc_final>>8)&0xFF; frame_data[frame_size-2]=(crc_final>>16)&0xFF; frame_data[frame_size-1]=(crc_final>>24)&0xFF; } /* valid 64 byte rfc2544 sec C.2.6.4 testframe */ static char* rfc2544_testframe = /* DATAGRAM HEADER*/ "123456789ABC" /* locally administered DST MAC address */ "DEF012345678" /* locally administered SRC MAC address */ "0800" /* type */ /* IP HEADER */ "4500002E000000000A11BC01C0000201C0000202" /* UDP HEADER */ "C0200007001A0000" /* UDP DATA */ "000102030405060708090A0B0C0D0E0F10110DBD8EC6"; char* traffic_generator_make_testframe(uint32_t frame_size, char* frame_data_hexstr, char* src_mac_address, char* dst_mac_address, char* src_ipv4_address, char* dst_ipv4_address, char* ipv4_ttl, char* src_ipv4_udp_port, char* dst_ipv4_udp_port) { unsigned int i; uint8_t* frame_data; uint8_t* ptr; char* result_frame_hexstr; frame_data = malloc(frame_size); memset(frame_data,0,frame_size); hexstr2bin(rfc2544_testframe, frame_data); /* payload - incrementing octets */ for(i=0;i<(frame_size-IPV4_UDP_PAYLOAD_OFFSET-4);i++) { frame_data[IPV4_UDP_PAYLOAD_OFFSET+i]=i&0xFF; } if(frame_data_hexstr) { assert(strlen(frame_data_hexstr)<=(frame_size*2)); hexstr2bin(frame_data_hexstr,frame_data); } if(src_ipv4_udp_port) { update_src_ipv4_udp_port(frame_data, frame_size, src_ipv4_udp_port); } if(dst_ipv4_udp_port) { update_dst_ipv4_udp_port(frame_data, frame_size, dst_ipv4_udp_port); } update_ipv4_udp_len(frame_data, frame_size); update_ipv4_udp_checksum(frame_data, frame_size); if(src_ipv4_address) { update_src_ipv4_address(frame_data, frame_size, src_ipv4_address); } if(dst_ipv4_address) { update_dst_ipv4_address(frame_data, frame_size, dst_ipv4_address); } if(ipv4_ttl) { update_ipv4_ttl(frame_data, frame_size, ipv4_ttl); } update_ipv4_len(frame_data, frame_size); update_ipv4_header_checksum(frame_data, frame_size); if(src_mac_address) { update_src_mac_address(frame_data, frame_size, src_mac_address); } if(dst_mac_address) { update_dst_mac_address(frame_data, frame_size, dst_mac_address); } update_crc(frame_data, frame_size); result_frame_hexstr = malloc(frame_size*2+1); bin2hexstr(frame_data,frame_size,result_frame_hexstr); free(frame_data); return result_frame_hexstr; } traffic_generator_t* traffic_generator_init(uint64_t interface_speed, char* realtime_epoch, uint32_t frame_size, char* frame_data_hexstr, uint32_t interframe_gap, uint32_t interburst_gap, uint32_t frames_per_burst, uint32_t bursts_per_stream, uint64_t total_frames, char* testframe_type) { unsigned int i; traffic_generator_t* tg; int ret; tg = (traffic_generator_t*)malloc(sizeof(traffic_generator_t)); memset(tg,0,sizeof(traffic_generator_t)); tg->streams_num = 1; tg->streams = malloc(sizeof(stream_t)*tg->streams_num); memset(tg->streams,0,sizeof(stream_t)*tg->streams_num); tg->nsec_per_octet = ((double)8*1000000000) / interface_speed; /* e.g. 8.0 for 1Gb */ tg->octets_per_sec = interface_speed / 8; tg->total_frames = total_frames; /* hardcode to 10 sec at 50% rate with minimum packets 10*(1000000000/((20+64)*8))/2=7440476 */ tg->total_frame_index = 0; for(i=0;istreams_num;i++) { tg->streams[i].bursts_per_stream = bursts_per_stream; tg->streams[i].frame_size = frame_size; tg->streams[i].frame_data = malloc(tg->streams[i].frame_size); memset(tg->streams[i].frame_data,0,frame_size); assert(strlen(frame_data_hexstr)/2 <= frame_size); hexstr2bin(frame_data_hexstr,tg->streams[i].frame_data); tg->streams[i].interframe_gap=interframe_gap; tg->streams[i].frames_per_burst=frames_per_burst; tg->streams[i].interburst_gap=interburst_gap; tg->streams[i].frames_per_burst=frames_per_burst; if(interburst_gap!=0) { tg->streams[i].interstream_gap=interburst_gap; } else { tg->streams[i].interstream_gap=interframe_gap; } if(testframe_type!=NULL) { tg->streams[i].testframe_type=1; if(0==strcmp(testframe_type,"dynamic")) { tg->streams[i].testframe_type_dynamic=1; } } } ret = yang_date_and_time_to_ieee_1588(realtime_epoch, &tg->sec, &tg->nsec); assert(ret==0); return tg; } static void frame_time_stamp(traffic_generator_t* tg) { uint8_t* timestamp; unsigned int offset; offset = tg->streams[tg->stream_index].frame_size - 10 - 4 /* crc */; timestamp = (uint8_t*)&tg->streams[tg->stream_index].frame_data[offset]; timestamp[0] = (tg->sec&0x0000FF0000000000)>>40; timestamp[1] = (tg->sec&0x000000FF00000000)>>32; timestamp[2] = (tg->sec&0x00000000FF000000)>>24; timestamp[3] = (tg->sec&0x0000000000FF0000)>>16; timestamp[4] = (tg->sec&0x000000000000FF00)>>8; timestamp[5] = (tg->sec&0x00000000000000FF)>>0; timestamp[6] = (tg->nsec&0x00000000FF000000)>>24; timestamp[7] = (tg->nsec&0x0000000000FF0000)>>16; timestamp[8] = (tg->nsec&0x000000000000FF00)>>8; timestamp[9] = (tg->nsec&0x00000000000000FF)>>0; } static void frame_sequence_stamp(traffic_generator_t* tg) { uint64_t index; uint8_t* seqnum; unsigned int offset; offset = tg->streams[tg->stream_index].frame_size - 18 - 4 /* crc */; seqnum = (uint8_t*)&tg->streams[tg->stream_index].frame_data[offset]; index = tg->total_frame_index-1; seqnum[0] = (index&0xFF00000000000000)>>56; seqnum[1] = (index&0x00FF000000000000)>>48; seqnum[2] = (index&0x0000FF0000000000)>>40; seqnum[3] = (index&0x000000FF00000000)>>32; seqnum[4] = (index&0x00000000FF000000)>>24; seqnum[5] = (index&0x0000000000FF0000)>>16; seqnum[6] = (index&0x000000000000FF00)>>8; seqnum[7] = (index&0x00000000000000FF)>>0; } static void frame_udp_checksum_update(traffic_generator_t* tg) { uint8_t* checksum; unsigned int offset; offset = 14/*ethernet*/+20+6; /* no vlan */ checksum = (uint8_t*)&tg->streams[tg->stream_index].frame_data[offset]; checksum[0] = 0; checksum[1] = 0; /* TODO - zero is OK */ } int traffic_generator_get_frame(traffic_generator_t* tg, uint32_t* frame_size, uint8_t** frame_data, uint64_t* tx_time_sec, uint32_t* tx_time_nsec) { struct timespec start, delta, next; double delta_nsec; uint64_t delta_time_octets; if((tg->total_frames>0) && (tg->total_frame_index==tg->total_frames)) { return 1; } else { tg->total_frame_index++; } *frame_data = tg->streams[tg->stream_index].frame_data; *frame_size = tg->streams[tg->stream_index].frame_size; *tx_time_sec = tg->sec; *tx_time_nsec = tg->nsec; start.tv_sec = tg->sec; start.tv_nsec = tg->nsec; /* update time and indexes for next transmission */ if((tg->frame_index+1) < tg->streams[tg->stream_index].frames_per_burst) { tg->frame_index++; delta_time_octets=(*frame_size+tg->streams[tg->stream_index].interframe_gap); } else if((tg->burst_index+1) < tg->streams[tg->stream_index].bursts_per_stream) { tg->frame_index=0; tg->burst_index++; delta_time_octets=(*frame_size+tg->streams[tg->stream_index].interburst_gap); } else { delta_time_octets=(*frame_size+tg->streams[tg->stream_index].interstream_gap); tg->frame_index=0; tg->burst_index=0; tg->stream_index=(tg->stream_index+1)%tg->streams_num; } delta.tv_sec=(delta_time_octets/tg->octets_per_sec); delta_nsec = (delta_time_octets-delta.tv_sec*tg->octets_per_sec)*(double)tg->nsec_per_octet + tg->nsec_fraction; delta.tv_sec += (long)delta_nsec/1000000000; delta.tv_nsec=(long)delta_nsec%1000000000; tg->nsec_fraction = delta_nsec - (long)delta_nsec; timespec_add(&start, &delta, &next); if(tg->streams[tg->stream_index].testframe_type && tg->streams[tg->stream_index].testframe_type_dynamic) { frame_time_stamp(tg); frame_sequence_stamp(tg); frame_udp_checksum_update(tg); update_crc(tg->streams[tg->stream_index].frame_data, tg->streams[tg->stream_index].frame_size); } tg->sec = next.tv_sec; tg->nsec = next.tv_nsec; return 0; } void traffic_generator_set_epoch(traffic_generator_t* tg, uint64_t sec, uint32_t nsec) { tg->sec=sec; tg->nsec=nsec; } yuma123_2.14/example-modules/ietf-traffic-generator/yang-date-and-time.c0000664000175000017500000000406314770023131026267 0ustar vladimirvladimir#include #include #include #include #include #include void ieee_1588_to_yang_date_and_time(uint64_t sec, uint32_t nsec, char* date_and_time) { struct tm my_tm; time_t time_t_sec = (time_t)sec; gmtime_r(&(time_t_sec), &my_tm); (void)sprintf((char *)date_and_time, "%04u-%02u-%02uT%02u:%02u:%02u.%09uZ", (uint32_t)(my_tm.tm_year+1900), (uint32_t)(my_tm.tm_mon+1), (uint32_t)my_tm.tm_mday, (uint32_t)my_tm.tm_hour, (uint32_t)my_tm.tm_min, (uint32_t)my_tm.tm_sec, (uint32_t)nsec); } int yang_date_and_time_to_ieee_1588(char* date_and_time, uint64_t* sec, uint32_t* nsec) { /* YYYY-MM-DDThh:mm:ss.n*Z */ struct tm tm; char nsec_str[]="000000000"; unsigned int date_and_time_len; unsigned int nsec_digits; unsigned int i; int ret; char* ptr; *sec=0; *nsec=0; date_and_time_len = strlen(date_and_time); if(date_and_time_len < strlen("YYYY-MM-DDThh:mm:ssZ")) { return -1; } if(date_and_time[date_and_time_len-1] != 'Z') { return -1; } memset(&tm, 0, sizeof(struct tm)); ret = sscanf(date_and_time, "%04d-%02d-%02dT%02d:%02d:%02d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); if(ret!=6) { return -1; } tm.tm_year = tm.tm_year - 1900; tm.tm_mon = tm.tm_mon - 1; tm.tm_isdst = 0; ptr = date_and_time + strlen("YYYY-MM-DDThh:mm:ss"); *sec = mktime(&tm); if(ptr==(date_and_time+date_and_time_len-1)) { return 0; } if(*ptr!='.') { return -1; } ptr++; nsec_digits = date_and_time_len-strlen("YYYY-MM-DDThh:mm:ss.")-strlen("Z"); for(i=0;i'9') { return -1; } nsec_str[i] = ptr[i]; } *nsec = atoi(nsec_str); return 0; } yuma123_2.14/example-modules/ietf-traffic-generator/configure.ac0000664000175000017500000000172114770023131025042 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-ietf-traffic-generator], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL AC_ARG_ENABLE([standalone], [ --enable-standalone Turn on standalone build], [case "${enableval}" in yes) standalone=true ;; no) standalone=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-standalone]) ;; esac],[standalone=true]) AM_CONDITIONAL([STANDALONE], [test x$standalone = xtrue]) netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) examples_yangdir="$prefix/share/yuma/modules/examples" AC_SUBST(examples_yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/ietf-traffic-generator/yang-date-and-time.h0000664000175000017500000000031014770023131026263 0ustar vladimirvladimir#include void ieee_1588_to_yang_date_and_time(uint64_t sec, uint32_t nsec, char* date_and_time); int yang_date_and_time_to_ieee_1588(char* date_and_time, uint64_t* sec, uint32_t* nsec); yuma123_2.14/example-modules/ietf-traffic-generator/traffic-generator-make-testframe.c0000664000175000017500000000474214770023131031233 0ustar vladimirvladimir#include #include #include #include #include #include #include #include #include "libtraffic-generator.h" static struct option const long_options[] = { {"frame-size", required_argument, NULL, 's'}, {"frame-data", required_argument, NULL, 'd'}, {"src-mac-addresss", required_argument, NULL, 'a'}, {"dst-mac-addresss", required_argument, NULL, 'A'}, {"src-ipv4-address", required_argument, NULL, 'v'}, {"dst-ipv4-address", required_argument, NULL, 'V'}, {"src-ipv4-udp-port", required_argument, NULL, 'p'}, {"dst-ipv4-udp-port", required_argument, NULL, 'P'}, {"ipv4-ttl", required_argument, NULL, 't'}, {NULL, 0, NULL, 0} }; int main(int argc, char** argv) { int ret; uint32_t frame_size=64; char* frame_data_hexstr=NULL; char* src_mac_address=NULL; char* dst_mac_address=NULL; char* src_ipv4_address=NULL; char* dst_ipv4_address=NULL; char* src_ipv4_udp_port=NULL; char* dst_ipv4_udp_port=NULL; char* ipv4_ttl=NULL; int optc; struct timespec epoch,rel,abs,now,req,rem; traffic_generator_t* tg; int stdout_mode = 0; while ((optc = getopt_long (argc, argv, "s:d:a:A:v:V:p:P:t:", long_options, NULL)) != -1) { switch (optc) { case 's': frame_size = atoi(optarg); break; case 'd': frame_data_hexstr = optarg; /*hexstr*/ break; case 'm': stdout_mode = 1; break; case 'a': src_mac_address = optarg; break; case 'A': dst_mac_address = optarg; break; case 'v': src_ipv4_address = optarg; break; case 'V': dst_ipv4_address = optarg; break; case 'p': src_ipv4_udp_port = optarg; break; case 'P': dst_ipv4_udp_port = optarg; break; case 't': ipv4_ttl = optarg; break; default: exit (-1); } } frame_data_hexstr = traffic_generator_make_testframe(frame_size, frame_data_hexstr, src_mac_address, dst_mac_address, src_ipv4_address, dst_ipv4_address, ipv4_ttl, src_ipv4_udp_port, dst_ipv4_udp_port); printf("%s\n", frame_data_hexstr); return 0; } yuma123_2.14/example-modules/ietf-traffic-generator/traffic-analyzer.c0000664000175000017500000000557714770023131026176 0ustar vladimirvladimir#include #include #include #include #include #include #include #include #include #include #include "libtraffic-analyzer.h" #include "timespec-math.h" #include "raw-socket.h" static struct option const long_options[] = { {"interface-name", required_argument, NULL, 'i'}, {"verbose", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0} }; traffic_analyzer_t* ta; void* monitor(void* arg) { int ret; while(1) { ret = getc(stdin); if(ret==EOF) { exit(0); } ret = fprintf(stdout,"%llu%llu%llu%llu%llu%llu%llu\n", ta->totalframes, ta->testframes, ta->testframe.sequence_errors, (uint64_t)ta->testframe.latency.samples, (uint64_t)ta->testframe.latency.min.tv_nsec, (uint64_t)ta->testframe.latency.max.tv_nsec, (uint64_t)ta->testframe.latency.last.tv_nsec); fflush(stdout); assert(ret>0); //sleep(1); } } int main(int argc, char** argv) { int ret; raw_socket_t raw_socket; uint64_t tx_time_sec; uint32_t tx_time_nsec; uint8_t* raw_frame_data; uint32_t raw_frame_max_len; uint32_t raw_frame_len; char* interface_name; int verbose=0; uint32_t frame_size=64; int optc; struct timespec now; pthread_t monitor_tid; ta = malloc(sizeof(traffic_analyzer_t)); memset(ta,sizeof(traffic_analyzer_t),0); while ((optc = getopt_long (argc, argv, "i:v", long_options, NULL)) != -1) { switch (optc) { case 'i': interface_name=optarg; break; case 'v': verbose=1; break; default: exit (-1); } } raw_frame_data = malloc(64*1024); assert(raw_frame_data!=NULL); raw_frame_max_len = 64*1024; ret = raw_socket_init(interface_name /*e.g eth0*/, &raw_socket); assert(ret==0); ret = pthread_create (&monitor_tid, NULL, monitor, NULL/*arg*/); assert(ret==0); while(1) { uint8_t* cur_frame_data; uint32_t cur_frame_size; ret = raw_socket_receive(&raw_socket, raw_frame_data, raw_frame_max_len, &raw_frame_len); assert(ret==0); clock_gettime( CLOCK_REALTIME, &now); traffic_analyzer_put_frame(ta, raw_frame_data, raw_frame_len, now.tv_sec, now.tv_nsec); if(verbose) { fprintf(stderr,"#%llu, size %09u latency %09u nsec\n", ta->totalframes, raw_frame_len, ta->testframe.latency.last.tv_nsec); } } } yuma123_2.14/example-modules/ietf-system/0000775000175000017500000000000014770023131020475 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-system/ietf-system.c0000664000175000017500000002041414770023131023113 0ustar vladimirvladimir/* module ietf-system namespace urn:ietf:params:xml:ns:yang:ietf-system */ #define _DEFAULT_SOURCE #define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "val.h" #include "val123.h" /* module static variables */ static ncx_module_t *ietf_system_mod; static obj_template_t* system_state_obj; /* Registered callback functions: get_system_state_clock */ static status_t get_system_state_clock_current_datetime(ses_cb_t *scb, getcb_mode_t cbmode, val_value_t *vir_val, val_value_t *dst_val) { status_t res; res = NO_ERR; char tstamp_buf[32]; tstamp123_datetime_nsec(tstamp_buf); /* /system-state/clock/current-datetime */ res = val_set_simval_obj(dst_val, dst_val->obj, tstamp_buf); /* disable cache */ vir_val->cachetime = 0; return res; } static status_t y_ietf_system_set_current_datetime ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { val_value_t* current_datetime_val; struct timespec ts; struct timeval tv; struct tm tm; char* ptr; int ret; current_datetime_val = val_find_child( msg->rpc_input, "ietf-system", "current-datetime"); assert(current_datetime_val!=NULL); memset(&tm, 0, sizeof(struct tm)); memset(&ts, 0, sizeof(struct timespec)); memset(&tv, 0, sizeof(struct timeval)); ptr=strptime(VAL_STRING(current_datetime_val), "%Y-%m-%dT%H:%M:%S", &tm); assert(ptr!=NULL); ts.tv_sec=mktime(&tm); ts.tv_nsec=0; //TIMESPEC_TO_TIMEVAL(ts, tv); tv.tv_sec = ts.tv_sec; tv.tv_usec = ts.tv_nsec / 1000; ret=clock_settime(CLOCK_REALTIME, &ts); assert(ret==0); ret=settimeofday (&tv, NULL); assert(ret==0); ret=system("hwclock --systohc"); assert(ret==0); return NO_ERR; } static status_t y_ietf_system_system_restart ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { agt_request_shutdown(NCX_SHUT_EXIT); /* wait for process termination before rebooting */ { int status; pid_t child_pid; log_debug("Waiting for netconfd to complete shutdown.\n"); if((child_pid = fork()) != 0) { while (child_pid != wait(&status)) { if(child_pid==-1) { exit(-1); } } fprintf(stderr, "system-restart: Rebooting system ..."); status = system("reboot"); if(status!=0) { return ERR_NCX_OPERATION_FAILED; } } } return NO_ERR; } static status_t y_ietf_system_system_hostname_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: case OP_EDITOP_MERGE: case OP_EDITOP_REPLACE: case OP_EDITOP_CREATE: case OP_EDITOP_DELETE: if(newval!=NULL) { char* buf; int ret; buf=malloc(strlen("hostname ") + strlen(VAL_STRING(newval))+1); sprintf(buf,"hostname %s", VAL_STRING(newval)); printf("Setting /system/hostname to %s - cmd=%s\n", VAL_STRING(newval), buf); ret=system(buf); if(ret != 0) { errorval=newval; errorstr="Can't set hostname. Are you sure your server is running as root?"; /* strdup(strerror(errno)); */ res = SET_ERROR(ERR_INTERNAL_VAL); } } break; default: assert(0); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* The 3 mandatory callback functions: y_ietf_system_init, y_ietf_system_init2, y_ietf_system_cleanup */ status_t y_ietf_system_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-system", NULL, &agt_profile->agt_savedevQ, &ietf_system_mod); if (res != NO_ERR) { return res; } system_state_obj = ncx_find_object( ietf_system_mod, "system-state"); if (system_state_obj == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } res = agt_cb_register_callback( "ietf-system", (const xmlChar *)"/system/hostname", (const xmlChar *)NULL /*"YYYY-MM-DD"*/, y_ietf_system_system_hostname_edit); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( "ietf-system", "set-current-datetime", AGT_RPC_PH_INVOKE, y_ietf_system_set_current_datetime); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( "ietf-system", "system-restart", AGT_RPC_PH_INVOKE, y_ietf_system_system_restart); if (res != NO_ERR) { return res; } return res; } status_t y_ietf_system_init2(void) { status_t res; cfg_template_t* runningcfg; val_value_t* system_state_val; obj_template_t* obj; val_value_t* clock_val; val_value_t* current_datetime_val; res = NO_ERR; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); if (!runningcfg || !runningcfg->root) { return SET_ERROR(ERR_INTERNAL_VAL); } system_state_val = val_find_child(runningcfg->root, "ietf-system", "system-state"); /* Can not coexist with other implementation * of ietf-system. */ if(system_state_val==NULL) { system_state_val = val_new_value(); assert(system_state_val != NULL); val_init_from_template(system_state_val, system_state_obj); val_add_child(system_state_val, runningcfg->root); } /* /system-state/clock */ clock_val = val_find_child(system_state_val, "ietf-system", "clock"); if(clock_val==NULL) { obj = obj_find_child(system_state_val->obj, "ietf-system", "clock"); assert(obj != NULL); clock_val = val_new_value(); assert(clock_val != NULL); val_init_from_template(clock_val, obj); val_add_child(clock_val, system_state_val); } obj = obj_find_child(clock_val->obj, "ietf-system", "current-datetime"); assert(obj != NULL); current_datetime_val = val_new_value(); assert(current_datetime_val != NULL); val_init_virtual(current_datetime_val, get_system_state_clock_current_datetime, obj); val_add_child(current_datetime_val, clock_val); return res; } void y_ietf_system_cleanup (void) { } yuma123_2.14/example-modules/ietf-system/README0000664000175000017500000000124614770023131021360 0ustar vladimirvladimir==Standalone project for netconfd module implementing ietf-system.yang== Dependency: installed netconfd run-time binaries and development shared libraries and headers. ===Build and install=== autoreconf -i -f ./configure CFLAGS="-g -O0" CXXFLAGS="-g -O0" --prefix=/usr make sudo make install sudo netconfd --module=ietf-system ===Functionality=== yangcli root@localhost> xget /system-state/clock/current-datetime RPC Data Reply 3 for session 1: rpc-reply { data { system-state { clock { current-datetime 2016-03-08T16:47:50Z } } } } ... yangcli root@localhost> set-current-datetime current-datetime=1970-01-01T15:34:27.882201125Z ... yuma123_2.14/example-modules/ietf-system/debian/0000775000175000017500000000000014770023131021717 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-system/debian/source/0000775000175000017500000000000014770023131023217 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-system/debian/source/format0000664000175000017500000000001414770023131024425 0ustar vladimirvladimir3.0 (quilt) yuma123_2.14/example-modules/ietf-system/debian/control0000664000175000017500000000156314770023131023327 0ustar vladimirvladimirSource: yuma123-netconfd-module-ietf-system Section: net Priority: optional Maintainer: Vladimir Vassilev Build-Depends: debhelper (>= 10), autoconf, libncurses5-dev, libreadline-dev, libssh2-1-dev, libssl-dev, libtool, libxml2-dev, libyuma-dev (>= 2.14), zlib1g-dev Standards-Version: 4.1.4 Homepage: http://yuma123.org Package: NETCONFd-module-ietf-system Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, debconf, lsb-base (>= 3.2-13), libyuma2 (>= 2.14) Description: SIL module for netconfd implementing ietf-system.yang The NETCONF protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. . The yuma123 netconfd module ietf-interfaces implements the functionality modeled in ietf-system.yang using some common command line tools. yuma123_2.14/example-modules/ietf-system/debian/rules0000775000175000017500000000071514770023131023002 0ustar vladimirvladimir#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ --with autoreconf yuma123_2.14/example-modules/ietf-system/debian/copyright0000664000175000017500000000326514770023131023660 0ustar vladimirvladimirFiles: * Copyright: (C) 2016-2025 Vladimir Vassilev Copyright: (C) 2016-2018 Transpacket AS Copyright: (C) 2019-2025 Lightside Instruments AS License: BSD License: BSD Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andy Bierman BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. yuma123_2.14/example-modules/ietf-system/debian/compat0000664000175000017500000000000314770023131023116 0ustar vladimirvladimir10 yuma123_2.14/example-modules/ietf-system/debian/changelog0000664000175000017500000000027214770023131023572 0ustar vladimirvladimiryuma123-netconfd-module-ietf-system (2.14-0) stable; urgency=medium * Unofficial package -- Vladimir Vassilev Sun, 23 Mar 2025 15:25:36 +0100 yuma123_2.14/example-modules/ietf-system/Makefile.am0000775000175000017500000000132614770023131022536 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libietf-system.la libietf_system_la_SOURCES = \ ietf-system.c if STANDALONE # Standalone mode. Depends on installed libyuma-dev libietf_system_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libietf_system_la_LDFLAGS = -module -lyumaagt -lyumancx else # Integrated yuma123 build libietf_system_la_CPPFLAGS = -I $(top_srcdir)/netconf/src/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump libietf_system_la_LDFLAGS = -module $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la endif #yang_DATA = ietf-system.yang yuma123_2.14/example-modules/ietf-system/configure.ac0000664000175000017500000000165314770023131022770 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-ietf-system], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL AC_ARG_ENABLE([standalone], [ --enable-standalone Turn on standalone build], [case "${enableval}" in yes) standalone=true ;; no) standalone=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-standalone]) ;; esac],[standalone=true]) AM_CONDITIONAL([STANDALONE], [test x$standalone = xtrue]) netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules" AC_SUBST(yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/interfaces-alarms/0000775000175000017500000000000014770023131021624 5ustar vladimirvladimiryuma123_2.14/example-modules/interfaces-alarms/interfaces-alarms.yang0000664000175000017500000000103414770023131026102 0ustar vladimirvladimirmodule interfaces-alarms { namespace "http://yuma123.org/ns/interfaces-alarms"; prefix alarms-if; import ietf-alarms { prefix alarms; } organization "yuma123.org"; contact "Editor: Vladimir Vassilev "; description "This module contains a single YANG definition of the link-alarm identity based on alarm-identity."; revision 2016-11-12 { description "Initial revision."; } identity link-alarm { base alarms:alarm-identity; } } yuma123_2.14/example-modules/interfaces-alarms/README0000664000175000017500000000127614770023131022512 0ustar vladimirvladimir==Standalone project for netconfd module implementing link-up/link-down alarms== Dependency: installed netconfd run-time binaries and development shared libraries and headers. Dependency on loaded ietf-alarms SIL and installed libalarmctrl from ../ietf-alarms Functionality: Generates polls for /interfaces-state/inteface/oper-state and generates alarm when interface present in /interfaces goes down. ===Build and install=== autoreconf -i -f ./configure CFLAGS="-g -O0" CXXFLAGS="-g -O0" --prefix=/usr make sudo make install #Starting netconfd: /usr/sbin/netconfd --module=ietf-interfaces --module=ietf-alarms --module=iana-if-type --module=interfaces-notifications --module=interfaces-alarms yuma123_2.14/example-modules/interfaces-alarms/interfaces-alarms.c0000664000175000017500000001037514770023131025376 0ustar vladimirvladimir/* module interfaces-alarms namespace http://yuma123.org/ns/interfaces-alarms */ #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "alarmctrl.h" #include #include static status_t y_interfaces_alarms_alarms_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: case OP_EDITOP_MERGE: case OP_EDITOP_REPLACE: case OP_EDITOP_CREATE: case OP_EDITOP_DELETE: /* device instrumentation here */ break; default: assert(0); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* callback intercepting all notifications. The registeration is done in y_interfaces_alarms_init */ status_t notification_cb(agt_not_msg_t *notif) { val_value_t *payload_val; char description_str[1024]; status_t res; int ret; if((0==strcmp(obj_get_name(notif->notobj),"link-up")) || (0==strcmp(obj_get_name(notif->notobj),"link-down"))) { int down; down = (0==strcmp(obj_get_name(notif->notobj),"link-down")); for (payload_val = (val_value_t *)dlq_firstEntry(¬if->payloadQ); payload_val != NULL; payload_val = (val_value_t *)dlq_nextEntry(payload_val)) { if(0==strcmp("if-name",obj_get_name(payload_val->obj))) { char* resource_str; sprintf(description_str,"Link down - %s",VAL_STRING(payload_val)); //alarm_event_w_type(description_str, "minor", "communications", down?1:0); resource_str=malloc(strlen("/interfaces/interface[name=\'%s\']")+strlen(VAL_STRING(payload_val))+1); sprintf(resource_str,"/interfaces/interface[name=\'%s\']",VAL_STRING(payload_val)); ret=alarmctrl_event(resource_str, "link-alarm"/*alarm_type_id_str*/, ""/*alarm_type_qualifier_str*/, "major", "Probably someone disconnected something?!", down?1:0); //assert(ret==0); free(resource_str); break; } } } return NO_ERR; } /* The 3 mandatory callback functions: y_interfaces_alarms_init, y_interfaces_alarms_init2, y_interfaces_alarms_cleanup */ status_t y_interfaces_alarms_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; ncx_module_t *mod; agt_profile = agt_get_profile(); res = ncxmod_load_module( "interfaces-alarms", NULL, &agt_profile->agt_savedevQ, &mod); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( "ietf-alarms", (const xmlChar *)"/alarms", (const xmlChar *)NULL /*"YYYY-MM-DD"*/, y_interfaces_alarms_alarms_edit); if (res != NO_ERR) { return res; } agt_not_queue_notification_cb_register("intrfaces-alarms", notification_cb); return res; } status_t y_interfaces_alarms_init2(void) { status_t res; cfg_template_t* runningcfg; val_value_t* alarms_val; res = NO_ERR; return res; } void y_interfaces_alarms_cleanup (void) { } yuma123_2.14/example-modules/interfaces-alarms/Makefile.am0000775000175000017500000000062114770023131023662 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libinterfaces-alarms.la libinterfaces_alarms_la_SOURCES = \ interfaces-alarms.c libinterfaces_alarms_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libinterfaces_alarms_la_LDFLAGS = -module -lyumaagt -lyumancx -lalarmctrl #yang_DATA = ietf-interfaces.yang dist_yang_DATA = \ interfaces-alarms.yang yuma123_2.14/example-modules/interfaces-alarms/configure.ac0000664000175000017500000000117014770023131024111 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-interfaces-alarms], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules" AC_SUBST(yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/ietf-alarms/0000775000175000017500000000000014770023131020430 5ustar vladimirvladimiryuma123_2.14/example-modules/ietf-alarms/ietf-alarms.c0000664000175000017500000001050614770023131023002 0ustar vladimirvladimir/* module ietf-alarms namespace urn:ietf:params:xml:ns:yang:ietf-alarms */ #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include #include static status_t y_ietf_alarms_alarms_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: case OP_EDITOP_MERGE: case OP_EDITOP_REPLACE: case OP_EDITOP_CREATE: case OP_EDITOP_DELETE: /* device instrumentation here */ break; default: assert(0); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* The 3 mandatory callback functions: y_ietf_alarms_init, y_ietf_alarms_init2, y_ietf_alarms_cleanup */ static obj_template_t* alarms_obj; status_t y_ietf_alarms_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; ncx_module_t *mod; agt_profile = agt_get_profile(); res = ncxmod_load_module( "ietf-alarms", NULL, &agt_profile->agt_savedevQ, &mod); if (res != NO_ERR) { return res; } alarms_obj = ncx_find_object( mod, "alarms"); assert(alarms_obj); agt_disable_feature ("ietf-alarms", "operator-actions"); agt_disable_feature ("ietf-alarms", "alarm-shelving"); agt_disable_feature ("ietf-alarms", "alarm-history"); res = agt_cb_register_callback( "ietf-system", (const xmlChar *)"/alarms", (const xmlChar *)NULL /*"YYYY-MM-DD"*/, y_ietf_alarms_alarms_edit); if (res != NO_ERR) { return res; } return res; } status_t y_ietf_alarms_init2(void) { status_t res; cfg_template_t* runningcfg; obj_template_t* alarm_list_obj; obj_template_t* number_of_alarms_obj; val_value_t* alarms_val; val_value_t* alarm_list_val; val_value_t* number_of_alarms_val; res = NO_ERR; runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg!=NULL && runningcfg->root!=NULL); alarms_val = val_find_child(runningcfg->root, "ietf-alarms", "alarms"); if(alarms_val==NULL) { alarms_val=val_new_value(); assert(alarms_val != NULL); val_init_from_template(alarms_val, alarms_obj); val_add_child(alarms_val, runningcfg->root); } alarm_list_obj = obj_find_child(alarms_obj, "ietf-alarms", "alarm-list"); assert(alarm_list_obj); alarm_list_val=val_new_value(); assert(alarm_list_val != NULL); val_init_from_template(alarm_list_val, alarm_list_obj); val_add_child(alarm_list_val, alarms_val); number_of_alarms_obj = obj_find_child(alarm_list_obj, "ietf-alarms", "number-of-alarms"); assert(number_of_alarms_obj); number_of_alarms_val=val_new_value(); assert(number_of_alarms_val != NULL); val_init_from_template(number_of_alarms_val, number_of_alarms_obj); VAL_UINT32(number_of_alarms_val)=0; val_add_child(number_of_alarms_val, alarm_list_val); return res; } void y_ietf_alarms_cleanup (void) { } yuma123_2.14/example-modules/ietf-alarms/alarmctrl.c0000664000175000017500000002310414770023131022555 0ustar vladimirvladimir#include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" #include "tstamp.h" #include #include /* create: * resource (key) * alarm-type-id (key) * alarm-type-qualifier (key) * time-created //resource-alarm-parameters * is-cleared * last-changed * perceived-severity * alarm-text * list state-change ** time ** perceived-severity {type severity-with-clear!: union of perceived-severity and is-cleared above} ** alarm-text */ static val_value_t* find_alarm(val_value_t* alarm_list_val, char* resource_str, char* alarm_type_id_str, char* alarm_type_qualifier_str) { status_t res; val_value_t* alarm_val; val_value_t* resource_val; val_value_t* alarm_type_id_val; val_value_t* alarm_type_id_match_val; val_value_t* alarm_type_qualifier_val; for (alarm_val = val_find_child(alarm_list_val,"ietf-alarms","alarm"); alarm_val != NULL; alarm_val = val_find_next_child(alarm_list_val, "ietf-alarms", "alarm", alarm_val)) { resource_val = val_find_child(alarm_val, "ietf-alarms", "resource"); alarm_type_id_val = val_find_child(alarm_val, "ietf-alarms", "alarm-type-id"); alarm_type_qualifier_val = val_find_child(alarm_val, "ietf-alarms", "alarm-type-qualifier"); alarm_type_id_match_val=val_new_value(); assert(alarm_type_id_match_val != NULL); val_init_from_template(alarm_type_id_match_val, alarm_type_id_val->obj); res = val_set_simval_obj(alarm_type_id_match_val, alarm_type_id_val->obj, alarm_type_id_str); assert(res==NO_ERR); if(0==strcmp(VAL_STRING(resource_val),resource_str) && //0==strcmp(VAL_STRING(alarm_type_id_val),alarm_type_id_str) && 0==val_compare(alarm_type_id_match_val,alarm_type_id_val) && 0==strcmp(VAL_STRING(alarm_type_qualifier_val),alarm_type_qualifier_str) ) { val_free_value(alarm_type_id_match_val); break; } val_free_value(alarm_type_id_match_val); } return alarm_val; } static val_value_t* create_alarm(val_value_t* alarm_list_val, char* resource_str, char* alarm_type_id_str, char* alarm_type_qualifier_str, char* perceived_severity_str, char* alarm_text_str) { obj_template_t* alarm_obj; obj_template_t* resource_obj; obj_template_t* alarm_type_id_obj; obj_template_t* alarm_type_qualifier_obj; obj_template_t* time_created_obj; obj_template_t* is_cleared_obj; obj_template_t* last_changed_obj; obj_template_t* perceived_severity_obj; obj_template_t* alarm_text_obj; val_value_t* alarm_val; val_value_t* resource_val; val_value_t* alarm_type_id_val; val_value_t* alarm_type_qualifier_val; val_value_t* time_created_val; val_value_t* is_cleared_val; val_value_t* last_changed_val; val_value_t* perceived_severity_val; val_value_t* alarm_text_val; val_value_t* number_of_alarms_val; char tstamp_buf[21]; status_t res; /*alarm*/ alarm_obj = obj_find_child(alarm_list_val->obj, "ietf-alarms", "alarm"); assert(alarm_obj); alarm_val=val_new_value(); assert(alarm_val != NULL); val_init_from_template(alarm_val, alarm_obj); val_add_child(alarm_val, alarm_list_val); /*resource*/ resource_obj = obj_find_child(alarm_obj, "ietf-alarms", "resource"); assert(resource_obj); resource_val=val_new_value(); assert(resource_val != NULL); val_init_from_template(resource_val, resource_obj); res = val_set_simval_obj(resource_val, resource_obj, resource_str); val_add_child(resource_val, alarm_val); /*alarm-type-id*/ alarm_type_id_obj = obj_find_child(alarm_obj, "ietf-alarms", "alarm-type-id"); assert(alarm_type_id_obj); alarm_type_id_val=val_new_value(); assert(alarm_type_id_val != NULL); val_init_from_template(alarm_type_id_val, alarm_type_id_obj); res = val_set_simval_obj(alarm_type_id_val, alarm_type_id_obj, alarm_type_id_str); val_add_child(alarm_type_id_val, alarm_val); /*alarm-type-qualifier*/ alarm_type_qualifier_obj = obj_find_child(alarm_obj, "ietf-alarms", "alarm-type-qualifier"); assert(alarm_type_qualifier_obj); alarm_type_qualifier_val=val_new_value(); assert(alarm_type_qualifier_val != NULL); val_init_from_template(alarm_type_qualifier_val, alarm_type_qualifier_obj); res = val_set_simval_obj(alarm_type_qualifier_val, alarm_type_qualifier_obj, alarm_type_qualifier_str); val_add_child(alarm_type_qualifier_val, alarm_val); /*time-created*/ tstamp_datetime (tstamp_buf); time_created_obj = obj_find_child(alarm_obj, "ietf-alarms", "time-created"); assert(time_created_obj); time_created_val=val_new_value(); assert(time_created_val != NULL); val_init_from_template(time_created_val, time_created_obj); res = val_set_simval_obj(time_created_val, time_created_obj, tstamp_buf); val_add_child(time_created_val, alarm_val); /*is-cleared*/ is_cleared_obj = obj_find_child(alarm_obj, "ietf-alarms", "is-cleared"); assert(is_cleared_obj); is_cleared_val=val_new_value(); assert(is_cleared_val != NULL); val_init_from_template(is_cleared_val, is_cleared_obj); VAL_BOOL(is_cleared_val)=FALSE; val_add_child(is_cleared_val, alarm_val); /*last-changed*/ last_changed_obj = obj_find_child(alarm_obj, "ietf-alarms", "last-changed"); assert(last_changed_obj); last_changed_val=val_new_value(); assert(last_changed_val != NULL); val_init_from_template(last_changed_val, last_changed_obj); res = val_set_simval_obj(last_changed_val, last_changed_obj, tstamp_buf); val_add_child(last_changed_val, alarm_val); /*perceived-severity*/ perceived_severity_obj = obj_find_child(alarm_obj, "ietf-alarms", "perceived-severity"); assert(perceived_severity_obj); perceived_severity_val=val_new_value(); assert(perceived_severity_val != NULL); val_init_from_template(perceived_severity_val, perceived_severity_obj); res = val_set_simval_obj(perceived_severity_val, perceived_severity_obj, perceived_severity_str); val_add_child(perceived_severity_val, alarm_val); /* update ../number-of-alarms */ number_of_alarms_val = val_find_child(alarm_list_val,"ietf-alarms","number-of-alarms"); assert(number_of_alarms_val); VAL_UINT32(number_of_alarms_val)+=1; } static void update_alarm(val_value_t* alarm_val, char* severity_str, char* alarm_text_str, int enable) { val_value_t* is_cleared_val; val_value_t* last_changed_val; char tstamp_buf[21]; status_t res; /*is-cleared*/ is_cleared_val=val_find_child(alarm_val,"ietf-alarms","is-cleared"); assert(is_cleared_val != NULL); VAL_BOOL(is_cleared_val)=enable?FALSE:TRUE; /*last-changed*/ tstamp_datetime (tstamp_buf); last_changed_val=val_find_child(alarm_val,"ietf-alarms","last-changed"); assert(last_changed_val != NULL); res = val_set_simval_obj(last_changed_val, last_changed_val->obj, tstamp_buf); } int alarmctrl_event(char* resource_str, char* alarm_type_id_str, char* alarm_type_qualifier_str, char* severity_str, char* alarm_text_str, int enable) { cfg_template_t* runningcfg; val_value_t* alarms_val; val_value_t* alarm_list_val; val_value_t* alarm_val; printf("%s: resource=%s - alarm-type-id=%s - alarm-type-qualifier=%s - severity=%s\n",__FUNCTION__, resource_str, alarm_type_id_str, alarm_type_qualifier_str, severity_str, alarm_text_str, enable?"on":"off"); runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING); assert(runningcfg!=NULL && runningcfg->root!=NULL); alarms_val = val_find_child(runningcfg->root, "ietf-alarms", "alarms"); if(alarms_val==NULL) { return -1; } alarm_list_val = val_find_child(alarms_val, "ietf-alarms", "alarm-list"); if(alarm_list_val==NULL) { return -1; } alarm_val = find_alarm(alarm_list_val, resource_str, alarm_type_id_str, alarm_type_qualifier_str); if(enable) { if(alarm_val==NULL) { alarm_val = create_alarm(alarm_list_val, resource_str, alarm_type_id_str, alarm_type_qualifier_str, severity_str, alarm_text_str); } else { update_alarm(alarm_val, severity_str, alarm_text_str, enable); } } else { if(alarm_val==NULL) { return -1; } update_alarm(alarm_val, severity_str, alarm_text_str, enable); } return 0; } yuma123_2.14/example-modules/ietf-alarms/Makefile.am0000775000175000017500000000130614770023131022467 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libietf-alarms.la libietf_alarms_la_SOURCES = \ ietf-alarms.c libietf_alarms_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libietf_alarms_la_LDFLAGS = -module -lyumaagt -lyumancx -lz $(LIBS) #ietf_draft_yang_DATA = \ #ietf-alarms@2016-10-27.yang \ #ietf-alarms-x733@2016-10-05.yang #public alarm control library lib_LTLIBRARIES = libalarmctrl.la libalarmctrl_la_SOURCES = \ alarmctrl.c libalarmctrl_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libalarmctrl_la_LDFLAGS = -module -lyumaagt -lyumancx include_HEADERS= \ alarmctrl.h yuma123_2.14/example-modules/ietf-alarms/alarmctrl.h0000664000175000017500000000023014770023131022555 0ustar vladimirvladimirint alarmctrl_event(char* resource_str, char* alarm_type_id_str, char* alarm_type_qualifier_str, char* severity_str, char* alarm_text_str, int enable); yuma123_2.14/example-modules/ietf-alarms/configure.ac0000664000175000017500000000122214770023131022713 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-ietf-alarms], [2.14], [vladimir@lightide-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) ietf_draft_yangdir="$prefix/share/yuma/modules/ietf-draft" AC_SUBST(ietf_draft_yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/example-modules/chat/0000775000175000017500000000000014770023131017143 5ustar vladimirvladimiryuma123_2.14/example-modules/chat/chat.c0000664000175000017500000000604214770023131020230 0ustar vladimirvladimir/* module chat (based on chat.yang) */ #define __USE_XOPEN 1 //#define _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include #include "procdefs.h" #include "agt.h" #include "agt_cb.h" #include "agt_timer.h" #include "agt_util.h" #include "agt_not.h" #include "agt_rpc.h" #include "dlq.h" #include "ncx.h" #include "ncxmod.h" #include "ncxtypes.h" #include "status.h" #include "rpc.h" /* module static variables */ static ncx_module_t *chat_mod; /******************************************************************** * FUNCTION y_chat_send_message_invoke * * RPC invocation phase * All constraints have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_chat_send_message_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { val_value_t* input_text_val=NULL; val_value_t* text_val=NULL; status_t res; obj_template_t* notification_obj; obj_template_t* text_obj; agt_not_msg_t *notif; res = NO_ERR; /* remove the next line if scb is used */ (void)scb; /* remove the next line if methnode is used */ (void)methnode; /* invoke your device instrumentation code here */ input_text_val = val_find_child( msg->rpc_input, "chat", "text"); assert(input_text_val); printf("message is: %s\n", VAL_STRING(input_text_val)); notification_obj = ncx_find_object( chat_mod, "message"); assert(notification_obj); notif = agt_not_new_notification(notification_obj); assert (notif != NULL); /* add params to payload */ text_obj = obj_find_child(notification_obj, "chat", "text"); assert(text_obj); text_val = val_new_value(); assert(text_val); val_init_from_template(text_val, text_obj); res = val_set_simval_obj(text_val, text_val->obj, VAL_STRING(input_text_val)); agt_not_add_to_payload(notif, text_val); agt_not_queue_notification(notif); return res; } /* The 3 mandatory callback functions: y_chat_init, y_chat_init2, y_chat_cleanup */ status_t y_chat_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; agt_profile = agt_get_profile(); res = ncxmod_load_module( "chat", NULL, &agt_profile->agt_savedevQ, &chat_mod); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( "chat", "send-message", AGT_RPC_PH_INVOKE, y_chat_send_message_invoke); if (res != NO_ERR) { return res; } return res; } status_t y_chat_init2(void) { return NO_ERR; } void y_chat_cleanup(void) { } yuma123_2.14/example-modules/chat/Makefile.am0000775000175000017500000000042314770023131021201 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libchat.la libchat_la_SOURCES = \ chat.c libchat_la_CPPFLAGS = -I${includedir}/yuma/agt -I${includedir}/yuma/mgr -I${includedir}/yuma/ncx -I${includedir}/yuma/platform libchat_la_LDFLAGS = -module -lyumaagt -lyumancx dist_yang_DATA = chat.yang yuma123_2.14/example-modules/chat/chat.yang0000664000175000017500000000120614770023131020741 0ustar vladimirvladimirmodule chat { namespace "http://yuma123.org/ns/chat"; prefix "chat"; organization "yuma123.org"; description "chat module"; revision 2016-10-17 { description "Initial revision"; } rpc send-message { description "Sends message."; input { leaf text { type string; mandatory true; description "Text of the message."; } } } notification message { description "This notification is used to report a message."; leaf text { type string; mandatory true; description "Text of the message."; } } } yuma123_2.14/example-modules/chat/configure.ac0000664000175000017500000000116614770023131021435 0ustar vladimirvladimirAC_INIT([yuma123-netconfd-module-ietf-interfaces], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([-Werror foreign]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yangdir="$prefix/share/yuma/modules" AC_SUBST(yangdir) ncx_netconf_includedir="$includedir/yuma/ncx" AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir="$includedir/yuma/agt" AC_SUBST(agt_netconf_includedir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1" AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT yuma123_2.14/Makefile.am0000664000175000017500000001321714770023131015163 0ustar vladimirvladimirSUBDIRS=\ netconf/src/ncx \ netconf/src/mgr \ netconf/src/agt \ netconf/src/netconfd \ netconf/src/subsys \ netconf/src/yangcli \ libtoaster/src \ netconf/modules \ example-modules/helloworld \ example-modules/ietf-interfaces \ example-modules/ietf-system \ example-modules/yangcli-to-rpc \ netconf/src/yangrpc \ netconf/src/yangrpc/example \ netconf/man \ netconf/src/yangdump #example-modules/ietf-network-bridge #netconf/src/yangdiff if WITH_TECLA SUBDIRS+=libtecla endif platform_netconf_include_HEADERS= \ $(top_srcdir)/netconf/src/platform/procdefs.h ncx_netconf_include_HEADERS= \ $(top_srcdir)/netconf/src/ncx/ncxconst.h \ $(top_srcdir)/netconf/src/ncx/val_util.h \ $(top_srcdir)/netconf/src/ncx/getcb.h \ $(top_srcdir)/netconf/src/ncx/rpc_err.h \ $(top_srcdir)/netconf/src/ncx/yinyang.h \ $(top_srcdir)/netconf/src/ncx/ncx.h \ $(top_srcdir)/netconf/src/ncx/bobhash.h \ $(top_srcdir)/netconf/src/ncx/log.h \ $(top_srcdir)/netconf/src/ncx/grp.h \ $(top_srcdir)/netconf/src/ncx/tstamp.h \ $(top_srcdir)/netconf/src/ncx/xpath_yang.h \ $(top_srcdir)/netconf/src/ncx/status.h \ $(top_srcdir)/netconf/src/ncx/yang_obj.h \ $(top_srcdir)/netconf/src/ncx/ncx_str.h \ $(top_srcdir)/netconf/src/ncx/yang_ext.h \ $(top_srcdir)/netconf/src/ncx/conf.h \ $(top_srcdir)/netconf/src/ncx/b64.h \ $(top_srcdir)/netconf/src/ncx/ncxmod.h \ $(top_srcdir)/netconf/src/ncx/top.h \ $(top_srcdir)/netconf/src/ncx/obj.h \ $(top_srcdir)/netconf/src/ncx/json_wr.h \ $(top_srcdir)/netconf/src/ncx/xml_wr.h \ $(top_srcdir)/netconf/src/ncx/xml_rd.h \ $(top_srcdir)/netconf/src/ncx/def_reg.h \ $(top_srcdir)/netconf/src/ncx/ncx_num.h \ $(top_srcdir)/netconf/src/ncx/op.h \ $(top_srcdir)/netconf/src/ncx/xpath1.h \ $(top_srcdir)/netconf/src/ncx/ses.h \ $(top_srcdir)/netconf/src/ncx/yin.h \ $(top_srcdir)/netconf/src/ncx/runstack.h \ $(top_srcdir)/netconf/src/ncx/val.h \ $(top_srcdir)/netconf/src/ncx/val_set_cplxval_obj.h \ $(top_srcdir)/netconf/src/ncx/val_get_leafref_targval.h \ $(top_srcdir)/netconf/src/ncx/version.h \ $(top_srcdir)/netconf/src/ncx/ext.h \ $(top_srcdir)/netconf/src/ncx/yangconst.h \ $(top_srcdir)/netconf/src/ncx/yang.h \ $(top_srcdir)/netconf/src/ncx/send_buff.h \ $(top_srcdir)/netconf/src/ncx/plock.h \ $(top_srcdir)/netconf/src/ncx/ncx_list.h \ $(top_srcdir)/netconf/src/ncx/ncxtypes.h \ $(top_srcdir)/netconf/src/ncx/xmlns.h \ $(top_srcdir)/netconf/src/ncx/yang_typ.h \ $(top_srcdir)/netconf/src/ncx/tk.h \ $(top_srcdir)/netconf/src/ncx/cli.h \ $(top_srcdir)/netconf/src/ncx/plock_cb.h \ $(top_srcdir)/netconf/src/ncx/dlq.h \ $(top_srcdir)/netconf/src/ncx/ncx_feature.h \ $(top_srcdir)/netconf/src/ncx/var.h \ $(top_srcdir)/netconf/src/ncx/blob.h \ $(top_srcdir)/netconf/src/ncx/obj_help.h \ $(top_srcdir)/netconf/src/ncx/xml_msg.h \ $(top_srcdir)/netconf/src/ncx/cap.h \ $(top_srcdir)/netconf/src/ncx/yang_grp.h \ $(top_srcdir)/netconf/src/ncx/typ.h \ $(top_srcdir)/netconf/src/ncx/xpath.h \ $(top_srcdir)/netconf/src/ncx/ses_msg.h \ $(top_srcdir)/netconf/src/ncx/ncx_appinfo.h \ $(top_srcdir)/netconf/src/ncx/xml_util.h \ $(top_srcdir)/netconf/src/ncx/xpath_wr.h \ $(top_srcdir)/netconf/src/ncx/rpc.h \ $(top_srcdir)/netconf/src/ncx/xml_val.h \ $(top_srcdir)/netconf/src/ncx/cfg.h \ $(top_srcdir)/netconf/src/ncx/help.h \ $(top_srcdir)/netconf/src/ncx/yang_parse.h \ $(top_srcdir)/netconf/src/ncx/libncx.h \ $(top_srcdir)/netconf/src/ncx/val123.h \ $(top_srcdir)/netconf/src/ncx/val_parse.h \ $(top_srcdir)/netconf/src/ncx/uptime.h agt_netconf_include_HEADERS= \ $(top_srcdir)/netconf/src/agt/agt_val_parse.h \ $(top_srcdir)/netconf/src/agt/agt_rpcerr.h \ $(top_srcdir)/netconf/src/agt/agt_connect.h \ $(top_srcdir)/netconf/src/agt/agt_rpc.h \ $(top_srcdir)/netconf/src/agt/agt_cb.h \ $(top_srcdir)/netconf/src/agt/agt_hello.h \ $(top_srcdir)/netconf/src/agt/agt_acm.h \ $(top_srcdir)/netconf/src/agt/agt_sys.h \ $(top_srcdir)/netconf/src/agt/agt_xml.h \ $(top_srcdir)/netconf/src/agt/agt_ncxserver.h \ $(top_srcdir)/netconf/src/agt/agt_if.h \ $(top_srcdir)/netconf/src/agt/agt_tree.h \ $(top_srcdir)/netconf/src/agt/agt_ncx.h \ $(top_srcdir)/netconf/src/agt/agt_state.h \ $(top_srcdir)/netconf/src/agt/agt_time_filter.h \ $(top_srcdir)/netconf/src/agt/agt_cap.h \ $(top_srcdir)/netconf/src/agt/agt_plock.h \ $(top_srcdir)/netconf/src/agt/agt_signal.h \ $(top_srcdir)/netconf/src/agt/agt_val.h \ $(top_srcdir)/netconf/src/agt/agt_xpath.h \ $(top_srcdir)/netconf/src/agt/agt_proc.h \ $(top_srcdir)/netconf/src/agt/agt_not.h \ $(top_srcdir)/netconf/src/agt/agt_timer.h \ $(top_srcdir)/netconf/src/agt/agt_util.h \ $(top_srcdir)/netconf/src/agt/agt_ses.h \ $(top_srcdir)/netconf/src/agt/agt.h \ $(top_srcdir)/netconf/src/agt/agt_top.h \ $(top_srcdir)/netconf/src/agt/agt_cli.h \ $(top_srcdir)/netconf/src/agt/agt_commit_complete.h \ $(top_srcdir)/netconf/src/agt/agt_commit_validate.h \ $(top_srcdir)/netconf/src/agt/agt_not_queue_notification_cb.h \ $(top_srcdir)/netconf/src/agt/agt_nmda.h \ $(top_srcdir)/netconf/src/agt/agt_cfg.h \ $(top_srcdir)/netconf/src/agt/agt_yang_library.h \ $(top_srcdir)/netconf/src/agt/agt_fd_event_cb.h mgr_netconf_include_HEADERS= \ $(top_srcdir)/netconf/src/mgr/mgr.h \ $(top_srcdir)/netconf/src/mgr/mgr_hello.h \ $(top_srcdir)/netconf/src/mgr/mgr_io.h \ $(top_srcdir)/netconf/src/mgr/mgr_not.h \ $(top_srcdir)/netconf/src/mgr/mgr_rpc.h \ $(top_srcdir)/netconf/src/mgr/mgr_ses.h \ $(top_srcdir)/netconf/src/mgr/mgr_val_parse.h \ $(top_srcdir)/netconf/src/mgr/mgr_xml.h \ $(top_srcdir)/netconf/src/mgr/mgr_cap.h \ $(top_srcdir)/netconf/src/mgr/mgr_signal.h \ $(top_srcdir)/netconf/src/mgr/mgr_load.h \ $(top_srcdir)/netconf/src/mgr/mgr_top.h yangrpc_netconf_include_HEADERS= \ $(top_srcdir)/netconf/src/yangrpc/yangrpc.h EXTRA_DIST= \ netconf/src/yangdiff \ example-modules/ietf-interfaces \ example-modules/ietf-system \ rpm yuma123_2.14/TODO0000664000175000017500000000353614770023131013622 0ustar vladimirvladimiryuma123 to-do List (2017-01-31) This file contains the list of known items not finished in the source code. -- ietf-yang-library.yang implementation -- YANG 1.1 support (RFC 7950) -- new Xpath functions - re-match, deref, enum-value, bit-is-set -- if-feature in identities, enum, bit -- action statement -- anydata statement -- modifier statement -- ... to be continued -- yangcli to-do: -- command completion for more data types -- command completion based on mirror of database -- server bugs: -- ietf-netconf.yang not used internally. hacks are used to advertise ietf-netconf but use yuma-netconf. An 'annotation' feature like deviation-stmt is needed to split out the implementation extensions and other YANG statements from the standard module. -- features are not advertised for ietf-netconf, just capabilities -- openssh client hello bug: the client hello code is not used; not sure why the SSH application timeout occurs even if there is NETCONF traffic; must be doing something wrong in the client or server SSH code. -- load module chicken-and-egg bug: - load a module dynamically and then save some config in in the newly loaded module namespace. Since there is no 'load foo' added to the netconfd CLI or .conf file, this new config will be treated as unknown namespace error on the next reboot -- SIL user callback functions do not yet support a 'PASS' mode, so that multiple callback handlers within the same YANG data structure can be utilized. -- server optimizations not done: -- save to NVstore: -- user hook to override output to XML file -- write /config/foo in sub-directories instead of XML. Start the XML file(s) for the next layer. /config/foo/bar.xml yuma123_2.14/rpm/0000775000175000017500000000000014770023131013721 5ustar vladimirvladimiryuma123_2.14/rpm/yuma123.spec.in0000664000175000017500000001351114770023131016404 0ustar vladimirvladimirName: @PACKAGE@ Version: @VERSION@ Release: 1%{?dist} Summary: YANG API in C and YANG based cli License: multiple URL: https://sourceforge.net/projects/yuma123 Source0: %{name}-%{version}.tar.gz BuildRequires: autoconf BuildRequires: libtool BuildRequires: ncurses-devel BuildRequires: readline-devel BuildRequires: zlib-devel BuildRequires: libxml2-devel BuildRequires: libssh2-devel %description The purpose of the yuma123 project is to provide an opensource YANG API in C and YANG based cli (yangcli) and server (netconfd) appications. %prep %setup -q %build %configure make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT %make_install rm -f %{buildroot}%{_libdir}/yuma/libhelloworld.la rm -f %{buildroot}%{_libdir}/yuma/libietf-interfaces.la rm -f %{buildroot}%{_libdir}/yuma/libietf-system.la rm -f %{buildroot}%{_libdir}/yuma/libtoaster.la # The base RPM is equivalent to the libyuma-base debian package %files %{_datarootdir}/yuma/modules/examples/helloworld.yang %{_datarootdir}/yuma/modules/examples/example-bridge.yang %{_datarootdir}/yuma/modules/ietf/* %{_datarootdir}/yuma/modules/ietf-derived/* %{_datarootdir}/yuma/modules/ietf-draft/* %{_datarootdir}/yuma/modules/ietf-expired/* %{_datarootdir}/yuma/modules/netconfcentral/* %{_datarootdir}/yuma/modules/yuma123/* %{_datarootdir}/yuma/nmda-modules/ietf/* %exclude %{_libdir}/yuma/libietf-network-bridge* %exclude %{_libdir}/yuma/libyangcli-to-rpc* %package libyangrpc2 Summary: Netconf/YANG library for simple manager clients %description libyangrpc2 The Netconf protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. The yuma123 yangrpc library contains common functions for the construction, sending, receiving, decoding, and manipulation of the netconf requests and responses used on simple manager clients. %files libyangrpc2 %{_bindir}/yangrpc-* %{_libdir}/libyangrpc*.so.* %package libyangrpc2-devel Summary: Netconf/YANG development files %description libyangrpc2-devel The yuma123 libyangrpc development files include the library headers, static libraries, and documentation needed for development of custom netconf/YANG applications. The yuma123 yangrpc library contains common functions for the construction, sending, receiving, decoding, and manipulation of the netconf requests and responses used on simple manager clients. %files libyangrpc2-devel %{_libdir}/libyangrpc*.so %{_libdir}/libyangrpc*.la %{_includedir}/yuma/yangrpc %package libyuma2 Summary: Netconf/YANG library %description libyuma2 The Netconf protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. The yuma123 libyuma library contains common functions for the construction, sending, receiving, decoding, and manipulation of the netconf requests and responses. %files libyuma2 %{_libdir}/libyuma*.so.* %package netconfd Summary: netconf (RFC-6241) agent %description netconfd Netconf provides a framework for the exchange of management information between agents (servers) and clients. The yuma123 agent is a daemon which listens for incoming netconf requests from clients and provides responses. %files netconfd %{_sbindir}/netconfd %{_sbindir}/netconf-subsystem %{_mandir}/man1/netconfd.1* %{_mandir}/man1/netconf-subsystem.1* %{_libdir}/yuma/libhelloworld.so* %{_libdir}/yuma/libtoaster.so* %package netconfd-module-ietf-interfaces Summary: SIL module for netconfd implementing ietf-interfaces.yang %description netconfd-module-ietf-interfaces The Netconf protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. The yuma123 netconfd module ietf-interfaces implements the functionality modeled in ietf-interfaces.yang using some common command line tools. %files netconfd-module-ietf-interfaces %{_libdir}/yuma/libietf-interfaces.so* %{_datarootdir}/yuma/modules/examples/interfaces-notifications.yang %package netconfd-module-ietf-system Summary: SIL module for netconfd implementing ietf-system.yang %description netconfd-module-ietf-system The Netconf protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. The yuma123 netconfd module ietf-interfaces implements the functionality modeled in ietf-system.yang using some common command line tools. %files netconfd-module-ietf-system %{_libdir}/yuma/libietf-system.so* %package yangcli Summary: netconf/YANG command line client application %description yangcli The netconf protocol and the YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. The yuma123 yangcli application allows the user to issue netconf requests to agents according to the specific YANG models of the agents. %files yangcli %{_bindir}/yangcli %{_mandir}/man1/yangcli.1* %package yangdump Summary: validate YANG modules and convert them to different formats %description yangdump yangdump provides validation and translation of YANG data models. Information about a module or submodule can be generated as well. %files yangdump %{_bindir}/yangdump %{_mandir}/man1/yangdump.1* %package libyuma2-devel Summary: Netconf/YANG development files %description libyuma2-devel The Netconf protocol and YANG modeling language provide a framework for the exchange of management information between agents (servers) and clients. The yuma123 libyuma development files include the library headers, static libraries, and documentation needed for development of custom netconf/YANG applications. %files libyuma2-devel %defattr(-,root,root) %{_includedir}/yuma/agt %{_includedir}/yuma/mgr %{_includedir}/yuma/ncx %{_includedir}/yuma/platform %{_libdir}/libyuma*.la %{_libdir}/libyuma*.so %changelog yuma123_2.14/configure.ac0000664000175000017500000000722214770023131015414 0ustar vladimirvladimirAC_INIT([yuma123], [2.14], [vladimir@lightside-instruments.com]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([foreign subdir-objects]) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_CXX AM_PROG_AS PKG_PROG_PKG_CONFIG AC_PROG_LIBTOOL AC_SEARCH_LIBS([dlopen], [dl dld], [], [ AC_MSG_ERROR([unable to find the dlopen() function]) ]) netconfmoduledir="$libdir/yuma" AC_SUBST(netconfmoduledir) yuma_datarootdir=${datarootdir}/yuma AC_SUBST(yuma_datarootdir) yangdir=${datarootdir}/yuma/modules AC_SUBST(yangdir) ietf_yangdir=${datarootdir}/yuma/modules/ietf AC_SUBST(ietf_yangdir) nmda_modules_ietf_yangdir=${datarootdir}/yuma/nmda-modules/ietf AC_SUBST(nmda_modules_ietf_yangdir) ietf_draft_yangdir=${datarootdir}/yuma/modules/ietf-draft AC_SUBST(ietf_draft_yangdir) ietf_patched_yangdir=${datarootdir}/yuma/modules/ietf-patched AC_SUBST(ietf_patched_yangdir) ietf_derived_yangdir=${datarootdir}/yuma/modules/ietf-derived AC_SUBST(ietf_derived_yangdir) ietf_expired_yangdir=${datarootdir}/yuma/modules/ietf-expired AC_SUBST(ietf_expired_yangdir) netconfcentral_yangdir=${datarootdir}/yuma/modules/netconfcentral AC_SUBST(netconfcentral_yangdir) yuma123_yangdir=${datarootdir}/yuma/modules/yuma123 AC_SUBST(yuma123_yangdir) examples_yangdir="$prefix/share/yuma/modules/examples" AC_SUBST(examples_yangdir) ncx_netconf_includedir=${includedir}/yuma/ncx AC_SUBST(ncx_netconf_includedir) agt_netconf_includedir=${includedir}/yuma/agt AC_SUBST(agt_netconf_includedir) mgr_netconf_includedir=${includedir}/yuma/mgr AC_SUBST(mgr_netconf_includedir) yangrpc_netconf_includedir=${includedir}/yuma/yangrpc AC_SUBST(yangrpc_netconf_includedir) platform_netconf_includedir=${includedir}/yuma/platform AC_SUBST(platform_netconf_includedir) apachemoduledir="$libdir/apache2/modules" AC_SUBST(apachemoduledir) CFLAGS="$CFLAGS -g -fPIC -DDEBUG=1 -DLINUX=1 -DGCC=1 -DRELEASE=0 -DENABLE_DIRECT_MUST_AUGMENT_EX=1 -DHAS_FLOAT=1" AC_SUBST(CFLAGS) #libreadline or libtecla AC_ARG_WITH(readline, [AS_HELP_STRING([--with-readline], [Use readline instead of tecla for interctive commandline yangcli input])], [READLINE="1"],[]) # [AC_MSG_ERROR([--with-readline option is mandatory])]) AM_CONDITIONAL([WITH_READLINE], [test "x$READLINE" = x1]) AC_ARG_WITH(tecla, [AS_HELP_STRING([--with-tecla], [Use tecla instead of readline for interctive commandline yangcli input])], [TECLA="1"],[]) AM_CONDITIONAL([WITH_TECLA], [test "x$TECLA" = x1]) AM_CONDITIONAL([STANDALONE], [test x$standalone = xtrue]) PKG_CHECK_MODULES([LIBXML2], [libxml-2.0]) XML_LIBS="$LIBXML2_LIBS" XML_CFLAGS="$LIBXML2_CFLAGS -DLIBXML2_ENABLED" XML_CPPFLAGS="$LIBXML2_CFLAGS -DLIBXML2_ENABLED" AC_SUBST([XML_LIBS]) AC_SUBST([XML_CPPFLAGS]) AC_CONFIG_FILES([ Makefile \ libtecla/Makefile netconf/src/netconfd/Makefile netconf/src/agt/Makefile netconf/modules/Makefile netconf/src/mgr/Makefile netconf/src/ncx/Makefile netconf/src/yangcli/Makefile netconf/src/subsys/Makefile libtoaster/src/Makefile example-modules/helloworld/Makefile example-modules/ietf-interfaces/Makefile example-modules/ietf-system/Makefile example-modules/yangcli-to-rpc/Makefile netconf/src/yangrpc/Makefile netconf/src/yangrpc/example/Makefile netconf/src/yangdump/Makefile netconf/man/Makefile rpm/yuma123.spec ]) # Disabled targets # netconf/src/yangdiff/Makefile # example-modules/ietf-network-bridge/Makefile # Test framework # netconf/test/Makefile # netconf/test/sys-test/Makefile # netconf/test/integ-tests/Makefile # netconf/test/sys-test-python/Makefile AC_OUTPUT yuma123_2.14/libtoaster/0000775000175000017500000000000014770023131015273 5ustar vladimirvladimiryuma123_2.14/libtoaster/src/0000775000175000017500000000000014770023131016062 5ustar vladimirvladimiryuma123_2.14/libtoaster/src/toaster.c.start0000664000175000017500000004572214770023131021055 0ustar vladimirvladimir /* *** Generated by yangdump 0.9.8.519M *** Copyright (c) 2009, Netconf Central, Inc., All Rights Reserved. module toaster revision 2009-11-20 namespace http://netconfcentral.com/ns/toaster organization Netconf Central, Inc. */ #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_agt #include "agt.h" #endif #ifndef _H_agt_cb #include "agt_cb.h" #endif #ifndef _H_agt_not #include "agt_not.h" #endif #ifndef _H_agt_rpc #include "agt_rpc.h" #endif #ifndef _H_agt_util #include "agt_util.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_toaster #include "toaster.h" #endif /* module static variables */ static ncx_module_t *toaster_mod; static obj_template_t *toaster_obj; static obj_template_t *make_toast_obj; static obj_template_t *cancel_toast_obj; static obj_template_t *toastDone_obj; static val_value_t *toaster_val; /* put your static variables here */ /******************************************************************** * FUNCTION y_toaster_toaster_toasterManufacturer_get * * Get database object callback * Path: /toaster/toasterManufacturer * Fill in 'dstval' contents * * INPUTS: * see ncx/getcb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_toaster_toasterManufacturer_get ( ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { status_t res; const xmlChar *toasterManufacturer; res = NO_ERR; /* remove the next line if scb is used */ (void)scb; /* remove the next line if virval is used */ (void)virval; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } /* set the toasterManufacturer var here, change EMPTY_STRING */ toasterManufacturer = EMPTY_STRING; res = val_set_simval_obj( dstval, dstval->obj, toasterManufacturer); return res; } /* y_toaster_toaster_toasterManufacturer_get */ /******************************************************************** * FUNCTION y_toaster_toaster_toasterModelNumber_get * * Get database object callback * Path: /toaster/toasterModelNumber * Fill in 'dstval' contents * * INPUTS: * see ncx/getcb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_toaster_toasterModelNumber_get ( ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { status_t res; const xmlChar *toasterModelNumber; res = NO_ERR; /* remove the next line if scb is used */ (void)scb; /* remove the next line if virval is used */ (void)virval; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } /* set the toasterModelNumber var here, change EMPTY_STRING */ toasterModelNumber = EMPTY_STRING; res = val_set_simval_obj( dstval, dstval->obj, toasterModelNumber); return res; } /* y_toaster_toaster_toasterModelNumber_get */ /******************************************************************** * FUNCTION y_toaster_toaster_toasterStatus_get * * Get database object callback * Path: /toaster/toasterStatus * Fill in 'dstval' contents * * INPUTS: * see ncx/getcb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_toaster_toasterStatus_get ( ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { status_t res; const xmlChar *toasterStatus; res = NO_ERR; /* remove the next line if scb is used */ (void)scb; /* remove the next line if virval is used */ (void)virval; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } /* set the toasterStatus var here, change enum */ toasterStatus = (const xmlChar *)"up"; res = val_set_simval_obj( dstval, dstval->obj, toasterStatus); return res; } /* y_toaster_toaster_toasterStatus_get */ /******************************************************************** * FUNCTION y_toaster_toaster_mro * * Make read-only child nodes * Path: /toaster * * INPUTS: * parentval == the parent struct to use for new child nodes * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_toaster_mro (val_value_t *parentval) { status_t res; val_value_t *childval; res = NO_ERR; /* add /toaster/toasterManufacturer */ childval = agt_make_virtual_leaf( parentval->obj, y_toaster_N_toasterManufacturer, y_toaster_toaster_toasterManufacturer_get, &res); if (childval != NULL) { val_add_child(childval, parentval); } else { return res; } /* add /toaster/toasterModelNumber */ childval = agt_make_virtual_leaf( parentval->obj, y_toaster_N_toasterModelNumber, y_toaster_toaster_toasterModelNumber_get, &res); if (childval != NULL) { val_add_child(childval, parentval); } else { return res; } /* add /toaster/toasterStatus */ childval = agt_make_virtual_leaf( parentval->obj, y_toaster_N_toasterStatus, y_toaster_toaster_toasterStatus_get, &res); if (childval != NULL) { val_add_child(childval, parentval); } else { return res; } return res; } /* y_toaster_toaster_mro */ /******************************************************************** * FUNCTION y_toaster_toaster_edit * * Edit database object callback * Path: /toaster * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_toaster_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: break; case OP_EDITOP_DELETE: break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res == NO_ERR) { res = agt_check_cache( &toaster_val, newval, curval, editop); } if (res == NO_ERR && (editop == OP_EDITOP_LOAD || editop == OP_EDITOP_CREATE)) { res = y_toaster_toaster_mro(newval); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_toaster_toaster_edit */ /******************************************************************** * FUNCTION y_toaster_make_toast_validate * * RPC validation phase * All YANG constriants have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_make_toast_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; val_value_t *errorval; const xmlChar *errorstr; val_value_t *toasterDoneness_val; val_value_t *toasterToastType_val; uint32 toasterDoneness; val_idref_t *toasterToastType; res = NO_ERR; errorval = NULL; errorstr = NULL; toasterDoneness_val = val_find_child( msg->rpc_input, y_toaster_M_toaster, y_toaster_N_toasterDoneness); if (toasterDoneness_val != NULL && toasterDoneness_val->res == NO_ERR) { toasterDoneness = VAL_UINT(toasterDoneness_val); } toasterToastType_val = val_find_child( msg->rpc_input, y_toaster_M_toaster, y_toaster_N_toasterToastType); if (toasterToastType_val != NULL && toasterToastType_val->res == NO_ERR) { toasterToastType = VAL_IDREF(toasterToastType_val); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_toaster_make_toast_validate */ /******************************************************************** * FUNCTION y_toaster_make_toast_invoke * * RPC invocation phase * All constriants have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_make_toast_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; val_value_t *toasterDoneness_val; val_value_t *toasterToastType_val; uint32 toasterDoneness; val_idref_t *toasterToastType; res = NO_ERR; toasterDoneness_val = val_find_child( msg->rpc_input, y_toaster_M_toaster, y_toaster_N_toasterDoneness); if (toasterDoneness_val != NULL && toasterDoneness_val->res == NO_ERR) { toasterDoneness = VAL_UINT(toasterDoneness_val); } toasterToastType_val = val_find_child( msg->rpc_input, y_toaster_M_toaster, y_toaster_N_toasterToastType); if (toasterToastType_val != NULL && toasterToastType_val->res == NO_ERR) { toasterToastType = VAL_IDREF(toasterToastType_val); } /* remove the next line if scb is used */ (void)scb; /* remove the next line if methnode is used */ (void)methnode; /* invoke your device instrumentation code here */ return res; } /* y_toaster_make_toast_invoke */ /******************************************************************** * FUNCTION y_toaster_cancel_toast_validate * * RPC validation phase * All YANG constriants have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_cancel_toast_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_toaster_cancel_toast_validate */ /******************************************************************** * FUNCTION y_toaster_cancel_toast_invoke * * RPC invocation phase * All constriants have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_cancel_toast_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; res = NO_ERR; /* remove the next line if scb is used */ (void)scb; /* remove the next line if msg is used */ (void)msg; /* remove the next line if methnode is used */ (void)methnode; /* invoke your device instrumentation code here */ return res; } /* y_toaster_cancel_toast_invoke */ /******************************************************************** * FUNCTION y_toaster_init_static_vars * * initialize module static variables * ********************************************************************/ static void y_toaster_init_static_vars (void) { toaster_mod = NULL; toaster_obj = NULL; make_toast_obj = NULL; cancel_toast_obj = NULL; toastDone_obj = NULL; toaster_val = NULL; /* init your static variables here */ } /* y_toaster_init_static_vars */ /******************************************************************** * FUNCTION y_toaster_toastDone_send * * Send a y_toaster_toastDone notification * Called by your code when notification event occurs * ********************************************************************/ void y_toaster_toastDone_send ( const xmlChar *toastStatus) { agt_not_msg_t *notif; val_value_t *parmval; status_t res; res = NO_ERR; if (LOGDEBUG) { log_debug("\nGenerating notification"); } notif = agt_not_new_notification(toastDone_obj); if (notif == NULL) { log_error("\nError: malloc failed, cannot send notification"); return; } /* add toastStatus to payload */ parmval = agt_make_leaf( toastDone_obj, y_toaster_N_toastStatus, toastStatus, &res); if (parmval == NULL) { log_error( "\nError: make leaf failed (%s), cannot send notification", get_error_string(res)); } else { agt_not_add_to_payload(notif, parmval); } agt_not_queue_notification(notif); } /* y_toaster_toastDone_send */ /******************************************************************** * FUNCTION y_toaster_init * * initialize the toaster server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ status_t y_toaster_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; y_toaster_init_static_vars(); /* change if custom handling done */ if (xml_strcmp(modname, y_toaster_M_toaster)) { return ERR_NCX_UNKNOWN_MODULE; } if (revision && xml_strcmp(revision, y_toaster_R_toaster)) { return ERR_NCX_WRONG_VERSION; } agt_profile = agt_get_profile(); res = ncxmod_load_module( y_toaster_M_toaster, y_toaster_R_toaster, &agt_profile->agt_savedevQ, &toaster_mod); if (res != NO_ERR) { return res; } toaster_obj = ncx_find_object( toaster_mod, y_toaster_N_toaster); if (toaster_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } make_toast_obj = ncx_find_object( toaster_mod, y_toaster_N_make_toast); if (toaster_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } cancel_toast_obj = ncx_find_object( toaster_mod, y_toaster_N_cancel_toast); if (toaster_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } toastDone_obj = ncx_find_object( toaster_mod, y_toaster_N_toastDone); if (toaster_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_make_toast, AGT_RPC_PH_VALIDATE, y_toaster_make_toast_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_make_toast, AGT_RPC_PH_INVOKE, y_toaster_make_toast_invoke); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_cancel_toast, AGT_RPC_PH_VALIDATE, y_toaster_cancel_toast_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_cancel_toast, AGT_RPC_PH_INVOKE, y_toaster_cancel_toast_invoke); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_toaster_M_toaster, (const xmlChar *)"/toaster", (const xmlChar *)"2009-11-20", y_toaster_toaster_edit); if (res != NO_ERR) { return res; } /* put your module initialization code here */ return res; } /* y_toaster_init */ /******************************************************************** * FUNCTION y_toaster_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ status_t y_toaster_init2 (void) { status_t res; res = NO_ERR; toaster_val = agt_init_cache( y_toaster_M_toaster, y_toaster_N_toaster, &res); if (res != NO_ERR) { return res; } /* put your init2 code here */ return res; } /* y_toaster_init2 */ /******************************************************************** * FUNCTION y_toaster_cleanup * cleanup the server instrumentation library * ********************************************************************/ void y_toaster_cleanup (void) { agt_rpc_unregister_method( y_toaster_M_toaster, y_toaster_N_make_toast); agt_rpc_unregister_method( y_toaster_M_toaster, y_toaster_N_cancel_toast); agt_cb_unregister_callbacks( y_toaster_M_toaster, (const xmlChar *)"/toaster"); /* put your cleanup code here */ } /* y_toaster_cleanup */ /* END toaster.c */ yuma123_2.14/libtoaster/src/toaster.c0000664000175000017500000005525514770023131017723 0ustar vladimirvladimir /* *** Generated by yangdump 0.9.8.519M *** Copyright (c) 2009, Netconf Central, Inc., All Rights Reserved. module toaster revision 2009-11-20 namespace http://netconfcentral.com/ns/toaster organization Netconf Central */ #include #ifndef _H_procdefs #include "procdefs.h" #endif #ifndef _H_agt #include "agt.h" #endif #ifndef _H_agt_cb #include "agt_cb.h" #endif #ifndef _H_agt_not #include "agt_not.h" #endif #ifndef _H_agt_rpc #include "agt_rpc.h" #endif #ifndef _H_agt_timer #include "agt_timer.h" #endif #ifndef _H_agt_util #include "agt_util.h" #endif #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncx #include "ncx.h" #endif #ifndef _H_ncxmod #include "ncxmod.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_rpc #include "rpc.h" #endif #ifndef _H_ses #include "ses.h" #endif #ifndef _H_status #include "status.h" #endif #ifndef _H_val #include "val.h" #endif #ifndef _H_val_util #include "val_util.h" #endif #ifndef _H_xml_util #include "xml_util.h" #endif #ifndef _H_toaster #include "toaster.h" #endif /* module static variables */ static ncx_module_t *toaster_mod; static obj_template_t *toaster_obj; static obj_template_t *make_toast_obj; static obj_template_t *cancel_toast_obj; static obj_template_t *toastDone_obj; static val_value_t *toaster_val; /* put your static variables here */ static boolean toaster_enabled; static boolean toaster_toasting; static uint32 toaster_duration; static uint32 toaster_timer_id; /******************************************************************** * FUNCTION toaster_timer_fn * * Added timeout function for toaster function * * INPUTS: * see agt/agt_timer.h * * RETURNS: * 0 for OK; -1 to kill periodic timer ********************************************************************/ static int toaster_timer_fn (uint32 timer_id, void *cookie) { (void)timer_id; (void)cookie; /* toast is finished */ toaster_toasting = FALSE; toaster_timer_id = 0; if (LOGDEBUG2) { log_debug2("\ntoast is finished"); } y_toaster_toastDone_send((const xmlChar *)"done"); return 0; } /* toaster_timer_fn */ /******************************************************************** * FUNCTION y_toaster_toaster_toasterManufacturer_get * * Get database object callback * Path: /toaster/toasterManufacturer * Fill in 'dstval' contents * * INPUTS: * see ncx/getcb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_toaster_toasterManufacturer_get ( ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { status_t res; const xmlChar *toasterManufacturer; res = NO_ERR; /* remove the next line if scb is used */ (void)scb; /* remove the next line if virval is used */ (void)virval; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } /* set the toasterManufacturer var here, change EMPTY_STRING */ toasterManufacturer = (const xmlChar *)"Acme, Inc."; res = val_set_simval_obj( dstval, dstval->obj, toasterManufacturer); return res; } /* y_toaster_toaster_toasterManufacturer_get */ /******************************************************************** * FUNCTION y_toaster_toaster_toasterModelNumber_get * * Get database object callback * Path: /toaster/toasterModelNumber * Fill in 'dstval' contents * * INPUTS: * see ncx/getcb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_toaster_toasterModelNumber_get ( ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { status_t res; const xmlChar *toasterModelNumber; res = NO_ERR; /* remove the next line if scb is used */ (void)scb; /* remove the next line if virval is used */ (void)virval; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } /* set the toasterModelNumber var here, change EMPTY_STRING */ toasterModelNumber = (const xmlChar *)"Super Toastamatic 2000"; res = val_set_simval_obj( dstval, dstval->obj, toasterModelNumber); return res; } /* y_toaster_toaster_toasterModelNumber_get */ /******************************************************************** * FUNCTION y_toaster_toaster_toasterStatus_get * * Get database object callback * Path: /toaster/toasterStatus * Fill in 'dstval' contents * * INPUTS: * see ncx/getcb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_toaster_toasterStatus_get ( ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval) { status_t res; const xmlChar *toasterStatus; res = NO_ERR; /* remove the next line if scb is used */ (void)scb; /* remove the next line if virval is used */ (void)virval; if (cbmode != GETCB_GET_VALUE) { return ERR_NCX_OPERATION_NOT_SUPPORTED; } if (toaster_toasting) { toasterStatus = (const xmlChar *)"down"; } else { toasterStatus = (const xmlChar *)"up"; } res = val_set_simval_obj( dstval, dstval->obj, toasterStatus); return res; } /* y_toaster_toaster_toasterStatus_get */ /******************************************************************** * FUNCTION y_toaster_toaster_mro * * Make read-only child nodes * Path: /toaster * * INPUTS: * parentval == the parent struct to use for new child nodes * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_toaster_mro (val_value_t *parentval) { status_t res; val_value_t *childval; res = NO_ERR; /* add /toaster/toasterManufacturer */ childval = agt_make_virtual_leaf( parentval->obj, y_toaster_N_toasterManufacturer, y_toaster_toaster_toasterManufacturer_get, &res); if (childval != NULL) { val_add_child(childval, parentval); } else { return res; } /* add /toaster/toasterModelNumber */ childval = agt_make_virtual_leaf( parentval->obj, y_toaster_N_toasterModelNumber, y_toaster_toaster_toasterModelNumber_get, &res); if (childval != NULL) { val_add_child(childval, parentval); } else { return res; } /* add /toaster/toasterStatus */ childval = agt_make_virtual_leaf( parentval->obj, y_toaster_N_toasterStatus, y_toaster_toaster_toasterStatus_get, &res); if (childval != NULL) { val_add_child(childval, parentval); } else { return res; } return res; } /* y_toaster_toaster_mro */ /******************************************************************** * FUNCTION y_toaster_toaster_edit * * Edit database object callback * Path: /toaster * Add object instrumentation in COMMIT phase. * * INPUTS: * see agt/agt_cb.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_toaster_edit ( ses_cb_t *scb, rpc_msg_t *msg, agt_cbtyp_t cbtyp, op_editop_t editop, val_value_t *newval, val_value_t *curval) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; switch (cbtyp) { case AGT_CB_VALIDATE: /* description-stmt validation here */ break; case AGT_CB_APPLY: /* database manipulation done here */ break; case AGT_CB_COMMIT: /* device instrumentation done here */ switch (editop) { case OP_EDITOP_LOAD: toaster_enabled = TRUE; toaster_toasting = FALSE; break; case OP_EDITOP_MERGE: break; case OP_EDITOP_REPLACE: break; case OP_EDITOP_CREATE: toaster_enabled = TRUE; toaster_toasting = FALSE; break; case OP_EDITOP_DELETE: toaster_enabled = FALSE; if (toaster_toasting) { agt_timer_delete(toaster_timer_id); toaster_timer_id = 0; toaster_toasting = FALSE; y_toaster_toastDone_send((const xmlChar *)"error"); } break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } if (res == NO_ERR) { res = agt_check_cache( &toaster_val, newval, curval, editop); } if (res == NO_ERR && (editop == OP_EDITOP_LOAD || editop == OP_EDITOP_CREATE)) { res = y_toaster_toaster_mro(newval); } break; case AGT_CB_ROLLBACK: /* undo device instrumentation here */ break; default: res = SET_ERROR(ERR_INTERNAL_VAL); } /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_CONTENT, res, NULL, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_toaster_toaster_edit */ /******************************************************************** * FUNCTION y_toaster_make_toast_validate * * RPC validation phase * All YANG constriants have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_make_toast_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; val_value_t *errorval; const xmlChar *errorstr; val_value_t *toasterDoneness_val; val_value_t *toasterToastType_val; //uint32 toasterDoneness; //val_idref_t *toasterToastType; res = NO_ERR; errorval = NULL; errorstr = NULL; toasterDoneness_val = val_find_child( msg->rpc_input, y_toaster_M_toaster, y_toaster_N_toasterDoneness); if (toasterDoneness_val != NULL && toasterDoneness_val->res == NO_ERR) { //toasterDoneness = VAL_UINT(toasterDoneness_val); // validate toast doneness within instrumentation if needed } toasterToastType_val = val_find_child( msg->rpc_input, y_toaster_M_toaster, y_toaster_N_toasterToastType); if (toasterToastType_val != NULL && toasterToastType_val->res == NO_ERR) { //toasterToastType = VAL_IDREF(toasterToastType_val); // validate toast-type within instrumentation if needed } /* added code starts here */ if (toaster_enabled) { /* toaster service enabled, check if in use */ if (toaster_toasting) { res = ERR_NCX_IN_USE; } else { /* this is where a check on bread inventory would go */ /* this is where a check on toaster HW ready would go */ } } else { /* toaster service disabled */ res = ERR_NCX_RESOURCE_DENIED; } /* added code ends here */ /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_toaster_make_toast_validate */ /******************************************************************** * FUNCTION y_toaster_make_toast_invoke * * RPC invocation phase * All constraints have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_make_toast_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; val_value_t *toasterDoneness_val; val_value_t *toasterToastType_val; uint32 toasterDoneness; //val_idref_t *toasterToastType; res = NO_ERR; toasterDoneness = 0; toasterDoneness_val = val_find_child( msg->rpc_input, y_toaster_M_toaster, y_toaster_N_toasterDoneness); if (toasterDoneness_val != NULL && toasterDoneness_val->res == NO_ERR) { toasterDoneness = VAL_UINT(toasterDoneness_val); } toasterToastType_val = val_find_child( msg->rpc_input, y_toaster_M_toaster, y_toaster_N_toasterToastType); if (toasterToastType_val != NULL && toasterToastType_val->res == NO_ERR) { //toasterToastType = VAL_IDREF(toasterToastType_val); // invoke instrumentation with this toast type } /* invoke your device instrumentation code here */ /* make sure the toasterDoneness value is set */ if (toasterDoneness_val == NULL) { toasterDoneness = 5; /* set the default */ } /* arbitrary formula to convert toaster doneness to the * number of seconds the toaster should be on */ toaster_duration = toasterDoneness * 12; /* this is where the code would go to adjust the duration * based on the bread type */ if (LOGDEBUG) { log_debug("\ntoaster: starting toaster for %u seconds", toaster_duration); } /* this is where the code would go to start the toaster * heater element */ /* start a timer to toast for the specified time interval */ res = agt_timer_create(toaster_duration, FALSE, toaster_timer_fn, NULL, &toaster_timer_id); if (res == NO_ERR) { toaster_toasting = TRUE; } else { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } /* added code ends here */ return res; } /* y_toaster_make_toast_invoke */ /******************************************************************** * FUNCTION y_toaster_cancel_toast_validate * * RPC validation phase * All YANG constriants have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_cancel_toast_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; val_value_t *errorval; const xmlChar *errorstr; res = NO_ERR; errorval = NULL; errorstr = NULL; /* added code starts here */ if (toaster_enabled) { /* toaster service enabled, check if not in use */ if (!toaster_toasting) { res = ERR_NCX_OPERATION_FAILED; } } else { /* toaster service disabled */ res = ERR_NCX_RESOURCE_DENIED; } /* added code ends here */ /* if error: set the res, errorstr, and errorval parms */ if (res != NO_ERR) { agt_record_error( scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, errorstr, NCX_NT_VAL, errorval); } return res; } /* y_toaster_cancel_toast_validate */ /******************************************************************** * FUNCTION y_toaster_cancel_toast_invoke * * RPC invocation phase * All constraints have passed at this point. * Call device instrumentation code in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_toaster_cancel_toast_invoke ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { status_t res; res = NO_ERR; /* remove the next line if scb is used */ (void)scb; /* remove the next line if msg is used */ (void)msg; /* remove the next line if methnode is used */ (void)methnode; /* invoke your device instrumentation code here */ agt_timer_delete(toaster_timer_id); toaster_timer_id = 0; toaster_toasting = FALSE; y_toaster_toastDone_send((const xmlChar *)"cancelled"); return res; } /* y_toaster_cancel_toast_invoke */ /******************************************************************** * FUNCTION y_toaster_init_static_vars * * initialize module static variables * ********************************************************************/ static void y_toaster_init_static_vars (void) { toaster_mod = NULL; toaster_obj = NULL; make_toast_obj = NULL; cancel_toast_obj = NULL; toastDone_obj = NULL; toaster_val = NULL; /* init your static variables here */ toaster_enabled = FALSE; toaster_toasting = FALSE; toaster_duration = 0; toaster_timer_id = 0; } /* y_toaster_init_static_vars */ /******************************************************************** * FUNCTION y_toaster_toastDone_send * * Send a y_toaster_toastDone notification * Called by your code when notification event occurs * ********************************************************************/ void y_toaster_toastDone_send ( const xmlChar *toastStatus) { agt_not_msg_t *notif; val_value_t *parmval; status_t res; res = NO_ERR; if (LOGDEBUG) { log_debug("\nGenerating notification"); } notif = agt_not_new_notification(toastDone_obj); if (notif == NULL) { log_error("\nError: malloc failed, cannot send notification"); return; } /* add toastStatus to payload */ parmval = agt_make_leaf( toastDone_obj, y_toaster_N_toastStatus, toastStatus, &res); if (parmval == NULL) { log_error( "\nError: make leaf failed (%s), cannot send notification", get_error_string(res)); } else { agt_not_add_to_payload(notif, parmval); } agt_not_queue_notification(notif); } /* y_toaster_toastDone_send */ /******************************************************************** * FUNCTION y_toaster_init * * initialize the toaster server instrumentation library * * INPUTS: * modname == requested module name * revision == requested version (NULL for any) * * RETURNS: * error status ********************************************************************/ status_t y_toaster_init ( const xmlChar *modname, const xmlChar *revision) { agt_profile_t *agt_profile; status_t res; y_toaster_init_static_vars(); /* change if custom handling done */ if (xml_strcmp(modname, y_toaster_M_toaster)) { return ERR_NCX_UNKNOWN_MODULE; } if (revision && xml_strcmp(revision, y_toaster_R_toaster)) { return ERR_NCX_WRONG_VERSION; } agt_profile = agt_get_profile(); res = ncxmod_load_module( y_toaster_M_toaster, y_toaster_R_toaster, &agt_profile->agt_savedevQ, &toaster_mod); if (res != NO_ERR) { return res; } toaster_obj = ncx_find_object( toaster_mod, y_toaster_N_toaster); if (toaster_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } make_toast_obj = ncx_find_object( toaster_mod, y_toaster_N_make_toast); if (toaster_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } cancel_toast_obj = ncx_find_object( toaster_mod, y_toaster_N_cancel_toast); if (toaster_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } toastDone_obj = ncx_find_object( toaster_mod, y_toaster_N_toastDone); if (toaster_mod == NULL) { return SET_ERROR(ERR_NCX_DEF_NOT_FOUND); } res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_make_toast, AGT_RPC_PH_VALIDATE, y_toaster_make_toast_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_make_toast, AGT_RPC_PH_INVOKE, y_toaster_make_toast_invoke); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_cancel_toast, AGT_RPC_PH_VALIDATE, y_toaster_cancel_toast_validate); if (res != NO_ERR) { return res; } res = agt_rpc_register_method( y_toaster_M_toaster, y_toaster_N_cancel_toast, AGT_RPC_PH_INVOKE, y_toaster_cancel_toast_invoke); if (res != NO_ERR) { return res; } res = agt_cb_register_callback( y_toaster_M_toaster, (const xmlChar *)"/toaster", (const xmlChar *)"2009-11-20", y_toaster_toaster_edit); if (res != NO_ERR) { return res; } /* put your module initialization code here */ return res; } /* y_toaster_init */ /******************************************************************** * FUNCTION y_toaster_init2 * * SIL init phase 2: non-config data structures * Called after running config is loaded * * RETURNS: * error status ********************************************************************/ status_t y_toaster_init2 (void) { status_t res; res = NO_ERR; toaster_val = agt_init_cache( y_toaster_M_toaster, y_toaster_N_toaster, &res); if (res != NO_ERR) { return res; } /* put your init2 code here */ return res; } /* y_toaster_init2 */ /******************************************************************** * FUNCTION y_toaster_cleanup * cleanup the server instrumentation library * ********************************************************************/ void y_toaster_cleanup (void) { agt_rpc_unregister_method( y_toaster_M_toaster, y_toaster_N_make_toast); agt_rpc_unregister_method( y_toaster_M_toaster, y_toaster_N_cancel_toast); agt_cb_unregister_callbacks( y_toaster_M_toaster, (const xmlChar *)"/toaster"); /* put your cleanup code here */ } /* y_toaster_cleanup */ /* END toaster.c */ yuma123_2.14/libtoaster/src/toaster.h0000664000175000017500000000475314770023131017725 0ustar vladimirvladimir #ifndef _H_toaster #define _H_toaster /* *** Generated by yangdump 0.9.8.519M *** Copyright (c) 2009, Netconf Central, Inc., All Rights Reserved. module toaster revision 2009-11-20 namespace http://netconfcentral.com/ns/toaster organization Netconf Central */ #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #define y_toaster_M_toaster (const xmlChar *)"toaster" #define y_toaster_R_toaster (const xmlChar *)"2009-11-20" #define y_toaster_I_toast_type (const xmlChar *)"toast-type" #define y_toaster_I_white_bread (const xmlChar *)"white-bread" #define y_toaster_I_wheat_bread (const xmlChar *)"wheat-bread" #define y_toaster_I_wonder_bread (const xmlChar *)"wonder-bread" #define y_toaster_I_frozen_waffle (const xmlChar *)"frozen-waffle" #define y_toaster_I_frozen_bagel (const xmlChar *)"frozen-bagel" #define y_toaster_I_hash_brown (const xmlChar *)"hash-brown" #define y_toaster_N_cancel_toast (const xmlChar *)"cancel-toast" #define y_toaster_N_make_toast (const xmlChar *)"make-toast" #define y_toaster_N_toastDone (const xmlChar *)"toastDone" #define y_toaster_N_toastStatus (const xmlChar *)"toastStatus" #define y_toaster_N_toaster (const xmlChar *)"toaster" #define y_toaster_N_toasterDoneness (const xmlChar *)"toasterDoneness" #define y_toaster_N_toasterManufacturer (const xmlChar *)"toasterManufacturer" #define y_toaster_N_toasterModelNumber (const xmlChar *)"toasterModelNumber" #define y_toaster_N_toasterStatus (const xmlChar *)"toasterStatus" #define y_toaster_N_toasterToastType (const xmlChar *)"toasterToastType" /* container /toaster */ typedef struct y_toaster_T_toaster_ { xmlChar *toasterManufacturer; xmlChar *toasterModelNumber; xmlChar *toasterStatus; } y_toaster_T_toaster; /* container /make-toast/input */ typedef struct y_toaster_T_make_toast_input_ { uint32 toasterDoneness; val_idref_t *toasterToastType; } y_toaster_T_make_toast_input; /* notification /toastDone */ typedef struct y_toaster_T_toastDone_ { xmlChar *toastStatus; } y_toaster_T_toastDone; /* send a y_toaster_toastDone notification */ extern void y_toaster_toastDone_send ( const xmlChar *toastStatus); /* toaster module init 1 */ extern status_t y_toaster_init ( const xmlChar *modname, const xmlChar *revision); /* toaster module init 2 */ extern status_t y_toaster_init2 (void); /* toaster module cleanup */ extern void y_toaster_cleanup (void); #endif yuma123_2.14/libtoaster/src/toaster.h.start0000664000175000017500000000501014770023131021044 0ustar vladimirvladimir #ifndef _H_toaster #define _H_toaster /* *** Generated by yangdump 0.9.8.519M *** Copyright (c) 2009, Netconf Central, Inc., All Rights Reserved. module toaster revision 2009-11-20 namespace http://netconfcentral.com/ns/toaster organization Netconf Central, Inc. */ #include #ifndef _H_dlq #include "dlq.h" #endif #ifndef _H_ncxtypes #include "ncxtypes.h" #endif #ifndef _H_status #include "status.h" #endif #define y_toaster_M_toaster (const xmlChar *)"toaster" #define y_toaster_R_toaster (const xmlChar *)"2009-11-20" #define y_toaster_I_toast_type (const xmlChar *)"toast-type" #define y_toaster_I_white_bread (const xmlChar *)"white-bread" #define y_toaster_I_wheat_bread (const xmlChar *)"wheat-bread" #define y_toaster_I_wonder_bread (const xmlChar *)"wonder-bread" #define y_toaster_I_frozen_waffle (const xmlChar *)"frozen-waffle" #define y_toaster_I_frozen_bagel (const xmlChar *)"frozen-bagel" #define y_toaster_I_hash_brown (const xmlChar *)"hash-brown" #define y_toaster_N_cancel_toast (const xmlChar *)"cancel-toast" #define y_toaster_N_make_toast (const xmlChar *)"make-toast" #define y_toaster_N_toastDone (const xmlChar *)"toastDone" #define y_toaster_N_toastStatus (const xmlChar *)"toastStatus" #define y_toaster_N_toaster (const xmlChar *)"toaster" #define y_toaster_N_toasterDoneness (const xmlChar *)"toasterDoneness" #define y_toaster_N_toasterManufacturer (const xmlChar *)"toasterManufacturer" #define y_toaster_N_toasterModelNumber (const xmlChar *)"toasterModelNumber" #define y_toaster_N_toasterStatus (const xmlChar *)"toasterStatus" #define y_toaster_N_toasterToastType (const xmlChar *)"toasterToastType" /* container /toaster */ typedef struct y_toaster_T_toaster_ { xmlChar *toasterManufacturer; xmlChar *toasterModelNumber; xmlChar *toasterStatus; } y_toaster_T_toaster; /* container /make-toast/input */ typedef struct y_toaster_T_make_toast_input_ { uint32 toasterDoneness; val_idref_t *toasterToastType; } y_toaster_T_make_toast_input; /* notification /toastDone */ typedef struct y_toaster_T_toastDone_ { xmlChar *toastStatus; } y_toaster_T_toastDone; /* send a y_toaster_toastDone notification */ extern void y_toaster_toastDone_send ( const xmlChar *toastStatus); /* toaster module init 1 */ extern status_t y_toaster_init ( const xmlChar *modname, const xmlChar *revision); /* toaster module init 2 */ extern status_t y_toaster_init2 (void); /* toaster module cleanup */ extern void y_toaster_cleanup (void); #endifyuma123_2.14/libtoaster/src/Makefile.am0000775000175000017500000000100514770023131020115 0ustar vladimirvladimirnetconfmodule_LTLIBRARIES = libtoaster.la noinst_HEADERS=\ $(top_srcdir)/libtoaster/src/toaster.h libtoaster_la_SOURCES = \ $(top_srcdir)/libtoaster/src/toaster.c libtoaster_la_CPPFLAGS = -I $(top_srcdir)/libtoaster/src/ -I$(top_srcdir)/netconf/src/agt -I$(top_srcdir)/netconf/src/mgr -I$(top_srcdir)/netconf/src/ncx -I$(top_srcdir)/netconf/src/platform -I$(top_srcdir)/netconf/src/ydump libtoaster_la_LDFLAGS = -module $(top_builddir)/netconf/src/agt/libyumaagt.la $(top_builddir)/netconf/src/ncx/libyumancx.la